package mage.game.events; import mage.ApprovingObject; import mage.MageIdentifier; import mage.abilities.Ability; import mage.constants.Zone; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import java.util.UUID; /** * @author BetaSteward_at_googlemail.com */ public class GameEvent implements Serializable { protected EventType type; protected UUID id; protected UUID targetId; 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: // for counters: event is result of effect (+1 from planeswalkers is cost, not effect) // 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; protected List appliedEffects = new ArrayList<>(); protected ApprovingObject approvingObject; // e.g. the approving object for casting a spell from non hand zone protected UUID customEventType = null; public enum EventType { //Game events BEGINNING, PREVENT_DAMAGE, PREVENTED_DAMAGE, //Turn-based events PLAY_TURN, EXTRA_TURN, CHANGE_PHASE, PHASE_CHANGED, CHANGE_STEP, STEP_CHANGED, BEGINNING_PHASE, BEGINNING_PHASE_PRE, BEGINNING_PHASE_POST, // The normal beginning phase -- at the beginning of turn BEGINNING_PHASE_EXTRA, BEGINNING_PHASE_PRE_EXTRA, BEGINNING_PHASE_POST_EXTRA, // Extra beginning phase, 'as turn begun' watchers don't want to react on thoses. UNTAP_STEP_PRE, UNTAP_STEP, UNTAP_STEP_POST, UPKEEP_STEP_PRE, UPKEEP_STEP, UPKEEP_STEP_POST, DRAW_STEP_PRE, DRAW_STEP, DRAW_STEP_POST, PRECOMBAT_MAIN_PHASE, PRECOMBAT_MAIN_PHASE_PRE, PRECOMBAT_MAIN_PHASE_POST, PRECOMBAT_MAIN_STEP_PRE, PRECOMBAT_MAIN_STEP, PRECOMBAT_MAIN_STEP_POST, COMBAT_PHASE, COMBAT_PHASE_PRE, COMBAT_PHASE_POST, BEGIN_COMBAT_STEP_PRE, BEGIN_COMBAT_STEP, BEGIN_COMBAT_STEP_POST, DECLARE_ATTACKERS_STEP_PRE, DECLARE_ATTACKERS_STEP, DECLARE_ATTACKERS_STEP_POST, DECLARE_BLOCKERS_STEP_PRE, DECLARE_BLOCKERS_STEP, DECLARE_BLOCKERS_STEP_POST, COMBAT_DAMAGE_STEP, COMBAT_DAMAGE_STEP_PRE, COMBAT_DAMAGE_STEP_PRIORITY, COMBAT_DAMAGE_STEP_POST, END_COMBAT_STEP_PRE, END_COMBAT_STEP, END_COMBAT_STEP_POST, POSTCOMBAT_MAIN_PHASE, POSTCOMBAT_MAIN_PHASE_PRE, POSTCOMBAT_MAIN_PHASE_POST, POSTCOMBAT_MAIN_STEP_PRE, POSTCOMBAT_MAIN_STEP, POSTCOMBAT_MAIN_STEP_POST, END_PHASE, END_PHASE_PRE, END_PHASE_POST, END_TURN_STEP_PRE, END_TURN_STEP, END_TURN_STEP_POST, CLEANUP_STEP_PRE, CLEANUP_STEP, CLEANUP_STEP_POST, EMPTY_MANA_POOL, AT_END_OF_TURN, //player events /* ZONE_CHANGE targetId id of the zone changing object sourceId sourceId of the ability with the object moving effect WARNING: can be null if moved by game rules (e.g. draw in draw step, discard in cleanup step, fizzled spell) playerId controller of the moved object amount not used for this event flag not used for this event */ ZONE_CHANGE, ZONE_CHANGE_GROUP, // between two specific zones only; TODO: rework all usages to ZONE_CHANGE_BATCH instead, see #11895 ZONE_CHANGE_BATCH, // all zone changes that occurred from a single effect DRAW_TWO_OR_MORE_CARDS, // event calls for multi draws only (if player draws 2+ cards at once) DRAW_CARD, DREW_CARD, EXPLORE, EXPLORED, // targetId is exploring permanent, playerId is its controller 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, // playerId is the player who investigated KICKED, /* DISCARD_CARD flag event is result of effect (1) or result of cost (0) */ DISCARD_CARD, DISCARDED_CARD, DISCARDED_CARDS, CYCLE_CARD, CYCLED_CARD, CYCLE_DRAW, /* CLASHED (one event fired for each player involved) playerId the id of the clashing player flag true = playerId won the clash targetId the id of the other player in the clash */ CLASH, CLASHED, DAMAGE_PLAYER, /* MILL_CARDS playerId the id of the player milling the card (not the source's controller) targetId the id of the card milled */ MILL_CARDS, /* MILLED_CARDS playerId the id of the player milling the card (not the source's controller) targetId the id of the card milled */ MILLED_CARD, /* MILLED_CARDS_BATCH_FOR_ONE_PLAYER, combines all MILLED_CARD events for a player milling card at the same time in a single batch playerId the id of the player whose batch it is */ MILLED_CARDS_BATCH_FOR_ONE_PLAYER, /* MILLED_CARDS_BATCH_FOR_ALL, combines all MILLED_CARD events for any player in a single batch */ MILLED_CARDS_BATCH_FOR_ALL, /* DAMAGED_PLAYER targetId the id of the damaged player sourceId sourceId of the ability which caused the damage playerId the id of the damaged player amount amount of damage flag true = combat damage - other damage = false */ DAMAGED_PLAYER, /* DAMAGED_BATCH_FOR_PLAYERS, combines all player damage events to a single batch (event) */ DAMAGED_BATCH_FOR_PLAYERS, /* DAMAGED_BATCH_FOR_ONE_PLAYER combines all player damage events to a single batch (event) and split it per damaged player targetId the id of the damaged player (playerId won't work for batch) */ DAMAGED_BATCH_FOR_ONE_PLAYER, /* DAMAGED_BATCH_FOR_ALL includes all damage events, both permanent damage and player damage, in single batch event */ DAMAGED_BATCH_FOR_ALL, /* DAMAGED_BATCH_FIRED * Does not contain any info on damage events, and can fire even when all damage is prevented. * Fire any time a DAMAGED_BATCH_FOR_ALL could have fired (combat & noncombat). * It is not a batch event (doesn't contain sub events), the name is a little ambiguous. */ DAMAGED_BATCH_COULD_HAVE_FIRED, /* 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 damaged 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 sourceId of the ability which caused the lose playerId the id of the player loosing life amount amount of life loss flag true = from combat damage - other from non combat damage */ LOST_LIFE_BATCH, /* LOST_LIFE_BATCH combines all player life lost events to a single batch (event) */ PLAY_LAND, LAND_PLAYED, CREATURE_CHAMPIONED, /* CREATURE_CHAMPIONED targetId the id of the creature that was championed sourceId sourceId of the creature using the champion ability playerId the id of the controlling player */ CREW_VEHICLE, /* CREW_VEHICLE targetId the id of the creature that will crew a vehicle sourceId sourceId of the vehicle playerId the id of the controlling player */ CREWED_VEHICLE, /* CREWED_VEHICLE targetId the id of the creature that crewed a vehicle sourceId sourceId of the vehicle playerId the id of the controlling player */ VEHICLE_CREWED, /* VEHICLE_CREWED targetId the id of the vehicle sourceId sourceId of the vehicle playerId the id of the controlling player */ SADDLE_MOUNT, /* SADDLE_MOUNT targetId the id of the creature that will saddle a mount sourceId sourceId of the mount playerId the id of the controlling player */ SADDLED_MOUNT, /* SADDLED_MOUNT targetId the id of the creature that saddled a mount sourceId sourceId of the mount playerId the id of the controlling player */ MOUNT_SADDLED, /* MOUNT_SADDLED targetId the id of the mount sourceId sourceId of the mount playerId the id of the controlling player */ CAST_SPELL, CAST_SPELL_LATE, /* SPELL_CAST, CAST_SPELL_LATE targetId id of the spell that's try to cast sourceId sourceId of the spell that's try to cast playerId player that try to cast the spell amount not used for this event flag not used for this event zone zone the spell is cast from (main card) */ SPELL_CAST, /* SPELL_CAST targetId id of the spell that's cast sourceId sourceId of the spell that's cast playerId player that casts the spell amount not used for this event flag not used for this event zone zone the spell is cast from */ ACTIVATE_ABILITY, ACTIVATED_ABILITY, /* ACTIVATE_ABILITY, ACTIVATED_ABILITY, WARNING, do not use choose dialogs inside, can be calls multiple types, e.g. on playable checking targetId id of the ability to activate / use sourceId sourceId of the object with that ability playerId player that tries to use this ability */ TAKE_SPECIAL_ACTION, TAKEN_SPECIAL_ACTION, /* TAKE_SPECIAL_ACTION, TAKEN_SPECIAL_ACTION, targetId id of the ability to activate / use sourceId sourceId of the object with that ability playerId player that tries to use this ability */ TAKE_SPECIAL_MANA_PAYMENT, TAKEN_SPECIAL_MANA_PAYMENT, /* TAKE_SPECIAL_MANA_PAYMENT, TAKEN_SPECIAL_MANA_PAYMENT targetId id of the ability to activate / use sourceId sourceId of the object with that ability playerId player that tries to use this ability */ TRIGGERED_ABILITY, RESOLVING_ABILITY, /* 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 playerId player the mana is added to the mana pool for mana the mana added */ ADD_MANA, /* MANA_ADDED targetId id of the ability that added the mana sourceId sourceId of the ability that added the mana playerId player the mana is added to the mana pool for mana the mana added */ MANA_ADDED, /* MANA_PAID targetId id if the ability the mana was paid for (not the sourceId) 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 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 sourceId = targetId playerId controller of the spell or stack ability amount not used for this event */ TARGETS_VALID, /* COUNTER targetId id of the spell or id of stack ability sourceId sourceId of the ability countering the spell or stack ability playerId controller of the countered spell or stack ability amount not used for this event flag not used for this event */ COUNTER, COUNTERED, DECLARING_ATTACKERS, DECLARED_ATTACKERS, /* DECLARE_ATTACKER REPLACE EVENT - can be used to replace attack declaration targetId id of the defending player or planeswalker attacked sourceId id of the attacking creature playerId player defining the attacking creatures */ DECLARE_ATTACKER, /* ATTACKER_DECLARED targetId id of the defending player or planeswalker attacked sourceId id of the attacking creature playerId player defining the attacking creatures amount not used for this event flag not used for this event */ ATTACKER_DECLARED, /* DECLARING_BLOCKERS targetId attackerId sourceId not used for this event playerId attackerId amount not used for this event flag not used for this event */ DEFENDER_ATTACKED, DECLARING_BLOCKERS, DECLARED_BLOCKERS, DECLARE_BLOCKER, /* BLOCKER_DECLARED raise one time for each declared blocker (e.g. multiple events per attacker allows) warning, must use for rules: becomes blocked by a creature rules ref: Acolyte of the Inferno’s last ability will trigger once for each creature that blocks it. Each of those creatures will be dealt 2 damage. (2015-06-22) targetId attacker id sourceId blocker id playerId blocker controller id */ BLOCKER_DECLARED, /* CREATURE_BLOCKED raise one time per attacker (e.g. only one event per attacker allows) warning, must use for rules: xxx becomes blocked, rules ref: Rakdos Roustabout An ability that triggers when a creature becomes blocked triggers only once if two or more creatures block it. (2019-01-25) targetId attacker id sourceId not used for this event playerId not used for this event */ CREATURE_BLOCKED, CREATURE_BLOCKS, BATCH_BLOCK_NONCOMBAT, UNBLOCKED_ATTACKER, SEARCH_LIBRARY, LIBRARY_SEARCHED, SHUFFLE_LIBRARY, LIBRARY_SHUFFLED, ENCHANT_PLAYER, ENCHANTED_PLAYER, CAN_TAKE_MULLIGAN, SCRY, SCRIED, SCRY_TO_BOTTOM, SURVEIL, SURVEILED, PROLIFERATE, PROLIFERATED, FATESEALED, FLIP_COIN, COIN_FLIPPED, REPLACE_ROLLED_DIE, // for Clam-I-Am workaround only ROLL_DIE, DIE_ROLLED, ROLL_DICE, DICE_ROLLED, PLANESWALK, PLANESWALKED, PAID_CUMULATIVE_UPKEEP, DIDNT_PAY_CUMULATIVE_UPKEEP, PAY_LIFE, LIFE_PAID, CASCADE_LAND, LEARN, //permanent events ENTERS_THE_BATTLEFIELD_SELF, /* 616.1a If any of the replacement and/or prevention effects are self-replacement effects (see rule 614.15), one of them must be chosen. If not, proceed to rule 616.1b. */ ENTERS_THE_BATTLEFIELD_CONTROL, // 616.1b ENTERS_THE_BATTLEFIELD_COPY, // 616.1c ENTERS_THE_BATTLEFIELD, // 616.1d TAP, /* TAPPED, targetId tapped permanent sourceId id of the ability's source (can be null for standard tap actions like combat) playerId source's controller, null if no source amount not used for this event flag is it tapped for combat */ TAPPED, /* TAPPED_FOR_MANA During calculation of the available mana for a player the "TappedForMana" event is fired to simulate triggered mana production. By checking the inCheckPlayableState these events are handled to give back only the available mana of instead really producing mana. IMPORTANT: Triggered non mana abilities have to ignore the event if game.inCheckPlayableState is true. */ TAPPED_FOR_MANA, /* TAPPED_BATCH combine all TAPPED events occuring at the same time in a single event */ TAPPED_BATCH, UNTAP, /* UNTAPPED, targetId untapped permanent sourceId not used for this event // TODO: add source for untap? playerId controller of permanent // TODO: replace by source controller of untap? need to check every usage if so. amount not used for this event flag true if untapped during untap step (event is checked at upkeep so can't trust the current Phase) */ UNTAPPED, /* UNTAPPED_BATCH combine all UNTAPPED events occuring at the same time in a single event */ UNTAPPED_BATCH, FLIP, FLIPPED, TRANSFORMING, TRANSFORMED, ADAPT, BECOMES_MONSTROUS, /* BECOMES_EXERTED targetId id of the exerted 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, GAINS_CLASS_LEVEL, /* CREATURE_ENLISTED targetId id of the enlisted creature sourceId id of the creature that enlisted playerId player who controls the creatures amount not used for this event flag not used for this event */ CREATURE_ENLISTED, /* BECOMES_MONARCH targetId playerId of the player that becomes the monarch sourceId id of the source object that created that effect, if no effect exist it's null playerId playerId of the player that becomes the monarch amount not used for this event flag not used for this event */ BECOME_MONARCH, BECOMES_MONARCH, TOOK_INITIATIVE, BECOMES_DAY_NIGHT, MEDITATED, PHASE_OUT, PHASED_OUT, PHASE_IN, PHASED_IN, TURN_FACE_UP, TURNED_FACE_UP, TURN_FACE_DOWN, TURNED_FACE_DOWN, /* OPTION_USED targetId originalId of the ability that triggered the event sourceId sourceId of the ability that triggered the event playerId controller of the ability amount not used for this event flag not used for this event */ OPTION_USED, DAMAGE_PERMANENT, DAMAGED_PERMANENT, /* DAMAGED_BATCH_FOR_PERMANENTS combine all permanent damage events to a single batch (event) */ DAMAGED_BATCH_FOR_PERMANENTS, /* DAMAGED_BATCH_FOR_ONE_PERMANENT combines all permanent damage events to a single batch (event) and split it per damaged permanent */ DAMAGED_BATCH_FOR_ONE_PERMANENT, DESTROY_PERMANENT, /* DESTROY_PERMANENT_BY_LEGENDARY_RULE targetId id of the permanent to destroy playerId controller of the permanent to detroy */ DESTROY_PERMANENT_BY_LEGENDARY_RULE, /* DESTROYED_PERMANENT targetId id of the destroyed creature sourceId sourceId of the ability with the destroy effect playerId controller of the creature amount not used for this event flag true if no regeneration is allowed */ DESTROYED_PERMANENT, SACRIFICE_PERMANENT, SACRIFICED_PERMANENT, FIGHTED_PERMANENT, BATCH_FIGHT, EXPLOITED_CREATURE, EVOLVED_CREATURE, EMBALMED_CREATURE, ETERNALIZED_CREATURE, ATTACH, 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, /* REMOVE_COUNTER, REMOVE_COUNTERS, COUNTER_REMOVED, COUNTERS_REMOVED targetId id of the permanent or player losing counter(s) sourceId id of the ability removing them playerId player who controls the ability removing the counters amount number of counters being removed data name of the counter(s) being removed */ REMOVE_COUNTER, COUNTER_REMOVED, REMOVE_COUNTERS, COUNTERS_REMOVED, LOSE_CONTROL, /* LOST_CONTROL targetId id of the creature that lost control sourceId null playerId player that controls the creature before amount not used for this event flag not used for this event */ LOST_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, CREATED_TOKENS, /* REGENERATE targetId id of the creature to regenerate sourceId sourceId of the effect doing the regeneration playerId controller of the creature amount not used for this event flag not used for this event */ REGENERATE, REGENERATED, CHANGE_COLOR, COLOR_CHANGED, NUMBER_OF_TRIGGERS, //combat events COMBAT_DAMAGE_APPLIED, SELECTED_ATTACKER, SELECTED_BLOCKER, /* voting targetId player who voting sourceId sourceId of the effect doing the voting playerId player who deciding about voting, can be changed by replace events amount not used for this event flag not used for this event */ VOTE, VOTED, /* dungeons targetId id of the room sourceId sourceId of the ability causing player to venture playerId player in the dungeon */ ROOM_ENTERED, VENTURE, VENTURED, DUNGEON_COMPLETED, TEMPTED_BY_RING, RING_BEARER_CHOSEN, REMOVED_FROM_COMBAT, // targetId id of permanent removed from combat FORETOLD, // targetId id of card foretold FORETELL, // targetId id of card foretell playerId id of the controller /* villainous choice targetId player making the choice sourceId sourceId of the ability forcing the choice playerId controller of the ability forcing the choice amount number of times choice is repeated flag not used for this event */ FACE_VILLAINOUS_CHOICE, /* DISCOVER targetId not used for this event sourceId sourceId of the ability discovering playerId controller of the ability amount discover value flag not used for this event */ DISCOVERED, /* Exiled while crafting (see Market Gnome) targetId the permanent exiled sourceId of the craft ability playerId the player crafting */ EXILED_WHILE_CRAFTING, /* Solving a Case targetId the permanent being solved sourceId of the ability solving playerId the player solving */ SOLVE_CASE, CASE_SOLVED, /* Become suspected targetId the permanent being suspected sourceId of the ability suspecting playerId the player suspecting */ BECOME_SUSPECTED, /* Evidence collected targetId same as sourceId sourceId of the ability for the cost playerId the player paying the cost */ EVIDENCE_COLLECTED, /* Mentored Creature targetId creature that was mentored sourceId of the mentor ability playerId controller of the creature mentoring */ MENTORED_CREATURE, /* the card becomes plotted targetId card that was plotted sourceId of the plotting ability (may be the card itself or another one) playerId owner of the plotted card (the one able to cast the card) */ BECOME_PLOTTED, /* the player foraged targetId same as sourceId sourceId of the ability playerId player who foraged */ FORAGED, /* gave a gift targetId the player who received the gift sourceId of the ability playerId player who gave the gift */ GAVE_GIFT, //custom events CUSTOM_EVENT } public GameEvent(EventType type, UUID targetId, Ability source, UUID playerId) { this(type, null, targetId, source, playerId, 0, false); } 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, Ability source, UUID playerId, int amount, boolean flag) { this(type, null, targetId, source, playerId, amount, flag); } 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, 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, Ability source, UUID playerId, int amount) { return new GameEvent(type, targetId, source, playerId, amount, false); } 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, 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, 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, Ability source, UUID playerId, int amount) { return new GameEvent(customEventType, targetId, source, playerId, amount, false); } public static GameEvent getEvent(UUID customEventType, UUID targetId, Ability source, UUID playerId) { return new GameEvent(customEventType, targetId, source, playerId); } 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, Ability source, UUID playerId, int amount, boolean flag, ApprovingObject approvingObject ) { this.type = type; this.customEventType = customEventType; this.targetId = targetId; this.sourceId = source == null ? null : source.getSourceId(); // We only keep the sourceId from the whole source. this.amount = amount; this.playerId = playerId; this.flag = flag; this.approvingObject = approvingObject; this.id = UUID.randomUUID(); } public EventType getType() { return type; } public UUID getCustomEventType() { return customEventType; } public UUID getId() { return id; } /** * Some batch events can contain multiple events list, see BatchGameEvent for usage * * @return */ public UUID getTargetId() { return targetId; } public void setTargetId(UUID targetId) { this.targetId = targetId; } public UUID getSourceId() { return sourceId; } public void setPlayerId(UUID playerId) { this.playerId = playerId; } public UUID getPlayerId() { return playerId; } public int getAmount() { return amount; } public void setAmount(int amount) { this.amount = amount; } public void setAmountForCounters(int amount, boolean isEffect) { this.amount = amount; // cost event must be "transformed" to effect event, as example: // planeswalker's +1 cost will be affected by Pir, Imaginative Rascal (1 + 1) and applied as effect by Doubling Season (2 * 2) // https://github.com/magefree/mage/issues/5802 if (isEffect) { setFlag(true); } } public boolean getFlag() { return flag; } public void setFlag(boolean flag) { this.flag = flag; } public String getData() { return data; } public void setData(String data) { this.data = data; } public Zone getZone() { return zone; } public void setZone(Zone zone) { this.zone = zone; } /** * Returns possibly approving object that allowed the creation of the event. * * @return */ public ApprovingObject getAdditionalReference() { return approvingObject; } public void setAdditionalReference(ApprovingObject approvingObject) { this.approvingObject = approvingObject; } /** * used to store which replacement effects were already applied to an event * or any modified events that may replace it *

* 614.5. A replacement effect doesn't invoke itself repeatedly; it gets * only one opportunity to affect an event or any modified events that may * replace it. Example: A player controls two permanents, each with an * ability that reads "If a creature you control would deal damage to a * creature or player, it deals double that damage to that creature or * player instead." A creature that normally deals 2 damage will deal 8 * damage--not just 4, and not an infinite amount. * * @return */ public List getAppliedEffects() { return appliedEffects; } public boolean isCustomEvent(UUID customEventType) { return type == EventType.CUSTOM_EVENT && this.customEventType.equals(customEventType); } public void addAppliedEffects(List appliedEffects) { if (appliedEffects != null) { this.appliedEffects.addAll(appliedEffects); } } public void setAppliedEffects(List appliedEffects) { if (appliedEffects != null) { if (this.appliedEffects.isEmpty()) { this.appliedEffects = appliedEffects; // Use object refecence to handle that an replacement effect can only be once applied to an event } else { this.appliedEffects.addAll(appliedEffects); } } } public boolean hasApprovingIdentifier(MageIdentifier identifier) { if (approvingObject == null) { return false; } if (identifier.equals(MageIdentifier.Default)) { return false; } 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 * for now, having the setter helps find all that do not provide an Ability source, * so keeping it is worthwhile until a thoughtfull cleanup. * * @param sourceId */ protected void setSourceId(UUID sourceId) { this.sourceId = sourceId; } @Override public String toString() { return this.type.toString(); } }