mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-17 11:18:01 +00:00
Initial pass of Exert (lots of things to be improved, but should be good enough to start scripting)
This commit is contained in:
2
.gitattributes
vendored
2
.gitattributes
vendored
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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}."),
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
8
forge-gui/res/cardsfolder/upcoming/watchful_naga.txt
Normal file
8
forge-gui/res/cardsfolder/upcoming/watchful_naga.txt
Normal 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.
|
||||||
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user