Search in sources :

Example 26 with Change

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

the class RevisedTest method testLoadUnloadAlliedTransport.

@Test
public void testLoadUnloadAlliedTransport() {
    // you cant load and unload an allied transport the same turn
    final UnitType infantryType = GameDataTestUtil.infantry(gameData);
    final Territory eastEurope = gameData.getMap().getTerritory("Eastern Europe");
    // add japanese infantry to eastern europe
    final PlayerID japanese = GameDataTestUtil.japanese(gameData);
    final Change change = ChangeFactory.addUnits(eastEurope, infantryType.create(1, japanese));
    gameData.performChange(change);
    final Territory sz5 = gameData.getMap().getTerritory("5 Sea Zone");
    final MoveDelegate moveDelegate = (MoveDelegate) gameData.getDelegateList().getDelegate("move");
    final ITestDelegateBridge bridge = getDelegateBridge(japanese);
    bridge.setStepName("CombatMove");
    moveDelegate.setDelegateBridgeAndPlayer(bridge);
    moveDelegate.start();
    final Route eeToSz5 = new Route();
    eeToSz5.setStart(eastEurope);
    eeToSz5.add(sz5);
    // load the transport in the baltic
    final List<Unit> infantry = eastEurope.getUnits().getMatches(Matches.unitIsOfType(infantryType).and(Matches.unitIsOwnedBy(japanese)));
    assertEquals(1, infantry.size());
    final TripleAUnit transport = (TripleAUnit) sz5.getUnits().getMatches(Matches.unitIsTransport()).get(0);
    String error = moveDelegate.move(infantry, eeToSz5, Collections.singletonList(transport));
    assertNull(error, error);
    // try to unload
    final Route sz5ToEee = new Route();
    sz5ToEee.setStart(sz5);
    sz5ToEee.add(eastEurope);
    error = moveDelegate.move(infantry, sz5ToEee);
    assertEquals(MoveValidator.CANNOT_LOAD_AND_UNLOAD_AN_ALLIED_TRANSPORT_IN_THE_SAME_ROUND, error);
}
Also used : PlayerID(games.strategy.engine.data.PlayerID) Territory(games.strategy.engine.data.Territory) UnitType(games.strategy.engine.data.UnitType) Change(games.strategy.engine.data.Change) ITestDelegateBridge(games.strategy.engine.data.ITestDelegateBridge) TripleAUnit(games.strategy.triplea.TripleAUnit) Unit(games.strategy.engine.data.Unit) TripleAUnit(games.strategy.triplea.TripleAUnit) Route(games.strategy.engine.data.Route) Test(org.junit.jupiter.api.Test)

Example 27 with Change

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

the class RocketsFireHelper method fireRocket.

