mirror of
https://github.com/correl/mage.git
synced 2025-01-12 19:25:44 +00:00
Merge pull request #4993 from Zzooouhh/Zzooouhh-woc
Implemented Word of Command (presumably buggy)
This commit is contained in:
commit
0d9f8ad19a
16 changed files with 443 additions and 17 deletions
|
@ -78,6 +78,7 @@ class DrainPowerEffect extends OneShotEffect {
|
|||
TargetPermanent target = null;
|
||||
|
||||
while (true) {
|
||||
targetPlayer.setPayManaMode(true);
|
||||
manaAbilitiesMap.clear();
|
||||
for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filter, targetPlayer.getId(), game)) {
|
||||
if (!ignorePermanents.contains(permanent)) {
|
||||
|
@ -138,6 +139,7 @@ class DrainPowerEffect extends OneShotEffect {
|
|||
}
|
||||
}
|
||||
}
|
||||
targetPlayer.setPayManaMode(false);
|
||||
|
||||
// 106.12. One card (Drain Power) causes one player to lose unspent mana and another to add “the mana lost this way.” (Note that these may be the same player.)
|
||||
// This empties the former player's mana pool and causes the mana emptied this way to be put into the latter player's mana pool. Which permanents, spells, and/or
|
||||
|
|
251
Mage.Sets/src/mage/cards/w/WordOfCommand.java
Normal file
251
Mage.Sets/src/mage/cards/w/WordOfCommand.java
Normal file
|
@ -0,0 +1,251 @@
|
|||
|
||||
package mage.cards.w;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageObject;
|
||||
import mage.MageObjectReference;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.SpellAbility;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.effects.AsThoughEffect;
|
||||
import mage.abilities.effects.AsThoughEffectImpl;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.RestrictionEffect;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.AsThoughEffectType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.game.stack.Spell;
|
||||
import mage.players.ManaPool;
|
||||
import mage.players.Player;
|
||||
import mage.target.TargetCard;
|
||||
import mage.target.common.TargetOpponent;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author L_J
|
||||
*/
|
||||
public final class WordOfCommand extends CardImpl {
|
||||
|
||||
public WordOfCommand(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{B}{B}");
|
||||
|
||||
// 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.
|
||||
this.getSpellAbility().addEffect(new WordOfCommandEffect());
|
||||
this.getSpellAbility().addTarget(new TargetOpponent());
|
||||
}
|
||||
|
||||
public WordOfCommand(final WordOfCommand card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WordOfCommand copy() {
|
||||
return new WordOfCommand(this);
|
||||
}
|
||||
}
|
||||
|
||||
class WordOfCommandEffect extends OneShotEffect {
|
||||
|
||||
public WordOfCommandEffect() {
|
||||
super(Outcome.GainControl);
|
||||
this.staticText = "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";
|
||||
}
|
||||
|
||||
public WordOfCommandEffect(final WordOfCommandEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WordOfCommandEffect copy() {
|
||||
return new WordOfCommandEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player sourceController = game.getPlayer(source.getControllerId());
|
||||
Player targetPlayer = game.getPlayer(source.getFirstTarget());
|
||||
MageObject sourceObject = game.getObject(source.getSourceId());
|
||||
Card card = null;
|
||||
if (sourceController != null && targetPlayer != null && sourceObject != null) {
|
||||
Player controller = null;
|
||||
Spell wordOfCommand = game.getSpell(source.getSourceId());
|
||||
if (wordOfCommand != null) {
|
||||
if (wordOfCommand.getCommandedBy() != null) {
|
||||
controller = game.getPlayer(wordOfCommand.getCommandedBy());
|
||||
} else {
|
||||
controller = game.getPlayer(sourceController.getTurnControlledBy());
|
||||
}
|
||||
}
|
||||
if (controller == null) {
|
||||
controller = sourceController; // reset the controller to avoid NPE
|
||||
}
|
||||
|
||||
// Look at target opponent's hand and choose a card from it
|
||||
TargetCard targetCard = new TargetCard(Zone.HAND, new FilterCard());
|
||||
if (controller.choose(Outcome.Discard, targetPlayer.getHand(), targetCard, game)) {
|
||||
card = game.getCard(targetCard.getFirstTarget());
|
||||
}
|
||||
|
||||
// You control that player until Word of Command finishes resolving
|
||||
controller.controlPlayersTurn(game, targetPlayer.getId());
|
||||
while (controller != null && controller.canRespond()) {
|
||||
if (controller.chooseUse(Outcome.Benefit, "Resolve " + sourceObject.getLogName() + " now" + (card != null ? " and play " + card.getLogName() : "") + '?', source, game)) {
|
||||
// this is used to give the controller a little space to utilize his player controlling effect (look at face down creatures, hand, etc.)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// The player plays that card if able
|
||||
if (card != null) {
|
||||
// While doing so, the player can activate mana abilities only if they're from lands that player controls
|
||||
RestrictionEffect effect = new WordOfCommandCantActivateEffect();
|
||||
effect.setTargetPointer(new FixedTarget(targetPlayer.getId()));
|
||||
game.addEffect(effect, source);
|
||||
|
||||
// and only if mana they produce is spent to activate other mana abilities of lands he or she controls and/or play that card
|
||||
ManaPool manaPool = targetPlayer.getManaPool();
|
||||
manaPool.setForcedToPay(true);
|
||||
manaPool.storeMana();
|
||||
int bookmark = game.bookmarkState();
|
||||
|
||||
boolean canPlay = checkPlayability(card, targetPlayer, game, source);
|
||||
while (canPlay
|
||||
&& targetPlayer.canRespond()
|
||||
&& !targetPlayer.playCard(card, game, false, true, new MageObjectReference(source.getSourceObject(game), game))) {
|
||||
SpellAbility spellAbility = card.getSpellAbility();
|
||||
if (spellAbility != null) {
|
||||
spellAbility.getManaCostsToPay().clear();
|
||||
spellAbility.getManaCostsToPay().addAll(spellAbility.getManaCosts());
|
||||
((ManaCostsImpl) spellAbility.getManaCostsToPay()).forceManaRollback(game, manaPool); // force rollback if card was deemed playable
|
||||
canPlay = checkPlayability(card, targetPlayer, game, source);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!canPlay) {
|
||||
game.informPlayers(targetPlayer.getLogName() + " didn't play " + card.getLogName() + " (card can't be played)");
|
||||
}
|
||||
|
||||
manaPool.setForcedToPay(false); // duplicate in case of a new mana pool existing - probably not necessary, but just in case
|
||||
manaPool = targetPlayer.getManaPool(); // a rollback creates a new mana pool for the player, so it's necessary to find it again
|
||||
manaPool.setForcedToPay(false);
|
||||
game.removeBookmark(bookmark);
|
||||
targetPlayer.resetStoredBookmark(game);
|
||||
for (RestrictionEffect eff : game.getContinuousEffects().getRestrictionEffects()) {
|
||||
if (eff instanceof WordOfCommandCantActivateEffect) {
|
||||
eff.discard();
|
||||
break;
|
||||
}
|
||||
}
|
||||
game.getContinuousEffects().removeInactiveEffects(game);
|
||||
Spell spell = game.getSpell(card.getId());
|
||||
if (spell != null) {
|
||||
spell.setCommandedBy(controller.getId()); // If the chosen card is cast as a spell, you control the player while that spell is resolving
|
||||
}
|
||||
}
|
||||
|
||||
wordOfCommand = game.getSpell(source.getSourceId());
|
||||
if (wordOfCommand != null) {
|
||||
wordOfCommand.setCommandedBy(controller.getId()); // You control the player until Word of Command finishes resolving
|
||||
} else {
|
||||
targetPlayer.setGameUnderYourControl(true, false);
|
||||
if (!targetPlayer.getTurnControlledBy().equals(controller.getId())) {
|
||||
controller.getPlayersUnderYourControl().remove(targetPlayer.getId());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean checkPlayability(Card card, Player targetPlayer, Game game, Ability source) {
|
||||
// check for card playability
|
||||
boolean canPlay = false;
|
||||
if (card.isLand()) { // we can't use getPlayableInHand(game) in here because it disallows playing lands outside the main step
|
||||
if (targetPlayer.canPlayLand()
|
||||
&& game.getActivePlayerId().equals(targetPlayer.getId())) {
|
||||
canPlay = true;
|
||||
for (Ability ability : card.getAbilities(game)) {
|
||||
if (!game.getContinuousEffects().preventedByRuleModification(GameEvent.getEvent(GameEvent.EventType.PLAY_LAND, ability.getSourceId(), ability.getSourceId(), targetPlayer.getId()), ability, game, true)) {
|
||||
canPlay &= true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else { // Word of Command allows the chosen card to be played "as if it had flash" so we need to invoke such effect to bypass the check
|
||||
AsThoughEffectImpl effect2 = new WordOfCommandTestFlashEffect();
|
||||
game.addEffect(effect2, source);
|
||||
if (targetPlayer.getPlayableInHand(game).contains(card.getId())) {
|
||||
canPlay = true;
|
||||
}
|
||||
for (AsThoughEffect eff : game.getContinuousEffects().getApplicableAsThoughEffects(AsThoughEffectType.CAST_AS_INSTANT, game)) {
|
||||
if (eff instanceof WordOfCommandTestFlashEffect) {
|
||||
eff.discard();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return canPlay;
|
||||
}
|
||||
}
|
||||
|
||||
class WordOfCommandCantActivateEffect extends RestrictionEffect {
|
||||
|
||||
public WordOfCommandCantActivateEffect() {
|
||||
super(Duration.EndOfTurn);
|
||||
}
|
||||
|
||||
public WordOfCommandCantActivateEffect(final WordOfCommandCantActivateEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WordOfCommandCantActivateEffect copy() {
|
||||
return new WordOfCommandCantActivateEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(Permanent permanent, Ability source, Game game) {
|
||||
return !permanent.isLand() && permanent.getControllerId().equals(this.targetPointer.getFirst(game, source));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canUseActivatedAbilities(Permanent permanent, Ability source, Game game) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class WordOfCommandTestFlashEffect extends AsThoughEffectImpl {
|
||||
|
||||
public WordOfCommandTestFlashEffect() {
|
||||
super(AsThoughEffectType.CAST_AS_INSTANT, Duration.EndOfTurn, Outcome.Benefit);
|
||||
}
|
||||
|
||||
public WordOfCommandTestFlashEffect(final WordOfCommandTestFlashEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WordOfCommandTestFlashEffect copy() {
|
||||
return new WordOfCommandTestFlashEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(UUID affectedSpellId, Ability source, UUID affectedControllerId, Game game) {
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -309,6 +309,7 @@ public final class LimitedEditionAlpha extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Will-o'-the-Wisp", 135, Rarity.RARE, mage.cards.w.WillOTheWisp.class));
|
||||
cards.add(new SetCardInfo("Winter Orb", 275, Rarity.RARE, mage.cards.w.WinterOrb.class));
|
||||
cards.add(new SetCardInfo("Wooden Sphere", 276, Rarity.UNCOMMON, mage.cards.w.WoodenSphere.class));
|
||||
cards.add(new SetCardInfo("Word of Command", 136, Rarity.RARE, mage.cards.w.WordOfCommand.class));
|
||||
cards.add(new SetCardInfo("Wrath of God", 45, Rarity.RARE, mage.cards.w.WrathOfGod.class));
|
||||
cards.add(new SetCardInfo("Zombie Master", 137, Rarity.RARE, mage.cards.z.ZombieMaster.class));
|
||||
}
|
||||
|
|
|
@ -316,6 +316,7 @@ public final class LimitedEditionBeta extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Will-o'-the-Wisp", 136, Rarity.RARE, mage.cards.w.WillOTheWisp.class));
|
||||
cards.add(new SetCardInfo("Winter Orb", 276, Rarity.RARE, mage.cards.w.WinterOrb.class));
|
||||
cards.add(new SetCardInfo("Wooden Sphere", 277, Rarity.UNCOMMON, mage.cards.w.WoodenSphere.class));
|
||||
cards.add(new SetCardInfo("Word of Command", 137, Rarity.RARE, mage.cards.w.WordOfCommand.class));
|
||||
cards.add(new SetCardInfo("Wrath of God", 46, Rarity.RARE, mage.cards.w.WrathOfGod.class));
|
||||
cards.add(new SetCardInfo("Zombie Master", 138, Rarity.RARE, mage.cards.z.ZombieMaster.class));
|
||||
}
|
||||
|
|
|
@ -298,6 +298,7 @@ public final class MastersEditionIV extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Wild Griffin", 35, Rarity.COMMON, mage.cards.w.WildGriffin.class));
|
||||
cards.add(new SetCardInfo("Wild Ox", 174, Rarity.UNCOMMON, mage.cards.w.WildOx.class));
|
||||
cards.add(new SetCardInfo("Wood Elemental", 175, Rarity.RARE, mage.cards.w.WoodElemental.class));
|
||||
cards.add(new SetCardInfo("Word of Command", 103, Rarity.RARE, mage.cards.w.WordOfCommand.class));
|
||||
cards.add(new SetCardInfo("Xenic Poltergeist", 104, Rarity.UNCOMMON, mage.cards.x.XenicPoltergeist.class));
|
||||
cards.add(new SetCardInfo("Yotian Soldier", 240, Rarity.COMMON, mage.cards.y.YotianSoldier.class));
|
||||
cards.add(new SetCardInfo("Zombie Master", 105, Rarity.UNCOMMON, mage.cards.z.ZombieMaster.class));
|
||||
|
|
|
@ -316,6 +316,7 @@ public final class UnlimitedEdition extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Will-o'-the-Wisp", 136, Rarity.RARE, mage.cards.w.WillOTheWisp.class));
|
||||
cards.add(new SetCardInfo("Winter Orb", 276, Rarity.RARE, mage.cards.w.WinterOrb.class));
|
||||
cards.add(new SetCardInfo("Wooden Sphere", 277, Rarity.UNCOMMON, mage.cards.w.WoodenSphere.class));
|
||||
cards.add(new SetCardInfo("Word of Command", 137, Rarity.RARE, mage.cards.w.WordOfCommand.class));
|
||||
cards.add(new SetCardInfo("Wrath of God", 46, Rarity.RARE, mage.cards.w.WrathOfGod.class));
|
||||
cards.add(new SetCardInfo("Zombie Master", 138, Rarity.RARE, mage.cards.z.ZombieMaster.class));
|
||||
}
|
||||
|
|
|
@ -207,7 +207,9 @@ public abstract class ManaCostImpl extends CostImpl implements ManaCost {
|
|||
return true;
|
||||
}
|
||||
Player player = game.getPlayer(controllerId);
|
||||
assignPayment(game, ability, player.getManaPool(), costToPay);
|
||||
if (!player.getManaPool().isForcedToPay()) {
|
||||
assignPayment(game, ability, player.getManaPool(), costToPay);
|
||||
}
|
||||
game.getState().getSpecialActions().removeManaActions();
|
||||
while (!isPaid()) {
|
||||
ManaCost unpaid = this.getUnpaid();
|
||||
|
|
|
@ -117,7 +117,9 @@ public class ManaCostsImpl<T extends ManaCost> extends ArrayList<T> implements M
|
|||
}
|
||||
|
||||
Player player = game.getPlayer(controllerId);
|
||||
assignPayment(game, ability, player.getManaPool(), this);
|
||||
if (!player.getManaPool().isForcedToPay()) {
|
||||
assignPayment(game, ability, player.getManaPool(), this);
|
||||
}
|
||||
game.getState().getSpecialActions().removeManaActions();
|
||||
while (!isPaid()) {
|
||||
ManaCost unpaid = this.getUnpaid();
|
||||
|
@ -235,10 +237,15 @@ public class ManaCostsImpl<T extends ManaCost> extends ArrayList<T> implements M
|
|||
|
||||
@Override
|
||||
public void assignPayment(Game game, Ability ability, ManaPool pool, Cost costToPay) {
|
||||
if (!pool.isAutoPayment() && pool.getUnlockedManaType() == null) {
|
||||
boolean wasUnlockedManaType = (pool.getUnlockedManaType() != null);
|
||||
if (!pool.isAutoPayment() && !wasUnlockedManaType) {
|
||||
// if auto payment is inactive and no mana type was clicked manually - do nothing
|
||||
return;
|
||||
}
|
||||
ManaCosts referenceCosts = null;
|
||||
if (pool.isForcedToPay()) {
|
||||
referenceCosts = this.copy();
|
||||
}
|
||||
// attempt to pay colorless costs (not generic) mana costs first
|
||||
for (ManaCost cost : this) {
|
||||
if (!cost.isPaid() && cost instanceof ColorlessManaCost) {
|
||||
|
@ -318,6 +325,31 @@ public class ManaCostsImpl<T extends ManaCost> extends ArrayList<T> implements M
|
|||
}
|
||||
// stop using mana of the clicked mana type
|
||||
pool.lockManaType();
|
||||
if (!wasUnlockedManaType) {
|
||||
handleForcedToPayOnlyForCurrentPayment(game, pool, referenceCosts);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleForcedToPayOnlyForCurrentPayment(Game game, ManaPool pool, ManaCosts referenceCosts) {
|
||||
// for Word of Command
|
||||
if (pool.isForcedToPay()) {
|
||||
if (referenceCosts != null && this.getText().equals(referenceCosts.getText())) {
|
||||
UUID playerId = pool.getPlayerId();
|
||||
Player player = game.getPlayer(playerId);
|
||||
if (player != null) {
|
||||
game.undo(playerId);
|
||||
this.clearPaid();
|
||||
this.setX(referenceCosts.getX());
|
||||
player.getManaPool().restoreMana(pool.getPoolBookmark());
|
||||
game.bookmarkState();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void forceManaRollback(Game game, ManaPool pool) {
|
||||
// for Word of Command
|
||||
handleForcedToPayOnlyForCurrentPayment(game, pool, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -543,7 +543,7 @@ public class ContinuousEffects implements Serializable {
|
|||
* @param game
|
||||
* @return
|
||||
*/
|
||||
private List<AsThoughEffect> getApplicableAsThoughEffects(AsThoughEffectType type, Game game) {
|
||||
public List<AsThoughEffect> getApplicableAsThoughEffects(AsThoughEffectType type, Game game) {
|
||||
List<AsThoughEffect> asThoughEffectsList = new ArrayList<>();
|
||||
if (asThoughEffectsMap.containsKey(type)) {
|
||||
for (AsThoughEffect effect : asThoughEffectsMap.get(type)) {
|
||||
|
|
|
@ -398,6 +398,8 @@ public interface Game extends MageItem, Serializable {
|
|||
|
||||
void playPriority(UUID activePlayerId, boolean resuming);
|
||||
|
||||
void resetControlAfterSpellResolve(UUID topId);
|
||||
|
||||
boolean endTurn(Ability source);
|
||||
|
||||
int doAction(MageAction action);
|
||||
|
|
|
@ -1395,6 +1395,7 @@ public abstract class GameImpl implements Game, Serializable {
|
|||
try {
|
||||
top = state.getStack().peek();
|
||||
top.resolve(this);
|
||||
resetControlAfterSpellResolve(top.getId());
|
||||
} finally {
|
||||
if (top != null) {
|
||||
state.getStack().remove(top, this); // seems partly redundant because move card from stack to grave is already done and the stack removed
|
||||
|
@ -1408,6 +1409,37 @@ public abstract class GameImpl implements Game, Serializable {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetControlAfterSpellResolve(UUID topId) {
|
||||
// for Word of Command
|
||||
Spell spell = getSpellOrLKIStack(topId);
|
||||
if (spell != null) {
|
||||
if (spell.getCommandedBy() != null) {
|
||||
UUID commandedBy = spell.getCommandedBy();
|
||||
UUID spellControllerId = null;
|
||||
if (commandedBy.equals(spell.getControllerId())) {
|
||||
spellControllerId = spell.getSpellAbility().getFirstTarget(); // i.e. resolved spell is Word of Command
|
||||
} else {
|
||||
spellControllerId = spell.getControllerId(); // i.e. resolved spell is the target opponent's spell
|
||||
}
|
||||
if (commandedBy != null && spellControllerId != null) {
|
||||
Player turnController = getPlayer(commandedBy);
|
||||
if (turnController != null) {
|
||||
Player targetPlayer = getPlayer(spellControllerId);
|
||||
if (targetPlayer != null) {
|
||||
targetPlayer.setGameUnderYourControl(true, false);
|
||||
informPlayers(turnController.getLogName() + " lost control over " + targetPlayer.getLogName());
|
||||
if (targetPlayer.getTurnControlledBy().equals(turnController.getId())) {
|
||||
turnController.getPlayersUnderYourControl().remove(targetPlayer.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
spell.setCommandedBy(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This checks if the stack gets filled iterated, without ever getting empty
|
||||
|
|
|
@ -67,6 +67,7 @@ public class Spell extends StackObjImpl implements Card {
|
|||
private boolean faceDown;
|
||||
private boolean countered;
|
||||
private boolean resolving = false;
|
||||
private UUID commandedBy = null; // for Word of Command
|
||||
|
||||
private boolean doneActivatingManaAbilities; // if this is true, the player is no longer allowed to pay the spell costs with activating of mana abilies
|
||||
|
||||
|
@ -121,6 +122,7 @@ public class Spell extends StackObjImpl implements Card {
|
|||
this.faceDown = spell.faceDown;
|
||||
this.countered = spell.countered;
|
||||
this.resolving = spell.resolving;
|
||||
this.commandedBy = spell.commandedBy;
|
||||
|
||||
this.doneActivatingManaAbilities = spell.doneActivatingManaAbilities;
|
||||
this.targetChanged = spell.targetChanged;
|
||||
|
@ -179,6 +181,12 @@ public class Spell extends StackObjImpl implements Card {
|
|||
return false;
|
||||
}
|
||||
this.resolving = true;
|
||||
if (commandedBy != null && !commandedBy.equals(getControllerId())) {
|
||||
Player turnController = game.getPlayer(commandedBy);
|
||||
if (turnController != null) {
|
||||
turnController.controlPlayersTurn(game, controller.getId());
|
||||
}
|
||||
}
|
||||
if (this.isInstant() || this.isSorcery()) {
|
||||
int index = 0;
|
||||
result = false;
|
||||
|
@ -1024,4 +1032,12 @@ public class Spell extends StackObjImpl implements Card {
|
|||
throw new UnsupportedOperationException("Not supported."); //To change body of generated methods, choose Tools | Templates.
|
||||
}
|
||||
|
||||
public void setCommandedBy(UUID playerId) {
|
||||
this.commandedBy = playerId;
|
||||
}
|
||||
|
||||
public UUID getCommandedBy() {
|
||||
return commandedBy;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -37,10 +37,11 @@ public class SpellStack extends ArrayDeque<StackObject> {
|
|||
try {
|
||||
top = this.peek();
|
||||
top.resolve(game);
|
||||
game.resetControlAfterSpellResolve(top.getId());
|
||||
} finally {
|
||||
if (top != null) {
|
||||
if (contains(top)) {
|
||||
logger.warn("StackObject was still on the stack after resoving" + top.getName());
|
||||
logger.warn("StackObject was still on the stack after resolving" + top.getName());
|
||||
this.remove(top, game);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,6 +35,8 @@ public class ManaPool implements Serializable {
|
|||
private boolean autoPayment; // auto payment from mana pool: true - mode is active
|
||||
private boolean autoPaymentRestricted; // auto payment from mana pool: true - if auto Payment is on, it will only pay if one kind of mana is in the pool
|
||||
private ManaType unlockedManaType; // type of mana that was selected to pay manually
|
||||
private boolean forcedToPay; // for Word of Command
|
||||
private final List<ManaPoolItem> poolBookmark = new ArrayList<>(); // mana pool bookmark for rollback purposes
|
||||
|
||||
private final Set<ManaType> doNotEmptyManaTypes = new HashSet<>();
|
||||
|
||||
|
@ -43,6 +45,7 @@ public class ManaPool implements Serializable {
|
|||
autoPayment = true;
|
||||
autoPaymentRestricted = true;
|
||||
unlockedManaType = null;
|
||||
forcedToPay = false;
|
||||
}
|
||||
|
||||
public ManaPool(final ManaPool pool) {
|
||||
|
@ -53,6 +56,10 @@ public class ManaPool implements Serializable {
|
|||
this.autoPayment = pool.autoPayment;
|
||||
this.autoPaymentRestricted = pool.autoPaymentRestricted;
|
||||
this.unlockedManaType = pool.unlockedManaType;
|
||||
this.forcedToPay = pool.forcedToPay;
|
||||
for (ManaPoolItem item : pool.poolBookmark) {
|
||||
poolBookmark.add(item.copy());
|
||||
}
|
||||
this.doNotEmptyManaTypes.addAll(pool.doNotEmptyManaTypes);
|
||||
}
|
||||
|
||||
|
@ -87,12 +94,12 @@ public class ManaPool implements Serializable {
|
|||
* @return
|
||||
*/
|
||||
public boolean pay(ManaType manaType, Ability ability, Filter filter, Game game, Cost costToPay, Mana usedManaToPay) {
|
||||
if (!autoPayment && manaType != unlockedManaType) {
|
||||
if (!isAutoPayment() && manaType != unlockedManaType) {
|
||||
// if manual payment and the needed mana type was not unlocked, nothing will be paid
|
||||
return false;
|
||||
}
|
||||
ManaType possibleAsThoughtPoolManaType = null;
|
||||
if (autoPayment && autoPaymentRestricted && !wasManaAddedBeyondStock() && manaType != unlockedManaType) {
|
||||
if (isAutoPayment() && isAutoPaymentRestricted() && !wasManaAddedBeyondStock() && manaType != unlockedManaType) {
|
||||
// if automatic restricted payment and there is already mana in the pool
|
||||
// and the needed mana type was not unlocked, nothing will be paid
|
||||
if (unlockedManaType != null) {
|
||||
|
@ -111,6 +118,7 @@ public class ManaPool implements Serializable {
|
|||
lockManaType(); // pay only one mana if mana payment is set to manually
|
||||
return true;
|
||||
}
|
||||
|
||||
for (ManaPoolItem mana : manaItems) {
|
||||
if (filter != null) {
|
||||
if (!filter.match(mana.getSourceObject(), game)) {
|
||||
|
@ -120,7 +128,7 @@ public class ManaPool implements Serializable {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (possibleAsThoughtPoolManaType == null && manaType != unlockedManaType && autoPayment && autoPaymentRestricted && mana.count() == mana.getStock()) {
|
||||
if (possibleAsThoughtPoolManaType == null && manaType != unlockedManaType && isAutoPayment() && isAutoPaymentRestricted() && mana.count() == mana.getStock()) {
|
||||
// no mana added beyond the stock so don't auto pay this
|
||||
continue;
|
||||
}
|
||||
|
@ -436,19 +444,19 @@ public class ManaPool implements Serializable {
|
|||
}
|
||||
|
||||
public boolean isAutoPayment() {
|
||||
return autoPayment;
|
||||
return autoPayment || forcedToPay;
|
||||
}
|
||||
|
||||
public void setAutoPayment(boolean autoPayment) {
|
||||
this.autoPayment = autoPayment;
|
||||
}
|
||||
|
||||
public void setAutoPaymentRestricted(boolean autoPaymentRestricted) {
|
||||
this.autoPaymentRestricted = autoPaymentRestricted;
|
||||
public boolean isAutoPaymentRestricted() {
|
||||
return autoPaymentRestricted || forcedToPay;
|
||||
}
|
||||
|
||||
public boolean isAutoPaymentRestricted() {
|
||||
return autoPaymentRestricted;
|
||||
public void setAutoPaymentRestricted(boolean autoPaymentRestricted) {
|
||||
this.autoPaymentRestricted = autoPaymentRestricted;
|
||||
}
|
||||
|
||||
public ManaType getUnlockedManaType() {
|
||||
|
@ -490,4 +498,39 @@ public class ManaPool implements Serializable {
|
|||
return itemsCopy;
|
||||
}
|
||||
|
||||
public void setForcedToPay(boolean forcedToPay) {
|
||||
this.forcedToPay = forcedToPay;
|
||||
}
|
||||
|
||||
public boolean isForcedToPay() {
|
||||
return forcedToPay;
|
||||
}
|
||||
|
||||
public UUID getPlayerId() {
|
||||
return playerId;
|
||||
}
|
||||
|
||||
public void storeMana() {
|
||||
poolBookmark.clear();
|
||||
poolBookmark.addAll(getManaItems());
|
||||
}
|
||||
|
||||
public List<ManaPoolItem> getPoolBookmark() {
|
||||
List<ManaPoolItem> itemsCopy = new ArrayList<>();
|
||||
for (ManaPoolItem manaItem : poolBookmark) {
|
||||
itemsCopy.add(manaItem.copy());
|
||||
}
|
||||
return itemsCopy;
|
||||
}
|
||||
|
||||
public void restoreMana(List<ManaPoolItem> manaList) {
|
||||
manaItems.clear();
|
||||
if (!manaList.isEmpty()) {
|
||||
List<ManaPoolItem> itemsCopy = new ArrayList<>();
|
||||
for (ManaPoolItem manaItem : manaList) {
|
||||
itemsCopy.add(manaItem.copy());
|
||||
}
|
||||
manaItems.addAll(itemsCopy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -278,6 +278,8 @@ public interface Player extends MageItem, Copyable<Player> {
|
|||
*/
|
||||
void setTurnControlledBy(UUID playerId);
|
||||
|
||||
List<UUID> getTurnControllers();
|
||||
|
||||
UUID getTurnControlledBy();
|
||||
|
||||
/**
|
||||
|
@ -305,6 +307,8 @@ public interface Player extends MageItem, Copyable<Player> {
|
|||
*/
|
||||
void setGameUnderYourControl(boolean value);
|
||||
|
||||
void setGameUnderYourControl(boolean value, boolean fullRestore);
|
||||
|
||||
boolean isTestMode();
|
||||
|
||||
void setTestMode(boolean value);
|
||||
|
@ -856,6 +860,8 @@ public interface Player extends MageItem, Copyable<Player> {
|
|||
|
||||
Set<UUID> getUsersAllowedToSeeHandCards();
|
||||
|
||||
void setPayManaMode(boolean payManaMode);
|
||||
|
||||
boolean isInPayManaMode();
|
||||
|
||||
void setMatchPlayer(MatchPlayer matchPlayer);
|
||||
|
|
|
@ -155,6 +155,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
|
||||
protected boolean isGameUnderControl = true;
|
||||
protected UUID turnController;
|
||||
protected List<UUID> turnControllers = new ArrayList<>();
|
||||
protected Set<UUID> playersUnderYourControl = new HashSet<>();
|
||||
|
||||
protected Set<UUID> usersAllowedToSeeHandCards = new HashSet<>();
|
||||
|
@ -262,6 +263,8 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
this.isGameUnderControl = player.isGameUnderControl;
|
||||
|
||||
this.turnController = player.turnController;
|
||||
this.turnControllers.clear();
|
||||
this.turnControllers.addAll(player.turnControllers);
|
||||
|
||||
this.passed = player.passed;
|
||||
this.passedTurn = player.passedTurn;
|
||||
|
@ -342,6 +345,8 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
this.isGameUnderControl = player.isGameUnderControl();
|
||||
|
||||
this.turnController = player.getTurnControlledBy();
|
||||
this.turnControllers.clear();
|
||||
this.turnControllers.addAll(player.getTurnControllers());
|
||||
this.reachedNextTurnAfterLeaving = player.hasReachedNextTurnAfterLeaving();
|
||||
this.castSourceIdWithAlternateMana = player.getCastSourceIdWithAlternateMana();
|
||||
this.castSourceIdManaCosts = player.getCastSourceIdManaCosts();
|
||||
|
@ -397,6 +402,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
this.turns = 0;
|
||||
this.isGameUnderControl = true;
|
||||
this.turnController = this.getId();
|
||||
this.turnControllers.clear();
|
||||
this.playersUnderYourControl.clear();
|
||||
|
||||
this.passed = false;
|
||||
|
@ -523,13 +529,13 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
|
||||
@Override
|
||||
public void controlPlayersTurn(Game game, UUID playerId) {
|
||||
Player player = game.getPlayer(playerId);
|
||||
player.setTurnControlledBy(this.getId());
|
||||
game.informPlayers(getLogName() + " controls the turn of " + player.getLogName());
|
||||
if (!playerId.equals(this.getId())) {
|
||||
this.playersUnderYourControl.add(playerId);
|
||||
Player player = game.getPlayer(playerId);
|
||||
if (!player.hasLeft() && !player.hasLost()) {
|
||||
player.setGameUnderYourControl(false);
|
||||
player.setTurnControlledBy(this.getId());
|
||||
game.informPlayers(getLogName() + " controls the turn of " + player.getLogName());
|
||||
}
|
||||
DelayedTriggeredAbility ability = new AtTheEndOfTurnStepPostDelayedTriggeredAbility(new LoseControlOnOtherPlayersControllerEffect(this.getLogName(), player.getLogName()));
|
||||
ability.setSourceId(getId());
|
||||
|
@ -541,6 +547,12 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
@Override
|
||||
public void setTurnControlledBy(UUID playerId) {
|
||||
this.turnController = playerId;
|
||||
this.turnControllers.add(playerId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UUID> getTurnControllers() {
|
||||
return this.turnControllers;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -566,9 +578,27 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
|
||||
@Override
|
||||
public void setGameUnderYourControl(boolean value) {
|
||||
setGameUnderYourControl(value, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGameUnderYourControl(boolean value, boolean fullRestore) {
|
||||
this.isGameUnderControl = value;
|
||||
if (isGameUnderControl) {
|
||||
this.turnController = getId();
|
||||
if (fullRestore) {
|
||||
this.turnControllers.clear();
|
||||
this.turnController = getId();
|
||||
} else {
|
||||
if (turnControllers.size() > 0) {
|
||||
this.turnControllers.remove(turnControllers.size() - 1);
|
||||
}
|
||||
if (turnControllers.isEmpty()) {
|
||||
this.turnController = getId();
|
||||
} else {
|
||||
this.turnController = turnControllers.get(turnControllers.size() - 1);
|
||||
isGameUnderControl = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -983,6 +1013,11 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
return castSourceIdManaCosts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPayManaMode(boolean payManaMode) {
|
||||
this.payManaMode = payManaMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInPayManaMode() {
|
||||
return payManaMode;
|
||||
|
|
Loading…
Reference in a new issue