mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-16 10:48:00 +00:00
Merge remote-tracking branch 'core/master'
This commit is contained in:
@@ -449,6 +449,9 @@ public class AiAttackController {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO for multiplayer this should either add some heuristics
|
||||||
|
// so the other opponents attack power is also measured in
|
||||||
|
// or refactor it with aiLifeInDanger somehow if performance impact isn't too bad
|
||||||
CardLists.sortByPowerDesc(oppList);
|
CardLists.sortByPowerDesc(oppList);
|
||||||
for (Card attacker : oppList) {
|
for (Card attacker : oppList) {
|
||||||
if (!ComputerUtilCombat.canAttackNextTurn(attacker)) {
|
if (!ComputerUtilCombat.canAttackNextTurn(attacker)) {
|
||||||
@@ -1037,7 +1040,7 @@ public class AiAttackController {
|
|||||||
&& defendingOpponent != null
|
&& defendingOpponent != null
|
||||||
&& ComputerUtil.countUsefulCreatures(ai) > ComputerUtil.countUsefulCreatures(defendingOpponent)
|
&& ComputerUtil.countUsefulCreatures(ai) > ComputerUtil.countUsefulCreatures(defendingOpponent)
|
||||||
&& ai.getLife() > defendingOpponent.getLife()
|
&& ai.getLife() > defendingOpponent.getLife()
|
||||||
&& !ComputerUtilCombat.lifeInDanger(ai, combat)
|
&& !ComputerUtilCombat.lifeInDanger(ai, combat) // this isn't really doing anything unless the attacking player in combat isn't the AI (which currently isn't used like that)
|
||||||
&& (ComputerUtilMana.getAvailableManaEstimate(ai) > 0) || tradeIfTappedOut
|
&& (ComputerUtilMana.getAvailableManaEstimate(ai) > 0) || tradeIfTappedOut
|
||||||
&& (ComputerUtilMana.getAvailableManaEstimate(defendingOpponent) == 0) || MyRandom.percentTrue(extraChanceIfOppHasMana)
|
&& (ComputerUtilMana.getAvailableManaEstimate(defendingOpponent) == 0) || MyRandom.percentTrue(extraChanceIfOppHasMana)
|
||||||
&& (!tradeIfLowerLifePressure || (ai.getLifeLostLastTurn() + ai.getLifeLostThisTurn() <
|
&& (!tradeIfLowerLifePressure || (ai.getLifeLostLastTurn() + ai.getLifeLostThisTurn() <
|
||||||
|
|||||||
@@ -160,9 +160,7 @@ public class AiController {
|
|||||||
all.add(ccvPlayerLibrary.get(0));
|
all.add(ccvPlayerLibrary.get(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (final Player opp : player.getOpponents()) {
|
all.addAll(player.getOpponents().getCardsIn(ZoneType.Exile));
|
||||||
all.addAll(opp.getCardsIn(ZoneType.Exile));
|
|
||||||
}
|
|
||||||
|
|
||||||
final List<SpellAbility> spellAbilities = Lists.newArrayList();
|
final List<SpellAbility> spellAbilities = Lists.newArrayList();
|
||||||
for (final Card c : all) {
|
for (final Card c : all) {
|
||||||
@@ -703,7 +701,7 @@ public class AiController {
|
|||||||
// but should work in most circumstances to ensure safety in whatever the AI is using this for.
|
// but should work in most circumstances to ensure safety in whatever the AI is using this for.
|
||||||
if (manaSources.size() >= cost.getConvertedManaCost()) {
|
if (manaSources.size() >= cost.getConvertedManaCost()) {
|
||||||
for (Card c : manaSources) {
|
for (Card c : manaSources) {
|
||||||
AiCardMemory.rememberCard(player, c, memSet);
|
memory.rememberCard(c, memSet);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -839,7 +837,7 @@ public class AiController {
|
|||||||
|
|
||||||
// When processing a new SA, clear the previously remembered cards that have been marked to avoid re-entry
|
// When processing a new SA, clear the previously remembered cards that have been marked to avoid re-entry
|
||||||
// which might potentially cause a stack overflow.
|
// which might potentially cause a stack overflow.
|
||||||
AiCardMemory.clearMemorySet(this, AiCardMemory.MemorySet.MARKED_TO_AVOID_REENTRY);
|
memory.clearMemorySet(AiCardMemory.MemorySet.MARKED_TO_AVOID_REENTRY);
|
||||||
|
|
||||||
// TODO before suspending some spells try to predict if relevant targets can be expected
|
// TODO before suspending some spells try to predict if relevant targets can be expected
|
||||||
if (sa.getApi() != null) {
|
if (sa.getApi() != null) {
|
||||||
@@ -1490,7 +1488,7 @@ public class AiController {
|
|||||||
predictedCombatNextTurn = null;
|
predictedCombatNextTurn = null;
|
||||||
|
|
||||||
// Reset priority mana reservation that's meant to work for one spell only
|
// Reset priority mana reservation that's meant to work for one spell only
|
||||||
AiCardMemory.clearMemorySet(player, AiCardMemory.MemorySet.HELD_MANA_SOURCES_FOR_NEXT_SPELL);
|
memory.clearMemorySet(AiCardMemory.MemorySet.HELD_MANA_SOURCES_FOR_NEXT_SPELL);
|
||||||
|
|
||||||
if (useSimulation) {
|
if (useSimulation) {
|
||||||
return singleSpellAbilityList(simPicker.chooseSpellAbilityToPlay(null));
|
return singleSpellAbilityList(simPicker.chooseSpellAbilityToPlay(null));
|
||||||
|
|||||||
@@ -344,8 +344,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
chosen = chosen.subList(0, c);
|
chosen = chosen.subList(0, c);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
chosen = ComputerUtil.choosePutToLibraryFrom(player, cost.getFrom(), cost.getType(), source, ability.getTargetCard(), c, ability);
|
chosen = ComputerUtil.choosePutToLibraryFrom(player, cost.getFrom(), cost.getType(), source, ability.getTargetCard(), c, ability);
|
||||||
}
|
}
|
||||||
return chosen.isEmpty() ? null : PaymentDecision.card(chosen);
|
return chosen.isEmpty() ? null : PaymentDecision.card(chosen);
|
||||||
@@ -363,8 +362,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
|
|||||||
Card card;
|
Card card;
|
||||||
if (cost.getType().equals("Creature.YouCtrl")) {
|
if (cost.getType().equals("Creature.YouCtrl")) {
|
||||||
card = ComputerUtilCard.getWorstCreatureAI(typeList);
|
card = ComputerUtilCard.getWorstCreatureAI(typeList);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
card = ComputerUtilCard.getWorstPermanentAI(typeList, false, false, false, false);
|
card = ComputerUtilCard.getWorstPermanentAI(typeList, false, false, false, false);
|
||||||
}
|
}
|
||||||
return PaymentDecision.card(card);
|
return PaymentDecision.card(card);
|
||||||
|
|||||||
@@ -119,8 +119,7 @@ public class AiProfileUtil {
|
|||||||
* @return ArrayList<String> - an array of strings containing all
|
* @return ArrayList<String> - an array of strings containing all
|
||||||
* available profiles.
|
* available profiles.
|
||||||
*/
|
*/
|
||||||
public static List<String> getAvailableProfiles()
|
public static List<String> getAvailableProfiles() {
|
||||||
{
|
|
||||||
final List<String> availableProfiles = new ArrayList<>();
|
final List<String> availableProfiles = new ArrayList<>();
|
||||||
|
|
||||||
final File dir = new File(AI_PROFILE_DIR);
|
final File dir = new File(AI_PROFILE_DIR);
|
||||||
|
|||||||
@@ -703,13 +703,13 @@ public class ComputerUtil {
|
|||||||
if (pow <= 0) {
|
if (pow <= 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
totalPower += pow;
|
|
||||||
if (pow >= amount) {
|
if (pow >= amount) {
|
||||||
// If the power of this creature matches the totalPower needed
|
// If the power of this creature matches the totalPower needed
|
||||||
// Might as well only use this creature?
|
// Might as well only use this creature?
|
||||||
tapList.clear();
|
tapList.clear();
|
||||||
}
|
}
|
||||||
tapList.add(next);
|
tapList.add(next);
|
||||||
|
totalPower = CardLists.getTotalPower(tapList, true, sa.hasParam("Crew"));
|
||||||
if (totalPower >= amount) {
|
if (totalPower >= amount) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -793,7 +793,7 @@ public class ComputerUtil {
|
|||||||
boolean exceptSelf = "ExceptSelf".equals(source.getParam("AILogic"));
|
boolean exceptSelf = "ExceptSelf".equals(source.getParam("AILogic"));
|
||||||
boolean removedSelf = false;
|
boolean removedSelf = false;
|
||||||
|
|
||||||
if (isOptional && source.hasParam("Devour") || source.hasParam("Exploit")) {
|
if (isOptional && (source.hasParam("Devour") || source.hasParam("Exploit"))) {
|
||||||
if (source.hasParam("Exploit")) {
|
if (source.hasParam("Exploit")) {
|
||||||
for (Trigger t : host.getTriggers()) {
|
for (Trigger t : host.getTriggers()) {
|
||||||
if (t.getMode() == TriggerType.Exploited) {
|
if (t.getMode() == TriggerType.Exploited) {
|
||||||
|
|||||||
@@ -72,10 +72,8 @@ public class ComputerUtilAbility {
|
|||||||
if (!player.getCardsIn(ZoneType.Library).isEmpty()) {
|
if (!player.getCardsIn(ZoneType.Library).isEmpty()) {
|
||||||
all.add(player.getCardsIn(ZoneType.Library).get(0));
|
all.add(player.getCardsIn(ZoneType.Library).get(0));
|
||||||
}
|
}
|
||||||
for (Player p : game.getPlayers()) {
|
all.addAll(game.getPlayers().getCardsIn(ZoneType.Exile));
|
||||||
all.addAll(p.getCardsIn(ZoneType.Exile));
|
all.addAll(game.getPlayers().getCardsIn(ZoneType.Battlefield));
|
||||||
all.addAll(p.getCardsIn(ZoneType.Battlefield));
|
|
||||||
}
|
|
||||||
return all;
|
return all;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package forge.ai;
|
package forge.ai;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
@@ -1361,7 +1360,7 @@ public class ComputerUtilCard {
|
|||||||
if (!Iterables.any(oppCreatures, CardPredicates.possibleBlockers(pumped))) {
|
if (!Iterables.any(oppCreatures, CardPredicates.possibleBlockers(pumped))) {
|
||||||
threat *= 2;
|
threat *= 2;
|
||||||
}
|
}
|
||||||
if (c.getNetPower() == 0 && c == sa.getHostCard() && power > 0 ) {
|
if (c.getNetPower() == 0 && c == sa.getHostCard() && power > 0) {
|
||||||
threat *= 4; //over-value self +attack for 0 power creatures which may be pumped further after attacking
|
threat *= 4; //over-value self +attack for 0 power creatures which may be pumped further after attacking
|
||||||
}
|
}
|
||||||
chance += threat;
|
chance += threat;
|
||||||
@@ -1378,9 +1377,7 @@ public class ComputerUtilCard {
|
|||||||
&& ComputerUtilMana.hasEnoughManaSourcesToCast(sa, ai)) {
|
&& ComputerUtilMana.hasEnoughManaSourcesToCast(sa, ai)) {
|
||||||
combatTrick = true;
|
combatTrick = true;
|
||||||
|
|
||||||
final List<String> kws = sa.hasParam("KW") ? Arrays.asList(sa.getParam("KW").split(" & "))
|
for (String kw : keywords) {
|
||||||
: Lists.newArrayList();
|
|
||||||
for (String kw : kws) {
|
|
||||||
if (!kw.equals("Trample") && !kw.equals("First Strike") && !kw.equals("Double Strike")) {
|
if (!kw.equals("Trample") && !kw.equals("First Strike") && !kw.equals("Double Strike")) {
|
||||||
combatTrick = false;
|
combatTrick = false;
|
||||||
break;
|
break;
|
||||||
@@ -1565,7 +1562,7 @@ public class ComputerUtilCard {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//5. if the life of the computer is in danger, try to pump blockers blocking Tramplers
|
//5. if the life of the computer is in danger, try to pump blockers blocking Tramplers
|
||||||
if (combat.isBlocking(c) && toughness > 0 ) {
|
if (combat.isBlocking(c) && toughness > 0) {
|
||||||
List<Card> blockedBy = combat.getAttackersBlockedBy(c);
|
List<Card> blockedBy = combat.getAttackersBlockedBy(c);
|
||||||
boolean attackerHasTrample = false;
|
boolean attackerHasTrample = false;
|
||||||
for (Card b : blockedBy) {
|
for (Card b : blockedBy) {
|
||||||
|
|||||||
@@ -417,6 +417,7 @@ public class ComputerUtilCombat {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO check for replacement effect instead
|
||||||
CardCollectionView otb = ai.getCardsIn(ZoneType.Battlefield);
|
CardCollectionView otb = ai.getCardsIn(ZoneType.Battlefield);
|
||||||
// Special cases:
|
// Special cases:
|
||||||
// AI can't lose in combat in presence of Worship (with creatures)
|
// AI can't lose in combat in presence of Worship (with creatures)
|
||||||
|
|||||||
@@ -795,6 +795,9 @@ public class ComputerUtilMana {
|
|||||||
|
|
||||||
if (toPay.isPhyrexian()) {
|
if (toPay.isPhyrexian()) {
|
||||||
cost.payPhyrexian();
|
cost.payPhyrexian();
|
||||||
|
if (!test) {
|
||||||
|
sa.setSpendPhyrexianMana(true);
|
||||||
|
}
|
||||||
} else if (lifeInsteadOfBlack) {
|
} else if (lifeInsteadOfBlack) {
|
||||||
cost.decreaseShard(ManaCostShard.BLACK, 1);
|
cost.decreaseShard(ManaCostShard.BLACK, 1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -242,11 +242,11 @@ public class PlayerControllerAi extends PlayerController {
|
|||||||
SpellAbility selected;
|
SpellAbility selected;
|
||||||
do {
|
do {
|
||||||
selected = chooseSingleSpellForEffect(remaining, sa, title, params);
|
selected = chooseSingleSpellForEffect(remaining, sa, title, params);
|
||||||
if ( selected != null ) {
|
if (selected != null) {
|
||||||
remaining.remove(selected);
|
remaining.remove(selected);
|
||||||
selecteds.add(selected);
|
selecteds.add(selected);
|
||||||
}
|
}
|
||||||
} while ( (selected != null ) && (selecteds.size() < num) );
|
} while (selected != null && selecteds.size() < num);
|
||||||
return selecteds;
|
return selecteds;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -544,12 +544,6 @@ public class PlayerControllerAi extends PlayerController {
|
|||||||
return getAi().chooseCardsToDelve(genericAmount, grave);
|
return getAi().chooseCardsToDelve(genericAmount, grave);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public TargetChoices chooseNewTargetsFor(SpellAbility ability, Predicate<GameObject> filter, boolean optional) {
|
|
||||||
// AI currently can't do this. But when it can it will need to be based on Ability API
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CardCollectionView chooseCardsToDiscardUnlessType(int num, CardCollectionView hand, String uType, SpellAbility sa) {
|
public CardCollectionView chooseCardsToDiscardUnlessType(int num, CardCollectionView hand, String uType, SpellAbility sa) {
|
||||||
String [] splitUTypes = uType.split(",");
|
String [] splitUTypes = uType.split(",");
|
||||||
@@ -1099,6 +1093,12 @@ public class PlayerControllerAi extends PlayerController {
|
|||||||
return brains.doTrigger(currentAbility, true);
|
return brains.doTrigger(currentAbility, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TargetChoices chooseNewTargetsFor(SpellAbility ability, Predicate<GameObject> filter, boolean optional) {
|
||||||
|
// AI currently can't do this. But when it can it will need to be based on Ability API
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean chooseCardsPile(SpellAbility sa, CardCollectionView pile1, CardCollectionView pile2, String faceUp) {
|
public boolean chooseCardsPile(SpellAbility sa, CardCollectionView pile1, CardCollectionView pile2, String faceUp) {
|
||||||
if (faceUp.equals("True")) {
|
if (faceUp.equals("True")) {
|
||||||
@@ -1341,8 +1341,7 @@ public class PlayerControllerAi extends PlayerController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<OptionalCostValue> chooseOptionalCosts(SpellAbility chosen,
|
public List<OptionalCostValue> chooseOptionalCosts(SpellAbility chosen, List<OptionalCostValue> optionalCostValues) {
|
||||||
List<OptionalCostValue> optionalCostValues) {
|
|
||||||
List<OptionalCostValue> chosenOptCosts = Lists.newArrayList();
|
List<OptionalCostValue> chosenOptCosts = Lists.newArrayList();
|
||||||
Cost costSoFar = chosen.getPayCosts().copy();
|
Cost costSoFar = chosen.getPayCosts().copy();
|
||||||
|
|
||||||
|
|||||||
@@ -182,6 +182,8 @@ public class ForgeScript {
|
|||||||
return sa.hasParam("Daybound");
|
return sa.hasParam("Daybound");
|
||||||
} else if (property.equals("Nightbound")) {
|
} else if (property.equals("Nightbound")) {
|
||||||
return sa.hasParam("Nightbound");
|
return sa.hasParam("Nightbound");
|
||||||
|
} else if (property.equals("paidPhyrexianMana")) {
|
||||||
|
return sa.getSpendPhyrexianMana();
|
||||||
} else if (property.equals("MayPlaySource")) {
|
} else if (property.equals("MayPlaySource")) {
|
||||||
StaticAbility m = sa.getMayPlay();
|
StaticAbility m = sa.getMayPlay();
|
||||||
if (m == null) {
|
if (m == null) {
|
||||||
|
|||||||
@@ -1308,6 +1308,7 @@ public class GameAction {
|
|||||||
desCreats = new CardCollection();
|
desCreats = new CardCollection();
|
||||||
}
|
}
|
||||||
desCreats.add(c);
|
desCreats.add(c);
|
||||||
|
c.setHasBeenDealtDeathtouchDamage(false);
|
||||||
checkAgain = true;
|
checkAgain = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2147,6 +2147,11 @@ public class CardFactoryUtil {
|
|||||||
|
|
||||||
re.setOverridingAbility(saReturn);
|
re.setOverridingAbility(saReturn);
|
||||||
|
|
||||||
|
inst.addReplacement(re);
|
||||||
|
} else if (keyword.equals("Compleated")) {
|
||||||
|
String sb = "etbCounter:LOYALTY:-2:ValidCard$ Card.CastSa Spell.paidPhyrexianMana:If life was paid, this planeswalker enters with two fewer loyalty counters";
|
||||||
|
final ReplacementEffect re = makeEtbCounter(sb, card, intrinsic);
|
||||||
|
|
||||||
inst.addReplacement(re);
|
inst.addReplacement(re);
|
||||||
} else if (keyword.startsWith("Dredge")) {
|
} else if (keyword.startsWith("Dredge")) {
|
||||||
final String dredgeAmount = keyword.split(":")[1];
|
final String dredgeAmount = keyword.split(":")[1];
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ public abstract class PlayerController {
|
|||||||
public final SpellAbility getAbilityToPlay(final Card hostCard, final List<SpellAbility> abilities) { return getAbilityToPlay(hostCard, abilities, null); }
|
public final SpellAbility getAbilityToPlay(final Card hostCard, final List<SpellAbility> abilities) { return getAbilityToPlay(hostCard, abilities, null); }
|
||||||
public abstract SpellAbility getAbilityToPlay(Card hostCard, List<SpellAbility> abilities, ITriggerEvent triggerEvent);
|
public abstract SpellAbility getAbilityToPlay(Card hostCard, List<SpellAbility> abilities, ITriggerEvent triggerEvent);
|
||||||
|
|
||||||
//public abstract void playFromSuspend(Card c);
|
@Deprecated
|
||||||
public abstract void playSpellAbilityForFree(SpellAbility copySA, boolean mayChoseNewTargets);
|
public abstract void playSpellAbilityForFree(SpellAbility copySA, boolean mayChoseNewTargets);
|
||||||
public abstract void playSpellAbilityNoStack(SpellAbility effectSA, boolean mayChoseNewTargets);
|
public abstract void playSpellAbilityNoStack(SpellAbility effectSA, boolean mayChoseNewTargets);
|
||||||
|
|
||||||
@@ -130,13 +130,13 @@ public abstract class PlayerController {
|
|||||||
|
|
||||||
public abstract List<SpellAbility> chooseSpellAbilitiesForEffect(List<SpellAbility> spells, SpellAbility sa, String title, int num, Map<String, Object> params);
|
public abstract List<SpellAbility> chooseSpellAbilitiesForEffect(List<SpellAbility> spells, SpellAbility sa, String title, int num, Map<String, Object> params);
|
||||||
|
|
||||||
public abstract SpellAbility chooseSingleSpellForEffect(List<SpellAbility> spells, SpellAbility sa, String title,
|
public abstract SpellAbility chooseSingleSpellForEffect(List<SpellAbility> spells, SpellAbility sa, String title, Map<String, Object> params);
|
||||||
Map<String, Object> params);
|
|
||||||
|
|
||||||
public abstract <T extends GameEntity> List<T> chooseEntitiesForEffect(FCollectionView<T> optionList, int min, int max, DelayedReveal delayedReveal, SpellAbility sa, String title, Player relatedPlayer, Map<String, Object> params);
|
public abstract <T extends GameEntity> List<T> chooseEntitiesForEffect(FCollectionView<T> optionList, int min, int max, DelayedReveal delayedReveal, SpellAbility sa, String title, Player relatedPlayer, Map<String, Object> params);
|
||||||
|
|
||||||
public abstract boolean confirmAction(SpellAbility sa, PlayerActionConfirmMode mode, String message);
|
public abstract boolean confirmAction(SpellAbility sa, PlayerActionConfirmMode mode, String message);
|
||||||
public abstract boolean confirmBidAction(SpellAbility sa, PlayerActionConfirmMode bidlife, String string, int bid, Player winner);
|
public abstract boolean confirmBidAction(SpellAbility sa, PlayerActionConfirmMode bidlife, String string, int bid, Player winner);
|
||||||
|
public abstract boolean confirmReplacementEffect(ReplacementEffect replacementEffect, SpellAbility effectSA, GameEntity affected, String question);
|
||||||
public abstract boolean confirmStaticApplication(Card hostCard, GameEntity affected, String logic, String message);
|
public abstract boolean confirmStaticApplication(Card hostCard, GameEntity affected, String logic, String message);
|
||||||
public abstract boolean confirmTrigger(WrappedAbility sa);
|
public abstract boolean confirmTrigger(WrappedAbility sa);
|
||||||
public abstract Player chooseStartingPlayer(boolean isFirstGame);
|
public abstract Player chooseStartingPlayer(boolean isFirstGame);
|
||||||
@@ -170,10 +170,11 @@ public abstract class PlayerController {
|
|||||||
|
|
||||||
/** p = target player, validCards - possible discards, min cards to discard */
|
/** p = target player, validCards - possible discards, min cards to discard */
|
||||||
public abstract CardCollectionView chooseCardsToDiscardFrom(Player playerDiscard, SpellAbility sa, CardCollection validCards, int min, int max);
|
public abstract CardCollectionView chooseCardsToDiscardFrom(Player playerDiscard, SpellAbility sa, CardCollection validCards, int min, int max);
|
||||||
|
public abstract CardCollectionView chooseCardsToDiscardUnlessType(int min, CardCollectionView hand, String param, SpellAbility sa);
|
||||||
|
public abstract CardCollection chooseCardsToDiscardToMaximumHandSize(int numDiscard);
|
||||||
|
|
||||||
public abstract CardCollectionView chooseCardsToDelve(int genericAmount, CardCollection grave);
|
public abstract CardCollectionView chooseCardsToDelve(int genericAmount, CardCollection grave);
|
||||||
public abstract CardCollectionView chooseCardsToRevealFromHand(int min, int max, CardCollectionView valid);
|
public abstract CardCollectionView chooseCardsToRevealFromHand(int min, int max, CardCollectionView valid);
|
||||||
public abstract CardCollectionView chooseCardsToDiscardUnlessType(int min, CardCollectionView hand, String param, SpellAbility sa);
|
|
||||||
public abstract List<SpellAbility> chooseSaToActivateFromOpeningHand(List<SpellAbility> usableFromOpeningHand);
|
public abstract List<SpellAbility> chooseSaToActivateFromOpeningHand(List<SpellAbility> usableFromOpeningHand);
|
||||||
public abstract Mana chooseManaFromPool(List<Mana> manaChoices);
|
public abstract Mana chooseManaFromPool(List<Mana> manaChoices);
|
||||||
|
|
||||||
@@ -183,7 +184,6 @@ public abstract class PlayerController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public abstract Object vote(SpellAbility sa, String prompt, List<Object> options, ListMultimap<Object, Player> votes, Player forPlayer);
|
public abstract Object vote(SpellAbility sa, String prompt, List<Object> options, ListMultimap<Object, Player> votes, Player forPlayer);
|
||||||
public abstract boolean confirmReplacementEffect(ReplacementEffect replacementEffect, SpellAbility effectSA, GameEntity affected, String question);
|
|
||||||
|
|
||||||
public abstract CardCollectionView getCardsToMulligan(Player firstPlayer);
|
public abstract CardCollectionView getCardsToMulligan(Player firstPlayer);
|
||||||
public abstract boolean mulliganKeepHand(Player player, int cardsToReturn);
|
public abstract boolean mulliganKeepHand(Player player, int cardsToReturn);
|
||||||
@@ -194,7 +194,6 @@ public abstract class PlayerController {
|
|||||||
public abstract List<SpellAbility> chooseSpellAbilityToPlay();
|
public abstract List<SpellAbility> chooseSpellAbilityToPlay();
|
||||||
public abstract boolean playChosenSpellAbility(SpellAbility sa);
|
public abstract boolean playChosenSpellAbility(SpellAbility sa);
|
||||||
|
|
||||||
public abstract CardCollection chooseCardsToDiscardToMaximumHandSize(int numDiscard);
|
|
||||||
public abstract boolean payManaOptional(Card card, Cost cost, SpellAbility sa, String prompt, ManaPaymentPurpose purpose);
|
public abstract boolean payManaOptional(Card card, Cost cost, SpellAbility sa, String prompt, ManaPaymentPurpose purpose);
|
||||||
|
|
||||||
public abstract int chooseNumberForKeywordCost(SpellAbility sa, Cost cost, KeywordInterface keyword, String prompt, int max);
|
public abstract int chooseNumberForKeywordCost(SpellAbility sa, Cost cost, KeywordInterface keyword, String prompt, int max);
|
||||||
@@ -213,6 +212,8 @@ public abstract class PlayerController {
|
|||||||
public boolean chooseBinary(SpellAbility sa, String question, BinaryChoiceType kindOfChoice, Map<String, Object> params) { return chooseBinary(sa, question, kindOfChoice); }
|
public boolean chooseBinary(SpellAbility sa, String question, BinaryChoiceType kindOfChoice, Map<String, Object> params) { return chooseBinary(sa, question, kindOfChoice); }
|
||||||
|
|
||||||
public abstract boolean chooseFlipResult(SpellAbility sa, Player flipper, boolean[] results, boolean call);
|
public abstract boolean chooseFlipResult(SpellAbility sa, Player flipper, boolean[] results, boolean call);
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public abstract Card chooseProtectionShield(GameEntity entityBeingDamaged, List<String> options, Map<String, Card> choiceMap);
|
public abstract Card chooseProtectionShield(GameEntity entityBeingDamaged, List<String> options, Map<String, Card> choiceMap);
|
||||||
|
|
||||||
public abstract List<AbilitySub> chooseModeForAbility(SpellAbility sa, List<AbilitySub> possible, int min, int num, boolean allowRepeat);
|
public abstract List<AbilitySub> chooseModeForAbility(SpellAbility sa, List<AbilitySub> possible, int min, int num, boolean allowRepeat);
|
||||||
@@ -250,7 +251,6 @@ public abstract class PlayerController {
|
|||||||
public final boolean payManaCost(CostPartMana costPartMana, SpellAbility sa, String prompt, boolean effect) {
|
public final boolean payManaCost(CostPartMana costPartMana, SpellAbility sa, String prompt, boolean effect) {
|
||||||
return payManaCost(costPartMana, sa, prompt, null, effect);
|
return payManaCost(costPartMana, sa, prompt, null, effect);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final boolean payManaCost(CostPartMana costPartMana, SpellAbility sa, String prompt, ManaConversionMatrix matrix, boolean effect) {
|
public final boolean payManaCost(CostPartMana costPartMana, SpellAbility sa, String prompt, ManaConversionMatrix matrix, boolean effect) {
|
||||||
return payManaCost(costPartMana.getManaCostFor(sa), costPartMana, sa, prompt, matrix, effect);
|
return payManaCost(costPartMana.getManaCostFor(sa), costPartMana, sa, prompt, matrix, effect);
|
||||||
}
|
}
|
||||||
@@ -261,7 +261,6 @@ public abstract class PlayerController {
|
|||||||
public abstract List<Card> chooseCardsForSplice(SpellAbility sa, List<Card> cards);
|
public abstract List<Card> chooseCardsForSplice(SpellAbility sa, List<Card> cards);
|
||||||
|
|
||||||
public abstract String chooseCardName(SpellAbility sa, Predicate<ICardFace> cpp, String valid, String message);
|
public abstract String chooseCardName(SpellAbility sa, Predicate<ICardFace> cpp, String valid, String message);
|
||||||
|
|
||||||
public abstract String chooseCardName(SpellAbility sa, List<ICardFace> faces, String message);
|
public abstract String chooseCardName(SpellAbility sa, List<ICardFace> faces, String message);
|
||||||
|
|
||||||
public abstract Card chooseDungeon(Player player, List<PaperCard> dungeonCards, String message);
|
public abstract Card chooseDungeon(Player player, List<PaperCard> dungeonCards, String message);
|
||||||
|
|||||||
@@ -115,6 +115,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
|||||||
private Player targetingPlayer = null;
|
private Player targetingPlayer = null;
|
||||||
private Pair<Long, Player> controlledByPlayer = null;
|
private Pair<Long, Player> controlledByPlayer = null;
|
||||||
private ManaCostBeingPaid manaCostBeingPaid = null;
|
private ManaCostBeingPaid manaCostBeingPaid = null;
|
||||||
|
private boolean spentPhyrexian = false;
|
||||||
|
|
||||||
private SpellAbility grantorOriginal = null;
|
private SpellAbility grantorOriginal = null;
|
||||||
private StaticAbility grantorStatic = null;
|
private StaticAbility grantorStatic = null;
|
||||||
@@ -613,6 +614,13 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
|||||||
payingMana.clear();
|
payingMana.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final boolean getSpendPhyrexianMana() {
|
||||||
|
return this.spentPhyrexian;
|
||||||
|
}
|
||||||
|
public final void setSpendPhyrexianMana(boolean value) {
|
||||||
|
this.spentPhyrexian = value;
|
||||||
|
}
|
||||||
|
|
||||||
public final void applyPayingManaEffects() {
|
public final void applyPayingManaEffects() {
|
||||||
Card host = getHostCard();
|
Card host = getHostCard();
|
||||||
|
|
||||||
@@ -905,8 +913,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
|||||||
// if alternate state is viewed while card uses original
|
// if alternate state is viewed while card uses original
|
||||||
if (node.isIntrinsic() && node.cardState != null && node.cardState.getCard() == node.getHostCard()) {
|
if (node.isIntrinsic() && node.cardState != null && node.cardState.getCard() == node.getHostCard()) {
|
||||||
currentName = node.cardState.getName();
|
currentName = node.cardState.getName();
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
currentName = node.getHostCard().getName();
|
currentName = node.getHostCard().getName();
|
||||||
}
|
}
|
||||||
desc = CardTranslation.translateMultipleDescriptionText(desc, currentName);
|
desc = CardTranslation.translateMultipleDescriptionText(desc, currentName);
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ T:Mode$ Discarded | ValidCard$ Card.YouCtrl | TriggerZones$ Battlefield | Execut
|
|||||||
SVar:TrigExile:DB$ ChangeZone | Defined$ TriggeredCard | Origin$ Graveyard | Destination$ Exile
|
SVar:TrigExile:DB$ ChangeZone | Defined$ TriggeredCard | Origin$ Graveyard | Destination$ Exile
|
||||||
A:AB$ Draw | Cost$ 2 T | NumCards$ 1 | SpellDescription$ Draw a card, then discard a card. | SubAbility$ DBDiscard
|
A:AB$ Draw | Cost$ 2 T | NumCards$ 1 | SpellDescription$ Draw a card, then discard a card. | SubAbility$ DBDiscard
|
||||||
SVar:DBDiscard:DB$ Discard | Defined$ You | NumCards$ 1 | Mode$ TgtChoose
|
SVar:DBDiscard:DB$ Discard | Defined$ You | NumCards$ 1 | Mode$ TgtChoose
|
||||||
AI:RemoveDeck:All
|
|
||||||
A:AB$ ChangeZoneAll | Cost$ 4 T Sac<1/CARDNAME> | ChangeType$ Card.ExiledWithSource | Origin$ Exile | Destination$ Hand | AILogic$ DiscardAllAndRetExiled.noDiscard.minAdv2 | SpellDescription$ Return all cards exiled with CARDNAME to their owner's hand.
|
A:AB$ ChangeZoneAll | Cost$ 4 T Sac<1/CARDNAME> | ChangeType$ Card.ExiledWithSource | Origin$ Exile | Destination$ Hand | AILogic$ DiscardAllAndRetExiled.noDiscard.minAdv2 | SpellDescription$ Return all cards exiled with CARDNAME to their owner's hand.
|
||||||
|
AI:RemoveDeck:All
|
||||||
AI:RemoveDeck:Random
|
AI:RemoveDeck:Random
|
||||||
Oracle:Whenever you discard a card, exile that card from your graveyard.\n{2}, {T}: Draw a card, then discard a card.\n{4}, {T}, Sacrifice Bag of Holding: Return all cards exiled with Bag of Holding to their owner's hand.
|
Oracle:Whenever you discard a card, exile that card from your graveyard.\n{2}, {T}: Draw a card, then discard a card.\n{4}, {T}, Sacrifice Bag of Holding: Return all cards exiled with Bag of Holding to their owner's hand.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
Name:Carbonize
|
Name:Carbonize
|
||||||
ManaCost:2 R
|
ManaCost:2 R
|
||||||
Types:Instant
|
Types:Instant
|
||||||
A:SP$ DealDamage | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | NumDmg$ 3 | SubAbility$ DB | ReplaceDyingDefined$ Targeted.Creature | SpellDescription$ CARDNAME deals 3 damage to any target. If it's a creature, it can't be regenerated this turn, and if it would die this turn, exile it instead.
|
A:SP$ DealDamage | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | NumDmg$ 3 | SubAbility$ DB | ReplaceDyingDefined$ ThisTargetedCard.Creature | SpellDescription$ CARDNAME deals 3 damage to any target. If it's a creature, it can't be regenerated this turn, and if it would die this turn, exile it instead.
|
||||||
SVar:DB:DB$ Pump | KW$ HIDDEN CARDNAME can't be regenerated. | Defined$ Targeted.Creature | StackDescription$ None
|
SVar:DB:DB$ Pump | KW$ HIDDEN CARDNAME can't be regenerated. | Defined$ ParentTarget | ConditionDefined$ ParentTarget | ConditionPresent$ Creature | StackDescription$ None
|
||||||
Oracle:Carbonize deals 3 damage to any target. If it's a creature, it can't be regenerated this turn, and if it would die this turn, exile it instead.
|
Oracle:Carbonize deals 3 damage to any target. If it's a creature, it can't be regenerated this turn, and if it would die this turn, exile it instead.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
Name:Disintegrate
|
Name:Disintegrate
|
||||||
ManaCost:X R
|
ManaCost:X R
|
||||||
Types:Sorcery
|
Types:Sorcery
|
||||||
A:SP$ DealDamage | Cost$ X R | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | NumDmg$ X | SubAbility$ DB | ReplaceDyingDefined$ Targeted.Creature | SpellDescription$ CARDNAME deals X damage to any target. That creature can't be regenerated this turn. If the creature would die this turn, exile it instead.
|
A:SP$ DealDamage | Cost$ X R | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | NumDmg$ X | SubAbility$ DB | ReplaceDyingDefined$ ThisTargetedCard.Creature | SpellDescription$ CARDNAME deals X damage to any target. That creature can't be regenerated this turn. If the creature would die this turn, exile it instead.
|
||||||
SVar:DB:DB$ Pump | KW$ HIDDEN CARDNAME can't be regenerated. | Defined$ Targeted.Creature
|
SVar:DB:DB$ Pump | KW$ HIDDEN CARDNAME can't be regenerated. | Defined$ ParentTarget | ConditionDefined$ ParentTarget | ConditionPresent$ Creature
|
||||||
SVar:X:Count$xPaid
|
SVar:X:Count$xPaid
|
||||||
Oracle:Disintegrate deals X damage to any target. If it's a creature, it can't be regenerated this turn, and if it would die this turn, exile it instead.
|
Oracle:Disintegrate deals X damage to any target. If it's a creature, it can't be regenerated this turn, and if it would die this turn, exile it instead.
|
||||||
|
|||||||
@@ -2,6 +2,6 @@ Name:Scorching Lava
|
|||||||
ManaCost:1 R
|
ManaCost:1 R
|
||||||
Types:Instant
|
Types:Instant
|
||||||
K:Kicker:R
|
K:Kicker:R
|
||||||
A:SP$ DealDamage | Cost$ 1 R | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | NumDmg$ 2 | ReplaceDyingDefined$ Targeted.Creature | ReplaceDyingCondition$ Kicked | SubAbility$ KickingLava | SpellDescription$ CARDNAME deals 2 damage to any target. If this spell was kicked, that creature can't be regenerated this turn and if it would die this turn, exile it instead.
|
A:SP$ DealDamage | Cost$ 1 R | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | NumDmg$ 2 | ReplaceDyingDefined$ ThisTargetedCard.Creature | ReplaceDyingCondition$ Kicked | SubAbility$ KickingLava | SpellDescription$ CARDNAME deals 2 damage to any target. If this spell was kicked, that creature can't be regenerated this turn and if it would die this turn, exile it instead.
|
||||||
SVar:KickingLava:DB$ Pump | KW$ HIDDEN CARDNAME can't be regenerated. | Defined$ Targeted.Creature | Condition$ Kicked
|
SVar:KickingLava:DB$ Pump | KW$ HIDDEN CARDNAME can't be regenerated. | Defined$ ParentTarget | ConditionDefined$ ParentTarget | ConditionPresent$ Creature | Condition$ Kicked
|
||||||
Oracle:Kicker {R} (You may pay an additional {R} as you cast this spell.)\nScorching Lava deals 2 damage to any target. If this spell was kicked, that creature can't be regenerated this turn and if it would die this turn, exile it instead.
|
Oracle:Kicker {R} (You may pay an additional {R} as you cast this spell.)\nScorching Lava deals 2 damage to any target. If this spell was kicked, that creature can't be regenerated this turn and if it would die this turn, exile it instead.
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
Name:Tamiyo, Compleated Sage
|
||||||
|
ManaCost:2 G PGU U
|
||||||
|
Types:Legendary Planeswalker Tamiyo
|
||||||
|
Loyalty:5
|
||||||
|
K:Compleated
|
||||||
|
A:AB$ Tap | Cost$ AddCounter<1/LOYALTY> | Planeswalker$ True | ValidTgts$ Artifact,Creature | TgtPrompt$ Select up to one target artifact or creature | TargetMin$ 0 | TargetMax$ 1 | SubAbility$ DBPump | SpellDescription$ Tap up to one target artifact or creature. It doesn't untap during its controller's next untap step.
|
||||||
|
SVar:DBPump:DB$ Pump | Defined$ Targeted | KW$ HIDDEN This card doesn't untap during your next untap step. | Duration$ Permanent
|
||||||
|
A:AB$ ChangeZone | Cost$ SubCounter<X/LOYALTY> | Planeswalker$ True | ValidTgts$ Permanent.nonLand+cmcEQX | TgtPrompt$ Select target nonland permanent card with mana value X | AILogic$ BestCard | Origin$ Graveyard | Destination$ Exile | SubAbility$ DBCopy | SpellDescription$ Exile target nonland permanent card with mana value X from your graveyard.
|
||||||
|
SVar:DBCopy:DB$ CopyPermanent | Defined$ Targeted | SpellDescription$ Create a token that's a copy of that card.
|
||||||
|
SVar:X:Count$xPaid
|
||||||
|
A:AB$ Token | Cost$ SubCounter<7/LOYALTY> | Planeswalker$ True | Ultimate$ True | TokenScript$ tamiyos_notebook | SpellDescription$ Create Tamiyo's Notebook, a legendary colorless artifact token with "Spells you cast cost {2} less to cast" and "{T}: Draw a card."
|
||||||
|
DeckHas:Ability$Token & Type$Artifact
|
||||||
|
Oracle:Compleated ({PGU} can be paid with {G}, {U}, or 2 life. If life was paid, this planeswalker enters with two fewer loyalty counters.)\n[+1]: Tap up to one target artifact or creature. It doesn't untap during its controller's next untap step.\n[−X]: Exile target nonland permanent card with mana value X from your graveyard. Create a token that's a copy of that card.\n[−7]: Create Tamiyo's Notebook, a legendary colorless artifact token with "Spells you cast cost {2} less to cast" and "{T}: Draw a card."
|
||||||
@@ -46,6 +46,7 @@ public class InputPayManaOfCostPayment extends InputPayMana {
|
|||||||
if (player == selected) {
|
if (player == selected) {
|
||||||
if (player.canPayLife(this.phyLifeToLose + 2, this.effect, saPaidFor)) {
|
if (player.canPayLife(this.phyLifeToLose + 2, this.effect, saPaidFor)) {
|
||||||
if (manaCost.payPhyrexian()) {
|
if (manaCost.payPhyrexian()) {
|
||||||
|
saPaidFor.setSpendPhyrexianMana(true);
|
||||||
this.phyLifeToLose += 2;
|
this.phyLifeToLose += 2;
|
||||||
} else {
|
} else {
|
||||||
if (player.hasKeyword("PayLifeInsteadOf:B") && manaCost.hasAnyKind(ManaAtom.BLACK)) {
|
if (player.hasKeyword("PayLifeInsteadOf:B") && manaCost.hasAnyKind(ManaAtom.BLACK)) {
|
||||||
|
|||||||
@@ -217,9 +217,9 @@ public class HumanPlaySpellAbility {
|
|||||||
fromZone.add(oldCard, zonePosition >= 0 ? Integer.valueOf(zonePosition) : null);
|
fromZone.add(oldCard, zonePosition >= 0 ? Integer.valueOf(zonePosition) : null);
|
||||||
ability.setHostCard(oldCard);
|
ability.setHostCard(oldCard);
|
||||||
ability.setXManaCostPaid(null);
|
ability.setXManaCostPaid(null);
|
||||||
|
ability.setSpendPhyrexianMana(false);
|
||||||
if (ability.hasParam("Announce")) {
|
if (ability.hasParam("Announce")) {
|
||||||
final String announce = ability.getParam("Announce");
|
for (final String aVar : ability.getParam("Announce").split(",")) {
|
||||||
for (final String aVar : announce.split(",")) {
|
|
||||||
final String varName = aVar.trim();
|
final String varName = aVar.trim();
|
||||||
if (!varName.equals("X")) {
|
if (!varName.equals("X")) {
|
||||||
ability.setSVar(varName, "0");
|
ability.setSVar(varName, "0");
|
||||||
|
|||||||
Reference in New Issue
Block a user