private static void fireRocket(final PlayerID player, final Territory attackedTerritory, final IDelegateBridge bridge, final Territory attackFrom) {
    final GameData data = bridge.getData();
    final PlayerID attacked = attackedTerritory.getOwner();
    final Resource pus = data.getResourceList().getResource(Constants.PUS);
    final boolean damageFromBombingDoneToUnits = isDamageFromBombingDoneToUnitsInsteadOfTerritories(data);
    // unit damage vs territory damage
    final Collection<Unit> enemyUnits = attackedTerritory.getUnits().getMatches(Matches.enemyUnit(player, data).and(Matches.unitIsBeingTransported().negate()));
    final Collection<Unit> enemyTargetsTotal = CollectionUtils.getMatches(enemyUnits, Matches.unitIsAtMaxDamageOrNotCanBeDamaged(attackedTerritory).negate());
    final Collection<Unit> rockets;
    // attackFrom could be null if WW2V1
    if (attackFrom == null) {
        rockets = null;
    } else {
        rockets = new ArrayList<>(CollectionUtils.getMatches(attackFrom.getUnits().getUnits(), rocketMatch(player)));
    }
    final int numberOfAttacks = (rockets == null ? 1 : Math.min(TechAbilityAttachment.getRocketNumberPerTerritory(player, data), TechAbilityAttachment.getRocketDiceNumber(rockets, data)));
    if (numberOfAttacks <= 0) {
        return;
    }
    final Collection<Unit> targets = new ArrayList<>();
    if (damageFromBombingDoneToUnits) {
        // TODO: rockets needs to be completely redone to allow for multiple rockets to fire at different targets, etc
        // etc.
        final HashSet<UnitType> legalTargetsForTheseRockets = new HashSet<>();
        if (rockets == null) {
            legalTargetsForTheseRockets.addAll(data.getUnitTypeList().getAllUnitTypes());
        } else {
            // a hack for now, we let the rockets fire at anyone who could be targetted by any rocket
            for (final Unit r : rockets) {
                legalTargetsForTheseRockets.addAll(UnitAttachment.get(r.getType()).getBombingTargets(data));
            }
        }
        final Collection<Unit> enemyTargets = CollectionUtils.getMatches(enemyTargetsTotal, Matches.unitIsOfTypes(legalTargetsForTheseRockets));
        if (enemyTargets.isEmpty()) {
            // TODO: this sucks
            return;
        }
        Unit target = null;
        if (enemyTargets.size() == 1) {
            target = enemyTargets.iterator().next();
        } else {
            while (target == null) {
                final ITripleAPlayer iplayer = (ITripleAPlayer) bridge.getRemotePlayer(player);
                target = iplayer.whatShouldBomberBomb(attackedTerritory, enemyTargets, rockets);
            }
        }
        if (target == null) {
            throw new IllegalStateException("No Targets in " + attackedTerritory.getName());
        }
        targets.add(target);
    }
    final boolean doNotUseBombingBonus = !Properties.getUseBombingMaxDiceSidesAndBonus(data) || rockets == null;
    int cost = 0;
    final String transcript;
    if (!Properties.getLowLuckDamageOnly(data)) {
        if (doNotUseBombingBonus || rockets == null) {
            // no low luck, and no bonus, so just roll based on the map's dice sides
            final int[] rolls = bridge.getRandom(data.getDiceSides(), numberOfAttacks, player, DiceType.BOMBING, "Rocket fired by " + player.getName() + " at " + attacked.getName());
            for (final int r : rolls) {
                // we are zero based
                cost += r + 1;
            }
            transcript = "Rockets " + (attackFrom == null ? "" : "in " + attackFrom.getName()) + " roll: " + MyFormatter.asDice(rolls);
        } else {
            // we must use bombing bonus
            int highestMaxDice = 0;
            int highestBonus = 0;
            final int diceSides = data.getDiceSides();
            for (final Unit u : rockets) {
                final UnitAttachment ua = UnitAttachment.get(u.getType());
                int maxDice = ua.getBombingMaxDieSides();
                final int bonus = ua.getBombingBonus();
                // map, and zero for the bonus.
                if (maxDice < 0) {
                    maxDice = diceSides;
                }
                // we only roll once for rockets, so if there are other rockets here we just roll for the best rocket
                if ((bonus + ((maxDice + 1) / 2)) > (highestBonus + ((highestMaxDice + 1) / 2))) {
                    highestMaxDice = maxDice;
                    highestBonus = bonus;
                }
            }
            // now we roll, or don't if there is nothing to roll.
            if (highestMaxDice > 0) {
                final int[] rolls = bridge.getRandom(highestMaxDice, numberOfAttacks, player, DiceType.BOMBING, "Rocket fired by " + player.getName() + " at " + attacked.getName());
                for (int i = 0; i < rolls.length; i++) {
                    final int r = Math.max(-1, rolls[i] + highestBonus);
                    rolls[i] = r;
                    // we are zero based
                    cost += r + 1;
                }
                transcript = "Rockets " + (attackFrom == null ? "" : "in " + attackFrom.getName()) + " roll: " + MyFormatter.asDice(rolls);
            } else {
                cost = highestBonus * numberOfAttacks;
                transcript = "Rockets " + (attackFrom == null ? "" : "in " + attackFrom.getName()) + " do " + highestBonus + " damage for each rocket";
            }
        }
    } else {
        if (doNotUseBombingBonus || rockets == null) {
            // no bonus, so just roll based on the map's dice sides, but modify for LL
            final int maxDice = (data.getDiceSides() + 1) / 3;
            final int bonus = (data.getDiceSides() + 1) / 3;
            final int[] rolls = bridge.getRandom(maxDice, numberOfAttacks, player, DiceType.BOMBING, "Rocket fired by " + player.getName() + " at " + attacked.getName());
            for (int i = 0; i < rolls.length; i++) {
                final int r = rolls[i] + bonus;
                rolls[i] = r;
                // we are zero based
                cost += r + 1;
            }
            transcript = "Rockets " + (attackFrom == null ? "" : "in " + attackFrom.getName()) + " roll: " + MyFormatter.asDice(rolls);
        } else {
            int highestMaxDice = 0;
            int highestBonus = 0;
            final int diceSides = data.getDiceSides();
            for (final Unit u : rockets) {
                final UnitAttachment ua = UnitAttachment.get(u.getType());
                int maxDice = ua.getBombingMaxDieSides();
                int bonus = ua.getBombingBonus();
                // map, and zero for the bonus.
                if (maxDice < 0 || doNotUseBombingBonus) {
                    maxDice = diceSides;
                }
                if (doNotUseBombingBonus) {
                    bonus = 0;
                }
                // luck by 2/3.
                if (maxDice >= 5) {
                    bonus += (maxDice + 1) / 3;
                    maxDice = (maxDice + 1) / 3;
                }
                // we only roll once for rockets, so if there are other rockets here we just roll for the best rocket
                if ((bonus + ((maxDice + 1) / 2)) > (highestBonus + ((highestMaxDice + 1) / 2))) {
                    highestMaxDice = maxDice;
                    highestBonus = bonus;
                }
            }
            // now we roll, or don't if there is nothing to roll.
            if (highestMaxDice > 0) {
                final int[] rolls = bridge.getRandom(highestMaxDice, numberOfAttacks, player, DiceType.BOMBING, "Rocket fired by " + player.getName() + " at " + attacked.getName());
                for (int i = 0; i < rolls.length; i++) {
                    final int r = Math.max(-1, rolls[i] + highestBonus);
                    rolls[i] = r;
                    // we are zero based
                    cost += r + 1;
                }
                transcript = "Rockets " + (attackFrom == null ? "" : "in " + attackFrom.getName()) + " roll: " + MyFormatter.asDice(rolls);
            } else {
                cost = highestBonus * numberOfAttacks;
                transcript = "Rockets " + (attackFrom == null ? "" : "in " + attackFrom.getName()) + " do " + highestBonus + " damage for each rocket";
            }
        }
    }
    int territoryProduction = TerritoryAttachment.getProduction(attackedTerritory);
    if (damageFromBombingDoneToUnits && !targets.isEmpty()) {
        // we are doing damage to 'target', not to the territory
        final Unit target = targets.iterator().next();
        // UnitAttachment ua = UnitAttachment.get(target.getType());
        final TripleAUnit taUnit = (TripleAUnit) target;
        final int damageLimit = taUnit.getHowMuchMoreDamageCanThisUnitTake(target, attackedTerritory);
        cost = Math.max(0, Math.min(cost, damageLimit));
        final int totalDamage = taUnit.getUnitDamage() + cost;
        // Record production lost
        // DelegateFinder.moveDelegate(data).PUsLost(attackedTerritory, cost);
        // apply the hits to the targets
        final IntegerMap<Unit> damageMap = new IntegerMap<>();
        damageMap.put(target, totalDamage);
        bridge.addChange(ChangeFactory.bombingUnitDamage(damageMap));
    // attackedTerritory.notifyChanged();
    // in WW2V2, limit rocket attack cost to production value of factory.
    } else if (isWW2V2(data) || isLimitRocketDamageToProduction(data)) {
        // If we are limiting total PUs lost then take that into account
        if (isPuCap(data) || isLimitRocketDamagePerTurn(data)) {
            final int alreadyLost = DelegateFinder.moveDelegate(data).pusAlreadyLost(attackedTerritory);
            territoryProduction -= alreadyLost;
            territoryProduction = Math.max(0, territoryProduction);
        }
        if (cost > territoryProduction) {
            cost = territoryProduction;
        }
    }
    // Record the PUs lost
    DelegateFinder.moveDelegate(data).pusLost(attackedTerritory, cost);
    if (damageFromBombingDoneToUnits && !targets.isEmpty()) {
        getRemote(bridge).reportMessage("Rocket attack in " + attackedTerritory.getName() + " does " + cost + " damage to " + targets.iterator().next(), "Rocket attack in " + attackedTerritory.getName() + " does " + cost + " damage to " + targets.iterator().next());
        bridge.getHistoryWriter().startEvent("Rocket attack in " + attackedTerritory.getName() + " does " + cost + " damage to " + targets.iterator().next());
    } else {
        cost *= Properties.getPuMultiplier(data);
        getRemote(bridge).reportMessage("Rocket attack in " + attackedTerritory.getName() + " costs:" + cost, "Rocket attack in " + attackedTerritory.getName() + " costs:" + cost);
        // Trying to remove more PUs than the victim has is A Bad Thing[tm]
        final int availForRemoval = attacked.getResources().getQuantity(pus);
        if (cost > availForRemoval) {
            cost = availForRemoval;
        }
        final String transcriptText = attacked.getName() + " lost " + cost + " PUs to rocket attack by " + player.getName();
        bridge.getHistoryWriter().startEvent(transcriptText);
        final Change rocketCharge = ChangeFactory.changeResourcesChange(attacked, pus, -cost);
        bridge.addChange(rocketCharge);
    }
    bridge.getHistoryWriter().addChildToEvent(transcript, rockets == null ? null : new ArrayList<>(rockets));
    // this is null in WW2V1
    if (attackFrom != null) {
        if (rockets != null && !rockets.isEmpty()) {
            // TODO: only a certain number fired...
            final Change change = ChangeFactory.markNoMovementChange(Collections.singleton(rockets.iterator().next()));
            bridge.addChange(change);
        } else {
            throw new IllegalStateException("No rockets?" + attackFrom.getUnits().getUnits());
        }
    }
    // kill any units that can die if they have reached max damage (veqryn)
    if (targets.stream().anyMatch(Matches.unitCanDieFromReachingMaxDamage())) {
        final List<Unit> unitsCanDie = CollectionUtils.getMatches(targets, Matches.unitCanDieFromReachingMaxDamage());
        unitsCanDie.retainAll(CollectionUtils.getMatches(unitsCanDie, Matches.unitIsAtMaxDamageOrNotCanBeDamaged(attackedTerritory)));
        if (!unitsCanDie.isEmpty()) {
            final Change removeDead = ChangeFactory.removeUnits(attackedTerritory, unitsCanDie);
            final String transcriptText = MyFormatter.unitsToText(unitsCanDie) + " lost in " + attackedTerritory.getName();
            bridge.getHistoryWriter().addChildToEvent(transcriptText, unitsCanDie);
            bridge.addChange(removeDead);
        }
    }
    // play a sound
    if (cost > 0) {
        bridge.getSoundChannelBroadcaster().playSoundForAll(SoundPath.CLIP_BOMBING_ROCKET, player);
    }
}
Also used : IntegerMap(games.strategy.util.IntegerMap) PlayerID(games.strategy.engine.data.PlayerID) GameData(games.strategy.engine.data.GameData) Resource(games.strategy.engine.data.Resource) ArrayList(java.util.ArrayList) Change(games.strategy.engine.data.Change) TripleAUnit(games.strategy.triplea.TripleAUnit) Unit(games.strategy.engine.data.Unit) TripleAUnit(games.strategy.triplea.TripleAUnit) ITripleAPlayer(games.strategy.triplea.player.ITripleAPlayer) UnitAttachment(games.strategy.triplea.attachments.UnitAttachment) UnitType(games.strategy.engine.data.UnitType) HashSet(java.util.HashSet)

