gdritter repos dndchar / 41dd3cf
Added library with richer data types Getty Ritter 7 years ago
1 changed file(s) with 183 addition(s) and 0 deletion(s). Collapse all Expand all
1 import random
2
3 class Die:
4 @staticmethod
5 def d4():
6 return random.randint(1, 4)
7
8 @staticmethod
9 def d6():
10 return random.randint(1, 6)
11
12 @staticmethod
13 def d8():
14 return random.randint(1, 8)
15
16 @staticmethod
17 def d10():
18 return random.randint(1, 10)
19
20 @staticmethod
21 def d12():
22 return random.randint(1, 12)
23
24 @staticmethod
25 def d20():
26 return random.randint(1, 20)
27
28
29 class Data(object):
30 @classmethod
31 def from_json(cls, obj):
32 if hasattr(cls, 'FIELDS'):
33 values = {}
34 for name, typ in cls.FIELDS:
35 values[name] = typ.from_json(obj[name])
36 return cls(**values)
37 else:
38 raise AttributeError(
39 'Class {0} has no FIELDS attribute'.format(cls.__name__))
40
41 @classmethod
42 def ensure(cls, name, obj):
43 if isinstance(obj, cls):
44 return obj
45 else:
46 raise TypeError(('Unexpected type for field {0}: '
47 'expected {1}, got {2}').format(
48 name, type(cls), type(obj)))
49
50 def __repr__(self):
51 return '{0}({1})'.format(
52 self.__class__.__name__,
53 ', '.join('{0}={1}'.format(k, repr(self.__dict__[k]))
54 for k, _ in self.__class__.FIELDS))
55
56 def __str__(self):
57 return '{0}({1})'.format(
58 self.__class__.__name__,
59 ', '.join('{0}={1}'.format(k, str(self.__dict__[k]))
60 for k, _ in self.__class__.FIELDS))
61
62 def __init__(self, **kwargs):
63 for field, typ in self.__class__.FIELDS:
64 self.__dict__[field] = typ.ensure(field, kwargs[field])
65
66 class Stat(Data):
67 STATS = [
68 'strength',
69 'dexterity',
70 'constitution',
71 'intelligence',
72 'wisdom',
73 'charisma',
74 'sanity'
75 ]
76
77 @staticmethod
78 def from_json(val):
79 return Stat(val)
80
81 @staticmethod
82 def random():
83 sum(sorted([Die.d6(), Die.d6(), Die.d6(), Die.d6()][1:]))
84
85 def __init__(self, score):
86 self.score = score
87
88 def modifier(self):
89 return (self.score // 2) - 5
90
91 def __repr__(self):
92 return 'Stat({0})'.format(self.score)
93
94 def __str__(self):
95 return str(self.score)
96
97 def mk_block(name, fields):
98 class Block(Data):
99 FIELDS = fields
100 Block.__name__ = name
101 return Block
102
103 StatBlock = mk_block('StatBlock', [(s, Stat) for s in Stat.STATS])
104
105 class Value(Data):
106 @staticmethod
107 def from_json(obj):
108 if (isinstance(obj, str) or
109 isinstance(obj, int) or
110 isinstance(obj, float)):
111 return Value(obj)
112 elif isinstance(obj, dict):
113 if len(obj) == 1:
114 k, v = obj.popitem()
115 if isinstance(v, dict):
116 return Value(k, **v)
117 else:
118 return Value(str(k) + ': ' + str(v))
119 else:
120 raise Exception('???')
121 elif isinstance(obj, list):
122 return [ Value.from_json(elem) for elem in obj ]
123
124 @staticmethod
125 def ensure(n, obj):
126 if isinstance(obj, Value):
127 return obj
128 elif isinstance(obj, list):
129 return [ Value.ensure(n, elem) for elem in obj ]
130 else:
131 return Value(obj)
132
133 def __init__(self, datum, **tags):
134 self.datum = datum
135 self.tags = tags
136
137 def __repr__(self):
138 if not self.tags:
139 return 'Value({0})'.format(repr(self.datum))
140 else:
141 return 'Value({0}, {1})'.format(
142 repr(self.datum),
143 ', '.join('{0}={1}'.format(k, repr(v))
144 for k, v in self.tags.items()))
145
146 def __str__(self):
147 if not self.tags:
148 return str(self.datum)
149 else:
150 return '{0} ({1})'.format(
151 str(self.datum),
152 ', '.join('{0}: {1}'.format(k, str(v))
153 for k, v in self.tags.items()))
154
155 class Character(Data):
156 FIELDS = [
157 ('name', Value),
158 ('class', Value),
159 ('level', Value),
160 ('background', Value),
161 ('race', Value),
162 ('alignment', Value),
163 ('stats', StatBlock),
164 ('speed', Value),
165 ('size', Value),
166 ('proficiency bonus', Value),
167 ('hit die', Value),
168 ('hit point maximum', Value),
169 ('ac', Value),
170 ('personality', Value),
171 ('ideals', Value),
172 ('bonds', Value),
173 ('flaw', Value),
174 ('saving throws', Value),
175 ('languages', Value),
176 ('proficiencies', Value),
177 ('skills', Value),
178 ('spell slots', Value),
179 ('spells', Value),
180 ('equipment', Value),
181 ('money', Value),
182 ('features & traits', Value),
183 ]