Add Word of Command

This commit is contained in:
Lyu Zong-Hong
2021-03-27 12:25:20 +09:00
parent 407c742124
commit 2e768cb77b
7 changed files with 101 additions and 1 deletions

View File

@@ -7,6 +7,7 @@ import forge.game.card.Card;
import forge.game.card.CardState;
import forge.game.cost.Cost;
import forge.game.player.Player;
import forge.game.spellability.AbilityManaPart;
import forge.game.spellability.SpellAbility;
import forge.game.staticability.StaticAbility;
import forge.util.Expressions;
@@ -189,6 +190,18 @@ public class ForgeScript {
if (!Expressions.compare(y, property, x)) {
return false;
}
} else if (property.equals("ManaAbilityCantPaidFor")) {
if (!sa.isManaAbility()) {
return false;
}
SpellAbility paidFor = sourceController.getPaidForSA();
do {
AbilityManaPart mana = sa.getManaPart();
if (paidFor != null && mana.meetsManaRestrictions(paidFor) && !mana.getExpressChoice().isEmpty()) {
return false;
}
sa = sa.getSubAbility();
} while(sa != null);
} else if (sa.getHostCard() != null) {
return sa.getHostCard().hasProperty(property, sourceController, source, spellAbility);
}

View File

@@ -62,6 +62,8 @@ public class PlayEffect extends SpellAbilityEffect {
public void resolve(final SpellAbility sa) {
final Card source = sa.getHostCard();
Player activator = sa.getActivatingPlayer();
Player controlledByPlayer = null;
long controlledByTimeStamp = -1;
final Game game = activator.getGame();
final boolean optional = sa.hasParam("Optional");
boolean remember = sa.hasParam("RememberPlayed");
@@ -74,6 +76,11 @@ public class PlayEffect extends SpellAbilityEffect {
activator = AbilityUtils.getDefinedPlayers(source, sa.getParam("Controller"), sa).get(0);
}
if (sa.hasParam("ControlledByPlayer")) {
controlledByTimeStamp = game.getNextTimestamp();
controlledByPlayer = AbilityUtils.getDefinedPlayers(source, sa.getParam("ControlledByPlayer"), sa).get(0);
}
final Player controller = activator;
CardCollection tgtCards;
CardCollection showCards = new CardCollection();
@@ -164,13 +171,17 @@ public class PlayEffect extends SpellAbilityEffect {
amount = tgtCards.size();
}
if (controlledByPlayer != null) {
activator.addController(controlledByTimeStamp, controlledByPlayer);
}
final CardCollection saidNoTo = new CardCollection();
while (tgtCards.size() > saidNoTo.size() && saidNoTo.size() < amount && amount > 0) {
activator.getController().tempShowCards(showCards);
Card tgtCard = controller.getController().chooseSingleEntityForEffect(tgtCards, sa, Localizer.getInstance().getMessage("lblSelectCardToPlay"), null);
activator.getController().endTempShowCards();
if (tgtCard == null) {
return;
break;
}
final boolean wasFaceDown;
@@ -300,6 +311,9 @@ public class PlayEffect extends SpellAbilityEffect {
tgtSA.setSVar("IsCastFromPlayEffect", "True");
// Add controlled by player to target SA so when the spell is resolving, the controller would be changed again
tgtSA.setControlledByPlayer(controlledByTimeStamp, controlledByPlayer);
if (controller.getController().playSaFromPlayEffect(tgtSA)) {
if (remember) {
source.addRemembered(tgtSA.getHostCard());
@@ -317,6 +331,11 @@ public class PlayEffect extends SpellAbilityEffect {
amount--;
}
// Remove controlled by player if any
if (controlledByPlayer != null) {
activator.removeController(controlledByTimeStamp);
}
} // end resolve

View File

@@ -17,10 +17,12 @@
*/
package forge.game.player;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
@@ -219,6 +221,8 @@ public class Player extends GameEntity implements Comparable<Player> {
private CardCollection lostOwnership = new CardCollection();
private CardCollection gainedOwnership = new CardCollection();
private int numManaConversion = 0;
// The SA currently being paid for
private Deque<SpellAbility> paidForStack = new ArrayDeque<>();
private Card monarchEffect = null;
private Card blessingEffect = null;
@@ -3281,6 +3285,16 @@ public class Player extends GameEntity implements Comparable<Player> {
return view;
}
public SpellAbility getPaidForSA() {
return paidForStack.peek();
}
public void pushPaidForSA(SpellAbility sa) {
paidForStack.push(sa);
}
public void popPaidForSA() {
paidForStack.pop();
}
public boolean isMonarch() {
return equals(game.getMonarch());
}

View File

@@ -28,6 +28,7 @@ import java.util.Objects;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
@@ -108,6 +109,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
private ManaCost multiKickerManaCost = null;
private Player activatingPlayer = null;
private Player targetingPlayer = null;
private Pair<Long, Player> controlledByPlayer = null;
private SpellAbility grantorOriginal = null;
private StaticAbility grantorStatic = null;
@@ -454,6 +456,24 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
targetingPlayer = targetingPlayer0;
}
/**
* @return returns who controls the controller of this sa when it is resolving (for Word of Command effect). Null means not being controlled by other
*/
public Pair<Long, Player> getControlledByPlayer() {
return controlledByPlayer;
}
/**
* @param ts time stamp of the control player effect
* @param controller the player who will control the controller of this sa
*/
public void setControlledByPlayer(long ts, Player controller) {
if (controller != null) {
controlledByPlayer = Pair.of(ts, controller);
} else {
controlledByPlayer = null;
}
}
public boolean isSpell() { return false; }
public boolean isAbility() { return true; }
public boolean isActivatedAbility() { return false; }

View File

@@ -479,6 +479,11 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
game.copyLastState();
}
// Change controller of activating player if it was set in SA
if (sa.getControlledByPlayer() != null) {
sa.getActivatingPlayer().addController(sa.getControlledByPlayer().getLeft(), sa.getControlledByPlayer().getRight());
}
if (thisHasFizzled) { // Fizzle
if (sa.isBestow()) {
// 702.102d: if its target is illegal,
@@ -508,6 +513,13 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
// do creatures ETB from here?
}
// Change controller back if it was changed
if (sa.getControlledByPlayer() != null) {
sa.getActivatingPlayer().removeController(sa.getControlledByPlayer().getLeft());
// Cleanup controlled by player states
sa.setControlledByPlayer(-1, null);
}
game.fireEvent(new GameEventSpellResolved(sa, thisHasFizzled));
finishResolving(sa, thisHasFizzled);