use of games.strategy.engine.data.Unit in project triplea by triplea-game.
the class MoveValidator method carrierMustMoveWith.
static Map<Unit, Collection<Unit>> carrierMustMoveWith(final Collection<Unit> units, final Collection<Unit> startUnits, final GameData data, final PlayerID player) {
// we want to get all air units that are owned by our allies but not us that can land on a carrier
final Predicate<Unit> friendlyNotOwnedAir = Matches.alliedUnit(player, data).and(Matches.unitIsOwnedBy(player).negate()).and(Matches.unitCanLandOnCarrier());
final Collection<Unit> alliedAir = CollectionUtils.getMatches(startUnits, friendlyNotOwnedAir);
if (alliedAir.isEmpty()) {
return Collections.emptyMap();
}
// remove air that can be carried by allied
final Predicate<Unit> friendlyNotOwnedCarrier = Matches.unitIsCarrier().and(Matches.alliedUnit(player, data)).and(Matches.unitIsOwnedBy(player).negate());
final Collection<Unit> alliedCarrier = CollectionUtils.getMatches(startUnits, friendlyNotOwnedCarrier);
for (final Unit carrier : alliedCarrier) {
final Collection<Unit> carrying = getCanCarry(carrier, alliedAir, player, data);
alliedAir.removeAll(carrying);
}
if (alliedAir.isEmpty()) {
return Collections.emptyMap();
}
final Map<Unit, Collection<Unit>> mapping = new HashMap<>();
// get air that must be carried by our carriers
final Collection<Unit> ownedCarrier = CollectionUtils.getMatches(units, Matches.unitIsCarrier().and(Matches.unitIsOwnedBy(player)));
for (final Unit carrier : ownedCarrier) {
final Collection<Unit> carrying = getCanCarry(carrier, alliedAir, player, data);
alliedAir.removeAll(carrying);
mapping.put(carrier, carrying);
}
return mapping;
}
use of games.strategy.engine.data.Unit in project triplea by triplea-game.
the class MoveValidator method validateTransport.
private static MoveValidationResult validateTransport(final boolean isNonCombat, final GameData data, final List<UndoableMove> undoableMoves, final Collection<Unit> units, final Route route, final PlayerID player, final Collection<Unit> transportsToLoad, final MoveValidationResult result) {
final boolean isEditMode = getEditMode(data);
if (!units.isEmpty() && units.stream().allMatch(Matches.unitIsAir())) {
return result;
}
if (!route.hasWater()) {
return result;
}
// If there are non-sea transports return
final boolean seaOrNoTransportsPresent = transportsToLoad.isEmpty() || transportsToLoad.stream().anyMatch(Matches.unitIsSea().and(Matches.unitCanTransport()));
if (!seaOrNoTransportsPresent) {
return result;
}
final Territory routeEnd = route.getEnd();
final Territory routeStart = route.getStart();
// if unloading make sure length of route is only 1
if (!isEditMode && route.isUnload()) {
if (route.hasMoreThenOneStep()) {
return result.setErrorReturnResult("Unloading units must stop where they are unloaded");
}
for (final Unit unit : TransportTracker.getUnitsLoadedOnAlliedTransportsThisTurn(units)) {
result.addDisallowedUnit(CANNOT_LOAD_AND_UNLOAD_AN_ALLIED_TRANSPORT_IN_THE_SAME_ROUND, unit);
}
final Collection<Unit> transports = TransportUtils.mapTransports(route, units, null).values();
final boolean isScramblingOrKamikazeAttacksEnabled = Properties.getScrambleRulesInEffect(data) || Properties.getUseKamikazeSuicideAttacks(data);
final boolean submarinesPreventUnescortedAmphibAssaults = Properties.getSubmarinesPreventUnescortedAmphibiousAssaults(data);
final Predicate<Unit> enemySubmarineMatch = Matches.unitIsEnemyOf(data, player).and(Matches.unitIsSub());
final Predicate<Unit> ownedSeaNonTransportMatch = Matches.unitIsOwnedBy(player).and(Matches.unitIsSea()).and(Matches.unitIsNotTransportButCouldBeCombatTransport());
for (final Unit transport : transports) {
if (!isNonCombat && route.numberOfStepsIncludingStart() == 2) {
if (Matches.territoryHasEnemyUnits(player, data).test(routeEnd) || Matches.isTerritoryEnemyAndNotUnownedWater(player, data).test(routeEnd)) {
// this is an amphibious assault
if (submarinesPreventUnescortedAmphibAssaults && !Matches.territoryHasUnitsThatMatch(ownedSeaNonTransportMatch).test(routeStart) && Matches.territoryHasUnitsThatMatch(enemySubmarineMatch).test(routeStart)) {
// stops our unloading for amphibious assault
for (final Unit unit : TransportTracker.transporting(transport)) {
result.addDisallowedUnit(ENEMY_SUBMARINE_PREVENTING_UNESCORTED_AMPHIBIOUS_ASSAULT_LANDING, unit);
}
}
} else if (!AbstractMoveDelegate.getBattleTracker(data).wasConquered(routeEnd)) {
// this is an unload to a friendly territory
if (isScramblingOrKamikazeAttacksEnabled || !Matches.territoryIsEmptyOfCombatUnits(data, player).test(routeStart)) {
// TODO: should we use the battle tracker for this instead?
for (final Unit unit : TransportTracker.transporting(transport)) {
result.addDisallowedUnit(TRANSPORT_MAY_NOT_UNLOAD_TO_FRIENDLY_TERRITORIES_UNTIL_AFTER_COMBAT_IS_RESOLVED, unit);
}
}
}
}
// in which they perform their actions. check whether transport has already unloaded
if (TransportTracker.hasTransportUnloadedInPreviousPhase(transport)) {
for (final Unit unit : TransportTracker.transporting(transport)) {
result.addDisallowedUnit(TRANSPORT_HAS_ALREADY_UNLOADED_UNITS_IN_A_PREVIOUS_PHASE, unit);
}
// check whether transport is restricted to another territory
} else if (TransportTracker.isTransportUnloadRestrictedToAnotherTerritory(transport, route.getEnd())) {
final Territory alreadyUnloadedTo = getTerritoryTransportHasUnloadedTo(undoableMoves, transport);
for (final Unit unit : TransportTracker.transporting(transport)) {
result.addDisallowedUnit(TRANSPORT_HAS_ALREADY_UNLOADED_UNITS_TO + alreadyUnloadedTo.getName(), unit);
}
// Check if the transport has already loaded after being in combat
} else if (TransportTracker.isTransportUnloadRestrictedInNonCombat(transport)) {
for (final Unit unit : TransportTracker.transporting(transport)) {
result.addDisallowedUnit(TRANSPORT_CANNOT_LOAD_AND_UNLOAD_AFTER_COMBAT, unit);
}
}
}
}
// if we are land make sure no water in route except for transport situations
final Collection<Unit> land = CollectionUtils.getMatches(units, Matches.unitIsLand());
final Collection<Unit> landAndAir = CollectionUtils.getMatches(units, Matches.unitIsLand().or(Matches.unitIsAir()));
// make sure we can be transported
final Predicate<Unit> cantBeTransported = Matches.unitCanBeTransported().negate();
for (final Unit unit : CollectionUtils.getMatches(land, cantBeTransported)) {
result.addDisallowedUnit("Not all units can be transported", unit);
}
// make sure that the only the first or last territory is land don't want situation where they go sea land sea
if (!isEditMode && route.hasLand() && !(route.getStart().isWater() || route.getEnd().isWater())) {
// carried by the air and that the air has enough capacity
if (nonParatroopersPresent(player, landAndAir)) {
return result.setErrorReturnResult("Invalid move, only start or end can be land when route has water.");
}
}
// TODO handle this
if (!isEditMode && !route.getEnd().isWater() && !route.getStart().isWater() && nonParatroopersPresent(player, landAndAir)) {
return result.setErrorReturnResult("Must stop units at a transport on route");
}
if (route.getEnd().isWater() && route.getStart().isWater()) {
// make sure units and transports stick together
for (final Unit unit : units) {
final UnitAttachment ua = UnitAttachment.get(unit.getType());
// make sure transports dont leave their units behind
if (ua.getTransportCapacity() != -1) {
final Collection<Unit> holding = TransportTracker.transporting(unit);
if (!units.containsAll(holding)) {
result.addDisallowedUnit("Transports cannot leave their units", unit);
}
}
// make sure units dont leave their transports behind
if (ua.getTransportCost() != -1) {
final Unit transport = TransportTracker.transportedBy(unit);
if (transport != null && !units.contains(transport)) {
result.addDisallowedUnit("Unit must stay with its transport while moving", unit);
}
}
}
}
if (route.isLoad()) {
if (!isEditMode && !route.hasExactlyOneStep() && nonParatroopersPresent(player, landAndAir)) {
return result.setErrorReturnResult("Units cannot move before loading onto transports");
}
final Predicate<Unit> enemyNonSubmerged = Matches.enemyUnit(player, data).and(Matches.unitIsSubmerged().negate());
if (!Properties.getUnitsCanLoadInHostileSeaZones(data) && route.getEnd().getUnits().anyMatch(enemyNonSubmerged) && nonParatroopersPresent(player, landAndAir) && !onlyIgnoredUnitsOnPath(route, player, data, false) && !AbstractMoveDelegate.getBattleTracker(data).didAllThesePlayersJustGoToWarThisTurn(player, route.getEnd().getUnits().getUnits(), data)) {
return result.setErrorReturnResult("Cannot load when enemy sea units are present");
}
final Map<Unit, Unit> unitsToTransports = TransportUtils.mapTransports(route, land, transportsToLoad);
if (!isEditMode) {
for (final Unit baseUnit : land) {
final TripleAUnit unit = (TripleAUnit) baseUnit;
if (Matches.unitHasMoved().test(unit)) {
result.addDisallowedUnit("Units cannot move before loading onto transports", unit);
}
final Unit transport = unitsToTransports.get(unit);
if (transport == null) {
continue;
}
if (TransportTracker.hasTransportUnloadedInPreviousPhase(transport)) {
result.addDisallowedUnit(TRANSPORT_HAS_ALREADY_UNLOADED_UNITS_IN_A_PREVIOUS_PHASE, unit);
} else if (TransportTracker.isTransportUnloadRestrictedToAnotherTerritory(transport, route.getEnd())) {
Territory alreadyUnloadedTo = getTerritoryTransportHasUnloadedTo(undoableMoves, transport);
for (final Unit transportToLoad : transportsToLoad) {
final TripleAUnit trn = (TripleAUnit) transportToLoad;
if (!TransportTracker.isTransportUnloadRestrictedToAnotherTerritory(trn, route.getEnd())) {
final UnitAttachment ua = UnitAttachment.get(unit.getType());
if (TransportTracker.getAvailableCapacity(trn) >= ua.getTransportCost()) {
alreadyUnloadedTo = null;
break;
}
}
}
if (alreadyUnloadedTo != null) {
result.addDisallowedUnit(TRANSPORT_HAS_ALREADY_UNLOADED_UNITS_TO + alreadyUnloadedTo.getName(), unit);
}
}
}
}
if (!unitsToTransports.keySet().containsAll(land)) {
// some units didn't get mapped to a transport
final Collection<UnitCategory> unitsToLoadCategories = UnitSeperator.categorize(land);
if (unitsToTransports.size() == 0 || unitsToLoadCategories.size() == 1) {
// set all unmapped units as disallowed if there are no transports or only one unit category
for (final Unit unit : land) {
if (unitsToTransports.containsKey(unit)) {
continue;
}
final UnitAttachment ua = UnitAttachment.get(unit.getType());
if (ua.getTransportCost() != -1) {
result.addDisallowedUnit("Not enough transports", unit);
}
}
} else {
// set all units as unresolved if there is at least one transport and mixed unit categories
for (final Unit unit : land) {
final UnitAttachment ua = UnitAttachment.get(unit.getType());
if (ua.getTransportCost() != -1) {
result.addUnresolvedUnit("Not enough transports", unit);
}
}
}
}
}
return result;
}
use of games.strategy.engine.data.Unit in project triplea by triplea-game.
the class MustFightBattle method markNoMovementLeft.
private void markNoMovementLeft(final IDelegateBridge bridge) {
if (m_headless) {
return;
}
final Collection<Unit> attackingNonAir = CollectionUtils.getMatches(m_attackingUnits, Matches.unitIsAir().negate());
final Change noMovementChange = ChangeFactory.markNoMovementChange(attackingNonAir);
if (!noMovementChange.isEmpty()) {
bridge.addChange(noMovementChange);
}
}
use of games.strategy.engine.data.Unit in project triplea by triplea-game.
the class MustFightBattle method fire.
private void fire(final String stepName, final Collection<Unit> firingUnits, final Collection<Unit> attackableUnits, final List<Unit> allEnemyUnitsAliveOrWaitingToDie, final boolean defender, final ReturnFire returnFire, final String text) {
final PlayerID firing = defender ? m_defender : m_attacker;
final PlayerID defending = !defender ? m_defender : m_attacker;
if (firingUnits.isEmpty()) {
return;
}
// Fire each type of suicide on hit unit separately and then remaining units
final List<Collection<Unit>> firingGroups = createFiringUnitGroups(firingUnits);
for (final Collection<Unit> units : firingGroups) {
m_stack.push(new Fire(attackableUnits, returnFire, firing, defending, units, stepName, text, this, defender, m_dependentUnits, m_headless, m_battleSite, m_territoryEffects, allEnemyUnitsAliveOrWaitingToDie));
}
}
use of games.strategy.engine.data.Unit in project triplea by triplea-game.
the class MustFightBattle method retreatUnits.
private void retreatUnits(Collection<Unit> retreating, final Territory to, final boolean defender, final IDelegateBridge bridge) {
retreating.addAll(getDependentUnits(retreating));
// our own air units don't retreat with land units
final Predicate<Unit> notMyAir = Matches.unitIsNotAir().or(Matches.unitIsOwnedBy(m_attacker).negate());
retreating = CollectionUtils.getMatches(retreating, notMyAir);
final String transcriptText;
// in WW2V1, defending subs can retreat so show owner
if (isWW2V2()) {
transcriptText = MyFormatter.unitsToTextNoOwner(retreating) + " retreated to " + to.getName();
} else {
transcriptText = MyFormatter.unitsToText(retreating) + " retreated to " + to.getName();
}
bridge.getHistoryWriter().addChildToEvent(transcriptText, new ArrayList<>(retreating));
final CompositeChange change = new CompositeChange();
change.add(ChangeFactory.moveUnits(m_battleSite, to, retreating));
if (m_isOver) {
final Collection<IBattle> dependentBattles = m_battleTracker.getBlocked(this);
// If there are no dependent battles, check landings in allied territories
if (dependentBattles.isEmpty()) {
change.add(retreatFromNonCombat(retreating, to));
// Else retreat the units from combat when their transport retreats
} else {
change.add(retreatFromDependents(retreating, to, dependentBattles));
}
}
bridge.addChange(change);
final Collection<Unit> units = defender ? m_defendingUnits : m_attackingUnits;
final Collection<Unit> unitsRetreated = defender ? m_defendingUnitsRetreated : m_attackingUnitsRetreated;
units.removeAll(retreating);
unitsRetreated.addAll(retreating);
if (units.isEmpty() || m_isOver) {
endBattle(bridge);
if (defender) {
attackerWins(bridge);
} else {
defenderWins(bridge);
}
} else {
getDisplay(bridge).notifyRetreat(m_battleID, retreating);
}
}
Aggregations