mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-14 09:48:02 +00:00
Some fixes (#8816)
This commit is contained in:
@@ -887,28 +887,9 @@ public class AiController {
|
||||
private AiPlayDecision canPlayAndPayForFace(final SpellAbility sa) {
|
||||
final Card host = sa.getHostCard();
|
||||
|
||||
// Check a predefined condition
|
||||
if (sa.hasParam("AICheckSVar")) {
|
||||
final String svarToCheck = sa.getParam("AICheckSVar");
|
||||
String comparator = "GE";
|
||||
int compareTo = 1;
|
||||
|
||||
if (sa.hasParam("AISVarCompare")) {
|
||||
final String fullCmp = sa.getParam("AISVarCompare");
|
||||
comparator = fullCmp.substring(0, 2);
|
||||
final String strCmpTo = fullCmp.substring(2);
|
||||
try {
|
||||
compareTo = Integer.parseInt(strCmpTo);
|
||||
} catch (final Exception ignored) {
|
||||
compareTo = AbilityUtils.calculateAmount(host, host.getSVar(strCmpTo), sa);
|
||||
}
|
||||
}
|
||||
|
||||
int left = AbilityUtils.calculateAmount(host, svarToCheck, sa);
|
||||
if (!Expressions.compare(left, comparator, compareTo)) {
|
||||
if (sa.hasParam("AICheckSVar") && !aiShouldRun(sa, sa, host, null)) {
|
||||
return AiPlayDecision.AnotherTime;
|
||||
}
|
||||
}
|
||||
|
||||
// this is the "heaviest" check, which also sets up targets, defines X, etc.
|
||||
AiPlayDecision canPlay = canPlaySa(sa);
|
||||
@@ -1817,14 +1798,9 @@ public class AiController {
|
||||
* @param sa the sa
|
||||
* @return true, if successful
|
||||
*/
|
||||
public final boolean aiShouldRun(final ReplacementEffect effect, final SpellAbility sa, GameEntity affected) {
|
||||
Card hostCard = effect.getHostCard();
|
||||
if (hostCard.hasAlternateState()) {
|
||||
hostCard = game.getCardState(hostCard);
|
||||
}
|
||||
|
||||
public final boolean aiShouldRun(final CardTraitBase effect, final SpellAbility sa, final Card host, final GameEntity affected) {
|
||||
if (effect.hasParam("AILogic") && effect.getParam("AILogic").equalsIgnoreCase("ProtectFriendly")) {
|
||||
final Player controller = hostCard.getController();
|
||||
final Player controller = host.getController();
|
||||
if (affected instanceof Player) {
|
||||
return !((Player) affected).isOpponentOf(controller);
|
||||
}
|
||||
@@ -1833,7 +1809,6 @@ public class AiController {
|
||||
}
|
||||
}
|
||||
if (effect.hasParam("AICheckSVar")) {
|
||||
System.out.println("aiShouldRun?" + sa);
|
||||
final String svarToCheck = effect.getParam("AICheckSVar");
|
||||
String comparator = "GE";
|
||||
int compareTo = 1;
|
||||
@@ -1846,9 +1821,9 @@ public class AiController {
|
||||
compareTo = Integer.parseInt(strCmpTo);
|
||||
} catch (final Exception ignored) {
|
||||
if (sa == null) {
|
||||
compareTo = AbilityUtils.calculateAmount(hostCard, hostCard.getSVar(strCmpTo), effect);
|
||||
compareTo = AbilityUtils.calculateAmount(host, host.getSVar(strCmpTo), effect);
|
||||
} else {
|
||||
compareTo = AbilityUtils.calculateAmount(hostCard, hostCard.getSVar(strCmpTo), sa);
|
||||
compareTo = AbilityUtils.calculateAmount(host, host.getSVar(strCmpTo), sa);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1856,13 +1831,12 @@ public class AiController {
|
||||
int left = 0;
|
||||
|
||||
if (sa == null) {
|
||||
left = AbilityUtils.calculateAmount(hostCard, svarToCheck, effect);
|
||||
left = AbilityUtils.calculateAmount(host, svarToCheck, effect);
|
||||
} else {
|
||||
left = AbilityUtils.calculateAmount(hostCard, svarToCheck, sa);
|
||||
left = AbilityUtils.calculateAmount(host, svarToCheck, sa);
|
||||
}
|
||||
System.out.println("aiShouldRun?" + left + comparator + compareTo);
|
||||
return Expressions.compare(left, comparator, compareTo);
|
||||
} else if (effect.hasParam("AICheckDredge")) {
|
||||
} else if (effect.isKeyword(Keyword.DREDGE)) {
|
||||
return player.getCardsIn(ZoneType.Library).size() > 8 || player.isCardInPlay("Laboratory Maniac");
|
||||
} else return sa != null && doTrigger(sa, false);
|
||||
}
|
||||
|
||||
@@ -460,7 +460,11 @@ public class PlayerControllerAi extends PlayerController {
|
||||
|
||||
@Override
|
||||
public boolean confirmReplacementEffect(ReplacementEffect replacementEffect, SpellAbility effectSA, GameEntity affected, String question) {
|
||||
return brains.aiShouldRun(replacementEffect, effectSA, affected);
|
||||
Card host = replacementEffect.getHostCard();
|
||||
if (host.hasAlternateState()) {
|
||||
host = host.getGame().getCardState(host);
|
||||
}
|
||||
return brains.aiShouldRun(replacementEffect, effectSA, host, affected);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -96,6 +96,10 @@ public class CloneAi extends SpellAbilityAi {
|
||||
if (sa.usesTargeting()) {
|
||||
chance = cloneTgtAI(sa);
|
||||
} else {
|
||||
if (sa.isReplacementAbility() && host.isCloned()) {
|
||||
// prevent StackOverflow from infinite loop copying another ETB RE
|
||||
return new AiAbilityDecision(0, AiPlayDecision.StopRunawayActivations);
|
||||
}
|
||||
if (sa.hasParam("Choices")) {
|
||||
CardCollectionView choices = CardLists.getValidCards(host.getGame().getCardsIn(ZoneType.Battlefield),
|
||||
sa.getParam("Choices"), host.getController(), host, sa);
|
||||
|
||||
@@ -92,9 +92,8 @@ public class CountersPutAi extends CountersAi {
|
||||
return false;
|
||||
}
|
||||
return chance > MyRandom.getRandom().nextFloat();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sa.isKeyword(Keyword.LEVEL_UP)) {
|
||||
@@ -124,7 +123,6 @@ public class CountersPutAi extends CountersAi {
|
||||
final Cost abCost = sa.getPayCosts();
|
||||
final Card source = sa.getHostCard();
|
||||
final String sourceName = ComputerUtilAbility.getAbilitySourceName(sa);
|
||||
CardCollection list;
|
||||
Card choice = null;
|
||||
final String amountStr = sa.getParamOrDefault("CounterNum", "1");
|
||||
final boolean divided = sa.isDividedAsYouChoose();
|
||||
@@ -292,10 +290,8 @@ public class CountersPutAi extends CountersAi {
|
||||
|
||||
if (willActivate) {
|
||||
return new AiAbilityDecision(100, AiPlayDecision.WillPlay);
|
||||
} else {
|
||||
return new AiAbilityDecision(0, AiPlayDecision.CantPlayAi);
|
||||
}
|
||||
|
||||
return new AiAbilityDecision(0, AiPlayDecision.CantPlayAi);
|
||||
} else if (logic.equals("ChargeToBestCMC")) {
|
||||
return doChargeToCMCLogic(ai, sa);
|
||||
} else if (logic.equals("ChargeToBestOppControlledCMC")) {
|
||||
@@ -348,7 +344,7 @@ public class CountersPutAi extends CountersAi {
|
||||
if (type.equals("P1P1")) {
|
||||
nPump = amount;
|
||||
}
|
||||
return FightAi.canFightAi(ai, sa, nPump, nPump);
|
||||
return FightAi.canFight(ai, sa, nPump, nPump);
|
||||
}
|
||||
|
||||
if (amountStr.equals("X")) {
|
||||
@@ -451,6 +447,7 @@ public class CountersPutAi extends CountersAi {
|
||||
|
||||
sa.resetTargets();
|
||||
|
||||
CardCollection list;
|
||||
if (sa.isCurse()) {
|
||||
list = ai.getOpponents().getCardsIn(ZoneType.Battlefield);
|
||||
} else {
|
||||
@@ -746,7 +743,7 @@ public class CountersPutAi extends CountersAi {
|
||||
protected AiAbilityDecision doTriggerNoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
||||
final SpellAbility root = sa.getRootAbility();
|
||||
final Card source = sa.getHostCard();
|
||||
final String aiLogic = sa.getParamOrDefault("AILogic", "");
|
||||
final String aiLogic = sa.getParam("AILogic");
|
||||
final String amountStr = sa.getParamOrDefault("CounterNum", "1");
|
||||
final boolean divided = sa.isDividedAsYouChoose();
|
||||
final int amount = AbilityUtils.calculateAmount(source, amountStr, sa);
|
||||
@@ -765,14 +762,10 @@ public class CountersPutAi extends CountersAi {
|
||||
}
|
||||
|
||||
if ("ChargeToBestCMC".equals(aiLogic)) {
|
||||
AiAbilityDecision decision = doChargeToCMCLogic(ai, sa);
|
||||
if (decision.willingToPlay()) {
|
||||
return decision;
|
||||
}
|
||||
if (mandatory) {
|
||||
return new AiAbilityDecision(50, AiPlayDecision.MandatoryPlay);
|
||||
}
|
||||
return new AiAbilityDecision(0, AiPlayDecision.CantPlayAi);
|
||||
return doChargeToCMCLogic(ai, sa);
|
||||
}
|
||||
|
||||
if (!sa.usesTargeting()) {
|
||||
@@ -796,7 +789,6 @@ public class CountersPutAi extends CountersAi {
|
||||
// things like Powder Keg, which are way too complex for the AI
|
||||
}
|
||||
} else if (sa.getTargetRestrictions().canOnlyTgtOpponent() && !sa.getTargetRestrictions().canTgtCreature()) {
|
||||
// can only target opponent
|
||||
PlayerCollection playerList = new PlayerCollection(IterableUtil.filter(
|
||||
sa.getTargetRestrictions().getAllCandidates(sa, true, true), Player.class));
|
||||
|
||||
@@ -811,13 +803,12 @@ public class CountersPutAi extends CountersAi {
|
||||
sa.getTargets().add(choice);
|
||||
}
|
||||
} else {
|
||||
String logic = sa.getParam("AILogic");
|
||||
if ("Fight".equals(logic) || "PowerDmg".equals(logic)) {
|
||||
if ("Fight".equals(aiLogic) || "PowerDmg".equals(aiLogic)) {
|
||||
int nPump = 0;
|
||||
if (type.equals("P1P1")) {
|
||||
nPump = amount;
|
||||
}
|
||||
AiAbilityDecision decision = FightAi.canFightAi(ai, sa, nPump, nPump);
|
||||
AiAbilityDecision decision = FightAi.canFight(ai, sa, nPump, nPump);
|
||||
if (decision.willingToPlay()) {
|
||||
return decision;
|
||||
}
|
||||
@@ -838,7 +829,6 @@ public class CountersPutAi extends CountersAi {
|
||||
|
||||
while (sa.canAddMoreTarget()) {
|
||||
if (mandatory) {
|
||||
// When things are mandatory, gotta handle a little differently
|
||||
if ((list.isEmpty() || !preferred) && sa.isTargetNumberValid()) {
|
||||
return new AiAbilityDecision(0, AiPlayDecision.TargetingFailed);
|
||||
}
|
||||
@@ -863,7 +853,7 @@ public class CountersPutAi extends CountersAi {
|
||||
return new AiAbilityDecision(sa.isTargetNumberValid() ? 100 : 0, sa.isTargetNumberValid() ? AiPlayDecision.WillPlay : AiPlayDecision.CantPlayAi);
|
||||
}
|
||||
|
||||
Card choice = null;
|
||||
Card choice;
|
||||
|
||||
// Choose targets here:
|
||||
if (sa.isCurse()) {
|
||||
@@ -889,10 +879,10 @@ public class CountersPutAi extends CountersAi {
|
||||
choice = Aggregates.random(list);
|
||||
}
|
||||
if (choice != null && divided) {
|
||||
int alloc = Math.max(amount / totalTargets, 1);
|
||||
if (sa.getTargets().size() == Math.min(totalTargets, sa.getMaxTargets()) - 1) {
|
||||
sa.addDividedAllocation(choice, left);
|
||||
} else {
|
||||
int alloc = Math.max(amount / totalTargets, 1);
|
||||
sa.addDividedAllocation(choice, alloc);
|
||||
left -= alloc;
|
||||
}
|
||||
@@ -982,9 +972,7 @@ public class CountersPutAi extends CountersAi {
|
||||
final String amountStr = sa.getParamOrDefault("CounterNum", "1");
|
||||
final int amount = AbilityUtils.calculateAmount(sa.getHostCard(), amountStr, sa);
|
||||
|
||||
final boolean isCurse = sa.isCurse();
|
||||
|
||||
if (isCurse) {
|
||||
if (sa.isCurse()) {
|
||||
final CardCollection opponents = CardLists.filterControlledBy(options, ai.getOpponents());
|
||||
|
||||
if (!opponents.isEmpty()) {
|
||||
@@ -1210,9 +1198,8 @@ public class CountersPutAi extends CountersAi {
|
||||
}
|
||||
if (numCtrs < optimalCMC) {
|
||||
return new AiAbilityDecision(100, AiPlayDecision.WillPlay);
|
||||
} else {
|
||||
return new AiAbilityDecision(0, AiPlayDecision.CantPlayAi);
|
||||
}
|
||||
return new AiAbilityDecision(0, AiPlayDecision.CantPlayAi);
|
||||
}
|
||||
|
||||
private AiAbilityDecision doChargeToOppCtrlCMCLogic(Player ai, SpellAbility sa) {
|
||||
|
||||
@@ -270,7 +270,7 @@ public class EffectAi extends SpellAbilityAi {
|
||||
}
|
||||
return new AiAbilityDecision(0, AiPlayDecision.CantPlayAi);
|
||||
} else if (logic.equals("Fight")) {
|
||||
return FightAi.canFightAi(ai, sa, 0,0);
|
||||
return FightAi.canFight(ai, sa, 0,0);
|
||||
} else if (logic.equals("Pump")) {
|
||||
sa.resetTargets();
|
||||
List<Card> options = CardUtil.getValidCardsToTarget(sa);
|
||||
|
||||
@@ -177,7 +177,7 @@ public class FightAi extends SpellAbilityAi {
|
||||
* @param power bonus to power
|
||||
* @return true if fight effect should be played, false otherwise
|
||||
*/
|
||||
public static AiAbilityDecision canFightAi(final Player ai, final SpellAbility sa, int power, int toughness) {
|
||||
public static AiAbilityDecision canFight(final Player ai, final SpellAbility sa, int power, int toughness) {
|
||||
final Card source = sa.getHostCard();
|
||||
final String sourceName = ComputerUtilAbility.getAbilitySourceName(sa);
|
||||
AbilitySub tgtFight = sa.getSubAbility();
|
||||
|
||||
@@ -453,7 +453,7 @@ public class PumpAi extends PumpAiBase {
|
||||
}
|
||||
|
||||
if (isFight) {
|
||||
return FightAi.canFightAi(ai, sa, attack, defense).willingToPlay();
|
||||
return FightAi.canFight(ai, sa, attack, defense).willingToPlay();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -269,22 +269,22 @@ public class EffectEffect extends SpellAbilityEffect {
|
||||
}
|
||||
}
|
||||
|
||||
// Set Chosen Color(s)
|
||||
if (hostCard.hasChosenColor()) {
|
||||
eff.setChosenColors(Lists.newArrayList(hostCard.getChosenColors()));
|
||||
}
|
||||
|
||||
// Set Chosen Cards
|
||||
if (hostCard.hasChosenCard()) {
|
||||
eff.setChosenCards(hostCard.getChosenCards());
|
||||
}
|
||||
|
||||
// Set Chosen Player
|
||||
if (hostCard.hasChosenPlayer()) {
|
||||
eff.setChosenPlayer(hostCard.getChosenPlayer());
|
||||
}
|
||||
|
||||
// Set Chosen Type
|
||||
if (hostCard.getChosenDirection() != null) {
|
||||
eff.setChosenDirection(hostCard.getChosenDirection());
|
||||
}
|
||||
|
||||
if (hostCard.hasChosenType()) {
|
||||
eff.setChosenType(hostCard.getChosenType());
|
||||
}
|
||||
@@ -292,12 +292,10 @@ public class EffectEffect extends SpellAbilityEffect {
|
||||
eff.setChosenType2(hostCard.getChosenType2());
|
||||
}
|
||||
|
||||
// Set Chosen name
|
||||
if (hostCard.hasNamedCard()) {
|
||||
eff.setNamedCards(Lists.newArrayList(hostCard.getNamedCards()));
|
||||
}
|
||||
|
||||
// chosen number
|
||||
if (sa.hasParam("SetChosenNumber")) {
|
||||
eff.setChosenNumber(AbilityUtils.calculateAmount(hostCard, sa.getParam("SetChosenNumber"), sa));
|
||||
} else if (hostCard.hasChosenNumber()) {
|
||||
|
||||
@@ -2238,7 +2238,7 @@ public class CardFactoryUtil {
|
||||
final String actualRep = "Event$ Draw | ActiveZones$ Graveyard | ValidPlayer$ You | "
|
||||
+ "Secondary$ True | Optional$ True | CheckSVar$ "
|
||||
+ "DredgeCheckLib | SVarCompare$ GE" + dredgeAmount
|
||||
+ " | AICheckDredge$ True | Description$ CARDNAME - Dredge " + dredgeAmount;
|
||||
+ " | Description$ CARDNAME - Dredge " + dredgeAmount;
|
||||
|
||||
final String abString = "DB$ Mill | Defined$ You | NumCards$ " + dredgeAmount;
|
||||
|
||||
|
||||
@@ -3,11 +3,9 @@ ManaCost:1 W
|
||||
Types:Instant
|
||||
A:SP$ ChooseSource | Choices$ Card,Emblem | AILogic$ NeedsPrevention | SubAbility$ DBEffect | StackDescription$ SpellDescription | SpellDescription$ Prevent all damage that would be dealt to you this turn by a source of your choice. Whenever damage from a black or red source is prevented this way this turn, you gain that much life.
|
||||
SVar:DBEffect:DB$ Effect | ReplacementEffects$ RepDmg | ConditionDefined$ ChosenCard | ConditionPresent$ Card,Emblem | ConditionCompare$ GE1
|
||||
SVar:RepDmg:Event$ DamageDone | ValidTarget$ You | ValidSource$ Card.ChosenCardStrict,Emblem.ChosenCard | ReplaceWith$ DBStoreSVar | PreventionEffect$ True | Description$ Prevent all damage that would be dealt to you this turn by a source of your choice. Whenever damage from a black or red source is prevented this way this turn, you gain that much life.
|
||||
SVar:DBStoreSVar:DB$ StoreSVar | SVar$ Z | Type$ Calculate | Expression$ X | SubAbility$ DBTrigger
|
||||
SVar:RepDmg:Event$ DamageDone | ValidTarget$ You | ValidSource$ Card.ChosenCardStrict,Emblem.ChosenCard | ReplaceWith$ DBTrigger | PreventionEffect$ True | Description$ Prevent all damage that would be dealt to you this turn by a source of your choice. Whenever damage from a black or red source is prevented this way this turn, you gain that much life.
|
||||
SVar:DBTrigger:DB$ ImmediateTrigger | Execute$ GainLifeYou | ConditionCheckSVar$ Y | ConditionSVarCompare$ GE1 | TriggerDescription$ Whenever damage from a black or red source is prevented this way this turn, you gain that much life.
|
||||
SVar:GainLifeYou:DB$ GainLife | Defined$ You | LifeAmount$ Z
|
||||
SVar:X:ReplaceCount$DamageAmount
|
||||
SVar:GainLifeYou:DB$ GainLife | Defined$ You | LifeAmount$ X
|
||||
SVar:X:Spawner>ReplaceCount$DamageAmount
|
||||
SVar:Y:ReplacedSource$Valid Card.BlackSource,Card.RedSource
|
||||
SVar:Z:Number$0
|
||||
Oracle:Prevent all damage that would be dealt to you this turn by a source of your choice. Whenever damage from a black or red source is prevented this way this turn, you gain that much life.
|
||||
|
||||
@@ -4,7 +4,8 @@ Types:Legendary Creature God Archer
|
||||
PT:2/4
|
||||
K:Reach
|
||||
K:Partner - Father & Son
|
||||
A:AB$ Draw | Cost$ 3 T | NumCards$ X | SubAbility$ DBDamage | SpellDescription$ Draw a card for each experience counter you have, then discard a card.
|
||||
A:AB$ Draw | Cost$ 3 T | NumCards$ X | SubAbility$ DBDiscard | SpellDescription$ Draw a card for each experience counter you have, then discard a card.
|
||||
SVar:DBDiscard:DB$ Discard | Mode$ YouChoose | SubAbility$ DBDamage
|
||||
SVar:DBDamage:DB$ DealDamage | NumDmg$ 2 | Defined$ Opponent | SpellDescription$ CARDNAME deals 2 damage to each opponent.
|
||||
SVar:X:Count$YourCountersExperience
|
||||
Oracle:Reach\n{3}, {T}: Draw a card for each experience counter you have, then discard a card. Atreus, Impulsive Son deals 2 damage to each opponent.\nPartner-Father & son
|
||||
|
||||
Reference in New Issue
Block a user