From 3fa0e8b8f4444c077085ad270feafcfd238d2719 Mon Sep 17 00:00:00 2001 From: BetaSteward Date: Wed, 1 Sep 2010 03:01:43 +0000 Subject: [PATCH] ... --- Mage.Client/config/config.properties | 9 +- Mage.Client/nbproject/project.properties | 3 +- Mage.Client/nbproject/project.xml | 1 + .../release/sample-decks/SanJuanSwartz.dck | Bin 0 -> 995 bytes Mage.Client/src/mage/client/MageFrame.java | 18 +- Mage.Client/src/mage/client/cards/Card.java | 47 +- .../src/mage/client/cards/CardDimensions.java | 26 +- .../src/mage/client/cards/CardGrid.form | 4 +- .../src/mage/client/cards/CardGrid.java | 86 ++- Mage.Client/src/mage/client/cards/Cards.java | 46 +- .../src/mage/client/cards/CardsList.form | 3 + .../src/mage/client/cards/CardsList.java | 9 +- .../src/mage/client/cards/Permanent.java | 34 +- .../src/mage/client/chat/ChatPanel.java | 21 +- .../mage/client/deckeditor/CardSelector.java | 25 +- .../client/deckeditor/DeckEditorPanel.java | 19 +- .../mage/client/dialog/ExileZoneDialog.form | 5 + .../mage/client/dialog/ExileZoneDialog.java | 27 +- .../mage/client/dialog/JoinTableDialog.form | 17 +- .../mage/client/dialog/JoinTableDialog.java | 17 +- .../src/mage/client/dialog/MageDialog.form | 2 +- .../src/mage/client/dialog/MageDialog.java | 2 + .../mage/client/dialog/NewTableDialog.java | 6 +- .../mage/client/dialog/PickNumberDialog.form | 53 +- .../mage/client/dialog/PickNumberDialog.java | 45 +- .../mage/client/dialog/ShowCardsDialog.java | 4 +- .../mage/client/game/BattlefieldPanel.java | 1 - .../src/mage/client/game/CombatGroup.form | 22 +- .../src/mage/client/game/CombatGroup.java | 22 +- .../src/mage/client/game/FeedbackPanel.java | 3 +- .../src/mage/client/game/GamePane.java | 29 + .../src/mage/client/game/GamePanel.form | 69 +- .../src/mage/client/game/GamePanel.java | 119 ++-- .../src/mage/client/game/PlayAreaPanel.form | 14 +- .../src/mage/client/game/PlayAreaPanel.java | 45 +- .../src/mage/client/remote/Client.java | 157 +++-- .../src/mage/client/remote/Session.java | 30 +- .../mage/client/table/TablePlayerPanel.java | 4 +- .../src/mage/client/table/TablesPanel.java | 2 - Mage.Common/src/mage/interfaces/Server.java | 9 +- .../src/mage/interfaces/ServerState.java | 69 ++ Mage.Common/src/mage/view/CardView.java | 2 +- Mage.Common/src/mage/view/CardsView.java | 29 +- Mage.Common/src/mage/view/ChatMessage.java | 12 +- .../src/mage/view/CombatGroupView.java | 15 +- Mage.Common/src/mage/view/ExileView.java | 7 +- Mage.Common/src/mage/view/GameView.java | 39 +- Mage.Common/src/mage/view/PlayerView.java | 17 +- .../src/mage/deck/Constructed.java | 4 +- .../src/mage/game/FreeForAll.java | 17 +- .../src/mage/game/TwoPlayerDuel.java | 11 +- Mage.Player.AI/nbproject/project.properties | 1 + .../src/mage/player/ai/ComputerPlayer.java | 131 ++-- .../config/AIMinimax.properties | 7 + .../nbproject/project.properties | 75 +++ Mage.Player.AIMinimax/nbproject/project.xml | 33 + .../src/mage/player/ai/Attackers.java | 52 ++ .../src/mage/player/ai/ComputerPlayer2.java | 631 ++++++++++++++++++ .../src/mage/player/ai/ComputerPlayer3.java | 540 +++++++++++++++ .../src/mage/player/ai/Config.java | 75 +++ .../mage/player/ai/GameStateEvaluator.java | 102 +++ .../mage/player/ai/SimulateBlockWorker.java | 62 ++ .../src/mage/player/ai/SimulatedAction.java | 68 ++ .../src/mage/player/ai/SimulatedPlayer.java | 250 +++++++ .../src/mage/player/ai/SimulationNode.java | 127 ++++ .../src/mage/player/ai/SimulationWorker.java | 69 ++ .../src/mage/player/human/HumanPlayer.java | 63 +- Mage.Server/config/config.xml | 2 + Mage.Server/nbproject/project.properties | 11 +- Mage.Server/nbproject/project.xml | 17 + Mage.Server/plugins/AIMinimax.properties | 7 + Mage.Server/plugins/Mage.Deck.Constructed.jar | Bin 2731 -> 2630 bytes Mage.Server/plugins/Mage.Game.FreeForAll.jar | Bin 4525 -> 5207 bytes .../plugins/Mage.Game.TwoPlayerDuel.jar | Bin 3663 -> 4411 bytes Mage.Server/plugins/Mage.Player.AI.jar | Bin 53183 -> 57060 bytes Mage.Server/plugins/Mage.Player.AIMinimax.jar | Bin 0 -> 70592 bytes Mage.Server/plugins/Mage.Player.Human.jar | Bin 18066 -> 19671 bytes Mage.Server/release/config/config.xml | 11 +- Mage.Server/src/mage/server/ChatManager.java | 5 +- Mage.Server/src/mage/server/ChatSession.java | 9 +- Mage.Server/src/mage/server/ServerImpl.java | 73 +- Mage.Server/src/mage/server/Session.java | 5 +- .../src/mage/server/game/GameController.java | 55 +- .../src/mage/server/game/GameManager.java | 4 +- .../src/mage/server/game/GameReplay.java | 9 +- .../src/mage/server/game/GameSession.java | 8 + .../src/mage/server/game/GamesRoom.java | 2 +- .../src/mage/server/game/GamesRoomImpl.java | 4 +- .../src/mage/server/game/PlayerFactory.java | 3 +- .../src/mage/server/game/ReplaySession.java | 17 +- .../src/mage/server/game/TableController.java | 26 +- .../src/mage/server/game/TableManager.java | 4 +- Mage.Sets/src/mage/sets/Magic2011.java | 102 +++ Mage.Sets/src/mage/sets/RiseOfTheEldrazi.java | 4 + Mage.Sets/src/mage/sets/Sets.java | 1 + Mage.Sets/src/mage/sets/Worldwake.java | 4 + Mage.Sets/src/mage/sets/Zendikar.java | 5 + .../mage/sets/alarareborn/BehemothSledge.java | 18 +- .../sets/alarareborn/BituminousBlast.java | 17 +- .../mage/sets/alarareborn/BloodbraidElf.java | 19 +- .../src/mage/sets/alarareborn/FinestHour.java | 48 +- .../mage/sets/alarareborn/MaelstromPulse.java | 17 +- .../mage/sets/alarareborn/PutridLeech.java | 17 +- .../alarareborn/SpellbreakerBehemoth.java | 17 +- .../src/mage/sets/alarareborn/Terminate.java | 17 +- .../sets/alarareborn/VengefulRebirth.java | 45 +- .../src/mage/sets/conflux/FontOfMythos.java | 30 +- .../mage/sets/conflux/HellsparkElemental.java | 21 +- .../sets/conflux/KnightOfTheReliquary.java | 37 +- .../src/mage/sets/conflux/MartialCoup.java | 41 +- .../src/mage/sets/conflux/NobleHierarch.java | 17 +- .../src/mage/sets/conflux/PathToExile.java | 34 +- .../src/mage/sets/conflux/QuenchableFire.java | 69 +- .../src/mage/sets/conflux/Thornling.java | 31 +- .../src/mage/sets/magic2010/AcidicSlime.java | 17 +- .../mage/sets/magic2010/AcolyteOfXathrid.java | 19 +- .../src/mage/sets/magic2010/ActOfTreason.java | 17 +- .../src/mage/sets/magic2010/AirElemental.java | 17 +- .../mage/sets/magic2010/AjaniGoldmane.java | 37 +- .../mage/sets/magic2010/AlluringSiren.java | 42 +- .../mage/sets/magic2010/AngelsFeather.java | 28 +- .../src/mage/sets/magic2010/AngelsMercy.java | 15 +- .../src/mage/sets/magic2010/AntQueen.java | 23 +- .../mage/sets/magic2010/ArmoredAscension.java | 38 +- .../src/mage/sets/magic2010/Assassinate.java | 17 +- .../mage/sets/magic2010/AwakenerDruid.java | 36 +- .../mage/sets/magic2010/BallLightning.java | 17 +- .../mage/sets/magic2010/BaneslayerAngel.java | 17 +- .../magic2010/BerserkersOfBloodRidge.java | 17 +- .../mage/sets/magic2010/BirdsOfParadise.java | 17 +- .../src/mage/sets/magic2010/BlackKnight.java | 17 +- .../src/mage/sets/magic2010/BlindingMage.java | 21 +- .../src/mage/sets/magic2010/BogWraith.java | 17 +- .../mage/sets/magic2010/BogardanHellkite.java | 18 +- Mage.Sets/src/mage/sets/magic2010/Cancel.java | 17 +- .../mage/sets/magic2010/CelestialPurge.java | 17 +- .../mage/sets/magic2010/DiabolicTutor.java | 18 +- .../src/mage/sets/magic2010/DoomBlade.java | 17 +- .../sets/magic2010/DragonskullSummit.java | 17 +- .../src/mage/sets/magic2010/Earthquake.java | 39 +- .../mage/sets/magic2010/EliteVanguard.java | 17 +- .../src/mage/sets/magic2010/Flashfreeze.java | 17 +- .../mage/sets/magic2010/GargoyleCastle.java | 28 +- .../sets/magic2010/GarrukWildspeaker.java | 19 +- .../src/mage/sets/magic2010/GiantGrowth.java | 17 +- .../mage/sets/magic2010/GlacialFortress.java | 17 +- .../mage/sets/magic2010/GreatSableStag.java | 17 +- .../mage/sets/magic2010/HonorOfThePure.java | 26 +- .../src/mage/sets/magic2010/HowlingMine.java | 30 +- .../src/mage/sets/magic2010/JaceBeleren.java | 17 +- .../mage/sets/magic2010/LightningBolt.java | 17 +- .../mage/sets/magic2010/LlanowarElves.java | 22 +- .../sets/magic2010/MasterOfTheWildHunt.java | 47 +- .../src/mage/sets/magic2010/MightOfOaks.java | 17 +- .../src/mage/sets/magic2010/MindRot.java | 17 +- .../src/mage/sets/magic2010/MindSpring.java | 37 +- .../src/mage/sets/magic2010/Naturalize.java | 17 +- Mage.Sets/src/mage/sets/magic2010/Negate.java | 16 +- .../src/mage/sets/magic2010/Overrun.java | 17 +- .../mage/sets/magic2010/RampantGrowth.java | 17 +- .../mage/sets/magic2010/RootboundCrag.java | 17 +- .../mage/sets/magic2010/RoyalAssassin.java | 29 +- .../src/mage/sets/magic2010/SafePassage.java | 17 +- .../mage/sets/magic2010/SunpetalGrove.java | 21 +- .../sets/magic2010/TerramorphicExpanse.java | 29 +- .../src/mage/sets/magic2010/TimeWarp.java | 33 +- .../src/mage/sets/magic2010/WhiteKnight.java | 16 +- .../src/mage/sets/magic2011/AcidicSlime.java | 45 ++ .../src/mage/sets/magic2011/ActOfTreason.java | 45 ++ .../src/mage/sets/magic2011/AetherAdept.java | 75 +++ .../src/mage/sets/magic2011/AirServant.java | 84 +++ .../mage/sets/magic2011/AjaniGoldmane.java | 45 ++ .../mage/sets/planechase/OblivionRing.java | 17 +- .../src/mage/sets/planechase/SoulWarden.java | 28 +- .../mage/sets/riseoftheeldrazi/Deprive.java | 16 +- .../riseoftheeldrazi/EmrakulsHatcher.java | 72 ++ .../sets/riseoftheeldrazi/EvolvingWilds.java | 91 +++ .../sets/riseoftheeldrazi/FlameSlash.java | 66 ++ .../sets/riseoftheeldrazi/GideonJura.java | 43 +- .../riseoftheeldrazi/JoragaTreespeaker.java | 115 ++++ .../riseoftheeldrazi/KarganDragonlord.java | 27 +- .../riseoftheeldrazi/KozileksPredator.java | 72 ++ .../sets/riseoftheeldrazi/NestInvader.java | 72 ++ .../mage/sets/riseoftheeldrazi/Vengevine.java | 152 +++++ .../sets/riseoftheeldrazi/WallOfOmens.java | 17 +- .../sets/shardsofalara/AjaniVengeant.java | 19 +- .../mage/sets/shardsofalara/Angelsong.java | 21 +- .../mage/sets/shardsofalara/Blightning.java | 17 +- .../sets/shardsofalara/BroodmateDragon.java | 17 +- .../shardsofalara/CrumblingNecropolis.java | 17 +- .../shardsofalara/ElspethKnightErrant.java | 23 +- .../mage/sets/shardsofalara/HellsThunder.java | 22 +- .../mage/sets/shardsofalara/JundPanorama.java | 29 +- .../shardsofalara/KnightOfTheWhiteOrchid.java | 28 +- .../sets/shardsofalara/RafiqOfTheMany.java | 30 +- .../mage/sets/shardsofalara/RangerOfEos.java | 17 +- .../mage/sets/shardsofalara/RhoxWarMonk.java | 17 +- .../mage/sets/shardsofalara/SarkhanVol.java | 21 +- .../mage/sets/shardsofalara/SavageLands.java | 17 +- .../sets/shardsofalara/SproutingThrinax.java | 17 +- .../mage/sets/shardsofalara/WildNacatl.java | 17 +- .../sets/shardsofalara/WoollyThoctar.java | 17 +- Mage.Sets/src/mage/sets/tenth/Forest1.java | 17 +- Mage.Sets/src/mage/sets/tenth/Island1.java | 17 +- Mage.Sets/src/mage/sets/tenth/Mountain1.java | 17 +- Mage.Sets/src/mage/sets/tenth/Plains1.java | 17 +- Mage.Sets/src/mage/sets/tenth/Swamp1.java | 17 +- .../src/mage/sets/worldwake/ArborElf.java | 84 +++ .../mage/sets/worldwake/BasiliskCollar.java | 17 +- .../sets/worldwake/CelestialColonnade.java | 21 +- .../mage/sets/worldwake/DreadStatuary.java | 83 +++ .../sets/worldwake/EverflowingChalice.java | 50 +- .../sets/worldwake/JaceTheMindSculptor.java | 87 ++- .../src/mage/sets/worldwake/KhalniGarden.java | 84 +++ .../mage/sets/worldwake/LavaclawReaches.java | 24 +- .../src/mage/sets/worldwake/RagingRavine.java | 21 +- .../src/mage/sets/worldwake/SearingBlaze.java | 58 +- .../src/mage/sets/worldwake/SejiriSteppe.java | 17 +- .../mage/sets/worldwake/StirringWildwood.java | 21 +- .../mage/sets/worldwake/StoneforgeMystic.java | 21 +- .../src/mage/sets/worldwake/TectonicEdge.java | 34 +- .../sets/worldwake/WolfbriarElemental.java | 75 +++ .../mage/sets/zendikar/AdventuringGear.java | 70 ++ .../src/mage/sets/zendikar/AetherFigment.java | 73 ++ .../src/mage/sets/zendikar/ArchiveTrap.java | 50 +- .../src/mage/sets/zendikar/AridMesa.java | 17 +- .../sets/zendikar/BeastmasterAscension.java | 140 ++++ .../mage/sets/zendikar/BraveTheElements.java | 35 +- .../mage/sets/zendikar/BurstLightning.java | 17 +- .../mage/sets/zendikar/ConquerorsPledge.java | 17 +- .../src/mage/sets/zendikar/DayOfJudgment.java | 17 +- .../mage/sets/zendikar/EldraziMonument.java | 131 ++++ .../src/mage/sets/zendikar/EmeriaAngel.java | 17 +- .../src/mage/sets/zendikar/GoblinGuide.java | 37 +- .../mage/sets/zendikar/GoblinRuinblaster.java | 19 +- .../mage/sets/zendikar/KabiraCrossroads.java | 17 +- .../src/mage/sets/zendikar/LotusCobra.java | 17 +- .../src/mage/sets/zendikar/MarshFlats.java | 17 +- .../mage/sets/zendikar/MistyRainforest.java | 17 +- .../sets/zendikar/OranRiefTheVastwood.java | 36 +- .../mage/sets/zendikar/RampagingBaloths.java | 17 +- .../src/mage/sets/zendikar/RiverBoa.java | 82 +++ .../src/mage/sets/zendikar/ScaldingTarn.java | 17 +- .../src/mage/sets/zendikar/ScuteMob.java | 28 +- .../src/mage/sets/zendikar/SpreadingSeas.java | 38 +- .../src/mage/sets/zendikar/SteppeLynx.java | 17 +- .../sets/zendikar/SunspringExpedition.java | 26 +- .../mage/sets/zendikar/TeeteringPeaks.java | 17 +- .../mage/sets/zendikar/VerdantCatacombs.java | 17 +- Mage/src/mage/abilities/Abilities.java | 29 +- Mage/src/mage/abilities/AbilitiesImpl.java | 92 ++- Mage/src/mage/abilities/Ability.java | 8 +- Mage/src/mage/abilities/AbilityImpl.java | 136 ++-- .../mage/abilities/ActivatedAbilityImpl.java | 30 +- .../abilities/DelayedTriggeredAbilities.java | 32 +- .../abilities/DelayedTriggeredAbility.java | 8 +- Mage/src/mage/abilities/EvasionAbility.java | 2 +- .../mage/abilities/EvasionAbilityImpl.java | 9 +- Mage/src/mage/abilities/LoyaltyAbility.java | 11 +- Mage/src/mage/abilities/PlayLandAbility.java | 25 +- Mage/src/mage/abilities/SpecialAction.java | 10 +- Mage/src/mage/abilities/SpecialActions.java | 13 +- Mage/src/mage/abilities/SpellAbility.java | 25 +- Mage/src/mage/abilities/StaticAbility.java | 13 +- .../mage/abilities/TriggeredAbilities.java | 14 +- .../mage/abilities/TriggeredAbilityImpl.java | 10 +- .../ActivateOncePerTurnActivatedAbility.java | 12 +- .../common/AttacksEachTurnStaticAbility.java | 31 +- .../common/AttacksTriggeredAbility.java | 11 +- .../abilities/common/CantCounterAbility.java | 11 +- .../common/CreateTokenActivatedAbility.java | 11 +- .../EntersBattlefieldStaticAbility.java | 11 +- .../EntersBattlefieldTriggeredAbility.java | 15 +- .../common/FetchLandActivatedAbility.java | 11 +- .../abilities/common/LandfallAbility.java | 11 +- .../LeavesBattlefieldTriggeredAbility.java | 11 +- .../common/OnEventTriggeredAbility.java | 14 +- .../mage/abilities/common/PassAbility.java | 59 ++ ...oGraveFromBattlefieldTriggeredAbility.java | 11 +- .../common/SimpleActivatedAbility.java | 11 +- .../abilities/common/SimpleStaticAbility.java | 11 +- .../common/SimpleTriggeredAbility.java | 12 +- .../mage/abilities/costs/AlternativeCost.java | 10 +- Mage/src/mage/abilities/costs/Cost.java | 10 +- Mage/src/mage/abilities/costs/CostImpl.java | 31 +- Mage/src/mage/abilities/costs/Costs.java | 5 +- Mage/src/mage/abilities/costs/CostsImpl.java | 55 +- .../costs/common/DiscardSourceCost.java | 27 +- .../abilities/costs/common/PayLifeCost.java | 25 +- .../costs/common/PayLoyaltyCost.java | 22 +- .../common/RemoveCountersSourceCost.java | 28 +- .../costs/common/ReturnToHandTargetCost.java | 26 +- .../costs/common/SacrificeSourceCost.java | 26 +- .../costs/common/SacrificeTargetCost.java | 26 +- .../abilities/costs/common/TapSourceCost.java | 26 +- .../abilities/costs/mana/ColoredManaCost.java | 12 +- .../abilities/costs/mana/GenericManaCost.java | 12 +- .../abilities/costs/mana/HybridManaCost.java | 13 +- .../mage/abilities/costs/mana/ManaCost.java | 4 +- .../abilities/costs/mana/ManaCostImpl.java | 25 +- .../mage/abilities/costs/mana/ManaCosts.java | 217 +----- .../abilities/costs/mana/ManaCostsImpl.java | 299 +++++++++ .../costs/mana/MonoHybridManaCost.java | 13 +- .../costs/mana/VariableManaCost.java | 13 +- .../effects/ApplyCountersEffect.java | 71 ++ .../abilities/effects/ContinuousEffect.java | 5 +- .../effects/ContinuousEffectImpl.java | 21 +- .../abilities/effects/ContinuousEffects.java | 145 ++-- Mage/src/mage/abilities/effects/Effect.java | 13 +- .../mage/abilities/effects/EffectImpl.java | 35 +- Mage/src/mage/abilities/effects/Effects.java | 53 +- .../effects/EntersBattlefieldEffect.java | 39 +- .../mage/abilities/effects/OneShotEffect.java | 6 +- .../abilities/effects/PreventionEffect.java | 2 +- .../effects/PreventionEffectImpl.java | 10 +- .../abilities/effects/ReplacementEffect.java | 7 +- .../effects/ReplacementEffectImpl.java | 7 +- .../effects/RequirementAttackEffect.java | 13 +- .../effects/RequirementBlockEffect.java | 13 +- .../mage/abilities/effects/SearchEffect.java | 55 ++ .../WhileControlsContinuousEffect.java | 16 +- .../common/AddCountersSourceEffect.java | 20 +- .../common/AddManaOfAnyColorEffect.java | 21 +- .../AddPlusOneCountersControlledEffect.java | 19 +- .../AddPlusOneCountersSourceEffect.java | 19 +- .../effects/common/AttachEffect.java | 14 +- .../BecomesCreatureSourceEOTEffect.java | 92 +-- .../effects/common/BoostControlledEffect.java | 22 +- .../effects/common/BoostEquippedEffect.java | 34 +- .../BoostPowerSourceVariableEffect.java | 20 +- .../effects/common/BoostSourceEffect.java | 20 +- .../BoostSourceWhileControlsEffect.java | 20 +- .../effects/common/BoostTargetEffect.java | 22 +- .../common/CantCounterControlledEffect.java | 23 +- .../common/CantCounterSourceEffect.java | 20 +- .../effects/common/CounterTargetEffect.java | 22 +- .../CreateDelayedTriggeredAbilityEffect.java | 23 +- .../common/CreateSpecialActionEffect.java | 23 +- .../effects/common/CreateTokenEffect.java | 36 +- .../effects/common/DamageMultiEffect.java | 28 +- .../effects/common/DamageTargetEffect.java | 27 +- .../DestroyAllControlledTargetEffect.java | 21 +- .../effects/common/DestroyAllEffect.java | 19 +- .../DestroyAllNamedPermanentsEffect.java | 26 +- .../common/DestroyNoRegenTargetEffect.java | 23 +- .../effects/common/DestroyTargetEffect.java | 21 +- .../effects/common/DiscardTargetEffect.java | 19 +- .../effects/common/DrawCardAllEffect.java | 17 +- .../common/DrawCardControllerEffect.java | 19 +- .../effects/common/DrawCardTargetEffect.java | 19 +- .../common/EntersBattlefieldTappedEffect.java | 12 +- ...BattlefieldTappedUnlessControlsEffect.java | 21 +- .../effects/common/ExileSourceEffect.java | 18 +- .../effects/common/ExileSpellEffect.java | 12 +- .../effects/common/ExileTargetEffect.java | 22 +- .../common/GainAbilityAttachedEffect.java | 19 +- .../common/GainAbilityControlledEffect.java | 21 +- .../common/GainAbilitySourceEffect.java | 19 +- .../common/GainAbilityTargetEffect.java | 19 +- .../common/GainControlTargetEOTEffect.java | 20 +- .../effects/common/GainLifeEffect.java | 19 +- ...ainProtectionFromColorTargetEOTEffect.java | 17 +- .../effects/common/LoseLifeTargetEffect.java | 21 +- .../abilities/effects/common/ManaEffect.java | 19 +- .../abilities/effects/common/PassEffect.java | 60 ++ .../PlayTargetWithoutPayingManaEffect.java | 22 +- .../common/PreventAllCombatDamageEffect.java | 22 +- .../common/PreventAllDamageSourceEffect.java | 24 +- .../common/PreventAllDamageToEffect.java | 23 +- .../common/PreventDamageTargetEffect.java | 21 +- .../PutLibraryIntoGraveTargetEffect.java | 21 +- .../common/RegenerateSourceEffect.java | 90 +++ .../RemoveDelayedTriggeredAbilityEffect.java | 18 +- .../common/RemoveSpecialActionEffect.java | 15 +- .../effects/common/ReturnFromExileEffect.java | 22 +- ...ourceFromGraveyardToBattlefieldEffect.java | 75 +++ .../common/ReturnToHandTargetEffect.java | 20 +- .../effects/common/SacrificeSourceEffect.java | 20 +- .../common/SearchLibraryPutInHandEffect.java | 41 +- .../common/SearchLibraryPutInPlayEffect.java | 39 +- .../SearchLibraryRevealPutInHandEffect.java | 41 +- .../common/SkipNextUntapTargetEffect.java | 20 +- .../effects/common/TapSourceEffect.java | 18 +- .../effects/common/TapTargetEffect.java | 24 +- .../effects/common/UntapTargetEffect.java | 24 +- .../abilities/keyword/CascadeAbility.java | 33 +- .../abilities/keyword/CyclingAbility.java | 29 +- .../abilities/keyword/DeathtouchAbility.java | 9 +- .../abilities/keyword/DefenderAbility.java | 7 +- .../keyword/DoubleStrikeAbility.java | 7 +- .../abilities/keyword/EnchantAbility.java | 11 +- .../mage/abilities/keyword/EquipAbility.java | 11 +- .../abilities/keyword/ExaltedAbility.java | 13 +- .../abilities/keyword/FirstStrikeAbility.java | 7 +- .../mage/abilities/keyword/FlashAbility.java | 7 +- .../mage/abilities/keyword/FlyingAbility.java | 7 +- .../abilities/keyword/FortifyAbility.java | 12 +- .../mage/abilities/keyword/HasteAbility.java | 7 +- .../keyword/IndestructibleAbility.java | 65 ++ .../mage/abilities/keyword/KickerAbility.java | 18 +- .../abilities/keyword/LandwalkAbility.java | 14 +- .../mage/abilities/keyword/LevelAbility.java | 23 +- .../abilities/keyword/LevelUpAbility.java | 12 +- .../abilities/keyword/LifelinkAbility.java | 7 +- .../abilities/keyword/MultikickerAbility.java | 14 +- .../abilities/keyword/PhasingAbility.java | 7 +- .../abilities/keyword/ProtectionAbility.java | 39 +- .../mage/abilities/keyword/ReachAbility.java | 7 +- .../mage/abilities/keyword/ShroudAbility.java | 7 +- .../abilities/keyword/TrampleAbility.java | 7 +- .../abilities/keyword/UnblockableAbility.java | 7 +- .../abilities/keyword/UnearthAbility.java | 118 ++-- .../abilities/keyword/VigilanceAbility.java | 7 +- .../mage/abilities/mana/BasicManaAbility.java | 6 +- .../mage/abilities/mana/BlackManaAbility.java | 11 +- .../mage/abilities/mana/BlueManaAbility.java | 11 +- .../abilities/mana/ColorlessManaAbility.java | 10 +- .../mage/abilities/mana/GreenManaAbility.java | 11 +- Mage/src/mage/abilities/mana/ManaAbility.java | 18 +- Mage/src/mage/abilities/mana/ManaOptions.java | 19 +- .../mage/abilities/mana/RedManaAbility.java | 11 +- .../mage/abilities/mana/WhiteManaAbility.java | 11 +- Mage/src/mage/cards/Card.java | 6 +- Mage/src/mage/cards/CardImpl.java | 54 +- Mage/src/mage/cards/Cards.java | 18 +- Mage/src/mage/cards/CardsImpl.java | 87 ++- Mage/src/mage/cards/ExpansionSet.java | 12 +- Mage/src/mage/cards/LevelerCard.java | 11 +- Mage/src/mage/cards/basiclands/BasicLand.java | 6 +- Mage/src/mage/cards/basiclands/Forest.java | 6 +- Mage/src/mage/cards/basiclands/Island.java | 6 +- Mage/src/mage/cards/basiclands/Mountain.java | 5 +- Mage/src/mage/cards/basiclands/Plains.java | 5 +- Mage/src/mage/cards/basiclands/Swamp.java | 5 +- Mage/src/mage/cards/decks/Deck.java | 67 +- Mage/src/mage/choices/Choice.java | 5 +- Mage/src/mage/choices/ChoiceColor.java | 11 +- Mage/src/mage/choices/ChoiceImpl.java | 27 +- Mage/src/mage/choices/Choices.java | 26 +- Mage/src/mage/counters/BoostCounter.java | 2 +- Mage/src/mage/counters/Counter.java | 11 +- Mage/src/mage/counters/Counters.java | 12 + Mage/src/mage/counters/MinusOneCounter.java | 2 +- Mage/src/mage/counters/PlusOneCounter.java | 2 +- Mage/src/mage/filter/Filter.java | 3 + Mage/src/mage/filter/FilterAbility.java | 127 ++++ Mage/src/mage/filter/FilterCard.java | 42 +- Mage/src/mage/filter/FilterImpl.java | 19 +- Mage/src/mage/filter/FilterObject.java | 127 +++- Mage/src/mage/filter/FilterPermanent.java | 55 +- Mage/src/mage/filter/FilterPlayer.java | 19 +- Mage/src/mage/filter/FilterSpell.java | 21 +- .../common/FilterAttackingCreature.java | 10 +- .../filter/common/FilterBasicLandCard.java | 11 +- .../common/FilterCreatureForAttack.java | 13 +- .../common/FilterCreatureForCombat.java | 11 +- .../filter/common/FilterCreatureOrPlayer.java | 17 +- .../common/FilterCreaturePermanent.java | 27 +- .../mage/filter/common/FilterEquipment.java | 10 +- .../filter/common/FilterFortification.java | 10 +- .../mage/filter/common/FilterLandCard.java | 11 +- .../filter/common/FilterLandPermanent.java | 10 +- .../common/FilterLegendaryPermanent.java | 12 +- .../mage/filter/common/FilterNonlandCard.java | 10 +- .../filter/common/FilterNonlandPermanent.java | 11 +- .../common/FilterPlaneswalkerOrPlayer.java | 22 +- .../common/FilterPlaneswalkerPermanent.java | 11 +- Mage/src/mage/game/Exile.java | 22 +- Mage/src/mage/game/ExileZone.java | 14 +- Mage/src/mage/game/Game.java | 13 +- Mage/src/mage/game/GameImpl.java | 147 ++-- Mage/src/mage/game/GameState.java | 166 +++-- Mage/src/mage/game/GameStates.java | 25 +- Mage/src/mage/game/Table.java | 19 +- Mage/src/mage/game/combat/Combat.java | 33 +- Mage/src/mage/game/combat/CombatGroup.java | 41 +- Mage/src/mage/game/permanent/Battlefield.java | 24 +- Mage/src/mage/game/permanent/Permanent.java | 7 +- .../mage/game/permanent/PermanentCard.java | 93 ++- .../mage/game/permanent/PermanentImpl.java | 83 ++- .../mage/game/permanent/PermanentToken.java | 46 +- .../permanent/token/EldraziSpawnToken.java | 56 ++ Mage/src/mage/game/permanent/token/Token.java | 21 +- Mage/src/mage/game/stack/Spell.java | 26 +- Mage/src/mage/game/stack/SpellStack.java | 29 +- Mage/src/mage/game/stack/StackAbility.java | 42 +- Mage/src/mage/game/stack/StackObject.java | 3 +- Mage/src/mage/game/turn/BeginCombatStep.java | 11 +- Mage/src/mage/game/turn/BeginningPhase.java | 11 +- Mage/src/mage/game/turn/CleanupStep.java | 10 +- Mage/src/mage/game/turn/CombatDamageStep.java | 12 +- Mage/src/mage/game/turn/CombatPhase.java | 11 +- .../mage/game/turn/DeclareAttackersStep.java | 13 +- .../mage/game/turn/DeclareBlockersStep.java | 12 +- Mage/src/mage/game/turn/DrawStep.java | 13 +- Mage/src/mage/game/turn/EndOfCombatStep.java | 13 +- Mage/src/mage/game/turn/EndPhase.java | 11 +- Mage/src/mage/game/turn/EndStep.java | 11 +- Mage/src/mage/game/turn/Phase.java | 38 +- .../mage/game/turn/PostCombatMainPhase.java | 12 +- .../mage/game/turn/PostCombatMainStep.java | 11 +- .../mage/game/turn/PreCombatMainPhase.java | 11 +- .../src/mage/game/turn/PreCombatMainStep.java | 11 +- Mage/src/mage/game/turn/Step.java | 12 +- Mage/src/mage/game/turn/Turn.java | 13 + Mage/src/mage/game/turn/TurnMod.java | 16 + Mage/src/mage/game/turn/TurnMods.java | 12 + Mage/src/mage/game/turn/UntapStep.java | 12 +- Mage/src/mage/game/turn/UpkeepStep.java | 11 +- Mage/src/mage/players/Library.java | 113 +++- Mage/src/mage/players/ManaPool.java | 14 + Mage/src/mage/players/Player.java | 23 +- Mage/src/mage/players/PlayerImpl.java | 154 +++-- Mage/src/mage/players/PlayerList.java | 11 + Mage/src/mage/players/Players.java | 13 + Mage/src/mage/target/Target.java | 14 +- Mage/src/mage/target/TargetAmount.java | 24 +- Mage/src/mage/target/TargetCard.java | 93 +-- Mage/src/mage/target/TargetImpl.java | 69 +- Mage/src/mage/target/TargetObject.java | 9 +- Mage/src/mage/target/TargetPermanent.java | 39 +- Mage/src/mage/target/TargetPlayer.java | 22 +- Mage/src/mage/target/TargetSpell.java | 15 +- Mage/src/mage/target/Targets.java | 30 +- .../common/TargetAttackingCreature.java | 12 +- .../target/common/TargetBasicLandCard.java | 10 +- .../target/common/TargetCardInGraveyard.java | 16 +- .../mage/target/common/TargetCardInHand.java | 16 +- .../target/common/TargetCardInLibrary.java | 37 +- .../common/TargetControlledPermanent.java | 10 +- .../target/common/TargetCreatureOrPlayer.java | 31 +- .../common/TargetCreatureOrPlayerAmount.java | 31 +- .../common/TargetCreaturePermanent.java | 15 +- .../mage/target/common/TargetDefender.java | 16 +- .../src/mage/target/common/TargetDiscard.java | 17 +- .../target/common/TargetLandPermanent.java | 10 +- .../common/TargetNonBasicLandPermanent.java | 10 +- .../target/common/TargetNonlandPermanent.java | 10 +- .../mage/target/common/TargetOpponent.java | 20 +- Mage/src/mage/util/CircularList.java | 15 + Mage/src/mage/util/Copyable.java | 38 ++ Mage/src/mage/watchers/Watcher.java | 5 +- Mage/src/mage/watchers/WatcherImpl.java | 13 +- Mage/src/mage/watchers/Watchers.java | 8 + 544 files changed, 13327 insertions(+), 3074 deletions(-) create mode 100644 Mage.Client/release/sample-decks/SanJuanSwartz.dck create mode 100644 Mage.Common/src/mage/interfaces/ServerState.java create mode 100644 Mage.Player.AIMinimax/config/AIMinimax.properties create mode 100644 Mage.Player.AIMinimax/nbproject/project.properties create mode 100644 Mage.Player.AIMinimax/nbproject/project.xml create mode 100644 Mage.Player.AIMinimax/src/mage/player/ai/Attackers.java create mode 100644 Mage.Player.AIMinimax/src/mage/player/ai/ComputerPlayer2.java create mode 100644 Mage.Player.AIMinimax/src/mage/player/ai/ComputerPlayer3.java create mode 100644 Mage.Player.AIMinimax/src/mage/player/ai/Config.java create mode 100644 Mage.Player.AIMinimax/src/mage/player/ai/GameStateEvaluator.java create mode 100644 Mage.Player.AIMinimax/src/mage/player/ai/SimulateBlockWorker.java create mode 100644 Mage.Player.AIMinimax/src/mage/player/ai/SimulatedAction.java create mode 100644 Mage.Player.AIMinimax/src/mage/player/ai/SimulatedPlayer.java create mode 100644 Mage.Player.AIMinimax/src/mage/player/ai/SimulationNode.java create mode 100644 Mage.Player.AIMinimax/src/mage/player/ai/SimulationWorker.java create mode 100644 Mage.Server/plugins/AIMinimax.properties create mode 100644 Mage.Server/plugins/Mage.Player.AIMinimax.jar create mode 100644 Mage.Sets/src/mage/sets/Magic2011.java create mode 100644 Mage.Sets/src/mage/sets/magic2011/AcidicSlime.java create mode 100644 Mage.Sets/src/mage/sets/magic2011/ActOfTreason.java create mode 100644 Mage.Sets/src/mage/sets/magic2011/AetherAdept.java create mode 100644 Mage.Sets/src/mage/sets/magic2011/AirServant.java create mode 100644 Mage.Sets/src/mage/sets/magic2011/AjaniGoldmane.java create mode 100644 Mage.Sets/src/mage/sets/riseoftheeldrazi/EmrakulsHatcher.java create mode 100644 Mage.Sets/src/mage/sets/riseoftheeldrazi/EvolvingWilds.java create mode 100644 Mage.Sets/src/mage/sets/riseoftheeldrazi/FlameSlash.java create mode 100644 Mage.Sets/src/mage/sets/riseoftheeldrazi/JoragaTreespeaker.java create mode 100644 Mage.Sets/src/mage/sets/riseoftheeldrazi/KozileksPredator.java create mode 100644 Mage.Sets/src/mage/sets/riseoftheeldrazi/NestInvader.java create mode 100644 Mage.Sets/src/mage/sets/riseoftheeldrazi/Vengevine.java create mode 100644 Mage.Sets/src/mage/sets/worldwake/ArborElf.java create mode 100644 Mage.Sets/src/mage/sets/worldwake/DreadStatuary.java create mode 100644 Mage.Sets/src/mage/sets/worldwake/KhalniGarden.java create mode 100644 Mage.Sets/src/mage/sets/worldwake/WolfbriarElemental.java create mode 100644 Mage.Sets/src/mage/sets/zendikar/AdventuringGear.java create mode 100644 Mage.Sets/src/mage/sets/zendikar/AetherFigment.java create mode 100644 Mage.Sets/src/mage/sets/zendikar/BeastmasterAscension.java create mode 100644 Mage.Sets/src/mage/sets/zendikar/EldraziMonument.java create mode 100644 Mage.Sets/src/mage/sets/zendikar/RiverBoa.java create mode 100644 Mage/src/mage/abilities/common/PassAbility.java create mode 100644 Mage/src/mage/abilities/costs/mana/ManaCostsImpl.java create mode 100644 Mage/src/mage/abilities/effects/ApplyCountersEffect.java create mode 100644 Mage/src/mage/abilities/effects/SearchEffect.java create mode 100644 Mage/src/mage/abilities/effects/common/PassEffect.java create mode 100644 Mage/src/mage/abilities/effects/common/RegenerateSourceEffect.java create mode 100644 Mage/src/mage/abilities/effects/common/ReturnSourceFromGraveyardToBattlefieldEffect.java create mode 100644 Mage/src/mage/abilities/keyword/IndestructibleAbility.java create mode 100644 Mage/src/mage/filter/FilterAbility.java create mode 100644 Mage/src/mage/game/permanent/token/EldraziSpawnToken.java create mode 100644 Mage/src/mage/util/Copyable.java diff --git a/Mage.Client/config/config.properties b/Mage.Client/config/config.properties index b550d599825..5b4f8d43b44 100644 --- a/Mage.Client/config/config.properties +++ b/Mage.Client/config/config.properties @@ -1,7 +1,10 @@ server-name=localhost port=17171 remote-server=mage-server -cards-resource-path=resources/images/cards/ -symbols-resource-path=resources/images/symbols/ -resource-path=resources/images/ +cards-resource-path=C:\\Program Files (x86)\\Wizards of the Coast\\Magic Online III\\Graphics\\Cards\\ +symbols-resource-path=C:\\Program Files (x86)\\Wizards of the Coast\\Magic Online III\\Graphics\\Chat\\chat_graphic_typingicon_ +resource-path=C:\\Program Files (x86)\\Wizards of the Coast\\Magic Online III\\Graphics\\Cards\\Pics\\ +#cards-resource-path=resources/images/cards/ +#symbols-resource-path=resources/images/symbols/ +#resource-path=resources/images/ card-scaling-factor=0.4 \ No newline at end of file diff --git a/Mage.Client/nbproject/project.properties b/Mage.Client/nbproject/project.properties index afa4656b9da..1d9871b7a57 100644 --- a/Mage.Client/nbproject/project.properties +++ b/Mage.Client/nbproject/project.properties @@ -20,6 +20,7 @@ debug.test.classpath=\ dist.dir=dist dist.jar=${dist.dir}/Mage.Client.jar dist.javadoc.dir=${dist.dir}/javadoc +endorsed.classpath= excludes= includes=** jar.compress=false @@ -52,7 +53,7 @@ jaxbwiz.endorsed.dirs="${netbeans.home}/../ide12/modules/ext/jaxb/api" main.class=mage.client.MageFrame manifest.file=manifest.mf meta.inf.dir=${src.dir}/META-INF -platform.active=default_platform +platform.active=JDK_1.6_21 project.license=bsd project.Mage=../Mage project.Mage_Common=../Mage.Common diff --git a/Mage.Client/nbproject/project.xml b/Mage.Client/nbproject/project.xml index 7ec054b48bc..5252a1682b0 100644 --- a/Mage.Client/nbproject/project.xml +++ b/Mage.Client/nbproject/project.xml @@ -4,6 +4,7 @@ Mage.Client + diff --git a/Mage.Client/release/sample-decks/SanJuanSwartz.dck b/Mage.Client/release/sample-decks/SanJuanSwartz.dck new file mode 100644 index 0000000000000000000000000000000000000000..baf9af36a70476e746c856c25dbfa034f7ab0097 GIT binary patch literal 995 zcmbVLyKYlK5S1xh7b9Q$2+Year40rn2p`?YJ`6Q!O&nG(p>)}5& zUi`E4_vq*Q&0iN_Ifa#iCU8CdfFrC9#pKn;A|UrzDsGA!*w$(tEX1_1D4TnhM3;BFDsllWxp zIM2q|qhyB(vxcAyk*5N;lp|!No9sCzFHd9Vi~k7%6?N6lhh5CAc^QsHHacHY5V)s` z8QIMAkdvcu#_2H{+b%Li`l^D!{c|)Y#2H$tqhQPRn?CZ_1U8kbfoj}zyqmWp=dyyp z6J@jUzspnraWhkx?Uyb592yCf% zF^AO2(AQm)F*2_y2s~8!nWV46J?`5PX7h~eQ8x#H+p1f%IW{BWZHEF6w0g$%glvaK z#!|Gqq3~B0o$*_jx0`G*%$fRO!7z`K4F!Qm+HH|#o6t)?e8e^1BI}JScBUMK<@48N ncYZ@h&JJUz$h@gA5m;}Gk!4nCpsgGX(H6@Q-wG5jW^M8ZumwR8 literal 0 HcmV?d00001 diff --git a/Mage.Client/src/mage/client/MageFrame.java b/Mage.Client/src/mage/client/MageFrame.java index a41b86ac17f..039e201d93b 100644 --- a/Mage.Client/src/mage/client/MageFrame.java +++ b/Mage.Client/src/mage/client/MageFrame.java @@ -45,7 +45,9 @@ import javax.swing.JLayeredPane; import javax.swing.SwingUtilities; import javax.swing.UIManager; import mage.client.dialog.AboutDialog; +import mage.client.dialog.CombatDialog; import mage.client.dialog.ConnectDialog; +import mage.client.dialog.PickNumberDialog; import mage.client.remote.Session; import mage.client.util.EDTExceptionHandler; import mage.util.Logging; @@ -60,6 +62,8 @@ public class MageFrame extends javax.swing.JFrame { private static Session session; private ConnectDialog connectDialog; + private static CombatDialog combat; + private static PickNumberDialog pickNumber; /** * @return the session @@ -96,8 +100,12 @@ public class MageFrame extends javax.swing.JFrame { session = new Session(this); connectDialog = new ConnectDialog(session); + combat = new CombatDialog(); + pickNumber = new PickNumberDialog(); desktopPane.add(connectDialog, JLayeredPane.POPUP_LAYER); -// connectDialog.setLocation(50, 50); + desktopPane.add(combat, JLayeredPane.POPUP_LAYER); + combat.hideDialog(); + desktopPane.add(pickNumber, JLayeredPane.POPUP_LAYER); disableButtons(); } @@ -311,6 +319,14 @@ public class MageFrame extends javax.swing.JFrame { this.deckEditorPane.setVisible(false); } + public static CombatDialog getCombatDialog() { + return combat; + } + + public static PickNumberDialog getPickNumberDialog() { + return pickNumber; + } + /** * @param args the command line arguments */ diff --git a/Mage.Client/src/mage/client/cards/Card.java b/Mage.Client/src/mage/client/cards/Card.java index 46686e49948..43d2eed4551 100644 --- a/Mage.Client/src/mage/client/cards/Card.java +++ b/Mage.Client/src/mage/client/cards/Card.java @@ -63,6 +63,7 @@ import javax.swing.text.StyledDocument; import mage.Constants.CardType; import mage.client.MageFrame; import mage.client.remote.Session; +import mage.client.util.Config; import mage.client.util.ImageHelper; import mage.view.CardView; @@ -79,10 +80,11 @@ public class Card extends javax.swing.JPanel implements MouseMotionListener, Mou protected Point p; protected CardDimensions dimension; - protected UUID gameId; - protected BigCard bigCard; + protected final UUID gameId; + protected final BigCard bigCard; protected CardView card; protected Popup popup; + protected boolean popupShowing; protected TextPopup popupText = new TextPopup(); protected BufferedImage background; @@ -97,7 +99,7 @@ public class Card extends javax.swing.JPanel implements MouseMotionListener, Mou this.gameId = gameId; this.card = card; this.bigCard = bigCard; - small = new BufferedImage(dimension.frameWidth, dimension.frameHeight, BufferedImage.TYPE_INT_RGB); + small = new BufferedImage(Config.dimensions.frameWidth, Config.dimensions.frameHeight, BufferedImage.TYPE_INT_RGB); background = ImageHelper.getBackground(card); StyledDocument doc = text.getStyledDocument(); @@ -136,7 +138,7 @@ public class Card extends javax.swing.JPanel implements MouseMotionListener, Mou gSmall.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); gSmall.setColor(Color.BLACK); - gSmall.drawImage(ImageHelper.ScaleImage(image, dimension.frameWidth, dimension.frameHeight), 0, 0, this); + gSmall.drawImage(ImageHelper.ScaleImage(image, Config.dimensions.frameWidth, Config.dimensions.frameHeight), 0, 0, this); gImage.setFont(new Font("Arial", Font.PLAIN, NAME_FONT_MAX_SIZE)); gImage.drawString(card.getName(), CONTENT_MAX_XOFFSET, NAME_MAX_YOFFSET); @@ -152,17 +154,17 @@ public class Card extends javax.swing.JPanel implements MouseMotionListener, Mou gImage.dispose(); - gSmall.setFont(new Font("Arial", Font.PLAIN, dimension.nameFontSize)); - gSmall.drawString(card.getName(), dimension.contentXOffset, dimension.nameYOffset); + gSmall.setFont(new Font("Arial", Font.PLAIN, Config.dimensions.nameFontSize)); + gSmall.drawString(card.getName(), Config.dimensions.contentXOffset, Config.dimensions.nameYOffset); if (card.getCardTypes().contains(CardType.CREATURE)) { - gSmall.drawString(card.getPower() + "/" + card.getToughness(), dimension.powBoxTextLeft, dimension.powBoxTextTop); + gSmall.drawString(card.getPower() + "/" + card.getToughness(), Config.dimensions.powBoxTextLeft, Config.dimensions.powBoxTextTop); } else if (card.getCardTypes().contains(CardType.PLANESWALKER)) { - gSmall.drawString(card.getLoyalty(), dimension.powBoxTextLeft, dimension.powBoxTextTop); + gSmall.drawString(card.getLoyalty(), Config.dimensions.powBoxTextLeft, Config.dimensions.powBoxTextTop); } if (card.getCardTypes().size() > 0) - gSmall.drawString(cardType, dimension.contentXOffset, dimension.typeYOffset); + gSmall.drawString(cardType, Config.dimensions.contentXOffset, Config.dimensions.typeYOffset); drawText(); gSmall.dispose(); @@ -187,6 +189,7 @@ public class Card extends javax.swing.JPanel implements MouseMotionListener, Mou for (String rule: getRules()) { sb.append("\n").append(rule); } + sb.append("\n").append(card.getId()); return sb.toString(); } @@ -272,7 +275,7 @@ public class Card extends javax.swing.JPanel implements MouseMotionListener, Mou } else { g2.setColor(Color.BLACK); } - g2.drawRect(0, 0, dimension.frameWidth - 1, dimension.frameHeight - 1); + g2.drawRect(0, 0, Config.dimensions.frameWidth - 1, Config.dimensions.frameHeight - 1); } @Override @@ -301,21 +304,27 @@ public class Card extends javax.swing.JPanel implements MouseMotionListener, Mou @Override public void mouseEntered(MouseEvent arg0) { - if (popup != null) + if (!popupShowing) { + if (popup != null) + popup.hide(); + PopupFactory factory = PopupFactory.getSharedInstance(); + popup = factory.getPopup(this, popupText, (int) this.getLocationOnScreen().getX() + Config.dimensions.frameWidth, (int) this.getLocationOnScreen().getY() + 40); + popup.show(); + //hack to get popup to resize to fit text popup.hide(); - PopupFactory factory = PopupFactory.getSharedInstance(); - popup = factory.getPopup(this, popupText, (int) this.getLocationOnScreen().getX() + dimension.frameWidth, (int) this.getLocationOnScreen().getY() + 40); - popup.show(); - //hack to get popup to resize to fit text - popup.hide(); - popup = factory.getPopup(this, popupText, (int) this.getLocationOnScreen().getX() + dimension.frameWidth, (int) this.getLocationOnScreen().getY() + 40); - popup.show(); + popup = factory.getPopup(this, popupText, (int) this.getLocationOnScreen().getX() + Config.dimensions.frameWidth, (int) this.getLocationOnScreen().getY() + 40); + popup.show(); + popupShowing = true; + } } @Override public void mouseExited(MouseEvent arg0) { - if (popup != null) + if(getMousePosition(true) != null) return; + if (popup != null) { popup.hide(); + popupShowing = false; + } } @Override diff --git a/Mage.Client/src/mage/client/cards/CardDimensions.java b/Mage.Client/src/mage/client/cards/CardDimensions.java index 56c297401f8..7284fd5ecad 100644 --- a/Mage.Client/src/mage/client/cards/CardDimensions.java +++ b/Mage.Client/src/mage/client/cards/CardDimensions.java @@ -36,19 +36,19 @@ import static mage.client.util.Constants.*; */ public class CardDimensions { - public static int frameHeight; - public static int frameWidth; - public static int symbolHeight; - public static int symbolWidth; - public static int contentXOffset; - public static int nameYOffset; - public static int typeYOffset; - public static int textYOffset; - public static int textWidth; - public static int textHeight; - public static int powBoxTextTop; - public static int powBoxTextLeft; - public static int nameFontSize; + public int frameHeight; + public int frameWidth; + public int symbolHeight; + public int symbolWidth; + public int contentXOffset; + public int nameYOffset; + public int typeYOffset; + public int textYOffset; + public int textWidth; + public int textHeight; + public int powBoxTextTop; + public int powBoxTextLeft; + public int nameFontSize; public CardDimensions(double scaleFactor) { frameHeight = (int)(FRAME_MAX_HEIGHT * scaleFactor); diff --git a/Mage.Client/src/mage/client/cards/CardGrid.form b/Mage.Client/src/mage/client/cards/CardGrid.form index 0f2b598a529..e0b3e229964 100644 --- a/Mage.Client/src/mage/client/cards/CardGrid.form +++ b/Mage.Client/src/mage/client/cards/CardGrid.form @@ -16,12 +16,12 @@ - + - + diff --git a/Mage.Client/src/mage/client/cards/CardGrid.java b/Mage.Client/src/mage/client/cards/CardGrid.java index bee2d23ed81..4bd90bfd4ec 100644 --- a/Mage.Client/src/mage/client/cards/CardGrid.java +++ b/Mage.Client/src/mage/client/cards/CardGrid.java @@ -37,12 +37,17 @@ package mage.client.cards; import java.awt.Component; import java.awt.Dimension; import java.awt.Rectangle; -import java.awt.event.ComponentEvent; -import java.awt.event.ComponentListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import java.util.UUID; import mage.client.util.Config; import mage.client.util.Event; @@ -54,45 +59,52 @@ import mage.view.CardsView; * * @author BetaSteward_at_googlemail.com */ -public class CardGrid extends javax.swing.JLayeredPane implements MouseListener, ComponentListener { +public class CardGrid extends javax.swing.JLayeredPane implements MouseListener { protected CardEventSource cardEventSource = new CardEventSource(); protected BigCard bigCard; protected UUID gameId; - protected List cards = new ArrayList(); + private Map cards = new HashMap(); public CardGrid() { initComponents(); - addComponentListener(this); - setPreferredSize(new Dimension(Config.dimensions.frameWidth, Config.dimensions.frameHeight)); } public void loadCards(CardsView showCards, BigCard bigCard, UUID gameId) { this.bigCard = bigCard; this.gameId = gameId; - cards.clear(); - for (CardView card: showCards) { - Card cardImg = new Card(card, bigCard, Config.dimensions, gameId); - cardImg.update(card); - cardImg.addMouseListener(this); - cards.add(cardImg); + for (CardView card: showCards.values()) { + if (!cards.containsKey(card.getId())) { + Card cardImg = new Card(card, bigCard, Config.dimensions, gameId); + cardImg.addMouseListener(this); + add(cardImg); + cardImg.update(card); + cards.put(card.getId(), cardImg); + } + } + for (Iterator> i = cards.entrySet().iterator(); i.hasNext();) { + Entry entry = i.next(); + if (!showCards.containsKey(entry.getKey())) { + removeCard(entry.getKey()); + i.remove(); + } } drawCards(); this.setVisible(true); } public void drawCards() { - removeAll(); int maxWidth = this.getParent().getWidth(); int numColumns = maxWidth / Config.dimensions.frameWidth; int curColumn = 0; int curRow = 0; if (cards.size() > 0) { Rectangle rectangle = new Rectangle(Config.dimensions.frameWidth, Config.dimensions.frameHeight); - for (Card cardImg: cards) { + List sortedCards = new ArrayList(cards.values()); + Collections.sort(sortedCards, new CardComparator()); + for (Card cardImg: sortedCards) { rectangle.setLocation(curColumn * Config.dimensions.frameWidth, curRow * 20); cardImg.setBounds(rectangle); - add(cardImg); moveToFront(cardImg); curColumn++; if (curColumn == numColumns) { @@ -104,6 +116,17 @@ public class CardGrid extends javax.swing.JLayeredPane implements MouseListener, resizeArea(); } + private void removeCard(UUID cardId) { + for (Component comp: getComponents()) { + if (comp instanceof Card) { + if (((Card)comp).getCardId().equals(cardId)) { + remove(comp); + } + } + } + } + + public void addCardEventListener(Listener listener) { cardEventSource.addListener(listener); } @@ -125,11 +148,11 @@ public class CardGrid extends javax.swing.JLayeredPane implements MouseListener, this.setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGap(0, 400, Short.MAX_VALUE) + .addGap(0, 294, Short.MAX_VALUE) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGap(0, 300, Short.MAX_VALUE) + .addGap(0, 197, Short.MAX_VALUE) ); }// //GEN-END:initComponents @@ -157,26 +180,6 @@ public class CardGrid extends javax.swing.JLayeredPane implements MouseListener, @Override public void mouseExited(MouseEvent e) {} - @Override - public void componentResized(ComponentEvent e) { - resizeArea(); - } - - @Override - public void componentMoved(ComponentEvent e) { - resizeArea(); - } - - @Override - public void componentShown(ComponentEvent e) { - resizeArea(); - } - - @Override - public void componentHidden(ComponentEvent e) { - resizeArea(); - } - private void resizeArea() { Dimension area = new Dimension(0, 0); Dimension size = getPreferredSize(); @@ -198,3 +201,12 @@ public class CardGrid extends javax.swing.JLayeredPane implements MouseListener, } } + +class CardComparator implements Comparator { + + @Override + public int compare(Card o1, Card o2) { + return o1.card.getName().compareTo(o2.card.getName()); + } + +} \ No newline at end of file diff --git a/Mage.Client/src/mage/client/cards/Cards.java b/Mage.Client/src/mage/client/cards/Cards.java index cd3e04e18c6..cbee19ab2e6 100644 --- a/Mage.Client/src/mage/client/cards/Cards.java +++ b/Mage.Client/src/mage/client/cards/Cards.java @@ -34,12 +34,16 @@ package mage.client.cards; +import java.awt.Component; import java.awt.Dimension; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; import java.util.UUID; import mage.client.util.Config; import mage.view.CardView; import mage.view.CardsView; -import static mage.client.util.Constants.*; /** * @@ -47,26 +51,52 @@ import static mage.client.util.Constants.*; */ public class Cards extends javax.swing.JPanel { + private Map cards = new HashMap(); + /** Creates new form Cards */ public Cards() { initComponents(); - cardArea.setPreferredSize(new Dimension(Config.dimensions.frameWidth, Config.dimensions.frameHeight)); } - public void loadCards(CardsView cards, BigCard bigCard, UUID gameId) { - cardArea.removeAll(); - for (CardView card: cards) { - Card cardImg = new Card(card, bigCard, Config.dimensions, gameId); - cardArea.add(cardImg); - cardImg.update(card); + public boolean loadCards(CardsView cardsView, BigCard bigCard, UUID gameId) { + boolean changed = false; + for (CardView card: cardsView.values()) { + if (!cards.containsKey(card.getId())) { + Card cardImg = new Card(card, bigCard, Config.dimensions, gameId); + cards.put(card.getId(), cardImg); + cardArea.add(cardImg); + changed = true; + } + cards.get(card.getId()).update(card); } + for (Iterator> i = cards.entrySet().iterator(); i.hasNext();) { + Entry entry = i.next(); + if (!cardsView.containsKey(entry.getKey())) { + removeCard(entry.getKey()); + i.remove(); + changed = true; + } + } + cardArea.setPreferredSize(new Dimension(cards.size() * Config.dimensions.frameWidth, Config.dimensions.frameHeight)); cardArea.revalidate(); cardArea.repaint(); this.revalidate(); this.repaint(); + return changed; } + private void removeCard(UUID cardId) { + for (Component comp: cardArea.getComponents()) { + if (comp instanceof Card) { + if (((Card)comp).getCardId().equals(cardId)) { + cardArea.remove(comp); + } + } + } + } + + /** This method is called from within the constructor to * initialize the form. * WARNING: Do NOT modify this code. The content of this method is diff --git a/Mage.Client/src/mage/client/cards/CardsList.form b/Mage.Client/src/mage/client/cards/CardsList.form index 4b0169ba1c2..5c03d47756d 100644 --- a/Mage.Client/src/mage/client/cards/CardsList.form +++ b/Mage.Client/src/mage/client/cards/CardsList.form @@ -7,6 +7,9 @@ + + + diff --git a/Mage.Client/src/mage/client/cards/CardsList.java b/Mage.Client/src/mage/client/cards/CardsList.java index 28fe44b5cdc..085401ce0fb 100644 --- a/Mage.Client/src/mage/client/cards/CardsList.java +++ b/Mage.Client/src/mage/client/cards/CardsList.java @@ -44,7 +44,6 @@ import mage.client.util.Event; import mage.client.util.Listener; import mage.view.CardView; import mage.view.CardsView; -import static mage.client.util.Constants.*; /** * @@ -57,7 +56,6 @@ public class CardsList extends javax.swing.JPanel implements MouseListener { /** Creates new form Cards */ public CardsList() { initComponents(); - cardArea.setPreferredSize(new Dimension(Config.dimensions.frameWidth, Config.dimensions.frameHeight)); } public void loadCards(CardsView showCards, BigCard bigCard, UUID gameId) { @@ -65,7 +63,7 @@ public class CardsList extends javax.swing.JPanel implements MouseListener { if (showCards != null && showCards.size() > 0) { Rectangle rectangle = new Rectangle(Config.dimensions.frameWidth, Config.dimensions.frameHeight); int count = 0; - for (CardView card: showCards) { + for (CardView card: showCards.values()) { Card cardImg = new Card(card, bigCard, Config.dimensions, gameId); cardImg.setBounds(rectangle); cardArea.add(cardImg); @@ -110,6 +108,7 @@ public class CardsList extends javax.swing.JPanel implements MouseListener { cardArea = new javax.swing.JLayeredPane(); setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0))); + setPreferredSize(new Dimension(Config.dimensions.frameWidth, Config.dimensions.frameHeight)); setLayout(new java.awt.BorderLayout()); jScrollPane1.setViewportView(cardArea); @@ -131,15 +130,19 @@ public class CardsList extends javax.swing.JPanel implements MouseListener { } } + @Override public void mousePressed(MouseEvent e) { } + @Override public void mouseReleased(MouseEvent e) { } + @Override public void mouseEntered(MouseEvent e) { } + @Override public void mouseExited(MouseEvent e) { } diff --git a/Mage.Client/src/mage/client/cards/Permanent.java b/Mage.Client/src/mage/client/cards/Permanent.java index a1346875e4a..2759a697a99 100644 --- a/Mage.Client/src/mage/client/cards/Permanent.java +++ b/Mage.Client/src/mage/client/cards/Permanent.java @@ -46,6 +46,7 @@ import java.util.ArrayList; import java.util.List; import java.util.UUID; import javax.swing.PopupFactory; +import mage.client.util.Config; import mage.client.util.ImageHelper; import mage.view.CounterView; import mage.view.PermanentView; @@ -69,7 +70,7 @@ public class Permanent extends Card { super(permanent, bigCard, dimensions, gameId); this.setSize(this.getPreferredSize()); this.permanent = permanent; - tappedImage = new BufferedImage(dimension.frameHeight, dimension.frameWidth, BufferedImage.TYPE_INT_RGB); + tappedImage = new BufferedImage(Config.dimensions.frameHeight, Config.dimensions.frameWidth, BufferedImage.TYPE_INT_RGB); } public UUID getPermanentId() { @@ -153,10 +154,10 @@ public class Permanent extends Card { g2.setColor(Color.BLACK); } if (permanent.isTapped()) { - g2.drawRect(0, 0, dimension.frameHeight - 1, dimension.frameWidth - 1); + g2.drawRect(0, 0, Config.dimensions.frameHeight - 1, Config.dimensions.frameWidth - 1); } else { - g2.drawRect(0, 0, dimension.frameWidth - 1, dimension.frameHeight - 1); + g2.drawRect(0, 0, Config.dimensions.frameWidth - 1, Config.dimensions.frameHeight - 1); } } @@ -185,10 +186,10 @@ public class Permanent extends Card { @Override public Dimension getPreferredSize() { if (permanent != null && permanent.isTapped()) { - return new Dimension(dimension.frameHeight, dimension.frameWidth); + return new Dimension(Config.dimensions.frameHeight, Config.dimensions.frameWidth); } else { - return new Dimension(dimension.frameWidth, dimension.frameHeight); + return new Dimension(Config.dimensions.frameWidth, Config.dimensions.frameHeight); } } @@ -203,17 +204,20 @@ public class Permanent extends Card { @Override public void mouseEntered(MouseEvent arg0) { - if (popup != null) + if (!popupShowing) { + if (popup != null) + popup.hide(); + PopupFactory factory = PopupFactory.getSharedInstance(); + int x = (int) this.getLocationOnScreen().getX() + (permanent.isTapped()?Config.dimensions.frameHeight:Config.dimensions.frameWidth); + int y = (int) this.getLocationOnScreen().getY() + 40; + popup = factory.getPopup(this, popupText, x, y); + popup.show(); + //hack to get popup to resize to fit text popup.hide(); - PopupFactory factory = PopupFactory.getSharedInstance(); - int x = (int) this.getLocationOnScreen().getX() + (permanent.isTapped()?dimension.frameHeight:dimension.frameWidth); - int y = (int) this.getLocationOnScreen().getY() + 40; - popup = factory.getPopup(this, popupText, x, y); - popup.show(); - //hack to get popup to resize to fit text - popup.hide(); - popup = factory.getPopup(this, popupText, x, y); - popup.show(); + popup = factory.getPopup(this, popupText, x, y); + popup.show(); + popupShowing = true; + } } /** This method is called from within the constructor to diff --git a/Mage.Client/src/mage/client/chat/ChatPanel.java b/Mage.Client/src/mage/client/chat/ChatPanel.java index 237d52bda05..f45db03bd26 100644 --- a/Mage.Client/src/mage/client/chat/ChatPanel.java +++ b/Mage.Client/src/mage/client/chat/ChatPanel.java @@ -34,12 +34,14 @@ package mage.client.chat; +import java.awt.Color; import java.awt.event.KeyEvent; import java.util.UUID; import java.util.logging.Logger; import mage.client.MageFrame; import mage.client.remote.Session; import mage.util.Logging; +import mage.view.ChatMessage.MessageColor; /** * * @author BetaSteward_at_googlemail.com @@ -69,7 +71,24 @@ public class ChatPanel extends javax.swing.JPanel { session.leaveChat(chatId); } - public void receiveMessage(String message) { + public void receiveMessage(String message, MessageColor color) { + switch (color) { + case BLACK: + this.txtConversation.setForeground(Color.BLACK); + break; + case RED: + this.txtConversation.setForeground(Color.RED); + break; + case GREEN: + this.txtConversation.setForeground(Color.GREEN); + break; + case BLUE: + this.txtConversation.setForeground(Color.BLUE); + break; + case ORANGE: + this.txtConversation.setForeground(Color.ORANGE); + break; + } this.txtConversation.append(message + "\n"); txtConversation.setCaretPosition(txtConversation.getText().length() - 1); } diff --git a/Mage.Client/src/mage/client/deckeditor/CardSelector.java b/Mage.Client/src/mage/client/deckeditor/CardSelector.java index 1c34ec8a762..156d2304aa9 100644 --- a/Mage.Client/src/mage/client/deckeditor/CardSelector.java +++ b/Mage.Client/src/mage/client/deckeditor/CardSelector.java @@ -37,11 +37,13 @@ package mage.client.deckeditor; import java.awt.Cursor; import java.awt.event.ComponentEvent; import java.awt.event.ComponentListener; +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; import javax.swing.DefaultComboBoxModel; import mage.Constants.CardType; -import mage.Constants.Zone; -import mage.cards.Cards; -import mage.cards.CardsImpl; +import mage.cards.Card; import mage.cards.ExpansionSet; import mage.client.cards.BigCard; import mage.client.cards.CardGrid; @@ -56,8 +58,9 @@ import mage.view.CardsView; */ public class CardSelector extends javax.swing.JPanel implements ComponentListener { - private Cards cards = new CardsImpl(Zone.OUTSIDE); - private FilterCard filter = new FilterCard(); + private final Set allCards = new LinkedHashSet(); + private final List cards = new ArrayList(); + private final FilterCard filter = new FilterCard(); private BigCard bigCard; /** Creates new form CardSelector */ @@ -68,12 +71,11 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene public void loadCards(BigCard bigCard) { this.bigCard = bigCard; - this.cards.clear(); cbExpansionSet.setModel(new DefaultComboBoxModel(Sets.getInstance().toArray())); cbExpansionSet.insertItemAt("All sets", 0); cbExpansionSet.setSelectedIndex(0); for (ExpansionSet set: Sets.getInstance()) { - cards.addAll(set.createCards()); + allCards.addAll(set.createCards()); } filter.setUseColor(true); filter.getColor().setBlack(true); @@ -98,8 +100,13 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene private void filterCards() { try { + cards.clear(); + for (Card card: allCards) { + if (filter.match(card)) + cards.add(card); + } setCursor(new Cursor(Cursor.WAIT_CURSOR)); - this.cardGrid.loadCards(new CardsView(cards.getCards(filter)), bigCard, null); + this.cardGrid.loadCards(new CardsView(cards), bigCard, null); } finally { setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); @@ -110,7 +117,7 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene return this.cardGrid; } - public Cards getCards() { + public List getCards() { return cards; } diff --git a/Mage.Client/src/mage/client/deckeditor/DeckEditorPanel.java b/Mage.Client/src/mage/client/deckeditor/DeckEditorPanel.java index d630b5c08ee..1482266681b 100644 --- a/Mage.Client/src/mage/client/deckeditor/DeckEditorPanel.java +++ b/Mage.Client/src/mage/client/deckeditor/DeckEditorPanel.java @@ -80,7 +80,12 @@ public class DeckEditorPanel extends javax.swing.JPanel { @Override public void event(Event event) { if (event.getEventName().equals("double-click")) { - deck.getCards().add(createCard(cardSelector.getCards().get((UUID)event.getSource()).getClass())); + for (Card card: cardSelector.getCards()) { + if (card.getId().equals((UUID)event.getSource())) { + deck.getCards().add(createCard(card.getClass())); + break; + } + } refreshDeck(); } } @@ -92,7 +97,12 @@ public class DeckEditorPanel extends javax.swing.JPanel { @Override public void event(Event event) { if (event.getEventName().equals("double-click")) { - deck.getCards().remove((UUID)event.getSource()); + for (Card card: deck.getCards()) { + if (card.getId().equals((UUID)event.getSource())) { + deck.getCards().remove(card); + break; + } + } refreshDeck(); } } @@ -272,8 +282,11 @@ public class DeckEditorPanel extends javax.swing.JPanel { if (ret == JFileChooser.APPROVE_OPTION) { File file = fcSelectDeck.getSelectedFile(); try { + String fileName = file.getPath(); + if (!fileName.endsWith(".dck")) + fileName += ".dck"; setCursor(new Cursor(Cursor.WAIT_CURSOR)); - deck.getDeckCardLists().save(file.getPath()); + deck.getDeckCardLists().save(fileName); } catch (Exception ex) { Logger.getLogger(DeckEditorPanel.class.getName()).log(Level.SEVERE, null, ex); } diff --git a/Mage.Client/src/mage/client/dialog/ExileZoneDialog.form b/Mage.Client/src/mage/client/dialog/ExileZoneDialog.form index cabd4e2962e..07b92a9fbd2 100644 --- a/Mage.Client/src/mage/client/dialog/ExileZoneDialog.form +++ b/Mage.Client/src/mage/client/dialog/ExileZoneDialog.form @@ -33,6 +33,11 @@ + + + + + diff --git a/Mage.Client/src/mage/client/dialog/ExileZoneDialog.java b/Mage.Client/src/mage/client/dialog/ExileZoneDialog.java index bb793f21aed..9200625990b 100644 --- a/Mage.Client/src/mage/client/dialog/ExileZoneDialog.java +++ b/Mage.Client/src/mage/client/dialog/ExileZoneDialog.java @@ -38,9 +38,8 @@ import java.beans.PropertyVetoException; import java.util.UUID; import java.util.logging.Level; import java.util.logging.Logger; -import javax.swing.JLayeredPane; -import mage.client.MageFrame; import mage.client.cards.BigCard; +import mage.client.util.Config; import mage.view.ExileView; /** @@ -57,17 +56,21 @@ public class ExileZoneDialog extends MageDialog { public void loadCards(ExileView exile, BigCard bigCard, UUID gameId) { this.title = exile.getName(); + boolean changed = false; + changed = cards.loadCards(exile, bigCard, gameId); if (exile.size() > 0) { - cards.loadCards(exile, bigCard, gameId); - if (getParent() != MageFrame.getDesktop() || this.isClosed) - MageFrame.getDesktop().add(this, JLayeredPane.POPUP_LAYER); -// try { -// this.setIcon(false); -// } catch (PropertyVetoException ex) { -// Logger.getLogger(ShowCardsDialog.class.getName()).log(Level.SEVERE, null, ex); -// } + show(); + if (changed) { + try { + this.setIcon(false); + } catch (PropertyVetoException ex) { + Logger.getLogger(ExileZoneDialog.class.getName()).log(Level.SEVERE, null, ex); + } + } + } + else { + hide(); } - this.setVisible(exile.size() > 0); } /** This method is called from within the constructor to @@ -83,6 +86,8 @@ public class ExileZoneDialog extends MageDialog { setIconifiable(true); + cards.setPreferredSize(new java.awt.Dimension(Config.dimensions.frameWidth, Config.dimensions.frameHeight + 25)); + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); getContentPane().setLayout(layout); layout.setHorizontalGroup( diff --git a/Mage.Client/src/mage/client/dialog/JoinTableDialog.form b/Mage.Client/src/mage/client/dialog/JoinTableDialog.form index 70e8af40834..56d2b74137b 100644 --- a/Mage.Client/src/mage/client/dialog/JoinTableDialog.form +++ b/Mage.Client/src/mage/client/dialog/JoinTableDialog.form @@ -22,21 +22,26 @@ - - - + - + + + + + + + + - + - + diff --git a/Mage.Client/src/mage/client/dialog/JoinTableDialog.java b/Mage.Client/src/mage/client/dialog/JoinTableDialog.java index 031dbb764f0..a6ee4d031b7 100644 --- a/Mage.Client/src/mage/client/dialog/JoinTableDialog.java +++ b/Mage.Client/src/mage/client/dialog/JoinTableDialog.java @@ -103,17 +103,20 @@ public class JoinTableDialog extends MageDialog { getContentPane().setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addContainerGap(310, Short.MAX_VALUE) - .addComponent(btnOK) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(btnCancel) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addComponent(btnOK) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(btnCancel)) + .addComponent(newPlayerPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 414, Short.MAX_VALUE)) .addContainerGap()) - .addComponent(newPlayerPanel, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 438, Short.MAX_VALUE) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addContainerGap() .addComponent(newPlayerPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) @@ -133,7 +136,7 @@ public class JoinTableDialog extends MageDialog { private void btnOKActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnOKActionPerformed Session session = MageFrame.getSession(); try { - joined = session.joinTable(roomId, tableId, 1, this.newPlayerPanel.getPlayerName(), DeckCardLists.load(this.newPlayerPanel.getDeckFile())); + joined = session.joinTable(roomId, tableId, this.newPlayerPanel.getPlayerName(), DeckCardLists.load(this.newPlayerPanel.getDeckFile())); } catch (Exception ex) { handleError(ex); } diff --git a/Mage.Client/src/mage/client/dialog/MageDialog.form b/Mage.Client/src/mage/client/dialog/MageDialog.form index 871b94ca200..fd8ee8f40b7 100644 --- a/Mage.Client/src/mage/client/dialog/MageDialog.form +++ b/Mage.Client/src/mage/client/dialog/MageDialog.form @@ -24,7 +24,7 @@ - + diff --git a/Mage.Client/src/mage/client/dialog/MageDialog.java b/Mage.Client/src/mage/client/dialog/MageDialog.java index b8e5933351b..66882175f4e 100644 --- a/Mage.Client/src/mage/client/dialog/MageDialog.java +++ b/Mage.Client/src/mage/client/dialog/MageDialog.java @@ -64,6 +64,7 @@ public class MageDialog extends javax.swing.JInternalFrame { @Override public void show() { super.show(); + this.toFront(); if (this.modal) { startModal(); } @@ -72,6 +73,7 @@ public class MageDialog extends javax.swing.JInternalFrame { @Override public void setVisible(boolean value) { super.setVisible(value); + this.toFront(); if (modal) { if (value) { startModal(); diff --git a/Mage.Client/src/mage/client/dialog/NewTableDialog.java b/Mage.Client/src/mage/client/dialog/NewTableDialog.java index 2f429acc149..c92348ca90c 100644 --- a/Mage.Client/src/mage/client/dialog/NewTableDialog.java +++ b/Mage.Client/src/mage/client/dialog/NewTableDialog.java @@ -286,18 +286,16 @@ public class NewTableDialog extends MageDialog { (MultiplayerAttackOption)this.cbAttackOption.getSelectedItem(), (RangeOfInfluence)this.cbRange.getSelectedItem()); try { - if (session.joinTable(roomId, table.getTableId(), 0, this.player1Panel.getPlayerName(), DeckCardLists.load(this.player1Panel.getDeckFile()))) { - int seatNum = 1; + if (session.joinTable(roomId, table.getTableId(), this.player1Panel.getPlayerName(), DeckCardLists.load(this.player1Panel.getDeckFile()))) { for (TablePlayerPanel player: players) { if (!player.getPlayerType().equals("Human")) { - if (!player.joinTable(roomId, table.getTableId(), seatNum)) { + if (!player.joinTable(roomId, table.getTableId())) { JOptionPane.showMessageDialog(MageFrame.getDesktop(), "Error joining table.", "Error", JOptionPane.ERROR_MESSAGE); session.removeTable(roomId, table.getTableId()); table = null; return; } } - seatNum++; } this.setVisible(false); return; diff --git a/Mage.Client/src/mage/client/dialog/PickNumberDialog.form b/Mage.Client/src/mage/client/dialog/PickNumberDialog.form index ef477c3cf85..7d68d2c54a1 100644 --- a/Mage.Client/src/mage/client/dialog/PickNumberDialog.form +++ b/Mage.Client/src/mage/client/dialog/PickNumberDialog.form @@ -20,23 +20,24 @@ + - - + + + + + + + + - - - + - - - - + - @@ -44,10 +45,10 @@ - - + + - + @@ -81,10 +82,30 @@ - + - + - + + + + + + + + + + + + + + + + + + + + + diff --git a/Mage.Client/src/mage/client/dialog/PickNumberDialog.java b/Mage.Client/src/mage/client/dialog/PickNumberDialog.java index baf8731462f..cd9cd977f17 100644 --- a/Mage.Client/src/mage/client/dialog/PickNumberDialog.java +++ b/Mage.Client/src/mage/client/dialog/PickNumberDialog.java @@ -53,7 +53,9 @@ public class PickNumberDialog extends MageDialog { public void showDialog(int min, int max, String message) { this.spnAmount.setModel(new SpinnerNumberModel(min, min, max, 1)); this.lblMessage.setText(message); + this.btnOk.setVisible(true); this.btnCancel.setVisible(false); + this.pack(); this.setVisible(true); } @@ -77,7 +79,8 @@ public class PickNumberDialog extends MageDialog { spnAmount = new javax.swing.JSpinner(); btnCancel = new javax.swing.JButton(); btnOk = new javax.swing.JButton(); - lblMessage = new javax.swing.JLabel(); + jScrollPane1 = new javax.swing.JScrollPane(); + lblMessage = new javax.swing.JTextPane(); spnAmount.setModel(new javax.swing.SpinnerNumberModel(Integer.valueOf(1), null, null, Integer.valueOf(1))); @@ -95,35 +98,42 @@ public class PickNumberDialog extends MageDialog { } }); - lblMessage.setHorizontalAlignment(javax.swing.SwingConstants.CENTER); + jScrollPane1.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + + lblMessage.setBorder(null); + lblMessage.setEditable(false); + lblMessage.setCursor(null ); + lblMessage.setFocusable(false); + lblMessage.setOpaque(false); + jScrollPane1.setViewportView(lblMessage); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); getContentPane().setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() + .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() - .addContainerGap(13, Short.MAX_VALUE) - .addComponent(btnOk) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(btnCancel)) - .addGroup(layout.createSequentialGroup() - .addGap(44, 44, 44) - .addComponent(spnAmount, javax.swing.GroupLayout.PREFERRED_SIZE, 52, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addGroup(layout.createSequentialGroup() - .addContainerGap() - .addComponent(lblMessage, javax.swing.GroupLayout.DEFAULT_SIZE, 121, Short.MAX_VALUE))) - .addContainerGap()) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addComponent(btnOk) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(btnCancel)) + .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 146, Short.MAX_VALUE)) + .addContainerGap()) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addComponent(spnAmount, javax.swing.GroupLayout.PREFERRED_SIZE, 52, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(54, 54, 54)))) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() .addContainerGap() - .addComponent(lblMessage, javax.swing.GroupLayout.DEFAULT_SIZE, 30, Short.MAX_VALUE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 64, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(spnAmount, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(btnCancel) .addComponent(btnOk)) @@ -146,7 +156,8 @@ public class PickNumberDialog extends MageDialog { // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton btnCancel; private javax.swing.JButton btnOk; - private javax.swing.JLabel lblMessage; + private javax.swing.JScrollPane jScrollPane1; + private javax.swing.JTextPane lblMessage; private javax.swing.JSpinner spnAmount; // End of variables declaration//GEN-END:variables diff --git a/Mage.Client/src/mage/client/dialog/ShowCardsDialog.java b/Mage.Client/src/mage/client/dialog/ShowCardsDialog.java index a77c19fefc7..b6102a5f04b 100644 --- a/Mage.Client/src/mage/client/dialog/ShowCardsDialog.java +++ b/Mage.Client/src/mage/client/dialog/ShowCardsDialog.java @@ -78,7 +78,7 @@ public class ShowCardsDialog extends MageDialog implements MouseListener { private void loadCardsFew(CardsView showCards, BigCard bigCard, CardDimensions dimension, UUID gameId) { Rectangle rectangle = new Rectangle(Config.dimensions.frameWidth, Config.dimensions.frameHeight); - for (CardView card: showCards) { + for (CardView card: showCards.values()) { Card cardImg = new Card(card, bigCard, dimension, gameId); cardImg.setBounds(rectangle); cardArea.add(cardImg); @@ -95,7 +95,7 @@ public class ShowCardsDialog extends MageDialog implements MouseListener { if (showCards != null && showCards.size() > 0) { Rectangle rectangle = new Rectangle(Config.dimensions.frameWidth, Config.dimensions.frameHeight); int count = 0; - for (CardView card: showCards) { + for (CardView card: showCards.values()) { Card cardImg = new Card(card, bigCard, dimension, gameId); cardImg.setBounds(rectangle); cardArea.add(cardImg); diff --git a/Mage.Client/src/mage/client/game/BattlefieldPanel.java b/Mage.Client/src/mage/client/game/BattlefieldPanel.java index f094197fd32..a5fa3c222ce 100644 --- a/Mage.Client/src/mage/client/game/BattlefieldPanel.java +++ b/Mage.Client/src/mage/client/game/BattlefieldPanel.java @@ -48,7 +48,6 @@ import mage.client.cards.BigCard; import mage.client.cards.Permanent; import mage.client.util.Config; import mage.view.PermanentView; -import static mage.client.util.Constants.*; /** * diff --git a/Mage.Client/src/mage/client/game/CombatGroup.form b/Mage.Client/src/mage/client/game/CombatGroup.form index 9bd20c54257..cfe13fcba76 100644 --- a/Mage.Client/src/mage/client/game/CombatGroup.form +++ b/Mage.Client/src/mage/client/game/CombatGroup.form @@ -16,9 +16,9 @@ - - - + + + @@ -26,22 +26,32 @@ - + - + + + + + + + + + + + - + diff --git a/Mage.Client/src/mage/client/game/CombatGroup.java b/Mage.Client/src/mage/client/game/CombatGroup.java index 58f454237c6..1f6ec40da66 100644 --- a/Mage.Client/src/mage/client/game/CombatGroup.java +++ b/Mage.Client/src/mage/client/game/CombatGroup.java @@ -39,7 +39,6 @@ import java.util.UUID; import mage.client.cards.BigCard; import mage.client.util.Config; import mage.view.CombatGroupView; -import static mage.client.util.Constants.*; /** * @@ -53,7 +52,6 @@ public class CombatGroup extends javax.swing.JPanel { /** Creates new form CombatGroup */ public CombatGroup() { initComponents(); -// setPreferredSize(new Dimension(160, 440)); } public void init(UUID gameId, BigCard bigCard) { @@ -64,9 +62,9 @@ public class CombatGroup extends javax.swing.JPanel { public void update(CombatGroupView combatGroup) { this.lblDefender.setText(combatGroup.getDefenderName()); this.attackers.loadCards(combatGroup.getAttackers(), bigCard, gameId); - attackers.setPreferredSize(new Dimension(Config.dimensions.frameWidth + 6, Config.dimensions.frameHeight + 6)); +// attackers.setPreferredSize(new Dimension(Config.dimensions.frameWidth + 6, Config.dimensions.frameHeight + 6)); this.blockers.loadCards(combatGroup.getBlockers(), bigCard, gameId); - blockers.setPreferredSize(new Dimension(Config.dimensions.frameWidth + 6, Config.dimensions.frameHeight + 6)); +// blockers.setPreferredSize(new Dimension(Config.dimensions.frameWidth + 6, Config.dimensions.frameHeight + 6)); this.attackers.setVisible(true); this.blockers.setVisible(true); } @@ -84,25 +82,29 @@ public class CombatGroup extends javax.swing.JPanel { attackers = new mage.client.cards.Cards(); lblDefender = new javax.swing.JLabel(); + blockers.setPreferredSize(new java.awt.Dimension(Config.dimensions.frameWidth + 8, Config.dimensions.frameHeight + 25)); + + attackers.setPreferredSize(new java.awt.Dimension(Config.dimensions.frameWidth + 8, Config.dimensions.frameHeight + 25)); + lblDefender.setHorizontalAlignment(javax.swing.SwingConstants.CENTER); - lblDefender.setText("jLabel1"); + lblDefender.setText("Defender"); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(lblDefender, javax.swing.GroupLayout.DEFAULT_SIZE, 142, Short.MAX_VALUE) - .addComponent(blockers, javax.swing.GroupLayout.DEFAULT_SIZE, 142, Short.MAX_VALUE) - .addComponent(attackers, javax.swing.GroupLayout.DEFAULT_SIZE, 142, Short.MAX_VALUE) + .addComponent(lblDefender, javax.swing.GroupLayout.DEFAULT_SIZE, 69, Short.MAX_VALUE) + .addComponent(blockers, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(attackers, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addComponent(lblDefender) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(blockers, javax.swing.GroupLayout.PREFERRED_SIZE, 199, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(blockers, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addGap(0, 0, 0) - .addComponent(attackers, javax.swing.GroupLayout.PREFERRED_SIZE, 199, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(attackers, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) ); }// //GEN-END:initComponents diff --git a/Mage.Client/src/mage/client/game/FeedbackPanel.java b/Mage.Client/src/mage/client/game/FeedbackPanel.java index f73974e374a..3ccf5003ef0 100644 --- a/Mage.Client/src/mage/client/game/FeedbackPanel.java +++ b/Mage.Client/src/mage/client/game/FeedbackPanel.java @@ -108,9 +108,10 @@ public class FeedbackPanel extends javax.swing.JPanel { break; } this.btnSpecial.setVisible(special); + this.revalidate(); + this.repaint(); if (modal) startModal(); - this.revalidate(); } public void clear() { diff --git a/Mage.Client/src/mage/client/game/GamePane.java b/Mage.Client/src/mage/client/game/GamePane.java index c3fce45cfdd..909ef4d92e4 100644 --- a/Mage.Client/src/mage/client/game/GamePane.java +++ b/Mage.Client/src/mage/client/game/GamePane.java @@ -36,6 +36,8 @@ package mage.client.game; import mage.client.*; import java.util.UUID; +import javax.swing.event.InternalFrameEvent; +import javax.swing.event.InternalFrameListener; /** * @@ -46,6 +48,33 @@ public class GamePane extends MagePane { /** Creates new form GamePane */ public GamePane() { initComponents(); + addInternalFrameListener(new InternalFrameListener() + { + + @Override + public void internalFrameOpened(InternalFrameEvent e) { } + + @Override + public void internalFrameClosing(InternalFrameEvent e) { + gamePanel.cleanUp(); + } + + @Override + public void internalFrameClosed(InternalFrameEvent e) { } + + @Override + public void internalFrameIconified(InternalFrameEvent e) { } + + @Override + public void internalFrameDeiconified(InternalFrameEvent e) { } + + @Override + public void internalFrameActivated(InternalFrameEvent e) { } + + @Override + public void internalFrameDeactivated(InternalFrameEvent e) { } + + }); } public void showGame(UUID gameId, UUID playerId) { diff --git a/Mage.Client/src/mage/client/game/GamePanel.form b/Mage.Client/src/mage/client/game/GamePanel.form index 6ab5b9e4d2a..697a4cabbe5 100644 --- a/Mage.Client/src/mage/client/game/GamePanel.form +++ b/Mage.Client/src/mage/client/game/GamePanel.form @@ -29,7 +29,7 @@ - + @@ -62,8 +62,8 @@ - - + + @@ -71,9 +71,9 @@ - + - + @@ -116,9 +116,7 @@ - - - + @@ -160,16 +158,15 @@ - + - + - @@ -325,14 +322,11 @@ - - - + + + - - - @@ -387,44 +381,17 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + diff --git a/Mage.Client/src/mage/client/game/GamePanel.java b/Mage.Client/src/mage/client/game/GamePanel.java index 030393f8bae..629bbd54c98 100644 --- a/Mage.Client/src/mage/client/game/GamePanel.java +++ b/Mage.Client/src/mage/client/game/GamePanel.java @@ -35,7 +35,6 @@ package mage.client.game; import java.awt.GridBagConstraints; -import java.util.logging.Level; import mage.client.*; import java.util.HashMap; import java.util.Map; @@ -44,11 +43,8 @@ import java.util.logging.Logger; import javax.swing.JLayeredPane; import javax.swing.JOptionPane; import javax.swing.JPanel; -import mage.cards.decks.DeckCardLists; -import mage.client.dialog.CombatDialog; import mage.client.dialog.ExileZoneDialog; import mage.client.dialog.PickChoiceDialog; -import mage.client.dialog.PickNumberDialog; import mage.client.dialog.ShowCardsDialog; import mage.client.game.FeedbackPanel.FeedbackMode; import mage.client.remote.Session; @@ -73,14 +69,20 @@ public class GamePanel extends javax.swing.JPanel { private UUID gameId; private UUID playerId; private Session session; - private CombatDialog combat = new CombatDialog(); - private PickNumberDialog pickNumber = new PickNumberDialog(); /** Creates new form GamePanel */ public GamePanel() { initComponents(); } + public void cleanUp() { + MageFrame.getCombatDialog().hideDialog(); + MageFrame.getPickNumberDialog().hide(); + for (ExileZoneDialog exile: exiles.values()) { + exile.hide(); + } + } + public synchronized void showGame(UUID gameId, UUID playerId) { this.gameId = gameId; this.playerId = playerId; @@ -108,7 +110,6 @@ public class GamePanel extends javax.swing.JPanel { this.feedbackPanel.clear(); this.btnConcede.setVisible(false); this.btnStopWatching.setVisible(true); - this.btnCheat.setVisible(false); this.pnlReplay.setVisible(false); this.setVisible(true); this.chatPanel.clear(); @@ -124,7 +125,6 @@ public class GamePanel extends javax.swing.JPanel { this.feedbackPanel.clear(); this.btnConcede.setVisible(false); this.btnStopWatching.setVisible(false); - this.btnCheat.setVisible(false); this.pnlReplay.setVisible(true); this.setVisible(true); this.chatPanel.clear(); @@ -136,16 +136,21 @@ public class GamePanel extends javax.swing.JPanel { this.chatPanel.disconnect(); this.players.clear(); this.pnlBattlefield.removeAll(); - this.combat.hideDialog(); - MageFrame.getDesktop().remove(combat); + MageFrame.getCombatDialog().hideDialog(); +// MageFrame.getDesktop().remove(combat); this.setVisible(false); } public synchronized void init(GameView game) { - combat.init(gameId, bigCard); - MageFrame.getDesktop().add(combat, JLayeredPane.POPUP_LAYER); - combat.setLocation(500, 300); - MageFrame.getDesktop().add(pickNumber, JLayeredPane.POPUP_LAYER); + MageFrame.getCombatDialog().init(gameId, bigCard); +// MageFrame.getDesktop().add(combat, JLayeredPane.POPUP_LAYER); + MageFrame.getCombatDialog().setLocation(500, 300); +// MageFrame.getDesktop().add(pickNumber, JLayeredPane.POPUP_LAYER); + addPlayers(game); + updateGame(game); + } + + private void addPlayers(GameView game) { this.players.clear(); this.pnlBattlefield.removeAll(); //arrange players in a circle with the session player at the bottom left @@ -155,10 +160,12 @@ public class GamePanel extends javax.swing.JPanel { int col = 0; int row = 1; int playerSeat = 0; - for (PlayerView player: game.getPlayers()) { - if (playerId.equals(player.getPlayerId())) - break; - playerSeat++; + if (playerId != null) { + for (PlayerView player: game.getPlayers()) { + if (playerId.equals(player.getPlayerId())) + break; + playerSeat++; + } } PlayerView player = game.getPlayers().get(playerSeat); PlayAreaPanel sessionPlayer = new PlayAreaPanel(player, bigCard, gameId); @@ -176,6 +183,8 @@ public class GamePanel extends javax.swing.JPanel { if (oddNumber) col++; int playerNum = playerSeat + 1; + if (playerNum >= numSeats) + playerNum = 0; while (true) { if (row == 1) col++; @@ -202,7 +211,6 @@ public class GamePanel extends javax.swing.JPanel { if (playerNum == playerSeat) break; } - updateGame(game); } public synchronized void updateGame(GameView game) { @@ -229,20 +237,23 @@ public class GamePanel extends javax.swing.JPanel { if (!exiles.containsKey(exile.getId())) { ExileZoneDialog newExile = new ExileZoneDialog(); exiles.put(exile.getId(), newExile); + MageFrame.getDesktop().add(newExile, JLayeredPane.POPUP_LAYER); + newExile.show(); } exiles.get(exile.getId()).loadCards(exile, bigCard, gameId); } if (game.getCombat().size() > 0) { - combat.showDialog(game.getCombat()); + MageFrame.getCombatDialog().showDialog(game.getCombat()); } else { - combat.hideDialog(); + MageFrame.getCombatDialog().hideDialog(); } this.revalidate(); this.repaint(); } - public void ask(String question) { + public void ask(String question, GameView gameView) { + updateGame(gameView); this.feedbackPanel.getFeedback(FeedbackMode.QUESTION, question, true, false); } @@ -309,11 +320,11 @@ public class GamePanel extends javax.swing.JPanel { } public void getAmount(int min, int max, String message) { - pickNumber.showDialog(min, max, message); - if (pickNumber.isCancel()) + MageFrame.getPickNumberDialog().showDialog(min, max, message); + if (MageFrame.getPickNumberDialog().isCancel()) session.sendPlayerBoolean(gameId, false); else - session.sendPlayerInteger(gameId, pickNumber.getAmount()); + session.sendPlayerInteger(gameId, MageFrame.getPickNumberDialog().getAmount()); } public void getChoice(String message, String[] choices) { @@ -350,14 +361,12 @@ public class GamePanel extends javax.swing.JPanel { btnStopWatching = new javax.swing.JButton(); bigCard = new mage.client.cards.BigCard(); stack = new mage.client.cards.Cards(); - btnCheat = new javax.swing.JButton(); pnlReplay = new javax.swing.JPanel(); btnStopReplay = new javax.swing.JButton(); btnPreviousPlay = new javax.swing.JButton(); btnNextPlay = new javax.swing.JButton(); - pnlHand = new javax.swing.JPanel(); - hand = new mage.client.cards.Cards(); pnlBattlefield = new javax.swing.JPanel(); + hand = new mage.client.cards.Cards(); chatPanel = new mage.client.chat.ChatPanel(); jSplitPane1.setBorder(null); @@ -420,12 +429,7 @@ public class GamePanel extends javax.swing.JPanel { } }); - btnCheat.setText("?"); - btnCheat.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - btnCheatActionPerformed(evt); - } - }); + stack.setPreferredSize(new java.awt.Dimension(Config.dimensions.frameWidth, Config.dimensions.frameHeight + 25)); btnStopReplay.setText("Stop"); btnStopReplay.addActionListener(new java.awt.event.ActionListener() { @@ -492,9 +496,7 @@ public class GamePanel extends javax.swing.JPanel { .addComponent(btnConcede) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(btnStopWatching) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(btnCheat) - .addContainerGap(19, Short.MAX_VALUE)) + .addContainerGap(62, Short.MAX_VALUE)) .addComponent(bigCard, javax.swing.GroupLayout.DEFAULT_SIZE, 256, Short.MAX_VALUE) .addComponent(feedbackPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 256, Short.MAX_VALUE) .addComponent(stack, javax.swing.GroupLayout.DEFAULT_SIZE, 256, Short.MAX_VALUE) @@ -528,36 +530,21 @@ public class GamePanel extends javax.swing.JPanel { .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(feedbackPanel, javax.swing.GroupLayout.PREFERRED_SIZE, 109, javax.swing.GroupLayout.PREFERRED_SIZE) .addGap(0, 0, 0) - .addComponent(stack, javax.swing.GroupLayout.PREFERRED_SIZE, 209, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(stack, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addGap(0, 0, 0) .addComponent(bigCard, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 8, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 164, Short.MAX_VALUE) .addComponent(pnlReplay, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(pnlGameInfoLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(btnConcede) - .addComponent(btnStopWatching) - .addComponent(btnCheat))) - ); - - pnlHand.setBorder(javax.swing.BorderFactory.createEtchedBorder()); - pnlHand.setPreferredSize(new java.awt.Dimension(Config.dimensions.frameWidth, Config.dimensions.frameHeight + 25)); - - hand.setPreferredSize(new java.awt.Dimension(Config.dimensions.frameWidth, Config.dimensions.frameHeight)); - - javax.swing.GroupLayout pnlHandLayout = new javax.swing.GroupLayout(pnlHand); - pnlHand.setLayout(pnlHandLayout); - pnlHandLayout.setHorizontalGroup( - pnlHandLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(hand, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 711, Short.MAX_VALUE) - ); - pnlHandLayout.setVerticalGroup( - pnlHandLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(hand, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 209, Short.MAX_VALUE) + .addComponent(btnStopWatching))) ); pnlBattlefield.setLayout(new java.awt.GridBagLayout()); + hand.setPreferredSize(new java.awt.Dimension(Config.dimensions.frameWidth, Config.dimensions.frameHeight + 25)); + javax.swing.GroupLayout jPanel3Layout = new javax.swing.GroupLayout(jPanel3); jPanel3.setLayout(jPanel3Layout); jPanel3Layout.setHorizontalGroup( @@ -566,15 +553,15 @@ public class GamePanel extends javax.swing.JPanel { .addComponent(pnlGameInfo, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addGap(0, 0, 0) .addGroup(jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(pnlHand, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(hand, javax.swing.GroupLayout.DEFAULT_SIZE, 715, Short.MAX_VALUE) .addComponent(pnlBattlefield, javax.swing.GroupLayout.DEFAULT_SIZE, 715, Short.MAX_VALUE))) ); jPanel3Layout.setVerticalGroup( jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel3Layout.createSequentialGroup() - .addComponent(pnlBattlefield, javax.swing.GroupLayout.DEFAULT_SIZE, 634, Short.MAX_VALUE) + .addComponent(pnlBattlefield, javax.swing.GroupLayout.DEFAULT_SIZE, 794, Short.MAX_VALUE) .addGap(0, 0, 0) - .addComponent(pnlHand, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(hand, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addComponent(pnlGameInfo, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) ); @@ -591,7 +578,7 @@ public class GamePanel extends javax.swing.JPanel { ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(jSplitPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 847, Short.MAX_VALUE) + .addComponent(jSplitPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 798, Short.MAX_VALUE) ); }// //GEN-END:initComponents @@ -621,18 +608,9 @@ public class GamePanel extends javax.swing.JPanel { session.previousPlay(); }//GEN-LAST:event_btnPreviousPlayActionPerformed - private void btnCheatActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnCheatActionPerformed - try { - session.cheat(gameId, DeckCardLists.load("cheat.dck")); - } catch (Exception ex) { - logger.log(Level.SEVERE, null, ex); - } - }//GEN-LAST:event_btnCheatActionPerformed - // Variables declaration - do not modify//GEN-BEGIN:variables private mage.client.game.AbilityPicker abilityPicker; private mage.client.cards.BigCard bigCard; - private javax.swing.JButton btnCheat; private javax.swing.JButton btnConcede; private javax.swing.JButton btnNextPlay; private javax.swing.JButton btnPreviousPlay; @@ -650,7 +628,6 @@ public class GamePanel extends javax.swing.JPanel { private javax.swing.JLabel lblTurn; private javax.swing.JPanel pnlBattlefield; private javax.swing.JPanel pnlGameInfo; - private javax.swing.JPanel pnlHand; private javax.swing.JPanel pnlReplay; private mage.client.cards.Cards stack; private javax.swing.JLabel txtActivePlayer; diff --git a/Mage.Client/src/mage/client/game/PlayAreaPanel.form b/Mage.Client/src/mage/client/game/PlayAreaPanel.form index 445030f17ac..6c0820794f3 100644 --- a/Mage.Client/src/mage/client/game/PlayAreaPanel.form +++ b/Mage.Client/src/mage/client/game/PlayAreaPanel.form @@ -25,8 +25,8 @@ + - @@ -45,6 +45,7 @@ + @@ -53,7 +54,8 @@ - + + @@ -63,6 +65,14 @@ + + + + + + + + diff --git a/Mage.Client/src/mage/client/game/PlayAreaPanel.java b/Mage.Client/src/mage/client/game/PlayAreaPanel.java index 8ed3b5c493a..6c43ab47594 100644 --- a/Mage.Client/src/mage/client/game/PlayAreaPanel.java +++ b/Mage.Client/src/mage/client/game/PlayAreaPanel.java @@ -34,7 +34,13 @@ package mage.client.game; +import java.io.FileNotFoundException; +import java.io.IOException; import java.util.UUID; +import java.util.logging.Level; +import java.util.logging.Logger; +import mage.cards.decks.DeckCardLists; +import mage.client.MageFrame; import mage.client.cards.BigCard; import mage.view.PlayerView; @@ -44,7 +50,9 @@ import mage.view.PlayerView; */ public class PlayAreaPanel extends javax.swing.JPanel { - + UUID playerId; + UUID gameId; + /** Creates new form PlayAreaPanel */ public PlayAreaPanel() { initComponents(); @@ -59,6 +67,14 @@ public class PlayAreaPanel extends javax.swing.JPanel { public void init(PlayerView player, BigCard bigCard, UUID gameId) { this.playerPanel.init(gameId, player.getPlayerId(), bigCard); this.battlefieldPanel.init(gameId, bigCard); + if (MageFrame.getSession().isTestMode()) { + this.playerId = player.getPlayerId(); + this.gameId = gameId; + this.btnCheat.setVisible(true); + } + else { + this.btnCheat.setVisible(false); + } } public void update(PlayerView player) { @@ -80,17 +96,26 @@ public class PlayAreaPanel extends javax.swing.JPanel { jPanel1 = new javax.swing.JPanel(); playerPanel = new mage.client.game.PlayerPanel(); manaPool = new mage.client.game.ManaPool(); + btnCheat = new javax.swing.JButton(); jScrollPane1 = new javax.swing.JScrollPane(); battlefieldPanel = new mage.client.game.BattlefieldPanel(); jPanel1.setBorder(javax.swing.BorderFactory.createBevelBorder(javax.swing.border.BevelBorder.RAISED)); + btnCheat.setText("Cheat"); + btnCheat.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + btnCheatActionPerformed(evt); + } + }); + javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1); jPanel1.setLayout(jPanel1Layout); jPanel1Layout.setHorizontalGroup( jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(manaPool, javax.swing.GroupLayout.DEFAULT_SIZE, 116, Short.MAX_VALUE) .addComponent(playerPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(btnCheat, javax.swing.GroupLayout.DEFAULT_SIZE, 116, Short.MAX_VALUE) ); jPanel1Layout.setVerticalGroup( jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -98,7 +123,8 @@ public class PlayAreaPanel extends javax.swing.JPanel { .addComponent(playerPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addGap(0, 0, 0) .addComponent(manaPool, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap(14, Short.MAX_VALUE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 17, Short.MAX_VALUE) + .addComponent(btnCheat)) ); jScrollPane1.setViewportView(battlefieldPanel); @@ -114,14 +140,27 @@ public class PlayAreaPanel extends javax.swing.JPanel { ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jScrollPane1, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 278, Short.MAX_VALUE) .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(jScrollPane1, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 252, Short.MAX_VALUE) ); }// //GEN-END:initComponents + private void btnCheatActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnCheatActionPerformed + try { + MageFrame.getSession().cheat(gameId, playerId, DeckCardLists.load("cheat.dck")); + } catch (FileNotFoundException ex) { + Logger.getLogger(PlayAreaPanel.class.getName()).log(Level.SEVERE, null, ex); + } catch (IOException ex) { + Logger.getLogger(PlayAreaPanel.class.getName()).log(Level.SEVERE, null, ex); + } catch (ClassNotFoundException ex) { + Logger.getLogger(PlayAreaPanel.class.getName()).log(Level.SEVERE, null, ex); + } + }//GEN-LAST:event_btnCheatActionPerformed + // Variables declaration - do not modify//GEN-BEGIN:variables private mage.client.game.BattlefieldPanel battlefieldPanel; + private javax.swing.JButton btnCheat; private javax.swing.JPanel jPanel1; private javax.swing.JScrollPane jScrollPane1; private mage.client.game.ManaPool manaPool; diff --git a/Mage.Client/src/mage/client/remote/Client.java b/Mage.Client/src/mage/client/remote/Client.java index d08ce3e941c..ae3a8406a0b 100644 --- a/Mage.Client/src/mage/client/remote/Client.java +++ b/Mage.Client/src/mage/client/remote/Client.java @@ -65,91 +65,90 @@ public class Client implements CallbackClient { @Override public synchronized void processCallback(ClientCallback callback) { - if (callback.getMessageId() > messageId) { - messageId = callback.getMessageId(); - logger.info(callback.getMessageId() + " - " + callback.getMethod()); - if (callback.getMethod().equals("startGame")) { - UUID[] data = (UUID[]) callback.getData(); - gameStarted(data[0], data[1]); + logger.info(callback.getMessageId() + " - " + callback.getMethod()); + if (callback.getMethod().equals("startGame")) { + UUID[] data = (UUID[]) callback.getData(); + gameStarted(data[0], data[1]); + } + else if (callback.getMethod().equals("replayGame")) { + replayGame(); + } + else if (callback.getMethod().equals("watchGame")) { + watchGame((UUID) callback.getData()); + } + else if (callback.getMethod().equals("chatMessage")) { + ChatMessage message = (ChatMessage) callback.getData(); + session.getChats().get(message.getChatId()).receiveMessage(message.getMessage(), message.getColor()); + } + else if (callback.getMethod().equals("replayInit")) { + session.getGame().init((GameView) callback.getData()); + } + else if (callback.getMethod().equals("replayDone")) { + session.getGame().modalMessage((String) callback.getData()); + session.getGame().hideGame(); + } + else if (callback.getMethod().equals("replayUpdate")) { + session.getGame().updateGame((GameView) callback.getData()); + } + else if (callback.getMethod().equals("gameInit")) { + session.getGame().init((GameView) callback.getData()); + } + else if (callback.getMethod().equals("gameOver")) { + session.getGame().modalMessage((String) callback.getData()); + session.getGame().hideGame(); + } + else if (callback.getMethod().equals("gameAsk")) { + GameClientMessage message = (GameClientMessage) callback.getData(); + session.getGame().ask(message.getMessage(), message.getGameView()); + } + else if (callback.getMethod().equals("gameTarget")) { + GameClientMessage message = (GameClientMessage) callback.getData(); + if (message.isFlag()) { + session.getGame().inform(message.getMessage(), message.getCardsView(), message.getGameView()); + } else { + session.getGame().cancel(message.getMessage(), message.getCardsView(), message.getGameView()); } - else if (callback.getMethod().equals("replayGame")) { - replayGame(); - } - else if (callback.getMethod().equals("watchGame")) { - watchGame((UUID) callback.getData()); - } - else if (callback.getMethod().equals("chatMessage")) { - ChatMessage message = (ChatMessage) callback.getData(); - session.getChats().get(message.getChatId()).receiveMessage(message.getMessage()); - } - else if (callback.getMethod().equals("replayInit")) { - session.getGame().init((GameView) callback.getData()); - } - else if (callback.getMethod().equals("replayDone")) { - session.getGame().modalMessage((String) callback.getData()); - session.getGame().hideGame(); - } - else if (callback.getMethod().equals("replayUpdate")) { - session.getGame().updateGame((GameView) callback.getData()); - } - else if (callback.getMethod().equals("gameInit")) { - session.getGame().init((GameView) callback.getData()); - } - else if (callback.getMethod().equals("gameUpdate")) { - session.getGame().updateGame((GameView) callback.getData()); - } - else if (callback.getMethod().equals("gameInform")) { + } + else if (callback.getMethod().equals("gameSelect")) { + GameClientMessage message = (GameClientMessage) callback.getData(); + session.getGame().select(message.getMessage(), message.getGameView()); + } + else if (callback.getMethod().equals("gameChooseAbility")) { + session.getGame().pickAbility((AbilityPickerView) callback.getData()); + } + else if (callback.getMethod().equals("gameChoose")) { + GameClientMessage message = (GameClientMessage) callback.getData(); + session.getGame().getChoice(message.getMessage(), message.getStrings()); + } + else if (callback.getMethod().equals("gamePlayMana")) { + GameClientMessage message = (GameClientMessage) callback.getData(); + session.getGame().playMana(message.getMessage(), message.getGameView()); + } + else if (callback.getMethod().equals("gamePlayXMana")) { + GameClientMessage message = (GameClientMessage) callback.getData(); + session.getGame().playXMana(message.getMessage(), message.getGameView()); + } + else if (callback.getMethod().equals("gameSelectAmount")) { + GameClientMessage message = (GameClientMessage) callback.getData(); + session.getGame().getAmount(message.getMin(), message.getMax(), message.getMessage()); + } + else if (callback.getMethod().equals("gameReveal")) { + GameClientMessage message = (GameClientMessage) callback.getData(); + session.getGame().revealCards(message.getMessage(), message.getCardsView()); + } + else if (callback.getMethod().equals("gameUpdate")) { + session.getGame().updateGame((GameView) callback.getData()); + } + else if (callback.getMethod().equals("gameInform")) { + if (callback.getMessageId() > messageId) { GameClientMessage message = (GameClientMessage) callback.getData(); session.getGame().inform(message.getMessage(), null, message.getGameView()); } - else if (callback.getMethod().equals("gameOver")) { - session.getGame().modalMessage((String) callback.getData()); - session.getGame().hideGame(); - } - else if (callback.getMethod().equals("gameAsk")) { - GameClientMessage message = (GameClientMessage) callback.getData(); - session.getGame().updateGame(message.getGameView()); - session.getGame().ask(message.getMessage()); - } - else if (callback.getMethod().equals("gameTarget")) { - GameClientMessage message = (GameClientMessage) callback.getData(); - if (message.isFlag()) { - session.getGame().inform(message.getMessage(), message.getCardsView(), message.getGameView()); - } else { - session.getGame().cancel(message.getMessage(), message.getCardsView(), message.getGameView()); - } - } - else if (callback.getMethod().equals("gameSelect")) { - GameClientMessage message = (GameClientMessage) callback.getData(); - session.getGame().select(message.getMessage(), message.getGameView()); - } - else if (callback.getMethod().equals("gameChooseAbility")) { - session.getGame().pickAbility((AbilityPickerView) callback.getData()); - } - else if (callback.getMethod().equals("gameChoose")) { - GameClientMessage message = (GameClientMessage) callback.getData(); - session.getGame().getChoice(message.getMessage(), message.getStrings()); - } - else if (callback.getMethod().equals("gamePlayMana")) { - GameClientMessage message = (GameClientMessage) callback.getData(); - session.getGame().playMana(message.getMessage(), message.getGameView()); - } - else if (callback.getMethod().equals("gamePlayXMana")) { - GameClientMessage message = (GameClientMessage) callback.getData(); - session.getGame().playXMana(message.getMessage(), message.getGameView()); - } - else if (callback.getMethod().equals("gameSelectAmount")) { - GameClientMessage message = (GameClientMessage) callback.getData(); - session.getGame().getAmount(message.getMin(), message.getMax(), message.getMessage()); - } - else if (callback.getMethod().equals("gameReveal")) { - GameClientMessage message = (GameClientMessage) callback.getData(); - session.getGame().revealCards(message.getMessage(), message.getCardsView()); + else { + logger.warning("message out of sequence - ignoring"); } } - else { - logger.warning("message out of sequence - ignoring"); - } + messageId = callback.getMessageId(); } public UUID getId() throws RemoteException { diff --git a/Mage.Client/src/mage/client/remote/Session.java b/Mage.Client/src/mage/client/remote/Session.java index e2920556698..f25cc024d4f 100644 --- a/Mage.Client/src/mage/client/remote/Session.java +++ b/Mage.Client/src/mage/client/remote/Session.java @@ -50,6 +50,7 @@ import mage.client.util.Config; import mage.game.GameException; import mage.interfaces.MageException; import mage.interfaces.Server; +import mage.interfaces.ServerState; import mage.interfaces.callback.CallbackClientDaemon; import mage.util.Logging; import mage.view.GameTypeView; @@ -68,9 +69,10 @@ public class Session { private Client client; private String userName; private MageFrame frame; - private String[] playerTypes; - private List gameTypes; - private String[] deckTypes; + private ServerState serverState; +// private String[] playerTypes; +// private List gameTypes; +// private String[] deckTypes; private Map chats = new HashMap(); private GamePanel game; private CallbackClientDaemon callbackDaemon; @@ -91,9 +93,7 @@ public class Session { this.client = new Client(this, frame, userName); sessionId = server.registerClient(userName, client.getId()); callbackDaemon = new CallbackClientDaemon(sessionId, client, server); - playerTypes = server.getPlayerTypes(); - gameTypes = server.getGameTypes(); - deckTypes = server.getDeckTypes(); + serverState = server.getServerState(); logger.info("Connected to RMI server at " + serverName + ":" + port); frame.setStatusText("Connected to " + serverName + ":" + port + " "); frame.enableButtons(); @@ -127,15 +127,19 @@ public class Session { } public String[] getPlayerTypes() { - return playerTypes; + return serverState.getPlayerTypes(); } public List getGameTypes() { - return gameTypes; + return serverState.getGameTypes(); } public String[] getDeckTypes() { - return deckTypes; + return serverState.getDeckTypes(); + } + + public boolean isTestMode() { + return serverState.isTestMode(); } public Map getChats() { @@ -229,9 +233,9 @@ public class Session { return false; } - public boolean joinTable(UUID roomId, UUID tableId, int seat, String playerName, DeckCardLists deckList) { + public boolean joinTable(UUID roomId, UUID tableId, String playerName, DeckCardLists deckList) { try { - return server.joinTable(sessionId, roomId, tableId, seat, playerName, deckList); + return server.joinTable(sessionId, roomId, tableId, playerName, deckList); } catch (RemoteException ex) { handleRemoteException(ex); } catch (MageException ex) { @@ -506,9 +510,9 @@ public class Session { return false; } - public boolean cheat(UUID gameId, DeckCardLists deckList) { + public boolean cheat(UUID gameId, UUID playerId, DeckCardLists deckList) { try { - server.cheat(gameId, sessionId, deckList); + server.cheat(gameId, sessionId, playerId, deckList); return true; } catch (RemoteException ex) { handleRemoteException(ex); diff --git a/Mage.Client/src/mage/client/table/TablePlayerPanel.java b/Mage.Client/src/mage/client/table/TablePlayerPanel.java index 83d74f66284..0d2cd254d18 100644 --- a/Mage.Client/src/mage/client/table/TablePlayerPanel.java +++ b/Mage.Client/src/mage/client/table/TablePlayerPanel.java @@ -72,9 +72,9 @@ public class TablePlayerPanel extends javax.swing.JPanel { this.lblPlayerNum.setText("Player " + playerNum); } - public boolean joinTable(UUID roomId, UUID tableId, int seatNum) throws FileNotFoundException, IOException, ClassNotFoundException { + public boolean joinTable(UUID roomId, UUID tableId) throws FileNotFoundException, IOException, ClassNotFoundException { if (!this.cbPlayerType.getSelectedItem().equals("Human")) { - return session.joinTable(roomId, tableId, seatNum, this.newPlayerPanel.getPlayerName(), DeckCardLists.load(this.newPlayerPanel.getDeckFile())); + return session.joinTable(roomId, tableId, this.newPlayerPanel.getPlayerName(), DeckCardLists.load(this.newPlayerPanel.getDeckFile())); } return true; } diff --git a/Mage.Client/src/mage/client/table/TablesPanel.java b/Mage.Client/src/mage/client/table/TablesPanel.java index 3c610bc2e37..3771b0bf92f 100644 --- a/Mage.Client/src/mage/client/table/TablesPanel.java +++ b/Mage.Client/src/mage/client/table/TablesPanel.java @@ -266,14 +266,12 @@ public class TablesPanel extends javax.swing.JPanel implements Observer { session.joinTable( roomId, table.getTableId(), - 0, "Human", DeckCardLists.load("test.dck") ); session.joinTable( roomId, table.getTableId(), - 1, "Computer", DeckCardLists.load("test.dck") ); diff --git a/Mage.Common/src/mage/interfaces/Server.java b/Mage.Common/src/mage/interfaces/Server.java index f95eb6bd971..330d7d1994d 100644 --- a/Mage.Common/src/mage/interfaces/Server.java +++ b/Mage.Common/src/mage/interfaces/Server.java @@ -46,17 +46,14 @@ import mage.view.TableView; */ public interface Server extends Remote, CallbackServer { -// public String getClientIp() throws RemoteException, MageException; public UUID registerClient(String userName, UUID clientId) throws RemoteException, MageException; public void deregisterClient(UUID sessionId) throws RemoteException, MageException; - public List getGameTypes() throws RemoteException, MageException; - public String[] getPlayerTypes() throws RemoteException, MageException; - public String[] getDeckTypes() throws RemoteException, MageException; + public ServerState getServerState() throws RemoteException, MageException; //table methods public TableView createTable(UUID sessionId, UUID roomId, String gameType, String deckType, List playerTypes, MultiplayerAttackOption attackOption, RangeOfInfluence range) throws RemoteException, MageException; - public boolean joinTable(UUID sessionId, UUID roomId, UUID tableId, int seatNum, String name, DeckCardLists deckList) throws RemoteException, MageException, GameException; + public boolean joinTable(UUID sessionId, UUID roomId, UUID tableId, String name, DeckCardLists deckList) throws RemoteException, MageException, GameException; public boolean watchTable(UUID sessionId, UUID roomId, UUID tableId) throws RemoteException, MageException; public boolean replayTable(UUID sessionId, UUID roomId, UUID tableId) throws RemoteException, MageException; public void leaveTable(UUID sessionId, UUID roomId, UUID tableId) throws RemoteException, MageException; @@ -95,6 +92,6 @@ public interface Server extends Remote, CallbackServer { public void previousPlay(UUID sessionId) throws RemoteException, MageException; //test methods - public void cheat(UUID gameId, UUID sessionId, DeckCardLists deckList) throws RemoteException, MageException; + public void cheat(UUID gameId, UUID sessionId, UUID playerId, DeckCardLists deckList) throws RemoteException, MageException; } diff --git a/Mage.Common/src/mage/interfaces/ServerState.java b/Mage.Common/src/mage/interfaces/ServerState.java new file mode 100644 index 00000000000..403616c12f5 --- /dev/null +++ b/Mage.Common/src/mage/interfaces/ServerState.java @@ -0,0 +1,69 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.interfaces; + +import java.io.Serializable; +import java.util.List; +import mage.view.GameTypeView; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class ServerState implements Serializable { + + private List gameTypes; + private String[] playerTypes; + private String[] deckTypes; + private boolean testMode; + + public ServerState(List gameTypes, String[] playerTypes, String[] deckTypes, boolean testMode) { + this.gameTypes = gameTypes; + this.playerTypes = playerTypes; + this.deckTypes = deckTypes; + this.testMode = testMode; + } + + public List getGameTypes() { + return gameTypes; + } + + public String[] getPlayerTypes() { + return playerTypes; + } + + public String[] getDeckTypes() { + return deckTypes; + } + + public boolean isTestMode() { + return testMode; + } + +} diff --git a/Mage.Common/src/mage/view/CardView.java b/Mage.Common/src/mage/view/CardView.java index 33608dee53d..de12cf57b84 100644 --- a/Mage.Common/src/mage/view/CardView.java +++ b/Mage.Common/src/mage/view/CardView.java @@ -78,7 +78,7 @@ public class CardView implements Serializable { } protected CardView() { - + } protected List formatRules(List rules) { diff --git a/Mage.Common/src/mage/view/CardsView.java b/Mage.Common/src/mage/view/CardsView.java index 586e986a4d6..02d3a768abf 100644 --- a/Mage.Common/src/mage/view/CardsView.java +++ b/Mage.Common/src/mage/view/CardsView.java @@ -28,40 +28,39 @@ package mage.view; -import java.util.ArrayList; import java.util.Collection; -import mage.MageObject; +import java.util.HashMap; +import java.util.UUID; import mage.abilities.Ability; import mage.cards.Card; -import mage.cards.Cards; -import mage.game.Game; +import mage.game.GameState; /** * * @author BetaSteward_at_googlemail.com */ -public class CardsView extends ArrayList { +public class CardsView extends HashMap { public CardsView() {} public CardsView(Collection cards) { for (Card card: cards) { - this.add(new CardView(card)); + this.put(card.getId(), new CardView(card)); } } - public CardsView(Cards cards) { - if (cards != null) - for (Card card: cards.values()) { - this.add(new CardView(card)); - } - } +// public CardsView(Cards cards) { +// if (cards != null) +// for (Card card: cards.values()) { +// this.add(new CardView(card)); +// } +// } - public CardsView(Collection abilities, Game game) { + public CardsView(Collection abilities, GameState state) { for (Ability ability: abilities) { - String sourceName = game.getPermanent(ability.getSourceId()).getName(); - this.add(new AbilityView(ability, sourceName)); + String sourceName = state.getPermanent(ability.getSourceId()).getName(); + this.put(ability.getId(), new AbilityView(ability, sourceName)); } } diff --git a/Mage.Common/src/mage/view/ChatMessage.java b/Mage.Common/src/mage/view/ChatMessage.java index 5be2789457e..6645072aa5e 100644 --- a/Mage.Common/src/mage/view/ChatMessage.java +++ b/Mage.Common/src/mage/view/ChatMessage.java @@ -39,10 +39,16 @@ public class ChatMessage implements Serializable { private UUID chatId; private String message; + private MessageColor color; - public ChatMessage(UUID chatId, String message) { + public enum MessageColor { + BLACK, RED, GREEN, BLUE, ORANGE; + } + + public ChatMessage(UUID chatId, String message, MessageColor color) { this.chatId = chatId; this.message = message; + this.color = color; } public String getMessage() { @@ -52,4 +58,8 @@ public class ChatMessage implements Serializable { public UUID getChatId() { return chatId; } + + public MessageColor getColor() { + return color; + } } diff --git a/Mage.Common/src/mage/view/CombatGroupView.java b/Mage.Common/src/mage/view/CombatGroupView.java index 92675ea9119..a757267e9d0 100644 --- a/Mage.Common/src/mage/view/CombatGroupView.java +++ b/Mage.Common/src/mage/view/CombatGroupView.java @@ -30,6 +30,7 @@ package mage.view; import java.io.Serializable; import java.util.UUID; +import mage.game.Game; import mage.game.GameState; import mage.game.combat.CombatGroup; import mage.game.permanent.Permanent; @@ -45,20 +46,24 @@ public class CombatGroupView implements Serializable { private CardsView blockers = new CardsView(); private String defenderName; - public CombatGroupView(CombatGroup combatGroup, GameState game) { - Player player = game.getPlayer(combatGroup.getDefenderId()); + public CombatGroupView(CombatGroup combatGroup, GameState state) { + Player player = state.getPlayer(combatGroup.getDefenderId()); if (player != null) { this.defenderName = player.getName(); } else { - Permanent perm = game.getPermanent(combatGroup.getDefenderId()); + Permanent perm = state.getPermanent(combatGroup.getDefenderId()); this.defenderName = perm.getName(); } for (UUID id: combatGroup.getAttackers()) { - attackers.add(new PermanentView(game.getPermanent(id))); + Permanent attacker = state.getPermanent(id); + if (attacker != null) + attackers.put(id, new PermanentView(attacker)); } for (UUID id: combatGroup.getBlockers()) { - blockers.add(new PermanentView(game.getPermanent(id))); + Permanent blocker = state.getPermanent(id); + if (blocker != null) + blockers.put(id, new PermanentView(blocker)); } } diff --git a/Mage.Common/src/mage/view/ExileView.java b/Mage.Common/src/mage/view/ExileView.java index 8497af06f17..be0ca1e660a 100644 --- a/Mage.Common/src/mage/view/ExileView.java +++ b/Mage.Common/src/mage/view/ExileView.java @@ -31,6 +31,7 @@ package mage.view; import java.util.UUID; import mage.cards.Card; import mage.game.ExileZone; +import mage.game.Game; /** * @@ -41,11 +42,11 @@ public class ExileView extends CardsView { private String name; private UUID id; - public ExileView(ExileZone exileZone) { + public ExileView(ExileZone exileZone, Game game) { this.name = exileZone.getName(); this.id = exileZone.getId(); - for (Card card: exileZone.values()) { - this.add(new CardView(card)); + for (Card card: exileZone.getCards(game)) { + this.put(card.getId(), new CardView(card)); } } diff --git a/Mage.Common/src/mage/view/GameView.java b/Mage.Common/src/mage/view/GameView.java index ea57709d224..730939317be 100644 --- a/Mage.Common/src/mage/view/GameView.java +++ b/Mage.Common/src/mage/view/GameView.java @@ -35,6 +35,7 @@ import mage.Constants.PhaseStep; import mage.Constants.TurnPhase; import mage.MageObject; import mage.game.ExileZone; +import mage.game.Game; import mage.game.GameState; import mage.game.combat.CombatGroup; import mage.game.stack.Spell; @@ -60,40 +61,40 @@ public class GameView implements Serializable { private int turn; private boolean special = false; - public GameView(GameState game) { - for (Player player: game.getPlayers().values()) { - players.add(new PlayerView(player, game)); + public GameView(GameState state, Game game) { + for (Player player: state.getPlayers().values()) { + players.add(new PlayerView(player, state, game)); } - for (StackObject stackObject: game.getStack()) { + for (StackObject stackObject: state.getStack()) { if (stackObject instanceof StackAbility) { MageObject object = game.getObject(stackObject.getSourceId()); if (object != null) - stack.add(new StackAbilityView((StackAbility)stackObject, object.getName())); + stack.put(stackObject.getId(), new StackAbilityView((StackAbility)stackObject, object.getName())); else - stack.add(new StackAbilityView((StackAbility)stackObject, "")); + stack.put(null, new StackAbilityView((StackAbility)stackObject, "")); } else { - stack.add(new CardView((Spell)stackObject)); + stack.put(stackObject.getId(), new CardView((Spell)stackObject)); } } - for (ExileZone exileZone: game.getExile().getExileZones()) { - exiles.add(new ExileView(exileZone)); + for (ExileZone exileZone: state.getExile().getExileZones()) { + exiles.add(new ExileView(exileZone, game)); } - this.phase = game.getTurn().getPhaseType(); - this.step = game.getTurn().getStepType(); - this.turn = game.getTurnNum(); - if (game.getActivePlayerId() != null) - this.activePlayerName = game.getPlayer(game.getActivePlayerId()).getName(); + this.phase = state.getTurn().getPhaseType(); + this.step = state.getTurn().getStepType(); + this.turn = state.getTurnNum(); + if (state.getActivePlayerId() != null) + this.activePlayerName = state.getPlayer(state.getActivePlayerId()).getName(); else this.activePlayerName = ""; - if (game.getPriorityPlayerId() != null) - this.priorityPlayerName = game.getPlayer(game.getPriorityPlayerId()).getName(); + if (state.getPriorityPlayerId() != null) + this.priorityPlayerName = state.getPlayer(state.getPriorityPlayerId()).getName(); else this.priorityPlayerName = ""; - for (CombatGroup combatGroup: game.getCombat().getGroups()) { - combat.add(new CombatGroupView(combatGroup, game)); + for (CombatGroup combatGroup: state.getCombat().getGroups()) { + combat.add(new CombatGroupView(combatGroup, state)); } - this.special = game.getSpecialActions().getControlledBy(game.getPriorityPlayerId()).size() > 0; + this.special = state.getSpecialActions().getControlledBy(state.getPriorityPlayerId()).size() > 0; } public List getPlayers() { diff --git a/Mage.Common/src/mage/view/PlayerView.java b/Mage.Common/src/mage/view/PlayerView.java index 360c69f7273..933d0ae9c1b 100644 --- a/Mage.Common/src/mage/view/PlayerView.java +++ b/Mage.Common/src/mage/view/PlayerView.java @@ -33,6 +33,7 @@ import java.util.HashMap; import java.util.Map; import java.util.UUID; import mage.cards.Card; +import mage.game.Game; import mage.game.GameState; import mage.game.permanent.Permanent; import mage.players.Player; @@ -54,33 +55,33 @@ public class PlayerView implements Serializable { private CardsView graveyard = new CardsView(); private Map battlefield = new HashMap(); - public PlayerView(Player player, GameState game) { + public PlayerView(Player player, GameState state, Game game) { this.playerId = player.getId(); this.name = player.getName(); this.life = player.getLife(); this.libraryCount = player.getLibrary().size(); this.handCount = player.getHand().size(); this.manaPool = new ManaPoolView(player.getManaPool()); - this.isActive = (player.getId().equals(game.getActivePlayerId())); + this.isActive = (player.getId().equals(state.getActivePlayerId())); this.hasLeft = player.hasLeft(); - for (Card card: player.getGraveyard().values()) { - graveyard.add(new CardView(card)); + for (Card card: player.getGraveyard().getCards(game)) { + graveyard.put(card.getId(), new CardView(card)); } - for (Permanent permanent: game.getBattlefield().getAllPermanents()) { - if (showInBattlefield(permanent, game)) { + for (Permanent permanent: state.getBattlefield().getAllPermanents()) { + if (showInBattlefield(permanent, state)) { PermanentView view = new PermanentView(permanent); battlefield.put(view.getId(), view); } } } - private boolean showInBattlefield(Permanent permanent, GameState game) { + private boolean showInBattlefield(Permanent permanent, GameState state) { //show permanents controlled by player or attachments to permanents controlled by player if (permanent.getAttachedTo() == null) return permanent.getControllerId().equals(playerId); else - return game.getPermanent(permanent.getAttachedTo()).getControllerId().equals(playerId); + return state.getPermanent(permanent.getAttachedTo()).getControllerId().equals(playerId); } public int getLife() { diff --git a/Mage.Deck.Constructed/src/mage/deck/Constructed.java b/Mage.Deck.Constructed/src/mage/deck/Constructed.java index a6d0d744cd6..94b21ba9edd 100644 --- a/Mage.Deck.Constructed/src/mage/deck/Constructed.java +++ b/Mage.Deck.Constructed/src/mage/deck/Constructed.java @@ -58,8 +58,8 @@ public class Constructed extends DeckValidatorImpl { List basicLandNames = new ArrayList(Arrays.asList("Forest", "Island", "Mountain", "Swamp", "Plains")); Map counts = new HashMap(); - countCards(counts, deck.getCards().values()); - countCards(counts, deck.getSideboard().values()); + countCards(counts, deck.getCards()); + countCards(counts, deck.getSideboard()); for (Entry entry: counts.entrySet()) { if (entry.getValue() > 4) { if (!basicLandNames.contains(entry.getKey())) { diff --git a/Mage.Game.FreeForAll/src/mage/game/FreeForAll.java b/Mage.Game.FreeForAll/src/mage/game/FreeForAll.java index 2c7d423ec8e..39d20ac0bd2 100644 --- a/Mage.Game.FreeForAll/src/mage/game/FreeForAll.java +++ b/Mage.Game.FreeForAll/src/mage/game/FreeForAll.java @@ -41,7 +41,7 @@ import mage.players.Player; * * @author BetaSteward_at_googlemail.com */ -public class FreeForAll extends GameImpl { +public class FreeForAll extends GameImpl { private int numPlayers; private List mulliganed = new ArrayList(); @@ -50,6 +50,14 @@ public class FreeForAll extends GameImpl { super(attackOption, range); } + public FreeForAll(final FreeForAll game) { + super(game); + this.numPlayers = game.numPlayers; + for (UUID playerId: game.mulliganed) { + mulliganed.add(playerId); + } + } + @Override public GameType getGameType() { return new FreeForAllType(); @@ -88,11 +96,16 @@ public class FreeForAll extends GameImpl { numCards += 1; mulliganed.add(playerId); } - player.getLibrary().addAll(player.getHand()); + player.getLibrary().addAll(player.getHand().getCards(this)); player.getHand().clear(); player.shuffleLibrary(this); player.drawCards(numCards - 1, this); fireInformEvent(player.getName() + " mulligans down to " + Integer.toString(numCards - 1) + " cards"); } + @Override + public FreeForAll copy() { + return new FreeForAll(this); + } + } diff --git a/Mage.Game.TwoPlayerDuel/src/mage/game/TwoPlayerDuel.java b/Mage.Game.TwoPlayerDuel/src/mage/game/TwoPlayerDuel.java index 24b52ea5b21..2a71e6085b3 100644 --- a/Mage.Game.TwoPlayerDuel/src/mage/game/TwoPlayerDuel.java +++ b/Mage.Game.TwoPlayerDuel/src/mage/game/TwoPlayerDuel.java @@ -36,12 +36,16 @@ import mage.Constants.PhaseStep; import mage.Constants.RangeOfInfluence; import mage.game.turn.TurnMod; -public class TwoPlayerDuel extends GameImpl { +public class TwoPlayerDuel extends GameImpl { public TwoPlayerDuel(MultiplayerAttackOption attackOption, RangeOfInfluence range) { super(MultiplayerAttackOption.LEFT, RangeOfInfluence.ALL); } + public TwoPlayerDuel(final TwoPlayerDuel game) { + super(game); + } + @Override public GameType getGameType() { return new TwoPlayerDuelType(); @@ -88,4 +92,9 @@ public class TwoPlayerDuel extends GameImpl { return opponents; } + @Override + public TwoPlayerDuel copy() { + return new TwoPlayerDuel(this); + } + } diff --git a/Mage.Player.AI/nbproject/project.properties b/Mage.Player.AI/nbproject/project.properties index bcf246a1b0b..c4a776de1f4 100644 --- a/Mage.Player.AI/nbproject/project.properties +++ b/Mage.Player.AI/nbproject/project.properties @@ -20,6 +20,7 @@ debug.test.classpath=\ dist.dir=dist dist.jar=${dist.dir}/Mage.Player.AI.jar dist.javadoc.dir=${dist.dir}/javadoc +endorsed.classpath= excludes= file.reference.Mage.AI-src=src file.reference.Mage.AI-test=test diff --git a/Mage.Player.AI/src/mage/player/ai/ComputerPlayer.java b/Mage.Player.AI/src/mage/player/ai/ComputerPlayer.java index 05658d0a022..a9413605314 100644 --- a/Mage.Player.AI/src/mage/player/ai/ComputerPlayer.java +++ b/Mage.Player.AI/src/mage/player/ai/ComputerPlayer.java @@ -40,14 +40,18 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Set; import java.util.TreeMap; import java.util.UUID; +import java.util.logging.Level; import java.util.logging.Logger; import mage.Constants.CardType; import mage.Constants.Outcome; import mage.Constants.RangeOfInfluence; import mage.Constants.Zone; +import mage.MageObject; import mage.Mana; +import mage.abilities.Ability; import mage.abilities.ActivatedAbility; import mage.abilities.TriggeredAbilities; import mage.abilities.TriggeredAbility; @@ -103,10 +107,10 @@ import mage.util.TreeNode; * * @author BetaSteward_at_googlemail.com */ -public class ComputerPlayer extends PlayerImpl implements Player { +public class ComputerPlayer> extends PlayerImpl implements Player { private final static transient Logger logger = Logging.getLogger(ComputerPlayer.class.getName()); - private boolean abort = false; + private boolean abort; private transient Map unplayable = new TreeMap(); private transient List playableNonInstant = new ArrayList(); private transient List playableInstant = new ArrayList(); @@ -121,31 +125,37 @@ public class ComputerPlayer extends PlayerImpl implements Player { super(id); } + public ComputerPlayer(final ComputerPlayer player) { + super(player); + this.abort = player.abort; + } + @Override public boolean chooseMulligan(Game game) { logger.fine("chooseMulligan"); if (hand.size() < 6) return false; - List lands = hand.getCards(new FilterLandCard()); + Set lands = hand.getCards(new FilterLandCard(), game); if (lands.size() < 2 || lands.size() > hand.size() - 2) return true; return false; } @Override - public boolean chooseTarget(Outcome outcome, Target target, Game game) { - logger.fine("chooseTarget: " + outcome.toString() + ":" + target.toString()); + public boolean chooseTarget(Outcome outcome, Target target, Ability source, Game game) { + if (logger.isLoggable(Level.FINE)) + logger.fine("chooseTarget: " + outcome.toString() + ":" + target.toString()); UUID opponentId = game.getOpponents(playerId).iterator().next(); if (target instanceof TargetPlayer) { if (outcome.isGood()) { - if (target.canTarget(playerId, game)) { - target.addTarget(playerId, game); + if (target.canTarget(playerId, source, game)) { + target.addTarget(playerId, source, game); return true; } } else { - if (target.canTarget(playerId, game)) { - target.addTarget(opponentId, game); + if (target.canTarget(playerId, source, game)) { + target.addTarget(opponentId, source, game); return true; } } @@ -154,15 +164,15 @@ public class ComputerPlayer extends PlayerImpl implements Player { findPlayables(game); if (unplayable.size() > 0) { for (int i = unplayable.size() - 1; i >= 0; i--) { - if (target.canTarget(unplayable.values().toArray(new Card[0])[i].getId(), game)) { - target.addTarget(unplayable.values().toArray(new Card[0])[i].getId(), game); + if (target.canTarget(unplayable.values().toArray(new Card[0])[i].getId(), source, game)) { + target.addTarget(unplayable.values().toArray(new Card[0])[i].getId(), source, game); return true; } } } if (hand.size() > 0) { - if (target.canTarget(hand.keySet().toArray(new UUID[0])[0], game)) { - target.addTarget(hand.keySet().toArray(new UUID[0])[0], game); + if (target.canTarget(hand.toArray(new UUID[0])[0], source, game)) { + target.addTarget(hand.toArray(new UUID[0])[0], source, game); return true; } } @@ -173,8 +183,8 @@ public class ComputerPlayer extends PlayerImpl implements Player { if (!outcome.isGood()) Collections.reverse(targets); for (Permanent permanent: targets) { - if (target.canTarget(permanent.getId(), game)) { - target.addTarget(permanent.getId(), game); + if (target.canTarget(permanent.getId(), source, game)) { + target.addTarget(permanent.getId(), source, game); return true; } } @@ -188,8 +198,8 @@ public class ComputerPlayer extends PlayerImpl implements Player { targets = threats(opponentId, ((TargetPermanent)target).getFilter(), game); } for (Permanent permanent: targets) { - if (target.canTarget(permanent.getId(), game)) { - target.addTarget(permanent.getId(), game); + if (target.canTarget(permanent.getId(), source, game)) { + target.addTarget(permanent.getId(), source, game); return true; } } @@ -198,12 +208,13 @@ public class ComputerPlayer extends PlayerImpl implements Player { } @Override - public boolean chooseTargetAmount(Outcome outcome, TargetAmount target, Game game) { - logger.fine("chooseTarget: " + outcome.toString() + ":" + target.toString()); + public boolean chooseTargetAmount(Outcome outcome, TargetAmount target, Ability source, Game game) { + if (logger.isLoggable(Level.FINE)) + logger.fine("chooseTarget: " + outcome.toString() + ":" + target.toString()); UUID opponentId = game.getOpponents(playerId).iterator().next(); if (target instanceof TargetCreatureOrPlayerAmount) { if (game.getPlayer(opponentId).getLife() <= target.getAmountRemaining()) { - target.addTarget(opponentId, target.getAmountRemaining(), game); + target.addTarget(opponentId, target.getAmountRemaining(), source, game); return true; } List targets; @@ -214,9 +225,9 @@ public class ComputerPlayer extends PlayerImpl implements Player { targets = threats(opponentId, new FilterCreaturePermanent(), game); } for (Permanent permanent: targets) { - if (target.canTarget(permanent.getId(), game)) { + if (target.canTarget(permanent.getId(), source, game)) { if (permanent.getToughness().getValue() <= target.getAmountRemaining()) { - target.addTarget(permanent.getId(), permanent.getToughness().getValue(), game); + target.addTarget(permanent.getId(), permanent.getToughness().getValue(), source, game); return true; } } @@ -304,17 +315,17 @@ public class ComputerPlayer extends PlayerImpl implements Player { protected void playLand(Game game) { logger.fine("playLand"); - List lands = hand.getCards(new FilterLandCard()); + Set lands = hand.getCards(new FilterLandCard(), game); while (lands.size() > 0 && this.landsPlayed < this.landsPerTurn) { if (lands.size() == 1) - this.playLand(lands.get(0), game); + this.playLand(lands.iterator().next(), game); else { playALand(lands, game); } } } - protected void playALand(List lands, Game game) { + protected void playALand(Set lands, Game game) { logger.fine("playALand"); //play a land that will allow us to play an unplayable for (Mana mana: unplayable.keySet()) { @@ -341,8 +352,8 @@ public class ComputerPlayer extends PlayerImpl implements Player { } } //play first available land - this.playLand(lands.get(0), game); - lands.remove(0); + this.playLand(lands.iterator().next(), game); + lands.remove(lands.iterator().next()); } protected void findPlayables(Game game) { @@ -350,7 +361,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { playableNonInstant.clear(); unplayable.clear(); playableAbilities.clear(); - List nonLands = hand.getCards(new FilterNonlandCard()); + Set nonLands = hand.getCards(new FilterNonlandCard(), game); ManaOptions available = getManaAvailable(game); available.addMana(manaPool.getMana()); @@ -402,7 +413,8 @@ public class ComputerPlayer extends PlayerImpl implements Player { } } } - logger.fine("findPlayables: " + playableInstant.toString() + "---" + playableNonInstant.toString() + "---" + playableAbilities.toString() ); + if (logger.isLoggable(Level.FINE)) + logger.fine("findPlayables: " + playableInstant.toString() + "---" + playableNonInstant.toString() + "---" + playableAbilities.toString() ); } @Override @@ -417,7 +429,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { ManaCost cost; List producers; if (unpaid instanceof ManaCosts) { - cost = ((ManaCosts)unpaid).get(0); + cost = ((ManaCosts)unpaid).get(0); producers = getSortedProducers((ManaCosts)unpaid, game); } else { @@ -477,7 +489,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { * @param game * @return List */ - private List getSortedProducers(ManaCosts unpaid, Game game) { + private List getSortedProducers(ManaCosts unpaid, Game game) { List unsorted = this.getAvailableManaProducers(game); Map scored = new HashMap(); for (Permanent permanent: unsorted) { @@ -490,6 +502,8 @@ public class ComputerPlayer extends PlayerImpl implements Player { } } } + if (score > 0) // score mana producers that produce other types higher + score += permanent.getAbilities().getManaAbilities(Zone.BATTLEFIELD).size(); scored.put(permanent, score); } return sortByValue(scored); @@ -546,15 +560,19 @@ public class ComputerPlayer extends PlayerImpl implements Player { } @Override - public boolean chooseTarget(Cards cards, TargetCard target, Game game) { + public boolean chooseTarget(Cards cards, TargetCard target, Ability source, Game game) { logger.fine("chooseTarget"); //TODO: improve this //return first match - for (Card card: cards.getCards(target.getFilter())) { - target.addTarget(card.getId(), game); - return true; + if (!target.doneChosing()) { + for (Card card: cards.getCards(target.getFilter(), game)) { + target.addTarget(card.getId(), source, game); + if (target.doneChosing()) + return true; + } + return false; } - return false; + return true; } @Override @@ -665,12 +683,12 @@ public class ComputerPlayer extends PlayerImpl implements Player { return potential; } - protected List getAvailableBlockers(Game game) { - logger.fine("getAvailableBlockers"); - FilterCreatureForCombat blockFilter = new FilterCreatureForCombat(); - List blockers = game.getBattlefield().getAllActivePermanents(blockFilter, playerId); - return blockers; - } +// protected List getAvailableBlockers(Game game) { +// logger.fine("getAvailableBlockers"); +// FilterCreatureForCombat blockFilter = new FilterCreatureForCombat(); +// List blockers = game.getBattlefield().getAllActivePermanents(blockFilter, playerId); +// return blockers; +// } protected List getOpponentBlockers(UUID opponentId, Game game) { logger.fine("getOpponentBlockers"); @@ -784,10 +802,24 @@ public class ComputerPlayer extends PlayerImpl implements Player { } protected void logState(Game game) { + if (logger.isLoggable(Level.FINE)) + logList("computer player hand: ", new ArrayList(hand.getCards(game))); + } + + protected void logList(String message, List list) { StringBuilder sb = new StringBuilder(); - sb.append("computer player hand: "); - for (Card card: hand.values()) { - sb.append(card.getName()).append(","); + sb.append(message).append(": "); + for (MageObject object: list) { + sb.append(object.getName()).append(","); + } + logger.fine(sb.toString()); + } + + protected void logAbilityList(String message, List list) { + StringBuilder sb = new StringBuilder(); + sb.append(message).append(": "); + for (Ability ability: list) { + sb.append(ability.getRule()).append(","); } logger.fine(sb.toString()); } @@ -798,7 +830,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { if (card.getSpellAbility().canActivate(playerId, game)) { for (Effect effect: card.getSpellAbility().getEffects()) { if (effect.getOutcome().equals(Outcome.DestroyPermanent)) { - if (card.getSpellAbility().getTargets().get(0).canTarget(creatureId, game)) { + if (card.getSpellAbility().getTargets().get(0).canTarget(creatureId, card.getSpellAbility(), game)) { if (this.activateAbility(card.getSpellAbility(), game)) return; } @@ -816,7 +848,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { if (card.getSpellAbility().canActivate(playerId, game)) { for (Effect effect: card.getSpellAbility().getEffects()) { if (effect instanceof DamageTargetEffect) { - if (card.getSpellAbility().getTargets().get(0).canTarget(creatureId, game)) { + if (card.getSpellAbility().getTargets().get(0).canTarget(creatureId, card.getSpellAbility(), game)) { if (((DamageTargetEffect)effect).getAmount() > (creature.getPower().getValue() - creature.getDamage())) { if (this.activateAbility(card.getSpellAbility(), game)) return; @@ -837,5 +869,10 @@ public class ComputerPlayer extends PlayerImpl implements Player { playableAbilities = new ArrayList(); } + @Override + public T copy() { + return (T)new ComputerPlayer(this); + } + } diff --git a/Mage.Player.AIMinimax/config/AIMinimax.properties b/Mage.Player.AIMinimax/config/AIMinimax.properties new file mode 100644 index 00000000000..96b5480294c --- /dev/null +++ b/Mage.Player.AIMinimax/config/AIMinimax.properties @@ -0,0 +1,7 @@ +maxDepth=10 +maxNodes=5000 +evaluatorLifeFactor=2 +evaluatorPermanentFactor=1 +evaluatorCreatureFactor=1 +evaluatorHandFactor=1 +maxThinkSeconds=30 \ No newline at end of file diff --git a/Mage.Player.AIMinimax/nbproject/project.properties b/Mage.Player.AIMinimax/nbproject/project.properties new file mode 100644 index 00000000000..d51cfa736ff --- /dev/null +++ b/Mage.Player.AIMinimax/nbproject/project.properties @@ -0,0 +1,75 @@ +application.title=Mage.Player.AIMinimax +application.vendor=BetaSteward_at_googlemail.com +build.classes.dir=${build.dir}/classes +build.classes.excludes=**/*.java,**/*.form +# This directory is removed when the project is cleaned: +build.dir=build +build.generated.dir=${build.dir}/generated +build.generated.sources.dir=${build.dir}/generated-sources +# Only compile against the classpath explicitly listed here: +build.sysclasspath=ignore +build.test.classes.dir=${build.dir}/test/classes +build.test.results.dir=${build.dir}/test/results +# Uncomment to specify the preferred debugger connection transport: +#debug.transport=dt_socket +debug.classpath=\ + ${run.classpath} +debug.test.classpath=\ + ${run.test.classpath} +# This directory is removed when the project is cleaned: +dist.dir=dist +dist.jar=${dist.dir}/Mage.Player.AIMinimax.jar +dist.javadoc.dir=${dist.dir}/javadoc +endorsed.classpath= +excludes= +includes=** +jar.compress=false +javac.classpath=\ + ${reference.Mage.jar}:\ + ${reference.Mage_Player_AI.jar} +# Space-separated list of extra javac options +javac.compilerargs= +javac.deprecation=false +javac.source=1.5 +javac.target=1.5 +javac.test.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir}:\ + ${libs.junit.classpath}:\ + ${libs.junit_4.classpath} +javadoc.additionalparam= +javadoc.author=false +javadoc.encoding=${source.encoding} +javadoc.noindex=false +javadoc.nonavbar=false +javadoc.notree=false +javadoc.private=false +javadoc.splitindex=true +javadoc.use=true +javadoc.version=false +javadoc.windowtitle= +jnlp.codebase.type=local +jnlp.descriptor=application +jnlp.enabled=false +jnlp.offline-allowed=false +jnlp.signed=false +meta.inf.dir=${src.dir}/META-INF +platform.active=default_platform +project.license=bsd +project.Mage=../Mage +project.Mage_Player_AI=../Mage.Player.AI +reference.Mage.jar=${project.Mage}/dist/Mage.jar +reference.Mage_Player_AI.jar=${project.Mage_Player_AI}/dist/Mage.Player.AI.jar +run.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +# Space-separated list of JVM arguments used when running the project +# (you may also define separate properties like run-sys-prop.name=value instead of -Dname=value +# or test-sys-prop.name=value to set system properties for unit tests): +run.jvmargs= +run.test.classpath=\ + ${javac.test.classpath}:\ + ${build.test.classes.dir} +source.encoding=UTF-8 +src.dir=src +test.src.dir=test diff --git a/Mage.Player.AIMinimax/nbproject/project.xml b/Mage.Player.AIMinimax/nbproject/project.xml new file mode 100644 index 00000000000..e32752056b8 --- /dev/null +++ b/Mage.Player.AIMinimax/nbproject/project.xml @@ -0,0 +1,33 @@ + + + org.netbeans.modules.java.j2seproject + + + Mage.Player.AIMinimax + + + + + + + + + + Mage + jar + + jar + clean + jar + + + Mage_Player_AI + jar + + jar + clean + jar + + + + diff --git a/Mage.Player.AIMinimax/src/mage/player/ai/Attackers.java b/Mage.Player.AIMinimax/src/mage/player/ai/Attackers.java new file mode 100644 index 00000000000..51da223cc92 --- /dev/null +++ b/Mage.Player.AIMinimax/src/mage/player/ai/Attackers.java @@ -0,0 +1,52 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.player.ai; + +import java.util.ArrayList; +import java.util.List; +import java.util.TreeMap; +import mage.game.permanent.Permanent; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class Attackers extends TreeMap> { + + public List getAttackers() { + List attackers = new ArrayList(); + for (List l: this.values()) { + for (Permanent permanent: l) { + attackers.add(permanent); + } + } + return attackers; + } + +} diff --git a/Mage.Player.AIMinimax/src/mage/player/ai/ComputerPlayer2.java b/Mage.Player.AIMinimax/src/mage/player/ai/ComputerPlayer2.java new file mode 100644 index 00000000000..0f1161b0d79 --- /dev/null +++ b/Mage.Player.AIMinimax/src/mage/player/ai/ComputerPlayer2.java @@ -0,0 +1,631 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.player.ai; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.FutureTask; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.logging.Level; +import java.util.logging.Logger; +import mage.Constants.Outcome; +import mage.Constants.PhaseStep; +import mage.Constants.RangeOfInfluence; +import mage.abilities.Ability; +import mage.abilities.ActivatedAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.SearchEffect; +import mage.cards.Cards; +import mage.cards.decks.Deck; +import mage.choices.Choice; +import mage.filter.FilterAbility; +import mage.game.Game; +import mage.game.combat.Combat; +import mage.game.combat.CombatGroup; +import mage.game.events.GameEvent; +import mage.game.stack.StackAbility; +import mage.game.stack.StackObject; +import mage.game.turn.BeginCombatStep; +import mage.game.turn.BeginningPhase; +import mage.game.turn.CleanupStep; +import mage.game.turn.CombatDamageStep; +import mage.game.turn.CombatPhase; +import mage.game.turn.DeclareAttackersStep; +import mage.game.turn.DeclareBlockersStep; +import mage.game.turn.DrawStep; +import mage.game.turn.EndOfCombatStep; +import mage.game.turn.EndPhase; +import mage.game.turn.EndStep; +import mage.game.turn.Phase; +import mage.game.turn.PostCombatMainPhase; +import mage.game.turn.PostCombatMainStep; +import mage.game.turn.PreCombatMainPhase; +import mage.game.turn.PreCombatMainStep; +import mage.game.turn.UntapStep; +import mage.game.turn.UpkeepStep; +import mage.players.Player; +import mage.target.Target; +import mage.target.TargetCard; +import mage.util.Logging; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class ComputerPlayer2 extends ComputerPlayer implements Player { + + private static final transient Logger logger = Logging.getLogger(ComputerPlayer2.class.getName()); + private static final ExecutorService pool = Executors.newFixedThreadPool(1); + + protected int maxDepth; + protected int maxNodes; + protected LinkedList actions = new LinkedList(); + protected List targets = new ArrayList(); + protected List choices = new ArrayList(); + protected Combat combat; + protected int currentScore; + protected SimulationNode root; + + public ComputerPlayer2(String name, Deck deck, RangeOfInfluence range) { + super(name, deck, range); + maxDepth = Config.maxDepth; + maxNodes = Config.maxNodes; + } + + public ComputerPlayer2(final ComputerPlayer2 player) { + super(player); + this.maxDepth = player.maxDepth; + this.currentScore = player.currentScore; + if (player.combat != null) + this.combat = player.combat.copy(); + for (Ability ability: player.actions) { + actions.add(ability); + } + for (UUID targetId: player.targets) { + targets.add(targetId); + } + for (String choice: player.choices) { + choices.add(choice); + } + } + + @Override + public ComputerPlayer2 copy() { + return new ComputerPlayer2(this); + } + + @Override + public void priority(Game game) { + logState(game); + game.firePriorityEvent(playerId); + switch (game.getTurn().getStepType()) { + case UPKEEP: + case DRAW: + pass(); + break; + case PRECOMBAT_MAIN: + case BEGIN_COMBAT: + case DECLARE_ATTACKERS: + case DECLARE_BLOCKERS: + case COMBAT_DAMAGE: + case END_COMBAT: + case POSTCOMBAT_MAIN: + if (actions.size() == 0) { + calculateActions(game); + } + act(game); + break; + case END_TURN: + case CLEANUP: + pass(); + break; + } + } + + protected void act(Game game) { + if (actions == null || actions.size() == 0) + pass(); + else { + boolean usedStack = false; + while (actions.peek() != null) { + Ability ability = actions.poll(); + this.activateAbility((ActivatedAbility) ability, game); + if (ability.isUsesStack()) + usedStack = true; + } + if (usedStack) + pass(); + } + } + + protected void calculateActions(Game game) { + currentScore = GameStateEvaluator.evaluate(playerId, game); + if (!getNextAction(game)) { + Game sim = createSimulation(game); + SimulationNode.resetCount(); + root = new SimulationNode(sim, maxDepth, playerId); + logger.fine("simulating actions"); + addActionsTimed(new FilterAbility()); + if (root.children.size() > 0) { + root = root.children.get(0); + actions = new LinkedList(root.abilities); + combat = root.combat; + } + } + } + + protected boolean getNextAction(Game game) { + if (root != null && root.children.size() > 0) { + SimulationNode test = root; + root = root.children.get(0); + while (root.children.size() > 0 && !root.playerId.equals(playerId)) { + test = root; + root = root.children.get(0); + } + logger.fine("simlating -- game value:" + game.getState().getValue() + " test value:" + test.gameValue); + if (root.playerId.equals(playerId) && root.abilities != null && game.getState().getValue() == test.gameValue) { + logger.fine("simulating -- continuing previous action chain"); + actions = new LinkedList(root.abilities); + combat = root.combat; + return true; + } + else { + return false; + } + } + return false; + } + + protected int minimaxAB(SimulationNode node, FilterAbility filter, int depth, int alpha, int beta) { + UUID currentPlayerId = node.getGame().getPlayerList().get(); + SimulationNode bestChild = null; + for (SimulationNode child: node.getChildren()) { + if (alpha >= beta) { + logger.fine("alpha beta pruning"); + break; + } + if (SimulationNode.nodeCount > maxNodes) { + logger.fine("simulating -- reached end-state"); + break; + } + int val = addActions(child, filter, depth-1, alpha, beta); + if (!currentPlayerId.equals(playerId)) { + if (val < beta) { + beta = val; + bestChild = child; + if (node.getCombat() == null) + node.setCombat(child.getCombat()); + } + } + else { + if (val > alpha) { + alpha = val; + bestChild = child; + if (node.getCombat() == null) + node.setCombat(child.getCombat()); + } + } + } + node.children.clear(); + if (bestChild != null) + node.children.add(bestChild); + if (!currentPlayerId.equals(playerId)) { + logger.fine("returning minimax beta: " + beta); + return beta; + } + else { + logger.fine("returning minimax alpha: " + alpha); + return alpha; + } + } + + protected SearchEffect getSearchEffect(StackAbility ability) { + for (Effect effect: ability.getEffects()) { + if (effect instanceof SearchEffect) { + return (SearchEffect) effect; + } + } + return null; + } + + protected void resolve(SimulationNode node, int depth, Game game) { + StackObject ability = game.getStack().pop(); + if (ability instanceof StackAbility) { + SearchEffect effect = getSearchEffect((StackAbility) ability); + if (effect != null && ability.getControllerId().equals(playerId)) { + Target target = effect.getTarget(); + if (!target.doneChosing()) { + for (UUID targetId: target.possibleTargets(ability.getSourceId(), ability.getControllerId(), game)) { + Game sim = game.copy(); + StackAbility newAbility = (StackAbility) ability.copy(); + SearchEffect newEffect = getSearchEffect((StackAbility) newAbility); + newEffect.getTarget().addTarget(targetId, newAbility, sim); + sim.getStack().push(newAbility); + SimulationNode newNode = new SimulationNode(sim, depth, ability.getControllerId()); + node.children.add(newNode); + newNode.getTargets().add(targetId); + logger.fine("simulating search -- node#: " + SimulationNode.getCount() + "for player: " + sim.getPlayer(ability.getControllerId()).getName()); + } + return; + } + } + } + logger.fine("simulating resolve "); + ability.resolve(game); + game.applyEffects(); + game.getPlayers().resetPassed(); + game.getPlayerList().setCurrent(game.getActivePlayerId()); + } + + protected void addActionsTimed(final FilterAbility filter) { + FutureTask task = new FutureTask(new Callable() { + public Integer call() throws Exception + { + return addActions(root, filter, maxDepth, Integer.MIN_VALUE, Integer.MAX_VALUE); + } + }); + pool.execute(task); + try { + task.get(Config.maxThinkSeconds, TimeUnit.SECONDS); + } catch (TimeoutException e) { + logger.fine("simulating - timed out"); + task.cancel(true); + } catch (ExecutionException e) { + + } catch (InterruptedException e) { + + } + } + + protected int addActions(SimulationNode node, FilterAbility filter, int depth, int alpha, int beta) { + Game game = node.getGame(); + int val; + if (Thread.interrupted()) { + Thread.currentThread().interrupt(); + logger.fine("interrupted"); + return GameStateEvaluator.evaluate(playerId, game); + } + if (depth <= 0 || SimulationNode.nodeCount > maxNodes || game.isGameOver()) { + logger.fine("simulating -- reached end state"); + val = GameStateEvaluator.evaluate(playerId, game); + } + else if (node.getChildren().size() > 0) { + logger.fine("simulating -- somthing added children:" + node.getChildren().size()); + val = minimaxAB(node, filter, depth-1, alpha, beta); + } + else { + if (logger.isLoggable(Level.FINE)) + logger.fine("simulating -- alpha: " + alpha + " beta: " + beta + " depth:" + depth + " step:" + game.getTurn().getStepType() + " for player:" + (node.getPlayerId().equals(playerId)?"yes":"no")); + if (allPassed(game)) { + if (!game.getStack().isEmpty()) { + resolve(node, depth, game); + } + else { +// int testScore = GameStateEvaluator.evaluate(playerId, game); +// if (testScore < currentScore) { +// // if score at end of step is worse than original score don't check any further +// logger.fine("simulating -- abandoning current check, no immediate benefit"); +// return testScore; +// } + game.getPlayers().resetPassed(); + playNext(game, game.getActivePlayerId(), node); + } + } + + if (game.isGameOver()) { + val = GameStateEvaluator.evaluate(playerId, game); + } + else if (node.getChildren().size() > 0) { + //declared attackers or blockers or triggered abilities + logger.fine("simulating -- attack/block/trigger added children:" + node.getChildren().size()); + val = minimaxAB(node, filter, depth-1, alpha, beta); + } + else { + val = simulatePriority(node, game, filter, depth, alpha, beta); + } + } + + if (logger.isLoggable(Level.FINE)) + logger.fine("returning -- score: " + val + " depth:" + depth + " step:" + game.getTurn().getStepType() + " for player:" + game.getPlayer(node.getPlayerId()).getName()); + return val; + + } + + protected int simulatePriority(SimulationNode node, Game game, FilterAbility filter, int depth, int alpha, int beta) { + if (Thread.interrupted()) { + Thread.currentThread().interrupt(); + logger.fine("interrupted"); + return GameStateEvaluator.evaluate(playerId, game); + } + node.setGameValue(game.getState().getValue()); + SimulatedPlayer currentPlayer = (SimulatedPlayer) game.getPlayer(game.getPlayerList().get()); + SimulationNode bestNode = null; + List allActions = currentPlayer.simulatePriority(game, filter); + if (logger.isLoggable(Level.FINE)) + logger.fine("simulating -- adding " + allActions.size() + " children:" + allActions); + for (Ability action: allActions) { + Game sim = game.copy(); + if (sim.getPlayer(playerId).activateAbility((ActivatedAbility) action.copy(), sim)) { + sim.applyEffects(); + if (!sim.isGameOver() && action.isUsesStack()) { + // only pass if the last action uses the stack + sim.getPlayer(currentPlayer.getId()).pass(); + sim.getPlayerList().getNext(); + } + SimulationNode newNode = new SimulationNode(sim, action, depth, currentPlayer.getId()); + if (logger.isLoggable(Level.FINE)) + logger.fine("simulating -- node #:" + SimulationNode.getCount() + " actions:" + action); + sim.checkStateAndTriggered(); + int val = addActions(newNode, filter, depth-1, alpha, beta); + if (!currentPlayer.getId().equals(playerId)) { + if (val < beta) { + beta = val; + bestNode = newNode; + node.setCombat(newNode.getCombat()); + } + } + else { + if (val > alpha) { + alpha = val; + bestNode = newNode; + node.setCombat(newNode.getCombat()); + if (node.getTargets().size() > 0) + targets = node.getTargets(); + if (node.getChoices().size() > 0) + choices = node.getChoices(); + } + } + if (alpha >= beta) { + logger.fine("simulating -- pruning"); + break; + } + if (SimulationNode.nodeCount > maxNodes) { + logger.fine("simulating -- reached end-state"); + break; + } + } + } + if (bestNode != null) { + node.children.clear(); + node.children.add(bestNode); + } + if (!currentPlayer.getId().equals(playerId)) { + logger.fine("returning priority beta: " + beta); + return beta; + } + else { + logger.fine("returning priority alpha: " + alpha); + return alpha; + } + } + + protected boolean allPassed(Game game) { + for (Player player: game.getPlayers().values()) { + if (!player.isPassed() && !player.hasLost() && !player.hasLeft()) + return false; + } + return true; + } + + @Override + public boolean choose(Outcome outcome, Choice choice, Game game) { + if (choices.size() == 0) + return super.choose(outcome, choice, game); + if (!choice.isChosen()) { + for (String achoice: choices) { + choice.setChoice(achoice); + if (choice.isChosen()) { + choices.clear(); + return true; + } + } + return false; + } + return true; + } + + @Override + public boolean chooseTarget(Cards cards, TargetCard target, Ability source, Game game) { + if (targets.size() == 0) + return super.chooseTarget(cards, target, source, game); + if (!target.doneChosing()) { + for (UUID targetId: targets) { + target.addTarget(targetId, source, game); + if (target.doneChosing()) { + targets.clear(); + return true; + } + } + return false; + } + return true; + } + + public void playNext(Game game, UUID activePlayerId, SimulationNode node) { + boolean skip = false; + while (true) { + Phase currentPhase = game.getPhase(); + if (!skip) + currentPhase.getStep().endStep(game, activePlayerId); + game.applyEffects(); + switch (currentPhase.getStep().getType()) { + case UNTAP: + game.getPhase().setStep(new UpkeepStep()); + break; + case UPKEEP: + game.getPhase().setStep(new DrawStep()); + break; + case DRAW: + game.getTurn().setPhase(new PreCombatMainPhase()); + game.getPhase().setStep(new PreCombatMainStep()); + break; + case PRECOMBAT_MAIN: + game.getTurn().setPhase(new CombatPhase()); + game.getPhase().setStep(new BeginCombatStep()); + break; + case BEGIN_COMBAT: + game.getPhase().setStep(new DeclareAttackersStep()); + break; + case DECLARE_ATTACKERS: + game.getPhase().setStep(new DeclareBlockersStep()); + break; + case DECLARE_BLOCKERS: + game.getPhase().setStep(new CombatDamageStep(true)); + break; + case COMBAT_DAMAGE: + if (((CombatDamageStep)currentPhase.getStep()).getFirst()) + game.getPhase().setStep(new CombatDamageStep(false)); + else + game.getPhase().setStep(new EndOfCombatStep()); + break; + case END_COMBAT: + game.getTurn().setPhase(new PostCombatMainPhase()); + game.getPhase().setStep(new PostCombatMainStep()); + break; + case POSTCOMBAT_MAIN: + game.getTurn().setPhase(new EndPhase()); + game.getPhase().setStep(new EndStep()); + break; + case END_TURN: + game.getPhase().setStep(new CleanupStep()); + break; + case CLEANUP: + game.getPhase().getStep().beginStep(game, activePlayerId); + if (!game.checkStateAndTriggered() && !game.isGameOver()) { + game.getState().setActivePlayerId(game.getState().getPlayerList(game.getActivePlayerId()).getNext()); + game.getTurn().setPhase(new BeginningPhase()); + game.getPhase().setStep(new UntapStep()); + } + } + if (!game.getStep().skipStep(game, game.getActivePlayerId())) { + if (game.getTurn().getStepType() == PhaseStep.DECLARE_ATTACKERS) { + game.fireEvent(new GameEvent(GameEvent.EventType.DECLARE_ATTACKERS_STEP_PRE, null, null, activePlayerId)); + if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.DECLARING_ATTACKERS, activePlayerId, activePlayerId))) { + for (Combat engagement: ((SimulatedPlayer)game.getPlayer(activePlayerId)).addAttackers(game)) { + Game sim = game.copy(); + UUID defenderId = game.getOpponents(playerId).iterator().next(); + for (CombatGroup group: engagement.getGroups()) { + for (UUID attackerId: group.getAttackers()) { + sim.getPlayer(activePlayerId).declareAttacker(attackerId, defenderId, sim); + } + } + sim.fireEvent(GameEvent.getEvent(GameEvent.EventType.DECLARED_ATTACKERS, playerId, playerId)); + SimulationNode newNode = new SimulationNode(sim, node.getDepth()-1, activePlayerId); + logger.fine("simulating -- node #:" + SimulationNode.getCount() + " declare attakers"); + newNode.setCombat(sim.getCombat()); + node.children.add(newNode); + } + } + } + else if (game.getTurn().getStepType() == PhaseStep.DECLARE_BLOCKERS) { + game.fireEvent(new GameEvent(GameEvent.EventType.DECLARE_BLOCKERS_STEP_PRE, null, null, activePlayerId)); + if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.DECLARING_BLOCKERS, activePlayerId, activePlayerId))) { + for (UUID defenderId: game.getCombat().getDefenders()) { + //check if defender is being attacked + if (game.getCombat().isAttacked(defenderId, game)) { + for (Combat engagement: ((SimulatedPlayer)game.getPlayer(defenderId)).addBlockers(game)) { + Game sim = game.copy(); + for (CombatGroup group: engagement.getGroups()) { + for (UUID blockerId: group.getBlockers()) { + group.addBlocker(blockerId, defenderId, sim); + } + } + sim.fireEvent(GameEvent.getEvent(GameEvent.EventType.DECLARED_BLOCKERS, playerId, playerId)); + SimulationNode newNode = new SimulationNode(sim, node.getDepth()-1, defenderId); + logger.fine("simulating -- node #:" + SimulationNode.getCount() + " declare blockers"); + newNode.setCombat(sim.getCombat()); + node.children.add(newNode); + } + } + } + } + } + else { + game.getStep().beginStep(game, activePlayerId); + } + if (game.getStep().getHasPriority()) + break; + } + else { + skip = true; + } + } + game.checkStateAndTriggered(); + } + + @Override + public void selectAttackers(Game game) { + logger.fine("selectAttackers"); + if (combat != null) { + UUID opponentId = game.getCombat().getDefenders().iterator().next(); + for (UUID attackerId: combat.getAttackers()) { + this.declareAttacker(attackerId, opponentId, game); + } + } + } + + @Override + public void selectBlockers(Game game) { + logger.fine("selectBlockers"); + if (combat != null && combat.getGroups().size() > 0) { + List groups = game.getCombat().getGroups(); + for (int i = 0; i< groups.size(); i++) { + for (UUID blockerId: combat.getGroups().get(i).getBlockers()) { + this.declareBlocker(blockerId, groups.get(i).getAttackers().get(0), game); + } + } + } + } + + /** + * Copies game and replaces all players in copy with simulated players + * + * @param game + * @return a new game object with simulated players + */ + protected Game createSimulation(Game game) { + Game sim = game.copy(); + + for (Player copyPlayer: sim.getState().getPlayers().values()) { + Player origPlayer = game.getState().getPlayers().get(copyPlayer.getId()); + SimulatedPlayer newPlayer = new SimulatedPlayer(copyPlayer.getId(), copyPlayer.getId().equals(playerId)); + newPlayer.restore(origPlayer); + sim.getState().getPlayers().put(copyPlayer.getId(), newPlayer); + } + return sim; + } + +} diff --git a/Mage.Player.AIMinimax/src/mage/player/ai/ComputerPlayer3.java b/Mage.Player.AIMinimax/src/mage/player/ai/ComputerPlayer3.java new file mode 100644 index 00000000000..c9e1fd897c4 --- /dev/null +++ b/Mage.Player.AIMinimax/src/mage/player/ai/ComputerPlayer3.java @@ -0,0 +1,540 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.player.ai; + +import java.util.LinkedList; +import java.util.UUID; +import java.util.logging.Level; +import java.util.logging.Logger; +import mage.Constants.AbilityType; +import mage.Constants.PhaseStep; +import mage.Constants.RangeOfInfluence; +import mage.Constants.Zone; +import mage.abilities.Ability; +import mage.cards.decks.Deck; +import mage.filter.FilterAbility; +import mage.game.Game; +import mage.game.combat.Combat; +import mage.game.combat.CombatGroup; +import mage.game.events.GameEvent; +import mage.game.turn.BeginCombatStep; +import mage.game.turn.BeginningPhase; +import mage.game.turn.CleanupStep; +import mage.game.turn.CombatDamageStep; +import mage.game.turn.CombatPhase; +import mage.game.turn.DeclareAttackersStep; +import mage.game.turn.DeclareBlockersStep; +import mage.game.turn.DrawStep; +import mage.game.turn.EndOfCombatStep; +import mage.game.turn.EndPhase; +import mage.game.turn.EndStep; +import mage.game.turn.PostCombatMainPhase; +import mage.game.turn.PostCombatMainStep; +import mage.game.turn.Step; +import mage.game.turn.UntapStep; +import mage.game.turn.UpkeepStep; +import mage.players.Player; +import mage.util.Logging; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class ComputerPlayer3 extends ComputerPlayer2 implements Player { + + private static final transient Logger logger = Logging.getLogger(ComputerPlayer3.class.getName()); + + private static FilterAbility filterLand = new FilterAbility(); + private static FilterAbility filterNotLand = new FilterAbility(); + + static { + filterLand.getTypes().add(AbilityType.PLAY_LAND); + filterLand.setZone(Zone.HAND); + + filterNotLand.getTypes().add(AbilityType.PLAY_LAND); + filterNotLand.setZone(Zone.HAND); + filterNotLand.setNotFilter(true); + + } + + public ComputerPlayer3(String name, Deck deck, RangeOfInfluence range) { + super(name, deck, range); + maxDepth = Config.maxDepth; + maxNodes = Config.maxNodes; + } + + public ComputerPlayer3(final ComputerPlayer3 player) { + super(player); + } + + @Override + public ComputerPlayer3 copy() { + return new ComputerPlayer3(this); + } + + @Override + public void priority(Game game) { + logState(game); + game.firePriorityEvent(playerId); + switch (game.getTurn().getStepType()) { + case UPKEEP: + case DRAW: + pass(); + break; + case PRECOMBAT_MAIN: + if (game.getActivePlayerId().equals(playerId)) { + if (actions.size() == 0) { + calculatePreCombatActions(game); + } + act(game); + } + else + pass(); + break; + case BEGIN_COMBAT: + pass(); + break; + case DECLARE_ATTACKERS: + if (!game.getActivePlayerId().equals(playerId)) { + if (actions.size() == 0) { + calculatePreCombatActions(game); + } + act(game); + } + else + pass(); + break; + case DECLARE_BLOCKERS: + case COMBAT_DAMAGE: + case END_COMBAT: + pass(); + break; + case POSTCOMBAT_MAIN: + if (game.getActivePlayerId().equals(playerId)) { + if (actions.size() == 0) { + calculatePostCombatActions(game); + } + act(game); + } + else + pass(); + break; + case END_TURN: + case CLEANUP: + pass(); + break; + } + } + + protected void calculatePreCombatActions(Game game) { + if (!getNextAction(game)) { + currentScore = GameStateEvaluator.evaluate(playerId, game); + Game sim = createSimulation(game); + SimulationNode.resetCount(); + root = new SimulationNode(sim, maxDepth, playerId); + logger.fine("simulating pre combat actions -----------------------------------------------------------------------------------------"); + + addActionsTimed(new FilterAbility()); + if (root.children.size() > 0) { + root = root.children.get(0); + actions = new LinkedList(root.abilities); + combat = root.combat; + } + } + } + + protected void calculatePostCombatActions(Game game) { + if (!getNextAction(game)) { + currentScore = GameStateEvaluator.evaluate(playerId, game); + Game sim = createSimulation(game); + SimulationNode.resetCount(); + root = new SimulationNode(sim, maxDepth, playerId); + logger.fine("simulating post combat actions ----------------------------------------------------------------------------------------"); + addActionsTimed(new FilterAbility()); + if (root.children.size() > 0) { + root = root.children.get(0); + actions = new LinkedList(root.abilities); + combat = root.combat; + } + } + } + + @Override + protected int addActions(SimulationNode node, FilterAbility filter, int depth, int alpha, int beta) { + boolean stepFinished = false; + int val; + Game game = node.getGame(); + if (Thread.interrupted()) { + Thread.currentThread().interrupt(); + logger.fine("interrupted"); + return GameStateEvaluator.evaluate(playerId, game); + } + if (depth <= 0 || SimulationNode.nodeCount > maxNodes || game.isGameOver()) { + logger.fine("simulating -- reached end state"); + val = GameStateEvaluator.evaluate(playerId, game); + } + else if (node.getChildren().size() > 0) { + logger.fine("simulating -- somthing added children:" + node.getChildren().size()); + val = minimaxAB(node, filter, depth-1, alpha, beta); + } + else { + if (logger.isLoggable(Level.FINE)) + logger.fine("simulating -- alpha: " + alpha + " beta: " + beta + " depth:" + depth + " step:" + game.getTurn().getStepType() + " for player:" + game.getPlayer(game.getPlayerList().get()).getName()); + if (allPassed(game)) { + if (!game.getStack().isEmpty()) { + resolve(node, depth, game); + } + else { + stepFinished = true; + } + } + + if (game.isGameOver()) { + val = GameStateEvaluator.evaluate(playerId, game); + } + else if (stepFinished) { + logger.fine("step finished"); + int testScore = GameStateEvaluator.evaluate(playerId, game); + if (game.getActivePlayerId().equals(playerId)) { + if (testScore < currentScore) { + // if score at end of step is worse than original score don't check further + logger.fine("simulating -- abandoning check, no immediate benefit"); + val = testScore; + } + else { + switch (game.getTurn().getStepType()) { + case PRECOMBAT_MAIN: + val = simulateCombat(game, node, depth-1, alpha, beta, false); + break; + case POSTCOMBAT_MAIN: + val = simulateCounterAttack(game, node, depth-1, alpha, beta); + break; + default: + val = GameStateEvaluator.evaluate(playerId, game); + break; + } + } + } + else { + if (game.getTurn().getStepType() == PhaseStep.DECLARE_ATTACKERS) + val = simulateBlockers(game, node, playerId, depth-1, alpha, beta, true); + else + val = GameStateEvaluator.evaluate(playerId, game); + } + } + else if (node.getChildren().size() > 0) { + logger.fine("simulating -- trigger added children:" + node.getChildren().size()); + val = minimaxAB(node, filter, depth, alpha, beta); + } + else { + val = simulatePriority(node, game, filter, depth, alpha, beta); + } + } + + if (logger.isLoggable(Level.FINE)) + logger.fine("returning -- score: " + val + " depth:" + depth + " step:" + game.getTurn().getStepType() + " for player:" + game.getPlayer(node.getPlayerId()).getName()); + return val; + + } + + protected int simulateCombat(Game game, SimulationNode node, int depth, int alpha, int beta, boolean counter) { + Integer val = null; + if (Thread.interrupted()) { + Thread.currentThread().interrupt(); + logger.fine("interrupted"); + return GameStateEvaluator.evaluate(playerId, game); + } + if (game.getTurn().getStepType() != PhaseStep.DECLARE_BLOCKERS) { + game.getTurn().setPhase(new CombatPhase()); + if (game.getPhase().beginPhase(game, game.getActivePlayerId())) { + simulateStep(game, new BeginCombatStep()); + game.getPhase().setStep(new DeclareAttackersStep()); + if (!game.getStep().skipStep(game, game.getActivePlayerId())) { + game.fireEvent(new GameEvent(GameEvent.EventType.DECLARE_ATTACKERS_STEP_PRE, null, null, game.getActivePlayerId())); + if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.DECLARING_ATTACKERS, game.getActivePlayerId(), game.getActivePlayerId()))) { + val = simulateAttackers(game, node, game.getActivePlayerId(), depth, alpha, beta, counter); + } + } + else if (!counter) { + simulateToEnd(game); + val = simulatePostCombatMain(game, node, depth, alpha, beta); + } + } + } + else { + if (!game.getStep().skipStep(game, game.getActivePlayerId())) { + game.fireEvent(new GameEvent(GameEvent.EventType.DECLARE_BLOCKERS_STEP_PRE, null, null, game.getActivePlayerId())); + if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.DECLARING_BLOCKERS, game.getActivePlayerId(), game.getActivePlayerId()))) { + //only suitable for two player games - only simulates blocks for 1st defender + val = simulateBlockers(game, node, game.getCombat().getDefenders().iterator().next(), depth, alpha, beta, counter); + } + } + else if (!counter) { + finishCombat(game); + val = GameStateEvaluator.evaluate(playerId, game); +// val = simulateCounterAttack(game, node, depth, alpha, beta); + } + } + if (val == null) + val = GameStateEvaluator.evaluate(playerId, game); + if (logger.isLoggable(Level.FINE)) + logger.fine("returning -- combat score: " + val + " depth:" + depth + " for player:" + game.getPlayer(node.getPlayerId()).getName()); + return val; + } + + + protected int simulateAttackers(Game game, SimulationNode node, UUID attackerId, int depth, int alpha, int beta, boolean counter) { + if (Thread.interrupted()) { + Thread.currentThread().interrupt(); + logger.fine("interrupted"); + return GameStateEvaluator.evaluate(playerId, game); + } + Integer val = null; + SimulationNode bestNode = null; + SimulatedPlayer attacker = (SimulatedPlayer) game.getPlayer(attackerId); + + for (Combat engagement: attacker.addAttackers(game)) { + if (alpha >= beta) { + logger.fine("simulating -- pruning attackers"); + break; + } + Game sim = game.copy(); + UUID defenderId = game.getOpponents(playerId).iterator().next(); + for (CombatGroup group: engagement.getGroups()) { + for (UUID attackId: group.getAttackers()) { + sim.getPlayer(attackerId).declareAttacker(attackId, defenderId, sim); + } + } + sim.fireEvent(GameEvent.getEvent(GameEvent.EventType.DECLARED_ATTACKERS, playerId, playerId)); + SimulationNode newNode = new SimulationNode(sim, depth, game.getActivePlayerId()); + if (logger.isLoggable(Level.FINE)) + logger.fine("simulating attack -- node#: " + SimulationNode.getCount()); + sim.checkStateAndTriggered(); + while (!sim.getStack().isEmpty()) { + sim.getStack().resolve(sim); + logger.fine("resolving triggered abilities"); + sim.applyEffects(); + } + sim.fireEvent(GameEvent.getEvent(GameEvent.EventType.DECLARE_ATTACKERS_STEP_POST, sim.getActivePlayerId(), sim.getActivePlayerId())); + Combat simCombat = sim.getCombat().copy(); + sim.getPhase().setStep(new DeclareBlockersStep()); + val = simulateCombat(sim, newNode, depth-1, alpha, beta, counter); + if (!attackerId.equals(playerId)) { + if (val < beta) { + beta = val; + bestNode = newNode; + node.setCombat(simCombat); + } + } + else { + if (val > alpha) { + alpha = val; + bestNode = newNode; + node.setCombat(simCombat); + } + } + } + if (val == null) + val = GameStateEvaluator.evaluate(playerId, game); + if (bestNode != null) { + node.children.clear(); + node.children.add(bestNode); + } + if (logger.isLoggable(Level.FINE)) + logger.fine("returning -- combat attacker score: " + val + " depth:" + depth + " for player:" + game.getPlayer(node.getPlayerId()).getName()); + return val; + } + + protected int simulateBlockers(Game game, SimulationNode node, UUID defenderId, int depth, int alpha, int beta, boolean counter) { + if (Thread.interrupted()) { + Thread.currentThread().interrupt(); + logger.fine("interrupted"); + return GameStateEvaluator.evaluate(playerId, game); + } + Integer val = null; + SimulationNode bestNode = null; + //check if defender is being attacked + if (game.getCombat().isAttacked(defenderId, game)) { + SimulatedPlayer defender = (SimulatedPlayer) game.getPlayer(defenderId); + for (Combat engagement: defender.addBlockers(game)) { + if (alpha >= beta) { + logger.fine("simulating -- pruning blockers"); + break; + } + Game sim = game.copy(); + for (CombatGroup group: engagement.getGroups()) { + UUID attackerId = group.getAttackers().get(0); + for (UUID blockerId: group.getBlockers()) { + sim.getPlayer(defenderId).declareBlocker(blockerId, attackerId, sim); + } + } + sim.fireEvent(GameEvent.getEvent(GameEvent.EventType.DECLARED_BLOCKERS, playerId, playerId)); + SimulationNode newNode = new SimulationNode(sim, depth, defenderId); + if (logger.isLoggable(Level.FINE)) + logger.fine("simulating block -- node#: " + SimulationNode.getCount()); + sim.checkStateAndTriggered(); + while (!sim.getStack().isEmpty()) { + sim.getStack().resolve(sim); + logger.fine("resolving triggered abilities"); + sim.applyEffects(); + } + sim.fireEvent(GameEvent.getEvent(GameEvent.EventType.DECLARE_BLOCKERS_STEP_POST, sim.getActivePlayerId(), sim.getActivePlayerId())); + Combat simCombat = sim.getCombat().copy(); + finishCombat(sim); + if (!counter) { + int testScore = GameStateEvaluator.evaluate(playerId, sim); + if (testScore < currentScore) { + // if score at end of combat is worse than original score don't check counterattack + logger.fine("simulating -- abandoning counterattack check, no immediate benefit"); + val = testScore; + } + else + val = simulatePostCombatMain(sim, newNode, depth-1, alpha, beta); + } + else + val = GameStateEvaluator.evaluate(playerId, sim); + if (!defenderId.equals(playerId)) { + if (val < beta) { + beta = val; + bestNode = newNode; + node.setCombat(simCombat); + } + } + else { + if (val > alpha) { + alpha = val; + bestNode = newNode; + node.setCombat(simCombat); + } + } + } + } + if (val == null) + val = GameStateEvaluator.evaluate(playerId, game); + if (bestNode != null) { + node.children.clear(); + node.children.add(bestNode); + } + if (logger.isLoggable(Level.FINE)) + logger.fine("returning -- combat blocker score: " + val + " depth:" + depth + " for player:" + game.getPlayer(node.getPlayerId()).getName()); + return val; + } + + protected int simulateCounterAttack(Game game, SimulationNode node, int depth, int alpha, int beta) { + if (Thread.interrupted()) { + Thread.currentThread().interrupt(); + logger.fine("interrupted"); + return GameStateEvaluator.evaluate(playerId, game); + } + Integer val = null; + if (!game.isGameOver()) { + logger.fine("simulating -- counter attack"); + simulateToEnd(game); + game.getState().setActivePlayerId(game.getState().getPlayerList(game.getActivePlayerId()).getNext()); + game.getTurn().setPhase(new BeginningPhase()); + if (game.getPhase().beginPhase(game, game.getActivePlayerId())) { + simulateStep(game, new UntapStep()); + simulateStep(game, new UpkeepStep()); + simulateStep(game, new DrawStep()); + game.getPhase().endPhase(game, game.getActivePlayerId()); + } + val = simulateCombat(game, node, depth-1, alpha, beta, true); + if (logger.isLoggable(Level.FINE)) + logger.fine("returning -- counter attack score: " + val + " depth:" + depth + " for player:" + game.getPlayer(node.getPlayerId()).getName()); + } + if (val == null) + val = GameStateEvaluator.evaluate(playerId, game); + return val; + } + + protected void simulateStep(Game game, Step step) { + if (Thread.interrupted()) { + Thread.currentThread().interrupt(); + logger.fine("interrupted"); + return; + } + if (!game.isGameOver()) { + game.getPhase().setStep(step); + if (!step.skipStep(game, game.getActivePlayerId())) { + step.beginStep(game, game.getActivePlayerId()); + game.checkStateAndTriggered(); + while (!game.getStack().isEmpty()) { + game.getStack().resolve(game); + game.applyEffects(); + } + step.endStep(game, game.getActivePlayerId()); + } + } + } + + protected void finishCombat(Game game) { + if (Thread.interrupted()) { + Thread.currentThread().interrupt(); + logger.fine("interrupted"); + return; + } + simulateStep(game, new CombatDamageStep(true)); + simulateStep(game, new CombatDamageStep(false)); + simulateStep(game, new EndOfCombatStep()); + } + + protected int simulatePostCombatMain(Game game, SimulationNode node, int depth, int alpha, int beta) { + if (Thread.interrupted()) { + Thread.currentThread().interrupt(); + logger.fine("interrupted"); + return GameStateEvaluator.evaluate(playerId, game); + } + logger.fine("simulating -- post combat main"); + game.getTurn().setPhase(new PostCombatMainPhase()); + if (game.getPhase().beginPhase(game, game.getActivePlayerId())) { + game.getPhase().setStep(new PostCombatMainStep()); + game.getStep().beginStep(game, playerId); + game.getPlayers().resetPassed(); + return addActions(node, new FilterAbility(), depth, alpha, beta); + } + return simulateCounterAttack(game, node, depth, alpha, beta); + } + + protected void simulateToEnd(Game game) { + if (Thread.interrupted()) { + Thread.currentThread().interrupt(); + logger.fine("interrupted"); + return; + } + if (!game.isGameOver()) { + game.getTurn().getPhase().endPhase(game, game.getActivePlayerId()); + game.getTurn().setPhase(new EndPhase()); + if (game.getTurn().getPhase().beginPhase(game, game.getActivePlayerId())) { + simulateStep(game, new EndStep()); + simulateStep(game, new CleanupStep()); + } + } + } + +} diff --git a/Mage.Player.AIMinimax/src/mage/player/ai/Config.java b/Mage.Player.AIMinimax/src/mage/player/ai/Config.java new file mode 100644 index 00000000000..c0854286f46 --- /dev/null +++ b/Mage.Player.AIMinimax/src/mage/player/ai/Config.java @@ -0,0 +1,75 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.player.ai; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.net.URISyntaxException; +import java.util.Properties; +import java.util.logging.Level; +import java.util.logging.Logger; +import mage.util.Logging; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class Config { + + private final static Logger logger = Logging.getLogger(Config.class.getName()); + + public static final int maxDepth; + public static final int maxNodes; + public static final int evaluatorLifeFactor; + public static final int evaluatorPermanentFactor; + public static final int evaluatorCreatureFactor; + public static final int evaluatorHandFactor; + public static final int maxThinkSeconds; + + static { + Properties p = new Properties(); + try { + File file = new File(Config.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath()); + p.load(new FileInputStream(new File(file.getParent() + File.separator + "AIMinimax.properties"))); + } catch (IOException ex) { + logger.log(Level.SEVERE, null, ex); + } catch (URISyntaxException ex) { + Logger.getLogger(Config.class.getName()).log(Level.SEVERE, null, ex); + } + maxDepth = Integer.parseInt(p.getProperty("maxDepth")); + maxNodes = Integer.parseInt(p.getProperty("maxNodes")); + evaluatorLifeFactor = Integer.parseInt(p.getProperty("evaluatorLifeFactor")); + evaluatorPermanentFactor = Integer.parseInt(p.getProperty("evaluatorPermanentFactor")); + evaluatorCreatureFactor = Integer.parseInt(p.getProperty("evaluatorCreatureFactor")); + evaluatorHandFactor = Integer.parseInt(p.getProperty("evaluatorHandFactor")); + maxThinkSeconds = Integer.parseInt(p.getProperty("maxThinkSeconds")); + } + +} diff --git a/Mage.Player.AIMinimax/src/mage/player/ai/GameStateEvaluator.java b/Mage.Player.AIMinimax/src/mage/player/ai/GameStateEvaluator.java new file mode 100644 index 00000000000..92a898dfeec --- /dev/null +++ b/Mage.Player.AIMinimax/src/mage/player/ai/GameStateEvaluator.java @@ -0,0 +1,102 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package mage.player.ai; + +import java.util.UUID; +import java.util.logging.Level; +import java.util.logging.Logger; +import mage.Constants.CardType; +import mage.Constants.Zone; +import mage.abilities.ActivatedAbility; +import mage.abilities.keyword.DoubleStrikeAbility; +import mage.abilities.keyword.FirstStrikeAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.abilities.mana.ManaAbility; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.util.Logging; + +/** + * + * @author BetaSteward_at_googlemail.com + * + * this evaluator is only good for two player games + * + */ +public class GameStateEvaluator { + + private final static transient Logger logger = Logging.getLogger(GameStateEvaluator.class.getName()); + + private static final int LIFE_FACTOR = Config.evaluatorLifeFactor; + private static final int PERMANENT_FACTOR = Config.evaluatorPermanentFactor; + private static final int CREATURE_FACTOR = Config.evaluatorCreatureFactor; + private static final int HAND_FACTOR = Config.evaluatorHandFactor; + + public static int evaluate(UUID playerId, Game game) { + Player player = game.getPlayer(playerId); + Player opponent = game.getPlayer(game.getOpponents(playerId).iterator().next()); + if (game.isGameOver()) { + if (player.hasLost() || opponent.hasWon()) + return Integer.MIN_VALUE; + if (opponent.hasLost() || player.hasWon()) + return Integer.MAX_VALUE; + } + int lifeScore = (player.getLife() - opponent.getLife()) * LIFE_FACTOR; + int permanentScore = 0; + for (Permanent permanent: game.getBattlefield().getAllActivePermanents(playerId)) { + permanentScore += evaluatePermanent(permanent, game); + } + for (Permanent permanent: game.getBattlefield().getAllActivePermanents(opponent.getId())) { + permanentScore -= evaluatePermanent(permanent, game); + } + permanentScore *= PERMANENT_FACTOR; + + int handScore = 0; + handScore = 7 - opponent.getHand().size(); + handScore = Math.min(7, player.getHand().size()); + handScore *= HAND_FACTOR; + + int score = lifeScore + permanentScore + handScore; + if (logger.isLoggable(Level.FINE)) + logger.fine("game state evaluated to- lifeScore:" + lifeScore + " permanentScore:" + permanentScore + " handScore:" + handScore + " total:" + score); + return score; + } + + public static int evaluatePermanent(Permanent permanent, Game game) { + int value = permanent.isTapped()?1:2; + if (permanent.getCardType().contains(CardType.CREATURE)) { + value += evaluateCreature(permanent, game) * CREATURE_FACTOR; + } + value += permanent.getAbilities().getManaAbilities(Zone.BATTLEFIELD).size(); + for (ActivatedAbility ability: permanent.getAbilities().getActivatedAbilities(Zone.BATTLEFIELD)) { + if (!(ability instanceof ManaAbility) && ability.canActivate(ability.getControllerId(), game)) + value += ability.getEffects().getOutcomeTotal(); + } + value += permanent.getAbilities().getStaticAbilities(Zone.BATTLEFIELD).getOutcomeTotal(); + value += permanent.getAbilities().getTriggeredAbilities(Zone.BATTLEFIELD).getOutcomeTotal(); + value += permanent.getManaCost().convertedManaCost(); + //TODO: add a difficulty to calculation to ManaCost - sort permanents by difficulty for casting when evaluating game states + return value; + } + + public static int evaluateCreature(Permanent creature, Game game) { + int value = 0; + value += creature.getPower().getValue(); + value += creature.getToughness().getValue(); +// if (creature.canAttack(game)) +// value += creature.getPower().getValue(); +// if (!creature.isTapped()) +// value += 2; + value += creature.getAbilities().getEvasionAbilities().getOutcomeTotal(); + value += creature.getAbilities().getProtectionAbilities().getOutcomeTotal(); + value += creature.getAbilities().containsKey(FirstStrikeAbility.getInstance().getId())?1:0; + value += creature.getAbilities().containsKey(DoubleStrikeAbility.getInstance().getId())?2:0; + value += creature.getAbilities().containsKey(TrampleAbility.getInstance().getId())?1:0; + return value; + } + +} diff --git a/Mage.Player.AIMinimax/src/mage/player/ai/SimulateBlockWorker.java b/Mage.Player.AIMinimax/src/mage/player/ai/SimulateBlockWorker.java new file mode 100644 index 00000000000..66a27c3c1cf --- /dev/null +++ b/Mage.Player.AIMinimax/src/mage/player/ai/SimulateBlockWorker.java @@ -0,0 +1,62 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.player.ai; + +import java.util.concurrent.Callable; +import java.util.logging.Level; +import java.util.logging.Logger; +import mage.game.Game; +import mage.util.Logging; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class SimulateBlockWorker implements Callable { + + private final static Logger logger = Logging.getLogger(SimulationWorker.class.getName()); + + private SimulationNode node; + private ComputerPlayer3 player; + + public SimulateBlockWorker(ComputerPlayer3 player, SimulationNode node) { + this.player = player; + this.node = node; + } + + @Override + public Object call() { + try { +// player.simulateBlock(node); + } catch (Exception ex) { + logger.log(Level.SEVERE, null, ex); + } + return null; + } +} diff --git a/Mage.Player.AIMinimax/src/mage/player/ai/SimulatedAction.java b/Mage.Player.AIMinimax/src/mage/player/ai/SimulatedAction.java new file mode 100644 index 00000000000..ab82ee8096b --- /dev/null +++ b/Mage.Player.AIMinimax/src/mage/player/ai/SimulatedAction.java @@ -0,0 +1,68 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.player.ai; + +import java.util.List; +import mage.abilities.Ability; +import mage.game.Game; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class SimulatedAction { + + private Game game; + private List abilities; + + public SimulatedAction(Game game, List abilities) { + this.game = game; + this.abilities = abilities; + } + + public Game getGame() { + return this.game; + } + + public List getAbilities() { + return this.abilities; + } + + @Override + public String toString() { + return this.abilities.toString(); + } + + public boolean usesStack() { + if (abilities != null && abilities.size() > 0) { + return abilities.get(abilities.size() -1).isUsesStack(); + } + return true; + } +} diff --git a/Mage.Player.AIMinimax/src/mage/player/ai/SimulatedPlayer.java b/Mage.Player.AIMinimax/src/mage/player/ai/SimulatedPlayer.java new file mode 100644 index 00000000000..be3b0c762c7 --- /dev/null +++ b/Mage.Player.AIMinimax/src/mage/player/ai/SimulatedPlayer.java @@ -0,0 +1,250 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.player.ai; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.logging.Level; +import java.util.logging.Logger; +import mage.abilities.Ability; +import mage.abilities.ActivatedAbility; +import mage.abilities.TriggeredAbility; +import mage.abilities.common.PassAbility; +import mage.abilities.mana.ManaOptions; +import mage.choices.Choice; +import mage.filter.FilterAbility; +import mage.game.Game; +import mage.game.combat.Combat; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.game.stack.StackAbility; +import mage.target.Target; +import mage.util.Copier; +import mage.util.Logging; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class SimulatedPlayer extends ComputerPlayer { + + private final static transient Logger logger = Logging.getLogger(SimulatedPlayer.class.getName()); + private boolean isSimulatedPlayer; + private FilterAbility filter; + private transient ConcurrentLinkedQueue allActions; + private static PassAbility pass = new PassAbility(); + + public SimulatedPlayer(UUID id, boolean isSimulatedPlayer) { + super(id); + pass.setControllerId(playerId); + this.isSimulatedPlayer = isSimulatedPlayer; + } + + public SimulatedPlayer(final SimulatedPlayer player) { + super(player); + this.isSimulatedPlayer = player.isSimulatedPlayer; + if (player.filter != null) + this.filter = player.filter.copy(); + } + + @Override + public SimulatedPlayer copy() { + return new SimulatedPlayer(this); + } + + public List simulatePriority(Game game, FilterAbility filter) { + allActions = new ConcurrentLinkedQueue(); + Game sim = game.copy(); + this.filter = filter; + + simulateOptions(sim, pass); + + ArrayList list = new ArrayList(allActions); + Collections.reverse(list); + return list; + } + + protected void simulateOptions(Game game, Ability previousActions) { + allActions.add(previousActions); + ManaOptions available = getManaAvailable(game); + available.addMana(manaPool.getMana()); + List playables = game.getPlayer(playerId).getPlayable(game, filter, available, isSimulatedPlayer); + for (Ability ability: playables) { + List options = game.getPlayer(playerId).getPlayableOptions(ability, game); + if (options.size() == 0) { + allActions.add(ability); +// simulateAction(game, previousActions, ability); + } + else { +// ExecutorService simulationExecutor = Executors.newFixedThreadPool(4); + for (Ability option: options) { + allActions.add(option); +// SimulationWorker worker = new SimulationWorker(game, this, previousActions, option); +// simulationExecutor.submit(worker); + } +// simulationExecutor.shutdown(); +// while(!simulationExecutor.isTerminated()) {} + } + } + } + +// protected void simulateAction(Game game, SimulatedAction previousActions, Ability action) { +// List actions = new ArrayList(previousActions.getAbilities()); +// actions.add(action); +// Game sim = game.copy(); +// if (sim.getPlayer(playerId).activateAbility((ActivatedAbility) action.copy(), sim)) { +// sim.applyEffects(); +// sim.getPlayers().resetPassed(); +// allActions.add(new SimulatedAction(sim, actions)); +// } +// } + + public List addAttackers(Game game) { + Map engagements = new HashMap(); + //useful only for two player games - will only attack first opponent + UUID defenderId = game.getOpponents(playerId).iterator().next(); + List attackersList = super.getAvailableAttackers(game); + //use binary digits to calculate powerset of attackers + int powerElements = (int) Math.pow(2, attackersList.size()); + StringBuilder binary = new StringBuilder(); + for (int i = powerElements - 1; i >= 0; i--) { + Game sim = game.copy(); + binary.setLength(0); + binary.append(Integer.toBinaryString(i)); + while (binary.length() < attackersList.size()) { + binary.insert(0, "0"); + } + for (int j = 0; j < attackersList.size(); j++) { + if (binary.charAt(j) == '1') + sim.getCombat().declareAttacker(attackersList.get(j).getId(), defenderId, sim); + } + if (engagements.put(sim.getCombat().getValue(sim), sim.getCombat()) != null) { + logger.fine("simulating -- found redundant attack combination"); + } + else if (logger.isLoggable(Level.FINE)) { + logger.fine("simulating -- attack:" + sim.getCombat().getGroups().size()); + } + } + return new ArrayList(engagements.values()); + } + + public List addBlockers(Game game) { + Map engagements = new HashMap(); + int numGroups = game.getCombat().getGroups().size(); + if (numGroups == 0) return new ArrayList(); + + //add a node with no blockers + Game sim = game.copy(); + engagements.put(sim.getCombat().getValue(sim), sim.getCombat()); + sim.fireEvent(GameEvent.getEvent(GameEvent.EventType.DECLARED_BLOCKERS, playerId, playerId)); + + List blockers = getAvailableBlockers(game); + addBlocker(game, blockers, engagements); + + return new ArrayList(engagements.values()); + } + + protected void addBlocker(Game game, List blockers, Map engagements) { + if (blockers.size() == 0) + return; + int numGroups = game.getCombat().getGroups().size(); + //try to block each attacker with each potential blocker + Permanent blocker = blockers.get(0); + if (logger.isLoggable(Level.FINE)) + logger.fine("simulating -- block:" + blocker); + List remaining = remove(blockers, blocker); + for (int i = 0; i < numGroups; i++) { + if (game.getCombat().getGroups().get(i).canBlock(blocker, game)) { + Game sim = game.copy(); + sim.getCombat().getGroups().get(i).addBlocker(blocker.getId(), playerId, sim); + if (engagements.put(sim.getCombat().getValue(sim), sim.getCombat()) != null) + logger.fine("simulating -- found redundant block combination"); + addBlocker(sim, remaining, engagements); // and recurse minus the used blocker + } + } + addBlocker(game, remaining, engagements); + } + + @Override + public boolean triggerAbility(TriggeredAbility source, Game game) { + Ability ability = source.copy(); + List options = getPlayableOptions(ability, game); + if (options.size() == 0) { + if (logger.isLoggable(Level.FINE)) + logger.fine("simulating -- triggered ability:" + ability); + game.getStack().push(new StackAbility(ability, playerId)); + ability.activate(game, false); + game.applyEffects(); + game.getPlayers().resetPassed(); + } + else { + SimulationNode parent = (SimulationNode) game.getCustomData(); + int depth = parent.getDepth() - 1; + if (depth == 0) return true; + logger.fine("simulating -- triggered ability - adding children:" + options.size()); + for (Ability option: options) { + addAbilityNode(parent, option, depth, game); + } + } + return true; + } + + protected void addAbilityNode(SimulationNode parent, Ability ability, int depth, Game game) { + Game sim = game.copy(); + sim.getStack().push(new StackAbility(ability, playerId)); + ability.activate(sim, false); + sim.applyEffects(); + SimulationNode newNode = new SimulationNode(sim, depth, playerId); + logger.fine("simulating -- node #:" + SimulationNode.getCount() + " triggered ability option"); + for (Target target: ability.getTargets()) { + for (UUID targetId: target.getTargets()) { + newNode.getTargets().add(targetId); + } + } + for (Choice choice: ability.getChoices()) { + newNode.getChoices().add(choice.getChoice()); + } + parent.children.add(newNode); + } + + @Override + public void priority(Game game) { + //should never get here + } + +} diff --git a/Mage.Player.AIMinimax/src/mage/player/ai/SimulationNode.java b/Mage.Player.AIMinimax/src/mage/player/ai/SimulationNode.java new file mode 100644 index 00000000000..954e4c7097f --- /dev/null +++ b/Mage.Player.AIMinimax/src/mage/player/ai/SimulationNode.java @@ -0,0 +1,127 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.player.ai; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import mage.abilities.Ability; +import mage.game.Game; +import mage.game.combat.Combat; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class SimulationNode implements Serializable { + + protected static int nodeCount; + + protected Game game; + protected int gameValue; + protected List abilities; + protected int depth; + protected List children = new ArrayList(); + protected List targets = new ArrayList(); + protected List choices = new ArrayList(); + protected UUID playerId; + protected Combat combat; + + public SimulationNode(Game game, int depth, UUID playerId) { + this.game = game; + this.depth = depth; + this.playerId = playerId; + game.setCustomData(this); + nodeCount++; + } + + public SimulationNode(Game game, List abilities, int depth, UUID playerId) { + this(game, depth, playerId); + this.abilities = abilities; + } + + public SimulationNode(Game game, Ability ability, int depth, UUID playerId) { + this(game, depth, playerId); + this.abilities = new ArrayList(); + abilities.add(ability); + } + + public static void resetCount() { + nodeCount = 0; + } + + public static int getCount() { + return nodeCount; + } + + public Game getGame() { + return this.game; + } + + public int getGameValue() { + return this.gameValue; + } + + public void setGameValue(int value) { + this.gameValue = value; + } + + public List getAbilities() { + return this.abilities; + } + + public List getChildren() { + return this.children; + } + + public int getDepth() { + return this.depth; + } + + public UUID getPlayerId() { + return this.playerId; + } + + public Combat getCombat() { + return this.combat; + } + + public void setCombat(Combat combat) { + this.combat = combat; + } + + public List getTargets() { + return this.targets; + } + + public List getChoices() { + return this.choices; + } +} diff --git a/Mage.Player.AIMinimax/src/mage/player/ai/SimulationWorker.java b/Mage.Player.AIMinimax/src/mage/player/ai/SimulationWorker.java new file mode 100644 index 00000000000..b5a5876ef01 --- /dev/null +++ b/Mage.Player.AIMinimax/src/mage/player/ai/SimulationWorker.java @@ -0,0 +1,69 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.player.ai; + +import java.util.concurrent.Callable; +import java.util.logging.Level; +import java.util.logging.Logger; +import mage.abilities.Ability; +import mage.game.Game; +import mage.util.Logging; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class SimulationWorker implements Callable { + + private final static Logger logger = Logging.getLogger(SimulationWorker.class.getName()); + + private Game game; + private SimulatedAction previousActions; + private Ability action; + private SimulatedPlayer player; + + public SimulationWorker(Game game, SimulatedPlayer player, SimulatedAction previousActions, Ability action) { + this.game = game; + this.player = player; + this.previousActions = previousActions; + this.action = action; + } + + @Override + public Object call() { + try { +// player.simulateAction(game, previousActions, action); + } catch (Exception ex) { + logger.log(Level.SEVERE, null, ex); + } + return null; + } + +} + diff --git a/Mage.Player.Human/src/mage/player/human/HumanPlayer.java b/Mage.Player.Human/src/mage/player/human/HumanPlayer.java index 3f1e9ad6375..cad9a6a55ec 100644 --- a/Mage.Player.Human/src/mage/player/human/HumanPlayer.java +++ b/Mage.Player.Human/src/mage/player/human/HumanPlayer.java @@ -43,6 +43,7 @@ import mage.Constants.RangeOfInfluence; import mage.Constants.TargetController; import mage.Constants.Zone; import mage.MageObject; +import mage.abilities.Ability; import mage.abilities.ActivatedAbility; import mage.abilities.SpecialAction; import mage.abilities.costs.mana.ManaCost; @@ -63,11 +64,11 @@ import mage.target.common.TargetDefender; * * @author BetaSteward_at_googlemail.com */ -public class HumanPlayer extends PlayerImpl { +public class HumanPlayer extends PlayerImpl { final transient PlayerResponse response = new PlayerResponse(); - private boolean abort = false; + private boolean abort; protected transient TargetPermanent targetCombat = new TargetPermanent(new FilterCreatureForCombat(), TargetController.YOU); @@ -76,6 +77,11 @@ public class HumanPlayer extends PlayerImpl { human = true; } + public HumanPlayer(final HumanPlayer player) { + super(player); + this.abort = player.abort; + } + protected void waitForResponse() { response.clear(); synchronized(response) { @@ -135,23 +141,27 @@ public class HumanPlayer extends PlayerImpl { @Override public boolean choose(Outcome outcome, Choice choice, Game game) { - game.fireChooseEvent(playerId, choice); while (!abort) { - waitForStringResponse(); - choice.setChoice(response.getString()); - return true; + game.fireChooseEvent(playerId, choice); + waitForResponse(); + if (response.getString() != null) { + choice.setChoice(response.getString()); + return true; + } else if (!choice.isRequired()) { + return false; + } } return false; } @Override - public boolean chooseTarget(Outcome outcome, Target target, Game game) { - game.fireSelectTargetEvent(playerId, target.getMessage(), target.isRequired()); + public boolean chooseTarget(Outcome outcome, Target target, Ability source, Game game) { while (!abort) { + game.fireSelectTargetEvent(playerId, target.getMessage(), target.isRequired()); waitForResponse(); if (response.getUUID() != null) { - if (target.canTarget(response.getUUID(), game)) { - target.addTarget(response.getUUID(), game); + if (target.canTarget(response.getUUID(), source, game)) { + target.addTarget(response.getUUID(), source, game); return true; } } else if (!target.isRequired()) { @@ -162,13 +172,13 @@ public class HumanPlayer extends PlayerImpl { } @Override - public boolean chooseTarget(Cards cards, TargetCard target, Game game) { + public boolean chooseTarget(Cards cards, TargetCard target, Ability source, Game game) { game.fireSelectTargetEvent(playerId, target.getMessage(), cards, target.isRequired()); while (!abort) { waitForResponse(); if (response.getUUID() != null) { if (target.canTarget(response.getUUID(), cards, game)) { - target.addTarget(response.getUUID(), game); + target.addTarget(response.getUUID(), source, game); return true; } } else if (!target.isRequired()) { @@ -180,15 +190,15 @@ public class HumanPlayer extends PlayerImpl { @Override - public boolean chooseTargetAmount(Outcome outcome, TargetAmount target, Game game) { + public boolean chooseTargetAmount(Outcome outcome, TargetAmount target, Ability source, Game game) { game.fireSelectTargetEvent(playerId, target.getMessage() + "\n Amount remaining:" + target.getAmountRemaining(), target.isRequired()); while (!abort) { waitForResponse(); if (response.getUUID() != null) { - if (target.canTarget(response.getUUID(), game)) { + if (target.canTarget(response.getUUID(), source, game)) { UUID targetId = response.getUUID(); int amountSelected = getAmount(1, target.getAmountRemaining(), "Select amount", game); - target.addTarget(targetId, amountSelected, game); + target.addTarget(targetId, amountSelected, source, game); return true; } } else if (!target.isRequired()) { @@ -313,7 +323,7 @@ public class HumanPlayer extends PlayerImpl { if (response.getBoolean() != null) { return; } else if (response.getUUID() != null) { - if (targetCombat.canTarget(playerId, response.getUUID(), game)) { + if (targetCombat.canTarget(playerId, response.getUUID(), null, game)) { selectDefender(game.getCombat().getDefenders(), response.getUUID(), game); } } @@ -321,11 +331,17 @@ public class HumanPlayer extends PlayerImpl { } protected boolean selectDefender(Set defenders, UUID attackerId, Game game) { - TargetDefender target = new TargetDefender(defenders, attackerId); - if (chooseTarget(Outcome.Damage, target, game)) { - declareAttacker(attackerId, response.getUUID(), game); + if (defenders.size() == 1) { + declareAttacker(attackerId, defenders.iterator().next(), game); return true; } + else { + TargetDefender target = new TargetDefender(defenders, attackerId); + if (chooseTarget(Outcome.Damage, target, null, game)) { + declareAttacker(attackerId, response.getUUID(), game); + return true; + } + } return false; } @@ -338,7 +354,7 @@ public class HumanPlayer extends PlayerImpl { if (response.getBoolean() != null) { return; } else if (response.getUUID() != null) { - if (targetCombat.canTarget(playerId, response.getUUID(), game)) { + if (targetCombat.canTarget(playerId, response.getUUID(), null, game)) { selectCombatGroup(response.getUUID(), game); } } @@ -361,7 +377,7 @@ public class HumanPlayer extends PlayerImpl { int remainingDamage = damage; while (remainingDamage > 0) { Target target = new TargetCreatureOrPlayer(); - chooseTarget(Outcome.Damage, target, game); + chooseTarget(Outcome.Damage, target, null, game); if (targets.size() == 0 || targets.contains(target.getFirstTarget())) { int damageAmount = getAmount(0, remainingDamage, "Select amount", game); Permanent permanent = game.getPermanent(target.getFirstTarget()); @@ -449,4 +465,9 @@ public class HumanPlayer extends PlayerImpl { } } + @Override + public HumanPlayer copy() { + return new HumanPlayer(this); + } + } diff --git a/Mage.Server/config/config.xml b/Mage.Server/config/config.xml index 8514f0a0b7d..23a34d273b1 100644 --- a/Mage.Server/config/config.xml +++ b/Mage.Server/config/config.xml @@ -5,6 +5,8 @@ + + diff --git a/Mage.Server/nbproject/project.properties b/Mage.Server/nbproject/project.properties index 5fb1ef044d2..562a7190eee 100644 --- a/Mage.Server/nbproject/project.properties +++ b/Mage.Server/nbproject/project.properties @@ -20,6 +20,7 @@ debug.test.classpath=\ dist.dir=dist dist.jar=${dist.dir}/Mage.Server.jar dist.javadoc.dir=${dist.dir}/javadoc +endorsed.classpath= excludes= includes=** jar.compress=false @@ -27,7 +28,9 @@ javac.classpath=\ ${reference.Mage.jar}:\ ${reference.Mage_Common.jar}:\ ${reference.Mage_Sets.jar}:\ - ${reference.Mage_Player_AI.jar} + ${reference.Mage_Player_AI.jar}:\ + ${reference.Mage_Player_Human.jar}:\ + ${reference.Mage_Player_AIMinimax.jar} # Space-separated list of extra javac options javac.compilerargs= javac.deprecation=false @@ -56,15 +59,19 @@ jaxbwiz.xjcrun.classpath=${libs.jaxb.classpath} main.class=mage.server.Main manifest.file=manifest.mf meta.inf.dir=${src.dir}/META-INF -platform.active=default_platform +platform.active=JDK_1.6_21 project.license=bsd project.Mage=../Mage project.Mage_Common=../Mage.Common project.Mage_Player_AI=../Mage.Player.AI +project.Mage_Player_AIMinimax=../Mage.Player.AIMinimax +project.Mage_Player_Human=../Mage.Player.Human project.Mage_Sets=../Mage.Sets reference.Mage.jar=${project.Mage}/dist/Mage.jar reference.Mage_Common.jar=${project.Mage_Common}/dist/Mage.Common.jar reference.Mage_Player_AI.jar=${project.Mage_Player_AI}/dist/Mage.Player.AI.jar +reference.Mage_Player_AIMinimax.jar=${project.Mage_Player_AIMinimax}/dist/Mage.Player.AIMinimax.jar +reference.Mage_Player_Human.jar=${project.Mage_Player_Human}/dist/Mage.Player.Human.jar reference.Mage_Sets.jar=${project.Mage_Sets}/dist/Mage.Sets.jar run-sys-prop.java.endorsed.dirs=${jaxbwiz.endorsed.dirs} run.classpath=\ diff --git a/Mage.Server/nbproject/project.xml b/Mage.Server/nbproject/project.xml index 85a76cd19b5..58756015ace 100644 --- a/Mage.Server/nbproject/project.xml +++ b/Mage.Server/nbproject/project.xml @@ -9,6 +9,7 @@ Mage.Server + @@ -41,6 +42,22 @@ clean jar + + Mage_Player_AIMinimax + jar + + jar + clean + jar + + + Mage_Player_Human + jar + + jar + clean + jar + Mage_Sets jar diff --git a/Mage.Server/plugins/AIMinimax.properties b/Mage.Server/plugins/AIMinimax.properties new file mode 100644 index 00000000000..96b5480294c --- /dev/null +++ b/Mage.Server/plugins/AIMinimax.properties @@ -0,0 +1,7 @@ +maxDepth=10 +maxNodes=5000 +evaluatorLifeFactor=2 +evaluatorPermanentFactor=1 +evaluatorCreatureFactor=1 +evaluatorHandFactor=1 +maxThinkSeconds=30 \ No newline at end of file diff --git a/Mage.Server/plugins/Mage.Deck.Constructed.jar b/Mage.Server/plugins/Mage.Deck.Constructed.jar index af635fbe68dfda8b3a4937462ee27b32cd50c248..daf82be21ec4dd264df934222565cff9cff970b4 100644 GIT binary patch delta 783 zcmZ9KTTc@~6vuzN-R^9+n;I;w1-VKU!#M0WBI(j`;XTcSXR8aN}Cl;(|q^O)*V2$7uG`LaRb;g1DgF zqQm02#R-c}i<35|=&~{C7M!-(MvqMqXZ-YXR;8Zv)5m$WT@dv92{RzLXcOU*I$ZX1 zg+akp!8HxDIhjhn|DhG?krIXk!ww^i3a&dO7<0J6s9RpUZIf2I^ia7fj7$zg(GVei06cAl&4T-?bLaK*R%R^>w747Usx53_z%~Vr^q163Vu23!erS<{&7cqG|ogs!f$RXL` zuw<4=o2yYLYvfq<|5!(AWLT#`5_OKUPg+|R3(L9LYx??nG(F$5HNtMj*RVOm?pt4L YZg_wCVmT1C>;9kO3A@eQuw+U80KA2cGynhq delta 920 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 delta 1440 zcmZ8h&2Jk;6#vcc+H0>T8z(7gjFTo!(>DIXr36ZvG!1oVVp6Aud=XkcHp$j;YdfyJ zZiED~xN|{!f)fW2mkLTk6Ggd{9uZPGbKws_;)+_SDsOg;9c3-gym|9}zxOdS?_lsr zub!Qi6%FG1qvEXp?ssNeXG6MB{F{huKlK4mefA`a*K2#4V0E5*)AMJVy?z z3>~eYW{m2_ylHW4Ag|&!#~l>N2}cnn^2D);GRIw1DC`zWq{30fEviP*$eGEUQ8bea zTbuTmy4J(c*-}o^PP$koQp^6;TTjTtktGH#XIcxjN@=cEr1YznQL*x+T&p5p^KQ*3 zRP7DlUv=47ryA)FWoolT#wDwYlXQ;V&b3iNQJ{~;6SU4irXLW9Zi8(@+JpQvEhY5R zsL>2WX^vrlqyg8I$OdUeAe_Pw5eexu&X6@j{VawX+~*wy;5&e_vIoxtGIWW(4k+o6 za!2+xNEssJ(2Xrlvmlo|_m3b)4k4u`GA599C#^&SDhH1p#f~1r%1taCdBdwgF^aUq z(Or0zZ=uJczd+rE?;H4|u|4?j%YssNDx^CVxlC_n5+Ph6>N=>-4!&8YFkV5D*v}D6 zfz;(8nQm9Z-_?dcO^|EkG|)N#mwp-^%)pPd6S>Q-HQ6=1=CCu2;B`oD{=XBchuozO zz^U|YnP_Ym`aaqj_7P;5OYEbA;cJ{Cp_Abex-Kdqg^Vao9uFz|I3{8FhZldw#i=Py zvfU1420qNu6S+>_vUrQ0{Qy~v&|Vy!c%x3iDAZ&kxPUjE>ZF>XrW&F8FoscFq!`Bt zDlUMl87GY>Qaj}5=uvS=#l-1nq`XW@iApSyKOtSg%QSbA)PoxcO@^qU-9YbTC_Bp! zx5u5OwiU^3h&$~UoK9)~(>~#9&IOaMuofIS+AF^gUK$kb^Fj3SRxh2V_@5+~|9{_o Uag&;`UDv~Qe@C}>kBg1}0Q<4>bN~PV diff --git a/Mage.Server/plugins/Mage.Player.AI.jar b/Mage.Server/plugins/Mage.Player.AI.jar index dd2678c0f3b0407bc0feee740542370af5f87d53..d72e2390a115257bc075b76f5a7c830342482d27 100644 GIT binary patch delta 21634 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 delta 18679 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 N9yr=45P3-k{2$Zh5wZXP diff --git a/Mage.Server/plugins/Mage.Player.AIMinimax.jar b/Mage.Server/plugins/Mage.Player.AIMinimax.jar new file mode 100644 index 0000000000000000000000000000000000000000..7031677ed67c07b67bc655ce2b7424b11329e692 GIT binary patch literal 70592 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 Vx;Ey^+8S=LkY>T~Ke$1j{ujUYA6ozb literal 18066 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 - - + + + + - + + - + diff --git a/Mage.Server/src/mage/server/ChatManager.java b/Mage.Server/src/mage/server/ChatManager.java index 2434db2877c..55e2cc8fa50 100644 --- a/Mage.Server/src/mage/server/ChatManager.java +++ b/Mage.Server/src/mage/server/ChatManager.java @@ -30,6 +30,7 @@ package mage.server; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; +import mage.view.ChatMessage.MessageColor; /** * @@ -65,8 +66,8 @@ public class ChatManager { chatSessions.remove(chatId); } - public void broadcast(UUID chatId, String userName, String message) { - chatSessions.get(chatId).broadcast(userName, message); + public void broadcast(UUID chatId, String userName, String message, MessageColor color) { + chatSessions.get(chatId).broadcast(userName, message, color); } void removeSession(UUID sessionId) { diff --git a/Mage.Server/src/mage/server/ChatSession.java b/Mage.Server/src/mage/server/ChatSession.java index cd39ce440c4..7e3cde350cb 100644 --- a/Mage.Server/src/mage/server/ChatSession.java +++ b/Mage.Server/src/mage/server/ChatSession.java @@ -39,6 +39,7 @@ import java.util.logging.Logger; import mage.interfaces.callback.ClientCallback; import mage.util.Logging; import mage.view.ChatMessage; +import mage.view.ChatMessage.MessageColor; /** * @@ -59,7 +60,7 @@ public class ChatSession { public void join(String userName, UUID sessionId) { clients.put(sessionId, userName); - broadcast(userName, " has joined"); + broadcast(userName, " has joined", MessageColor.BLACK); logger.info(userName + " joined chat " + chatId); } @@ -67,18 +68,18 @@ public class ChatSession { if (clients.containsKey(sessionId)) { String userName = clients.get(sessionId); clients.remove(sessionId); - broadcast(userName, " has left"); + broadcast(userName, " has left", MessageColor.BLACK); logger.info(userName + " has left chat " + chatId); } } - public void broadcast(String userName, String message) { + public void broadcast(String userName, String message, MessageColor color) { Calendar cal = new GregorianCalendar(); final String msg = timeFormatter.format(cal.getTime()) + " " + userName + ":" + message; for (UUID sessionId: clients.keySet()) { Session session = SessionManager.getInstance().getSession(sessionId); if (session != null) - session.fireCallback(new ClientCallback("chatMessage", new ChatMessage(chatId, msg))); + session.fireCallback(new ClientCallback("chatMessage", new ChatMessage(chatId, msg, color))); else kill(sessionId); } diff --git a/Mage.Server/src/mage/server/ServerImpl.java b/Mage.Server/src/mage/server/ServerImpl.java index 50d57d6f3b4..067c50aaa13 100644 --- a/Mage.Server/src/mage/server/ServerImpl.java +++ b/Mage.Server/src/mage/server/ServerImpl.java @@ -43,6 +43,7 @@ import mage.cards.decks.DeckCardLists; import mage.game.GameException; import mage.interfaces.MageException; import mage.interfaces.Server; +import mage.interfaces.ServerState; import mage.interfaces.callback.ClientCallback; import mage.server.game.DeckValidatorFactory; import mage.server.game.GameFactory; @@ -52,6 +53,7 @@ import mage.server.game.PlayerFactory; import mage.server.game.ReplayManager; import mage.server.game.TableManager; import mage.util.Logging; +import mage.view.ChatMessage.MessageColor; import mage.view.GameTypeView; import mage.view.TableView; @@ -126,9 +128,9 @@ public class ServerImpl extends RemoteServer implements Server { } @Override - public boolean joinTable(UUID sessionId, UUID roomId, UUID tableId, int seatNum, String name, DeckCardLists deckList) throws MageException, GameException { + public boolean joinTable(UUID sessionId, UUID roomId, UUID tableId, String name, DeckCardLists deckList) throws MageException, GameException { try { - boolean ret = GamesRoomManager.getInstance().getRoom(roomId).joinTable(sessionId, tableId, seatNum, name, deckList); + boolean ret = GamesRoomManager.getInstance().getRoom(roomId).joinTable(sessionId, tableId, name, deckList); logger.info("Session " + sessionId + " joined table " + tableId); return ret; } @@ -186,7 +188,7 @@ public class ServerImpl extends RemoteServer implements Server { @Override public void sendChatMessage(UUID chatId, String userName, String message) throws MageException { try { - ChatManager.getInstance().broadcast(chatId, userName, message); + ChatManager.getInstance().broadcast(chatId, userName, message, MessageColor.BLUE); } catch (Exception ex) { handleException(ex); @@ -431,9 +433,13 @@ public class ServerImpl extends RemoteServer implements Server { } @Override - public List getGameTypes() throws MageException { + public ServerState getServerState() throws RemoteException, MageException { try { - return GameFactory.getInstance().getGameTypes(); + return new ServerState( + GameFactory.getInstance().getGameTypes(), + PlayerFactory.getInstance().getPlayerTypes().toArray(new String[0]), + DeckValidatorFactory.getInstance().getDeckTypes().toArray(new String[0]), + testMode); } catch (Exception ex) { handleException(ex); @@ -441,33 +447,44 @@ public class ServerImpl extends RemoteServer implements Server { return null; } - @Override - public String[] getPlayerTypes() throws MageException { - try { - return PlayerFactory.getInstance().getPlayerTypes().toArray(new String[0]); - } - catch (Exception ex) { - handleException(ex); - } - return null; - } +// @Override +// public List getGameTypes() throws MageException { +// try { +// return GameFactory.getInstance().getGameTypes(); +// } +// catch (Exception ex) { +// handleException(ex); +// } +// return null; +// } +// +// @Override +// public String[] getPlayerTypes() throws MageException { +// try { +// return PlayerFactory.getInstance().getPlayerTypes().toArray(new String[0]); +// } +// catch (Exception ex) { +// handleException(ex); +// } +// return null; +// } +// +// @Override +// public String[] getDeckTypes() throws MageException { +// try { +// return DeckValidatorFactory.getInstance().getDeckTypes().toArray(new String[0]); +// } +// catch (Exception ex) { +// handleException(ex); +// } +// return null; +// } @Override - public String[] getDeckTypes() throws MageException { - try { - return DeckValidatorFactory.getInstance().getDeckTypes().toArray(new String[0]); - } - catch (Exception ex) { - handleException(ex); - } - return null; - } - - @Override - public void cheat(UUID gameId, UUID sessionId, DeckCardLists deckList) throws MageException { + public void cheat(UUID gameId, UUID sessionId, UUID playerId, DeckCardLists deckList) throws MageException { try { if (testMode) - GameManager.getInstance().cheat(gameId, sessionId, deckList); + GameManager.getInstance().cheat(gameId, sessionId, playerId, deckList); } catch (Exception ex) { handleException(ex); diff --git a/Mage.Server/src/mage/server/Session.java b/Mage.Server/src/mage/server/Session.java index dc6265704a5..fd77530c12e 100644 --- a/Mage.Server/src/mage/server/Session.java +++ b/Mage.Server/src/mage/server/Session.java @@ -77,10 +77,11 @@ public class Session { return null; } - public void fireCallback(ClientCallback call) { + public synchronized void fireCallback(ClientCallback call) { try { call.setMessageId(messageId++); - logger.info(sessionId + " - " + call.getMessageId() + " - " + call.getMethod()); + if (logger.isLoggable(Level.FINE)) + logger.fine(sessionId + " - " + call.getMessageId() + " - " + call.getMethod()); callback.setCallback(call); } catch (InterruptedException ex) { logger.log(Level.SEVERE, null, ex); diff --git a/Mage.Server/src/mage/server/game/GameController.java b/Mage.Server/src/mage/server/game/GameController.java index 916aa2698b9..f3901609d86 100644 --- a/Mage.Server/src/mage/server/game/GameController.java +++ b/Mage.Server/src/mage/server/game/GameController.java @@ -50,6 +50,7 @@ import mage.players.Player; import mage.util.Logging; import mage.view.AbilityPickerView; import mage.view.CardsView; +import mage.view.ChatMessage.MessageColor; import mage.view.GameView; /** @@ -90,7 +91,7 @@ public class GameController implements GameCallback { updateGame(); break; case INFO: - ChatManager.getInstance().broadcast(chatId, "", event.getMessage()); + ChatManager.getInstance().broadcast(chatId, "", event.getMessage(), MessageColor.BLACK); logger.finest(game.getId() + " " + event.getMessage()); break; case REVEAL: @@ -152,7 +153,7 @@ public class GameController implements GameCallback { gameSessions.put(playerId, gameSession); logger.info("player " + playerId + " has joined game " + game.getId()); // gameSession.init(getGameView(playerId)); - ChatManager.getInstance().broadcast(chatId, "", game.getPlayer(playerId).getName() + " has joined the game"); + ChatManager.getInstance().broadcast(chatId, "", game.getPlayer(playerId).getName() + " has joined the game", MessageColor.BLACK); if (allJoined()) { startGame(); } @@ -181,12 +182,12 @@ public class GameController implements GameCallback { GameWatcher gameWatcher = new GameWatcher(sessionId, game.getId()); watchers.put(sessionId, gameWatcher); gameWatcher.init(getGameView()); - ChatManager.getInstance().broadcast(chatId, "", " has started watching"); + ChatManager.getInstance().broadcast(chatId, "", " has started watching", MessageColor.BLACK); } public void stopWatching(UUID sessionId) { watchers.remove(sessionId); - ChatManager.getInstance().broadcast(chatId, "", " has stopped watching"); + ChatManager.getInstance().broadcast(chatId, "", " has stopped watching", MessageColor.BLACK); } public void concede(UUID sessionId) { @@ -197,11 +198,11 @@ public class GameController implements GameCallback { game.quit(getPlayerId(sessionId)); } - public void cheat(UUID sessionId, DeckCardLists deckList) { - Player player = game.getPlayer(getPlayerId(sessionId)); + public void cheat(UUID sessionId, UUID playerId, DeckCardLists deckList) { + Player player = game.getPlayer(playerId); Deck deck = Deck.load(deckList); - deck.setOwnerId(player.getId()); - for (Card card: deck.getCards().values()) { + game.loadCards(deck.getCards(), playerId); + for (Card card: deck.getCards()) { player.putOntoBattlefield(card, game); } updateGame(); @@ -222,7 +223,7 @@ public class GameController implements GameCallback { public void timeout(UUID sessionId) { if (sessionPlayerMap.containsKey(sessionId)) { - ChatManager.getInstance().broadcast(chatId, "", game.getPlayer(sessionPlayerMap.get(sessionId)).getName() + " has timed out. Auto concede."); + ChatManager.getInstance().broadcast(chatId, "", game.getPlayer(sessionPlayerMap.get(sessionId)).getName() + " has timed out. Auto concede.", MessageColor.BLACK); concede(sessionId); } } @@ -272,68 +273,72 @@ public class GameController implements GameCallback { } private synchronized void ask(UUID playerId, String question) { - informOthers(playerId); if (gameSessions.containsKey(playerId)) gameSessions.get(playerId).ask(question, getGameView(playerId)); + informOthers(playerId); } private synchronized void chooseAbility(UUID playerId, Collection choices) { - informOthers(playerId); if (gameSessions.containsKey(playerId)) gameSessions.get(playerId).chooseAbility(new AbilityPickerView(choices)); + informOthers(playerId); } private synchronized void choose(UUID playerId, String message, String[] choices) { - informOthers(playerId); if (gameSessions.containsKey(playerId)) gameSessions.get(playerId).choose(message, choices); + informOthers(playerId); } private synchronized void target(UUID playerId, String question, Cards cards, boolean required) { + if (gameSessions.containsKey(playerId)) { + if (cards != null) + gameSessions.get(playerId).target(question, new CardsView(cards.getCards(game)), required, getGameView(playerId)); + else + gameSessions.get(playerId).target(question, new CardsView(), required, getGameView(playerId)); + } informOthers(playerId); - if (gameSessions.containsKey(playerId)) - gameSessions.get(playerId).target(question, new CardsView(cards), required, getGameView(playerId)); } private synchronized void target(UUID playerId, String question, Collection abilities, boolean required) { - informOthers(playerId); if (gameSessions.containsKey(playerId)) - gameSessions.get(playerId).target(question, new CardsView(abilities, game), required, getGameView(playerId)); + gameSessions.get(playerId).target(question, new CardsView(abilities, game.getState()), required, getGameView(playerId)); + informOthers(playerId); } private synchronized void select(UUID playerId, String message) { - informOthers(playerId); if (gameSessions.containsKey(playerId)) gameSessions.get(playerId).select(message, getGameView(playerId)); + informOthers(playerId); } private synchronized void playMana(UUID playerId, String message) { - informOthers(playerId); if (gameSessions.containsKey(playerId)) gameSessions.get(playerId).playMana(message, getGameView(playerId)); + informOthers(playerId); } private synchronized void playXMana(UUID playerId, String message) { - informOthers(playerId); if (gameSessions.containsKey(playerId)) gameSessions.get(playerId).playXMana(message, getGameView(playerId)); + informOthers(playerId); } private synchronized void amount(UUID playerId, String message, int min, int max) { - informOthers(playerId); if (gameSessions.containsKey(playerId)) gameSessions.get(playerId).getAmount(message, min, max); + informOthers(playerId); } private synchronized void revealCards(String name, Cards cards) { for (GameSession session: gameSessions.values()) { - session.revealCards(name, new CardsView(cards)); + session.revealCards(name, new CardsView(cards.getCards(game))); } } private synchronized void lookAtCards(UUID playerId, String name, Cards cards) { if (gameSessions.containsKey(playerId)) - gameSessions.get(playerId).revealCards(name, new CardsView(cards)); + gameSessions.get(playerId).revealCards(name, new CardsView(cards.getCards(game))); } private void informOthers(UUID playerId) { @@ -349,12 +354,12 @@ public class GameController implements GameCallback { } private GameView getGameView() { - return new GameView(game.getState()); + return new GameView(game.getState(), game); } private GameView getGameView(UUID playerId) { - GameView gameView = new GameView(game.getState()); - gameView.setHand(new CardsView(game.getPlayer(playerId).getHand())); + GameView gameView = new GameView(game.getState(), game); + gameView.setHand(new CardsView(game.getPlayer(playerId).getHand().getCards(game))); return gameView; } diff --git a/Mage.Server/src/mage/server/game/GameManager.java b/Mage.Server/src/mage/server/game/GameManager.java index 8f02c9a3c22..77da7725e4a 100644 --- a/Mage.Server/src/mage/server/game/GameManager.java +++ b/Mage.Server/src/mage/server/game/GameManager.java @@ -108,8 +108,8 @@ public class GameManager { // return gameControllers.get(gameId).createReplay(); // } - public void cheat(UUID gameId, UUID sessionId, DeckCardLists deckList) { - gameControllers.get(gameId).cheat(sessionId, deckList); + public void cheat(UUID gameId, UUID sessionId, UUID playerId, DeckCardLists deckList) { + gameControllers.get(gameId).cheat(sessionId, playerId, deckList); } void timeout(UUID gameId, UUID sessionId) { diff --git a/Mage.Server/src/mage/server/game/GameReplay.java b/Mage.Server/src/mage/server/game/GameReplay.java index d14bec528db..61ab7284b3c 100644 --- a/Mage.Server/src/mage/server/game/GameReplay.java +++ b/Mage.Server/src/mage/server/game/GameReplay.java @@ -37,10 +37,12 @@ import mage.game.*; public class GameReplay { private GameStates savedGame; + private Game game; private int stateIndex; - public GameReplay(GameStates savedGame) { - this.savedGame = savedGame; + public GameReplay(Game game) { + this.savedGame = game.getGameStates(); + this.game = game; } public void start() { @@ -61,4 +63,7 @@ public class GameReplay { return null; } + public Game getGame() { + return this.game; + } } diff --git a/Mage.Server/src/mage/server/game/GameSession.java b/Mage.Server/src/mage/server/game/GameSession.java index fe3da1a1c9a..096294358ae 100644 --- a/Mage.Server/src/mage/server/game/GameSession.java +++ b/Mage.Server/src/mage/server/game/GameSession.java @@ -63,6 +63,7 @@ public class GameSession extends GameWatcher { public void ask(final String question, final GameView gameView) { if (!killed) { + setupTimeout(); Session session = SessionManager.getInstance().getSession(sessionId); if (session != null) session.fireCallback(new ClientCallback("gameAsk", new GameClientMessage(gameView, question))); @@ -71,6 +72,7 @@ public class GameSession extends GameWatcher { public void target(final String question, final CardsView cardView, final boolean required, final GameView gameView) { if (!killed) { + setupTimeout(); Session session = SessionManager.getInstance().getSession(sessionId); if (session != null) session.fireCallback(new ClientCallback("gameTarget", new GameClientMessage(gameView, question, cardView, required))); @@ -79,6 +81,7 @@ public class GameSession extends GameWatcher { public void select(final String message, final GameView gameView) { if (!killed) { + setupTimeout(); Session session = SessionManager.getInstance().getSession(sessionId); if (session != null) session.fireCallback(new ClientCallback("gameSelect", new GameClientMessage(gameView, message))); @@ -87,6 +90,7 @@ public class GameSession extends GameWatcher { public void chooseAbility(final AbilityPickerView abilities) { if (!killed) { + setupTimeout(); Session session = SessionManager.getInstance().getSession(sessionId); if (session != null) session.fireCallback(new ClientCallback("gameChooseAbility", abilities)); @@ -95,6 +99,7 @@ public class GameSession extends GameWatcher { public void choose(final String message, final String[] choices) { if (!killed) { + setupTimeout(); Session session = SessionManager.getInstance().getSession(sessionId); if (session != null) session.fireCallback(new ClientCallback("gameChoose", new GameClientMessage(choices, message))); @@ -103,6 +108,7 @@ public class GameSession extends GameWatcher { public void playMana(final String message, final GameView gameView) { if (!killed) { + setupTimeout(); Session session = SessionManager.getInstance().getSession(sessionId); if (session != null) session.fireCallback(new ClientCallback("gamePlayMana", new GameClientMessage(gameView, message))); @@ -111,6 +117,7 @@ public class GameSession extends GameWatcher { public void playXMana(final String message, final GameView gameView) { if (!killed) { + setupTimeout(); Session session = SessionManager.getInstance().getSession(sessionId); if (session != null) session.fireCallback(new ClientCallback("gamePlayXMana", new GameClientMessage(gameView, message))); @@ -119,6 +126,7 @@ public class GameSession extends GameWatcher { public void getAmount(final String message, final int min, final int max) { if (!killed) { + setupTimeout(); Session session = SessionManager.getInstance().getSession(sessionId); if (session != null) session.fireCallback(new ClientCallback("gameSelectAmount", new GameClientMessage(message, min, max))); diff --git a/Mage.Server/src/mage/server/game/GamesRoom.java b/Mage.Server/src/mage/server/game/GamesRoom.java index d7022b0d034..3e72cdaa061 100644 --- a/Mage.Server/src/mage/server/game/GamesRoom.java +++ b/Mage.Server/src/mage/server/game/GamesRoom.java @@ -43,7 +43,7 @@ import mage.view.TableView; public interface GamesRoom extends Room { public List getTables(); - public boolean joinTable(UUID sessionId, UUID tableId, int seatNum, String name, DeckCardLists deckList) throws GameException; + public boolean joinTable(UUID sessionId, UUID tableId, String name, DeckCardLists deckList) throws GameException; public TableView createTable(UUID sessionId, String gameType, String deckType, List playerTypes, MultiplayerAttackOption attackOption, RangeOfInfluence range); public void removeTable(UUID sessionId, UUID tableId); public TableView getTable(UUID tableId); diff --git a/Mage.Server/src/mage/server/game/GamesRoomImpl.java b/Mage.Server/src/mage/server/game/GamesRoomImpl.java index ad87a33b98d..a4fc250fa50 100644 --- a/Mage.Server/src/mage/server/game/GamesRoomImpl.java +++ b/Mage.Server/src/mage/server/game/GamesRoomImpl.java @@ -62,9 +62,9 @@ public class GamesRoomImpl extends RoomImpl implements GamesRoom, Serializable { } @Override - public boolean joinTable(UUID sessionId, UUID tableId, int seatNum, String name, DeckCardLists deckList) throws GameException { + public boolean joinTable(UUID sessionId, UUID tableId, String name, DeckCardLists deckList) throws GameException { if (tables.containsKey(tableId)) { - return TableManager.getInstance().joinTable(sessionId, tableId, seatNum, name, deckList); + return TableManager.getInstance().joinTable(sessionId, tableId, name, deckList); } else { return false; } diff --git a/Mage.Server/src/mage/server/game/PlayerFactory.java b/Mage.Server/src/mage/server/game/PlayerFactory.java index 15dcbef50c7..4242ccec1a3 100644 --- a/Mage.Server/src/mage/server/game/PlayerFactory.java +++ b/Mage.Server/src/mage/server/game/PlayerFactory.java @@ -30,6 +30,7 @@ package mage.server.game; import java.lang.reflect.Constructor; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; import java.util.logging.Level; @@ -48,7 +49,7 @@ public class PlayerFactory { private final static PlayerFactory INSTANCE = new PlayerFactory(); private final static Logger logger = Logging.getLogger(PlayerFactory.class.getName()); - private Map playerTypes = new HashMap(); + private Map playerTypes = new LinkedHashMap(); public static PlayerFactory getInstance() { return INSTANCE; diff --git a/Mage.Server/src/mage/server/game/ReplaySession.java b/Mage.Server/src/mage/server/game/ReplaySession.java index d3c0fe1dd38..14d295175e6 100644 --- a/Mage.Server/src/mage/server/game/ReplaySession.java +++ b/Mage.Server/src/mage/server/game/ReplaySession.java @@ -30,6 +30,7 @@ package mage.server.game; import java.util.UUID; import java.util.logging.Logger; +import mage.game.Game; import mage.game.GameState; import mage.interfaces.callback.ClientCallback; import mage.server.Session; @@ -45,19 +46,19 @@ public class ReplaySession implements GameCallback { private final static Logger logger = Logging.getLogger(ReplaySession.class.getName()); - private GameReplay game; + private GameReplay replay; protected UUID sessionId; ReplaySession(UUID tableId, UUID sessionId) { - this.game = TableManager.getInstance().createReplay(tableId); + this.replay = TableManager.getInstance().createReplay(tableId); this.sessionId = sessionId; } public void replay() { - game.start(); + replay.start(); Session session = SessionManager.getInstance().getSession(sessionId); if (session != null) - session.fireCallback(new ClientCallback("replayInit", new GameView(game.next()))); + session.fireCallback(new ClientCallback("replayInit", new GameView(replay.next(), replay.getGame()))); } public void stop() { @@ -65,11 +66,11 @@ public class ReplaySession implements GameCallback { } public synchronized void next() { - updateGame(game.next()); + updateGame(replay.next(), replay.getGame()); } public synchronized void previous() { - updateGame(game.previous()); + updateGame(replay.previous(), replay.getGame()); } @Override @@ -79,14 +80,14 @@ public class ReplaySession implements GameCallback { session.fireCallback(new ClientCallback("replayDone", result)); } - private void updateGame(final GameState state) { + private void updateGame(final GameState state, Game game) { if (state == null) { gameResult("game ended"); } else { Session session = SessionManager.getInstance().getSession(sessionId); if (session != null) - session.fireCallback(new ClientCallback("replayUpdate", new GameView(state))); + session.fireCallback(new ClientCallback("replayUpdate", new GameView(state, game))); } } diff --git a/Mage.Server/src/mage/server/game/TableController.java b/Mage.Server/src/mage/server/game/TableController.java index efd1ad37b02..ba5e30da5b5 100644 --- a/Mage.Server/src/mage/server/game/TableController.java +++ b/Mage.Server/src/mage/server/game/TableController.java @@ -86,21 +86,26 @@ public class TableController { table = new Table(gameType, DeckValidatorFactory.getInstance().createDeckValidator(deckType), playerTypes); } - public synchronized boolean joinTable(UUID sessionId, int seatNum, String name, DeckCardLists deckList) throws GameException { + public synchronized boolean joinTable(UUID sessionId, String name, DeckCardLists deckList) throws GameException { if (table.getState() != TableState.WAITING) { return false; } - Seat seat = table.getSeats()[seatNum]; + Seat seat = table.getNextAvailableSeat(); + if (seat == null) { + throw new GameException("No available seats."); + } Deck deck = Deck.load(deckList); if (!Main.server.isTestMode() && !validDeck(deck)) { throw new GameException(name + " has an invalid deck for this format"); } Player player = createPlayer(name, deck, seat.getPlayerType()); - table.joinTable(player, seatNum); + game.loadCards(deck.getCards(), player.getId()); + game.loadCards(deck.getSideboard(), player.getId()); + table.joinTable(player, seat); logger.info("player joined " + player.getId()); //only add human players to sessionPlayerMap - if (table.getSeats()[seatNum].getPlayer().isHuman()) { + if (seat.getPlayer().isHuman()) { sessionPlayerMap.put(sessionId, player.getId()); } @@ -183,11 +188,11 @@ public class TableController { private void saveGame() { try { - //use buffering OutputStream file = new FileOutputStream("saved/" + game.getId().toString() + ".game"); OutputStream buffer = new BufferedOutputStream(file); ObjectOutput output = new ObjectOutputStream(new GZIPOutputStream(buffer)); try { + output.writeObject(game); output.writeObject(game.getGameStates()); } finally { @@ -200,17 +205,16 @@ public class TableController { } } - private GameStates loadGame() { + private Game loadGame() { try{ - //use buffering InputStream file = new FileInputStream("saved/" + gameId.toString() + ".game"); InputStream buffer = new BufferedInputStream(file); ObjectInput input = new CopierObjectInputStream(Main.classLoader, new GZIPInputStream(buffer)); - //ObjectInput input = new ObjectInputStream(buffer); try { - //deserialize the List - GameStates gameStates = (GameStates)input.readObject(); - return gameStates; + Game game = (Game)input.readObject(); + GameStates states = (GameStates)input.readObject(); + game.loadGameStates(states); + return game; } finally { input.close(); diff --git a/Mage.Server/src/mage/server/game/TableManager.java b/Mage.Server/src/mage/server/game/TableManager.java index effb5d9ebba..9369d13574c 100644 --- a/Mage.Server/src/mage/server/game/TableManager.java +++ b/Mage.Server/src/mage/server/game/TableManager.java @@ -71,8 +71,8 @@ public class TableManager { return tables.values(); } - public boolean joinTable(UUID sessionId, UUID tableId, int seatNum, String name, DeckCardLists deckList) throws GameException { - return controllers.get(tableId).joinTable(sessionId, seatNum, name, deckList); + public boolean joinTable(UUID sessionId, UUID tableId, String name, DeckCardLists deckList) throws GameException { + return controllers.get(tableId).joinTable(sessionId, name, deckList); } public void removeSession(UUID sessionId) { diff --git a/Mage.Sets/src/mage/sets/Magic2011.java b/Mage.Sets/src/mage/sets/Magic2011.java new file mode 100644 index 00000000000..e665b7d5751 --- /dev/null +++ b/Mage.Sets/src/mage/sets/Magic2011.java @@ -0,0 +1,102 @@ +/* +* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, are +* permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, this list of +* conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, this list +* of conditions and the following disclaimer in the documentation and/or other materials +* provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR +* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +* The views and conclusions contained in the software and documentation are those of the +* authors and should not be interpreted as representing official policies, either expressed +* or implied, of BetaSteward_at_googlemail.com. +*/ + +package mage.sets; + +import mage.sets.magic2011.*; +import mage.cards.ExpansionSet; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class Magic2011 extends ExpansionSet { + + private static final Magic2011 fINSTANCE = new Magic2011(); + + public static Magic2011 getInstance() { + return fINSTANCE; + } + + private Magic2011() { + this.name = "Magic 2011"; + this.cards.add(AcidicSlime.class); + this.cards.add(ActOfTreason.class); + this.cards.add(AetherAdept.class); + this.cards.add(AirServant.class); + this.cards.add(AjaniGoldmane.class); +// this.cards.add(AlluringSiren.class); +// this.cards.add(AngelsFeather.class); +// this.cards.add(AngelsMercy.class); +// this.cards.add(AntQueen.class); +// this.cards.add(ArmoredAscension.class); +// this.cards.add(Assassinate.class); +// this.cards.add(AwakenerDruid.class); +// this.cards.add(BallLightning.class); +// this.cards.add(BaneslayerAngel.class); +// this.cards.add(BirdsOfParadise.class); +// this.cards.add(BerserkersOfBloodRidge.class); +// this.cards.add(BlackKnight.class); +// this.cards.add(BlindingMage.class); +// this.cards.add(BogWraith.class); +// this.cards.add(BogardanHellkite.class); +// this.cards.add(Cancel.class); +// this.cards.add(CelestialPurge.class); +// this.cards.add(DiabolicTutor.class); +// this.cards.add(DoomBlade.class); +// this.cards.add(DragonskullSummit.class); +// this.cards.add(Earthquake.class); +// this.cards.add(EliteVanguard.class); +// this.cards.add(Flashfreeze.class); +// this.cards.add(GargoyleCastle.class); +// this.cards.add(GarrukWildspeaker.class); +// this.cards.add(GiantGrowth.class); +// this.cards.add(GlacialFortress.class); +// this.cards.add(GreatSableStag.class); +// this.cards.add(HonorOfThePure.class); +// this.cards.add(HowlingMine.class); +// this.cards.add(JaceBeleren.class); +// this.cards.add(LlanowarElves.class); +// this.cards.add(LightningBolt.class); +// this.cards.add(MasterOfTheWildHunt.class); +// this.cards.add(MightOfOaks.class); +// this.cards.add(MindRot.class); +// this.cards.add(Naturalize.class); +// this.cards.add(Overrun.class); +// this.cards.add(RampantGrowth.class); +// this.cards.add(MindSpring.class); +// this.cards.add(Negate.class); +// this.cards.add(RootboundCrag.class); +// this.cards.add(RoyalAssassin.class); +// this.cards.add(SafePassage.class); +// this.cards.add(SunpetalGrove.class); +// this.cards.add(TerramorphicExpanse.class); +// this.cards.add(TimeWarp.class); +// this.cards.add(WhiteKnight.class); + } +} diff --git a/Mage.Sets/src/mage/sets/RiseOfTheEldrazi.java b/Mage.Sets/src/mage/sets/RiseOfTheEldrazi.java index f0bebd7d8d5..02586d9a016 100644 --- a/Mage.Sets/src/mage/sets/RiseOfTheEldrazi.java +++ b/Mage.Sets/src/mage/sets/RiseOfTheEldrazi.java @@ -47,7 +47,11 @@ public class RiseOfTheEldrazi extends ExpansionSet { this.name = "Rise Of The Eldrazi"; this.cards.add(Deprive.class); this.cards.add(GideonJura.class); + this.cards.add(JoragaTreespeaker.class); this.cards.add(KarganDragonlord.class); + this.cards.add(KozileksPredator.class); + this.cards.add(NestInvader.class); + this.cards.add(Vengevine.class); this.cards.add(WallOfOmens.class); } diff --git a/Mage.Sets/src/mage/sets/Sets.java b/Mage.Sets/src/mage/sets/Sets.java index 724eda10377..e10c016bbd4 100644 --- a/Mage.Sets/src/mage/sets/Sets.java +++ b/Mage.Sets/src/mage/sets/Sets.java @@ -47,6 +47,7 @@ public class Sets extends ArrayList { this.add(AlaraReborn.getInstance()); this.add(Conflux.getInstance()); this.add(Magic2010.getInstance()); + this.add(Magic2011.getInstance()); this.add(Planechase.getInstance()); this.add(RiseOfTheEldrazi.getInstance()); this.add(ShardsOfAlara.getInstance()); diff --git a/Mage.Sets/src/mage/sets/Worldwake.java b/Mage.Sets/src/mage/sets/Worldwake.java index 0e6e5264f3f..2df474c09b9 100644 --- a/Mage.Sets/src/mage/sets/Worldwake.java +++ b/Mage.Sets/src/mage/sets/Worldwake.java @@ -45,10 +45,13 @@ public class Worldwake extends ExpansionSet { private Worldwake() { this.name = "Worldwake"; + this.cards.add(ArborElf.class); this.cards.add(BasiliskCollar.class); this.cards.add(CelestialColonnade.class); + this.cards.add(DreadStatuary.class); this.cards.add(EverflowingChalice.class); this.cards.add(JaceTheMindSculptor.class); + this.cards.add(KhalniGarden.class); this.cards.add(LavaclawReaches.class); this.cards.add(RagingRavine.class); this.cards.add(SearingBlaze.class); @@ -56,6 +59,7 @@ public class Worldwake extends ExpansionSet { this.cards.add(StirringWildwood.class); this.cards.add(StoneforgeMystic.class); this.cards.add(TectonicEdge.class); + this.cards.add(WolfbriarElemental.class); } } diff --git a/Mage.Sets/src/mage/sets/Zendikar.java b/Mage.Sets/src/mage/sets/Zendikar.java index 48e848ec47b..81c69fe9fdb 100644 --- a/Mage.Sets/src/mage/sets/Zendikar.java +++ b/Mage.Sets/src/mage/sets/Zendikar.java @@ -45,12 +45,16 @@ public class Zendikar extends ExpansionSet { private Zendikar() { this.name = "Zendikar"; + this.cards.add(AdventuringGear.class); + this.cards.add(AetherFigment.class); this.cards.add(ArchiveTrap.class); this.cards.add(AridMesa.class); + this.cards.add(BeastmasterAscension.class); this.cards.add(BraveTheElements.class); this.cards.add(BurstLightning.class); this.cards.add(ConquerorsPledge.class); this.cards.add(DayOfJudgment.class); + this.cards.add(EldraziMonument.class); this.cards.add(EmeriaAngel.class); this.cards.add(GoblinGuide.class); this.cards.add(GoblinRuinblaster.class); @@ -60,6 +64,7 @@ public class Zendikar extends ExpansionSet { this.cards.add(MistyRainforest.class); this.cards.add(OranRiefTheVastwood.class); this.cards.add(RampagingBaloths.class); + this.cards.add(RiverBoa.class); this.cards.add(ScaldingTarn.class); this.cards.add(ScuteMob.class); this.cards.add(SpreadingSeas.class); diff --git a/Mage.Sets/src/mage/sets/alarareborn/BehemothSledge.java b/Mage.Sets/src/mage/sets/alarareborn/BehemothSledge.java index 4efeb2096f4..f899d2b0a27 100644 --- a/Mage.Sets/src/mage/sets/alarareborn/BehemothSledge.java +++ b/Mage.Sets/src/mage/sets/alarareborn/BehemothSledge.java @@ -32,6 +32,7 @@ import java.util.UUID; import mage.Constants.CardType; import mage.Constants.Outcome; import mage.Constants.Zone; +import mage.MageObjectImpl; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.common.BoostEquippedEffect; @@ -46,12 +47,11 @@ import mage.sets.AlaraReborn; * * @author BetaSteward_at_googlemail.com */ -public class BehemothSledge extends CardImpl { +public class BehemothSledge extends CardImpl { public BehemothSledge(UUID ownerId) { super(ownerId, "Behemoth Sledge", new CardType[]{CardType.ARTIFACT}, "{1}{G}{W}"); this.expansionSetId = AlaraReborn.getInstance().getId(); - this.art = "120963_typ_reg_sty_010.jpg"; this.subtype.add("Equipment"); this.addAbility(new EquipAbility(Outcome.AddAbility, new GenericManaCost(3))); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAttachedEffect(LifelinkAbility.getInstance()))); @@ -59,4 +59,18 @@ public class BehemothSledge extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEquippedEffect(2, 2))); } + protected BehemothSledge(BehemothSledge me) { + super(me); + } + + @Override + public BehemothSledge copy() { + return new BehemothSledge(this); + } + + @Override + public String getArt() { + return "120963_typ_reg_sty_010.jpg"; + } + } diff --git a/Mage.Sets/src/mage/sets/alarareborn/BituminousBlast.java b/Mage.Sets/src/mage/sets/alarareborn/BituminousBlast.java index 3cb68af0c07..234e5764aa2 100644 --- a/Mage.Sets/src/mage/sets/alarareborn/BituminousBlast.java +++ b/Mage.Sets/src/mage/sets/alarareborn/BituminousBlast.java @@ -40,17 +40,30 @@ import mage.target.common.TargetCreaturePermanent; * * @author BetaSteward_at_googlemail.com */ -public class BituminousBlast extends CardImpl { +public class BituminousBlast extends CardImpl { public BituminousBlast(UUID ownerId) { super(ownerId, "Bituminous Blast", new CardType[]{CardType.INSTANT}, "{3}{B}{R}"); this.expansionSetId = AlaraReborn.getInstance().getId(); this.color.setBlack(true); this.color.setRed(true); - this.art = "121045_typ_reg_sty_010.jpg"; this.getSpellAbility().addTarget(new TargetCreaturePermanent()); this.getSpellAbility().addEffect(new DamageTargetEffect(4)); this.addAbility(new CascadeAbility()); } + public BituminousBlast(final BituminousBlast card) { + super(card); + } + + @Override + public BituminousBlast copy() { + return new BituminousBlast(this); + } + + @Override + public String getArt() { + return "121045_typ_reg_sty_010.jpg"; + } + } diff --git a/Mage.Sets/src/mage/sets/alarareborn/BloodbraidElf.java b/Mage.Sets/src/mage/sets/alarareborn/BloodbraidElf.java index 4444fb9df6f..48324dba483 100644 --- a/Mage.Sets/src/mage/sets/alarareborn/BloodbraidElf.java +++ b/Mage.Sets/src/mage/sets/alarareborn/BloodbraidElf.java @@ -40,7 +40,7 @@ import mage.sets.AlaraReborn; * * @author BetaSteward_at_googlemail.com */ -public class BloodbraidElf extends CardImpl { +public class BloodbraidElf extends CardImpl { public BloodbraidElf(UUID ownerId) { super(ownerId, "Bloodbraid Elf", new CardType[]{CardType.CREATURE}, "{2}{R}{G}"); @@ -49,10 +49,23 @@ public class BloodbraidElf extends CardImpl { this.color.setGreen(true); this.subtype.add("Elf"); this.subtype.add("Berserker"); - this.art = "121042_typ_reg_sty_010.jpg"; this.power = new MageInt(3); this.toughness = new MageInt(2); this.addAbility(HasteAbility.getInstance()); this.addAbility(new CascadeAbility()); - } + } + + public BloodbraidElf(final BloodbraidElf card) { + super(card); + } + + @Override + public BloodbraidElf copy() { + return new BloodbraidElf(this); + } + + @Override + public String getArt() { + return "121042_typ_reg_sty_010.jpg"; + } } diff --git a/Mage.Sets/src/mage/sets/alarareborn/FinestHour.java b/Mage.Sets/src/mage/sets/alarareborn/FinestHour.java index bb1a635e034..1af61244cb5 100644 --- a/Mage.Sets/src/mage/sets/alarareborn/FinestHour.java +++ b/Mage.Sets/src/mage/sets/alarareborn/FinestHour.java @@ -33,6 +33,7 @@ import mage.Constants.CardType; import mage.Constants.Outcome; import mage.Constants.TurnPhase; import mage.Constants.Zone; +import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.ExaltedAbility; @@ -49,7 +50,7 @@ import mage.target.common.TargetCreaturePermanent; * * @author BetaSteward_at_googlemail.com */ -public class FinestHour extends CardImpl { +public class FinestHour extends CardImpl { public FinestHour(UUID ownerId) { super(ownerId, "Finest Hour", new CardType[]{CardType.ENCHANTMENT}, "{2}{G}{W}{U}"); @@ -57,26 +58,48 @@ public class FinestHour extends CardImpl { this.color.setWhite(true); this.color.setGreen(true); this.color.setBlue(true); - this.art = "121018_typ_reg_sty_010.jpg"; this.addAbility(new ExaltedAbility()); this.addAbility(new FinestHourAbility()); } + public FinestHour(final FinestHour card) { + super(card); + } + + @Override + public FinestHour copy() { + return new FinestHour(this); + } + + @Override + public String getArt() { + return "121018_typ_reg_sty_010.jpg"; + } + } -class FinestHourAbility extends TriggeredAbilityImpl { +class FinestHourAbility extends TriggeredAbilityImpl { public FinestHourAbility() { super(Zone.BATTLEFIELD, new FinestHourEffect()); } + public FinestHourAbility(final FinestHourAbility ability) { + super(ability); + } + + @Override + public FinestHourAbility copy() { + return new FinestHourAbility(this); + } + @Override public boolean checkTrigger(GameEvent event, Game game) { if (checkIfClause(game) && game.getActivePlayerId().equals(this.controllerId)) { if (event.getType() == EventType.DECLARED_ATTACKERS) { if (game.getCombat().attacksAlone()) { this.addTarget(new TargetCreaturePermanent()); - this.targets.get(0).getTargets().add(game.getCombat().getAttackers().get(0)); + this.targets.get(0).addTarget(game.getCombat().getAttackers().get(0), this, game); trigger(game, event.getPlayerId()); return true; } @@ -97,18 +120,27 @@ class FinestHourAbility extends TriggeredAbilityImpl { } -class FinestHourEffect extends OneShotEffect { +class FinestHourEffect extends OneShotEffect { public FinestHourEffect() { super(Outcome.Benefit); } + public FinestHourEffect(final FinestHourEffect effect) { + super(effect); + } + @Override - public boolean apply(Game game) { - Permanent permanent = game.getPermanent(this.source.getFirstTarget()); + public FinestHourEffect copy() { + return new FinestHourEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); if (permanent != null) { permanent.setTapped(false); - game.getState().getTurnMods().add(new TurnMod(this.source.getControllerId(), TurnPhase.COMBAT, null, false)); + game.getState().getTurnMods().add(new TurnMod(source.getControllerId(), TurnPhase.COMBAT, null, false)); } else { return false; diff --git a/Mage.Sets/src/mage/sets/alarareborn/MaelstromPulse.java b/Mage.Sets/src/mage/sets/alarareborn/MaelstromPulse.java index 3d1e534df2a..23bfe9b3e90 100644 --- a/Mage.Sets/src/mage/sets/alarareborn/MaelstromPulse.java +++ b/Mage.Sets/src/mage/sets/alarareborn/MaelstromPulse.java @@ -39,16 +39,29 @@ import mage.target.common.TargetNonlandPermanent; * * @author BetaSteward_at_googlemail.com */ -public class MaelstromPulse extends CardImpl { +public class MaelstromPulse extends CardImpl { public MaelstromPulse(UUID ownerId) { super(ownerId, "Maelstrom Pulse", new CardType[]{CardType.SORCERY}, "{1}{B}{G}"); this.expansionSetId = AlaraReborn.getInstance().getId(); this.color.setBlack(true); this.color.setGreen(true); - this.art = "121021_typ_reg_sty_010.jpg"; this.getSpellAbility().addTarget(new TargetNonlandPermanent()); this.getSpellAbility().addEffect(new DestroyAllNamedPermanentsEffect()); } + public MaelstromPulse(final MaelstromPulse card) { + super(card); + } + + @Override + public MaelstromPulse copy() { + return new MaelstromPulse(this); + } + + @Override + public String getArt() { + return "121021_typ_reg_sty_010.jpg"; + } + } diff --git a/Mage.Sets/src/mage/sets/alarareborn/PutridLeech.java b/Mage.Sets/src/mage/sets/alarareborn/PutridLeech.java index a8a6713aee4..3eb7b44fe46 100644 --- a/Mage.Sets/src/mage/sets/alarareborn/PutridLeech.java +++ b/Mage.Sets/src/mage/sets/alarareborn/PutridLeech.java @@ -43,7 +43,7 @@ import mage.sets.AlaraReborn; * * @author BetaSteward_at_googlemail.com */ -public class PutridLeech extends CardImpl { +public class PutridLeech extends CardImpl { public PutridLeech(UUID ownerId) { super(ownerId, "Putrid Leech", new CardType[]{CardType.CREATURE}, "{B}{G}"); @@ -52,9 +52,22 @@ public class PutridLeech extends CardImpl { this.color.setGreen(true); this.subtype.add("Zombie"); this.subtype.add("Leech"); - this.art = "120997_typ_reg_sty_010.jpg"; this.power = new MageInt(2); this.toughness = new MageInt(2); this.addAbility(new ActivateOncePerTurnActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(2, 2, Duration.EndOfTurn), new PayLifeCost(2))); } + + public PutridLeech(final PutridLeech card) { + super(card); + } + + @Override + public PutridLeech copy() { + return new PutridLeech(this); + } + + @Override + public String getArt() { + return "120997_typ_reg_sty_010.jpg"; + } } diff --git a/Mage.Sets/src/mage/sets/alarareborn/SpellbreakerBehemoth.java b/Mage.Sets/src/mage/sets/alarareborn/SpellbreakerBehemoth.java index 655f6d7383c..6d60a1046dc 100644 --- a/Mage.Sets/src/mage/sets/alarareborn/SpellbreakerBehemoth.java +++ b/Mage.Sets/src/mage/sets/alarareborn/SpellbreakerBehemoth.java @@ -43,7 +43,7 @@ import mage.sets.AlaraReborn; * * @author BetaSteward_at_googlemail.com */ -public class SpellbreakerBehemoth extends CardImpl { +public class SpellbreakerBehemoth extends CardImpl { public SpellbreakerBehemoth(UUID ownerId) { super(ownerId, "Spellbreaker Behemoth", new CardType[]{CardType.CREATURE}, "{1}{R}{G}{G}"); @@ -51,7 +51,6 @@ public class SpellbreakerBehemoth extends CardImpl { this.color.setRed(true); this.color.setGreen(true); this.subtype.add("Beast"); - this.art = "120966_typ_reg_sty_010.jpg"; this.power = new MageInt(5); this.toughness = new MageInt(5); this.addAbility(new SimpleStaticAbility(Zone.STACK, new CantCounterSourceEffect())); @@ -61,5 +60,19 @@ public class SpellbreakerBehemoth extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.STACK, new CantCounterControlledEffect(filter))); } + + public SpellbreakerBehemoth(final SpellbreakerBehemoth card) { + super(card); + } + + @Override + public SpellbreakerBehemoth copy() { + return new SpellbreakerBehemoth(this); + } + + @Override + public String getArt() { + return "120966_typ_reg_sty_010.jpg"; + } } diff --git a/Mage.Sets/src/mage/sets/alarareborn/Terminate.java b/Mage.Sets/src/mage/sets/alarareborn/Terminate.java index 6f1da123533..f3442f61c97 100644 --- a/Mage.Sets/src/mage/sets/alarareborn/Terminate.java +++ b/Mage.Sets/src/mage/sets/alarareborn/Terminate.java @@ -39,16 +39,29 @@ import mage.target.common.TargetCreaturePermanent; * * @author BetaSteward_at_googlemail.com */ -public class Terminate extends CardImpl { +public class Terminate extends CardImpl { public Terminate(UUID ownerId) { super(ownerId, "Terminate", new CardType[]{CardType.INSTANT}, "{B}{R}"); this.expansionSetId = AlaraReborn.getInstance().getId(); this.color.setBlack(true); this.color.setRed(true); - this.art = "115230_typ_reg_sty_010.jpg"; this.getSpellAbility().addTarget(new TargetCreaturePermanent()); this.getSpellAbility().addEffect(new DestroyNoRegenTargetEffect()); } + public Terminate(final Terminate card) { + super(card); + } + + @Override + public Terminate copy() { + return new Terminate(this); + } + + @Override + public String getArt() { + return "115230_typ_reg_sty_010.jpg"; + } + } diff --git a/Mage.Sets/src/mage/sets/alarareborn/VengefulRebirth.java b/Mage.Sets/src/mage/sets/alarareborn/VengefulRebirth.java index ebd0f16531f..991950d2238 100644 --- a/Mage.Sets/src/mage/sets/alarareborn/VengefulRebirth.java +++ b/Mage.Sets/src/mage/sets/alarareborn/VengefulRebirth.java @@ -31,6 +31,7 @@ package mage.sets.alarareborn; import java.util.UUID; import mage.Constants.CardType; import mage.Constants.Outcome; +import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ExileSpellEffect; import mage.cards.Card; @@ -46,43 +47,65 @@ import mage.target.common.TargetCreatureOrPlayer; * * @author BetaSteward_at_googlemail.com */ -public class VengefulRebirth extends CardImpl { +public class VengefulRebirth extends CardImpl { public VengefulRebirth(UUID ownerId) { super(ownerId, "Vengeful Rebirth", new CardType[]{CardType.SORCERY}, "{4}{R}{G}"); this.expansionSetId = AlaraReborn.getInstance().getId(); this.color.setRed(true); this.color.setGreen(true); - this.art = "115104_typ_reg_sty_010.jpg"; this.getSpellAbility().addTarget(new TargetCardInGraveyard()); this.getSpellAbility().addTarget(new TargetCreatureOrPlayer()); this.getSpellAbility().addEffect(new VengefulRebirthEffect()); this.getSpellAbility().addEffect(ExileSpellEffect.getInstance()); } + public VengefulRebirth(final VengefulRebirth card) { + super(card); + } + + @Override + public VengefulRebirth copy() { + return new VengefulRebirth(this); + } + + @Override + public String getArt() { + return "115104_typ_reg_sty_010.jpg"; + } + } -class VengefulRebirthEffect extends OneShotEffect { +class VengefulRebirthEffect extends OneShotEffect { public VengefulRebirthEffect() { super(Outcome.DrawCard); } + public VengefulRebirthEffect(final VengefulRebirthEffect effect) { + super(effect); + } + @Override - public boolean apply(Game game) { - Player player = game.getPlayer(this.source.getControllerId()); - Card card = (Card)game.getObject(this.source.getFirstTarget()); + public VengefulRebirthEffect copy() { + return new VengefulRebirthEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Card card = (Card)game.getObject(source.getFirstTarget()); if (player.removeFromGraveyard(card, game) && player.putInHand(card, game)) { int damage = card.getManaCost().convertedManaCost(); if (!card.getCardType().contains(CardType.LAND)) { - Permanent permanent = game.getPermanent(this.source.getTargets().get(1).getTargets().get(0)); + Permanent permanent = game.getPermanent(source.getTargets().get(1).getTargets().get(0)); if (permanent != null) { - permanent.damage(damage, this.source.getSourceId(), game); + permanent.damage(damage, source.getSourceId(), game); return true; } - Player targetPlayer = game.getPlayer(this.source.getTargets().get(1).getTargets().get(0)); + Player targetPlayer = game.getPlayer(source.getTargets().get(1).getTargets().get(0)); if (targetPlayer != null) { - targetPlayer.damage(damage, this.source.getSourceId(), game); + targetPlayer.damage(damage, source.getSourceId(), game); return true; } return false; @@ -92,7 +115,7 @@ class VengefulRebirthEffect extends OneShotEffect { } @Override - public String getText() { + public String getText(Ability source) { return "Return target card from your graveyard to your hand. If you return a nonland card to your hand this way, Vengeful Rebirth deals damage equal to that card's converted mana cost to target creature or player"; } diff --git a/Mage.Sets/src/mage/sets/conflux/FontOfMythos.java b/Mage.Sets/src/mage/sets/conflux/FontOfMythos.java index 0203dfe57f4..cbab6d63a46 100644 --- a/Mage.Sets/src/mage/sets/conflux/FontOfMythos.java +++ b/Mage.Sets/src/mage/sets/conflux/FontOfMythos.java @@ -44,28 +44,50 @@ import mage.target.TargetPlayer; * * @author BetaSteward_at_googlemail.com */ -public class FontOfMythos extends CardImpl { +public class FontOfMythos extends CardImpl { public FontOfMythos(UUID ownerId) { super(ownerId, "Font of Mythos", new CardType[]{CardType.ARTIFACT}, "{4}"); this.expansionSetId = Conflux.getInstance().getId(); - this.art = "119800_typ_reg_sty_010.jpg"; this.addAbility(new FontOfMythosAbility()); } + public FontOfMythos(final FontOfMythos card) { + super(card); + } + + @Override + public FontOfMythos copy() { + return new FontOfMythos(this); + } + + @Override + public String getArt() { + return "119800_typ_reg_sty_010.jpg"; + } + } -class FontOfMythosAbility extends TriggeredAbilityImpl { +class FontOfMythosAbility extends TriggeredAbilityImpl { public FontOfMythosAbility() { super(Zone.BATTLEFIELD, new DrawCardTargetEffect(2)); } + public FontOfMythosAbility(final FontOfMythosAbility ability) { + super(ability); + } + + @Override + public FontOfMythosAbility copy() { + return new FontOfMythosAbility(this); + } + @Override public boolean checkTrigger(GameEvent event, Game game) { if (event.getType() == EventType.DRAW_STEP_PRE) { this.addTarget(new TargetPlayer()); - this.targets.get(0).getTargets().add(event.getPlayerId()); + this.targets.get(0).addTarget(event.getPlayerId(), this, game); trigger(game, event.getPlayerId()); return true; } diff --git a/Mage.Sets/src/mage/sets/conflux/HellsparkElemental.java b/Mage.Sets/src/mage/sets/conflux/HellsparkElemental.java index 8273c920e54..f8ad0229a19 100644 --- a/Mage.Sets/src/mage/sets/conflux/HellsparkElemental.java +++ b/Mage.Sets/src/mage/sets/conflux/HellsparkElemental.java @@ -32,7 +32,7 @@ import java.util.UUID; import mage.Constants.CardType; import mage.MageInt; import mage.abilities.common.OnEventTriggeredAbility; -import mage.abilities.costs.mana.ManaCosts; +import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.SacrificeSourceEffect; import mage.abilities.keyword.HasteAbility; import mage.abilities.keyword.UnearthAbility; @@ -44,21 +44,34 @@ import mage.sets.Conflux; * * @author BetaSteward_at_googlemail.com */ -public class HellsparkElemental extends CardImpl { +public class HellsparkElemental extends CardImpl { public HellsparkElemental(UUID ownerId) { super(ownerId, "Hellspark Elemental", new CardType[]{CardType.CREATURE}, "{1}{R}"); this.expansionSetId = Conflux.getInstance().getId(); this.subtype.add("Elemental"); this.color.setRed(true); - this.art = "118669_typ_reg_sty_010.jpg"; this.power = new MageInt(3); this.toughness = new MageInt(1); this.addAbility(HasteAbility.getInstance()); this.addAbility(new OnEventTriggeredAbility(EventType.END_TURN_STEP_PRE, "beginning of the end step", new SacrificeSourceEffect())); - this.addAbility(new UnearthAbility(new ManaCosts("{1}{R}"))); + this.addAbility(new UnearthAbility(new ManaCostsImpl("{1}{R}"))); } + public HellsparkElemental(final HellsparkElemental card) { + super(card); + } + + @Override + public HellsparkElemental copy() { + return new HellsparkElemental(this); + } + + @Override + public String getArt() { + return "118669_typ_reg_sty_010.jpg"; + } + } diff --git a/Mage.Sets/src/mage/sets/conflux/KnightOfTheReliquary.java b/Mage.Sets/src/mage/sets/conflux/KnightOfTheReliquary.java index 37f1d475b7b..09fbd19404d 100644 --- a/Mage.Sets/src/mage/sets/conflux/KnightOfTheReliquary.java +++ b/Mage.Sets/src/mage/sets/conflux/KnightOfTheReliquary.java @@ -36,6 +36,7 @@ import mage.Constants.Outcome; import mage.Constants.SubLayer; import mage.Constants.Zone; import mage.MageInt; +import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.Costs; @@ -58,7 +59,7 @@ import mage.target.common.TargetControlledPermanent; * * @author BetaSteward_at_googlemail.com */ -public class KnightOfTheReliquary extends CardImpl { +public class KnightOfTheReliquary extends CardImpl { private static FilterLandPermanent filter = new FilterLandPermanent("Forest or Plains"); @@ -75,7 +76,6 @@ public class KnightOfTheReliquary extends CardImpl { this.color.setGreen(true); this.subtype.add("Human"); this.subtype.add("Knight"); - this.art = "119798_typ_reg_sty_010.jpg"; this.power = new MageInt(2); this.toughness = new MageInt(2); TargetCardInLibrary target = new TargetCardInLibrary(new FilterLandCard()); @@ -86,9 +86,23 @@ public class KnightOfTheReliquary extends CardImpl { this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new SearchLibraryPutInPlayEffect(target, false, Outcome.PutLandInPlay), costs)); } + public KnightOfTheReliquary(final KnightOfTheReliquary card) { + super(card); + } + + @Override + public KnightOfTheReliquary copy() { + return new KnightOfTheReliquary(this); + } + + @Override + public String getArt() { + return "119798_typ_reg_sty_010.jpg"; + } + } -class KnightOfTheReliquaryEffect extends ContinuousEffectImpl { +class KnightOfTheReliquaryEffect extends ContinuousEffectImpl { private static FilterLandCard filter = new FilterLandCard(); @@ -96,11 +110,20 @@ class KnightOfTheReliquaryEffect extends ContinuousEffectImpl { super(Duration.WhileOnBattlefield, Layer.PTChangingEffects_7, SubLayer.ModifyPT_7c, Outcome.BoostCreature); } + public KnightOfTheReliquaryEffect(final KnightOfTheReliquaryEffect effect) { + super(effect); + } + @Override - public boolean apply(Game game) { - int count = game.getPlayer(this.source.getControllerId()).getGraveyard().count(filter); + public KnightOfTheReliquaryEffect copy() { + return new KnightOfTheReliquaryEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + int count = game.getPlayer(source.getControllerId()).getGraveyard().count(filter, game); if (count > 0) { - Permanent target = (Permanent) game.getPermanent(this.source.getSourceId()); + Permanent target = (Permanent) game.getPermanent(source.getSourceId()); if (target != null) { target.addPower(count); target.addToughness(count); @@ -111,7 +134,7 @@ class KnightOfTheReliquaryEffect extends ContinuousEffectImpl { } @Override - public String getText() { + public String getText(Ability source) { return "{this} gets +1/+1 for each land card in your graveyard"; } diff --git a/Mage.Sets/src/mage/sets/conflux/MartialCoup.java b/Mage.Sets/src/mage/sets/conflux/MartialCoup.java index f28e3b6813c..18ea5dd540e 100644 --- a/Mage.Sets/src/mage/sets/conflux/MartialCoup.java +++ b/Mage.Sets/src/mage/sets/conflux/MartialCoup.java @@ -31,6 +31,7 @@ package mage.sets.conflux; import java.util.UUID; import mage.Constants.CardType; import mage.Constants.Outcome; +import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.filter.common.FilterCreaturePermanent; @@ -44,32 +45,54 @@ import mage.sets.Conflux; * * @author BetaSteward_at_googlemail.com */ -public class MartialCoup extends CardImpl { +public class MartialCoup extends CardImpl { public MartialCoup(UUID ownerId) { super(ownerId, "Martial Coup", new CardType[]{CardType.SORCERY}, "{X}{W}{W}"); this.expansionSetId = Conflux.getInstance().getId(); this.color.setWhite(true); - this.art = "118685_typ_reg_sty_010.jpg"; this.getSpellAbility().addEffect(new MartialCoupEffect()); } + + public MartialCoup(final MartialCoup card) { + super(card); + } + + @Override + public MartialCoup copy() { + return new MartialCoup(this); + } + + @Override + public String getArt() { + return "118685_typ_reg_sty_010.jpg"; + } } -class MartialCoupEffect extends OneShotEffect { +class MartialCoupEffect extends OneShotEffect { public MartialCoupEffect() { super(Outcome.PutCreatureInPlay); } + public MartialCoupEffect(final MartialCoupEffect effect) { + super(effect); + } + @Override - public boolean apply(Game game) { - Player controller = game.getPlayer(this.source.getControllerId()); - int amount = this.source.getManaCosts().getVariableCosts().get(0).getValue(); + public MartialCoupEffect copy() { + return new MartialCoupEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + int amount = source.getManaCosts().getVariableCosts().get(0).getValue(); FilterCreaturePermanent filter = new FilterCreaturePermanent(); if (amount > 4) { - for (Permanent permanent: game.getBattlefield().getActivePermanents(filter, this.source.getControllerId(), game)) { - permanent.destroy(this.source.getSourceId(), game, false); + for (Permanent permanent: game.getBattlefield().getActivePermanents(filter, source.getControllerId(), game)) { + permanent.destroy(source.getSourceId(), game, false); } } for (int i = 0; i < amount; i++) { @@ -79,7 +102,7 @@ class MartialCoupEffect extends OneShotEffect { } @Override - public String getText() { + public String getText(Ability source) { return "Put X 1/1 white Soldier creature tokens onto the battlefield. If X is 5 or more, destroy all other creatures"; } diff --git a/Mage.Sets/src/mage/sets/conflux/NobleHierarch.java b/Mage.Sets/src/mage/sets/conflux/NobleHierarch.java index ec23eb937f1..2c5eb5dd604 100644 --- a/Mage.Sets/src/mage/sets/conflux/NobleHierarch.java +++ b/Mage.Sets/src/mage/sets/conflux/NobleHierarch.java @@ -42,7 +42,7 @@ import mage.sets.Conflux; * * @author BetaSteward_at_googlemail.com */ -public class NobleHierarch extends CardImpl { +public class NobleHierarch extends CardImpl { public NobleHierarch(UUID ownerId) { super(ownerId, "Noble Hierarch", new CardType[]{CardType.CREATURE}, "{G}"); @@ -50,7 +50,6 @@ public class NobleHierarch extends CardImpl { this.color.setGreen(true); this.subtype.add("Human"); this.subtype.add("Druid"); - this.art = "118694_typ_reg_sty_010.jpg"; this.power = new MageInt(0); this.toughness = new MageInt(1); this.addAbility(new ExaltedAbility()); @@ -58,4 +57,18 @@ public class NobleHierarch extends CardImpl { this.addAbility(new WhiteManaAbility()); this.addAbility(new BlueManaAbility()); } + + public NobleHierarch(final NobleHierarch card) { + super(card); + } + + @Override + public NobleHierarch copy() { + return new NobleHierarch(this); + } + + @Override + public String getArt() { + return "118694_typ_reg_sty_010.jpg"; + } } diff --git a/Mage.Sets/src/mage/sets/conflux/PathToExile.java b/Mage.Sets/src/mage/sets/conflux/PathToExile.java index 99c5959348b..a92b7937303 100644 --- a/Mage.Sets/src/mage/sets/conflux/PathToExile.java +++ b/Mage.Sets/src/mage/sets/conflux/PathToExile.java @@ -32,6 +32,7 @@ import java.util.UUID; import mage.Constants.CardType; import mage.Constants.Outcome; import mage.Constants.Zone; +import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.Card; import mage.cards.CardImpl; @@ -47,17 +48,29 @@ import mage.target.common.TargetCreaturePermanent; * * @author BetaSteward_at_googlemail.com */ -public class PathToExile extends CardImpl { +public class PathToExile extends CardImpl { public PathToExile(UUID ownerId) { super(ownerId, "Path To Exile", new CardType[]{CardType.INSTANT}, "{W}"); this.expansionSetId = Conflux.getInstance().getId(); this.color.setWhite(true); - this.art = "118686_typ_reg_sty_010.jpg"; this.getSpellAbility().addTarget(new TargetCreaturePermanent()); this.getSpellAbility().addEffect(new PathToExileEffect()); } + public PathToExile(final PathToExile card) { + super(card); + } + + @Override + public PathToExile copy() { + return new PathToExile(this); + } + + @Override + public String getArt() { + return "118686_typ_reg_sty_010.jpg"; + } } class PathToExileEffect extends OneShotEffect { @@ -66,16 +79,25 @@ class PathToExileEffect extends OneShotEffect { super(Outcome.Exile); } + public PathToExileEffect(final PathToExileEffect effect) { + super(effect); + } + @Override - public boolean apply(Game game) { - Permanent permanent = game.getPermanent(this.source.getFirstTarget()); + public PathToExileEffect copy() { + return new PathToExileEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); if (permanent != null) { Player player = game.getPlayer(permanent.getControllerId()); if (permanent.moveToZone(Zone.EXILED, game, false)) { if (player.chooseUse(Outcome.PutCardInPlay, "Use Path to Exile effect?", game)) { TargetCardInLibrary target = new TargetCardInLibrary(new FilterBasicLandCard()); player.searchLibrary(target, game); - Card card = player.getLibrary().remove(target.getFirstTarget()); + Card card = player.getLibrary().remove(target.getFirstTarget(), game); if (card != null) { if (player.putOntoBattlefield(card, game)) { Permanent land = game.getPermanent(card.getId()); @@ -92,7 +114,7 @@ class PathToExileEffect extends OneShotEffect { } @Override - public String getText() { + public String getText(Ability source) { return "Exile target creature. Its controller may search his or her library for a basic land card, put that card onto the battlefield tapped, then shuffle his or her library"; } diff --git a/Mage.Sets/src/mage/sets/conflux/QuenchableFire.java b/Mage.Sets/src/mage/sets/conflux/QuenchableFire.java index 2511f00384d..34a9b531c49 100644 --- a/Mage.Sets/src/mage/sets/conflux/QuenchableFire.java +++ b/Mage.Sets/src/mage/sets/conflux/QuenchableFire.java @@ -31,9 +31,10 @@ package mage.sets.conflux; import java.util.UUID; import mage.Constants.CardType; import mage.Constants.Outcome; +import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.SpecialAction; -import mage.abilities.costs.mana.ManaCosts; +import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.RemoveDelayedTriggeredAbilityEffect; @@ -49,47 +50,68 @@ import mage.target.TargetPlayer; * * @author BetaSteward_at_googlemail.com */ -public class QuenchableFire extends CardImpl { +public class QuenchableFire extends CardImpl { public QuenchableFire(UUID ownerId) { super(ownerId, "Quenchable Fire", new CardType[]{CardType.SORCERY}, "{3}{R}"); this.expansionSetId = Conflux.getInstance().getId(); this.color.setRed(true); - this.art = "118698_typ_reg_sty_010.jpg"; this.getSpellAbility().addTarget(new TargetPlayer()); this.getSpellAbility().addEffect(new DamageTargetEffect(3)); this.getSpellAbility().addEffect(new QuenchableFireEffect()); } + public QuenchableFire(final QuenchableFire card) { + super(card); + } + + @Override + public QuenchableFire copy() { + return new QuenchableFire(this); + } + + @Override + public String getArt() { + return "118698_typ_reg_sty_010.jpg"; + } } -class QuenchableFireEffect extends OneShotEffect { +class QuenchableFireEffect extends OneShotEffect { public QuenchableFireEffect() { super(Outcome.Damage); } + public QuenchableFireEffect(final QuenchableFireEffect effect) { + super(effect); + } + @Override - public boolean apply(Game game) { + public QuenchableFireEffect copy() { + return new QuenchableFireEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { //create delayed triggereda ability QuenchableFireDelayedTriggeredAbility delayedAbility = new QuenchableFireDelayedTriggeredAbility(); - delayedAbility.setSourceId(this.source.getSourceId()); - delayedAbility.setControllerId(this.source.getControllerId()); - delayedAbility.getTargets().addAll(this.source.getTargets()); + delayedAbility.setSourceId(source.getSourceId()); + delayedAbility.setControllerId(source.getControllerId()); + delayedAbility.getTargets().addAll(source.getTargets()); game.getState().addDelayedTriggeredAbility(delayedAbility); //create special action QuenchableFireSpecialAction newAction = new QuenchableFireSpecialAction(delayedAbility.getId()); delayedAbility.setSpecialActionId(newAction.getId()); - newAction.setSourceId(this.source.getSourceId()); - newAction.setControllerId(this.source.getFirstTarget()); - newAction.getTargets().addAll(this.source.getTargets()); + newAction.setSourceId(source.getSourceId()); + newAction.setControllerId(source.getFirstTarget()); + newAction.getTargets().addAll(source.getTargets()); game.getState().getSpecialActions().add(newAction); return true; } } -class QuenchableFireDelayedTriggeredAbility extends DelayedTriggeredAbility { +class QuenchableFireDelayedTriggeredAbility extends DelayedTriggeredAbility { private UUID specialActionId; @@ -101,6 +123,15 @@ class QuenchableFireDelayedTriggeredAbility extends DelayedTriggeredAbility { this.specialActionId = specialActionId; } + public QuenchableFireDelayedTriggeredAbility(final QuenchableFireDelayedTriggeredAbility ability) { + super(ability); + } + + @Override + public QuenchableFireDelayedTriggeredAbility copy() { + return new QuenchableFireDelayedTriggeredAbility(this); + } + @Override public boolean checkTrigger(GameEvent event, Game game) { if (event.getType() == EventType.UPKEEP_STEP_PRE && event.getPlayerId().equals(this.controllerId)) { @@ -123,11 +154,21 @@ class QuenchableFireDelayedTriggeredAbility extends DelayedTriggeredAbility { } -class QuenchableFireSpecialAction extends SpecialAction { +class QuenchableFireSpecialAction extends SpecialAction { public QuenchableFireSpecialAction(UUID effectId) { - this.addCost(new ManaCosts("{U}")); + this.addCost(new ManaCostsImpl("{U}")); this.addEffect(new RemoveDelayedTriggeredAbilityEffect(effectId)); this.addEffect(new RemoveSpecialActionEffect(this.getId())); } + + public QuenchableFireSpecialAction(final QuenchableFireSpecialAction ability) { + super(ability); + } + + @Override + public QuenchableFireSpecialAction copy() { + return new QuenchableFireSpecialAction(this); + } + } \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/conflux/Thornling.java b/Mage.Sets/src/mage/sets/conflux/Thornling.java index 9208acc3895..640e6626d86 100644 --- a/Mage.Sets/src/mage/sets/conflux/Thornling.java +++ b/Mage.Sets/src/mage/sets/conflux/Thornling.java @@ -34,11 +34,11 @@ import mage.Constants.Duration; import mage.Constants.Zone; import mage.MageInt; import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.costs.mana.ManaCosts; +import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.BoostSourceEffect; import mage.abilities.effects.common.GainAbilitySourceEffect; import mage.abilities.keyword.HasteAbility; -import mage.abilities.keyword.IndestructableAbility; +import mage.abilities.keyword.IndestructibleAbility; import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; import mage.sets.Conflux; @@ -47,7 +47,7 @@ import mage.sets.Conflux; * * @author BetaSteward_at_googlemail.com */ -public class Thornling extends CardImpl { +public class Thornling extends CardImpl { public Thornling(UUID ownerId) { super(ownerId, "Thornling", new CardType[]{CardType.CREATURE}, "{3}{G}{G}"); @@ -55,14 +55,27 @@ public class Thornling extends CardImpl { this.color.setGreen(true); this.subtype.add("Elemental"); this.subtype.add("Shapeshifter"); - this.art = "118734_typ_reg_sty_010.jpg"; this.power = new MageInt(4); this.toughness = new MageInt(4); - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new GainAbilitySourceEffect(HasteAbility.getInstance(), Duration.EndOfTurn), new ManaCosts("{G}"))); - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new GainAbilitySourceEffect(TrampleAbility.getInstance(), Duration.EndOfTurn), new ManaCosts("{G}"))); - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new GainAbilitySourceEffect(IndestructableAbility.getInstance(), Duration.EndOfTurn), new ManaCosts("{G}"))); - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(1, -1, Duration.EndOfTurn), new ManaCosts("{1}"))); - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(-1, 1, Duration.EndOfTurn), new ManaCosts("{1}"))); + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new GainAbilitySourceEffect(HasteAbility.getInstance(), Duration.EndOfTurn), new ManaCostsImpl("{G}"))); + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new GainAbilitySourceEffect(TrampleAbility.getInstance(), Duration.EndOfTurn), new ManaCostsImpl("{G}"))); + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new GainAbilitySourceEffect(IndestructibleAbility.getInstance(), Duration.EndOfTurn), new ManaCostsImpl("{G}"))); + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(1, -1, Duration.EndOfTurn), new ManaCostsImpl("{1}"))); + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(-1, 1, Duration.EndOfTurn), new ManaCostsImpl("{1}"))); + } + + public Thornling(final Thornling card) { + super(card); + } + + @Override + public Thornling copy() { + return new Thornling(this); + } + + @Override + public String getArt() { + return "118734_typ_reg_sty_010.jpg"; } } diff --git a/Mage.Sets/src/mage/sets/magic2010/AcidicSlime.java b/Mage.Sets/src/mage/sets/magic2010/AcidicSlime.java index 104403c097f..ce09cbe9bd2 100644 --- a/Mage.Sets/src/mage/sets/magic2010/AcidicSlime.java +++ b/Mage.Sets/src/mage/sets/magic2010/AcidicSlime.java @@ -47,7 +47,7 @@ import mage.target.TargetPermanent; * * @author BetaSteward_at_googlemail.com */ -public class AcidicSlime extends CardImpl { +public class AcidicSlime extends CardImpl { private static FilterPermanent filter = new FilterPermanent("artifact, enchantment, or land"); @@ -63,7 +63,6 @@ public class AcidicSlime extends CardImpl { this.expansionSetId = Magic2010.getInstance().getId(); this.subtype.add("Ooze"); this.color.setGreen(true); - this.art = "121561_typ_reg_sty_010.jpg"; this.power = new MageInt(2); this.toughness = new MageInt(2); @@ -75,4 +74,18 @@ public class AcidicSlime extends CardImpl { this.addAbility(ability); } + public AcidicSlime(final AcidicSlime card) { + super(card); + } + + @Override + public AcidicSlime copy() { + return new AcidicSlime(this); + } + + @Override + public String getArt() { + return "121561_typ_reg_sty_010.jpg"; + } + } diff --git a/Mage.Sets/src/mage/sets/magic2010/AcolyteOfXathrid.java b/Mage.Sets/src/mage/sets/magic2010/AcolyteOfXathrid.java index baaded23c13..35a1dc365ee 100644 --- a/Mage.Sets/src/mage/sets/magic2010/AcolyteOfXathrid.java +++ b/Mage.Sets/src/mage/sets/magic2010/AcolyteOfXathrid.java @@ -35,7 +35,7 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.costs.mana.ManaCosts; +import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.LoseLifeTargetEffect; import mage.cards.CardImpl; import mage.sets.Magic2010; @@ -53,13 +53,26 @@ public class AcolyteOfXathrid extends CardImpl { this.subtype.add("Human"); this.subtype.add("Cleric"); this.color.setBlack(true); - this.art = "121644_typ_reg_sty_010.jpg"; this.power = new MageInt(0); this.toughness = new MageInt(1); - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new LoseLifeTargetEffect(1), new ManaCosts("{1}{B}")); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new LoseLifeTargetEffect(1), new ManaCostsImpl("{1}{B}")); ability.addCost(new TapSourceCost()); ability.addTarget(new TargetPlayer()); this.addAbility(ability); } + public AcolyteOfXathrid(final AcolyteOfXathrid card) { + super(card); + } + + @Override + public AcolyteOfXathrid copy() { + return new AcolyteOfXathrid(this); + } + + @Override + public String getArt() { + return "121644_typ_reg_sty_010.jpg"; + } + } diff --git a/Mage.Sets/src/mage/sets/magic2010/ActOfTreason.java b/Mage.Sets/src/mage/sets/magic2010/ActOfTreason.java index 5a2b81361ec..6d96699a364 100644 --- a/Mage.Sets/src/mage/sets/magic2010/ActOfTreason.java +++ b/Mage.Sets/src/mage/sets/magic2010/ActOfTreason.java @@ -43,13 +43,12 @@ import mage.target.common.TargetCreaturePermanent; * * @author BetaSteward_at_googlemail.com */ -public class ActOfTreason extends CardImpl { +public class ActOfTreason extends CardImpl { public ActOfTreason(UUID ownerId) { super(ownerId, "Act Of Treason", new CardType[]{CardType.SORCERY}, "{2}{R}"); this.expansionSetId = Magic2010.getInstance().getId(); this.color.setRed(true); - this.art = "121664_typ_reg_sty_010.jpg"; this.getSpellAbility().addTarget(new TargetCreaturePermanent()); this.getSpellAbility().addEffect(new GainControlTargetEOTEffect()); this.getSpellAbility().addEffect(new UntapTargetEffect()); @@ -57,4 +56,18 @@ public class ActOfTreason extends CardImpl { } + public ActOfTreason(final ActOfTreason card) { + super(card); + } + + @Override + public ActOfTreason copy() { + return new ActOfTreason(this); + } + + @Override + public String getArt() { + return "121664_typ_reg_sty_010.jpg"; + } + } diff --git a/Mage.Sets/src/mage/sets/magic2010/AirElemental.java b/Mage.Sets/src/mage/sets/magic2010/AirElemental.java index 4f73932cf0f..90637be7bd5 100644 --- a/Mage.Sets/src/mage/sets/magic2010/AirElemental.java +++ b/Mage.Sets/src/mage/sets/magic2010/AirElemental.java @@ -39,17 +39,30 @@ import mage.sets.Magic2010; * * @author BetaSteward_at_googlemail.com */ -public class AirElemental extends CardImpl { +public class AirElemental extends CardImpl { public AirElemental(UUID ownerId) { super(ownerId, "Air Elemental", new CardType[]{CardType.CREATURE}, "{3}{U}{U}"); this.expansionSetId = Magic2010.getInstance().getId(); this.color.setBlue(true); this.subtype.add("Elemental"); - this.art = "101044_typ_reg_sty_010.jpg"; this.power = new MageInt(4); this.toughness = new MageInt(4); this.addAbility(FlyingAbility.getInstance()); } + public AirElemental(final AirElemental card) { + super(card); + } + + @Override + public AirElemental copy() { + return new AirElemental(this); + } + + @Override + public String getArt() { + return "101044_typ_reg_sty_010.jpg"; + } + } diff --git a/Mage.Sets/src/mage/sets/magic2010/AjaniGoldmane.java b/Mage.Sets/src/mage/sets/magic2010/AjaniGoldmane.java index dffdb4b63cc..5e3f322b292 100644 --- a/Mage.Sets/src/mage/sets/magic2010/AjaniGoldmane.java +++ b/Mage.Sets/src/mage/sets/magic2010/AjaniGoldmane.java @@ -36,6 +36,7 @@ import mage.Constants.Outcome; import mage.Constants.SubLayer; import mage.Constants.Zone; import mage.MageInt; +import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ContinuousEffectImpl; @@ -57,19 +58,18 @@ import mage.sets.Magic2010; * * @author BetaSteward_at_googlemail.com */ -public class AjaniGoldmane extends CardImpl { +public class AjaniGoldmane extends CardImpl { public AjaniGoldmane(UUID ownerId) { super(ownerId, "Ajani Goldmane", new CardType[]{CardType.PLANESWALKER}, "{2}{W}{W}"); this.expansionSetId = Magic2010.getInstance().getId(); this.subtype.add("Ajani"); this.color.setWhite(true); - this.art = "105545_typ_reg_sty_010.jpg"; this.loyalty = new MageInt(4); this.addAbility(new LoyaltyAbility(new GainLifeEffect(2), 1)); - Effects effects1 = new Effects(null); + Effects effects1 = new Effects(); effects1.add(new AddPlusOneCountersControlledEffect(1)); effects1.add(new GainAbilityControlledEffect(VigilanceAbility.getInstance(), Duration.EndOfTurn, new FilterCreaturePermanent())); this.addAbility(new LoyaltyAbility(effects1, -1)); @@ -78,6 +78,20 @@ public class AjaniGoldmane extends CardImpl { } + public AjaniGoldmane(final AjaniGoldmane card) { + super(card); + } + + @Override + public AjaniGoldmane copy() { + return new AjaniGoldmane(this); + } + + @Override + public String getArt() { + return "105545_typ_reg_sty_010.jpg"; + } + } class AvatarToken extends Token { @@ -92,17 +106,26 @@ class AvatarToken extends Token { } -class AvatarTokenEffect extends ContinuousEffectImpl { +class AvatarTokenEffect extends ContinuousEffectImpl { public AvatarTokenEffect() { super(Duration.WhileOnBattlefield, Layer.PTChangingEffects_7, SubLayer.SetPT_7b, Outcome.BoostCreature); } + public AvatarTokenEffect(final AvatarTokenEffect effect) { + super(effect); + } + @Override - public boolean apply(Game game) { - Permanent token = game.getPermanent(this.source.getSourceId()); + public AvatarTokenEffect copy() { + return new AvatarTokenEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent token = game.getPermanent(source.getSourceId()); if (token != null) { - Player controller = game.getPlayer(this.source.getControllerId()); + Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { token.getPower().setValue(controller.getLife()); token.getToughness().setValue(controller.getLife()); diff --git a/Mage.Sets/src/mage/sets/magic2010/AlluringSiren.java b/Mage.Sets/src/mage/sets/magic2010/AlluringSiren.java index f520e05d51e..33cddbc2170 100644 --- a/Mage.Sets/src/mage/sets/magic2010/AlluringSiren.java +++ b/Mage.Sets/src/mage/sets/magic2010/AlluringSiren.java @@ -50,14 +50,13 @@ import mage.target.common.TargetCreaturePermanent; * * @author BetaSteward_at_googlemail.com */ -public class AlluringSiren extends CardImpl { +public class AlluringSiren extends CardImpl { public AlluringSiren(UUID ownerId) { super(ownerId, "Alluring Siren", new CardType[]{CardType.CREATURE}, "{1}{U}"); this.expansionSetId = Magic2010.getInstance().getId(); this.color.setBlue(true); this.subtype.add("Siren"); - this.art = "121568_typ_reg_sty_010.jpg"; this.power = new MageInt(1); this.toughness = new MageInt(1); Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new AlluringSirenEffect(), new TapSourceCost()); @@ -67,36 +66,59 @@ public class AlluringSiren extends CardImpl { this.addAbility(ability); } + public AlluringSiren(final AlluringSiren card) { + super(card); + } + + @Override + public AlluringSiren copy() { + return new AlluringSiren(this); + } + + @Override + public String getArt() { + return "121568_typ_reg_sty_010.jpg"; + } + } -class AlluringSirenEffect extends RequirementAttackEffect { +class AlluringSirenEffect extends RequirementAttackEffect { public AlluringSirenEffect() { super(Duration.OneUse); } + public AlluringSirenEffect(final AlluringSirenEffect effect) { + super(effect); + } + @Override - public boolean applies(GameEvent event, Game game) { - if (event.getType().equals(EventType.DECLARE_ATTACKERS_STEP_PRE) && event.getPlayerId().equals(this.source.getFirstTarget())) + public AlluringSirenEffect copy() { + return new AlluringSirenEffect(this); + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + if (event.getType().equals(EventType.DECLARE_ATTACKERS_STEP_PRE) && event.getPlayerId().equals(source.getFirstTarget())) return true; - if (event.getType().equals(EventType.END_PHASE_POST) && event.getPlayerId().equals(this.source.getFirstTarget())) + if (event.getType().equals(EventType.END_PHASE_POST) && event.getPlayerId().equals(source.getFirstTarget())) used = true; return false; } @Override - public boolean apply(Game game) { - Permanent creature = game.getPermanent(this.source.getFirstTarget()); + public boolean apply(Game game, Ability source) { + Permanent creature = game.getPermanent(source.getFirstTarget()); if (creature != null) { if (creature.canAttack(game)) { - game.getCombat().declareAttacker(creature.getId(), this.source.getControllerId(), game); + game.getCombat().declareAttacker(creature.getId(), source.getControllerId(), game); } } return false; } @Override - public String getText() { + public String getText(Ability source) { return "Target creature an opponent controls attacks you this turn if able."; } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/magic2010/AngelsFeather.java b/Mage.Sets/src/mage/sets/magic2010/AngelsFeather.java index 17682b4f8e2..665a8165a9b 100644 --- a/Mage.Sets/src/mage/sets/magic2010/AngelsFeather.java +++ b/Mage.Sets/src/mage/sets/magic2010/AngelsFeather.java @@ -44,23 +44,45 @@ import mage.sets.Magic2010; * * @author BetaSteward_at_googlemail.com */ -public class AngelsFeather extends CardImpl { +public class AngelsFeather extends CardImpl { public AngelsFeather(UUID ownerId) { super(ownerId, "Angel's Feather", new CardType[]{CardType.ARTIFACT}, "{2}"); this.expansionSetId = Magic2010.getInstance().getId(); - this.art = "75223_typ_reg_sty_010.jpg"; this.addAbility(new AngelsFeatherAbility()); } + public AngelsFeather(final AngelsFeather card) { + super(card); + } + + @Override + public AngelsFeather copy() { + return new AngelsFeather(this); + } + + @Override + public String getArt() { + return "75223_typ_reg_sty_010.jpg"; + } + } -class AngelsFeatherAbility extends TriggeredAbilityImpl { +class AngelsFeatherAbility extends TriggeredAbilityImpl { public AngelsFeatherAbility() { super(Zone.BATTLEFIELD, new GainLifeEffect(1), true); } + public AngelsFeatherAbility(final AngelsFeatherAbility ability) { + super(ability); + } + + @Override + public AngelsFeatherAbility copy() { + return new AngelsFeatherAbility(this); + } + @Override public boolean checkTrigger(GameEvent event, Game game) { if (event.getType() == EventType.SPELL_CAST) { diff --git a/Mage.Sets/src/mage/sets/magic2010/AngelsMercy.java b/Mage.Sets/src/mage/sets/magic2010/AngelsMercy.java index 857953c3d4c..e740e0c008e 100644 --- a/Mage.Sets/src/mage/sets/magic2010/AngelsMercy.java +++ b/Mage.Sets/src/mage/sets/magic2010/AngelsMercy.java @@ -38,15 +38,26 @@ import mage.sets.Magic2010; * * @author BetaSteward_at_googlemail.com */ -public class AngelsMercy extends CardImpl { +public class AngelsMercy extends CardImpl { public AngelsMercy(UUID ownerId) { super(ownerId, "Angel's Mercy", new CardType[]{CardType.INSTANT}, "{2}{W}{W}"); this.expansionSetId = Magic2010.getInstance().getId(); this.color.setWhite(true); - this.art = "121628_typ_reg_sty_010.jpg"; this.getSpellAbility().addEffect(new GainLifeEffect(7)); } + public AngelsMercy(final AngelsMercy card) { + super(card); + } + @Override + public AngelsMercy copy() { + return new AngelsMercy(this); + } + + @Override + public String getArt() { + return "121628_typ_reg_sty_010.jpg"; + } } diff --git a/Mage.Sets/src/mage/sets/magic2010/AntQueen.java b/Mage.Sets/src/mage/sets/magic2010/AntQueen.java index e13cfbda3f8..2f5f8bb67cf 100644 --- a/Mage.Sets/src/mage/sets/magic2010/AntQueen.java +++ b/Mage.Sets/src/mage/sets/magic2010/AntQueen.java @@ -33,8 +33,7 @@ import mage.Constants.CardType; import mage.Constants.Zone; import mage.MageInt; import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.costs.mana.ManaCost; -import mage.abilities.costs.mana.ManaCosts; +import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.CreateTokenEffect; import mage.cards.CardImpl; import mage.game.permanent.token.InsectToken; @@ -44,7 +43,7 @@ import mage.sets.Magic2010; * * @author BetaSteward_at_googlemail.com */ -public class AntQueen extends CardImpl { +public class AntQueen extends CardImpl { private static InsectToken insectToken = new InsectToken(); @@ -53,11 +52,25 @@ public class AntQueen extends CardImpl { this.expansionSetId = Magic2010.getInstance().getId(); this.subtype.add("Insect"); this.color.setGreen(true); - this.art = "122179_typ_reg_sty_010.jpg"; +// this.art = "122179_typ_reg_sty_010.jpg"; this.power = new MageInt(5); this.toughness = new MageInt(5); - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(insectToken), new ManaCosts("{1}{G}"))); + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(insectToken), new ManaCostsImpl("{1}{G}"))); + } + + public AntQueen(final AntQueen card) { + super(card); + } + + @Override + public AntQueen copy() { + return new AntQueen(this); + } + + @Override + public String getArt() { + return "122179_typ_reg_sty_010.jpg"; } } diff --git a/Mage.Sets/src/mage/sets/magic2010/ArmoredAscension.java b/Mage.Sets/src/mage/sets/magic2010/ArmoredAscension.java index 6a38641e9b9..f3bc83c672b 100644 --- a/Mage.Sets/src/mage/sets/magic2010/ArmoredAscension.java +++ b/Mage.Sets/src/mage/sets/magic2010/ArmoredAscension.java @@ -53,13 +53,12 @@ import mage.target.common.TargetCreaturePermanent; * * @author BetaSteward_at_googlemail.com */ -public class ArmoredAscension extends CardImpl { +public class ArmoredAscension extends CardImpl { public ArmoredAscension(UUID ownerId) { super(ownerId, "Armored Ascension", new CardType[]{CardType.ENCHANTMENT}, "{3}{W}"); this.expansionSetId = Magic2010.getInstance().getId(); this.color.setWhite(true); - this.art = "122162_typ_reg_sty_010.jpg"; this.subtype.add("Aura"); TargetPermanent auraTarget = new TargetCreaturePermanent(); @@ -70,9 +69,23 @@ public class ArmoredAscension extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ArmoredAscensionEffect())); } + + public ArmoredAscension(final ArmoredAscension card) { + super(card); + } + + @Override + public ArmoredAscension copy() { + return new ArmoredAscension(this); + } + + @Override + public String getArt() { + return "122162_typ_reg_sty_010.jpg"; + } } -class ArmoredAscensionEffect extends ContinuousEffectImpl { +class ArmoredAscensionEffect extends ContinuousEffectImpl { private static FilterLandPermanent filter = new FilterLandPermanent("Plains"); @@ -84,16 +97,25 @@ class ArmoredAscensionEffect extends ContinuousEffectImpl { super(Duration.WhileOnBattlefield, Outcome.BoostCreature); } + public ArmoredAscensionEffect(final ArmoredAscensionEffect effect) { + super(effect); + } + @Override - public boolean apply(Layer layer, SubLayer sublayer, Game game) { - Permanent enchantment = game.getPermanent(this.source.getSourceId()); + public ArmoredAscensionEffect copy() { + return new ArmoredAscensionEffect(this); + } + + @Override + public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + Permanent enchantment = game.getPermanent(source.getSourceId()); if (enchantment.getAttachedTo() != null) { Permanent creature = game.getPermanent(enchantment.getAttachedTo()); if (creature != null) { switch (layer) { case PTChangingEffects_7: if (sublayer == SubLayer.ModifyPT_7c) { - int amount = game.getBattlefield().countAll(filter, this.source.getControllerId()); + int amount = game.getBattlefield().countAll(filter, source.getControllerId()); creature.addPower(amount); creature.addToughness(amount); } @@ -111,7 +133,7 @@ class ArmoredAscensionEffect extends ContinuousEffectImpl { } @Override - public boolean apply(Game game) { + public boolean apply(Game game, Ability source) { return false; } @@ -121,7 +143,7 @@ class ArmoredAscensionEffect extends ContinuousEffectImpl { } @Override - public String getText() { + public String getText(Ability source) { return "Enchanted creature gets +1/+1 for each Plains you control and has flying."; } diff --git a/Mage.Sets/src/mage/sets/magic2010/Assassinate.java b/Mage.Sets/src/mage/sets/magic2010/Assassinate.java index f78494591a3..b8ea6afe0fd 100644 --- a/Mage.Sets/src/mage/sets/magic2010/Assassinate.java +++ b/Mage.Sets/src/mage/sets/magic2010/Assassinate.java @@ -41,7 +41,7 @@ import mage.target.common.TargetCreaturePermanent; * * @author BetaSteward_at_googlemail.com */ -public class Assassinate extends CardImpl { +public class Assassinate extends CardImpl { private static FilterCreaturePermanent filter = new FilterCreaturePermanent("tapped creature"); @@ -54,10 +54,23 @@ public class Assassinate extends CardImpl { super(ownerId, "Assassinate", new CardType[]{CardType.SORCERY}, "{2}{B}"); this.expansionSetId = Magic2010.getInstance().getId(); this.color.setBlack(true); - this.art = "97461_typ_reg_sty_010.jpg"; this.getSpellAbility().addTarget(new TargetCreaturePermanent(1, 1, filter, TargetController.ANY)); this.getSpellAbility().addEffect(new DestroyTargetEffect()); } + public Assassinate(final Assassinate card) { + super(card); + } + + @Override + public Assassinate copy() { + return new Assassinate(this); + } + + @Override + public String getArt() { + return "97461_typ_reg_sty_010.jpg"; + } + } diff --git a/Mage.Sets/src/mage/sets/magic2010/AwakenerDruid.java b/Mage.Sets/src/mage/sets/magic2010/AwakenerDruid.java index 0a0a39c4716..b8370f0e4f8 100644 --- a/Mage.Sets/src/mage/sets/magic2010/AwakenerDruid.java +++ b/Mage.Sets/src/mage/sets/magic2010/AwakenerDruid.java @@ -50,7 +50,7 @@ import mage.target.common.TargetLandPermanent; * * @author BetaSteward_at_googlemail.com */ -public class AwakenerDruid extends CardImpl { +public class AwakenerDruid extends CardImpl { private static FilterLandPermanent filter = new FilterLandPermanent("Forest"); @@ -64,7 +64,6 @@ public class AwakenerDruid extends CardImpl { this.color.setGreen(true); this.subtype.add("Human"); this.subtype.add("Druid"); - this.art = "121576_typ_reg_sty_010.jpg"; this.power = new MageInt(1); this.toughness = new MageInt(1); Ability ability = new EntersBattlefieldTriggeredAbility(new AwakenerDruidEffect(), false); @@ -72,9 +71,23 @@ public class AwakenerDruid extends CardImpl { this.addAbility(ability); } + + public AwakenerDruid(final AwakenerDruid card) { + super(card); + } + + @Override + public AwakenerDruid copy() { + return new AwakenerDruid(this); + } + + @Override + public String getArt() { + return "121576_typ_reg_sty_010.jpg"; + } } -class AwakenerDruidEffect extends ContinuousEffectImpl { +class AwakenerDruidEffect extends ContinuousEffectImpl { protected static AwakenerDruidToken token = new AwakenerDruidToken(); @@ -82,9 +95,18 @@ class AwakenerDruidEffect extends ContinuousEffectImpl { super(Duration.WhileOnBattlefield, Outcome.BecomeCreature); } + public AwakenerDruidEffect(final AwakenerDruidEffect effect) { + super(effect); + } + @Override - public boolean apply(Layer layer, SubLayer sublayer, Game game) { - Permanent permanent = game.getPermanent(this.source.getFirstTarget()); + public AwakenerDruidEffect copy() { + return new AwakenerDruidEffect(this); + } + + @Override + public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); if (permanent != null) { switch (layer) { case TypeChangingEffects_4: @@ -114,12 +136,12 @@ class AwakenerDruidEffect extends ContinuousEffectImpl { } @Override - public boolean apply(Game game) { + public boolean apply(Game game, Ability source) { return false; } @Override - public String getText() { + public String getText(Ability source) { return "target Forest becomes a 4/5 green Treefolk creature for as long as {this} is on the battlefield. It's still a land."; } diff --git a/Mage.Sets/src/mage/sets/magic2010/BallLightning.java b/Mage.Sets/src/mage/sets/magic2010/BallLightning.java index 6a5e1508b32..5ab07d27ede 100644 --- a/Mage.Sets/src/mage/sets/magic2010/BallLightning.java +++ b/Mage.Sets/src/mage/sets/magic2010/BallLightning.java @@ -43,14 +43,13 @@ import mage.sets.Magic2010; * * @author BetaSteward_at_googlemail.com */ -public class BallLightning extends CardImpl { +public class BallLightning extends CardImpl { public BallLightning(UUID ownerId) { super(ownerId, "Ball Lightning", new CardType[]{CardType.CREATURE}, "{R}{R}{R}"); this.expansionSetId = Magic2010.getInstance().getId(); this.subtype.add("Elemental"); this.color.setRed(true); - this.art = "122161_typ_reg_sty_010.jpg"; this.power = new MageInt(6); this.toughness = new MageInt(1); @@ -59,4 +58,18 @@ public class BallLightning extends CardImpl { this.addAbility(new OnEventTriggeredAbility(EventType.END_TURN_STEP_PRE, "beginning of the end step", new SacrificeSourceEffect())); } + public BallLightning(final BallLightning card) { + super(card); + } + + @Override + public BallLightning copy() { + return new BallLightning(this); + } + + @Override + public String getArt() { + return "122161_typ_reg_sty_010.jpg"; + } + } diff --git a/Mage.Sets/src/mage/sets/magic2010/BaneslayerAngel.java b/Mage.Sets/src/mage/sets/magic2010/BaneslayerAngel.java index 9d54b70e0bd..b4826b84beb 100644 --- a/Mage.Sets/src/mage/sets/magic2010/BaneslayerAngel.java +++ b/Mage.Sets/src/mage/sets/magic2010/BaneslayerAngel.java @@ -44,14 +44,13 @@ import mage.sets.Magic2010; * * @author BetaSteward_at_googlemail.com */ -public class BaneslayerAngel extends CardImpl { +public class BaneslayerAngel extends CardImpl { public BaneslayerAngel(UUID ownerId) { super(ownerId, "Baneslayer Angel", new CardType[]{CardType.CREATURE}, "{3}{W}{W}"); this.expansionSetId = Magic2010.getInstance().getId(); this.subtype.add("Angel"); this.color.setWhite(true); - this.art = ""; this.power = new MageInt(5); this.toughness = new MageInt(5); @@ -66,4 +65,18 @@ public class BaneslayerAngel extends CardImpl { this.addAbility(new ProtectionAbility(filter2)); } + public BaneslayerAngel(final BaneslayerAngel card) { + super(card); + } + + @Override + public BaneslayerAngel copy() { + return new BaneslayerAngel(this); + } + + @Override + public String getArt() { + return "87090_typ_reg_sty_010.jpg"; + } + } diff --git a/Mage.Sets/src/mage/sets/magic2010/BerserkersOfBloodRidge.java b/Mage.Sets/src/mage/sets/magic2010/BerserkersOfBloodRidge.java index 7d3cbcd349c..8738289372e 100644 --- a/Mage.Sets/src/mage/sets/magic2010/BerserkersOfBloodRidge.java +++ b/Mage.Sets/src/mage/sets/magic2010/BerserkersOfBloodRidge.java @@ -39,7 +39,7 @@ import mage.sets.Magic2010; * * @author BetaSteward_at_googlemail.com */ -public class BerserkersOfBloodRidge extends CardImpl { +public class BerserkersOfBloodRidge extends CardImpl { public BerserkersOfBloodRidge(UUID ownerId) { super(ownerId, "Berserkers of Blood Ridge", new CardType[]{CardType.CREATURE}, "{4}{R}"); @@ -47,10 +47,23 @@ public class BerserkersOfBloodRidge extends CardImpl { this.color.setRed(true); this.subtype.add("Human"); this.subtype.add("Berserker"); - this.art = "121659_typ_reg_sty_010.jpg"; this.power = new MageInt(4); this.toughness = new MageInt(4); this.addAbility(new AttacksEachTurnStaticAbility()); } + public BerserkersOfBloodRidge(final BerserkersOfBloodRidge card) { + super(card); + } + + @Override + public BerserkersOfBloodRidge copy() { + return new BerserkersOfBloodRidge(this); + } + + @Override + public String getArt() { + return "121659_typ_reg_sty_010.jpg"; + } + } diff --git a/Mage.Sets/src/mage/sets/magic2010/BirdsOfParadise.java b/Mage.Sets/src/mage/sets/magic2010/BirdsOfParadise.java index 47890fcf012..a4330294f23 100644 --- a/Mage.Sets/src/mage/sets/magic2010/BirdsOfParadise.java +++ b/Mage.Sets/src/mage/sets/magic2010/BirdsOfParadise.java @@ -44,14 +44,13 @@ import mage.sets.Magic2010; * * @author BetaSteward_at_googlemail.com */ -public class BirdsOfParadise extends CardImpl { +public class BirdsOfParadise extends CardImpl { public BirdsOfParadise(UUID ownerId) { super(ownerId, "Birds of Paradise", new CardType[]{CardType.CREATURE}, "{G}"); this.expansionSetId = Magic2010.getInstance().getId(); this.subtype.add("Bird"); this.color.setGreen(true); - this.art = "88690_typ_reg_sty_010.jpg"; this.power = new MageInt(0); this.toughness = new MageInt(1); this.addAbility(FlyingAbility.getInstance()); @@ -62,4 +61,18 @@ public class BirdsOfParadise extends CardImpl { this.addAbility(new WhiteManaAbility()); } + public BirdsOfParadise(final BirdsOfParadise card) { + super(card); + } + + @Override + public BirdsOfParadise copy() { + return new BirdsOfParadise(this); + } + + @Override + public String getArt() { + return "88690_typ_reg_sty_010.jpg"; + } + } diff --git a/Mage.Sets/src/mage/sets/magic2010/BlackKnight.java b/Mage.Sets/src/mage/sets/magic2010/BlackKnight.java index fee5eb6ac93..90d2f32cdd8 100644 --- a/Mage.Sets/src/mage/sets/magic2010/BlackKnight.java +++ b/Mage.Sets/src/mage/sets/magic2010/BlackKnight.java @@ -41,7 +41,7 @@ import mage.sets.Magic2010; * * @author BetaSteward_at_googlemail.com */ -public class BlackKnight extends CardImpl { +public class BlackKnight extends CardImpl { private static FilterCard filter = new FilterCard("White"); @@ -56,7 +56,6 @@ public class BlackKnight extends CardImpl { this.subtype.add("Human"); this.subtype.add("Knight"); this.color.setBlack(true); - this.art = "121622_typ_reg_sty_010.jpg"; this.power = new MageInt(2); this.toughness = new MageInt(2); @@ -64,4 +63,18 @@ public class BlackKnight extends CardImpl { this.addAbility(new ProtectionAbility(filter)); } + public BlackKnight(final BlackKnight card) { + super(card); + } + + @Override + public BlackKnight copy() { + return new BlackKnight(this); + } + + @Override + public String getArt() { + return "121622_typ_reg_sty_010.jpg"; + } + } diff --git a/Mage.Sets/src/mage/sets/magic2010/BlindingMage.java b/Mage.Sets/src/mage/sets/magic2010/BlindingMage.java index 05415a13745..ebaa3227b4d 100644 --- a/Mage.Sets/src/mage/sets/magic2010/BlindingMage.java +++ b/Mage.Sets/src/mage/sets/magic2010/BlindingMage.java @@ -35,7 +35,7 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.costs.mana.ManaCosts; +import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.TapTargetEffect; import mage.cards.CardImpl; import mage.sets.Magic2010; @@ -45,7 +45,7 @@ import mage.target.common.TargetCreaturePermanent; * * @author BetaSteward_at_googlemail.com */ -public class BlindingMage extends CardImpl { +public class BlindingMage extends CardImpl { public BlindingMage(UUID ownerId) { super(ownerId, "Blinding Mage", new CardType[]{CardType.CREATURE}, "{1}{W}"); @@ -53,13 +53,26 @@ public class BlindingMage extends CardImpl { this.subtype.add("Human"); this.subtype.add("Wizard"); this.color.setWhite(true); - this.art = "121614_typ_reg_sty_010.jpg"; this.power = new MageInt(1); this.toughness = new MageInt(2); - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new TapTargetEffect(), new ManaCosts("{W}")); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new TapTargetEffect(), new ManaCostsImpl("{W}")); ability.addCost(new TapSourceCost()); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } + public BlindingMage(final BlindingMage card) { + super(card); + } + + @Override + public BlindingMage copy() { + return new BlindingMage(this); + } + + @Override + public String getArt() { + return "121614_typ_reg_sty_010.jpg"; + } + } diff --git a/Mage.Sets/src/mage/sets/magic2010/BogWraith.java b/Mage.Sets/src/mage/sets/magic2010/BogWraith.java index 5b21f691a37..d6c3e3f5dbf 100644 --- a/Mage.Sets/src/mage/sets/magic2010/BogWraith.java +++ b/Mage.Sets/src/mage/sets/magic2010/BogWraith.java @@ -41,7 +41,7 @@ import mage.sets.Magic2010; * * @author BetaSteward_at_googlemail.com */ -public class BogWraith extends CardImpl { +public class BogWraith extends CardImpl { private static FilterLandPermanent filter = new FilterLandPermanent("Swamp"); @@ -55,10 +55,23 @@ public class BogWraith extends CardImpl { this.expansionSetId = Magic2010.getInstance().getId(); this.color.setBlack(true); this.subtype.add("Wraith"); - this.art = ""; this.power = new MageInt(3); this.toughness = new MageInt(3); this.addAbility(new LandwalkAbility(filter)); } + public BogWraith(final BogWraith card) { + super(card); + } + + @Override + public BogWraith copy() { + return new BogWraith(this); + } + + @Override + public String getArt() { + return "106215_typ_reg_sty_010.jpg"; + } + } diff --git a/Mage.Sets/src/mage/sets/magic2010/BogardanHellkite.java b/Mage.Sets/src/mage/sets/magic2010/BogardanHellkite.java index 96ec3dceff0..1391523308a 100644 --- a/Mage.Sets/src/mage/sets/magic2010/BogardanHellkite.java +++ b/Mage.Sets/src/mage/sets/magic2010/BogardanHellkite.java @@ -38,21 +38,19 @@ import mage.abilities.keyword.FlashAbility; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.sets.Magic2010; -import mage.target.common.TargetCreatureOrPlayer; import mage.target.common.TargetCreatureOrPlayerAmount; /** * * @author BetaSteward_at_googlemail.com */ -public class BogardanHellkite extends CardImpl { +public class BogardanHellkite extends CardImpl { public BogardanHellkite(UUID ownerId) { super(ownerId, "Bogardan Hellkite", new CardType[]{CardType.CREATURE}, "{6}{R}{R}"); this.expansionSetId = Magic2010.getInstance().getId(); this.subtype.add("Dragon"); this.color.setRed(true); - this.art = "97381_typ_reg_sty_010.jpg"; this.power = new MageInt(5); this.toughness = new MageInt(5); @@ -63,4 +61,18 @@ public class BogardanHellkite extends CardImpl { this.addAbility(ability); } + public BogardanHellkite(final BogardanHellkite card) { + super(card); + } + + @Override + public BogardanHellkite copy() { + return new BogardanHellkite(this); + } + + @Override + public String getArt() { + return "97381_typ_reg_sty_010.jpg"; + } + } diff --git a/Mage.Sets/src/mage/sets/magic2010/Cancel.java b/Mage.Sets/src/mage/sets/magic2010/Cancel.java index d5d967df761..36af1b92be5 100644 --- a/Mage.Sets/src/mage/sets/magic2010/Cancel.java +++ b/Mage.Sets/src/mage/sets/magic2010/Cancel.java @@ -39,15 +39,28 @@ import mage.target.TargetSpell; * * @author BetaSteward_at_googlemail.com */ -public class Cancel extends CardImpl { +public class Cancel extends CardImpl { public Cancel(UUID ownerId) { super(ownerId, "Cancel", new CardType[]{CardType.INSTANT}, "{1}{U}{U}"); this.expansionSetId = Magic2010.getInstance().getId(); this.color.setBlue(true); - this.art = "116179_typ_reg_sty_010.jpg"; this.getSpellAbility().addTarget(new TargetSpell()); this.getSpellAbility().addEffect(new CounterTargetEffect()); } + public Cancel(final Cancel card) { + super(card); + } + + @Override + public Cancel copy() { + return new Cancel(this); + } + + @Override + public String getArt() { + return "116179_typ_reg_sty_010.jpg"; + } + } diff --git a/Mage.Sets/src/mage/sets/magic2010/CelestialPurge.java b/Mage.Sets/src/mage/sets/magic2010/CelestialPurge.java index 878286a912c..f5ee9895184 100644 --- a/Mage.Sets/src/mage/sets/magic2010/CelestialPurge.java +++ b/Mage.Sets/src/mage/sets/magic2010/CelestialPurge.java @@ -42,7 +42,7 @@ import mage.target.TargetPermanent; * * @author BetaSteward_at_googlemail.com */ -public class CelestialPurge extends CardImpl { +public class CelestialPurge extends CardImpl { private static FilterPermanent filter = new FilterPermanent("black or red permanent"); @@ -57,9 +57,22 @@ public class CelestialPurge extends CardImpl { super(ownerId, "Celestial Purge", new CardType[]{CardType.INSTANT}, "{1}{W}"); this.expansionSetId = Magic2010.getInstance().getId(); this.color.setWhite(true); - this.art = "118751_typ_reg_sty_010.jpg"; this.getSpellAbility().addTarget(new TargetPermanent(filter, TargetController.ANY)); this.getSpellAbility().addEffect(new ExileTargetEffect()); } + public CelestialPurge(final CelestialPurge card) { + super(card); + } + + @Override + public CelestialPurge copy() { + return new CelestialPurge(this); + } + + @Override + public String getArt() { + return "118751_typ_reg_sty_010.jpg"; + } + } diff --git a/Mage.Sets/src/mage/sets/magic2010/DiabolicTutor.java b/Mage.Sets/src/mage/sets/magic2010/DiabolicTutor.java index 161539a3b1c..f7747e39d86 100644 --- a/Mage.Sets/src/mage/sets/magic2010/DiabolicTutor.java +++ b/Mage.Sets/src/mage/sets/magic2010/DiabolicTutor.java @@ -32,7 +32,6 @@ import java.util.UUID; import mage.Constants.CardType; import mage.abilities.effects.common.SearchLibraryPutInHandEffect; import mage.cards.CardImpl; -import mage.filter.FilterCard; import mage.sets.Magic2010; import mage.target.common.TargetCardInLibrary; @@ -40,14 +39,27 @@ import mage.target.common.TargetCardInLibrary; * * @author LokiX */ -public class DiabolicTutor extends CardImpl { +public class DiabolicTutor extends CardImpl { public DiabolicTutor(UUID onwerId){ super(onwerId, "Diabolic Tutor", new CardType[]{CardType.INSTANT},"{2}{B}{B}"); this.expansionSetId = Magic2010.getInstance().getId(); this.color.setBlack(true); - this.art = "101052_typ_reg_sty_010.jpg"; TargetCardInLibrary target = new TargetCardInLibrary(); this.getSpellAbility().addEffect(new SearchLibraryPutInHandEffect(target)); } + + public DiabolicTutor(final DiabolicTutor card) { + super(card); + } + + @Override + public DiabolicTutor copy() { + return new DiabolicTutor(this); + } + + @Override + public String getArt() { + return "101052_typ_reg_sty_010.jpg"; + } } diff --git a/Mage.Sets/src/mage/sets/magic2010/DoomBlade.java b/Mage.Sets/src/mage/sets/magic2010/DoomBlade.java index 351372a551d..a8ca1fa6e03 100644 --- a/Mage.Sets/src/mage/sets/magic2010/DoomBlade.java +++ b/Mage.Sets/src/mage/sets/magic2010/DoomBlade.java @@ -41,7 +41,7 @@ import mage.target.common.TargetCreaturePermanent; * * @author LokiX */ -public class DoomBlade extends CardImpl { +public class DoomBlade extends CardImpl { private static FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creature"); @@ -55,8 +55,21 @@ public class DoomBlade extends CardImpl { super(onwerId, "Doom Blade", new CardType[]{CardType.INSTANT},"{1}{B}"); this.expansionSetId = Magic2010.getInstance().getId(); this.color.setBlack(true); - this.art = "121618_typ_reg_sty_010.jpg"; this.getSpellAbility().addTarget(new TargetCreaturePermanent(1,1,filter, TargetController.ANY)); this.getSpellAbility().addEffect(new DestroyTargetEffect()); } + + public DoomBlade(final DoomBlade card) { + super(card); + } + + @Override + public DoomBlade copy() { + return new DoomBlade(this); + } + + @Override + public String getArt() { + return "121618_typ_reg_sty_010.jpg"; + } } diff --git a/Mage.Sets/src/mage/sets/magic2010/DragonskullSummit.java b/Mage.Sets/src/mage/sets/magic2010/DragonskullSummit.java index 84e9ae3feff..5b4dd2c39ad 100644 --- a/Mage.Sets/src/mage/sets/magic2010/DragonskullSummit.java +++ b/Mage.Sets/src/mage/sets/magic2010/DragonskullSummit.java @@ -43,7 +43,7 @@ import mage.sets.Magic2010; * * @author BetaSteward_at_googlemail.com */ -public class DragonskullSummit extends CardImpl { +public class DragonskullSummit extends CardImpl { private static FilterLandPermanent filter = new FilterLandPermanent(); @@ -57,9 +57,22 @@ public class DragonskullSummit extends CardImpl { public DragonskullSummit(UUID ownerId) { super(ownerId, "Dragonskull Summit", new CardType[]{CardType.LAND}, null); this.expansionSetId = Magic2010.getInstance().getId(); - this.art = "121671_typ_reg_sty_010.jpg"; this.addAbility(new EntersBattlefieldStaticAbility(new EntersBattlefieldTappedUnlessControlsEffect(filter))); this.addAbility(new BlackManaAbility()); this.addAbility(new RedManaAbility()); } + + public DragonskullSummit(final DragonskullSummit card) { + super(card); + } + + @Override + public DragonskullSummit copy() { + return new DragonskullSummit(this); + } + + @Override + public String getArt() { + return "121671_typ_reg_sty_010.jpg"; + } } diff --git a/Mage.Sets/src/mage/sets/magic2010/Earthquake.java b/Mage.Sets/src/mage/sets/magic2010/Earthquake.java index 65b0fede41c..86574995a5d 100644 --- a/Mage.Sets/src/mage/sets/magic2010/Earthquake.java +++ b/Mage.Sets/src/mage/sets/magic2010/Earthquake.java @@ -31,6 +31,7 @@ package mage.sets.magic2010; import java.util.UUID; import mage.Constants.CardType; import mage.Constants.Outcome; +import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; @@ -44,34 +45,56 @@ import mage.sets.Magic2010; * * @author BetaSteward_at_googlemail.com */ -public class Earthquake extends CardImpl { +public class Earthquake extends CardImpl { public Earthquake(UUID ownerId) { super(ownerId, "Earthquake", new CardType[]{CardType.SORCERY}, "{X}{R}"); this.expansionSetId = Magic2010.getInstance().getId(); this.color.setRed(true); - this.art = "118685_typ_reg_sty_010.jpg"; this.getSpellAbility().addEffect(new EarthquakeEffect()); } + + public Earthquake(final Earthquake card) { + super(card); + } + + @Override + public Earthquake copy() { + return new Earthquake(this); + } + + @Override + public String getArt() { + return "118685_typ_reg_sty_010.jpg"; + } } -class EarthquakeEffect extends OneShotEffect { +class EarthquakeEffect extends OneShotEffect { public EarthquakeEffect() { super(Outcome.Damage); } + public EarthquakeEffect(final EarthquakeEffect effect) { + super(effect); + } + @Override - public boolean apply(Game game) { - int amount = this.source.getManaCosts().getVariableCosts().get(0).getValue(); + public EarthquakeEffect copy() { + return new EarthquakeEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + int amount = source.getManaCosts().getVariableCosts().get(0).getValue(); FilterCreaturePermanent filter = new FilterCreaturePermanent(); filter.getAbilities().add(FlyingAbility.getInstance()); filter.setNotAbilities(true); - for (Permanent permanent: game.getBattlefield().getActivePermanents(filter, this.source.getControllerId(), game)) { + for (Permanent permanent: game.getBattlefield().getActivePermanents(filter, source.getControllerId(), game)) { permanent.damage(amount, source.getId(), game); } - for (UUID playerId: game.getPlayer(this.source.getControllerId()).getInRange()) { + for (UUID playerId: game.getPlayer(source.getControllerId()).getInRange()) { Player player = game.getPlayer(playerId); if (player != null) player.damage(amount, source.getId(), game); @@ -80,7 +103,7 @@ class EarthquakeEffect extends OneShotEffect { } @Override - public String getText() { + public String getText(Ability source) { return "Earthquake deals X damage to each creature without flying and each player"; } } diff --git a/Mage.Sets/src/mage/sets/magic2010/EliteVanguard.java b/Mage.Sets/src/mage/sets/magic2010/EliteVanguard.java index bccca173eec..ad31e283329 100644 --- a/Mage.Sets/src/mage/sets/magic2010/EliteVanguard.java +++ b/Mage.Sets/src/mage/sets/magic2010/EliteVanguard.java @@ -38,7 +38,7 @@ import mage.sets.Magic2010; * * @author BetaSteward_at_googlemail.com */ -public class EliteVanguard extends CardImpl { +public class EliteVanguard extends CardImpl { public EliteVanguard(UUID ownerId) { super(ownerId, "Elite Vanguard", new CardType[]{CardType.CREATURE}, "{W}"); @@ -46,9 +46,22 @@ public class EliteVanguard extends CardImpl { this.color.setWhite(true); this.subtype.add("Human"); this.subtype.add("Soldier"); - this.art = "103534_typ_reg_sty_010.jpg"; this.power = new MageInt(2); this.toughness = new MageInt(1); } + public EliteVanguard(final EliteVanguard card) { + super(card); + } + + @Override + public EliteVanguard copy() { + return new EliteVanguard(this); + } + + @Override + public String getArt() { + return "103534_typ_reg_sty_010.jpg"; + } + } diff --git a/Mage.Sets/src/mage/sets/magic2010/Flashfreeze.java b/Mage.Sets/src/mage/sets/magic2010/Flashfreeze.java index 9aa15965f88..cd652f1e9c9 100644 --- a/Mage.Sets/src/mage/sets/magic2010/Flashfreeze.java +++ b/Mage.Sets/src/mage/sets/magic2010/Flashfreeze.java @@ -41,7 +41,7 @@ import mage.target.TargetSpell; * * @author BetaSteward_at_googlemail.com */ -public class Flashfreeze extends CardImpl { +public class Flashfreeze extends CardImpl { private static FilterSpell filter = new FilterSpell("red or green spell"); @@ -56,9 +56,22 @@ public class Flashfreeze extends CardImpl { super(ownerId, "Flashfreeze", new CardType[]{CardType.INSTANT}, "{1}{U}"); this.expansionSetId = Magic2010.getInstance().getId(); this.color.setBlue(true); - this.art = "96954_typ_reg_sty_010.jpg"; this.getSpellAbility().addTarget(new TargetSpell(filter)); this.getSpellAbility().addEffect(new CounterTargetEffect()); } + public Flashfreeze(final Flashfreeze card) { + super(card); + } + + @Override + public Flashfreeze copy() { + return new Flashfreeze(this); + } + + @Override + public String getArt() { + return "96954_typ_reg_sty_010.jpg"; + } + } diff --git a/Mage.Sets/src/mage/sets/magic2010/GargoyleCastle.java b/Mage.Sets/src/mage/sets/magic2010/GargoyleCastle.java index a814a0831de..00d4955be64 100644 --- a/Mage.Sets/src/mage/sets/magic2010/GargoyleCastle.java +++ b/Mage.Sets/src/mage/sets/magic2010/GargoyleCastle.java @@ -47,19 +47,32 @@ import mage.sets.Magic2010; * * @author BetaSteward_at_googlemail.com */ -public class GargoyleCastle extends CardImpl { +public class GargoyleCastle extends CardImpl { public GargoyleCastle(UUID ownerId) { super(ownerId, "Gargoyle Castle", new CardType[]{CardType.LAND}, null); this.expansionSetId = Magic2010.getInstance().getId(); - this.art = "122169_typ_reg_sty_010.jpg"; this.addAbility(new ColorlessManaAbility()); this.addAbility(new GargoyleCastleAbility()); } + public GargoyleCastle(final GargoyleCastle card) { + super(card); + } + + @Override + public GargoyleCastle copy() { + return new GargoyleCastle(this); + } + + @Override + public String getArt() { + return "122169_typ_reg_sty_010.jpg"; + } + } -class GargoyleCastleAbility extends ActivatedAbilityImpl { +class GargoyleCastleAbility extends ActivatedAbilityImpl { public GargoyleCastleAbility() { super(Zone.BATTLEFIELD, null); @@ -69,6 +82,15 @@ class GargoyleCastleAbility extends ActivatedAbilityImpl { addEffect(new CreateTokenEffect(new GargoyleToken())); } + public GargoyleCastleAbility(final GargoyleCastleAbility ability) { + super(ability); + } + + @Override + public GargoyleCastleAbility copy() { + return new GargoyleCastleAbility(this); + } + } class GargoyleToken extends Token { diff --git a/Mage.Sets/src/mage/sets/magic2010/GarrukWildspeaker.java b/Mage.Sets/src/mage/sets/magic2010/GarrukWildspeaker.java index 54d57c40b13..7443f5e2869 100644 --- a/Mage.Sets/src/mage/sets/magic2010/GarrukWildspeaker.java +++ b/Mage.Sets/src/mage/sets/magic2010/GarrukWildspeaker.java @@ -50,7 +50,7 @@ import mage.target.common.TargetLandPermanent; * * @author BetaSteward_at_googlemail.com */ -public class GarrukWildspeaker extends CardImpl { +public class GarrukWildspeaker extends CardImpl { private static BeastToken beastToken = new BeastToken(); @@ -59,7 +59,6 @@ public class GarrukWildspeaker extends CardImpl { this.expansionSetId = Magic2010.getInstance().getId(); this.subtype.add("Garruk"); this.color.setGreen(true); - this.art = "105523_typ_reg_sty_010.jpg"; this.loyalty = new MageInt(3); LoyaltyAbility ability1 = new LoyaltyAbility(new UntapTargetEffect(), 1); @@ -68,9 +67,23 @@ public class GarrukWildspeaker extends CardImpl { this.addAbility(new LoyaltyAbility(new CreateTokenEffect(beastToken), -1)); - Effects effects1 = new Effects(null); + Effects effects1 = new Effects(); effects1.add(new BoostControlledEffect(3, 3, Duration.EndOfTurn)); effects1.add(new GainAbilityControlledEffect(TrampleAbility.getInstance(), Duration.EndOfTurn, new FilterCreaturePermanent())); this.addAbility(new LoyaltyAbility(effects1, -4)); } + + public GarrukWildspeaker(final GarrukWildspeaker card) { + super(card); + } + + @Override + public GarrukWildspeaker copy() { + return new GarrukWildspeaker(this); + } + + @Override + public String getArt() { + return "105523_typ_reg_sty_010.jpg"; + } } diff --git a/Mage.Sets/src/mage/sets/magic2010/GiantGrowth.java b/Mage.Sets/src/mage/sets/magic2010/GiantGrowth.java index 19bac95a5c8..6aa16ebe34e 100644 --- a/Mage.Sets/src/mage/sets/magic2010/GiantGrowth.java +++ b/Mage.Sets/src/mage/sets/magic2010/GiantGrowth.java @@ -40,15 +40,28 @@ import mage.target.common.TargetCreaturePermanent; * * @author BetaSteward_at_googlemail.com */ -public class GiantGrowth extends CardImpl { +public class GiantGrowth extends CardImpl { public GiantGrowth(UUID ownerId) { super(ownerId, "Giant Growth", new CardType[]{CardType.INSTANT}, "{G}"); this.expansionSetId = Magic2010.getInstance().getId(); this.color.setGreen(true); - this.art = "101059_typ_reg_sty_010.jpg"; this.getSpellAbility().addTarget(new TargetCreaturePermanent()); this.getSpellAbility().addEffect(new BoostTargetEffect(3, 3, Duration.EndOfTurn)); } + public GiantGrowth(final GiantGrowth card) { + super(card); + } + + @Override + public GiantGrowth copy() { + return new GiantGrowth(this); + } + + @Override + public String getArt() { + return "101059_typ_reg_sty_010.jpg"; + } + } diff --git a/Mage.Sets/src/mage/sets/magic2010/GlacialFortress.java b/Mage.Sets/src/mage/sets/magic2010/GlacialFortress.java index 4dfda1620fb..3dbe50bcf28 100644 --- a/Mage.Sets/src/mage/sets/magic2010/GlacialFortress.java +++ b/Mage.Sets/src/mage/sets/magic2010/GlacialFortress.java @@ -43,7 +43,7 @@ import mage.sets.Magic2010; * * @author BetaSteward_at_googlemail.com */ -public class GlacialFortress extends CardImpl { +public class GlacialFortress extends CardImpl { private static FilterLandPermanent filter = new FilterLandPermanent(); @@ -57,9 +57,22 @@ public class GlacialFortress extends CardImpl { public GlacialFortress(UUID ownerId) { super(ownerId, "Glacial Fortress", new CardType[]{CardType.LAND}, null); this.expansionSetId = Magic2010.getInstance().getId(); - this.art = "121634_typ_reg_sty_010.jpg"; this.addAbility(new EntersBattlefieldStaticAbility(new EntersBattlefieldTappedUnlessControlsEffect(filter))); this.addAbility(new BlueManaAbility()); this.addAbility(new WhiteManaAbility()); } + + public GlacialFortress(final GlacialFortress card) { + super(card); + } + + @Override + public GlacialFortress copy() { + return new GlacialFortress(this); + } + + @Override + public String getArt() { + return "121634_typ_reg_sty_010.jpg"; + } } diff --git a/Mage.Sets/src/mage/sets/magic2010/GreatSableStag.java b/Mage.Sets/src/mage/sets/magic2010/GreatSableStag.java index f11c2aed32e..9d656ab802e 100644 --- a/Mage.Sets/src/mage/sets/magic2010/GreatSableStag.java +++ b/Mage.Sets/src/mage/sets/magic2010/GreatSableStag.java @@ -43,14 +43,13 @@ import mage.sets.Magic2010; * * @author BetaSteward_at_googlemail.com */ -public class GreatSableStag extends CardImpl { +public class GreatSableStag extends CardImpl { public GreatSableStag(UUID ownerId) { super(ownerId, "Great Sable Stag", new CardType[]{CardType.CREATURE}, "{1}{G}{G}"); this.expansionSetId = Magic2010.getInstance().getId(); this.subtype.add("Elk"); this.color.setGreen(true); - this.art = "122172_typ_reg_sty_010.jpg"; this.power = new MageInt(3); this.toughness = new MageInt(3); @@ -65,4 +64,18 @@ public class GreatSableStag extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.STACK, new CantCounterSourceEffect())); } + public GreatSableStag(final GreatSableStag card) { + super(card); + } + + @Override + public GreatSableStag copy() { + return new GreatSableStag(this); + } + + @Override + public String getArt() { + return "122172_typ_reg_sty_010.jpg"; + } + } diff --git a/Mage.Sets/src/mage/sets/magic2010/HonorOfThePure.java b/Mage.Sets/src/mage/sets/magic2010/HonorOfThePure.java index 967f1fff187..b113b1eb6aa 100644 --- a/Mage.Sets/src/mage/sets/magic2010/HonorOfThePure.java +++ b/Mage.Sets/src/mage/sets/magic2010/HonorOfThePure.java @@ -42,18 +42,34 @@ import mage.sets.Magic2010; * * @author BetaSteward_at_googlemail.com */ -public class HonorOfThePure extends CardImpl { +public class HonorOfThePure extends CardImpl { - private FilterCreaturePermanent filter = new FilterCreaturePermanent("White creatures"); + private static FilterCreaturePermanent filter = new FilterCreaturePermanent("White creatures"); + + static { + filter.setUseColor(true); + filter.getColor().setWhite(true); + } public HonorOfThePure(UUID ownerId) { super(ownerId, "Honor of the Pure", new CardType[]{CardType.ENCHANTMENT}, "{1}{W}"); this.expansionSetId = Magic2010.getInstance().getId(); this.color.setWhite(true); - this.art = "102628_typ_reg_sty_010.jpg"; - filter.setUseColor(true); - filter.getColor().setWhite(true); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostControlledEffect(1, 1, Duration.WhileOnBattlefield, filter))); } + public HonorOfThePure(final HonorOfThePure card) { + super(card); + } + + @Override + public HonorOfThePure copy() { + return new HonorOfThePure(this); + } + + @Override + public String getArt() { + return "102628_typ_reg_sty_010.jpg"; + } + } diff --git a/Mage.Sets/src/mage/sets/magic2010/HowlingMine.java b/Mage.Sets/src/mage/sets/magic2010/HowlingMine.java index a64246e382e..caf2f243d79 100644 --- a/Mage.Sets/src/mage/sets/magic2010/HowlingMine.java +++ b/Mage.Sets/src/mage/sets/magic2010/HowlingMine.java @@ -44,28 +44,50 @@ import mage.target.TargetPlayer; * * @author BetaSteward_at_googlemail.com */ -public class HowlingMine extends CardImpl { +public class HowlingMine extends CardImpl { public HowlingMine(UUID ownerId) { super(ownerId, "Howling Mine", new CardType[]{CardType.ARTIFACT}, "{2}"); this.expansionSetId = Magic2010.getInstance().getId(); - this.art = "102959_typ_reg_sty_010.jpg"; this.addAbility(new HowlingMineAbility()); } + public HowlingMine(final HowlingMine card) { + super(card); + } + + @Override + public HowlingMine copy() { + return new HowlingMine(this); + } + + @Override + public String getArt() { + return "102959_typ_reg_sty_010.jpg"; + } + } -class HowlingMineAbility extends TriggeredAbilityImpl { +class HowlingMineAbility extends TriggeredAbilityImpl { public HowlingMineAbility() { super(Zone.BATTLEFIELD, new DrawCardTargetEffect(1)); } + public HowlingMineAbility(final HowlingMineAbility ability) { + super(ability); + } + + @Override + public HowlingMineAbility copy() { + return new HowlingMineAbility(this); + } + @Override public boolean checkTrigger(GameEvent event, Game game) { if (event.getType() == EventType.DRAW_STEP_PRE) { this.addTarget(new TargetPlayer()); - this.targets.get(0).getTargets().add(event.getPlayerId()); + this.targets.get(0).addTarget(event.getPlayerId(), null, game); trigger(game, event.getPlayerId()); return true; } diff --git a/Mage.Sets/src/mage/sets/magic2010/JaceBeleren.java b/Mage.Sets/src/mage/sets/magic2010/JaceBeleren.java index 6db5674aab0..f0222422f69 100644 --- a/Mage.Sets/src/mage/sets/magic2010/JaceBeleren.java +++ b/Mage.Sets/src/mage/sets/magic2010/JaceBeleren.java @@ -43,14 +43,13 @@ import mage.target.TargetPlayer; * * @author BetaSteward_at_googlemail.com */ -public class JaceBeleren extends CardImpl { +public class JaceBeleren extends CardImpl { public JaceBeleren(UUID ownerId) { super(ownerId, "Jace Beleren", new CardType[]{CardType.PLANESWALKER}, "{1}{U}{U}"); this.expansionSetId = Magic2010.getInstance().getId(); this.subtype.add("Jace"); this.color.setBlue(true); - this.art = "105537_typ_reg_sty_010.jpg"; this.loyalty = new MageInt(3); this.addAbility(new LoyaltyAbility(new DrawCardAllEffect(1), 2)); @@ -65,4 +64,18 @@ public class JaceBeleren extends CardImpl { } + public JaceBeleren(final JaceBeleren card) { + super(card); + } + + @Override + public JaceBeleren copy() { + return new JaceBeleren(this); + } + + @Override + public String getArt() { + return "105537_typ_reg_sty_010.jpg"; + } + } diff --git a/Mage.Sets/src/mage/sets/magic2010/LightningBolt.java b/Mage.Sets/src/mage/sets/magic2010/LightningBolt.java index c524be5cf8d..4cc5a2177bc 100644 --- a/Mage.Sets/src/mage/sets/magic2010/LightningBolt.java +++ b/Mage.Sets/src/mage/sets/magic2010/LightningBolt.java @@ -39,15 +39,28 @@ import mage.target.common.TargetCreatureOrPlayer; * * @author BetaSteward_at_googlemail.com */ -public class LightningBolt extends CardImpl { +public class LightningBolt extends CardImpl { public LightningBolt(UUID ownerId) { super(ownerId, "Lightning Bolt", new CardType[]{CardType.INSTANT}, "{R}"); this.expansionSetId = Magic2010.getInstance().getId(); this.color.setRed(true); - this.art = "121669_typ_reg_sty_010.jpg"; this.getSpellAbility().addTarget(new TargetCreatureOrPlayer()); this.getSpellAbility().addEffect(new DamageTargetEffect(3)); } + public LightningBolt(final LightningBolt card) { + super(card); + } + + @Override + public LightningBolt copy() { + return new LightningBolt(this); + } + + @Override + public String getArt() { + return "121669_typ_reg_sty_010.jpg"; + } + } diff --git a/Mage.Sets/src/mage/sets/magic2010/LlanowarElves.java b/Mage.Sets/src/mage/sets/magic2010/LlanowarElves.java index 170aa54bb8d..49a1d65999d 100644 --- a/Mage.Sets/src/mage/sets/magic2010/LlanowarElves.java +++ b/Mage.Sets/src/mage/sets/magic2010/LlanowarElves.java @@ -31,12 +31,7 @@ package mage.sets.magic2010; import java.util.UUID; import mage.Constants.CardType; import mage.MageInt; -import mage.abilities.keyword.FlyingAbility; -import mage.abilities.mana.BlackManaAbility; -import mage.abilities.mana.BlueManaAbility; import mage.abilities.mana.GreenManaAbility; -import mage.abilities.mana.RedManaAbility; -import mage.abilities.mana.WhiteManaAbility; import mage.cards.CardImpl; import mage.sets.Magic2010; @@ -44,7 +39,7 @@ import mage.sets.Magic2010; * * @author BetaSteward_at_googlemail.com */ -public class LlanowarElves extends CardImpl { +public class LlanowarElves extends CardImpl { public LlanowarElves(UUID ownerId) { super(ownerId, "Llanowar Elves", new CardType[]{CardType.CREATURE}, "{G}"); @@ -52,10 +47,23 @@ public class LlanowarElves extends CardImpl { this.subtype.add("Elf"); this.subtype.add("Druid"); this.color.setGreen(true); - this.art = "86938_typ_reg_sty_010.jpg"; this.power = new MageInt(1); this.toughness = new MageInt(1); this.addAbility(new GreenManaAbility()); } + public LlanowarElves(final LlanowarElves card) { + super(card); + } + + @Override + public LlanowarElves copy() { + return new LlanowarElves(this); + } + + @Override + public String getArt() { + return "86938_typ_reg_sty_010.jpg"; + } + } diff --git a/Mage.Sets/src/mage/sets/magic2010/MasterOfTheWildHunt.java b/Mage.Sets/src/mage/sets/magic2010/MasterOfTheWildHunt.java index 02f7b67c1f0..c63147d2a91 100644 --- a/Mage.Sets/src/mage/sets/magic2010/MasterOfTheWildHunt.java +++ b/Mage.Sets/src/mage/sets/magic2010/MasterOfTheWildHunt.java @@ -55,7 +55,7 @@ import mage.target.common.TargetCreaturePermanent; * * @author BetaSteward_at_googlemail.com */ -public class MasterOfTheWildHunt extends CardImpl { +public class MasterOfTheWildHunt extends CardImpl { private static WolfToken wolfToken = new WolfToken(); @@ -65,34 +65,59 @@ public class MasterOfTheWildHunt extends CardImpl { this.subtype.add("Human"); this.subtype.add("Shaman"); this.color.setGreen(true); - this.art = "121652_typ_reg_sty_010.jpg"; this.power = new MageInt(3); this.toughness = new MageInt(3); this.addAbility(new OnEventTriggeredAbility(EventType.UPKEEP_STEP_PRE, "beginning of your upkeep", new CreateTokenEffect(wolfToken))); Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new MasterOfTheWildHuntEffect(), new TapSourceCost()); - ability.getTargets().add(new TargetCreaturePermanent()); + ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } + + public MasterOfTheWildHunt(final MasterOfTheWildHunt card) { + super(card); + } + + @Override + public MasterOfTheWildHunt copy() { + return new MasterOfTheWildHunt(this); + } + + @Override + public String getArt() { + return "121652_typ_reg_sty_010.jpg"; + } } -class MasterOfTheWildHuntEffect extends OneShotEffect { +class MasterOfTheWildHuntEffect extends OneShotEffect { - private FilterCreaturePermanent filter = new FilterCreaturePermanent(); + private static FilterCreaturePermanent filter = new FilterCreaturePermanent(); - public MasterOfTheWildHuntEffect() { - super(Outcome.Damage); + static { filter.getName().add("Wolf"); filter.setTapped(false); filter.setUseTapped(true); } + public MasterOfTheWildHuntEffect() { + super(Outcome.Damage); + } + + public MasterOfTheWildHuntEffect(final MasterOfTheWildHuntEffect effect) { + super(effect); + } + @Override - public boolean apply(Game game) { + public MasterOfTheWildHuntEffect copy() { + return new MasterOfTheWildHuntEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { List wolves = new ArrayList(); - Permanent target = game.getPermanent(this.source.getFirstTarget()); + Permanent target = game.getPermanent(source.getFirstTarget()); if (target != null) { - for (Permanent permanent: game.getBattlefield().getAllActivePermanents(filter, this.source.getControllerId())) { + for (Permanent permanent: game.getBattlefield().getAllActivePermanents(filter, source.getControllerId())) { permanent.tap(game); target.damage(permanent.getToughness().getValue(), permanent.getId(), game); } @@ -103,7 +128,7 @@ class MasterOfTheWildHuntEffect extends OneShotEffect { } @Override - public String getText() { + public String getText(Ability source) { return "Tap all untapped Wolf creatures you control. Each Wolf tapped this way deals damage equal to its power to target creature. That creature deals damage equal to its power divided as its controller chooses among any number of those Wolves"; } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/magic2010/MightOfOaks.java b/Mage.Sets/src/mage/sets/magic2010/MightOfOaks.java index e391e95e9df..597a5f9a32d 100644 --- a/Mage.Sets/src/mage/sets/magic2010/MightOfOaks.java +++ b/Mage.Sets/src/mage/sets/magic2010/MightOfOaks.java @@ -40,14 +40,27 @@ import mage.target.common.TargetCreaturePermanent; * * @author LokiX */ -public class MightOfOaks extends CardImpl { +public class MightOfOaks extends CardImpl { public MightOfOaks(UUID onwerId){ super(onwerId, "Might of Oaks", new CardType[]{CardType.INSTANT},"{3}{G}"); this.expansionSetId = Magic2010.getInstance().getId(); this.color.setGreen(true); - this.art = "102958_typ_reg_sty_010.jpg"; this.getSpellAbility().addTarget(new TargetCreaturePermanent()); this.getSpellAbility().addEffect(new BoostTargetEffect(7, 7, Duration.EndOfTurn)); } + + public MightOfOaks(final MightOfOaks card) { + super(card); + } + + @Override + public MightOfOaks copy() { + return new MightOfOaks(this); + } + + @Override + public String getArt() { + return "102958_typ_reg_sty_010.jpg"; + } } diff --git a/Mage.Sets/src/mage/sets/magic2010/MindRot.java b/Mage.Sets/src/mage/sets/magic2010/MindRot.java index f5544e8e83b..3ec9a218a0e 100644 --- a/Mage.Sets/src/mage/sets/magic2010/MindRot.java +++ b/Mage.Sets/src/mage/sets/magic2010/MindRot.java @@ -39,14 +39,27 @@ import mage.target.TargetPlayer; * * @author LokiX */ -public class MindRot extends CardImpl { +public class MindRot extends CardImpl { public MindRot(UUID onwerId){ super(onwerId, "Mind Rot", new CardType[]{CardType.SORCERY},"{2}{B}"); this.expansionSetId = Magic2010.getInstance().getId(); this.color.setBlack(true); - this.art = ""; this.getSpellAbility().addTarget(new TargetPlayer()); this.getSpellAbility().addEffect(new DiscardTargetEffect(2)); } + + public MindRot(final MindRot card) { + super(card); + } + + @Override + public MindRot copy() { + return new MindRot(this); + } + + @Override + public String getArt() { + return "04716_typ_reg_sty_001.jpg"; + } } diff --git a/Mage.Sets/src/mage/sets/magic2010/MindSpring.java b/Mage.Sets/src/mage/sets/magic2010/MindSpring.java index a5ecbb6a10f..d3a060f875a 100644 --- a/Mage.Sets/src/mage/sets/magic2010/MindSpring.java +++ b/Mage.Sets/src/mage/sets/magic2010/MindSpring.java @@ -31,6 +31,7 @@ package mage.sets.magic2010; import java.util.UUID; import mage.Constants.CardType; import mage.Constants.Outcome; +import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.game.Game; @@ -41,28 +42,50 @@ import mage.sets.Magic2010; * * @author BetaSteward_at_googlemail.com */ -public class MindSpring extends CardImpl { +public class MindSpring extends CardImpl { public MindSpring(UUID ownerId) { super(ownerId, "Mind Spring", new CardType[]{CardType.SORCERY}, "{X}{U}{U}"); this.expansionSetId = Magic2010.getInstance().getId(); this.color.setBlue(true); - this.art = "109919_typ_reg_sty_010.jpg"; this.getSpellAbility().addEffect(new MindSpringEffect()); } + public MindSpring(final MindSpring card) { + super(card); + } + + @Override + public MindSpring copy() { + return new MindSpring(this); + } + + @Override + public String getArt() { + return "109919_typ_reg_sty_010.jpg"; + } + } -class MindSpringEffect extends OneShotEffect { +class MindSpringEffect extends OneShotEffect { public MindSpringEffect() { super(Outcome.DrawCard); } + public MindSpringEffect(final MindSpringEffect effect) { + super(effect); + } + @Override - public boolean apply(Game game) { - int amount = this.source.getManaCosts().getVariableCosts().get(0).getValue(); - Player player = game.getPlayer(this.source.getControllerId()); + public MindSpringEffect copy() { + return new MindSpringEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + int amount = source.getManaCosts().getVariableCosts().get(0).getValue(); + Player player = game.getPlayer(source.getControllerId()); if (player != null) { player.drawCards(amount, game); return true; @@ -71,7 +94,7 @@ class MindSpringEffect extends OneShotEffect { } @Override - public String getText() { + public String getText(Ability source) { return "Draw X cards"; } } diff --git a/Mage.Sets/src/mage/sets/magic2010/Naturalize.java b/Mage.Sets/src/mage/sets/magic2010/Naturalize.java index f10dac036fc..4bbfdd39d7a 100644 --- a/Mage.Sets/src/mage/sets/magic2010/Naturalize.java +++ b/Mage.Sets/src/mage/sets/magic2010/Naturalize.java @@ -42,7 +42,7 @@ import mage.target.TargetPermanent; * * @author LokiX */ -public class Naturalize extends CardImpl { +public class Naturalize extends CardImpl { private static FilterPermanent filter = new FilterPermanent("artifact or enchantment"); @@ -56,8 +56,21 @@ public class Naturalize extends CardImpl { super(onwerId, "Naturalize", new CardType[]{CardType.INSTANT},"{1}{G}"); this.expansionSetId = Magic2010.getInstance().getId(); this.color.setGreen(true); - this.art = "49669_typ_reg_sty_010.jpg"; this.getSpellAbility().addTarget(new TargetPermanent(filter, TargetController.ANY)); this.getSpellAbility().addEffect(new DestroyTargetEffect()); } + + public Naturalize(final Naturalize card) { + super(card); + } + + @Override + public Naturalize copy() { + return new Naturalize(this); + } + + @Override + public String getArt() { + return "49669_typ_reg_sty_010.jpg"; + } } diff --git a/Mage.Sets/src/mage/sets/magic2010/Negate.java b/Mage.Sets/src/mage/sets/magic2010/Negate.java index 7b0e7dd5c14..9695821bfb7 100644 --- a/Mage.Sets/src/mage/sets/magic2010/Negate.java +++ b/Mage.Sets/src/mage/sets/magic2010/Negate.java @@ -40,7 +40,7 @@ import mage.target.TargetSpell; * * @author BetaSteward_at_googlemail.com */ -public class Negate extends CardImpl { +public class Negate extends CardImpl { private static FilterSpell filter = new FilterSpell("noncreature spell"); @@ -53,10 +53,22 @@ public class Negate extends CardImpl { super(ownerId, "Negate", new CardType[]{CardType.INSTANT}, "{1}{U}"); this.expansionSetId = Magic2010.getInstance().getId(); this.color.setBlue(true); - this.art = "109907_typ_reg_sty_010.jpg"; this.getSpellAbility().addTarget(new TargetSpell(filter)); this.getSpellAbility().addEffect(new CounterTargetEffect()); } + public Negate(final Negate card) { + super(card); + } + + @Override + public Negate copy() { + return new Negate(this); + } + + @Override + public String getArt() { + return "109907_typ_reg_sty_010.jpg"; + } } diff --git a/Mage.Sets/src/mage/sets/magic2010/Overrun.java b/Mage.Sets/src/mage/sets/magic2010/Overrun.java index f847d64041f..d869c5b871b 100644 --- a/Mage.Sets/src/mage/sets/magic2010/Overrun.java +++ b/Mage.Sets/src/mage/sets/magic2010/Overrun.java @@ -42,15 +42,28 @@ import mage.sets.Magic2010; * * @author LokiX */ -public class Overrun extends CardImpl { +public class Overrun extends CardImpl { public Overrun(UUID onwerId) { super(onwerId, "Overrun", new CardType[]{CardType.SORCERY},"{2}{G}{G}{G}"); this.expansionSetId = Magic2010.getInstance().getId(); this.color.setGreen(true); - this.art = "33280_typ_reg_sty_001.jpg"; this.getSpellAbility().addEffect(new BoostControlledEffect(3, 3, Duration.EndOfTurn)); this.getSpellAbility().addEffect(new GainAbilityControlledEffect(TrampleAbility.getInstance(), Duration.EndOfTurn, new FilterCreaturePermanent())); } + + public Overrun(final Overrun card) { + super(card); + } + + @Override + public Overrun copy() { + return new Overrun(this); + } + + @Override + public String getArt() { + return "33280_typ_reg_sty_001.jpg"; + } } diff --git a/Mage.Sets/src/mage/sets/magic2010/RampantGrowth.java b/Mage.Sets/src/mage/sets/magic2010/RampantGrowth.java index ef2f1d2a8c2..7eb8ef542f7 100644 --- a/Mage.Sets/src/mage/sets/magic2010/RampantGrowth.java +++ b/Mage.Sets/src/mage/sets/magic2010/RampantGrowth.java @@ -40,15 +40,28 @@ import mage.target.common.TargetCardInLibrary; * * @author LokiX */ -public class RampantGrowth extends CardImpl { +public class RampantGrowth extends CardImpl { public RampantGrowth(UUID ownerId){ super(ownerId, "Rampant Growth", new CardType[]{CardType.SORCERY}, "{1}{G}"); this.expansionSetId = Magic2010.getInstance().getId(); this.color.setGreen(true); - this.art = "101061_typ_reg_sty_010.jpg"; TargetCardInLibrary target = new TargetCardInLibrary(new FilterBasicLandCard()); this.getSpellAbility().addTarget(target); this.getSpellAbility().addEffect(new SearchLibraryPutInPlayEffect(target, true)); } + + public RampantGrowth(final RampantGrowth card) { + super(card); + } + + @Override + public RampantGrowth copy() { + return new RampantGrowth(this); + } + + @Override + public String getArt() { + return "101061_typ_reg_sty_010.jpg"; + } } diff --git a/Mage.Sets/src/mage/sets/magic2010/RootboundCrag.java b/Mage.Sets/src/mage/sets/magic2010/RootboundCrag.java index 8bc13007d58..d9dbdd67b76 100644 --- a/Mage.Sets/src/mage/sets/magic2010/RootboundCrag.java +++ b/Mage.Sets/src/mage/sets/magic2010/RootboundCrag.java @@ -43,7 +43,7 @@ import mage.sets.Magic2010; * * @author BetaSteward_at_googlemail.com */ -public class RootboundCrag extends CardImpl { +public class RootboundCrag extends CardImpl { private static FilterLandPermanent filter = new FilterLandPermanent(); @@ -57,9 +57,22 @@ public class RootboundCrag extends CardImpl { public RootboundCrag(UUID ownerId) { super(ownerId, "Rootbound Crag", new CardType[]{CardType.LAND}, null); this.expansionSetId = Magic2010.getInstance().getId(); - this.art = "121648_typ_reg_sty_010.jpg"; this.addAbility(new EntersBattlefieldStaticAbility(new EntersBattlefieldTappedUnlessControlsEffect(filter))); this.addAbility(new RedManaAbility()); this.addAbility(new GreenManaAbility()); } + + public RootboundCrag(final RootboundCrag card) { + super(card); + } + + @Override + public RootboundCrag copy() { + return new RootboundCrag(this); + } + + @Override + public String getArt() { + return "121648_typ_reg_sty_010.jpg"; + } } diff --git a/Mage.Sets/src/mage/sets/magic2010/RoyalAssassin.java b/Mage.Sets/src/mage/sets/magic2010/RoyalAssassin.java index 08047659b61..c4da00da4e0 100644 --- a/Mage.Sets/src/mage/sets/magic2010/RoyalAssassin.java +++ b/Mage.Sets/src/mage/sets/magic2010/RoyalAssassin.java @@ -46,7 +46,7 @@ import mage.target.common.TargetCreaturePermanent; * * @author LokiX */ -public class RoyalAssassin extends CardImpl { +public class RoyalAssassin extends CardImpl { public RoyalAssassin(UUID onwerId){ super(onwerId,"Royal Assassin", new CardType[]{CardType.CREATURE},"{1}{B}{B}"); @@ -54,15 +54,28 @@ public class RoyalAssassin extends CardImpl { this.color.setBlack(true); this.subtype.add("Human"); this.subtype.add("Assassin"); - this.art = "48786_typ_reg_sty_010.jpg"; this.power = new MageInt(1); this.toughness = new MageInt(1); this.addAbility(new RoyalAssassinAbility()); } + + public RoyalAssassin(final RoyalAssassin card) { + super(card); + } + + @Override + public RoyalAssassin copy() { + return new RoyalAssassin(this); + } + + @Override + public String getArt() { + return "48786_typ_reg_sty_010.jpg"; + } } -class RoyalAssassinAbility extends ActivatedAbilityImpl { +class RoyalAssassinAbility extends ActivatedAbilityImpl { private static FilterCreaturePermanent filter = new FilterCreaturePermanent("tapped creature"); @@ -78,4 +91,14 @@ class RoyalAssassinAbility extends ActivatedAbilityImpl { addCost(new TapSourceCost()); addEffect(new DestroyTargetEffect()); } + + public RoyalAssassinAbility(final RoyalAssassinAbility ability) { + super(ability); + } + + @Override + public RoyalAssassinAbility copy() { + return new RoyalAssassinAbility(this); + } + } diff --git a/Mage.Sets/src/mage/sets/magic2010/SafePassage.java b/Mage.Sets/src/mage/sets/magic2010/SafePassage.java index 13dad533116..90d1cb025d6 100644 --- a/Mage.Sets/src/mage/sets/magic2010/SafePassage.java +++ b/Mage.Sets/src/mage/sets/magic2010/SafePassage.java @@ -40,14 +40,27 @@ import mage.sets.Magic2010; * * @author BetaSteward_at_googlemail.com */ -public class SafePassage extends CardImpl { +public class SafePassage extends CardImpl { public SafePassage(UUID ownerId) { super(ownerId, "Safe Passage", new CardType[]{CardType.INSTANT}, "{2}{W}"); this.expansionSetId = Magic2010.getInstance().getId(); this.color.setWhite(true); - this.art = "121601_typ_reg_sty_010.jpg"; this.getSpellAbility().addEffect(new PreventAllDamageToEffect(Duration.EndOfTurn, new FilterCreatureOrPlayer("you and creatures you control", ownerId))); } + public SafePassage(final SafePassage card) { + super(card); + } + + @Override + public SafePassage copy() { + return new SafePassage(this); + } + + @Override + public String getArt() { + return "121601_typ_reg_sty_010.jpg"; + } + } diff --git a/Mage.Sets/src/mage/sets/magic2010/SunpetalGrove.java b/Mage.Sets/src/mage/sets/magic2010/SunpetalGrove.java index f7a4bd440d9..b3c6b3181e8 100644 --- a/Mage.Sets/src/mage/sets/magic2010/SunpetalGrove.java +++ b/Mage.Sets/src/mage/sets/magic2010/SunpetalGrove.java @@ -31,23 +31,19 @@ package mage.sets.magic2010; import java.util.UUID; import mage.Constants.CardType; import mage.abilities.common.EntersBattlefieldStaticAbility; -import mage.abilities.effects.common.EntersBattlefieldTappedEffect; import mage.abilities.effects.common.EntersBattlefieldTappedUnlessControlsEffect; import mage.abilities.mana.GreenManaAbility; import mage.abilities.mana.WhiteManaAbility; import mage.cards.CardImpl; import mage.filter.Filter.ComparisonScope; -import mage.filter.FilterPermanent; import mage.filter.common.FilterLandPermanent; -import mage.game.Game; -import mage.game.events.GameEvent; import mage.sets.Magic2010; /** * * @author BetaSteward_at_googlemail.com */ -public class SunpetalGrove extends CardImpl { +public class SunpetalGrove extends CardImpl { private static FilterLandPermanent filter = new FilterLandPermanent(); @@ -61,9 +57,22 @@ public class SunpetalGrove extends CardImpl { public SunpetalGrove(UUID ownerId) { super(ownerId, "Sunpetal Grove", new CardType[]{CardType.LAND}, null); this.expansionSetId = Magic2010.getInstance().getId(); - this.art = "121679_typ_reg_sty_010.jpg"; this.addAbility(new EntersBattlefieldStaticAbility(new EntersBattlefieldTappedUnlessControlsEffect(filter))); this.addAbility(new GreenManaAbility()); this.addAbility(new WhiteManaAbility()); } + + public SunpetalGrove(final SunpetalGrove card) { + super(card); + } + + @Override + public SunpetalGrove copy() { + return new SunpetalGrove(this); + } + + @Override + public String getArt() { + return "121679_typ_reg_sty_010.jpg"; + } } diff --git a/Mage.Sets/src/mage/sets/magic2010/TerramorphicExpanse.java b/Mage.Sets/src/mage/sets/magic2010/TerramorphicExpanse.java index 6f72b29ab5c..58e15b7d9d9 100644 --- a/Mage.Sets/src/mage/sets/magic2010/TerramorphicExpanse.java +++ b/Mage.Sets/src/mage/sets/magic2010/TerramorphicExpanse.java @@ -45,18 +45,32 @@ import mage.target.common.TargetCardInLibrary; * * @author BetaSteward_at_googlemail.com */ -public class TerramorphicExpanse extends CardImpl { +public class TerramorphicExpanse extends CardImpl { public TerramorphicExpanse(UUID ownerId) { super(ownerId, "Terramorphic Expanse", new CardType[]{CardType.LAND}, null); this.expansionSetId = Magic2010.getInstance().getId(); - this.art = "97502_typ_reg_sty_010.jpg"; +// this.art = "97502_typ_reg_sty_010.jpg"; this.addAbility(new TerramorphicExpanseAbility()); } + public TerramorphicExpanse(final TerramorphicExpanse card) { + super(card); + } + + @Override + public TerramorphicExpanse copy() { + return new TerramorphicExpanse(this); + } + + @Override + public String getArt() { + return "97502_typ_reg_sty_010.jpg"; + } + } -class TerramorphicExpanseAbility extends ActivatedAbilityImpl { +class TerramorphicExpanseAbility extends ActivatedAbilityImpl { public TerramorphicExpanseAbility() { super(Zone.BATTLEFIELD, null); @@ -66,4 +80,13 @@ class TerramorphicExpanseAbility extends ActivatedAbilityImpl { addEffect(new SearchLibraryPutInPlayEffect(target, true, Outcome.PutLandInPlay)); } + public TerramorphicExpanseAbility(final TerramorphicExpanseAbility ability) { + super(ability); + } + + @Override + public TerramorphicExpanseAbility copy() { + return new TerramorphicExpanseAbility(this); + } + } diff --git a/Mage.Sets/src/mage/sets/magic2010/TimeWarp.java b/Mage.Sets/src/mage/sets/magic2010/TimeWarp.java index 3846c6f77f1..982f9a35ec5 100644 --- a/Mage.Sets/src/mage/sets/magic2010/TimeWarp.java +++ b/Mage.Sets/src/mage/sets/magic2010/TimeWarp.java @@ -31,6 +31,7 @@ package mage.sets.magic2010; import java.util.UUID; import mage.Constants.CardType; import mage.Constants.Outcome; +import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.game.Game; @@ -42,33 +43,55 @@ import mage.target.TargetPlayer; * * @author BetaSteward_at_googlemail.com */ -public class TimeWarp extends CardImpl { +public class TimeWarp extends CardImpl { public TimeWarp(UUID ownerId) { super(ownerId, "Time Warp", new CardType[]{CardType.SORCERY}, "{3}{U}{U}"); this.expansionSetId = Magic2010.getInstance().getId(); this.color.setBlue(true); - this.art = "122160_typ_reg_sty_010.jpg"; this.getSpellAbility().addTarget(new TargetPlayer()); this.getSpellAbility().addEffect(new TimeWarpEffect()); } + public TimeWarp(final TimeWarp card) { + super(card); + } + + @Override + public TimeWarp copy() { + return new TimeWarp(this); + } + + @Override + public String getArt() { + return "122160_typ_reg_sty_010.jpg"; + } + } -class TimeWarpEffect extends OneShotEffect { +class TimeWarpEffect extends OneShotEffect { public TimeWarpEffect() { super(Outcome.ExtraTurn); } + public TimeWarpEffect(final TimeWarpEffect effect) { + super(effect); + } + @Override - public boolean apply(Game game) { + public TimeWarpEffect copy() { + return new TimeWarpEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { game.getState().getTurnMods().add(new TurnMod(source.getFirstTarget(), false)); return true; } @Override - public String getText() { + public String getText(Ability source) { return "Target player takes an extra turn after this one."; } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/magic2010/WhiteKnight.java b/Mage.Sets/src/mage/sets/magic2010/WhiteKnight.java index 17e9aa7040e..9626bbca128 100644 --- a/Mage.Sets/src/mage/sets/magic2010/WhiteKnight.java +++ b/Mage.Sets/src/mage/sets/magic2010/WhiteKnight.java @@ -41,7 +41,7 @@ import mage.sets.Magic2010; * * @author BetaSteward_at_googlemail.com */ -public class WhiteKnight extends CardImpl { +public class WhiteKnight extends CardImpl { public WhiteKnight(UUID ownerId) { super(ownerId, "White Knight", new CardType[]{CardType.CREATURE}, "{W}{W}"); @@ -49,7 +49,6 @@ public class WhiteKnight extends CardImpl { this.subtype.add("Human"); this.subtype.add("Knight"); this.color.setWhite(true); - this.art = "121715_typ_reg_sty_010.jpg"; this.power = new MageInt(2); this.toughness = new MageInt(2); @@ -60,5 +59,18 @@ public class WhiteKnight extends CardImpl { this.addAbility(new ProtectionAbility(filter)); } + public WhiteKnight(final WhiteKnight card) { + super(card); + } + + @Override + public WhiteKnight copy() { + return new WhiteKnight(this); + } + + @Override + public String getArt() { + return "121715_typ_reg_sty_010.jpg"; + } } diff --git a/Mage.Sets/src/mage/sets/magic2011/AcidicSlime.java b/Mage.Sets/src/mage/sets/magic2011/AcidicSlime.java new file mode 100644 index 00000000000..26000c0197d --- /dev/null +++ b/Mage.Sets/src/mage/sets/magic2011/AcidicSlime.java @@ -0,0 +1,45 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.sets.magic2011; + +import java.util.UUID; +import mage.sets.Magic2011; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class AcidicSlime extends mage.sets.magic2010.AcidicSlime { + + public AcidicSlime(UUID ownerId) { + super(ownerId); + this.expansionSetId = Magic2011.getInstance().getId(); + } + +} diff --git a/Mage.Sets/src/mage/sets/magic2011/ActOfTreason.java b/Mage.Sets/src/mage/sets/magic2011/ActOfTreason.java new file mode 100644 index 00000000000..b8491b2450c --- /dev/null +++ b/Mage.Sets/src/mage/sets/magic2011/ActOfTreason.java @@ -0,0 +1,45 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.sets.magic2011; + +import java.util.UUID; +import mage.sets.Magic2011; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class ActOfTreason extends mage.sets.magic2010.ActOfTreason { + + public ActOfTreason(UUID ownerId) { + super(ownerId); + this.expansionSetId = Magic2011.getInstance().getId(); + } + +} diff --git a/Mage.Sets/src/mage/sets/magic2011/AetherAdept.java b/Mage.Sets/src/mage/sets/magic2011/AetherAdept.java new file mode 100644 index 00000000000..ec559626aef --- /dev/null +++ b/Mage.Sets/src/mage/sets/magic2011/AetherAdept.java @@ -0,0 +1,75 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.sets.magic2011; + +import java.util.UUID; +import mage.Constants.CardType; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.cards.CardImpl; +import mage.sets.Magic2011; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class AetherAdept extends CardImpl { + + public AetherAdept(UUID ownerId) { + super(ownerId, "Æther Adept", new CardType[]{CardType.CREATURE}, "{1}{U}{U}"); + this.expansionSetId = Magic2011.getInstance().getId(); + this.subtype.add("Human"); + this.subtype.add("Wizard"); + this.color.setBlue(true); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + Ability ability = new EntersBattlefieldTriggeredAbility(new ReturnToHandTargetEffect()); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + } + + public AetherAdept(final AetherAdept card) { + super(card); + } + + @Override + public AetherAdept copy() { + return new AetherAdept(this); + } + + @Override + public String getArt() { + return ""; + } + +} diff --git a/Mage.Sets/src/mage/sets/magic2011/AirServant.java b/Mage.Sets/src/mage/sets/magic2011/AirServant.java new file mode 100644 index 00000000000..daadb8ac624 --- /dev/null +++ b/Mage.Sets/src/mage/sets/magic2011/AirServant.java @@ -0,0 +1,84 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.sets.magic2011; + +import java.util.UUID; +import mage.Constants.CardType; +import mage.Constants.Zone; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.TapTargetEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.filter.common.FilterCreaturePermanent; +import mage.sets.Magic2011; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class AirServant extends CardImpl { + + private static FilterCreaturePermanent filter = new FilterCreaturePermanent("creature with flying"); + + static { + filter.getAbilities().add(FlyingAbility.getInstance()); + } + + public AirServant(UUID ownerId) { + super(ownerId, "Air Servant", new CardType[]{CardType.CREATURE}, "{4}{U}"); + this.expansionSetId = Magic2011.getInstance().getId(); + this.color.setBlue(true); + this.subtype.add("Elemental"); + this.power = new MageInt(4); + this.toughness = new MageInt(3); + this.addAbility(FlyingAbility.getInstance()); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new TapTargetEffect(), new ManaCostsImpl("{2}{U}")); + ability.addTarget(new TargetCreaturePermanent(filter)); + this.addAbility(ability); + } + + public AirServant(final AirServant card) { + super(card); + } + + @Override + public AirServant copy() { + return new AirServant(this); + } + + @Override + public String getArt() { + return ""; + } + +} diff --git a/Mage.Sets/src/mage/sets/magic2011/AjaniGoldmane.java b/Mage.Sets/src/mage/sets/magic2011/AjaniGoldmane.java new file mode 100644 index 00000000000..d06a9c82e6b --- /dev/null +++ b/Mage.Sets/src/mage/sets/magic2011/AjaniGoldmane.java @@ -0,0 +1,45 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.sets.magic2011; + +import java.util.UUID; +import mage.sets.Magic2011; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class AjaniGoldmane extends mage.sets.magic2010.AjaniGoldmane { + + public AjaniGoldmane(UUID ownerId) { + super(ownerId); + this.expansionSetId = Magic2011.getInstance().getId(); + } + +} diff --git a/Mage.Sets/src/mage/sets/planechase/OblivionRing.java b/Mage.Sets/src/mage/sets/planechase/OblivionRing.java index 13435f4046b..801b007313c 100644 --- a/Mage.Sets/src/mage/sets/planechase/OblivionRing.java +++ b/Mage.Sets/src/mage/sets/planechase/OblivionRing.java @@ -46,13 +46,12 @@ import mage.target.TargetPermanent; * * @author BetaSteward_at_googlemail.com */ -public class OblivionRing extends CardImpl { +public class OblivionRing extends CardImpl { public OblivionRing(UUID ownerId) { super(ownerId, "Oblivion Ring", new CardType[]{CardType.ENCHANTMENT}, "{2}{W}"); this.expansionSetId = Planechase.getInstance().getId(); this.color.setWhite(true); - this.art = "115005_typ_reg_sty_010.jpg"; FilterNonlandPermanent filter = new FilterNonlandPermanent(); filter.setId(this.getId()); filter.setNotId(true); @@ -63,4 +62,18 @@ public class OblivionRing extends CardImpl { this.addAbility(ability2); } + public OblivionRing(final OblivionRing card) { + super(card); + } + + @Override + public OblivionRing copy() { + return new OblivionRing(this); + } + + @Override + public String getArt() { + return "115005_typ_reg_sty_010.jpg"; + } + } diff --git a/Mage.Sets/src/mage/sets/planechase/SoulWarden.java b/Mage.Sets/src/mage/sets/planechase/SoulWarden.java index 6472de80653..24449440c55 100644 --- a/Mage.Sets/src/mage/sets/planechase/SoulWarden.java +++ b/Mage.Sets/src/mage/sets/planechase/SoulWarden.java @@ -46,7 +46,7 @@ import mage.sets.Planechase; * * @author BetaSteward_at_googlemail.com */ -public class SoulWarden extends CardImpl { +public class SoulWarden extends CardImpl { public SoulWarden(UUID ownerId) { super(ownerId, "Soul Warden", new CardType[]{CardType.CREATURE}, "{W}"); @@ -54,20 +54,42 @@ public class SoulWarden extends CardImpl { this.subtype.add("Human"); this.subtype.add("Cleric"); this.color.setWhite(true); - this.art = "05621_typ_reg_sty_010.jpg"; this.power = new MageInt(1); this.toughness = new MageInt(1); this.addAbility(new SoulWardenAbility()); } + public SoulWarden(final SoulWarden card) { + super(card); + } + + @Override + public SoulWarden copy() { + return new SoulWarden(this); + } + + @Override + public String getArt() { + return "05621_typ_reg_sty_010.jpg"; + } + } -class SoulWardenAbility extends TriggeredAbilityImpl { +class SoulWardenAbility extends TriggeredAbilityImpl { public SoulWardenAbility() { super(Zone.BATTLEFIELD, new GainLifeEffect(1)); } + public SoulWardenAbility(final SoulWardenAbility ability) { + super(ability); + } + + @Override + public SoulWardenAbility copy() { + return new SoulWardenAbility(this); + } + @Override public boolean checkTrigger(GameEvent event, Game game) { if (event.getType() == EventType.ZONE_CHANGE && ((ZoneChangeEvent)event).getToZone() == Zone.BATTLEFIELD) { diff --git a/Mage.Sets/src/mage/sets/riseoftheeldrazi/Deprive.java b/Mage.Sets/src/mage/sets/riseoftheeldrazi/Deprive.java index b608ace0304..cf27aa2031b 100644 --- a/Mage.Sets/src/mage/sets/riseoftheeldrazi/Deprive.java +++ b/Mage.Sets/src/mage/sets/riseoftheeldrazi/Deprive.java @@ -42,17 +42,29 @@ import mage.target.common.TargetControlledPermanent; * * @author BetaSteward_at_googlemail.com */ -public class Deprive extends CardImpl { +public class Deprive extends CardImpl { public Deprive(UUID ownerId) { super(ownerId, "Deprive", new CardType[]{CardType.INSTANT}, "{U}{U}"); this.expansionSetId = RiseOfTheEldrazi.getInstance().getId(); this.color.setBlue(true); - this.art = "127324_typ_reg_sty_010.jpg"; this.getSpellAbility().addCost(new ReturnToHandTargetCost(new TargetControlledPermanent(1, 1, new FilterLandPermanent()))); this.getSpellAbility().addTarget(new TargetSpell()); this.getSpellAbility().addEffect(new CounterTargetEffect()); } + public Deprive(final Deprive card) { + super(card); + } + + @Override + public Deprive copy() { + return new Deprive(this); + } + + @Override + public String getArt() { + return "127324_typ_reg_sty_010.jpg"; + } } diff --git a/Mage.Sets/src/mage/sets/riseoftheeldrazi/EmrakulsHatcher.java b/Mage.Sets/src/mage/sets/riseoftheeldrazi/EmrakulsHatcher.java new file mode 100644 index 00000000000..041a870e50f --- /dev/null +++ b/Mage.Sets/src/mage/sets/riseoftheeldrazi/EmrakulsHatcher.java @@ -0,0 +1,72 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.sets.riseoftheeldrazi; + +import java.util.UUID; +import mage.Constants.CardType; +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.cards.CardImpl; +import mage.game.permanent.token.EldraziSpawnToken; +import mage.sets.RiseOfTheEldrazi; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class EmrakulsHatcher extends CardImpl { + + public EmrakulsHatcher(UUID ownerId) { + super(ownerId, "Emrakul's Hatcher", new CardType[]{CardType.CREATURE}, "{4}{R}"); + this.expansionSetId = RiseOfTheEldrazi.getInstance().getId(); + this.subtype.add("Eldrazi"); + this.subtype.add("Drone"); + this.color.setRed(true); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new EldraziSpawnToken(), 3), false)); + } + + public EmrakulsHatcher(final EmrakulsHatcher card) { + super(card); + } + + @Override + public EmrakulsHatcher copy() { + return new EmrakulsHatcher(this); + } + + @Override + public String getArt() { + return ""; + } + +} diff --git a/Mage.Sets/src/mage/sets/riseoftheeldrazi/EvolvingWilds.java b/Mage.Sets/src/mage/sets/riseoftheeldrazi/EvolvingWilds.java new file mode 100644 index 00000000000..938ae137710 --- /dev/null +++ b/Mage.Sets/src/mage/sets/riseoftheeldrazi/EvolvingWilds.java @@ -0,0 +1,91 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.sets.riseoftheeldrazi; + +import java.util.UUID; +import mage.Constants.CardType; +import mage.Constants.Outcome; +import mage.Constants.Zone; +import mage.abilities.ActivatedAbilityImpl; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.common.SearchLibraryPutInPlayEffect; +import mage.cards.CardImpl; +import mage.filter.common.FilterBasicLandCard; +import mage.sets.RiseOfTheEldrazi; +import mage.target.common.TargetCardInLibrary; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class EvolvingWilds extends CardImpl { + + public EvolvingWilds(UUID ownerId) { + super(ownerId, "Evolving Wilds", new CardType[]{CardType.LAND}, null); + this.expansionSetId = RiseOfTheEldrazi.getInstance().getId(); + this.addAbility(new EvolvingWildsAbility()); + } + + public EvolvingWilds(final EvolvingWilds card) { + super(card); + } + + @Override + public EvolvingWilds copy() { + return new EvolvingWilds(this); + } + + @Override + public String getArt() { + return ""; + } + +} + +class EvolvingWildsAbility extends ActivatedAbilityImpl { + + public EvolvingWildsAbility() { + super(Zone.BATTLEFIELD, null); + addCost(new TapSourceCost()); + addCost(new SacrificeSourceCost()); + TargetCardInLibrary target = new TargetCardInLibrary(new FilterBasicLandCard()); + addEffect(new SearchLibraryPutInPlayEffect(target, true, Outcome.PutLandInPlay)); + } + + public EvolvingWildsAbility(final EvolvingWildsAbility ability) { + super(ability); + } + + @Override + public EvolvingWildsAbility copy() { + return new EvolvingWildsAbility(this); + } + +} diff --git a/Mage.Sets/src/mage/sets/riseoftheeldrazi/FlameSlash.java b/Mage.Sets/src/mage/sets/riseoftheeldrazi/FlameSlash.java new file mode 100644 index 00000000000..95b8a9a60e4 --- /dev/null +++ b/Mage.Sets/src/mage/sets/riseoftheeldrazi/FlameSlash.java @@ -0,0 +1,66 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.sets.riseoftheeldrazi; + +import java.util.UUID; +import mage.Constants.CardType; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.cards.CardImpl; +import mage.sets.RiseOfTheEldrazi; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class FlameSlash extends CardImpl { + + public FlameSlash(UUID ownerId) { + super(ownerId, "Flame Slash", new CardType[]{CardType.SORCERY}, "{R}"); + this.expansionSetId = RiseOfTheEldrazi.getInstance().getId(); + this.color.setRed(true); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + this.getSpellAbility().addEffect(new DamageTargetEffect(4)); + } + + public FlameSlash(final FlameSlash card) { + super(card); + } + + @Override + public FlameSlash copy() { + return new FlameSlash(this); + } + + @Override + public String getArt() { + return ""; + } + +} diff --git a/Mage.Sets/src/mage/sets/riseoftheeldrazi/GideonJura.java b/Mage.Sets/src/mage/sets/riseoftheeldrazi/GideonJura.java index 96bfecfa455..dc1cb7f3fed 100644 --- a/Mage.Sets/src/mage/sets/riseoftheeldrazi/GideonJura.java +++ b/Mage.Sets/src/mage/sets/riseoftheeldrazi/GideonJura.java @@ -33,6 +33,7 @@ import mage.Constants.CardType; import mage.Constants.Duration; import mage.Constants.TargetController; import mage.MageInt; +import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.effects.RequirementAttackEffect; import mage.abilities.effects.common.BecomesCreatureSourceEOTEffect; @@ -55,7 +56,7 @@ import mage.target.common.TargetOpponent; * * @author BetaSteward_at_googlemail.com */ -public class GideonJura extends CardImpl { +public class GideonJura extends CardImpl { private static FilterCreaturePermanent filter = new FilterCreaturePermanent("tapped creature"); @@ -68,7 +69,6 @@ public class GideonJura extends CardImpl { this.expansionSetId = RiseOfTheEldrazi.getInstance().getId(); this.subtype.add("Gideon"); this.color.setWhite(true); - this.art = "127248_typ_reg_sty_010.jpg"; this.loyalty = new MageInt(6); LoyaltyAbility ability1 = new LoyaltyAbility(new GideonJuraEffect(), 2); @@ -84,6 +84,20 @@ public class GideonJura extends CardImpl { this.addAbility(ability3); } + public GideonJura(final GideonJura card) { + super(card); + } + + @Override + public GideonJura copy() { + return new GideonJura(this); + } + + @Override + public String getArt() { + return "127248_typ_reg_sty_010.jpg"; + } + } class GideonJuraToken extends Token { @@ -99,28 +113,37 @@ class GideonJuraToken extends Token { } -class GideonJuraEffect extends RequirementAttackEffect { +class GideonJuraEffect extends RequirementAttackEffect { public GideonJuraEffect() { super(Duration.OneUse); } + public GideonJuraEffect(final GideonJuraEffect effect) { + super(effect); + } + @Override - public boolean applies(GameEvent event, Game game) { - if (event.getType().equals(EventType.DECLARE_ATTACKERS_STEP_PRE) && event.getPlayerId().equals(this.source.getFirstTarget())) + public GideonJuraEffect copy() { + return new GideonJuraEffect(this); + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + if (event.getType().equals(EventType.DECLARE_ATTACKERS_STEP_PRE) && event.getPlayerId().equals(source.getFirstTarget())) return true; - if (event.getType().equals(EventType.END_PHASE_POST) && event.getPlayerId().equals(this.source.getFirstTarget())) + if (event.getType().equals(EventType.END_PHASE_POST) && event.getPlayerId().equals(source.getFirstTarget())) used = true; return false; } @Override - public boolean apply(Game game) { - Player player = game.getPlayer(this.source.getFirstTarget()); + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getFirstTarget()); if (player != null) { for (Permanent creature: game.getBattlefield().getAllActivePermanents(new FilterCreatureForCombat(), player.getId())) { if (creature.canAttack(game)) { - game.getCombat().declareAttacker(creature.getId(), this.source.getControllerId(), game); + game.getCombat().declareAttacker(creature.getId(), source.getControllerId(), game); } } return true; @@ -129,7 +152,7 @@ class GideonJuraEffect extends RequirementAttackEffect { } @Override - public String getText() { + public String getText(Ability source) { return "During target opponent's next turn, creatures that player controls attack {this} if able"; } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/riseoftheeldrazi/JoragaTreespeaker.java b/Mage.Sets/src/mage/sets/riseoftheeldrazi/JoragaTreespeaker.java new file mode 100644 index 00000000000..37cd1b6c7a5 --- /dev/null +++ b/Mage.Sets/src/mage/sets/riseoftheeldrazi/JoragaTreespeaker.java @@ -0,0 +1,115 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.sets.riseoftheeldrazi; + +import java.util.UUID; +import mage.Constants.CardType; +import mage.Constants.Duration; +import mage.Constants.Zone; +import mage.MageInt; +import mage.Mana; +import mage.abilities.Abilities; +import mage.abilities.AbilitiesImpl; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.GainAbilityControlledEffect; +import mage.abilities.effects.common.ManaEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.LevelAbility; +import mage.abilities.keyword.LevelUpAbility; +import mage.abilities.mana.BasicManaAbility; +import mage.cards.LevelerCard; +import mage.filter.common.FilterCreaturePermanent; +import mage.sets.RiseOfTheEldrazi; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class JoragaTreespeaker extends LevelerCard { + + private static FilterCreaturePermanent filter = new FilterCreaturePermanent("Elves"); + + static { + filter.getSubtype().add("Elf"); + } + + public JoragaTreespeaker(UUID ownerId) { + super(ownerId, "Joraga Treespeaker", new CardType[]{CardType.CREATURE}, "{G}"); + this.expansionSetId = RiseOfTheEldrazi.getInstance().getId(); + this.subtype.add("Elf"); + this.subtype.add("Druid"); + this.color.setGreen(true); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + this.addAbility(new LevelUpAbility(new ManaCostsImpl("{1}{G}"))); + Abilities abilities1 = new AbilitiesImpl(); + abilities1.add(new JoragaManaAbility()); + this.getLevels().add(new LevelAbility(1, 4, abilities1, 1, 2)); + Abilities abilities2 = new AbilitiesImpl(); + abilities2.add(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityControlledEffect(new JoragaManaAbility(), Duration.WhileOnBattlefield, filter))); + this.getLevels().add(new LevelAbility(5, -1, abilities2, 1, 4)); + } + + public JoragaTreespeaker(final JoragaTreespeaker card) { + super(card); + } + + @Override + public JoragaTreespeaker copy() { + return new JoragaTreespeaker(this); + } + + @Override + public String getArt() { + return "127280_typ_reg_sty_010.jpg"; + } + +} + +class JoragaManaAbility extends BasicManaAbility { + + public JoragaManaAbility() { + super(new ManaEffect(Mana.GreenMana(2))); + this.netMana.setGreen(2); + } + + public JoragaManaAbility(JoragaManaAbility ability) { + super(ability); + } + + @Override + public JoragaManaAbility copy() { + return new JoragaManaAbility(this); + } + +} + diff --git a/Mage.Sets/src/mage/sets/riseoftheeldrazi/KarganDragonlord.java b/Mage.Sets/src/mage/sets/riseoftheeldrazi/KarganDragonlord.java index 0f2b390370e..d11f028b4da 100644 --- a/Mage.Sets/src/mage/sets/riseoftheeldrazi/KarganDragonlord.java +++ b/Mage.Sets/src/mage/sets/riseoftheeldrazi/KarganDragonlord.java @@ -35,8 +35,9 @@ import mage.Constants.Zone; import mage.MageInt; import mage.abilities.Abilities; import mage.abilities.AbilitiesImpl; +import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.costs.mana.ManaCosts; +import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.BoostSourceEffect; import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.LevelAbility; @@ -49,7 +50,7 @@ import mage.sets.RiseOfTheEldrazi; * * @author BetaSteward_at_googlemail.com */ -public class KarganDragonlord extends LevelerCard { +public class KarganDragonlord extends LevelerCard { public KarganDragonlord(UUID ownerId) { super(ownerId, "Kargan Dragonlord", new CardType[]{CardType.CREATURE}, "{R}{R}"); @@ -57,20 +58,32 @@ public class KarganDragonlord extends LevelerCard { this.subtype.add("Human"); this.subtype.add("Warrior"); this.color.setRed(true); - this.art = "127295_typ_reg_sty_010.jpg"; this.power = new MageInt(2); this.toughness = new MageInt(2); - this.addAbility(new LevelUpAbility(new ManaCosts("{R}"))); - Abilities abilities1 = new AbilitiesImpl(); + this.addAbility(new LevelUpAbility(new ManaCostsImpl("{R}"))); + Abilities abilities1 = new AbilitiesImpl(); abilities1.add(FlyingAbility.getInstance()); this.getLevels().add(new LevelAbility(4, 7, abilities1, 4, 4)); - Abilities abilities2 = new AbilitiesImpl(); + Abilities abilities2 = new AbilitiesImpl(); abilities2.add(FlyingAbility.getInstance()); abilities2.add(TrampleAbility.getInstance()); - abilities2.add(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(1, 0, Duration.EndOfTurn), new ManaCosts("{R}"))); + abilities2.add(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(1, 0, Duration.EndOfTurn), new ManaCostsImpl("{R}"))); this.getLevels().add(new LevelAbility(8, -1, abilities2, 8, 8)); } + public KarganDragonlord(final KarganDragonlord card) { + super(card); + } + + @Override + public KarganDragonlord copy() { + return new KarganDragonlord(this); + } + + @Override + public String getArt() { + return "127295_typ_reg_sty_010.jpg"; + } } diff --git a/Mage.Sets/src/mage/sets/riseoftheeldrazi/KozileksPredator.java b/Mage.Sets/src/mage/sets/riseoftheeldrazi/KozileksPredator.java new file mode 100644 index 00000000000..07b7d9e185f --- /dev/null +++ b/Mage.Sets/src/mage/sets/riseoftheeldrazi/KozileksPredator.java @@ -0,0 +1,72 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.sets.riseoftheeldrazi; + +import java.util.UUID; +import mage.Constants.CardType; +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.cards.CardImpl; +import mage.game.permanent.token.EldraziSpawnToken; +import mage.sets.RiseOfTheEldrazi; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class KozileksPredator extends CardImpl { + + public KozileksPredator(UUID ownerId) { + super(ownerId, "Kozilek's Predator", new CardType[]{CardType.CREATURE}, "{3}{G}"); + this.expansionSetId = RiseOfTheEldrazi.getInstance().getId(); + this.subtype.add("Eldrazi"); + this.subtype.add("Drone"); + this.color.setGreen(true); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new EldraziSpawnToken(), 2), false)); + } + + public KozileksPredator(final KozileksPredator card) { + super(card); + } + + @Override + public KozileksPredator copy() { + return new KozileksPredator(this); + } + + @Override + public String getArt() { + return "127475_typ_reg_sty_010.jpg"; + } + +} diff --git a/Mage.Sets/src/mage/sets/riseoftheeldrazi/NestInvader.java b/Mage.Sets/src/mage/sets/riseoftheeldrazi/NestInvader.java new file mode 100644 index 00000000000..d008d4dcec5 --- /dev/null +++ b/Mage.Sets/src/mage/sets/riseoftheeldrazi/NestInvader.java @@ -0,0 +1,72 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.sets.riseoftheeldrazi; + +import java.util.UUID; +import mage.Constants.CardType; +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.cards.CardImpl; +import mage.game.permanent.token.EldraziSpawnToken; +import mage.sets.RiseOfTheEldrazi; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class NestInvader extends CardImpl { + + public NestInvader(UUID ownerId) { + super(ownerId, "Nest Invader", new CardType[]{CardType.CREATURE}, "{1}{G}"); + this.expansionSetId = RiseOfTheEldrazi.getInstance().getId(); + this.subtype.add("Eldrazi"); + this.subtype.add("Drone"); + this.color.setGreen(true); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new EldraziSpawnToken()), false)); + } + + public NestInvader(final NestInvader card) { + super(card); + } + + @Override + public NestInvader copy() { + return new NestInvader(this); + } + + @Override + public String getArt() { + return "127252_typ_reg_sty_010.jpg"; + } + +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/riseoftheeldrazi/Vengevine.java b/Mage.Sets/src/mage/sets/riseoftheeldrazi/Vengevine.java new file mode 100644 index 00000000000..cab08080165 --- /dev/null +++ b/Mage.Sets/src/mage/sets/riseoftheeldrazi/Vengevine.java @@ -0,0 +1,152 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.sets.riseoftheeldrazi; + +import java.util.UUID; +import mage.Constants.CardType; +import mage.Constants.Zone; +import mage.MageInt; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.common.ReturnSourceFromGraveyardToBattlefieldEffect; +import mage.abilities.keyword.HasteAbility; +import mage.cards.CardImpl; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; +import mage.game.stack.Spell; +import mage.sets.RiseOfTheEldrazi; +import mage.watchers.Watcher; +import mage.watchers.WatcherImpl; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class Vengevine extends CardImpl { + + public Vengevine(UUID ownerId) { + super(ownerId, "Vengevine", new CardType[]{CardType.CREATURE}, "{2}{G}{G}"); + this.expansionSetId = RiseOfTheEldrazi.getInstance().getId(); + this.subtype.add("Elemental"); + this.color.setGreen(true); + this.power = new MageInt(4); + this.toughness = new MageInt(3); + + this.addAbility(HasteAbility.getInstance()); + this.addAbility(new VengevineAbility()); + this.watchers.add(new VengevineWatcher(ownerId)); + } + + public Vengevine(final Vengevine card) { + super(card); + } + + @Override + public Vengevine copy() { + return new Vengevine(this); + } + + @Override + public String getArt() { + return "127350_typ_reg_sty_010.jpg"; + } + +} + +class VengevineAbility extends TriggeredAbilityImpl { + + public VengevineAbility() { + super(Zone.GRAVEYARD, new ReturnSourceFromGraveyardToBattlefieldEffect(), true); + } + + public VengevineAbility(final VengevineAbility ability) { + super(ability); + } + + @Override + public VengevineAbility copy() { + return new VengevineAbility(this); + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (event.getType() == EventType.SPELL_CAST && event.getPlayerId().equals(controllerId)) { + Watcher watcher = game.getState().getWatchers().get(controllerId, "CreatureCast"); + if (watcher != null && watcher.conditionMet()) { + trigger(game, event.getPlayerId()); + return true; + } + } + return false; + } + + @Override + public String getRule() { + return "Whenever you cast a spell, if it's the second creature spell you cast this turn, you may return Vengevine from your graveyard to the battlefield."; + } + +} + + +class VengevineWatcher extends WatcherImpl { + + int creatureSpellCount = 0; + + public VengevineWatcher(UUID controllerId) { + super("CreatureCast", controllerId); + } + + public VengevineWatcher(final VengevineWatcher watcher) { + super(watcher); + } + + @Override + public VengevineWatcher copy() { + return new VengevineWatcher(this); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == EventType.SPELL_CAST && event.getPlayerId().equals(controllerId)) { + Spell spell = (Spell)game.getStack().getStackObject(event.getTargetId()); + if (spell.getCardType().contains(CardType.CREATURE)) { + creatureSpellCount++; + if (creatureSpellCount == 2) + condition = true; + } + } + } + + @Override + public void reset() { + super.reset(); + creatureSpellCount = 0; + } + +} diff --git a/Mage.Sets/src/mage/sets/riseoftheeldrazi/WallOfOmens.java b/Mage.Sets/src/mage/sets/riseoftheeldrazi/WallOfOmens.java index ad7cff1b52a..e6d3bfc6454 100644 --- a/Mage.Sets/src/mage/sets/riseoftheeldrazi/WallOfOmens.java +++ b/Mage.Sets/src/mage/sets/riseoftheeldrazi/WallOfOmens.java @@ -41,14 +41,13 @@ import mage.sets.RiseOfTheEldrazi; * * @author BetaSteward_at_googlemail.com */ -public class WallOfOmens extends CardImpl { +public class WallOfOmens extends CardImpl { public WallOfOmens(UUID ownerId) { super(ownerId, "Wall Of Omens", new CardType[]{CardType.CREATURE}, "{1}{W}"); this.expansionSetId = RiseOfTheEldrazi.getInstance().getId(); this.subtype.add("Wall"); this.color.setWhite(true); - this.art = "127342_typ_reg_sty_010.jpg"; this.power = new MageInt(0); this.toughness = new MageInt(4); @@ -56,4 +55,18 @@ public class WallOfOmens extends CardImpl { this.addAbility(new EntersBattlefieldTriggeredAbility(new DrawCardControllerEffect(1), false)); } + public WallOfOmens(final WallOfOmens card) { + super(card); + } + + @Override + public WallOfOmens copy() { + return new WallOfOmens(this); + } + + @Override + public String getArt() { + return "127342_typ_reg_sty_010.jpg"; + } + } diff --git a/Mage.Sets/src/mage/sets/shardsofalara/AjaniVengeant.java b/Mage.Sets/src/mage/sets/shardsofalara/AjaniVengeant.java index a39f662b43d..80988f90e5f 100644 --- a/Mage.Sets/src/mage/sets/shardsofalara/AjaniVengeant.java +++ b/Mage.Sets/src/mage/sets/shardsofalara/AjaniVengeant.java @@ -48,7 +48,7 @@ import mage.target.common.TargetCreatureOrPlayer; * * @author BetaSteward_at_googlemail.com */ -public class AjaniVengeant extends CardImpl { +public class AjaniVengeant extends CardImpl { public AjaniVengeant(UUID ownerId) { super(ownerId, "Ajani Vengeant", new CardType[]{CardType.PLANESWALKER}, "{2}{R}{W}"); @@ -56,14 +56,13 @@ public class AjaniVengeant extends CardImpl { this.subtype.add("Ajani"); this.color.setRed(true); this.color.setWhite(true); - this.art = "114969_typ_reg_sty_010.jpg"; this.loyalty = new MageInt(3); LoyaltyAbility ability1 = new LoyaltyAbility(new SkipNextUntapTargetEffect(), 1); ability1.addTarget(new TargetPermanent()); this.addAbility(ability1); - Effects effects1 = new Effects(null); + Effects effects1 = new Effects(); effects1.add(new DamageTargetEffect(3)); effects1.add(new GainLifeEffect(3)); LoyaltyAbility ability2 = new LoyaltyAbility(effects1, -2); @@ -78,4 +77,18 @@ public class AjaniVengeant extends CardImpl { } + + public AjaniVengeant(final AjaniVengeant card) { + super(card); + } + + @Override + public AjaniVengeant copy() { + return new AjaniVengeant(this); + } + + @Override + public String getArt() { + return "114969_typ_reg_sty_010.jpg"; + } } diff --git a/Mage.Sets/src/mage/sets/shardsofalara/Angelsong.java b/Mage.Sets/src/mage/sets/shardsofalara/Angelsong.java index bc320aa1d8d..5a109ee5923 100644 --- a/Mage.Sets/src/mage/sets/shardsofalara/Angelsong.java +++ b/Mage.Sets/src/mage/sets/shardsofalara/Angelsong.java @@ -31,7 +31,7 @@ package mage.sets.shardsofalara; import java.util.UUID; import mage.Constants.CardType; import mage.Constants.Duration; -import mage.abilities.costs.mana.ManaCosts; +import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.PreventAllCombatDamageEffect; import mage.abilities.keyword.CyclingAbility; import mage.cards.CardImpl; @@ -41,15 +41,28 @@ import mage.sets.ShardsOfAlara; * * @author BetaSteward_at_googlemail.com */ -public class Angelsong extends CardImpl { +public class Angelsong extends CardImpl { public Angelsong(UUID ownerId) { super(ownerId, "Angelsong", new CardType[]{CardType.INSTANT}, "{1}{W}"); this.expansionSetId = ShardsOfAlara.getInstance().getId(); this.color.setWhite(true); - this.art = "115068_typ_reg_sty_010.jpg"; this.getSpellAbility().addEffect(new PreventAllCombatDamageEffect(Duration.EndOfTurn)); - this.addAbility(new CyclingAbility(new ManaCosts("{2}"))); + this.addAbility(new CyclingAbility(new ManaCostsImpl("{2}"))); + } + + public Angelsong(final Angelsong card) { + super(card); + } + + @Override + public Angelsong copy() { + return new Angelsong(this); + } + + @Override + public String getArt() { + return "115068_typ_reg_sty_010.jpg"; } } diff --git a/Mage.Sets/src/mage/sets/shardsofalara/Blightning.java b/Mage.Sets/src/mage/sets/shardsofalara/Blightning.java index 6c82e6a49da..d6067ee382f 100644 --- a/Mage.Sets/src/mage/sets/shardsofalara/Blightning.java +++ b/Mage.Sets/src/mage/sets/shardsofalara/Blightning.java @@ -40,16 +40,29 @@ import mage.target.TargetPlayer; * * @author BetaSteward_at_googlemail.com */ -public class Blightning extends CardImpl { +public class Blightning extends CardImpl { public Blightning(UUID ownerId) { super(ownerId, "Blightning", new CardType[]{CardType.SORCERY}, "{1}{B}{R}"); this.expansionSetId = ShardsOfAlara.getInstance().getId(); this.color.setBlack(true); this.color.setRed(true); - this.art = "115010_typ_reg_sty_010.jpg"; this.getSpellAbility().addTarget(new TargetPlayer()); this.getSpellAbility().addEffect(new DamageTargetEffect(3)); this.getSpellAbility().addEffect(new DiscardTargetEffect(2)); } + + public Blightning(final Blightning card) { + super(card); + } + + @Override + public Blightning copy() { + return new Blightning(this); + } + + @Override + public String getArt() { + return "115010_typ_reg_sty_010.jpg"; + } } diff --git a/Mage.Sets/src/mage/sets/shardsofalara/BroodmateDragon.java b/Mage.Sets/src/mage/sets/shardsofalara/BroodmateDragon.java index be90a1962fc..03be8858fc2 100644 --- a/Mage.Sets/src/mage/sets/shardsofalara/BroodmateDragon.java +++ b/Mage.Sets/src/mage/sets/shardsofalara/BroodmateDragon.java @@ -42,7 +42,7 @@ import mage.sets.ShardsOfAlara; * * @author BetaSteward_at_googlemail.com */ -public class BroodmateDragon extends CardImpl { +public class BroodmateDragon extends CardImpl { private static DragonToken dragonToken = new DragonToken(); @@ -53,7 +53,6 @@ public class BroodmateDragon extends CardImpl { this.color.setRed(true); this.color.setGreen(true); this.color.setBlack(true); - this.art = "116186_typ_reg_sty_010.jpg"; this.power = new MageInt(4); this.toughness = new MageInt(4); @@ -61,5 +60,19 @@ public class BroodmateDragon extends CardImpl { this.addAbility(FlyingAbility.getInstance()); } + public BroodmateDragon(final BroodmateDragon card) { + super(card); + } + + @Override + public BroodmateDragon copy() { + return new BroodmateDragon(this); + } + + @Override + public String getArt() { + return "116186_typ_reg_sty_010.jpg"; + } + } diff --git a/Mage.Sets/src/mage/sets/shardsofalara/CrumblingNecropolis.java b/Mage.Sets/src/mage/sets/shardsofalara/CrumblingNecropolis.java index e76873a2db5..3683024c749 100644 --- a/Mage.Sets/src/mage/sets/shardsofalara/CrumblingNecropolis.java +++ b/Mage.Sets/src/mage/sets/shardsofalara/CrumblingNecropolis.java @@ -42,16 +42,29 @@ import mage.sets.ShardsOfAlara; * * @author BetaSteward_at_googlemail.com */ -public class CrumblingNecropolis extends CardImpl { +public class CrumblingNecropolis extends CardImpl { public CrumblingNecropolis(UUID ownerId) { super(ownerId, "Crumbling Necropolis", new CardType[]{CardType.LAND}, null); this.expansionSetId = ShardsOfAlara.getInstance().getId(); - this.art = "115145_typ_reg_sty_010.jpg"; this.addAbility(new EntersBattlefieldStaticAbility(new EntersBattlefieldTappedEffect())); this.addAbility(new RedManaAbility()); this.addAbility(new BlueManaAbility()); this.addAbility(new BlackManaAbility()); } + public CrumblingNecropolis(final CrumblingNecropolis card) { + super(card); + } + + @Override + public CrumblingNecropolis copy() { + return new CrumblingNecropolis(this); + } + + @Override + public String getArt() { + return "115145_typ_reg_sty_010.jpg"; + } + } diff --git a/Mage.Sets/src/mage/sets/shardsofalara/ElspethKnightErrant.java b/Mage.Sets/src/mage/sets/shardsofalara/ElspethKnightErrant.java index 83639e573e3..436855b59f1 100644 --- a/Mage.Sets/src/mage/sets/shardsofalara/ElspethKnightErrant.java +++ b/Mage.Sets/src/mage/sets/shardsofalara/ElspethKnightErrant.java @@ -39,7 +39,7 @@ import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.GainAbilityControlledEffect; import mage.abilities.effects.common.GainAbilityTargetEffect; import mage.abilities.keyword.FlyingAbility; -import mage.abilities.keyword.IndestructableAbility; +import mage.abilities.keyword.IndestructibleAbility; import mage.cards.CardImpl; import mage.filter.FilterPermanent; import mage.game.permanent.token.SoldierToken; @@ -50,7 +50,7 @@ import mage.target.common.TargetCreaturePermanent; * * @author BetaSteward_at_googlemail.com */ -public class ElspethKnightErrant extends CardImpl { +public class ElspethKnightErrant extends CardImpl { private static SoldierToken soldierToken = new SoldierToken(); @@ -59,12 +59,11 @@ public class ElspethKnightErrant extends CardImpl { this.expansionSetId = ShardsOfAlara.getInstance().getId(); this.subtype.add("Elspeth"); this.color.setWhite(true); - this.art = "114973_typ_reg_sty_010.jpg"; this.loyalty = new MageInt(4); this.addAbility(new LoyaltyAbility(new CreateTokenEffect(soldierToken), 1)); - Effects effects1 = new Effects(null); + Effects effects1 = new Effects(); effects1.add(new BoostTargetEffect(3, 3, Duration.EndOfTurn)); effects1.add(new GainAbilityTargetEffect(FlyingAbility.getInstance(), Duration.EndOfTurn)); LoyaltyAbility ability1 = new LoyaltyAbility(effects1, 1); @@ -76,8 +75,22 @@ public class ElspethKnightErrant extends CardImpl { filter.getCardType().add(CardType.CREATURE); filter.getCardType().add(CardType.ENCHANTMENT); filter.getCardType().add(CardType.LAND); - this.addAbility(new LoyaltyAbility(new GainAbilityControlledEffect(IndestructableAbility.getInstance(), Duration.EndOfGame, filter), -8)); + this.addAbility(new LoyaltyAbility(new GainAbilityControlledEffect(IndestructibleAbility.getInstance(), Duration.EndOfGame, filter), -8)); } + public ElspethKnightErrant(final ElspethKnightErrant card) { + super(card); + } + + @Override + public ElspethKnightErrant copy() { + return new ElspethKnightErrant(this); + } + + @Override + public String getArt() { + return "114973_typ_reg_sty_010.jpg"; + } + } diff --git a/Mage.Sets/src/mage/sets/shardsofalara/HellsThunder.java b/Mage.Sets/src/mage/sets/shardsofalara/HellsThunder.java index 2039b3582be..a95f205f541 100644 --- a/Mage.Sets/src/mage/sets/shardsofalara/HellsThunder.java +++ b/Mage.Sets/src/mage/sets/shardsofalara/HellsThunder.java @@ -32,7 +32,7 @@ import java.util.UUID; import mage.Constants.CardType; import mage.MageInt; import mage.abilities.common.OnEventTriggeredAbility; -import mage.abilities.costs.mana.ManaCosts; +import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.SacrificeSourceEffect; import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.HasteAbility; @@ -45,22 +45,36 @@ import mage.sets.ShardsOfAlara; * * @author BetaSteward_at_googlemail.com */ -public class HellsThunder extends CardImpl { +public class HellsThunder extends CardImpl { public HellsThunder(UUID ownerId) { super(ownerId, "Hell's Thunder", new CardType[]{CardType.CREATURE}, "{1}{R}{R}"); this.expansionSetId = ShardsOfAlara.getInstance().getId(); this.subtype.add("Elemental"); this.color.setRed(true); - this.art = "115234_typ_reg_sty_010.jpg"; +// this.art = "115234_typ_reg_sty_010.jpg"; this.power = new MageInt(4); this.toughness = new MageInt(4); this.addAbility(FlyingAbility.getInstance()); this.addAbility(HasteAbility.getInstance()); this.addAbility(new OnEventTriggeredAbility(EventType.END_TURN_STEP_PRE, "beginning of the end step", new SacrificeSourceEffect())); - this.addAbility(new UnearthAbility(new ManaCosts("{4}{R}"))); + this.addAbility(new UnearthAbility(new ManaCostsImpl("{4}{R}"))); } + public HellsThunder(final HellsThunder card) { + super(card); + } + + @Override + public HellsThunder copy() { + return new HellsThunder(this); + } + + @Override + public String getArt() { + return "115234_typ_reg_sty_010.jpg"; + } + } diff --git a/Mage.Sets/src/mage/sets/shardsofalara/JundPanorama.java b/Mage.Sets/src/mage/sets/shardsofalara/JundPanorama.java index eddacb1b752..cd84c664f99 100644 --- a/Mage.Sets/src/mage/sets/shardsofalara/JundPanorama.java +++ b/Mage.Sets/src/mage/sets/shardsofalara/JundPanorama.java @@ -47,19 +47,32 @@ import mage.target.common.TargetCardInLibrary; * * @author BetaSteward_at_googlemail.com */ -public class JundPanorama extends CardImpl { +public class JundPanorama extends CardImpl { public JundPanorama(UUID ownerId) { super(ownerId, "Jund Panorama", new CardType[]{CardType.LAND}, null); this.expansionSetId = ShardsOfAlara.getInstance().getId(); - this.art = "116191_typ_reg_sty_010.jpg"; this.addAbility(new ColorlessManaAbility()); this.addAbility(new JundPanoramaAbility()); } + public JundPanorama(final JundPanorama card) { + super(card); + } + + @Override + public JundPanorama copy() { + return new JundPanorama(this); + } + + @Override + public String getArt() { + return "116191_typ_reg_sty_010.jpg"; + } + } -class JundPanoramaAbility extends ActivatedAbilityImpl { +class JundPanoramaAbility extends ActivatedAbilityImpl { public JundPanoramaAbility() { super(Zone.BATTLEFIELD, null); @@ -73,5 +86,15 @@ class JundPanoramaAbility extends ActivatedAbilityImpl { TargetCardInLibrary target = new TargetCardInLibrary(filter); addEffect(new SearchLibraryPutInPlayEffect(target, true, Outcome.PutLandInPlay)); } + + public JundPanoramaAbility(final JundPanoramaAbility ability) { + super(ability); + } + + @Override + public JundPanoramaAbility copy() { + return new JundPanoramaAbility(this); + } + } \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/shardsofalara/KnightOfTheWhiteOrchid.java b/Mage.Sets/src/mage/sets/shardsofalara/KnightOfTheWhiteOrchid.java index c248ade28ab..35b6ad4b899 100644 --- a/Mage.Sets/src/mage/sets/shardsofalara/KnightOfTheWhiteOrchid.java +++ b/Mage.Sets/src/mage/sets/shardsofalara/KnightOfTheWhiteOrchid.java @@ -50,7 +50,7 @@ import mage.target.common.TargetCardInLibrary; * * @author BetaSteward_at_googlemail.com */ -public class KnightOfTheWhiteOrchid extends CardImpl { +public class KnightOfTheWhiteOrchid extends CardImpl { public KnightOfTheWhiteOrchid(UUID ownerId) { super(ownerId, "Knight of the White Orchid", new CardType[]{CardType.CREATURE}, "{W}{W}"); @@ -58,7 +58,6 @@ public class KnightOfTheWhiteOrchid extends CardImpl { this.subtype.add("Human"); this.subtype.add("Knight"); this.color.setWhite(true); - this.art = "116188_typ_reg_sty_010.jpg"; this.power = new MageInt(2); this.toughness = new MageInt(2); @@ -66,9 +65,23 @@ public class KnightOfTheWhiteOrchid extends CardImpl { this.addAbility(FirstStrikeAbility.getInstance()); } + public KnightOfTheWhiteOrchid(final KnightOfTheWhiteOrchid card) { + super(card); + } + + @Override + public KnightOfTheWhiteOrchid copy() { + return new KnightOfTheWhiteOrchid(this); + } + + @Override + public String getArt() { + return "116188_typ_reg_sty_010.jpg"; + } + } -class KnightOfTheWhiteOrchidAbility extends EntersBattlefieldTriggeredAbility { +class KnightOfTheWhiteOrchidAbility extends EntersBattlefieldTriggeredAbility { public KnightOfTheWhiteOrchidAbility() { super(null, true); @@ -78,6 +91,15 @@ class KnightOfTheWhiteOrchidAbility extends EntersBattlefieldTriggeredAbility { addEffect(new SearchLibraryPutInPlayEffect(target, false, Outcome.PutLandInPlay)); } + public KnightOfTheWhiteOrchidAbility(final KnightOfTheWhiteOrchidAbility ability) { + super(ability); + } + + @Override + public KnightOfTheWhiteOrchidAbility copy() { + return new KnightOfTheWhiteOrchidAbility(this); + } + @Override public boolean checkTrigger(GameEvent event, Game game) { if (event.getType() == EventType.ZONE_CHANGE && event.getTargetId().equals(this.getSourceId()) ) { diff --git a/Mage.Sets/src/mage/sets/shardsofalara/RafiqOfTheMany.java b/Mage.Sets/src/mage/sets/shardsofalara/RafiqOfTheMany.java index 2951a7a639f..d95f3608b87 100644 --- a/Mage.Sets/src/mage/sets/shardsofalara/RafiqOfTheMany.java +++ b/Mage.Sets/src/mage/sets/shardsofalara/RafiqOfTheMany.java @@ -48,7 +48,7 @@ import mage.target.common.TargetCreaturePermanent; * * @author BetaSteward_at_googlemail.com */ -public class RafiqOfTheMany extends CardImpl { +public class RafiqOfTheMany extends CardImpl { public RafiqOfTheMany(UUID ownerId) { super(ownerId, "Rafiq of the Many", new CardType[]{CardType.CREATURE}, "{1}{G}{W}{U}"); @@ -59,27 +59,49 @@ public class RafiqOfTheMany extends CardImpl { this.supertype.add("Legendary"); this.subtype.add("Human"); this.subtype.add("Knight"); - this.art = "115029_typ_reg_sty_010.jpg"; this.power = new MageInt(3); this.toughness = new MageInt(3); this.addAbility(new ExaltedAbility()); this.addAbility(new RafiqOfTheManyAbility()); } + public RafiqOfTheMany(final RafiqOfTheMany card) { + super(card); + } + + @Override + public RafiqOfTheMany copy() { + return new RafiqOfTheMany(this); + } + + @Override + public String getArt() { + return "115029_typ_reg_sty_010.jpg"; + } + } -class RafiqOfTheManyAbility extends TriggeredAbilityImpl { +class RafiqOfTheManyAbility extends TriggeredAbilityImpl { public RafiqOfTheManyAbility() { super(Zone.BATTLEFIELD, new GainAbilityTargetEffect(DoubleStrikeAbility.getInstance(), Duration.EndOfTurn)); } + public RafiqOfTheManyAbility(final RafiqOfTheManyAbility ability) { + super(ability); + } + + @Override + public RafiqOfTheManyAbility copy() { + return new RafiqOfTheManyAbility(this); + } + @Override public boolean checkTrigger(GameEvent event, Game game) { if (event.getType() == EventType.DECLARED_ATTACKERS && game.getActivePlayerId().equals(this.controllerId) ) { if (game.getCombat().attacksAlone()) { this.addTarget(new TargetCreaturePermanent()); - this.targets.get(0).getTargets().add(game.getCombat().getAttackers().get(0)); + this.targets.get(0).addTarget(game.getCombat().getAttackers().get(0), null, game); trigger(game, event.getPlayerId()); return true; } diff --git a/Mage.Sets/src/mage/sets/shardsofalara/RangerOfEos.java b/Mage.Sets/src/mage/sets/shardsofalara/RangerOfEos.java index 3d4daf597a4..25f1615785b 100644 --- a/Mage.Sets/src/mage/sets/shardsofalara/RangerOfEos.java +++ b/Mage.Sets/src/mage/sets/shardsofalara/RangerOfEos.java @@ -43,7 +43,7 @@ import mage.target.common.TargetCardInLibrary; * * @author BetaSteward_at_googlemail.com */ -public class RangerOfEos extends CardImpl { +public class RangerOfEos extends CardImpl { private static FilterCard filter = new FilterCard("creature cards with converted mana cost 1 or less"); @@ -59,7 +59,6 @@ public class RangerOfEos extends CardImpl { this.color.setWhite(true); this.subtype.add("Human"); this.subtype.add("Soldier"); - this.art = "114946_typ_reg_sty_010.jpg"; this.power = new MageInt(3); this.toughness = new MageInt(2); @@ -67,4 +66,18 @@ public class RangerOfEos extends CardImpl { this.addAbility(new EntersBattlefieldTriggeredAbility(new SearchLibraryRevealPutInHandEffect(target), true)); } + public RangerOfEos(final RangerOfEos card) { + super(card); + } + + @Override + public RangerOfEos copy() { + return new RangerOfEos(this); + } + + @Override + public String getArt() { + return "114946_typ_reg_sty_010.jpg"; + } + } diff --git a/Mage.Sets/src/mage/sets/shardsofalara/RhoxWarMonk.java b/Mage.Sets/src/mage/sets/shardsofalara/RhoxWarMonk.java index c9006e81c59..8150355262a 100644 --- a/Mage.Sets/src/mage/sets/shardsofalara/RhoxWarMonk.java +++ b/Mage.Sets/src/mage/sets/shardsofalara/RhoxWarMonk.java @@ -39,7 +39,7 @@ import mage.sets.ShardsOfAlara; * * @author BetaSteward_at_googlemail.com */ -public class RhoxWarMonk extends CardImpl { +public class RhoxWarMonk extends CardImpl { public RhoxWarMonk(UUID ownerId) { super(ownerId, "Rhox War Monk", new CardType[]{CardType.CREATURE}, "{G}{W}{U}"); @@ -49,11 +49,24 @@ public class RhoxWarMonk extends CardImpl { this.color.setWhite(true); this.color.setBlue(true); this.color.setGreen(true); - this.art = "115055_typ_reg_sty_010.jpg"; this.power = new MageInt(3); this.toughness = new MageInt(4); this.addAbility(LifelinkAbility.getInstance()); } + public RhoxWarMonk(final RhoxWarMonk card) { + super(card); + } + + @Override + public RhoxWarMonk copy() { + return new RhoxWarMonk(this); + } + + @Override + public String getArt() { + return "115055_typ_reg_sty_010.jpg"; + } + } diff --git a/Mage.Sets/src/mage/sets/shardsofalara/SarkhanVol.java b/Mage.Sets/src/mage/sets/shardsofalara/SarkhanVol.java index 2d113537a18..e34121d363c 100644 --- a/Mage.Sets/src/mage/sets/shardsofalara/SarkhanVol.java +++ b/Mage.Sets/src/mage/sets/shardsofalara/SarkhanVol.java @@ -51,7 +51,7 @@ import mage.target.common.TargetCreaturePermanent; * * @author BetaSteward_at_googlemail.com */ -public class SarkhanVol extends CardImpl { +public class SarkhanVol extends CardImpl { private static DragonToken dragonToken = new DragonToken(); @@ -61,15 +61,14 @@ public class SarkhanVol extends CardImpl { this.subtype.add("Sarkhan"); this.color.setRed(true); this.color.setGreen(true); - this.art = "115056_typ_reg_sty_010.jpg"; this.loyalty = new MageInt(4); - Effects effects1 = new Effects(null); + Effects effects1 = new Effects(); effects1.add(new BoostControlledEffect(1, 1, Duration.EndOfTurn)); effects1.add(new GainAbilityControlledEffect(HasteAbility.getInstance(), Duration.EndOfTurn, new FilterCreaturePermanent())); this.addAbility(new LoyaltyAbility(effects1, 1)); - Effects effects2 = new Effects(null); + Effects effects2 = new Effects(); effects2.add(new GainControlTargetEOTEffect()); effects2.add(new UntapTargetEffect()); effects2.add(new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn)); @@ -80,4 +79,18 @@ public class SarkhanVol extends CardImpl { this.addAbility(new LoyaltyAbility(new CreateTokenEffect(dragonToken), -6)); } + + public SarkhanVol(final SarkhanVol card) { + super(card); + } + + @Override + public SarkhanVol copy() { + return new SarkhanVol(this); + } + + @Override + public String getArt() { + return "115056_typ_reg_sty_010.jpg"; + } } diff --git a/Mage.Sets/src/mage/sets/shardsofalara/SavageLands.java b/Mage.Sets/src/mage/sets/shardsofalara/SavageLands.java index ba4480c5c9b..0fd6defdb20 100644 --- a/Mage.Sets/src/mage/sets/shardsofalara/SavageLands.java +++ b/Mage.Sets/src/mage/sets/shardsofalara/SavageLands.java @@ -42,16 +42,29 @@ import mage.sets.ShardsOfAlara; * * @author BetaSteward_at_googlemail.com */ -public class SavageLands extends CardImpl { +public class SavageLands extends CardImpl { public SavageLands(UUID ownerId) { super(ownerId, "Savage Lands", new CardType[]{CardType.LAND}, null); this.expansionSetId = ShardsOfAlara.getInstance().getId(); - this.art = "114986_typ_reg_sty_010.jpg"; this.addAbility(new EntersBattlefieldStaticAbility(new EntersBattlefieldTappedEffect())); this.addAbility(new RedManaAbility()); this.addAbility(new GreenManaAbility()); this.addAbility(new BlackManaAbility()); } + public SavageLands(final SavageLands card) { + super(card); + } + + @Override + public SavageLands copy() { + return new SavageLands(this); + } + + @Override + public String getArt() { + return "114986_typ_reg_sty_010.jpg"; + } + } diff --git a/Mage.Sets/src/mage/sets/shardsofalara/SproutingThrinax.java b/Mage.Sets/src/mage/sets/shardsofalara/SproutingThrinax.java index 1f48dce857a..5ca8e2c2a08 100644 --- a/Mage.Sets/src/mage/sets/shardsofalara/SproutingThrinax.java +++ b/Mage.Sets/src/mage/sets/shardsofalara/SproutingThrinax.java @@ -41,7 +41,7 @@ import mage.sets.ShardsOfAlara; * * @author BetaSteward_at_googlemail.com */ -public class SproutingThrinax extends CardImpl { +public class SproutingThrinax extends CardImpl { private static SaprolingToken saprolingToken = new SaprolingToken(); @@ -52,13 +52,26 @@ public class SproutingThrinax extends CardImpl { this.color.setRed(true); this.color.setGreen(true); this.subtype.add("Lizard"); - this.art = "114974_typ_reg_sty_010.jpg"; this.power = new MageInt(3); this.toughness = new MageInt(3); this.addAbility(new PutIntoGraveFromBattlefieldTriggeredAbility(new CreateTokenEffect(saprolingToken, 3), false)); } + public SproutingThrinax(final SproutingThrinax card) { + super(card); + } + + @Override + public SproutingThrinax copy() { + return new SproutingThrinax(this); + } + + @Override + public String getArt() { + return "114974_typ_reg_sty_010.jpg"; + } + } diff --git a/Mage.Sets/src/mage/sets/shardsofalara/WildNacatl.java b/Mage.Sets/src/mage/sets/shardsofalara/WildNacatl.java index b5043f6fe71..da2d7f5888d 100644 --- a/Mage.Sets/src/mage/sets/shardsofalara/WildNacatl.java +++ b/Mage.Sets/src/mage/sets/shardsofalara/WildNacatl.java @@ -42,7 +42,7 @@ import mage.sets.ShardsOfAlara; * * @author BetaSteward_at_googlemail.com */ -public class WildNacatl extends CardImpl { +public class WildNacatl extends CardImpl { private FilterPermanent filter1 = new FilterPermanent("Mountain"); private FilterPermanent filter2 = new FilterPermanent("Plains"); @@ -53,7 +53,6 @@ public class WildNacatl extends CardImpl { this.color.setGreen(true); this.subtype.add("Cat"); this.subtype.add("Warrior"); - this.art = "115062_typ_reg_sty_010.jpg"; this.power = new MageInt(1); this.toughness = new MageInt(1); filter1.getName().add("Mountain"); @@ -61,4 +60,18 @@ public class WildNacatl extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostSourceWhileControlsEffect(filter1, 1, 1))); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostSourceWhileControlsEffect(filter2, 1, 1))); } + + public WildNacatl(final WildNacatl card) { + super(card); + } + + @Override + public WildNacatl copy() { + return new WildNacatl(this); + } + + @Override + public String getArt() { + return "115062_typ_reg_sty_010.jpg"; + } } diff --git a/Mage.Sets/src/mage/sets/shardsofalara/WoollyThoctar.java b/Mage.Sets/src/mage/sets/shardsofalara/WoollyThoctar.java index 020ac5c7f92..683ec96eb9a 100644 --- a/Mage.Sets/src/mage/sets/shardsofalara/WoollyThoctar.java +++ b/Mage.Sets/src/mage/sets/shardsofalara/WoollyThoctar.java @@ -38,7 +38,7 @@ import mage.sets.ShardsOfAlara; * * @author BetaSteward_at_googlemail.com */ -public class WoollyThoctar extends CardImpl { +public class WoollyThoctar extends CardImpl { public WoollyThoctar(UUID ownerId) { super(ownerId, "Woolly Thoctar", new CardType[]{CardType.CREATURE}, "{R}{G}{W}"); @@ -47,9 +47,22 @@ public class WoollyThoctar extends CardImpl { this.color.setGreen(true); this.color.setWhite(true); this.subtype.add("Beast"); - this.art = "117002_typ_reg_sty_010.jpg"; this.power = new MageInt(5); this.toughness = new MageInt(4); } + public WoollyThoctar(final WoollyThoctar card) { + super(card); + } + + @Override + public WoollyThoctar copy() { + return new WoollyThoctar(this); + } + + @Override + public String getArt() { + return "117002_typ_reg_sty_010.jpg"; + } + } diff --git a/Mage.Sets/src/mage/sets/tenth/Forest1.java b/Mage.Sets/src/mage/sets/tenth/Forest1.java index 48165d225ef..35b3cc340e7 100644 --- a/Mage.Sets/src/mage/sets/tenth/Forest1.java +++ b/Mage.Sets/src/mage/sets/tenth/Forest1.java @@ -36,12 +36,25 @@ import mage.sets.Tenth; * * @author BetaSteward_at_googlemail.com */ -public class Forest1 extends Forest { +public class Forest1 extends Forest { public Forest1(UUID ownerId) { super(ownerId); this.expansionSetId = Tenth.getInstance().getId(); - this.art = "89160_typ_reg_sty_010.jpg"; + } + + public Forest1(final Forest1 card) { + super(card); + } + + @Override + public Forest1 copy() { + return new Forest1(this); + } + + @Override + public String getArt() { + return "89160_typ_reg_sty_010.jpg"; } } diff --git a/Mage.Sets/src/mage/sets/tenth/Island1.java b/Mage.Sets/src/mage/sets/tenth/Island1.java index 2e0d53082f1..0c4f25591ae 100644 --- a/Mage.Sets/src/mage/sets/tenth/Island1.java +++ b/Mage.Sets/src/mage/sets/tenth/Island1.java @@ -36,12 +36,25 @@ import mage.sets.Tenth; * * @author BetaSteward_at_googlemail.com */ -public class Island1 extends Island { +public class Island1 extends Island { public Island1(UUID ownerId) { super(ownerId); this.expansionSetId = Tenth.getInstance().getId(); - this.art = "80957_typ_reg_sty_010.jpg"; + } + + public Island1(final Island1 card) { + super(card); + } + + @Override + public Island1 copy() { + return new Island1(this); + } + + @Override + public String getArt() { + return "80957_typ_reg_sty_010.jpg"; } } diff --git a/Mage.Sets/src/mage/sets/tenth/Mountain1.java b/Mage.Sets/src/mage/sets/tenth/Mountain1.java index b1f8cf9e0cf..eced6bf3b36 100644 --- a/Mage.Sets/src/mage/sets/tenth/Mountain1.java +++ b/Mage.Sets/src/mage/sets/tenth/Mountain1.java @@ -36,12 +36,25 @@ import mage.sets.Tenth; * * @author BetaSteward_at_googlemail.com */ -public class Mountain1 extends Mountain { +public class Mountain1 extends Mountain { public Mountain1(UUID ownerId) { super(ownerId); this.expansionSetId = Tenth.getInstance().getId(); - this.art = "89159_typ_reg_sty_010.jpg"; + } + + public Mountain1(final Mountain1 card) { + super(card); + } + + @Override + public Mountain1 copy() { + return new Mountain1(this); + } + + @Override + public String getArt() { + return "89159_typ_reg_sty_010.jpg"; } } diff --git a/Mage.Sets/src/mage/sets/tenth/Plains1.java b/Mage.Sets/src/mage/sets/tenth/Plains1.java index d36e4c2b33d..fb4720676e3 100644 --- a/Mage.Sets/src/mage/sets/tenth/Plains1.java +++ b/Mage.Sets/src/mage/sets/tenth/Plains1.java @@ -36,12 +36,25 @@ import mage.sets.Tenth; * * @author BetaSteward_at_googlemail.com */ -public class Plains1 extends Plains { +public class Plains1 extends Plains { public Plains1(UUID ownerId) { super(ownerId); this.expansionSetId = Tenth.getInstance().getId(); - this.art = "80949_typ_reg_sty_010.jpg"; + } + + public Plains1(final Plains1 card) { + super(card); + } + + @Override + public Plains1 copy() { + return new Plains1(this); + } + + @Override + public String getArt() { + return "80949_typ_reg_sty_010.jpg"; } } diff --git a/Mage.Sets/src/mage/sets/tenth/Swamp1.java b/Mage.Sets/src/mage/sets/tenth/Swamp1.java index 07fd7796e2b..fa292bd9725 100644 --- a/Mage.Sets/src/mage/sets/tenth/Swamp1.java +++ b/Mage.Sets/src/mage/sets/tenth/Swamp1.java @@ -36,12 +36,25 @@ import mage.sets.Tenth; * * @author BetaSteward_at_googlemail.com */ -public class Swamp1 extends Swamp { +public class Swamp1 extends Swamp { public Swamp1(UUID ownerId) { super(ownerId); this.expansionSetId = Tenth.getInstance().getId(); - this.art = "89177_typ_reg_sty_010.jpg"; + } + + public Swamp1(final Swamp1 card) { + super(card); + } + + @Override + public Swamp1 copy() { + return new Swamp1(this); + } + + @Override + public String getArt() { + return "89177_typ_reg_sty_010.jpg"; } } diff --git a/Mage.Sets/src/mage/sets/worldwake/ArborElf.java b/Mage.Sets/src/mage/sets/worldwake/ArborElf.java new file mode 100644 index 00000000000..7b302d67a87 --- /dev/null +++ b/Mage.Sets/src/mage/sets/worldwake/ArborElf.java @@ -0,0 +1,84 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.sets.worldwake; + +import java.util.UUID; +import mage.Constants.CardType; +import mage.Constants.Zone; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.common.UntapTargetEffect; +import mage.cards.CardImpl; +import mage.filter.common.FilterLandPermanent; +import mage.sets.Worldwake; +import mage.target.common.TargetLandPermanent; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class ArborElf extends CardImpl { + + private static FilterLandPermanent filter = new FilterLandPermanent(); + + static { + filter.getName().add("Forest"); + } + + public ArborElf(UUID ownerId) { + super(ownerId, "Arbor Elf", new CardType[]{CardType.CREATURE}, "{G}"); + this.expansionSetId = Worldwake.getInstance().getId(); + this.subtype.add("Elf"); + this.subtype.add("Druid"); + this.color.setGreen(true); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new UntapTargetEffect(), new TapSourceCost()); + TargetLandPermanent target = new TargetLandPermanent(filter); + ability.addTarget(target); + this.addAbility(ability); + } + + public ArborElf(final ArborElf card) { + super(card); + } + + @Override + public ArborElf copy() { + return new ArborElf(this); + } + + @Override + public String getArt() { + return "126593_typ_reg_sty_010.jpg"; + } + +} diff --git a/Mage.Sets/src/mage/sets/worldwake/BasiliskCollar.java b/Mage.Sets/src/mage/sets/worldwake/BasiliskCollar.java index 8ee9446050c..1332e269ef6 100644 --- a/Mage.Sets/src/mage/sets/worldwake/BasiliskCollar.java +++ b/Mage.Sets/src/mage/sets/worldwake/BasiliskCollar.java @@ -45,15 +45,28 @@ import mage.sets.Worldwake; * * @author BetaSteward_at_googlemail.com */ -public class BasiliskCollar extends CardImpl { +public class BasiliskCollar extends CardImpl { public BasiliskCollar(UUID ownerId) { super(ownerId, "Basilisk Collar", new CardType[]{CardType.ARTIFACT}, "{1}"); this.expansionSetId = Worldwake.getInstance().getId(); - this.art = "126549_typ_reg_sty_010.jpg"; this.subtype.add("Equipment"); this.addAbility(new EquipAbility(Outcome.AddAbility, new GenericManaCost(2))); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAttachedEffect(LifelinkAbility.getInstance()))); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAttachedEffect(DeathtouchAbility.getInstance()))); } + + public BasiliskCollar(final BasiliskCollar card) { + super(card); + } + + @Override + public BasiliskCollar copy() { + return new BasiliskCollar(this); + } + + @Override + public String getArt() { + return "126549_typ_reg_sty_010.jpg"; + } } diff --git a/Mage.Sets/src/mage/sets/worldwake/CelestialColonnade.java b/Mage.Sets/src/mage/sets/worldwake/CelestialColonnade.java index 074caed813c..56982454701 100644 --- a/Mage.Sets/src/mage/sets/worldwake/CelestialColonnade.java +++ b/Mage.Sets/src/mage/sets/worldwake/CelestialColonnade.java @@ -34,7 +34,7 @@ import mage.Constants.Zone; import mage.MageInt; import mage.abilities.common.EntersBattlefieldStaticAbility; import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.costs.mana.ManaCosts; +import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.BecomesCreatureSourceEOTEffect; import mage.abilities.effects.common.EntersBattlefieldTappedEffect; import mage.abilities.keyword.FlyingAbility; @@ -49,16 +49,29 @@ import mage.sets.Worldwake; * * @author BetaSteward_at_googlemail.com */ -public class CelestialColonnade extends CardImpl { +public class CelestialColonnade extends CardImpl { public CelestialColonnade(UUID ownerId) { super(ownerId, "Celestial Colonnade", new CardType[]{CardType.LAND}, null); this.expansionSetId = Worldwake.getInstance().getId(); - this.art = "126518_typ_reg_sty_010.jpg"; this.addAbility(new EntersBattlefieldStaticAbility(new EntersBattlefieldTappedEffect())); this.addAbility(new BlueManaAbility()); this.addAbility(new WhiteManaAbility()); - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BecomesCreatureSourceEOTEffect(new CelestialColonnadeToken(), "land"), new ManaCosts("{3}{W}{U}"))); + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BecomesCreatureSourceEOTEffect(new CelestialColonnadeToken(), "land"), new ManaCostsImpl("{3}{W}{U}"))); + } + + public CelestialColonnade(final CelestialColonnade card) { + super(card); + } + + @Override + public CelestialColonnade copy() { + return new CelestialColonnade(this); + } + + @Override + public String getArt() { + return "126518_typ_reg_sty_010.jpg"; } } diff --git a/Mage.Sets/src/mage/sets/worldwake/DreadStatuary.java b/Mage.Sets/src/mage/sets/worldwake/DreadStatuary.java new file mode 100644 index 00000000000..117790b4b91 --- /dev/null +++ b/Mage.Sets/src/mage/sets/worldwake/DreadStatuary.java @@ -0,0 +1,83 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.sets.worldwake; + +import java.util.UUID; +import mage.Constants.CardType; +import mage.Constants.Zone; +import mage.MageInt; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.BecomesCreatureSourceEOTEffect; +import mage.abilities.mana.ColorlessManaAbility; +import mage.cards.CardImpl; +import mage.game.permanent.token.Token; +import mage.sets.Worldwake; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class DreadStatuary extends CardImpl { + + public DreadStatuary(UUID ownerId) { + super(ownerId, "Dread Statuary", new CardType[]{CardType.LAND}, null); + this.expansionSetId = Worldwake.getInstance().getId(); + this.addAbility(new ColorlessManaAbility()); + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BecomesCreatureSourceEOTEffect(new DreadStatuaryToken(), "land"), new ManaCostsImpl("{4}"))); + } + + public DreadStatuary(final DreadStatuary card) { + super(card); + } + + @Override + public DreadStatuary copy() { + return new DreadStatuary(this); + } + + @Override + public String getArt() { + return "126755_typ_reg_sty_010.jpg"; + } + +} + +class DreadStatuaryToken extends Token { + + public DreadStatuaryToken() { + super("", "4/2 Golem artifact creature"); + cardType.add(CardType.CREATURE); + cardType.add(CardType.ARTIFACT); + subtype.add("Golem"); + power = new MageInt(4); + toughness = new MageInt(2); + } + +} diff --git a/Mage.Sets/src/mage/sets/worldwake/EverflowingChalice.java b/Mage.Sets/src/mage/sets/worldwake/EverflowingChalice.java index a6521f94aaf..3f9697b3160 100644 --- a/Mage.Sets/src/mage/sets/worldwake/EverflowingChalice.java +++ b/Mage.Sets/src/mage/sets/worldwake/EverflowingChalice.java @@ -32,6 +32,7 @@ import java.util.UUID; import mage.Constants.CardType; import mage.Constants.Zone; import mage.Mana; +import mage.abilities.Ability; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.common.AddCountersSourceEffect; @@ -46,26 +47,48 @@ import mage.sets.Worldwake; * * @author BetaSteward_at_googlemail.com */ -public class EverflowingChalice extends CardImpl { +public class EverflowingChalice extends CardImpl { public EverflowingChalice(UUID ownerId) { super(ownerId, "Everflowing Chalice", new CardType[]{CardType.ARTIFACT}, "{0}"); this.expansionSetId = Worldwake.getInstance().getId(); - this.art = "126542_typ_reg_sty_010.jpg"; MultikickerAbility ability = new MultikickerAbility(new AddCountersSourceEffect("charge", 1), false); ability.addManaCost(new GenericManaCost(2)); this.addAbility(ability); this.addAbility(new EverflowingChaliceAbility()); } + public EverflowingChalice(final EverflowingChalice card) { + super(card); + } + + @Override + public EverflowingChalice copy() { + return new EverflowingChalice(this); + } + + @Override + public String getArt() { + return "126542_typ_reg_sty_010.jpg"; + } + } -class EverflowingChaliceAbility extends ManaAbility { +class EverflowingChaliceAbility extends ManaAbility { public EverflowingChaliceAbility() { super(Zone.BATTLEFIELD, new EverflowingChaliceEffect(), new TapSourceCost()); } + public EverflowingChaliceAbility(final EverflowingChaliceAbility ability) { + super(ability); + } + + @Override + public EverflowingChaliceAbility copy() { + return new EverflowingChaliceAbility(this); + } + @Override public Mana getNetMana(Game game) { return Mana.ColorlessMana(game.getPermanent(this.getSourceId()).getCounters().getCount("charge")); @@ -79,15 +102,24 @@ class EverflowingChaliceEffect extends ManaEffect { super(new Mana()); } - @Override - public boolean apply(Game game) { - this.mana.clear(); - this.mana.setColorless(game.getPermanent(this.source.getSourceId()).getCounters().getCount("charge")); - return super.apply(game); + public EverflowingChaliceEffect(final EverflowingChaliceEffect effect) { + super(effect); } @Override - public String getText() { + public EverflowingChaliceEffect copy() { + return new EverflowingChaliceEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + this.mana.clear(); + this.mana.setColorless(game.getPermanent(source.getSourceId()).getCounters().getCount("charge")); + return super.apply(game, source); + } + + @Override + public String getText(Ability source) { return "Add {1} to your mana pool for each charge counter on {this}"; } diff --git a/Mage.Sets/src/mage/sets/worldwake/JaceTheMindSculptor.java b/Mage.Sets/src/mage/sets/worldwake/JaceTheMindSculptor.java index 033b33503eb..99a39f09b8e 100644 --- a/Mage.Sets/src/mage/sets/worldwake/JaceTheMindSculptor.java +++ b/Mage.Sets/src/mage/sets/worldwake/JaceTheMindSculptor.java @@ -33,6 +33,7 @@ import mage.Constants.CardType; import mage.Constants.Outcome; import mage.Constants.Zone; import mage.MageInt; +import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ReturnToHandTargetEffect; @@ -52,7 +53,7 @@ import mage.target.common.TargetCreaturePermanent; * * @author BetaSteward_at_googlemail.com */ -public class JaceTheMindSculptor extends CardImpl { +public class JaceTheMindSculptor extends CardImpl { public JaceTheMindSculptor(UUID ownerId) { @@ -60,7 +61,6 @@ public class JaceTheMindSculptor extends CardImpl { this.expansionSetId = Worldwake.getInstance().getId(); this.subtype.add("Jace"); this.color.setBlue(true); - this.art = "126493_typ_reg_sty_010.jpg"; this.loyalty = new MageInt(3); LoyaltyAbility ability1 = new LoyaltyAbility(new JaceTheMindSculptorEffect1(), 2); @@ -80,20 +80,43 @@ public class JaceTheMindSculptor extends CardImpl { } + public JaceTheMindSculptor(final JaceTheMindSculptor card) { + super(card); + } + + @Override + public JaceTheMindSculptor copy() { + return new JaceTheMindSculptor(this); + } + + @Override + public String getArt() { + return "126493_typ_reg_sty_010.jpg"; + } + } -class JaceTheMindSculptorEffect1 extends OneShotEffect { +class JaceTheMindSculptorEffect1 extends OneShotEffect { public JaceTheMindSculptorEffect1() { super(Outcome.Detriment); } + public JaceTheMindSculptorEffect1(final JaceTheMindSculptorEffect1 effect) { + super(effect); + } + @Override - public boolean apply(Game game) { - Player controller = game.getPlayer(this.source.getControllerId()); - Player player = game.getPlayer(this.source.getFirstTarget()); + public JaceTheMindSculptorEffect1 copy() { + return new JaceTheMindSculptorEffect1(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Player player = game.getPlayer(source.getFirstTarget()); if (controller != null && player != null) { - Cards cards = new CardsImpl(Zone.REVEALED); + Cards cards = new CardsImpl(); cards.add(player.getLibrary().getFromTop(game)); controller.lookAtCards(cards, game); if (controller.chooseUse(outcome, "Do you wish to put card on the bottom of player's library?", game)) { @@ -107,36 +130,45 @@ class JaceTheMindSculptorEffect1 extends OneShotEffect { } @Override - public String getText() { + public String getText(Ability source) { return "Look at the top card of target player's library. You may put that card on the bottom of that player's library"; } } -class JaceTheMindSculptorEffect2 extends OneShotEffect { +class JaceTheMindSculptorEffect2 extends OneShotEffect { public JaceTheMindSculptorEffect2() { super(Outcome.DrawCard); } + public JaceTheMindSculptorEffect2(final JaceTheMindSculptorEffect2 effect) { + super(effect); + } + @Override - public boolean apply(Game game) { - Player player = game.getPlayer(this.source.getControllerId()); + public JaceTheMindSculptorEffect2 copy() { + return new JaceTheMindSculptorEffect2(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); if (player != null) { player.drawCards(3, game); - putOnLibrary(player, game); - putOnLibrary(player, game); + putOnLibrary(player, source, game); + putOnLibrary(player, source, game); return true; } return false; } - private boolean putOnLibrary(Player player, Game game) { + private boolean putOnLibrary(Player player, Ability source, Game game) { TargetCardInHand target = new TargetCardInHand(); target.setRequired(true); - target.setAbility(source); - player.chooseTarget(Outcome.ReturnToHand, target, game); - Card card = player.getHand().get(target.getFirstTarget()); +// target.setAbility(source); + player.chooseTarget(Outcome.ReturnToHand, target, source, game); + Card card = player.getHand().get(target.getFirstTarget(), game); if (card != null) { player.getHand().remove(card); player.getLibrary().putOnTop(card, game); @@ -145,21 +177,30 @@ class JaceTheMindSculptorEffect2 extends OneShotEffect { } @Override - public String getText() { + public String getText(Ability source) { return "Draw three cards, then put two cards from your hand on top of your library in any order"; } } -class JaceTheMindSculptorEffect3 extends OneShotEffect { +class JaceTheMindSculptorEffect3 extends OneShotEffect { public JaceTheMindSculptorEffect3() { super(Outcome.DrawCard); } + public JaceTheMindSculptorEffect3(final JaceTheMindSculptorEffect3 effect) { + super(effect); + } + @Override - public boolean apply(Game game) { - Player player = game.getPlayer(this.source.getFirstTarget()); + public JaceTheMindSculptorEffect3 copy() { + return new JaceTheMindSculptorEffect3(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getFirstTarget()); ExileZone exile = game.getExile().getPermanentExile(); if (player != null) { while (true) { @@ -168,7 +209,7 @@ class JaceTheMindSculptorEffect3 extends OneShotEffect { Card card = player.getLibrary().removeFromTop(game); exile.add(card); } - player.getLibrary().addAll(player.getHand()); + player.getLibrary().addAll(player.getHand().getCards(game)); player.getLibrary().shuffle(); player.getHand().clear(); return true; @@ -177,7 +218,7 @@ class JaceTheMindSculptorEffect3 extends OneShotEffect { } @Override - public String getText() { + public String getText(Ability source) { return "Exile all cards from target player's library, then that player shuffles his or her hand into his or her library"; } diff --git a/Mage.Sets/src/mage/sets/worldwake/KhalniGarden.java b/Mage.Sets/src/mage/sets/worldwake/KhalniGarden.java new file mode 100644 index 00000000000..f9cd98d1f8c --- /dev/null +++ b/Mage.Sets/src/mage/sets/worldwake/KhalniGarden.java @@ -0,0 +1,84 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.sets.worldwake; + +import java.util.UUID; +import mage.Constants.CardType; +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldStaticAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.EntersBattlefieldTappedEffect; +import mage.abilities.mana.GreenManaAbility; +import mage.cards.CardImpl; +import mage.game.permanent.token.Token; +import mage.sets.Worldwake; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class KhalniGarden extends CardImpl { + + public KhalniGarden(UUID ownerId) { + super(ownerId, "Khalni Garden", new CardType[]{CardType.LAND}, null); + this.expansionSetId = Worldwake.getInstance().getId(); + this.addAbility(new EntersBattlefieldStaticAbility(new EntersBattlefieldTappedEffect())); + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new PlantToken()), false)); + this.addAbility(new GreenManaAbility()); + } + + public KhalniGarden(final KhalniGarden card) { + super(card); + } + + @Override + public KhalniGarden copy() { + return new KhalniGarden(this); + } + + @Override + public String getArt() { + return "123552_typ_reg_sty_010.jpg"; + } + +} + +class PlantToken extends Token { + + public PlantToken() { + super("Plant", "0/1 green Plant creature"); + cardType.add(CardType.CREATURE); + color.setGreen(true); + subtype.add("Plant"); + power = new MageInt(0); + toughness = new MageInt(1); + } + +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/worldwake/LavaclawReaches.java b/Mage.Sets/src/mage/sets/worldwake/LavaclawReaches.java index 193d074ab75..43fd946182a 100644 --- a/Mage.Sets/src/mage/sets/worldwake/LavaclawReaches.java +++ b/Mage.Sets/src/mage/sets/worldwake/LavaclawReaches.java @@ -35,7 +35,7 @@ import mage.Constants.Zone; import mage.MageInt; import mage.abilities.common.EntersBattlefieldStaticAbility; import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.costs.mana.ManaCosts; +import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.BecomesCreatureSourceEOTEffect; import mage.abilities.effects.common.BoostPowerSourceVariableEffect; import mage.abilities.effects.common.EntersBattlefieldTappedEffect; @@ -49,16 +49,30 @@ import mage.sets.Worldwake; * * @author BetaSteward_at_googlemail.com */ -public class LavaclawReaches extends CardImpl { +public class LavaclawReaches extends CardImpl { public LavaclawReaches(UUID ownerId) { super(ownerId, "Lavaclaw Reaches", new CardType[]{CardType.LAND}, null); this.expansionSetId = Worldwake.getInstance().getId(); - this.art = "126532_typ_reg_sty_010.jpg"; +// this.art = "126532_typ_reg_sty_010.jpg"; this.addAbility(new EntersBattlefieldStaticAbility(new EntersBattlefieldTappedEffect())); this.addAbility(new BlackManaAbility()); this.addAbility(new RedManaAbility()); - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BecomesCreatureSourceEOTEffect(new LavaclawReachesToken(), "land"), new ManaCosts("{1}{B}{R}"))); + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BecomesCreatureSourceEOTEffect(new LavaclawReachesToken(), "land"), new ManaCostsImpl("{1}{B}{R}"))); + } + + public LavaclawReaches(final LavaclawReaches card) { + super(card); + } + + @Override + public LavaclawReaches copy() { + return new LavaclawReaches(this); + } + + @Override + public String getArt() { + return "126532_typ_reg_sty_010.jpg"; } } @@ -73,7 +87,7 @@ class LavaclawReachesToken extends Token { color.setBlack(true); power = new MageInt(2); toughness = new MageInt(2); - addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostPowerSourceVariableEffect(Duration.EndOfTurn), new ManaCosts("{X}"))); + addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostPowerSourceVariableEffect(Duration.EndOfTurn), new ManaCostsImpl("{X}"))); } } diff --git a/Mage.Sets/src/mage/sets/worldwake/RagingRavine.java b/Mage.Sets/src/mage/sets/worldwake/RagingRavine.java index 8bc80a442df..a15dff430fc 100644 --- a/Mage.Sets/src/mage/sets/worldwake/RagingRavine.java +++ b/Mage.Sets/src/mage/sets/worldwake/RagingRavine.java @@ -35,7 +35,7 @@ import mage.MageInt; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.common.EntersBattlefieldStaticAbility; import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.costs.mana.ManaCosts; +import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.AddPlusOneCountersSourceEffect; import mage.abilities.effects.common.BecomesCreatureSourceEOTEffect; import mage.abilities.effects.common.EntersBattlefieldTappedEffect; @@ -49,16 +49,29 @@ import mage.sets.Worldwake; * * @author BetaSteward_at_googlemail.com */ -public class RagingRavine extends CardImpl { +public class RagingRavine extends CardImpl { public RagingRavine(UUID ownerId) { super(ownerId, "Raging Ravine", new CardType[]{CardType.LAND}, null); this.expansionSetId = Worldwake.getInstance().getId(); - this.art = "126537_typ_reg_sty_010.jpg"; this.addAbility(new EntersBattlefieldStaticAbility(new EntersBattlefieldTappedEffect())); this.addAbility(new GreenManaAbility()); this.addAbility(new RedManaAbility()); - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BecomesCreatureSourceEOTEffect(new RagingRavineToken(), "land"), new ManaCosts("{2}{R}{G}"))); + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BecomesCreatureSourceEOTEffect(new RagingRavineToken(), "land"), new ManaCostsImpl("{2}{R}{G}"))); + } + + public RagingRavine(final RagingRavine card) { + super(card); + } + + @Override + public RagingRavine copy() { + return new RagingRavine(this); + } + + @Override + public String getArt() { + return "126537_typ_reg_sty_010.jpg"; } } diff --git a/Mage.Sets/src/mage/sets/worldwake/SearingBlaze.java b/Mage.Sets/src/mage/sets/worldwake/SearingBlaze.java index f80e77fb05e..e896b4b1114 100644 --- a/Mage.Sets/src/mage/sets/worldwake/SearingBlaze.java +++ b/Mage.Sets/src/mage/sets/worldwake/SearingBlaze.java @@ -31,11 +31,14 @@ package mage.sets.worldwake; import java.util.UUID; import mage.Constants.CardType; import mage.Constants.Outcome; +import mage.Constants.Zone; +import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; +import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; import mage.players.Player; import mage.sets.Worldwake; @@ -48,13 +51,12 @@ import mage.watchers.WatcherImpl; * * @author BetaSteward_at_googlemail.com */ -public class SearingBlaze extends CardImpl { +public class SearingBlaze extends CardImpl { public SearingBlaze(UUID ownerId) { super(ownerId, "Searing Blaze", new CardType[]{CardType.INSTANT}, "{R}{R}"); this.expansionSetId = Worldwake.getInstance().getId(); this.color.setRed(true); - this.art = "126476_typ_reg_sty_010.jpg"; this.getSpellAbility().addTarget(new TargetPlayer()); //TODO: change this to only allow creatures controlled by first target this.getSpellAbility().addTarget(new TargetCreaturePermanent()); @@ -62,31 +64,67 @@ public class SearingBlaze extends CardImpl { this.watchers.add(new SearingBlazeWatcher(ownerId)); } + public SearingBlaze(final SearingBlaze card) { + super(card); + } + + @Override + public SearingBlaze copy() { + return new SearingBlaze(this); + } + + @Override + public String getArt() { + return "126476_typ_reg_sty_010.jpg"; + } + } -class SearingBlazeWatcher extends WatcherImpl { +class SearingBlazeWatcher extends WatcherImpl { public SearingBlazeWatcher(UUID controllerId) { - super("LandPlayed"); + super("LandPlayed", controllerId); + } + + public SearingBlazeWatcher(final SearingBlazeWatcher watcher) { + super(watcher); + } + + @Override + public SearingBlazeWatcher copy() { + return new SearingBlazeWatcher(this); } @Override public void watch(GameEvent event, Game game) { - if (event.getType() == EventType.LAND_PLAYED && game.getOpponents(controllerId).contains(event.getPlayerId())) - condition = true; + if (event.getType() == EventType.ZONE_CHANGE && ((ZoneChangeEvent)event).getToZone() == Zone.BATTLEFIELD) { + Permanent permanent = game.getPermanent(event.getTargetId()); + if (permanent.getCardType().contains(CardType.LAND) && permanent.getControllerId().equals(event.getPlayerId())) { + condition = true; + } + } } } -class SearingBlazeEffect extends OneShotEffect { +class SearingBlazeEffect extends OneShotEffect { public SearingBlazeEffect() { super(Outcome.Damage); } + public SearingBlazeEffect(final SearingBlazeEffect effect) { + super(effect); + } + @Override - public boolean apply(Game game) { - Watcher watcher = game.getState().getWatchers().get(this.source.getControllerId(), "LandPlayed"); + public SearingBlazeEffect copy() { + return new SearingBlazeEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Watcher watcher = game.getState().getWatchers().get(source.getControllerId(), "LandPlayed"); Player player = game.getPlayer(source.getTargets().get(0).getFirstTarget()); Permanent creature = game.getPermanent(source.getTargets().get(1).getFirstTarget()); if (watcher != null && watcher.conditionMet()) { @@ -109,7 +147,7 @@ class SearingBlazeEffect extends OneShotEffect { } @Override - public String getText() { + public String getText(Ability source) { return "{this} deals 1 damage to target player and 1 damage to target creature that player controls. \nLandfall — If you had a land enter the battlefield under your control this turn, {this} deals 3 damage to that player and 3 damage to that creature instead."; } } diff --git a/Mage.Sets/src/mage/sets/worldwake/SejiriSteppe.java b/Mage.Sets/src/mage/sets/worldwake/SejiriSteppe.java index e2322662438..9f27eae3d16 100644 --- a/Mage.Sets/src/mage/sets/worldwake/SejiriSteppe.java +++ b/Mage.Sets/src/mage/sets/worldwake/SejiriSteppe.java @@ -45,12 +45,11 @@ import mage.target.common.TargetCreaturePermanent; * * @author BetaSteward_at_googlemail.com */ -public class SejiriSteppe extends CardImpl { +public class SejiriSteppe extends CardImpl { public SejiriSteppe(UUID ownerId) { super(ownerId, "Sejiri Steppe", new CardType[]{CardType.LAND}, null); this.expansionSetId = Worldwake.getInstance().getId(); - this.art = "123657_typ_reg_sty_010.jpg"; this.addAbility(new EntersBattlefieldStaticAbility(new EntersBattlefieldTappedEffect())); EntersBattlefieldTriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new GainProtectionFromColorTargetEOTEffect(), false); ability.addTarget(new TargetCreaturePermanent(1, TargetController.YOU)); @@ -59,4 +58,18 @@ public class SejiriSteppe extends CardImpl { this.addAbility(new WhiteManaAbility()); } + public SejiriSteppe(final SejiriSteppe card) { + super(card); + } + + @Override + public SejiriSteppe copy() { + return new SejiriSteppe(this); + } + + @Override + public String getArt() { + return "123657_typ_reg_sty_010.jpg"; + } + } diff --git a/Mage.Sets/src/mage/sets/worldwake/StirringWildwood.java b/Mage.Sets/src/mage/sets/worldwake/StirringWildwood.java index eb7168f527f..b4aa5baf947 100644 --- a/Mage.Sets/src/mage/sets/worldwake/StirringWildwood.java +++ b/Mage.Sets/src/mage/sets/worldwake/StirringWildwood.java @@ -34,7 +34,7 @@ import mage.Constants.Zone; import mage.MageInt; import mage.abilities.common.EntersBattlefieldStaticAbility; import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.costs.mana.ManaCosts; +import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.BecomesCreatureSourceEOTEffect; import mage.abilities.effects.common.EntersBattlefieldTappedEffect; import mage.abilities.keyword.ReachAbility; @@ -48,16 +48,29 @@ import mage.sets.Worldwake; * * @author BetaSteward_at_googlemail.com */ -public class StirringWildwood extends CardImpl { +public class StirringWildwood extends CardImpl { public StirringWildwood(UUID ownerId) { super(ownerId, "Stirring Wildwood", new CardType[]{CardType.LAND}, null); this.expansionSetId = Worldwake.getInstance().getId(); - this.art = "126541_typ_reg_sty_010.jpg"; this.addAbility(new EntersBattlefieldStaticAbility(new EntersBattlefieldTappedEffect())); this.addAbility(new GreenManaAbility()); this.addAbility(new WhiteManaAbility()); - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BecomesCreatureSourceEOTEffect(new StirringWildwoodToken(), "land"), new ManaCosts("{1}{G}{W}"))); + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BecomesCreatureSourceEOTEffect(new StirringWildwoodToken(), "land"), new ManaCostsImpl("{1}{G}{W}"))); + } + + public StirringWildwood(final StirringWildwood card) { + super(card); + } + + @Override + public StirringWildwood copy() { + return new StirringWildwood(this); + } + + @Override + public String getArt() { + return "126541_typ_reg_sty_010.jpg"; } } diff --git a/Mage.Sets/src/mage/sets/worldwake/StoneforgeMystic.java b/Mage.Sets/src/mage/sets/worldwake/StoneforgeMystic.java index 5c76222f06c..3b4de9d4484 100644 --- a/Mage.Sets/src/mage/sets/worldwake/StoneforgeMystic.java +++ b/Mage.Sets/src/mage/sets/worldwake/StoneforgeMystic.java @@ -35,7 +35,7 @@ import mage.MageInt; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.costs.mana.ManaCosts; +import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.PlayTargetWithoutPayingManaEffect; import mage.abilities.effects.common.SearchLibraryRevealPutInHandEffect; import mage.cards.CardImpl; @@ -48,7 +48,7 @@ import mage.target.common.TargetCardInLibrary; * * @author BetaSteward_at_googlemail.com */ -public class StoneforgeMystic extends CardImpl { +public class StoneforgeMystic extends CardImpl { private static FilterCard filter = new FilterCard("an Equipment card"); @@ -63,17 +63,30 @@ public class StoneforgeMystic extends CardImpl { this.color.setWhite(true); this.subtype.add("Kor"); this.subtype.add("Artificer"); - this.art = "126571_typ_reg_sty_010.jpg"; this.power = new MageInt(1); this.toughness = new MageInt(2); TargetCardInLibrary target = new TargetCardInLibrary(1, 1, filter); this.addAbility(new EntersBattlefieldTriggeredAbility(new SearchLibraryRevealPutInHandEffect(target), true)); - SimpleActivatedAbility ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new PlayTargetWithoutPayingManaEffect(), new ManaCosts("{1}{W}")); + SimpleActivatedAbility ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new PlayTargetWithoutPayingManaEffect(), new ManaCostsImpl("{1}{W}")); ability.addCost(new TapSourceCost()); ability.addTarget(new TargetCardInHand(0, 1, filter)); this.addAbility(ability); } + public StoneforgeMystic(final StoneforgeMystic card) { + super(card); + } + + @Override + public StoneforgeMystic copy() { + return new StoneforgeMystic(this); + } + + @Override + public String getArt() { + return "126571_typ_reg_sty_010.jpg"; + } + } diff --git a/Mage.Sets/src/mage/sets/worldwake/TectonicEdge.java b/Mage.Sets/src/mage/sets/worldwake/TectonicEdge.java index f8128141c6a..9dfeabeaf97 100644 --- a/Mage.Sets/src/mage/sets/worldwake/TectonicEdge.java +++ b/Mage.Sets/src/mage/sets/worldwake/TectonicEdge.java @@ -51,12 +51,11 @@ import mage.target.common.TargetNonBasicLandPermanent; * * @author BetaSteward_at_googlemail.com */ -public class TectonicEdge extends CardImpl { +public class TectonicEdge extends CardImpl { public TectonicEdge(UUID ownerId) { super(ownerId, "Tectonic Edge", new CardType[]{CardType.LAND}, null); this.expansionSetId = Worldwake.getInstance().getId(); - this.art = "126492_typ_reg_sty_010.jpg"; this.addAbility(new ColorlessManaAbility()); Costs costs = new CostsImpl(); costs.add(new TapSourceCost()); @@ -68,9 +67,23 @@ public class TectonicEdge extends CardImpl { this.addAbility(ability); } + public TectonicEdge(final TectonicEdge card) { + super(card); + } + + @Override + public TectonicEdge copy() { + return new TectonicEdge(this); + } + + @Override + public String getArt() { + return "126492_typ_reg_sty_010.jpg"; + } + } -class TectonicEdgeCost extends CostImpl { +class TectonicEdgeCost extends CostImpl { FilterLandPermanent filter = new FilterLandPermanent(); @@ -78,9 +91,18 @@ class TectonicEdgeCost extends CostImpl { this.text = "Activate this ability only if an opponent controls four or more lands"; } + public TectonicEdgeCost(final TectonicEdgeCost cost) { + super(cost); + } + @Override - public boolean canPay(UUID playerId, Game game) { - for (UUID opponentId: game.getOpponents(playerId)) { + public TectonicEdgeCost copy() { + return new TectonicEdgeCost(this); + } + + @Override + public boolean canPay(Ability source, Game game) { + for (UUID opponentId: game.getOpponents(source.getControllerId())) { if (game.getBattlefield().countAll(filter, opponentId) > 3) { return true; } @@ -89,7 +111,7 @@ class TectonicEdgeCost extends CostImpl { } @Override - public boolean pay(Game game, boolean noMana) { + public boolean pay(Game game, Ability source, boolean noMana) { this.paid = true; return paid; } diff --git a/Mage.Sets/src/mage/sets/worldwake/WolfbriarElemental.java b/Mage.Sets/src/mage/sets/worldwake/WolfbriarElemental.java new file mode 100644 index 00000000000..a5a904abff0 --- /dev/null +++ b/Mage.Sets/src/mage/sets/worldwake/WolfbriarElemental.java @@ -0,0 +1,75 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.sets.worldwake; + +import java.util.UUID; +import mage.Constants.CardType; +import mage.Constants.ColoredManaSymbol; +import mage.MageInt; +import mage.abilities.costs.mana.ColoredManaCost; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.keyword.MultikickerAbility; +import mage.cards.CardImpl; +import mage.game.permanent.token.WolfToken; +import mage.sets.Worldwake; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class WolfbriarElemental extends CardImpl { + + public WolfbriarElemental(UUID ownerId) { + super(ownerId, "Wolfbriar Elemental", new CardType[]{CardType.CREATURE}, "{2}{G}{G}"); + this.expansionSetId = Worldwake.getInstance().getId(); + this.subtype.add("Elemental"); + this.color.setGreen(true); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + MultikickerAbility ability = new MultikickerAbility(new CreateTokenEffect(new WolfToken()), false); + ability.addManaCost(new ColoredManaCost(ColoredManaSymbol.G)); + this.addAbility(ability); + } + + public WolfbriarElemental(final WolfbriarElemental card) { + super(card); + } + + @Override + public WolfbriarElemental copy() { + return new WolfbriarElemental(this); + } + + @Override + public String getArt() { + return "126583_typ_reg_sty_010.jpg"; + } + +} diff --git a/Mage.Sets/src/mage/sets/zendikar/AdventuringGear.java b/Mage.Sets/src/mage/sets/zendikar/AdventuringGear.java new file mode 100644 index 00000000000..46ccd6d9d33 --- /dev/null +++ b/Mage.Sets/src/mage/sets/zendikar/AdventuringGear.java @@ -0,0 +1,70 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.sets.zendikar; + +import java.util.UUID; +import mage.Constants.CardType; +import mage.Constants.Duration; +import mage.Constants.Outcome; +import mage.abilities.common.LandfallAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.BoostEquippedEffect; +import mage.abilities.keyword.EquipAbility; +import mage.cards.CardImpl; +import mage.sets.Zendikar; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class AdventuringGear extends CardImpl { + + public AdventuringGear(UUID ownerId) { + super(ownerId, "Adventuring Gear", new CardType[]{CardType.ARTIFACT}, "{1}"); + this.expansionSetId = Zendikar.getInstance().getId(); + this.subtype.add("Equipment"); + this.addAbility(new EquipAbility(Outcome.BoostCreature, new GenericManaCost(1))); + this.addAbility(new LandfallAbility(new BoostEquippedEffect(2, 2, Duration.EndOfTurn), false)); + } + + public AdventuringGear(final AdventuringGear card) { + super(card); + } + + @Override + public AdventuringGear copy() { + return new AdventuringGear(this); + } + + @Override + public String getArt() { + return "123577_typ_reg_sty_010.jpg"; + } + +} diff --git a/Mage.Sets/src/mage/sets/zendikar/AetherFigment.java b/Mage.Sets/src/mage/sets/zendikar/AetherFigment.java new file mode 100644 index 00000000000..60bd3d30c2a --- /dev/null +++ b/Mage.Sets/src/mage/sets/zendikar/AetherFigment.java @@ -0,0 +1,73 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.sets.zendikar; + +import java.util.UUID; +import mage.Constants.CardType; +import mage.MageInt; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.AddPlusOneCountersSourceEffect; +import mage.abilities.keyword.KickerAbility; +import mage.cards.CardImpl; +import mage.sets.Zendikar; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class AetherFigment extends CardImpl { + + public AetherFigment(UUID ownerId) { + super(ownerId, "Æther Figment", new CardType[]{CardType.CREATURE}, "{1}{U}"); + this.expansionSetId = Zendikar.getInstance().getId(); + this.subtype.add("Illusion"); + this.color.setBlue(true); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + KickerAbility ability = new KickerAbility(new AddPlusOneCountersSourceEffect(2), false); + ability.addManaCost(new GenericManaCost(3)); + this.addAbility(ability); + } + + public AetherFigment(final AetherFigment card) { + super(card); + } + + @Override + public AetherFigment copy() { + return new AetherFigment(this); + } + + @Override + public String getArt() { + return "102621_typ_reg_sty_010.jpg"; + } + +} diff --git a/Mage.Sets/src/mage/sets/zendikar/ArchiveTrap.java b/Mage.Sets/src/mage/sets/zendikar/ArchiveTrap.java index a947a11373b..5a60a5e7aab 100644 --- a/Mage.Sets/src/mage/sets/zendikar/ArchiveTrap.java +++ b/Mage.Sets/src/mage/sets/zendikar/ArchiveTrap.java @@ -30,6 +30,7 @@ package mage.sets.zendikar; import java.util.UUID; import mage.Constants.CardType; +import mage.abilities.Ability; import mage.abilities.costs.AlternativeCost; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.common.PutLibraryIntoGraveTargetEffect; @@ -46,25 +47,47 @@ import mage.watchers.WatcherImpl; * * @author BetaSteward_at_googlemail.com */ -public class ArchiveTrap extends CardImpl { +public class ArchiveTrap extends CardImpl { public ArchiveTrap(UUID ownerId) { super(ownerId, "Archive Trap", new CardType[]{CardType.INSTANT}, "{3}{U}{U}"); this.expansionSetId = Zendikar.getInstance().getId(); this.color.setBlue(true); - this.art = "125080_typ_reg_sty_010.jpg"; this.getSpellAbility().addTarget(new TargetOpponent()); this.getSpellAbility().addEffect(new PutLibraryIntoGraveTargetEffect(13)); this.getSpellAbility().addAlternativeCost(new ArchiveTrapAlternativeCost()); - this.watchers.add(new ArchiveTrapWatcher()); + this.watchers.add(new ArchiveTrapWatcher(ownerId)); + } + + public ArchiveTrap(final ArchiveTrap card) { + super(card); + } + + @Override + public ArchiveTrap copy() { + return new ArchiveTrap(this); + } + + @Override + public String getArt() { + return "125080_typ_reg_sty_010.jpg"; } } -class ArchiveTrapWatcher extends WatcherImpl { +class ArchiveTrapWatcher extends WatcherImpl { - public ArchiveTrapWatcher() { - super("LibrarySearched"); + public ArchiveTrapWatcher(UUID controllerId) { + super("LibrarySearched", controllerId); + } + + public ArchiveTrapWatcher(final ArchiveTrapWatcher watcher) { + super(watcher); + } + + @Override + public ArchiveTrapWatcher copy() { + return new ArchiveTrapWatcher(this); } @Override @@ -75,16 +98,25 @@ class ArchiveTrapWatcher extends WatcherImpl { } -class ArchiveTrapAlternativeCost extends AlternativeCost { +class ArchiveTrapAlternativeCost extends AlternativeCost { public ArchiveTrapAlternativeCost() { super("you may pay {0} rather than pay Archive Trap's mana cost"); this.add(new GenericManaCost(0)); } + public ArchiveTrapAlternativeCost(final ArchiveTrapAlternativeCost cost) { + super(cost); + } + @Override - public boolean isAvailable(Game game) { - Watcher watcher = game.getState().getWatchers().get(this.ability.getControllerId(), "LibrarySearched"); + public ArchiveTrapAlternativeCost copy() { + return new ArchiveTrapAlternativeCost(this); + } + + @Override + public boolean isAvailable(Game game, Ability source) { + Watcher watcher = game.getState().getWatchers().get(source.getControllerId(), "LibrarySearched"); if (watcher != null && watcher.conditionMet()) return true; return false; diff --git a/Mage.Sets/src/mage/sets/zendikar/AridMesa.java b/Mage.Sets/src/mage/sets/zendikar/AridMesa.java index c3dfa2e08ea..16c779409af 100644 --- a/Mage.Sets/src/mage/sets/zendikar/AridMesa.java +++ b/Mage.Sets/src/mage/sets/zendikar/AridMesa.java @@ -38,13 +38,26 @@ import mage.sets.Zendikar; * * @author BetaSteward_at_googlemail.com */ -public class AridMesa extends CardImpl { +public class AridMesa extends CardImpl { public AridMesa(UUID ownerId) { super(ownerId, "Arid Mesa", new CardType[]{CardType.LAND}, null); this.expansionSetId = Zendikar.getInstance().getId(); - this.art = "123565_typ_reg_sty_010.jpg"; this.addAbility(new FetchLandActivatedAbility(new String[] {"Mountain", "Plains"})); } + public AridMesa(final AridMesa card) { + super(card); + } + + @Override + public AridMesa copy() { + return new AridMesa(this); + } + + @Override + public String getArt() { + return "123565_typ_reg_sty_010.jpg"; + } + } diff --git a/Mage.Sets/src/mage/sets/zendikar/BeastmasterAscension.java b/Mage.Sets/src/mage/sets/zendikar/BeastmasterAscension.java new file mode 100644 index 00000000000..25805f8a06e --- /dev/null +++ b/Mage.Sets/src/mage/sets/zendikar/BeastmasterAscension.java @@ -0,0 +1,140 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.sets.zendikar; + +import java.util.UUID; +import mage.Constants.CardType; +import mage.Constants.Duration; +import mage.Constants.Zone; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.AddCountersSourceEffect; +import mage.abilities.effects.common.BoostControlledEffect; +import mage.cards.CardImpl; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; +import mage.game.permanent.Permanent; +import mage.sets.Zendikar; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class BeastmasterAscension extends CardImpl { + + public BeastmasterAscension(UUID ownerId) { + super(ownerId, "Beastmaster Ascension", new CardType[]{CardType.ENCHANTMENT}, "{2}{G}"); + this.expansionSetId = Zendikar.getInstance().getId(); + this.color.setGreen(true); + + this.addAbility(new BeastmasterAscensionAbility()); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BeastmasterAscensionEffect())); + } + + public BeastmasterAscension(final BeastmasterAscension card) { + super(card); + } + + @Override + public BeastmasterAscension copy() { + return new BeastmasterAscension(this); + } + + @Override + public String getArt() { + return "125085_typ_reg_sty_010.jpg"; + } + +} + +class BeastmasterAscensionAbility extends TriggeredAbilityImpl { + + public BeastmasterAscensionAbility() { + super(Zone.BATTLEFIELD, new AddCountersSourceEffect("quest", 1), true); + } + + public BeastmasterAscensionAbility(final BeastmasterAscensionAbility ability) { + super(ability); + } + + @Override + public BeastmasterAscensionAbility copy() { + return new BeastmasterAscensionAbility(this); + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (event.getType() == EventType.ATTACKER_DECLARED) { + Permanent source = game.getPermanent(event.getSourceId()); + if (source != null && source.getControllerId().equals(controllerId)) { + trigger(game, event.getPlayerId()); + return true; + } + } + return false; + } + + @Override + public String getRule() { + return "Whenever a creature you control attacks, you may put a quest counter on Beastmaster Ascension."; + } + +} + +class BeastmasterAscensionEffect extends BoostControlledEffect { + + public BeastmasterAscensionEffect() { + super(5, 5, Duration.WhileOnBattlefield); + } + + public BeastmasterAscensionEffect(final BeastmasterAscensionEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getSourceId()); + if (permanent != null && permanent.getCounters().getCount("quest") > 6) { + super.apply(game, source); + } + return false; + } + + @Override + public BeastmasterAscensionEffect copy() { + return new BeastmasterAscensionEffect(this); + } + + @Override + public String getText(Ability source) { + return "As long as Beastmaster Ascension has seven or more quest counters on it, creatures you control get +5/+5"; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/zendikar/BraveTheElements.java b/Mage.Sets/src/mage/sets/zendikar/BraveTheElements.java index ba2b971dc47..cbb3c17c6e9 100644 --- a/Mage.Sets/src/mage/sets/zendikar/BraveTheElements.java +++ b/Mage.Sets/src/mage/sets/zendikar/BraveTheElements.java @@ -31,6 +31,7 @@ package mage.sets.zendikar; import java.util.UUID; import mage.Constants.CardType; import mage.Constants.Duration; +import mage.abilities.Ability; import mage.abilities.effects.common.GainAbilityControlledEffect; import mage.abilities.keyword.ProtectionAbility; import mage.cards.CardImpl; @@ -45,17 +46,30 @@ import mage.sets.Zendikar; * * @author BetaSteward_at_googlemail.com */ -public class BraveTheElements extends CardImpl { +public class BraveTheElements extends CardImpl { public BraveTheElements(UUID ownerId) { super(ownerId, "Brave the Elements", new CardType[]{CardType.INSTANT}, "{W}"); this.expansionSetId = Zendikar.getInstance().getId(); this.color.setWhite(true); - this.art = "123638_typ_reg_sty_010.jpg"; this.getSpellAbility().addChoice(new ChoiceColor()); this.getSpellAbility().addEffect(new BraveTheElementsEffect()); } + public BraveTheElements(final BraveTheElements card) { + super(card); + } + + @Override + public BraveTheElements copy() { + return new BraveTheElements(this); + } + + @Override + public String getArt() { + return "123638_typ_reg_sty_010.jpg"; + } + } class BraveTheElementsEffect extends GainAbilityControlledEffect { @@ -71,9 +85,20 @@ class BraveTheElementsEffect extends GainAbilityControlledEffect { filter2.setUseColor(true); } + public BraveTheElementsEffect(final BraveTheElementsEffect effect) { + super(effect); + this.filter1 = effect.filter1.copy(); + this.filter2 = effect.filter2.copy(); + } + @Override - public boolean apply(Game game) { - ChoiceColor choice = (ChoiceColor) this.source.getChoices().get(0); + public BraveTheElementsEffect copy() { + return new BraveTheElementsEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + ChoiceColor choice = (ChoiceColor) source.getChoices().get(0); filter2.setColor(choice.getColor()); filter2.setMessage(choice.getChoice()); for (Permanent perm: game.getBattlefield().getAllActivePermanents(filter1, source.getControllerId())) { @@ -83,7 +108,7 @@ class BraveTheElementsEffect extends GainAbilityControlledEffect { } @Override - public String getText() { + public String getText(Ability source) { return "Choose a color. White creatures you control gain protection from the chosen color until end of turn"; } diff --git a/Mage.Sets/src/mage/sets/zendikar/BurstLightning.java b/Mage.Sets/src/mage/sets/zendikar/BurstLightning.java index adae31baa78..0eb50d0ad02 100644 --- a/Mage.Sets/src/mage/sets/zendikar/BurstLightning.java +++ b/Mage.Sets/src/mage/sets/zendikar/BurstLightning.java @@ -41,13 +41,12 @@ import mage.target.common.TargetCreatureOrPlayer; * * @author BetaSteward_at_googlemail.com */ -public class BurstLightning extends CardImpl { +public class BurstLightning extends CardImpl { public BurstLightning(UUID ownerId) { super(ownerId, "Burst Lightning", new CardType[]{CardType.INSTANT}, "{R}"); this.expansionSetId = Zendikar.getInstance().getId(); this.color.setRed(true); - this.art = "123560_typ_reg_sty_010.jpg"; this.getSpellAbility().addTarget(new TargetCreatureOrPlayer()); this.getSpellAbility().addEffect(new DamageTargetEffect(2)); KickerAbility ability = new KickerAbility(new DamageTargetEffect(4), true); @@ -56,4 +55,18 @@ public class BurstLightning extends CardImpl { this.addAbility(ability); } + public BurstLightning(final BurstLightning card) { + super(card); + } + + @Override + public BurstLightning copy() { + return new BurstLightning(this); + } + + @Override + public String getArt() { + return "123560_typ_reg_sty_010.jpg"; + } + } diff --git a/Mage.Sets/src/mage/sets/zendikar/ConquerorsPledge.java b/Mage.Sets/src/mage/sets/zendikar/ConquerorsPledge.java index 66d11d5c877..90893ed8a4c 100644 --- a/Mage.Sets/src/mage/sets/zendikar/ConquerorsPledge.java +++ b/Mage.Sets/src/mage/sets/zendikar/ConquerorsPledge.java @@ -42,19 +42,32 @@ import mage.sets.Zendikar; * * @author BetaSteward_at_googlemail.com */ -public class ConquerorsPledge extends CardImpl { +public class ConquerorsPledge extends CardImpl { public ConquerorsPledge(UUID ownerId) { super(ownerId, "Conquerors Pledge", new CardType[]{CardType.SORCERY}, "{2}{W}{W}{W}"); this.expansionSetId = Zendikar.getInstance().getId(); this.color.setWhite(true); - this.art = "123781_typ_reg_sty_010.jpg"; this.getSpellAbility().addEffect(new CreateTokenEffect(new KorSoldierToken(), 6)); KickerAbility ability = new KickerAbility(new CreateTokenEffect(new KorSoldierToken(), 12), true); ability.addManaCost(new GenericManaCost(6)); this.addAbility(ability); } + public ConquerorsPledge(final ConquerorsPledge card) { + super(card); + } + + @Override + public ConquerorsPledge copy() { + return new ConquerorsPledge(this); + } + + @Override + public String getArt() { + return "123781_typ_reg_sty_010.jpg"; + } + } class KorSoldierToken extends Token { diff --git a/Mage.Sets/src/mage/sets/zendikar/DayOfJudgment.java b/Mage.Sets/src/mage/sets/zendikar/DayOfJudgment.java index ac72c0c38ad..6e2f6f959c9 100644 --- a/Mage.Sets/src/mage/sets/zendikar/DayOfJudgment.java +++ b/Mage.Sets/src/mage/sets/zendikar/DayOfJudgment.java @@ -39,14 +39,27 @@ import mage.sets.Zendikar; * * @author BetaSteward_at_googlemail.com */ -public class DayOfJudgment extends CardImpl { +public class DayOfJudgment extends CardImpl { public DayOfJudgment(UUID ownerId) { super(ownerId, "Day of Judgment", new CardType[]{CardType.SORCERY}, "{2}{W}{W}"); this.expansionSetId = Zendikar.getInstance().getId(); this.color.setWhite(true); - this.art = "123645_typ_reg_sty_010.jpg"; this.getSpellAbility().addEffect(new DestroyAllEffect(new FilterCreaturePermanent("creatures"))); } + public DayOfJudgment(final DayOfJudgment card) { + super(card); + } + + @Override + public DayOfJudgment copy() { + return new DayOfJudgment(this); + } + + @Override + public String getArt() { + return "123645_typ_reg_sty_010.jpg"; + } + } diff --git a/Mage.Sets/src/mage/sets/zendikar/EldraziMonument.java b/Mage.Sets/src/mage/sets/zendikar/EldraziMonument.java new file mode 100644 index 00000000000..640b58bca7c --- /dev/null +++ b/Mage.Sets/src/mage/sets/zendikar/EldraziMonument.java @@ -0,0 +1,131 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.sets.zendikar; + +import java.util.UUID; +import mage.Constants.CardType; +import mage.Constants.Duration; +import mage.Constants.Outcome; +import mage.Constants.Zone; +import mage.abilities.Ability; +import mage.abilities.common.OnEventTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.BoostControlledEffect; +import mage.abilities.effects.common.GainAbilityControlledEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.IndestructibleAbility; +import mage.cards.CardImpl; +import mage.filter.common.FilterCreaturePermanent; +import mage.game.Game; +import mage.game.events.GameEvent.EventType; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.sets.Zendikar; +import mage.target.common.TargetControlledPermanent; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class EldraziMonument extends CardImpl { + + private static FilterCreaturePermanent filter = new FilterCreaturePermanent("Creatures"); + + public EldraziMonument(UUID ownerId) { + super(ownerId, "Eldrazi Monument", new CardType[]{CardType.ARTIFACT}, "{5}"); + this.expansionSetId = Zendikar.getInstance().getId(); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostControlledEffect(1, 1, Duration.WhileOnBattlefield, filter))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityControlledEffect(FlyingAbility.getInstance(), Duration.WhileOnBattlefield, filter))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityControlledEffect(IndestructibleAbility.getInstance(), Duration.WhileOnBattlefield, filter))); + this.addAbility(new OnEventTriggeredAbility(EventType.UPKEEP_STEP_PRE, "beginning of your upkeep", new EldraziMonumentEffect())); + } + + public EldraziMonument(final EldraziMonument card) { + super(card); + } + + @Override + public EldraziMonument copy() { + return new EldraziMonument(this); + } + + @Override + public String getArt() { + return "123741_typ_reg_sty_010.jpg"; + } + +} + +class EldraziMonumentEffect extends OneShotEffect { + + private static FilterCreaturePermanent filter = new FilterCreaturePermanent(); + + public EldraziMonumentEffect() { + super(Outcome.Sacrifice); + } + + public EldraziMonumentEffect(final EldraziMonumentEffect ability) { + super(ability); + } + + @Override + public EldraziMonumentEffect copy() { + return new EldraziMonumentEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + TargetControlledPermanent target = new TargetControlledPermanent(1, 1, filter); + target.setRequired(true); + Player player = game.getPlayer(source.getControllerId()); + if (target.canChoose(source.getId(), source.getControllerId(), game)) { + player.chooseTarget(this.outcome, target, source, game); + Permanent permanent = game.getPermanent(target.getFirstTarget()); + if (permanent != null) { + return permanent.sacrifice(source.getSourceId(), game); + } + } + else { + Permanent permanent = game.getPermanent(source.getSourceId()); + if (permanent != null) { + return permanent.sacrifice(source.getSourceId(), game); + } + } + return false; + + } + + @Override + public String getText(Ability source) { + return "sacrifice a creature. If you can't, sacrifice Eldrazi Monument"; + } + +} + diff --git a/Mage.Sets/src/mage/sets/zendikar/EmeriaAngel.java b/Mage.Sets/src/mage/sets/zendikar/EmeriaAngel.java index 49ab125db9b..ca3c95ac365 100644 --- a/Mage.Sets/src/mage/sets/zendikar/EmeriaAngel.java +++ b/Mage.Sets/src/mage/sets/zendikar/EmeriaAngel.java @@ -42,14 +42,13 @@ import mage.sets.Zendikar; * * @author BetaSteward_at_googlemail.com */ -public class EmeriaAngel extends CardImpl { +public class EmeriaAngel extends CardImpl { public EmeriaAngel(UUID ownerId) { super(ownerId, "Emeria Angel", new CardType[]{CardType.CREATURE}, "{2}{W}{W}"); this.expansionSetId = Zendikar.getInstance().getId(); this.subtype.add("Angel"); this.color.setWhite(true); - this.art = "123678_typ_reg_sty_010.jpg"; this.power = new MageInt(3); this.toughness = new MageInt(3); @@ -57,6 +56,20 @@ public class EmeriaAngel extends CardImpl { this.addAbility(new LandfallAbility(new CreateTokenEffect(new BirdToken()), true)); } + public EmeriaAngel(final EmeriaAngel card) { + super(card); + } + + @Override + public EmeriaAngel copy() { + return new EmeriaAngel(this); + } + + @Override + public String getArt() { + return "123678_typ_reg_sty_010.jpg"; + } + } class BirdToken extends Token { diff --git a/Mage.Sets/src/mage/sets/zendikar/GoblinGuide.java b/Mage.Sets/src/mage/sets/zendikar/GoblinGuide.java index 512dc55687c..41ef93a620c 100644 --- a/Mage.Sets/src/mage/sets/zendikar/GoblinGuide.java +++ b/Mage.Sets/src/mage/sets/zendikar/GoblinGuide.java @@ -33,6 +33,7 @@ import mage.Constants.CardType; import mage.Constants.Outcome; import mage.Constants.Zone; import mage.MageInt; +import mage.abilities.Ability; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.HasteAbility; @@ -49,7 +50,7 @@ import mage.sets.Zendikar; * * @author BetaSteward_at_googlemail.com */ -public class GoblinGuide extends CardImpl { +public class GoblinGuide extends CardImpl { public GoblinGuide(UUID ownerId) { super(ownerId, "Goblin Guide", new CardType[]{CardType.CREATURE}, "{R}"); @@ -57,32 +58,54 @@ public class GoblinGuide extends CardImpl { this.color.setRed(true); this.subtype.add("Goblin"); this.subtype.add("Scout"); - this.art = "123540_typ_reg_sty_010.jpg"; this.power = new MageInt(2); this.toughness = new MageInt(2); this.addAbility(HasteAbility.getInstance()); this.addAbility(new AttacksTriggeredAbility(new GoblinGuideEffect(), false)); } + public GoblinGuide(final GoblinGuide card) { + super(card); + } + + @Override + public GoblinGuide copy() { + return new GoblinGuide(this); + } + + @Override + public String getArt() { + return "123540_typ_reg_sty_010.jpg"; + } + } -class GoblinGuideEffect extends OneShotEffect { +class GoblinGuideEffect extends OneShotEffect { public GoblinGuideEffect() { super(Outcome.DrawCard); } + public GoblinGuideEffect(final GoblinGuideEffect effect) { + super(effect); + } + @Override - public boolean apply(Game game) { + public GoblinGuideEffect copy() { + return new GoblinGuideEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { Player defender = null; for (CombatGroup group: game.getCombat().getGroups()) { - if (group.getAttackers().contains(this.source.getSourceId())) { + if (group.getAttackers().contains(source.getSourceId())) { defender = game.getPlayer(group.getDefenderId()); break; } } if (defender != null) { - Cards cards = new CardsImpl(Zone.REVEALED); + Cards cards = new CardsImpl(); Card card = defender.getLibrary().getFromTop(game); if (card != null) { cards.add(card); @@ -97,7 +120,7 @@ class GoblinGuideEffect extends OneShotEffect { } @Override - public String getText() { + public String getText(Ability source) { return "defending player reveals the top card of his or her library. If it's a land card, that player puts it into his or her hand"; } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/zendikar/GoblinRuinblaster.java b/Mage.Sets/src/mage/sets/zendikar/GoblinRuinblaster.java index 9ae4fa5d12f..fb09f0de31a 100644 --- a/Mage.Sets/src/mage/sets/zendikar/GoblinRuinblaster.java +++ b/Mage.Sets/src/mage/sets/zendikar/GoblinRuinblaster.java @@ -46,7 +46,7 @@ import mage.target.common.TargetNonBasicLandPermanent; * * @author BetaSteward_at_googlemail.com */ -public class GoblinRuinblaster extends CardImpl { +public class GoblinRuinblaster extends CardImpl { public GoblinRuinblaster(UUID ownerId) { super(ownerId, "Goblin Ruinblaster", new CardType[]{CardType.CREATURE}, "{2}{R}"); @@ -54,15 +54,28 @@ public class GoblinRuinblaster extends CardImpl { this.color.setRed(true); this.subtype.add("Goblin"); this.subtype.add("Shaman"); - this.art = "123599_typ_reg_sty_010.jpg"; this.power = new MageInt(2); this.toughness = new MageInt(1); this.addAbility(HasteAbility.getInstance()); KickerAbility ability = new KickerAbility(new EntersBattlefieldEffect(new DestroyTargetEffect()), false); - ability.getTargets().add(new TargetNonBasicLandPermanent()); + ability.addTarget(new TargetNonBasicLandPermanent()); ability.addManaCost(new ColoredManaCost(ColoredManaSymbol.R)); this.addAbility(ability); } + + public GoblinRuinblaster(final GoblinRuinblaster card) { + super(card); + } + + @Override + public GoblinRuinblaster copy() { + return new GoblinRuinblaster(this); + } + + @Override + public String getArt() { + return "123599_typ_reg_sty_010.jpg"; + } } diff --git a/Mage.Sets/src/mage/sets/zendikar/KabiraCrossroads.java b/Mage.Sets/src/mage/sets/zendikar/KabiraCrossroads.java index 4ab7451bba7..dfd56716898 100644 --- a/Mage.Sets/src/mage/sets/zendikar/KabiraCrossroads.java +++ b/Mage.Sets/src/mage/sets/zendikar/KabiraCrossroads.java @@ -42,14 +42,27 @@ import mage.sets.Zendikar; * * @author BetaSteward_at_googlemail.com */ -public class KabiraCrossroads extends CardImpl { +public class KabiraCrossroads extends CardImpl { public KabiraCrossroads(UUID ownerId) { super(ownerId, "Kabira Crossroads", new CardType[]{CardType.LAND}, null); this.expansionSetId = Zendikar.getInstance().getId(); - this.art = "126610_typ_reg_sty_010.jpg"; this.addAbility(new EntersBattlefieldStaticAbility(new EntersBattlefieldTappedEffect())); this.addAbility(new EntersBattlefieldTriggeredAbility(new GainLifeEffect(2), false)); this.addAbility(new WhiteManaAbility()); } + + public KabiraCrossroads(final KabiraCrossroads card) { + super(card); + } + + @Override + public KabiraCrossroads copy() { + return new KabiraCrossroads(this); + } + + @Override + public String getArt() { + return "126610_typ_reg_sty_010.jpg"; + } } diff --git a/Mage.Sets/src/mage/sets/zendikar/LotusCobra.java b/Mage.Sets/src/mage/sets/zendikar/LotusCobra.java index 8a3f161c422..c49cb1c1780 100644 --- a/Mage.Sets/src/mage/sets/zendikar/LotusCobra.java +++ b/Mage.Sets/src/mage/sets/zendikar/LotusCobra.java @@ -41,14 +41,13 @@ import mage.sets.Zendikar; * * @author BetaSteward_at_googlemail.com */ -public class LotusCobra extends CardImpl { +public class LotusCobra extends CardImpl { public LotusCobra(UUID ownerId) { super(ownerId, "Lotus Cobra", new CardType[]{CardType.CREATURE}, "{1}{G}"); this.expansionSetId = Zendikar.getInstance().getId(); this.subtype.add("Snake"); this.color.setGreen(true); - this.art = "123641_typ_reg_sty_010.jpg"; this.power = new MageInt(2); this.toughness = new MageInt(1); @@ -57,4 +56,18 @@ public class LotusCobra extends CardImpl { this.addAbility(ability); } + public LotusCobra(final LotusCobra card) { + super(card); + } + + @Override + public LotusCobra copy() { + return new LotusCobra(this); + } + + @Override + public String getArt() { + return "123641_typ_reg_sty_010.jpg"; + } + } diff --git a/Mage.Sets/src/mage/sets/zendikar/MarshFlats.java b/Mage.Sets/src/mage/sets/zendikar/MarshFlats.java index 4334931e45b..a96699a56b9 100644 --- a/Mage.Sets/src/mage/sets/zendikar/MarshFlats.java +++ b/Mage.Sets/src/mage/sets/zendikar/MarshFlats.java @@ -38,14 +38,27 @@ import mage.sets.Zendikar; * * @author BetaSteward_at_googlemail.com */ -public class MarshFlats extends CardImpl { +public class MarshFlats extends CardImpl { public MarshFlats(UUID ownerId) { super(ownerId, "Marsh Flats", new CardType[]{CardType.LAND}, null); this.expansionSetId = Zendikar.getInstance().getId(); - this.art = "123707_typ_reg_sty_010.jpg"; this.addAbility(new FetchLandActivatedAbility(new String[] {"Swamp", "Plains"})); } + public MarshFlats(final MarshFlats card) { + super(card); + } + + @Override + public MarshFlats copy() { + return new MarshFlats(this); + } + + @Override + public String getArt() { + return "123707_typ_reg_sty_010.jpg"; + } + } diff --git a/Mage.Sets/src/mage/sets/zendikar/MistyRainforest.java b/Mage.Sets/src/mage/sets/zendikar/MistyRainforest.java index b4d4574384d..f9b0ca97e62 100644 --- a/Mage.Sets/src/mage/sets/zendikar/MistyRainforest.java +++ b/Mage.Sets/src/mage/sets/zendikar/MistyRainforest.java @@ -38,14 +38,27 @@ import mage.sets.Zendikar; * * @author BetaSteward_at_googlemail.com */ -public class MistyRainforest extends CardImpl { +public class MistyRainforest extends CardImpl { public MistyRainforest(UUID ownerId) { super(ownerId, "Misty Rainforest", new CardType[]{CardType.LAND}, null); this.expansionSetId = Zendikar.getInstance().getId(); - this.art = "123689_typ_reg_sty_010.jpg"; this.addAbility(new FetchLandActivatedAbility(new String[] {"Forest", "Island"})); } + public MistyRainforest(final MistyRainforest card) { + super(card); + } + + @Override + public MistyRainforest copy() { + return new MistyRainforest(this); + } + + @Override + public String getArt() { + return "123689_typ_reg_sty_010.jpg"; + } + } diff --git a/Mage.Sets/src/mage/sets/zendikar/OranRiefTheVastwood.java b/Mage.Sets/src/mage/sets/zendikar/OranRiefTheVastwood.java index aede50ece06..0d3952ac173 100644 --- a/Mage.Sets/src/mage/sets/zendikar/OranRiefTheVastwood.java +++ b/Mage.Sets/src/mage/sets/zendikar/OranRiefTheVastwood.java @@ -32,6 +32,7 @@ import java.util.UUID; import mage.Constants.CardType; import mage.Constants.Outcome; import mage.Constants.Zone; +import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldStaticAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; @@ -49,31 +50,54 @@ import mage.sets.Zendikar; * * @author BetaSteward_at_googlemail.com */ -public class OranRiefTheVastwood extends CardImpl { +public class OranRiefTheVastwood extends CardImpl { public OranRiefTheVastwood(UUID ownerId) { super(ownerId, "Oran-Rief, the Vastwood", new CardType[]{CardType.LAND}, null); this.expansionSetId = Zendikar.getInstance().getId(); - this.art = "123692_typ_reg_sty_010.jpg"; +// this.art = "123692_typ_reg_sty_010.jpg"; this.addAbility(new EntersBattlefieldStaticAbility(new EntersBattlefieldTappedEffect())); this.addAbility(new GreenManaAbility()); this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new OranRiefTheVastwoodEffect(), new TapSourceCost())); } + public OranRiefTheVastwood(final OranRiefTheVastwood card) { + super(card); + } + + @Override + public OranRiefTheVastwood copy() { + return new OranRiefTheVastwood(this); + } + + @Override + public String getArt() { + return "123692_typ_reg_sty_010.jpg"; + } + } -class OranRiefTheVastwoodEffect extends OneShotEffect { +class OranRiefTheVastwoodEffect extends OneShotEffect { public OranRiefTheVastwoodEffect() { super(Outcome.BoostCreature); } + public OranRiefTheVastwoodEffect(final OranRiefTheVastwoodEffect effect) { + super(effect); + } + @Override - public boolean apply(Game game) { + public OranRiefTheVastwoodEffect copy() { + return new OranRiefTheVastwoodEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { FilterPermanent filter = new FilterPermanent(); filter.getCardType().add(CardType.CREATURE); filter.getColor().setGreen(true); - for (Permanent permanent: game.getBattlefield().getActivePermanents(filter, this.source.getControllerId(), game)) { + for (Permanent permanent: game.getBattlefield().getActivePermanents(filter, source.getControllerId(), game)) { if (permanent.getTurnsOnBattlefield() == 0) { permanent.getCounters().addCounter(new PlusOneCounter()); } @@ -82,7 +106,7 @@ class OranRiefTheVastwoodEffect extends OneShotEffect { } @Override - public String getText() { + public String getText(Ability source) { return "Put a +1/+1 counter on each green creature that entered the battlefield this turn"; } diff --git a/Mage.Sets/src/mage/sets/zendikar/RampagingBaloths.java b/Mage.Sets/src/mage/sets/zendikar/RampagingBaloths.java index 4489238c102..d7fa3bf150e 100644 --- a/Mage.Sets/src/mage/sets/zendikar/RampagingBaloths.java +++ b/Mage.Sets/src/mage/sets/zendikar/RampagingBaloths.java @@ -42,20 +42,33 @@ import mage.sets.Zendikar; * * @author BetaSteward_at_googlemail.com */ -public class RampagingBaloths extends CardImpl { +public class RampagingBaloths extends CardImpl { public RampagingBaloths(UUID ownerId) { super(ownerId, "Rampaging Baloths", new CardType[]{CardType.CREATURE}, "{4}{G}{G}"); this.expansionSetId = Zendikar.getInstance().getId(); this.subtype.add("Beast"); this.color.setGreen(true); - this.art = "123726_typ_reg_sty_010.jpg"; this.power = new MageInt(6); this.toughness = new MageInt(6); this.addAbility(TrampleAbility.getInstance()); this.addAbility(new LandfallAbility(new CreateTokenEffect(new RampagingBalothsToken()), true)); } + public RampagingBaloths(final RampagingBaloths card) { + super(card); + } + + @Override + public RampagingBaloths copy() { + return new RampagingBaloths(this); + } + + @Override + public String getArt() { + return "123726_typ_reg_sty_010.jpg"; + } + } class RampagingBalothsToken extends Token { diff --git a/Mage.Sets/src/mage/sets/zendikar/RiverBoa.java b/Mage.Sets/src/mage/sets/zendikar/RiverBoa.java new file mode 100644 index 00000000000..340d5ffb76b --- /dev/null +++ b/Mage.Sets/src/mage/sets/zendikar/RiverBoa.java @@ -0,0 +1,82 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.sets.zendikar; + +import java.util.UUID; +import mage.Constants.CardType; +import mage.Constants.Zone; +import mage.MageInt; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.RegenerateSourceEffect; +import mage.abilities.keyword.LandwalkAbility; +import mage.cards.CardImpl; +import mage.filter.Filter.ComparisonScope; +import mage.filter.common.FilterLandPermanent; +import mage.sets.Zendikar; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class RiverBoa extends CardImpl { + + private static FilterLandPermanent filter = new FilterLandPermanent("Island"); + + static { + filter.getSubtype().add("Island"); + filter.setScopeSubtype(ComparisonScope.Any); + } + + public RiverBoa(UUID ownerId) { + super(ownerId, "River Boa", new CardType[]{CardType.CREATURE}, "{1}{G}"); + this.expansionSetId = Zendikar.getInstance().getId(); + this.color.setGreen(true); + this.subtype.add("Snake"); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + this.addAbility(new LandwalkAbility(filter)); + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new RegenerateSourceEffect(), new ManaCostsImpl("{G}"))); + } + + public RiverBoa(final RiverBoa card) { + super(card); + } + + @Override + public RiverBoa copy() { + return new RiverBoa(this); + } + + @Override + public String getArt() { + return "125067_typ_reg_sty_010.jpg"; + } + +} diff --git a/Mage.Sets/src/mage/sets/zendikar/ScaldingTarn.java b/Mage.Sets/src/mage/sets/zendikar/ScaldingTarn.java index 868d2b0acd9..141b6aace27 100644 --- a/Mage.Sets/src/mage/sets/zendikar/ScaldingTarn.java +++ b/Mage.Sets/src/mage/sets/zendikar/ScaldingTarn.java @@ -38,14 +38,27 @@ import mage.sets.Zendikar; * * @author BetaSteward_at_googlemail.com */ -public class ScaldingTarn extends CardImpl { +public class ScaldingTarn extends CardImpl { public ScaldingTarn(UUID ownerId) { super(ownerId, "Scalding Tarn", new CardType[]{CardType.LAND}, null); this.expansionSetId = Zendikar.getInstance().getId(); - this.art = "123672_typ_reg_sty_010.jpg"; this.addAbility(new FetchLandActivatedAbility(new String[] {"Island", "Mountain"})); } + public ScaldingTarn(final ScaldingTarn card) { + super(card); + } + + @Override + public ScaldingTarn copy() { + return new ScaldingTarn(this); + } + + @Override + public String getArt() { + return "123672_typ_reg_sty_010.jpg"; + } + } diff --git a/Mage.Sets/src/mage/sets/zendikar/ScuteMob.java b/Mage.Sets/src/mage/sets/zendikar/ScuteMob.java index 793ddd3fcea..7737244d635 100644 --- a/Mage.Sets/src/mage/sets/zendikar/ScuteMob.java +++ b/Mage.Sets/src/mage/sets/zendikar/ScuteMob.java @@ -45,22 +45,35 @@ import mage.sets.Zendikar; * * @author BetaSteward_at_googlemail.com */ -public class ScuteMob extends CardImpl { +public class ScuteMob extends CardImpl { public ScuteMob(UUID ownerId) { super(ownerId, "Scute Mob", new CardType[]{CardType.CREATURE}, "{G}"); this.expansionSetId = Zendikar.getInstance().getId(); this.color.setGreen(true); this.subtype.add("Insect"); - this.art = "123606_typ_reg_sty_010.jpg"; this.power = new MageInt(1); this.toughness = new MageInt(1); this.addAbility(new ScuteMobAbility()); } + public ScuteMob(final ScuteMob card) { + super(card); + } + + @Override + public ScuteMob copy() { + return new ScuteMob(this); + } + + @Override + public String getArt() { + return "123606_typ_reg_sty_010.jpg"; + } + } -class ScuteMobAbility extends TriggeredAbilityImpl { +class ScuteMobAbility extends TriggeredAbilityImpl { private FilterLandPermanent filter = new FilterLandPermanent(); @@ -68,6 +81,15 @@ class ScuteMobAbility extends TriggeredAbilityImpl { super(Zone.BATTLEFIELD, new AddPlusOneCountersSourceEffect(4)); } + public ScuteMobAbility(final ScuteMobAbility ability) { + super(ability); + } + + @Override + public ScuteMobAbility copy() { + return new ScuteMobAbility(this); + } + @Override public boolean checkTrigger(GameEvent event, Game game) { if (event.getType() == EventType.UPKEEP_STEP_PRE && event.getPlayerId().equals(this.controllerId)) { diff --git a/Mage.Sets/src/mage/sets/zendikar/SpreadingSeas.java b/Mage.Sets/src/mage/sets/zendikar/SpreadingSeas.java index 03e9a7f1484..49f183f6130 100644 --- a/Mage.Sets/src/mage/sets/zendikar/SpreadingSeas.java +++ b/Mage.Sets/src/mage/sets/zendikar/SpreadingSeas.java @@ -54,13 +54,12 @@ import mage.target.common.TargetLandPermanent; * * @author BetaSteward_at_googlemail.com */ -public class SpreadingSeas extends CardImpl { +public class SpreadingSeas extends CardImpl { public SpreadingSeas(UUID ownerId) { super(ownerId, "Spreading Seas", new CardType[]{CardType.ENCHANTMENT}, "{1}{U}"); this.expansionSetId = Zendikar.getInstance().getId(); this.color.setBlue(true); - this.art = "123683_typ_reg_sty_010.jpg"; this.subtype.add("Aura"); TargetPermanent auraTarget = new TargetLandPermanent(); @@ -72,20 +71,43 @@ public class SpreadingSeas extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpreadingSeasEffect())); } + + public SpreadingSeas(final SpreadingSeas card) { + super(card); + } + + @Override + public SpreadingSeas copy() { + return new SpreadingSeas(this); + } + + @Override + public String getArt() { + return "123683_typ_reg_sty_010.jpg"; + } } -class SpreadingSeasEffect extends ContinuousEffectImpl { +class SpreadingSeasEffect extends ContinuousEffectImpl { public SpreadingSeasEffect() { super(Duration.WhileOnBattlefield, Outcome.Detriment); } + public SpreadingSeasEffect(final SpreadingSeasEffect effect) { + super(effect); + } + @Override - public boolean apply(Layer layer, SubLayer sublayer, Game game) { - Permanent enchantment = game.getPermanent(this.source.getSourceId()); + public SpreadingSeasEffect copy() { + return new SpreadingSeasEffect(this); + } + + @Override + public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + Permanent enchantment = game.getPermanent(source.getSourceId()); if (enchantment.getAttachedTo() != null) { Permanent land = game.getPermanent(enchantment.getAttachedTo()); - if (land != null) { + if (land != null) { switch (layer) { case TypeChangingEffects_4: if (sublayer == SubLayer.NA) { @@ -107,7 +129,7 @@ class SpreadingSeasEffect extends ContinuousEffectImpl { } @Override - public boolean apply(Game game) { + public boolean apply(Game game, Ability source) { return false; } @@ -117,7 +139,7 @@ class SpreadingSeasEffect extends ContinuousEffectImpl { } @Override - public String getText() { + public String getText(Ability source) { return "Enchanted land is an Island."; } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/zendikar/SteppeLynx.java b/Mage.Sets/src/mage/sets/zendikar/SteppeLynx.java index 72e8f1a3311..c58caedcfc2 100644 --- a/Mage.Sets/src/mage/sets/zendikar/SteppeLynx.java +++ b/Mage.Sets/src/mage/sets/zendikar/SteppeLynx.java @@ -41,18 +41,31 @@ import mage.sets.Zendikar; * * @author BetaSteward_at_googlemail.com */ -public class SteppeLynx extends CardImpl { +public class SteppeLynx extends CardImpl { public SteppeLynx(UUID ownerId) { super(ownerId, "Steppe Lynx", new CardType[]{CardType.CREATURE}, "{W}"); this.expansionSetId = Zendikar.getInstance().getId(); this.subtype.add("Cat"); this.color.setWhite(true); - this.art = "123546_typ_reg_sty_010.jpg"; this.power = new MageInt(0); this.toughness = new MageInt(1); this.addAbility(new LandfallAbility(new BoostSourceEffect(2, 2, Duration.EndOfTurn), false)); } + public SteppeLynx(final SteppeLynx card) { + super(card); + } + + @Override + public SteppeLynx copy() { + return new SteppeLynx(this); + } + + @Override + public String getArt() { + return "123546_typ_reg_sty_010.jpg"; + } + } diff --git a/Mage.Sets/src/mage/sets/zendikar/SunspringExpedition.java b/Mage.Sets/src/mage/sets/zendikar/SunspringExpedition.java index f6c15333afa..e6f9b135182 100644 --- a/Mage.Sets/src/mage/sets/zendikar/SunspringExpedition.java +++ b/Mage.Sets/src/mage/sets/zendikar/SunspringExpedition.java @@ -34,6 +34,9 @@ import mage.Constants.Zone; import mage.abilities.ActivatedAbility; import mage.abilities.ActivatedAbilityImpl; import mage.abilities.common.LandfallAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.Costs; +import mage.abilities.costs.CostsImpl; import mage.abilities.costs.common.RemoveCountersSourceCost; import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.effects.common.AddCountersSourceEffect; @@ -45,20 +48,33 @@ import mage.sets.Zendikar; * * @author BetaSteward_at_googlemail.com */ -public class SunspringExpedition extends CardImpl { +public class SunspringExpedition extends CardImpl { public SunspringExpedition(UUID ownerId) { super(ownerId, "Sunspring Expedition", new CardType[]{CardType.ENCHANTMENT}, "{W}"); this.expansionSetId = Zendikar.getInstance().getId(); this.color.setWhite(true); - this.art = "123558_typ_reg_sty_010.jpg"; this.addAbility(new LandfallAbility(new AddCountersSourceEffect("quest", 1), true)); - ActivatedAbility ability = new ActivatedAbilityImpl(Zone.BATTLEFIELD, new GainLifeEffect(8)) {}; - ability.addCost(new RemoveCountersSourceCost("quest", 3)); - ability.addCost(new SacrificeSourceCost()); + Costs costs = new CostsImpl(); + costs.add(new RemoveCountersSourceCost("quest", 3)); + costs.add(new SacrificeSourceCost()); + ActivatedAbility ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new GainLifeEffect(8), costs); this.addAbility(ability); } + public SunspringExpedition(final SunspringExpedition card) { + super(card); + } + + @Override + public SunspringExpedition copy() { + return new SunspringExpedition(this); + } + + @Override + public String getArt() { + return "123558_typ_reg_sty_010.jpg"; + } } diff --git a/Mage.Sets/src/mage/sets/zendikar/TeeteringPeaks.java b/Mage.Sets/src/mage/sets/zendikar/TeeteringPeaks.java index 4665cf6951b..cb10a66489c 100644 --- a/Mage.Sets/src/mage/sets/zendikar/TeeteringPeaks.java +++ b/Mage.Sets/src/mage/sets/zendikar/TeeteringPeaks.java @@ -45,12 +45,11 @@ import mage.target.common.TargetCreaturePermanent; * * @author BetaSteward_at_googlemail.com */ -public class TeeteringPeaks extends CardImpl { +public class TeeteringPeaks extends CardImpl { public TeeteringPeaks(UUID ownerId) { super(ownerId, "Teetering Peaks", new CardType[]{CardType.LAND}, null); this.expansionSetId = Zendikar.getInstance().getId(); - this.art = "123557_typ_reg_sty_010.jpg"; this.addAbility(new EntersBattlefieldStaticAbility(new EntersBattlefieldTappedEffect())); Ability ability = new EntersBattlefieldTriggeredAbility(new BoostTargetEffect(2, 0, Duration.EndOfTurn), false); ability.addTarget(new TargetCreaturePermanent()); @@ -58,4 +57,18 @@ public class TeeteringPeaks extends CardImpl { this.addAbility(new RedManaAbility()); } + public TeeteringPeaks(final TeeteringPeaks card) { + super(card); + } + + @Override + public TeeteringPeaks copy() { + return new TeeteringPeaks(this); + } + + @Override + public String getArt() { + return "123557_typ_reg_sty_010.jpg"; + } + } diff --git a/Mage.Sets/src/mage/sets/zendikar/VerdantCatacombs.java b/Mage.Sets/src/mage/sets/zendikar/VerdantCatacombs.java index 0ae746f935d..73e3d24513f 100644 --- a/Mage.Sets/src/mage/sets/zendikar/VerdantCatacombs.java +++ b/Mage.Sets/src/mage/sets/zendikar/VerdantCatacombs.java @@ -38,14 +38,27 @@ import mage.sets.Zendikar; * * @author BetaSteward_at_googlemail.com */ -public class VerdantCatacombs extends CardImpl { +public class VerdantCatacombs extends CardImpl { public VerdantCatacombs(UUID ownerId) { super(ownerId, "Verdant Catacombs", new CardType[]{CardType.LAND}, null); this.expansionSetId = Zendikar.getInstance().getId(); - this.art = "123743_typ_reg_sty_010.jpg"; this.addAbility(new FetchLandActivatedAbility(new String[] {"Forest", "Swamp"})); } + public VerdantCatacombs(final VerdantCatacombs card) { + super(card); + } + + @Override + public VerdantCatacombs copy() { + return new VerdantCatacombs(this); + } + + @Override + public String getArt() { + return "123743_typ_reg_sty_010.jpg"; + } + } diff --git a/Mage/src/mage/abilities/Abilities.java b/Mage/src/mage/abilities/Abilities.java index f85b5cce42f..78af1b21b69 100644 --- a/Mage/src/mage/abilities/Abilities.java +++ b/Mage/src/mage/abilities/Abilities.java @@ -28,33 +28,34 @@ package mage.abilities; +import java.io.Serializable; import java.util.List; import java.util.UUID; import mage.Constants.Zone; import mage.abilities.keyword.KickerAbility; import mage.abilities.keyword.ProtectionAbility; import mage.abilities.mana.ManaAbility; +import mage.filter.FilterAbility; -public interface Abilities extends List { +public interface Abilities extends List, Serializable { public List getRules(); - public List getActivatedAbilities(Zone zone); - public List getManaAbilities(Zone zone); - public List getStaticAbilities(Zone zone); - public List getEvasionAbilities(); - public List getTriggeredAbilities(Zone zone); - public List getProtectionAbilities(); - public List getKickerAbilities(); + public Abilities getActivatedAbilities(Zone zone); + public Abilities getActivatedAbilities(Zone zone, FilterAbility filter); + public Abilities getManaAbilities(Zone zone); + public Abilities getStaticAbilities(Zone zone); + public Abilities getEvasionAbilities(); + public Abilities getTriggeredAbilities(Zone zone); + public Abilities getProtectionAbilities(); + public Abilities getKickerAbilities(); + public int getOutcomeTotal(); - public Abilities copy(); - public void setControllerId(UUID controllerId); public void setSourceId(UUID sourceId); public boolean containsKey(UUID abilityId); - public Ability get(UUID abilityId); - - public boolean containsAll(Abilities abilities); - + public T get(UUID abilityId); + public boolean containsAll(Abilities abilities); + public Abilities copy(); } diff --git a/Mage/src/mage/abilities/AbilitiesImpl.java b/Mage/src/mage/abilities/AbilitiesImpl.java index b2ca281b834..93eb1e91c5a 100644 --- a/Mage/src/mage/abilities/AbilitiesImpl.java +++ b/Mage/src/mage/abilities/AbilitiesImpl.java @@ -28,7 +28,6 @@ package mage.abilities; -import java.io.Serializable; import java.util.ArrayList; import java.util.List; import java.util.UUID; @@ -36,20 +35,32 @@ import mage.Constants.Zone; import mage.abilities.keyword.KickerAbility; import mage.abilities.keyword.ProtectionAbility; import mage.abilities.mana.ManaAbility; -import mage.util.Copier; +import mage.filter.FilterAbility; /** * * @author BetaSteward_at_googlemail.com */ -public class AbilitiesImpl extends ArrayList implements Abilities, Serializable { +public class AbilitiesImpl extends ArrayList implements Abilities { + public AbilitiesImpl() {} + + public AbilitiesImpl(final AbilitiesImpl abilities) { + for (T ability: abilities) { + this.add((T)ability.copy()); + } + } + + @Override + public AbilitiesImpl copy() { + return new AbilitiesImpl(this); + } @Override public List getRules() { List rules = new ArrayList(); - for (Ability ability:this) { + for (T ability:this) { if (!(ability instanceof SpellAbility || ability instanceof PlayLandAbility)) rules.add(ability.getRule()); } @@ -58,9 +69,9 @@ public class AbilitiesImpl extends ArrayList implements Abilities, Seri } @Override - public List getActivatedAbilities(Zone zone) { - List zonedAbilities = new ArrayList(); - for (Ability ability: this) { + public Abilities getActivatedAbilities(Zone zone) { + Abilities zonedAbilities = new AbilitiesImpl(); + for (T ability: this) { if (ability instanceof ActivatedAbility && ability.getZone().match(zone)) { zonedAbilities.add((ActivatedAbility)ability); } @@ -69,14 +80,20 @@ public class AbilitiesImpl extends ArrayList implements Abilities, Seri } @Override - public Abilities copy() { - return new Copier().copy(this); + public Abilities getActivatedAbilities(Zone zone, FilterAbility filter) { + Abilities zonedAbilities = new AbilitiesImpl(); + for (T ability: this) { + if (ability instanceof ActivatedAbility && ability.getZone().match(zone) && filter.match(ability)) { + zonedAbilities.add((ActivatedAbility)ability); + } + } + return zonedAbilities; } @Override - public List getManaAbilities(Zone zone) { - List abilities = new ArrayList(); - for (Ability ability: this) { + public Abilities getManaAbilities(Zone zone) { + Abilities abilities = new AbilitiesImpl(); + for (T ability: this) { if (ability instanceof ManaAbility && ability.getZone().match(zone)) { abilities.add((ManaAbility)ability); } @@ -85,9 +102,9 @@ public class AbilitiesImpl extends ArrayList implements Abilities, Seri } @Override - public List getEvasionAbilities() { - List abilities = new ArrayList(); - for (Ability ability: this) { + public Abilities getEvasionAbilities() { + Abilities abilities = new AbilitiesImpl(); + for (T ability: this) { if (ability instanceof EvasionAbility) { abilities.add((EvasionAbility)ability); } @@ -96,9 +113,9 @@ public class AbilitiesImpl extends ArrayList implements Abilities, Seri } @Override - public List getStaticAbilities(Zone zone) { - List zonedAbilities = new ArrayList(); - for (Ability ability: this) { + public Abilities getStaticAbilities(Zone zone) { + Abilities zonedAbilities = new AbilitiesImpl(); + for (T ability: this) { if (ability instanceof StaticAbility && ability.getZone().match(zone)) { zonedAbilities.add((StaticAbility)ability); } @@ -107,9 +124,9 @@ public class AbilitiesImpl extends ArrayList implements Abilities, Seri } @Override - public List getTriggeredAbilities(Zone zone) { - List zonedAbilities = new ArrayList(); - for (Ability ability: this) { + public Abilities getTriggeredAbilities(Zone zone) { + Abilities zonedAbilities = new AbilitiesImpl(); + for (T ability: this) { if (ability instanceof TriggeredAbility && ability.getZone().match(zone)) { zonedAbilities.add((TriggeredAbility)ability); } @@ -118,9 +135,9 @@ public class AbilitiesImpl extends ArrayList implements Abilities, Seri } @Override - public List getProtectionAbilities() { - List abilities = new ArrayList(); - for (Ability ability: this) { + public Abilities getProtectionAbilities() { + Abilities abilities = new AbilitiesImpl(); + for (T ability: this) { if (ability instanceof ProtectionAbility) { abilities.add((ProtectionAbility)ability); } @@ -129,9 +146,9 @@ public class AbilitiesImpl extends ArrayList implements Abilities, Seri } @Override - public List getKickerAbilities() { - List abilities = new ArrayList(); - for (Ability ability: this) { + public Abilities getKickerAbilities() { + Abilities abilities = new AbilitiesImpl(); + for (T ability: this) { if (ability instanceof KickerAbility) { abilities.add((KickerAbility)ability); } @@ -154,12 +171,12 @@ public class AbilitiesImpl extends ArrayList implements Abilities, Seri } @Override - public boolean containsAll(Abilities abilities) { + public boolean containsAll(Abilities abilities) { if (this.size() < abilities.size()) return false; - for (Ability ability: abilities) { + for (T ability: abilities) { boolean found = false; - for (Ability test: this) { + for (T test: this) { if (ability.getRule().equals(test.getRule())) { found = true; break; @@ -173,7 +190,7 @@ public class AbilitiesImpl extends ArrayList implements Abilities, Seri @Override public boolean containsKey(UUID abilityId) { - for (Ability ability: this) { + for (T ability: this) { if (ability.getId().equals(abilityId)) return true; } @@ -181,12 +198,21 @@ public class AbilitiesImpl extends ArrayList implements Abilities, Seri } @Override - public Ability get(UUID abilityId) { - for (Ability ability: this) { + public T get(UUID abilityId) { + for (T ability: this) { if (ability.getId().equals(abilityId)) return ability; } return null; } + @Override + public int getOutcomeTotal() { + int total = 0; + for (T ability: this) { + total += ability.getEffects().getOutcomeTotal(); + } + return total; + } + } diff --git a/Mage/src/mage/abilities/Ability.java b/Mage/src/mage/abilities/Ability.java index ecbb253fc04..6833c70002f 100644 --- a/Mage/src/mage/abilities/Ability.java +++ b/Mage/src/mage/abilities/Ability.java @@ -31,6 +31,7 @@ package mage.abilities; import java.io.Serializable; import java.util.List; import java.util.UUID; +import mage.Constants.AbilityType; import mage.Constants.Zone; import mage.abilities.costs.AlternativeCost; import mage.abilities.costs.Cost; @@ -48,11 +49,12 @@ import mage.target.Targets; public interface Ability extends Serializable { public UUID getId(); + public AbilityType getAbilityType(); public UUID getControllerId(); public UUID getSourceId(); public Costs getCosts(); public void addCost(Cost cost); - public ManaCosts getManaCosts(); + public ManaCosts getManaCosts(); public void addManaCost(ManaCost cost); public List getAlternativeCosts(); public void addAlternativeCost(AlternativeCost cost); @@ -64,14 +66,14 @@ public interface Ability extends Serializable { public Choices getChoices(); public void addChoice(Choice choice); public Zone getZone(); + public boolean isUsesStack(); public String getRule(); - public String getName(); public boolean activate(Game game, boolean noMana); public boolean resolve(Game game); public void reset(Game game); public void setControllerId(UUID controllerId); public void setSourceId(UUID sourceID); + public Ability copy(); - } diff --git a/Mage/src/mage/abilities/AbilityImpl.java b/Mage/src/mage/abilities/AbilityImpl.java index 87ed70c0c16..1abc3cc256e 100644 --- a/Mage/src/mage/abilities/AbilityImpl.java +++ b/Mage/src/mage/abilities/AbilityImpl.java @@ -28,10 +28,11 @@ package mage.abilities; -import java.io.Serializable; import java.util.ArrayList; import java.util.List; import java.util.UUID; +import java.util.logging.Logger; +import mage.Constants.AbilityType; import mage.Constants.Outcome; import mage.Constants.Zone; import mage.abilities.costs.AlternativeCost; @@ -40,6 +41,7 @@ import mage.abilities.costs.Costs; import mage.abilities.costs.CostsImpl; import mage.abilities.costs.mana.ManaCost; import mage.abilities.costs.mana.ManaCosts; +import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.Effect; import mage.abilities.effects.Effects; @@ -49,34 +51,60 @@ import mage.choices.Choices; import mage.game.Game; import mage.target.Target; import mage.target.Targets; -import mage.util.Copier; +import mage.util.Logging; /** * * @author BetaSteward_at_googlemail.com */ -public abstract class AbilityImpl implements Ability, Serializable { +public abstract class AbilityImpl> implements Ability { - protected UUID id; + private final static transient Logger logger = Logging.getLogger(AbilityImpl.class.getName()); + + protected final UUID id; + protected AbilityType abilityType; protected UUID controllerId; protected UUID sourceId; - protected ManaCosts manaCosts = new ManaCosts(this); - protected Costs costs = new CostsImpl(this); - protected List alternativeCosts = new ArrayList(); - protected Targets targets = new Targets(this); - protected Choices choices = new Choices(this); - protected Effects effects = new Effects(this); + protected ManaCosts manaCosts; + protected Costs costs; + protected ArrayList alternativeCosts = new ArrayList(); + protected Targets targets; + protected Choices choices; + protected Effects effects; protected Zone zone; protected String name; - protected boolean enabled = true; + protected boolean usesStack = true; - public AbilityImpl(Zone zone) { + @Override + public abstract T copy(); + + public AbilityImpl(AbilityType abilityType, Zone zone) { this.id = UUID.randomUUID(); + this.abilityType = abilityType; this.zone = zone; - targets.setSource(this); - costs.setAbility(this); - choices.setSource(this); - effects.setSource(this); + this.manaCosts = new ManaCostsImpl(); + this.costs = new CostsImpl(); + this.effects = new Effects(); + this.targets = new Targets(); + this.choices = new Choices(); + } + + public AbilityImpl(AbilityImpl ability) { + this.id = ability.id; + this.abilityType = ability.abilityType; + this.controllerId = ability.controllerId; + this.sourceId = ability.sourceId; + this.zone = ability.zone; + this.name = ability.name; + this.usesStack = ability.usesStack; + this.manaCosts = ability.manaCosts.copy(); + this.costs = ability.costs.copy(); + for (AlternativeCost cost: ability.alternativeCosts) { + this.alternativeCosts.add((AlternativeCost)cost.copy()); + } + this.targets = ability.targets.copy(); + this.choices = ability.choices.copy(); + this.effects = ability.effects.copy(); } @Override @@ -84,17 +112,20 @@ public abstract class AbilityImpl implements Ability, Serializable { return id; } + @Override + public AbilityType getAbilityType() { + return this.abilityType; + } + @Override public boolean resolve(Game game) { boolean result = true; - if (enabled) { - for (Effect effect: getEffects()) { - if (effect instanceof OneShotEffect) { - result &= effect.apply(game); - } - else { - game.addEffect((ContinuousEffect) effect); - } + for (Effect effect: getEffects()) { + if (effect instanceof OneShotEffect) { + result &= effect.apply(game, this); + } + else { + game.addEffect((ContinuousEffect) effect, this); } } return result; @@ -102,15 +133,25 @@ public abstract class AbilityImpl implements Ability, Serializable { @Override public boolean activate(Game game, boolean noMana) { - if (choices.size() > 0 && choices.choose(game) == false) + if (choices.size() > 0 && choices.choose(game, this) == false) { + logger.fine("activate failed - choice"); return false; - if (targets.size() > 0 && targets.choose(effects.get(0).getOutcome(), game) == false) - return false; - if (!useAlternativeCost(game)) { - if (!manaCosts.pay(game, noMana)) - return false; } - return costs.pay(game, noMana); + if (targets.size() > 0 && targets.choose(effects.get(0).getOutcome(), this.controllerId, this, game) == false) { + logger.fine("activate failed - target"); + return false; + } + if (!useAlternativeCost(game)) { + if (!manaCosts.pay(game, this, noMana)) { + logger.fine("activate failed - mana"); + return false; + } + } + if (!costs.pay(game, this, noMana)) { + logger.fine("activate failed - non mana costs"); + return false; + } + return true; } @Override @@ -118,9 +159,9 @@ public abstract class AbilityImpl implements Ability, Serializable { protected boolean useAlternativeCost(Game game) { for (AlternativeCost cost: alternativeCosts) { - if (cost.isAvailable(game)) { + if (cost.isAvailable(game, this)) { if (game.getPlayer(this.controllerId).chooseUse(Outcome.Neutral, "Use alternative cost " + cost.getName(), game)) - return cost.pay(game, false); + return cost.pay(game, this, false); } } return false; @@ -153,7 +194,7 @@ public abstract class AbilityImpl implements Ability, Serializable { } @Override - public ManaCosts getManaCosts() { + public ManaCosts getManaCosts() { return manaCosts; } @@ -177,11 +218,16 @@ public abstract class AbilityImpl implements Ability, Serializable { return zone; } + @Override + public boolean isUsesStack() { + return usesStack; + } + @Override public String getRule() { StringBuilder sbRule = new StringBuilder(); - if (!(this instanceof SpellAbility)) { + if (!(this.abilityType == AbilityType.SPELL)) { if (manaCosts.size() > 0) { sbRule.append(manaCosts.getText()); } @@ -208,20 +254,19 @@ public abstract class AbilityImpl implements Ability, Serializable { } } - sbRule.append(effects.getText()); + sbRule.append(effects.getText(this)); return sbRule.toString(); } - @Override - public String getName() { - return name; - } +// @Override +// public String getName() { +// return ""; +// } @Override public void addCost(Cost cost) { if (cost != null) { - cost.setAbility(this); this.costs.add(cost); } } @@ -229,7 +274,6 @@ public abstract class AbilityImpl implements Ability, Serializable { @Override public void addManaCost(ManaCost cost) { if (cost != null) { - cost.setAbility(this); this.manaCosts.add(cost); } } @@ -237,7 +281,6 @@ public abstract class AbilityImpl implements Ability, Serializable { @Override public void addAlternativeCost(AlternativeCost cost) { if (cost != null) { - cost.setAbility(this); this.alternativeCosts.add(cost); } } @@ -245,7 +288,6 @@ public abstract class AbilityImpl implements Ability, Serializable { @Override public void addEffect(Effect effect) { if (effect != null) { - effect.setSource(this); this.effects.add(effect); } } @@ -253,7 +295,6 @@ public abstract class AbilityImpl implements Ability, Serializable { @Override public void addTarget(Target target) { if (target != null) { - target.setAbility(this); this.targets.add(target); } } @@ -261,7 +302,6 @@ public abstract class AbilityImpl implements Ability, Serializable { @Override public void addChoice(Choice choice) { if (choice != null) { - choice.setAbility(this); this.choices.add(choice); } } @@ -277,7 +317,7 @@ public abstract class AbilityImpl implements Ability, Serializable { } @Override - public Ability copy() { - return new Copier().copy(this); + public String toString() { + return getRule(); } } diff --git a/Mage/src/mage/abilities/ActivatedAbilityImpl.java b/Mage/src/mage/abilities/ActivatedAbilityImpl.java index bd8b4cf9a9f..f610be1a413 100644 --- a/Mage/src/mage/abilities/ActivatedAbilityImpl.java +++ b/Mage/src/mage/abilities/ActivatedAbilityImpl.java @@ -29,6 +29,7 @@ package mage.abilities; import java.util.UUID; +import mage.Constants.AbilityType; import mage.Constants.TimingRule; import mage.Constants.Zone; import mage.abilities.costs.Cost; @@ -44,23 +45,32 @@ import mage.target.Target; * * @author BetaSteward_at_googlemail.com */ -public abstract class ActivatedAbilityImpl extends AbilityImpl implements ActivatedAbility { +public abstract class ActivatedAbilityImpl> extends AbilityImpl implements ActivatedAbility { protected TimingRule timing = TimingRule.INSTANT; + protected ActivatedAbilityImpl(AbilityType abilityType, Zone zone) { + super(abilityType, zone); + } + + public ActivatedAbilityImpl(ActivatedAbilityImpl ability) { + super(ability); + timing = ability.timing; + } + public ActivatedAbilityImpl(Zone zone) { - super(zone); + this(zone, null); } public ActivatedAbilityImpl(Zone zone, Effect effect) { - super(zone); + super(AbilityType.ACTIVATED, zone); if (effect != null) { this.addEffect(effect); } } public ActivatedAbilityImpl(Zone zone, Effect effect, ManaCosts cost) { - super(zone); + super(AbilityType.ACTIVATED, zone); if (effect != null) { this.addEffect(effect); } @@ -69,7 +79,7 @@ public abstract class ActivatedAbilityImpl extends AbilityImpl implements Activa } public ActivatedAbilityImpl(Zone zone, Effects effects, ManaCosts cost) { - super(zone); + super(AbilityType.ACTIVATED, zone); if (effects != null) { for (Effect effect: effects) { this.addEffect(effect); @@ -80,7 +90,7 @@ public abstract class ActivatedAbilityImpl extends AbilityImpl implements Activa } public ActivatedAbilityImpl(Zone zone, Effect effect, Cost cost) { - super(zone); + super(AbilityType.ACTIVATED, zone); if (effect != null) { this.addEffect(effect); } @@ -89,7 +99,7 @@ public abstract class ActivatedAbilityImpl extends AbilityImpl implements Activa } public ActivatedAbilityImpl(Zone zone, Effect effect, Costs costs) { - super(zone); + super(AbilityType.ACTIVATED, zone); if (effect != null) { this.addEffect(effect); } @@ -101,7 +111,7 @@ public abstract class ActivatedAbilityImpl extends AbilityImpl implements Activa } public ActivatedAbilityImpl(Zone zone, Effects effects, Cost cost) { - super(zone); + super(AbilityType.ACTIVATED, zone); if (effects != null) { for (Effect effect: effects) { this.addEffect(effect); @@ -112,7 +122,7 @@ public abstract class ActivatedAbilityImpl extends AbilityImpl implements Activa } public ActivatedAbilityImpl(Zone zone, Effects effects, Costs costs) { - super(zone); + super(AbilityType.ACTIVATED, zone); for (Effect effect: effects) { if (effect != null) { this.addEffect(effect); @@ -131,7 +141,7 @@ public abstract class ActivatedAbilityImpl extends AbilityImpl implements Activa if (!controlsAbility(playerId, game)) return false; //20091005 - 602.5d/602.5e - if ((timing == TimingRule.INSTANT || game.canPlaySorcery(playerId)) && costs.canPay(playerId, game) && targets.canChoose(sourceId, playerId, game)) { + if ((timing == TimingRule.INSTANT || game.canPlaySorcery(playerId)) && costs.canPay(this, game) && targets.canChoose(sourceId, playerId, game)) { return true; } return false; diff --git a/Mage/src/mage/abilities/DelayedTriggeredAbilities.java b/Mage/src/mage/abilities/DelayedTriggeredAbilities.java index b28ec81a052..1366bbe5fc4 100644 --- a/Mage/src/mage/abilities/DelayedTriggeredAbilities.java +++ b/Mage/src/mage/abilities/DelayedTriggeredAbilities.java @@ -28,7 +28,6 @@ package mage.abilities; -import java.util.ArrayList; import java.util.Iterator; import mage.game.Game; import mage.game.events.GameEvent; @@ -37,7 +36,13 @@ import mage.game.events.GameEvent; * * @author BetaSteward_at_googlemail.com */ -public class DelayedTriggeredAbilities extends ArrayList { + public class DelayedTriggeredAbilities extends AbilitiesImpl { + + public DelayedTriggeredAbilities() {} + + public DelayedTriggeredAbilities(final DelayedTriggeredAbilities abilities) { + super(abilities); + } public void checkTriggers(GameEvent event, Game game) { Iterator it = this.iterator(); @@ -48,25 +53,10 @@ public class DelayedTriggeredAbilities extends ArrayList it = this.iterator(); -// while(it.hasNext()) { -// TriggeredAbility ability = it.next(); -// it.remove(); -// played |= game.getPlayer(ability.getControllerId()).triggerAbility(ability, game); -// } -// return played; -// } - -// public TriggeredAbilities getControlledBy(UUID controllerId) { -// TriggeredAbilities controlledBy = new TriggeredAbilities(); -// for (TriggeredAbility ability: this) { -// if (ability.getControllerId().equals(controllerId)) -// controlledBy.add(ability); -// } -// return controlledBy; -// } + @Override + public DelayedTriggeredAbilities copy() { + return new DelayedTriggeredAbilities(this); + } } diff --git a/Mage/src/mage/abilities/DelayedTriggeredAbility.java b/Mage/src/mage/abilities/DelayedTriggeredAbility.java index 24bbddd3a2c..68d6e55766e 100644 --- a/Mage/src/mage/abilities/DelayedTriggeredAbility.java +++ b/Mage/src/mage/abilities/DelayedTriggeredAbility.java @@ -35,10 +35,16 @@ import mage.abilities.effects.Effect; * * @author BetaSteward_at_googlemail.com */ -public abstract class DelayedTriggeredAbility extends TriggeredAbilityImpl { +public abstract class DelayedTriggeredAbility> extends TriggeredAbilityImpl { public DelayedTriggeredAbility(Effect effect) { super(Zone.ALL, effect); } + public DelayedTriggeredAbility(final DelayedTriggeredAbility ability) { + super(ability); + } + + @Override + public abstract T copy(); } diff --git a/Mage/src/mage/abilities/EvasionAbility.java b/Mage/src/mage/abilities/EvasionAbility.java index f4c317b7f70..c0cc7f6f20f 100644 --- a/Mage/src/mage/abilities/EvasionAbility.java +++ b/Mage/src/mage/abilities/EvasionAbility.java @@ -36,7 +36,7 @@ import mage.game.permanent.Permanent; * * @author BetaSteward_at_googlemail.com */ -public interface EvasionAbility { +public interface EvasionAbility extends Ability { public boolean canBlock(Permanent permanent, Game game); diff --git a/Mage/src/mage/abilities/EvasionAbilityImpl.java b/Mage/src/mage/abilities/EvasionAbilityImpl.java index 399b43a7cfb..9f89cacf323 100644 --- a/Mage/src/mage/abilities/EvasionAbilityImpl.java +++ b/Mage/src/mage/abilities/EvasionAbilityImpl.java @@ -28,16 +28,21 @@ package mage.abilities; +import mage.Constants.AbilityType; import mage.Constants.Zone; /** * * @author BetaSteward_at_googlemail.com */ -public abstract class EvasionAbilityImpl extends StaticAbility implements EvasionAbility { +public abstract class EvasionAbilityImpl> extends StaticAbility implements EvasionAbility { public EvasionAbilityImpl() { - super(Zone.BATTLEFIELD, null); + super(AbilityType.EVASION, Zone.BATTLEFIELD); + } + + public EvasionAbilityImpl(final EvasionAbilityImpl ability) { + super(ability); } } diff --git a/Mage/src/mage/abilities/LoyaltyAbility.java b/Mage/src/mage/abilities/LoyaltyAbility.java index f200eac0eb6..b3a35c31f3c 100644 --- a/Mage/src/mage/abilities/LoyaltyAbility.java +++ b/Mage/src/mage/abilities/LoyaltyAbility.java @@ -38,7 +38,7 @@ import mage.abilities.effects.Effects; * * @author BetaSteward_at_googlemail.com */ -public class LoyaltyAbility extends ActivatedAbilityImpl { +public class LoyaltyAbility extends ActivatedAbilityImpl { public LoyaltyAbility(Effect effect, int loyalty) { super(Zone.BATTLEFIELD, effect, new PayLoyaltyCost(loyalty)); @@ -50,4 +50,13 @@ public class LoyaltyAbility extends ActivatedAbilityImpl { this.timing = TimingRule.SORCERY; } + public LoyaltyAbility(LoyaltyAbility ability) { + super(ability); + } + + @Override + public LoyaltyAbility copy() { + return new LoyaltyAbility(this); + } + } diff --git a/Mage/src/mage/abilities/PlayLandAbility.java b/Mage/src/mage/abilities/PlayLandAbility.java index fdbd90d0bea..5a367aace77 100644 --- a/Mage/src/mage/abilities/PlayLandAbility.java +++ b/Mage/src/mage/abilities/PlayLandAbility.java @@ -29,6 +29,7 @@ package mage.abilities; import java.util.UUID; +import mage.Constants.AbilityType; import mage.Constants.Zone; import mage.game.Game; @@ -36,11 +37,16 @@ import mage.game.Game; * * @author BetaSteward_at_googlemail.com */ -public class PlayLandAbility extends ActivatedAbilityImpl { +public class PlayLandAbility extends ActivatedAbilityImpl { - public PlayLandAbility() { - super(Zone.HAND); - this.name = "Play"; + public PlayLandAbility(String cardName) { + super(AbilityType.PLAY_LAND, Zone.HAND); + this.usesStack = false; + this.name = "Play " + cardName; + } + + public PlayLandAbility(PlayLandAbility ability) { + super(ability); } @Override @@ -58,4 +64,15 @@ public class PlayLandAbility extends ActivatedAbilityImpl { public String getActivatedMessage(Game game) { return " plays " + getMessageText(game); } + + @Override + public String toString() { + return this.name; + } + + @Override + public PlayLandAbility copy() { + return new PlayLandAbility(this); + } + } diff --git a/Mage/src/mage/abilities/SpecialAction.java b/Mage/src/mage/abilities/SpecialAction.java index 7e7aaec4dd1..a51206c174b 100644 --- a/Mage/src/mage/abilities/SpecialAction.java +++ b/Mage/src/mage/abilities/SpecialAction.java @@ -28,16 +28,22 @@ package mage.abilities; +import mage.Constants.AbilityType; import mage.Constants.Zone; /** * * @author BetaSteward_at_googlemail.com */ -public abstract class SpecialAction extends ActivatedAbilityImpl { +public abstract class SpecialAction> extends ActivatedAbilityImpl { public SpecialAction() { - super(Zone.ALL); + super(AbilityType.SPECIAL_ACTION, Zone.ALL); + this.usesStack = false; + } + + public SpecialAction(final SpecialAction action) { + super(action); } } \ No newline at end of file diff --git a/Mage/src/mage/abilities/SpecialActions.java b/Mage/src/mage/abilities/SpecialActions.java index fe53c0f2002..47256271fa1 100644 --- a/Mage/src/mage/abilities/SpecialActions.java +++ b/Mage/src/mage/abilities/SpecialActions.java @@ -37,7 +37,13 @@ import java.util.UUID; * * @author BetaSteward_at_googlemail.com */ -public class SpecialActions extends ArrayList { +public class SpecialActions extends AbilitiesImpl { + + public SpecialActions() {} + + public SpecialActions(final SpecialActions actions) { + super(actions); + } public Map getControlledBy(UUID controllerId) { HashMap controlledBy = new HashMap(); @@ -48,4 +54,9 @@ public class SpecialActions extends ArrayList { return controlledBy; } + @Override + public SpecialActions copy() { + return new SpecialActions(this); + } + } diff --git a/Mage/src/mage/abilities/SpellAbility.java b/Mage/src/mage/abilities/SpellAbility.java index 78277d2e7db..ef1b48c81e9 100644 --- a/Mage/src/mage/abilities/SpellAbility.java +++ b/Mage/src/mage/abilities/SpellAbility.java @@ -29,6 +29,7 @@ package mage.abilities; import java.util.UUID; +import mage.Constants.AbilityType; import mage.Constants.CardType; import mage.Constants.Zone; import mage.abilities.costs.mana.ManaCost; @@ -39,12 +40,16 @@ import mage.game.Game; * * @author BetaSteward_at_googlemail.com */ -public class SpellAbility extends ActivatedAbilityImpl { +public class SpellAbility extends ActivatedAbilityImpl { - public SpellAbility(ManaCost cost) { - super(Zone.HAND); + public SpellAbility(ManaCost cost, String cardName) { + super(AbilityType.SPELL, Zone.HAND); this.addManaCost(cost); - this.name = "Cast"; + this.name = "Cast " + cardName; + } + + public SpellAbility(SpellAbility ability) { + super(ability); } @Override @@ -52,7 +57,7 @@ public class SpellAbility extends ActivatedAbilityImpl { if ((game.getObject(sourceId).getCardType().contains(CardType.INSTANT) || game.getObject(sourceId).getAbilities().containsKey(FlashAbility.getInstance().getId()) || game.canPlaySorcery(playerId)) && - costs.canPay(playerId, game) && targets.canChoose(sourceId, playerId, game)) { + costs.canPay(this, game) && targets.canChoose(sourceId, playerId, game)) { return true; } return false; @@ -70,4 +75,14 @@ public class SpellAbility extends ActivatedAbilityImpl { this.costs.clearPaid(); } + @Override + public String toString() { + return this.name; + } + + @Override + public SpellAbility copy() { + return new SpellAbility(this); + } + } diff --git a/Mage/src/mage/abilities/StaticAbility.java b/Mage/src/mage/abilities/StaticAbility.java index 9cb7a859784..f28a16853ec 100644 --- a/Mage/src/mage/abilities/StaticAbility.java +++ b/Mage/src/mage/abilities/StaticAbility.java @@ -28,6 +28,7 @@ package mage.abilities; +import mage.Constants.AbilityType; import mage.Constants.Zone; import mage.abilities.effects.Effect; @@ -35,11 +36,19 @@ import mage.abilities.effects.Effect; * * @author BetaSteward_at_googlemail.com */ -public abstract class StaticAbility extends AbilityImpl { +public abstract class StaticAbility> extends AbilityImpl { + + protected StaticAbility(AbilityType abilityType, Zone zone) { + super(abilityType, zone); + } public StaticAbility(Zone zone, Effect effect) { - super(zone); + super(AbilityType.STATIC, zone); if (effect != null) this.addEffect(effect); } + + public StaticAbility(StaticAbility ability) { + super(ability); + } } diff --git a/Mage/src/mage/abilities/TriggeredAbilities.java b/Mage/src/mage/abilities/TriggeredAbilities.java index 71f090e4161..5e6bb8b623a 100644 --- a/Mage/src/mage/abilities/TriggeredAbilities.java +++ b/Mage/src/mage/abilities/TriggeredAbilities.java @@ -28,14 +28,19 @@ package mage.abilities; -import java.util.ArrayList; import java.util.UUID; /** * * @author BetaSteward_at_googlemail.com */ -public class TriggeredAbilities extends ArrayList { +public class TriggeredAbilities extends AbilitiesImpl { + + public TriggeredAbilities() {} + + public TriggeredAbilities(final TriggeredAbilities abilities) { + super(abilities); + } public TriggeredAbilities getControlledBy(UUID controllerId) { TriggeredAbilities controlledBy = new TriggeredAbilities(); @@ -45,5 +50,10 @@ public class TriggeredAbilities extends ArrayList { } return controlledBy; } + + @Override + public TriggeredAbilities copy() { + return new TriggeredAbilities(this); + } } diff --git a/Mage/src/mage/abilities/TriggeredAbilityImpl.java b/Mage/src/mage/abilities/TriggeredAbilityImpl.java index 6de639bd649..b4b83c4a2cc 100644 --- a/Mage/src/mage/abilities/TriggeredAbilityImpl.java +++ b/Mage/src/mage/abilities/TriggeredAbilityImpl.java @@ -29,6 +29,7 @@ package mage.abilities; import java.util.UUID; +import mage.Constants.AbilityType; import mage.Constants.Zone; import mage.abilities.effects.Effect; import mage.game.Game; @@ -39,7 +40,7 @@ import mage.players.Player; * * @author BetaSteward_at_googlemail.com */ -public abstract class TriggeredAbilityImpl extends AbilityImpl implements TriggeredAbility { +public abstract class TriggeredAbilityImpl> extends AbilityImpl implements TriggeredAbility { protected boolean optional; @@ -48,12 +49,17 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge } public TriggeredAbilityImpl(Zone zone, Effect effect, boolean optional) { - super(zone); + super(AbilityType.TRIGGERED, zone); if (effect != null) addEffect(effect); this.optional = optional; } + public TriggeredAbilityImpl(final TriggeredAbilityImpl ability) { + super(ability); + this.optional = ability.optional; + } + @Override public void trigger(Game game, UUID controllerId) { //20091005 - 603.4 diff --git a/Mage/src/mage/abilities/common/ActivateOncePerTurnActivatedAbility.java b/Mage/src/mage/abilities/common/ActivateOncePerTurnActivatedAbility.java index 01cc36adba9..e16fabd11e1 100644 --- a/Mage/src/mage/abilities/common/ActivateOncePerTurnActivatedAbility.java +++ b/Mage/src/mage/abilities/common/ActivateOncePerTurnActivatedAbility.java @@ -40,13 +40,16 @@ import mage.game.Game; * * @author BetaSteward_at_googlemail.com */ -public class ActivateOncePerTurnActivatedAbility extends ActivatedAbilityImpl { - +public class ActivateOncePerTurnActivatedAbility extends ActivatedAbilityImpl { public ActivateOncePerTurnActivatedAbility(Zone zone, Effect effect, Cost cost) { super(zone, effect, cost); } + public ActivateOncePerTurnActivatedAbility(ActivateOncePerTurnActivatedAbility ability) { + super(ability); + } + @Override public boolean canActivate(UUID playerId, Game game) { Boolean activated = (Boolean)game.getState().getValue(this.id.toString() + "activated"); @@ -85,4 +88,9 @@ public class ActivateOncePerTurnActivatedAbility extends ActivatedAbilityImpl { return super.getRule() + " Activate this ability only once each turn."; } + @Override + public ActivateOncePerTurnActivatedAbility copy() { + return new ActivateOncePerTurnActivatedAbility(this); + } + } diff --git a/Mage/src/mage/abilities/common/AttacksEachTurnStaticAbility.java b/Mage/src/mage/abilities/common/AttacksEachTurnStaticAbility.java index 0136776e30c..9ccdbf25a6f 100644 --- a/Mage/src/mage/abilities/common/AttacksEachTurnStaticAbility.java +++ b/Mage/src/mage/abilities/common/AttacksEachTurnStaticAbility.java @@ -31,6 +31,7 @@ package mage.abilities.common; import mage.Constants.Duration; import mage.Constants.Outcome; import mage.Constants.Zone; +import mage.abilities.Ability; import mage.abilities.StaticAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.RequirementAttackEffect; @@ -43,29 +44,42 @@ import mage.target.common.TargetDefender; * * @author BetaSteward_at_googlemail.com */ -public class AttacksEachTurnStaticAbility extends StaticAbility { +public class AttacksEachTurnStaticAbility extends StaticAbility { public AttacksEachTurnStaticAbility() { super(Zone.BATTLEFIELD, new AttacksEachTurnEffect()); } + + public AttacksEachTurnStaticAbility(AttacksEachTurnStaticAbility ability) { + super(ability); + } + + @Override + public AttacksEachTurnStaticAbility copy() { + return new AttacksEachTurnStaticAbility(this); + } } -class AttacksEachTurnEffect extends RequirementAttackEffect { +class AttacksEachTurnEffect extends RequirementAttackEffect { public AttacksEachTurnEffect() { super(Duration.WhileOnBattlefield); } + public AttacksEachTurnEffect(final AttacksEachTurnEffect effect) { + super(effect); + } + @Override - public boolean apply(Game game) { - Permanent creature = game.getPermanent(this.source.getSourceId()); + public boolean apply(Game game, Ability source) { + Permanent creature = game.getPermanent(source.getSourceId()); if (creature != null) { if (creature.canAttack(game)) { TargetDefender target = new TargetDefender(game.getCombat().getDefenders(), creature.getControllerId()); Player controller = game.getPlayer(creature.getControllerId()); while (!target.isChosen()) - controller.chooseTarget(Outcome.Damage, target, game); + controller.chooseTarget(Outcome.Damage, target, source, game); game.getCombat().declareAttacker(creature.getId(), target.getFirstTarget(), game); return true; } @@ -74,7 +88,12 @@ class AttacksEachTurnEffect extends RequirementAttackEffect { } @Override - public String getText() { + public String getText(Ability source) { return "{this} attacks each turn if able."; } + + @Override + public AttacksEachTurnEffect copy() { + return new AttacksEachTurnEffect(this); + } } \ No newline at end of file diff --git a/Mage/src/mage/abilities/common/AttacksTriggeredAbility.java b/Mage/src/mage/abilities/common/AttacksTriggeredAbility.java index 86036232ef9..c8faa66f473 100644 --- a/Mage/src/mage/abilities/common/AttacksTriggeredAbility.java +++ b/Mage/src/mage/abilities/common/AttacksTriggeredAbility.java @@ -39,12 +39,16 @@ import mage.game.events.GameEvent.EventType; * * @author BetaSteward_at_googlemail.com */ -public class AttacksTriggeredAbility extends TriggeredAbilityImpl { +public class AttacksTriggeredAbility extends TriggeredAbilityImpl { public AttacksTriggeredAbility(Effect effect, boolean optional) { super(Zone.BATTLEFIELD, effect, optional); } + public AttacksTriggeredAbility(AttacksTriggeredAbility ability) { + super(ability); + } + @Override public boolean checkTrigger(GameEvent event, Game game) { if (event.getType() == EventType.ATTACKER_DECLARED && event.getSourceId().equals(this.getSourceId()) ) { @@ -59,5 +63,10 @@ public class AttacksTriggeredAbility extends TriggeredAbilityImpl { return "When {this} attacks, " + super.getRule(); } + @Override + public AttacksTriggeredAbility copy() { + return new AttacksTriggeredAbility(this); + } + } diff --git a/Mage/src/mage/abilities/common/CantCounterAbility.java b/Mage/src/mage/abilities/common/CantCounterAbility.java index b2de0a9cb45..a2656d2f700 100644 --- a/Mage/src/mage/abilities/common/CantCounterAbility.java +++ b/Mage/src/mage/abilities/common/CantCounterAbility.java @@ -36,14 +36,23 @@ import mage.abilities.effects.common.CantCounterSourceEffect; * * @author BetaSteward_at_googlemail.com */ -public class CantCounterAbility extends StaticAbility { +public class CantCounterAbility extends StaticAbility { public CantCounterAbility() { super(Zone.STACK, new CantCounterSourceEffect()); } + public CantCounterAbility(CantCounterAbility ability) { + super(ability); + } + @Override public String getRule() { return "{this} can't be countered"; } + + @Override + public CantCounterAbility copy() { + return new CantCounterAbility(this); + } } diff --git a/Mage/src/mage/abilities/common/CreateTokenActivatedAbility.java b/Mage/src/mage/abilities/common/CreateTokenActivatedAbility.java index 8bfad0e4d44..829ae53c27c 100644 --- a/Mage/src/mage/abilities/common/CreateTokenActivatedAbility.java +++ b/Mage/src/mage/abilities/common/CreateTokenActivatedAbility.java @@ -38,10 +38,19 @@ import mage.game.permanent.token.Token; * * @author BetaSteward_at_googlemail.com */ -public class CreateTokenActivatedAbility extends ActivatedAbilityImpl { +public class CreateTokenActivatedAbility extends ActivatedAbilityImpl { public CreateTokenActivatedAbility(Zone zone, Token token, Cost cost) { super(zone, new CreateTokenEffect(token), cost); } + public CreateTokenActivatedAbility(CreateTokenActivatedAbility ability) { + super(ability); + } + + @Override + public CreateTokenActivatedAbility copy() { + return new CreateTokenActivatedAbility(this); + } + } diff --git a/Mage/src/mage/abilities/common/EntersBattlefieldStaticAbility.java b/Mage/src/mage/abilities/common/EntersBattlefieldStaticAbility.java index 7f66d175727..338bab35e8c 100644 --- a/Mage/src/mage/abilities/common/EntersBattlefieldStaticAbility.java +++ b/Mage/src/mage/abilities/common/EntersBattlefieldStaticAbility.java @@ -36,10 +36,19 @@ import mage.abilities.effects.EntersBattlefieldEffect; * * @author BetaSteward_at_googlemail.com */ -public class EntersBattlefieldStaticAbility extends StaticAbility { +public class EntersBattlefieldStaticAbility extends StaticAbility { public EntersBattlefieldStaticAbility(EntersBattlefieldEffect effect) { super(Zone.BATTLEFIELD, effect); } + public EntersBattlefieldStaticAbility(EntersBattlefieldStaticAbility ability) { + super(ability); + } + + @Override + public EntersBattlefieldStaticAbility copy() { + return new EntersBattlefieldStaticAbility(this); + } + } diff --git a/Mage/src/mage/abilities/common/EntersBattlefieldTriggeredAbility.java b/Mage/src/mage/abilities/common/EntersBattlefieldTriggeredAbility.java index 2bba79e3598..bd9f74bb297 100644 --- a/Mage/src/mage/abilities/common/EntersBattlefieldTriggeredAbility.java +++ b/Mage/src/mage/abilities/common/EntersBattlefieldTriggeredAbility.java @@ -40,12 +40,20 @@ import mage.game.events.ZoneChangeEvent; * * @author BetaSteward_at_googlemail.com */ -public class EntersBattlefieldTriggeredAbility extends TriggeredAbilityImpl { +public class EntersBattlefieldTriggeredAbility> extends TriggeredAbilityImpl { + + public EntersBattlefieldTriggeredAbility(Effect effect) { + this(effect, false); + } public EntersBattlefieldTriggeredAbility(Effect effect, boolean optional) { super(Zone.BATTLEFIELD, effect, optional); } + public EntersBattlefieldTriggeredAbility(EntersBattlefieldTriggeredAbility ability) { + super(ability); + } + @Override public boolean checkTrigger(GameEvent event, Game game) { if (event.getType() == EventType.ZONE_CHANGE && event.getTargetId().equals(this.getSourceId()) ) { @@ -63,4 +71,9 @@ public class EntersBattlefieldTriggeredAbility extends TriggeredAbilityImpl { return "When {this} enters the battlefield, " + super.getRule(); } + @Override + public T copy() { + return (T) new EntersBattlefieldTriggeredAbility(this); + } + } diff --git a/Mage/src/mage/abilities/common/FetchLandActivatedAbility.java b/Mage/src/mage/abilities/common/FetchLandActivatedAbility.java index 10e4b26528f..218988aada3 100644 --- a/Mage/src/mage/abilities/common/FetchLandActivatedAbility.java +++ b/Mage/src/mage/abilities/common/FetchLandActivatedAbility.java @@ -44,7 +44,7 @@ import mage.target.common.TargetCardInLibrary; * * @author BetaSteward_at_googlemail.com */ -public class FetchLandActivatedAbility extends ActivatedAbilityImpl { +public class FetchLandActivatedAbility extends ActivatedAbilityImpl { public FetchLandActivatedAbility(String[] subTypes) { super(Zone.BATTLEFIELD, null); @@ -61,6 +61,10 @@ public class FetchLandActivatedAbility extends ActivatedAbilityImpl { addEffect(new SearchLibraryPutInPlayEffect(target, false, Outcome.PutLandInPlay)); } + public FetchLandActivatedAbility(FetchLandActivatedAbility ability) { + super(ability); + } + private String subTypeNames(String[] subTypes) { StringBuilder sb = new StringBuilder(); for (String subType: subTypes) { @@ -68,4 +72,9 @@ public class FetchLandActivatedAbility extends ActivatedAbilityImpl { } return sb.substring(0, sb.length() - 4); } + + @Override + public FetchLandActivatedAbility copy() { + return new FetchLandActivatedAbility(this); + } } diff --git a/Mage/src/mage/abilities/common/LandfallAbility.java b/Mage/src/mage/abilities/common/LandfallAbility.java index b8c06ebace3..128f7b4a0fb 100644 --- a/Mage/src/mage/abilities/common/LandfallAbility.java +++ b/Mage/src/mage/abilities/common/LandfallAbility.java @@ -42,12 +42,16 @@ import mage.game.permanent.Permanent; * * @author BetaSteward_at_googlemail.com */ -public class LandfallAbility extends TriggeredAbilityImpl { +public class LandfallAbility extends TriggeredAbilityImpl { public LandfallAbility(Effect effect, boolean optional) { super(Zone.BATTLEFIELD, effect, optional); } + public LandfallAbility(LandfallAbility ability) { + super(ability); + } + @Override public boolean checkTrigger(GameEvent event, Game game) { if (event.getType() == EventType.ZONE_CHANGE && ((ZoneChangeEvent)event).getToZone() == Zone.BATTLEFIELD) { @@ -65,6 +69,9 @@ public class LandfallAbility extends TriggeredAbilityImpl { return "Landfall - Whenever a land enters the battlefield under your control, " + (optional?" you may ":"") + super.getRule(); } - + @Override + public LandfallAbility copy() { + return new LandfallAbility(this); + } } diff --git a/Mage/src/mage/abilities/common/LeavesBattlefieldTriggeredAbility.java b/Mage/src/mage/abilities/common/LeavesBattlefieldTriggeredAbility.java index ac33afe522f..5bf6b46fc6d 100644 --- a/Mage/src/mage/abilities/common/LeavesBattlefieldTriggeredAbility.java +++ b/Mage/src/mage/abilities/common/LeavesBattlefieldTriggeredAbility.java @@ -40,12 +40,16 @@ import mage.game.events.ZoneChangeEvent; * * @author BetaSteward_at_googlemail.com */ -public class LeavesBattlefieldTriggeredAbility extends TriggeredAbilityImpl { +public class LeavesBattlefieldTriggeredAbility extends TriggeredAbilityImpl { public LeavesBattlefieldTriggeredAbility(Effect effect, boolean optional) { super(Zone.BATTLEFIELD, effect, optional); } + public LeavesBattlefieldTriggeredAbility(LeavesBattlefieldTriggeredAbility ability) { + super(ability); + } + @Override public boolean checkTrigger(GameEvent event, Game game) { if (event.getType() == EventType.ZONE_CHANGE && event.getTargetId().equals(this.getSourceId()) ) { @@ -63,4 +67,9 @@ public class LeavesBattlefieldTriggeredAbility extends TriggeredAbilityImpl { return "When {this} leaves the battlefield, " + super.getRule(); } + @Override + public LeavesBattlefieldTriggeredAbility copy() { + return new LeavesBattlefieldTriggeredAbility(this); + } + } diff --git a/Mage/src/mage/abilities/common/OnEventTriggeredAbility.java b/Mage/src/mage/abilities/common/OnEventTriggeredAbility.java index 9246c8109ae..816c64e3980 100644 --- a/Mage/src/mage/abilities/common/OnEventTriggeredAbility.java +++ b/Mage/src/mage/abilities/common/OnEventTriggeredAbility.java @@ -39,7 +39,7 @@ import mage.game.events.GameEvent.EventType; * * @author BetaSteward_at_googlemail.com */ -public class OnEventTriggeredAbility extends TriggeredAbilityImpl { +public class OnEventTriggeredAbility extends TriggeredAbilityImpl { private EventType eventType; private String eventName; @@ -58,6 +58,13 @@ public class OnEventTriggeredAbility extends TriggeredAbilityImpl { this.allPlayers = allPlayers; } + public OnEventTriggeredAbility(OnEventTriggeredAbility ability) { + super(ability); + this.eventType = ability.eventType; + this.eventName = ability.eventName; + this.allPlayers = ability.allPlayers; + } + @Override public boolean checkTrigger(GameEvent event, Game game) { if (event.getType() == eventType) { @@ -74,4 +81,9 @@ public class OnEventTriggeredAbility extends TriggeredAbilityImpl { return "At the " + eventName + ", " + super.getRule(); } + @Override + public OnEventTriggeredAbility copy() { + return new OnEventTriggeredAbility(this); + } + } diff --git a/Mage/src/mage/abilities/common/PassAbility.java b/Mage/src/mage/abilities/common/PassAbility.java new file mode 100644 index 00000000000..d0ac2e1da81 --- /dev/null +++ b/Mage/src/mage/abilities/common/PassAbility.java @@ -0,0 +1,59 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.abilities.common; + +import mage.Constants.Zone; +import mage.abilities.ActivatedAbilityImpl; +import mage.abilities.effects.common.PassEffect; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class PassAbility extends ActivatedAbilityImpl { + + public PassAbility() { + super(Zone.ALL, new PassEffect()); + } + + public PassAbility(final PassAbility ability) { + super(ability); + } + + @Override + public PassAbility copy() { + return new PassAbility(this); + } + + @Override + public String toString() { + return "Pass"; + } + +} diff --git a/Mage/src/mage/abilities/common/PutIntoGraveFromBattlefieldTriggeredAbility.java b/Mage/src/mage/abilities/common/PutIntoGraveFromBattlefieldTriggeredAbility.java index 20002471f36..5cc32d5ae05 100644 --- a/Mage/src/mage/abilities/common/PutIntoGraveFromBattlefieldTriggeredAbility.java +++ b/Mage/src/mage/abilities/common/PutIntoGraveFromBattlefieldTriggeredAbility.java @@ -40,12 +40,16 @@ import mage.game.events.ZoneChangeEvent; * * @author BetaSteward_at_googlemail.com */ -public class PutIntoGraveFromBattlefieldTriggeredAbility extends TriggeredAbilityImpl { +public class PutIntoGraveFromBattlefieldTriggeredAbility extends TriggeredAbilityImpl { public PutIntoGraveFromBattlefieldTriggeredAbility(Effect effect, boolean optional) { super(Zone.GRAVEYARD, effect, optional); } + public PutIntoGraveFromBattlefieldTriggeredAbility(PutIntoGraveFromBattlefieldTriggeredAbility ability) { + super(ability); + } + @Override public boolean checkTrigger(GameEvent event, Game game) { if (event.getType() == EventType.ZONE_CHANGE && event.getTargetId().equals(this.getSourceId()) ) { @@ -63,4 +67,9 @@ public class PutIntoGraveFromBattlefieldTriggeredAbility extends TriggeredAbilit return "When {this} is put into a graveyard from the battlefield, " + super.getRule(); } + @Override + public PutIntoGraveFromBattlefieldTriggeredAbility copy() { + return new PutIntoGraveFromBattlefieldTriggeredAbility(this); + } + } diff --git a/Mage/src/mage/abilities/common/SimpleActivatedAbility.java b/Mage/src/mage/abilities/common/SimpleActivatedAbility.java index a0b262680d2..37fe19e602f 100644 --- a/Mage/src/mage/abilities/common/SimpleActivatedAbility.java +++ b/Mage/src/mage/abilities/common/SimpleActivatedAbility.java @@ -39,7 +39,7 @@ import mage.abilities.effects.Effect; * * @author BetaSteward_at_googlemail.com */ -public class SimpleActivatedAbility extends ActivatedAbilityImpl { +public class SimpleActivatedAbility extends ActivatedAbilityImpl { public SimpleActivatedAbility(Zone zone, Effect effect, ManaCosts cost) { super(zone, effect, cost); @@ -53,4 +53,13 @@ public class SimpleActivatedAbility extends ActivatedAbilityImpl { super(zone, effect, cost); } + public SimpleActivatedAbility(SimpleActivatedAbility ability) { + super(ability); + } + + @Override + public SimpleActivatedAbility copy() { + return new SimpleActivatedAbility(this); + } + } diff --git a/Mage/src/mage/abilities/common/SimpleStaticAbility.java b/Mage/src/mage/abilities/common/SimpleStaticAbility.java index 217b83620a4..adc00849fc0 100644 --- a/Mage/src/mage/abilities/common/SimpleStaticAbility.java +++ b/Mage/src/mage/abilities/common/SimpleStaticAbility.java @@ -36,10 +36,19 @@ import mage.abilities.effects.Effect; * * @author BetaSteward_at_googlemail.com */ -public class SimpleStaticAbility extends StaticAbility { +public class SimpleStaticAbility extends StaticAbility { public SimpleStaticAbility(Zone zone, Effect effect) { super(zone, effect); } + public SimpleStaticAbility(SimpleStaticAbility ability) { + super(ability); + } + + @Override + public SimpleStaticAbility copy() { + return new SimpleStaticAbility(this); + } + } diff --git a/Mage/src/mage/abilities/common/SimpleTriggeredAbility.java b/Mage/src/mage/abilities/common/SimpleTriggeredAbility.java index b39b3fdc624..5c03d6bcf60 100644 --- a/Mage/src/mage/abilities/common/SimpleTriggeredAbility.java +++ b/Mage/src/mage/abilities/common/SimpleTriggeredAbility.java @@ -39,7 +39,7 @@ import mage.game.events.GameEvent.EventType; * * @author BetaSteward_at_googlemail.com */ -public class SimpleTriggeredAbility extends TriggeredAbilityImpl { +public class SimpleTriggeredAbility extends TriggeredAbilityImpl { EventType eventType; @@ -48,6 +48,11 @@ public class SimpleTriggeredAbility extends TriggeredAbilityImpl { this.eventType = eventType; } + public SimpleTriggeredAbility(SimpleTriggeredAbility ability) { + super(ability); + this.eventType = ability.eventType; + } + @Override public boolean checkTrigger(GameEvent event, Game game) { if (event.getType() == eventType) { @@ -56,4 +61,9 @@ public class SimpleTriggeredAbility extends TriggeredAbilityImpl { } return false; } + + @Override + public SimpleTriggeredAbility copy() { + return new SimpleTriggeredAbility(this); + } } diff --git a/Mage/src/mage/abilities/costs/AlternativeCost.java b/Mage/src/mage/abilities/costs/AlternativeCost.java index f8076f33d59..4c161c6639c 100644 --- a/Mage/src/mage/abilities/costs/AlternativeCost.java +++ b/Mage/src/mage/abilities/costs/AlternativeCost.java @@ -28,13 +28,14 @@ package mage.abilities.costs; +import mage.abilities.Ability; import mage.game.Game; /** * * @author BetaSteward_at_googlemail.com */ -public class AlternativeCost extends CostsImpl { +public abstract class AlternativeCost> extends CostsImpl { protected String name; @@ -42,11 +43,16 @@ public class AlternativeCost extends CostsImpl { this.name = name; } + public AlternativeCost(final AlternativeCost cost) { + super(cost); + this.name = cost.name; + } + public String getName() { return name; } - public boolean isAvailable(Game game) { + public boolean isAvailable(Game game, Ability source) { return true; } } diff --git a/Mage/src/mage/abilities/costs/Cost.java b/Mage/src/mage/abilities/costs/Cost.java index cb9fc99f80a..f81016b8dfe 100644 --- a/Mage/src/mage/abilities/costs/Cost.java +++ b/Mage/src/mage/abilities/costs/Cost.java @@ -29,7 +29,6 @@ package mage.abilities.costs; import java.io.Serializable; -import java.util.UUID; import mage.abilities.Ability; import mage.game.Game; import mage.target.Targets; @@ -37,14 +36,13 @@ import mage.target.Targets; public interface Cost extends Serializable { public String getText(); - public boolean canPay(UUID playerId, Game game); - public boolean pay(Game game, boolean noMana); + public boolean canPay(Ability source, Game game); + public boolean pay(Game game, Ability source, boolean noMana); public boolean isPaid(); public void clearPaid(); public void setPaid(); public Targets getTargets(); - - public Ability getAbility(); - public void setAbility(Ability ability); + + public Cost copy(); } diff --git a/Mage/src/mage/abilities/costs/CostImpl.java b/Mage/src/mage/abilities/costs/CostImpl.java index 6e13d68a3d0..7aed4b6841d 100644 --- a/Mage/src/mage/abilities/costs/CostImpl.java +++ b/Mage/src/mage/abilities/costs/CostImpl.java @@ -28,18 +28,27 @@ package mage.abilities.costs; -import mage.abilities.Ability; import mage.target.Target; import mage.target.Targets; -public abstract class CostImpl implements Cost { +public abstract class CostImpl> implements Cost { protected String text; - protected Ability ability; - protected boolean paid = false; - protected Targets targets = new Targets(null); + protected boolean paid; + protected Targets targets; + + @Override + public abstract T copy(); public CostImpl() { + paid = false; + targets = new Targets(); + } + + public CostImpl(final CostImpl cost) { + this.text = cost.text; + this.paid = cost.paid; + this.targets = cost.targets.copy(); } @Override @@ -47,20 +56,8 @@ public abstract class CostImpl implements Cost { return text; } - @Override - public Ability getAbility() { - return ability; - } - - @Override - public void setAbility(Ability ability) { - this.ability = ability; - targets.setSource(ability); - } - public void addTarget(Target target) { if (target != null) { - target.setAbility(ability); this.targets.add(target); } } diff --git a/Mage/src/mage/abilities/costs/Costs.java b/Mage/src/mage/abilities/costs/Costs.java index ab091966176..b527f57c9d4 100644 --- a/Mage/src/mage/abilities/costs/Costs.java +++ b/Mage/src/mage/abilities/costs/Costs.java @@ -31,9 +31,8 @@ package mage.abilities.costs; import java.util.List; public interface Costs extends List, Cost { - @Override - public boolean add(T cost); public List getUnpaid(); - + @Override + public Costs copy(); } diff --git a/Mage/src/mage/abilities/costs/CostsImpl.java b/Mage/src/mage/abilities/costs/CostsImpl.java index 4d1f3011783..c076e667b12 100644 --- a/Mage/src/mage/abilities/costs/CostsImpl.java +++ b/Mage/src/mage/abilities/costs/CostsImpl.java @@ -29,7 +29,6 @@ package mage.abilities.costs; import java.util.ArrayList; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.costs.mana.VariableManaCost; import mage.game.Game; @@ -41,12 +40,12 @@ import mage.target.Targets; */ public class CostsImpl extends ArrayList implements Costs { - protected Ability ability; - public CostsImpl() {} - - public CostsImpl(Ability ability) { - this.ability = ability; + + public CostsImpl(final CostsImpl costs) { + for (Cost cost: costs) { + this.add((T)cost.copy()); + } } @Override @@ -63,20 +62,20 @@ public class CostsImpl extends ArrayList implements Costs } @Override - public boolean canPay(UUID playerId, Game game) { - for (Cost cost: this) { - if (!cost.canPay(playerId, game)) + public boolean canPay(Ability source, Game game) { + for (T cost: this) { + if (!cost.canPay(source, game)) return false; } return true; } @Override - public boolean pay(Game game, boolean noMana) { + public boolean pay(Game game, Ability source, boolean noMana) { if (this.size() > 0) { while (!isPaid()) { - Cost cost = getFirstUnpaid(); - if (!cost.pay(game, noMana)) + T cost = getFirstUnpaid(); + if (!cost.pay(game, source, noMana)) return false; } } @@ -86,7 +85,7 @@ public class CostsImpl extends ArrayList implements Costs @Override public boolean isPaid() { for (T cost: this) { - if (!(cost instanceof VariableManaCost) && !cost.isPaid()) + if (!((T)cost instanceof VariableManaCost) && !cost.isPaid()) return false; } return true; @@ -106,28 +105,9 @@ public class CostsImpl extends ArrayList implements Costs } } - @Override - public Ability getAbility() { - return ability; - } - - @Override - public void setAbility(Ability ability) { - this.ability = ability; - for (T cost: this) { - cost.setAbility(ability); - } - } - - @Override - public boolean add(T cost) { - cost.setAbility(ability); - return super.add(cost); - } - @Override public Costs getUnpaid() { - Costs unpaid = new CostsImpl(ability); + Costs unpaid = new CostsImpl(); for (T cost: this) { if (!cost.isPaid()) unpaid.add(cost); @@ -145,11 +125,16 @@ public class CostsImpl extends ArrayList implements Costs @Override public Targets getTargets() { - Targets targets = new Targets(ability); - for (Cost cost: this) { + Targets targets = new Targets(); + for (T cost: this) { targets.addAll(cost.getTargets()); } return targets; } + @Override + public Costs copy() { + return new CostsImpl(this); + } + } diff --git a/Mage/src/mage/abilities/costs/common/DiscardSourceCost.java b/Mage/src/mage/abilities/costs/common/DiscardSourceCost.java index b4d7530d01a..6ca9888777f 100644 --- a/Mage/src/mage/abilities/costs/common/DiscardSourceCost.java +++ b/Mage/src/mage/abilities/costs/common/DiscardSourceCost.java @@ -28,7 +28,7 @@ package mage.abilities.costs.common; -import java.util.UUID; +import mage.abilities.Ability; import mage.abilities.costs.CostImpl; import mage.cards.Card; import mage.game.Game; @@ -38,17 +38,23 @@ import mage.players.Player; * * @author BetaSteward_at_googlemail.com */ -public class DiscardSourceCost extends CostImpl { +public class DiscardSourceCost extends CostImpl { - @Override - public boolean canPay(UUID playerId, Game game) { - return game.getPlayer(playerId).getHand().containsKey(this.ability.getSourceId()); + public DiscardSourceCost() {} + + public DiscardSourceCost(DiscardSourceCost cost) { + super(cost); } @Override - public boolean pay(Game game, boolean noMana) { - Player player = game.getPlayer(this.ability.getControllerId()); - Card card = player.getHand().get(this.ability.getSourceId()); + public boolean canPay(Ability source, Game game) { + return game.getPlayer(source.getControllerId()).getHand().contains(source.getSourceId()); + } + + @Override + public boolean pay(Game game, Ability source, boolean noMana) { + Player player = game.getPlayer(source.getControllerId()); + Card card = player.getHand().get(source.getSourceId(), game); return player.discard(card, game); } @@ -56,4 +62,9 @@ public class DiscardSourceCost extends CostImpl { public String getText() { return "Discard {this}"; } + + @Override + public DiscardSourceCost copy() { + return new DiscardSourceCost(this); + } } diff --git a/Mage/src/mage/abilities/costs/common/PayLifeCost.java b/Mage/src/mage/abilities/costs/common/PayLifeCost.java index e5201ff0948..b6e27be6fe7 100644 --- a/Mage/src/mage/abilities/costs/common/PayLifeCost.java +++ b/Mage/src/mage/abilities/costs/common/PayLifeCost.java @@ -29,6 +29,7 @@ package mage.abilities.costs.common; import java.util.UUID; +import mage.abilities.Ability; import mage.abilities.costs.CostImpl; import mage.game.Game; @@ -36,7 +37,7 @@ import mage.game.Game; * * @author BetaSteward_at_googlemail.com */ -public class PayLifeCost extends CostImpl { +public class PayLifeCost extends CostImpl { private int amount; @@ -45,17 +46,25 @@ public class PayLifeCost extends CostImpl { this.text = "Pay " + Integer.toString(amount) + " life"; } - @Override - public boolean canPay(UUID playerId, Game game) { - if (ability.getControllerId().equals(playerId)) - return game.getPlayer(playerId).getLife() >= amount; - return false; + public PayLifeCost(PayLifeCost cost) { + super(cost); + this.amount = cost.amount; } @Override - public boolean pay(Game game, boolean noMana) { - this.paid = game.getPlayer(ability.getControllerId()).loseLife(amount, game) == amount; + public boolean canPay(Ability source, Game game) { + return game.getPlayer(source.getControllerId()).getLife() >= amount; + } + + @Override + public boolean pay(Game game, Ability source, boolean noMana) { + this.paid = game.getPlayer(source.getControllerId()).loseLife(amount, game) == amount; return paid; } + @Override + public PayLifeCost copy() { + return new PayLifeCost(this); + } + } diff --git a/Mage/src/mage/abilities/costs/common/PayLoyaltyCost.java b/Mage/src/mage/abilities/costs/common/PayLoyaltyCost.java index 4eef40f11d9..d4f30561769 100644 --- a/Mage/src/mage/abilities/costs/common/PayLoyaltyCost.java +++ b/Mage/src/mage/abilities/costs/common/PayLoyaltyCost.java @@ -28,7 +28,7 @@ package mage.abilities.costs.common; -import java.util.UUID; +import mage.abilities.Ability; import mage.abilities.costs.CostImpl; import mage.game.Game; import mage.game.permanent.Permanent; @@ -37,7 +37,7 @@ import mage.game.permanent.Permanent; * * @author BetaSteward_at_googlemail.com */ -public class PayLoyaltyCost extends CostImpl { +public class PayLoyaltyCost extends CostImpl { private int amount; @@ -46,17 +46,22 @@ public class PayLoyaltyCost extends CostImpl { this.text = Integer.toString(amount) + " loyalty"; } + public PayLoyaltyCost(PayLoyaltyCost cost) { + super(cost); + this.amount = cost.amount; + } + @Override - public boolean canPay(UUID playerId, Game game) { - Permanent planeswalker = game.getPermanent(this.ability.getSourceId()); + public boolean canPay(Ability source, Game game) { + Permanent planeswalker = game.getPermanent(source.getSourceId()); if (planeswalker.getLoyalty().getValue() + amount >= 0 && !planeswalker.isLoyaltyUsed()) return true; return false; } @Override - public boolean pay(Game game, boolean noMana) { - Permanent planeswalker = game.getPermanent(this.ability.getSourceId()); + public boolean pay(Game game, Ability source, boolean noMana) { + Permanent planeswalker = game.getPermanent(source.getSourceId()); if (planeswalker.getLoyalty().getValue() + amount > 0 && !planeswalker.isLoyaltyUsed()) { planeswalker.getLoyalty().boostValue(amount); planeswalker.setLoyaltyUsed(true); @@ -65,4 +70,9 @@ public class PayLoyaltyCost extends CostImpl { return paid; } + @Override + public PayLoyaltyCost copy() { + return new PayLoyaltyCost(this); + } + } diff --git a/Mage/src/mage/abilities/costs/common/RemoveCountersSourceCost.java b/Mage/src/mage/abilities/costs/common/RemoveCountersSourceCost.java index 5c5b88d113e..0e5fdeb5c69 100644 --- a/Mage/src/mage/abilities/costs/common/RemoveCountersSourceCost.java +++ b/Mage/src/mage/abilities/costs/common/RemoveCountersSourceCost.java @@ -29,6 +29,7 @@ package mage.abilities.costs.common; import java.util.UUID; +import mage.abilities.Ability; import mage.abilities.costs.CostImpl; import mage.game.Game; import mage.game.permanent.Permanent; @@ -37,7 +38,7 @@ import mage.game.permanent.Permanent; * * @author BetaSteward_at_googlemail.com */ -public class RemoveCountersSourceCost extends CostImpl { +public class RemoveCountersSourceCost extends CostImpl { private int amount; private String name; @@ -48,21 +49,32 @@ public class RemoveCountersSourceCost extends CostImpl { this.text = "Remove " + amount + " " + name + " counters from {this}"; } + public RemoveCountersSourceCost(RemoveCountersSourceCost cost) { + super(cost); + this.amount = cost.amount; + this.name = cost.name; + } + @Override - public boolean canPay(UUID playerId, Game game) { - Permanent source = game.getPermanent(this.ability.getSourceId()); - if (source.getCounters().getCount(name) >= amount) + public boolean canPay(Ability source, Game game) { + Permanent permanent = game.getPermanent(source.getSourceId()); + if (permanent.getCounters().getCount(name) >= amount) return true; return false; } @Override - public boolean pay(Game game, boolean noMana) { - Permanent source = game.getPermanent(this.ability.getSourceId()); - if (source.getCounters().getCount(name) >= amount) { - source.getCounters().removeCounter(name, amount); + public boolean pay(Game game, Ability source, boolean noMana) { + Permanent permanent = game.getPermanent(source.getSourceId()); + if (permanent.getCounters().getCount(name) >= amount) { + permanent.getCounters().removeCounter(name, amount); this.paid = true; } return paid; } + + @Override + public RemoveCountersSourceCost copy() { + return new RemoveCountersSourceCost(this); + } } diff --git a/Mage/src/mage/abilities/costs/common/ReturnToHandTargetCost.java b/Mage/src/mage/abilities/costs/common/ReturnToHandTargetCost.java index 7a89def14e8..cc41c9a5da9 100644 --- a/Mage/src/mage/abilities/costs/common/ReturnToHandTargetCost.java +++ b/Mage/src/mage/abilities/costs/common/ReturnToHandTargetCost.java @@ -31,6 +31,7 @@ package mage.abilities.costs.common; import java.util.UUID; import mage.Constants.Outcome; import mage.Constants.Zone; +import mage.abilities.Ability; import mage.abilities.costs.CostImpl; import mage.game.Game; import mage.game.permanent.Permanent; @@ -40,27 +41,36 @@ import mage.target.common.TargetControlledPermanent; * * @author BetaSteward_at_googlemail.com */ -public class ReturnToHandTargetCost extends CostImpl { +public class ReturnToHandTargetCost extends CostImpl { public ReturnToHandTargetCost(TargetControlledPermanent target) { this.addTarget(target); this.text = "return " + target.getTargetName() + " you control to it's owner's hand"; } + public ReturnToHandTargetCost(ReturnToHandTargetCost cost) { + super(cost); + } + @Override - public boolean pay(Game game, boolean noMana) { - if (targets.choose(Outcome.ReturnToHand, game)) { - Permanent source = game.getPermanent(targets.getFirstTarget()); - if (source != null) { - paid = source.moveToZone(Zone.HAND, game, false); + public boolean pay(Game game, Ability source, boolean noMana) { + if (targets.choose(Outcome.ReturnToHand, source.getControllerId(), source, game)) { + Permanent permanent = game.getPermanent(targets.getFirstTarget()); + if (permanent != null) { + paid = permanent.moveToZone(Zone.HAND, game, false); } } return paid; } @Override - public boolean canPay(UUID playerId, Game game) { - return targets.canChoose(playerId, playerId, game); + public boolean canPay(Ability source, Game game) { + return targets.canChoose(source.getControllerId(), source.getControllerId(), game); + } + + @Override + public ReturnToHandTargetCost copy() { + return new ReturnToHandTargetCost(this); } diff --git a/Mage/src/mage/abilities/costs/common/SacrificeSourceCost.java b/Mage/src/mage/abilities/costs/common/SacrificeSourceCost.java index c96c47facb3..de507ac9feb 100644 --- a/Mage/src/mage/abilities/costs/common/SacrificeSourceCost.java +++ b/Mage/src/mage/abilities/costs/common/SacrificeSourceCost.java @@ -29,6 +29,7 @@ package mage.abilities.costs.common; import java.util.UUID; +import mage.abilities.Ability; import mage.abilities.costs.CostImpl; import mage.game.Game; import mage.game.permanent.Permanent; @@ -37,28 +38,37 @@ import mage.game.permanent.Permanent; * * @author BetaSteward_at_googlemail.com */ -public class SacrificeSourceCost extends CostImpl { +public class SacrificeSourceCost extends CostImpl { public SacrificeSourceCost() { this.text = "Sacrifice {this}"; } + public SacrificeSourceCost(SacrificeSourceCost cost) { + super(cost); + } + @Override - public boolean pay(Game game, boolean noMana) { - Permanent source = game.getPermanent(ability.getSourceId()); - if (source != null) { - paid = source.sacrifice(this.ability.getSourceId(), game); + public boolean pay(Game game, Ability source, boolean noMana) { + Permanent permanent = game.getPermanent(source.getSourceId()); + if (permanent != null) { + paid = permanent.sacrifice(source.getSourceId(), game); } return paid; } @Override - public boolean canPay(UUID playerId, Game game) { - Permanent source = game.getPermanent(ability.getSourceId()); - if (source != null) { + public boolean canPay(Ability source, Game game) { + Permanent permanent = game.getPermanent(source.getSourceId()); + if (permanent != null) { return true; } return false; } + @Override + public SacrificeSourceCost copy() { + return new SacrificeSourceCost(this); + } + } diff --git a/Mage/src/mage/abilities/costs/common/SacrificeTargetCost.java b/Mage/src/mage/abilities/costs/common/SacrificeTargetCost.java index f8b65c15759..d4861ed5476 100644 --- a/Mage/src/mage/abilities/costs/common/SacrificeTargetCost.java +++ b/Mage/src/mage/abilities/costs/common/SacrificeTargetCost.java @@ -28,7 +28,6 @@ package mage.abilities.costs.common; -import java.util.UUID; import mage.Constants.Outcome; import mage.abilities.Ability; import mage.abilities.costs.CostImpl; @@ -40,31 +39,36 @@ import mage.target.common.TargetControlledPermanent; * * @author BetaSteward_at_googlemail.com */ -public class SacrificeTargetCost extends CostImpl { +public class SacrificeTargetCost extends CostImpl { public SacrificeTargetCost(TargetControlledPermanent target) { this.addTarget(target); this.text = "Sacrifice " + target.getTargetName(); } + public SacrificeTargetCost(SacrificeTargetCost cost) { + super(cost); + } + @Override - public boolean pay(Game game, boolean noMana) { - if (targets.choose(Outcome.Sacrifice, game)) { - Permanent source = game.getPermanent(targets.getFirstTarget()); - if (source != null) { - paid = source.sacrifice(this.ability.getSourceId(), game); + public boolean pay(Game game, Ability source, boolean noMana) { + if (targets.choose(Outcome.Sacrifice, source.getControllerId(), source, game)) { + Permanent permanent = game.getPermanent(targets.getFirstTarget()); + if (permanent != null) { + paid = permanent.sacrifice(source.getSourceId(), game); } } return paid; } @Override - public boolean canPay(UUID playerId, Game game) { - return targets.canChoose(playerId, playerId, game); + public boolean canPay(Ability source, Game game) { + return targets.canChoose(source.getControllerId(), source.getControllerId(), game); } @Override - public void setAbility(Ability ability) { - super.setAbility(ability); + public SacrificeTargetCost copy() { + return new SacrificeTargetCost(this); } + } diff --git a/Mage/src/mage/abilities/costs/common/TapSourceCost.java b/Mage/src/mage/abilities/costs/common/TapSourceCost.java index 4ee099a0e39..e31ce2c8c2a 100644 --- a/Mage/src/mage/abilities/costs/common/TapSourceCost.java +++ b/Mage/src/mage/abilities/costs/common/TapSourceCost.java @@ -38,28 +38,36 @@ import mage.game.permanent.Permanent; * * @author BetaSteward_at_googlemail.com */ -public class TapSourceCost extends CostImpl { +public class TapSourceCost extends CostImpl { public TapSourceCost() { this.text = "{T}"; } + public TapSourceCost(TapSourceCost cost) { + super(cost); + } + @Override - public boolean pay(Game game, boolean noMana) { - Permanent source = game.getPermanent(ability.getSourceId()); - if (source != null) { - paid = source.tap(game); + public boolean pay(Game game, Ability source, boolean noMana) { + Permanent permanent = game.getPermanent(source.getSourceId()); + if (permanent != null) { + paid = permanent.tap(game); } return paid; } @Override - public boolean canPay(UUID playerId, Game game) { - Permanent source = game.getPermanent(ability.getSourceId()); - if (source != null) { - return !source.isTapped() && source.canTap(); + public boolean canPay(Ability source, Game game) { + Permanent permanent = game.getPermanent(source.getSourceId()); + if (permanent != null) { + return !permanent.isTapped() && permanent.canTap(); } return false; } + @Override + public TapSourceCost copy() { + return new TapSourceCost(this); + } } diff --git a/Mage/src/mage/abilities/costs/mana/ColoredManaCost.java b/Mage/src/mage/abilities/costs/mana/ColoredManaCost.java index 3843bce2e11..ba9d31cc101 100644 --- a/Mage/src/mage/abilities/costs/mana/ColoredManaCost.java +++ b/Mage/src/mage/abilities/costs/mana/ColoredManaCost.java @@ -32,7 +32,7 @@ import mage.Constants.ColoredManaSymbol; import mage.Mana; import mage.players.ManaPool; -public class ColoredManaCost extends ManaCostImpl implements ManaCost { +public class ColoredManaCost extends ManaCostImpl { protected ColoredManaSymbol mana; @@ -41,6 +41,11 @@ public class ColoredManaCost extends ManaCostImpl implements ManaCost { addColoredOption(mana); } + public ColoredManaCost(ColoredManaCost cost) { + super(cost); + this.mana = cost.mana; + } + public ColoredManaSymbol getMana() { return mana; } @@ -89,4 +94,9 @@ public class ColoredManaCost extends ManaCostImpl implements ManaCost { return false; } + @Override + public ColoredManaCost copy() { + return new ColoredManaCost(this); + } + } diff --git a/Mage/src/mage/abilities/costs/mana/GenericManaCost.java b/Mage/src/mage/abilities/costs/mana/GenericManaCost.java index 9dc08dbfca4..04074d23983 100644 --- a/Mage/src/mage/abilities/costs/mana/GenericManaCost.java +++ b/Mage/src/mage/abilities/costs/mana/GenericManaCost.java @@ -31,7 +31,7 @@ package mage.abilities.costs.mana; import mage.Mana; import mage.players.ManaPool; -public class GenericManaCost extends ManaCostImpl implements ManaCost { +public class GenericManaCost extends ManaCostImpl { protected int mana; @@ -40,6 +40,11 @@ public class GenericManaCost extends ManaCostImpl implements ManaCost { this.options.addMana(Mana.ColorlessMana(mana)); } + public GenericManaCost(GenericManaCost cost) { + super(cost); + this.mana = cost.mana; + } + public int getMana() { return mana; } @@ -76,4 +81,9 @@ public class GenericManaCost extends ManaCostImpl implements ManaCost { return testMana.count() > 0; } + @Override + public GenericManaCost copy() { + return new GenericManaCost(this); + } + } diff --git a/Mage/src/mage/abilities/costs/mana/HybridManaCost.java b/Mage/src/mage/abilities/costs/mana/HybridManaCost.java index 546a373a413..3176a3515b7 100644 --- a/Mage/src/mage/abilities/costs/mana/HybridManaCost.java +++ b/Mage/src/mage/abilities/costs/mana/HybridManaCost.java @@ -32,7 +32,7 @@ import mage.Constants.ColoredManaSymbol; import mage.Mana; import mage.players.ManaPool; -public class HybridManaCost extends ManaCostImpl implements ManaCost { +public class HybridManaCost extends ManaCostImpl { private ColoredManaSymbol mana1; private ColoredManaSymbol mana2; @@ -43,6 +43,12 @@ public class HybridManaCost extends ManaCostImpl implements ManaCost { addColoredOption(mana2); } + public HybridManaCost(HybridManaCost cost) { + super(cost); + this.mana1 = cost.mana1; + this.mana2 = cost.mana2; + } + @Override public int convertedManaCost() { return 1; @@ -113,4 +119,9 @@ public class HybridManaCost extends ManaCostImpl implements ManaCost { } return false; } + + @Override + public HybridManaCost copy() { + return new HybridManaCost(this); + } } diff --git a/Mage/src/mage/abilities/costs/mana/ManaCost.java b/Mage/src/mage/abilities/costs/mana/ManaCost.java index 0afe90bc548..8aa0ccc2985 100644 --- a/Mage/src/mage/abilities/costs/mana/ManaCost.java +++ b/Mage/src/mage/abilities/costs/mana/ManaCost.java @@ -43,5 +43,7 @@ public interface ManaCost extends Cost { public ManaCost getUnpaid(); public ManaOptions getOptions(); public boolean testPay(Mana testMana); - + + @Override + public ManaCost copy(); } diff --git a/Mage/src/mage/abilities/costs/mana/ManaCostImpl.java b/Mage/src/mage/abilities/costs/mana/ManaCostImpl.java index a79e82d3ead..60f801fd94c 100644 --- a/Mage/src/mage/abilities/costs/mana/ManaCostImpl.java +++ b/Mage/src/mage/abilities/costs/mana/ManaCostImpl.java @@ -28,21 +28,32 @@ package mage.abilities.costs.mana; -import java.util.UUID; import mage.Constants.ColoredManaSymbol; import mage.Mana; +import mage.abilities.Ability; import mage.abilities.costs.CostImpl; import mage.abilities.mana.ManaOptions; import mage.game.Game; import mage.players.ManaPool; import mage.players.Player; -public abstract class ManaCostImpl extends CostImpl implements ManaCost { +public abstract class ManaCostImpl> extends CostImpl implements ManaCost { - protected Mana payment = new Mana(); - protected ManaOptions options = new ManaOptions(); + protected Mana payment; + protected ManaOptions options; + + @Override + public abstract T copy(); public ManaCostImpl() { + payment = new Mana(); + options = new ManaOptions(); + } + + public ManaCostImpl(final ManaCostImpl cost) { + super(cost); + this.payment = cost.payment.copy(); + this.options = cost.options.copy(); } @Override @@ -166,17 +177,17 @@ public abstract class ManaCostImpl extends CostImpl implements ManaCost { } @Override - public boolean canPay(UUID playerId, Game game) { + public boolean canPay(Ability source, Game game) { return true; } @Override - public boolean pay(Game game, boolean noMana) { + public boolean pay(Game game, Ability source, boolean noMana) { if (noMana) { setPaid(); return true; } - Player player = game.getPlayer(this.getAbility().getControllerId()); + Player player = game.getPlayer(source.getControllerId()); assignPayment(player.getManaPool()); while (!isPaid()) { if (player.playMana(this, game)) diff --git a/Mage/src/mage/abilities/costs/mana/ManaCosts.java b/Mage/src/mage/abilities/costs/mana/ManaCosts.java index 6563f003d39..27a986f52c5 100644 --- a/Mage/src/mage/abilities/costs/mana/ManaCosts.java +++ b/Mage/src/mage/abilities/costs/mana/ManaCosts.java @@ -28,224 +28,19 @@ package mage.abilities.costs.mana; -import java.util.ArrayList; import java.util.List; -import mage.Constants.ColoredManaSymbol; -import mage.Mana; -import mage.abilities.Ability; -import mage.abilities.costs.Costs; -import mage.abilities.costs.CostsImpl; -import mage.abilities.mana.ManaOptions; -import mage.game.Game; -import mage.players.ManaPool; -import mage.players.Player; /** * * @author BetaSteward_at_googlemail.com */ -public class ManaCosts extends CostsImpl implements Costs, ManaCost { +public interface ManaCosts extends List, ManaCost { - public ManaCosts(String mana) { - load(mana); - } - - public ManaCosts(Ability ability) { - super(ability); - } - - public ManaCosts(Ability ability, String mana) { - super(ability); - load(mana); - } + public ManaCosts getUnpaidVariableCosts(); + public List getVariableCosts(); + public void load(String mana); + public List getSymbols(); @Override - public boolean add(ManaCost cost) { - if (cost instanceof ManaCosts) { - for (ManaCost manaCost: (ManaCosts)cost) { - super.add(manaCost); - } - return true; - } - else { - return super.add(cost); - } - } - - @Override - public int convertedManaCost() { - int total = 0; - for (ManaCost cost: this) { - total += cost.convertedManaCost(); - } - return total; - } - - @Override - public Mana getPayment() { - Mana manaTotal = new Mana(); - for (ManaCost cost: this) { - manaTotal.add(cost.getPayment()); - } - return manaTotal; - } - - @Override - public boolean pay(Game game, boolean noMana) { - if (this.size() == 0 || noMana) { - setPaid(); - return true; - } - Player player = game.getPlayer(this.getAbility().getControllerId()); - assignPayment(player.getManaPool()); - while (!isPaid()) { - if (player.playMana(this.getUnpaid(), game)) - assignPayment(player.getManaPool()); - else - return false; - } - for (ManaCost cost: this.getUnpaidVariableCosts()) { - VariableManaCost vCost = (VariableManaCost) cost; - while (!vCost.isPaid()) { - if (player.playXMana(vCost, game)) - vCost.assignPayment(player.getManaPool()); - else - return false; - } - } - return true; - } - - @Override - public ManaCosts getUnpaid() { - ManaCosts unpaid = new ManaCosts(ability); - for (ManaCost cost: this) { - if (!(cost instanceof VariableManaCost) && !cost.isPaid()) - unpaid.add(cost.getUnpaid()); - } - return unpaid; - } - - public ManaCosts getUnpaidVariableCosts() { - ManaCosts unpaid = new ManaCosts(ability); - for (ManaCost cost: this) { - if (cost instanceof VariableManaCost && !cost.isPaid()) - unpaid.add(cost.getUnpaid()); - } - return unpaid; - } - - public List getVariableCosts() { - List variableCosts = new ArrayList(); - for (ManaCost cost: this) { - if (cost instanceof VariableManaCost) - variableCosts.add((VariableManaCost) cost); - } - return variableCosts; - } - - @Override - public void assignPayment(ManaPool pool) { - //attempt to pay colored costs first - - for (ManaCost cost: this) { - if (!cost.isPaid() && cost instanceof ColoredManaCost) { - cost.assignPayment(pool); - } - } - - for (ManaCost cost: this) { - if (!cost.isPaid() && cost instanceof HybridManaCost) { - cost.assignPayment(pool); - } - } - - for (ManaCost cost: this) { - if (!cost.isPaid() && cost instanceof MonoHybridManaCost) { - cost.assignPayment(pool); - } - } - - for (ManaCost cost: this) { - if (!cost.isPaid() && cost instanceof GenericManaCost) { - cost.assignPayment(pool); - } - } - - for (ManaCost cost: this) { - if (!cost.isPaid() && cost instanceof VariableManaCost) { - cost.assignPayment(pool); - } - } - } - - public void load(String mana) { - this.clear(); - if (mana == null || mana.length() == 0) - return; - String[] symbols = mana.split("^\\{|\\}\\{|\\}$"); - for (String symbol: symbols) { - if (symbol.length() > 0) { - if (symbol.length() == 1) { - if (Character.isDigit(symbol.charAt(0))) { - this.add(new GenericManaCost(Integer.valueOf(symbol))); - } - else { - if (!symbol.equals("X")) - this.add(new ColoredManaCost(ColoredManaSymbol.lookup(symbol.charAt(0)))); - else - this.add(new VariableManaCost()); - //TODO: handle multiple {X} and/or {Y} symbols - } - } - else { - if (Character.isDigit(symbol.charAt(0))) { - this.add(new MonoHybridManaCost(ColoredManaSymbol.lookup(symbol.charAt(2)))); - } - else { - this.add(new HybridManaCost(ColoredManaSymbol.lookup(symbol.charAt(0)), ColoredManaSymbol.lookup(symbol.charAt(2)))); - } - } - } - } - } - - public List getSymbols() { - List symbols = new ArrayList(); - for (ManaCost cost: this) { - symbols.add(cost.getText()); - } - return symbols; - } - - @Override - public String getText() { - if (this.size() == 0) - return ""; - - StringBuilder sbText = new StringBuilder(); - for (ManaCost cost: this) { - sbText.append(cost.getText()); - } - return sbText.toString(); - } - - @Override - public ManaOptions getOptions() { - ManaOptions options = new ManaOptions(); - for (ManaCost cost: this) { - options.addMana(cost.getOptions()); - } - return options; - } - - @Override - public boolean testPay(Mana testMana) { - for (ManaCost cost: this) { - if (cost.testPay(testMana)) - return true; - } - return false; - } - + public ManaCosts copy(); } diff --git a/Mage/src/mage/abilities/costs/mana/ManaCostsImpl.java b/Mage/src/mage/abilities/costs/mana/ManaCostsImpl.java new file mode 100644 index 00000000000..860e4702cb3 --- /dev/null +++ b/Mage/src/mage/abilities/costs/mana/ManaCostsImpl.java @@ -0,0 +1,299 @@ +/* +* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, are +* permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, this list of +* conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, this list +* of conditions and the following disclaimer in the documentation and/or other materials +* provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR +* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +* The views and conclusions contained in the software and documentation are those of the +* authors and should not be interpreted as representing official policies, either expressed +* or implied, of BetaSteward_at_googlemail.com. +*/ + +package mage.abilities.costs.mana; + +import java.util.ArrayList; +import java.util.List; +import mage.Constants.ColoredManaSymbol; +import mage.Mana; +import mage.abilities.Ability; +import mage.abilities.mana.ManaOptions; +import mage.game.Game; +import mage.players.ManaPool; +import mage.players.Player; +import mage.target.Targets; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class ManaCostsImpl extends ArrayList implements ManaCosts { + + public ManaCostsImpl() {} + + public ManaCostsImpl(String mana) { + load(mana); + } + + public ManaCostsImpl(final ManaCostsImpl costs) { + for (T cost: costs) { + this.add((T)cost.copy()); + } + } + + @Override + public boolean add(ManaCost cost) { + if (cost instanceof ManaCosts) { + for (ManaCost manaCost: (ManaCosts)cost) { + super.add((T)manaCost); + } + return true; + } + else { + return super.add((T)cost); + } + } + + @Override + public int convertedManaCost() { + int total = 0; + for (ManaCost cost: this) { + total += cost.convertedManaCost(); + } + return total; + } + + @Override + public Mana getPayment() { + Mana manaTotal = new Mana(); + for (ManaCost cost: this) { + manaTotal.add(cost.getPayment()); + } + return manaTotal; + } + + @Override + public boolean pay(Game game, Ability source, boolean noMana) { + if (this.size() == 0 || noMana) { + setPaid(); + return true; + } + Player player = game.getPlayer(source.getControllerId()); + assignPayment(player.getManaPool()); + while (!isPaid()) { + if (player.playMana(this.getUnpaid(), game)) + assignPayment(player.getManaPool()); + else + return false; + } + for (ManaCost cost: this.getUnpaidVariableCosts()) { + VariableManaCost vCost = (VariableManaCost) cost; + while (!vCost.isPaid()) { + if (player.playXMana(vCost, game)) + vCost.assignPayment(player.getManaPool()); + else + return false; + } + } + return true; + } + + @Override + public ManaCosts getUnpaid() { + ManaCosts unpaid = new ManaCostsImpl(); + for (T cost: this) { + if (!(cost instanceof VariableManaCost) && !cost.isPaid()) + unpaid.add((T)cost.getUnpaid()); + } + return unpaid; + } + + @Override + public ManaCosts getUnpaidVariableCosts() { + ManaCosts unpaid = new ManaCostsImpl(); + for (ManaCost cost: this) { + if (cost instanceof VariableManaCost && !cost.isPaid()) + unpaid.add((T)cost.getUnpaid()); + } + return unpaid; + } + + @Override + public List getVariableCosts() { + List variableCosts = new ArrayList(); + for (ManaCost cost: this) { + if (cost instanceof VariableManaCost) + variableCosts.add((VariableManaCost) cost); + } + return variableCosts; + } + + @Override + public void assignPayment(ManaPool pool) { + //attempt to pay colored costs first + + for (ManaCost cost: this) { + if (!cost.isPaid() && cost instanceof ColoredManaCost) { + cost.assignPayment(pool); + } + } + + for (ManaCost cost: this) { + if (!cost.isPaid() && cost instanceof HybridManaCost) { + cost.assignPayment(pool); + } + } + + for (ManaCost cost: this) { + if (!cost.isPaid() && cost instanceof MonoHybridManaCost) { + cost.assignPayment(pool); + } + } + + for (ManaCost cost: this) { + if (!cost.isPaid() && cost instanceof GenericManaCost) { + cost.assignPayment(pool); + } + } + + for (ManaCost cost: this) { + if (!cost.isPaid() && cost instanceof VariableManaCost) { + cost.assignPayment(pool); + } + } + } + + @Override + public void load(String mana) { + this.clear(); + if (mana == null || mana.length() == 0) + return; + String[] symbols = mana.split("^\\{|\\}\\{|\\}$"); + for (String symbol: symbols) { + if (symbol.length() > 0) { + if (symbol.length() == 1) { + if (Character.isDigit(symbol.charAt(0))) { + this.add((T)new GenericManaCost(Integer.valueOf(symbol))); + } + else { + if (!symbol.equals("X")) + this.add((T)new ColoredManaCost(ColoredManaSymbol.lookup(symbol.charAt(0)))); + else + this.add((T)new VariableManaCost()); + //TODO: handle multiple {X} and/or {Y} symbols + } + } + else { + if (Character.isDigit(symbol.charAt(0))) { + this.add((T)new MonoHybridManaCost(ColoredManaSymbol.lookup(symbol.charAt(2)))); + } + else { + this.add((T)new HybridManaCost(ColoredManaSymbol.lookup(symbol.charAt(0)), ColoredManaSymbol.lookup(symbol.charAt(2)))); + } + } + } + } + } + + @Override + public List getSymbols() { + List symbols = new ArrayList(); + for (ManaCost cost: this) { + symbols.add(cost.getText()); + } + return symbols; + } + + @Override + public String getText() { + if (this.size() == 0) + return ""; + + StringBuilder sbText = new StringBuilder(); + for (ManaCost cost: this) { + sbText.append(cost.getText()); + } + return sbText.toString(); + } + + @Override + public ManaOptions getOptions() { + ManaOptions options = new ManaOptions(); + for (ManaCost cost: this) { + options.addMana(cost.getOptions()); + } + return options; + } + + @Override + public boolean testPay(Mana testMana) { + for (ManaCost cost: this) { + if (cost.testPay(testMana)) + return true; + } + return false; + } + + @Override + public boolean canPay(Ability source, Game game) { + for (T cost: this) { + if (!cost.canPay(source, game)) + return false; + } + return true; + } + + @Override + public boolean isPaid() { + for (T cost: this) { + if (!((T)cost instanceof VariableManaCost) && !cost.isPaid()) + return false; + } + return true; + } + + @Override + public void clearPaid() { + for (T cost: this) { + cost.clearPaid(); + } + } + + @Override + public void setPaid() { + for (T cost: this) { + cost.setPaid(); + } + } + + @Override + public Targets getTargets() { + Targets targets = new Targets(); + for (T cost: this) { + targets.addAll(cost.getTargets()); + } + return targets; + } + + @Override + public ManaCosts copy() { + return new ManaCostsImpl(this); + } + +} diff --git a/Mage/src/mage/abilities/costs/mana/MonoHybridManaCost.java b/Mage/src/mage/abilities/costs/mana/MonoHybridManaCost.java index e659dc4d288..732007a5cff 100644 --- a/Mage/src/mage/abilities/costs/mana/MonoHybridManaCost.java +++ b/Mage/src/mage/abilities/costs/mana/MonoHybridManaCost.java @@ -32,7 +32,7 @@ import mage.Constants.ColoredManaSymbol; import mage.Mana; import mage.players.ManaPool; -public class MonoHybridManaCost extends ManaCostImpl implements ManaCost { +public class MonoHybridManaCost extends ManaCostImpl { private ColoredManaSymbol mana; private int mana2 = 2; @@ -43,6 +43,12 @@ public class MonoHybridManaCost extends ManaCostImpl implements ManaCost { options.add(Mana.ColorlessMana(2)); } + public MonoHybridManaCost(MonoHybridManaCost cost) { + super(cost); + this.mana = cost.mana; + this.mana2 = cost.mana2; + } + @Override public int convertedManaCost() { return 2; @@ -98,4 +104,9 @@ public class MonoHybridManaCost extends ManaCostImpl implements ManaCost { return testMana.count() > 0; } + @Override + public MonoHybridManaCost copy() { + return new MonoHybridManaCost(this); + } + } diff --git a/Mage/src/mage/abilities/costs/mana/VariableManaCost.java b/Mage/src/mage/abilities/costs/mana/VariableManaCost.java index 9f9b1fdabc9..56cc20093fb 100644 --- a/Mage/src/mage/abilities/costs/mana/VariableManaCost.java +++ b/Mage/src/mage/abilities/costs/mana/VariableManaCost.java @@ -35,7 +35,7 @@ import mage.players.ManaPool; * * @author BetaSteward_at_googlemail.com */ -public class VariableManaCost extends ManaCostImpl implements ManaCost { +public class VariableManaCost extends ManaCostImpl { protected Mana manaPaid = new Mana(); protected int multiplier; @@ -49,6 +49,12 @@ public class VariableManaCost extends ManaCostImpl implements ManaCost { this.multiplier = multiplier; } + public VariableManaCost(VariableManaCost cost) { + super(cost); + this.manaPaid = cost.manaPaid.copy(); + this.multiplier = cost.multiplier; + } + @Override public int convertedManaCost() { return 0; @@ -97,5 +103,10 @@ public class VariableManaCost extends ManaCostImpl implements ManaCost { public boolean testPay(Mana testMana) { return true; } + + @Override + public VariableManaCost copy() { + return new VariableManaCost(this); + } } diff --git a/Mage/src/mage/abilities/effects/ApplyCountersEffect.java b/Mage/src/mage/abilities/effects/ApplyCountersEffect.java new file mode 100644 index 00000000000..3e3a3fc3a52 --- /dev/null +++ b/Mage/src/mage/abilities/effects/ApplyCountersEffect.java @@ -0,0 +1,71 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.abilities.effects; + +import mage.Constants.CardType; +import mage.Constants.Duration; +import mage.Constants.Layer; +import mage.Constants.Outcome; +import mage.Constants.SubLayer; +import mage.abilities.Ability; +import mage.counters.BoostCounter; +import mage.game.Game; +import mage.game.permanent.Permanent; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class ApplyCountersEffect extends ContinuousEffectImpl { + + public ApplyCountersEffect() { + super(Duration.EndOfGame, Layer.PTChangingEffects_7, SubLayer.Counters_7d, Outcome.BoostCreature); + } + + public ApplyCountersEffect(ApplyCountersEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + for (Permanent permanent: game.getBattlefield().getAllActivePermanents(CardType.CREATURE)) { + for (BoostCounter counter: permanent.getCounters().getBoostCounters()) { + permanent.addPower(counter.getPower() * counter.getCount()); + permanent.addToughness(counter.getToughness() * counter.getCount()); + } + } + return true; + } + + @Override + public ApplyCountersEffect copy() { + return new ApplyCountersEffect(this); + } + +} diff --git a/Mage/src/mage/abilities/effects/ContinuousEffect.java b/Mage/src/mage/abilities/effects/ContinuousEffect.java index 30c4e8c358a..ffaeb005094 100644 --- a/Mage/src/mage/abilities/effects/ContinuousEffect.java +++ b/Mage/src/mage/abilities/effects/ContinuousEffect.java @@ -32,17 +32,18 @@ import java.util.Date; import mage.Constants.Duration; import mage.Constants.Layer; import mage.Constants.SubLayer; +import mage.abilities.Ability; import mage.game.Game; /** * * @author BetaSteward_at_googlemail.com */ -public interface ContinuousEffect extends Effect { +public interface ContinuousEffect> extends Effect { public Duration getDuration(); public Date getTimestamp(); - public boolean apply(Layer layer, SubLayer sublayer, Game game); + public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game); public boolean hasLayer(Layer layer); } diff --git a/Mage/src/mage/abilities/effects/ContinuousEffectImpl.java b/Mage/src/mage/abilities/effects/ContinuousEffectImpl.java index fd62414fbc9..09de9afecc7 100644 --- a/Mage/src/mage/abilities/effects/ContinuousEffectImpl.java +++ b/Mage/src/mage/abilities/effects/ContinuousEffectImpl.java @@ -33,40 +33,49 @@ import mage.Constants.Duration; import mage.Constants.Layer; import mage.Constants.Outcome; import mage.Constants.SubLayer; +import mage.abilities.Ability; import mage.game.Game; /** * * @author BetaSteward_at_googlemail.com */ -public abstract class ContinuousEffectImpl extends EffectImpl implements ContinuousEffect { +public abstract class ContinuousEffectImpl> extends EffectImpl implements ContinuousEffect { protected Duration duration; protected Layer layer; protected SubLayer sublayer; - protected Date timestamp = new Date(); + protected Date timestamp; public ContinuousEffectImpl(Duration duration, Outcome outcome) { super(outcome); this.duration = duration; + this.timestamp = new Date(); } public ContinuousEffectImpl(Duration duration, Layer layer, SubLayer sublayer, Outcome outcome) { - super(outcome); - this.duration = duration; + this(duration, outcome); this.layer = layer; this.sublayer = sublayer; } + public ContinuousEffectImpl(final ContinuousEffectImpl effect) { + super(effect); + this.duration = effect.duration; + this.layer = effect.layer; + this.sublayer = effect.sublayer; + this.timestamp = new Date(effect.timestamp.getTime()); + } + @Override public Duration getDuration() { return duration; } @Override - public boolean apply(Layer layer, SubLayer sublayer, Game game) { + public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { if (this.layer == layer && this.sublayer == sublayer) { - return apply(game); + return apply(game, source); } return false; } diff --git a/Mage/src/mage/abilities/effects/ContinuousEffects.java b/Mage/src/mage/abilities/effects/ContinuousEffects.java index 139638ea9de..c9d85af9f87 100644 --- a/Mage/src/mage/abilities/effects/ContinuousEffects.java +++ b/Mage/src/mage/abilities/effects/ContinuousEffects.java @@ -32,14 +32,15 @@ import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; +import java.util.HashMap; import java.util.Iterator; import java.util.List; -import java.util.UUID; -import mage.Constants.CardType; +import java.util.Map; +import java.util.Map.Entry; import mage.Constants.Duration; import mage.Constants.Layer; import mage.Constants.SubLayer; -import mage.counters.BoostCounter; +import mage.abilities.Ability; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; @@ -53,10 +54,26 @@ import mage.players.Player; */ public class ContinuousEffects implements Serializable { - private List effects = new ArrayList(); + private final Map effects = new HashMap(); + private final ApplyCountersEffect applyCounters; + + public ContinuousEffects() { + applyCounters = new ApplyCountersEffect(); + } + + public ContinuousEffects(ContinuousEffects effect) { + this.applyCounters = effect.applyCounters.copy(); + for (Entry entry: effect.effects.entrySet()) { + effects.put((ContinuousEffect)entry.getKey().copy(), entry.getValue().copy()); + } + } + + public ContinuousEffects copy() { + return new ContinuousEffects(this); + } public void removeEndOfTurnEffects() { - for (Iterator i = effects.iterator(); i.hasNext();) { + for (Iterator i = effects.keySet().iterator(); i.hasNext();) { ContinuousEffect entry = i.next(); if (entry.getDuration() == Duration.EndOfTurn) i.remove(); @@ -64,10 +81,10 @@ public class ContinuousEffects implements Serializable { } public void removeInactiveEffects(Game game) { - for (Iterator i = effects.iterator(); i.hasNext();) { + for (Iterator i = effects.keySet().iterator(); i.hasNext();) { ContinuousEffect entry = i.next(); if (entry.getDuration() == Duration.WhileOnBattlefield) { - Permanent permanent = (Permanent)game.getPermanent(entry.getSource().getSourceId()); + Permanent permanent = game.getPermanent(effects.get(entry).getSourceId()); if (permanent == null || !permanent.isPhasedIn()) i.remove(); } @@ -82,7 +99,7 @@ public class ContinuousEffects implements Serializable { private List getLayeredEffects() { List layerEffects = new ArrayList(); - for (ContinuousEffect effect: effects) { + for (ContinuousEffect effect: effects.keySet()) { if (!(effect instanceof ReplacementEffect) && !(effect instanceof PreventionEffect)) { layerEffects.add(effect); } @@ -94,9 +111,9 @@ public class ContinuousEffects implements Serializable { private List getApplicableReplacementEffects(GameEvent event, Game game) { List replacementEffects = new ArrayList(); - for (Effect effect: effects) { - if (effect instanceof ReplacementEffect && ((ReplacementEffect)effect).applies(event, game)) { - if (((ReplacementEffect)effect).getDuration() != Duration.OneUse || !((ReplacementEffect)effect).isUsed()) + for (ContinuousEffect effect: effects.keySet()) { + if (effect instanceof ReplacementEffect && ((ReplacementEffect)effect).applies(event, effects.get(effect), game)) { + if (effect.getDuration() != Duration.OneUse || !((ReplacementEffect)effect).isUsed()) replacementEffects.add((ReplacementEffect)effect); } } @@ -129,13 +146,15 @@ public class ContinuousEffects implements Serializable { if (!caught) { List rEffects = getApplicableReplacementEffects(event, game); if (rEffects.size() > 0) { + int index; if (rEffects.size() == 1) { - caught = rEffects.get(0).replaceEvent(event, game); + index = 0; } else { Player player = game.getPlayer(event.getPlayerId()); - caught = rEffects.get(player.chooseEffect(rEffects, game)).replaceEvent(event, game); + index = player.chooseEffect(rEffects, game); } + caught = rEffects.get(index).replaceEvent(event, effects.get(rEffects.get(index)), game); } } @@ -147,93 +166,93 @@ public class ContinuousEffects implements Serializable { removeInactiveEffects(game); List layeredEffects = getLayeredEffects(); for (ContinuousEffect effect: layeredEffects) { - effect.apply(Layer.CopyEffects_1, SubLayer.CharacteristicDefining_7a, game); + effect.apply(Layer.CopyEffects_1, SubLayer.CharacteristicDefining_7a, effects.get(effect), game); } for (ContinuousEffect effect: layeredEffects) { - effect.apply(Layer.CopyEffects_1, SubLayer.NA, game); + effect.apply(Layer.CopyEffects_1, SubLayer.NA, effects.get(effect), game); } for (ContinuousEffect effect: layeredEffects) { - effect.apply(Layer.ControlChangingEffects_2, SubLayer.CharacteristicDefining_7a, game); + effect.apply(Layer.ControlChangingEffects_2, SubLayer.CharacteristicDefining_7a, effects.get(effect), game); } for (ContinuousEffect effect: layeredEffects) { - effect.apply(Layer.ControlChangingEffects_2, SubLayer.NA, game); + effect.apply(Layer.ControlChangingEffects_2, SubLayer.NA, effects.get(effect), game); } for (ContinuousEffect effect: layeredEffects) { - effect.apply(Layer.TextChangingEffects_3, SubLayer.CharacteristicDefining_7a, game); + effect.apply(Layer.TextChangingEffects_3, SubLayer.CharacteristicDefining_7a, effects.get(effect), game); } for (ContinuousEffect effect: layeredEffects) { - effect.apply(Layer.TextChangingEffects_3, SubLayer.NA, game); + effect.apply(Layer.TextChangingEffects_3, SubLayer.NA, effects.get(effect), game); } for (ContinuousEffect effect: layeredEffects) { - effect.apply(Layer.TypeChangingEffects_4, SubLayer.CharacteristicDefining_7a, game); + effect.apply(Layer.TypeChangingEffects_4, SubLayer.CharacteristicDefining_7a, effects.get(effect), game); } for (ContinuousEffect effect: layeredEffects) { - effect.apply(Layer.TypeChangingEffects_4, SubLayer.NA, game); + effect.apply(Layer.TypeChangingEffects_4, SubLayer.NA, effects.get(effect), game); } for (ContinuousEffect effect: layeredEffects) { - effect.apply(Layer.ColorChangingEffects_5, SubLayer.CharacteristicDefining_7a, game); + effect.apply(Layer.ColorChangingEffects_5, SubLayer.CharacteristicDefining_7a, effects.get(effect), game); } for (ContinuousEffect effect: layeredEffects) { - effect.apply(Layer.ColorChangingEffects_5, SubLayer.NA, game); + effect.apply(Layer.ColorChangingEffects_5, SubLayer.NA, effects.get(effect), game); } for (ContinuousEffect effect: layeredEffects) { - effect.apply(Layer.AbilityAddingRemovingEffects_6, SubLayer.CharacteristicDefining_7a, game); + effect.apply(Layer.AbilityAddingRemovingEffects_6, SubLayer.CharacteristicDefining_7a, effects.get(effect), game); } for (ContinuousEffect effect: layeredEffects) { - effect.apply(Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, game); + effect.apply(Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, effects.get(effect), game); } for (ContinuousEffect effect: layeredEffects) { - effect.apply(Layer.PTChangingEffects_7, SubLayer.CharacteristicDefining_7a, game); + effect.apply(Layer.PTChangingEffects_7, SubLayer.CharacteristicDefining_7a, effects.get(effect), game); } for (ContinuousEffect effect: layeredEffects) { - effect.apply(Layer.PTChangingEffects_7, SubLayer.SetPT_7b, game); - } - applyCounters(game); - for (ContinuousEffect effect: layeredEffects) { - effect.apply(Layer.PTChangingEffects_7, SubLayer.ModifyPT_7c, game); + effect.apply(Layer.PTChangingEffects_7, SubLayer.SetPT_7b, effects.get(effect), game); } for (ContinuousEffect effect: layeredEffects) { - effect.apply(Layer.PTChangingEffects_7, SubLayer.Counters_7d, game); + effect.apply(Layer.PTChangingEffects_7, SubLayer.ModifyPT_7c, effects.get(effect), game); + } + applyCounters.apply(Layer.PTChangingEffects_7, SubLayer.Counters_7d, null, game); +// for (ContinuousEffect effect: layeredEffects) { +// effect.apply(Layer.PTChangingEffects_7, SubLayer.Counters_7d, game); +// } + for (ContinuousEffect effect: layeredEffects) { + effect.apply(Layer.PTChangingEffects_7, SubLayer.SwitchPT_e, effects.get(effect), game); } for (ContinuousEffect effect: layeredEffects) { - effect.apply(Layer.PTChangingEffects_7, SubLayer.SwitchPT_e, game); + effect.apply(Layer.PlayerEffects, SubLayer.NA, effects.get(effect), game); } for (ContinuousEffect effect: layeredEffects) { - effect.apply(Layer.PlayerEffects, SubLayer.NA, game); - } - for (ContinuousEffect effect: layeredEffects) { - effect.apply(Layer.RulesEffects, SubLayer.NA, game); + effect.apply(Layer.RulesEffects, SubLayer.NA, effects.get(effect), game); } } - protected void applyCounters(Game game) { - for (Permanent permanent: game.getBattlefield().getAllActivePermanents(CardType.CREATURE)) { - for (BoostCounter counter: permanent.getCounters().getBoostCounters()) { - permanent.addPower(counter.getPower() * counter.getCount()); - permanent.addToughness(counter.getToughness() * counter.getCount()); - } - } +// protected void applyCounters(Game game) { +// for (Permanent permanent: game.getBattlefield().getAllActivePermanents(CardType.CREATURE)) { +// for (BoostCounter counter: permanent.getCounters().getBoostCounters()) { +// permanent.addPower(counter.getPower() * counter.getCount()); +// permanent.addToughness(counter.getToughness() * counter.getCount()); +// } +// } +// } + +// public String getText() { +// StringBuilder sbText = new StringBuilder(); +// for (ActiveContinuousEffect effect: effects) { +// sbText.append(effect.getEffect().getText()).append(" "); +// } +// return sbText.toString(); +// } + + public void addEffect(ContinuousEffect effect, Ability source) { + effects.put(effect, source); } - public String getText() { - StringBuilder sbText = new StringBuilder(); - for (ContinuousEffect effect: effects) { - sbText.append(effect.getText()).append(" "); - } - return sbText.toString(); - } - - public void addEffect(ContinuousEffect effect) { - effects.add(effect); - } - - public boolean effectExists(UUID abilityId) { - for (ContinuousEffect effect: effects) { - if (effect.getSource().getId().equals(abilityId)) - return true; - } - return false; - } +// public boolean effectExists(UUID abilityId) { +// for (ContinuousEffect effect: effects) { +// if (effect.getSource().getId().equals(abilityId)) +// return true; +// } +// return false; +// } } diff --git a/Mage/src/mage/abilities/effects/Effect.java b/Mage/src/mage/abilities/effects/Effect.java index 33e010128ef..ed1700da7fb 100644 --- a/Mage/src/mage/abilities/effects/Effect.java +++ b/Mage/src/mage/abilities/effects/Effect.java @@ -38,13 +38,12 @@ import mage.game.Game; * * @author BetaSteward_at_googlemail.com */ -public interface Effect extends Serializable { +public interface Effect> extends Serializable { - public UUID getId(); - public String getText(); - public boolean apply(Game game); - public Ability getSource(); +// public UUID getId(); + public String getText(Ability source); + public boolean apply(Game game, Ability source); public Outcome getOutcome(); - public void setSource(Ability ability); - + + public T copy(); } diff --git a/Mage/src/mage/abilities/effects/EffectImpl.java b/Mage/src/mage/abilities/effects/EffectImpl.java index 9bf22c7b0c5..f2942dd269d 100644 --- a/Mage/src/mage/abilities/effects/EffectImpl.java +++ b/Mage/src/mage/abilities/effects/EffectImpl.java @@ -36,37 +36,30 @@ import mage.abilities.Ability; * * @author BetaSteward_at_googlemail.com */ -public abstract class EffectImpl implements Effect { +public abstract class EffectImpl> implements Effect { - protected UUID id; - protected String text = ""; - protected Ability source; - protected Outcome outcome; +// protected final UUID id; + protected final Outcome outcome; public EffectImpl(Outcome outcome) { - this.id = UUID.randomUUID(); +// this.id = UUID.randomUUID(); this.outcome = outcome; } - - @Override - public String getText() { - return text; + + public EffectImpl(final EffectImpl effect) { +// this.id = effect.id; + this.outcome = effect.outcome; } @Override - public Ability getSource() { - return source; + public String getText(Ability source) { + return ""; } - @Override - public void setSource(Ability ability) { - this.source = ability; - } - - @Override - public UUID getId() { - return id; - } +// @Override +// public UUID getId() { +// return id; +// } @Override public Outcome getOutcome() { diff --git a/Mage/src/mage/abilities/effects/Effects.java b/Mage/src/mage/abilities/effects/Effects.java index 265936c67a0..68fc45c6c53 100644 --- a/Mage/src/mage/abilities/effects/Effects.java +++ b/Mage/src/mage/abilities/effects/Effects.java @@ -29,6 +29,9 @@ package mage.abilities.effects; import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; import mage.Constants.Outcome; import mage.abilities.Ability; @@ -38,33 +41,26 @@ import mage.abilities.Ability; */ public class Effects extends ArrayList { - private Ability source; - - public Effects(Ability ability) { - this.source = ability; + public Effects() {} + + public Effects(final Effects effects) { + for (Effect effect: effects) { + this.add(effect.copy()); + } } - public String getText() { + public Effects copy() { + return new Effects(this); + } + + public String getText(Ability source) { StringBuilder sbText = new StringBuilder(); for (Effect effect: this) { - sbText.append(effect.getText()).append(". "); + sbText.append(effect.getText(source)).append(". "); } return sbText.toString(); } - public void setSource(Ability ability) { - this.source = ability; - for (Effect effect: this) { - effect.setSource(ability); - } - } - - @Override - public boolean add(Effect effect) { - effect.setSource(source); - return super.add(effect); - } - public boolean hasOutcome(Outcome outcome) { for (Effect effect: this) { if (effect.getOutcome() == outcome) @@ -72,4 +68,23 @@ public class Effects extends ArrayList { } return false; } + + public List getOutcomes() { + Set outcomes = new HashSet(); + for (Effect effect: this) { + outcomes.add(effect.getOutcome()); + } + return new ArrayList(outcomes); + } + + public int getOutcomeTotal() { + int total = 0; + for (Effect effect: this) { + if (effect.getOutcome().isGood()) + total++; + else + total--; + } + return total; + } } diff --git a/Mage/src/mage/abilities/effects/EntersBattlefieldEffect.java b/Mage/src/mage/abilities/effects/EntersBattlefieldEffect.java index a512d4be083..3e917bd9a5b 100644 --- a/Mage/src/mage/abilities/effects/EntersBattlefieldEffect.java +++ b/Mage/src/mage/abilities/effects/EntersBattlefieldEffect.java @@ -42,18 +42,23 @@ import mage.game.events.ZoneChangeEvent; * * @author BetaSteward_at_googlemail.com */ -public class EntersBattlefieldEffect extends ReplacementEffectImpl { +public class EntersBattlefieldEffect extends ReplacementEffectImpl { - protected Effects baseEffects = new Effects(null); + protected Effects baseEffects = new Effects(); public EntersBattlefieldEffect(Effect baseEffect) { super(Duration.WhileOnBattlefield, baseEffect.getOutcome()); this.baseEffects.add(baseEffect); } + public EntersBattlefieldEffect(EntersBattlefieldEffect effect) { + super(effect); + this.baseEffects = effect.baseEffects.copy(); + } + @Override - public boolean applies(GameEvent event, Game game) { - if (event.getType() == EventType.ZONE_CHANGE && event.getTargetId().equals(this.source.getSourceId())) { + public boolean applies(GameEvent event, Ability source, Game game) { + if (event.getType() == EventType.ZONE_CHANGE && event.getTargetId().equals(source.getSourceId())) { ZoneChangeEvent zEvent = (ZoneChangeEvent)event; if (zEvent.getToZone() == Zone.BATTLEFIELD) return true; @@ -62,32 +67,30 @@ public class EntersBattlefieldEffect extends ReplacementEffectImpl { } @Override - public boolean apply(Game game) { + public boolean apply(Game game, Ability source) { for (Effect effect: baseEffects) { if (effect instanceof ContinuousEffect) { - game.addEffect((ContinuousEffect) effect); + game.addEffect((ContinuousEffect) effect, source); } else - effect.apply(game); + effect.apply(game, source); } return true; } @Override - public boolean replaceEvent(GameEvent event, Game game) { - return apply(game); - } - - - @Override - public void setSource(Ability source) { - this.source = source; - baseEffects.setSource(source); + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + return apply(game, source); } @Override - public String getText() { - return "When {this} enters the battlefield, " + baseEffects.getText(); + public String getText(Ability source) { + return "When {this} enters the battlefield, " + baseEffects.getText(source); + } + + @Override + public EntersBattlefieldEffect copy() { + return new EntersBattlefieldEffect(this); } } diff --git a/Mage/src/mage/abilities/effects/OneShotEffect.java b/Mage/src/mage/abilities/effects/OneShotEffect.java index 7fc27872861..6640e252ea0 100644 --- a/Mage/src/mage/abilities/effects/OneShotEffect.java +++ b/Mage/src/mage/abilities/effects/OneShotEffect.java @@ -34,10 +34,14 @@ import mage.Constants.Outcome; * * @author BetaSteward_at_googlemail.com */ -public abstract class OneShotEffect extends EffectImpl { +public abstract class OneShotEffect> extends EffectImpl { public OneShotEffect(Outcome outcome) { super(outcome); } + public OneShotEffect(final OneShotEffect effect) { + super(effect); + } + } diff --git a/Mage/src/mage/abilities/effects/PreventionEffect.java b/Mage/src/mage/abilities/effects/PreventionEffect.java index 2563d5ef677..fbb3140296b 100644 --- a/Mage/src/mage/abilities/effects/PreventionEffect.java +++ b/Mage/src/mage/abilities/effects/PreventionEffect.java @@ -32,6 +32,6 @@ package mage.abilities.effects; * * @author BetaSteward_at_googlemail.com */ -public interface PreventionEffect extends ContinuousEffect { +public interface PreventionEffect> extends ContinuousEffect { } diff --git a/Mage/src/mage/abilities/effects/PreventionEffectImpl.java b/Mage/src/mage/abilities/effects/PreventionEffectImpl.java index bd79b2d0e6e..29277315286 100644 --- a/Mage/src/mage/abilities/effects/PreventionEffectImpl.java +++ b/Mage/src/mage/abilities/effects/PreventionEffectImpl.java @@ -30,6 +30,7 @@ package mage.abilities.effects; import mage.Constants.Duration; import mage.Constants.Outcome; +import mage.abilities.Ability; import mage.game.Game; import mage.game.events.GameEvent; @@ -38,15 +39,18 @@ import mage.game.events.GameEvent; * * @author BetaSteward_at_googlemail.com */ -public abstract class PreventionEffectImpl extends ReplacementEffectImpl { - +public abstract class PreventionEffectImpl> extends ReplacementEffectImpl { public PreventionEffectImpl(Duration duration) { super(duration, Outcome.PreventDamage); } + public PreventionEffectImpl(final PreventionEffectImpl effect) { + super(effect); + } + @Override - public boolean applies(GameEvent event, Game game) { + public boolean applies(GameEvent event, Ability source, Game game) { switch (event.getType()) { case DAMAGE_CREATURE: case DAMAGE_PLAYER: diff --git a/Mage/src/mage/abilities/effects/ReplacementEffect.java b/Mage/src/mage/abilities/effects/ReplacementEffect.java index 3a00ad212d5..41cffdcad12 100644 --- a/Mage/src/mage/abilities/effects/ReplacementEffect.java +++ b/Mage/src/mage/abilities/effects/ReplacementEffect.java @@ -28,6 +28,7 @@ package mage.abilities.effects; +import mage.abilities.Ability; import mage.game.Game; import mage.game.events.GameEvent; @@ -35,10 +36,10 @@ import mage.game.events.GameEvent; * * @author BetaSteward_at_googlemail.com */ -public interface ReplacementEffect extends ContinuousEffect { +public interface ReplacementEffect> extends ContinuousEffect { - public boolean replaceEvent(GameEvent event, Game game); - public boolean applies(GameEvent event, Game game); + public boolean replaceEvent(GameEvent event, Ability source, Game game); + public boolean applies(GameEvent event, Ability source, Game game); boolean isUsed(); } diff --git a/Mage/src/mage/abilities/effects/ReplacementEffectImpl.java b/Mage/src/mage/abilities/effects/ReplacementEffectImpl.java index bd652aea722..e8140c495d5 100644 --- a/Mage/src/mage/abilities/effects/ReplacementEffectImpl.java +++ b/Mage/src/mage/abilities/effects/ReplacementEffectImpl.java @@ -35,7 +35,7 @@ import mage.Constants.Outcome; * * @author BetaSteward_at_googlemail.com */ -public abstract class ReplacementEffectImpl extends ContinuousEffectImpl implements ReplacementEffect { +public abstract class ReplacementEffectImpl> extends ContinuousEffectImpl implements ReplacementEffect { protected boolean used = false; @@ -43,6 +43,11 @@ public abstract class ReplacementEffectImpl extends ContinuousEffectImpl impleme super(duration, outcome); } + public ReplacementEffectImpl(final ReplacementEffectImpl effect) { + super(effect); + this.used = effect.used; + } + @Override public boolean isUsed() { return used; diff --git a/Mage/src/mage/abilities/effects/RequirementAttackEffect.java b/Mage/src/mage/abilities/effects/RequirementAttackEffect.java index 25c3124191a..9ac98e8685e 100644 --- a/Mage/src/mage/abilities/effects/RequirementAttackEffect.java +++ b/Mage/src/mage/abilities/effects/RequirementAttackEffect.java @@ -30,6 +30,7 @@ package mage.abilities.effects; import mage.Constants.Duration; import mage.Constants.Outcome; +import mage.abilities.Ability; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; @@ -38,20 +39,24 @@ import mage.game.events.GameEvent.EventType; * * @author BetaSteward_at_googlemail.com */ -public abstract class RequirementAttackEffect extends ReplacementEffectImpl { +public abstract class RequirementAttackEffect> extends ReplacementEffectImpl { public RequirementAttackEffect(Duration duration) { super(duration, Outcome.Detriment); } + public RequirementAttackEffect(final RequirementAttackEffect effect) { + super(effect); + } + @Override - public boolean replaceEvent(GameEvent event, Game game) { - apply(game); + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + apply(game, source); return false; } @Override - public boolean applies(GameEvent event, Game game) { + public boolean applies(GameEvent event, Ability source, Game game) { if (event.getType().equals(EventType.DECLARE_ATTACKERS_STEP_PRE)) return true; return false; diff --git a/Mage/src/mage/abilities/effects/RequirementBlockEffect.java b/Mage/src/mage/abilities/effects/RequirementBlockEffect.java index d6c018d0a92..9db17820509 100644 --- a/Mage/src/mage/abilities/effects/RequirementBlockEffect.java +++ b/Mage/src/mage/abilities/effects/RequirementBlockEffect.java @@ -30,6 +30,7 @@ package mage.abilities.effects; import mage.Constants.Duration; import mage.Constants.Outcome; +import mage.abilities.Ability; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; @@ -38,20 +39,24 @@ import mage.game.events.GameEvent.EventType; * * @author BetaSteward_at_googlemail.com */ -public abstract class RequirementBlockEffect extends ReplacementEffectImpl { +public abstract class RequirementBlockEffect> extends ReplacementEffectImpl { public RequirementBlockEffect(Duration duration) { super(duration, Outcome.Detriment); } + public RequirementBlockEffect(final RequirementBlockEffect effect) { + super(effect); + } + @Override - public boolean replaceEvent(GameEvent event, Game game) { - apply(game); + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + apply(game, source); return false; } @Override - public boolean applies(GameEvent event, Game game) { + public boolean applies(GameEvent event, Ability source, Game game) { if (event.getType().equals(EventType.DECLARE_BLOCKERS_STEP_PRE)) return true; return false; diff --git a/Mage/src/mage/abilities/effects/SearchEffect.java b/Mage/src/mage/abilities/effects/SearchEffect.java new file mode 100644 index 00000000000..7b8c9d30d79 --- /dev/null +++ b/Mage/src/mage/abilities/effects/SearchEffect.java @@ -0,0 +1,55 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.abilities.effects; + +import mage.Constants.Outcome; +import mage.target.TargetCard; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public abstract class SearchEffect> extends OneShotEffect { + + protected TargetCard target; + + public SearchEffect(TargetCard target, Outcome outcome) { + super(outcome); + this.target = target; + } + + public SearchEffect(SearchEffect effect) { + super(effect); + this.target = effect.target.copy(); + } + + public TargetCard getTarget() { + return target; + } +} diff --git a/Mage/src/mage/abilities/effects/WhileControlsContinuousEffect.java b/Mage/src/mage/abilities/effects/WhileControlsContinuousEffect.java index 6e59253862e..07a24ffe5ef 100644 --- a/Mage/src/mage/abilities/effects/WhileControlsContinuousEffect.java +++ b/Mage/src/mage/abilities/effects/WhileControlsContinuousEffect.java @@ -32,6 +32,7 @@ import mage.Constants.Duration; import mage.Constants.Layer; import mage.Constants.Outcome; import mage.Constants.SubLayer; +import mage.abilities.Ability; import mage.filter.FilterPermanent; import mage.game.Game; @@ -39,7 +40,7 @@ import mage.game.Game; * * @author BetaSteward_at_googlemail.com */ -public abstract class WhileControlsContinuousEffect extends ContinuousEffectImpl { +public abstract class WhileControlsContinuousEffect> extends ContinuousEffectImpl { protected FilterPermanent filter; @@ -49,15 +50,20 @@ public abstract class WhileControlsContinuousEffect extends ContinuousEffectImpl this.layer = layer; this.sublayer = sublayer; } + + public WhileControlsContinuousEffect(WhileControlsContinuousEffect effect) { + super(effect); + this.filter = effect.filter.copy(); + } @Override - public boolean apply(Game game) { - if (game.getBattlefield().countAll(filter, this.source.getControllerId()) > 0) { - return applyEffect(game); + public boolean apply(Game game, Ability source) { + if (game.getBattlefield().countAll(filter, source.getControllerId()) > 0) { + return applyEffect(game, source); } return false; } - protected abstract boolean applyEffect(Game game); + protected abstract boolean applyEffect(Game game, Ability source); } diff --git a/Mage/src/mage/abilities/effects/common/AddCountersSourceEffect.java b/Mage/src/mage/abilities/effects/common/AddCountersSourceEffect.java index 81d156d2b58..cade08d1507 100644 --- a/Mage/src/mage/abilities/effects/common/AddCountersSourceEffect.java +++ b/Mage/src/mage/abilities/effects/common/AddCountersSourceEffect.java @@ -29,6 +29,7 @@ package mage.abilities.effects.common; import mage.Constants.Outcome; +import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.counters.Counter; import mage.game.Game; @@ -38,7 +39,7 @@ import mage.game.permanent.Permanent; * * @author BetaSteward_at_googlemail.com */ -public class AddCountersSourceEffect extends OneShotEffect { +public class AddCountersSourceEffect extends OneShotEffect { private int amount; private String name; @@ -49,9 +50,15 @@ public class AddCountersSourceEffect extends OneShotEffect { this.amount = amount; } + public AddCountersSourceEffect(final AddCountersSourceEffect effect) { + super(effect); + this.amount = effect.amount; + this.name = effect.name; + } + @Override - public boolean apply(Game game) { - Permanent permanent = game.getPermanent(this.source.getSourceId()); + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getSourceId()); if (permanent != null) { Counter counter = new Counter(name); counter.add(amount); @@ -61,7 +68,7 @@ public class AddCountersSourceEffect extends OneShotEffect { } @Override - public String getText() { + public String getText(Ability source) { if (amount > 1) { StringBuilder sb = new StringBuilder(); sb.append("put ").append(Integer.toString(amount)).append(" ").append(name).append("counters on {this}"); @@ -71,5 +78,10 @@ public class AddCountersSourceEffect extends OneShotEffect { return "put a " + name + " counter on {this}"; } + @Override + public AddCountersSourceEffect copy() { + return new AddCountersSourceEffect(this); + } + } diff --git a/Mage/src/mage/abilities/effects/common/AddManaOfAnyColorEffect.java b/Mage/src/mage/abilities/effects/common/AddManaOfAnyColorEffect.java index 6257388e5be..6ac19b67d7a 100644 --- a/Mage/src/mage/abilities/effects/common/AddManaOfAnyColorEffect.java +++ b/Mage/src/mage/abilities/effects/common/AddManaOfAnyColorEffect.java @@ -30,6 +30,7 @@ package mage.abilities.effects.common; import mage.Constants.Outcome; import mage.Mana; +import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.choices.ChoiceColor; import mage.game.Game; @@ -39,7 +40,7 @@ import mage.players.Player; * * @author BetaSteward_at_googlemail.com */ -public class AddManaOfAnyColorEffect extends OneShotEffect { +public class AddManaOfAnyColorEffect extends OneShotEffect { int amount; @@ -48,10 +49,20 @@ public class AddManaOfAnyColorEffect extends OneShotEffect { this.amount = amount; } + public AddManaOfAnyColorEffect(final AddManaOfAnyColorEffect effect) { + super(effect); + this.amount = effect.amount; + } + @Override - public boolean apply(Game game) { - ChoiceColor choice = (ChoiceColor) this.source.getChoices().get(0); - Player player = game.getPlayer(this.source.getControllerId()); + public AddManaOfAnyColorEffect copy() { + return new AddManaOfAnyColorEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + ChoiceColor choice = (ChoiceColor) source.getChoices().get(0); + Player player = game.getPlayer(source.getControllerId()); if (choice.getColor().isBlack()) { player.getManaPool().changeMana(Mana.BlackMana); return true; @@ -76,7 +87,7 @@ public class AddManaOfAnyColorEffect extends OneShotEffect { } @Override - public String getText() { + public String getText(Ability source) { if (amount > 1) return "add " + Integer.toString(amount) + " mana of any color to your mana pool"; else diff --git a/Mage/src/mage/abilities/effects/common/AddPlusOneCountersControlledEffect.java b/Mage/src/mage/abilities/effects/common/AddPlusOneCountersControlledEffect.java index 2e1a9848130..a0bdb460a08 100644 --- a/Mage/src/mage/abilities/effects/common/AddPlusOneCountersControlledEffect.java +++ b/Mage/src/mage/abilities/effects/common/AddPlusOneCountersControlledEffect.java @@ -46,7 +46,7 @@ import mage.game.permanent.Permanent; * * @author BetaSteward_at_googlemail.com */ -public class AddPlusOneCountersControlledEffect extends OneShotEffect { +public class AddPlusOneCountersControlledEffect extends OneShotEffect { private int amount; private FilterPermanent filter; @@ -61,15 +61,26 @@ public class AddPlusOneCountersControlledEffect extends OneShotEffect { this.filter = filter; } + public AddPlusOneCountersControlledEffect(final AddPlusOneCountersControlledEffect effect) { + super(effect); + this.amount = effect.amount; + this.filter = effect.filter.copy(); + } + @Override - public boolean apply(Game game) { - for (Permanent perm: game.getBattlefield().getAllActivePermanents(filter, this.source.getControllerId())) { + public AddPlusOneCountersControlledEffect copy() { + return new AddPlusOneCountersControlledEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + for (Permanent perm: game.getBattlefield().getAllActivePermanents(filter, source.getControllerId())) { perm.getCounters().addCounter(new PlusOneCounter(amount)); } return true; } @Override - public String getText() { + public String getText(Ability source) { StringBuilder sb = new StringBuilder(); sb.append("Put ").append(amount).append(" +1/+1 counter"); if (amount > 1) diff --git a/Mage/src/mage/abilities/effects/common/AddPlusOneCountersSourceEffect.java b/Mage/src/mage/abilities/effects/common/AddPlusOneCountersSourceEffect.java index 204df50f79d..40604dcc966 100644 --- a/Mage/src/mage/abilities/effects/common/AddPlusOneCountersSourceEffect.java +++ b/Mage/src/mage/abilities/effects/common/AddPlusOneCountersSourceEffect.java @@ -29,6 +29,7 @@ package mage.abilities.effects.common; import mage.Constants.Outcome; +import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.counters.PlusOneCounter; import mage.game.Game; @@ -38,7 +39,7 @@ import mage.game.permanent.Permanent; * * @author BetaSteward_at_googlemail.com */ -public class AddPlusOneCountersSourceEffect extends OneShotEffect { +public class AddPlusOneCountersSourceEffect extends OneShotEffect { private int amount; @@ -47,9 +48,19 @@ public class AddPlusOneCountersSourceEffect extends OneShotEffect { this.amount = amount; } + public AddPlusOneCountersSourceEffect(final AddPlusOneCountersSourceEffect effect) { + super(effect); + this.amount = effect.amount; + } + @Override - public boolean apply(Game game) { - Permanent permanent = game.getPermanent(this.source.getSourceId()); + public AddPlusOneCountersSourceEffect copy() { + return new AddPlusOneCountersSourceEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getSourceId()); if (permanent != null) { permanent.getCounters().addCounter(new PlusOneCounter(amount)); } @@ -57,7 +68,7 @@ public class AddPlusOneCountersSourceEffect extends OneShotEffect { } @Override - public String getText() { + public String getText(Ability source) { if (amount > 1) return "put " + Integer.toString(amount) + " +1/+1 counters on {this}"; else diff --git a/Mage/src/mage/abilities/effects/common/AttachEffect.java b/Mage/src/mage/abilities/effects/common/AttachEffect.java index a7175a1d35f..62b57321ddc 100644 --- a/Mage/src/mage/abilities/effects/common/AttachEffect.java +++ b/Mage/src/mage/abilities/effects/common/AttachEffect.java @@ -29,6 +29,7 @@ package mage.abilities.effects.common; import mage.Constants.Outcome; +import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.game.Game; import mage.game.permanent.Permanent; @@ -37,14 +38,23 @@ import mage.game.permanent.Permanent; * * @author BetaSteward_at_googlemail.com */ -public class AttachEffect extends OneShotEffect { +public class AttachEffect extends OneShotEffect { public AttachEffect(Outcome outcome) { super(outcome); } + public AttachEffect(final AttachEffect effect) { + super(effect); + } + @Override - public boolean apply(Game game) { + public AttachEffect copy() { + return new AttachEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { Permanent permanent = game.getPermanent(source.getFirstTarget()); if (permanent != null) { return permanent.addAttachment(source.getSourceId(), game); diff --git a/Mage/src/mage/abilities/effects/common/BecomesCreatureSourceEOTEffect.java b/Mage/src/mage/abilities/effects/common/BecomesCreatureSourceEOTEffect.java index 8b2ac7f6871..6c1fd851b8f 100644 --- a/Mage/src/mage/abilities/effects/common/BecomesCreatureSourceEOTEffect.java +++ b/Mage/src/mage/abilities/effects/common/BecomesCreatureSourceEOTEffect.java @@ -43,7 +43,7 @@ import mage.game.permanent.token.Token; * * @author BetaSteward_at_googlemail.com */ -public class BecomesCreatureSourceEOTEffect extends ContinuousEffectImpl { +public class BecomesCreatureSourceEOTEffect extends ContinuousEffectImpl { protected Token token; protected String type; @@ -54,51 +54,65 @@ public class BecomesCreatureSourceEOTEffect extends ContinuousEffectImpl { this.type = type; } - @Override - public boolean apply(Layer layer, SubLayer sublayer, Game game) { - Permanent permanent = game.getPermanent(source.getSourceId()); - switch (layer) { - case TypeChangingEffects_4: - if (sublayer == SubLayer.NA) { - if (token.getCardType().size() > 0) - permanent.getCardType().addAll(token.getCardType()); - if (token.getSubtype().size() > 0) - permanent.getSubtype().addAll(token.getSubtype()); - } - break; - case ColorChangingEffects_5: - if (sublayer == SubLayer.NA) { - if (token.getColor().hasColor()) - permanent.getColor().setColor(token.getColor()); - } - break; - case AbilityAddingRemovingEffects_6: - if (sublayer == SubLayer.NA) { - if (token.getAbilities().size() > 0) { - for (Ability ability: token.getAbilities()) { - permanent.addAbility(ability); - } - } - } - break; - case PTChangingEffects_7: - if (sublayer == SubLayer.SetPT_7b) { - if (token.getPower() != MageInt.EmptyMageInt) - permanent.getPower().setValue(token.getPower().getValue()); - if (token.getToughness() != MageInt.EmptyMageInt) - permanent.getToughness().setValue(token.getToughness().getValue()); - } - } - return true; + public BecomesCreatureSourceEOTEffect(final BecomesCreatureSourceEOTEffect effect) { + super(effect); + this.token = effect.token.copy(); + this.type = effect.type; } @Override - public boolean apply(Game game) { + public BecomesCreatureSourceEOTEffect copy() { + return new BecomesCreatureSourceEOTEffect(this); + } + + @Override + public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + Permanent permanent = game.getPermanent(source.getSourceId()); + if (permanent != null) { + switch (layer) { + case TypeChangingEffects_4: + if (sublayer == SubLayer.NA) { + if (token.getCardType().size() > 0) + permanent.getCardType().addAll(token.getCardType()); + if (token.getSubtype().size() > 0) + permanent.getSubtype().addAll(token.getSubtype()); + } + break; + case ColorChangingEffects_5: + if (sublayer == SubLayer.NA) { + if (token.getColor().hasColor()) + permanent.getColor().setColor(token.getColor()); + } + break; + case AbilityAddingRemovingEffects_6: + if (sublayer == SubLayer.NA) { + if (token.getAbilities().size() > 0) { + for (Ability ability: token.getAbilities()) { + permanent.addAbility(ability); + } + } + } + break; + case PTChangingEffects_7: + if (sublayer == SubLayer.SetPT_7b) { + if (token.getPower() != MageInt.EmptyMageInt) + permanent.getPower().setValue(token.getPower().getValue()); + if (token.getToughness() != MageInt.EmptyMageInt) + permanent.getToughness().setValue(token.getToughness().getValue()); + } + } + return true; + } return false; } @Override - public String getText() { + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public String getText(Ability source) { return "Until end of turn {this} becomes a " + token.getDescription() + " that's still a " + this.type; } diff --git a/Mage/src/mage/abilities/effects/common/BoostControlledEffect.java b/Mage/src/mage/abilities/effects/common/BoostControlledEffect.java index be2e64e207a..1f0eac64f2b 100644 --- a/Mage/src/mage/abilities/effects/common/BoostControlledEffect.java +++ b/Mage/src/mage/abilities/effects/common/BoostControlledEffect.java @@ -28,11 +28,11 @@ package mage.abilities.effects.common; -import mage.Constants.CardType; import mage.Constants.Duration; import mage.Constants.Layer; import mage.Constants.Outcome; import mage.Constants.SubLayer; +import mage.abilities.Ability; import mage.abilities.effects.ContinuousEffectImpl; import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; @@ -42,7 +42,7 @@ import mage.game.permanent.Permanent; * * @author BetaSteward_at_googlemail.com */ -public class BoostControlledEffect extends ContinuousEffectImpl { +public class BoostControlledEffect extends ContinuousEffectImpl { private int power; private int toughness; @@ -59,9 +59,21 @@ public class BoostControlledEffect extends ContinuousEffectImpl { this.filter = filter; } + public BoostControlledEffect(final BoostControlledEffect effect) { + super(effect); + this.power = effect.power; + this.toughness = effect.toughness; + this.filter = effect.filter.copy(); + } + @Override - public boolean apply(Game game) { - for (Permanent perm: game.getBattlefield().getAllActivePermanents(filter, this.source.getControllerId())) { + public BoostControlledEffect copy() { + return new BoostControlledEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + for (Permanent perm: game.getBattlefield().getAllActivePermanents(filter, source.getControllerId())) { perm.addPower(power); perm.addToughness(toughness); } @@ -69,7 +81,7 @@ public class BoostControlledEffect extends ContinuousEffectImpl { } @Override - public String getText() { + public String getText(Ability source) { StringBuilder sb = new StringBuilder(); sb.append(filter.getMessage()); sb.append(" you control get ").append(String.format("%1$+d/%2$+d", power, toughness)); diff --git a/Mage/src/mage/abilities/effects/common/BoostEquippedEffect.java b/Mage/src/mage/abilities/effects/common/BoostEquippedEffect.java index ccb728e0053..dcd4fee8240 100644 --- a/Mage/src/mage/abilities/effects/common/BoostEquippedEffect.java +++ b/Mage/src/mage/abilities/effects/common/BoostEquippedEffect.java @@ -28,11 +28,11 @@ package mage.abilities.effects.common; -import mage.Constants.CardType; import mage.Constants.Duration; import mage.Constants.Layer; import mage.Constants.Outcome; import mage.Constants.SubLayer; +import mage.abilities.Ability; import mage.abilities.effects.ContinuousEffectImpl; import mage.game.Game; import mage.game.permanent.Permanent; @@ -41,20 +41,35 @@ import mage.game.permanent.Permanent; * * @author BetaSteward_at_googlemail.com */ -public class BoostEquippedEffect extends ContinuousEffectImpl { +public class BoostEquippedEffect extends ContinuousEffectImpl { private int power; private int toughness; public BoostEquippedEffect(int power, int toughness) { - super(Duration.WhileOnBattlefield, Layer.PTChangingEffects_7, SubLayer.ModifyPT_7c, Outcome.BoostCreature); + this(power, toughness, Duration.WhileOnBattlefield); + } + + public BoostEquippedEffect(int power, int toughness, Duration duration) { + super(duration, Layer.PTChangingEffects_7, SubLayer.ModifyPT_7c, Outcome.BoostCreature); this.power = power; this.toughness = toughness; } + public BoostEquippedEffect(final BoostEquippedEffect effect) { + super(effect); + this.power = effect.power; + this.toughness = effect.toughness; + } + @Override - public boolean apply(Game game) { - Permanent equipment = game.getPermanent(this.source.getSourceId()); + public BoostEquippedEffect copy() { + return new BoostEquippedEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent equipment = game.getPermanent(source.getSourceId()); if (equipment.getAttachedTo() != null) { Permanent creature = game.getPermanent(equipment.getAttachedTo()); if (creature != null) { @@ -66,9 +81,12 @@ public class BoostEquippedEffect extends ContinuousEffectImpl { } @Override - public String getText() { - return "Equipped creatures gets " + String.format("%1$+d/%2$+d", power, toughness); + public String getText(Ability source) { + StringBuilder sb = new StringBuilder(); + sb.append("Equipped creatures gets ").append(String.format("%1$+d/%2$+d", power, toughness)); + if (duration != Duration.WhileOnBattlefield) + sb.append(" ").append(duration.toString()); + return sb.toString(); } - } diff --git a/Mage/src/mage/abilities/effects/common/BoostPowerSourceVariableEffect.java b/Mage/src/mage/abilities/effects/common/BoostPowerSourceVariableEffect.java index 85992c0b524..8024a27d7f2 100644 --- a/Mage/src/mage/abilities/effects/common/BoostPowerSourceVariableEffect.java +++ b/Mage/src/mage/abilities/effects/common/BoostPowerSourceVariableEffect.java @@ -32,6 +32,7 @@ import mage.Constants.Duration; import mage.Constants.Layer; import mage.Constants.Outcome; import mage.Constants.SubLayer; +import mage.abilities.Ability; import mage.abilities.effects.ContinuousEffectImpl; import mage.game.Game; import mage.game.permanent.Permanent; @@ -40,16 +41,25 @@ import mage.game.permanent.Permanent; * * @author BetaSteward_at_googlemail.com */ -public class BoostPowerSourceVariableEffect extends ContinuousEffectImpl { +public class BoostPowerSourceVariableEffect extends ContinuousEffectImpl { public BoostPowerSourceVariableEffect(Duration duration) { super(duration, Layer.PTChangingEffects_7, SubLayer.ModifyPT_7c, Outcome.BoostCreature); } + public BoostPowerSourceVariableEffect(final BoostPowerSourceVariableEffect effect) { + super(effect); + } + @Override - public boolean apply(Game game) { - int amount = this.source.getManaCosts().getVariableCosts().get(0).getValue(); - Permanent target = (Permanent) game.getPermanent(this.source.getSourceId()); + public BoostPowerSourceVariableEffect copy() { + return new BoostPowerSourceVariableEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + int amount = source.getManaCosts().getVariableCosts().get(0).getValue(); + Permanent target = (Permanent) game.getPermanent(source.getSourceId()); if (target != null) { target.addPower(amount); return true; @@ -58,7 +68,7 @@ public class BoostPowerSourceVariableEffect extends ContinuousEffectImpl { } @Override - public String getText() { + public String getText(Ability source) { return "{this} gets " + String.format("+X/+0") + " " + duration.toString(); } diff --git a/Mage/src/mage/abilities/effects/common/BoostSourceEffect.java b/Mage/src/mage/abilities/effects/common/BoostSourceEffect.java index ad3bf920680..eaf2ccf0a99 100644 --- a/Mage/src/mage/abilities/effects/common/BoostSourceEffect.java +++ b/Mage/src/mage/abilities/effects/common/BoostSourceEffect.java @@ -32,6 +32,7 @@ import mage.Constants.Duration; import mage.Constants.Layer; import mage.Constants.Outcome; import mage.Constants.SubLayer; +import mage.abilities.Ability; import mage.abilities.effects.ContinuousEffectImpl; import mage.game.Game; import mage.game.permanent.Permanent; @@ -40,7 +41,7 @@ import mage.game.permanent.Permanent; * * @author BetaSteward_at_googlemail.com */ -public class BoostSourceEffect extends ContinuousEffectImpl { +public class BoostSourceEffect extends ContinuousEffectImpl { private int power; private int toughness; @@ -51,9 +52,20 @@ public class BoostSourceEffect extends ContinuousEffectImpl { this.toughness = toughness; } + public BoostSourceEffect(final BoostSourceEffect effect) { + super(effect); + this.power = effect.power; + this.toughness = effect.toughness; + } + @Override - public boolean apply(Game game) { - Permanent target = (Permanent) game.getPermanent(this.source.getSourceId()); + public BoostSourceEffect copy() { + return new BoostSourceEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent target = (Permanent) game.getPermanent(source.getSourceId()); if (target != null) { target.addPower(power); target.addToughness(toughness); @@ -63,7 +75,7 @@ public class BoostSourceEffect extends ContinuousEffectImpl { } @Override - public String getText() { + public String getText(Ability source) { return "{this} gets " + String.format("%1$+d/%2$+d", power, toughness) + " " + duration.toString(); } diff --git a/Mage/src/mage/abilities/effects/common/BoostSourceWhileControlsEffect.java b/Mage/src/mage/abilities/effects/common/BoostSourceWhileControlsEffect.java index a4f2ee87c99..845be8d499b 100644 --- a/Mage/src/mage/abilities/effects/common/BoostSourceWhileControlsEffect.java +++ b/Mage/src/mage/abilities/effects/common/BoostSourceWhileControlsEffect.java @@ -33,6 +33,7 @@ import mage.Constants.Duration; import mage.Constants.Layer; import mage.Constants.Outcome; import mage.Constants.SubLayer; +import mage.abilities.Ability; import mage.filter.FilterPermanent; import mage.game.Game; import mage.game.permanent.Permanent; @@ -41,7 +42,7 @@ import mage.game.permanent.Permanent; * * @author BetaSteward_at_googlemail.com */ -public class BoostSourceWhileControlsEffect extends WhileControlsContinuousEffect { +public class BoostSourceWhileControlsEffect extends WhileControlsContinuousEffect { private int power; private int toughness; @@ -52,9 +53,20 @@ public class BoostSourceWhileControlsEffect extends WhileControlsContinuousEffec this.toughness = toughness; } + public BoostSourceWhileControlsEffect(final BoostSourceWhileControlsEffect effect) { + super(effect); + this.power = effect.power; + this.toughness = effect.toughness; + } + @Override - protected boolean applyEffect(Game game) { - Permanent permanent = game.getPermanent(this.source.getSourceId()); + public BoostSourceWhileControlsEffect copy() { + return new BoostSourceWhileControlsEffect(this); + } + + @Override + public boolean applyEffect(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getSourceId()); if (permanent != null) { permanent.addPower(power); permanent.addToughness(toughness); @@ -63,7 +75,7 @@ public class BoostSourceWhileControlsEffect extends WhileControlsContinuousEffec } @Override - public String getText() { + public String getText(Ability source) { return "{this} gets " + String.format("%1$+d/%2$+d", power, toughness) + " as long as you control a " + filter.getName(); } } diff --git a/Mage/src/mage/abilities/effects/common/BoostTargetEffect.java b/Mage/src/mage/abilities/effects/common/BoostTargetEffect.java index 6826122d8be..b6ae86815c2 100644 --- a/Mage/src/mage/abilities/effects/common/BoostTargetEffect.java +++ b/Mage/src/mage/abilities/effects/common/BoostTargetEffect.java @@ -32,6 +32,7 @@ import mage.Constants.Duration; import mage.Constants.Layer; import mage.Constants.Outcome; import mage.Constants.SubLayer; +import mage.abilities.Ability; import mage.abilities.effects.ContinuousEffectImpl; import mage.game.Game; import mage.game.permanent.Permanent; @@ -40,7 +41,7 @@ import mage.game.permanent.Permanent; * * @author BetaSteward_at_googlemail.com */ -public class BoostTargetEffect extends ContinuousEffectImpl { +public class BoostTargetEffect extends ContinuousEffectImpl { private int power; private int toughness; @@ -51,9 +52,20 @@ public class BoostTargetEffect extends ContinuousEffectImpl { this.toughness = toughness; } + public BoostTargetEffect(final BoostTargetEffect effect) { + super(effect); + this.power = effect.power; + this.toughness = effect.toughness; + } + @Override - public boolean apply(Game game) { - Permanent target = (Permanent) game.getPermanent(this.source.getFirstTarget()); + public BoostTargetEffect copy() { + return new BoostTargetEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent target = (Permanent) game.getPermanent(source.getFirstTarget()); if (target != null) { target.addPower(power); target.addToughness(toughness); @@ -63,9 +75,9 @@ public class BoostTargetEffect extends ContinuousEffectImpl { } @Override - public String getText() { + public String getText(Ability source) { StringBuilder sb = new StringBuilder(); - sb.append("target ").append(this.source.getTargets().get(0).getTargetName()).append(" gets "); + sb.append("target ").append(source.getTargets().get(0).getTargetName()).append(" gets "); sb.append(String.format("%1$+d/%2$+d", power, toughness)).append(" ").append(duration.toString()); return sb.toString(); } diff --git a/Mage/src/mage/abilities/effects/common/CantCounterControlledEffect.java b/Mage/src/mage/abilities/effects/common/CantCounterControlledEffect.java index 10955ea8bc3..7ff84035c4b 100644 --- a/Mage/src/mage/abilities/effects/common/CantCounterControlledEffect.java +++ b/Mage/src/mage/abilities/effects/common/CantCounterControlledEffect.java @@ -30,6 +30,7 @@ package mage.abilities.effects.common; import mage.Constants.Duration; import mage.Constants.Outcome; +import mage.abilities.Ability; import mage.abilities.effects.ReplacementEffectImpl; import mage.filter.FilterSpell; import mage.game.Game; @@ -42,7 +43,7 @@ import mage.game.stack.StackObject; * * @author BetaSteward_at_googlemail.com */ -public class CantCounterControlledEffect extends ReplacementEffectImpl { +public class CantCounterControlledEffect extends ReplacementEffectImpl { private FilterSpell filter; @@ -51,21 +52,31 @@ public class CantCounterControlledEffect extends ReplacementEffectImpl { this.filter = filter; } + public CantCounterControlledEffect(final CantCounterControlledEffect effect) { + super(effect); + this.filter = effect.filter.copy(); + } + @Override - public boolean apply(Game game) { + public CantCounterControlledEffect copy() { + return new CantCounterControlledEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { return true; } @Override - public boolean replaceEvent(GameEvent event, Game game) { + public boolean replaceEvent(GameEvent event, Ability source, Game game) { return true; } @Override - public boolean applies(GameEvent event, Game game) { + public boolean applies(GameEvent event, Ability source, Game game) { if (event.getType() == EventType.COUNTER) { filter.getControllerId().clear(); - filter.getControllerId().add(this.source.getControllerId()); + filter.getControllerId().add(source.getControllerId()); StackObject stackObject = game.getStack().getStackObject(event.getTargetId()); if (stackObject instanceof Spell) { if (filter.match((Spell) stackObject)) @@ -76,7 +87,7 @@ public class CantCounterControlledEffect extends ReplacementEffectImpl { } @Override - public String getText() { + public String getText(Ability source) { return filter.getMessage() + " can't be countered"; } diff --git a/Mage/src/mage/abilities/effects/common/CantCounterSourceEffect.java b/Mage/src/mage/abilities/effects/common/CantCounterSourceEffect.java index 41f6fdb99a8..ef8a3b0c284 100644 --- a/Mage/src/mage/abilities/effects/common/CantCounterSourceEffect.java +++ b/Mage/src/mage/abilities/effects/common/CantCounterSourceEffect.java @@ -30,6 +30,7 @@ package mage.abilities.effects.common; import mage.Constants.Duration; import mage.Constants.Outcome; +import mage.abilities.Ability; import mage.abilities.effects.ReplacementEffectImpl; import mage.game.Game; import mage.game.events.GameEvent; @@ -39,31 +40,40 @@ import mage.game.events.GameEvent.EventType; * * @author BetaSteward_at_googlemail.com */ -public class CantCounterSourceEffect extends ReplacementEffectImpl { +public class CantCounterSourceEffect extends ReplacementEffectImpl { public CantCounterSourceEffect() { super(Duration.WhileOnStack, Outcome.Benefit); } + public CantCounterSourceEffect(final CantCounterSourceEffect effect) { + super(effect); + } + @Override - public boolean apply(Game game) { + public CantCounterSourceEffect copy() { + return new CantCounterSourceEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { return true; } @Override - public boolean replaceEvent(GameEvent event, Game game) { + public boolean replaceEvent(GameEvent event, Ability source, Game game) { return true; } @Override - public boolean applies(GameEvent event, Game game) { + public boolean applies(GameEvent event, Ability source, Game game) { if (event.getType() == EventType.COUNTER && event.getTargetId().equals(source.getSourceId())) return true; return false; } @Override - public String getText() { + public String getText(Ability source) { return "{this} can't be countered"; } diff --git a/Mage/src/mage/abilities/effects/common/CounterTargetEffect.java b/Mage/src/mage/abilities/effects/common/CounterTargetEffect.java index b45fbd6b0ce..62ebd5de5ed 100644 --- a/Mage/src/mage/abilities/effects/common/CounterTargetEffect.java +++ b/Mage/src/mage/abilities/effects/common/CounterTargetEffect.java @@ -29,6 +29,7 @@ package mage.abilities.effects.common; import mage.Constants.Outcome; +import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.game.Game; @@ -36,20 +37,29 @@ import mage.game.Game; * * @author BetaSteward_at_googlemail.com */ -public class CounterTargetEffect extends OneShotEffect { +public class CounterTargetEffect extends OneShotEffect { public CounterTargetEffect() { super(Outcome.Detriment); } - @Override - public boolean apply(Game game) { - return game.getStack().counter(this.source.getFirstTarget(), source.getSourceId(), game); + public CounterTargetEffect(final CounterTargetEffect effect) { + super(effect); } @Override - public String getText() { - return "Counter target " + this.source.getTargets().get(0).getTargetName(); + public CounterTargetEffect copy() { + return new CounterTargetEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return game.getStack().counter(source.getFirstTarget(), source.getSourceId(), game); + } + + @Override + public String getText(Ability source) { + return "Counter target " + source.getTargets().get(0).getTargetName(); } } diff --git a/Mage/src/mage/abilities/effects/common/CreateDelayedTriggeredAbilityEffect.java b/Mage/src/mage/abilities/effects/common/CreateDelayedTriggeredAbilityEffect.java index d2de9af8672..e8f65229ce4 100644 --- a/Mage/src/mage/abilities/effects/common/CreateDelayedTriggeredAbilityEffect.java +++ b/Mage/src/mage/abilities/effects/common/CreateDelayedTriggeredAbilityEffect.java @@ -28,6 +28,7 @@ package mage.abilities.effects.common; +import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.game.Game; @@ -36,7 +37,7 @@ import mage.game.Game; * * @author BetaSteward_at_googlemail.com */ -public class CreateDelayedTriggeredAbilityEffect extends OneShotEffect { +public class CreateDelayedTriggeredAbilityEffect extends OneShotEffect { protected DelayedTriggeredAbility ability; @@ -45,18 +46,28 @@ public class CreateDelayedTriggeredAbilityEffect extends OneShotEffect { this.ability = ability; } + public CreateDelayedTriggeredAbilityEffect(final CreateDelayedTriggeredAbilityEffect effect) { + super(effect); + this.ability = effect.ability.copy(); + } + @Override - public boolean apply(Game game) { + public CreateDelayedTriggeredAbilityEffect copy() { + return new CreateDelayedTriggeredAbilityEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { DelayedTriggeredAbility delayedAbility = (DelayedTriggeredAbility) ability.copy(); - delayedAbility.setSourceId(this.source.getSourceId()); - delayedAbility.setControllerId(this.source.getControllerId()); - delayedAbility.getTargets().addAll(this.source.getTargets()); + delayedAbility.setSourceId(source.getSourceId()); + delayedAbility.setControllerId(source.getControllerId()); + delayedAbility.getTargets().addAll(source.getTargets()); game.getState().addDelayedTriggeredAbility(delayedAbility); return true; } @Override - public String getText() { + public String getText(Ability source) { return ability.getRule(); } diff --git a/Mage/src/mage/abilities/effects/common/CreateSpecialActionEffect.java b/Mage/src/mage/abilities/effects/common/CreateSpecialActionEffect.java index bc3f8072c46..715675a5101 100644 --- a/Mage/src/mage/abilities/effects/common/CreateSpecialActionEffect.java +++ b/Mage/src/mage/abilities/effects/common/CreateSpecialActionEffect.java @@ -28,6 +28,7 @@ package mage.abilities.effects.common; +import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.SpecialAction; import mage.abilities.effects.OneShotEffect; @@ -37,7 +38,7 @@ import mage.game.Game; * * @author BetaSteward_at_googlemail.com */ -public class CreateSpecialActionEffect extends OneShotEffect { +public class CreateSpecialActionEffect extends OneShotEffect { protected SpecialAction action; @@ -46,18 +47,28 @@ public class CreateSpecialActionEffect extends OneShotEffect { this.action = action; } + public CreateSpecialActionEffect(final CreateSpecialActionEffect effect) { + super(effect); + this.action = (SpecialAction) effect.action.copy(); + } + @Override - public boolean apply(Game game) { + public CreateSpecialActionEffect copy() { + return new CreateSpecialActionEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { SpecialAction newAction = (SpecialAction) action.copy(); - newAction.setSourceId(this.source.getSourceId()); - newAction.setControllerId(this.source.getControllerId()); - newAction.getTargets().addAll(this.source.getTargets()); + newAction.setSourceId(source.getSourceId()); + newAction.setControllerId(source.getControllerId()); + newAction.getTargets().addAll(source.getTargets()); game.getState().getSpecialActions().add(newAction); return true; } @Override - public String getText() { + public String getText(Ability source) { return action.getRule(); } diff --git a/Mage/src/mage/abilities/effects/common/CreateTokenEffect.java b/Mage/src/mage/abilities/effects/common/CreateTokenEffect.java index 227c0c504cd..7f2ae89e456 100644 --- a/Mage/src/mage/abilities/effects/common/CreateTokenEffect.java +++ b/Mage/src/mage/abilities/effects/common/CreateTokenEffect.java @@ -29,6 +29,7 @@ package mage.abilities.effects.common; import mage.Constants.Outcome; +import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.game.Game; import mage.game.permanent.token.Token; @@ -38,7 +39,7 @@ import mage.players.Player; * * @author BetaSteward_at_googlemail.com */ -public class CreateTokenEffect extends OneShotEffect { +public class CreateTokenEffect extends OneShotEffect { private Token token; private int amount; @@ -51,22 +52,37 @@ public class CreateTokenEffect extends OneShotEffect { super(Outcome.PutCreatureInPlay); this.token = token; this.amount = amount; - if (amount == 1) { - text = "put a " + token.getDescription(); - } - else { - text = "put " + Integer.toString(amount) + " " + token.getDescription(); - } - text += " onto the battlefield"; + } + + public CreateTokenEffect(final CreateTokenEffect effect) { + super(effect); + this.amount = effect.amount; + this.token = effect.token.copy(); } @Override - public boolean apply(Game game) { - Player controller = game.getPlayer(this.source.getControllerId()); + public CreateTokenEffect copy() { + return new CreateTokenEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); for (int i = 0; i < amount; i++) { controller.putOntoBattlefield(token, game); } return true; } + @Override + public String getText(Ability source) { + StringBuilder sb = new StringBuilder(); + if (amount == 1) + sb.append("put a"); + else + sb.append("put ").append(Integer.toString(amount)); + sb.append(" ").append(token.getDescription()).append(" onto the battlefield"); + return sb.toString(); + } + } diff --git a/Mage/src/mage/abilities/effects/common/DamageMultiEffect.java b/Mage/src/mage/abilities/effects/common/DamageMultiEffect.java index faaa61a6436..082da434caf 100644 --- a/Mage/src/mage/abilities/effects/common/DamageMultiEffect.java +++ b/Mage/src/mage/abilities/effects/common/DamageMultiEffect.java @@ -28,22 +28,20 @@ package mage.abilities.effects.common; -import java.util.HashMap; -import java.util.Map; import java.util.UUID; import mage.Constants.Outcome; +import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.Target; -import mage.target.TargetAmount; /** * * @author BetaSteward_at_googlemail.com */ -public class DamageMultiEffect extends OneShotEffect { +public class DamageMultiEffect extends OneShotEffect { protected int amount; @@ -56,18 +54,28 @@ public class DamageMultiEffect extends OneShotEffect { return amount; } + public DamageMultiEffect(final DamageMultiEffect effect) { + super(effect); + this.amount = effect.amount; + } + @Override - public boolean apply(Game game) { - Target multiTarget = this.source.getTargets().get(0); + public DamageMultiEffect copy() { + return new DamageMultiEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Target multiTarget = source.getTargets().get(0); for (UUID target: multiTarget.getTargets()) { Permanent permanent = game.getPermanent(target); if (permanent != null) { - permanent.damage(multiTarget.getTargetAmount(target), this.source.getSourceId(), game); + permanent.damage(multiTarget.getTargetAmount(target), source.getSourceId(), game); } else { Player player = game.getPlayer(target); if (player != null) { - player.damage(multiTarget.getTargetAmount(target), this.source.getSourceId(), game); + player.damage(multiTarget.getTargetAmount(target), source.getSourceId(), game); } } } @@ -75,10 +83,10 @@ public class DamageMultiEffect extends OneShotEffect { } @Override - public String getText() { + public String getText(Ability source) { StringBuilder sb = new StringBuilder(); sb.append("{source} deals ").append(Integer.toString(amount)); - sb.append(" damage divided as you choose among any number of target ").append(this.source.getTargets().get(0).getTargetName()); + sb.append(" damage divided as you choose among any number of target ").append(source.getTargets().get(0).getTargetName()); return sb.toString(); } diff --git a/Mage/src/mage/abilities/effects/common/DamageTargetEffect.java b/Mage/src/mage/abilities/effects/common/DamageTargetEffect.java index 9112fd44b74..33d2b4c74aa 100644 --- a/Mage/src/mage/abilities/effects/common/DamageTargetEffect.java +++ b/Mage/src/mage/abilities/effects/common/DamageTargetEffect.java @@ -29,6 +29,7 @@ package mage.abilities.effects.common; import mage.Constants.Outcome; +import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.game.Game; import mage.game.permanent.Permanent; @@ -38,7 +39,7 @@ import mage.players.Player; * * @author BetaSteward_at_googlemail.com */ -public class DamageTargetEffect extends OneShotEffect { +public class DamageTargetEffect extends OneShotEffect { protected int amount; @@ -51,24 +52,34 @@ public class DamageTargetEffect extends OneShotEffect { return amount; } + public DamageTargetEffect(final DamageTargetEffect effect) { + super(effect); + this.amount = effect.amount; + } + @Override - public boolean apply(Game game) { - Permanent permanent = game.getPermanent(this.source.getFirstTarget()); + public DamageTargetEffect copy() { + return new DamageTargetEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); if (permanent != null) { - permanent.damage(amount, this.source.getSourceId(), game); + permanent.damage(amount, source.getSourceId(), game); return true; } - Player player = game.getPlayer(this.source.getFirstTarget()); + Player player = game.getPlayer(source.getFirstTarget()); if (player != null) { - player.damage(amount, this.source.getSourceId(), game); + player.damage(amount, source.getSourceId(), game); return true; } return false; } @Override - public String getText() { - return "{source} deals " + Integer.toString(amount) + " damage to target " + this.source.getTargets().get(0).getTargetName(); + public String getText(Ability source) { + return "{source} deals " + Integer.toString(amount) + " damage to target " + source.getTargets().get(0).getTargetName(); } } diff --git a/Mage/src/mage/abilities/effects/common/DestroyAllControlledTargetEffect.java b/Mage/src/mage/abilities/effects/common/DestroyAllControlledTargetEffect.java index 4f196703e41..8b702bd9f84 100644 --- a/Mage/src/mage/abilities/effects/common/DestroyAllControlledTargetEffect.java +++ b/Mage/src/mage/abilities/effects/common/DestroyAllControlledTargetEffect.java @@ -29,6 +29,7 @@ package mage.abilities.effects.common; import mage.Constants.Outcome; +import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.filter.FilterPermanent; import mage.game.Game; @@ -38,7 +39,7 @@ import mage.game.permanent.Permanent; * * @author BetaSteward_at_googlemail.com */ -public class DestroyAllControlledTargetEffect extends OneShotEffect { +public class DestroyAllControlledTargetEffect extends OneShotEffect { private FilterPermanent filter; @@ -47,16 +48,26 @@ public class DestroyAllControlledTargetEffect extends OneShotEffect { this.filter = filter; } + public DestroyAllControlledTargetEffect(final DestroyAllControlledTargetEffect effect) { + super(effect); + this.filter = effect.filter.copy(); + } + @Override - public boolean apply(Game game) { - for (Permanent permanent: game.getBattlefield().getAllActivePermanents(filter, this.source.getFirstTarget())) { - permanent.destroy(this.source.getSourceId(), game, false); + public DestroyAllControlledTargetEffect copy() { + return new DestroyAllControlledTargetEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + for (Permanent permanent: game.getBattlefield().getAllActivePermanents(filter, source.getFirstTarget())) { + permanent.destroy(source.getSourceId(), game, false); } return true; } @Override - public String getText() { + public String getText(Ability source) { return "Destroy all " + filter.getMessage() + " controlled by target player"; } diff --git a/Mage/src/mage/abilities/effects/common/DestroyAllEffect.java b/Mage/src/mage/abilities/effects/common/DestroyAllEffect.java index 1ad1dbd12db..689551f6ebf 100644 --- a/Mage/src/mage/abilities/effects/common/DestroyAllEffect.java +++ b/Mage/src/mage/abilities/effects/common/DestroyAllEffect.java @@ -29,6 +29,7 @@ package mage.abilities.effects.common; import mage.Constants.Outcome; +import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.filter.FilterPermanent; import mage.game.Game; @@ -47,16 +48,26 @@ public class DestroyAllEffect extends OneShotEffect { this.filter = filter; } + public DestroyAllEffect(final DestroyAllEffect effect) { + super(effect); + this.filter = effect.filter.copy(); + } + @Override - public boolean apply(Game game) { - for (Permanent permanent: game.getBattlefield().getActivePermanents(filter, this.source.getControllerId(), game)) { - permanent.destroy(this.source.getSourceId(), game, false); + public DestroyAllEffect copy() { + return new DestroyAllEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + for (Permanent permanent: game.getBattlefield().getActivePermanents(filter, source.getControllerId(), game)) { + permanent.destroy(source.getSourceId(), game, false); } return true; } @Override - public String getText() { + public String getText(Ability source) { return "Destroy all " + filter.getMessage(); } diff --git a/Mage/src/mage/abilities/effects/common/DestroyAllNamedPermanentsEffect.java b/Mage/src/mage/abilities/effects/common/DestroyAllNamedPermanentsEffect.java index 417c0374d7b..c22658a4db0 100644 --- a/Mage/src/mage/abilities/effects/common/DestroyAllNamedPermanentsEffect.java +++ b/Mage/src/mage/abilities/effects/common/DestroyAllNamedPermanentsEffect.java @@ -29,6 +29,7 @@ package mage.abilities.effects.common; import mage.Constants.Outcome; +import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.game.Game; import mage.game.permanent.Permanent; @@ -37,29 +38,38 @@ import mage.game.permanent.Permanent; * * @author BetaSteward_at_googlemail.com */ -public class DestroyAllNamedPermanentsEffect extends OneShotEffect { +public class DestroyAllNamedPermanentsEffect extends OneShotEffect { public DestroyAllNamedPermanentsEffect() { super(Outcome.DestroyPermanent); } + public DestroyAllNamedPermanentsEffect(final DestroyAllNamedPermanentsEffect effect) { + super(effect); + } + @Override - public boolean apply(Game game) { - Permanent permanent = game.getPermanent(this.source.getFirstTarget()); + public DestroyAllNamedPermanentsEffect copy() { + return new DestroyAllNamedPermanentsEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); String name = permanent.getName(); - permanent.destroy(this.source.getSourceId(), game, false); - for (Permanent perm: game.getBattlefield().getActivePermanents(this.source.getControllerId(), game)) { + permanent.destroy(source.getSourceId(), game, false); + for (Permanent perm: game.getBattlefield().getActivePermanents(source.getControllerId(), game)) { if (perm.getName().equals(name)) - perm.destroy(this.source.getSourceId(), game, false); + perm.destroy(source.getSourceId(), game, false); } return true; } @Override - public String getText() { - return "Destroy target " + this.source.getTargets().get(0).getTargetName() + " and all other permanents with the same name as that permanent"; + public String getText(Ability source) { + return "Destroy target " + source.getTargets().get(0).getTargetName() + " and all other permanents with the same name as that permanent"; } } diff --git a/Mage/src/mage/abilities/effects/common/DestroyNoRegenTargetEffect.java b/Mage/src/mage/abilities/effects/common/DestroyNoRegenTargetEffect.java index d3f377ccd62..a875bd42ef1 100644 --- a/Mage/src/mage/abilities/effects/common/DestroyNoRegenTargetEffect.java +++ b/Mage/src/mage/abilities/effects/common/DestroyNoRegenTargetEffect.java @@ -29,6 +29,7 @@ package mage.abilities.effects.common; import mage.Constants.Outcome; +import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.game.Game; import mage.game.permanent.Permanent; @@ -37,21 +38,31 @@ import mage.game.permanent.Permanent; * * @author BetaSteward_at_googlemail.com */ -public class DestroyNoRegenTargetEffect extends OneShotEffect { +public class DestroyNoRegenTargetEffect extends OneShotEffect { public DestroyNoRegenTargetEffect() { super(Outcome.DestroyPermanent); } - public boolean apply(Game game) { - Permanent permanent = game.getPermanent(this.source.getFirstTarget()); - permanent.destroy(this.source.getSourceId(), game, true); + public DestroyNoRegenTargetEffect(final DestroyNoRegenTargetEffect effect) { + super(effect); + } + + @Override + public DestroyNoRegenTargetEffect copy() { + return new DestroyNoRegenTargetEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); + permanent.destroy(source.getSourceId(), game, true); return true; } @Override - public String getText() { - return "Destroy target " + this.source.getTargets().get(0).getTargetName() + ". It can't be regenerated"; + public String getText(Ability source) { + return "Destroy target " + source.getTargets().get(0).getTargetName() + ". It can't be regenerated"; } } diff --git a/Mage/src/mage/abilities/effects/common/DestroyTargetEffect.java b/Mage/src/mage/abilities/effects/common/DestroyTargetEffect.java index 6e7cea4d7a1..d0f3872e067 100644 --- a/Mage/src/mage/abilities/effects/common/DestroyTargetEffect.java +++ b/Mage/src/mage/abilities/effects/common/DestroyTargetEffect.java @@ -29,6 +29,7 @@ package mage.abilities.effects.common; import mage.Constants.Outcome; +import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.game.Game; import mage.game.permanent.Permanent; @@ -37,7 +38,7 @@ import mage.game.permanent.Permanent; * * @author BetaSteward_at_googlemail.com */ -public class DestroyTargetEffect extends OneShotEffect { +public class DestroyTargetEffect extends OneShotEffect { protected boolean noRegen; @@ -50,18 +51,28 @@ public class DestroyTargetEffect extends OneShotEffect { this.noRegen = noRegen; } + public DestroyTargetEffect(final DestroyTargetEffect effect) { + super(effect); + this.noRegen = effect.noRegen; + } + @Override - public boolean apply(Game game) { - Permanent permanent = game.getPermanent(this.source.getFirstTarget()); + public DestroyTargetEffect copy() { + return new DestroyTargetEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); if (permanent != null) { - permanent.destroy(this.source.getSourceId(), game, noRegen); + permanent.destroy(source.getSourceId(), game, noRegen); return true; } return false; } @Override - public String getText() { + public String getText(Ability source) { return "Destroy target " + source.getTargets().get(0).getTargetName(); } diff --git a/Mage/src/mage/abilities/effects/common/DiscardTargetEffect.java b/Mage/src/mage/abilities/effects/common/DiscardTargetEffect.java index 475c8e9a36a..d08b3d5eb40 100644 --- a/Mage/src/mage/abilities/effects/common/DiscardTargetEffect.java +++ b/Mage/src/mage/abilities/effects/common/DiscardTargetEffect.java @@ -29,6 +29,7 @@ package mage.abilities.effects.common; import mage.Constants.Outcome; +import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.game.Game; import mage.players.Player; @@ -37,7 +38,7 @@ import mage.players.Player; * * @author BetaSteward_at_googlemail.com */ -public class DiscardTargetEffect extends OneShotEffect { +public class DiscardTargetEffect extends OneShotEffect { protected int amount; @@ -46,9 +47,19 @@ public class DiscardTargetEffect extends OneShotEffect { this.amount = amount; } + public DiscardTargetEffect(final DiscardTargetEffect effect) { + super(effect); + this.amount = effect.amount; + } + @Override - public boolean apply(Game game) { - Player player = game.getPlayer(this.source.getFirstTarget()); + public DiscardTargetEffect copy() { + return new DiscardTargetEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getFirstTarget()); if (player != null) { player.discard(amount, game); return true; @@ -57,7 +68,7 @@ public class DiscardTargetEffect extends OneShotEffect { } @Override - public String getText() { + public String getText(Ability source) { return "Target player discards " + Integer.toString(amount) + " card" + (amount == 1?"":"s"); } diff --git a/Mage/src/mage/abilities/effects/common/DrawCardAllEffect.java b/Mage/src/mage/abilities/effects/common/DrawCardAllEffect.java index 75ba0f7c230..a0c20527cd5 100644 --- a/Mage/src/mage/abilities/effects/common/DrawCardAllEffect.java +++ b/Mage/src/mage/abilities/effects/common/DrawCardAllEffect.java @@ -29,6 +29,7 @@ package mage.abilities.effects.common; import mage.Constants.Outcome; +import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.game.Game; import mage.players.Player; @@ -37,7 +38,7 @@ import mage.players.Player; * * @author BetaSteward_at_googlemail.com */ -public class DrawCardAllEffect extends OneShotEffect { +public class DrawCardAllEffect extends OneShotEffect { protected int amount; @@ -46,8 +47,18 @@ public class DrawCardAllEffect extends OneShotEffect { this.amount = amount; } + public DrawCardAllEffect(final DrawCardAllEffect effect) { + super(effect); + this.amount = effect.amount; + } + @Override - public boolean apply(Game game) { + public DrawCardAllEffect copy() { + return new DrawCardAllEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { for (Player player: game.getPlayers().values()) { player.drawCards(amount, game); @@ -56,7 +67,7 @@ public class DrawCardAllEffect extends OneShotEffect { } @Override - public String getText() { + public String getText(Ability source) { return "Each player draws " + Integer.toString(amount) + " card" + (amount == 1?"":"s"); } diff --git a/Mage/src/mage/abilities/effects/common/DrawCardControllerEffect.java b/Mage/src/mage/abilities/effects/common/DrawCardControllerEffect.java index 9443292b45b..2719995dafc 100644 --- a/Mage/src/mage/abilities/effects/common/DrawCardControllerEffect.java +++ b/Mage/src/mage/abilities/effects/common/DrawCardControllerEffect.java @@ -29,6 +29,7 @@ package mage.abilities.effects.common; import mage.Constants.Outcome; +import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.game.Game; import mage.players.Player; @@ -37,7 +38,7 @@ import mage.players.Player; * * @author BetaSteward_at_googlemail.com */ -public class DrawCardControllerEffect extends OneShotEffect { +public class DrawCardControllerEffect extends OneShotEffect { protected int amount; @@ -46,9 +47,19 @@ public class DrawCardControllerEffect extends OneShotEffect { this.amount = amount; } + public DrawCardControllerEffect(final DrawCardControllerEffect effect) { + super(effect); + this.amount = effect.amount; + } + @Override - public boolean apply(Game game) { - Player player = game.getPlayer(this.source.getControllerId()); + public DrawCardControllerEffect copy() { + return new DrawCardControllerEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); if (player != null) { player.drawCards(amount, game); return true; @@ -57,7 +68,7 @@ public class DrawCardControllerEffect extends OneShotEffect { } @Override - public String getText() { + public String getText(Ability source) { StringBuilder sb = new StringBuilder(); sb.append("draw ").append(Integer.toString(amount)).append(" card").append((amount == 1?"":"s")); return sb.toString(); diff --git a/Mage/src/mage/abilities/effects/common/DrawCardTargetEffect.java b/Mage/src/mage/abilities/effects/common/DrawCardTargetEffect.java index 16d8b0c68a1..b2ebcae5fcb 100644 --- a/Mage/src/mage/abilities/effects/common/DrawCardTargetEffect.java +++ b/Mage/src/mage/abilities/effects/common/DrawCardTargetEffect.java @@ -29,6 +29,7 @@ package mage.abilities.effects.common; import mage.Constants.Outcome; +import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.game.Game; import mage.players.Player; @@ -37,7 +38,7 @@ import mage.players.Player; * * @author BetaSteward_at_googlemail.com */ -public class DrawCardTargetEffect extends OneShotEffect { +public class DrawCardTargetEffect extends OneShotEffect { protected int amount; @@ -46,9 +47,19 @@ public class DrawCardTargetEffect extends OneShotEffect { this.amount = amount; } + public DrawCardTargetEffect(final DrawCardTargetEffect effect) { + super(effect); + this.amount = effect.amount; + } + @Override - public boolean apply(Game game) { - Player player = game.getPlayer(this.source.getFirstTarget()); + public DrawCardTargetEffect copy() { + return new DrawCardTargetEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getFirstTarget()); if (player != null) { player.drawCards(amount, game); return true; @@ -57,7 +68,7 @@ public class DrawCardTargetEffect extends OneShotEffect { } @Override - public String getText() { + public String getText(Ability source) { StringBuilder sb = new StringBuilder(); sb.append("Target player draws ").append(Integer.toString(amount)).append(" card").append((amount == 1?"":"s")); return sb.toString(); diff --git a/Mage/src/mage/abilities/effects/common/EntersBattlefieldTappedEffect.java b/Mage/src/mage/abilities/effects/common/EntersBattlefieldTappedEffect.java index cfbbd4ff9b6..a360f5a4e48 100644 --- a/Mage/src/mage/abilities/effects/common/EntersBattlefieldTappedEffect.java +++ b/Mage/src/mage/abilities/effects/common/EntersBattlefieldTappedEffect.java @@ -28,6 +28,7 @@ package mage.abilities.effects.common; +import mage.abilities.Ability; import mage.abilities.effects.EntersBattlefieldEffect; /** @@ -40,8 +41,17 @@ public class EntersBattlefieldTappedEffect extends EntersBattlefieldEffect { super(new TapSourceEffect()); } + public EntersBattlefieldTappedEffect(final EntersBattlefieldTappedEffect effect) { + super(effect); + } + @Override - public String getText() { + public EntersBattlefieldTappedEffect copy() { + return new EntersBattlefieldTappedEffect(this); + } + + @Override + public String getText(Ability source) { return "{this} enters the battlefield tapped"; } } diff --git a/Mage/src/mage/abilities/effects/common/EntersBattlefieldTappedUnlessControlsEffect.java b/Mage/src/mage/abilities/effects/common/EntersBattlefieldTappedUnlessControlsEffect.java index dbec15a5736..491149a66c4 100644 --- a/Mage/src/mage/abilities/effects/common/EntersBattlefieldTappedUnlessControlsEffect.java +++ b/Mage/src/mage/abilities/effects/common/EntersBattlefieldTappedUnlessControlsEffect.java @@ -28,6 +28,7 @@ package mage.abilities.effects.common; +import mage.abilities.Ability; import mage.filter.FilterPermanent; import mage.game.Game; import mage.game.events.GameEvent; @@ -44,16 +45,26 @@ public class EntersBattlefieldTappedUnlessControlsEffect extends EntersBattlefie this.filter = filter; } + public EntersBattlefieldTappedUnlessControlsEffect(final EntersBattlefieldTappedUnlessControlsEffect effect) { + super(effect); + this.filter = effect.filter.copy(); + } + @Override - public boolean replaceEvent(GameEvent event, Game game) { - if (game.getBattlefield().countAll(filter, this.source.getControllerId()) == 0) - return apply(game); + public EntersBattlefieldTappedUnlessControlsEffect copy() { + return new EntersBattlefieldTappedUnlessControlsEffect(this); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + if (game.getBattlefield().countAll(filter, source.getControllerId()) == 0) + return apply(game, source); return false; } @Override - public String getText() { - return super.getText() + " unless you control a " + filter.getMessage(); + public String getText(Ability source) { + return super.getText(source) + " unless you control a " + filter.getMessage(); } } diff --git a/Mage/src/mage/abilities/effects/common/ExileSourceEffect.java b/Mage/src/mage/abilities/effects/common/ExileSourceEffect.java index adbf25a5477..d6dda6e1d1f 100644 --- a/Mage/src/mage/abilities/effects/common/ExileSourceEffect.java +++ b/Mage/src/mage/abilities/effects/common/ExileSourceEffect.java @@ -29,6 +29,7 @@ package mage.abilities.effects.common; import mage.Constants.Outcome; +import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.game.Game; import mage.game.permanent.Permanent; @@ -37,15 +38,24 @@ import mage.game.permanent.Permanent; * * @author BetaSteward_at_googlemail.com */ -public class ExileSourceEffect extends OneShotEffect { +public class ExileSourceEffect extends OneShotEffect { public ExileSourceEffect() { super(Outcome.Exile); } + public ExileSourceEffect(final ExileSourceEffect effect) { + super(effect); + } + @Override - public boolean apply(Game game) { - Permanent permanent = game.getPermanent(this.source.getSourceId()); + public ExileSourceEffect copy() { + return new ExileSourceEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getSourceId()); if (permanent != null) { return permanent.moveToExile(null, "", game); } @@ -53,7 +63,7 @@ public class ExileSourceEffect extends OneShotEffect { } @Override - public String getText() { + public String getText(Ability source) { return "Exile {this}"; } diff --git a/Mage/src/mage/abilities/effects/common/ExileSpellEffect.java b/Mage/src/mage/abilities/effects/common/ExileSpellEffect.java index 12e24cd6306..d0bc5aa1fd3 100644 --- a/Mage/src/mage/abilities/effects/common/ExileSpellEffect.java +++ b/Mage/src/mage/abilities/effects/common/ExileSpellEffect.java @@ -30,6 +30,7 @@ package mage.abilities.effects.common; import java.io.ObjectStreamException; import mage.Constants.Outcome; +import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.game.Game; @@ -37,7 +38,7 @@ import mage.game.Game; * * @author BetaSteward_at_googlemail.com */ -public class ExileSpellEffect extends OneShotEffect { +public class ExileSpellEffect extends OneShotEffect { private static final ExileSpellEffect fINSTANCE = new ExileSpellEffect(); @@ -54,13 +55,18 @@ public class ExileSpellEffect extends OneShotEffect { } @Override - public boolean apply(Game game) { + public boolean apply(Game game, Ability source) { //this effect is applied when a spell resolves - see Spell.java return true; } @Override - public String getText() { + public String getText(Ability source) { return "Exile {this}"; } + + @Override + public ExileSpellEffect copy() { + return fINSTANCE; + } } diff --git a/Mage/src/mage/abilities/effects/common/ExileTargetEffect.java b/Mage/src/mage/abilities/effects/common/ExileTargetEffect.java index e1a47479847..8f06a569f6a 100644 --- a/Mage/src/mage/abilities/effects/common/ExileTargetEffect.java +++ b/Mage/src/mage/abilities/effects/common/ExileTargetEffect.java @@ -31,6 +31,7 @@ package mage.abilities.effects.common; import java.util.UUID; import mage.Constants.Outcome; import mage.Constants.Zone; +import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.game.Game; import mage.game.permanent.Permanent; @@ -39,7 +40,7 @@ import mage.game.permanent.Permanent; * * @author BetaSteward_at_googlemail.com */ -public class ExileTargetEffect extends OneShotEffect { +public class ExileTargetEffect extends OneShotEffect { private String exileZone = null; private UUID exileId = null; @@ -54,9 +55,20 @@ public class ExileTargetEffect extends OneShotEffect { super(Outcome.Exile); } + public ExileTargetEffect(final ExileTargetEffect effect) { + super(effect); + this.exileZone = effect.exileZone; + this.exileId = effect.exileId; + } + @Override - public boolean apply(Game game) { - Permanent permanent = game.getPermanent(this.source.getFirstTarget()); + public ExileTargetEffect copy() { + return new ExileTargetEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); if (permanent != null) { return permanent.moveToExile(exileId, exileZone, game); } @@ -64,8 +76,8 @@ public class ExileTargetEffect extends OneShotEffect { } @Override - public String getText() { - return "Exile target " + this.source.getTargets().get(0).getTargetName(); + public String getText(Ability source) { + return "Exile target " + source.getTargets().get(0).getTargetName(); } } diff --git a/Mage/src/mage/abilities/effects/common/GainAbilityAttachedEffect.java b/Mage/src/mage/abilities/effects/common/GainAbilityAttachedEffect.java index b57d4e50083..8c37b149b63 100644 --- a/Mage/src/mage/abilities/effects/common/GainAbilityAttachedEffect.java +++ b/Mage/src/mage/abilities/effects/common/GainAbilityAttachedEffect.java @@ -41,7 +41,7 @@ import mage.game.permanent.Permanent; * * @author BetaSteward_at_googlemail.com */ -public class GainAbilityAttachedEffect extends ContinuousEffectImpl { +public class GainAbilityAttachedEffect extends ContinuousEffectImpl { protected Ability ability; @@ -50,19 +50,30 @@ public class GainAbilityAttachedEffect extends ContinuousEffectImpl { this.ability = ability; } + public GainAbilityAttachedEffect(final GainAbilityAttachedEffect effect) { + super(effect); + this.ability = effect.ability.copy(); + } + @Override - public boolean apply(Game game) { - Permanent equipment = game.getPermanent(this.source.getSourceId()); + public GainAbilityAttachedEffect copy() { + return new GainAbilityAttachedEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent equipment = game.getPermanent(source.getSourceId()); if (equipment.getAttachedTo() != null) { Permanent creature = game.getPermanent(equipment.getAttachedTo()); if (creature != null) creature.addAbility(ability); + //TODO: should copy ability before adding } return true; } @Override - public String getText() { + public String getText(Ability source) { return "Equipped creature gains " + ability.getRule(); } diff --git a/Mage/src/mage/abilities/effects/common/GainAbilityControlledEffect.java b/Mage/src/mage/abilities/effects/common/GainAbilityControlledEffect.java index 7de93b2ec2b..7d0ba7b6d8f 100644 --- a/Mage/src/mage/abilities/effects/common/GainAbilityControlledEffect.java +++ b/Mage/src/mage/abilities/effects/common/GainAbilityControlledEffect.java @@ -42,7 +42,7 @@ import mage.game.permanent.Permanent; * * @author BetaSteward_at_googlemail.com */ -public class GainAbilityControlledEffect extends ContinuousEffectImpl { +public class GainAbilityControlledEffect extends ContinuousEffectImpl { protected Ability ability; protected FilterPermanent permanentFilter; @@ -57,16 +57,27 @@ public class GainAbilityControlledEffect extends ContinuousEffectImpl { this.permanentFilter = filter; } + public GainAbilityControlledEffect(final GainAbilityControlledEffect effect) { + super(effect); + this.ability = effect.ability.copy(); + this.permanentFilter = effect.permanentFilter.copy(); + } + @Override - public boolean apply(Game game) { - for (Permanent perm: game.getBattlefield().getAllActivePermanents(permanentFilter, this.source.getControllerId())) { - perm.addAbility(ability); + public GainAbilityControlledEffect copy() { + return new GainAbilityControlledEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + for (Permanent perm: game.getBattlefield().getAllActivePermanents(permanentFilter, source.getControllerId())) { + perm.addAbility(ability.copy()); } return true; } @Override - public String getText() { + public String getText(Ability source) { StringBuilder sb = new StringBuilder(); sb.append(permanentFilter.getMessage()).append(" you control gain ").append(ability.getRule()); sb.append(" ").append(duration.toString()); diff --git a/Mage/src/mage/abilities/effects/common/GainAbilitySourceEffect.java b/Mage/src/mage/abilities/effects/common/GainAbilitySourceEffect.java index 7d064e8eeac..8bed43abcda 100644 --- a/Mage/src/mage/abilities/effects/common/GainAbilitySourceEffect.java +++ b/Mage/src/mage/abilities/effects/common/GainAbilitySourceEffect.java @@ -41,7 +41,7 @@ import mage.game.permanent.Permanent; * * @author BetaSteward_at_googlemail.com */ -public class GainAbilitySourceEffect extends ContinuousEffectImpl { +public class GainAbilitySourceEffect extends ContinuousEffectImpl { protected Ability ability; @@ -50,18 +50,29 @@ public class GainAbilitySourceEffect extends ContinuousEffectImpl { this.ability = ability; } + public GainAbilitySourceEffect(final GainAbilitySourceEffect effect) { + super(effect); + this.ability = effect.ability.copy(); + } + @Override - public boolean apply(Game game) { - Permanent permanent = game.getPermanent(this.source.getSourceId()); + public GainAbilitySourceEffect copy() { + return new GainAbilitySourceEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getSourceId()); if (permanent != null) { permanent.addAbility(ability); + //TODO: should copy ability before adding return true; } return false; } @Override - public String getText() { + public String getText(Ability source) { StringBuilder sb = new StringBuilder(); sb.append("{this} gains ").append(ability.getRule()).append(" ").append(duration.toString()); return sb.toString(); diff --git a/Mage/src/mage/abilities/effects/common/GainAbilityTargetEffect.java b/Mage/src/mage/abilities/effects/common/GainAbilityTargetEffect.java index ae8f246be6a..05c29b6b0ac 100644 --- a/Mage/src/mage/abilities/effects/common/GainAbilityTargetEffect.java +++ b/Mage/src/mage/abilities/effects/common/GainAbilityTargetEffect.java @@ -50,20 +50,31 @@ public class GainAbilityTargetEffect extends ContinuousEffectImpl { this.ability = ability; } + public GainAbilityTargetEffect(final GainAbilityTargetEffect effect) { + super(effect); + this.ability = effect.ability.copy(); + } + @Override - public boolean apply(Game game) { - Permanent permanent = game.getPermanent(this.source.getFirstTarget()); + public GainAbilityTargetEffect copy() { + return new GainAbilityTargetEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); if (permanent != null) { permanent.addAbility(ability); + //TODO: should copy ability before adding return true; } return false; } @Override - public String getText() { + public String getText(Ability source) { StringBuilder sb = new StringBuilder(); - sb.append("Target ").append(this.source.getTargets().get(0).getTargetName()).append(" gains "); + sb.append("Target ").append(source.getTargets().get(0).getTargetName()).append(" gains "); sb.append(ability.getRule()).append(" ").append(duration.toString()); return sb.toString(); } diff --git a/Mage/src/mage/abilities/effects/common/GainControlTargetEOTEffect.java b/Mage/src/mage/abilities/effects/common/GainControlTargetEOTEffect.java index 7e6482285a2..27eac7b9058 100644 --- a/Mage/src/mage/abilities/effects/common/GainControlTargetEOTEffect.java +++ b/Mage/src/mage/abilities/effects/common/GainControlTargetEOTEffect.java @@ -32,6 +32,7 @@ import mage.Constants.Duration; import mage.Constants.Layer; import mage.Constants.Outcome; import mage.Constants.SubLayer; +import mage.abilities.Ability; import mage.abilities.effects.ContinuousEffectImpl; import mage.game.Game; import mage.game.permanent.Permanent; @@ -46,17 +47,26 @@ public class GainControlTargetEOTEffect extends ContinuousEffectImpl { super(Duration.EndOfTurn, Layer.ControlChangingEffects_2, SubLayer.NA, Outcome.GainControl); } + public GainControlTargetEOTEffect(final GainControlTargetEOTEffect effect) { + super(effect); + } + @Override - public boolean apply(Game game) { - Permanent permanent = game.getPermanent(this.source.getFirstTarget()); + public GainControlTargetEOTEffect copy() { + return new GainControlTargetEOTEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); if (permanent != null) { - return permanent.changeControllerId(this.source.getControllerId(), game); + return permanent.changeControllerId(source.getControllerId(), game); } return false; } @Override - public String getText() { - return "Gain control of target " + this.source.getTargets().get(0).getTargetName() + " until end of turn"; + public String getText(Ability source) { + return "Gain control of target " + source.getTargets().get(0).getTargetName() + " until end of turn"; } } diff --git a/Mage/src/mage/abilities/effects/common/GainLifeEffect.java b/Mage/src/mage/abilities/effects/common/GainLifeEffect.java index 92624d3b9d0..f5058f4c6ba 100644 --- a/Mage/src/mage/abilities/effects/common/GainLifeEffect.java +++ b/Mage/src/mage/abilities/effects/common/GainLifeEffect.java @@ -29,6 +29,7 @@ package mage.abilities.effects.common; import mage.Constants.Outcome; +import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.game.Game; import mage.players.Player; @@ -37,7 +38,7 @@ import mage.players.Player; * * @author BetaSteward_at_googlemail.com */ -public class GainLifeEffect extends OneShotEffect { +public class GainLifeEffect extends OneShotEffect { private int life; @@ -46,9 +47,19 @@ public class GainLifeEffect extends OneShotEffect { this.life = life; } + public GainLifeEffect(final GainLifeEffect effect) { + super(effect); + this.life = effect.life; + } + @Override - public boolean apply(Game game) { - Player player = game.getPlayer(this.source.getControllerId()); + public GainLifeEffect copy() { + return new GainLifeEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); if (player != null) { player.gainLife(life, game); } @@ -56,7 +67,7 @@ public class GainLifeEffect extends OneShotEffect { } @Override - public String getText() { + public String getText(Ability source) { return "you gain " + Integer.toString(life) + " life"; } diff --git a/Mage/src/mage/abilities/effects/common/GainProtectionFromColorTargetEOTEffect.java b/Mage/src/mage/abilities/effects/common/GainProtectionFromColorTargetEOTEffect.java index cb4af20d850..3842bf28372 100644 --- a/Mage/src/mage/abilities/effects/common/GainProtectionFromColorTargetEOTEffect.java +++ b/Mage/src/mage/abilities/effects/common/GainProtectionFromColorTargetEOTEffect.java @@ -29,6 +29,7 @@ package mage.abilities.effects.common; import mage.Constants.Duration; +import mage.abilities.Ability; import mage.abilities.keyword.ProtectionAbility; import mage.choices.ChoiceColor; import mage.filter.FilterCard; @@ -49,9 +50,19 @@ public class GainProtectionFromColorTargetEOTEffect extends GainAbilityTargetEff protectionFilter.setUseColor(true); } + public GainProtectionFromColorTargetEOTEffect(final GainProtectionFromColorTargetEOTEffect effect) { + super(effect); + this.protectionFilter = effect.protectionFilter.copy(); + } + @Override - public boolean apply(Game game) { - ChoiceColor choice = (ChoiceColor) this.source.getChoices().get(0); + public GainProtectionFromColorTargetEOTEffect copy() { + return new GainProtectionFromColorTargetEOTEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + ChoiceColor choice = (ChoiceColor) source.getChoices().get(0); protectionFilter.setColor(choice.getColor()); protectionFilter.setMessage(choice.getChoice()); Permanent creature = game.getPermanent(source.getFirstTarget()); @@ -63,7 +74,7 @@ public class GainProtectionFromColorTargetEOTEffect extends GainAbilityTargetEff } @Override - public String getText() { + public String getText(Ability source) { return "target creature you control gains protection from the color of your choice until end of turn"; } diff --git a/Mage/src/mage/abilities/effects/common/LoseLifeTargetEffect.java b/Mage/src/mage/abilities/effects/common/LoseLifeTargetEffect.java index 60636afb4fd..090ccd69f9a 100644 --- a/Mage/src/mage/abilities/effects/common/LoseLifeTargetEffect.java +++ b/Mage/src/mage/abilities/effects/common/LoseLifeTargetEffect.java @@ -29,6 +29,7 @@ package mage.abilities.effects.common; import mage.Constants.Outcome; +import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.game.Game; import mage.players.Player; @@ -37,7 +38,7 @@ import mage.players.Player; * * @author BetaSteward_at_googlemail.com */ -public class LoseLifeTargetEffect extends OneShotEffect { +public class LoseLifeTargetEffect extends OneShotEffect { protected int amount; @@ -50,9 +51,19 @@ public class LoseLifeTargetEffect extends OneShotEffect { return amount; } + public LoseLifeTargetEffect(final LoseLifeTargetEffect effect) { + super(effect); + this.amount = effect.amount; + } + @Override - public boolean apply(Game game) { - Player player = game.getPlayer(this.source.getFirstTarget()); + public LoseLifeTargetEffect copy() { + return new LoseLifeTargetEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getFirstTarget()); if (player != null) { player.loseLife(amount, game); return true; @@ -61,8 +72,8 @@ public class LoseLifeTargetEffect extends OneShotEffect { } @Override - public String getText() { - return "Target " + this.source.getTargets().get(0).getTargetName() + " loses " + Integer.toString(amount) + " life"; + public String getText(Ability source) { + return "Target " + source.getTargets().get(0).getTargetName() + " loses " + Integer.toString(amount) + " life"; } diff --git a/Mage/src/mage/abilities/effects/common/ManaEffect.java b/Mage/src/mage/abilities/effects/common/ManaEffect.java index 1856c774e46..a555a0b7452 100644 --- a/Mage/src/mage/abilities/effects/common/ManaEffect.java +++ b/Mage/src/mage/abilities/effects/common/ManaEffect.java @@ -30,6 +30,7 @@ package mage.abilities.effects.common; import mage.Constants.Outcome; import mage.Mana; +import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.game.Game; @@ -37,7 +38,7 @@ import mage.game.Game; * * @author BetaSteward_at_googlemail.com */ -public class ManaEffect extends OneShotEffect { +public class ManaEffect extends OneShotEffect { protected Mana mana; @@ -46,14 +47,24 @@ public class ManaEffect extends OneShotEffect { this.mana = mana; } + public ManaEffect(final ManaEffect effect) { + super(effect); + this.mana = effect.mana.copy(); + } + @Override - public boolean apply(Game game) { - game.getPlayer(this.source.getControllerId()).getManaPool().changeMana(mana); + public ManaEffect copy() { + return new ManaEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + game.getPlayer(source.getControllerId()).getManaPool().changeMana(mana); return true; } @Override - public String getText() { + public String getText(Ability source) { return "Add " + mana.toString() + " to your mana pool"; } diff --git a/Mage/src/mage/abilities/effects/common/PassEffect.java b/Mage/src/mage/abilities/effects/common/PassEffect.java new file mode 100644 index 00000000000..175897c1c0e --- /dev/null +++ b/Mage/src/mage/abilities/effects/common/PassEffect.java @@ -0,0 +1,60 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.abilities.effects.common; + +import mage.Constants.Outcome; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.game.Game; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class PassEffect extends OneShotEffect { + + public PassEffect() { + super(Outcome.Neutral); + } + + public PassEffect(final PassEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public PassEffect copy() { + return new PassEffect(this); + } + +} diff --git a/Mage/src/mage/abilities/effects/common/PlayTargetWithoutPayingManaEffect.java b/Mage/src/mage/abilities/effects/common/PlayTargetWithoutPayingManaEffect.java index 282e6eaf4a4..5e3859156f9 100644 --- a/Mage/src/mage/abilities/effects/common/PlayTargetWithoutPayingManaEffect.java +++ b/Mage/src/mage/abilities/effects/common/PlayTargetWithoutPayingManaEffect.java @@ -29,6 +29,7 @@ package mage.abilities.effects.common; import mage.Constants.Outcome; +import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.Card; import mage.game.Game; @@ -38,21 +39,30 @@ import mage.players.Player; * * @author BetaSteward_at_googlemail.com */ -public class PlayTargetWithoutPayingManaEffect extends OneShotEffect { +public class PlayTargetWithoutPayingManaEffect extends OneShotEffect { public PlayTargetWithoutPayingManaEffect() { super(Outcome.GainControl); } + public PlayTargetWithoutPayingManaEffect(final PlayTargetWithoutPayingManaEffect effect) { + super(effect); + } + @Override - public boolean apply(Game game) { - Player controller = game.getPlayer(this.source.getControllerId()); - Card target = (Card) game.getObject(this.source.getFirstTarget()); + public PlayTargetWithoutPayingManaEffect copy() { + return new PlayTargetWithoutPayingManaEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Card target = (Card) game.getObject(source.getFirstTarget()); return controller.cast(target.getSpellAbility(), game, true); } @Override - public String getText() { - return "you may put " + this.getSource().getTargets().get(0).getTargetName() + " from your hand onto the battlefield"; + public String getText(Ability source) { + return "you may put " + source.getTargets().get(0).getTargetName() + " from your hand onto the battlefield"; } } diff --git a/Mage/src/mage/abilities/effects/common/PreventAllCombatDamageEffect.java b/Mage/src/mage/abilities/effects/common/PreventAllCombatDamageEffect.java index 44b7c0f53d4..00038e5fa5c 100644 --- a/Mage/src/mage/abilities/effects/common/PreventAllCombatDamageEffect.java +++ b/Mage/src/mage/abilities/effects/common/PreventAllCombatDamageEffect.java @@ -30,6 +30,7 @@ package mage.abilities.effects.common; import mage.Constants.Duration; import mage.Constants.PhaseStep; +import mage.abilities.Ability; import mage.abilities.effects.PreventionEffectImpl; import mage.game.Game; import mage.game.events.GameEvent; @@ -38,32 +39,41 @@ import mage.game.events.GameEvent; * * @author BetaSteward_at_googlemail.com */ -public class PreventAllCombatDamageEffect extends PreventionEffectImpl { +public class PreventAllCombatDamageEffect extends PreventionEffectImpl { public PreventAllCombatDamageEffect(Duration duration) { super(duration); } + public PreventAllCombatDamageEffect(final PreventAllCombatDamageEffect effect) { + super(effect); + } + @Override - public boolean apply(Game game) { + public PreventAllCombatDamageEffect copy() { + return new PreventAllCombatDamageEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { return true; } @Override - public boolean replaceEvent(GameEvent event, Game game) { + public boolean replaceEvent(GameEvent event, Ability source, Game game) { event.setAmount(0); return false; } @Override - public boolean applies(GameEvent event, Game game) { + public boolean applies(GameEvent event, Ability source, Game game) { if (game.getTurn().getStepType() == PhaseStep.COMBAT_DAMAGE) - return super.applies(event, game); + return super.applies(event, source, game); return false; } @Override - public String getText() { + public String getText(Ability source) { return "Prevent all combat damage " + duration.toString(); } } diff --git a/Mage/src/mage/abilities/effects/common/PreventAllDamageSourceEffect.java b/Mage/src/mage/abilities/effects/common/PreventAllDamageSourceEffect.java index 63383c7fd1f..869a2892a92 100644 --- a/Mage/src/mage/abilities/effects/common/PreventAllDamageSourceEffect.java +++ b/Mage/src/mage/abilities/effects/common/PreventAllDamageSourceEffect.java @@ -29,6 +29,7 @@ package mage.abilities.effects.common; import mage.Constants.Duration; +import mage.abilities.Ability; import mage.abilities.effects.PreventionEffectImpl; import mage.game.Game; import mage.game.events.GameEvent; @@ -38,27 +39,36 @@ import mage.game.permanent.Permanent; * * @author BetaSteward_at_googlemail.com */ -public class PreventAllDamageSourceEffect extends PreventionEffectImpl { +public class PreventAllDamageSourceEffect extends PreventionEffectImpl { public PreventAllDamageSourceEffect(Duration duration) { super(duration); } + public PreventAllDamageSourceEffect(final PreventAllDamageSourceEffect effect) { + super(effect); + } + @Override - public boolean apply(Game game) { + public PreventAllDamageSourceEffect copy() { + return new PreventAllDamageSourceEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { return true; } @Override - public boolean replaceEvent(GameEvent event, Game game) { + public boolean replaceEvent(GameEvent event, Ability source, Game game) { event.setAmount(0); return false; } @Override - public boolean applies(GameEvent event, Game game) { - if (super.applies(event, game)) { - if (event.getTargetId().equals(this.source.getId())) { + public boolean applies(GameEvent event, Ability source, Game game) { + if (super.applies(event, source, game)) { + if (event.getTargetId().equals(source.getId())) { return true; } } @@ -66,7 +76,7 @@ public class PreventAllDamageSourceEffect extends PreventionEffectImpl { } @Override - public String getText() { + public String getText(Ability source) { return "Prevent all damage that would be dealt to {this} " + duration.toString(); } diff --git a/Mage/src/mage/abilities/effects/common/PreventAllDamageToEffect.java b/Mage/src/mage/abilities/effects/common/PreventAllDamageToEffect.java index 9b513314021..76119f9c36b 100644 --- a/Mage/src/mage/abilities/effects/common/PreventAllDamageToEffect.java +++ b/Mage/src/mage/abilities/effects/common/PreventAllDamageToEffect.java @@ -29,6 +29,7 @@ package mage.abilities.effects.common; import mage.Constants.Duration; +import mage.abilities.Ability; import mage.abilities.effects.PreventionEffectImpl; import mage.filter.Filter; import mage.game.Game; @@ -40,7 +41,7 @@ import mage.players.Player; * * @author BetaSteward_at_googlemail.com */ -public class PreventAllDamageToEffect extends PreventionEffectImpl { +public class PreventAllDamageToEffect extends PreventionEffectImpl { protected Filter filter; @@ -49,20 +50,30 @@ public class PreventAllDamageToEffect extends PreventionEffectImpl { this.filter = filter; } + public PreventAllDamageToEffect(final PreventAllDamageToEffect effect) { + super(effect); + this.filter = effect.filter.copy(); + } + @Override - public boolean apply(Game game) { + public PreventAllDamageToEffect copy() { + return new PreventAllDamageToEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { return true; } @Override - public boolean replaceEvent(GameEvent event, Game game) { + public boolean replaceEvent(GameEvent event, Ability source, Game game) { event.setAmount(0); return false; } @Override - public boolean applies(GameEvent event, Game game) { - if (super.applies(event, game)) { + public boolean applies(GameEvent event, Ability source, Game game) { + if (super.applies(event, source, game)) { Permanent permanent = game.getPermanent(event.getTargetId()); if (permanent != null) { if (filter.match(permanent)) @@ -78,7 +89,7 @@ public class PreventAllDamageToEffect extends PreventionEffectImpl { } @Override - public String getText() { + public String getText(Ability source) { return "Prevent all damage that would be dealt to " + filter.getMessage() + " " + duration.toString(); } diff --git a/Mage/src/mage/abilities/effects/common/PreventDamageTargetEffect.java b/Mage/src/mage/abilities/effects/common/PreventDamageTargetEffect.java index 06e62f1254b..3264c5e54d7 100644 --- a/Mage/src/mage/abilities/effects/common/PreventDamageTargetEffect.java +++ b/Mage/src/mage/abilities/effects/common/PreventDamageTargetEffect.java @@ -29,6 +29,7 @@ package mage.abilities.effects.common; import mage.Constants.Duration; +import mage.abilities.Ability; import mage.abilities.effects.PreventionEffectImpl; import mage.game.Game; import mage.game.events.GameEvent; @@ -37,7 +38,7 @@ import mage.game.events.GameEvent; * * @author BetaSteward_at_googlemail.com */ -public class PreventDamageTargetEffect extends PreventionEffectImpl { +public class PreventDamageTargetEffect extends PreventionEffectImpl { private int amount; @@ -46,13 +47,23 @@ public class PreventDamageTargetEffect extends PreventionEffectImpl { this.amount = amount; } + public PreventDamageTargetEffect(final PreventDamageTargetEffect effect) { + super(effect); + this.amount = effect.amount; + } + @Override - public boolean apply(Game game) { + public PreventDamageTargetEffect copy() { + return new PreventDamageTargetEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { return true; } @Override - public boolean replaceEvent(GameEvent event, Game game) { + public boolean replaceEvent(GameEvent event, Ability source, Game game) { if (event.getAmount() >= this.amount) { event.setAmount(event.getAmount() - amount); this.used = true; @@ -65,8 +76,8 @@ public class PreventDamageTargetEffect extends PreventionEffectImpl { } @Override - public boolean applies(GameEvent event, Game game) { - if (!this.used && super.applies(event, game) && event.getTargetId().equals(this.getSource().getFirstTarget())) { + public boolean applies(GameEvent event, Ability source, Game game) { + if (!this.used && super.applies(event, source, game) && event.getTargetId().equals(source.getFirstTarget())) { return true; } return false; diff --git a/Mage/src/mage/abilities/effects/common/PutLibraryIntoGraveTargetEffect.java b/Mage/src/mage/abilities/effects/common/PutLibraryIntoGraveTargetEffect.java index 5b2b0970a1d..2f495b55ac3 100644 --- a/Mage/src/mage/abilities/effects/common/PutLibraryIntoGraveTargetEffect.java +++ b/Mage/src/mage/abilities/effects/common/PutLibraryIntoGraveTargetEffect.java @@ -29,6 +29,7 @@ package mage.abilities.effects.common; import mage.Constants.Outcome; +import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.Card; import mage.game.Game; @@ -38,7 +39,7 @@ import mage.players.Player; * * @author BetaSteward_at_googlemail.com */ -public class PutLibraryIntoGraveTargetEffect extends OneShotEffect { +public class PutLibraryIntoGraveTargetEffect extends OneShotEffect { private int amount; @@ -47,9 +48,19 @@ public class PutLibraryIntoGraveTargetEffect extends OneShotEffect { this.amount = amount; } + public PutLibraryIntoGraveTargetEffect(final PutLibraryIntoGraveTargetEffect effect) { + super(effect); + this.amount = effect.amount; + } + @Override - public boolean apply(Game game) { - Player player = game.getPlayer(this.source.getFirstTarget()); + public PutLibraryIntoGraveTargetEffect copy() { + return new PutLibraryIntoGraveTargetEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getFirstTarget()); Card card; for (int i = 0; i < amount; i++) { card = player.getLibrary().removeFromTop(game); @@ -62,9 +73,9 @@ public class PutLibraryIntoGraveTargetEffect extends OneShotEffect { } @Override - public String getText() { + public String getText(Ability source) { StringBuilder sb = new StringBuilder(); - sb.append("Target ").append(this.getSource().getTargets().get(0).getTargetName()); + sb.append("Target ").append(source.getTargets().get(0).getTargetName()); sb.append(" puts the top ").append(amount).append("cards of his or her library into his or her graveyard."); return sb.toString(); } diff --git a/Mage/src/mage/abilities/effects/common/RegenerateSourceEffect.java b/Mage/src/mage/abilities/effects/common/RegenerateSourceEffect.java new file mode 100644 index 00000000000..ddc38f068a0 --- /dev/null +++ b/Mage/src/mage/abilities/effects/common/RegenerateSourceEffect.java @@ -0,0 +1,90 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.abilities.effects.common; + +import mage.Constants.Duration; +import mage.Constants.Outcome; +import mage.abilities.Ability; +import mage.abilities.effects.ReplacementEffect; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; +import mage.game.permanent.Permanent; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class RegenerateSourceEffect extends ReplacementEffectImpl { + + public RegenerateSourceEffect() { + super(Duration.EndOfTurn, Outcome.Regenerate); + } + + public RegenerateSourceEffect(final RegenerateSourceEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getSourceId()); + if (permanent != null) { + permanent.setTapped(true); + permanent.removeFromCombat(game); + permanent.removeAllDamage(game); + return true; + } + return false; + } + + @Override + public RegenerateSourceEffect copy() { + return new RegenerateSourceEffect(this); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + return apply(game, source); + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + if (event.getType() == EventType.DESTROY_PERMANENT && event.getAmount() == 0 && event.getTargetId().equals(source.getSourceId())) { + return true; + } + return false; + } + + @Override + public String getText(Ability source) { + return "Regenerate {this}"; + } + +} diff --git a/Mage/src/mage/abilities/effects/common/RemoveDelayedTriggeredAbilityEffect.java b/Mage/src/mage/abilities/effects/common/RemoveDelayedTriggeredAbilityEffect.java index 56c3eee2fa0..07e50110735 100644 --- a/Mage/src/mage/abilities/effects/common/RemoveDelayedTriggeredAbilityEffect.java +++ b/Mage/src/mage/abilities/effects/common/RemoveDelayedTriggeredAbilityEffect.java @@ -30,7 +30,7 @@ package mage.abilities.effects.common; import java.util.UUID; import mage.Constants.Outcome; -import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.game.Game; @@ -38,7 +38,7 @@ import mage.game.Game; * * @author BetaSteward_at_googlemail.com */ -public class RemoveDelayedTriggeredAbilityEffect extends OneShotEffect { +public class RemoveDelayedTriggeredAbilityEffect extends OneShotEffect { protected UUID abilityId; @@ -47,14 +47,24 @@ public class RemoveDelayedTriggeredAbilityEffect extends OneShotEffect { this.abilityId = abilityId; } + public RemoveDelayedTriggeredAbilityEffect(final RemoveDelayedTriggeredAbilityEffect effect) { + super(effect); + this.abilityId = effect.abilityId; + } + @Override - public boolean apply(Game game) { + public RemoveDelayedTriggeredAbilityEffect copy() { + return new RemoveDelayedTriggeredAbilityEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { game.getState().removeDelayedTriggeredAbility(abilityId); return true; } @Override - public String getText() { + public String getText(Ability source) { //TODO: improve this return "remove triggered ability"; } diff --git a/Mage/src/mage/abilities/effects/common/RemoveSpecialActionEffect.java b/Mage/src/mage/abilities/effects/common/RemoveSpecialActionEffect.java index a52a1df22f8..ecd6a65cadb 100644 --- a/Mage/src/mage/abilities/effects/common/RemoveSpecialActionEffect.java +++ b/Mage/src/mage/abilities/effects/common/RemoveSpecialActionEffect.java @@ -30,6 +30,7 @@ package mage.abilities.effects.common; import java.util.UUID; import mage.Constants.Outcome; +import mage.abilities.Ability; import mage.abilities.SpecialAction; import mage.abilities.effects.OneShotEffect; import mage.game.Game; @@ -38,7 +39,7 @@ import mage.game.Game; * * @author BetaSteward_at_googlemail.com */ -public class RemoveSpecialActionEffect extends OneShotEffect { +public class RemoveSpecialActionEffect extends OneShotEffect { protected UUID actionId; @@ -47,8 +48,18 @@ public class RemoveSpecialActionEffect extends OneShotEffect { this.actionId = actionId; } + public RemoveSpecialActionEffect(final RemoveSpecialActionEffect effect) { + super(effect); + this.actionId = effect.actionId; + } + @Override - public boolean apply(Game game) { + public RemoveSpecialActionEffect copy() { + return new RemoveSpecialActionEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { for (SpecialAction action: game.getState().getSpecialActions()) { if (action.getId().equals(actionId)) { game.getState().getSpecialActions().remove(action); diff --git a/Mage/src/mage/abilities/effects/common/ReturnFromExileEffect.java b/Mage/src/mage/abilities/effects/common/ReturnFromExileEffect.java index 290a629b14b..33db8cd25d8 100644 --- a/Mage/src/mage/abilities/effects/common/ReturnFromExileEffect.java +++ b/Mage/src/mage/abilities/effects/common/ReturnFromExileEffect.java @@ -31,6 +31,7 @@ package mage.abilities.effects.common; import java.util.UUID; import mage.Constants.Outcome; import mage.Constants.Zone; +import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.Card; import mage.game.Game; @@ -40,7 +41,7 @@ import mage.players.Player; * * @author BetaSteward_at_googlemail.com */ -public class ReturnFromExileEffect extends OneShotEffect { +public class ReturnFromExileEffect extends OneShotEffect { private UUID exileId; private Zone zone; @@ -51,9 +52,22 @@ public class ReturnFromExileEffect extends OneShotEffect { this.zone = zone; } - public boolean apply(Game game) { + public ReturnFromExileEffect(final ReturnFromExileEffect effect) { + super(effect); + this.exileId = effect.exileId; + this.zone = effect.zone; + } + + @Override + public ReturnFromExileEffect copy() { + return new ReturnFromExileEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { Player player; - for (Card card: game.getExile().getExileZone(exileId).values()) { + for (UUID cardId: game.getExile().getExileZone(exileId)) { + Card card = game.getCard(cardId); player = game.getPlayer(card.getOwnerId()); switch(zone) { case BATTLEFIELD: @@ -72,7 +86,7 @@ public class ReturnFromExileEffect extends OneShotEffect { } @Override - public String getText() { + public String getText(Ability source) { StringBuilder sb = new StringBuilder(); sb.append("return the exiled cards "); switch(zone) { diff --git a/Mage/src/mage/abilities/effects/common/ReturnSourceFromGraveyardToBattlefieldEffect.java b/Mage/src/mage/abilities/effects/common/ReturnSourceFromGraveyardToBattlefieldEffect.java new file mode 100644 index 00000000000..5096cc41c94 --- /dev/null +++ b/Mage/src/mage/abilities/effects/common/ReturnSourceFromGraveyardToBattlefieldEffect.java @@ -0,0 +1,75 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.abilities.effects.common; + +import mage.Constants.Outcome; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +import mage.game.Game; +import mage.players.Player; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class ReturnSourceFromGraveyardToBattlefieldEffect extends OneShotEffect { + + public ReturnSourceFromGraveyardToBattlefieldEffect() { + super(Outcome.PutCreatureInPlay); + } + + public ReturnSourceFromGraveyardToBattlefieldEffect(final ReturnSourceFromGraveyardToBattlefieldEffect effect) { + super(effect); + } + + @Override + public ReturnSourceFromGraveyardToBattlefieldEffect copy() { + return new ReturnSourceFromGraveyardToBattlefieldEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Card card = player.getGraveyard().get(source.getSourceId(), game); + if (card != null) { + player.putOntoBattlefield(card, game); + player.removeFromGraveyard(card, game); + return true; + } + return false; + } + + @Override + public String getText(Ability source) { + return "Return {this} from your graveyard to the battlefield"; + } + + +} diff --git a/Mage/src/mage/abilities/effects/common/ReturnToHandTargetEffect.java b/Mage/src/mage/abilities/effects/common/ReturnToHandTargetEffect.java index 9ca529ec696..bfcf1320bd2 100644 --- a/Mage/src/mage/abilities/effects/common/ReturnToHandTargetEffect.java +++ b/Mage/src/mage/abilities/effects/common/ReturnToHandTargetEffect.java @@ -30,6 +30,7 @@ package mage.abilities.effects.common; import mage.Constants.Outcome; import mage.Constants.Zone; +import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.game.Game; import mage.game.permanent.Permanent; @@ -38,15 +39,24 @@ import mage.game.permanent.Permanent; * * @author BetaSteward_at_googlemail.com */ -public class ReturnToHandTargetEffect extends OneShotEffect { +public class ReturnToHandTargetEffect extends OneShotEffect { public ReturnToHandTargetEffect() { super(Outcome.ReturnToHand); } + public ReturnToHandTargetEffect(final ReturnToHandTargetEffect effect) { + super(effect); + } + @Override - public boolean apply(Game game) { - Permanent permanent = game.getPermanent(this.source.getFirstTarget()); + public ReturnToHandTargetEffect copy() { + return new ReturnToHandTargetEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); if (permanent != null) { return permanent.moveToZone(Zone.HAND, game, false); } @@ -54,8 +64,8 @@ public class ReturnToHandTargetEffect extends OneShotEffect { } @Override - public String getText() { - return "Return target " + this.source.getTargets().get(0).getTargetName() + " to it's owner's hand"; + public String getText(Ability source) { + return "Return target " + source.getTargets().get(0).getTargetName() + " to it's owner's hand"; } } diff --git a/Mage/src/mage/abilities/effects/common/SacrificeSourceEffect.java b/Mage/src/mage/abilities/effects/common/SacrificeSourceEffect.java index 083ec35ca37..581025a2424 100644 --- a/Mage/src/mage/abilities/effects/common/SacrificeSourceEffect.java +++ b/Mage/src/mage/abilities/effects/common/SacrificeSourceEffect.java @@ -29,6 +29,7 @@ package mage.abilities.effects.common; import mage.Constants.Outcome; +import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.game.Game; import mage.game.permanent.Permanent; @@ -37,23 +38,32 @@ import mage.game.permanent.Permanent; * * @author BetaSteward_at_googlemail.com */ -public class SacrificeSourceEffect extends OneShotEffect { +public class SacrificeSourceEffect extends OneShotEffect { public SacrificeSourceEffect() { super(Outcome.Sacrifice); } + public SacrificeSourceEffect(final SacrificeSourceEffect effect) { + super(effect); + } + @Override - public boolean apply(Game game) { - Permanent permanent = game.getPermanent(this.source.getSourceId()); + public SacrificeSourceEffect copy() { + return new SacrificeSourceEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getSourceId()); if (permanent != null) { - return permanent.sacrifice(this.source.getSourceId(), game); + return permanent.sacrifice(source.getSourceId(), game); } return false; } @Override - public String getText() { + public String getText(Ability source) { return "sacrifice {this}"; } diff --git a/Mage/src/mage/abilities/effects/common/SearchLibraryPutInHandEffect.java b/Mage/src/mage/abilities/effects/common/SearchLibraryPutInHandEffect.java index d978295668c..8f5a69c76a7 100644 --- a/Mage/src/mage/abilities/effects/common/SearchLibraryPutInHandEffect.java +++ b/Mage/src/mage/abilities/effects/common/SearchLibraryPutInHandEffect.java @@ -28,10 +28,11 @@ package mage.abilities.effects.common; +import java.util.List; import java.util.UUID; import mage.Constants.Outcome; import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.SearchEffect; import mage.cards.Card; import mage.game.Game; import mage.players.Player; @@ -39,24 +40,30 @@ import mage.target.common.TargetCardInLibrary; /** * - * @author LokiX + * @author LokiX, BetaSteward_at_googlemail.com */ -public class SearchLibraryPutInHandEffect extends OneShotEffect { - - private TargetCardInLibrary target; +public class SearchLibraryPutInHandEffect extends SearchEffect { public SearchLibraryPutInHandEffect(TargetCardInLibrary target) { - super(Outcome.DrawCard); - this.target = target; + super(target, Outcome.DrawCard); } + public SearchLibraryPutInHandEffect(final SearchLibraryPutInHandEffect effect) { + super(effect); + } + + @Override + public SearchLibraryPutInHandEffect copy() { + return new SearchLibraryPutInHandEffect(this); + } + @Override - public boolean apply(Game game) { - Player player = game.getPlayer(this.source.getControllerId()); + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); player.searchLibrary(target, game); if (target.getTargets().size() > 0) { - for (UUID cardId: target.getTargets()) { - Card card = player.getLibrary().remove(cardId); + for (UUID cardId: (List)target.getTargets()) { + Card card = player.getLibrary().remove(cardId, game); if (card != null){ player.putInHand(card, game); } @@ -67,7 +74,7 @@ public class SearchLibraryPutInHandEffect extends OneShotEffect { } @Override - public String getText() { + public String getText(Ability source) { StringBuilder sb = new StringBuilder(); sb.append("Search your library for "); if (target.getNumberOfTargets() == 0 && target.getMaxNumberOfTargets() > 0) { @@ -82,9 +89,9 @@ public class SearchLibraryPutInHandEffect extends OneShotEffect { return sb.toString(); } - @Override - public void setSource(Ability ability) { - super.setSource(ability); - target.setAbility(ability); - } +// @Override +// public void setSource(Ability ability) { +// super.setSource(ability); +// target.setAbility(ability); +// } } diff --git a/Mage/src/mage/abilities/effects/common/SearchLibraryPutInPlayEffect.java b/Mage/src/mage/abilities/effects/common/SearchLibraryPutInPlayEffect.java index 07f46071ed7..39fe67c17f0 100644 --- a/Mage/src/mage/abilities/effects/common/SearchLibraryPutInPlayEffect.java +++ b/Mage/src/mage/abilities/effects/common/SearchLibraryPutInPlayEffect.java @@ -28,10 +28,11 @@ package mage.abilities.effects.common; +import java.util.List; import java.util.UUID; import mage.Constants.Outcome; import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.SearchEffect; import mage.cards.Card; import mage.game.Game; import mage.game.permanent.Permanent; @@ -42,9 +43,8 @@ import mage.target.common.TargetCardInLibrary; * * @author BetaSteward_at_googlemail.com */ -public class SearchLibraryPutInPlayEffect extends OneShotEffect { +public class SearchLibraryPutInPlayEffect extends SearchEffect { - private TargetCardInLibrary target; private boolean tapped; public SearchLibraryPutInPlayEffect(TargetCardInLibrary target) { @@ -56,18 +56,27 @@ public class SearchLibraryPutInPlayEffect extends OneShotEffect { } public SearchLibraryPutInPlayEffect(TargetCardInLibrary target, boolean tapped, Outcome outcome) { - super(outcome); - this.target = target; + super(target, outcome); this.tapped = tapped; } + public SearchLibraryPutInPlayEffect(final SearchLibraryPutInPlayEffect effect) { + super(effect); + this.tapped = effect.tapped; + } + @Override - public boolean apply(Game game) { - Player player = game.getPlayer(this.source.getControllerId()); + public SearchLibraryPutInPlayEffect copy() { + return new SearchLibraryPutInPlayEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); if (player.searchLibrary(target, game)) { if (target.getTargets().size() > 0) { - for (UUID cardId: target.getTargets()) { - Card card = player.getLibrary().remove(cardId); + for (UUID cardId: (List)target.getTargets()) { + Card card = player.getLibrary().remove(cardId, game); if (card != null) { if (player.putOntoBattlefield(card, game)) { if (tapped) { @@ -86,7 +95,7 @@ public class SearchLibraryPutInPlayEffect extends OneShotEffect { } @Override - public String getText() { + public String getText(Ability source) { StringBuilder sb = new StringBuilder(); sb.append("Search your library for "); if (target.getNumberOfTargets() == 0 && target.getMaxNumberOfTargets() > 0) { @@ -102,10 +111,10 @@ public class SearchLibraryPutInPlayEffect extends OneShotEffect { return sb.toString(); } - @Override - public void setSource(Ability ability) { - super.setSource(ability); - target.setAbility(ability); - } +// @Override +// public void setSource(Ability ability) { +// super.setSource(ability); +// target.setAbility(ability); +// } } diff --git a/Mage/src/mage/abilities/effects/common/SearchLibraryRevealPutInHandEffect.java b/Mage/src/mage/abilities/effects/common/SearchLibraryRevealPutInHandEffect.java index a3e702b9fca..4f939e313f9 100644 --- a/Mage/src/mage/abilities/effects/common/SearchLibraryRevealPutInHandEffect.java +++ b/Mage/src/mage/abilities/effects/common/SearchLibraryRevealPutInHandEffect.java @@ -28,11 +28,12 @@ package mage.abilities.effects.common; +import java.util.List; import java.util.UUID; import mage.Constants.Outcome; import mage.Constants.Zone; import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.SearchEffect; import mage.cards.Card; import mage.cards.Cards; import mage.cards.CardsImpl; @@ -44,23 +45,29 @@ import mage.target.common.TargetCardInLibrary; * * @author BetaSteward_at_googlemail.com */ -public class SearchLibraryRevealPutInHandEffect extends OneShotEffect { - - private TargetCardInLibrary target; +public class SearchLibraryRevealPutInHandEffect extends SearchEffect { public SearchLibraryRevealPutInHandEffect(TargetCardInLibrary target) { - super(Outcome.DrawCard); - this.target = target; + super(target, Outcome.DrawCard); + } + + public SearchLibraryRevealPutInHandEffect(final SearchLibraryRevealPutInHandEffect effect) { + super(effect); } @Override - public boolean apply(Game game) { - Player player = game.getPlayer(this.source.getControllerId()); + public SearchLibraryRevealPutInHandEffect copy() { + return new SearchLibraryRevealPutInHandEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); player.searchLibrary(target, game); if (target.getTargets().size() > 0) { - Cards revealed = new CardsImpl(Zone.REVEALED); - for (UUID cardId: target.getTargets()) { - Card card = player.getLibrary().remove(cardId); + Cards revealed = new CardsImpl(); + for (UUID cardId: (List)target.getTargets()) { + Card card = player.getLibrary().remove(cardId, game); if (card != null) { player.putInHand(card, game); revealed.add(card); @@ -73,7 +80,7 @@ public class SearchLibraryRevealPutInHandEffect extends OneShotEffect { } @Override - public String getText() { + public String getText(Ability source) { StringBuilder sb = new StringBuilder(); sb.append("Search your library for "); if (target.getNumberOfTargets() == 0 && target.getMaxNumberOfTargets() > 0) { @@ -88,10 +95,10 @@ public class SearchLibraryRevealPutInHandEffect extends OneShotEffect { return sb.toString(); } - @Override - public void setSource(Ability ability) { - super.setSource(ability); - target.setAbility(ability); - } +// @Override +// public void setSource(Ability ability) { +// super.setSource(ability); +// target.setAbility(ability); +// } } diff --git a/Mage/src/mage/abilities/effects/common/SkipNextUntapTargetEffect.java b/Mage/src/mage/abilities/effects/common/SkipNextUntapTargetEffect.java index c98edebc4c2..a80952953d1 100644 --- a/Mage/src/mage/abilities/effects/common/SkipNextUntapTargetEffect.java +++ b/Mage/src/mage/abilities/effects/common/SkipNextUntapTargetEffect.java @@ -31,6 +31,7 @@ package mage.abilities.effects.common; import mage.Constants.Duration; import mage.Constants.Outcome; import mage.Constants.PhaseStep; +import mage.abilities.Ability; import mage.abilities.effects.ReplacementEffectImpl; import mage.game.Game; import mage.game.events.GameEvent; @@ -40,25 +41,34 @@ import mage.game.events.GameEvent.EventType; * * @author BetaSteward_at_googlemail.com */ -public class SkipNextUntapTargetEffect extends ReplacementEffectImpl { +public class SkipNextUntapTargetEffect extends ReplacementEffectImpl { public SkipNextUntapTargetEffect() { super(Duration.OneUse, Outcome.Detriment); } + public SkipNextUntapTargetEffect(final SkipNextUntapTargetEffect effect) { + super(effect); + } + @Override - public boolean apply(Game game) { + public SkipNextUntapTargetEffect copy() { + return new SkipNextUntapTargetEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { return false; } @Override - public boolean replaceEvent(GameEvent event, Game game) { + public boolean replaceEvent(GameEvent event, Ability source, Game game) { used = true; return true; } @Override - public boolean applies(GameEvent event, Game game) { + public boolean applies(GameEvent event, Ability source, Game game) { if (game.getTurn().getStepType() == PhaseStep.UNTAP && event.getType() == EventType.UNTAP && event.getTargetId().equals(source.getFirstTarget())) { @@ -68,7 +78,7 @@ public class SkipNextUntapTargetEffect extends ReplacementEffectImpl { } @Override - public String getText() { + public String getText(Ability source) { return "Target " + source.getTargets().get(0).getTargetName() + " doesn't untap during its controller's next untap step"; } diff --git a/Mage/src/mage/abilities/effects/common/TapSourceEffect.java b/Mage/src/mage/abilities/effects/common/TapSourceEffect.java index 2f92580d26e..0f7b091da15 100644 --- a/Mage/src/mage/abilities/effects/common/TapSourceEffect.java +++ b/Mage/src/mage/abilities/effects/common/TapSourceEffect.java @@ -29,6 +29,7 @@ package mage.abilities.effects.common; import mage.Constants.Outcome; +import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.game.Game; import mage.game.permanent.Permanent; @@ -37,15 +38,24 @@ import mage.game.permanent.Permanent; * * @author BetaSteward_at_googlemail.com */ -public class TapSourceEffect extends OneShotEffect { +public class TapSourceEffect extends OneShotEffect { public TapSourceEffect() { super(Outcome.Tap); } + public TapSourceEffect(final TapSourceEffect effect) { + super(effect); + } + @Override - public boolean apply(Game game) { - Permanent permanent = game.getPermanent(this.source.getSourceId()); + public TapSourceEffect copy() { + return new TapSourceEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getSourceId()); if (permanent != null) { permanent.setTapped(true); return true; @@ -54,7 +64,7 @@ public class TapSourceEffect extends OneShotEffect { } @Override - public String getText() { + public String getText(Ability source) { return "tap {this}"; } diff --git a/Mage/src/mage/abilities/effects/common/TapTargetEffect.java b/Mage/src/mage/abilities/effects/common/TapTargetEffect.java index 1abbd569043..8d6458566e0 100644 --- a/Mage/src/mage/abilities/effects/common/TapTargetEffect.java +++ b/Mage/src/mage/abilities/effects/common/TapTargetEffect.java @@ -30,6 +30,7 @@ package mage.abilities.effects.common; import java.util.UUID; import mage.Constants.Outcome; +import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.game.Game; import mage.game.permanent.Permanent; @@ -39,15 +40,24 @@ import mage.target.Target; * * @author BetaSteward_at_googlemail.com */ -public class TapTargetEffect extends OneShotEffect { +public class TapTargetEffect extends OneShotEffect { public TapTargetEffect() { super(Outcome.Tap); } + public TapTargetEffect(final TapTargetEffect effect) { + super(effect); + } + @Override - public boolean apply(Game game) { - for (UUID target: this.source.getTargets().get(0).getTargets()) { + public TapTargetEffect copy() { + return new TapTargetEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + for (UUID target: source.getTargets().get(0).getTargets()) { Permanent permanent = game.getPermanent(target); if (permanent != null) { permanent.setTapped(true); @@ -60,12 +70,12 @@ public class TapTargetEffect extends OneShotEffect { } @Override - public String getText() { - Target target = this.source.getTargets().get(0); + public String getText(Ability source) { + Target target = source.getTargets().get(0); if (target.getNumberOfTargets() > 1) - return "tap " + target.getNumberOfTargets() + " target " + this.source.getTargets().get(0).getTargetName() + "s"; + return "tap " + target.getNumberOfTargets() + " target " + source.getTargets().get(0).getTargetName() + "s"; else - return "tap target " + this.source.getTargets().get(0).getTargetName(); + return "tap target " + source.getTargets().get(0).getTargetName(); } } diff --git a/Mage/src/mage/abilities/effects/common/UntapTargetEffect.java b/Mage/src/mage/abilities/effects/common/UntapTargetEffect.java index ce61310a646..51ab8673067 100644 --- a/Mage/src/mage/abilities/effects/common/UntapTargetEffect.java +++ b/Mage/src/mage/abilities/effects/common/UntapTargetEffect.java @@ -30,6 +30,7 @@ package mage.abilities.effects.common; import java.util.UUID; import mage.Constants.Outcome; +import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.game.Game; import mage.game.permanent.Permanent; @@ -39,15 +40,24 @@ import mage.target.Target; * * @author BetaSteward_at_googlemail.com */ -public class UntapTargetEffect extends OneShotEffect { +public class UntapTargetEffect extends OneShotEffect { public UntapTargetEffect() { super(Outcome.Untap); } + public UntapTargetEffect(final UntapTargetEffect effect) { + super(effect); + } + @Override - public boolean apply(Game game) { - for (UUID target: this.source.getTargets().get(0).getTargets()) { + public UntapTargetEffect copy() { + return new UntapTargetEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + for (UUID target: source.getTargets().get(0).getTargets()) { Permanent permanent = game.getPermanent(target); if (permanent != null) { permanent.setTapped(false); @@ -60,12 +70,12 @@ public class UntapTargetEffect extends OneShotEffect { } @Override - public String getText() { - Target target = this.source.getTargets().get(0); + public String getText(Ability source) { + Target target = source.getTargets().get(0); if (target.getNumberOfTargets() > 1) - return "untap " + target.getNumberOfTargets() + " target " + this.source.getTargets().get(0).getTargetName() + "s"; + return "untap " + target.getNumberOfTargets() + " target " + source.getTargets().get(0).getTargetName() + "s"; else - return "untap target " + this.source.getTargets().get(0).getTargetName(); + return "untap target " + source.getTargets().get(0).getTargetName(); } } diff --git a/Mage/src/mage/abilities/keyword/CascadeAbility.java b/Mage/src/mage/abilities/keyword/CascadeAbility.java index 27db22f65ce..1491253e2d5 100644 --- a/Mage/src/mage/abilities/keyword/CascadeAbility.java +++ b/Mage/src/mage/abilities/keyword/CascadeAbility.java @@ -31,6 +31,7 @@ package mage.abilities.keyword; import mage.Constants.CardType; import mage.Constants.Outcome; import mage.Constants.Zone; +import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.OneShotEffect; import mage.cards.Card; @@ -44,13 +45,17 @@ import mage.players.Player; * * @author BetaSteward_at_googlemail.com */ -public class CascadeAbility extends TriggeredAbilityImpl { +public class CascadeAbility extends TriggeredAbilityImpl { //20091005 - 702.82 public CascadeAbility() { super(Zone.STACK, new CascadeEffect()); } + public CascadeAbility(CascadeAbility ability) { + super(ability); + } + @Override public boolean checkTrigger(GameEvent event, Game game) { if (event.getType() == EventType.SPELL_CAST && event.getTargetId().equals(this.getSourceId()) ) { @@ -64,20 +69,29 @@ public class CascadeAbility extends TriggeredAbilityImpl { public String getRule() { return "Cascade"; } + + @Override + public CascadeAbility copy() { + return new CascadeAbility(this); + } } -class CascadeEffect extends OneShotEffect { +class CascadeEffect extends OneShotEffect { public CascadeEffect() { super(Outcome.PutCardInPlay); } + public CascadeEffect(CascadeEffect effect) { + super(effect); + } + @Override - public boolean apply(Game game) { + public boolean apply(Game game, Ability source) { Card card; - Player player = game.getPlayer(this.getSource().getControllerId()); - ExileZone exile = game.getExile().createZone(this.source.getSourceId(), player.getName() + " " + this.getSource().getName() + " Cascade"); - int sourceCost = game.getObject(this.source.getSourceId()).getManaCost().convertedManaCost(); + Player player = game.getPlayer(source.getControllerId()); + ExileZone exile = game.getExile().createZone(source.getSourceId(), player.getName() + " Cascade"); + int sourceCost = game.getObject(source.getSourceId()).getManaCost().convertedManaCost(); do { card = player.getLibrary().removeFromTop(game); if (card == null) @@ -93,7 +107,7 @@ class CascadeEffect extends OneShotEffect { } while (exile.size() > 0) { - card = exile.getRandom(); + card = exile.getRandom(game); exile.remove(card.getId()); player.getLibrary().putOnBottom(card, game); } @@ -101,4 +115,9 @@ class CascadeEffect extends OneShotEffect { return true; } + @Override + public CascadeEffect copy() { + return new CascadeEffect(this); + } + } \ No newline at end of file diff --git a/Mage/src/mage/abilities/keyword/CyclingAbility.java b/Mage/src/mage/abilities/keyword/CyclingAbility.java index efd2f08c1c4..168e1568aed 100644 --- a/Mage/src/mage/abilities/keyword/CyclingAbility.java +++ b/Mage/src/mage/abilities/keyword/CyclingAbility.java @@ -30,6 +30,7 @@ package mage.abilities.keyword; import mage.Constants.Outcome; import mage.Constants.Zone; +import mage.abilities.Ability; import mage.abilities.ActivatedAbilityImpl; import mage.abilities.costs.common.DiscardSourceCost; import mage.abilities.costs.mana.ManaCosts; @@ -42,13 +43,22 @@ import mage.players.Player; * * @author BetaSteward_at_googlemail.com */ -public class CyclingAbility extends ActivatedAbilityImpl { +public class CyclingAbility extends ActivatedAbilityImpl { public CyclingAbility(ManaCosts costs) { super(Zone.HAND, new CycleEffect(), costs); this.addCost(new DiscardSourceCost()); } + public CyclingAbility(final CyclingAbility ability) { + super(ability); + } + + @Override + public CyclingAbility copy() { + return new CyclingAbility(this); + } + @Override public String getRule() { return "Cycling " + super.getRule(); @@ -56,21 +66,30 @@ public class CyclingAbility extends ActivatedAbilityImpl { } -class CycleEffect extends OneShotEffect { +class CycleEffect extends OneShotEffect { public CycleEffect() { super(Outcome.DrawCard); } + public CycleEffect(final CycleEffect effect) { + super(effect); + } + @Override - public boolean apply(Game game) { - Player player = game.getPlayer(this.source.getControllerId()); + public CycleEffect copy() { + return new CycleEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); player.drawCards(1, game); return true; } @Override - public String getText() { + public String getText(Ability source) { return "Draw a card"; } diff --git a/Mage/src/mage/abilities/keyword/DeathtouchAbility.java b/Mage/src/mage/abilities/keyword/DeathtouchAbility.java index 94fdd4565ee..33f0e485b38 100644 --- a/Mage/src/mage/abilities/keyword/DeathtouchAbility.java +++ b/Mage/src/mage/abilities/keyword/DeathtouchAbility.java @@ -36,7 +36,7 @@ import mage.abilities.StaticAbility; * * @author BetaSteward_at_googlemail.com */ -public class DeathtouchAbility extends StaticAbility { +public class DeathtouchAbility extends StaticAbility { private static final DeathtouchAbility fINSTANCE = new DeathtouchAbility(); @@ -57,4 +57,11 @@ public class DeathtouchAbility extends StaticAbility { return "Deathtouch"; } + @Override + public DeathtouchAbility copy() { + return fINSTANCE; + } + + + } diff --git a/Mage/src/mage/abilities/keyword/DefenderAbility.java b/Mage/src/mage/abilities/keyword/DefenderAbility.java index c0edd13a973..5b5a9a1c1bb 100644 --- a/Mage/src/mage/abilities/keyword/DefenderAbility.java +++ b/Mage/src/mage/abilities/keyword/DefenderAbility.java @@ -36,7 +36,7 @@ import mage.abilities.StaticAbility; * * @author BetaSteward_at_googlemail.com */ -public class DefenderAbility extends StaticAbility { +public class DefenderAbility extends StaticAbility { private static final DefenderAbility fINSTANCE = new DefenderAbility(); @@ -57,4 +57,9 @@ public class DefenderAbility extends StaticAbility { return "Defender"; } + @Override + public DefenderAbility copy() { + return fINSTANCE; + } + } diff --git a/Mage/src/mage/abilities/keyword/DoubleStrikeAbility.java b/Mage/src/mage/abilities/keyword/DoubleStrikeAbility.java index b4bfcc14965..9eb38fdf399 100644 --- a/Mage/src/mage/abilities/keyword/DoubleStrikeAbility.java +++ b/Mage/src/mage/abilities/keyword/DoubleStrikeAbility.java @@ -36,7 +36,7 @@ import mage.abilities.StaticAbility; * * @author BetaSteward_at_googlemail.com */ -public class DoubleStrikeAbility extends StaticAbility { +public class DoubleStrikeAbility extends StaticAbility { private static final DoubleStrikeAbility fINSTANCE = new DoubleStrikeAbility(); @@ -57,4 +57,9 @@ public class DoubleStrikeAbility extends StaticAbility { return "Double Strike"; } + @Override + public DoubleStrikeAbility copy() { + return fINSTANCE; + } + } \ No newline at end of file diff --git a/Mage/src/mage/abilities/keyword/EnchantAbility.java b/Mage/src/mage/abilities/keyword/EnchantAbility.java index 5c00e0a6b6b..4018e7bde02 100644 --- a/Mage/src/mage/abilities/keyword/EnchantAbility.java +++ b/Mage/src/mage/abilities/keyword/EnchantAbility.java @@ -38,13 +38,22 @@ import mage.target.TargetPermanent; * * @author BetaSteward_at_googlemail.com */ -public class EnchantAbility extends StaticAbility { +public class EnchantAbility extends StaticAbility { public EnchantAbility(Outcome outcome, TargetPermanent target) { super(Zone.BATTLEFIELD, new AttachEffect(outcome)); this.addTarget(target); } + public EnchantAbility(final EnchantAbility ability) { + super(ability); + } + + @Override + public EnchantAbility copy() { + return new EnchantAbility(this); + } + @Override public String getRule() { return "Enchant " + this.getTargets().get(0).getTargetName(); diff --git a/Mage/src/mage/abilities/keyword/EquipAbility.java b/Mage/src/mage/abilities/keyword/EquipAbility.java index c254f3d8b63..1c29da61e1a 100644 --- a/Mage/src/mage/abilities/keyword/EquipAbility.java +++ b/Mage/src/mage/abilities/keyword/EquipAbility.java @@ -41,7 +41,7 @@ import mage.target.common.TargetCreaturePermanent; * * @author BetaSteward_at_googlemail.com */ -public class EquipAbility extends ActivatedAbilityImpl { +public class EquipAbility extends ActivatedAbilityImpl { public EquipAbility(Outcome outcome, Cost cost) { super(Zone.BATTLEFIELD, new AttachEffect(outcome), cost); @@ -49,6 +49,15 @@ public class EquipAbility extends ActivatedAbilityImpl { this.timing = TimingRule.SORCERY; } + public EquipAbility(final EquipAbility ability) { + super(ability); + } + + @Override + public EquipAbility copy() { + return new EquipAbility(this); + } + @Override public String getRule() { return "Equip" + super.getRule(); diff --git a/Mage/src/mage/abilities/keyword/ExaltedAbility.java b/Mage/src/mage/abilities/keyword/ExaltedAbility.java index e64e159c41c..143d8dfd6ff 100644 --- a/Mage/src/mage/abilities/keyword/ExaltedAbility.java +++ b/Mage/src/mage/abilities/keyword/ExaltedAbility.java @@ -41,19 +41,28 @@ import mage.target.common.TargetCreaturePermanent; * * @author BetaSteward_at_googlemail.com */ -public class ExaltedAbility extends TriggeredAbilityImpl { +public class ExaltedAbility extends TriggeredAbilityImpl { public ExaltedAbility() { super(Zone.BATTLEFIELD, new BoostTargetEffect(1, 1, Duration.EndOfTurn)); } + public ExaltedAbility(final ExaltedAbility ability) { + super(ability); + } + + @Override + public ExaltedAbility copy() { + return new ExaltedAbility(this); + } + @Override public boolean checkTrigger(GameEvent event, Game game) { if (event.getType() == EventType.DECLARED_ATTACKERS && game.getActivePlayerId().equals(this.controllerId) ) { if (game.getCombat().attacksAlone()) { TargetCreaturePermanent target = new TargetCreaturePermanent(); this.addTarget(target); - this.getTargets().get(0).addTarget(game.getCombat().getAttackers().get(0), game); + this.getTargets().get(0).addTarget(game.getCombat().getAttackers().get(0), null, game); trigger(game, event.getPlayerId()); return true; } diff --git a/Mage/src/mage/abilities/keyword/FirstStrikeAbility.java b/Mage/src/mage/abilities/keyword/FirstStrikeAbility.java index 334386999b8..c3f1605ad58 100644 --- a/Mage/src/mage/abilities/keyword/FirstStrikeAbility.java +++ b/Mage/src/mage/abilities/keyword/FirstStrikeAbility.java @@ -36,7 +36,7 @@ import mage.abilities.StaticAbility; * * @author BetaSteward_at_googlemail.com */ -public class FirstStrikeAbility extends StaticAbility { +public class FirstStrikeAbility extends StaticAbility { private static final FirstStrikeAbility fINSTANCE = new FirstStrikeAbility(); @@ -57,4 +57,9 @@ public class FirstStrikeAbility extends StaticAbility { return "First Strike"; } + @Override + public FirstStrikeAbility copy() { + return fINSTANCE; + } + } \ No newline at end of file diff --git a/Mage/src/mage/abilities/keyword/FlashAbility.java b/Mage/src/mage/abilities/keyword/FlashAbility.java index cf986366a81..4dee70c1787 100644 --- a/Mage/src/mage/abilities/keyword/FlashAbility.java +++ b/Mage/src/mage/abilities/keyword/FlashAbility.java @@ -36,7 +36,7 @@ import mage.abilities.StaticAbility; * * @author BetaSteward_at_googlemail.com */ -public class FlashAbility extends StaticAbility { +public class FlashAbility extends StaticAbility { private static final FlashAbility fINSTANCE = new FlashAbility(); @@ -57,4 +57,9 @@ public class FlashAbility extends StaticAbility { return "Flash"; } + @Override + public FlashAbility copy() { + return fINSTANCE; + } + } diff --git a/Mage/src/mage/abilities/keyword/FlyingAbility.java b/Mage/src/mage/abilities/keyword/FlyingAbility.java index 779fa37f301..70889d34220 100644 --- a/Mage/src/mage/abilities/keyword/FlyingAbility.java +++ b/Mage/src/mage/abilities/keyword/FlyingAbility.java @@ -37,7 +37,7 @@ import mage.game.permanent.Permanent; * * @author BetaSteward_at_googlemail.com */ -public class FlyingAbility extends EvasionAbilityImpl { +public class FlyingAbility extends EvasionAbilityImpl { private static final FlyingAbility fINSTANCE = new FlyingAbility(); @@ -63,4 +63,9 @@ public class FlyingAbility extends EvasionAbilityImpl { return "Flying"; } + @Override + public FlyingAbility copy() { + return fINSTANCE; + } + } diff --git a/Mage/src/mage/abilities/keyword/FortifyAbility.java b/Mage/src/mage/abilities/keyword/FortifyAbility.java index 50888030912..792cbc845ac 100644 --- a/Mage/src/mage/abilities/keyword/FortifyAbility.java +++ b/Mage/src/mage/abilities/keyword/FortifyAbility.java @@ -40,7 +40,7 @@ import mage.target.common.TargetLandPermanent; * * @author BetaSteward_at_googlemail.com */ -public class FortifyAbility extends ActivatedAbilityImpl { +public class FortifyAbility extends ActivatedAbilityImpl { //20091005 - 702.64 public FortifyAbility(Zone zone, AttachEffect effect, Cost cost) { @@ -49,4 +49,14 @@ public class FortifyAbility extends ActivatedAbilityImpl { timing = TimingRule.SORCERY; } + public FortifyAbility(final FortifyAbility ability) { + super(ability); + } + + @Override + public FortifyAbility copy() { + return new FortifyAbility(this); + } + + } \ No newline at end of file diff --git a/Mage/src/mage/abilities/keyword/HasteAbility.java b/Mage/src/mage/abilities/keyword/HasteAbility.java index 3410dc1622a..778d9550b3d 100644 --- a/Mage/src/mage/abilities/keyword/HasteAbility.java +++ b/Mage/src/mage/abilities/keyword/HasteAbility.java @@ -36,7 +36,7 @@ import mage.abilities.StaticAbility; * * @author BetaSteward_at_googlemail.com */ -public class HasteAbility extends StaticAbility { +public class HasteAbility extends StaticAbility { private static final HasteAbility fINSTANCE = new HasteAbility(); @@ -57,4 +57,9 @@ public class HasteAbility extends StaticAbility { return "Haste"; } + @Override + public HasteAbility copy() { + return fINSTANCE; + } + } diff --git a/Mage/src/mage/abilities/keyword/IndestructibleAbility.java b/Mage/src/mage/abilities/keyword/IndestructibleAbility.java new file mode 100644 index 00000000000..a5de961e143 --- /dev/null +++ b/Mage/src/mage/abilities/keyword/IndestructibleAbility.java @@ -0,0 +1,65 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.abilities.keyword; + +import java.io.ObjectStreamException; +import mage.Constants.Zone; +import mage.abilities.StaticAbility; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class IndestructibleAbility extends StaticAbility { + + private static final IndestructibleAbility fINSTANCE = new IndestructibleAbility(); + + private Object readResolve() throws ObjectStreamException { + return fINSTANCE; + } + + public static IndestructibleAbility getInstance() { + return fINSTANCE; + } + + private IndestructibleAbility() { + super(Zone.BATTLEFIELD, null); + } + + @Override + public String getRule() { + return "Indestructible"; + } + + @Override + public IndestructibleAbility copy() { + return fINSTANCE; + } + +} diff --git a/Mage/src/mage/abilities/keyword/KickerAbility.java b/Mage/src/mage/abilities/keyword/KickerAbility.java index 9c23e98071d..9abadb793de 100644 --- a/Mage/src/mage/abilities/keyword/KickerAbility.java +++ b/Mage/src/mage/abilities/keyword/KickerAbility.java @@ -38,7 +38,7 @@ import mage.players.Player; * * @author BetaSteward_at_googlemail.com */ -public class KickerAbility extends StaticAbility { +public class KickerAbility extends StaticAbility { protected boolean kicked = false; protected boolean replaces = false; @@ -48,11 +48,21 @@ public class KickerAbility extends StaticAbility { this.replaces = replaces; } + public KickerAbility(final KickerAbility ability) { + super(ability); + this.kicked = ability.kicked; + this.replaces = ability.replaces; + } + + @Override + public KickerAbility copy() { + return new KickerAbility(this); + } + @Override public boolean activate(Game game, boolean noMana) { Player player = game.getPlayer(this.getControllerId()); - if (player.chooseUse(this.effects.get(0).getOutcome(), "Use kicker " + this.effects.get(0).getText() + "?", game)) { - game.saveState(); + if (player.chooseUse(this.effects.get(0).getOutcome(), "Use kicker " + this.effects.get(0).getText(this) + "?", game)) { game.bookmarkState(); if (super.activate(game, noMana)) { game.removeLastBookmark(); @@ -86,7 +96,7 @@ public class KickerAbility extends StaticAbility { } if (costs.size() > 0) sb.append(costs.getText()); - sb.append(":").append(effects.getText()); + sb.append(":").append(effects.getText(this)); if (replaces) sb.append(" instead"); return sb.toString(); diff --git a/Mage/src/mage/abilities/keyword/LandwalkAbility.java b/Mage/src/mage/abilities/keyword/LandwalkAbility.java index e472389c21e..febbfa27eeb 100644 --- a/Mage/src/mage/abilities/keyword/LandwalkAbility.java +++ b/Mage/src/mage/abilities/keyword/LandwalkAbility.java @@ -29,17 +29,15 @@ package mage.abilities.keyword; import mage.abilities.EvasionAbilityImpl; -import mage.filter.common.FilterLandCard; import mage.filter.common.FilterLandPermanent; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.players.Player; /** * * @author BetaSteward_at_googlemail.com */ -public class LandwalkAbility extends EvasionAbilityImpl { +public class LandwalkAbility extends EvasionAbilityImpl { FilterLandPermanent filter; @@ -47,6 +45,16 @@ public class LandwalkAbility extends EvasionAbilityImpl { this.filter = filter; } + public LandwalkAbility(final LandwalkAbility ability) { + super(ability); + this.filter = ability.filter.copy(); + } + + @Override + public LandwalkAbility copy() { + return new LandwalkAbility(this); + } + @Override public boolean canBlock(Permanent permanent, Game game) { return game.getBattlefield().countAll(filter, permanent.getControllerId()) == 0; diff --git a/Mage/src/mage/abilities/keyword/LevelAbility.java b/Mage/src/mage/abilities/keyword/LevelAbility.java index 31c0c31c30b..ec1df74a835 100644 --- a/Mage/src/mage/abilities/keyword/LevelAbility.java +++ b/Mage/src/mage/abilities/keyword/LevelAbility.java @@ -31,21 +31,22 @@ package mage.abilities.keyword; import mage.Constants.Zone; import mage.abilities.Abilities; import mage.abilities.AbilitiesImpl; +import mage.abilities.Ability; import mage.abilities.StaticAbility; /** * * @author BetaSteward_at_googlemail.com */ -public class LevelAbility extends StaticAbility { +public class LevelAbility extends StaticAbility { private int level1; private int level2; - private Abilities abilities = new AbilitiesImpl(); + private Abilities abilities = new AbilitiesImpl(); private int power; private int toughness; - public LevelAbility(int level1, int level2, Abilities abilities, int power, int toughness) { + public LevelAbility(int level1, int level2, Abilities abilities, int power, int toughness) { super(Zone.BATTLEFIELD, null); this.level1 = level1; this.level2 = level2; @@ -54,6 +55,15 @@ public class LevelAbility extends StaticAbility { this.toughness = toughness; } + public LevelAbility(LevelAbility ability) { + super(ability); + this.level1 = ability.level1; + this.level2 = ability.level2; + this.abilities = ability.abilities.copy(); + this.power = ability.power; + this.toughness = ability.toughness; + } + public int getLevel1() { return level1; } @@ -62,7 +72,7 @@ public class LevelAbility extends StaticAbility { return level2; } - public Abilities getAbilities() { + public Abilities getAbilities() { return abilities; } @@ -89,4 +99,9 @@ public class LevelAbility extends StaticAbility { return sb.toString(); } + @Override + public LevelAbility copy() { + return new LevelAbility(this); + } + } diff --git a/Mage/src/mage/abilities/keyword/LevelUpAbility.java b/Mage/src/mage/abilities/keyword/LevelUpAbility.java index caed37fe715..5770a134298 100644 --- a/Mage/src/mage/abilities/keyword/LevelUpAbility.java +++ b/Mage/src/mage/abilities/keyword/LevelUpAbility.java @@ -38,11 +38,21 @@ import mage.abilities.effects.common.AddCountersSourceEffect; * * @author BetaSteward_at_googlemail.com */ -public class LevelUpAbility extends ActivatedAbilityImpl { +public class LevelUpAbility extends ActivatedAbilityImpl { public LevelUpAbility(ManaCosts costs) { super(Zone.BATTLEFIELD, new AddCountersSourceEffect("Level", 1), costs); this.timing = TimingRule.SORCERY; } + public LevelUpAbility(final LevelUpAbility ability) { + super(ability); + } + + @Override + public LevelUpAbility copy() { + return new LevelUpAbility(this); + } + + } diff --git a/Mage/src/mage/abilities/keyword/LifelinkAbility.java b/Mage/src/mage/abilities/keyword/LifelinkAbility.java index 9ca355346f7..42b246446fb 100644 --- a/Mage/src/mage/abilities/keyword/LifelinkAbility.java +++ b/Mage/src/mage/abilities/keyword/LifelinkAbility.java @@ -36,7 +36,7 @@ import mage.abilities.StaticAbility; * * @author BetaSteward_at_googlemail.com */ -public class LifelinkAbility extends StaticAbility { +public class LifelinkAbility extends StaticAbility { private static final LifelinkAbility fINSTANCE = new LifelinkAbility(); @@ -57,4 +57,9 @@ public class LifelinkAbility extends StaticAbility { return "Lifelink"; } + @Override + public LifelinkAbility copy() { + return fINSTANCE; + } + } diff --git a/Mage/src/mage/abilities/keyword/MultikickerAbility.java b/Mage/src/mage/abilities/keyword/MultikickerAbility.java index c4b8a3c1632..7aa13c51807 100644 --- a/Mage/src/mage/abilities/keyword/MultikickerAbility.java +++ b/Mage/src/mage/abilities/keyword/MultikickerAbility.java @@ -37,12 +37,22 @@ import mage.game.Game; */ public class MultikickerAbility extends KickerAbility { - int activateCount = 0; + int activateCount; public MultikickerAbility(Effect effect, boolean replaces) { super(effect, replaces); } + public MultikickerAbility(final MultikickerAbility ability) { + super(ability); + this.activateCount = ability.activateCount; + } + + @Override + public MultikickerAbility copy() { + return new MultikickerAbility(this); + } + @Override public boolean activate(Game game, boolean noMana) { activateCount = 0; @@ -77,7 +87,7 @@ public class MultikickerAbility extends KickerAbility { } if (costs.size() > 0) sb.append(costs.getText()); - sb.append(":").append(effects.getText()); + sb.append(":").append(effects.getText(this)); if (replaces) sb.append(" instead"); return sb.toString(); diff --git a/Mage/src/mage/abilities/keyword/PhasingAbility.java b/Mage/src/mage/abilities/keyword/PhasingAbility.java index 16fdbcf99b5..7be2c2cbb13 100644 --- a/Mage/src/mage/abilities/keyword/PhasingAbility.java +++ b/Mage/src/mage/abilities/keyword/PhasingAbility.java @@ -36,7 +36,7 @@ import mage.abilities.StaticAbility; * * @author BetaSteward_at_googlemail.com */ -public class PhasingAbility extends StaticAbility { +public class PhasingAbility extends StaticAbility { private static final PhasingAbility fINSTANCE = new PhasingAbility(); @@ -57,4 +57,9 @@ public class PhasingAbility extends StaticAbility { return "Phasing"; } + @Override + public PhasingAbility copy() { + return fINSTANCE; + } + } diff --git a/Mage/src/mage/abilities/keyword/ProtectionAbility.java b/Mage/src/mage/abilities/keyword/ProtectionAbility.java index 085631d0e37..f1b0df008ce 100644 --- a/Mage/src/mage/abilities/keyword/ProtectionAbility.java +++ b/Mage/src/mage/abilities/keyword/ProtectionAbility.java @@ -31,13 +31,20 @@ package mage.abilities.keyword; import mage.Constants.Zone; import mage.MageObject; import mage.abilities.StaticAbility; +import mage.cards.Card; import mage.filter.Filter; +import mage.filter.FilterCard; +import mage.filter.FilterObject; +import mage.filter.FilterPermanent; +import mage.filter.FilterSpell; +import mage.game.permanent.Permanent; +import mage.game.stack.Spell; /** * * @author BetaSteward_at_googlemail.com */ -public class ProtectionAbility extends StaticAbility { +public class ProtectionAbility extends StaticAbility { protected Filter filter; @@ -46,13 +53,41 @@ public class ProtectionAbility extends StaticAbility { this.filter = filter; } + public ProtectionAbility(final ProtectionAbility ability) { + super(ability); + this.filter = ability.filter.copy(); + } + + @Override + public ProtectionAbility copy() { + return new ProtectionAbility(this); + } + @Override public String getRule() { return "Protection from " + filter.getMessage(); } public boolean canTarget(MageObject source) { - return !filter.match(source); + if (filter instanceof FilterPermanent) { + if (source instanceof Permanent) + return !filter.match(source); + return true; + } + if (filter instanceof FilterSpell) { + if (source instanceof Spell) + return !filter.match(source); + return true; + } + if (filter instanceof FilterCard) { + if (source instanceof Card) + return !filter.match(source); + return true; + } + if (filter instanceof FilterObject) { + return !filter.match(source); + } + return true; } public Filter getFilter() { diff --git a/Mage/src/mage/abilities/keyword/ReachAbility.java b/Mage/src/mage/abilities/keyword/ReachAbility.java index 7ca1ce295fc..ff515b6cc60 100644 --- a/Mage/src/mage/abilities/keyword/ReachAbility.java +++ b/Mage/src/mage/abilities/keyword/ReachAbility.java @@ -36,7 +36,7 @@ import mage.abilities.StaticAbility; * * @author BetaSteward_at_googlemail.com */ -public class ReachAbility extends StaticAbility { +public class ReachAbility extends StaticAbility { private static final ReachAbility fINSTANCE = new ReachAbility(); @@ -57,4 +57,9 @@ public class ReachAbility extends StaticAbility { return "Reach"; } + @Override + public ReachAbility copy() { + return fINSTANCE; + } + } diff --git a/Mage/src/mage/abilities/keyword/ShroudAbility.java b/Mage/src/mage/abilities/keyword/ShroudAbility.java index 3514094494c..4a58de35dd7 100644 --- a/Mage/src/mage/abilities/keyword/ShroudAbility.java +++ b/Mage/src/mage/abilities/keyword/ShroudAbility.java @@ -36,7 +36,7 @@ import mage.abilities.StaticAbility; * * @author BetaSteward_at_googlemail.com */ -public class ShroudAbility extends StaticAbility { +public class ShroudAbility extends StaticAbility { private static final ShroudAbility fINSTANCE = new ShroudAbility(); @@ -57,4 +57,9 @@ public class ShroudAbility extends StaticAbility { return "Shroud"; } + @Override + public ShroudAbility copy() { + return fINSTANCE; + } + } diff --git a/Mage/src/mage/abilities/keyword/TrampleAbility.java b/Mage/src/mage/abilities/keyword/TrampleAbility.java index 4502939fa50..5b20819b3dc 100644 --- a/Mage/src/mage/abilities/keyword/TrampleAbility.java +++ b/Mage/src/mage/abilities/keyword/TrampleAbility.java @@ -36,7 +36,7 @@ import mage.abilities.StaticAbility; * * @author BetaSteward_at_googlemail.com */ -public class TrampleAbility extends StaticAbility { +public class TrampleAbility extends StaticAbility { private static final TrampleAbility fINSTANCE = new TrampleAbility(); @@ -57,4 +57,9 @@ public class TrampleAbility extends StaticAbility { return "Trample"; } + @Override + public TrampleAbility copy() { + return fINSTANCE; + } + } \ No newline at end of file diff --git a/Mage/src/mage/abilities/keyword/UnblockableAbility.java b/Mage/src/mage/abilities/keyword/UnblockableAbility.java index 06c41cede34..bacc74ad270 100644 --- a/Mage/src/mage/abilities/keyword/UnblockableAbility.java +++ b/Mage/src/mage/abilities/keyword/UnblockableAbility.java @@ -37,7 +37,7 @@ import mage.game.permanent.Permanent; * * @author BetaSteward_at_googlemail.com */ -public class UnblockableAbility extends EvasionAbilityImpl { +public class UnblockableAbility extends EvasionAbilityImpl { private static final UnblockableAbility fINSTANCE = new UnblockableAbility(); @@ -61,4 +61,9 @@ public class UnblockableAbility extends EvasionAbilityImpl { return "Unblockable"; } + @Override + public UnblockableAbility copy() { + return fINSTANCE; + } + } diff --git a/Mage/src/mage/abilities/keyword/UnearthAbility.java b/Mage/src/mage/abilities/keyword/UnearthAbility.java index 027c90aa913..193fda37f1a 100644 --- a/Mage/src/mage/abilities/keyword/UnearthAbility.java +++ b/Mage/src/mage/abilities/keyword/UnearthAbility.java @@ -32,38 +32,46 @@ import mage.Constants.Duration; import mage.Constants.Outcome; import mage.Constants.TimingRule; import mage.Constants.Zone; +import mage.abilities.Ability; import mage.abilities.ActivatedAbilityImpl; import mage.abilities.DelayedTriggeredAbility; -import mage.abilities.common.OnEventTriggeredAbility; import mage.abilities.costs.mana.ManaCosts; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.ReplacementEffectImpl; import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; import mage.abilities.effects.common.ExileSourceEffect; import mage.abilities.effects.common.GainAbilitySourceEffect; -import mage.cards.Card; +import mage.abilities.effects.common.ReturnSourceFromGraveyardToBattlefieldEffect; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; import mage.game.events.ZoneChangeEvent; -import mage.players.Player; /** * * @author BetaSteward_at_googlemail.com */ -public class UnearthAbility extends ActivatedAbilityImpl { +public class UnearthAbility extends ActivatedAbilityImpl { - protected boolean unearthed = false; + protected boolean unearthed; public UnearthAbility(ManaCosts costs) { - super(Zone.GRAVEYARD, new UnearthEffect(), costs); + super(Zone.GRAVEYARD, new ReturnSourceFromGraveyardToBattlefieldEffect(), costs); this.timing = TimingRule.SORCERY; this.addEffect(new GainAbilitySourceEffect(HasteAbility.getInstance(), Duration.WhileOnBattlefield)); this.addEffect(new CreateDelayedTriggeredAbilityEffect(new UnearthDelayedTriggeredAbility())); this.addEffect(new UnearthLeavesBattlefieldEffect()); } + public UnearthAbility(final UnearthAbility ability) { + super(ability); + this.unearthed = ability.unearthed; + } + + @Override + public UnearthAbility copy() { + return new UnearthAbility(this); + } + public boolean isUnearthed() { return unearthed; } @@ -75,37 +83,55 @@ public class UnearthAbility extends ActivatedAbilityImpl { } -class UnearthEffect extends OneShotEffect { +//class UnearthEffect extends OneShotEffect { +// +// public UnearthEffect() { +// super(Outcome.PutCreatureInPlay); +// } +// +// public UnearthEffect(final UnearthEffect effect) { +// super(effect); +// } +// +// @Override +// public UnearthEffect copy() { +// return new UnearthEffect(this); +// } +// +// @Override +// public boolean apply(Game game, Ability source) { +// Player player = game.getPlayer(source.getControllerId()); +// Card card = player.getGraveyard().get(source.getSourceId(), game); +// if (card != null) { +// player.putOntoBattlefield(card, game); +// player.removeFromGraveyard(card, game); +// return true; +// } +// return false; +// } +// +// @Override +// public String getText(Ability source) { +// return "Return {this} from your graveyard to the battlefield"; +// } +// +//} - public UnearthEffect() { - super(Outcome.PutCreatureInPlay); - } - - @Override - public boolean apply(Game game) { - Player player = game.getPlayer(this.source.getControllerId()); - Card card = player.getGraveyard().get(this.source.getSourceId()); - if (card != null) { - player.putOntoBattlefield(card, game); - player.removeFromGraveyard(card, game); - return true; - } - return false; - } - - @Override - public String getText() { - return "Return this card from your graveyard to the battlefield"; - } - -} - -class UnearthDelayedTriggeredAbility extends DelayedTriggeredAbility { +class UnearthDelayedTriggeredAbility extends DelayedTriggeredAbility { public UnearthDelayedTriggeredAbility() { super(new ExileSourceEffect()); } + public UnearthDelayedTriggeredAbility(final UnearthDelayedTriggeredAbility ability) { + super(ability); + } + + @Override + public UnearthDelayedTriggeredAbility copy() { + return new UnearthDelayedTriggeredAbility(this); + } + @Override public boolean checkTrigger(GameEvent event, Game game) { if (event.getType() == EventType.END_TURN_STEP_PRE && event.getPlayerId().equals(this.controllerId)) { @@ -122,16 +148,24 @@ class UnearthDelayedTriggeredAbility extends DelayedTriggeredAbility { } -class UnearthLeavesBattlefieldEffect extends ReplacementEffectImpl { - +class UnearthLeavesBattlefieldEffect extends ReplacementEffectImpl { public UnearthLeavesBattlefieldEffect() { super(Duration.WhileOnBattlefield, Outcome.Exile); } + public UnearthLeavesBattlefieldEffect(final UnearthLeavesBattlefieldEffect effect) { + super(effect); + } + @Override - public boolean applies(GameEvent event, Game game) { - if (event.getType() == EventType.ZONE_CHANGE && event.getTargetId().equals(this.source.getSourceId())) { + public UnearthLeavesBattlefieldEffect copy() { + return new UnearthLeavesBattlefieldEffect(this); + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + if (event.getType() == EventType.ZONE_CHANGE && event.getTargetId().equals(source.getSourceId())) { ZoneChangeEvent zEvent = (ZoneChangeEvent)event; if (zEvent.getFromZone() == Zone.BATTLEFIELD && zEvent.getToZone() != Zone.EXILED) return true; @@ -140,19 +174,19 @@ class UnearthLeavesBattlefieldEffect extends ReplacementEffectImpl { } @Override - public boolean apply(Game game) { + public boolean apply(Game game, Ability source) { ExileSourceEffect effect = new ExileSourceEffect(); - effect.setSource(source); - return effect.apply(game); +// effect.setSource(source); + return effect.apply(game, source); } @Override - public boolean replaceEvent(GameEvent event, Game game) { - return apply(game); + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + return apply(game, source); } @Override - public String getText() { + public String getText(Ability source) { return "When {this} leaves the battlefield, exile it"; } } \ No newline at end of file diff --git a/Mage/src/mage/abilities/keyword/VigilanceAbility.java b/Mage/src/mage/abilities/keyword/VigilanceAbility.java index 0722e5d48b5..8470b8ffa41 100644 --- a/Mage/src/mage/abilities/keyword/VigilanceAbility.java +++ b/Mage/src/mage/abilities/keyword/VigilanceAbility.java @@ -36,7 +36,7 @@ import mage.abilities.StaticAbility; * * @author BetaSteward_at_googlemail.com */ -public class VigilanceAbility extends StaticAbility { +public class VigilanceAbility extends StaticAbility { private static final VigilanceAbility fINSTANCE = new VigilanceAbility(); @@ -57,4 +57,9 @@ public class VigilanceAbility extends StaticAbility { return "Vigilance"; } + @Override + public VigilanceAbility copy() { + return fINSTANCE; + } + } diff --git a/Mage/src/mage/abilities/mana/BasicManaAbility.java b/Mage/src/mage/abilities/mana/BasicManaAbility.java index 757ec79cc5d..f4ec6c62286 100644 --- a/Mage/src/mage/abilities/mana/BasicManaAbility.java +++ b/Mage/src/mage/abilities/mana/BasicManaAbility.java @@ -36,10 +36,14 @@ import mage.abilities.effects.common.ManaEffect; * * @author BetaSteward_at_googlemail.com */ -public abstract class BasicManaAbility extends ManaAbility { +public abstract class BasicManaAbility> extends ManaAbility { public BasicManaAbility(ManaEffect effect) { super(Zone.BATTLEFIELD, effect, new TapSourceCost()); } + public BasicManaAbility(BasicManaAbility ability) { + super(ability); + } + } diff --git a/Mage/src/mage/abilities/mana/BlackManaAbility.java b/Mage/src/mage/abilities/mana/BlackManaAbility.java index bfbfa618928..efae4308156 100644 --- a/Mage/src/mage/abilities/mana/BlackManaAbility.java +++ b/Mage/src/mage/abilities/mana/BlackManaAbility.java @@ -35,11 +35,20 @@ import mage.abilities.effects.common.ManaEffect; * * @author BetaSteward_at_googlemail.com */ -public class BlackManaAbility extends BasicManaAbility { +public class BlackManaAbility extends BasicManaAbility { public BlackManaAbility() { super(new ManaEffect(Mana.BlackMana)); this.netMana.setBlack(1); } + public BlackManaAbility(BlackManaAbility ability) { + super(ability); + } + + @Override + public BlackManaAbility copy() { + return new BlackManaAbility(this); + } + } diff --git a/Mage/src/mage/abilities/mana/BlueManaAbility.java b/Mage/src/mage/abilities/mana/BlueManaAbility.java index 89d30542cb6..0ceeddf667b 100644 --- a/Mage/src/mage/abilities/mana/BlueManaAbility.java +++ b/Mage/src/mage/abilities/mana/BlueManaAbility.java @@ -35,11 +35,20 @@ import mage.abilities.effects.common.ManaEffect; * * @author BetaSteward_at_googlemail.com */ -public class BlueManaAbility extends BasicManaAbility { +public class BlueManaAbility extends BasicManaAbility { public BlueManaAbility() { super(new ManaEffect(Mana.BlueMana)); this.netMana.setBlue(1); } + public BlueManaAbility(BlueManaAbility ability) { + super(ability); + } + + @Override + public BlueManaAbility copy() { + return new BlueManaAbility(this); + } + } diff --git a/Mage/src/mage/abilities/mana/ColorlessManaAbility.java b/Mage/src/mage/abilities/mana/ColorlessManaAbility.java index c155acb49b3..c7f54f3b72f 100644 --- a/Mage/src/mage/abilities/mana/ColorlessManaAbility.java +++ b/Mage/src/mage/abilities/mana/ColorlessManaAbility.java @@ -35,11 +35,19 @@ import mage.abilities.effects.common.ManaEffect; * * @author BetaSteward_at_googlemail.com */ -public class ColorlessManaAbility extends BasicManaAbility { +public class ColorlessManaAbility extends BasicManaAbility { public ColorlessManaAbility() { super(new ManaEffect(Mana.ColorlessMana)); this.netMana.setColorless(1); } + public ColorlessManaAbility(ColorlessManaAbility ability) { + super(ability); + } + + @Override + public ColorlessManaAbility copy() { + return new ColorlessManaAbility(this); + } } diff --git a/Mage/src/mage/abilities/mana/GreenManaAbility.java b/Mage/src/mage/abilities/mana/GreenManaAbility.java index 0decbd99080..703f7b6f56f 100644 --- a/Mage/src/mage/abilities/mana/GreenManaAbility.java +++ b/Mage/src/mage/abilities/mana/GreenManaAbility.java @@ -35,11 +35,20 @@ import mage.abilities.effects.common.ManaEffect; * * @author BetaSteward_at_googlemail.com */ -public class GreenManaAbility extends BasicManaAbility { +public class GreenManaAbility extends BasicManaAbility { public GreenManaAbility() { super(new ManaEffect(Mana.GreenMana)); this.netMana.setGreen(1); } + public GreenManaAbility(GreenManaAbility ability) { + super(ability); + } + + @Override + public GreenManaAbility copy() { + return new GreenManaAbility(this); + } + } diff --git a/Mage/src/mage/abilities/mana/ManaAbility.java b/Mage/src/mage/abilities/mana/ManaAbility.java index 9e885af45d2..91640f064d8 100644 --- a/Mage/src/mage/abilities/mana/ManaAbility.java +++ b/Mage/src/mage/abilities/mana/ManaAbility.java @@ -29,6 +29,7 @@ package mage.abilities.mana; import java.util.UUID; +import mage.Constants.AbilityType; import mage.Constants.Zone; import mage.Mana; import mage.abilities.ActivatedAbilityImpl; @@ -40,12 +41,23 @@ import mage.game.Game; * * @author BetaSteward_at_googlemail.com */ -public abstract class ManaAbility extends ActivatedAbilityImpl { +public abstract class ManaAbility> extends ActivatedAbilityImpl { protected Mana netMana = new Mana(); public ManaAbility(Zone zone, ManaEffect effect, Cost cost) { - super(zone, effect, cost); + super(AbilityType.MANA, zone); + this.usesStack = false; + if (effect != null) { + this.addEffect(effect); + } + if (cost != null) + this.addCost(cost); + } + + public ManaAbility(ManaAbility ability) { + super(ability); + this.netMana = ability.netMana.copy(); } @Override @@ -53,7 +65,7 @@ public abstract class ManaAbility extends ActivatedAbilityImpl { if (!controlsAbility(playerId, game)) return false; //20091005 - 605.3a - return costs.canPay(playerId, game); + return costs.canPay(this, game); } public Mana getNetMana(Game game) { diff --git a/Mage/src/mage/abilities/mana/ManaOptions.java b/Mage/src/mage/abilities/mana/ManaOptions.java index 1c4e21ff59b..6ed9e882ed1 100644 --- a/Mage/src/mage/abilities/mana/ManaOptions.java +++ b/Mage/src/mage/abilities/mana/ManaOptions.java @@ -32,7 +32,6 @@ import java.util.ArrayList; import java.util.List; import mage.Mana; import mage.game.Game; -import mage.util.Copier; /** * @@ -45,6 +44,14 @@ import mage.util.Copier; */ public class ManaOptions extends ArrayList { + public ManaOptions () {}; + + public ManaOptions(final ManaOptions options) { + for (Mana mana: options) { + this.add(mana.copy()); + } + } + public void addMana(List abilities, Game game) { if (isEmpty()) this.add(new Mana()); @@ -55,8 +62,7 @@ public class ManaOptions extends ArrayList { } else if (abilities.size() > 1) { //perform a union of all existing options and the new options - Copier> copier = new Copier>(); - List copy = copier.copy(this); + List copy = copy(); this.clear(); for (ManaAbility ability: abilities) { for (Mana mana: copy) { @@ -88,8 +94,7 @@ public class ManaOptions extends ArrayList { } else if (options.size() > 1) { //perform a union of all existing options and the new options - Copier> copier = new Copier>(); - List copy = copier.copy(this); + List copy = copy(); this.clear(); for (Mana addMana: options) { for (Mana mana: copy) { @@ -103,4 +108,8 @@ public class ManaOptions extends ArrayList { } } + public ManaOptions copy() { + return new ManaOptions(this); + } + } \ No newline at end of file diff --git a/Mage/src/mage/abilities/mana/RedManaAbility.java b/Mage/src/mage/abilities/mana/RedManaAbility.java index 8f4ae45e343..74483249e08 100644 --- a/Mage/src/mage/abilities/mana/RedManaAbility.java +++ b/Mage/src/mage/abilities/mana/RedManaAbility.java @@ -35,11 +35,20 @@ import mage.abilities.effects.common.ManaEffect; * * @author BetaSteward_at_googlemail.com */ -public class RedManaAbility extends BasicManaAbility { +public class RedManaAbility extends BasicManaAbility { public RedManaAbility() { super(new ManaEffect(Mana.RedMana)); this.netMana.setRed(1); } + public RedManaAbility(RedManaAbility ability) { + super(ability); + } + + @Override + public RedManaAbility copy() { + return new RedManaAbility(this); + } + } diff --git a/Mage/src/mage/abilities/mana/WhiteManaAbility.java b/Mage/src/mage/abilities/mana/WhiteManaAbility.java index 5711b878808..3c3c6d84b9a 100644 --- a/Mage/src/mage/abilities/mana/WhiteManaAbility.java +++ b/Mage/src/mage/abilities/mana/WhiteManaAbility.java @@ -35,11 +35,20 @@ import mage.abilities.effects.common.ManaEffect; * * @author BetaSteward_at_googlemail.com */ -public class WhiteManaAbility extends BasicManaAbility { +public class WhiteManaAbility extends BasicManaAbility { public WhiteManaAbility() { super(new ManaEffect(Mana.WhiteMana)); this.netMana.setWhite(1); } + public WhiteManaAbility(WhiteManaAbility ability) { + super(ability); + } + + @Override + public WhiteManaAbility copy() { + return new WhiteManaAbility(this); + } + } diff --git a/Mage/src/mage/cards/Card.java b/Mage/src/mage/cards/Card.java index 6009550b50c..6e2d6b8dc85 100644 --- a/Mage/src/mage/cards/Card.java +++ b/Mage/src/mage/cards/Card.java @@ -43,14 +43,14 @@ public interface Card extends MageObject { public UUID getOwnerId(); public String getArt(); public void setControllerId(UUID controllerId); - public void setOwnerId(UUID controllerId); + public void setOwnerId(UUID ownerId); public void addAbility(Ability ability); public SpellAbility getSpellAbility(); public List getRules(); public Watchers getWatchers(); - public Card copy(); public UUID getExpansionSetId(); public void checkTriggers(Zone zone, GameEvent event, Game game); - + + public Card copy(); } diff --git a/Mage/src/mage/cards/CardImpl.java b/Mage/src/mage/cards/CardImpl.java index cdb710f0a91..6960d8a8a24 100644 --- a/Mage/src/mage/cards/CardImpl.java +++ b/Mage/src/mage/cards/CardImpl.java @@ -28,6 +28,7 @@ package mage.cards; +import java.lang.reflect.Constructor; import java.util.List; import java.util.UUID; import mage.Constants.CardType; @@ -39,16 +40,14 @@ import mage.abilities.SpellAbility; import mage.abilities.TriggeredAbility; import mage.game.Game; import mage.game.events.GameEvent; -import mage.util.Copier; import mage.watchers.Watchers; -public abstract class CardImpl extends MageObjectImpl implements Card { +public abstract class CardImpl> extends MageObjectImpl implements Card { protected UUID ownerId; - protected String art = ""; protected Watchers watchers = new Watchers(); protected UUID expansionSetId; - + public CardImpl(UUID ownerId, String name, CardType[] cardTypes, String costs) { this.ownerId = ownerId; this.name = name; @@ -56,9 +55,9 @@ public abstract class CardImpl extends MageObjectImpl implements Card { this.cardType.add(newCardType); this.manaCost.load(costs); if (cardType.contains(CardType.LAND)) - addAbility(new PlayLandAbility()); + addAbility(new PlayLandAbility(name)); else - addAbility(new SpellAbility(manaCost)); + addAbility(new SpellAbility(manaCost, name)); } protected CardImpl(UUID ownerId, String name) { @@ -66,15 +65,42 @@ public abstract class CardImpl extends MageObjectImpl implements Card { this.name = name; } + protected CardImpl(UUID id, UUID ownerId, String name) { + super(id); + this.ownerId = ownerId; + this.name = name; + } + + public CardImpl(final CardImpl card) { + super(card); + ownerId = card.ownerId; + expansionSetId = card.expansionSetId; + watchers = card.watchers.copy(); + } + + public static Card createCard(String name) { + try { + Class theClass = Class.forName(name); + Constructor con = theClass.getConstructor(new Class[]{UUID.class}); + Card card = (Card) con.newInstance(new Object[] {null}); + card.setZone(Zone.OUTSIDE); + return card; + } + catch (Exception e) { + e.printStackTrace(); + return null; + } + } + @Override public UUID getOwnerId() { return ownerId; } - @Override - public String getArt() { - return art; - } +// @Override +// public String getArt() { +// return art; +// } @Override public List getRules() { @@ -111,10 +137,10 @@ public abstract class CardImpl extends MageObjectImpl implements Card { abilities.setControllerId(ownerId); } - @Override - public Card copy() { - return new Copier().copy(this); - } +// @Override +// public Card copy() { +// return new Copier().copy(this); +// } @Override public Watchers getWatchers() { diff --git a/Mage/src/mage/cards/Cards.java b/Mage/src/mage/cards/Cards.java index 0cefeda031c..b742bb17922 100644 --- a/Mage/src/mage/cards/Cards.java +++ b/Mage/src/mage/cards/Cards.java @@ -31,22 +31,26 @@ package mage.cards; import java.io.Serializable; import java.util.Collection; import java.util.List; -import java.util.Map; +import java.util.Set; import java.util.UUID; import mage.filter.FilterCard; import mage.game.Game; import mage.game.events.GameEvent; -public interface Cards extends Map, Serializable { +public interface Cards extends Set, Serializable { public void add(Card card); + public Card get(UUID cardId, Game game); public void remove(Card card); - public void setOwner(UUID ownerId); + public void setOwner(UUID ownerId, Game game); public void addAll(List createCards); - public List getCards(FilterCard filter); - public Collection getUniqueCards(); - public Card getRandom(); - public int count(FilterCard filter); + public Set getCards(Game game); + public Set getCards(FilterCard filter, Game game); + public Collection getUniqueCards(Game game); + public Card getRandom(Game game); + public int count(FilterCard filter, Game game); public void checkTriggers(GameEvent event, Game game); + + public Cards copy(); } diff --git a/Mage/src/mage/cards/CardsImpl.java b/Mage/src/mage/cards/CardsImpl.java index a9ac423e458..6e7e566d543 100644 --- a/Mage/src/mage/cards/CardsImpl.java +++ b/Mage/src/mage/cards/CardsImpl.java @@ -29,13 +29,13 @@ package mage.cards; import java.io.Serializable; -import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; -import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Random; +import java.util.Set; import java.util.UUID; import mage.Constants.Zone; import mage.filter.FilterCard; @@ -46,12 +46,14 @@ import mage.game.events.GameEvent; * * @author BetaSteward_at_googlemail.com */ -public class CardsImpl extends LinkedHashMap implements Cards, Serializable { +public class CardsImpl extends LinkedHashSet implements Cards, Serializable { private static Random rnd = new Random(); private UUID ownerId; private Zone zone; + public CardsImpl() { } + public CardsImpl(Zone zone) { this.zone = zone; } @@ -59,13 +61,35 @@ public class CardsImpl extends LinkedHashMap implements Cards, Seria public CardsImpl(Zone zone, List cards) { this(zone); for (Card card: cards) { - this.put(card.getId(), card); + add(card); } } + public CardsImpl(final CardsImpl cards) { + for (UUID cardId: cards) { + this.add(cardId); + } + this.ownerId = cards.ownerId; + this.zone = cards.zone; + } + + @Override + public Cards copy() { + return new CardsImpl(this); + } + @Override public void add(Card card) { - this.put(card.getId(), card); + this.add(card.getId()); + if (zone != null) + card.setZone(zone); + } + + @Override + public Card get(UUID cardId, Game game) { + if (this.contains(cardId)) + return game.getCard(cardId); + return null; } @Override @@ -74,24 +98,24 @@ public class CardsImpl extends LinkedHashMap implements Cards, Seria } @Override - public void setOwner(UUID ownerId) { + public void setOwner(UUID ownerId, Game game) { this.ownerId = ownerId; - for (Card card: this.values()) { - card.setOwnerId(ownerId); + for (UUID card: this) { + game.getCard(card).setOwnerId(ownerId); } } @Override - public Card getRandom() { - Card[] cards = this.values().toArray(new Card[0]); - return cards[rnd.nextInt(cards.length)]; + public Card getRandom(Game game) { + UUID[] cards = this.toArray(new UUID[0]); + return game.getCard(cards[rnd.nextInt(cards.length)]); } @Override - public int count(FilterCard filter) { + public int count(FilterCard filter, Game game) { int result = 0; - for (Card card: this.values()) { - if (filter.match(card)) + for (UUID card: this) { + if (filter.match(game.getCard(card))) result++; } return result; @@ -99,35 +123,44 @@ public class CardsImpl extends LinkedHashMap implements Cards, Seria @Override public void checkTriggers(GameEvent event, Game game) { - for (Card card: this.values()) { - card.checkTriggers(zone, event, game); + for (UUID card: this) { + game.getCard(card).checkTriggers(zone, event, game); } } @Override - public List getCards(FilterCard filter) { - List cards = new ArrayList(); - for (Card card: this.values()) { - boolean match = filter.match(card); + public Set getCards(FilterCard filter, Game game) { + Set cards = new LinkedHashSet(); + for (UUID card: this) { + boolean match = filter.match(game.getCard(card)); if (match) - cards.add(card); + cards.add(game.getCard(card)); + } + return cards; + } + + @Override + public Set getCards(Game game) { + Set cards = new LinkedHashSet(); + for (UUID card: this) { + cards.add(game.getCard(card)); } return cards; } @Override public void addAll(List cards) { - for(Card card: cards) { - this.put(card.getId(), card); + for (Card card: cards) { + add(card); } } @Override - public Collection getUniqueCards() { + public Collection getUniqueCards(Game game) { Map cards = new HashMap(); - for(Card card: this.values()) { - if (!cards.containsKey(card.getName())) { - cards.put(card.getName(), card); + for(UUID card: this) { + if (!cards.containsKey(game.getCard(card).getName())) { + cards.put(game.getCard(card).getName(), game.getCard(card)); } } return cards.values(); diff --git a/Mage/src/mage/cards/ExpansionSet.java b/Mage/src/mage/cards/ExpansionSet.java index b78d4b81113..c7e925e3fcf 100644 --- a/Mage/src/mage/cards/ExpansionSet.java +++ b/Mage/src/mage/cards/ExpansionSet.java @@ -31,7 +31,9 @@ package mage.cards; import java.io.Serializable; import java.lang.reflect.Constructor; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.UUID; import java.util.logging.Level; import java.util.logging.Logger; @@ -40,11 +42,11 @@ import java.util.logging.Logger; * * @author BetaSteward_at_googlemail.com */ -public class ExpansionSet implements Serializable { +public abstract class ExpansionSet implements Serializable { protected String name; - protected List cards = new ArrayList(); - protected UUID id = UUID.randomUUID(); + protected final List cards = new ArrayList(); + protected final UUID id = UUID.randomUUID(); public List getCards() { return cards; @@ -68,8 +70,8 @@ public class ExpansionSet implements Serializable { } } - public List createCards() { - List created = new ArrayList(); + public Set createCards() { + Set created = new HashSet(); for (Class clazz: cards) { created.add(createCard(clazz)); } diff --git a/Mage/src/mage/cards/LevelerCard.java b/Mage/src/mage/cards/LevelerCard.java index 773b0db9e89..b5e5c9f25b3 100644 --- a/Mage/src/mage/cards/LevelerCard.java +++ b/Mage/src/mage/cards/LevelerCard.java @@ -38,14 +38,21 @@ import mage.abilities.keyword.LevelAbility; * * @author BetaSteward_at_googlemail.com */ -public abstract class LevelerCard extends CardImpl { +public abstract class LevelerCard> extends CardImpl { - private List levels = new ArrayList(); + protected List levels = new ArrayList(); public LevelerCard(UUID ownerId, String name, CardType[] cardTypes, String costs) { super(ownerId, name, cardTypes, costs); } + public LevelerCard(LevelerCard card) { + super(card); + for (LevelAbility ability: (List)card.levels) { + this.levels.add(ability.copy()); + } + } + public List getLevels() { return levels; } diff --git a/Mage/src/mage/cards/basiclands/BasicLand.java b/Mage/src/mage/cards/basiclands/BasicLand.java index 85952931eba..38ff3d8a064 100644 --- a/Mage/src/mage/cards/basiclands/BasicLand.java +++ b/Mage/src/mage/cards/basiclands/BasicLand.java @@ -37,7 +37,7 @@ import mage.cards.CardImpl; * * @author BetaSteward_at_googlemail.com */ -public class BasicLand extends CardImpl { +public abstract class BasicLand> extends CardImpl { public BasicLand(UUID ownerId, String name, ManaAbility mana) { super(ownerId, name, new CardType[]{CardType.LAND}, null); @@ -45,5 +45,9 @@ public class BasicLand extends CardImpl { this.subtype.add(name); this.addAbility(mana); } + + public BasicLand(BasicLand land) { + super(land); + } } diff --git a/Mage/src/mage/cards/basiclands/Forest.java b/Mage/src/mage/cards/basiclands/Forest.java index 4b2216b68fa..6bbeb9c5a3a 100644 --- a/Mage/src/mage/cards/basiclands/Forest.java +++ b/Mage/src/mage/cards/basiclands/Forest.java @@ -35,10 +35,14 @@ import mage.abilities.mana.GreenManaAbility; * * @author BetaSteward_at_googlemail.com */ -public class Forest extends BasicLand { +public abstract class Forest> extends BasicLand { public Forest(UUID ownerId) { super(ownerId, "Forest", new GreenManaAbility()); } + public Forest(Forest land) { + super(land); + } + } diff --git a/Mage/src/mage/cards/basiclands/Island.java b/Mage/src/mage/cards/basiclands/Island.java index 1153eceb0ea..1c5d1ea986f 100644 --- a/Mage/src/mage/cards/basiclands/Island.java +++ b/Mage/src/mage/cards/basiclands/Island.java @@ -35,10 +35,14 @@ import mage.abilities.mana.BlueManaAbility; * * @author BetaSteward_at_googlemail.com */ -public class Island extends BasicLand { +public abstract class Island> extends BasicLand { public Island(UUID ownerId) { super(ownerId, "Island", new BlueManaAbility()); } + public Island(Island land) { + super(land); + } + } diff --git a/Mage/src/mage/cards/basiclands/Mountain.java b/Mage/src/mage/cards/basiclands/Mountain.java index 9c7b8e0d68f..cbd421b01a0 100644 --- a/Mage/src/mage/cards/basiclands/Mountain.java +++ b/Mage/src/mage/cards/basiclands/Mountain.java @@ -35,10 +35,13 @@ import mage.abilities.mana.RedManaAbility; * * @author BetaSteward_at_googlemail.com */ -public class Mountain extends BasicLand { +public abstract class Mountain> extends BasicLand { public Mountain(UUID ownerId) { super(ownerId, "Mountain", new RedManaAbility()); } + public Mountain(Mountain land) { + super(land); + } } diff --git a/Mage/src/mage/cards/basiclands/Plains.java b/Mage/src/mage/cards/basiclands/Plains.java index f791801f03d..8de31be5820 100644 --- a/Mage/src/mage/cards/basiclands/Plains.java +++ b/Mage/src/mage/cards/basiclands/Plains.java @@ -35,10 +35,13 @@ import mage.abilities.mana.WhiteManaAbility; * * @author BetaSteward_at_googlemail.com */ -public class Plains extends BasicLand { +public abstract class Plains> extends BasicLand { public Plains(UUID ownerId) { super(ownerId, "Plains", new WhiteManaAbility()); } + public Plains(Plains land) { + super(land); + } } diff --git a/Mage/src/mage/cards/basiclands/Swamp.java b/Mage/src/mage/cards/basiclands/Swamp.java index fda5430121b..a56751a8dd1 100644 --- a/Mage/src/mage/cards/basiclands/Swamp.java +++ b/Mage/src/mage/cards/basiclands/Swamp.java @@ -35,10 +35,13 @@ import mage.abilities.mana.BlackManaAbility; * * @author BetaSteward_at_googlemail.com */ -public class Swamp extends BasicLand { +public abstract class Swamp> extends BasicLand { public Swamp(UUID ownerId) { super(ownerId, "Swamp", new BlackManaAbility()); } + public Swamp(Swamp land) { + super(land); + } } diff --git a/Mage/src/mage/cards/decks/Deck.java b/Mage/src/mage/cards/decks/Deck.java index 4380bb0847f..676b3d97520 100644 --- a/Mage/src/mage/cards/decks/Deck.java +++ b/Mage/src/mage/cards/decks/Deck.java @@ -30,49 +30,36 @@ package mage.cards.decks; import mage.cards.*; import java.io.Serializable; -import java.lang.reflect.Constructor; -import java.util.UUID; -import mage.Constants.Zone; +import java.util.LinkedHashSet; +import java.util.Set; public class Deck implements Serializable { private String name; - private Cards cards = new CardsImpl(Zone.OUTSIDE); - private Cards sideboard = new CardsImpl(Zone.OUTSIDE); + private Set cards = new LinkedHashSet(); + private Set sideboard = new LinkedHashSet(); public static Deck load(DeckCardLists deckCardLists) { Deck deck = new Deck(); deck.setName(deckCardLists.getName()); for (String cardName: deckCardLists.getCards()) { - deck.cards.add(createCard(cardName)); + deck.cards.add(CardImpl.createCard(cardName)); } for (String cardName: deckCardLists.getSideboard()) { - deck.sideboard.add(createCard(cardName)); + deck.sideboard.add(CardImpl.createCard(cardName)); } return deck; } - private static Card createCard(String name) { - try { - Class theClass = Class.forName(name); - Constructor con = theClass.getConstructor(new Class[]{UUID.class}); - return (Card) con.newInstance(new Object[] {null}); - } - catch (Exception e) { - e.printStackTrace(); - return null; - } - } - public DeckCardLists getDeckCardLists() { DeckCardLists deckCardLists = new DeckCardLists(); deckCardLists.setName(name); - for (Card card: cards.values()) { + for (Card card: cards) { deckCardLists.getCards().add(card.getClass().getCanonicalName()); } - for (Card card: sideboard.values()) { + for (Card card: sideboard) { deckCardLists.getSideboard().add(card.getClass().getCanonicalName()); } @@ -96,36 +83,34 @@ public class Deck implements Serializable { /** * @return the cards */ - public Cards getCards() { + public Set getCards() { return cards; } - /** - * @param cards the cards to set - */ - public void setCards(Cards cards) { - this.cards = cards; - } +// /** +// * @param cards the cards to set +// */ +// public void setCards(List cards) { +// this.cards = cards; +// } /** * @return the sideboard */ - public Cards getSideboard() { + public Set getSideboard() { return sideboard; } - /** - * @param sideboard the sideboard to set - */ - public void setSideboard(Cards sideboard) { - this.sideboard = sideboard; - } - - public void setOwnerId(UUID playerId) { - cards.setOwner(playerId); - sideboard.setOwner(playerId); - } - +// /** +// * @param sideboard the sideboard to set +// */ +// public void setSideboard(Cards sideboard) { +// this.sideboard = sideboard; +// } +// public void setOwnerId(UUID playerId) { +// cards.setOwner(playerId); +// sideboard.setOwner(playerId); +// } } diff --git a/Mage/src/mage/choices/Choice.java b/Mage/src/mage/choices/Choice.java index 5126c0f9c0e..7fa1f23faf1 100644 --- a/Mage/src/mage/choices/Choice.java +++ b/Mage/src/mage/choices/Choice.java @@ -29,7 +29,6 @@ package mage.choices; import java.util.List; -import mage.abilities.Ability; /** * @@ -38,12 +37,12 @@ import mage.abilities.Ability; public interface Choice { public boolean isChosen(); + public boolean isRequired(); public void clearChoice(); public String getMessage(); public void setChoice(String choice); public List getChoices(); public String getChoice(); - public Ability getAbility(); - public void setAbility(Ability ability); + public Choice copy(); } diff --git a/Mage/src/mage/choices/ChoiceColor.java b/Mage/src/mage/choices/ChoiceColor.java index 754300fab8d..563df021c0e 100644 --- a/Mage/src/mage/choices/ChoiceColor.java +++ b/Mage/src/mage/choices/ChoiceColor.java @@ -34,7 +34,7 @@ import mage.ObjectColor; * * @author BetaSteward_at_googlemail.com */ -public class ChoiceColor extends ChoiceImpl { +public class ChoiceColor extends ChoiceImpl { public ChoiceColor() { this.choices.add("Black"); @@ -45,6 +45,15 @@ public class ChoiceColor extends ChoiceImpl { this.message = "Choose color"; } + public ChoiceColor(final ChoiceColor choice) { + super(choice); + } + + @Override + public ChoiceColor copy() { + return new ChoiceColor(this); + } + public ObjectColor getColor() { ObjectColor color = new ObjectColor(); if (choice.equals("Black")) diff --git a/Mage/src/mage/choices/ChoiceImpl.java b/Mage/src/mage/choices/ChoiceImpl.java index 73202fc660d..68e69fa5e05 100644 --- a/Mage/src/mage/choices/ChoiceImpl.java +++ b/Mage/src/mage/choices/ChoiceImpl.java @@ -31,22 +31,35 @@ package mage.choices; import java.io.Serializable; import java.util.ArrayList; import java.util.List; -import mage.abilities.Ability; /** * * @author BetaSteward_at_googlemail.com */ -public class ChoiceImpl implements Choice, Serializable { +public class ChoiceImpl> implements Choice, Serializable { - protected Ability source; protected boolean chosen; + protected boolean required; protected String choice; protected List choices = new ArrayList(); protected String message; public ChoiceImpl() { + this(false); + } + public ChoiceImpl(boolean required) { + this.required = required; + } + + public ChoiceImpl(ChoiceImpl choice) { + this.choice = choice.choice; + this.chosen = choice.chosen; + this.required = choice.required; + this.message = choice.message; + for (String c: choice.choices) { + this.choices.add(c); + } } @Override @@ -84,13 +97,13 @@ public class ChoiceImpl implements Choice, Serializable { } @Override - public Ability getAbility() { - return source; + public boolean isRequired() { + return this.required; } @Override - public void setAbility(Ability source) { - this.source = source; + public T copy() { + return (T)new ChoiceImpl(this); } } diff --git a/Mage/src/mage/choices/Choices.java b/Mage/src/mage/choices/Choices.java index 8aff28fd027..511ab58a3b3 100644 --- a/Mage/src/mage/choices/Choices.java +++ b/Mage/src/mage/choices/Choices.java @@ -39,23 +39,21 @@ import mage.players.Player; * * @author BetaSteward_at_googlemail.com */ -public class Choices extends ArrayList{ +public class Choices extends ArrayList { - protected Ability source; protected Outcome outcome; - public Choices(Ability ability) { - this.source = ability; + public Choices() {} + + public Choices(final Choices choices) { + this.outcome = choices.outcome; + for (Choice choice: choices) { + this.add(choice.copy()); + } } - public void setSource(Ability ability) { - this.source = ability; - } - - @Override - public boolean add(Choice choice) { - choice.setAbility(source); - return super.add(choice); + public Choices copy() { + return new Choices(this); } public List getUnchosen() { @@ -81,9 +79,9 @@ public class Choices extends ArrayList{ return true; } - public boolean choose(Game game) { + public boolean choose(Game game, Ability source) { if (this.size() > 0) { - Player player = game.getPlayer(this.source.getControllerId()); + Player player = game.getPlayer(source.getControllerId()); while (!isChosen()) { Choice choice = this.getUnchosen().get(0); if (!player.choose(outcome, choice, game)) diff --git a/Mage/src/mage/counters/BoostCounter.java b/Mage/src/mage/counters/BoostCounter.java index 204e2677058..eb8abe4e109 100644 --- a/Mage/src/mage/counters/BoostCounter.java +++ b/Mage/src/mage/counters/BoostCounter.java @@ -32,7 +32,7 @@ package mage.counters; * * @author BetaSteward_at_googlemail.com */ -public class BoostCounter extends Counter { +public class BoostCounter> extends Counter { protected int power; protected int toughness; diff --git a/Mage/src/mage/counters/Counter.java b/Mage/src/mage/counters/Counter.java index c30d8806139..392fa8e99db 100644 --- a/Mage/src/mage/counters/Counter.java +++ b/Mage/src/mage/counters/Counter.java @@ -34,7 +34,7 @@ import java.io.Serializable; * * @author BetaSteward_at_googlemail.com */ -public class Counter implements Serializable { +public class Counter> implements Serializable { protected String name; protected int count; @@ -43,6 +43,11 @@ public class Counter implements Serializable { this.name = name; } + public Counter(Counter counter) { + this.name = counter.name; + this.count = counter.count; + } + public void add() { count++; } @@ -70,4 +75,8 @@ public class Counter implements Serializable { public int getCount() { return count; } + + public T copy() { + return (T)new Counter(this); + } } diff --git a/Mage/src/mage/counters/Counters.java b/Mage/src/mage/counters/Counters.java index 6a7e81c3032..625db5a855a 100644 --- a/Mage/src/mage/counters/Counters.java +++ b/Mage/src/mage/counters/Counters.java @@ -39,6 +39,18 @@ import java.util.List; */ public class Counters extends HashMap implements Serializable { + public Counters() {} + + public Counters(final Counters counters) { + for (String key: counters.keySet()) { + this.put(key, counters.get(key).copy()); + } + } + + public Counters copy() { + return new Counters(this); + } + public void addCounter(String name) { if (!this.containsKey(name)) this.put(name, new Counter(name)); diff --git a/Mage/src/mage/counters/MinusOneCounter.java b/Mage/src/mage/counters/MinusOneCounter.java index 545a6b64999..1d030900c74 100644 --- a/Mage/src/mage/counters/MinusOneCounter.java +++ b/Mage/src/mage/counters/MinusOneCounter.java @@ -32,7 +32,7 @@ package mage.counters; * * @author BetaSteward_at_googlemail.com */ -public class MinusOneCounter extends BoostCounter { +public class MinusOneCounter extends BoostCounter { public MinusOneCounter() { super(-1, -1); diff --git a/Mage/src/mage/counters/PlusOneCounter.java b/Mage/src/mage/counters/PlusOneCounter.java index 0fe63040126..008cb73afe8 100644 --- a/Mage/src/mage/counters/PlusOneCounter.java +++ b/Mage/src/mage/counters/PlusOneCounter.java @@ -32,7 +32,7 @@ package mage.counters; * * @author BetaSteward_at_googlemail.com */ -public class PlusOneCounter extends BoostCounter { +public class PlusOneCounter extends BoostCounter { public PlusOneCounter() { this(1); diff --git a/Mage/src/mage/filter/Filter.java b/Mage/src/mage/filter/Filter.java index 9005e42d155..c3e866fe207 100644 --- a/Mage/src/mage/filter/Filter.java +++ b/Mage/src/mage/filter/Filter.java @@ -47,5 +47,8 @@ public interface Filter extends Serializable { public boolean match(T o); public String getMessage(); public void setMessage(String message); + public void setNotFilter(boolean notFilter); + + public Filter copy(); } diff --git a/Mage/src/mage/filter/FilterAbility.java b/Mage/src/mage/filter/FilterAbility.java new file mode 100644 index 00000000000..9a3cfaaf1b0 --- /dev/null +++ b/Mage/src/mage/filter/FilterAbility.java @@ -0,0 +1,127 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.filter; + +import java.util.ArrayList; +import java.util.List; +import mage.Constants.AbilityType; +import mage.Constants.Outcome; +import mage.Constants.Zone; +import mage.abilities.Ability; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class FilterAbility extends FilterImpl> implements Filter { + + protected static ListComparer compOutcome = new ListComparer(); + + protected List outcomes = new ArrayList(); + protected ComparisonScope scopeOutcome = ComparisonScope.All; + protected boolean notOutcome; + protected List types = new ArrayList(); + protected boolean notType; + protected Zone zone; + protected boolean notZone; + + public FilterAbility() { + super(""); + } + + public FilterAbility(FilterAbility filter) { + super(filter); + for (Outcome outcome: filter.outcomes) { + this.outcomes.add(outcome); + } + this.scopeOutcome = filter.scopeOutcome; + this.notOutcome = filter.notOutcome; + for (AbilityType aType: filter.types) { + this.types.add(aType); + } + this.notType = filter.notType; + this.zone = filter.zone; + this.notZone = filter.notZone; + } + + @Override + public boolean match(T object) { + + if (zone != null) { + if (object.getZone().match(zone) == notZone) + return notFilter; + } + + if (outcomes.size() > 0) { + if (!compOutcome.compare(outcomes, object.getEffects().getOutcomes(), scopeOutcome, notOutcome)) + return notFilter; + } + + if (types.size() > 0) { + if (types.contains(object.getAbilityType()) == notType) + return notFilter; + } + + return !notFilter; + } + + public List getOutcomes() { + return this.outcomes; + } + + public void setScopeOutcome(ComparisonScope scopeOutcome) { + this.scopeOutcome = scopeOutcome; + } + + public void setNotOutcome(boolean notOutcome) { + this.notOutcome = notOutcome; + } + + public List getTypes() { + return types; + } + + public void setNotType(boolean notType) { + this.notType = notType; + } + + public void setZone(Zone zone) { + this.zone = zone; + } + + public void setNotZone(boolean notZone) { + this.notZone = notZone; + } + + @Override + public FilterAbility copy() { + return new FilterAbility(this); + } + +} diff --git a/Mage/src/mage/filter/FilterCard.java b/Mage/src/mage/filter/FilterCard.java index 6f448b105fb..72409411640 100644 --- a/Mage/src/mage/filter/FilterCard.java +++ b/Mage/src/mage/filter/FilterCard.java @@ -29,7 +29,9 @@ package mage.filter; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.UUID; import mage.cards.Card; @@ -37,12 +39,12 @@ import mage.cards.Card; * * @author BetaSteward_at_googlemail.com */ -public class FilterCard extends FilterObject { +public class FilterCard> extends FilterObject> { protected List ownerId = new ArrayList(); - protected boolean notOwner = false; + protected boolean notOwner; protected List expansionSetId = new ArrayList(); - protected boolean notExpansionSetId = false; + protected boolean notExpansionSetId; public FilterCard() { super("card"); @@ -52,18 +54,30 @@ public class FilterCard extends FilterObject { super(name); } + public FilterCard(FilterCard filter) { + super(filter); + for (UUID oId: filter.ownerId) { + this.ownerId.add(oId); + } + this.notOwner = filter.notOwner; + for (UUID eId: filter.expansionSetId) { + this.expansionSetId.add(eId); + } + this.notExpansionSetId = filter.notExpansionSetId; + } + @Override public boolean match(Card card) { if (!super.match(card)) - return false; + return notFilter; if (ownerId.size() > 0 && ownerId.contains(card.getOwnerId()) == notOwner) - return false; + return notFilter; if (expansionSetId.size() > 0 && expansionSetId.contains(card.getExpansionSetId()) == notExpansionSetId) - return false; + return notFilter; - return true; + return !notFilter; } public List getOwnerId() { @@ -88,4 +102,18 @@ public class FilterCard extends FilterObject { return true; } + public Set filter(Set cards) { + Set filtered = new HashSet(); + for (Card card: cards) { + if (match(card)) { + filtered.add(card); + } + } + return filtered; + } + + @Override + public FilterCard copy() { + return new FilterCard(this); + } } diff --git a/Mage/src/mage/filter/FilterImpl.java b/Mage/src/mage/filter/FilterImpl.java index 1a9c7b1ba86..463c4961c03 100644 --- a/Mage/src/mage/filter/FilterImpl.java +++ b/Mage/src/mage/filter/FilterImpl.java @@ -35,16 +35,25 @@ import mage.ObjectColor; * * @author BetaSteward_at_googlemail.com */ -public abstract class FilterImpl implements Filter { +public abstract class FilterImpl> implements Filter { protected static ListComparer compCardType = new ListComparer(); protected static ListComparer compString = new ListComparer(); + protected String message; + protected boolean notFilter = false; + + @Override + public abstract FilterImpl copy(); + public FilterImpl(String name) { this.message = name; } - protected String message; + public FilterImpl(FilterImpl filter) { + this.message = filter.message; + this.notFilter = filter.notFilter; + } protected boolean compareInts(int int1, int int2, ComparisonType type) { switch (type) { @@ -80,4 +89,10 @@ public abstract class FilterImpl implements Filter { public void setMessage(String message) { this.message = message; } + + @Override + public void setNotFilter(boolean notFilter) { + this.notFilter = notFilter; + } + } diff --git a/Mage/src/mage/filter/FilterObject.java b/Mage/src/mage/filter/FilterObject.java index 06bc04c09fc..51f6d6326f2 100644 --- a/Mage/src/mage/filter/FilterObject.java +++ b/Mage/src/mage/filter/FilterObject.java @@ -32,117 +32,172 @@ import java.util.ArrayList; import java.util.List; import java.util.UUID; import mage.Constants.CardType; +import mage.Constants.Zone; import mage.MageObject; import mage.ObjectColor; import mage.abilities.Abilities; import mage.abilities.AbilitiesImpl; +import mage.abilities.Ability; /** * * @author BetaSteward_at_googlemail.com */ -public class FilterObject extends FilterImpl implements Filter { - protected Abilities abilities = new AbilitiesImpl(); - protected boolean notAbilities = false; +public abstract class FilterObject> extends FilterImpl implements Filter { + protected Abilities abilities; + protected boolean notAbilities; protected List cardType = new ArrayList(); protected ComparisonScope scopeCardType = ComparisonScope.All; - protected boolean notCardType = false; - protected boolean colorless = false; - protected boolean useColorless = false; - protected boolean useColor = false; - protected ObjectColor color = new ObjectColor(); + protected boolean notCardType; + protected boolean colorless; + protected boolean useColorless; + protected boolean useColor; + protected ObjectColor color; protected ComparisonScope scopeColor = ComparisonScope.All; - protected boolean notColor = false; + protected boolean notColor; protected List name = new ArrayList(); - protected boolean notName = false; + protected boolean notName; protected List subtype = new ArrayList(); protected ComparisonScope scopeSubtype = ComparisonScope.All; - protected boolean notSubtype = false; + protected boolean notSubtype; protected List supertype = new ArrayList(); protected ComparisonScope scopeSupertype = ComparisonScope.All; - protected boolean notSupertype = false; - protected int convertedManaCost = 0; + protected boolean notSupertype; + protected Zone zone; + protected boolean notZone; + protected int convertedManaCost; protected ComparisonType convertedManaCostComparison; - protected int power = 0; + protected int power; protected ComparisonType powerComparison; - protected int toughness = 0; + protected int toughness; protected ComparisonType toughnessComparison; - protected UUID id = null; + protected UUID id; protected boolean notId; + @Override + public abstract FilterObject copy(); + public FilterObject(String name) { super(name); + abilities = new AbilitiesImpl(); + color = new ObjectColor(); + } + + public FilterObject(FilterObject filter) { + super(filter); + this.abilities = filter.abilities.copy(); + this.notAbilities = filter.notAbilities; + for (CardType cType: (List)filter.cardType) { + this.cardType.add(cType); + } + this.scopeCardType = filter.scopeCardType; + this.notCardType = filter.notCardType; + this.colorless = filter.colorless; + this.useColorless = filter.useColorless; + this.useColor = filter.useColor; + this.color = filter.color.copy(); + this.scopeColor = filter.scopeColor; + this.notColor = filter.notColor; + for (String fName: (List)filter.name) { + this.name.add(fName); + } + this.notName = filter.notName; + for (String fSubtype: (List)filter.subtype) { + this.subtype.add(fSubtype); + } + this.scopeSubtype = filter.scopeSubtype; + this.notSubtype = filter.notSubtype; + for (String fSupertype: (List)filter.supertype) { + this.supertype.add(fSupertype); + } + this.scopeSupertype = filter.scopeSupertype; + this.notSupertype = filter.notSupertype; + this.zone = filter.zone; + this.notZone = filter.notZone; + this.convertedManaCost = filter.convertedManaCost; + this.convertedManaCostComparison = filter.convertedManaCostComparison; + this.power = filter.power; + this.powerComparison = filter.powerComparison; + this.toughness = filter.toughness; + this.toughnessComparison = filter.toughnessComparison; + this.id = filter.id; + this.notId = filter.notId; } @Override - public boolean match(T object) { + public boolean match(E object) { if (id != null) { if (object.getId().equals(id) == notId) - return false; + return notFilter; } if (name.size() > 0) { if (name.contains(object.getName()) == notName) - return false; + return notFilter; + } + + if (zone != null) { + if (object.getZone().match(zone) == notZone) + return notFilter; } if (useColor) { if (scopeColor == ComparisonScope.All) { if (object.getColor().equals(color) == notColor) { - return false; + return notFilter; } } else if (object.getColor().contains(color) == notColor) { if (useColorless && colorless) { //need to treat colorless like a color in this case if (object.getColor().isColorless() != colorless) { - return false; + return notFilter; } } else { - return false; + return notFilter; } } } else if (useColorless && object.getColor().isColorless() != colorless) { - return false; + return notFilter; } if (cardType.size() > 0) { if (!compCardType.compare(cardType, object.getCardType(), scopeCardType, notCardType)) - return false; + return notFilter; } if (subtype.size() > 0) { if (!compString.compare(subtype, object.getSubtype(), scopeSubtype, notSubtype)) - return false; + return notFilter; } if (supertype.size() > 0) { - if (!compString.compare(supertype, object.getSubtype(), scopeSupertype, notSupertype)) - return false; + if (!compString.compare(supertype, object.getSupertype(), scopeSupertype, notSupertype)) + return notFilter; } if (abilities.size() > 0 && object.getAbilities().containsAll(abilities) == notAbilities) { - return false; + return notFilter; } if (convertedManaCostComparison != null) { if (!compareInts(object.getManaCost().convertedManaCost(), convertedManaCost, convertedManaCostComparison)) - return false; + return notFilter; } if (powerComparison != null) { if (!compareInts(object.getPower().getValue(), power, powerComparison)) - return false; + return notFilter; } if (toughnessComparison != null) { if (!compareInts(object.getToughness().getValue(), toughness, toughnessComparison)) - return false; + return notFilter; } - return true; + return !notFilter; } public Abilities getAbilities() { @@ -256,5 +311,13 @@ public class FilterObject extends FilterImpl implements public void setNotId(boolean notId) { this.notId = notId; } + + public void setZone(Zone zone) { + this.zone = zone; + } + + public void setNotZone(boolean notZone) { + this.notZone = notZone; + } } diff --git a/Mage/src/mage/filter/FilterPermanent.java b/Mage/src/mage/filter/FilterPermanent.java index e302cb0a421..dd311e371f9 100644 --- a/Mage/src/mage/filter/FilterPermanent.java +++ b/Mage/src/mage/filter/FilterPermanent.java @@ -37,24 +37,44 @@ import mage.game.permanent.Permanent; * * @author BetaSteward_at_googlemail.com */ -public class FilterPermanent extends FilterObject { +public class FilterPermanent> extends FilterObject> { protected List ownerId = new ArrayList(); - protected boolean notOwner = false; + protected boolean notOwner; protected List controllerId = new ArrayList(); - protected boolean notController = false; - protected boolean useTapped = false; + protected boolean notController; + protected boolean useTapped; protected boolean tapped; - protected boolean useFlipped = false; + protected boolean useFlipped; protected boolean flipped; - protected boolean useFaceup = false; + protected boolean useFaceup; protected boolean faceup; - protected boolean usePhased = false; + protected boolean usePhased; protected boolean phasedIn; public FilterPermanent() { super("permanent"); } + public FilterPermanent(FilterPermanent filter) { + super(filter); + for (UUID oId: filter.ownerId) { + this.ownerId.add(oId); + } + this.notOwner = filter.notOwner; + for (UUID oId: filter.controllerId) { + this.controllerId.add(oId); + } + this.notController = filter.notController; + this.useTapped = filter.useTapped; + this.tapped = filter.tapped; + this.useFlipped = filter.useFlipped; + this.flipped = filter.flipped; + this.useFaceup = filter.useFaceup; + this.faceup = filter.faceup; + this.usePhased = filter.usePhased; + this.phasedIn = filter.phasedIn; + } + public FilterPermanent(String name) { super(name); } @@ -62,27 +82,27 @@ public class FilterPermanent extends FilterObject { @Override public boolean match(Permanent permanent) { if (!super.match(permanent)) - return false; + return notFilter; if (ownerId.size() > 0 && ownerId.contains(permanent.getOwnerId()) == notOwner) - return false; + return notFilter; if (controllerId.size() > 0 && controllerId.contains(permanent.getControllerId()) == notController) - return false; + return notFilter; if (useTapped && permanent.isTapped() != tapped) - return false; + return notFilter; if (useFlipped && permanent.isFlipped() != flipped) - return false; + return notFilter; if (useFaceup && permanent.isFaceUp() != faceup) - return false; + return notFilter; if (usePhased && permanent.isPhasedIn() != phasedIn) - return false; + return notFilter; - return true; + return !notFilter; } public List getOwnerId() { @@ -137,4 +157,9 @@ public class FilterPermanent extends FilterObject { return true; } + @Override + public FilterPermanent copy() { + return new FilterPermanent(this); + } + } diff --git a/Mage/src/mage/filter/FilterPlayer.java b/Mage/src/mage/filter/FilterPlayer.java index be548adc318..eb40d13b949 100644 --- a/Mage/src/mage/filter/FilterPlayer.java +++ b/Mage/src/mage/filter/FilterPlayer.java @@ -37,7 +37,7 @@ import mage.players.Player; * * @author BetaSteward_at_googlemail.com */ -public class FilterPlayer extends FilterImpl implements Filter { +public class FilterPlayer extends FilterImpl implements Filter { protected List playerId = new ArrayList(); protected boolean notPlayer; @@ -46,13 +46,21 @@ public class FilterPlayer extends FilterImpl implements Filter { super("player"); } + public FilterPlayer(FilterPlayer filter) { + super(filter); + for (UUID pId: filter.playerId) { + this.playerId.add(pId); + } + this.notPlayer = filter.notPlayer; + } + @Override public boolean match(Player player) { if (playerId.size() > 0 && playerId.contains(player.getId()) == notPlayer) - return false; + return notFilter; - return true; + return !notFilter; } public List getPlayerId() { @@ -63,4 +71,9 @@ public class FilterPlayer extends FilterImpl implements Filter { this.notPlayer = notPlayer; } + @Override + public FilterPlayer copy() { + return new FilterPlayer(this); + } + } diff --git a/Mage/src/mage/filter/FilterSpell.java b/Mage/src/mage/filter/FilterSpell.java index b74d66836f5..a922870b2ab 100644 --- a/Mage/src/mage/filter/FilterSpell.java +++ b/Mage/src/mage/filter/FilterSpell.java @@ -37,7 +37,7 @@ import mage.game.stack.Spell; * * @author BetaSteward_at_googlemail.com */ -public class FilterSpell extends FilterObject { +public class FilterSpell extends FilterObject { protected List controllerId = new ArrayList(); protected boolean notController = false; @@ -50,16 +50,24 @@ public class FilterSpell extends FilterObject { super(name); } + public FilterSpell(FilterSpell filter) { + super(filter); + for (UUID cId: filter.controllerId) { + this.controllerId.add(cId); + } + this.notController = filter.notController; + } + @Override public boolean match(Spell spell) { if (!super.match(spell)) - return false; + return notFilter; if (controllerId.size() > 0 && controllerId.contains(spell.getControllerId()) == notController) - return false; + return notFilter; - return true; + return !notFilter; } public List getControllerId() { @@ -70,4 +78,9 @@ public class FilterSpell extends FilterObject { this.notController = notController; } + @Override + public FilterSpell copy() { + return new FilterSpell(this); + } + } diff --git a/Mage/src/mage/filter/common/FilterAttackingCreature.java b/Mage/src/mage/filter/common/FilterAttackingCreature.java index 28c24cb9ae7..2f05ec50670 100644 --- a/Mage/src/mage/filter/common/FilterAttackingCreature.java +++ b/Mage/src/mage/filter/common/FilterAttackingCreature.java @@ -32,7 +32,7 @@ package mage.filter.common; * * @author BetaSteward_at_googlemail.com */ -public class FilterAttackingCreature extends FilterCreaturePermanent { +public class FilterAttackingCreature extends FilterCreaturePermanent { public FilterAttackingCreature() { this(""); @@ -44,4 +44,12 @@ public class FilterAttackingCreature extends FilterCreaturePermanent { this.useAttacking = true; } + public FilterAttackingCreature(final FilterAttackingCreature filter) { + super(filter); + } + + @Override + public FilterAttackingCreature copy() { + return new FilterAttackingCreature(this); + } } diff --git a/Mage/src/mage/filter/common/FilterBasicLandCard.java b/Mage/src/mage/filter/common/FilterBasicLandCard.java index 4c61e719b66..169a0c3f61a 100644 --- a/Mage/src/mage/filter/common/FilterBasicLandCard.java +++ b/Mage/src/mage/filter/common/FilterBasicLandCard.java @@ -34,7 +34,7 @@ import mage.filter.FilterCard; * * @author BetaSteward_at_googlemail.com */ -public class FilterBasicLandCard extends FilterCard { +public class FilterBasicLandCard extends FilterCard { public FilterBasicLandCard() { super("basic land card"); @@ -45,4 +45,13 @@ public class FilterBasicLandCard extends FilterCard { name.add("Plains"); } + + public FilterBasicLandCard(final FilterBasicLandCard filter) { + super(filter); + } + + @Override + public FilterBasicLandCard copy() { + return new FilterBasicLandCard(this); + } } diff --git a/Mage/src/mage/filter/common/FilterCreatureForAttack.java b/Mage/src/mage/filter/common/FilterCreatureForAttack.java index 009baa19162..7431bbbe229 100644 --- a/Mage/src/mage/filter/common/FilterCreatureForAttack.java +++ b/Mage/src/mage/filter/common/FilterCreatureForAttack.java @@ -35,7 +35,7 @@ import mage.game.permanent.Permanent; * * @author BetaSteward_at_googlemail.com */ -public class FilterCreatureForAttack extends FilterCreaturePermanent { +public class FilterCreatureForAttack extends FilterCreaturePermanent { public FilterCreatureForAttack() { this(""); @@ -53,11 +53,20 @@ public class FilterCreatureForAttack extends FilterCreaturePermanent { this.notAbilities = true; } + public FilterCreatureForAttack(final FilterCreatureForAttack filter) { + super(filter); + } + @Override public boolean match(Permanent permanent) { if (!super.match(permanent)) - return false; + return notFilter; return permanent.canTap(); } + + @Override + public FilterCreatureForAttack copy() { + return new FilterCreatureForAttack(this); + } } diff --git a/Mage/src/mage/filter/common/FilterCreatureForCombat.java b/Mage/src/mage/filter/common/FilterCreatureForCombat.java index 4a62c4cc5db..fccfc8cead6 100644 --- a/Mage/src/mage/filter/common/FilterCreatureForCombat.java +++ b/Mage/src/mage/filter/common/FilterCreatureForCombat.java @@ -32,7 +32,7 @@ package mage.filter.common; * * @author BetaSteward_at_googlemail.com */ -public class FilterCreatureForCombat extends FilterCreaturePermanent { +public class FilterCreatureForCombat extends FilterCreaturePermanent { public FilterCreatureForCombat() { this(""); @@ -50,4 +50,13 @@ public class FilterCreatureForCombat extends FilterCreaturePermanent { this.usePhased = true; } + public FilterCreatureForCombat(final FilterCreatureForCombat filter) { + super(filter); + } + + @Override + public FilterCreatureForCombat copy() { + return new FilterCreatureForCombat(this); + } + } diff --git a/Mage/src/mage/filter/common/FilterCreatureOrPlayer.java b/Mage/src/mage/filter/common/FilterCreatureOrPlayer.java index eb1f6f926bf..f82db852b66 100644 --- a/Mage/src/mage/filter/common/FilterCreatureOrPlayer.java +++ b/Mage/src/mage/filter/common/FilterCreatureOrPlayer.java @@ -39,7 +39,7 @@ import mage.players.Player; * * @author BetaSteward_at_googlemail.com */ -public class FilterCreatureOrPlayer extends FilterImpl implements Filter { +public class FilterCreatureOrPlayer extends FilterImpl implements Filter { protected FilterCreaturePermanent creatureFilter; protected FilterPlayer playerFilter; @@ -59,7 +59,13 @@ public class FilterCreatureOrPlayer extends FilterImpl implements Filter creatureFilter = new FilterCreaturePermanent(); playerFilter = new FilterPlayer(); } - + + public FilterCreatureOrPlayer(final FilterCreatureOrPlayer filter) { + super(filter); + this.creatureFilter = filter.creatureFilter.copy(); + this.playerFilter = filter.playerFilter.copy(); + } + @Override public boolean match(Object o) { if (o instanceof Player) { @@ -68,7 +74,12 @@ public class FilterCreatureOrPlayer extends FilterImpl implements Filter else if (o instanceof Permanent) { return creatureFilter.match((Permanent)o); } - return false; + return notFilter; + } + + @Override + public FilterCreatureOrPlayer copy() { + return new FilterCreatureOrPlayer(this); } } diff --git a/Mage/src/mage/filter/common/FilterCreaturePermanent.java b/Mage/src/mage/filter/common/FilterCreaturePermanent.java index 70fbf09388b..c0a456dd6f7 100644 --- a/Mage/src/mage/filter/common/FilterCreaturePermanent.java +++ b/Mage/src/mage/filter/common/FilterCreaturePermanent.java @@ -36,11 +36,11 @@ import mage.game.permanent.Permanent; * * @author BetaSteward_at_googlemail.com */ -public class FilterCreaturePermanent extends FilterPermanent { +public class FilterCreaturePermanent> extends FilterPermanent> { - protected boolean useAttacking = false; + protected boolean useAttacking; protected boolean attacking; - protected boolean useBlocking = false; + protected boolean useBlocking; protected boolean blocking; public FilterCreaturePermanent() { @@ -52,17 +52,30 @@ public class FilterCreaturePermanent extends FilterPermanent { cardType.add(CardType.CREATURE); } + public FilterCreaturePermanent(final FilterCreaturePermanent filter) { + super(filter); + this.useAttacking = filter.useAttacking; + this.attacking = filter.attacking; + this.useBlocking = filter.useBlocking; + this.blocking = filter.blocking; + } + @Override public boolean match(Permanent permanent) { if (!super.match(permanent)) - return false; + return notFilter; if (useAttacking && permanent.isAttacking() != attacking) - return false; + return notFilter; if (useBlocking && permanent.isBlocking() != blocking) - return false; + return notFilter; - return true; + return !notFilter; + } + + @Override + public FilterCreaturePermanent copy() { + return new FilterCreaturePermanent(this); } } diff --git a/Mage/src/mage/filter/common/FilterEquipment.java b/Mage/src/mage/filter/common/FilterEquipment.java index 881e476d369..4fdb17f8185 100644 --- a/Mage/src/mage/filter/common/FilterEquipment.java +++ b/Mage/src/mage/filter/common/FilterEquipment.java @@ -35,7 +35,7 @@ import mage.filter.FilterPermanent; * * @author BetaSteward_at_googlemail.com */ -public class FilterEquipment extends FilterPermanent { +public class FilterEquipment extends FilterPermanent { public FilterEquipment() { this("equipment"); @@ -47,4 +47,12 @@ public class FilterEquipment extends FilterPermanent { this.getSubtype().add("Equipment"); } + public FilterEquipment(final FilterEquipment filter) { + super(filter); + } + + @Override + public FilterEquipment copy() { + return new FilterEquipment(this); + } } diff --git a/Mage/src/mage/filter/common/FilterFortification.java b/Mage/src/mage/filter/common/FilterFortification.java index a792da4fef4..e49b732821b 100644 --- a/Mage/src/mage/filter/common/FilterFortification.java +++ b/Mage/src/mage/filter/common/FilterFortification.java @@ -35,7 +35,7 @@ import mage.filter.FilterPermanent; * * @author BetaSteward_at_googlemail.com */ -public class FilterFortification extends FilterPermanent { +public class FilterFortification extends FilterPermanent { public FilterFortification() { this("fortification"); @@ -47,4 +47,12 @@ public class FilterFortification extends FilterPermanent { this.getSubtype().add("Fortification"); } + public FilterFortification(final FilterFortification filter) { + super(filter); + } + + @Override + public FilterFortification copy() { + return new FilterFortification(this); + } } diff --git a/Mage/src/mage/filter/common/FilterLandCard.java b/Mage/src/mage/filter/common/FilterLandCard.java index 3fff2152d01..739ddb455e2 100644 --- a/Mage/src/mage/filter/common/FilterLandCard.java +++ b/Mage/src/mage/filter/common/FilterLandCard.java @@ -35,7 +35,7 @@ import mage.filter.FilterCard; * * @author BetaSteward_at_googlemail.com */ -public class FilterLandCard extends FilterCard { +public class FilterLandCard extends FilterCard { public FilterLandCard() { this("land card"); @@ -46,4 +46,13 @@ public class FilterLandCard extends FilterCard { cardType.add(CardType.LAND); } + public FilterLandCard(final FilterLandCard filter) { + super(filter); + } + + @Override + public FilterLandCard copy() { + return new FilterLandCard(this); + } + } diff --git a/Mage/src/mage/filter/common/FilterLandPermanent.java b/Mage/src/mage/filter/common/FilterLandPermanent.java index d15b607af15..5e0bf6aa6e9 100644 --- a/Mage/src/mage/filter/common/FilterLandPermanent.java +++ b/Mage/src/mage/filter/common/FilterLandPermanent.java @@ -35,7 +35,7 @@ import mage.filter.FilterPermanent; * * @author BetaSteward_at_googlemail.com */ -public class FilterLandPermanent extends FilterPermanent { +public class FilterLandPermanent extends FilterPermanent { public FilterLandPermanent() { this("land"); @@ -46,4 +46,12 @@ public class FilterLandPermanent extends FilterPermanent { cardType.add(CardType.LAND); } + public FilterLandPermanent(final FilterLandPermanent filter) { + super(filter); + } + + @Override + public FilterLandPermanent copy() { + return new FilterLandPermanent(this); + } } diff --git a/Mage/src/mage/filter/common/FilterLegendaryPermanent.java b/Mage/src/mage/filter/common/FilterLegendaryPermanent.java index 9db69f0a038..d8ba7431e7f 100644 --- a/Mage/src/mage/filter/common/FilterLegendaryPermanent.java +++ b/Mage/src/mage/filter/common/FilterLegendaryPermanent.java @@ -34,16 +34,24 @@ import mage.filter.FilterPermanent; * * @author BetaSteward_at_googlemail.com */ -public class FilterLegendaryPermanent extends FilterPermanent { +public class FilterLegendaryPermanent extends FilterPermanent { public FilterLegendaryPermanent() { this("legend"); } - public FilterLegendaryPermanent(String name) { super(name); this.supertype.add("Legendary"); } + public FilterLegendaryPermanent(final FilterLegendaryPermanent filter) { + super(filter); + } + + @Override + public FilterLegendaryPermanent copy() { + return new FilterLegendaryPermanent(this); + } + } diff --git a/Mage/src/mage/filter/common/FilterNonlandCard.java b/Mage/src/mage/filter/common/FilterNonlandCard.java index 7fe23a9abf3..a0e24d3065b 100644 --- a/Mage/src/mage/filter/common/FilterNonlandCard.java +++ b/Mage/src/mage/filter/common/FilterNonlandCard.java @@ -35,7 +35,7 @@ import mage.filter.FilterCard; * * @author BetaSteward_at_googlemail.com */ -public class FilterNonlandCard extends FilterCard { +public class FilterNonlandCard extends FilterCard { public FilterNonlandCard() { this("non-land card"); @@ -47,4 +47,12 @@ public class FilterNonlandCard extends FilterCard { this.setNotCardType(true); } + public FilterNonlandCard(final FilterNonlandCard filter) { + super(filter); + } + + @Override + public FilterNonlandCard copy() { + return new FilterNonlandCard(this); + } } diff --git a/Mage/src/mage/filter/common/FilterNonlandPermanent.java b/Mage/src/mage/filter/common/FilterNonlandPermanent.java index a3dba54b43d..23a48a4f132 100644 --- a/Mage/src/mage/filter/common/FilterNonlandPermanent.java +++ b/Mage/src/mage/filter/common/FilterNonlandPermanent.java @@ -35,7 +35,7 @@ import mage.filter.FilterPermanent; * * @author BetaSteward_at_googlemail.com */ -public class FilterNonlandPermanent extends FilterPermanent { +public class FilterNonlandPermanent extends FilterPermanent { public FilterNonlandPermanent() { this("nonland permanent"); @@ -47,4 +47,13 @@ public class FilterNonlandPermanent extends FilterPermanent { cardType.add(CardType.LAND); } + public FilterNonlandPermanent(final FilterNonlandPermanent filter) { + super(filter); + } + + @Override + public FilterNonlandPermanent copy() { + return new FilterNonlandPermanent(this); + } + } diff --git a/Mage/src/mage/filter/common/FilterPlaneswalkerOrPlayer.java b/Mage/src/mage/filter/common/FilterPlaneswalkerOrPlayer.java index 1145e2f6aa8..74ae59d6a82 100644 --- a/Mage/src/mage/filter/common/FilterPlaneswalkerOrPlayer.java +++ b/Mage/src/mage/filter/common/FilterPlaneswalkerOrPlayer.java @@ -28,7 +28,6 @@ package mage.filter.common; -import java.util.List; import java.util.Set; import java.util.UUID; import mage.filter.Filter; @@ -41,17 +40,25 @@ import mage.players.Player; * * @author BetaSteward_at_googlemail.com */ -public class FilterPlaneswalkerOrPlayer extends FilterImpl implements Filter { +public class FilterPlaneswalkerOrPlayer extends FilterImpl implements Filter { - protected FilterPlaneswalkerPermanent planeswalkerFilter = new FilterPlaneswalkerPermanent(); - protected FilterPlayer playerFilter = new FilterPlayer(); + protected FilterPlaneswalkerPermanent planeswalkerFilter; + protected FilterPlayer playerFilter; public FilterPlaneswalkerOrPlayer(Set defenders) { super("planeswalker or player"); + planeswalkerFilter = new FilterPlaneswalkerPermanent(); planeswalkerFilter.getControllerId().addAll(defenders); + playerFilter = new FilterPlayer(); playerFilter.getPlayerId().addAll(defenders); } + public FilterPlaneswalkerOrPlayer(final FilterPlaneswalkerOrPlayer filter) { + super(filter); + this.planeswalkerFilter = filter.planeswalkerFilter.copy(); + this.playerFilter = filter.playerFilter.copy(); + } + @Override public boolean match(Object o) { if (o instanceof Player) { @@ -60,6 +67,11 @@ public class FilterPlaneswalkerOrPlayer extends FilterImpl implements Fi else if (o instanceof Permanent) { return planeswalkerFilter.match((Permanent)o); } - return false; + return notFilter; + } + + @Override + public FilterPlaneswalkerOrPlayer copy() { + return new FilterPlaneswalkerOrPlayer(this); } } \ No newline at end of file diff --git a/Mage/src/mage/filter/common/FilterPlaneswalkerPermanent.java b/Mage/src/mage/filter/common/FilterPlaneswalkerPermanent.java index 912ff38896c..9ad4c9841b5 100644 --- a/Mage/src/mage/filter/common/FilterPlaneswalkerPermanent.java +++ b/Mage/src/mage/filter/common/FilterPlaneswalkerPermanent.java @@ -35,16 +35,23 @@ import mage.filter.FilterPermanent; * * @author BetaSteward_at_googlemail.com */ -public class FilterPlaneswalkerPermanent extends FilterPermanent { +public class FilterPlaneswalkerPermanent extends FilterPermanent { public FilterPlaneswalkerPermanent() { this("planeswalker"); } - public FilterPlaneswalkerPermanent(String name) { super(name); cardType.add(CardType.PLANESWALKER); } + public FilterPlaneswalkerPermanent(final FilterPlaneswalkerPermanent filter) { + super(filter); + } + + @Override + public FilterPlaneswalkerPermanent copy() { + return new FilterPlaneswalkerPermanent(this); + } } diff --git a/Mage/src/mage/game/Exile.java b/Mage/src/mage/game/Exile.java index 4cf82d44b88..84baeaa6bab 100644 --- a/Mage/src/mage/game/Exile.java +++ b/Mage/src/mage/game/Exile.java @@ -35,12 +35,13 @@ import java.util.Map; import java.util.UUID; import mage.cards.Card; import mage.game.events.GameEvent; +import mage.util.Copyable; /** * * @author BetaSteward_at_googlemail.com */ -public class Exile implements Serializable { +public class Exile implements Serializable, Copyable { private static final UUID PERMANENT = UUID.randomUUID(); @@ -50,6 +51,12 @@ public class Exile implements Serializable { createZone(PERMANENT, "Permanent"); } + public Exile(final Exile exile) { + for (UUID exileId: exile.exileZones.keySet()) { + exileZones.put(exileId, exile.exileZones.get(exileId).copy()); + } + } + public Collection getExileZones() { return exileZones.values(); } @@ -80,13 +87,16 @@ public class Exile implements Serializable { } } - public Card getCard(UUID cardId) { - Card card; + public Card getCard(UUID cardId, Game game) { for (ExileZone exile: exileZones.values()) { - card = exile.get(cardId); - if (card != null) - return card; + if (exile.contains(cardId)) + return game.getCard(cardId); } return null; } + + @Override + public Exile copy() { + return new Exile(this); + } } diff --git a/Mage/src/mage/game/ExileZone.java b/Mage/src/mage/game/ExileZone.java index b1fe749dadf..4d35805dc36 100644 --- a/Mage/src/mage/game/ExileZone.java +++ b/Mage/src/mage/game/ExileZone.java @@ -41,7 +41,7 @@ public class ExileZone extends CardsImpl implements Serializable { private UUID id; private String name; - private boolean hidden = false; + private boolean hidden; public ExileZone(UUID id, String name) { this(id, name, false); @@ -54,6 +54,13 @@ public class ExileZone extends CardsImpl implements Serializable { this.hidden = hidden; } + public ExileZone(final ExileZone zone) { + super(zone); + this.id = zone.id; + this.name = zone.name; + this.hidden = zone.hidden; + } + public UUID getId() { return id; } @@ -65,4 +72,9 @@ public class ExileZone extends CardsImpl implements Serializable { public boolean isHidden() { return hidden; } + + @Override + public ExileZone copy() { + return new ExileZone(this); + } } diff --git a/Mage/src/mage/game/Game.java b/Mage/src/mage/game/Game.java index 4288d30bc54..8bd87a0b790 100644 --- a/Mage/src/mage/game/Game.java +++ b/Mage/src/mage/game/Game.java @@ -38,6 +38,7 @@ import java.util.UUID; import mage.Constants.MultiplayerAttackOption; import mage.Constants.RangeOfInfluence; import mage.MageItem; +import mage.abilities.Ability; import mage.abilities.ActivatedAbility; import mage.abilities.TriggeredAbilities; import mage.abilities.TriggeredAbility; @@ -45,6 +46,7 @@ import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.ContinuousEffects; import mage.cards.Cards; import mage.choices.Choice; +import mage.choices.ChoiceImpl; import mage.game.combat.Combat; import mage.game.events.GameEvent; import mage.game.events.TableEvent; @@ -52,6 +54,8 @@ import mage.game.events.Listener; import mage.game.events.PlayerQueryEvent; import mage.game.permanent.Battlefield; import mage.game.permanent.Permanent; +import mage.game.turn.Phase; +import mage.game.turn.Step; import mage.game.turn.Turn; import mage.players.Player; import mage.players.PlayerList; @@ -66,6 +70,7 @@ public interface Game extends MageItem, Serializable { public MultiplayerAttackOption getAttackOption(); //game data methods + public void loadCards(Set cards, UUID ownerId); public Object getCustomData(); public void setCustomData(Object data); public MageObject getObject(UUID objectId); @@ -77,6 +82,8 @@ public interface Game extends MageItem, Serializable { public PlayerList getPlayerList(); public Set getOpponents(UUID playerId); public Turn getTurn(); + public Phase getPhase(); + public Step getStep(); public int getTurnNum(); public boolean isMainPhase(); public boolean canPlaySorcery(UUID playerId); @@ -87,10 +94,12 @@ public interface Game extends MageItem, Serializable { public SpellStack getStack(); public Exile getExile(); public Combat getCombat(); - public GameStates getGameStates(); public GameState getState(); public String getWinner(); public ContinuousEffects getContinuousEffects(); + public GameStates getGameStates(); + public void loadGameStates(GameStates states); + public Game copy(); //client event methods public void addTableEventListener(Listener listener); @@ -124,7 +133,7 @@ public interface Game extends MageItem, Serializable { public void quit(UUID playerId); public void concede(UUID playerId); public void emptyManaPools(); - public void addEffect(ContinuousEffect continuousEffect); + public void addEffect(ContinuousEffect continuousEffect, Ability source); public void addTriggeredAbility(TriggeredAbility ability); public void applyEffects(); public boolean checkStateAndTriggered(); diff --git a/Mage/src/mage/game/GameImpl.java b/Mage/src/mage/game/GameImpl.java index 197540bbe86..ab428cad165 100644 --- a/Mage/src/mage/game/GameImpl.java +++ b/Mage/src/mage/game/GameImpl.java @@ -32,9 +32,14 @@ import java.io.IOException; import mage.game.stack.SpellStack; import java.io.Serializable; import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.Random; +import java.util.Set; import java.util.Stack; import java.util.UUID; +import java.util.logging.Logger; import mage.Constants.CardType; import mage.Constants.MultiplayerAttackOption; import mage.Constants.Outcome; @@ -42,6 +47,7 @@ import mage.Constants.PhaseStep; import mage.Constants.RangeOfInfluence; import mage.Constants.Zone; import mage.MageObject; +import mage.abilities.Ability; import mage.abilities.ActivatedAbility; import mage.abilities.TriggeredAbilities; import mage.abilities.TriggeredAbility; @@ -50,6 +56,7 @@ import mage.abilities.effects.ContinuousEffects; import mage.cards.Card; import mage.cards.Cards; import mage.choices.Choice; +import mage.choices.ChoiceImpl; import mage.filter.Filter.ComparisonScope; import mage.filter.common.FilterEquipment; import mage.filter.common.FilterFortification; @@ -66,34 +73,64 @@ import mage.game.events.PlayerQueryEvent; import mage.game.events.PlayerQueryEventSource; import mage.game.permanent.Battlefield; import mage.game.permanent.Permanent; +import mage.game.stack.StackObject; +import mage.game.turn.Phase; +import mage.game.turn.Step; import mage.game.turn.Turn; import mage.players.PlayerList; import mage.players.Players; import mage.target.TargetPlayer; +import mage.util.Logging; -public abstract class GameImpl implements Game, Serializable { +public abstract class GameImpl> implements Game, Serializable { + + private final static transient Logger logger = Logging.getLogger(GameImpl.class.getName()); + + private static FilterPlaneswalkerPermanent filterPlaneswalker = new FilterPlaneswalkerPermanent(); + private static FilterLegendaryPermanent filterLegendary = new FilterLegendaryPermanent(); + private static FilterLegendaryPermanent filterLegendName = new FilterLegendaryPermanent(); + private static FilterEquipment filterEquipment = new FilterEquipment(); + private static FilterFortification filterFortification = new FilterFortification(); private transient Stack savedStates = new Stack(); - private Object customData; + private transient Object customData; - protected UUID id; - protected boolean ready = false; + protected final UUID id; + protected boolean ready; protected transient TableEventSource tableEventSource = new TableEventSource(); protected transient PlayerQueryEventSource playerQueryEventSource = new PlayerQueryEventSource(); + protected Map gameCards = new HashMap(); protected GameState state; protected UUID startingPlayerId; protected UUID choosingPlayerId; - protected Player winner; + protected UUID winnerId; protected transient GameStates gameStates = new GameStates(); protected RangeOfInfluence range; protected MultiplayerAttackOption attackOption; + @Override + public abstract T copy(); + public GameImpl(MultiplayerAttackOption attackOption, RangeOfInfluence range) { - id = UUID.randomUUID(); + this.id = UUID.randomUUID(); this.range = range; this.attackOption = attackOption; - state = new GameState(); + this.state = new GameState(); + } + + public GameImpl(final GameImpl game) { + this.id = game.id; + this.ready = game.ready; + this.startingPlayerId = game.startingPlayerId; + this.choosingPlayerId = game.choosingPlayerId; + this.winnerId = game.winnerId; + this.range = game.range; + this.attackOption = game.attackOption; + this.state = game.state.copy(); + for (UUID cardId: game.gameCards.keySet()) { + this.gameCards.put(cardId, game.gameCards.get(cardId).copy()); + } } @Override @@ -111,6 +148,14 @@ public abstract class GameImpl implements Game, Serializable { this.customData = data; } + @Override + public void loadCards(Set cards, UUID ownerId) { + for (Card card: cards) { + card.setOwnerId(ownerId); + gameCards.put(card.getId(), card); + } + } + @Override public void addPlayer(Player player) throws GameException { state.addPlayer(player); @@ -133,7 +178,23 @@ public abstract class GameImpl implements Game, Serializable { @Override public MageObject getObject(UUID objectId) { - return state.getObject(objectId); + MageObject object; + if (state.getBattlefield().containsPermanent(objectId)) { + object = state.getBattlefield().getPermanent(objectId); + object.setZone(Zone.BATTLEFIELD); + return object; + } + object = getCard(objectId); + if (object != null) + return object; + for (StackObject item: state.getStack()) { + if (item.getId().equals(objectId)) { + item.setZone(Zone.STACK); + return item; + } + } + + return null; } @Override @@ -143,13 +204,18 @@ public abstract class GameImpl implements Game, Serializable { @Override public Card getCard(UUID cardId) { - return state.getCard(cardId); + return gameCards.get(cardId); } @Override public GameStates getGameStates() { return gameStates; } + + @Override + public void loadGameStates(GameStates states) { + this.gameStates = states; + } @Override public void saveState() { @@ -178,9 +244,9 @@ public abstract class GameImpl implements Game, Serializable { @Override public String getWinner() { - if (winner == null) + if (winnerId == null) return "Game is a draw"; - return "Player " + winner.getName() + " is the winner"; + return "Player " + state.getPlayer(winnerId).getName() + " is the winner"; } @Override @@ -190,7 +256,9 @@ public abstract class GameImpl implements Game, Serializable { @Override public void bookmarkState() { - savedStates.push(gameStates.getSize()); + saveState(); + logger.fine("Bookmarking state: " + gameStates.getSize()); + savedStates.push(gameStates.getSize() - 1); } @Override @@ -223,7 +291,7 @@ public abstract class GameImpl implements Game, Serializable { player = players.getNext(this); } - winner = findWinner(); + winnerId = findWinner(); saveState(); } @@ -247,8 +315,8 @@ public abstract class GameImpl implements Game, Serializable { targetPlayer.setRequired(true); targetPlayer.setTargetName("starting player"); Player choosingPlayer = getPlayer(pickChoosingPlayer()); - if (choosingPlayer.chooseTarget(Outcome.Benefit, targetPlayer, this)) { - startingPlayerId = targetPlayer.getTargets().get(0); + if (choosingPlayer.chooseTarget(Outcome.Benefit, targetPlayer, null, this)) { + startingPlayerId = ((List)targetPlayer.getTargets()).get(0); fireInformEvent(state.getPlayer(startingPlayerId).getName() + " will start"); } else { @@ -276,10 +344,10 @@ public abstract class GameImpl implements Game, Serializable { } - protected Player findWinner() { + protected UUID findWinner() { for (Player player: state.getPlayers().values()) { if (player.hasWon() || (!player.hasLost() && !player.hasLeft())) { - return player; + return player.getId(); } } return null; @@ -316,7 +384,7 @@ public abstract class GameImpl implements Game, Serializable { public void mulligan(UUID playerId) { Player player = getPlayer(playerId); int numCards = player.getHand().size(); - player.getLibrary().addAll(player.getHand()); + player.getLibrary().addAll(player.getHand().getCards(this)); player.getHand().clear(); player.shuffleLibrary(this); player.drawCards(numCards - 1, this); @@ -357,7 +425,6 @@ public abstract class GameImpl implements Game, Serializable { player.priority(this); if (isGameOver()) return; applyEffects(); - saveState(); } if (isGameOver()) return; if (allPassed()) { @@ -367,7 +434,6 @@ public abstract class GameImpl implements Game, Serializable { applyEffects(); state.getPlayers().resetPassed(); fireUpdatePlayersEvent(); - saveState(); break; } else @@ -401,8 +467,8 @@ public abstract class GameImpl implements Game, Serializable { } @Override - public void addEffect(ContinuousEffect continuousEffect) { - state.addEffect(continuousEffect); + public void addEffect(ContinuousEffect continuousEffect, Ability source) { + state.addEffect(continuousEffect, source); } @Override @@ -414,9 +480,9 @@ public abstract class GameImpl implements Game, Serializable { public boolean checkStateAndTriggered() { boolean somethingHappened = false; //20091005 - 115.5 - while (true) { + while (!this.isGameOver()) { if (!checkStateBasedActions() ) { - if (!checkTriggered()) { + if (this.isGameOver() || !checkTriggered()) { break; } } @@ -454,7 +520,7 @@ public abstract class GameImpl implements Game, Serializable { for (Player player: state.getPlayers().values()) { if (!player.hasLost() && (player.getLife() <= 0 || player.isEmptyDraw() || player.getCounters().getCount("Poison") >= 10)) { player.lost(this); - somethingHappened = true; + return false; } } for (Permanent perm: getBattlefield().getAllActivePermanents(CardType.CREATURE)) { @@ -477,7 +543,6 @@ public abstract class GameImpl implements Game, Serializable { } } //20091005 - 704.5j, 801.14 - FilterPlaneswalkerPermanent filterPlaneswalker = new FilterPlaneswalkerPermanent(); if (getBattlefield().countAll(filterPlaneswalker) > 1) { //don't bother checking if less than 2 planeswalkers in play for (Permanent planeswalker: getBattlefield().getAllActivePermanents(CardType.PLANESWALKER)) { for (String planeswalkertype: planeswalker.getSubtype()) { @@ -494,10 +559,9 @@ public abstract class GameImpl implements Game, Serializable { } } //20091005 - 704.5k, 801.12 - FilterLegendaryPermanent filterLegendary = new FilterLegendaryPermanent(); if (getBattlefield().countAll(filterLegendary) > 1) { //don't bother checking if less than 2 legends in play for (Permanent legend: getBattlefield().getAllActivePermanents(filterLegendary)) { - FilterLegendaryPermanent filterLegendName = new FilterLegendaryPermanent(); + filterLegendName.getName().clear(); filterLegendName.getName().add(legend.getName()); if (getBattlefield().count(filterLegendName, legend.getControllerId(), this) > 1) { for (Permanent dupLegend: getBattlefield().getActivePermanents(filterLegendName, legend.getControllerId(), this)) { @@ -508,7 +572,7 @@ public abstract class GameImpl implements Game, Serializable { } } //20091005 - 704.5p - for (Permanent perm: getBattlefield().getAllActivePermanents(new FilterEquipment())) { + for (Permanent perm: getBattlefield().getAllActivePermanents(filterEquipment)) { if (perm.getAttachedTo() != null) { Permanent creature = getPermanent(perm.getAttachedTo()); if (creature == null) { @@ -520,7 +584,7 @@ public abstract class GameImpl implements Game, Serializable { } } } - for (Permanent perm: getBattlefield().getAllActivePermanents(new FilterFortification())) { + for (Permanent perm: getBattlefield().getAllActivePermanents(filterFortification)) { if (perm.getAttachedTo() != null) { Permanent land = getPermanent(perm.getAttachedTo()); if (land == null) { @@ -625,12 +689,12 @@ public abstract class GameImpl implements Game, Serializable { @Override public void fireChooseEvent(UUID playerId, Choice choice) { - playerQueryEventSource.choose(playerId, choice.getMessage(), choice.getChoices().toArray(new String[0])); + playerQueryEventSource.choose(playerId, choice.getMessage(), ((List)choice.getChoices()).toArray(new String[0])); } @Override public void informPlayers(String message) { - state.addMessage(message); +// state.addMessage(message); fireInformEvent(message); } @@ -659,15 +723,15 @@ public abstract class GameImpl implements Game, Serializable { return state.getTurn(); } -// @Override -// public TurnPhase getPhase() { -// return state.getPhase(); -// } -// -// @Override -// public PhaseStep getStep() { -// return state.getStep(); -// } + @Override + public Phase getPhase() { + return state.getTurn().getPhase(); + } + + @Override + public Step getStep() { + return state.getTurn().getStep(); + } @Override public Battlefield getBattlefield() { @@ -737,6 +801,7 @@ public abstract class GameImpl implements Game, Serializable { } private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { + //initialize transient objects during deserialization in.defaultReadObject(); savedStates = new Stack(); tableEventSource = new TableEventSource(); diff --git a/Mage/src/mage/game/GameState.java b/Mage/src/mage/game/GameState.java index 1a2627d573c..b26a3a0537d 100644 --- a/Mage/src/mage/game/GameState.java +++ b/Mage/src/mage/game/GameState.java @@ -29,18 +29,14 @@ package mage.game; import mage.abilities.TriggeredAbility; -import mage.cards.Card; import mage.game.events.GameEvent; import mage.game.stack.SpellStack; -import mage.game.stack.StackObject; import java.io.Serializable; -import java.util.ArrayList; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.UUID; import mage.Constants.Zone; -import mage.MageObject; +import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbilities; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.SpecialActions; @@ -50,11 +46,13 @@ import mage.abilities.effects.ContinuousEffects; import mage.game.combat.Combat; import mage.game.permanent.Battlefield; import mage.game.permanent.Permanent; +import mage.game.stack.StackObject; import mage.game.turn.Turn; import mage.game.turn.TurnMods; import mage.players.Player; import mage.players.PlayerList; import mage.players.Players; +import mage.util.Copyable; import mage.watchers.Watchers; /** @@ -67,33 +65,103 @@ import mage.watchers.Watchers; * these will always remain constant throughout its lifetime * */ -public class GameState implements Serializable { +public class GameState implements Serializable, Copyable { - private Players players = new Players(); - private PlayerList playerList = new PlayerList(); + private Players players; + private PlayerList playerList; private UUID activePlayerId; private UUID priorityPlayerId; - private Turn turn = new Turn(); - private SpellStack stack = new SpellStack(); - private Exile exile = new Exile(); - private Battlefield battlefield = new Battlefield(); - private int turnNum = 0; - private boolean gameOver = false; - private List messages = new ArrayList(); - private ContinuousEffects effects = new ContinuousEffects(); - private TriggeredAbilities triggers = new TriggeredAbilities(); - private DelayedTriggeredAbilities delayed = new DelayedTriggeredAbilities(); - private SpecialActions specialActions = new SpecialActions(); - private Combat combat = new Combat(); - private TurnMods turnMods = new TurnMods(); - private Watchers watchers = new Watchers(); + private Turn turn; + private SpellStack stack; + private Exile exile; + private Battlefield battlefield; + private int turnNum; + private boolean gameOver; +// private List messages = new ArrayList(); + private ContinuousEffects effects; + private TriggeredAbilities triggers; + private DelayedTriggeredAbilities delayed; + private SpecialActions specialActions; + private Combat combat; + private TurnMods turnMods; + private Watchers watchers; private Map values = new HashMap(); + public GameState() { + players = new Players(); + playerList = new PlayerList(); + turn = new Turn(); + stack = new SpellStack(); + exile = new Exile(); + battlefield = new Battlefield(); + effects = new ContinuousEffects(); + triggers = new TriggeredAbilities(); + delayed = new DelayedTriggeredAbilities(); + specialActions = new SpecialActions(); + combat = new Combat(); + turnMods = new TurnMods(); + watchers = new Watchers(); + } + + public GameState(final GameState state) { + this.players = state.players.copy(); + this.playerList = state.playerList.copy(); + this.activePlayerId = state.activePlayerId; + this.priorityPlayerId = state.priorityPlayerId; + this.turn = state.turn.copy(); + this.stack = state.stack.copy(); + this.exile = state.exile.copy(); + this.battlefield = state.battlefield.copy(); + this.turnNum = state.turnNum; + this.gameOver = state.gameOver; + this.effects = state.effects.copy(); + this.triggers = state.triggers.copy(); + this.delayed = state.delayed.copy(); + this.specialActions = state.specialActions.copy(); + this.combat = state.combat.copy(); + this.turnMods = state.turnMods.copy(); + this.watchers = state.watchers.copy(); + for (String key: state.values.keySet()) { + values.put(key, state.values.get(key)); + //TODO: might have to change value to Copyable + } + } + + @Override + public GameState copy() { + return new GameState(this); + } + public void addPlayer(Player player) { players.put(player.getId(), player); playerList.add(player.getId()); } - + + public int getValue() { +// final int prime = 31; +// int result = 1; +// result *= prime + turnNum; +// result *= prime + turn.getPhaseType().toString().hashCode(); + + StringBuilder sb = new StringBuilder(1024); + + sb.append(turnNum).append(turn.getPhaseType()).append(turn.getStepType()).append(activePlayerId).append(priorityPlayerId); + + for (Player player: players.values()) { + sb.append("player").append(player.getLife()).append(player.getHand()).append(player.getLibrary().getCardList()); + } + + for (UUID permanentId: battlefield.getAllPermanentIds()) { + sb.append("permanent").append(permanentId); + } + + for (StackObject spell: stack) { + sb.append("spell").append(spell.getId()); + } + + return sb.toString().hashCode(); + } + public Players getPlayers() { return players; } @@ -170,7 +238,7 @@ public class GameState implements Serializable { for (Player player: players.values()) { player.reset(); } - battlefield.reset(); + battlefield.reset(game); effects.apply(game); } @@ -179,13 +247,13 @@ public class GameState implements Serializable { applyEffects(game); } - public void addEffect(ContinuousEffect effect) { - effects.addEffect(effect); + public void addEffect(ContinuousEffect effect, Ability source) { + effects.addEffect(effect, source); } - public void addMessage(String message) { - this.messages.add(message); - } +// public void addMessage(String message) { +// this.messages.add(message); +// } public PlayerList getPlayerList() { return playerList; @@ -201,43 +269,6 @@ public class GameState implements Serializable { return newPlayerList; } - public MageObject getObject(UUID objectId) { - MageObject object; - if (battlefield.containsPermanent(objectId)) { - object = battlefield.getPermanent(objectId); - object.setZone(Zone.BATTLEFIELD); - return object; - } - object = getCard(objectId); - if (object != null) - return object; - for (StackObject item: stack) { - if (item.getId().equals(objectId)) { - item.setZone(Zone.STACK); - return item; - } - } - - return null; - } - - public Card getCard(UUID cardId) { - Card card; - for(Player player: players.values()) { - if (player.getHand().containsKey(cardId)) { - card = player.getHand().get(cardId); - card.setZone(Zone.HAND); - return card; - } - if (player.getGraveyard().containsKey(cardId)) { - card = player.getGraveyard().get(cardId); - card.setZone(Zone.GRAVEYARD); - return card; - } - } - return this.exile.getCard(cardId); - } - public Permanent getPermanent(UUID permanentId) { Permanent permanent; if (battlefield.containsPermanent(permanentId)) { @@ -262,6 +293,7 @@ public class GameState implements Serializable { } public void handleEvent(GameEvent event, Game game) { + watchers.watch(event, game); if (!replaceEvent(event, game)) { for (Player player: players.values()) { player.checkTriggers(event, game); @@ -271,7 +303,6 @@ public class GameState implements Serializable { stack.checkTriggers(event, game); delayed.checkTriggers(event, game); exile.checkTriggers(event, game); - watchers.watch(event, game); } public boolean replaceEvent(GameEvent event, Game game) { @@ -311,4 +342,5 @@ public class GameState implements Serializable { public void setValue(String valueId, Object value) { values.put(valueId, value); } + } diff --git a/Mage/src/mage/game/GameStates.java b/Mage/src/mage/game/GameStates.java index 437fd8bf67a..5f118eeab51 100644 --- a/Mage/src/mage/game/GameStates.java +++ b/Mage/src/mage/game/GameStates.java @@ -31,7 +31,9 @@ package mage.game; import java.io.Serializable; import java.util.LinkedList; import java.util.List; +import java.util.logging.Logger; import mage.util.Copier; +import mage.util.Logging; /** * @@ -39,10 +41,15 @@ import mage.util.Copier; */ public class GameStates implements Serializable { - private List states = new LinkedList(); + private final static transient Logger logger = Logging.getLogger(GameStates.class.getName()); + +// private List states = new LinkedList(); + private List states = new LinkedList(); public void save(GameState gameState) { - states.add(new Copier().copyCompressed(gameState)); +// states.add(new Copier().copyCompressed(gameState)); + states.add(gameState.copy()); + logger.fine("Saved game state: " + states.size()); } public int getSize() { @@ -50,18 +57,22 @@ public class GameStates implements Serializable { } public GameState rollback(int index) { - if (index < states.size()) { - while (states.size() > index) { - states.remove(index); + if (states.size() > 0 && index < states.size()) { + while (states.size() > index + 1) { + states.remove(states.size() - 1); } - return new Copier().uncompressCopy(states.get(index - 1)); +// return new Copier().uncompressCopy(states.get(index)); + logger.fine("Rolling back state: " + index); + return states.get(index); } return null; } public GameState get(int index) { if (index < states.size()) - return new Copier().uncompressCopy(states.get(index)); +// return new Copier().uncompressCopy(states.get(index)); + return states.get(index); return null; } + } diff --git a/Mage/src/mage/game/Table.java b/Mage/src/mage/game/Table.java index 990de8b3713..464bb5f355e 100644 --- a/Mage/src/mage/game/Table.java +++ b/Mage/src/mage/game/Table.java @@ -89,17 +89,14 @@ public class Table implements Serializable { return validator.getName(); } - public UUID joinTable(Player player, int seatNum) throws GameException { - if (seatNum >= numSeats || seatNum < 0) { - throw new GameException("Invalid seat number"); - } - if (seats[seatNum].getPlayer() != null) { + public UUID joinTable(Player player, Seat seat) throws GameException { + if (seat.getPlayer() != null) { throw new GameException("Seat is occupied."); } - this.seats[seatNum].setPlayer(player); + seat.setPlayer(player); if (isReady()) state = TableState.STARTING; - return this.seats[seatNum].getPlayer().getId(); + return seat.getPlayer().getId(); } private boolean isReady() { @@ -114,6 +111,14 @@ public class Table implements Serializable { return seats; } + public Seat getNextAvailableSeat() { + for (int i = 0; i < numSeats; i++ ) { + if (seats[i].getPlayer() == null) + return seats[i]; + } + return null; + } + public void leaveTable(UUID playerId) { for (int i = 0; i < numSeats; i++ ) { if (seats[i].getPlayer().getId().equals(playerId)) diff --git a/Mage/src/mage/game/combat/Combat.java b/Mage/src/mage/game/combat/Combat.java index 806a9b60cc9..9fb4827325e 100644 --- a/Mage/src/mage/game/combat/Combat.java +++ b/Mage/src/mage/game/combat/Combat.java @@ -40,18 +40,31 @@ import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.players.Player; import mage.players.PlayerList; +import mage.util.Copyable; /** * * @author BetaSteward_at_googlemail.com */ -public class Combat implements Serializable { +public class Combat implements Serializable, Copyable { protected List groups = new ArrayList(); protected Set defenders = new HashSet(); protected UUID attackerId; + public Combat() {} + + public Combat(final Combat combat) { + this.attackerId = combat.attackerId; + for (CombatGroup group: combat.groups) { + groups.add(group.copy()); + } + for (UUID defenderId: combat.defenders) { + defenders.add(defenderId); + } + } + public List getGroups() { return groups; } @@ -82,6 +95,15 @@ public class Combat implements Serializable { attackerId = null; } + public int getValue(Game game) { + StringBuilder sb = new StringBuilder(); + sb.append(attackerId).append(defenders); + for (CombatGroup group: groups) { + sb.append(group.getValue(game)); + } + return sb.toString().hashCode(); + } + public void setAttacker(UUID playerId) { this.attackerId = playerId; } @@ -230,4 +252,13 @@ public class Combat implements Serializable { return false; } + public void damageAssignmentOrder(Game game) { + //TODO: set damage assignment order + } + + @Override + public Combat copy() { + return new Combat(this); + } + } diff --git a/Mage/src/mage/game/combat/CombatGroup.java b/Mage/src/mage/game/combat/CombatGroup.java index 63f2f27cbe2..9b0a7c56086 100644 --- a/Mage/src/mage/game/combat/CombatGroup.java +++ b/Mage/src/mage/game/combat/CombatGroup.java @@ -39,12 +39,13 @@ import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.players.Player; +import mage.util.Copyable; /** * * @author BetaSteward_at_googlemail.com */ -public class CombatGroup implements Serializable { +public class CombatGroup implements Serializable, Copyable { protected List attackers = new ArrayList(); protected List blockers = new ArrayList(); @@ -58,6 +59,37 @@ public class CombatGroup implements Serializable { this.defenderIsPlaneswalker = defenderIsPlaneswalker; } + public CombatGroup(final CombatGroup group) { + this.blocked = group.blocked; + this.defenderId = group.defenderId; + this.defenderIsPlaneswalker = group.defenderIsPlaneswalker; + for (UUID attackerId: group.attackers) { + this.attackers.add(attackerId); + } + for (UUID blockerId: group.blockers) { + this.blockers.add(blockerId); + } + for (UUID orderId: group.blockerOrder) { + this.blockerOrder.add(orderId); + } + } + + protected String getValue(Game game) { + StringBuilder sb = new StringBuilder(1024); + for (UUID attackerId: attackers) { + getPermanentValue(attackerId, sb, game); + } + for (UUID blockerId: blockers) { + getPermanentValue(blockerId, sb, game); + } + return sb.toString(); + } + + private void getPermanentValue(UUID permId, StringBuilder sb, Game game) { + Permanent perm = game.getPermanent(permId); + sb.append(perm.getValue()); + } + public boolean hasFirstOrDoubleStrike(Game game) { for (UUID permId: attackers) { if (hasFirstOrDoubleStrike(game.getPermanent(permId))) { @@ -219,6 +251,7 @@ public class CombatGroup implements Serializable { } game.getPermanent(blockerId).setBlocking(true); blockers.add(blockerId); + blockerOrder.add(blockerId); this.blocked = true; for (UUID attackerId: attackers) { game.fireEvent(GameEvent.getEvent(GameEvent.EventType.BLOCKER_DECLARED, blockerId, attackerId, playerId)); @@ -243,9 +276,15 @@ public class CombatGroup implements Serializable { } if (blockers.contains(creatureId)) { blockers.remove(creatureId); + //20100423 - 509.2a if (blockerOrder.contains(creatureId)) blockerOrder.remove(creatureId); } } + @Override + public CombatGroup copy() { + return new CombatGroup(this); + } + } diff --git a/Mage/src/mage/game/permanent/Battlefield.java b/Mage/src/mage/game/permanent/Battlefield.java index b091ff83a72..27cb747148e 100644 --- a/Mage/src/mage/game/permanent/Battlefield.java +++ b/Mage/src/mage/game/permanent/Battlefield.java @@ -31,7 +31,7 @@ package mage.game.permanent; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -49,11 +49,23 @@ import mage.game.events.GameEvent; */ public class Battlefield implements Serializable { - private Map field = new HashMap(); + private Map field = new LinkedHashMap(); - public void reset() { + public Battlefield () {} + + public Battlefield(final Battlefield battlefield) { + for (UUID permId: battlefield.field.keySet()) { + field.put(permId, battlefield.field.get(permId).copy()); + } + } + + public Battlefield copy() { + return new Battlefield(this); + } + + public void reset(Game game) { for (Permanent perm: field.values()) { - perm.reset(); + perm.reset(game); } } @@ -145,6 +157,10 @@ public class Battlefield implements Serializable { return field.values(); } + public Set getAllPermanentIds() { + return field.keySet(); + } + public List getAllActivePermanents() { List active = new ArrayList(); for (Permanent perm: field.values()) { diff --git a/Mage/src/mage/game/permanent/Permanent.java b/Mage/src/mage/game/permanent/Permanent.java index 22b9a2f963c..e8a279d4af1 100644 --- a/Mage/src/mage/game/permanent/Permanent.java +++ b/Mage/src/mage/game/permanent/Permanent.java @@ -69,13 +69,15 @@ public interface Permanent extends Card { public boolean canBeTargetedBy(MageObject source); public int getDamage(); public int damage(int damage, UUID sourceId, Game game); + public void removeAllDamage(Game game); public Counters getCounters(); - public void reset(); + public void reset(Game game); public boolean destroy(UUID sourceId, Game game, boolean noRegen); public boolean sacrifice(UUID sourceId, Game game); public void entersBattlefield(Game game); public boolean moveToZone(Zone zone, Game game, boolean sacrificed); public boolean moveToExile(UUID exileId, String name, Game game); + public String getValue(); @Override public void addAbility(Ability ability); @@ -99,4 +101,7 @@ public interface Permanent extends Card { public boolean removeFromCombat(Game game); public boolean isDeathtouched(); + @Override + public Permanent copy(); + } diff --git a/Mage/src/mage/game/permanent/PermanentCard.java b/Mage/src/mage/game/permanent/PermanentCard.java index db4eb8cab07..7035ee22672 100644 --- a/Mage/src/mage/game/permanent/PermanentCard.java +++ b/Mage/src/mage/game/permanent/PermanentCard.java @@ -28,10 +28,13 @@ package mage.game.permanent; +import java.util.ArrayList; +import java.util.List; import java.util.UUID; import mage.Constants.CardType; import mage.Constants.Zone; import mage.MageInt; +import mage.abilities.Ability; import mage.abilities.keyword.LevelAbility; import mage.cards.Card; import mage.cards.LevelerCard; @@ -42,49 +45,58 @@ import mage.game.events.ZoneChangeEvent; * * @author BetaSteward_at_googlemail.com */ -public class PermanentCard extends PermanentImpl { +public class PermanentCard extends PermanentImpl { - protected Card card; + protected String art; + protected List levelerRules; public PermanentCard(Card card, UUID controllerId) { - super(card.getOwnerId(), controllerId, card.getName()); - this.card = card; - this.objectId = card.getId(); - reset(); + super(card.getId(), card.getOwnerId(), controllerId, card.getName()); + copyFromCard(card); if (card.getCardType().contains(CardType.PLANESWALKER)) { this.loyalty = new MageInt(card.getLoyalty().getValue()); - } + } + if (card instanceof LevelerCard) { + levelerRules = ((LevelerCard)card).getRules(); + } + } + + public PermanentCard(final PermanentCard permanent) { + super(permanent); + this.art = permanent.art; } @Override - public void reset() { + public void reset(Game game) { // when the permanent is reset copy all original values from the card // must copy card each reset so that the original values don't get modified - Card copy = card.copy(); - this.name = copy.getName(); - this.abilities = copy.getAbilities(); + Card copy = game.getCard(objectId).copy(); + copyFromCard(copy); + super.reset(game); + } + + protected void copyFromCard(Card card) { + this.name = card.getName(); + this.abilities = card.getAbilities(); this.abilities.setControllerId(this.controllerId); - this.cardType = copy.getCardType(); - this.color = copy.getColor(); - this.manaCost = copy.getManaCost(); - this.power = copy.getPower(); - this.toughness = copy.getToughness(); + this.cardType = card.getCardType(); + this.color = card.getColor(); + this.manaCost = card.getManaCost(); + this.power = card.getPower(); + this.toughness = card.getToughness(); if (card instanceof LevelerCard) { LevelAbility level = ((LevelerCard)card).getLevel(this.getCounters().getCount("Level")); if (level != null) { this.power.setValue(level.getPower()); this.toughness.setValue(level.getToughness()); - this.abilities.addAll(level.getAbilities()); + for (Ability ability: level.getAbilities()) { + this.addAbility(ability); + } } } - this.subtype = copy.getSubtype(); - this.supertype = copy.getSupertype(); - this.art = copy.getArt(); - super.reset(); - } - - public Card getCard() { - return card; + this.subtype = card.getSubtype(); + this.supertype = card.getSupertype(); + this.art = card.getArt(); } @Override @@ -93,13 +105,13 @@ public class PermanentCard extends PermanentImpl { if (game.getPlayer(controllerId).removeFromBattlefield(this, game)) { switch (zone) { case GRAVEYARD: - game.getPlayer(ownerId).putInGraveyard(card, game, !sacrificed); + game.getPlayer(ownerId).putInGraveyard(game.getCard(objectId), game, !sacrificed); break; case HAND: - game.getPlayer(ownerId).getHand().add(card); + game.getPlayer(ownerId).getHand().add(game.getCard(objectId)); break; case EXILED: - game.getExile().getPermanentExile().add(card); + game.getExile().getPermanentExile().add(game.getCard(objectId)); break; } game.fireEvent(new ZoneChangeEvent(this.getId(), this.getControllerId(), Zone.BATTLEFIELD, zone)); @@ -114,10 +126,10 @@ public class PermanentCard extends PermanentImpl { if (!game.replaceEvent(new ZoneChangeEvent(this.getId(), this.getControllerId(), Zone.BATTLEFIELD, Zone.EXILED))) { if (game.getPlayer(controllerId).removeFromBattlefield(this, game)) { if (exileId == null) { - game.getExile().getPermanentExile().add(card); + game.getExile().getPermanentExile().add(game.getCard(objectId)); } else { - game.getExile().createZone(exileId, name).add(card); + game.getExile().createZone(exileId, name).add(game.getCard(objectId)); } game.fireEvent(new ZoneChangeEvent(this.getId(), this.getControllerId(), Zone.BATTLEFIELD, Zone.EXILED)); return true; @@ -125,4 +137,25 @@ public class PermanentCard extends PermanentImpl { } return false; } + + @Override + public String getArt() { + return art; + } + + @Override + public PermanentCard copy() { + return new PermanentCard(this); + } + + @Override + public List getRules() { + if (levelerRules == null) + return super.getRules(); + List rules = new ArrayList(); + rules.addAll(super.getRules()); + rules.addAll(levelerRules); + return rules; + } + } diff --git a/Mage/src/mage/game/permanent/PermanentImpl.java b/Mage/src/mage/game/permanent/PermanentImpl.java index 05221c63a29..a8f4b0de6ed 100644 --- a/Mage/src/mage/game/permanent/PermanentImpl.java +++ b/Mage/src/mage/game/permanent/PermanentImpl.java @@ -43,10 +43,11 @@ import mage.abilities.effects.Effect; import mage.abilities.keyword.DeathtouchAbility; import mage.abilities.keyword.DefenderAbility; import mage.abilities.keyword.HasteAbility; -import mage.abilities.keyword.IndestructableAbility; +import mage.abilities.keyword.IndestructibleAbility; import mage.abilities.keyword.LifelinkAbility; import mage.abilities.keyword.ProtectionAbility; import mage.abilities.keyword.ShroudAbility; +import mage.cards.Card; import mage.cards.CardImpl; import mage.counters.Counters; import mage.game.Game; @@ -58,34 +59,71 @@ import mage.players.Player; * * @author BetaSteward_at_googlemail.com */ -public abstract class PermanentImpl extends CardImpl implements Permanent -{ - protected boolean tapped = false; - protected boolean flipped = false; +public abstract class PermanentImpl> extends CardImpl implements Permanent { + + protected boolean tapped; + protected boolean flipped; protected UUID controllerId; protected int damage; - protected boolean controlledFromStartOfTurn = false; - protected int turnsOnBattlefield = 0; + protected boolean controlledFromStartOfTurn; + protected int turnsOnBattlefield; protected boolean phasedIn = true; protected boolean faceUp = true; - protected boolean attacking = false; - protected boolean blocking = false; - protected boolean loyaltyUsed = false; - protected boolean deathtouched = false; - protected Counters counters = new Counters(); + protected boolean attacking; + protected boolean blocking; + protected boolean loyaltyUsed; + protected boolean deathtouched; + protected Counters counters; protected List attachments = new ArrayList(); protected UUID attachedTo; public PermanentImpl(UUID ownerId, UUID controllerId, String name) { super(ownerId, name); this.controllerId = controllerId; + this.counters = new Counters(); + } + + public PermanentImpl(UUID id, UUID ownerId, UUID controllerId, String name) { + super(id, ownerId, name); + this.controllerId = controllerId; + this.counters = new Counters(); + } + + public PermanentImpl(final PermanentImpl permanent) { + super(permanent); + this.tapped = permanent.tapped; + this.flipped = permanent.flipped; + this.controllerId = permanent.controllerId; + this.damage = permanent.damage; + this.controlledFromStartOfTurn = permanent.controlledFromStartOfTurn; + this.turnsOnBattlefield = permanent.turnsOnBattlefield; + this.phasedIn = permanent.phasedIn; + this.faceUp = permanent.faceUp; + this.attacking = permanent.attacking; + this.blocking = permanent.blocking; + this.loyaltyUsed = permanent.loyaltyUsed; + this.deathtouched = permanent.deathtouched; + this.counters = permanent.counters.copy(); + for (UUID attachmentId: permanent.attachments) { + this.attachments.add(attachmentId); + } + this.attachedTo = permanent.attachedTo; } @Override - public void reset() { + public void reset(Game game) { this.controllerId = ownerId; } + @Override + public String getValue() { + StringBuilder sb = new StringBuilder(1024); + sb.append(controllerId).append(name).append(tapped).append(damage); + sb.append(subtype).append(supertype).append(power.getValue()).append(toughness.getValue()); + sb.append(abilities); + return sb.toString(); + } + @Override public void addAbility(Ability ability) { Ability copy = ability.copy(); @@ -335,14 +373,14 @@ public abstract class PermanentImpl extends CardImpl implements Permanent } @Override - public int damage(int damage, UUID sourceId, Game game) { + public int damage(int damageAmount, UUID sourceId, Game game) { int damageDone = 0; - if (damage > 0 && canDamage(game.getObject(sourceId))) { + if (damageAmount > 0 && canDamage(game.getObject(sourceId))) { if (cardType.contains(CardType.PLANESWALKER)) { - damageDone = damagePlaneswalker(damage, sourceId, game); + damageDone = damagePlaneswalker(damageAmount, sourceId, game); } else { - damageDone = damageCreature(damage, sourceId, game); + damageDone = damageCreature(damageAmount, sourceId, game); } if (damageDone > 0) { Permanent source = game.getPermanent(sourceId); @@ -358,6 +396,11 @@ public abstract class PermanentImpl extends CardImpl implements Permanent return damageDone; } + @Override + public void removeAllDamage(Game game) { + damage = 0; + } + protected int damagePlaneswalker(int damage, UUID sourceId, Game game) { GameEvent event = new GameEvent(GameEvent.EventType.DAMAGE_PLANESWALKER, objectId, sourceId, controllerId, damage); if (!game.replaceEvent(event)) { @@ -423,7 +466,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent for (StaticAbility ability: abilities.getStaticAbilities(Zone.BATTLEFIELD)) { for (Effect effect: ability.getEffects()) { if (effect instanceof ContinuousEffect) - game.addEffect((ContinuousEffect)effect); + game.addEffect((ContinuousEffect)effect, ability); } } } @@ -432,8 +475,8 @@ public abstract class PermanentImpl extends CardImpl implements Permanent public boolean destroy(UUID sourceId, Game game, boolean noRegen) { //20091005 - 701.6 //TODO: handle noRegen - if (!game.replaceEvent(GameEvent.getEvent(EventType.DESTROY_PERMANENT, objectId, sourceId, controllerId))) { - if (!this.getAbilities().containsKey(IndestructableAbility.getInstance().getId())) { + if (!game.replaceEvent(GameEvent.getEvent(EventType.DESTROY_PERMANENT, objectId, sourceId, controllerId, noRegen?1:0))) { + if (!this.getAbilities().containsKey(IndestructibleAbility.getInstance().getId())) { if (moveToZone(Zone.GRAVEYARD, game, false)) { game.fireEvent(GameEvent.getEvent(EventType.DESTROYED_PERMANENT, objectId, sourceId, controllerId)); return true; diff --git a/Mage/src/mage/game/permanent/PermanentToken.java b/Mage/src/mage/game/permanent/PermanentToken.java index 1a9dc0dbcb8..c64b90dd8fb 100644 --- a/Mage/src/mage/game/permanent/PermanentToken.java +++ b/Mage/src/mage/game/permanent/PermanentToken.java @@ -31,6 +31,7 @@ package mage.game.permanent; import mage.game.permanent.token.Token; import java.util.UUID; import mage.Constants.Zone; +import mage.abilities.Ability; import mage.game.Game; import mage.game.events.ZoneChangeEvent; @@ -38,28 +39,39 @@ import mage.game.events.ZoneChangeEvent; * * @author BetaSteward_at_googlemail.com */ -public class PermanentToken extends PermanentImpl { +public class PermanentToken extends PermanentImpl { protected Token token; public PermanentToken(Token token, UUID ownerId, UUID controllerId) { super(ownerId, controllerId, token.getName()); this.token = token; - reset(); + copyFromToken(token); + } + + public PermanentToken(final PermanentToken permanent) { + super(permanent); + this.token = permanent.token.copy(); } @Override - public void reset() { + public void reset(Game game) { Token copy = token.copy(); - this.name = copy.getName(); - this.abilities = copy.getAbilities(); - this.abilities.setControllerId(this.controllerId); - this.cardType = copy.getCardType(); - this.color = copy.getColor(); - this.power = copy.getPower(); - this.toughness = copy.getToughness(); - this.subtype = copy.getSubtype(); - super.reset(); + copyFromToken(copy); + super.reset(game); + } + + protected void copyFromToken(Token token) { + this.name = token.getName(); + this.abilities.clear(); + for (Ability ability: token.getAbilities()) { + this.addAbility(ability); + } + this.cardType = token.getCardType(); + this.color = token.getColor(); + this.power = token.getPower(); + this.toughness = token.getToughness(); + this.subtype = token.getSubtype(); } @Override @@ -84,4 +96,14 @@ public class PermanentToken extends PermanentImpl { return false; } + @Override + public String getArt() { + return ""; + } + + @Override + public PermanentToken copy() { + return new PermanentToken(this); + } + } diff --git a/Mage/src/mage/game/permanent/token/EldraziSpawnToken.java b/Mage/src/mage/game/permanent/token/EldraziSpawnToken.java new file mode 100644 index 00000000000..9987195a5bd --- /dev/null +++ b/Mage/src/mage/game/permanent/token/EldraziSpawnToken.java @@ -0,0 +1,56 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.game.permanent.token; + +import mage.Constants.CardType; +import mage.Constants.Zone; +import mage.MageInt; +import mage.Mana; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.effects.common.ManaEffect; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class EldraziSpawnToken extends Token { + + public EldraziSpawnToken() { + super("Eldrazi Spawn", "0/1 colorless Eldrazi Spawn creature with \"Sacrifice this creature: Add {1} to your mana pool.\""); + cardType.add(CardType.CREATURE); + subtype.add("Eldrazi"); + subtype.add("Spawn"); + power = new MageInt(0); + toughness = new MageInt(1); + addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new ManaEffect(Mana.ColorlessMana), new SacrificeSourceCost())); + } + + +} diff --git a/Mage/src/mage/game/permanent/token/Token.java b/Mage/src/mage/game/permanent/token/Token.java index 9b13c537ddb..fd01bd818e1 100644 --- a/Mage/src/mage/game/permanent/token/Token.java +++ b/Mage/src/mage/game/permanent/token/Token.java @@ -34,11 +34,8 @@ import mage.MageObjectImpl; import mage.ObjectColor; import mage.abilities.Abilities; import mage.abilities.Ability; -import mage.util.Copier; -public class Token extends MageObjectImpl { - - private static final transient Copier copier = new Copier(); +public class Token extends MageObjectImpl { protected String description; @@ -50,7 +47,7 @@ public class Token extends MageObjectImpl { public Token(String name, String description, ObjectColor color, List subtype, int power, int toughness, Abilities abilities) { this(name, description); this.cardType.add(CardType.CREATURE); - this.color = color; + this.color = color.copy(); this.subtype = subtype; this.power.setValue(power); this.toughness.setValue(toughness); @@ -59,16 +56,22 @@ public class Token extends MageObjectImpl { } } - public String getDescription() { - return description; + public Token(final Token token) { + super(token); + this.description = token.description; } - public Token copy() { - return copier.copy(this); + public String getDescription() { + return description; } public void addAbility(Ability ability) { ability.setSourceId(this.getId()); abilities.add(ability); } + + @Override + public Token copy() { + return new Token(this); + } } diff --git a/Mage/src/mage/game/stack/Spell.java b/Mage/src/mage/game/stack/Spell.java index 3914cb5fafc..80565776885 100644 --- a/Mage/src/mage/game/stack/Spell.java +++ b/Mage/src/mage/game/stack/Spell.java @@ -44,16 +44,13 @@ import mage.abilities.keyword.KickerAbility; import mage.cards.Card; import mage.game.events.GameEvent; import mage.players.Player; -import mage.util.Copier; import mage.watchers.Watchers; /** * * @author BetaSteward_at_googlemail.com */ -public class Spell implements StackObject, Card { - -// private static final transient Copier copier = new Copier(); +public class Spell> implements StackObject, Card { private Card card; private SpellAbility ability; @@ -65,11 +62,17 @@ public class Spell implements StackObject, Card { this.controllerId = controllerId; } + public Spell(final Spell spell) { + this.card = spell.card.copy(); + this.ability = spell.ability.copy(); + this.controllerId = spell.controllerId; + } + @Override public boolean resolve(Game game) { boolean result = false; if (card.getCardType().contains(CardType.INSTANT) || card.getCardType().contains(CardType.SORCERY)) { - if (ability.getTargets().stillLegal(game)) { + if (ability.getTargets().stillLegal(ability, game)) { boolean replaced = resolveKicker(game); if (!replaced) result = ability.resolve(game); @@ -87,7 +90,7 @@ public class Spell implements StackObject, Card { return false; } else if (card.getCardType().contains(CardType.ENCHANTMENT) && card.getSubtype().contains("Aura")) { - if (ability.getTargets().stillLegal(game)) { + if (ability.getTargets().stillLegal(ability, game)) { Player controller = game.getPlayers().get(controllerId); if (controller.putOntoBattlefield(card, game)) { return ability.resolve(game); @@ -216,7 +219,7 @@ public class Spell implements StackObject, Card { @Override public SpellAbility getSpellAbility() { - return card.getSpellAbility(); + return ability; } @Override @@ -229,11 +232,6 @@ public class Spell implements StackObject, Card { } - @Override - public Card copy() { - return new Copier().copy(this); - } - @Override public void checkTriggers(GameEvent event, Game game) { checkTriggers(Zone.STACK, event, game); @@ -259,4 +257,8 @@ public class Spell implements StackObject, Card { return card.getExpansionSetId(); } + @Override + public Spell copy() { + return new Spell(this); + } } diff --git a/Mage/src/mage/game/stack/SpellStack.java b/Mage/src/mage/game/stack/SpellStack.java index 20a75aeea5d..e7996683b03 100644 --- a/Mage/src/mage/game/stack/SpellStack.java +++ b/Mage/src/mage/game/stack/SpellStack.java @@ -29,7 +29,9 @@ package mage.game.stack; import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.Stack; import java.util.UUID; import mage.Constants.Zone; @@ -46,6 +48,14 @@ import mage.players.Player; */ public class SpellStack extends Stack { + public SpellStack () {} + + public SpellStack(final SpellStack stack) { + for (StackObject spell: stack) { + this.push(spell.copy()); + } + } + //resolve top StackObject public void resolve(Game game) { StackObject top = this.peek(); @@ -59,7 +69,6 @@ public class SpellStack extends Stack { } } - public boolean counter(UUID objectId, UUID sourceId, Game game) { StackObject stackObject = getStackObject(objectId); if (stackObject != null) { @@ -76,32 +85,34 @@ public class SpellStack extends Stack { public boolean replaceEvent(GameEvent event, Game game) { boolean caught = false; - List rEffects = new ArrayList(); + Map rEffects = new LinkedHashMap(); for (StackObject stackObject: this) { for (Ability ability: stackObject.getAbilities()) { if (ability.getZone() == Zone.STACK) { for (Effect effect: ability.getEffects()) { if (effect instanceof ReplacementEffect) { - if (((ReplacementEffect)effect).applies(event, game)) - rEffects.add((ReplacementEffect) effect); + if (((ReplacementEffect)effect).applies(event, ability, game)) + rEffects.put((ReplacementEffect) effect, ability); } } } } } if (rEffects.size() > 0) { + List effects = new ArrayList(rEffects.keySet()); + int index; if (rEffects.size() == 1) { - caught = rEffects.get(0).replaceEvent(event, game); + index = 0; } else { Player player = game.getPlayer(event.getPlayerId()); - caught = rEffects.get(player.chooseEffect(rEffects, game)).replaceEvent(event, game); + index = player.chooseEffect(effects, game); } + caught = effects.get(index).replaceEvent(event, rEffects.get(effects.get(index)), game); } return caught; } - public StackObject getStackObject(UUID id) { for (StackObject stackObject: this) { if (stackObject.getId().equals(id)) @@ -109,4 +120,8 @@ public class SpellStack extends Stack { } return null; } + + public SpellStack copy() { + return new SpellStack(this); + } } diff --git a/Mage/src/mage/game/stack/StackAbility.java b/Mage/src/mage/game/stack/StackAbility.java index 1fc49fb341a..520fddc0d64 100644 --- a/Mage/src/mage/game/stack/StackAbility.java +++ b/Mage/src/mage/game/stack/StackAbility.java @@ -29,6 +29,7 @@ package mage.game.stack; import java.util.ArrayList; +import mage.Constants.AbilityType; import mage.abilities.costs.AlternativeCost; import mage.abilities.costs.Cost; import mage.abilities.costs.Costs; @@ -47,7 +48,9 @@ import mage.ObjectColor; import mage.abilities.Abilities; import mage.abilities.AbilitiesImpl; import mage.abilities.Ability; +import mage.abilities.costs.CostsImpl; import mage.abilities.costs.mana.ManaCosts; +import mage.abilities.costs.mana.ManaCostsImpl; import mage.game.events.GameEvent; import mage.target.Target; import mage.target.Targets; @@ -60,9 +63,9 @@ public class StackAbility implements StackObject, Ability { private static List emptyList = new ArrayList(); private static ObjectColor emptyColor = new ObjectColor(); - private static ManaCosts emptyCost = new ManaCosts(""); - private static Abilities emptyAbilites = new AbilitiesImpl(); - private static Effects emptyEffects = new Effects(null); + private static ManaCosts emptyCost = new ManaCostsImpl(); + private static Costs emptyCosts = new CostsImpl(); + private static Abilities emptyAbilites = new AbilitiesImpl(); private Ability ability; private UUID controllerId; @@ -74,9 +77,15 @@ public class StackAbility implements StackObject, Ability { this.controllerId = controllerId; } + public StackAbility(final StackAbility spell) { + this.ability = spell.ability.copy(); + this.controllerId = spell.controllerId; + this.id = spell.id; + } + @Override public boolean resolve(Game game) { - if (ability.getTargets().stillLegal(game)) { + if (ability.getTargets().stillLegal(ability, game)) { return ability.resolve(game); } counter(game); @@ -166,12 +175,12 @@ public class StackAbility implements StackObject, Ability { @Override public Costs getCosts() { - return emptyCost; + return emptyCosts; } @Override public Effects getEffects() { - return emptyEffects; + return ability.getEffects(); } @Override @@ -219,12 +228,6 @@ public class StackAbility implements StackObject, Ability { @Override public void addChoice(Choice choice) {} - @Override - public Ability copy() { - //TODO: not sure if this will be needed - throw new UnsupportedOperationException("Not supported yet."); - } - @Override public List getAlternativeCosts() { return ability.getAlternativeCosts(); @@ -244,4 +247,19 @@ public class StackAbility implements StackObject, Ability { @Override public void checkTriggers(GameEvent event, Game game) { } + @Override + public AbilityType getAbilityType() { + return ability.getAbilityType(); + } + + @Override + public boolean isUsesStack() { + return true; + } + + @Override + public StackAbility copy() { + return new StackAbility(this); + } + } diff --git a/Mage/src/mage/game/stack/StackObject.java b/Mage/src/mage/game/stack/StackObject.java index 95f95bacf07..365fb64d703 100644 --- a/Mage/src/mage/game/stack/StackObject.java +++ b/Mage/src/mage/game/stack/StackObject.java @@ -41,5 +41,6 @@ public interface StackObject extends MageObject { public UUID getControllerId(); public void checkTriggers(GameEvent event, Game game); public void counter(Game game); - + @Override + public StackObject copy(); } diff --git a/Mage/src/mage/game/turn/BeginCombatStep.java b/Mage/src/mage/game/turn/BeginCombatStep.java index 088be68e74e..38aa32becaa 100644 --- a/Mage/src/mage/game/turn/BeginCombatStep.java +++ b/Mage/src/mage/game/turn/BeginCombatStep.java @@ -37,7 +37,7 @@ import mage.game.events.GameEvent.EventType; * * @author BetaSteward_at_googlemail.com */ -public class BeginCombatStep extends Step { +public class BeginCombatStep extends Step { public BeginCombatStep() { super(PhaseStep.BEGIN_COMBAT, true); @@ -46,6 +46,10 @@ public class BeginCombatStep extends Step { this.postStepEvent = EventType.BEGIN_COMBAT_STEP_POST; } + public BeginCombatStep(final BeginCombatStep step) { + super(step); + } + @Override public void beginStep(Game game, UUID activePlayerId) { //20091005 - 507.1 @@ -55,4 +59,9 @@ public class BeginCombatStep extends Step { super.beginStep(game, activePlayerId); } + @Override + public BeginCombatStep copy() { + return new BeginCombatStep(this); + } + } diff --git a/Mage/src/mage/game/turn/BeginningPhase.java b/Mage/src/mage/game/turn/BeginningPhase.java index f676c865dbd..47d0a7ec932 100644 --- a/Mage/src/mage/game/turn/BeginningPhase.java +++ b/Mage/src/mage/game/turn/BeginningPhase.java @@ -35,7 +35,7 @@ import mage.game.events.GameEvent.EventType; * * @author BetaSteward_at_googlemail.com */ -public class BeginningPhase extends Phase { +public class BeginningPhase extends Phase { public BeginningPhase() { this.type = TurnPhase.BEGINNING; @@ -47,4 +47,13 @@ public class BeginningPhase extends Phase { this.steps.add(new DrawStep()); } + public BeginningPhase(final BeginningPhase phase) { + super(phase); + } + + @Override + public BeginningPhase copy() { + return new BeginningPhase(this); + } + } diff --git a/Mage/src/mage/game/turn/CleanupStep.java b/Mage/src/mage/game/turn/CleanupStep.java index de3747f174f..f0aa0f20403 100644 --- a/Mage/src/mage/game/turn/CleanupStep.java +++ b/Mage/src/mage/game/turn/CleanupStep.java @@ -38,7 +38,7 @@ import mage.players.Player; * * @author BetaSteward_at_googlemail.com */ -public class CleanupStep extends Step { +public class CleanupStep extends Step { public CleanupStep() { super(PhaseStep.CLEANUP, true); @@ -47,6 +47,10 @@ public class CleanupStep extends Step { this.postStepEvent = EventType.CLEANUP_STEP_POST; } + public CleanupStep(final CleanupStep step) { + super(step); + } + @Override public void beginStep(Game game, UUID activePlayerId) { super.beginStep(game, activePlayerId); @@ -57,7 +61,11 @@ public class CleanupStep extends Step { //20100423 - 514.2 game.getBattlefield().endOfTurn(activePlayerId, game); game.getState().removeEotEffects(game); + } + @Override + public CleanupStep copy() { + return new CleanupStep(this); } } diff --git a/Mage/src/mage/game/turn/CombatDamageStep.java b/Mage/src/mage/game/turn/CombatDamageStep.java index 7d32e04e3bf..fea9274efb6 100644 --- a/Mage/src/mage/game/turn/CombatDamageStep.java +++ b/Mage/src/mage/game/turn/CombatDamageStep.java @@ -38,7 +38,7 @@ import mage.game.events.GameEvent.EventType; * * @author BetaSteward_at_googlemail.com */ -public class CombatDamageStep extends Step { +public class CombatDamageStep extends Step { private boolean first; @@ -50,6 +50,11 @@ public class CombatDamageStep extends Step { this.postStepEvent = EventType.COMBAT_DAMAGE_STEP_POST; } + public CombatDamageStep(final CombatDamageStep step) { + super(step); + this.first = step.first; + } + @Override public boolean skipStep(Game game, UUID activePlayerId) { if (game.getCombat().noAttackers()) @@ -71,4 +76,9 @@ public class CombatDamageStep extends Step { return first; } + @Override + public CombatDamageStep copy() { + return new CombatDamageStep(this); + } + } diff --git a/Mage/src/mage/game/turn/CombatPhase.java b/Mage/src/mage/game/turn/CombatPhase.java index ce127d353f9..fcb21689efe 100644 --- a/Mage/src/mage/game/turn/CombatPhase.java +++ b/Mage/src/mage/game/turn/CombatPhase.java @@ -35,7 +35,7 @@ import mage.game.events.GameEvent.EventType; * * @author BetaSteward_at_googlemail.com */ -public class CombatPhase extends Phase { +public class CombatPhase extends Phase { public CombatPhase() { this.type = TurnPhase.COMBAT; @@ -50,4 +50,13 @@ public class CombatPhase extends Phase { this.steps.add(new EndOfCombatStep()); } + public CombatPhase(final CombatPhase phase) { + super(phase); + } + + @Override + public CombatPhase copy() { + return new CombatPhase(this); + } + } diff --git a/Mage/src/mage/game/turn/DeclareAttackersStep.java b/Mage/src/mage/game/turn/DeclareAttackersStep.java index b2780f68f74..43efb2011a8 100644 --- a/Mage/src/mage/game/turn/DeclareAttackersStep.java +++ b/Mage/src/mage/game/turn/DeclareAttackersStep.java @@ -37,7 +37,7 @@ import mage.game.events.GameEvent.EventType; * * @author BetaSteward_at_googlemail.com */ -public class DeclareAttackersStep extends Step { +public class DeclareAttackersStep extends Step { public DeclareAttackersStep() { super(PhaseStep.DECLARE_ATTACKERS, true); @@ -46,9 +46,13 @@ public class DeclareAttackersStep extends Step { this.postStepEvent = EventType.DECLARE_ATTACKERS_STEP_POST; } + public DeclareAttackersStep(final DeclareAttackersStep step) { + super(step); + } + @Override public boolean skipStep(Game game, UUID activePlayerId) { - if (!game.getPlayer(activePlayerId).hasAvailableAttackers(game)) + if (game.getPlayer(activePlayerId).getAvailableAttackers(game).size() == 0) return true; return super.skipStep(game, activePlayerId); } @@ -59,4 +63,9 @@ public class DeclareAttackersStep extends Step { game.getCombat().selectAttackers(game); } + @Override + public DeclareAttackersStep copy() { + return new DeclareAttackersStep(this); + } + } diff --git a/Mage/src/mage/game/turn/DeclareBlockersStep.java b/Mage/src/mage/game/turn/DeclareBlockersStep.java index 6aee8a83b7a..117cd588dfd 100644 --- a/Mage/src/mage/game/turn/DeclareBlockersStep.java +++ b/Mage/src/mage/game/turn/DeclareBlockersStep.java @@ -37,7 +37,7 @@ import mage.game.events.GameEvent.EventType; * * @author BetaSteward_at_googlemail.com */ -public class DeclareBlockersStep extends Step { +public class DeclareBlockersStep extends Step { public DeclareBlockersStep() { super(PhaseStep.DECLARE_BLOCKERS, true); @@ -46,6 +46,10 @@ public class DeclareBlockersStep extends Step { this.postStepEvent = EventType.DECLARE_BLOCKERS_STEP_POST; } + public DeclareBlockersStep(final DeclareBlockersStep step) { + super(step); + } + @Override public boolean skipStep(Game game, UUID activePlayerId) { if (game.getCombat().noAttackers()) @@ -57,6 +61,12 @@ public class DeclareBlockersStep extends Step { public void beginStep(Game game, UUID activePlayerId) { super.beginStep(game, activePlayerId); game.getCombat().selectBlockers(game); + game.getCombat().damageAssignmentOrder(game); + } + + @Override + public DeclareBlockersStep copy() { + return new DeclareBlockersStep(this); } } diff --git a/Mage/src/mage/game/turn/DrawStep.java b/Mage/src/mage/game/turn/DrawStep.java index fe4b409696f..69b2242d66a 100644 --- a/Mage/src/mage/game/turn/DrawStep.java +++ b/Mage/src/mage/game/turn/DrawStep.java @@ -38,7 +38,7 @@ import mage.players.Player; * * @author BetaSteward_at_googlemail.com */ -public class DrawStep extends Step { +public class DrawStep extends Step { public DrawStep() { super(PhaseStep.DRAW, true); @@ -47,14 +47,23 @@ public class DrawStep extends Step { this.postStepEvent = EventType.DRAW_STEP_POST; } + public DrawStep(final DrawStep step) { + super(step); + } + @Override public void beginStep(Game game, UUID activePlayerId) { Player activePlayer = game.getPlayer(activePlayerId); //20091005 - 504.1/703.4c activePlayer.drawCards(1, game); - game.saveState(); +// game.saveState(); super.beginStep(game, activePlayerId); } + @Override + public DrawStep copy() { + return new DrawStep(this); + } + } diff --git a/Mage/src/mage/game/turn/EndOfCombatStep.java b/Mage/src/mage/game/turn/EndOfCombatStep.java index 1399e0d8cc4..2fa6c4f1f04 100644 --- a/Mage/src/mage/game/turn/EndOfCombatStep.java +++ b/Mage/src/mage/game/turn/EndOfCombatStep.java @@ -37,7 +37,7 @@ import mage.game.events.GameEvent.EventType; * * @author BetaSteward_at_googlemail.com */ -public class EndOfCombatStep extends Step { +public class EndOfCombatStep extends Step { public EndOfCombatStep() { super(PhaseStep.END_COMBAT, true); @@ -46,12 +46,21 @@ public class EndOfCombatStep extends Step { this.postStepEvent = EventType.END_COMBAT_STEP_POST; } + public EndOfCombatStep(final EndOfCombatStep step) { + super(step); + } + @Override public void endStep(Game game, UUID activePlayerId) { super.endStep(game, activePlayerId); //20091005 - 511.3 game.getCombat().endCombat(game); - game.saveState(); +// game.saveState(); + } + + @Override + public EndOfCombatStep copy() { + return new EndOfCombatStep(this); } } diff --git a/Mage/src/mage/game/turn/EndPhase.java b/Mage/src/mage/game/turn/EndPhase.java index 91c79d117cd..3b4546b4483 100644 --- a/Mage/src/mage/game/turn/EndPhase.java +++ b/Mage/src/mage/game/turn/EndPhase.java @@ -38,7 +38,7 @@ import mage.game.events.GameEvent.EventType; * * @author BetaSteward_at_googlemail.com */ -public class EndPhase extends Phase { +public class EndPhase extends Phase { public EndPhase() { this.type = TurnPhase.END; @@ -49,6 +49,10 @@ public class EndPhase extends Phase { this.steps.add(new CleanupStep()); } + public EndPhase(final EndPhase phase) { + super(phase); + } + @Override protected void playStep(Game game, UUID activePlayerId) { if (currentStep.getType() == PhaseStep.CLEANUP) { @@ -62,4 +66,9 @@ public class EndPhase extends Phase { super.playStep(game, activePlayerId); } + @Override + public EndPhase copy() { + return new EndPhase(this); + } + } diff --git a/Mage/src/mage/game/turn/EndStep.java b/Mage/src/mage/game/turn/EndStep.java index 454caecdeb8..93d02e532ef 100644 --- a/Mage/src/mage/game/turn/EndStep.java +++ b/Mage/src/mage/game/turn/EndStep.java @@ -35,7 +35,7 @@ import mage.game.events.GameEvent.EventType; * * @author BetaSteward_at_googlemail.com */ -public class EndStep extends Step { +public class EndStep extends Step { public EndStep() { super(PhaseStep.END_TURN, true); @@ -44,4 +44,13 @@ public class EndStep extends Step { this.postStepEvent = EventType.END_TURN_STEP_POST; } + public EndStep(final EndStep step) { + super(step); + } + + @Override + public EndStep copy() { + return new EndStep(this); + } + } diff --git a/Mage/src/mage/game/turn/Phase.java b/Mage/src/mage/game/turn/Phase.java index 4eda1d7e7de..eae89258630 100644 --- a/Mage/src/mage/game/turn/Phase.java +++ b/Mage/src/mage/game/turn/Phase.java @@ -42,7 +42,7 @@ import mage.game.events.GameEvent.EventType; * * @author BetaSteward_at_googlemail.com */ -public abstract class Phase implements Serializable { +public abstract class Phase> implements Serializable { protected TurnPhase type; protected List steps = new ArrayList(); @@ -54,6 +54,24 @@ public abstract class Phase implements Serializable { protected Step currentStep; protected int count; + public abstract T copy(); + + public Phase() {} + + public Phase(final Phase phase) { + this.type = phase.type; + this.event = phase.event; + this.preEvent = phase.preEvent; + this.postEvent = phase.postEvent; + this.activePlayerId = phase.activePlayerId; + if (phase.currentStep != null) + this.currentStep = phase.currentStep.copy(); + this.count = phase.count; + for (Step step: phase.steps) { + this.steps.add(step.copy()); + } + } + public TurnPhase getType() { return type; } @@ -80,8 +98,7 @@ public abstract class Phase implements Serializable { this.activePlayerId = activePlayerId; - if (!game.replaceEvent(new GameEvent(event, null, null, activePlayerId))) { - game.fireEvent(new GameEvent(preEvent, null, null, activePlayerId)); + if (beginPhase(game, activePlayerId)) { for (Step step: steps) { if (game.isGameOver()) @@ -91,12 +108,24 @@ public abstract class Phase implements Serializable { playStep(game, activePlayerId); } count++; - game.fireEvent(new GameEvent(postEvent, null, null, activePlayerId)); + endPhase(game, activePlayerId); return true; } return false; } + public boolean beginPhase(Game game, UUID activePlayerId) { + if (!game.replaceEvent(new GameEvent(event, null, null, activePlayerId))) { + game.fireEvent(new GameEvent(preEvent, null, null, activePlayerId)); + return true; + } + return false; + } + + public void endPhase(Game game, UUID activePlayerId) { + game.fireEvent(new GameEvent(postEvent, null, null, activePlayerId)); + } + public void prePriority(Game game, UUID activePlayerId) { currentStep.beginStep(game, activePlayerId); } @@ -105,7 +134,6 @@ public abstract class Phase implements Serializable { currentStep.endStep(game, activePlayerId); //20091005 - 500.4/703.4n game.emptyManaPools(); - game.saveState(); //20091005 - 500.9 playExtraSteps(game, currentStep.getType()); } diff --git a/Mage/src/mage/game/turn/PostCombatMainPhase.java b/Mage/src/mage/game/turn/PostCombatMainPhase.java index 6c6143f8119..bd55d432891 100644 --- a/Mage/src/mage/game/turn/PostCombatMainPhase.java +++ b/Mage/src/mage/game/turn/PostCombatMainPhase.java @@ -28,7 +28,6 @@ package mage.game.turn; -import mage.Constants.PhaseStep; import mage.Constants.TurnPhase; import mage.game.events.GameEvent.EventType; @@ -36,7 +35,7 @@ import mage.game.events.GameEvent.EventType; * * @author BetaSteward_at_googlemail.com */ -public class PostCombatMainPhase extends Phase { +public class PostCombatMainPhase extends Phase { public PostCombatMainPhase() { this.type = TurnPhase.POSTCOMBAT_MAIN; @@ -46,4 +45,13 @@ public class PostCombatMainPhase extends Phase { this.steps.add(new PostCombatMainStep()); } + public PostCombatMainPhase(final PostCombatMainPhase phase) { + super(phase); + } + + @Override + public PostCombatMainPhase copy() { + return new PostCombatMainPhase(this); + } + } diff --git a/Mage/src/mage/game/turn/PostCombatMainStep.java b/Mage/src/mage/game/turn/PostCombatMainStep.java index e24464dfcbb..bd05d01fcbb 100644 --- a/Mage/src/mage/game/turn/PostCombatMainStep.java +++ b/Mage/src/mage/game/turn/PostCombatMainStep.java @@ -35,7 +35,7 @@ import mage.game.events.GameEvent.EventType; * * @author BetaSteward_at_googlemail.com */ -public class PostCombatMainStep extends Step { +public class PostCombatMainStep extends Step { public PostCombatMainStep() { super(PhaseStep.POSTCOMBAT_MAIN, true); @@ -44,4 +44,13 @@ public class PostCombatMainStep extends Step { this.postStepEvent = EventType.POSTCOMBAT_MAIN_STEP_POST; } + public PostCombatMainStep(final PostCombatMainStep step) { + super(step); + } + + @Override + public PostCombatMainStep copy() { + return new PostCombatMainStep(this); + } + } diff --git a/Mage/src/mage/game/turn/PreCombatMainPhase.java b/Mage/src/mage/game/turn/PreCombatMainPhase.java index a458e506958..5d78c51e871 100644 --- a/Mage/src/mage/game/turn/PreCombatMainPhase.java +++ b/Mage/src/mage/game/turn/PreCombatMainPhase.java @@ -35,7 +35,7 @@ import mage.game.events.GameEvent.EventType; * * @author BetaSteward_at_googlemail.com */ -public class PreCombatMainPhase extends Phase { +public class PreCombatMainPhase extends Phase { public PreCombatMainPhase() { this.type = TurnPhase.PRECOMBAT_MAIN; @@ -45,4 +45,13 @@ public class PreCombatMainPhase extends Phase { this.steps.add(new PreCombatMainStep()); } + public PreCombatMainPhase(final PreCombatMainPhase phase) { + super(phase); + } + + @Override + public PreCombatMainPhase copy() { + return new PreCombatMainPhase(this); + } + } diff --git a/Mage/src/mage/game/turn/PreCombatMainStep.java b/Mage/src/mage/game/turn/PreCombatMainStep.java index 09504e3b970..16ff28855fb 100644 --- a/Mage/src/mage/game/turn/PreCombatMainStep.java +++ b/Mage/src/mage/game/turn/PreCombatMainStep.java @@ -35,7 +35,7 @@ import mage.game.events.GameEvent.EventType; * * @author BetaSteward_at_googlemail.com */ -public class PreCombatMainStep extends Step { +public class PreCombatMainStep extends Step { public PreCombatMainStep() { super(PhaseStep.PRECOMBAT_MAIN, true); @@ -44,4 +44,13 @@ public class PreCombatMainStep extends Step { this.postStepEvent = EventType.PRECOMBAT_MAIN_STEP_POST; } + public PreCombatMainStep(final PreCombatMainStep step) { + super(step); + } + + @Override + public PreCombatMainStep copy() { + return new PreCombatMainStep(this); + } + } diff --git a/Mage/src/mage/game/turn/Step.java b/Mage/src/mage/game/turn/Step.java index 8534d4864e9..62f7fa6ddea 100644 --- a/Mage/src/mage/game/turn/Step.java +++ b/Mage/src/mage/game/turn/Step.java @@ -39,7 +39,7 @@ import mage.game.events.GameEvent.EventType; * * @author BetaSteward_at_googlemail.com */ -public abstract class Step implements Serializable { +public abstract class Step> implements Serializable { private PhaseStep type; private boolean hasPriority; @@ -47,11 +47,21 @@ public abstract class Step implements Serializable { protected EventType preStepEvent; protected EventType postStepEvent; + public abstract T copy(); + public Step(PhaseStep type, boolean hasPriority) { this.type = type; this.hasPriority = hasPriority; } + public Step(final Step step) { + this.type = step.type; + this.hasPriority = step.hasPriority; + this.stepEvent = step.stepEvent; + this.preStepEvent = step.preStepEvent; + this.postStepEvent = step.postStepEvent; + } + public PhaseStep getType() { return type; } diff --git a/Mage/src/mage/game/turn/Turn.java b/Mage/src/mage/game/turn/Turn.java index fa73dc1860e..16fff6a74de 100644 --- a/Mage/src/mage/game/turn/Turn.java +++ b/Mage/src/mage/game/turn/Turn.java @@ -54,6 +54,16 @@ public class Turn implements Serializable { phases.add(new EndPhase()); } + public Turn(final Turn turn) { + if (turn.currentPhase != null) + this.currentPhase = turn.currentPhase.copy(); + this.activePlayerId = turn.activePlayerId; + for (Phase phase: turn.phases) { + this.phases.add(phase.copy()); + } + + } + public TurnPhase getPhaseType() { if (currentPhase != null) return currentPhase.getType(); @@ -154,4 +164,7 @@ public class Turn implements Serializable { } } + public Turn copy() { + return new Turn(this); + } } diff --git a/Mage/src/mage/game/turn/TurnMod.java b/Mage/src/mage/game/turn/TurnMod.java index ecff96b88ee..bceddbebda5 100644 --- a/Mage/src/mage/game/turn/TurnMod.java +++ b/Mage/src/mage/game/turn/TurnMod.java @@ -58,6 +58,19 @@ public class TurnMod implements Serializable { this.extraTurn = true; } + public TurnMod(final TurnMod mod) { + this.playerId = mod.playerId; + this.extraTurn = mod.extraTurn; + this.skipTurn = mod.skipTurn; + this.extraPhase = mod.extraPhase; + this.skipPhase = mod.skipPhase; + if (mod.extraStep != null) + this.extraStep = mod.extraStep.copy(); + this.skipStep = mod.skipStep; + this.afterPhase = mod.afterPhase; + this.afterStep = mod.afterStep; + } + /** * * @param playerId @@ -126,4 +139,7 @@ public class TurnMod implements Serializable { return afterStep; } + public TurnMod copy() { + return new TurnMod(this); + } } diff --git a/Mage/src/mage/game/turn/TurnMods.java b/Mage/src/mage/game/turn/TurnMods.java index bcf8b8f3efb..8456d44c754 100644 --- a/Mage/src/mage/game/turn/TurnMods.java +++ b/Mage/src/mage/game/turn/TurnMods.java @@ -40,6 +40,14 @@ import mage.Constants.TurnPhase; */ public class TurnMods extends ArrayList { + public TurnMods() {} + + public TurnMods(final TurnMods mods) { + for (TurnMod mod: mods) { + this.add(mod.copy()); + } + } + public boolean extraTurn(UUID playerId) { ListIterator it = this.listIterator(this.size()); while (it.hasPrevious()) { @@ -112,4 +120,8 @@ public class TurnMods extends ArrayList { return false; } + public TurnMods copy() { + return new TurnMods(this); + } + } diff --git a/Mage/src/mage/game/turn/UntapStep.java b/Mage/src/mage/game/turn/UntapStep.java index 8e7d59bfdaa..f3908d03d8a 100644 --- a/Mage/src/mage/game/turn/UntapStep.java +++ b/Mage/src/mage/game/turn/UntapStep.java @@ -38,7 +38,7 @@ import mage.players.Player; * * @author BetaSteward_at_googlemail.com */ -public class UntapStep extends Step { +public class UntapStep extends Step { public UntapStep() { super(PhaseStep.UNTAP, false); @@ -46,7 +46,11 @@ public class UntapStep extends Step { this.preStepEvent = EventType.UNTAP_STEP_PRE; this.postStepEvent = EventType.UNTAP_STEP_POST; } - + + public UntapStep(final UntapStep step) { + super(step); + } + @Override public void beginStep(Game game, UUID activePlayerId) { super.beginStep(game, activePlayerId); @@ -57,5 +61,9 @@ public class UntapStep extends Step { activePlayer.untap(game); } + @Override + public UntapStep copy() { + return new UntapStep(this); + } } diff --git a/Mage/src/mage/game/turn/UpkeepStep.java b/Mage/src/mage/game/turn/UpkeepStep.java index 3a96c9ba335..ed812473a9d 100644 --- a/Mage/src/mage/game/turn/UpkeepStep.java +++ b/Mage/src/mage/game/turn/UpkeepStep.java @@ -35,7 +35,7 @@ import mage.game.events.GameEvent.EventType; * * @author BetaSteward_at_googlemail.com */ -public class UpkeepStep extends Step { +public class UpkeepStep extends Step { public UpkeepStep() { super(PhaseStep.UPKEEP, true); @@ -44,4 +44,13 @@ public class UpkeepStep extends Step { this.postStepEvent = EventType.UPKEEP_STEP_POST; } + public UpkeepStep(final UpkeepStep step) { + super(step); + } + + @Override + public UpkeepStep copy() { + return new UpkeepStep(this); + } + } diff --git a/Mage/src/mage/players/Library.java b/Mage/src/mage/players/Library.java index d24090f48b3..36121b60118 100644 --- a/Mage/src/mage/players/Library.java +++ b/Mage/src/mage/players/Library.java @@ -30,12 +30,18 @@ package mage.players; import java.io.Serializable; import java.util.ArrayDeque; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Deque; +import java.util.HashMap; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Random; +import java.util.Set; import java.util.UUID; +import mage.Constants.Zone; import mage.cards.Card; import mage.cards.Cards; import mage.filter.FilterCard; @@ -48,28 +54,34 @@ import mage.util.Copier; */ public class Library implements Serializable { -// private static final transient Copier copier = new Copier(); - private static Random rnd = new Random(); - private boolean emptyDraw = false; - private Deque library = new ArrayDeque(); + private boolean emptyDraw; + private Deque library = new ArrayDeque(); private UUID playerId; public Library(UUID playerId) { this.playerId = playerId; } + public Library(final Library lib) { + this.emptyDraw = lib.emptyDraw; + this.playerId = lib.playerId; + for (UUID id: lib.library) { + this.library.addLast(id); + } + } + public void shuffle() { - Card[] shuffled = library.toArray(new Card[0]); + UUID[] shuffled = library.toArray(new UUID[0]); for (int n = shuffled.length - 1; n > 0; n--) { int r = rnd.nextInt(n); - Card temp = shuffled[n]; + UUID temp = shuffled[n]; shuffled[n] = shuffled[r]; shuffled[r] = temp; } library.clear(); - for (Card card: shuffled) { + for (UUID card: shuffled) { library.add(card); } } @@ -82,7 +94,7 @@ public class Library implements Serializable { * @see Card */ public Card removeFromTop(Game game) { - Card card = library.pollFirst(); + Card card = game.getCard(library.pollFirst()); if (card == null) { emptyDraw = true; } @@ -97,25 +109,32 @@ public class Library implements Serializable { * @see Card */ public Card getFromTop(Game game) { - return library.peekFirst(); + return game.getCard(library.peekFirst()); } public void putOnTop(Card card, Game game) { - if (card.getOwnerId().equals(playerId)) - library.addFirst(card); - else + if (card.getOwnerId().equals(playerId)) { + card.setZone(Zone.LIBRARY); + library.addFirst(card.getId()); + } + else { game.getPlayer(card.getOwnerId()).getLibrary().putOnTop(card, game); + } } public void putOnBottom(Card card, Game game) { - if (card.getOwnerId().equals(playerId)) - library.add(card); - else + if (card.getOwnerId().equals(playerId)) { + card.setZone(Zone.LIBRARY); + library.add(card.getId()); + } + else { game.getPlayer(card.getOwnerId()).getLibrary().putOnBottom(card, game); + } } public Library copy() { - return new Copier().copy(this); +// return new Copier().copy(this); + return new Library(this); } public void clear() { @@ -128,19 +147,38 @@ public class Library implements Serializable { public void set(Library newLibrary) { library.clear(); - for (Card card: newLibrary.getCards()) { + for (UUID card: newLibrary.getCardList()) { library.add(card); } } - public List getCards() { - return Arrays.asList(library.toArray(new Card[0])); + public List getCardList() { + return new ArrayList(library); } - public int count(FilterCard filter) { + public List getCards(Game game) { + List cards = new ArrayList(); + for (UUID cardId: library) { + cards.add(game.getCard(cardId)); + } + return cards; + } + + public Collection getUniqueCards(Game game) { + Map cards = new HashMap(); + for (UUID cardId: library) { + Card card = game.getCard(cardId); + if (!cards.containsKey(card.getName())) { + cards.put(card.getName(), card); + } + } + return cards.values(); + } + + public int count(FilterCard filter, Game game) { int result = 0; - for (Card card: library) { - if (filter.match(card)) + for (UUID card: library) { + if (filter.match(game.getCard(card))) result++; } return result; @@ -151,31 +189,34 @@ public class Library implements Serializable { return emptyDraw; } - void setControllerId(UUID playerId) { - for (Card card: library) { - card.setControllerId(playerId); + void setControllerId(UUID playerId, Game game) { + for (UUID cardId: library) { + game.getCard(cardId).setControllerId(playerId); } } - public void addAll(Cards cards) { - library.addAll(cards.values()); + public void addAll(Set cards) { + for (Card card: cards) { + card.setZone(Zone.LIBRARY); + library.add(card.getId()); + } } - public Card getCard(UUID cardId) { - for (Card card: library) { - if (card.getId().equals(cardId)) - return card; + public Card getCard(UUID cardId, Game game) { + for (UUID card: library) { + if (card.equals(cardId)) + return game.getCard(card); } return null; } - public Card remove(UUID cardId) { - Iterator it = library.iterator(); + public Card remove(UUID cardId, Game game) { + Iterator it = library.iterator(); while(it.hasNext()) { - Card card = it.next(); - if (card.getId().equals(cardId)) { + UUID card = it.next(); + if (card.equals(cardId)) { it.remove(); - return card; + return game.getCard(card); } } return null; diff --git a/Mage/src/mage/players/ManaPool.java b/Mage/src/mage/players/ManaPool.java index 2ca6d7045ee..e0f02656496 100644 --- a/Mage/src/mage/players/ManaPool.java +++ b/Mage/src/mage/players/ManaPool.java @@ -44,6 +44,17 @@ public class ManaPool implements Serializable { private int black = 0; private int colorless = 0; + public ManaPool() {} + + public ManaPool(final ManaPool pool) { + this.red = pool.red; + this.green = pool.green; + this.blue = pool.blue; + this.white = pool.white; + this.black = pool.black; + this.colorless = pool.colorless; + } + public void setRed(int red) { this.red = red; } @@ -193,4 +204,7 @@ public class ManaPool implements Serializable { return red + green + blue + white + black + colorless; } + public ManaPool copy() { + return new ManaPool(this); + } } diff --git a/Mage/src/mage/players/Player.java b/Mage/src/mage/players/Player.java index b838318f844..dd454f508db 100644 --- a/Mage/src/mage/players/Player.java +++ b/Mage/src/mage/players/Player.java @@ -28,6 +28,7 @@ package mage.players; +import java.util.Collection; import java.util.List; import java.util.Set; import java.util.UUID; @@ -43,10 +44,12 @@ import mage.abilities.TriggeredAbility; import mage.abilities.costs.mana.ManaCost; import mage.abilities.costs.mana.VariableManaCost; import mage.abilities.effects.ReplacementEffect; +import mage.abilities.mana.ManaOptions; import mage.cards.Card; import mage.cards.Cards; import mage.choices.Choice; import mage.counters.Counters; +import mage.filter.FilterAbility; import mage.game.events.GameEvent; import mage.game.Game; import mage.game.permanent.Permanent; @@ -54,19 +57,19 @@ import mage.game.permanent.token.Token; import mage.target.Target; import mage.target.TargetAmount; import mage.target.TargetCard; -import mage.target.common.TargetCardInLibrary; +import mage.util.Copyable; /** * * @author BetaSteward_at_googlemail.com */ -public interface Player extends MageItem { +public interface Player extends MageItem, Copyable { public boolean isHuman(); public String getName(); public Library getLibrary(); public Cards getGraveyard(); - public Abilities getAbilities(); + public Abilities getAbilities(); public Counters getCounters(); public int getLife(); public void setLife(int life, Game game); @@ -97,7 +100,7 @@ public interface Player extends MageItem { public boolean removeFromBattlefield(Permanent permanent, Game game); public boolean putInGraveyard(Card card, Game game, boolean fromBattlefield); public boolean removeFromGraveyard(Card card, Game game); - public boolean searchLibrary(TargetCardInLibrary target, Game game); + public boolean searchLibrary(TargetCard target, Game game); public boolean canPlayLand(); public boolean playLand(Card card, Game game); public boolean activateAbility(ActivatedAbility ability, Game game); @@ -116,6 +119,7 @@ public interface Player extends MageItem { public void revealCards(Cards cards, Game game); public void lookAtCards(Cards cards, Game game); + @Override public Player copy(); public void restore(Player player); @@ -125,9 +129,9 @@ public interface Player extends MageItem { public void setResponseInteger(Integer data); public abstract void priority(Game game); - public abstract boolean chooseTarget(Outcome outcome, Target target, Game game); - public abstract boolean chooseTarget(Cards cards, TargetCard target, Game game); - public abstract boolean chooseTargetAmount(Outcome outcome, TargetAmount target, Game game); + public abstract boolean chooseTarget(Outcome outcome, Target target, Ability source, Game game); + public abstract boolean chooseTarget(Cards cards, TargetCard target, Ability source, Game game); + public abstract boolean chooseTargetAmount(Outcome outcome, TargetAmount target, Ability source, Game game); public abstract boolean chooseMulligan(Game game); public abstract boolean chooseUse(Outcome outcome, String message, Game game); public abstract boolean choose(Outcome outcome, Choice choice, Game game); @@ -142,14 +146,15 @@ public interface Player extends MageItem { public void declareAttacker(UUID attackerId, UUID defenderId, Game game); public void declareBlocker(UUID blockerId, UUID attackerId, Game game); - public boolean hasAvailableAttackers(Game game); + public List getAvailableAttackers(Game game); + public List getAvailableBlockers(Game game); public void beginTurn(Game game); public void endOfTurn(Game game); public void phasing(Game game); public void untap(Game game); - public List getPlayable(Game game, boolean hidden); + public List getPlayable(Game game, FilterAbility filter, ManaOptions available, boolean hidden); public List getPlayableOptions(Ability ability, Game game); } diff --git a/Mage/src/mage/players/PlayerImpl.java b/Mage/src/mage/players/PlayerImpl.java index 8f8d185266f..42743bc37b1 100644 --- a/Mage/src/mage/players/PlayerImpl.java +++ b/Mage/src/mage/players/PlayerImpl.java @@ -30,6 +30,7 @@ package mage.players; import java.io.Serializable; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -50,7 +51,9 @@ import mage.abilities.PlayLandAbility; import mage.abilities.SpecialAction; import mage.abilities.SpellAbility; import mage.abilities.TriggeredAbility; +import mage.abilities.common.PassAbility; import mage.abilities.keyword.KickerAbility; +import mage.abilities.keyword.LifelinkAbility; import mage.abilities.keyword.ProtectionAbility; import mage.abilities.keyword.ShroudAbility; import mage.abilities.mana.ManaAbility; @@ -60,7 +63,9 @@ import mage.cards.Cards; import mage.cards.CardsImpl; import mage.cards.decks.Deck; import mage.counters.Counters; +import mage.filter.FilterAbility; import mage.filter.common.FilterCreatureForAttack; +import mage.filter.common.FilterCreatureForCombat; import mage.game.Game; import mage.game.combat.CombatGroup; import mage.game.permanent.Permanent; @@ -72,39 +77,46 @@ import mage.game.permanent.token.Token; import mage.game.stack.Spell; import mage.game.stack.StackAbility; import mage.game.stack.StackObject; +import mage.target.TargetCard; import mage.target.common.TargetCardInLibrary; import mage.target.common.TargetDiscard; -import mage.util.Copier; import mage.watchers.Watcher; -public abstract class PlayerImpl implements Player, Serializable { +public abstract class PlayerImpl> implements Player, Serializable { - protected UUID playerId; + protected final UUID playerId; protected String name; protected boolean human; protected int life; protected boolean wins; protected boolean loses; protected Library library; - protected Cards hand = new CardsImpl(Zone.HAND); - protected Cards graveyard = new CardsImpl(Zone.GRAVEYARD); - protected Abilities abilities = new AbilitiesImpl(); - protected Counters counters = new Counters(); + protected Cards hand; + protected Cards graveyard; + protected Abilities abilities; + protected Counters counters; protected int landsPlayed; protected int landsPerTurn = 1; protected int maxHandSize = 7; - protected ManaPool manaPool = new ManaPool(); + protected ManaPool manaPool; protected boolean passed; protected boolean passedTurn; protected boolean left; protected RangeOfInfluence range; protected Set inRange = new HashSet(); + @Override + public abstract T copy(); + public PlayerImpl(String name, Deck deck, RangeOfInfluence range) { - this.playerId = UUID.randomUUID(); + this(UUID.randomUUID()); this.name = name; this.range = range; - deck.setOwnerId(playerId); + hand = new CardsImpl(Zone.HAND); + graveyard = new CardsImpl(Zone.GRAVEYARD); + abilities = new AbilitiesImpl(); + counters = new Counters(); + manaPool = new ManaPool(); library = new Library(playerId); library.addAll(deck.getCards()); } @@ -113,6 +125,31 @@ public abstract class PlayerImpl implements Player, Serializable { this.playerId = id; } + public PlayerImpl(final PlayerImpl player) { + this.playerId = player.playerId; + this.name = player.name; + this.human = player.human; + this.life = player.life; + this.wins = player.loses; + this.loses = player.loses; + this.library = player.library.copy(); + this.hand = player.hand.copy(); + this.graveyard = player.graveyard.copy(); + this.abilities = player.abilities.copy(); + this.counters = player.counters.copy(); + this.landsPlayed = player.landsPlayed; + this.landsPerTurn = player.landsPerTurn; + this.maxHandSize = player.maxHandSize; + this.manaPool = player.manaPool.copy(); + this.passed = player.passed; + this.passedTurn = player.passedTurn; + this.left = player.left; + this.range = player.range; + for (UUID id: player.inRange) { + this.inRange.add(id); + } + } + @Override public void init(Game game) { this.hand.clear(); @@ -123,7 +160,7 @@ public abstract class PlayerImpl implements Player, Serializable { this.left = false; this.passed = false; this.passedTurn = false; - for (Card card: library.getCards()) { + for (Card card: library.getCards(game)) { for (Watcher watcher: card.getWatchers()) { watcher.setControllerId(playerId); game.getState().getWatchers().add(watcher); @@ -219,7 +256,6 @@ public abstract class PlayerImpl implements Player, Serializable { return true; } -// @Override protected boolean drawCard(Game game) { if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.DRAW_CARD, null, playerId))) { Card card = getLibrary().removeFromTop(game); @@ -249,8 +285,8 @@ public abstract class PlayerImpl implements Player, Serializable { public void discardToMax(Game game) { while (hand.size() > this.maxHandSize) { TargetDiscard target = new TargetDiscard(playerId); - chooseTarget(Outcome.Discard, target, game); - discard(hand.get(target.getFirstTarget()), game); + chooseTarget(Outcome.Discard, target, null, game); + discard(hand.get(target.getFirstTarget(), game), game); } } @@ -258,8 +294,8 @@ public abstract class PlayerImpl implements Player, Serializable { public boolean putInHand(Card card, Game game) { if (card.getOwnerId().equals(playerId)) { this.hand.add(card); - if (card.getSpellAbility() != null) - card.getSpellAbility().clear(); +// if (card.getSpellAbility() != null) +// card.getSpellAbility().clear(); } else { return game.getPlayer(card.getOwnerId()).putInHand(card, game); } @@ -276,16 +312,16 @@ public abstract class PlayerImpl implements Player, Serializable { public void discard(int amount, Game game) { if (amount >= hand.size()) { int discardAmount = hand.size(); - for (Card card: hand.values()) { - discard(card, game); + while (hand.size() > 0) { + discard(hand.get(hand.iterator().next(), game), game); } game.fireInformEvent(name + " discards " + Integer.toString(discardAmount) + " card" + (discardAmount > 1?"s":"")); return; } for (int i = 0; i < amount; i++) { TargetDiscard target = new TargetDiscard(playerId); - chooseTarget(Outcome.Discard, target, game); - discard(hand.get(target.getFirstTarget()), game); + chooseTarget(Outcome.Discard, target, null, game); + discard(hand.get(target.getFirstTarget(), game), game); } game.fireInformEvent(name + " discards " + Integer.toString(amount) + " card" + (amount > 1?"s":"")); } @@ -326,6 +362,9 @@ public abstract class PlayerImpl implements Player, Serializable { public boolean removeFromBattlefield(Permanent permanent, Game game) { permanent.removeFromCombat(game); game.getBattlefield().removePermanent(permanent.getId()); + if (permanent.getAttachedTo() != null) { + game.getPermanent(permanent.getAttachedTo()).removeAttachment(permanent.getId(), game); + } return true; } @@ -403,9 +442,10 @@ public abstract class PlayerImpl implements Player, Serializable { if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.ACTIVATE_ABILITY, ability.getId(), playerId))) { game.bookmarkState(); game.getStack().push(new StackAbility(ability, playerId)); + String message = ability.getActivatedMessage(game); if (ability.activate(game, false)) { game.fireEvent(GameEvent.getEvent(GameEvent.EventType.ACTIVATED_ABILITY, ability.getId(), playerId)); - game.fireInformEvent(name + ability.getActivatedMessage(game)); + game.fireInformEvent(name + message); game.removeLastBookmark(); return true; } @@ -437,8 +477,11 @@ public abstract class PlayerImpl implements Player, Serializable { if (!ability.canActivate(this.playerId, game)) return false; - if (ability instanceof PlayLandAbility) { - result = playLand(hand.get(ability.getSourceId()), game); + if (ability instanceof PassAbility) { + return true; + } + else if (ability instanceof PlayLandAbility) { + result = playLand(hand.get(ability.getSourceId(), game), game); } else if (ability instanceof SpecialAction) { result = specialAction((SpecialAction)ability.copy(), game); @@ -447,7 +490,7 @@ public abstract class PlayerImpl implements Player, Serializable { result = playManaAbility((ManaAbility)ability.copy(), game); } else if (ability instanceof SpellAbility) { - result = cast((SpellAbility)ability, game, false); + result = cast((SpellAbility)ability.copy(), game, false); } else { result = playAbility((ActivatedAbility)ability.copy(), game); @@ -462,7 +505,6 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public boolean triggerAbility(TriggeredAbility source, Game game) { //20091005 - 603.3c, 603.3d - game.saveState(); game.bookmarkState(); TriggeredAbility ability = (TriggeredAbility) source.copy(); if (ability.getTargets().canChoose(ability.getSourceId(), playerId, game)) { @@ -605,6 +647,11 @@ public abstract class PlayerImpl implements Player, Serializable { int actualDamage = event.getAmount(); if (actualDamage > 0) { actualDamage = this.loseLife(actualDamage, game); + Permanent source = game.getPermanent(sourceId); + if (source != null && source.getAbilities().containsKey(LifelinkAbility.getInstance().getId())) { + Player player = game.getPlayer(source.getControllerId()); + player.gainLife(actualDamage, game); + } game.fireEvent(GameEvent.getEvent(GameEvent.EventType.DAMAGED_PLAYER, playerId, sourceId, playerId, actualDamage)); return actualDamage; } @@ -638,11 +685,6 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public void setResponseInteger(Integer responseInteger) {} - @Override - public Player copy() { - return new Copier().copy(this); - } - @Override public void restore(Player player) { this.library = player.getLibrary(); @@ -654,6 +696,7 @@ public abstract class PlayerImpl implements Player, Serializable { this.counters = player.getCounters(); this.inRange = player.getInRange(); this.landsPlayed = player.getLandsPlayed(); + this.name = player.getName(); } @Override @@ -787,15 +830,15 @@ public abstract class PlayerImpl implements Player, Serializable { } @Override - public boolean searchLibrary(TargetCardInLibrary target, Game game) { + public boolean searchLibrary(TargetCard target, Game game) { //20091005 - 701.14c if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.SEARCH_LIBRARY, playerId, playerId))) { - TargetCardInLibrary newTarget; - if (library.count(target.getFilter()) < target.getNumberOfTargets()) - newTarget = new TargetCardInLibrary(library.count(target.getFilter()), target.getMaxNumberOfTargets(), target.getFilter()); + TargetCard newTarget; + if (library.count(target.getFilter(), game) < target.getNumberOfTargets()) + newTarget = new TargetCardInLibrary(library.count(target.getFilter(), game), target.getMaxNumberOfTargets(), target.getFilter()); else newTarget = target; - if (chooseTarget(new CardsImpl(Zone.LIBRARY, getLibrary().getCards()), newTarget, game)) { + if (newTarget.choose(Outcome.Neutral, playerId, null, game)) { game.fireEvent(GameEvent.getEvent(GameEvent.EventType.LIBRARY_SEARCHED, playerId, playerId)); return true; } @@ -804,17 +847,20 @@ public abstract class PlayerImpl implements Player, Serializable { } @Override - public boolean hasAvailableAttackers(Game game) { - return getAvailableAttackers(game).size() > 0; - } - - protected List getAvailableAttackers(Game game) { + public List getAvailableAttackers(Game game) { FilterCreatureForAttack attackFilter = new FilterCreatureForAttack(); attackFilter.getControllerId().add(playerId); List attackers = game.getBattlefield().getAllActivePermanents(attackFilter); return attackers; } + @Override + public List getAvailableBlockers(Game game) { + FilterCreatureForCombat blockFilter = new FilterCreatureForCombat(); + List blockers = game.getBattlefield().getAllActivePermanents(blockFilter, playerId); + return blockers; + } + protected ManaOptions getManaAvailable(Game game) { List manaPerms = this.getAvailableManaProducers(game); @@ -859,28 +905,28 @@ public abstract class PlayerImpl implements Player, Serializable { } @Override - public List getPlayable(Game game, boolean hidden) { + public List getPlayable(Game game, FilterAbility filter, ManaOptions available, boolean hidden) { List playable = new ArrayList(); - ManaOptions available = getManaAvailable(game); - available.addMana(manaPool.getMana()); +// ManaOptions available = getManaAvailable(game); +// available.addMana(manaPool.getMana()); if (hidden) { - for (Card card: hand.getUniqueCards()) { - for (ActivatedAbility ability: card.getAbilities().getActivatedAbilities(Zone.HAND)) { + for (Card card: hand.getUniqueCards(game)) { + for (ActivatedAbility ability: card.getAbilities().getActivatedAbilities(Zone.HAND, filter)) { if (canPlay(ability, available, game)) playable.add(ability); } } } - for (Card card: graveyard.getUniqueCards()) { - for (ActivatedAbility ability: card.getAbilities().getActivatedAbilities(Zone.GRAVEYARD)) { + for (Card card: graveyard.getUniqueCards(game)) { + for (ActivatedAbility ability: card.getAbilities().getActivatedAbilities(Zone.GRAVEYARD, filter)) { if (canPlay(ability, available, game)) playable.add(ability); } } for (Permanent permanent: game.getBattlefield().getAllActivePermanents(playerId)) { - for (ActivatedAbility ability: permanent.getAbilities().getActivatedAbilities(Zone.BATTLEFIELD)) { + for (ActivatedAbility ability: permanent.getAbilities().getActivatedAbilities(Zone.BATTLEFIELD, filter)) { if (canPlay(ability, available, game)) playable.add(ability); } @@ -892,20 +938,20 @@ public abstract class PlayerImpl implements Player, Serializable { public List getPlayableOptions(Ability ability, Game game) { List options = new ArrayList(); - if (ability.getTargets().size() > 0) + if (ability.getTargets().getUnchosen().size() > 0) addTargetOptions(options, ability, 0, game); - else if (ability.getChoices().size() > 0) + else if (ability.getChoices().getUnchosen().size() > 0) addChoiceOptions(options, ability, 0, game); - else if (ability.getCosts().getTargets().size() > 0) + else if (ability.getCosts().getTargets().getUnchosen().size() > 0) addCostTargetOptions(options, ability, 0, game); return options; } private void addTargetOptions(List options, Ability option, int targetNum, Game game) { - for (UUID targetId: option.getTargets().get(targetNum).possibleTargets(option.getSourceId(), playerId, game)) { + for (UUID targetId: option.getTargets().getUnchosen().get(targetNum).possibleTargets(option.getSourceId(), playerId, game)) { Ability newOption = option.copy(); - newOption.getTargets().get(targetNum).addTarget(targetId, game); + newOption.getTargets().get(targetNum).addTarget(targetId, option, game); if (targetNum < option.getTargets().size() - 1) { addTargetOptions(options, newOption, targetNum + 1, game); } @@ -939,7 +985,7 @@ public abstract class PlayerImpl implements Player, Serializable { private void addCostTargetOptions(List options, Ability option, int targetNum, Game game) { for (UUID targetId: option.getCosts().getTargets().get(targetNum).possibleTargets(option.getSourceId(), playerId, game)) { Ability newOption = option.copy(); - newOption.getCosts().getTargets().get(targetNum).addTarget(targetId, game); + newOption.getCosts().getTargets().get(targetNum).addTarget(targetId, option, game); if (targetNum < option.getCosts().getTargets().size() - 1) { addCostTargetOptions(options, newOption, targetNum + 1, game); } diff --git a/Mage/src/mage/players/PlayerList.java b/Mage/src/mage/players/PlayerList.java index ce81d857f88..8650113a234 100644 --- a/Mage/src/mage/players/PlayerList.java +++ b/Mage/src/mage/players/PlayerList.java @@ -38,6 +38,12 @@ import mage.util.CircularList; */ public class PlayerList extends CircularList { + public PlayerList() {} + + public PlayerList(final PlayerList list) { + super(list); + } + public Player getNext(Game game) { Player player; UUID start = this.get(); @@ -64,4 +70,9 @@ public class PlayerList extends CircularList { return player; } + @Override + public PlayerList copy() { + return new PlayerList(this); + } + } diff --git a/Mage/src/mage/players/Players.java b/Mage/src/mage/players/Players.java index 06a5f97d9c6..2ca5cf71705 100644 --- a/Mage/src/mage/players/Players.java +++ b/Mage/src/mage/players/Players.java @@ -37,6 +37,14 @@ import java.util.UUID; */ public class Players extends LinkedHashMap { + public Players() {} + + public Players(final Players players) { + for (UUID id: players.keySet()) { + this.put(id, players.get(id).copy()); + } + } + public void addPlayer(Player player) { this.put(player.getId(), player); } @@ -46,4 +54,9 @@ public class Players extends LinkedHashMap { player.resetPassed(); } } + + public Players copy() { + return new Players(this); + } + } diff --git a/Mage/src/mage/target/Target.java b/Mage/src/mage/target/Target.java index ddb933036c7..dd9a7a42fcf 100644 --- a/Mage/src/mage/target/Target.java +++ b/Mage/src/mage/target/Target.java @@ -48,28 +48,28 @@ public interface Target extends Serializable { public void clearChosen(); public boolean canChoose(UUID sourceId, UUID sourceControllerId, Game game); public List possibleTargets(UUID sourceId, UUID sourceControllerId, Game game); - public boolean choose(Outcome outcome, Game game); + public boolean choose(Outcome outcome, UUID playerId, Ability source, Game game); public String getMessage(); public String getTargetName(); public void setTargetName(String name); public String getTargetedName(Game game); public Zone getZone(); - public boolean isLegal(Game game); - public boolean canTarget(UUID id, Game game); - public void addTarget(UUID id, Game game); - public void addTarget(UUID id, int amount, Game game); + public boolean isLegal(Ability source, Game game); + public boolean canTarget(UUID id, Ability source, Game game); + public void addTarget(UUID id, Ability source, Game game); + public void addTarget(UUID id, int amount, Ability source, Game game); public int getTargetAmount(UUID targetId); public int getNumberOfTargets(); public int getMaxNumberOfTargets(); public List getTargets(); public Filter getFilter(); - public Ability getAbility(); - public void setAbility(Ability ability); public boolean isRequired(); public void setRequired(boolean required); // public UUID getLastTarget(); public UUID getFirstTarget(); + + public Target copy(); } diff --git a/Mage/src/mage/target/TargetAmount.java b/Mage/src/mage/target/TargetAmount.java index dc2b4c8bc65..4454f115249 100644 --- a/Mage/src/mage/target/TargetAmount.java +++ b/Mage/src/mage/target/TargetAmount.java @@ -30,6 +30,7 @@ package mage.target; import java.util.UUID; import mage.Constants.Outcome; +import mage.abilities.Ability; import mage.game.Game; import mage.players.Player; @@ -37,7 +38,7 @@ import mage.players.Player; * * @author BetaSteward_at_googlemail.com */ -public abstract class TargetAmount extends TargetImpl { +public abstract class TargetAmount> extends TargetImpl { int amount; int remainingAmount; @@ -48,10 +49,21 @@ public abstract class TargetAmount extends TargetImpl { this.required = true; } + public TargetAmount(final TargetAmount target) { + super(target); + this.amount = target.amount; + this.remainingAmount = target.remainingAmount; + } + public int getAmountRemaining() { return remainingAmount; } + @Override + public boolean isChosen() { + return doneChosing(); + } + @Override public boolean doneChosing() { return remainingAmount == 0; @@ -64,19 +76,19 @@ public abstract class TargetAmount extends TargetImpl { } @Override - public void addTarget(UUID id, int amount, Game game) { + public void addTarget(UUID id, int amount, Ability source, Game game) { if (amount <= remainingAmount) { - super.addTarget(id, amount, game); + super.addTarget(id, amount, source, game); remainingAmount -= amount; } } @Override - public boolean choose(Outcome outcome, Game game) { - Player player = game.getPlayer(this.source.getControllerId()); + public boolean choose(Outcome outcome, UUID playerId, Ability source, Game game) { + Player player = game.getPlayer(playerId); chosen = remainingAmount == 0; while (remainingAmount > 0) { - if (!player.chooseTargetAmount(outcome, this, game)) { + if (!player.chooseTargetAmount(outcome, this, source, game)) { return chosen; } chosen = remainingAmount == 0; diff --git a/Mage/src/mage/target/TargetCard.java b/Mage/src/mage/target/TargetCard.java index fe7f1189940..e18cc8dfb64 100644 --- a/Mage/src/mage/target/TargetCard.java +++ b/Mage/src/mage/target/TargetCard.java @@ -29,7 +29,9 @@ package mage.target; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.UUID; import mage.Constants.Zone; import mage.cards.Card; @@ -42,7 +44,7 @@ import mage.players.Player; * * @author BetaSteward_at_googlemail.com */ -public class TargetCard extends TargetObject { +public class TargetCard> extends TargetObject> { protected FilterCard filter; @@ -66,27 +68,32 @@ public class TargetCard extends TargetObject { this.targetName = filter.getMessage(); } + public TargetCard(final TargetCard target) { + super(target); + this.filter = target.filter.copy(); + } + @Override public FilterCard getFilter() { return this.filter; } - public boolean choose(Cards cards, Game game) { - Player player = game.getPlayer(this.source.getControllerId()); - while (!isChosen() && !doneChosing()) { - chosen = targets.size() >= minNumberOfTargets; - if (!player.chooseTarget(cards, this, game)) { - return chosen; - } - chosen = targets.size() >= minNumberOfTargets; - } - while (!doneChosing()) { - if (!player.chooseTarget(cards, this, game)) { - break; - } - } - return chosen = true; - } +// public boolean choose(Cards cards, Game game) { +// Player player = game.getPlayer(this.source.getControllerId()); +// while (!isChosen() && !doneChosing()) { +// chosen = targets.size() >= minNumberOfTargets; +// if (!player.chooseTarget(cards, this, game)) { +// return chosen; +// } +// chosen = targets.size() >= minNumberOfTargets; +// } +// while (!doneChosing()) { +// if (!player.chooseTarget(cards, this, game)) { +// break; +// } +// } +// return chosen = true; +// } @Override public boolean canChoose(UUID sourceId, UUID sourceControllerId, Game game) { @@ -96,15 +103,15 @@ public class TargetCard extends TargetObject { if (player != null) { switch (zone) { case HAND: - if (player.getHand().count(filter) >= this.minNumberOfTargets) + if (player.getHand().count(filter, game) >= this.minNumberOfTargets) return true; break; case GRAVEYARD: - if (player.getGraveyard().count(filter) >= this.minNumberOfTargets) + if (player.getGraveyard().count(filter, game) >= this.minNumberOfTargets) return true; break; case LIBRARY: - if (player.getLibrary().count(filter) >= this.minNumberOfTargets) + if (player.getLibrary().count(filter, game) >= this.minNumberOfTargets) return true; break; } @@ -116,35 +123,41 @@ public class TargetCard extends TargetObject { @Override public List possibleTargets(UUID sourceId, UUID sourceControllerId, Game game) { - List possibleTargets = new ArrayList(); - for (UUID playerId: game.getPlayer(sourceControllerId).getInRange()) { - if (filter.matchOwner(playerId)) { - Player player = game.getPlayer(playerId); - if (player != null) { - switch (zone) { - case HAND: - for (Card card: player.getHand().getCards(filter)) { - possibleTargets.add(card.getId()); - } - break; - case GRAVEYARD: - for (Card card: player.getGraveyard().getCards(filter)) { - possibleTargets.add(card.getId()); - } - break; + Map possibleTargets = new HashMap(); + Player player = game.getPlayer(sourceControllerId); + if (player != null) { + switch (zone) { + case HAND: + for (Card card: player.getHand().getCards(filter, game)) { + possibleTargets.put(card.getName(), card.getId()); } - } + break; + case GRAVEYARD: + for (Card card: player.getGraveyard().getCards(filter, game)) { + possibleTargets.put(card.getName(), card.getId()); + } + break; + case LIBRARY: + for (Card card: player.getLibrary().getUniqueCards(game)) { + if (filter.match(card)) + possibleTargets.put(card.getName(), card.getId()); + } + break; } } - return possibleTargets; - + return new ArrayList(possibleTargets.values()); } public boolean canTarget(UUID id, Cards cards, Game game) { - Card card = cards.get(id); + Card card = cards.get(id, game); if (card != null) return filter.match(card); return false; } + @Override + public TargetCard copy() { + return new TargetCard(this); + } + } diff --git a/Mage/src/mage/target/TargetImpl.java b/Mage/src/mage/target/TargetImpl.java index 4e226df2971..422b60ca211 100644 --- a/Mage/src/mage/target/TargetImpl.java +++ b/Mage/src/mage/target/TargetImpl.java @@ -45,7 +45,7 @@ import mage.players.Player; * * @author BetaSteward_at_googlemail.com */ -public abstract class TargetImpl implements Target { +public abstract class TargetImpl> implements Target { protected Map targets = new HashMap(); @@ -54,9 +54,25 @@ public abstract class TargetImpl implements Target { protected int maxNumberOfTargets; protected int minNumberOfTargets; protected boolean required = false; - protected Ability source; protected boolean chosen = false; + @Override + public abstract T copy(); + + public TargetImpl() {} + + public TargetImpl(final TargetImpl target) { + this.targetName = target.targetName; + this.zone = target.zone; + this.maxNumberOfTargets = target.maxNumberOfTargets; + this.minNumberOfTargets = target.minNumberOfTargets; + this.required = target.required; + this.chosen = target.chosen; + for (UUID id: target.targets.keySet()) { + this.targets.put(id, target.targets.get(id)); + } + } + @Override public int getNumberOfTargets() { return this.minNumberOfTargets; @@ -99,6 +115,8 @@ public abstract class TargetImpl implements Target { @Override public boolean isChosen() { + if (targets.size() == maxNumberOfTargets) + return true; return chosen; } @@ -120,29 +138,33 @@ public abstract class TargetImpl implements Target { * @return true if able to add target */ @Override - public void addTarget(UUID id, Game game) { + public void addTarget(UUID id, Ability source, Game game) { //20100423 - 113.3 - if (!targets.containsKey(id)) { - if (source != null) { - if (!game.replaceEvent(GameEvent.getEvent(EventType.TARGET, id, source.getSourceId(), source.getControllerId()))) { - targets.put(id, 0); - game.fireEvent(GameEvent.getEvent(EventType.TARGETED, id, source.getSourceId(), source.getControllerId())); + if (targets.size() < maxNumberOfTargets) { + if (!targets.containsKey(id)) { + if (source != null) { + if (!game.replaceEvent(GameEvent.getEvent(EventType.TARGET, id, source.getSourceId(), source.getControllerId()))) { + targets.put(id, 0); + chosen = targets.size() >= minNumberOfTargets; + game.fireEvent(GameEvent.getEvent(EventType.TARGETED, id, source.getSourceId(), source.getControllerId())); + } + } + else { + targets.put(id, 0); } - } - else { - targets.put(id, 0); } } } @Override - public void addTarget(UUID id, int amount, Game game) { + public void addTarget(UUID id, int amount, Ability source, Game game) { if (targets.containsKey(id)) { amount += targets.get(id); } if (source != null) { if (!game.replaceEvent(GameEvent.getEvent(EventType.TARGET, id, source.getSourceId(), source.getControllerId()))) { targets.put(id, amount); + chosen = targets.size() >= minNumberOfTargets; game.fireEvent(GameEvent.getEvent(EventType.TARGETED, id, source.getSourceId(), source.getControllerId())); } } @@ -152,28 +174,27 @@ public abstract class TargetImpl implements Target { } @Override - public boolean choose(Outcome outcome, Game game) { - Player player = game.getPlayer(this.source.getControllerId()); + public boolean choose(Outcome outcome, UUID playerId, Ability source, Game game) { + Player player = game.getPlayer(playerId); while (!isChosen() && !doneChosing()) { chosen = targets.size() >= minNumberOfTargets; - if (!player.chooseTarget(outcome, this, game)) { + if (!player.chooseTarget(outcome, this, source, game)) { return chosen; } chosen = targets.size() >= minNumberOfTargets; } while (!doneChosing()) { - if (!player.chooseTarget(outcome, this, game)) { + if (!player.chooseTarget(outcome, this, source, game)) { break; } } return chosen = true; } - @Override - public boolean isLegal(Game game) { + public boolean isLegal(Ability source, Game game) { for (UUID targetId: targets.keySet()) { - if (!canTarget(targetId, game)) + if (!canTarget(targetId, source, game)) return false; } return true; @@ -205,14 +226,4 @@ public abstract class TargetImpl implements Target { return null; } - @Override - public Ability getAbility() { - return source; - } - - @Override - public void setAbility(Ability ability) { - this.source = ability; - } - } diff --git a/Mage/src/mage/target/TargetObject.java b/Mage/src/mage/target/TargetObject.java index b4e1397fced..20f0cb4ca96 100644 --- a/Mage/src/mage/target/TargetObject.java +++ b/Mage/src/mage/target/TargetObject.java @@ -31,13 +31,14 @@ package mage.target; import java.util.UUID; import mage.Constants.Zone; import mage.MageObject; +import mage.abilities.Ability; import mage.game.Game; /** * * @author BetaSteward_at_googlemail.com */ -public abstract class TargetObject extends TargetImpl { +public abstract class TargetObject> extends TargetImpl { protected TargetObject() {} @@ -49,6 +50,10 @@ public abstract class TargetObject extends TargetImpl { this(numTargets, numTargets, zone); } + public TargetObject(final TargetObject target) { + super(target); + } + public TargetObject(int minNumTargets, int maxNumTargets, Zone zone) { this.minNumberOfTargets = minNumTargets; this.maxNumberOfTargets = maxNumTargets; @@ -66,7 +71,7 @@ public abstract class TargetObject extends TargetImpl { } @Override - public boolean canTarget(UUID id, Game game) { + public boolean canTarget(UUID id, Ability source, Game game) { MageObject object = game.getObject(id); if (object != null && object.getZone().match(zone)) return getFilter().match(object); diff --git a/Mage/src/mage/target/TargetPermanent.java b/Mage/src/mage/target/TargetPermanent.java index 9ae229e8fd8..85ff91e2c67 100644 --- a/Mage/src/mage/target/TargetPermanent.java +++ b/Mage/src/mage/target/TargetPermanent.java @@ -29,12 +29,14 @@ package mage.target; import java.util.ArrayList; -import java.util.Iterator; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.UUID; import mage.Constants.TargetController; import mage.Constants.Zone; import mage.MageObject; +import mage.abilities.Ability; import mage.filter.FilterPermanent; import mage.game.Game; import mage.game.permanent.Permanent; @@ -43,7 +45,7 @@ import mage.game.permanent.Permanent; * * @author BetaSteward_at_googlemail.com */ -public class TargetPermanent extends TargetObject { +public class TargetPermanent> extends TargetObject> { protected FilterPermanent filter; protected TargetController controller; @@ -69,18 +71,24 @@ public class TargetPermanent extends TargetObject { this.controller = controller; } - @Override - public boolean canTarget(UUID id, Game game) { - return canTarget(null, id, game); + public TargetPermanent(final TargetPermanent target) { + super(target); + this.filter = target.filter.copy(); + this.controller = target.controller; } - public boolean canTarget(UUID controllerId, UUID id, Game game) { + @Override + public boolean canTarget(UUID id, Ability source, Game game) { + return canTarget(null, id, source, game); + } + + public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) { Permanent permanent = game.getPermanent(id); if (controllerId != null) setController(controllerId, game); if (permanent != null) { - if (this.source != null) - return permanent.canBeTargetedBy(game.getObject(this.source.getSourceId())) && filter.match(permanent); + if (source != null) + return permanent.canBeTargetedBy(game.getObject(source.getSourceId())) && filter.match(permanent); else return filter.match(permanent); } @@ -120,6 +128,8 @@ public class TargetPermanent extends TargetObject { public boolean canChoose(UUID sourceId, UUID sourceControllerId, Game game) { int count = 0; MageObject targetSource = game.getObject(sourceId); + if (sourceControllerId != null) + setController(sourceControllerId, game); for (Permanent permanent: game.getBattlefield().getActivePermanents(filter, sourceControllerId, game)) { if (permanent.canBeTargetedBy(targetSource)) { count++; @@ -132,14 +142,21 @@ public class TargetPermanent extends TargetObject { @Override public List possibleTargets(UUID sourceId, UUID sourceControllerId, Game game) { - List possibleTargets = new ArrayList(); + Map possibleTargets = new HashMap(); MageObject targetSource = game.getObject(sourceId); + if (sourceControllerId != null) + setController(sourceControllerId, game); for (Permanent permanent: game.getBattlefield().getActivePermanents(filter, sourceControllerId, game)) { if (permanent.canBeTargetedBy(targetSource)) { - possibleTargets.add(permanent.getId()); + possibleTargets.put(permanent.getValue().hashCode(), permanent.getId()); } } - return possibleTargets; + return new ArrayList(possibleTargets.values()); + } + + @Override + public TargetPermanent copy() { + return new TargetPermanent(this); } } diff --git a/Mage/src/mage/target/TargetPlayer.java b/Mage/src/mage/target/TargetPlayer.java index 4c2eaafef2a..52f5953f719 100644 --- a/Mage/src/mage/target/TargetPlayer.java +++ b/Mage/src/mage/target/TargetPlayer.java @@ -33,6 +33,7 @@ import java.util.List; import java.util.UUID; import mage.Constants.Zone; import mage.MageObject; +import mage.abilities.Ability; import mage.filter.FilterPlayer; import mage.game.Game; import mage.players.Player; @@ -41,7 +42,7 @@ import mage.players.Player; * * @author BetaSteward_at_googlemail.com */ -public class TargetPlayer extends TargetImpl { +public class TargetPlayer> extends TargetImpl> { protected FilterPlayer filter = new FilterPlayer(); @@ -56,10 +57,14 @@ public class TargetPlayer extends TargetImpl { public TargetPlayer(int minNumTargets, int maxNumTargets) { this.minNumberOfTargets = minNumTargets; this.maxNumberOfTargets = maxNumTargets; - this.zone = Zone.PLAYER; this.targetName = "player"; } + public TargetPlayer(final TargetPlayer target) { + super(target); + this.filter = target.filter.copy(); + } + @Override public FilterPlayer getFilter() { return filter; @@ -97,20 +102,20 @@ public class TargetPlayer extends TargetImpl { } @Override - public boolean isLegal(Game game) { + public boolean isLegal(Ability source, Game game) { for (UUID playerId: targets.keySet()) { - if (!canTarget(playerId, game)) + if (!canTarget(playerId, source, game)) return false; } return true; } @Override - public boolean canTarget(UUID id, Game game) { + public boolean canTarget(UUID id, Ability source, Game game) { Player player = game.getPlayer(id); if (player != null) { if (source != null) - return player.canBeTargetedBy(game.getObject(this.source.getSourceId())) && filter.match(player); + return player.canBeTargetedBy(game.getObject(source.getSourceId())) && filter.match(player); else return filter.match(player); } @@ -126,4 +131,9 @@ public class TargetPlayer extends TargetImpl { return sb.toString(); } + @Override + public TargetPlayer copy() { + return new TargetPlayer(this); + } + } diff --git a/Mage/src/mage/target/TargetSpell.java b/Mage/src/mage/target/TargetSpell.java index af49aaaea7a..0af6f384439 100644 --- a/Mage/src/mage/target/TargetSpell.java +++ b/Mage/src/mage/target/TargetSpell.java @@ -32,6 +32,7 @@ import java.util.ArrayList; import java.util.List; import java.util.UUID; import mage.Constants.Zone; +import mage.abilities.Ability; import mage.filter.FilterSpell; import mage.game.Game; import mage.game.stack.Spell; @@ -41,7 +42,7 @@ import mage.game.stack.StackObject; * * @author BetaSteward_at_googlemail.com */ -public class TargetSpell extends TargetObject { +public class TargetSpell extends TargetObject { protected FilterSpell filter; @@ -65,13 +66,18 @@ public class TargetSpell extends TargetObject { this.targetName = filter.getMessage(); } + public TargetSpell(final TargetSpell target) { + super(target); + this.filter = target.filter.copy(); + } + @Override public FilterSpell getFilter() { return filter; } @Override - public boolean canTarget(UUID id, Game game) { + public boolean canTarget(UUID id, Ability source, Game game) { StackObject stackObject = game.getStack().getStackObject(id); if (stackObject != null && stackObject instanceof Spell) { return filter.match((Spell)stackObject); @@ -103,4 +109,9 @@ public class TargetSpell extends TargetObject { return possibleTargets; } + @Override + public TargetSpell copy() { + return new TargetSpell(this); + } + } diff --git a/Mage/src/mage/target/Targets.java b/Mage/src/mage/target/Targets.java index 0f6dcb62b32..f01949f3b36 100644 --- a/Mage/src/mage/target/Targets.java +++ b/Mage/src/mage/target/Targets.java @@ -41,25 +41,14 @@ import mage.game.Game; */ public class Targets extends ArrayList { - protected Ability source; + public Targets() {} - public Targets(Ability ability) { - this.source = ability; - } - - public void setSource(Ability ability) { - this.source = ability; - for (Target target: this) { - target.setAbility(ability); + public Targets(final Targets targets) { + for (Target target: targets) { + this.add(target.copy()); } } - @Override - public boolean add(Target target) { - target.setAbility(source); - return super.add(target); - } - public List getUnchosen() { List unchosen = new ArrayList(); for (Target target: this) { @@ -83,20 +72,20 @@ public class Targets extends ArrayList { return true; } - public boolean choose(Outcome outcome, Game game) { + public boolean choose(Outcome outcome, UUID playerId, Ability source, Game game) { if (this.size() > 0) { while (!isChosen()) { Target target = this.getUnchosen().get(0); - if (!target.choose(outcome, game)) + if (!target.choose(outcome, playerId, source, game)) return false; } } return true; } - public boolean stillLegal(Game game) { + public boolean stillLegal(Ability source, Game game) { for (Target target: this) { - if (!target.isLegal(game)) { + if (!target.isLegal(source, game)) { return false; } } @@ -117,4 +106,7 @@ public class Targets extends ArrayList { return null; } + public Targets copy() { + return new Targets(this); + } } diff --git a/Mage/src/mage/target/common/TargetAttackingCreature.java b/Mage/src/mage/target/common/TargetAttackingCreature.java index 9c6801dcdc6..1fa99c761fd 100644 --- a/Mage/src/mage/target/common/TargetAttackingCreature.java +++ b/Mage/src/mage/target/common/TargetAttackingCreature.java @@ -35,7 +35,7 @@ import mage.filter.common.FilterAttackingCreature; * * @author BetaSteward_at_googlemail.com */ -public class TargetAttackingCreature extends TargetCreaturePermanent { +public class TargetAttackingCreature extends TargetCreaturePermanent { public TargetAttackingCreature() { this(1, 1, new FilterAttackingCreature(), TargetController.ANY); @@ -49,4 +49,14 @@ public class TargetAttackingCreature extends TargetCreaturePermanent { super(1, 1, filter, controller); this.targetName = filter.getMessage(); } + + public TargetAttackingCreature(final TargetAttackingCreature target) { + super(target); + } + + @Override + public TargetAttackingCreature copy() { + return new TargetAttackingCreature(this); + } + } diff --git a/Mage/src/mage/target/common/TargetBasicLandCard.java b/Mage/src/mage/target/common/TargetBasicLandCard.java index 87ed7a54f07..d924b96ca17 100644 --- a/Mage/src/mage/target/common/TargetBasicLandCard.java +++ b/Mage/src/mage/target/common/TargetBasicLandCard.java @@ -35,7 +35,7 @@ import mage.target.TargetCard; * * @author BetaSteward_at_googlemail.com */ -public class TargetBasicLandCard extends TargetCard { +public class TargetBasicLandCard extends TargetCard { public TargetBasicLandCard(Zone zone) { super(zone); @@ -46,4 +46,12 @@ public class TargetBasicLandCard extends TargetCard { filter.getName().add("Plains"); } + public TargetBasicLandCard(final TargetBasicLandCard target) { + super(target); + } + + @Override + public TargetBasicLandCard copy() { + return new TargetBasicLandCard(this); + } } diff --git a/Mage/src/mage/target/common/TargetCardInGraveyard.java b/Mage/src/mage/target/common/TargetCardInGraveyard.java index 9f64dc8143b..6e57f9f39af 100644 --- a/Mage/src/mage/target/common/TargetCardInGraveyard.java +++ b/Mage/src/mage/target/common/TargetCardInGraveyard.java @@ -30,6 +30,7 @@ package mage.target.common; import java.util.UUID; import mage.Constants.Zone; +import mage.abilities.Ability; import mage.cards.Card; import mage.filter.FilterCard; import mage.game.Game; @@ -39,7 +40,7 @@ import mage.target.TargetCard; * * @author BetaSteward_at_googlemail.com */ -public class TargetCardInGraveyard extends TargetCard { +public class TargetCardInGraveyard extends TargetCard { public TargetCardInGraveyard() { this(1, 1, new FilterCard()); @@ -58,12 +59,21 @@ public class TargetCardInGraveyard extends TargetCard { this.targetName = filter.getMessage(); } + public TargetCardInGraveyard(final TargetCardInGraveyard target) { + super(target); + } + @Override - public boolean canTarget(UUID id, Game game) { - Card card = game.getPlayer(this.source.getControllerId()).getGraveyard().get(id); + public boolean canTarget(UUID id, Ability source, Game game) { + Card card = game.getPlayer(source.getControllerId()).getGraveyard().get(id, game); if (card != null) return filter.match(card); return false; } + @Override + public TargetCardInGraveyard copy() { + return new TargetCardInGraveyard(this); + } + } diff --git a/Mage/src/mage/target/common/TargetCardInHand.java b/Mage/src/mage/target/common/TargetCardInHand.java index ad4e91b21eb..d64c3712612 100644 --- a/Mage/src/mage/target/common/TargetCardInHand.java +++ b/Mage/src/mage/target/common/TargetCardInHand.java @@ -30,6 +30,7 @@ package mage.target.common; import java.util.UUID; import mage.Constants.Zone; +import mage.abilities.Ability; import mage.cards.Card; import mage.filter.FilterCard; import mage.game.Game; @@ -39,7 +40,7 @@ import mage.target.TargetCard; * * @author BetaSteward_at_googlemail.com */ -public class TargetCardInHand extends TargetCard { +public class TargetCardInHand extends TargetCard { public TargetCardInHand() { this(1, 1, new FilterCard()); @@ -58,12 +59,21 @@ public class TargetCardInHand extends TargetCard { this.targetName = filter.getMessage(); } + public TargetCardInHand(final TargetCardInHand target) { + super(target); + } + @Override - public boolean canTarget(UUID id, Game game) { - Card card = game.getPlayer(this.source.getControllerId()).getHand().get(id); + public boolean canTarget(UUID id, Ability source, Game game) { + Card card = game.getPlayer(source.getControllerId()).getHand().get(id, game); if (card != null) return filter.match(card); return false; } + @Override + public TargetCardInHand copy() { + return new TargetCardInHand(this); + } + } diff --git a/Mage/src/mage/target/common/TargetCardInLibrary.java b/Mage/src/mage/target/common/TargetCardInLibrary.java index 07ee3fe0f16..533875163f2 100644 --- a/Mage/src/mage/target/common/TargetCardInLibrary.java +++ b/Mage/src/mage/target/common/TargetCardInLibrary.java @@ -29,17 +29,21 @@ package mage.target.common; import java.util.UUID; +import mage.Constants.Outcome; import mage.Constants.Zone; +import mage.abilities.Ability; import mage.cards.Card; +import mage.cards.CardsImpl; import mage.filter.FilterCard; import mage.game.Game; +import mage.players.Player; import mage.target.TargetCard; /** * * @author BetaSteward_at_googlemail.com */ -public class TargetCardInLibrary extends TargetCard { +public class TargetCardInLibrary extends TargetCard { public TargetCardInLibrary() { this(1, 1, new FilterCard()); @@ -57,12 +61,39 @@ public class TargetCardInLibrary extends TargetCard { super(minNumTargets, maxNumTargets, Zone.LIBRARY, filter); } + public TargetCardInLibrary(final TargetCardInLibrary target) { + super(target); + } + @Override - public boolean canTarget(UUID id, Game game) { - Card card = game.getPlayer(this.source.getControllerId()).getLibrary().getCard(id); + public boolean choose(Outcome outcome, UUID playerId, Ability source, Game game) { + Player player = game.getPlayer(playerId); + while (!isChosen() && !doneChosing()) { + chosen = targets.size() >= minNumberOfTargets; + if (!player.chooseTarget(new CardsImpl(Zone.LIBRARY, player.getLibrary().getCards(game)), this, null, game)) { + return chosen; + } + chosen = targets.size() >= minNumberOfTargets; + } + while (!doneChosing()) { + if (!player.chooseTarget(new CardsImpl(Zone.LIBRARY, player.getLibrary().getCards(game)), this, null, game)) { + break; + } + } + return chosen = true; + } + + @Override + public boolean canTarget(UUID id, Ability source, Game game) { + Card card = game.getPlayer(source.getControllerId()).getLibrary().getCard(id, game); if (card != null) return filter.match(card); return false; } + @Override + public TargetCardInLibrary copy() { + return new TargetCardInLibrary(this); + } + } diff --git a/Mage/src/mage/target/common/TargetControlledPermanent.java b/Mage/src/mage/target/common/TargetControlledPermanent.java index 09a6663a296..271931013ee 100644 --- a/Mage/src/mage/target/common/TargetControlledPermanent.java +++ b/Mage/src/mage/target/common/TargetControlledPermanent.java @@ -36,7 +36,7 @@ import mage.target.TargetPermanent; * * @author BetaSteward_at_googlemail.com */ -public class TargetControlledPermanent extends TargetPermanent { +public class TargetControlledPermanent extends TargetPermanent { public TargetControlledPermanent() { this(1, 1, new FilterPermanent()); @@ -51,4 +51,12 @@ public class TargetControlledPermanent extends TargetPermanent { this.targetName = filter.getMessage(); } + public TargetControlledPermanent(final TargetControlledPermanent target) { + super(target); + } + + @Override + public TargetControlledPermanent copy() { + return new TargetControlledPermanent(this); + } } diff --git a/Mage/src/mage/target/common/TargetCreatureOrPlayer.java b/Mage/src/mage/target/common/TargetCreatureOrPlayer.java index f89b73dcd55..73121303db3 100644 --- a/Mage/src/mage/target/common/TargetCreatureOrPlayer.java +++ b/Mage/src/mage/target/common/TargetCreatureOrPlayer.java @@ -29,10 +29,13 @@ package mage.target.common; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.UUID; import mage.Constants.Zone; import mage.MageObject; +import mage.abilities.Ability; import mage.filter.Filter; import mage.filter.common.FilterCreatureOrPlayer; import mage.filter.common.FilterCreaturePermanent; @@ -45,7 +48,7 @@ import mage.target.TargetImpl; * * @author BetaSteward_at_googlemail.com */ -public class TargetCreatureOrPlayer extends TargetImpl { +public class TargetCreatureOrPlayer extends TargetImpl { protected FilterCreatureOrPlayer filter; @@ -65,24 +68,29 @@ public class TargetCreatureOrPlayer extends TargetImpl { this.targetName = filter.getMessage(); } + public TargetCreatureOrPlayer(final TargetCreatureOrPlayer target) { + super(target); + this.filter = target.filter.copy(); + } + @Override public Filter getFilter() { return this.filter; } @Override - public boolean canTarget(UUID id, Game game) { + public boolean canTarget(UUID id, Ability source, Game game) { Permanent permanent = game.getPermanent(id); - MageObject targetSource = game.getObject(this.source.getSourceId()); + MageObject targetSource = game.getObject(source.getSourceId()); if (permanent != null) { - if (this.source != null) + if (source != null) return permanent.canBeTargetedBy(targetSource) && filter.match(permanent); else return filter.match(permanent); } Player player = game.getPlayer(id); if (player != null) - if (this.source != null) + if (source != null) return player.canBeTargetedBy(targetSource) && filter.match(player); else return filter.match(player); @@ -113,20 +121,20 @@ public class TargetCreatureOrPlayer extends TargetImpl { @Override public List possibleTargets(UUID sourceId, UUID sourceControllerId, Game game) { - List possibleTargets = new ArrayList(); + Map possibleTargets = new HashMap(); MageObject targetSource = game.getObject(sourceId); for (UUID playerId: game.getPlayer(sourceControllerId).getInRange()) { Player player = game.getPlayer(playerId); if (player != null && player.canBeTargetedBy(targetSource) && filter.match(player)) { - possibleTargets.add(playerId); + possibleTargets.put(player.hashCode(), playerId); } } for (Permanent permanent: game.getBattlefield().getActivePermanents(new FilterCreaturePermanent(), sourceControllerId, game)) { if (permanent.canBeTargetedBy(targetSource) && filter.match(permanent)) { - possibleTargets.add(permanent.getId()); + possibleTargets.put(permanent.getValue().hashCode(), permanent.getId()); } } - return possibleTargets; + return new ArrayList(possibleTargets.values()); } @Override @@ -145,4 +153,9 @@ public class TargetCreatureOrPlayer extends TargetImpl { return sb.toString(); } + @Override + public TargetCreatureOrPlayer copy() { + return new TargetCreatureOrPlayer(this); + } + } diff --git a/Mage/src/mage/target/common/TargetCreatureOrPlayerAmount.java b/Mage/src/mage/target/common/TargetCreatureOrPlayerAmount.java index 11793500e71..f688bbf94b8 100644 --- a/Mage/src/mage/target/common/TargetCreatureOrPlayerAmount.java +++ b/Mage/src/mage/target/common/TargetCreatureOrPlayerAmount.java @@ -29,10 +29,13 @@ package mage.target.common; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.UUID; import mage.Constants.Zone; import mage.MageObject; +import mage.abilities.Ability; import mage.filter.Filter; import mage.filter.common.FilterCreatureOrPlayer; import mage.filter.common.FilterCreaturePermanent; @@ -45,7 +48,7 @@ import mage.target.TargetAmount; * * @author BetaSteward_at_googlemail.com */ -public class TargetCreatureOrPlayerAmount extends TargetAmount { +public class TargetCreatureOrPlayerAmount extends TargetAmount { protected FilterCreatureOrPlayer filter; @@ -56,24 +59,29 @@ public class TargetCreatureOrPlayerAmount extends TargetAmount { this.targetName = filter.getMessage(); } + public TargetCreatureOrPlayerAmount(final TargetCreatureOrPlayerAmount target) { + super(target); + this.filter = target.filter.copy(); + } + @Override public Filter getFilter() { return this.filter; } @Override - public boolean canTarget(UUID id, Game game) { + public boolean canTarget(UUID id, Ability source, Game game) { Permanent permanent = game.getPermanent(id); - MageObject targetSource = game.getObject(this.source.getSourceId()); + MageObject targetSource = game.getObject(source.getSourceId()); if (permanent != null) { - if (this.source != null) + if (source != null) return permanent.canBeTargetedBy(targetSource) && filter.match(permanent); else return filter.match(permanent); } Player player = game.getPlayer(id); if (player != null) - if (this.source != null) + if (source != null) return player.canBeTargetedBy(targetSource) && filter.match(player); else return filter.match(player); @@ -104,20 +112,20 @@ public class TargetCreatureOrPlayerAmount extends TargetAmount { @Override public List possibleTargets(UUID sourceId, UUID sourceControllerId, Game game) { - List possibleTargets = new ArrayList(); + Map possibleTargets = new HashMap(); MageObject targetSource = game.getObject(sourceId); for (UUID playerId: game.getPlayer(sourceControllerId).getInRange()) { Player player = game.getPlayer(playerId); if (player != null && player.canBeTargetedBy(targetSource) && filter.match(player)) { - possibleTargets.add(playerId); + possibleTargets.put(player.hashCode(), playerId); } } for (Permanent permanent: game.getBattlefield().getActivePermanents(new FilterCreaturePermanent(), sourceControllerId, game)) { if (permanent.canBeTargetedBy(targetSource) && filter.match(permanent)) { - possibleTargets.add(permanent.getId()); + possibleTargets.put(permanent.getValue().hashCode(), permanent.getId()); } } - return possibleTargets; + return new ArrayList(possibleTargets.values()); } @Override @@ -136,4 +144,9 @@ public class TargetCreatureOrPlayerAmount extends TargetAmount { return sb.toString(); } + @Override + public TargetCreatureOrPlayerAmount copy() { + return new TargetCreatureOrPlayerAmount(this); + } + } diff --git a/Mage/src/mage/target/common/TargetCreaturePermanent.java b/Mage/src/mage/target/common/TargetCreaturePermanent.java index 625510db371..2b00cee9e45 100644 --- a/Mage/src/mage/target/common/TargetCreaturePermanent.java +++ b/Mage/src/mage/target/common/TargetCreaturePermanent.java @@ -36,12 +36,16 @@ import mage.target.TargetPermanent; * * @author BetaSteward_at_googlemail.com */ -public class TargetCreaturePermanent extends TargetPermanent { +public class TargetCreaturePermanent> extends TargetPermanent> { public TargetCreaturePermanent() { this(1, 1, new FilterCreaturePermanent(), TargetController.ANY); } + public TargetCreaturePermanent(FilterCreaturePermanent filter) { + this(1, 1, filter, TargetController.ANY); + } + public TargetCreaturePermanent(int numTargets, TargetController controller) { this(numTargets, numTargets, new FilterCreaturePermanent(), controller); } @@ -51,4 +55,13 @@ public class TargetCreaturePermanent extends TargetPermanent { this.targetName = filter.getMessage(); } + public TargetCreaturePermanent(final TargetCreaturePermanent target) { + super(target); + } + + @Override + public TargetCreaturePermanent copy() { + return new TargetCreaturePermanent(this); + } + } diff --git a/Mage/src/mage/target/common/TargetDefender.java b/Mage/src/mage/target/common/TargetDefender.java index 71be3735371..dcba0a7a6d5 100644 --- a/Mage/src/mage/target/common/TargetDefender.java +++ b/Mage/src/mage/target/common/TargetDefender.java @@ -35,6 +35,7 @@ import java.util.UUID; import mage.Constants.CardType; import mage.Constants.Zone; import mage.MageObject; +import mage.abilities.Ability; import mage.filter.Filter; import mage.filter.common.FilterPlaneswalkerOrPlayer; import mage.filter.common.FilterPlaneswalkerPermanent; @@ -47,7 +48,7 @@ import mage.target.TargetImpl; * * @author BetaSteward_at_googlemail.com */ -public class TargetDefender extends TargetImpl { +public class TargetDefender extends TargetImpl { protected FilterPlaneswalkerOrPlayer filter; protected UUID attackerId; @@ -69,6 +70,12 @@ public class TargetDefender extends TargetImpl { this.attackerId = attackerId; } + public TargetDefender(final TargetDefender target) { + super(target); + this.filter = target.filter.copy(); + this.attackerId = target.attackerId; + } + @Override public Filter getFilter() { return this.filter; @@ -131,7 +138,7 @@ public class TargetDefender extends TargetImpl { } @Override - public boolean canTarget(UUID id, Game game) { + public boolean canTarget(UUID id, Ability source, Game game) { Player player = game.getPlayer(id); MageObject targetSource = game.getObject(attackerId); if (player != null) { @@ -144,5 +151,10 @@ public class TargetDefender extends TargetImpl { return false; } + @Override + public TargetDefender copy() { + return new TargetDefender(this); + } + } diff --git a/Mage/src/mage/target/common/TargetDiscard.java b/Mage/src/mage/target/common/TargetDiscard.java index 62a396109de..f2e97414c5d 100644 --- a/Mage/src/mage/target/common/TargetDiscard.java +++ b/Mage/src/mage/target/common/TargetDiscard.java @@ -30,6 +30,7 @@ package mage.target.common; import java.util.UUID; import mage.Constants.Zone; +import mage.abilities.Ability; import mage.cards.Card; import mage.filter.FilterCard; import mage.game.Game; @@ -39,7 +40,7 @@ import mage.target.TargetCard; * * @author BetaSteward_at_googlemail.com */ -public class TargetDiscard extends TargetCard { +public class TargetDiscard extends TargetCard { private UUID playerId; @@ -63,12 +64,22 @@ public class TargetDiscard extends TargetCard { this.targetName = "card to discard"; } + public TargetDiscard(final TargetDiscard target) { + super(target); + this.playerId = target.playerId; + } + @Override - public boolean canTarget(UUID id, Game game) { - Card card = game.getPlayer(playerId).getHand().get(id); + public boolean canTarget(UUID id, Ability source, Game game) { + Card card = game.getPlayer(playerId).getHand().get(id, game); if (card != null) return filter.match(card); return false; } + @Override + public TargetDiscard copy() { + return new TargetDiscard(this); + } + } diff --git a/Mage/src/mage/target/common/TargetLandPermanent.java b/Mage/src/mage/target/common/TargetLandPermanent.java index ef2cd8788fc..5c01d853d81 100644 --- a/Mage/src/mage/target/common/TargetLandPermanent.java +++ b/Mage/src/mage/target/common/TargetLandPermanent.java @@ -36,7 +36,7 @@ import mage.target.TargetPermanent; * * @author BetaSteward_at_googlemail.com */ -public class TargetLandPermanent extends TargetPermanent { +public class TargetLandPermanent> extends TargetPermanent> { public TargetLandPermanent() { this(1, 1, new FilterLandPermanent(), TargetController.ANY); @@ -55,4 +55,12 @@ public class TargetLandPermanent extends TargetPermanent { this.targetName = filter.getMessage(); } + public TargetLandPermanent(final TargetLandPermanent target) { + super(target); + } + + @Override + public TargetLandPermanent copy() { + return new TargetLandPermanent(this); + } } diff --git a/Mage/src/mage/target/common/TargetNonBasicLandPermanent.java b/Mage/src/mage/target/common/TargetNonBasicLandPermanent.java index 66bf972bdef..32b14b31a24 100644 --- a/Mage/src/mage/target/common/TargetNonBasicLandPermanent.java +++ b/Mage/src/mage/target/common/TargetNonBasicLandPermanent.java @@ -32,7 +32,7 @@ package mage.target.common; * * @author BetaSteward_at_googlemail.com */ -public class TargetNonBasicLandPermanent extends TargetLandPermanent { +public class TargetNonBasicLandPermanent extends TargetLandPermanent { public TargetNonBasicLandPermanent() { filter.setNotName(true); @@ -44,4 +44,12 @@ public class TargetNonBasicLandPermanent extends TargetLandPermanent { this.targetName = "nonbasic land"; } + public TargetNonBasicLandPermanent(final TargetNonBasicLandPermanent target) { + super(target); + } + + @Override + public TargetNonBasicLandPermanent copy() { + return new TargetNonBasicLandPermanent(this); + } } diff --git a/Mage/src/mage/target/common/TargetNonlandPermanent.java b/Mage/src/mage/target/common/TargetNonlandPermanent.java index 789fa2093cd..6d419afae47 100644 --- a/Mage/src/mage/target/common/TargetNonlandPermanent.java +++ b/Mage/src/mage/target/common/TargetNonlandPermanent.java @@ -36,7 +36,7 @@ import mage.target.TargetPermanent; * * @author BetaSteward_at_googlemail.com */ -public class TargetNonlandPermanent extends TargetPermanent { +public class TargetNonlandPermanent extends TargetPermanent { public TargetNonlandPermanent() { this(1, 1, TargetController.ANY); @@ -51,4 +51,12 @@ public class TargetNonlandPermanent extends TargetPermanent { this.targetName = filter.getMessage(); } + public TargetNonlandPermanent(final TargetNonlandPermanent target) { + super(target); + } + + @Override + public TargetNonlandPermanent copy() { + return new TargetNonlandPermanent(this); + } } diff --git a/Mage/src/mage/target/common/TargetOpponent.java b/Mage/src/mage/target/common/TargetOpponent.java index d14e785d946..e40612386d1 100644 --- a/Mage/src/mage/target/common/TargetOpponent.java +++ b/Mage/src/mage/target/common/TargetOpponent.java @@ -29,6 +29,7 @@ package mage.target.common; import java.util.UUID; +import mage.abilities.Ability; import mage.game.Game; import mage.target.TargetPlayer; @@ -36,25 +37,34 @@ import mage.target.TargetPlayer; * * @author BetaSteward_at_googlemail.com */ -public class TargetOpponent extends TargetPlayer { +public class TargetOpponent extends TargetPlayer { public TargetOpponent() { super(); this.targetName = "opponent"; } + + public TargetOpponent(final TargetOpponent target) { + super(target); + } @Override public boolean canChoose(UUID sourceId, UUID sourceControllerId, Game game) { filter.getPlayerId().clear(); - filter.getPlayerId().addAll(game.getOpponents(this.getAbility().getControllerId())); + filter.getPlayerId().addAll(game.getOpponents(sourceControllerId)); return super.canChoose(sourceId, sourceControllerId, game); } @Override - public boolean canTarget(UUID id, Game game) { + public boolean canTarget(UUID id, Ability source, Game game) { filter.getPlayerId().clear(); - filter.getPlayerId().addAll(game.getOpponents(this.getAbility().getControllerId())); - return super.canTarget(id, game); + filter.getPlayerId().addAll(game.getOpponents(source.getControllerId())); + return super.canTarget(id, source, game); + } + + @Override + public TargetOpponent copy() { + return new TargetOpponent(this); } } diff --git a/Mage/src/mage/util/CircularList.java b/Mage/src/mage/util/CircularList.java index f3d91556e12..61fc7f25e6f 100644 --- a/Mage/src/mage/util/CircularList.java +++ b/Mage/src/mage/util/CircularList.java @@ -43,6 +43,7 @@ import java.util.concurrent.locks.ReentrantLock; * @author BetaSteward_at_googlemail.com */ public class CircularList implements List, Iterable, Serializable { + //TODO: might have to make E extend Copyable protected List list = new ArrayList(); @@ -51,6 +52,20 @@ public class CircularList implements List, Iterable, Serializable { protected int modCount; protected int index; + public CircularList() {} + + public CircularList(final CircularList cList) { + this.modCount = cList.modCount; + for (E entry: cList.list) { + this.list.add((E)entry); + } + this.index = cList.index; + } + + public CircularList copy() { + return new CircularList(this); + } + /** * Inserts an element into the current position */ diff --git a/Mage/src/mage/util/Copyable.java b/Mage/src/mage/util/Copyable.java new file mode 100644 index 00000000000..bb80fd01c7a --- /dev/null +++ b/Mage/src/mage/util/Copyable.java @@ -0,0 +1,38 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.util; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public interface Copyable { + + public abstract T copy(); +} diff --git a/Mage/src/mage/watchers/Watcher.java b/Mage/src/mage/watchers/Watcher.java index b1128e90383..4f04b60ac81 100644 --- a/Mage/src/mage/watchers/Watcher.java +++ b/Mage/src/mage/watchers/Watcher.java @@ -37,7 +37,7 @@ import mage.game.events.GameEvent; * * @author BetaSteward_at_googlemail.com */ -public interface Watcher extends Serializable { +public interface Watcher> extends Serializable { public UUID getControllerId(); public void setControllerId(UUID controllerId); @@ -45,5 +45,6 @@ public interface Watcher extends Serializable { public void watch(GameEvent event, Game game); public boolean conditionMet(); public void reset(); - + + public abstract T copy(); } diff --git a/Mage/src/mage/watchers/WatcherImpl.java b/Mage/src/mage/watchers/WatcherImpl.java index 95f284328b9..ebb7ee8d5ce 100644 --- a/Mage/src/mage/watchers/WatcherImpl.java +++ b/Mage/src/mage/watchers/WatcherImpl.java @@ -36,14 +36,21 @@ import java.util.UUID; * * @author BetaSteward_at_googlemail.com */ -public abstract class WatcherImpl implements Watcher { +public abstract class WatcherImpl> implements Watcher { protected UUID controllerId; protected String key; - protected boolean condition = false; + protected boolean condition; - public WatcherImpl(String key) { + public WatcherImpl(String key, UUID controllerId) { this.key = key; + this.controllerId = controllerId; + } + + public WatcherImpl(final WatcherImpl watcher) { + this.condition = watcher.condition; + this.key = watcher.key; + this.controllerId = watcher.controllerId; } @Override diff --git a/Mage/src/mage/watchers/Watchers.java b/Mage/src/mage/watchers/Watchers.java index eb132c75813..6871c426444 100644 --- a/Mage/src/mage/watchers/Watchers.java +++ b/Mage/src/mage/watchers/Watchers.java @@ -39,6 +39,14 @@ import mage.game.events.GameEvent; */ public class Watchers extends ArrayList { + public Watchers copy() { + Watchers newCopy = new Watchers(); + for (Watcher watcher: this) { + newCopy.add(watcher.copy()); + } + return newCopy; + } + public void watch(GameEvent event, Game game) { for (Watcher watcher: this) { watcher.watch(event, game);