use of games.strategy.engine.data.Unit in project triplea by triplea-game.
the class BattleCalculator method individuallyFiredAaCasualties.
/**
* Choose plane casualties based on individual AA shots at each aircraft.
*/
private static CasualtyDetails individuallyFiredAaCasualties(final boolean defending, final Collection<Unit> planes, final Collection<Unit> defendingAa, final DiceRoll dice, final IDelegateBridge bridge, final boolean allowMultipleHitsPerUnit) {
// if we have aa guns that are not infinite, then we need to randomly decide the aa casualties since there are not
// enough rolls to have
// a single roll for each aircraft, or too many rolls
// normal behavior is instant kill, which means planes.size()
final int planeHitPoints = (allowMultipleHitsPerUnit ? getTotalHitpointsLeft(planes) : planes.size());
if (DiceRoll.getTotalAAattacks(defendingAa, planes) != planeHitPoints) {
return randomAaCasualties(planes, dice, bridge, allowMultipleHitsPerUnit);
}
final Triple<Integer, Integer, Boolean> triple = DiceRoll.getTotalAaPowerThenHitsAndFillSortedDiceThenIfAllUseSameAttack(null, null, !defending, defendingAa, planes, bridge.getData(), false);
final boolean allSameAttackPower = triple.getThird();
if (!allSameAttackPower) {
return randomAaCasualties(planes, dice, bridge, allowMultipleHitsPerUnit);
}
final Tuple<Integer, Integer> attackThenDiceSides = DiceRoll.getAAattackAndMaxDiceSides(defendingAa, bridge.getData(), !defending);
final int highestAttack = attackThenDiceSides.getFirst();
// int chosenDiceSize = attackThenDiceSides[1];
final CasualtyDetails finalCasualtyDetails = new CasualtyDetails();
final int hits = dice.getHits();
final List<Unit> planesList = new ArrayList<>();
for (final Unit plane : planes) {
final int hpLeft = allowMultipleHitsPerUnit ? (UnitAttachment.get(plane.getType()).getHitPoints() - plane.getHits()) : (Math.min(1, UnitAttachment.get(plane.getType()).getHitPoints() - plane.getHits()));
for (int hp = 0; hp < hpLeft; ++hp) {
// if allowMultipleHitsPerUnit, then because the number of rolls exactly equals the hitpoints of all units,
// we roll multiple times for any unit with multiple hitpoints
planesList.add(plane);
}
}
// We need to choose which planes die based on their position in the list and the individual AA rolls
if (hits > planeHitPoints) {
throw new IllegalStateException("Cannot have more hits than number of die rolls");
}
if (hits < planeHitPoints) {
final List<Die> rolls = dice.getRolls(highestAttack);
for (int i = 0; i < rolls.size(); i++) {
final Die die = rolls.get(i);
if (die.getType() == DieType.HIT) {
final Unit unit = planesList.get(i);
if (allowMultipleHitsPerUnit && (Collections.frequency(finalCasualtyDetails.getDamaged(), unit) < (getTotalHitpointsLeft(unit) - 1))) {
finalCasualtyDetails.addToDamaged(unit);
} else {
finalCasualtyDetails.addToKilled(unit);
}
}
}
} else {
for (final Unit plane : planesList) {
if (finalCasualtyDetails.getKilled().contains(plane)) {
finalCasualtyDetails.addToDamaged(plane);
} else {
finalCasualtyDetails.addToKilled(plane);
}
}
}
return finalCasualtyDetails;
}
use of games.strategy.engine.data.Unit in project triplea by triplea-game.
the class BattleCalculator method getDefaultCasualties.
/**
* A unit with two hitpoints will be listed twice if they will die. The first time they are listed it is as damaged.
* The second time they
* are listed, it is dead.
*/
private static Tuple<CasualtyList, List<Unit>> getDefaultCasualties(final Collection<Unit> targetsToPickFrom, final int hits, final boolean defending, final PlayerID player, final Collection<Unit> enemyUnits, final boolean amphibious, final Collection<Unit> amphibiousLandAttackers, final Territory battlesite, final IntegerMap<UnitType> costs, final Collection<TerritoryEffect> territoryEffects, final GameData data, final boolean allowMultipleHitsPerUnit, final boolean bonus) {
final CasualtyList defaultCasualtySelection = new CasualtyList();
// Sort units by power and cost in ascending order
final List<Unit> sorted = sortUnitsForCasualtiesWithSupport(targetsToPickFrom, defending, player, enemyUnits, amphibious, amphibiousLandAttackers, battlesite, costs, territoryEffects, data, bonus);
// Remove two hit bb's selecting them first for default casualties
int numSelectedCasualties = 0;
if (allowMultipleHitsPerUnit) {
for (final Unit unit : sorted) {
// Stop if we have already selected as many hits as there are targets
if (numSelectedCasualties >= hits) {
return Tuple.of(defaultCasualtySelection, sorted);
}
final UnitAttachment ua = UnitAttachment.get(unit.getType());
final int extraHitPoints = Math.min((hits - numSelectedCasualties), (ua.getHitPoints() - (1 + unit.getHits())));
for (int i = 0; i < extraHitPoints; i++) {
numSelectedCasualties++;
defaultCasualtySelection.addToDamaged(unit);
}
}
}
// Select units
for (final Unit unit : sorted) {
// Stop if we have already selected as many hits as there are targets
if (numSelectedCasualties >= hits) {
return Tuple.of(defaultCasualtySelection, sorted);
}
defaultCasualtySelection.addToKilled(unit);
numSelectedCasualties++;
}
return Tuple.of(defaultCasualtySelection, sorted);
}
use of games.strategy.engine.data.Unit 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);
}
}
}
}
use of games.strategy.engine.data.Unit 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);
}
}
}
}
}
use of games.strategy.engine.data.Unit 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);
}
}
Aggregations