MH1 Slice onto instance or sorcery

This commit is contained in:
Hans Mackowiak
2019-06-09 09:22:57 +00:00
committed by Michael Kamensky
parent 6ea96baf0a
commit 3644b8f74f
11 changed files with 95 additions and 33 deletions

View File

@@ -76,17 +76,15 @@ public class ComputerUtil {
source.setSplitStateToPlayAbility(sa);
if (sa.isSpell() && !source.isCopiedSpell()) {
if (source.getType().hasStringType("Arcane")) {
sa = AbilityUtils.addSpliceEffects(sa);
if (sa.getSplicedCards() != null && !sa.getSplicedCards().isEmpty() && ai.getController().isAI()) {
// we need to reconsider and retarget the SA after additional SAs have been added onto it via splice,
// otherwise the AI will fail to add the card to stack and that'll knock it out of the game
sa.resetTargets();
if (((PlayerControllerAi) ai.getController()).getAi().canPlaySa(sa) != AiPlayDecision.WillPlay) {
// for whatever reason the AI doesn't want to play the thing with the spliced subs anymore,
// proceeding past this point may result in an illegal play
return false;
}
sa = AbilityUtils.addSpliceEffects(sa);
if (sa.getSplicedCards() != null && !sa.getSplicedCards().isEmpty() && ai.getController().isAI()) {
// we need to reconsider and retarget the SA after additional SAs have been added onto it via splice,
// otherwise the AI will fail to add the card to stack and that'll knock it out of the game
sa.resetTargets();
if (((PlayerControllerAi) ai.getController()).getAi().canPlaySa(sa) != AiPlayDecision.WillPlay) {
// for whatever reason the AI doesn't want to play the thing with the spliced subs anymore,
// proceeding past this point may result in an illegal play
return false;
}
}

View File

@@ -17,6 +17,7 @@ import forge.game.GameObject;
import forge.game.ability.AbilityFactory.AbilityRecordType;
import forge.game.card.*;
import forge.game.cost.Cost;
import forge.game.keyword.Keyword;
import forge.game.keyword.KeywordInterface;
import forge.game.mana.ManaConversionMatrix;
import forge.game.mana.ManaCostBeingPaid;
@@ -1843,10 +1844,23 @@ public class AbilityUtils {
final Card source = sa.getHostCard();
final Player player = sa.getActivatingPlayer();
final CardCollection splices = CardLists.filter(player.getCardsIn(ZoneType.Hand), new Predicate<Card>() {
final CardCollectionView hand = player.getCardsIn(ZoneType.Hand);
if (hand.isEmpty()) {
return sa;
}
final CardCollection splices = CardLists.filter(hand, new Predicate<Card>() {
@Override
public boolean apply(Card input) {
return input.hasStartOfKeyword("Splice");
for (final KeywordInterface inst : input.getKeywords(Keyword.SPLICE)) {
String k = inst.getOriginal();
final String n[] = k.split(":");
if (source.isValid(n[1].split(","), player, input, sa)) {
return true;
}
}
return false;
}
});
@@ -1871,12 +1885,11 @@ public class AbilityUtils {
public static void addSpliceEffect(final SpellAbility sa, final Card c) {
Cost spliceCost = null;
for (final KeywordInterface inst : c.getKeywords()) {
// This Function thinks that Splice exist only once on the card
for (final KeywordInterface inst : c.getKeywords(Keyword.SPLICE)) {
final String k = inst.getOriginal();
if (k.startsWith("Splice")) {
final String n[] = k.split(":");
spliceCost = new Cost(n[2], false);
}
final String n[] = k.split(":");
spliceCost = new Cost(n[2], false);
}
if (spliceCost == null)

View File

@@ -2198,7 +2198,22 @@ public class Card extends GameEntity implements Comparable<Card> {
} else if (keyword.startsWith("Splice")) {
final String[] n = keyword.split(":");
final Cost cost = new Cost(n[2], false);
sbAfter.append("Splice onto ").append(n[1]).append(" ").append(cost.toSimpleString());
String desc;
if (n.length > 3) {
desc = n[3];
} else {
String k[] = n[1].split(",");
for (int i = 0; i < k.length; i++) {
if (CardType.isACardType(k[i])) {
k[i] = k[i].toLowerCase();
}
}
desc = StringUtils.join(k, " or ");
}
sbAfter.append("Splice onto ").append(desc).append(" ").append(cost.toSimpleString());
sbAfter.append(" (" + inst.getReminderText() + ")").append("\r\n");
} else if (keyword.equals("Storm")) {
sbAfter.append("Storm (");
@@ -4236,7 +4251,14 @@ public class Card extends GameEntity implements Comparable<Card> {
return getAmountOfKeyword(k, currentState);
}
public final int getAmountOfKeyword(final Keyword k, CardState state) {
return state.getCachedKeyword(k).size();
return getKeywords(k, state).size();
}
public final Collection<KeywordInterface> getKeywords(final Keyword k) {
return getKeywords(k, currentState);
}
public final Collection<KeywordInterface> getKeywords(final Keyword k, CardState state) {
return state.getCachedKeyword(k);
}
// This is for keywords with a number like Bushido, Annihilator and Rampage.
@@ -4254,7 +4276,7 @@ public class Card extends GameEntity implements Comparable<Card> {
*/
public final int getKeywordMagnitude(final Keyword k, CardState state) {
int count = 0;
for (final KeywordInterface inst : state.getCachedKeyword(k)) {
for (final KeywordInterface inst : getKeywords(k, state)) {
String kw = inst.getOriginal();
// this can't be used yet for everything because of X values in Bushido X
// KeywordInterface#getAmount

View File

@@ -4435,9 +4435,7 @@ public class CardFactoryUtil {
sb.append(" | CostDesc$ ").append(ManaCostParser.parse(manacost));
sb.append("| Origin$ Library | Destination$ Hand |");
sb.append("ChangeType$ ").append(type);
// no Keyword.getReminderText, it does not work yet
sb.append(" | SpellDescription$ (Search your library for a ").append(desc).append(" card, reveal it,");
sb.append(" and put it into your hand. Then shuffle your library.)");
sb.append(" | SpellDescription$ (").append(inst.getReminderText()).append(")");
SpellAbility sa = AbilityFactory.getAbility(sb.toString(), card);
sa.setIsCycling(true);

View File

@@ -18,6 +18,8 @@ import forge.game.keyword.KeywordInterface;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.item.PaperToken;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import java.util.List;
@@ -231,7 +233,7 @@ public class TokenInfo {
final Card host = sa.getHostCard();
final Game game = host.getGame();
String edition = host.getSetCode();
String edition = ObjectUtils.firstNonNull(sa.getOriginalHost(), host).getSetCode();
PaperToken token = StaticData.instance().getAllTokens().getToken(script, edition);
if (token != null) {

View File

@@ -1,20 +1,38 @@
package forge.game.keyword;
import org.apache.commons.lang3.StringUtils;
import forge.card.CardType;
import forge.game.cost.Cost;
public class KeywordWithCostAndType extends KeywordInstance<KeywordWithCostAndType> {
private Cost cost;
private String type;
private String strType = null;
@Override
protected void parse(String details) {
final String[] k = details.split(":");
type = k[0];
cost = new Cost(k[1], false);
if (k.length > 2) {
strType = k[2];
} else {
String n[] = type.split(",");
for (int i = 0; i < n.length; i++) {
if (CardType.isACardType(n[i])) {
n[i] = n[i].toLowerCase();
} else if (n[i].equals("Basic")) {
n[i] = "basic land";
}
}
strType = StringUtils.join(n, " or ");
}
}
@Override
protected String formatReminderText(String reminderText) {
return String.format(reminderText, cost.toSimpleString(), type);
return String.format(reminderText, cost.toSimpleString(), strType);
}
}

View File

@@ -0,0 +1,7 @@
Name:Everdream
ManaCost:1 U
Types:Instant
K:Splice:Instant,Sorcery:2 U
A:SP$ Draw | Cost$ 1 U | NumCards$ 1 | SpellDescription$ Draw a card.
AI:RemoveDeck:Random
Oracle:Draw a card.\nSplice onto instant or sorcery {2}{U} (As you cast an instant or sorcery spell, you may reveal this card from your hand and pay its splice cost. If you do, add this card's effects to that spell.)

View File

@@ -0,0 +1,7 @@
Name:Splicer's Skill
ManaCost:2 W
Types:Sorcery
K:Splice:Instant,Sorcery:3 W
A:SP$ Token | Cost$ 2 W | TokenAmount$ 1 | TokenScript$ c_3_3_a_golem | TokenOwner$ You | LegacyImage$ c 3 3 a golem mh1 | SpellDescription$ Create a 3/3 colorless Golem artifact creature token.
AI:RemoveDeck:Random
Oracle:Create a 3/3 colorless Golem artifact creature token.\nSplice onto instant or sorcery {3}{W} (As you cast an instant or sorcery spell, you may reveal this card from your hand and pay its splice cost. If you do, add this card's effects to that spell.)

View File

@@ -325,6 +325,7 @@ c_2_2_pincher_5dn.jpg https://downloads.cardforge.org/images/tokens/c_2_2_pinche
c_2_2_a_spawn_dst.jpg https://downloads.cardforge.org/images/tokens/c_2_2_a_spawn_dst.jpg
c_2_2_a_spawn_td2.jpg https://downloads.cardforge.org/images/tokens/c_2_2_a_spawn_td2.jpg
c_3_2_eldrazi_horror_emn.jpg https://downloads.cardforge.org/images/tokens/c_3_2_eldrazi_horror_emn.jpg
c_3_3_a_golem_mh1.jpg https://downloads.cardforge.org/images/tokens/c_3_3_a_golem_mh1.jpg
c_3_3_a_golem_mm2.jpg https://downloads.cardforge.org/images/tokens/c_3_3_a_golem_mm2.jpg
c_3_3_a_golem_mm3.jpg https://downloads.cardforge.org/images/tokens/c_3_3_a_golem_mm3.jpg
c_3_3_a_golem_nph.jpg https://downloads.cardforge.org/images/tokens/c_3_3_a_golem_nph.jpg

View File

@@ -85,9 +85,7 @@ public class HumanPlay {
CharmEffect.makeChoices(sa);
}
if (sa.isSpell() && source.getType().hasStringType("Arcane")) {
sa = AbilityUtils.addSpliceEffects(sa);
}
sa = AbilityUtils.addSpliceEffects(sa);
if (sa.hasParam("Bestow")) {
source.animateBestow();
@@ -199,9 +197,7 @@ public class HumanPlay {
if (sa.getApi() == ApiType.Charm && !sa.isWrapper()) {
CharmEffect.makeChoices(sa);
}
if (sa.isSpell() && source.getType().hasStringType("Arcane")) {
sa = AbilityUtils.addSpliceEffects(sa);
}
sa = AbilityUtils.addSpliceEffects(sa);
}
final CostPayment payment = new CostPayment(sa.getPayCosts(), sa);

View File

@@ -71,7 +71,7 @@ public abstract class ImageFetcher {
final String filename = imageKey.substring(2) + ".jpg";
String tokenUrl = tokenImages.get(filename);
if (tokenUrl == null) {
System.err.println("No specified file.. Attempting to download from default Url");
System.err.println("No specified file for '" + filename + "'.. Attempting to download from default Url");
tokenUrl = String.format("%s%s", ForgeConstants.URL_TOKEN_DOWNLOAD, filename);
}
destFile = new File(ForgeConstants.CACHE_TOKEN_PICS_DIR, filename);