Merge branch 'sentry2019Breadcrumbs' into 'master'

Sentry: add more Breadcumbs

See merge request core-developers/forge!1274
This commit is contained in:
Michael Kamensky
2019-01-13 09:44:15 +00:00
3 changed files with 256 additions and 233 deletions

View File

@@ -28,6 +28,8 @@ import forge.game.cost.Cost;
import forge.game.spellability.*; import forge.game.spellability.*;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import forge.util.FileSection; import forge.util.FileSection;
import io.sentry.Sentry;
import io.sentry.event.BreadcrumbBuilder;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -130,7 +132,16 @@ public final class AbilityFactory {
String source = state.getName().isEmpty() ? abString : state.getName(); String source = state.getName().isEmpty() ? abString : state.getName();
throw new RuntimeException("AbilityFactory : getAbility -- no API in " + source + ": " + abString); throw new RuntimeException("AbilityFactory : getAbility -- no API in " + source + ": " + abString);
} }
return getAbility(mapParams, type, state, parent); try {
return getAbility(mapParams, type, state, parent);
} catch (Error | Exception ex) {
String msg = "AbilityFactory:getAbility: crash when trying to create ability ";
Sentry.getContext().recordBreadcrumb(
new BreadcrumbBuilder().setMessage(msg)
.withData("Card", state.getName()).withData("Ability", abString).build()
);
throw new RuntimeException(msg + " of card: " + state.getName(), ex);
}
} }
public static final SpellAbility getAbility(final Card hostCard, final String svar) { public static final SpellAbility getAbility(final Card hostCard, final String svar) {

View File

@@ -1447,250 +1447,260 @@ public class Card extends GameEntity implements Comparable<Card> {
int i = 0; int i = 0;
for (KeywordInterface inst : keywords) { for (KeywordInterface inst : keywords) {
String keyword = inst.getOriginal(); String keyword = inst.getOriginal();
if (keyword.startsWith("SpellCantTarget")) { try {
continue; if (keyword.startsWith("SpellCantTarget")) {
} continue;
// format text changes }
if (CardUtil.isKeywordModifiable(keyword) // format text changes
&& keywordsGrantedByTextChanges.contains(inst)) { if (CardUtil.isKeywordModifiable(keyword)
for (final Entry<String, String> e : textChanges) { && keywordsGrantedByTextChanges.contains(inst)) {
final String value = e.getValue(); for (final Entry<String, String> e : textChanges) {
if (keyword.contains(value)) { final String value = e.getValue();
keyword = TextUtil.fastReplace(keyword, value, if (keyword.contains(value)) {
TextUtil.concatNoSpace("<strike>", e.getKey(), "</strike> ", value)); keyword = TextUtil.fastReplace(keyword, value,
// assume (for now) max one change per keyword TextUtil.concatNoSpace("<strike>", e.getKey(), "</strike> ", value));
break; // assume (for now) max one change per keyword
break;
}
} }
} }
} if (keyword.startsWith("CantBeCounteredBy")) {
if (keyword.startsWith("CantBeCounteredBy")) { final String[] p = keyword.split(":");
final String[] p = keyword.split(":"); sbLong.append(p[2]).append("\r\n");
sbLong.append(p[2]).append("\r\n"); } else if (keyword.startsWith("etbCounter")) {
} else if (keyword.startsWith("etbCounter")) { final String[] p = keyword.split(":");
final String[] p = keyword.split(":"); final StringBuilder s = new StringBuilder();
final StringBuilder s = new StringBuilder(); if (p.length > 4) {
if (p.length > 4) { if (!"no desc".equals(p[4])) {
if (!"no desc".equals(p[4])) { s.append(p[4]);
s.append(p[4]); }
} else {
s.append(getName());
s.append(" enters the battlefield with ");
s.append(Lang.nounWithNumeral(p[2], CounterType.valueOf(p[1]).getName() + " counter"));
s.append(" on it.");
} }
} else { sbLong.append(s).append("\r\n");
s.append(getName()); } else if (keyword.startsWith("Protection:")) {
s.append(" enters the battlefield with "); final String[] k = keyword.split(":");
s.append(Lang.nounWithNumeral(p[2], CounterType.valueOf(p[1]).getName() + " counter")); sbLong.append(k[2]).append("\r\n");
s.append(" on it."); } else if (keyword.startsWith("Creatures can't attack unless their controller pays")) {
} final String[] k = keyword.split(":");
sbLong.append(s).append("\r\n"); if (!k[3].equals("no text")) {
} else if (keyword.startsWith("Protection:")) { sbLong.append(k[3]).append("\r\n");
final String[] k = keyword.split(":");
sbLong.append(k[2]).append("\r\n");
} else if (keyword.startsWith("Creatures can't attack unless their controller pays")) {
final String[] k = keyword.split(":");
if (!k[3].equals("no text")) {
sbLong.append(k[3]).append("\r\n");
}
} else if (keyword.startsWith("Enchant")) {
String k = keyword;
k = TextUtil.fastReplace(k, "Curse", "");
sbLong.append(k).append("\r\n");
} else if (keyword.startsWith("Ripple")) {
sbLong.append(TextUtil.fastReplace(keyword, ":", " ")).append("\r\n");
} else if (keyword.startsWith("Madness")) {
String[] parts = keyword.split(":");
// If no colon exists in Madness keyword, it must have been granted and assumed the cost from host
if (parts.length < 2) {
sbLong.append(parts[0]).append(" ").append(this.getManaCost()).append("\r\n");
} else {
sbLong.append(parts[0]).append(" ").append(ManaCostParser.parse(parts[1])).append("\r\n");
}
} else if (keyword.startsWith("Morph") || keyword.startsWith("Megamorph")) {
String[] k = keyword.split(":");
sbLong.append(k[0]);
if (k.length > 1) {
final Cost mCost = new Cost(k[1], true);
if (!mCost.isOnlyManaCost()) {
sbLong.append("");
} }
if (mCost.isOnlyManaCost()) { } else if (keyword.startsWith("Enchant")) {
sbLong.append(" "); String k = keyword;
k = TextUtil.fastReplace(k, "Curse", "");
sbLong.append(k).append("\r\n");
} else if (keyword.startsWith("Ripple")) {
sbLong.append(TextUtil.fastReplace(keyword, ":", " ")).append("\r\n");
} else if (keyword.startsWith("Madness")) {
String[] parts = keyword.split(":");
// If no colon exists in Madness keyword, it must have been granted and assumed the cost from host
if (parts.length < 2) {
sbLong.append(parts[0]).append(" ").append(this.getManaCost()).append("\r\n");
} else {
sbLong.append(parts[0]).append(" ").append(ManaCostParser.parse(parts[1])).append("\r\n");
} }
sbLong.append(mCost.toString()).delete(sbLong.length() - 2, sbLong.length()); } else if (keyword.startsWith("Morph") || keyword.startsWith("Megamorph")) {
if (!mCost.isOnlyManaCost()) { String[] k = keyword.split(":");
sbLong.append("."); sbLong.append(k[0]);
if (k.length > 1) {
final Cost mCost = new Cost(k[1], true);
if (!mCost.isOnlyManaCost()) {
sbLong.append("");
}
if (mCost.isOnlyManaCost()) {
sbLong.append(" ");
}
sbLong.append(mCost.toString()).delete(sbLong.length() - 2, sbLong.length());
if (!mCost.isOnlyManaCost()) {
sbLong.append(".");
}
sbLong.append(" (" + inst.getReminderText() + ")");
sbLong.append("\r\n");
} }
} else if (keyword.startsWith("Emerge")) {
final String[] k = keyword.split(":");
sbLong.append(k[0]).append(" ").append(ManaCostParser.parse(k[1]));
sbLong.append(" (" + inst.getReminderText() + ")"); sbLong.append(" (" + inst.getReminderText() + ")");
sbLong.append("\r\n"); sbLong.append("\r\n");
} } else if (keyword.startsWith("Echo")) {
} else if (keyword.startsWith("Emerge")) { sbLong.append("Echo ");
final String[] k = keyword.split(":"); final String[] upkeepCostParams = keyword.split(":");
sbLong.append(k[0]).append(" ").append(ManaCostParser.parse(k[1])); sbLong.append(upkeepCostParams.length > 2 ? "- " + upkeepCostParams[2] : ManaCostParser.parse(upkeepCostParams[1]));
sbLong.append(" (" + inst.getReminderText() + ")"); sbLong.append(" (At the beginning of your upkeep, if CARDNAME came under your control since the beginning of your last upkeep, sacrifice it unless you pay its echo cost.)");
sbLong.append("\r\n"); sbLong.append("\r\n");
} else if (keyword.startsWith("Echo")) { } else if (keyword.startsWith("Cumulative upkeep")) {
sbLong.append("Echo "); sbLong.append("Cumulative upkeep ");
final String[] upkeepCostParams = keyword.split(":"); final String[] upkeepCostParams = keyword.split(":");
sbLong.append(upkeepCostParams.length > 2 ? "- " + upkeepCostParams[2] : ManaCostParser.parse(upkeepCostParams[1])); sbLong.append(upkeepCostParams.length > 2 ? "- " + upkeepCostParams[2] : ManaCostParser.parse(upkeepCostParams[1]));
sbLong.append(" (At the beginning of your upkeep, if CARDNAME came under your control since the beginning of your last upkeep, sacrifice it unless you pay its echo cost.)"); sbLong.append("\r\n");
sbLong.append("\r\n"); } else if (keyword.startsWith("Alternative Cost")) {
} else if (keyword.startsWith("Cumulative upkeep")) { sbLong.append("Has alternative cost.");
sbLong.append("Cumulative upkeep "); } else if (keyword.startsWith("AlternateAdditionalCost")) {
final String[] upkeepCostParams = keyword.split(":"); final String costString1 = keyword.split(":")[1];
sbLong.append(upkeepCostParams.length > 2 ? "- " + upkeepCostParams[2] : ManaCostParser.parse(upkeepCostParams[1])); final String costString2 = keyword.split(":")[2];
sbLong.append("\r\n"); final Cost cost1 = new Cost(costString1, false);
} else if (keyword.startsWith("Alternative Cost")) { final Cost cost2 = new Cost(costString2, false);
sbLong.append("Has alternative cost."); sbLong.append("As an additional cost to cast ")
} else if (keyword.startsWith("AlternateAdditionalCost")) { .append(getName()).append(", ")
final String costString1 = keyword.split(":")[1]; .append(cost1.toSimpleString())
final String costString2 = keyword.split(":")[2]; .append(" or pay ")
final Cost cost1 = new Cost(costString1, false); .append(cost2.toSimpleString())
final Cost cost2 = new Cost(costString2, false); .append(".\r\n");
sbLong.append("As an additional cost to cast ") } else if (keyword.startsWith("Multikicker")) {
.append(getName()).append(", ") if (!keyword.endsWith("Generic")) {
.append(cost1.toSimpleString()) final String[] n = keyword.split(":");
.append(" or pay ") final Cost cost = new Cost(n[1], false);
.append(cost2.toSimpleString()) sbLong.append("Multikicker ").append(cost.toSimpleString());
.append(".\r\n"); sbLong.append(" (" + inst.getReminderText() + ")").append("\r\n");
} else if (keyword.startsWith("Multikicker")) {
if (!keyword.endsWith("Generic")) {
final String[] n = keyword.split(":");
final Cost cost = new Cost(n[1], false);
sbLong.append("Multikicker ").append(cost.toSimpleString());
sbLong.append(" (" + inst.getReminderText() + ")").append("\r\n");
}
} else if (keyword.startsWith("Kicker")) {
if (!keyword.endsWith("Generic")) {
final StringBuilder sbx = new StringBuilder();
final String[] n = keyword.split(":");
sbx.append("Kicker ");
final Cost cost = new Cost(n[1], false);
sbx.append(cost.toSimpleString());
if (Lists.newArrayList(n).size() > 2) {
sbx.append(" and/or ");
final Cost cost2 = new Cost(n[2], false);
sbx.append(cost2.toSimpleString());
} }
sbx.append(" (" + inst.getReminderText() + ")"); } else if (keyword.startsWith("Kicker")) {
sbLong.append(sbx).append("\r\n"); if (!keyword.endsWith("Generic")) {
} final StringBuilder sbx = new StringBuilder();
} else if (keyword.startsWith("Hexproof:")) { final String[] n = keyword.split(":");
final String k[] = keyword.split(":"); sbx.append("Kicker ");
sbLong.append("Hexproof from ").append(k[2]) final Cost cost = new Cost(n[1], false);
.append(" (").append(inst.getReminderText()).append(")").append("\r\n"); sbx.append(cost.toSimpleString());
} else if (keyword.endsWith(".") && !keyword.startsWith("Haunt")) { if (Lists.newArrayList(n).size() > 2) {
sbLong.append(keyword).append("\r\n"); sbx.append(" and/or ");
} else if (keyword.startsWith("Presence") || keyword.startsWith("MayFlash")) { final Cost cost2 = new Cost(n[2], false);
// Pseudo keywords, only print Reminder sbx.append(cost2.toSimpleString());
sbLong.append(inst.getReminderText()); }
} else if (keyword.contains("At the beginning of your upkeep, ") sbx.append(" (" + inst.getReminderText() + ")");
&& keyword.contains(" unless you pay")) { sbLong.append(sbx).append("\r\n");
sbLong.append(keyword).append("\r\n"); }
} else if (keyword.startsWith("Strive") || keyword.startsWith("Escalate") } else if (keyword.startsWith("Hexproof:")) {
|| keyword.startsWith("ETBReplacement") final String k[] = keyword.split(":");
|| keyword.startsWith("CantBeBlockedBy ") sbLong.append("Hexproof from ").append(k[2])
|| keyword.startsWith("Affinity") .append(" (").append(inst.getReminderText()).append(")").append("\r\n");
|| keyword.equals("CARDNAME enters the battlefield tapped.") } else if (keyword.endsWith(".") && !keyword.startsWith("Haunt")) {
|| keyword.startsWith("UpkeepCost")) { sbLong.append(keyword).append("\r\n");
} else if (keyword.equals("Provoke") || keyword.equals("Ingest") || keyword.equals("Unleash") } else if (keyword.startsWith("Presence") || keyword.startsWith("MayFlash")) {
|| keyword.equals("Soulbond") || keyword.equals("Partner") || keyword.equals("Retrace") // Pseudo keywords, only print Reminder
|| keyword.equals("Living Weapon") || keyword.equals("Myriad") || keyword.equals("Exploit") sbLong.append(inst.getReminderText());
|| keyword.equals("Changeling") || keyword.equals("Delve") } else if (keyword.contains("At the beginning of your upkeep, ")
|| keyword.equals("Split second") && keyword.contains(" unless you pay")) {
|| keyword.equals("Suspend") // for the ones without amounnt sbLong.append(keyword).append("\r\n");
|| keyword.equals("Hideaway") || keyword.equals("Ascend") } else if (keyword.startsWith("Strive") || keyword.startsWith("Escalate")
|| keyword.equals("Totem armor") || keyword.equals("Battle cry") || keyword.startsWith("ETBReplacement")
|| keyword.equals("Devoid") || keyword.equals("Riot")){ || keyword.startsWith("CantBeBlockedBy ")
sbLong.append(keyword + " (" + inst.getReminderText() + ")"); || keyword.startsWith("Affinity")
} else if (keyword.startsWith("Partner:")) { || keyword.equals("CARDNAME enters the battlefield tapped.")
final String[] k = keyword.split(":"); || keyword.startsWith("UpkeepCost")) {
sbLong.append("Partner with " + k[1] + " (" + inst.getReminderText() + ")"); } else if (keyword.equals("Provoke") || keyword.equals("Ingest") || keyword.equals("Unleash")
} else if (keyword.startsWith("Modular") || keyword.startsWith("Bloodthirst") || keyword.startsWith("Dredge") || keyword.equals("Soulbond") || keyword.equals("Partner") || keyword.equals("Retrace")
|| keyword.startsWith("Fabricate") || keyword.startsWith("Soulshift") || keyword.startsWith("Bushido") || keyword.equals("Living Weapon") || keyword.equals("Myriad") || keyword.equals("Exploit")
|| keyword.startsWith("Crew") || keyword.startsWith("Tribute") || keyword.startsWith("Absorb") || keyword.equals("Changeling") || keyword.equals("Delve")
|| keyword.startsWith("Graft") || keyword.startsWith("Fading") || keyword.startsWith("Vanishing") || keyword.equals("Split second")
|| keyword.startsWith("Afterlife") || keyword.equals("Suspend") // for the ones without amounnt
|| keyword.startsWith("Afflict") || keyword.startsWith ("Poisonous") || keyword.startsWith("Rampage") || keyword.equals("Hideaway") || keyword.equals("Ascend")
|| keyword.startsWith("Renown") || keyword.startsWith("Annihilator") || keyword.startsWith("Devour")) { || keyword.equals("Totem armor") || keyword.equals("Battle cry")
final String[] k = keyword.split(":"); || keyword.equals("Devoid") || keyword.equals("Riot")){
sbLong.append(k[0] + " " + k[1] + " (" + inst.getReminderText() + ")"); sbLong.append(keyword + " (" + inst.getReminderText() + ")");
} else if (keyword.contains("Haunt")) { } else if (keyword.startsWith("Partner:")) {
sb.append("\r\nHaunt (");
if (isCreature()) {
sb.append("When this creature dies, exile it haunting target creature.");
} else {
sb.append("When this spell card is put into a graveyard after resolving, ");
sb.append("exile it haunting target creature.");
}
sb.append(")");
} else if (keyword.equals("Convoke") || keyword.equals("Dethrone")|| keyword.equals("Fear")
|| keyword.equals("Melee") || keyword.equals("Improvise")|| keyword.equals("Shroud")
|| keyword.equals("Banding") || keyword.equals("Intimidate")|| keyword.equals("Evolve")
|| keyword.equals("Exalted") || keyword.equals("Extort")|| keyword.equals("Flanking")
|| keyword.equals("Horsemanship") || keyword.equals("Infect")|| keyword.equals("Persist")
|| keyword.equals("Phasing") || keyword.equals("Shadow")|| keyword.equals("Skulk")
|| keyword.equals("Undying") || keyword.equals("Wither") || keyword.equals("Cascade")
|| keyword.equals("Mentor")) {
if (sb.length() != 0) {
sb.append("\r\n");
}
sb.append(keyword + " (" + inst.getReminderText() + ")");
} else if (keyword.endsWith(" offering")) {
String offeringType = keyword.split(" ")[0];
if (sb.length() != 0) {
sb.append("\r\n");
}
sbLong.append(keyword);
sbLong.append(" (" + Keyword.getInstance("Offering:"+ offeringType).getReminderText() + ")");
} else if (keyword.startsWith("Equip") || keyword.startsWith("Fortify") || keyword.startsWith("Outlast")
|| keyword.startsWith("Unearth") || keyword.startsWith("Scavenge") || keyword.startsWith("Spectacle")
|| keyword.startsWith("Evoke") || keyword.startsWith("Bestow") || keyword.startsWith("Dash")
|| keyword.startsWith("Surge") || keyword.startsWith("Transmute") || keyword.startsWith("Suspend")
|| keyword.equals("Undaunted") || keyword.startsWith("Monstrosity") || keyword.startsWith("Embalm")
|| keyword.startsWith("Level up") || keyword.equals("Prowess") || keyword.startsWith("Eternalize")
|| keyword.startsWith("Reinforce") || keyword.startsWith("Champion") || keyword.startsWith("Prowl")
|| keyword.startsWith("Amplify") || keyword.startsWith("Ninjutsu") || keyword.startsWith("Adapt")
|| keyword.startsWith("Cycling") || keyword.startsWith("TypeCycling")) {
// keyword parsing takes care of adding a proper description
} else if (keyword.startsWith("CantBeBlockedByAmount")) {
sbLong.append(getName()).append(" can't be blocked ");
sbLong.append(getTextForKwCantBeBlockedByAmount(keyword));
} else if (keyword.startsWith("CantBlock")) {
sbLong.append(getName()).append(" can't block ");
if (keyword.contains("CardUID")) {
sbLong.append("CardID (").append(Integer.valueOf(keyword.split("CantBlockCardUID_")[1])).append(")");
} else {
final String[] k = keyword.split(":"); final String[] k = keyword.split(":");
sbLong.append(k.length > 1 ? k[1] + ".\r\n" : ""); sbLong.append("Partner with " + k[1] + " (" + inst.getReminderText() + ")");
} else if (keyword.startsWith("Modular") || keyword.startsWith("Bloodthirst") || keyword.startsWith("Dredge")
|| keyword.startsWith("Fabricate") || keyword.startsWith("Soulshift") || keyword.startsWith("Bushido")
|| keyword.startsWith("Crew") || keyword.startsWith("Tribute") || keyword.startsWith("Absorb")
|| keyword.startsWith("Graft") || keyword.startsWith("Fading") || keyword.startsWith("Vanishing")
|| keyword.startsWith("Afterlife")
|| keyword.startsWith("Afflict") || keyword.startsWith ("Poisonous") || keyword.startsWith("Rampage")
|| keyword.startsWith("Renown") || keyword.startsWith("Annihilator") || keyword.startsWith("Devour")) {
final String[] k = keyword.split(":");
sbLong.append(k[0] + " " + k[1] + " (" + inst.getReminderText() + ")");
} else if (keyword.contains("Haunt")) {
sb.append("\r\nHaunt (");
if (isCreature()) {
sb.append("When this creature dies, exile it haunting target creature.");
} else {
sb.append("When this spell card is put into a graveyard after resolving, ");
sb.append("exile it haunting target creature.");
}
sb.append(")");
} else if (keyword.equals("Convoke") || keyword.equals("Dethrone")|| keyword.equals("Fear")
|| keyword.equals("Melee") || keyword.equals("Improvise")|| keyword.equals("Shroud")
|| keyword.equals("Banding") || keyword.equals("Intimidate")|| keyword.equals("Evolve")
|| keyword.equals("Exalted") || keyword.equals("Extort")|| keyword.equals("Flanking")
|| keyword.equals("Horsemanship") || keyword.equals("Infect")|| keyword.equals("Persist")
|| keyword.equals("Phasing") || keyword.equals("Shadow")|| keyword.equals("Skulk")
|| keyword.equals("Undying") || keyword.equals("Wither") || keyword.equals("Cascade")
|| keyword.equals("Mentor")) {
if (sb.length() != 0) {
sb.append("\r\n");
}
sb.append(keyword + " (" + inst.getReminderText() + ")");
} else if (keyword.endsWith(" offering")) {
String offeringType = keyword.split(" ")[0];
if (sb.length() != 0) {
sb.append("\r\n");
}
sbLong.append(keyword);
sbLong.append(" (" + Keyword.getInstance("Offering:"+ offeringType).getReminderText() + ")");
} else if (keyword.startsWith("Equip") || keyword.startsWith("Fortify") || keyword.startsWith("Outlast")
|| keyword.startsWith("Unearth") || keyword.startsWith("Scavenge") || keyword.startsWith("Spectacle")
|| keyword.startsWith("Evoke") || keyword.startsWith("Bestow") || keyword.startsWith("Dash")
|| keyword.startsWith("Surge") || keyword.startsWith("Transmute") || keyword.startsWith("Suspend")
|| keyword.equals("Undaunted") || keyword.startsWith("Monstrosity") || keyword.startsWith("Embalm")
|| keyword.startsWith("Level up") || keyword.equals("Prowess") || keyword.startsWith("Eternalize")
|| keyword.startsWith("Reinforce") || keyword.startsWith("Champion") || keyword.startsWith("Prowl")
|| keyword.startsWith("Amplify") || keyword.startsWith("Ninjutsu") || keyword.startsWith("Adapt")
|| keyword.startsWith("Cycling") || keyword.startsWith("TypeCycling")) {
// keyword parsing takes care of adding a proper description
} else if (keyword.startsWith("CantBeBlockedByAmount")) {
sbLong.append(getName()).append(" can't be blocked ");
sbLong.append(getTextForKwCantBeBlockedByAmount(keyword));
} else if (keyword.startsWith("CantBlock")) {
sbLong.append(getName()).append(" can't block ");
if (keyword.contains("CardUID")) {
sbLong.append("CardID (").append(Integer.valueOf(keyword.split("CantBlockCardUID_")[1])).append(")");
} else {
final String[] k = keyword.split(":");
sbLong.append(k.length > 1 ? k[1] + ".\r\n" : "");
}
} else if (keyword.equals("Unblockable")) {
sbLong.append(getName()).append(" can't be blocked.\r\n");
} else if (keyword.equals("AllNonLegendaryCreatureNames")) {
sbLong.append(getName()).append(" has all names of nonlegendary creature cards.\r\n");
} else if (keyword.startsWith("IfReach")) {
String k[] = keyword.split(":");
sbLong.append(getName()).append(" can block ")
.append(CardType.getPluralType(k[1]))
.append(" as though it had reach.\r\n");
} else if (keyword.startsWith("MayEffectFromOpeningHand")) {
final String[] k = keyword.split(":");
// need to get SpellDescription from Svar
String desc = AbilityFactory.getMapParams(getSVar(k[1])).get("SpellDescription");
sbLong.append(desc);
} else if (keyword.startsWith("Saga")) {
String k[] = keyword.split(":");
String desc = "(As this Saga enters and after your draw step, "
+ " add a lore counter. Sacrifice after " + Strings.repeat("I", Integer.valueOf(k[1])) + ".)";
sbLong.append(desc);
} }
} else if (keyword.equals("Unblockable")) { else {
sbLong.append(getName()).append(" can't be blocked.\r\n"); if ((i != 0) && (sb.length() != 0)) {
} else if (keyword.equals("AllNonLegendaryCreatureNames")) { sb.append(", ");
sbLong.append(getName()).append(" has all names of nonlegendary creature cards.\r\n"); }
} else if (keyword.startsWith("IfReach")) { sb.append(keyword);
String k[] = keyword.split(":"); }
sbLong.append(getName()).append(" can block ") if (sbLong.length() > 0) {
.append(CardType.getPluralType(k[1])) sbLong.append("\r\n");
.append(" as though it had reach.\r\n");
} else if (keyword.startsWith("MayEffectFromOpeningHand")) {
final String[] k = keyword.split(":");
// need to get SpellDescription from Svar
String desc = AbilityFactory.getMapParams(getSVar(k[1])).get("SpellDescription");
sbLong.append(desc);
} else if (keyword.startsWith("Saga")) {
String k[] = keyword.split(":");
String desc = "(As this Saga enters and after your draw step, "
+ " add a lore counter. Sacrifice after " + Strings.repeat("I", Integer.valueOf(k[1])) + ".)";
sbLong.append(desc);
}
else {
if ((i != 0) && (sb.length() != 0)) {
sb.append(", ");
} }
sb.append(keyword);
}
if (sbLong.length() > 0) {
sbLong.append("\r\n");
}
i++; i++;
} catch (Exception e) {
String msg = "Card:keywordToText: crash in Keyword parsing";
Sentry.getContext().recordBreadcrumb(
new BreadcrumbBuilder().setMessage(msg)
.withData("Card", this.getName()).withData("Keyword", keyword).build()
);
throw new RuntimeException("Error in Card " + this.getName() + " with Keyword " + keyword, e);
}
} }
if (sb.length() > 0) { if (sb.length() > 0) {
sb.append("\r\n"); sb.append("\r\n");

View File

@@ -430,6 +430,9 @@ public class CardFactory {
private static void readCardFace(Card c, ICardFace face) { private static void readCardFace(Card c, ICardFace face) {
// Name first so Senty has the Card name
c.setName(face.getName());
for (String r : face.getReplacements()) c.addReplacementEffect(ReplacementHandler.parseReplacement(r, c, true)); for (String r : face.getReplacements()) c.addReplacementEffect(ReplacementHandler.parseReplacement(r, c, true));
for (String s : face.getStaticAbilities()) c.addStaticAbility(s); for (String s : face.getStaticAbilities()) c.addStaticAbility(s);
for (String t : face.getTriggers()) c.addTrigger(TriggerHandler.parseTrigger(t, c, true)); for (String t : face.getTriggers()) c.addTrigger(TriggerHandler.parseTrigger(t, c, true));
@@ -439,7 +442,6 @@ public class CardFactory {
// keywords not before variables // keywords not before variables
c.addIntrinsicKeywords(face.getKeywords(), false); c.addIntrinsicKeywords(face.getKeywords(), false);
c.setName(face.getName());
c.setManaCost(face.getManaCost()); c.setManaCost(face.getManaCost());
c.setText(face.getNonAbilityText()); c.setText(face.getNonAbilityText());