mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-20 20:58:03 +00:00
AI: changed many calls to accept Collection<T> instead of List<T>
GameAction - legend rule and planeswalker rule are updated to match changes introduced with "Magic 2014 Core Set" InputSelectCardsFromList also accepts any Collection<T>, not just List<T> PlayerControllerHuman - chooseSingleCardForEffect tries to use InputSelectCardsFromList when all cards are in Battlefield or own hand
This commit is contained in:
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -14251,6 +14251,7 @@ src/main/java/forge/card/ability/ai/FlipACoinAi.java -text
|
|||||||
src/main/java/forge/card/ability/ai/FogAi.java -text
|
src/main/java/forge/card/ability/ai/FogAi.java -text
|
||||||
src/main/java/forge/card/ability/ai/GameLossAi.java -text
|
src/main/java/forge/card/ability/ai/GameLossAi.java -text
|
||||||
src/main/java/forge/card/ability/ai/GameWinAi.java -text
|
src/main/java/forge/card/ability/ai/GameWinAi.java -text
|
||||||
|
src/main/java/forge/card/ability/ai/LegendaryRuleAi.java -text
|
||||||
src/main/java/forge/card/ability/ai/LifeExchangeAi.java -text
|
src/main/java/forge/card/ability/ai/LifeExchangeAi.java -text
|
||||||
src/main/java/forge/card/ability/ai/LifeGainAi.java -text
|
src/main/java/forge/card/ability/ai/LifeGainAi.java -text
|
||||||
src/main/java/forge/card/ability/ai/LifeLoseAi.java -text
|
src/main/java/forge/card/ability/ai/LifeLoseAi.java -text
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ import forge.card.ability.ai.FlipACoinAi;
|
|||||||
import forge.card.ability.ai.FogAi;
|
import forge.card.ability.ai.FogAi;
|
||||||
import forge.card.ability.ai.GameLossAi;
|
import forge.card.ability.ai.GameLossAi;
|
||||||
import forge.card.ability.ai.GameWinAi;
|
import forge.card.ability.ai.GameWinAi;
|
||||||
|
import forge.card.ability.ai.LegendaryRuleAi;
|
||||||
import forge.card.ability.ai.LifeExchangeAi;
|
import forge.card.ability.ai.LifeExchangeAi;
|
||||||
import forge.card.ability.ai.LifeGainAi;
|
import forge.card.ability.ai.LifeGainAi;
|
||||||
import forge.card.ability.ai.LifeLoseAi;
|
import forge.card.ability.ai.LifeLoseAi;
|
||||||
@@ -220,8 +221,8 @@ public enum ApiType {
|
|||||||
WinsGame (GameWinEffect.class, GameWinAi.class),
|
WinsGame (GameWinEffect.class, GameWinAi.class),
|
||||||
|
|
||||||
|
|
||||||
|
InternalEtbReplacement(ETBReplacementEffect.class, CanPlayAsDrawbackAi.class),
|
||||||
InternalEtbReplacement(ETBReplacementEffect.class, CanPlayAsDrawbackAi.class);
|
InternalLegendaryRule(CharmEffect.class, LegendaryRuleAi.class); // Charm has empty resolve blocks, may act as a dummy
|
||||||
|
|
||||||
private final Class<? extends SpellAbilityEffect> clsEffect;
|
private final Class<? extends SpellAbilityEffect> clsEffect;
|
||||||
private final Class<? extends SpellAbilityAi> clsAi;
|
private final Class<? extends SpellAbilityAi> clsAi;
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
package forge.card.ability;
|
package forge.card.ability;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
|
|
||||||
import forge.Card;
|
import forge.Card;
|
||||||
import forge.card.spellability.AbilitySub;
|
import forge.card.spellability.AbilitySub;
|
||||||
import forge.card.spellability.SpellAbility;
|
import forge.card.spellability.SpellAbility;
|
||||||
@@ -113,9 +116,9 @@ public abstract class SpellAbilityAi extends SaTargetRountines {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Card chooseSingleCard(Player ai, SpellAbility sa, List<Card> options, boolean isOptional) {
|
public Card chooseSingleCard(Player ai, SpellAbility sa, Collection<Card> options, boolean isOptional) {
|
||||||
System.err.println("Warning: default (ie. inherited from base class) implementation of chooseSingleCard is used for " + this.getClass().getName() + ". Consider declaring an overloaded method");
|
System.err.println("Warning: default (ie. inherited from base class) implementation of chooseSingleCard is used for " + this.getClass().getName() + ". Consider declaring an overloaded method");
|
||||||
return options.get(0);
|
return Iterables.getFirst(options, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Player chooseSinglePlayer(Player ai, SpellAbility sa, List<Player> options) {
|
public Player chooseSinglePlayer(Player ai, SpellAbility sa, List<Player> options) {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package forge.card.ability.ai;
|
package forge.card.ability.ai;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -1208,7 +1209,7 @@ public class AttachAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Card chooseSingleCard(Player ai, SpellAbility sa, List<Card> options, boolean isOptional) {
|
public Card chooseSingleCard(Player ai, SpellAbility sa, Collection<Card> options, boolean isOptional) {
|
||||||
return attachToCardAIPreferences(ai, sa, true);
|
return attachToCardAIPreferences(ai, sa, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
package forge.card.ability.ai;
|
package forge.card.ability.ai;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.Collection;
|
||||||
|
|
||||||
import forge.Card;
|
import forge.Card;
|
||||||
import forge.card.ability.SpellAbilityAi;
|
import forge.card.ability.SpellAbilityAi;
|
||||||
@@ -52,7 +52,7 @@ public final class BondAi extends SpellAbilityAi {
|
|||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Card chooseSingleCard(Player ai, SpellAbility sa, List<Card> options, boolean isOptional) {
|
public Card chooseSingleCard(Player ai, SpellAbility sa, Collection<Card> options, boolean isOptional) {
|
||||||
return ComputerUtilCard.getBestCreatureAI(options);
|
return ComputerUtilCard.getBestCreatureAI(options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package forge.card.ability.ai;
|
package forge.card.ability.ai;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
@@ -1402,7 +1403,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
|||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Card chooseSingleCard(Player ai, SpellAbility sa, List<Card> options, boolean isOptional) {
|
public Card chooseSingleCard(Player ai, SpellAbility sa, Collection<Card> options, boolean isOptional) {
|
||||||
// Called when looking for creature to attach aura or equipment
|
// Called when looking for creature to attach aura or equipment
|
||||||
return ComputerUtilCard.getBestAI(options);
|
return ComputerUtilCard.getBestAI(options);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package forge.card.ability.ai;
|
package forge.card.ability.ai;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
@@ -97,7 +98,7 @@ public class ChooseCardAi extends SpellAbilityAi {
|
|||||||
* @see forge.card.ability.SpellAbilityAi#chooseSingleCard(forge.card.spellability.SpellAbility, java.util.List, boolean)
|
* @see forge.card.ability.SpellAbilityAi#chooseSingleCard(forge.card.spellability.SpellAbility, java.util.List, boolean)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Card chooseSingleCard(final Player ai, SpellAbility sa, List<Card> options, boolean isOptional) {
|
public Card chooseSingleCard(final Player ai, SpellAbility sa, Collection<Card> options, boolean isOptional) {
|
||||||
final Card host = sa.getSourceCard();
|
final Card host = sa.getSourceCard();
|
||||||
final String logic = sa.getParam("AILogic");
|
final String logic = sa.getParam("AILogic");
|
||||||
Card choice = null;
|
Card choice = null;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package forge.card.ability.ai;
|
package forge.card.ability.ai;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
@@ -126,7 +127,7 @@ public class CopyPermanentAi extends SpellAbilityAi {
|
|||||||
* @see forge.card.ability.SpellAbilityAi#chooseSingleCard(forge.game.player.Player, forge.card.spellability.SpellAbility, java.util.List, boolean)
|
* @see forge.card.ability.SpellAbilityAi#chooseSingleCard(forge.game.player.Player, forge.card.spellability.SpellAbility, java.util.List, boolean)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Card chooseSingleCard(Player ai, SpellAbility sa, List<Card> options, boolean isOptional) {
|
public Card chooseSingleCard(Player ai, SpellAbility sa, Collection<Card> options, boolean isOptional) {
|
||||||
// Select a card to attach to
|
// Select a card to attach to
|
||||||
return ComputerUtilCard.getBestAI(options);
|
return ComputerUtilCard.getBestAI(options);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
package forge.card.ability.ai;
|
package forge.card.ability.ai;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
@@ -71,7 +72,7 @@ public final class EncodeAi extends SpellAbilityAi {
|
|||||||
* @see forge.card.ability.SpellAbilityAi#chooseSingleCard(forge.game.player.Player, forge.card.spellability.SpellAbility, java.util.List, boolean)
|
* @see forge.card.ability.SpellAbilityAi#chooseSingleCard(forge.game.player.Player, forge.card.spellability.SpellAbility, java.util.List, boolean)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Card chooseSingleCard(final Player ai, SpellAbility sa, List<Card> options, boolean isOptional) {
|
public Card chooseSingleCard(final Player ai, SpellAbility sa, Collection<Card> options, boolean isOptional) {
|
||||||
Card choice = null;
|
Card choice = null;
|
||||||
// final String logic = sa.getParam("AILogic");
|
// final String logic = sa.getParam("AILogic");
|
||||||
// if (logic == null) {
|
// if (logic == null) {
|
||||||
|
|||||||
42
src/main/java/forge/card/ability/ai/LegendaryRuleAi.java
Normal file
42
src/main/java/forge/card/ability/ai/LegendaryRuleAi.java
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
package forge.card.ability.ai;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
|
|
||||||
|
import forge.Card;
|
||||||
|
import forge.card.ability.SpellAbilityAi;
|
||||||
|
import forge.card.spellability.SpellAbility;
|
||||||
|
import forge.game.player.Player;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: Write javadoc for this type.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class LegendaryRuleAi extends SpellAbilityAi {
|
||||||
|
|
||||||
|
/* (non-Javadoc)
|
||||||
|
* @see forge.card.ability.SpellAbilityAi#canPlayAI(forge.game.player.Player, forge.card.spellability.SpellAbility)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
|
||||||
|
return false; // should not get here
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Card chooseSingleCard(Player ai, SpellAbility sa, Collection<Card> options, boolean isOptional) {
|
||||||
|
// Choose a single legendary/planeswalker card to keep
|
||||||
|
Card firstOption = Iterables.getFirst(options, null);
|
||||||
|
boolean choosingFromPlanewalkers = firstOption.isPlaneswalker();
|
||||||
|
|
||||||
|
if ( choosingFromPlanewalkers ) {
|
||||||
|
// AI decision making - should AI compare counters?
|
||||||
|
} else {
|
||||||
|
// AI decision making - should AI compare damage and debuffs?
|
||||||
|
}
|
||||||
|
|
||||||
|
return firstOption;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package forge.card.ability.ai;
|
package forge.card.ability.ai;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
@@ -112,7 +113,7 @@ public class PlayAi extends SpellAbilityAi {
|
|||||||
* @see forge.card.ability.SpellAbilityAi#chooseSingleCard(forge.game.player.Player, forge.card.spellability.SpellAbility, java.util.List, boolean)
|
* @see forge.card.ability.SpellAbilityAi#chooseSingleCard(forge.game.player.Player, forge.card.spellability.SpellAbility, java.util.List, boolean)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Card chooseSingleCard(final Player ai, SpellAbility sa, List<Card> options, boolean isOptional) {
|
public Card chooseSingleCard(final Player ai, SpellAbility sa, Collection<Card> options, boolean isOptional) {
|
||||||
List<Card> tgtCards = CardLists.filter(options, new Predicate<Card>() {
|
List<Card> tgtCards = CardLists.filter(options, new Predicate<Card>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(final Card c) {
|
public boolean apply(final Card c) {
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ public class InputQueue extends Observable {
|
|||||||
input.awaitLatchRelease();
|
input.awaitLatchRelease();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setInput(InputSynchronized input) {
|
void setInput(InputSynchronized input) {
|
||||||
this.inputStack.push(input);
|
this.inputStack.push(input);
|
||||||
syncPoint();
|
syncPoint();
|
||||||
this.updateObservers();
|
this.updateObservers();
|
||||||
|
|||||||
@@ -28,9 +28,10 @@ import java.util.Map.Entry;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.collect.ArrayListMultimap;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.common.collect.Multimap;
|
||||||
|
|
||||||
import forge.Card;
|
import forge.Card;
|
||||||
import forge.CardCharacteristicName;
|
import forge.CardCharacteristicName;
|
||||||
@@ -46,6 +47,7 @@ import forge.ITargetable;
|
|||||||
import forge.card.CardType;
|
import forge.card.CardType;
|
||||||
import forge.card.TriggerReplacementBase;
|
import forge.card.TriggerReplacementBase;
|
||||||
import forge.card.ability.AbilityFactory;
|
import forge.card.ability.AbilityFactory;
|
||||||
|
import forge.card.ability.ApiType;
|
||||||
import forge.card.ability.effects.AttachEffect;
|
import forge.card.ability.effects.AttachEffect;
|
||||||
import forge.card.cardfactory.CardFactory;
|
import forge.card.cardfactory.CardFactory;
|
||||||
import forge.card.cardfactory.CardFactoryUtil;
|
import forge.card.cardfactory.CardFactoryUtil;
|
||||||
@@ -54,6 +56,7 @@ import forge.card.mana.ManaCost;
|
|||||||
import forge.card.replacement.ReplacementResult;
|
import forge.card.replacement.ReplacementResult;
|
||||||
import forge.card.spellability.Ability;
|
import forge.card.spellability.Ability;
|
||||||
import forge.card.spellability.AbilityActivated;
|
import forge.card.spellability.AbilityActivated;
|
||||||
|
import forge.card.spellability.AbilitySub;
|
||||||
import forge.card.spellability.SpellAbility;
|
import forge.card.spellability.SpellAbility;
|
||||||
import forge.card.spellability.TargetRestrictions;
|
import forge.card.spellability.TargetRestrictions;
|
||||||
import forge.card.staticability.StaticAbility;
|
import forge.card.staticability.StaticAbility;
|
||||||
@@ -902,13 +905,15 @@ public class GameAction {
|
|||||||
game.getStack().chooseOrderOfSimultaneousStackEntryAll();
|
game.getStack().chooseOrderOfSimultaneousStackEntryAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.handleLegendRule()) {
|
for(Player p : game.getPlayers() ) {
|
||||||
|
if (this.handleLegendRule(p)) {
|
||||||
checkAgain = true;
|
checkAgain = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.handlePlaneswalkerRule()) {
|
if (this.handlePlaneswalkerRule(p)) {
|
||||||
checkAgain = true;
|
checkAgain = true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!checkAgain) {
|
if (!checkAgain) {
|
||||||
break; // do not continue the loop
|
break; // do not continue the loop
|
||||||
@@ -1139,15 +1144,13 @@ public class GameAction {
|
|||||||
* destroyPlaneswalkers.
|
* destroyPlaneswalkers.
|
||||||
* </p>
|
* </p>
|
||||||
*/
|
*/
|
||||||
private boolean handlePlaneswalkerRule() {
|
private boolean handlePlaneswalkerRule(Player p) {
|
||||||
// get all Planeswalkers
|
// get all Planeswalkers
|
||||||
final List<Card> list = CardLists.filter(game.getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.PLANEWALKERS);
|
final List<Card> list = CardLists.filter(p.getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.PLANEWALKERS);
|
||||||
|
|
||||||
boolean recheck = false;
|
boolean recheck = false;
|
||||||
Card c;
|
final Multimap<String, Card> uniqueWalkers = ArrayListMultimap.create();
|
||||||
for (int i = 0; i < list.size(); i++) {
|
for (Card c : list) {
|
||||||
c = list.get(i);
|
|
||||||
|
|
||||||
if (c.getCounters(CounterType.LOYALTY) <= 0) {
|
if (c.getCounters(CounterType.LOYALTY) <= 0) {
|
||||||
moveToGraveyard(c);
|
moveToGraveyard(c);
|
||||||
// Play the Destroy sound
|
// Play the Destroy sound
|
||||||
@@ -1155,21 +1158,28 @@ public class GameAction {
|
|||||||
recheck = true;
|
recheck = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
final ArrayList<String> types = c.getType();
|
|
||||||
for (final String type : types) {
|
for (final String type : c.getType()) {
|
||||||
if (!CardType.isAPlaneswalkerType(type)) {
|
if (CardType.isAPlaneswalkerType(type)) {
|
||||||
|
uniqueWalkers.put(type, c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(String key : uniqueWalkers.keySet())
|
||||||
|
{
|
||||||
|
Collection<Card> duplicates = uniqueWalkers.get(key);
|
||||||
|
if ( duplicates.size() < 2)
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
final List<Card> cl = CardLists.getType(list, type);
|
|
||||||
|
|
||||||
if (cl.size() > 1) {
|
|
||||||
for (final Card crd : cl) {
|
|
||||||
moveToGraveyard(crd);
|
|
||||||
}
|
|
||||||
recheck = true;
|
recheck = true;
|
||||||
|
|
||||||
|
Card toKeep = p.getController().chooseSingleCardForEffect(duplicates, new AbilitySub(ApiType.InternalLegendaryRule, null, null, null), "You have multiple planeswalkers of type \""+key+"\"in play.\n\nChoose one to stay on battlefield (the rest will be moved to graveyard)");
|
||||||
|
for(Card c: duplicates) {
|
||||||
|
if ( c != toKeep )
|
||||||
|
moveToGraveyard(c);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return recheck;
|
return recheck;
|
||||||
}
|
}
|
||||||
@@ -1179,8 +1189,8 @@ public class GameAction {
|
|||||||
* destroyLegendaryCreatures.
|
* destroyLegendaryCreatures.
|
||||||
* </p>
|
* </p>
|
||||||
*/
|
*/
|
||||||
private boolean handleLegendRule() {
|
private boolean handleLegendRule(Player p) {
|
||||||
final List<Card> a = CardLists.getType(game.getCardsIn(ZoneType.Battlefield), "Legendary");
|
final List<Card> a = CardLists.getType(p.getCardsIn(ZoneType.Battlefield), "Legendary");
|
||||||
if (a.isEmpty() || game.getStaticEffects().getGlobalRuleChange(GlobalRuleChange.noLegendRule)) {
|
if (a.isEmpty() || game.getStaticEffects().getGlobalRuleChange(GlobalRuleChange.noLegendRule)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1189,24 +1199,26 @@ public class GameAction {
|
|||||||
if (yamazaki.size() == 2) {
|
if (yamazaki.size() == 2) {
|
||||||
a.removeAll(yamazaki);
|
a.removeAll(yamazaki);
|
||||||
}
|
}
|
||||||
while (!a.isEmpty()) {
|
|
||||||
List<Card> b = CardLists.filter(game.getCardsIn(ZoneType.Battlefield), CardPredicates.nameEquals(a.get(0).getName()));
|
Multimap<String, Card> uniqueLegends = ArrayListMultimap.create();
|
||||||
b = CardLists.getType(b, "Legendary");
|
for(Card c : a) {
|
||||||
b = CardLists.filter(b, new Predicate<Card>() {
|
if ( !c.isFaceDown() )
|
||||||
@Override
|
uniqueLegends.put(c.getName(), c);
|
||||||
public boolean apply(final Card c) {
|
|
||||||
return !c.isFaceDown();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
a.remove(0);
|
|
||||||
if (1 < b.size()) {
|
|
||||||
for (int i = 0; i < b.size(); i++) {
|
|
||||||
sacrificeDestroy(b.get(i));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for(String name : uniqueLegends.keySet()) {
|
||||||
|
Collection<Card> cc = uniqueLegends.get(name);
|
||||||
|
if ( cc.size() < 2 )
|
||||||
|
continue;
|
||||||
|
|
||||||
recheck = true;
|
recheck = true;
|
||||||
// Play the Destroy sound
|
|
||||||
game.fireEvent(new GameEventCardDestroyed());
|
Card toKeep = p.getController().chooseSingleCardForEffect(cc, new AbilitySub(ApiType.InternalLegendaryRule, null, null, null), "You have multiple legendary creatures named \""+name+"\" in play.\n\nChoose the one to stay on battlefield (the rest will be moved to graveyard)");
|
||||||
|
for(Card c: cc) {
|
||||||
|
if ( c != toKeep )
|
||||||
|
sacrificeDestroy(c);
|
||||||
}
|
}
|
||||||
|
game.fireEvent(new GameEventCardDestroyed());
|
||||||
}
|
}
|
||||||
|
|
||||||
return recheck;
|
return recheck;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package forge.game.ai;
|
package forge.game.ai;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@@ -124,7 +125,7 @@ public class ComputerUtilCard {
|
|||||||
* a {@link forge.CardList} object.
|
* a {@link forge.CardList} object.
|
||||||
* @return a {@link forge.Card} object.
|
* @return a {@link forge.Card} object.
|
||||||
*/
|
*/
|
||||||
public static Card getBestLandAI(final List<Card> list) {
|
public static Card getBestLandAI(final Collection<Card> list) {
|
||||||
final List<Card> land = CardLists.filter(list, CardPredicates.Presets.LANDS);
|
final List<Card> land = CardLists.filter(list, CardPredicates.Presets.LANDS);
|
||||||
if (land.isEmpty()) {
|
if (land.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
@@ -180,8 +181,7 @@ public class ComputerUtilCard {
|
|||||||
* a boolean.
|
* a boolean.
|
||||||
* @return a {@link forge.Card} object.
|
* @return a {@link forge.Card} object.
|
||||||
*/
|
*/
|
||||||
public static Card getCheapestPermanentAI(final List<Card> list, final SpellAbility spell, final boolean targeted) {
|
public static Card getCheapestPermanentAI(Collection<Card> all, final SpellAbility spell, final boolean targeted) {
|
||||||
List<Card> all = list;
|
|
||||||
if (targeted) {
|
if (targeted) {
|
||||||
all = CardLists.filter(all, new Predicate<Card>() {
|
all = CardLists.filter(all, new Predicate<Card>() {
|
||||||
@Override
|
@Override
|
||||||
@@ -190,17 +190,16 @@ public class ComputerUtilCard {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (all.size() == 0) {
|
if (all.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// get cheapest card:
|
// get cheapest card:
|
||||||
Card cheapest = null;
|
Card cheapest = null;
|
||||||
cheapest = all.get(0);
|
|
||||||
|
|
||||||
for (int i = 0; i < all.size(); i++) {
|
for (Card c : all) {
|
||||||
if (cheapest.getManaCost().getCMC() <= cheapest.getManaCost().getCMC()) {
|
if (cheapest == null || cheapest.getManaCost().getCMC() <= cheapest.getManaCost().getCMC()) {
|
||||||
cheapest = all.get(i);
|
cheapest = c;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,7 +217,7 @@ public class ComputerUtilCard {
|
|||||||
* a {@link forge.CardList} object.
|
* a {@link forge.CardList} object.
|
||||||
* @return a {@link forge.Card} object.
|
* @return a {@link forge.Card} object.
|
||||||
*/
|
*/
|
||||||
public static Card getBestAI(final List<Card> list) {
|
public static Card getBestAI(final Collection<Card> list) {
|
||||||
// Get Best will filter by appropriate getBest list if ALL of the list
|
// Get Best will filter by appropriate getBest list if ALL of the list
|
||||||
// is of that type
|
// is of that type
|
||||||
if (Iterables.all(list, CardPredicates.Presets.CREATURES))
|
if (Iterables.all(list, CardPredicates.Presets.CREATURES))
|
||||||
@@ -239,7 +238,7 @@ public class ComputerUtilCard {
|
|||||||
* the list
|
* the list
|
||||||
* @return the card
|
* @return the card
|
||||||
*/
|
*/
|
||||||
public static Card getBestCreatureAI(final List<Card> list) {
|
public static Card getBestCreatureAI(final Collection<Card> list) {
|
||||||
return Aggregates.itemWithMax(Iterables.filter(list, CardPredicates.Presets.CREATURES), ComputerUtilCard.fnEvaluateCreature);
|
return Aggregates.itemWithMax(Iterables.filter(list, CardPredicates.Presets.CREATURES), ComputerUtilCard.fnEvaluateCreature);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -292,7 +291,7 @@ public class ComputerUtilCard {
|
|||||||
* a {@link forge.CardList} object.
|
* a {@link forge.CardList} object.
|
||||||
* @return a {@link forge.Card} object.
|
* @return a {@link forge.Card} object.
|
||||||
*/
|
*/
|
||||||
public static Card getWorstAI(final List<Card> list) {
|
public static Card getWorstAI(final Collection<Card> list) {
|
||||||
return ComputerUtilCard.getWorstPermanentAI(list, false, false, false, false);
|
return ComputerUtilCard.getWorstPermanentAI(list, false, false, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -313,7 +312,7 @@ public class ComputerUtilCard {
|
|||||||
* a boolean.
|
* a boolean.
|
||||||
* @return a {@link forge.Card} object.
|
* @return a {@link forge.Card} object.
|
||||||
*/
|
*/
|
||||||
public static Card getWorstPermanentAI(final List<Card> list, final boolean biasEnch, final boolean biasLand,
|
public static Card getWorstPermanentAI(final Collection<Card> list, final boolean biasEnch, final boolean biasLand,
|
||||||
final boolean biasArt, final boolean biasCreature) {
|
final boolean biasArt, final boolean biasCreature) {
|
||||||
if (list.size() == 0) {
|
if (list.size() == 0) {
|
||||||
return null;
|
return null;
|
||||||
@@ -628,7 +627,7 @@ public class ComputerUtilCard {
|
|||||||
* the all
|
* the all
|
||||||
* @return the card
|
* @return the card
|
||||||
*/
|
*/
|
||||||
public static Card getMostExpensivePermanentAI(final List<Card> all) {
|
public static Card getMostExpensivePermanentAI(final Collection<Card> all) {
|
||||||
Card biggest = null;
|
Card biggest = null;
|
||||||
|
|
||||||
int bigCMC = -1;
|
int bigCMC = -1;
|
||||||
|
|||||||
@@ -112,8 +112,8 @@ public abstract class PlayerController {
|
|||||||
// Specify a target of a spell (Spellskite)
|
// Specify a target of a spell (Spellskite)
|
||||||
public abstract Pair<SpellAbilityStackInstance, ITargetable> chooseTarget(SpellAbility sa, List<Pair<SpellAbilityStackInstance, ITargetable>> allTargets);
|
public abstract Pair<SpellAbilityStackInstance, ITargetable> chooseTarget(SpellAbility sa, List<Pair<SpellAbilityStackInstance, ITargetable>> allTargets);
|
||||||
|
|
||||||
public Card chooseSingleCardForEffect(List<Card> sourceList, SpellAbility sa, String title) { return chooseSingleCardForEffect(sourceList, sa, title, false); }
|
public Card chooseSingleCardForEffect(Collection<Card> sourceList, SpellAbility sa, String title) { return chooseSingleCardForEffect(sourceList, sa, title, false); }
|
||||||
public abstract Card chooseSingleCardForEffect(List<Card> sourceList, SpellAbility sa, String title, boolean isOptional);
|
public abstract Card chooseSingleCardForEffect(Collection<Card> sourceList, SpellAbility sa, String title, boolean isOptional);
|
||||||
public abstract Player chooseSinglePlayerForEffect(List<Player> options, SpellAbility sa, String title);
|
public abstract Player chooseSinglePlayerForEffect(List<Player> options, SpellAbility sa, String title);
|
||||||
public abstract SpellAbility chooseSingleSpellForEffect(List<SpellAbility> spells, SpellAbility sa, String title);
|
public abstract SpellAbility chooseSingleSpellForEffect(List<SpellAbility> spells, SpellAbility sa, String title);
|
||||||
|
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ public class PlayerControllerAi extends PlayerController {
|
|||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Card chooseSingleCardForEffect(List<Card> options, SpellAbility sa, String title, boolean isOptional) {
|
public Card chooseSingleCardForEffect(Collection<Card> options, SpellAbility sa, String title, boolean isOptional) {
|
||||||
ApiType api = sa.getApi();
|
ApiType api = sa.getApi();
|
||||||
if ( null == api ) {
|
if ( null == api ) {
|
||||||
throw new InvalidParameterException("SA is not api-based, this is not supported yet");
|
throw new InvalidParameterException("SA is not api-based, this is not supported yet");
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ import forge.game.Game;
|
|||||||
import forge.game.GameType;
|
import forge.game.GameType;
|
||||||
import forge.game.combat.Combat;
|
import forge.game.combat.Combat;
|
||||||
import forge.game.phase.PhaseType;
|
import forge.game.phase.PhaseType;
|
||||||
|
import forge.game.zone.Zone;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
import forge.gui.GuiChoose;
|
import forge.gui.GuiChoose;
|
||||||
import forge.gui.GuiDialog;
|
import forge.gui.GuiDialog;
|
||||||
@@ -272,17 +273,33 @@ public class PlayerControllerHuman extends PlayerController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Card chooseSingleCardForEffect(List<Card> options, SpellAbility sa, String title, boolean isOptional) {
|
public Card chooseSingleCardForEffect(Collection<Card> options, SpellAbility sa, String title, boolean isOptional) {
|
||||||
// Human is supposed to read the message and understand from it what to choose
|
// Human is supposed to read the message and understand from it what to choose
|
||||||
if (options.isEmpty())
|
if (options.isEmpty())
|
||||||
return null;
|
return null;
|
||||||
|
if ( !isOptional && options.size() == 1 )
|
||||||
|
return Iterables.getFirst(options, null);
|
||||||
|
|
||||||
if ( isOptional )
|
boolean canUseSelectCardsInput = true;
|
||||||
return GuiChoose.oneOrNone(title, options);
|
for(Card c : options) {
|
||||||
else if ( options.size() > 1 )
|
Zone cz = c.getZone();
|
||||||
return GuiChoose.one(title, options);
|
// can point at cards in own hand and anyone's battlefield
|
||||||
else
|
boolean canUiPointAtCards = cz != null && ( cz.is(ZoneType.Hand) && cz.getPlayer() == player || cz.is(ZoneType.Battlefield));
|
||||||
return options.get(0);
|
if ( !canUiPointAtCards ) {
|
||||||
|
canUseSelectCardsInput = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( canUseSelectCardsInput ) {
|
||||||
|
InputSelectCardsFromList input = new InputSelectCardsFromList(isOptional ? 0 : 1, 1, options);
|
||||||
|
input.setCancelAllowed(isOptional);
|
||||||
|
input.setMessage(title);
|
||||||
|
Singletons.getControl().getInputQueue().setInputAndWait(input);
|
||||||
|
return Iterables.getFirst(input.getSelected(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return isOptional ? GuiChoose.oneOrNone(title, options) : GuiChoose.one(title, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
package forge.gui.input;
|
package forge.gui.input;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.Collection;
|
||||||
|
|
||||||
import forge.Card;
|
import forge.Card;
|
||||||
|
|
||||||
public class InputSelectCardsFromList extends InputSelectCards {
|
public class InputSelectCardsFromList extends InputSelectCards {
|
||||||
private static final long serialVersionUID = 6230360322294805986L;
|
private static final long serialVersionUID = 6230360322294805986L;
|
||||||
|
|
||||||
private final List<Card> validChoices;
|
private final Collection<Card> validChoices;
|
||||||
|
|
||||||
public InputSelectCardsFromList(int min, int max, List<Card> validCards) {
|
public InputSelectCardsFromList(int min, int max, Collection<Card> validCards) {
|
||||||
super(Math.min(min, validCards.size()), Math.min(max, validCards.size())); // to avoid hangs
|
super(Math.min(min, validCards.size()), Math.min(max, validCards.size())); // to avoid hangs
|
||||||
this.validChoices = validCards;
|
this.validChoices = validCards;
|
||||||
|
|
||||||
|
|||||||
@@ -369,9 +369,12 @@ public enum CMatchUI {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final static boolean LOG_UIEVENTS = false;
|
||||||
|
|
||||||
// UI-related events should arrive here
|
// UI-related events should arrive here
|
||||||
public void fireEvent(UiEvent uiEvent) {
|
public void fireEvent(UiEvent uiEvent) {
|
||||||
|
if ( LOG_UIEVENTS )
|
||||||
|
System.out.println("UI: " + uiEvent.toString() + " \t\t " + FThreads.debugGetStackTraceItem(4, true));
|
||||||
uiEvents.post(uiEvent);
|
uiEvents.post(uiEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user