* Doran, the Siege Tower - Fixed handling of use toughness instead of power for damage effect. Prevention effects work now always for the correct amount of damage.

This commit is contained in:
LevelX2 2013-10-01 14:41:19 +02:00
parent 19714312cd
commit a7f9ba65fe
5 changed files with 85 additions and 43 deletions

View file

@ -36,9 +36,13 @@ import mage.constants.Zone;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.ContinuousEffectImpl;
import mage.abilities.effects.ReplacementEffectImpl;
import mage.cards.CardImpl;
import mage.constants.AttachmentType;
import mage.constants.Duration;
import mage.constants.Layer;
import mage.constants.SubLayer;
import mage.game.Game;
import mage.game.events.DamageCreatureEvent;
import mage.game.events.DamagePlaneswalkerEvent;
@ -47,6 +51,15 @@ import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
/**
*
* 613.10. Some continuous effects affect game rules rather than objects. For example,
* effects may modify a player's maximum hand size, or say that a creature must attack
* this turn if able. These effects are applied after all other continuous effects have
* been applied. Continuous effects that affect the costs of spells or abilities are
* applied according to the order specified in rule 601.2e. All other such effects are
* applied in timestamp order. See also the rules for timestamp order and dependency
* (rules 613.6 and 613.7)
*
*
* @author LevelX2
*/
@ -66,7 +79,7 @@ public class DoranTheSiegeTower extends CardImpl<DoranTheSiegeTower> {
this.toughness = new MageInt(5);
// Each creature assigns combat damage equal to its toughness rather than its power.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DoranTheSiegeTowerEffect()));
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DoranTheSiegeTowerCombatDamageRuleEffect()));
}
public DoranTheSiegeTower(final DoranTheSiegeTower card) {
@ -79,56 +92,40 @@ public class DoranTheSiegeTower extends CardImpl<DoranTheSiegeTower> {
}
}
class DoranTheSiegeTowerEffect extends ReplacementEffectImpl<DoranTheSiegeTowerEffect> {
class DoranTheSiegeTowerCombatDamageRuleEffect extends ContinuousEffectImpl<DoranTheSiegeTowerCombatDamageRuleEffect> {
public DoranTheSiegeTowerEffect() {
super(Duration.WhileOnBattlefield, Outcome.Damage);
public DoranTheSiegeTowerCombatDamageRuleEffect() {
super(Duration.WhileOnBattlefield, Outcome.Detriment);
staticText = "Each creature assigns combat damage equal to its toughness rather than its power";
}
public DoranTheSiegeTowerEffect(final DoranTheSiegeTowerEffect effect) {
public DoranTheSiegeTowerCombatDamageRuleEffect(final DoranTheSiegeTowerCombatDamageRuleEffect effect) {
super(effect);
}
@Override
public DoranTheSiegeTowerEffect copy() {
return new DoranTheSiegeTowerEffect(this);
public DoranTheSiegeTowerCombatDamageRuleEffect copy() {
return new DoranTheSiegeTowerCombatDamageRuleEffect(this);
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
switch (event.getType()) {
case DAMAGE_PLAYER:
if (((DamagePlayerEvent) event).isCombatDamage()) {
return true;
}
break;
case DAMAGE_PLANESWALKER:
if (((DamagePlaneswalkerEvent) event).isCombatDamage()) {
return true;
}
break;
case DAMAGE_CREATURE:
if (((DamageCreatureEvent) event).isCombatDamage()) {
return true;
}
break;
public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) {
switch (layer) {
case RulesEffects:
// Change the rule
game.getCombat().setUseToughnessForDamage(true);
break;
}
return false;
}
@Override
public boolean apply(Game game, Ability source) {
return true;
return false;
}
@Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
Permanent permanent = game.getPermanent(event.getSourceId());
if (permanent != null) {
event.setAmount(permanent.getToughness().getValue());
}
return false;
public boolean hasLayer(Layer layer) {
return layer == Layer.RulesEffects;
}
}

View file

@ -384,6 +384,7 @@ public class GameState implements Serializable, Copyable<GameState> {
player.reset();
}
battlefield.reset(game);
combat.reset();
resetOtherAbilities();
effects.apply(game);
battlefield.fireControlChangeEvents(game);
@ -578,6 +579,16 @@ public class GameState implements Serializable, Copyable<GameState> {
values.put(valueId, value);
}
/**
* Other abilities are used to implement some special kind of continious effects.
*
* Crucible of Worlds - You may play land cards from your graveyard.
* Past in Flames - Each instant and sorcery card in your graveyard gains flashback until end of turn. The flashback cost is equal to its mana cost.
*
* @param objectId
* @param zone
* @return
*/
public Abilities<ActivatedAbility> getOtherAbilities(UUID objectId, Zone zone) {
if (otherAbilities.containsKey(objectId)) {
return otherAbilities.get(objectId).getActivatedAbilities(zone);

View file

@ -56,6 +56,8 @@ public class Combat implements Serializable, Copyable<Combat> {
private static FilterPlaneswalkerPermanent filterPlaneswalker = new FilterPlaneswalkerPermanent();
private static FilterCreatureForCombatBlock filterBlockers = new FilterCreatureForCombatBlock();
// There are effects that let creatures assigns combat damage equal to its toughness rather than its power
private boolean useToughnessForDamage;
protected List<CombatGroup> groups = new ArrayList<CombatGroup>();
protected Map<UUID, CombatGroup> blockingGroups = new HashMap<UUID, CombatGroup>();
@ -64,7 +66,10 @@ public class Combat implements Serializable, Copyable<Combat> {
// <creature that can block, <all attackers that force the creature to block it>>
protected Map<UUID, Set<UUID>> creaturesForcedToBlockAttackers = new HashMap<UUID, Set<UUID>>();
public Combat() {
this.useToughnessForDamage = false;
}
public Combat(final Combat combat) {
@ -76,6 +81,7 @@ public class Combat implements Serializable, Copyable<Combat> {
for (Map.Entry<UUID, CombatGroup> group : combat.blockingGroups.entrySet()) {
blockingGroups.put(group.getKey(), group.getValue());
}
this.useToughnessForDamage = combat.useToughnessForDamage;
}
public List<CombatGroup> getGroups() {
@ -106,6 +112,18 @@ public class Combat implements Serializable, Copyable<Combat> {
return blockers;
}
public boolean useToughnessForDamage() {
return useToughnessForDamage;
}
public void setUseToughnessForDamage(boolean useToughnessForDamage) {
this.useToughnessForDamage = useToughnessForDamage;
}
public void reset() {
this.useToughnessForDamage = false;
}
public void clear() {
groups.clear();
blockingGroups.clear();

View file

@ -209,20 +209,22 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
if (canDamage(attacker, first)) {
//20091005 - 510.1c, 702.17c
if (!blocked || hasTrample(attacker)) {
defenderDamage(attacker, attacker.getPower().getValue(), game);
defenderDamage(attacker, getDamageValueFromPermanent(attacker, game), game);
}
}
}
}
private void singleBlockerDamage(boolean first, Game game) {
//TODO: handle banding
Permanent blocker = game.getPermanent(blockers.get(0));
Permanent attacker = game.getPermanent(attackers.get(0));
if (blocker != null && attacker != null) {
int blockerDamage = blocker.getPower().getValue(); // must be set before attacker damage marking because of effects like Test of Faith
int blockerDamage = getDamageValueFromPermanent(blocker, game); // must be set before attacker damage marking because of effects like Test of Faith
if (blocked && canDamage(attacker, first)) {
int damage = attacker.getPower().getValue();
int damage = getDamageValueFromPermanent(attacker, game);
if (hasTrample(attacker)) {
int lethalDamage;
if (attacker.getAbilities().containsKey(DeathtouchAbility.getInstance().getId())) {
@ -262,7 +264,7 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
return;
}
Player player = game.getPlayer(attacker.getControllerId());
int damage = attacker.getPower().getValue();
int damage = getDamageValueFromPermanent(attacker, game);
if (canDamage(attacker, first)) {
// must be set before attacker damage marking because of effects like Test of Faith
Map<UUID, Integer> blockerPower = new HashMap<UUID, Integer>();
@ -270,7 +272,7 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
Permanent blocker = game.getPermanent(blockerId);
if (canDamage(blocker, first)) {
if (blocker.getBlocking() == 1) { // blocking several creatures handled separately
blockerPower.put(blockerId, blocker.getPower().getValue());
blockerPower.put(blockerId, getDamageValueFromPermanent(blocker, game));
}
}
}
@ -312,7 +314,7 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
for (UUID blockerId: blockerOrder) {
Permanent blocker = game.getPermanent(blockerId);
if (canDamage(blocker, first)) {
attacker.markDamage(blocker.getPower().getValue(), blocker.getId(), game, true, true);
attacker.markDamage(getDamageValueFromPermanent(blocker, game), blocker.getId(), game, true, true);
}
}
}
@ -332,7 +334,7 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
Permanent attacker = game.getPermanent(attackers.get(0));
if (blocker != null && attacker != null) {
if (canDamage(blocker, first)) {
int damage = blocker.getPower().getValue();
int damage = getDamageValueFromPermanent(blocker, game);
attacker.markDamage(damage, blocker.getId(), game, true, true);
}
}
@ -353,7 +355,7 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
return;
}
Player player = game.getPlayer(blocker.getControllerId());
int damage = blocker.getPower().getValue();
int damage = getDamageValueFromPermanent(blocker, game);
if (canDamage(blocker, first)) {
Map<UUID, Integer> assigned = new HashMap<UUID, Integer>();
@ -474,7 +476,7 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
public int totalAttackerDamage(Game game) {
int total = 0;
for (UUID attackerId: attackers) {
total += game.getPermanent(attackerId).getPower().getValue();
total += getDamageValueFromPermanent(game.getPermanent(attackerId), game);
}
return total;
}
@ -574,6 +576,21 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
}
return blockWasLegal;
}
/**
* There are effects that let creatures assigns combat damage equal to its toughness rather than its power.
* So this method takes this into account to get the value of damage a creature will assign
*
* @param permanent
* @param game
* @return
*/
private int getDamageValueFromPermanent(Permanent permanent, Game game) {
if (game.getCombat().useToughnessForDamage()) {
return permanent.getToughness().getValue();
} else {
return permanent.getPower().getValue();
}
}
@Override
public CombatGroup copy() {

View file

@ -619,8 +619,7 @@ public abstract class PermanentImpl<T extends PermanentImpl<T>> extends CardImpl
*/
private int damage(int damageAmount, UUID sourceId, Game game, boolean preventable, boolean combat, boolean markDamage, ArrayList<UUID> appliedEffects) {
int damageDone = 0;
// because of "Doran, the Siege Tower" we can't check here for 0 damage.
if (canDamage(game.getObject(sourceId), game)) {
if (damageAmount > 0 && canDamage(game.getObject(sourceId), game)) {
if (cardType.contains(CardType.PLANESWALKER)) {
damageDone = damagePlaneswalker(damageAmount, sourceId, game, preventable, combat, markDamage, appliedEffects);
} else {