Fix handling of damage to permanents (WIP) (#7592)

* initial refactor of damage events

* cleaned up some instances of classes that need to be removed

* removed old damage event classes

* removed outdated imports

* temporarily refactor Everlasting Torment (this will need to be changed more)

* updated damage handling to use new changes

* some reworking of lethal/excess damage plus a test

* updated damage marking to handle planeswalkers

* updated implementation of Phyrexian Unlife

* updated implementation of Everlasting Torment

* added some more excess damage tests

* small change to wither check
This commit is contained in:
Evan Kranzler 2021-02-22 17:11:24 -05:00 committed by GitHub
parent 39f6b69391
commit 5390963d38
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
201 changed files with 1132 additions and 1187 deletions

View file

@ -2,24 +2,24 @@
package mage.abilities.common;
import mage.constants.Zone;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.Effect;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.DamagedCreatureEvent;
import mage.game.events.DamagedEvent;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.target.targetpointer.FixedTarget;
/**
*
* @author LevelX
*/
public class DealsCombatDamageToACreatureTriggeredAbility extends TriggeredAbilityImpl {
private boolean setTargetPointer;
private final boolean setTargetPointer;
public DealsCombatDamageToACreatureTriggeredAbility(Effect effect, boolean optional) {
this(effect, optional, false);
this(effect, optional, false);
}
public DealsCombatDamageToACreatureTriggeredAbility(Effect effect, boolean optional, boolean setTargetPointer) {
@ -28,38 +28,41 @@ public class DealsCombatDamageToACreatureTriggeredAbility extends TriggeredAbili
}
public DealsCombatDamageToACreatureTriggeredAbility(final DealsCombatDamageToACreatureTriggeredAbility ability) {
super(ability);
this.setTargetPointer = ability.setTargetPointer;
super(ability);
this.setTargetPointer = ability.setTargetPointer;
}
@Override
public DealsCombatDamageToACreatureTriggeredAbility copy() {
return new DealsCombatDamageToACreatureTriggeredAbility(this);
return new DealsCombatDamageToACreatureTriggeredAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.DAMAGED_CREATURE;
return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (event.getSourceId().equals(this.sourceId)
&& ((DamagedCreatureEvent) event).isCombatDamage()) {
if (setTargetPointer) {
for (Effect effect : this.getEffects()) {
effect.setTargetPointer(new FixedTarget(event.getTargetId()));
effect.setValue("damage", event.getAmount());
}
}
return true;
Permanent permanent = game.getPermanent(event.getTargetId());
if (permanent == null
|| !permanent.isCreature()
|| !event.getSourceId().equals(this.sourceId)
|| !((DamagedEvent) event).isCombatDamage()) {
return false;
}
return false;
if (setTargetPointer) {
for (Effect effect : this.getEffects()) {
effect.setTargetPointer(new FixedTarget(event.getTargetId(), game));
effect.setValue("damage", event.getAmount());
}
}
return true;
}
@Override
public String getRule() {
return "Whenever {this} deals combat damage to a creature, " + super.getRule();
return "Whenever {this} deals combat damage to a creature, " + super.getRule();
}
}

View file

@ -6,7 +6,7 @@ import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.DamagedEvent;
import mage.game.events.GameEvent;
import mage.players.Player;
import mage.game.permanent.Permanent;
import mage.target.targetpointer.FixedTarget;
/**
@ -60,28 +60,34 @@ public class DealsCombatDamageToAPlayerTriggeredAbility extends TriggeredAbility
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.DAMAGED_PLAYER
|| (orPlaneswalker && event.getType() == GameEvent.EventType.DAMAGED_PLANESWALKER);
|| event.getType() == GameEvent.EventType.DAMAGED_PERMANENT;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (event.getSourceId().equals(getSourceId())
&& ((DamagedEvent) event).isCombatDamage()) {
if (onlyOpponents && event.getType() == GameEvent.EventType.DAMAGED_PLAYER) {
Player controller = game.getPlayer(getControllerId());
if (controller == null || !controller.hasOpponent(event.getPlayerId(), game)) {
if (!event.getSourceId().equals(getSourceId())
|| !((DamagedEvent) event).isCombatDamage()) {
return false;
}
switch (event.getType()) {
case DAMAGED_PLAYER:
if (onlyOpponents && !game.getOpponents(getControllerId()).contains(event.getTargetId())) {
return false;
}
}
if (setTargetPointer) {
for (Effect effect : this.getAllEffects()) {
effect.setTargetPointer(new FixedTarget(event.getPlayerId()));
effect.setValue("damage", event.getAmount());
break;
case DAMAGED_PERMANENT:
Permanent permanent = game.getPermanent(event.getTargetId());
if (permanent == null
|| !permanent.isPlaneswalker()
|| !orPlaneswalker) {
return false;
}
}
return true;
}
return false;
if (setTargetPointer) {
getAllEffects().setTargetPointer(new FixedTarget(event.getPlayerId()));
getAllEffects().setValue("damage", event.getAmount());
}
return true;
}
@Override

View file

@ -28,9 +28,8 @@ public class DealsDamageAttachedTriggeredAbility extends TriggeredAbilityImpl {
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.DAMAGED_CREATURE
|| event.getType() == GameEvent.EventType.DAMAGED_PLAYER
|| event.getType() == GameEvent.EventType.DAMAGED_PLANESWALKER;
return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT
|| event.getType() == GameEvent.EventType.DAMAGED_PLAYER;
}
@Override

View file

@ -33,9 +33,8 @@ public class DealsDamageGainLifeSourceTriggeredAbility extends TriggeredAbilityI
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.DAMAGED_CREATURE
|| event.getType() == GameEvent.EventType.DAMAGED_PLAYER
|| event.getType() == GameEvent.EventType.DAMAGED_PLANESWALKER;
return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT
|| event.getType() == GameEvent.EventType.DAMAGED_PLAYER;
}
@Override

View file

@ -6,14 +6,12 @@ import mage.constants.SetTargetPointer;
import mage.constants.Zone;
import mage.filter.FilterPermanent;
import mage.game.Game;
import mage.game.events.DamagedCreatureEvent;
import mage.game.events.DamagedEvent;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import mage.game.permanent.Permanent;
import mage.target.targetpointer.FixedTarget;
/**
*
* @author LevelX2
*/
public class DealsDamageToACreatureAllTriggeredAbility extends TriggeredAbilityImpl {
@ -27,15 +25,15 @@ public class DealsDamageToACreatureAllTriggeredAbility extends TriggeredAbilityI
*
* @param effect
* @param optional
* @param filterPermanent The filter that restricts which permanets have to
* trigger
* @param filterPermanent The filter that restricts which permanets have to
* trigger
* @param setTargetPointer The target to be set to target pointer of the
* effect.<br>
* - PLAYER = player controlling the damage source.<br>
* - PERMANENT = source permanent.<br>
* - PERMANENT_TARGET = damaged creature.
* effect.<br>
* - PLAYER = player controlling the damage source.<br>
* - PERMANENT = source permanent.<br>
* - PERMANENT_TARGET = damaged creature.
* @param combatDamageOnly The flag to determine if only combat damage has
* to trigger
* to trigger
*/
public DealsDamageToACreatureAllTriggeredAbility(Effect effect, boolean optional, FilterPermanent filterPermanent, SetTargetPointer setTargetPointer, boolean combatDamageOnly) {
super(Zone.BATTLEFIELD, effect, optional);
@ -58,37 +56,42 @@ public class DealsDamageToACreatureAllTriggeredAbility extends TriggeredAbilityI
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.DAMAGED_CREATURE;
return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (!combatDamageOnly || ((DamagedCreatureEvent) event).isCombatDamage()) {
Permanent permanent = game.getPermanentOrLKIBattlefield(event.getSourceId());
if (permanent != null && filterPermanent.match(permanent, getSourceId(), getControllerId(), game)) {
for (Effect effect : this.getEffects()) {
effect.setValue("damage", event.getAmount());
effect.setValue("sourceId", event.getSourceId());
switch (setTargetPointer) {
case PLAYER:
effect.setTargetPointer(new FixedTarget(permanent.getControllerId()));
break;
case PERMANENT:
effect.setTargetPointer(new FixedTarget(permanent, game));
break;
case PERMANENT_TARGET:
Permanent permanent_target = game.getPermanentOrLKIBattlefield(event.getTargetId());
if (permanent_target != null) {
effect.setTargetPointer(new FixedTarget(permanent_target, game));
}
break;
}
}
return true;
}
Permanent permanent = game.getPermanent(event.getTargetId());
if (permanent == null || !permanent.isCreature()) {
return false;
}
return false;
if (combatDamageOnly && !((DamagedEvent) event).isCombatDamage()) {
return false;
}
permanent = game.getPermanentOrLKIBattlefield(event.getSourceId());
if (permanent == null || !filterPermanent.match(permanent, getSourceId(), getControllerId(), game)) {
return false;
}
for (Effect effect : this.getEffects()) {
effect.setValue("damage", event.getAmount());
effect.setValue("sourceId", event.getSourceId());
switch (setTargetPointer) {
case PLAYER:
effect.setTargetPointer(new FixedTarget(permanent.getControllerId()));
break;
case PERMANENT:
effect.setTargetPointer(new FixedTarget(permanent, game));
break;
case PERMANENT_TARGET:
Permanent permanent_target = game.getPermanentOrLKIBattlefield(event.getTargetId());
if (permanent_target != null) {
effect.setTargetPointer(new FixedTarget(permanent_target, game));
}
break;
}
}
return true;
}
@Override

View file

@ -6,7 +6,7 @@ import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.Effect;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.DamagedCreatureEvent;
import mage.game.events.DamagedEvent;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.target.targetpointer.FixedTarget;
@ -40,12 +40,12 @@ public class DealsDamageToACreatureAttachedTriggeredAbility extends TriggeredAbi
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.DAMAGED_CREATURE;
return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (!combatOnly || ((DamagedCreatureEvent) event).isCombatDamage()) {
if (!combatOnly || ((DamagedEvent) event).isCombatDamage()) {
Permanent attachment = game.getPermanent(this.getSourceId());
if (attachment != null
&& attachment.isAttachedTo(event.getSourceId())) {

View file

@ -6,7 +6,7 @@ import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.Effect;
import mage.filter.common.FilterCreaturePermanent;
import mage.game.Game;
import mage.game.events.DamagedCreatureEvent;
import mage.game.events.DamagedEvent;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.target.targetpointer.FixedTarget;
@ -46,13 +46,13 @@ public class DealsDamageToACreatureTriggeredAbility extends TriggeredAbilityImpl
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.DAMAGED_CREATURE;
return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (event.getSourceId().equals(this.sourceId)
&& (!combatOnly || ((DamagedCreatureEvent) event).isCombatDamage())) {
&& (!combatOnly || ((DamagedEvent) event).isCombatDamage())) {
if (filter != null) {
Permanent creature = game.getPermanentOrLKIBattlefield(event.getTargetId());
if (creature == null || !filter.match(creature, getSourceId(), getControllerId(), game)) {

View file

@ -7,6 +7,7 @@ import mage.abilities.effects.Effect;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.target.targetpointer.FixedTarget;
/**
@ -44,21 +45,27 @@ public class DealsDamageToAPlayerTriggeredAbility extends TriggeredAbilityImpl {
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.DAMAGED_PLAYER
|| (orPlaneswalker && event.getType() == GameEvent.EventType.DAMAGED_PLANESWALKER);
|| event.getType() == GameEvent.EventType.DAMAGED_PERMANENT;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (event.getSourceId().equals(this.sourceId)) {
if (setTargetPointer) {
for (Effect effect : this.getEffects()) {
effect.setTargetPointer(new FixedTarget(event.getPlayerId()));
effect.setValue("damage", event.getAmount());
}
}
return true;
if (!event.getSourceId().equals(this.sourceId)) {
return false;
}
return false;
if (event.getType() == GameEvent.EventType.DAMAGED_PERMANENT) {
Permanent permanent = game.getPermanent(event.getTargetId());
if (permanent == null
|| !permanent.isPlaneswalker()
|| !orPlaneswalker) {
return false;
}
}
if (setTargetPointer) {
getEffects().setTargetPointer(new FixedTarget(event.getPlayerId()));
getEffects().setValue("damage", event.getAmount());
}
return true;
}
@Override

View file

@ -41,7 +41,7 @@ public class DealtDamageAttachedTriggeredAbility extends TriggeredAbilityImpl {
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.DAMAGED_CREATURE;
return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT;
}
@Override

View file

@ -5,7 +5,7 @@ import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.Effect;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.DamagedCreatureEvent;
import mage.game.events.DamagedEvent;
import mage.game.events.GameEvent;
/**
@ -46,12 +46,12 @@ public class DealtDamageToSourceTriggeredAbility extends TriggeredAbilityImpl {
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.DAMAGED_CREATURE || event.getType() == GameEvent.EventType.COMBAT_DAMAGE_STEP_POST;
return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT || event.getType() == GameEvent.EventType.COMBAT_DAMAGE_STEP_POST;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (event.getType() == GameEvent.EventType.DAMAGED_CREATURE && event.getTargetId().equals(getSourceId())) {
if (event.getType() == GameEvent.EventType.DAMAGED_PERMANENT && event.getTargetId().equals(getSourceId())) {
if (useValue) {
// TODO: this ability should only trigger once for multiple creatures dealing combat damage.
// If the damaged creature uses the amount (e.g. Boros Reckoner), this will still trigger separately instead of all at once
@ -60,7 +60,7 @@ public class DealtDamageToSourceTriggeredAbility extends TriggeredAbilityImpl {
}
return true;
} else {
if (((DamagedCreatureEvent) event).isCombatDamage()) {
if (((DamagedEvent) event).isCombatDamage()) {
if (!usedForCombatDamageStep) {
usedForCombatDamageStep = true;
return true;

View file

@ -35,22 +35,23 @@ public class DestroyPlaneswalkerWhenDamagedTriggeredAbility extends TriggeredAbi
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.DAMAGED_PLANESWALKER;
return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
Permanent permanent = getSourcePermanentIfItStillExists(game);
if (permanent != null) {
boolean applies = filter != null ?
filter.match(permanent, game) : event.getSourceId().equals(getSourceId());
if (applies) {
Effect effect = new DestroyTargetEffect();
effect.setTargetPointer(new FixedTarget(event.getTargetId(), game));
this.getEffects().clear();
this.addEffect(effect);
return true;
}
if (permanent == null) {
return false;
}
boolean applies = filter != null ?
permanent.isPlaneswalker() && filter.match(permanent, game) : event.getSourceId().equals(getSourceId());
if (applies) {
Effect effect = new DestroyTargetEffect();
effect.setTargetPointer(new FixedTarget(event.getTargetId(), game));
this.getEffects().clear();
this.addEffect(effect);
return true;
}
return false;
}

View file

@ -96,9 +96,8 @@ public abstract class PreventionEffectImpl extends ReplacementEffectImpl impleme
@Override
public boolean checksEventType(GameEvent event, Game game) {
switch (event.getType()) {
case DAMAGE_CREATURE:
case DAMAGE_PERMANENT:
case DAMAGE_PLAYER:
case DAMAGE_PLANESWALKER:
return true;
}
return false;

View file

@ -52,9 +52,8 @@ public abstract class RedirectionEffect extends ReplacementEffectImpl {
@Override
public boolean checksEventType(GameEvent event, Game game) {
switch (event.getType()) {
case DAMAGE_CREATURE:
case DAMAGE_PERMANENT:
case DAMAGE_PLAYER:
case DAMAGE_PLANESWALKER:
return true;
}
return false;

View file

@ -51,9 +51,8 @@ public class AssignNoCombatDamageSourceEffect extends ReplacementEffectImpl {
@Override
public boolean checksEventType(GameEvent event, Game game) {
switch (event.getType()) {
case DAMAGE_CREATURE:
case DAMAGE_PERMANENT:
case DAMAGE_PLAYER:
case DAMAGE_PLANESWALKER:
return true;
default:
return false;

View file

@ -8,7 +8,6 @@ import mage.abilities.keyword.*;
import mage.constants.AsThoughEffectType;
import mage.constants.Outcome;
import mage.filter.StaticFilters;
import mage.filter.common.FilterCreaturePermanent;
import mage.game.Game;
import mage.game.events.BlockerDeclaredEvent;
import mage.game.events.DeclareBlockerEvent;
@ -214,6 +213,12 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
permanent.applyDamage(game);
}
}
if (defenderIsPlaneswalker) {
Permanent permanent = game.getPermanent(defenderId);
if (permanent != null) {
permanent.applyDamage(game);
}
}
}
/**
@ -896,27 +901,6 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
}
private static int getLethalDamage(Permanent blocker, Permanent attacker, Game game) {
int lethalDamage;
if (attacker.getAbilities().containsKey(DeathtouchAbility.getInstance().getId())) {
lethalDamage = 1;
} else {
lethalDamage = getLethalDamage(blocker, game);
}
return lethalDamage;
}
public static int getLethalDamage(Permanent damagedPermanent, Game game) {
List<FilterCreaturePermanent> usePowerInsteadOfToughnessForDamageLethalityFilters = game.getState().getActivePowerInsteadOfToughnessForDamageLethalityFilters();
/*
* for handling Zilortha, Strength Incarnate:
* 2020-04-17
* Any time the game is checking whether damage is lethal or if a creature should be destroyed for having lethal damage marked on it, use the power of your creatures rather than their toughness to check the damage against. This includes being assigned trample damage, damage from Flame Spill, and so on.
*/
boolean usePowerInsteadOfToughnessForDamageLethality = usePowerInsteadOfToughnessForDamageLethalityFilters.stream()
.anyMatch(filter -> filter.match(damagedPermanent, game));
int lethalDamageThreshold = usePowerInsteadOfToughnessForDamageLethality ?
// Zilortha, Strength Incarnate, 2020-04-17: A creature with 0 power isnt destroyed unless it has at least 1 damage marked on it.
Math.max(damagedPermanent.getPower().getValue(), 1) : damagedPermanent.getToughness().getValue();
return Math.max(lethalDamageThreshold - damagedPermanent.getDamage(), 0);
return blocker.getLethalDamage(attacker.getId(), game);
}
}

View file

@ -58,9 +58,9 @@ class AjaniSteadfastPreventEffect extends PreventionEffectImpl {
return super.applies(event, source, game);
}
if (event.getType() == GameEvent.EventType.DAMAGE_PLANESWALKER) {
if (event.getType() == GameEvent.EventType.DAMAGE_PERMANENT) {
Permanent permanent = game.getPermanent(event.getTargetId());
if (permanent != null && permanent.isControlledBy(source.getControllerId())) {
if (permanent != null && permanent.isPlaneswalker() && permanent.isControlledBy(source.getControllerId())) {
return super.applies(event, source, game);
}
}

View file

@ -1,14 +0,0 @@
package mage.game.events;
import java.util.UUID;
/**
*
* @author BetaSteward_at_googlemail.com
*/
public class DamageCreatureEvent extends DamageEvent {
public DamageCreatureEvent(UUID targetId, UUID damageSourceId, UUID targetControllerId, int amount, boolean preventable, boolean combat) {
super(GameEvent.EventType.DAMAGE_CREATURE, targetId, damageSourceId, targetControllerId, amount, preventable, combat);
}
}

View file

@ -3,12 +3,13 @@ package mage.game.events;
import java.util.UUID;
/**
*
* @author BetaSteward_at_googlemail.com
*/
public abstract class DamageEvent extends GameEvent {
protected boolean combat;
private boolean asThoughInfect = false;
private boolean asThoughWither = false;
public DamageEvent(EventType type, UUID targetId, UUID damageSourceId, UUID targetControllerId, int amount, boolean preventable, boolean combat) {
super(type, targetId, null, targetControllerId, amount, preventable);
@ -24,4 +25,19 @@ public abstract class DamageEvent extends GameEvent {
return flag;
}
public void setAsThoughInfect(boolean asThoughInfect) {
this.asThoughInfect = asThoughInfect;
}
public boolean isAsThoughInfect() {
return asThoughInfect;
}
public void setAsThoughWither(boolean asThoughWither) {
this.asThoughWither = asThoughWither;
}
public boolean isAsThoughWither() {
return asThoughWither;
}
}

View file

@ -0,0 +1,13 @@
package mage.game.events;
import java.util.UUID;
/**
* @author TheElk801
*/
public class DamagePermanentEvent extends DamageEvent {
public DamagePermanentEvent(UUID targetId, UUID damageSourceId, UUID targetControllerId, int amount, boolean preventable, boolean combat) {
super(EventType.DAMAGE_PERMANENT, targetId, damageSourceId, targetControllerId, amount, preventable, combat);
}
}

View file

@ -1,14 +0,0 @@
package mage.game.events;
import java.util.UUID;
/**
*
* @author BetaSteward_at_googlemail.com
*/
public class DamagePlaneswalkerEvent extends DamageEvent {
public DamagePlaneswalkerEvent(UUID targetId, UUID damageSourceId, UUID targetControllerId, int amount, boolean preventable, boolean combat) {
super(GameEvent.EventType.DAMAGE_PLANESWALKER, targetId, damageSourceId, targetControllerId, amount, preventable, combat);
}
}

View file

@ -33,11 +33,8 @@ public abstract class DamagedBatchEvent extends GameEvent {
if (damagedEvent instanceof DamagedPlayerEvent) {
event = new DamagedPlayerBatchEvent();
event.addEvent(damagedEvent);
} else if (damagedEvent instanceof DamagedCreatureEvent) {
event = new DamagedCreatureBatchEvent();
event.addEvent(damagedEvent);
} else if (damagedEvent instanceof DamagedPlaneswalkerEvent) {
event = new DamagedPlaneswalkerBatchEvent();
} else if (damagedEvent instanceof DamagedPermanentEvent) {
event = new DamagedPermanentBatchEvent();
event.addEvent(damagedEvent);
}
return event;

View file

@ -1,11 +0,0 @@
package mage.game.events;
/**
* @author TheElk801
*/
public class DamagedCreatureBatchEvent extends DamagedBatchEvent {
public DamagedCreatureBatchEvent() {
super(GameEvent.EventType.DAMAGED_CREATURE_BATCH, DamagedCreatureEvent.class);
}
}

View file

@ -1,16 +0,0 @@
package mage.game.events;
import java.util.UUID;
/**
*
* @author BetaSteward_at_googlemail.com
*/
public class DamagedCreatureEvent extends DamagedEvent {
public DamagedCreatureEvent(UUID targetId, UUID attackerId, UUID playerId, int amount, boolean combat) {
super(GameEvent.EventType.DAMAGED_CREATURE, targetId, attackerId, playerId, amount, combat);
}
}

View file

@ -0,0 +1,11 @@
package mage.game.events;
/**
* @author TheElk801
*/
public class DamagedPermanentBatchEvent extends DamagedBatchEvent {
public DamagedPermanentBatchEvent() {
super(EventType.DAMAGED_PERMANENT_BATCH, DamagedPermanentEvent.class);
}
}

View file

@ -0,0 +1,13 @@
package mage.game.events;
import java.util.UUID;
/**
* @author TheElk801
*/
public class DamagedPermanentEvent extends DamagedEvent {
public DamagedPermanentEvent(UUID targetId, UUID attackerId, UUID playerId, int amount, boolean combat) {
super(EventType.DAMAGED_PERMANENT, targetId, attackerId, playerId, amount, combat);
}
}

View file

@ -1,11 +0,0 @@
package mage.game.events;
/**
* @author TheElk801
*/
public class DamagedPlaneswalkerBatchEvent extends DamagedBatchEvent {
public DamagedPlaneswalkerBatchEvent() {
super(GameEvent.EventType.DAMAGED_PLANESWALKER_BATCH, DamagedPlaneswalkerEvent.class);
}
}

View file

@ -1,16 +0,0 @@
package mage.game.events;
import java.util.UUID;
/**
*
* @author BetaSteward_at_googlemail.com
*/
public class DamagedPlaneswalkerEvent extends DamagedEvent {
public DamagedPlaneswalkerEvent(UUID targetId, UUID attackerId, UUID playerId, int amount, boolean combat) {
super(GameEvent.EventType.DAMAGED_PLANESWALKER, targetId, null, playerId, amount, combat);
this.setSourceId(attackerId);
}
}

View file

@ -359,8 +359,9 @@ public class GameEvent implements Serializable {
flag not used for this event
*/
OPTION_USED,
DAMAGE_CREATURE, DAMAGED_CREATURE, DAMAGED_CREATURE_BATCH,
DAMAGE_PLANESWALKER, DAMAGED_PLANESWALKER, DAMAGED_PLANESWALKER_BATCH,
DAMAGE_PERMANENT,
DAMAGED_PERMANENT,
DAMAGED_PERMANENT_BATCH,
DESTROY_PERMANENT,
/* DESTROY_PERMANENT_BY_LEGENDARY_RULE
targetId id of the permanent to destroy
@ -492,8 +493,7 @@ public class GameEvent implements Serializable {
return new GameEvent(customEventType, targetId, source, playerId);
}
private GameEvent(EventType type, UUID customEventType, UUID targetId, Ability source, UUID playerId, int amount, boolean flag)
{
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);
}

View file

@ -101,7 +101,7 @@ public interface Permanent extends Card, Controllable {
/**
* @param attachment
* @param source can be null for default checks like state base
* @param source can be null for default checks like state base
* @param game
* @param silentMode - use it to ignore warning message for users (e.g. for
* checking only)
@ -123,8 +123,8 @@ public interface Permanent extends Card, Controllable {
* Uses in replace events only
*
* @param damage
* @param attackerId id of the permanent or player who make damage (source.getSourceId() in most cases)
* @param source can be null for default game actions like combat
* @param attackerId id of the permanent or player who make damage (source.getSourceId() in most cases)
* @param source can be null for default game actions like combat
* @param game
* @param combat
* @param preventable
@ -137,8 +137,8 @@ public interface Permanent extends Card, Controllable {
* Uses in combat only to deal damage at the same time
*
* @param damage
* @param attackerId id of the permanent or player who make damage (source.getSourceId() in most cases)
* @param source can be null for default game actions like combat
* @param attackerId id of the permanent or player who make damage (source.getSourceId() in most cases)
* @param source can be null for default game actions like combat
* @param game
* @param preventable
* @param combat
@ -150,6 +150,8 @@ public interface Permanent extends Card, Controllable {
int applyDamage(Game game);
int getLethalDamage(UUID attackerId, Game game);
void removeAllDamage(Game game);
void reset(Game game);
@ -159,7 +161,6 @@ public interface Permanent extends Card, Controllable {
boolean destroy(Ability source, Game game, boolean noRegen);
/**
*
* @param source can be null for state base actions
* @param game
* @return

View file

@ -50,15 +50,17 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
private static final Logger logger = Logger.getLogger(PermanentImpl.class);
static class MarkedDamageInfo implements Serializable {
private static class MarkedDamageInfo implements Serializable {
public MarkedDamageInfo(Counter counter, MageObject sourceObject) {
private final Counter counter;
private final MageObject sourceObject;
private final boolean addCounters;
private MarkedDamageInfo(Counter counter, MageObject sourceObject, boolean addCounters) {
this.counter = counter;
this.sourceObject = sourceObject;
this.addCounters = addCounters;
}
Counter counter;
MageObject sourceObject;
}
private static final ThreadLocalStringBuilder threadLocalBuilder = new ThreadLocalStringBuilder(300);
@ -146,7 +148,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
if (permanent.markedDamage != null) {
markedDamage = new ArrayList<>();
for (MarkedDamageInfo mdi : permanent.markedDamage) {
markedDamage.add(new MarkedDamageInfo(mdi.counter.copy(), mdi.sourceObject));
markedDamage.add(new MarkedDamageInfo(mdi.counter.copy(), mdi.sourceObject, mdi.addCounters));
}
}
if (permanent.info != null) {
@ -864,71 +866,127 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
*/
private int doDamage(int damageAmount, UUID attackerId, Ability source, Game game, boolean preventable, boolean combat, boolean markDamage, List<UUID> appliedEffects) {
int damageDone = 0;
if (damageAmount > 0 && canDamage(game.getObject(attackerId), game)) {
if (this.isPlaneswalker()) {
damageDone = damagePlaneswalker(damageAmount, attackerId, source, game, preventable, combat, markDamage, appliedEffects);
if (damageAmount < 1 || !canDamage(game.getObject(attackerId), game)) {
return 0;
}
DamageEvent event = new DamagePermanentEvent(objectId, attackerId, controllerId, damageAmount, preventable, combat);
event.setAppliedEffects(appliedEffects);
if (game.replaceEvent(event)) {
return 0;
}
int actualDamage = checkProtectionAbilities(event, attackerId, source, game);
if (actualDamage < 1) {
return 0;
}
int lethal = getLethalDamage(attackerId, game);
MageObject attacker = game.getObject(attackerId);
if (this.isCreature()) {
if (checkWither(event, attacker, game)) {
if (markDamage) {
// mark damage only
markDamage(CounterType.M1M1.createInstance(actualDamage), attacker, true);
} else {
Ability damageSourceAbility = null;
if (attacker instanceof Permanent) {
damageSourceAbility = ((Permanent) attacker).getSpellAbility();
}
// deal damage immediately
addCounters(CounterType.M1M1.createInstance(actualDamage), game.getControllerId(attackerId), damageSourceAbility, game);
}
} else {
damageDone = damageCreature(damageAmount, attackerId, source, game, preventable, combat, markDamage, appliedEffects);
}
if (damageDone > 0) {
UUID sourceControllerId = null;
Abilities sourceAbilities = null;
MageObject attacker = game.getPermanentOrLKIBattlefield(attackerId);
if (attacker == null) {
StackObject stackObject = game.getStack().getStackObject(attackerId);
if (stackObject != null) {
attacker = stackObject.getStackAbility().getSourceObject(game);
} else {
attacker = game.getObject(attackerId);
}
if (attacker instanceof Spell) {
sourceAbilities = ((Spell) attacker).getAbilities(game);
sourceControllerId = ((Spell) attacker).getControllerId();
} else if (attacker instanceof Card) {
sourceAbilities = ((Card) attacker).getAbilities(game);
sourceControllerId = ((Card) attacker).getOwnerId();
} else if (attacker instanceof CommandObject) {
sourceControllerId = ((CommandObject) attacker).getControllerId();
sourceAbilities = attacker.getAbilities();
} else {
attacker = null;
}
} else {
sourceAbilities = ((Permanent) attacker).getAbilities(game);
sourceControllerId = ((Permanent) attacker).getControllerId();
}
if (attacker != null && sourceAbilities != null) {
if (sourceAbilities.containsKey(LifelinkAbility.getInstance().getId())) {
if (markDamage) {
game.getPermanent(attackerId).markLifelink(damageDone);
} else {
Player player = game.getPlayer(sourceControllerId);
player.gainLife(damageDone, game, source);
}
}
if (sourceAbilities.containsKey(DeathtouchAbility.getInstance().getId())) {
deathtouched = true;
}
if (dealtDamageByThisTurn == null) {
dealtDamageByThisTurn = new HashSet<>();
}
// Unstable ability - Earl of Squirrel
if (sourceAbilities.containsKey(SquirrellinkAbility.getInstance().getId())) {
Player player = game.getPlayer(sourceControllerId);
new SquirrelToken().putOntoBattlefield(damageDone, game, source, player.getId());
}
dealtDamageByThisTurn.add(new MageObjectReference(attacker, game));
}
if (attacker == null) {
game.informPlayers(getLogName() + " gets " + damageDone + " damage");
} else {
game.informPlayers(attacker.getLogName() + " deals " + damageDone + " damage to " + getLogName());
}
this.damage = CardUtil.overflowInc(this.damage, actualDamage);
}
}
if (this.isPlaneswalker()) {
int loyalty = getCounters(game).getCount(CounterType.LOYALTY);
int countersToRemove = Math.min(actualDamage, loyalty);
if (attacker != null && markDamage) {
markDamage(CounterType.LOYALTY.createInstance(countersToRemove), attacker, false);
} else {
removeCounters(CounterType.LOYALTY.getName(), countersToRemove, source, game);
}
}
DamagedEvent damagedEvent = new DamagedPermanentEvent(this.getId(), attackerId, this.getControllerId(), actualDamage, combat);
damagedEvent.setExcess(actualDamage - lethal);
game.fireEvent(damagedEvent);
game.getState().addSimultaneousDamage(damagedEvent, game);
damageDone = actualDamage;
if (damageDone < 1) {
return 0;
}
UUID sourceControllerId = null;
Abilities sourceAbilities = null;
attacker = game.getPermanentOrLKIBattlefield(attackerId);
if (attacker == null) {
StackObject stackObject = game.getStack().getStackObject(attackerId);
if (stackObject != null) {
attacker = stackObject.getStackAbility().getSourceObject(game);
} else {
attacker = game.getObject(attackerId);
}
if (attacker instanceof Spell) {
sourceAbilities = ((Spell) attacker).getAbilities(game);
sourceControllerId = ((Spell) attacker).getControllerId();
} else if (attacker instanceof Card) {
sourceAbilities = ((Card) attacker).getAbilities(game);
sourceControllerId = ((Card) attacker).getOwnerId();
} else if (attacker instanceof CommandObject) {
sourceControllerId = ((CommandObject) attacker).getControllerId();
sourceAbilities = attacker.getAbilities();
} else {
attacker = null;
}
} else {
sourceAbilities = ((Permanent) attacker).getAbilities(game);
sourceControllerId = ((Permanent) attacker).getControllerId();
}
if (attacker != null && sourceAbilities != null) {
if (sourceAbilities.containsKey(LifelinkAbility.getInstance().getId())) {
if (markDamage) {
game.getPermanent(attackerId).markLifelink(damageDone);
} else {
Player player = game.getPlayer(sourceControllerId);
player.gainLife(damageDone, game, source);
}
}
if (sourceAbilities.containsKey(DeathtouchAbility.getInstance().getId())) {
deathtouched = true;
}
if (dealtDamageByThisTurn == null) {
dealtDamageByThisTurn = new HashSet<>();
}
// Unstable ability - Earl of Squirrel
if (sourceAbilities.containsKey(SquirrellinkAbility.getInstance().getId())) {
Player player = game.getPlayer(sourceControllerId);
new SquirrelToken().putOntoBattlefield(damageDone, game, source, player.getId());
}
dealtDamageByThisTurn.add(new MageObjectReference(attacker, game));
}
if (attacker == null) {
game.informPlayers(getLogName() + " gets " + damageDone + " damage");
} else {
game.informPlayers(attacker.getLogName() + " deals " + damageDone + " damage to " + getLogName());
}
return damageDone;
}
private static boolean checkWither(DamageEvent event, MageObject attacker, Game game) {
if (event.isAsThoughWither() || event.isAsThoughInfect()) {
return true;
}
if (attacker == null) {
return false;
}
Abilities abilities;
if (attacker instanceof Card) {
abilities = ((Card) attacker).getAbilities(game);
} else {
abilities = attacker.getAbilities();
}
return abilities.containsKey(InfectAbility.getInstance().getId())
|| abilities.containsKey(WitherAbility.getInstance().getId());
}
@Override
public void markLifelink(int damage) {
markedLifelink += damage;
@ -960,81 +1018,43 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
} else if (mdi.sourceObject instanceof Permanent) {
source = ((Permanent) mdi.sourceObject).getSpellAbility();
}
addCounters(mdi.counter, game.getControllerId(mdi.sourceObject.getId()), source, game);
if (mdi.addCounters) {
addCounters(mdi.counter, game.getControllerId(mdi.sourceObject.getId()), source, game);
} else {
removeCounters(mdi.counter, source, game);
}
}
markedDamage.clear();
return 0;
}
@Override
public int getLethalDamage(UUID attackerId, Game game) {
int lethal = Integer.MAX_VALUE;
if (this.isCreature()) {
if (game.getState().getActivePowerInsteadOfToughnessForDamageLethalityFilters().stream().anyMatch(f -> f.match(this, game))) {
lethal = Math.min(lethal, power.getValue());
} else {
lethal = Math.min(lethal, toughness.getValue());
}
lethal = Math.max(lethal - this.damage, 0);
Card attacker = game.getCard(attackerId);
if (attacker != null && attacker.getAbilities(game).containsKey(DeathtouchAbility.getInstance().getId())) {
lethal = Math.min(1, lethal);
}
}
if (this.isPlaneswalker()) {
lethal = Math.min(lethal, this.getCounters(game).getCount(CounterType.LOYALTY));
}
return lethal;
}
@Override
public void removeAllDamage(Game game) {
damage = 0;
deathtouched = false;
}
protected int damagePlaneswalker(int damage, UUID attackerId, Ability source, Game game, boolean preventable, boolean combat, boolean markDamage, List<UUID> appliedEffects) {
GameEvent event = new DamagePlaneswalkerEvent(objectId, attackerId, controllerId, damage, preventable, combat);
event.setAppliedEffects(appliedEffects);
if (game.replaceEvent(event)) {
return 0;
}
int actualDamage = checkProtectionAbilities(event, attackerId, source, game);
if (actualDamage <= 0) {
return 0;
}
int countersToRemove = Math.min(actualDamage, getCounters(game).getCount(CounterType.LOYALTY));
removeCounters(CounterType.LOYALTY.getName(), countersToRemove, source, game);
DamagedEvent damagedEvent = new DamagedPlaneswalkerEvent(objectId, attackerId, controllerId, actualDamage, combat);
damagedEvent.setExcess(actualDamage - countersToRemove);
game.fireEvent(damagedEvent);
game.getState().addSimultaneousDamage(damagedEvent, game);
return actualDamage;
}
protected int damageCreature(int damage, UUID attackerId, Ability source, Game game, boolean preventable, boolean combat, boolean markDamage, List<UUID> appliedEffects) {
GameEvent event = new DamageCreatureEvent(this.getId(), attackerId, this.getControllerId(), damage, preventable, combat);
event.setAppliedEffects(appliedEffects);
if (game.replaceEvent(event)) {
return 0;
}
int actualDamage = checkProtectionAbilities(event, attackerId, source, game);
if (actualDamage <= 0) {
return 0;
}
MageObject attacker = game.getObject(attackerId);
int lethal = 0;
if (game.getState().getActivePowerInsteadOfToughnessForDamageLethalityFilters().stream().anyMatch(f -> f.match(this, game))) {
lethal = power.getValue();
} else {
lethal = toughness.getValue();
}
lethal = Math.max(lethal - this.damage, 0);
if (attacker.getAbilities().containsKey(DeathtouchAbility.getInstance().getId())) {
lethal = Math.min(1, lethal);
}
if (attacker != null && (attacker.getAbilities().containsKey(InfectAbility.getInstance().getId())
|| attacker.getAbilities().containsKey(WitherAbility.getInstance().getId()))) {
if (markDamage) {
// mark damage only
markDamage(CounterType.M1M1.createInstance(actualDamage), attacker);
} else {
Ability damageSourceAbility = null;
if (attacker instanceof Permanent) {
damageSourceAbility = ((Permanent) attacker).getSpellAbility();
}
// deal damage immediately
addCounters(CounterType.M1M1.createInstance(actualDamage), game.getControllerId(attackerId), damageSourceAbility, game);
}
} else {
this.damage = CardUtil.overflowInc(this.damage, actualDamage);
}
DamagedEvent damagedEvent = new DamagedCreatureEvent(this.getId(), attackerId, this.getControllerId(), actualDamage, combat);
damagedEvent.setExcess(actualDamage - lethal);
game.fireEvent(damagedEvent);
game.getState().addSimultaneousDamage(damagedEvent, game);
return actualDamage;
}
private int checkProtectionAbilities(GameEvent event, UUID attackerId, Ability source, Game game) {
MageObject attacker = game.getObject(attackerId);
if (attacker != null && hasProtectionFrom(attacker, game)) {
@ -1049,11 +1069,11 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
return event.getAmount();
}
private void markDamage(Counter counter, MageObject source) {
private void markDamage(Counter counter, MageObject source, boolean addCounters) {
if (markedDamage == null) {
markedDamage = new ArrayList<>();
}
markedDamage.add(new MarkedDamageInfo(counter, source));
markedDamage.add(new MarkedDamageInfo(counter, source, addCounters));
}
@Override

View file

@ -2075,75 +2075,76 @@ public abstract class PlayerImpl implements Player, Serializable {
return 0;
}
if (damage > 0) {
if (canDamage(game.getObject(attackerId), game)) {
GameEvent event = new DamagePlayerEvent(playerId,
attackerId, playerId, damage, preventable, combatDamage);
event.setAppliedEffects(appliedEffects);
if (!game.replaceEvent(event)) {
int actualDamage = event.getAmount();
if (actualDamage > 0) {
UUID sourceControllerId = null;
Abilities sourceAbilities = null;
MageObject attacker = game.getPermanentOrLKIBattlefield(attackerId);
if (attacker == null) {
StackObject stackObject = game.getStack().getStackObject(attackerId);
if (stackObject != null) {
attacker = stackObject.getStackAbility().getSourceObject(game);
} else {
attacker = game.getObject(attackerId);
}
if (attacker instanceof Spell) {
sourceAbilities = ((Spell) attacker).getAbilities(game);
sourceControllerId = ((Spell) attacker).getControllerId();
} else if (attacker instanceof Card) {
sourceAbilities = ((Card) attacker).getAbilities(game);
sourceControllerId = ((Card) attacker).getOwnerId();
} else if (attacker instanceof CommandObject) {
sourceControllerId = ((CommandObject) attacker).getControllerId();
sourceAbilities = attacker.getAbilities();
}
} else {
sourceAbilities = ((Permanent) attacker).getAbilities(game);
sourceControllerId = ((Permanent) attacker).getControllerId();
}
if (sourceAbilities != null && sourceAbilities.containsKey(InfectAbility.getInstance().getId())) {
addCounters(CounterType.POISON.createInstance(actualDamage), sourceControllerId, source, game);
} else {
GameEvent damageToLifeLossEvent = new GameEvent(GameEvent.EventType.DAMAGE_CAUSES_LIFE_LOSS,
playerId, source, playerId, actualDamage, combatDamage);
if (!game.replaceEvent(damageToLifeLossEvent)) {
this.loseLife(damageToLifeLossEvent.getAmount(), game, source, combatDamage, attackerId);
}
}
if (sourceAbilities != null && sourceAbilities.containsKey(LifelinkAbility.getInstance().getId())) {
if (combatDamage) {
game.getPermanent(attackerId).markLifelink(actualDamage);
} else {
Player player = game.getPlayer(sourceControllerId);
player.gainLife(actualDamage, game, source);
}
}
// Unstable ability - Earl of Squirrel
if (sourceAbilities != null && sourceAbilities.containsKey(SquirrellinkAbility.getInstance().getId())) {
Player player = game.getPlayer(sourceControllerId);
new SquirrelToken().putOntoBattlefield(actualDamage, game, source, player.getId());
}
DamagedEvent damagedEvent = new DamagedPlayerEvent(playerId, attackerId, playerId, actualDamage, combatDamage);
game.fireEvent(damagedEvent);
game.getState().addSimultaneousDamage(damagedEvent, game);
return actualDamage;
}
}
if (damage < 1) {
return 0;
}
if (!canDamage(game.getObject(attackerId), game)) {
MageObject sourceObject = game.getObject(attackerId);
game.informPlayers(damage + " damage "
+ (sourceObject == null ? "" : "from " + sourceObject.getLogName())
+ " to " + getLogName()
+ (damage > 1 ? " were" : "was") + " prevented because of protection");
return 0;
}
DamageEvent event = new DamagePlayerEvent(playerId, attackerId, playerId, damage, preventable, combatDamage);
event.setAppliedEffects(appliedEffects);
if (game.replaceEvent(event)) {
return 0;
}
int actualDamage = event.getAmount();
if (actualDamage < 1) {
return 0;
}
UUID sourceControllerId = null;
Abilities sourceAbilities = null;
MageObject attacker = game.getPermanentOrLKIBattlefield(attackerId);
if (attacker == null) {
StackObject stackObject = game.getStack().getStackObject(attackerId);
if (stackObject != null) {
attacker = stackObject.getStackAbility().getSourceObject(game);
} else {
MageObject sourceObject = game.getObject(attackerId);
game.informPlayers(damage + " damage "
+ (sourceObject == null ? "" : "from " + sourceObject.getLogName())
+ " to " + getLogName()
+ (damage > 1 ? " were" : "was") + " prevented because of protection");
attacker = game.getObject(attackerId);
}
if (attacker instanceof Spell) {
sourceAbilities = ((Spell) attacker).getAbilities(game);
sourceControllerId = ((Spell) attacker).getControllerId();
} else if (attacker instanceof Card) {
sourceAbilities = ((Card) attacker).getAbilities(game);
sourceControllerId = ((Card) attacker).getOwnerId();
} else if (attacker instanceof CommandObject) {
sourceControllerId = ((CommandObject) attacker).getControllerId();
sourceAbilities = attacker.getAbilities();
}
} else {
sourceAbilities = ((Permanent) attacker).getAbilities(game);
sourceControllerId = ((Permanent) attacker).getControllerId();
}
if (event.isAsThoughInfect() || (sourceAbilities != null && sourceAbilities.containsKey(InfectAbility.getInstance().getId()))) {
addCounters(CounterType.POISON.createInstance(actualDamage), sourceControllerId, source, game);
} else {
GameEvent damageToLifeLossEvent = new GameEvent(GameEvent.EventType.DAMAGE_CAUSES_LIFE_LOSS,
playerId, source, playerId, actualDamage, combatDamage);
if (!game.replaceEvent(damageToLifeLossEvent)) {
this.loseLife(damageToLifeLossEvent.getAmount(), game, source, combatDamage, attackerId);
}
}
return 0;
if (sourceAbilities != null && sourceAbilities.containsKey(LifelinkAbility.getInstance().getId())) {
if (combatDamage) {
game.getPermanent(attackerId).markLifelink(actualDamage);
} else {
Player player = game.getPlayer(sourceControllerId);
player.gainLife(actualDamage, game, source);
}
}
// Unstable ability - Earl of Squirrel
if (sourceAbilities != null && sourceAbilities.containsKey(SquirrellinkAbility.getInstance().getId())) {
Player player = game.getPlayer(sourceControllerId);
new SquirrelToken().putOntoBattlefield(actualDamage, game, source, player.getId());
}
DamagedEvent damagedEvent = new DamagedPlayerEvent(playerId, attackerId, playerId, actualDamage, combatDamage);
game.fireEvent(damagedEvent);
game.getState().addSimultaneousDamage(damagedEvent, game);
return actualDamage;
}
@Override

View file

@ -38,8 +38,7 @@ public class DamageDoneWatcher extends Watcher {
@Override
public void watch(GameEvent event, Game game) {
switch (event.getType()) {
case DAMAGED_CREATURE:
case DAMAGED_PLANESWALKER:
case DAMAGED_PERMANENT:
case DAMAGED_PLAYER: {
MageObjectReference damageSourceRef = new MageObjectReference(event.getSourceId(), game);
damagingObjects.putIfAbsent(damageSourceRef, 0);

View file

@ -1,25 +1,24 @@
package mage.watchers.common;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import mage.MageObject;
import mage.MageObjectReference;
import mage.constants.WatcherScope;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import mage.game.permanent.Permanent;
import mage.watchers.Watcher;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
/**
* @author BetaSteward_at_googlemail.com
*/
public class DamagedByWatcher extends Watcher {
public final Set<MageObjectReference> damagedBySource = new HashSet<>();
private final Set<MageObjectReference> damagedBySource = new HashSet<>();
private final boolean watchPlaneswalkers;
@ -30,12 +29,15 @@ public class DamagedByWatcher extends Watcher {
@Override
public void watch(GameEvent event, Game game) {
boolean eventHasAppropriateType = (event.getType() == GameEvent.EventType.DAMAGED_CREATURE) ||
(watchPlaneswalkers && event.getType() == GameEvent.EventType.DAMAGED_PLANESWALKER);
if (eventHasAppropriateType && sourceId.equals(event.getSourceId())) {
MageObjectReference mor = new MageObjectReference(event.getTargetId(), game);
damagedBySource.add(mor);
if (event.getType() != GameEvent.EventType.DAMAGED_PERMANENT) {
return;
}
Permanent permanent = game.getPermanent(event.getTargetId());
if (permanent != null && !watchPlaneswalkers && !permanent.isCreature()) {
return;
}
if (sourceId.equals(event.getSourceId())) {
damagedBySource.add(new MageObjectReference(event.getTargetId(), game));
}
}

View file

@ -24,11 +24,9 @@ public class SourceDidDamageWatcher extends Watcher {
@Override
public void watch(GameEvent event, Game game) {
if (event.getType() == GameEvent.EventType.DAMAGED_CREATURE
|| event.getType() == GameEvent.EventType.DAMAGED_PLANESWALKER
if (event.getType() == GameEvent.EventType.DAMAGED_PERMANENT
|| event.getType() == GameEvent.EventType.DAMAGED_PLAYER) {
damageSources.add(event.getSourceId());
}
}