mirror of
https://github.com/correl/mage.git
synced 2024-12-26 03:00:11 +00:00
* 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:
parent
19714312cd
commit
a7f9ba65fe
5 changed files with 85 additions and 43 deletions
|
@ -36,9 +36,13 @@ import mage.constants.Zone;
|
||||||
import mage.MageInt;
|
import mage.MageInt;
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.common.SimpleStaticAbility;
|
import mage.abilities.common.SimpleStaticAbility;
|
||||||
|
import mage.abilities.effects.ContinuousEffectImpl;
|
||||||
import mage.abilities.effects.ReplacementEffectImpl;
|
import mage.abilities.effects.ReplacementEffectImpl;
|
||||||
import mage.cards.CardImpl;
|
import mage.cards.CardImpl;
|
||||||
|
import mage.constants.AttachmentType;
|
||||||
import mage.constants.Duration;
|
import mage.constants.Duration;
|
||||||
|
import mage.constants.Layer;
|
||||||
|
import mage.constants.SubLayer;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.events.DamageCreatureEvent;
|
import mage.game.events.DamageCreatureEvent;
|
||||||
import mage.game.events.DamagePlaneswalkerEvent;
|
import mage.game.events.DamagePlaneswalkerEvent;
|
||||||
|
@ -47,6 +51,15 @@ import mage.game.events.GameEvent;
|
||||||
import mage.game.permanent.Permanent;
|
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
|
* @author LevelX2
|
||||||
*/
|
*/
|
||||||
|
@ -66,7 +79,7 @@ public class DoranTheSiegeTower extends CardImpl<DoranTheSiegeTower> {
|
||||||
this.toughness = new MageInt(5);
|
this.toughness = new MageInt(5);
|
||||||
|
|
||||||
// Each creature assigns combat damage equal to its toughness rather than its power.
|
// 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) {
|
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() {
|
public DoranTheSiegeTowerCombatDamageRuleEffect() {
|
||||||
super(Duration.WhileOnBattlefield, Outcome.Damage);
|
super(Duration.WhileOnBattlefield, Outcome.Detriment);
|
||||||
staticText = "Each creature assigns combat damage equal to its toughness rather than its power";
|
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);
|
super(effect);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DoranTheSiegeTowerEffect copy() {
|
public DoranTheSiegeTowerCombatDamageRuleEffect copy() {
|
||||||
return new DoranTheSiegeTowerEffect(this);
|
return new DoranTheSiegeTowerCombatDamageRuleEffect(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) {
|
||||||
switch (event.getType()) {
|
switch (layer) {
|
||||||
case DAMAGE_PLAYER:
|
case RulesEffects:
|
||||||
if (((DamagePlayerEvent) event).isCombatDamage()) {
|
// Change the rule
|
||||||
return true;
|
game.getCombat().setUseToughnessForDamage(true);
|
||||||
}
|
break;
|
||||||
break;
|
|
||||||
case DAMAGE_PLANESWALKER:
|
|
||||||
if (((DamagePlaneswalkerEvent) event).isCombatDamage()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case DAMAGE_CREATURE:
|
|
||||||
if (((DamageCreatureEvent) event).isCombatDamage()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(Game game, Ability source) {
|
public boolean apply(Game game, Ability source) {
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
|
public boolean hasLayer(Layer layer) {
|
||||||
Permanent permanent = game.getPermanent(event.getSourceId());
|
return layer == Layer.RulesEffects;
|
||||||
if (permanent != null) {
|
|
||||||
event.setAmount(permanent.getToughness().getValue());
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -384,6 +384,7 @@ public class GameState implements Serializable, Copyable<GameState> {
|
||||||
player.reset();
|
player.reset();
|
||||||
}
|
}
|
||||||
battlefield.reset(game);
|
battlefield.reset(game);
|
||||||
|
combat.reset();
|
||||||
resetOtherAbilities();
|
resetOtherAbilities();
|
||||||
effects.apply(game);
|
effects.apply(game);
|
||||||
battlefield.fireControlChangeEvents(game);
|
battlefield.fireControlChangeEvents(game);
|
||||||
|
@ -578,6 +579,16 @@ public class GameState implements Serializable, Copyable<GameState> {
|
||||||
values.put(valueId, value);
|
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) {
|
public Abilities<ActivatedAbility> getOtherAbilities(UUID objectId, Zone zone) {
|
||||||
if (otherAbilities.containsKey(objectId)) {
|
if (otherAbilities.containsKey(objectId)) {
|
||||||
return otherAbilities.get(objectId).getActivatedAbilities(zone);
|
return otherAbilities.get(objectId).getActivatedAbilities(zone);
|
||||||
|
|
|
@ -56,6 +56,8 @@ public class Combat implements Serializable, Copyable<Combat> {
|
||||||
|
|
||||||
private static FilterPlaneswalkerPermanent filterPlaneswalker = new FilterPlaneswalkerPermanent();
|
private static FilterPlaneswalkerPermanent filterPlaneswalker = new FilterPlaneswalkerPermanent();
|
||||||
private static FilterCreatureForCombatBlock filterBlockers = new FilterCreatureForCombatBlock();
|
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 List<CombatGroup> groups = new ArrayList<CombatGroup>();
|
||||||
protected Map<UUID, CombatGroup> blockingGroups = new HashMap<UUID, 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>>
|
// <creature that can block, <all attackers that force the creature to block it>>
|
||||||
protected Map<UUID, Set<UUID>> creaturesForcedToBlockAttackers = new HashMap<UUID, Set<UUID>>();
|
protected Map<UUID, Set<UUID>> creaturesForcedToBlockAttackers = new HashMap<UUID, Set<UUID>>();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public Combat() {
|
public Combat() {
|
||||||
|
this.useToughnessForDamage = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Combat(final Combat combat) {
|
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()) {
|
for (Map.Entry<UUID, CombatGroup> group : combat.blockingGroups.entrySet()) {
|
||||||
blockingGroups.put(group.getKey(), group.getValue());
|
blockingGroups.put(group.getKey(), group.getValue());
|
||||||
}
|
}
|
||||||
|
this.useToughnessForDamage = combat.useToughnessForDamage;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<CombatGroup> getGroups() {
|
public List<CombatGroup> getGroups() {
|
||||||
|
@ -106,6 +112,18 @@ public class Combat implements Serializable, Copyable<Combat> {
|
||||||
return blockers;
|
return blockers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean useToughnessForDamage() {
|
||||||
|
return useToughnessForDamage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUseToughnessForDamage(boolean useToughnessForDamage) {
|
||||||
|
this.useToughnessForDamage = useToughnessForDamage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
this.useToughnessForDamage = false;
|
||||||
|
}
|
||||||
|
|
||||||
public void clear() {
|
public void clear() {
|
||||||
groups.clear();
|
groups.clear();
|
||||||
blockingGroups.clear();
|
blockingGroups.clear();
|
||||||
|
|
|
@ -209,20 +209,22 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
|
||||||
if (canDamage(attacker, first)) {
|
if (canDamage(attacker, first)) {
|
||||||
//20091005 - 510.1c, 702.17c
|
//20091005 - 510.1c, 702.17c
|
||||||
if (!blocked || hasTrample(attacker)) {
|
if (!blocked || hasTrample(attacker)) {
|
||||||
defenderDamage(attacker, attacker.getPower().getValue(), game);
|
defenderDamage(attacker, getDamageValueFromPermanent(attacker, game), game);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private void singleBlockerDamage(boolean first, Game game) {
|
private void singleBlockerDamage(boolean first, Game game) {
|
||||||
//TODO: handle banding
|
//TODO: handle banding
|
||||||
Permanent blocker = game.getPermanent(blockers.get(0));
|
Permanent blocker = game.getPermanent(blockers.get(0));
|
||||||
Permanent attacker = game.getPermanent(attackers.get(0));
|
Permanent attacker = game.getPermanent(attackers.get(0));
|
||||||
if (blocker != null && attacker != null) {
|
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)) {
|
if (blocked && canDamage(attacker, first)) {
|
||||||
int damage = attacker.getPower().getValue();
|
int damage = getDamageValueFromPermanent(attacker, game);
|
||||||
if (hasTrample(attacker)) {
|
if (hasTrample(attacker)) {
|
||||||
int lethalDamage;
|
int lethalDamage;
|
||||||
if (attacker.getAbilities().containsKey(DeathtouchAbility.getInstance().getId())) {
|
if (attacker.getAbilities().containsKey(DeathtouchAbility.getInstance().getId())) {
|
||||||
|
@ -262,7 +264,7 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Player player = game.getPlayer(attacker.getControllerId());
|
Player player = game.getPlayer(attacker.getControllerId());
|
||||||
int damage = attacker.getPower().getValue();
|
int damage = getDamageValueFromPermanent(attacker, game);
|
||||||
if (canDamage(attacker, first)) {
|
if (canDamage(attacker, first)) {
|
||||||
// must be set before attacker damage marking because of effects like Test of Faith
|
// must be set before attacker damage marking because of effects like Test of Faith
|
||||||
Map<UUID, Integer> blockerPower = new HashMap<UUID, Integer>();
|
Map<UUID, Integer> blockerPower = new HashMap<UUID, Integer>();
|
||||||
|
@ -270,7 +272,7 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
|
||||||
Permanent blocker = game.getPermanent(blockerId);
|
Permanent blocker = game.getPermanent(blockerId);
|
||||||
if (canDamage(blocker, first)) {
|
if (canDamage(blocker, first)) {
|
||||||
if (blocker.getBlocking() == 1) { // blocking several creatures handled separately
|
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) {
|
for (UUID blockerId: blockerOrder) {
|
||||||
Permanent blocker = game.getPermanent(blockerId);
|
Permanent blocker = game.getPermanent(blockerId);
|
||||||
if (canDamage(blocker, first)) {
|
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));
|
Permanent attacker = game.getPermanent(attackers.get(0));
|
||||||
if (blocker != null && attacker != null) {
|
if (blocker != null && attacker != null) {
|
||||||
if (canDamage(blocker, first)) {
|
if (canDamage(blocker, first)) {
|
||||||
int damage = blocker.getPower().getValue();
|
int damage = getDamageValueFromPermanent(blocker, game);
|
||||||
attacker.markDamage(damage, blocker.getId(), game, true, true);
|
attacker.markDamage(damage, blocker.getId(), game, true, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -353,7 +355,7 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Player player = game.getPlayer(blocker.getControllerId());
|
Player player = game.getPlayer(blocker.getControllerId());
|
||||||
int damage = blocker.getPower().getValue();
|
int damage = getDamageValueFromPermanent(blocker, game);
|
||||||
|
|
||||||
if (canDamage(blocker, first)) {
|
if (canDamage(blocker, first)) {
|
||||||
Map<UUID, Integer> assigned = new HashMap<UUID, Integer>();
|
Map<UUID, Integer> assigned = new HashMap<UUID, Integer>();
|
||||||
|
@ -474,7 +476,7 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
|
||||||
public int totalAttackerDamage(Game game) {
|
public int totalAttackerDamage(Game game) {
|
||||||
int total = 0;
|
int total = 0;
|
||||||
for (UUID attackerId: attackers) {
|
for (UUID attackerId: attackers) {
|
||||||
total += game.getPermanent(attackerId).getPower().getValue();
|
total += getDamageValueFromPermanent(game.getPermanent(attackerId), game);
|
||||||
}
|
}
|
||||||
return total;
|
return total;
|
||||||
}
|
}
|
||||||
|
@ -574,6 +576,21 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
|
||||||
}
|
}
|
||||||
return blockWasLegal;
|
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
|
@Override
|
||||||
public CombatGroup copy() {
|
public CombatGroup copy() {
|
||||||
|
|
|
@ -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) {
|
private int damage(int damageAmount, UUID sourceId, Game game, boolean preventable, boolean combat, boolean markDamage, ArrayList<UUID> appliedEffects) {
|
||||||
int damageDone = 0;
|
int damageDone = 0;
|
||||||
// because of "Doran, the Siege Tower" we can't check here for 0 damage.
|
if (damageAmount > 0 && canDamage(game.getObject(sourceId), game)) {
|
||||||
if (canDamage(game.getObject(sourceId), game)) {
|
|
||||||
if (cardType.contains(CardType.PLANESWALKER)) {
|
if (cardType.contains(CardType.PLANESWALKER)) {
|
||||||
damageDone = damagePlaneswalker(damageAmount, sourceId, game, preventable, combat, markDamage, appliedEffects);
|
damageDone = damagePlaneswalker(damageAmount, sourceId, game, preventable, combat, markDamage, appliedEffects);
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Reference in a new issue