mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-20 04:38:00 +00:00
space_beleren.txt and support
This commit is contained in:
@@ -1,11 +1,7 @@
|
|||||||
package forge.ai;
|
package forge.ai;
|
||||||
|
|
||||||
import java.security.InvalidParameterException;
|
import java.security.InvalidParameterException;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import forge.game.keyword.Keyword;
|
import forge.game.keyword.Keyword;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
@@ -591,6 +587,12 @@ public class PlayerControllerAi extends PlayerController {
|
|||||||
return ComputerUtil.vote(player, options, sa, votes, forPlayer);
|
return ComputerUtil.vote(player, options, sa, votes, forPlayer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String chooseSector(Card assignee, String ai) {
|
||||||
|
final List<String> sectors = Arrays.asList("Alpha", "Beta", "Gamma");
|
||||||
|
return Aggregates.random(sectors);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean mulliganKeepHand(Player firstPlayer, int cardsToReturn) {
|
public boolean mulliganKeepHand(Player firstPlayer, int cardsToReturn) {
|
||||||
return !ComputerUtil.wantMulligan(player, cardsToReturn);
|
return !ComputerUtil.wantMulligan(player, cardsToReturn);
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ public enum SpellApiToAi {
|
|||||||
.put(ApiType.ChooseEvenOdd, ChooseEvenOddAi.class)
|
.put(ApiType.ChooseEvenOdd, ChooseEvenOddAi.class)
|
||||||
.put(ApiType.ChooseNumber, ChooseNumberAi.class)
|
.put(ApiType.ChooseNumber, ChooseNumberAi.class)
|
||||||
.put(ApiType.ChoosePlayer, ChoosePlayerAi.class)
|
.put(ApiType.ChoosePlayer, ChoosePlayerAi.class)
|
||||||
|
.put(ApiType.ChooseSector, AlwaysPlayAi.class)
|
||||||
.put(ApiType.ChooseSource, ChooseSourceAi.class)
|
.put(ApiType.ChooseSource, ChooseSourceAi.class)
|
||||||
.put(ApiType.ChooseType, ChooseTypeAi.class)
|
.put(ApiType.ChooseType, ChooseTypeAi.class)
|
||||||
.put(ApiType.Clash, ClashAi.class)
|
.put(ApiType.Clash, ClashAi.class)
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ import forge.game.mulligan.MulliganService;
|
|||||||
import forge.game.player.GameLossReason;
|
import forge.game.player.GameLossReason;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.player.PlayerActionConfirmMode;
|
import forge.game.player.PlayerActionConfirmMode;
|
||||||
|
import forge.game.player.PlayerCollection;
|
||||||
import forge.game.player.PlayerPredicates;
|
import forge.game.player.PlayerPredicates;
|
||||||
import forge.game.replacement.ReplacementEffect;
|
import forge.game.replacement.ReplacementEffect;
|
||||||
import forge.game.replacement.ReplacementResult;
|
import forge.game.replacement.ReplacementResult;
|
||||||
@@ -1317,7 +1318,11 @@ public class GameAction {
|
|||||||
CardCollection desCreats = null;
|
CardCollection desCreats = null;
|
||||||
CardCollection unAttachList = new CardCollection();
|
CardCollection unAttachList = new CardCollection();
|
||||||
CardCollection sacrificeList = new CardCollection();
|
CardCollection sacrificeList = new CardCollection();
|
||||||
|
PlayerCollection spaceSculptors = new PlayerCollection();
|
||||||
for (final Card c : game.getCardsIn(ZoneType.Battlefield)) {
|
for (final Card c : game.getCardsIn(ZoneType.Battlefield)) {
|
||||||
|
if (c.hasKeyword(Keyword.SPACE_SCULPTOR)) {
|
||||||
|
spaceSculptors.add(c.getController());
|
||||||
|
}
|
||||||
if (c.isCreature()) {
|
if (c.isCreature()) {
|
||||||
// Rule 704.5f - Put into grave (no regeneration) for toughness <= 0
|
// Rule 704.5f - Put into grave (no regeneration) for toughness <= 0
|
||||||
if (c.getNetToughness() <= 0) {
|
if (c.getNetToughness() <= 0) {
|
||||||
@@ -1393,6 +1398,9 @@ public class GameAction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (Player p : game.getPlayers()) {
|
for (Player p : game.getPlayers()) {
|
||||||
|
if (!spaceSculptors.isEmpty() && !spaceSculptors.contains(p)) {
|
||||||
|
checkAgain |= stateBasedAction704_5u(p);
|
||||||
|
}
|
||||||
if (handleLegendRule(p, noRegCreats)) {
|
if (handleLegendRule(p, noRegCreats)) {
|
||||||
checkAgain = true;
|
checkAgain = true;
|
||||||
}
|
}
|
||||||
@@ -1412,6 +1420,11 @@ public class GameAction {
|
|||||||
checkAgain = true;
|
checkAgain = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!spaceSculptors.isEmpty()) {
|
||||||
|
for (Player p : spaceSculptors) {
|
||||||
|
checkAgain |= stateBasedAction704_5u(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
// 704.5m World rule
|
// 704.5m World rule
|
||||||
checkAgain |= handleWorldRule(noRegCreats);
|
checkAgain |= handleWorldRule(noRegCreats);
|
||||||
// only check static abilities once after destroying all the creatures
|
// only check static abilities once after destroying all the creatures
|
||||||
@@ -1551,6 +1564,26 @@ public class GameAction {
|
|||||||
return checkAgain;
|
return checkAgain;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean stateBasedAction704_5u(Player p) {
|
||||||
|
boolean checkAgain = false;
|
||||||
|
|
||||||
|
CardCollection toAssign = new CardCollection();
|
||||||
|
|
||||||
|
for (final Card c : p.getCreaturesInPlay().threadSafeIterable()) {
|
||||||
|
if (!c.hasSector()) {
|
||||||
|
toAssign.add(c);
|
||||||
|
checkAgain = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Card assignee : toAssign) { // probably would be nice for players to pick order of assigning?
|
||||||
|
assignee.assignSector(p.getController().chooseSector(assignee, "Assign"));
|
||||||
|
toAssign.remove(assignee);
|
||||||
|
}
|
||||||
|
|
||||||
|
return checkAgain;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean stateBasedAction903_9a(Card c) {
|
private boolean stateBasedAction903_9a(Card c) {
|
||||||
if (c.isRealCommander() && c.canMoveToCommandZone()) {
|
if (c.isRealCommander() && c.canMoveToCommandZone()) {
|
||||||
// FIXME: need to flush the tracker to make sure the Commander is properly updated
|
// FIXME: need to flush the tracker to make sure the Commander is properly updated
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ public enum ApiType {
|
|||||||
ChooseEvenOdd (ChooseEvenOddEffect.class),
|
ChooseEvenOdd (ChooseEvenOddEffect.class),
|
||||||
ChooseNumber (ChooseNumberEffect.class),
|
ChooseNumber (ChooseNumberEffect.class),
|
||||||
ChoosePlayer (ChoosePlayerEffect.class),
|
ChoosePlayer (ChoosePlayerEffect.class),
|
||||||
|
ChooseSector (ChooseSectorEffect.class),
|
||||||
ChooseSource (ChooseSourceEffect.class),
|
ChooseSource (ChooseSourceEffect.class),
|
||||||
ChooseType (ChooseTypeEffect.class),
|
ChooseType (ChooseTypeEffect.class),
|
||||||
Clash (ClashEffect.class),
|
Clash (ClashEffect.class),
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package forge.game.ability.effects;
|
||||||
|
|
||||||
|
import forge.game.ability.SpellAbilityEffect;
|
||||||
|
import forge.game.card.Card;
|
||||||
|
import forge.game.player.Player;
|
||||||
|
import forge.game.spellability.SpellAbility;
|
||||||
|
|
||||||
|
public class ChooseSectorEffect extends SpellAbilityEffect {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void resolve(SpellAbility sa) {
|
||||||
|
final Card card = sa.getHostCard();
|
||||||
|
final String chosen = card.getController().getController().chooseSector(null, sa.getParamOrDefault("AILogic", ""));
|
||||||
|
card.setChosenSector(chosen);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -290,6 +290,8 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
private Direction chosenDirection = null;
|
private Direction chosenDirection = null;
|
||||||
private String chosenMode = "";
|
private String chosenMode = "";
|
||||||
private String currentRoom = null;
|
private String currentRoom = null;
|
||||||
|
private String sector = null;
|
||||||
|
private String chosenSector = null;
|
||||||
|
|
||||||
private Card exiledWith;
|
private Card exiledWith;
|
||||||
private Player exiledBy;
|
private Player exiledBy;
|
||||||
@@ -1898,6 +1900,23 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getSector() {
|
||||||
|
return sector;
|
||||||
|
}
|
||||||
|
public void assignSector(String s) {
|
||||||
|
sector = s;
|
||||||
|
view.updateSector(this);
|
||||||
|
}
|
||||||
|
public boolean hasSector() {
|
||||||
|
return sector != null;
|
||||||
|
}
|
||||||
|
public String getChosenSector() {
|
||||||
|
return chosenSector;
|
||||||
|
}
|
||||||
|
public final void setChosenSector(final String s) {
|
||||||
|
chosenSector = s;
|
||||||
|
}
|
||||||
|
|
||||||
// used for cards like Meddling Mage...
|
// used for cards like Meddling Mage...
|
||||||
public final String getNamedCard() {
|
public final String getNamedCard() {
|
||||||
return getChosenName();
|
return getChosenName();
|
||||||
@@ -2183,7 +2202,8 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
|| keyword.equals("Ascend") || keyword.equals("Totem armor")
|
|| keyword.equals("Ascend") || keyword.equals("Totem armor")
|
||||||
|| keyword.equals("Battle cry") || keyword.equals("Devoid") || keyword.equals("Riot")
|
|| keyword.equals("Battle cry") || keyword.equals("Devoid") || keyword.equals("Riot")
|
||||||
|| keyword.equals("Daybound") || keyword.equals("Nightbound")
|
|| keyword.equals("Daybound") || keyword.equals("Nightbound")
|
||||||
|| keyword.equals("Friends forever") || keyword.equals("Choose a Background")) {
|
|| keyword.equals("Friends forever") || keyword.equals("Choose a Background")
|
||||||
|
|| keyword.equals("Space sculptor")) {
|
||||||
sbLong.append(keyword).append(" (").append(inst.getReminderText()).append(")");
|
sbLong.append(keyword).append(" (").append(inst.getReminderText()).append(")");
|
||||||
} else if (keyword.startsWith("Partner:")) {
|
} else if (keyword.startsWith("Partner:")) {
|
||||||
final String[] k = keyword.split(":");
|
final String[] k = keyword.split(":");
|
||||||
|
|||||||
@@ -120,6 +120,14 @@ public class CardProperty {
|
|||||||
if (source.hasChosenCard(card)) {
|
if (source.hasChosenCard(card)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
} else if (property.equals("ChosenSector")) {
|
||||||
|
if (!source.getChosenSector().equals(card.getSector())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (property.equals("DifferentSector")) {
|
||||||
|
if (source.getSector().equals(card.getSector())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
} else if (property.equals("DoubleFaced")) {
|
} else if (property.equals("DoubleFaced")) {
|
||||||
if (!card.isDoubleFaced()) {
|
if (!card.isDoubleFaced()) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -479,6 +479,13 @@ public class CardView extends GameEntityView {
|
|||||||
set(TrackableProperty.Remembered, sb.toString());
|
set(TrackableProperty.Remembered, sb.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getSector() {
|
||||||
|
return get(TrackableProperty.Sector);
|
||||||
|
}
|
||||||
|
void updateSector(Card c) {
|
||||||
|
set(TrackableProperty.Sector, c.getSector());
|
||||||
|
}
|
||||||
|
|
||||||
public String getNamedCard() {
|
public String getNamedCard() {
|
||||||
return get(TrackableProperty.NamedCard);
|
return get(TrackableProperty.NamedCard);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -157,6 +157,7 @@ public enum Keyword {
|
|||||||
SCAVENGE("Scavenge", KeywordWithCost.class, false, "%s, Exile this card from your graveyard: Put a number of +1/+1 counters equal to this card's power on target creature. Scavenge only as a sorcery."),
|
SCAVENGE("Scavenge", KeywordWithCost.class, false, "%s, Exile this card from your graveyard: Put a number of +1/+1 counters equal to this card's power on target creature. Scavenge only as a sorcery."),
|
||||||
SOULBOND("Soulbond", SimpleKeyword.class, true, "You may pair this creature with another unpaired creature when either enters the battlefield. They remain paired for as long as you control both of them."),
|
SOULBOND("Soulbond", SimpleKeyword.class, true, "You may pair this creature with another unpaired creature when either enters the battlefield. They remain paired for as long as you control both of them."),
|
||||||
SOULSHIFT("Soulshift", KeywordWithAmount.class, false, "When this creature dies, you may return target Spirit card with mana value %d or less from your graveyard to your hand."),
|
SOULSHIFT("Soulshift", KeywordWithAmount.class, false, "When this creature dies, you may return target Spirit card with mana value %d or less from your graveyard to your hand."),
|
||||||
|
SPACE_SCULPTOR("Space sculptor", SimpleKeyword.class, true, "CARDNAME divides the battlefield into alpha, beta, and gamma sectors. If a creature isn't assigned to a sector, its controller assigns it to one. Opponents assign first."),
|
||||||
SPECIALIZE("Specialize", KeywordWithCost.class, false, "%s, Choose a color, discard a card of that color or associated basic land type: This card perpetually specializes into that color. Activate only as a sorcery."),
|
SPECIALIZE("Specialize", KeywordWithCost.class, false, "%s, Choose a color, discard a card of that color or associated basic land type: This card perpetually specializes into that color. Activate only as a sorcery."),
|
||||||
SPECTACLE("Spectacle", KeywordWithCost.class, false, "You may cast this spell for its spectacle cost rather than its mana cost if an opponent lost life this turn."),
|
SPECTACLE("Spectacle", KeywordWithCost.class, false, "You may cast this spell for its spectacle cost rather than its mana cost if an opponent lost life this turn."),
|
||||||
SPLICE("Splice", KeywordWithCostAndType.class, false, "As you cast an %2$s spell, you may reveal this card from your hand and pay its splice cost. If you do, add this card's effects to that spell."),
|
SPLICE("Splice", KeywordWithCostAndType.class, false, "As you cast an %2$s spell, you may reveal this card from your hand and pay its splice cost. If you do, add this card's effects to that spell."),
|
||||||
|
|||||||
@@ -183,6 +183,8 @@ public abstract class PlayerController {
|
|||||||
return chooseSomeType(kindOfType, sa, validTypes, invalidTypes, false);
|
return chooseSomeType(kindOfType, sa, validTypes, invalidTypes, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public abstract String chooseSector(Card assignee, String ai);
|
||||||
|
|
||||||
public abstract Object vote(SpellAbility sa, String prompt, List<Object> options, ListMultimap<Object, Player> votes, Player forPlayer);
|
public abstract Object vote(SpellAbility sa, String prompt, List<Object> options, ListMultimap<Object, Player> votes, Player forPlayer);
|
||||||
|
|
||||||
public abstract CardCollectionView getCardsToMulligan(Player firstPlayer);
|
public abstract CardCollectionView getCardsToMulligan(Player firstPlayer);
|
||||||
|
|||||||
@@ -68,6 +68,8 @@ public enum TrackableProperty {
|
|||||||
ChosenDirection(TrackableTypes.EnumType(Direction.class)),
|
ChosenDirection(TrackableTypes.EnumType(Direction.class)),
|
||||||
ChosenEvenOdd(TrackableTypes.EnumType(EvenOdd.class)),
|
ChosenEvenOdd(TrackableTypes.EnumType(EvenOdd.class)),
|
||||||
ChosenMode(TrackableTypes.StringType),
|
ChosenMode(TrackableTypes.StringType),
|
||||||
|
ChosenSector(TrackableTypes.StringType),
|
||||||
|
Sector(TrackableTypes.StringType),
|
||||||
ClassLevel(TrackableTypes.IntegerType),
|
ClassLevel(TrackableTypes.IntegerType),
|
||||||
CurrentRoom(TrackableTypes.StringType),
|
CurrentRoom(TrackableTypes.StringType),
|
||||||
Intensity(TrackableTypes.IntegerType),
|
Intensity(TrackableTypes.IntegerType),
|
||||||
|
|||||||
@@ -55,10 +55,7 @@ import org.apache.commons.lang3.tuple.ImmutablePair;
|
|||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.*;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default harmless implementation for tests.
|
* Default harmless implementation for tests.
|
||||||
@@ -496,6 +493,12 @@ public class PlayerControllerForTests extends PlayerController {
|
|||||||
return chooseItem(validTypes);
|
return chooseItem(validTypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String chooseSector(Card assignee, String ai) {
|
||||||
|
final List<String> sectors = Arrays.asList("Alpha", "Beta", "Gamma");
|
||||||
|
return chooseItem(sectors);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object vote(SpellAbility sa, String prompt, List<Object> options, ListMultimap<Object, Player> votes, Player forPlayer) {
|
public Object vote(SpellAbility sa, String prompt, List<Object> options, ListMultimap<Object, Player> votes, Player forPlayer) {
|
||||||
return chooseItem(options);
|
return chooseItem(options);
|
||||||
|
|||||||
14
forge-gui/res/cardsfolder/upcoming/space_beleren.txt
Normal file
14
forge-gui/res/cardsfolder/upcoming/space_beleren.txt
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
Name:Space Beleren
|
||||||
|
ManaCost:2 W U
|
||||||
|
Types:Legendary Planeswalker Jace
|
||||||
|
Loyalty:3
|
||||||
|
K:Space sculptor
|
||||||
|
A:AB$ Effect | Cost$ AddCounter<1/LOYALTY> | Planeswalker$ True | StaticAbilities$ SectorBlock | SpellDescription$ Creatures in each sector can be blocked this turn only by creatures in the same sector.
|
||||||
|
SVar:SectorBlock:Mode$ CantBlockBy | ValidAttacker$ Creature | ValidBlockerRelative$ Creature.DifferentSector | Description$ Creatures in each sector can be blocked this turn only by creatures in the same sector.
|
||||||
|
A:AB$ ChooseSector | Cost$ SubCounter<1/LOYALTY> | Planeswalker$ True | SubAbility$ DBPutCounterAll | AILogic$ Pump | SpellDescription$ Put a +1/+1 counter on each creature in the sector of your choice.
|
||||||
|
SVar:DBPutCounterAll:DB$ PutCounterAll | ValidCards$ Creature.ChosenSector | CounterType$ P1P1 | StackDescription$ None
|
||||||
|
A:AB$ ChooseSector | Cost$ SubCounter<5/LOYALTY> | Planeswalker$ True | Ultimate$ True | SubAbility$ DBDestroyAll | AILogic$ Destroy | SpellDescription$ Destroy all creatures in the sector of your choice.
|
||||||
|
SVar:DBDestroyAll:DB$ DestroyAll | ValidCards$ Creature.ChosenSector | StackDescription$ None
|
||||||
|
DeckHas:Ability$Counters
|
||||||
|
AI:RemoveDeck:All
|
||||||
|
Oracle:Space sculptor (Space Beleren divides the battlefield into alpha, beta, and gamma sectors. If a creature isn't assigned to a sector, its controller assigns it to one. Opponents assign first.)\n[+1]: Creatures in each sector can be blocked this turn only by creatures in the same sector.\n[−1]: Put a +1/+1 counter on each creature in the sector of your choice.\n[−5]: Destroy all creatures in the sector of your choice.
|
||||||
@@ -527,6 +527,14 @@ public class CardDetailUtil {
|
|||||||
area.append("(Class Level:").append(card.getClassLevel()).append(")");
|
area.append("(Class Level:").append(card.getClassLevel()).append(")");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sector
|
||||||
|
if (!card.getSector().isEmpty()) {
|
||||||
|
if (area.length() != 0) {
|
||||||
|
area.append("\n");
|
||||||
|
}
|
||||||
|
area.append("Sector: ").append(card.getSector());
|
||||||
|
}
|
||||||
|
|
||||||
// a card has something attached to it
|
// a card has something attached to it
|
||||||
if (card.hasCardAttachments()) {
|
if (card.hasCardAttachments()) {
|
||||||
if (area.length() != 0) {
|
if (area.length() != 0) {
|
||||||
|
|||||||
@@ -1364,6 +1364,17 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String chooseSector(Card assignee, String ai) {
|
||||||
|
final List<String> sectors = Arrays.asList("Alpha", "Beta", "Gamma");
|
||||||
|
// turn this into two separate localized prompts
|
||||||
|
String prompt = "Choose sector";
|
||||||
|
if (assignee != null) {
|
||||||
|
prompt = prompt + " for " + assignee.getName();
|
||||||
|
}
|
||||||
|
return getGui().one(prompt, sectors);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object vote(final SpellAbility sa, final String prompt, final List<Object> options,
|
public Object vote(final SpellAbility sa, final String prompt, final List<Object> options,
|
||||||
final ListMultimap<Object, Player> votes, Player forPlayer) {
|
final ListMultimap<Object, Player> votes, Player forPlayer) {
|
||||||
|
|||||||
Reference in New Issue
Block a user