mirror of https://github.com/FreeCol/freecol.git
The European starting areas defined by map authors can now be used when starting a new game.
This commit is contained in:
parent
a35f586e61
commit
4ee64d08fe
|
@ -4624,6 +4624,10 @@
|
|||
sail in? -->
|
||||
<booleanOption id="model.option.teleportREF"
|
||||
defaultValue="true"/>
|
||||
|
||||
<!-- Use map defined starting positions (if available). -->
|
||||
<booleanOption id="model.option.mapDefinedStartingPositions" defaultValue="true" />
|
||||
|
||||
<!-- Types of starting positions -->
|
||||
<selectOption id="model.option.startingPositions"
|
||||
defaultValue="0" localizedLabels="true">
|
||||
|
|
|
@ -727,6 +727,8 @@ model.option.continueFoundingFatherRecruitment.name=Continue recruiting Founding
|
|||
model.option.continueFoundingFatherRecruitment.shortDescription=Continue recruiting Founding Fathers after independence is granted.
|
||||
model.option.teleportREF.name=Teleport REF
|
||||
model.option.teleportREF.shortDescription=REF appears at the landing site for its first target.
|
||||
model.option.mapDefinedStartingPositions.name=Use starting positions defined by map (if available).
|
||||
model.option.mapDefinedStartingPositions.shortDescription=Starting positions defined by the map author is used if available.
|
||||
model.option.startingPositions.name=Starting Positions
|
||||
model.option.startingPositions.shortDescription=Determines the starting positions of the European players.
|
||||
model.option.startingPositions.classic.name=Classic
|
||||
|
|
|
@ -3088,6 +3088,12 @@ public final class Specification implements OptionContainer {
|
|||
Boolean.FALSE, BooleanOption.class);
|
||||
// end @compat 0.11.6
|
||||
|
||||
// @compat 1.1.0
|
||||
ret |= checkOp(GameOptions.MAP_DEFINED_STARTING_POSITIONS,
|
||||
GameOptions.GAMEOPTIONS_MAP,
|
||||
Boolean.TRUE, BooleanOption.class);
|
||||
// end @compat 1.1.0
|
||||
|
||||
// SAVEGAME_VERSION == 14
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -100,6 +100,11 @@ public class GameOptions {
|
|||
public static final String TELEPORT_REF
|
||||
= "model.option.teleportREF";
|
||||
|
||||
/**
|
||||
* Use map defined starting positions (if available).
|
||||
*/
|
||||
public static final String MAP_DEFINED_STARTING_POSITIONS = "model.option.mapDefinedStartingPositions";
|
||||
|
||||
/** How to determine the starting positions of European players. */
|
||||
public static final String STARTING_POSITIONS
|
||||
= "model.option.startingPositions";
|
||||
|
|
|
@ -8,10 +8,13 @@ import static net.sf.freecol.common.util.RandomUtils.getRandomMember;
|
|||
import static net.sf.freecol.common.util.RandomUtils.randomShuffle;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.function.ToIntFunction;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.stream.Collectors;
|
||||
|
@ -20,6 +23,7 @@ import java.util.stream.StreamSupport;
|
|||
import net.sf.freecol.common.debug.FreeColDebugger;
|
||||
import net.sf.freecol.common.i18n.Messages;
|
||||
import net.sf.freecol.common.model.AbstractUnit;
|
||||
import net.sf.freecol.common.model.Area;
|
||||
import net.sf.freecol.common.model.Building;
|
||||
import net.sf.freecol.common.model.BuildingType;
|
||||
import net.sf.freecol.common.model.Colony;
|
||||
|
@ -78,6 +82,8 @@ class EuropeanStartingPositionsGenerator {
|
|||
if (europeanPlayers.isEmpty()) {
|
||||
throw new RuntimeException("No players to generate units for!");
|
||||
}
|
||||
|
||||
Collections.shuffle(europeanPlayers, random);
|
||||
|
||||
final java.util.Map<Player, StartingUnits> playerStartingUnits = determineStartingUnits(europeanPlayers);
|
||||
final java.util.Map<Player, Tile> playerStartingTiles = determineStartingTiles(map, europeanPlayers, playerStartingUnits);
|
||||
|
@ -100,7 +106,138 @@ class EuropeanStartingPositionsGenerator {
|
|||
}
|
||||
|
||||
private java.util.Map<Player, Tile> determineStartingTiles(Map map, List<Player> europeanPlayers, java.util.Map<Player, StartingUnits> playerStartingUnits) {
|
||||
return determineStartingTilesWithoutUsingPredeterminedPositions(map, europeanPlayers, playerStartingUnits);
|
||||
final Specification spec = map.getSpecification();
|
||||
final Game game = map.getGame();
|
||||
|
||||
final boolean mapDefinedStartingPositionsAvailable = isMapDefinedStartingPositionsAvailableFor(europeanPlayers, game);
|
||||
|
||||
if (!mapDefinedStartingPositionsAvailable) {
|
||||
return determineStartingTilesWithoutUsingPredeterminedPositions(map, europeanPlayers, playerStartingUnits);
|
||||
}
|
||||
|
||||
final int positionType = spec.getInteger(GameOptions.STARTING_POSITIONS);
|
||||
switch (positionType) {
|
||||
case GameOptions.STARTING_POSITIONS_CLASSIC:
|
||||
return playerMapStartingAreaAndEnsureDistance(europeanPlayers, playerStartingUnits, game);
|
||||
case GameOptions.STARTING_POSITIONS_RANDOM:
|
||||
return randomMapStartingArea(europeanPlayers, playerStartingUnits, game);
|
||||
case GameOptions.STARTING_POSITIONS_HISTORICAL:
|
||||
return playerMapStartingArea(europeanPlayers, playerStartingUnits, game);
|
||||
default:
|
||||
throw new IllegalStateException("Unknown positionType=" + positionType);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private java.util.Map<Player, Tile> playerMapStartingAreaAndEnsureDistance(List<Player> europeanPlayers,
|
||||
java.util.Map<Player, StartingUnits> playerStartingUnits, final Game game) {
|
||||
final int MINIMUM_DISTANCE_BETWEEN_PLAYERS = 10;
|
||||
final int MAX_TRIES = 10;
|
||||
for (int i=0; i<MAX_TRIES; i++) {
|
||||
final java.util.Map<Player, Tile> startingTiles = playerMapStartingArea(europeanPlayers, playerStartingUnits, game);
|
||||
final boolean satisfiesMinimumDistance = startingTiles.values().stream()
|
||||
.noneMatch(t -> startingTiles.values().stream().anyMatch(t2 -> t != t2 && t.getDistanceTo(t2) < MINIMUM_DISTANCE_BETWEEN_PLAYERS));
|
||||
if (satisfiesMinimumDistance) {
|
||||
return startingTiles;
|
||||
}
|
||||
}
|
||||
logger.info("Not able to secure minimum starting distance between European players.");
|
||||
return playerMapStartingArea(europeanPlayers, playerStartingUnits, game);
|
||||
}
|
||||
|
||||
|
||||
private java.util.Map<Player, Tile> playerMapStartingArea(List<Player> europeanPlayers, java.util.Map<Player, StartingUnits> playerStartingUnits, final Game game) {
|
||||
final java.util.Map<Player, Tile> startingTiles = new HashMap<>();
|
||||
for (Player player : europeanPlayers) {
|
||||
final StartingUnits startingUnits = playerStartingUnits.get(player);
|
||||
final boolean prefersLand = startingUnits.getCarriers().isEmpty();
|
||||
final Area startingArea = game.getNationStartingArea(player.getNation());
|
||||
|
||||
List<Tile> possibleStartingTiles = startingArea.getTiles().stream()
|
||||
.filter(t -> t.isLand() == prefersLand)
|
||||
.collect(Collectors.toList());
|
||||
if (possibleStartingTiles.isEmpty()) {
|
||||
possibleStartingTiles = startingArea.getTiles();
|
||||
}
|
||||
|
||||
final int randomIndex = random.nextInt(possibleStartingTiles.size());
|
||||
final Tile startingTile = possibleStartingTiles.get(randomIndex);
|
||||
startingTiles.put(player, startingTile);
|
||||
}
|
||||
return startingTiles;
|
||||
}
|
||||
|
||||
private java.util.Map<Player, Tile> randomMapStartingArea(List<Player> europeanPlayers, java.util.Map<Player, StartingUnits> playerStartingUnits, final Game game) {
|
||||
final java.util.Map<Player, Tile> startingTiles = new HashMap<>();
|
||||
|
||||
final Set<Tile> tilesToChooseFrom = new HashSet<>();
|
||||
for (Player player : europeanPlayers) {
|
||||
final Area startingArea = game.getNationStartingArea(player.getNation());
|
||||
if (startingArea != null) {
|
||||
tilesToChooseFrom.addAll(startingArea.getTiles());
|
||||
}
|
||||
}
|
||||
|
||||
final List<Tile> randomLandTilesToChooseFrom = tilesToChooseFrom.stream()
|
||||
.filter(t -> t.isLand())
|
||||
.collect(Collectors.toList());
|
||||
Collections.shuffle(randomLandTilesToChooseFrom, random);
|
||||
int indexLand = 0;
|
||||
|
||||
final List<Tile> randomOceanTilesToChooseFrom = tilesToChooseFrom.stream()
|
||||
.filter(t -> !t.isLand())
|
||||
.collect(Collectors.toList());
|
||||
Collections.shuffle(randomOceanTilesToChooseFrom, random);
|
||||
int indexOcean = 0;
|
||||
|
||||
for (Player player : europeanPlayers) {
|
||||
final StartingUnits startingUnits = playerStartingUnits.get(player);
|
||||
final boolean prefersLand = startingUnits.getCarriers().isEmpty();
|
||||
final Tile startingTile;
|
||||
if (prefersLand && indexLand < randomLandTilesToChooseFrom.size()) {
|
||||
startingTile = randomLandTilesToChooseFrom.get(indexLand);
|
||||
indexLand++;
|
||||
} else if (!prefersLand && indexOcean < randomOceanTilesToChooseFrom.size()) {
|
||||
startingTile = randomOceanTilesToChooseFrom.get(indexOcean);
|
||||
indexOcean++;
|
||||
} else if (indexLand < randomLandTilesToChooseFrom.size()) {
|
||||
startingTile = randomLandTilesToChooseFrom.get(indexLand);
|
||||
indexLand++;
|
||||
} else {
|
||||
startingTile = randomOceanTilesToChooseFrom.get(indexOcean);
|
||||
indexOcean++;
|
||||
}
|
||||
|
||||
startingTiles.put(player, startingTile);
|
||||
}
|
||||
return startingTiles;
|
||||
}
|
||||
|
||||
|
||||
private boolean isMapDefinedStartingPositionsAvailableFor(List<Player> europeanPlayers, Game game) {
|
||||
final Specification spec = game.getSpecification();
|
||||
boolean mapDefinedStartingPositions = spec.getBoolean(GameOptions.MAP_DEFINED_STARTING_POSITIONS);
|
||||
final int positionType = spec.getInteger(GameOptions.STARTING_POSITIONS);
|
||||
|
||||
int numAreas = 0;
|
||||
if (mapDefinedStartingPositions) {
|
||||
for (Player player : europeanPlayers) {
|
||||
final Area area = game.getNationStartingArea(player.getNation());
|
||||
if (area == null || area.getTiles().isEmpty()) {
|
||||
logger.info("No map defined starting area for: " + player.getNationId());
|
||||
if (positionType == GameOptions.STARTING_POSITIONS_CLASSIC
|
||||
|| positionType == GameOptions.STARTING_POSITIONS_HISTORICAL) {
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
numAreas++;
|
||||
}
|
||||
}
|
||||
if (numAreas < europeanPlayers.size()) {
|
||||
mapDefinedStartingPositions = false;
|
||||
}
|
||||
return mapDefinedStartingPositions;
|
||||
}
|
||||
|
||||
private java.util.Map<Player, Tile> determineStartingTilesWithoutUsingPredeterminedPositions(Map map, List<Player> europeanPlayers, java.util.Map<Player, StartingUnits> playerStartingUnits) {
|
||||
|
|
Loading…
Reference in New Issue