- Added support for damage prevention shields with effects.

- Added: Candles' Glow, Temper, Test of Faith and Vengeful Archon
This commit is contained in:
moomarc
2013-06-28 16:56:56 +00:00
parent 4d8d51facc
commit 4b71ea18b8
18 changed files with 334 additions and 13 deletions

View File

@@ -48,6 +48,7 @@ import forge.card.CardRarity;
import forge.card.CardRules;
import forge.card.ColorSet;
import forge.card.MagicColor;
import forge.card.ability.AbilityFactory;
import forge.card.ability.AbilityUtils;
import forge.card.ability.ApiType;
import forge.card.cardfactory.CardFactoryUtil;
@@ -7341,6 +7342,66 @@ public class Card extends GameEntity implements Comparable<Card> {
int restDamage = damage;
boolean DEBUGShieldsWithEffects = false;
while (!this.getPreventNextDamageWithEffect().isEmpty() && restDamage != 0) {
TreeMap<Card, Map<String, String>> shieldMap = this.getPreventNextDamageWithEffect();
List<Card> preventionEffectSources = new ArrayList<Card>(shieldMap.keySet());
Card shieldSource = preventionEffectSources.get(0);
if (preventionEffectSources.size() > 1) {
Map<String, Card> choiceMap = new TreeMap<String, Card>();
List<String> choices = new ArrayList<String>();
for (final Card key : preventionEffectSources) {
String effDesc = shieldMap.get(key).get("EffectString");
int descIndex = effDesc.indexOf("SpellDescription");
effDesc = effDesc.substring(descIndex + 18);
String shieldDescription = key.toString() + " - " + shieldMap.get(key).get("ShieldAmount")
+ " shields - " + effDesc;
choices.add(shieldDescription);
choiceMap.put(shieldDescription, key);
}
shieldSource = this.getController().getController().chooseProtectionShield(this, choices, choiceMap);
}
if (DEBUGShieldsWithEffects) {
System.out.println("Prevention shield source: " + shieldSource);
}
int shieldAmount = Integer.valueOf(shieldMap.get(shieldSource).get("ShieldAmount"));
int dmgToBePrevented = Math.min(restDamage, shieldAmount);
if (DEBUGShieldsWithEffects) {
System.out.println("Selected source initial shield amount: " + shieldAmount);
System.out.println("Incoming damage: " + restDamage);
System.out.println("Damage to be prevented: " + dmgToBePrevented);
}
//Set up ability
SpellAbility shieldSA = null;
String effectAbString = shieldMap.get(shieldSource).get("EffectString");
effectAbString = effectAbString.replace("PreventedDamage", Integer.toString(dmgToBePrevented));
effectAbString = effectAbString.replace("ShieldEffectTarget", shieldMap.get(shieldSource).get("ShieldEffectTarget"));
if (DEBUGShieldsWithEffects) {
System.out.println("Final shield ability string: " + effectAbString);
}
shieldSA = AbilityFactory.getAbility(effectAbString, shieldSource);
if (shieldSA.usesTargeting()) {
System.err.println(shieldSource + " - Targeting for prevention shield's effect should be done with initial spell");
}
if (restDamage >= shieldAmount) {
this.getController().getController().playSpellAbilityNoStack(this.getController(), shieldSA);
this.subtractPreventNextDamageWithEffect(shieldSource, restDamage);
restDamage = restDamage - shieldAmount;
} else {
this.subtractPreventNextDamageWithEffect(shieldSource, restDamage);
this.getController().getController().playSpellAbilityNoStack(this.getController(), shieldSA);
restDamage = 0;
}
if (DEBUGShieldsWithEffects) {
System.out.println("Remaining shields: "
+ (shieldMap.containsKey(shieldSource) ? shieldMap.get(shieldSource).get("ShieldAmount") : "all shields used"));
System.out.println("Remaining damage: " + restDamage);
}
}
if (this.getName().equals("Swans of Bryn Argoll")) {
source.getController().drawCards(restDamage);
return 0;
@@ -8228,6 +8289,7 @@ public class Card extends GameEntity implements Comparable<Card> {
public void onCleanupPhase(final Player turn) {
setDamage(0);
resetPreventNextDamage();
resetPreventNextDamageWithEffect();
resetReceivedDamageFromThisTurn();
resetDealtDamageToThisTurn();
resetDealtDamageToPlayerThisTurn();

View File

@@ -18,6 +18,8 @@
package forge;
import java.util.ArrayList;
import java.util.Map;
import java.util.TreeMap;
import forge.game.Game;
import forge.game.player.Player;
@@ -34,6 +36,7 @@ import forge.util.MyObservable;
public abstract class GameEntity extends MyObservable implements ITargetable {
private String name = "";
private int preventNextDamage = 0;
private TreeMap<Card, Map<String, String>> preventionShieldsWithEffects = new TreeMap<Card, Map<String, String>>();
/** The enchanted by. */
private ArrayList<Card> enchantedBy = new ArrayList<Card>();
@@ -255,6 +258,78 @@ public abstract class GameEntity extends MyObservable implements ITargetable {
this.preventNextDamage = 0;
}
// PreventNextDamageWithEffect
/**
* <p>
* Gets the map of damage prevention shields with effects.
* </p>
*
* @return the map of damage prevention shields with effects.
*/
public TreeMap<Card, Map<String, String>> getPreventNextDamageWithEffect() {
return this.preventionShieldsWithEffects;
}
/**
* <p>
* Adds a damage prevention shield with an effect that happens at time of prevention.
* </p>
*
* @param shieldSource The source card which generated the shield
* @param effectMap A map of the effect occurring with the damage prevention
*/
public void addPreventNextDamageWithEffect(final Card shieldSource, TreeMap<String, String> effectMap) {
if (this.preventionShieldsWithEffects.containsKey(shieldSource)) {
int currentShields = Integer.valueOf(this.preventionShieldsWithEffects.get(shieldSource).get("ShieldAmount"));
currentShields += Integer.valueOf(effectMap.get("ShieldAmount"));
effectMap.put("ShieldAmount", Integer.toString(currentShields));
this.preventionShieldsWithEffects.put(shieldSource, effectMap);
} else {
this.preventionShieldsWithEffects.put(shieldSource, effectMap);
}
}
/**
* <p>
* subtractPreventNextDamageWithEffect.
* </p>
*
* @param shieldSource The source card which generated the shield
* @param n The number of shields to remove originating from shieldSource
*/
public void subtractPreventNextDamageWithEffect(final Card shieldSource, final int n) {
int currentShields = Integer.valueOf(this.preventionShieldsWithEffects.get(shieldSource).get("ShieldAmount"));
if (currentShields > n) {
this.preventionShieldsWithEffects.get(shieldSource).put("ShieldAmount", String.valueOf(currentShields - n));
} else {
this.preventionShieldsWithEffects.remove(shieldSource);
}
}
/**
* <p>
* resetPreventNextDamageWithEffect.
* </p>
*/
public void resetPreventNextDamageWithEffect() {
this.preventionShieldsWithEffects = new TreeMap<Card, Map<String, String>>();
}
/**
* <p>
* Gets the total amount of damage prevention shields.
* </p>
*
* @return the number of damage prevention shields with and without effects.
*/
public int getPreventNextDamageTotalShields() {
int shields = this.preventNextDamage;
for (final Map<String, String> value : this.preventionShieldsWithEffects.values()) {
shields += Integer.valueOf(value.get("ShieldAmount"));
}
return shields;
}
/**
* Checks for keyword.
*

View File

@@ -166,6 +166,10 @@ public final class AbilityFactory {
}
}
if (mapParams.containsKey("PreventionSubAbility")) {
spellAbility.setSVar(mapParams.get("PreventionSubAbility"), hostCard.getSVar(mapParams.get("PreventionSubAbility")));
}
if (mapParams.containsKey("SubAbility")) {
spellAbility.setSubAbility(getSubAbility(hostCard, hostCard.getSVar(mapParams.get("SubAbility"))));
}

View File

@@ -204,6 +204,14 @@ public class AbilityUtils {
for (final Card chosen : hostCard.getChosenCard()) {
cards.add(game.getCardState(chosen));
}
}
else if (defined.startsWith("CardUID_")) {
String idString = defined.substring(8);
for (final Card cardByID : game.getCardsInGame()) {
if (cardByID.getUniqueNumber() == Integer.valueOf(idString)) {
cards.add(game.getCardState(cardByID));
}
}
} else {
List<Card> list = null;
if (defined.startsWith("Sacrificed")) {
@@ -905,6 +913,13 @@ public class AbilityUtils {
if (!players.contains(p)) {
players.add(p);
}
} else if (defined.startsWith("PlayerNamed_")) {
for (Player p : game.getPlayers()) {
System.out.println("Named player " + defined.substring(12));
if (p.getName().equals(defined.substring(12))) {
players.add(p);
}
}
} else if (defined.startsWith("Flipped")) {
for (Player p : game.getPlayers()) {
if (null != sa.getSourceCard().getFlipResult(p)) {

View File

@@ -2,6 +2,7 @@ package forge.card.ability.effects;
import java.util.ArrayList;
import java.util.List;
import java.util.TreeMap;
import forge.Card;
import forge.CardUtil;
@@ -86,19 +87,62 @@ public class DamagePreventEffect extends SpellAbilityEffect {
}
final boolean targeted = (sa.usesTargeting());
final boolean preventionWithEffect = sa.hasParam("PreventionSubAbility");
for (final Object o : tgts) {
numDam = (sa.usesTargeting() && sa.hasParam("DividedAsYouChoose")) ? sa.getTargetRestrictions().getDividedValue(o) : numDam;
if (o instanceof Card) {
final Card c = (Card) o;
if (c.isInPlay() && (!targeted || c.canBeTargetedBy(sa))) {
c.addPreventNextDamage(numDam);
if (preventionWithEffect) {
TreeMap<String, String> effectMap = new TreeMap<String, String>();
effectMap.put("EffectString", sa.getSVar(sa.getParam("PreventionSubAbility")));
effectMap.put("ShieldAmount", String.valueOf(numDam));
if (sa.hasParam("ShieldEffectTarget")) {
String effTgtString = "";
List<ITargetable> effTgts = new ArrayList<ITargetable>();
effTgts = AbilityUtils.getDefinedObjects(host, sa.getParam("ShieldEffectTarget"), sa);
for (final Object effTgt : effTgts) {
if (effTgt instanceof Card) {
effTgtString = String.valueOf(((Card) effTgt).getUniqueNumber());
effectMap.put("ShieldEffectTarget", "CardUID_" + effTgtString);
} else if (effTgt instanceof Player) {
effTgtString = ((Player) effTgt).getName();
effectMap.put("ShieldEffectTarget", "PlayerNamed_" + effTgtString);
}
}
}
c.addPreventNextDamageWithEffect(host, effectMap);
} else {
c.addPreventNextDamage(numDam);
}
}
} else if (o instanceof Player) {
final Player p = (Player) o;
if (!targeted || p.canBeTargetedBy(sa)) {
p.addPreventNextDamage(numDam);
if (preventionWithEffect) {
TreeMap<String, String> effectMap = new TreeMap<String, String>();
effectMap.put("EffectString", sa.getSVar(sa.getParam("PreventionSubAbility")));
effectMap.put("ShieldAmount", String.valueOf(numDam));
if (sa.hasParam("ShieldEffectTarget")) {
String effTgtString = "";
List<ITargetable> effTgts = new ArrayList<ITargetable>();
effTgts = AbilityUtils.getDefinedObjects(host, sa.getParam("ShieldEffectTarget"), sa);
for (final Object effTgt : effTgts) {
if (effTgt instanceof Card) {
effTgtString = String.valueOf(((Card) effTgt).getUniqueNumber());
effectMap.put("ShieldEffectTarget", "CardUID_" + effTgtString);
} else if (effTgt instanceof Player) {
effTgtString = ((Player) effTgt).getName();
effectMap.put("ShieldEffectTarget", "PlayerNamed_" + effTgtString);
}
}
}
p.addPreventNextDamageWithEffect(host, effectMap);
} else {
p.addPreventNextDamage(numDam);
}
}
}
}

View File

@@ -240,12 +240,7 @@ public class ReplacementHandler {
}
Player player = replacementEffect.getHostCard().getController();
//player.getController().playNoStack()
if (player.isHuman()) {
HumanPlay.playSpellAbilityNoStack(player, effectSA);
} else {
ComputerUtil.playNoStack(player, effectSA, game);
}
player.getController().playSpellAbilityNoStack(player, effectSA);
return ReplacementResult.Replaced;
}

View File

@@ -1828,10 +1828,10 @@ public class ComputerUtilCombat {
* @return a int.
*/
public final static int getDamageToKill(final Card c) {
int killDamage = c.getLethalDamage() + c.getPreventNextDamage();
if ((killDamage > c.getPreventNextDamage())
int killDamage = c.getLethalDamage() + c.getPreventNextDamageTotalShields();
if ((killDamage > c.getPreventNextDamageTotalShields())
&& c.hasStartOfKeyword("When CARDNAME is dealt damage, destroy it.")) {
killDamage = 1 + c.getPreventNextDamage();
killDamage = 1 + c.getPreventNextDamageTotalShields();
}
return killDamage;

View File

@@ -25,6 +25,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.TreeMap;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
@@ -887,6 +888,66 @@ public class Player extends GameEntity implements Comparable<Player> {
int restDamage = damage;
boolean DEBUGShieldsWithEffects = false;
while (!this.getPreventNextDamageWithEffect().isEmpty() && restDamage != 0) {
TreeMap<Card, Map<String, String>> shieldMap = this.getPreventNextDamageWithEffect();
List<Card> preventionEffectSources = new ArrayList<Card>(shieldMap.keySet());
Card shieldSource = preventionEffectSources.get(0);
if (preventionEffectSources.size() > 1) {
Map<String, Card> choiceMap = new TreeMap<String, Card>();
List<String> choices = new ArrayList<String>();
for (final Card key : preventionEffectSources) {
String effDesc = shieldMap.get(key).get("EffectString");
int descIndex = effDesc.indexOf("SpellDescription");
effDesc = effDesc.substring(descIndex + 18);
String shieldDescription = key.toString() + " - " + shieldMap.get(shieldSource).get("ShieldAmount")
+ " shields - " + effDesc;
choices.add(shieldDescription);
choiceMap.put(shieldDescription, key);
}
shieldSource = this.getController().chooseProtectionShield(this, choices, choiceMap);
}
if (DEBUGShieldsWithEffects) {
System.out.println("Prevention shield source: " + shieldSource);
}
int shieldAmount = Integer.valueOf(shieldMap.get(shieldSource).get("ShieldAmount"));
int dmgToBePrevented = Math.min(restDamage, shieldAmount);
if (DEBUGShieldsWithEffects) {
System.out.println("Selected source initial shield amount: " + shieldAmount);
System.out.println("Incoming damage: " + restDamage);
System.out.println("Damage to be prevented: " + dmgToBePrevented);
}
//Set up ability
SpellAbility shieldSA = null;
String effectAbString = shieldMap.get(shieldSource).get("EffectString");
effectAbString = effectAbString.replace("PreventedDamage", Integer.toString(dmgToBePrevented));
effectAbString = effectAbString.replace("ShieldEffectTarget", shieldMap.get(shieldSource).get("ShieldEffectTarget"));
if (DEBUGShieldsWithEffects) {
System.out.println("Final shield ability string: " + effectAbString);
}
shieldSA = AbilityFactory.getAbility(effectAbString, shieldSource);
if (shieldSA.usesTargeting()) {
System.err.println(shieldSource + " - Targeting for prevention shield's effect should be done with initial spell");
}
if (restDamage >= shieldAmount) {
this.getController().playSpellAbilityNoStack(this, shieldSA);
this.subtractPreventNextDamageWithEffect(shieldSource, restDamage);
restDamage = restDamage - shieldAmount;
} else {
this.subtractPreventNextDamageWithEffect(shieldSource, restDamage);
this.getController().playSpellAbilityNoStack(this, shieldSA);
restDamage = 0;
}
if (DEBUGShieldsWithEffects) {
System.out.println("Remaining shields: "
+ (shieldMap.containsKey(shieldSource) ? shieldMap.get(shieldSource).get("ShieldAmount") : "all shields used"));
System.out.println("Remaining damage: " + restDamage);
}
}
final HashMap<String, Object> repParams = new HashMap<String, Object>();
repParams.put("Event", "DamageDone");
repParams.put("Affected", this);
@@ -2757,6 +2818,7 @@ public class Player extends GameEntity implements Comparable<Player> {
c.setDrawnThisTurn(false);
}
resetPreventNextDamage();
resetPreventNextDamageWithEffect();
resetNumDrawnThisTurn();
resetNumDiscardedThisTurn();
setAttackedWithCreatureThisTurn(false);

View File

@@ -96,6 +96,7 @@ public abstract class PlayerController {
//public abstract void playFromSuspend(Card c);
public abstract boolean playCascade(Card cascadedCard, Card sourceCard);
public abstract void playSpellAbilityForFree(SpellAbility copySA, boolean mayChoseNewTargets);
public abstract void playSpellAbilityNoStack(Player player, SpellAbility effectSA);
public abstract Deck sideboard(final Deck deck, GameType gameType);
@@ -157,5 +158,6 @@ public abstract class PlayerController {
public abstract boolean chooseBinary(SpellAbility sa, String question, boolean isCoin);
public abstract boolean chooseFilpResult(SpellAbility sa, Player flipper, boolean[] results, boolean call);
public abstract Card chooseProtectionShield(GameEntity entityBeingDamaged, List<String> options, Map<String, Card> choiceMap);
}

View File

@@ -248,6 +248,11 @@ public class PlayerControllerAi extends PlayerController {
ComputerUtil.playSpellAbilityForFree(player, copySA);
}
@Override
public void playSpellAbilityNoStack(Player player, SpellAbility effectSA) {
ComputerUtil.playNoStack(player, effectSA, game);
}
@Override
public void playMiracle(SpellAbility miracle, Card card) {
getAi().chooseAndPlaySa(false, false, miracle);
@@ -412,4 +417,10 @@ public class PlayerControllerAi extends PlayerController {
public boolean chooseBinary(SpellAbility sa, String question, boolean isCoin) {
return MyRandom.getRandom().nextBoolean();
}
@Override
public Card chooseProtectionShield(GameEntity entityBeingDamaged, List<String> options, Map<String, Card> choiceMap) {
int i = MyRandom.getRandom().nextInt(options.size());
return choiceMap.get(options.get(i));
}
}

View File

@@ -121,6 +121,11 @@ public class PlayerControllerHuman extends PlayerController {
HumanPlay.playSaWithoutPayingManaCost(player.getGame(), copySA, mayChoseNewTargets);
}
@Override
public void playSpellAbilityNoStack(Player player, SpellAbility effectSA) {
HumanPlay.playSpellAbilityNoStack(player, effectSA);
}
/* (non-Javadoc)
* @see forge.game.player.PlayerController#sideboard(forge.deck.Deck)
*/
@@ -645,6 +650,12 @@ public class PlayerControllerHuman extends PlayerController {
return GuiChoose.one(sa.getSourceCard().getName() + " - Choose a result", strResults) == labelsSrc[0];
}
@Override
public Card chooseProtectionShield(GameEntity entityBeingDamaged, List<String> options, Map<String, Card> choiceMap) {
String title = entityBeingDamaged + " - select which prevention shield to use";
return choiceMap.get(GuiChoose.one(title, options));
}
@Override
public Pair<SpellAbilityStackInstance, ITargetable> chooseTarget(SpellAbility saSpellskite, List<Pair<SpellAbilityStackInstance, ITargetable>> allTargets) {

View File

@@ -400,7 +400,7 @@ public class CardDetailPanel extends FPanel {
}
// Damage Prevention
final int preventNextDamage = card.getPreventNextDamage();
final int preventNextDamage = card.getPreventNextDamageTotalShields();
if (preventNextDamage > 0) {
area.append("\n");
area.append("Prevent the next ").append(preventNextDamage).append(" damage that would be dealt to ");

View File

@@ -152,7 +152,7 @@ public enum VPlayers implements IVDoc<CPlayers> {
+ String.valueOf(p0.getPoisonCounters()));
temp[2].setText("Maximum hand size: " + String.valueOf(p0.getMaxHandSize()));
temp[3].setText("Cards drawn this turn: " + String.valueOf(p0.getNumDrawnThisTurn()));
temp[4].setText("Damage Prevention: " + String.valueOf(p0.getPreventNextDamage()));
temp[4].setText("Damage Prevention: " + String.valueOf(p0.getPreventNextDamageTotalShields()));
if (!p0.getKeywords().isEmpty()) {
temp[5].setText(p0.getKeywords().toString());
} else {