diff --git a/Mage.Sets/src/mage/cards/a/ArmoryMice.java b/Mage.Sets/src/mage/cards/a/ArmoryMice.java index a34e67c3fbf..8d401c79487 100644 --- a/Mage.Sets/src/mage/cards/a/ArmoryMice.java +++ b/Mage.Sets/src/mage/cards/a/ArmoryMice.java @@ -11,7 +11,7 @@ import mage.constants.AbilityWord; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.watchers.common.CelebrationWatcher; +import mage.watchers.common.PermanentsEnteredBattlefieldWatcher; import java.util.UUID; @@ -32,7 +32,7 @@ public final class ArmoryMice extends CardImpl { new BoostSourceEffect(0, 2, Duration.WhileOnBattlefield), CelebrationCondition.instance, "{this} gets +0/+2 as long as two or more " + "nonland permanents entered the battlefield under your control this turn" - )).addHint(CelebrationCondition.getHint()).setAbilityWord(AbilityWord.CELEBRATION), new CelebrationWatcher()); + )).addHint(CelebrationCondition.getHint()).setAbilityWord(AbilityWord.CELEBRATION), new PermanentsEnteredBattlefieldWatcher()); } private ArmoryMice(final ArmoryMice card) { diff --git a/Mage.Sets/src/mage/cards/a/AshPartyCrasher.java b/Mage.Sets/src/mage/cards/a/AshPartyCrasher.java index e43eeae3085..7deb6eea062 100644 --- a/Mage.Sets/src/mage/cards/a/AshPartyCrasher.java +++ b/Mage.Sets/src/mage/cards/a/AshPartyCrasher.java @@ -14,7 +14,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.SuperType; import mage.counters.CounterType; -import mage.watchers.common.CelebrationWatcher; +import mage.watchers.common.PermanentsEnteredBattlefieldWatcher; import java.util.UUID; @@ -43,7 +43,7 @@ public final class AshPartyCrasher extends CardImpl { ); ability.setAbilityWord(AbilityWord.CELEBRATION); ability.addHint(CelebrationCondition.getHint()); - this.addAbility(ability, new CelebrationWatcher()); + this.addAbility(ability, new PermanentsEnteredBattlefieldWatcher()); } private AshPartyCrasher(final AshPartyCrasher card) { diff --git a/Mage.Sets/src/mage/cards/b/BelligerentOfTheBall.java b/Mage.Sets/src/mage/cards/b/BelligerentOfTheBall.java index ef30fa1a307..8441b5bc775 100644 --- a/Mage.Sets/src/mage/cards/b/BelligerentOfTheBall.java +++ b/Mage.Sets/src/mage/cards/b/BelligerentOfTheBall.java @@ -15,7 +15,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.TargetController; import mage.target.common.TargetControlledCreaturePermanent; -import mage.watchers.common.CelebrationWatcher; +import mage.watchers.common.PermanentsEnteredBattlefieldWatcher; import java.util.UUID; @@ -47,7 +47,7 @@ public final class BelligerentOfTheBall extends CardImpl { ability.addTarget(new TargetControlledCreaturePermanent()); ability.setAbilityWord(AbilityWord.CELEBRATION); ability.addHint(CelebrationCondition.getHint()); - this.addAbility(ability, new CelebrationWatcher()); + this.addAbility(ability, new PermanentsEnteredBattlefieldWatcher()); } private BelligerentOfTheBall(final BelligerentOfTheBall card) { diff --git a/Mage.Sets/src/mage/cards/b/BespokeBattlegarb.java b/Mage.Sets/src/mage/cards/b/BespokeBattlegarb.java index 44c6f0b1b83..b41cb610dc2 100644 --- a/Mage.Sets/src/mage/cards/b/BespokeBattlegarb.java +++ b/Mage.Sets/src/mage/cards/b/BespokeBattlegarb.java @@ -13,7 +13,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.target.common.TargetControlledCreaturePermanent; -import mage.watchers.common.CelebrationWatcher; +import mage.watchers.common.PermanentsEnteredBattlefieldWatcher; import java.util.UUID; @@ -46,7 +46,7 @@ public final class BespokeBattlegarb extends CardImpl { ability.addTarget(new TargetControlledCreaturePermanent(0, 1)); ability.setAbilityWord(AbilityWord.CELEBRATION); ability.addHint(CelebrationCondition.getHint()); - this.addAbility(ability, new CelebrationWatcher()); + this.addAbility(ability, new PermanentsEnteredBattlefieldWatcher()); // Equip {2} this.addAbility(new EquipAbility(Outcome.BoostCreature, new GenericManaCost(2))); diff --git a/Mage.Sets/src/mage/cards/d/DiamondCity.java b/Mage.Sets/src/mage/cards/d/DiamondCity.java new file mode 100644 index 00000000000..c39088f9cb4 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DiamondCity.java @@ -0,0 +1,108 @@ +package mage.cards.d; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.condition.Condition; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.decorator.ConditionalActivatedAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.effects.common.counter.MoveCountersFromSourceToTargetEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueConditionHint; +import mage.abilities.mana.ColorlessManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.common.TargetCreaturePermanent; +import mage.watchers.common.PermanentsEnteredBattlefieldWatcher; + +import java.util.List; +import java.util.UUID; + +/** + * + * @author notgreat + */ +public final class DiamondCity extends CardImpl { + + public DiamondCity(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + // Diamond City enters the battlefield with a shield counter on it. + this.addAbility(new EntersBattlefieldAbility( + new AddCountersSourceEffect(CounterType.SHIELD.createInstance(1)), + "with a shield counter on it. (If it would be dealt damage " + + "or destroyed, remove a shield counter from it instead.)" + )); + + // {T}: Add {C}. + this.addAbility(new ColorlessManaAbility()); + + // {T}: Move a shield counter from Diamond City onto target creature. Activate only if two or more creatures entered the battlefield under your control this turn. + Ability ability = new ConditionalActivatedAbility(Zone.BATTLEFIELD, new MoveCountersFromSourceToTargetEffect(CounterType.SHIELD), + new TapSourceCost(), DiamondCityCondition.instance); + ability.addTarget(new TargetCreaturePermanent()); + ability.addHint(DiamondCityCreaturesThatEnteredThisTurnCount.getHint()); + this.addAbility(ability, new PermanentsEnteredBattlefieldWatcher()); + } + + private DiamondCity(final DiamondCity card) { + super(card); + } + + @Override + public DiamondCity copy() { + return new DiamondCity(this); + } +} + +enum DiamondCityCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + return DiamondCityCreaturesThatEnteredThisTurnCount.instance.calculate(game, source, null) >= 2; + } + + @Override + public String toString() { + return "two or more creatures entered the battlefield under your control this turn"; + } +} + +enum DiamondCityCreaturesThatEnteredThisTurnCount implements DynamicValue { + instance; + + private static final Hint hint = new ValueConditionHint(instance, DiamondCityCondition.instance); + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + PermanentsEnteredBattlefieldWatcher watcher = game.getState().getWatcher(PermanentsEnteredBattlefieldWatcher.class); + if (watcher != null) { + List list = watcher.getThisTurnEnteringPermanents(sourceAbility.getControllerId()); + return (int) list.stream().filter(MageObject::isCreature).count(); + } + return 0; + } + + @Override + public DynamicValue copy() { + return instance; + } + + @Override + public String getMessage() { + return "creatures that attacked this turn"; + } + + public static Hint getHint() { + return hint; + } +} diff --git a/Mage.Sets/src/mage/cards/g/GallantPieWielder.java b/Mage.Sets/src/mage/cards/g/GallantPieWielder.java index 17ac6b67605..e7c79368726 100644 --- a/Mage.Sets/src/mage/cards/g/GallantPieWielder.java +++ b/Mage.Sets/src/mage/cards/g/GallantPieWielder.java @@ -12,7 +12,7 @@ import mage.cards.CardSetInfo; import mage.constants.AbilityWord; import mage.constants.CardType; import mage.constants.SubType; -import mage.watchers.common.CelebrationWatcher; +import mage.watchers.common.PermanentsEnteredBattlefieldWatcher; import java.util.UUID; @@ -37,7 +37,7 @@ public final class GallantPieWielder extends CardImpl { new GainAbilitySourceEffect(DoubleStrikeAbility.getInstance()), CelebrationCondition.instance, "{this} has double strike as long as two or " + "more nonland permanents entered the battlefield under your control this turn" - )).addHint(CelebrationCondition.getHint()).setAbilityWord(AbilityWord.CELEBRATION), new CelebrationWatcher()); + )).addHint(CelebrationCondition.getHint()).setAbilityWord(AbilityWord.CELEBRATION), new PermanentsEnteredBattlefieldWatcher()); } private GallantPieWielder(final GallantPieWielder card) { diff --git a/Mage.Sets/src/mage/cards/g/GoddricCloakedReveler.java b/Mage.Sets/src/mage/cards/g/GoddricCloakedReveler.java index acf329b9259..4d93474b591 100644 --- a/Mage.Sets/src/mage/cards/g/GoddricCloakedReveler.java +++ b/Mage.Sets/src/mage/cards/g/GoddricCloakedReveler.java @@ -16,7 +16,7 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.common.FilterCreaturePermanent; import mage.game.permanent.token.custom.CreatureToken; -import mage.watchers.common.CelebrationWatcher; +import mage.watchers.common.PermanentsEnteredBattlefieldWatcher; import java.util.UUID; @@ -65,7 +65,7 @@ public final class GoddricCloakedReveler extends CardImpl { )); ability.setAbilityWord(AbilityWord.CELEBRATION); ability.addHint(CelebrationCondition.getHint()); - this.addAbility(ability, new CelebrationWatcher()); + this.addAbility(ability, new PermanentsEnteredBattlefieldWatcher()); } private GoddricCloakedReveler(final GoddricCloakedReveler card) { diff --git a/Mage.Sets/src/mage/cards/g/GrandBallGuest.java b/Mage.Sets/src/mage/cards/g/GrandBallGuest.java index 50358262f58..09f02578333 100644 --- a/Mage.Sets/src/mage/cards/g/GrandBallGuest.java +++ b/Mage.Sets/src/mage/cards/g/GrandBallGuest.java @@ -14,7 +14,7 @@ import mage.constants.AbilityWord; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.watchers.common.CelebrationWatcher; +import mage.watchers.common.PermanentsEnteredBattlefieldWatcher; import java.util.UUID; @@ -42,7 +42,7 @@ public final class GrandBallGuest extends CardImpl { + "permanents entered the battlefield under your control this turn" )); ability.addHint(CelebrationCondition.getHint()).setAbilityWord(AbilityWord.CELEBRATION); - this.addAbility(ability, new CelebrationWatcher()); + this.addAbility(ability, new PermanentsEnteredBattlefieldWatcher()); } private GrandBallGuest(final GrandBallGuest card) { diff --git a/Mage.Sets/src/mage/cards/l/LadyOfLaughter.java b/Mage.Sets/src/mage/cards/l/LadyOfLaughter.java index ca8f7fe1b7d..8a86b21516b 100644 --- a/Mage.Sets/src/mage/cards/l/LadyOfLaughter.java +++ b/Mage.Sets/src/mage/cards/l/LadyOfLaughter.java @@ -11,7 +11,7 @@ import mage.constants.AbilityWord; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.TargetController; -import mage.watchers.common.CelebrationWatcher; +import mage.watchers.common.PermanentsEnteredBattlefieldWatcher; import java.util.UUID; @@ -35,7 +35,7 @@ public final class LadyOfLaughter extends CardImpl { this.addAbility(new BeginningOfEndStepTriggeredAbility( new DrawCardSourceControllerEffect(1), TargetController.YOU, CelebrationCondition.instance, false - ).addHint(CelebrationCondition.getHint()).setAbilityWord(AbilityWord.CELEBRATION), new CelebrationWatcher()); + ).addHint(CelebrationCondition.getHint()).setAbilityWord(AbilityWord.CELEBRATION), new PermanentsEnteredBattlefieldWatcher()); } private LadyOfLaughter(final LadyOfLaughter card) { diff --git a/Mage.Sets/src/mage/cards/l/LastNightTogether.java b/Mage.Sets/src/mage/cards/l/LastNightTogether.java new file mode 100644 index 00000000000..1988c97d5c3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LastNightTogether.java @@ -0,0 +1,137 @@ +package mage.cards.l; + +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.UntapTargetEffect; +import mage.abilities.effects.common.combat.CantAttackAllEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.keyword.HasteAbility; +import mage.abilities.keyword.IndestructibleAbility; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.TurnPhase; +import mage.counters.CounterType; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.permanent.PermanentReferenceInCollectionPredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.turn.TurnMod; +import mage.target.common.TargetCreaturePermanent; + +import java.util.List; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * @author notgreat + */ +public final class LastNightTogether extends CardImpl { + + public LastNightTogether(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{R}{G}"); + + // Choose two target creatures. Untap them. Put two +1/+1 counters on each of them. They gain vigilance, indestructible, and haste until end of turn. After this main phase, there is an additional combat phase. Only the chosen creatures can attack during that combat phase. + this.getSpellAbility().addEffect(new UntapTargetEffect().setText("Choose two target creatures. Untap them")); + this.getSpellAbility().addEffect(new AddCountersTargetEffect(CounterType.P1P1.createInstance(2)).setText("Put two +1/+1 counters on each of them")); + this.getSpellAbility().addEffect(new GainAbilityTargetEffect(VigilanceAbility.getInstance()).setText("They gain vigilance")); + this.getSpellAbility().addEffect(new GainAbilityTargetEffect(IndestructibleAbility.getInstance()).setText("indestructible").concatBy(",")); + this.getSpellAbility().addEffect(new GainAbilityTargetEffect(HasteAbility.getInstance()).setText("haste until end of turn").concatBy(", and")); + this.getSpellAbility().addEffect(new LastNightTogetherEffect()); + + this.getSpellAbility().addTarget(new TargetCreaturePermanent(2)); + + } + + private LastNightTogether(final LastNightTogether card) { + super(card); + } + + @Override + public LastNightTogether copy() { + return new LastNightTogether(this); + } +} + +//Based on AddCombatAndMainPhaseEffect +class LastNightTogetherEffect extends OneShotEffect { + + LastNightTogetherEffect() { + super(Outcome.Benefit); + staticText = "After this main phase, there is an additional combat phase. Only the chosen creatures can attack during that combat phase"; + } + + private LastNightTogetherEffect(final LastNightTogetherEffect effect) { + super(effect); + } + + @Override + public LastNightTogetherEffect copy() { + return new LastNightTogetherEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + // 15.07.2006 If it's somehow not a main phase when Fury of the Horde resolves, all it does is untap all creatures that attacked that turn. No new phases are created. + // Same ruling applies here + if (game.getTurnPhaseType() == TurnPhase.PRECOMBAT_MAIN || game.getTurnPhaseType() == TurnPhase.POSTCOMBAT_MAIN) { + // At the start of that combat, add a restriction effect preventing other creatures from attacking. + TurnMod combat = new TurnMod(game.getState().getActivePlayerId()).withExtraPhase(TurnPhase.COMBAT); + game.getState().getTurnMods().add(combat); + List targets = source.getTargets().get(0).getTargets(); + LastNightTogetherDelayedCantAttackAbility delayedTriggeredAbility = new LastNightTogetherDelayedCantAttackAbility(targets, game, combat.getId()); + game.addDelayedTriggeredAbility(delayedTriggeredAbility, source); + return true; + } + return true; + } + +} + +class LastNightTogetherDelayedCantAttackAbility extends DelayedTriggeredAbility { + + private final UUID connectedTurnMod; + + LastNightTogetherDelayedCantAttackAbility(List targets, Game game, UUID connectedTurnMod) { + super(null, Duration.EndOfTurn); + FilterCreaturePermanent filterRestriction = new FilterCreaturePermanent(); + Set targetRefs = targets.stream().map(x -> new MageObjectReference(x, game)).collect(Collectors.toSet()); + filterRestriction.add(Predicates.not(new PermanentReferenceInCollectionPredicate(targetRefs))); + this.addEffect(new CantAttackAllEffect(Duration.EndOfCombat, filterRestriction)); + this.usesStack = false; // don't show this to the user + this.connectedTurnMod = connectedTurnMod; + } + + LastNightTogetherDelayedCantAttackAbility(LastNightTogetherDelayedCantAttackAbility ability) { + super(ability); + this.connectedTurnMod = ability.connectedTurnMod; + } + + @Override + public LastNightTogetherDelayedCantAttackAbility copy() { + return new LastNightTogetherDelayedCantAttackAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.PHASE_CHANGED; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return (event.getType() == GameEvent.EventType.PHASE_CHANGED && this.connectedTurnMod.equals(event.getSourceId())); + } + + @Override + public String getRule() { + return "Only the chosen creatures can attack during that combat phase"; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/m/MilitantAngel.java b/Mage.Sets/src/mage/cards/m/MilitantAngel.java index 17092af0f46..9da71ccaad9 100644 --- a/Mage.Sets/src/mage/cards/m/MilitantAngel.java +++ b/Mage.Sets/src/mage/cards/m/MilitantAngel.java @@ -36,7 +36,8 @@ public final class MilitantAngel extends CardImpl { // When Militant Angel enters the battlefield, create a number of 2/2 white Knight creature tokens with vigilance equal to the number of opponents you attacked this turn. Effect effect = new CreateTokenEffect(new KnightToken(), AttackedThisTurnOpponentsCount.instance); effect.setText("create a number of 2/2 white Knight creature tokens with vigilance equal to the number of opponents you attacked this turn"); - this.addAbility(new EntersBattlefieldTriggeredAbility(effect), new PlayersAttackedThisTurnWatcher()); + this.addAbility(new EntersBattlefieldTriggeredAbility(effect).addHint(AttackedThisTurnOpponentsCount.getHint()), + new PlayersAttackedThisTurnWatcher()); } private MilitantAngel(final MilitantAngel card) { diff --git a/Mage.Sets/src/mage/cards/n/NanogeneConversion.java b/Mage.Sets/src/mage/cards/n/NanogeneConversion.java new file mode 100644 index 00000000000..93ec6f2f40a --- /dev/null +++ b/Mage.Sets/src/mage/cards/n/NanogeneConversion.java @@ -0,0 +1,76 @@ +package mage.cards.n; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SuperType; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.util.functions.CopyApplier; + +import java.util.UUID; + +/** + * + * @author notgreat + */ +public final class NanogeneConversion extends CardImpl { + + public NanogeneConversion(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{U}"); + + // Choose target creature you control. Each other creature becomes a copy of that creature until end of turn, except it isn't legendary. + this.getSpellAbility().addEffect(new NanogeneConversionEffect()); + this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); + } + + private NanogeneConversion(final NanogeneConversion card) { + super(card); + } + + @Override + public NanogeneConversion copy() { + return new NanogeneConversion(this); + } +} +//Based on Augmenter Pugilist's EchoingEquationEffect +class NanogeneConversionEffect extends OneShotEffect { + + NanogeneConversionEffect() { + super(Outcome.Benefit); + staticText = "choose target creature you control. Each other creature you control becomes a copy of it until end of turn, except those creatures aren't legendary"; + } + + private NanogeneConversionEffect(final NanogeneConversionEffect effect) { + super(effect); + } + + @Override + public NanogeneConversionEffect copy() { + return new NanogeneConversionEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent copyFrom = game.getPermanent(source.getFirstTarget()); + if (copyFrom != null) { + game.getBattlefield().getActivePermanents(source.getControllerId(), game).stream() + .filter(permanent -> permanent.isCreature(game) && !permanent.getId().equals(copyFrom.getId())) + .forEach(copyTo -> game.copyPermanent(Duration.EndOfTurn, copyFrom, copyTo.getId(), source, new CopyApplier() { + @Override + public boolean apply(Game game, MageObject blueprint, Ability source, UUID targetObjectId) { + blueprint.removeSuperType(SuperType.LEGENDARY); + return true; + } + })); + return true; + } + return false; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/p/PestsOfHonor.java b/Mage.Sets/src/mage/cards/p/PestsOfHonor.java index aa9038ff639..03037df78c6 100644 --- a/Mage.Sets/src/mage/cards/p/PestsOfHonor.java +++ b/Mage.Sets/src/mage/cards/p/PestsOfHonor.java @@ -12,7 +12,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.TargetController; import mage.counters.CounterType; -import mage.watchers.common.CelebrationWatcher; +import mage.watchers.common.PermanentsEnteredBattlefieldWatcher; import java.util.UUID; @@ -35,7 +35,7 @@ public final class PestsOfHonor extends CardImpl { TargetController.YOU, false ), CelebrationCondition.instance, "At the beginning of combat on your turn, if two or more " + "nonland permanents entered the battlefield under your control this turn, put a +1/+1 counter on {this}." - ).addHint(CelebrationCondition.getHint()).setAbilityWord(AbilityWord.CELEBRATION), new CelebrationWatcher()); + ).addHint(CelebrationCondition.getHint()).setAbilityWord(AbilityWord.CELEBRATION), new PermanentsEnteredBattlefieldWatcher()); } private PestsOfHonor(final PestsOfHonor card) { diff --git a/Mage.Sets/src/mage/cards/r/RagingBattleMouse.java b/Mage.Sets/src/mage/cards/r/RagingBattleMouse.java index fb2d6b4a7d7..5f680a1b527 100644 --- a/Mage.Sets/src/mage/cards/r/RagingBattleMouse.java +++ b/Mage.Sets/src/mage/cards/r/RagingBattleMouse.java @@ -16,7 +16,7 @@ import mage.constants.*; import mage.filter.StaticFilters; import mage.game.Game; import mage.target.common.TargetControlledCreaturePermanent; -import mage.watchers.common.CelebrationWatcher; +import mage.watchers.common.PermanentsEnteredBattlefieldWatcher; import mage.watchers.common.SpellsCastWatcher; import java.util.UUID; @@ -51,7 +51,7 @@ public final class RagingBattleMouse extends CardImpl { ability.addTarget(new TargetControlledCreaturePermanent()); ability.setAbilityWord(AbilityWord.CELEBRATION); ability.addHint(CelebrationCondition.getHint()); - this.addAbility(ability, new CelebrationWatcher()); + this.addAbility(ability, new PermanentsEnteredBattlefieldWatcher()); } private RagingBattleMouse(final RagingBattleMouse card) { diff --git a/Mage.Sets/src/mage/cards/r/ReturnThePast.java b/Mage.Sets/src/mage/cards/r/ReturnThePast.java new file mode 100644 index 00000000000..26cba69ed6a --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/ReturnThePast.java @@ -0,0 +1,74 @@ +package mage.cards.r; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.hint.common.MyTurnHint; +import mage.abilities.keyword.FlashbackAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.players.Player; + +import java.util.UUID; + +/** + * + * @author notgreat + */ +public final class ReturnThePast extends CardImpl { + + public ReturnThePast(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{4}{R}{R}"); + + + // As long as it's your turn, each instant and sorcery card in your graveyard has flashback. Its flashback cost is equal to its mana cost. + this.addAbility(new SimpleStaticAbility(new ReturnThePastEffect()).addHint(MyTurnHint.instance)); + } + + private ReturnThePast(final ReturnThePast card) { + super(card); + } + + @Override + public ReturnThePast copy() { + return new ReturnThePast(this); + } +} + +//Based on LierDiscipleOfTheDrownedFlashbackEffect +class ReturnThePastEffect extends ContinuousEffectImpl { + + ReturnThePastEffect() { + super(Duration.WhileOnBattlefield, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility); + this.staticText = "As long as it's your turn, each instant and sorcery card in your graveyard has flashback. " + + "Its flashback cost is equal to that card's mana cost"; + } + + private ReturnThePastEffect(final ReturnThePastEffect effect) { + super(effect); + } + + @Override + public ReturnThePastEffect copy() { + return new ReturnThePastEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null || game.getActivePlayerId() != source.getControllerId()) { + return false; + } + for (Card card : player.getGraveyard().getCards(StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY, game)) { + Ability ability = new FlashbackAbility(card, card.getManaCost()); + ability.setSourceId(card.getId()); + ability.setControllerId(card.getOwnerId()); + game.getState().addOtherAbility(card, ability); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/r/RoseCutthroatRaider.java b/Mage.Sets/src/mage/cards/r/RoseCutthroatRaider.java new file mode 100644 index 00000000000..598e86ea29e --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RoseCutthroatRaider.java @@ -0,0 +1,71 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.Mana; +import mage.abilities.Ability; +import mage.abilities.common.EndOfCombatTriggeredAbility; +import mage.abilities.common.SacrificePermanentTriggeredAbility; +import mage.abilities.condition.common.RaidCondition; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.dynamicvalue.common.AttackedThisTurnOpponentsCount; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.mana.AddManaToManaPoolSourceControllerEffect; +import mage.abilities.keyword.FirstStrikeAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AbilityWord; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.FilterPermanent; +import mage.game.permanent.token.JunkToken; +import mage.watchers.common.PlayerAttackedWatcher; +import mage.watchers.common.PlayersAttackedThisTurnWatcher; + +import java.util.UUID; + +/** + * @author notgreat + */ +public final class RoseCutthroatRaider extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent("a Junk"); + + static { + filter.add(SubType.JUNK.getPredicate()); + } + + public RoseCutthroatRaider(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{2}{R}{R}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.ROBOT); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // First strike + this.addAbility(FirstStrikeAbility.getInstance()); + + // Raid -- At end of combat on your turn, if you attacked this turn, create a Junk token for each opponent you attacked. + Ability ability = new ConditionalInterveningIfTriggeredAbility(new EndOfCombatTriggeredAbility( + new CreateTokenEffect(new JunkToken(), AttackedThisTurnOpponentsCount.instance), false), RaidCondition.instance, + "At end of combat on your turn, if you attacked this turn, create a Junk token for each opponent you attacked."); + ability.setAbilityWord(AbilityWord.RAID); + ability.addHint(AttackedThisTurnOpponentsCount.getHint()); + ability.addWatcher(new PlayerAttackedWatcher()); + ability.addWatcher(new PlayersAttackedThisTurnWatcher()); + this.addAbility(ability); + + // Whenever you sacrifice a Junk, add {R}. + this.addAbility(new SacrificePermanentTriggeredAbility(new AddManaToManaPoolSourceControllerEffect(Mana.RedMana(1)), filter)); + } + + private RoseCutthroatRaider(final RoseCutthroatRaider card) { + super(card); + } + + @Override + public RoseCutthroatRaider copy() { + return new RoseCutthroatRaider(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TuinvaleGuide.java b/Mage.Sets/src/mage/cards/t/TuinvaleGuide.java index bbddee831d4..91d128ac48e 100644 --- a/Mage.Sets/src/mage/cards/t/TuinvaleGuide.java +++ b/Mage.Sets/src/mage/cards/t/TuinvaleGuide.java @@ -15,7 +15,7 @@ import mage.constants.AbilityWord; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.watchers.common.CelebrationWatcher; +import mage.watchers.common.PermanentsEnteredBattlefieldWatcher; import java.util.UUID; @@ -46,7 +46,7 @@ public final class TuinvaleGuide extends CardImpl { + "permanents entered the battlefield under your control this turn" )); ability.addHint(CelebrationCondition.getHint()).setAbilityWord(AbilityWord.CELEBRATION); - this.addAbility(ability, new CelebrationWatcher()); + this.addAbility(ability, new PermanentsEnteredBattlefieldWatcher()); } private TuinvaleGuide(final TuinvaleGuide card) { diff --git a/Mage.Sets/src/mage/sets/DoctorWho.java b/Mage.Sets/src/mage/sets/DoctorWho.java index f708ac2fed4..2f46779c101 100644 --- a/Mage.Sets/src/mage/sets/DoctorWho.java +++ b/Mage.Sets/src/mage/sets/DoctorWho.java @@ -137,6 +137,7 @@ public final class DoctorWho extends ExpansionSet { cards.add(new SetCardInfo("Karvanista, Loyal Lupari", 106, Rarity.RARE, mage.cards.k.KarvanistaLoyalLupari.class)); cards.add(new SetCardInfo("Kate Stewart", 139, Rarity.RARE, mage.cards.k.KateStewart.class)); cards.add(new SetCardInfo("Laser Screwdriver", 178, Rarity.UNCOMMON, mage.cards.l.LaserScrewdriver.class)); + cards.add(new SetCardInfo("Last Night Together", 140, Rarity.RARE, mage.cards.l.LastNightTogether.class)); cards.add(new SetCardInfo("Lavaclaw Reaches", 289, Rarity.RARE, mage.cards.l.LavaclawReaches.class)); cards.add(new SetCardInfo("Leela, Sevateem Warrior", 107, Rarity.RARE, mage.cards.l.LeelaSevateemWarrior.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Leela, Sevateem Warrior", 398, Rarity.RARE, mage.cards.l.LeelaSevateemWarrior.class, NON_FULL_USE_VARIOUS)); @@ -152,6 +153,7 @@ public final class DoctorWho extends ExpansionSet { cards.add(new SetCardInfo("Mountain", 202, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Myriad Landscape", 290, Rarity.UNCOMMON, mage.cards.m.MyriadLandscape.class)); cards.add(new SetCardInfo("Mystic Monastery", 291, Rarity.UNCOMMON, mage.cards.m.MysticMonastery.class)); + cards.add(new SetCardInfo("Nanogene Conversion", 49, Rarity.RARE, mage.cards.n.NanogeneConversion.class)); cards.add(new SetCardInfo("Ominous Cemetery", 189, Rarity.UNCOMMON, mage.cards.o.OminousCemetery.class)); cards.add(new SetCardInfo("Out of Time", 209, Rarity.RARE, mage.cards.o.OutOfTime.class)); cards.add(new SetCardInfo("Overgrown Farmland", 292, Rarity.RARE, mage.cards.o.OvergrownFarmland.class)); @@ -169,6 +171,7 @@ public final class DoctorWho extends ExpansionSet { cards.add(new SetCardInfo("Regenerations Restored", 151, Rarity.RARE, mage.cards.r.RegenerationsRestored.class)); cards.add(new SetCardInfo("Reliquary Tower", 296, Rarity.UNCOMMON, mage.cards.r.ReliquaryTower.class)); cards.add(new SetCardInfo("Renegade Silent", 53, Rarity.UNCOMMON, mage.cards.r.RenegadeSilent.class)); + cards.add(new SetCardInfo("Return the Past", 92, Rarity.RARE, mage.cards.r.ReturnThePast.class)); cards.add(new SetCardInfo("Return to Dust", 211, Rarity.UNCOMMON, mage.cards.r.ReturnToDust.class)); cards.add(new SetCardInfo("Reverse the Polarity", 54, Rarity.RARE, mage.cards.r.ReverseThePolarity.class)); cards.add(new SetCardInfo("River of Tears", 297, Rarity.RARE, mage.cards.r.RiverOfTears.class)); diff --git a/Mage.Sets/src/mage/sets/Fallout.java b/Mage.Sets/src/mage/sets/Fallout.java index 193859d42b8..dbbca115211 100644 --- a/Mage.Sets/src/mage/sets/Fallout.java +++ b/Mage.Sets/src/mage/sets/Fallout.java @@ -103,6 +103,7 @@ public final class Fallout extends ExpansionSet { cards.add(new SetCardInfo("Darkwater Catacombs", 260, Rarity.RARE, mage.cards.d.DarkwaterCatacombs.class)); cards.add(new SetCardInfo("Deadly Dispute", 184, Rarity.COMMON, mage.cards.d.DeadlyDispute.class)); cards.add(new SetCardInfo("Desolate Mire", 146, Rarity.RARE, mage.cards.d.DesolateMire.class)); + cards.add(new SetCardInfo("Diamond City", 147, Rarity.RARE, mage.cards.d.DiamondCity.class)); cards.add(new SetCardInfo("Dispatch", 159, Rarity.UNCOMMON, mage.cards.d.Dispatch.class)); cards.add(new SetCardInfo("Dogmeat, Ever Loyal", 2, Rarity.MYTHIC, mage.cards.d.DogmeatEverLoyal.class)); cards.add(new SetCardInfo("Dr. Madison Li", 3, Rarity.MYTHIC, mage.cards.d.DrMadisonLi.class)); @@ -282,6 +283,7 @@ public final class Fallout extends ExpansionSet { cards.add(new SetCardInfo("Robobrain War Mind", 38, Rarity.UNCOMMON, mage.cards.r.RobobrainWarMind.class)); cards.add(new SetCardInfo("Rogue's Passage", 283, Rarity.UNCOMMON, mage.cards.r.RoguesPassage.class)); cards.add(new SetCardInfo("Rootbound Crag", 284, Rarity.RARE, mage.cards.r.RootboundCrag.class)); + cards.add(new SetCardInfo("Rose, Cutthroat Raider", 66, Rarity.RARE, mage.cards.r.RoseCutthroatRaider.class)); cards.add(new SetCardInfo("Ruinous Ultimatum", 220, Rarity.RARE, mage.cards.r.RuinousUltimatum.class)); cards.add(new SetCardInfo("Rustvale Bridge", 285, Rarity.COMMON, mage.cards.r.RustvaleBridge.class)); cards.add(new SetCardInfo("Ruthless Radrat", 48, Rarity.UNCOMMON, mage.cards.r.RuthlessRadrat.class)); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/CelebrationTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/CelebrationTest.java new file mode 100644 index 00000000000..edf06ad9bd3 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/CelebrationTest.java @@ -0,0 +1,82 @@ +package org.mage.test.cards.abilities.keywords; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Ignore; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author notgreat + */ +public class CelebrationTest extends CardTestPlayerBase { + @Test + public void testBasic1() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + addCard(Zone.HAND, playerA, "Armory Mice"); + + setStrictChooseMode(true); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Armory Mice"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertPowerToughness(playerA, "Armory Mice", 3, 1); + assertGraveyardCount(playerA, 0); + } + @Test + public void testBasic2() { + addCard(Zone.HAND, playerA, "Armory Mice"); + addCard(Zone.HAND, playerA, "Black Lotus"); + + setStrictChooseMode(true); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Black Lotus", true); + setChoice(playerA, "White"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Armory Mice"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertPowerToughness(playerA, "Armory Mice", 3, 3); + assertGraveyardCount(playerA, 1); + } + @Test + public void testContinuousModifier1() { + addCard(Zone.BATTLEFIELD, playerA, "Ashaya, Soul of the Wild"); + addCard(Zone.HAND, playerA, "Armory Mice"); + addCard(Zone.HAND, playerA, "Black Lotus"); + + setStrictChooseMode(true); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Black Lotus", true); + setChoice(playerA, "White"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Armory Mice"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertPowerToughness(playerA, "Armory Mice", 3, 1); + assertGraveyardCount(playerA, 1); + } + @Test + @Ignore //Currently failing due to PermanentsEnteredBattlefieldWatcher not storing permanents' current state + public void testContinuousModifier2() { + addCard(Zone.BATTLEFIELD, playerA, "Ashaya, Soul of the Wild"); + addCard(Zone.HAND, playerA, "Armory Mice"); + addCard(Zone.HAND, playerA, "Swords to Plowshares"); + addCard(Zone.HAND, playerA, "Black Lotus"); + + setStrictChooseMode(true); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Black Lotus", true); + setChoice(playerA, "White"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Armory Mice", true); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Swords to Plowshares", "Ashaya, Soul of the Wild", true); + //Even though the Ashaya is now gone, the Armory Mice entered as a land + //Thus only the only nonland permanent that ETB this turn is Black Lotus, and Celebration should not be on + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertPowerToughness(playerA, "Armory Mice", 3, 1); + assertGraveyardCount(playerA, 2); + } +} diff --git a/Mage/src/main/java/mage/abilities/condition/common/CelebrationCondition.java b/Mage/src/main/java/mage/abilities/condition/common/CelebrationCondition.java index ddc90bb27e8..0b6a6ca94ce 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/CelebrationCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/CelebrationCondition.java @@ -3,22 +3,26 @@ package mage.abilities.condition.common; import mage.abilities.Ability; import mage.abilities.condition.Condition; -import mage.abilities.hint.ConditionHint; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueConditionHint; import mage.game.Game; -import mage.watchers.common.CelebrationWatcher; +import mage.game.permanent.Permanent; +import mage.watchers.common.PermanentsEnteredBattlefieldWatcher; + +import java.util.List; /** * @author Susucr */ public enum CelebrationCondition implements Condition { instance; - private static final Hint hint = new ConditionHint(instance, "You had two or more nonland permanents enter this turn"); + private static final Hint hint = new ValueConditionHint(CelebrationNonlandsThatEnteredThisTurnCount.instance, instance); @Override public boolean apply(Game game, Ability source) { - CelebrationWatcher watcher = game.getState().getWatcher(CelebrationWatcher.class); - return watcher != null && watcher.celebrationActive(source.getControllerId()); + return CelebrationNonlandsThatEnteredThisTurnCount.instance.calculate(game, source, null) >= 2; } @Override @@ -30,3 +34,29 @@ public enum CelebrationCondition implements Condition { return hint; } } + +enum CelebrationNonlandsThatEnteredThisTurnCount implements DynamicValue { + instance; + + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + PermanentsEnteredBattlefieldWatcher watcher = game.getState().getWatcher(PermanentsEnteredBattlefieldWatcher.class); + if (watcher != null) { + List list = watcher.getThisTurnEnteringPermanents(sourceAbility.getControllerId()); + return (int) list.stream().filter(x -> !x.isLand(game)).count(); + } + return 0; + } + + @Override + public DynamicValue copy() { + return instance; + } + + @Override + public String getMessage() { + return "nonland permanents that entered under your control this turn"; + } + +} diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/AttackedThisTurnOpponentsCount.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/AttackedThisTurnOpponentsCount.java index dd177feaf48..58ea6397103 100644 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/AttackedThisTurnOpponentsCount.java +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/AttackedThisTurnOpponentsCount.java @@ -4,6 +4,8 @@ package mage.abilities.dynamicvalue.common; import mage.abilities.Ability; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.effects.Effect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; import mage.game.Game; import mage.watchers.common.PlayersAttackedThisTurnWatcher; @@ -14,6 +16,7 @@ import mage.watchers.common.PlayersAttackedThisTurnWatcher; */ public enum AttackedThisTurnOpponentsCount implements DynamicValue { instance; + private static final Hint hint = new ValueHint("the number of opponents you attacked this turn", instance); @Override public int calculate(Game game, Ability sourceAbility, Effect effect) { @@ -38,4 +41,8 @@ public enum AttackedThisTurnOpponentsCount implements DynamicValue { public String getMessage() { return "the number of opponents you attacked this turn"; } + + public static Hint getHint() { + return hint; + } } diff --git a/Mage/src/main/java/mage/abilities/hint/ValueConditionHint.java b/Mage/src/main/java/mage/abilities/hint/ValueConditionHint.java new file mode 100644 index 00000000000..008ee44543a --- /dev/null +++ b/Mage/src/main/java/mage/abilities/hint/ValueConditionHint.java @@ -0,0 +1,38 @@ +package mage.abilities.hint; + +import mage.abilities.Ability; +import mage.abilities.condition.Condition; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.game.Game; + +/** + * @author notgreat + */ +public class ValueConditionHint extends ConditionHint { + + private final DynamicValue value; + + public ValueConditionHint(DynamicValue value, Condition condition) { + this(condition.toString(), value, condition); + } + + public ValueConditionHint(String name, DynamicValue value, Condition condition) { + super(condition, name); + this.value = value; + } + + private ValueConditionHint(final ValueConditionHint hint) { + super(hint); + this.value = hint.value.copy(); + } + + @Override + public String getText(Game game, Ability ability) { + return super.getText(game, ability) + " (current: " + value.calculate(game, ability, null) + ")"; + } + + @Override + public ValueConditionHint copy() { + return new ValueConditionHint(this); + } +} diff --git a/Mage/src/main/java/mage/watchers/common/CelebrationWatcher.java b/Mage/src/main/java/mage/watchers/common/CelebrationWatcher.java deleted file mode 100644 index d191b94f947..00000000000 --- a/Mage/src/main/java/mage/watchers/common/CelebrationWatcher.java +++ /dev/null @@ -1,47 +0,0 @@ -package mage.watchers.common; - -import mage.constants.WatcherScope; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.util.CardUtil; -import mage.watchers.Watcher; - -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; - -/** - * @author Susucr - */ -public class CelebrationWatcher extends Watcher { - - // playerId -> number of nonland permanents entered the battlefield this turn under that player's control. - private final Map celebrationCounts = new HashMap<>(); - - public CelebrationWatcher() { - super(WatcherScope.GAME); - } - - @Override - public void watch(GameEvent event, Game game) { - if (event.getType() != GameEvent.EventType.ENTERS_THE_BATTLEFIELD) { - return; - } - - Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent != null && !permanent.isLand(game)) { - celebrationCounts.compute(permanent.getControllerId(), CardUtil::setOrIncrementValue); - } - } - - public boolean celebrationActive(UUID playerId) { - return celebrationCounts.getOrDefault(playerId, 0) >= 2; - } - - @Override - public void reset() { - super.reset(); - celebrationCounts.clear(); - } -}