mirror of
https://github.com/magefree/mage.git
synced 2025-12-25 21:12:04 -08:00
Ability refactor: fixed broken effects timestamp in some use cases
This commit is contained in:
parent
f3f1c29926
commit
d63a3e88ce
6 changed files with 213 additions and 42 deletions
|
|
@ -57,7 +57,7 @@ public class ContinuousEffects implements Serializable {
|
|||
// private final PlaneswalkerRedirectionEffect planeswalkerRedirectionEffect;
|
||||
private final AuraReplacementEffect auraReplacementEffect;
|
||||
|
||||
private final List<ContinuousEffect> previous = new ArrayList<>();
|
||||
private final Map<String, List<ContinuousEffect>> lastEffectsListOnLayer = new HashMap<>(); // helps to find out new effect timestamps
|
||||
|
||||
// note all effect/abilities that were only added temporary
|
||||
private final Map<ContinuousEffect, Set<Ability>> temporaryEffects = new HashMap<>();
|
||||
|
|
@ -178,6 +178,16 @@ public class ContinuousEffects implements Serializable {
|
|||
}
|
||||
|
||||
public synchronized List<ContinuousEffect> getLayeredEffects(Game game) {
|
||||
return getLayeredEffects(game, "main");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return effects list ordered by timestamps (timestamps are automaticity generates from new/old lists on same layer)
|
||||
*
|
||||
* @param timestampGroupName workaround to fix broken timestamps on effect's add/remove between different layers
|
||||
* @return effects list ordered by timestamp
|
||||
*/
|
||||
public synchronized List<ContinuousEffect> getLayeredEffects(Game game, String timestampGroupName) {
|
||||
List<ContinuousEffect> layerEffects = new ArrayList<>();
|
||||
for (ContinuousEffect effect : layeredEffects) {
|
||||
switch (effect.getDuration()) {
|
||||
|
|
@ -202,9 +212,14 @@ public class ContinuousEffects implements Serializable {
|
|||
}
|
||||
}
|
||||
|
||||
updateTimestamps(layerEffects);
|
||||
updateTimestamps(timestampGroupName, layerEffects);
|
||||
layerEffects.sort(Comparator.comparingLong(ContinuousEffect::getOrder));
|
||||
/* debug effects apply order:
|
||||
if (game.getStep() != null) System.out.println("layr - " + game.getTurnNum() + "." + game.getStep().getType() + ": layers " + layerEffects.size()
|
||||
+ " - " + layerEffects.stream().map(l -> l.getClass().getSimpleName()).collect(Collectors.joining(", "))
|
||||
+ " - " + callName);
|
||||
//*/
|
||||
|
||||
Collections.sort(layerEffects, Comparator.comparingLong(ContinuousEffect::getOrder));
|
||||
return layerEffects;
|
||||
}
|
||||
|
||||
|
|
@ -215,17 +230,23 @@ public class ContinuousEffects implements Serializable {
|
|||
* Ability.#isInUseableZone(Game, boolean) method in
|
||||
* #getLayeredEffects(Game).
|
||||
*
|
||||
* It must be called with different timestamp group name (otherwise sort order will be changed for add/remove effects, see Urborg and Bloodmoon test)
|
||||
*
|
||||
* @param layerEffects
|
||||
*/
|
||||
private synchronized void updateTimestamps(List<ContinuousEffect> layerEffects) {
|
||||
private synchronized void updateTimestamps(String timestampGroupName, List<ContinuousEffect> layerEffects) {
|
||||
if (!lastEffectsListOnLayer.containsKey(timestampGroupName)) {
|
||||
lastEffectsListOnLayer.put(timestampGroupName, new ArrayList<>());
|
||||
}
|
||||
List<ContinuousEffect> prevs = lastEffectsListOnLayer.get(timestampGroupName);
|
||||
for (ContinuousEffect continuousEffect : layerEffects) {
|
||||
// check if it's new, then set order
|
||||
if (!previous.contains(continuousEffect)) {
|
||||
if (!prevs.contains(continuousEffect)) {
|
||||
setOrder(continuousEffect);
|
||||
}
|
||||
}
|
||||
previous.clear();
|
||||
previous.addAll(layerEffects);
|
||||
prevs.clear();
|
||||
prevs.addAll(layerEffects);
|
||||
}
|
||||
|
||||
public void setOrder(ContinuousEffect effect) {
|
||||
|
|
@ -425,12 +446,12 @@ public class ContinuousEffects implements Serializable {
|
|||
return false;
|
||||
}
|
||||
boolean exists = true;
|
||||
if (!object.getAbilities().contains(ability)) {
|
||||
if (!object.hasAbility(ability, game)) {
|
||||
exists = false;
|
||||
if (object instanceof PermanentCard) {
|
||||
PermanentCard permanent = (PermanentCard) object;
|
||||
if (permanent.isTransformable() && event.getType() == GameEvent.EventType.TRANSFORMED) {
|
||||
exists = permanent.getCard().getAbilities().contains(ability);
|
||||
exists = permanent.getCard().hasAbility(ability, game);
|
||||
}
|
||||
}
|
||||
} else if (object instanceof PermanentCard) {
|
||||
|
|
@ -903,7 +924,7 @@ public class ContinuousEffects implements Serializable {
|
|||
//20091005 - 613
|
||||
public synchronized void apply(Game game) {
|
||||
removeInactiveEffects(game);
|
||||
List<ContinuousEffect> activeLayerEffects = getLayeredEffects(game);
|
||||
List<ContinuousEffect> activeLayerEffects = getLayeredEffects(game); // main call
|
||||
|
||||
List<ContinuousEffect> layer = filterLayeredEffects(activeLayerEffects, Layer.CopyEffects_1);
|
||||
for (ContinuousEffect effect : layer) {
|
||||
|
|
@ -914,7 +935,7 @@ public class ContinuousEffects implements Serializable {
|
|||
}
|
||||
//Reload layerEffect if copy effects were applied
|
||||
if (!layer.isEmpty()) {
|
||||
activeLayerEffects = getLayeredEffects(game);
|
||||
activeLayerEffects = getLayeredEffects(game, "layer_1");
|
||||
}
|
||||
|
||||
layer = filterLayeredEffects(activeLayerEffects, Layer.ControlChangingEffects_2);
|
||||
|
|
@ -936,16 +957,16 @@ public class ContinuousEffects implements Serializable {
|
|||
game.getBattlefield().resetPermanentsControl();
|
||||
}
|
||||
|
||||
applyLayer(activeLayerEffects, Layer.TextChangingEffects_3, game);
|
||||
applyLayer(activeLayerEffects, Layer.TypeChangingEffects_4, game);
|
||||
applyLayer(activeLayerEffects, Layer.ColorChangingEffects_5, game);
|
||||
applyLayer(activeLayerEffects, Layer.TextChangingEffects_3, game, "layer_3");
|
||||
applyLayer(activeLayerEffects, Layer.TypeChangingEffects_4, game, "layer_4");
|
||||
applyLayer(activeLayerEffects, Layer.ColorChangingEffects_5, game, "layer_5");
|
||||
|
||||
Map<ContinuousEffect, List<Ability>> appliedEffectAbilities = new HashMap<>();
|
||||
boolean done = false;
|
||||
Map<ContinuousEffect, Set<UUID>> waitingEffects = new LinkedHashMap<>();
|
||||
Set<UUID> appliedEffects = new HashSet<>();
|
||||
applyCounters.apply(Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, null, game);
|
||||
activeLayerEffects = getLayeredEffects(game);
|
||||
activeLayerEffects = getLayeredEffects(game, "layer_6");
|
||||
|
||||
while (!done) { // loop needed if a added effect adds again an effect (e.g. Level 5- of Joraga Treespeaker)
|
||||
done = true;
|
||||
|
|
@ -1000,7 +1021,7 @@ public class ContinuousEffects implements Serializable {
|
|||
effect.apply(Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, ability, game);
|
||||
done = false;
|
||||
// list must be updated after each applied effect (eg. if "Turn to Frog" removes abilities)
|
||||
activeLayerEffects = getLayeredEffects(game);
|
||||
activeLayerEffects = getLayeredEffects(game, "apply");
|
||||
}
|
||||
appliedEffects.add(effect.getId());
|
||||
|
||||
|
|
@ -1027,7 +1048,7 @@ public class ContinuousEffects implements Serializable {
|
|||
entry.getKey().apply(Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, ability, game);
|
||||
done = false;
|
||||
// list must be updated after each applied effect (eg. if "Turn to Frog" removes abilities)
|
||||
activeLayerEffects = getLayeredEffects(game);
|
||||
activeLayerEffects = getLayeredEffects(game, "apply");
|
||||
}
|
||||
appliedEffects.add(entry.getKey().getId());
|
||||
iterator.remove();
|
||||
|
|
@ -1083,10 +1104,10 @@ public class ContinuousEffects implements Serializable {
|
|||
|
||||
private boolean abilityActive(Ability ability, Game game) {
|
||||
MageObject object = game.getObject(ability.getSourceId());
|
||||
return object != null && object.hasAbility(ability.getId(), game);
|
||||
return object != null && object.hasAbility(ability, game);
|
||||
}
|
||||
|
||||
private void applyLayer(List<ContinuousEffect> activeLayerEffects, Layer currentLayer, Game game) {
|
||||
private void applyLayer(List<ContinuousEffect> activeLayerEffects, Layer currentLayer, Game game, String timestampGroupName) {
|
||||
List<ContinuousEffect> layer = filterLayeredEffects(activeLayerEffects, currentLayer);
|
||||
// layer is a list of all effects at the current layer
|
||||
if (!layer.isEmpty()) {
|
||||
|
|
@ -1109,7 +1130,7 @@ public class ContinuousEffects implements Serializable {
|
|||
applyContinuousEffect(effect, currentLayer, game);
|
||||
// add it to the applied effects list
|
||||
appliedEffects.add(effect.getId());
|
||||
layer = getLayeredEffects(game);
|
||||
layer = getLayeredEffects(game, timestampGroupName);
|
||||
|
||||
// check waiting effects to see if it has anything to check
|
||||
if (!waitingEffects.isEmpty()) {
|
||||
|
|
@ -1120,7 +1141,7 @@ public class ContinuousEffects implements Serializable {
|
|||
applyContinuousEffect(entry.getKey(), currentLayer, game);
|
||||
// add it to the applied effects list
|
||||
appliedEffects.add(entry.getKey().getId());
|
||||
layer = getLayeredEffects(game);
|
||||
layer = getLayeredEffects(game, timestampGroupName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1131,7 +1152,7 @@ public class ContinuousEffects implements Serializable {
|
|||
applyContinuousEffect(entry.getKey(), currentLayer, game);
|
||||
// add it to the applied effects list
|
||||
appliedEffects.add(entry.getKey().getId());
|
||||
layer = getLayeredEffects(game);
|
||||
layer = getLayeredEffects(game, timestampGroupName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1154,7 +1175,7 @@ public class ContinuousEffects implements Serializable {
|
|||
if (!(effect instanceof BecomesFaceDownCreatureEffect)
|
||||
&& (effect != null && !effect.getDuration().equals(Duration.Custom))) { // Custom effects do not depend on the creating permanent
|
||||
if (card != null) {
|
||||
return card.getAbilities(game).contains(ability);
|
||||
return card.hasAbility(ability, game);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue