From 341a241a824e0710c0c784999002dac8303b09ed Mon Sep 17 00:00:00 2001 From: tehdiplomat Date: Tue, 28 Aug 2018 12:24:04 -0400 Subject: [PATCH 1/3] Fixing Mana types being inappropriately referenced in relation to Matrices --- .../src/main/java/forge/ai/ComputerUtilMana.java | 4 ++++ .../src/main/java/forge/card/MagicColor.java | 9 --------- .../src/main/java/forge/card/mana/ManaAtom.java | 5 ++++- .../java/forge/game/ability/AbilityUtils.java | 3 ++- .../java/forge/game/mana/ManaCostBeingPaid.java | 6 ++++-- .../src/main/java/forge/game/mana/ManaPool.java | 15 +++++++++------ 6 files changed, 23 insertions(+), 19 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java b/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java index 98df78bf24a..02ffc322634 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java @@ -913,6 +913,10 @@ public class ComputerUtilMana { // Make mana needed to avoid negative effect a mandatory cost for the AI for (String manaPart : card.getSVar("ManaNeededToAvoidNegativeEffect").split(",")) { // convert long color strings to short color strings + if (manaPart.isEmpty()) { + continue; + } + byte mask = ManaAtom.fromName(manaPart); // make mana mandatory for AI diff --git a/forge-core/src/main/java/forge/card/MagicColor.java b/forge-core/src/main/java/forge/card/MagicColor.java index bc452e19520..37e6294a056 100644 --- a/forge-core/src/main/java/forge/card/MagicColor.java +++ b/forge-core/src/main/java/forge/card/MagicColor.java @@ -103,15 +103,6 @@ public final class MagicColor { } } - public static int getIndexOfFirstColor(final byte color){ - for (int i = 0; i < NUMBER_OR_COLORS; i++) { - if ((color & WUBRG[i]) != 0) { - return i; - } - } - return -1; // colorless - } - /** * The Interface Color. */ diff --git a/forge-core/src/main/java/forge/card/mana/ManaAtom.java b/forge-core/src/main/java/forge/card/mana/ManaAtom.java index 1416139cda5..5a38b3a3310 100644 --- a/forge-core/src/main/java/forge/card/mana/ManaAtom.java +++ b/forge-core/src/main/java/forge/card/mana/ManaAtom.java @@ -14,6 +14,9 @@ public abstract class ManaAtom { public static final byte[] MANACOLORS = new byte[] { WHITE, BLUE, BLACK, RED, GREEN }; public static final byte[] MANATYPES = new byte[] { WHITE, BLUE, BLACK, RED, GREEN, COLORLESS }; + public static final byte ALL_MANA_COLORS = WHITE | BLUE | BLACK | RED | GREEN; + public static final byte ALL_MANA_TYPES = ALL_MANA_COLORS | COLORLESS; + public static final int GENERIC = 1 << 6; // Below here skip due to byte conversion shenanigans @@ -62,6 +65,6 @@ public abstract class ManaAtom { return i; } } - return -1; // colorless + return -1; // somehow the mana is not colored or colorless? } } \ No newline at end of file diff --git a/forge-game/src/main/java/forge/game/ability/AbilityUtils.java b/forge-game/src/main/java/forge/game/ability/AbilityUtils.java index 3401a5d6a80..f3b9d374e66 100644 --- a/forge-game/src/main/java/forge/game/ability/AbilityUtils.java +++ b/forge-game/src/main/java/forge/game/ability/AbilityUtils.java @@ -1655,7 +1655,8 @@ public class AbilityUtils { String convertTo = params.get(key); byte convertByte = 0; if ("All".equals(convertTo)) { - convertByte = ColorSet.ALL_COLORS.getColor(); + // IMPORTANT! We need to use Mana Color here not Card Color. + convertByte = ManaAtom.ALL_MANA_TYPES; } else { for (final String convertColor : convertTo.split(",")) { convertByte |= ManaAtom.fromName(convertColor); diff --git a/forge-game/src/main/java/forge/game/mana/ManaCostBeingPaid.java b/forge-game/src/main/java/forge/game/mana/ManaCostBeingPaid.java index f1f43259799..f5c381fb9ca 100644 --- a/forge-game/src/main/java/forge/game/mana/ManaCostBeingPaid.java +++ b/forge-game/src/main/java/forge/game/mana/ManaCostBeingPaid.java @@ -586,6 +586,7 @@ public class ManaCostBeingPaid { // Boolean addX used to add Xs into the returned value final StringBuilder sb = new StringBuilder(); + // TODO Prepend a line about paying with any type/color if available if (addX) { for (int i = 0; i < this.getXcounter(); i++) { sb.append("{X}"); @@ -595,10 +596,11 @@ public class ManaCostBeingPaid { int nGeneric = getGenericManaAmount(); List shards = new ArrayList(unpaidShards.keySet()); - if (pool != null) { //replace shards with generic mana if they can be paid with any color mana + // TODO Fix this. Should we really be changing Shards here? + if (false && pool != null) { //replace shards with generic mana if they can be paid with any color mana for (int i = 0; i < shards.size(); i++) { ManaCostShard shard = shards.get(i); - if (shard != ManaCostShard.GENERIC && pool.getPossibleColorUses(shard.getColorMask()) == MagicColor.ALL_COLORS) { + if (shard != ManaCostShard.GENERIC && pool.getPossibleColorUses(shard.getColorMask()) == ManaAtom.ALL_MANA_TYPES) { nGeneric += unpaidShards.get(shard).totalCount; shards.remove(i); i--; diff --git a/forge-game/src/main/java/forge/game/mana/ManaPool.java b/forge-game/src/main/java/forge/game/mana/ManaPool.java index 2d2de63d968..d76c2145973 100644 --- a/forge-game/src/main/java/forge/game/mana/ManaPool.java +++ b/forge-game/src/main/java/forge/game/mana/ManaPool.java @@ -22,7 +22,6 @@ import com.google.common.collect.Lists; import com.google.common.collect.Multimap; import forge.GameCommand; -import forge.card.ColorSet; import forge.card.MagicColor; import forge.card.mana.ManaAtom; import forge.card.mana.ManaCostShard; @@ -364,7 +363,7 @@ public class ManaPool implements Iterable { public void adjustColorReplacement(byte originalColor, byte replacementColor, boolean additive) { // Fix the index without hardcodes - int rowIdx = MagicColor.getIndexOfFirstColor(originalColor); + int rowIdx = ManaAtom.getIndexOfFirstManaType(originalColor); rowIdx = rowIdx < 0 ? identityMatrix.length - 1 : rowIdx; if (additive) { colorConversionMatrix[rowIdx] |= replacementColor; @@ -375,17 +374,19 @@ public class ManaPool implements Iterable { } public void restoreColorReplacements() { + // By default each color can only be paid by itself ( {G} -> {G}, {C} -> {C} for (int i = 0; i < colorConversionMatrix.length; i++) { colorConversionMatrix[i] = identityMatrix[i]; } + // By default all mana types are unrestricted for (int i = 0; i < colorRestrictionMatrix.length; i++) { - colorRestrictionMatrix[i] = ColorSet.ALL_COLORS.getColor(); + colorRestrictionMatrix[i] = ManaAtom.ALL_MANA_TYPES; } } public byte getPossibleColorUses(byte color) { // Take the current conversion value, AND with restrictions to get mana usage - int rowIdx = MagicColor.getIndexOfFirstColor(color); + int rowIdx = ManaAtom.getIndexOfFirstManaType(color); int matrixIdx = rowIdx < 0 ? identityMatrix.length - 1 : rowIdx; byte colorUse = colorConversionMatrix[matrixIdx]; @@ -394,14 +395,16 @@ public class ManaPool implements Iterable { } public boolean canPayForShardWithColor(ManaCostShard shard, byte color) { + // TODO Debug this for Paying Gonti, byte line = getPossibleColorUses(color); - for (int i = 0; i < MagicColor.NUMBER_OR_COLORS; i++) { - byte outColor = MagicColor.WUBRG[i]; + + for(byte outColor : ManaAtom.MANATYPES) { if ((line & outColor) != 0 && shard.canBePaidWithManaOfColor(outColor)) { return true; } } + // TODO The following may not be needed anymore? if (((color & (byte) ManaAtom.COLORLESS) != 0) && shard.canBePaidWithManaOfColor((byte) (byte)ManaAtom.COLORLESS)) { return true; } From 29ff9d3c92a124d99df59bf4799229eeb73d0c11 Mon Sep 17 00:00:00 2001 From: Chris H Date: Sun, 7 Oct 2018 22:07:53 -0400 Subject: [PATCH 2/3] Pull ManaConversion out to be reused for SpellAbilityStackInstance --- .../src/main/java/forge/game/GameAction.java | 3 +- .../java/forge/game/ability/AbilityUtils.java | 3 +- .../game/mana/IManaConversionMatrix.java | 11 +++++ .../game/mana/ManaConversionService.java | 36 ++++++++++++++++ .../main/java/forge/game/mana/ManaPool.java | 43 ++----------------- .../forge/player/HumanPlaySpellAbility.java | 25 +++++------ 6 files changed, 66 insertions(+), 55 deletions(-) create mode 100644 forge-game/src/main/java/forge/game/mana/IManaConversionMatrix.java create mode 100644 forge-game/src/main/java/forge/game/mana/ManaConversionService.java diff --git a/forge-game/src/main/java/forge/game/GameAction.java b/forge-game/src/main/java/forge/game/GameAction.java index 37a1a96a89e..ab9d96e0a33 100644 --- a/forge-game/src/main/java/forge/game/GameAction.java +++ b/forge-game/src/main/java/forge/game/GameAction.java @@ -29,6 +29,7 @@ import forge.game.ability.effects.AttachEffect; import forge.game.card.*; import forge.game.event.*; import forge.game.keyword.KeywordInterface; +import forge.game.mana.ManaConversionService; import forge.game.player.GameLossReason; import forge.game.player.Player; import forge.game.replacement.ReplacementEffect; @@ -785,7 +786,7 @@ public class GameAction { game.getReplacementHandler().cleanUpTemporaryReplacements(); for (final Player p : game.getPlayers()) { - p.getManaPool().restoreColorReplacements(); + new ManaConversionService(p.getManaPool()).restoreColorReplacements(); p.clearStaticAbilities(); } diff --git a/forge-game/src/main/java/forge/game/ability/AbilityUtils.java b/forge-game/src/main/java/forge/game/ability/AbilityUtils.java index f3b9d374e66..c980a16b4de 100644 --- a/forge-game/src/main/java/forge/game/ability/AbilityUtils.java +++ b/forge-game/src/main/java/forge/game/ability/AbilityUtils.java @@ -18,6 +18,7 @@ import forge.game.ability.AbilityFactory.AbilityRecordType; import forge.game.card.*; import forge.game.cost.Cost; import forge.game.keyword.KeywordInterface; +import forge.game.mana.ManaConversionService; import forge.game.mana.ManaCostBeingPaid; import forge.game.player.Player; import forge.game.player.PlayerCollection; @@ -1663,7 +1664,7 @@ public class AbilityUtils { } } // AdjustColorReplacement has two different matrices handling final mana conversion under the covers - p.getManaPool().adjustColorReplacement(ManaAtom.fromName(c), convertByte, additive); + new ManaConversionService(p.getManaPool()).adjustColorReplacement(ManaAtom.fromName(c), convertByte, additive); } } } diff --git a/forge-game/src/main/java/forge/game/mana/IManaConversionMatrix.java b/forge-game/src/main/java/forge/game/mana/IManaConversionMatrix.java new file mode 100644 index 00000000000..0a047f766d7 --- /dev/null +++ b/forge-game/src/main/java/forge/game/mana/IManaConversionMatrix.java @@ -0,0 +1,11 @@ +package forge.game.mana; + +import forge.card.mana.ManaAtom; + +public interface IManaConversionMatrix { + // Conversion matrix ORs byte values to make mana more payable + // Restrictive matrix ANDs byte values to make mana less payable + byte[] colorConversionMatrix = new byte[ManaAtom.MANATYPES.length]; + byte[] colorRestrictionMatrix = new byte[ManaAtom.MANATYPES.length]; + +} diff --git a/forge-game/src/main/java/forge/game/mana/ManaConversionService.java b/forge-game/src/main/java/forge/game/mana/ManaConversionService.java new file mode 100644 index 00000000000..513d47cd5db --- /dev/null +++ b/forge-game/src/main/java/forge/game/mana/ManaConversionService.java @@ -0,0 +1,36 @@ +package forge.game.mana; + +import forge.card.mana.ManaAtom; + +public class ManaConversionService { + static byte[] identityMatrix = { ManaAtom.WHITE, ManaAtom.BLUE, ManaAtom.BLACK, ManaAtom.RED, ManaAtom.GREEN, ManaAtom.COLORLESS }; + + IManaConversionMatrix matrix; + + public ManaConversionService(IManaConversionMatrix mtrx) { + matrix = mtrx; + } + + public void adjustColorReplacement(byte originalColor, byte replacementColor, boolean additive) { + // Fix the index without hardcodes + int rowIdx = ManaAtom.getIndexOfFirstManaType(originalColor); + rowIdx = rowIdx < 0 ? identityMatrix.length - 1 : rowIdx; + if (additive) { + matrix.colorConversionMatrix[rowIdx] |= replacementColor; + } + else { + matrix.colorRestrictionMatrix[rowIdx] &= replacementColor; + } + } + + public void restoreColorReplacements() { + // By default each color can only be paid by itself ( {G} -> {G}, {C} -> {C} + for (int i = 0; i < matrix.colorConversionMatrix.length; i++) { + matrix.colorConversionMatrix[i] = identityMatrix[i]; + } + // By default all mana types are unrestricted + for (int i = 0; i < matrix.colorRestrictionMatrix.length; i++) { + matrix.colorRestrictionMatrix[i] = ManaAtom.ALL_MANA_TYPES; + } + } +} diff --git a/forge-game/src/main/java/forge/game/mana/ManaPool.java b/forge-game/src/main/java/forge/game/mana/ManaPool.java index d76c2145973..0b0c4f3924c 100644 --- a/forge-game/src/main/java/forge/game/mana/ManaPool.java +++ b/forge-game/src/main/java/forge/game/mana/ManaPool.java @@ -20,7 +20,6 @@ package forge.game.mana; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Lists; import com.google.common.collect.Multimap; - import forge.GameCommand; import forge.card.MagicColor; import forge.card.mana.ManaAtom; @@ -36,14 +35,9 @@ import forge.game.player.Player; import forge.game.spellability.AbilityManaPart; import forge.game.spellability.SpellAbility; import forge.game.zone.ZoneType; - import org.apache.commons.lang3.StringUtils; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; +import java.util.*; /** *

@@ -53,13 +47,13 @@ import java.util.List; * @author Forge * @version $Id$ */ -public class ManaPool implements Iterable { +public class ManaPool implements Iterable, IManaConversionMatrix { private final Player owner; private final Multimap floatingMana = ArrayListMultimap.create(); public ManaPool(final Player player) { owner = player; - restoreColorReplacements(); + new ManaConversionService(this).restoreColorReplacements(); } public final int getAmountOfColor(final byte color) { @@ -354,40 +348,11 @@ public class ManaPool implements Iterable { Player p = sa.getActivatingPlayer(); p.getGame().fireEvent(new GameEventZone(ZoneType.Battlefield, p, EventValueChangeType.ComplexUpdate, null)); } - - // Conversion matrix ORs byte values to make mana more payable - // Restrictive matrix ANDs byte values to make mana less payable - private final byte[] colorConversionMatrix = new byte[ManaAtom.MANATYPES.length]; - private final byte[] colorRestrictionMatrix = new byte[ManaAtom.MANATYPES.length]; - private static final byte[] identityMatrix = { ManaAtom.WHITE, ManaAtom.BLUE, ManaAtom.BLACK, ManaAtom.RED, ManaAtom.GREEN, ManaAtom.COLORLESS }; - - public void adjustColorReplacement(byte originalColor, byte replacementColor, boolean additive) { - // Fix the index without hardcodes - int rowIdx = ManaAtom.getIndexOfFirstManaType(originalColor); - rowIdx = rowIdx < 0 ? identityMatrix.length - 1 : rowIdx; - if (additive) { - colorConversionMatrix[rowIdx] |= replacementColor; - } - else { - colorRestrictionMatrix[rowIdx] &= replacementColor; - } - } - - public void restoreColorReplacements() { - // By default each color can only be paid by itself ( {G} -> {G}, {C} -> {C} - for (int i = 0; i < colorConversionMatrix.length; i++) { - colorConversionMatrix[i] = identityMatrix[i]; - } - // By default all mana types are unrestricted - for (int i = 0; i < colorRestrictionMatrix.length; i++) { - colorRestrictionMatrix[i] = ManaAtom.ALL_MANA_TYPES; - } - } public byte getPossibleColorUses(byte color) { // Take the current conversion value, AND with restrictions to get mana usage int rowIdx = ManaAtom.getIndexOfFirstManaType(color); - int matrixIdx = rowIdx < 0 ? identityMatrix.length - 1 : rowIdx; + int matrixIdx = rowIdx < 0 ? ManaConversionService.identityMatrix.length - 1 : rowIdx; byte colorUse = colorConversionMatrix[matrixIdx]; colorUse &= colorRestrictionMatrix[matrixIdx]; diff --git a/forge-gui/src/main/java/forge/player/HumanPlaySpellAbility.java b/forge-gui/src/main/java/forge/player/HumanPlaySpellAbility.java index e03c8d89251..02b4bf64ea8 100644 --- a/forge-gui/src/main/java/forge/player/HumanPlaySpellAbility.java +++ b/forge-gui/src/main/java/forge/player/HumanPlaySpellAbility.java @@ -17,14 +17,8 @@ */ package forge.player; -import java.util.Collections; -import java.util.Map; - -import org.apache.commons.lang3.StringUtils; - import com.google.common.collect.Iterables; import com.google.common.collect.Maps; - import forge.card.CardStateName; import forge.card.CardType; import forge.card.MagicColor; @@ -38,16 +32,17 @@ import forge.game.cost.CostPart; import forge.game.cost.CostPartMana; import forge.game.cost.CostPayment; import forge.game.keyword.KeywordInterface; +import forge.game.mana.ManaConversionService; import forge.game.mana.ManaPool; import forge.game.player.Player; import forge.game.player.PlayerController; -import forge.game.spellability.AbilityActivated; -import forge.game.spellability.AbilitySub; -import forge.game.spellability.Spell; -import forge.game.spellability.SpellAbility; -import forge.game.spellability.TargetRestrictions; +import forge.game.spellability.*; import forge.game.zone.Zone; import forge.util.collect.FCollection; +import org.apache.commons.lang3.StringUtils; + +import java.util.Collections; +import java.util.Map; /** *

@@ -81,6 +76,7 @@ public class HumanPlaySpellAbility { final Card c = ability.getHostCard(); final CardPlayOption option = c.mayPlay(ability.getMayPlay()); + ManaConversionService service = new ManaConversionService(manapool); boolean manaTypeConversion = false; boolean manaColorConversion = false; @@ -169,11 +165,12 @@ public class HumanPlaySpellAbility { ability.getHostCard().unanimateBestow(); } } + if (manaTypeConversion || manaColorConversion || keywordColor) { - manapool.restoreColorReplacements(); + service.restoreColorReplacements(); } if (playerManaConversion) { - manapool.restoreColorReplacements(); + service.restoreColorReplacements(); human.decNumManaConversion(); } return false; @@ -196,7 +193,7 @@ public class HumanPlaySpellAbility { clearTargets(ability); } if (manaTypeConversion || manaColorConversion || keywordColor) { - manapool.restoreColorReplacements(); + service.restoreColorReplacements(); } } return true; From 8ac960168d1efdb7ad5100198dc693644cd1fb91 Mon Sep 17 00:00:00 2001 From: Chris H Date: Mon, 8 Oct 2018 23:35:53 -0400 Subject: [PATCH 3/3] Cross my fingers --- .../java/forge/ai/PlayerControllerAi.java | 3 +- .../src/main/java/forge/game/GameAction.java | 5 ++- .../java/forge/game/ability/AbilityUtils.java | 6 +-- .../java/forge/game/cost/CostPartMana.java | 8 +++- .../java/forge/game/cost/CostPayment.java | 9 +++- .../game/mana/IManaConversionMatrix.java | 11 ----- .../forge/game/mana/ManaConversionMatrix.java | 45 +++++++++++++++++++ .../game/mana/ManaConversionService.java | 36 --------------- .../main/java/forge/game/mana/ManaPool.java | 6 +-- .../forge/game/player/PlayerController.java | 9 +++- .../StaticAbilityContinuous.java | 3 +- .../util/PlayerControllerForTests.java | 3 +- .../input/InputPayManaOfCostPayment.java | 16 ++++++- .../src/main/java/forge/player/HumanPlay.java | 40 ++++++----------- .../forge/player/HumanPlaySpellAbility.java | 18 ++++---- .../forge/player/PlayerControllerHuman.java | 5 ++- 16 files changed, 122 insertions(+), 101 deletions(-) delete mode 100644 forge-game/src/main/java/forge/game/mana/IManaConversionMatrix.java create mode 100644 forge-game/src/main/java/forge/game/mana/ManaConversionMatrix.java delete mode 100644 forge-game/src/main/java/forge/game/mana/ManaConversionService.java diff --git a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java index 7a21356b85b..e0c144ea10f 100644 --- a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java +++ b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java @@ -26,6 +26,7 @@ import forge.game.card.CardPredicates.Presets; import forge.game.combat.Combat; import forge.game.cost.*; import forge.game.mana.Mana; +import forge.game.mana.ManaConversionMatrix; import forge.game.mana.ManaCostBeingPaid; import forge.game.phase.PhaseHandler; import forge.game.phase.PhaseType; @@ -892,7 +893,7 @@ public class PlayerControllerAi extends PlayerController { } @Override - public boolean payManaCost(ManaCost toPay, CostPartMana costPartMana, SpellAbility sa, String prompt /* ai needs hints as well */, boolean isActivatedSa) { + public boolean payManaCost(ManaCost toPay, CostPartMana costPartMana, SpellAbility sa, String prompt /* ai needs hints as well */, ManaConversionMatrix matrix, boolean isActivatedSa) { // TODO Auto-generated method stub ManaCostBeingPaid cost = isActivatedSa ? ComputerUtilMana.calculateManaCost(sa, false, 0) : new ManaCostBeingPaid(toPay); return ComputerUtilMana.payManaCost(cost, sa, player); diff --git a/forge-game/src/main/java/forge/game/GameAction.java b/forge-game/src/main/java/forge/game/GameAction.java index ab9d96e0a33..9d8bbcc0c9e 100644 --- a/forge-game/src/main/java/forge/game/GameAction.java +++ b/forge-game/src/main/java/forge/game/GameAction.java @@ -29,7 +29,6 @@ import forge.game.ability.effects.AttachEffect; import forge.game.card.*; import forge.game.event.*; import forge.game.keyword.KeywordInterface; -import forge.game.mana.ManaConversionService; import forge.game.player.GameLossReason; import forge.game.player.Player; import forge.game.replacement.ReplacementEffect; @@ -786,7 +785,9 @@ public class GameAction { game.getReplacementHandler().cleanUpTemporaryReplacements(); for (final Player p : game.getPlayers()) { - new ManaConversionService(p.getManaPool()).restoreColorReplacements(); + if (!game.getStack().isFrozen()) { + p.getManaPool().restoreColorReplacements(); + } p.clearStaticAbilities(); } diff --git a/forge-game/src/main/java/forge/game/ability/AbilityUtils.java b/forge-game/src/main/java/forge/game/ability/AbilityUtils.java index c980a16b4de..4bfbe40fd9c 100644 --- a/forge-game/src/main/java/forge/game/ability/AbilityUtils.java +++ b/forge-game/src/main/java/forge/game/ability/AbilityUtils.java @@ -18,7 +18,7 @@ import forge.game.ability.AbilityFactory.AbilityRecordType; import forge.game.card.*; import forge.game.cost.Cost; import forge.game.keyword.KeywordInterface; -import forge.game.mana.ManaConversionService; +import forge.game.mana.ManaConversionMatrix; import forge.game.mana.ManaCostBeingPaid; import forge.game.player.Player; import forge.game.player.PlayerCollection; @@ -1643,7 +1643,7 @@ public class AbilityUtils { return CardFactoryUtil.xCount(c, s2); } - public static final void applyManaColorConversion(final Player p, final Map params) { + public static final void applyManaColorConversion(ManaConversionMatrix matrix, final Map params) { String conversionType = params.get("ManaColorConversion"); // Choices are Additives(OR) or Restrictive(AND) @@ -1664,7 +1664,7 @@ public class AbilityUtils { } } // AdjustColorReplacement has two different matrices handling final mana conversion under the covers - new ManaConversionService(p.getManaPool()).adjustColorReplacement(ManaAtom.fromName(c), convertByte, additive); + matrix.adjustColorReplacement(ManaAtom.fromName(c), convertByte, additive); } } } diff --git a/forge-game/src/main/java/forge/game/cost/CostPartMana.java b/forge-game/src/main/java/forge/game/cost/CostPartMana.java index 81d94a288e2..1fd0b568131 100644 --- a/forge-game/src/main/java/forge/game/cost/CostPartMana.java +++ b/forge-game/src/main/java/forge/game/cost/CostPartMana.java @@ -19,7 +19,9 @@ package forge.game.cost; import forge.card.mana.ManaCost; import forge.card.mana.ManaCostShard; +import forge.game.mana.ManaConversionMatrix; import forge.game.player.Player; +import forge.game.spellability.AbilityActivated; import forge.game.spellability.SpellAbility; /** @@ -37,6 +39,9 @@ public class CostPartMana extends CostPart { private boolean isEnchantedCreatureCost = false; private final String restriction; + private ManaConversionMatrix cardMatrix = null; + public void setCardMatrix(ManaConversionMatrix mtrx) { cardMatrix = mtrx; } + public int paymentOrder() { return shouldPayLast() ? 200 : 0; } public boolean shouldPayLast() { @@ -147,8 +152,9 @@ public class CostPartMana extends CostPart { // TODO Auto-generated method stub sa.clearManaPaid(); + boolean isActivated = sa instanceof AbilityActivated; // decision not used here, the whole payment is interactive! - return payer.getController().payManaCost(this, sa, null, true); + return payer.getController().payManaCost(this, sa, null, cardMatrix, isActivated); } } diff --git a/forge-game/src/main/java/forge/game/cost/CostPayment.java b/forge-game/src/main/java/forge/game/cost/CostPayment.java index 9785a12a49b..ca002861212 100644 --- a/forge-game/src/main/java/forge/game/cost/CostPayment.java +++ b/forge-game/src/main/java/forge/game/cost/CostPayment.java @@ -21,6 +21,7 @@ import com.google.common.collect.Lists; import com.google.common.collect.Maps; import forge.game.Game; import forge.game.card.Card; +import forge.game.mana.ManaConversionMatrix; import forge.game.spellability.SpellAbility; import forge.game.zone.ZoneType; @@ -35,7 +36,7 @@ import java.util.Map; * @author Forge * @version $Id$ */ -public class CostPayment { +public class CostPayment extends ManaConversionMatrix { private final Cost cost; private Cost adjustedCost; private final SpellAbility ability; @@ -70,6 +71,7 @@ public class CostPayment { this.cost = cost; this.adjustedCost = cost; this.ability = abil; + restoreColorReplacements(); } /** @@ -137,6 +139,11 @@ public class CostPayment { PaymentDecision pd = part.accept(decisionMaker); + // RIght before we start paying as decided, we need to transfer the CostPayments matrix over? + if (part instanceof CostPartMana) { + ((CostPartMana)part).setCardMatrix(this); + } + if (pd == null || !part.payAsDecided(decisionMaker.getPlayer(), pd, ability)) { game.costPaymentStack.pop(); // cost is resolved return false; diff --git a/forge-game/src/main/java/forge/game/mana/IManaConversionMatrix.java b/forge-game/src/main/java/forge/game/mana/IManaConversionMatrix.java deleted file mode 100644 index 0a047f766d7..00000000000 --- a/forge-game/src/main/java/forge/game/mana/IManaConversionMatrix.java +++ /dev/null @@ -1,11 +0,0 @@ -package forge.game.mana; - -import forge.card.mana.ManaAtom; - -public interface IManaConversionMatrix { - // Conversion matrix ORs byte values to make mana more payable - // Restrictive matrix ANDs byte values to make mana less payable - byte[] colorConversionMatrix = new byte[ManaAtom.MANATYPES.length]; - byte[] colorRestrictionMatrix = new byte[ManaAtom.MANATYPES.length]; - -} diff --git a/forge-game/src/main/java/forge/game/mana/ManaConversionMatrix.java b/forge-game/src/main/java/forge/game/mana/ManaConversionMatrix.java new file mode 100644 index 00000000000..e8caf56d774 --- /dev/null +++ b/forge-game/src/main/java/forge/game/mana/ManaConversionMatrix.java @@ -0,0 +1,45 @@ +package forge.game.mana; + +import forge.card.mana.ManaAtom; + +public class ManaConversionMatrix { + static byte[] identityMatrix = { ManaAtom.WHITE, ManaAtom.BLUE, ManaAtom.BLACK, ManaAtom.RED, ManaAtom.GREEN, ManaAtom.COLORLESS }; + + // Conversion matrix ORs byte values to make mana more payable + // Restrictive matrix ANDs byte values to make mana less payable + byte[] colorConversionMatrix = new byte[ManaAtom.MANATYPES.length]; + byte[] colorRestrictionMatrix = new byte[ManaAtom.MANATYPES.length]; + + public void adjustColorReplacement(byte originalColor, byte replacementColor, boolean additive) { + // Fix the index without hardcodes + int rowIdx = ManaAtom.getIndexOfFirstManaType(originalColor); + rowIdx = rowIdx < 0 ? identityMatrix.length - 1 : rowIdx; + if (additive) { + colorConversionMatrix[rowIdx] |= replacementColor; + } + else { + colorRestrictionMatrix[rowIdx] &= replacementColor; + } + } + + public void applyCardMatrix(ManaConversionMatrix extraMatrix) { + for (int i = 0; i < colorConversionMatrix.length; i++) { + colorConversionMatrix[i] |= extraMatrix.colorConversionMatrix[i]; + } + + for (int i = 0; i < colorRestrictionMatrix.length; i++) { + colorRestrictionMatrix[i] &= extraMatrix.colorRestrictionMatrix[i]; + } + } + + public void restoreColorReplacements() { + // By default each color can only be paid by itself ( {G} -> {G}, {C} -> {C} + for (int i = 0; i < colorConversionMatrix.length; i++) { + colorConversionMatrix[i] = identityMatrix[i]; + } + // By default all mana types are unrestricted + for (int i = 0; i < colorRestrictionMatrix.length; i++) { + colorRestrictionMatrix[i] = ManaAtom.ALL_MANA_TYPES; + } + } +} \ No newline at end of file diff --git a/forge-game/src/main/java/forge/game/mana/ManaConversionService.java b/forge-game/src/main/java/forge/game/mana/ManaConversionService.java deleted file mode 100644 index 513d47cd5db..00000000000 --- a/forge-game/src/main/java/forge/game/mana/ManaConversionService.java +++ /dev/null @@ -1,36 +0,0 @@ -package forge.game.mana; - -import forge.card.mana.ManaAtom; - -public class ManaConversionService { - static byte[] identityMatrix = { ManaAtom.WHITE, ManaAtom.BLUE, ManaAtom.BLACK, ManaAtom.RED, ManaAtom.GREEN, ManaAtom.COLORLESS }; - - IManaConversionMatrix matrix; - - public ManaConversionService(IManaConversionMatrix mtrx) { - matrix = mtrx; - } - - public void adjustColorReplacement(byte originalColor, byte replacementColor, boolean additive) { - // Fix the index without hardcodes - int rowIdx = ManaAtom.getIndexOfFirstManaType(originalColor); - rowIdx = rowIdx < 0 ? identityMatrix.length - 1 : rowIdx; - if (additive) { - matrix.colorConversionMatrix[rowIdx] |= replacementColor; - } - else { - matrix.colorRestrictionMatrix[rowIdx] &= replacementColor; - } - } - - public void restoreColorReplacements() { - // By default each color can only be paid by itself ( {G} -> {G}, {C} -> {C} - for (int i = 0; i < matrix.colorConversionMatrix.length; i++) { - matrix.colorConversionMatrix[i] = identityMatrix[i]; - } - // By default all mana types are unrestricted - for (int i = 0; i < matrix.colorRestrictionMatrix.length; i++) { - matrix.colorRestrictionMatrix[i] = ManaAtom.ALL_MANA_TYPES; - } - } -} diff --git a/forge-game/src/main/java/forge/game/mana/ManaPool.java b/forge-game/src/main/java/forge/game/mana/ManaPool.java index 0b0c4f3924c..de59a57ef21 100644 --- a/forge-game/src/main/java/forge/game/mana/ManaPool.java +++ b/forge-game/src/main/java/forge/game/mana/ManaPool.java @@ -47,13 +47,13 @@ import java.util.*; * @author Forge * @version $Id$ */ -public class ManaPool implements Iterable, IManaConversionMatrix { +public class ManaPool extends ManaConversionMatrix implements Iterable { private final Player owner; private final Multimap floatingMana = ArrayListMultimap.create(); public ManaPool(final Player player) { owner = player; - new ManaConversionService(this).restoreColorReplacements(); + restoreColorReplacements(); } public final int getAmountOfColor(final byte color) { @@ -352,7 +352,7 @@ public class ManaPool implements Iterable, IManaConversionMatrix { public byte getPossibleColorUses(byte color) { // Take the current conversion value, AND with restrictions to get mana usage int rowIdx = ManaAtom.getIndexOfFirstManaType(color); - int matrixIdx = rowIdx < 0 ? ManaConversionService.identityMatrix.length - 1 : rowIdx; + int matrixIdx = rowIdx < 0 ? identityMatrix.length - 1 : rowIdx; byte colorUse = colorConversionMatrix[matrixIdx]; colorUse &= colorRestrictionMatrix[matrixIdx]; diff --git a/forge-game/src/main/java/forge/game/player/PlayerController.java b/forge-game/src/main/java/forge/game/player/PlayerController.java index 3d02dcea56d..d9a8c240f06 100644 --- a/forge-game/src/main/java/forge/game/player/PlayerController.java +++ b/forge-game/src/main/java/forge/game/player/PlayerController.java @@ -20,6 +20,7 @@ import forge.game.cost.Cost; import forge.game.cost.CostPart; import forge.game.cost.CostPartMana; import forge.game.mana.Mana; +import forge.game.mana.ManaConversionMatrix; import forge.game.replacement.ReplacementEffect; import forge.game.spellability.*; import forge.game.trigger.WrappedAbility; @@ -218,9 +219,13 @@ public abstract class PlayerController { public abstract void resetAtEndOfTurn(); // currently used by the AI to perform card memory cleanup public final boolean payManaCost(CostPartMana costPartMana, SpellAbility sa, String prompt, boolean isActivatedAbility) { - return payManaCost(costPartMana.getManaCostFor(sa), costPartMana, sa, prompt, isActivatedAbility); + return payManaCost(costPartMana, sa, prompt, null, isActivatedAbility); } - public abstract boolean payManaCost(ManaCost toPay, CostPartMana costPartMana, SpellAbility sa, String prompt, boolean isActivatedAbility); + + public final boolean payManaCost(CostPartMana costPartMana, SpellAbility sa, String prompt, ManaConversionMatrix matrix, boolean isActivatedAbility) { + return payManaCost(costPartMana.getManaCostFor(sa), costPartMana, sa, prompt, matrix, isActivatedAbility); + } + public abstract boolean payManaCost(ManaCost toPay, CostPartMana costPartMana, SpellAbility sa, String prompt, ManaConversionMatrix matrix, boolean isActivatedAbility); public abstract Map chooseCardsForConvokeOrImprovise(SpellAbility sa, ManaCost manaCost, CardCollectionView untappedCards, boolean improvise); diff --git a/forge-game/src/main/java/forge/game/staticability/StaticAbilityContinuous.java b/forge-game/src/main/java/forge/game/staticability/StaticAbilityContinuous.java index 30cf2ca1749..63e71ca52a1 100644 --- a/forge-game/src/main/java/forge/game/staticability/StaticAbilityContinuous.java +++ b/forge-game/src/main/java/forge/game/staticability/StaticAbilityContinuous.java @@ -41,7 +41,6 @@ import forge.game.trigger.Trigger; import forge.game.trigger.TriggerHandler; import forge.game.zone.ZoneType; import forge.util.TextUtil; - import org.apache.commons.lang3.StringUtils; import java.util.*; @@ -481,7 +480,7 @@ public final class StaticAbilityContinuous { } if (params.containsKey("ManaColorConversion")) { - AbilityUtils.applyManaColorConversion(p, params); + AbilityUtils.applyManaColorConversion(p.getManaPool(), params); } } } diff --git a/forge-gui-desktop/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java b/forge-gui-desktop/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java index 0994bcc54f1..f086c277e20 100644 --- a/forge-gui-desktop/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java +++ b/forge-gui-desktop/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java @@ -30,6 +30,7 @@ import forge.game.cost.Cost; import forge.game.cost.CostPart; import forge.game.cost.CostPartMana; import forge.game.mana.Mana; +import forge.game.mana.ManaConversionMatrix; import forge.game.mana.ManaCostBeingPaid; import forge.game.player.*; import forge.game.replacement.ReplacementEffect; @@ -586,7 +587,7 @@ public class PlayerControllerForTests extends PlayerController { } @Override - public boolean payManaCost(ManaCost toPay, CostPartMana costPartMana, SpellAbility sa, String prompt /* ai needs hints as well */, boolean isActivatedSa ) { + public boolean payManaCost(ManaCost toPay, CostPartMana costPartMana, SpellAbility sa, String prompt /* ai needs hints as well */, ManaConversionMatrix matrix, boolean isActivatedSa) { // TODO Auto-generated method stub ManaCostBeingPaid cost = new ManaCostBeingPaid(toPay); return ComputerUtilMana.payManaCost(cost, sa, player); diff --git a/forge-gui/src/main/java/forge/match/input/InputPayManaOfCostPayment.java b/forge-gui/src/main/java/forge/match/input/InputPayManaOfCostPayment.java index 1f3fba6da55..15da37980f1 100644 --- a/forge-gui/src/main/java/forge/match/input/InputPayManaOfCostPayment.java +++ b/forge-gui/src/main/java/forge/match/input/InputPayManaOfCostPayment.java @@ -1,6 +1,7 @@ package forge.match.input; import forge.game.card.Card; +import forge.game.mana.ManaConversionMatrix; import forge.game.mana.ManaCostBeingPaid; import forge.game.player.Player; import forge.game.spellability.SpellAbility; @@ -10,13 +11,16 @@ import forge.properties.ForgePreferences; import forge.util.ITriggerEvent; public class InputPayManaOfCostPayment extends InputPayMana { - public InputPayManaOfCostPayment(final PlayerControllerHuman controller, ManaCostBeingPaid cost, SpellAbility spellAbility, Player payer) { + public InputPayManaOfCostPayment(final PlayerControllerHuman controller, ManaCostBeingPaid cost, SpellAbility spellAbility, Player payer, ManaConversionMatrix matrix) { super(controller, spellAbility, payer); manaCost = cost; + extraMatrix = matrix; + applyMatrix(); } private static final long serialVersionUID = 3467312982164195091L; private int phyLifeToLose = 0; + private ManaConversionMatrix extraMatrix; @Override protected final void onPlayerSelected(Player selected, final ITriggerEvent triggerEvent) { @@ -47,6 +51,8 @@ public class InputPayManaOfCostPayment extends InputPayMana { final String displayMana = manaCost.toString(false, player.getManaPool()); final StringBuilder msg = new StringBuilder(); + applyMatrix(); + if (messagePrefix != null) { msg.append(messagePrefix).append("\n"); } @@ -71,4 +77,12 @@ public class InputPayManaOfCostPayment extends InputPayMana { return msg.toString(); } + + private void applyMatrix() { + if (extraMatrix == null) { + return; + } + + player.getManaPool().applyCardMatrix(extraMatrix); + } } diff --git a/forge-gui/src/main/java/forge/player/HumanPlay.java b/forge-gui/src/main/java/forge/player/HumanPlay.java index fd96df17d56..8e7b84f42e3 100644 --- a/forge-gui/src/main/java/forge/player/HumanPlay.java +++ b/forge-gui/src/main/java/forge/player/HumanPlay.java @@ -1,21 +1,8 @@ package forge.player; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import forge.game.cost.*; -import forge.game.spellability.LandAbility; -import forge.game.spellability.OptionalCostValue; -import forge.game.spellability.Spell; -import forge.util.TextUtil; -import org.apache.commons.lang3.StringUtils; - import com.google.common.base.Predicate; import com.google.common.collect.Iterables; import com.google.common.collect.Maps; - import forge.FThreads; import forge.card.mana.ManaCost; import forge.game.Game; @@ -25,30 +12,31 @@ import forge.game.ability.AbilityUtils; import forge.game.ability.ApiType; import forge.game.ability.effects.CharmEffect; import forge.game.ability.effects.FlipCoinEffect; -import forge.game.card.Card; -import forge.game.card.CardCollection; -import forge.game.card.CardCollectionView; -import forge.game.card.CardDamageMap; -import forge.game.card.CardLists; -import forge.game.card.CardPredicates; +import forge.game.card.*; import forge.game.card.CardPredicates.Presets; -import forge.game.card.CardView; -import forge.game.card.CounterType; +import forge.game.cost.*; +import forge.game.mana.ManaConversionMatrix; import forge.game.mana.ManaCostBeingPaid; import forge.game.player.Player; import forge.game.player.PlayerController; import forge.game.player.PlayerView; -import forge.game.spellability.SpellAbility; -import forge.game.spellability.TargetRestrictions; +import forge.game.spellability.*; import forge.game.trigger.TriggerType; import forge.game.zone.ZoneType; import forge.match.input.InputPayMana; import forge.match.input.InputPayManaOfCostPayment; import forge.match.input.InputPayManaSimple; import forge.match.input.InputSelectCardsFromList; -import forge.util.collect.FCollectionView; import forge.util.Lang; +import forge.util.TextUtil; +import forge.util.collect.FCollectionView; import forge.util.gui.SGuiChoose; +import org.apache.commons.lang3.StringUtils; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; public class HumanPlay { @@ -814,7 +802,7 @@ public class HumanPlay { return !manaInputCancelled; } - public static boolean payManaCost(final PlayerControllerHuman controller, final ManaCost realCost, final CostPartMana mc, final SpellAbility ability, final Player activator, String prompt, boolean isActivatedSa) { + public static boolean payManaCost(final PlayerControllerHuman controller, final ManaCost realCost, final CostPartMana mc, final SpellAbility ability, final Player activator, String prompt, ManaConversionMatrix matrix, boolean isActivatedSa) { final Card source = ability.getHostCard(); ManaCostBeingPaid toPay = new ManaCostBeingPaid(realCost, mc.getRestiction()); @@ -879,7 +867,7 @@ public class HumanPlay { } if (!toPay.isPaid()) { // Input is somehow clearing out the offering card? - inpPayment = new InputPayManaOfCostPayment(controller, toPay, ability, activator); + inpPayment = new InputPayManaOfCostPayment(controller, toPay, ability, activator, matrix); inpPayment.setMessagePrefix(prompt); inpPayment.showAndWait(); if (!inpPayment.isPaid()) { diff --git a/forge-gui/src/main/java/forge/player/HumanPlaySpellAbility.java b/forge-gui/src/main/java/forge/player/HumanPlaySpellAbility.java index 02b4bf64ea8..c349a80de42 100644 --- a/forge-gui/src/main/java/forge/player/HumanPlaySpellAbility.java +++ b/forge-gui/src/main/java/forge/player/HumanPlaySpellAbility.java @@ -32,7 +32,6 @@ import forge.game.cost.CostPart; import forge.game.cost.CostPartMana; import forge.game.cost.CostPayment; import forge.game.keyword.KeywordInterface; -import forge.game.mana.ManaConversionService; import forge.game.mana.ManaPool; import forge.game.player.Player; import forge.game.player.PlayerController; @@ -76,7 +75,6 @@ public class HumanPlaySpellAbility { final Card c = ability.getHostCard(); final CardPlayOption option = c.mayPlay(ability.getMayPlay()); - ManaConversionService service = new ManaConversionService(manapool); boolean manaTypeConversion = false; boolean manaColorConversion = false; @@ -118,13 +116,15 @@ public class HumanPlaySpellAbility { ability.resetPaidHash(); } + // TODO Apply this to the SAStackInstance instead of the Player if (manaTypeConversion) { - AbilityUtils.applyManaColorConversion(human, MagicColor.Constant.ANY_TYPE_CONVERSION); + AbilityUtils.applyManaColorConversion(payment, MagicColor.Constant.ANY_TYPE_CONVERSION); } else if (manaColorConversion) { - AbilityUtils.applyManaColorConversion(human, MagicColor.Constant.ANY_COLOR_CONVERSION); + AbilityUtils.applyManaColorConversion(payment, MagicColor.Constant.ANY_COLOR_CONVERSION); } + if (playerManaConversion) { - AbilityUtils.applyManaColorConversion(human, MagicColor.Constant.ANY_COLOR_CONVERSION); + AbilityUtils.applyManaColorConversion(manapool, MagicColor.Constant.ANY_COLOR_CONVERSION); human.incNumManaConversion(); } @@ -142,7 +142,7 @@ public class HumanPlaySpellAbility { } if (keywordColor) { - AbilityUtils.applyManaColorConversion(human, params); + AbilityUtils.applyManaColorConversion(payment, params); } } @@ -167,10 +167,10 @@ public class HumanPlaySpellAbility { } if (manaTypeConversion || manaColorConversion || keywordColor) { - service.restoreColorReplacements(); + manapool.restoreColorReplacements(); } if (playerManaConversion) { - service.restoreColorReplacements(); + manapool.restoreColorReplacements(); human.decNumManaConversion(); } return false; @@ -193,7 +193,7 @@ public class HumanPlaySpellAbility { clearTargets(ability); } if (manaTypeConversion || manaColorConversion || keywordColor) { - service.restoreColorReplacements(); + manapool.restoreColorReplacements(); } } return true; diff --git a/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java b/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java index 49b32d26133..16b8ec32d52 100644 --- a/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java +++ b/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java @@ -31,6 +31,7 @@ import forge.game.cost.CostPart; import forge.game.cost.CostPartMana; import forge.game.keyword.Keyword; import forge.game.mana.Mana; +import forge.game.mana.ManaConversionMatrix; import forge.game.player.*; import forge.game.replacement.ReplacementEffect; import forge.game.replacement.ReplacementLayer; @@ -1702,8 +1703,8 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont @Override public boolean payManaCost(final ManaCost toPay, final CostPartMana costPartMana, final SpellAbility sa, - final String prompt, final boolean isActivatedSa) { - return HumanPlay.payManaCost(this, toPay, costPartMana, sa, player, prompt, isActivatedSa); + final String prompt, ManaConversionMatrix matrix, final boolean isActivatedSa) { + return HumanPlay.payManaCost(this, toPay, costPartMana, sa, player, prompt, matrix, isActivatedSa); } @Override