mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-16 10:48:00 +00:00
@@ -782,15 +782,9 @@ public class AiController {
|
|||||||
return AiPlayDecision.CantAfford;
|
return AiPlayDecision.CantAfford;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (wardCost.hasSpecificCostType(CostPayLife.class)) {
|
SpellAbilityAi topAI = new SpellAbilityAi() {};
|
||||||
int lifeToPay = wardCost.getCostPartByType(CostPayLife.class).convertAmount();
|
if (!topAI.willPayCosts(player, sa , wardCost, host)) {
|
||||||
if (lifeToPay > player.getLife() || (lifeToPay == player.getLife() && !player.cantLoseForZeroOrLessLife())) {
|
return AiPlayDecision.CostNotAcceptable;
|
||||||
return AiPlayDecision.CantAfford;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (wardCost.hasSpecificCostType(CostDiscard.class)
|
|
||||||
&& wardCost.getCostPartByType(CostDiscard.class).convertAmount() > player.getCardsIn(ZoneType.Hand).size()) {
|
|
||||||
return AiPlayDecision.CantAfford;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -620,6 +620,9 @@ public abstract class GameState {
|
|||||||
|
|
||||||
game.getAction().checkStateEffects(true); //ensure state based effects and triggers are updated
|
game.getAction().checkStateEffects(true); //ensure state based effects and triggers are updated
|
||||||
|
|
||||||
|
// prevent interactions with objects from old state
|
||||||
|
game.copyLastState();
|
||||||
|
|
||||||
// Set negative or zero life after state effects if need be, important for some puzzles that rely on
|
// Set negative or zero life after state effects if need be, important for some puzzles that rely on
|
||||||
// pre-setting negative life (e.g. PS_NEO4).
|
// pre-setting negative life (e.g. PS_NEO4).
|
||||||
for (int i = 0; i < playerStates.size(); i++) {
|
for (int i = 0; i < playerStates.size(); i++) {
|
||||||
|
|||||||
@@ -64,16 +64,13 @@ public class SacrificeEffect extends SpellAbilityEffect {
|
|||||||
|
|
||||||
table.replaceCounterEffect(game, sa, true);
|
table.replaceCounterEffect(game, sa, true);
|
||||||
|
|
||||||
Cost cumCost = new Cost(sa.getParam("CumulativeUpkeep"), true);
|
|
||||||
Cost payCost = new Cost(ManaCost.ZERO, true);
|
Cost payCost = new Cost(ManaCost.ZERO, true);
|
||||||
int n = card.getCounters(CounterEnumType.AGE);
|
int n = card.getCounters(CounterEnumType.AGE);
|
||||||
|
if (n > 0) {
|
||||||
// multiply cost
|
Cost cumCost = new Cost(sa.getParam("CumulativeUpkeep"), true);
|
||||||
for (int i = 0; i < n; ++i) {
|
payCost.mergeTo(cumCost, n);
|
||||||
payCost.add(cumCost);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sa.setCumulativeupkeep(true);
|
|
||||||
game.updateLastStateForCard(card);
|
game.updateLastStateForCard(card);
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|||||||
@@ -883,7 +883,24 @@ public class Cost implements Serializable {
|
|||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void mergeTo(Cost source, int amt) {
|
||||||
|
// multiply to create the full cost
|
||||||
|
if (amt > 1) {
|
||||||
|
// to double itself we need to work on a copy
|
||||||
|
Cost sourceCpy = source.copy();
|
||||||
|
for (int i = 1; i < amt; ++i) {
|
||||||
|
// in theory setAmount could be used instead but it depends on the cost complexity (probably not worth trying to determine that first)
|
||||||
|
source.add(sourceCpy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// combine costs (these shouldn't mix together)
|
||||||
|
this.add(source, false);
|
||||||
|
}
|
||||||
|
|
||||||
public Cost add(Cost cost1) {
|
public Cost add(Cost cost1) {
|
||||||
|
return add(cost1, true);
|
||||||
|
}
|
||||||
|
public Cost add(Cost cost1, boolean mergeAdditional) {
|
||||||
CostPartMana costPart2 = this.getCostMana();
|
CostPartMana costPart2 = this.getCostMana();
|
||||||
List<CostPart> toRemove = Lists.newArrayList();
|
List<CostPart> toRemove = Lists.newArrayList();
|
||||||
for (final CostPart part : cost1.getCostParts()) {
|
for (final CostPart part : cost1.getCostParts()) {
|
||||||
@@ -906,10 +923,10 @@ public class Cost implements Serializable {
|
|||||||
} else {
|
} else {
|
||||||
costParts.add(0, new CostPartMana(oldManaCost.toManaCost(), r));
|
costParts.add(0, new CostPartMana(oldManaCost.toManaCost(), r));
|
||||||
}
|
}
|
||||||
} else if (part instanceof CostDiscard || part instanceof CostDraw
|
} else if (part instanceof CostPutCounter || (mergeAdditional && // below usually not desired because they're from different causes
|
||||||
|| part instanceof CostAddMana || part instanceof CostPayLife
|
(part instanceof CostDiscard || part instanceof CostDraw ||
|
||||||
|| part instanceof CostPutCounter || part instanceof CostTapType
|
part instanceof CostAddMana || part instanceof CostPayLife ||
|
||||||
|| part instanceof CostExile) {
|
part instanceof CostSacrifice || part instanceof CostTapType))) {
|
||||||
boolean alreadyAdded = false;
|
boolean alreadyAdded = false;
|
||||||
for (final CostPart other : costParts) {
|
for (final CostPart other : costParts) {
|
||||||
if ((other.getClass().equals(part.getClass()) || (part instanceof CostPutCounter && ((CostPutCounter)part).getCounter().is(CounterEnumType.LOYALTY))) &&
|
if ((other.getClass().equals(part.getClass()) || (part instanceof CostPutCounter && ((CostPutCounter)part).getCounter().is(CounterEnumType.LOYALTY))) &&
|
||||||
@@ -917,7 +934,7 @@ public class Cost implements Serializable {
|
|||||||
StringUtils.isNumeric(part.getAmount()) &&
|
StringUtils.isNumeric(part.getAmount()) &&
|
||||||
StringUtils.isNumeric(other.getAmount())) {
|
StringUtils.isNumeric(other.getAmount())) {
|
||||||
String amount = String.valueOf(part.convertAmount() + other.convertAmount());
|
String amount = String.valueOf(part.convertAmount() + other.convertAmount());
|
||||||
if (part instanceof CostPutCounter) { // path for Carth & Cumulative Upkeep
|
if (part instanceof CostPutCounter) { // CR 606.5 path for Carth
|
||||||
if (other instanceof CostPutCounter && ((CostPutCounter)other).getCounter().equals(((CostPutCounter) part).getCounter())) {
|
if (other instanceof CostPutCounter && ((CostPutCounter)other).getCounter().equals(((CostPutCounter) part).getCounter())) {
|
||||||
costParts.add(new CostPutCounter(amount, ((CostPutCounter) part).getCounter(), part.getType(), part.getTypeDescription()));
|
costParts.add(new CostPutCounter(amount, ((CostPutCounter) part).getCounter(), part.getType(), part.getTypeDescription()));
|
||||||
} else if (other instanceof CostRemoveCounter && ((CostRemoveCounter)other).counter.is(CounterEnumType.LOYALTY)) {
|
} else if (other instanceof CostRemoveCounter && ((CostRemoveCounter)other).counter.is(CounterEnumType.LOYALTY)) {
|
||||||
@@ -931,6 +948,8 @@ public class Cost implements Serializable {
|
|||||||
} else {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
} else if (part instanceof CostSacrifice) {
|
||||||
|
costParts.add(new CostSacrifice(amount, part.getType(), part.getTypeDescription()));
|
||||||
} else if (part instanceof CostDiscard) {
|
} else if (part instanceof CostDiscard) {
|
||||||
costParts.add(new CostDiscard(amount, part.getType(), part.getTypeDescription()));
|
costParts.add(new CostDiscard(amount, part.getType(), part.getTypeDescription()));
|
||||||
} else if (part instanceof CostDraw) {
|
} else if (part instanceof CostDraw) {
|
||||||
@@ -942,12 +961,6 @@ public class Cost implements Serializable {
|
|||||||
costParts.add(new CostAddMana(amount, part.getType(), part.getTypeDescription()));
|
costParts.add(new CostAddMana(amount, part.getType(), part.getTypeDescription()));
|
||||||
} else if (part instanceof CostPayLife) {
|
} else if (part instanceof CostPayLife) {
|
||||||
costParts.add(new CostPayLife(amount, part.getTypeDescription()));
|
costParts.add(new CostPayLife(amount, part.getTypeDescription()));
|
||||||
} else if (part instanceof CostExile) {
|
|
||||||
ZoneType z = ((CostExile) part).getFrom();
|
|
||||||
if (((CostExile) other).getFrom() != z) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
costParts.add(new CostExile(amount, part.getType(), part.getTypeDescription(), z));
|
|
||||||
}
|
}
|
||||||
toRemove.add(other);
|
toRemove.add(other);
|
||||||
alreadyAdded = true;
|
alreadyAdded = true;
|
||||||
|
|||||||
@@ -112,7 +112,6 @@ public class CostAdjustment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final String scost = st.getParamOrDefault("Cost", "1");
|
final String scost = st.getParamOrDefault("Cost", "1");
|
||||||
Cost part = new Cost(scost, sa.isAbility());
|
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
|
||||||
if (st.hasParam("ForEachShard")) {
|
if (st.hasParam("ForEachShard")) {
|
||||||
@@ -155,8 +154,9 @@ public class CostAdjustment {
|
|||||||
// Amount 1 as default
|
// Amount 1 as default
|
||||||
count = 1;
|
count = 1;
|
||||||
}
|
}
|
||||||
for (int i = 0; i < count; ++i) {
|
if (count > 0) {
|
||||||
cost.add(part);
|
Cost part = new Cost(scost, sa.isAbility());
|
||||||
|
cost.mergeTo(part, count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,7 +177,7 @@ public class CostAdjustment {
|
|||||||
originalCard.turnFaceDownNoUpdate();
|
originalCard.turnFaceDownNoUpdate();
|
||||||
isStateChangeToFaceDown = true;
|
isStateChangeToFaceDown = true;
|
||||||
}
|
}
|
||||||
} // isSpell
|
}
|
||||||
|
|
||||||
CardCollection cardsOnBattlefield = new CardCollection(game.getCardsIn(ZoneType.Battlefield));
|
CardCollection cardsOnBattlefield = new CardCollection(game.getCardsIn(ZoneType.Battlefield));
|
||||||
cardsOnBattlefield.addAll(game.getCardsIn(ZoneType.Stack));
|
cardsOnBattlefield.addAll(game.getCardsIn(ZoneType.Stack));
|
||||||
|
|||||||
@@ -127,7 +127,6 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
|||||||
|
|
||||||
private boolean aftermath = false;
|
private boolean aftermath = false;
|
||||||
|
|
||||||
private boolean cumulativeupkeep = false;
|
|
||||||
private boolean blessing = false;
|
private boolean blessing = false;
|
||||||
private Integer chapter = null;
|
private Integer chapter = null;
|
||||||
private boolean lastChapter = false;
|
private boolean lastChapter = false;
|
||||||
@@ -516,6 +515,10 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
|||||||
return this.hasParam("Ninjutsu");
|
return this.hasParam("Ninjutsu");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isCumulativeupkeep() {
|
||||||
|
return hasParam("CumulativeUpkeep");
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isEpic() {
|
public boolean isEpic() {
|
||||||
AbilitySub sub = this.getSubAbility();
|
AbilitySub sub = this.getSubAbility();
|
||||||
while (sub != null && !sub.hasParam("Epic")) {
|
while (sub != null && !sub.hasParam("Epic")) {
|
||||||
@@ -2140,13 +2143,6 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
|||||||
return ForgeScript.spellAbilityHasProperty(this, property, sourceController, source, spellAbility);
|
return ForgeScript.spellAbilityHasProperty(this, property, sourceController, source, spellAbility);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isCumulativeupkeep() {
|
|
||||||
return cumulativeupkeep;
|
|
||||||
}
|
|
||||||
public void setCumulativeupkeep(boolean cumulativeupkeep0) {
|
|
||||||
cumulativeupkeep = cumulativeupkeep0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return whether this spell tracks what color mana is spent to cast it for the sake of the effect
|
// Return whether this spell tracks what color mana is spent to cast it for the sake of the effect
|
||||||
public boolean tracksManaSpent() {
|
public boolean tracksManaSpent() {
|
||||||
if (hostCard == null || hostCard.getRules() == null) { return false; }
|
if (hostCard == null || hostCard.getRules() == null) { return false; }
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ public class StaticAbilityCantGainLosePayLife {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!stAb.matchesValidParam("ValidCause", cause)) {
|
if (!stAb.matchesValidParam("ValidCause", cause)) {
|
||||||
return false;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (applyCommonAbility(stAb, player)) {
|
if (applyCommonAbility(stAb, player)) {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
Name:Land Grant
|
Name:Land Grant
|
||||||
ManaCost:1 G
|
ManaCost:1 G
|
||||||
Types:Sorcery
|
Types:Sorcery
|
||||||
S:Mode$ Continuous | CharacteristicDefining$ True | AddKeyword$ Alternative Cost:Reveal<1/Hand> | CheckSVar$ X | SVarCompare$ EQ0 | Description$ If you have no land cards in hand, you may reveal your hand rather than pay Land Grant's mana cost.
|
S:Mode$ Continuous | CharacteristicDefining$ True | AddKeyword$ Alternative Cost:Reveal<1/Hand> | CheckSVar$ X | SVarCompare$ EQ0 | Description$ If you have no land cards in hand, you may reveal your hand rather than pay this spell's mana cost.
|
||||||
SVar:X:Count$TypeInYourHand.Land
|
SVar:X:Count$TypeInYourHand.Land
|
||||||
A:SP$ ChangeZone | Cost$ 1 G | Origin$ Library | Destination$ Hand | ChangeType$ Forest | ChangeNum$ 1 | SpellDescription$ Search your library for a Forest card, reveal that card, put it into your hand, then shuffle.
|
A:SP$ ChangeZone | Cost$ 1 G | Origin$ Library | Destination$ Hand | ChangeType$ Forest | ChangeNum$ 1 | SpellDescription$ Search your library for a Forest card, reveal that card, put it into your hand, then shuffle.
|
||||||
Oracle:If you have no land cards in hand, you may reveal your hand rather than pay this spell's mana cost.\nSearch your library for a Forest card, reveal that card, put it into your hand, then shuffle.
|
Oracle:If you have no land cards in hand, you may reveal your hand rather than pay this spell's mana cost.\nSearch your library for a Forest card, reveal that card, put it into your hand, then shuffle.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ Name:The Lady of Otaria
|
|||||||
ManaCost:3 R G
|
ManaCost:3 R G
|
||||||
Types:Legendary Creature Avatar
|
Types:Legendary Creature Avatar
|
||||||
PT:5/5
|
PT:5/5
|
||||||
SVar:AltCost:Cost$ tapXType<3/Creature.Dwarf> | Description$ You may tap three untapped Dwarves you control rather than pay this spell's mana cost.
|
SVar:AltCost:Cost$ tapXType<3/Dwarf> | Description$ You may tap three untapped Dwarves you control rather than pay this spell's mana cost.
|
||||||
T:Mode$ Phase | Phase$ End of Turn | TriggerZones$ Battlefield | Execute$ TrigDig | CheckSVar$ X | TriggerDescription$ At the beginning of each end step, if a land you controlled was put into your graveyard from the battlefield this turn, reveal the top four cards of your library. Put any number of Dwarf cards from among them into your hand and the rest on the bottom of your library in a random order.
|
T:Mode$ Phase | Phase$ End of Turn | TriggerZones$ Battlefield | Execute$ TrigDig | CheckSVar$ X | TriggerDescription$ At the beginning of each end step, if a land you controlled was put into your graveyard from the battlefield this turn, reveal the top four cards of your library. Put any number of Dwarf cards from among them into your hand and the rest on the bottom of your library in a random order.
|
||||||
SVar:TrigDig:DB$ Dig | DigNum$ 4 | ChangeValid$ Dwarf | DestinationZone$ Hand | RestRandomOrder$ True | AnyNumber$ True
|
SVar:TrigDig:DB$ Dig | DigNum$ 4 | ChangeValid$ Dwarf | DestinationZone$ Hand | RestRandomOrder$ True | AnyNumber$ True
|
||||||
SVar:X:Count$ThisTurnEntered_Graveyard_from_Battlefield_Land.YouCtrl+YouOwn
|
SVar:X:Count$ThisTurnEntered_Graveyard_from_Battlefield_Land.YouCtrl+YouOwn
|
||||||
|
|||||||
@@ -452,7 +452,6 @@ public class HumanPlay {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
else if (part instanceof CostGainControl) {
|
else if (part instanceof CostGainControl) {
|
||||||
int amount = Integer.parseInt(part.getAmount());
|
int amount = Integer.parseInt(part.getAmount());
|
||||||
|
|||||||
Reference in New Issue
Block a user