Refactor: replaced sourceId by source and introduced source param in some methods;

This commit is contained in:
Oleg Agafonov 2020-12-12 20:23:19 +04:00
parent 2bb472607b
commit db239a1055
3205 changed files with 7080 additions and 6795 deletions

View file

@ -361,7 +361,12 @@ public interface Game extends MageItem, Serializable {
void undo(UUID playerId);
void emptyManaPools();
/**
* Empty mana pool with mana burn and life lose checks
*
* @param source must be null for default game events
*/
void emptyManaPools(Ability source);
void addEffect(ContinuousEffect continuousEffect, Ability source);
@ -420,7 +425,7 @@ public interface Game extends MageItem, Serializable {
boolean endTurn(Ability source);
int doAction(MageAction action, UUID sourceId);
int doAction(Ability source, MageAction action);
//game transaction methods
void saveState(boolean bookmark);
@ -448,7 +453,7 @@ public interface Game extends MageItem, Serializable {
// game cheats (for tests only)
void cheat(UUID ownerId, Map<Zone, String> commands);
void cheat(UUID ownerId, UUID activePlayerId, List<Card> library, List<Card> hand, List<PermanentCard> battlefield, List<Card> graveyard, List<Card> command);
void cheat(UUID ownerId, List<Card> library, List<Card> hand, List<PermanentCard> battlefield, List<Card> graveyard, List<Card> command);
// controlling the behaviour of replacement effects while permanents entering the battlefield
void setScopeRelevant(boolean scopeRelevant);
@ -486,9 +491,9 @@ public interface Game extends MageItem, Serializable {
void setMonarchId(Ability source, UUID monarchId);
int damagePlayerOrPlaneswalker(UUID playerOrWalker, int damage, UUID sourceId, Game game, boolean combatDamage, boolean preventable);
int damagePlayerOrPlaneswalker(UUID playerOrWalker, int damage, UUID attackerId, Ability source, Game game, boolean combatDamage, boolean preventable);
int damagePlayerOrPlaneswalker(UUID playerOrWalker, int damage, UUID sourceId, Game game, boolean combatDamage, boolean preventable, List<UUID> appliedEffects);
int damagePlayerOrPlaneswalker(UUID playerOrWalker, int damage, UUID attackerId, Ability source, Game game, boolean combatDamage, boolean preventable, List<UUID> appliedEffects);
Mulligan getMulligan();

View file

@ -6,12 +6,14 @@ import mage.abilities.*;
import mage.abilities.common.AttachableToRestrictedAbility;
import mage.abilities.common.CantHaveMoreThanAmountCountersSourceAbility;
import mage.abilities.common.SagaAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.common.delayed.ReflexiveTriggeredAbility;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.ContinuousEffects;
import mage.abilities.effects.Effect;
import mage.abilities.effects.PreventionEffectData;
import mage.abilities.effects.common.CopyEffect;
import mage.abilities.effects.common.InfoEffect;
import mage.abilities.keyword.BestowAbility;
import mage.abilities.keyword.CompanionAbility;
import mage.abilities.keyword.MorphAbility;
@ -1069,7 +1071,7 @@ public abstract class GameImpl implements Game, Serializable {
Player player = getPlayer(playerId);
Cards cardsWithOpeningAction = new CardsImpl();
for (Card card : player.getHand().getCards(this)) {
for (Ability ability : card.getAbilities()) {
for (Ability ability : card.getAbilities(this)) {
if (ability instanceof OpeningHandAction) {
OpeningHandAction action = (OpeningHandAction) ability;
if (action.isOpeningHandActionAllowed(card, player, this)) {
@ -1088,7 +1090,7 @@ public abstract class GameImpl implements Game, Serializable {
card = cardsWithOpeningAction.getRandom(this);
}
if (card != null) {
for (Ability ability : card.getAbilities()) {
for (Ability ability : card.getAbilities(this)) {
if (ability instanceof OpeningHandAction) {
OpeningHandAction action = (OpeningHandAction) ability;
if (action.askUseOpeningHandAction(card, player, this)) {
@ -1541,11 +1543,11 @@ public abstract class GameImpl implements Game, Serializable {
}
@Override
public void emptyManaPools() {
public void emptyManaPools(Ability source) {
for (Player player : getPlayers().values()) {
int amount = player.getManaPool().emptyPool(this);
if (state.isManaBurn() && amount > 0) {
player.loseLife(amount, this, false);
player.loseLife(amount, this, source, false);
}
}
}
@ -1783,7 +1785,7 @@ public abstract class GameImpl implements Game, Serializable {
@Override
public UUID fireReflexiveTriggeredAbility(ReflexiveTriggeredAbility reflexiveAbility, Ability source) {
UUID uuid = this.addDelayedTriggeredAbility(reflexiveAbility, source);
this.fireEvent(GameEvent.getEvent(GameEvent.EventType.OPTION_USED, source.getOriginalId(), source.getSourceId(), source.getControllerId()));
this.fireEvent(GameEvent.getEvent(GameEvent.EventType.OPTION_USED, source.getOriginalId(), source, source.getControllerId()));
return uuid;
}
@ -2088,8 +2090,8 @@ public abstract class GameImpl implements Game, Serializable {
Card card = this.getCard(perm.getId());
if (card != null && card.isCreature()) {
UUID wasAttachedTo = perm.getAttachedTo();
perm.attachTo(null, this);
fireEvent(new GameEvent(GameEvent.EventType.UNATTACHED, wasAttachedTo, perm.getId(), perm.getControllerId()));
perm.attachTo(null, null, this);
fireEvent(new UnattachedEvent(wasAttachedTo, perm.getId(), perm, null));
} else if (movePermanentToGraveyardWithInfo(perm)) {
somethingHappened = true;
}
@ -2097,19 +2099,19 @@ public abstract class GameImpl implements Game, Serializable {
Filter auraFilter = spellAbility.getTargets().get(0).getFilter();
if (auraFilter instanceof FilterControlledPermanent) {
if (!((FilterControlledPermanent) auraFilter).match(attachedTo, perm.getId(), perm.getControllerId(), this)
|| attachedTo.cantBeAttachedBy(perm, this, true)) {
|| attachedTo.cantBeAttachedBy(perm, null, this, true)) {
if (movePermanentToGraveyardWithInfo(perm)) {
somethingHappened = true;
}
}
} else if (!auraFilter.match(attachedTo, this) || attachedTo.cantBeAttachedBy(perm, this, true)) {
} else if (!auraFilter.match(attachedTo, this) || attachedTo.cantBeAttachedBy(perm, null, this, true)) {
// handle bestow unattachment
Card card = this.getCard(perm.getId());
if (card != null && card.isCreature()) {
UUID wasAttachedTo = perm.getAttachedTo();
perm.attachTo(null, this);
perm.attachTo(null, null, this);
BestowAbility.becomeCreature(perm, this);
fireEvent(new GameEvent(GameEvent.EventType.UNATTACHED, wasAttachedTo, perm.getId(), perm.getControllerId()));
fireEvent(new UnattachedEvent(wasAttachedTo, perm.getId(), perm, null));
} else if (movePermanentToGraveyardWithInfo(perm)) {
somethingHappened = true;
}
@ -2135,7 +2137,7 @@ public abstract class GameImpl implements Game, Serializable {
|| !(spellAbility.getTargets().get(0)).canTarget(perm.getControllerId(), perm.getAttachedTo(), spellAbility, this)) {
if (movePermanentToGraveyardWithInfo(perm)) {
if (attachedTo != null) {
attachedTo.removeAttachment(perm.getId(), this);
attachedTo.removeAttachment(perm.getId(), null, this);
}
somethingHappened = true;
}
@ -2169,7 +2171,7 @@ public abstract class GameImpl implements Game, Serializable {
}
if (noChapterAbilityTriggeredOrOnStack) {
// After the last chapter ability has left the stack, you'll sacrifice the Saga
perm.sacrifice(perm.getId(), this);
perm.sacrifice(null, this);
somethingHappened = true;
}
}
@ -2196,10 +2198,10 @@ public abstract class GameImpl implements Game, Serializable {
}
if (attachedTo == null || !attachedTo.getAttachments().contains(perm.getId())) {
UUID wasAttachedTo = perm.getAttachedTo();
perm.attachTo(null, this);
fireEvent(new GameEvent(GameEvent.EventType.UNATTACHED, wasAttachedTo, perm.getId(), perm.getControllerId()));
perm.attachTo(null, null, this);
fireEvent(new UnattachedEvent(wasAttachedTo, perm.getId(), perm, null));
} else if (!attachedTo.isCreature() || attachedTo.hasProtectionFrom(perm, this)) {
if (attachedTo.removeAttachment(perm.getId(), this)) {
if (attachedTo.removeAttachment(perm.getId(), null, this)) {
somethingHappened = true;
}
}
@ -2209,9 +2211,9 @@ public abstract class GameImpl implements Game, Serializable {
if (perm.getAttachedTo() != null) {
Permanent land = getPermanent(perm.getAttachedTo());
if (land == null || !land.getAttachments().contains(perm.getId())) {
perm.attachTo(null, this);
perm.attachTo(null, null, this);
} else if (!land.isLand() || land.hasProtectionFrom(perm, this)) {
if (land.removeAttachment(perm.getId(), this)) {
if (land.removeAttachment(perm.getId(), null, this)) {
somethingHappened = true;
}
}
@ -2228,7 +2230,7 @@ public abstract class GameImpl implements Game, Serializable {
|| !(attachment.getSubtype(this).contains(SubType.AURA)
|| attachment.getSubtype(this).contains(SubType.EQUIPMENT)
|| attachment.getSubtype(this).contains(SubType.FORTIFICATION)))) {
if (perm.removeAttachment(attachment.getId(), this)) {
if (perm.removeAttachment(attachment.getId(), null, this)) {
somethingHappened = true;
break;
}
@ -2253,7 +2255,7 @@ public abstract class GameImpl implements Game, Serializable {
CantHaveMoreThanAmountCountersSourceAbility counterAbility = (CantHaveMoreThanAmountCountersSourceAbility) ability;
int count = perm.getCounters(this).getCount(counterAbility.getCounterType());
if (count > counterAbility.getAmount()) {
perm.removeCounters(counterAbility.getCounterType().getName(), count - counterAbility.getAmount(), this);
perm.removeCounters(counterAbility.getCounterType().getName(), count - counterAbility.getAmount(), counterAbility, this);
somethingHappened = true;
}
}
@ -2638,11 +2640,11 @@ public abstract class GameImpl implements Game, Serializable {
if (perm.getAttachedTo() != null) {
Permanent attachedTo = getPermanent(perm.getAttachedTo());
if (attachedTo != null) {
attachedTo.removeAttachment(perm.getId(), this);
attachedTo.removeAttachment(perm.getId(), null, this);
} else {
Player attachedToPlayer = getPlayer(perm.getAttachedTo());
if (attachedToPlayer != null) {
attachedToPlayer.removeAttachment(perm, this);
attachedToPlayer.removeAttachment(perm, null, this);
}
}
}
@ -2814,7 +2816,7 @@ public abstract class GameImpl implements Game, Serializable {
return result;
}
DamageEvent damageEvent = (DamageEvent) event;
GameEvent preventEvent = new PreventDamageEvent(damageEvent.getTargetId(), damageEvent.getSourceId(), source.getControllerId(), damageEvent.getAmount(), damageEvent.isCombatDamage());
GameEvent preventEvent = new PreventDamageEvent(damageEvent.getTargetId(), damageEvent.getSourceId(), source, source.getControllerId(), damageEvent.getAmount(), damageEvent.isCombatDamage());
if (game.replaceEvent(preventEvent)) {
result.setReplaced(true);
return result;
@ -2855,7 +2857,7 @@ public abstract class GameImpl implements Game, Serializable {
game.informPlayers(message.toString());
}
}
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.PREVENTED_DAMAGE, damageEvent.getTargetId(), source.getSourceId(), source.getControllerId(), result.getPreventedDamage()));
game.fireEvent(new PreventedDamageEvent(damageEvent.getTargetId(), source.getSourceId(), source, source.getControllerId(), result.getPreventedDamage()));
return result;
}
@ -3039,7 +3041,7 @@ public abstract class GameImpl implements Game, Serializable {
if (s.length == 2) {
try {
Integer amount = Integer.parseInt(s[1]);
player.setLife(amount, this, ownerId);
player.setLife(amount, this, null);
logger.debug("Setting player's life: ");
} catch (NumberFormatException e) {
logger.fatal("error setting life", e);
@ -3060,7 +3062,11 @@ public abstract class GameImpl implements Game, Serializable {
}
@Override
public void cheat(UUID ownerId, UUID activePlayerId, List<Card> library, List<Card> hand, List<PermanentCard> battlefield, List<Card> graveyard, List<Card> command) {
public void cheat(UUID ownerId, List<Card> library, List<Card> hand, List<PermanentCard> battlefield, List<Card> graveyard, List<Card> command) {
// fake test ability for triggers and events
Ability fakeSourceAbility = new SimpleStaticAbility(Zone.OUTSIDE, new InfoEffect("adding testing cards"));
fakeSourceAbility.setControllerId(ownerId);
Player player = getPlayer(ownerId);
if (player != null) {
loadCards(ownerId, library);
@ -3094,7 +3100,7 @@ public abstract class GameImpl implements Game, Serializable {
}
for (PermanentCard permanentCard : battlefield) {
CardUtil.putCardOntoBattlefieldWithEffects(this, permanentCard, player);
CardUtil.putCardOntoBattlefieldWithEffects(fakeSourceAbility, this, permanentCard, player);
}
applyEffects();
@ -3116,11 +3122,8 @@ public abstract class GameImpl implements Game, Serializable {
}
@Override
public int doAction(MageAction action, UUID sourceId) {
//actions.add(action);
int value = action.doAction(sourceId, this);
// score += action.getScore(scorePlayer);
return value;
public int doAction(Ability source, MageAction action) {
return action.doAction(source, this);
}
@Override
@ -3370,10 +3373,7 @@ public abstract class GameImpl implements Game, Serializable {
if (monarchId.equals(getMonarchId())) { // Nothing happens if you're already the monarch
return;
}
if (replaceEvent(GameEvent.getEvent(
GameEvent.EventType.BECOME_MONARCH,
monarchId, source.getSourceId(), monarchId
))) {
if (replaceEvent(GameEvent.getEvent(GameEvent.EventType.BECOME_MONARCH, monarchId, source, monarchId))) {
return;
}
Player newMonarch = getPlayer(monarchId);
@ -3383,24 +3383,24 @@ public abstract class GameImpl implements Game, Serializable {
if (newMonarch != null) {
getState().setMonarchId(monarchId);
informPlayers(newMonarch.getLogName() + " is the monarch");
fireEvent(new GameEvent(GameEvent.EventType.BECOMES_MONARCH, monarchId, source == null ? null : source.getSourceId(), monarchId));
fireEvent(new GameEvent(GameEvent.EventType.BECOMES_MONARCH, monarchId, source, monarchId));
}
}
@Override
public int damagePlayerOrPlaneswalker(UUID playerOrWalker, int damage, UUID sourceId, Game game, boolean combatDamage, boolean preventable) {
return damagePlayerOrPlaneswalker(playerOrWalker, damage, sourceId, game, combatDamage, preventable, null);
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);
}
@Override
public int damagePlayerOrPlaneswalker(UUID playerOrWalker, int damage, UUID sourceId, Game game, boolean combatDamage, boolean preventable, List<UUID> appliedEffects) {
public int damagePlayerOrPlaneswalker(UUID playerOrWalker, int damage, UUID attackerId, Ability source, Game game, boolean combatDamage, boolean preventable, List<UUID> appliedEffects) {
Player player = getPlayer(playerOrWalker);
if (player != null) {
return player.damage(damage, sourceId, game, combatDamage, preventable, appliedEffects);
return player.damage(damage, attackerId, source, game, combatDamage, preventable, appliedEffects);
}
Permanent permanent = getPermanent(playerOrWalker);
if (permanent != null) {
return permanent.damage(damage, sourceId, game, combatDamage, preventable, appliedEffects);
return permanent.damage(damage, attackerId, source, game, combatDamage, preventable, appliedEffects);
}
return 0;
}

View file

@ -3,6 +3,8 @@ package mage.game;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import mage.abilities.Ability;
import mage.cards.MeldCard;
import mage.game.events.ZoneChangeEvent;
import mage.game.stack.Spell;
@ -91,20 +93,24 @@ public class ZoneChangeInfo {
public static class Battlefield extends ZoneChangeInfo {
public boolean tapped;
public Ability source;
public Battlefield(ZoneChangeEvent event, boolean tapped) {
public Battlefield(ZoneChangeEvent event, boolean tapped, Ability source) {
super(event);
this.tapped = tapped;
this.source = source;
}
public Battlefield(ZoneChangeEvent event, boolean faceDown, boolean tapped) {
public Battlefield(ZoneChangeEvent event, boolean faceDown, boolean tapped, Ability source) {
super(event, faceDown);
this.tapped = tapped;
this.source = source;
}
public Battlefield(final Battlefield info) {
super(info);
this.tapped = info.tapped;
this.source = info.source;
}
@Override
@ -147,14 +153,14 @@ public class ZoneChangeInfo {
MeldCard meld = game.getMeldCard(info.event.getTargetId());
if (meld != null) {
if (meld.hasTopHalf(game)) {
ZoneChangeEvent topEvent = new ZoneChangeEvent(meld.getTopHalfCard().getId(), event.getSourceId(),
ZoneChangeEvent topEvent = new ZoneChangeEvent(meld.getTopHalfCard().getId(), event.getSource(),
event.getPlayerId(), event.getFromZone(), event.getToZone(), event.getAppliedEffects());
ZoneChangeInfo topInfo = info.copy();
topInfo.event = topEvent;
subInfo.add(topInfo);
}
if (meld.hasBottomHalf(game)) {
ZoneChangeEvent bottomEvent = new ZoneChangeEvent(meld.getBottomHalfCard().getId(), event.getSourceId(),
ZoneChangeEvent bottomEvent = new ZoneChangeEvent(meld.getBottomHalfCard().getId(), event.getSource(),
event.getPlayerId(), event.getFromZone(), event.getToZone(), event.getAppliedEffects());
ZoneChangeInfo bottomInfo = info.copy();
bottomInfo.event = bottomEvent;

View file

@ -1,5 +1,6 @@
package mage.game;
import mage.abilities.Ability;
import mage.abilities.keyword.TransformAbility;
import mage.cards.*;
import mage.constants.Outcome;
@ -23,8 +24,8 @@ import java.util.*;
*/
public final class ZonesHandler {
public static boolean cast(ZoneChangeInfo info, Game game) {
if (maybeRemoveFromSourceZone(info, game)) {
public static boolean cast(ZoneChangeInfo info, Game game, Ability source) {
if (maybeRemoveFromSourceZone(info, game, source)) {
placeInDestinationZone(info, game, 0);
// create a group zone change event if a card is moved to stack for casting (it's always only one card, but some effects check for group events (one or more xxx))
Set<Card> cards = new HashSet<>();
@ -43,13 +44,13 @@ public final class ZonesHandler {
return false;
}
public static boolean moveCard(ZoneChangeInfo info, Game game) {
public static boolean moveCard(ZoneChangeInfo info, Game game, Ability source) {
List<ZoneChangeInfo> list = new ArrayList<>();
list.add(info);
return !moveCards(list, game).isEmpty();
return !moveCards(list, game, source).isEmpty();
}
public static List<ZoneChangeInfo> moveCards(List<ZoneChangeInfo> zoneChangeInfos, Game game) {
public static List<ZoneChangeInfo> moveCards(List<ZoneChangeInfo> zoneChangeInfos, Game game, Ability source) {
// Handle Unmelded Meld Cards
for (ListIterator<ZoneChangeInfo> itr = zoneChangeInfos.listIterator(); itr.hasNext(); ) {
ZoneChangeInfo info = itr.next();
@ -75,13 +76,17 @@ public final class ZonesHandler {
ZoneChangeInfo info = itr.next();
if (info.event.getToZone().equals(Zone.BATTLEFIELD)) {
Card card = game.getCard(info.event.getTargetId());
if (card instanceof ModalDoubleFacesCard || card instanceof ModalDoubleFacesCardHalf) {
System.out.println("!"); // TODO: remove after mdf test fixes
}
if (card instanceof ModalDoubleFacesCard) {
info.event.setTargetId(((ModalDoubleFacesCard) card).getLeftHalfCard().getId());
}
}
}
zoneChangeInfos.removeIf(zoneChangeInfo -> !maybeRemoveFromSourceZone(zoneChangeInfo, game));
zoneChangeInfos.removeIf(zoneChangeInfo -> !maybeRemoveFromSourceZone(zoneChangeInfo, game, source));
int createOrder = 0;
for (ZoneChangeInfo zoneChangeInfo : zoneChangeInfos) {
if (createOrder == 0 && Zone.BATTLEFIELD.equals(zoneChangeInfo.event.getToZone())) {
@ -270,14 +275,16 @@ public final class ZonesHandler {
return game.getPermanent(targetId);
}
private static boolean maybeRemoveFromSourceZone(ZoneChangeInfo info, Game game) {
private static boolean maybeRemoveFromSourceZone(ZoneChangeInfo info, Game game, Ability source) {
ZoneChangeEvent event = info.event;
// Handle Unmelded Cards
if (info instanceof ZoneChangeInfo.Unmelded) {
ZoneChangeInfo.Unmelded unmelded = (ZoneChangeInfo.Unmelded) info;
MeldCard meld = game.getMeldCard(info.event.getTargetId());
MeldCard meld = game.getMeldCard(event.getTargetId());
for (Iterator<ZoneChangeInfo> itr = unmelded.subInfo.iterator(); itr.hasNext(); ) {
ZoneChangeInfo subInfo = itr.next();
if (!maybeRemoveFromSourceZone(subInfo, game)) {
if (!maybeRemoveFromSourceZone(subInfo, game, source)) {
itr.remove();
} else if (Objects.equals(subInfo.event.getTargetId(), meld.getTopHalfCard().getId())) {
meld.setTopLastZoneChangeCounter(meld.getTopHalfCard().getZoneChangeCounter(game));
@ -294,7 +301,6 @@ public final class ZonesHandler {
}
// Handle all normal cases
ZoneChangeEvent event = info.event;
Card card = getTargetCard(game, event.getTargetId());
if (card == null) {
// If we can't find the card we can't remove it.
@ -304,7 +310,7 @@ public final class ZonesHandler {
boolean success = false;
if (info.faceDown) {
card.setFaceDown(true, game);
} else if (info.event.getToZone().equals(Zone.BATTLEFIELD)) {
} else if (event.getToZone().equals(Zone.BATTLEFIELD)) {
if (!card.isPermanent()
&& (!card.isTransformable() || Boolean.FALSE.equals(game.getState().getValue(TransformAbility.VALUE_KEY_ENTER_TRANSFORMED + card.getId())))) {
// Non permanents (Instants, Sorceries, ... stay in the zone they are if an abilty/effect tries to move it to the battlefield
@ -332,7 +338,7 @@ public final class ZonesHandler {
// put onto battlefield with possible counters
game.getPermanentsEntering().put(permanent.getId(), permanent);
card.checkForCountersToAdd(permanent, game);
card.checkForCountersToAdd(permanent, source, game);
permanent.setTapped(info instanceof ZoneChangeInfo.Battlefield
&& ((ZoneChangeInfo.Battlefield) info).tapped);
@ -344,8 +350,8 @@ public final class ZonesHandler {
// make sure the controller of all continuous effects of this card are switched to the current controller
game.setScopeRelevant(true);
game.getContinuousEffects().setController(permanent.getId(), permanent.getControllerId());
if (permanent.entersBattlefield(event.getSourceId(), game, fromZone, true)
&& card.removeFromZone(game, fromZone, event.getSourceId())) {
if (permanent.entersBattlefield(source, game, fromZone, true)
&& card.removeFromZone(game, fromZone, source)) {
success = true;
event.setTarget(permanent);
} else {
@ -357,11 +363,11 @@ public final class ZonesHandler {
} else if (event.getTarget() != null) {
card.setFaceDown(info.faceDown, game);
Permanent target = event.getTarget();
success = game.getPlayer(target.getControllerId()).removeFromBattlefield(target, game)
&& target.removeFromZone(game, fromZone, event.getSourceId());
success = game.getPlayer(target.getControllerId()).removeFromBattlefield(target, source, game)
&& target.removeFromZone(game, fromZone, source);
} else {
card.setFaceDown(info.faceDown, game);
success = card.removeFromZone(game, fromZone, event.getSourceId());
success = card.removeFromZone(game, fromZone, source);
}
}
if (success) {

View file

@ -23,6 +23,9 @@ import mage.filter.predicate.mageobject.NamePredicate;
import mage.filter.predicate.permanent.AttackingSameNotBandedPredicate;
import mage.filter.predicate.permanent.PermanentIdPredicate;
import mage.game.Game;
import mage.game.events.AttackerDeclaredEvent;
import mage.game.events.DeclareAttackerEvent;
import mage.game.events.DeclareBlockerEvent;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import mage.game.permanent.Permanent;
@ -272,13 +275,13 @@ public class Combat implements Serializable, Copyable<Combat> {
Permanent attackingPermanent = game.getPermanent(attacker);
if (attackingPermanent != null) {
attackingPermanent.setTapped(false);
attackingPermanent.tap(true, game); // to tap with event finally here is needed to prevent abusing of Vampire Envoy like cards
attackingPermanent.tap(true, null, game); // to tap with event finally here is needed to prevent abusing of Vampire Envoy like cards
}
}
handleBanding(attacker, game);
// This can only be used to modify the event, the attack can't be replaced here
game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.ATTACKER_DECLARED, group.defenderId, attacker, attackingPlayerId));
game.addSimultaneousEvent(GameEvent.getEvent(GameEvent.EventType.ATTACKER_DECLARED, group.defenderId, attacker, attackingPlayerId));
game.replaceEvent(new AttackerDeclaredEvent(group.defenderId, attacker, attackingPlayerId));
game.addSimultaneousEvent(new AttackerDeclaredEvent(group.defenderId, attacker, attackingPlayerId));
}
}
attackersTappedByAttack.clear();
@ -452,8 +455,7 @@ public class Combat implements Serializable, Copyable<Combat> {
Set<UUID> defendersCostlessAttackable = new HashSet<>(defenders);
for (UUID defenderId : defenders) {
if (game.getContinuousEffects().checkIfThereArePayCostToAttackBlockEffects(
GameEvent.getEvent(GameEvent.EventType.DECLARE_ATTACKER,
defenderId, creature.getId(), creature.getControllerId()), game)) {
new DeclareAttackerEvent(defenderId, creature.getId(), creature.getControllerId()), game)) {
defendersCostlessAttackable.remove(defenderId);
defendersForcedToAttack.remove(defenderId);
}
@ -550,13 +552,13 @@ public class Combat implements Serializable, Copyable<Combat> {
public void selectBlockers(Game game) {
if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.DECLARING_BLOCKERS, attackingPlayerId, attackingPlayerId))) {
game.getCombat().selectBlockers(null, game);
game.getCombat().selectBlockers(null, null, game);
}
for (UUID attackingCreatureID : game.getCombat().getAttackers()) {
Permanent permanent = game.getPermanent(attackingCreatureID);
CombatGroup group = game.getCombat().findGroup(attackingCreatureID);
if (permanent != null && group != null && !group.getBlocked()) {
game.fireEvent(GameEvent.getEvent(EventType.UNBLOCKED_ATTACKER, attackingCreatureID, attackingPlayerId));
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.UNBLOCKED_ATTACKER, attackingCreatureID, attackingPlayerId));
}
}
}
@ -568,7 +570,7 @@ public class Combat implements Serializable, Copyable<Combat> {
* defender is the controller
* @param game
*/
public void selectBlockers(Player blockController, Game game) {
public void selectBlockers(Player blockController, Ability source, Game game) {
Player attacker = game.getPlayer(attackingPlayerId);
//20101001 - 509.1c
game.getCombat().retrieveMustBlockAttackerRequirements(attacker, game);
@ -583,7 +585,7 @@ public class Combat implements Serializable, Copyable<Combat> {
controller = blockController;
}
while (choose) {
controller.selectBlockers(game, defenderId);
controller.selectBlockers(source, game, defenderId);
if (game.isPaused() || game.checkIfGameIsOver() || game.executingRollback()) {
return;
}
@ -719,7 +721,7 @@ public class Combat implements Serializable, Copyable<Combat> {
}
// check if the possible blocker has to pay cost to block, if so don't force
if (game.getContinuousEffects().checkIfThereArePayCostToAttackBlockEffects(
GameEvent.getEvent(GameEvent.EventType.DECLARE_BLOCKER, attackingCreatureId, possibleBlocker.getId(), possibleBlocker.getControllerId()), game)) {
new DeclareBlockerEvent(attackingCreatureId, possibleBlocker.getId(), possibleBlocker.getControllerId()), game)) {
// has cost to block to pay so remove this attacker
continue;
}
@ -1291,7 +1293,7 @@ public class Combat implements Serializable, Copyable<Combat> {
public boolean declareAttacker(UUID creatureId, UUID defenderId, UUID playerId, Game game) {
Permanent attacker = game.getPermanent(creatureId);
if (attacker != null) {
if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.DECLARE_ATTACKER, defenderId, creatureId, playerId))) {
if (!game.replaceEvent(new DeclareAttackerEvent(defenderId, creatureId, playerId))) {
if (addAttackerToCombat(creatureId, defenderId, game)) {
if (!attacker.hasAbility(VigilanceAbility.getInstance(), game)
&& !attacker.hasAbility(JohanVigilanceAbility.getInstance(), game)) {

View file

@ -10,6 +10,8 @@ import mage.constants.Outcome;
import mage.filter.StaticFilters;
import mage.filter.common.FilterCreaturePermanent;
import mage.game.Game;
import mage.game.events.BlockerDeclaredEvent;
import mage.game.events.DeclareBlockerEvent;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.players.Player;
@ -266,23 +268,23 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
if (hasTrample(attacker)) {
int lethalDamage = getLethalDamage(blocker, attacker, game);
if (lethalDamage >= damage) {
blocker.markDamage(damage, attacker.getId(), game, true, true);
blocker.markDamage(damage, attacker.getId(), null, game, true, true);
} else {
int damageAssigned = player.getAmount(lethalDamage, damage, "Assign damage to " + blocker.getName(), game);
blocker.markDamage(damageAssigned, attacker.getId(), game, true, true);
blocker.markDamage(damageAssigned, attacker.getId(), null, game, true, true);
damage -= damageAssigned;
if (damage > 0) {
defenderDamage(attacker, damage, game);
}
}
} else {
blocker.markDamage(damage, attacker.getId(), game, true, true);
blocker.markDamage(damage, attacker.getId(), null, game, true, true);
}
}
if (canDamage(blocker, first)) {
if (checkSoleBlockerAfter(blocker, game)) { // blocking several creatures handled separately
if (!assignsDefendingPlayerAndOrDefendingCreaturesDividedDamage(blocker, blocker.getControllerId(), first, game, false)) {
attacker.markDamage(blockerDamage, blocker.getId(), game, true, true);
attacker.markDamage(blockerDamage, blocker.getId(), null, game, true, true);
}
}
}
@ -349,20 +351,20 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
// might be missing canDamage condition?
Permanent blocker = game.getPermanent(blockerId);
if (!assignsDefendingPlayerAndOrDefendingCreaturesDividedDamage(blocker, blocker.getControllerId(), first, game, false)) {
attacker.markDamage(power, blockerId, game, true, true);
attacker.markDamage(power, blockerId, null, game, true, true);
}
}
}
for (Map.Entry<UUID, Integer> entry : assigned.entrySet()) {
Permanent blocker = game.getPermanent(entry.getKey());
blocker.markDamage(entry.getValue(), attacker.getId(), game, true, true);
blocker.markDamage(entry.getValue(), attacker.getId(), null, game, true, true);
}
} else {
for (UUID blockerId : blockerOrder) {
Permanent blocker = game.getPermanent(blockerId);
if (canDamage(blocker, first)) {
if (!assignsDefendingPlayerAndOrDefendingCreaturesDividedDamage(blocker, blocker.getControllerId(), first, game, false)) {
attacker.markDamage(getDamageValueFromPermanent(blocker, game), blocker.getId(), game, true, true);
attacker.markDamage(getDamageValueFromPermanent(blocker, game), blocker.getId(), null, game, true, true);
}
}
}
@ -402,7 +404,7 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
if (damage > 0) {
Player defendingPlayer = game.getPlayer(defendingPlayerId);
if (defendingPlayer != null) {
defendingPlayer.damage(damage, attacker.getId(), game, true, true);
defendingPlayer.damage(damage, attacker.getId(), null, game, true, true);
}
}
if (isAttacking) {
@ -412,14 +414,14 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
// might be missing canDamage condition?
Permanent blocker = game.getPermanent(blockerId);
if (!assignsDefendingPlayerAndOrDefendingCreaturesDividedDamage(blocker, blocker.getControllerId(), first, game, false)) {
attacker.markDamage(power, blockerId, game, true, true);
attacker.markDamage(power, blockerId, null, game, true, true);
}
}
}
}
for (Map.Entry<UUID, Integer> entry : assigned.entrySet()) {
Permanent defendingCreature = game.getPermanent(entry.getKey());
defendingCreature.markDamage(entry.getValue(), attacker.getId(), game, true, true);
defendingCreature.markDamage(entry.getValue(), attacker.getId(), null, game, true, true);
}
} else {
if (isAttacking) {
@ -427,7 +429,7 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
Permanent blocker = game.getPermanent(blockerId);
if (canDamage(blocker, first)) {
if (!assignsDefendingPlayerAndOrDefendingCreaturesDividedDamage(blocker, blocker.getControllerId(), first, game, false)) {
attacker.markDamage(getDamageValueFromPermanent(blocker, game), blocker.getId(), game, true, true);
attacker.markDamage(getDamageValueFromPermanent(blocker, game), blocker.getId(), null, game, true, true);
}
}
}
@ -475,7 +477,7 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
if (blocker != null && attacker != null) {
if (canDamage(blocker, first)) {
int damage = getDamageValueFromPermanent(blocker, game);
attacker.markDamage(damage, blocker.getId(), game, true, true);
attacker.markDamage(damage, blocker.getId(), null, game, true, true);
}
}
}
@ -531,7 +533,7 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
for (Map.Entry<UUID, Integer> entry : assigned.entrySet()) {
Permanent attacker = game.getPermanent(entry.getKey());
attacker.markDamage(entry.getValue(), blocker.getId(), game, true, true);
attacker.markDamage(entry.getValue(), blocker.getId(), null, game, true, true);
}
}
}
@ -540,12 +542,12 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
if (this.defenderIsPlaneswalker) {
Permanent defender = game.getPermanent(defenderId);
if (defender != null) {
defender.markDamage(amount, attacker.getId(), game, true, true);
defender.markDamage(amount, attacker.getId(), null, game, true, true);
}
} else {
Player defender = game.getPlayer(defenderId);
if (defender != null) {
defender.damage(amount, attacker.getId(), game, true, true);
defender.damage(amount, attacker.getId(), null, game, true, true);
}
}
}
@ -570,7 +572,7 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
*/
public void addBlocker(UUID blockerId, UUID playerId, Game game) {
for (UUID attackerId : attackers) {
if (game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.DECLARE_BLOCKER, attackerId, blockerId, playerId))) {
if (game.replaceEvent(new DeclareBlockerEvent(attackerId, blockerId, playerId))) {
return;
}
}
@ -690,7 +692,7 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
}
for (UUID blockerId : blockers) {
for (UUID attackerId : attackers) {
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.BLOCKER_DECLARED, attackerId, blockerId, players.get(blockerId)));
game.fireEvent(new BlockerDeclaredEvent(attackerId, blockerId, players.get(blockerId)));
}
}

View file

@ -54,7 +54,7 @@ class DackFaydenEmblemTriggeredAbility extends TriggeredAbilityImpl {
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == EventType.SPELL_CAST;
return event.getType() == GameEvent.EventType.SPELL_CAST;
}
@Override
@ -137,7 +137,7 @@ class DackFaydenEmblemEffect extends ContinuousEffectImpl {
for (UUID permanentId : fixedTargets.getTargets(game, source)) {
Permanent permanent = game.getPermanent(permanentId);
if (permanent != null) {
permanent.changeControllerId(source.getControllerId(), game);
permanent.changeControllerId(source.getControllerId(), game, source);
}
}
return true;

View file

@ -50,7 +50,7 @@ class DarettiScrapSavantTriggeredAbility extends TriggeredAbilityImpl {
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == EventType.ZONE_CHANGE;
return event.getType() == GameEvent.EventType.ZONE_CHANGE;
}
@Override

View file

@ -103,8 +103,7 @@ class JayaBallardReplacementEffect extends ReplacementEffectImpl {
if (controller != null) {
Card card = (Card) game.getState().getValue("JayaBallard");
if (card != null) {
controller.moveCardToExileWithInfo(card, null, "", source.getSourceId(), game, Zone.STACK, true);
return true;
((ZoneChangeEvent) event).setToZone(Zone.EXILED);
}
}
return false;
@ -112,7 +111,7 @@ class JayaBallardReplacementEffect extends ReplacementEffectImpl {
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == EventType.ZONE_CHANGE;
return event.getType() == GameEvent.EventType.ZONE_CHANGE;
}
@Override

View file

@ -89,7 +89,7 @@ class MomirEffect extends OneShotEffect {
}
}
if (token != null) {
token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId(), false, false);
token.putOntoBattlefield(1, game, source, source.getControllerId(), false, false);
return true;
}

View file

@ -36,7 +36,7 @@ class ObNixilisEmblemTriggeredAbility extends TriggeredAbilityImpl {
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == EventType.DREW_CARD;
return event.getType() == GameEvent.EventType.DREW_CARD;
}
@Override

View file

@ -56,7 +56,7 @@ class VenserTheSojournerSpellCastTriggeredAbility extends TriggeredAbilityImpl {
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == EventType.SPELL_CAST;
return event.getType() == GameEvent.EventType.SPELL_CAST;
}
@Override

View file

@ -94,7 +94,7 @@ class DrawCardsActivePlayerEffect extends OneShotEffect {
}
Player player = game.getPlayer(game.getActivePlayerId());
if (player != null) {
player.drawCards(amount.calculate(game, source, this), source.getSourceId(), game);
player.drawCards(amount.calculate(game, source, this), source, game);
return true;
}
return false;

View file

@ -84,7 +84,7 @@ class EdgeOfMalacolEffect extends ContinuousRuleModifyingEffectImpl {
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == EventType.UNTAP;
return event.getType() == GameEvent.EventType.UNTAP;
}
@Override

View file

@ -76,7 +76,7 @@ class PanopticonTriggeredAbility extends TriggeredAbilityImpl {
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == EventType.PLANESWALKED;
return event.getType() == GameEvent.EventType.PLANESWALKED;
}
@Override

View file

@ -82,7 +82,7 @@ class UndercityReachesTriggeredAbility extends TriggeredAbilityImpl {
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == EventType.DAMAGED_PLAYER;
return event.getType() == GameEvent.EventType.DAMAGED_PLAYER;
}
@Override

View file

@ -0,0 +1,17 @@
package mage.game.events;
import mage.abilities.Ability;
import mage.game.permanent.Permanent;
import java.util.UUID;
/**
* @author JayDi85
*/
public class AttachEvent extends GameEvent {
public AttachEvent(UUID targetPermanentId, Permanent attachment, Ability source) {
super(GameEvent.EventType.ATTACH, targetPermanentId, null, attachment.getControllerId());
this.setSourceId(attachment.getId());
}
}

View file

@ -0,0 +1,17 @@
package mage.game.events;
import mage.abilities.Ability;
import mage.game.permanent.Permanent;
import java.util.UUID;
/**
* @author JayDi85
*/
public class AttachedEvent extends GameEvent {
public AttachedEvent(UUID targetPermanentId, Permanent attachment, Ability source) {
super(GameEvent.EventType.ATTACHED, targetPermanentId, null, attachment.getControllerId());
this.setSourceId(attachment.getId());
}
}

View file

@ -0,0 +1,14 @@
package mage.game.events;
import java.util.UUID;
/**
* @author JayDi85
*/
public class AttackerDeclaredEvent extends GameEvent {
public AttackerDeclaredEvent(UUID targetId, UUID attackerId, UUID attackerControllerId) {
super(GameEvent.EventType.ATTACKER_DECLARED, targetId, null, attackerControllerId);
this.setSourceId(attackerId);
}
}

View file

@ -0,0 +1,14 @@
package mage.game.events;
import java.util.UUID;
/**
* @author JayDi85
*/
public class BlockerDeclaredEvent extends GameEvent {
public BlockerDeclaredEvent(UUID attackerId, UUID blockerId, UUID blockerControllerId) {
super(GameEvent.EventType.BLOCKER_DECLARED, attackerId, null, blockerControllerId);
this.setSourceId(blockerId);
}
}

View file

@ -8,15 +8,17 @@ import java.util.UUID;
* @author TheElk801
**/
public class CoinFlippedEvent extends GameEvent {
private final boolean result;
private final boolean chosen;
private final boolean winnable;
CoinFlippedEvent(UUID playerId, UUID sourceId, boolean result, boolean chosen, boolean winnable) {
super(EventType.COIN_FLIPPED, playerId, sourceId, playerId);
super(GameEvent.EventType.COIN_FLIPPED, playerId, null, playerId);
this.result = result;
this.chosen = chosen;
this.winnable = winnable;
this.setSourceId(sourceId);
}
public boolean getResult() {

View file

@ -0,0 +1,16 @@
package mage.game.events;
import mage.MageObject;
import java.util.UUID;
/**
* @author JayDi85
*/
public class CopiedStackObjectEvent extends GameEvent {
public CopiedStackObjectEvent(MageObject target, MageObject newCopy, UUID newControllerId) {
super(GameEvent.EventType.COPIED_STACKOBJECT, newCopy.getId(), null, newControllerId);
this.setSourceId(target.getId());
}
}

View file

@ -0,0 +1,16 @@
package mage.game.events;
import mage.MageObject;
import mage.abilities.Ability;
import java.util.UUID;
/**
* @author JayDi85
*/
public class CopyStackObjectEvent extends GameEvent {
public CopyStackObjectEvent(Ability source, MageObject targetToCopy, UUID newControllerId, int amount) {
super(GameEvent.EventType.COPY_STACKOBJECT, targetToCopy.getId(), source, newControllerId, amount, false);
}
}

View file

@ -1,28 +0,0 @@
package mage.game.events;
import java.util.UUID;
import mage.abilities.costs.Cost;
/**
*
* @author LevelX2
*/
public class CostEvent extends GameEvent {
protected Cost cost;
public CostEvent(EventType type, UUID targetId, UUID sourceId, UUID playerId, Cost cost) {
super(type, targetId, sourceId, playerId);
this.cost = cost;
}
public Cost getCost() {
return cost;
}
public void setCost(Cost cost) {
this.cost = cost;
}
}

View file

@ -1,14 +1,16 @@
package mage.game.events;
import mage.abilities.Ability;
import mage.game.permanent.token.Token;
import java.util.UUID;
public class CreateTokenEvent extends GameEvent {
private Token token;
public CreateTokenEvent(UUID sourceId, UUID controllerId, int amount, Token token) {
super(EventType.CREATE_TOKEN, null, sourceId, controllerId, amount, false);
public CreateTokenEvent(Ability source, UUID controllerId, int amount, Token token) {
super(GameEvent.EventType.CREATE_TOKEN, null, source, controllerId, amount, false);
this.token = token;
}

View file

@ -1,12 +1,11 @@
package mage.game.events;
import mage.abilities.Ability;
import mage.game.permanent.PermanentToken;
import java.util.UUID;
public class CreatedTokenEvent extends GameEvent {
public CreatedTokenEvent(UUID sourceId, PermanentToken tokenPerm) {
super(EventType.CREATED_TOKEN, tokenPerm.getId(), sourceId, tokenPerm.getControllerId());
public CreatedTokenEvent(Ability source, PermanentToken tokenPerm) {
super(GameEvent.EventType.CREATED_TOKEN, tokenPerm.getId(), source, tokenPerm.getControllerId());
}
}

View file

@ -1,5 +1,3 @@
package mage.game.events;
import java.util.UUID;
@ -10,8 +8,7 @@ import java.util.UUID;
*/
public class DamageCreatureEvent extends DamageEvent {
public DamageCreatureEvent(UUID targetId, UUID sourceId, UUID playerId, int amount, boolean preventable, boolean combat) {
super(EventType.DAMAGE_CREATURE, targetId, sourceId, playerId, amount, preventable, combat);
public DamageCreatureEvent(UUID targetId, UUID damageSourceId, UUID targetControllerId, int amount, boolean preventable, boolean combat) {
super(GameEvent.EventType.DAMAGE_CREATURE, targetId, damageSourceId, targetControllerId, amount, preventable, combat);
}
}

View file

@ -1,5 +1,3 @@
package mage.game.events;
import java.util.UUID;
@ -12,9 +10,10 @@ public abstract class DamageEvent extends GameEvent {
protected boolean combat;
public DamageEvent(EventType type, UUID targetId, UUID sourceId, UUID playerId, int amount, boolean preventable, boolean combat) {
super(type, targetId, sourceId, playerId, amount, preventable);
public DamageEvent(EventType type, UUID targetId, UUID damageSourceId, UUID targetControllerId, int amount, boolean preventable, boolean combat) {
super(type, targetId, null, targetControllerId, amount, preventable);
this.combat = combat;
this.setSourceId(damageSourceId);
}
public boolean isCombatDamage() {

View file

@ -1,5 +1,3 @@
package mage.game.events;
import java.util.UUID;
@ -10,8 +8,7 @@ import java.util.UUID;
*/
public class DamagePlaneswalkerEvent extends DamageEvent {
public DamagePlaneswalkerEvent(UUID targetId, UUID sourceId, UUID playerId, int amount, boolean preventable, boolean combat) {
super(EventType.DAMAGE_PLANESWALKER, targetId, sourceId, playerId, amount, preventable, combat);
public DamagePlaneswalkerEvent(UUID targetId, UUID damageSourceId, UUID targetControllerId, int amount, boolean preventable, boolean combat) {
super(GameEvent.EventType.DAMAGE_PLANESWALKER, targetId, damageSourceId, targetControllerId, amount, preventable, combat);
}
}

View file

@ -1,5 +1,3 @@
package mage.game.events;
import java.util.UUID;
@ -10,8 +8,7 @@ import java.util.UUID;
*/
public class DamagePlayerEvent extends DamageEvent {
public DamagePlayerEvent(UUID targetId, UUID sourceId, UUID playerId, int amount, boolean preventable, boolean combat) {
super(EventType.DAMAGE_PLAYER, targetId, sourceId, playerId, amount, preventable, combat);
public DamagePlayerEvent(UUID targetId, UUID damageSourceId, UUID targetControllerId, int amount, boolean preventable, boolean combat) {
super(GameEvent.EventType.DAMAGE_PLAYER, targetId, damageSourceId, targetControllerId, amount, preventable, combat);
}
}

View file

@ -12,7 +12,7 @@ public abstract class DamagedBatchEvent extends GameEvent {
private final Set<DamagedEvent> events = new HashSet<>();
public DamagedBatchEvent(EventType type, Class<? extends DamagedEvent> damageClazz) {
super(EventType.DAMAGED_PLAYER_BATCH, null, null, null);
super(GameEvent.EventType.DAMAGED_PLAYER_BATCH, null, null, null);
this.damageClazz = damageClazz;
}

View file

@ -6,6 +6,6 @@ package mage.game.events;
public class DamagedCreatureBatchEvent extends DamagedBatchEvent {
public DamagedCreatureBatchEvent() {
super(EventType.DAMAGED_CREATURE_BATCH, DamagedCreatureEvent.class);
super(GameEvent.EventType.DAMAGED_CREATURE_BATCH, DamagedCreatureEvent.class);
}
}

View file

@ -10,8 +10,7 @@ import java.util.UUID;
*/
public class DamagedCreatureEvent extends DamagedEvent {
public DamagedCreatureEvent(UUID targetId, UUID sourceId, UUID playerId, int amount, boolean combat) {
super(EventType.DAMAGED_CREATURE, targetId, sourceId, playerId, amount, combat);
public DamagedCreatureEvent(UUID targetId, UUID attackerId, UUID playerId, int amount, boolean combat) {
super(GameEvent.EventType.DAMAGED_CREATURE, targetId, attackerId, playerId, amount, combat);
}
}

View file

@ -12,9 +12,10 @@ public abstract class DamagedEvent extends GameEvent {
protected boolean combat;
public DamagedEvent(EventType type, UUID targetId, UUID sourceId, UUID playerId, int amount, boolean combat) {
super(type, targetId, sourceId, playerId, amount, false);
public DamagedEvent(EventType type, UUID targetId, UUID attackerId, UUID playerId, int amount, boolean combat) {
super(type, targetId, null, playerId, amount, false);
this.combat = combat;
this.setSourceId(attackerId);
}
public boolean isCombatDamage() {

View file

@ -6,6 +6,6 @@ package mage.game.events;
public class DamagedPlaneswalkerBatchEvent extends DamagedBatchEvent {
public DamagedPlaneswalkerBatchEvent() {
super(EventType.DAMAGED_PLANESWALKER_BATCH, DamagedPlaneswalkerEvent.class);
super(GameEvent.EventType.DAMAGED_PLANESWALKER_BATCH, DamagedPlaneswalkerEvent.class);
}
}

View file

@ -1,5 +1,3 @@
package mage.game.events;
import java.util.UUID;
@ -10,8 +8,9 @@ import java.util.UUID;
*/
public class DamagedPlaneswalkerEvent extends DamagedEvent {
public DamagedPlaneswalkerEvent(UUID targetId, UUID sourceId, UUID playerId, int amount, boolean combat) {
super(EventType.DAMAGED_PLANESWALKER, targetId, sourceId, playerId, amount, combat);
public DamagedPlaneswalkerEvent(UUID targetId, UUID attackerId, UUID playerId, int amount, boolean combat) {
super(GameEvent.EventType.DAMAGED_PLANESWALKER, targetId, null, playerId, amount, combat);
this.setSourceId(attackerId);
}
}

View file

@ -6,6 +6,6 @@ package mage.game.events;
public class DamagedPlayerBatchEvent extends DamagedBatchEvent {
public DamagedPlayerBatchEvent() {
super(EventType.DAMAGED_PLAYER_BATCH, DamagedPlayerEvent.class);
super(GameEvent.EventType.DAMAGED_PLAYER_BATCH, DamagedPlayerEvent.class);
}
}

View file

@ -10,8 +10,7 @@ import java.util.UUID;
*/
public class DamagedPlayerEvent extends DamagedEvent {
public DamagedPlayerEvent(UUID targetId, UUID sourceId, UUID playerId, int amount, boolean combat) {
super(EventType.DAMAGED_PLAYER, targetId, sourceId, playerId, amount, combat);
public DamagedPlayerEvent(UUID targetId, UUID attackerId, UUID playerId, int amount, boolean combat) {
super(GameEvent.EventType.DAMAGED_PLAYER, targetId, attackerId, playerId, amount, combat);
}
}

View file

@ -0,0 +1,14 @@
package mage.game.events;
import java.util.UUID;
/**
* @author JayDi85
*/
public class DeclareAttackerEvent extends GameEvent {
public DeclareAttackerEvent(UUID targetId, UUID attackerId, UUID attackerControllerId) {
super(GameEvent.EventType.DECLARE_ATTACKER, targetId, null, attackerControllerId);
this.setSourceId(attackerId);
}
}

View file

@ -0,0 +1,14 @@
package mage.game.events;
import java.util.UUID;
/**
* @author JayDi85
*/
public class DeclareBlockerEvent extends GameEvent {
public DeclareBlockerEvent(UUID attackerId, UUID blockerId, UUID blockerControllerId) {
super(GameEvent.EventType.DECLARE_BLOCKER, attackerId, null, blockerControllerId);
this.setSourceId(blockerId);
}
}

View file

@ -0,0 +1,21 @@
package mage.game.events;
import mage.abilities.Ability;
import java.util.UUID;
/**
* @author JayDi85
*/
public class DrawCardEvent extends GameEvent {
public DrawCardEvent(UUID playerId, Ability source, GameEvent originalDrawEvent) {
super(GameEvent.EventType.DRAW_CARD, playerId, source, playerId, 0, false);
// source of draw events must be kept between replacements, example: UnpredictableCycloneTest
if (originalDrawEvent != null) {
this.addAppliedEffects(originalDrawEvent.getAppliedEffects());
this.setSourceId(originalDrawEvent.getSourceId());
}
}
}

View file

@ -0,0 +1,21 @@
package mage.game.events;
import mage.abilities.Ability;
import java.util.UUID;
/**
* @author JayDi85
*/
public class DrawCardsEvent extends GameEvent {
public DrawCardsEvent(UUID playerId, Ability source, GameEvent originalDrawEvent, int amount) {
super(GameEvent.EventType.DRAW_CARDS, playerId, source, playerId, amount, false);
// source of draw events must be kept between replacements, example: UnpredictableCycloneTest
if (originalDrawEvent != null) {
this.addAppliedEffects(originalDrawEvent.getAppliedEffects());
this.setSourceId(originalDrawEvent.getSourceId());
}
}
}

View file

@ -0,0 +1,21 @@
package mage.game.events;
import mage.abilities.Ability;
import java.util.UUID;
/**
* @author JayDi85
*/
public class DrewCardEvent extends GameEvent {
public DrewCardEvent(UUID cardId, UUID playerId, Ability source, GameEvent originalDrawEvent) {
super(EventType.DREW_CARD, cardId, source, playerId, 0, false);
// source of draw events must be kept between replacements, example: UnpredictableCycloneTest
if (originalDrawEvent != null) {
//this.addAppliedEffects(originalDrawEvent.getAppliedEffects()); // event can't used for replace, so no needs in applied effects
this.setSourceId(originalDrawEvent.getSourceId());
}
}
}

View file

@ -0,0 +1,17 @@
package mage.game.events;
import mage.abilities.Ability;
import mage.game.permanent.Permanent;
import java.util.UUID;
/**
* @author JayDi85
*/
public class EnchantPlayerEvent extends GameEvent {
public EnchantPlayerEvent(UUID targetId, Permanent attachment, Ability source) {
super(GameEvent.EventType.ENCHANT_PLAYER, targetId, null, attachment.getControllerId());
this.setSourceId(attachment.getId());
}
}

View file

@ -0,0 +1,17 @@
package mage.game.events;
import mage.abilities.Ability;
import mage.game.permanent.Permanent;
import java.util.UUID;
/**
* @author JayDi85
*/
public class EnchantedPlayerEvent extends GameEvent {
public EnchantedPlayerEvent(UUID targetId, Permanent attachment, Ability source) {
super(GameEvent.EventType.ENCHANTED_PLAYER, targetId, null, attachment.getControllerId());
this.setSourceId(attachment.getId());
}
}

View file

@ -2,6 +2,8 @@
package mage.game.events;
import java.util.UUID;
import mage.abilities.Ability;
import mage.constants.EnterEventType;
import static mage.constants.EnterEventType.SELF;
import mage.constants.Zone;
@ -16,12 +18,20 @@ public class EntersTheBattlefieldEvent extends GameEvent {
private final Zone fromZone;
private Permanent target;
public EntersTheBattlefieldEvent(Permanent target, UUID sourceId, UUID playerId, Zone fromZone) {
this(target, sourceId, playerId, fromZone, EnterEventType.OTHER);
public EntersTheBattlefieldEvent(Permanent target, Ability source, UUID playerId, Zone fromZone) {
this(target, source, playerId, fromZone, EnterEventType.OTHER);
}
public EntersTheBattlefieldEvent(Permanent target, UUID sourceId, UUID playerId, Zone fromZone, EnterEventType enterType) {
super(EventType.ENTERS_THE_BATTLEFIELD, target.getId(), sourceId, playerId);
/**
*
* @param target
* @param source can be null for default game actions like cheats
* @param playerId
* @param fromZone
* @param enterType
*/
public EntersTheBattlefieldEvent(Permanent target, Ability source, UUID playerId, Zone fromZone, EnterEventType enterType) {
super(GameEvent.EventType.ENTERS_THE_BATTLEFIELD, target.getId(), source, playerId);
switch (enterType) {
case SELF:
type = EventType.ENTERS_THE_BATTLEFIELD_SELF;

View file

@ -1,5 +1,6 @@
package mage.game.events;
import mage.abilities.Ability;
import mage.util.CardUtil;
import java.util.UUID;
@ -13,8 +14,8 @@ public class FlipCoinEvent extends GameEvent {
private final boolean winnable;
private int flipCount = 1;
public FlipCoinEvent(UUID playerId, UUID sourceId, boolean result, boolean chosen, boolean winnable) {
super(EventType.FLIP_COIN, playerId, sourceId, playerId);
public FlipCoinEvent(UUID playerId, Ability source, boolean result, boolean chosen, boolean winnable) {
super(GameEvent.EventType.FLIP_COIN, playerId, source, playerId);
this.result = result;
this.chosen = chosen;
this.winnable = winnable;
@ -52,7 +53,7 @@ public class FlipCoinEvent extends GameEvent {
this.flipCount = flipCount;
}
public CoinFlippedEvent getFlippedEvent() {
public CoinFlippedEvent createFlippedEvent() {
return new CoinFlippedEvent(playerId, sourceId, result, chosen, winnable);
}
}

View file

@ -2,6 +2,7 @@ package mage.game.events;
import mage.ApprovingObject;
import mage.MageIdentifier;
import mage.abilities.Ability;
import mage.constants.Zone;
import java.io.Serializable;
@ -17,7 +18,7 @@ public class GameEvent implements Serializable {
protected EventType type;
protected UUID id;
protected UUID targetId;
protected UUID sourceId;
protected UUID sourceId; // TODO: check sourceId usage in all events, it must gets sourceId from source ability only, not other values
protected UUID playerId;
protected int amount;
// flags:
@ -25,6 +26,7 @@ public class GameEvent implements Serializable {
// for combat damage: event is preventable damage
// for discard: event is result of effect (1) or result of cost (0)
// for prevent damage: try to prevent combat damage (1) or other damage (0)
// for tapped: is it tapped for combat (1) or for another reason (0)
protected boolean flag;
protected String data;
protected Zone zone;
@ -70,11 +72,16 @@ public class GameEvent implements Serializable {
*/
ZONE_CHANGE,
ZONE_CHANGE_GROUP,
DRAW_CARDS, // applies to an instruction to draw more than one card before any replacement effects apply to individual cards drawn
DRAW_CARDS, // event calls for multi draws only (if player draws 2+ cards at once)
DRAW_CARD, DREW_CARD,
EXPLORED,
ECHO_PAID,
MIRACLE_CARD_REVEALED,
/* MADNESS_CARD_EXILED,
targetId id of the card with madness
sourceId original id of the madness ability
playerId controller of the card
*/
MADNESS_CARD_EXILED,
INVESTIGATED,
KICKED,
@ -84,6 +91,9 @@ public class GameEvent implements Serializable {
playerId controller of the convoked spell
*/
CONVOKED,
/* DISCARD_CARD
flag event is result of effect (1) or result of cost (0)
*/
DISCARD_CARD,
DISCARDED_CARD,
DISCARDED_CARDS,
@ -101,13 +111,20 @@ public class GameEvent implements Serializable {
*/
DAMAGED_PLAYER,
DAMAGED_PLAYER_BATCH,
/* DAMAGE_CAUSES_LIFE_LOSS,
targetId the id of the damaged player
sourceId sourceId of the ability which caused the damage, can be null for default events like combat
playerId the id of the damged player
amount amount of damage
flag is it combat damage
*/
DAMAGE_CAUSES_LIFE_LOSS,
PLAYER_LIFE_CHANGE,
GAIN_LIFE, GAINED_LIFE,
LOSE_LIFE, LOST_LIFE,
/* LOSE_LIFE + LOST_LIFE
targetId the id of the player loosing life
sourceId the id of the player loosing life
sourceId sourceId of the ability which caused the lose
playerId the id of the player loosing life
amount amount of life loss
flag true = from comabat damage - other from non combat damage
@ -172,7 +189,19 @@ public class GameEvent implements Serializable {
*/
TRIGGERED_ABILITY,
RESOLVING_ABILITY,
COPY_STACKOBJECT, COPIED_STACKOBJECT,
/* COPY_STACKOBJECT
targetId id of the spell/ability to copy
sourceId id of the object with copy ability
playerId id of the player who will be control new copied spell/ability
amount number on copies
*/
COPY_STACKOBJECT,
/* COPIED_STACKOBJECT, TODO: make same logic in params for COPY_STACKOBJECT and COPIED_STACKOBJECT
targetId id of the new copied spell/ability
sourceId id of the spell/ability to copy
playerId id of the player who will be control new copied spell/ability
*/
COPIED_STACKOBJECT,
/* ADD_MANA
targetId id of the ability that added the mana
sourceId sourceId of the ability that added the mana
@ -192,11 +221,19 @@ public class GameEvent implements Serializable {
sourceId sourceId of the mana source
playerId controller of the ability the mana was paid for
amount not used for this event
flag indicates a special condition
data originalId of the mana producing ability
flag indicates a special condition of mana
data originalId of the mana producing ability as string (converted from UUID)
*/
MANA_PAID,
LOSES, LOST, WINS, DRAW_PLAYER,
/* TARGET
targetId id of the targeting card
sourceId id of the ability's object that try to targeting
playerId player who try to targeting (can be different from source ability's controller)
TODO: BUT there is isLegal(Ability source, Game game) code and it uses only source ability's controller,
so some abilities can be fizzled on resolve cause no legal targets?
amount not used for this event
*/
TARGET, TARGETED,
/* TARGETS_VALID
targetId id of the spell or id of stack ability the targets were set to
@ -239,11 +276,13 @@ public class GameEvent implements Serializable {
*/
DECLARING_BLOCKERS,
DECLARED_BLOCKERS,
/* DECLARING_BLOCKERS
targetId id of the blocking player
sourceId id of the blocking player
DECLARE_BLOCKER,
/* BLOCKER_DECLARED
targetId attacker id
sourceId blocker id
playerId blocker controller id
*/
DECLARE_BLOCKER, BLOCKER_DECLARED,
BLOCKER_DECLARED,
CREATURE_BLOCKED,
BATCH_BLOCK_NONCOMBAT,
UNBLOCKED_ATTACKER,
@ -268,7 +307,15 @@ public class GameEvent implements Serializable {
ENTERS_THE_BATTLEFIELD_CONTROL, // 616.1b
ENTERS_THE_BATTLEFIELD_COPY, // 616.1c
ENTERS_THE_BATTLEFIELD, // 616.1d
TAP, TAPPED,
TAP,
/* TAPPED,
targetId tapped permanent
sourceId id of the abilitity's source (can be null for standard tap actions like combat)
playerId controller of the tapped permanent
amount not used for this event
flag is it tapped for combat
*/
TAPPED,
TAPPED_FOR_MANA,
/* TAPPED_FOR_MANA
During calculation of the available mana for a player the "TappedForMana" event is fired to simulate triggered mana production.
@ -281,14 +328,14 @@ public class GameEvent implements Serializable {
TRANSFORM, TRANSFORMED,
ADAPT,
BECOMES_MONSTROUS,
BECOMES_EXERTED,
/* BECOMES_EXERTED
targetId id of the exerted creature
sourceId id of the exerted creature
playerId playerId of the player that controlls the creature
sourceId sourceId of the ability that triggered the event (do exert)
playerId player who makes the exert (can be different from permanent's controller)
amount not used for this event
flag not used for this event
*/
BECOMES_EXERTED,
BECOMES_RENOWNED,
/* BECOMES_MONARCH
targetId playerId of the player that becomes the monarch
@ -336,21 +383,44 @@ public class GameEvent implements Serializable {
EMBALMED_CREATURE,
ETERNALIZED_CREATURE,
ATTACH, ATTACHED,
STAY_ATTACHED,
UNATTACH, UNATTACHED,
/* ATTACH, ATTACHED,
UNATTACH, UNATTACHED,
targetId id of the permanent who get/lose attachment
sourceId id of the attachment
playerId player who control the attachment
amount not used for this event
flag not used for this event
*/
STAY_ATTACHED,
ADD_COUNTER, COUNTER_ADDED,
ADD_COUNTERS, COUNTERS_ADDED,
COUNTER_REMOVED, COUNTERS_REMOVED,
LOSE_CONTROL,
/* LOST_CONTROL
targetId id of the creature that lost control
sourceId id of the creature that lost control
sourceId null
playerId player that controlles the creature before
amount not used for this event
flag not used for this event
*/
LOST_CONTROL,
GAIN_CONTROL, GAINED_CONTROL,
/* GAIN_CONTROL
targetId id of the permanent that trying to get control
sourceId null
playerId new player that try to get control of permanent
amount not used for this event
flag not used for this event
*/
GAIN_CONTROL,
/* GAINED_CONTROL
targetId id of the permanent that got control
sourceId null
playerId new player that got control of permanent
amount not used for this event
flag not used for this event
*/
GAINED_CONTROL,
CREATE_TOKEN, CREATED_TOKEN,
/* REGENERATE
targetId id of the creature to regenerate
@ -370,79 +440,69 @@ public class GameEvent implements Serializable {
CUSTOM_EVENT
}
public GameEvent(EventType type, UUID targetId, UUID sourceId, UUID playerId) {
this(type, null, targetId, sourceId, playerId, 0, false);
public GameEvent(EventType type, UUID targetId, Ability source, UUID playerId) {
this(type, null, targetId, source, playerId, 0, false);
}
public GameEvent(EventType type, UUID targetId, UUID sourceId, UUID playerId, ApprovingObject approvingObject) {
this(type, null, targetId, sourceId, playerId, 0, false, approvingObject);
public GameEvent(EventType type, UUID targetId, Ability source, UUID playerId, ApprovingObject approvingObject) {
this(type, null, targetId, source, playerId, 0, false, approvingObject);
}
public GameEvent(EventType type, UUID targetId, UUID sourceId, UUID playerId, int amount, boolean flag) {
this(type, null, targetId, sourceId, playerId, amount, flag);
public GameEvent(EventType type, UUID targetId, Ability source, UUID playerId, int amount, boolean flag) {
this(type, null, targetId, source, playerId, amount, flag);
}
public GameEvent(UUID customEventType, UUID targetId, UUID sourceId, UUID playerId) {
this(EventType.CUSTOM_EVENT, customEventType, targetId, sourceId, playerId, 0, false);
public GameEvent(UUID customEventType, UUID targetId, Ability source, UUID playerId) {
this(EventType.CUSTOM_EVENT, customEventType, targetId, source, playerId, 0, false);
}
public GameEvent(UUID customEventType, UUID targetId, UUID sourceId, UUID playerId, int amount, boolean flag) {
this(EventType.CUSTOM_EVENT, customEventType, targetId, sourceId, playerId, amount, flag);
public GameEvent(UUID customEventType, UUID targetId, Ability source, UUID playerId, int amount, boolean flag) {
this(EventType.CUSTOM_EVENT, customEventType, targetId, source, playerId, amount, flag);
}
public static GameEvent getEvent(EventType type, UUID targetId, UUID sourceId, UUID playerId, int amount) {
return new GameEvent(type, targetId, sourceId, playerId, amount, false);
public static GameEvent getEvent(EventType type, UUID targetId, Ability source, UUID playerId, int amount) {
return new GameEvent(type, targetId, source, playerId, amount, false);
}
public static GameEvent getEvent(EventType type, UUID targetId, UUID sourceId, UUID playerId) {
return new GameEvent(type, targetId, sourceId, playerId);
public static GameEvent getEvent(EventType type, UUID targetId, Ability source, UUID playerId) {
return new GameEvent(type, targetId, source, playerId);
}
public static GameEvent getEvent(EventType type, UUID targetId, UUID sourceId, UUID playerId, ApprovingObject approvingObject) {
return new GameEvent(type, targetId, sourceId, playerId, approvingObject);
public static GameEvent getEvent(EventType type, UUID targetId, Ability source, UUID playerId, ApprovingObject approvingObject) {
return new GameEvent(type, targetId, source, playerId, approvingObject);
}
@Deprecated // usage must be replaced by getEvent with source ability
public static GameEvent getEvent(EventType type, UUID targetId, UUID playerId) {
return new GameEvent(type, targetId, null, playerId);
}
public static GameEvent getEvent(EventType type, UUID targetId, UUID sourceId, UUID playerId, String data, int amount) {
GameEvent event = getEvent(type, targetId, sourceId, playerId);
public static GameEvent getEvent(EventType type, UUID targetId, Ability source, UUID playerId, String data, int amount) {
GameEvent event = getEvent(type, targetId, source, playerId);
event.setAmount(amount);
event.setData(data);
return event;
}
public static GameEvent getEvent(UUID customEventType, UUID targetId, UUID sourceId, UUID playerId, int amount) {
return new GameEvent(customEventType, targetId, sourceId, playerId, amount, false);
public static GameEvent getEvent(UUID customEventType, UUID targetId, Ability source, UUID playerId, int amount) {
return new GameEvent(customEventType, targetId, source, playerId, amount, false);
}
public static GameEvent getEvent(UUID customEventType, UUID targetId, UUID sourceId, UUID playerId) {
return new GameEvent(customEventType, targetId, sourceId, playerId);
public static GameEvent getEvent(UUID customEventType, UUID targetId, Ability source, UUID playerId) {
return new GameEvent(customEventType, targetId, source, playerId);
}
public static GameEvent getEvent(UUID customEventType, UUID targetId, UUID playerId) {
return new GameEvent(customEventType, targetId, null, playerId);
}
public static GameEvent getEvent(UUID customEventType, UUID targetId, UUID playerId, String data, int amount) {
GameEvent event = getEvent(customEventType, targetId, playerId);
event.setAmount(amount);
event.setData(data);
return event;
private GameEvent(EventType type, UUID customEventType, UUID targetId, Ability source, UUID playerId, int amount, boolean flag)
{
this(type, customEventType, targetId, source, playerId, amount, flag, null);
}
private GameEvent(EventType type, UUID customEventType,
UUID targetId, UUID sourceId, UUID playerId, int amount, boolean flag) {
this(type, customEventType, targetId, sourceId, playerId, amount, flag, null);
}
private GameEvent(EventType type, UUID customEventType,
UUID targetId, UUID sourceId, UUID playerId, int amount, boolean flag, ApprovingObject approvingObject) {
UUID targetId, Ability source, UUID playerId, int amount, boolean flag, ApprovingObject approvingObject) {
this.type = type;
this.customEventType = customEventType;
this.targetId = targetId;
this.sourceId = sourceId;
this.sourceId = source == null ? null : source.getSourceId();
this.amount = amount;
this.playerId = playerId;
this.flag = flag;
@ -585,4 +645,13 @@ public class GameEvent implements Serializable {
}
return identifier.equals(approvingObject.getApprovingAbility().getIdentifier());
}
/**
* Custom sourceId setup for some events (use it in constructor). TODO: replace all custom sourceId to normal event classes
*
* @param sourceId
*/
protected void setSourceId(UUID sourceId) {
this.sourceId = sourceId;
}
}

View file

@ -1,5 +1,6 @@
package mage.game.events;
import mage.abilities.Ability;
import mage.target.Target;
import java.util.UUID;
@ -15,12 +16,12 @@ public class LibrarySearchedEvent extends GameEvent {
* Searched library event (after library searching finished). Return false on replaceEvent to
*
* @param targetPlayerId whose library searched
* @param sourceId source of the searching effect
* @param source source ability of the searching effect
* @param playerId who must search the library
* @param searchedTarget founded cards (targets list can be changed by replace events, see Opposition Agent)
*/
public LibrarySearchedEvent(UUID targetPlayerId, UUID sourceId, UUID playerId, Target searchedTarget) {
super(EventType.LIBRARY_SEARCHED, targetPlayerId, sourceId, playerId, searchedTarget.getTargets().size(), false);
public LibrarySearchedEvent(UUID targetPlayerId, Ability source, UUID playerId, Target searchedTarget) {
super(GameEvent.EventType.LIBRARY_SEARCHED, targetPlayerId, source, playerId, searchedTarget.getTargets().size(), false);
this.searchedTarget = searchedTarget;
}

View file

@ -0,0 +1,16 @@
package mage.game.events;
import mage.abilities.Ability;
import java.util.UUID;
/**
* @author JayDi85
*/
public class MadnessCardExiledEvent extends GameEvent {
public MadnessCardExiledEvent(UUID cardId, Ability source, UUID controllerId) {
super(GameEvent.EventType.MADNESS_CARD_EXILED, cardId, null, controllerId);
this.setSourceId(source.getOriginalId()); // save ability's id
}
}

View file

@ -4,6 +4,7 @@ package mage.game.events;
import java.util.UUID;
import mage.Mana;
import mage.abilities.Ability;
/**
*
@ -13,8 +14,8 @@ public class ManaEvent extends GameEvent {
protected Mana mana;
public ManaEvent(EventType type, UUID targetId, UUID sourceId, UUID playerId, Mana mana) {
super(type, targetId, sourceId, playerId);
public ManaEvent(EventType type, UUID targetId, Ability source, UUID playerId, Mana mana) {
super(type, targetId, source, playerId);
this.mana = mana;
}

View file

@ -0,0 +1,17 @@
package mage.game.events;
import mage.abilities.Ability;
import java.util.UUID;
/**
* @author JayDi85
*/
public class ManaPaidEvent extends GameEvent {
public ManaPaidEvent(Ability abilityToPay, UUID manaSourceId, boolean manaFlag, UUID manaOriginalId) {
super(GameEvent.EventType.MANA_PAID, abilityToPay.getId(), null, abilityToPay.getControllerId(), 0, manaFlag);
this.setSourceId(manaSourceId);
this.setData(manaOriginalId.toString());
}
}

View file

@ -1,7 +1,7 @@
package mage.game.events;
import mage.abilities.Ability;
import java.util.UUID;
/**
@ -12,8 +12,8 @@ public class NumberOfTriggersEvent extends GameEvent {
private final GameEvent sourceEvent;
public NumberOfTriggersEvent(UUID controllerOfAbilityId, UUID sourceOfTrigger, GameEvent sourceEvent) {
super(EventType.NUMBER_OF_TRIGGERS, null, sourceOfTrigger, controllerOfAbilityId);
public NumberOfTriggersEvent(Ability triggeredAbility, GameEvent sourceEvent) {
super(GameEvent.EventType.NUMBER_OF_TRIGGERS, null, triggeredAbility, triggeredAbility.getControllerId());
this.sourceEvent = sourceEvent;
this.amount = 1; // Number of times to trigger. Panharmonicon can change this.
}

View file

@ -0,0 +1,16 @@
package mage.game.events;
import mage.game.turn.TurnMod;
import java.util.UUID;
/**
* @author JayDi85
*/
public class PhaseChangedEvent extends GameEvent {
public PhaseChangedEvent(UUID playerId, TurnMod extraTurnMode) {
super(GameEvent.EventType.PHASE_CHANGED, playerId, null, playerId);
this.setSourceId(extraTurnMode == null ? null : extraTurnMode.getId());
}
}

View file

@ -1,5 +1,7 @@
package mage.game.events;
import mage.abilities.Ability;
import java.util.UUID;
/**
@ -7,8 +9,9 @@ import java.util.UUID;
*/
public class PreventDamageEvent extends GameEvent {
public PreventDamageEvent(UUID targetId, UUID sourceId, UUID playerId, int damageToPrevent, boolean isCombatDamage) {
super(EventType.PREVENT_DAMAGE, targetId, sourceId, playerId, damageToPrevent, isCombatDamage);
public PreventDamageEvent(UUID targetId, UUID attackerId, Ability source, UUID playerId, int damageToPrevent, boolean isCombatDamage) {
super(GameEvent.EventType.PREVENT_DAMAGE, targetId, null, playerId, damageToPrevent, isCombatDamage);
this.setSourceId(attackerId);
}
public boolean isCombatDamage() {

View file

@ -0,0 +1,16 @@
package mage.game.events;
import mage.abilities.Ability;
import java.util.UUID;
/**
* @author JayDi85
*/
public class PreventedDamageEvent extends GameEvent {
public PreventedDamageEvent(UUID targetId, UUID attackerId, Ability source, UUID playerId, int preventedDamage) {
super(GameEvent.EventType.PREVENTED_DAMAGE, targetId, null, playerId, preventedDamage, false);
this.setSourceId(attackerId);
}
}

View file

@ -1,5 +1,7 @@
package mage.game.events;
import mage.abilities.Ability;
import java.util.UUID;
/**
@ -17,8 +19,8 @@ public class SearchLibraryEvent extends GameEvent {
* @param playerId who must search the library (also see searchingControllerId)
* @param amount cards amount to search
*/
public SearchLibraryEvent(UUID targetPlayerId, UUID sourceId, UUID playerId, int amount) {
super(GameEvent.EventType.SEARCH_LIBRARY, targetPlayerId, sourceId, playerId, amount, false);
public SearchLibraryEvent(UUID targetPlayerId, Ability source, UUID playerId, int amount) {
super(GameEvent.EventType.SEARCH_LIBRARY, targetPlayerId, source, playerId, amount, false);
this.searchingControllerId = playerId;
}

View file

@ -0,0 +1,18 @@
package mage.game.events;
import mage.abilities.Ability;
import mage.game.permanent.Permanent;
import java.util.UUID;
/**
*
* @author JayDi85
*/
public class StayAttachedEvent extends GameEvent {
public StayAttachedEvent(UUID targetId, UUID attachmentId, Ability source) {
super(GameEvent.EventType.STAY_ATTACHED, targetId, null, null);
this.setSourceId(attachmentId);
}
}

View file

@ -0,0 +1,30 @@
package mage.game.events;
import mage.abilities.Ability;
import mage.cards.Card;
import java.util.UUID;
/**
* @author JayDi85
*/
public class TargetEvent extends GameEvent {
/**
* @param target
* @param sourceId
* @param sourceControllerId can be different from real controller (example: ability instructs another player to targeting)
*/
public TargetEvent(Card target, UUID sourceId, UUID sourceControllerId) {
super(GameEvent.EventType.TARGET, target.getId(), null, sourceControllerId);
this.setSourceId(sourceId);
}
/**
* @param targetId
* @param source
*/
public TargetEvent(UUID targetId, Ability source) {
super(GameEvent.EventType.TARGET, targetId, source, source.getControllerId());
}
}

View file

@ -0,0 +1,17 @@
package mage.game.events;
import mage.abilities.Ability;
import mage.game.permanent.Permanent;
import java.util.UUID;
/**
* @author JayDi85
*/
public class UnattachEvent extends GameEvent {
public UnattachEvent(UUID targetId, UUID attachmentId, Permanent attachment, Ability source) {
super(GameEvent.EventType.UNATTACH, targetId, null, attachment == null ? null : attachment.getControllerId());
this.setSourceId(attachmentId);
}
}

View file

@ -0,0 +1,17 @@
package mage.game.events;
import mage.abilities.Ability;
import mage.game.permanent.Permanent;
import java.util.UUID;
/**
* @author JayDi85
*/
public class UnattachedEvent extends GameEvent {
public UnattachedEvent(UUID targetId, UUID attachmentId, Permanent attachment, Ability source) {
super(GameEvent.EventType.UNATTACHED, targetId, null, attachment == null ? null : attachment.getControllerId());
this.setSourceId(attachmentId);
}
}

View file

@ -2,6 +2,8 @@ package mage.game.events;
import java.util.List;
import java.util.UUID;
import mage.abilities.Ability;
import mage.constants.Zone;
import mage.game.permanent.Permanent;
@ -15,34 +17,39 @@ public class ZoneChangeEvent extends GameEvent {
private Zone toZone;
private Zone originalToZone;
private Permanent target;
private Ability source; // link to source ability, can be null in rare situations
public ZoneChangeEvent(Permanent target, UUID sourceId, UUID playerId, Zone fromZone, Zone toZone) {
super(EventType.ZONE_CHANGE, target.getId(), sourceId, playerId);
public ZoneChangeEvent(Permanent target, Ability source, UUID playerId, Zone fromZone, Zone toZone) {
super(GameEvent.EventType.ZONE_CHANGE, target.getId(), source, playerId);
this.fromZone = fromZone;
this.setToZone(toZone);
this.target = target;
this.source = source;
}
public ZoneChangeEvent(Permanent target, UUID sourceId, UUID playerId, Zone fromZone, Zone toZone, List<UUID> appliedEffects) {
super(EventType.ZONE_CHANGE, target.getId(), sourceId, playerId);
public ZoneChangeEvent(Permanent target, Ability source, UUID playerId, Zone fromZone, Zone toZone, List<UUID> appliedEffects) {
super(GameEvent.EventType.ZONE_CHANGE, target.getId(), source, playerId);
this.fromZone = fromZone;
this.setToZone(toZone);
this.target = target;
this.source = source;
if (appliedEffects != null) {
this.appliedEffects = appliedEffects;
}
}
public ZoneChangeEvent(UUID targetId, UUID sourceId, UUID playerId, Zone fromZone, Zone toZone) {
super(EventType.ZONE_CHANGE, targetId, sourceId, playerId);
public ZoneChangeEvent(UUID targetId, Ability source, UUID playerId, Zone fromZone, Zone toZone) {
super(GameEvent.EventType.ZONE_CHANGE, targetId, source, playerId);
this.fromZone = fromZone;
this.setToZone(toZone);
this.source = source;
}
public ZoneChangeEvent(UUID targetId, UUID sourceId, UUID playerId, Zone fromZone, Zone toZone, List<UUID> appliedEffects) {
super(EventType.ZONE_CHANGE, targetId, sourceId, playerId);
public ZoneChangeEvent(UUID targetId, Ability source, UUID playerId, Zone fromZone, Zone toZone, List<UUID> appliedEffects) {
super(GameEvent.EventType.ZONE_CHANGE, targetId, source, playerId);
this.fromZone = fromZone;
this.setToZone(toZone);
this.source = source;
if (appliedEffects != null) {
this.appliedEffects = appliedEffects;
}
@ -87,4 +94,13 @@ public class ZoneChangeEvent extends GameEvent {
return originalToZone;
}
/**
* Source ability of the event, can be null in rare cases
*
* @return
*/
public Ability getSource() {
return this.source;
}
}

View file

@ -18,7 +18,7 @@ public class ZoneChangeGroupEvent extends GameEvent {
private final Set<PermanentToken> tokens;
public ZoneChangeGroupEvent(Set<Card> cards, Set<PermanentToken> tokens, UUID sourceId, UUID playerId, Zone fromZone, Zone toZone) {
super(EventType.ZONE_CHANGE_GROUP, null, sourceId, playerId);
super(GameEvent.EventType.ZONE_CHANGE_GROUP, null, null, playerId);
this.fromZone = fromZone;
this.toZone = toZone;
this.cards = cards;

View file

@ -24,9 +24,9 @@ public interface Permanent extends Card, Controllable {
boolean untap(Game game);
boolean tap(Game game);
boolean tap(Ability source, Game game);
boolean tap(boolean forCombat, Game game);
boolean tap(boolean forCombat, Ability source, Game game);
/**
* use tap(game)
@ -91,25 +91,23 @@ public interface Permanent extends Card, Controllable {
int getAttachedToZoneChangeCounter();
void attachTo(UUID permanentId, Game game);
void attachTo(UUID permanentId, Ability source, Game game);
void unattach(Game game);
// boolean addAttachment(UUID permanentId, Game game);
//
// boolean removeAttachment(UUID permanentId, Game game);
boolean canBeTargetedBy(MageObject source, UUID controllerId, Game game);
boolean hasProtectionFrom(MageObject source, Game game);
/**
* @param source
* @param attachment
* @param source can be null for default checks like state base
* @param game
* @param silentMode - use it to ignore warning message for users (e.g. for
* checking only)
* @return
*/
boolean cantBeAttachedBy(MageObject source, Game game, boolean silentMode);
boolean cantBeAttachedBy(MageObject attachment, Ability source, Game game, boolean silentMode);
boolean wasControlledFromStartOfControllerTurn();
@ -117,23 +115,36 @@ public interface Permanent extends Card, Controllable {
int getDamage();
int damage(int damage, UUID sourceId, Game game);
int damage(int damage, UUID attackerId, Ability source, Game game);
int damage(int damage, UUID sourceId, Game game, boolean combat, boolean preventable);
int damage(int damage, UUID sourceId, Game game, boolean combat, boolean preventable, List<UUID> appliedEffects);
int damage(int damage, UUID attackerId, Ability source, Game game, boolean combat, boolean preventable);
/**
* used in combat only to deal damage at the same time
* Uses in replace events only
*
* @param damage
* @param sourceId
* @param attackerId id of the permanent or player who make damage (source.getSourceId() in most cases)
* @param source can be null for default game actions like combat
* @param game
* @param combat
* @param preventable
* @param appliedEffects
* @return
*/
int damage(int damage, UUID attackerId, Ability source, Game game, boolean combat, boolean preventable, List<UUID> appliedEffects);
/**
* Uses in combat only to deal damage at the same time
*
* @param damage
* @param attackerId id of the permanent or player who make damage (source.getSourceId() in most cases)
* @param source can be null for default game actions like combat
* @param game
* @param preventable
* @param combat
* @return
*/
int markDamage(int damage, UUID sourceId, Game game, boolean preventable, boolean combat);
int markDamage(int damage, UUID attackerId, Ability source, Game game, boolean preventable, boolean combat);
void markLifelink(int damage);
@ -145,9 +156,15 @@ public interface Permanent extends Card, Controllable {
MageObject getBasicMageObject(Game game);
boolean destroy(UUID sourceId, Game game, boolean noRegen);
boolean destroy(Ability source, Game game, boolean noRegen);
boolean sacrifice(UUID sourceId, Game game);
/**
*
* @param source can be null for state base actions
* @param game
* @return
*/
boolean sacrifice(Ability source, Game game);
boolean regenerate(Ability source, Game game);
@ -155,7 +172,7 @@ public interface Permanent extends Card, Controllable {
boolean fight(Permanent fightTarget, Ability source, Game game, boolean batchTrigger);
boolean entersBattlefield(UUID sourceId, Game game, Zone fromZone, boolean fireEvent);
boolean entersBattlefield(Ability source, Game game, Zone fromZone, boolean fireEvent);
String getValue(GameState state);
@ -173,7 +190,7 @@ public interface Permanent extends Card, Controllable {
void resetControl();
boolean changeControllerId(UUID controllerId, Game game);
boolean changeControllerId(UUID controllerId, Game game, Ability source);
boolean checkControlChanged(Game game);

View file

@ -163,8 +163,8 @@ public class PermanentCard extends PermanentImpl {
}
@Override
public boolean turnFaceUp(Game game, UUID playerId) {
if (super.turnFaceUp(game, playerId)) {
public boolean turnFaceUp(Ability source, Game game, UUID playerId) {
if (super.turnFaceUp(source, game, playerId)) {
power.modifyBaseValue(power.getBaseValue());
toughness.modifyBaseValue(toughness.getBaseValue());
setManifested(false);

View file

@ -500,17 +500,17 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
}
@Override
public boolean tap(Game game) {
return tap(false, game);
public boolean tap(Ability source, Game game) {
return tap(false, source, game);
}
@Override
public boolean tap(boolean forCombat, Game game) {
public boolean tap(boolean forCombat, Ability source, Game game) {
//20091005 - 701.15a
if (!tapped) {
if (!replaceEvent(EventType.TAP, game)) {
this.tapped = true;
game.fireEvent(new GameEvent(EventType.TAPPED, objectId, ownerId, controllerId, 0, forCombat));
game.fireEvent(new GameEvent(GameEvent.EventType.TAPPED, objectId, source, controllerId, 0, forCombat));
return true;
}
}
@ -562,7 +562,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
if (!replaceEvent(EventType.TRANSFORM, game)) {
setTransformed(!transformed);
game.applyEffects();
game.addSimultaneousEvent(GameEvent.getEvent(EventType.TRANSFORMED, getId(), getControllerId()));
game.addSimultaneousEvent(GameEvent.getEvent(GameEvent.EventType.TRANSFORMED, getId(), getControllerId()));
return true;
}
}
@ -701,27 +701,29 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
}
@Override
public boolean changeControllerId(UUID controllerId, Game game) {
Player newController = game.getPlayer(controllerId);
public boolean changeControllerId(UUID newControllerId, Game game, Ability source) {
Player newController = game.getPlayer(newControllerId);
if (newController == null || !newController.isInGame()) {
return false;
}
// For each control change compared to last controler send a GAIN_CONTROL replace event to be able to prevent the gain control (e.g. Guardian Beast)
if (beforeResetControllerId != controllerId) {
GameEvent gainControlEvent = GameEvent.getEvent(GameEvent.EventType.GAIN_CONTROL, this.getId(), null, controllerId);
if (beforeResetControllerId != newControllerId) {
GameEvent gainControlEvent = GameEvent.getEvent(GameEvent.EventType.GAIN_CONTROL, this.getId(), null, newControllerId);
if (game.replaceEvent(gainControlEvent)) {
return false;
}
}
GameEvent loseControlEvent = GameEvent.getEvent(GameEvent.EventType.LOSE_CONTROL, this.getId(), null, controllerId);
GameEvent loseControlEvent = GameEvent.getEvent(GameEvent.EventType.LOSE_CONTROL, this.getId(), null, newControllerId);
if (game.replaceEvent(loseControlEvent)) {
return false;
}
if (newController != null && (!newController.hasLeft() || !newController.hasLost())) {
this.controllerId = controllerId;
return true;
}
return false;
// must change abilities controller too
this.controllerId = newControllerId;
this.getAbilities().setControllerId(newControllerId);
return true;
}
@Override
@ -733,8 +735,8 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
this.getAbilities(game).setControllerId(controllerId);
game.getContinuousEffects().setController(objectId, controllerId);
// the controller of triggered abilites is always set/checked before the abilities triggers so not needed here
game.fireEvent(new GameEvent(EventType.LOST_CONTROL, objectId, objectId, beforeResetControllerId));
game.fireEvent(new GameEvent(EventType.GAINED_CONTROL, objectId, objectId, controllerId));
game.fireEvent(new GameEvent(GameEvent.EventType.LOST_CONTROL, objectId, null, beforeResetControllerId));
game.fireEvent(new GameEvent(GameEvent.EventType.GAINED_CONTROL, objectId, null, controllerId));
return true;
} else if (isCopy()) {// Because the previous copied abilities can be from another controller - change controller in any case for abilities
@ -784,15 +786,15 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
}
@Override
public void attachTo(UUID attachToObjectId, Game game) {
public void attachTo(UUID attachToObjectId, Ability source, Game game) {
if (this.attachedTo != null && !Objects.equals(this.attachedTo, attachToObjectId)) {
Permanent attachedToUntilNowObject = game.getPermanent(this.attachedTo);
if (attachedToUntilNowObject != null) {
attachedToUntilNowObject.removeAttachment(this.objectId, game);
attachedToUntilNowObject.removeAttachment(this.objectId, source, game);
} else {
Card attachedToUntilNowCard = game.getCard(this.attachedTo);
if (attachedToUntilNowCard != null) {
attachedToUntilNowCard.removeAttachment(this.objectId, game);
attachedToUntilNowCard.removeAttachment(this.objectId, source, game);
}
}
@ -835,72 +837,73 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
}
@Override
public int damage(int damage, UUID sourceId, Game game) {
return damage(damage, sourceId, game, true, false, false, null);
public int damage(int damage, UUID attackerId, Ability source, Game game) {
return doDamage(damage, attackerId, source, game, true, false, false, null);
}
@Override
public int damage(int damage, UUID sourceId, Game game, boolean combat, boolean preventable) {
return damage(damage, sourceId, game, preventable, combat, false, null);
public int damage(int damage, UUID attackerId, Ability source, Game game, boolean combat, boolean preventable) {
return doDamage(damage, attackerId, source, game, preventable, combat, false, null);
}
@Override
public int damage(int damage, UUID sourceId, Game game, boolean combat, boolean preventable, List<UUID> appliedEffects) {
return damage(damage, sourceId, game, preventable, combat, false, appliedEffects);
public int damage(int damage, UUID attackerId, Ability source, Game game, boolean combat, boolean preventable, List<UUID> appliedEffects) {
return doDamage(damage, attackerId, source, game, preventable, combat, false, appliedEffects);
}
/**
* @param damageAmount
* @param sourceId
* @param attackerId id of the permanent or player who make damage (source.getSourceId() in most cases)
* @param source
* @param game
* @param preventable
* @param combat
* @param markDamage If true, damage will be dealt later in applyDamage
* method
* method, uses only in inner markDamage.
* @return
*/
private int damage(int damageAmount, UUID sourceId, Game game, boolean preventable, boolean combat, boolean markDamage, List<UUID> appliedEffects) {
private int doDamage(int damageAmount, UUID attackerId, Ability source, Game game, boolean preventable, boolean combat, boolean markDamage, List<UUID> appliedEffects) {
int damageDone = 0;
if (damageAmount > 0 && canDamage(game.getObject(sourceId), game)) {
if (damageAmount > 0 && canDamage(game.getObject(attackerId), game)) {
if (this.isPlaneswalker()) {
damageDone = damagePlaneswalker(damageAmount, sourceId, game, preventable, combat, markDamage, appliedEffects);
damageDone = damagePlaneswalker(damageAmount, attackerId, source, game, preventable, combat, markDamage, appliedEffects);
} else {
damageDone = damageCreature(damageAmount, sourceId, game, preventable, combat, markDamage, appliedEffects);
damageDone = damageCreature(damageAmount, attackerId, source, game, preventable, combat, markDamage, appliedEffects);
}
if (damageDone > 0) {
UUID sourceControllerId = null;
Abilities sourceAbilities = null;
MageObject source = game.getPermanentOrLKIBattlefield(sourceId);
if (source == null) {
StackObject stackObject = game.getStack().getStackObject(sourceId);
MageObject attacker = game.getPermanentOrLKIBattlefield(attackerId);
if (attacker == null) {
StackObject stackObject = game.getStack().getStackObject(attackerId);
if (stackObject != null) {
source = stackObject.getStackAbility().getSourceObject(game);
attacker = stackObject.getStackAbility().getSourceObject(game);
} else {
source = game.getObject(sourceId);
attacker = game.getObject(attackerId);
}
if (source instanceof Spell) {
sourceAbilities = ((Spell) source).getAbilities(game);
sourceControllerId = ((Spell) source).getControllerId();
} else if (source instanceof Card) {
sourceAbilities = ((Card) source).getAbilities(game);
sourceControllerId = ((Card) source).getOwnerId();
} else if (source instanceof CommandObject) {
sourceControllerId = ((CommandObject) source).getControllerId();
sourceAbilities = source.getAbilities();
if (attacker instanceof Spell) {
sourceAbilities = ((Spell) attacker).getAbilities(game);
sourceControllerId = ((Spell) attacker).getControllerId();
} else if (attacker instanceof Card) {
sourceAbilities = ((Card) attacker).getAbilities(game);
sourceControllerId = ((Card) attacker).getOwnerId();
} else if (attacker instanceof CommandObject) {
sourceControllerId = ((CommandObject) attacker).getControllerId();
sourceAbilities = attacker.getAbilities();
} else {
source = null;
attacker = null;
}
} else {
sourceAbilities = ((Permanent) source).getAbilities(game);
sourceControllerId = ((Permanent) source).getControllerId();
sourceAbilities = ((Permanent) attacker).getAbilities(game);
sourceControllerId = ((Permanent) attacker).getControllerId();
}
if (source != null && sourceAbilities != null) {
if (attacker != null && sourceAbilities != null) {
if (sourceAbilities.containsKey(LifelinkAbility.getInstance().getId())) {
if (markDamage) {
game.getPermanent(sourceId).markLifelink(damageDone);
game.getPermanent(attackerId).markLifelink(damageDone);
} else {
Player player = game.getPlayer(sourceControllerId);
player.gainLife(damageDone, game, sourceId);
player.gainLife(damageDone, game, source);
}
}
if (sourceAbilities.containsKey(DeathtouchAbility.getInstance().getId())) {
@ -912,14 +915,14 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
// Unstable ability - Earl of Squirrel
if (sourceAbilities.containsKey(SquirrellinkAbility.getInstance().getId())) {
Player player = game.getPlayer(sourceControllerId);
new SquirrelToken().putOntoBattlefield(damageDone, game, sourceId, player.getId());
new SquirrelToken().putOntoBattlefield(damageDone, game, source, player.getId());
}
dealtDamageByThisTurn.add(new MageObjectReference(source, game));
dealtDamageByThisTurn.add(new MageObjectReference(attacker, game));
}
if (source == null) {
if (attacker == null) {
game.informPlayers(getLogName() + " gets " + damageDone + " damage");
} else {
game.informPlayers(source.getLogName() + " deals " + damageDone + " damage to " + getLogName());
game.informPlayers(attacker.getLogName() + " deals " + damageDone + " damage to " + getLogName());
}
}
}
@ -932,15 +935,15 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
}
@Override
public int markDamage(int damageAmount, UUID sourceId, Game game, boolean preventable, boolean combat) {
return damage(damageAmount, sourceId, game, preventable, combat, true, null);
public int markDamage(int damageAmount, UUID attackerId, Ability source, Game game, boolean preventable, boolean combat) {
return doDamage(damageAmount, attackerId, source, game, preventable, combat, true, null);
}
@Override
public int applyDamage(Game game) {
if (markedLifelink > 0) {
Player player = game.getPlayer(this.getControllerId());
player.gainLife(markedLifelink, game, this.getId());
player.gainLife(markedLifelink, game, null);
markedLifelink = 0;
}
if (markedDamage == null) {
@ -969,18 +972,18 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
deathtouched = false;
}
protected int damagePlaneswalker(int damage, UUID sourceId, Game game, boolean preventable, boolean combat, boolean markDamage, List<UUID> appliedEffects) {
GameEvent event = new DamagePlaneswalkerEvent(objectId, sourceId, controllerId, damage, preventable, combat);
protected int damagePlaneswalker(int damage, UUID attackerId, Ability source, Game game, boolean preventable, boolean combat, boolean markDamage, List<UUID> appliedEffects) {
GameEvent event = new DamagePlaneswalkerEvent(objectId, attackerId, controllerId, damage, preventable, combat);
event.setAppliedEffects(appliedEffects);
if (!game.replaceEvent(event)) {
int actualDamage = checkProtectionAbilities(event, sourceId, game);
int actualDamage = checkProtectionAbilities(event, attackerId, source, game);
if (actualDamage > 0) {
int countersToRemove = actualDamage;
if (countersToRemove > getCounters(game).getCount(CounterType.LOYALTY)) {
countersToRemove = getCounters(game).getCount(CounterType.LOYALTY);
}
removeCounters(CounterType.LOYALTY.getName(), countersToRemove, game);
DamagedEvent damagedEvent = new DamagedPlaneswalkerEvent(objectId, sourceId, controllerId, actualDamage, combat);
removeCounters(CounterType.LOYALTY.getName(), countersToRemove, source, game);
DamagedEvent damagedEvent = new DamagedPlaneswalkerEvent(objectId, attackerId, controllerId, actualDamage, combat);
game.fireEvent(damagedEvent);
game.getState().addSimultaneousDamage(damagedEvent, game);
return actualDamage;
@ -989,23 +992,22 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
return 0;
}
protected int damageCreature(int damage, UUID sourceId, Game game, boolean preventable, boolean combat, boolean markDamage, List<UUID> appliedEffects) {
GameEvent event = new DamageCreatureEvent(objectId, sourceId, controllerId, damage, preventable, combat);
protected int damageCreature(int damage, UUID attackerId, Ability source, Game game, boolean preventable, boolean combat, boolean markDamage, List<UUID> appliedEffects) {
GameEvent event = new DamageCreatureEvent(this.getId(), attackerId, this.getControllerId(), damage, preventable, combat);
event.setAppliedEffects(appliedEffects);
if (!game.replaceEvent(event)) {
int actualDamage = checkProtectionAbilities(event, sourceId, game);
int actualDamage = checkProtectionAbilities(event, attackerId, source, game);
if (actualDamage > 0) {
//Permanent source = game.getPermanent(sourceId);
MageObject source = game.getObject(sourceId);
if (source != null && (source.getAbilities().containsKey(InfectAbility.getInstance().getId())
|| source.getAbilities().containsKey(WitherAbility.getInstance().getId()))) {
MageObject attacker = game.getObject(attackerId);
if (attacker != null && (attacker.getAbilities().containsKey(InfectAbility.getInstance().getId())
|| attacker.getAbilities().containsKey(WitherAbility.getInstance().getId()))) {
if (markDamage) {
// mark damage only
markDamage(CounterType.M1M1.createInstance(actualDamage), source);
markDamage(CounterType.M1M1.createInstance(actualDamage), attacker);
} else {
Ability damageSourceAbility = null;
if (source instanceof Permanent) {
damageSourceAbility = ((Permanent) source).getSpellAbility();
if (attacker instanceof Permanent) {
damageSourceAbility = ((Permanent) attacker).getSpellAbility();
}
// deal damage immediately
addCounters(CounterType.M1M1.createInstance(actualDamage), damageSourceAbility, game);
@ -1013,7 +1015,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
} else {
this.damage = CardUtil.addWithOverflowCheck(this.damage, actualDamage);
}
DamagedEvent damagedEvent = new DamagedCreatureEvent(objectId, sourceId, controllerId, actualDamage, combat);
DamagedEvent damagedEvent = new DamagedCreatureEvent(this.getId(), attackerId, this.getControllerId(), actualDamage, combat);
game.fireEvent(damagedEvent);
game.getState().addSimultaneousDamage(damagedEvent, game);
return actualDamage;
@ -1022,14 +1024,14 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
return 0;
}
private int checkProtectionAbilities(GameEvent event, UUID sourceId, Game game) {
MageObject source = game.getObject(sourceId);
if (source != null && hasProtectionFrom(source, game)) {
GameEvent preventEvent = new PreventDamageEvent(this.objectId, sourceId, this.controllerId, event.getAmount(), ((DamageEvent) event).isCombatDamage());
private int checkProtectionAbilities(GameEvent event, UUID attackerId, Ability source, Game game) {
MageObject attacker = game.getObject(attackerId);
if (attacker != null && hasProtectionFrom(attacker, game)) {
GameEvent preventEvent = new PreventDamageEvent(this.getId(), attackerId, source, this.getControllerId(), event.getAmount(), ((DamageEvent) event).isCombatDamage());
if (!game.replaceEvent(preventEvent)) {
int preventedDamage = event.getAmount();
event.setAmount(0);
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.PREVENTED_DAMAGE, this.objectId, sourceId, this.controllerId, preventedDamage));
game.fireEvent(new PreventedDamageEvent(this.getId(), attackerId, source, this.getControllerId(), preventedDamage));
return 0;
}
}
@ -1044,18 +1046,18 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
}
@Override
public boolean entersBattlefield(UUID sourceId, Game game, Zone fromZone, boolean fireEvent) {
public boolean entersBattlefield(Ability source, Game game, Zone fromZone, boolean fireEvent) {
controlledFromStartOfControllerTurn = false;
if (this.isFaceDown(game)) {
// remove some attributes here, because first apply effects comes later otherwise abilities (e.g. color related) will unintended trigger
MorphAbility.setPermanentToFaceDownCreature(this);
}
EntersTheBattlefieldEvent event = new EntersTheBattlefieldEvent(this, sourceId, getControllerId(), fromZone, EnterEventType.SELF);
EntersTheBattlefieldEvent event = new EntersTheBattlefieldEvent(this, source, getControllerId(), fromZone, EnterEventType.SELF);
if (game.replaceEvent(event)) {
return false;
}
event = new EntersTheBattlefieldEvent(this, sourceId, getControllerId(), fromZone);
event = new EntersTheBattlefieldEvent(this, source, getControllerId(), fromZone);
if (!game.replaceEvent(event)) {
if (fireEvent) {
game.addSimultaneousEvent(event);
@ -1088,7 +1090,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
}
// needed to get the correct possible targets if target rule modification effects are active
// e.g. Fiendslayer Paladin tried to target with Ultimate Price
return !game.getContinuousEffects().preventedByRuleModification(GameEvent.getEvent(EventType.TARGET, this.getId(), source.getId(), sourceControllerId), null, game, true);
return !game.getContinuousEffects().preventedByRuleModification(new TargetEvent(this, source.getId(), sourceControllerId), null, game, true);
}
return true;
@ -1105,19 +1107,19 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
}
@Override
public boolean cantBeAttachedBy(MageObject source, Game game, boolean silentMode) {
public boolean cantBeAttachedBy(MageObject attachment, Ability source, Game game, boolean silentMode) {
for (ProtectionAbility ability : this.getAbilities(game).getProtectionAbilities()) {
if (!(source.hasSubtype(SubType.AURA, game)
if (!(attachment.hasSubtype(SubType.AURA, game)
&& !ability.removesAuras())
&& !(source.hasSubtype(SubType.EQUIPMENT, game)
&& !(attachment.hasSubtype(SubType.EQUIPMENT, game)
&& !ability.removesEquipment())) {
if (!source.getId().equals(ability.getAuraIdNotToBeRemoved())
&& !ability.canTarget(source, game)) {
return !ability.getDoesntRemoveControlled() || isControlledBy(game.getControllerId(source.getId()));
if (!attachment.getId().equals(ability.getAuraIdNotToBeRemoved())
&& !ability.canTarget(attachment, game)) {
return !ability.getDoesntRemoveControlled() || isControlledBy(game.getControllerId(attachment.getId()));
}
}
}
return game.getContinuousEffects().preventedByRuleModification(GameEvent.getEvent(EventType.STAY_ATTACHED, objectId, source.getId(), null), null, game, silentMode);
return game.getContinuousEffects().preventedByRuleModification(new StayAttachedEvent(this.getId(), attachment.getId(), source), null, game, silentMode);
}
protected boolean canDamage(MageObject source, Game game) {
@ -1128,7 +1130,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
}
@Override
public boolean destroy(UUID sourceId, Game game, boolean noRegen) {
public boolean destroy(Ability source, Game game, boolean noRegen) {
// Only permanets on the battlefield can be destroyed
if (!game.getState().getZone(getId()).equals(Zone.BATTLEFIELD)) {
return false;
@ -1138,10 +1140,10 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
return false;
}
if (!game.replaceEvent(GameEvent.getEvent(EventType.DESTROY_PERMANENT, objectId, sourceId, controllerId, noRegen ? 1 : 0))) {
if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.DESTROY_PERMANENT, objectId, source, controllerId, noRegen ? 1 : 0))) {
// this means destroy was successful, if object movement to graveyard will be replaced (e.g. commander to command zone) it's still
// handled as successful destroying (but not as sucessful "dies this way" for destroying).
if (moveToZone(Zone.GRAVEYARD, sourceId, game, false)) {
if (moveToZone(Zone.GRAVEYARD, source, game, false)) {
if (!game.isSimulation()) {
String logName;
Card card = game.getCard(this.getId());
@ -1156,7 +1158,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
game.informPlayers(logName + " was destroyed");
}
}
game.fireEvent(GameEvent.getEvent(EventType.DESTROYED_PERMANENT, objectId, sourceId, controllerId));
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.DESTROYED_PERMANENT, objectId, source, controllerId));
}
return true;
}
@ -1164,17 +1166,17 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
}
@Override
public boolean sacrifice(UUID sourceId, Game game) {
public boolean sacrifice(Ability source, Game game) {
//20091005 - 701.13
if (isPhasedIn() && !game.replaceEvent(GameEvent.getEvent(EventType.SACRIFICE_PERMANENT, objectId, sourceId, controllerId))) {
if (isPhasedIn() && !game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.SACRIFICE_PERMANENT, objectId, source, controllerId))) {
// Commander replacement effect or Rest in Peace (exile instead of graveyard) in play does not prevent successful sacrifice
// so the return value of the moveToZone is not taken into account here
moveToZone(Zone.GRAVEYARD, sourceId, game, false);
moveToZone(Zone.GRAVEYARD, source, game, false);
Player player = game.getPlayer(getControllerId());
if (player != null && !game.isSimulation()) {
game.informPlayers(player.getLogName() + " sacrificed " + this.getLogName());
}
game.fireEvent(GameEvent.getEvent(EventType.SACRIFICED_PERMANENT, objectId, sourceId, controllerId));
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.SACRIFICED_PERMANENT, objectId, source, controllerId));
return true;
}
return false;
@ -1183,15 +1185,15 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
@Override
public boolean regenerate(Ability source, Game game) {
//20110930 - 701.12
if (!game.replaceEvent(GameEvent.getEvent(EventType.REGENERATE, objectId, source.getSourceId(), controllerId))) {
this.tap(game);
if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.REGENERATE, objectId, source, controllerId))) {
this.tap(source, game);
this.removeFromCombat(game);
this.removeAllDamage(game);
// remove one regen shield
RegenerateSourceEffect.decRegenerationShieldsAmount(game, this.getId());
game.fireEvent(GameEvent.getEvent(EventType.REGENERATED, objectId, source.getSourceId(), controllerId));
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.REGENERATED, objectId, source, controllerId));
return true;
}
return false;
@ -1207,12 +1209,23 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
this.toughness.boostValue(toughness);
}
/**
* Simple event without source
* @param eventType
* @param game
*/
protected void fireEvent(EventType eventType, Game game) {
game.fireEvent(GameEvent.getEvent(eventType, this.objectId, ownerId)); // controllerId seems to me more logical (LevelX2)
game.fireEvent(GameEvent.getEvent(eventType, this.objectId, null, this.controllerId));
}
/**
* Simple event without source
* @param eventType
* @param game
* @return
*/
protected boolean replaceEvent(EventType eventType, Game game) {
return game.replaceEvent(GameEvent.getEvent(eventType, this.objectId, ownerId));// controllerId seems to me more logical (LevelX2)
return game.replaceEvent(GameEvent.getEvent(eventType, this.objectId, null, this.controllerId));
}
@Override
@ -1580,19 +1593,21 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
@Override
public boolean fight(Permanent fightTarget, Ability source, Game game, boolean batchTrigger) {
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.FIGHTED_PERMANENT, fightTarget.getId(), getId(), source.getControllerId()));
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.FIGHTED_PERMANENT, getId(), fightTarget.getId(), source.getControllerId()));
damage(fightTarget.getPower().getValue(), fightTarget.getId(), game);
fightTarget.damage(getPower().getValue(), getId(), game);
if (!batchTrigger) {
return true;
// double fight events for each creature
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.FIGHTED_PERMANENT, fightTarget.getId(), source, source.getControllerId()));
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.FIGHTED_PERMANENT, getId(), source, source.getControllerId()));
damage(fightTarget.getPower().getValue(), fightTarget.getId(), source, game);
fightTarget.damage(getPower().getValue(), getId(), source, game);
if (batchTrigger) {
Set<MageObjectReference> morSet = new HashSet<>();
morSet.add(new MageObjectReference(this, game));
morSet.add(new MageObjectReference(fightTarget, game));
String data = UUID.randomUUID().toString();
game.getState().setValue("batchFight_" + data, morSet);
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.BATCH_FIGHT, getId(), source, source.getControllerId(), data, 0));
}
Set<MageObjectReference> morSet = new HashSet<>();
morSet.add(new MageObjectReference(this, game));
morSet.add(new MageObjectReference(fightTarget, game));
String data = UUID.randomUUID().toString();
game.getState().setValue("batchFight_" + data, morSet);
game.fireEvent(GameEvent.getEvent(EventType.BATCH_FIGHT, getId(), getId(), source.getControllerId(), data, 0));
return true;
}
@ -1630,18 +1645,18 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
}
@Override
public boolean moveToZone(Zone toZone, UUID sourceId, Game game, boolean flag, List<UUID> appliedEffects) {
public boolean moveToZone(Zone toZone, Ability source, Game game, boolean flag, List<UUID> appliedEffects) {
Zone fromZone = game.getState().getZone(objectId);
Player controller = game.getPlayer(controllerId);
if (controller != null) {
ZoneChangeEvent event = new ZoneChangeEvent(this, sourceId, controllerId, fromZone, toZone, appliedEffects);
ZoneChangeEvent event = new ZoneChangeEvent(this, source, controllerId, fromZone, toZone, appliedEffects);
ZoneChangeInfo zoneChangeInfo;
if (toZone == Zone.LIBRARY) {
zoneChangeInfo = new ZoneChangeInfo.Library(event, flag /* put on top */);
} else {
zoneChangeInfo = new ZoneChangeInfo(event);
}
boolean successfullyMoved = ZonesHandler.moveCard(zoneChangeInfo, game);
boolean successfullyMoved = ZonesHandler.moveCard(zoneChangeInfo, game, source);
//20180810 - 701.3d
detachAllAttachments(game);
return successfullyMoved;
@ -1650,12 +1665,12 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
}
@Override
public boolean moveToExile(UUID exileId, String name, UUID sourceId, Game game, List<UUID> appliedEffects) {
public boolean moveToExile(UUID exileId, String name, Ability source, Game game, List<UUID> appliedEffects) {
Zone fromZone = game.getState().getZone(objectId);
ZoneChangeEvent event = new ZoneChangeEvent(this, sourceId, ownerId, fromZone, Zone.EXILED, appliedEffects);
ZoneChangeEvent event = new ZoneChangeEvent(this, source, ownerId, fromZone, Zone.EXILED, appliedEffects);
ZoneChangeInfo.Exile zcInfo = new ZoneChangeInfo.Exile(event, exileId, name);
boolean successfullyMoved = ZonesHandler.moveCard(zcInfo, game);
boolean successfullyMoved = ZonesHandler.moveCard(zcInfo, game, source);
//20180810 - 701.3d
detachAllAttachments(game);
return successfullyMoved;

View file

@ -95,7 +95,7 @@ class BelzenlokDemonTokenEffect extends OneShotEffect {
} else {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
controller.damage(6, source.getSourceId(), game);
controller.damage(6, source.getSourceId(), source, game);
}
}
return true;

View file

@ -85,7 +85,7 @@ class ReturnSengirNosferatuEffect extends OneShotEffect {
player.chooseTarget(Outcome.PutCreatureInPlay, target, source, game);
Card card = game.getCard(target.getTargets().get(0));
if (card != null) {
return card.moveToZone(Zone.BATTLEFIELD, source.getSourceId(), game, false);
return card.moveToZone(Zone.BATTLEFIELD, source, game, false);
}
}
return false;

View file

@ -27,13 +27,13 @@ public interface Token extends MageObject {
void addAbility(Ability ability);
boolean putOntoBattlefield(int amount, Game game, UUID sourceId, UUID controllerId);
boolean putOntoBattlefield(int amount, Game game, Ability source, UUID controllerId);
boolean putOntoBattlefield(int amount, Game game, UUID sourceId, UUID controllerId, boolean tapped, boolean attacking);
boolean putOntoBattlefield(int amount, Game game, Ability source, UUID controllerId, boolean tapped, boolean attacking);
boolean putOntoBattlefield(int amount, Game game, UUID sourceId, UUID controllerId, boolean tapped, boolean attacking, UUID attackedPlayer);
boolean putOntoBattlefield(int amount, Game game, Ability source, UUID controllerId, boolean tapped, boolean attacking, UUID attackedPlayer);
boolean putOntoBattlefield(int amount, Game game, UUID sourceId, UUID controllerId, boolean tapped, boolean attacking, UUID attackedPlayer, boolean created);
boolean putOntoBattlefield(int amount, Game game, Ability source, UUID controllerId, boolean tapped, boolean attacking, UUID attackedPlayer, boolean created);
void setPower(int power);

View file

@ -123,13 +123,13 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
}
@Override
public boolean putOntoBattlefield(int amount, Game game, UUID sourceId, UUID controllerId) {
return this.putOntoBattlefield(amount, game, sourceId, controllerId, false, false);
public boolean putOntoBattlefield(int amount, Game game, Ability source, UUID controllerId) {
return this.putOntoBattlefield(amount, game, source, controllerId, false, false);
}
@Override
public boolean putOntoBattlefield(int amount, Game game, UUID sourceId, UUID controllerId, boolean tapped, boolean attacking) {
return putOntoBattlefield(amount, game, sourceId, controllerId, tapped, attacking, null);
public boolean putOntoBattlefield(int amount, Game game, Ability source, UUID controllerId, boolean tapped, boolean attacking) {
return putOntoBattlefield(amount, game, source, controllerId, tapped, attacking, null);
}
private String getSetCode(Game game, UUID sourceId) {
@ -158,12 +158,12 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
}
@Override
public boolean putOntoBattlefield(int amount, Game game, UUID sourceId, UUID controllerId, boolean tapped, boolean attacking, UUID attackedPlayer) {
return putOntoBattlefield(amount, game, sourceId, controllerId, tapped, attacking, attackedPlayer, true);
public boolean putOntoBattlefield(int amount, Game game, Ability source, UUID controllerId, boolean tapped, boolean attacking, UUID attackedPlayer) {
return putOntoBattlefield(amount, game, source, controllerId, tapped, attacking, attackedPlayer, true);
}
@Override
public boolean putOntoBattlefield(int amount, Game game, UUID sourceId, UUID controllerId, boolean tapped, boolean attacking, UUID attackedPlayer, boolean created) {
public boolean putOntoBattlefield(int amount, Game game, Ability source, UUID controllerId, boolean tapped, boolean attacking, UUID attackedPlayer, boolean created) {
Player controller = game.getPlayer(controllerId);
if (controller == null) {
return false;
@ -173,16 +173,20 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
}
lastAddedTokenIds.clear();
CreateTokenEvent event = new CreateTokenEvent(sourceId, controllerId, amount, this);
CreateTokenEvent event = new CreateTokenEvent(source, controllerId, amount, this);
if (!created || !game.replaceEvent(event)) {
putOntoBattlefieldHelper(event, game, tapped, attacking, attackedPlayer, created);
putOntoBattlefieldHelper(event, game, source, tapped, attacking, attackedPlayer, created);
return true;
}
return false;
}
private static void putOntoBattlefieldHelper(CreateTokenEvent event, Game game, boolean tapped, boolean attacking, UUID attackedPlayer, boolean created) {
private static void putOntoBattlefieldHelper(CreateTokenEvent event, Game game, Ability source, boolean tapped, boolean attacking, UUID attackedPlayer, boolean created) {
Player controller = game.getPlayer(event.getPlayerId());
if (controller == null) {
return;
}
Token token = event.getToken();
int amount = event.getAmount();
@ -200,7 +204,7 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
}
game.setScopeRelevant(true);
for (Permanent permanent : permanents) {
if (permanent.entersBattlefield(event.getSourceId(), game, Zone.OUTSIDE, true)) {
if (permanent.entersBattlefield(source, game, Zone.OUTSIDE, true)) {
permanentsEntered.add(permanent);
} else {
game.getPermanentsEntering().remove(permanent.getId());
@ -219,7 +223,7 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
}
game.addSimultaneousEvent(new ZoneChangeEvent(permanent, permanent.getControllerId(), Zone.OUTSIDE, Zone.BATTLEFIELD));
if (permanent instanceof PermanentToken && created) {
game.addSimultaneousEvent(new CreatedTokenEvent(event.getSourceId(), (PermanentToken) permanent));
game.addSimultaneousEvent(new CreatedTokenEvent(source, (PermanentToken) permanent));
}
if (attacking && game.getCombat() != null && game.getActivePlayerId().equals(permanent.getControllerId())) {
game.getCombat().addAttackingCreature(permanent.getId(), game, attackedPlayer);

View file

@ -22,6 +22,8 @@ import mage.counters.Counters;
import mage.filter.FilterMana;
import mage.game.Game;
import mage.game.GameState;
import mage.game.events.CopiedStackObjectEvent;
import mage.game.events.CopyStackObjectEvent;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import mage.game.events.ZoneChangeEvent;
@ -243,7 +245,7 @@ public class Spell extends StackObjImpl implements Card {
if (!game.isSimulation()) {
game.informPlayers(getName() + " has been fizzled.");
}
counter(null, game);
counter(null, /*this.getSpellAbility()*/ game);
return false;
} else if (this.isEnchantment() && this.hasSubtype(SubType.AURA, game)) {
if (ability.getTargets().stillLegal(ability, game)) {
@ -265,7 +267,7 @@ public class Spell extends StackObjImpl implements Card {
EmptyToken token = new EmptyToken();
CardUtil.copyTo(token).from(card);
// The token that a resolving copy of a spell becomes isnt said to have been created. (2020-09-25)
if (token.putOntoBattlefield(1, game, ability.getSourceId(), getControllerId(), false, false, null, false)) {
if (token.putOntoBattlefield(1, game, ability, getControllerId(), false, false, null, false)) {
permId = token.getLastAddedToken();
flag = true;
}
@ -318,14 +320,14 @@ public class Spell extends StackObjImpl implements Card {
if (!game.isSimulation()) {
game.informPlayers(getName() + " has been fizzled.");
}
counter(null, game);
counter(null, /*this.getSpellAbility()*/ game);
return false;
}
} else if (isCopy()) {
EmptyToken token = new EmptyToken();
CardUtil.copyTo(token).from(card);
// The token that a resolving copy of a spell becomes isnt said to have been created. (2020-09-25)
token.putOntoBattlefield(1, game, ability.getSourceId(), getControllerId(), false, false, null, false);
token.putOntoBattlefield(1, game, ability, getControllerId(), false, false, null, false);
return true;
} else {
return controller.moveCards(card, Zone.BATTLEFIELD, ability, game, false, faceDown, false, null);
@ -394,21 +396,25 @@ public class Spell extends StackObjImpl implements Card {
}
@Override
public void counter(UUID sourceId, Game game) {
this.counter(sourceId, game, Zone.GRAVEYARD, false, ZoneDetail.NONE);
public void counter(Ability source, Game game) {
this.counter(source, game, Zone.GRAVEYARD, false, ZoneDetail.NONE);
}
@Override
public void counter(UUID sourceId, Game game, Zone zone, boolean owner, ZoneDetail zoneDetail) {
public void counter(Ability source, Game game, Zone zone, boolean owner, ZoneDetail zoneDetail) {
// source can be null for fizzled spells, found only one place with that usage -- Rebound Ability:
// event.getSourceId().equals(source.getSourceId())
// TODO: so later it must be replaced to another technics with non null source
UUID counteringSourceId = (source == null ? null : source.getSourceId());
this.countered = true;
if (!isCopy()) {
Player player = game.getPlayer(game.getControllerId(sourceId));
Player player = game.getPlayer(game.getControllerId(counteringSourceId));
if (player == null) {
player = game.getPlayer(getControllerId());
}
if (player != null) {
Ability counteringAbility = null;
MageObject counteringObject = game.getObject(sourceId);
MageObject counteringObject = game.getObject(counteringSourceId);
if (counteringObject instanceof StackObject) {
counteringAbility = ((StackObject) counteringObject).getStackAbility();
}
@ -692,13 +698,13 @@ public class Spell extends StackObjImpl implements Card {
}
@Override
public boolean turnFaceUp(Game game, UUID playerId) {
public boolean turnFaceUp(Ability source, Game game, UUID playerId) {
setFaceDown(false, game);
return true;
}
@Override
public boolean turnFaceDown(Game game, UUID playerId) {
public boolean turnFaceDown(Ability source, Game game, UUID playerId) {
setFaceDown(true, game);
return true;
}
@ -770,57 +776,57 @@ public class Spell extends StackObjImpl implements Card {
}
@Override
public boolean removeFromZone(Game game, Zone fromZone, UUID sourceId) {
return card.removeFromZone(game, fromZone, sourceId);
public boolean removeFromZone(Game game, Zone fromZone, Ability source) {
return card.removeFromZone(game, fromZone, source);
}
@Override
public boolean moveToZone(Zone zone, UUID sourceId, Game game, boolean flag) {
return moveToZone(zone, sourceId, game, flag, null);
public boolean moveToZone(Zone zone, Ability source, Game game, boolean flag) {
return moveToZone(zone, source, game, flag, null);
}
@Override
public boolean moveToZone(Zone zone, UUID sourceId, Game game, boolean flag, List<UUID> appliedEffects) {
public boolean moveToZone(Zone zone, Ability source, Game game, boolean flag, List<UUID> appliedEffects) {
// 706.10a If a copy of a spell is in a zone other than the stack, it ceases to exist.
// If a copy of a card is in any zone other than the stack or the battlefield, it ceases to exist.
// These are state-based actions. See rule 704.
if (this.isCopy() && zone != Zone.STACK) {
return true;
}
return card.moveToZone(zone, sourceId, game, flag, appliedEffects);
return card.moveToZone(zone, source, game, flag, appliedEffects);
}
@Override
public boolean moveToExile(UUID exileId, String name, UUID sourceId, Game game) {
return moveToExile(exileId, name, sourceId, game, null);
public boolean moveToExile(UUID exileId, String name, Ability source, Game game) {
return moveToExile(exileId, name, source, game, null);
}
@Override
public boolean moveToExile(UUID exileId, String name, UUID sourceId, Game game, List<UUID> appliedEffects) {
public boolean moveToExile(UUID exileId, String name, Ability source, Game game, List<UUID> appliedEffects) {
if (this.isCopy()) {
game.getStack().remove(this, game);
return true;
}
return this.card.moveToExile(exileId, name, sourceId, game, appliedEffects);
return this.card.moveToExile(exileId, name, source, game, appliedEffects);
}
@Override
public boolean putOntoBattlefield(Game game, Zone fromZone, UUID sourceId, UUID controllerId) {
public boolean putOntoBattlefield(Game game, Zone fromZone, Ability source, UUID controllerId) {
throw new UnsupportedOperationException("Unsupported operation");
}
@Override
public boolean putOntoBattlefield(Game game, Zone fromZone, UUID sourceId, UUID controllerId, boolean tapped) {
public boolean putOntoBattlefield(Game game, Zone fromZone, Ability source, UUID controllerId, boolean tapped) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public boolean putOntoBattlefield(Game game, Zone fromZone, UUID sourceId, UUID controllerId, boolean tapped, boolean facedown) {
public boolean putOntoBattlefield(Game game, Zone fromZone, Ability source, UUID controllerId, boolean tapped, boolean facedown) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public boolean putOntoBattlefield(Game game, Zone fromZone, UUID sourceId, UUID controllerId, boolean tapped, boolean facedown, List<UUID> appliedEffects) {
public boolean putOntoBattlefield(Game game, Zone fromZone, Ability source, UUID controllerId, boolean tapped, boolean facedown, List<UUID> appliedEffects) {
throw new UnsupportedOperationException("Not supported yet.");
}
@ -930,13 +936,13 @@ public class Spell extends StackObjImpl implements Card {
}
@Override
public void removeCounters(String name, int amount, Game game) {
card.removeCounters(name, amount, game);
public void removeCounters(String name, int amount, Ability source, Game game) {
card.removeCounters(name, amount, source, game);
}
@Override
public void removeCounters(Counter counter, Game game) {
card.removeCounters(counter, game);
public void removeCounters(Counter counter, Ability source, Game game) {
card.removeCounters(counter, source, game);
}
public Card getCard() {
@ -1025,8 +1031,8 @@ public class Spell extends StackObjImpl implements Card {
}
@Override
public void checkForCountersToAdd(Permanent permanent, Game game) {
card.checkForCountersToAdd(permanent, game);
public void checkForCountersToAdd(Permanent permanent, Ability source, Game game) {
card.checkForCountersToAdd(permanent, source, game);
}
@Override
@ -1037,7 +1043,7 @@ public class Spell extends StackObjImpl implements Card {
@Override
public StackObject createCopyOnStack(Game game, Ability source, UUID newControllerId, boolean chooseNewTargets, int amount) {
Spell spellCopy = null;
GameEvent gameEvent = GameEvent.getEvent(EventType.COPY_STACKOBJECT, this.getId(), source.getSourceId(), newControllerId, amount);
GameEvent gameEvent = new CopyStackObjectEvent(source, this, newControllerId, amount);
if (game.replaceEvent(gameEvent)) {
return null;
}
@ -1048,7 +1054,7 @@ public class Spell extends StackObjImpl implements Card {
if (chooseNewTargets) {
spellCopy.chooseNewTargets(game, newControllerId);
}
game.fireEvent(new GameEvent(EventType.COPIED_STACKOBJECT, spellCopy.getId(), this.getId(), newControllerId));
game.fireEvent(new CopiedStackObjectEvent(this, spellCopy, newControllerId));
}
return spellCopy;
}
@ -1078,12 +1084,12 @@ public class Spell extends StackObjImpl implements Card {
}
@Override
public boolean addAttachment(UUID permanentId, Game game) {
public boolean addAttachment(UUID permanentId, Ability source, Game game) {
throw new UnsupportedOperationException("Not supported."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public boolean removeAttachment(UUID permanentId, Game game) {
public boolean removeAttachment(UUID permanentId, Ability source, Game game) {
throw new UnsupportedOperationException("Not supported."); //To change body of generated methods, choose Tools | Templates.
}

View file

@ -5,6 +5,7 @@ import java.util.ArrayDeque;
import java.util.Date;
import java.util.UUID;
import mage.MageObject;
import mage.abilities.Ability;
import mage.constants.Zone;
import mage.constants.ZoneDetail;
import mage.game.Game;
@ -58,16 +59,16 @@ public class SpellStack extends ArrayDeque<StackObject> {
return false;
}
public boolean counter(UUID objectId, UUID sourceId, Game game) {
return counter(objectId, sourceId, game, Zone.GRAVEYARD, false, ZoneDetail.TOP);
public boolean counter(UUID objectId, Ability source, Game game) {
return counter(objectId, source, game, Zone.GRAVEYARD, false, ZoneDetail.TOP);
}
public boolean counter(UUID objectId, UUID sourceId, Game game, Zone zone, boolean owner, ZoneDetail zoneDetail) {
public boolean counter(UUID objectId, Ability source, Game game, Zone zone, boolean owner, ZoneDetail zoneDetail) {
// the counter logic is copied by some spells to handle replacement effects of the countered spell
// so if logic is changed here check those spells for needed changes too
// Concerned cards to check: Hinder, Spell Crumple
StackObject stackObject = getStackObject(objectId);
MageObject sourceObject = game.getObject(sourceId);
MageObject sourceObject = game.getObject(source.getSourceId());
if (stackObject != null && sourceObject != null) {
MageObject targetSourceObject = game.getObject(stackObject.getSourceId());
String counteredObjectName, targetSourceName;
@ -81,15 +82,15 @@ public class SpellStack extends ArrayDeque<StackObject> {
} else {
counteredObjectName = "Ability (" + stackObject.getStackAbility().getRule(targetSourceName) + ") of " + targetSourceName;
}
if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.COUNTER, objectId, sourceId, stackObject.getControllerId()))) {
if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.COUNTER, objectId, source, stackObject.getControllerId()))) {
if (!(stackObject instanceof Spell)) { // spells are removed from stack by the card movement
this.remove(stackObject, game);
}
stackObject.counter(sourceId, game, zone, owner, zoneDetail);
stackObject.counter(source, game, zone, owner, zoneDetail);
if (!game.isSimulation()) {
game.informPlayers(counteredObjectName + " is countered by " + sourceObject.getLogName());
}
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.COUNTERED, objectId, sourceId, stackObject.getControllerId()));
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.COUNTERED, objectId, source, stackObject.getControllerId()));
return true;
} else if (!game.isSimulation()) {
game.informPlayers(counteredObjectName + " could not be countered by " + sourceObject.getLogName());

View file

@ -20,6 +20,8 @@ import mage.cards.Card;
import mage.cards.FrameStyle;
import mage.constants.*;
import mage.game.Game;
import mage.game.events.CopiedStackObjectEvent;
import mage.game.events.CopyStackObjectEvent;
import mage.game.events.GameEvent;
import mage.game.events.ZoneChangeEvent;
import mage.game.permanent.Permanent;
@ -90,7 +92,7 @@ public class StackAbility extends StackObjImpl implements Ability {
if (!game.isSimulation()) {
game.informPlayers("Ability has been fizzled: " + getRule());
}
counter(null, game);
counter(null, /*this*/ game);
game.getStack().remove(this, game);
return false;
}
@ -100,13 +102,13 @@ public class StackAbility extends StackObjImpl implements Ability {
}
@Override
public void counter(UUID sourceId, Game game) {
public void counter(Ability source, Game game) {
// zone, owner, top ignored
this.counter(sourceId, game, Zone.GRAVEYARD, true, ZoneDetail.TOP);
this.counter(source, game, Zone.GRAVEYARD, true, ZoneDetail.TOP);
}
@Override
public void counter(UUID sourceId, Game game, Zone zone, boolean owner, ZoneDetail zoneDetail) {
public void counter(Ability source, Game game, Zone zone, boolean owner, ZoneDetail zoneDetail) {
//20100716 - 603.8
if (ability instanceof StateTriggeredAbility) {
((StateTriggeredAbility) ability).counter(game);
@ -579,7 +581,7 @@ public class StackAbility extends StackObjImpl implements Ability {
public StackObject createCopyOnStack(Game game, Ability source, UUID newControllerId, boolean chooseNewTargets, int amount) {
StackAbility newStackAbility = null;
GameEvent gameEvent = GameEvent.getEvent(GameEvent.EventType.COPY_STACKOBJECT, this.getId(), source.getSourceId(), newControllerId, amount);
GameEvent gameEvent = new CopyStackObjectEvent(source, this, newControllerId, amount);
if (game.replaceEvent(gameEvent)) {
return null;
}
@ -596,7 +598,7 @@ public class StackAbility extends StackObjImpl implements Ability {
newAbility.getTargets().chooseTargets(outcome, newControllerId, newAbility, false, game, false);
}
}
game.fireEvent(new GameEvent(GameEvent.EventType.COPIED_STACKOBJECT, newStackAbility.getId(), this.getId(), newControllerId));
game.fireEvent(new CopiedStackObjectEvent(this, newStackAbility, newControllerId));
}
return newStackAbility;
}

View file

@ -16,13 +16,17 @@ public interface StackObject extends MageObject, Controllable {
UUID getSourceId();
void counter(UUID sourceId, Game game);
/**
*
* @param source null for fizzled events (sourceId will be null)
* @param game
*/
void counter(Ability source, Game game);
void counter(UUID sourceId, Game game, Zone zone, boolean owner, ZoneDetail zoneDetail);
void counter(Ability source, Game game, Zone zone, boolean owner, ZoneDetail zoneDetail);
Ability getStackAbility();
// int getConvertedManaCost();
boolean chooseNewTargets(Game game, UUID playerId, boolean forceChange, boolean onlyOneTarget, FilterPermanent filterNewTarget);
StackObject createCopyOnStack(Game game, Ability source, UUID newControllerId, boolean chooseNewTargets);

View file

@ -27,7 +27,7 @@ public class CombatDamageStep extends Step {
@Override
public void priority(Game game, UUID activePlayerId, boolean resuming) {
game.fireEvent(new GameEvent(EventType.COMBAT_DAMAGE_STEP_PRIORITY, null, null, activePlayerId));
game.fireEvent(new GameEvent(GameEvent.EventType.COMBAT_DAMAGE_STEP_PRIORITY, null, null, activePlayerId));
super.priority(game, activePlayerId, resuming);
}

View file

@ -28,7 +28,6 @@ public class DrawStep extends Step {
Player activePlayer = game.getPlayer(activePlayerId);
//20091005 - 504.1/703.4c
activePlayer.drawCards(1, null, game);
// game.saveState();
game.applyEffects();
super.beginStep(game, activePlayerId);
}

View file

@ -27,7 +27,7 @@ public class FirstCombatDamageStep extends Step {
@Override
public void priority(Game game, UUID activePlayerId, boolean resuming) {
game.fireEvent(new GameEvent(EventType.COMBAT_DAMAGE_STEP_PRIORITY, null, null, activePlayerId));
game.fireEvent(new GameEvent(GameEvent.EventType.COMBAT_DAMAGE_STEP_PRIORITY, null, null, activePlayerId));
super.priority(game, activePlayerId, resuming);
}

View file

@ -171,7 +171,7 @@ public abstract class Phase implements Serializable {
public void postPriority(Game game, UUID activePlayerId) {
currentStep.endStep(game, activePlayerId);
//20091005 - 500.4/703.4n
game.emptyManaPools();
game.emptyManaPools(null);
//20091005 - 500.9
playExtraSteps(game, currentStep.getType());
}

View file

@ -6,6 +6,7 @@ import mage.constants.TurnPhase;
import mage.counters.CounterType;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.PhaseChangedEvent;
import mage.game.permanent.Permanent;
import mage.game.stack.Spell;
import mage.game.stack.StackObject;
@ -121,14 +122,14 @@ public class Turn implements Serializable {
}
if (!isEndTurnRequested() || phase.getType() == TurnPhase.END) {
currentPhase = phase;
game.fireEvent(new GameEvent(GameEvent.EventType.PHASE_CHANGED, activePlayer.getId(), null, activePlayer.getId()));
game.fireEvent(new PhaseChangedEvent(activePlayer.getId(), null));
if (!game.getState().getTurnMods().skipPhase(activePlayer.getId(), currentPhase.getType())) {
if (phase.play(game, activePlayer.getId())) {
if (game.executingRollback()) {
return false;
}
//20091005 - 500.4/703.4n
game.emptyManaPools();
game.emptyManaPools(null);
game.saveState(false);
//20091005 - 500.8
@ -155,7 +156,7 @@ public class Turn implements Serializable {
} while (phase.type != phaseType);
if (phase.resumePlay(game, stepType, wasPaused)) {
//20091005 - 500.4/703.4n
game.emptyManaPools();
game.emptyManaPools(null);
//game.saveState();
//20091005 - 500.8
playExtraPhases(game, phase.getType());
@ -169,14 +170,14 @@ public class Turn implements Serializable {
if (!game.getState().getTurnMods().skipPhase(activePlayerId, currentPhase.getType())) {
if (phase.play(game, activePlayerId)) {
//20091005 - 500.4/703.4n
game.emptyManaPools();
game.emptyManaPools(null);
//game.saveState();
//20091005 - 500.8
playExtraPhases(game, phase.getType());
}
}
if (!currentPhase.equals(phase)) { // phase was changed from the card
game.fireEvent(new GameEvent(GameEvent.EventType.PHASE_CHANGED, activePlayerId, null, activePlayerId));
game.fireEvent(new PhaseChangedEvent(activePlayerId, null));
break;
}
}
@ -223,7 +224,7 @@ public class Turn implements Serializable {
phase = new EndPhase();
}
currentPhase = phase;
game.fireEvent(new GameEvent(GameEvent.EventType.PHASE_CHANGED, activePlayerId, extraPhaseTurnMod.getId(), activePlayerId));
game.fireEvent(new PhaseChangedEvent(activePlayerId, extraPhaseTurnMod));
Player activePlayer = game.getPlayer(activePlayerId);
if (activePlayer != null && !game.isSimulation()) {
game.informPlayers(activePlayer.getLogName() + " starts an additional " + phase.getType().toString() + " phase");
@ -256,7 +257,7 @@ public class Turn implements Serializable {
while (!game.hasEnded() && !game.getStack().isEmpty()) {
StackObject stackObject = game.getStack().peekFirst();
if (stackObject instanceof Spell) {
((Spell) stackObject).moveToExile(null, "", source.getSourceId(), game);
((Spell) stackObject).moveToExile(null, "", source, game);
} else {
game.getStack().remove(stackObject, game); // stack ability
}