Skip to content

PlaneswalkerMod

src.templates.planeswalker.PlaneswalkerMod

Bases: FullartMod, StarterTemplate

A modifier class which adds methods required for Planeswalker type cards introduced in Lorwyn block.

Adds
  • Planeswalker text layers.
  • Planeswalker text layer positioning.
  • Planeswalker ability mask generation.
Source code in src\templates\planeswalker.py
 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
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
class PlaneswalkerMod (FullartMod, StarterTemplate):
    """A modifier class which adds methods required for Planeswalker type cards introduced in Lorwyn block.

    Adds:
        * Planeswalker text layers.
        * Planeswalker text layer positioning.
        * Planeswalker ability mask generation.
    """
    frame_suffix = 'Normal'

    def __init__(self, layout: PlaneswalkerLayouts, **kwargs):
        super().__init__(layout, **kwargs)

        # Settable Properties
        self._ability_layers = []
        self._icons = []
        self._colons = []

    @auto_prop_cached
    def text_layer_methods(self) -> list[Callable]:
        """Add and position Planeswalker text layers, add ability mask."""
        return [*super().text_layer_methods, self.pw_text_layers]

    @auto_prop_cached
    def post_text_methods(self) -> list[Callable]:
        """Add Planeswalker layer positioning and ability mask step."""
        return [
            *super().post_text_methods,
            self.pw_layer_positioning,
            self.pw_ability_mask
        ]

    """
    * Frame Details
    """

    @auto_prop_cached
    def art_frame_vertical(self):
        """Use special Borderless frame for 'Colorless' cards."""
        if self.is_colorless:
            return LAYERS.BORDERLESS_FRAME
        return LAYERS.FULL_ART_FRAME

    """
    * Planeswalker Details
    """

    @auto_prop_cached
    def abilities(self) -> list[dict]:
        """List of Planeswalker abilities data."""
        return self.layout.pw_abilities

    @auto_prop_cached
    def fill_color(self):
        """Ragged lines mask fill color."""
        return self.RGB_BLACK

    """
    * Planeswalker Layers
    """

    @property
    def ability_layers(self) -> list[ArtLayer]:
        return self._ability_layers

    @ability_layers.setter
    def ability_layers(self, value):
        self._ability_layers = value

    @property
    def colons(self) -> list:
        return self._colons

    @colons.setter
    def colons(self, value):
        self._colons = value

    @property
    def icons(self) -> list:
        return self._icons

    @icons.setter
    def icons(self, value):
        self._icons = value

    """
    * Groups
    """

    @auto_prop_cached
    def group(self) -> LayerSet:
        """The main Planeswalker layer group, sized according to number of abilities."""
        if group := psd.getLayerSet(f"pw-{str(self.layout.pw_size)}"):
            group.visible = True
            return group

    @auto_prop_cached
    def loyalty_group(self) -> LayerSet:
        """Group containing Planeswalker loyalty graphics."""
        return psd.getLayerSet(LAYERS.LOYALTY_GRAPHICS)

    @auto_prop_cached
    def border_group(self) -> Optional[LayerSet]:
        """Border group, nested in the appropriate Planeswalker group."""
        return psd.getLayerSet(LAYERS.BORDER, self.group)

    @auto_prop_cached
    def mask_group(self) -> Optional[LayerSet]:
        """Group containing the vector shapes used to create the ragged lines divider mask."""
        return psd.getLayerSet(LAYERS.MASKS)

    @auto_prop_cached
    def textbox_group(self) -> Optional[LayerSet]:
        """Group to populate with ragged lines divider mask."""
        return psd.getLayerSet("Ragged Lines", [self.group, LAYERS.TEXTBOX, "Ability Dividers"])

    @auto_prop_cached
    def text_group(self) -> LayerSet:
        """Text Layer group, nexted in the appropriate Planeswalker group."""
        return psd.getLayerSet(LAYERS.TEXT_AND_ICONS, self.group)

    """
    * Frame Layers
    """

    @auto_prop_cached
    def twins_layer(self) -> Optional[ArtLayer]:
        return psd.getLayer(self.twins, psd.getLayerSet(LAYERS.TWINS, self.group))

    @auto_prop_cached
    def pinlines_layer(self) -> Optional[ArtLayer]:
        return psd.getLayer(self.pinlines, psd.getLayerSet(LAYERS.PINLINES, self.group))

    @auto_prop_cached
    def background_layer(self) -> Optional[ArtLayer]:
        return psd.getLayer(self.background, psd.getLayerSet(LAYERS.BACKGROUND, self.group))

    @auto_prop_cached
    def color_indicator_layer(self) -> Optional[ArtLayer]:
        return psd.getLayer(self.pinlines, [self.group, LAYERS.COLOR_INDICATOR])

    """
    * Text Layers
    """

    @auto_prop_cached
    def text_layer_loyalty(self) -> ArtLayer:
        return psd.getLayer(LAYERS.TEXT, [self.loyalty_group, LAYERS.STARTING_LOYALTY])

    @auto_prop_cached
    def text_layer_ability(self) -> ArtLayer:
        return psd.getLayer(LAYERS.ABILITY_TEXT, self.loyalty_group)

    @auto_prop_cached
    def text_layer_static(self) -> ArtLayer:
        return psd.getLayer(LAYERS.STATIC_TEXT, self.loyalty_group)

    @auto_prop_cached
    def text_layer_colon(self) -> ArtLayer:
        return psd.getLayer(LAYERS.COLON, self.loyalty_group)

    """
    * References
    """

    @auto_prop_cached
    def loyalty_reference(self) -> ReferenceLayer:
        """ArtLayer: Reference used to check ability layer collision with the loyalty box."""
        return psd.get_reference_layer(LAYERS.LOYALTY_REFERENCE, self.loyalty_group)

    """
    * Methods
    """

    def enable_frame_layers(self):

        # Twins
        if self.twins_layer:
            self.twins_layer.visible = True

        # Pinlines
        if self.pinlines_layer:
            self.pinlines_layer.visible = True

        # Background
        if self.background_layer:
            self.background_layer.visible = True

        # Color Indicator
        if self.is_type_shifted and self.color_indicator_layer:
            self.color_indicator_layer.visible = True

    """
    * Planeswalker Methods
    """

    def pw_text_layers(self) -> None:
        """Add and modify text layers required by Planeswalker cards."""

        # Iterate through abilities to add text layers
        for ability in self.abilities:
            self.pw_add_ability(ability)

        # Starting loyalty
        if self.layout.loyalty:
            self.text_layer_loyalty.textItem.contents = self.layout.loyalty
        else:
            self.text_layer_loyalty.parent.visible = False

    def pw_layer_positioning(self) -> None:
        """Position Planeswalker ability layers and icons."""

        # Auto-position the ability text, colons, and shields.
        spacing = self.app.scale_by_dpi(64)
        spaces = len(self.ability_layers) + 1
        total_height = self.textbox_reference.dims['height'] - (spacing * spaces)

        # Resize text items till they fit in the available space
        font_size = scale_text_layers_to_height(
            text_layers=self.ability_layers,
            ref_height=total_height)

        # Space abilities evenly apart
        uniform_gap = True if len(self.ability_layers) < 3 or not self.layout.loyalty else False
        psd.spread_layers_over_reference(
            layers=self.ability_layers,
            ref=self.textbox_reference,
            gap=spacing if not uniform_gap else None)

        # Adjust text to avoid loyalty badge
        if self.layout.loyalty and self.loyalty_reference:
            psd.clear_reference_vertical_multi(
                text_layers=self.ability_layers,
                ref=self.textbox_reference,
                loyalty_ref=self.loyalty_reference,
                space=spacing,
                uniform_gap=uniform_gap,
                font_size=font_size,
                docref=self.docref,
                docsel=self.doc_selection)

        # Align colons and shields to respective text layers
        for i, ref_layer in enumerate(self.ability_layers):
            # Break if we encounter a length mismatch
            if len(self.icons) < (i + 1) or len(self.colons) < (i + 1):
                self.raise_warning("Encountered bizarre Planeswalker data!")
                break
            # Skip if this is a static ability
            if self.icons[i] and self.colons[i]:
                before = self.colons[i].bounds[1]
                psd.align_vertical(self.colons[i], ref_layer)
                difference = self.colons[i].bounds[1] - before
                self.icons[i].translate(0, difference)

    def pw_ability_mask(self) -> None:
        """Position the ragged edge ability mask."""

        # Ragged line layers
        line_top = psd.getLayer(LAYERS.TOP, self.mask_group)
        line_bottom = psd.getLayer(LAYERS.BOTTOM, self.mask_group)

        # Create our line mask pairs
        lines: list[list[ArtLayer]] = []
        for i in range(len(self.ability_layers)-1):
            if lines and len(lines[-1]) == 1:
                lines[-1].append(line_bottom.duplicate(self.textbox_group, ElementPlacement.PlaceInside))
            else:
                lines.append([line_top.duplicate(self.textbox_group, ElementPlacement.PlaceInside)])

        # Position and fill each pair
        n = 0
        for i, group in enumerate(lines):
            # Position the top line, bottom if provided, then fill the area between
            self.position_divider([self.ability_layers[n], self.ability_layers[n+1]], group[0])
            if len(group) == 2:
                self.position_divider([self.ability_layers[n+1], self.ability_layers[n+2]], group[1])
            self.fill_between_dividers(group)
            # Skip every other ability
            n += 2

    """
    * Utility Methods
    """

    def pw_add_ability(self, ability: dict) -> None:
        """Add a Planeswalker ability.

        Args:
            ability: Planeswalker ability data.
        """
        # Create an icon and colon if this isn't a static ability
        static = False if ability.get('icon') and ability.get('cost') else True
        icon = None if static else psd.getLayerSet(ability.get('icon', '0'), self.loyalty_group)
        colon = None if static else self.text_layer_colon.duplicate()

        # Update ability cost if needed
        if not static:
            psd.getLayer(LAYERS.COST, icon).textItem.contents = ability.get('cost', '0')
            icon = icon.duplicate(*[self.icons[-1], ElementPlacement.PlaceBefore]) if (
                self.icons and self.icons[-1]
            ) else icon.duplicate()

        # Add ability, icons, and colons
        self.icons.append(icon)
        self.colons.append(colon)
        self.ability_layers.append(
            self.text_layer_static.duplicate() if static
            else self.text_layer_ability.duplicate())
        self.text.append(
            text_classes.FormattedTextField(
                layer=self.ability_layers[-1],
                contents=ability.get('text', '')
            ))

    def fill_between_dividers(self, group: list[ArtLayer]) -> None:
        """Fill area between two ragged lines, or a top line and the bottom of the document.

        Args:
            group: List containing 1 or 2 ragged lines to fill between.
        """
        # If no second line is provided use the bottom of the document
        bottom_bound: int = (group[1].bounds[1] if len(group) == 2 else self.docref.height) + 1
        top_bound = group[0].bounds

        # Create a new layer to fill the selection
        self.active_layer = self.docref.artLayers.add()
        self.active_layer.move(group[0], ElementPlacement.PlaceAfter)

        # Select between the two points and fill
        self.doc_selection.select([
            [top_bound[0] - 200, top_bound[3] - 1],
            [top_bound[2] + 200, top_bound[3] - 1],
            [top_bound[2] + 200, bottom_bound],
            [top_bound[0] - 200, bottom_bound]
        ])
        self.doc_selection.fill(self.fill_color, ColorBlendMode.NormalBlendColor, 100)
        self.doc_selection.deselect()

    @staticmethod
    def position_divider(layers: list[ArtLayer], line: ArtLayer) -> None:
        """Positions a ragged divider line for an ability text mask.

        Args:
            layers: Two layers to position the line between.
            line: Line layer to be positioned.
        """
        delta = (layers[1].bounds[1] - layers[0].bounds[3]) / 2
        reference_position = (line.bounds[3] + line.bounds[1]) / 2
        target_position = delta + layers[0].bounds[3]
        line.translate(0, (target_position - reference_position))

