mirror of
https://github.com/correl/mage.git
synced 2024-12-26 03:00:11 +00:00
Fix handling of multiple simultaneous control changing effects
This commit is contained in:
parent
c519814f03
commit
bba23e05cb
5 changed files with 53 additions and 39 deletions
|
@ -701,11 +701,22 @@ public class ContinuousEffects implements Serializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
layer = filterLayeredEffects(layerEffects, Layer.ControlChangingEffects_2);
|
layer = filterLayeredEffects(layerEffects, Layer.ControlChangingEffects_2);
|
||||||
for (ContinuousEffect effect: layer) {
|
// apply control changing effects multiple times if it's needed
|
||||||
HashSet<Ability> abilities = layeredEffects.getAbility(effect.getId());
|
// for cases when control over permanents with change control abilities is changed
|
||||||
for (Ability ability : abilities) {
|
// e.g. Mind Control is controlled by Steal Enchantment
|
||||||
effect.apply(Layer.ControlChangingEffects_2, SubLayer.NA, ability, game);
|
while (true) {
|
||||||
|
for (ContinuousEffect effect: layer) {
|
||||||
|
HashSet<Ability> abilities = layeredEffects.getAbility(effect.getId());
|
||||||
|
for (Ability ability : abilities) {
|
||||||
|
effect.apply(Layer.ControlChangingEffects_2, SubLayer.NA, ability, game);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
// if control over all permanent has not changed, we can no longer reapply control changing effects
|
||||||
|
if (!game.getBattlefield().fireControlChangeEvents(game)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// reset control before reapplying control changing effects
|
||||||
|
game.getBattlefield().resetPermanentsControl();
|
||||||
}
|
}
|
||||||
layer = filterLayeredEffects(layerEffects, Layer.TextChangingEffects_3);
|
layer = filterLayeredEffects(layerEffects, Layer.TextChangingEffects_3);
|
||||||
for (ContinuousEffect effect: layer) {
|
for (ContinuousEffect effect: layer) {
|
||||||
|
|
|
@ -403,7 +403,6 @@ public class GameState implements Serializable, Copyable<GameState> {
|
||||||
combat.reset();
|
combat.reset();
|
||||||
this.reset();
|
this.reset();
|
||||||
effects.apply(game);
|
effects.apply(game);
|
||||||
battlefield.fireControlChangeEvents(game);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove End of Combat effects
|
// Remove End of Combat effects
|
||||||
|
|
|
@ -412,18 +412,28 @@ public class Battlefield implements Serializable {
|
||||||
return phasedOut;
|
return phasedOut;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void resetPermanentsControl() {
|
||||||
|
for (Permanent perm: field.values()) {
|
||||||
|
if (perm.isPhasedIn()) {
|
||||||
|
perm.resetControl();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* since control could change several times during applyEvents we only want to fire
|
* since control could change several times during applyEvents we only want to fire
|
||||||
* control changed events after all control change effects have been applied
|
* control changed events after all control change effects have been applied
|
||||||
*
|
*
|
||||||
* @param game
|
* @param game
|
||||||
*/
|
*/
|
||||||
public void fireControlChangeEvents(Game game) {
|
public boolean fireControlChangeEvents(Game game) {
|
||||||
|
boolean controlChanged = false;
|
||||||
for (Permanent perm: field.values()) {
|
for (Permanent perm: field.values()) {
|
||||||
if (perm.isPhasedIn()) {
|
if (perm.isPhasedIn()) {
|
||||||
perm.checkControlChanged(game);
|
controlChanged |= perm.checkControlChanged(game);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return controlChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,7 +76,6 @@ public interface Permanent extends Card, Controllable {
|
||||||
boolean addAttachment(UUID permanentId, Game game);
|
boolean addAttachment(UUID permanentId, Game game);
|
||||||
boolean removeAttachment(UUID permanentId, Game game);
|
boolean removeAttachment(UUID permanentId, Game game);
|
||||||
|
|
||||||
boolean changeControllerId(UUID controllerId, Game game);
|
|
||||||
boolean canBeTargetedBy(MageObject source, UUID controllerId, Game game);
|
boolean canBeTargetedBy(MageObject source, UUID controllerId, Game game);
|
||||||
boolean hasProtectionFrom(MageObject source, Game game);
|
boolean hasProtectionFrom(MageObject source, Game game);
|
||||||
boolean hasSummoningSickness();
|
boolean hasSummoningSickness();
|
||||||
|
@ -119,9 +118,12 @@ public interface Permanent extends Card, Controllable {
|
||||||
void setLoyaltyUsed(boolean used);
|
void setLoyaltyUsed(boolean used);
|
||||||
boolean isLoyaltyUsed();
|
boolean isLoyaltyUsed();
|
||||||
|
|
||||||
|
public void resetControl();
|
||||||
|
boolean changeControllerId(UUID controllerId, Game game);
|
||||||
|
boolean checkControlChanged(Game game);
|
||||||
|
|
||||||
void beginningOfTurn(Game game);
|
void beginningOfTurn(Game game);
|
||||||
void endOfTurn(Game game);
|
void endOfTurn(Game game);
|
||||||
void checkControlChanged(Game game);
|
|
||||||
int getTurnsOnBattlefield();
|
int getTurnsOnBattlefield();
|
||||||
|
|
||||||
void addPower(int power);
|
void addPower(int power);
|
||||||
|
|
|
@ -80,7 +80,6 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
|
||||||
protected UUID originalControllerId;
|
protected UUID originalControllerId;
|
||||||
protected UUID controllerId;
|
protected UUID controllerId;
|
||||||
protected UUID beforeResetControllerId;
|
protected UUID beforeResetControllerId;
|
||||||
protected boolean controllerChanged;
|
|
||||||
protected int damage;
|
protected int damage;
|
||||||
protected boolean controlledFromStartOfControllerTurn;
|
protected boolean controlledFromStartOfControllerTurn;
|
||||||
protected int turnsOnBattlefield;
|
protected int turnsOnBattlefield;
|
||||||
|
@ -169,9 +168,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void reset(Game game) {
|
public void reset(Game game) {
|
||||||
this.beforeResetControllerId = this.controllerId;
|
this.resetControl();
|
||||||
this.controllerId = originalControllerId;
|
|
||||||
controllerChanged = !controllerId.equals(beforeResetControllerId);
|
|
||||||
this.maxBlocks = 1;
|
this.maxBlocks = 1;
|
||||||
this.minBlockedBy = 1;
|
this.minBlockedBy = 1;
|
||||||
this.maxBlockedBy = 0;
|
this.maxBlockedBy = 0;
|
||||||
|
@ -448,42 +445,37 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
|
||||||
return this.controllerId;
|
return this.controllerId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void resetControl() {
|
||||||
|
this.beforeResetControllerId = this.controllerId;
|
||||||
|
this.controllerId = this.originalControllerId;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean changeControllerId(UUID controllerId, Game game) {
|
public boolean changeControllerId(UUID controllerId, Game game) {
|
||||||
if (!controllerId.equals(this.controllerId)) {
|
Player newController = game.getPlayer(controllerId);
|
||||||
Player newController = game.getPlayer(controllerId);
|
if (newController != null && (!newController.hasLeft() || !newController.hasLost())) {
|
||||||
if (newController != null && (!newController.hasLeft() || !newController.hasLost())) {
|
this.controllerId = controllerId;
|
||||||
// changeControllerId can be called by continuous effect
|
return true;
|
||||||
// so it will lead to this.controlledFromStartOfControllerTurn set to false over and over
|
|
||||||
// because of reset(game) method called before applying effect as state-based action
|
|
||||||
// that changes this.controllerId to original one (actually owner)
|
|
||||||
if (!controllerId.equals(beforeResetControllerId)) {
|
|
||||||
this.removeFromCombat(game);
|
|
||||||
this.controlledFromStartOfControllerTurn = false;
|
|
||||||
this.controllerChanged = true;
|
|
||||||
} else {
|
|
||||||
this.controllerChanged = false;
|
|
||||||
}
|
|
||||||
this.controllerId = controllerId;
|
|
||||||
this.abilities.setControllerId(controllerId);
|
|
||||||
game.getContinuousEffects().setController(this.objectId, controllerId);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void checkControlChanged(Game game) {
|
public boolean checkControlChanged(Game game) {
|
||||||
if (this.controllerChanged) {
|
if (!controllerId.equals(beforeResetControllerId)) {
|
||||||
|
this.removeFromCombat(game);
|
||||||
|
this.controlledFromStartOfControllerTurn = false;
|
||||||
|
|
||||||
|
this.abilities.setControllerId(controllerId);
|
||||||
|
game.getContinuousEffects().setController(objectId, controllerId);
|
||||||
|
|
||||||
game.fireEvent(new GameEvent(EventType.LOST_CONTROL, objectId, objectId, beforeResetControllerId));
|
game.fireEvent(new GameEvent(EventType.LOST_CONTROL, objectId, objectId, beforeResetControllerId));
|
||||||
// reset the original controller to abilities and ContinuousEffects
|
|
||||||
if (controllerId.equals(originalControllerId)) {
|
|
||||||
this.abilities.setControllerId(controllerId);
|
|
||||||
game.getContinuousEffects().setController(this.objectId, controllerId);
|
|
||||||
}
|
|
||||||
game.fireEvent(new GameEvent(EventType.GAINED_CONTROL, objectId, objectId, controllerId));
|
game.fireEvent(new GameEvent(EventType.GAINED_CONTROL, objectId, objectId, controllerId));
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
Loading…
Reference in a new issue