Added treasure mana check (#7979)

* add treasure mana support to mana code

* [AFR] Implemented Hired Hexblade

* [AFR] Implemented Jaded Sell-Sword

* [AFR] Implemented Forsword Paladin

* added test

* fixed test failure

* another test fix

* completely reworked tracking mana sources
This commit is contained in:
Evan Kranzler 2021-07-06 19:43:32 -04:00 committed by GitHub
parent ddecfc388f
commit b4355b8f6e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 818 additions and 571 deletions

View file

@ -113,25 +113,25 @@ public class ConditionalMana extends Mana implements Serializable, Emptiable {
return;
}
if (filter.isBlack()) {
black.clear();
black = 0;
}
if (filter.isBlue()) {
blue.clear();
blue = 0;
}
if (filter.isWhite()) {
white.clear();
white = 0;
}
if (filter.isGreen()) {
green.clear();
green = 0;
}
if (filter.isRed()) {
red.clear();
red = 0;
}
if (filter.isColorless()) {
colorless.clear();
colorless = 0;
}
if (filter.isGeneric()) {
generic.clear();
generic = 0;
}
}
@ -154,25 +154,25 @@ public class ConditionalMana extends Mana implements Serializable, Emptiable {
public void clear(ManaType manaType) {
switch (manaType) {
case BLACK:
black.clear();
black = 0;
break;
case BLUE:
blue.clear();
blue = 0;
break;
case GREEN:
green.clear();
green = 0;
break;
case RED:
red.clear();
red = 0;
break;
case WHITE:
white.clear();
white = 0;
break;
case GENERIC:
generic.clear();
generic = 0;
break;
case COLORLESS:
colorless.clear();
colorless = 0;
break;
}
}
@ -198,25 +198,25 @@ public class ConditionalMana extends Mana implements Serializable, Emptiable {
public void add(ManaType manaType, int amount) {
switch (manaType) {
case BLACK:
black.incrementAmount(amount, false);
black += amount;
break;
case BLUE:
blue.incrementAmount(amount, false);
blue += amount;
break;
case GREEN:
green.incrementAmount(amount, false);
green += amount;
break;
case RED:
red.incrementAmount(amount, false);
red += amount;
break;
case WHITE:
white.incrementAmount(amount, false);
white += amount;
break;
case COLORLESS:
colorless.incrementAmount(amount, false);
colorless += amount;
break;
case GENERIC:
generic.incrementAmount(amount, false);
generic += amount;
break;
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,22 @@
package mage.abilities.condition.common;
import mage.abilities.Ability;
import mage.abilities.condition.Condition;
import mage.constants.AbilityType;
import mage.game.Game;
import mage.watchers.common.ManaPaidSourceWatcher;
/**
* @author TheElk801
*/
public enum TreasureSpentToCastCondition implements Condition {
instance;
@Override
public boolean apply(Game game, Ability source) {
if (source.getAbilityType() == AbilityType.SPELL) {
return ManaPaidSourceWatcher.getTreasurePaid(source.getId(), game) > 0;
}
return ManaPaidSourceWatcher.getTreasurePaid(source.getSourceId(), game) > 0;
}
}

View file

@ -103,31 +103,31 @@ public abstract class ManaCostImpl extends CostImpl implements ManaCost {
switch (mana) {
case W:
if (pool.pay(ManaType.WHITE, ability, sourceFilter, game, costToPay, usedManaToPay)) {
this.payment.increaseWhite(1, pool.getLastPaymentWasSnow());
this.payment.increaseWhite();
return true;
}
break;
case U:
if (pool.pay(ManaType.BLUE, ability, sourceFilter, game, costToPay, usedManaToPay)) {
this.payment.increaseBlue(1, pool.getLastPaymentWasSnow());
this.payment.increaseBlue();
return true;
}
break;
case B:
if (pool.pay(ManaType.BLACK, ability, sourceFilter, game, costToPay, usedManaToPay)) {
this.payment.increaseBlack(1, pool.getLastPaymentWasSnow());
this.payment.increaseBlack();
return true;
}
break;
case R:
if (pool.pay(ManaType.RED, ability, sourceFilter, game, costToPay, usedManaToPay)) {
this.payment.increaseRed(1, pool.getLastPaymentWasSnow());
this.payment.increaseRed();
return true;
}
break;
case G:
if (pool.pay(ManaType.GREEN, ability, sourceFilter, game, costToPay, usedManaToPay)) {
this.payment.increaseGreen(1, pool.getLastPaymentWasSnow());
this.payment.increaseGreen();
return true;
}
break;
@ -139,7 +139,7 @@ public abstract class ManaCostImpl extends CostImpl implements ManaCost {
int conditionalCount = pool.getConditionalCount(ability, game, null, costToPay);
while (mana > payment.count() && (pool.count() > 0 || conditionalCount > 0)) {
if (pool.pay(ManaType.COLORLESS, ability, sourceFilter, game, costToPay, usedManaToPay)) {
this.payment.increaseColorless(1, pool.getLastPaymentWasSnow());
this.payment.increaseColorless();
}
break;
}
@ -155,7 +155,7 @@ public abstract class ManaCostImpl extends CostImpl implements ManaCost {
if ((filterMana == null || filterMana.isColorless()) && pool.pay(
ManaType.COLORLESS, ability, sourceFilter, game, costToPay, usedManaToPay
)) {
this.payment.increaseColorless(1, pool.getLastPaymentWasSnow());
this.payment.increaseColorless();
continue;
}
@ -163,7 +163,7 @@ public abstract class ManaCostImpl extends CostImpl implements ManaCost {
if ((filterMana == null || filterMana.isBlack()) && pool.pay(
ManaType.BLACK, ability, sourceFilter, game, costToPay, usedManaToPay
)) {
this.payment.increaseBlack(1, pool.getLastPaymentWasSnow());
this.payment.increaseBlack();
continue;
}
@ -171,7 +171,7 @@ public abstract class ManaCostImpl extends CostImpl implements ManaCost {
if ((filterMana == null || filterMana.isBlue()) && pool.pay(
ManaType.BLUE, ability, sourceFilter, game, costToPay, usedManaToPay
)) {
this.payment.increaseBlue(1, pool.getLastPaymentWasSnow());
this.payment.increaseBlue();
continue;
}
@ -179,7 +179,7 @@ public abstract class ManaCostImpl extends CostImpl implements ManaCost {
if ((filterMana == null || filterMana.isWhite()) && pool.pay(
ManaType.WHITE, ability, sourceFilter, game, costToPay, usedManaToPay
)) {
this.payment.increaseWhite(1, pool.getLastPaymentWasSnow());
this.payment.increaseWhite();
continue;
}
@ -187,7 +187,7 @@ public abstract class ManaCostImpl extends CostImpl implements ManaCost {
if ((filterMana == null || filterMana.isGreen()) && pool.pay(
ManaType.GREEN, ability, sourceFilter, game, costToPay, usedManaToPay
)) {
this.payment.increaseGreen(1, pool.getLastPaymentWasSnow());
this.payment.increaseGreen();
continue;
}
@ -195,7 +195,7 @@ public abstract class ManaCostImpl extends CostImpl implements ManaCost {
if ((filterMana == null || filterMana.isRed()) && pool.pay(
ManaType.RED, ability, sourceFilter, game, costToPay, usedManaToPay
)) {
this.payment.increaseRed(1, pool.getLastPaymentWasSnow());
this.payment.increaseRed();
continue;
}

View file

@ -1,12 +1,11 @@
package mage.abilities.dynamicvalue.common;
import mage.Mana;
import mage.abilities.Ability;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.effects.Effect;
import mage.constants.AbilityType;
import mage.game.Game;
import mage.watchers.common.ManaSpentToCastWatcher;
import mage.watchers.common.ManaPaidSourceWatcher;
/**
* @author TheElk801
@ -17,17 +16,9 @@ public enum SnowManaSpentValue implements DynamicValue {
@Override
public int calculate(Game game, Ability sourceAbility, Effect effect) {
if (sourceAbility.getAbilityType() == AbilityType.SPELL) {
return sourceAbility.getManaCostsToPay().getUsedManaToPay().getSnow();
return ManaPaidSourceWatcher.getSnowPaid(sourceAbility.getId(), game);
}
ManaSpentToCastWatcher watcher = game.getState().getWatcher(ManaSpentToCastWatcher.class);
if (watcher == null) {
return 0;
}
Mana payment = watcher.getAndResetLastPayment(sourceAbility.getSourceId());
if (payment == null) {
return 0;
}
return payment.getSnow();
return ManaPaidSourceWatcher.getSnowPaid(sourceAbility.getSourceId(), game);
}
@Override

View file

@ -1,6 +1,8 @@
package mage.game.events;
import mage.MageObject;
import mage.abilities.Ability;
import mage.constants.ManaType;
import java.util.UUID;
@ -9,9 +11,28 @@ import java.util.UUID;
*/
public class ManaPaidEvent extends GameEvent {
public ManaPaidEvent(Ability abilityToPay, UUID manaSourceId, boolean manaFlag, UUID manaOriginalId) {
private final UUID sourcePaidId;
private final MageObject sourceObject;
private final ManaType manaType;
public ManaPaidEvent(Ability abilityToPay, UUID manaSourceId, boolean manaFlag, UUID manaOriginalId, MageObject sourceObject, ManaType manaType) {
super(GameEvent.EventType.MANA_PAID, abilityToPay.getId(), null, abilityToPay.getControllerId(), 0, manaFlag);
this.setSourceId(manaSourceId);
this.setData(manaOriginalId.toString());
this.sourcePaidId = abilityToPay.getSourceId();
this.sourceObject = sourceObject;
this.manaType = manaType;
}
public UUID getSourcePaidId() {
return sourcePaidId;
}
public MageObject getSourceObject() {
return sourceObject;
}
public ManaType getManaType() {
return manaType;
}
}

View file

@ -50,15 +50,12 @@ public class ManaPool implements Serializable {
}
}
private boolean lastPaymentWasSnow;
public ManaPool(UUID playerId) {
this.playerId = playerId;
autoPayment = true;
autoPaymentRestricted = true;
unlockedManaType = null;
forcedToPay = false;
lastPaymentWasSnow = false;
}
public ManaPool(final ManaPool pool) {
@ -74,7 +71,6 @@ public class ManaPool implements Serializable {
poolBookmark.add(item.copy());
}
this.doNotEmptyManaTypes.addAll(pool.doNotEmptyManaTypes);
this.lastPaymentWasSnow = pool.lastPaymentWasSnow;
this.manaBecomesColorless = pool.manaBecomesColorless;
}
@ -109,7 +105,6 @@ public class ManaPool implements Serializable {
* @return
*/
public boolean pay(ManaType manaType, Ability ability, Filter filter, Game game, Cost costToPay, Mana usedManaToPay) {
lastPaymentWasSnow = false;
if (!isAutoPayment() && manaType != unlockedManaType) {
// if manual payment and the needed mana type was not unlocked, nothing will be paid
return false;
@ -139,7 +134,6 @@ public class ManaPool implements Serializable {
lockManaType(); // pay only one mana if mana payment is set to manually
return true;
}
lastPaymentWasSnow = false;
for (ManaPoolItem mana : manaItems) {
if (filter != null) {
@ -164,11 +158,10 @@ public class ManaPool implements Serializable {
continue;
}
if (mana.get(usableManaType) > 0) {
GameEvent event = new ManaPaidEvent(ability, mana.getSourceId(), mana.getFlag(), mana.getOriginalId());
GameEvent event = new ManaPaidEvent(ability, mana.getSourceId(), mana.getFlag(), mana.getOriginalId(), mana.getSourceObject(), usableManaType);
game.fireEvent(event);
usedManaToPay.increase(usableManaType, mana.getSourceObject().isSnow());
usedManaToPay.increase(usableManaType);
mana.remove(usableManaType);
lastPaymentWasSnow |= mana.getSourceObject().isSnow();
if (mana.count() == 0) { // so no items with count 0 stay in list
manaItems.remove(mana);
}
@ -410,8 +403,8 @@ public class ManaPool implements Serializable {
for (ConditionalMana mana : getConditionalMana()) {
if (mana.get(manaInfo.manaType) > 0 && mana.apply(ability, game, mana.getManaProducerId(), costToPay)) {
mana.set(manaInfo.manaType, CardUtil.overflowDec(mana.get(manaInfo.manaType), 1));
usedManaToPay.increase(manaInfo.manaType, manaInfo.sourceObject.isSnow());
GameEvent event = new ManaPaidEvent(ability, mana.getManaProducerId(), mana.getFlag(), mana.getManaProducerOriginalId());
usedManaToPay.increase(manaInfo.manaType);
GameEvent event = new ManaPaidEvent(ability, mana.getManaProducerId(), mana.getFlag(), mana.getManaProducerOriginalId(), manaInfo.sourceObject, manaInfo.manaType);
game.fireEvent(event);
break;
}
@ -528,10 +521,6 @@ public class ManaPool implements Serializable {
}
}
public boolean getLastPaymentWasSnow() {
return lastPaymentWasSnow;
}
@Override
public String toString() {
return getMana().toString();

View file

@ -0,0 +1,123 @@
package mage.watchers.common;
import mage.MageObject;
import mage.ObjectColor;
import mage.constants.ManaType;
import mage.constants.SubType;
import mage.constants.WatcherScope;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.ManaPaidEvent;
import mage.game.events.ZoneChangeEvent;
import mage.game.stack.Spell;
import mage.watchers.Watcher;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/**
* @author TheElk801
*/
public class ManaPaidSourceWatcher extends Watcher {
private static final class ManaPaidTracker implements Serializable {
private int whiteSnow = 0;
private int blueSnow = 0;
private int blackSnow = 0;
private int redSnow = 0;
private int greenSnow = 0;
private int colorlessSnow = 0;
private int treasure = 0;
private void increment(MageObject sourceObject, ManaType manaType, Game game) {
if (sourceObject.hasSubtype(SubType.TREASURE, game)) {
treasure++;
}
if (!sourceObject.isSnow()) {
return;
}
switch (manaType) {
case WHITE:
whiteSnow++;
break;
case BLUE:
blueSnow++;
break;
case BLACK:
blackSnow++;
break;
case RED:
redSnow++;
break;
case GREEN:
greenSnow++;
break;
case COLORLESS:
case GENERIC:
colorlessSnow++;
break;
}
}
private int getSnow() {
return whiteSnow + blueSnow + blackSnow + redSnow + greenSnow + colorlessSnow;
}
private boolean checkSnowColor(Spell spell, Game game) {
ObjectColor color = spell.getColor(game);
return color.isWhite() && whiteSnow > 0
|| color.isBlue() && blueSnow > 0
|| color.isBlack() && blackSnow > 0
|| color.isRed() && redSnow > 0
|| color.isGreen() && greenSnow > 0;
}
}
private static final ManaPaidTracker emptyTracker = new ManaPaidTracker();
private final Map<UUID, ManaPaidTracker> manaMap = new HashMap<>();
public ManaPaidSourceWatcher() {
super(WatcherScope.GAME);
}
@Override
public void watch(GameEvent event, Game game) {
switch (event.getType()) {
case ZONE_CHANGE:
if (((ZoneChangeEvent) event).getFromZone() == Zone.BATTLEFIELD) {
manaMap.remove(event.getSourceId());
}
return;
case MANA_PAID:
ManaPaidEvent manaEvent = (ManaPaidEvent) event;
manaMap.computeIfAbsent(manaEvent.getTargetId(), x -> new ManaPaidTracker())
.increment(manaEvent.getSourceObject(), manaEvent.getManaType(), game);
manaMap.computeIfAbsent(manaEvent.getSourcePaidId(), x -> new ManaPaidTracker())
.increment(manaEvent.getSourceObject(), manaEvent.getManaType(), game);
}
}
@Override
public void reset() {
super.reset();
manaMap.clear();
}
public static int getTreasurePaid(UUID sourceId, Game game) {
ManaPaidSourceWatcher watcher = game.getState().getWatcher(ManaPaidSourceWatcher.class);
return watcher == null ? 0 : watcher.manaMap.getOrDefault(sourceId, emptyTracker).treasure;
}
public static int getSnowPaid(UUID sourceId, Game game) {
ManaPaidSourceWatcher watcher = game.getState().getWatcher(ManaPaidSourceWatcher.class);
return watcher == null ? 0 : watcher.manaMap.getOrDefault(sourceId, emptyTracker).getSnow();
}
public static boolean checkSnowColor(Spell spell, Game game) {
ManaPaidSourceWatcher watcher = game.getState().getWatcher(ManaPaidSourceWatcher.class);
return watcher != null && watcher.manaMap.getOrDefault(spell.getSpellAbility().getId(), emptyTracker).checkSnowColor(spell, game);
}
}