mirror of
https://github.com/magefree/mage.git
synced 2025-12-25 04:52:07 -08:00
* Fixed some problems with State.values using mutable objects.
This commit is contained in:
parent
43c799e889
commit
0974e6b42d
8 changed files with 177 additions and 71 deletions
|
|
@ -40,8 +40,9 @@ import mage.game.Game;
|
|||
*/
|
||||
public class ActivationInfo {
|
||||
|
||||
protected int turnNum;
|
||||
protected int activationCounter;
|
||||
protected int turnNum = 0;
|
||||
protected int activationCounter = 0;
|
||||
protected String key;
|
||||
|
||||
public static ActivationInfo getInstance(Game game, UUID sourceId) {
|
||||
return ActivationInfo.getInstance(game, sourceId, game.getState().getZoneChangeCounter(sourceId));
|
||||
|
|
@ -49,17 +50,25 @@ public class ActivationInfo {
|
|||
|
||||
public static ActivationInfo getInstance(Game game, UUID sourceId, int zoneChangeCounter) {
|
||||
String key = "ActivationInfo" + sourceId.toString() + zoneChangeCounter;
|
||||
ActivationInfo activationInfo = (ActivationInfo) game.getState().getValue(key);
|
||||
if (activationInfo == null) {
|
||||
activationInfo = new ActivationInfo(game);
|
||||
game.getState().setValue(key, activationInfo);
|
||||
Integer activations = (Integer) game.getState().getValue(key);
|
||||
ActivationInfo activationInfo;
|
||||
if (activations != null) {
|
||||
Integer turnNum = (Integer) game.getState().getValue(key + "T");
|
||||
activationInfo = new ActivationInfo(game, turnNum, activations);
|
||||
} else {
|
||||
activationInfo = new ActivationInfo(game, game.getTurnNum(), 0);
|
||||
}
|
||||
activationInfo.setKey(key);
|
||||
return activationInfo;
|
||||
}
|
||||
|
||||
protected ActivationInfo(Game game) {
|
||||
this.turnNum = game.getTurnNum();
|
||||
this.activationCounter = 0;
|
||||
public void setKey(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
protected ActivationInfo(Game game, int turnNum, int activationCounter) {
|
||||
this.turnNum = turnNum;
|
||||
this.activationCounter = activationCounter;
|
||||
}
|
||||
|
||||
public void addActivation(Game game) {
|
||||
|
|
@ -69,6 +78,8 @@ public class ActivationInfo {
|
|||
} else {
|
||||
activationCounter++;
|
||||
}
|
||||
game.getState().setValue(key, activationCounter);
|
||||
game.getState().setValue(key + "T", turnNum);
|
||||
}
|
||||
|
||||
public int getActivationCounter() {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import mage.abilities.effects.Effect;
|
|||
import mage.constants.PhaseStep;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.turn.Step;
|
||||
|
||||
/**
|
||||
*
|
||||
|
|
@ -30,10 +29,10 @@ public class DealsDamageToOneOrMoreCreaturesTriggeredAbility extends DealsDamage
|
|||
if (super.checkTrigger(event, game)) {
|
||||
// check that combat damage does only once trigger also if multiple creatures were damaged because they block or were blocked by source
|
||||
if (game.getTurn().getStepType().equals(PhaseStep.COMBAT_DAMAGE) || game.getTurn().getStepType().equals(PhaseStep.FIRST_COMBAT_DAMAGE)) {
|
||||
Step step = (Step) game.getState().getValue("damageStep" + getOriginalId());
|
||||
if (!game.getStep().equals(step)) {
|
||||
Integer stepHash = (Integer) game.getState().getValue("damageStep" + getOriginalId());
|
||||
if (stepHash == null || game.getStep().hashCode() != stepHash) {
|
||||
// this ability did not trigger during this damage step
|
||||
game.getState().setValue("damageStep" + getOriginalId(), game.getStep());
|
||||
game.getState().setValue("damageStep" + getOriginalId(), game.getStep().hashCode());
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -37,6 +37,8 @@ import mage.abilities.costs.mana.AlternateManaPaymentAbility;
|
|||
import mage.abilities.costs.mana.ManaCost;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.Cards;
|
||||
import mage.cards.CardsImpl;
|
||||
import mage.constants.AbilityType;
|
||||
import mage.constants.ManaType;
|
||||
import mage.constants.Outcome;
|
||||
|
|
@ -56,17 +58,17 @@ import mage.util.CardUtil;
|
|||
* applies only after the total cost of the spell with delve is determined.
|
||||
* 702.65b Multiple instances of delve on the same spell are redundant.
|
||||
*
|
||||
* The rules for delve have changed slightly since it was last in an
|
||||
* expansion. Previously, delve reduced the cost to cast a spell. Under the
|
||||
* current rules, you exile cards from your graveyard at the same time you pay
|
||||
* the spell’s cost. Exiling a card this way is simply another way to pay that
|
||||
* cost. * Delve doesn't change a spell’s mana cost or converted mana cost. For
|
||||
* example, Dead Drop’s converted mana cost is 10 even if you exiled three cards
|
||||
* to cast it. * You can’t exile cards to pay for the colored mana requirements
|
||||
* of a spell with delve. * You can’t exile more cards than the generic mana
|
||||
* requirement of a spell with delve. For example, you can’t exile more than
|
||||
* nine cards from your graveyard to cast Dead Drop. * Because delve isn't an
|
||||
* alternative cost, it can be used in conjunction with alternative costs.
|
||||
* The rules for delve have changed slightly since it was last in an expansion.
|
||||
* Previously, delve reduced the cost to cast a spell. Under the current rules,
|
||||
* you exile cards from your graveyard at the same time you pay the spell’s
|
||||
* cost. Exiling a card this way is simply another way to pay that cost. * Delve
|
||||
* doesn't change a spell’s mana cost or converted mana cost. For example, Dead
|
||||
* Drop’s converted mana cost is 10 even if you exiled three cards to cast it. *
|
||||
* You can’t exile cards to pay for the colored mana requirements of a spell
|
||||
* with delve. * You can’t exile more cards than the generic mana requirement of
|
||||
* a spell with delve. For example, you can’t exile more than nine cards from
|
||||
* your graveyard to cast Dead Drop. * Because delve isn't an alternative cost,
|
||||
* it can be used in conjunction with alternative costs.
|
||||
*
|
||||
* @author LevelX2
|
||||
*
|
||||
|
|
@ -155,18 +157,23 @@ class DelveEffect extends OneShotEffect {
|
|||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller != null) {
|
||||
ExileFromGraveCost exileFromGraveCost = (ExileFromGraveCost) source.getCosts().get(0);
|
||||
|
||||
List<Card> exiledCards = exileFromGraveCost.getExiledCards();
|
||||
if (exiledCards.size() > 0) {
|
||||
Cards toDelve = new CardsImpl();
|
||||
for (Card card : exiledCards) {
|
||||
toDelve.add(card);
|
||||
}
|
||||
ManaPool manaPool = controller.getManaPool();
|
||||
manaPool.addMana(new Mana(0, 0, 0, 0, 0, 0, 0, exiledCards.size()), game, source);
|
||||
manaPool.addMana(new Mana(0, 0, 0, 0, 0, 0, 0, toDelve.size()), game, source);
|
||||
manaPool.unlockManaType(ManaType.COLORLESS);
|
||||
String keyString = CardUtil.getCardZoneString("delvedCards", source.getSourceId(), game);
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Card> delvedCards = (List<Card>) game.getState().getValue(keyString);
|
||||
Cards delvedCards = (Cards) game.getState().getValue(keyString);
|
||||
if (delvedCards == null) {
|
||||
game.getState().setValue(keyString, exiledCards);
|
||||
game.getState().setValue(keyString, toDelve);
|
||||
} else {
|
||||
delvedCards.addAll(exiledCards);
|
||||
delvedCards.addAll(toDelve);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ import java.util.ArrayList;
|
|||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.LinkedList;
|
||||
|
|
@ -182,7 +183,11 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
this.turnMods = state.turnMods.copy();
|
||||
this.watchers = state.watchers.copy();
|
||||
for (Map.Entry<String, Object> entry : state.values.entrySet()) {
|
||||
this.values.put(entry.getKey(), entry.getValue());
|
||||
if (entry.getValue() instanceof HashSet) {
|
||||
this.values.put(entry.getKey(), (HashSet) ((HashSet) entry.getValue()).clone());
|
||||
} else {
|
||||
this.values.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
this.zones.putAll(state.zones);
|
||||
this.simultaneousEvents.addAll(state.simultaneousEvents);
|
||||
|
|
@ -893,8 +898,10 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
|
||||
/**
|
||||
* Best only use immutable objects, otherwise the states/values of the
|
||||
* object may be changed by AI simulation, because the Value objects are not
|
||||
* copied as the state class is copied.
|
||||
* object may be changed by AI simulation or rollbacks, because the Value
|
||||
* objects are not copied as the state class is copied. Mutable supported:
|
||||
* HashSet with immutable entries (e.g. HashSet< UUID > or HashSet< String
|
||||
* >)
|
||||
*
|
||||
* @param valueId
|
||||
* @param value
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue