mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-17 19:28:01 +00:00
Merge branch 'mayPlayLand' into 'master'
May play land See merge request core-developers/forge!415
This commit is contained in:
@@ -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<Player, PlayerView> 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
|
||||
|
||||
@@ -20,6 +20,9 @@ package forge.game;
|
||||
import com.google.common.base.Predicates;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import forge.card.CardStateName;
|
||||
import forge.card.MagicColor;
|
||||
import forge.card.mana.ManaCostParser;
|
||||
import forge.game.ability.AbilityFactory;
|
||||
@@ -119,9 +122,32 @@ public final class GameActionUtil {
|
||||
public static final List<SpellAbility> getAlternativeCosts(final SpellAbility sa, final Player activator) {
|
||||
final List<SpellAbility> alternatives = Lists.newArrayList();
|
||||
|
||||
final Card source = sa.getHostCard();
|
||||
Card source = sa.getHostCard();
|
||||
final Game game = source.getGame();
|
||||
|
||||
if (sa.isSpell()) {
|
||||
boolean lkicheck = false;
|
||||
if (sa.hasParam("Bestow") && !source.isBestowed() && !source.isInZone(ZoneType.Battlefield)) {
|
||||
if (!source.isLKI()) {
|
||||
source = CardUtil.getLKICopy(source);
|
||||
}
|
||||
|
||||
source.animateBestow(false);
|
||||
lkicheck = true;
|
||||
} else if (((Spell) sa).isCastFaceDown()) {
|
||||
// need a copy of the card to turn facedown without trigger anything
|
||||
if (!source.isLKI()) {
|
||||
source = CardUtil.getLKICopy(source);
|
||||
}
|
||||
source.setState(CardStateName.FaceDown, false);
|
||||
lkicheck = true;
|
||||
}
|
||||
|
||||
if (lkicheck) {
|
||||
CardCollection preList = new CardCollection(source);
|
||||
game.getAction().checkStaticAbilities(false, Sets.newHashSet(source), preList);
|
||||
}
|
||||
|
||||
for (CardPlayOption o : source.mayPlay(activator)) {
|
||||
// non basic are only allowed if PayManaCost is yes
|
||||
if (!sa.isBasicSpell() && o.getPayManaCost() == PayManaCost.NO) {
|
||||
@@ -186,6 +212,11 @@ public final class GameActionUtil {
|
||||
newSA.setDescription(sb.toString());
|
||||
alternatives.add(newSA);
|
||||
}
|
||||
|
||||
// reset static abilities
|
||||
if (lkicheck) {
|
||||
game.getAction().checkStaticAbilities(false, Sets.newHashSet(), CardCollection.EMPTY);
|
||||
}
|
||||
}
|
||||
|
||||
if (!sa.isBasicSpell()) {
|
||||
|
||||
@@ -5377,9 +5377,19 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
}
|
||||
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;
|
||||
|
||||
@@ -1644,21 +1644,7 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
public final boolean playLand(final Card land, final boolean ignoreZoneAndTiming) {
|
||||
// Dakkon Blackblade Avatar will use a similar effect
|
||||
if (canPlayLand(land, ignoreZoneAndTiming)) {
|
||||
land.setController(this, 0);
|
||||
if (land.isFaceDown()) {
|
||||
land.turnFaceUp();
|
||||
}
|
||||
game.getAction().moveTo(getZone(ZoneType.Battlefield), land, null, new HashMap<String, Object>());
|
||||
|
||||
// play a sound
|
||||
game.fireEvent(new GameEventLandPlayed(this, land));
|
||||
|
||||
// Run triggers
|
||||
final Map<String, Object> runParams = Maps.newHashMap();
|
||||
runParams.put("Card", land);
|
||||
game.getTriggerHandler().runTrigger(TriggerType.LandPlayed, runParams, false);
|
||||
game.getStack().unfreezeStack();
|
||||
addLandPlayedThisTurn();
|
||||
this.playLandNoCheck(land);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1666,6 +1652,24 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
return false;
|
||||
}
|
||||
|
||||
public final void playLandNoCheck(final Card land) {
|
||||
land.setController(this, 0);
|
||||
if (land.isFaceDown()) {
|
||||
land.turnFaceUp();
|
||||
}
|
||||
game.getAction().moveTo(getZone(ZoneType.Battlefield), land, null, new HashMap<String, Object>());
|
||||
|
||||
// play a sound
|
||||
game.fireEvent(new GameEventLandPlayed(this, land));
|
||||
|
||||
// Run triggers
|
||||
final Map<String, Object> runParams = Maps.newHashMap();
|
||||
runParams.put("Card", land);
|
||||
game.getTriggerHandler().runTrigger(TriggerType.LandPlayed, runParams, false);
|
||||
game.getStack().unfreezeStack();
|
||||
addLandPlayedThisTurn();
|
||||
}
|
||||
|
||||
public final boolean canPlayLand(final Card land) {
|
||||
return canPlayLand(land, false);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* 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 forge.game.Game;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.cost.Cost;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.staticability.StaticAbility;
|
||||
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() {
|
||||
getActivatingPlayer().playLandNoCheck(getHostCard());
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user