forked from External/mage
Merge pull request 'master' (#19) from External/mage:master into master
All checks were successful
/ example-docker-compose (push) Successful in 15m4s
All checks were successful
/ example-docker-compose (push) Successful in 15m4s
Reviewed-on: #19
This commit is contained in:
commit
d1ca46fd85
111 changed files with 3335 additions and 976 deletions
|
|
@ -7,7 +7,10 @@ import mage.abilities.dynamicvalue.common.ControllerSpeedCount;
|
|||
import mage.abilities.effects.ContinuousEffectImpl;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.cards.Card;
|
||||
import mage.constants.*;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Layer;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubLayer;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.util.CardUtil;
|
||||
|
|
@ -22,7 +25,7 @@ public class MaxSpeedAbility extends StaticAbility {
|
|||
}
|
||||
|
||||
public MaxSpeedAbility(Ability ability) {
|
||||
super(Zone.ALL, new MaxSpeedAbilityEffect(ability));
|
||||
super(ability.getZone(), new MaxSpeedAbilityEffect(ability));
|
||||
}
|
||||
|
||||
private MaxSpeedAbility(final MaxSpeedAbility ability) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,76 @@
|
|||
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.SpellsCastWatcher;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
public enum InstantAndSorceryCastThisTurn implements DynamicValue
|
||||
{
|
||||
YOU("you've cast"),
|
||||
ALL("all players have cast"),
|
||||
OPPONENTS("your opponents have cast");
|
||||
|
||||
private final String message;
|
||||
private final ValueHint hint;
|
||||
|
||||
InstantAndSorceryCastThisTurn(String message) {
|
||||
this.message = "Instant and sorcery spells " + message + " this turn";
|
||||
this.hint = new ValueHint(this.message, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int calculate(Game game, Ability sourceAbility, Effect effect) {
|
||||
return getSpellsCastThisTurn(game, sourceAbility);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InstantAndSorceryCastThisTurn copy() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return this.message;
|
||||
}
|
||||
|
||||
public Hint getHint() {
|
||||
return this.hint;
|
||||
}
|
||||
|
||||
private int getSpellsCastThisTurn(Game game, Ability ability) {
|
||||
Collection<UUID> playerIds;
|
||||
switch (this) {
|
||||
case YOU:
|
||||
playerIds = Collections.singletonList(ability.getControllerId());
|
||||
break;
|
||||
case ALL:
|
||||
playerIds = game.getState().getPlayersInRange(ability.getControllerId(), game);
|
||||
break;
|
||||
case OPPONENTS:
|
||||
playerIds = game.getOpponents(ability.getControllerId());
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
SpellsCastWatcher watcher = game.getState().getWatcher(SpellsCastWatcher.class);
|
||||
if (watcher == null) {
|
||||
return 0;
|
||||
}
|
||||
return (int) playerIds.stream()
|
||||
.map(watcher::getSpellsCastThisTurn)
|
||||
.flatMap(Collection::stream)
|
||||
.filter(Objects::nonNull)
|
||||
.filter(spell -> spell.isInstantOrSorcery(game))
|
||||
.count();
|
||||
}
|
||||
}
|
||||
|
|
@ -22,7 +22,6 @@ import mage.game.stack.Spell;
|
|||
import mage.players.ManaPoolItem;
|
||||
import mage.players.Player;
|
||||
import mage.target.common.TargetCardInHand;
|
||||
import mage.util.trace.TraceInfo;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
|
@ -1405,88 +1404,6 @@ public class ContinuousEffects implements Serializable {
|
|||
return controllerFound;
|
||||
}
|
||||
|
||||
/**
|
||||
* Debug only: prints out a status of the currently existing continuous effects
|
||||
*
|
||||
* @param game
|
||||
*/
|
||||
public void traceContinuousEffects(Game game) {
|
||||
game.getContinuousEffects().getLayeredEffects(game);
|
||||
logger.info("-------------------------------------------------------------------------------------------------");
|
||||
int numberEffects = 0;
|
||||
for (ContinuousEffectsList list : allEffectsLists) {
|
||||
numberEffects += list.size();
|
||||
}
|
||||
logger.info("Turn: " + game.getTurnNum() + " - currently existing continuous effects: " + numberEffects);
|
||||
logger.info("layeredEffects ...................: " + layeredEffects.size());
|
||||
logger.info("continuousRuleModifyingEffects ...: " + continuousRuleModifyingEffects.size());
|
||||
logger.info("replacementEffects ...............: " + replacementEffects.size());
|
||||
logger.info("preventionEffects ................: " + preventionEffects.size());
|
||||
logger.info("requirementEffects ...............: " + requirementEffects.size());
|
||||
logger.info("restrictionEffects ...............: " + restrictionEffects.size());
|
||||
logger.info("restrictionUntapNotMoreThanEffects: " + restrictionUntapNotMoreThanEffects.size());
|
||||
logger.info("costModificationEffects ..........: " + costModificationEffects.size());
|
||||
logger.info("spliceCardEffects ................: " + spliceCardEffects.size());
|
||||
logger.info("asThoughEffects:");
|
||||
for (Map.Entry<AsThoughEffectType, ContinuousEffectsList<AsThoughEffect>> entry : asThoughEffectsMap.entrySet()) {
|
||||
logger.info("... " + entry.getKey().toString() + ": " + entry.getValue().size());
|
||||
}
|
||||
logger.info("applyStatus ....................: " + (applyStatus != null ? "exists" : "null"));
|
||||
logger.info("auraReplacementEffect ............: " + (continuousRuleModifyingEffects != null ? "exists" : "null"));
|
||||
Map<String, TraceInfo> orderedEffects = new TreeMap<>();
|
||||
traceAddContinuousEffects(orderedEffects, layeredEffects, game, "layeredEffects................");
|
||||
traceAddContinuousEffects(orderedEffects, continuousRuleModifyingEffects, game, "continuousRuleModifyingEffects");
|
||||
traceAddContinuousEffects(orderedEffects, replacementEffects, game, "replacementEffects............");
|
||||
traceAddContinuousEffects(orderedEffects, preventionEffects, game, "preventionEffects.............");
|
||||
traceAddContinuousEffects(orderedEffects, requirementEffects, game, "requirementEffects............");
|
||||
traceAddContinuousEffects(orderedEffects, restrictionEffects, game, "restrictionEffects............");
|
||||
traceAddContinuousEffects(orderedEffects, restrictionUntapNotMoreThanEffects, game, "restrictionUntapNotMore...");
|
||||
traceAddContinuousEffects(orderedEffects, costModificationEffects, game, "costModificationEffects.......");
|
||||
traceAddContinuousEffects(orderedEffects, spliceCardEffects, game, "spliceCardEffects.............");
|
||||
for (Map.Entry<AsThoughEffectType, ContinuousEffectsList<AsThoughEffect>> entry : asThoughEffectsMap.entrySet()) {
|
||||
traceAddContinuousEffects(orderedEffects, entry.getValue(), game, entry.getKey().toString());
|
||||
}
|
||||
String playerName = "";
|
||||
for (Map.Entry<String, TraceInfo> entry : orderedEffects.entrySet()) {
|
||||
if (!entry.getValue().getPlayerName().equals(playerName)) {
|
||||
playerName = entry.getValue().getPlayerName();
|
||||
logger.info("--- Player: " + playerName + " --------------------------------");
|
||||
}
|
||||
logger.info(entry.getValue().getInfo()
|
||||
+ " " + entry.getValue().getSourceName()
|
||||
+ " " + entry.getValue().getDuration().name()
|
||||
+ " " + entry.getValue().getRule()
|
||||
+ " (Order: " + entry.getValue().getOrder() + ")"
|
||||
);
|
||||
}
|
||||
logger.info("---- End trace Continuous effects --------------------------------------------------------------------------");
|
||||
}
|
||||
|
||||
public static void traceAddContinuousEffects(Map orderedEffects, ContinuousEffectsList<?> cel, Game game, String listName) {
|
||||
for (ContinuousEffect effect : cel) {
|
||||
Set<Ability> abilities = cel.getAbility(effect.getId());
|
||||
for (Ability ability : abilities) {
|
||||
Player controller = game.getPlayer(ability.getControllerId());
|
||||
MageObject source = game.getObject(ability.getSourceId());
|
||||
TraceInfo traceInfo = new TraceInfo();
|
||||
traceInfo.setInfo(listName);
|
||||
traceInfo.setOrder(effect.getOrder());
|
||||
if (ability instanceof MageSingleton) {
|
||||
traceInfo.setPlayerName("Mage Singleton");
|
||||
traceInfo.setSourceName("Mage Singleton");
|
||||
} else {
|
||||
traceInfo.setPlayerName(controller == null ? "no controller" : controller.getName());
|
||||
traceInfo.setSourceName(source == null ? "no source" : source.getIdName());
|
||||
}
|
||||
traceInfo.setRule(ability.getRule());
|
||||
traceInfo.setAbilityId(ability.getId());
|
||||
traceInfo.setEffectId(effect.getId());
|
||||
traceInfo.setDuration(effect.getDuration());
|
||||
orderedEffects.put(traceInfo.getPlayerName() + traceInfo.getSourceName() + effect.getId() + ability.getId(), traceInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int getTotalEffectsCount() {
|
||||
return allEffectsLists.stream().mapToInt(ContinuousEffectsList::size).sum();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import mage.abilities.effects.Effect;
|
|||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.game.Game;
|
||||
import mage.target.targetpointer.TargetPointer;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
/**
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
|
|
@ -67,7 +68,7 @@ public class CreateDelayedTriggeredAbilityEffect extends OneShotEffect {
|
|||
return staticText;
|
||||
}
|
||||
if (ability.getRuleVisible()) {
|
||||
return rulePrefix + ability.getRule();
|
||||
return rulePrefix + CardUtil.getTextWithFirstCharLowerCase(ability.getRule());
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,14 +10,26 @@ import mage.target.targetpointer.FixedTarget;
|
|||
|
||||
public class CrewsVehicleSourceTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
||||
private final boolean mountsAlso;
|
||||
private final boolean yourMainPhaseOnly;
|
||||
|
||||
public CrewsVehicleSourceTriggeredAbility(Effect effect) {
|
||||
this(effect, false, false);
|
||||
}
|
||||
|
||||
public CrewsVehicleSourceTriggeredAbility(Effect effect, boolean mountsAlso, boolean yourMainPhaseOnly) {
|
||||
super(Zone.BATTLEFIELD, effect, false);
|
||||
this.addIcon(CardIconImpl.ABILITY_CREW);
|
||||
setTriggerPhrase("Whenever {this} crews a Vehicle, ");
|
||||
this.mountsAlso = mountsAlso;
|
||||
this.yourMainPhaseOnly = yourMainPhaseOnly;
|
||||
setTriggerPhrase("Whenever {this}" + (mountsAlso ? " saddles a Mount or" : "") +
|
||||
" crews a Vehicle" + (yourMainPhaseOnly ? " during your main phase" : "") + ", ");
|
||||
}
|
||||
|
||||
protected CrewsVehicleSourceTriggeredAbility(final CrewsVehicleSourceTriggeredAbility ability) {
|
||||
super(ability);
|
||||
this.mountsAlso = ability.mountsAlso;
|
||||
this.yourMainPhaseOnly = ability.yourMainPhaseOnly;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -27,11 +39,14 @@ public class CrewsVehicleSourceTriggeredAbility extends TriggeredAbilityImpl {
|
|||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.CREWED_VEHICLE;
|
||||
return event.getType() == GameEvent.EventType.CREWED_VEHICLE || (mountsAlso && event.getType() == GameEvent.EventType.SADDLED_MOUNT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
if (yourMainPhaseOnly && !(game.isMainPhase() && this.isControlledBy(game.getActivePlayerId()))) {
|
||||
return false;
|
||||
}
|
||||
if (event.getTargetId().equals(getSourceId())) {
|
||||
for (Effect effect : getEffects()) {
|
||||
// set the vehicle id as target
|
||||
|
|
|
|||
|
|
@ -45,22 +45,22 @@ public class GainAbilityControlledSpellsEffect extends ContinuousEffectImpl {
|
|||
}
|
||||
|
||||
for (Card card : game.getExile().getAllCardsByRange(game, source.getControllerId())) {
|
||||
if (filter.match(card, game)) {
|
||||
if (filter.match(card, player.getId(), source, game)) {
|
||||
game.getState().addOtherAbility(card, ability);
|
||||
}
|
||||
}
|
||||
for (Card card : player.getLibrary().getCards(game)) {
|
||||
if (filter.match(card, game)) {
|
||||
if (filter.match(card, player.getId(), source, game)) {
|
||||
game.getState().addOtherAbility(card, ability);
|
||||
}
|
||||
}
|
||||
for (Card card : player.getHand().getCards(game)) {
|
||||
if (filter.match(card, game)) {
|
||||
if (filter.match(card, player.getId(), source, game)) {
|
||||
game.getState().addOtherAbility(card, ability);
|
||||
}
|
||||
}
|
||||
for (Card card : player.getGraveyard().getCards(game)) {
|
||||
if (filter.match(card, game)) {
|
||||
if (filter.match(card, player.getId(), source, game)) {
|
||||
game.getState().addOtherAbility(card, ability);
|
||||
}
|
||||
}
|
||||
|
|
@ -68,7 +68,7 @@ public class GainAbilityControlledSpellsEffect extends ContinuousEffectImpl {
|
|||
// workaround to gain cost reduction abilities to commanders before cast (make it playable)
|
||||
game.getCommanderCardsFromCommandZone(player, CommanderCardType.ANY)
|
||||
.stream()
|
||||
.filter(card -> filter.match(card, game))
|
||||
.filter(card -> filter.match(card, player.getId(), source, game))
|
||||
.forEach(card -> game.getState().addOtherAbility(card, ability));
|
||||
|
||||
for (StackObject stackObject : game.getStack()) {
|
||||
|
|
@ -77,7 +77,7 @@ public class GainAbilityControlledSpellsEffect extends ContinuousEffectImpl {
|
|||
}
|
||||
// TODO: Distinguish "you cast" to exclude copies
|
||||
Card card = game.getCard(stackObject.getSourceId());
|
||||
if (card != null && filter.match((Spell) stackObject, game)) {
|
||||
if (card != null && filter.match((Spell) stackObject, player.getId(), source, game)) {
|
||||
game.getState().addOtherAbility(card, ability);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,27 +12,37 @@ import mage.filter.FilterCard;
|
|||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.target.common.TargetCardInLibrary;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
/**
|
||||
* @author BetaSteward_at_googlemail.com, edited by Cguy7777
|
||||
*/
|
||||
public class SearchLibraryPutOneOntoBattlefieldTappedRestInHandEffect extends SearchEffect {
|
||||
public class SearchLibraryPutOntoBattlefieldTappedRestInHandEffect extends SearchEffect {
|
||||
|
||||
private static final FilterCard filter = new FilterCard("card to put on the battlefield tapped");
|
||||
private final FilterCard filter;
|
||||
private final int numToBattlefield;
|
||||
|
||||
public SearchLibraryPutOneOntoBattlefieldTappedRestInHandEffect(TargetCardInLibrary target) {
|
||||
public SearchLibraryPutOntoBattlefieldTappedRestInHandEffect(TargetCardInLibrary target, int numToBattlefield) {
|
||||
super(target, Outcome.PutLandInPlay);
|
||||
staticText = "search your library for " + target.getDescription() +
|
||||
", reveal those cards, put one onto the battlefield tapped and the other into your hand, then shuffle";
|
||||
", reveal those cards, put " + CardUtil.numberToText(numToBattlefield) + " onto the battlefield tapped and the other into your hand, then shuffle";
|
||||
this.filter = new FilterCard((numToBattlefield > 1 ? "cards" : "card") + "to put on the battlefield tapped");
|
||||
this.numToBattlefield = numToBattlefield;
|
||||
}
|
||||
|
||||
protected SearchLibraryPutOneOntoBattlefieldTappedRestInHandEffect(final SearchLibraryPutOneOntoBattlefieldTappedRestInHandEffect effect) {
|
||||
public SearchLibraryPutOntoBattlefieldTappedRestInHandEffect(TargetCardInLibrary target) {
|
||||
this(target, 1);
|
||||
}
|
||||
|
||||
protected SearchLibraryPutOntoBattlefieldTappedRestInHandEffect(final SearchLibraryPutOntoBattlefieldTappedRestInHandEffect effect) {
|
||||
super(effect);
|
||||
this.filter = effect.filter.copy();
|
||||
this.numToBattlefield = effect.numToBattlefield;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SearchLibraryPutOneOntoBattlefieldTappedRestInHandEffect copy() {
|
||||
return new SearchLibraryPutOneOntoBattlefieldTappedRestInHandEffect(this);
|
||||
public SearchLibraryPutOntoBattlefieldTappedRestInHandEffect copy() {
|
||||
return new SearchLibraryPutOntoBattlefieldTappedRestInHandEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -49,14 +59,15 @@ public class SearchLibraryPutOneOntoBattlefieldTappedRestInHandEffect extends Se
|
|||
controller.revealCards(sourceObject.getIdName(), revealed, game);
|
||||
|
||||
if (target.getTargets().size() >= 2) {
|
||||
TargetCardInLibrary targetCardToBattlefield = new TargetCardInLibrary(filter);
|
||||
controller.choose(Outcome.PutLandInPlay, revealed, targetCardToBattlefield, source, game);
|
||||
int maxToBattlefield = Math.min(numToBattlefield, target.getTargets().size());
|
||||
TargetCardInLibrary targetCardsToBattlefield = new TargetCardInLibrary(maxToBattlefield, filter);
|
||||
controller.choose(Outcome.PutLandInPlay, revealed, targetCardsToBattlefield, source, game);
|
||||
|
||||
Card cardToBattlefield = revealed.get(targetCardToBattlefield.getFirstTarget(), game);
|
||||
Cards cardsToBattlefield = new CardsImpl(targetCardsToBattlefield.getTargets());
|
||||
Cards cardsToHand = new CardsImpl(revealed);
|
||||
if (cardToBattlefield != null) {
|
||||
controller.moveCards(cardToBattlefield, Zone.BATTLEFIELD, source, game, true, false, false, null);
|
||||
cardsToHand.remove(cardToBattlefield);
|
||||
if (!cardsToBattlefield.isEmpty()) {
|
||||
controller.moveCards(cardsToBattlefield.getCards(game), Zone.BATTLEFIELD, source, game, true, false, false, null);
|
||||
cardsToHand.removeAll(cardsToBattlefield);
|
||||
}
|
||||
|
||||
controller.moveCardsToHandWithInfo(cardsToHand, source, game, true);
|
||||
|
|
@ -551,7 +551,7 @@ public enum SubType {
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Subtype(" + subtype + ')';
|
||||
return "Subtype(" + subtype + ')'; // warning, do not change until refactor code like predicate.toString().equals
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3814,7 +3814,7 @@ public abstract class GameImpl implements Game {
|
|||
|
||||
@Override
|
||||
public boolean endTurn(Ability source) {
|
||||
getTurn().endTurn(this, getActivePlayerId(), source);
|
||||
getTurn().endTurn(this, source);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@ import mage.target.common.TargetControlledPermanent;
|
|||
import mage.target.common.TargetDefender;
|
||||
import mage.util.CardUtil;
|
||||
import mage.util.Copyable;
|
||||
import mage.util.trace.TraceUtil;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
|
@ -744,8 +743,6 @@ public class Combat implements Serializable, Copyable<Combat> {
|
|||
game.getCombat().logBlockerInfo(defender, game);
|
||||
}
|
||||
}
|
||||
// tool to catch the bug about flyers blocked by non flyers or intimidate blocked by creatures with other colors
|
||||
TraceUtil.traceCombatIfNeeded(game, game.getCombat());
|
||||
}
|
||||
|
||||
private void makeSureItsNotComputer(Player controller) {
|
||||
|
|
@ -761,7 +758,7 @@ public class Combat implements Serializable, Copyable<Combat> {
|
|||
* Add info about attacker blocked by blocker to the game log
|
||||
*/
|
||||
private void logBlockerInfo(Player defender, Game game) {
|
||||
boolean shownDefendingPlayer = game.getPlayers().size() < 3; // only two players no need to saw the attacked player
|
||||
boolean shownDefendingPlayer = game.getPlayers().size() <= 2; // 1 vs 1 game, no need to saw the attacked player
|
||||
for (CombatGroup group : game.getCombat().getGroups()) {
|
||||
if (group.defendingPlayerId.equals(defender.getId())) {
|
||||
if (!shownDefendingPlayer) {
|
||||
|
|
|
|||
|
|
@ -70,7 +70,8 @@ class DackFaydenEmblemTriggeredAbility extends TriggeredAbilityImpl {
|
|||
Spell spell = game.getStack().getSpell(event.getTargetId());
|
||||
if (spell != null) {
|
||||
SpellAbility spellAbility = spell.getSpellAbility();
|
||||
for (Mode mode : spellAbility.getModes().values()) {
|
||||
for (UUID modeId : spellAbility.getModes().getSelectedModes()) {
|
||||
Mode mode = spellAbility.getModes().get(modeId);
|
||||
for (Target target : mode.getTargets()) {
|
||||
if (!target.isNotTarget()) {
|
||||
for (UUID targetId : target.getTargets()) {
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import mage.counters.CounterType;
|
|||
import mage.filter.StaticFilters;
|
||||
import mage.game.Game;
|
||||
import mage.game.command.Emblem;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.players.Player;
|
||||
|
||||
/**
|
||||
|
|
@ -106,8 +107,13 @@ class RadiationEffect extends OneShotEffect {
|
|||
Cards milled = player.millCards(amount, source, game);
|
||||
int countNonLand = milled.count(StaticFilters.FILTER_CARD_NON_LAND, player.getId(), source, game);
|
||||
if (countNonLand > 0) {
|
||||
// TODO: support gaining life instead with [[Strong, the Brutish Thespian]]
|
||||
player.loseLife(countNonLand, game, source, false);
|
||||
GameEvent event = new GameEvent(GameEvent.EventType.RADIATION_GAIN_LIFE, null, source, player.getId(), amount, false);
|
||||
if (game.replaceEvent(event)) {
|
||||
player.gainLife(countNonLand, game, source);
|
||||
} else {
|
||||
player.loseLife(countNonLand, game, source, false);
|
||||
}
|
||||
|
||||
player.loseCounters(CounterType.RAD.getName(), countNonLand, source, game);
|
||||
}
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -672,6 +672,9 @@ public class GameEvent implements Serializable {
|
|||
playerId player who gave the gift
|
||||
*/
|
||||
GAVE_GIFT,
|
||||
/* rad counter life loss/gain effect
|
||||
*/
|
||||
RADIATION_GAIN_LIFE,
|
||||
// custom events - must store some unique data to track
|
||||
CUSTOM_EVENT;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
package mage.game.permanent.token;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.effects.common.continuous.GainAbilityControlledEffect;
|
||||
import mage.abilities.keyword.HorsemanshipAbility;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
|
||||
/**
|
||||
* @author padfoot
|
||||
*/
|
||||
public final class TheGirlInTheFireplaceHorseToken extends TokenImpl {
|
||||
|
||||
public TheGirlInTheFireplaceHorseToken() {
|
||||
super("Horse Token", "2/2 white Horse creature token with \"Doctors you control have horsemanship.\"");
|
||||
cardType.add(CardType.CREATURE);
|
||||
color.setWhite(true);
|
||||
subtype.add(SubType.HORSE);
|
||||
power = new MageInt(2);
|
||||
toughness = new MageInt(2);
|
||||
this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect(HorsemanshipAbility.getInstance(),
|
||||
Duration.WhileOnBattlefield, new FilterCreaturePermanent(SubType.DOCTOR, "Doctors"), false)));
|
||||
}
|
||||
|
||||
private TheGirlInTheFireplaceHorseToken(final TheGirlInTheFireplaceHorseToken token) {
|
||||
super(token);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TheGirlInTheFireplaceHorseToken copy() {
|
||||
return new TheGirlInTheFireplaceHorseToken(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
package mage.game.permanent.token;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.effects.common.PreventDamageToSourceEffect;
|
||||
import mage.abilities.keyword.VanishingAbility;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Duration;
|
||||
|
||||
/**
|
||||
* @author padfoot
|
||||
*/
|
||||
public final class TheGirlInTheFireplaceHumanNobleToken extends TokenImpl {
|
||||
|
||||
public TheGirlInTheFireplaceHumanNobleToken() {
|
||||
super("Human Noble Token", "1/1 white Human Noble creature token with vanishing 3 and \"Prevent all damage that would be dealt to this creature.\"");
|
||||
cardType.add(CardType.CREATURE);
|
||||
color.setWhite(true);
|
||||
subtype.add(SubType.HUMAN,SubType.NOBLE);
|
||||
power = new MageInt(1);
|
||||
toughness = new MageInt(1);
|
||||
this.addAbility(new VanishingAbility(3));
|
||||
this.addAbility(new SimpleStaticAbility(
|
||||
new PreventDamageToSourceEffect(
|
||||
Duration.WhileOnBattlefield,
|
||||
Integer.MAX_VALUE
|
||||
).setText("Prevent all damage that would be dealt to this creature.")
|
||||
));
|
||||
}
|
||||
|
||||
private TheGirlInTheFireplaceHumanNobleToken(final TheGirlInTheFireplaceHumanNobleToken token) {
|
||||
super(token);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TheGirlInTheFireplaceHumanNobleToken copy() {
|
||||
return new TheGirlInTheFireplaceHumanNobleToken(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
package mage.game.permanent.token;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class ZombieDruidToken extends TokenImpl {
|
||||
|
||||
public ZombieDruidToken() {
|
||||
super("Zombie Druid Token", "2/2 black Zombie Druid creature token");
|
||||
cardType.add(CardType.CREATURE);
|
||||
color.setBlack(true);
|
||||
subtype.add(SubType.ZOMBIE);
|
||||
subtype.add(SubType.DRUID);
|
||||
power = new MageInt(2);
|
||||
toughness = new MageInt(2);
|
||||
}
|
||||
|
||||
private ZombieDruidToken(final ZombieDruidToken token) {
|
||||
super(token);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ZombieDruidToken copy() {
|
||||
return new ZombieDruidToken(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,6 @@ package mage.game.turn;
|
|||
import mage.abilities.Ability;
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.TurnPhase;
|
||||
import mage.counters.CounterType;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.PhaseChangedEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
|
@ -17,6 +16,7 @@ import java.util.ArrayList;
|
|||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
|
|
@ -53,13 +53,6 @@ public class Turn implements Serializable {
|
|||
|
||||
}
|
||||
|
||||
public TurnPhase getPhaseType() {
|
||||
if (currentPhase != null) {
|
||||
return currentPhase.getType();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Phase getPhase() {
|
||||
return currentPhase;
|
||||
}
|
||||
|
|
@ -85,14 +78,9 @@ public class Turn implements Serializable {
|
|||
}
|
||||
|
||||
/**
|
||||
* @param game
|
||||
* @param activePlayer
|
||||
* @return true if turn is skipped
|
||||
*/
|
||||
public boolean play(Game game, Player activePlayer) {
|
||||
// uncomment this to trace triggered abilities and/or continous effects
|
||||
// TraceUtil.traceTriggeredAbilities(game);
|
||||
// game.getState().getContinuousEffects().traceContinuousEffects(game);
|
||||
activePlayer.becomesActivePlayer();
|
||||
this.setDeclareAttackersStepStarted(false);
|
||||
if (game.isPaused() || game.checkIfGameIsOver()) {
|
||||
|
|
@ -150,7 +138,12 @@ public class Turn implements Serializable {
|
|||
game.saveState(false);
|
||||
|
||||
//20091005 - 500.8
|
||||
while (playExtraPhases(game, phase.getType())) ;
|
||||
while (true) {
|
||||
// TODO: make sure it work fine (without freeze) on game errors inside extra phases
|
||||
if (!playExtraPhases(game, phase.getType())) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
@ -158,7 +151,6 @@ public class Turn implements Serializable {
|
|||
public void resumePlay(Game game, boolean wasPaused) {
|
||||
activePlayerId = game.getActivePlayerId();
|
||||
Player activePlayer = game.getPlayer(activePlayerId);
|
||||
UUID priorityPlayerId = game.getPriorityPlayerId();
|
||||
TurnPhase needPhaseType = game.getTurnPhaseType();
|
||||
PhaseStep needStepType = game.getTurnStepType();
|
||||
|
||||
|
|
@ -259,10 +251,16 @@ public class Turn implements Serializable {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Play additional phases one by one
|
||||
*
|
||||
* @return false to finish
|
||||
*/
|
||||
private boolean playExtraPhases(Game game, TurnPhase afterPhase) {
|
||||
while (true) {
|
||||
TurnMod extraPhaseMod = game.getState().getTurnMods().useNextExtraPhase(activePlayerId, afterPhase);
|
||||
if (extraPhaseMod == null) {
|
||||
// no more extra phases
|
||||
return false;
|
||||
}
|
||||
TurnPhase extraPhase = extraPhaseMod.getExtraPhase();
|
||||
|
|
@ -316,12 +314,8 @@ public class Turn implements Serializable {
|
|||
|
||||
/**
|
||||
* Used for some spells with end turn effect (e.g. Time Stop).
|
||||
*
|
||||
* @param game
|
||||
* @param activePlayerId
|
||||
* @param source
|
||||
*/
|
||||
public void endTurn(Game game, UUID activePlayerId, Ability source) {
|
||||
public void endTurn(Game game, Ability source) {
|
||||
// Ending the turn this way (Time Stop) means the following things happen in order:
|
||||
|
||||
setEndTurnRequested(true);
|
||||
|
|
@ -391,26 +385,18 @@ public class Turn implements Serializable {
|
|||
}
|
||||
|
||||
private void logStartOfTurn(Game game, Player player) {
|
||||
StringBuilder sb = new StringBuilder("Turn ");
|
||||
sb.append(game.getState().getTurnNum()).append(' ');
|
||||
if (game.getState().isExtraTurn()) {
|
||||
sb.append("(extra) ");
|
||||
}
|
||||
sb.append(player.getLogName());
|
||||
sb.append(" (");
|
||||
int delimiter = game.getPlayers().size() - 1;
|
||||
for (Player gamePlayer : game.getPlayers().values()) {
|
||||
sb.append(gamePlayer.getLife());
|
||||
int poison = gamePlayer.getCountersCount(CounterType.POISON);
|
||||
if (poison > 0) {
|
||||
sb.append("[P:").append(poison).append(']');
|
||||
}
|
||||
if (delimiter > 0) {
|
||||
sb.append(" - ");
|
||||
delimiter--;
|
||||
}
|
||||
}
|
||||
sb.append(')');
|
||||
game.fireStatusEvent(sb.toString(), true, false);
|
||||
// example: 0:40: TURN 1 for Human (40 - 40)
|
||||
|
||||
String infoTurn = String.format("TURN %d%s for %s",
|
||||
game.getState().getTurnNum(),
|
||||
game.getState().isExtraTurn() ? " (extra)" : "",
|
||||
player.getLogName()
|
||||
);
|
||||
|
||||
String infoLife = game.getPlayers().values().stream()
|
||||
.map(p -> String.valueOf(p.getLife()))
|
||||
.collect(Collectors.joining(" - "));
|
||||
|
||||
game.fireStatusEvent(infoTurn + " (" + infoLife + ")", true, false);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,85 +0,0 @@
|
|||
package mage.util.trace;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.constants.Duration;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class TraceInfo {
|
||||
public String info;
|
||||
public String playerName;
|
||||
public String sourceName;
|
||||
public String rule;
|
||||
public UUID abilityId;
|
||||
public UUID effectId;
|
||||
public Duration duration;
|
||||
public long order;
|
||||
|
||||
public String getPlayerName() {
|
||||
return playerName;
|
||||
}
|
||||
|
||||
public void setPlayerName(String playerName) {
|
||||
this.playerName = playerName;
|
||||
}
|
||||
|
||||
public String getSourceName() {
|
||||
return sourceName;
|
||||
}
|
||||
|
||||
public void setSourceName(String sourceName) {
|
||||
this.sourceName = sourceName;
|
||||
}
|
||||
|
||||
public String getRule() {
|
||||
return rule;
|
||||
}
|
||||
|
||||
public void setRule(String rule) {
|
||||
this.rule = rule;
|
||||
}
|
||||
|
||||
public UUID getAbilityId() {
|
||||
return abilityId;
|
||||
}
|
||||
|
||||
public void setAbilityId(UUID abilityId) {
|
||||
this.abilityId = abilityId;
|
||||
}
|
||||
|
||||
public UUID getEffectId() {
|
||||
return effectId;
|
||||
}
|
||||
|
||||
public void setEffectId(UUID effectId) {
|
||||
this.effectId = effectId;
|
||||
}
|
||||
|
||||
public String getInfo() {
|
||||
return info;
|
||||
}
|
||||
|
||||
public void setInfo(String info) {
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
public Duration getDuration() {
|
||||
return duration;
|
||||
}
|
||||
|
||||
public void setDuration(Duration duration) {
|
||||
this.duration = duration;
|
||||
}
|
||||
|
||||
public long getOrder() {
|
||||
return order;
|
||||
}
|
||||
|
||||
public void setOrder(long order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -1,241 +0,0 @@
|
|||
package mage.util.trace;
|
||||
|
||||
import java.util.*;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.StaticAbility;
|
||||
import mage.abilities.TriggeredAbility;
|
||||
import mage.abilities.effects.ContinuousEffectsList;
|
||||
import mage.abilities.effects.RestrictionEffect;
|
||||
import mage.abilities.keyword.CantBeBlockedSourceAbility;
|
||||
import mage.abilities.keyword.FlyingAbility;
|
||||
import mage.abilities.keyword.IntimidateAbility;
|
||||
import mage.abilities.keyword.ReachAbility;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.combat.Combat;
|
||||
import mage.game.combat.CombatGroup;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
/**
|
||||
* @author magenoxx_at_gmail.com
|
||||
*/
|
||||
public final class TraceUtil {
|
||||
|
||||
private static final Logger log = Logger.getLogger(TraceUtil.class);
|
||||
|
||||
/**
|
||||
* This method is intended to catch various bugs with combat.
|
||||
*
|
||||
* One of them (possibly the most annoying) is when creature without flying or reach blocks creature with flying.
|
||||
* No test managed to reproduce it, but it happens in the games time to time and was reported by different players.
|
||||
*
|
||||
* The idea: is to catch such cases manually and print out as much information from game state that may help as possible.
|
||||
* @param game
|
||||
* @param combat
|
||||
*/
|
||||
public static void traceCombatIfNeeded(Game game, Combat combat) {
|
||||
// trace non-flying vs flying
|
||||
for (CombatGroup group : combat.getGroups()) {
|
||||
for (UUID attackerId : group.getAttackers()) {
|
||||
Permanent attacker = game.getPermanent(attackerId);
|
||||
if (attacker != null) {
|
||||
if (hasFlying(attacker)) {
|
||||
// traceCombat(game, attacker, null);
|
||||
for (UUID blockerId : group.getBlockers()) {
|
||||
Permanent blocker = game.getPermanent(blockerId);
|
||||
if (blocker != null && !hasFlying(blocker) && !hasReach(blocker)) {
|
||||
log.warn("Found non-flying non-reach creature blocking creature with flying");
|
||||
traceCombat(game, attacker, blocker);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hasIntimidate(attacker)) {
|
||||
for (UUID blockerId : group.getBlockers()) {
|
||||
Permanent blocker = game.getPermanent(blockerId);
|
||||
if (blocker != null && !blocker.isArtifact(game)
|
||||
&& !attacker.getColor(game).shares(blocker.getColor(game))) {
|
||||
log.warn("Found creature with intimidate blocked by non artifact not sharing color creature");
|
||||
traceCombat(game, attacker, blocker);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cantBeBlocked(attacker)) {
|
||||
if (!group.getBlockers().isEmpty()) {
|
||||
Permanent blocker = game.getPermanent(group.getBlockers().get(0));
|
||||
if (blocker != null) {
|
||||
log.warn("Found creature that can't be blocked by some other creature");
|
||||
traceCombat(game, attacker, blocker);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* We need this to check Flying existence in not-common way: by instanceof.
|
||||
* @return
|
||||
*/
|
||||
private static boolean hasFlying(Permanent permanent) {
|
||||
for (Ability ability : permanent.getAbilities()) {
|
||||
if (ability instanceof FlyingAbility) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean hasIntimidate(Permanent permanent) {
|
||||
for (Ability ability : permanent.getAbilities()) {
|
||||
if (ability instanceof IntimidateAbility) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean hasReach(Permanent permanent) {
|
||||
for (Ability ability : permanent.getAbilities()) {
|
||||
if (ability instanceof ReachAbility) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean cantBeBlocked(Permanent permanent) {
|
||||
for (Ability ability : permanent.getAbilities()) {
|
||||
if (ability instanceof CantBeBlockedSourceAbility) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void traceCombat(Game game, Permanent attacker, Permanent blocker) {
|
||||
String prefix = "> ";
|
||||
log.error(prefix+"Tracing game state...");
|
||||
if (blocker != null) {
|
||||
log.error(prefix+blocker.getLogName() + " could block " + attacker.getLogName());
|
||||
}
|
||||
|
||||
log.error(prefix);
|
||||
log.error(prefix+"Attacker abilities: ");
|
||||
for (Ability ability : attacker.getAbilities()) {
|
||||
log.error(prefix+" " + ability.toString() + ", id=" + ability.getId());
|
||||
}
|
||||
if (blocker != null) {
|
||||
log.error(prefix+"Blocker abilities: ");
|
||||
for (Ability ability : blocker.getAbilities()) {
|
||||
log.error(prefix+" " + ability.toString() + ", id=" + ability.getId());
|
||||
}
|
||||
}
|
||||
|
||||
log.error(prefix);
|
||||
log.error(prefix+"Flying ability id: " + FlyingAbility.getInstance().getId());
|
||||
log.error(prefix+"Reach ability id: " + ReachAbility.getInstance().getId());
|
||||
log.error(prefix+"Intimidate ability id: " + IntimidateAbility.getInstance().getId());
|
||||
log.error(prefix);
|
||||
|
||||
log.error(prefix+"Restriction effects:");
|
||||
log.error(prefix+" Applied to ATTACKER:");
|
||||
Map<RestrictionEffect, Set<Ability>> attackerResEffects = game.getContinuousEffects().getApplicableRestrictionEffects(attacker, game);
|
||||
for (Map.Entry<RestrictionEffect, Set<Ability>> entry : attackerResEffects.entrySet()) {
|
||||
log.error(prefix+" " + entry.getKey());
|
||||
log.error(prefix+" id=" + entry.getKey().getId());
|
||||
for (Ability ability: entry.getValue()) {
|
||||
log.error(prefix+" ability=" + ability);
|
||||
}
|
||||
}
|
||||
log.error(prefix+" Applied to BLOCKER:");
|
||||
if (blocker != null) {
|
||||
Map<RestrictionEffect, Set<Ability>> blockerResEffects = game.getContinuousEffects().getApplicableRestrictionEffects(blocker, game);
|
||||
for (Map.Entry<RestrictionEffect, Set<Ability>> entry : blockerResEffects.entrySet()) {
|
||||
log.error(prefix+" " + entry.getKey());
|
||||
log.error(prefix+" id=" + entry.getKey().getId());
|
||||
for (Ability ability: entry.getValue()) {
|
||||
log.error(prefix+" ability=" + ability);
|
||||
}
|
||||
}
|
||||
}
|
||||
ContinuousEffectsList<RestrictionEffect> restrictionEffects = (ContinuousEffectsList<RestrictionEffect>) game.getContinuousEffects().getRestrictionEffects();
|
||||
log.error(prefix);
|
||||
log.error(prefix+" List of all restriction effects:");
|
||||
for (RestrictionEffect effect : restrictionEffects) {
|
||||
log.error(prefix+" " + effect);
|
||||
log.error(prefix+" id=" + effect.getId());
|
||||
}
|
||||
|
||||
log.error(prefix);
|
||||
log.error(prefix+" Trace Attacker:");
|
||||
traceForPermanent(game, attacker, prefix, restrictionEffects);
|
||||
if (blocker != null) {
|
||||
log.error(prefix);
|
||||
log.error(prefix+" Trace Blocker:");
|
||||
traceForPermanent(game, blocker, prefix, restrictionEffects);
|
||||
}
|
||||
|
||||
log.error(prefix);
|
||||
}
|
||||
|
||||
private static void traceForPermanent(Game game, Permanent permanent, String uuid, ContinuousEffectsList<RestrictionEffect> restrictionEffects) {
|
||||
for (RestrictionEffect effect: restrictionEffects) {
|
||||
log.error(uuid+" effect=" + effect.toString() + " id=" + effect.getId());
|
||||
for (Ability ability : restrictionEffects.getAbility(effect.getId())) {
|
||||
if (!(ability instanceof StaticAbility) || ability.isInUseableZone(game, permanent, null)) {
|
||||
log.error(uuid+" ability=" + ability + ", applies_to_attacker=" + effect.applies(permanent, ability, game));
|
||||
} else {
|
||||
boolean usable = ability.isInUseableZone(game, permanent, null);
|
||||
log.error(uuid+" instanceof StaticAbility: " + (ability instanceof StaticAbility) + ", ability=" + ability);
|
||||
log.error(uuid+" usable zone: " + usable + ", ability=" + ability);
|
||||
if (!usable) {
|
||||
Zone zone = ability.getZone();
|
||||
log.error(uuid+" zone: " + zone);
|
||||
MageObject object = game.getObject(ability.getSourceId());
|
||||
log.error(uuid+" object: " + object);
|
||||
if (object != null) {
|
||||
log.error(uuid + " contains ability: " + object.getAbilities().contains(ability));
|
||||
}
|
||||
Zone test = game.getState().getZone(ability.getSourceId());
|
||||
log.error(uuid+" test_zone: " + test);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void trace(String msg) {
|
||||
log.info(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints out a status of the currently existing triggered abilities
|
||||
* @param game
|
||||
*/
|
||||
public static void traceTriggeredAbilities(Game game) {
|
||||
log.info("-------------------------------------------------------------------------------------------------");
|
||||
log.info("Turn: " + game.getTurnNum() + " - currently existing triggered abilities: " + game.getState().getTriggers().size());
|
||||
Map<String, String> orderedAbilities = new TreeMap<>();
|
||||
for (Map.Entry<String, TriggeredAbility> entry : game.getState().getTriggers().entrySet()) {
|
||||
Player controller = game.getPlayer(entry.getValue().getControllerId());
|
||||
MageObject source = game.getObject(entry.getValue().getSourceId());
|
||||
orderedAbilities.put((controller == null ? "no controller": controller.getName()) + (source == null ? "no source": source.getIdName())+ entry.getKey(), entry.getKey());
|
||||
}
|
||||
String playerName = "";
|
||||
for (Map.Entry<String, String> entry : orderedAbilities.entrySet()) {
|
||||
TriggeredAbility trAbility = game.getState().getTriggers().get(entry.getValue());
|
||||
Player controller = game.getPlayer(trAbility.getControllerId());
|
||||
MageObject source = game.getObject(trAbility.getSourceId());
|
||||
if (!controller.getName().equals(playerName)) {
|
||||
playerName = controller.getName();
|
||||
log.info("--- Player: " + playerName + " --------------------------------");
|
||||
}
|
||||
log.info((source == null ? "no source": source.getIdName()) + " -> "
|
||||
+ trAbility.getRule());
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue