- Added Debt of Loyalty, Matopi Golem, Skeleton Scavengers, and Soldevi Sentry

This commit is contained in:
swordshine
2013-12-28 05:08:39 +00:00
parent 2de5beb6d1
commit 58e529097f
22 changed files with 200 additions and 29 deletions

5
.gitattributes vendored
View File

@@ -2757,6 +2757,7 @@ forge-gui/res/cardsfolder/d/deaths_head_buzzard.txt svneol=native#text/plain
forge-gui/res/cardsfolder/d/deaths_presence.txt -text
forge-gui/res/cardsfolder/d/deaths_shadow.txt svneol=native#text/plain
forge-gui/res/cardsfolder/d/deathspore_thallid.txt svneol=native#text/plain
forge-gui/res/cardsfolder/d/debt_of_loyalty.txt -text
forge-gui/res/cardsfolder/d/debt_to_the_deathless.txt -text
forge-gui/res/cardsfolder/d/debtors_knell.txt svneol=native#text/plain
forge-gui/res/cardsfolder/d/debtors_pulpit.txt -text
@@ -7064,6 +7065,7 @@ forge-gui/res/cardsfolder/m/masters_call.txt svneol=native#text/plain
forge-gui/res/cardsfolder/m/masticore.txt svneol=native#text/plain
forge-gui/res/cardsfolder/m/masumaro_first_to_live.txt svneol=native#text/plain
forge-gui/res/cardsfolder/m/matca_rioters.txt svneol=native#text/plain
forge-gui/res/cardsfolder/m/matopi_golem.txt -text
forge-gui/res/cardsfolder/m/matsu_tribe_birdstalker.txt svneol=native#text/plain
forge-gui/res/cardsfolder/m/matsu_tribe_decoy.txt -text
forge-gui/res/cardsfolder/m/matsu_tribe_sniper.txt svneol=native#text/plain
@@ -10505,6 +10507,7 @@ forge-gui/res/cardsfolder/s/skeletal_scrying.txt -text
forge-gui/res/cardsfolder/s/skeletal_snake.txt svneol=native#text/plain
forge-gui/res/cardsfolder/s/skeletal_vampire.txt svneol=native#text/plain
forge-gui/res/cardsfolder/s/skeletal_wurm.txt svneol=native#text/plain
forge-gui/res/cardsfolder/s/skeleton_scavengers.txt -text
forge-gui/res/cardsfolder/s/skeleton_shard.txt svneol=native#text/plain
forge-gui/res/cardsfolder/s/skeleton_ship.txt svneol=native#text/plain
forge-gui/res/cardsfolder/s/skeletonize.txt -text
@@ -10738,6 +10741,7 @@ forge-gui/res/cardsfolder/s/soldevi_golem.txt -text svneol=unset#text/plain
forge-gui/res/cardsfolder/s/soldevi_heretic.txt svneol=native#text/plain
forge-gui/res/cardsfolder/s/soldevi_machinist.txt -text
forge-gui/res/cardsfolder/s/soldevi_sage.txt svneol=native#text/plain
forge-gui/res/cardsfolder/s/soldevi_sentry.txt -text
forge-gui/res/cardsfolder/s/soldevi_simulacrum.txt svneol=native#text/plain
forge-gui/res/cardsfolder/s/soldevi_steam_beast.txt svneol=native#text/plain
forge-gui/res/cardsfolder/s/soldier_of_fortune.txt svneol=native#text/plain
@@ -14987,6 +14991,7 @@ forge-gui/src/main/java/forge/game/card/CardKeywords.java svneol=native#text/pla
forge-gui/src/main/java/forge/game/card/CardLists.java svneol=native#text/plain
forge-gui/src/main/java/forge/game/card/CardPowerToughness.java svneol=native#text/plain
forge-gui/src/main/java/forge/game/card/CardPredicates.java svneol=native#text/plain
forge-gui/src/main/java/forge/game/card/CardShields.java -text
forge-gui/src/main/java/forge/game/card/CardType.java svneol=native#text/plain
forge-gui/src/main/java/forge/game/card/CardUtil.java svneol=native#text/plain
forge-gui/src/main/java/forge/game/card/CounterType.java svneol=native#text/plain

