mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-17 11:18:01 +00:00
Merge branch 'master' of https://git.cardforge.org/core-developers/forge.git
This commit is contained in:
@@ -181,20 +181,6 @@ public class PlayerControllerAi extends PlayerController {
|
||||
return selecteds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends GameEntity> List<T> chooseFromTwoListsForEffect(FCollectionView<T> optionList1, FCollectionView<T> optionList2,
|
||||
boolean optional, DelayedReveal delayedReveal, SpellAbility sa, String title, Player targetedPlayer) {
|
||||
if (delayedReveal != null) {
|
||||
reveal(delayedReveal.getCards(), delayedReveal.getZone(), delayedReveal.getOwner(), delayedReveal.getMessagePrefix());
|
||||
}
|
||||
T selected1 = chooseSingleEntityForEffect(optionList1, null, sa, title, optional, targetedPlayer);
|
||||
T selected2 = chooseSingleEntityForEffect(optionList2, null, sa, title, optional || selected1!=null, targetedPlayer);
|
||||
List<T> selecteds = new ArrayList<T>();
|
||||
if ( selected1 != null ) { selecteds.add(selected1); }
|
||||
if ( selected2 != null ) { selecteds.add(selected2); }
|
||||
return selecteds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpellAbility chooseSingleSpellForEffect(java.util.List<SpellAbility> spells, SpellAbility sa, String title,
|
||||
Map<String, Object> params) {
|
||||
@@ -1275,4 +1261,17 @@ public class PlayerControllerAi extends PlayerController {
|
||||
|
||||
return chosenAmount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CardCollection chooseCardsForEffectMultiple(Map<String, CardCollection> validMap, SpellAbility sa, String title) {
|
||||
CardCollection choices = new CardCollection();
|
||||
|
||||
for (String mapKey: validMap.keySet()) {
|
||||
CardCollection cc = validMap.get(mapKey);
|
||||
cc.removeAll(choices);
|
||||
choices.add(ComputerUtilCard.getBestAI(cc)); // TODO: should the AI limit itself here with the max number of cards in hand?
|
||||
}
|
||||
|
||||
return choices;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,6 +62,7 @@ public enum SpellApiToAi {
|
||||
.put(ApiType.Destroy, DestroyAi.class)
|
||||
.put(ApiType.DestroyAll, DestroyAllAi.class)
|
||||
.put(ApiType.Dig, DigAi.class)
|
||||
.put(ApiType.DigMultiple, DigMultipleAi.class)
|
||||
.put(ApiType.DigUntil, DigUntilAi.class)
|
||||
.put(ApiType.Discard, DiscardAi.class)
|
||||
.put(ApiType.DrainMana, DrainManaAi.class)
|
||||
|
||||
101
forge-ai/src/main/java/forge/ai/ability/DigMultipleAi.java
Normal file
101
forge-ai/src/main/java/forge/ai/ability/DigMultipleAi.java
Normal file
@@ -0,0 +1,101 @@
|
||||
package forge.ai.ability;
|
||||
|
||||
import forge.ai.ComputerUtil;
|
||||
import forge.ai.SpellAbilityAi;
|
||||
import forge.game.Game;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.player.PlayerActionConfirmMode;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.zone.ZoneType;
|
||||
|
||||
|
||||
public class DigMultipleAi extends SpellAbilityAi {
|
||||
/* (non-Javadoc)
|
||||
* @see forge.card.abilityfactory.SpellAiLogic#canPlayAI(forge.game.player.Player, java.util.Map, forge.card.spellability.SpellAbility)
|
||||
*/
|
||||
@Override
|
||||
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
||||
final Game game = ai.getGame();
|
||||
Player opp = ai.getWeakestOpponent();
|
||||
final Card host = sa.getHostCard();
|
||||
Player libraryOwner = ai;
|
||||
|
||||
if (sa.usesTargeting()) {
|
||||
sa.resetTargets();
|
||||
if (!opp.canBeTargetedBy(sa)) {
|
||||
return false;
|
||||
} else {
|
||||
sa.getTargets().add(opp);
|
||||
}
|
||||
libraryOwner = opp;
|
||||
}
|
||||
|
||||
// return false if nothing to dig into
|
||||
if (libraryOwner.getCardsIn(ZoneType.Library).isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ("Never".equals(sa.getParam("AILogic"))) {
|
||||
return false;
|
||||
} else if ("AtOppEndOfTurn".equals(sa.getParam("AILogic"))) {
|
||||
if (!(game.getPhaseHandler().getNextTurn() == ai && game.getPhaseHandler().is(PhaseType.END_OF_TURN))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// don't deck yourself
|
||||
if (sa.hasParam("DestinationZone2") && !"Library".equals(sa.getParam("DestinationZone2"))) {
|
||||
int numToDig = AbilityUtils.calculateAmount(host, sa.getParam("DigNum"), sa);
|
||||
if (libraryOwner == ai && ai.getCardsIn(ZoneType.Library).size() <= numToDig + 2) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Don't use draw abilities before main 2 if possible
|
||||
if (game.getPhaseHandler().getPhase().isBefore(PhaseType.MAIN2) && !sa.hasParam("ActivationPhases")
|
||||
&& !sa.hasParam("DestinationZone") && !ComputerUtil.castSpellInMain1(ai, sa)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (SpellAbilityAi.playReusable(ai, sa)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((!game.getPhaseHandler().getNextTurn().equals(ai)
|
||||
|| game.getPhaseHandler().getPhase().isBefore(PhaseType.END_OF_TURN))
|
||||
&& !sa.hasParam("PlayerTurn") && !SpellAbilityAi.isSorcerySpeed(sa)
|
||||
&& (ai.getCardsIn(ZoneType.Hand).size() > 1 || game.getPhaseHandler().getPhase().isBefore(PhaseType.DRAW))
|
||||
&& !ComputerUtil.activateForCost(sa, ai)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !ComputerUtil.preventRunAwayActivations(sa);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
||||
final Player opp = ai.getWeakestOpponent();
|
||||
if (sa.usesTargeting()) {
|
||||
sa.resetTargets();
|
||||
if (mandatory && sa.canTarget(opp)) {
|
||||
sa.getTargets().add(opp);
|
||||
} else if (mandatory && sa.canTarget(ai)) {
|
||||
sa.getTargets().add(ai);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.card.ability.SpellAbilityAi#confirmAction(forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +60,7 @@ public enum ApiType {
|
||||
Destroy (DestroyEffect.class),
|
||||
DestroyAll (DestroyAllEffect.class),
|
||||
Dig (DigEffect.class),
|
||||
DigMultiple (DigMultipleEffect.class),
|
||||
DigUntil (DigUntilEffect.class),
|
||||
Discard (DiscardEffect.class),
|
||||
DrainMana (DrainManaEffect.class),
|
||||
|
||||
@@ -66,8 +66,6 @@ public class DigEffect extends SpellAbilityEffect {
|
||||
int destZone1ChangeNum = 1;
|
||||
final boolean mitosis = sa.hasParam("Mitosis");
|
||||
String changeValid = sa.hasParam("ChangeValid") ? sa.getParam("ChangeValid") : "";
|
||||
//andOrValid is for cards with "creature card and/or a land card"
|
||||
String andOrValid = sa.hasParam("AndOrValid") ? sa.getParam("AndOrValid") : "";
|
||||
final boolean anyNumber = sa.hasParam("AnyNumber");
|
||||
|
||||
final int libraryPosition2 = sa.hasParam("LibraryPosition2") ? Integer.parseInt(sa.getParam("LibraryPosition2")) : -1;
|
||||
@@ -173,28 +171,18 @@ public class DigEffect extends SpellAbilityEffect {
|
||||
|
||||
if (!noMove) {
|
||||
CardCollection movedCards;
|
||||
CardCollection andOrCards;
|
||||
for (final Card c : top) {
|
||||
rest.add(c);
|
||||
}
|
||||
CardCollection valid;
|
||||
if (mitosis) {
|
||||
valid = sharesNameWithCardOnBattlefield(game, top);
|
||||
andOrCards = new CardCollection();
|
||||
}
|
||||
else if (!changeValid.isEmpty()) {
|
||||
if (changeValid.contains("ChosenType")) {
|
||||
changeValid = changeValid.replace("ChosenType", host.getChosenType());
|
||||
}
|
||||
valid = CardLists.getValidCards(top, changeValid.split(","), host.getController(), host, sa);
|
||||
if (!andOrValid.equals("")) {
|
||||
andOrCards = CardLists.getValidCards(top, andOrValid.split(","), host.getController(), host, sa);
|
||||
andOrCards.removeAll((Collection<?>)valid);
|
||||
valid.addAll(andOrCards); //pfps need to add andOr cards to valid to have set of all valid cards set up
|
||||
}
|
||||
else {
|
||||
andOrCards = new CardCollection();
|
||||
}
|
||||
}
|
||||
else {
|
||||
// If all the cards are valid choices, no need for a separate reveal dialog to the chooser. pfps??
|
||||
@@ -202,7 +190,6 @@ public class DigEffect extends SpellAbilityEffect {
|
||||
delayedReveal = null;
|
||||
}
|
||||
valid = top;
|
||||
andOrCards = new CardCollection();
|
||||
}
|
||||
|
||||
if (forceRevealToController) {
|
||||
@@ -282,16 +269,13 @@ public class DigEffect extends SpellAbilityEffect {
|
||||
chooser.getController().tempShowCards(top);
|
||||
}
|
||||
List<Card> chosen = new ArrayList<Card>();
|
||||
if (!andOrValid.equals("")) {
|
||||
valid.removeAll(andOrCards); //pfps remove andOr cards to get two two choices set up correctly
|
||||
chosen = chooser.getController().chooseFromTwoListsForEffect(valid, andOrCards, optional, delayedReveal, sa, prompt, p);
|
||||
} else {
|
||||
int max = anyNumber ? valid.size() : Math.min(valid.size(),destZone1ChangeNum);
|
||||
int min = (anyNumber || optional) ? 0 : max;
|
||||
if ( max > 0 ) { // if max is 0 don't make a choice
|
||||
chosen = chooser.getController().chooseEntitiesForEffect(valid, min, max, delayedReveal, sa, prompt, p);
|
||||
}
|
||||
|
||||
int max = anyNumber ? valid.size() : Math.min(valid.size(),destZone1ChangeNum);
|
||||
int min = (anyNumber || optional) ? 0 : max;
|
||||
if ( max > 0 ) { // if max is 0 don't make a choice
|
||||
chosen = chooser.getController().chooseEntitiesForEffect(valid, min, max, delayedReveal, sa, prompt, p);
|
||||
}
|
||||
|
||||
chooser.getController().endTempShowCards();
|
||||
movedCards.addAll(chosen);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
package forge.game.ability.effects;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import forge.game.Game;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.card.CardZoneTable;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.zone.PlayerZone;
|
||||
import forge.game.zone.ZoneType;
|
||||
|
||||
public class DigMultipleEffect extends SpellAbilityEffect {
|
||||
|
||||
@Override
|
||||
public void resolve(SpellAbility sa) {
|
||||
// TODO Auto-generated method stub
|
||||
final Card host = sa.getHostCard();
|
||||
final Player player = sa.getActivatingPlayer();
|
||||
final Game game = player.getGame();
|
||||
Player chooser = player;
|
||||
int numToDig = AbilityUtils.calculateAmount(host, sa.getParam("DigNum"), sa);
|
||||
|
||||
final ZoneType srcZone = sa.hasParam("SourceZone") ? ZoneType.smartValueOf(sa.getParam("SourceZone")) : ZoneType.Library;
|
||||
|
||||
final ZoneType destZone1 = sa.hasParam("DestinationZone") ? ZoneType.smartValueOf(sa.getParam("DestinationZone")) : ZoneType.Hand;
|
||||
final ZoneType destZone2 = sa.hasParam("DestinationZone2") ? ZoneType.smartValueOf(sa.getParam("DestinationZone2")) : ZoneType.Library;
|
||||
int libraryPosition = sa.hasParam("LibraryPosition") ? Integer.parseInt(sa.getParam("LibraryPosition")) : -1;
|
||||
final int libraryPosition2 = sa.hasParam("LibraryPosition2") ? Integer.parseInt(sa.getParam("LibraryPosition2")) : -1;
|
||||
|
||||
String changeValid = sa.hasParam("ChangeValid") ? sa.getParam("ChangeValid") : "";
|
||||
|
||||
CardZoneTable table = new CardZoneTable();
|
||||
for (final Player p : getTargetPlayers(sa)) {
|
||||
if (sa.usesTargeting() && !p.canBeTargetedBy(sa)) {
|
||||
continue;
|
||||
}
|
||||
final CardCollection top = new CardCollection();
|
||||
final CardCollection rest = new CardCollection();
|
||||
final PlayerZone sourceZone = p.getZone(srcZone);
|
||||
|
||||
numToDig = Math.min(numToDig, sourceZone.size());
|
||||
for (int i = 0; i < numToDig; i++) {
|
||||
top.add(sourceZone.get(i));
|
||||
}
|
||||
|
||||
if (top.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
rest.addAll(top);
|
||||
|
||||
if (sa.hasParam("Reveal")) {
|
||||
game.getAction().reveal(top, p, false);
|
||||
} else {
|
||||
// reveal cards first
|
||||
game.getAction().revealTo(top, player);
|
||||
}
|
||||
|
||||
Map<String, CardCollection> validMap = Maps.newHashMap();
|
||||
|
||||
for (final String valid : changeValid.split(",")) {
|
||||
CardCollection list = CardLists.getValidCards(top, valid, host.getController(), host, sa);
|
||||
if (!list.isEmpty()) {
|
||||
validMap.put(valid, list);
|
||||
}
|
||||
}
|
||||
|
||||
if (validMap.isEmpty()) {
|
||||
chooser.getController().notifyOfValue(sa, null, "No valid cards");
|
||||
continue;
|
||||
}
|
||||
|
||||
CardCollection chosen = chooser.getController().chooseCardsForEffectMultiple(validMap, sa, "Choose cards");
|
||||
|
||||
if (!chosen.isEmpty()) {
|
||||
game.getAction().reveal(chosen, chooser, true,
|
||||
chooser + " picked " + (chosen.size() == 1 ? "this card" : "these cards") + " from ");
|
||||
}
|
||||
|
||||
for (Card c : chosen) {
|
||||
final ZoneType origin = c.getZone().getZoneType();
|
||||
final PlayerZone zone = c.getOwner().getZone(destZone1);
|
||||
|
||||
if (zone.is(ZoneType.Library) || zone.is(ZoneType.PlanarDeck) || zone.is(ZoneType.SchemeDeck)) {
|
||||
if (libraryPosition == -1 || libraryPosition > zone.size()) {
|
||||
libraryPosition = zone.size();
|
||||
}
|
||||
c = game.getAction().moveTo(zone, c, libraryPosition, sa);
|
||||
}
|
||||
else {
|
||||
c = game.getAction().moveTo(zone, c, sa);
|
||||
if (destZone1.equals(ZoneType.Battlefield)) {
|
||||
if (sa.hasParam("Tapped")) {
|
||||
c.setTapped(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!origin.equals(c.getZone().getZoneType())) {
|
||||
table.put(origin, c.getZone().getZoneType(), c);
|
||||
}
|
||||
|
||||
if (sa.hasParam("ExileFaceDown")) {
|
||||
c.turnFaceDown(true);
|
||||
}
|
||||
if (sa.hasParam("Imprint")) {
|
||||
host.addImprintedCard(c);
|
||||
}
|
||||
if (sa.hasParam("ForgetOtherRemembered")) {
|
||||
host.clearRemembered();
|
||||
}
|
||||
if (sa.hasParam("RememberChanged")) {
|
||||
host.addRemembered(c);
|
||||
}
|
||||
rest.remove(c);
|
||||
}
|
||||
|
||||
// now, move the rest to destZone2
|
||||
if (destZone2 == ZoneType.Library || destZone2 == ZoneType.PlanarDeck || destZone2 == ZoneType.SchemeDeck
|
||||
|| destZone2 == ZoneType.Graveyard) {
|
||||
CardCollection afterOrder = rest;
|
||||
if (sa.hasParam("RestRandomOrder")) {
|
||||
CardLists.shuffle(afterOrder);
|
||||
}
|
||||
if (libraryPosition2 != -1) {
|
||||
// Closest to top
|
||||
Collections.reverse(afterOrder);
|
||||
}
|
||||
for (final Card c : afterOrder) {
|
||||
final ZoneType origin = c.getZone().getZoneType();
|
||||
Card m;
|
||||
if (destZone2 == ZoneType.Library) {
|
||||
m = game.getAction().moveToLibrary(c, libraryPosition2, sa);
|
||||
}
|
||||
else {
|
||||
m = game.getAction().moveToVariantDeck(c, destZone2, libraryPosition2, sa);
|
||||
}
|
||||
if (m != null && !origin.equals(m.getZone().getZoneType())) {
|
||||
table.put(origin, m.getZone().getZoneType(), m);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// just move them randomly
|
||||
for (int i = 0; i < rest.size(); i++) {
|
||||
Card c = rest.get(i);
|
||||
final ZoneType origin = c.getZone().getZoneType();
|
||||
final PlayerZone toZone = c.getOwner().getZone(destZone2);
|
||||
c = game.getAction().moveTo(toZone, c, sa);
|
||||
if (!origin.equals(c.getZone().getZoneType())) {
|
||||
table.put(origin, c.getZone().getZoneType(), c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//table trigger there
|
||||
table.triggerChangesZoneAll(game);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -115,7 +115,6 @@ public abstract class PlayerController {
|
||||
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);
|
||||
public abstract <T extends GameEntity> List<T> chooseFromTwoListsForEffect(FCollectionView<T> optionList1, FCollectionView<T> optionList2, boolean optional, DelayedReveal delayedReveal, SpellAbility sa, String title, Player relatedPlayer);
|
||||
|
||||
public abstract boolean confirmAction(SpellAbility sa, PlayerActionConfirmMode mode, String message);
|
||||
public abstract boolean confirmBidAction(SpellAbility sa, PlayerActionConfirmMode bidlife, String string, int bid, Player winner);
|
||||
@@ -273,4 +272,7 @@ public abstract class PlayerController {
|
||||
public abstract List<OptionalCostValue> chooseOptionalCosts(SpellAbility choosen, List<OptionalCostValue> optionalCostValues);
|
||||
|
||||
public abstract boolean confirmMulliganScry(final Player p);
|
||||
|
||||
public abstract CardCollection chooseCardsForEffectMultiple(Map<String, CardCollection> validMap,
|
||||
SpellAbility sa, String title);
|
||||
}
|
||||
|
||||
@@ -180,12 +180,6 @@ public class PlayerControllerForTests extends PlayerController {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends GameEntity> List<T> chooseFromTwoListsForEffect(FCollectionView<T> optionList1, FCollectionView<T> optionList2, boolean optional, DelayedReveal delayedReveal, SpellAbility sa, String title, Player targetedPlayer) {
|
||||
// this isn't used
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean confirmAction(SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
||||
return true;
|
||||
@@ -703,4 +697,9 @@ public class PlayerControllerForTests extends PlayerController {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CardCollection chooseCardsForEffectMultiple(Map<String, CardCollection> validMap, SpellAbility sa, String title) {
|
||||
// TODO Auto-generated method stub
|
||||
return new CardCollection();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Name:Benefaction of Rhonas
|
||||
ManaCost:2 G
|
||||
Types:Sorcery
|
||||
A:SP$ Dig | Cost$ 2 G | DigNum$ 5 | Reveal$ True | ChangeNum$ 2 | ChangeValid$ Creature | AndOrValid$ Enchantment | DestinationZone2$ Graveyard | Optional$ True | SpellDescription$ Reveal the top five cards of your library. You may put a creature card and/or an enchantment card from among them into your hand. Put the rest into your graveyard.
|
||||
A:SP$ DigMultiple | Cost$ 2 G | DigNum$ 5 | Reveal$ True | ChangeValid$ Creature,Enchantment | DestinationZone2$ Graveyard | SpellDescription$ Reveal the top five cards of your library. You may put a creature card and/or an enchantment card from among them into your hand. Put the rest into your graveyard.
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/benefaction_of_rhonas.jpg
|
||||
Oracle:Reveal the top five cards of your library. You may put a creature card and/or an enchantment card from among them into your hand. Put the rest into your graveyard.
|
||||
Oracle:Reveal the top five cards of your library. You may put a creature card and/or an enchantment card from among them into your hand. Put the rest into your graveyard.
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
Name:Domri's Ambush
|
||||
ManaCost:R G
|
||||
Types:Sorcery
|
||||
A:SP$ PutCounter | Cost$ R G | ValidTgts$ Creature.YouCtrl | CounterType$ P1P1 | TgtPrompt$ Select target creature you control to put a +1/+1 counter | SubAbility$ DBDamage | AILogic$ Fight | SpellDescription$ Put a +1/+1 counter on target creature you control. Then that creature deals damage equal to its power to target creature or planeswalker you don't control.
|
||||
SVar:DBDamage:DB$ DealDamage | ValidTgts$ Creature.YouDontCtrl,Planeswalker.YouDontCtrl | TgtPrompt$ Select target creature or planeswalker you don't control | AILogic$ PowerDmg | NumDmg$ X | References$ X
|
||||
A:SP$ PutCounter | Cost$ R G | ValidTgts$ Creature.YouCtrl | CounterType$ P1P1 | TgtPrompt$ Select target creature you control to put a +1/+1 counter | SubAbility$ DBDamage | AILogic$ PowerDmg | SpellDescription$ Put a +1/+1 counter on target creature you control. Then that creature deals damage equal to its power to target creature or planeswalker you don't control.
|
||||
SVar:DBDamage:DB$ DealDamage | ValidTgts$ Creature.YouDontCtrl,Planeswalker.YouDontCtrl | TgtPrompt$ Select target creature or planeswalker you don't control | AILogic$ PowerDmg | NumDmg$ X | References$ X | DamageSource$ ParentTarget
|
||||
SVar:X:ParentTargeted$CardPower
|
||||
DeckHas:Ability$Counters
|
||||
Oracle:Put a +1/+1 counter on target creature you control. Then that creature deals damage equal to its power to target creature or planeswalker you don't control.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Name:Gift of the Gargantuan
|
||||
ManaCost:2 G
|
||||
Types:Sorcery
|
||||
A:SP$ Dig | Cost$ 2 G | DigNum$ 4 | ChangeValid$ Creature | AndOrValid$ Land | ChangeNum$ 2 | Optional$ True | SpellDescription$ Look at the top four cards of your library. You may reveal a creature card and/or a land card from among them and put the revealed cards into your hand. Put the rest on the bottom of your library in any order.
|
||||
A:SP$ DigMultiple | Cost$ 2 G | DigNum$ 4 | ChangeValid$ Creature,Land | SpellDescription$ Look at the top four cards of your library. You may reveal a creature card and/or a land card from among them and put the revealed cards into your hand. Put the rest on the bottom of your library in any order.
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/gift_of_the_gargantuan.jpg
|
||||
Oracle:Look at the top four cards of your library. You may reveal a creature card and/or a land card from among them and put the revealed cards into your hand. Put the rest on the bottom of your library in any order.
|
||||
|
||||
9
forge-gui/res/cardsfolder/k/kaalia_zenith_seeker.txt
Normal file
9
forge-gui/res/cardsfolder/k/kaalia_zenith_seeker.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
Name:Kaalia, Zenith Seeker
|
||||
ManaCost:R W B
|
||||
Types:Legendary Creature Human Cleric
|
||||
PT:3/3
|
||||
K:Flying
|
||||
K:Vigilance
|
||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigDigMulti | TriggerDescription$ When CARDNAME enters the battlefield, look at the top six cards of your library. You may reveal an Angel card, a Demon card, and/or a Dragon card from among them and put them into your hand. Put the rest on the bottom of your library in a random order.
|
||||
SVar:TrigDigMulti:DB$ DigMultiple | DigNum$ 6 | ChangeValid$ Card.Angel,Card.Demon,Card.Dragon | SourceZone$ Library | DestinationZone$ Hand | DestinationZone2$ Library | LibraryPosition$ -1 | RestRandomOrder$ True
|
||||
Oracle:Flying, vigilance\nWhen Kaalia, Zenith Seeker enters the battlefield, look at the top six cards of your library. You may reveal an Angel card, a Demon card, and/or a Dragon card from among them and put them into your hand. Put the rest on the bottom of your library in a random order.
|
||||
@@ -4,7 +4,7 @@ Types:Legendary Planeswalker Kiora
|
||||
Loyalty:4
|
||||
A:AB$ Untap | Cost$ AddCounter<1/LOYALTY> | ValidTgts$ Creature | TgtPrompt$ Choose target creature | TargetMin$ 0 | TargetMax$ 1 | Planeswalker$ True | SubAbility$ DBUntap | SpellDescription$ Untap up to one target creature and up to one target land.
|
||||
SVar:DBUntap:DB$ Untap | ValidTgts$ Land | TgtPrompt$ Choose target land | TargetMin$ 0 | TargetMax$ 1
|
||||
A:AB$ Dig | Cost$ SubCounter<2/LOYALTY> | Planeswalker$ True | DigNum$ 4 | Reveal$ True | ChangeNum$ 2 | ChangeValid$ Creature | AndOrValid$ Land | DestinationZone2$ Graveyard | Optional$ True | SpellDescription$ Reveal the top four cards of your library. You may put a creature card and/or a land card from among them into your hand. Put the rest into your graveyard.
|
||||
A:AB$ DigMultiple | Cost$ SubCounter<2/LOYALTY> | Planeswalker$ True | DigNum$ 4 | Reveal$ True | ChangeValid$ Creature,Land | DestinationZone2$ Graveyard | Optional$ True | SpellDescription$ Reveal the top four cards of your library. You may put a creature card and/or a land card from among them into your hand. Put the rest into your graveyard.
|
||||
A:AB$ Effect | Cost$ SubCounter<8/LOYALTY> | Planeswalker$ True | Ultimate$ True | Name$ Emblem - Kiora, Master of the Depths | Image$ emblem_kiora_master_of_the_depths | Triggers$ TrigFight | SVars$ DBFight | Duration$ Permanent | AILogic$ Always | SubAbility$ DBToken | SpellDescription$ You get an emblem with "Whenever a creature enters the battlefield under your control, you may have it fight target creature." Then create three 8/8 blue Octopus creature tokens.
|
||||
SVar:TrigFight:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Creature.YouCtrl | Execute$ DBFight | OptionalDecider$ You | TriggerZones$ Command | TriggerDescription$ Whenever a creature enters the battlefield under your control, you may have it fight target creature.
|
||||
SVar:DBFight:DB$ Fight | Defined$ TriggeredCardLKICopy | ValidTgts$ Creature | TgtPrompt$ Choose target creature
|
||||
|
||||
@@ -1,152 +0,0 @@
|
||||
package forge.match.input;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import forge.game.GameEntity;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardView;
|
||||
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.player.PlayerControllerHuman;
|
||||
import forge.util.collect.FCollection;
|
||||
import forge.util.collect.FCollectionView;
|
||||
import forge.util.ITriggerEvent;
|
||||
import forge.player.PlayerZoneUpdate;
|
||||
import forge.player.PlayerZoneUpdates;
|
||||
import forge.game.zone.Zone;
|
||||
import forge.FThreads;
|
||||
|
||||
public class InputSelectFromTwoLists<T extends GameEntity> extends InputSelectManyBase<T> {
|
||||
private final FCollectionView<T> valid1, valid2;
|
||||
private final FCollection<T> validBoth;
|
||||
private FCollectionView<T> validChoices;
|
||||
|
||||
protected final FCollection<T> selected = new FCollection<T>();
|
||||
protected final PlayerZoneUpdates zonesToUpdate = new PlayerZoneUpdates();
|
||||
protected Iterable<PlayerZoneUpdate> zonesShown; // want to hide these zones when input done
|
||||
|
||||
public InputSelectFromTwoLists(final PlayerControllerHuman controller, final boolean optional,
|
||||
final FCollectionView<T> list1, final FCollectionView<T> list2, final SpellAbility sa0) {
|
||||
super(controller, optional?0:1, 2, sa0);
|
||||
valid1 = list1;
|
||||
valid2 = list2;
|
||||
validBoth = new FCollection<T>(valid1);
|
||||
for ( T v : valid2 ) { validBoth.add(v); }
|
||||
validChoices = validBoth;
|
||||
setSelectables();
|
||||
|
||||
for (final GameEntity c : validChoices) {
|
||||
final Zone cz = (c instanceof Card) ? ((Card) c).getZone() : null ;
|
||||
if ( cz != null ) {
|
||||
zonesToUpdate.add(new PlayerZoneUpdate(cz.getPlayer().getView(),cz.getZoneType()));
|
||||
}
|
||||
}
|
||||
FThreads.invokeInEdtNowOrLater(new Runnable() {
|
||||
@Override public void run() {
|
||||
controller.getGui().updateZones(zonesToUpdate);
|
||||
zonesShown = controller.getGui().tempShowZones(controller.getPlayer().getView(),zonesToUpdate);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setSelectables() {
|
||||
ArrayList<CardView> vCards = new ArrayList<CardView>();
|
||||
getController().getGui().clearSelectables();
|
||||
for ( T c : validChoices ) {
|
||||
if ( c instanceof Card ) {
|
||||
vCards.add(((Card)c).getView()) ;
|
||||
}
|
||||
}
|
||||
getController().getGui().setSelectables(vCards);
|
||||
}
|
||||
|
||||
private void setValid() {
|
||||
boolean selected1 = false, selected2 = false;
|
||||
for ( T s : selected ) {
|
||||
if ( valid1.contains(s) ) { selected1 = true; }
|
||||
if ( valid2.contains(s) ) { selected2 = true; }
|
||||
}
|
||||
validChoices = selected1 ? ( selected2 ? FCollection.<T>getEmpty() : valid2 ) : ( selected2 ? valid1 : validBoth );
|
||||
setSelectables();
|
||||
FThreads.invokeInEdtNowOrLater(new Runnable() {
|
||||
@Override public void run() {
|
||||
getController().getGui().updateZones(zonesToUpdate);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onCardSelected(final Card c, final List<Card> otherCardsToSelect, final ITriggerEvent triggerEvent) {
|
||||
if (!selectEntity(c)) {
|
||||
return false;
|
||||
}
|
||||
refresh();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getActivateAction(final Card card) {
|
||||
if (validChoices.contains(card)) {
|
||||
if (selected.contains(card)) {
|
||||
return "unselect card";
|
||||
}
|
||||
return "select card";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPlayerSelected(final Player p, final ITriggerEvent triggerEvent) {
|
||||
if (!selectEntity(p)) {
|
||||
return;
|
||||
}
|
||||
refresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Collection<T> getSelected() {
|
||||
return selected;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected boolean selectEntity(final GameEntity c) {
|
||||
if (!validChoices.contains(c) && !selected.contains(c)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final boolean entityWasSelected = selected.contains(c);
|
||||
if (entityWasSelected) {
|
||||
selected.remove(c);
|
||||
}
|
||||
else {
|
||||
selected.add((T)c);
|
||||
}
|
||||
setValid();
|
||||
onSelectStateChanged(c, !entityWasSelected);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// might re-define later
|
||||
@Override
|
||||
protected boolean hasEnoughTargets() { return selected.size() >= min; }
|
||||
@Override
|
||||
protected boolean hasAllTargets() { return selected.size() >= max; }
|
||||
|
||||
@Override
|
||||
protected String getMessage() {
|
||||
return max == Integer.MAX_VALUE
|
||||
? String.format(message, selected.size())
|
||||
: String.format(message, max - selected.size());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
getController().getGui().hideZones(getController().getPlayer().getView(),zonesShown);
|
||||
getController().getGui().clearSelectables();
|
||||
super.onStop();
|
||||
}
|
||||
}
|
||||
@@ -511,41 +511,6 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont
|
||||
return results;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends GameEntity> List<T> chooseFromTwoListsForEffect(final FCollectionView<T> optionList1, final FCollectionView<T> optionList2,
|
||||
boolean optional, final DelayedReveal delayedReveal, final SpellAbility sa, final String title, final Player targetedPlayer) {
|
||||
// Human is supposed to read the message and understand from it what to choose
|
||||
// useful details for debugging problems with the mass select logic
|
||||
Sentry.getContext().addExtra("Card", sa.getCardView().toString());
|
||||
Sentry.getContext().addExtra("SpellAbility", sa.toString());
|
||||
|
||||
if (delayedReveal != null) {
|
||||
tempShow(delayedReveal.getCards());
|
||||
}
|
||||
|
||||
tempShow(optionList1);
|
||||
tempShow(optionList2);
|
||||
|
||||
if (useSelectCardsInput(optionList1) && useSelectCardsInput(optionList2)) {
|
||||
final InputSelectFromTwoLists<T> input = new InputSelectFromTwoLists<T>(this, optional, optionList1, optionList2, sa);
|
||||
input.setCancelAllowed(optional);
|
||||
input.setMessage(MessageUtil.formatMessage(title, player, targetedPlayer));
|
||||
input.showAndWait();
|
||||
endTempShowCards();
|
||||
return (List<T>) input.getSelected();
|
||||
}
|
||||
|
||||
final GameEntityView result1 = getGui().chooseSingleEntityForEffect(title, GameEntityView.getEntityCollection(optionList1), null, optional);
|
||||
final GameEntityView result2 = getGui().chooseSingleEntityForEffect(title, GameEntityView.getEntityCollection(optionList2), null, (result1==null)?optional:true);
|
||||
endTempShowCards();
|
||||
List<T> results = new ArrayList<>();
|
||||
GameEntity entity1 = convertToEntity(result1);
|
||||
if (entity1!=null) { results.add((T) entity1); }
|
||||
GameEntity entity2 = convertToEntity(result2);
|
||||
if (entity2!=null) { results.add((T) entity2); }
|
||||
return results;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int chooseNumber(final SpellAbility sa, final String title, final int min, final int max) {
|
||||
if (min >= max) {
|
||||
@@ -2945,5 +2910,14 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont
|
||||
return v == null ? 0 : v.intValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CardCollection chooseCardsForEffectMultiple(Map<String, CardCollection> validMap, SpellAbility sa, String title) {
|
||||
CardCollection result = new CardCollection();
|
||||
for (Map.Entry<String, CardCollection> e : validMap.entrySet()) {
|
||||
result.addAll(chooseCardsForEffect(e.getValue(), sa, title + " " + e.getKey(), 0, 1, true));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user