Commit ac81c9b6 authored by Luboš Horáček's avatar Luboš Horáček

#317 & #318 Chyby při volbě položky nového uživatele

parent aa80ade5
......@@ -661,7 +661,7 @@ public class Tablexia extends com.activeandroid.app.Application {
@Override
public void onAnimationEnd(Animator animation) {
((Activity) getCurrentMenuActivity()).startActivityForResult(intent, 0);
((Activity) getCurrentMenuActivity()).startActivityForResult(intent, MainActivity.REQUEST_CODE_NEW_USER);
// TODO remove TestFlight.log("Create user button");
}
});
......
/*******************************************************************************
* Tablexia
*
*
* Copyright (C) 2013 CZ NIC z.s.p.o. <podpora at nic dot cz>
*
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
......@@ -35,535 +35,536 @@ import cz.nic.tablexia.game.games.potme.map.tile.TileType;
/**
* Map generator for game "Potmě"
*
*
* @author Matyáš Latner
*
*/
public class MapGenerator implements IMapProvider {
private static final double OBSTACLE_PROBABILITY = 0.4;
/**
* Class for storing information about tiles group distance.
* Stores two tiles and distance between this tiles.
*
* @author Matyáš Latner
*
*/
protected static class TileGroupsDistanceContainer {
private Tile tile1;
private Tile tile2;
private int distance;
public TileGroupsDistanceContainer(Tile tile1, Tile tile2, int distance) {
this.tile1 = tile1;
this.tile2 = tile2;
this.distance = distance;
}
public Tile getTile1() {
return tile1;
}
public Tile getTile2() {
return tile2;
}
public int getDistance() {
return distance;
}
}
protected RandomAccess randomAccess;
public MapGenerator(RandomAccess randomAccess) {
this.randomAccess = randomAccess;
}
public TileMap prepareMap(Tile lastFinishTile, int mapXSize, int mapYSize, MapObjectType finishMapObject, boolean hasKey) {
Log.d(MapGenerator.class.getCanonicalName(), "Start generating with random seed: " + randomAccess.getRandomSeed());
TileMap tileMap = new TileMap(mapXSize, mapYSize);
// add start tile
TileMapPosition startPosition = createStartTile(tileMap, lastFinishTile);
// fill all map
for (int i = 0; i < tileMap.getMapXSize(); i++) {
for (int j = 0; j < tileMap.getMapYSize(); j++) {
if (!tileMap.isTileAtPosition(i, j)) {
Tile tile = getRandomTileForMapPosition(tileMap, i, j, null);
tile.generateRandomObstacle(randomAccess, OBSTACLE_PROBABILITY);
tileMap.addTileAtPosition(i, j, tile);
}
}
}
// connect all tile groups
connectAllGroups(tileMap, Arrays.asList(new TileMapPosition[] {startPosition}));
// select best tile for safe
Tile finishTile = createFinishTile(tileMap, finishMapObject);
tileMap.setMapFinishPosition(finishTile.getTileMapPosition());
if (hasKey) {
// selects best tile for key
createKeyTile(tileMap, finishTile);
}
return tileMap;
}
/**
* Creates start tile in dependency of lastStartTile parameter
* For <code>null</code> lastStartTile parameter is generated start tile as an entry to map
* else is generated start tile with stairs.
*
* @param tileMap
* @param lastEndTile last tile from previous floor
* @return start tile position
*/
private TileMapPosition createStartTile(TileMap tileMap, Tile lastEndTile) {
TileMapPosition startPosition;
Tile startTile;
if (lastEndTile == null) {
// create entry tile
startPosition = PotmeActivity.DEFAULT_MAP_START_POSITION;
startTile = new Tile(TileType.getRandomTileTypeForAvailableDoors(null, true, null, null, true, randomAccess));
} else {
startPosition = lastEndTile.getTileMapPosition();
if (Arrays.asList(new TileType[] {TileType.TILE_1B, TileType.TILE_1D}).contains(lastEndTile.getTileType()) && (startPosition.getPositionX() == 0 || startPosition.getPositionX() == tileMap.getMapXSize() - 1) ||
Arrays.asList(new TileType[] {TileType.TILE_1A, TileType.TILE_1C}).contains(lastEndTile.getTileType()) && (startPosition.getPositionY() == 0 || startPosition.getPositionY() == tileMap.getMapYSize() - 1)) {
// create same tile as last tile
startTile = new Tile(lastEndTile.getTileType());
} else {
// create inverted tile from last tile
startTile = new Tile(TileType.getSwipedTileType(lastEndTile.getTileType()));
}
startTile.setMapObject(MapObjectType.STAIRS);
}
tileMap.addTileAtPosition(startPosition, startTile);
tileMap.setMapStartPosition(startPosition);
return startPosition;
}
/**
* Connects all tile groups to one tile group
*
* @param tileMap tile map with tile groups
*/
private void connectAllGroups(TileMap tileMap, List<TileMapPosition> bannedPositions) {
List<List<Tile>> tileGroups = getAllTileGroups(tileMap);
while (tileGroups.size() > 1) {
List<Tile> biggestGroup = getBiggestTileGroup(tileGroups);
tileGroups.remove(biggestGroup);
TileGroupsDistanceContainer minimalDistance = null;
for (List<Tile> tileGroup : tileGroups) {
TileGroupsDistanceContainer distance = getGroupsMinimalDistance(biggestGroup, tileGroup, bannedPositions);
if (minimalDistance == null || distance.getDistance() < minimalDistance.getDistance()) {
minimalDistance = distance;
}
}
if (minimalDistance != null) {
try {
Log.d(MapGenerator.class.getCanonicalName(), "Try to connect tile groups: " + minimalDistance.getTile1() + " ---> " + minimalDistance.getTile2());
generatePathFromPointToPoint(tileMap, minimalDistance.getTile1().getTileMapPosition(), minimalDistance.getTile2().getTileMapPosition(), false, true, bannedPositions);
} catch (Exception e) {
Log.e(MapGenerator.class.getCanonicalName(), "Cannot connect tile groups: " + minimalDistance.getTile1() + " ---> " + minimalDistance.getTile2(), e);
}
}
tileGroups = getAllTileGroups(tileMap);
}
}
/**
* Return biggest tile group from tile groups list.
*
* @param tileGroups tile groups list.
* @return biggest tile group from tile groups list
*/
private List<Tile> getBiggestTileGroup(List<List<Tile>> tileGroups) {
List<Tile> biggestGroup = null;
for (List<Tile> tileGroup : tileGroups) {
if (biggestGroup == null || tileGroup.size() > biggestGroup.size()) {
biggestGroup = tileGroup;
}
}
return biggestGroup;
}
/**
* Return all tile groups from tile map.
*
* @param tileMap tile map with tile groups
* @return all tile groups from tile map
*/
private List<List<Tile>> getAllTileGroups(TileMap tileMap) {
List<Tile> checkedTiles = new ArrayList<Tile>();
List<List<Tile>> result = new ArrayList<List<Tile>>();
for (int i = 0; i < tileMap.getMapXSize(); i++) {
for (int j = 0; j < tileMap.getMapYSize(); j++) {
Tile tile = tileMap.getTileAtPosition(i, j);
if (tile != null && !checkedTiles.contains(tile)) {
List<Tile> group = getTileGroup(checkedTiles, tile);
if (group.size() > 0) {
result.add(group);
}
}
}
}
return result;
}
/**
* Returns minimal distance between two tiles groups and tile from each group for this distance.
* For example:
*
* 0 1 2
* ----------------
* 0 | | | |
* |__|_|____|_|__|
* 1 | | | _|_| |
* |__|_|__|_|____|
* 2 | | | | | |
* |____|____|____|
*
* Method returns distance: 1 and tiles on positions: [0, 1], [1, 1]
*
* @param group1 first tile group
* @param group2 second tile group
* @param bannedPositions list of banned positions which is not included to distance processing
* @return minimal distance between tile groups and tiles with this distance
*/
protected TileGroupsDistanceContainer getGroupsMinimalDistance(List<Tile> group1, List<Tile> group2, List<TileMapPosition> bannedPositions) {
TileGroupsDistanceContainer minimalDistance = null;
for (Tile tile1 : group1) {
if (bannedPositions == null || !bannedPositions.contains(tile1.getTileMapPosition())) {
for (Tile tile2 : group2) {
if (bannedPositions == null || !bannedPositions.contains(tile2.getTileMapPosition())) {
int distance = Math.abs(tile1.getMapPositionX() - tile2.getMapPositionX()) + Math.abs(tile1.getMapPositionY() - tile2.getMapPositionY());
if (minimalDistance == null || distance < minimalDistance.getDistance()) {
minimalDistance = new TileGroupsDistanceContainer(tile1, tile2, distance);
}
}
}
}
}
return minimalDistance;
}
/**
* Returns all tiles connected with start tile.
* For example:
*
* 0 1 2
* ----------------
* 0 | __ | _ | |
* |___ |__|_|__|_|
* 1 | | |_| _| |
* |____|__|_|__|_|
* 2 | | | |_| _| |
* |____|____|____|
*
* For start tile at position: [0, 0]
* method returns tiles at positions: [0, 0], [1, 0], [1, 1], [1, 2], [2, 0], [2, 1], [2, 2]
*
*
* @param checkedTiles list of already checked tiles
* @param startTile tile to start from
* @return list of all tiles connected with start tile
*/
protected List<Tile> getTileGroup(List<Tile> checkedTiles, Tile startTile) {
List<Tile> result = new ArrayList<Tile>();
if (startTile != null && !startTile.getTileType().isWall()) {
checkedTiles.add(startTile);
result.add(startTile);
for (Tile tile : startTile.getNeighbors()) {
if (tile != null && !checkedTiles.contains(tile)) {
result.addAll(getTileGroup(checkedTiles, tile));
}
}
}
return result;
}
/**
* Generate path from tile position to tile position
*
* @param tileMap tile map for generating in
* @param startPosition start tile position
* @param finishPosition finish tile position
* @param useOnlyFreeTiles for <code>true</code> generates path only on free tiles
* @param generateFinalTile for <code>true</code> generates tile on final position
*/
private void generatePathFromPointToPoint(TileMap tileMap, TileMapPosition startPosition, TileMapPosition finishPosition, boolean useOnlyFreeTiles, boolean generateFinalTile, List<TileMapPosition> bannedPositions) {
int lastXPosition = startPosition.getPositionX();
int lastYPosition = startPosition.getPositionY();
while (lastXPosition != finishPosition.getPositionX() || lastYPosition != finishPosition.getPositionY()) {
List<TileMapPosition> freeBorders = tileMap.getTileFreeBorders(lastXPosition, lastYPosition, useOnlyFreeTiles, true);
for (int i = freeBorders.size() - 1; i >= 0; i--) {
if (bannedPositions.contains(freeBorders.get(i))) {
freeBorders.remove(i);
}
}
if (freeBorders.size() == 0) {
throw new IllegalStateException("Cannot generate path from: " + startPosition + " to: " + finishPosition);
}
TileMapPosition borderTilePosition = getClosestTileMapPosition(freeBorders, finishPosition.getPositionX(), finishPosition.getPositionY());
Tile tempDirectionTile = new Tile(getBorderTileDirectionTileType(lastXPosition, lastYPosition, borderTilePosition.getPositionX(), borderTilePosition.getPositionY()));
tileMap.addTileAtPosition(borderTilePosition.getPositionX(), borderTilePosition.getPositionY(), tempDirectionTile);
tileMap.addTileAtPosition(lastXPosition, lastYPosition, getRandomTileForMapPosition(tileMap, lastXPosition, lastYPosition, true));
tileMap.removeTileAtPosition(borderTilePosition.getPositionX(), borderTilePosition.getPositionY());
lastXPosition = borderTilePosition.getPositionX();
lastYPosition = borderTilePosition.getPositionY();
if (generateFinalTile) {
tileMap.addTileAtPosition(lastXPosition, lastYPosition, getRandomTileForMapPosition(tileMap, lastXPosition, lastYPosition, null));
}
}
}
/**
* Return tile map positions from tile map position list in parameter with minimal distance to destination position in parameter.
*
* @param tileMapPositions list of tile map positions
* @param destionationXPos destination X position
* @param destionationYPos destination Y position
*
* @return tile map position from tile map positions list in parameter with minimal distance to destination position in parameter
*/
protected TileMapPosition getClosestTileMapPosition(List<TileMapPosition> tileMapPositions, int destionationXPos, int destionationYPos) {
int smallestDistance = Integer.MAX_VALUE;
TileMapPosition result = null;
for (TileMapPosition tileMapPosition : tileMapPositions) {
int distance = Math.abs(tileMapPosition.getPositionX() - destionationXPos) + Math.abs(tileMapPosition.getPositionY() - destionationYPos);
if (distance < smallestDistance) {
smallestDistance = distance;
result = tileMapPosition;
}
}
return result;
}
/**
* Returns tile type for position [positionX, positionY] to connect to tile at [lastPositionX, lastPositionY].
*
* @param lastPositionX
* @param lastPositionY
* @param positionX
* @param positionY
* @return
*/
protected TileType getBorderTileDirectionTileType(int lastPositionX, int lastPositionY, int positionX, int positionY) {
TileType result;
if (lastPositionX == positionX) {
result = lastPositionY < positionY ? TileType.TILE_1A : TileType.TILE_1C;
} else if (lastPositionY == positionY) {
result = lastPositionX < positionX ? TileType.TILE_1D : TileType.TILE_1B;
} else {
result = TileType.TILE_0;
}
return result;
}
/**
* Returns random tile for specific position in map. Tile have to fit map position.
* Tile is selected for map position surroundings tiles. For example in map:
*
* 0 1 2
* ____ ____ ____ X --> WALL
* 0 | | | | D --> DOOR
* |____|DDDD|____| ? --> NOT SPECIFIED
* 1 | X D |
* |____X D____| is selected TILE_2LA: | | as only possible choice.
* 2 | |XXXX| | | |___
* |____|____|____| |_______
*
*
* 0 1 2
* ____ ____ ____ X --> WALL
* 0 | | | | D --> DOOR
* |____|????|____| ? --> NOT SPECIFIED
* 1 | X ? |
* |____X ?____| is selected TILE_1A: | | or TILE_1B: ______ for parameter <code>walkThrough = false</code>
* 2 | |XXXX| | | | |______
* |____|____|____| |___|
*
*
* is selected TILE_2LA: | | for parameter <code>walkThrough = true</code>
* | |___
* |_______
*
* @param tileMap game map with tiles
* @param positionX position X for selecting tile
* @param positionY position Y for selecting tile
* @param walkThrough for <code>true</code> selects only tiles with walk through possibility,
* for <code>false</code> without walk through possibility.
* <code>null</code> is wildcard.
* @return randomly selected tile which fits surrounded tile map
*/
protected Tile getRandomTileForMapPosition(TileMap tileMap, int positionX, int positionY, Boolean walkThrough) {
Boolean topDoor = positionY > 0;
if (topDoor.booleanValue()) {
Tile topBorderTile = tileMap.getTileAtPosition(positionX, positionY - 1);
topDoor = topBorderTile == null ? null : topBorderTile.getTileType().isBottomDoor();
}
Boolean rightDoor = positionX < (tileMap.getMapXSize() - 1);
if (rightDoor.booleanValue()) {
Tile rightBorderTile = tileMap.getTileAtPosition(positionX + 1, positionY);
rightDoor = rightBorderTile == null ? null : rightBorderTile.getTileType().isLeftDoor();
}
Boolean bottomDoor = positionY < (tileMap.getMapYSize() - 1);
if (bottomDoor.booleanValue()) {
Tile bottomBorderTile = tileMap.getTileAtPosition(positionX, positionY + 1);
bottomDoor = bottomBorderTile == null ? null : bottomBorderTile.getTileType().isTopDoor();
}
Boolean leftDoor = positionX > 0;
if (leftDoor.booleanValue()) {
Tile leftBorderTile = tileMap.getTileAtPosition(positionX - 1, positionY);
leftDoor = leftBorderTile == null ? null : leftBorderTile.getTileType().isRightDoor();
}
TileType randomTileType = TileType.getRandomTileTypeForAvailableDoors(topDoor, rightDoor, bottomDoor, leftDoor, walkThrough, randomAccess);
return new Tile(randomTileType);
}
private Tile createFinishTile(TileMap tileMap, MapObjectType finishMapObjectType) {
calculateTileDistances(tileMap.getTileAtPosition(tileMap.getMapStartPosition()), 0);
Tile tile = searchFurthermostTile(tileMap, Arrays.asList(new TileType[] { TileType.TILE_1A, TileType.TILE_1B, TileType.TILE_1C, TileType.TILE_1D }));
// in case there is no end tile
if (tile == null) {
tile = searchFurthermostTile(tileMap, null);
}
tile.setMapObject(finishMapObjectType);
return tile;
}
/**
* Finds best tile for key. Finds middle distance from start and safe positions.
*
* @param tileMap
* @param finishTile
*/
private void createKeyTile(TileMap tileMap, Tile finishTile) {
Map<TileMapPosition, Integer> distanceMap = new HashMap<TileMapPosition, Integer>();
for (int i = 0; i < tileMap.getMapXSize(); i++) {
for (int j = 0; j < tileMap.getMapYSize(); j++) {
if (tileMap.isTileAtPosition(i, j)) {
Tile tile = tileMap.getTileAtPosition(i, j);
distanceMap.put(new TileMapPosition(i, j), tile.getDistance());
tile.resetDistance();
}
}
}
calculateTileDistances(finishTile, 0);
Tile result = null;
int maxPosition = -1;
for (TileMapPosition tileMapPosition : distanceMap.keySet()) {
Tile localTile = tileMap.getTileAtPosition(tileMapPosition);
int localMaxPosition = localTile.getDistance() + distanceMap.get(tileMapPosition);
if (localMaxPosition > maxPosition) {
maxPosition = localMaxPosition;
result = localTile;
}
}
result.setMapObject(MapObjectType.KEY);
}
/**
* Search and return furtermost tile for specified tile type in the parameter
* @param tileMap map of tiles for searching in
* @param tileTypes list of tile types for searching
* @return
*/
private Tile searchFurthermostTile(TileMap tileMap, List<TileType> tileTypes) {
Tile result = null;
int maxDistance = -1;
for (int i = 0; i < tileMap.getMapXSize(); i++) {
for (int j = 0; j < tileMap.getMapYSize(); j++) {
Tile tile = tileMap.getTileAtPosition(i, j);
if (tile != null && (tileTypes == null || tileTypes.contains(tile.getTileType()))) {
if (tile.getDistance() > maxDistance) {
result = tile;
maxDistance = tile.getDistance();
}
}
}
}
return result;
}
/**
* Iterate over all map and calculate distances for all tiles.
* For example:
*
* 0 1 2
* ----------------
* 0 | _|____|__ |
* |__|_|____|__|_|
* 1 | |_|__ | | |
* |____|__|_|__|_|
* 2 | __|__|_|__| |
* |____|____|____|
*
* For start tile at position [0, 2]
* Method sets distances to tiles: 0 for tile postion: [0, 2]
* 1 for tile postion: [1, 2]
* 2 for tile postion: [1, 1] and [2, 2]
* 3 for tile postion: [0, 1] and [2, 1]
* 4 for tile postion: [0, 0] and [2, 0]
* 5 for tile postion: [1, 0]
*
* @param tile tile for start of the searching from
* @param tileDistance initial distance
*/
protected void calculateTileDistances(Tile tile, int tileDistance) {
if (tile != null) {
if (tile.getDistance() > tileDistance) {
tile.setDistance(tileDistance);
tileDistance++;
for (Tile neighbor : tile.getNeighbors()) {
if (neighbor != null) {
calculateTileDistances(neighbor, tileDistance);
}
}
}
}
}
private static final double OBSTACLE_PROBABILITY = 0.4;
/**
* Class for storing information about tiles group distance.
* Stores two tiles and distance between this tiles.
*
* @author Matyáš Latner
*
*/
protected static class TileGroupsDistanceContainer {
private Tile tile1;
private Tile tile2;
private int distance;
public TileGroupsDistanceContainer(Tile tile1, Tile tile2, int distance) {
this.tile1 = tile1;
this.tile2 = tile2;
this.distance = distance;
}
public Tile getTile1() {
return tile1;
}
public Tile getTile2() {
return tile2;
}
public int getDistance() {
return distance;
}
}
protected RandomAccess randomAccess;
public MapGenerator(RandomAccess randomAccess) {
this.randomAccess = randomAccess;
}
@Override
public TileMap prepareMap(Tile lastFinishTile, int mapXSize, int mapYSize, MapObjectType finishMapObject, boolean hasKey) {
Log.d(MapGenerator.class.getCanonicalName(), "Start generating with random seed: " + randomAccess.getRandomSeed());
TileMap tileMap = new TileMap(mapXSize, mapYSize);
// add start tile
TileMapPosition startPosition = createStartTile(tileMap, lastFinishTile);
// fill all map
for (int i = 0; i < tileMap.getMapXSize(); i++) {
for (int j = 0; j < tileMap.getMapYSize(); j++) {
if (!tileMap.isTileAtPosition(i, j)) {
Tile tile = getRandomTileForMapPosition(tileMap, i, j, null);
tile.generateRandomObstacle(randomAccess, OBSTACLE_PROBABILITY);
tileMap.addTileAtPosition(i, j, tile);
}
}
}
// connect all tile groups
connectAllGroups(tileMap, Arrays.asList(new TileMapPosition[] {startPosition}));
// select best tile for safe
Tile finishTile = createFinishTile(tileMap, finishMapObject);
tileMap.setMapFinishPosition(finishTile.getTileMapPosition());
if (hasKey) {
// selects best tile for key
createKeyTile(tileMap, finishTile);
}
return tileMap;
}
/**
* Creates start tile in dependency of lastStartTile parameter
* For <code>null</code> lastStartTile parameter is generated start tile as an entry to map
* else is generated start tile with stairs.
*
* @param tileMap
* @param lastEndTile last tile from previous floor
* @return start tile position
*/
private TileMapPosition createStartTile(TileMap tileMap, Tile lastEndTile) {
TileMapPosition startPosition;
Tile startTile;
if (lastEndTile == null) {
// create entry tile
startPosition = PotmeActivity.DEFAULT_MAP_START_POSITION;
startTile = new Tile(TileType.getRandomTileTypeForAvailableDoors(null, true, null, null, true, randomAccess));
} else {
startPosition = lastEndTile.getTileMapPosition();
if ((Arrays.asList(new TileType[] {TileType.TILE_1B, TileType.TILE_1D}).contains(lastEndTile.getTileType()) && ((startPosition.getPositionX() == 0) || (startPosition.getPositionX() == (tileMap.getMapXSize() - 1)))) ||
(Arrays.asList(new TileType[] {TileType.TILE_1A, TileType.TILE_1C}).contains(lastEndTile.getTileType()) && ((startPosition.getPositionY() == 0) || (startPosition.getPositionY() == (tileMap.getMapYSize() - 1))))) {
// create same tile as last tile
startTile = new Tile(lastEndTile.getTileType());
} else {
// create inverted tile from last tile
startTile = new Tile(TileType.getSwipedTileType(lastEndTile.getTileType()));
}
startTile.setMapObject(MapObjectType.STAIRS);
}
tileMap.addTileAtPosition(startPosition, startTile);
tileMap.setMapStartPosition(startPosition);
return startPosition;
}
/**
* Connects all tile groups to one tile group
*
* @param tileMap tile map with tile groups
*/
private void connectAllGroups(TileMap tileMap, List<TileMapPosition> bannedPositions) {