Functions

abilities() -> list[dict]

List of Planeswalker abilities data.

Source code in src\templates\planeswalker.py
77
78
79
80
@auto_prop_cached
def abilities(self) -> list[dict]:
    """List of Planeswalker abilities data."""
    return self.layout.pw_abilities

art_frame_vertical()

Use special Borderless frame for 'Colorless' cards.

Source code in src\templates\planeswalker.py
66
67
68
69
70
71
@auto_prop_cached
def art_frame_vertical(self):
    """Use special Borderless frame for 'Colorless' cards."""
    if self.is_colorless:
        return LAYERS.BORDERLESS_FRAME
    return LAYERS.FULL_ART_FRAME

border_group() -> Optional[LayerSet]

Border group, nested in the appropriate Planeswalker group.

Source code in src\templates\planeswalker.py
131
132
133
134
@auto_prop_cached
def border_group(self) -> Optional[LayerSet]:
    """Border group, nested in the appropriate Planeswalker group."""
    return psd.getLayerSet(LAYERS.BORDER, self.group)

fill_between_dividers(group: list[ArtLayer]) -> None

Fill area between two ragged lines, or a top line and the bottom of the document.

Parameters:

Name Type Description Default
group list[ArtLayer]

List containing 1 or 2 ragged lines to fill between.

