Search in sources :

Example 1 with GameData

use of games.strategy.engine.data.GameData in project triplea by triplea-game.

the class BattleCalculator method getAaCasualties.

/**
 * Choose plane casualties according to specified rules.
 */
static CasualtyDetails getAaCasualties(final boolean defending, final Collection<Unit> planes, final Collection<Unit> allFriendlyUnits, final Collection<Unit> defendingAa, final Collection<Unit> allEnemyUnits, final DiceRoll dice, final IDelegateBridge bridge, final PlayerID firingPlayer, final PlayerID hitPlayer, final GUID battleId, final Territory terr, final Collection<TerritoryEffect> territoryEffects, final boolean amphibious, final Collection<Unit> amphibiousLandAttackers) {
    if (planes.isEmpty()) {
        return new CasualtyDetails();
    }
    final GameData data = bridge.getData();
    final boolean allowMultipleHitsPerUnit = !defendingAa.isEmpty() && defendingAa.stream().allMatch(Matches.unitAaShotDamageableInsteadOfKillingInstantly());
    if (isChooseAa(data)) {
        final String text = "Select " + dice.getHits() + " casualties from aa fire in " + terr.getName();
        return selectCasualties(null, hitPlayer, planes, allFriendlyUnits, firingPlayer, allEnemyUnits, amphibious, amphibiousLandAttackers, terr, territoryEffects, bridge, text, dice, defending, battleId, false, dice.getHits(), allowMultipleHitsPerUnit);
    }
    if (Properties.getLowLuck(data) || Properties.getLowLuckAaOnly(data)) {
        return getLowLuckAaCasualties(defending, planes, defendingAa, dice, bridge, allowMultipleHitsPerUnit);
    }
    // if none are set, we roll individually
    if (isRollAaIndividually(data)) {
        return individuallyFiredAaCasualties(defending, planes, defendingAa, dice, bridge, allowMultipleHitsPerUnit);
    }
    if (isRandomAaCasualties(data)) {
        return randomAaCasualties(planes, dice, bridge, allowMultipleHitsPerUnit);
    }
    return individuallyFiredAaCasualties(defending, planes, defendingAa, dice, bridge, allowMultipleHitsPerUnit);
}
Also used : GameData(games.strategy.engine.data.GameData) CasualtyDetails(games.strategy.triplea.delegate.dataObjects.CasualtyDetails)

Example 2 with GameData

use of games.strategy.engine.data.GameData in project triplea by triplea-game.

the class BattleCalculator method selectCasualties.

/**
 * @param battleId
 *        may be null if we are not in a battle (eg, if this is an aa fire due to moving).
 */
