mtgencode/lib/cardlib.py
reimannsum fed25c77f4 Improvements to HTML spoiler
Changed how Creativity card examples are handled as to not flood
magiccards.info and cause it to blacklist computer. Also added forum
spoiler to each card. changed the link for similar cards to the
magiccards.info page.
2016-06-12 22:19:03 -04:00

1030 lines
40 KiB
Python

# card representation
import re
import random
import utils
import transforms
from manalib import Manacost, Manatext
# Some text prettification stuff that people may not have installed
try:
from titlecase import titlecase
except ImportError:
def titlecase(s):
return s.title()
try:
import textwrap
import nltk.data
sent_tokenizer = nltk.data.load('tokenizers/punkt/english.pickle')
# This could me made smarter - MSE will capitalize for us after :,
# but we still need to capitalize the first english component of an activation
# cost that starts with symbols, such as {2U}, *R*emove a +1/+1 counter from @: etc.
def cap(s):
return s[:1].capitalize() + s[1:]
# This crazy thing is actually invoked as an unpass, so newlines are still
# encoded.
def sentencecase(s):
s = s.replace(utils.x_marker, utils.reserved_marker)
lines = s.split(utils.newline)
clines = []
for line in lines:
if line:
sentences = sent_tokenizer.tokenize(line)
clines += [' '.join([cap(sent) for sent in sentences])]
return utils.newline.join(clines).replace(utils.reserved_marker, utils.x_marker)
except ImportError:
# non-nltk implementation provided by PAK90
def uppercaseNewLineAndFullstop(string):
# ok, let's capitalize every letter after a full stop and newline.
# first let's find all indices of '.' and '\n'
indices = [0] # initialise with 0, since we always want to capitalise the first letter.
newlineIndices = [0] # also need to keep track of pure newlines (for planeswalkers).
for i in range (len(string)):
if string[i] == '\\':
indices.append(i + 1) # we want the index of the letter after the \n, so add one.
newlineIndices.append(i + 1)
if string[i] == '.' or string[i] == "=": # also handle the choice bullets.
indices.append(i + 2) # we want the index of the letter after the ., so we need to count the space as well.
indexSet = set(indices) # convert it to a set for the next part; the capitalisation.
return "".join(c.upper() if i in indexSet else c for i, c in enumerate(string))
def sentencecase(s):
return uppercaseNewLineAndFullstop(s)
# These are used later to determine what the fields of the Card object are called.
# Define them here because they have nothing to do with the actual format.
field_name = 'name'
field_rarity = 'rarity'
field_cost = 'cost'
field_supertypes = 'supertypes'
field_types = 'types'
field_subtypes = 'subtypes'
field_loyalty = 'loyalty'
field_pt = 'pt'
field_text = 'text'
field_other = 'other' # it's kind of a pseudo-field
# Import the labels, because these do appear in the encoded text.
field_label_name = utils.field_label_name
field_label_rarity = utils.field_label_rarity
field_label_cost = utils.field_label_cost
field_label_supertypes = utils.field_label_supertypes
field_label_types = utils.field_label_types
field_label_subtypes = utils.field_label_subtypes
field_label_loyalty = utils.field_label_loyalty
field_label_pt = utils.field_label_pt
field_label_text = utils.field_label_text
fieldnames = [
field_name,
field_rarity,
field_cost,
field_supertypes,
field_types,
field_subtypes,
field_loyalty,
field_pt,
field_text,
]
# legacy
fmt_ordered_old = [
field_name,
field_supertypes,
field_types,
field_loyalty,
field_subtypes,
field_rarity,
field_pt,
field_cost,
field_text,
]
fmt_ordered_norarity = [
field_name,
field_supertypes,
field_types,
field_loyalty,
field_subtypes,
field_pt,
field_cost,
field_text,
]
# standard
fmt_ordered_default = [
field_types,
field_supertypes,
field_subtypes,
field_loyalty,
field_pt,
field_text,
field_cost,
field_rarity,
field_name,
]
# minor variations
fmt_ordered_noname = [
field_types,
field_supertypes,
field_subtypes,
field_loyalty,
field_pt,
field_text,
field_cost,
field_rarity,
]
fmt_ordered_named = [
field_name,
field_types,
field_supertypes,
field_subtypes,
field_loyalty,
field_pt,
field_text,
field_cost,
field_rarity,
]
fmt_labeled_default = {
field_name : field_label_name,
field_rarity : field_label_rarity,
field_cost : field_label_cost,
field_supertypes : field_label_supertypes,
field_types : field_label_types,
field_subtypes : field_label_subtypes,
field_loyalty : field_label_loyalty,
field_pt : field_label_pt,
field_text : field_label_text,
}
# sanity test if a card's fields look plausible
def fields_check_valid(fields):
# all cards must have a name and a type
if not field_name in fields:
return False
if not field_types in fields:
return False
# creatures have p/t, other things don't
iscreature = False
for idx, value in fields[field_types]:
if 'creature' in value:
iscreature = True
if iscreature:
return field_pt in fields
else:
return not field_pt in fields
# These functions take a bunch of source data in some format and turn
# it into nicely labeled fields that we know how to initialize a card from.
# Both return a dict that maps field names to lists of possible values,
# paired with the index that we read that particular field value from.
# So, {fieldname : [(idx, value), (idx, value)...].
# Usually we want these lists to be length 1, but you never know.
# Of course to make things nice and simple, that dict is the third element
# of a triple that reports parsing success and valid success as its
# first two elements.
# This whole things assumes the json format of mtgjson.com.
# Here's a brief list of relevant fields:
# name - string
# names - list (used for split, flip, and double-faced)
# manaCost - string
# cmc - number
# colors - list
# type - string (the whole big long damn thing)
# supertypes - list
# types - list
# subtypes - list
# text - string
# power - string
# toughness - string
# loyalty - number
# And some less useful ones, in case they're wanted for something:
# layout - string
# rarity - string
# flavor - string
# artist - string
# number - string
# multiverseid - number
# variations - list
# imageName - string
# watermark - string
# border - string
# timeshifted - boolean
# hand - number
# life - number
# reserved - boolean
# releaseDate - string
# starter - boolean
def fields_from_json(src_json, linetrans = True):
parsed = True
valid = True
fields = {}
# we hardcode in what the things are called in the mtgjson format
if 'name' in src_json:
name_val = src_json['name'].lower()
name_orig = name_val
name_val = transforms.name_pass_1_sanitize(name_val)
name_val = utils.to_ascii(name_val)
fields[field_name] = [(-1, name_val)]
else:
name_orig = ''
parsed = False
# return the actual Manacost object
if 'manaCost' in src_json:
cost = Manacost(src_json['manaCost'], fmt = 'json')
valid = valid and cost.valid
parsed = parsed and cost.parsed
fields[field_cost] = [(-1, cost)]
if 'supertypes' in src_json:
fields[field_supertypes] = [(-1, map(lambda s: utils.to_ascii(s.lower()),
src_json['supertypes']))]
if 'types' in src_json:
fields[field_types] = [(-1, map(lambda s: utils.to_ascii(s.lower()),
src_json['types']))]
else:
parsed = False
if 'subtypes' in src_json:
fields[field_subtypes] = [(-1, map(lambda s: utils.to_ascii(s.lower())
# urza's lands...
.replace('"', "'").replace('-', utils.dash_marker),
src_json['subtypes']))]
if 'rarity' in src_json:
if src_json['rarity'] in utils.json_rarity_map:
fields[field_rarity] = [(-1, utils.json_rarity_map[src_json['rarity']])]
else:
fields[field_rarity] = [(-1, src_json['rarity'])]
parsed = False
else:
parsed = False
if 'loyalty' in src_json:
fields[field_loyalty] = [(-1, utils.to_unary(str(src_json['loyalty'])))]
p_t = ''
parsed_pt = True
if 'power' in src_json:
p_t = utils.to_ascii(utils.to_unary(src_json['power'])) + '/' # hardcoded
parsed_pt = False
if 'toughness' in src_json:
p_t = p_t + utils.to_ascii(utils.to_unary(src_json['toughness']))
parsed_pt = True
elif 'toughness' in src_json:
p_t = '/' + utils.to_ascii(utils.to_unary(src_json['toughness'])) # hardcoded
parsed_pt = False
if p_t:
fields[field_pt] = [(-1, p_t)]
parsed = parsed and parsed_pt
# similarly, return the actual Manatext object
if 'text' in src_json:
text_val = src_json['text'].lower()
text_val = transforms.text_pass_1_strip_rt(text_val)
text_val = transforms.text_pass_2_cardname(text_val, name_orig)
text_val = transforms.text_pass_3_unary(text_val)
text_val = transforms.text_pass_4a_dashes(text_val)
text_val = transforms.text_pass_4b_x(text_val)
text_val = transforms.text_pass_5_counters(text_val)
text_val = transforms.text_pass_6_uncast(text_val)
text_val = transforms.text_pass_7_choice(text_val)
text_val = transforms.text_pass_8_equip(text_val)
text_val = transforms.text_pass_9_newlines(text_val)
text_val = transforms.text_pass_10_symbols(text_val)
if linetrans:
text_val = transforms.text_pass_11_linetrans(text_val)
text_val = utils.to_ascii(text_val)
text_val = text_val.strip()
mtext = Manatext(text_val, fmt = 'json')
valid = valid and mtext.valid
fields[field_text] = [(-1, mtext)]
# we don't need to worry about bsides because we handle that in the constructor
return parsed, valid and fields_check_valid(fields), fields
def fields_from_format(src_text, fmt_ordered, fmt_labeled, fieldsep):
parsed = True
valid = True
fields = {}
if fmt_labeled:
labels = {fmt_labeled[k] : k for k in fmt_labeled}
field_label_regex = '[' + ''.join(labels.keys()) + ']'
def addf(fields, fkey, fval):
# make sure you pass a pair
if fval and fval[1]:
if fkey in fields:
fields[fkey] += [fval]
else:
fields[fkey] = [fval]
textfields = src_text.split(fieldsep)
idx = 0
true_idx = 0
for textfield in textfields:
# ignore leading or trailing empty fields due to seps
if textfield == '':
if true_idx == 0 or true_idx == len(textfields) - 1:
true_idx += 1
continue
# count the field index for other empty fields but don't add them
else:
idx += 1
true_idx += 1
continue
lab = None
if fmt_labeled:
labs = re.findall(field_label_regex, textfield)
# use the first label if we saw any at all
if len(labs) > 0:
lab = labs[0]
textfield = textfield.replace(lab, '', 1)
# try to use the field label if we got one
if lab and lab in labels:
fname = labels[lab]
# fall back to the field order specified
elif idx < len(fmt_ordered):
fname = fmt_ordered[idx]
# we don't know what to do with this field: call it other
else:
fname = field_other
parsed = False
valid = False
# specialized handling
if fname in [field_cost]:
fval = Manacost(textfield)
parsed = parsed and fval.parsed
valid = valid and fval.valid
addf(fields, fname, (idx, fval))
elif fname in [field_text]:
fval = Manatext(textfield)
valid = valid and fval.valid
addf(fields, fname, (idx, fval))
elif fname in [field_supertypes, field_types, field_subtypes]:
addf(fields, fname, (idx, textfield.split()))
else:
addf(fields, fname, (idx, textfield))
idx += 1
true_idx += 1
# again, bsides are handled by the constructor
return parsed, valid and fields_check_valid(fields), fields
# Here's the actual Card class that other files should use.
class Card:
'''card representation with data'''
def __init__(self, src, fmt_ordered = fmt_ordered_default,
fmt_labeled = fmt_labeled_default,
fieldsep = utils.fieldsep, linetrans = True):
# source fields, exactly one will be set
self.json = None
self.raw = None
# flags
self.parsed = True
self.valid = True # doesn't record that much
# placeholders to fill in with expensive distance metrics
self.nearest_names = []
self.nearest_cards = []
# default values for all fields
self.__dict__[field_name] = ''
self.__dict__[field_rarity] = ''
self.__dict__[field_cost] = Manacost('')
self.__dict__[field_supertypes] = []
self.__dict__[field_types] = []
self.__dict__[field_subtypes] = []
self.__dict__[field_loyalty] = ''
self.__dict__[field_loyalty + '_value'] = None
self.__dict__[field_pt] = ''
self.__dict__[field_pt + '_p'] = None
self.__dict__[field_pt + '_p_value'] = None
self.__dict__[field_pt + '_t'] = None
self.__dict__[field_pt + '_t_value'] = None
self.__dict__[field_text] = Manatext('')
self.__dict__[field_text + '_lines'] = []
self.__dict__[field_text + '_words'] = []
self.__dict__[field_text + '_lines_words'] = []
self.__dict__[field_other] = []
self.bside = None
# format-independent view of processed input
self.fields = None # will be reset later
# looks like a json object
if isinstance(src, dict):
self.json = src
if utils.json_field_bside in src:
self.bside = Card(src[utils.json_field_bside],
fmt_ordered = fmt_ordered,
fmt_labeled = fmt_labeled,
fieldsep = fieldsep,
linetrans = linetrans)
p_success, v_success, parsed_fields = fields_from_json(src, linetrans = linetrans)
self.parsed = p_success
self.valid = v_success
self.fields = parsed_fields
# otherwise assume text encoding
else:
self.raw = src
sides = src.split(utils.bsidesep)
if len(sides) > 1:
self.bside = Card(utils.bsidesep.join(sides[1:]),
fmt_ordered = fmt_ordered,
fmt_labeled = fmt_labeled,
fieldsep = fieldsep,
linetrans = linetrans)
p_success, v_success, parsed_fields = fields_from_format(sides[0], fmt_ordered,
fmt_labeled, fieldsep)
self.parsed = p_success
self.valid = v_success
self.fields = parsed_fields
# amusingly enough, both encodings allow infinitely deep nesting of bsides...
# python name hackery
if self.fields:
for field in self.fields:
# look for a specialized set function
if hasattr(self, '_set_' + field):
getattr(self, '_set_' + field)(self.fields[field])
# otherwise use the default one
elif field in self.__dict__:
self.set_field_default(field, self.fields[field])
# If we don't recognize the field, fail. This is a totally artificial
# limitation; if we just used the default handler for the else case,
# we could set arbitrarily named fields.
else:
raise ValueError('python name mangling failure: unknown field for Card(): '
+ field)
else:
# valid but not parsed indicates that the card was apparently empty
self.parsed = False
# These setters are invoked via name mangling, so they have to match
# the field names specified above to be used. Otherwise we just
# always fall back to the (uninteresting) default handler.
# Also note that all fields come wrapped in pairs, with the first member
# specifying the index the field was found at when parsing the card. These will
# all be -1 if the card was parsed from (unordered) json.
def set_field_default(self, field, values):
first = True
for idx, value in values:
if first:
first = False
self.__dict__[field] = value
else:
# stick it in other so we'll be know about it when we format the card
self.valid = False
self.__dict__[field_other] += [(idx, '<' + field + '> ' + str(value))]
def _set_loyalty(self, values):
first = True
for idx, value in values:
if first:
first = False
self.__dict__[field_loyalty] = value
try:
self.__dict__[field_loyalty + '_value'] = int(value)
except ValueError:
self.__dict__[field_loyalty + '_value'] = None
# Technically '*' could still be valid, but it's unlikely...
else:
self.valid = False
self.__dict__[field_other] += [(idx, '<loyalty> ' + str(value))]
def _set_pt(self, values):
first = True
for idx, value in values:
if first:
first = False
self.__dict__[field_pt] = value
p_t = value.split('/') # hardcoded
if len(p_t) == 2:
self.__dict__[field_pt + '_p'] = p_t[0]
try:
self.__dict__[field_pt + '_p_value'] = int(p_t[0])
except ValueError:
self.__dict__[field_pt + '_p_value'] = None
self.__dict__[field_pt + '_t'] = p_t[1]
try:
self.__dict__[field_pt + '_t_value'] = int(p_t[1])
except ValueError:
self.__dict__[field_pt + '_t_value'] = None
else:
self.valid = False
else:
self.valid = False
self.__dict__[field_other] += [(idx, '<pt> ' + str(value))]
def _set_text(self, values):
first = True
for idx, value in values:
if first:
first = False
mtext = value
self.__dict__[field_text] = mtext
fulltext = mtext.encode()
if fulltext:
self.__dict__[field_text + '_lines'] = map(Manatext,
fulltext.split(utils.newline))
self.__dict__[field_text + '_words'] = re.sub(utils.unletters_regex,
' ',
fulltext).split()
self.__dict__[field_text + '_lines_words'] = map(
lambda line: re.sub(utils.unletters_regex, ' ', line).split(),
fulltext.split(utils.newline))
else:
self.valid = False
self.__dict__[field_other] += [(idx, '<text> ' + str(value))]
def _set_other(self, values):
# just record these, we could do somthing unset valid if we really wanted
for idx, value in values:
self.__dict__[field_other] += [(idx, value)]
# Output functions that produce various formats. encode() is specific to
# the NN representation, use str() or format() for output intended for human
# readers.
def encode(self, fmt_ordered = fmt_ordered_default, fmt_labeled = fmt_labeled_default,
fieldsep = utils.fieldsep, initial_sep = True, final_sep = True,
randomize_fields = False, randomize_mana = False, randomize_lines = False):
outfields = []
for field in fmt_ordered:
if field in self.__dict__:
outfield = self.__dict__[field]
if outfield:
# specialized field handling for the ones that aren't strings (sigh)
if isinstance(outfield, list):
outfield_str = ' '.join(outfield)
elif isinstance(outfield, Manacost):
outfield_str = outfield.encode(randomize = randomize_mana)
elif isinstance(outfield, Manatext):
outfield_str = outfield.encode(randomize = randomize_mana)
if randomize_lines:
outfield_str = transforms.randomize_lines(outfield_str)
else:
outfield_str = outfield
else:
outfield_str = ''
if fmt_labeled and field in fmt_labeled:
outfield_str = fmt_labeled[field] + outfield_str
outfields += [outfield_str]
else:
raise ValueError('unknown field for Card.encode(): ' + str(field))
if randomize_fields:
random.shuffle(outfields)
if initial_sep:
outfields = [''] + outfields
if final_sep:
outfields = outfields + ['']
outstr = fieldsep.join(outfields)
if self.bside:
outstr = (outstr + utils.bsidesep
+ self.bside.encode(fmt_ordered = fmt_ordered,
fmt_labeled = fmt_labeled,
fieldsep = fieldsep,
randomize_fields = randomize_fields,
randomize_mana = randomize_mana,
initial_sep = initial_sep, final_sep = final_sep))
return outstr
def format(self, gatherer = False, for_forum = False, vdump = False, for_html = False):
linebreak = '\n'
if for_html:
linebreak = '<hr>' + linebreak
outstr = ''
if for_html:
outstr += '<div class="card-text">\n'
if gatherer:
cardname = titlecase(transforms.name_unpass_1_dashes(self.__dict__[field_name]))
if vdump and not cardname:
cardname = '_NONAME_'
# in general, for_html overrides for_forum
if for_html:
outstr += '<b>'
elif for_forum:
outstr += '[b]'
outstr += cardname
if for_html:
outstr += '</b>'
elif for_forum:
outstr += '[/b]'
coststr = self.__dict__[field_cost].format(for_forum=for_forum, for_html=for_html)
if vdump or not coststr == '_NOCOST_':
outstr += ' ' + coststr
if self.__dict__[field_rarity]:
if self.__dict__[field_rarity] in utils.json_rarity_unmap:
rarity = utils.json_rarity_unmap[self.__dict__[field_rarity]]
else:
rarity = self.__dict__[field_rarity]
outstr += ' (' + rarity + ')'
if for_html and for_forum:
#force for_html to false to create tootip with forum spoiler
outstr += ('<div class="hover_img"><a href="#">[F]</a> <span><p>'
+ self.format(gatherer=gatherer, for_forum=for_forum, for_html=False, vdump=vdump).replace('\n', '<br>')
+ '</p></span></div>')
if vdump:
if not self.parsed:
outstr += ' _UNPARSED_'
if not self.valid:
outstr += ' _INVALID_'
outstr += linebreak
basetypes = map(str.capitalize, self.__dict__[field_types])
if vdump and len(basetypes) < 1:
basetypes = ['_NOTYPE_']
outstr += ' '.join(map(str.capitalize, self.__dict__[field_supertypes]) + basetypes)
if self.__dict__[field_subtypes]:
outstr += (' ' + utils.dash_marker + ' ' +
' '.join(self.__dict__[field_subtypes]).title())
if self.__dict__[field_pt]:
outstr += ' (' + utils.from_unary(self.__dict__[field_pt]) + ')'
if self.__dict__[field_loyalty]:
outstr += ' ((' + utils.from_unary(self.__dict__[field_loyalty]) + '))'
if self.__dict__[field_text].text:
outstr += linebreak
mtext = self.__dict__[field_text].text
mtext = transforms.text_unpass_1_choice(mtext, delimit = False)
mtext = transforms.text_unpass_2_counters(mtext)
#mtext = transforms.text_unpass_3_uncast(mtext)
mtext = transforms.text_unpass_4_unary(mtext)
mtext = transforms.text_unpass_5_symbols(mtext, for_forum, for_html)
mtext = sentencecase(mtext)
mtext = transforms.text_unpass_6_cardname(mtext, cardname)
mtext = transforms.text_unpass_7_newlines(mtext)
#mtext = transforms.text_unpass_8_unicode(mtext)
newtext = Manatext('')
newtext.text = mtext
newtext.costs = self.__dict__[field_text].costs
outstr += newtext.format(for_forum = for_forum, for_html = for_html)
if vdump and self.__dict__[field_other]:
outstr += linebreak
if for_html:
outstr += '<i>'
elif for_forum:
outstr += '[i]'
else:
outstr += utils.dash_marker * 2
first = True
for idx, value in self.__dict__[field_other]:
if for_html:
if not first:
outstr += '<br>\n'
else:
first = False
else:
outstr += linebreak
outstr += '(' + str(idx) + ') ' + str(value)
if for_html:
outstr += '</i>'
if for_forum:
outstr += '[/i]'
else:
cardname = self.__dict__[field_name]
#cardname = transforms.name_unpass_1_dashes(self.__dict__[field_name])
if vdump and not cardname:
cardname = '_NONAME_'
outstr += cardname
coststr = self.__dict__[field_cost].format(for_forum=for_forum, for_html=for_html)
if vdump or not coststr == '_NOCOST_':
outstr += ' ' + coststr
if vdump:
if not self.parsed:
outstr += ' _UNPARSED_'
if not self.valid:
outstr += ' _INVALID_'
if for_html and for_forum:
#force for_html to false to create tootip with forum spoiler
outstr += ('<div class="hover_img"><a href="#">[F] <span><p>'
+ self.format(gatherer=gatherer, for_forum=for_forum, for_html=False, vdump=vdump).replace('\n', '<br>')
+ '</p></span></a></div>')
outstr += linebreak
outstr += ' '.join(self.__dict__[field_supertypes] + self.__dict__[field_types])
if self.__dict__[field_subtypes]:
outstr += ' ' + utils.dash_marker + ' ' + ' '.join(self.__dict__[field_subtypes])
if self.__dict__[field_rarity]:
if self.__dict__[field_rarity] in utils.json_rarity_unmap:
rarity = utils.json_rarity_unmap[self.__dict__[field_rarity]]
else:
rarity = self.__dict__[field_rarity]
outstr += ' (' + rarity.lower() + ')'
if self.__dict__[field_text].text:
outstr += linebreak
mtext = self.__dict__[field_text].text
mtext = transforms.text_unpass_1_choice(mtext, delimit = True)
#mtext = transforms.text_unpass_2_counters(mtext)
#mtext = transforms.text_unpass_3_uncast(mtext)
mtext = transforms.text_unpass_4_unary(mtext)
mtext = transforms.text_unpass_5_symbols(mtext, for_forum, for_html)
#mtext = transforms.text_unpass_6_cardname(mtext, cardname)
mtext = transforms.text_unpass_7_newlines(mtext)
#mtext = transforms.text_unpass_8_unicode(mtext)
newtext = Manatext('')
newtext.text = mtext
newtext.costs = self.__dict__[field_text].costs
outstr += newtext.format(for_forum=for_forum, for_html=for_html)
if self.__dict__[field_pt]:
outstr += linebreak
outstr += '(' + utils.from_unary(self.__dict__[field_pt]) + ')'
if self.__dict__[field_loyalty]:
outstr += linebreak
outstr += '((' + utils.from_unary(self.__dict__[field_loyalty]) + '))'
if vdump and self.__dict__[field_other]:
outstr += linebreak
if for_html:
outstr += '<i>'
else:
outstr += utils.dash_marker * 2
first = True
for idx, value in self.__dict__[field_other]:
if for_html:
if not first:
outstr += '<br>\n'
else:
first = False
else:
outstr += linebreak
outstr += '(' + str(idx) + ') ' + str(value)
if for_html:
outstr += '</i>'
if self.bside:
if for_html:
outstr += '\n'
# force for_forum to false so that the inner div doesn't duplicate the forum
# spoiler of the bside
outstr += self.bside.format(gatherer=gatherer, for_forum=False, for_html=for_html, vdump=vdump)
else:
outstr += linebreak
outstr += utils.dash_marker * 8
outstr += linebreak
outstr += self.bside.format(gatherer=gatherer, for_forum=for_forum, for_html=for_html, vdump=vdump)
# if for_html:
# if for_forum:
# outstr += linebreak
# # force for_html to false to create a copyable forum spoiler div
# outstr += ('<div>'
# + self.format(gatherer=gatherer, for_forum=for_forum, for_html=False, vdump=vdump).replace('\n', '<br>')
# + '</div>')
if for_html:
outstr += "</div>"
return outstr
def to_mse(self, print_raw = False, vdump = False):
outstr = ''
# need a 'card' string first
outstr += 'card:\n'
cardname = titlecase(transforms.name_unpass_1_dashes(self.__dict__[field_name]))
outstr += '\tname: ' + cardname + '\n'
if self.__dict__[field_rarity]:
if self.__dict__[field_rarity] in utils.json_rarity_unmap:
rarity = utils.json_rarity_unmap[self.__dict__[field_rarity]]
else:
rarity = self.__dict__[field_rarity]
outstr += '\trarity: ' + rarity.lower() + '\n'
if not self.__dict__[field_cost].none:
outstr += ('\tcasting cost: '
+ self.__dict__[field_cost].format().replace('{','').replace('}','')
+ '\n')
outstr += '\tsuper type: ' + ' '.join(self.__dict__[field_supertypes]
+ self.__dict__[field_types]).title() + '\n'
if self.__dict__[field_subtypes]:
outstr += '\tsub type: ' + ' '.join(self.__dict__[field_subtypes]).title() + '\n'
if self.__dict__[field_pt]:
ptstring = utils.from_unary(self.__dict__[field_pt]).split('/')
if (len(ptstring) > 1): # really don't want to be accessing anything nonexistent.
outstr += '\tpower: ' + ptstring[0] + '\n'
outstr += '\ttoughness: ' + ptstring[1] + '\n'
if self.__dict__[field_text].text:
mtext = self.__dict__[field_text].text
mtext = transforms.text_unpass_1_choice(mtext, delimit = False)
mtext = transforms.text_unpass_2_counters(mtext)
mtext = transforms.text_unpass_3_uncast(mtext)
mtext = transforms.text_unpass_4_unary(mtext)
mtext = transforms.text_unpass_5_symbols(mtext, False, False)
mtext = sentencecase(mtext)
# I don't really want these MSE specific passes in transforms,
# but they could be pulled out separately somewhere else in here.
mtext = mtext.replace(utils.this_marker, '<atom-cardname><nospellcheck>'
+ utils.this_marker + '</nospellcheck></atom-cardname>')
mtext = transforms.text_unpass_6_cardname(mtext, cardname)
mtext = transforms.text_unpass_7_newlines(mtext)
mtext = transforms.text_unpass_8_unicode(mtext)
newtext = Manatext('')
newtext.text = mtext
newtext.costs = self.__dict__[field_text].costs
newtext = newtext.format()
# See, the thing is, I think it's simplest and easiest to just leave it like this.
# What could possibly go wrong?
newtext = newtext.replace('{','<sym-auto>').replace('}','</sym-auto>')
else:
newtext = ''
# Annoying special case for bsides;
# This could be improved by having an intermediate function that returned
# all of the formatted fields in a data structure and a separate wrapper
# that actually packed them into the MSE format.
if self.bside:
newtext = newtext.replace('\n','\n\t\t')
outstr += '\trule text:\n\t\t' + newtext + '\n'
outstr += '\tstylesheet: new-split\n'
cardname2 = titlecase(transforms.name_unpass_1_dashes(
self.bside.__dict__[field_name]))
outstr += '\tname 2: ' + cardname2 + '\n'
if self.bside.__dict__[field_rarity]:
if self.bside.__dict__[field_rarity] in utils.json_rarity_unmap:
rarity2 = utils.json_rarity_unmap[self.bside.__dict__[field_rarity]]
else:
rarity2 = self.bside.__dict__[field_rarity]
outstr += '\trarity 2: ' + rarity2.lower() + '\n'
if not self.bside.__dict__[field_cost].none:
outstr += ('\tcasting cost 2: '
+ self.bside.__dict__[field_cost].format()
.replace('{','').replace('}','')
+ '\n')
outstr += ('\tsuper type 2: '
+ ' '.join(self.bside.__dict__[field_supertypes]
+ self.bside.__dict__[field_types]).title() + '\n')
if self.bside.__dict__[field_subtypes]:
outstr += ('\tsub type 2: '
+ ' '.join(self.bside.__dict__[field_subtypes]).title() + '\n')
if self.bside.__dict__[field_pt]:
ptstring2 = utils.from_unary(self.bside.__dict__[field_pt]).split('/')
if (len(ptstring2) > 1): # really don't want to be accessing anything nonexistent.
outstr += '\tpower 2: ' + ptstring2[0] + '\n'
outstr += '\ttoughness 2: ' + ptstring2[1] + '\n'
if self.bside.__dict__[field_text].text:
mtext2 = self.bside.__dict__[field_text].text
mtext2 = transforms.text_unpass_1_choice(mtext2, delimit = False)
mtext2 = transforms.text_unpass_2_counters(mtext2)
mtext2 = transforms.text_unpass_3_uncast(mtext2)
mtext2 = transforms.text_unpass_4_unary(mtext2)
mtext2 = transforms.text_unpass_5_symbols(mtext2, False, False)
mtext2 = sentencecase(mtext2)
mtext2 = mtext2.replace(utils.this_marker, '<atom-cardname><nospellcheck>'
+ utils.this_marker + '</nospellcheck></atom-cardname>')
mtext2 = transforms.text_unpass_6_cardname(mtext2, cardname2)
mtext2 = transforms.text_unpass_7_newlines(mtext2)
mtext2 = transforms.text_unpass_8_unicode(mtext2)
newtext2 = Manatext('')
newtext2.text = mtext2
newtext2.costs = self.bside.__dict__[field_text].costs
newtext2 = newtext2.format()
newtext2 = newtext2.replace('{','<sym-auto>').replace('}','</sym-auto>')
newtext2 = newtext2.replace('\n','\n\t\t')
outstr += '\trule text 2:\n\t\t' + newtext2 + '\n'
# Need to do Special Things if it's a planeswalker.
# This code mostly works, but it won't get quite the right thing if the planeswalker
# abilities don't come before any other ones. Should be fixed.
elif "planeswalker" in str(self.__dict__[field_types]):
outstr += '\tstylesheet: m15-planeswalker\n'
# set up the loyalty cost fields using regex to find how many there are.
i = 0
lcost_regex = r'[-+]?\d+: ' # 1+ figures, might be 0.
for lcost in re.findall(lcost_regex, newtext):
i += 1
outstr += '\tloyalty cost ' + str(i) + ': ' + lcost + '\n'
# sub out the loyalty costs.
newtext = re.sub(lcost_regex, '', newtext)
# We need to uppercase again, because MSE won't magically capitalize for us
# like it does after semicolons.
# Abusing passes like this is terrible, should really fix sentencecase.
newtext = transforms.text_pass_9_newlines(newtext)
newtext = sentencecase(newtext)
newtext = transforms.text_unpass_7_newlines(newtext)
if self.__dict__[field_loyalty]:
outstr += '\tloyalty: ' + utils.from_unary(self.__dict__[field_loyalty]) + '\n'
newtext = newtext.replace('\n','\n\t\t')
outstr += '\trule text:\n\t\t' + newtext + '\n'
else:
newtext = newtext.replace('\n','\n\t\t')
outstr += '\trule text:\n\t\t' + newtext + '\n'
# now append all the other useless fields that the setfile expects.
outstr += '\thas styling: false\n\ttime created:2015-07-20 22:53:07\n\ttime modified:2015-07-20 22:53:08\n\textra data:\n\timage:\n\tcard code text:\n\tcopyright:\n\timage 2:\n\tcopyright 2:\n\tnotes:'
return outstr
def vectorize(self):
ld = '('
rd = ')'
outstr = ''
if self.__dict__[field_rarity]:
outstr += ld + self.__dict__[field_rarity] + rd + ' '
coststr = self.__dict__[field_cost].vectorize(delimit = True)
if coststr:
outstr += coststr + ' '
typestr = ' '.join(map(lambda s: '(' + s + ')',
self.__dict__[field_supertypes] + self.__dict__[field_types]))
if typestr:
outstr += typestr + ' '
if self.__dict__[field_subtypes]:
outstr += ' '.join(self.__dict__[field_subtypes]) + ' '
if self.__dict__[field_pt]:
outstr += ' '.join(map(lambda s: '(' + s + ')',
self.__dict__[field_pt].replace('/', '/ /').split()))
outstr += ' '
if self.__dict__[field_loyalty]:
outstr += '((' + self.__dict__[field_loyalty] + ')) '
outstr += self.__dict__[field_text].vectorize()
if self.bside:
outstr = '_ASIDE_ ' + outstr + '\n\n_BSIDE_ ' + self.bside.vectorize()
return outstr