mirror of
https://github.com/magefree/mage.git
synced 2026-01-10 21:02:08 -08:00
[CLB] Implemented White Plume Adventurer
This commit is contained in:
parent
4777466b50
commit
86dad5e54f
13 changed files with 524 additions and 9 deletions
|
|
@ -0,0 +1,32 @@
|
|||
package mage.abilities.effects.common;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.constants.Outcome;
|
||||
import mage.game.Game;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public class TakeTheInitiativeEffect extends OneShotEffect {
|
||||
|
||||
public TakeTheInitiativeEffect() {
|
||||
super(Outcome.Benefit);
|
||||
staticText = "you take the initiative";
|
||||
}
|
||||
|
||||
private TakeTheInitiativeEffect(final TakeTheInitiativeEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TakeTheInitiativeEffect copy() {
|
||||
return new TakeTheInitiativeEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
game.takeInitiative(source, source.getControllerId());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -26,7 +26,7 @@ public class VentureIntoTheDungeonEffect extends OneShotEffect {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
game.ventureIntoDungeon(source.getControllerId());
|
||||
game.ventureIntoDungeon(source.getControllerId(), false);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
|
||||
package mage.designations;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public enum DesignationType {
|
||||
THE_MONARCH("The Monarch"),
|
||||
CITYS_BLESSING("City's Blessing");
|
||||
CITYS_BLESSING("City's Blessing"),
|
||||
THE_INITIATIVE("The Initiative");
|
||||
|
||||
private final String text;
|
||||
|
||||
|
|
|
|||
179
Mage/src/main/java/mage/designations/Initiative.java
Normal file
179
Mage/src/main/java/mage/designations/Initiative.java
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
package mage.designations;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Controllable;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.DamagedPlayerBatchEvent;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public class Initiative extends Designation {
|
||||
|
||||
public Initiative() {
|
||||
super(DesignationType.THE_INITIATIVE, "CLB");
|
||||
|
||||
// Whenever one or more creatures a player controls deals combat damage to you, that player takes the initiative.
|
||||
this.addAbility(new InitiativeDamageTriggeredAbility());
|
||||
|
||||
// Whenever you take the initiative and at the beginning of your upkeep, venture into Undercity.
|
||||
this.addAbility(new InitiativeVentureTriggeredAbility());
|
||||
}
|
||||
|
||||
private Initiative(final Initiative card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Initiative copy() {
|
||||
return new Initiative(this);
|
||||
}
|
||||
}
|
||||
|
||||
class InitiativeDamageTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
||||
InitiativeDamageTriggeredAbility() {
|
||||
super(Zone.ALL, new InitiativeTakeEffect());
|
||||
}
|
||||
|
||||
private InitiativeDamageTriggeredAbility(final InitiativeDamageTriggeredAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InitiativeDamageTriggeredAbility copy() {
|
||||
return new InitiativeDamageTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.DAMAGED_PLAYER_BATCH;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
if (!event.getTargetId().equals(game.getInitiativeId())) {
|
||||
return false;
|
||||
}
|
||||
DamagedPlayerBatchEvent dEvent = (DamagedPlayerBatchEvent) event;
|
||||
UUID playerId = dEvent
|
||||
.getEvents()
|
||||
.stream()
|
||||
.map(GameEvent::getSourceId)
|
||||
.map(game::getPermanent)
|
||||
.filter(Objects::nonNull)
|
||||
.map(Controllable::getControllerId)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
if (playerId == null) {
|
||||
return false;
|
||||
}
|
||||
this.getEffects().setTargetPointer(new FixedTarget(playerId));
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "Whenever one or more creatures a player controls deals combat damage to you, that player takes the initiative.";
|
||||
}
|
||||
}
|
||||
|
||||
class InitiativeTakeEffect extends OneShotEffect {
|
||||
|
||||
InitiativeTakeEffect() {
|
||||
super(Outcome.Benefit);
|
||||
}
|
||||
|
||||
private InitiativeTakeEffect(final InitiativeTakeEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InitiativeTakeEffect copy() {
|
||||
return new InitiativeTakeEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
game.takeInitiative(source, getTargetPointer().getFirst(game, source));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class InitiativeVentureTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
||||
InitiativeVentureTriggeredAbility() {
|
||||
super(Zone.ALL, new InitiativeUndercityEffect());
|
||||
}
|
||||
|
||||
private InitiativeVentureTriggeredAbility(final InitiativeVentureTriggeredAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InitiativeVentureTriggeredAbility copy() {
|
||||
return new InitiativeVentureTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.UPKEEP_STEP_PRE
|
||||
|| event.getType() == GameEvent.EventType.TOOK_INITIATIVE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
UUID playerId;
|
||||
switch (event.getType()) {
|
||||
case UPKEEP_STEP_PRE:
|
||||
if (!game.isActivePlayer(game.getInitiativeId())) {
|
||||
return false;
|
||||
}
|
||||
playerId = game.getActivePlayerId();
|
||||
break;
|
||||
case TOOK_INITIATIVE:
|
||||
playerId = event.getPlayerId();
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
this.getEffects().setTargetPointer(new FixedTarget(playerId));
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "Whenever you take the initiative and at the beginning of your upkeep, venture into Undercity.";
|
||||
}
|
||||
}
|
||||
|
||||
class InitiativeUndercityEffect extends OneShotEffect {
|
||||
|
||||
InitiativeUndercityEffect() {
|
||||
super(Outcome.Benefit);
|
||||
}
|
||||
|
||||
private InitiativeUndercityEffect(final InitiativeUndercityEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InitiativeUndercityEffect copy() {
|
||||
return new InitiativeUndercityEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
game.ventureIntoDungeon(getTargetPointer().getFirst(game, source), true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -386,7 +386,7 @@ public interface Game extends MageItem, Serializable, Copyable<Game> {
|
|||
|
||||
Dungeon addDungeon(Dungeon dungeon, UUID playerId);
|
||||
|
||||
void ventureIntoDungeon(UUID playerId);
|
||||
void ventureIntoDungeon(UUID playerId, boolean undercity);
|
||||
|
||||
/**
|
||||
* Tells whether the current game has day or night, defaults to false
|
||||
|
|
@ -519,6 +519,10 @@ public interface Game extends MageItem, Serializable, Copyable<Game> {
|
|||
|
||||
void setMonarchId(Ability source, UUID monarchId);
|
||||
|
||||
UUID getInitiativeId();
|
||||
|
||||
void takeInitiative(Ability source, UUID initiativeId);
|
||||
|
||||
int damagePlayerOrPlaneswalker(UUID playerOrWalker, int damage, UUID attackerId, Ability source, Game game, boolean combatDamage, boolean preventable);
|
||||
|
||||
int damagePlayerOrPlaneswalker(UUID playerOrWalker, int damage, UUID attackerId, Ability source, Game game, boolean combatDamage, boolean preventable, List<UUID> appliedEffects);
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import mage.constants.*;
|
|||
import mage.counters.CounterType;
|
||||
import mage.counters.Counters;
|
||||
import mage.designations.Designation;
|
||||
import mage.designations.Initiative;
|
||||
import mage.designations.Monarch;
|
||||
import mage.filter.Filter;
|
||||
import mage.filter.FilterCard;
|
||||
|
|
@ -36,6 +37,7 @@ import mage.filter.predicate.mageobject.NamePredicate;
|
|||
import mage.filter.predicate.permanent.ControllerIdPredicate;
|
||||
import mage.game.combat.Combat;
|
||||
import mage.game.command.*;
|
||||
import mage.game.command.dungeons.UndercityDungeon;
|
||||
import mage.game.events.*;
|
||||
import mage.game.events.TableEvent.EventType;
|
||||
import mage.game.mulligan.Mulligan;
|
||||
|
|
@ -536,21 +538,24 @@ public abstract class GameImpl implements Game {
|
|||
));
|
||||
}
|
||||
|
||||
private Dungeon getOrCreateDungeon(UUID playerId) {
|
||||
private Dungeon getOrCreateDungeon(UUID playerId, boolean undercity) {
|
||||
Dungeon dungeon = this.getPlayerDungeon(playerId);
|
||||
if (dungeon != null && dungeon.hasNextRoom()) {
|
||||
return dungeon;
|
||||
}
|
||||
removeDungeon(dungeon);
|
||||
return this.addDungeon(Dungeon.selectDungeon(playerId, this), playerId);
|
||||
return this.addDungeon(undercity ? new UndercityDungeon() : Dungeon.selectDungeon(playerId, this), playerId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ventureIntoDungeon(UUID playerId) {
|
||||
public void ventureIntoDungeon(UUID playerId, boolean undercity) {
|
||||
if (playerId == null) {
|
||||
return;
|
||||
}
|
||||
if (replaceEvent(GameEvent.getEvent(GameEvent.EventType.VENTURE, playerId, null, playerId))) {
|
||||
return;
|
||||
}
|
||||
this.getOrCreateDungeon(playerId).moveToNextRoom(playerId, this);
|
||||
this.getOrCreateDungeon(playerId, undercity).moveToNextRoom(playerId, this);
|
||||
fireEvent(GameEvent.getEvent(GameEvent.EventType.VENTURED, playerId, null, playerId));
|
||||
}
|
||||
|
||||
|
|
@ -3691,6 +3696,22 @@ public abstract class GameImpl implements Game {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getInitiativeId() {
|
||||
return getState().getInitiativeId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void takeInitiative(Ability source, UUID initiativeId) {
|
||||
if (getInitiativeId() == null) {
|
||||
getState().addDesignation(new Initiative(), this, initiativeId);
|
||||
} else if (!getInitiativeId().equals(initiativeId)) {
|
||||
getState().setInitiativeId(initiativeId);
|
||||
}
|
||||
informPlayers(getPlayer(initiativeId).getLogName() + " takes the initiative");
|
||||
fireEvent(new GameEvent(GameEvent.EventType.TOOK_INITIATIVE, initiativeId, source, initiativeId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int damagePlayerOrPlaneswalker(UUID playerOrWalker, int damage, UUID attackerId, Ability source, Game game, boolean combatDamage, boolean preventable) {
|
||||
return damagePlayerOrPlaneswalker(playerOrWalker, damage, attackerId, source, game, combatDamage, preventable, null);
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
private UUID priorityPlayerId; // player that has currently priority
|
||||
private UUID playerByOrderId; // player that has currently priority
|
||||
private UUID monarchId; // player that is the monarch
|
||||
private UUID initiativeId; // player that has the initiative
|
||||
private SpellStack stack;
|
||||
private Command command;
|
||||
private boolean isPlaneChase;
|
||||
|
|
@ -144,6 +145,7 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
this.priorityPlayerId = state.priorityPlayerId;
|
||||
this.playerByOrderId = state.playerByOrderId;
|
||||
this.monarchId = state.monarchId;
|
||||
this.initiativeId = state.initiativeId;
|
||||
this.turn = state.turn.copy();
|
||||
|
||||
this.stack = state.stack.copy();
|
||||
|
|
@ -249,6 +251,7 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
this.playerByOrderId = state.playerByOrderId;
|
||||
this.priorityPlayerId = state.priorityPlayerId;
|
||||
this.monarchId = state.monarchId;
|
||||
this.initiativeId = state.initiativeId;
|
||||
this.stack = state.stack;
|
||||
this.command = state.command;
|
||||
this.isPlaneChase = state.isPlaneChase;
|
||||
|
|
@ -482,6 +485,14 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
this.monarchId = monarchId;
|
||||
}
|
||||
|
||||
public UUID getInitiativeId() {
|
||||
return initiativeId;
|
||||
}
|
||||
|
||||
public void setInitiativeId(UUID initiativeId) {
|
||||
this.initiativeId = initiativeId;
|
||||
}
|
||||
|
||||
public UUID getChoosingPlayerId() {
|
||||
return choosingPlayerId;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,152 @@
|
|||
package mage.game.command.dungeons;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.CreateTokenEffect;
|
||||
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
|
||||
import mage.abilities.effects.common.LoseLifeTargetEffect;
|
||||
import mage.abilities.effects.common.combat.GoadTargetEffect;
|
||||
import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
|
||||
import mage.abilities.effects.common.counter.AddCountersTargetEffect;
|
||||
import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect;
|
||||
import mage.abilities.effects.keyword.ScryEffect;
|
||||
import mage.abilities.keyword.HexproofAbility;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.Cards;
|
||||
import mage.cards.CardsImpl;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.counters.CounterType;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.game.Game;
|
||||
import mage.game.command.Dungeon;
|
||||
import mage.game.command.DungeonRoom;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.game.permanent.token.SkeletonToken;
|
||||
import mage.game.permanent.token.TreasureToken;
|
||||
import mage.players.Player;
|
||||
import mage.target.TargetPlayer;
|
||||
import mage.target.common.TargetCardInLibrary;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
import mage.util.RandomUtil;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public class UndercityDungeon extends Dungeon {
|
||||
|
||||
public UndercityDungeon() {
|
||||
super("Undercity", "CLB");
|
||||
|
||||
DungeonRoom secretEntrance = new DungeonRoom(
|
||||
"Secret Entrance",
|
||||
new SearchLibraryPutInHandEffect(
|
||||
new TargetCardInLibrary(StaticFilters.FILTER_CARD_BASIC_LAND)
|
||||
)
|
||||
);
|
||||
|
||||
DungeonRoom forge = new DungeonRoom(
|
||||
"Forge", new AddCountersTargetEffect(CounterType.P1P1.createInstance())
|
||||
);
|
||||
forge.addTarget(new TargetCreaturePermanent());
|
||||
|
||||
DungeonRoom lostWell = new DungeonRoom(
|
||||
"Lost Well", new ScryEffect(2, false)
|
||||
);
|
||||
|
||||
DungeonRoom trap = new DungeonRoom("Trap!", new LoseLifeTargetEffect(5));
|
||||
trap.addTarget(new TargetPlayer());
|
||||
|
||||
DungeonRoom arena = new DungeonRoom("Arena", new GoadTargetEffect());
|
||||
arena.addTarget(new TargetCreaturePermanent());
|
||||
|
||||
DungeonRoom stash = new DungeonRoom("Stash", new CreateTokenEffect(new TreasureToken()));
|
||||
|
||||
DungeonRoom archives = new DungeonRoom("Archives", new DrawCardSourceControllerEffect(1));
|
||||
|
||||
DungeonRoom catacombs = new DungeonRoom("Catacombs", new CreateTokenEffect(new SkeletonToken()));
|
||||
|
||||
DungeonRoom throneOfTheDeadThree = new DungeonRoom("Throne of the Dead Three", new ThroneOfTheDeadThreeEffect());
|
||||
|
||||
secretEntrance.addNextRoom(forge);
|
||||
secretEntrance.addNextRoom(lostWell);
|
||||
forge.addNextRoom(trap);
|
||||
forge.addNextRoom(arena);
|
||||
lostWell.addNextRoom(arena);
|
||||
lostWell.addNextRoom(stash);
|
||||
trap.addNextRoom(archives);
|
||||
arena.addNextRoom(archives);
|
||||
arena.addNextRoom(catacombs);
|
||||
archives.addNextRoom(throneOfTheDeadThree);
|
||||
catacombs.addNextRoom(throneOfTheDeadThree);
|
||||
|
||||
this.addRoom(secretEntrance);
|
||||
this.addRoom(forge);
|
||||
this.addRoom(lostWell);
|
||||
this.addRoom(trap);
|
||||
this.addRoom(arena);
|
||||
this.addRoom(stash);
|
||||
this.addRoom(archives);
|
||||
this.addRoom(catacombs);
|
||||
this.addRoom(throneOfTheDeadThree);
|
||||
}
|
||||
|
||||
private UndercityDungeon(final UndercityDungeon dungeon) {
|
||||
super(dungeon);
|
||||
}
|
||||
|
||||
public UndercityDungeon copy() {
|
||||
return new UndercityDungeon(this);
|
||||
}
|
||||
}
|
||||
|
||||
class ThroneOfTheDeadThreeEffect extends OneShotEffect {
|
||||
|
||||
ThroneOfTheDeadThreeEffect() {
|
||||
super(Outcome.Benefit);
|
||||
staticText = "Reveal the top ten cards of your library. Put a creature card from among them onto the " +
|
||||
"battlefield with three +1/+1 counters on it. It gains hexproof until your next turn. Then shuffle.";
|
||||
}
|
||||
|
||||
private ThroneOfTheDeadThreeEffect(final ThroneOfTheDeadThreeEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ThroneOfTheDeadThreeEffect copy() {
|
||||
return new ThroneOfTheDeadThreeEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
if (player == null) {
|
||||
return false;
|
||||
}
|
||||
Cards cards = new CardsImpl(player.getLibrary().getTopCards(game, 10));
|
||||
player.revealCards(source, cards, game);
|
||||
Card card;
|
||||
switch (cards.count(StaticFilters.FILTER_CARD_CREATURE, game)) {
|
||||
case 0:
|
||||
card = null;
|
||||
break;
|
||||
case 1:
|
||||
card = RandomUtil.randomFromCollection(cards.getCards(StaticFilters.FILTER_CARD_CREATURE, game));
|
||||
break;
|
||||
default:
|
||||
TargetCardInLibrary target = new TargetCardInLibrary(StaticFilters.FILTER_CARD_CREATURE);
|
||||
player.choose(outcome, cards, target, game);
|
||||
card = cards.get(target.getFirstTarget(), game);
|
||||
}
|
||||
if (card != null) {
|
||||
player.moveCards(card, Zone.BATTLEFIELD, source, game);
|
||||
Permanent permanent = game.getPermanent(card.getId());
|
||||
permanent.addCounters(CounterType.P1P1.createInstance(), source, game);
|
||||
game.addEffect(new GainAbilityTargetEffect(HexproofAbility.getInstance())
|
||||
.setTargetPointer(new FixedTarget(permanent, game)), source);
|
||||
}
|
||||
player.shuffleLibrary(source, game);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -362,6 +362,7 @@ public class GameEvent implements Serializable {
|
|||
*/
|
||||
BECOME_MONARCH,
|
||||
BECOMES_MONARCH,
|
||||
TOOK_INITIATIVE,
|
||||
BECOMES_DAY_NIGHT,
|
||||
MEDITATED,
|
||||
PHASE_OUT, PHASED_OUT,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue