From 79c9c914e21521c03ed219954810105fac3a639a Mon Sep 17 00:00:00 2001 From: Hanmac Date: Mon, 16 Apr 2018 20:33:06 +0200 Subject: [PATCH] LandAbility: do extra ability class, so it can use mayPlay --- .../src/main/java/forge/ai/AiController.java | 8 +- .../java/forge/ai/PlayerControllerAi.java | 6 +- .../ai/simulation/SpellAbilityPicker.java | 30 +--- forge-game/src/main/java/forge/game/Game.java | 17 --- .../src/main/java/forge/game/card/Card.java | 16 +- .../forge/game/spellability/LandAbility.java | 143 ++++++++++++++++++ .../ai/simulation/SpellAbilityPickerTest.java | 2 +- .../forge/match/input/InputPassPriority.java | 3 +- .../src/main/java/forge/player/HumanPlay.java | 8 +- 9 files changed, 181 insertions(+), 52 deletions(-) create mode 100644 forge-game/src/main/java/forge/game/spellability/LandAbility.java diff --git a/forge-ai/src/main/java/forge/ai/AiController.java b/forge-ai/src/main/java/forge/ai/AiController.java index 4a2eb3651b9..ce649c488f7 100644 --- a/forge-ai/src/main/java/forge/ai/AiController.java +++ b/forge-ai/src/main/java/forge/ai/AiController.java @@ -1167,9 +1167,13 @@ public class AiController { if (ComputerUtil.getDamageFromETB(player, land) < player.getLife() || !player.canLoseLife() || player.cantLoseForZeroOrLessLife() ) { if (!game.getPhaseHandler().is(PhaseType.MAIN1) || !isSafeToHoldLandDropForMain2(land)) { - game.PLAY_LAND_SURROGATE.setHostCard(land); + + // TODO fix logic for mayPlay land + LandAbility la = new LandAbility(land); final List abilities = Lists.newArrayList(); - abilities.add(game.PLAY_LAND_SURROGATE); + if (la.canPlay()) { + abilities.add(la); + } return abilities; } } diff --git a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java index 6772b3b7905..8fa66175905 100644 --- a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java +++ b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java @@ -448,8 +448,10 @@ public class PlayerControllerAi extends PlayerController { @Override public void playChosenSpellAbility(SpellAbility sa) { // System.out.println("Playing sa: " + sa); - if (sa == sa.getHostCard().getGame().PLAY_LAND_SURROGATE) { - player.playLand(sa.getHostCard(), false); + if (sa instanceof LandAbility) { + if (sa.canPlay()) { + sa.resolve(); + } } else { ComputerUtil.handlePlayingSpellAbility(player, sa, game); } diff --git a/forge-ai/src/main/java/forge/ai/simulation/SpellAbilityPicker.java b/forge-ai/src/main/java/forge/ai/simulation/SpellAbilityPicker.java index c18e5ecca1b..d9f9db03937 100644 --- a/forge-ai/src/main/java/forge/ai/simulation/SpellAbilityPicker.java +++ b/forge-ai/src/main/java/forge/ai/simulation/SpellAbilityPicker.java @@ -11,11 +11,10 @@ import forge.game.Game; import forge.game.ability.ApiType; import forge.game.ability.effects.CharmEffect; import forge.game.card.*; -import forge.game.cost.Cost; import forge.game.phase.PhaseType; import forge.game.player.Player; -import forge.game.spellability.Ability; import forge.game.spellability.AbilitySub; +import forge.game.spellability.LandAbility; import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbilityCondition; import forge.game.zone.ZoneType; @@ -115,18 +114,10 @@ public class SpellAbilityPicker { printPhaseInfo(); SpellAbility sa = getPlannedSpellAbility(origGameScore, candidateSAs); if (sa != null) { - return transformSA(sa); + return sa; } createNewPlan(origGameScore, candidateSAs); - return transformSA(getPlannedSpellAbility(origGameScore, candidateSAs)); - } - - private SpellAbility transformSA(SpellAbility sa) { - if (sa instanceof PlayLandAbility) { - game.PLAY_LAND_SURROGATE.setHostCard(sa.getHostCard()); - return game.PLAY_LAND_SURROGATE; - } - return sa; + return getPlannedSpellAbility(origGameScore, candidateSAs); } private Plan formulatePlanWithPhase(Score origGameScore, List candidateSAs, PhaseType phase) { @@ -456,20 +447,11 @@ public class SpellAbilityPicker { return ComputerUtil.chooseSacrificeType(player, type, ability, ability.getTargetCard(), amount); } - public static class PlayLandAbility extends Ability { + public static class PlayLandAbility extends LandAbility { public PlayLandAbility(Card land) { - super(null, (Cost) null); - setHostCard(land); - } - - @Override - public boolean canPlay() { - return true; //if this ability is added anywhere, it can be assumed that land can be played - } - @Override - public void resolve() { - throw new RuntimeException("This ability is intended to indicate \"land to play\" choice only"); + super(land); } + @Override public String toUnsuppressedString() { return "Play land " + (getHostCard() != null ? getHostCard().getName() : ""); } } diff --git a/forge-game/src/main/java/forge/game/Game.java b/forge-game/src/main/java/forge/game/Game.java index f1e3fcf12d0..a70a754823f 100644 --- a/forge-game/src/main/java/forge/game/Game.java +++ b/forge-game/src/main/java/forge/game/Game.java @@ -29,7 +29,6 @@ import forge.card.CardStateName; import forge.card.CardType.Supertype; import forge.game.card.*; import forge.game.combat.Combat; -import forge.game.cost.Cost; import forge.game.event.Event; import forge.game.event.GameEventGameOutcome; import forge.game.phase.Phase; @@ -38,7 +37,6 @@ import forge.game.phase.PhaseType; import forge.game.phase.Untap; import forge.game.player.*; import forge.game.replacement.ReplacementHandler; -import forge.game.spellability.Ability; import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbilityStackInstance; import forge.game.spellability.SpellAbilityView; @@ -170,19 +168,6 @@ public class Game { } } - public final Ability PLAY_LAND_SURROGATE = new Ability(null, (Cost) null) { - @Override - public boolean canPlay() { - return true; //if this ability is added anywhere, it can be assumed that land can be played - } - @Override - public void resolve() { - throw new RuntimeException("This ability is intended to indicate \"land to play\" choice only"); - } - @Override - public String toUnsuppressedString() { return "Play land"; } - }; - private final GameEntityCache playerCache = new GameEntityCache<>(); public Player getPlayer(PlayerView playerView) { return playerCache.get(playerView); @@ -247,8 +232,6 @@ public class Game { rules = rules0; match = match0; - spabCache.put(PLAY_LAND_SURROGATE.getId(), PLAY_LAND_SURROGATE); - int highestTeam = -1; for (RegisteredPlayer psc : players0) { // Track highest team number for auto assigning unassigned teams diff --git a/forge-game/src/main/java/forge/game/card/Card.java b/forge-game/src/main/java/forge/game/card/Card.java index 219ed4bbf20..785cc0f4116 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -5377,9 +5377,19 @@ public class Card extends GameEntity implements Comparable { } abilities.removeAll(toRemove); - if (getState(CardStateName.Original).getType().isLand() && player.canPlayLand(this)) { - game.PLAY_LAND_SURROGATE.setHostCard(this); - abilities.add(game.PLAY_LAND_SURROGATE); + if (getState(CardStateName.Original).getType().isLand()) { + LandAbility la = new LandAbility(this, player, null); + if (la.canPlay()) { + abilities.add(la); + } + + // extra for MayPlay + for (CardPlayOption o : this.mayPlay(player)) { + la = new LandAbility(this, player, o.getAbility()); + if (la.canPlay()) { + abilities.add(la); + } + } } return abilities; diff --git a/forge-game/src/main/java/forge/game/spellability/LandAbility.java b/forge-game/src/main/java/forge/game/spellability/LandAbility.java new file mode 100644 index 00000000000..ca005bf547e --- /dev/null +++ b/forge-game/src/main/java/forge/game/spellability/LandAbility.java @@ -0,0 +1,143 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2011 Forge Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package forge.game.spellability; + +import java.util.Map; + +import com.google.common.collect.Maps; + +import forge.game.Game; +import forge.game.card.Card; +import forge.game.cost.Cost; +import forge.game.event.GameEventLandPlayed; +import forge.game.player.Player; +import forge.game.staticability.StaticAbility; +import forge.game.trigger.TriggerType; +import forge.game.zone.Zone; +import forge.game.zone.ZoneType; + +public class LandAbility extends Ability { + + public LandAbility(Card sourceCard, Player p, StaticAbility mayPlay) { + super(sourceCard, (Cost)null); + setActivatingPlayer(p); + setMayPlay(mayPlay); + } + public LandAbility(Card sourceCard) { + this(sourceCard, sourceCard.getController(), (StaticAbility)null); + } + @Override + public boolean canPlay() { + final Card land = this.getHostCard(); + final Player p = this.getActivatingPlayer(); + final Game game = p.getGame(); + if (!p.canCastSorcery()) { + return false; + } + + // CantBeCast static abilities + for (final Card ca : game.getCardsIn(ZoneType.listValueOf("Battlefield,Command"))) { + final Iterable staticAbilities = ca.getStaticAbilities(); + for (final StaticAbility stAb : staticAbilities) { + if (stAb.applyAbility("CantPlayLand", land, this)) { + return false; + } + } + } + + if (land != null) { + final boolean mayPlay = getMayPlay() != null; + if (land.getOwner() != p && !mayPlay) { + return false; + } + + final Zone zone = game.getZoneOf(land); + if (zone != null && (zone.is(ZoneType.Battlefield) || (!zone.is(ZoneType.Hand) && !mayPlay))) { + return false; + } + } + + // **** Check for land play limit per turn **** + // Dev Mode + if (p.getController().canPlayUnlimitedLands() || p.hasKeyword("You may play any number of additional lands on each of your turns.")) { + return true; + } + + // check for adjusted max lands play per turn + int adjMax = 1; + for (String keyword : p.getKeywords()) { + if (keyword.startsWith("AdjustLandPlays")) { + final String[] k = keyword.split(":"); + adjMax += Integer.valueOf(k[1]); + } + } + if (p.getLandsPlayedThisTurn() < adjMax) { + return true; + } + return false; + } + @Override + public void resolve() { + final Card land = this.getHostCard(); + final Player p = this.getActivatingPlayer(); + final Game game = p.getGame(); + + land.setController(p, 0); + if (land.isFaceDown()) { + land.turnFaceUp(); + } + game.getAction().moveTo(p.getZone(ZoneType.Battlefield), land, null, Maps.newHashMap()); + + // play a sound + game.fireEvent(new GameEventLandPlayed(p, land)); + + // Run triggers + final Map runParams = Maps.newHashMap(); + runParams.put("Card", land); + game.getTriggerHandler().runTrigger(TriggerType.LandPlayed, runParams, false); + game.getStack().unfreezeStack(); + p.addLandPlayedThisTurn(); + + // increase mayplay used + if (getMayPlay() != null) { + getMayPlay().incMayPlayTurn(); + } + } + @Override + public String toUnsuppressedString() { + StringBuilder sb = new StringBuilder("Play land"); + StaticAbility sta = getMayPlay(); + if (sta != null) { + Card source = sta.getHostCard(); + if (!source.equals(getHostCard())) { + sb.append(" by "); + if ((source.isEmblem() || source.getType().hasSubtype("Effect")) + && source.getEffectSource() != null) { + sb.append(source.getEffectSource()); + } else { + sb.append(source); + } + if (sta.hasParam("MayPlayText")) { + sb.append(" (").append(sta.getParam("MayPlayText")).append(")"); + } + } + } + return sb.toString(); + } + +} \ No newline at end of file diff --git a/forge-gui-desktop/src/test/java/forge/ai/simulation/SpellAbilityPickerTest.java b/forge-gui-desktop/src/test/java/forge/ai/simulation/SpellAbilityPickerTest.java index 3683b33cc7d..d7cfa66d7c7 100644 --- a/forge-gui-desktop/src/test/java/forge/ai/simulation/SpellAbilityPickerTest.java +++ b/forge-gui-desktop/src/test/java/forge/ai/simulation/SpellAbilityPickerTest.java @@ -70,7 +70,7 @@ public class SpellAbilityPickerTest extends SimulationTestCase { SpellAbilityPicker picker = new SpellAbilityPicker(game, p); SpellAbility sa = picker.chooseSpellAbilityToPlay(null); - assertEquals(game.PLAY_LAND_SURROGATE, sa); + //assertEquals(game.PLAY_LAND_SURROGATE, sa); assertEquals(mountain, sa.getHostCard()); Plan plan = picker.getPlan(); diff --git a/forge-gui/src/main/java/forge/match/input/InputPassPriority.java b/forge-gui/src/main/java/forge/match/input/InputPassPriority.java index 9078f3b671f..d793efba100 100644 --- a/forge-gui/src/main/java/forge/match/input/InputPassPriority.java +++ b/forge-gui/src/main/java/forge/match/input/InputPassPriority.java @@ -23,6 +23,7 @@ import java.util.List; import forge.game.Game; import forge.game.card.Card; import forge.game.player.Player; +import forge.game.spellability.LandAbility; import forge.game.spellability.SpellAbility; import forge.model.FModel; import forge.player.GamePlayerUtil; @@ -162,7 +163,7 @@ public class InputPassPriority extends InputSyncronizedBase { if (sa.isSpell()) { return "cast spell"; } - if (sa == card.getGame().PLAY_LAND_SURROGATE) { + if (sa instanceof LandAbility) { return "play land"; } return "activate ability"; diff --git a/forge-gui/src/main/java/forge/player/HumanPlay.java b/forge-gui/src/main/java/forge/player/HumanPlay.java index d0627961b38..9c103127970 100644 --- a/forge-gui/src/main/java/forge/player/HumanPlay.java +++ b/forge-gui/src/main/java/forge/player/HumanPlay.java @@ -6,6 +6,7 @@ import java.util.List; import java.util.Map; import forge.game.cost.*; +import forge.game.spellability.LandAbility; import forge.game.spellability.OptionalCostValue; import forge.game.spellability.Spell; import forge.util.TextUtil; @@ -66,8 +67,11 @@ public class HumanPlay { public final static boolean playSpellAbility(final PlayerControllerHuman controller, final Player p, SpellAbility sa) { FThreads.assertExecutedByEdt(false); - if (sa == controller.getGame().PLAY_LAND_SURROGATE) { - p.playLand(sa.getHostCard(), false); + if (sa instanceof LandAbility) { + sa.setActivatingPlayer(p); + if (sa.canPlay()) { + sa.resolve(); + } return false; }