mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-20 12:48:00 +00:00
Merge branch 'runChaosEffect' into 'master'
RunChaos: better effect for Pools of Becoming See merge request core-developers/forge!3362
This commit is contained in:
@@ -31,7 +31,6 @@ import forge.deck.CardPool;
|
||||
import forge.deck.Deck;
|
||||
import forge.deck.DeckSection;
|
||||
import forge.game.*;
|
||||
import forge.game.ability.AbilityFactory;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.ability.ApiType;
|
||||
import forge.game.ability.SpellApiBased;
|
||||
@@ -64,7 +63,6 @@ import forge.util.collect.FCollectionView;
|
||||
import io.sentry.Sentry;
|
||||
import io.sentry.event.BreadcrumbBuilder;
|
||||
|
||||
import java.security.InvalidParameterException;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
@@ -204,24 +202,22 @@ public class AiController {
|
||||
return api == null;
|
||||
}
|
||||
boolean rightapi = false;
|
||||
String battlefield = ZoneType.Battlefield.toString();
|
||||
Player activatingPlayer = sa.getActivatingPlayer();
|
||||
|
||||
// Trigger play improvements
|
||||
for (final Trigger tr : card.getTriggers()) {
|
||||
// These triggers all care for ETB effects
|
||||
|
||||
final Map<String, String> params = tr.getMapParams();
|
||||
if (tr.getMode() != TriggerType.ChangesZone) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!params.get("Destination").equals(battlefield)) {
|
||||
if (!ZoneType.Battlefield.toString().equals(tr.getParam("Destination"))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (params.containsKey("ValidCard")) {
|
||||
String validCard = params.get("ValidCard");
|
||||
if (tr.hasParam("ValidCard")) {
|
||||
String validCard = tr.getParam("ValidCard");
|
||||
if (!validCard.contains("Self")) {
|
||||
continue;
|
||||
}
|
||||
@@ -245,21 +241,11 @@ public class AiController {
|
||||
}
|
||||
|
||||
// if trigger is not mandatory - no problem
|
||||
if (params.get("OptionalDecider") != null && api == null) {
|
||||
if (tr.hasParam("OptionalDecider") && api == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
SpellAbility exSA = tr.getOverridingAbility();
|
||||
|
||||
if (exSA == null) {
|
||||
// Maybe better considerations
|
||||
if (!params.containsKey("Execute")) {
|
||||
continue;
|
||||
}
|
||||
exSA = AbilityFactory.getAbility(card, params.get("Execute"));
|
||||
} else {
|
||||
exSA = exSA.copy();
|
||||
}
|
||||
SpellAbility exSA = tr.ensureAbility().copy(activatingPlayer);
|
||||
|
||||
if (api != null) {
|
||||
if (exSA.getApi() != api) {
|
||||
@@ -273,13 +259,7 @@ public class AiController {
|
||||
}
|
||||
}
|
||||
|
||||
if (sa != null) {
|
||||
exSA.setActivatingPlayer(activatingPlayer);
|
||||
}
|
||||
else {
|
||||
exSA.setActivatingPlayer(player);
|
||||
}
|
||||
exSA.setTrigger(true);
|
||||
exSA.setTrigger(tr);
|
||||
|
||||
// for trigger test, need to ignore the conditions
|
||||
SpellAbilityCondition cons = exSA.getConditions();
|
||||
@@ -304,18 +284,16 @@ public class AiController {
|
||||
// Replacement effects
|
||||
for (final ReplacementEffect re : card.getReplacementEffects()) {
|
||||
// These Replacements all care for ETB effects
|
||||
|
||||
final Map<String, String> params = re.getMapParams();
|
||||
if (!(re instanceof ReplaceMoved)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!params.get("Destination").equals(battlefield)) {
|
||||
if (!ZoneType.Battlefield.toString().equals(re.getParam("Destination"))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (params.containsKey("ValidCard")) {
|
||||
String validCard = params.get("ValidCard");
|
||||
if (re.hasParam("ValidCard")) {
|
||||
String validCard = re.getParam("ValidCard");
|
||||
if (!validCard.contains("Self")) {
|
||||
continue;
|
||||
}
|
||||
@@ -337,26 +315,17 @@ public class AiController {
|
||||
if (!re.requirementsCheck(game)) {
|
||||
continue;
|
||||
}
|
||||
final SpellAbility exSA = re.getOverridingAbility();
|
||||
SpellAbility exSA = re.getOverridingAbility();
|
||||
|
||||
if (exSA != null) {
|
||||
if (sa != null) {
|
||||
exSA.setActivatingPlayer(activatingPlayer);
|
||||
}
|
||||
else {
|
||||
exSA.setActivatingPlayer(player);
|
||||
}
|
||||
exSA = exSA.copy(activatingPlayer);
|
||||
|
||||
if (exSA.getActivatingPlayer() == null) {
|
||||
throw new InvalidParameterException("Executing SpellAbility for Replacement Effect has no activating player");
|
||||
// ETBReplacement uses overriding abilities.
|
||||
// These checks only work if the Executing SpellAbility is an Ability_Sub.
|
||||
if ((exSA instanceof AbilitySub) && !doTrigger(exSA, false)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// ETBReplacement uses overriding abilities.
|
||||
// These checks only work if the Executing SpellAbility is an Ability_Sub.
|
||||
if (exSA != null && (exSA instanceof AbilitySub) && !doTrigger(exSA, false)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -745,14 +745,9 @@ public class ComputerUtil {
|
||||
if (source.hasParam("Exploit")) {
|
||||
for (Trigger t : host.getTriggers()) {
|
||||
if (t.getMode() == TriggerType.Exploited) {
|
||||
final String execute = t.getParam("Execute");
|
||||
if (execute == null) {
|
||||
continue;
|
||||
}
|
||||
final SpellAbility exSA = AbilityFactory.getAbility(host.getSVar(execute), host);
|
||||
final SpellAbility exSA = t.ensureAbility().copy(ai);
|
||||
|
||||
exSA.setActivatingPlayer(ai);
|
||||
exSA.setTrigger(true);
|
||||
exSA.setTrigger(t);
|
||||
|
||||
// Run non-mandatory trigger.
|
||||
// These checks only work if the Executing SpellAbility is an Ability_Sub.
|
||||
|
||||
@@ -142,7 +142,7 @@ public enum SpellApiToAi {
|
||||
.put(ApiType.RevealHand, RevealHandAi.class)
|
||||
.put(ApiType.ReverseTurnOrder, AlwaysPlayAi.class)
|
||||
.put(ApiType.RollPlanarDice, RollPlanarDiceAi.class)
|
||||
.put(ApiType.RunSVarAbility, AlwaysPlayAi.class)
|
||||
.put(ApiType.RunChaos, AlwaysPlayAi.class)
|
||||
.put(ApiType.Sacrifice, SacrificeAi.class)
|
||||
.put(ApiType.SacrificeAll, SacrificeAllAi.class)
|
||||
.put(ApiType.Scry, ScryAi.class)
|
||||
|
||||
@@ -142,7 +142,7 @@ public enum ApiType {
|
||||
RevealHand (RevealHandEffect.class),
|
||||
ReverseTurnOrder (ReverseTurnOrderEffect.class),
|
||||
RollPlanarDice (RollPlanarDiceEffect.class),
|
||||
RunSVarAbility (RunSVarAbilityEffect.class),
|
||||
RunChaos (RunChaosEffect.class),
|
||||
Sacrifice (SacrificeEffect.class),
|
||||
SacrificeAll (SacrificeAllEffect.class),
|
||||
Scry (ScryEffect.class),
|
||||
|
||||
@@ -44,7 +44,6 @@ public class CharmEffect extends SpellAbilityEffect {
|
||||
}
|
||||
// set CharmOrder
|
||||
for (AbilitySub sub : choices) {
|
||||
sub.setTrigger(sa.isTrigger());
|
||||
sub.setSVar("CharmOrder", Integer.toString(indx));
|
||||
indx++;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
package forge.game.ability.effects;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import forge.game.PlanarDice;
|
||||
import forge.game.ability.AbilityKey;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.trigger.Trigger;
|
||||
import forge.game.trigger.TriggerType;
|
||||
import forge.game.trigger.WrappedAbility;
|
||||
|
||||
public class RunChaosEffect extends SpellAbilityEffect {
|
||||
|
||||
@Override
|
||||
public void resolve(SpellAbility sa) {
|
||||
Map<AbilityKey, Object> map = AbilityKey.newMap();
|
||||
map.put(AbilityKey.Player, sa.getActivatingPlayer());
|
||||
map.put(AbilityKey.Result, PlanarDice.Chaos);
|
||||
|
||||
List<SpellAbility> validSA = Lists.newArrayList();
|
||||
for (final Card c : getTargetCards(sa)) {
|
||||
for (Trigger t : c.getTriggers()) {
|
||||
if (TriggerType.PlanarDice.equals(t.getMode()) && t.performTest(map)) {
|
||||
SpellAbility triggerSA = t.ensureAbility().copy(sa.getActivatingPlayer());
|
||||
|
||||
Player decider = sa.getActivatingPlayer();
|
||||
if (t.hasParam("OptionalDecider")) {
|
||||
sa.setOptionalTrigger(true);
|
||||
decider = AbilityUtils.getDefinedPlayers(c, t.getParam("OptionalDecider"), sa).get(0);
|
||||
} else if (t.hasParam("Cost")) {
|
||||
sa.setOptionalTrigger(true);
|
||||
}
|
||||
|
||||
final WrappedAbility wrapperAbility = new WrappedAbility(t, triggerSA, decider);
|
||||
validSA.add(wrapperAbility);
|
||||
}
|
||||
}
|
||||
}
|
||||
sa.getActivatingPlayer().getController().orderAndPlaySimultaneousSa(validSA);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
package forge.game.ability.effects;
|
||||
|
||||
import forge.game.ability.AbilityFactory;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class RunSVarAbilityEffect extends SpellAbilityEffect {
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.card.abilityfactory.SpellEffect#resolve(forge.card.spellability.SpellAbility)
|
||||
*/
|
||||
@Override
|
||||
public void resolve(SpellAbility sa) {
|
||||
String sVars = sa.getParam("SVars");
|
||||
List<Card> cards = getTargetCards(sa);
|
||||
if (sVars == null || cards.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
List<SpellAbility> validSA = new ArrayList<>();
|
||||
final boolean isTrigger = sa.hasParam("IsTrigger");
|
||||
for (final Card tgtC : cards) {
|
||||
if (!tgtC.hasSVar(sVars)) {
|
||||
continue;
|
||||
}
|
||||
final SpellAbility actualSA = AbilityFactory.getAbility(tgtC.getSVar(sVars), tgtC);
|
||||
actualSA.setTrigger(isTrigger);
|
||||
actualSA.setActivatingPlayer(sa.getActivatingPlayer());
|
||||
actualSA.setDescription(tgtC.getName() + "'s ability");
|
||||
validSA.add(actualSA);
|
||||
}
|
||||
sa.getActivatingPlayer().getController().orderAndPlaySimultaneousSa(validSA);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -299,19 +299,20 @@ public class CardFactory {
|
||||
|
||||
private static void buildPlaneAbilities(Card card) {
|
||||
StringBuilder triggerSB = new StringBuilder();
|
||||
triggerSB.append("Mode$ PlanarDice | Result$ Planeswalk | TriggerZones$ Command | Execute$ RolledWalk | ");
|
||||
triggerSB.append("Secondary$ True | TriggerDescription$ Whenever you roll Planeswalk, put this card on the ");
|
||||
triggerSB.append("bottom of its owner's planar deck face down, then move the top card of your planar deck off ");
|
||||
triggerSB.append("that planar deck and turn it face up");
|
||||
triggerSB.append("Mode$ PlanarDice | Result$ Planeswalk | TriggerZones$ Command | Secondary$ True | ");
|
||||
triggerSB.append("TriggerDescription$ Whenever you roll Planeswalk, put this card on the bottom of its owner's planar deck face down, ");
|
||||
triggerSB.append("then move the top card of your planar deck off that planar deck and turn it face up");
|
||||
|
||||
String rolledWalk = "DB$ Planeswalk";
|
||||
|
||||
Trigger planesWalkTrigger = TriggerHandler.parseTrigger(triggerSB.toString(), card, true);
|
||||
planesWalkTrigger.setOverridingAbility(AbilityFactory.getAbility(rolledWalk, card));
|
||||
card.addTrigger(planesWalkTrigger);
|
||||
|
||||
StringBuilder saSB = new StringBuilder();
|
||||
saSB.append("AB$ RollPlanarDice | Cost$ X | SorcerySpeed$ True | Activator$ Player | ActivationZone$ Command | ");
|
||||
saSB.append("SpellDescription$ Roll the planar dice. X is equal to the amount of times the planar die has been rolled this turn.");
|
||||
|
||||
card.setSVar("RolledWalk", "DB$ Planeswalk | Cost$ 0");
|
||||
Trigger planesWalkTrigger = TriggerHandler.parseTrigger(triggerSB.toString(), card, true);
|
||||
card.addTrigger(planesWalkTrigger);
|
||||
|
||||
SpellAbility planarRoll = AbilityFactory.getAbility(saSB.toString(), card);
|
||||
planarRoll.setSVar("X", "Count$RolledThisTurn");
|
||||
|
||||
|
||||
@@ -99,7 +99,6 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
||||
private CardCollection splicedCards = null;
|
||||
|
||||
private boolean basicSpell = true;
|
||||
private boolean trigger = false;
|
||||
private Trigger triggerObj = null;
|
||||
private boolean optionalTrigger = false;
|
||||
private ReplacementEffect replacementEffect = null;
|
||||
@@ -938,10 +937,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
||||
}
|
||||
|
||||
public boolean isTrigger() {
|
||||
return trigger;
|
||||
}
|
||||
public void setTrigger(final boolean trigger0) {
|
||||
trigger = trigger0;
|
||||
return triggerObj != null;
|
||||
}
|
||||
|
||||
public Trigger getTrigger() {
|
||||
|
||||
@@ -539,7 +539,7 @@ public class TriggerHandler {
|
||||
sa.setLastStateBattlefield(game.getLastStateBattlefield());
|
||||
sa.setLastStateGraveyard(game.getLastStateGraveyard());
|
||||
|
||||
sa.setTrigger(true);
|
||||
sa.setTrigger(regtrig);
|
||||
sa.setSourceTrigger(regtrig.getId());
|
||||
regtrig.setTriggeringObjects(sa, runParams);
|
||||
sa.setTriggerRemembered(regtrig.getTriggerRemembered());
|
||||
@@ -560,8 +560,6 @@ public class TriggerHandler {
|
||||
|
||||
sa.setStackDescription(sa.toString());
|
||||
if (sa.getApi() == ApiType.Charm && !sa.isWrapper()) {
|
||||
// need to be set for demonic pact to look for chosen modes
|
||||
sa.setTrigger(regtrig);
|
||||
if (!CharmEffect.makeChoices(sa)) {
|
||||
// 603.3c If no mode is chosen, the ability is removed from the stack.
|
||||
return;
|
||||
|
||||
@@ -51,8 +51,8 @@ public class WrappedAbility extends Ability {
|
||||
public WrappedAbility(final Trigger regtrig0, final SpellAbility sa0, final Player decider0) {
|
||||
super(sa0.getHostCard(), ManaCost.ZERO, sa0.getView());
|
||||
setTrigger(regtrig0);
|
||||
setTrigger(true);
|
||||
sa = sa0;
|
||||
sa.setTrigger(regtrig0);
|
||||
decider = decider0;
|
||||
sa.setDescription(this.getStackDescription());
|
||||
}
|
||||
@@ -474,9 +474,6 @@ public class WrappedAbility extends Ability {
|
||||
}
|
||||
}
|
||||
|
||||
// set Trigger
|
||||
sa.setTrigger(regtrig);
|
||||
|
||||
if (decider != null && !decider.getController().confirmTrigger(this)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -5,11 +5,9 @@ T:Mode$ PlaneswalkedTo | ValidCard$ Card.Self | Execute$ PutCounter | TriggerDes
|
||||
T:Mode$ Phase | PreCombatMain$ True | ValidPlayer$ You | TriggerZones$ Command | Execute$ PutCounter | Secondary$ True | TriggerDescription$ When you planeswalk to CARDNAME or at the beginning of your precombat main phase, put a charge counter on CARDNAME, then add {R} for each charge counter on it.
|
||||
SVar:PutCounter:DB$PutCounter | Defined$ Self | CounterType$ CHARGE | CounterNum$ 1 | SubAbility$ DBMana
|
||||
SVar:DBMana:DB$ Mana | Produced$ R | Amount$ Y | References$ Y
|
||||
T:Mode$ PlanarDice | Result$ Chaos | TriggerZones$ Command | Execute$ DBPay | TriggerDescription$ Whenever you roll {CHAOS}, you may pay {X}. If you do, CARDNAME deals X damage to any target.
|
||||
SVar:DBPay:DB$ ChooseNumber | Defined$ TriggeredPlayer | ChooseAnyNumber$ True | ListTitle$ X to pay | SubAbility$ RolledChaos
|
||||
SVar:RolledChaos:DB$ DealDamage | UnlessCost$ ChosenNumber | UnlessPayer$ TriggeredPlayer | UnlessSwitched$ True | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | NumDmg$ PaidChaos | References$ PaidChaos
|
||||
SVar:PaidChaos:Count$ChosenNumber
|
||||
T:Mode$ PlanarDice | Result$ Chaos | TriggerZones$ Command | Execute$ RolledChaos | TriggerDescription$ Whenever you roll {CHAOS}, you may pay {X}. If you do, CARDNAME deals X damage to any target.
|
||||
SVar:RolledChaos:AB$ DealDamage | Cost$ X | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | NumDmg$ X | References$ X
|
||||
SVar:X:Count$xPaid
|
||||
SVar:Y:Count$CardCounters.CHARGE
|
||||
SVar:AIRollPlanarDieParams:Mode$ Always
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/kilnspire_district.jpg
|
||||
Oracle:When you planeswalk to Kilnspire District or at the beginning of your precombat main phase, put a charge counter on Kilnspire District, then add {R} for each charge counter on it.\nWhenever you roll {CHAOS}, you may pay {X}. If you do, Kilnspire District deals X damage to any target.
|
||||
|
||||
@@ -8,8 +8,7 @@ SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||
SVar:Y:Remembered$Amount
|
||||
T:Mode$ PlanarDice | Result$ Chaos | TriggerZones$ Command | Execute$ RolledChaos | TriggerDescription$ Whenever you roll {CHAOS}, reveal the top three cards of your planar deck. Each of the revealed cards' {CHAOS} abilities triggers. Then put the revealed cards on the bottom of your planar deck in any order.
|
||||
SVar:RolledChaos:DB$ Dig | DigNum$ 3 | NoMove$ True | Reveal$ True | SourceZone$ PlanarDeck | RememberRevealed$ True | SubAbility$ DBRunChaos
|
||||
SVar:DBRunChaos:DB$ RunSVarAbility | Defined$ Remembered | SVars$ RolledChaos | IsTrigger$ True | SubAbility$ DBChangeZone
|
||||
SVar:DBRunChaos:DB$ RunChaos | Defined$ Remembered | SubAbility$ DBChangeZone
|
||||
SVar:DBChangeZone:DB$ ChangeZoneAll | ChangeType$ Card.IsRemembered | Origin$ PlanarDeck | Destination$ PlanarDeck | LibraryPosition$ -1 | SubAbility$ DBCleanup
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/pools_of_becoming.jpg
|
||||
SVar:AIRollPlanarDieParams:Mode$ Always
|
||||
Oracle:At the beginning of your end step, put the cards in your hand on the bottom of your library in any order, then draw that many cards.\nWhenever you roll {CHAOS}, reveal the top three cards of your planar deck. Each of the revealed cards' {CHAOS} abilities triggers. Then put the revealed cards on the bottom of your planar deck in any order.
|
||||
|
||||
Reference in New Issue
Block a user