required
Source code in src\templates\planeswalker.py
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
def fill_between_dividers(self, group: list[ArtLayer]) -> None:
    """Fill area between two ragged lines, or a top line and the bottom of the document.

    Args:
        group: List containing 1 or 2 ragged lines to fill between.
    """
    # If no second line is provided use the bottom of the document
    bottom_bound: int = (group[1].bounds[1] if len(group) == 2 else self.docref.height) + 1
    top_bound = group[0].bounds

    # Create a new layer to fill the selection
    self.active_layer = self.docref.artLayers.add()
    self.active_layer.move(group[0], ElementPlacement.PlaceAfter)

    # Select between the two points and fill
    self.doc_selection.select([
        [top_bound[0] - 200, top_bound[3] - 1],
        [top_bound[2] + 200, top_bound[3] - 1],
        [top_bound[2] + 200, bottom_bound],
        [top_bound[0] - 200, bottom_bound]
    ])
    self.doc_selection.fill(self.fill_color, ColorBlendMode.NormalBlendColor, 100)
    self.doc_selection.deselect()

fill_color()

Ragged lines mask fill color.

Source code in src\templates\planeswalker.py
82
83
84
85
@auto_prop_cached
def fill_color(self):
    """Ragged lines mask fill color."""
    return self.RGB_BLACK

