mirror of
https://github.com/magefree/mage.git
synced 2026-01-09 20:32:06 -08:00
rework batch events (#13066)
* add new framework for batch triggers apply for tapped, untapped, sacrificed, milled simplify Ob Nixilis, Captive Kingpin * add a verify check * fix mistakes * add simple tests * another test * zone change - enters battlefield * zone change: not battlefield * zone change - leaves battlefield * fix Kaya Spirit's Justice * rename OneOrMoreCombatDamagePlayerTriggeredAbility * refactor OneOrMoreDamagePlayerTriggeredAbility * new YoureDealtDamageTriggeredAbility * new OpponentDealtNoncombatDamageTriggeredAbility * rework Risona, Asari Commander * simplify War Elemental * Add damage batch by source rework some delayed triggered abilities * fix Mindblade Render * rework Initiative and a few others * [temp] initiative test * refactor: common style for DealsDamageSourceTriggeredAbility * refactor cards to use common DealsDamageSourceTriggeredAbility * update damage players batch triggers * fix mistake in initiative * new DealtDamageAnyTriggeredAbility * new DealtCombatDamageToSourceTriggeredAbility * update dealt damage to permanent batch triggered abilities * refactor Hot Soup and param in DealtDamageAttachedTriggeredAbility * a few more permanent batch triggered abilities * fix mistake * update some more damage batch triggers * add test for Phyrexian Negator * update Felix Five-Boots and enable test update Wayta, Trainer Prodigy to align * update damage batch by source triggers * undo mistaken change * fix verify * cleanup unused methods * Revert "[temp] initiative test" This reverts commit11ed19295f. * Revert "add a verify check" This reverts commite7de47a656. * fixes from checking text discrepancies * fix Shriekwood Devourer * merge fix --------- Co-authored-by: Susucre <34709007+Susucre@users.noreply.github.com>
This commit is contained in:
parent
cef2a1edc8
commit
d06d594934
192 changed files with 2411 additions and 3363 deletions
38
Mage/src/main/java/mage/abilities/BatchTriggeredAbility.java
Normal file
38
Mage/src/main/java/mage/abilities/BatchTriggeredAbility.java
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
package mage.abilities;
|
||||
|
||||
import mage.game.Game;
|
||||
import mage.game.events.BatchEvent;
|
||||
import mage.game.events.GameEvent;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Batch triggers (e.g. 'When... one or more ..., ')
|
||||
* require additional logic to check the events in the batch.
|
||||
* Parametrized on the individual game event type.
|
||||
*
|
||||
* @see mage.game.events.BatchEvent
|
||||
*
|
||||
* @author Susucr, xenohedron
|
||||
*/
|
||||
public interface BatchTriggeredAbility<T extends GameEvent> extends TriggeredAbility {
|
||||
|
||||
/**
|
||||
* Some events in the batch may not be relevant to the trigger logic.
|
||||
* If so, use this method to exclude them.
|
||||
*/
|
||||
default boolean checkEvent(T event, Game game) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* For use in checkTrigger - streams all events that pass the event check
|
||||
*/
|
||||
default List<T> getFilteredEvents(BatchEvent<T> event, Game game) {
|
||||
return event.getEvents()
|
||||
.stream()
|
||||
.filter(e -> checkEvent(e, game))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package mage.abilities.common;
|
||||
|
||||
import mage.abilities.BatchTriggeredAbility;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.constants.Zone;
|
||||
|
|
@ -7,13 +8,15 @@ import mage.filter.FilterPermanent;
|
|||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.TappedBatchEvent;
|
||||
import mage.game.events.TappedEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
||||
/**
|
||||
* @author Susucr
|
||||
*/
|
||||
public class BecomesTappedOneOrMoreTriggeredAbility extends TriggeredAbilityImpl {
|
||||
public class BecomesTappedOneOrMoreTriggeredAbility extends TriggeredAbilityImpl implements BatchTriggeredAbility<TappedEvent> {
|
||||
|
||||
protected FilterPermanent filter;
|
||||
private final FilterPermanent filter;
|
||||
|
||||
public BecomesTappedOneOrMoreTriggeredAbility(Zone zone, Effect effect, boolean optional, FilterPermanent filter) {
|
||||
super(zone, effect, optional);
|
||||
|
|
@ -36,13 +39,14 @@ public class BecomesTappedOneOrMoreTriggeredAbility extends TriggeredAbilityImpl
|
|||
return event.getType() == GameEvent.EventType.TAPPED_BATCH;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEvent(TappedEvent event, Game game) {
|
||||
Permanent permanent = game.getPermanent(event.getTargetId());
|
||||
return permanent != null && filter.match(permanent, getControllerId(), this, game);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
TappedBatchEvent batchEvent = (TappedBatchEvent) event;
|
||||
return batchEvent
|
||||
.getTargetIds()
|
||||
.stream()
|
||||
.map(game::getPermanent)
|
||||
.anyMatch(p -> filter.match(p, getControllerId(), this, game));
|
||||
return !getFilteredEvents((TappedBatchEvent) event, game).isEmpty();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,15 @@
|
|||
package mage.abilities.common;
|
||||
|
||||
import mage.abilities.BatchTriggeredAbility;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.DamagedBatchForPlayersEvent;
|
||||
import mage.game.events.DamagedEvent;
|
||||
import mage.game.events.DamagedBatchForOnePlayerEvent;
|
||||
import mage.game.events.DamagedPlayerEvent;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* A triggered ability for whenever one or more creatures deal combat damage to
|
||||
* you. Has an optional component for setting the target pointer to the opponent
|
||||
|
|
@ -19,87 +17,47 @@ import java.util.UUID;
|
|||
*
|
||||
* @author alexander-novo
|
||||
*/
|
||||
public class CombatDamageDealtToYouTriggeredAbility extends TriggeredAbilityImpl {
|
||||
public class CombatDamageDealtToYouTriggeredAbility extends TriggeredAbilityImpl implements BatchTriggeredAbility<DamagedPlayerEvent> {
|
||||
|
||||
// Whether the ability should set a target targetting the opponent who
|
||||
// Whether the ability should set a target targeting the opponent who
|
||||
// controls the creatures who dealt damage to you
|
||||
private final boolean setTarget;
|
||||
private final boolean setTargetPointer;
|
||||
|
||||
/**
|
||||
* @param effect The effect that should happen when the ability resolves
|
||||
*/
|
||||
public CombatDamageDealtToYouTriggeredAbility(Effect effect) {
|
||||
this(effect, false);
|
||||
this(Zone.BATTLEFIELD, effect, false, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param effect The effect that should happen when the ability resolves
|
||||
* @param setTarget Whether or not the ability should set a target targetting
|
||||
* the opponent who controls the creatures who dealt damage to
|
||||
* you
|
||||
*/
|
||||
public CombatDamageDealtToYouTriggeredAbility(Effect effect, boolean setTarget) {
|
||||
this(Zone.BATTLEFIELD, effect, setTarget, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param zone Which zone the ability shoudl take effect in
|
||||
* @param effect The effect that should happen when the ability resolves
|
||||
* @param setTarget Whether or not the ability should set a target targetting
|
||||
* the opponent who controls the creatures who dealt damage to
|
||||
* you
|
||||
* @param optional Whether or not the ability is optional
|
||||
*/
|
||||
public CombatDamageDealtToYouTriggeredAbility(Zone zone, Effect effect, boolean setTarget,
|
||||
boolean optional) {
|
||||
public CombatDamageDealtToYouTriggeredAbility(Zone zone, Effect effect, boolean setTargetPointer, boolean optional) {
|
||||
super(zone, effect, optional);
|
||||
|
||||
this.setTarget = setTarget;
|
||||
|
||||
this.setTargetPointer = setTargetPointer;
|
||||
setTriggerPhrase(generateTriggerPhrase());
|
||||
}
|
||||
|
||||
private CombatDamageDealtToYouTriggeredAbility(final CombatDamageDealtToYouTriggeredAbility ability) {
|
||||
super(ability);
|
||||
|
||||
this.setTarget = ability.setTarget;
|
||||
this.setTargetPointer = ability.setTargetPointer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_PLAYERS;
|
||||
return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_ONE_PLAYER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
DamagedBatchForPlayersEvent dEvent = (DamagedBatchForPlayersEvent) event;
|
||||
|
||||
boolean isDamaged = false;
|
||||
UUID damageSourceControllerID = null;
|
||||
for (DamagedEvent damagedEvent : dEvent.getEvents()) {
|
||||
if (damagedEvent.isCombatDamage() && damagedEvent.getPlayerId() == this.controllerId) {
|
||||
isDamaged = true;
|
||||
// TODO: current code support only one controller
|
||||
// (it's can be potentially bugged in team mode with multiple attack players)
|
||||
Permanent damageSource = game.getPermanent(damagedEvent.getSourceId());
|
||||
if (damageSource != null) {
|
||||
damageSourceControllerID = damageSource.getControllerId();
|
||||
}
|
||||
}
|
||||
if (!isControlledBy(event.getTargetId()) || !((DamagedBatchForOnePlayerEvent) event).isCombatDamage()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isDamaged) {
|
||||
if (this.setTarget && damageSourceControllerID != null) {
|
||||
this.getEffects().setTargetPointer(new FixedTarget(damageSourceControllerID));
|
||||
}
|
||||
return true;
|
||||
if (setTargetPointer) {
|
||||
// attacking player is active player
|
||||
this.getEffects().setTargetPointer(new FixedTarget(game.getActivePlayerId()));
|
||||
}
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private String generateTriggerPhrase() {
|
||||
if (setTarget) {
|
||||
if (setTargetPointer) {
|
||||
return "Whenever one or more creatures an opponent controls deal combat damage to you, ";
|
||||
} else {
|
||||
return "Whenever one or more creatures deal combat damage to you, ";
|
||||
|
|
@ -110,4 +68,4 @@ public class CombatDamageDealtToYouTriggeredAbility extends TriggeredAbilityImpl
|
|||
public CombatDamageDealtToYouTriggeredAbility copy() {
|
||||
return new CombatDamageDealtToYouTriggeredAbility(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,39 +0,0 @@
|
|||
package mage.abilities.common;
|
||||
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.constants.SetTargetPointer;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
|
||||
/**
|
||||
* @author Xanderhall, xenohedron
|
||||
*/
|
||||
public class DealCombatDamageControlledTriggeredAbility extends OneOrMoreDealDamageTriggeredAbility {
|
||||
|
||||
public DealCombatDamageControlledTriggeredAbility(Effect effect) {
|
||||
this(effect, SetTargetPointer.NONE);
|
||||
}
|
||||
|
||||
public DealCombatDamageControlledTriggeredAbility(Effect effect, FilterCreaturePermanent filter) {
|
||||
this(Zone.BATTLEFIELD, effect, filter, SetTargetPointer.NONE, false);
|
||||
}
|
||||
|
||||
public DealCombatDamageControlledTriggeredAbility(Effect effect, SetTargetPointer setTargetPointer) {
|
||||
this(Zone.BATTLEFIELD, effect, StaticFilters.FILTER_PERMANENT_CREATURES, setTargetPointer, false);
|
||||
}
|
||||
|
||||
public DealCombatDamageControlledTriggeredAbility(Zone zone, Effect effect, FilterCreaturePermanent filter,
|
||||
SetTargetPointer setTargetPointer, boolean optional) {
|
||||
super(zone, effect, filter, true, true, setTargetPointer, optional);
|
||||
}
|
||||
|
||||
protected DealCombatDamageControlledTriggeredAbility(final DealCombatDamageControlledTriggeredAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DealCombatDamageControlledTriggeredAbility copy() {
|
||||
return new DealCombatDamageControlledTriggeredAbility(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,11 @@
|
|||
package mage.abilities.common;
|
||||
|
||||
import mage.abilities.BatchTriggeredAbility;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.DamagedBatchAllEvent;
|
||||
import mage.game.events.DamagedBatchBySourceEvent;
|
||||
import mage.game.events.DamagedEvent;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
|
@ -12,7 +13,7 @@ import mage.game.permanent.Permanent;
|
|||
/**
|
||||
* @author TheElk801, xenohedron
|
||||
*/
|
||||
public class DealsCombatDamageEquippedTriggeredAbility extends TriggeredAbilityImpl {
|
||||
public class DealsCombatDamageEquippedTriggeredAbility extends TriggeredAbilityImpl implements BatchTriggeredAbility<DamagedEvent> {
|
||||
|
||||
public DealsCombatDamageEquippedTriggeredAbility(Effect effect) {
|
||||
this(effect, false);
|
||||
|
|
@ -34,26 +35,19 @@ public class DealsCombatDamageEquippedTriggeredAbility extends TriggeredAbilityI
|
|||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_ALL;
|
||||
return event.getType() == GameEvent.EventType.DAMAGED_BATCH_BY_SOURCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
if (!((DamagedBatchBySourceEvent) event).isCombatDamage()) {
|
||||
return false;
|
||||
}
|
||||
Permanent sourcePermanent = getSourcePermanentOrLKI(game);
|
||||
if (sourcePermanent == null || sourcePermanent.getAttachedTo() == null) {
|
||||
if (sourcePermanent == null || !event.getSourceId().equals(sourcePermanent.getAttachedTo())) {
|
||||
return false;
|
||||
}
|
||||
int amount = ((DamagedBatchAllEvent) event)
|
||||
.getEvents()
|
||||
.stream()
|
||||
.filter(DamagedEvent::isCombatDamage)
|
||||
.filter(e -> e.getAttackerId().equals(sourcePermanent.getAttachedTo()))
|
||||
.mapToInt(GameEvent::getAmount)
|
||||
.sum();
|
||||
if (amount < 1) {
|
||||
return false;
|
||||
}
|
||||
this.getEffects().setValue("damage", amount);
|
||||
this.getEffects().setValue("damage", event.getAmount());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
package mage.abilities.common;
|
||||
|
||||
import mage.abilities.BatchTriggeredAbility;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.DamagedBatchAllEvent;
|
||||
import mage.game.events.DamagedBatchBySourceEvent;
|
||||
import mage.game.events.DamagedEvent;
|
||||
import mage.game.events.GameEvent;
|
||||
|
||||
|
|
@ -15,7 +16,7 @@ import mage.game.events.GameEvent;
|
|||
*
|
||||
* @author LevelX, xenohedron
|
||||
*/
|
||||
public class DealsCombatDamageTriggeredAbility extends TriggeredAbilityImpl {
|
||||
public class DealsCombatDamageTriggeredAbility extends TriggeredAbilityImpl implements BatchTriggeredAbility<DamagedEvent> {
|
||||
|
||||
public DealsCombatDamageTriggeredAbility(Effect effect, boolean optional) {
|
||||
super(Zone.BATTLEFIELD, effect, optional);
|
||||
|
|
@ -34,22 +35,15 @@ public class DealsCombatDamageTriggeredAbility extends TriggeredAbilityImpl {
|
|||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_ALL;
|
||||
return event.getType() == GameEvent.EventType.DAMAGED_BATCH_BY_SOURCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
int amount = ((DamagedBatchAllEvent) event)
|
||||
.getEvents()
|
||||
.stream()
|
||||
.filter(DamagedEvent::isCombatDamage)
|
||||
.filter(e -> e.getAttackerId().equals(getSourceId()))
|
||||
.mapToInt(GameEvent::getAmount)
|
||||
.sum();
|
||||
if (amount < 1) {
|
||||
if (!event.getSourceId().equals(getSourceId()) || !((DamagedBatchBySourceEvent) event).isCombatDamage() ) {
|
||||
return false;
|
||||
}
|
||||
this.getEffects().setValue("damage", amount);
|
||||
this.getEffects().setValue("damage", event.getAmount());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,47 +0,0 @@
|
|||
package mage.abilities.common;
|
||||
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.dynamicvalue.common.SavedDamageValue;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.common.GainLifeEffect;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
|
||||
/**
|
||||
* @author LevelX2
|
||||
*/
|
||||
|
||||
public class DealsDamageGainLifeSourceTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
||||
public DealsDamageGainLifeSourceTriggeredAbility() {
|
||||
super(Zone.BATTLEFIELD, new GainLifeEffect(SavedDamageValue.MUCH), false);
|
||||
setTriggerPhrase("Whenever {this} deals damage, ");
|
||||
}
|
||||
|
||||
protected DealsDamageGainLifeSourceTriggeredAbility(final DealsDamageGainLifeSourceTriggeredAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DealsDamageGainLifeSourceTriggeredAbility copy() {
|
||||
return new DealsDamageGainLifeSourceTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT
|
||||
|| event.getType() == GameEvent.EventType.DAMAGED_PLAYER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
if (event.getSourceId().equals(this.getSourceId())) {
|
||||
for (Effect effect : this.getEffects()) {
|
||||
effect.setValue("damage", event.getAmount());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
package mage.abilities.common;
|
||||
|
||||
import mage.abilities.BatchTriggeredAbility;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.DamagedEvent;
|
||||
import mage.game.events.GameEvent;
|
||||
|
||||
/**
|
||||
* @author xenohedron
|
||||
*/
|
||||
|
||||
public class DealsDamageSourceTriggeredAbility extends TriggeredAbilityImpl implements BatchTriggeredAbility<DamagedEvent> {
|
||||
|
||||
public DealsDamageSourceTriggeredAbility(Effect effect) {
|
||||
this(effect, false);
|
||||
}
|
||||
|
||||
public DealsDamageSourceTriggeredAbility(Effect effect, boolean optional) {
|
||||
super(Zone.BATTLEFIELD, effect, optional);
|
||||
setTriggerPhrase("Whenever {this} deals damage, ");
|
||||
this.withRuleTextReplacement(true);
|
||||
}
|
||||
|
||||
protected DealsDamageSourceTriggeredAbility(final DealsDamageSourceTriggeredAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DealsDamageSourceTriggeredAbility copy() {
|
||||
return new DealsDamageSourceTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.DAMAGED_BATCH_BY_SOURCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
// all events in the batch are always relevant
|
||||
if (event.getSourceId().equals(this.getSourceId())) {
|
||||
this.getEffects().setValue("damage", event.getAmount());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
package mage.abilities.common;
|
||||
|
||||
import mage.abilities.BatchTriggeredAbility;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.constants.SetTargetPointer;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.DamagedBatchBySourceEvent;
|
||||
import mage.game.events.DamagedEvent;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
/**
|
||||
* @author xenohedron
|
||||
*/
|
||||
public class DealsDamageToAnyTriggeredAbility extends TriggeredAbilityImpl implements BatchTriggeredAbility<DamagedEvent> {
|
||||
|
||||
private final FilterPermanent filter;
|
||||
private final boolean onlyCombat;
|
||||
private final SetTargetPointer setTargetPointer;
|
||||
|
||||
public DealsDamageToAnyTriggeredAbility(Zone zone, Effect effect, FilterPermanent filter,
|
||||
SetTargetPointer setTargetPointer, boolean onlyCombat, boolean optional) {
|
||||
super(zone, effect, optional);
|
||||
this.filter = filter;
|
||||
this.onlyCombat = onlyCombat;
|
||||
this.setTargetPointer = setTargetPointer;
|
||||
setTriggerPhrase(getWhen() + CardUtil.addArticle(filter.getMessage()) + " deals "
|
||||
+ (onlyCombat ? "combat " : "") + "damage, ");
|
||||
}
|
||||
|
||||
protected DealsDamageToAnyTriggeredAbility(final DealsDamageToAnyTriggeredAbility ability) {
|
||||
super(ability);
|
||||
this.filter = ability.filter;
|
||||
this.onlyCombat = ability.onlyCombat;
|
||||
this.setTargetPointer = ability.setTargetPointer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DealsDamageToAnyTriggeredAbility copy() {
|
||||
return new DealsDamageToAnyTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.DAMAGED_BATCH_BY_SOURCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
// all events in the batch are always relevant if triggers at all
|
||||
if (onlyCombat && !((DamagedBatchBySourceEvent) event).isCombatDamage()) {
|
||||
return false;
|
||||
}
|
||||
Permanent permanent = game.getPermanentOrLKIBattlefield(event.getSourceId());
|
||||
if (permanent == null || !filter.match(permanent, getControllerId(), this, game)) {
|
||||
return false;
|
||||
}
|
||||
getEffects().setValue("damage", event.getAmount());
|
||||
switch (setTargetPointer) {
|
||||
case PERMANENT:
|
||||
getEffects().setTargetPointer(new FixedTarget(permanent, game));
|
||||
return true;
|
||||
case PLAYER:
|
||||
getEffects().setTargetPointer(new FixedTarget(permanent.getControllerId()));
|
||||
return true;
|
||||
case NONE:
|
||||
return true;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported SetTargetPointer in DealtDamageAttachedTriggeredAbility");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
package mage.abilities.common;
|
||||
|
||||
import mage.abilities.BatchTriggeredAbility;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.DamagedBatchForOnePermanentEvent;
|
||||
import mage.game.events.DamagedPermanentEvent;
|
||||
import mage.game.events.GameEvent;
|
||||
|
||||
/**
|
||||
* @author xenohedron
|
||||
*/
|
||||
public class DealtCombatDamageToSourceTriggeredAbility extends TriggeredAbilityImpl implements BatchTriggeredAbility<DamagedPermanentEvent> {
|
||||
|
||||
public DealtCombatDamageToSourceTriggeredAbility(Effect effect, boolean optional) {
|
||||
super(Zone.BATTLEFIELD, effect, optional);
|
||||
setTriggerPhrase("Whenever {this} is dealt combat damage, ");
|
||||
this.withRuleTextReplacement(true);
|
||||
}
|
||||
|
||||
protected DealtCombatDamageToSourceTriggeredAbility(final DealtCombatDamageToSourceTriggeredAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DealtCombatDamageToSourceTriggeredAbility copy() {
|
||||
return new DealtCombatDamageToSourceTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_ONE_PERMANENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
// all events in the batch are always relevant if triggers at all
|
||||
if (!getSourceId().equals(event.getTargetId()) || !((DamagedBatchForOnePermanentEvent) event).isCombatDamage()) {
|
||||
return false;
|
||||
}
|
||||
this.getEffects().setValue("damage", event.getAmount());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
package mage.abilities.common;
|
||||
|
||||
import mage.abilities.BatchTriggeredAbility;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.constants.SetTargetPointer;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.DamagedPermanentEvent;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
/**
|
||||
* @author xenohedron
|
||||
*/
|
||||
public class DealtDamageAnyTriggeredAbility extends TriggeredAbilityImpl implements BatchTriggeredAbility<DamagedPermanentEvent> {
|
||||
|
||||
private final FilterPermanent filter;
|
||||
private final SetTargetPointer setTargetPointer;
|
||||
|
||||
public DealtDamageAnyTriggeredAbility(Effect effect, FilterPermanent filter, SetTargetPointer setTargetPointer, boolean optional) {
|
||||
this(Zone.BATTLEFIELD, effect, filter, setTargetPointer, optional);
|
||||
}
|
||||
|
||||
public DealtDamageAnyTriggeredAbility(Zone zone, Effect effect, FilterPermanent filter, SetTargetPointer setTargetPointer, boolean optional) {
|
||||
super(zone, effect, optional);
|
||||
this.filter = filter;
|
||||
this.setTargetPointer = setTargetPointer;
|
||||
setTriggerPhrase(getWhen() + CardUtil.addArticle(filter.getMessage()) + " is dealt damage, ");
|
||||
}
|
||||
|
||||
protected DealtDamageAnyTriggeredAbility(final DealtDamageAnyTriggeredAbility ability) {
|
||||
super(ability);
|
||||
this.filter = ability.filter;
|
||||
this.setTargetPointer = ability.setTargetPointer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DealtDamageAnyTriggeredAbility copy() {
|
||||
return new DealtDamageAnyTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_ONE_PERMANENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
// all events in the batch are always relevant if triggers at all
|
||||
Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId());
|
||||
if (permanent == null || !filter.match(permanent, getControllerId(), this, game)) {
|
||||
return false;
|
||||
}
|
||||
getEffects().setValue("damage", event.getAmount());
|
||||
switch (setTargetPointer) {
|
||||
case PERMANENT:
|
||||
getEffects().setTargetPointer(new FixedTarget(permanent, game));
|
||||
return true;
|
||||
case PLAYER:
|
||||
getEffects().setTargetPointer(new FixedTarget(permanent.getControllerId()));
|
||||
return true;
|
||||
case NONE:
|
||||
return true;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported SetTargetPointer in DealtDamageAttachedTriggeredAbility");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,12 @@
|
|||
package mage.abilities.common;
|
||||
|
||||
import mage.abilities.BatchTriggeredAbility;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.constants.SetTargetPointer;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.DamagedPermanentEvent;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
|
|
@ -14,12 +16,12 @@ import java.util.UUID;
|
|||
/**
|
||||
* @author LoneFox
|
||||
*/
|
||||
public class DealtDamageAttachedTriggeredAbility extends TriggeredAbilityImpl {
|
||||
public class DealtDamageAttachedTriggeredAbility extends TriggeredAbilityImpl implements BatchTriggeredAbility<DamagedPermanentEvent> {
|
||||
|
||||
protected SetTargetPointer setTargetPointer;
|
||||
private final SetTargetPointer setTargetPointer;
|
||||
|
||||
public DealtDamageAttachedTriggeredAbility(Effect effect, boolean optional) {
|
||||
this(Zone.BATTLEFIELD, effect, optional, SetTargetPointer.NONE);
|
||||
public DealtDamageAttachedTriggeredAbility(Effect effect) {
|
||||
this(Zone.BATTLEFIELD, effect, false, SetTargetPointer.NONE);
|
||||
}
|
||||
|
||||
public DealtDamageAttachedTriggeredAbility(Zone zone, Effect effect, boolean optional, SetTargetPointer setTargetPointer) {
|
||||
|
|
@ -45,21 +47,23 @@ public class DealtDamageAttachedTriggeredAbility extends TriggeredAbilityImpl {
|
|||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
Permanent enchantment = game.getPermanent(sourceId);
|
||||
// all events in the batch are always relevant if triggers at all
|
||||
Permanent attachment = getSourcePermanentOrLKI(game);
|
||||
UUID targetId = event.getTargetId();
|
||||
if (enchantment != null && enchantment.getAttachedTo() != null && targetId.equals(enchantment.getAttachedTo())) {
|
||||
for (Effect effect : this.getEffects()) {
|
||||
effect.setValue("damage", event.getAmount());
|
||||
switch (setTargetPointer) {
|
||||
case PERMANENT:
|
||||
effect.setTargetPointer(new FixedTarget(targetId, game));
|
||||
break;
|
||||
case PLAYER:
|
||||
effect.setTargetPointer(new FixedTarget(game.getPermanentOrLKIBattlefield(targetId).getControllerId()));
|
||||
break;
|
||||
}
|
||||
if (attachment != null && attachment.getAttachedTo() != null && targetId.equals(attachment.getAttachedTo())) {
|
||||
getEffects().setValue("damage", event.getAmount());
|
||||
switch (setTargetPointer) {
|
||||
case PERMANENT:
|
||||
getEffects().setTargetPointer(new FixedTarget(targetId, game));
|
||||
return true;
|
||||
case PLAYER:
|
||||
getEffects().setTargetPointer(new FixedTarget(attachment.getControllerId()));
|
||||
return true;
|
||||
case NONE:
|
||||
return true;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported SetTargetPointer in DealtDamageAttachedTriggeredAbility");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,18 @@
|
|||
package mage.abilities.common;
|
||||
|
||||
import mage.abilities.BatchTriggeredAbility;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.constants.AbilityWord;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.DamagedBatchForPermanentsEvent;
|
||||
import mage.game.events.DamagedPermanentEvent;
|
||||
import mage.game.events.GameEvent;
|
||||
|
||||
/**
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class DealtDamageToSourceTriggeredAbility extends TriggeredAbilityImpl {
|
||||
public class DealtDamageToSourceTriggeredAbility extends TriggeredAbilityImpl implements BatchTriggeredAbility<DamagedPermanentEvent> {
|
||||
|
||||
public DealtDamageToSourceTriggeredAbility(Effect effect, boolean optional) {
|
||||
this(effect, optional, false);
|
||||
|
|
@ -37,22 +38,16 @@ public class DealtDamageToSourceTriggeredAbility extends TriggeredAbilityImpl {
|
|||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_PERMANENTS;
|
||||
return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_ONE_PERMANENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
DamagedBatchForPermanentsEvent dEvent = (DamagedBatchForPermanentsEvent) event;
|
||||
int damage = dEvent
|
||||
.getEvents()
|
||||
.stream()
|
||||
.filter(damagedEvent -> getSourceId().equals(damagedEvent.getTargetId()))
|
||||
.mapToInt(GameEvent::getAmount)
|
||||
.sum();
|
||||
if (damage < 1) {
|
||||
// all events in the batch are always relevant if triggers at all
|
||||
if (!getSourceId().equals(event.getTargetId())) {
|
||||
return false;
|
||||
}
|
||||
this.getEffects().setValue("damage", damage);
|
||||
this.getEffects().setValue("damage", event.getAmount());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,53 +0,0 @@
|
|||
package mage.abilities.common;
|
||||
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.ZoneChangeBatchEvent;
|
||||
import mage.game.events.ZoneChangeEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
||||
/**
|
||||
* @author Cguy7777
|
||||
*/
|
||||
public class DiesOneOrMoreCreaturesTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
||||
public DiesOneOrMoreCreaturesTriggeredAbility(Effect effect) {
|
||||
this(effect, false);
|
||||
}
|
||||
|
||||
public DiesOneOrMoreCreaturesTriggeredAbility(Effect effect, boolean optional) {
|
||||
super(Zone.BATTLEFIELD, effect, optional);
|
||||
setTriggerPhrase("Whenever one or more creatures die, ");
|
||||
}
|
||||
|
||||
private DiesOneOrMoreCreaturesTriggeredAbility(final DiesOneOrMoreCreaturesTriggeredAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DiesOneOrMoreCreaturesTriggeredAbility copy() {
|
||||
return new DiesOneOrMoreCreaturesTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.ZONE_CHANGE_BATCH;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
for (ZoneChangeEvent zEvent : ((ZoneChangeBatchEvent) event).getEvents()) {
|
||||
if (!zEvent.isDiesEvent()) {
|
||||
continue;
|
||||
}
|
||||
Permanent permanent = game.getPermanentOrLKIBattlefield(zEvent.getTargetId());
|
||||
if (permanent != null && permanent.isCreature(game)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package mage.abilities.common;
|
||||
|
||||
import mage.MageObject;
|
||||
import mage.abilities.BatchTriggeredAbility;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.constants.Zone;
|
||||
|
|
@ -9,30 +10,29 @@ import mage.game.Game;
|
|||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.ZoneChangeBatchEvent;
|
||||
import mage.game.events.ZoneChangeEvent;
|
||||
|
||||
import java.util.Objects;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
||||
/**
|
||||
* @author Susucr
|
||||
*/
|
||||
public class DiesOneOrMoreCreatureTriggeredAbility extends TriggeredAbilityImpl {
|
||||
public class DiesOneOrMoreTriggeredAbility extends TriggeredAbilityImpl implements BatchTriggeredAbility<ZoneChangeEvent> {
|
||||
|
||||
private final FilterCreaturePermanent filter;
|
||||
|
||||
public DiesOneOrMoreCreatureTriggeredAbility(Effect effect, FilterCreaturePermanent filter, boolean optional) {
|
||||
public DiesOneOrMoreTriggeredAbility(Effect effect, FilterCreaturePermanent filter, boolean optional) {
|
||||
super(Zone.BATTLEFIELD, effect, optional);
|
||||
this.filter = filter;
|
||||
this.setTriggerPhrase("Whenever one or more " + filter.getMessage() + " die, ");
|
||||
}
|
||||
|
||||
private DiesOneOrMoreCreatureTriggeredAbility(final DiesOneOrMoreCreatureTriggeredAbility ability) {
|
||||
private DiesOneOrMoreTriggeredAbility(final DiesOneOrMoreTriggeredAbility ability) {
|
||||
super(ability);
|
||||
this.filter = ability.filter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DiesOneOrMoreCreatureTriggeredAbility copy() {
|
||||
return new DiesOneOrMoreCreatureTriggeredAbility(this);
|
||||
public DiesOneOrMoreTriggeredAbility copy() {
|
||||
return new DiesOneOrMoreTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -40,16 +40,18 @@ public class DiesOneOrMoreCreatureTriggeredAbility extends TriggeredAbilityImpl
|
|||
return event.getType() == GameEvent.EventType.ZONE_CHANGE_BATCH;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEvent(ZoneChangeEvent event, Game game) {
|
||||
if (!event.isDiesEvent()) {
|
||||
return false;
|
||||
}
|
||||
Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId());
|
||||
return permanent != null && filter.match(permanent, getControllerId(), this, game);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
return ((ZoneChangeBatchEvent) event)
|
||||
.getEvents()
|
||||
.stream()
|
||||
.filter(ZoneChangeEvent::isDiesEvent)
|
||||
.map(ZoneChangeEvent::getTargetId)
|
||||
.map(game::getPermanentOrLKIBattlefield)
|
||||
.filter(Objects::nonNull)
|
||||
.anyMatch(p -> filter.match(p, getControllerId(), this, game));
|
||||
return !getFilteredEvents((ZoneChangeBatchEvent) event, game).isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package mage.abilities.common;
|
||||
|
||||
import mage.abilities.BatchTriggeredAbility;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.constants.TargetController;
|
||||
|
|
@ -8,30 +9,29 @@ import mage.filter.FilterPermanent;
|
|||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.ZoneChangeBatchEvent;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.game.events.ZoneChangeEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
||||
/**
|
||||
* "Whenever one or more {filter} enter the battlefield under {target controller} control,
|
||||
*
|
||||
* @author Alex-Vasile
|
||||
* @author Alex-Vasile, xenohedron
|
||||
*/
|
||||
public class EntersBattlefieldOneOrMoreTriggeredAbility extends TriggeredAbilityImpl {
|
||||
public class EntersBattlefieldOneOrMoreTriggeredAbility extends TriggeredAbilityImpl implements BatchTriggeredAbility<ZoneChangeEvent> {
|
||||
|
||||
private final FilterPermanent filterPermanent;
|
||||
private final FilterPermanent filter;
|
||||
private final TargetController targetController;
|
||||
|
||||
public EntersBattlefieldOneOrMoreTriggeredAbility(Effect effect, FilterPermanent filter, TargetController targetController) {
|
||||
super(Zone.BATTLEFIELD, effect);
|
||||
this.filterPermanent = filter;
|
||||
this.filter = filter;
|
||||
this.targetController = targetController;
|
||||
setTriggerPhrase(generateTriggerPhrase());
|
||||
}
|
||||
|
||||
private EntersBattlefieldOneOrMoreTriggeredAbility(final EntersBattlefieldOneOrMoreTriggeredAbility ability) {
|
||||
super(ability);
|
||||
this.filterPermanent = ability.filterPermanent;
|
||||
this.filter = ability.filter;
|
||||
this.targetController = ability.targetController;
|
||||
}
|
||||
|
||||
|
|
@ -41,28 +41,27 @@ public class EntersBattlefieldOneOrMoreTriggeredAbility extends TriggeredAbility
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
|
||||
Player controller = game.getPlayer(this.controllerId);
|
||||
if (controller == null) {
|
||||
public boolean checkEvent(ZoneChangeEvent event, Game game) {
|
||||
if (event.getToZone() != Zone.BATTLEFIELD) {
|
||||
return false;
|
||||
}
|
||||
Permanent permanent = event.getTarget();
|
||||
if (permanent == null || !filter.match(permanent, getControllerId(), this, game)) {
|
||||
return false;
|
||||
}
|
||||
switch (targetController) {
|
||||
case YOU:
|
||||
return isControlledBy(permanent.getControllerId());
|
||||
case OPPONENT:
|
||||
return game.getOpponents(getControllerId()).contains(permanent.getControllerId());
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported TargetController in EntersBattlefieldOneOrMoreTriggeredAbility: " + targetController);
|
||||
}
|
||||
}
|
||||
|
||||
ZoneChangeBatchEvent zEvent = (ZoneChangeBatchEvent) event;
|
||||
return zEvent.getEvents().stream()
|
||||
.filter(z -> z.getToZone() == Zone.BATTLEFIELD)
|
||||
.filter(z -> filterPermanent.match(z.getTarget(), this.controllerId, this, game))
|
||||
.anyMatch(z -> {
|
||||
UUID enteringPermanentControllerID = z.getTarget().getControllerId();
|
||||
switch (this.targetController) {
|
||||
case YOU:
|
||||
return enteringPermanentControllerID.equals(this.controllerId);
|
||||
case OPPONENT:
|
||||
return controller.hasOpponent(enteringPermanentControllerID, game);
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported target: " + this.targetController);
|
||||
}
|
||||
});
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
return !getFilteredEvents((ZoneChangeBatchEvent) event, game).isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -71,10 +70,10 @@ public class EntersBattlefieldOneOrMoreTriggeredAbility extends TriggeredAbility
|
|||
}
|
||||
|
||||
private String generateTriggerPhrase() {
|
||||
StringBuilder sb = new StringBuilder("Whenever one or more " + filterPermanent.getMessage());
|
||||
StringBuilder sb = new StringBuilder("Whenever one or more " + filter.getMessage());
|
||||
switch (targetController) {
|
||||
case YOU:
|
||||
if (filterPermanent.getMessage().contains("you control")) {
|
||||
if (filter.getMessage().contains("you control")) {
|
||||
sb.append(" enter, ");
|
||||
} else {
|
||||
sb.append(" you control enter, ");
|
||||
|
|
@ -84,7 +83,7 @@ public class EntersBattlefieldOneOrMoreTriggeredAbility extends TriggeredAbility
|
|||
sb.append(" enter under an opponent's control, ");
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported TargetController in EntersBattlefieldOneOrMoreTriggeredAbility");
|
||||
throw new IllegalArgumentException("Unsupported TargetController in EntersBattlefieldOneOrMoreTriggeredAbility: " + targetController);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
package mage.abilities.common;
|
||||
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.constants.SetTargetPointer;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
|
||||
/**
|
||||
* @author Xanderhall, xenohedron
|
||||
*/
|
||||
public class OneOrMoreCombatDamagePlayerTriggeredAbility extends OneOrMoreDamagePlayerTriggeredAbility {
|
||||
|
||||
public OneOrMoreCombatDamagePlayerTriggeredAbility(Effect effect) {
|
||||
this(effect, SetTargetPointer.NONE);
|
||||
}
|
||||
|
||||
public OneOrMoreCombatDamagePlayerTriggeredAbility(Effect effect, FilterCreaturePermanent filter) {
|
||||
this(Zone.BATTLEFIELD, effect, filter, SetTargetPointer.NONE, false);
|
||||
}
|
||||
|
||||
public OneOrMoreCombatDamagePlayerTriggeredAbility(Effect effect, SetTargetPointer setTargetPointer) {
|
||||
this(Zone.BATTLEFIELD, effect, StaticFilters.FILTER_PERMANENT_CREATURES, setTargetPointer, false);
|
||||
}
|
||||
|
||||
public OneOrMoreCombatDamagePlayerTriggeredAbility(Zone zone, Effect effect, FilterCreaturePermanent filter,
|
||||
SetTargetPointer setTargetPointer, boolean optional) {
|
||||
super(zone, effect, filter, true, true, setTargetPointer, optional);
|
||||
}
|
||||
|
||||
protected OneOrMoreCombatDamagePlayerTriggeredAbility(final OneOrMoreCombatDamagePlayerTriggeredAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OneOrMoreCombatDamagePlayerTriggeredAbility copy() {
|
||||
return new OneOrMoreCombatDamagePlayerTriggeredAbility(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package mage.abilities.common;
|
||||
|
||||
import mage.abilities.BatchTriggeredAbility;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.constants.SetTargetPointer;
|
||||
|
|
@ -8,30 +9,30 @@ import mage.filter.FilterPermanent;
|
|||
import mage.game.Game;
|
||||
import mage.game.events.DamagedBatchForOnePlayerEvent;
|
||||
import mage.game.events.DamagedEvent;
|
||||
import mage.game.events.DamagedPlayerEvent;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author Xanderhall, xenohedron
|
||||
*/
|
||||
public class OneOrMoreDealDamageTriggeredAbility extends TriggeredAbilityImpl {
|
||||
public class OneOrMoreDamagePlayerTriggeredAbility extends TriggeredAbilityImpl implements BatchTriggeredAbility<DamagedPlayerEvent> {
|
||||
|
||||
private final SetTargetPointer setTargetPointer;
|
||||
private final FilterPermanent filter;
|
||||
private final boolean onlyCombat;
|
||||
private final boolean onlyControlled;
|
||||
|
||||
public OneOrMoreDealDamageTriggeredAbility(Effect effect, FilterPermanent filter, boolean onlyCombat, boolean onlyControlled) {
|
||||
public OneOrMoreDamagePlayerTriggeredAbility(Effect effect, FilterPermanent filter, boolean onlyCombat, boolean onlyControlled) {
|
||||
this(Zone.BATTLEFIELD, effect, filter, onlyCombat, onlyControlled, SetTargetPointer.NONE, false);
|
||||
}
|
||||
|
||||
public OneOrMoreDealDamageTriggeredAbility(Zone zone, Effect effect, FilterPermanent filter,
|
||||
boolean onlyCombat, boolean onlyControlled,
|
||||
SetTargetPointer setTargetPointer, boolean optional) {
|
||||
public OneOrMoreDamagePlayerTriggeredAbility(Zone zone, Effect effect, FilterPermanent filter,
|
||||
boolean onlyCombat, boolean onlyControlled,
|
||||
SetTargetPointer setTargetPointer, boolean optional) {
|
||||
super(zone, effect, optional);
|
||||
this.setTargetPointer = setTargetPointer;
|
||||
this.filter = filter;
|
||||
|
|
@ -40,7 +41,7 @@ public class OneOrMoreDealDamageTriggeredAbility extends TriggeredAbilityImpl {
|
|||
makeTriggerPhrase();
|
||||
}
|
||||
|
||||
protected OneOrMoreDealDamageTriggeredAbility(final OneOrMoreDealDamageTriggeredAbility ability) {
|
||||
protected OneOrMoreDamagePlayerTriggeredAbility(final OneOrMoreDamagePlayerTriggeredAbility ability) {
|
||||
super(ability);
|
||||
this.setTargetPointer = ability.setTargetPointer;
|
||||
this.filter = ability.filter;
|
||||
|
|
@ -49,8 +50,8 @@ public class OneOrMoreDealDamageTriggeredAbility extends TriggeredAbilityImpl {
|
|||
}
|
||||
|
||||
@Override
|
||||
public OneOrMoreDealDamageTriggeredAbility copy() {
|
||||
return new OneOrMoreDealDamageTriggeredAbility(this);
|
||||
public OneOrMoreDamagePlayerTriggeredAbility copy() {
|
||||
return new OneOrMoreDamagePlayerTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -59,30 +60,26 @@ public class OneOrMoreDealDamageTriggeredAbility extends TriggeredAbilityImpl {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
DamagedBatchForOnePlayerEvent dEvent = (DamagedBatchForOnePlayerEvent) event;
|
||||
if (onlyCombat && !dEvent.isCombatDamage()) {
|
||||
public boolean checkEvent(DamagedPlayerEvent event, Game game) {
|
||||
if (onlyCombat && !event.isCombatDamage()) {
|
||||
return false;
|
||||
}
|
||||
List<DamagedEvent> events = dEvent
|
||||
.getEvents()
|
||||
.stream()
|
||||
.filter(e -> {
|
||||
Permanent permanent = game.getPermanentOrLKIBattlefield(e.getSourceId());
|
||||
if (permanent == null) {
|
||||
return false;
|
||||
}
|
||||
if (onlyControlled && !permanent.isControlledBy(this.getControllerId())) {
|
||||
return false;
|
||||
}
|
||||
return filter.match(permanent, this.getControllerId(), this, game);
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
Permanent permanent = game.getPermanentOrLKIBattlefield(event.getSourceId());
|
||||
if (permanent == null) {
|
||||
return false;
|
||||
}
|
||||
if (onlyControlled && !permanent.isControlledBy(this.getControllerId())) {
|
||||
return false;
|
||||
}
|
||||
return filter.match(permanent, this.getControllerId(), this, game);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
List<DamagedPlayerEvent> events = getFilteredEvents((DamagedBatchForOnePlayerEvent) event, game);
|
||||
if (events.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.getAllEffects().setValue("damage", events.stream().mapToInt(DamagedEvent::getAmount).sum());
|
||||
switch (setTargetPointer) {
|
||||
case PLAYER:
|
||||
|
|
@ -91,9 +88,8 @@ public class OneOrMoreDealDamageTriggeredAbility extends TriggeredAbilityImpl {
|
|||
case NONE:
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid SetTargetPointer option");
|
||||
throw new IllegalArgumentException("Unsupported SetTargetPointer in OneOrMoreDamagePlayerTriggeredAbility");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -1,19 +1,21 @@
|
|||
|
||||
package mage.abilities.common;
|
||||
|
||||
import mage.abilities.BatchTriggeredAbility;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.dynamicvalue.common.SavedMilledValue;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.cards.Card;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.MilledBatchAllEvent;
|
||||
import mage.game.events.MilledCardEvent;
|
||||
|
||||
/**
|
||||
* @author Susucr
|
||||
*/
|
||||
public class OneOrMoreMilledTriggeredAbility extends TriggeredAbilityImpl {
|
||||
public class OneOrMoreMilledTriggeredAbility extends TriggeredAbilityImpl implements BatchTriggeredAbility<MilledCardEvent> {
|
||||
|
||||
private final FilterCard filter;
|
||||
|
||||
|
|
@ -42,10 +44,16 @@ public class OneOrMoreMilledTriggeredAbility extends TriggeredAbilityImpl {
|
|||
return event.getType() == GameEvent.EventType.MILLED_CARDS_BATCH_FOR_ALL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEvent(MilledCardEvent event, Game game) {
|
||||
Card card = event.getCard(game);
|
||||
return card != null && filter.match(card, getControllerId(), this, game);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
int count = ((MilledBatchAllEvent) event).getCards(game).count(filter, getControllerId(), this, game);
|
||||
if (count <= 0) {
|
||||
int count = getFilteredEvents((MilledBatchAllEvent) event, game).size();
|
||||
if (count == 0) {
|
||||
return false;
|
||||
}
|
||||
getEffects().setValue(SavedMilledValue.VALUE_KEY, count);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
package mage.abilities.common;
|
||||
|
||||
import mage.abilities.BatchTriggeredAbility;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.*;
|
||||
|
||||
/**
|
||||
* @author xenohedron
|
||||
*/
|
||||
public class OpponentDealtNoncombatDamageTriggeredAbility extends TriggeredAbilityImpl implements BatchTriggeredAbility<DamagedPlayerEvent> {
|
||||
|
||||
public OpponentDealtNoncombatDamageTriggeredAbility(Effect effect) {
|
||||
this(Zone.BATTLEFIELD, effect, false);
|
||||
}
|
||||
|
||||
public OpponentDealtNoncombatDamageTriggeredAbility(Zone zone, Effect effect, boolean optional) {
|
||||
super(zone, effect, optional);
|
||||
setTriggerPhrase("Whenever an opponent is dealt noncombat damage, ");
|
||||
}
|
||||
|
||||
protected OpponentDealtNoncombatDamageTriggeredAbility(final OpponentDealtNoncombatDamageTriggeredAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OpponentDealtNoncombatDamageTriggeredAbility copy() {
|
||||
return new OpponentDealtNoncombatDamageTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_ONE_PLAYER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
// all events in the batch are always relevant if triggers at all
|
||||
if (game.getOpponents(getControllerId()).contains(event.getTargetId())
|
||||
&& !((DamagedBatchForOnePlayerEvent) event).isCombatDamage()) {
|
||||
this.getAllEffects().setValue("damage", event.getAmount());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package mage.abilities.common;
|
||||
|
||||
import mage.abilities.BatchTriggeredAbility;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.constants.SetTargetPointer;
|
||||
|
|
@ -9,16 +10,18 @@ import mage.filter.FilterPermanent;
|
|||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.SacrificedPermanentBatchEvent;
|
||||
import mage.game.events.SacrificedPermanentEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
import mage.target.targetpointer.FixedTargets;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author TheElk801, xenohedron
|
||||
*/
|
||||
public class SacrificeOneOrMorePermanentsTriggeredAbility extends TriggeredAbilityImpl {
|
||||
public class SacrificeOneOrMorePermanentsTriggeredAbility extends TriggeredAbilityImpl implements BatchTriggeredAbility<SacrificedPermanentEvent> {
|
||||
|
||||
private final FilterPermanent filter;
|
||||
private final SetTargetPointer setTargetPointer;
|
||||
|
|
@ -64,31 +67,30 @@ public class SacrificeOneOrMorePermanentsTriggeredAbility extends TriggeredAbili
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
ArrayList<Permanent> matchingPermanents = new ArrayList<>();
|
||||
for (GameEvent sEvent : ((SacrificedPermanentBatchEvent) event).getEvents()) {
|
||||
Permanent permanent = game.getPermanentOrLKIBattlefield(sEvent.getTargetId());
|
||||
if (permanent != null && filter.match(permanent, getControllerId(), this, game)) {
|
||||
switch (sacrificingPlayer) {
|
||||
case YOU:
|
||||
if (!sEvent.getPlayerId().equals(getControllerId())) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case OPPONENT:
|
||||
Player controller = game.getPlayer(getControllerId());
|
||||
if (controller == null || !controller.hasOpponent(sEvent.getPlayerId(), game)) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case ANY:
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported TargetController in SacrificePermanentTriggeredAbility: " + sacrificingPlayer);
|
||||
}
|
||||
matchingPermanents.add(permanent);
|
||||
}
|
||||
public boolean checkEvent(SacrificedPermanentEvent event, Game game) {
|
||||
Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId());
|
||||
if (permanent == null || !filter.match(permanent, getControllerId(), this, game)) {
|
||||
return false;
|
||||
}
|
||||
switch (sacrificingPlayer) {
|
||||
case YOU:
|
||||
return isControlledBy(event.getPlayerId());
|
||||
case OPPONENT:
|
||||
return game.getOpponents(getControllerId()).contains(event.getPlayerId());
|
||||
case ANY:
|
||||
return true;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported TargetController in SacrificePermanentTriggeredAbility: " + sacrificingPlayer); }
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
List<Permanent> matchingPermanents = getFilteredEvents((SacrificedPermanentBatchEvent) event, game)
|
||||
.stream()
|
||||
.map(GameEvent::getTargetId)
|
||||
.map(game::getPermanentOrLKIBattlefield)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
if (matchingPermanents.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
package mage.abilities.common;
|
||||
|
||||
import mage.abilities.BatchTriggeredAbility;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.FilterSpell;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.DamagedEvent;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.stack.Spell;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
/**
|
||||
* @author xenohedron
|
||||
*/
|
||||
public class SpellControlledDealsDamageTriggeredAbility extends TriggeredAbilityImpl implements BatchTriggeredAbility<DamagedEvent> {
|
||||
|
||||
private final FilterSpell filter;
|
||||
|
||||
public SpellControlledDealsDamageTriggeredAbility(Zone zone, Effect effect, FilterSpell filter, boolean optional) {
|
||||
super(zone, effect, optional);
|
||||
this.filter = filter;
|
||||
setTriggerPhrase(getWhen() + CardUtil.addArticle(filter.getMessage()) + " you control deals damage, ");
|
||||
}
|
||||
|
||||
protected SpellControlledDealsDamageTriggeredAbility(final SpellControlledDealsDamageTriggeredAbility ability) {
|
||||
super(ability);
|
||||
this.filter = ability.filter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpellControlledDealsDamageTriggeredAbility copy() {
|
||||
return new SpellControlledDealsDamageTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.DAMAGED_BATCH_BY_SOURCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
// all events in the batch are always relevant if triggers at all
|
||||
Spell spell = game.getSpellOrLKIStack(event.getSourceId());
|
||||
if (spell == null
|
||||
|| !isControlledBy(spell.getControllerId())
|
||||
|| !filter.match(spell, getControllerId(), this, game)) {
|
||||
return false;
|
||||
}
|
||||
getEffects().setValue("damage", event.getAmount());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
package mage.abilities.common;
|
||||
|
||||
import mage.abilities.BatchTriggeredAbility;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.DamagedPlayerEvent;
|
||||
import mage.game.events.GameEvent;
|
||||
|
||||
/**
|
||||
* @author xenohedron
|
||||
*/
|
||||
public class YoureDealtDamageTriggeredAbility extends TriggeredAbilityImpl implements BatchTriggeredAbility<DamagedPlayerEvent> {
|
||||
|
||||
public YoureDealtDamageTriggeredAbility(Effect effect, boolean optional) {
|
||||
this(Zone.BATTLEFIELD, effect, optional);
|
||||
}
|
||||
|
||||
public YoureDealtDamageTriggeredAbility(Zone zone, Effect effect, boolean optional) {
|
||||
super(zone, effect, optional);
|
||||
setTriggerPhrase("Whenever you're dealt damage, ");
|
||||
}
|
||||
|
||||
protected YoureDealtDamageTriggeredAbility(final YoureDealtDamageTriggeredAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public YoureDealtDamageTriggeredAbility copy() {
|
||||
return new YoureDealtDamageTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_ONE_PLAYER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
// all events in the batch are always relevant
|
||||
if (isControlledBy(event.getTargetId())) {
|
||||
this.getAllEffects().setValue("damage", event.getAmount());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package mage.designations;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.BatchTriggeredAbility;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.hint.common.CurrentDungeonHint;
|
||||
|
|
@ -8,9 +9,10 @@ import mage.constants.Outcome;
|
|||
import mage.constants.Zone;
|
||||
import mage.game.Controllable;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.DamagedEvent;
|
||||
import mage.game.events.DamagedBatchForPlayersEvent;
|
||||
import mage.game.events.DamagedBatchForOnePlayerEvent;
|
||||
import mage.game.events.DamagedPlayerEvent;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
|
||||
import java.util.Objects;
|
||||
|
|
@ -41,7 +43,7 @@ public class Initiative extends Designation {
|
|||
}
|
||||
}
|
||||
|
||||
class InitiativeDamageTriggeredAbility extends TriggeredAbilityImpl {
|
||||
class InitiativeDamageTriggeredAbility extends TriggeredAbilityImpl implements BatchTriggeredAbility<DamagedPlayerEvent> {
|
||||
|
||||
InitiativeDamageTriggeredAbility() {
|
||||
super(Zone.ALL, new InitiativeTakeEffect());
|
||||
|
|
@ -58,17 +60,22 @@ class InitiativeDamageTriggeredAbility extends TriggeredAbilityImpl {
|
|||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_PLAYERS;
|
||||
return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_ONE_PLAYER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEvent(DamagedPlayerEvent event, Game game) {
|
||||
if (!event.isCombatDamage() || !event.getTargetId().equals(game.getInitiativeId())) {
|
||||
return false;
|
||||
}
|
||||
Permanent permanent = game.getPermanentOrLKIBattlefield(event.getSourceId());
|
||||
return permanent != null && permanent.isCreature(game);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
DamagedBatchForPlayersEvent dEvent = (DamagedBatchForPlayersEvent) event;
|
||||
UUID playerId = dEvent
|
||||
.getEvents()
|
||||
UUID playerId = getFilteredEvents((DamagedBatchForOnePlayerEvent) event, game)
|
||||
.stream()
|
||||
.filter(DamagedEvent::isCombatDamage)
|
||||
.filter(e -> e.getTargetId().equals(game.getInitiativeId()))
|
||||
.map(GameEvent::getSourceId)
|
||||
.map(game::getPermanent)
|
||||
.filter(Objects::nonNull)
|
||||
|
|
@ -84,7 +91,7 @@ class InitiativeDamageTriggeredAbility extends TriggeredAbilityImpl {
|
|||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "Whenever one or more creatures a player controls deals combat damage to you, that player takes the initiative.";
|
||||
return "Whenever one or more creatures a player controls deal combat damage to the player who has the initiative, the controller of those creatures takes the initiative.";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -845,6 +845,8 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
// DAMAGED_BATCH_FOR_PERMANENTS + DAMAGED_BATCH_FOR_ONE_PERMANENT
|
||||
addSimultaneousDamageToPermanentBatches((DamagedPermanentEvent) damagedEvent, game);
|
||||
}
|
||||
// DAMAGED_BATCH_BY_SOURCE
|
||||
addSimultaneousDamageBySourceBatched(damagedEvent, game);
|
||||
// DAMAGED_BATCH_FOR_ALL
|
||||
addSimultaneousDamageToBatchForAll(damagedEvent, game);
|
||||
}
|
||||
|
|
@ -895,6 +897,22 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
}
|
||||
}
|
||||
|
||||
public void addSimultaneousDamageBySourceBatched(DamagedEvent damageEvent, Game game) {
|
||||
// find existing batch first
|
||||
boolean isBatchUsed = false;
|
||||
for (GameEvent event : simultaneousEvents) {
|
||||
if (event instanceof DamagedBatchBySourceEvent
|
||||
&& damageEvent.getSourceId().equals(event.getSourceId())) {
|
||||
((DamagedBatchBySourceEvent) event).addEvent(damageEvent);
|
||||
isBatchUsed = true;
|
||||
}
|
||||
}
|
||||
// new batch if necessary
|
||||
if (!isBatchUsed) {
|
||||
addSimultaneousEvent(new DamagedBatchBySourceEvent(damageEvent), game);
|
||||
}
|
||||
}
|
||||
|
||||
public void addSimultaneousDamageToBatchForAll(DamagedEvent damagedEvent, Game game) {
|
||||
boolean isBatchUsed = false;
|
||||
for (GameEvent event : simultaneousEvents) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package mage.game.command.emblems;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.DealCombatDamageControlledTriggeredAbility;
|
||||
import mage.abilities.common.OneOrMoreCombatDamagePlayerTriggeredAbility;
|
||||
import mage.abilities.condition.Condition;
|
||||
import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
|
||||
import mage.abilities.effects.Effect;
|
||||
|
|
@ -26,7 +26,7 @@ public final class LolthSpiderQueenEmblem extends Emblem {
|
|||
public LolthSpiderQueenEmblem() {
|
||||
super("Emblem Lolth");
|
||||
this.getAbilities().add(new ConditionalInterveningIfTriggeredAbility(
|
||||
new DealCombatDamageControlledTriggeredAbility(
|
||||
new OneOrMoreCombatDamagePlayerTriggeredAbility(
|
||||
Zone.COMMAND, new LolthSpiderQueenEmblemEffect(), StaticFilters.FILTER_PERMANENT_CREATURES, SetTargetPointer.PLAYER, false
|
||||
), LolthSpiderQueenEmblemCondition.instance, "Whenever an opponent " +
|
||||
"is dealt combat damage by one or more creatures you control, " +
|
||||
|
|
|
|||
|
|
@ -39,6 +39,9 @@ public abstract class BatchEvent<T extends GameEvent> extends GameEvent {
|
|||
if (firstEvent instanceof BatchEvent) { // sanity check, if you need it then think twice and research carefully
|
||||
throw new UnsupportedOperationException("Wrong code usage: nesting batch events not supported");
|
||||
}
|
||||
if (!eventType.isBatch()) { // sanity check, a batch event should use a batch EventType
|
||||
throw new IllegalArgumentException("Wrong code usage: batch event should use batch EventType: " + eventType);
|
||||
}
|
||||
this.addEvent(firstEvent);
|
||||
}
|
||||
|
||||
|
|
@ -50,6 +53,9 @@ public abstract class BatchEvent<T extends GameEvent> extends GameEvent {
|
|||
this.singleTargetId = false;
|
||||
this.singleSourceId = false;
|
||||
this.singlePlayerId = false;
|
||||
if (!eventType.isBatch()) { // sanity check, a batch event should use a batch EventType
|
||||
throw new IllegalArgumentException("Wrong code usage: batch event should use batch EventType: " + eventType);
|
||||
}
|
||||
}
|
||||
|
||||
public void addEvent(T event) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
package mage.game.events;
|
||||
|
||||
/**
|
||||
* Batch all simultaneous damage events dealt by a single source.
|
||||
*
|
||||
* @author Susucr
|
||||
*/
|
||||
public class DamagedBatchBySourceEvent extends BatchEvent<DamagedEvent> {
|
||||
|
||||
public DamagedBatchBySourceEvent(DamagedEvent firstEvent) {
|
||||
super(EventType.DAMAGED_BATCH_BY_SOURCE, false, true, false, firstEvent);
|
||||
}
|
||||
|
||||
public boolean isCombatDamage() {
|
||||
return getEvents()
|
||||
.stream()
|
||||
.anyMatch(DamagedEvent::isCombatDamage);
|
||||
}
|
||||
}
|
||||
|
|
@ -10,7 +10,7 @@ public abstract class DamagedEvent extends GameEvent {
|
|||
protected boolean combat;
|
||||
protected int excess;
|
||||
|
||||
public DamagedEvent(EventType type, UUID targetId, UUID attackerId, UUID playerId, int amount, boolean combat) {
|
||||
protected DamagedEvent(EventType type, UUID targetId, UUID attackerId, UUID playerId, int amount, boolean combat) {
|
||||
super(type, targetId, null, playerId, amount, false);
|
||||
this.combat = combat;
|
||||
this.excess = 0;
|
||||
|
|
@ -21,10 +21,6 @@ public abstract class DamagedEvent extends GameEvent {
|
|||
return combat;
|
||||
}
|
||||
|
||||
public boolean isPreventable() {
|
||||
return flag;
|
||||
}
|
||||
|
||||
public void setExcess(int excess) {
|
||||
this.excess = Math.max(excess, 0);
|
||||
}
|
||||
|
|
@ -33,7 +29,4 @@ public abstract class DamagedEvent extends GameEvent {
|
|||
return excess;
|
||||
}
|
||||
|
||||
public UUID getAttackerId() {
|
||||
return getSourceId();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ public class GameEvent implements Serializable {
|
|||
*/
|
||||
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
|
||||
ZONE_CHANGE_BATCH(true), // 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
|
||||
|
|
@ -116,11 +116,11 @@ public class GameEvent implements Serializable {
|
|||
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_ONE_PLAYER(true),
|
||||
/* MILLED_CARDS_BATCH_FOR_ALL,
|
||||
combines all MILLED_CARD events for any player in a single batch
|
||||
*/
|
||||
MILLED_CARDS_BATCH_FOR_ALL,
|
||||
MILLED_CARDS_BATCH_FOR_ALL(true),
|
||||
|
||||
/* DAMAGED_PLAYER
|
||||
targetId the id of the damaged player
|
||||
|
|
@ -134,18 +134,21 @@ public class GameEvent implements Serializable {
|
|||
/* DAMAGED_BATCH_FOR_PLAYERS,
|
||||
combines all player damage events to a single batch (event)
|
||||
*/
|
||||
DAMAGED_BATCH_FOR_PLAYERS,
|
||||
DAMAGED_BATCH_FOR_PLAYERS(true),
|
||||
|
||||
/* 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_ONE_PLAYER(true),
|
||||
/* DAMAGED_BATCH_BY_SOURCE
|
||||
combine all damage events from a single source to a single batch (event)
|
||||
*/
|
||||
DAMAGED_BATCH_BY_SOURCE(true),
|
||||
/* DAMAGED_BATCH_FOR_ALL
|
||||
includes all damage events, both permanent damage and player damage, in single batch event
|
||||
*/
|
||||
DAMAGED_BATCH_FOR_ALL,
|
||||
DAMAGED_BATCH_FOR_ALL(true),
|
||||
/* 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).
|
||||
|
|
@ -171,7 +174,7 @@ public class GameEvent implements Serializable {
|
|||
amount amount of life loss
|
||||
flag true = from combat damage - other from non combat damage
|
||||
*/
|
||||
LOST_LIFE_BATCH,
|
||||
LOST_LIFE_BATCH(true),
|
||||
/* LOST_LIFE_BATCH
|
||||
combines all player life lost events to a single batch (event)
|
||||
*/
|
||||
|
|
@ -426,7 +429,7 @@ public class GameEvent implements Serializable {
|
|||
/* TAPPED_BATCH
|
||||
combine all TAPPED events occuring at the same time in a single event
|
||||
*/
|
||||
TAPPED_BATCH,
|
||||
TAPPED_BATCH(true),
|
||||
UNTAP,
|
||||
/* UNTAPPED,
|
||||
targetId untapped permanent
|
||||
|
|
@ -439,7 +442,7 @@ public class GameEvent implements Serializable {
|
|||
/* UNTAPPED_BATCH
|
||||
combine all UNTAPPED events occuring at the same time in a single event
|
||||
*/
|
||||
UNTAPPED_BATCH,
|
||||
UNTAPPED_BATCH(true),
|
||||
FLIP, FLIPPED,
|
||||
TRANSFORMING, TRANSFORMED,
|
||||
ADAPT,
|
||||
|
|
@ -494,12 +497,12 @@ public class GameEvent implements Serializable {
|
|||
/* DAMAGED_BATCH_FOR_PERMANENTS
|
||||
combine all permanent damage events to a single batch (event)
|
||||
*/
|
||||
DAMAGED_BATCH_FOR_PERMANENTS,
|
||||
DAMAGED_BATCH_FOR_PERMANENTS(true),
|
||||
|
||||
/* 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,
|
||||
DAMAGED_BATCH_FOR_ONE_PERMANENT(true),
|
||||
|
||||
DESTROY_PERMANENT,
|
||||
/* DESTROY_PERMANENT_BY_LEGENDARY_RULE
|
||||
|
|
@ -515,7 +518,7 @@ public class GameEvent implements Serializable {
|
|||
flag true if no regeneration is allowed
|
||||
*/
|
||||
DESTROYED_PERMANENT,
|
||||
SACRIFICE_PERMANENT, SACRIFICED_PERMANENT, SACRIFICED_PERMANENT_BATCH,
|
||||
SACRIFICE_PERMANENT, SACRIFICED_PERMANENT, SACRIFICED_PERMANENT_BATCH(true),
|
||||
FIGHTED_PERMANENT,
|
||||
BATCH_FIGHT,
|
||||
EXPLOITED_CREATURE,
|
||||
|
|
@ -669,7 +672,21 @@ public class GameEvent implements Serializable {
|
|||
*/
|
||||
GAVE_GIFT,
|
||||
// custom events - must store some unique data to track
|
||||
CUSTOM_EVENT
|
||||
CUSTOM_EVENT;
|
||||
|
||||
private final boolean isBatch;
|
||||
|
||||
EventType() {
|
||||
this(false);
|
||||
}
|
||||
|
||||
EventType(boolean isBatch) {
|
||||
this.isBatch = isBatch;
|
||||
}
|
||||
|
||||
public boolean isBatch() {
|
||||
return isBatch;
|
||||
}
|
||||
}
|
||||
|
||||
public GameEvent(EventType type, UUID targetId, Ability source, UUID playerId) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package mage.game.events;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
|
|
@ -11,10 +12,9 @@ public class LifeLostBatchEvent extends BatchEvent<LifeLostEvent> {
|
|||
super(EventType.LOST_LIFE_BATCH, false, false, false, firstEvent);
|
||||
}
|
||||
|
||||
public int getLifeLostByPlayer(UUID playerID) {
|
||||
return getEvents()
|
||||
.stream()
|
||||
.filter(ev -> ev.getTargetId().equals(playerID))
|
||||
public static int getLifeLostByPlayer(Collection<LifeLostEvent> events, UUID playerId) {
|
||||
return events.stream()
|
||||
.filter(ev -> ev.getTargetId().equals(playerId))
|
||||
.mapToInt(GameEvent::getAmount)
|
||||
.sum();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,5 @@
|
|||
package mage.game.events;
|
||||
|
||||
import mage.cards.Cards;
|
||||
import mage.cards.CardsImpl;
|
||||
import mage.game.Game;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author Susucr
|
||||
*/
|
||||
|
|
@ -16,12 +9,4 @@ public class MilledBatchAllEvent extends BatchEvent<MilledCardEvent> {
|
|||
super(EventType.MILLED_CARDS_BATCH_FOR_ALL, false, false, false, event);
|
||||
}
|
||||
|
||||
public Cards getCards(Game game) {
|
||||
return new CardsImpl(getEvents()
|
||||
.stream()
|
||||
.map(mce -> mce.getCard(game))
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,5 @@
|
|||
package mage.game.events;
|
||||
|
||||
import mage.cards.Cards;
|
||||
import mage.cards.CardsImpl;
|
||||
import mage.game.Game;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author Susucr
|
||||
*/
|
||||
|
|
@ -16,12 +9,4 @@ public class MilledBatchForOnePlayerEvent extends BatchEvent<MilledCardEvent> {
|
|||
super(EventType.MILLED_CARDS_BATCH_FOR_ONE_PLAYER, false, false, true, event);
|
||||
}
|
||||
|
||||
public Cards getCards(Game game) {
|
||||
return new CardsImpl(getEvents()
|
||||
.stream()
|
||||
.map(mce -> mce.getCard(game))
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package mage.target.targetadjustment;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.OneOrMoreDamagePlayerTriggeredAbility;
|
||||
import mage.filter.Filter;
|
||||
import mage.filter.predicate.card.OwnerIdPredicate;
|
||||
import mage.filter.predicate.permanent.ControllerIdPredicate;
|
||||
|
|
@ -21,7 +22,7 @@ public class DamagedPlayerControlsTargetAdjuster extends GenericTargetAdjuster {
|
|||
|
||||
/**
|
||||
* Use with {@link mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility} with setTargetPointer enabled,
|
||||
* or {@link mage.abilities.common.OneOrMoreDealDamageTriggeredAbility} with "SetTargetPointer.PLAYER" or similar.
|
||||
* or {@link OneOrMoreDamagePlayerTriggeredAbility} with "SetTargetPointer.PLAYER" or similar.
|
||||
* Adjusts the target to only target something the damaged player controls (or owns with alternative constructor)
|
||||
* And then removes the effects' target pointer that the triggered ability set
|
||||
*/
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue