mirror of
https://github.com/magefree/mage.git
synced 2025-12-24 20:41:58 -08: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
|
|
@ -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…
Add table
Add a link
Reference in a new issue