Files
MagicSetEditor2/data/magic.mse-game/script
T
pichoro af4a13ce96 Fixing type statistics in Magic - had a minor error.
git-svn-id: svn://svn.code.sf.net/p/magicseteditor/code/trunk@1502 0fc631ac-6414-0410-93d0-97cfa31319b6
2010-08-15 02:15:52 +00:00

633 lines
24 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
############################################################## Localization
include file: language
############################################################## Sorting mana symbols
# correctly sort a mana symbol (no guild mana)
mana_sort := sort_text@(order: "XYZI[0123456789]S(WUBRG)")
# correctly sort guild mana
mana_sort_guild := sort_text@(order: "[XYZI01234567890SWUBRG/|]") +
replace@(
# No lookbehind :(
#match: "(?<!/)(./.|././.|./././.|.[|])(?!/)",
match: "./.|././.|./././.|.[|]",
in_context: "(^|[^/])<match>($|[^/])",
replace: {sort_text(order:"in_place((WUBRG)")}
)
mana_has_guild := match@(match: "[/|]") # Is there guild or half mana in the input?
# A mana cost can contain both normal and guild mana
mana_filter := to_upper + {
if mana_has_guild() then mana_sort_guild()
else mana_sort()
}
# Like mana filter, only also allow tap symbols:
tap_filter := sort_text@(order: "<CTQ>")
mana_filter_t := replace@( # Remove [] used for forcing mana symbols
match: "[\\[\\]]",
replace: ""
) + { tap_filter() + mana_filter() }
############################################################## Determine card color
# Names of colors
color_name := {
if input = "W" then "white"
else if input = "U" then "blue"
else if input = "B" then "black"
else if input = "R" then "red"
else if input = "G" then "green"
else ""
}
color_names_1 := { color_name(colors.0) }
color_names_2 := { color_name(colors.0) + ", " + color_name(colors.1) }
color_names_3 := { color_name(colors.0) + ", " + color_name(colors.1) + ", " + color_name(colors.2) }
color_names_4 := { color_name(colors.0) + ", " + color_name(colors.1) + ", " + color_name(colors.2) + ", " + color_name(colors.3) }
color_names_5 := { color_name(colors.0) + ", " + color_name(colors.1) + ", " + color_name(colors.2) + ", " + color_name(colors.3) + ", " + color_name(colors.4) }
# color based on mana cost, input = a mana cost
color_filter := sort_text@(order: "<WUBRG>")
color_filterH := sort_text@(order: "</>")
mana_to_color := {
count := number_of_items(in: colors)
if hybrid == "" and contains(type, match:"Artifact") then
# not a hybrid, but artifact
if count == 0 then "artifact"
else if count == 1 then color_names_1() + ", artifact"
else if set.set_info.use_gradient_multicolor == "no" then "artifact, multicolor" # stop here
else if count == 2 then color_names_2() + ", artifact, multicolor"
else if set.set_info.use_gradient_multicolor != "yes" then "artifact, multicolor" # stop here
else if count == 3 then color_names_3() + ", artifact, multicolor"
else if count == 4 then color_names_4() + ", artifact, multicolor"
else if count == 5 then color_names_5() + ", artifact, multicolor"
else "artifact, multicolor"
else if hybrid == "" then
# not a hybrid, not artifact
if count == 0 then "colorless"
else if count == 1 then color_names_1()
else if set.set_info.use_gradient_multicolor == "no" then "multicolor" # stop here
else if count == 2 then color_names_2() + ", multicolor"
else if set.set_info.use_gradient_multicolor != "yes" then "multicolor" # stop here
else if count == 3 then color_names_3() + ", multicolor"
else if count == 4 then color_names_4() + ", multicolor"
else if count == 5 then color_names_5() + ", multicolor"
else "multicolor"
else if contains(type, match:"Artifact") then
# hybrid, but artifact
if count == 0 then "artifact"
else if count == 1 then color_names_1() + ", artifact"
else if count == 2 then color_names_2() + ", artifact"
else "artifact, multicolor"
else
# hybrid, not artifact
if count == 0 then "colorless"
else if count == 1 then color_names_1()
else if count == 2 then color_names_2() + ", hybrid"
else "multicolor"
}
# color based on land text box, input = textbox contents
color_text_filter :=
# remove activation costs
replace@(
match: "<sym[^>]*>[^<]+</sym[^>]*>"
in_context: "(?ix) (\\n|^)[^:]*<match>(,|:) | (pays?|additional|costs?)[ ]<match>",
replace: ""
) +
# keep only mana
filter_text@(match: "<sym[^>]*>([^<]+)") + color_filter;
# get the land frame for a "WUBRG"-style input.
land_multicolor := {
count := number_of_items(in: colors)
if count == 0 then "land"
else if count == 1 then color_names_1() + ", land"
else if count == 2 then color_names_2() + ", land"
else "land, multicolor"
}
land_to_color := {
# Based on watermark
if watermark = "mana symbol white" then "white, land"
else if watermark = "mana symbol blue" then "blue, land"
else if watermark = "mana symbol black" then "black, land"
else if watermark = "mana symbol red" then "red, land"
else if watermark = "mana symbol green" then "green, land"
else land_multicolor(colors:color_text_filter(input: card.rule_text))
};
# Look for a CDA that defines colors
text_to_color := {
# Note: running filter_text is quite slow, do a quick 'contains' check first
if contains(match: card_name) then (
text := filter_text(match: "is (colorless|all colors|((blue|white|green|red|black)((,|,? and) (blue|white|green|red|black))*))", in_context: regex_escape(card_name)+"(</[-a-z]+>)* <match>\\.")
if text != "" then (
if contains(text, match: "all colors") then (
colors := "WUBRG"
if land = "land" then land_multicolor()
else mana_to_color(hybrid: "")
) else (
colors := ""
if contains(text, match: "white") then colors := colors + "W"
if contains(text, match: "blue") then colors := colors + "U"
if contains(text, match: "black") then colors := colors + "B"
if contains(text, match: "red") then colors := colors + "R"
if contains(text, match: "green") then colors := colors + "G"
if land = "land" then land_multicolor()
else mana_to_color(hybrid: "")
)
)
)
}
# The color of a card
is_creature := match@(match: "(?i)Creature")
is_tribal := match@(match: "(?i)Tribal")
is_artifact := match@(match: "(?i)Artifact")
is_land := match@(match: "(?i)Land")
is_enchantment := match@(match: "(?i)Enchantment")
is_spell := match@(match: "(?i)Instant|Sorcery")
card_color := {
# usually the color of mana
text_color := text_to_color(rules_text, land: is_land(type));
if text_color == "" then (
mana_color := mana_to_color(colors: color_filter(casting_cost), hybrid: color_filterH(casting_cost))
if mana_color == "colorless" and is_land (type) then land_to_color(watermark)
else if mana_color == "colorless" and is_artifact(type) then "artifact"
else if mana_color == "colorless" and contains(card.shape, match:"flip") then default
else mana_color
)
else text_color
};
# Number of colors in a card_color
card_color_color_count := count_chosen@(choices: "white,blue,black,red,green,artifact")
# Clean up color field
card_color_filter := {
colors := card_color_color_count()
if colors > 2 then
input := remove_choice(choice: "overlay")
if colors > 1 then (
input := require_choice(choices: "multicolor, hybrid, land, artifact")
input := exclusive_choice(choices: "multicolor, hybrid")
input := require_exclusive_choice(choices: "horizontal, vertical, radial, overlay")
) else
input := remove_choice(choices: "radial, horizontal, vertical, overlay, hybrid, reversed")
if chosen(choice:"overlay") then
input := remove_choice(choice: "reversed")
input
}
# needed by all style files anyway
include file: /magic-blends.mse-include/new-blends
############################################################## Card number
# Index for sorting, white cards are first, so white->A, blue->B, .. ,
# The code consists of 3 parts:
# color, shifted, split
sort_index := {
color_of_card() +
(if contains(card.shape, match:"shifted") then "S" else " ") + # planeshifted cards come after normal ones
(if contains(card.shape, match:"split") then "S" else " ") + # split cards come after normal ones
":"
}
# Process the name for sorting rules
sort_name :=
# Remove "The", "A", and "And" at the beginning
replace@(match: "^(The|An?) ", replace: "") +
# Remove commas and apostrophes
replace@(match: "(,|'|)", replace: "") +
# Remove bold and italic tags
replace@(match: "(<b>|<i>|</b>|</i>)", replace: "") +
# Make lowercase
to_lower
is_multicolor := { chosen(choice: "multicolor") and input != "artifact, multicolor" }
is_null_cost := { input == "" or input == "0" }
is_hybrid_cost := { contains(card.casting_cost, match: "W/") or contains(card.casting_cost, match: "U/") or contains(card.casting_cost, match: "B/") or contains(card.casting_cost, match: "R/") or contains(card.casting_cost, match: "G/") }
basic_land_sort := {
if contains(card.name, match:"Plains") then "MB" # Plains
else if contains(card.name, match:"Island") then "MC" # Islands
else if contains(card.name, match:"Swamp") then "MD" # Swamps
else if contains(card.name, match:"Mountain") then "ME" # Mountains
else if contains(card.name, match:"Forest") then "MF" # Forests
else "MA" # other basic lands
}
hybrid_color_pair_sort := {
colors := sort_text(casting_cost, order: "<WUBRG>")
if colors = "WU" then "HA"
else if colors = "UB" then "HB"
else if colors = "BR" then "HC"
else if colors = "RG" then "HD"
else if colors = "WG" then "HE"
else if colors = "WB" then "HF"
else if colors = "UR" then "HG"
else if colors = "BG" then "HH"
else if colors = "WR" then "HI"
else if colors = "UG" then "HJ"
else "HK"
}
multi_color_pair_sort := {
colors := sort_text(casting_cost, order: "<WUBRG>")
if colors = "WU" then "GA"
else if colors = "UB" then "GB"
else if colors = "BR" then "GC"
else if colors = "RG" then "GD"
else if colors = "WG" then "GE"
else if colors = "WB" then "GF"
else if colors = "UR" then "GG"
else if colors = "BG" then "GH"
else if colors = "WR" then "GI"
else if colors = "UG" then "GJ"
else if contains(card.casting_cost, match:"/") then "GL"
else "GK"
}
# A code for the color of the card
color_of_card := {
card_color := card.card_color
casting_cost := card.casting_cost
type := card.super_type
if card.shape == "split" and
card_color != card.card_color_2 then "I" # Diff Color Splits
else if chosen(choice: "land", card_color) then ( # Lands
if card.rarity != "basic land" then "L" # Nonbasic Land
else basic_land_sort() # Basic Land
) else if is_null_cost(casting_cost) then ( # Non-Land Cards with no or zero costs.
if chosen(choice: "colorless", card_color) then "A" # Clear Colorless
else if chosen(choice: "hybrid", card_color) then "HK" # Hybrids
else if is_multicolor(card_color) then "GK" # Multicolor
else if chosen(choice:"white", card_color) then "B" # White
else if chosen(choice:"blue", card_color) then "C" # Blue
else if chosen(choice:"black", card_color) then "D" # Black
else if chosen(choice:"red", card_color) then "E" # Red
else if chosen(choice:"green", card_color) then "F" # Green
else "J" # Artifact
) else (
# Cards with costs.
colors := sort_text(casting_cost, order: "<WUBRG>")
if colors == "" and contains(type, match:"Artifact") then "J" # Artifact
else if colors == "" then "A" # Clear Colorless
else if colors == "W" then "B" # White
else if colors == "U" then "C" # Blue
else if colors == "B" then "D" # Black
else if colors == "R" then "E" # Red
else if colors == "G" then "F" # Green
else if is_hybrid_cost() then hybrid_color_pair_sort() # Hybrid (by pairs)
else if contains(casting_cost, match:"/") and contains(type, match:"Artifact") then "I" # Hybrid Artifacts
else multi_color_pair_sort() # Multicolor (by pairs)
)
}
rarity_sort := {
if card.shape == "token" then "T"
else if card.shape == "rulestip" then "U"
else if set.sort_special_rarity == "with the rest" or card.rarity != "special" then " "
else "S"
}
set_filter := {
# TODO: what about rulestips?
if card.shape == "token" then
{ card.shape == "token" }
else if card.shape == "rulestip" then
{ card.shape == "rulestip" }
else if set.sort_special_rarity != "separate numbering" then
{ card.shape != "token" and card.shape != "rulestip" }
else if card.rarity == "special" then
{ card.shape != "token" and card.shape != "rulestip" and card.rarity == "special" }
else
{ card.shape != "token" and card.shape != "rulestip" and card.rarity != "special" }
}
card_number := {
position (
of: card
in: set
order_by: { rarity_sort() + sort_index() + sort_name(card.name) }
filter: set_filter()
) + 1
}
card_count := {
number_of_items(in: set, filter: set_filter())
}
# used by pack scripts
is_token_card := { card.shape == "token" or card.shape == "rulestip" }
is_shifted_card := { contains(card.shape, match:"shifted") }
############################################################## Utilities for keywords
# Replace spaces by a spacer
separate_words := remove_tags + trim + replace@(match:" ", replace: {spacer})
# replaces — correctly
add := "" # default is nothing
# If the 'input' parameter is a mana costs, then adds 'add'
for_mana_costs := format_cost := {
if input.separator_before == "—" and contains(input.param, match: " ") then (
if contains(input.param, match:",") then (
if match(match: "^[SCTQXYZIWUBRG0-9/|]+,", input.param) then
"{add}<param-cost>{combined_cost(input.param)}</param-cost>"
else "<param-cost>{combined_cost(input.param)}</param-cost>"
) else
"<param-cost>{alternative_cost(input.param)}</param-cost>"
) else
"{add}<param-mana>{input.param}</param-mana>"
}
# Convert first character to lower case
alternative_cost := replace@(match:"^[A-Z]", replace: { to_lower() })
#
combined_cost := replace@(match:", [A-Z]", replace: { to_lower() })+
replace@(match:",", replace:" and")+
replace@(match:"^[SCTQXYZIWUBRG0-9/|]", in_context: "(^|[[:space:]])<match>", replace: "<sym-auto>&</sym-auto>")+
replace@(match:"^[A-Z]", replace: { to_lower() })
long_dash := replace@(match:"-", replace:"—")
# Utilities for keywords
has_cc := { card.casting_cost != "" }
has_pt := { card.pt != "" }
contains_target := match@(match:"(?i)([^a-z]|^)target([^a-z]|$)")
is_targeted := { contains_target(card.rule_text) }
############################################################## The text box
# Filters for the text box
# context in which mana symbols are found
mana_context :=
"(?ix) # case insensitive, ignore whitespace
(^|[[:space:]\"(“']) # start of a word
( <match>: # G: something
| <match>, # G, tap: something
| <match>[ ]can[ ]be[ ]pay
| (pays?|additional|costs?|the # pay X. creatures cost 1 less. pay an additional G.
|adds?|pay(ed)?[ ](with|using)
)
([ ]either)? # pay either X or Y
([ ](<sym[^>]*>)?[SCTQXYZIWUBRG0-9/|]+(</sym[^>]*>)?,)* # pay X, Y or Z
([ ](<sym[^>]*>)?[SCTQXYZIWUBRG0-9/|]+(</sym[^>]*>)?[ ](and|or|and/or))* # pay X or Y
[ ]<match>
([,.)]|$ # (end of word)
|[ ][^ .,]*$ # still typing...
|[ ]( or | and | in | less | more | to ) # or next word is ...
)
)
| <param-mana><match></param-mana> # keyword argument that is declared as mana
| <param-cost>[ ]*<match></param-cost> # keyword argument that is declared as cost
| <param-cost><match>, # keyword argument that is declared as cost
";
# truncates the name of legends
legend_filter := replace@(match:"(, | of | the ).*", replace: "" )
# these are considered a correct 'word' for spellchecking in the text box:
additional_text_words := match@(match:
"(?ix)^(?: # match whole word
<atom-[^>]*>.*?</atom-[^>]*> # cardnames and stuff
| [+-]?[0-9X]+ / [+-]?[0-9X]+ # '3/3', '+X/+X'
)$")
# the rule text filter
# - adds mana symbols
# - makes text in parentheses italic
text_filter :=
# step 1 : remove all automatic tags
remove_tag@(tag: "<sym-auto>") +
remove_tag@(tag: "<i-auto>") +
remove_tag@(tag: "<b-auto>") +
remove_tag@(tag: "<error-spelling") +
remove_tag@(tag: "<nospellcheck") +
# step 2 : reminder text for keywords
expand_keywords@(
condition: {
correct_case or (mode != "pseudo" and not used_placeholders)
}
default_expand: {
chosen(choice:if correct_case then mode else "lower case", set.automatic_reminder_text)
},
combine: {
keyword := "<nospellcheck>{keyword}</nospellcheck>"
reminder := process_english_hints(reminder)
if mode == "pseudo" then "<i-auto>{keyword}</i-auto>"
else keyword + if expand then "<atom-reminder-{mode}> ({reminder})</atom-reminder-{mode}>"
}) +
# step 2b : move action keywords' reminder text to the end of the line
replace@(
match: "(<atom-reminder-action>(?:(?!<kw-).)*</atom-reminder-action></kw[^>]*>)(((?!<atom-reminder| ?<kw-)[^\n(])+)",
replace: "\\2\\1"
) +
# step 2c : remove duplicate reminder text
replace@(
match: "(<atom-reminder-[^>]*>[^)]+[)]</atom-reminder-[^>]*>)([^\n]+)\\1"
replace: "\\2\\1"
) +
# step 3a : expand shortcut word CARDNAME
replace@(
match: "CARDNAME",
in_context: "(^|[[:space:]]|\\()<match>", # TODO: Allow any punctuation before
replace: "<atom-cardname></atom-cardname>"
) +
# step 3b : expand shortcut word LEGENDNAME
replace@(
match: "LEGENDNAME",
in_context: "(^|[[:space:]]|\\()<match>", # TODO: Allow any punctuation before
replace: "<atom-legname></atom-legname>"
) +
# step 3c : fill in atom fields
tag_contents@(
tag: "<atom-cardname>",
contents: { "<nospellcheck>" + (if card_name=="" then "CARDNAME" else card_name) + "</nospellcheck>" }
) +
tag_contents@(
tag: "<atom-legname>",
contents: { "<nospellcheck>" + (if card_name=="" then "LEGENDNAME" else legend_filter(card_name)) + "</nospellcheck>" }
) +
# step 4 : explict non mana symbols
replace@(
match: "\\][SCTQXYZIWUBRG0-9/|]+\\[",
replace: {"<nosym>" + mana_filter_t() + "</nosym>"} ) +
# step 5 : add mana & tap symbols
replace@(
match: "\\b[SCTQXYZIWUBRG0-9/|]+\\b",
in_context: mana_context,
replace: {"<sym-auto>" + mana_filter_t() + "</sym-auto>"} ) +
# step 5b : add explict mana symbols
replace@(
match: "\\[[SCTQXYZIWUBRG0-9/|]+\\]",
replace: {"<sym>" + mana_filter_t() + "</sym>"} ) +
# step 6 : curly quotes
curly_quotes +
# step 7 : italicize text in parenthesis
replace@(
match: "[(]([^)\n]|[(][^)\n]*[)])*[)]?",
in_context: "(^|[[:space:]])<match>|<atom-keyword><match></",
replace: "<i-auto>&</i-auto>") +
# step 8 : automatic capitalization, but not after "("
replace@(
match: "([ ]*: |—| — )" # preceded by this
+ "([[:lower:]])" # match this
+ "(?![)])", # not followed by this
replace: { _1 + to_upper(_2) }) +
# step 9 : spellcheck
{ if set.mark_errors then
check_spelling(
language: language().spellcheck_code,
extra_dictionary: "/magic.mse-game/magic-words",
extra_match: additional_text_words
)
else input
}
############################################################## Other boxes
# the flavor text filter
# - makes all text italic
flavor_text_filter :=
# step 1 : remove italic tags
remove_tag@(tag: "<i-flavor>") +
# step 2 : surround by <i> tags
{ "<i-flavor>" + input + "</i-flavor>" } +
# curly quotes
curly_quotes +
# spellcheck
{ if set.mark_errors
then check_spelling(language:language().spellcheck_code)
else input
}
# Move the cursor past the separator in the p/t and type boxes
type_over_pt := replace@(match:"/$", replace:"")
type_over_type := replace@(match:" ?[-:]$", replace:"")
super_type_filter := {
input := remove_tag(tag: "<word-list-")
input := type_over_type()
tag := "word-list-type" # TODO: localize
"<{tag}>{input}</{tag}>"
}
break_subtypes := split_text@(match: " +|<atom-sep>[^<]*</atom-sep>", include_empty:false)
sub_type_filter := {
input := remove_tag(tag: "<word-list-")
input := remove_tag(tag: "<soft")
lang := language()
# What word list to use?
list_type_rest := if lang.is_creature(type) then "class"
else if lang.is_land(type) then "land"
else if lang.is_artifact(type) then "artifact"
else if lang.is_enchantment(type) then "enchantment"
else if lang.is_spell(type) then "spell"
else if lang.is_planeswalker(type) then "planeswalker"
if list_type_rest != "" then (
if lang.is_creature(type) or lang.is_tribal(type) then (
list_type_first := "race"
) else (
list_type_first := list_type_rest
);
# wrap wordlist tag around each part
parts := break_subtypes()
(for each i:part in parts do
if i == 0 then
"<word-list-{list_type_first}>{part}</word-list-{list_type_first}>"
else
lang.subtype_separator + "<word-list-{list_type_rest}>{part}</word-list-{list_type_rest}>"
) +
(if length(parts) > 0 then
# Add a new box at the end
"<soft>{lang.subtype_separator}</soft><word-list-{list_type_rest}></word-list-{list_type_rest}>"
else
"<word-list-{list_type_first}></word-list-{list_type_first}>"
)
) else input # do nothing
}
# all sub types, for word list
space_to_comma := replace@(match:" ", replace:",")
only_first := replace@(match:" .*", replace:"")
only_next := replace@(match:"^[^ ]* ?", replace:"")
all_sub_types := {
for each card in set do
if contains(card.super_type) then "," + space_to_comma(to_text(card.sub_type))
}
all_races := {
for each card in set do
if is_creature(card.super_type) or is_tribal(card.super_type) then
"," + only_first(to_text(card.sub_type))
}
all_classes := {
for each card in set do
if contains(card.super_type, match:"Creature") then
"," + space_to_comma(only_next(to_text(card.sub_type)))
}
# Shape of cards, can be changed in style files
card_shape := { "normal" }
typesymbol_for :=
to_text +
replace@(match: "(Legendary|Basic|Snow|World| )", replace: "") +
{ if input == "Creature" then "creature"
else if input == "Sorcery" then "sorcery"
else if input == "Instant" then "instant"
else if input == "Artifact" then "artifact"
else if input == "Enchantment" then "enchantment"
else if input == "Land" then "land"
else if input == "Planeswalker" then "planeswalker"
else "multitype"
}
typesymbol_type := { typesymbol_for(card.super_type) }
#Script to make magic-mana-future compatible w/ other templates
colorless_color := { "c" }
############################################################## Statistics utilities
# Converted mana cost
is_half_mana := match@(match: "1/2|[|][WUBRGS]")
is_colored_mana := match@(match: "[WUBRG]")
only_numbers := filter_text@(match: "^[0123456789]+")
cmc_split := break_text@(match: "(?ix) 1/2 | [|][WUBRG] | [0-9]+(?!/[WUBRGSCTQ2]) | [WUBRGS0-9](/[WUBRGS])\{0,4} ")
cmc := {to_number(
for each sym in cmc_split(to_text()) do (
numbers := only_numbers(sym)
if is_half_mana(sym) then 0.5
else if numbers != "" then to_int(numbers)
else 1 # all other symbols are 1
))
}
colored_mana := {to_number(
for each sym in cmc_split(to_text()) do (
numbers := only_numbers(sym)
if is_colored_mana(sym) then
if is_half_mana(sym) then 0.5 else 1
else 0
))
}
primary_card_color := {
artifact := chosen(choice:"artifact")
land := chosen(choice:"land")
multi := chosen(choice:"multicolor")
hybrid := chosen(choice:"hybrid")
if land then "land"
else if multi and input != "artifact, multicolor" then "multicolor"
else if hybrid then "hybrid"
else if artifact then "artifact"
else input
}
word_count := break_text@(match:"[^[:space:]]+") + length
line_count := split_text@(match:"\n+",include_empty:false) + length
#Remove supertypes or types to look at parts of the super_type field by themselves.
remove_supertype := replace@(match: "(Legendary|Basic|Snow|World|Tribal|Token)", replace: "")+
replace@(match: "[ ]+", in_context: "^<match>", replace: "")+
replace@(match: "[ ]+", in_context: "<match>$", replace: "")
remove_type := replace@(match: "(Artifact|Creature|Enchantment|Instant|Land|Planeswalker|Sorcery)", replace: "")+
replace@(match: "[ ]+", in_context: "^<match>", replace: "")+
replace@(match: "[ ]+", in_context: "<match>$", replace: "")