mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-14 01:38:13 +00:00
Merge branch 'adversary' into 'master'
MID – Adversary cycle See merge request core-developers/forge!5339
This commit is contained in:
@@ -1,11 +1,8 @@
|
||||
package forge.ai.ability;
|
||||
|
||||
import forge.ai.AiController;
|
||||
import forge.ai.AiPlayDecision;
|
||||
import forge.ai.ComputerUtilCost;
|
||||
import forge.ai.PlayerControllerAi;
|
||||
import forge.ai.SpellAbilityAi;
|
||||
import forge.ai.SpellApiToAi;
|
||||
import forge.ai.*;
|
||||
import forge.card.mana.ManaCost;
|
||||
import forge.game.mana.ManaCostBeingPaid;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.AbilitySub;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
@@ -47,6 +44,19 @@ public class ImmediateTriggerAi extends SpellAbilityAi {
|
||||
sa.setXManaCostPaid(ComputerUtilCost.getMaxXValue(sa, ai));
|
||||
}
|
||||
|
||||
if ("NumTimes".equals(sa.getParam("Announce"))) { // e.g. the Adversary cycle
|
||||
ManaCost mkCost = sa.getPayCosts().getTotalMana();
|
||||
ManaCost mCost = ManaCost.ZERO;
|
||||
for (int i = 0; i < 10; i++) {
|
||||
mCost = ManaCost.combine(mCost, mkCost);
|
||||
ManaCostBeingPaid mcbp = new ManaCostBeingPaid(mCost);
|
||||
if (!ComputerUtilMana.canPayManaCost(mcbp, sa, ai)) {
|
||||
sa.getHostCard().setSVar("NumTimes", "Number$" + i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AiController aic = ((PlayerControllerAi)ai.getController()).getAi();
|
||||
trigsa.setActivatingPlayer(ai);
|
||||
|
||||
|
||||
@@ -321,6 +321,8 @@ public enum CounterEnumType {
|
||||
|
||||
UNITY("UNITY", 242, 156, 255),
|
||||
|
||||
VALOR("VALOR", 252, 250, 222),
|
||||
|
||||
VELOCITY("VELO", 255, 95, 138),
|
||||
|
||||
VERSE("VERSE", 0, 237, 155),
|
||||
|
||||
@@ -18,7 +18,9 @@
|
||||
package forge.game.cost;
|
||||
|
||||
import forge.card.mana.ManaCost;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.mana.ManaConversionMatrix;
|
||||
import forge.game.mana.ManaCostBeingPaid;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
|
||||
@@ -35,6 +37,7 @@ public class CostPartMana extends CostPart {
|
||||
private boolean xCantBe0 = false;
|
||||
private boolean isExiledCreatureCost = false;
|
||||
private boolean isEnchantedCreatureCost = false;
|
||||
private boolean isCostPayAnyNumberOfTimes = false;
|
||||
private final String restriction;
|
||||
|
||||
private ManaConversionMatrix cardMatrix = null;
|
||||
@@ -53,7 +56,8 @@ public class CostPartMana extends CostPart {
|
||||
this.xCantBe0 = "XCantBe0".equals(restriction);
|
||||
this.isExiledCreatureCost = "Exiled".equalsIgnoreCase(restriction);
|
||||
this.isEnchantedCreatureCost = "EnchantedCost".equalsIgnoreCase(restriction);
|
||||
this.restriction = xCantBe0 || isExiledCreatureCost || isEnchantedCreatureCost? null : restriction;
|
||||
this.isCostPayAnyNumberOfTimes = "NumTimes".equalsIgnoreCase(restriction);
|
||||
this.restriction = xCantBe0 || isExiledCreatureCost || isEnchantedCreatureCost || isCostPayAnyNumberOfTimes ? null : restriction;
|
||||
}
|
||||
|
||||
// This version of the constructor allows to explicitly set exiledCreatureCost/enchantedCreatureCost, used only when copying costs
|
||||
@@ -62,7 +66,7 @@ public class CostPartMana extends CostPart {
|
||||
this.xCantBe0 = XCantBe0;
|
||||
this.isExiledCreatureCost = exiledCreatureCost;
|
||||
this.isEnchantedCreatureCost = enchantedCreatureCost;
|
||||
this.restriction = xCantBe0 || isExiledCreatureCost || isEnchantedCreatureCost? null : restriction;
|
||||
this.restriction = xCantBe0 || isExiledCreatureCost || isEnchantedCreatureCost || isCostPayAnyNumberOfTimes ? null : restriction;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -140,6 +144,18 @@ public class CostPartMana extends CostPart {
|
||||
if (isEnchantedCreatureCost() && sa.getHostCard().getEnchantingCard() != null) {
|
||||
return sa.getHostCard().getEnchantingCard().getManaCost();
|
||||
}
|
||||
if (isCostPayAnyNumberOfTimes) {
|
||||
int timesToPay = AbilityUtils.calculateAmount(sa.getHostCard(), sa.getSVar("NumTimes"), sa);
|
||||
if (timesToPay == 0) {
|
||||
return null;
|
||||
} else {
|
||||
ManaCostBeingPaid totalMana = new ManaCostBeingPaid(getManaToPay());
|
||||
for (int i = 1; i < timesToPay; i++) {
|
||||
totalMana.addManaCost(getManaToPay());
|
||||
}
|
||||
return totalMana.toManaCost();
|
||||
}
|
||||
}
|
||||
return getManaToPay();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
Name:Bloodthirsty Adversary
|
||||
ManaCost:1 R
|
||||
Types:Creature Vampire
|
||||
PT:2/2
|
||||
K:Haste
|
||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigPay | TriggerDescription$ When CARDNAME enters the battlefield, you may pay {2}{R} any number of times. When you pay this cost one or more times, put that many +1/+1 counters on CARDNAME, then exile up to that many target instant and/or sorcery cards with mana value 3 or less from your graveyard and copy them. You may cast any number of the copies without paying their mana costs.
|
||||
SVar:TrigPay:AB$ ImmediateTrigger | Cost$ Mana<2 R\NumTimes> | Announce$ NumTimes | ConditionCheckSVar$ NumTimes | ConditionSVarCompare$ GE1 | Execute$ TrigPutCounter | TriggerDescription$ When you pay this cost one or more times, put that many +1/+1 counters on CARDNAME, then exile up to that many target instant and/or sorcery cards with mana value 3 or less from your graveyard and copy them. You may cast any number of the copies without paying their mana costs.
|
||||
SVar:TrigPutCounter:DB$ PutCounter | CounterType$ P1P1 | CounterNum$ NumTimes | SubAbility$ DBExile
|
||||
SVar:DBExile:DB$ ChangeZone | Origin$ Graveyard | Destination$ Exile | TargetMin$ 0 | TargetMax$ NumTimes | ValidTgts$ Instant.cmcLE3,Sorcery.cmcLE3 | TgtPrompt$ Select up to that many target instant and/or sorcery cards with mana value 3 or less from your graveyard | RememberChanged$ True | SubAbility$ DBCopyCast
|
||||
SVar:DBCopyCast:DB$ Play | Valid$ Card.IsRemembered | ValidZone$ Exile | Controller$ You | CopyCard$ True | WithoutManaCost$ True | ValidSA$ Spell | Optional$ True | Amount$ All | SubAbility$ ExileMe
|
||||
SVar:ExileMe:DB$ ChangeZoneAll | Origin$ Stack | Destination$ Exile | ChangeType$ Card.Self | SubAbility$ DBCleanup
|
||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||
SVar:NumTimes:Number$0
|
||||
DeckHas:Ability$Counters
|
||||
Oracle:Haste\nWhen Bloodthirsty Adversary enters the battlefield, you may pay {2}{R} any number of times. When you pay this cost one or more times, put that many +1/+1 counters on Bloodthirsty Adversary, then exile up to that many target instant and/or sorcery cards with mana value 3 or less from your graveyard and copy them. You may cast any number of the copies without paying their mana costs.
|
||||
13
forge-gui/res/cardsfolder/upcoming/intrepid_adversary.txt
Normal file
13
forge-gui/res/cardsfolder/upcoming/intrepid_adversary.txt
Normal file
@@ -0,0 +1,13 @@
|
||||
Name:Intrepid Adversary
|
||||
ManaCost:1 W
|
||||
Types:Creature Human Scout
|
||||
PT:3/1
|
||||
K:Lifelink
|
||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigPay | TriggerDescription$ When CARDNAME enters the battlefield, you may pay {1}{W} any number of times. When you pay this cost one or more times, put that many valor counters on CARDNAME.
|
||||
SVar:TrigPay:AB$ ImmediateTrigger | Cost$ Mana<1 W\NumTimes> | Announce$ NumTimes | ConditionCheckSVar$ NumTimes | ConditionSVarCompare$ GE1 | Execute$ TrigPutCounter | TriggerDescription$ When you pay this cost one or more times, put that many valor counters on CARDNAME.
|
||||
SVar:TrigPutCounter:DB$ PutCounter | CounterType$ VALOR | CounterNum$ NumTimes
|
||||
SVar:NumTimes:Number$0
|
||||
S:Mode$ Continuous | Affected$ Creature.YouCtrl | AddPower$ Z | AddToughness$ Z | Description$ Creatures you control get +1/+1 for each valor counter on CARDNAME.
|
||||
SVar:Z:Count$CardCounters.VALOR
|
||||
DeckHas:Ability$LifeGain & Ability$Counters
|
||||
Oracle:Lifelink\nWhen Intrepid Adversary enters the battlefield, you may pay {1}{W} any number of times. When you pay this cost one or more times, put that many valor counters on Intrepid Adversary.\nCreatures you control get +1/+1 for each valor counter on Intrepid Adversary.
|
||||
12
forge-gui/res/cardsfolder/upcoming/primal_adversary.txt
Normal file
12
forge-gui/res/cardsfolder/upcoming/primal_adversary.txt
Normal file
@@ -0,0 +1,12 @@
|
||||
Name:Primal Adversary
|
||||
ManaCost:2 G
|
||||
Types:Creature Wolf
|
||||
PT:4/5
|
||||
K:Trample
|
||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigPay | TriggerDescription$ When CARDNAME enters the battlefield, you may pay {1}{G} any number of times. When you pay this cost one or more times, put that many +1/+1 counters on CARDNAME, then up to that many target lands you control become 3/3 Wolf creatures with haste that are still lands.
|
||||
SVar:TrigPay:AB$ ImmediateTrigger | Cost$ Mana<1 G\NumTimes> | Announce$ NumTimes | ConditionCheckSVar$ NumTimes | ConditionSVarCompare$ GE1 | Execute$ TrigPutCounter | TriggerDescription$ When you pay this cost one or more times, put that many +1/+1 counters on CARDNAME, then up to that many target lands you control become 3/3 Wolf creatures with haste that are still lands.
|
||||
SVar:TrigPutCounter:DB$ PutCounter | CounterType$ P1P1 | CounterNum$ NumTimes | SubAbility$ DBAnimate
|
||||
SVar:DBAnimate:DB$ Animate | TargetMin$ 0 | TargetMax$ NumTimes | ValidTgts$ Land.YouCtrl | TgtPrompt$ Select up to that many target lands you control | Power$ 3 | Toughness$ 3 | Types$ Wolf,Creature | Keywords$ Haste | Duration$ Permanent
|
||||
SVar:NumTimes:Number$0
|
||||
DeckHas:Ability$Counters
|
||||
Oracle:Trample\nWhen Primal Adversary enters the battlefield, you may pay {1}{G} any number of times. When you pay this cost one or more times, put that many +1/+1 counters on Primal Adversary, then up to that many target lands you control become 3/3 Wolf creatures with haste that are still lands.
|
||||
13
forge-gui/res/cardsfolder/upcoming/spectral_adversary.txt
Normal file
13
forge-gui/res/cardsfolder/upcoming/spectral_adversary.txt
Normal file
@@ -0,0 +1,13 @@
|
||||
Name:Spectral Adversary
|
||||
ManaCost:1 U
|
||||
Types:Creature Spirit
|
||||
PT:2/1
|
||||
K:Flash
|
||||
K:Flying
|
||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigPay | TriggerDescription$ When CARDNAME enters the battlefield, you may pay {1}{U} any number of times. When you pay this cost one or more times, put that many +1/+1 counters on CARDNAME, then up to that many other target artifacts, creatures, and/or enchantments phase out.
|
||||
SVar:TrigPay:AB$ ImmediateTrigger | Cost$ Mana<1 U\NumTimes> | Announce$ NumTimes | ConditionCheckSVar$ NumTimes | ConditionSVarCompare$ GE1 | Execute$ TrigPutCounter | TriggerDescription$ When you pay this cost one or more times, put that many +1/+1 counters on CARDNAME, then up to that many other target artifacts, creatures, and/or enchantments phase out.
|
||||
SVar:TrigPutCounter:DB$ PutCounter | CounterType$ P1P1 | CounterNum$ NumTimes | SubAbility$ DBPhases
|
||||
SVar:DBPhases:DB$ Phases | ValidTgts$ Creature.Other,Artifact.Other,Enchantment.Other | TgtPrompt$ Select up to that many other target artifacts, creatures, and/or enchantments | TargetMin$ 0 | TargetMax$ NumTimes
|
||||
SVar:NumTimes:Number$0
|
||||
DeckHas:Ability$Counters
|
||||
Oracle:Flash\nFlying\nWhen Spectral Adversary enters the battlefield, you may pay {1}{U} any number of times. When you pay this cost one or more times, put that many +1/+1 counters on Spectral Adversary, then up to that many other target artifacts, creatures, and/or enchantments phase out.
|
||||
13
forge-gui/res/cardsfolder/upcoming/tainted_adversary.txt
Normal file
13
forge-gui/res/cardsfolder/upcoming/tainted_adversary.txt
Normal file
@@ -0,0 +1,13 @@
|
||||
Name:Tainted Adversary
|
||||
ManaCost:1 B
|
||||
Types:Creature Zombie
|
||||
PT:2/3
|
||||
K:Deathtouch
|
||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigPay | TriggerDescription$ When CARDNAME enters the battlefield, you may pay {2}{B} any number of times. When you pay this cost one or more times, put that many +1/+1 counters on CARDNAME, then create twice that many black 2/2 Zombie creature tokens with decayed. (A creature with decayed can't block. When it attacks, sacrifice it at end of combat.)
|
||||
SVar:TrigPay:AB$ ImmediateTrigger | Cost$ Mana<2 B\NumTimes> | Announce$ NumTimes | ConditionCheckSVar$ NumTimes | ConditionSVarCompare$ GE1 | Execute$ TrigPutCounter | TriggerDescription$ When you pay this cost one or more times, put that many +1/+1 counters on CARDNAME, then create twice that many black 2/2 Zombie creature tokens with decayed. (A creature with decayed can't block. When it attacks, sacrifice it at end of combat.)
|
||||
SVar:TrigPutCounter:DB$ PutCounter | CounterType$ P1P1 | CounterNum$ NumTimes | SubAbility$ DBToken
|
||||
SVar:DBToken:DB$ Token | TokenScript$ b_2_2_zombie_decayed | TokenAmount$ TwiceNumTimes
|
||||
SVar:NumTimes:Number$0
|
||||
SVar:TwiceNumTimes:SVar$NumTimes/Twice
|
||||
DeckHas:Ability$Token & Ability$Counters
|
||||
Oracle:Deathtouch\nWhen Tainted Adversary enters the battlefield, you may pay {2}{B} any number of times. When you pay this cost one or more times, put that many +1/+1 counters on Tainted Adversary, then create twice that many black 2/2 Zombie creature tokens with decayed. (A creature with decayed can't block. When it attacks, sacrifice it at end of combat.)
|
||||
@@ -1356,6 +1356,7 @@ lblErrorPleaseCheckID=Fehler: Prüfe IDs und ob sie durch Leerzeichen und/oder K
|
||||
lblErrorEntityWithId=Fehler: Objekt mit ID
|
||||
lblNotFound=nicht gefunden
|
||||
lblChooseAnnounceForCard=Wähle {0} für {1}
|
||||
lblHowManyTimesToPay=How many times do you want to pay {0} for {1}?
|
||||
lblSacrifice=Opfern
|
||||
lblLookCardInPlayerZone=Schaue nach Karten in {0} {1}
|
||||
lblPlayerZone={0} {1}
|
||||
|
||||
@@ -1358,6 +1358,7 @@ lblErrorPleaseCheckID=Error: Check IDs and ensure they''re separated by spaces a
|
||||
lblErrorEntityWithId=Error: Entity with ID
|
||||
lblNotFound=not found
|
||||
lblChooseAnnounceForCard=Choose {0} for {1}
|
||||
lblHowManyTimesToPay=How many times do you want to pay {0} for {1}?
|
||||
lblSacrifice=Sacrifice
|
||||
lblLookCardInPlayerZone=Looking at cards in {0} {1}
|
||||
lblPlayerZone={0} {1}
|
||||
|
||||
@@ -1356,6 +1356,7 @@ lblErrorPleaseCheckID=Error: Comprueba los IDs y asegúrate de que están separa
|
||||
lblErrorEntityWithId=Error: Entidad con ID
|
||||
lblNotFound=no encontrado
|
||||
lblChooseAnnounceForCard=Selecciona {0} para {1}
|
||||
lblHowManyTimesToPay=¿Cuántas veces quieres pagar {0} para {1}?
|
||||
lblSacrifice=Sacrificio
|
||||
lblLookCardInPlayerZone=Mirando las cartas en {1} {0}
|
||||
lblPlayerZone={0} {1}
|
||||
|
||||
@@ -1355,7 +1355,8 @@ lblRestartingActionSequence=Riavvio sequenza di azioni.
|
||||
lblErrorPleaseCheckID=Errore: Controlla gli ID e assicurati che siano separati da spazi e/o virgole..
|
||||
lblErrorEntityWithId=Errore: Entità con ID
|
||||
lblNotFound=non trovato
|
||||
lblChooseAnnounceForCard=Scegli{0} per {1}
|
||||
lblChooseAnnounceForCard=Scegli {0} per {1}
|
||||
lblHowManyTimesToPay=How many times do you want to pay {0} for {1}?
|
||||
lblSacrifice=Sacrifica
|
||||
lblLookCardInPlayerZone=Stai guardando le carte in {1} di {0}
|
||||
lblPlayerZone={1} di {0}
|
||||
|
||||
@@ -1357,6 +1357,7 @@ lblErrorPleaseCheckID=エラー: IDをスペースや「,」で分けてくだ
|
||||
lblErrorEntityWithId=エラー: オブジェクト ID
|
||||
lblNotFound=が見つかりません
|
||||
lblChooseAnnounceForCard={1}の{0}を選ぶ
|
||||
lblHowManyTimesToPay=How many times do you want to pay {0} for {1}?
|
||||
lblSacrifice=生け贄
|
||||
lblLookCardInPlayerZone={0} {1}のカードを見る
|
||||
lblPlayerZone={0} {1}
|
||||
|
||||
@@ -1358,6 +1358,7 @@ lblErrorPleaseCheckID=错误:请检查ID,并确保他们之间用空格和/
|
||||
lblErrorEntityWithId=错误:实体的ID
|
||||
lblNotFound=没有找到
|
||||
lblChooseAnnounceForCard=为{1}选择{0}
|
||||
lblHowManyTimesToPay=How many times do you want to pay {0} for {1}?
|
||||
lblSacrifice=牺牲
|
||||
lblLookCardInPlayerZone=查看{0}的{1}中的牌
|
||||
lblPlayerZone={0}的{1}
|
||||
|
||||
@@ -237,7 +237,7 @@ public class HumanPlaySpellAbility {
|
||||
}
|
||||
|
||||
private boolean announceValuesLikeX() {
|
||||
if (ability.isCopied()) { return true; } //don't re-announce for spell copies
|
||||
if (ability.isCopied() || ability.isWrapper()) { return true; } //don't re-announce for spell copies
|
||||
|
||||
boolean needX = true;
|
||||
final Cost cost = ability.getPayCosts();
|
||||
|
||||
@@ -469,8 +469,13 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont
|
||||
return chooseNumber(ability, localizer.getMessage("lblChooseAnnounceForCard", announce,
|
||||
CardTranslation.getTranslatedName(ability.getHostCard().getName())) , min, max);
|
||||
} else {
|
||||
return getGui().getInteger(localizer.getMessage("lblChooseAnnounceForCard", announce,
|
||||
CardTranslation.getTranslatedName(ability.getHostCard().getName())) , min, max, min + 9);
|
||||
if ("NumTimes".equals(announce)) {
|
||||
return getGui().getInteger(localizer.getMessage("lblHowManyTimesToPay", ability.getPayCosts().getTotalMana(),
|
||||
CardTranslation.getTranslatedName(ability.getHostCard().getName())), min, max, min + 9);
|
||||
} else {
|
||||
return getGui().getInteger(localizer.getMessage("lblChooseAnnounceForCard", announce,
|
||||
CardTranslation.getTranslatedName(ability.getHostCard().getName())), min, max, min + 9);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user