View File

@@ -0,0 +1,8 @@
Name:Debt of Loyalty
ManaCost:1 W W
Types:Instant
A:SP$ Regenerate | Cost$ 1 W W | ValidTgts$ Creature | TgtPrompt$ Select target creature | RegenerationTrigger$ TrigGainControl | ReplaceCardUID$ Targeted | SpellDescription$ Regenerate target creature. You gain control of that creature if it regenerates this way.
SVar:TrigGainControl:ST$ GainControl | Cost$ 0 | Defined$ CardUID_Targeted | NewController$ You | SpellDescription$ Source controller gains control of CARDNAME if it regenerates this way.
SVar:RemAIDeck:True
SVar:Picture:http://www.wizards.com/global/images/magic/general/debt_of_loyalty.jpg
Oracle:Regenerate target creature. You gain control of that creature if it regenerates this way.

View File

@@ -0,0 +1,8 @@
Name:Matopi Golem
ManaCost:5
Types:Artifact Creature Golem
PT:3/3
A:AB$ Regenerate | Cost$ 1 | RegenerationTrigger$ TrigPutCounter | SpellDescription$ Regenerate CARDNAME. When it regenerates this way, put a -1/-1 counter on it.
SVar:TrigPutCounter:DB$ PutCounter | CounterType$ M1M1 | CounterNum$ 1 | SpellDescription$ When it regenerates this way, put a -1/-1 counter on it.
SVar:Picture:http://www.wizards.com/global/images/magic/general/matopi_golem.jpg
Oracle:{1}: Regenerate Matopi Golem. When it regenerates this way, put a -1/-1 counter on it.

View File

@@ -0,0 +1,10 @@
Name:Skeleton Scavengers
ManaCost:2 B
Types:Creature Skeleton
PT:0/0
K:etbCounter:P1P1:1
A:AB$ Regenerate | Cost$ X | CostDesc$ Pay {1} for each +1/+1 counter on CARDNAME: | References$ X | RegenerationTrigger$ TrigPutCounter | SpellDescription$ Regenerate CARDNAME. When it regenerates this way, put a +1/+1 counter on it.
SVar:X:Count$CardCounters.P1P1
SVar:TrigPutCounter:DB$ PutCounter | CounterType$ P1P1 | CounterNum$ 1 | SpellDescription$ When it regenerates this way, put a +1/+1 counter on it.
SVar:Picture:http://www.wizards.com/global/images/magic/general/skeleton_scavengers.jpg
Oracle:Skeleton Scavengers enters the battlefield with a +1/+1 counter on it.\nPay {1} for each +1/+1 counter on Skeleton Scavengers: Regenerate Skeleton Scavengers. When it regenerates this way, put a +1/+1 counter on it.

View File

@@ -0,0 +1,8 @@
Name:Soldevi Sentry
ManaCost:1
Types:Artifact Creature Soldier
PT:1/1
A:AB$ Regenerate | Cost$ 1 | ValidTgts$ Opponent | Defined$ Self | RegenerationTrigger$ TrigDraw | ReplacePlayerName$ Targeted | SpellDescription$ Choose target opponent. Regenerate CARDNAME. When it regenerates this way, that player may draw a card.
SVar:TrigDraw:DB$ Draw | Defined$ PlayerNamed_Targeted | NumCards$ 1 | OptionalDecider$ True | SpellDescription$ When it regenerates this way, that player may draw a card.
SVar:Picture:http://www.wizards.com/global/images/magic/general/soldevi_sentry.jpg
Oracle:{1}: Choose target opponent. Regenerate Soldevi Sentry. When it regenerates this way, that player may draw a card.

