mirror of
https://github.com/magefree/mage.git
synced 2026-01-26 21:29:17 -08:00
refactor, combat: improved declare blockers code, added docs, added additional runtime checks for AI, added debug info
This commit is contained in:
parent
60112c6be5
commit
23a414e074
2 changed files with 63 additions and 17 deletions
|
|
@ -663,26 +663,47 @@ public class Combat implements Serializable, Copyable<Combat> {
|
|||
if (defender == null) {
|
||||
continue;
|
||||
}
|
||||
boolean choose = true;
|
||||
if (blockController == null) {
|
||||
controller = defender;
|
||||
} else {
|
||||
controller = blockController;
|
||||
}
|
||||
while (choose) {
|
||||
|
||||
// choosing until good block configuration
|
||||
while (true) {
|
||||
// declare normal blockers
|
||||
// TODO: need reseach - is it possible to concede on bad blocker configuration (e.g. user can't continue)
|
||||
controller.selectBlockers(source, game, defenderId);
|
||||
if (game.isPaused() || game.checkIfGameIsOver() || game.executingRollback()) {
|
||||
return;
|
||||
}
|
||||
if (!game.getCombat().checkBlockRestrictions(defender, game)) {
|
||||
if (controller.isHuman()) { // only human player can decide to do the block in another way
|
||||
continue;
|
||||
}
|
||||
|
||||
// check multiple restrictions by permanents and effects, reset on invalid blocking configuration, try to auto-fix
|
||||
// TODO: wtf, some checks contains AI related code inside -- it must be reworked and moved to computer classes?!
|
||||
|
||||
// check 1 of 3
|
||||
boolean isValidBlock = game.getCombat().checkBlockRestrictions(defender, game);
|
||||
if (!isValidBlock) {
|
||||
makeSureItsNotComputer(controller);
|
||||
continue;
|
||||
}
|
||||
choose = !game.getCombat().checkBlockRequirementsAfter(defender, controller, game);
|
||||
if (!choose) {
|
||||
choose = !game.getCombat().checkBlockRestrictionsAfter(defender, controller, game);
|
||||
|
||||
// check 2 of 3
|
||||
isValidBlock = game.getCombat().checkBlockRequirementsAfter(defender, controller, game);
|
||||
if (!isValidBlock) {
|
||||
makeSureItsNotComputer(controller);
|
||||
continue;
|
||||
}
|
||||
|
||||
// check 3 of 3
|
||||
isValidBlock = game.getCombat().checkBlockRestrictionsAfter(defender, controller, game);
|
||||
if (!isValidBlock) {
|
||||
makeSureItsNotComputer(controller);
|
||||
continue;
|
||||
}
|
||||
|
||||
// all valid, can finish now
|
||||
break;
|
||||
}
|
||||
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.DECLARED_BLOCKERS, defenderId, defenderId));
|
||||
|
||||
|
|
@ -695,6 +716,15 @@ public class Combat implements Serializable, Copyable<Combat> {
|
|||
TraceUtil.traceCombatIfNeeded(game, game.getCombat());
|
||||
}
|
||||
|
||||
private void makeSureItsNotComputer(Player controller) {
|
||||
if (controller.isComputer() || !controller.isHuman()) {
|
||||
// TODO: wtf, AI will freeze forever here in games with attacker/blocker restrictions,
|
||||
// but it pass in some use cases due random choices. AI must deside blocker configuration
|
||||
// in one attempt
|
||||
throw new IllegalStateException("AI can't find good blocker configuration, report it to github");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add info about attacker blocked by blocker to the game log
|
||||
*/
|
||||
|
|
@ -852,10 +882,7 @@ public class Combat implements Serializable, Copyable<Combat> {
|
|||
* attacking creature fulfills both the restriction and the requirement, so
|
||||
* that's the only option.
|
||||
*
|
||||
* @param player
|
||||
* @param controller
|
||||
* @param game
|
||||
* @return
|
||||
* @return false on invalid block configuration e.g. player must choose new blockers
|
||||
*/
|
||||
public boolean checkBlockRequirementsAfter(Player player, Player controller, Game game) {
|
||||
// Get once a list of all opponents in range
|
||||
|
|
@ -1274,10 +1301,7 @@ public class Combat implements Serializable, Copyable<Combat> {
|
|||
* Checks the canBeBlockedCheckAfter RestrictionEffect Is the block still
|
||||
* valid after all block decisions are done
|
||||
*
|
||||
* @param player
|
||||
* @param controller
|
||||
* @param game
|
||||
* @return
|
||||
* @return false on invalid block configuration e.g. player must choose new blockers
|
||||
*/
|
||||
public boolean checkBlockRestrictionsAfter(Player player, Player controller, Game game) {
|
||||
// Restrictions applied to blocking creatures
|
||||
|
|
@ -1906,4 +1930,18 @@ public class Combat implements Serializable, Copyable<Combat> {
|
|||
return new Combat(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
List<String> res = new ArrayList<>();
|
||||
for (int i = 0; i < this.groups.size(); i++) {
|
||||
res.add(String.format("group %d with %s",
|
||||
i + 1,
|
||||
this.groups.get(i)
|
||||
));
|
||||
}
|
||||
return String.format("%d groups%s",
|
||||
this.groups.size(),
|
||||
this.groups.size() > 0 ? ": " + String.join("; ", res) : ""
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -976,4 +976,12 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
|
|||
private static int getLethalDamage(Permanent blocker, Permanent attacker, Game game) {
|
||||
return blocker.getLethalDamage(attacker.getId(), game);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%d attackers, %d blockers",
|
||||
this.getAttackers().size(),
|
||||
this.getBlockers().size()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue