GameActionUtil: store OptionalKeywordAmount in CastSA (#5853)

* GameActionUtil: store OptionalKeywordAmount in CastSA

* ~ rename and make into a Table
This commit is contained in:
Hans Mackowiak
2024-08-08 22:16:17 +02:00
committed by GitHub
parent 021ac6c155
commit a800bf681c
5 changed files with 97 additions and 114 deletions

View File

@@ -17,7 +17,6 @@
*/
package forge.game;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import forge.card.MagicColor;
@@ -41,7 +40,6 @@ import forge.game.spellability.*;
import forge.game.staticability.StaticAbility;
import forge.game.staticability.StaticAbilityAlternativeCost;
import forge.game.staticability.StaticAbilityLayer;
import forge.game.trigger.Trigger;
import forge.game.zone.Zone;
import forge.game.zone.ZoneType;
import forge.util.Aggregates;
@@ -572,112 +570,87 @@ public final class GameActionUtil {
for (KeywordInterface ki : host.getKeywords()) {
final String o = ki.getOriginal();
if (o.startsWith("Casualty")) {
Trigger tr = Iterables.getFirst(ki.getTriggers(), null);
if (tr != null) {
String n = o.split(":")[1];
if (host.wasCast() && n.equals("X")) {
CardCollectionView creatures = activator.getCreaturesInPlay();
int max = Aggregates.max(creatures, Card::getNetPower);
n = Integer.toString(pc.chooseNumber(sa, "Choose X for Casualty", 0, max));
}
final String casualtyCost = "Sac<1/Creature.powerGE" + n + "/creature with power " + n +
" or greater>";
final Cost cost = new Cost(casualtyCost, false);
String str = "Pay for Casualty? " + cost.toSimpleString();
boolean v = pc.addKeywordCost(sa, cost, ki, str);
String n = o.split(":")[1];
if (host.wasCast() && n.equals("X")) {
CardCollectionView creatures = activator.getCreaturesInPlay();
int max = Aggregates.max(creatures, Card::getNetPower);
n = Integer.toString(pc.chooseNumber(sa, "Choose X for Casualty", 0, max));
}
final String casualtyCost = "Sac<1/Creature.powerGE" + n + "/creature with power " + n +
" or greater>";
final Cost cost = new Cost(casualtyCost, false);
String str = "Pay for Casualty? " + cost.toSimpleString();
boolean v = pc.addKeywordCost(sa, cost, ki, str);
tr.setSVar("CasualtyPaid", v ? "1" : "0");
tr.getOverridingAbility().setSVar("CasualtyPaid", v ? "1" : "0");
tr.setSVar("Casualty", v ? n : "0");
tr.getOverridingAbility().setSVar("Casualty", v ? n : "0");
if (v) {
if (result == null) {
result = sa.copy();
}
result.getPayCosts().add(cost);
reset = true;
if (v) {
if (result == null) {
result = sa.copy();
}
result.getPayCosts().add(cost);
reset = true;
result.setOptionalKeywordAmount(ki, Integer.valueOf(n));
}
} else if (o.equals("Conspire")) {
Trigger tr = Iterables.getFirst(ki.getTriggers(), null);
if (tr != null) {
final String conspireCost = "tapXType<2/Creature.SharesColorWith/" +
"creature that shares a color with " + host.getName() + ">";
final Cost cost = new Cost(conspireCost, false);
String str = "Pay for Conspire? " + cost.toSimpleString();
final String conspireCost = "tapXType<2/Creature.SharesColorWith/" +
"creature that shares a color with " + host.getName() + ">";
final Cost cost = new Cost(conspireCost, false);
String str = "Pay for Conspire? " + cost.toSimpleString();
boolean v = pc.addKeywordCost(sa, cost, ki, str);
tr.setSVar("Conspire", v ? "1" : "0");
if (v) {
if (result == null) {
result = sa.copy();
}
result.getPayCosts().add(cost);
reset = true;
if (pc.addKeywordCost(sa, cost, ki, str)) {
if (result == null) {
result = sa.copy();
}
result.getPayCosts().add(cost);
result.setOptionalKeywordAmount(ki, 1);
reset = true;
}
} else if (o.startsWith("Offspring")) {
String[] k = o.split(":");
final Cost cost = new Cost(k[1], false);
Trigger tr = Iterables.getFirst(ki.getTriggers(), null);
if (tr != null) {
String str = "Pay for Offspring? " + cost.toSimpleString();
String str = "Pay for Offspring? " + cost.toSimpleString();
boolean v = pc.addKeywordCost(sa, cost, ki, str);
tr.setSVar("Offspring", v ? "1" : "0");
boolean v = pc.addKeywordCost(sa, cost, ki, str);
if (v) {
if (result == null) {
result = sa.copy();
}
result.getPayCosts().add(cost);
reset = true;
if (v) {
if (result == null) {
result = sa.copy();
}
result.getPayCosts().add(cost);
reset = true;
result.setOptionalKeywordAmount(ki, 1);
}
} else if (o.startsWith("Replicate")) {
Trigger tr = Iterables.getFirst(ki.getTriggers(), null);
if (tr != null) {
String costStr = o.split(":")[1];
final Cost cost = new Cost(costStr, false);
String costStr = o.split(":")[1];
final Cost cost = new Cost(costStr, false);
String str = "Choose Amount for Replicate: " + cost.toSimpleString();
String str = "Choose Amount for Replicate: " + cost.toSimpleString();
int v = pc.chooseNumberForKeywordCost(sa, cost, ki, str, Integer.MAX_VALUE);
int v = pc.chooseNumberForKeywordCost(sa, cost, ki, str, Integer.MAX_VALUE);
tr.setSVar("ReplicateAmount", String.valueOf(v));
tr.getOverridingAbility().setSVar("ReplicateAmount", String.valueOf(v));
for (int i = 0; i < v; i++) {
if (result == null) {
result = sa.copy();
}
result.getPayCosts().add(cost);
reset = true;
for (int i = 0; i < v; i++) {
if (result == null) {
result = sa.copy();
}
result.getPayCosts().add(cost);
reset = true;
}
result.setOptionalKeywordAmount(ki, v);
} else if (o.startsWith("Squad")) {
Trigger tr = Iterables.getFirst(ki.getTriggers(), null);
if (tr != null) {
String costStr = o.split(":")[1];
final Cost cost = new Cost(costStr, false);
String costStr = o.split(":")[1];
final Cost cost = new Cost(costStr, false);
String str = "Choose amount for Squad: " + cost.toSimpleString();
String str = "Choose amount for Squad: " + cost.toSimpleString();
int v = pc.chooseNumberForKeywordCost(sa, cost, ki, str, Integer.MAX_VALUE);
int v = pc.chooseNumberForKeywordCost(sa, cost, ki, str, Integer.MAX_VALUE);
tr.setSVar("SquadAmount", String.valueOf(v));
tr.getOverridingAbility().setSVar("SquadAmount", String.valueOf(v));
for (int i = 0; i < v; i++) {
if (result == null) {
result = sa.copy();
}
result.getPayCosts().add(cost);
reset = true;
for (int i = 0; i < v; i++) {
if (result == null) {
result = sa.copy();
}
result.getPayCosts().add(cost);
reset = true;
}
result.setOptionalKeywordAmount(ki, v);
}
}

View File

@@ -1893,6 +1893,13 @@ public class AbilityUtils {
}
return doXMath(v, expr, c, ctb);
}
if (sq[0].equals("hasOptionalKeywordAmount")) {
return doXMath(c.getCastSA() != null && c.getCastSA().hasOptionalKeywordAmount(ctb.getKeyword()) ? 1 : 0, expr, c, ctb);
}
if (sq[0].equals("OptionalKeywordAmount")) {
return doXMath(c.getCastSA() != null ? c.getCastSA().getOptionalKeywordAmount(ctb.getKeyword()) : 0, expr, c, ctb);
}
// Count$DevotionDual.<color name>.<color name>
// Count$Devotion.<color name>

View File

@@ -907,13 +907,16 @@ public class CardFactoryUtil {
String abString = "DB$ CopySpellAbility | Defined$ TriggeredSpellAbility | MayChooseTarget$ True";
String[] k = keyword.split(":");
if (k.length > 2) {
abString = abString + " | " + k[2];
abString += " | " + k[2];
}
final Trigger casualtyTrigger = TriggerHandler.parseTrigger(trigScript, card, intrinsic);
casualtyTrigger.setOverridingAbility(AbilityFactory.getAbility(abString, card));
casualtyTrigger.setSVar("Casualty", "0");
casualtyTrigger.setSVar("CasualtyPaid", "0");
SpellAbility sa = AbilityFactory.getAbility(abString, card);
sa.setSVar("CasualtyPaid", "Count$hasOptionalKeywordAmount");
sa.setSVar("Casualty", "Count$OptionalKeywordAmount");
casualtyTrigger.setOverridingAbility(sa);
casualtyTrigger.setSVar("CasualtyPaid", "Count$hasOptionalKeywordAmount");
casualtyTrigger.setSVar("Casualty", "Count$OptionalKeywordAmount");
inst.addTrigger(casualtyTrigger);
} else if (keyword.startsWith("Chapter")) {
@@ -960,7 +963,7 @@ public class CardFactoryUtil {
final Trigger conspireTrigger = TriggerHandler.parseTrigger(trigScript, card, intrinsic);
conspireTrigger.setOverridingAbility(AbilityFactory.getAbility(abString, card));
conspireTrigger.setSVar("Conspire", "0");
conspireTrigger.setSVar("Conspire", "Count$OptionalKeywordAmount");
inst.addTrigger(conspireTrigger);
} else if (keyword.startsWith("Cumulative upkeep")) {
@@ -1585,14 +1588,14 @@ public class CardFactoryUtil {
costDesc = "" + costDesc;
}
final String trigStr = "Mode$ ChangesZone | Destination$ Battlefield | ValidCard$ Card.Self+linkedCastTrigger | CheckSVar$ Offspring | Secondary$ True " +
final String trigStr = "Mode$ ChangesZone | Destination$ Battlefield | ValidCard$ Card.Self | CheckSVar$ Offspring | Secondary$ True " +
"| TriggerDescription$ Offspring " + costDesc + " (" + inst.getReminderText() + ")";
final String effect = "DB$ CopyPermanent | Defined$ TriggeredCardLKICopy | NumCopies$ 1 | SetPower$ 1 | SetToughness$ 1";
final Trigger trigger = TriggerHandler.parseTrigger(trigStr, card, intrinsic);
trigger.setOverridingAbility(AbilityFactory.getAbility(effect, card));
trigger.setSVar("Offspring", "0");
trigger.setSVar("Offspring", "Count$OptionalKeywordAmount");
inst.addTrigger(trigger);
} else if (keyword.startsWith("Partner:")) {
@@ -1743,9 +1746,9 @@ public class CardFactoryUtil {
final Trigger replicateTrigger = TriggerHandler.parseTrigger(trigScript, card, intrinsic);
final SpellAbility replicateAbility = AbilityFactory.getAbility(abString, card);
replicateAbility.setSVar("ReplicateAmount", "0");
replicateAbility.setSVar("ReplicateAmount", "Count$OptionalKeywordAmount");
replicateTrigger.setOverridingAbility(replicateAbility);
replicateTrigger.setSVar("ReplicateAmount", "0");
replicateTrigger.setSVar("ReplicateAmount", "Count$OptionalKeywordAmount");
inst.addTrigger(replicateTrigger);
} else if (keyword.startsWith("Ripple")) {
final String[] k = keyword.split(":");
@@ -1825,9 +1828,9 @@ public class CardFactoryUtil {
final Trigger squadTrigger = TriggerHandler.parseTrigger(trigScript, card, intrinsic);
final SpellAbility squadAbility = AbilityFactory.getAbility(abString, card);
squadAbility.setSVar("SquadAmount", "0");
squadAbility.setSVar("SquadAmount", "Count$OptionalKeywordAmount");
squadTrigger.setOverridingAbility(squadAbility);
squadTrigger.setSVar("SquadAmount", "0");
squadTrigger.setSVar("SquadAmount", "Count$OptionalKeywordAmount");
inst.addTrigger(squadTrigger);
} else if (keyword.equals("Storm")) {
final String actualTrigger = "Mode$ SpellCast | ValidCard$ Card.Self | TriggerZones$ Stack | Secondary$ True"

View File

@@ -20,7 +20,6 @@ import forge.game.combat.AttackRequirement;
import forge.game.combat.AttackingBand;
import forge.game.combat.Combat;
import forge.game.combat.CombatUtil;
import forge.game.keyword.KeywordInterface;
import forge.game.mana.Mana;
import forge.game.player.Player;
import forge.game.spellability.OptionalCost;
@@ -1801,25 +1800,6 @@ public class CardProperty {
if (AbilityUtils.isUnlinkedFromCastSA(spellAbility, card)) {
return false;
}
} else if (property.equals("linkedCastTrigger")) {
if (card.getCastSA() == null) {
return false;
}
List<Card> spellCast = game.getStack().getSpellsCastThisTurn();
int idx = spellCast.lastIndexOf(source);
if (idx == -1) {
return false;
}
boolean found = false;
for (KeywordInterface kw: spellCast.get(idx).getUnhiddenKeywords()) {
if (!Collections.disjoint(kw.getTriggers(), spellAbility.getKeyword().getTriggers())) {
found = true;
break;
}
}
if (!found) {
return false;
}
} else if (property.startsWith("kicked")) {
// CR 607.2i check cost is linked
if (AbilityUtils.isUnlinkedFromCastSA(spellAbility, card)) {

View File

@@ -127,6 +127,8 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
private List<Object> triggerRemembered = Lists.newArrayList();
private AlternativeCost altCost = null;
private EnumSet<OptionalCost> optionalCosts = EnumSet.noneOf(OptionalCost.class);
private Table<Keyword, Pair<Long, Long>, Integer> optionalKeywordAmount = HashBasedTable.create();
private boolean aftermath = false;
@@ -166,7 +168,6 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
private boolean isCastFromPlayEffect = false;
private EnumSet<OptionalCost> optionalCosts = EnumSet.noneOf(OptionalCost.class);
private TargetRestrictions targetRestrictions;
private TargetChoices targetChosen = new TargetChoices();
@@ -1196,6 +1197,8 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
clone.manaPart = new AbilityManaPart(clone, mapParams);
}
clone.optionalKeywordAmount = HashBasedTable.create(optionalKeywordAmount);
// need to copy the damage tables
if (damageMap != null) {
clone.damageMap = new CardDamageMap(damageMap);
@@ -2594,4 +2597,21 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
public boolean isCounterableBy(SpellAbility sa) {
return true;
}
public boolean hasOptionalKeywordAmount(KeywordInterface kw) {
return this.optionalKeywordAmount.contains(kw.getKeyword(), Pair.of(kw.getIdx(), kw.getStaticId()));
}
public boolean hasOptionalKeywordAmount(Keyword kw) {
return this.optionalKeywordAmount.containsRow(kw);
}
public Set<Keyword> getOptionalKeywords() {
return this.optionalKeywordAmount.rowKeySet();
}
public int getOptionalKeywordAmount(KeywordInterface kw) {
return ObjectUtils.firstNonNull(this.optionalKeywordAmount.get(kw.getKeyword(), Pair.of(kw.getIdx(), kw.getStaticId())), 0);
}
public void setOptionalKeywordAmount(KeywordInterface kw, int amount) {
this.optionalKeywordAmount.put(kw.getKeyword(), Pair.of(kw.getIdx(), kw.getStaticId()), amount);
}
}