View File

@@ -1359,7 +1359,7 @@ public class ComputerUtil {
}
// already regenerated
if (c.getShield() > 0) {
if (!c.getShield().isEmpty()) {
continue;
}
@@ -1411,7 +1411,7 @@ public class ComputerUtil {
}
// already regenerated
if (c.getShield() > 0) {
if (!c.getShield().isEmpty()) {
continue;
}

View File

@@ -1884,7 +1884,7 @@ public class ComputerUtilCombat {
final boolean noPrevention) {
final int killDamage = ComputerUtilCombat.getDamageToKill(c);
if (c.hasKeyword("Indestructible") || (c.getShield() > 0)) {
if (c.hasKeyword("Indestructible") || !c.getShield().isEmpty()) {
if (!(source.hasKeyword("Wither") || source.hasKeyword("Infect"))) {
return maxDamage + 1;
}

View File

@@ -765,7 +765,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
CardLists.sortByEvaluateCreature(combatants);
for (final Card c : combatants) {
if (c.getShield() == 0 && ComputerUtilCombat.combatantWouldBeDestroyed(ai, c, combat) && c.getOwner() == ai && !c.isToken()) {
if (c.getShield().isEmpty() && ComputerUtilCombat.combatantWouldBeDestroyed(ai, c, combat) && c.getOwner() == ai && !c.isToken()) {
sa.getTargets().add(c);
return true;
}

View File

@@ -104,7 +104,7 @@ public class DestroyAi extends SpellAbilityAi {
list = CardLists.filter(list, new Predicate<Card>() {
@Override
public boolean apply(final Card c) {
return ((c.getShield() == 0) && !ComputerUtil.canRegenerate(ai, c));
return (c.getShield().isEmpty() && !ComputerUtil.canRegenerate(ai, c));
}
});
}
@@ -201,7 +201,7 @@ public class DestroyAi extends SpellAbilityAi {
preferred = CardLists.filter(preferred, new Predicate<Card>() {
@Override
public boolean apply(final Card c) {
return c.getShield() == 0;
return c.getShield().isEmpty();
}
});
}

View File

@@ -132,7 +132,7 @@ public abstract class PumpAiBase extends SpellAbilityAi {
return false;
}
} else if (keyword.endsWith("CARDNAME can't be regenerated.")) {
if (card.getShield() > 0) {
if (!card.getShield().isEmpty()) {
return true;
}
if (card.hasKeyword("If CARDNAME would be destroyed, regenerate it.") && combat != null

View File

@@ -103,7 +103,7 @@ public class RegenerateAi extends SpellAbilityAi {
boolean flag = false;
for (final Card c : list) {
if (c.getShield() == 0) {
if (c.getShield().isEmpty()) {
flag |= ComputerUtilCombat.combatantWouldBeDestroyed(ai, c, combat);
}
}
@@ -132,7 +132,7 @@ public class RegenerateAi extends SpellAbilityAi {
final List<Card> threatenedTargets = new ArrayList<Card>();
for (final Card c : targetables) {
if (objects.contains(c) && (c.getShield() == 0)) {
if (objects.contains(c) && c.getShield().isEmpty()) {
threatenedTargets.add(c);
}
}
@@ -148,7 +148,7 @@ public class RegenerateAi extends SpellAbilityAi {
CardLists.sortByEvaluateCreature(combatants);
for (final Card c : combatants) {
if ((c.getShield() == 0) && ComputerUtilCombat.combatantWouldBeDestroyed(ai, c, combat)) {
if (c.getShield().isEmpty() && ComputerUtilCombat.combatantWouldBeDestroyed(ai, c, combat)) {
sa.getTargets().add(c);
chance = true;
break;
@@ -204,7 +204,7 @@ public class RegenerateAi extends SpellAbilityAi {
if (game.getPhaseHandler().is(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
Combat combat = game.getCombat();
for (final Card c : combatants) {
if ((c.getShield() == 0) && ComputerUtilCombat.combatantWouldBeDestroyed(ai, c, combat)) {
if (c.getShield().isEmpty() && ComputerUtilCombat.combatantWouldBeDestroyed(ai, c, combat)) {
sa.getTargets().add(c);
return true;
}
@@ -217,7 +217,7 @@ public class RegenerateAi extends SpellAbilityAi {
// choose my best X without regen
if (CardLists.getNotType(compTargetables, "Creature").isEmpty()) {
for (final Card c : combatants) {
if (c.getShield() == 0) {
if (c.getShield().isEmpty()) {
sa.getTargets().add(c);
return true;
}
@@ -227,7 +227,7 @@ public class RegenerateAi extends SpellAbilityAi {
} else {
CardLists.sortByCmcDesc(compTargetables);
for (final Card c : compTargetables) {
if (c.getShield() == 0) {
if (c.getShield().isEmpty()) {
sa.getTargets().add(c);
return true;
}

View File

@@ -61,7 +61,7 @@ public class RegenerateAllAi extends SpellAbilityAi {
final List<GameObject> objects = ComputerUtil.predictThreatenedObjects(sa.getActivatingPlayer(), sa);
for (final Card c : list) {
if (objects.contains(c) && c.getShield() == 0) {
if (objects.contains(c) && c.getShield().isEmpty()) {
numSaved++;
}
}
@@ -70,7 +70,7 @@ public class RegenerateAllAi extends SpellAbilityAi {
final List<Card> combatants = CardLists.filter(list, CardPredicates.Presets.CREATURES);
final Combat combat = game.getCombat();
for (final Card c : combatants) {
if (c.getShield() == 0 && ComputerUtilCombat.combatantWouldBeDestroyed(ai, c, combat)) {
if (c.getShield().isEmpty() && ComputerUtilCombat.combatantWouldBeDestroyed(ai, c, combat)) {
numSaved++;
}
}

View File

@@ -1244,8 +1244,8 @@ public class GameAction {
}
if (c.canBeShielded() && (!c.isCreature() || c.getNetDefense() > 0)
&& (c.getShield() > 0 || c.hasKeyword("If CARDNAME would be destroyed, regenerate it."))) {
c.subtractShield();
&& (!c.getShield().isEmpty() || c.hasKeyword("If CARDNAME would be destroyed, regenerate it."))) {
c.subtractShield(c.getController().getController().chooseRegenerationShield(c));
c.setDamage(0);
c.tap();
c.addRegeneratedThisTurn();

View File

@@ -7,6 +7,7 @@ import forge.game.Game;
import forge.game.ability.SpellAbilityEffect;
import forge.game.card.Card;
import forge.game.card.CardLists;
import forge.game.card.CardShields;
import forge.game.spellability.SpellAbility;
import forge.game.zone.ZoneType;
@@ -41,7 +42,7 @@ public class RegenerateAllEffect extends SpellAbilityEffect {
};
if (c.isInPlay()) {
c.addShield();
c.addShield(new CardShields(sa, null));
game.getEndOfTurn().addUntil(untilEOT);
}
}

View File

@@ -5,8 +5,12 @@ import java.util.List;
import forge.Command;
import forge.game.Game;
import forge.game.ability.AbilityFactory;
import forge.game.ability.AbilityUtils;
import forge.game.ability.SpellAbilityEffect;
import forge.game.card.Card;
import forge.game.card.CardShields;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
@@ -46,6 +50,7 @@ public class RegenerateEffect extends SpellAbilityEffect {
public void resolve(SpellAbility sa) {
final TargetRestrictions tgt = sa.getTargetRestrictions();
final Game game = sa.getActivatingPlayer().getGame();
final Card sourceCard = sa.getSourceCard();
for (final Card tgtC : getTargetCards(sa)) {
final Command untilEOT = new Command() {
@@ -57,8 +62,26 @@ public class RegenerateEffect extends SpellAbilityEffect {
}
};
if (tgtC.isInPlay() && ((tgt == null) || tgtC.canBeTargetedBy(sa))) {
tgtC.addShield();
if (tgtC.isInPlay() && (tgt == null || tgt.canTgtPlayer() || tgtC.canBeTargetedBy(sa))) {
SpellAbility triggerSA = null;
if (sa.hasParam("RegenerationTrigger")) {
String abString = sa.getSourceCard().getSVar(sa.getParam("RegenerationTrigger"));
if (sa.hasParam("ReplacePlayerName")) { // Soldevi Sentry
String def = sa.getParam("ReplacePlayerName");
List<Player> replaced = AbilityUtils.getDefinedPlayers(sourceCard, def, sa);
abString = abString.replace(def, replaced.isEmpty() ? "" : replaced.get(0).getName());
} else if (sa.hasParam("ReplaceCardUID")) { // Debt of Loyalty
String def = sa.getParam("ReplaceCardUID");
List<Card> replaced = AbilityUtils.getDefinedCards(sourceCard, def, sa);
abString = abString.replace(def, replaced.isEmpty() ? "" : Integer.toString(replaced.get(0).getUniqueNumber()));
}
triggerSA = AbilityFactory.getAbility(abString, sourceCard);
triggerSA.setActivatingPlayer(sa.getActivatingPlayer());
triggerSA.setTrigger(true);
triggerSA.setSourceCard(sourceCard);
}
CardShields shield = new CardShields(sa, triggerSA);
tgtC.addShield(shield);
game.getEndOfTurn().addUntil(untilEOT);
}
}

View File

@@ -198,7 +198,7 @@ public class Card extends GameEntity implements Comparable<Card> {
private int damage;
// regeneration
private int nShield;
private List<CardShields> nShield = new ArrayList<CardShields>();
private int regeneratedThisTurn = 0;
private int turnInZone;
@@ -2686,7 +2686,7 @@ public class Card extends GameEntity implements Comparable<Card> {
*
* @return a int.
*/
public final int getShield() {
public final List<CardShields> getShield() {
return this.nShield;
}
@@ -2695,8 +2695,8 @@ public class Card extends GameEntity implements Comparable<Card> {
* addShield.
* </p>
*/
public final void addShield() {
this.nShield++;
public final void addShield(final CardShields shield) {
this.nShield.add(shield);
}
/**
@@ -2704,8 +2704,11 @@ public class Card extends GameEntity implements Comparable<Card> {
* subtractShield.
* </p>
*/
public final void subtractShield() {
this.nShield--;
public final void subtractShield(CardShields shield) {
if (shield != null && shield.hasTrigger()) {
this.getGame().getStack().addSimultaneousStackEntry(shield.getTriggerSA());
}
this.nShield.remove(shield);
}
/**
@@ -2740,7 +2743,7 @@ public class Card extends GameEntity implements Comparable<Card> {
* </p>
*/
public final void resetShield() {
this.nShield = 0;
this.nShield.clear();;
}
/**

View File

@@ -0,0 +1,79 @@
/*
* 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.card;
import forge.game.spellability.SpellAbility;
/**
* <p>
* Card_Shields class.
* </p>
*
* @author Forge
* @version $Id: CardShields.java 23786 2013-11-24 06:59:42Z Max mtg $
*/
public class CardShields {
// restore the regeneration shields
private final SpellAbility sourceSA;
private final SpellAbility triggerSA;
/**
* Instantiates a new CardShields.
*
* @param sourceSA
* a SpellAbility
*/
public CardShields(final SpellAbility sourceSA, final SpellAbility triggerSA) {
this.sourceSA = sourceSA;
this.triggerSA = triggerSA;
}
/**
*
* getSourceSA.
*
* @return sourceSA
*/
public final SpellAbility getSourceSA() {
return this.sourceSA;
}
/**
*
* getTriggerSA.
*
* @return triggerSA
*/
public final SpellAbility getTriggerSA() {
return this.triggerSA;
}
public final boolean hasTrigger() {
return this.triggerSA != null;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
String suffix = this.triggerSA != null ? " - " + triggerSA.getDescription() : "";
return this.sourceSA.getSourceCard().getName() + suffix;
}
}

View File

@@ -19,6 +19,7 @@ import forge.game.GameEntity;
import forge.game.GameObject;
import forge.game.GameType;
import forge.game.card.Card;
import forge.game.card.CardShields;
import forge.game.card.CounterType;
import forge.game.combat.Combat;
import forge.game.cost.Cost;
@@ -195,7 +196,8 @@ public abstract class PlayerController {
public abstract boolean confirmPayment(CostPart costPart, String string);
public abstract ReplacementEffect chooseSingleReplacementEffect(String prompt, List<ReplacementEffect> possibleReplacers, HashMap<String, Object> runParams);
public abstract String chooseProtectionType(String string, SpellAbility sa, List<String> choices);
public abstract CardShields chooseRegenerationShield(Card c);
// these 4 need some refining.
public abstract boolean payCostToPreventEffect(Cost cost, SpellAbility sa, boolean alreadyPaid, List<Player> allPayers);
public abstract void orderAndPlaySimultaneousSa(List<SpellAbility> activePlayerSAs);
@@ -210,4 +212,6 @@ public abstract class PlayerController {
// These 2 are for AI
public List<Card> cheatShuffle(List<Card> list) { return list; }
public Collection<? extends PaperCard> complainCardsCantPlayWell(Deck myDeck) { return null; }
}

View File

@@ -38,6 +38,7 @@ import forge.game.ability.ApiType;
import forge.game.card.Card;
import forge.game.card.CardLists;
import forge.game.card.CardPredicates;
import forge.game.card.CardShields;
import forge.game.card.CounterType;
import forge.game.combat.Combat;
import forge.game.cost.Cost;
@@ -688,4 +689,9 @@ public class PlayerControllerAi extends PlayerController {
return brains.cheatShuffle(list);
}
@Override
public CardShields chooseRegenerationShield(Card c) {
return Iterables.getFirst(c.getShield(), null);
}
}

View File

@@ -393,7 +393,7 @@ public class CardDetailPanel extends FPanel {
}
// Regeneration Shields
final int regenShields = card.getShield();
final int regenShields = card.getShield().size();
if (regenShields > 0) {
if (area.length() != 0) {
area.append("\n");

View File

@@ -8,6 +8,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.KeyStroke;
@@ -38,6 +39,7 @@ import forge.game.GameObject;
import forge.game.GameType;
import forge.game.ability.effects.CharmEffect;
import forge.game.card.Card;
import forge.game.card.CardShields;
import forge.game.card.CounterType;
import forge.game.combat.Combat;
import forge.game.cost.Cost;
@@ -1066,4 +1068,12 @@ public class PlayerControllerHuman extends PlayerController {
GuiChoose.one(message, anteedThings);
}
@Override
public CardShields chooseRegenerationShield(Card c) {
if (c.getShield().size() < 2) {
return Iterables.getFirst(c.getShield(), null);
}
return GuiChoose.one("Choose a regeneration shield:", c.getShield());
}
}

View File

@@ -28,6 +28,7 @@ import forge.game.GameObject;
import forge.game.GameType;
import forge.game.ability.AbilityUtils;
import forge.game.card.Card;
import forge.game.card.CardShields;
import forge.game.card.CounterType;
import forge.game.combat.Combat;
import forge.game.combat.CombatUtil;
@@ -544,4 +545,9 @@ public class PlayerControllerForTests extends PlayerController {
public void revealAnte(String message, Multimap<Player, PaperCard> removedAnteCards) {
// test this!
}
@Override
public CardShields chooseRegenerationShield(Card c) {
return Iterables.getFirst(c.getShield(), null);
}
}