mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-17 19:28:01 +00:00
Add Word of Command
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
13
forge-gui/res/cardsfolder/w/word_of_command.txt
Normal file
13
forge-gui/res/cardsfolder/w/word_of_command.txt
Normal file
@@ -0,0 +1,13 @@
|
||||
Name:Word of Command
|
||||
ManaCost:B B
|
||||
Types:Instant
|
||||
A:SP$ RevealHand | ValidTgts$ Opponent | RememberRevealed$ True | SubAbility$ DBChoose | StackDescription$ SpellDescription | SpellDescription$ Look at target opponent's hand and choose a card from it. You control that player until CARDNAME finishes resolving. The player plays that card if able. While doing so, the player can activate mana abilities only if they're from lands that player controls and only if mana they produce is spent to activate other mana abilities of lands the player controls and/or to play that card. If the chosen card is cast as a spell, you control the player while that spell is resolving.
|
||||
SVar:DBChoose:DB$ ChooseCard | Defined$ You | Choices$ Card.IsRemembered | ChoiceZone$ Hand | Amount$ 1 | Mandatory$ True | SubAbility$ DBManaLandOnlyEffect
|
||||
SVar:DBManaLandOnlyEffect:DB$ Effect | EffectOwner$ TargetedPlayer | StaticAbilities$ DBLimitMana | ImprintOnHost$ True | Duration$ Permanent | SubAbility$ DBPlay
|
||||
SVar:DBLimitMana:Mode$ CantBeActivated | ValidSA$ Activated.nonLand,Activated.ManaAbilityCantPaidFor | EffectZone$ Command | Description$ While doing so, the player can activate mana abilities only if they're from lands that player controls and only if mana they produce is spent to activate other mana abilities of lands the player controls and/or to play that card.
|
||||
SVar:DBPlay:DB$ Play | Defined$ ChosenCard | Controller$ TargetedPlayer | ControlledByPlayer$ You | SubAbility$ DBExileEffect
|
||||
SVar:DBExileEffect:DB$ ChangeZone | Defined$ Imprinted | Origin$ Command | Destination$ Exile | SubAbility$ DBCleanup
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True | ClearImprinted$ True | ClearChosenCard$ True
|
||||
AI:RemoveDeck:All
|
||||
AI:RemoveDeck:Random
|
||||
Oracle:Look at target opponent's hand and choose a card from it. You control that player until Word of Command finishes resolving. The player plays that card if able. While doing so, the player can activate mana abilities only if they're from lands that player controls and only if mana they produce is spent to activate other mana abilities of lands the player controls and/or to play that card. If the chosen card is cast as a spell, you control the player while that spell is resolving.
|
||||
@@ -57,6 +57,9 @@ public abstract class InputPayMana extends InputSyncronizedBase {
|
||||
game = player.getGame();
|
||||
saPaidFor = saPaidFor0;
|
||||
|
||||
// Set current paid for SA for player to be able to reference it later
|
||||
player.pushPaidForSA(saPaidFor);
|
||||
|
||||
//if player is floating mana, show mana pool to make it easier to use that mana
|
||||
wasFloatingMana = !player.getManaPool().isEmpty();
|
||||
if (wasFloatingMana) {
|
||||
@@ -66,6 +69,9 @@ public abstract class InputPayMana extends InputSyncronizedBase {
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
// Clear current paid for SA
|
||||
player.popPaidForSA();
|
||||
|
||||
if (wasFloatingMana) { //hide mana pool if it was shown due to floating mana
|
||||
getController().getGui().hideManaPool(PlayerView.get(player));
|
||||
}
|
||||
@@ -351,6 +357,9 @@ public abstract class InputPayMana extends InputSyncronizedBase {
|
||||
}
|
||||
onManaAbilityPaid();
|
||||
onStateChanged();
|
||||
} else {
|
||||
// Need to call this to unlock
|
||||
onStateChanged();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user