Example 28 with Change

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

the class SpecialMoveDelegate method move.

@Override
public String move(final Collection<Unit> units, final Route route, final Collection<Unit> transportsThatCanBeLoaded, final Map<Unit, Collection<Unit>> newDependents) {
    if (!allowAirborne(player, getData())) {
        return "No Airborne Movement Allowed Yet";
    }
    final GameData data = getData();
    // there reason we use this, is because if we are in edit mode, we may have a different unit owner than the current
    // player.
    final PlayerID player = getUnitsOwner(units);
    // here we have our own new validation method....
    final MoveValidationResult result = validateMove(units, route, player, data);
    final StringBuilder errorMsg = new StringBuilder(100);
    final int numProblems = result.getTotalWarningCount() - (result.hasError() ? 0 : 1);
    final String numErrorsMsg = numProblems > 0 ? ("; " + numProblems + " " + MyFormatter.pluralize("error", numProblems) + " not shown") : "";
    if (result.hasError()) {
        return errorMsg.append(result.getError()).append(numErrorsMsg).toString();
    }
    if (result.hasDisallowedUnits()) {
        return errorMsg.append(result.getDisallowedUnitWarning(0)).append(numErrorsMsg).toString();
    }
    if (result.hasUnresolvedUnits()) {
        return errorMsg.append(result.getUnresolvedUnitWarning(0)).append(numErrorsMsg).toString();
    }
    // allow user to cancel move if aa guns will fire
    final AAInMoveUtil aaInMoveUtil = new AAInMoveUtil();
    aaInMoveUtil.initialize(bridge);
    final Collection<Territory> aaFiringTerritores = aaInMoveUtil.getTerritoriesWhereAaWillFire(route, units);
    if (!aaFiringTerritores.isEmpty()) {
        if (!getRemotePlayer().confirmMoveInFaceOfAa(aaFiringTerritores)) {
            return null;
        }
    }
    // do the move
    final UndoableMove currentMove = new UndoableMove(units, route);
    // add dependencies (any move that came before this, from this start territory, is a dependency)
    for (final UndoableMove otherMove : movesToUndo) {
        if (otherMove.getStart().equals(route.getStart())) {
            currentMove.addDependency(otherMove);
        }
    }
    // make the units airborne
    final CompositeChange airborneChange = new CompositeChange();
    for (final Unit u : units) {
        airborneChange.add(ChangeFactory.unitPropertyChange(u, true, TripleAUnit.AIRBORNE));
    }
    currentMove.addChange(airborneChange);
    // make the bases start filling up their capacity
    final Collection<Unit> basesAtStart = route.getStart().getUnits().getMatches(getAirborneBaseMatch(player, data));
    final Change fillLaunchCapacity = getNewAssignmentOfNumberLaunchedChange(units.size(), basesAtStart, player, data);
    currentMove.addChange(fillLaunchCapacity);
    // start event
    final String transcriptText = MyFormatter.unitsToTextNoOwner(units) + " moved from " + route.getStart().getName() + " to " + route.getEnd().getName();
    bridge.getHistoryWriter().startEvent(transcriptText, currentMove.getDescriptionObject());
    // actually do our special changes
    bridge.addChange(airborneChange);
    bridge.addChange(fillLaunchCapacity);
    tempMovePerformer = new MovePerformer();
    tempMovePerformer.initialize(this);
    tempMovePerformer.moveUnits(units, route, player, transportsThatCanBeLoaded, newDependents, currentMove);
    tempMovePerformer = null;
    return null;
}
Also used : PlayerID(games.strategy.engine.data.PlayerID) Territory(games.strategy.engine.data.Territory) GameData(games.strategy.engine.data.GameData) CompositeChange(games.strategy.engine.data.CompositeChange) Change(games.strategy.engine.data.Change) TripleAUnit(games.strategy.triplea.TripleAUnit) Unit(games.strategy.engine.data.Unit) MoveValidationResult(games.strategy.triplea.delegate.dataObjects.MoveValidationResult) CompositeChange(games.strategy.engine.data.CompositeChange)

