Merge branch 'moraug' into 'master'

Moraug: improve AI

See merge request core-developers/forge!5878
This commit is contained in:
Michael Kamensky
2021-11-25 12:14:25 +00:00
20 changed files with 35 additions and 50 deletions

View File

@@ -1622,6 +1622,7 @@ public class AiController {
Map<String, String> params = t.getMapParams();
if ("ChangesZone".equals(params.get("Mode"))
&& params.containsKey("ValidCard")
&& (!params.containsKey("AILogic") || !params.get("AILogic").equals("SafeToHold"))
&& !params.get("ValidCard").contains("nonLand")
&& ((params.get("ValidCard").contains("Land")) || (params.get("ValidCard").contains("Permanent")))
&& "Battlefield".equals(params.get("Destination"))) {

View File

@@ -14,8 +14,7 @@ public class ChooseEvenOddAi extends SpellAbilityAi {
if (!sa.hasParam("AILogic")) {
return false;
}
TargetRestrictions tgt = sa.getTargetRestrictions();
if (tgt != null) {
if (sa.usesTargeting()) {
sa.resetTargets();
Player opp = AiAttackController.choosePreferredDefenderPlayer(aiPlayer);
if (sa.canTarget(opp)) {
@@ -34,4 +33,3 @@ public class ChooseEvenOddAi extends SpellAbilityAi {
}
}

View File

@@ -44,8 +44,7 @@ public class ChooseNumberAi extends SpellAbilityAi {
return ownCreatureCount > oppMaxCreatureCount + 2 || ownCreatureCount < Math.min(oppMaxCreatureCount, maxChoiceLimit);
}
TargetRestrictions tgt = sa.getTargetRestrictions();
if (tgt != null) {
if (sa.usesTargeting()) {
sa.resetTargets();
Player opp = AiAttackController.choosePreferredDefenderPlayer(aiPlayer);
if (sa.canTarget(opp)) {

View File

@@ -52,8 +52,7 @@ public class ChooseSourceAi extends SpellAbilityAi {
}
}
final TargetRestrictions tgt = sa.getTargetRestrictions();
if (tgt != null) {
if (sa.usesTargeting()) {
sa.resetTargets();
Player opp = AiAttackController.choosePreferredDefenderPlayer(ai);
if (sa.canTarget(opp)) {

View File

@@ -58,8 +58,7 @@ public class CopySpellAbilityAi extends SpellAbilityAi {
}
}
final TargetRestrictions tgt = sa.getTargetRestrictions();
if (tgt != null) {
if (sa.usesTargeting()) {
// Filter AI-specific targets if provided
if ("OnlyOwned".equals(sa.getParam("AITgts"))) {
if (!top.getActivatingPlayer().equals(aiPlayer)) {

View File

@@ -63,8 +63,7 @@ public class CounterAi extends SpellAbilityAi {
}
}
final TargetRestrictions tgt = sa.getTargetRestrictions();
if (tgt != null) {
if (sa.usesTargeting()) {
final SpellAbility topSA = ComputerUtilAbility.getTopSpellAbilityOnStack(game, sa);
if (!CardFactoryUtil.isCounterableBy(topSA.getHostCard(), sa) || topSA.getActivatingPlayer() == ai
|| ai.getAllies().contains(topSA.getActivatingPlayer())) {
@@ -246,10 +245,9 @@ public class CounterAi extends SpellAbilityAi {
@Override
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
final TargetRestrictions tgt = sa.getTargetRestrictions();
final Game game = ai.getGame();
if (tgt != null) {
if (sa.usesTargeting()) {
if (game.getStack().isEmpty()) {
return false;
}

View File

@@ -91,8 +91,7 @@ public class DebuffAi extends SpellAbilityAi {
@Override
public boolean chkAIDrawback(SpellAbility sa, Player ai) {
if (!sa.usesTargeting()) {
// TODO - copied from AF_Pump.pumpDrawbackAI() - what should be
// here?
// TODO - copied from AF_Pump.pumpDrawbackAI() - what should be here?
} else {
return debuffTgtAI(ai, sa, sa.hasParam("Keywords") ? Arrays.asList(sa.getParam("Keywords").split(" & ")) : null, false);
}

View File

@@ -57,19 +57,20 @@ public class PumpAllAi extends PumpAiBase {
}
}
final TargetRestrictions tgt = sa.getTargetRestrictions();
final Player opp = ai.getStrongestOpponent();
if (tgt != null && sa.canTarget(opp) && sa.isCurse()) {
sa.resetTargets();
sa.getTargets().add(opp);
return true;
}
if (tgt != null && sa.canTarget(ai) && !sa.isCurse()) {
sa.resetTargets();
sa.getTargets().add(ai);
return true;
if (sa.usesTargeting()) {
if (sa.canTarget(opp) && sa.isCurse()) {
sa.resetTargets();
sa.getTargets().add(opp);
return true;
}
if (sa.canTarget(ai) && !sa.isCurse()) {
sa.resetTargets();
sa.getTargets().add(ai);
return true;
}
}
final int power = AbilityUtils.calculateAmount(source, sa.getParam("NumAtt"), sa);

View File

@@ -29,7 +29,7 @@ public class TapAllAi extends SpellAbilityAi {
// or during upkeep/begin combat?
final Card source = sa.getHostCard();
final Player opp = ai.getWeakestOpponent();
final Player opp = ai.getStrongestOpponent();
final Game game = ai.getGame();
if (game.getPhaseHandler().getPhase().isAfter(PhaseType.COMBAT_BEGIN)) {

View File

@@ -28,8 +28,7 @@ public class TwoPilesAi extends SpellAbilityAi {
final Player opp = AiAttackController.choosePreferredDefenderPlayer(ai);
final TargetRestrictions tgt = sa.getTargetRestrictions();
if (tgt != null) {
if (sa.usesTargeting()) {
sa.resetTargets();
if (sa.canTarget(opp)) {
sa.getTargets().add(opp);

View File

@@ -48,7 +48,6 @@ public class UnattachAllAi extends SpellAbilityAi {
return chance;
}
/* (non-Javadoc)
* @see forge.card.abilityfactory.SpellAiLogic#doTriggerAINoCost(forge.game.player.Player, java.util.Map, forge.card.spellability.SpellAbility, boolean)
*/
@@ -57,8 +56,7 @@ public class UnattachAllAi extends SpellAbilityAi {
final Card card = sa.getHostCard();
// Check if there are any valid targets
List<GameObject> targets = new ArrayList<>();
final TargetRestrictions tgt = sa.getTargetRestrictions();
if (tgt == null) {
if (!sa.usesTargeting()) {
targets = AbilityUtils.getDefinedObjects(sa.getHostCard(), sa.getParam("Defined"), sa);
}

View File

@@ -125,11 +125,11 @@ public class UntapAi extends SpellAbilityAi {
private static boolean untapPrefTargeting(final Player ai, final SpellAbility sa, final boolean mandatory) {
final Card source = sa.getHostCard();
Player targetController = ai;
final PlayerCollection targetController = new PlayerCollection();
if (sa.isCurse()) {
// TODO search through all opponents, may need to check if different controllers allowed
targetController = AiAttackController.choosePreferredDefenderPlayer(ai);
targetController.addAll(ai.getOpponents());
} else {
targetController.add(ai);
}
CardCollection list = CardLists.getTargetableCards(targetController.getCardsIn(ZoneType.Battlefield), sa);

View File

@@ -1689,10 +1689,6 @@ public class AbilityUtils {
// accept straight numbers
if (l[0].startsWith("Number$")) {
final String number = l[0].substring(7);
if (number.equals("ChosenNumber")) { // TODO remove in favor of Count ChosenNumber
int x = c.getChosenNumber() == null ? 0 : c.getChosenNumber();
return doXMath(x, expr, c, ctb);
}
return doXMath(Integer.parseInt(number), expr, c, ctb);
}

View File

@@ -272,8 +272,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
sb.append(".");
} else if (origin.equals("Battlefield")) {
// TODO Expand on this Description as more cards use it
// for the non-targeted SAs when you choose what is returned on
// resolution
// for the non-targeted SAs when you choose what is returned on resolution
sb.append("Return ").append(num).append(" ").append(type).append(" card(s) ");
sb.append(" to your ").append(destination);
}
@@ -361,8 +360,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
sb.append(fromGraveyard);
}
// this needs to be zero indexed. Top = 0, Third = 2, -1 =
// Bottom
// this needs to be zero indexed. Top = 0, Third = 2, -1 = Bottom
final int libraryPosition = sa.hasParam("LibraryPosition") ? AbilityUtils.calculateAmount(host, sa.getParam("LibraryPosition"), sa) : 0;
if (libraryPosition == -1) {

View File

@@ -9,7 +9,7 @@ SVar:DBShuffle:DB$ Shuffle | Defined$ ParentTarget | SubAbility$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
SVar:X:TargetedPlayer$CardsInLibrary
SVar:Y:Remembered$Valid Card.NamedCard
SVar:Z:Number$ChosenNumber
SVar:Z:Count$ChosenNumber
AI:RemoveDeck:All
AI:RemoveDeck:Random
SVar:Picture:http://www.wizards.com/global/images/magic/general/mindblaze.jpg

View File

@@ -4,7 +4,7 @@ Types:Legendary Creature Minotaur Warrior
PT:6/6
S:Mode$ Continuous | Affected$ Creature.YouCtrl | AddPower$ AffectedX | Description$ Each creature you control gets +1/+0 for each time it has attacked this turn.
SVar:AffectedX:Count$CardNumAttacksThisTurn
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Land.YouCtrl | TriggerZones$ Battlefield | PlayerTurn$ True | Phase$ Main1,Main2 | Execute$ TrigAddPhase | TriggerDescription$ Landfall - Whenever a land enters the battlefield under your control, if it's your main phase, there's an additional combat phase after this phase. At the beginning of that combat, untap all creatures you control.
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | AILogic$ SafeToHold | ValidCard$ Land.YouCtrl | TriggerZones$ Battlefield | PlayerTurn$ True | Phase$ Main1,Main2 | Execute$ TrigAddPhase | TriggerDescription$ Landfall - Whenever a land enters the battlefield under your control, if it's your main phase, there's an additional combat phase after this phase. At the beginning of that combat, untap all creatures you control.
SVar:TrigAddPhase:DB$ AddPhase | ExtraPhase$ Combat | ConditionPhases$ Main1,Main2 | ExtraPhaseDelayedTrigger$ DelTrigUntap | ExtraPhaseDelayedTriggerExcute$ TrigUntapAll
SVar:DelTrigUntap:Mode$ Phase | Phase$ BeginCombat | ValidPlayer$ You | TriggerDescription$ At the beginning of that combat, untap all creatures you control.
SVar:TrigUntapAll:DB$ UntapAll | ValidCards$ Creature.YouCtrl

View File

@@ -5,7 +5,7 @@ PT:2/2
K:ETBReplacement:Other:ChooseNumber
SVar:ChooseNumber:DB$ ChooseNumber | Defined$ You | SpellDescription$ As CARDNAME enters the battlefield, choose a number.
S:Mode$ CantBeCast | ValidCard$ Card.nonCreature+cmcEQX | Description$ Noncreature spells with mana value equal to the chosen number can't be cast.
SVar:X:Number$ChosenNumber
SVar:X:Count$ChosenNumber
AI:RemoveDeck:All
SVar:Picture:http://www.wizards.com/global/images/magic/general/sanctum_prelate.jpg
Oracle:As Sanctum Prelate enters the battlefield, choose a number.\nNoncreature spells with mana value equal to the chosen number can't be cast.

View File

@@ -10,7 +10,7 @@ SVar:IncrementLoss:DB$ StoreSVar | SVar$ Loss | Type$ CountSVar | Expression$ Lo
SVar:SetFilpsDone:DB$ StoreSVar | SVar$ FlipsDone | Type$ CountSVar | Expression$ TimesToFlip
# Draw Cards
SVar:DrawIfWin:DB$ Draw | Defined$ You | NumCards$ CardsToDraw | ConditionCheckSVar$ Loss | ConditionSVarCompare$ EQ0
SVar:TimesToFlip:Number$ChosenNumber
SVar:TimesToFlip:Count$ChosenNumber
SVar:FlipsDone:Number$0
SVar:Loss:Number$0
SVar:CardsToDraw:Count$ChosenNumber/Times.2

View File

@@ -4,7 +4,7 @@ Types:Sorcery
A:SP$ ChooseNumber | Cost$ 3 B R | SubAbility$ DBVoidDestroyAll | SpellDescription$ Choose a number. Destroy all artifacts and creatures with mana value equal to that number. Then target player reveals their hand and discards all nonland cards with mana value equal to the number.
SVar:DBVoidDestroyAll:DB$ DestroyAll | ValidCards$ Artifact.cmcEQX,Creature.cmcEQX | SubAbility$ DBVoidRevealDiscard
SVar:DBVoidRevealDiscard:DB$ Discard | ValidTgts$ Player | TgtPrompt$ Select target player | Mode$ RevealDiscardAll | DiscardValid$ Card.nonLand+cmcEQX
SVar:X:Number$ChosenNumber
SVar:X:Count$ChosenNumber
AI:RemoveDeck:All
SVar:Picture:http://www.wizards.com/global/images/magic/general/void.jpg
Oracle:Choose a number. Destroy all artifacts and creatures with mana value equal to that number. Then target player reveals their hand and discards all nonland cards with mana value equal to the number.

View File

@@ -11,7 +11,7 @@ SVar:DBDamage:DB$ DealDamage | NumDmg$ Damage | Defined$ You
SVar:DBEffect:DB$ Effect | StaticAbilities$ MayPlay | Stackable$ False | ConditionCheckSVar$ Y | ConditionSVarCompare$ EQ5 | SubAbility$ DBCleanup
SVar:MayPlay:Mode$ Continuous | EffectZone$ Command | Affected$ Card.nonLand+YouOwn | MayPlay$ True | MayPlayWithoutManaCost$ True | AffectedZone$ Hand | Description$ You may cast spells from your hand this turn without paying their mana costs.
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
SVar:X:Number$ChosenNumber
SVar:X:Count$ChosenNumber
SVar:Y:Count$RememberedNumber
SVar:Damage:SVar$Losses/Times.2
Oracle:Flying\nWhenever Yusri, Fortune's Flame attacks, choose a number between 1 and 5. Flip that many coins. For each flip you win, draw a card. For each flip you lose, Yusri deals 2 damage to you. If you won five flips this way, you may cast spells from your hand this turn without paying their mana costs.