group() -> LayerSet

The main Planeswalker layer group, sized according to number of abilities.

Source code in src\templates\planeswalker.py
119
120
121
122
123
124
@auto_prop_cached
def group(self) -> LayerSet:
    """The main Planeswalker layer group, sized according to number of abilities."""
    if group := psd.getLayerSet(f"pw-{str(self.layout.pw_size)}"):
        group.visible = True
        return group

loyalty_group() -> LayerSet

Group containing Planeswalker loyalty graphics.

Source code in src\templates\planeswalker.py
126
127
128
129
@auto_prop_cached
def loyalty_group(self) -> LayerSet:
    """Group containing Planeswalker loyalty graphics."""
    return psd.getLayerSet(LAYERS.LOYALTY_GRAPHICS)

loyalty_reference() -> ReferenceLayer

Source code in src\templates\planeswalker.py
195
196
197
198
@auto_prop_cached
def loyalty_reference(self) -> ReferenceLayer:
    """ArtLayer: Reference used to check ability layer collision with the loyalty box."""
    return psd.get_reference_layer(LAYERS.LOYALTY_REFERENCE, self.loyalty_group)

mask_group() -> Optional[LayerSet]

Group containing the vector shapes used to create the ragged lines divider mask.

Source code in src\templates\planeswalker.py
136
137
138
139
@auto_prop_cached
def mask_group(self) -> Optional[LayerSet]:
    """Group containing the vector shapes used to create the ragged lines divider mask."""
    return psd.getLayerSet(LAYERS.MASKS)

