mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-20 12:48:00 +00:00
space_beleren.txt and support
This commit is contained in:
@@ -1,11 +1,7 @@
|
||||
package forge.ai;
|
||||
|
||||
import java.security.InvalidParameterException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
import forge.game.keyword.Keyword;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
@@ -591,6 +587,12 @@ public class PlayerControllerAi extends PlayerController {
|
||||
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
|
||||
public boolean mulliganKeepHand(Player firstPlayer, int cardsToReturn) {
|
||||
return !ComputerUtil.wantMulligan(player, cardsToReturn);
|
||||
|
||||
@@ -47,6 +47,7 @@ public enum SpellApiToAi {
|
||||
.put(ApiType.ChooseEvenOdd, ChooseEvenOddAi.class)
|
||||
.put(ApiType.ChooseNumber, ChooseNumberAi.class)
|
||||
.put(ApiType.ChoosePlayer, ChoosePlayerAi.class)
|
||||
.put(ApiType.ChooseSector, AlwaysPlayAi.class)
|
||||
.put(ApiType.ChooseSource, ChooseSourceAi.class)
|
||||
.put(ApiType.ChooseType, ChooseTypeAi.class)
|
||||
.put(ApiType.Clash, ClashAi.class)
|
||||
|
||||
@@ -37,6 +37,7 @@ import forge.game.mulligan.MulliganService;
|
||||
import forge.game.player.GameLossReason;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.player.PlayerActionConfirmMode;
|
||||
import forge.game.player.PlayerCollection;
|
||||
import forge.game.player.PlayerPredicates;
|
||||
import forge.game.replacement.ReplacementEffect;
|
||||
import forge.game.replacement.ReplacementResult;
|
||||
@@ -1317,7 +1318,11 @@ public class GameAction {
|
||||
CardCollection desCreats = null;
|
||||
CardCollection unAttachList = new CardCollection();
|
||||
CardCollection sacrificeList = new CardCollection();
|
||||
PlayerCollection spaceSculptors = new PlayerCollection();
|
||||
for (final Card c : game.getCardsIn(ZoneType.Battlefield)) {
|
||||
if (c.hasKeyword(Keyword.SPACE_SCULPTOR)) {
|
||||
spaceSculptors.add(c.getController());
|
||||
}
|
||||
if (c.isCreature()) {
|
||||
// Rule 704.5f - Put into grave (no regeneration) for toughness <= 0
|
||||
if (c.getNetToughness() <= 0) {
|
||||
@@ -1393,6 +1398,9 @@ public class GameAction {
|
||||
}
|
||||
|
||||
for (Player p : game.getPlayers()) {
|
||||
if (!spaceSculptors.isEmpty() && !spaceSculptors.contains(p)) {
|
||||
checkAgain |= stateBasedAction704_5u(p);
|
||||
}
|
||||
if (handleLegendRule(p, noRegCreats)) {
|
||||
checkAgain = true;
|
||||
}
|
||||
@@ -1412,6 +1420,11 @@ public class GameAction {
|
||||
checkAgain = true;
|
||||
}
|
||||
}
|
||||
if (!spaceSculptors.isEmpty()) {
|
||||
for (Player p : spaceSculptors) {
|
||||
checkAgain |= stateBasedAction704_5u(p);
|
||||
}
|
||||
}
|
||||
// 704.5m World rule
|
||||
checkAgain |= handleWorldRule(noRegCreats);
|
||||
// only check static abilities once after destroying all the creatures
|
||||
@@ -1551,6 +1564,26 @@ public class GameAction {
|
||||
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) {
|
||||
if (c.isRealCommander() && c.canMoveToCommandZone()) {
|
||||
// FIXME: need to flush the tracker to make sure the Commander is properly updated
|
||||
|
||||
@@ -45,6 +45,7 @@ public enum ApiType {
|
||||
ChooseEvenOdd (ChooseEvenOddEffect.class),
|
||||
ChooseNumber (ChooseNumberEffect.class),
|
||||
ChoosePlayer (ChoosePlayerEffect.class),
|
||||
ChooseSector (ChooseSectorEffect.class),
|
||||
ChooseSource (ChooseSourceEffect.class),
|
||||
ChooseType (ChooseTypeEffect.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 String chosenMode = "";
|
||||
private String currentRoom = null;
|
||||
private String sector = null;
|
||||
private String chosenSector = null;
|
||||
|
||||
private Card exiledWith;
|
||||
private Player exiledBy;
|
||||
@@ -1898,6 +1900,23 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
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...
|
||||
public final String getNamedCard() {
|
||||
return getChosenName();
|
||||
@@ -2183,7 +2202,8 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
|| keyword.equals("Ascend") || keyword.equals("Totem armor")
|
||||
|| keyword.equals("Battle cry") || keyword.equals("Devoid") || keyword.equals("Riot")
|
||||
|| 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(")");
|
||||
} else if (keyword.startsWith("Partner:")) {
|
||||
final String[] k = keyword.split(":");
|
||||
|
||||
@@ -120,6 +120,14 @@ public class CardProperty {
|
||||
if (source.hasChosenCard(card)) {
|
||||
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")) {
|
||||
if (!card.isDoubleFaced()) {
|
||||
return false;
|
||||
|
||||
@@ -479,6 +479,13 @@ public class CardView extends GameEntityView {
|
||||
set(TrackableProperty.Remembered, sb.toString());
|
||||
}
|
||||
|
||||
public String getSector() {
|
||||
return get(TrackableProperty.Sector);
|
||||
}
|
||||
void updateSector(Card c) {
|
||||
set(TrackableProperty.Sector, c.getSector());
|
||||
}
|
||||
|
||||
public String getNamedCard() {
|
||||
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."),
|
||||
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."),
|
||||
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."),
|
||||
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."),
|
||||
|
||||
@@ -183,6 +183,8 @@ public abstract class PlayerController {
|
||||
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 CardCollectionView getCardsToMulligan(Player firstPlayer);
|
||||
|
||||
@@ -68,6 +68,8 @@ public enum TrackableProperty {
|
||||
ChosenDirection(TrackableTypes.EnumType(Direction.class)),
|
||||
ChosenEvenOdd(TrackableTypes.EnumType(EvenOdd.class)),
|
||||
ChosenMode(TrackableTypes.StringType),
|
||||
ChosenSector(TrackableTypes.StringType),
|
||||
Sector(TrackableTypes.StringType),
|
||||
ClassLevel(TrackableTypes.IntegerType),
|
||||
CurrentRoom(TrackableTypes.StringType),
|
||||
Intensity(TrackableTypes.IntegerType),
|
||||
|
||||
@@ -55,10 +55,7 @@ import org.apache.commons.lang3.tuple.ImmutablePair;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Default harmless implementation for tests.
|
||||
@@ -496,6 +493,12 @@ public class PlayerControllerForTests extends PlayerController {
|
||||
return chooseItem(validTypes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String chooseSector(Card assignee, String ai) {
|
||||
final List<String> sectors = Arrays.asList("Alpha", "Beta", "Gamma");
|
||||
return chooseItem(sectors);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object vote(SpellAbility sa, String prompt, List<Object> options, ListMultimap<Object, Player> votes, Player forPlayer) {
|
||||
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(")");
|
||||
}
|
||||
|
||||
// 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
|
||||
if (card.hasCardAttachments()) {
|
||||
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
|
||||
public Object vote(final SpellAbility sa, final String prompt, final List<Object> options,
|
||||
final ListMultimap<Object, Player> votes, Player forPlayer) {
|
||||
|
||||
Reference in New Issue
Block a user