mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-18 11:48:02 +00:00
Merged changes from trunk to GuiRefactoring: 27266-27293; plus some minor fixes.
This commit is contained in:
@@ -51,7 +51,7 @@ public class AiCardMemory {
|
||||
*/
|
||||
public enum MemorySet {
|
||||
MANDATORY_ATTACKERS,
|
||||
HELD_MANA_SOURCES, // stub, not linked to AI code yet
|
||||
HELD_MANA_SOURCES,
|
||||
//REVEALED_CARDS // stub, not linked to AI code yet
|
||||
}
|
||||
|
||||
|
||||
@@ -68,6 +68,7 @@ import forge.game.replacement.ReplaceMoved;
|
||||
import forge.game.replacement.ReplacementEffect;
|
||||
import forge.game.spellability.Ability;
|
||||
import forge.game.spellability.AbilityManaPart;
|
||||
import forge.game.spellability.AbilityStatic;
|
||||
import forge.game.spellability.AbilitySub;
|
||||
import forge.game.spellability.OptionalCost;
|
||||
import forge.game.spellability.Spell;
|
||||
@@ -740,6 +741,7 @@ public class AiController {
|
||||
&& !ComputerUtil.castPermanentInMain1(player, sa)) {
|
||||
return AiPlayDecision.WaitForMain2;
|
||||
}
|
||||
|
||||
// save cards with flash for surprise blocking
|
||||
if (card.hasKeyword("Flash")
|
||||
&& (player.isUnlimitedHandSize() || player.getCardsIn(ZoneType.Hand).size() <= player.getMaxHandSize())
|
||||
@@ -750,6 +752,20 @@ public class AiController {
|
||||
&& !ComputerUtil.castPermanentInMain1(player, sa)) {
|
||||
return AiPlayDecision.AnotherTime;
|
||||
}
|
||||
|
||||
// don't play cards without being able to pay the upkeep for
|
||||
for (String ability : card.getKeyword()) {
|
||||
if (ability.startsWith("At the beginning of your upkeep, sacrifice CARDNAME unless you pay")) {
|
||||
final String[] k = ability.split(" pay ");
|
||||
final String costs = k[1].replaceAll("[{]", "").replaceAll("[}]", " ");
|
||||
Cost cost = new Cost(costs, true);
|
||||
final Ability emptyAbility = new AbilityStatic(card, cost, sa.getTargetRestrictions()) { @Override public void resolve() { } };
|
||||
emptyAbility.setActivatingPlayer(player);
|
||||
if (!ComputerUtilCost.canPayCost(emptyAbility, player)) {
|
||||
return AiPlayDecision.AnotherTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return canPlayFromEffectAI((SpellPermanent)sa, false, true);
|
||||
} else if( sa instanceof Spell ) {
|
||||
@@ -759,7 +775,7 @@ public class AiController {
|
||||
}
|
||||
|
||||
private AiPlayDecision canPlaySpellBasic(final Card card) {
|
||||
if (card.getSVar("NeedsToPlay").length() > 0) {
|
||||
if (card.hasSVar("NeedsToPlay")) {
|
||||
final String needsToPlay = card.getSVar("NeedsToPlay");
|
||||
List<Card> list = game.getCardsIn(ZoneType.Battlefield);
|
||||
|
||||
|
||||
@@ -29,7 +29,8 @@ public enum AiProps { /** */
|
||||
DEFAULT_PLANAR_DIE_ROLL_CHANCE ("50"), /** */
|
||||
MULLIGAN_THRESHOLD ("5"), /** */
|
||||
PLANAR_DIE_ROLL_HESITATION_CHANCE ("10"),
|
||||
CHEAT_WITH_MANA_ON_SHUFFLE ("FALSE"); /** */
|
||||
CHEAT_WITH_MANA_ON_SHUFFLE ("false"),
|
||||
MOVE_EQUIPMENT_TO_BETTER_CREATURES ("always"); /** */
|
||||
|
||||
private final String strDefaultVal;
|
||||
|
||||
|
||||
@@ -985,8 +985,8 @@ public class AttachAi extends SpellAbilityAi {
|
||||
// TODO AttachSource is currently set for the Source of the Spell, but
|
||||
// at some point can support attaching a different card
|
||||
|
||||
// Don't equip if already equipping
|
||||
if (attachSource.getEquippingCard() != null && attachSource.getEquippingCard().getController() == aiPlayer || attachSource.hasSVar("DontEquip")) {
|
||||
// Don't equip if DontEquip SVar is set
|
||||
if (attachSource.hasSVar("DontEquip")) {
|
||||
return null;
|
||||
}
|
||||
// Don't fortify if already fortifying
|
||||
@@ -1017,6 +1017,35 @@ public class AttachAi extends SpellAbilityAi {
|
||||
|
||||
Card c = attachGeneralAI(aiPlayer, sa, prefList, mandatory, attachSource, sa.getParam("AILogic"));
|
||||
|
||||
AiController aic = ((PlayerControllerAi)aiPlayer.getController()).getAi();
|
||||
if (c != null && attachSource.getType().contains("Equipment")
|
||||
&& attachSource.getEquippingCard() != null
|
||||
&& attachSource.getEquippingCard().getController() == aiPlayer) {
|
||||
if (c.equals(attachSource.getEquippingCard())) {
|
||||
// Do not equip if equipping the same card already
|
||||
return null;
|
||||
}
|
||||
|
||||
if (aic.getProperty(AiProps.MOVE_EQUIPMENT_TO_BETTER_CREATURES).equals("never")) {
|
||||
// Do not equip other creatures if the AI profile does not allow moving equipment around
|
||||
return null;
|
||||
} else if (aic.getProperty(AiProps.MOVE_EQUIPMENT_TO_BETTER_CREATURES).equals("from_useless_only")) {
|
||||
// Do not equip other creatures if the AI profile only allows moving equipment from useless creatures
|
||||
// and the equipped creature is still useful (not non-untapping+tapped and not set to can't attack/block)
|
||||
if (!isUselessCreature(aiPlayer, attachSource.getEquippingCard())) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// make sure to prioritize casting spells in main 2 (creatures, other equipment, etc.) rather than moving equipment around
|
||||
if (aic.getCardMemory().isMemorySetEmpty(AiCardMemory.MemorySet.HELD_MANA_SOURCES)) {
|
||||
SpellAbility futureSpell = aic.predictSpellToCastInMain2(ApiType.Attach);
|
||||
if (futureSpell != null && futureSpell.getHostCard() != null) {
|
||||
aic.reserveManaSourcesForMain2(futureSpell);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((c == null) && mandatory) {
|
||||
CardLists.shuffle(list);
|
||||
c = list.get(0);
|
||||
@@ -1086,7 +1115,7 @@ public class AttachAi extends SpellAbilityAi {
|
||||
}
|
||||
|
||||
// Consider exceptional cases which break the normal evaluation rules
|
||||
if (!isUsefulAttachAction(c, sa)) {
|
||||
if (!isUsefulAttachAction(ai, c, sa)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1279,7 +1308,7 @@ public class AttachAi extends SpellAbilityAi {
|
||||
* @param sa SpellAbility
|
||||
* @return true, if the action is useful (beneficial) in the current minimal context (Card vs. Attach SpellAbility)
|
||||
*/
|
||||
private static boolean isUsefulAttachAction(Card c, SpellAbility sa) {
|
||||
private static boolean isUsefulAttachAction(Player ai, Card c, SpellAbility sa) {
|
||||
if (c == null) {
|
||||
return false;
|
||||
}
|
||||
@@ -1292,17 +1321,28 @@ public class AttachAi extends SpellAbilityAi {
|
||||
|
||||
ArrayList<String> cardTypes = sa.getHostCard().getType();
|
||||
|
||||
if (cardTypes.contains("Equipment") && c.hasKeyword("CARDNAME can't attack or block.")) {
|
||||
if (cardTypes.contains("Equipment") && isUselessCreature(ai, c)) {
|
||||
// useless to equip a creature that can't attack or block.
|
||||
return false;
|
||||
} else if (cardTypes.contains("Equipment") && c.isTapped() && c.hasKeyword("CARDNAME doesn't untap during your untap step.")) {
|
||||
// useless to equip a creature that won't untap and is tapped.
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean isUselessCreature(Player ai, Card c) {
|
||||
if (c == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (c.hasKeyword("CARDNAME can't attack or block.")
|
||||
|| (c.hasKeyword("CARDNAME doesn't untap during your untap step.") && c.isTapped())
|
||||
|| (c.getOwner() == ai && ai.getOpponents().contains(c.getController()))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
||||
return true;
|
||||
|
||||
Reference in New Issue
Block a user