package games.strategy.triplea.delegate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import games.strategy.engine.data.GameData;
import games.strategy.engine.data.PlayerID;
import games.strategy.engine.data.RelationshipType;
import games.strategy.engine.data.Territory;
import games.strategy.engine.data.Unit;
import games.strategy.triplea.Constants;
import games.strategy.triplea.TripleAUnit;
import games.strategy.triplea.attachments.TerritoryAttachment;
import games.strategy.triplea.attachments.UnitAttachment;
import games.strategy.triplea.util.TransportUtils;
import games.strategy.util.CompositeMatchAnd;
import games.strategy.util.IntegerMap;
import games.strategy.util.Match;
import games.strategy.util.Triple;
/**
* Provides some static methods for validating game edits.
*/
public class EditValidator {
private static String validateTerritoryBasic(final GameData data, final Territory territory) {
final String result = null;
/*
* // territory cannot contain enemy units
* if (!Matches.territoryIsEmptyOfCombatUnits(data, player).match(territory))
* return "Territory contains enemy units";
*/
/*
* // territory cannot be in a pending battle
* BattleTracker battleTracker = DelegateFinder.battleDelegate(data).getBattleTracker();
* if (battleTracker.getPendingBattle(territory, true) != null)
* return "Territory contains a pending SBR battle";
* if (battleTracker.getPendingBattle(territory, false) != null)
* return "Territory contains a pending battle";
*/
// territory cannot be in an UndoableMove route
final List<UndoableMove> moves = DelegateFinder.moveDelegate(data).getMovesMade();
for (final UndoableMove move : moves) {
if (move.getRoute().getStart() == territory || move.getRoute().getEnd() == territory) {
return "Territory is start or end of a pending move";
}
}
return result;
}
public static String validateChangeTerritoryOwner(final GameData data, final Territory territory,
final PlayerID player) {
String result = null;
if (Matches.TerritoryIsWater.match(territory) && territory.getOwner().equals(PlayerID.NULL_PLAYERID)
&& TerritoryAttachment.get(territory) == null) {
return "Territory is water and has no attachment";
}
if ((result = validateTerritoryBasic(data, territory)) != null) {
return result;
}
return result;
}
public static String validateAddUnits(final GameData data, final Territory territory, final Collection<Unit> units) {
String result = null;
if (units.isEmpty()) {
return "No units selected";
}
final PlayerID player = units.iterator().next().getOwner();
// check land/water sanity
if (territory.isWater()) {
if (!Match.allMatch(units, Matches.UnitIsSea)) {
if (Match.someMatch(units, Matches.UnitIsLand)) {
if (!Match.allMatch(units, Matches.alliedUnit(player, data))) {
return "Can't add mixed nationality units to water";
}
final Match<Unit> friendlySeaTransports =
new CompositeMatchAnd<>(Matches.UnitIsTransport, Matches.UnitIsSea, Matches.alliedUnit(player, data));
final Collection<Unit> seaTransports = Match.getMatches(units, friendlySeaTransports);
final Collection<Unit> landUnitsToAdd = Match.getMatches(units, Matches.UnitIsLand);
if (!Match.allMatch(landUnitsToAdd, Matches.UnitCanBeTransported)) {
return "Can't add land units that can't be transported, to water";
}
seaTransports.addAll(territory.getUnits().getMatches(friendlySeaTransports));
if (seaTransports.isEmpty()) {
return "Can't add land units to water without enough transports";
}
final Map<Unit, Unit> mapLoading = TransportUtils.mapTransportsToLoad(landUnitsToAdd, seaTransports);
if (!mapLoading.keySet().containsAll(landUnitsToAdd)) {
return "Can't add land units to water without enough transports";
}
}
if (Match.someMatch(units, Matches.UnitIsAir)) {
if (Match.someMatch(units,
new CompositeMatchAnd<>(Matches.UnitIsAir, Matches.UnitCanLandOnCarrier.invert()))) {
return "Cannot add air to water unless it can land on carriers";
}
// Set up matches
final Match<Unit> friendlyCarriers =
new CompositeMatchAnd<>(Matches.UnitIsCarrier, Matches.alliedUnit(player, data));
final Match<Unit> friendlyAirUnits =
new CompositeMatchAnd<>(Matches.UnitIsAir, Matches.alliedUnit(player, data));
// Determine transport capacity
final int carrierCapacityTotal =
AirMovementValidator.carrierCapacity(territory.getUnits().getMatches(friendlyCarriers), territory)
+ AirMovementValidator.carrierCapacity(units, territory);
final int carrierCost = AirMovementValidator.carrierCost(territory.getUnits().getMatches(friendlyAirUnits))
+ AirMovementValidator.carrierCost(units);
if (carrierCapacityTotal < carrierCost) {
return "Can't add more air units to water without sufficient space";
}
}
}
} else {
/*
* // Can't add to enemy territory
* if (Matches.isTerritoryEnemy(player, data).match(territory) && !Matches.TerritoryIsWater.match(territory))
* return "Can't add units to enemy territory";
*/
if (Match.someMatch(units, Matches.UnitIsSea)) {
return "Can't add sea units to land";
}
}
if ((result = validateTerritoryBasic(data, territory)) != null) {
return result;
}
return result;
}
public static String validateRemoveUnits(final GameData data, final Territory territory,
final Collection<Unit> units) {
String result = null;
if (units.isEmpty()) {
return "No units selected";
}
/*
* all units should be same owner
* if (!Match.allMatch(units, Matches.unitIsOwnedBy(player)))
* return "Not all units have the same owner";
*/
if ((result = validateTerritoryBasic(data, territory)) != null) {
return result;
}
// if transport selected, all transported units must be deleted too
for (final Unit unit : Match.getMatches(units, Matches.UnitCanTransport)) {
if (!units.containsAll(TransportTracker.transporting(unit))) {
return "Can't remove transport without removing transported units";
}
}
// if transported units selected, transport must be deleted too
for (final Unit unit : Match.getMatches(units, Matches.UnitCanBeTransported)) {
final Unit transport = TransportTracker.transportedBy(unit);
if (transport != null && !units.contains(transport)) {
return "Can't remove transported units without removing transport";
}
}
// TODO: if carrier selected, all carried planes must be deleted too
// TODO: if carried planes selected, carrier must be deleted too
return result;
}
public static String validateAddTech(final GameData data, final Collection<TechAdvance> techs,
final PlayerID player) {
final String result = null;
if (techs == null) {
return "No tech selected";
}
if (player == null) {
return "No player selected";
}
if (!games.strategy.triplea.Properties.getTechDevelopment(data)) {
return "Technology not enabled";
}
if (player.getAttachment(Constants.TECH_ATTACHMENT_NAME) == null) {
return "Player has no Tech Attachment";
}
for (final TechAdvance tech : techs) {
if (tech == null) {
return "No tech selected";
}
if (!TechnologyDelegate.getAvailableTechs(player, data).contains(tech)) {
return "Technology not available for this player";
}
}
return result;
}
public static String validateRemoveTech(final GameData data, final Collection<TechAdvance> techs,
final PlayerID player) {
final String result = null;
if (techs == null) {
return "No tech selected";
}
if (player == null) {
return "No player selected";
}
if (!games.strategy.triplea.Properties.getTechDevelopment(data)) {
return "Technology not enabled";
}
for (final TechAdvance tech : techs) {
if (tech == null) {
return "No tech selected";
}
if (!TechTracker.getCurrentTechAdvances(player, data).contains(tech)) {
return "Player does not have this tech";
}
if (tech.getProperty().equals(TechAdvance.TECH_PROPERTY_INDUSTRIAL_TECHNOLOGY)) {
return "Cannot remove " + TechAdvance.TECH_NAME_INDUSTRIAL_TECHNOLOGY;
}
if (tech.getProperty().equals(TechAdvance.TECH_PROPERTY_IMPROVED_SHIPYARDS)) {
return "Cannot remove " + TechAdvance.TECH_NAME_IMPROVED_SHIPYARDS;
}
}
return result;
}
public static String validateChangeHitDamage(final GameData data, final IntegerMap<Unit> unitDamageMap,
final Territory territory) {
String result = null;
if (unitDamageMap == null || unitDamageMap.isEmpty()) {
return "Damage map is empty";
}
if ((result = validateTerritoryBasic(data, territory)) != null) {
return result;
}
final Collection<Unit> units = new ArrayList<>(unitDamageMap.keySet());
if (!territory.getUnits().getUnits().containsAll(units)) {
return "Selected Territory does not contain all of the selected units";
}
final PlayerID player = units.iterator().next().getOwner();
// all units should be same owner
if (!Match.allMatch(units, Matches.unitIsOwnedBy(player))) {
return "Not all units have the same owner";
}
if (!Match.allMatch(units, Matches.UnitHasMoreThanOneHitPointTotal)) {
return "Not all units have more than one total hitpoints";
}
for (final Unit u : units) {
final int dmg = unitDamageMap.getInt(u);
if (dmg < 0 || dmg >= UnitAttachment.get(u.getType()).getHitPoints()) {
return "Damage cannot be less than zero or equal to or greater than unit hitpoints (if you want to kill the "
+ "unit, use remove unit)";
}
}
return result;
}
public static String validateChangeBombingDamage(final GameData data, final IntegerMap<Unit> unitDamageMap,
final Territory territory) {
String result = null;
if (unitDamageMap == null || unitDamageMap.isEmpty()) {
return "Damage map is empty";
}
if ((result = validateTerritoryBasic(data, territory)) != null) {
return result;
}
if (!games.strategy.triplea.Properties.getDamageFromBombingDoneToUnitsInsteadOfTerritories(data)) {
return "Game does not allow bombing damage";
}
final Collection<Unit> units = new ArrayList<>(unitDamageMap.keySet());
if (!territory.getUnits().getUnits().containsAll(units)) {
return "Selected Territory does not contain all of the selected units";
}
final PlayerID player = units.iterator().next().getOwner();
// all units should be same owner
if (!Match.allMatch(units, Matches.unitIsOwnedBy(player))) {
return "Not all units have the same owner";
}
if (!Match.allMatch(units, Matches.UnitCanBeDamaged)) {
return "Not all units can take bombing damage";
}
for (final Unit u : units) {
final int dmg = unitDamageMap.getInt(u);
if (dmg < 0 || dmg > ((TripleAUnit) u).getHowMuchDamageCanThisUnitTakeTotal(u, territory)) {
return "Damage cannot be less than zero or greater than the max damage of the unit";
}
}
return result;
}
public static String validateChangePoliticalRelationships(final GameData data,
final Collection<Triple<PlayerID, PlayerID, RelationshipType>> relationshipChanges) {
final String result = null;
if (relationshipChanges == null || relationshipChanges.isEmpty()) {
return "Relationship Changes are empty";
}
for (final Triple<PlayerID, PlayerID, RelationshipType> relationshipChange : relationshipChanges) {
if (relationshipChange.getFirst() == null || relationshipChange.getSecond() == null) {
return "Players are null";
}
if (relationshipChange.getThird() == null) {
return "New Relationship is null";
}
}
return result;
}
}