mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-16 02:38:02 +00:00
Manapool: remove use of player keywords, use static and replacement effect
This commit is contained in:
@@ -25,7 +25,6 @@ public enum GlobalRuleChange {
|
||||
alwaysWither ("All damage is dealt as though its source had wither."),
|
||||
attackerChoosesBlockers ("The attacking player chooses how each creature blocks each combat."),
|
||||
manaBurn ("A player losing unspent mana causes that player to lose that much life."),
|
||||
manapoolsDontEmpty ("Mana pools don't empty as steps and phases end."),
|
||||
noCreatureETBTriggers ("Creatures entering the battlefield don't cause abilities to trigger."),
|
||||
noCreatureDyingTriggers ("Creatures dying don't cause abilities to trigger."),
|
||||
noLegendRule ("The legend rule doesn't apply."),
|
||||
|
||||
@@ -17,10 +17,10 @@
|
||||
*/
|
||||
package forge.game.mana;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@@ -28,17 +28,21 @@ import com.google.common.collect.ArrayListMultimap;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Multimap;
|
||||
|
||||
import forge.card.MagicColor;
|
||||
import forge.card.mana.ManaAtom;
|
||||
import forge.card.mana.ManaCostShard;
|
||||
import forge.game.Game;
|
||||
import forge.game.GlobalRuleChange;
|
||||
import forge.game.ability.AbilityKey;
|
||||
import forge.game.event.EventValueChangeType;
|
||||
import forge.game.event.GameEventManaPool;
|
||||
import forge.game.event.GameEventZone;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.replacement.ReplacementLayer;
|
||||
import forge.game.replacement.ReplacementType;
|
||||
import forge.game.spellability.AbilityManaPart;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.staticability.StaticAbilityUnspentMana;
|
||||
import forge.game.zone.ZoneType;
|
||||
|
||||
/**
|
||||
@@ -83,42 +87,50 @@ public class ManaPool extends ManaConversionMatrix implements Iterable<Mana> {
|
||||
* </p>
|
||||
*/
|
||||
public final boolean willManaBeLostAtEndOfPhase() {
|
||||
if (floatingMana.isEmpty() ||
|
||||
owner.getGame().getStaticEffects().getGlobalRuleChange(GlobalRuleChange.manapoolsDontEmpty) ||
|
||||
owner.hasKeyword("Convert unused mana to Colorless")) {
|
||||
if (floatingMana.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromAffected(owner);
|
||||
if (!owner.getGame().getReplacementHandler().getReplacementList(ReplacementType.LoseMana, runParams, ReplacementLayer.Other).isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int safeMana = 0;
|
||||
for (final byte c : MagicColor.WUBRG) {
|
||||
final String captName = StringUtils.capitalize(MagicColor.toLongString(c));
|
||||
if (owner.hasKeyword(captName + " mana doesn't empty from your mana pool as steps and phases end.")) {
|
||||
for (final byte c : StaticAbilityUnspentMana.getManaToKeep(owner)) {
|
||||
safeMana += getAmountOfColor(c);
|
||||
}
|
||||
}
|
||||
|
||||
return totalMana() != safeMana; //won't lose floating mana if all mana is of colors that aren't going to be emptied
|
||||
}
|
||||
|
||||
public final List<Mana> clearPool(boolean isEndOfPhase) {
|
||||
// isEndOfPhase parameter: true = end of phase, false = mana drain effect
|
||||
List<Mana> cleared = new ArrayList<>();
|
||||
if (floatingMana.isEmpty()) { return cleared; }
|
||||
|
||||
if (isEndOfPhase && owner.getGame().getStaticEffects().getGlobalRuleChange(GlobalRuleChange.manapoolsDontEmpty)) {
|
||||
return cleared;
|
||||
public final boolean hasBurn() {
|
||||
final Game game = owner.getGame();
|
||||
return game.getRules().hasManaBurn() || game.getStaticEffects().getGlobalRuleChange(GlobalRuleChange.manaBurn);
|
||||
}
|
||||
|
||||
final boolean convertToColorless = owner.hasKeyword("Convert unused mana to Colorless");
|
||||
public final List<Mana> clearPool(boolean isEndOfPhase) {
|
||||
// isEndOfPhase parameter: true = end of phase, false = mana drain effect
|
||||
List<Mana> cleared = Lists.newArrayList();
|
||||
if (floatingMana.isEmpty()) { return cleared; }
|
||||
|
||||
boolean convertToColorless = false;
|
||||
|
||||
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromAffected(owner);
|
||||
switch (owner.getGame().getReplacementHandler().run(ReplacementType.LoseMana, runParams)) {
|
||||
case NotReplaced:
|
||||
break;
|
||||
case Skipped:
|
||||
return cleared;
|
||||
default: // the only ones that does replace losing Mana are making it colorless instead
|
||||
convertToColorless = true;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
final List<Byte> keys = Lists.newArrayList(floatingMana.keySet());
|
||||
if (isEndOfPhase) {
|
||||
for (final Byte c : Lists.newArrayList(keys)) {
|
||||
final String captName = StringUtils.capitalize(MagicColor.toLongString(c));
|
||||
if (owner.hasKeyword(captName + " mana doesn't empty from your mana pool as steps and phases end.")) {
|
||||
keys.remove(c);
|
||||
}
|
||||
}
|
||||
keys.removeAll(StaticAbilityUnspentMana.getManaToKeep(owner));
|
||||
}
|
||||
if (convertToColorless) {
|
||||
keys.remove(Byte.valueOf((byte)ManaAtom.COLORLESS));
|
||||
@@ -126,13 +138,14 @@ public class ManaPool extends ManaConversionMatrix implements Iterable<Mana> {
|
||||
|
||||
for (Byte b : keys) {
|
||||
Collection<Mana> cm = floatingMana.get(b);
|
||||
final List<Mana> pMana = Lists.newArrayList();
|
||||
if (isEndOfPhase && !owner.getGame().getPhaseHandler().is(PhaseType.CLEANUP)) {
|
||||
final List<Mana> pMana = new ArrayList<>();
|
||||
for (final Mana mana : cm) {
|
||||
if (mana.getManaAbility()!= null && mana.getManaAbility().isPersistentMana()) {
|
||||
pMana.add(mana);
|
||||
}
|
||||
}
|
||||
}
|
||||
cm.removeAll(pMana);
|
||||
if (convertToColorless) {
|
||||
convertManaColor(b, (byte)ManaAtom.COLORLESS);
|
||||
@@ -142,14 +155,6 @@ public class ManaPool extends ManaConversionMatrix implements Iterable<Mana> {
|
||||
cm.clear();
|
||||
floatingMana.putAll(b, pMana);
|
||||
}
|
||||
} else {
|
||||
if (convertToColorless) {
|
||||
convertManaColor(b, (byte)ManaAtom.COLORLESS);
|
||||
} else {
|
||||
cleared.addAll(cm);
|
||||
cm.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
owner.updateManaForView();
|
||||
@@ -158,7 +163,7 @@ public class ManaPool extends ManaConversionMatrix implements Iterable<Mana> {
|
||||
}
|
||||
|
||||
private void convertManaColor(final byte originalColor, final byte toColor) {
|
||||
List<Mana> convert = new ArrayList<>();
|
||||
List<Mana> convert = Lists.newArrayList();
|
||||
Collection<Mana> cm = floatingMana.get(originalColor);
|
||||
for (Mana m : cm) {
|
||||
convert.add(new Mana(toColor, m.getSourceCard(), m.getManaAbility()));
|
||||
@@ -249,7 +254,7 @@ public class ManaPool extends ManaConversionMatrix implements Iterable<Mana> {
|
||||
return false;
|
||||
}
|
||||
|
||||
final List<Mana> removeFloating = new ArrayList<>();
|
||||
final List<Mana> removeFloating = Lists.newArrayList();
|
||||
|
||||
boolean manaNotAccountedFor = false;
|
||||
// loop over mana produced by mana ability
|
||||
@@ -329,8 +334,10 @@ public class ManaPool extends ManaConversionMatrix implements Iterable<Mana> {
|
||||
return shard.canBePaidWithManaOfColor((byte)0);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Iterator<Mana> iterator() {
|
||||
return floatingMana.values().iterator();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -478,9 +478,7 @@ public class PhaseHandler implements java.io.Serializable {
|
||||
for (Player p : game.getPlayers()) {
|
||||
int burn = p.getManaPool().clearPool(true).size();
|
||||
|
||||
boolean manaBurns = game.getRules().hasManaBurn() ||
|
||||
(game.getStaticEffects().getGlobalRuleChange(GlobalRuleChange.manaBurn));
|
||||
if (manaBurns) {
|
||||
if (p.getManaPool().hasBurn()) {
|
||||
p.loseLife(burn, false, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Forge: Play Magic: the Gathering.
|
||||
* Copyright (C) 2011 Forge Team
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package forge.game.replacement;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import forge.game.ability.AbilityKey;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
|
||||
public class ReplaceLoseMana extends ReplacementEffect {
|
||||
|
||||
public ReplaceLoseMana(Map<String, String> map, Card host, boolean intrinsic) {
|
||||
super(map, host, intrinsic);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.card.replacement.ReplacementEffect#canReplace(java.util.HashMap)
|
||||
*/
|
||||
@Override
|
||||
public boolean canReplace(Map<AbilityKey, Object> runParams) {
|
||||
if (!matchesValidParam("ValidPlayer", runParams.get(AbilityKey.Affected))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.card.replacement.ReplacementEffect#setReplacingObjects(java.util.HashMap, forge.card.spellability.SpellAbility)
|
||||
*/
|
||||
@Override
|
||||
public void setReplacingObjects(Map<AbilityKey, Object> runParams, SpellAbility sa) {
|
||||
sa.setReplacingObject(AbilityKey.Player, runParams.get(AbilityKey.Affected));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -403,7 +403,6 @@ public class ReplacementHandler {
|
||||
if ("True".equals(replacementEffect.getParam("Skip"))) {
|
||||
return ReplacementResult.Skipped; // Event is skipped.
|
||||
}
|
||||
|
||||
Player player = host.getController();
|
||||
|
||||
if (effectSA != null) {
|
||||
@@ -422,6 +421,10 @@ public class ReplacementHandler {
|
||||
}
|
||||
}
|
||||
|
||||
if ("Replaced".equals(replacementEffect.getParam("ReplacementResult"))) {
|
||||
return ReplacementResult.Replaced; // Event is replaced without SA.
|
||||
}
|
||||
|
||||
// if the spellability is a replace effect then its some new logic
|
||||
// if ReplacementResult is set in run params use that instead
|
||||
if (runParams.containsKey(AbilityKey.ReplacementResult)) {
|
||||
|
||||
@@ -30,6 +30,7 @@ public enum ReplacementType {
|
||||
GameLoss(ReplaceGameLoss.class),
|
||||
Learn(ReplaceLearn.class),
|
||||
LifeReduced(ReplaceLifeReduced.class),
|
||||
LoseMana(ReplaceLoseMana.class),
|
||||
Mill(ReplaceMill.class),
|
||||
Moved(ReplaceMoved.class),
|
||||
ProduceMana(ReplaceProduceMana.class),
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package forge.game.staticability;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import forge.card.MagicColor;
|
||||
import forge.card.mana.ManaAtom;
|
||||
import forge.game.Game;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.zone.ZoneType;
|
||||
|
||||
public class StaticAbilityUnspentMana {
|
||||
|
||||
static String MODE = "UnspentMana";
|
||||
|
||||
public static Collection<Byte> getManaToKeep(final Player player) {
|
||||
final Game game = player.getGame();
|
||||
Set<Byte> result = Sets.newHashSet();
|
||||
for (final Card ca : game.getCardsIn(ZoneType.STATIC_ABILITIES_SOURCE_ZONES)) {
|
||||
for (final StaticAbility stAb : ca.getStaticAbilities()) {
|
||||
if (!stAb.getParam("Mode").equals(MODE) || stAb.isSuppressed() || !stAb.checkConditions()) {
|
||||
continue;
|
||||
}
|
||||
applyUnspentManaAbility(stAb, player, result);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
public static void applyUnspentManaAbility(final StaticAbility stAb, final Player player, Set<Byte> result) {
|
||||
if (!stAb.matchesValidParam("ValidPlayer", player)) {
|
||||
return;
|
||||
}
|
||||
if (!stAb.hasParam("ManaType")) {
|
||||
for (byte b : ManaAtom.MANATYPES) {
|
||||
result.add(b);
|
||||
}
|
||||
} else {
|
||||
result.add(MagicColor.fromName(stAb.getParam("ManaType")));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
Name:Horizon Stone
|
||||
ManaCost:5
|
||||
Types:Artifact
|
||||
S:Mode$ Continuous | Affected$ You | AddKeyword$ Convert unused mana to Colorless | Description$ If you would lose unspent mana, that mana becomes colorless instead.
|
||||
R:Event$ LoseMana | ValidPlayer$ You | ReplacementResult$ Replaced | Description$ If you would lose unspent mana, that mana becomes colorless instead.
|
||||
Oracle:If you would lose unspent mana, that mana becomes colorless instead.
|
||||
|
||||
@@ -6,7 +6,7 @@ K:Indestructible
|
||||
S:Mode$ Continuous | Affected$ Card.Self | RemoveType$ Creature | CheckSVar$ X | SVarCompare$ LT7 | Description$ As long as your devotion to green and blue is less than seven, CARDNAME isn't a creature.
|
||||
SVar:X:Count$DevotionDual.Green.Blue
|
||||
S:Mode$ Continuous | Affected$ You | SetMaxHandSize$ Unlimited | Description$ You have no maximum hand size.
|
||||
S:Mode$ Continuous | Affected$ You | AddKeyword$ Convert unused mana to Colorless | Description$ If you would lose unspent mana, that mana becomes colorless instead.
|
||||
R:Event$ LoseMana | ValidPlayer$ You | ReplacementResult$ Replaced | Description$ If you would lose unspent mana, that mana becomes colorless instead.
|
||||
SVar:BuffedBy:Permanent.Green,Permanent.Blue
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/kruphix_god_of_horizons.jpg
|
||||
Oracle:Indestructible\nAs long as your devotion to green and blue is less than seven, Kruphix isn't a creature.\nYou have no maximum hand size.\nIf you would lose unspent mana, that mana becomes colorless instead.
|
||||
|
||||
@@ -3,7 +3,7 @@ ManaCost:2 R R
|
||||
Types:Creature Dragon
|
||||
PT:4/4
|
||||
K:Flying
|
||||
S:Mode$ Continuous | Affected$ You | AddKeyword$ Red mana doesn't empty from your mana pool as steps and phases end. | Description$ You don't lose unspent red mana as steps and phases end.
|
||||
S:Mode$ UnspentMana | ValidPlayer$ You | ManaType$ Red | Description$ You don't lose unspent red mana as steps and phases end.
|
||||
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigChooseX | TriggerController$ TriggeredCardController | TriggerDescription$ When CARDNAME dies, you may pay any amount of {R}. When you do, it deals that much damage to any target.
|
||||
SVar:TrigChooseX:DB$ ChooseNumber | Defined$ You | ChooseAnyNumber$ True | ListTitle$ any amount of red mana | SubAbility$ DBDamage
|
||||
SVar:DBDamage:DB$ DealDamage | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target to damage with CARDNAME | NumDmg$ X | UnlessCost$ X | UnlessXColor$ R | UnlessSwitched$ True | UnlessPayer$ You
|
||||
|
||||
@@ -2,7 +2,7 @@ Name:Omnath, Locus of Mana
|
||||
ManaCost:2 G
|
||||
Types:Legendary Creature Elemental
|
||||
PT:1/1
|
||||
S:Mode$ Continuous | Affected$ You | AddKeyword$ Green mana doesn't empty from your mana pool as steps and phases end. | Description$ You don't lose unspent green mana as steps and phases end.
|
||||
S:Mode$ UnspentMana | ValidPlayer$ You | ManaType$ Green | Description$ You don't lose unspent green mana as steps and phases end.
|
||||
S:Mode$ Continuous | Affected$ Card.Self | AddPower$ X | AddToughness$ X | Description$ CARDNAME gets +1/+1 for each unspent green mana you have.
|
||||
SVar:X:Count$ManaPool:green
|
||||
AI:RemoveDeck:All
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
Name:Upwelling
|
||||
ManaCost:3 G
|
||||
Types:Enchantment
|
||||
S:Mode$ Continuous | GlobalRule$ Mana pools don't empty as steps and phases end. | Description$ Players don't lose unspent mana as steps and phases end.
|
||||
S:Mode$ UnspentMana | Description$ Players don't lose unspent mana as steps and phases end.
|
||||
SVar:NonStackingEffect:True
|
||||
AI:RemoveDeck:Random
|
||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/upwelling.jpg
|
||||
Oracle:Players don't lose unspent mana as steps and phases end.
|
||||
|
||||
@@ -110,7 +110,7 @@ public class InputPassPriority extends InputSyncronizedBase {
|
||||
public void run() {
|
||||
Localizer localizer = Localizer.getInstance();
|
||||
String message = localizer.getMessage("lblYouHaveManaFloatingInYourManaPoolCouldBeLostIfPassPriority");
|
||||
if (FModel.getPreferences().getPrefBoolean(FPref.UI_MANABURN)) {
|
||||
if (player.getManaPool().hasBurn()) {
|
||||
message += " " + localizer.getMessage("lblYouWillTakeManaBurnDamageEqualAmountFloatingManaLostThisWay");
|
||||
}
|
||||
if (getController().getGui().showConfirmDialog(message, localizer.getMessage("lblManaFloating"), localizer.getMessage("lblOk"), localizer.getMessage("lblCancel"))) {
|
||||
|
||||
Reference in New Issue
Block a user