Initial pass of Exert (lots of things to be improved, but should be good enough to start scripting)

This commit is contained in:
Sol
2017-04-16 20:00:42 +00:00
parent ea91d23bc2
commit 2f9319c420
12 changed files with 114 additions and 31 deletions

2
.gitattributes vendored
View File

@@ -716,6 +716,7 @@ forge-game/src/main/java/forge/game/trigger/TriggerDevoured.java -text
forge-game/src/main/java/forge/game/trigger/TriggerDiscarded.java svneol=native#text/plain forge-game/src/main/java/forge/game/trigger/TriggerDiscarded.java svneol=native#text/plain
forge-game/src/main/java/forge/game/trigger/TriggerDrawn.java svneol=native#text/plain forge-game/src/main/java/forge/game/trigger/TriggerDrawn.java svneol=native#text/plain
forge-game/src/main/java/forge/game/trigger/TriggerEvolved.java -text forge-game/src/main/java/forge/game/trigger/TriggerEvolved.java -text
forge-game/src/main/java/forge/game/trigger/TriggerExerted.java -text
forge-game/src/main/java/forge/game/trigger/TriggerExploited.java -text forge-game/src/main/java/forge/game/trigger/TriggerExploited.java -text
forge-game/src/main/java/forge/game/trigger/TriggerFight.java -text forge-game/src/main/java/forge/game/trigger/TriggerFight.java -text
forge-game/src/main/java/forge/game/trigger/TriggerFlippedCoin.java -text forge-game/src/main/java/forge/game/trigger/TriggerFlippedCoin.java -text
@@ -16855,6 +16856,7 @@ forge-gui/res/cardsfolder/upcoming/trueheart_duelist.txt -text
forge-gui/res/cardsfolder/upcoming/unwavering_initiate.txt -text forge-gui/res/cardsfolder/upcoming/unwavering_initiate.txt -text
forge-gui/res/cardsfolder/upcoming/vizier_of_remedies.txt -text forge-gui/res/cardsfolder/upcoming/vizier_of_remedies.txt -text
forge-gui/res/cardsfolder/upcoming/vizier_of_tumbling_sands.txt -text forge-gui/res/cardsfolder/upcoming/vizier_of_tumbling_sands.txt -text
forge-gui/res/cardsfolder/upcoming/watchful_naga.txt -text
forge-gui/res/cardsfolder/upcoming/wayward_servant.txt -text forge-gui/res/cardsfolder/upcoming/wayward_servant.txt -text
forge-gui/res/cardsfolder/upcoming/winds_of_rebuke.txt -text forge-gui/res/cardsfolder/upcoming/winds_of_rebuke.txt -text
forge-gui/res/cardsfolder/upcoming/winged_shepherd.txt -text forge-gui/res/cardsfolder/upcoming/winged_shepherd.txt -text

View File

