use of games.strategy.triplea.attachments.UnitAttachment in project triplea by triplea-game.
the class UnitBattleComparator method compare.
@Override
public int compare(final Unit u1, final Unit u2) {
if (u1.equals(u2)) {
return 0;
}
final boolean transporting1 = TransportTracker.isTransporting(u1);
final boolean transporting2 = TransportTracker.isTransporting(u2);
final UnitAttachment ua1 = UnitAttachment.get(u1.getType());
final UnitAttachment ua2 = UnitAttachment.get(u2.getType());
if (ua1 == ua2 && u1.getOwner().equals(u2.getOwner())) {
if (transporting1 && !transporting2) {
return 1;
}
if (!transporting1 && transporting2) {
return -1;
}
return 0;
}
final boolean airOrCarrierOrTransport1 = Matches.unitIsAir().test(u1) || Matches.unitIsCarrier().test(u1) || (!transporting1 && Matches.unitIsTransport().test(u1));
final boolean airOrCarrierOrTransport2 = Matches.unitIsAir().test(u2) || Matches.unitIsCarrier().test(u2) || (!transporting2 && Matches.unitIsTransport().test(u2));
final boolean subDestroyer1 = Matches.unitIsSub().test(u1) || Matches.unitIsDestroyer().test(u1);
final boolean subDestroyer2 = Matches.unitIsSub().test(u2) || Matches.unitIsDestroyer().test(u2);
final boolean multiHpCanRepair1 = multiHitpointCanRepair.contains(u1.getType());
final boolean multiHpCanRepair2 = multiHitpointCanRepair.contains(u2.getType());
if (!ignorePrimaryPower) {
int power1 = 8 * BattleCalculator.getUnitPowerForSorting(u1, defending, gameData, territoryEffects);
int power2 = 8 * BattleCalculator.getUnitPowerForSorting(u2, defending, gameData, territoryEffects);
if (bonus) {
if (subDestroyer1 && !subDestroyer2) {
power1 += 4;
} else if (!subDestroyer1 && subDestroyer2) {
power2 += 4;
}
if (multiHpCanRepair1 && !multiHpCanRepair2) {
power1++;
} else if (!multiHpCanRepair1 && multiHpCanRepair2) {
power2++;
}
if (transporting1 && !transporting2) {
power1++;
} else if (!transporting1 && transporting2) {
power2++;
}
if (airOrCarrierOrTransport1 && !airOrCarrierOrTransport2) {
power1++;
} else if (!airOrCarrierOrTransport1 && airOrCarrierOrTransport2) {
power2++;
}
}
if (power1 != power2) {
return power1 - power2;
}
}
{
final int cost1 = costs.getInt(u1.getType());
final int cost2 = costs.getInt(u2.getType());
if (cost1 != cost2) {
return cost1 - cost2;
}
}
{
int power1reverse = 8 * BattleCalculator.getUnitPowerForSorting(u1, !defending, gameData, territoryEffects);
int power2reverse = 8 * BattleCalculator.getUnitPowerForSorting(u2, !defending, gameData, territoryEffects);
if (bonus) {
if (subDestroyer1 && !subDestroyer2) {
power1reverse += 4;
} else if (!subDestroyer1 && subDestroyer2) {
power2reverse += 4;
}
if (multiHpCanRepair1 && !multiHpCanRepair2) {
power1reverse++;
} else if (!multiHpCanRepair1 && multiHpCanRepair2) {
power2reverse++;
}
if (transporting1 && !transporting2) {
power1reverse++;
} else if (!transporting1 && transporting2) {
power2reverse++;
}
if (airOrCarrierOrTransport1 && !airOrCarrierOrTransport2) {
power1reverse++;
} else if (!airOrCarrierOrTransport1 && airOrCarrierOrTransport2) {
power2reverse++;
}
}
if (power1reverse != power2reverse) {
return power1reverse - power2reverse;
}
}
if (subDestroyer1 && !subDestroyer2) {
return 1;
} else if (!subDestroyer1 && subDestroyer2) {
return -1;
}
if (multiHpCanRepair1 && !multiHpCanRepair2) {
return 1;
} else if (!multiHpCanRepair1 && multiHpCanRepair2) {
return -1;
}
if (transporting1 && !transporting2) {
return 1;
} else if (!transporting1 && transporting2) {
return -1;
}
if (airOrCarrierOrTransport1 && !airOrCarrierOrTransport2) {
return 1;
} else if (!airOrCarrierOrTransport1 && airOrCarrierOrTransport2) {
return -1;
}
return ua1.getMovement(u1.getOwner()) - ua2.getMovement(u2.getOwner());
}
use of games.strategy.triplea.attachments.UnitAttachment in project triplea by triplea-game.
the class InitializationDelegate method initTwoHitBattleship.
private static void initTwoHitBattleship(final IDelegateBridge bridge) {
final GameData data = bridge.getData();
final boolean userEnabled = Properties.getTwoHitBattleships(data);
final UnitType battleShipUnit = data.getUnitTypeList().getUnitType(Constants.UNIT_TYPE_BATTLESHIP);
if (battleShipUnit == null) {
return;
}
final UnitAttachment battleShipAttachment = UnitAttachment.get(battleShipUnit);
final boolean defaultEnabled = battleShipAttachment.getHitPoints() > 1;
if (userEnabled != defaultEnabled) {
bridge.getHistoryWriter().startEvent("TwoHitBattleships:" + userEnabled);
bridge.addChange(ChangeFactory.attachmentPropertyChange(battleShipAttachment, userEnabled ? 2 : 1, "hitPoints"));
}
}
use of games.strategy.triplea.attachments.UnitAttachment in project triplea by triplea-game.
the class InitializationDelegate method initTransportedLandUnits.
/**
* Want to make sure that all units in the sea that can be transported are
* marked as being transported by something.
* We assume that all transportable units in the sea are in a transport, no
* exceptions.
*/
private static void initTransportedLandUnits(final IDelegateBridge bridge) {
final GameData data = bridge.getData();
// check every territory
boolean historyItemCreated = false;
for (final Territory current : data.getMap().getTerritories()) {
// only care about water
if (!current.isWater()) {
continue;
}
final Collection<Unit> units = current.getUnits().getUnits();
if (units.size() == 0 || units.stream().noneMatch(Matches.unitIsLand())) {
continue;
}
// map transports, try to fill
final Collection<Unit> transports = CollectionUtils.getMatches(units, Matches.unitIsTransport());
final Collection<Unit> land = CollectionUtils.getMatches(units, Matches.unitIsLand());
for (final Unit toLoad : land) {
final UnitAttachment ua = UnitAttachment.get(toLoad.getType());
final int cost = ua.getTransportCost();
if (cost == -1) {
throw new IllegalStateException("Non transportable unit in sea");
}
// find the next transport that can hold it
boolean found = false;
for (final Unit transport : transports) {
final int capacity = TransportTracker.getAvailableCapacity(transport);
if (capacity >= cost) {
if (!historyItemCreated) {
bridge.getHistoryWriter().startEvent("Initializing Units in Transports");
historyItemCreated = true;
}
try {
bridge.addChange(TransportTracker.loadTransportChange((TripleAUnit) transport, toLoad));
} catch (final IllegalStateException e) {
System.err.println("You can only edit add transports+units after the initialization delegate of the game is finished. " + "If this error came up and you have not used Edit Mode to add units + transports, then please " + "report this as a bug: \r\n" + e.getMessage());
}
found = true;
break;
}
}
if (!found) {
throw new IllegalStateException("Cannot load all land units in sea transports. " + "Please make sure you have enough transports. " + "You may need to re-order the xml's placement of transports and land units, " + "as the engine will try to fill them in the order they are given.");
}
}
}
}
use of games.strategy.triplea.attachments.UnitAttachment in project triplea by triplea-game.
the class BattleDelegate method doScrambling.
private void doScrambling() {
// first, figure out all the territories where scrambling units could scramble to
// then ask the defending player if they wish to scramble units there, and actually move the units there
final GameData data = getData();
if (!Properties.getScrambleRulesInEffect(data)) {
return;
}
final boolean fromIslandOnly = Properties.getScrambleFromIslandOnly(data);
final boolean toSeaOnly = Properties.getScrambleToSeaOnly(data);
final boolean toAnyAmphibious = Properties.getScrambleToAnyAmphibiousAssault(data);
final boolean toSbr = Properties.getCanScrambleIntoAirBattles(data);
int maxScrambleDistance = 0;
for (final UnitType unitType : data.getUnitTypeList()) {
final UnitAttachment ua = UnitAttachment.get(unitType);
if (ua.getCanScramble() && maxScrambleDistance < ua.getMaxScrambleDistance()) {
maxScrambleDistance = ua.getMaxScrambleDistance();
}
}
final Predicate<Unit> airbasesCanScramble = Matches.unitIsEnemyOf(data, player).and(Matches.unitIsAirBase()).and(Matches.unitIsNotDisabled()).and(Matches.unitIsBeingTransported().negate());
final Predicate<Territory> canScramble = PredicateBuilder.of(Matches.territoryIsWater().or(Matches.isTerritoryEnemy(player, data))).and(Matches.territoryHasUnitsThatMatch(Matches.unitCanScramble().and(Matches.unitIsEnemyOf(data, player)).and(Matches.unitIsNotDisabled()))).and(Matches.territoryHasUnitsThatMatch(airbasesCanScramble)).andIf(fromIslandOnly, Matches.territoryIsIsland()).build();
final Set<Territory> territoriesWithBattles = battleTracker.getPendingBattleSites().getNormalBattlesIncludingAirBattles();
if (toSbr) {
territoriesWithBattles.addAll(battleTracker.getPendingBattleSites().getStrategicBombingRaidsIncludingAirBattles());
}
final Set<Territory> territoriesWithBattlesWater = new HashSet<>(CollectionUtils.getMatches(territoriesWithBattles, Matches.territoryIsWater()));
final Set<Territory> territoriesWithBattlesLand = new HashSet<>(CollectionUtils.getMatches(territoriesWithBattles, Matches.territoryIsLand()));
final Map<Territory, Set<Territory>> scrambleTerrs = new HashMap<>();
for (final Territory battleTerr : territoriesWithBattlesWater) {
final Set<Territory> canScrambleFrom = new HashSet<>(CollectionUtils.getMatches(data.getMap().getNeighbors(battleTerr, maxScrambleDistance), canScramble));
if (!canScrambleFrom.isEmpty()) {
scrambleTerrs.put(battleTerr, canScrambleFrom);
}
}
for (final Territory battleTerr : territoriesWithBattlesLand) {
if (!toSeaOnly) {
final Set<Territory> canScrambleFrom = new HashSet<>(CollectionUtils.getMatches(data.getMap().getNeighbors(battleTerr, maxScrambleDistance), canScramble));
if (!canScrambleFrom.isEmpty()) {
scrambleTerrs.put(battleTerr, canScrambleFrom);
}
}
final IBattle battle = battleTracker.getPendingBattle(battleTerr, false, BattleType.NORMAL);
// it.
if (battle != null && battle.isAmphibious() && battle instanceof DependentBattle) {
final Collection<Territory> amphibFromTerrs = ((DependentBattle) battle).getAmphibiousAttackTerritories();
amphibFromTerrs.removeAll(territoriesWithBattlesWater);
for (final Territory amphibFrom : amphibFromTerrs) {
final Set<Territory> canScrambleFrom = scrambleTerrs.getOrDefault(amphibFrom, new HashSet<>());
if (toAnyAmphibious) {
canScrambleFrom.addAll(CollectionUtils.getMatches(data.getMap().getNeighbors(amphibFrom, maxScrambleDistance), canScramble));
} else if (canScramble.test(battleTerr)) {
canScrambleFrom.add(battleTerr);
}
if (!canScrambleFrom.isEmpty()) {
scrambleTerrs.put(amphibFrom, canScrambleFrom);
}
}
}
}
// now scrambleTerrs is a list of places we can scramble from
if (scrambleTerrs.isEmpty()) {
return;
}
final Map<Tuple<Territory, PlayerID>, Collection<Map<Territory, Tuple<Collection<Unit>, Collection<Unit>>>>> scramblersByTerritoryPlayer = new HashMap<>();
for (final Territory to : scrambleTerrs.keySet()) {
// find who we should ask
PlayerID defender = null;
if (battleTracker.hasPendingBattle(to, false)) {
defender = AbstractBattle.findDefender(to, player, data);
}
final Map<Territory, Tuple<Collection<Unit>, Collection<Unit>>> scramblers = new HashMap<>();
for (final Territory from : scrambleTerrs.get(to)) {
if (defender == null) {
defender = AbstractBattle.findDefender(from, player, data);
}
// find how many is the max this territory can scramble
final Collection<Unit> airbases = from.getUnits().getMatches(airbasesCanScramble);
final int maxCanScramble = getMaxScrambleCount(airbases);
final Route toBattleRoute = data.getMap().getRoute_IgnoreEnd(from, to, Matches.territoryIsNotImpassable());
final Collection<Unit> canScrambleAir = from.getUnits().getMatches(Matches.unitIsEnemyOf(data, player).and(Matches.unitCanScramble()).and(Matches.unitIsNotDisabled()).and(Matches.unitWasScrambled().negate()).and(Matches.unitCanScrambleOnRouteDistance(toBattleRoute)));
if (maxCanScramble > 0 && !canScrambleAir.isEmpty()) {
scramblers.put(from, Tuple.of(airbases, canScrambleAir));
}
}
if (defender == null || scramblers.isEmpty()) {
continue;
}
final Tuple<Territory, PlayerID> terrPlayer = Tuple.of(to, defender);
final Collection<Map<Territory, Tuple<Collection<Unit>, Collection<Unit>>>> tempScrambleList = scramblersByTerritoryPlayer.getOrDefault(terrPlayer, new ArrayList<>());
tempScrambleList.add(scramblers);
scramblersByTerritoryPlayer.put(terrPlayer, tempScrambleList);
}
// now scramble them
for (final Tuple<Territory, PlayerID> terrPlayer : scramblersByTerritoryPlayer.keySet()) {
final Territory to = terrPlayer.getFirst();
final PlayerID defender = terrPlayer.getSecond();
if (defender == null || defender.isNull()) {
continue;
}
boolean scrambledHere = false;
for (final Map<Territory, Tuple<Collection<Unit>, Collection<Unit>>> scramblers : scramblersByTerritoryPlayer.get(terrPlayer)) {
// verify that we didn't already scramble any of these units
final Iterator<Territory> territoryIter = scramblers.keySet().iterator();
while (territoryIter.hasNext()) {
final Territory t = territoryIter.next();
scramblers.get(t).getSecond().retainAll(t.getUnits());
if (scramblers.get(t).getSecond().isEmpty()) {
territoryIter.remove();
}
}
if (scramblers.isEmpty()) {
continue;
}
final Map<Territory, Collection<Unit>> toScramble = getRemotePlayer(defender).scrambleUnitsQuery(to, scramblers);
if (toScramble == null) {
continue;
}
// verify max allowed
if (!scramblers.keySet().containsAll(toScramble.keySet())) {
throw new IllegalStateException("Trying to scramble from illegal territory");
}
for (final Territory t : scramblers.keySet()) {
if (toScramble.get(t) == null) {
continue;
}
if (toScramble.get(t).size() > getMaxScrambleCount(scramblers.get(t).getFirst())) {
throw new IllegalStateException("Trying to scramble " + toScramble.get(t).size() + " out of " + t.getName() + ", but max allowed is " + scramblers.get(t).getFirst());
}
}
// Validate players have enough fuel to move there and back
final Map<PlayerID, ResourceCollection> playerFuelCost = new HashMap<>();
for (final Entry<Territory, Collection<Unit>> entry : toScramble.entrySet()) {
final Map<PlayerID, ResourceCollection> map = Route.getScrambleFuelCostCharge(entry.getValue(), entry.getKey(), to, data);
for (final Entry<PlayerID, ResourceCollection> playerAndCost : map.entrySet()) {
if (playerFuelCost.containsKey(playerAndCost.getKey())) {
playerFuelCost.get(playerAndCost.getKey()).add(playerAndCost.getValue());
} else {
playerFuelCost.put(playerAndCost.getKey(), playerAndCost.getValue());
}
}
}
for (final Entry<PlayerID, ResourceCollection> playerAndCost : playerFuelCost.entrySet()) {
if (!playerAndCost.getKey().getResources().has(playerAndCost.getValue().getResourcesCopy())) {
throw new IllegalStateException("Not enough fuel to scramble, player: " + playerAndCost.getKey() + ", needs: " + playerAndCost.getValue());
}
}
final CompositeChange change = new CompositeChange();
for (final Territory t : toScramble.keySet()) {
final Collection<Unit> scrambling = toScramble.get(t);
if (scrambling == null || scrambling.isEmpty()) {
continue;
}
int numberScrambled = scrambling.size();
final Collection<Unit> airbases = t.getUnits().getMatches(airbasesCanScramble);
final int maxCanScramble = getMaxScrambleCount(airbases);
if (maxCanScramble != Integer.MAX_VALUE) {
// TODO: maybe sort from biggest to smallest first?
for (final Unit airbase : airbases) {
final int allowedScramble = ((TripleAUnit) airbase).getMaxScrambleCount();
if (allowedScramble > 0) {
final int newAllowed;
if (allowedScramble >= numberScrambled) {
newAllowed = allowedScramble - numberScrambled;
numberScrambled = 0;
} else {
newAllowed = 0;
numberScrambled -= allowedScramble;
}
change.add(ChangeFactory.unitPropertyChange(airbase, newAllowed, TripleAUnit.MAX_SCRAMBLE_COUNT));
}
if (numberScrambled <= 0) {
break;
}
}
}
for (final Unit u : scrambling) {
change.add(ChangeFactory.unitPropertyChange(u, t, TripleAUnit.ORIGINATED_FROM));
change.add(ChangeFactory.unitPropertyChange(u, true, TripleAUnit.WAS_SCRAMBLED));
change.add(Route.getFuelChanges(Collections.singleton(u), new Route(t, to), u.getOwner(), data));
}
// should we mark combat, or call setupUnitsInSameTerritoryBattles again?
change.add(ChangeFactory.moveUnits(t, to, scrambling));
bridge.getHistoryWriter().startEvent(defender.getName() + " scrambles " + scrambling.size() + " units out of " + t.getName() + " to defend against the attack in " + to.getName(), scrambling);
scrambledHere = true;
}
if (!change.isEmpty()) {
bridge.addChange(change);
}
}
if (!scrambledHere) {
continue;
}
// make sure the units join the battle, or create a new battle.
final IBattle bombing = battleTracker.getPendingBattle(to, true, null);
IBattle battle = battleTracker.getPendingBattle(to, false, BattleType.NORMAL);
if (battle == null) {
final List<Unit> attackingUnits = to.getUnits().getMatches(Matches.unitIsOwnedBy(player));
if (bombing != null) {
attackingUnits.removeAll(bombing.getAttackingUnits());
}
// into an air battle / air raid
if (attackingUnits.isEmpty()) {
continue;
}
bridge.getHistoryWriter().startEvent(defender.getName() + " scrambles to create a battle in territory " + to.getName());
// TODO: the attacking sea units do not remember where they came from, so they cannot retreat anywhere. Need to
// fix.
battleTracker.addBattle(new RouteScripted(to), attackingUnits, player, bridge, null, null);
battle = battleTracker.getPendingBattle(to, false, BattleType.NORMAL);
if (battle instanceof MustFightBattle) {
// this is an ugly mess of hacks, but will have to stay here till all transport related code is gutted and
// refactored.
final MustFightBattle mfb = (MustFightBattle) battle;
final Collection<Territory> neighborsLand = data.getMap().getNeighbors(to, Matches.territoryIsLand());
if (attackingUnits.stream().anyMatch(Matches.unitIsTransport())) {
// first, we have to reset the "transportedBy" setting for all the land units that were offloaded
final CompositeChange change1 = new CompositeChange();
mfb.reLoadTransports(attackingUnits, change1);
if (!change1.isEmpty()) {
bridge.addChange(change1);
}
// after that is applied, we have to make a map of all dependencies
final Map<Unit, Collection<Unit>> dependenciesForMfb = TransportTracker.transporting(attackingUnits, attackingUnits);
for (final Unit transport : CollectionUtils.getMatches(attackingUnits, Matches.unitIsTransport())) {
// BUT it must still hold all transports
if (!dependenciesForMfb.containsKey(transport)) {
dependenciesForMfb.put(transport, new ArrayList<>());
}
}
final Map<Territory, Map<Unit, Collection<Unit>>> dependencies = new HashMap<>();
dependencies.put(to, dependenciesForMfb);
for (final Territory t : neighborsLand) {
// All other maps, must hold only the transported units that in their territory
final Collection<Unit> allNeighborUnits = new ArrayList<>(CollectionUtils.getMatches(attackingUnits, Matches.unitIsTransport()));
allNeighborUnits.addAll(t.getUnits().getMatches(Matches.unitIsLandAndOwnedBy(player)));
final Map<Unit, Collection<Unit>> dependenciesForNeighbors = TransportTracker.transporting(CollectionUtils.getMatches(allNeighborUnits, Matches.unitIsTransport()), CollectionUtils.getMatches(allNeighborUnits, Matches.unitIsTransport().negate()));
dependencies.put(t, dependenciesForNeighbors);
}
mfb.addDependentUnits(dependencies.get(to));
for (final Territory territoryNeighborToNewBattle : neighborsLand) {
final IBattle battleInTerritoryNeighborToNewBattle = battleTracker.getPendingBattle(territoryNeighborToNewBattle, false, BattleType.NORMAL);
if (battleInTerritoryNeighborToNewBattle instanceof MustFightBattle) {
final MustFightBattle mfbattleInTerritoryNeighborToNewBattle = (MustFightBattle) battleInTerritoryNeighborToNewBattle;
mfbattleInTerritoryNeighborToNewBattle.addDependentUnits(dependencies.get(territoryNeighborToNewBattle));
} else if (battleInTerritoryNeighborToNewBattle instanceof NonFightingBattle) {
final NonFightingBattle nfbattleInTerritoryNeighborToNewBattle = (NonFightingBattle) battleInTerritoryNeighborToNewBattle;
nfbattleInTerritoryNeighborToNewBattle.addDependentUnits(dependencies.get(territoryNeighborToNewBattle));
}
}
}
if (attackingUnits.stream().anyMatch(Matches.unitIsAir().negate())) {
// TODO: for now, we will hack and say that the attackers came from Everywhere, and hope the user will
// choose the correct place
// to retreat to! (TODO: Fix this)
final Map<Territory, Collection<Unit>> attackingFromMap = new HashMap<>();
final Collection<Territory> neighbors = data.getMap().getNeighbors(to, (Matches.territoryIsLand().test(to) ? Matches.territoryIsLand() : Matches.territoryIsWater()));
// neighbors.removeAll(Matches.getMatches(neighbors, Matches.territoryHasEnemyUnits(player, data)));
for (final Territory t : neighbors) {
attackingFromMap.put(t, attackingUnits);
}
mfb.setAttackingFromAndMap(attackingFromMap);
}
}
} else if (battle instanceof MustFightBattle) {
((MustFightBattle) battle).resetDefendingUnits(player, data);
}
// now make sure any amphibious battles that are dependent on this 'new' sea battle have their dependencies set.
if (to.isWater()) {
for (final Territory t : data.getMap().getNeighbors(to, Matches.territoryIsLand())) {
final IBattle battleAmphib = battleTracker.getPendingBattle(t, false, BattleType.NORMAL);
if (battleAmphib != null) {
if (!battleTracker.getDependentOn(battle).contains(battleAmphib)) {
battleTracker.addDependency(battleAmphib, battle);
}
if (battleAmphib instanceof MustFightBattle) {
// and we want to reset the defenders if the scrambling air has left that battle
((MustFightBattle) battleAmphib).resetDefendingUnits(player, data);
}
}
}
}
}
}
use of games.strategy.triplea.attachments.UnitAttachment in project triplea by triplea-game.
the class BattleDelegate method fireKamikazeSuicideAttacks.
/**
* This rolls the dice and validates them to see if units died or not.
* It will use LowLuck or normal dice.
* If any units die, we remove them from the game, and if units take damage but live, we also do that here.
*/
private void fireKamikazeSuicideAttacks(final Unit unitUnderFire, final IntegerMap<Resource> numberOfAttacks, final IntegerMap<Resource> resourcesAndAttackValues, final PlayerID firingEnemy, final Territory location) {
// TODO: find a way to autosave after each dice roll.
final GameData data = getData();
final int diceSides = data.getDiceSides();
final CompositeChange change = new CompositeChange();
int hits = 0;
int[] rolls = null;
if (Properties.getLowLuck(data)) {
int power = 0;
for (final Entry<Resource, Integer> entry : numberOfAttacks.entrySet()) {
final Resource r = entry.getKey();
final int num = entry.getValue();
change.add(ChangeFactory.changeResourcesChange(firingEnemy, r, -num));
power += num * resourcesAndAttackValues.getInt(r);
}
if (power > 0) {
hits = power / diceSides;
final int remainder = power % diceSides;
if (remainder > 0) {
rolls = bridge.getRandom(diceSides, 1, firingEnemy, DiceType.COMBAT, "Rolling for remainder in Kamikaze Suicide Attack on unit: " + unitUnderFire.getType().getName());
if (remainder > rolls[0]) {
hits++;
}
}
}
} else {
// avoid multiple calls of getRandom, so just do it once at the beginning
final int numTokens = numberOfAttacks.totalValues();
rolls = bridge.getRandom(diceSides, numTokens, firingEnemy, DiceType.COMBAT, "Rolling for Kamikaze Suicide Attack on unit: " + unitUnderFire.getType().getName());
final int[] powerOfTokens = new int[numTokens];
int j = 0;
for (final Entry<Resource, Integer> entry : numberOfAttacks.entrySet()) {
final Resource r = entry.getKey();
int num = entry.getValue();
change.add(ChangeFactory.changeResourcesChange(firingEnemy, r, -num));
final int power = resourcesAndAttackValues.getInt(r);
while (num > 0) {
powerOfTokens[j] = power;
j++;
num--;
}
}
for (int i = 0; i < rolls.length; i++) {
if (powerOfTokens[i] > rolls[i]) {
hits++;
}
}
}
final String title = "Kamikaze Suicide Attack attacks " + MyFormatter.unitsToText(Collections.singleton(unitUnderFire));
final String dice = " scoring " + hits + " hits. Rolls: " + MyFormatter.asDice(rolls);
bridge.getHistoryWriter().startEvent(title + dice, unitUnderFire);
if (hits > 0) {
final UnitAttachment ua = UnitAttachment.get(unitUnderFire.getType());
final int currentHits = unitUnderFire.getHits();
if (ua.getHitPoints() <= currentHits + hits) {
// TODO: kill dependents
change.add(ChangeFactory.removeUnits(location, Collections.singleton(unitUnderFire)));
} else {
final IntegerMap<Unit> hitMap = new IntegerMap<>();
hitMap.put(unitUnderFire, hits);
change.add(createDamageChange(hitMap, bridge));
}
}
if (!change.isEmpty()) {
bridge.addChange(change);
}
// kamikaze suicide attacks, even if unsuccessful, deny the ability to bombard from this sea zone
battleTracker.addNoBombardAllowedFromHere(location);
// TODO: display this as actual dice for both players
final Collection<PlayerID> playersInvolved = new ArrayList<>();
playersInvolved.add(player);
playersInvolved.add(firingEnemy);
this.getDisplay().reportMessageToPlayers(playersInvolved, null, title + dice, title);
}
Aggregations