Example 29 with Change

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

the class StrategicBombingRaidBattle method removeAaHits.

private void removeAaHits(final IDelegateBridge bridge, final CasualtyDetails casualties, final String currentTypeAa) {
    final List<Unit> killed = casualties.getKilled();
    if (!killed.isEmpty()) {
        bridge.getHistoryWriter().addChildToEvent(MyFormatter.unitsToTextNoOwner(killed) + " killed by " + currentTypeAa, new ArrayList<>(killed));
        final IntegerMap<UnitType> costs = TuvUtils.getCostsForTuv(m_attacker, m_data);
        final int tuvLostAttacker = TuvUtils.getTuv(killed, m_attacker, costs, m_data);
        m_attackerLostTUV += tuvLostAttacker;
        // m_attackingUnits.removeAll(casualties);
        removeAttackers(killed, false);
        final Change remove = ChangeFactory.removeUnits(m_battleSite, killed);
        bridge.addChange(remove);
    }
}
Also used : UnitType(games.strategy.engine.data.UnitType) Change(games.strategy.engine.data.Change) TripleAUnit(games.strategy.triplea.TripleAUnit) Unit(games.strategy.engine.data.Unit)

Example 30 with Change

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

the class TechTracker method addAdvance.

public static void addAdvance(final PlayerID player, final IDelegateBridge bridge, final TechAdvance advance) {
    final Change attachmentChange;
    if (advance instanceof GenericTechAdvance && ((GenericTechAdvance) advance).getAdvance() == null) {
        attachmentChange = ChangeFactory.genericTechChange(TechAttachment.get(player), true, advance.getProperty());
    } else {
        attachmentChange = ChangeFactory.attachmentPropertyChange(TechAttachment.get(player), "true", advance.getProperty());
    }
    bridge.addChange(attachmentChange);
    advance.perform(player, bridge);
}
Also used : Change(games.strategy.engine.data.Change)

Aggregations

Change (games.strategy.engine.data.Change)70 CompositeChange (games.strategy.engine.data.CompositeChange)44 Unit (games.strategy.engine.data.Unit)40 TripleAUnit (games.strategy.triplea.TripleAUnit)29 PlayerID (games.strategy.engine.data.PlayerID)25 Territory (games.strategy.engine.data.Territory)23 ArrayList (java.util.ArrayList)20 GameData (games.strategy.engine.data.GameData)19 UnitType (games.strategy.engine.data.UnitType)16 Resource (games.strategy.engine.data.Resource)12 Test (org.junit.jupiter.api.Test)11 ITestDelegateBridge (games.strategy.engine.data.ITestDelegateBridge)9 Route (games.strategy.engine.data.Route)9 HashSet (java.util.HashSet)8 UnitAttachment (games.strategy.triplea.attachments.UnitAttachment)6 Collection (java.util.Collection)6 IntegerMap (games.strategy.util.IntegerMap)5 Tuple (games.strategy.util.Tuple)4 ProductionFrontier (games.strategy.engine.data.ProductionFrontier)3 ITripleAPlayer (games.strategy.triplea.player.ITripleAPlayer)3