public static CasualtyDetails selectCasualties(final String step, final PlayerID player, final Collection<Unit> targetsToPickFrom, final Collection<Unit> friendlyUnits, final PlayerID enemyPlayer, final Collection<Unit> enemyUnits, final boolean amphibious, final Collection<Unit> amphibiousLandAttackers, final Territory battlesite, final Collection<TerritoryEffect> territoryEffects, final IDelegateBridge bridge, final String text, final DiceRoll dice, final boolean defending, final GUID battleId, final boolean headLess, final int extraHits, final boolean allowMultipleHitsPerUnit) {
    if (targetsToPickFrom.isEmpty()) {
        return new CasualtyDetails();
    }
    if (!friendlyUnits.containsAll(targetsToPickFrom)) {
        throw new IllegalStateException("friendlyUnits should but does not contain all units from targetsToPickFrom");
    }
    final GameData data = bridge.getData();
    final boolean isEditMode = BaseEditDelegate.getEditMode(data);
    final ITripleAPlayer tripleaPlayer = player.isNull() ? new WeakAi(player.getName(), TripleA.WEAK_COMPUTER_PLAYER_TYPE) : (ITripleAPlayer) bridge.getRemotePlayer(player);
    final Map<Unit, Collection<Unit>> dependents = headLess ? Collections.emptyMap() : getDependents(targetsToPickFrom);
    if (isEditMode && !headLess) {
        final CasualtyDetails editSelection = tripleaPlayer.selectCasualties(targetsToPickFrom, dependents, 0, text, dice, player, friendlyUnits, enemyPlayer, enemyUnits, amphibious, amphibiousLandAttackers, new CasualtyList(), battleId, battlesite, allowMultipleHitsPerUnit);
        final List<Unit> killed = editSelection.getKilled();
        // if partial retreat is possible, kill amphibious units first
        if (isPartialAmphibiousRetreat(data)) {
            killAmphibiousFirst(killed, targetsToPickFrom);
        }
        return editSelection;
    }
    if (dice.getHits() == 0) {
        return new CasualtyDetails(Collections.emptyList(), Collections.emptyList(), true);
    }
    int hitsRemaining = dice.getHits();
    if (isTransportCasualtiesRestricted(data)) {
        hitsRemaining = extraHits;
    }
    if (!isEditMode && allTargetsOneTypeOneHitPoint(targetsToPickFrom, dependents)) {
        final List<Unit> killed = new ArrayList<>();
        final Iterator<Unit> iter = targetsToPickFrom.iterator();
        for (int i = 0; i < hitsRemaining; i++) {
            if (i >= targetsToPickFrom.size()) {
                break;
            }
            killed.add(iter.next());
        }
        return new CasualtyDetails(killed, Collections.emptyList(), true);
    }
    // Create production cost map, Maybe should do this elsewhere, but in case prices change, we do it here.
    final IntegerMap<UnitType> costs = TuvUtils.getCostsForTuv(player, data);
    final Tuple<CasualtyList, List<Unit>> defaultCasualtiesAndSortedTargets = getDefaultCasualties(targetsToPickFrom, hitsRemaining, defending, player, enemyUnits, amphibious, amphibiousLandAttackers, battlesite, costs, territoryEffects, data, allowMultipleHitsPerUnit, true);
    final CasualtyList defaultCasualties = defaultCasualtiesAndSortedTargets.getFirst();
    final List<Unit> sortedTargetsToPickFrom = defaultCasualtiesAndSortedTargets.getSecond();
    if (sortedTargetsToPickFrom.size() != targetsToPickFrom.size() || !targetsToPickFrom.containsAll(sortedTargetsToPickFrom) || !sortedTargetsToPickFrom.containsAll(targetsToPickFrom)) {
        throw new IllegalStateException("sortedTargetsToPickFrom must contain the same units as targetsToPickFrom list");
    }
    final int totalHitpoints = (allowMultipleHitsPerUnit ? getTotalHitpointsLeft(sortedTargetsToPickFrom) : sortedTargetsToPickFrom.size());
    final CasualtyDetails casualtySelection;
    if (hitsRemaining >= totalHitpoints) {
        casualtySelection = new CasualtyDetails(defaultCasualties, true);
    } else {
        casualtySelection = tripleaPlayer.selectCasualties(sortedTargetsToPickFrom, dependents, hitsRemaining, text, dice, player, friendlyUnits, enemyPlayer, enemyUnits, amphibious, amphibiousLandAttackers, defaultCasualties, battleId, battlesite, allowMultipleHitsPerUnit);
    }
    List<Unit> killed = casualtySelection.getKilled();
    // if partial retreat is possible, kill amphibious units first
    if (isPartialAmphibiousRetreat(data)) {
        killed = killAmphibiousFirst(killed, sortedTargetsToPickFrom);
    }
    final List<Unit> damaged = casualtySelection.getDamaged();
    int numhits = killed.size();
    if (!allowMultipleHitsPerUnit) {
        damaged.clear();
    } else {
        for (final Unit unit : killed) {
            final UnitAttachment ua = UnitAttachment.get(unit.getType());
            final int damageToUnit = Collections.frequency(damaged, unit);
            // allowed damage
            numhits += Math.max(0, Math.min(damageToUnit, (ua.getHitPoints() - (1 + unit.getHits()))));
            // remove from damaged list, since they will die
            damaged.removeIf(unit::equals);
        }
    }
    // check right number
    if (!isEditMode && !(numhits + damaged.size() == (hitsRemaining > totalHitpoints ? totalHitpoints : hitsRemaining))) {
        tripleaPlayer.reportError("Wrong number of casualties selected");
        if (headLess) {
            System.err.println("Possible Infinite Loop: Wrong number of casualties selected: number of hits on units " + (numhits + damaged.size()) + " != number of hits to take " + (hitsRemaining > totalHitpoints ? totalHitpoints : hitsRemaining) + ", for " + casualtySelection.toString());
        }
        return selectCasualties(step, player, sortedTargetsToPickFrom, friendlyUnits, enemyPlayer, enemyUnits, amphibious, amphibiousLandAttackers, battlesite, territoryEffects, bridge, text, dice, defending, battleId, headLess, extraHits, allowMultipleHitsPerUnit);
    }
    // check we have enough of each type
    if (!sortedTargetsToPickFrom.containsAll(killed) || !sortedTargetsToPickFrom.containsAll(damaged)) {
        tripleaPlayer.reportError("Cannot remove enough units of those types");
        if (headLess) {
            System.err.println("Possible Infinite Loop: Cannot remove enough units of those types: targets " + MyFormatter.unitsToTextNoOwner(sortedTargetsToPickFrom) + ", for " + casualtySelection.toString());
        }
        return selectCasualties(step, player, sortedTargetsToPickFrom, friendlyUnits, enemyPlayer, enemyUnits, amphibious, amphibiousLandAttackers, battlesite, territoryEffects, bridge, text, dice, defending, battleId, headLess, extraHits, allowMultipleHitsPerUnit);
    }
    return casualtySelection;
}
Also used : GameData(games.strategy.engine.data.GameData) ArrayList(java.util.ArrayList) Unit(games.strategy.engine.data.Unit) ITripleAPlayer(games.strategy.triplea.player.ITripleAPlayer) UnitAttachment(games.strategy.triplea.attachments.UnitAttachment) WeakAi(games.strategy.triplea.ai.weak.WeakAi) CasualtyList(games.strategy.triplea.delegate.dataObjects.CasualtyList) UnitType(games.strategy.engine.data.UnitType) Collection(java.util.Collection) CasualtyDetails(games.strategy.triplea.delegate.dataObjects.CasualtyDetails) ArrayList(java.util.ArrayList) CasualtyList(games.strategy.triplea.delegate.dataObjects.CasualtyList) List(java.util.List)

