Full Control

This commit is contained in:
tool4EvEr
2023-10-25 21:58:39 +02:00
parent 0ed6a133e2
commit ebbbbc847b
20 changed files with 119 additions and 23 deletions

View File

@@ -36,6 +36,7 @@ import forge.game.phase.PhaseType;
import forge.game.player.*;
import forge.game.replacement.ReplacementEffect;
import forge.game.spellability.*;
import forge.game.staticability.StaticAbility;
import forge.game.trigger.WrappedAbility;
import forge.game.zone.ZoneType;
import forge.item.PaperCard;
@@ -980,6 +981,12 @@ public class PlayerControllerAi extends PlayerController {
return brains.chooseSingleReplacementEffect(possibleReplacers);
}
@Override
public StaticAbility chooseSingleStaticAbility(String prompt, List<StaticAbility> possibleStatics) {
// only matters in corner cases
return Iterables.getFirst(possibleStatics, null);
}
@Override
public String chooseProtectionType(String string, SpellAbility sa, List<String> choices) {
String choice = choices.get(0);
@@ -1436,7 +1443,6 @@ public class PlayerControllerAi extends PlayerController {
@Override
public int chooseNumberForKeywordCost(SpellAbility sa, Cost cost, KeywordInterface keyword, String prompt, int max) {
// TODO: improve the logic depending on the keyword and the playability of the cost-modified SA (enough targets present etc.)
if (keyword.getKeyword() == Keyword.CASUALTY
&& "true".equalsIgnoreCase(sa.getHostCard().getSVar("AINoCasualtyPayment"))) {
// TODO: Grisly Sigil - currently will be misplayed if Casualty is paid (the cost is always paid, targeting is wrong).
@@ -1460,6 +1466,11 @@ public class PlayerControllerAi extends PlayerController {
return chosenAmount;
}
@Override
public int chooseNumberForCostReduction(final SpellAbility sa, final int min, final int max) {
return max;
}
@Override
public CardCollection chooseCardsForEffectMultiple(Map<String, CardCollection> validMap, SpellAbility sa, String title, boolean isOptional) {
CardCollection choices = new CardCollection();

View File

@@ -34,6 +34,7 @@ import forge.game.spellability.TargetChoices;
import forge.game.staticability.StaticAbility;
import forge.game.zone.Zone;
import forge.game.zone.ZoneType;
import forge.util.Localizer;
public class CostAdjustment {
@@ -169,7 +170,7 @@ public class CostAdjustment {
// If cardsToDelveOut is null, will immediately exile the delved cards and remember them on the host card.
// Otherwise, will return them in cardsToDelveOut and the caller is responsible for doing the above.
public static final void adjust(ManaCostBeingPaid cost, final SpellAbility sa, CardCollection cardsToDelveOut, boolean test) {
if (sa.isTrigger()) {
if (sa.isTrigger() || sa.isReplacementAbility()) {
return;
}
@@ -212,8 +213,10 @@ public class CostAdjustment {
sumGeneric += AbilityUtils.calculateAmount(originalCard, sa.getParam("ReduceCost"), sa);
}
for (final StaticAbility stAb : reduceAbilities) {
sumGeneric += applyReduceCostAbility(stAb, sa, cost, sumGeneric);
while (!reduceAbilities.isEmpty()) {
StaticAbility choice = sa.getActivatingPlayer().getController().chooseSingleStaticAbility(Localizer.getInstance().getMessage("lblChooseCostReduction"), reduceAbilities);
reduceAbilities.remove(choice);
sumGeneric += applyReduceCostAbility(choice, sa, cost, sumGeneric);
}
// need to reduce generic extra because of 2 hybrid mana
cost.decreaseGenericMana(sumGeneric);
@@ -402,6 +405,10 @@ public class CostAdjustment {
value = AbilityUtils.calculateAmount(hostCard, amount, staticAbility);
}
if (staticAbility.hasParam("UpTo")) {
value = sa.getActivatingPlayer().getController().chooseNumberForCostReduction(sa, 0, value);
}
if (!staticAbility.hasParam("Cost") && !staticAbility.hasParam("Color")) {
int minMana = 0;
if (staticAbility.hasParam("MinMana")) {

View File

@@ -39,6 +39,7 @@ import forge.game.spellability.OptionalCostValue;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.SpellAbilityStackInstance;
import forge.game.spellability.TargetChoices;
import forge.game.staticability.StaticAbility;
import forge.game.trigger.WrappedAbility;
import forge.game.zone.ZoneType;
import forge.item.PaperCard;
@@ -201,6 +202,7 @@ public abstract class PlayerController {
public abstract boolean payManaOptional(Card card, Cost cost, SpellAbility sa, String prompt, ManaPaymentPurpose purpose);
public abstract int chooseNumberForCostReduction(final SpellAbility sa, final int min, final int max);
public abstract int chooseNumberForKeywordCost(SpellAbility sa, Cost cost, KeywordInterface keyword, String prompt, int max);
public boolean addKeywordCost(SpellAbility sa, Cost cost, KeywordInterface keyword, String prompt) {
return chooseNumberForKeywordCost(sa, cost, keyword, prompt, 1) == 1;
@@ -232,6 +234,7 @@ public abstract class PlayerController {
public abstract boolean confirmPayment(CostPart costPart, String string, SpellAbility sa);
public abstract ReplacementEffect chooseSingleReplacementEffect(String prompt, List<ReplacementEffect> possibleReplacers);
public abstract StaticAbility chooseSingleStaticAbility(String prompt, List<StaticAbility> possibleReplacers);
public abstract String chooseProtectionType(String string, SpellAbility sa, List<String> choices);
// these 4 need some refining.
@@ -269,6 +272,11 @@ public abstract class PlayerController {
public abstract List<Card> chooseCardsForZoneChange(ZoneType destination, List<ZoneType> origin, SpellAbility sa, CardCollection fetchList, int min, int max, DelayedReveal delayedReveal, String selectPrompt, Player decider);
public boolean isFullControl() {
return false;
}
public void setFullControl(boolean full) {}
public abstract void autoPassCancel();
public abstract void awaitNextInput();

View File

@@ -35,6 +35,7 @@ import forge.game.mana.ManaCostBeingPaid;
import forge.game.player.*;
import forge.game.replacement.ReplacementEffect;
import forge.game.spellability.*;
import forge.game.staticability.StaticAbility;
import forge.game.trigger.WrappedAbility;
import forge.game.zone.ZoneType;
import forge.gamesimulationtests.util.card.CardSpecification;
@@ -529,6 +530,12 @@ public class PlayerControllerForTests extends PlayerController {
return Iterables.getFirst(possibleReplacers, null);
}
@Override
public StaticAbility chooseSingleStaticAbility(String prompt, List<StaticAbility> possibleStatics) {
// TODO Auto-generated method stub
return Iterables.getFirst(possibleStatics, null);
}
@Override
public String chooseProtectionType(String string, SpellAbility sa, List<String> choices) {
return choices.get(0);
@@ -720,12 +727,16 @@ public class PlayerControllerForTests extends PlayerController {
}
@Override
public int chooseNumberForKeywordCost(SpellAbility sa, Cost cost, KeywordInterface keyword, String prompt,
int max) {
public int chooseNumberForKeywordCost(SpellAbility sa, Cost cost, KeywordInterface keyword, String prompt, int max) {
// TODO Auto-generated method stub
return 0;
}
@Override
public int chooseNumberForCostReduction(final SpellAbility sa, final int min, final int max) {
return max;
}
@Override
public CardCollection chooseCardsForEffectMultiple(Map<String, CardCollection> validMap, SpellAbility sa, String title, boolean isOptional) {
// TODO Auto-generated method stub

View File

@@ -1,7 +1,7 @@
Name:Catalyst Stone
ManaCost:2
Types:Artifact
S:Mode$ ReduceCost | ValidCard$ Card | ValidSpell$ Spell.Flashback | Activator$ You | Amount$ 2 | Description$ Flashback costs you pay cost up to {2} less.
S:Mode$ ReduceCost | ValidCard$ Card | ValidSpell$ Spell.Flashback | Activator$ You | Amount$ 2 | UpTo$ True | Description$ Flashback costs you pay cost up to {2} less.
S:Mode$ RaiseCost | ValidCard$ Card | ValidSpell$ Spell.Flashback | Activator$ Opponent | Amount$ 2 | Description$ Flashback costs your opponents pay cost {2} more.
AI:RemoveDeck:Random
Oracle:Flashback costs you pay cost up to {2} less.\nFlashback costs your opponents pay cost {2} more.

View File

@@ -1,6 +1,6 @@
Name:Fluctuator
ManaCost:2
Types:Artifact
S:Mode$ ReduceCost | ValidCard$ Card | ValidSpell$ Activated.Cycling | Activator$ You | Amount$ 2 | Description$ Cycling abilities you activate cost you up to {2} less to activate.
S:Mode$ ReduceCost | ValidCard$ Card | ValidSpell$ Activated.Cycling | Activator$ You | Amount$ 2 | UpTo$ True | Description$ Cycling abilities you activate cost you up to {2} less to activate.
AI:RemoveDeck:Random
Oracle:Cycling abilities you activate cost up to {2} less to activate.

View File

@@ -1,6 +1,6 @@
Name:Mana Matrix
ManaCost:6
Types:Artifact
S:Mode$ ReduceCost | ValidCard$ Instant,Enchantment | Type$ Spell | Activator$ You | Amount$ 2 | Description$ Instant and enchantment spells you cast cost up to {2} less to cast.
S:Mode$ ReduceCost | ValidCard$ Instant,Enchantment | Type$ Spell | Activator$ You | Amount$ 2 | UpTo$ True | Description$ Instant and enchantment spells you cast cost up to {2} less to cast.
DeckNeeds:Type$Enchantment|Instant
Oracle:Instant and enchantment spells you cast cost up to {2} less to cast.

View File

@@ -1,5 +1,5 @@
Name:Planar Gate
ManaCost:6
Types:Artifact
S:Mode$ ReduceCost | ValidCard$ Creature | Type$ Spell | Activator$ You | Amount$ 2 | Description$ Creature spells you cast cost up to {2} less to cast.
S:Mode$ ReduceCost | ValidCard$ Creature | Type$ Spell | Activator$ You | Amount$ 2 | UpTo$ True | Description$ Creature spells you cast cost up to {2} less to cast.
Oracle:Creature spells you cast cost up to {2} less to cast.

View File

@@ -1,5 +1,5 @@
Name:Stone Calendar
ManaCost:5
Types:Artifact
S:Mode$ ReduceCost | Type$ Spell | Activator$ You | Amount$ 1 | Description$ Spells you cast cost up to {1} less to cast.
S:Mode$ ReduceCost | Type$ Spell | Activator$ You | Amount$ 1 | UpTo$ True | Description$ Spells you cast cost up to {1} less to cast.
Oracle:Spells you cast cost up to {1} less to cast.

View File

@@ -1,5 +1,5 @@
Name:Urza's Filter
ManaCost:4
Types:Artifact
S:Mode$ ReduceCost | ValidCard$ Card.MultiColor | Type$ Spell | Amount$ 2 | Description$ Multicolored spells cost up to {2} less to cast.
S:Mode$ ReduceCost | ValidCard$ Card.MultiColor | Type$ Spell | Amount$ 2 | UpTo$ True | Description$ Multicolored spells cost up to {2} less to cast.
Oracle:Multicolored spells cost up to {2} less to cast.

View File

@@ -1405,6 +1405,8 @@ lblSacrifice=Opfern
lblLookCardInPlayerZone=Schaue nach Karten in {0} {1}
lblPlayerZone={0} {1}
lblActionFromPlayerDeck={0} von {1} Deck
lblChooseCostReduction=Choose which cost reduction to apply first
lblChooseAmountCostReduction=Choose amount of cost reduction
#AbstractGuiGame.java
lblConcedeCurrentGame=Das Spiel wird als verloren gewertet.\n\nTrotzdem aufgeben?
lblConcedeTitle=Spiel verloren geben?

View File

@@ -1410,6 +1410,8 @@ lblSacrifice=Sacrifice
lblLookCardInPlayerZone=Looking at cards in {0} {1}
lblPlayerZone={0} {1}
lblActionFromPlayerDeck={0} from {1} Deck
lblChooseCostReduction=Choose which cost reduction to apply first
lblChooseAmountCostReduction=Choose amount of cost reduction
#AbstractGuiGame.java
lblConcedeCurrentGame=This will concede the current game and you will lose.\n\nConcede anyway?
lblConcedeTitle=Concede Game?

View File

@@ -1406,6 +1406,8 @@ lblSacrifice=Sacrificio
lblLookCardInPlayerZone=Mirando las cartas en {1} {0}
lblPlayerZone={0} {1}
lblActionFromPlayerDeck={0} del Mazo {1}
lblChooseCostReduction=Choose which cost reduction to apply first
lblChooseAmountCostReduction=Choose amount of cost reduction
#AbstractGuiGame.java
lblConcedeCurrentGame=Concederás la partida actual y perderás.\n\n¿Conceder de todos modos?
lblConcedeTitle=¿Conceder partida?

View File

@@ -1409,6 +1409,8 @@ lblSacrifice=Sacrifice
lblLookCardInPlayerZone=Regarder des cartes dans {0} {1}
lblPlayerZone={0} {1}
lblActionFromPlayerDeck={0} de la plate-forme {1}
lblChooseCostReduction=Choose which cost reduction to apply first
lblChooseAmountCostReduction=Choose amount of cost reduction
#AbstractGuiGame.java
lblConcedeCurrentGame=Ceci concédera la partie en cours et vous perdrez.\n\nConcéder quand même ?
lblConcedeTitle=Concéder le jeu ?

View File

@@ -1406,6 +1406,8 @@ lblSacrifice=Sacrifica
lblLookCardInPlayerZone=Stai guardando le carte in {1} di {0}
lblPlayerZone={1} di {0}
lblActionFromPlayerDeck={0} dal mazzo di {1}
lblChooseCostReduction=Choose which cost reduction to apply first
lblChooseAmountCostReduction=Choose amount of cost reduction
#AbstractGuiGame.java
lblConcedeCurrentGame=Questo concederà la partita in corso e perderai. \n \nConcedi comunque?
lblConcedeTitle=Concedere la partita?

View File

@@ -1407,6 +1407,8 @@ lblSacrifice=生け贄
lblLookCardInPlayerZone={0} {1}のカードを見る
lblPlayerZone={0} {1}
lblActionFromPlayerDeck={1}デッキから{0}
lblChooseCostReduction=Choose which cost reduction to apply first
lblChooseAmountCostReduction=Choose amount of cost reduction
#AbstractGuiGame.java
lblConcedeCurrentGame=これは現在のゲームを投了し、負けます。とにかく投了しますか?
lblConcedeTitle=ゲームを投了する?

View File

@@ -1437,6 +1437,8 @@ lblSacrifice=Sacrifice
lblLookCardInPlayerZone=Olhando as cartas em {0} {1}
lblPlayerZone={0} {1}
lblActionFromPlayerDeck={0} do deck {1}
lblChooseCostReduction=Choose which cost reduction to apply first
lblChooseAmountCostReduction=Choose amount of cost reduction
#AbstractGuiGame.java
lblConcedeCurrentGame=Isto concede o jogo atual e você perderá.\n\
\n\

View File

@@ -1410,6 +1410,8 @@ lblSacrifice=牺牲
lblLookCardInPlayerZone=查看{0}的{1}中的牌
lblPlayerZone={0}的{1}
lblActionFromPlayerDeck=从{1}的套牌{0}
lblChooseCostReduction=Choose which cost reduction to apply first
lblChooseAmountCostReduction=Choose amount of cost reduction
#AbstractGuiGame.java
lblConcedeCurrentGame=这局游戏认输。\n\n确认吗
lblConcedeTitle=这局游戏认输?

View File

@@ -23,6 +23,7 @@ import java.util.List;
import forge.game.Game;
import forge.game.card.Card;
import forge.game.player.Player;
import forge.game.player.PlayerController;
import forge.game.spellability.LandAbility;
import forge.game.spellability.SpellAbility;
import forge.localinstance.properties.ForgePreferences.FPref;
@@ -127,6 +128,14 @@ public class InputPassPriority extends InputSyncronizedBase {
public List<SpellAbility> getChosenSa() { return chosenSa; }
@Override
protected final void onPlayerSelected(Player selected, final ITriggerEvent triggerEvent) {
PlayerController pc = selected.getController();
if (pc.isGuiPlayer()) {
pc.setFullControl(!pc.isFullControl());
}
}
@Override
protected boolean onCardSelected(final Card card, final List<Card> otherCardsToSelect, final ITriggerEvent triggerEvent) {
//remove unplayable unless triggerEvent specified, in which case unplayable may be shown as disabled options

View File

@@ -100,6 +100,7 @@ import forge.game.spellability.SpellAbility;
import forge.game.spellability.SpellAbilityStackInstance;
import forge.game.spellability.SpellAbilityView;
import forge.game.spellability.TargetChoices;
import forge.game.staticability.StaticAbility;
import forge.game.trigger.Trigger;
import forge.game.trigger.WrappedAbility;
import forge.game.zone.MagicStack;
@@ -160,6 +161,8 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont
private boolean mayLookAtAllCards = false;
private boolean disableAutoYields = false;
private boolean fullControl = false;
private IGuiGame gui;
protected final InputQueue inputQueue;
@@ -205,7 +208,6 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont
public boolean getDisableAutoYields() {
return disableAutoYields;
}
public void setDisableAutoYields(final boolean disableAutoYields0) {
disableAutoYields = disableAutoYields0;
}
@@ -214,6 +216,15 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont
public boolean mayLookAtAllCards() {
return mayLookAtAllCards;
}
/**
* Set this to {@code true} to enable this player to see all cards any other
* player can see.
*
* @param mayLookAtAllCards the mayLookAtAllCards to set
*/
public void setMayLookAtAllCards(final boolean mayLookAtAllCards) {
this.mayLookAtAllCards = mayLookAtAllCards;
}
private final ArrayList<Card> tempShownCards = new ArrayList<>();
@@ -255,14 +266,13 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont
tempShownCards.clear();
}
/**
* Set this to {@code true} to enable this player to see all cards any other
* player can see.
*
* @param mayLookAtAllCards the mayLookAtAllCards to set
*/
public void setMayLookAtAllCards(final boolean mayLookAtAllCards) {
this.mayLookAtAllCards = mayLookAtAllCards;
@Override
public boolean isFullControl() {
return fullControl;
}
@Override
public void setFullControl(boolean full) {
fullControl = full;
}
/**
@@ -1883,6 +1893,23 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont
return first;
}
@Override
public StaticAbility chooseSingleStaticAbility(final String prompt, final List<StaticAbility> possibleStatics) {
final StaticAbility first = possibleStatics.get(0);
if (possibleStatics.size() == 1 || !fullControl) {
return first;
}
final String firstStr = first.toString();
for (int i = 1; i < possibleStatics.size(); i++) {
// prompt user if there are multiple different options
if (!possibleStatics.get(i).toString().equals(firstStr)) {
return getGui().one(prompt, possibleStatics);
}
}
// return first option without prompting if all options are the same
return first;
}
@Override
public String chooseProtectionType(final String string, final SpellAbility sa, final List<String> choices) {
return getGui().one(string, choices);
@@ -3343,8 +3370,7 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont
* spellability.SpellAbility, java.util.List)
*/
@Override
public List<OptionalCostValue> chooseOptionalCosts(SpellAbility choosen,
List<OptionalCostValue> optionalCost) {
public List<OptionalCostValue> chooseOptionalCosts(SpellAbility choosen, List<OptionalCostValue> optionalCost) {
return getGui().many(localizer.getMessage("lblChooseOptionalCosts"), localizer.getMessage("lblOptionalCosts"), 0, optionalCost.size(),
optionalCost, choosen.getHostCard().getView());
}
@@ -3367,6 +3393,14 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont
return v == null ? 0 : v.intValue();
}
@Override
public int chooseNumberForCostReduction(final SpellAbility sa, final int min, final int max) {
if (fullControl) {
return chooseNumber(sa, localizer.getMessage("lblChooseAmountCostReduction"), min, max);
}
return max;
}
@Override
public CardCollection chooseCardsForEffectMultiple(Map<String, CardCollection> validMap, SpellAbility sa, String title, boolean isOptional) {
CardCollection result = new CardCollection();