position_divider(layers: list[ArtLayer], line: ArtLayer) -> None

Positions a ragged divider line for an ability text mask.

Parameters:

Name Type Description Default
layers list[ArtLayer]

Two layers to position the line between.

required
line ArtLayer

Line layer to be positioned.

required
Source code in src\templates\planeswalker.py
368
369
370
371
372
373
374
375
376
377
378
379
@staticmethod
def position_divider(layers: list[ArtLayer], line: ArtLayer) -> None:
    """Positions a ragged divider line for an ability text mask.

    Args:
        layers: Two layers to position the line between.
        line: Line layer to be positioned.
    """
    delta = (layers[1].bounds[1] - layers[0].bounds[3]) / 2
    reference_position = (line.bounds[3] + line.bounds[1]) / 2
    target_position = delta + layers[0].bounds[3]
    line.translate(0, (target_position - reference_position))

post_text_methods() -> list[Callable]

Add Planeswalker layer positioning and ability mask step.

Source code in src\templates\planeswalker.py
53
54
55
56
57
58
59
60
@auto_prop_cached
def post_text_methods(self) -> list[Callable]:
    """Add Planeswalker layer positioning and ability mask step."""
    return [
        *super().post_text_methods,
        self.pw_layer_positioning,
        self.pw_ability_mask
    ]

pw_ability_mask() -> None

Position the ragged edge ability mask.

Source code in src\templates\planeswalker.py
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
def pw_ability_mask(self) -> None:
    """Position the ragged edge ability mask."""

    # Ragged line layers
    line_top = psd.getLayer(LAYERS.TOP, self.mask_group)
    line_bottom = psd.getLayer(LAYERS.BOTTOM, self.mask_group)

    # Create our line mask pairs
    lines: list[list[ArtLayer]] = []
    for i in range(len(self.ability_layers)-1):
        if lines and len(lines[-1]) == 1:
            lines[-1].append(line_bottom.duplicate(self.textbox_group, ElementPlacement.PlaceInside))
        else:
            lines.append([line_top.duplicate(self.textbox_group, ElementPlacement.PlaceInside)])

    # Position and fill each pair
    n = 0
    for i, group in enumerate(lines):
        # Position the top line, bottom if provided, then fill the area between
        self.position_divider([self.ability_layers[n], self.ability_layers[n+1]], group[0])
        if len(group) == 2:
            self.position_divider([self.ability_layers[n+1], self.ability_layers[n+2]], group[1])
        self.fill_between_dividers(group)
        # Skip every other ability
        n += 2

pw_add_ability(ability: dict) -> None

Add a Planeswalker ability.

Parameters:

Name Type Description Default
ability dict

Planeswalker ability data.

required
Source code in src\templates\planeswalker.py
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
def pw_add_ability(self, ability: dict) -> None:
    """Add a Planeswalker ability.

    Args:
        ability: Planeswalker ability data.
    """
    # Create an icon and colon if this isn't a static ability
    static = False if ability.get('icon') and ability.get('cost') else True
    icon = None if static else psd.getLayerSet(ability.get('icon', '0'), self.loyalty_group)
    colon = None if static else self.text_layer_colon.duplicate()

    # Update ability cost if needed
    if not static:
        psd.getLayer(LAYERS.COST, icon).textItem.contents = ability.get('cost', '0')
        icon = icon.duplicate(*[self.icons[-1], ElementPlacement.PlaceBefore]) if (
            self.icons and self.icons[-1]
        ) else icon.duplicate()

    # Add ability, icons, and colons
    self.icons.append(icon)
    self.colons.append(colon)
    self.ability_layers.append(
        self.text_layer_static.duplicate() if static
        else self.text_layer_ability.duplicate())
    self.text.append(
        text_classes.FormattedTextField(
            layer=self.ability_layers[-1],
            contents=ability.get('text', '')
        ))