Example 3 with GameData

use of games.strategy.engine.data.GameData in project triplea by triplea-game.

the class BattleDelegate method scramblingCleanup.

private void scramblingCleanup() {
    // return scrambled units to their original territories, or let them move 1 or x to a new territory.
    final GameData data = getData();
    if (!Properties.getScrambleRulesInEffect(data)) {
        return;
    }
    final boolean mustReturnToBase = Properties.getScrambledUnitsReturnToBase(data);
    for (final Territory t : data.getMap().getTerritories()) {
        int carrierCostOfCurrentTerr = 0;
        final Collection<Unit> wasScrambled = t.getUnits().getMatches(Matches.unitWasScrambled());
        for (final Unit u : wasScrambled) {
            final CompositeChange change = new CompositeChange();
            final Territory originatedFrom = TripleAUnit.get(u).getOriginatedFrom();
            Territory landingTerr = null;
            final String historyText;
            if (!mustReturnToBase || !Matches.isTerritoryAllied(u.getOwner(), data).test(originatedFrom)) {
                final Collection<Territory> possible = whereCanAirLand(Collections.singletonList(u), t, u.getOwner(), data, battleTracker, carrierCostOfCurrentTerr, 1, !mustReturnToBase);
                if (possible.size() > 1) {
                    landingTerr = getRemotePlayer(u.getOwner()).selectTerritoryForAirToLand(possible, t, "Select territory for air units to land. (Current territory is " + t.getName() + "): " + MyFormatter.unitsToText(Collections.singletonList(u)));
                } else if (possible.size() == 1) {
                    landingTerr = possible.iterator().next();
                }
                if (landingTerr == null || landingTerr.equals(t)) {
                    carrierCostOfCurrentTerr += AirMovementValidator.carrierCost(Collections.singletonList(u));
                    historyText = "Scrambled unit stays in territory " + t.getName();
                } else {
                    historyText = "Moving scrambled unit from " + t.getName() + " to " + landingTerr.getName();
                }
            } else {
                landingTerr = originatedFrom;
                historyText = "Moving scrambled unit from " + t.getName() + " back to originating territory: " + landingTerr.getName();
            }
            // if null, we leave it to die
            if (landingTerr != null) {
                change.add(ChangeFactory.moveUnits(t, landingTerr, Collections.singletonList(u)));
                change.add(Route.getFuelChanges(Collections.singleton(u), new Route(t, landingTerr), u.getOwner(), data));
            }
            change.add(ChangeFactory.unitPropertyChange(u, null, TripleAUnit.ORIGINATED_FROM));
            change.add(ChangeFactory.unitPropertyChange(u, false, TripleAUnit.WAS_SCRAMBLED));
            if (!change.isEmpty()) {
                bridge.getHistoryWriter().startEvent(historyText, u);
                bridge.addChange(change);
            }
        }
    }
}
Also used : Territory(games.strategy.engine.data.Territory) GameData(games.strategy.engine.data.GameData) TripleAUnit(games.strategy.triplea.TripleAUnit) Unit(games.strategy.engine.data.Unit) CompositeChange(games.strategy.engine.data.CompositeChange) Route(games.strategy.engine.data.Route)

