- Improved and fixed the handling of game states inside combat phases (DA, DB), added a way to mark cards that are attacking in saved game states.

This commit is contained in:
Agetian
2017-08-01 10:10:15 +00:00
parent 5931b53896
commit e43cfce016

View File

@@ -1,24 +1,14 @@
package forge.ai; package forge.ai;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.*;
import java.util.Map.Entry;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import forge.StaticData; import forge.StaticData;
import forge.card.CardStateName; import forge.card.CardStateName;
import forge.game.Game; import forge.game.Game;
import forge.game.GameEntity; import forge.game.GameEntity;
import forge.game.ability.AbilityFactory; import forge.game.ability.AbilityFactory;
import forge.game.ability.effects.DetachedCardEffect; import forge.game.ability.effects.DetachedCardEffect;
import forge.game.card.Card; import forge.game.card.*;
import forge.game.card.CardCollection; import forge.game.combat.Combat;
import forge.game.card.CardCollectionView;
import forge.game.card.CardFactory;
import forge.game.card.CounterType;
import forge.game.phase.PhaseType; import forge.game.phase.PhaseType;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
@@ -30,6 +20,12 @@ import forge.item.PaperCard;
import forge.util.TextUtil; import forge.util.TextUtil;
import forge.util.collect.FCollectionView; import forge.util.collect.FCollectionView;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.*;
import java.util.Map.Entry;
public abstract class GameState { public abstract class GameState {
private static final Map<ZoneType, String> ZONES = new HashMap<ZoneType, String>(); private static final Map<ZoneType, String> ZONES = new HashMap<ZoneType, String>();
static { static {
@@ -56,6 +52,7 @@ public abstract class GameState {
private final Map<Card, String> cardToChosenType = new HashMap<>(); private final Map<Card, String> cardToChosenType = new HashMap<>();
private final Map<Card, List<String>> cardToRememberedId = new HashMap<>(); private final Map<Card, List<String>> cardToRememberedId = new HashMap<>();
private final Map<Card, String> cardToExiledWithId = new HashMap<>(); private final Map<Card, String> cardToExiledWithId = new HashMap<>();
private final Map<Card, Card> cardAttackMap = new HashMap<>();
private final Map<Card, String> cardToScript = new HashMap<>(); private final Map<Card, String> cardToScript = new HashMap<>();
@@ -141,6 +138,13 @@ public abstract class GameState {
cardsReferencedByID.add((Card)o); cardsReferencedByID.add((Card)o);
} }
} }
if (game.getCombat() != null && game.getCombat().isAttacking(card)) {
// Remember the IDs of attacked planeswalkers
GameEntity def = game.getCombat().getDefenderByAttacker(card);
if (def instanceof Card) {
cardsReferencedByID.add((Card)def);
}
}
} }
} }
@@ -237,6 +241,16 @@ public abstract class GameState {
} }
} }
if (c.getGame().getCombat() != null) {
if (c.getGame().getCombat().isAttacking(c)) {
newText.append("|Attacking");
GameEntity def = c.getGame().getCombat().getDefenderByAttacker(c);
if (def instanceof Card) {
newText.append(":" + def.getId());
}
}
}
cardTexts.put(zoneType, newText.toString()); cardTexts.put(zoneType, newText.toString());
} }
@@ -388,6 +402,7 @@ public abstract class GameState {
cardToChosenClrs.clear(); cardToChosenClrs.clear();
cardToChosenType.clear(); cardToChosenType.clear();
cardToScript.clear(); cardToScript.clear();
cardAttackMap.clear();
Player newPlayerTurn = tChangePlayer.equals("human") ? human : tChangePlayer.equals("ai") ? ai : null; Player newPlayerTurn = tChangePlayer.equals("human") ? human : tChangePlayer.equals("ai") ? ai : null;
PhaseType newPhase = tChangePhase.equals("none") ? null : PhaseType.smartValueOf(tChangePhase); PhaseType newPhase = tChangePhase.equals("none") ? null : PhaseType.smartValueOf(tChangePhase);
@@ -401,6 +416,7 @@ public abstract class GameState {
if (!computerCounters.isEmpty()) { if (!computerCounters.isEmpty()) {
applyCountersToGameEntity(ai, computerCounters); applyCountersToGameEntity(ai, computerCounters);
} }
game.getPhaseHandler().devModeSet(newPhase, newPlayerTurn); game.getPhaseHandler().devModeSet(newPhase, newPlayerTurn);
game.getTriggerHandler().suppressMode(TriggerType.ChangesZone); game.getTriggerHandler().suppressMode(TriggerType.ChangesZone);
@@ -416,6 +432,12 @@ public abstract class GameState {
handleScriptExecution(); handleScriptExecution();
handleMarkedDamage(); handleMarkedDamage();
// Combat only works for 1v1 matches for now (which are the only matches dev mode supports anyway)
if (newPhase == PhaseType.COMBAT_DECLARE_ATTACKERS || newPhase == PhaseType.COMBAT_DECLARE_BLOCKERS) {
boolean toDeclareBlockers = newPhase == PhaseType.COMBAT_DECLARE_BLOCKERS;
handleCombat(game, newPlayerTurn, newPlayerTurn.getSingleOpponent(), toDeclareBlockers);
}
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone); game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
game.getTriggerHandler().clearSuppression(TriggerType.DamageDone); game.getTriggerHandler().clearSuppression(TriggerType.DamageDone);
game.getTriggerHandler().clearSuppression(TriggerType.Unequip); game.getTriggerHandler().clearSuppression(TriggerType.Unequip);
@@ -425,6 +447,31 @@ public abstract class GameState {
game.getAction().checkStateEffects(true); //ensure state based effects and triggers are updated game.getAction().checkStateEffects(true); //ensure state based effects and triggers are updated
} }
private void handleCombat(final Game game, final Player attackingPlayer, final Player defendingPlayer, final boolean toDeclareBlockers) {
// First we need to ensure that all attackers are declared in the Declare Attackers step,
// even if proceeding straight to Declare Blockers
game.getPhaseHandler().devModeSet(PhaseType.COMBAT_DECLARE_ATTACKERS, attackingPlayer);
if (game.getPhaseHandler().getCombat() == null) {
game.getPhaseHandler().setCombat(new Combat(attackingPlayer));
game.updateCombatForView();
}
for (Entry<Card, Card> attackMap : cardAttackMap.entrySet()) {
Card attacker = attackMap.getKey();
Card attacked = attackMap.getValue();
game.getPhaseHandler().getCombat().addAttacker(attacker, attacked == null ? defendingPlayer : attacked);
}
game.updateCombatForView();
// Gracefully proceed to Declare Blockers, giving priority to the defending player
if (toDeclareBlockers) {
game.getPhaseHandler().devAdvanceToPhase(PhaseType.COMBAT_DECLARE_BLOCKERS);
}
}
private void handleRememberedEntities() { private void handleRememberedEntities() {
// Remembered: X // Remembered: X
for (Entry<Card, List<String>> rememberedEnts : cardToRememberedId.entrySet()) { for (Entry<Card, List<String>> rememberedEnts : cardToRememberedId.entrySet()) {
@@ -678,6 +725,13 @@ public abstract class GameState {
cardToRememberedId.put(c, Arrays.asList(info.substring(info.indexOf(':') + 1).split(","))); cardToRememberedId.put(c, Arrays.asList(info.substring(info.indexOf(':') + 1).split(",")));
} else if (info.startsWith("ExiledWith:")) { } else if (info.startsWith("ExiledWith:")) {
cardToExiledWithId.put(c, info.substring(info.indexOf(':') + 1)); cardToExiledWithId.put(c, info.substring(info.indexOf(':') + 1));
} else if (info.startsWith("Attacking")) {
if (info.contains(":")) {
int id = Integer.parseInt(info.substring(info.indexOf(':') + 1));
cardAttackMap.put(c, idToCard.get(id));
} else {
cardAttackMap.put(c, null);
}
} }
} }