pw_layer_positioning() -> None

Position Planeswalker ability layers and icons.

Source code in src\templates\planeswalker.py
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
def pw_layer_positioning(self) -> None:
    """Position Planeswalker ability layers and icons."""

    # Auto-position the ability text, colons, and shields.
    spacing = self.app.scale_by_dpi(64)
    spaces = len(self.ability_layers) + 1
    total_height = self.textbox_reference.dims['height'] - (spacing * spaces)

    # Resize text items till they fit in the available space
    font_size = scale_text_layers_to_height(
        text_layers=self.ability_layers,
        ref_height=total_height)

    # Space abilities evenly apart
    uniform_gap = True if len(self.ability_layers) < 3 or not self.layout.loyalty else False
    psd.spread_layers_over_reference(
        layers=self.ability_layers,
        ref=self.textbox_reference,
        gap=spacing if not uniform_gap else None)

    # Adjust text to avoid loyalty badge
    if self.layout.loyalty and self.loyalty_reference:
        psd.clear_reference_vertical_multi(
            text_layers=self.ability_layers,
            ref=self.textbox_reference,
            loyalty_ref=self.loyalty_reference,
            space=spacing,
            uniform_gap=uniform_gap,
            font_size=font_size,
            docref=self.docref,
            docsel=self.doc_selection)

    # Align colons and shields to respective text layers
    for i, ref_layer in enumerate(self.ability_layers):
        # Break if we encounter a length mismatch
        if len(self.icons) < (i + 1) or len(self.colons) < (i + 1):
            self.raise_warning("Encountered bizarre Planeswalker data!")
            break
        # Skip if this is a static ability
        if self.icons[i] and self.colons[i]:
            before = self.colons[i].bounds[1]
            psd.align_vertical(self.colons[i], ref_layer)
            difference = self.colons[i].bounds[1] - before
            self.icons[i].translate(0, difference)

pw_text_layers() -> None

Add and modify text layers required by Planeswalker cards.

Source code in src\templates\planeswalker.py
226
227
228
229
230
231
232
233
234
235
236
237
def pw_text_layers(self) -> None:
    """Add and modify text layers required by Planeswalker cards."""

    # Iterate through abilities to add text layers
    for ability in self.abilities:
        self.pw_add_ability(ability)

    # Starting loyalty
    if self.layout.loyalty:
        self.text_layer_loyalty.textItem.contents = self.layout.loyalty
    else:
        self.text_layer_loyalty.parent.visible = False

text_group() -> LayerSet

Text Layer group, nexted in the appropriate Planeswalker group.

Source code in src\templates\planeswalker.py
146
147
148
149
@auto_prop_cached
def text_group(self) -> LayerSet:
    """Text Layer group, nexted in the appropriate Planeswalker group."""
    return psd.getLayerSet(LAYERS.TEXT_AND_ICONS, self.group)

text_layer_methods() -> list[Callable]

Add and position Planeswalker text layers, add ability mask.

Source code in src\templates\planeswalker.py
48
49
50
51
@auto_prop_cached
def text_layer_methods(self) -> list[Callable]:
    """Add and position Planeswalker text layers, add ability mask."""
    return [*super().text_layer_methods, self.pw_text_layers]

textbox_group() -> Optional[LayerSet]

Group to populate with ragged lines divider mask.

Source code in src\templates\planeswalker.py
141
142
143
144
@auto_prop_cached
def textbox_group(self) -> Optional[LayerSet]:
    """Group to populate with ragged lines divider mask."""
    return psd.getLayerSet("Ragged Lines", [self.group, LAYERS.TEXTBOX, "Ability Dividers"])