mirror of
https://github.com/magefree/mage.git
synced 2026-01-26 21:29:17 -08:00
[OTJ] Implement Slick Sequence
This commit is contained in:
parent
18a05acd81
commit
5a16d319d2
3 changed files with 240 additions and 0 deletions
116
Mage.Sets/src/mage/cards/s/SlickSequence.java
Normal file
116
Mage.Sets/src/mage/cards/s/SlickSequence.java
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
package mage.cards.s;
|
||||
|
||||
import mage.MageObjectReference;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.condition.Condition;
|
||||
import mage.abilities.decorator.ConditionalOneShotEffect;
|
||||
import mage.abilities.effects.common.DamageTargetEffect;
|
||||
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
|
||||
import mage.abilities.hint.ConditionHint;
|
||||
import mage.abilities.hint.Hint;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.WatcherScope;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.stack.Spell;
|
||||
import mage.target.common.TargetAnyTarget;
|
||||
import mage.watchers.Watcher;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author Susucr
|
||||
*/
|
||||
public final class SlickSequence extends CardImpl {
|
||||
|
||||
public SlickSequence(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{U}{R}");
|
||||
|
||||
// Slick Sequence deals 2 damage to any target. If you've cast another spell this turn, draw a card.
|
||||
this.getSpellAbility().addEffect(new DamageTargetEffect(2));
|
||||
this.getSpellAbility().addTarget(new TargetAnyTarget());
|
||||
this.getSpellAbility().addEffect(
|
||||
new ConditionalOneShotEffect(
|
||||
new DrawCardSourceControllerEffect(1),
|
||||
SlickSequenceCondition.instance
|
||||
)
|
||||
);
|
||||
this.getSpellAbility().addHint(SlickSequenceCondition.hint);
|
||||
this.getSpellAbility().addWatcher(new SlickSequenceWatcher());
|
||||
}
|
||||
|
||||
private SlickSequence(final SlickSequence card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SlickSequence copy() {
|
||||
return new SlickSequence(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
enum SlickSequenceCondition implements Condition {
|
||||
instance;
|
||||
static final Hint hint = new ConditionHint(instance, "you've cast another spell this turn");
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
SlickSequenceWatcher watcher = game.getState().getWatcher(SlickSequenceWatcher.class);
|
||||
if (watcher == null) {
|
||||
return false;
|
||||
}
|
||||
// may be null, watcher will handle that.
|
||||
Spell sourceSpell = game.getSpell(source.getSourceId());
|
||||
return watcher.didPlayerCastAnotherSpellThisTurn(source.getControllerId(), sourceSpell, game);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "you've cast another spell this turn";
|
||||
}
|
||||
}
|
||||
|
||||
class SlickSequenceWatcher extends Watcher {
|
||||
|
||||
// Per player, MOR of the spells cast this turn.
|
||||
private final Map<UUID, Set<MageObjectReference>> spellsCastThisTurn = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Game default watcher
|
||||
*/
|
||||
public SlickSequenceWatcher() {
|
||||
super(WatcherScope.GAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void watch(GameEvent event, Game game) {
|
||||
if (event.getType() == GameEvent.EventType.SPELL_CAST) {
|
||||
UUID playerId = event.getPlayerId();
|
||||
if (playerId != null) {
|
||||
MageObjectReference mor = new MageObjectReference(event.getTargetId(), game);
|
||||
spellsCastThisTurn.computeIfAbsent(playerId, x -> new HashSet<>()).add(mor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
super.reset();
|
||||
spellsCastThisTurn.clear();
|
||||
}
|
||||
|
||||
boolean didPlayerCastAnotherSpellThisTurn(UUID playerId, Spell spell, Game game) {
|
||||
Set<MageObjectReference> spells = spellsCastThisTurn.getOrDefault(playerId, new HashSet<>());
|
||||
if (spell == null) {
|
||||
// Not currently a spell, so any spell recorded does count as another.
|
||||
return !spells.isEmpty();
|
||||
} else {
|
||||
// Is currently a spell, so need to exclude that one.
|
||||
MageObjectReference spellMOR = new MageObjectReference(spell.getId(), game);
|
||||
return spells.stream().anyMatch(mor -> !mor.equals(spellMOR));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -140,6 +140,7 @@ public final class OutlawsOfThunderJunction extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Shepherd of the Clouds", 28, Rarity.UNCOMMON, mage.cards.s.ShepherdOfTheClouds.class));
|
||||
cards.add(new SetCardInfo("Sheriff of Safe Passage", 29, Rarity.UNCOMMON, mage.cards.s.SheriffOfSafePassage.class));
|
||||
cards.add(new SetCardInfo("Shoot the Sheriff", 106, Rarity.UNCOMMON, mage.cards.s.ShootTheSheriff.class));
|
||||
cards.add(new SetCardInfo("Slick Sequence", 233, Rarity.UNCOMMON, mage.cards.s.SlickSequence.class));
|
||||
cards.add(new SetCardInfo("Slickshot Lockpicker", 67, Rarity.UNCOMMON, mage.cards.s.SlickshotLockpicker.class));
|
||||
cards.add(new SetCardInfo("Slickshot Show-Off", 146, Rarity.RARE, mage.cards.s.SlickshotShowOff.class));
|
||||
cards.add(new SetCardInfo("Slickshot Vault-Buster", 68, Rarity.COMMON, mage.cards.s.SlickshotVaultBuster.class));
|
||||
|
|
|
|||
|
|
@ -0,0 +1,123 @@
|
|||
package org.mage.test.cards.single.otj;
|
||||
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
* @author Susucr
|
||||
*/
|
||||
public class SlickSequenceTest extends CardTestPlayerBase {
|
||||
|
||||
/**
|
||||
* {@link mage.cards.s.SlickSequence Slick Sequence} {U}{R}
|
||||
* Instant
|
||||
* Slick Sequence deals 2 damage to any target. If you’ve cast another spell this turn, draw a card.
|
||||
*/
|
||||
private static final String sequence = "Slick Sequence";
|
||||
|
||||
@Test
|
||||
public void testOne() {
|
||||
setStrictChooseMode(true);
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Volcanic Island", 2);
|
||||
addCard(Zone.HAND, playerA, sequence);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, sequence, playerB);
|
||||
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
setStrictChooseMode(true);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerA, sequence, 1);
|
||||
assertLife(playerB, 20 - 2);
|
||||
assertHandCount(playerA, 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOneResolveThenAnother() {
|
||||
setStrictChooseMode(true);
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Volcanic Island", 4);
|
||||
addCard(Zone.HAND, playerA, sequence, 2);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, sequence, playerB);
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, sequence, playerB);
|
||||
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
setStrictChooseMode(true);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerA, sequence, 2);
|
||||
assertLife(playerB, 20 - 2 * 2);
|
||||
assertHandCount(playerA, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOneThenAnotherInResponse() {
|
||||
setStrictChooseMode(true);
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Volcanic Island", 4);
|
||||
addCard(Zone.HAND, playerA, sequence, 2);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, sequence, playerB);
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, sequence, playerB);
|
||||
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
setStrictChooseMode(true);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerA, sequence, 2);
|
||||
assertLife(playerB, 20 - 2 * 2);
|
||||
assertHandCount(playerA, 1 * 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOneRemandedThenRecast() {
|
||||
setStrictChooseMode(true);
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Volcanic Island", 4);
|
||||
addCard(Zone.HAND, playerA, sequence, 1);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Island", 2);
|
||||
addCard(Zone.HAND, playerB, "Remand", 1);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, sequence, playerB);
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Remand", sequence);
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, sequence, playerB);
|
||||
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
setStrictChooseMode(true);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerA, sequence, 1);
|
||||
assertGraveyardCount(playerB, "Remand", 1);
|
||||
assertLife(playerB, 20 - 2);
|
||||
assertHandCount(playerA, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOtherPlayerSpellsNotCounting() {
|
||||
setStrictChooseMode(true);
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Volcanic Island", 2);
|
||||
addCard(Zone.HAND, playerA, sequence, 1);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1);
|
||||
addCard(Zone.HAND, playerB, "Lightning Bolt", 1);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Lightning Bolt", playerB);
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, sequence, playerB);
|
||||
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
setStrictChooseMode(true);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerA, sequence, 1);
|
||||
assertGraveyardCount(playerB, "Lightning Bolt", 1);
|
||||
assertLife(playerB, 20 - 2 - 3);
|
||||
assertHandCount(playerA, 0);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue