mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-17 11:18:01 +00:00
- Convert Balance to script, Simplify Balancing Act
- Add Restore Balance - Adding a Balance Effect API
This commit is contained in:
3
.gitattributes
vendored
3
.gitattributes
vendored
@@ -8843,6 +8843,7 @@ res/cardsfolder/r/restless_dead.txt svneol=native#text/plain
|
||||
res/cardsfolder/r/restless_dreams.txt -text
|
||||
res/cardsfolder/r/restock.txt svneol=native#text/plain
|
||||
res/cardsfolder/r/restoration_angel.txt -text
|
||||
res/cardsfolder/r/restore_balance.txt -text
|
||||
res/cardsfolder/r/restore_the_peace.txt -text
|
||||
res/cardsfolder/r/restrain.txt -text
|
||||
res/cardsfolder/r/resurrection.txt svneol=native#text/plain
|
||||
@@ -14042,6 +14043,7 @@ src/main/java/forge/card/ability/ai/AlwaysPlayAi.java -text
|
||||
src/main/java/forge/card/ability/ai/AnimateAi.java -text
|
||||
src/main/java/forge/card/ability/ai/AnimateAllAi.java -text
|
||||
src/main/java/forge/card/ability/ai/AttachAi.java -text
|
||||
src/main/java/forge/card/ability/ai/BalanceAi.java -text
|
||||
src/main/java/forge/card/ability/ai/BecomesBlockedAi.java -text
|
||||
src/main/java/forge/card/ability/ai/BondAi.java -text
|
||||
src/main/java/forge/card/ability/ai/CanPlayAsDrawbackAi.java -text
|
||||
@@ -14143,6 +14145,7 @@ src/main/java/forge/card/ability/effects/AnimateAllEffect.java -text
|
||||
src/main/java/forge/card/ability/effects/AnimateEffect.java -text
|
||||
src/main/java/forge/card/ability/effects/AnimateEffectBase.java svneol=native#text/plain
|
||||
src/main/java/forge/card/ability/effects/AttachEffect.java -text
|
||||
src/main/java/forge/card/ability/effects/BalanceEffect.java -text
|
||||
src/main/java/forge/card/ability/effects/BecomesBlockedEffect.java -text
|
||||
src/main/java/forge/card/ability/effects/BondEffect.java -text
|
||||
src/main/java/forge/card/ability/effects/ChangeTargetsEffect.java -text
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
Name:Balance
|
||||
ManaCost:1 W
|
||||
Types:Sorcery
|
||||
Text:Each player chooses a number of lands he or she controls equal to the number of lands controlled by the player who controls the fewest, then sacrifices the rest. Players discard cards and sacrifice creatures the same way.
|
||||
A:SP$ Balance | Cost$ 1 W | Valid$ Land | AILogic$ BalanceCreaturesAndLands | SubAbility$ BalanceHands | SpellDescription$ Each player chooses a number of lands he or she controls equal to the number of lands controlled by the player who controls the fewest, then sacrifices the rest. Players discard cards and sacrifice creatures the same way.
|
||||
SVar:BalanceHands:DB$ Balance | Zone$ Hand | SubAbility$ BalanceCreatures
|
||||
SVar:BalanceCreatures:DB$ Balance | Valid$ Creature
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/balance.jpg
|
||||
Oracle:Each player chooses a number of lands he or she controls equal to the number of lands controlled by the player who controls the fewest, then sacrifices the rest. Players discard cards and sacrifice creatures the same way.
|
||||
SetInfo:2ED Rare
|
||||
SetInfo:V09 Mythic
|
||||
SetInfo:LEB Rare
|
||||
SetInfo:LEA Rare
|
||||
SetInfo:4ED Rare
|
||||
SetInfo:3ED Rare
|
||||
@@ -1,24 +1,8 @@
|
||||
Name:Balancing Act
|
||||
ManaCost:2 W W
|
||||
Types:Sorcery
|
||||
A:SP$ RepeatEach | Cost$ 2 W W | RepeatPlayers$ Player | RepeatSubAbility$ FindFewestPermanent | StackDescription$ SpellDescription | SubAbility$ DBChooseRepeat | SpellDescription$ Each player chooses a number of permanents he or she controls equal to the number of permanents controlled by the player who controls the fewest, then sacrifices the rest. Each player discards cards the same way.
|
||||
SVar:FindFewestPermanent:DB$ StoreSVar | SVar$ MinPermanent | Type$ CountSVar | Expression$ NumPermanent | ConditionCheckSVar$ NumPermanent | ConditionSVarCompare$ LTMinPermanent
|
||||
SVar:NumPermanent:Count$Valid Permanent.RememberedPlayerCtrl
|
||||
SVar:MinPermanent:Number$9999
|
||||
SVar:DBChooseRepeat:DB$ RepeatEach | RepeatPlayers$ Player | RepeatSubAbility$ DBChoose | StackDescription$ None | SubAbility$ SacAll
|
||||
SVar:DBChoose:DB$ ChooseCard | Defined$ Player.IsRemembered | Choices$ Permanent.RememberedPlayerCtrl | Amount$ MinPermanent | References$ MinPermanent | ChoiceTitle$ Choose permanents you control | RememberChosen$ True
|
||||
SVar:SacAll:DB$ SacrificeAll | ValidCards$ Permanent.IsNotRemembered | SubAbility$ DBCleanup1 | StackDescription$ None
|
||||
SVar:DBCleanup1:DB$ Cleanup | ClearRemembered$ True | SubAbility$ DBFindFewestHand
|
||||
SVar:DBFindFewestHand:DB$ RepeatEach | RepeatPlayers$ Player | RepeatSubAbility$ FindFewestHand | StackDescription$ None | SubAbility$ DBChooseRepeat2
|
||||
SVar:FindFewestHand:DB$ StoreSVar | SVar$ MinHand | Type$ CountSVar | Expression$ NumHand | ConditionCheckSVar$ NumHand | ConditionSVarCompare$ LTMinHand
|
||||
SVar:MinHand:Number$9999
|
||||
SVar:NumHand:Count$ValidHand Card.RememberedPlayerCtrl
|
||||
SVar:DBChooseRepeat2:DB$ RepeatEach | RepeatPlayers$ Player | RepeatSubAbility$ DBChooseHand | StackDescription$ None | SubAbility$ DisCardAll
|
||||
SVar:DBChooseHand:DB$ ChooseCard | Defined$ Player.IsRemembered | Choices$ Card.RememberedPlayerCtrl | ChoiceZone$ Hand | Amount$ MinHand | References$ MinHand | ChoiceTitle$ Choose cards in your hand | RememberChosen$ True
|
||||
SVar:DisCardAll:DB$ Discard | Mode$ Defined | DefinedCards$ ValidHand Card.IsNotRemembered | Defined$ Each | SubAbility$ DBCleanup2
|
||||
SVar:DBCleanup2:DB$ Cleanup | ClearRemembered$ True | SubAbility$ DBReset1
|
||||
SVar:DBReset1:DB$ StoreSVar | SVar$ MinPermanent | Type$ Number | Expression$ 9999 | SubAbility$ DBReset2
|
||||
SVar:DBReset2:DB$ StoreSVar | SVar$ MinHand | Type$ Number | Expression$ 9999
|
||||
A:SP$ Balance | Cost$ 2 W W | Valid$ Permanent | AILogic$ BalancePermanents | SubAbility$ BalanceHands | SpellDescription$ Each player chooses a number of permanents he or she controls equal to the number of permanents controlled by the player who controls the fewest, then sacrifices the rest. Each player discards cards the same way.
|
||||
SVar:BalanceHands:DB$ Balance | Zone$ Hand
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/balancing_act.jpg
|
||||
SVar:RemAIDeck:True
|
||||
SVar:RemRandomDeck:True
|
||||
|
||||
11
res/cardsfolder/r/restore_balance.txt
Normal file
11
res/cardsfolder/r/restore_balance.txt
Normal file
@@ -0,0 +1,11 @@
|
||||
Name:Restore Balance
|
||||
ManaCost:no cost
|
||||
Types:Sorcery
|
||||
Colors:white
|
||||
K:Suspend:6:W
|
||||
A:SP$ Balance | Cost$ 1 W | Valid$ Land | AILogic$ BalanceCreaturesAndLands | SubAbility$ BalanceCreatures | SpellDescription$ Each player chooses a number of lands he or she controls equal to the number of lands controlled by the player who controls the fewest, then sacrifices the rest. Players discard cards and sacrifice creatures the same way. | ActivationLimit$ 0
|
||||
SVar:BalanceCreatures:DB$ Balance | Valid$ Creature | SubAbility$ BalanceHands
|
||||
SVar:BalanceHands:DB$ Balance | Zone$ Hand
|
||||
SVar:RemAIDeck:True
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/restore_balance.jpg
|
||||
Oracle:Sorcery\nSuspend 6- {W} (Rather than cast this card from your hand, pay {W} and exile it with six time counters on it. At the beginning of your upkeep, remove a time counter. When the last is removed, cast it without paying its mana cost.)\nEach player chooses a number of lands he or she controls equal to the number of lands controlled by the player who controls the fewest, then sacrifices the rest. Players sacrifice creatures and discard cards the same way.
|
||||
@@ -10,6 +10,7 @@ import forge.card.ability.ai.AlwaysPlayAi;
|
||||
import forge.card.ability.ai.AnimateAi;
|
||||
import forge.card.ability.ai.AnimateAllAi;
|
||||
import forge.card.ability.ai.AttachAi;
|
||||
import forge.card.ability.ai.BalanceAi;
|
||||
import forge.card.ability.ai.BecomesBlockedAi;
|
||||
import forge.card.ability.ai.BondAi;
|
||||
import forge.card.ability.ai.CanPlayAsDrawbackAi;
|
||||
@@ -114,6 +115,7 @@ public enum ApiType {
|
||||
Animate (AnimateEffect.class, AnimateAi.class),
|
||||
AnimateAll (AnimateAllEffect.class, AnimateAllAi.class),
|
||||
Attach (AttachEffect.class, AttachAi.class),
|
||||
Balance (BalanceEffect.class, BalanceAi.class),
|
||||
BecomesBlocked (BecomesBlockedEffect.class, BecomesBlockedAi.class),
|
||||
Bond (BondEffect.class, BondAi.class),
|
||||
ChangeTargets(ChangeTargetsEffect.class, CannotPlayAi.class),
|
||||
|
||||
53
src/main/java/forge/card/ability/ai/BalanceAi.java
Normal file
53
src/main/java/forge/card/ability/ai/BalanceAi.java
Normal file
@@ -0,0 +1,53 @@
|
||||
package forge.card.ability.ai;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang.math.RandomUtils;
|
||||
|
||||
import forge.Card;
|
||||
import forge.CardLists;
|
||||
import forge.CardPredicates;
|
||||
import forge.card.ability.SpellAbilityAi;
|
||||
import forge.card.spellability.SpellAbility;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.zone.ZoneType;
|
||||
|
||||
public class BalanceAi extends SpellAbilityAi {
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.card.ability.SpellAbilityAi#canPlayAI(forge.game.player.Player, forge.card.spellability.SpellAbility)
|
||||
*/
|
||||
@Override
|
||||
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
|
||||
String logic = sa.getParam("AILogic");
|
||||
|
||||
int diff = 0;
|
||||
// TODO Add support for multiplayer logic
|
||||
final Player opp = aiPlayer.getOpponent();
|
||||
final List<Card> humPerms = opp.getCardsIn(ZoneType.Battlefield);
|
||||
final List<Card> compPerms = aiPlayer.getCardsIn(ZoneType.Battlefield);
|
||||
|
||||
if ("BalanceCreaturesAndLands".equals(logic)) {
|
||||
// Copied over from hardcoded Balance. We should be checking value of the lands/creatures not just counting
|
||||
diff += CardLists.filter(humPerms, CardPredicates.Presets.LANDS).size() -
|
||||
CardLists.filter(compPerms, CardPredicates.Presets.LANDS).size();
|
||||
diff += 1.5 * ( CardLists.filter(humPerms, CardPredicates.Presets.CREATURES).size() -
|
||||
CardLists.filter(compPerms, CardPredicates.Presets.CREATURES).size());
|
||||
} else if ("BalancePermanents".equals(logic)) {
|
||||
// Don't cast if you have to sacrifice permanents
|
||||
diff += humPerms.size() - compPerms.size();
|
||||
}
|
||||
|
||||
if (diff < 0) {
|
||||
// Don't sacrifice permanents even if opponent has a ton of cards in hand
|
||||
return false;
|
||||
}
|
||||
|
||||
final List<Card> humHand = opp.getCardsIn(ZoneType.Hand);
|
||||
final List<Card> compHand = aiPlayer.getCardsIn(ZoneType.Hand);
|
||||
diff += 0.5 * (humHand.size() - compHand.size());
|
||||
|
||||
// Larger differential == more chance to actually cast this spell
|
||||
return diff > 2 && RandomUtils.nextInt(100) < diff*10;
|
||||
}
|
||||
}
|
||||
61
src/main/java/forge/card/ability/effects/BalanceEffect.java
Normal file
61
src/main/java/forge/card/ability/effects/BalanceEffect.java
Normal file
@@ -0,0 +1,61 @@
|
||||
package forge.card.ability.effects;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import forge.Card;
|
||||
import forge.CardLists;
|
||||
import forge.card.ability.SpellAbilityEffect;
|
||||
import forge.card.spellability.SpellAbility;
|
||||
import forge.game.Game;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.zone.ZoneType;
|
||||
|
||||
/**
|
||||
* TODO: Write javadoc for this type.
|
||||
*
|
||||
*/
|
||||
public class BalanceEffect extends SpellAbilityEffect {
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.card.ability.SpellAbilityEffect#resolve(forge.card.spellability.SpellAbility)
|
||||
*/
|
||||
@Override
|
||||
public void resolve(SpellAbility sa) {
|
||||
Player activator = sa.getActivatingPlayer();
|
||||
Card source = sa.getSourceCard();
|
||||
Game game = activator.getGame();
|
||||
String valid = sa.hasParam("Valid") ? sa.getParam("Valid") : "Card";
|
||||
ZoneType zone = sa.hasParam("Zone") ? ZoneType.smartValueOf(sa.getParam("Zone")) : ZoneType.Battlefield;
|
||||
|
||||
int min = Integer.MAX_VALUE;
|
||||
|
||||
final List<Player> players = game.getPlayers();
|
||||
final List<List<Card>> validCards = new ArrayList<List<Card>>(players.size());
|
||||
|
||||
for(int i = 0; i < players.size(); i++) {
|
||||
// Find the minimum of each Valid per player
|
||||
validCards.add(CardLists.getValidCards(players.get(i).getCardsIn(zone), valid, activator, source));
|
||||
min = Math.min(min, validCards.get(i).size());
|
||||
}
|
||||
|
||||
for(int i = 0; i < players.size(); i++) {
|
||||
Player p = players.get(i);
|
||||
int numToBalance = validCards.get(i).size() - min;
|
||||
if (numToBalance == 0) {
|
||||
continue;
|
||||
}
|
||||
if (zone.equals(ZoneType.Hand)) {
|
||||
for (Card card : p.getController().chooseCardsToDiscardFrom(p, sa, validCards.get(i), numToBalance, numToBalance)) {
|
||||
if ( null == card ) continue;
|
||||
p.discard(card, sa);
|
||||
}
|
||||
} else { // Battlefield
|
||||
for(Card card : p.getController().choosePermanentsToSacrifice(sa, numToBalance, numToBalance, validCards.get(i), valid)) {
|
||||
if ( null == card ) continue;
|
||||
game.getAction().sacrifice(card, sa);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,12 +17,9 @@
|
||||
*/
|
||||
package forge.card.cardfactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import forge.Card;
|
||||
import forge.CardLists;
|
||||
import forge.CardPredicates;
|
||||
import forge.Singletons;
|
||||
import forge.CardPredicates.Presets;
|
||||
import forge.card.cost.Cost;
|
||||
@@ -45,108 +42,6 @@ import forge.gui.input.InputPayManaExecuteCommands;
|
||||
*/
|
||||
public class CardFactorySorceries {
|
||||
|
||||
private static final void balanceLands(Game game, Spell card) {
|
||||
|
||||
int minLands = Integer.MAX_VALUE;
|
||||
for (Player p : game.getPlayers()) {
|
||||
int pL = p.getLandsInPlay().size();
|
||||
if( pL < minLands )
|
||||
minLands = pL;
|
||||
}
|
||||
|
||||
for (Player p : game.getPlayers()) {
|
||||
|
||||
List<Card> l = p.getLandsInPlay();
|
||||
int sac = l.size() - minLands;
|
||||
if (sac == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
List<Card> toSac = p.getController().choosePermanentsToSacrifice(card, sac, sac, l, "land(s)");
|
||||
for( Card crd : toSac )
|
||||
p.getGame().getAction().sacrifice(crd, card);
|
||||
}
|
||||
}
|
||||
|
||||
private static final void balanceHands(Game game, Spell spell) {
|
||||
int min = Integer.MAX_VALUE;
|
||||
for (Player p : game.getPlayers()) {
|
||||
min = Math.min(min, p.getZone(ZoneType.Hand).size());
|
||||
}
|
||||
|
||||
for (Player p : game.getPlayers()) {
|
||||
List<Card> hand = new ArrayList<Card>(p.getCardsIn(ZoneType.Hand));
|
||||
int sac = hand.size() - min;
|
||||
if (sac == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
List<Card> toDiscard = p.getController().chooseCardsToDiscardFrom(p, spell, hand, sac, sac); // "Select %d more card(s) to discard"
|
||||
for (Card c : toDiscard)
|
||||
p.discard(c, spell);
|
||||
}
|
||||
}
|
||||
|
||||
private static final void balanceCreatures(Game game, Spell card) {
|
||||
List<List<Card>> creats = new ArrayList<List<Card>>();
|
||||
for (Player p : game.getPlayers()) {
|
||||
creats.add(p.getCreaturesInPlay());
|
||||
}
|
||||
int min = Integer.MAX_VALUE;
|
||||
for (List<Card> h : creats) {
|
||||
int s = h.size();
|
||||
min = Math.min(min, s);
|
||||
}
|
||||
Iterator<List<Card>> cc = creats.iterator();
|
||||
for (Player p : game.getPlayers()) {
|
||||
|
||||
List<Card> c = cc.next();
|
||||
int sac = c.size() - min;
|
||||
if (sac == 0) {
|
||||
continue;
|
||||
}
|
||||
List<Card> toSac = p.getController().choosePermanentsToSacrifice(card, sac, sac, c, "creature(s)");
|
||||
|
||||
for( Card crd : toSac )
|
||||
p.getGame().getAction().sacrifice(crd, card);
|
||||
}
|
||||
}
|
||||
|
||||
private static final SpellAbility getBalance(final Card card) {
|
||||
return new Spell(card) {
|
||||
private static final long serialVersionUID = -5941893280103164961L;
|
||||
|
||||
@Override
|
||||
public void resolve() {
|
||||
final Game game = this.getActivatingPlayer().getGame();
|
||||
balanceLands(game, this);
|
||||
balanceHands(game, this);
|
||||
balanceCreatures(game, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPlayAI() {
|
||||
int diff = 0;
|
||||
final Player ai = getActivatingPlayer();
|
||||
final Player opp = ai.getOpponent();
|
||||
final List<Card> humLand = opp.getLandsInPlay();
|
||||
final List<Card> compLand = ai.getLandsInPlay();
|
||||
diff += humLand.size() - compLand.size();
|
||||
|
||||
final List<Card> humCreats = opp.getCreaturesInPlay();
|
||||
List<Card> compCreats = ai.getCreaturesInPlay();
|
||||
compCreats = CardLists.filter(compCreats, CardPredicates.Presets.CREATURES);
|
||||
diff += 1.5 * (humCreats.size() - compCreats.size());
|
||||
|
||||
final List<Card> humHand = opp.getCardsIn(ZoneType.Hand);
|
||||
final List<Card> compHand = ai.getCardsIn(ZoneType.Hand);
|
||||
diff += 0.5 * (humHand.size() - compHand.size());
|
||||
|
||||
return diff > 2;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static final SpellAbility getTransmuteArtifact(final Card card) {
|
||||
/*
|
||||
* Sacrifice an artifact. If you do, search your library for an
|
||||
@@ -219,8 +114,7 @@ public class CardFactorySorceries {
|
||||
|
||||
public static void buildCard(final Card card, final String cardName) {
|
||||
|
||||
if (cardName.equals("Balance")) { card.addSpellAbility(getBalance(card));
|
||||
} else if (cardName.equals("Transmute Artifact")) { card.addSpellAbility(getTransmuteArtifact(card));
|
||||
if (cardName.equals("Transmute Artifact")) { card.addSpellAbility(getTransmuteArtifact(card));
|
||||
}
|
||||
} // getCard
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user