forked from External/mage
Combat trace (to catch annoying bug with non-flying blockers)
This commit is contained in:
parent
36cb5f95b9
commit
ad7391b4b5
3 changed files with 133 additions and 1 deletions
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
123
Mage/src/mage/util/trace/TraceUtil.java
Normal file
123
Mage/src/mage/util/trace/TraceUtil.java
Normal 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);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue