diff --git a/.gitattributes b/.gitattributes
index 8fd62b6aa56..31542bc92b4 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -84,7 +84,6 @@ forge-core/src/main/java/forge/util/Aggregates.java -text
forge-core/src/main/java/forge/util/BinaryUtil.java -text
forge-core/src/main/java/forge/util/CollectionSuppliers.java -text
forge-core/src/main/java/forge/util/ComparableOp.java -text
-forge-core/src/main/java/forge/util/Expressions.java -text
forge-core/src/main/java/forge/util/FileSection.java -text
forge-core/src/main/java/forge/util/FileSectionManual.java -text
forge-core/src/main/java/forge/util/FileUtil.java svneol=native#text/plain
@@ -96,7 +95,6 @@ forge-core/src/main/java/forge/util/ItemPoolSorter.java -text
forge-core/src/main/java/forge/util/Lang.java -text
forge-core/src/main/java/forge/util/MyRandom.java svneol=native#text/plain
forge-core/src/main/java/forge/util/PredicateString.java -text
-forge-core/src/main/java/forge/util/ReflectionUtil.java -text
forge-core/src/main/java/forge/util/TextUtil.java -text
forge-core/src/main/java/forge/util/ThreadUtil.java -text
forge-core/src/main/java/forge/util/package-info.java -text
@@ -115,506 +113,10 @@ forge-game/.settings/org.eclipse.core.resources.prefs -text
forge-game/.settings/org.eclipse.jdt.core.prefs -text
forge-game/.settings/org.eclipse.m2e.core.prefs -text
forge-game/pom.xml -text
-forge-game/src/main/java/forge/Command.java -text
-forge-game/src/main/java/forge/Constant.java -text
-forge-game/src/main/java/forge/ImageCacheBridge.java -text
-forge-game/src/main/java/forge/PreferencesBridge.java -text
-forge-game/src/main/java/forge/ai/AiAttackController.java -text
-forge-game/src/main/java/forge/ai/AiBlockController.java -text
-forge-game/src/main/java/forge/ai/AiController.java -text
-forge-game/src/main/java/forge/ai/AiCostDecision.java -text
-forge-game/src/main/java/forge/ai/AiProfileUtil.java -text
-forge-game/src/main/java/forge/ai/ComputerUtil.java -text
-forge-game/src/main/java/forge/ai/ComputerUtilCard.java -text
-forge-game/src/main/java/forge/ai/ComputerUtilCombat.java -text
-forge-game/src/main/java/forge/ai/ComputerUtilCost.java -text
-forge-game/src/main/java/forge/ai/ComputerUtilMana.java -text
-forge-game/src/main/java/forge/ai/SpellAbilityAi.java -text
-forge-game/src/main/java/forge/ai/ability/AddPhaseAi.java -text
-forge-game/src/main/java/forge/ai/ability/AddTurnAi.java -text
-forge-game/src/main/java/forge/ai/ability/AlwaysPlayAi.java -text
-forge-game/src/main/java/forge/ai/ability/AnimateAi.java -text
-forge-game/src/main/java/forge/ai/ability/AnimateAllAi.java -text
-forge-game/src/main/java/forge/ai/ability/AttachAi.java -text
-forge-game/src/main/java/forge/ai/ability/BalanceAi.java -text
-forge-game/src/main/java/forge/ai/ability/BecomesBlockedAi.java -text
-forge-game/src/main/java/forge/ai/ability/BondAi.java -text
-forge-game/src/main/java/forge/ai/ability/CanPlayAsDrawbackAi.java -text
-forge-game/src/main/java/forge/ai/ability/CannotPlayAi.java -text
-forge-game/src/main/java/forge/ai/ability/ChangeTargetsAi.java -text
-forge-game/src/main/java/forge/ai/ability/ChangeZoneAi.java -text
-forge-game/src/main/java/forge/ai/ability/ChangeZoneAllAi.java -text
-forge-game/src/main/java/forge/ai/ability/CharmAi.java -text
-forge-game/src/main/java/forge/ai/ability/ChooseCardAi.java -text
-forge-game/src/main/java/forge/ai/ability/ChooseCardNameAi.java -text
-forge-game/src/main/java/forge/ai/ability/ChooseColorAi.java -text
-forge-game/src/main/java/forge/ai/ability/ChooseGenericEffectAi.java -text
-forge-game/src/main/java/forge/ai/ability/ChoosePlayerAi.java -text
-forge-game/src/main/java/forge/ai/ability/ChooseSourceAi.java -text
-forge-game/src/main/java/forge/ai/ability/ChooseTypeAi.java -text
-forge-game/src/main/java/forge/ai/ability/ClashAi.java -text
-forge-game/src/main/java/forge/ai/ability/CloneAi.java -text
-forge-game/src/main/java/forge/ai/ability/ControlExchangeAi.java -text
-forge-game/src/main/java/forge/ai/ability/ControlGainAi.java -text
-forge-game/src/main/java/forge/ai/ability/CopyPermanentAi.java -text
-forge-game/src/main/java/forge/ai/ability/CounterAi.java -text
-forge-game/src/main/java/forge/ai/ability/CountersAi.java -text
-forge-game/src/main/java/forge/ai/ability/CountersMoveAi.java -text
-forge-game/src/main/java/forge/ai/ability/CountersProliferateAi.java -text
-forge-game/src/main/java/forge/ai/ability/CountersPutAi.java -text
-forge-game/src/main/java/forge/ai/ability/CountersPutAllAi.java -text
-forge-game/src/main/java/forge/ai/ability/CountersPutOrRemoveAi.java -text
-forge-game/src/main/java/forge/ai/ability/CountersRemoveAi.java -text
-forge-game/src/main/java/forge/ai/ability/DamageAiBase.java -text
-forge-game/src/main/java/forge/ai/ability/DamageAllAi.java -text
-forge-game/src/main/java/forge/ai/ability/DamageDealAi.java -text
-forge-game/src/main/java/forge/ai/ability/DamageEachAi.java -text
-forge-game/src/main/java/forge/ai/ability/DamagePreventAi.java -text
-forge-game/src/main/java/forge/ai/ability/DamagePreventAllAi.java -text
-forge-game/src/main/java/forge/ai/ability/DebuffAi.java -text
-forge-game/src/main/java/forge/ai/ability/DebuffAllAi.java -text
-forge-game/src/main/java/forge/ai/ability/DelayedTriggerAi.java -text
-forge-game/src/main/java/forge/ai/ability/DestroyAi.java -text
-forge-game/src/main/java/forge/ai/ability/DestroyAllAi.java -text
-forge-game/src/main/java/forge/ai/ability/DigAi.java -text
-forge-game/src/main/java/forge/ai/ability/DigUntilAi.java -text
-forge-game/src/main/java/forge/ai/ability/DiscardAi.java -text
-forge-game/src/main/java/forge/ai/ability/DrainManaAi.java -text
-forge-game/src/main/java/forge/ai/ability/DrawAi.java -text
-forge-game/src/main/java/forge/ai/ability/EffectAi.java -text
-forge-game/src/main/java/forge/ai/ability/EncodeAi.java -text
-forge-game/src/main/java/forge/ai/ability/EndTurnAi.java -text
-forge-game/src/main/java/forge/ai/ability/FightAi.java -text
-forge-game/src/main/java/forge/ai/ability/FlipACoinAi.java -text
-forge-game/src/main/java/forge/ai/ability/FogAi.java -text
-forge-game/src/main/java/forge/ai/ability/GameLossAi.java -text
-forge-game/src/main/java/forge/ai/ability/GameWinAi.java -text
-forge-game/src/main/java/forge/ai/ability/HauntAi.java -text
-forge-game/src/main/java/forge/ai/ability/LegendaryRuleAi.java -text
-forge-game/src/main/java/forge/ai/ability/LifeExchangeAi.java -text
-forge-game/src/main/java/forge/ai/ability/LifeGainAi.java -text
-forge-game/src/main/java/forge/ai/ability/LifeLoseAi.java -text
-forge-game/src/main/java/forge/ai/ability/LifeSetAi.java -text
-forge-game/src/main/java/forge/ai/ability/ManaEffectAi.java -text
-forge-game/src/main/java/forge/ai/ability/MillAi.java -text
-forge-game/src/main/java/forge/ai/ability/MustAttackAi.java -text
-forge-game/src/main/java/forge/ai/ability/MustBlockAi.java -text
-forge-game/src/main/java/forge/ai/ability/PeekAndRevealAi.java -text
-forge-game/src/main/java/forge/ai/ability/PermanentCreatureAi.java -text
-forge-game/src/main/java/forge/ai/ability/PermanentNoncreatureAi.java -text
-forge-game/src/main/java/forge/ai/ability/PhasesAi.java -text
-forge-game/src/main/java/forge/ai/ability/PlayAi.java -text
-forge-game/src/main/java/forge/ai/ability/PoisonAi.java -text
-forge-game/src/main/java/forge/ai/ability/PowerExchangeAi.java -text
-forge-game/src/main/java/forge/ai/ability/ProtectAi.java -text
-forge-game/src/main/java/forge/ai/ability/ProtectAllAi.java -text
-forge-game/src/main/java/forge/ai/ability/PumpAi.java -text
-forge-game/src/main/java/forge/ai/ability/PumpAiBase.java -text
-forge-game/src/main/java/forge/ai/ability/PumpAllAi.java -text
-forge-game/src/main/java/forge/ai/ability/RearrangeTopOfLibraryAi.java -text
-forge-game/src/main/java/forge/ai/ability/RegenerateAi.java -text
-forge-game/src/main/java/forge/ai/ability/RegenerateAllAi.java -text
-forge-game/src/main/java/forge/ai/ability/RemoveFromCombatAi.java -text
-forge-game/src/main/java/forge/ai/ability/RepeatAi.java -text
-forge-game/src/main/java/forge/ai/ability/RepeatEachAi.java -text
-forge-game/src/main/java/forge/ai/ability/RestartGameAi.java -text
-forge-game/src/main/java/forge/ai/ability/RevealAi.java -text
-forge-game/src/main/java/forge/ai/ability/RevealAiBase.java -text
-forge-game/src/main/java/forge/ai/ability/RevealHandAi.java -text
-forge-game/src/main/java/forge/ai/ability/RollPlanarDiceAi.java -text
-forge-game/src/main/java/forge/ai/ability/SacrificeAi.java -text
-forge-game/src/main/java/forge/ai/ability/SacrificeAllAi.java -text
-forge-game/src/main/java/forge/ai/ability/ScryAi.java -text
-forge-game/src/main/java/forge/ai/ability/SetStateAi.java -text
-forge-game/src/main/java/forge/ai/ability/ShuffleAi.java -text
-forge-game/src/main/java/forge/ai/ability/StoreSVarAi.java -text
-forge-game/src/main/java/forge/ai/ability/TapAi.java -text
-forge-game/src/main/java/forge/ai/ability/TapAiBase.java -text
-forge-game/src/main/java/forge/ai/ability/TapAllAi.java -text
-forge-game/src/main/java/forge/ai/ability/TapOrUntapAi.java -text
-forge-game/src/main/java/forge/ai/ability/TapOrUntapAllAi.java -text
-forge-game/src/main/java/forge/ai/ability/TokenAi.java -text
-forge-game/src/main/java/forge/ai/ability/TwoPilesAi.java -text
-forge-game/src/main/java/forge/ai/ability/UnattachAllAi.java -text
-forge-game/src/main/java/forge/ai/ability/UntapAi.java -text
-forge-game/src/main/java/forge/ai/ability/UntapAllAi.java -text
-forge-game/src/main/java/forge/ai/ability/ZoneExchangeAi.java -text
-forge-game/src/main/java/forge/game/Game.java -text
-forge-game/src/main/java/forge/game/GameAction.java -text
-forge-game/src/main/java/forge/game/GameActionUtil.java -text
-forge-game/src/main/java/forge/game/GameEndReason.java -text
-forge-game/src/main/java/forge/game/GameEntity.java -text
forge-game/src/main/java/forge/game/GameFormat.java -text
-forge-game/src/main/java/forge/game/GameLog.java -text
-forge-game/src/main/java/forge/game/GameLogEntry.java -text
-forge-game/src/main/java/forge/game/GameLogEntryType.java -text
-forge-game/src/main/java/forge/game/GameLogFormatter.java -text
-forge-game/src/main/java/forge/game/GameObject.java -text
-forge-game/src/main/java/forge/game/GameOutcome.java -text
-forge-game/src/main/java/forge/game/GameStage.java -text
-forge-game/src/main/java/forge/game/GameType.java -text
-forge-game/src/main/java/forge/game/GlobalRuleChange.java -text
-forge-game/src/main/java/forge/game/Match.java -text
-forge-game/src/main/java/forge/game/PlanarDice.java -text
-forge-game/src/main/java/forge/game/StaticEffect.java -text
-forge-game/src/main/java/forge/game/StaticEffects.java -text
-forge-game/src/main/java/forge/game/TriggerReplacementBase.java -text
-forge-game/src/main/java/forge/game/ability/AbilityApiBased.java -text
-forge-game/src/main/java/forge/game/ability/AbilityFactory.java -text
-forge-game/src/main/java/forge/game/ability/AbilityUtils.java -text
-forge-game/src/main/java/forge/game/ability/ApiType.java -text
-forge-game/src/main/java/forge/game/ability/SaTargetRoutines.java -text
-forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java -text
-forge-game/src/main/java/forge/game/ability/SpellApiBased.java -text
-forge-game/src/main/java/forge/game/ability/StaticAbilityApiBased.java -text
-forge-game/src/main/java/forge/game/ability/effects/AbandonEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/AddPhaseEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/AddTurnEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/AnimateAllEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/AnimateEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/AnimateEffectBase.java -text
-forge-game/src/main/java/forge/game/ability/effects/AttachEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/BalanceEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/BecomesBlockedEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/BondEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/ChangeTargetsEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/ChangeZoneAllEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/CharmEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/ChooseCardEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/ChooseCardNameEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/ChooseColorEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/ChooseGenericEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/ChooseNumberEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/ChoosePlayerEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/ChooseSourceEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/ChooseTypeEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/ClashEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/CleanUpEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/CloneEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/ControlExchangeEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/ControlGainEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/ControlPlayerEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/CopyPermanentEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/CopySpellAbilityEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/CounterEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/CountersMoveEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/CountersProliferateEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/CountersPutAllEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/CountersPutEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/CountersPutOrRemoveEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/CountersRemoveAllEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/CountersRemoveEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/DamageAllEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/DamageDealEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/DamageEachEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/DamagePreventAllEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/DamagePreventEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/DebuffAllEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/DebuffEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/DeclareCombatantsEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/DelayedTriggerEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/DestroyAllEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/DestroyEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/DigEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/DigUntilEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/DiscardEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/DrainManaEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/DrawEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/ETBReplacementEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/EffectEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/EncodeEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/EndTurnEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/FightEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/FlipCoinEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/FogEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/GameLossEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/GameWinEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/LifeExchangeEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/LifeGainEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/LifeLoseEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/LifeSetEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/ManaEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/ManaReflectedEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/MillEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/MustAttackEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/MustBlockEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/OwnershipGainEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/PeekAndRevealEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/PermanentCreatureEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/PermanentNoncreatureEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/PhasesEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/PlaneswalkEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/PlayEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/PoisonEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/PowerExchangeEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/ProtectAllEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/ProtectEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/PumpAllEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/PumpEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/RearrangeTopOfLibraryEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/RegenerateAllEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/RegenerateEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/RemoveFromCombatEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/ReorderZoneEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/RepeatEachEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/RepeatEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/RestartGameEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/RevealEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/RevealHandEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/RollPlanarDiceEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/RunSVarAbilityEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/SacrificeAllEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/SacrificeEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/ScryEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/SetInMotionEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/SetStateEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/ShuffleEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/SkipTurnEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/StoreSVarEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/TapAllEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/TapEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/TapOrUntapAllEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/TapOrUntapEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/TokenEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/TwoPilesEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/UnattachAllEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/UntapAllEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/UntapEffect.java -text
-forge-game/src/main/java/forge/game/ability/effects/ZoneExchangeEffect.java -text
-forge-game/src/main/java/forge/game/ability/package-info.java -text
-forge-game/src/main/java/forge/game/card/Card.java -text
-forge-game/src/main/java/forge/game/card/CardCharacteristics.java -text
-forge-game/src/main/java/forge/game/card/CardColor.java -text
-forge-game/src/main/java/forge/game/card/CardDamageHistory.java -text
-forge-game/src/main/java/forge/game/card/CardFactory.java -text
-forge-game/src/main/java/forge/game/card/CardFactoryUtil.java -text
-forge-game/src/main/java/forge/game/card/CardKeywords.java -text
-forge-game/src/main/java/forge/game/card/CardLists.java -text
-forge-game/src/main/java/forge/game/card/CardPowerToughness.java -text
-forge-game/src/main/java/forge/game/card/CardPredicates.java -text
-forge-game/src/main/java/forge/game/card/CardShields.java -text
-forge-game/src/main/java/forge/game/card/CardType.java -text
-forge-game/src/main/java/forge/game/card/CardUtil.java -text
-forge-game/src/main/java/forge/game/card/CounterType.java -text
forge-game/src/main/java/forge/game/card/package-info.java -text
-forge-game/src/main/java/forge/game/combat/AttackingBand.java -text
-forge-game/src/main/java/forge/game/combat/Combat.java -text
-forge-game/src/main/java/forge/game/combat/CombatLki.java -text
-forge-game/src/main/java/forge/game/combat/CombatUtil.java -text
-forge-game/src/main/java/forge/game/cost/Cost.java -text
-forge-game/src/main/java/forge/game/cost/CostAddMana.java -text
-forge-game/src/main/java/forge/game/cost/CostChooseCreatureType.java -text
-forge-game/src/main/java/forge/game/cost/CostDamage.java -text
-forge-game/src/main/java/forge/game/cost/CostDecisionMakerBase.java -text
-forge-game/src/main/java/forge/game/cost/CostDiscard.java -text
-forge-game/src/main/java/forge/game/cost/CostDraw.java -text
-forge-game/src/main/java/forge/game/cost/CostExile.java -text
-forge-game/src/main/java/forge/game/cost/CostExiledMoveToGrave.java -text
-forge-game/src/main/java/forge/game/cost/CostFlipCoin.java -text
-forge-game/src/main/java/forge/game/cost/CostGainControl.java -text
-forge-game/src/main/java/forge/game/cost/CostGainLife.java -text
-forge-game/src/main/java/forge/game/cost/CostMill.java -text
-forge-game/src/main/java/forge/game/cost/CostPart.java -text
-forge-game/src/main/java/forge/game/cost/CostPartMana.java -text
-forge-game/src/main/java/forge/game/cost/CostPartWithList.java -text
-forge-game/src/main/java/forge/game/cost/CostPayLife.java -text
-forge-game/src/main/java/forge/game/cost/CostPayment.java -text
-forge-game/src/main/java/forge/game/cost/CostPutCardToLib.java -text
-forge-game/src/main/java/forge/game/cost/CostPutCounter.java -text
-forge-game/src/main/java/forge/game/cost/CostRemoveAnyCounter.java -text
-forge-game/src/main/java/forge/game/cost/CostRemoveCounter.java -text
-forge-game/src/main/java/forge/game/cost/CostReturn.java -text
-forge-game/src/main/java/forge/game/cost/CostReveal.java -text
-forge-game/src/main/java/forge/game/cost/CostSacrifice.java -text
-forge-game/src/main/java/forge/game/cost/CostTap.java -text
-forge-game/src/main/java/forge/game/cost/CostTapType.java -text
-forge-game/src/main/java/forge/game/cost/CostUnattach.java -text
-forge-game/src/main/java/forge/game/cost/CostUntap.java -text
-forge-game/src/main/java/forge/game/cost/CostUntapType.java -text
-forge-game/src/main/java/forge/game/cost/ICostVisitor.java -text
-forge-game/src/main/java/forge/game/cost/PaymentDecision.java -text
-forge-game/src/main/java/forge/game/cost/package-info.java -text
-forge-game/src/main/java/forge/game/event/EventValueChangeType.java -text
-forge-game/src/main/java/forge/game/event/GameEvent.java -text
-forge-game/src/main/java/forge/game/event/GameEventAnteCardsSelected.java -text
-forge-game/src/main/java/forge/game/event/GameEventAttackersDeclared.java -text
-forge-game/src/main/java/forge/game/event/GameEventBlockersDeclared.java -text
-forge-game/src/main/java/forge/game/event/GameEventCardAttachment.java -text
-forge-game/src/main/java/forge/game/event/GameEventCardChangeZone.java -text
-forge-game/src/main/java/forge/game/event/GameEventCardCounters.java -text
-forge-game/src/main/java/forge/game/event/GameEventCardDamaged.java -text
-forge-game/src/main/java/forge/game/event/GameEventCardDestroyed.java -text
-forge-game/src/main/java/forge/game/event/GameEventCardPhased.java -text
-forge-game/src/main/java/forge/game/event/GameEventCardRegenerated.java -text
-forge-game/src/main/java/forge/game/event/GameEventCardSacrificed.java -text
-forge-game/src/main/java/forge/game/event/GameEventCardStatsChanged.java -text
-forge-game/src/main/java/forge/game/event/GameEventCardTapped.java -text
-forge-game/src/main/java/forge/game/event/GameEventCombatEnded.java -text
-forge-game/src/main/java/forge/game/event/GameEventFlipCoin.java -text
-forge-game/src/main/java/forge/game/event/GameEventGameFinished.java -text
-forge-game/src/main/java/forge/game/event/GameEventGameOutcome.java -text
-forge-game/src/main/java/forge/game/event/GameEventGameRestarted.java -text
-forge-game/src/main/java/forge/game/event/GameEventGameStarted.java -text
-forge-game/src/main/java/forge/game/event/GameEventLandPlayed.java -text
-forge-game/src/main/java/forge/game/event/GameEventManaBurn.java -text
-forge-game/src/main/java/forge/game/event/GameEventManaPool.java -text
-forge-game/src/main/java/forge/game/event/GameEventMulligan.java -text
-forge-game/src/main/java/forge/game/event/GameEventPlayerControl.java -text
-forge-game/src/main/java/forge/game/event/GameEventPlayerDamaged.java -text
-forge-game/src/main/java/forge/game/event/GameEventPlayerLivesChanged.java -text
-forge-game/src/main/java/forge/game/event/GameEventPlayerPoisoned.java -text
-forge-game/src/main/java/forge/game/event/GameEventPlayerPriority.java -text
-forge-game/src/main/java/forge/game/event/GameEventShuffle.java -text
-forge-game/src/main/java/forge/game/event/GameEventSpellAbilityCast.java -text
-forge-game/src/main/java/forge/game/event/GameEventSpellRemovedFromStack.java -text
-forge-game/src/main/java/forge/game/event/GameEventSpellResolved.java -text
-forge-game/src/main/java/forge/game/event/GameEventTokenCreated.java -text
-forge-game/src/main/java/forge/game/event/GameEventTurnBegan.java -text
-forge-game/src/main/java/forge/game/event/GameEventTurnEnded.java -text
-forge-game/src/main/java/forge/game/event/GameEventTurnPhase.java -text
-forge-game/src/main/java/forge/game/event/GameEventZone.java -text
-forge-game/src/main/java/forge/game/event/IGameEventVisitor.java -text
-forge-game/src/main/java/forge/game/event/package-info.java -text
-forge-game/src/main/java/forge/game/mana/Mana.java -text
-forge-game/src/main/java/forge/game/mana/ManaCostBeingPaid.java -text
-forge-game/src/main/java/forge/game/mana/ManaPool.java -text
-forge-game/src/main/java/forge/game/mana/package-info.java -text
forge-game/src/main/java/forge/game/package-info.java -text
-forge-game/src/main/java/forge/game/phase/EndOfTurn.java -text
-forge-game/src/main/java/forge/game/phase/ExtraTurn.java -text
-forge-game/src/main/java/forge/game/phase/Phase.java -text
-forge-game/src/main/java/forge/game/phase/PhaseHandler.java -text
-forge-game/src/main/java/forge/game/phase/PhaseType.java -text
-forge-game/src/main/java/forge/game/phase/Untap.java -text
-forge-game/src/main/java/forge/game/phase/Upkeep.java -text
-forge-game/src/main/java/forge/game/phase/package-info.java -text
-forge-game/src/main/java/forge/game/player/GameLossReason.java -text
-forge-game/src/main/java/forge/game/player/IHasIcon.java -text
-forge-game/src/main/java/forge/game/player/LobbyPlayer.java -text
-forge-game/src/main/java/forge/game/player/LobbyPlayerAi.java -text
-forge-game/src/main/java/forge/game/player/Player.java -text
-forge-game/src/main/java/forge/game/player/PlayerActionConfirmMode.java -text
-forge-game/src/main/java/forge/game/player/PlayerController.java -text
-forge-game/src/main/java/forge/game/player/PlayerControllerAi.java -text
-forge-game/src/main/java/forge/game/player/PlayerOutcome.java -text
-forge-game/src/main/java/forge/game/player/PlayerStatistics.java -text
-forge-game/src/main/java/forge/game/player/RegisteredPlayer.java -text
-forge-game/src/main/java/forge/game/player/package-info.java -text
-forge-game/src/main/java/forge/game/replacement/ReplaceAddCounter.java -text
-forge-game/src/main/java/forge/game/replacement/ReplaceCounter.java -text
-forge-game/src/main/java/forge/game/replacement/ReplaceDamage.java -text
-forge-game/src/main/java/forge/game/replacement/ReplaceDestroy.java -text
-forge-game/src/main/java/forge/game/replacement/ReplaceDiscard.java -text
-forge-game/src/main/java/forge/game/replacement/ReplaceDraw.java -text
-forge-game/src/main/java/forge/game/replacement/ReplaceGainLife.java -text
-forge-game/src/main/java/forge/game/replacement/ReplaceGameLoss.java -text
-forge-game/src/main/java/forge/game/replacement/ReplaceMoved.java -text
-forge-game/src/main/java/forge/game/replacement/ReplaceProduceMana.java -text
-forge-game/src/main/java/forge/game/replacement/ReplaceSetInMotion.java -text
-forge-game/src/main/java/forge/game/replacement/ReplaceTurnFaceUp.java -text
-forge-game/src/main/java/forge/game/replacement/ReplaceUntap.java -text
-forge-game/src/main/java/forge/game/replacement/ReplacementEffect.java -text
-forge-game/src/main/java/forge/game/replacement/ReplacementHandler.java -text
-forge-game/src/main/java/forge/game/replacement/ReplacementLayer.java -text
-forge-game/src/main/java/forge/game/replacement/ReplacementResult.java -text
-forge-game/src/main/java/forge/game/replacement/ReplacementType.java -text
-forge-game/src/main/java/forge/game/replacement/package-info.java -text
-forge-game/src/main/java/forge/game/spellability/Ability.java -text
-forge-game/src/main/java/forge/game/spellability/AbilityActivated.java -text
-forge-game/src/main/java/forge/game/spellability/AbilityManaPart.java -text
-forge-game/src/main/java/forge/game/spellability/AbilityStatic.java -text
-forge-game/src/main/java/forge/game/spellability/AbilitySub.java -text
-forge-game/src/main/java/forge/game/spellability/AbilityTriggered.java -text
-forge-game/src/main/java/forge/game/spellability/ISpellAbility.java -text
-forge-game/src/main/java/forge/game/spellability/OptionalCost.java -text
-forge-game/src/main/java/forge/game/spellability/Spell.java -text
-forge-game/src/main/java/forge/game/spellability/SpellAbility.java -text
-forge-game/src/main/java/forge/game/spellability/SpellAbilityCondition.java -text
-forge-game/src/main/java/forge/game/spellability/SpellAbilityRestriction.java -text
-forge-game/src/main/java/forge/game/spellability/SpellAbilityStackInstance.java -text
-forge-game/src/main/java/forge/game/spellability/SpellAbilityVariables.java -text
-forge-game/src/main/java/forge/game/spellability/SpellPermanent.java -text
-forge-game/src/main/java/forge/game/spellability/TargetChoices.java -text
-forge-game/src/main/java/forge/game/spellability/TargetRestrictions.java -text
-forge-game/src/main/java/forge/game/spellability/package-info.java -text
-forge-game/src/main/java/forge/game/staticability/StaticAbility.java -text
-forge-game/src/main/java/forge/game/staticability/StaticAbilityCantAttackBlock.java -text
-forge-game/src/main/java/forge/game/staticability/StaticAbilityCantBeCast.java -text
-forge-game/src/main/java/forge/game/staticability/StaticAbilityCantTarget.java -text
-forge-game/src/main/java/forge/game/staticability/StaticAbilityContinuous.java -text
-forge-game/src/main/java/forge/game/staticability/StaticAbilityCostChange.java -text
-forge-game/src/main/java/forge/game/staticability/StaticAbilityETBTapped.java -text
-forge-game/src/main/java/forge/game/staticability/StaticAbilityMayLookAt.java -text
-forge-game/src/main/java/forge/game/staticability/StaticAbilityPreventDamage.java -text
-forge-game/src/main/java/forge/game/staticability/package-info.java -text
-forge-game/src/main/java/forge/game/trigger/Trigger.java -text
-forge-game/src/main/java/forge/game/trigger/TriggerAlways.java -text
-forge-game/src/main/java/forge/game/trigger/TriggerAttached.java -text
-forge-game/src/main/java/forge/game/trigger/TriggerAttackerBlocked.java -text
-forge-game/src/main/java/forge/game/trigger/TriggerAttackerUnblocked.java -text
-forge-game/src/main/java/forge/game/trigger/TriggerAttackersDeclared.java -text
-forge-game/src/main/java/forge/game/trigger/TriggerAttacks.java -text
-forge-game/src/main/java/forge/game/trigger/TriggerBecomeMonstrous.java -text
-forge-game/src/main/java/forge/game/trigger/TriggerBecomesTarget.java -text
-forge-game/src/main/java/forge/game/trigger/TriggerBlockersDeclared.java -text
-forge-game/src/main/java/forge/game/trigger/TriggerBlocks.java -text
-forge-game/src/main/java/forge/game/trigger/TriggerChampioned.java -text
-forge-game/src/main/java/forge/game/trigger/TriggerChangesController.java -text
-forge-game/src/main/java/forge/game/trigger/TriggerChangesZone.java -text
-forge-game/src/main/java/forge/game/trigger/TriggerClashed.java -text
-forge-game/src/main/java/forge/game/trigger/TriggerCombatDamageDoneOnce.java -text
-forge-game/src/main/java/forge/game/trigger/TriggerCounterAdded.java -text
-forge-game/src/main/java/forge/game/trigger/TriggerCounterRemoved.java -text
-forge-game/src/main/java/forge/game/trigger/TriggerCountered.java -text
-forge-game/src/main/java/forge/game/trigger/TriggerCycled.java -text
-forge-game/src/main/java/forge/game/trigger/TriggerDamageDone.java -text
-forge-game/src/main/java/forge/game/trigger/TriggerDestroyed.java -text
-forge-game/src/main/java/forge/game/trigger/TriggerDevoured.java -text
-forge-game/src/main/java/forge/game/trigger/TriggerDiscarded.java -text
-forge-game/src/main/java/forge/game/trigger/TriggerDrawn.java -text
-forge-game/src/main/java/forge/game/trigger/TriggerEvolved.java -text
-forge-game/src/main/java/forge/game/trigger/TriggerFlippedCoin.java -text
-forge-game/src/main/java/forge/game/trigger/TriggerHandler.java -text
-forge-game/src/main/java/forge/game/trigger/TriggerLandPlayed.java -text
-forge-game/src/main/java/forge/game/trigger/TriggerLifeGained.java -text
-forge-game/src/main/java/forge/game/trigger/TriggerLifeLost.java -text
-forge-game/src/main/java/forge/game/trigger/TriggerLosesGame.java -text
-forge-game/src/main/java/forge/game/trigger/TriggerNewGame.java -text
-forge-game/src/main/java/forge/game/trigger/TriggerPayCumulativeUpkeep.java -text
-forge-game/src/main/java/forge/game/trigger/TriggerPhase.java -text
-forge-game/src/main/java/forge/game/trigger/TriggerPlanarDice.java -text
-forge-game/src/main/java/forge/game/trigger/TriggerPlaneswalkedFrom.java -text
-forge-game/src/main/java/forge/game/trigger/TriggerPlaneswalkedTo.java -text
-forge-game/src/main/java/forge/game/trigger/TriggerSacrificed.java -text
-forge-game/src/main/java/forge/game/trigger/TriggerScry.java -text
-forge-game/src/main/java/forge/game/trigger/TriggerSetInMotion.java -text
-forge-game/src/main/java/forge/game/trigger/TriggerShuffled.java -text
-forge-game/src/main/java/forge/game/trigger/TriggerSpellAbilityCast.java -text
-forge-game/src/main/java/forge/game/trigger/TriggerTaps.java -text
-forge-game/src/main/java/forge/game/trigger/TriggerTapsForMana.java -text
-forge-game/src/main/java/forge/game/trigger/TriggerTransformed.java -text
-forge-game/src/main/java/forge/game/trigger/TriggerTurnFaceUp.java -text
-forge-game/src/main/java/forge/game/trigger/TriggerType.java -text
-forge-game/src/main/java/forge/game/trigger/TriggerUnequip.java -text
-forge-game/src/main/java/forge/game/trigger/TriggerUntaps.java -text
-forge-game/src/main/java/forge/game/trigger/TriggerWaiting.java -text
-forge-game/src/main/java/forge/game/trigger/WrappedAbility.java -text
-forge-game/src/main/java/forge/game/trigger/ZCTrigger.java -text
-forge-game/src/main/java/forge/game/trigger/package-info.java -text
-forge-game/src/main/java/forge/game/zone/MagicStack.java -text
-forge-game/src/main/java/forge/game/zone/PlayerZone.java -text
-forge-game/src/main/java/forge/game/zone/PlayerZoneBattlefield.java -text
-forge-game/src/main/java/forge/game/zone/Zone.java -text
-forge-game/src/main/java/forge/game/zone/ZoneType.java -text
-forge-game/src/main/java/forge/game/zone/package-info.java -text
forge-game/src/main/java/forge/package-info.java -text
-forge-game/src/main/java/forge/util/maps/EnumMapOfLists.java -text
-forge-game/src/main/java/forge/util/maps/EnumMapToAmount.java -text
-forge-game/src/main/java/forge/util/maps/HashMapOfLists.java -text
-forge-game/src/main/java/forge/util/maps/MapOfLists.java -text
-forge-game/src/main/java/forge/util/maps/MapToAmount.java -text
-forge-game/src/main/java/forge/util/maps/package-info.java -text
forge-gui/.classpath -text
forge-gui/.project -text
forge-gui/.settings/org.eclipse.core.resources.prefs -text
@@ -15204,12 +14706,132 @@ forge-gui/src/main/html/js/jquery/jquery-1.9.1.js -text
forge-gui/src/main/html/js/jquery/jquery-1.9.1.min.js -text
forge-gui/src/main/html/js/observable.js -text
forge-gui/src/main/html/js/socket.js -text
+forge-gui/src/main/java/forge/Command.java svneol=native#text/plain
+forge-gui/src/main/java/forge/Constant.java svneol=native#text/plain
forge-gui/src/main/java/forge/FThreads.java -text
forge-gui/src/main/java/forge/ImageCache.java svneol=native#text/plain
-forge-gui/src/main/java/forge/ImageCacheProvider.java -text
forge-gui/src/main/java/forge/ImageLoader.java -text
-forge-gui/src/main/java/forge/PreferencesProvider.java -text
forge-gui/src/main/java/forge/Singletons.java svneol=native#text/plain
+forge-gui/src/main/java/forge/ai/AiAttackController.java svneol=native#text/plain
+forge-gui/src/main/java/forge/ai/AiBlockController.java svneol=native#text/plain
+forge-gui/src/main/java/forge/ai/AiController.java svneol=native#text/plain
+forge-gui/src/main/java/forge/ai/AiCostDecision.java -text
+forge-gui/src/main/java/forge/ai/AiProfileUtil.java -text
+forge-gui/src/main/java/forge/ai/ComputerUtil.java svneol=native#text/plain
+forge-gui/src/main/java/forge/ai/ComputerUtilCard.java -text
+forge-gui/src/main/java/forge/ai/ComputerUtilCombat.java -text
+forge-gui/src/main/java/forge/ai/ComputerUtilCost.java -text
+forge-gui/src/main/java/forge/ai/ComputerUtilMana.java -text
+forge-gui/src/main/java/forge/ai/SpellAbilityAi.java -text
+forge-gui/src/main/java/forge/ai/ability/AddPhaseAi.java -text
+forge-gui/src/main/java/forge/ai/ability/AddTurnAi.java svneol=native#text/plain
+forge-gui/src/main/java/forge/ai/ability/AlwaysPlayAi.java -text
+forge-gui/src/main/java/forge/ai/ability/AnimateAi.java -text
+forge-gui/src/main/java/forge/ai/ability/AnimateAllAi.java -text
+forge-gui/src/main/java/forge/ai/ability/AttachAi.java -text
+forge-gui/src/main/java/forge/ai/ability/BalanceAi.java -text
+forge-gui/src/main/java/forge/ai/ability/BecomesBlockedAi.java -text
+forge-gui/src/main/java/forge/ai/ability/BondAi.java -text
+forge-gui/src/main/java/forge/ai/ability/CanPlayAsDrawbackAi.java -text
+forge-gui/src/main/java/forge/ai/ability/CannotPlayAi.java -text
+forge-gui/src/main/java/forge/ai/ability/ChangeTargetsAi.java -text
+forge-gui/src/main/java/forge/ai/ability/ChangeZoneAi.java -text
+forge-gui/src/main/java/forge/ai/ability/ChangeZoneAllAi.java -text
+forge-gui/src/main/java/forge/ai/ability/CharmAi.java -text
+forge-gui/src/main/java/forge/ai/ability/ChooseCardAi.java -text
+forge-gui/src/main/java/forge/ai/ability/ChooseCardNameAi.java -text
+forge-gui/src/main/java/forge/ai/ability/ChooseColorAi.java -text
+forge-gui/src/main/java/forge/ai/ability/ChooseGenericEffectAi.java -text
+forge-gui/src/main/java/forge/ai/ability/ChoosePlayerAi.java -text
+forge-gui/src/main/java/forge/ai/ability/ChooseSourceAi.java -text
+forge-gui/src/main/java/forge/ai/ability/ChooseTypeAi.java -text
+forge-gui/src/main/java/forge/ai/ability/ClashAi.java -text
+forge-gui/src/main/java/forge/ai/ability/CloneAi.java -text
+forge-gui/src/main/java/forge/ai/ability/ControlExchangeAi.java -text
+forge-gui/src/main/java/forge/ai/ability/ControlGainAi.java -text
+forge-gui/src/main/java/forge/ai/ability/CopyPermanentAi.java -text
+forge-gui/src/main/java/forge/ai/ability/CounterAi.java -text
+forge-gui/src/main/java/forge/ai/ability/CountersAi.java svneol=native#text/plain
+forge-gui/src/main/java/forge/ai/ability/CountersMoveAi.java -text
+forge-gui/src/main/java/forge/ai/ability/CountersProliferateAi.java -text
+forge-gui/src/main/java/forge/ai/ability/CountersPutAi.java -text
+forge-gui/src/main/java/forge/ai/ability/CountersPutAllAi.java -text
+forge-gui/src/main/java/forge/ai/ability/CountersPutOrRemoveAi.java -text
+forge-gui/src/main/java/forge/ai/ability/CountersRemoveAi.java -text
+forge-gui/src/main/java/forge/ai/ability/DamageAiBase.java -text
+forge-gui/src/main/java/forge/ai/ability/DamageAllAi.java -text
+forge-gui/src/main/java/forge/ai/ability/DamageDealAi.java -text
+forge-gui/src/main/java/forge/ai/ability/DamageEachAi.java -text
+forge-gui/src/main/java/forge/ai/ability/DamagePreventAi.java -text
+forge-gui/src/main/java/forge/ai/ability/DamagePreventAllAi.java -text
+forge-gui/src/main/java/forge/ai/ability/DebuffAi.java -text
+forge-gui/src/main/java/forge/ai/ability/DebuffAllAi.java -text
+forge-gui/src/main/java/forge/ai/ability/DelayedTriggerAi.java -text
+forge-gui/src/main/java/forge/ai/ability/DestroyAi.java -text
+forge-gui/src/main/java/forge/ai/ability/DestroyAllAi.java -text
+forge-gui/src/main/java/forge/ai/ability/DigAi.java -text
+forge-gui/src/main/java/forge/ai/ability/DigUntilAi.java -text
+forge-gui/src/main/java/forge/ai/ability/DiscardAi.java -text
+forge-gui/src/main/java/forge/ai/ability/DrainManaAi.java -text
+forge-gui/src/main/java/forge/ai/ability/DrawAi.java svneol=native#text/plain
+forge-gui/src/main/java/forge/ai/ability/EffectAi.java -text
+forge-gui/src/main/java/forge/ai/ability/EncodeAi.java -text
+forge-gui/src/main/java/forge/ai/ability/EndTurnAi.java -text
+forge-gui/src/main/java/forge/ai/ability/FightAi.java -text
+forge-gui/src/main/java/forge/ai/ability/FlipACoinAi.java -text
+forge-gui/src/main/java/forge/ai/ability/FogAi.java -text
+forge-gui/src/main/java/forge/ai/ability/GameLossAi.java -text
+forge-gui/src/main/java/forge/ai/ability/GameWinAi.java -text
+forge-gui/src/main/java/forge/ai/ability/HauntAi.java -text
+forge-gui/src/main/java/forge/ai/ability/LegendaryRuleAi.java -text
+forge-gui/src/main/java/forge/ai/ability/LifeExchangeAi.java -text
+forge-gui/src/main/java/forge/ai/ability/LifeGainAi.java -text
+forge-gui/src/main/java/forge/ai/ability/LifeLoseAi.java -text
+forge-gui/src/main/java/forge/ai/ability/LifeSetAi.java -text
+forge-gui/src/main/java/forge/ai/ability/ManaEffectAi.java -text
+forge-gui/src/main/java/forge/ai/ability/MillAi.java -text
+forge-gui/src/main/java/forge/ai/ability/MustAttackAi.java -text
+forge-gui/src/main/java/forge/ai/ability/MustBlockAi.java -text
+forge-gui/src/main/java/forge/ai/ability/PeekAndRevealAi.java -text
+forge-gui/src/main/java/forge/ai/ability/PermanentCreatureAi.java -text
+forge-gui/src/main/java/forge/ai/ability/PermanentNoncreatureAi.java -text
+forge-gui/src/main/java/forge/ai/ability/PhasesAi.java -text
+forge-gui/src/main/java/forge/ai/ability/PlayAi.java -text
+forge-gui/src/main/java/forge/ai/ability/PoisonAi.java -text
+forge-gui/src/main/java/forge/ai/ability/PowerExchangeAi.java -text
+forge-gui/src/main/java/forge/ai/ability/ProtectAi.java -text
+forge-gui/src/main/java/forge/ai/ability/ProtectAllAi.java -text
+forge-gui/src/main/java/forge/ai/ability/PumpAi.java -text
+forge-gui/src/main/java/forge/ai/ability/PumpAiBase.java -text
+forge-gui/src/main/java/forge/ai/ability/PumpAllAi.java -text
+forge-gui/src/main/java/forge/ai/ability/RearrangeTopOfLibraryAi.java -text
+forge-gui/src/main/java/forge/ai/ability/RegenerateAi.java svneol=native#text/plain
+forge-gui/src/main/java/forge/ai/ability/RegenerateAllAi.java -text
+forge-gui/src/main/java/forge/ai/ability/RemoveFromCombatAi.java -text
+forge-gui/src/main/java/forge/ai/ability/RepeatAi.java -text
+forge-gui/src/main/java/forge/ai/ability/RepeatEachAi.java -text
+forge-gui/src/main/java/forge/ai/ability/RestartGameAi.java -text
+forge-gui/src/main/java/forge/ai/ability/RevealAi.java -text
+forge-gui/src/main/java/forge/ai/ability/RevealAiBase.java -text
+forge-gui/src/main/java/forge/ai/ability/RevealHandAi.java -text
+forge-gui/src/main/java/forge/ai/ability/RollPlanarDiceAi.java -text
+forge-gui/src/main/java/forge/ai/ability/SacrificeAi.java -text
+forge-gui/src/main/java/forge/ai/ability/SacrificeAllAi.java -text
+forge-gui/src/main/java/forge/ai/ability/ScryAi.java -text
+forge-gui/src/main/java/forge/ai/ability/SetStateAi.java -text
+forge-gui/src/main/java/forge/ai/ability/ShuffleAi.java -text
+forge-gui/src/main/java/forge/ai/ability/StoreSVarAi.java -text
+forge-gui/src/main/java/forge/ai/ability/TapAi.java -text
+forge-gui/src/main/java/forge/ai/ability/TapAiBase.java -text
+forge-gui/src/main/java/forge/ai/ability/TapAllAi.java -text
+forge-gui/src/main/java/forge/ai/ability/TapOrUntapAi.java -text
+forge-gui/src/main/java/forge/ai/ability/TapOrUntapAllAi.java -text
+forge-gui/src/main/java/forge/ai/ability/TokenAi.java -text
+forge-gui/src/main/java/forge/ai/ability/TwoPilesAi.java -text
+forge-gui/src/main/java/forge/ai/ability/UnattachAllAi.java -text
+forge-gui/src/main/java/forge/ai/ability/UntapAi.java -text
+forge-gui/src/main/java/forge/ai/ability/UntapAllAi.java -text
+forge-gui/src/main/java/forge/ai/ability/ZoneExchangeAi.java -text
forge-gui/src/main/java/forge/control/ChatArea.java -text
forge-gui/src/main/java/forge/control/FControl.java -text
forge-gui/src/main/java/forge/control/FControlGameEventHandler.java -text
@@ -15224,6 +14846,374 @@ forge-gui/src/main/java/forge/deck/io/OldDeckParser.java -text
forge-gui/src/main/java/forge/deck/io/package-info.java svneol=native#text/plain
forge-gui/src/main/java/forge/error/ExceptionHandler.java svneol=native#text/plain
forge-gui/src/main/java/forge/error/package-info.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/Game.java -text
+forge-gui/src/main/java/forge/game/GameAction.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/GameActionUtil.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/GameEndReason.java -text
+forge-gui/src/main/java/forge/game/GameEntity.java -text
+forge-gui/src/main/java/forge/game/GameLog.java -text
+forge-gui/src/main/java/forge/game/GameLogEntry.java -text
+forge-gui/src/main/java/forge/game/GameLogEntryType.java -text
+forge-gui/src/main/java/forge/game/GameLogFormatter.java -text
+forge-gui/src/main/java/forge/game/GameObject.java -text
+forge-gui/src/main/java/forge/game/GameOutcome.java -text
+forge-gui/src/main/java/forge/game/GameStage.java -text
+forge-gui/src/main/java/forge/game/GameType.java -text
+forge-gui/src/main/java/forge/game/GlobalRuleChange.java -text
+forge-gui/src/main/java/forge/game/Match.java -text
+forge-gui/src/main/java/forge/game/PlanarDice.java -text
+forge-gui/src/main/java/forge/game/StaticEffect.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/StaticEffects.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/TriggerReplacementBase.java -text
+forge-gui/src/main/java/forge/game/ability/AbilityApiBased.java -text
+forge-gui/src/main/java/forge/game/ability/AbilityFactory.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/ability/AbilityUtils.java -text
+forge-gui/src/main/java/forge/game/ability/ApiType.java -text
+forge-gui/src/main/java/forge/game/ability/SaTargetRoutines.java -text
+forge-gui/src/main/java/forge/game/ability/SpellAbilityEffect.java -text
+forge-gui/src/main/java/forge/game/ability/SpellApiBased.java -text
+forge-gui/src/main/java/forge/game/ability/StaticAbilityApiBased.java -text
+forge-gui/src/main/java/forge/game/ability/effects/AbandonEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/AddPhaseEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/AddTurnEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/AnimateAllEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/AnimateEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/AnimateEffectBase.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/ability/effects/AttachEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/BalanceEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/BecomesBlockedEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/BondEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/ChangeTargetsEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/ChangeZoneAllEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/CharmEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/ChooseCardEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/ChooseCardNameEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/ChooseColorEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/ChooseGenericEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/ChooseNumberEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/ChoosePlayerEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/ChooseSourceEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/ChooseTypeEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/ClashEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/CleanUpEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/CloneEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/ControlExchangeEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/ControlGainEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/ControlPlayerEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/CopyPermanentEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/CopySpellAbilityEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/CounterEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/CountersMoveEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/CountersProliferateEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/CountersPutAllEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/CountersPutEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/CountersPutOrRemoveEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/CountersRemoveAllEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/CountersRemoveEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/DamageAllEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/DamageDealEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/DamageEachEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/DamagePreventAllEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/DamagePreventEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/DebuffAllEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/DebuffEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/DeclareCombatantsEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/DelayedTriggerEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/DestroyAllEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/DestroyEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/DigEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/DigUntilEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/DiscardEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/DrainManaEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/DrawEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/ETBReplacementEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/EffectEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/EncodeEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/EndTurnEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/FightEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/FlipCoinEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/FogEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/GameLossEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/GameWinEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/LifeExchangeEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/LifeGainEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/LifeLoseEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/LifeSetEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/ManaEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/ManaReflectedEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/MillEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/MustAttackEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/MustBlockEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/OwnershipGainEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/PeekAndRevealEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/PermanentCreatureEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/PermanentNoncreatureEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/PhasesEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/PlaneswalkEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/PlayEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/PoisonEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/PowerExchangeEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/ProtectAllEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/ProtectEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/PumpAllEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/PumpEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/RearrangeTopOfLibraryEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/RegenerateAllEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/RegenerateEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/RemoveFromCombatEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/ReorderZoneEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/RepeatEachEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/RepeatEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/RestartGameEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/RevealEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/RevealHandEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/RollPlanarDiceEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/RunSVarAbilityEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/SacrificeAllEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/SacrificeEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/ScryEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/SetInMotionEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/SetStateEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/ShuffleEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/SkipTurnEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/StoreSVarEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/TapAllEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/TapEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/TapOrUntapAllEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/TapOrUntapEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/TokenEffect.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/ability/effects/TwoPilesEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/UnattachAllEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/UntapAllEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/UntapEffect.java -text
+forge-gui/src/main/java/forge/game/ability/effects/ZoneExchangeEffect.java -text
+forge-gui/src/main/java/forge/game/ability/package-info.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/card/Card.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/card/CardCharacteristics.java -text
+forge-gui/src/main/java/forge/game/card/CardColor.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/card/CardDamageHistory.java -text
+forge-gui/src/main/java/forge/game/card/CardFactory.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/card/CardFactoryUtil.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/card/CardKeywords.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/card/CardLists.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/card/CardPowerToughness.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/card/CardPredicates.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/card/CardShields.java -text
+forge-gui/src/main/java/forge/game/card/CardType.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/card/CardUtil.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/card/CounterType.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/card/package-info.java -text
+forge-gui/src/main/java/forge/game/combat/AttackingBand.java -text
+forge-gui/src/main/java/forge/game/combat/Combat.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/combat/CombatLki.java -text
+forge-gui/src/main/java/forge/game/combat/CombatUtil.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/cost/Cost.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/cost/CostAddMana.java -text
+forge-gui/src/main/java/forge/game/cost/CostChooseCreatureType.java -text
+forge-gui/src/main/java/forge/game/cost/CostDamage.java -text
+forge-gui/src/main/java/forge/game/cost/CostDecisionMakerBase.java -text
+forge-gui/src/main/java/forge/game/cost/CostDiscard.java -text
+forge-gui/src/main/java/forge/game/cost/CostDraw.java -text
+forge-gui/src/main/java/forge/game/cost/CostExile.java -text
+forge-gui/src/main/java/forge/game/cost/CostExiledMoveToGrave.java -text
+forge-gui/src/main/java/forge/game/cost/CostFlipCoin.java -text
+forge-gui/src/main/java/forge/game/cost/CostGainControl.java -text
+forge-gui/src/main/java/forge/game/cost/CostGainLife.java -text
+forge-gui/src/main/java/forge/game/cost/CostMill.java -text
+forge-gui/src/main/java/forge/game/cost/CostPart.java -text
+forge-gui/src/main/java/forge/game/cost/CostPartMana.java -text
+forge-gui/src/main/java/forge/game/cost/CostPartWithList.java -text
+forge-gui/src/main/java/forge/game/cost/CostPayLife.java -text
+forge-gui/src/main/java/forge/game/cost/CostPayment.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/cost/CostPutCardToLib.java -text
+forge-gui/src/main/java/forge/game/cost/CostPutCounter.java -text
+forge-gui/src/main/java/forge/game/cost/CostRemoveAnyCounter.java -text
+forge-gui/src/main/java/forge/game/cost/CostRemoveCounter.java -text
+forge-gui/src/main/java/forge/game/cost/CostReturn.java -text
+forge-gui/src/main/java/forge/game/cost/CostReveal.java -text
+forge-gui/src/main/java/forge/game/cost/CostSacrifice.java -text
+forge-gui/src/main/java/forge/game/cost/CostTap.java -text
+forge-gui/src/main/java/forge/game/cost/CostTapType.java -text
+forge-gui/src/main/java/forge/game/cost/CostUnattach.java -text
+forge-gui/src/main/java/forge/game/cost/CostUntap.java -text
+forge-gui/src/main/java/forge/game/cost/CostUntapType.java -text
+forge-gui/src/main/java/forge/game/cost/ICostVisitor.java -text
+forge-gui/src/main/java/forge/game/cost/PaymentDecision.java -text
+forge-gui/src/main/java/forge/game/cost/package-info.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/event/EventValueChangeType.java -text
+forge-gui/src/main/java/forge/game/event/GameEvent.java -text
+forge-gui/src/main/java/forge/game/event/GameEventAnteCardsSelected.java -text
+forge-gui/src/main/java/forge/game/event/GameEventAttackersDeclared.java -text
+forge-gui/src/main/java/forge/game/event/GameEventBlockersDeclared.java -text
+forge-gui/src/main/java/forge/game/event/GameEventCardAttachment.java -text
+forge-gui/src/main/java/forge/game/event/GameEventCardChangeZone.java -text
+forge-gui/src/main/java/forge/game/event/GameEventCardCounters.java -text
+forge-gui/src/main/java/forge/game/event/GameEventCardDamaged.java -text
+forge-gui/src/main/java/forge/game/event/GameEventCardDestroyed.java -text
+forge-gui/src/main/java/forge/game/event/GameEventCardPhased.java -text
+forge-gui/src/main/java/forge/game/event/GameEventCardRegenerated.java -text
+forge-gui/src/main/java/forge/game/event/GameEventCardSacrificed.java -text
+forge-gui/src/main/java/forge/game/event/GameEventCardStatsChanged.java -text
+forge-gui/src/main/java/forge/game/event/GameEventCardTapped.java -text
+forge-gui/src/main/java/forge/game/event/GameEventCombatEnded.java -text
+forge-gui/src/main/java/forge/game/event/GameEventFlipCoin.java -text
+forge-gui/src/main/java/forge/game/event/GameEventGameFinished.java -text
+forge-gui/src/main/java/forge/game/event/GameEventGameOutcome.java -text
+forge-gui/src/main/java/forge/game/event/GameEventGameRestarted.java -text
+forge-gui/src/main/java/forge/game/event/GameEventGameStarted.java -text
+forge-gui/src/main/java/forge/game/event/GameEventLandPlayed.java -text
+forge-gui/src/main/java/forge/game/event/GameEventManaBurn.java -text
+forge-gui/src/main/java/forge/game/event/GameEventManaPool.java -text
+forge-gui/src/main/java/forge/game/event/GameEventMulligan.java -text
+forge-gui/src/main/java/forge/game/event/GameEventPlayerControl.java -text
+forge-gui/src/main/java/forge/game/event/GameEventPlayerDamaged.java -text
+forge-gui/src/main/java/forge/game/event/GameEventPlayerLivesChanged.java -text
+forge-gui/src/main/java/forge/game/event/GameEventPlayerPoisoned.java -text
+forge-gui/src/main/java/forge/game/event/GameEventPlayerPriority.java -text
+forge-gui/src/main/java/forge/game/event/GameEventShuffle.java -text
+forge-gui/src/main/java/forge/game/event/GameEventSpellAbilityCast.java -text
+forge-gui/src/main/java/forge/game/event/GameEventSpellRemovedFromStack.java -text
+forge-gui/src/main/java/forge/game/event/GameEventSpellResolved.java -text
+forge-gui/src/main/java/forge/game/event/GameEventTokenCreated.java -text
+forge-gui/src/main/java/forge/game/event/GameEventTurnBegan.java -text
+forge-gui/src/main/java/forge/game/event/GameEventTurnEnded.java -text
+forge-gui/src/main/java/forge/game/event/GameEventTurnPhase.java -text
+forge-gui/src/main/java/forge/game/event/GameEventZone.java -text
+forge-gui/src/main/java/forge/game/event/IGameEventVisitor.java -text
+forge-gui/src/main/java/forge/game/event/package-info.java -text
+forge-gui/src/main/java/forge/game/mana/Mana.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/mana/ManaCostBeingPaid.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/mana/ManaPool.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/mana/package-info.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/package-info.java -text
+forge-gui/src/main/java/forge/game/phase/EndOfTurn.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/phase/ExtraTurn.java -text
+forge-gui/src/main/java/forge/game/phase/Phase.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/phase/PhaseHandler.java -text
+forge-gui/src/main/java/forge/game/phase/PhaseType.java -text
+forge-gui/src/main/java/forge/game/phase/Untap.java -text
+forge-gui/src/main/java/forge/game/phase/Upkeep.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/phase/package-info.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/player/GameLossReason.java -text
+forge-gui/src/main/java/forge/game/player/LobbyPlayer.java -text
+forge-gui/src/main/java/forge/game/player/LobbyPlayerAi.java -text
+forge-gui/src/main/java/forge/game/player/LobbyPlayerRemote.java -text
+forge-gui/src/main/java/forge/game/player/Player.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/player/PlayerActionConfirmMode.java -text
+forge-gui/src/main/java/forge/game/player/PlayerController.java -text
+forge-gui/src/main/java/forge/game/player/PlayerControllerAi.java -text
+forge-gui/src/main/java/forge/game/player/PlayerOutcome.java -text
+forge-gui/src/main/java/forge/game/player/PlayerStatistics.java -text
+forge-gui/src/main/java/forge/game/player/RegisteredPlayer.java -text
+forge-gui/src/main/java/forge/game/player/package-info.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/replacement/ReplaceAddCounter.java -text
+forge-gui/src/main/java/forge/game/replacement/ReplaceCounter.java -text
+forge-gui/src/main/java/forge/game/replacement/ReplaceDamage.java -text
+forge-gui/src/main/java/forge/game/replacement/ReplaceDestroy.java -text
+forge-gui/src/main/java/forge/game/replacement/ReplaceDiscard.java -text
+forge-gui/src/main/java/forge/game/replacement/ReplaceDraw.java -text
+forge-gui/src/main/java/forge/game/replacement/ReplaceGainLife.java -text
+forge-gui/src/main/java/forge/game/replacement/ReplaceGameLoss.java -text
+forge-gui/src/main/java/forge/game/replacement/ReplaceMoved.java -text
+forge-gui/src/main/java/forge/game/replacement/ReplaceProduceMana.java -text
+forge-gui/src/main/java/forge/game/replacement/ReplaceSetInMotion.java -text
+forge-gui/src/main/java/forge/game/replacement/ReplaceTurnFaceUp.java -text
+forge-gui/src/main/java/forge/game/replacement/ReplaceUntap.java -text
+forge-gui/src/main/java/forge/game/replacement/ReplacementEffect.java -text
+forge-gui/src/main/java/forge/game/replacement/ReplacementHandler.java -text
+forge-gui/src/main/java/forge/game/replacement/ReplacementLayer.java -text
+forge-gui/src/main/java/forge/game/replacement/ReplacementResult.java -text
+forge-gui/src/main/java/forge/game/replacement/ReplacementType.java -text
+forge-gui/src/main/java/forge/game/replacement/package-info.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/spellability/Ability.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/spellability/AbilityActivated.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/spellability/AbilityManaPart.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/spellability/AbilityStatic.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/spellability/AbilitySub.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/spellability/AbilityTriggered.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/spellability/ISpellAbility.java -text
+forge-gui/src/main/java/forge/game/spellability/OptionalCost.java -text
+forge-gui/src/main/java/forge/game/spellability/Spell.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/spellability/SpellAbility.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/spellability/SpellAbilityCondition.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/spellability/SpellAbilityRestriction.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/spellability/SpellAbilityStackInstance.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/spellability/SpellAbilityVariables.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/spellability/SpellPermanent.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/spellability/TargetChoices.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/spellability/TargetRestrictions.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/spellability/package-info.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/staticability/StaticAbility.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/staticability/StaticAbilityCantAttackBlock.java -text
+forge-gui/src/main/java/forge/game/staticability/StaticAbilityCantBeCast.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/staticability/StaticAbilityCantTarget.java -text
+forge-gui/src/main/java/forge/game/staticability/StaticAbilityContinuous.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/staticability/StaticAbilityCostChange.java -text
+forge-gui/src/main/java/forge/game/staticability/StaticAbilityETBTapped.java -text
+forge-gui/src/main/java/forge/game/staticability/StaticAbilityMayLookAt.java -text
+forge-gui/src/main/java/forge/game/staticability/StaticAbilityPreventDamage.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/staticability/package-info.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/trigger/Trigger.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/trigger/TriggerAlways.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/trigger/TriggerAttached.java -text
+forge-gui/src/main/java/forge/game/trigger/TriggerAttackerBlocked.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/trigger/TriggerAttackerUnblocked.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/trigger/TriggerAttackersDeclared.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/trigger/TriggerAttacks.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/trigger/TriggerBecomeMonstrous.java -text
+forge-gui/src/main/java/forge/game/trigger/TriggerBecomesTarget.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/trigger/TriggerBlockersDeclared.java -text
+forge-gui/src/main/java/forge/game/trigger/TriggerBlocks.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/trigger/TriggerChampioned.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/trigger/TriggerChangesController.java -text
+forge-gui/src/main/java/forge/game/trigger/TriggerChangesZone.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/trigger/TriggerClashed.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/trigger/TriggerCombatDamageDoneOnce.java -text
+forge-gui/src/main/java/forge/game/trigger/TriggerCounterAdded.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/trigger/TriggerCounterRemoved.java -text
+forge-gui/src/main/java/forge/game/trigger/TriggerCountered.java -text
+forge-gui/src/main/java/forge/game/trigger/TriggerCycled.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/trigger/TriggerDamageDone.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/trigger/TriggerDestroyed.java -text
+forge-gui/src/main/java/forge/game/trigger/TriggerDevoured.java -text
+forge-gui/src/main/java/forge/game/trigger/TriggerDiscarded.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/trigger/TriggerDrawn.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/trigger/TriggerEvolved.java -text
+forge-gui/src/main/java/forge/game/trigger/TriggerFlippedCoin.java -text
+forge-gui/src/main/java/forge/game/trigger/TriggerHandler.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/trigger/TriggerLandPlayed.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/trigger/TriggerLifeGained.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/trigger/TriggerLifeLost.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/trigger/TriggerLosesGame.java -text
+forge-gui/src/main/java/forge/game/trigger/TriggerNewGame.java -text
+forge-gui/src/main/java/forge/game/trigger/TriggerPayCumulativeUpkeep.java -text
+forge-gui/src/main/java/forge/game/trigger/TriggerPhase.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/trigger/TriggerPlanarDice.java -text
+forge-gui/src/main/java/forge/game/trigger/TriggerPlaneswalkedFrom.java -text
+forge-gui/src/main/java/forge/game/trigger/TriggerPlaneswalkedTo.java -text
+forge-gui/src/main/java/forge/game/trigger/TriggerSacrificed.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/trigger/TriggerScry.java -text
+forge-gui/src/main/java/forge/game/trigger/TriggerSetInMotion.java -text
+forge-gui/src/main/java/forge/game/trigger/TriggerShuffled.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/trigger/TriggerSpellAbilityCast.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/trigger/TriggerTaps.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/trigger/TriggerTapsForMana.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/trigger/TriggerTransformed.java -text
+forge-gui/src/main/java/forge/game/trigger/TriggerTurnFaceUp.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/trigger/TriggerType.java -text
+forge-gui/src/main/java/forge/game/trigger/TriggerUnequip.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/trigger/TriggerUntaps.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/trigger/TriggerWaiting.java -text
+forge-gui/src/main/java/forge/game/trigger/WrappedAbility.java -text
+forge-gui/src/main/java/forge/game/trigger/ZCTrigger.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/trigger/package-info.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/zone/MagicStack.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/zone/PlayerZone.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/zone/PlayerZoneBattlefield.java svneol=native#text/plain
+forge-gui/src/main/java/forge/game/zone/Zone.java -text
+forge-gui/src/main/java/forge/game/zone/ZoneType.java -text
+forge-gui/src/main/java/forge/game/zone/package-info.java svneol=native#text/plain
forge-gui/src/main/java/forge/gauntlet/GauntletData.java -text
forge-gui/src/main/java/forge/gauntlet/GauntletIO.java -text
forge-gui/src/main/java/forge/gui/CardContainer.java svneol=native#text/plain
@@ -15584,7 +15574,6 @@ forge-gui/src/main/java/forge/net/FServer.java -text
forge-gui/src/main/java/forge/net/IClientSocket.java -text
forge-gui/src/main/java/forge/net/IConnectionObserver.java -text
forge-gui/src/main/java/forge/net/Lobby.java -text
-forge-gui/src/main/java/forge/net/LobbyPlayerRemote.java -text
forge-gui/src/main/java/forge/net/NetServer.java -text
forge-gui/src/main/java/forge/net/client/INetClient.java -text
forge-gui/src/main/java/forge/net/client/InvalidFieldInPacketException.java -text
@@ -15679,13 +15668,21 @@ forge-gui/src/main/java/forge/sound/SoundSystem.java -text
forge-gui/src/main/java/forge/util/AwtUtil.java -text
forge-gui/src/main/java/forge/util/Base64Coder.java svneol=native#text/plain
forge-gui/src/main/java/forge/util/Evaluator.java -text
+forge-gui/src/main/java/forge/util/Expressions.java -text
forge-gui/src/main/java/forge/util/HttpUtil.java svneol=native#text/plain
forge-gui/src/main/java/forge/util/IgnoringXStream.java -text
forge-gui/src/main/java/forge/util/LineReader.java -text
forge-gui/src/main/java/forge/util/MultiplexOutputStream.java svneol=native#text/plain
forge-gui/src/main/java/forge/util/NameGenerator.java -text
forge-gui/src/main/java/forge/util/OperatingSystem.java -text
+forge-gui/src/main/java/forge/util/ReflectionUtil.java -text
forge-gui/src/main/java/forge/util/XmlUtil.java -text
+forge-gui/src/main/java/forge/util/maps/EnumMapOfLists.java -text
+forge-gui/src/main/java/forge/util/maps/EnumMapToAmount.java -text
+forge-gui/src/main/java/forge/util/maps/HashMapOfLists.java -text
+forge-gui/src/main/java/forge/util/maps/MapOfLists.java -text
+forge-gui/src/main/java/forge/util/maps/MapToAmount.java -text
+forge-gui/src/main/java/forge/util/maps/package-info.java -text
forge-gui/src/main/java/forge/util/package-info.java -text
forge-gui/src/main/java/forge/view/ButtonUtil.java svneol=native#text/plain
forge-gui/src/main/java/forge/view/CardReaderExperiments.java -text
diff --git a/forge-ai/pom.xml b/forge-ai/pom.xml
index 3f5cc7a4880..088df599180 100644
--- a/forge-ai/pom.xml
+++ b/forge-ai/pom.xml
@@ -18,6 +18,11 @@
forge-core
${project.version}
+
+ forge
+ forge-game
+ ${project.version}
+
diff --git a/forge-game/pom.xml b/forge-game/pom.xml
index 75e444fec85..370cd0d8adc 100644
--- a/forge-game/pom.xml
+++ b/forge-game/pom.xml
@@ -18,15 +18,5 @@
forge-core
${project.version}
-
- com.googlecode
- minlog
- 1.2
-
-
- forge
- forge-ai
- ${project.version}
-
diff --git a/forge-game/src/main/java/forge/ImageCacheBridge.java b/forge-game/src/main/java/forge/ImageCacheBridge.java
deleted file mode 100644
index cfd0e59fbc7..00000000000
--- a/forge-game/src/main/java/forge/ImageCacheBridge.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package forge;
-
-import forge.item.InventoryItem;
-
-public class ImageCacheBridge {
-
- public static Methods instance;
-
- public interface Methods {
- String getImageKey(InventoryItem cp, boolean altState);
- String getTokenKey(String imageName);
- String getMorphImage();
- }
-}
\ No newline at end of file
diff --git a/forge-game/src/main/java/forge/PreferencesBridge.java b/forge-game/src/main/java/forge/PreferencesBridge.java
deleted file mode 100644
index 19cdc877f1e..00000000000
--- a/forge-game/src/main/java/forge/PreferencesBridge.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package forge;
-
-
-public class PreferencesBridge {
- public static PreferencesSet Instance;
-
- public interface PreferencesSet {
-
- public abstract boolean getEnableAiCheats();
-
- public abstract boolean getCloneModeSource();
-
- public abstract String getLogEntryType();
-
- public abstract String getCurrentAiProfile();
-
- public abstract boolean canRandomFoil();
-
- public abstract boolean isManaBurnEnabled();
-
- public abstract boolean areBlocksFree();
-
- }
-
-}
\ No newline at end of file
diff --git a/forge-game/src/main/java/forge/game/package-info.java b/forge-game/src/main/java/forge/game/package-info.java
index c1a6d89a2c5..8921a7477e3 100644
--- a/forge-game/src/main/java/forge/game/package-info.java
+++ b/forge-game/src/main/java/forge/game/package-info.java
@@ -1,3 +1,8 @@
-/** Forge Card Game. */
-package forge.game;
-
+/**
+ *
+ */
+/**
+ * @author Max
+ *
+ */
+package forge.game;
\ No newline at end of file
diff --git a/forge-game/src/main/java/forge/Command.java b/forge-gui/src/main/java/forge/Command.java
similarity index 91%
rename from forge-game/src/main/java/forge/Command.java
rename to forge-gui/src/main/java/forge/Command.java
index 4ae9fa26ec4..b3ab1d42115 100644
--- a/forge-game/src/main/java/forge/Command.java
+++ b/forge-gui/src/main/java/forge/Command.java
@@ -1,39 +1,39 @@
-/*
- * Forge: Play Magic: the Gathering.
- * Copyright (C) 2011 Forge Team
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-package forge;
-
-/**
- *
- * Command interface.
- *
- *
- * @author Forge
- * @version $Id: Command.java 21051 2013-04-17 11:48:06Z Max mtg $
- */
-public interface Command extends java.io.Serializable, Runnable {
- /** Constant Blank. */
- public final Command BLANK = new Command() {
-
- private static final long serialVersionUID = 2689172297036001710L;
-
- @Override
- public void run() {
- }
-
- };
-}
+/*
+ * Forge: Play Magic: the Gathering.
+ * Copyright (C) 2011 Forge Team
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package forge;
+
+/**
+ *
+ * Command interface.
+ *
+ *
+ * @author Forge
+ * @version $Id$
+ */
+public interface Command extends java.io.Serializable, Runnable {
+ /** Constant Blank. */
+ public final Command BLANK = new Command() {
+
+ private static final long serialVersionUID = 2689172297036001710L;
+
+ @Override
+ public void run() {
+ }
+
+ };
+}
diff --git a/forge-game/src/main/java/forge/Constant.java b/forge-gui/src/main/java/forge/Constant.java
similarity index 93%
rename from forge-game/src/main/java/forge/Constant.java
rename to forge-gui/src/main/java/forge/Constant.java
index d849cc2e66b..a2225037c7b 100644
--- a/forge-game/src/main/java/forge/Constant.java
+++ b/forge-gui/src/main/java/forge/Constant.java
@@ -1,71 +1,71 @@
-/*
- * Forge: Play Magic: the Gathering.
- * Copyright (C) 2011 Forge Team
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-package forge;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- *
- * Constant interface.
- *
- *
- * @author Forge
- * @version $Id: Constant.java 23713 2013-11-20 08:34:37Z Max mtg $
- */
-public final class Constant {
- // used to pass information between the GUI screens
- /**
- * The Class Runtime.
- */
- public static class Preferences {
- /** The Constant DevMode. */
- // one for normal mode, one for quest mode
- public static boolean DEV_MODE;
- /** The Constant UpldDrft. */
- public static boolean UPLOAD_DRAFT;
-
- }
-
- public static class Runtime {
- /** The Constant NetConn. */
- public static volatile boolean NET_CONN = false;
-
- /** The Constant width. */
- public static final int WIDTH = 300;
-
- /** The Constant height. */
- public static final int HEIGHT = 0;
- }
-
-
- /**
- * The Interface Keywords.
- */
- public static class Keywords {
-
- /** The loaded. */
- public static final boolean[] LOADED = { false };
-
- /** The Non stacking list. */
- public static final List NON_STACKING_LIST = new ArrayList();
- }
-
-} // Constant
-
-
+/*
+ * Forge: Play Magic: the Gathering.
+ * Copyright (C) 2011 Forge Team
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package forge;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ *
+ * Constant interface.
+ *
+ *
+ * @author Forge
+ * @version $Id$
+ */
+public final class Constant {
+ // used to pass information between the GUI screens
+ /**
+ * The Class Runtime.
+ */
+ public static class Preferences {
+ /** The Constant DevMode. */
+ // one for normal mode, one for quest mode
+ public static boolean DEV_MODE;
+ /** The Constant UpldDrft. */
+ public static boolean UPLOAD_DRAFT;
+
+ }
+
+ public static class Runtime {
+ /** The Constant NetConn. */
+ public static volatile boolean NET_CONN = false;
+
+ /** The Constant width. */
+ public static final int WIDTH = 300;
+
+ /** The Constant height. */
+ public static final int HEIGHT = 0;
+ }
+
+
+ /**
+ * The Interface Keywords.
+ */
+ public static class Keywords {
+
+ /** The loaded. */
+ public static final boolean[] LOADED = { false };
+
+ /** The Non stacking list. */
+ public static final List NON_STACKING_LIST = new ArrayList();
+ }
+
+} // Constant
+
+
diff --git a/forge-gui/src/main/java/forge/ImageCache.java b/forge-gui/src/main/java/forge/ImageCache.java
index 039c83af079..1c2d4ccc046 100644
--- a/forge-gui/src/main/java/forge/ImageCache.java
+++ b/forge-gui/src/main/java/forge/ImageCache.java
@@ -111,7 +111,7 @@ public class ImageCache {
}
return scaleImage(key, width, height, true);
}
-
+
/**
* retrieve an image from the cache. returns null if the image is not found in the cache
* and cannot be loaded from disk. pass -1 for width and/or height to avoid resizing in that dimension.
@@ -250,12 +250,6 @@ public class ImageCache {
return null;
}
- public static String getTokenImageKey(String tokenName) {
- return TOKEN_PREFIX + tokenName;
- }
-
-
-
private static String getImageLocator(PaperCard cp, boolean backFace, boolean includeSet, boolean isDownloadUrl) {
final String nameToUse = getNameToUse(cp, backFace);
if ( null == nameToUse )
diff --git a/forge-gui/src/main/java/forge/ImageCacheProvider.java b/forge-gui/src/main/java/forge/ImageCacheProvider.java
deleted file mode 100644
index 081a0f9a69b..00000000000
--- a/forge-gui/src/main/java/forge/ImageCacheProvider.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package forge;
-
-import forge.item.InventoryItem;
-import forge.properties.NewConstants;
-
-public class ImageCacheProvider implements ImageCacheBridge.Methods {
-
- @Override
- public String getImageKey(InventoryItem cp, boolean altState) {
- return ImageCache.getImageKey(cp, altState);
- }
-
- @Override
- public String getTokenKey(String imageName) {
- return ImageCache.getTokenImageKey(imageName);
- }
-
- @Override
- public String getMorphImage() {
- return NewConstants.CACHE_MORPH_IMAGE_FILE;
- }
-}
\ No newline at end of file
diff --git a/forge-gui/src/main/java/forge/PreferencesProvider.java b/forge-gui/src/main/java/forge/PreferencesProvider.java
deleted file mode 100644
index 4c23292b928..00000000000
--- a/forge-gui/src/main/java/forge/PreferencesProvider.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package forge;
-
-import forge.PreferencesBridge.PreferencesSet;
-import forge.properties.ForgePreferences.FPref;
-
-public class PreferencesProvider implements PreferencesSet {
- @Override
- public boolean getEnableAiCheats() {
- return Singletons.getModel().getPreferences().getPrefBoolean(FPref.UI_ENABLE_AI_CHEATS);
- }
-
-
- @Override
- public boolean getCloneModeSource() {
- return Singletons.getModel().getPreferences().getPrefBoolean(FPref.UI_CLONE_MODE_SOURCE);
- }
-
- @Override
- public String getLogEntryType() {
- return Singletons.getModel().getPreferences().getPref(FPref.DEV_LOG_ENTRY_TYPE);
- }
-
-
- @Override
- public String getCurrentAiProfile() {
- return Singletons.getModel().getPreferences().getPref(FPref.UI_CURRENT_AI_PROFILE);
- }
-
- @Override
- public boolean canRandomFoil() {
- return Singletons.getModel().getPreferences().getPrefBoolean(FPref.UI_RANDOM_FOIL);
- }
-
-
- @Override
- public boolean isManaBurnEnabled() {
- // TODO Auto-generated method stub
- return Singletons.getModel().getPreferences().getPrefBoolean(FPref.UI_MANABURN);
- }
-
- @Override
- public boolean areBlocksFree() {
- return Singletons.getModel().getPreferences().getPrefBoolean(FPref.MATCHPREF_PROMPT_FREE_BLOCKS);
- }
-}
\ No newline at end of file
diff --git a/forge-game/src/main/java/forge/ai/AiAttackController.java b/forge-gui/src/main/java/forge/ai/AiAttackController.java
similarity index 97%
rename from forge-game/src/main/java/forge/ai/AiAttackController.java
rename to forge-gui/src/main/java/forge/ai/AiAttackController.java
index b2b8b35c17f..34fe742c66e 100644
--- a/forge-game/src/main/java/forge/ai/AiAttackController.java
+++ b/forge-gui/src/main/java/forge/ai/AiAttackController.java
@@ -1,1021 +1,1021 @@
-/*
- * Forge: Play Magic: the Gathering.
- * Copyright (C) 2011 Forge Team
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-package forge.ai;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Random;
-
-import com.google.common.base.Predicate;
-import com.google.common.collect.Lists;
-
-import forge.game.GameEntity;
-import forge.game.card.Card;
-import forge.game.card.CardLists;
-import forge.game.card.CounterType;
-import forge.game.combat.Combat;
-import forge.game.combat.CombatUtil;
-import forge.game.player.Player;
-import forge.game.trigger.Trigger;
-import forge.game.trigger.TriggerType;
-import forge.game.zone.ZoneType;
-
-
-//doesHumanAttackAndWin() uses the global variable AllZone.getComputerPlayer()
-/**
- *
- * ComputerUtil_Attack2 class.
- *
- *
- * @author Forge
- * @version $Id: AiAttackController.java 24355 2014-01-19 01:33:24Z swordshine $
- */
-public class AiAttackController {
-
- // possible attackers and blockers
- private final List attackers;
- private final List blockers;
-
- private final static Random random = new Random();
- private final static int randomInt = random.nextInt();
-
- private List oppList; // holds human player creatures
- private List myList; // holds computer creatures
-
- private final Player ai;
-
- private int aiAggression = 0; // added by Masher, how aggressive the ai is
- // attack will be depending on circumstances
-
-
- /**
- *
- * Constructor for ComputerUtil_Attack2.
- *
- *
- * @param possibleAttackers
- * a {@link forge.CardList} object.
- * @param possibleBlockers
- * a {@link forge.CardList} object.
- */
- public AiAttackController(final Player ai) {
- this.ai = ai;
- Player opponent = ai.getOpponent();
-
- this.oppList = Lists.newArrayList();
- this.oppList.addAll(opponent.getCreaturesInPlay());
- this.myList = ai.getCreaturesInPlay();
-
-
- this.attackers = new ArrayList();
- for (Card c : myList) {
- if (CombatUtil.canAttack(c, opponent)) {
- attackers.add(c);
- }
- }
- this.blockers = this.getPossibleBlockers(oppList, this.attackers);
- } // constructor
-
- /**
- *
- * sortAttackers.
- *
- *
- * @param in
- * a {@link forge.CardList} object.
- * @return a {@link forge.CardList} object.
- */
- public final List sortAttackers(final List in) {
- final List list = new ArrayList();
-
- // Cards with triggers should come first (for Battle Cry)
- for (final Card attacker : in) {
- for (final Trigger trigger : attacker.getTriggers()) {
- if (trigger.getMode() == TriggerType.Attacks) {
- list.add(attacker);
- break;
- }
- }
- }
-
- for (final Card attacker : in) {
- if (!list.contains(attacker)) {
- list.add(attacker);
- }
- }
-
- return list;
- } // sortAttackers()
-
- // Is there any reward for attacking? (for 0/1 creatures there is not)
- /**
- *
- * isEffectiveAttacker.
- *
- *
- * @param attacker
- * a {@link forge.game.card.Card} object.
- * @param combat
- * a {@link forge.game.combat.Combat} object.
- * @return a boolean.
- */
- public final boolean isEffectiveAttacker(final Player ai, final Card attacker, final Combat combat) {
-
- // if the attacker will die when attacking don't attack
- if ((attacker.getNetDefense() + ComputerUtilCombat.predictToughnessBonusOfAttacker(attacker, null, combat, true)) <= 0) {
- return false;
- }
-
- final Player opp = ai.getOpponent();
- if (ComputerUtilCombat.damageIfUnblocked(attacker, opp, combat) > 0) {
- return true;
- }
- if (ComputerUtilCombat.poisonIfUnblocked(attacker, opp) > 0) {
- return true;
- }
- if (this.attackers.size() == 1 && attacker.hasKeyword("Exalted") && ComputerUtilCombat.predictDamageTo(opp, 1, attacker, true) > 0) {
- return true;
- }
-
- final List controlledByCompy = ai.getCardsIn(ZoneType.STATIC_ABILITIES_SOURCE_ZONES);
- for (final Card c : controlledByCompy) {
- for (final Trigger trigger : c.getTriggers()) {
- if (ComputerUtilCombat.combatTriggerWillTrigger(attacker, null, trigger, combat)) {
- return true;
- }
- }
- }
-
- return false;
- }
-
- /**
- *
- * getPossibleBlockers.
- *
- *
- * @param blockers
- * a {@link forge.CardList} object.
- * @param attackers
- * a {@link forge.CardList} object.
- * @return a {@link forge.CardList} object.
- */
- public final List getPossibleBlockers(final List blockers, final List attackers) {
- List possibleBlockers = new ArrayList(blockers);
- possibleBlockers = CardLists.filter(possibleBlockers, new Predicate() {
- @Override
- public boolean apply(final Card c) {
- return canBlockAnAttacker(c, attackers);
- }
- });
- return possibleBlockers;
- } // getPossibleBlockers()
-
- /**
- *
- * canBlockAnAttacker.
- *
- *
- * @param c
- * a {@link forge.game.card.Card} object.
- * @param attackers
- * a {@link forge.CardList} object.
- * @return a boolean.
- */
- public final boolean canBlockAnAttacker(final Card c, final List attackers) {
- final List attackerList = new ArrayList(attackers);
- if (!c.isCreature()) {
- return false;
- }
- for (final Card attacker : attackerList) {
- if (CombatUtil.canBlock(attacker, c)) {
- return true;
- }
- }
- return false;
- } // getPossibleBlockers()
-
- // this checks to make sure that the computer player
- // doesn't lose when the human player attacks
- // this method is used by getAttackers()
- /**
- *
- * notNeededAsBlockers.
- *
- *
- * @param attackers
- * a {@link forge.CardList} object.
- * @param combat
- * a {@link forge.game.combat.Combat} object.
- * @return a {@link forge.CardList} object.
- */
- public final List notNeededAsBlockers(final Player ai, final List attackers) {
- final List notNeededAsBlockers = new ArrayList(attackers);
- int fixedBlockers = 0;
- final List vigilantes = new ArrayList();
- //check for time walks
- if (ai.getGame().getPhaseHandler().getNextTurn().equals(ai)) {
- return attackers;
- }
- for (final Card c : this.myList) {
- if (c.getName().equals("Masako the Humorless")) {
- // "Tapped creatures you control can block as though they were untapped."
- return attackers;
- }
- if (!attackers.contains(c)) { // this creature can't attack anyway
- if (canBlockAnAttacker(c, this.oppList)) {
- fixedBlockers++;
- }
- continue;
- }
- if (c.hasKeyword("Vigilance")) {
- vigilantes.add(c);
- notNeededAsBlockers.remove(c); // they will be re-added later
- if (canBlockAnAttacker(c, this.oppList)) {
- fixedBlockers++;
- }
- }
- }
- CardLists.sortByPowerAsc(attackers);
- int blockersNeeded = this.oppList.size();
-
- // don't hold back creatures that can't block any of the human creatures
- final List list = this.getPossibleBlockers(attackers, this.oppList);
-
- //Calculate the amount of creatures necessary
- for (int i = 0; i < list.size(); i++) {
- if (!this.doesHumanAttackAndWin(ai, i)) {
- blockersNeeded = i;
- break;
- }
- }
- int blockersStillNeeded = blockersNeeded - fixedBlockers;
- blockersStillNeeded = Math.min(blockersNeeded, list.size());
- for (int i = 0; i < blockersStillNeeded; i++) {
- notNeededAsBlockers.remove(list.get(i));
- }
-
- // re-add creatures with vigilance
- notNeededAsBlockers.addAll(vigilantes);
-
- if (blockersNeeded > 1) {
- return notNeededAsBlockers;
- }
-
- final Player opp = ai.getOpponent();
-
- // Increase the total number of blockers needed by 1 if Finest Hour in
- // play
- // (human will get an extra first attack with a creature that untaps)
- // In addition, if the computer guesses it needs no blockers, make sure
- // that
- // it won't be surprised by Exalted
- final int humanExaltedBonus = this.countExaltedBonus(opp);
-
- if (humanExaltedBonus > 0) {
- final int nFinestHours = opp.getCardsIn(ZoneType.Battlefield, "Finest Hour").size();
-
- if (((blockersNeeded == 0) || (nFinestHours > 0)) && (this.oppList.size() > 0)) {
- //
- // total attack = biggest creature + exalted, *2 if Rafiq is in
- // play
- int humanBaseAttack = this.getAttack(this.oppList.get(0)) + humanExaltedBonus;
- if (nFinestHours > 0) {
- // For Finest Hour, one creature could attack and get the
- // bonus TWICE
- humanBaseAttack = humanBaseAttack + humanExaltedBonus;
- }
- final int totalExaltedAttack = opp.isCardInPlay("Rafiq of the Many") ? 2 * humanBaseAttack
- : humanBaseAttack;
- if (ai.getLife() - 3 <= totalExaltedAttack) {
- // We will lose if there is an Exalted attack -- keep one
- // blocker
- if ((blockersNeeded == 0) && (notNeededAsBlockers.size() > 0)) {
- notNeededAsBlockers.remove(0);
- }
-
- // Finest Hour allows a second Exalted attack: keep a
- // blocker for that too
- if ((nFinestHours > 0) && (notNeededAsBlockers.size() > 0)) {
- notNeededAsBlockers.remove(0);
- }
- }
- }
- }
- return notNeededAsBlockers;
- }
-
- // this uses a global variable, which isn't perfect
- /**
- *
- * doesHumanAttackAndWin.
- *
- *
- * @param nBlockingCreatures
- * a int.
- * @return a boolean.
- */
- public final boolean doesHumanAttackAndWin(final Player ai, final int nBlockingCreatures) {
- int totalAttack = 0;
- int totalPoison = 0;
- int blockersLeft = nBlockingCreatures;
-
- if (ai.cantLose()) {
- return false;
- }
-
- for (Card attacker : oppList) {
- if (!CombatUtil.canAttackNextTurn(attacker)) {
- continue;
- }
- if (blockersLeft > 0 && CombatUtil.canBeBlocked(attacker, ai)) {
- blockersLeft--;
- continue;
- }
- totalAttack += ComputerUtilCombat.damageIfUnblocked(attacker, ai, null);
- totalPoison += ComputerUtilCombat.poisonIfUnblocked(attacker, ai);
- }
-
- if (totalAttack > 0 && ai.getLife() <= totalAttack
- && !ai.cantLoseForZeroOrLessLife()) {
- return true;
- }
- return ai.getPoisonCounters() + totalPoison > 9;
- }
-
- /**
- *
- * doAssault.
- *
- *
- * @return a boolean.
- */
- private boolean doAssault(final Player ai) {
- // Beastmaster Ascension
- if (ai.isCardInPlay("Beastmaster Ascension")
- && (this.attackers.size() > 1)) {
- final List beastions = ai.getCardsIn(ZoneType.Battlefield, "Beastmaster Ascension");
- int minCreatures = 7;
- for (final Card beastion : beastions) {
- final int counters = beastion.getCounters(CounterType.QUEST);
- minCreatures = Math.min(minCreatures, 7 - counters);
- }
- if (this.attackers.size() >= minCreatures) {
- return true;
- }
- }
-
- CardLists.sortByPowerDesc(this.attackers);
-
- final List unblockedAttackers = new ArrayList();
- final List remainingAttackers = new ArrayList(this.attackers);
- final List remainingBlockers = new ArrayList(this.blockers);
- final Player opp = ai.getOpponent();
-
-
- for (Card attacker : attackers) {
- if (!CombatUtil.canBeBlocked(attacker, this.blockers, null)
- || attacker.hasKeyword("You may have CARDNAME assign its combat damage as though it weren't blocked.")) {
- unblockedAttackers.add(attacker);
- }
- }
-
- for (Card blocker : this.blockers) {
- if (blocker.hasKeyword("CARDNAME can block any number of creatures.")
- || blocker.hasKeyword("CARDNAME can block an additional ninety-nine creatures.")) {
- for (Card attacker : this.attackers) {
- if (CombatUtil.canBlock(attacker, blocker)) {
- remainingAttackers.remove(attacker);
- }
- }
- remainingBlockers.remove(blocker);
- }
- }
-
- // presumes the Human will block
- for (Card blocker : remainingBlockers) {
- if (remainingAttackers.isEmpty()) {
- break;
- }
- if (blocker.hasKeyword("CARDNAME can block an additional creature.")) {
- remainingAttackers.remove(0);
- if (remainingAttackers.isEmpty()) {
- break;
- }
- }
- remainingAttackers.remove(0);
- }
- unblockedAttackers.addAll(remainingAttackers);
-
- if (ComputerUtilCombat.sumDamageIfUnblocked(remainingAttackers, opp) + ComputerUtil.possibleNonCombatDamage(ai) >= opp.getLife()
- && !((opp.cantLoseForZeroOrLessLife() || ai.cantWin()) && (opp.getLife() < 1))) {
- return true;
- }
-
- if (ComputerUtilCombat.sumPoisonIfUnblocked(remainingAttackers, opp) >= (10 - opp.getPoisonCounters())) {
- return true;
- }
-
- return false;
- } // doAssault()
-
- /**
- *
- * chooseDefender.
- *
- *
- * @param c
- * a {@link forge.game.combat.Combat} object.
- * @param bAssault
- * a boolean.
- */
- private final GameEntity chooseDefender(final Combat c, final boolean bAssault) {
- final List defs = c.getDefenders();
- if (defs.size() == 1) {
- return defs.get(0);
- }
-
- final GameEntity entity = ai.getMustAttackEntity();
- if (null != entity) {
- int n = defs.indexOf(entity);
- if (-1 == n) {
- System.out.println("getMustAttackEntity() returned something not in defenders.");
- return defs.get(0);
- } else {
- return entity;
- }
- } else {
- // 1. assault the opponent if you can kill him
- if (bAssault) {
- return getDefendingPlayers(c).get(0);
- }
- // 2. attack planeswalkers
- List pwDefending = c.getDefendingPlaneswalkers();
- if (!pwDefending.isEmpty()) {
- return pwDefending.get(0);
- } else {
- return defs.get(0);
- }
- }
- }
-
- final boolean LOG_AI_ATTACKS = false;
-
-
- public final List getDefendingPlayers(Combat combat) {
- final List defending = new ArrayList();
- for (final GameEntity o : combat.getDefenders()) {
- if (o instanceof Player) {
- defending.add((Player) o);
- }
- }
- return defending;
- }
-
-
- /**
- *
- * Getter for the field attackers.
- *
- *
- * @return a {@link forge.game.combat.Combat} object.
- */
- public final void declareAttackers(final Combat combat) {
- // if this method is called multiple times during a turn,
- // it will always return the same value
- // randomInt is used so that the computer doesn't always
- // do the same thing on turn 3 if he had the same creatures in play
- // I know this is a little confusing
-
- random.setSeed(ai.getGame().getPhaseHandler().getTurn() + AiAttackController.randomInt);
-
- if (this.attackers.isEmpty()) {
- return;
- }
-
- final boolean bAssault = this.doAssault(ai);
- // Determine who will be attacked
- GameEntity defender = this.chooseDefender(combat, bAssault);
- List attackersLeft = new ArrayList(this.attackers);
- // Attackers that don't really have a choice
- for (final Card attacker : this.attackers) {
- if (!CombatUtil.canAttack(attacker, defender, combat)) {
- continue;
- }
- boolean mustAttack = false;
- for (String s : attacker.getKeyword()) {
- if (s.equals("CARDNAME attacks each turn if able.")
- || s.startsWith("CARDNAME attacks specific player each combat if able")) {
- mustAttack = true;
- break;
- }
- if ((s.equals("At the beginning of the end step, destroy CARDNAME.")
- || s.equals("At the beginning of the end step, exile CARDNAME.")
- || s.equals("At the beginning of the end step, sacrifice CARDNAME."))
- && isEffectiveAttacker(ai, attacker, combat)) {
- mustAttack = true;
- break;
- }
- }
- if (mustAttack || attacker.getController().getMustAttackEntity() != null
- || attacker.getSVar("MustAttack").equals("True")) {
- combat.addAttacker(attacker, defender);
- attackersLeft.remove(attacker);
- }
- }
- if (attackersLeft.isEmpty()) {
- return;
- }
- if (bAssault) {
- if ( LOG_AI_ATTACKS )
- System.out.println("Assault");
- CardLists.sortByPowerDesc(attackersLeft);
- for (Card attacker : attackersLeft) {
- if (CombatUtil.canAttack(attacker, defender, combat) && this.isEffectiveAttacker(ai, attacker, combat)) {
- combat.addAttacker(attacker, defender);
- }
- }
- return;
- }
-
- // Exalted
- if (combat.getAttackers().isEmpty()) {
- boolean exalted = false;
- int exaltedCount = 0;
- for (Card c : ai.getCardsIn(ZoneType.Battlefield)) {
- if (c.getName().equals("Rafiq of the Many") || c.getName().equals("Battlegrace Angel")) {
- exalted = true;
- break;
- }
- if (c.getName().equals("Finest Hour") && ai.getGame().getPhaseHandler().isFirstCombat()) {
- exalted = true;
- break;
- }
- if (c.hasKeyword("Exalted")) {
- exaltedCount++;
- if (exaltedCount > 2) {
- exalted = true;
- break;
- }
- }
- }
- if (exalted) {
- CardLists.sortByPowerDesc(this.attackers);
- if ( LOG_AI_ATTACKS )
- System.out.println("Exalted");
- this.aiAggression = 6;
- for (Card attacker : this.attackers) {
- if (CombatUtil.canAttack(attacker, defender, combat) && this.shouldAttack(ai, attacker, this.blockers, combat)) {
- combat.addAttacker(attacker, defender);
- return;
- }
- }
- }
- }
-
- // *******************
- // Evaluate the creature forces
- // *******************
-
- int computerForces = 0;
- int humanForces = 0;
- int humanForcesForAttritionalAttack = 0;
-
- // examine the potential forces
- final List nextTurnAttackers = new ArrayList();
- int candidateCounterAttackDamage = 0;
-
- for (final Card pCard : this.oppList) {
- // if the creature can attack next turn add it to counter attackers
- // list
- if (CombatUtil.canAttackNextTurn(pCard)) {
- nextTurnAttackers.add(pCard);
- if (pCard.getNetCombatDamage() > 0) {
- candidateCounterAttackDamage += pCard.getNetCombatDamage();
- humanForces += 1; // player forces they might use to attack
- }
- }
- // increment player forces that are relevant to an attritional
- // attack - includes walls
- if (CombatUtil.canBlock(pCard, true)) {
- humanForcesForAttritionalAttack += 1;
- }
- }
-
- // find the potential counter attacking damage compared to AI life total
- double aiLifeToPlayerDamageRatio = 1000000;
- if (candidateCounterAttackDamage > 0) {
- aiLifeToPlayerDamageRatio = (double) ai.getLife() / candidateCounterAttackDamage;
- }
-
- final Player opp = ai.getOpponent();
- // get the potential damage and strength of the AI forces
- final List candidateAttackers = new ArrayList();
- int candidateUnblockedDamage = 0;
- for (final Card pCard : this.myList) {
- // if the creature can attack then it's a potential attacker this
- // turn, assume summoning sickness creatures will be able to
- if (CombatUtil.canAttackNextTurn(pCard)) {
- candidateAttackers.add(pCard);
- if (pCard.getNetCombatDamage() > 0) {
- candidateUnblockedDamage += ComputerUtilCombat.damageIfUnblocked(pCard, opp, null);
- computerForces += 1;
- }
- }
- }
-
- // find the potential damage ratio the AI can cause
- double humanLifeToDamageRatio = 1000000;
- if (candidateUnblockedDamage > 0) {
- humanLifeToDamageRatio = (double) (opp.getLife() - ComputerUtil.possibleNonCombatDamage(ai)) / candidateUnblockedDamage;
- }
-
- // determine if the ai outnumbers the player
- final int outNumber = computerForces - humanForces;
-
- for (Card blocker : this.blockers) {
- if (blocker.hasKeyword("CARDNAME can block any number of creatures.")) {
- aiLifeToPlayerDamageRatio--;
- }
- }
-
- // compare the ratios, higher = better for ai
- final double ratioDiff = aiLifeToPlayerDamageRatio - humanLifeToDamageRatio;
-
- // *********************
- // if outnumber and superior ratio work out whether attritional all out
- // attacking will work
- // attritional attack will expect some creatures to die but to achieve
- // victory by sheer weight
- // of numbers attacking turn after turn. It's not calculate very
- // carefully, the accuracy
- // can probably be improved
- // *********************
- boolean doAttritionalAttack = false;
- // get list of attackers ordered from low power to high
- CardLists.sortByPowerAsc(this.attackers);
- // get player life total
- int humanLife = opp.getLife();
- // get the list of attackers up to the first blocked one
- final List attritionalAttackers = new ArrayList();
- for (int x = 0; x < (this.attackers.size() - humanForces); x++) {
- attritionalAttackers.add(this.attackers.get(x));
- }
- // until the attackers are used up or the player would run out of life
- int attackRounds = 1;
- while (attritionalAttackers.size() > 0 && humanLife > 0 && attackRounds < 99) {
- // sum attacker damage
- int damageThisRound = 0;
- for (int y = 0; y < attritionalAttackers.size(); y++) {
- damageThisRound += attritionalAttackers.get(y).getNetCombatDamage();
- }
- // remove from player life
- humanLife -= damageThisRound;
- // shorten attacker list by the length of the blockers - assuming
- // all blocked are killed for convenience
- for (int z = 0; z < humanForcesForAttritionalAttack; z++) {
- if (attritionalAttackers.size() > 0) {
- attritionalAttackers.remove(attritionalAttackers.size() - 1);
- }
- }
- attackRounds += 1;
- if (humanLife <= 0) {
- doAttritionalAttack = true;
- }
- }
- // System.out.println(doAttritionalAttack + " = do attritional attack");
- // *********************
- // end attritional attack calculation
- // *********************
-
- // *********************
- // see how long until unblockable attackers will be fatal
- // *********************
- double unblockableDamage = 0;
- double nextUnblockableDamage = 0;
- double turnsUntilDeathByUnblockable = 0;
- boolean doUnblockableAttack = false;
- for (final Card attacker : this.attackers) {
- boolean isUnblockableCreature = true;
- // check blockers individually, as the bulk canBeBlocked doesn't
- // check all circumstances
- for (final Card blocker : this.blockers) {
- if (CombatUtil.canBlock(attacker, blocker)) {
- isUnblockableCreature = false;
- break;
- }
- }
- if (isUnblockableCreature) {
- unblockableDamage += ComputerUtilCombat.damageIfUnblocked(attacker, opp, combat);
- }
- }
- for (final Card attacker : nextTurnAttackers) {
- boolean isUnblockableCreature = true;
- // check blockers individually, as the bulk canBeBlocked doesn't
- // check all circumstances
- for (final Card blocker : this.myList) {
- if (CombatUtil.canBlock(attacker, blocker, true)) {
- isUnblockableCreature = false;
- break;
- }
- }
- if (isUnblockableCreature) {
- nextUnblockableDamage += ComputerUtilCombat.damageIfUnblocked(attacker, opp, null);
- }
- }
- if (unblockableDamage > 0 && !opp.cantLoseForZeroOrLessLife()
- && opp.canLoseLife()) {
- turnsUntilDeathByUnblockable = 1 + (opp.getLife() - unblockableDamage) / nextUnblockableDamage;
- }
- if (opp.canLoseLife()) {
- doUnblockableAttack = true;
- }
- // *****************
- // end see how long until unblockable attackers will be fatal
- // *****************
-
- // decide on attack aggression based on a comparison of forces, life
- // totals and other considerations
- // some bad "magic numbers" here, TODO replace with nice descriptive
- // variable names
- if (ratioDiff > 0 && doAttritionalAttack) {
- this.aiAggression = 5; // attack at all costs
- } else if (ratioDiff >= 1 && (humanLifeToDamageRatio < 2 || outNumber > 0)) {
- this.aiAggression = 4; // attack expecting to trade or damage player.
- } else if (ratioDiff >= 0) {
- this.aiAggression = 3; // attack expecting to make good trades or damage player.
- } else if (ratioDiff + outNumber >= -1 || aiLifeToPlayerDamageRatio > 1
- || ratioDiff * -1 < turnsUntilDeathByUnblockable) {
- // at 0 ratio expect to potentially gain an advantage by attacking first
- // if the ai has a slight advantage
- // or the ai has a significant advantage numerically but only a slight disadvantage damage/life
- this.aiAggression = 2; // attack expecting to destroy creatures/be unblockable
- } else if (doUnblockableAttack) {
- this.aiAggression = 1;
- // look for unblockable creatures that might be
- // able to attack for a bit of fatal damage even if the player is significantly better
- } else {
- this.aiAggression = 0;
- } // stay at home to block
-
- if ( LOG_AI_ATTACKS )
- System.out.println(String.valueOf(this.aiAggression) + " = ai aggression");
-
- // ****************
- // Evaluation the end
- // ****************
-
- if ( LOG_AI_ATTACKS )
- System.out.println("Normal attack");
-
- attackersLeft = this.notNeededAsBlockers(ai, attackersLeft);
- attackersLeft = this.sortAttackers(attackersLeft);
-
- if ( LOG_AI_ATTACKS )
- System.out.println("attackersLeft = " + attackersLeft);
-
- for (int i = 0; i < attackersLeft.size(); i++) {
- final Card attacker = attackersLeft.get(i);
- if (this.aiAggression < 5 && !attacker.hasFirstStrike() && !attacker.hasDoubleStrike()
- && ComputerUtilCombat.getTotalFirstStrikeBlockPower(attacker, ai.getOpponent())
- >= ComputerUtilCombat.getDamageToKill(attacker)) {
- continue;
- }
-
- if (this.shouldAttack(ai, attacker, this.blockers, combat) && CombatUtil.canAttack(attacker, defender, combat)) {
- combat.addAttacker(attacker, defender);
- // check if attackers are enough to finish the attacked planeswalker
- if (defender instanceof Card) {
- Card pw = (Card) defender;
- final int blockNum = this.blockers.size();
- int attackNum = 0;
- int damage = 0;
- List attacking = combat.getAttackersOf(defender);
- CardLists.sortByPowerAsc(attacking);
- for (Card atta : attacking) {
- if (attackNum >= blockNum || !CombatUtil.canBeBlocked(attacker, this.blockers, combat)) {
- damage += ComputerUtilCombat.damageIfUnblocked(atta, opp, null);
- } else if (CombatUtil.canBeBlocked(attacker, this.blockers, combat)) {
- attackNum++;
- }
- }
- // if enough damage: switch to next planeswalker or player
- if (damage >= pw.getCounters(CounterType.LOYALTY)) {
- List pwDefending = combat.getDefendingPlaneswalkers();
- boolean found = false;
- // look for next planeswalker
- for (Card walker : pwDefending) {
- if (combat.getAttackersOf(walker).isEmpty()) {
- defender = walker;
- found = true;
- break;
- }
- }
- if (!found) {
- defender = getDefendingPlayers(combat).get(0);
- }
- }
- }
- }
- }
- } // getAttackers()
-
- /**
- *
- * countExaltedBonus.
- *
- *
- * @param player
- * a {@link forge.game.player.Player} object.
- * @return a int.
- */
- public final int countExaltedBonus(final Player player) {
- int bonus = 0;
- for (Card c : player.getCardsIn(ZoneType.Battlefield)) {
- bonus += c.getKeywordAmount("Exalted");
- }
-
- return bonus;
- }
-
- /**
- *
- * getAttack.
- *
- *
- * @param c
- * a {@link forge.game.card.Card} object.
- * @return a int.
- */
- public final int getAttack(final Card c) {
- int n = c.getNetCombatDamage();
-
- if (c.hasKeyword("Double Strike")) {
- n *= 2;
- }
-
- return n;
- }
-
- /**
- *
- * shouldAttack.
- *
- *
- * @param attacker
- * a {@link forge.game.card.Card} object.
- * @param defenders
- * a {@link forge.CardList} object.
- * @param combat
- * a {@link forge.game.combat.Combat} object.
- * @return a boolean.
- */
- public final boolean shouldAttack(final Player ai, final Card attacker, final List defenders, final Combat combat) {
- boolean canBeKilledByOne = false; // indicates if the attacker can be killed by a single blocker
- boolean canKillAll = true; // indicates if the attacker can kill all single blockers
- boolean canKillAllDangerous = true; // indicates if the attacker can kill all single blockers with wither or infect
- boolean isWorthLessThanAllKillers = true;
- boolean canBeBlocked = false;
- int numberOfPossibleBlockers = 0;
-
-
- if (!this.isEffectiveAttacker(ai, attacker, combat)) {
- return false;
- }
- boolean hasAttackEffect = attacker.getSVar("HasAttackEffect").equals("TRUE") || attacker.hasStartOfKeyword("Annihilator");
- // is there a gain in attacking even when the blocker is not killed (Lifelink, Wither,...)
- boolean hasCombatEffect = attacker.getSVar("HasCombatEffect").equals("TRUE");
- if (!hasCombatEffect) {
- for (String keyword : attacker.getKeyword()) {
- if (keyword.equals("Wither") || keyword.equals("Infect") || keyword.equals("Lifelink")) {
- hasCombatEffect = true;
- break;
- }
- }
- }
-
- // look at the attacker in relation to the blockers to establish a
- // number of factors about the attacking
- // context that will be relevant to the attackers decision according to
- // the selected strategy
- for (final Card defender : defenders) {
- // if both isWorthLessThanAllKillers and canKillAllDangerous are false there's nothing more to check
- if ((isWorthLessThanAllKillers || canKillAllDangerous || numberOfPossibleBlockers < 2)
- && CombatUtil.canBlock(attacker, defender)) {
- numberOfPossibleBlockers += 1;
- if (isWorthLessThanAllKillers && ComputerUtilCombat.canDestroyAttacker(ai, attacker, defender, combat, false)
- && !(attacker.hasKeyword("Undying") && attacker.getCounters(CounterType.P1P1) == 0)) {
- canBeKilledByOne = true; // there is a single creature on
- // the battlefield that can kill
- // the creature
- // see if the defending creature is of higher or lower
- // value. We don't want to attack only to lose value
- if (isWorthLessThanAllKillers && !attacker.hasSVar("SacMe")
- && ComputerUtilCard.evaluateCreature(defender) <= ComputerUtilCard.evaluateCreature(attacker)) {
- isWorthLessThanAllKillers = false;
- }
- }
- // see if this attacking creature can destroy this defender, if
- // not record that it can't kill everything
- if (canKillAllDangerous && !ComputerUtilCombat.canDestroyBlocker(ai, defender, attacker, combat, false)) {
- canKillAll = false;
- if (!canKillAllDangerous) {
- continue;
- }
- if (defender.getSVar("HasCombatEffect").equals("TRUE")) {
- canKillAllDangerous = false;
- } else {
- for (String keyword : defender.getKeyword()) {
- if (keyword.equals("Wither") || keyword.equals("Infect") || keyword.equals("Lifelink")) {
- canKillAllDangerous = false;
- break;
- // there is a creature that can survive an attack from this creature
- // and combat will have negative effects
- }
- }
- }
- }
- }
- }
-
- // if the creature cannot block and can kill all opponents they might as
- // well attack, they do nothing staying back
- if (canKillAll && isWorthLessThanAllKillers && !CombatUtil.canBlock(attacker)) {
- if ( LOG_AI_ATTACKS )
- System.out.println(attacker.getName() + " = attacking because they can't block, expecting to kill or damage player");
- return true;
- }
-
- if (numberOfPossibleBlockers > 1 || (numberOfPossibleBlockers == 1 && CombatUtil.canAttackerBeBlockedWithAmount(attacker, 1, combat))) {
- canBeBlocked = true;
- }
- /*System.out.println(attacker + " canBeKilledByOne: " + canBeKilledByOne + " canKillAll: "
- + canKillAll + " isWorthLessThanAllKillers: " + isWorthLessThanAllKillers + " canBeBlocked: " + canBeBlocked);*/
- // decide if the creature should attack based on the prevailing strategy
- // choice in aiAggression
- switch (this.aiAggression) {
- case 6: // Exalted: expecting to at least kill a creature of equal value or not be blocked
- if ((canKillAll && isWorthLessThanAllKillers) || !canBeBlocked) {
- if ( LOG_AI_ATTACKS )
- System.out.println(attacker.getName() + " = attacking expecting to kill creature, or is unblockable");
- return true;
- }
- break;
- case 5: // all out attacking
- if ( LOG_AI_ATTACKS )
- System.out.println(attacker.getName() + " = all out attacking");
- return true;
- case 4: // expecting to at least trade with something
- if (canKillAll || (canKillAllDangerous && !canBeKilledByOne) || !canBeBlocked) {
- if ( LOG_AI_ATTACKS )
- System.out.println(attacker.getName() + " = attacking expecting to at least trade with something");
- return true;
- }
- break;
- case 3: // expecting to at least kill a creature of equal value, not be
- // blocked
- if ((canKillAll && isWorthLessThanAllKillers)
- || ((canKillAllDangerous || hasAttackEffect || hasCombatEffect) && !canBeKilledByOne)
- || !canBeBlocked) {
- if ( LOG_AI_ATTACKS )
- System.out.println(attacker.getName() + " = attacking expecting to kill creature or cause damage, or is unblockable");
- return true;
- }
- break;
- case 2: // attack expecting to attract a group block or destroying a
- // single blocker and surviving
- if (((canKillAll || hasAttackEffect || hasCombatEffect) && !canBeKilledByOne) || !canBeBlocked) {
- if ( LOG_AI_ATTACKS )
- System.out.println(attacker.getName() + " = attacking expecting to survive or attract group block");
- return true;
- }
- break;
- case 1: // unblockable creatures only
- if (!canBeBlocked || (numberOfPossibleBlockers == 1 && canKillAll && !canBeKilledByOne)) {
- if ( LOG_AI_ATTACKS )
- System.out.println(attacker.getName() + " = attacking expecting not to be blocked");
- return true;
- }
- break;
- default:
- break;
- }
- return false; // don't attack
- }
-
-} // end class ComputerUtil_Attack2
+/*
+ * Forge: Play Magic: the Gathering.
+ * Copyright (C) 2011 Forge Team
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package forge.ai;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.Lists;
+
+import forge.game.GameEntity;
+import forge.game.card.Card;
+import forge.game.card.CardLists;
+import forge.game.card.CounterType;
+import forge.game.combat.Combat;
+import forge.game.combat.CombatUtil;
+import forge.game.player.Player;
+import forge.game.trigger.Trigger;
+import forge.game.trigger.TriggerType;
+import forge.game.zone.ZoneType;
+
+
+//doesHumanAttackAndWin() uses the global variable AllZone.getComputerPlayer()
+/**
+ *
+ * ComputerUtil_Attack2 class.
+ *
+ *
+ * @author Forge
+ * @version $Id$
+ */
+public class AiAttackController {
+
+ // possible attackers and blockers
+ private final List attackers;
+ private final List blockers;
+
+ private final static Random random = new Random();
+ private final static int randomInt = random.nextInt();
+
+ private List oppList; // holds human player creatures
+ private List myList; // holds computer creatures
+
+ private final Player ai;
+
+ private int aiAggression = 0; // added by Masher, how aggressive the ai is
+ // attack will be depending on circumstances
+
+
+ /**
+ *
+ * Constructor for ComputerUtil_Attack2.
+ *
+ *
+ * @param possibleAttackers
+ * a {@link forge.CardList} object.
+ * @param possibleBlockers
+ * a {@link forge.CardList} object.
+ */
+ public AiAttackController(final Player ai) {
+ this.ai = ai;
+ Player opponent = ai.getOpponent();
+
+ this.oppList = Lists.newArrayList();
+ this.oppList.addAll(opponent.getCreaturesInPlay());
+ this.myList = ai.getCreaturesInPlay();
+
+
+ this.attackers = new ArrayList();
+ for (Card c : myList) {
+ if (CombatUtil.canAttack(c, opponent)) {
+ attackers.add(c);
+ }
+ }
+ this.blockers = this.getPossibleBlockers(oppList, this.attackers);
+ } // constructor
+
+ /**
+ *
+ * sortAttackers.
+ *
+ *
+ * @param in
+ * a {@link forge.CardList} object.
+ * @return a {@link forge.CardList} object.
+ */
+ public final List sortAttackers(final List in) {
+ final List list = new ArrayList();
+
+ // Cards with triggers should come first (for Battle Cry)
+ for (final Card attacker : in) {
+ for (final Trigger trigger : attacker.getTriggers()) {
+ if (trigger.getMode() == TriggerType.Attacks) {
+ list.add(attacker);
+ break;
+ }
+ }
+ }
+
+ for (final Card attacker : in) {
+ if (!list.contains(attacker)) {
+ list.add(attacker);
+ }
+ }
+
+ return list;
+ } // sortAttackers()
+
+ // Is there any reward for attacking? (for 0/1 creatures there is not)
+ /**
+ *
+ * isEffectiveAttacker.
+ *
+ *
+ * @param attacker
+ * a {@link forge.game.card.Card} object.
+ * @param combat
+ * a {@link forge.game.combat.Combat} object.
+ * @return a boolean.
+ */
+ public final boolean isEffectiveAttacker(final Player ai, final Card attacker, final Combat combat) {
+
+ // if the attacker will die when attacking don't attack
+ if ((attacker.getNetDefense() + ComputerUtilCombat.predictToughnessBonusOfAttacker(attacker, null, combat, true)) <= 0) {
+ return false;
+ }
+
+ final Player opp = ai.getOpponent();
+ if (ComputerUtilCombat.damageIfUnblocked(attacker, opp, combat) > 0) {
+ return true;
+ }
+ if (ComputerUtilCombat.poisonIfUnblocked(attacker, opp) > 0) {
+ return true;
+ }
+ if (this.attackers.size() == 1 && attacker.hasKeyword("Exalted") && ComputerUtilCombat.predictDamageTo(opp, 1, attacker, true) > 0) {
+ return true;
+ }
+
+ final List controlledByCompy = ai.getCardsIn(ZoneType.STATIC_ABILITIES_SOURCE_ZONES);
+ for (final Card c : controlledByCompy) {
+ for (final Trigger trigger : c.getTriggers()) {
+ if (ComputerUtilCombat.combatTriggerWillTrigger(attacker, null, trigger, combat)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ *
+ * getPossibleBlockers.
+ *
+ *
+ * @param blockers
+ * a {@link forge.CardList} object.
+ * @param attackers
+ * a {@link forge.CardList} object.
+ * @return a {@link forge.CardList} object.
+ */
+ public final List getPossibleBlockers(final List blockers, final List attackers) {
+ List possibleBlockers = new ArrayList(blockers);
+ possibleBlockers = CardLists.filter(possibleBlockers, new Predicate() {
+ @Override
+ public boolean apply(final Card c) {
+ return canBlockAnAttacker(c, attackers);
+ }
+ });
+ return possibleBlockers;
+ } // getPossibleBlockers()
+
+ /**
+ *
+ * canBlockAnAttacker.
+ *
+ *
+ * @param c
+ * a {@link forge.game.card.Card} object.
+ * @param attackers
+ * a {@link forge.CardList} object.
+ * @return a boolean.
+ */
+ public final boolean canBlockAnAttacker(final Card c, final List attackers) {
+ final List attackerList = new ArrayList(attackers);
+ if (!c.isCreature()) {
+ return false;
+ }
+ for (final Card attacker : attackerList) {
+ if (CombatUtil.canBlock(attacker, c)) {
+ return true;
+ }
+ }
+ return false;
+ } // getPossibleBlockers()
+
+ // this checks to make sure that the computer player
+ // doesn't lose when the human player attacks
+ // this method is used by getAttackers()
+ /**
+ *
+ * notNeededAsBlockers.
+ *
+ *
+ * @param attackers
+ * a {@link forge.CardList} object.
+ * @param combat
+ * a {@link forge.game.combat.Combat} object.
+ * @return a {@link forge.CardList} object.
+ */
+ public final List notNeededAsBlockers(final Player ai, final List attackers) {
+ final List notNeededAsBlockers = new ArrayList(attackers);
+ int fixedBlockers = 0;
+ final List vigilantes = new ArrayList();
+ //check for time walks
+ if (ai.getGame().getPhaseHandler().getNextTurn().equals(ai)) {
+ return attackers;
+ }
+ for (final Card c : this.myList) {
+ if (c.getName().equals("Masako the Humorless")) {
+ // "Tapped creatures you control can block as though they were untapped."
+ return attackers;
+ }
+ if (!attackers.contains(c)) { // this creature can't attack anyway
+ if (canBlockAnAttacker(c, this.oppList)) {
+ fixedBlockers++;
+ }
+ continue;
+ }
+ if (c.hasKeyword("Vigilance")) {
+ vigilantes.add(c);
+ notNeededAsBlockers.remove(c); // they will be re-added later
+ if (canBlockAnAttacker(c, this.oppList)) {
+ fixedBlockers++;
+ }
+ }
+ }
+ CardLists.sortByPowerAsc(attackers);
+ int blockersNeeded = this.oppList.size();
+
+ // don't hold back creatures that can't block any of the human creatures
+ final List list = this.getPossibleBlockers(attackers, this.oppList);
+
+ //Calculate the amount of creatures necessary
+ for (int i = 0; i < list.size(); i++) {
+ if (!this.doesHumanAttackAndWin(ai, i)) {
+ blockersNeeded = i;
+ break;
+ }
+ }
+ int blockersStillNeeded = blockersNeeded - fixedBlockers;
+ blockersStillNeeded = Math.min(blockersNeeded, list.size());
+ for (int i = 0; i < blockersStillNeeded; i++) {
+ notNeededAsBlockers.remove(list.get(i));
+ }
+
+ // re-add creatures with vigilance
+ notNeededAsBlockers.addAll(vigilantes);
+
+ if (blockersNeeded > 1) {
+ return notNeededAsBlockers;
+ }
+
+ final Player opp = ai.getOpponent();
+
+ // Increase the total number of blockers needed by 1 if Finest Hour in
+ // play
+ // (human will get an extra first attack with a creature that untaps)
+ // In addition, if the computer guesses it needs no blockers, make sure
+ // that
+ // it won't be surprised by Exalted
+ final int humanExaltedBonus = this.countExaltedBonus(opp);
+
+ if (humanExaltedBonus > 0) {
+ final int nFinestHours = opp.getCardsIn(ZoneType.Battlefield, "Finest Hour").size();
+
+ if (((blockersNeeded == 0) || (nFinestHours > 0)) && (this.oppList.size() > 0)) {
+ //
+ // total attack = biggest creature + exalted, *2 if Rafiq is in
+ // play
+ int humanBaseAttack = this.getAttack(this.oppList.get(0)) + humanExaltedBonus;
+ if (nFinestHours > 0) {
+ // For Finest Hour, one creature could attack and get the
+ // bonus TWICE
+ humanBaseAttack = humanBaseAttack + humanExaltedBonus;
+ }
+ final int totalExaltedAttack = opp.isCardInPlay("Rafiq of the Many") ? 2 * humanBaseAttack
+ : humanBaseAttack;
+ if (ai.getLife() - 3 <= totalExaltedAttack) {
+ // We will lose if there is an Exalted attack -- keep one
+ // blocker
+ if ((blockersNeeded == 0) && (notNeededAsBlockers.size() > 0)) {
+ notNeededAsBlockers.remove(0);
+ }
+
+ // Finest Hour allows a second Exalted attack: keep a
+ // blocker for that too
+ if ((nFinestHours > 0) && (notNeededAsBlockers.size() > 0)) {
+ notNeededAsBlockers.remove(0);
+ }
+ }
+ }
+ }
+ return notNeededAsBlockers;
+ }
+
+ // this uses a global variable, which isn't perfect
+ /**
+ *
+ * doesHumanAttackAndWin.
+ *
+ *
+ * @param nBlockingCreatures
+ * a int.
+ * @return a boolean.
+ */
+ public final boolean doesHumanAttackAndWin(final Player ai, final int nBlockingCreatures) {
+ int totalAttack = 0;
+ int totalPoison = 0;
+ int blockersLeft = nBlockingCreatures;
+
+ if (ai.cantLose()) {
+ return false;
+ }
+
+ for (Card attacker : oppList) {
+ if (!CombatUtil.canAttackNextTurn(attacker)) {
+ continue;
+ }
+ if (blockersLeft > 0 && CombatUtil.canBeBlocked(attacker, ai)) {
+ blockersLeft--;
+ continue;
+ }
+ totalAttack += ComputerUtilCombat.damageIfUnblocked(attacker, ai, null);
+ totalPoison += ComputerUtilCombat.poisonIfUnblocked(attacker, ai);
+ }
+
+ if (totalAttack > 0 && ai.getLife() <= totalAttack
+ && !ai.cantLoseForZeroOrLessLife()) {
+ return true;
+ }
+ return ai.getPoisonCounters() + totalPoison > 9;
+ }
+
+ /**
+ *
+ * doAssault.
+ *
+ *
+ * @return a boolean.
+ */
+ private boolean doAssault(final Player ai) {
+ // Beastmaster Ascension
+ if (ai.isCardInPlay("Beastmaster Ascension")
+ && (this.attackers.size() > 1)) {
+ final List beastions = ai.getCardsIn(ZoneType.Battlefield, "Beastmaster Ascension");
+ int minCreatures = 7;
+ for (final Card beastion : beastions) {
+ final int counters = beastion.getCounters(CounterType.QUEST);
+ minCreatures = Math.min(minCreatures, 7 - counters);
+ }
+ if (this.attackers.size() >= minCreatures) {
+ return true;
+ }
+ }
+
+ CardLists.sortByPowerDesc(this.attackers);
+
+ final List unblockedAttackers = new ArrayList();
+ final List remainingAttackers = new ArrayList(this.attackers);
+ final List remainingBlockers = new ArrayList(this.blockers);
+ final Player opp = ai.getOpponent();
+
+
+ for (Card attacker : attackers) {
+ if (!CombatUtil.canBeBlocked(attacker, this.blockers, null)
+ || attacker.hasKeyword("You may have CARDNAME assign its combat damage as though it weren't blocked.")) {
+ unblockedAttackers.add(attacker);
+ }
+ }
+
+ for (Card blocker : this.blockers) {
+ if (blocker.hasKeyword("CARDNAME can block any number of creatures.")
+ || blocker.hasKeyword("CARDNAME can block an additional ninety-nine creatures.")) {
+ for (Card attacker : this.attackers) {
+ if (CombatUtil.canBlock(attacker, blocker)) {
+ remainingAttackers.remove(attacker);
+ }
+ }
+ remainingBlockers.remove(blocker);
+ }
+ }
+
+ // presumes the Human will block
+ for (Card blocker : remainingBlockers) {
+ if (remainingAttackers.isEmpty()) {
+ break;
+ }
+ if (blocker.hasKeyword("CARDNAME can block an additional creature.")) {
+ remainingAttackers.remove(0);
+ if (remainingAttackers.isEmpty()) {
+ break;
+ }
+ }
+ remainingAttackers.remove(0);
+ }
+ unblockedAttackers.addAll(remainingAttackers);
+
+ if (ComputerUtilCombat.sumDamageIfUnblocked(remainingAttackers, opp) + ComputerUtil.possibleNonCombatDamage(ai) >= opp.getLife()
+ && !((opp.cantLoseForZeroOrLessLife() || ai.cantWin()) && (opp.getLife() < 1))) {
+ return true;
+ }
+
+ if (ComputerUtilCombat.sumPoisonIfUnblocked(remainingAttackers, opp) >= (10 - opp.getPoisonCounters())) {
+ return true;
+ }
+
+ return false;
+ } // doAssault()
+
+ /**
+ *
+ * chooseDefender.
+ *
+ *
+ * @param c
+ * a {@link forge.game.combat.Combat} object.
+ * @param bAssault
+ * a boolean.
+ */
+ private final GameEntity chooseDefender(final Combat c, final boolean bAssault) {
+ final List defs = c.getDefenders();
+ if (defs.size() == 1) {
+ return defs.get(0);
+ }
+
+ final GameEntity entity = ai.getMustAttackEntity();
+ if (null != entity) {
+ int n = defs.indexOf(entity);
+ if (-1 == n) {
+ System.out.println("getMustAttackEntity() returned something not in defenders.");
+ return defs.get(0);
+ } else {
+ return entity;
+ }
+ } else {
+ // 1. assault the opponent if you can kill him
+ if (bAssault) {
+ return getDefendingPlayers(c).get(0);
+ }
+ // 2. attack planeswalkers
+ List pwDefending = c.getDefendingPlaneswalkers();
+ if (!pwDefending.isEmpty()) {
+ return pwDefending.get(0);
+ } else {
+ return defs.get(0);
+ }
+ }
+ }
+
+ final boolean LOG_AI_ATTACKS = false;
+
+
+ public final List getDefendingPlayers(Combat combat) {
+ final List defending = new ArrayList();
+ for (final GameEntity o : combat.getDefenders()) {
+ if (o instanceof Player) {
+ defending.add((Player) o);
+ }
+ }
+ return defending;
+ }
+
+
+ /**
+ *
+ * Getter for the field attackers.
+ *
+ *
+ * @return a {@link forge.game.combat.Combat} object.
+ */
+ public final void declareAttackers(final Combat combat) {
+ // if this method is called multiple times during a turn,
+ // it will always return the same value
+ // randomInt is used so that the computer doesn't always
+ // do the same thing on turn 3 if he had the same creatures in play
+ // I know this is a little confusing
+
+ random.setSeed(ai.getGame().getPhaseHandler().getTurn() + AiAttackController.randomInt);
+
+ if (this.attackers.isEmpty()) {
+ return;
+ }
+
+ final boolean bAssault = this.doAssault(ai);
+ // Determine who will be attacked
+ GameEntity defender = this.chooseDefender(combat, bAssault);
+ List attackersLeft = new ArrayList(this.attackers);
+ // Attackers that don't really have a choice
+ for (final Card attacker : this.attackers) {
+ if (!CombatUtil.canAttack(attacker, defender, combat)) {
+ continue;
+ }
+ boolean mustAttack = false;
+ for (String s : attacker.getKeyword()) {
+ if (s.equals("CARDNAME attacks each turn if able.")
+ || s.startsWith("CARDNAME attacks specific player each combat if able")) {
+ mustAttack = true;
+ break;
+ }
+ if ((s.equals("At the beginning of the end step, destroy CARDNAME.")
+ || s.equals("At the beginning of the end step, exile CARDNAME.")
+ || s.equals("At the beginning of the end step, sacrifice CARDNAME."))
+ && isEffectiveAttacker(ai, attacker, combat)) {
+ mustAttack = true;
+ break;
+ }
+ }
+ if (mustAttack || attacker.getController().getMustAttackEntity() != null
+ || attacker.getSVar("MustAttack").equals("True")) {
+ combat.addAttacker(attacker, defender);
+ attackersLeft.remove(attacker);
+ }
+ }
+ if (attackersLeft.isEmpty()) {
+ return;
+ }
+ if (bAssault) {
+ if ( LOG_AI_ATTACKS )
+ System.out.println("Assault");
+ CardLists.sortByPowerDesc(attackersLeft);
+ for (Card attacker : attackersLeft) {
+ if (CombatUtil.canAttack(attacker, defender, combat) && this.isEffectiveAttacker(ai, attacker, combat)) {
+ combat.addAttacker(attacker, defender);
+ }
+ }
+ return;
+ }
+
+ // Exalted
+ if (combat.getAttackers().isEmpty()) {
+ boolean exalted = false;
+ int exaltedCount = 0;
+ for (Card c : ai.getCardsIn(ZoneType.Battlefield)) {
+ if (c.getName().equals("Rafiq of the Many") || c.getName().equals("Battlegrace Angel")) {
+ exalted = true;
+ break;
+ }
+ if (c.getName().equals("Finest Hour") && ai.getGame().getPhaseHandler().isFirstCombat()) {
+ exalted = true;
+ break;
+ }
+ if (c.hasKeyword("Exalted")) {
+ exaltedCount++;
+ if (exaltedCount > 2) {
+ exalted = true;
+ break;
+ }
+ }
+ }
+ if (exalted) {
+ CardLists.sortByPowerDesc(this.attackers);
+ if ( LOG_AI_ATTACKS )
+ System.out.println("Exalted");
+ this.aiAggression = 6;
+ for (Card attacker : this.attackers) {
+ if (CombatUtil.canAttack(attacker, defender, combat) && this.shouldAttack(ai, attacker, this.blockers, combat)) {
+ combat.addAttacker(attacker, defender);
+ return;
+ }
+ }
+ }
+ }
+
+ // *******************
+ // Evaluate the creature forces
+ // *******************
+
+ int computerForces = 0;
+ int humanForces = 0;
+ int humanForcesForAttritionalAttack = 0;
+
+ // examine the potential forces
+ final List nextTurnAttackers = new ArrayList();
+ int candidateCounterAttackDamage = 0;
+
+ for (final Card pCard : this.oppList) {
+ // if the creature can attack next turn add it to counter attackers
+ // list
+ if (CombatUtil.canAttackNextTurn(pCard)) {
+ nextTurnAttackers.add(pCard);
+ if (pCard.getNetCombatDamage() > 0) {
+ candidateCounterAttackDamage += pCard.getNetCombatDamage();
+ humanForces += 1; // player forces they might use to attack
+ }
+ }
+ // increment player forces that are relevant to an attritional
+ // attack - includes walls
+ if (CombatUtil.canBlock(pCard, true)) {
+ humanForcesForAttritionalAttack += 1;
+ }
+ }
+
+ // find the potential counter attacking damage compared to AI life total
+ double aiLifeToPlayerDamageRatio = 1000000;
+ if (candidateCounterAttackDamage > 0) {
+ aiLifeToPlayerDamageRatio = (double) ai.getLife() / candidateCounterAttackDamage;
+ }
+
+ final Player opp = ai.getOpponent();
+ // get the potential damage and strength of the AI forces
+ final List candidateAttackers = new ArrayList();
+ int candidateUnblockedDamage = 0;
+ for (final Card pCard : this.myList) {
+ // if the creature can attack then it's a potential attacker this
+ // turn, assume summoning sickness creatures will be able to
+ if (CombatUtil.canAttackNextTurn(pCard)) {
+ candidateAttackers.add(pCard);
+ if (pCard.getNetCombatDamage() > 0) {
+ candidateUnblockedDamage += ComputerUtilCombat.damageIfUnblocked(pCard, opp, null);
+ computerForces += 1;
+ }
+ }
+ }
+
+ // find the potential damage ratio the AI can cause
+ double humanLifeToDamageRatio = 1000000;
+ if (candidateUnblockedDamage > 0) {
+ humanLifeToDamageRatio = (double) (opp.getLife() - ComputerUtil.possibleNonCombatDamage(ai)) / candidateUnblockedDamage;
+ }
+
+ // determine if the ai outnumbers the player
+ final int outNumber = computerForces - humanForces;
+
+ for (Card blocker : this.blockers) {
+ if (blocker.hasKeyword("CARDNAME can block any number of creatures.")) {
+ aiLifeToPlayerDamageRatio--;
+ }
+ }
+
+ // compare the ratios, higher = better for ai
+ final double ratioDiff = aiLifeToPlayerDamageRatio - humanLifeToDamageRatio;
+
+ // *********************
+ // if outnumber and superior ratio work out whether attritional all out
+ // attacking will work
+ // attritional attack will expect some creatures to die but to achieve
+ // victory by sheer weight
+ // of numbers attacking turn after turn. It's not calculate very
+ // carefully, the accuracy
+ // can probably be improved
+ // *********************
+ boolean doAttritionalAttack = false;
+ // get list of attackers ordered from low power to high
+ CardLists.sortByPowerAsc(this.attackers);
+ // get player life total
+ int humanLife = opp.getLife();
+ // get the list of attackers up to the first blocked one
+ final List attritionalAttackers = new ArrayList();
+ for (int x = 0; x < (this.attackers.size() - humanForces); x++) {
+ attritionalAttackers.add(this.attackers.get(x));
+ }
+ // until the attackers are used up or the player would run out of life
+ int attackRounds = 1;
+ while (attritionalAttackers.size() > 0 && humanLife > 0 && attackRounds < 99) {
+ // sum attacker damage
+ int damageThisRound = 0;
+ for (int y = 0; y < attritionalAttackers.size(); y++) {
+ damageThisRound += attritionalAttackers.get(y).getNetCombatDamage();
+ }
+ // remove from player life
+ humanLife -= damageThisRound;
+ // shorten attacker list by the length of the blockers - assuming
+ // all blocked are killed for convenience
+ for (int z = 0; z < humanForcesForAttritionalAttack; z++) {
+ if (attritionalAttackers.size() > 0) {
+ attritionalAttackers.remove(attritionalAttackers.size() - 1);
+ }
+ }
+ attackRounds += 1;
+ if (humanLife <= 0) {
+ doAttritionalAttack = true;
+ }
+ }
+ // System.out.println(doAttritionalAttack + " = do attritional attack");
+ // *********************
+ // end attritional attack calculation
+ // *********************
+
+ // *********************
+ // see how long until unblockable attackers will be fatal
+ // *********************
+ double unblockableDamage = 0;
+ double nextUnblockableDamage = 0;
+ double turnsUntilDeathByUnblockable = 0;
+ boolean doUnblockableAttack = false;
+ for (final Card attacker : this.attackers) {
+ boolean isUnblockableCreature = true;
+ // check blockers individually, as the bulk canBeBlocked doesn't
+ // check all circumstances
+ for (final Card blocker : this.blockers) {
+ if (CombatUtil.canBlock(attacker, blocker)) {
+ isUnblockableCreature = false;
+ break;
+ }
+ }
+ if (isUnblockableCreature) {
+ unblockableDamage += ComputerUtilCombat.damageIfUnblocked(attacker, opp, combat);
+ }
+ }
+ for (final Card attacker : nextTurnAttackers) {
+ boolean isUnblockableCreature = true;
+ // check blockers individually, as the bulk canBeBlocked doesn't
+ // check all circumstances
+ for (final Card blocker : this.myList) {
+ if (CombatUtil.canBlock(attacker, blocker, true)) {
+ isUnblockableCreature = false;
+ break;
+ }
+ }
+ if (isUnblockableCreature) {
+ nextUnblockableDamage += ComputerUtilCombat.damageIfUnblocked(attacker, opp, null);
+ }
+ }
+ if (unblockableDamage > 0 && !opp.cantLoseForZeroOrLessLife()
+ && opp.canLoseLife()) {
+ turnsUntilDeathByUnblockable = 1 + (opp.getLife() - unblockableDamage) / nextUnblockableDamage;
+ }
+ if (opp.canLoseLife()) {
+ doUnblockableAttack = true;
+ }
+ // *****************
+ // end see how long until unblockable attackers will be fatal
+ // *****************
+
+ // decide on attack aggression based on a comparison of forces, life
+ // totals and other considerations
+ // some bad "magic numbers" here, TODO replace with nice descriptive
+ // variable names
+ if (ratioDiff > 0 && doAttritionalAttack) {
+ this.aiAggression = 5; // attack at all costs
+ } else if (ratioDiff >= 1 && (humanLifeToDamageRatio < 2 || outNumber > 0)) {
+ this.aiAggression = 4; // attack expecting to trade or damage player.
+ } else if (ratioDiff >= 0) {
+ this.aiAggression = 3; // attack expecting to make good trades or damage player.
+ } else if (ratioDiff + outNumber >= -1 || aiLifeToPlayerDamageRatio > 1
+ || ratioDiff * -1 < turnsUntilDeathByUnblockable) {
+ // at 0 ratio expect to potentially gain an advantage by attacking first
+ // if the ai has a slight advantage
+ // or the ai has a significant advantage numerically but only a slight disadvantage damage/life
+ this.aiAggression = 2; // attack expecting to destroy creatures/be unblockable
+ } else if (doUnblockableAttack) {
+ this.aiAggression = 1;
+ // look for unblockable creatures that might be
+ // able to attack for a bit of fatal damage even if the player is significantly better
+ } else {
+ this.aiAggression = 0;
+ } // stay at home to block
+
+ if ( LOG_AI_ATTACKS )
+ System.out.println(String.valueOf(this.aiAggression) + " = ai aggression");
+
+ // ****************
+ // Evaluation the end
+ // ****************
+
+ if ( LOG_AI_ATTACKS )
+ System.out.println("Normal attack");
+
+ attackersLeft = this.notNeededAsBlockers(ai, attackersLeft);
+ attackersLeft = this.sortAttackers(attackersLeft);
+
+ if ( LOG_AI_ATTACKS )
+ System.out.println("attackersLeft = " + attackersLeft);
+
+ for (int i = 0; i < attackersLeft.size(); i++) {
+ final Card attacker = attackersLeft.get(i);
+ if (this.aiAggression < 5 && !attacker.hasFirstStrike() && !attacker.hasDoubleStrike()
+ && ComputerUtilCombat.getTotalFirstStrikeBlockPower(attacker, ai.getOpponent())
+ >= ComputerUtilCombat.getDamageToKill(attacker)) {
+ continue;
+ }
+
+ if (this.shouldAttack(ai, attacker, this.blockers, combat) && CombatUtil.canAttack(attacker, defender, combat)) {
+ combat.addAttacker(attacker, defender);
+ // check if attackers are enough to finish the attacked planeswalker
+ if (defender instanceof Card) {
+ Card pw = (Card) defender;
+ final int blockNum = this.blockers.size();
+ int attackNum = 0;
+ int damage = 0;
+ List attacking = combat.getAttackersOf(defender);
+ CardLists.sortByPowerAsc(attacking);
+ for (Card atta : attacking) {
+ if (attackNum >= blockNum || !CombatUtil.canBeBlocked(attacker, this.blockers, combat)) {
+ damage += ComputerUtilCombat.damageIfUnblocked(atta, opp, null);
+ } else if (CombatUtil.canBeBlocked(attacker, this.blockers, combat)) {
+ attackNum++;
+ }
+ }
+ // if enough damage: switch to next planeswalker or player
+ if (damage >= pw.getCounters(CounterType.LOYALTY)) {
+ List pwDefending = combat.getDefendingPlaneswalkers();
+ boolean found = false;
+ // look for next planeswalker
+ for (Card walker : pwDefending) {
+ if (combat.getAttackersOf(walker).isEmpty()) {
+ defender = walker;
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ defender = getDefendingPlayers(combat).get(0);
+ }
+ }
+ }
+ }
+ }
+ } // getAttackers()
+
+ /**
+ *
+ * countExaltedBonus.
+ *
+ *
+ * @param player
+ * a {@link forge.game.player.Player} object.
+ * @return a int.
+ */
+ public final int countExaltedBonus(final Player player) {
+ int bonus = 0;
+ for (Card c : player.getCardsIn(ZoneType.Battlefield)) {
+ bonus += c.getKeywordAmount("Exalted");
+ }
+
+ return bonus;
+ }
+
+ /**
+ *
+ * getAttack.
+ *
+ *
+ * @param c
+ * a {@link forge.game.card.Card} object.
+ * @return a int.
+ */
+ public final int getAttack(final Card c) {
+ int n = c.getNetCombatDamage();
+
+ if (c.hasKeyword("Double Strike")) {
+ n *= 2;
+ }
+
+ return n;
+ }
+
+ /**
+ *
+ * shouldAttack.
+ *
+ *
+ * @param attacker
+ * a {@link forge.game.card.Card} object.
+ * @param defenders
+ * a {@link forge.CardList} object.
+ * @param combat
+ * a {@link forge.game.combat.Combat} object.
+ * @return a boolean.
+ */
+ public final boolean shouldAttack(final Player ai, final Card attacker, final List defenders, final Combat combat) {
+ boolean canBeKilledByOne = false; // indicates if the attacker can be killed by a single blocker
+ boolean canKillAll = true; // indicates if the attacker can kill all single blockers
+ boolean canKillAllDangerous = true; // indicates if the attacker can kill all single blockers with wither or infect
+ boolean isWorthLessThanAllKillers = true;
+ boolean canBeBlocked = false;
+ int numberOfPossibleBlockers = 0;
+
+
+ if (!this.isEffectiveAttacker(ai, attacker, combat)) {
+ return false;
+ }
+ boolean hasAttackEffect = attacker.getSVar("HasAttackEffect").equals("TRUE") || attacker.hasStartOfKeyword("Annihilator");
+ // is there a gain in attacking even when the blocker is not killed (Lifelink, Wither,...)
+ boolean hasCombatEffect = attacker.getSVar("HasCombatEffect").equals("TRUE");
+ if (!hasCombatEffect) {
+ for (String keyword : attacker.getKeyword()) {
+ if (keyword.equals("Wither") || keyword.equals("Infect") || keyword.equals("Lifelink")) {
+ hasCombatEffect = true;
+ break;
+ }
+ }
+ }
+
+ // look at the attacker in relation to the blockers to establish a
+ // number of factors about the attacking
+ // context that will be relevant to the attackers decision according to
+ // the selected strategy
+ for (final Card defender : defenders) {
+ // if both isWorthLessThanAllKillers and canKillAllDangerous are false there's nothing more to check
+ if ((isWorthLessThanAllKillers || canKillAllDangerous || numberOfPossibleBlockers < 2)
+ && CombatUtil.canBlock(attacker, defender)) {
+ numberOfPossibleBlockers += 1;
+ if (isWorthLessThanAllKillers && ComputerUtilCombat.canDestroyAttacker(ai, attacker, defender, combat, false)
+ && !(attacker.hasKeyword("Undying") && attacker.getCounters(CounterType.P1P1) == 0)) {
+ canBeKilledByOne = true; // there is a single creature on
+ // the battlefield that can kill
+ // the creature
+ // see if the defending creature is of higher or lower
+ // value. We don't want to attack only to lose value
+ if (isWorthLessThanAllKillers && !attacker.hasSVar("SacMe")
+ && ComputerUtilCard.evaluateCreature(defender) <= ComputerUtilCard.evaluateCreature(attacker)) {
+ isWorthLessThanAllKillers = false;
+ }
+ }
+ // see if this attacking creature can destroy this defender, if
+ // not record that it can't kill everything
+ if (canKillAllDangerous && !ComputerUtilCombat.canDestroyBlocker(ai, defender, attacker, combat, false)) {
+ canKillAll = false;
+ if (!canKillAllDangerous) {
+ continue;
+ }
+ if (defender.getSVar("HasCombatEffect").equals("TRUE")) {
+ canKillAllDangerous = false;
+ } else {
+ for (String keyword : defender.getKeyword()) {
+ if (keyword.equals("Wither") || keyword.equals("Infect") || keyword.equals("Lifelink")) {
+ canKillAllDangerous = false;
+ break;
+ // there is a creature that can survive an attack from this creature
+ // and combat will have negative effects
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // if the creature cannot block and can kill all opponents they might as
+ // well attack, they do nothing staying back
+ if (canKillAll && isWorthLessThanAllKillers && !CombatUtil.canBlock(attacker)) {
+ if ( LOG_AI_ATTACKS )
+ System.out.println(attacker.getName() + " = attacking because they can't block, expecting to kill or damage player");
+ return true;
+ }
+
+ if (numberOfPossibleBlockers > 1 || (numberOfPossibleBlockers == 1 && CombatUtil.canAttackerBeBlockedWithAmount(attacker, 1, combat))) {
+ canBeBlocked = true;
+ }
+ /*System.out.println(attacker + " canBeKilledByOne: " + canBeKilledByOne + " canKillAll: "
+ + canKillAll + " isWorthLessThanAllKillers: " + isWorthLessThanAllKillers + " canBeBlocked: " + canBeBlocked);*/
+ // decide if the creature should attack based on the prevailing strategy
+ // choice in aiAggression
+ switch (this.aiAggression) {
+ case 6: // Exalted: expecting to at least kill a creature of equal value or not be blocked
+ if ((canKillAll && isWorthLessThanAllKillers) || !canBeBlocked) {
+ if ( LOG_AI_ATTACKS )
+ System.out.println(attacker.getName() + " = attacking expecting to kill creature, or is unblockable");
+ return true;
+ }
+ break;
+ case 5: // all out attacking
+ if ( LOG_AI_ATTACKS )
+ System.out.println(attacker.getName() + " = all out attacking");
+ return true;
+ case 4: // expecting to at least trade with something
+ if (canKillAll || (canKillAllDangerous && !canBeKilledByOne) || !canBeBlocked) {
+ if ( LOG_AI_ATTACKS )
+ System.out.println(attacker.getName() + " = attacking expecting to at least trade with something");
+ return true;
+ }
+ break;
+ case 3: // expecting to at least kill a creature of equal value, not be
+ // blocked
+ if ((canKillAll && isWorthLessThanAllKillers)
+ || ((canKillAllDangerous || hasAttackEffect || hasCombatEffect) && !canBeKilledByOne)
+ || !canBeBlocked) {
+ if ( LOG_AI_ATTACKS )
+ System.out.println(attacker.getName() + " = attacking expecting to kill creature or cause damage, or is unblockable");
+ return true;
+ }
+ break;
+ case 2: // attack expecting to attract a group block or destroying a
+ // single blocker and surviving
+ if (((canKillAll || hasAttackEffect || hasCombatEffect) && !canBeKilledByOne) || !canBeBlocked) {
+ if ( LOG_AI_ATTACKS )
+ System.out.println(attacker.getName() + " = attacking expecting to survive or attract group block");
+ return true;
+ }
+ break;
+ case 1: // unblockable creatures only
+ if (!canBeBlocked || (numberOfPossibleBlockers == 1 && canKillAll && !canBeKilledByOne)) {
+ if ( LOG_AI_ATTACKS )
+ System.out.println(attacker.getName() + " = attacking expecting not to be blocked");
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
+ return false; // don't attack
+ }
+
+} // end class ComputerUtil_Attack2
diff --git a/forge-game/src/main/java/forge/ai/AiBlockController.java b/forge-gui/src/main/java/forge/ai/AiBlockController.java
similarity index 97%
rename from forge-game/src/main/java/forge/ai/AiBlockController.java
rename to forge-gui/src/main/java/forge/ai/AiBlockController.java
index de2fc174d4a..dae77d3c716 100644
--- a/forge-game/src/main/java/forge/ai/AiBlockController.java
+++ b/forge-gui/src/main/java/forge/ai/AiBlockController.java
@@ -1,809 +1,809 @@
-/*
- * Forge: Play Magic: the Gathering.
- * Copyright (C) 2011 Forge Team
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-package forge.ai;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-
-import com.google.common.base.Predicate;
-import com.google.common.base.Predicates;
-
-import forge.game.GameEntity;
-import forge.game.TriggerReplacementBase;
-import forge.game.card.Card;
-import forge.game.card.CardLists;
-import forge.game.card.CardPredicates;
-import forge.game.card.CounterType;
-import forge.game.combat.Combat;
-import forge.game.combat.CombatUtil;
-import forge.game.player.Player;
-import forge.game.trigger.Trigger;
-import forge.game.trigger.TriggerType;
-
-
-/**
- *
- * ComputerUtil_Block2 class.
- *
- *
- * @author Forge
- * @version $Id: AiBlockController.java 24355 2014-01-19 01:33:24Z swordshine $
- */
-public class AiBlockController {
-
- private final Player ai;
- /** Constant attackers. */
- private List attackers = new ArrayList(); // all attackers
- /** Constant attackersLeft. */
- private List attackersLeft = new ArrayList(); // keeps track of
- // all currently
- // unblocked
- // attackers
- /** Constant blockedButUnkilled. */
- private List blockedButUnkilled = new ArrayList(); // blocked
- // attackers
- // that
- // currently
- // wouldn't be
- // destroyed
- /** Constant blockersLeft. */
- private List blockersLeft = new ArrayList(); // keeps track of all
- // unassigned
- // blockers
- private int diff = 0;
-
- private boolean lifeInDanger = false;
- public AiBlockController(Player aiPlayer) {
- this.ai = aiPlayer;
- }
-
- // finds the creatures able to block the attacker
- private List getPossibleBlockers(final Combat combat, final Card attacker, final List blockersLeft, final boolean solo) {
- final List blockers = new ArrayList();
-
- for (final Card blocker : blockersLeft) {
- // if the blocker can block a creature with lure it can't block a
- // creature without
- if (CombatUtil.canBlock(attacker, blocker, combat)) {
- if (solo && blocker.hasKeyword("CARDNAME can't attack or block alone.")) {
- continue;
- }
- blockers.add(blocker);
- }
- }
-
- return blockers;
- }
-
- // finds blockers that won't be destroyed
- private List getSafeBlockers(final Combat combat, final Card attacker, final List blockersLeft) {
- final List blockers = new ArrayList();
-
- for (final Card b : blockersLeft) {
- if (!ComputerUtilCombat.canDestroyBlocker(ai, b, attacker, combat, false)) {
- blockers.add(b);
- }
- }
-
- return blockers;
- }
-
- // finds blockers that destroy the attacker
- private List getKillingBlockers(final Combat combat, final Card attacker, final List blockersLeft) {
- final List blockers = new ArrayList();
-
- for (final Card b : blockersLeft) {
- if (ComputerUtilCombat.canDestroyAttacker(ai, attacker, b, combat, false)) {
- blockers.add(b);
- }
- }
-
- return blockers;
- }
-
-
-
- private List> sortAttackerByDefender(final Combat combat) {
- List defenders = combat.getDefenders();
- final ArrayList> attackers = new ArrayList>(defenders.size());
- for (GameEntity defender : defenders) {
- attackers.add(combat.getAttackersOf(defender));
- }
- return attackers;
- }
-
- private List sortPotentialAttackers(final Combat combat) {
- final List> attackerLists = sortAttackerByDefender(combat);
- final List sortedAttackers = new ArrayList();
- final List firstAttacker = attackerLists.get(0);
-
- final List defenders = combat.getDefenders();
-
-
- // Begin with the attackers that pose the biggest threat
- CardLists.sortByEvaluateCreature(firstAttacker);
- CardLists.sortByPowerDesc(firstAttacker);
-
- // If I don't have any planeswalkers than sorting doesn't really matter
- if (defenders.size() == 1) {
- return firstAttacker;
- }
-
- final boolean bLifeInDanger = ComputerUtilCombat.lifeInDanger(ai, combat);
-
- // TODO Add creatures attacking Planeswalkers in order of which we want
- // to protect
- // defend planeswalkers with more loyalty before planeswalkers with less
- // loyalty
- // if planeswalker will be too difficult to defend don't even bother
- for (List attacker : attackerLists) {
- // Begin with the attackers that pose the biggest threat
- CardLists.sortByPowerDesc(attacker);
- for (final Card c : attacker) {
- sortedAttackers.add(c);
- }
- }
-
- if (bLifeInDanger) {
- // add creatures attacking the Player to the front of the list
- for (final Card c : firstAttacker) {
- sortedAttackers.add(0, c);
- }
-
- } else {
- // add creatures attacking the Player to the back of the list
- for (final Card c : firstAttacker) {
- sortedAttackers.add(c);
- }
- }
-
- return sortedAttackers;
- }
-
- // ======================= block assignment functions
- // ================================
-
- // Good Blocks means a good trade or no trade
- private void makeGoodBlocks(final Combat combat) {
-
- List currentAttackers = new ArrayList(attackersLeft);
-
- for (final Card attacker : attackersLeft) {
-
- if (attacker.hasStartOfKeyword("CantBeBlockedByAmount LT")
- || attacker.hasKeyword("CARDNAME can't be blocked unless " +
- "all creatures defending player controls block it.")) {
- continue;
- }
-
- Card blocker = null;
-
- final List blockers = getPossibleBlockers(combat, attacker, blockersLeft, true);
-
- final List safeBlockers = getSafeBlockers(combat, attacker, blockers);
- List killingBlockers;
-
- if (safeBlockers.size() > 0) {
- // 1.Blockers that can destroy the attacker but won't get
- // destroyed
- killingBlockers = getKillingBlockers(combat, attacker, safeBlockers);
- if (!killingBlockers.isEmpty()) {
- blocker = ComputerUtilCard.getWorstCreatureAI(killingBlockers);
- } else if (!attacker.hasKeyword("You may have CARDNAME assign its combat damage as though it weren't blocked.")) {
- blocker = ComputerUtilCard.getWorstCreatureAI(safeBlockers);
- blockedButUnkilled.add(attacker);
- }
- } // no safe blockers
- else {
- // 3.Blockers that can destroy the attacker and have an upside when dying
- killingBlockers = getKillingBlockers(combat, attacker, blockers);
- for (Card b : killingBlockers) {
- if ((b.hasKeyword("Undying") && b.getCounters(CounterType.P1P1) == 0)
- || !b.getSVar("SacMe").equals("")) {
- blocker = b;
- break;
- }
- }
- // 4.Blockers that can destroy the attacker and are worth less
- if (blocker == null && !killingBlockers.isEmpty()) {
- final Card worst = ComputerUtilCard.getWorstCreatureAI(killingBlockers);
- int value = ComputerUtilCard.evaluateCreature(attacker);
-
- // check for triggers when unblocked
- for (Trigger trigger : attacker.getTriggers()) {
- final HashMap trigParams = trigger.getMapParams();
- TriggerType mode = trigger.getMode();
-
- if (!trigger.requirementsCheck(attacker.getGame())) {
- continue;
- }
-
- if (mode == TriggerType.DamageDone) {
- if ((!trigParams.containsKey("ValidSource")
- || TriggerReplacementBase.matchesValid(attacker, trigParams.get("ValidSource").split(","), attacker))
- && attacker.getNetCombatDamage() > 0
- && (!trigParams.containsKey("ValidTarget")
- || TriggerReplacementBase.matchesValid(combat.getDefenderByAttacker(attacker), trigParams.get("ValidTarget").split(","), attacker))) {
- value += 50;
- }
- } else if (mode == TriggerType.AttackerUnblocked) {
- if (TriggerReplacementBase.matchesValid(attacker, trigParams.get("ValidCard").split(","), attacker)) {
- value += 50;
- }
- }
- }
-
- if ((ComputerUtilCard.evaluateCreature(worst) + diff) < value) {
- blocker = worst;
- }
- }
- }
- if (blocker != null) {
- currentAttackers.remove(attacker);
- combat.addBlocker(attacker, blocker);
- }
- }
- attackersLeft = (new ArrayList(currentAttackers));
- }
-
- // Good Gang Blocks means a good trade or no trade
- /**
- *
- * makeGangBlocks.
- *
- *
- * @param combat
- * a {@link forge.game.combat.Combat} object.
- * @return a {@link forge.game.combat.Combat} object.
- */
- static final Predicate rampagesOrNeedsManyToBlock = Predicates.or(CardPredicates.containsKeyword("Rampage"), CardPredicates.containsKeyword("CantBeBlockedByAmount GT"));
-
- private void makeGangBlocks(final Combat combat) {
- List currentAttackers = CardLists.filter(attackersLeft, Predicates.not(rampagesOrNeedsManyToBlock));
- List blockers;
-
- // Try to block an attacker without first strike with a gang of first strikers
- for (final Card attacker : attackersLeft) {
- if (!attacker.hasKeyword("First Strike") && !attacker.hasKeyword("Double Strike")) {
- blockers = getPossibleBlockers(combat, attacker, blockersLeft, false);
- final List firstStrikeBlockers = new ArrayList();
- final List blockGang = new ArrayList();
- for (int i = 0; i < blockers.size(); i++) {
- if (blockers.get(i).hasFirstStrike() || blockers.get(i).hasDoubleStrike()) {
- firstStrikeBlockers.add(blockers.get(i));
- }
- }
-
- if (firstStrikeBlockers.size() > 1) {
- CardLists.sortByPowerDesc(firstStrikeBlockers);
- for (final Card blocker : firstStrikeBlockers) {
- final int damageNeeded = ComputerUtilCombat.getDamageToKill(attacker)
- + ComputerUtilCombat.predictToughnessBonusOfAttacker(attacker, blocker, combat, false);
- // if the total damage of the blockgang was not enough
- // without but is enough with this blocker finish the
- // blockgang
- if (ComputerUtilCombat.totalDamageOfBlockers(attacker, blockGang) < damageNeeded
- || CombatUtil.needsBlockers(attacker) > blockGang.size()) {
- blockGang.add(blocker);
- if (ComputerUtilCombat.totalDamageOfBlockers(attacker, blockGang) >= damageNeeded) {
- currentAttackers.remove(attacker);
- for (final Card b : blockGang) {
- if (CombatUtil.canBlock(attacker, blocker, combat)) {
- combat.addBlocker(attacker, b);
- }
- }
- }
- }
- }
- }
- }
- }
-
- attackersLeft = (new ArrayList(currentAttackers));
- currentAttackers = new ArrayList(attackersLeft);
-
- // Try to block an attacker with two blockers of which only one will die
- for (final Card attacker : attackersLeft) {
- blockers = getPossibleBlockers(combat, attacker, blockersLeft, false);
- List usableBlockers;
- final List blockGang = new ArrayList();
- int absorbedDamage = 0; // The amount of damage needed to kill the first blocker
- int currentValue = 0; // The value of the creatures in the blockgang
-
- // AI can't handle good triple blocks yet
- if (CombatUtil.needsBlockers(attacker) > 2) {
- continue;
- }
-
- // Try to add blockers that could be destroyed, but are worth less than the attacker
- // Don't use blockers without First Strike or Double Strike if attacker has it
- usableBlockers = CardLists.filter(blockers, new Predicate() {
- @Override
- public boolean apply(final Card c) {
- if ((attacker.hasKeyword("First Strike") || attacker.hasKeyword("Double Strike"))
- && !(c.hasKeyword("First Strike") || c.hasKeyword("Double Strike"))) {
- return false;
- }
- return lifeInDanger || (ComputerUtilCard.evaluateCreature(c) + diff) < ComputerUtilCard.evaluateCreature(attacker);
- }
- });
- if (usableBlockers.size() < 2) {
- return;
- }
-
- final Card leader = ComputerUtilCard.getBestCreatureAI(usableBlockers);
- blockGang.add(leader);
- usableBlockers.remove(leader);
- absorbedDamage = ComputerUtilCombat.getEnoughDamageToKill(leader, attacker.getNetCombatDamage(), attacker, true);
- currentValue = ComputerUtilCard.evaluateCreature(leader);
-
- for (final Card blocker : usableBlockers) {
- // Add an additional blocker if the current blockers are not
- // enough and the new one would deal the remaining damage
- final int currentDamage = ComputerUtilCombat.totalDamageOfBlockers(attacker, blockGang);
- final int additionalDamage = ComputerUtilCombat.dealsDamageAsBlocker(attacker, blocker);
- final int absorbedDamage2 = ComputerUtilCombat.getEnoughDamageToKill(blocker, attacker.getNetCombatDamage(), attacker, true);
- final int addedValue = ComputerUtilCard.evaluateCreature(blocker);
- final int damageNeeded = ComputerUtilCombat.getDamageToKill(attacker)
- + ComputerUtilCombat.predictToughnessBonusOfAttacker(attacker, blocker, combat, false);
- if ((damageNeeded > currentDamage || CombatUtil.needsBlockers(attacker) > blockGang.size())
- && !(damageNeeded > currentDamage + additionalDamage)
- // The attacker will be killed
- && (absorbedDamage2 + absorbedDamage > attacker.getNetCombatDamage()
- // only one blocker can be killed
- || currentValue + addedValue - 50 <= ComputerUtilCard.evaluateCreature(attacker)
- // or attacker is worth more
- || (lifeInDanger && ComputerUtilCombat.lifeInDanger(ai, combat)))
- // or life is in danger
- && CombatUtil.canBlock(attacker, blocker, combat)) {
- // this is needed for attackers that can't be blocked by
- // more than 1
- currentAttackers.remove(attacker);
- combat.addBlocker(attacker, blocker);
- if (CombatUtil.canBlock(attacker, leader, combat)) {
- combat.addBlocker(attacker, leader);
- }
- break;
- }
- }
- }
-
- attackersLeft = (new ArrayList(currentAttackers));
- }
-
- // Bad Trade Blocks (should only be made if life is in danger)
- /**
- *
- * makeTradeBlocks.
- *
- *
- * @param combat
- * a {@link forge.game.combat.Combat} object.
- * @return a {@link forge.game.combat.Combat} object.
- */
- private void makeTradeBlocks(final Combat combat) {
-
- List currentAttackers = new ArrayList(attackersLeft);
- List killingBlockers;
-
- for (final Card attacker : attackersLeft) {
-
- if (attacker.hasStartOfKeyword("CantBeBlockedByAmount LT")
- || attacker.hasKeyword("CARDNAME can't be blocked unless " +
- "all creatures defending player controls block it.")) {
- continue;
- }
-
- List possibleBlockers = getPossibleBlockers(combat, attacker, blockersLeft, true);
- killingBlockers = getKillingBlockers(combat, attacker, possibleBlockers);
- if (!killingBlockers.isEmpty() && ComputerUtilCombat.lifeInDanger(ai, combat)) {
- final Card blocker = ComputerUtilCard.getWorstCreatureAI(killingBlockers);
- combat.addBlocker(attacker, blocker);
- currentAttackers.remove(attacker);
- }
- }
- attackersLeft = (new ArrayList(currentAttackers));
- }
-
- // Chump Blocks (should only be made if life is in danger)
- private void makeChumpBlocks(final Combat combat) {
-
- List currentAttackers = new ArrayList(attackersLeft);
-
- makeChumpBlocks(combat, currentAttackers);
-
- if (ComputerUtilCombat.lifeInDanger(ai, combat)) {
- makeMultiChumpBlocks(combat);
- }
- }
-
- private void makeChumpBlocks(final Combat combat, List attackers) {
-
- if (attackers.isEmpty() || !ComputerUtilCombat.lifeInDanger(ai, combat)) {
- return;
- }
-
- Card attacker = attackers.get(0);
-
- if (attacker.hasStartOfKeyword("CantBeBlockedByAmount LT") || attacker.hasKeyword("You may have CARDNAME assign its combat damage as though it weren't blocked.")
- || attacker.hasKeyword("CARDNAME can't be blocked unless all creatures defending player controls block it.")) {
- attackers.remove(0);
- makeChumpBlocks(combat, attackers);
- return;
- }
-
- List chumpBlockers = getPossibleBlockers(combat, attacker, blockersLeft, true);
- if (!chumpBlockers.isEmpty()) {
- final Card blocker = ComputerUtilCard.getWorstCreatureAI(chumpBlockers);
-
- // check if it's better to block a creature with lower power and without trample
- if (attacker.hasKeyword("Trample")) {
- final int damageAbsorbed = blocker.getLethalDamage();
- if (attacker.getNetCombatDamage() > damageAbsorbed) {
- for (Card other : attackers) {
- if (other.equals(attacker)) {
- continue;
- }
- if (other.getNetCombatDamage() >= damageAbsorbed
- && !other.hasKeyword("Trample")
- && CombatUtil.canBlock(other, blocker, combat)) {
- combat.addBlocker(other, blocker);
- attackersLeft.remove(other);
- blockedButUnkilled.add(other);
- attackers.remove(other);
- makeChumpBlocks(combat, attackers);
- return;
- }
- }
- }
- }
-
- combat.addBlocker(attacker, blocker);
- attackersLeft.remove(attacker);
- blockedButUnkilled.add(attacker);
- }
- attackers.remove(0);
- makeChumpBlocks(combat, attackers);
- }
-
- // Block creatures with "can't be blocked except by two or more creatures"
- private void makeMultiChumpBlocks(final Combat combat) {
-
- List currentAttackers = new ArrayList(attackersLeft);
-
- for (final Card attacker : currentAttackers) {
-
- if (!attacker.hasStartOfKeyword("CantBeBlockedByAmount LT")
- && !attacker.hasKeyword("CARDNAME can't be blocked unless all creatures defending player controls block it.")) {
- continue;
- }
- List possibleBlockers = getPossibleBlockers(combat, attacker, blockersLeft, true);
- if (!CombatUtil.canAttackerBeBlockedWithAmount(attacker, possibleBlockers.size(), combat)) {
- continue;
- }
- List usedBlockers = new ArrayList();
- for (Card blocker : possibleBlockers) {
- if (CombatUtil.canBlock(attacker, blocker, combat)) {
- combat.addBlocker(attacker, blocker);
- usedBlockers.add(blocker);
- if (CombatUtil.canAttackerBeBlockedWithAmount(attacker, usedBlockers.size(), combat)) {
- break;
- }
- }
- }
- if (CombatUtil.canAttackerBeBlockedWithAmount(attacker, usedBlockers.size(), combat)) {
- attackersLeft.remove(attacker);
- } else {
- for (Card blocker : usedBlockers) {
- combat.removeBlockAssignment(attacker, blocker);
- }
- }
- }
- }
-
- /** Reinforce blockers blocking attackers with trample (should only be made if life is in danger) */
- private void reinforceBlockersAgainstTrample(final Combat combat) {
-
- List chumpBlockers;
-
- List tramplingAttackers = CardLists.getKeyword(attackers, "Trample");
- tramplingAttackers = CardLists.filter(tramplingAttackers, Predicates.not(rampagesOrNeedsManyToBlock));
-
- // TODO - should check here for a "rampage-like" trigger that replaced
- // the keyword:
- // "Whenever CARDNAME becomes blocked, it gets +1/+1 until end of turn for each creature blocking it."
-
- for (final Card attacker : tramplingAttackers) {
-
- if ((attacker.hasStartOfKeyword("CantBeBlockedByAmount LT") && !combat.isBlocked(attacker))
- || attacker.hasKeyword("You may have CARDNAME assign its combat damage as though it weren't blocked.")
- || attacker.hasKeyword("CARDNAME can't be blocked unless all creatures defending player controls block it.")) {
- continue;
- }
-
- chumpBlockers = getPossibleBlockers(combat, attacker, blockersLeft, false);
- chumpBlockers.removeAll(combat.getBlockers(attacker));
- for (final Card blocker : chumpBlockers) {
- // Add an additional blocker if the current blockers are not
- // enough and the new one would suck some of the damage
- if (ComputerUtilCombat.getAttack(attacker) > ComputerUtilCombat.totalShieldDamage(attacker, combat.getBlockers(attacker))
- && ComputerUtilCombat.shieldDamage(attacker, blocker) > 0
- && CombatUtil.canBlock(attacker, blocker, combat) && ComputerUtilCombat.lifeInDanger(ai, combat)) {
- combat.addBlocker(attacker, blocker);
- }
- }
- }
- }
-
- /** Support blockers not destroying the attacker with more blockers to try to kill the attacker */
- private void reinforceBlockersToKill(final Combat combat) {
-
- List safeBlockers;
- List blockers;
-
- List targetAttackers = CardLists.filter(blockedButUnkilled, Predicates.not(rampagesOrNeedsManyToBlock));
-
- // TODO - should check here for a "rampage-like" trigger that replaced
- // the keyword:
- // "Whenever CARDNAME becomes blocked, it gets +1/+1 until end of turn for each creature blocking it."
-
- for (final Card attacker : targetAttackers) {
- blockers = getPossibleBlockers(combat, attacker, blockersLeft, false);
- blockers.removeAll(combat.getBlockers(attacker));
-
- // Try to use safe blockers first
- safeBlockers = getSafeBlockers(combat, attacker, blockers);
- for (final Card blocker : safeBlockers) {
- final int damageNeeded = ComputerUtilCombat.getDamageToKill(attacker)
- + ComputerUtilCombat.predictToughnessBonusOfAttacker(attacker, blocker, combat, false);
- // Add an additional blocker if the current blockers are not
- // enough and the new one would deal additional damage
- if ((damageNeeded > ComputerUtilCombat.totalDamageOfBlockers(attacker, combat.getBlockers(attacker)))
- && ComputerUtilCombat.dealsDamageAsBlocker(attacker, blocker) > 0
- && CombatUtil.canBlock(attacker, blocker, combat)) {
- combat.addBlocker(attacker, blocker);
- }
- blockers.remove(blocker); // Don't check them again next
- }
-
- // Try to add blockers that could be destroyed, but are worth less
- // than the attacker
- // Don't use blockers without First Strike or Double Strike if
- // attacker has it
- if (attacker.hasKeyword("First Strike") || attacker.hasKeyword("Double Strike")) {
- safeBlockers = CardLists.getKeyword(blockers, "First Strike");
- safeBlockers.addAll(CardLists.getKeyword(blockers, "Double Strike"));
- } else {
- safeBlockers = new ArrayList(blockers);
- }
-
- for (final Card blocker : safeBlockers) {
- final int damageNeeded = ComputerUtilCombat.getDamageToKill(attacker)
- + ComputerUtilCombat.predictToughnessBonusOfAttacker(attacker, blocker, combat, false);
- // Add an additional blocker if the current blockers are not
- // enough and the new one would deal the remaining damage
- final int currentDamage = ComputerUtilCombat.totalDamageOfBlockers(attacker, combat.getBlockers(attacker));
- final int additionalDamage = ComputerUtilCombat.dealsDamageAsBlocker(attacker, blocker);
- if ((damageNeeded > currentDamage)
- && !(damageNeeded > (currentDamage + additionalDamage))
- && ((ComputerUtilCard.evaluateCreature(blocker) + diff) < ComputerUtilCard
- .evaluateCreature(attacker)) && CombatUtil.canBlock(attacker, blocker, combat)) {
- combat.addBlocker(attacker, blocker);
- blockersLeft.remove(blocker);
- }
- }
- }
- }
-
- private void clearBlockers(final Combat combat, final List possibleBlockers) {
-
- final List oldBlockers = combat.getAllBlockers();
- for (final Card blocker : oldBlockers) {
- if ( blocker.getController() == ai ) // don't touch other player's blockers
- combat.removeFromCombat(blocker);
- }
-
- attackersLeft = new ArrayList(attackers); // keeps track of all currently unblocked attackers
- blockersLeft = new ArrayList(possibleBlockers); // keeps track of all unassigned blockers
- blockedButUnkilled = new ArrayList(); // keeps track of all blocked attackers that currently wouldn't be destroyed
- }
-
- /** Assigns blockers for the provided combat instance (in favor of player passes to ctor) */
- public void assignBlockers(final Combat combat) {
-
- final List possibleBlockers = ai.getCreaturesInPlay();
-
- attackers = sortPotentialAttackers(combat);
-
- if (attackers.isEmpty()) {
- return;
- }
-
- clearBlockers(combat, possibleBlockers);
-
- List blockers;
- List chumpBlockers;
-
- diff = (ai.getLife() * 2) - 5; // This is the minimal gain for an unnecessary trade
-
- // remove all attackers that can't be blocked anyway
- for (final Card a : attackers) {
- if (!CombatUtil.canBeBlocked(a, ai)) {
- attackersLeft.remove(a);
- }
- }
-
- // remove all blockers that can't block anyway
- for (final Card b : possibleBlockers) {
- if (!CombatUtil.canBlock(b, combat)) {
- blockersLeft.remove(b);
- }
- }
-
- if (attackersLeft.isEmpty()) {
- return;
- }
-
- // Begin with the weakest blockers
- CardLists.sortByPowerAsc(blockersLeft);
-
- // == 1. choose best blocks first ==
- makeGoodBlocks(combat);
- makeGangBlocks(combat);
- if (ComputerUtilCombat.lifeInDanger(ai, combat)) {
- makeTradeBlocks(combat); // choose necessary trade blocks
- }
- // if life is in danger
- if (ComputerUtilCombat.lifeInDanger(ai, combat)) {
- makeChumpBlocks(combat); // choose necessary chump blocks
- }
- // if life is still in danger
- // Reinforce blockers blocking attackers with trample if life is still
- // in danger
- if (ComputerUtilCombat.lifeInDanger(ai, combat)) {
- reinforceBlockersAgainstTrample(combat);
- }
- // Support blockers not destroying the attacker with more blockers to
- // try to kill the attacker
- if (!ComputerUtilCombat.lifeInDanger(ai, combat)) {
- reinforceBlockersToKill(combat);
- }
-
- // == 2. If the AI life would still be in danger make a safer approach ==
- if (ComputerUtilCombat.lifeInDanger(ai, combat)) {
- lifeInDanger = true;
- clearBlockers(combat, possibleBlockers); // reset every block assignment
- makeTradeBlocks(combat); // choose necessary trade blocks
- // if life is in danger
- makeGoodBlocks(combat);
- // choose necessary chump blocks if life is still in danger
- if (ComputerUtilCombat.lifeInDanger(ai, combat)) {
- makeChumpBlocks(combat);
- }
- // Reinforce blockers blocking attackers with trample if life is
- // still in danger
- if (ComputerUtilCombat.lifeInDanger(ai, combat)) {
- reinforceBlockersAgainstTrample(combat);
- }
- makeGangBlocks(combat);
- reinforceBlockersToKill(combat);
- }
-
- // == 3. If the AI life would be in serious danger make an even safer approach ==
- if (lifeInDanger && ComputerUtilCombat.lifeInSeriousDanger(ai, combat)) {
- clearBlockers(combat, possibleBlockers); // reset every block assignment
- makeChumpBlocks(combat); // choose chump blocks
- if (ComputerUtilCombat.lifeInDanger(ai, combat)) {
- makeTradeBlocks(combat); // choose necessary trade
- }
-
- if (!ComputerUtilCombat.lifeInDanger(ai, combat)) {
- makeGoodBlocks(combat);
- }
- // Reinforce blockers blocking attackers with trample if life is
- // still in danger
- else {
- reinforceBlockersAgainstTrample(combat);
- }
- makeGangBlocks(combat);
- // Support blockers not destroying the attacker with more blockers
- // to try to kill the attacker
- reinforceBlockersToKill(combat);
- }
-
- // assign blockers that have to block
- chumpBlockers = CardLists.getKeyword(blockersLeft, "CARDNAME blocks each turn if able.");
- // if an attacker with lure attacks - all that can block
- for (final Card blocker : blockersLeft) {
- if (CombatUtil.mustBlockAnAttacker(blocker, combat)) {
- chumpBlockers.add(blocker);
- }
- }
- if (!chumpBlockers.isEmpty()) {
- CardLists.shuffle(attackers);
- for (final Card attacker : attackers) {
- blockers = getPossibleBlockers(combat, attacker, chumpBlockers, false);
- for (final Card blocker : blockers) {
- if (CombatUtil.canBlock(attacker, blocker, combat) && blockersLeft.contains(blocker)
- && (CombatUtil.mustBlockAnAttacker(blocker, combat)
- || blocker.hasKeyword("CARDNAME blocks each turn if able."))) {
- combat.addBlocker(attacker, blocker);
- if (blocker.getMustBlockCards() != null) {
- int mustBlockAmt = blocker.getMustBlockCards().size();
- List blockedSoFar = combat.getAttackersBlockedBy(blocker);
- boolean canBlockAnother = CombatUtil.canBlockMoreCreatures(blocker, blockedSoFar);
- if (!canBlockAnother || mustBlockAmt == blockedSoFar.size()) {
- blockersLeft.remove(blocker);
- }
- } else {
- blockersLeft.remove(blocker);
- }
- }
- }
- }
- }
- }
-
- public static List orderBlockers(Card attacker, List blockers) {
- // ordering of blockers, sort by evaluate, then try to kill the best
- int damage = attacker.getNetCombatDamage();
- CardLists.sortByEvaluateCreature(blockers);
- final List first = new ArrayList();
- final List last = new ArrayList();
- for (Card blocker : blockers) {
- int lethal = ComputerUtilCombat.getEnoughDamageToKill(blocker, damage, attacker, true);
- if (lethal > damage) {
- last.add(blocker);
- } else {
- first.add(blocker);
- damage -= lethal;
- }
- }
- first.addAll(last);
-
- // TODO: Take total damage, and attempt to maximize killing the greatest evaluation of creatures
- // It's probably generally better to kill the largest creature, but sometimes its better to kill a few smaller ones
-
- return first;
- }
-
- public static List orderAttackers(Card blocker, List attackers) {
- // This shouldn't really take trample into account, but otherwise should be pretty similar to orderBlockers
- // ordering of blockers, sort by evaluate, then try to kill the best
- int damage = blocker.getNetCombatDamage();
- CardLists.sortByEvaluateCreature(attackers);
- final List first = new ArrayList();
- final List last = new ArrayList();
- for (Card attacker : attackers) {
- int lethal = ComputerUtilCombat.getEnoughDamageToKill(attacker, damage, blocker, true);
- if (lethal > damage) {
- last.add(attacker);
- } else {
- first.add(attacker);
- damage -= lethal;
- }
- }
- first.addAll(last);
-
- // TODO: Take total damage, and attempt to maximize killing the greatest evaluation of creatures
- // It's probably generally better to kill the largest creature, but sometimes its better to kill a few smaller ones
-
- return first;
- }
-}
+/*
+ * Forge: Play Magic: the Gathering.
+ * Copyright (C) 2011 Forge Team
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package forge.ai;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+
+import forge.game.GameEntity;
+import forge.game.TriggerReplacementBase;
+import forge.game.card.Card;
+import forge.game.card.CardLists;
+import forge.game.card.CardPredicates;
+import forge.game.card.CounterType;
+import forge.game.combat.Combat;
+import forge.game.combat.CombatUtil;
+import forge.game.player.Player;
+import forge.game.trigger.Trigger;
+import forge.game.trigger.TriggerType;
+
+
+/**
+ *
+ * ComputerUtil_Block2 class.
+ *
+ *
+ * @author Forge
+ * @version $Id$
+ */
+public class AiBlockController {
+
+ private final Player ai;
+ /** Constant attackers. */
+ private List attackers = new ArrayList(); // all attackers
+ /** Constant attackersLeft. */
+ private List attackersLeft = new ArrayList(); // keeps track of
+ // all currently
+ // unblocked
+ // attackers
+ /** Constant blockedButUnkilled. */
+ private List blockedButUnkilled = new ArrayList(); // blocked
+ // attackers
+ // that
+ // currently
+ // wouldn't be
+ // destroyed
+ /** Constant blockersLeft. */
+ private List blockersLeft = new ArrayList(); // keeps track of all
+ // unassigned
+ // blockers
+ private int diff = 0;
+
+ private boolean lifeInDanger = false;
+ public AiBlockController(Player aiPlayer) {
+ this.ai = aiPlayer;
+ }
+
+ // finds the creatures able to block the attacker
+ private List getPossibleBlockers(final Combat combat, final Card attacker, final List blockersLeft, final boolean solo) {
+ final List blockers = new ArrayList();
+
+ for (final Card blocker : blockersLeft) {
+ // if the blocker can block a creature with lure it can't block a
+ // creature without
+ if (CombatUtil.canBlock(attacker, blocker, combat)) {
+ if (solo && blocker.hasKeyword("CARDNAME can't attack or block alone.")) {
+ continue;
+ }
+ blockers.add(blocker);
+ }
+ }
+
+ return blockers;
+ }
+
+ // finds blockers that won't be destroyed
+ private List getSafeBlockers(final Combat combat, final Card attacker, final List blockersLeft) {
+ final List blockers = new ArrayList();
+
+ for (final Card b : blockersLeft) {
+ if (!ComputerUtilCombat.canDestroyBlocker(ai, b, attacker, combat, false)) {
+ blockers.add(b);
+ }
+ }
+
+ return blockers;
+ }
+
+ // finds blockers that destroy the attacker
+ private List getKillingBlockers(final Combat combat, final Card attacker, final List blockersLeft) {
+ final List blockers = new ArrayList();
+
+ for (final Card b : blockersLeft) {
+ if (ComputerUtilCombat.canDestroyAttacker(ai, attacker, b, combat, false)) {
+ blockers.add(b);
+ }
+ }
+
+ return blockers;
+ }
+
+
+
+ private List> sortAttackerByDefender(final Combat combat) {
+ List defenders = combat.getDefenders();
+ final ArrayList> attackers = new ArrayList>(defenders.size());
+ for (GameEntity defender : defenders) {
+ attackers.add(combat.getAttackersOf(defender));
+ }
+ return attackers;
+ }
+
+ private List sortPotentialAttackers(final Combat combat) {
+ final List> attackerLists = sortAttackerByDefender(combat);
+ final List sortedAttackers = new ArrayList();
+ final List firstAttacker = attackerLists.get(0);
+
+ final List defenders = combat.getDefenders();
+
+
+ // Begin with the attackers that pose the biggest threat
+ CardLists.sortByEvaluateCreature(firstAttacker);
+ CardLists.sortByPowerDesc(firstAttacker);
+
+ // If I don't have any planeswalkers than sorting doesn't really matter
+ if (defenders.size() == 1) {
+ return firstAttacker;
+ }
+
+ final boolean bLifeInDanger = ComputerUtilCombat.lifeInDanger(ai, combat);
+
+ // TODO Add creatures attacking Planeswalkers in order of which we want
+ // to protect
+ // defend planeswalkers with more loyalty before planeswalkers with less
+ // loyalty
+ // if planeswalker will be too difficult to defend don't even bother
+ for (List attacker : attackerLists) {
+ // Begin with the attackers that pose the biggest threat
+ CardLists.sortByPowerDesc(attacker);
+ for (final Card c : attacker) {
+ sortedAttackers.add(c);
+ }
+ }
+
+ if (bLifeInDanger) {
+ // add creatures attacking the Player to the front of the list
+ for (final Card c : firstAttacker) {
+ sortedAttackers.add(0, c);
+ }
+
+ } else {
+ // add creatures attacking the Player to the back of the list
+ for (final Card c : firstAttacker) {
+ sortedAttackers.add(c);
+ }
+ }
+
+ return sortedAttackers;
+ }
+
+ // ======================= block assignment functions
+ // ================================
+
+ // Good Blocks means a good trade or no trade
+ private void makeGoodBlocks(final Combat combat) {
+
+ List currentAttackers = new ArrayList(attackersLeft);
+
+ for (final Card attacker : attackersLeft) {
+
+ if (attacker.hasStartOfKeyword("CantBeBlockedByAmount LT")
+ || attacker.hasKeyword("CARDNAME can't be blocked unless " +
+ "all creatures defending player controls block it.")) {
+ continue;
+ }
+
+ Card blocker = null;
+
+ final List blockers = getPossibleBlockers(combat, attacker, blockersLeft, true);
+
+ final List safeBlockers = getSafeBlockers(combat, attacker, blockers);
+ List killingBlockers;
+
+ if (safeBlockers.size() > 0) {
+ // 1.Blockers that can destroy the attacker but won't get
+ // destroyed
+ killingBlockers = getKillingBlockers(combat, attacker, safeBlockers);
+ if (!killingBlockers.isEmpty()) {
+ blocker = ComputerUtilCard.getWorstCreatureAI(killingBlockers);
+ } else if (!attacker.hasKeyword("You may have CARDNAME assign its combat damage as though it weren't blocked.")) {
+ blocker = ComputerUtilCard.getWorstCreatureAI(safeBlockers);
+ blockedButUnkilled.add(attacker);
+ }
+ } // no safe blockers
+ else {
+ // 3.Blockers that can destroy the attacker and have an upside when dying
+ killingBlockers = getKillingBlockers(combat, attacker, blockers);
+ for (Card b : killingBlockers) {
+ if ((b.hasKeyword("Undying") && b.getCounters(CounterType.P1P1) == 0)
+ || !b.getSVar("SacMe").equals("")) {
+ blocker = b;
+ break;
+ }
+ }
+ // 4.Blockers that can destroy the attacker and are worth less
+ if (blocker == null && !killingBlockers.isEmpty()) {
+ final Card worst = ComputerUtilCard.getWorstCreatureAI(killingBlockers);
+ int value = ComputerUtilCard.evaluateCreature(attacker);
+
+ // check for triggers when unblocked
+ for (Trigger trigger : attacker.getTriggers()) {
+ final HashMap trigParams = trigger.getMapParams();
+ TriggerType mode = trigger.getMode();
+
+ if (!trigger.requirementsCheck(attacker.getGame())) {
+ continue;
+ }
+
+ if (mode == TriggerType.DamageDone) {
+ if ((!trigParams.containsKey("ValidSource")
+ || TriggerReplacementBase.matchesValid(attacker, trigParams.get("ValidSource").split(","), attacker))
+ && attacker.getNetCombatDamage() > 0
+ && (!trigParams.containsKey("ValidTarget")
+ || TriggerReplacementBase.matchesValid(combat.getDefenderByAttacker(attacker), trigParams.get("ValidTarget").split(","), attacker))) {
+ value += 50;
+ }
+ } else if (mode == TriggerType.AttackerUnblocked) {
+ if (TriggerReplacementBase.matchesValid(attacker, trigParams.get("ValidCard").split(","), attacker)) {
+ value += 50;
+ }
+ }
+ }
+
+ if ((ComputerUtilCard.evaluateCreature(worst) + diff) < value) {
+ blocker = worst;
+ }
+ }
+ }
+ if (blocker != null) {
+ currentAttackers.remove(attacker);
+ combat.addBlocker(attacker, blocker);
+ }
+ }
+ attackersLeft = (new ArrayList(currentAttackers));
+ }
+
+ // Good Gang Blocks means a good trade or no trade
+ /**
+ *
+ * makeGangBlocks.
+ *
+ *
+ * @param combat
+ * a {@link forge.game.combat.Combat} object.
+ * @return a {@link forge.game.combat.Combat} object.
+ */
+ static final Predicate rampagesOrNeedsManyToBlock = Predicates.or(CardPredicates.containsKeyword("Rampage"), CardPredicates.containsKeyword("CantBeBlockedByAmount GT"));
+
+ private void makeGangBlocks(final Combat combat) {
+ List currentAttackers = CardLists.filter(attackersLeft, Predicates.not(rampagesOrNeedsManyToBlock));
+ List blockers;
+
+ // Try to block an attacker without first strike with a gang of first strikers
+ for (final Card attacker : attackersLeft) {
+ if (!attacker.hasKeyword("First Strike") && !attacker.hasKeyword("Double Strike")) {
+ blockers = getPossibleBlockers(combat, attacker, blockersLeft, false);
+ final List firstStrikeBlockers = new ArrayList();
+ final List blockGang = new ArrayList();
+ for (int i = 0; i < blockers.size(); i++) {
+ if (blockers.get(i).hasFirstStrike() || blockers.get(i).hasDoubleStrike()) {
+ firstStrikeBlockers.add(blockers.get(i));
+ }
+ }
+
+ if (firstStrikeBlockers.size() > 1) {
+ CardLists.sortByPowerDesc(firstStrikeBlockers);
+ for (final Card blocker : firstStrikeBlockers) {
+ final int damageNeeded = ComputerUtilCombat.getDamageToKill(attacker)
+ + ComputerUtilCombat.predictToughnessBonusOfAttacker(attacker, blocker, combat, false);
+ // if the total damage of the blockgang was not enough
+ // without but is enough with this blocker finish the
+ // blockgang
+ if (ComputerUtilCombat.totalDamageOfBlockers(attacker, blockGang) < damageNeeded
+ || CombatUtil.needsBlockers(attacker) > blockGang.size()) {
+ blockGang.add(blocker);
+ if (ComputerUtilCombat.totalDamageOfBlockers(attacker, blockGang) >= damageNeeded) {
+ currentAttackers.remove(attacker);
+ for (final Card b : blockGang) {
+ if (CombatUtil.canBlock(attacker, blocker, combat)) {
+ combat.addBlocker(attacker, b);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ attackersLeft = (new ArrayList(currentAttackers));
+ currentAttackers = new ArrayList(attackersLeft);
+
+ // Try to block an attacker with two blockers of which only one will die
+ for (final Card attacker : attackersLeft) {
+ blockers = getPossibleBlockers(combat, attacker, blockersLeft, false);
+ List usableBlockers;
+ final List blockGang = new ArrayList();
+ int absorbedDamage = 0; // The amount of damage needed to kill the first blocker
+ int currentValue = 0; // The value of the creatures in the blockgang
+
+ // AI can't handle good triple blocks yet
+ if (CombatUtil.needsBlockers(attacker) > 2) {
+ continue;
+ }
+
+ // Try to add blockers that could be destroyed, but are worth less than the attacker
+ // Don't use blockers without First Strike or Double Strike if attacker has it
+ usableBlockers = CardLists.filter(blockers, new Predicate() {
+ @Override
+ public boolean apply(final Card c) {
+ if ((attacker.hasKeyword("First Strike") || attacker.hasKeyword("Double Strike"))
+ && !(c.hasKeyword("First Strike") || c.hasKeyword("Double Strike"))) {
+ return false;
+ }
+ return lifeInDanger || (ComputerUtilCard.evaluateCreature(c) + diff) < ComputerUtilCard.evaluateCreature(attacker);
+ }
+ });
+ if (usableBlockers.size() < 2) {
+ return;
+ }
+
+ final Card leader = ComputerUtilCard.getBestCreatureAI(usableBlockers);
+ blockGang.add(leader);
+ usableBlockers.remove(leader);
+ absorbedDamage = ComputerUtilCombat.getEnoughDamageToKill(leader, attacker.getNetCombatDamage(), attacker, true);
+ currentValue = ComputerUtilCard.evaluateCreature(leader);
+
+ for (final Card blocker : usableBlockers) {
+ // Add an additional blocker if the current blockers are not
+ // enough and the new one would deal the remaining damage
+ final int currentDamage = ComputerUtilCombat.totalDamageOfBlockers(attacker, blockGang);
+ final int additionalDamage = ComputerUtilCombat.dealsDamageAsBlocker(attacker, blocker);
+ final int absorbedDamage2 = ComputerUtilCombat.getEnoughDamageToKill(blocker, attacker.getNetCombatDamage(), attacker, true);
+ final int addedValue = ComputerUtilCard.evaluateCreature(blocker);
+ final int damageNeeded = ComputerUtilCombat.getDamageToKill(attacker)
+ + ComputerUtilCombat.predictToughnessBonusOfAttacker(attacker, blocker, combat, false);
+ if ((damageNeeded > currentDamage || CombatUtil.needsBlockers(attacker) > blockGang.size())
+ && !(damageNeeded > currentDamage + additionalDamage)
+ // The attacker will be killed
+ && (absorbedDamage2 + absorbedDamage > attacker.getNetCombatDamage()
+ // only one blocker can be killed
+ || currentValue + addedValue - 50 <= ComputerUtilCard.evaluateCreature(attacker)
+ // or attacker is worth more
+ || (lifeInDanger && ComputerUtilCombat.lifeInDanger(ai, combat)))
+ // or life is in danger
+ && CombatUtil.canBlock(attacker, blocker, combat)) {
+ // this is needed for attackers that can't be blocked by
+ // more than 1
+ currentAttackers.remove(attacker);
+ combat.addBlocker(attacker, blocker);
+ if (CombatUtil.canBlock(attacker, leader, combat)) {
+ combat.addBlocker(attacker, leader);
+ }
+ break;
+ }
+ }
+ }
+
+ attackersLeft = (new ArrayList(currentAttackers));
+ }
+
+ // Bad Trade Blocks (should only be made if life is in danger)
+ /**
+ *
+ * makeTradeBlocks.
+ *
+ *
+ * @param combat
+ * a {@link forge.game.combat.Combat} object.
+ * @return a {@link forge.game.combat.Combat} object.
+ */
+ private void makeTradeBlocks(final Combat combat) {
+
+ List currentAttackers = new ArrayList(attackersLeft);
+ List killingBlockers;
+
+ for (final Card attacker : attackersLeft) {
+
+ if (attacker.hasStartOfKeyword("CantBeBlockedByAmount LT")
+ || attacker.hasKeyword("CARDNAME can't be blocked unless " +
+ "all creatures defending player controls block it.")) {
+ continue;
+ }
+
+ List possibleBlockers = getPossibleBlockers(combat, attacker, blockersLeft, true);
+ killingBlockers = getKillingBlockers(combat, attacker, possibleBlockers);
+ if (!killingBlockers.isEmpty() && ComputerUtilCombat.lifeInDanger(ai, combat)) {
+ final Card blocker = ComputerUtilCard.getWorstCreatureAI(killingBlockers);
+ combat.addBlocker(attacker, blocker);
+ currentAttackers.remove(attacker);
+ }
+ }
+ attackersLeft = (new ArrayList(currentAttackers));
+ }
+
+ // Chump Blocks (should only be made if life is in danger)
+ private void makeChumpBlocks(final Combat combat) {
+
+ List currentAttackers = new ArrayList