Example 4 with GameData

use of games.strategy.engine.data.GameData in project triplea by triplea-game.

the class BattleDelegate method doKamikazeSuicideAttacks.

/**
 * KamikazeSuicideAttacks are attacks that are made during an Opponent's turn, using Resources that you own that have
 * been designated.
 * The resources are designated in PlayerAttachment, and hold information like the attack power of the resource.
 * KamikazeSuicideAttacks are done in any territory that is a kamikazeZone, and the attacks are done by the original
 * owner of that
 * territory.
 * The user has the option not to do any attacks, and they make target any number of units with any number of resource
 * tokens.
 * The units are then attacked individually by each resource token (meaning that casualties do not get selected
 * because the attacks are
 * targeted).
 * The enemies of current player should decide all their attacks before the attacks are rolled.
 */
private void doKamikazeSuicideAttacks() {
    final GameData data = getData();
    if (!Properties.getUseKamikazeSuicideAttacks(data)) {
        return;
    }
    // the current player is not the one who is doing these attacks, it is the all the enemies of this player who will
    // do attacks
    final Collection<PlayerID> enemies = CollectionUtils.getMatches(data.getPlayerList().getPlayers(), Matches.isAtWar(player, data));
    if (enemies.isEmpty()) {
        return;
    }
    final Predicate<Unit> canBeAttackedDefault = Matches.unitIsOwnedBy(player).and(Matches.unitIsSea()).and(Matches.unitIsNotTransportButCouldBeCombatTransport()).and(Matches.unitIsNotSub());
    final boolean onlyWhereThereAreBattlesOrAmphibious = Properties.getKamikazeSuicideAttacksOnlyWhereBattlesAre(data);
    final Collection<Territory> pendingBattles = battleTracker.getPendingBattleSites(false);
    // create a list of all kamikaze zones, listed by enemy
    final Map<PlayerID, Collection<Territory>> kamikazeZonesByEnemy = new HashMap<>();
    for (final Territory t : data.getMap().getTerritories()) {
        final TerritoryAttachment ta = TerritoryAttachment.get(t);
        if (ta == null || !ta.getKamikazeZone()) {
            continue;
        }
        final PlayerID owner = !Properties.getKamikazeSuicideAttacksDoneByCurrentTerritoryOwner(data) ? ta.getOriginalOwner() : t.getOwner();
        if (owner == null) {
            continue;
        }
        if (enemies.contains(owner)) {
            if (t.getUnits().getUnits().stream().noneMatch(Matches.unitIsOwnedBy(player))) {
                continue;
            }
            if (onlyWhereThereAreBattlesOrAmphibious) {
                // if no battle or amphibious from here, ignore it
                if (!pendingBattles.contains(t)) {
                    if (!Matches.territoryIsWater().test(t)) {
                        continue;
                    }
                    boolean amphib = false;
                    final Collection<Territory> landNeighbors = data.getMap().getNeighbors(t, Matches.territoryIsLand());
                    for (final Territory neighbor : landNeighbors) {
                        final IBattle battle = battleTracker.getPendingBattle(neighbor, false, BattleType.NORMAL);
                        if (battle == null) {
                            final Map<Territory, Collection<Unit>> whereFrom = battleTracker.getFinishedBattlesUnitAttackFromMap().get(neighbor);
                            if (whereFrom != null && whereFrom.containsKey(t)) {
                                amphib = true;
                                break;
                            }
                            continue;
                        }
                        if (battle.isAmphibious() && ((battle instanceof MustFightBattle && ((MustFightBattle) battle).getAmphibiousAttackTerritories().contains(t)) || (battle instanceof NonFightingBattle && ((NonFightingBattle) battle).getAmphibiousAttackTerritories().contains(t)))) {
                            amphib = true;
                            break;
                        }
                    }
                    if (!amphib) {
                        continue;
                    }
                }
            }
            final Collection<Territory> currentTerrs = kamikazeZonesByEnemy.getOrDefault(owner, new ArrayList<>());
            currentTerrs.add(t);
            kamikazeZonesByEnemy.put(owner, currentTerrs);
        }
    }
    if (kamikazeZonesByEnemy.isEmpty()) {
        return;
    }
    for (final Entry<PlayerID, Collection<Territory>> entry : kamikazeZonesByEnemy.entrySet()) {
        final PlayerID currentEnemy = entry.getKey();
        final PlayerAttachment pa = PlayerAttachment.get(currentEnemy);
        if (pa == null) {
            continue;
        }
        Predicate<Unit> canBeAttacked = canBeAttackedDefault;
        final Set<UnitType> suicideAttackTargets = pa.getSuicideAttackTargets();
        if (suicideAttackTargets != null) {
            canBeAttacked = Matches.unitIsOwnedBy(player).and(Matches.unitIsOfTypes(suicideAttackTargets));
        }
        // See if the player has any attack tokens
        final IntegerMap<Resource> resourcesAndAttackValues = pa.getSuicideAttackResources();
        if (resourcesAndAttackValues.size() <= 0) {
            continue;
        }
        final IntegerMap<Resource> playerResourceCollection = currentEnemy.getResources().getResourcesCopy();
        final IntegerMap<Resource> attackTokens = new IntegerMap<>();
        for (final Resource possible : resourcesAndAttackValues.keySet()) {
            final int amount = playerResourceCollection.getInt(possible);
            if (amount > 0) {
                attackTokens.put(possible, amount);
            }
        }
        if (attackTokens.size() <= 0) {
            continue;
        }
        // now let the enemy decide if they will do attacks
        final Collection<Territory> kamikazeZones = entry.getValue();
        final HashMap<Territory, Collection<Unit>> possibleUnitsToAttack = new HashMap<>();
        for (final Territory t : kamikazeZones) {
            final List<Unit> validTargets = t.getUnits().getMatches(canBeAttacked);
            if (!validTargets.isEmpty()) {
                possibleUnitsToAttack.put(t, validTargets);
            }
        }
        final Map<Territory, HashMap<Unit, IntegerMap<Resource>>> attacks = getRemotePlayer(currentEnemy).selectKamikazeSuicideAttacks(possibleUnitsToAttack);
        if (attacks == null || attacks.isEmpty()) {
            continue;
        }
        // now validate that we have the resources and those units are valid targets
        for (final Entry<Territory, HashMap<Unit, IntegerMap<Resource>>> territoryEntry : attacks.entrySet()) {
            final Territory t = territoryEntry.getKey();
            final Collection<Unit> possibleUnits = possibleUnitsToAttack.get(t);
            if (possibleUnits == null || !possibleUnits.containsAll(territoryEntry.getValue().keySet())) {
                throw new IllegalStateException("Player has chosen illegal units during Kamikaze Suicide Attacks");
            }
            for (final IntegerMap<Resource> resourceMap : territoryEntry.getValue().values()) {
                attackTokens.subtract(resourceMap);
            }
        }
        if (!attackTokens.isPositive()) {
            throw new IllegalStateException("Player has chosen illegal resource during Kamikaze Suicide Attacks");
        }
        for (final Entry<Territory, HashMap<Unit, IntegerMap<Resource>>> territoryEntry : attacks.entrySet()) {
            final Territory location = territoryEntry.getKey();
            for (final Entry<Unit, IntegerMap<Resource>> unitEntry : territoryEntry.getValue().entrySet()) {
                final Unit unitUnderFire = unitEntry.getKey();
                final IntegerMap<Resource> numberOfAttacks = unitEntry.getValue();
                if (numberOfAttacks != null && numberOfAttacks.size() > 0 && numberOfAttacks.totalValues() > 0) {
                    fireKamikazeSuicideAttacks(unitUnderFire, numberOfAttacks, resourcesAndAttackValues, currentEnemy, location);
                }
            }
        }
    }
}
Also used : PlayerID(games.strategy.engine.data.PlayerID) GameData(games.strategy.engine.data.GameData) HashMap(java.util.HashMap) TripleAUnit(games.strategy.triplea.TripleAUnit) Unit(games.strategy.engine.data.Unit) PlayerAttachment(games.strategy.triplea.attachments.PlayerAttachment) UnitType(games.strategy.engine.data.UnitType) IntegerMap(games.strategy.util.IntegerMap) Territory(games.strategy.engine.data.Territory) TerritoryAttachment(games.strategy.triplea.attachments.TerritoryAttachment) Resource(games.strategy.engine.data.Resource) Collection(java.util.Collection) ResourceCollection(games.strategy.engine.data.ResourceCollection)

