mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-16 18:58:00 +00:00
Merge branch 'aifix' into 'master'
Fix Muse Vessel + Hexproof See merge request core-developers/forge!5587
This commit is contained in:
@@ -35,7 +35,11 @@ public class ActivateAbilityAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
sa.getTargets().add(opp);
|
if (sa.canTarget(opp)) {
|
||||||
|
sa.getTargets().add(opp);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean randomReturn = MyRandom.getRandom().nextFloat() <= Math.pow(.6667, sa.getActivationsThisTurn());
|
boolean randomReturn = MyRandom.getRandom().nextFloat() <= Math.pow(.6667, sa.getActivationsThisTurn());
|
||||||
@@ -57,7 +61,6 @@ public class ActivateAbilityAi extends SpellAbilityAi {
|
|||||||
|
|
||||||
return defined.contains(opp);
|
return defined.contains(opp);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
sa.getTargets().add(opp);
|
sa.getTargets().add(opp);
|
||||||
|
|||||||
@@ -344,6 +344,9 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
|||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
sa.getTargets().add(ai);
|
sa.getTargets().add(ai);
|
||||||
}
|
}
|
||||||
|
if (!sa.isTargetNumberValid()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
pDefined = sa.getTargets().getTargetPlayers();
|
pDefined = sa.getTargets().getTargetPlayers();
|
||||||
} else {
|
} else {
|
||||||
if (sa.hasParam("DefinedPlayer")) {
|
if (sa.hasParam("DefinedPlayer")) {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package forge.ai.ability;
|
package forge.ai.ability;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import com.google.common.base.Predicates;
|
import com.google.common.base.Predicates;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
@@ -138,13 +139,17 @@ public class ChangeZoneAllAi extends SpellAbilityAi {
|
|||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
// search targetable Opponents
|
// search targetable Opponents
|
||||||
final Iterable<Player> oppList = Iterables.filter(ai.getOpponents(), PlayerPredicates.isTargetableBy(sa));
|
final List<Player> oppList = Lists.newArrayList(Iterables.filter(ai.getOpponents(), PlayerPredicates.isTargetableBy(sa)));
|
||||||
|
|
||||||
|
if (oppList.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// get the one with the most handsize
|
// get the one with the most handsize
|
||||||
Player oppTarget = Collections.max(Lists.newArrayList(oppList), PlayerPredicates.compareByZoneSize(origin));
|
Player oppTarget = Collections.max(oppList, PlayerPredicates.compareByZoneSize(origin));
|
||||||
|
|
||||||
// set the target
|
// set the target
|
||||||
if (oppTarget != null && !oppTarget.getCardsIn(ZoneType.Hand).isEmpty()) {
|
if (!oppTarget.getCardsIn(ZoneType.Hand).isEmpty()) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
sa.getTargets().add(oppTarget);
|
sa.getTargets().add(oppTarget);
|
||||||
} else {
|
} else {
|
||||||
@@ -152,24 +157,26 @@ public class ChangeZoneAllAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (origin.equals(ZoneType.Battlefield)) {
|
} else if (origin.equals(ZoneType.Battlefield)) {
|
||||||
// this statement is assuming the AI is trying to use this spell
|
// this statement is assuming the AI is trying to use this spell offensively
|
||||||
// offensively
|
// if the AI is using it defensively, then something else needs to occur
|
||||||
// if the AI is using it defensively, then something else needs to
|
|
||||||
// occur
|
|
||||||
// if only creatures are affected evaluate both lists and pass only
|
// if only creatures are affected evaluate both lists and pass only
|
||||||
// if human creatures are more valuable
|
// if human creatures are more valuable
|
||||||
if (sa.usesTargeting()) {
|
if (sa.usesTargeting()) {
|
||||||
// search targetable Opponents
|
// search targetable Opponents
|
||||||
final Iterable<Player> oppList = Iterables.filter(ai.getOpponents(),
|
final List<Player> oppList = Lists.newArrayList(Iterables.filter(ai.getOpponents(),
|
||||||
PlayerPredicates.isTargetableBy(sa));
|
PlayerPredicates.isTargetableBy(sa)));
|
||||||
|
|
||||||
|
if (oppList.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// get the one with the most in graveyard
|
// get the one with the most in graveyard
|
||||||
// zone is visible so evaluate which would be hurt the most
|
// zone is visible so evaluate which would be hurt the most
|
||||||
Player oppTarget = Collections.max(Lists.newArrayList(oppList),
|
Player oppTarget = Collections.max(oppList,
|
||||||
PlayerPredicates.compareByZoneSize(origin));
|
PlayerPredicates.compareByZoneSize(origin));
|
||||||
|
|
||||||
// set the target
|
// set the target
|
||||||
if (oppTarget != null && !oppTarget.getCardsIn(ZoneType.Graveyard).isEmpty()) {
|
if (oppTarget.getCardsIn(ZoneType.Graveyard).isEmpty()) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
sa.getTargets().add(oppTarget);
|
sa.getTargets().add(oppTarget);
|
||||||
} else {
|
} else {
|
||||||
@@ -234,7 +241,7 @@ public class ChangeZoneAllAi extends SpellAbilityAi {
|
|||||||
AiPlayerPredicates.compareByZoneValue(sa.getParam("ChangeType"), origin, sa));
|
AiPlayerPredicates.compareByZoneValue(sa.getParam("ChangeType"), origin, sa));
|
||||||
|
|
||||||
// set the target
|
// set the target
|
||||||
if (oppTarget != null && !oppTarget.getCardsIn(ZoneType.Graveyard).isEmpty()) {
|
if (!oppTarget.getCardsIn(ZoneType.Graveyard).isEmpty()) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
sa.getTargets().add(oppTarget);
|
sa.getTargets().add(oppTarget);
|
||||||
} else {
|
} else {
|
||||||
@@ -380,15 +387,19 @@ public class ChangeZoneAllAi extends SpellAbilityAi {
|
|||||||
if (origin.equals(ZoneType.Hand) || origin.equals(ZoneType.Library)) {
|
if (origin.equals(ZoneType.Hand) || origin.equals(ZoneType.Library)) {
|
||||||
if (sa.usesTargeting()) {
|
if (sa.usesTargeting()) {
|
||||||
// search targetable Opponents
|
// search targetable Opponents
|
||||||
final Iterable<Player> oppList = Iterables.filter(ai.getOpponents(),
|
final List<Player> oppList = Lists.newArrayList(Iterables.filter(ai.getOpponents(),
|
||||||
PlayerPredicates.isTargetableBy(sa));
|
PlayerPredicates.isTargetableBy(sa)));
|
||||||
|
|
||||||
|
if (oppList.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// get the one with the most handsize
|
// get the one with the most handsize
|
||||||
Player oppTarget = Collections.max(Lists.newArrayList(oppList),
|
Player oppTarget = Collections.max(oppList,
|
||||||
PlayerPredicates.compareByZoneSize(origin));
|
PlayerPredicates.compareByZoneSize(origin));
|
||||||
|
|
||||||
// set the target
|
// set the target
|
||||||
if (oppTarget != null && !oppTarget.getCardsIn(ZoneType.Hand).isEmpty()) {
|
if (!oppTarget.getCardsIn(ZoneType.Hand).isEmpty()) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
sa.getTargets().add(oppTarget);
|
sa.getTargets().add(oppTarget);
|
||||||
} else {
|
} else {
|
||||||
@@ -418,16 +429,20 @@ public class ChangeZoneAllAi extends SpellAbilityAi {
|
|||||||
} else if (origin.equals(ZoneType.Graveyard)) {
|
} else if (origin.equals(ZoneType.Graveyard)) {
|
||||||
if (sa.usesTargeting()) {
|
if (sa.usesTargeting()) {
|
||||||
// search targetable Opponents
|
// search targetable Opponents
|
||||||
final Iterable<Player> oppList = Iterables.filter(ai.getOpponents(),
|
final List<Player> oppList = Lists.newArrayList(Iterables.filter(ai.getOpponents(),
|
||||||
PlayerPredicates.isTargetableBy(sa));
|
PlayerPredicates.isTargetableBy(sa)));
|
||||||
|
|
||||||
|
if (oppList.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// get the one with the most in graveyard
|
// get the one with the most in graveyard
|
||||||
// zone is visible so evaluate which would be hurt the most
|
// zone is visible so evaluate which would be hurt the most
|
||||||
Player oppTarget = Collections.max(Lists.newArrayList(oppList),
|
Player oppTarget = Collections.max(oppList,
|
||||||
AiPlayerPredicates.compareByZoneValue(sa.getParam("ChangeType"), origin, sa));
|
AiPlayerPredicates.compareByZoneValue(sa.getParam("ChangeType"), origin, sa));
|
||||||
|
|
||||||
// set the target
|
// set the target
|
||||||
if (oppTarget != null && !oppTarget.getCardsIn(ZoneType.Graveyard).isEmpty()) {
|
if (!oppTarget.getCardsIn(ZoneType.Graveyard).isEmpty()) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
sa.getTargets().add(oppTarget);
|
sa.getTargets().add(oppTarget);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -107,8 +107,7 @@ public class CountersPutAi extends CountersAi {
|
|||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
|
|
||||||
if (sa.isOutlast()) {
|
if (sa.isOutlast()) {
|
||||||
if (ph.is(PhaseType.MAIN2, ai)) { // applicable to non-attackers
|
if (ph.is(PhaseType.MAIN2, ai)) { // applicable to non-attackers only
|
||||||
// only
|
|
||||||
float chance = 0.8f;
|
float chance = 0.8f;
|
||||||
if (ComputerUtilCard.doesSpecifiedCreatureBlock(ai, source)) {
|
if (ComputerUtilCard.doesSpecifiedCreatureBlock(ai, source)) {
|
||||||
return false;
|
return false;
|
||||||
@@ -788,7 +787,7 @@ public class CountersPutAi extends CountersAi {
|
|||||||
List<Player> playerList = Lists.newArrayList(Iterables.filter(
|
List<Player> playerList = Lists.newArrayList(Iterables.filter(
|
||||||
sa.getTargetRestrictions().getAllCandidates(sa, true, true), Player.class));
|
sa.getTargetRestrictions().getAllCandidates(sa, true, true), Player.class));
|
||||||
|
|
||||||
if (playerList.isEmpty() && mandatory) {
|
if (playerList.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -117,8 +117,7 @@ public class MillAi extends SpellAbilityAi {
|
|||||||
|
|
||||||
// if it would mill none, try other one
|
// if it would mill none, try other one
|
||||||
if (numCards <= 0) {
|
if (numCards <= 0) {
|
||||||
if ((sa.getParam("NumCards").equals("X") || sa.getParam("NumCards").equals("Z")))
|
if ((sa.getParam("NumCards").equals("X") || sa.getParam("NumCards").equals("Z"))) {
|
||||||
{
|
|
||||||
if (source.getSVar("X").startsWith("Count$xPaid")) {
|
if (source.getSVar("X").startsWith("Count$xPaid")) {
|
||||||
// Spell is PayX based
|
// Spell is PayX based
|
||||||
} else if (source.getSVar("X").startsWith("Remembered$ChromaSource")) {
|
} else if (source.getSVar("X").startsWith("Remembered$ChromaSource")) {
|
||||||
@@ -136,7 +135,7 @@ public class MillAi extends SpellAbilityAi {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if that player can be miled, select this one.
|
// if that player can be milled, select this one.
|
||||||
if (numCards >= pLibrary.size()) {
|
if (numCards >= pLibrary.size()) {
|
||||||
sa.getTargets().add(o);
|
sa.getTargets().add(o);
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -3,8 +3,7 @@ ManaCost:4 B B
|
|||||||
Types:Legendary Creature Demon Spirit
|
Types:Legendary Creature Demon Spirit
|
||||||
PT:6/4
|
PT:6/4
|
||||||
T:Mode$ SpellCast | ValidCard$ Spirit,Arcane | ValidActivatingPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigExile | TriggerDescription$ Whenever you cast a Spirit or Arcane spell, target opponent exiles a card from their hand.
|
T:Mode$ SpellCast | ValidCard$ Spirit,Arcane | ValidActivatingPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigExile | TriggerDescription$ Whenever you cast a Spirit or Arcane spell, target opponent exiles a card from their hand.
|
||||||
#This needs Defined$ Opponent because ValidTgts$ Opponent lets Kyoki's controller select the card to be exiled
|
SVar:TrigExile:DB$ChangeZone | Origin$ Hand | Destination$ Exile | ChangeType$ Card | ValidTgts$ Opponent | Chooser$ Targeted | TgtPrompt$ Select target opponent | ChangeNum$ 1 | Mandatory$ True
|
||||||
SVar:TrigExile:DB$ChangeZone | Origin$ Hand | Destination$ Exile | ChangeType$ Card | ValidTgts$ Opponent | Chooser$ Targeted | TgtPrompt$ Select target opponent | ChangeNum$ 1
|
|
||||||
AI:RemoveDeck:Random
|
AI:RemoveDeck:Random
|
||||||
DeckHints:Type$Spirit|Arcane
|
DeckHints:Type$Spirit|Arcane
|
||||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/kyoki_sanitys_eclipse.jpg
|
SVar:Picture:http://www.wizards.com/global/images/magic/general/kyoki_sanitys_eclipse.jpg
|
||||||
|
|||||||
Reference in New Issue
Block a user