@@ -30,11 +30,7 @@ import forge.card.CardTypeView;
import forge.game.GameEntity; import forge.game.GameEntity;
import forge.game.ability.ApiType; import forge.game.ability.ApiType;
import forge.game.ability.effects.ProtectEffect; import forge.game.ability.effects.ProtectEffect;
import forge.game.card.Card; import forge.game.card.*;
import forge.game.card.CardCollectionView;
import forge.game.card.CardLists;
import forge.game.card.CardPredicates;
import forge.game.card.CounterType;
import forge.game.combat.Combat; import forge.game.combat.Combat;
import forge.game.combat.CombatUtil; import forge.game.combat.CombatUtil;
import forge.game.combat.GlobalAttackRestrictions; import forge.game.combat.GlobalAttackRestrictions;
@@ -78,11 +74,7 @@ public class AiAttackController {
* <p> * <p>
* Constructor for ComputerUtil_Attack2. * Constructor for ComputerUtil_Attack2.
* </p> * </p>
* *
* @param possibleAttackers
* a {@link forge.CardList} object.
* @param possibleBlockers
* a {@link forge.CardList} object.
*/ */
public AiAttackController(final Player ai) { public AiAttackController(final Player ai) {
this.ai = ai; this.ai = ai;
@@ -156,10 +148,7 @@ public class AiAttackController {
* <p> * <p>
* sortAttackers. * sortAttackers.
* </p> * </p>
* *
* @param in
* a {@link forge.CardList} object.
* @return a {@link forge.CardList} object.
*/ */
public final static List<Card> sortAttackers(final List<Card> in) { public final static List<Card> sortAttackers(final List<Card> in) {
final List<Card> list = new ArrayList<Card>(); final List<Card> list = new ArrayList<Card>();
@@ -971,7 +960,7 @@ public class AiAttackController {
* @param attacker * @param attacker
* a {@link forge.game.card.Card} object. * a {@link forge.game.card.Card} object.
* @param defenders * @param defenders
* a {@link forge.CardList} object. * a object.
* @param combat * @param combat
* a {@link forge.game.combat.Combat} object. * a {@link forge.game.combat.Combat} object.
* @return a boolean. * @return a boolean.
@@ -1117,6 +1106,18 @@ public class AiAttackController {
} }
return false; // don't attack return false; // don't attack
} }
public static List<Card> exertAttackers(List<Card> attackers) {
List<Card> exerters = Lists.newArrayList();
for(Card c : attackers) {
// TODO Improve when the AI wants to use Exert powers
if (random.nextBoolean()) {
exerters.add(c);
}
}
return exerters;
}
/** /**
* Find a protection type that will make an attacker unblockable. * Find a protection type that will make an attacker unblockable.
@@ -1181,5 +1182,4 @@ public class AiAttackController {
} }
return null; //should never get here return null; //should never get here
} }
} // end class ComputerUtil_Attack2 } // end class ComputerUtil_Attack2

View File

@@ -231,11 +231,16 @@ public class PlayerControllerAi extends PlayerController {
public CardCollection orderBlockers(Card attacker, CardCollection blockers) { public CardCollection orderBlockers(Card attacker, CardCollection blockers) {
return AiBlockController.orderBlockers(attacker, blockers); return AiBlockController.orderBlockers(attacker, blockers);
} }
@Override
public List<Card> exertAttackers(List<Card> attackers) {
return AiAttackController.exertAttackers(attackers);
}
@Override @Override
public CardCollection orderBlocker(Card attacker, Card blocker, CardCollection oldBlockers) { public CardCollection orderBlocker(Card attacker, Card blocker, CardCollection oldBlockers) {
return AiBlockController.orderBlocker(attacker, blocker, oldBlockers); return AiBlockController.orderBlocker(attacker, blocker, oldBlockers);
}; }
@Override @Override
public CardCollection orderAttackers(Card blocker, CardCollection attackers) { public CardCollection orderAttackers(Card blocker, CardCollection attackers) {

View File

@@ -49,6 +49,7 @@ public enum Keyword {
EVOKE(KeywordWithCost.class, false, "You may cast this spell for its evoke cost. If you do, it's sacrificed when it enters the battlefield."), EVOKE(KeywordWithCost.class, false, "You may cast this spell for its evoke cost. If you do, it's sacrificed when it enters the battlefield."),
EVOLVE(SimpleKeyword.class, false, "Whenever a creature enters the battlefield under your control, if that creature has greater power or toughness than this creature, put a +1/+1 counter on this creature."), EVOLVE(SimpleKeyword.class, false, "Whenever a creature enters the battlefield under your control, if that creature has greater power or toughness than this creature, put a +1/+1 counter on this creature."),
EXALTED(SimpleKeyword.class, false, "Whenever a creature you control attacks alone, that creature gets +1/+1 until end of turn."), EXALTED(SimpleKeyword.class, false, "Whenever a creature you control attacks alone, that creature gets +1/+1 until end of turn."),
EXERTED(SimpleKeyword.class, true, "This creature won't untap during your next untap step."),
EXPLOIT(SimpleKeyword.class, false, "When this creature enters the battlefield, you may sacrifice a creature."), EXPLOIT(SimpleKeyword.class, false, "When this creature enters the battlefield, you may sacrifice a creature."),
EXTORT(SimpleKeyword.class, false, "Whenever you cast a spell, you may pay {W/B}. If you do, each opponent loses 1 life and you gain that much life."), EXTORT(SimpleKeyword.class, false, "Whenever you cast a spell, you may pay {W/B}. If you do, each opponent loses 1 life and you gain that much life."),
FABRICATE(KeywordWithAmount.class, false, "When this creature enters the battlefield, put {%1$d:+1/+1 counter} on it, or create {%1$d:1/1 colorless Servo artifact creature token}."), FABRICATE(KeywordWithAmount.class, false, "When this creature enters the battlefield, put {%1$d:+1/+1 counter} on it, or create {%1$d:1/1 colorless Servo artifact creature token}."),

View File

@@ -508,6 +508,18 @@ public class PhaseHandler implements java.io.Serializable {
} }
} while (!success); } while (!success);
// Exert creatures here
List<Card> possibleExerters = CardLists.getKeyword(combat.getAttackers(),
"You may exert CARDNAME as it attacks.");
for(Card exerter : whoDeclares.getController().exertAttackers(possibleExerters)) {
exerter.addExtrinsicKeyword("Exerted");
final HashMap<String, Object> runParams = new HashMap<String, Object>();
runParams.put("Card", exerter);
runParams.put("Player", playerTurn);
game.getTriggerHandler().runTrigger(TriggerType.Exerted, runParams, false);
}
} }
if (game.isGameOver()) { // they just like to close window at any moment if (game.isGameOver()) { // they just like to close window at any moment

View File

@@ -17,17 +17,8 @@
*/ */
package forge.game.phase; package forge.game.phase;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.base.Predicates; import com.google.common.base.Predicates;
import forge.card.CardType; import forge.card.CardType;
import forge.game.Game; import forge.game.Game;
import forge.game.GameEntity; import forge.game.GameEntity;
@@ -43,6 +34,9 @@ import forge.game.spellability.SpellAbility;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import forge.util.collect.FCollection; import forge.util.collect.FCollection;
import java.util.*;
import java.util.Map.Entry;
/** /**
* <p> * <p>
* Untap class. * Untap class.
@@ -91,7 +85,8 @@ public class Untap extends Phase {
if (c.hasKeyword("CARDNAME doesn't untap during your untap step.") if (c.hasKeyword("CARDNAME doesn't untap during your untap step.")
|| c.hasKeyword("This card doesn't untap during your next untap step.") || c.hasKeyword("This card doesn't untap during your next untap step.")
|| c.hasKeyword("This card doesn't untap during your next two untap steps.") || c.hasKeyword("This card doesn't untap during your next two untap steps.")
|| c.hasKeyword("This card doesn't untap.")) { || c.hasKeyword("This card doesn't untap.")
|| c.hasKeyword("Exerted")) {
return false; return false;
} }
return true; return true;
@@ -211,6 +206,7 @@ public class Untap extends Phase {
c.removeAllExtrinsicKeyword("HIDDEN This card doesn't untap during your next two untap steps."); c.removeAllExtrinsicKeyword("HIDDEN This card doesn't untap during your next two untap steps.");
c.addHiddenExtrinsicKeyword("HIDDEN This card doesn't untap during your next untap step."); c.addHiddenExtrinsicKeyword("HIDDEN This card doesn't untap during your next untap step.");
} }
c.removeAllExtrinsicKeyword("Exerted");
} }
} // end doUntap } // end doUntap

View File

@@ -57,7 +57,7 @@ public abstract class PlayerController {
Echo, Echo,
Multikicker, Multikicker,
Replicate, Replicate,
CumulativeUpkeep; CumulativeUpkeep,
} }
public static enum BinaryChoiceType { public static enum BinaryChoiceType {
@@ -126,6 +126,7 @@ public abstract class PlayerController {
public abstract Player chooseStartingPlayer(boolean isFirstGame); public abstract Player chooseStartingPlayer(boolean isFirstGame);
public abstract CardCollection orderBlockers(Card attacker, CardCollection blockers); public abstract CardCollection orderBlockers(Card attacker, CardCollection blockers);
public abstract List<Card> exertAttackers(List<Card> attackers);
/** /**
* Add a card to a pre-existing blocking order. * Add a card to a pre-existing blocking order.
* @param attacker the attacking creature. * @param attacker the attacking creature.
@@ -179,7 +180,7 @@ public abstract class PlayerController {
public abstract int chooseNumber(SpellAbility sa, String title, List<Integer> values, Player relatedPlayer); public abstract int chooseNumber(SpellAbility sa, String title, List<Integer> values, Player relatedPlayer);
public int chooseNumber(SpellAbility sa, String string, int min, int max, Map<String, Object> params) { public int chooseNumber(SpellAbility sa, String string, int min, int max, Map<String, Object> params) {
return chooseNumber(sa, string, min, max); return chooseNumber(sa, string, min, max);
}; }
public final boolean chooseBinary(SpellAbility sa, String question, BinaryChoiceType kindOfChoice) { return chooseBinary(sa, question, kindOfChoice, (Boolean) null); } public final boolean chooseBinary(SpellAbility sa, String question, BinaryChoiceType kindOfChoice) { return chooseBinary(sa, question, kindOfChoice, (Boolean) null); }
public abstract boolean chooseBinary(SpellAbility sa, String question, BinaryChoiceType kindOfChoice, Boolean defaultChioce); public abstract boolean chooseBinary(SpellAbility sa, String question, BinaryChoiceType kindOfChoice, Boolean defaultChioce);

View File

@@ -0,0 +1,47 @@
package forge.game.trigger;
import forge.game.card.Card;
import forge.game.spellability.SpellAbility;
import java.util.HashMap;
import java.util.Map;
public class TriggerExerted extends Trigger {
/**
* <p>
* Constructor for Trigger.
* </p>
*
* @param params a {@link HashMap} object.
* @param host a {@link Card} object.
* @param intrinsic
*/
public TriggerExerted(Map<String, String> params, Card host, boolean intrinsic) {
super(params, host, intrinsic);
}
@Override
public boolean performTest(Map<String, Object> runParams2) {
final Card exerter = (Card) runParams2.get("Card");
if (this.mapParams.containsKey("ValidCard")) {
if (!exerter.isValid(this.mapParams.get("ValidCard").split(","), this.getHostCard().getController(),
this.getHostCard(), null)) {
return false;
}
}
return true;
}
@Override
public void setTriggeringObjects(SpellAbility sa) {
sa.setTriggeringObject("Card", this.getRunParams().get("Card"));
sa.setTriggeringObject("Player", this.getRunParams().get("Player"));
}
@Override
public String getImportantStackObjects(SpellAbility sa) {
StringBuilder sb = new StringBuilder();
sb.append("Exerted: ").append(sa.getTriggeringObject("Card"));
return sb.toString();
}
}

View File

@@ -47,6 +47,7 @@ public enum TriggerType {
Discarded(TriggerDiscarded.class), Discarded(TriggerDiscarded.class),
Drawn(TriggerDrawn.class), Drawn(TriggerDrawn.class),
Evolved(TriggerEvolved.class), Evolved(TriggerEvolved.class),
Exerted(TriggerExerted.class),
Exploited(TriggerExploited.class), Exploited(TriggerExploited.class),
Fight(TriggerFight.class), Fight(TriggerFight.class),
FlippedCoin(TriggerFlippedCoin.class), FlippedCoin(TriggerFlippedCoin.class),
@@ -104,7 +105,7 @@ public enum TriggerType {
/** /**
* TODO: Write javadoc for this method. * TODO: Write javadoc for this method.
* @param string * @param value
* @return * @return
*/ */
public static TriggerType smartValueOf(String value) { public static TriggerType smartValueOf(String value) {

View File

@@ -221,6 +221,11 @@ public class PlayerControllerForTests extends PlayerController {
return blockers; return blockers;
} }
@Override
public List<Card> exertAttackers(List<Card> attackers) {
return Lists.newArrayList(attackers);
}
@Override @Override
public CardCollection orderBlocker(final Card attacker, final Card blocker, final CardCollection oldBlockers) { public CardCollection orderBlocker(final Card attacker, final Card blocker, final CardCollection oldBlockers) {
final CardCollection allBlockers = new CardCollection(oldBlockers); final CardCollection allBlockers = new CardCollection(oldBlockers);

View File

@@ -0,0 +1,8 @@
Name:Watchful Naga
ManaCost:2 G
Types:Creature Naga Wizard
PT:2/2
K:You may exert CARDNAME as it attacks.
T:Mode$ Exerted | ValidCard$ Card.Self | Execute$ DBDraw | TriggerDescription$ When you exert CARDNAME, draw a card.
SVar:DBDraw:DB$ Draw | Defined$ TriggeredPlayer
Oracle:You may exert Watchful Naga as it attacks. When you do, draw a card.

View File

@@ -576,6 +576,11 @@ public class PlayerControllerHuman
return game.getCardList(getGui().order("Choose Damage Order for " + vAttacker, "Damaged First", CardView.getCollection(blockers), vAttacker)); return game.getCardList(getGui().order("Choose Damage Order for " + vAttacker, "Damaged First", CardView.getCollection(blockers), vAttacker));
} }
@Override
public List<Card> exertAttackers(List<Card> attackers) {
return getGui().getChoices("Exert Attackers?", 0, attackers.size(), attackers);
}
@Override @Override
public CardCollection orderBlocker(final Card attacker, final Card blocker, final CardCollection oldBlockers) { public CardCollection orderBlocker(final Card attacker, final Card blocker, final CardCollection oldBlockers) {
final CardView vAttacker = CardView.get(attacker); final CardView vAttacker = CardView.get(attacker);