mirror of
https://github.com/magefree/mage.git
synced 2025-12-22 03:22:00 -08:00
Fixed a bug about must be blocked requirement forcing the blocker to block multiple attackers locking the game.
This commit is contained in:
parent
bb99caeb29
commit
b2ed48da87
3 changed files with 103 additions and 45 deletions
|
|
@ -76,4 +76,40 @@ public class BlockRequirementTest extends CardTestPlayerBase {
|
||||||
assertPermanentCount(playerA, "Silvercoat Lion", 1);
|
assertPermanentCount(playerA, "Silvercoat Lion", 1);
|
||||||
assertPermanentCount(playerB, "Prized Unicorn", 1);
|
assertPermanentCount(playerB, "Prized Unicorn", 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Joraga Invocation is bugged big time. He cast it with 2 creatures out. I
|
||||||
|
* only had one untapped creature. Blocked one of his, hit Done, error
|
||||||
|
* message popped up saying the other one needed to be blocked in an
|
||||||
|
* infinite loop. Had to shut down the program via Task Manager.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testJoragaInvocationTest() {
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, "Forest", 6);
|
||||||
|
// Each creature you control gets +3/+3 until end of turn and must be blocked this turn if able.
|
||||||
|
addCard(Zone.HAND, playerB, "Joraga Invocation");
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion"); // 2/2
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, "Pillarfield Ox"); // 2/4
|
||||||
|
|
||||||
|
// Swampwalk
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Bog Wraith"); // 3/3
|
||||||
|
|
||||||
|
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Joraga Invocation");
|
||||||
|
|
||||||
|
// Silvercoat Lion has not to block because it has to pay {3} to block
|
||||||
|
attack(2, playerB, "Silvercoat Lion");
|
||||||
|
attack(2, playerB, "Pillarfield Ox");
|
||||||
|
block(2, playerA, "Bog Wraith", "Pillarfield Ox");
|
||||||
|
|
||||||
|
setStopAt(2, PhaseStep.POSTCOMBAT_MAIN);
|
||||||
|
execute();
|
||||||
|
|
||||||
|
assertLife(playerA, 15);
|
||||||
|
|
||||||
|
assertGraveyardCount(playerB, "Joraga Invocation", 1);
|
||||||
|
assertPowerToughness(playerB, "Silvercoat Lion", 5, 5);
|
||||||
|
assertPowerToughness(playerB, "Pillarfield Ox", 5, 7);
|
||||||
|
assertGraveyardCount(playerA, "Bog Wraith", 1);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ public enum CardRepository {
|
||||||
// raise this if db structure was changed
|
// raise this if db structure was changed
|
||||||
private static final long CARD_DB_VERSION = 39;
|
private static final long CARD_DB_VERSION = 39;
|
||||||
// raise this if new cards were added to the server
|
// raise this if new cards were added to the server
|
||||||
private static final long CARD_CONTENT_VERSION = 22;
|
private static final long CARD_CONTENT_VERSION = 23;
|
||||||
|
|
||||||
private final Random random = new Random();
|
private final Random random = new Random();
|
||||||
private Dao<CardInfo, Object> cardDao;
|
private Dao<CardInfo, Object> cardDao;
|
||||||
|
|
|
||||||
|
|
@ -665,7 +665,7 @@ public class Combat implements Serializable, Copyable<Combat> {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// check attacking creature mustBeBlockedByAtLeastOne
|
// check if for attacking creatures with mustBeBlockedByAtLeastOne requirements are fulfilled
|
||||||
for (UUID toBeBlockedCreatureId : mustBeBlockedByAtLeastOne.keySet()) {
|
for (UUID toBeBlockedCreatureId : mustBeBlockedByAtLeastOne.keySet()) {
|
||||||
for (CombatGroup combatGroup : game.getCombat().getGroups()) {
|
for (CombatGroup combatGroup : game.getCombat().getGroups()) {
|
||||||
if (combatGroup.getBlockers().isEmpty() && combatGroup.getAttackers().contains(toBeBlockedCreatureId)) {
|
if (combatGroup.getBlockers().isEmpty() && combatGroup.getAttackers().contains(toBeBlockedCreatureId)) {
|
||||||
|
|
@ -675,56 +675,27 @@ public class Combat implements Serializable, Copyable<Combat> {
|
||||||
if (toBeBlockedCreature != null) {
|
if (toBeBlockedCreature != null) {
|
||||||
// check if all possible blocker block other creatures they are forced to block
|
// check if all possible blocker block other creatures they are forced to block
|
||||||
// read through all possible blockers
|
// read through all possible blockers
|
||||||
boolean possibleBlockerAvailable = false;
|
|
||||||
for (UUID possibleBlockerId : mustBeBlockedByAtLeastOne.get(toBeBlockedCreatureId)) {
|
for (UUID possibleBlockerId : mustBeBlockedByAtLeastOne.get(toBeBlockedCreatureId)) {
|
||||||
Set<UUID> forcingAttackers = creatureMustBlockAttackers.get(possibleBlockerId);
|
String blockRequiredMessage = isCreatureDoingARequiredBlock(possibleBlockerId, mustBeBlockedByAtLeastOne, game);
|
||||||
if (forcingAttackers == null) {
|
if (blockRequiredMessage != null) { // message means not required
|
||||||
// no other creature forces the blocker to block -> it's available
|
game.informPlayer(controller, blockRequiredMessage + "It's a requirement to block " + toBeBlockedCreature.getIdName());
|
||||||
possibleBlockerAvailable = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// get the attackers he blocks
|
|
||||||
List<UUID> blockedAttackers = null;
|
|
||||||
for (CombatGroup combatGroupToCheck : game.getCombat().getGroups()) {
|
|
||||||
if (combatGroupToCheck.getBlockers().contains(possibleBlockerId)) {
|
|
||||||
blockedAttackers = combatGroupToCheck.getAttackers();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (blockedAttackers == null) {
|
|
||||||
// he blocks no other creature -> it's available
|
|
||||||
possibleBlockerAvailable = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// get attackers forcing the possible blocker to block
|
|
||||||
possibleBlockerAvailable = true;
|
|
||||||
for (UUID blockedAttackerId : blockedAttackers) {
|
|
||||||
if (creatureMustBlockAttackers.get(possibleBlockerId).contains(blockedAttackerId)) {
|
|
||||||
possibleBlockerAvailable = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (possibleBlockerAvailable) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (possibleBlockerAvailable) {
|
|
||||||
if (!game.isSimulation()) {
|
|
||||||
game.informPlayer(controller, new StringBuilder(toBeBlockedCreature.getLogName()).append(" has to be blocked by at least one creature.").toString());
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// take the first potential blocker from the set to block for the AI
|
// take the first potential blocker from the set to block for the AI
|
||||||
UUID blockingCreatureId = mustBeBlockedByAtLeastOne.get(toBeBlockedCreatureId).iterator().next();
|
for (UUID possibleBlockerId : mustBeBlockedByAtLeastOne.get(toBeBlockedCreatureId)) {
|
||||||
Permanent blockingCreature = game.getPermanent(blockingCreatureId);
|
String blockRequiredMessage = isCreatureDoingARequiredBlock(possibleBlockerId, mustBeBlockedByAtLeastOne, game);
|
||||||
if (blockingCreature != null) {
|
if (blockRequiredMessage != null) {
|
||||||
Player defender = game.getPlayer(blockingCreature.getControllerId());
|
// set the block
|
||||||
|
Permanent possibleBlocker = game.getPermanent(possibleBlockerId);
|
||||||
|
Player defender = game.getPlayer(possibleBlocker.getControllerId());
|
||||||
if (defender != null) {
|
if (defender != null) {
|
||||||
defender.declareBlocker(defender.getId(), blockingCreatureId, toBeBlockedCreatureId, game);
|
defender.declareBlocker(defender.getId(), possibleBlockerId, toBeBlockedCreatureId, game);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -800,6 +771,48 @@ public class Combat implements Serializable, Copyable<Combat> {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a possible creature for a block is already doing another
|
||||||
|
* required block
|
||||||
|
*
|
||||||
|
* @param possibleBlockerId
|
||||||
|
* @param mustBeBlockedByAtLeastOne
|
||||||
|
* @param game
|
||||||
|
* @return null block is required otherwise message with reason why not
|
||||||
|
*/
|
||||||
|
protected String isCreatureDoingARequiredBlock(UUID possibleBlockerId, Map<UUID, Set<UUID>> mustBeBlockedByAtLeastOne, Game game) {
|
||||||
|
Permanent possibleBlocker = game.getPermanent(possibleBlockerId);
|
||||||
|
if (possibleBlocker != null) {
|
||||||
|
if (possibleBlocker.getBlocking() == 0) {
|
||||||
|
return possibleBlocker.getIdName() + " does not block, but could block creatures with requirement to be blocked.";
|
||||||
|
}
|
||||||
|
Set<UUID> forcingAttackers = creatureMustBlockAttackers.get(possibleBlockerId);
|
||||||
|
if (forcingAttackers == null) {
|
||||||
|
// no other creature forces the blocker to block -> it's available
|
||||||
|
// check now, if it already blocks a creature that mustBeBlockedByAtLeastOne
|
||||||
|
if (possibleBlocker.getBlocking() > 0) {
|
||||||
|
CombatGroup combatGroupOfPossibleBlocker = findGroupOfBlocker(possibleBlockerId);
|
||||||
|
for (UUID blockedAttackerId : combatGroupOfPossibleBlocker.getAttackers()) {
|
||||||
|
if (mustBeBlockedByAtLeastOne.containsKey(blockedAttackerId)) {
|
||||||
|
// blocks a creature that has to be blocked by at least one
|
||||||
|
if (combatGroupOfPossibleBlocker.getBlockers().size() == 1) {
|
||||||
|
// the creature blocks alone already a creature that has to be blocked by at least one,
|
||||||
|
// so this is ok
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// TODO: Check if the attacker is already blocked by another creature
|
||||||
|
// and despite there is need that this attacker blocks this attacker also
|
||||||
|
// I don't know why
|
||||||
|
Permanent blockedAttacker = game.getPermanent(blockedAttackerId);
|
||||||
|
return possibleBlocker.getIdName() + " blocks with other creatures " + blockedAttacker.getIdName() + ", which has to be blocked by only one creature. ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks the canBeBlockedCheckAfter RestrictionEffect Is the block still
|
* Checks the canBeBlockedCheckAfter RestrictionEffect Is the block still
|
||||||
* valid after all block decisions are done
|
* valid after all block decisions are done
|
||||||
|
|
@ -1037,6 +1050,15 @@ public class Combat implements Serializable, Copyable<Combat> {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CombatGroup findGroupOfBlocker(UUID blockerId) {
|
||||||
|
for (CombatGroup group : groups) {
|
||||||
|
if (group.getBlockers().contains(blockerId)) {
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
// public int totalUnblockedDamage(Game game) {
|
// public int totalUnblockedDamage(Game game) {
|
||||||
// int total = 0;
|
// int total = 0;
|
||||||
// for (CombatGroup group : groups) {
|
// for (CombatGroup group : groups) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue