Merge pull request #4993 from Zzooouhh/Zzooouhh-woc

Implemented Word of Command (presumably buggy)
This commit is contained in:
LevelX2 2018-07-14 10:40:38 +02:00 committed by GitHub
commit 0d9f8ad19a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 443 additions and 17 deletions

View file

@ -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();

View file

@ -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

View file

@ -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)) {

View file

@ -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);

View file

@ -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

View file

@ -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;
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}
}

View file

@ -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);

View file

@ -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;