Backup Plan with hot swappable Input

This commit is contained in:
Chris H
2024-06-30 12:30:05 -04:00
parent 7b3d98aabd
commit b03b3517ea
12 changed files with 279 additions and 120 deletions

View File

@@ -31,6 +31,7 @@ import forge.game.ability.AbilityUtils;
import forge.game.ability.ApiType;
import forge.game.card.*;
import forge.game.event.*;
import forge.game.extrahands.BackupPlanService;
import forge.game.keyword.Keyword;
import forge.game.keyword.KeywordInterface;
import forge.game.mulligan.MulliganService;
@@ -2074,7 +2075,10 @@ public class GameAction {
p1.drawCards(p1.getStartingHandSize());
}
// If pl has Backup Plan as a Conspiracy draw that many extra hands
BackupPlanService backupPlans = new BackupPlanService(p1);
if (backupPlans.initializeExtraHands()) {
backupPlans.chooseHand();
}
}
// Choose starting hand for each player with multiple hands

View File

@@ -0,0 +1,78 @@
package forge.game.extrahands;
import com.google.common.collect.Lists;
import forge.game.card.Card;
import forge.game.player.Player;
import forge.game.zone.PlayerZone;
import forge.game.zone.ZoneType;
import java.util.List;
public class BackupPlanService {
private final Player player;
private boolean multipleHands = false;
List<PlayerZone> hands = Lists.newArrayList();
private PlayerZone hand;
public BackupPlanService(Player p1) {
this.player = p1;
}
public boolean initializeExtraHands() {
hand = player.getZone(ZoneType.Hand);
hands.add(hand);
// If pl has Backup Plan as a Conspiracy draw that many extra hands
if (player.getExtraZones() == null) {
return multipleHands;
}
for(PlayerZone extraHand : player.getExtraZones()) {
if (extraHand.getZoneType() == ZoneType.ExtraHand) {
player.drawCards(7, extraHand);
multipleHands = true;
hands.add(extraHand);
// If we figure out how to render the zone in the UI, do it here
}
}
player.updateZoneForView(hand);
return multipleHands;
}
public void chooseHand() {
if (!multipleHands) {
return;
}
PlayerZone library = player.getZone(ZoneType.Library);
// Choose one of the starting hands and recycle the rest
PlayerZone startingHand = player.getController().chooseStartingHand(hands);
if (startingHand == hand) {
for(PlayerZone extraHand : player.getExtraZones()) {
if (extraHand.getZoneType() == ZoneType.ExtraHand) {
for (Card c : Lists.newArrayList(extraHand.getCards().iterator())) {
player.getGame().getAction().moveTo(library, c, null);
}
}
}
} else {
for (Card c : Lists.newArrayList(hand.getCards().iterator())) {
player.getGame().getAction().moveTo(library, c, null);
}
for(PlayerZone extraHand : player.getExtraZones()) {
boolean starting = startingHand.equals(extraHand);
for (Card c : Lists.newArrayList(extraHand.getCards().iterator())) {
if (starting) {
player.getGame().getAction().moveTo(hand, c, null);
} else {
player.getGame().getAction().moveTo(library, c, null);
}
}
}
}
player.resetExtraZones(ZoneType.ExtraHand);
player.updateZoneForView(player.getZone(ZoneType.Hand));
}
}

View File

