import json
import re
from reportlab.lib.units import cm
class Card(object):
sets = None
types = None
type_names = None
bonus_regex = None
class CardJSONEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, Card):
return obj.__dict__
return json.JSONEncoder.default(self, obj)
@staticmethod
def decode_json(obj):
return Card(**obj)
def __init__(
self,
name=None,
cardset="",
types=None,
cost="",
description="",
potcost=0,
debtcost=0,
extra="",
count=-1,
card_tag="missing card_tag",
cardset_tags=None,
group_tag="",
group_top=False,
image=None,
text_icon=None,
randomizer=True,
cardset_tag="",
):
if types is None:
types = [] # make sure types is a list
if cardset_tags is None:
cardset_tags = [] # make sure cardset_tags is a list
if name is None:
name = card_tag # make sure there is a meaningful default name
self.name = name.strip()
self.cardset = cardset.strip()
self.types = types
self.types_name = ""
self.cost = cost
self.description = description
self.potcost = potcost
self.debtcost = debtcost
self.extra = extra
self.card_tag = card_tag
self.cardset_tags = cardset_tags
self.group_tag = group_tag
self.group_top = group_top
self.image = image
self.text_icon = text_icon
self.cardset_tag = cardset_tag
self.setCardCount(count)
self.randomizer = randomizer
def getCardCount(self):
return sum(i for i in self.count)
def setCardCount(self, value):
value = int(value)
if value < 0:
self.count = [self.getType().getTypeDefaultCardCount()]
elif value == 0:
self.count = []
else:
self.count = [value]
def addCardCount(self, value):
self.count.extend(value)
def getStackHeight(self, thickness):
# return height of the stacked cards in cm. Using hight in cm of a stack of 60 Copper cards as thickness.
return self.getCardCount() * cm * (thickness / 60.0) + 2
def getType(self):
return Card.types[tuple(self.types)]
def getBonusBoldText(self, text):
for regex in Card.bonus_regex:
text = re.sub(regex, "\\1", text)
return text
@staticmethod
def addBonusRegex(bonus):
# Each bonus_regex matches the bonus keywords to be highlighted
# This only needs to be done once per language
if Card.bonus_regex is None:
# initialize the information holder
Card.bonus_regex = []
# Make sure have minimum to to anything
if not isinstance(bonus, dict):
return
if "include" not in bonus:
return
if not bonus["include"]:
return
if "exclude" not in bonus:
bonus["exclude"] = []
# Start processing of lists into a single regex statement
# (?i) makes this case insensitive
# (?!\) and (?!\<\/b\>) prevents matching already bolded items
# (?!\w) prevents smaller word matches. Prevents matching "Action" in "Actions"
if bonus["exclude"]:
bonus["exclude"].sort(reverse=True)
exclude_regex = r"(?!\w)(?!\s*(" + "|".join(bonus["exclude"]) + "))"
else:
exclude_regex = ""
bonus["include"].sort(reverse=True)
include_regex = r"(\+\s*\d+\s*(" + "|".join(bonus["include"]) + "))"
regex = r"(?i)((?!\)" + include_regex + exclude_regex + r"(?!\<\/b\>))"
Card.bonus_regex.append(regex)
def __repr__(self):
return '"' + self.name + '"'
def toString(self):
return (
self.name
+ " "
+ self.cardset
+ " "
+ "-".join(self.types)
+ " "
+ self.cost
+ " "
+ self.description
+ " "
+ self.extra
)
def isType(self, what):
return what in self.getType().getTypeNames()
def isExpansion(self):
return self.isType("Expansion")
def isEvent(self):
return self.isType("Event")
def isLandmark(self):
return self.isType("Landmark")
def isPrize(self):
return self.isType("Prize")
def get_GroupGlobalType(self):
return self.getType().getGroupGlobalType()
def get_GroupCost(self):
return self.getType().getGroupCost()
def get_total_cost(self, c):
# Return a tuple that represents the total cost of card c
# Hightest cost cards are in order:
# - Types with group cost of "" sort at the very end
# - cards with * since that can mean anything
# - cards with numeric errors
# convert cost (a string) into a number
if c.get_GroupCost() == "":
c_cost = 999
elif not c.cost:
c_cost = 0 # if no cost, treat as 0
elif "*" in c.cost:
c_cost = 998 # make it a really big number
else:
try:
c_cost = int(c.cost)
except ValueError:
c_cost = 997 # can't, so make it a really big number
return c_cost, c.potcost, c.debtcost
def set_lowest_cost(self, other):
# set self cost fields to the lower of the two's total cost
self_cost = self.get_total_cost(self)
other_cost = self.get_total_cost(other)
if other_cost < self_cost:
self.cost = other.cost
self.potcost = other.potcost
self.debtcost = other.debtcost
def setImage(self, use_set_icon=False):
setImage = None
if not use_set_icon and self.image is not None:
setImage = self.image
else:
if self.cardset_tag in Card.sets:
if "image" in Card.sets[self.cardset_tag]:
setImage = Card.sets[self.cardset_tag]["image"]
if setImage is None and self.cardset_tag != "base":
print(
'warning, no set image for set "{}", card "{}"'.format(
self.cardset, self.name
)
)
return setImage
def setTextIcon(self):
setTextIcon = None
if self.text_icon:
setTextIcon = self.text_icon
else:
if self.cardset_tag in Card.sets:
if "text_icon" in Card.sets[self.cardset_tag]:
setTextIcon = Card.sets[self.cardset_tag]["text_icon"]
if setTextIcon is None and self.cardset != "base":
print(
'warning, no set text for set "{}", card "{}"'.format(
self.cardset, self.name
)
)
return setTextIcon
def isBlank(self):
return self.isType("Blank")
class BlankCard(Card):
def __init__(self, num):
Card.__init__(self, str(num), "extra", ("Blank",), 0)
def isBlank(self):
return True
class CardType(object):
@staticmethod
def decode_json(obj):
return CardType(**obj)
def __init__(
self,
card_type,
card_type_image,
group_global_type=None,
group_cost=None,
defaultCardCount=10,
tabTextHeightOffset=0,
tabCostHeightOffset=-1,
):
self.typeNames = tuple(card_type)
self.tabImageFile = card_type_image
self.group_global_type = group_global_type
self.group_cost = group_cost
self.defaultCardCount = defaultCardCount
self.tabTextHeightOffset = tabTextHeightOffset
self.tabCostHeightOffset = tabCostHeightOffset
def getTypeDefaultCardCount(self):
return self.defaultCardCount
def getTypeNames(self):
return self.typeNames
def getTabImageFile(self):
if not self.tabImageFile:
return None
return self.tabImageFile
def getGroupGlobalType(self):
return self.group_global_type
def getGroupCost(self):
return self.group_cost
def getTabTextHeightOffset(self):
return self.tabTextHeightOffset
def getTabCostHeightOffset(self):
return self.tabCostHeightOffset