27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204 | class ClassMod (NormalTemplate):
"""
* A template modifier for Class cards introduced in Adventures in the Forgotten Realms.
* Utilizes similar automated positioning techniques as Planeswalker templates.
Adds:
* Level stage groups which contain a cost and level text layer, as well as the divider bar.
* Level line groups which contain the ability text for each level.
* A positioning step to evenly space the abilities and stage dividers.
"""
def __init__(self, layout: ClassLayout, **kwargs):
self._line_layers: list[ArtLayer] = []
self._stage_layers: list[LayerSet] = []
super().__init__(layout, **kwargs)
"""
* Checks
"""
@auto_prop_cached
def is_class_layout(self) -> bool:
"""bool: Checks if this card is a ClassLayout object."""
return isinstance(self.layout, ClassLayout)
"""
* Mixin Methods
"""
@auto_prop_cached
def text_layer_methods(self) -> list[Callable]:
"""Add Class text layers."""
funcs = [self.text_layers_classes] if self.is_class_layout else []
return [*super().text_layer_methods, *funcs]
@auto_prop_cached
def frame_layer_methods(self) -> list[Callable]:
"""Add Class text layers."""
funcs = [self.frame_layers_classes] if self.is_class_layout else []
return [*super().frame_layer_methods, *funcs]
@auto_prop_cached
def post_text_methods(self) -> list[Callable]:
"""Position Class abilities and stage dividers."""
funcs = [self.layer_positioning_classes] if self.is_class_layout else []
return [*super().post_text_methods, *funcs]
"""
* Class Groups
"""
@auto_prop_cached
def class_group(self) -> LayerSet:
return psd.getLayerSet(LAYERS.CLASS)
@auto_prop_cached
def stage_group(self) -> LayerSet:
return psd.getLayerSet(LAYERS.STAGE, self.class_group)
"""
* Class Text Layers
"""
@auto_prop_cached
def text_layer_ability(self) -> ArtLayer:
return psd.getLayer(LAYERS.TEXT, self.class_group)
"""
* Class Abilities
"""
@property
def line_layers(self) -> list[ArtLayer]:
return self._line_layers
@line_layers.setter
def line_layers(self, value):
self._line_layers = value
"""
* Class Stage Dividers
"""
@property
def stage_layers(self) -> list[LayerSet]:
return self._stage_layers
@stage_layers.setter
def stage_layers(self, value):
self._stage_layers = value
"""
* Text Layer Methods
"""
def rules_text_and_pt_layers(self) -> None:
"""Skip this step for Class cards."""
pass
"""
* Class Text Layer Methods
"""
def text_layers_classes(self) -> None:
"""Add and modify text layers relating to Class type cards."""
# Add first static line
self.line_layers.append(self.text_layer_ability)
self.text.append(
FormattedTextField(
layer=self.text_layer_ability,
contents=self.layout.class_lines[0]['text']
))
# Add text fields for each line and class stage
for i, line in enumerate(self.layout.class_lines[1:]):
# Create a new ability line
line_layer = self.text_layer_ability.duplicate()
self.line_layers.append(line_layer)
# Use existing stage divider or create new one
stage = self.stage_group if i == 0 else self.stage_group.duplicate()
cost, level = [*stage.artLayers][:2]
self.stage_layers.append(stage)
# Add text layers to be formatted
self.text.extend([
FormattedTextField(layer=line_layer, contents=line['text']),
FormattedTextField(layer=cost, contents=f"{line['cost']}:"),
TextField(layer=level, contents=f"Level {line['level']}")
])
"""
* Class Frame Layer Methods
"""
def frame_layers_classes(self) -> None:
"""Enable frame layers required by Class cards. None by default."""
pass
"""
* Class Positioning Methods
"""
def layer_positioning_classes(self) -> None:
"""Positions and sizes class ability layers and stage dividers."""
# Core vars
spacing = self.app.scale_by_dpi(80)
spaces = len(self.line_layers) - 1
divider_height = psd.get_layer_height(self.stage_layers[0])
ref_height = self.textbox_reference.dims['height']
spacing_total = (spaces * (spacing + divider_height)) + (spacing * 2)
total_height = ref_height - spacing_total
# Resize text items till they fit in the available space
psd.scale_text_layers_to_height(
text_layers=self.line_layers,
ref_height=total_height)
# Get the exact gap between each layer left over
layer_heights = sum([psd.get_layer_height(lyr) for lyr in self.line_layers])
gap = (ref_height - layer_heights) * (spacing / spacing_total)
inside_gap = (ref_height - layer_heights) * ((spacing + divider_height) / spacing_total)
# Space Class lines evenly apart
psd.spread_layers_over_reference(
layers=self.line_layers,
ref=self.textbox_reference,
gap=gap,
inside_gap=inside_gap)
# Position a class stage between each ability line
psd.position_dividers(
dividers=self.stage_layers,
layers=self.line_layers,
docref=self.docref)
|