mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-17 19:28:01 +00:00
refactor LandAbility to be created by CardFactory (#5047)
--------- Co-authored-by: tool4EvEr <tool4EvEr@192.168.0.60>
This commit is contained in:
@@ -468,8 +468,7 @@ public class AiController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return Iterables.any(c.getAllPossibleAbilities(player, true), SpellAbility::isLandAbility);
|
||||||
return player.canPlayLand(c);
|
|
||||||
});
|
});
|
||||||
return landList;
|
return landList;
|
||||||
}
|
}
|
||||||
@@ -1376,30 +1375,12 @@ public class AiController {
|
|||||||
Card land = chooseBestLandToPlay(landsWannaPlay);
|
Card land = chooseBestLandToPlay(landsWannaPlay);
|
||||||
if ((!player.canLoseLife() || player.cantLoseForZeroOrLessLife() || ComputerUtil.getDamageFromETB(player, land) < player.getLife())
|
if ((!player.canLoseLife() || player.cantLoseForZeroOrLessLife() || ComputerUtil.getDamageFromETB(player, land) < player.getLife())
|
||||||
&& (!game.getPhaseHandler().is(PhaseType.MAIN1) || !isSafeToHoldLandDropForMain2(land))) {
|
&& (!game.getPhaseHandler().is(PhaseType.MAIN1) || !isSafeToHoldLandDropForMain2(land))) {
|
||||||
final List<SpellAbility> abilities = Lists.newArrayList();
|
final List<SpellAbility> abilities = land.getAllPossibleAbilities(player, true);
|
||||||
|
// skip non Land Abilities
|
||||||
|
abilities.removeIf(sa -> !sa.isLandAbility());
|
||||||
|
|
||||||
// TODO extend this logic to evaluate MDFC with both sides land
|
|
||||||
// this can only happen if its a MDFC land
|
|
||||||
if (!land.isLand()) {
|
|
||||||
land.setState(CardStateName.Modal, true);
|
|
||||||
land.setBackSide(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
LandAbility la = new LandAbility(land, player, null);
|
|
||||||
la.setCardState(land.getCurrentState());
|
|
||||||
if (la.canPlay()) {
|
|
||||||
abilities.add(la);
|
|
||||||
}
|
|
||||||
|
|
||||||
// add mayPlay option
|
|
||||||
for (CardPlayOption o : land.mayPlay(player)) {
|
|
||||||
la = new LandAbility(land, player, o);
|
|
||||||
la.setCardState(land.getCurrentState());
|
|
||||||
if (la.canPlay()) {
|
|
||||||
abilities.add(la);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!abilities.isEmpty()) {
|
if (!abilities.isEmpty()) {
|
||||||
|
// TODO extend this logic to evaluate MDFC with both sides land
|
||||||
return abilities;
|
return abilities;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1570,7 +1551,7 @@ public class AiController {
|
|||||||
|
|
||||||
Iterables.removeIf(saList, spellAbility -> { //don't include removedAI cards if somehow the AI can play the ability or gain control of unsupported card
|
Iterables.removeIf(saList, spellAbility -> { //don't include removedAI cards if somehow the AI can play the ability or gain control of unsupported card
|
||||||
// TODO allow when experimental profile?
|
// TODO allow when experimental profile?
|
||||||
return spellAbility instanceof LandAbility || (spellAbility.getHostCard() != null && ComputerUtilCard.isCardRemAIDeck(spellAbility.getHostCard()));
|
return spellAbility.isLandAbility() || (spellAbility.getHostCard() != null && ComputerUtilCard.isCardRemAIDeck(spellAbility.getHostCard()));
|
||||||
});
|
});
|
||||||
//update LivingEndPlayer
|
//update LivingEndPlayer
|
||||||
useLivingEnd = Iterables.any(player.getZone(ZoneType.Library), CardPredicates.nameEquals("Living End"));
|
useLivingEnd = Iterables.any(player.getZone(ZoneType.Library), CardPredicates.nameEquals("Living End"));
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ public class ComputerUtilAbility {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return player.canPlayLand(c);
|
return player.canPlayLand(c, false, c.getFirstSpellAbility());
|
||||||
});
|
});
|
||||||
|
|
||||||
final CardCollection landsNotInHand = new CardCollection(player.getCardsIn(ZoneType.Graveyard));
|
final CardCollection landsNotInHand = new CardCollection(player.getCardsIn(ZoneType.Graveyard));
|
||||||
|
|||||||
@@ -810,7 +810,7 @@ public class PlayerControllerAi extends PlayerController {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean playChosenSpellAbility(SpellAbility sa) {
|
public boolean playChosenSpellAbility(SpellAbility sa) {
|
||||||
if (sa instanceof LandAbility) {
|
if (sa.isLandAbility()) {
|
||||||
if (sa.canPlay()) {
|
if (sa.canPlay()) {
|
||||||
sa.resolve();
|
sa.resolve();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import forge.game.ability.AbilityUtils;
|
|||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.player.PlayerActionConfirmMode;
|
import forge.game.player.PlayerActionConfirmMode;
|
||||||
import forge.game.spellability.LandAbility;
|
|
||||||
import forge.game.spellability.Spell;
|
import forge.game.spellability.Spell;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@ public class DiscoverAi extends SpellAbilityAi {
|
|||||||
public boolean confirmAction(Player ai, SpellAbility sa, PlayerActionConfirmMode mode, String message, Map<String, Object> params) {
|
public boolean confirmAction(Player ai, SpellAbility sa, PlayerActionConfirmMode mode, String message, Map<String, Object> params) {
|
||||||
Card c = (Card)params.get("Card");
|
Card c = (Card)params.get("Card");
|
||||||
for (SpellAbility s : AbilityUtils.getBasicSpellsFromPlayEffect(c, ai)) {
|
for (SpellAbility s : AbilityUtils.getBasicSpellsFromPlayEffect(c, ai)) {
|
||||||
if (s instanceof LandAbility) {
|
if (s.isLandAbility()) {
|
||||||
// return false or we get a ClassCastException later if the AI encounters MDFC with land backside
|
// return false or we get a ClassCastException later if the AI encounters MDFC with land backside
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -154,7 +154,7 @@ public class PlayAi extends SpellAbilityAi {
|
|||||||
if (!sa.matchesValidParam("ValidSA", s)) {
|
if (!sa.matchesValidParam("ValidSA", s)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (s instanceof LandAbility) {
|
if (s.isLandAbility()) {
|
||||||
// might want to run some checks here but it's rare anyway
|
// might want to run some checks here but it's rare anyway
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package forge.ai.simulation;
|
package forge.ai.simulation;
|
||||||
|
|
||||||
import forge.game.spellability.LandAbility;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@@ -158,9 +158,9 @@ public class GameSimulator {
|
|||||||
}
|
}
|
||||||
public Score simulateSpellAbility(SpellAbility origSa, GameStateEvaluator eval, boolean resolve) {
|
public Score simulateSpellAbility(SpellAbility origSa, GameStateEvaluator eval, boolean resolve) {
|
||||||
SpellAbility sa;
|
SpellAbility sa;
|
||||||
if (origSa instanceof LandAbility) {
|
if (origSa.isLandAbility()) {
|
||||||
Card hostCard = (Card) copier.find(origSa.getHostCard());
|
Card hostCard = (Card) copier.find(origSa.getHostCard());
|
||||||
if (!aiPlayer.playLand(hostCard, false)) {
|
if (!aiPlayer.playLand(hostCard, false, origSa)) {
|
||||||
System.err.println("Simulation: Couldn't play land! " + origSa);
|
System.err.println("Simulation: Couldn't play land! " + origSa);
|
||||||
}
|
}
|
||||||
sa = origSa;
|
sa = origSa;
|
||||||
|
|||||||
@@ -260,7 +260,10 @@ public class GameStateEvaluator {
|
|||||||
// The value should be more than the value of having a card in hand, so if a land has an
|
// The value should be more than the value of having a card in hand, so if a land has an
|
||||||
// activated ability but not a mana ability, it will still be played.
|
// activated ability but not a mana ability, it will still be played.
|
||||||
for (SpellAbility m: c.getNonManaAbilities()) {
|
for (SpellAbility m: c.getNonManaAbilities()) {
|
||||||
if (!m.getPayCosts().hasTapCost()) {
|
if (m.isLandAbility()) {
|
||||||
|
// Land Ability has no extra Score
|
||||||
|
continue;
|
||||||
|
} if (!m.getPayCosts().hasTapCost()) {
|
||||||
// probably a manland, rate it higher than a rainbow land
|
// probably a manland, rate it higher than a rainbow land
|
||||||
value += 25;
|
value += 25;
|
||||||
} else if (m.getPayCosts().hasSpecificCostType(CostSacrifice.class)) {
|
} else if (m.getPayCosts().hasSpecificCostType(CostSacrifice.class)) {
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import forge.game.card.CardPredicates;
|
|||||||
import forge.game.phase.PhaseType;
|
import forge.game.phase.PhaseType;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
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;
|
||||||
@@ -136,7 +136,7 @@ public class SpellAbilityPicker {
|
|||||||
|
|
||||||
private static boolean isSorcerySpeed(SpellAbility sa, Player player) {
|
private static boolean isSorcerySpeed(SpellAbility sa, Player player) {
|
||||||
// TODO: Can we use the actual rules engine for this instead of trying to do the logic ourselves?
|
// TODO: Can we use the actual rules engine for this instead of trying to do the logic ourselves?
|
||||||
if (sa instanceof LandAbility) {
|
if (sa.isLandAbility()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (sa.isSpell()) {
|
if (sa.isSpell()) {
|
||||||
@@ -327,16 +327,16 @@ public class SpellAbilityPicker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private AiPlayDecision canPlayAndPayForSim(final SpellAbility sa) {
|
private AiPlayDecision canPlayAndPayForSim(final SpellAbility sa) {
|
||||||
if (!sa.isLegalAfterStack()) {
|
|
||||||
return AiPlayDecision.CantPlaySa;
|
|
||||||
}
|
|
||||||
if (!sa.checkRestrictions(sa.getHostCard(), player)) {
|
if (!sa.checkRestrictions(sa.getHostCard(), player)) {
|
||||||
return AiPlayDecision.CantPlaySa;
|
return AiPlayDecision.CantPlaySa;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sa instanceof LandAbility) {
|
if (sa.isLandAbility()) {
|
||||||
return AiPlayDecision.WillPlay;
|
return AiPlayDecision.WillPlay;
|
||||||
}
|
}
|
||||||
|
if (!sa.isLegalAfterStack()) {
|
||||||
|
return AiPlayDecision.CantPlaySa;
|
||||||
|
}
|
||||||
if (!sa.canPlay()) {
|
if (!sa.canPlay()) {
|
||||||
return AiPlayDecision.CantPlaySa;
|
return AiPlayDecision.CantPlaySa;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -170,7 +170,7 @@ public abstract class CardTraitBase extends GameObject implements IHasCardView,
|
|||||||
*
|
*
|
||||||
* @return a boolean.
|
* @return a boolean.
|
||||||
*/
|
*/
|
||||||
public final boolean isSecondary() {
|
public boolean isSecondary() {
|
||||||
return getParamOrDefault("Secondary", "False").equals("True");
|
return getParamOrDefault("Secondary", "False").equals("True");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -91,10 +91,10 @@ public final class GameActionUtil {
|
|||||||
return alternatives;
|
return alternatives;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sa.isSpell()) {
|
if (sa.isSpell() || sa.isLandAbility()) {
|
||||||
boolean lkicheck = false;
|
boolean lkicheck = false;
|
||||||
|
|
||||||
Card newHost = ((Spell)sa).getAlternateHost(source);
|
Card newHost = sa.getAlternateHost(source);
|
||||||
if (newHost != null) {
|
if (newHost != null) {
|
||||||
source = newHost;
|
source = newHost;
|
||||||
lkicheck = true;
|
lkicheck = true;
|
||||||
|
|||||||
@@ -2953,7 +2953,7 @@ public class AbilityUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (SpellAbility s : list) {
|
for (SpellAbility s : list) {
|
||||||
if (s instanceof LandAbility) {
|
if (s.isLandAbility()) {
|
||||||
// CR 305.3
|
// CR 305.3
|
||||||
if (controller.getGame().getPhaseHandler().isPlayerTurn(controller) && controller.canPlayLand(tgtCard, true, s)) {
|
if (controller.getGame().getPhaseHandler().isPlayerTurn(controller) && controller.canPlayLand(tgtCard, true, s)) {
|
||||||
sas.add(s);
|
sas.add(s);
|
||||||
@@ -2981,9 +2981,7 @@ public class AbilityUtils {
|
|||||||
|
|
||||||
private static void collectSpellsForPlayEffect(final List<SpellAbility> result, final CardState state, final Player controller, final boolean withAltCost) {
|
private static void collectSpellsForPlayEffect(final List<SpellAbility> result, final CardState state, final Player controller, final boolean withAltCost) {
|
||||||
if (state.getType().isLand()) {
|
if (state.getType().isLand()) {
|
||||||
LandAbility la = new LandAbility(state.getCard(), controller, null);
|
result.add(state.getFirstSpellAbility());
|
||||||
la.setCardState(state);
|
|
||||||
result.add(la);
|
|
||||||
}
|
}
|
||||||
final Iterable<SpellAbility> spells = state.getSpellAbilities();
|
final Iterable<SpellAbility> spells = state.getSpellAbilities();
|
||||||
for (SpellAbility sa : spells) {
|
for (SpellAbility sa : spells) {
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
package forge.game.ability.effects;
|
package forge.game.ability.effects;
|
||||||
|
|
||||||
import com.google.common.base.Predicates;
|
|
||||||
import com.google.common.collect.Iterables;
|
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.ability.AbilityKey;
|
import forge.game.ability.AbilityKey;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
@@ -15,7 +13,7 @@ import forge.game.cost.CostPart;
|
|||||||
import forge.game.cost.CostReveal;
|
import forge.game.cost.CostReveal;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.player.PlayerCollection;
|
import forge.game.player.PlayerCollection;
|
||||||
import forge.game.spellability.LandAbility;
|
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.trigger.TriggerType;
|
import forge.game.trigger.TriggerType;
|
||||||
import forge.game.zone.PlayerZone;
|
import forge.game.zone.PlayerZone;
|
||||||
@@ -94,7 +92,7 @@ public class DiscoverEffect extends SpellAbilityEffect {
|
|||||||
List<SpellAbility> sas = AbilityUtils.getBasicSpellsFromPlayEffect(found, p);
|
List<SpellAbility> sas = AbilityUtils.getBasicSpellsFromPlayEffect(found, p);
|
||||||
|
|
||||||
// filter out land abilities due to MDFC or similar
|
// filter out land abilities due to MDFC or similar
|
||||||
Iterables.removeIf(sas, Predicates.instanceOf(LandAbility.class));
|
sas.removeIf(sp -> sp.isLandAbility());
|
||||||
// the spell must also have a mana value equal to or less than the discover number
|
// the spell must also have a mana value equal to or less than the discover number
|
||||||
sas.removeIf(sp -> sp.getPayCosts().getTotalMana().getCMC() > num);
|
sas.removeIf(sp -> sp.getPayCosts().getTotalMana().getCMC() > num);
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ import forge.game.replacement.ReplacementEffect;
|
|||||||
import forge.game.replacement.ReplacementHandler;
|
import forge.game.replacement.ReplacementHandler;
|
||||||
import forge.game.replacement.ReplacementLayer;
|
import forge.game.replacement.ReplacementLayer;
|
||||||
import forge.game.spellability.AlternativeCost;
|
import forge.game.spellability.AlternativeCost;
|
||||||
import forge.game.spellability.LandAbility;
|
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.spellability.SpellAbilityPredicates;
|
import forge.game.spellability.SpellAbilityPredicates;
|
||||||
import forge.game.zone.Zone;
|
import forge.game.zone.Zone;
|
||||||
@@ -343,7 +343,7 @@ public class PlayEffect extends SpellAbilityEffect {
|
|||||||
final Zone originZone = tgtCard.getZone();
|
final Zone originZone = tgtCard.getZone();
|
||||||
|
|
||||||
// lands will be played
|
// lands will be played
|
||||||
if (tgtSA instanceof LandAbility) {
|
if (tgtSA.isLandAbility()) {
|
||||||
tgtSA.resolve();
|
tgtSA.resolve();
|
||||||
amount--;
|
amount--;
|
||||||
if (remember) {
|
if (remember) {
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ public class PlayLandVariantEffect extends SpellAbilityEffect {
|
|||||||
PaperCard ran = Aggregates.random(cards);
|
PaperCard ran = Aggregates.random(cards);
|
||||||
random = CardFactory.getCard(ran, activator, game);
|
random = CardFactory.getCard(ran, activator, game);
|
||||||
cards.remove(ran);
|
cards.remove(ran);
|
||||||
} while (!activator.canPlayLand(random, false));
|
} while (!activator.canPlayLand(random, false, random.getFirstSpellAbility()));
|
||||||
|
|
||||||
source.addCloneState(CardFactory.getCloneStates(random, source, sa), game.getNextTimestamp());
|
source.addCloneState(CardFactory.getCloneStates(random, source, sa), game.getNextTimestamp());
|
||||||
source.updateStateForView();
|
source.updateStateForView();
|
||||||
|
|||||||
@@ -7430,7 +7430,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
for (SpellAbility sa : getState(CardStateName.Modal).getSpellAbilities()) {
|
for (SpellAbility sa : getState(CardStateName.Modal).getSpellAbilities()) {
|
||||||
//add alternative costs as additional spell abilities
|
//add alternative costs as additional spell abilities
|
||||||
// only add Spells there
|
// only add Spells there
|
||||||
if (sa.isSpell()) {
|
if (sa.isSpell() || sa.isLandAbility()) {
|
||||||
abilities.add(sa);
|
abilities.add(sa);
|
||||||
abilities.addAll(GameActionUtil.getAlternativeCosts(sa, player, false));
|
abilities.addAll(GameActionUtil.getAlternativeCosts(sa, player, false));
|
||||||
}
|
}
|
||||||
@@ -7466,106 +7466,6 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
}
|
}
|
||||||
abilities.removeAll(toRemove);
|
abilities.removeAll(toRemove);
|
||||||
|
|
||||||
// Land Abilities below, move them to CardFactory after MayPlayRefactor
|
|
||||||
if (getLastKnownZone().is(ZoneType.Battlefield)) {
|
|
||||||
return abilities;
|
|
||||||
}
|
|
||||||
if (getState(CardStateName.Original).getType().isLand()) {
|
|
||||||
LandAbility la = new LandAbility(this, player, null);
|
|
||||||
la.setCardState(oState);
|
|
||||||
if (la.canPlay()) {
|
|
||||||
abilities.add(la);
|
|
||||||
}
|
|
||||||
|
|
||||||
Card source = this;
|
|
||||||
boolean lkicheck = false;
|
|
||||||
|
|
||||||
// if Card is Facedown, need to check if MayPlay still applies
|
|
||||||
if (isFaceDown()) {
|
|
||||||
lkicheck = true;
|
|
||||||
source = CardCopyService.getLKICopy(source);
|
|
||||||
source.forceTurnFaceUp();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lkicheck) {
|
|
||||||
// double freeze tracker, so it doesn't update view
|
|
||||||
game.getTracker().freeze();
|
|
||||||
CardCollection preList = new CardCollection(source);
|
|
||||||
game.getAction().checkStaticAbilities(false, Sets.newHashSet(source), preList);
|
|
||||||
}
|
|
||||||
|
|
||||||
// extra for MayPlay
|
|
||||||
for (CardPlayOption o : source.mayPlay(player)) {
|
|
||||||
la = new LandAbility(this, player, o);
|
|
||||||
la.setCardState(oState);
|
|
||||||
if (la.canPlay()) {
|
|
||||||
abilities.add(la);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// reset static abilities
|
|
||||||
if (lkicheck) {
|
|
||||||
game.getAction().checkStaticAbilities(false);
|
|
||||||
// clear delayed changes, this check should not have updated the view
|
|
||||||
game.getTracker().clearDelayed();
|
|
||||||
// need to unfreeze tracker
|
|
||||||
game.getTracker().unfreeze();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isModal() && hasState(CardStateName.Modal)) {
|
|
||||||
CardState modal = getState(CardStateName.Modal);
|
|
||||||
if (modal.getType().isLand()) {
|
|
||||||
LandAbility la = new LandAbility(this, player, null);
|
|
||||||
la.setCardState(modal);
|
|
||||||
|
|
||||||
Card source = CardCopyService.getLKICopy(this);
|
|
||||||
boolean lkicheck = true;
|
|
||||||
|
|
||||||
// if Card is Facedown, need to check if MayPlay still applies
|
|
||||||
if (isFaceDown()) {
|
|
||||||
source.forceTurnFaceUp();
|
|
||||||
}
|
|
||||||
|
|
||||||
// the modal state is not copied with lki, need to copy it extra
|
|
||||||
if (!source.hasState(CardStateName.Modal)) {
|
|
||||||
source.addAlternateState(CardStateName.Modal, false);
|
|
||||||
source.getState(CardStateName.Modal).copyFrom(this.getState(CardStateName.Modal), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
source.setSplitStateToPlayAbility(la);
|
|
||||||
|
|
||||||
if (la.canPlay(source)) {
|
|
||||||
abilities.add(la);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lkicheck) {
|
|
||||||
// double freeze tracker, so it doesn't update view
|
|
||||||
game.getTracker().freeze();
|
|
||||||
CardCollection preList = new CardCollection(source);
|
|
||||||
game.getAction().checkStaticAbilities(false, Sets.newHashSet(source), preList);
|
|
||||||
}
|
|
||||||
|
|
||||||
// extra for MayPlay
|
|
||||||
for (CardPlayOption o : source.mayPlay(player)) {
|
|
||||||
la = new LandAbility(this, player, o);
|
|
||||||
la.setCardState(modal);
|
|
||||||
if (la.canPlay(source)) {
|
|
||||||
abilities.add(la);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// reset static abilities
|
|
||||||
if (lkicheck) {
|
|
||||||
game.getAction().checkStaticAbilities(false);
|
|
||||||
// clear delayed changes, this check should not have updated the view
|
|
||||||
game.getTracker().clearDelayed();
|
|
||||||
// need to unfreeze tracker
|
|
||||||
game.getTracker().unfreeze();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return abilities;
|
return abilities;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -413,17 +413,16 @@ public class CardFactory {
|
|||||||
|
|
||||||
// SpellPermanent only for Original State
|
// SpellPermanent only for Original State
|
||||||
if (c.getCurrentStateName() == CardStateName.Original || c.getCurrentStateName() == CardStateName.Modal || c.getCurrentStateName().toString().startsWith("Specialize")) {
|
if (c.getCurrentStateName() == CardStateName.Original || c.getCurrentStateName() == CardStateName.Modal || c.getCurrentStateName().toString().startsWith("Specialize")) {
|
||||||
// this is the "default" spell for permanents like creatures and artifacts
|
if (c.isLand()) {
|
||||||
if (c.isPermanent() && !c.isAura() && !c.isLand()) {
|
SpellAbility sa = new LandAbility(c);
|
||||||
|
sa.setCardState(c.getCurrentState());
|
||||||
|
c.addSpellAbility(sa);
|
||||||
|
} else if (c.isPermanent() && !c.isAura()) {
|
||||||
|
// this is the "default" spell for permanents like creatures and artifacts
|
||||||
SpellAbility sa = new SpellPermanent(c);
|
SpellAbility sa = new SpellPermanent(c);
|
||||||
|
sa.setCardState(c.getCurrentState());
|
||||||
// Currently only for Modal, might react different when state is always set
|
|
||||||
//if (c.getCurrentStateName() == CardStateName.Modal) {
|
|
||||||
sa.setCardState(c.getCurrentState());
|
|
||||||
//}
|
|
||||||
c.addSpellAbility(sa);
|
c.addSpellAbility(sa);
|
||||||
}
|
}
|
||||||
// TODO add LandAbility there when refactor MayPlay
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CardFactoryUtil.addAbilityFactoryAbilities(c, face.getAbilities());
|
CardFactoryUtil.addAbilityFactoryAbilities(c, face.getAbilities());
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ import forge.game.event.*;
|
|||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.replacement.ReplacementResult;
|
import forge.game.replacement.ReplacementResult;
|
||||||
import forge.game.replacement.ReplacementType;
|
import forge.game.replacement.ReplacementType;
|
||||||
import forge.game.spellability.LandAbility;
|
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.trigger.Trigger;
|
import forge.game.trigger.Trigger;
|
||||||
import forge.game.trigger.TriggerType;
|
import forge.game.trigger.TriggerType;
|
||||||
@@ -1064,7 +1064,7 @@ public class PhaseHandler implements java.io.Serializable {
|
|||||||
final Zone currentZone = saHost.getZone();
|
final Zone currentZone = saHost.getZone();
|
||||||
|
|
||||||
// Need to check if Zone did change
|
// Need to check if Zone did change
|
||||||
if (currentZone != null && originZone != null && !currentZone.equals(originZone) && (sa.isSpell() || sa instanceof LandAbility)) {
|
if (currentZone != null && originZone != null && !currentZone.equals(originZone) && (sa.isSpell() || sa.isLandAbility())) {
|
||||||
// currently there can be only one Spell put on the Stack at once, or Land Abilities be played
|
// currently there can be only one Spell put on the Stack at once, or Land Abilities be played
|
||||||
final CardZoneTable triggerList = new CardZoneTable(game.getLastStateBattlefield(), game.getLastStateGraveyard());
|
final CardZoneTable triggerList = new CardZoneTable(game.getLastStateBattlefield(), game.getLastStateGraveyard());
|
||||||
triggerList.put(originZone.getZoneType(), currentZone.getZoneType(), saHost);
|
triggerList.put(originZone.getZoneType(), currentZone.getZoneType(), saHost);
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ import forge.game.replacement.ReplacementHandler;
|
|||||||
import forge.game.replacement.ReplacementResult;
|
import forge.game.replacement.ReplacementResult;
|
||||||
import forge.game.replacement.ReplacementType;
|
import forge.game.replacement.ReplacementType;
|
||||||
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.staticability.*;
|
import forge.game.staticability.*;
|
||||||
import forge.game.trigger.Trigger;
|
import forge.game.trigger.Trigger;
|
||||||
@@ -1691,9 +1691,9 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
game.fireEvent(new GameEventShuffle(this));
|
game.fireEvent(new GameEventShuffle(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
public final boolean playLand(final Card land, final boolean ignoreZoneAndTiming) {
|
public final boolean playLand(final Card land, final boolean ignoreZoneAndTiming, SpellAbility cause) {
|
||||||
// Dakkon Blackblade Avatar will use a similar effect
|
// Dakkon Blackblade Avatar will use a similar effect
|
||||||
if (canPlayLand(land, ignoreZoneAndTiming)) {
|
if (canPlayLand(land, ignoreZoneAndTiming, cause)) {
|
||||||
playLandNoCheck(land, null);
|
playLandNoCheck(land, null);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -1706,7 +1706,7 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
land.setController(this, 0);
|
land.setController(this, 0);
|
||||||
if (land.isFaceDown()) {
|
if (land.isFaceDown()) {
|
||||||
land.turnFaceUp(null);
|
land.turnFaceUp(null);
|
||||||
if (cause instanceof LandAbility) {
|
if (cause.isLandAbility()) {
|
||||||
land.changeToState(cause.getCardStateName());
|
land.changeToState(cause.getCardStateName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1730,12 +1730,6 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final boolean canPlayLand(final Card land) {
|
|
||||||
return canPlayLand(land, false);
|
|
||||||
}
|
|
||||||
public final boolean canPlayLand(final Card land, final boolean ignoreZoneAndTiming) {
|
|
||||||
return canPlayLand(land, ignoreZoneAndTiming, null);
|
|
||||||
}
|
|
||||||
public final boolean canPlayLand(final Card land, final boolean ignoreZoneAndTiming, SpellAbility landSa) {
|
public final boolean canPlayLand(final Card land, final boolean ignoreZoneAndTiming, SpellAbility landSa) {
|
||||||
if (!ignoreZoneAndTiming) {
|
if (!ignoreZoneAndTiming) {
|
||||||
// CR 305.3
|
// CR 305.3
|
||||||
|
|||||||
@@ -21,24 +21,20 @@ import forge.card.CardStateName;
|
|||||||
import forge.card.mana.ManaCost;
|
import forge.card.mana.ManaCost;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardCopyService;
|
import forge.game.card.CardCopyService;
|
||||||
import forge.game.card.CardPlayOption;
|
|
||||||
import forge.game.cost.Cost;
|
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.staticability.StaticAbility;
|
import forge.game.staticability.StaticAbility;
|
||||||
|
import forge.game.zone.ZoneType;
|
||||||
import forge.util.CardTranslation;
|
import forge.util.CardTranslation;
|
||||||
import forge.util.Localizer;
|
import forge.util.Localizer;
|
||||||
import org.apache.commons.lang3.ObjectUtils;
|
import org.apache.commons.lang3.ObjectUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
public class LandAbility extends Ability {
|
public class LandAbility extends AbilityStatic {
|
||||||
|
|
||||||
public LandAbility(Card sourceCard, Player p, CardPlayOption mayPlay) {
|
|
||||||
super(sourceCard, new Cost(ManaCost.NO_COST, false));
|
|
||||||
setActivatingPlayer(p);
|
|
||||||
setMayPlay(mayPlay);
|
|
||||||
}
|
|
||||||
public LandAbility(Card sourceCard) {
|
public LandAbility(Card sourceCard) {
|
||||||
this(sourceCard, sourceCard.getController(), null);
|
super(sourceCard, ManaCost.NO_COST);
|
||||||
|
|
||||||
|
getRestrictions().setZone(ZoneType.Hand);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean canPlay(Card newHost) {
|
public boolean canPlay(Card newHost) {
|
||||||
@@ -46,11 +42,21 @@ public class LandAbility extends Ability {
|
|||||||
return p.canPlayLand(newHost, false, this);
|
return p.canPlayLand(newHost, false, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isLandAbility() { return true; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSecondary() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canPlay() {
|
public boolean canPlay() {
|
||||||
Card land = this.getHostCard();
|
Card land = this.getHostCard();
|
||||||
final Player p = this.getActivatingPlayer();
|
final Player p = this.getActivatingPlayer();
|
||||||
|
if (p == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (this.getCardState() != null && land.getCurrentStateName() != this.getCardStateName()) {
|
if (this.getCardState() != null && land.getCurrentStateName() != this.getCardStateName()) {
|
||||||
if (!land.isLKI()) {
|
if (!land.isLKI()) {
|
||||||
land = CardCopyService.getLKICopy(land);
|
land = CardCopyService.getLKICopy(land);
|
||||||
@@ -113,4 +119,41 @@ public class LandAbility extends Ability {
|
|||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Card getAlternateHost(Card source) {
|
||||||
|
boolean lkicheck = false;
|
||||||
|
|
||||||
|
// need to be done before so it works with Vivien and Zoetic Cavern
|
||||||
|
if (source.isFaceDown() && source.isInZone(ZoneType.Exile)) {
|
||||||
|
if (!source.isLKI()) {
|
||||||
|
source = CardCopyService.getLKICopy(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
source.forceTurnFaceUp();
|
||||||
|
lkicheck = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getCardState() != null && source.getCurrentStateName() != getCardStateName()) {
|
||||||
|
if (!source.isLKI()) {
|
||||||
|
source = CardCopyService.getLKICopy(source);
|
||||||
|
}
|
||||||
|
CardStateName stateName = getCardState().getStateName();
|
||||||
|
if (!source.hasState(stateName)) {
|
||||||
|
source.addAlternateState(stateName, false);
|
||||||
|
source.getState(stateName).copyFrom(getHostCard().getState(stateName), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
source.setState(stateName, false);
|
||||||
|
if (getHostCard().isDoubleFaced()) {
|
||||||
|
source.setBackSide(getHostCard().getRules().getSplitType().getChangedStateName().equals(stateName));
|
||||||
|
}
|
||||||
|
|
||||||
|
// need to reset CMC
|
||||||
|
source.setLKICMC(-1);
|
||||||
|
source.setLKICMC(source.getCMC());
|
||||||
|
lkicheck = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return lkicheck ? source : null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -153,6 +153,7 @@ public abstract class Spell extends SpellAbility implements java.io.Serializable
|
|||||||
this.castFaceDown = faceDown;
|
this.castFaceDown = faceDown;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Card getAlternateHost(Card source) {
|
public Card getAlternateHost(Card source) {
|
||||||
boolean lkicheck = false;
|
boolean lkicheck = false;
|
||||||
|
|
||||||
|
|||||||
@@ -527,6 +527,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
|||||||
public boolean isSpell() { return false; }
|
public boolean isSpell() { return false; }
|
||||||
public boolean isAbility() { return true; }
|
public boolean isAbility() { return true; }
|
||||||
public boolean isActivatedAbility() { return false; }
|
public boolean isActivatedAbility() { return false; }
|
||||||
|
public boolean isLandAbility() { return false; }
|
||||||
|
|
||||||
public boolean isTurnFaceUp() {
|
public boolean isTurnFaceUp() {
|
||||||
return isMorphUp() || isDisguiseUp() || isManifestUp() || isCloakUp();
|
return isMorphUp() || isDisguiseUp() || isManifestUp() || isCloakUp();
|
||||||
@@ -2185,7 +2186,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (incR[0].contains("LandAbility")) {
|
else if (incR[0].contains("LandAbility")) {
|
||||||
if (!(root instanceof LandAbility)) {
|
if (!(root.isLandAbility())) {
|
||||||
return testFailed;
|
return testFailed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2544,7 +2545,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
|||||||
if (getRestrictions().isInstantSpeed()) {
|
if (getRestrictions().isInstantSpeed()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if ((isSpell() || this instanceof LandAbility) && (isCastFromPlayEffect() || host.isInstant() || host.hasKeyword(Keyword.FLASH))) {
|
if ((isSpell() || this.isLandAbility()) && (isCastFromPlayEffect() || host.isInstant() || host.hasKeyword(Keyword.FLASH))) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2589,6 +2590,10 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Card getAlternateHost(Card source) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean hasOptionalKeywordAmount(KeywordInterface kw) {
|
public boolean hasOptionalKeywordAmount(KeywordInterface kw) {
|
||||||
return this.optionalKeywordAmount.contains(kw.getKeyword(), Pair.of(kw.getIdx(), kw.getStaticId()));
|
return this.optionalKeywordAmount.contains(kw.getKeyword(), Pair.of(kw.getIdx(), kw.getStaticId()));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package forge.ai.simulation;
|
package forge.ai.simulation;
|
||||||
|
|
||||||
import forge.game.spellability.LandAbility;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -84,7 +84,7 @@ public class SpellAbilityPickerSimulationTest extends SimulationTest {
|
|||||||
|
|
||||||
SpellAbilityPicker picker = new SpellAbilityPicker(game, p);
|
SpellAbilityPicker picker = new SpellAbilityPicker(game, p);
|
||||||
SpellAbility sa = picker.chooseSpellAbilityToPlay(null);
|
SpellAbility sa = picker.chooseSpellAbilityToPlay(null);
|
||||||
AssertJUnit.assertTrue(sa instanceof LandAbility);
|
AssertJUnit.assertTrue(sa.isLandAbility());
|
||||||
AssertJUnit.assertEquals(mountain, sa.getHostCard());
|
AssertJUnit.assertEquals(mountain, sa.getHostCard());
|
||||||
|
|
||||||
Plan plan = picker.getPlan();
|
Plan plan = picker.getPlan();
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import forge.game.card.Card;
|
|||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.player.PlayerController;
|
import forge.game.player.PlayerController;
|
||||||
import forge.game.player.actions.PassPriorityAction;
|
import forge.game.player.actions.PassPriorityAction;
|
||||||
import forge.game.spellability.LandAbility;
|
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.localinstance.properties.ForgePreferences.FPref;
|
import forge.localinstance.properties.ForgePreferences.FPref;
|
||||||
import forge.model.FModel;
|
import forge.model.FModel;
|
||||||
@@ -169,7 +169,7 @@ public class InputPassPriority extends InputSyncronizedBase {
|
|||||||
if (sa.isSpell()) {
|
if (sa.isSpell()) {
|
||||||
return Localizer.getInstance().getMessage("lblCastSpell");
|
return Localizer.getInstance().getMessage("lblCastSpell");
|
||||||
}
|
}
|
||||||
if (sa instanceof LandAbility) {
|
if (sa.isLandAbility()) {
|
||||||
return Localizer.getInstance().getMessage("lblPlayLand");
|
return Localizer.getInstance().getMessage("lblPlayLand");
|
||||||
}
|
}
|
||||||
return Localizer.getInstance().getMessage("lblActivateAbility");
|
return Localizer.getInstance().getMessage("lblActivateAbility");
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ import forge.game.mana.ManaRefundService;
|
|||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.player.PlayerController;
|
import forge.game.player.PlayerController;
|
||||||
import forge.game.player.PlayerView;
|
import forge.game.player.PlayerView;
|
||||||
import forge.game.spellability.LandAbility;
|
|
||||||
import forge.game.spellability.OptionalCostValue;
|
import forge.game.spellability.OptionalCostValue;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.staticability.StaticAbilityManaConvert;
|
import forge.game.staticability.StaticAbilityManaConvert;
|
||||||
@@ -70,7 +70,7 @@ public class HumanPlay {
|
|||||||
Card source = sa.getHostCard();
|
Card source = sa.getHostCard();
|
||||||
sa.setActivatingPlayer(p);
|
sa.setActivatingPlayer(p);
|
||||||
|
|
||||||
if (sa instanceof LandAbility) {
|
if (sa.isLandAbility()) {
|
||||||
if (sa.canPlay()) {
|
if (sa.canPlay()) {
|
||||||
sa.resolve();
|
sa.resolve();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user