Combat trace (to catch annoying bug with non-flying blockers)

This commit is contained in:
magenoxx 2012-08-08 13:21:31 +04:00
parent 36cb5f95b9
commit ad7391b4b5
3 changed files with 133 additions and 1 deletions

View file

@ -105,11 +105,14 @@ public class AttackBlockRestrictionsTest extends CardTestPlayerBase {
addCard(Constants.Zone.BATTLEFIELD, playerB, "Elite Vanguard");
addCard(Constants.Zone.BATTLEFIELD, playerB, "Arbor Elf");
addCard(Constants.Zone.BATTLEFIELD, playerB, "Assault Griffin");
addCard(Constants.Zone.BATTLEFIELD, playerB, "Sky Ruin Drake");
addCard(Constants.Zone.BATTLEFIELD, playerA, "Angelic Wall");
addCard(Constants.Zone.BATTLEFIELD, playerA, "Air Elemental");
addCard(Constants.Zone.BATTLEFIELD, playerA, "Llanowar Elves");
addCard(Constants.Zone.BATTLEFIELD, playerA, "Sentinel Spider");
// attacker vs. blocker:
// non flying vs. flying
attack(2, playerB, "Elite Vanguard");
block(2, playerA, "Angelic Wall", "Elite Vanguard");
@ -119,6 +122,9 @@ public class AttackBlockRestrictionsTest extends CardTestPlayerBase {
// flying vs. flying
attack(2, playerB, "Assault Griffin");
block(2, playerA, "Air Elemental", "Assault Griffin");
// flying vs. reach
attack(2, playerB, "Sky Ruin Drake");
block(2, playerA, "Sentinel Spider", "Sky Ruin Drake");
setStopAt(2, Constants.PhaseStep.END_TURN);
execute();
@ -145,6 +151,7 @@ public class AttackBlockRestrictionsTest extends CardTestPlayerBase {
addCard(Constants.Zone.BATTLEFIELD, playerA, "Forest", 2);
addCard(Constants.Zone.HAND, playerA, "Naturalize");
// attacker vs. blocker:
// non flying vs. flying
attack(2, playerB, "Elite Vanguard");
block(2, playerA, "Angelic Wall", "Elite Vanguard");
@ -167,7 +174,7 @@ public class AttackBlockRestrictionsTest extends CardTestPlayerBase {
}
/**
* Tests "Creatures with flying can't block creatures you control"
* Tests "Creatures with power less than Champion of Lambholt's power can't block creatures you control."
*/
@Test
public void testChampionOfLambholt() {

View file

@ -42,6 +42,7 @@ import mage.players.Player;
import mage.players.PlayerList;
import mage.target.common.TargetDefender;
import mage.util.Copyable;
import mage.util.trace.TraceUtil;
import java.io.Serializable;
import java.util.*;
@ -210,6 +211,7 @@ public class Combat implements Serializable, Copyable<Combat> {
}
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.DECLARED_BLOCKERS, defenderId, defenderId));
}
TraceUtil.traceCombatIfNeeded(game, this);
}
}

View file

@ -0,0 +1,123 @@
package mage.util.trace;
import mage.abilities.Ability;
import mage.abilities.effects.RestrictionEffect;
import mage.abilities.keyword.FlyingAbility;
import mage.abilities.keyword.ReachAbility;
import mage.abilities.keyword.UnblockableAbility;
import mage.game.Game;
import mage.game.combat.Combat;
import mage.game.combat.CombatGroup;
import mage.game.permanent.Permanent;
import org.apache.log4j.Logger;
import java.util.UUID;
/**
* @author magenoxx_at_gmail.com
*/
public class TraceUtil {
private static final Logger log = Logger.getLogger(TraceUtil.class);
/**
* This method is intended to catch various bugs with combat.
*
* One of them (possibly the most annoying) is when creature without flying or reach blocks creature with flying.
* No test managed to reproduce it, but it happens in the games time to time and was reported by different players.
*
* The idea: is to catch such cases manually and print out as much information from game state that may help as possible.
*/
public static void traceCombatIfNeeded(Game game, Combat combat) {
// trace non-flying vs flying
for (CombatGroup group : combat.getGroups()) {
for (UUID attackerId : group.getAttackers()) {
Permanent attacker = game.getPermanent(attackerId);
if (attacker != null) {
if (hasFlying(attacker)) {
for (UUID blockerId : group.getBlockers()) {
Permanent blocker = game.getPermanent(blockerId);
//if (blocker != null && !hasFlying(blocker) && !hasReach(blocker)) {
log.warn("Found non-flying non-reach creature blocking creature with flying");
traceCombat(game, attacker, blocker);
//}
}
}
if (hasUnblockable(attacker)) {
if (group.getBlockers().size() > 0) {
Permanent blocker = game.getPermanent(group.getBlockers().get(0));
if (blocker != null) {
log.warn("Found unblockable creature blocked by some other creature");
traceCombat(game, attacker, blocker);
}
}
}
}
}
}
}
/**
* We need this to check Flying existence in not-common way: by instanceof.
* @return
*/
private static boolean hasFlying(Permanent permanent) {
for (Ability ability : permanent.getAbilities()) {
if (ability instanceof FlyingAbility) {
return true;
}
}
return false;
}
private static boolean hasReach(Permanent permanent) {
for (Ability ability : permanent.getAbilities()) {
if (ability instanceof ReachAbility) {
return true;
}
}
return false;
}
private static boolean hasUnblockable(Permanent permanent) {
for (Ability ability : permanent.getAbilities()) {
if (ability instanceof UnblockableAbility) {
return true;
}
}
return false;
}
private static void traceCombat(Game game, Permanent attacker, Permanent blocker) {
String uuid = "[" + UUID.randomUUID() + "] ";
log.error(uuid+"Tracing game state...");
log.error(uuid+blocker.getName() + " could block " + attacker.getName());
log.error(uuid);
log.error(uuid+"Attacker abilities: ");
for (Ability ability : attacker.getAbilities()) {
log.error(uuid+" " + ability.toString() + ", id=" + ability.getId());
}
log.error(uuid+"Blocker abilities: ");
for (Ability ability : blocker.getAbilities()) {
log.error(uuid+" " + ability.toString() + ", id=" + ability.getId());
}
log.error(uuid);
log.error(uuid+"Flying ability id: " + FlyingAbility.getInstance().getId());
log.error(uuid+"Reach ability id: " + ReachAbility.getInstance().getId());
log.error(uuid+"Unblockable ability id: " + UnblockableAbility.getInstance().getId());
log.error(uuid);
log.error(uuid+"Restriction effects:");
Ability ability = attacker.getAbilities().size() > 0 ? attacker.getAbilities().get(0) : null;
for (RestrictionEffect effect : game.getState().getContinuousEffects().getRestrictionEffects()) {
log.error(uuid+" " + effect);
log.error(uuid+" id=" + effect.getId());
log.error(uuid+" applies to attacker=" + effect.applies(attacker, ability, game));
log.error(uuid+" applies to blocker=" + effect.applies(blocker, ability, game));
}
log.error(uuid);
}
}