diff --git a/.gitattributes b/.gitattributes index 2a5f214a7f7..ab3aa841e64 100644 --- a/.gitattributes +++ b/.gitattributes @@ -14477,6 +14477,7 @@ src/main/java/forge/game/ai/ComputerUtilCost.java -text src/main/java/forge/game/ai/ComputerUtilMana.java -text src/main/java/forge/game/combat/AttackingBand.java -text src/main/java/forge/game/combat/Combat.java svneol=native#text/plain +src/main/java/forge/game/combat/CombatLki.java -text src/main/java/forge/game/combat/CombatUtil.java svneol=native#text/plain src/main/java/forge/game/event/GameEvent.java -text src/main/java/forge/game/event/GameEventAnteCardsSelected.java -text diff --git a/src/main/java/forge/game/GameAction.java b/src/main/java/forge/game/GameAction.java index 6a918f0092b..4fd6f17597b 100644 --- a/src/main/java/forge/game/GameAction.java +++ b/src/main/java/forge/game/GameAction.java @@ -224,7 +224,10 @@ public class GameAction { if (zoneFrom != null) { if (zoneFrom.is(ZoneType.Battlefield) && c.isCreature() && game.getCombat() != null) { + if ( !zoneTo.is(ZoneType.Battlefield) ) + game.getCombat().saveLKI(lastKnownInfo); game.getCombat().removeFromCombat(c); + } zoneFrom.remove(c); } @@ -233,11 +236,7 @@ public class GameAction { final HashMap runParams = new HashMap(); runParams.put("Card", lastKnownInfo); - if (zoneFrom != null) { - runParams.put("Origin", zoneFrom.getZoneType().name()); - } else { - runParams.put("Origin", null); - } + runParams.put("Origin", zoneFrom != null ? zoneFrom.getZoneType().name() : null); runParams.put("Destination", zoneTo.getZoneType().name()); game.getTriggerHandler().runTrigger(TriggerType.ChangesZone, runParams, false); // AllZone.getStack().chooseOrderOfSimultaneousStackEntryAll(); @@ -1350,9 +1349,6 @@ public class GameAction { final boolean persist = (c.hasKeyword("Persist") && (c.getCounters(CounterType.M1M1) == 0)) && !c.isToken(); final boolean undying = (c.hasKeyword("Undying") && (c.getCounters(CounterType.P1P1) == 0)) && !c.isToken(); - if (game.getPhaseHandler().inCombat()) - game.getPhaseHandler().getCombat().removeFromCombat(c); - final Card newCard = this.moveToGraveyard(c); // Destroy needs to be called with Last Known Information diff --git a/src/main/java/forge/game/combat/Combat.java b/src/main/java/forge/game/combat/Combat.java index 27e0ef56e2e..a719635ed0f 100644 --- a/src/main/java/forge/game/combat/Combat.java +++ b/src/main/java/forge/game/combat/Combat.java @@ -24,8 +24,6 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; -import org.apache.commons.lang3.tuple.Pair; - import com.google.common.collect.Lists; import forge.Card; @@ -59,6 +57,7 @@ public class Combat { private Map> attackersOrderedForDamageAssignment = new HashMap>(); private Map> blockersOrderedForDamageAssignment = new HashMap>(); + private Map lkiCache = new HashMap(); public Combat(Player attacker) { @@ -104,7 +103,6 @@ public class Combat { return result; } - public final void addAttacker(final Card c, GameEntity defender) { addAttacker(c, defender, null); } @@ -125,13 +123,7 @@ public class Combat { } public final GameEntity getDefenderByAttacker(final Card c) { - for(Entry> e : attackedEntities.entrySet()) { - for(AttackingBand ab : e.getValue()) { - if ( ab.contains(c) ) - return e.getKey(); - } - } - return null; + return getDefenderByAttacker(getBandOfAttacker(c)); } public final GameEntity getDefenderByAttacker(final AttackingBand c) { @@ -160,6 +152,10 @@ public class Combat { } public final AttackingBand getBandOfAttacker(final Card c) { + if ( !c.isInPlay() ) { + CombatLki lki = lkiCache.get(c); + return lki == null ? null : lki.getFirstBand(); + } for(Collection abs : attackedEntities.values()) { for(AttackingBand ab : abs) { if ( ab.contains(c) ) @@ -182,10 +178,11 @@ public class Combat { } public boolean isAttacking(Card card, GameEntity defender) { + AttackingBand ab = getBandOfAttacker(card); + for(Entry> ee : attackedEntities.entrySet()) - for(AttackingBand ab : ee.getValue()) - if (ab.contains(card)) - return ee.getKey() == defender; + if ( ee.getValue().contains(ab) ) + return ee.getKey() == defender; return false; } @@ -258,6 +255,15 @@ public class Combat { } return blocked; } + + public final List getAttackingBandsBlockedBy(Card blocker) { + List bands = Lists.newArrayList(); + for( Entry> kv : blockedBands.entrySet()) { + if (kv.getValue().contains(blocker)) + bands.add(kv.getKey()); + } + return bands; + } public Player getDefendingPlayerRelatedTo(final Card source) { Card attacker = source; @@ -333,24 +339,13 @@ public class Combat { } } } - - private void removeAttackerFromBand(AttackingBand ab, Card c) { - ab.removeAttacker(c); - - // removed - if (ab.isEmpty()) { - blockedBands.remove(ab); - for(Collection abs: attackedEntities.values()) - abs.remove(ab); - } - } // remove a combatant whose side is unknown public final void removeFromCombat(final Card c) { AttackingBand ab = getBandOfAttacker(c); if (ab != null) { unregisterAttacker(c, ab); - removeAttackerFromBand(ab, c); + ab.removeAttacker(c); } // if not found in attackers, look for this card in blockers @@ -365,7 +360,6 @@ public class Combat { public final void removeAbsentCombatants() { // iterate all attackers and remove them - List> toRemoveAtk = new ArrayList<>(); for(Entry> ee : attackedEntities.entrySet()) { for(AttackingBand ab : ee.getValue()) { List atk = ab.getAttackers(); @@ -373,15 +367,10 @@ public class Combat { Card c = atk.get(i); if ( !c.isInPlay() ) { unregisterAttacker(c, ab); - toRemoveAtk.add(Pair.of(ab, c)); } } } } - // remove as a separate step to avoid modifications with iterator open in for-loop - for(Pair pair : toRemoveAtk) { - removeAttackerFromBand(pair.getKey(), pair.getValue()); - } Collection toRemove = Lists.newArrayList(); for(Entry> be : blockedBands.entrySet()) { @@ -389,7 +378,6 @@ public class Combat { for( Card b : be.getValue()) { if ( !b.isInPlay() ) { unregisterDefender(b, be.getKey()); - toRemove.add(b); } } be.getValue().removeAll(toRemove); @@ -665,4 +653,21 @@ public class Combat { return blockers != null && blockers.contains(blocker); } + /** + * TODO: Write javadoc for this method. + * @param lastKnownInfo + */ + public void saveLKI(Card lastKnownInfo) { + List attackersBlocked = null; + AttackingBand attackingBand = getBandOfAttacker(lastKnownInfo); + boolean isAttacker = attackingBand != null; + if ( !isAttacker ) { + attackersBlocked= getAttackingBandsBlockedBy(lastKnownInfo); + if ( attackersBlocked.isEmpty() ) + return; // card was not even in combat + } + List relatedBands = isAttacker ? Lists.newArrayList(attackingBand) : attackersBlocked; + lkiCache.put(lastKnownInfo, new CombatLki(isAttacker, relatedBands)); + } + } // Class Combat diff --git a/src/main/java/forge/game/combat/CombatLki.java b/src/main/java/forge/game/combat/CombatLki.java new file mode 100644 index 00000000000..6deb9a7df1b --- /dev/null +++ b/src/main/java/forge/game/combat/CombatLki.java @@ -0,0 +1,25 @@ +package forge.game.combat; + +import java.util.Collections; +import java.util.List; + +/** + * TODO: Write javadoc for this type. + * + */ +public class CombatLki { + + public final List relatedBands; + public final boolean isAttacker; + + + public CombatLki(boolean isAttacker, List relatedBands) { + this.isAttacker = isAttacker; + this.relatedBands = Collections.unmodifiableList(relatedBands); + } + + public AttackingBand getFirstBand() { + return relatedBands.isEmpty() ? null : relatedBands.get(0); + } + +}