Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/python
- from __future__ import with_statement
- import string, re
- __all__ = ["create_card_code", "add_oracle_info"]
- def add_oracle_info(card, text):
- info = parse_card_oracle(text)
- supertypes = info['supertypes']
- if isinstance(supertypes, list):
- card.supertypes = ' '.join(supertypes)
- types = info["types"]
- if isinstance(types, list): types = ' '.join(types)
- card.types = types
- subtypes = info['subtypes']
- if subtypes:
- card.subtypes = ' '.join(subtypes)
- if 'power' in info:
- card.power = info["power"]
- card.toughness = info["toughness"]
- elif "loyalty" in info:
- card.loyalty = info["loyalty"]
- cost = info['cost']
- if cost:
- card.cost = info['cost']
- card.textbox = "\n".join(info['text'])
- def create_card_code(text):
- return carddict_to_code(parse_card_oracle(text))
- # Convert card oracle text into a dictionary containing card fields
- def parse_card_oracle(oracle_text):
- if "|" in oracle_text: oracle_text = oracle_text.split("|")
- else: oracle_text = oracle_text.split("\n")
- lines_iter = iter(oracle_text)
- supertypes = ""
- subtypes = ""
- name = lines_iter.next()
- cost = lines_iter.next()
- if set(cost).difference(set("1234567890XYZWUBRGPxyzwubrgp(){}/ ")):
- # This card doesn't have a cost
- types = cost
- cost = ""
- else:
- cost = cost.replace(" ", '').upper().replace("P}", "/P}").replace("//P}", "/P}")
- types = lines_iter.next()
- if " -- " in types: types = types.split(" -- ")
- else: types = types.split(" - ")
- if len(types) == 2: subtypes = set(types[1].split())
- types = types[0].split()
- supertypes = list(set(types).intersection(set(("Basic", "Snow", "Legendary", "World"))))
- types = list(set(types).difference(set(("Basic", "Snow", "Legendary", "World"))))
- if "Creature" in types:
- pt = lines_iter.next().split("/")
- if "Planeswalker" in types:
- loyalty = lines_iter.next()
- if loyalty.endswith((" loyalty", " loyalty.")):
- loyalty = loyalty.split(" ")[0]
- text = []
- for l in lines_iter:
- text.append(l)
- text = parse_text(text)
- for i, line in enumerate(text):
- text[i] = line.replace(name, "~").replace("named ~", "named %s"%name).replace("{untap}", "{Q}")
- card = {"name": name, "cost": cost, "supertypes": supertypes, "types": types, "subtypes": subtypes, "text": text}
- if "Creature" in types:
- try:
- card["power"], card["toughness"] = map(conv_pt, pt)
- except:
- print repr(card)
- raise
- if "Planeswalker" in types:
- try:
- card["loyalty"] = int(loyalty)
- except:
- print repr(card)
- raise
- return card
- def parse_text(text):
- new_text = []
- for txt in text:
- pos = -1 #txt.find("Choose one -")
- if pos != -1:
- choices = txt.split(" - ")[1].split(";")
- temp = []
- for c in choices:
- newtemp = c.split(" or")
- if len(newtemp) == 1: newtemp = newtemp[0]
- else: newtemp = newtemp[1].strip()
- temp.append(newtemp)
- new_text.extend(temp)
- else:
- new_text.append(txt)
- # Now get rid of parentheses
- removal = []
- for i,t in enumerate(new_text):
- m = re.match('(.*) (\(.*\))',t)
- if m: new_text[i] = m.groups()[0]
- elif t.startswith("("):
- removal.append(i - len(removal))
- for idx in removal:
- del new_text[idx]
- return new_text
- def conv_pt(pt):
- if set(pt).difference(set("1234567890-+*")): pt = 0
- else:
- try: pt = eval(pt.replace("*","0"))
- except Exception, err:
- print "%s (%s: %s)"%(pt, err.__class__.__name__, err)
- pt = 0
- return pt
- # Convert dictionary containing card definition into sample Incantus template
- def carddict_to_code(card):
- attributes = ["name", "cost", "supertypes", "types", "subtypes", "power", "toughness", "loyalty", "text"]
- characteristics = set(["types", "supertypes", "subtypes"])
- name = card["name"]
- lines = []
- #if card["text"][0] in ("Land", "Common", "Uncommon", "Rare", "Mythic Rare"): card["text"] = card["text"][1:] # Stupid hack for something going screwy on magiccards.info
- for k in attributes:
- val = card.get(k, None)
- if val:
- if k in characteristics:
- if type(val) == list or type(val) == set: val = ", ".join(val)
- lines.append("%s = %s"%(k, val))
- else:
- lines.append("%s = %s"%(k, repr(val)))
- lines.append('')
- # Now the rest of the file
- cardtypes = card["types"]
- if "Instant" in cardtypes:
- lines.append(nonperm%"instant")
- elif "Sorcery" in cardtypes:
- lines.append(nonperm%"sorcery")
- # Otherwise we have permanents
- else:
- #skip = []
- #if "Enchantment" in cardtypes and "Aura" in card["subtypes"]:
- # lines.append("abilities.add(enchant(# XXX target type))\n")
- # skip = [i for i, line in enumerate(card["text"]) if line.startswith("Enchant ")]
- lines.append("#################################")
- lines.append("")
- # Parse the text box here
- for i, txt in enumerate(card["text"]):
- #if i in skip: continue # Silly hack until we can treat enchant like a real keyword.
- # Check for keywords
- global it
- it = None
- abilities = parse_abilities(txt, card, i)
- #if txt.startswith("When") or txt.startswith("At") or "- When" in txt or "- At" in txt: # Chroma - When ~ comes into play...
- # lines.append(triggered%i)
- #elif "mana pool" in txt:
- # lines.append(mana%(i, parse_cost(txt.split(":")[0], name)))
- #elif ":" in txt:
- # lines.append(activated%(i, parse_cost(txt.split(":")[0], name)))
- #elif "enters the battlefield" in txt:
- # lines.append(comes_into_play%i)
- #elif " get " in txt or " have " in txt:
- # lines.append(static_tracking%i)
- #elif "Enchanted" in txt or "Equipped" in txt:
- # lines.append(attached%i)
- #elif keywords: # To prevent "equipped" from matching "equip"
- # lines.extend(keywords)
- lines.extend(abilities)
- #else:
- # #lines.append("# Another ability here - can't determine the type\n")
- # # If it doesn't match any of the above, it must be a static ability (by definition).
- # lines.append(static%i)
- return '\n'.join(lines)
- common_abilities = dict([("~ enters the battlefield tapped.", "abilities.add(enters_battlefield_tapped())\n"),
- ("~ is unblockable.", "abilities.add(this_card_is_unblockable())\n"),
- ("~ is indestructible.", "abilities.add(this_card_is_indestructible())\n"),
- ("~ attacks each turn if able.", "abilities.add(this_card_must_attack())\n"),
- ("~ can't block.", "abilities.add(this_card_cant_block())\n"),
- ("~ doesn't untap during your untap step.", "abilities.add(this_card_doesnt_untap())\n"),
- ])
- def parse_abilities(txt, card, i):
- if not txt: return []
- if txt in common_abilities:
- return [common_abilities[txt]]
- else:
- if re.match(r"^(\w+) -{1,2} (.+)", txt): # Strip out ability words.
- txt = re.match(r"^(\w+) -{1,2} (.+)", txt).group(2)
- if txt.startswith(("When", "At")):
- return [parse_triggered(txt, card, i)]
- else: # I tried this as a regexp, but it took too long to parse non-activated abilities.
- inquote = False
- for char in txt:
- if char == "\"":
- inquote = not inquote
- elif char == ":" and not inquote:
- break
- else:
- return [parse_static(txt, card, i)]
- return [parse_activated(txt, card, i)]
- def parse_triggered(txt, card, i):
- condition = None
- trigger = None
- effect = None
- end_trigger = 0
- global it
- if txt.split(" ")[0] in ("When", "Whenever"):
- # logic for "action" triggers ("whenever <object> deals combat damage", "when <object> is put into a graveyard from the battlefield", "whenever a player loses the game")
- if txt.startswith("When ~ enters the battlefield, "):
- end_trigger = 31
- trigger = 'EnterTrigger("battlefield", source_match)'
- it = "~"
- elif txt.startswith("When ~ leaves the battlefield, "):
- end_trigger = 31
- trigger = 'LeaveTrigger("battlefield", source_match)'
- it = "~"
- elif re.match(r"^When(ever)? ((equipped|enchanted|fortified) (permanent|creature|artifact|enchantment|land|planeswalker)) leaves the battlefield, ", txt):
- match = re.match(r"^When(ever)? ((equipped|enchanted|fortified) (permanent|creature|artifact|enchantment|land|planeswalker)) leaves the battlefield, ", txt)
- end_trigger = len(match.group(0))
- trigger = 'LeaveTrigger("battlefield", attached_match)'
- it = match.group(2)
- elif txt.startswith(("When ~ is put into a graveyard from the battlefield, ", "When ~ dies, ")):
- end_trigger = 13 if txt.split(" ")[2] == "dies," else 53
- trigger = 'EnterFromTrigger("graveyard", "battlefield", source_match)'
- it = "~"
- elif re.match(r"^When(ever)? (equipped|enchanted) creature dies, ", txt):
- match = re.match(r"^When(ever)? ((equipped|enchanted) creature) dies, ", txt)
- end_trigger = len(match.group(0))
- trigger = 'EnterFromTrigger("graveyard", "battlefield", attached_match, player="any")'
- it = match.group(2)
- elif txt.startswith(("Whenever another creature enters the battlefield, ", "When another creature enters the battlefield, ")):
- end_trigger = 50 if txt.startswith("Whenever") else 46
- trigger = 'EnterTrigger("battlefield", condition, player="any")'
- condition = 'def condition(source, card): return not card == source and isCreature(card)'
- elif txt.startswith(("Whenever a creature dealt damage by ~ this turn dies, ", "When a creature dealt damage by ~ this turn dies, ")):
- end_trigger = 54 if txt.startswith("Whenever") else 50
- trigger = 'EnterFromTrigger("graveyard", "battlefield", condition, player="any")'
- condition = 'def condition(source, card): return isCreature(card) and damage_tracker.dealt(source, card)'
- elif re.match(r"^When(ever)? a creature dealt damage by ((equipped|enchanted|fortified) (permanent|artifact|creature|enchantment|land|planeswalker)) this turn dies, ", txt):
- match = re.match(r"^When(ever)? a creature dealt damage by ((equipped|enchanted|fortified) (permanent|artifact|creature|enchantment|land|planeswalker)) this turn dies, ", txt)
- end_trigger = len(match.group(0))
- trigger = 'EnterFromTrigger("graveyard", "battlefield", condition, player="any")'
- condition = 'def condition(source, card): return source.attached_to and isCreature(card) and damage_tracker.dealt(source.attached_to, card)'
- elif txt.startswith(("Whenever ~ deals damage, ", "When ~ deals damage, ")):
- end_trigger = 25 if txt.startswith("Whenever") else 21
- trigger = 'DealsDamageTrigger(sender_match)'
- it = "~"
- elif re.match(r"^When(ever)? ((enchanted|equipped|fortified) (permanent|artifact|creature|enchantment|land|planeswalker)) deals damage, ", txt):
- match = re.match(r"^When(ever)? ((enchanted|equipped|fortified) (permanent|artifact|creature|enchantment|land|planeswalker)) deals damage, ", txt)
- end_trigger = len(match.group(0))
- trigger = 'DealsDamageTrigger(condition)'
- condition = 'def condition(source, sender): return sender == source.attached_to'
- it = match.group(2)
- elif txt.startswith(("Whenever ~ is dealt damage, ", "When ~ is dealt damage, ")):
- end_trigger = 28 if txt.startswith("Whenever") else 24
- trigger = 'ReceivesDamageTrigger(sender_match)'
- it = "~"
- elif txt.startswith(("Whenever ~ is dealt combat damage, ", "When ~ is dealt combat damage, ")):
- end_trigger = 35 if txt.startswith("Whenever") else 31
- trigger = 'ReceivesDamageTrigger(condition)'
- condition = 'def condition(source, sender, combat): sender == source and combat'
- it = "~"
- elif re.match(r"^When(ever)? ((enchanted|equipped) (creature|planeswalker)) is dealt( combat)? damage, ", txt):
- match = re.match(r"^When(ever)? ((enchanted|equipped) (creature|planeswalker)) is dealt( combat)? damage, ", txt)
- end_trigger = len(match.group(0))
- trigger = 'ReceivesDamageTrigger(condition)'
- tmp = ("", "")
- if match.group(5):
- tmp = (", combat", " and combat")
- condition = 'def condition(source, sender%s): sender == source.attached_to%s'%tmp
- it = match.group(2)
- elif txt.startswith(("Whenever ~ deals damage to a player, ", "When ~ deals damage to a player, ")):
- end_trigger = 37 if txt.startswith("Whenever") else 33
- condition = 'def condition(source, sender, to): return sender == source and isPlayer(to)'
- trigger = 'DealsDamageToTrigger(condition)'
- it = "~"
- elif re.match(r"^When(ever)? ((enchanted|equipped|fortified) (permanent|artifact|creature|enchantment|land|planeswalker)) deals damage to a player, ", txt):
- match = re.match(r"^When(ever)? ((enchanted|equipped|fortified) (permanent|artifact|creature|enchantment|land|planeswalker)) deals damage to a player, ", txt)
- end_trigger = len(match.group(0))
- trigger = 'DealsDamageToTrigger(condition)'
- condition = 'def condition(source, sender, to): return sender == source.attached_to and isPlayer(to)'
- it = match.group(2)
- elif txt.startswith(("Whenever ~ deals combat damage to a player, ", "When ~ deals combat damage to a player, ")):
- end_trigger = 44 if txt.startswith("Whenever") else 40
- condition = 'def condition(source, sender, to, combat): return sender == source and isPlayer(to) and combat'
- trigger = 'DealsDamageToTrigger(condition)'
- it = "~"
- elif txt.startswith(("Whenever ~ deals combat damage, ", "When ~ deals combat damage, ")):
- end_trigger = 32 if txt.startswith("Whenever") else 28
- condition = 'def condition(source, sender, to, combat): return sender == source and combat'
- trigger = 'DealsDamageToTrigger(condition)'
- it = "~"
- elif re.match(r"^When(ever)? ((enchanted|equipped) creature) deals combat damage( to a player)?, ", txt):
- match = re.match(r"^When(ever)? ((enchanted|equipped) creature) deals combat damage( to a player)?, ", txt)
- end_trigger = len(match.group(0))
- trigger = 'DealsDamageToTrigger(condition)'
- condition = 'def condition(source, sender, to, combat): return sender == source.attached_to%s and combat'%(" and isPlayer(to)" if match.group(4) else "")
- it = match.group(2)
- elif txt.startswith(("Whenever ~ attacks, ", "When ~ attacks, ")):
- end_trigger = 20 if txt.startswith("Whenever") else 16
- trigger = 'Trigger(AttackerDeclaredEvent(), sender_match)'
- it = "~"
- elif txt.startswith(("Whenever ~ becomes blocked, ", "When ~ becomes blocked, ")):
- end_trigger = 28 if txt.startswith("Whenever") else 24
- trigger = 'Trigger(AttackerBlockedEvent(), sender_match)'
- it = "~"
- elif txt.startswith(("Whenever ~ becomes blocked by a creature, ", "When ~ becomes blocked by a creature, ")):
- end_trigger = 42 if txt.startswith("Whenever") else 38
- condition = 'def condition(source, attacker): return attacker == source'
- trigger = 'Trigger(BlockerDeclaredEvent(), condition)'
- elif re.match(r"^When(ever)? ((enchanted|equipped) creature) (attacks|becomes blocked|becomes blocked by a creature), ", txt):
- match = re.match(r"^When(ever)? ((enchanted|equipped) creature) (attacks|becomes blocked|becomes blocked by a creature), ", txt)
- end_trigger = len(match.group(0))
- if match.group(4) == "attacks": trigger = "Trigger(AttackerDeclaredEvent(), condition)"
- elif match.group(4) == "becomes blocked": trigger = "Trigger(AttackerBlockedEvent(), condition)"
- else: trigger = "Trigger(BlockerDeclaredEvent(), condition)"
- if match.group(4) == "becomes blocked by a creature":
- condition = "def condition(source, attacker): return attacker == source.attached_to"
- else:
- condition = "def condition(source, sender): sender == source.attached_to"
- it = match.group(2)
- elif txt.startswith(("Whenever ~ attacks and isn't blocked, ", "When ~ attacks and isn't blocked, ")):
- end_trigger = 38 if txt.startswith("Whenever") else 34
- condition = 'def condition(source, combat_assignment): return source.attacking and not combat_assignment[source]'
- trigger = 'Trigger(DeclareBlockersEvent(), condition)'
- it = "~"
- elif re.match(r"^When(ever)? ((enchanted|equipped) creature) attacks and isn't blocked, ", txt):
- match = re.match(r"^When(ever)? ((enchanted|equipped) creature) attacks and isn't blocked, ", txt)
- end_trigger = len(match.group(0))
- condition = 'def condition(source, combat_assignment): return source.attached_to and source.attached_to.attacking and not combat_assignment[source.attached_to]'
- trigger = 'Trigger(DeclareBlockersEvent(), condition)'
- it = match.group(2)
- elif txt.startswith(("Whenever ~ blocks, ", "When ~ blocks, ")):
- end_trigger = 19 if txt.startswith("Whenever") else 15
- trigger = 'Trigger(BlockerDeclaredEvent(), sender_match)'
- it = "~"
- elif txt.startswith(("Whenever ~ blocks a creature, ", "When ~ blocks a creature, ")):
- end_trigger = 30 if txt.startswith("Whenever") else 26
- trigger = 'Trigger(BlockerDeclaredEvent(), sender_match)'
- it = "~"
- elif re.match(r"^When(ever)? ((enchanted|equipped) creature) blocks( a creature)?, ", txt):
- match = re.match(r"^When(ever)? ((enchanted|equipped) creature) blocks( a creature)?, ", txt)
- end_trigger = len(match.group(0))
- condition = 'def condition(source, sender): return sender == source.attached_to'
- trigger = 'Trigger(BlockerDeclaredEvent(), condition)'
- it = match.group(2)
- elif txt.startswith(("Whenever ~ becomes the target of a spell or ability, ", "When ~ becomes the target of a spell or ability, ")):
- end_trigger = 53 if txt.startswith("Whenever") else 49
- trigger = 'Trigger(TargetedByEvent(), sender_match)'
- it = "~"
- elif re.match(r"^When(ever)? ((enchanted|equipped|fortified) (permanent|artifact|creature|enchantment|land|planeswalker)) becomes the target of a spell or ability, ", txt):
- match = re.match(r"^When(ever)? ((enchanted|equipped|fortified) (permanent|artifact|creature|enchantment|land|planeswalker)) becomes the target of a spell or ability, ", txt)
- end_trigger = len(match.group(0))
- trigger = 'Trigger(TargetedByEvent(), condition)'
- condition = 'def condition(source, sender): return sender == source.attached_to'
- it = match.group(2)
- elif txt.startswith(("Whenever you discard a card, ", "When you discard a card, ")):
- end_trigger = 29 if txt.startswith("Whenever") else 25
- condition = 'def condition(source, sender): return sender == source.controller'
- trigger = 'Trigger(DiscardCardEvent(), condition)'
- elif txt.startswith(("Whenever an opponent discards a card, ", "When an opponent discards a card, ")):
- end_trigger = 38 if txt.startswith("Whenever") else 34
- condition = 'def condition(source, sender): return sender in source.controller.opponents'
- trigger = 'Trigger(DiscardCardEvent(), condition)'
- elif txt.startswith(("Whenever a player discards a card, ", "When you discard a card, ")):
- end_trigger = 35 if txt.startswith("Whenever") else 31
- trigger = 'Trigger(DiscardCardEvent())'
- elif re.match(r"When(ever)? (you|a player) casts? an? (white|blue|black|red|green|artifact|creature|enchantment|planeswalker|tribal|permanent|instant|sorcery|instant or sorcery|colorless|monocolored|multicolored) spell, ", txt):
- match = re.match(r"When(ever)? (you|a player) casts? an? (white|blue|black|red|green|artifact|creature|enchantment|planeswalker|tribal|permanent|instant|sorcery|instant or sorcery|colorless|monocolored|multicolored) spell, ", txt)
- end_trigger = len(match.group(0))
- who, type = match.group(2, 3)
- trigger = "Trigger(SpellPlayedEvent(), condition)"
- condition = "def condition(source, sender, spell): return "
- if who == "you":
- condition += "sender == source.controller and "
- if type in ("white", "blue", "black", "red", "green"):
- condition += "spell.color == %s"%(type.capitalize())
- elif type in ("artifact", "creature", "enchantment", "planeswalker", "tribal", "instant", "sorcery"):
- condition += "spell.types == %s"%(type.capitalize())
- elif type == "instant or sorcery":
- condition += "spell.types == Instant or spell.types == Sorcery"
- elif type == "permanent":
- condition += "spell.types.intersects(set([Artifact, Enchantment, Creature, Land, Planeswalker]))"
- elif type == "colorless":
- condition += "len(spell.color) == 0"
- elif type == "monocolored":
- condition += "len(spell.color) == 1"
- elif type == "multicolored":
- condition += "len(spell.color) > 1"
- elif txt.split(" ")[0] == "At":
- # logic for "time" triggers ("at the beginning of your upkeep", "at the beginning of the end step", "at the beginning of your precombat main phase")
- if txt.startswith("At the beginning of your upkeep, "):
- end_trigger = 33
- trigger = 'YourUpkeepTrigger()'
- elif txt.startswith("At the beginning of your end step, "):
- end_trigger = 35
- trigger = 'PhaseTrigger(EndTurnStepEvent(), controller_match)'
- elif txt.startswith("At the beginning of each upkeep, "):
- end_trigger = 33
- trigger = 'PhaseTrigger(UpkeepStepEvent())'
- elif txt.startswith(("At the beginning of each end step, ", "At the beginning of the end step, ")):
- end_trigger = (35 if "each" in txt else 34)
- trigger = 'PhaseTrigger(EndTurnStepEvent())'
- elif txt.startswith("At the beginning of your precombat main phase, "):
- end_trigger = 47
- trigger = 'PhaseTrigger(MainPhase1Event(), controller_match)'
- elif txt.startswith("At the beginning of your postcombat main phase, "):
- end_trigger = 48
- trigger = 'PhaseTrigger(MainPhase2Event(), controller_match)'
- effect = txt[end_trigger:]
- match = re.match(r"choose (one|two|three|four|five|six|seven|eight|nine|ten) -{1,2} (.+)", effect)
- if match:
- num = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"].index(match.group(1)) + 1
- options = [item[0].upper() + item[1:] + ("" if item.endswith(".") else ".") for item in match.group(2).split("; or ")]
- effects = []
- for option in options:
- if it is not None: option = re.sub(r"\bit\b", it, option)
- effects.append(parse_effect(option, card, triggered=True))
- text = ""
- for i, effect in enumerate(effects):
- if effect:
- if len(effect) == 4:
- vars, target, extra, effect = effect
- else:
- vars, target, effect = effect
- extra = None
- else:
- vars, target, extra, effect = None, None, None, ""
- if vars: vars = ", %s"%(", ".join(vars))
- else: vars = ""
- if isinstance(target, (list, tuple)) and len(target) == 1: target = target[0]
- if not target: target = "target = yield NoTarget()"
- elif isinstance(target, (list, tuple)): target = "targets = yield Target(%s)"%("), Target(".join(target))
- else: target = "target = yield Target(%s)"%target
- text += "%sdef mode_%i(controller, source%s):\n"%((" " if i > 0 else ""), i+1, vars)
- text += " '''%s'''\n"%options[i]
- text += " %s\n"%target
- if extra:
- text += " %s\n"%extra
- text += " %s\n"%effect
- text += " yield\n"
- text += " effects = modal_triggered_effects(%s, choose=%i)\n"%(", ".join(["mode_%i"%mode for mode in range(1, i+2)]), num)
- effect = text
- else:
- if it is not None: effect = re.sub(r"\bit\b", it, effect)
- effect = parse_effect(effect, card, triggered = True)
- if effect and not isinstance(effect, str):
- if len(effect) == 4: vars, target, extra, effect = effect
- else:
- vars, target, effect = effect
- extra = None
- if vars: vars = ", %s"%(", ".join(vars))
- else: vars = ""
- if isinstance(target, (list, tuple)) and len(target) == 1: target = target[0]
- if not target: target = "target = yield NoTarget()"
- elif isinstance(target, (list, tuple)): target = "targets = yield Target(%s)"%("), Target(".join(target))
- else: target = "target = yield Target(%s)"%target
- temp = "def effects(controller, source%s):\n"%vars
- temp += " %s\n"%target
- if extra:
- temp += " %s\n"%extra
- temp += " %s\n"%effect
- temp += " yield"
- effect = temp
- if trigger is None:
- return triggered%i
- else:
- if not effect:
- effect = '''def effects(controller, source):
- target = yield NoTarget()
- yield'''
- text = "@triggered(txt=text[%i])\n"%i
- text += "def ability():\n"
- if condition:
- text += " %s\n"%condition
- text += " %s\n"%effect
- text += " return %s, effects\n"%trigger
- text += "abilities.add(ability)\n"
- return text
- def parse_activated(txt, card, i):
- limit = None
- target = None
- effect = None
- extra = None
- cost, txt = txt.split(": ", 1)
- cost = parse_cost(cost, card)
- if ". Activate this ability" in txt:
- limit = txt.index(". Activate this ability") + 1
- effect = txt[:limit]
- limit = parse_limit(txt[limit+1:], card)
- txt = effect
- effect = None
- match = re.match(r"Choose (one|two|three|four|five|six|seven|eight|nine|ten) -{1,2} (.+)", txt)
- if match:
- num = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"].index(match.group(1)) + 1
- options = [item[0].upper() + item[1:] + ("" if item.endswith(".") else ".") for item in match.group(2).split("; or ")]
- effects = []
- for option in options:
- effects.append(parse_effect(option, card))
- if limit: limit = ", limit=%s"%limit
- else: limit = ""
- text = "@activated(txt=text[%i]%s)\n"%(i, limit)
- text += "def ability():\n"
- for i, effect in enumerate(effects):
- if effect:
- if len(effect) == 3:
- target, extra, effect = effect
- else:
- target, effect = effect
- extra = None
- else:
- target, extra, effect = None, None, ""
- if isinstance(target, (list, tuple)) and len(target) == 1: target = target[0]
- if not target: target = "target = yield NoTarget()"
- elif isinstance(target, (list, tuple)): target = "targets = yield Target(%s)"%("), Target(".join(target))
- else: target = "target = yield Target(%s)"%target
- text += " def mode_%i(controller, source):\n"%(i+1)
- text += " '''%s'''\n"%options[i]
- text += " cost = yield NoCost()\n"
- text += " %s\n"%target
- if extra:
- text += " %s\n"%extra
- text += " %s\n"%effect
- text += " yield\n"
- text += " @modal_effects(%s, choose=%i)\n"%(", ".join(["mode_%i"%mode for mode in range(1, i+2)]), num)
- text += " def effects(controller, source):\n"
- text += " cost = yield %s\n"%cost
- text += " return effects\n"
- text += "abilities.add(ability)\n"
- return text
- else:
- effect = parse_effect(txt, card)
- if effect is None and limit is None:
- if "mana pool" in txt: return mana%(i, cost)
- else: return activated%(i, cost)
- elif effect is None:
- if "mana pool" in txt: return mana_limit%(i, limit, cost)
- else: return activated_limit%(i, limit, cost)
- else:
- if len(effect) == 3:
- target, extra, effect = effect
- else:
- target, effect = effect
- extra = None
- if isinstance(target, (list, tuple)) and len(target) == 1: target = target[0]
- if not target: target = "target = yield NoTarget()"
- elif isinstance(target, (list, tuple)): target = "targets = yield Target(%s)"%("), Target(".join(target))
- else: target = "target = yield Target(%s)"%target
- if limit: limit = ", limit=%s"%limit
- else: limit = ""
- text = "@%s(txt=text[%i]%s)\n"%(("mana" if (target.endswith("NoTarget()") and "add_mana" in effect) else "activated"), i, limit)
- text += "def ability():\n"
- text += " def effects(controller, source):\n"
- text += " cost = yield %s\n"%cost
- text += " %s\n"%target
- if extra:
- text += " %s\n"%extra
- text += " %s\n"%effect
- text += " yield\n"
- text += " return effects\n"
- text += "abilities.add(ability)\n"
- return text
- def parse_static(txt, card, i):
- effect = None
- condition = None
- after_condition = 0
- static_type = "static"
- keywords = parse_keywords(txt, card)
- tracking = parse_tracking(txt, card, i)
- if keywords:
- return '\n'.join(keywords)
- elif "enters the battlefield" in txt or "enter the battlefield" in txt:
- return parse_etb(txt, card, i)
- elif tracking:
- return tracking
- if " as long as " in txt:
- # So the condition and effect parse normally.
- txt = "As long as " + txt[txt.index(" as long as ") + 12:-1] + ", " + txt[:txt.index(" as long as ")] + "."
- if txt.startswith("As long as "):
- condition = parse_condition(txt[:txt.index(", ")+2], card)
- after_condition = txt.index(", ")+2
- global it
- if it is not None: txt = txt[:after_condition] + re.sub(r"\bit\b", it, txt[after_condition:])
- if txt[after_condition:].startswith(("Equipped ", "equipped ", "Enchanted ", "enchanted ", "Fortified ", "fortified ")):
- effect = parse_attached_effect(txt[after_condition:], card)
- static_type = "attached"
- else:
- effect = parse_static_effect(txt[after_condition:], card)
- if isinstance(effect, tuple): extra, effect = effect
- else: extra = None
- if effect is None and condition is None:
- return (static if static_type == "static" else attached)%i
- else:
- if not effect: effect = "# XXX"
- text = "@%s(txt=text[%i])\n"%(static_type, i)
- text += "def ability():\n"
- if condition:
- text += " def condition(source):\n"
- text += " %s\n"%condition
- text += " def effects(source):\n"
- if static_type == "attached":
- text += " card = source.attached_to\n"
- if extra:
- text += " %s\n"%extra
- text += " yield %s\n"%effect
- text += " return %scondition, effects\n"%("" if condition else "no_")
- text += "abilities.add(ability)\n"
- return text
- def parse_etb(txt, card, i):
- extra = None
- before = None
- effect = None
- match = re.match(r"^~ enters the battlefield( tapped)? with (an?|two|three|four|five|six|seven|eight|nine|ten) (([+-]\d+)/([+-]\d+)|\w+) counter(s?) on it.", txt)
- if match:
- effect = "" if not match.group(1) else "self.tapped = True\n "
- num = match.group(2)
- counter = match.group(3)
- if counter[0] in "+-":
- power, toughness = match.group(4, 5)
- counter = "PowerToughnessCounter(%i, %i)"%(int(power), int(toughness))
- else:
- counter = repr(counter)
- if num in ("a", "an"):
- effect += "self.add_counters(%s)"%counter
- else:
- num = ["two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"].index(num) + 2
- effect += "self.add_counters(%s, number=%i)"%(counter, num)
- match = re.match(r"^You may have ~ enter the battlefield as a copy of any creature on the battlefield.", txt)
- if match:
- extra = "cards = []"
- before = '''cards[:] = self.controller.choose_from_zone(cardtype=isCreature, action="clone", required=False, all=True)
- if cards: self.clone(cards[0])
- return True'''
- effect = "if cards: self.clone(cards[0])"
- if effect is None:
- return comes_into_play%i
- else:
- text = "@enters_battlefield(txt=text[%i])\n"%i
- text += "def ability():\n"
- if extra:
- text += " %s\n"%extra
- if before:
- text += " def before(self):\n"
- text += " %s\n"%before
- text += " def enterBattlefield(self):\n"
- text += " \"\"\"%s\"\"\"\n"%txt
- text += " %s\n"%effect
- text += " return %sbefore, enterBattlefield\n"%("" if before else "no_")
- text += "abilities.add(ability)\n"
- return text
- def parse_tracking_condition(other, what, who, card):
- condition = "not card == source" if other else ""
- events = []
- for word in what.split(" "):
- comp = parse_plural(word.lower(), card)
- if comp in ("white", "blue", "black", "red", "green"):
- if condition: condition += " and "
- condition += "card.color == " + comp.capitalize()
- if not "ColorModifiedEvent()" in events: events.append("ColorModifiedEvent()")
- elif comp in ("nonwhite", "nonblue", "nonblack", "nonred", "nongreen"):
- if condition: condition += " and "
- condition += "not card.color == " + comp[3:].capitalize()
- if not "ColorModifiedEvent()" in events: events.append("ColorModifiedEvent()")
- elif comp in ("artifact", "creature", "enchantment", "land", "planeswalker", "tribal", "token"):
- if condition: condition += " and "
- condition += "is" + comp.capitalize() + "(card)"
- if not "TypesModifiedEvent()" in events: events.append("TypesModifiedEvent()")
- elif comp in ("nonartifact", "noncreature", "nonenchantment", "nonland", "nonplaneswalker", "nontribal", "nontoken"):
- if condition: condition += " and "
- condition += "not is" + comp[3:].capitalize() + "(card)"
- if not "TypesModifiedEvent()" in events: events.append("TypesModifiedEvent()")
- elif comp.startswith("non"):
- if condition: condition += " and "
- condition += "not card.subtypes == " + comp[3:].capitalize().replace("-", "").replace("'", "")
- if not "SubtypesModifiedEvent()" in events: events.append("SubtypesModifiedEvent()")
- elif word[0].isupper():
- if condition: condition += " and "
- condition += "card.subtypes == " + comp.capitalize().replace("-", "").replace("'", "")
- if not "SubtypesModifiedEvent()" in events: events.append("SubtypesModifiedEvent()")
- if who:
- if condition: condition += " and "
- condition += "card.controller == source.controller"
- condition = "return " + condition
- if len(events) == 1: events = events[0]
- else: events = "(%s)"%(", ".join(events))
- return condition, events
- def parse_tracking(txt, card, i):
- effect = None
- condition = None
- conditional = None
- extra = None
- events = None
- if " as long as " in txt:
- # So the condition and effect parse normally.
- txt = "As long as " + txt[txt.index(" as long as ") + 12:-1] + ", " + txt[:txt.index(" as long as ")] + "."
- if txt.startswith("As long as "):
- conditional = parse_condition(txt[:txt.index(", ")+2], card)
- txt = txt[txt.index(", ")+2:]
- match = re.match(r"(Other )?(.+?)( you control)? get ([+-]\d+)/([+-]\d+)\.$", txt)
- if match:
- other, what, who, power, toughness = match.group(1, 2, 3, 4, 5)
- power = int(power)
- toughness = int(toughness)
- condition, events = parse_tracking_condition(other, what, who, card)
- effect = "card.augment_power_toughness(%i, %i)"%(power, toughness)
- match = re.match(r"(Other )?(.+?)( you control)? have (.+?)\.$", txt)
- if match:
- other, what, who, abilities = match.group(1, 2, 3, 4)
- if " and " in abilities:
- if ", " in abilities:
- lst = abilities.split(", ")
- lst[-1] = lst[-1].replace("and ", "")
- else:
- lst = abilities.split(" and ")
- abilities = []
- for item in lst: abilities.append("card.abilities.add(%s())"%(item.replace(" ", "_").lower()))
- abilities = ", ".join(abilities)
- else:
- abilities = "card.abilities.add(%s())"%(abilities.replace(" ", "_").lower())
- condition, events = parse_tracking_condition(other, what, who, card)
- effect = abilities
- match = re.match(r"(Other )?(.+?)( you control)? get ([+-]\d+)/([+-]\d+) and have (.+?)\.$", txt)
- if match:
- other, what, who, power, toughness, abilities = match.group(1, 2, 3, 4, 5, 6)
- power = int(power)
- toughness = int(toughness)
- if " and " in abilities:
- if ", " in abilities:
- lst = abilities.split(", ")
- lst[-1] = lst[-1].replace("and ", "")
- else:
- lst = abilities.split(" and ")
- abilities = []
- for item in lst: abilities.append("card.abilities.add(%s())"%(item.replace(" ", "_").lower()))
- abilities = ", ".join(abilities)
- else:
- abilities = "card.abilities.add(%s())"%(abilities.replace(" ", "_").lower())
- condition, events = parse_tracking_condition(other, what, who, card)
- effect = "card.augment_power_toughness(%i, %i), %s"%(power, toughness, abilities)
- if effect:
- text = "@static_tracking%s(txt=text[%i], events=%s)\n"%(("_conditional" if conditional else ""), i, events)
- text += "def ability():\n"
- text += " def condition(source, card):\n"
- text += " %s\n"%condition
- if conditional:
- text += " def conditional(source):\n"
- text += " %s\n"%conditional
- text += " def effects(source, card):\n"
- if extra:
- text += " %s\n"%extra
- text += " yield %s\n"%effect
- text += " return condition, %seffects\n"%("conditional, " if conditional else "")
- text += "abilities.add(ability)\n"
- return text
- else:
- return None
- def parse_limit(txt, card):
- if txt == "Activate this ability only during your upkeep.":
- return "UpkeepLimit()"
- elif txt == "Activate this ability only during your turn.":
- return "TurnLimit()"
- elif txt == "Activate this ability only any time you could cast a sorcery.":
- return "sorcery_limit"
- elif txt == "Activate this ability only once each turn.":
- return "CountLimit(1)"
- elif txt == "Activate this ability only if you have no cards in hand.":
- return "ConditionalLimit(lambda source: len(source.controller.hand) == 0)"
- match = re.match(r"^Activate this ability no more than (twice|(three|four|five|six|seven|eight|nine|ten) times) each turn\.$", txt)
- if match:
- num = match.group(1)
- num = ["twice", "three times", "four times", "five times", "six times", "seven times", "eight times", "nine times", "ten times"].index(num) + 2
- return "CountLimit(%i)"%num
- match = re.match(r"^Activate this ability only if you control (one|two|three|four|five|six|seven|eight|nine|ten) or (more|fewer) (.+)\.$", txt)
- if match:
- num, dir, type = match.group(1, 2, 3)
- num = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"].index(num) + 1
- type = parse_target(parse_plural(type, card), card)
- if dir == "more":
- dir = ">="
- else:
- dir = "<="
- return "ConditionalLimit(lambda source: len(source.controller.battlefield.get(%s)) %s %i)"%(type, dir, num)
- match = re.match(r"^Activate this ability only if (there are|you have) (one|two|three|four|five|six|seven|eight|nine|ten) or (more|fewer) cards in your (hand|graveyard)\.$", txt)
- if match:
- num, dir, zone = match.group(2, 3, 4)
- num = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"].index(num) + 1
- if dir == "more":
- dir = ">="
- else:
- dir = "<="
- return "ConditionalLimit(lambda source: len(source.controller.%s) %s %i)"%(zone, dir, num)
- match = re.match(r"^Activate this ability only if you have exactly (one|two|three|four|five|six|seven|eight|nine|ten) cards in your hand\.$", txt)
- if match:
- num = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"].index(match.group(1)) + 1
- return "ConditionalLimit(lambda source: len(source.controller.hand) == %i)"%num
- match = re.match(r"^Activate this ability only if ~'s (power|toughness) is (\d+) or (greater|less)\.$", txt)
- if match:
- stat, num, dir = match.group(1, 2, 3)
- num = int(num)
- if dir == "greater": dir = ">="
- else: dir = "<="
- return "ConditionalLimit(lambda source: source.%s %s %i)"%(stat, dir, num)
- match = re.match(r"^Activate this ability only if you have (\d+) or (more|less) life\.$", txt)
- if match:
- num, dir = match.group(1, 2)
- num = int(num)
- if dir == "more": dir = ">="
- else: dir = "<="
- return "ConditionalLimit(lambda source: source.controller.life %s %i)"%(dir, num)
- match = re.match(r"^Activate this ability only if a((ny)? player|ny? opponent) has (\d+) or (more|less) life\.$", txt)
- if match:
- type, num, dir = match.group(1, 3, 4)
- num = int(num)
- if dir == "more": dir = ">="
- else: dir = "<="
- if type.endswith("player"): type = "Keeper.players"
- else: type = "source.controller.opponents"
- return "ConditionalLimit(lambda source: any((True for player in %s if player.life %s %i)))"%(type, dir, num)
- return "# XXX"
- def parse_target(txt, card):
- return {
- "card": "isCard",
- "creature card": "isCreatureCard",
- "artifact card": "isArtifactCard",
- "enchantment card": "isEnchantmentCard",
- "land card": "isLandCard",
- "planeswalker card": "isPlaneswalkerCard",
- "instant card": "isInstantCard",
- "sorcery card": "isSorceryCard",
- "creature": "isCreature",
- "creature you control": "isCreature.with_condition(lambda c: c.controller == controller)",
- "artifact": "isArtifact",
- "artifact you control": "isArtifact.with_condition(lambda a: a.controller == controller)",
- "artifact creature": "isArtifactCreature",
- "noncreature artifact": "isNonCreatureArtifact",
- "player": "isPlayer",
- "opponent": "OpponentMatch(controller)",
- "creature or player": "isCreatureOrPlayer",
- "land": "isLand",
- "land you control": "isLand.with_condition(lambda l: l.controller == controller)",
- "permanent": "isPermanent",
- "permanent you control": "isPermanent.with_condition(lambda p: p.controller == controller)",
- "enchantment": "isEnchantment",
- "enchantment you control": "isEnchantment.with_condition(lambda e: e.controller == controller)",
- "noncreature permanent": "isPermanent.with_condition(lambda p: not p.types == Creature)",
- "nonland permanent": "isNonLand",
- "blocking creature": "isCreature.with_condition(lambda c: c.blocking)",
- "attacking creature": "isCreature.with_condition(lambda c: c.attacking)",
- "attacking or blocking creature": "isCreature.with_condition(lambda c: c.attacking or c.blocking)",
- "creature blocked by ~": "isCreature.with_condition(lambda c: c.attacking and c.blocked and source in c.blockers)",
- "creature blocking or blocked by ~": "isCreature.with_condition(lambda c: (c.blocking and source in c.blockers) or (source.blocking and c in source.blockers))",
- "tapped creature": "isCreature.with_condition(lambda c: c.tapped)",
- "untapped creature": "isCreature.with_condition(lambda c: not c.tapped)",
- "white creature": "isCreature.with_condition(lambda c: c.color == White)",
- "blue creature": "isCreature.with_condition(lambda c: c.color == Blue)",
- "black creature": "isCreature.with_condition(lambda c: c.color == Black)",
- "red creature": "isCreature.with_condition(lambda c: c.color == Red)",
- "green creature": "isCreature.with_condition(lambda c: c.color == Green)",
- "white permanent": "isPermanent.with_condition(lambda p: p.color == White)",
- "blue permanent": "isPermanent.with_condition(lambda p: p.color == Blue)",
- "black permanent": "isPermanent.with_condition(lambda p: p.color == Black)",
- "red permanent": "isPermanent.with_condition(lambda p: p.color == Red)",
- "green permanent": "isPermanent.with_condition(lambda p: p.color == Green)",
- "nonwhite creature": "isCreature.with_condition(lambda c: not c.color == White)",
- "nonblue creature": "isCreature.with_condition(lambda c: not c.color == Blue)",
- "nonblack creature": "isCreature.with_condition(lambda c: not c.color == Black)",
- "nonred creature": "isCreature.with_condition(lambda c: not c.color == Red)",
- "nongreen creature": "isCreature.with_condition(lambda c: not c.color == Green)",
- "nonwhite permanent": "isPermanent.with_condition(lambda p: not p.color == White)",
- "nonblue permanent": "isPermanent.with_condition(lambda p: not p.color == Blue)",
- "nonblack permanent": "isPermanent.with_condition(lambda p: not p.color == Black)",
- "nonred permanent": "isPermanent.with_condition(lambda p: not p.color == Red)",
- "nongreen permanent": "isPermanent.with_condition(lambda p: not p.color == Green)",
- }.get(txt, "# XXX")
- def parse_plural(txt, card):
- plurals = {
- "artifacts": "artifact",
- "creatures": "creature",
- "enchantments": "enchantment",
- "instants": "instant",
- "lands": "land",
- "planeswalkers": "planeswalker",
- "sorceries": "sorcery",
- "tribals": "tribal",
- "cards": "card",
- "permanents": "permanent",
- "advisors": "advisor",
- "allies": "ally",
- "angels": "angel",
- "anteaters": "anteater",
- #"antelope": "antelope",
- "apes": "ape",
- "archers": "archer",
- "archons": "archon",
- "artificers": "artificer",
- "assassins": "assassin",
- "assembly-workers": "assembly-worker",
- "atogs": "atog",
- "aurochs": "auroch",
- "avatars": "avatar",
- "badgers": "badger",
- "barbarians": "barbarian",
- "basilisks": "basilisk",
- "bats": "bat",
- "bears": "bear",
- "beasts": "beast",
- "beebles": "beeble",
- "berserkers": "berserker",
- "birds": "bird",
- "blinkmoths": "blinkmoth",
- "boars": "boar",
- "bringers": "bringer",
- "brushwaggs": "brushwagg",
- "camarids": "camarid",
- "camels": "camel",
- #"caribou": "caribou",
- "carriers": "carrier",
- "cats": "cat",
- "centaurs": "centaur",
- "cephalids": "cephalid",
- "chimeras": "chimera",
- "citizens": "citizen",
- "clerics": "cleric",
- "cockatrices": "cockatrice",
- "constructs": "construct",
- "cowards": "coward",
- "crabs": "crab",
- "crocodiles": "crocodile",
- #"cyclops": "cyclops",
- #"dauthi": "dauthi",
- "demons": "demon",
- "deserters": "deserter",
- "devils": "devil",
- "djinns": "djinn",
- "dragons": "dragon",
- "drakes": "drake",
- "dreadnoughts": "dreadnought",
- "drones": "drone",
- "druids": "druid",
- "dyrads": "dryad",
- "dwarves": "dwarf",
- "efreets": "efreet",
- "eggs": "egg",
- "elders": "elder",
- "elementals": "elemental",
- "elephants": "elephant",
- "elves": "elf",
- #"elk": "elk",
- "eyes": "eye",
- "faeries": "faerie",
- "ferrets": "ferret",
- "fishes": "fish",
- "flagbearers": "flagbearer",
- "foxes": "fox",
- "frogs": "frog",
- "fungi": "fungus",
- "gargoyles": "gargoyle",
- "germs": "germ",
- "giants": "giant",
- "gnomes": "gnome",
- "goats": "goat",
- "goblins": "goblin",
- "golems": "golem",
- "gorgons": "gorgon",
- "graveborns": "graveborn",
- "gremlins": "gremlin",
- "griffins": "griffin",
- "hags": "hag",
- "harpies": "harpy",
- "hellions": "hellion",
- "hippos": "hippo",
- "homarids": "homarid",
- "homunculi": "homunculus",
- "horrors": "horror",
- "horses": "horse",
- "hounds": "hound",
- "humans": "human",
- "hydras": "hydra",
- "hyenas": "hyena",
- "illusions": "illusion",
- "imps": "imp",
- "incarnations": "incarnation",
- "insects": "insect",
- "jellyfishes": "jellyfish",
- "juggernauts": "juggernaut",
- "kavus": "kavu",
- "kirins": "kirin",
- "kithkins": "kithkin",
- "knights": "knight",
- "kobolds": "kobold",
- "kors": "kor",
- "krakens": "kraken",
- "lammasi": "lammasu",
- "leeches": "leech",
- "leviathans": "leviathan",
- "lhurgoyfs": "lhurgoyf",
- "licids": "licid",
- "lizards": "lizard",
- "manticores": "manticore",
- "masticores": "masticore",
- "mercenaries": "mercenary",
- #"merfolk": "merfolk",
- "metathrans": "metathran",
- "minions": "minion",
- "minotaurs": "minotaur",
- "mongers": "monger",
- "mongeese": "mongoose",
- "monks": "monk",
- #"moonfolk": "moonfolk",
- "mutants": "mutant",
- #"myr": "myr",
- "mystics": "mystic",
- "nautili": "nautilus",
- #"nephilim": "nephilim",
- "nightmares": "nightmare",
- "nightstalkers": "nightstalker",
- "ninjas": "ninja",
- "noggles": "noggle",
- "nomads": "nomad",
- "octopi": "octopus",
- "ogres": "ogre",
- "oozes": "ooze",
- "orbs": "orb",
- "orcs": "orc",
- "orggs": "orgg",
- "ouphes": "ouphe",
- "oxen": "ox",
- "oysters": "oyster",
- "pegasi": "pegasus",
- "pentavites": "pentavite",
- "pests": "pest",
- "phelddagrifs": "phelddagrif",
- #"phoenix": "phoenix",
- "pinchers": "pincher",
- "pirates": "pirate",
- "plants": "plant",
- "prisms": "prism",
- "rabbits": "rabbit",
- "rats": "rat",
- "rebels": "rebel",
- "reflections": "reflection",
- "rhinos": "rhino",
- "riggers": "rigger",
- "rogues": "rogue",
- "salamanders": "salamander",
- #"samurai": "samurai",
- #"sand": "sand",
- "saprolings": "saproling",
- "satyrs": "satyr",
- "scarecrows": "scarecrow",
- "scorpions": "scorpion",
- "scouts": "scout",
- "serfs": "serf",
- "serpents": "serpent",
- "shades": "shade",
- "shamans": "shaman",
- "shapeshifters": "shapeshifter",
- #"sheep": "sheep",
- "skeletons": "skeleton",
- "sliths": "slith",
- "slivers": "sliver",
- "slugs": "slug",
- "snakes": "snake",
- "soldiers": "soldier",
- #"soltari": "soltari",
- #"spawn": "spawn",
- "specters": "specter",
- "spellshapers": "spellshaper",
- "sphinxes": "sphinx",
- "spiders": "spider",
- "spikes": "spike",
- "spirits": "spirit",
- "splinters": "splinter",
- "sponges": "sponge",
- "squids": "squid",
- "squirrels": "squirrel",
- "starfishes": "starfish",
- "survivors": "survivor",
- "tetravites": "tetravite",
- "thalakoses": "thalakos",
- "thopters": "thopter",
- "thrulls": "thrull",
- #"treefolk": "treefolk",
- "triskelavites": "triskelavite",
- "trolls": "troll",
- "turtles": "turtle",
- "unicorns": "unicorn",
- "vampires": "vampire",
- #"vedalken": "vedalken",
- #"viashino": "viashino",
- "volvers": "volver",
- "walls": "wall",
- "warriors": "warrior",
- "weirds": "weird",
- "whales": "whale",
- "wizards": "wizard",
- "wolves": "wolf",
- "wolverines": "wolverine",
- "wombats": "wombat",
- "worms": "worm",
- "wraiths": "wraith",
- "wurms": "wurm",
- "yetis": "yeti",
- "zombies": "zombie",
- #"zubera": "zubera",
- "contraptions": "contraption",
- #"equipment": "equipment",
- "fortifications": "fortification",
- "auras": "aura",
- "shrines": "shrine",
- #"arcanes": "arcane",
- "traps": "trap",
- "deserts": "desert",
- "forests": "forest",
- "islands": "island",
- "lairs": "lair",
- "locuses": "locus",
- "mines": "mine",
- "mountains": "mountain",
- #"plains": "plains",
- "power-plants": "power-plant",
- "swamps": "swamp",
- "towers": "tower",
- #"urza's": "urza's",
- }
- for word in plurals:
- if word in txt: txt = txt.replace(word, plurals[word])
- return txt
- def parse_condition(txt, card):
- global it
- if txt == "As long as you have no cards in hand, ":
- return "return len(source.controller.hand) == 0"
- match = re.match(r"^As long as you control (an?|two|three|four|five|six|seven|eight|nine|ten)( or (more|fewer))? (.+), ", txt)
- if match:
- num, dir, type = match.group(1, 3, 4)
- type = parse_target(parse_plural(type, card), card)
- if num == "an": num = "a"
- if not dir: dir = "more"
- num = ["a", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"].index(num) + 1
- if dir == "more":
- dir = ">="
- else:
- dir = "<="
- return "return len(source.controller.battlefield.get(%s)) %s %i"%(type, dir, num)
- match = re.match(r"^As long as (one|two|three|four|five|six|seven|eight|nine|ten) or (more|fewer) card(s are| is) in your graveyard, ", txt)
- if match:
- num, dir = match.group(1, 2)
- num = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"].index(num) + 1
- if dir == "more":
- dir = ">="
- else:
- dir = "<="
- return "return len(source.controller.graveyard) %s %i"%(dir, num)
- match = re.match(r"^As long as you have (one|two|three|four|five|six|seven|eight|nine|ten) or (more|fewer) card(s) in hand, ", txt)
- if match:
- num, dir = match.group(1, 2)
- num = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"].index(num) + 1
- if dir == "more":
- dir = ">="
- else:
- dir = "<="
- return "return len(source.controller.hand) %s %i"%(dir, num)
- match = re.match(r"^As long as you have (\d+) or (more|less) life, ", txt)
- if match:
- num, dir = match.group(1, 2)
- num = int(num)
- if dir == "more": dir = ">="
- else: dir = "<="
- return "return source.controller.life %s %i"%(dir, num)
- match = re.match(r"^As long as a((ny?) player|ny? opponent) has (\d+) or (more|less) life, ", txt)
- if match:
- type, num, dir = match.group(1, 3, 4)
- num = int(num)
- if dir == "more": dir = ">="
- else: dir = "<="
- if type.endswith("player"): type = "Keeper.players"
- else: type = "source.controller.opponents"
- return """for player in %s:
- if player.life %s %i: return True
- else: return False"""%(type, dir, num)
- match = re.match(r"^As long as (~|(enchanted|equipped|fortified) (permanent|artifact|creature|enchantment|land|planeswalker)) is (white|blue|black|red|green), ", txt)
- if match:
- who, color = match.group(1, 4)
- it = who
- if who == "~":
- return "source.color == %s"%(color.capitalize())
- else:
- return "return source.attached_to and source.attached_to.color == %s"%(color.capitalize())
- return "return True"
- def parse_effect(txt, card, triggered=False):
- txt = re.sub(r", then (?!shuffle)(\w)", lambda match: ". " + match.group(1).upper(), txt)
- txt = re.sub(r", then shuffle your library.", r". Then shuffle your library.", txt)
- if ". " in txt:
- txt = txt.split(". ")
- txt = [(item + "." if not i == len(txt) - 1 else item) for i, item in enumerate(txt)]
- while "Then shuffle your library." in txt: txt.remove("Then shuffle your library.")
- i = 0
- while i < len(txt):
- element = txt[i]
- if element in ("It can't be regenerated.", "They can't be regenerated."):
- txt[i-1] += " "+element
- del txt[i]
- continue
- i += 1
- if len(txt) == 1:
- txt = txt[0]
- else:
- results = [parse_effect(item, card, triggered) for item in txt]
- results = [(item if item else ((None, None, None) if triggered else (None, None))) for item in results]
- if triggered:
- vars, targets, effects = zip(*results)
- vars = [item for item in vars if item]
- else:
- targets, effects = zip(*results)
- targets = [item for item in targets if item]
- effects = "\n yield\n ".join([(item if item else "") for item in effects])
- if len(targets) > 1:
- temp = effects.split("target")
- effects = ""
- index = 0
- for section in temp[:-1]:
- effects += section + "targets[%i]"%index
- index += 1
- effects += temp[-1]
- if triggered:
- return vars, targets, effects
- else:
- return targets, effects
- vars = None
- target = None
- effect = None
- match = re.match(r"^~ deals (\d+) damage to you\.$", txt)
- if match:
- effect = "source.deal_damage(controller, %i)"%int(match.group(1))
- match = re.match(r"^~ deals (\d+) damage to target (.+)\.$", txt)
- if match:
- target = parse_target(match.group(2), card)
- effect = "source.deal_damage(target, %i)"%int(match.group(1))
- match = re.match(r"^~ deals damage equal to (its (power|toughness)|the number of (.+?) (on the battlefield|you control)) to (you|target (.+))\.$", txt)
- if match:
- val, who = match.group(1, 5)
- if who == "you":
- who = "controller"
- else:
- target = parse_target(match.group(6), card)
- who = "target"
- if val.startswith("its "):
- val = match.group(2)
- if val == "power": val = "source.power"
- elif val == "toughness": val = "source.toughness"
- elif val.startswith("the number of "):
- val = parse_target(parse_plural(match.group(3), card), card)
- scope = match.group(4)
- if scope == "on the battlefield": scope = ", all=True"
- else: scope = ""
- val = "len(controller.battlefield.get(%s%s))"%(val, scope)
- effect = "source.deal_damage(%s, %s)"%(who, val)
- match = re.match(r"^[Pp]revent the next (\d+) damage that would be dealt to (~|target (.+?)|equipped creature|enchanted creature|enchanted planeswalker|enchanted player)( this turn)?\.$", txt)
- if match:
- num, who, temp = match.group(1, 2, 4)
- num = int(num)
- if who == "~":
- who = "source"
- elif who.startswith("target "):
- who = "target"
- target = parse_target(match.group(3), card)
- else:
- who = "source.attached_to"
- effect = "prevent_damage(%s, %i)"%(who, num)
- if temp:
- effect = "until_end_of_turn(%s)"%effect
- if who == "source.attached_to":
- effect = "if %s: %s"%(who, effect)
- match = re.match(r"^([Yy]ou )?[Dd]raw (a|two|three|four|five|six|seven|eight|nine|ten) card(s)?\.$", txt)
- if match:
- num = match.group(2)
- if num == "a":
- effect = "controller.draw()"
- else:
- num = ["two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"].index(num) + 2
- effect = "controller.draw(%i)"%num
- match = re.match(r"^[Yy]ou may draw (a|two|three|four|five|six|seven|eight|nine|ten) card(s)?\.$", txt)
- if match:
- num = match.group(1)
- if num == "a":
- effect = "controller.draw()"
- else:
- num = ["two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"].index(num) + 2
- effect = "controller.draw(%i)"%num
- effect = "if controller.you_may('draw %s card%s'): %s"%(match.group(1), match.group(2) if match.group(2) else "", effect)
- match = re.match(r"^[Tt]arget (player|opponent) draws? (a|two|three|four|five|six|seven|eight|nine|ten) cards?\.$", txt)
- if match:
- who, num = match.group(1, 2)
- if num == "a":
- effect = "target.draw()"
- else:
- num = ["two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"].index(num) + 2
- effect = "target.draw(%i)"%num
- if who == "opponent":
- target = "OpponentMatch(controller)"
- else: target = "isPlayer"
- match = re.match(r"^([Tt]arget (player|opponent) puts|([Yy]ou )?[Pp]ut) the top( (two|three|four|five|six|seven|eight|nine|ten|eleven|twelve|thirteen|fourteen|fifteen|sixteen|seventeen|eighteen|nineteen|twenty))? card(s)? of his or her library into his or her graveyard?\.$", txt)
- if match:
- who, which, num = match.group(1, 2, 5)
- if num: num = ["two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen", "twenty"].index(num) + 2
- else: num = 1
- if who.startswith(("T", "t")):
- who = "target"
- target = parse_target(which, card)
- else: who = "controller"
- if num == 1:
- effect = "card = %s.library.top()"%who
- effect += "\n if card:\n "
- else:
- effect = "for card in %s.library.top(%d):\n "%(who, num)
- effect += "card.move_to('graveyard')"
- match = re.match(r"^([Yy]ou |[Tt]arget (player|opponent) )?([Gg]ain|[Ll]ose)(s)? (\d+) life\.$", txt)
- if match:
- who, dir, num = match.group(1, 3, 5)
- num = int(num)
- if dir in ("Gain", "gain"):
- dir = "+"
- else:
- dir = "-"
- if not who or who in ("You ", "you "):
- effect = "controller"
- else:
- target = parse_target(match.group(2), card)
- effect = "target"
- effect += ".life %s= %i"%(dir, num)
- match = re.match(r"^[Yy]ou may (gain|lose) (\d+) life\.$", txt)
- if match:
- dir, num = match.group(1, 2)
- num = int(num)
- if dir == "gain": dir = "+"
- else: dir = "-"
- effect = 'if controller.you_may("%s %i life"): controller.life %s= %i'%(match.group(1), num, dir, num)
- match = re.match(r"^[Ee]ach (player|opponent) (gains|loses) (\d+) life\.$", txt)
- if match:
- who, dir, num = match.group(1, 2, 3)
- num = int(num)
- if dir == "gain":
- dir = "+"
- else:
- dir = "-"
- if who == "player":
- effect = "for player in keeper.players"
- elif who == "opponent":
- effect = "for player in controller.opponents"
- effect += ": player.life %s= %i"%(dir, num)
- match = re.match(r"^([Yy]our|[Tt]arget (player|opponent)'s) life total becomes (\d+)\.$", txt)
- if match:
- who, num = match.group(1, 3)
- num = int(num)
- if who in ("Your", "your"):
- effect = "controller"
- else:
- target = parse_target(match.group(2), card)
- effect = "target"
- effect += ".life = %i"%num
- match = re.match(r"^Look at the top card of (your|target (player|opponent)'s) library, then you may put that card into (your|that player's) graveyard\.$", txt)
- if match:
- who = match.group(1)
- if who == "target player's":
- target = "isPlayer"
- who = "target"
- elif who == "target opponent's":
- target = "OpponentMatch(controller)"
- who = "target"
- else:
- who = "controller"
- effect = "topcard = %s.library.top()\n"%who
- effect += " if topcard:\n"
- effect += " controller.look_at((topcard,))\n"
- if who == "controller":
- effect += " if controller.you_may('put %s into your graveyard'%topcard.name):\n"
- else:
- effect += " if controller.you_may('put %s into %s's graveyard'%(topcard.name, topcard.owner)):\n"
- effect += " topcard.move_to('graveyard')"
- match = re.match(r"^(~|[Tt]arget (.+?)|([Ee]quipped|[Ee]nchanted|[Ff]ortified) (permanent|artifact|creature|land|enchantment|planeswalker)) gains (.+?) until end of turn\.$", txt)
- if match:
- effect = ""
- if match.group(1) == "~":
- who = "source"
- elif match.group(1).startswith(("Target ", "target ")):
- target = parse_target(match.group(2), card)
- who = "target"
- else: who = "source.attached_to"
- what = match.group(5)
- if what.startswith("your choice of "):
- what = what[15:]
- if ", " in what:
- lst = what.split(", ")
- lst[-1] = lst[-1].replace("or ", "")
- else:
- lst = what.split(" or ")
- effect = "choice = controller.make_selection(%r, prompt='Choose an ability')\n "%lst
- what = "%s.abilities.add(eval(choice.replace(' ', '_').lower())())"%who
- elif " and " in what:
- if ", " in what:
- lst = what.split(", ")
- lst[-1] = lst[-1].replace("and ", "")
- else:
- lst = what.split(" and ")
- what = []
- for item in lst: what.append("%s.abilities.add(%s())"%(who, item.replace(" ", "_").lower()))
- what = ", ".join(what)
- else:
- what = "%s.abilities.add(%s())"%(who, what.replace(" ", "_").lower())
- effect += "until_end_of_turn(%s)"%what
- if who == "source.attached_to":
- effect = "if %s: %s"%(who, effect)
- match = re.match(r"^(~|[Tt]arget (.+?)|([Ee]quipped|[Ee]nchanted|[Ff]ortified) (permanent|artifact|creature|land|enchantment|planeswalker)) loses (.+?) until end of turn\.$", txt)
- if match:
- who, what = match.group(1, 5)
- if who == "~":
- who = "source"
- elif who.startswith(("Target ", "target ")):
- target = parse_target(match.group(2), card)
- who = "target"
- else: who = "source.attached_to"
- effect = "until_end_of_turn(%s.abilites.remove(%r))"%(who, what.lower())
- if who == "source.attached_to":
- effect = "if %s: %s"%(who, effect)
- match = re.match(r"^(~|[Tt]arget (.+?)|([Ee]quipped|[Ee]nchanted|[Ff]ortified) (permanent|artifact|creature|land|enchantment|planeswalker)) gains or loses (.+?) until end of turn\.$", txt)
- if match:
- who, what = match.group(1, 5)
- if who == "~":
- who = "source"
- elif who.startswith(("Target ", "target ")):
- target = parse_target(match.group(2), card)
- who = "target"
- else: who = "source.attached_to"
- ability_name = what.lower()
- ability = ability_name.replace(" ", "_")
- effect = """if %r not in %s.abilities:
- if controller.you_may("have %%s gain %s"%%(%s.name)):
- until_end_of_turn(%s.abilities.add(%s()))
- else:
- until_end_of_turn(%s.abilities.remove(%r))
- else:
- if controller.you_may("have %%s lose %s"%%(%s.name)):
- until_end_of_turn(%s.abilities.remove(%r))
- else:
- until_end_of_turn(%s.abilities.add(%s()))"""%(ability_name, who, ability_name, who, who, ability, effect, ability_name, ability_name, who, who, ability_name, who, ability)
- if who == "source.attached_to":
- new = effect.split("\n")
- effect = "if %s:\n"%who
- for line in new:
- effect += " %s\n"%line
- effect = effect[:-1]
- match = re.match(r"^~ gets ([+-]\d+)/([+-]\d+) until end of turn\.$", txt)
- if match:
- power, toughness = match.group(1, 2)
- effect = "until_end_of_turn(source.augment_power_toughness(%i, %i))"%(int(power), int(toughness))
- match = re.match(r"^[Tt]arget (.+) gets ([+-]\d+)/([+-]\d+) until end of turn\.$", txt)
- if match:
- target, power, toughness = match.group(1, 2, 3)
- target = parse_target(target, card)
- effect = "until_end_of_turn(target.augment_power_toughness(%i, %i))"%(int(power), int(toughness))
- match = re.match(r"^[Ee](nchanted|quipped) creature gets ([+-]\d+)/([+-]\d+) until end of turn\.$", txt)
- if match:
- power, toughness = match.group(2, 3)
- effect = "if source.attached_to: until_end_of_turn(source.attached_to.augment_power_toughness(%i, %i))"%(int(power), int(toughness))
- match = re.match(r"^(~|[Tt]arget (.+?)|[Ee]nchanted creature|[Ee]quipped creature) gets ([+-]\d+)/([+-]\d+) and gains (.+?) until end of turn\.$", txt)
- if match:
- effect = ""
- who, power, toughness, what = match.group(1, 3, 4, 5)
- if who == "~":
- who = "source"
- elif who.startswith(("Target ", "target ")):
- who = "target"
- target = parse_target(match.group(2), card)
- else:
- who = "source.attached_to"
- if what.startswith("your choice of "):
- what = what[15:]
- if ", " in what:
- lst = what.split(", ")
- lst[-1] = lst[-1].replace("or ", "")
- else:
- lst = what.split(" or ")
- effect = "choice = controller.make_selection(%r, prompt='Choose an ability')\n "%lst
- what = "%s.abilities.add(eval(choice.replace(' ', '_').lower())())"%who
- elif " and " in what:
- if ", " in what:
- lst = what.split(", ")
- lst[-1] = lst[-1].replace("and ", "")
- else:
- lst = what.split(" and ")
- what = []
- for item in lst: what.append("%s.abilities.add(%s())"%(who, item.replace(" ", "_").lower()))
- what = ", ".join(what)
- else:
- what = "%s.abilities.add(%s())"%(who, what.replace(" ", "_").lower())
- effect += "until_end_of_turn(%s.augment_power_toughness(%i, %i), %s)"%(who, int(power), int(toughness), what)
- if who == "source.attached_to":
- effect = "if %s: %s"%(who, effect)
- match = re.match(r"^(~|[Tt]arget (.+)|[Ee]nchanted creature|[Ee]quipped creature) becomes (\d+)/(\d+) until end of turn\.$", txt)
- if match:
- who, power, toughness = match.group(1, 3, 4)
- power, toughness = int(power), int(toughness)
- if who == "~":
- effect = "source"
- elif who.startswith(("Target ", "target ")):
- target = parse_target(match.group(2), card)
- effect = "target"
- else: who = "source.attached_to"
- effect = "until_end_of_turn(%s.set_power_toughness(%i, %i))"%(effect, power, toughness)
- if who == "source.attached_to":
- effect = "if %s: %s"%(who, effect)
- match = re.match(r"^(~|[Tt]arget (.+)|([Ee]quipped|[Ee]nchanted|[Ff]ortified) (permanent|artifact|creature|land|enchantment|planeswalker)) is (indestructible|unblockable) this turn\.$", txt)
- if match:
- who, what = match.group(1, 5)
- if who == "~":
- who = "source"
- elif who.startswith(("Target ", "target ")):
- target = parse_target(match.group(2), card)
- who = "target"
- else: who = "source.attached_to"
- effect = "until_end_of_turn(%s.%s())"%(who, what)
- if who == "source.attached_to":
- effect = "if %s: %s"%(who, effect)
- match = re.match(r"^[Aa]dd (one|two|three|four|five|six|seven|eight|nine|ten) mana of any (one )?color to your mana pool\.$", txt)
- if match:
- num = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"].index(match.group(1)) + 1
- effect = "controller.add_mana(%r, %r, %r, %r, %r)"%("W"*num, "U"*num, "B"*num, "R"*num, "G"*num)
- match = re.match(r"^[Aa]dd (two|three|four|five|six|seven|eight|nine|ten) mana in any combination of colors to your mana pool\.$", txt)
- if match:
- num = ["two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"].index(match.group(1)) + 2
- effect = "controller.add_mana(%r)"%("(W/U/B/R/G)"*num)
- match = re.match(r"^[Aa]dd (([WUBRG1234567890/(){}]+(, )?(( )?or )?)+) to your mana pool\.$", txt)
- if match:
- mana = match.group(1)
- mana = [strip_parens(m.strip(",")) for m in mana.split(" ") if not m == "or"]
- effect = "controller.add_mana(%s)"%(', '.join([repr(m) for m in mana]))
- match = re.match(r"^[Pp]ut (an?|two|three|four|five|six|seven|eight|nine|ten) (([+-]\d+)/([+-]\d+)|\w+) counter(s?) on (~|target ([^.]+)|(enchanted|equipped|fortified) (permanent|artifact|creature|land|enchantment|planeswalker))\.$", txt)
- if match:
- counter = match.group(2)
- if counter[0] in "+-":
- power, toughness = match.group(3, 4)
- counter = "PowerToughnessCounter(%i, %i)"%(int(power), int(toughness))
- else:
- counter = repr(counter)
- num = match.group(1)
- if num in ("a", "an"):
- num = ""
- else:
- num = ", number=%i"%(["two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"].index(num) + 2)
- if match.group(6) == "~":
- effect = "source"
- elif match.group(6).startswith("target "):
- target = parse_target(match.group(7), card)
- effect = "target"
- else:
- effect = "source.attached_to"
- effect += ".add_counters(%s%s)"%(counter, num)
- if effect.startswith("source.attached_to"):
- effect = "if source.attached_to: %s"%effect
- match = re.match(r"^[Pp]ut (an?|two|three|four|five|six|seven|eight|nine|ten)( legendary)? (\d+)/(\d+) (.+?) tokens?( with (.+?))?( named (.+?))? onto the battlefield\.$", txt)
- if match:
- num, legendary, power, toughness, characteristics, abilities, name = match.group(1, 2, 3, 4, 5, 7, 9)
- if num == "an": num = "a"
- num = ["a", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"].index(num) + 1
- if num > 1:
- num = ", number=%d"%num
- else: num = ""
- power, toughness = int(power), int(toughness)
- pt = '"P/T": (%d, %d), '%(power, toughness)
- if legendary: legendary = '"supertypes": Legendary, '
- else: legendary = ""
- characteristics = characteristics.replace('colorless ', '').replace(' and ', ' ')
- colors = []
- subtypes = []
- types = []
- for word in characteristics.split(" "):
- if word in ("white", "blue", "black", "red", "green"): colors.append(word.capitalize())
- elif word[0].isupper(): subtypes.append(word) # In most use-cases, subtypes are capitalized and types are not; since we don't have to worry about start-of-sentence capitalization here, we can use this to divide the two without an exhaustive list of either.
- else: types.append(word.capitalize())
- if colors:
- if len(colors) == 1: colors = colors[0]
- else: colors = "(%s)"%(', '.join(colors))
- colors = '"color": %s, '%colors
- else: colors = ""
- if subtypes:
- if len(subtypes) == 1: subtypes = subtypes[0]
- else: subtypes = "(%s)"%(', '.join(subtypes))
- subtypes = '"subtypes": %s, '%subtypes
- else: subtypes = ""
- if types:
- if len(types) == 1: types = types[0]
- else: types = "(%s)"%(', '.join(types))
- types = '"types": %s, '%types
- else: types = ""
- if abilities:
- abilities = abilities.replace(", and ", ", ").replace(" and ", ", ").split(", ")
- if len(abilities) == 1: abilities = abilities[0]
- else: abilities = tuple(abilities)
- abilities = '"abilities": %r, '%(abilities,)
- else: abilities = ""
- if name: name = '"name": %r, '%name
- else: name = ""
- effect = ("controller.play_tokens({%s%s%s%s%s%s%s}%s)"%(legendary, pt, colors, subtypes, types, abilities, name, num)).replace(", }", "}")
- match = re.match(r"^[Ee]xile (~|target (.+)|(equipped|enchanted|fortified) (permanent|artifact|creature|land|enchantment|planeswalker))\.$", txt)
- if match:
- if match.group(1) == "~":
- effect = "source"
- elif match.group(1).startswith("target "):
- target = parse_target(match.group(2), card)
- effect = "target"
- else:
- effect = "if source.attached_to: source.attached_to"
- effect += ".move_to('exile')"
- match = re.match(r"^[Ss]acrifice (~|target (.+)|(equipped|enchanted|fortified) (permanent|artifact|creature|land|enchantment|planeswalker))\.$", txt)
- if match:
- if match.group(1) == "~":
- effect = "source"
- elif match.group(1).startswith("target "):
- target = parse_target(match.group(2), card)
- effect = "target"
- else: effect = "source.attached_to"
- effect = "controller.sacrifice(%s)"%effect
- if effect.endswith("_to)"):
- effect = "if source.attached_to: %s"%effect
- match = re.match(r"^[Tt]arget (.+?)'s controller sacrifices it.$", txt)
- if match:
- target = parse_target(match.group(1), card)
- effect = "target.controller.sacrifice(target)"
- match = re.match(r"^[Rr]eturn (~|an? (.+?)( you control)?|(equipped|enchanted|fortified) (permanent|artifact|creature|land|enchantment|planeswalker)|target (.+?)) to its owner's hand\.$", txt)
- if match:
- who = match.group(1)
- if who == "~":
- effect = "source.move_to('hand')"
- elif who.startswith(("a ", "an ")):
- effect = "for perm in controller.choose_from_zone(zone='battlefield', cardtype=%s, required=True, action=\"return to its owner's hand\"%s):\n perm.move_to('hand')"%(parse_target(match.group(2), card), "" if match.group(3) else ", all=True")
- elif who.startswith("target "):
- target = parse_target(match.group(4), card)
- effect = "target.move_to('hand')"
- else:
- effect = "if source.attached_to: source.attached_to.move_to('hand')"
- match = re.match(r"^[Dd]estroy (~|target (.+?)|(equipped|enchanted|fortified) (permanent|artifact|creature|land|enchantment|planeswalker))(\. It can't be regenerated)?\.$", txt)
- if match:
- if match.group(1) == "~":
- effect = "source"
- elif match.group(1).startswith("target "):
- target = parse_target(match.group(2), card)
- effect = "target"
- else: effect = "if source.attached_to: source.attached_to"
- effect += ".destroy(%s)"%("regenerate=False" if match.group(5) else "")
- match = re.match(r"^[Dd]estroy all (.+?)( you control)?(\. They can't be regenerated)?\.$", txt)
- if match:
- type = parse_target(parse_plural(match.group(1), card), card)
- effect = "for permanent in controller.battlefield.get(%s%s): permanent.destroy(%s)"%(type, ("" if match.group(2) else ", all=True"), ("regenerate=False" if match.group(3) else ""))
- match = re.match(r"^([Yy]ou |([Tt]arget|[Ee]ach) (player|opponent) )?[Ss]acrifices? (an?|two|three|four|five|six|seven|eight|nine|ten) (.+?)\.$", txt)
- if match:
- who, targeted, ptype, num, type = match.group(1, 2, 3, 4, 5)
- if not who or who in ("You ", "you "):
- effect = "controller"
- elif targeted in ("Target", "target"):
- effect = "target"
- target = "isPlayer" if ptype == "player" else "OpponentMatch(controller)"
- else:
- effect = "for player in "
- if ptype == "player":
- effect += "Keeper.players"
- else: effect += "controller.opponents"
- effect += ":\n player"
- if num == "an": num = "a"
- num = ["a", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"].index(num) + 1
- type = parse_target(parse_plural(type, card), card)
- effect += ".force_sacrifice(cardtype=%s%s)"%(type, "" if num == 1 else ", number=%i"%num)
- match = re.match(r"^([Yy]ou |([Tt]arget|[Ee]ach) (player|opponent) )?[Dd]iscards? (a|two|three|four|five|six|seven|eight|nine|ten) cards?( at random)?\.$", txt)
- if match:
- who, targeted, ptype, num, random = match.group(1, 2, 3, 4, 5)
- if not who or who in ("You ", "you "):
- who = "controller"
- effect = who
- elif targeted in ("Target", "target"):
- who = "target"
- effect = who
- target = "isPlayer" if ptype == "player" else "OpponentMatch(controller)"
- else:
- who = "player"
- effect = "for player in "
- if ptype == "player":
- effect += "Keeper.players"
- else: effect += "controller.opponents"
- effect += ":\n player"
- num = ["a", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"].index(num) + 1
- if random:
- effect += ".discard_at_random(%s)"%("" if num == 1 else str(num))
- else:
- effect += ".force_discard(%s)"%("" if num == 1 else str(num))
- match = re.match(r"^[Rr]egenerate (~|target (.+)|(equipped|enchanted|fortified) (permanent|artifact|creature|land|enchantment|planeswalker))\.$", txt)
- if match:
- if match.group(1) == "~":
- effect = "source"
- elif match.group(1).startswith("target "):
- target = parse_target(match.group(2), card)
- effect = "target"
- else: effect = "if source.attached_to: source.attached_to"
- effect += ".regenerate()"
- match = re.match(r"^[Yy]ou may put (an|two|three|four|five|six|seven|eight|nine|ten|) (([+-]\d+)/([+-]\d+)|\w+) counter(s?) on (~|target ([^.]+)|(equipped|enchanted|fortified) (permanent|artifact|creature|land|enchantment|planeswalker))\.$", txt)
- if match:
- num, counter, who = match.group(1, 2, 6)
- if counter[0] in "+-":
- power, toughness = match.group(3, 4)
- counter = "PowerToughnessCounter(%i, %i)"%(int(power), int(toughness))
- else:
- counter = repr(counter)
- if num in ("a", "an"):
- num = ""
- else:
- num = ", number=%i"%(["two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"].index(num) + 2)
- if who == "~":
- who = "source"
- elif who.startswith("target "):
- target = parse_target(match.group(7), card)
- who = "target"
- else:
- who = "source.attached_to"
- effect = "%s.add_counters(%s%s)"%(who, counter, num)
- effect = 'if %scontroller.you_may("put %s %s counter%s on %%s"%%%s.name): %s'%(("%s and "%who if who == "source.attached_to" else ""), match.group(1), match.group(2), ("s" if match.group(5) else ""), who, effect)
- match = re.match(r"^(~|[Tt]arget (.+?)|([Ee]quipped|[Ee]nchanted|[Ff]ortified) (permanent|artifact|creature|land|enchantment|planeswalker)) can't be blocked this turn except by (one|two|three|four|five|six|seven|eight|nine|ten) or (more|fewer) creatures\.$", txt)
- if match:
- who, num, dir = match.group(1, 5, 6)
- if who == "~":
- who = "source"
- elif who.startswith(("Target ", "target ")):
- target = parse_target(match.group(2), card)
- who = "target"
- else: who = "source.attached_to"
- num = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"].index(num) + 1
- if dir == "more":
- dir = ">="
- else:
- dir = "<="
- if triggered: return vars, target, "def checkBlock(self, combat_assignment, not_blocking): return (not self in combat_assignment) or (len(combat_assignment[self]) == 0 or len(combat_assignment[self]) %s %i)"%(dir, num), '%suntil_end_of_turn(override(%s, "checkBlock", checkBlock))'%(("if %s: "%who if who == "source.attached_to" else ""), who)
- else: return target, "def checkBlock(self, combat_assignment, not_blocking): return (not self in combat_assignment) or (len(combat_assignment[self]) == 0 or len(combat_assignment[self]) %s %i)"%(dir, num), '%suntil_end_of_turn(override(%s, "checkBlock", checkBlock))'%(("if %s: "%who if who == "source.attached_to" else ""), who)
- match = re.match(r"^([Tt]ap|[Uu]ntap) (~|target (.+?)|(equipped|enchanted|fortified) (permanent|artifact|creature|land|enchantment|planeswalker))\.$", txt)
- if match:
- dir, who = match.group(1, 2)
- dir = dir.lower()
- if who == "~":
- who = "source"
- elif who.startswith("target "):
- target = parse_target(match.group(3), card)
- who = "target"
- else: who = "source.attached_to"
- effect = "%s.%s()"%(who, dir)
- if who == "source.attached_to":
- effect = "if %s: %s"%(who, effect)
- match = re.match(r"^~ doesn't untap during your next untap step\.$", txt)
- if match:
- effect = "source.doesnt_untap_your_next_untap_step()"
- match = re.match(r"^[Dd]raw a card at the beginning of the next turn's upkeep\.$", txt)
- if match:
- effect = "source.delay(draw_card_next_upkeep)"
- if effect:
- if triggered: return vars, target, effect
- else: return target, effect
- else:
- return None
- def parse_static_effect(txt, card):
- match = re.match(r"~ gets ([+-]\d+)/([+-]\d+)\.$", txt)
- if match:
- power, toughness = match.group(1, 2)
- return "source.augment_power_toughness(%i, %i)"%(int(power), int(toughness))
- match = re.match(r"~ has (.+)\.$", txt)
- if match:
- return "source.abilities.add(%s())"%(match.group(1).lower().replace(" ", "_"))
- match = re.match(r"~ gets ([+-]\d+)/([+-]\d+) and has (.+)\.$", txt)
- if match:
- power, toughness, abilities = match.group(1, 2, 3)
- if " and " in abilities:
- if ", " in abilities:
- lst = abilities.split(", ")
- lst[-1] = lst[-1].replace("and ", "")
- else:
- lst = abilities.split(" and ")
- abilities = []
- for item in lst: abilities.append("source.abilities.add(%s())"%(item.replace(" ", "_").lower()))
- abilities = ", ".join(abilities)
- else:
- abilities = "source.abilities.add(%s())"%(abilities.replace(" ", "_").lower())
- return "source.augment_power_toughness(%i, %i), %s"%(int(power), int(toughness), abilities)
- match = re.match(r"~ can't be blocked except by (one|two|three|four|five|six|seven|eight|nine|ten) or (more|fewer) creatures\.$", txt)
- if match:
- num, dir = match.group(1, 2)
- num = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"].index(num) + 1
- if dir == "more":
- dir = ">="
- else:
- dir = "<="
- return ("def checkBlock(self, combat_assignment, not_blocking): return (not self in combat_assignment) or (len(combat_assignment[self]) == 0 or len(combat_assignment[self]) %s %i)"%(dir, num), 'override(source, "checkBlock", checkBlock)')
- else:
- return None
- def parse_attached_effect(txt, card):
- match = re.match(r"[Ee](quipped|nchanted) creature gets ([+-]\d+)/([+-]\d+)\.$", txt)
- if match:
- power, toughness = match.group(2, 3)
- return "card.augment_power_toughness(%i, %i)"%(int(power), int(toughness))
- match = re.match(r"([Ee](quipped|nchanted)|[Ff]ortified) (permanent|artifact|creature|land|enchantment|planeswalker) has (.+?)\.$", txt)
- if match:
- return "card.abilities.add(%s())"%(match.group(4).lower().replace(" ", "_"))
- match = re.match(r"[Ee](quipped|nchanted) creature gets ([+-]\d+)/([+-]\d+) and has (.+?)\.$", txt)
- if match:
- power, toughness, abilities = match.group(2, 3, 4)
- if " and " in abilities:
- if ", " in abilities:
- lst = abilities.split(", ")
- lst[-1] = lst[-1].replace("and ", "")
- else:
- lst = abilities.split(" and ")
- abilities = []
- for item in lst: abilities.append("card.abilities.add(%s())"%(item.replace(" ", "_").lower()))
- abilities = ", ".join(abilities)
- else:
- abilities = "card.abilities.add(%s())"%(abilities.replace(" ", "_").lower())
- return "card.augment_power_toughness(%i, %i), %s"%(int(power), int(toughness), abilities)
- match = re.match(r"[Ee](quipped|nchanted) creature can't be blocked except by (one|two|three|four|five|six|seven|eight|nine|ten) or (more|fewer) creatures\.$", txt)
- if match:
- num, dir = match.group(2, 3)
- num = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"].index(num) + 1
- if dir == "more":
- dir = ">="
- else:
- dir = "<="
- return ("def checkBlock(self, combat_assignment, not_blocking): return (not self in combat_assignment) or (len(combat_assignment[self]) == 0 or len(combat_assignment[self]) %s %i)"%(dir, num), 'override(card, "checkBlock", checkBlock)')
- match = re.match(r"[Ee](quipped|nchanted) creature can't (attack|block|attack or block)\.$", txt)
- if match:
- type = match.group(2)
- effect = ""
- if type == "attack" or type == "attack or block":
- effect += 'override(card, "canAttack", lambda self: False)'
- if type.endswith("block"): effect += ", "
- if type == "block" or type == "attack or block":
- effect += 'override(card, "canBlock", lambda self: False)'
- return effect
- else:
- return None
- def parse_keywords(txt, card):
- lines = []
- txt = txt.lower().replace(";", ",")
- parts = [t.strip() for t in txt.split(",")]
- for p in parts:
- if p in keywords:
- keyword = keywords[p]
- else:
- for k, data in variable_keywords:
- if p.startswith(k) and (len(p) == len(k) or not p[len(k)].isalpha()):
- break
- else: continue
- val = p[len(k):].strip("- \t")
- if len(data) > 2:
- val = val.split(" ")
- # Suspend and Reinforce are written "Keyword Number--Cost"
- # However, the magiccards.info parser writes it "Keyword Number-Cost".
- val = sum([i.split("-") for i in val], [])
- # But we can make it work with either! This strips out blank strings:
- val = [i for i in val if i]
- if len(val) == len(data) - 1:
- result = []
- for type, info in zip(data[1:], val):
- result.append(convert_value(info, type, card))
- keyword = data[0]%tuple(result)
- else:
- continue
- else:
- keyword = data[0]%convert_value(val, data[1], card)
- if keyword: lines.append("abilities.add(%s)\n"%keyword)
- #lines.extend(["abilities.add(%s)\n"%keywords[p] for p in parts if p in keywords])
- if len(lines) < len(parts): return []
- return lines
- def convert_value(value, type, card):
- if type == "number":
- # Do nothing
- return value
- elif type == "cost":
- return parse_cost(value, card)
- elif type == "subtype":
- return value.capitalize()
- elif type == "types":
- types = []
- subtypes = []
- for val in value.split(" or "):
- val = parse_plural(val, card)
- if val in types_list: types.append(val.capitalize())
- else: subtypes.append(val.capitalize())
- temp = []
- if types: temp.append("types=[%s]"%", ".join(types))
- if subtypes: temp.append("subtypes=[%s]"%", ".join(subtypes))
- return ", ".join(temp)
- elif type == "target":
- return parse_target(value, card)
- return "# XXX"
- def strip_parens(manastr):
- result = ""
- current = ""
- inner = False
- for char in manastr:
- if char in "0123456789WUBRGTQSPXYZwubrgtqspxyz" or (inner and char == "/"):
- current += char
- elif char in ("(", "{"):
- inner = True
- elif char in (")", "}"):
- inner = False
- if not inner:
- if len(current) > 1: result += "(" + current + ")"
- else: result += current
- current = ""
- return result
- def parse_cost(s, card):
- if re.search(r", (?!rounded)", s): #Some costs annoyingly have commas. Here, I'm assuming they're all of the "rounded up/down" variety.
- return " + ".join([parse_cost(cost, card) for cost in re.split(r", (?!rounded)", s)])
- else:
- if not set(s).difference(set("0123456789WUBRGTQSPXYZwubrgtqspxyz{}/()")):
- s = strip_parens(s)
- #if s.startswith("{"):
- # s = s.replace("{", "").replace("}", "")
- if s in ("T", "t"):
- return "TapCost()"
- elif s in ("Q", "q"):
- return "UntapCost()"
- elif re.match(r"^(un)?tap (an|one|two|three|four|five|six|seven|eight|nine|ten|x|any number of) (un)?tapped (.+?) you control\.?$", s.lower()):
- dir, num, type = re.match(r"^(un)?tap (an|one|two|three|four|five|six|seven|eight|nine|ten|x|any number of) (un)?tapped (.+?) you control\.?$", s.lower()).group(1, 2, 4)
- num = {"an": 1,
- "one": 1,
- "two": 2,
- "three": 3,
- "four": 4,
- "five": 5,
- "six": 6,
- "seven": 7,
- "eight": 8,
- "nine": 9,
- "ten": 10,
- "x": None,
- "any number of": None}[num]
- type = parse_target(parse_plural(type, card), card)
- if num is None:
- num = ", number=controller.getX()"
- elif num == 1: num = ""
- else: num = ", number=%i"%num
- if dir: dir = "Untap"
- else: dir = "Tap"
- return "%sCost(%s%s)"%(dir, type, num)
- elif re.match(r"[Pp]ay ([0123456789WUBRGSPXYZwubrgspxyz{}()/]+) and (\d+) life(, rounded (up|down))?\.?", s):
- return "ManaCost(%r) + LifeCost(%i)"%(strip_parens(s.split()[1]), int(s.split()[3]))
- elif s.lower().startswith("pay") and s.lower().endswith(("life", "life.", "life, rounded up", "life, rounded up.", "life, rounded down", "life, rounded down.")):
- if s.lower().split(" ")[1] == "half": # I hope there's no "pay half of X, rounded down" life costs out there... it would just get REALLY confusing.
- if s.lower().endswith(("down", "down.")): return "LifeCost(controller.life / 2)"
- return "LifeCost((controller.life + 1) / 2)"
- elif s.lower().split(" ")[1] == "x":
- return "LifeCost(controller.getX())" # The value can be retrieved with cost.payment if need be.
- return "LifeCost(%d)"%int(s.split(" ")[1])
- elif re.match(r"^sacrifice (~|(an?|two|three|four|five|six|seven|eight|nine|ten) (.+?))\.?$", s.lower()):
- who, num, type = re.match(r"^sacrifice (~|(an?|two|three|four|five|six|seven|eight|nine|ten) (.+?))\.?$", s.lower()).group(1, 2, 3)
- if who == "~": return "SacrificeCost()"
- else:
- type = parse_target(parse_plural(type, card), card)
- if num == "an": num = "a"
- num = ["a", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"].index(num) + 1
- return "SacrificeCost(%s%s)"%(type, ("" if num == 1 else ", number=%i"%num))
- elif re.match(r"^exile (~|(an?|two|three|four|five|six|seven|eight|nine|ten) (.+?))( from your (hand|graveyard))\.?$", s.lower()):
- who, num, type, where = re.match(r"^exile (~|(an?|two|three|four|five|six|seven|eight|nine|ten) (.+?))( from your (hand|graveyard))\.?$", s.lower()).group(1, 2, 3, 5)
- if not where: where = "Battlefield"
- else: where = where.capitalize()
- if who == "~":
- return "ExileFrom%sCost()"%where
- else:
- type = parse_target(parse_plural(type, card), card)
- if num == "an": num = "a"
- num = ["a", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"].index(num) + 1
- return "ExileFrom%sCost(%s%s)"%(where, type, ("" if num == 1 else ", number=%i"%num))
- elif re.match(r"^discard (an?|two|three|four|five|six|seven|eight|nine|ten) (.+?)\.?$", s.lower()):
- num, what = re.match(r"^discard (an?|two|three|four|five|six|seven|eight|nine|ten) (.+?)\.?$", s.lower()).group(1, 2)
- what = parse_target(parse_plural(what, card), card)
- if num == "an": num = "a"
- num = ["a", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"].index(num) + 1
- return "DiscardCost(%s%s)"%(what, ("" if num == 1 else ", number=%i"%num))
- elif (s.startswith(("-", "+")) or (s == "0" and "Planeswalker" in card["types"])) and not set(s[1:]).difference(set("0123456789")):
- return "LoyaltyCost(%i)"%(int(s))
- elif s.lower().startswith("remove ") and s.lower().endswith(("counter from ~", "counters from ~")):
- temp = {"one": 1,
- "two": 2,
- "three": 3,
- "four": 4,
- "five": 5,
- "six": 6,
- "seven": 7,
- "eight": 8,
- "nine": 9,
- "ten": 10, # Does any card ever remove this many counters as part of a (non-X) cost?
- "all": -1,
- "a": 1,
- "an": 1,
- "x": None,
- "any": None,
- }[s.lower().split()[1]]
- if " ".join(s.lower().split()[2:4]) == "number of":
- if len(s.lower().split()) == 7: counter = ""
- else: counter = s.lower().split()[4].replace("/", "")
- else:
- if len(s.lower().split()) == 5: counter = ""
- else: counter = s.lower().split()[2].replace("/", "")
- if temp is None:
- return "RemoveCounterCost(%snumber=controller.getX())"%(repr(counter)+", " if counter else "") # As with LifeCost above, the value can be retrieved with cost.payment if need be.
- elif temp == 1:
- return "RemoveCounterCost(%s)"%(repr(counter) if counter else "")
- else:
- return "RemoveCounterCost(%snumber=%d)"%((repr(counter)+", " if counter else ""), temp)
- elif s.lower().startswith("remove ") and ("counter from a" in s.lower() or "counters from a" in s.lower()) and s.lower().endswith(" you control"):
- temp = {"one": 1,
- "two": 2,
- "three": 3,
- "four": 4,
- "five": 5,
- "six": 6,
- "seven": 7,
- "eight": 8,
- "nine": 9,
- "ten": 10,
- "all": -1,
- "every": -1,
- "each": -1,
- "a": 1,
- "an": 1,
- "x": None,
- "any number of": None,
- }[s.lower().split()[1]]
- if " ".join(s.lower().split()[2:4]) == "counter from": counter = ""
- else: counter = s.lower().split()[2].replace("/", "")
- cardtype = parse_target(s.lower().split(" from ", 1)[1][:-12].split(" ", 1)[1], card)
- if temp is None:
- return "RemoveCounterCost(%scardtype=%s, number=controller.getX())"%((repr(counter)+", " if counter else ""), cardtype)
- elif temp == 1:
- return "RemoveCounterCost(%scardtype=%s)"%((repr(counter)+", " if counter else ""), cardtype)
- else:
- return "RemoveCounterCost(%scardtype=%s, number=%d)"%((repr(counter)+", " if counter else ""), cardtype, temp)
- elif set(s).difference(set("0123456789WUBRGSPXYZwubrgspxyz{}()/")):
- return "# XXX cost object"
- else:
- return "ManaCost(%s)"%repr(s.upper())
- types_list = ("artifact",
- "creature",
- "enchantment",
- "instant",
- "land",
- "planeswalker",
- "sorcery",
- "tribal",
- )
- keywords = dict([
- ("first strike", "first_strike()"),
- ("double strike", "double_strike()"),
- ("trample", "trample()"),
- ("haste", "haste()"),
- ("flash", "flash()"),
- ("flying", "flying()"),
- ("banding", "banding()"),
- ("reach", "reach()"),
- ("protection from white", "protection_from_white()"),
- ("protection from blue", "protection_from_blue()"),
- ("protection from black", "protection_from_black()"),
- ("protection from red", "protection_from_red()"),
- ("protection from green", "protection_from_green()"),
- ("protection from artifacts", "protection_from_artifacts()"),
- ("protection from everything", "protection_from_everything()"),
- ("protection from multicolored", "protection_from_multicolored()"),
- ("protection from monocolored", "protection_from_monocolored()"),
- ("lifelink", "lifelink()"),
- ("plainswalk", "plainswalk()"),
- ("islandwalk", "islandwalk()"),
- ("swampwalk", "swampwalk()"),
- ("mountainwalk", "mountainwalk()"),
- ("forestwalk", "forestwalk()"),
- ("nonbasic landwalk", "nonbasic_landwalk()"),
- ("legendary_landwalk", "legendary_landwalk()"),
- ("shadow", "shadow()"),
- ("defender", "defender()"),
- ("shroud", "shroud()"),
- ("vigilance", "vigilance()"),
- ("fear", "fear()"),
- ("flanking", "flanking()"),
- ("exalted", "exalted()"),
- ("cascade", "cascase()"),
- ("deathtouch", "deathtouch()"),
- ("intimidate", "intimidate()"),
- ("bloodthirst x", "bloodthirst_x()"), #rule 702.51b - "Bloodthirst X" is a special form of bloodthirst. "Bloodthirst X" means "This permanent enters the battlefield with X +1/+1 counters on it, where X is the total damage your opponents have been dealt this turn."
- ("sunburst", "sunburst()"),
- ("hideaway", "hideaway()"),
- ("changeling", "changeling()"),
- ("horsemanship", "horsemanship()"),
- ("persist", "persist()"),
- ("wither", "wither()"),
- ("split second", "split_second()"),
- ("provoke", "provoke()"),
- ("storm", "storm()"),
- ("epic", "epic()"),
- ("convoke", "convoke()"),
- ("haunt", "haunt()"),
- ("gravestorm", "gravestorm()"),
- ("delve", "delve()"),
- ("conspire", "conspire()"),
- ("phasing", "phasing()"),
- ("rebound", "rebound()"),
- ("totem armor", "totem_armor()"),
- ("infect", "infect()"),
- ("battle cry", "battle_cry()"),
- ("living weapon", "living_weapon()"),
- ("hexproof", "hexproof()"),
- ("undying", "undying()"),
- ("soulbond", "soulbond()"),
- ])
- # Has to be a tuple because Python versions prior to 2.7 don't have OrderedDicts, and "champion an" must be checked before "champion a".
- variable_keywords = (
- ("enchant", ("enchant(%s)", "target")),
- ("kicker", ("kicker(%s)", "cost")),
- ("equip", ("equip(%s)", "cost")),
- ("absorb", ("absorb(%s)", "number")),
- ("fortify", ("fortify(%s)", "cost")),
- ("multikicker", ("multikicker(%s)", "cost")),
- ("devour", ("devour(%s)", "number")),
- ("unearth", ("unearth(%s)", "cost")),
- ("recover", ("recover(%s)", "cost")),
- ("cycling", ("cycling(%s)", "cost")),
- ("basic landcycling", ("basic_landcycling(%s)", "cost")),
- ("plainscycling", ("plains_cycling(%s)", "cost")),
- ("islandcycling", ("island_cycling(%s)", "cost")),
- ("swampcycling", ("swamp_cycling(%s)", "cost")),
- ("mountaincycling", ("mountain_cycling(%s)", "cost")),
- ("forestcycling", ("forest_cycling(%s)", "cost")),
- ("modular", ("modular(%s)", "number")),
- ("graft", ("graft(%s)", "number")),
- ("soulshift", ("soulshift(%s)", "number")),
- ("bushido", ("bushido(%s)", "number")),
- ("evoke", ("evoke(%s)", "cost")),
- ("reinforce", ("reinforce(%s, %s)", "number", "cost")),
- ("prowl", ("prowl(%s)", "cost")),
- ("fading", ("fading(%s)", "number")),
- ("transmute", ("transmute(%s)", "cost")),
- ("echo", ("echo(%s)", "cost")),
- ("suspend", ("suspend(%s, %s)", "number", "cost")),
- ("rampage", ("rampage(%s)", "number")),
- ("cumulative upkeep", ("cumulative_upkeep(%s)", "cost")),
- ("vanishing", ("vanishing(%s)", "number")),
- ("buyback", ("buyback(%s)", "cost")),
- ("flashback", ("flashback(%s)", "cost")),
- ("madness", ("madness(%s)", "cost")),
- ("morph", ("morph(%s)", "cost")),
- ("amplify", ("amplify(%s)", "number")),
- ("affinity for", ("affinity(%s)", "types")),
- ("entwine", ("entwine(%s)", "cost")),
- ("splice onto", ("splice(%s, %s)", "subtype", "cost")),
- ("offering", ("offering(%s)", "subtype")),
- ("ninjutsu", ("ninjutsu(%s)", "cost")),
- ("dredge", ("dredge(%s)", "number")),
- ("bloodthirst", ("bloodthirst(%s)", "number")),
- ("replicate", ("replicate(%s)", "cost")),
- ("graft", ("graft(%s)", "number")),
- ("ripple", ("ripple(%s)", "number")),
- ("aura swap", ("aura_swap(%s)", "cost")),
- ("frenzy", ("frenzy(%s)", "number")),
- ("poisonous", ("poisonous(%s)", "number")),
- ("transfigure", ("transfigure(%s)", "number")),
- ("champion an", ("champion(%s)", "types")),
- ("champion a", ("champion(%s)", "types")),
- ("annihilator", ("annihilator(%s)", "number")),
- ("level up", ("level_up(%s)", "cost")),
- ("miracle", ("miracle(%s)", "cost")),
- )
- activated = '''@activated(txt=text[%d])
- def ability():
- def effects(controller, source):
- cost = yield %s
- target = yield NoTarget()
- yield
- return effects
- abilities.add(ability)
- '''
- activated_limit = '''@activated(txt=text[%d], limit=%s)
- def ability():
- def effects(controller, source):
- cost = yield %s
- target = yield NoTarget()
- yield
- return effects
- abilities.add(ability)
- '''
- mana = '''@mana(txt=text[%d])
- def ability():
- def effects(controller, source):
- cost = yield %s
- target = yield NoTarget()
- controller.add_mana("0")
- yield
- return effects
- abilities.add(ability)
- '''
- mana_limit = '''@mana(txt=text[%d], limit=%s)
- def ability():
- def effects(controller, source):
- cost = yield %s
- target = yield NoTarget()
- controller.add_mana("0")
- yield
- return effects
- abilities.add(ability)
- '''
- triggered = '''@triggered(txt=text[%d])
- def ability():
- def condition(source, /* relevant event fields */):
- return True
- def effects(controller, source, /* relevant event fields */):
- target = yield NoTarget()
- yield
- return Trigger(# XXX Put event here, condition), effects
- abilities.add(ability)
- '''
- static_tracking = '''@static_tracking(txt=text[%d])
- def ability():
- def condition(source, card):
- return True
- def effects(source, card):
- yield /* continuous_effect */ # Make sure to yield the result of continuous effects
- return condition, effects
- abilities.add(ability)
- '''
- static = '''@static(txt=text[%d])
- def ability():
- def condition(source):
- return True
- def effects(source):
- yield /* continuous_effect */ # Make sure to yield the result of continuous effects
- return condition, effects
- abilities.add(ability)
- '''
- attached = '''@attached(txt=text[%d])
- def ability():
- def condition(source):
- return True
- def effects(source):
- card = source.attached_to
- yield /* continuous_effect */ # Make sure to yield the result of continuous effects
- return condition, effects
- abilities.add(ability)
- '''
- comes_into_play = '''@enters_battlefield(txt=text[%d])
- def ability():
- def before(source):
- pass
- def enterBattlefieldAs(self):
- \'''XXX Add replacement message\'''
- pass
- return before, enterBattlefieldAs
- abilities.add(ability)
- '''
- nonperm = '''\
- #################################
- @%s()
- def ability():
- def effects(controller, source):
- cost = yield source.cost
- target = yield NoTarget()
- yield
- return effects
- abilities.add(ability)
- '''
- if __name__ == "__main__":
- import sys, os
- if sys.version_info[0] < 3: input = raw_input
- outfile = None
- if len(sys.argv) == 3:
- lines = []
- with open(sys.argv[1], "r") as f:
- for line in f:
- line = line.strip()
- lines.append(line)
- if not "|" in lines[0]:
- newlines = []
- current = []
- for line in lines:
- if line:
- current.append(line)
- elif current:
- newlines.append("|".join(current))
- current = []
- if current:
- newlines.append("|".join(current))
- lines = newlines
- results = []
- for line in lines:
- dict = parse_card_oracle(line)
- print("Parsing %r..."%dict["name"])
- results.append(carddict_to_code(dict) + "--------------- %s\n"%dict["name"])
- with open(sys.argv[2], "a") as f:
- f.write("\n".join(results))
- raise SystemExit("Conversion complete.")
- elif len(sys.argv) == 2:
- outfile = sys.argv[1]
- elif len(sys.argv) > 1:
- raise SystemExit("Usage: %s [[read_file] write_file]"%(os.path.basename(sys.argv[0])))
- line = None
- lines = []
- while not line == "exit":
- while not (isinstance(line, str) and (line.strip() == "" or line == "exit")):
- line = input()
- if not line.strip() == "": lines.append(line)
- if not line == "exit" and lines:
- dict = parse_card_oracle("|".join(lines))
- result = carddict_to_code(dict) + "--------------- %s\n\n"%dict["name"]
- if outfile:
- with open(outfile, "a") as f:
- f.write(result)
- else:
- print result
- line = None
- lines = []
Add Comment
Please, Sign In to add comment