forked from External/mage
* added dungeon and dungeon room class * [AFR] Implemented Tomb of Annihilation * [AFR] Implemented Shortcut Seeker * [AFR] Implemented Gloom Stalker * [AFR] Implemented Nadaar, Selfless Paladin * added room triggers * added more venturing code, currently untested * fixed error * moved venture into dungeon from player class to game class * removed unnecessary sourceobject from dungeon * fixed npe error * added dungeon completion * fixed concurrent modification exception * added logging * added proper copy methods * added views * updated room text generation * added some missing code * finished implementing CompletedDungeonCondition * [AFR] Implemented Ellywick Tumblestrum * [AFR] Implemented Lost Mine of Phandelver * added choice dialog for dungeons * [AFR] Implemented Dungeon of the Mad Mage * small text fix * added initial dungeon test * [AFR] Implemented Cloister Gargoyle * [AFR] Implemented Dungeon Crawler * small text change for dungeon rooms * added more tests * some simplification to dungeon props * updated testing helper functions * added currently failing test for venturing on separate steps and turns * added tests for dungeon completion * fixed missing trigger visual and dungeons not persisting through turns * some text updates * added rollback test * added a test for multiple dungeons at once * added one more condition test
266 lines
8.9 KiB
Java
266 lines
8.9 KiB
Java
package mage.game.command.dungeons;
|
|
|
|
import mage.abilities.Ability;
|
|
import mage.abilities.dynamicvalue.common.CardTypeAssignment;
|
|
import mage.abilities.effects.OneShotEffect;
|
|
import mage.abilities.effects.common.CreateTokenEffect;
|
|
import mage.abilities.effects.common.LoseLifeAllPlayersEffect;
|
|
import mage.cards.Card;
|
|
import mage.cards.Cards;
|
|
import mage.cards.CardsImpl;
|
|
import mage.constants.CardType;
|
|
import mage.constants.Outcome;
|
|
import mage.filter.StaticFilters;
|
|
import mage.filter.common.FilterControlledPermanent;
|
|
import mage.filter.predicate.Predicates;
|
|
import mage.game.Game;
|
|
import mage.game.command.Dungeon;
|
|
import mage.game.command.DungeonRoom;
|
|
import mage.game.permanent.Permanent;
|
|
import mage.game.permanent.token.TheAtropalToken;
|
|
import mage.players.Player;
|
|
import mage.target.TargetPermanent;
|
|
import mage.target.common.TargetControlledPermanent;
|
|
import mage.target.common.TargetDiscard;
|
|
|
|
import java.util.*;
|
|
|
|
/**
|
|
* @author TheElk801
|
|
*/
|
|
public final class TombOfAnnihilation extends Dungeon {
|
|
|
|
static final FilterControlledPermanent filter
|
|
= new FilterControlledPermanent("an artifact, a creature, or a land");
|
|
|
|
static {
|
|
filter.add(Predicates.or(
|
|
CardType.ARTIFACT.getPredicate(),
|
|
CardType.CREATURE.getPredicate(),
|
|
CardType.LAND.getPredicate()
|
|
));
|
|
}
|
|
|
|
public TombOfAnnihilation() {
|
|
super("Tomb of Annihilation", "AFR");
|
|
// (1) Trapped Entry — Each player loses 1 life. (→ 2a or 2b)
|
|
DungeonRoom trappedEntry = new DungeonRoom("Trapped Entry", new LoseLifeAllPlayersEffect(1));
|
|
|
|
// (2a) Veils of Fear — Each player loses 2 life unless they discard a card. (→ 3)
|
|
DungeonRoom veilsOfFear = new DungeonRoom("Veils of Fear", new VeilsOfFearEffect());
|
|
|
|
// (2b) Oubliette — Discard a card and sacrifice an artifact, a creature, and a land. (→ 4)
|
|
DungeonRoom oubliette = new DungeonRoom("Oubliette", new OublietteEffect());
|
|
|
|
// (3) Sandfall Cell — Each player loses 2 life unless they sacrifice an artifact, a creature, or a land. (→ 4)
|
|
DungeonRoom sandfallCell = new DungeonRoom("Sandfall Cell", new SandfallCellEffect());
|
|
|
|
// (4) Cradle of the Death God — Create The Atropal, a legendary 4/4 black God Horror creature token with deathtouch.
|
|
DungeonRoom cradleOfTheDeathGod = new DungeonRoom("Cradle of the Death God", new CreateTokenEffect(new TheAtropalToken()));
|
|
|
|
trappedEntry.addNextRoom(veilsOfFear);
|
|
trappedEntry.addNextRoom(oubliette);
|
|
veilsOfFear.addNextRoom(sandfallCell);
|
|
oubliette.addNextRoom(sandfallCell);
|
|
sandfallCell.addNextRoom(cradleOfTheDeathGod);
|
|
|
|
this.addRoom(trappedEntry);
|
|
this.addRoom(veilsOfFear);
|
|
this.addRoom(oubliette);
|
|
this.addRoom(sandfallCell);
|
|
this.addRoom(cradleOfTheDeathGod);
|
|
}
|
|
|
|
private TombOfAnnihilation(final TombOfAnnihilation dungeon) {
|
|
super(dungeon);
|
|
}
|
|
|
|
public TombOfAnnihilation copy() {
|
|
return new TombOfAnnihilation(this);
|
|
}
|
|
}
|
|
|
|
class VeilsOfFearEffect extends OneShotEffect {
|
|
|
|
VeilsOfFearEffect() {
|
|
super(Outcome.Neutral);
|
|
staticText = "each player loses 2 life unless they discard a card";
|
|
}
|
|
|
|
private VeilsOfFearEffect(final VeilsOfFearEffect effect) {
|
|
super(effect);
|
|
}
|
|
|
|
@Override
|
|
public VeilsOfFearEffect copy() {
|
|
return new VeilsOfFearEffect(this);
|
|
}
|
|
|
|
@Override
|
|
public boolean apply(Game game, Ability source) {
|
|
Map<UUID, Card> map = new HashMap<>();
|
|
for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) {
|
|
Player player = game.getPlayer(playerId);
|
|
if (player == null) {
|
|
continue;
|
|
}
|
|
TargetDiscard target = new TargetDiscard(0, 1, StaticFilters.FILTER_CARD, playerId);
|
|
player.choose(Outcome.PreventDamage, target, source.getSourceId(), game);
|
|
map.put(playerId, game.getCard(target.getFirstTarget()));
|
|
}
|
|
for (Map.Entry<UUID, Card> entry : map.entrySet()) {
|
|
Player player = game.getPlayer(entry.getKey());
|
|
if (player == null) {
|
|
continue;
|
|
}
|
|
if (entry.getValue() != null) {
|
|
player.discard(entry.getValue(), false, source, game);
|
|
} else {
|
|
player.loseLife(2, game, source, false);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
class OublietteEffect extends OneShotEffect {
|
|
|
|
OublietteEffect() {
|
|
super(Outcome.Sacrifice);
|
|
staticText = "discard a card and sacrifice an artifact, a creature, and a land";
|
|
}
|
|
|
|
private OublietteEffect(final OublietteEffect effect) {
|
|
super(effect);
|
|
}
|
|
|
|
@Override
|
|
public OublietteEffect copy() {
|
|
return new OublietteEffect(this);
|
|
}
|
|
|
|
@Override
|
|
public boolean apply(Game game, Ability source) {
|
|
Player player = game.getPlayer(source.getControllerId());
|
|
if (player == null) {
|
|
return false;
|
|
}
|
|
player.discard(1, false, false, source, game);
|
|
int saccable = OublietteTarget.checkTargetCount(source, game);
|
|
if (saccable < 1) {
|
|
return true;
|
|
}
|
|
OublietteTarget target = new OublietteTarget(Math.min(saccable, 3));
|
|
player.choose(Outcome.Sacrifice, target, source.getSourceId(), game);
|
|
for (UUID targetId : target.getTargets()) {
|
|
Permanent permanent = game.getPermanent(targetId);
|
|
if (permanent != null) {
|
|
permanent.sacrifice(source, game);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
class OublietteTarget extends TargetControlledPermanent {
|
|
|
|
private static final CardTypeAssignment cardTypeAssigner = new CardTypeAssignment(
|
|
CardType.ARTIFACT,
|
|
CardType.CREATURE,
|
|
CardType.LAND
|
|
);
|
|
private static final FilterControlledPermanent filter = TombOfAnnihilation.filter.copy();
|
|
|
|
static {
|
|
filter.setMessage("an artifact, a creature, and a land");
|
|
}
|
|
|
|
OublietteTarget(int numTargets) {
|
|
super(numTargets, numTargets, filter, true);
|
|
}
|
|
|
|
private OublietteTarget(final OublietteTarget target) {
|
|
super(target);
|
|
}
|
|
|
|
@Override
|
|
public OublietteTarget copy() {
|
|
return new OublietteTarget(this);
|
|
}
|
|
|
|
@Override
|
|
public boolean canTarget(UUID playerId, UUID id, Ability ability, Game game) {
|
|
if (!super.canTarget(playerId, id, ability, game)) {
|
|
return false;
|
|
}
|
|
Permanent permanent = game.getPermanent(id);
|
|
if (permanent == null) {
|
|
return false;
|
|
}
|
|
if (this.getTargets().isEmpty()) {
|
|
return true;
|
|
}
|
|
Cards cards = new CardsImpl(this.getTargets());
|
|
cards.add(permanent);
|
|
return cardTypeAssigner.getRoleCount(cards, game) >= cards.size();
|
|
}
|
|
|
|
|
|
@Override
|
|
public Set<UUID> possibleTargets(UUID sourceId, UUID sourceControllerId, Game game) {
|
|
Set<UUID> possibleTargets = super.possibleTargets(sourceId, sourceControllerId, game);
|
|
possibleTargets.removeIf(uuid -> !this.canTarget(sourceControllerId, uuid, null, game));
|
|
return possibleTargets;
|
|
}
|
|
|
|
static int checkTargetCount(Ability source, Game game) {
|
|
List<Permanent> permanents = game
|
|
.getBattlefield()
|
|
.getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game);
|
|
return cardTypeAssigner.getRoleCount(new CardsImpl(permanents), game);
|
|
}
|
|
}
|
|
|
|
class SandfallCellEffect extends OneShotEffect {
|
|
|
|
SandfallCellEffect() {
|
|
super(Outcome.Neutral);
|
|
staticText = "each player loses 2 life unless they sacrifice an artifact, a creature, or a land";
|
|
}
|
|
|
|
private SandfallCellEffect(final SandfallCellEffect effect) {
|
|
super(effect);
|
|
}
|
|
|
|
@Override
|
|
public SandfallCellEffect copy() {
|
|
return new SandfallCellEffect(this);
|
|
}
|
|
|
|
@Override
|
|
public boolean apply(Game game, Ability source) {
|
|
Map<UUID, Permanent> map = new HashMap<>();
|
|
for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) {
|
|
Player player = game.getPlayer(playerId);
|
|
if (player == null) {
|
|
continue;
|
|
}
|
|
TargetPermanent target = new TargetPermanent(0, 1, TombOfAnnihilation.filter, true);
|
|
player.choose(Outcome.PreventDamage, target, source.getSourceId(), game);
|
|
map.put(playerId, game.getPermanent(target.getFirstTarget()));
|
|
}
|
|
for (Map.Entry<UUID, Permanent> entry : map.entrySet()) {
|
|
Player player = game.getPlayer(entry.getKey());
|
|
if (player == null) {
|
|
continue;
|
|
}
|
|
if (entry.getValue() != null) {
|
|
entry.getValue().sacrifice(source, game);
|
|
} else {
|
|
player.loseLife(2, game, source, false);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|