Example 5 with GameData

use of games.strategy.engine.data.GameData in project triplea by triplea-game.

the class BattleDelegate method resetMaxScrambleCount.

private static void resetMaxScrambleCount(final IDelegateBridge bridge) {
    // reset the tripleaUnit property for all airbases that were used
    final GameData data = bridge.getData();
    if (!Properties.getScrambleRulesInEffect(data)) {
        return;
    }
    final CompositeChange change = new CompositeChange();
    for (final Territory t : data.getMap().getTerritories()) {
        final Collection<Unit> airbases = t.getUnits().getMatches(Matches.unitIsAirBase());
        for (final Unit u : airbases) {
            final UnitAttachment ua = UnitAttachment.get(u.getType());
            final int currentMax = ((TripleAUnit) u).getMaxScrambleCount();
            final int allowedMax = ua.getMaxScrambleCount();
            if (currentMax != allowedMax) {
                change.add(ChangeFactory.unitPropertyChange(u, allowedMax, TripleAUnit.MAX_SCRAMBLE_COUNT));
            }
        }
    }
    if (!change.isEmpty()) {
        bridge.getHistoryWriter().startEvent("Preparing Airbases for Possible Scrambling");
        bridge.addChange(change);
    }
}
Also used : UnitAttachment(games.strategy.triplea.attachments.UnitAttachment) Territory(games.strategy.engine.data.Territory) GameData(games.strategy.engine.data.GameData) CompositeChange(games.strategy.engine.data.CompositeChange) TripleAUnit(games.strategy.triplea.TripleAUnit) Unit(games.strategy.engine.data.Unit) TripleAUnit(games.strategy.triplea.TripleAUnit)

Aggregations

GameData (games.strategy.engine.data.GameData)204 Unit (games.strategy.engine.data.Unit)100 PlayerID (games.strategy.engine.data.PlayerID)92 Territory (games.strategy.engine.data.Territory)92 ArrayList (java.util.ArrayList)83 TripleAUnit (games.strategy.triplea.TripleAUnit)64 HashSet (java.util.HashSet)50 CompositeChange (games.strategy.engine.data.CompositeChange)40 List (java.util.List)36 HashMap (java.util.HashMap)32 Set (java.util.Set)32 Route (games.strategy.engine.data.Route)31 UnitAttachment (games.strategy.triplea.attachments.UnitAttachment)30 Collection (java.util.Collection)29 UnitType (games.strategy.engine.data.UnitType)26 Change (games.strategy.engine.data.Change)24 Test (org.junit.jupiter.api.Test)23 Resource (games.strategy.engine.data.Resource)22 TestMapGameData (games.strategy.triplea.xml.TestMapGameData)22 Map (java.util.Map)21