LandAbility: do extra ability class, so it can use mayPlay

This commit is contained in:
Hanmac
2018-04-16 20:33:06 +02:00
parent 190db26ebd
commit 79c9c914e2
9 changed files with 181 additions and 52 deletions

View File

@@ -1167,9 +1167,13 @@ public class AiController {
if (ComputerUtil.getDamageFromETB(player, land) < player.getLife() || !player.canLoseLife() if (ComputerUtil.getDamageFromETB(player, land) < player.getLife() || !player.canLoseLife()
|| player.cantLoseForZeroOrLessLife() ) { || player.cantLoseForZeroOrLessLife() ) {
if (!game.getPhaseHandler().is(PhaseType.MAIN1) || !isSafeToHoldLandDropForMain2(land)) { 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<SpellAbility> abilities = Lists.newArrayList(); final List<SpellAbility> abilities = Lists.newArrayList();
abilities.add(game.PLAY_LAND_SURROGATE); if (la.canPlay()) {
abilities.add(la);
}
return abilities; return abilities;
} }
} }

View File

@@ -448,8 +448,10 @@ public class PlayerControllerAi extends PlayerController {
@Override @Override
public void playChosenSpellAbility(SpellAbility sa) { public void playChosenSpellAbility(SpellAbility sa) {
// System.out.println("Playing sa: " + sa); // System.out.println("Playing sa: " + sa);
if (sa == sa.getHostCard().getGame().PLAY_LAND_SURROGATE) { if (sa instanceof LandAbility) {
player.playLand(sa.getHostCard(), false); if (sa.canPlay()) {
sa.resolve();
}
} else { } else {
ComputerUtil.handlePlayingSpellAbility(player, sa, game); ComputerUtil.handlePlayingSpellAbility(player, sa, game);
} }

View File

@@ -11,11 +11,10 @@ import forge.game.Game;
import forge.game.ability.ApiType; import forge.game.ability.ApiType;
import forge.game.ability.effects.CharmEffect; import forge.game.ability.effects.CharmEffect;
import forge.game.card.*; import forge.game.card.*;
import forge.game.cost.Cost;
import forge.game.phase.PhaseType; import forge.game.phase.PhaseType;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.spellability.Ability;
import forge.game.spellability.AbilitySub; import forge.game.spellability.AbilitySub;
import forge.game.spellability.LandAbility;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
import forge.game.spellability.SpellAbilityCondition; import forge.game.spellability.SpellAbilityCondition;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
@@ -115,18 +114,10 @@ public class SpellAbilityPicker {
printPhaseInfo(); printPhaseInfo();
SpellAbility sa = getPlannedSpellAbility(origGameScore, candidateSAs); SpellAbility sa = getPlannedSpellAbility(origGameScore, candidateSAs);
if (sa != null) { if (sa != null) {
return transformSA(sa); return sa;
} }
createNewPlan(origGameScore, candidateSAs); createNewPlan(origGameScore, candidateSAs);
return transformSA(getPlannedSpellAbility(origGameScore, candidateSAs)); return 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;
} }
private Plan formulatePlanWithPhase(Score origGameScore, List<SpellAbility> candidateSAs, PhaseType phase) { private Plan formulatePlanWithPhase(Score origGameScore, List<SpellAbility> candidateSAs, PhaseType phase) {
@@ -456,20 +447,11 @@ public class SpellAbilityPicker {
return ComputerUtil.chooseSacrificeType(player, type, ability, ability.getTargetCard(), amount); 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) { public PlayLandAbility(Card land) {
super(null, (Cost) null); super(land);
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");
}
@Override @Override
public String toUnsuppressedString() { return "Play land " + (getHostCard() != null ? getHostCard().getName() : ""); } public String toUnsuppressedString() { return "Play land " + (getHostCard() != null ? getHostCard().getName() : ""); }
} }

View File

@@ -29,7 +29,6 @@ import forge.card.CardStateName;
import forge.card.CardType.Supertype; import forge.card.CardType.Supertype;
import forge.game.card.*; import forge.game.card.*;
import forge.game.combat.Combat; import forge.game.combat.Combat;
import forge.game.cost.Cost;
import forge.game.event.Event; import forge.game.event.Event;
import forge.game.event.GameEventGameOutcome; import forge.game.event.GameEventGameOutcome;
import forge.game.phase.Phase; import forge.game.phase.Phase;
@@ -38,7 +37,6 @@ import forge.game.phase.PhaseType;
import forge.game.phase.Untap; import forge.game.phase.Untap;
import forge.game.player.*; import forge.game.player.*;
import forge.game.replacement.ReplacementHandler; import forge.game.replacement.ReplacementHandler;
import forge.game.spellability.Ability;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
import forge.game.spellability.SpellAbilityStackInstance; import forge.game.spellability.SpellAbilityStackInstance;
import forge.game.spellability.SpellAbilityView; 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<Player, PlayerView> playerCache = new GameEntityCache<>(); private final GameEntityCache<Player, PlayerView> playerCache = new GameEntityCache<>();
public Player getPlayer(PlayerView playerView) { public Player getPlayer(PlayerView playerView) {
return playerCache.get(playerView); return playerCache.get(playerView);
@@ -247,8 +232,6 @@ public class Game {
rules = rules0; rules = rules0;
match = match0; match = match0;
spabCache.put(PLAY_LAND_SURROGATE.getId(), PLAY_LAND_SURROGATE);
int highestTeam = -1; int highestTeam = -1;
for (RegisteredPlayer psc : players0) { for (RegisteredPlayer psc : players0) {
// Track highest team number for auto assigning unassigned teams // Track highest team number for auto assigning unassigned teams

View File

@@ -5377,9 +5377,19 @@ public class Card extends GameEntity implements Comparable<Card> {
} }
abilities.removeAll(toRemove); abilities.removeAll(toRemove);
if (getState(CardStateName.Original).getType().isLand() && player.canPlayLand(this)) { if (getState(CardStateName.Original).getType().isLand()) {
game.PLAY_LAND_SURROGATE.setHostCard(this); LandAbility la = new LandAbility(this, player, null);
abilities.add(game.PLAY_LAND_SURROGATE); 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; return abilities;

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
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<StaticAbility> 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<String, Object> 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();
}
}

View File

@@ -70,7 +70,7 @@ public class SpellAbilityPickerTest extends SimulationTestCase {
SpellAbilityPicker picker = new SpellAbilityPicker(game, p); SpellAbilityPicker picker = new SpellAbilityPicker(game, p);
SpellAbility sa = picker.chooseSpellAbilityToPlay(null); SpellAbility sa = picker.chooseSpellAbilityToPlay(null);
assertEquals(game.PLAY_LAND_SURROGATE, sa); //assertEquals(game.PLAY_LAND_SURROGATE, sa);
assertEquals(mountain, sa.getHostCard()); assertEquals(mountain, sa.getHostCard());
Plan plan = picker.getPlan(); Plan plan = picker.getPlan();

View File

@@ -23,6 +23,7 @@ import java.util.List;
import forge.game.Game; import forge.game.Game;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.spellability.LandAbility;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
import forge.model.FModel; import forge.model.FModel;
import forge.player.GamePlayerUtil; import forge.player.GamePlayerUtil;
@@ -162,7 +163,7 @@ public class InputPassPriority extends InputSyncronizedBase {
if (sa.isSpell()) { if (sa.isSpell()) {
return "cast spell"; return "cast spell";
} }
if (sa == card.getGame().PLAY_LAND_SURROGATE) { if (sa instanceof LandAbility) {
return "play land"; return "play land";
} }
return "activate ability"; return "activate ability";

View File

@@ -6,6 +6,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import forge.game.cost.*; import forge.game.cost.*;
import forge.game.spellability.LandAbility;
import forge.game.spellability.OptionalCostValue; import forge.game.spellability.OptionalCostValue;
import forge.game.spellability.Spell; import forge.game.spellability.Spell;
import forge.util.TextUtil; import forge.util.TextUtil;
@@ -66,8 +67,11 @@ public class HumanPlay {
public final static boolean playSpellAbility(final PlayerControllerHuman controller, final Player p, SpellAbility sa) { public final static boolean playSpellAbility(final PlayerControllerHuman controller, final Player p, SpellAbility sa) {
FThreads.assertExecutedByEdt(false); FThreads.assertExecutedByEdt(false);
if (sa == controller.getGame().PLAY_LAND_SURROGATE) { if (sa instanceof LandAbility) {
p.playLand(sa.getHostCard(), false); sa.setActivatingPlayer(p);
if (sa.canPlay()) {
sa.resolve();
}
return false; return false;
} }