@@ -156,6 +156,8 @@ public class Player extends GameEntity implements Comparable<Player> {
private List<Card> completedDungeons = new ArrayList<>();
private final Map<ZoneType, PlayerZone> zones = Maps.newEnumMap(ZoneType.class);
private List<PlayerZone> extraZones = null;
private final Map<Long, Integer> adjustLandPlays = Maps.newHashMap();
private final Set<Long> adjustLandPlaysInfinite = Sets.newHashSet();
private Map<Card, Card> maingameCardsMap = Maps.newHashMap();
@@ -1198,9 +1200,18 @@ public class Player extends GameEntity implements Comparable<Player> {
}
public final CardCollectionView drawCards(final int n) {
return drawCards(n, null, AbilityKey.newMap());
return drawCards(n, null, AbilityKey.newMap(), this.getZone(ZoneType.Hand));
}
public final CardCollectionView drawCards(final int n, PlayerZone zone) {
return drawCards(n, null, AbilityKey.newMap(), zone);
}
public final CardCollectionView drawCards(final int n, SpellAbility cause, Map<AbilityKey, Object> params) {
return drawCards(n, cause, params, this.getZone(ZoneType.Hand));
}
public final CardCollectionView drawCards(final int n, SpellAbility cause, Map<AbilityKey, Object> params, PlayerZone zone) {
final CardCollection drawn = new CardCollection();
if (n <= 0) {
return drawn;
@@ -1225,7 +1236,7 @@ public class Player extends GameEntity implements Comparable<Player> {
if (gameStarted && !canDraw()) {
return drawn;
}
drawn.addAll(doDraw(toReveal, cause, params));
drawn.addAll(doDraw(toReveal, cause, params, zone));
}
// reveal multiple drawn cards when playing with the top of the library revealed
@@ -1240,7 +1251,7 @@ public class Player extends GameEntity implements Comparable<Player> {
/**
* @return a CardCollectionView of cards actually drawn
*/
private CardCollectionView doDraw(Map<Player, CardCollection> revealed, SpellAbility sa, Map<AbilityKey, Object> params) {
private CardCollectionView doDraw(Map<Player, CardCollection> revealed, SpellAbility sa, Map<AbilityKey, Object> params, PlayerZone hand) {
final CardCollection drawn = new CardCollection();
final PlayerZone library = getZone(ZoneType.Library);
@@ -1275,7 +1286,7 @@ public class Player extends GameEntity implements Comparable<Player> {
}
}
c = game.getAction().moveToHand(c, cause, params);
c = game.getAction().moveTo(hand, c, cause, params);
drawn.add(c);
// CR 121.6c additional actions can't be performed when draw gets replaced
@@ -1341,6 +1352,15 @@ public class Player extends GameEntity implements Comparable<Player> {
}
}
public final List<PlayerZone> getExtraZones() {
return extraZones;
}
public void resetExtraZones(ZoneType type) {
extraZones.removeIf(z -> z.getZoneType().equals(type));
}
public final CardCollectionView getCardsIn(final ZoneType zoneType) {
return getCardsIn(zoneType, true);
}
@@ -2964,6 +2984,15 @@ public class Player extends GameEntity implements Comparable<Player> {
continue;
}
}
if (Objects.equals(conspire.getName(), "Backup Plan")) {
PlayerZone hand = new PlayerZone(ZoneType.ExtraHand, this);
if (this.extraZones == null) {
this.extraZones = new ArrayList<>();
}
this.extraZones.add(hand);
}
com.add(conspire);
}

View File

@@ -1,20 +1,9 @@
package forge.game.player;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import com.google.common.collect.Lists;
import forge.game.*;
import forge.game.mana.ManaCostBeingPaid;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import com.google.common.base.Predicate;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import forge.LobbyPlayer;
import forge.card.ColorSet;
import forge.card.ICardFace;
@@ -22,12 +11,9 @@ import forge.card.mana.ManaCost;
import forge.card.mana.ManaCostShard;
import forge.deck.Deck;
import forge.deck.DeckSection;
import forge.game.*;
import forge.game.GameOutcome.AnteResult;
import forge.game.card.Card;
import forge.game.card.CardCollection;
import forge.game.card.CardCollectionView;
import forge.game.card.CardView;
import forge.game.card.CounterType;
import forge.game.card.*;
import forge.game.combat.Combat;
import forge.game.cost.Cost;
import forge.game.cost.CostPart;
@@ -35,18 +21,23 @@ import forge.game.cost.CostPartMana;
import forge.game.keyword.KeywordInterface;
import forge.game.mana.Mana;
import forge.game.mana.ManaConversionMatrix;
import forge.game.mana.ManaCostBeingPaid;
import forge.game.replacement.ReplacementEffect;
import forge.game.spellability.AbilitySub;
import forge.game.spellability.OptionalCostValue;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.SpellAbilityStackInstance;
import forge.game.spellability.TargetChoices;
import forge.game.spellability.*;
import forge.game.staticability.StaticAbility;
import forge.game.trigger.WrappedAbility;
import forge.game.zone.PlayerZone;
import forge.game.zone.ZoneType;
import forge.item.PaperCard;
import forge.util.ITriggerEvent;
import forge.util.collect.FCollectionView;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
* A prototype for player controller class
@@ -192,6 +183,7 @@ public abstract class PlayerController {
public abstract CardCollectionView chooseCardsToDelve(int genericAmount, CardCollection grave);
public abstract CardCollectionView chooseCardsToRevealFromHand(int min, int max, CardCollectionView valid);
public abstract List<SpellAbility> chooseSaToActivateFromOpeningHand(List<SpellAbility> usableFromOpeningHand);
public abstract PlayerZone chooseStartingHand(List<PlayerZone> zones);
public abstract Mana chooseManaFromPool(List<Mana> manaChoices);
public abstract String chooseSomeType(String kindOfType, SpellAbility sa, Collection<String> validTypes, List<String> invalidTypes, boolean isOptional);

View File

@@ -1,13 +1,12 @@
package forge.game.zone;
import com.google.common.base.Function;
import forge.util.Localizer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import com.google.common.base.Function;
import forge.util.Localizer;
/**
* The Enum Zone.
*/
@@ -26,6 +25,8 @@ public enum ZoneType {
SchemeDeck(true, "lblSchemeDeckZone"),
PlanarDeck(true, "lblPlanarDeckZone"),
Subgame(true, "lblSubgameZone"),
// ExtraHand is used for Backup Plan for temporary extra hands
ExtraHand(true, "lblHandZone"),
None(true, "lblNoneZone");
public static final List<ZoneType> STATIC_ABILITIES_SOURCE_ZONES = Arrays.asList(Battlefield, Graveyard, Exile, Command, Stack/*, Hand*/);