1init offset = -100
2
3python early in layeredimage:
4
5    from store import Transform, ConditionSwitch, Fixed, Null, config, Text, eval
6    from collections import OrderedDict
7
8    ATL_PROPERTIES = [ i for i in renpy.atl.PROPERTIES ]
9    ATL_PROPERTIES_SET = set(ATL_PROPERTIES)
10
11    # The properties for attribute layers.
12    LAYER_PROPERTIES = [ "if_all", "if_any", "if_not", "at" ] + ATL_PROPERTIES
13
14    # This is the default value for predict_all given to conditions.
15    predict_all = False
16
17    def format_function(what, name, group, variant, attribute, image, image_format, **kwargs):
18        """
19        :doc: li_ff
20
21        This is called to format the information about an attribute
22        or condition into a displayable. This can be replaced by a
23        creator, but the new function should ignore unknown kwargs.
24
25        `what`
26            A string giving a description of the thing being formatted,
27            which is used to create better error messages.
28
29        `name`
30            The name of the layeredimage.
31
32        `group`
33            The group of an attribute, None if not supplied or if it's
34            part of a condition.
35
36        `variant`
37            The variant argument to the group, or None if it is not
38            supplied.
39
40        `attribute`
41            The attribute itself.
42
43        `image`
44            Either a displayable or string.
45
46        `image_format`
47            The image_format argument of the LayeredImage.
48
49        If `image` is None, then `name`, `group` (if not None), `variant` (if not None),
50        and `attribute` are combined with underscores to create `image`, which
51        will then be a string.
52
53        If `images` is a string, and `image_format` is not None, `image` is formatted
54        into the string to get the final displayable.
55
56        So if `name` is "eileen", `group` is "expression", and
57        `attribute` is "happy", `image` would be set to "eileen_expression_happy".
58        If `image_format` is "images/{image}.png",
59        the final image Ren'Py finds is "images/eileen_expression_happy.png".
60        But note that it would have found the same image without the format
61        argument.
62        """
63
64        if image is None:
65
66            if name is None:
67                raise Exception("Can't find an image name to format {}.".format(what))
68
69            if attribute is None:
70                raise Exception("Can't find an attribute name to format {}.".format(what))
71
72            parts = [ name ]
73
74            if group is not None:
75                parts.append(group)
76
77            if variant is not None:
78                parts.append(variant)
79
80            parts.append(attribute)
81
82            image = "_".join(parts)
83
84        if isinstance(image, basestring) and (image_format is not None):
85            image = image_format.format(name=name, image=image)
86
87        return image
88
89    class Layer(object):
90        """
91        Base class for our layers.
92        """
93
94        group_args = {}
95
96        def __init__(self, if_all=[ ], if_any=[ ], if_not=[ ], at=[ ], group_args={}, **kwargs):
97
98            if not isinstance(at, list):
99                at = [ at ]
100
101            self.at = at
102
103            if not isinstance(if_all, list):
104                if_all = [ if_all ]
105
106            self.if_all = if_all
107
108            if not isinstance(if_any, list):
109                if_any = [ if_any ]
110
111            self.if_any = if_any
112
113            if not isinstance(if_not, list):
114                if_not = [ if_not ]
115
116            self.if_not = if_not
117
118            self.group_args = group_args
119            self.transform_args = kwargs
120
121        def check(self, attributes):
122
123            for i in self.if_all:
124                if i not in attributes:
125                    return False
126
127            if self.if_any:
128
129                for i in self.if_any:
130                    if i in attributes:
131                        break
132                else:
133                    return False
134
135            for i in self.if_not:
136                if i in attributes:
137                    return False
138
139            return True
140
141        def wrap(self, d):
142            """
143            Wraps a displayable in the at list and transform arguments.
144            """
145
146            d = renpy.displayable(d)
147
148            for i in self.at:
149                d = i(d)
150
151            if self.group_args or self.transform_args:
152                d = Transform(d)
153
154                for k, v in self.group_args.items():
155                    setattr(d, k, v)
156
157                for k, v in self.transform_args.items():
158                    setattr(d, k, v)
159
160            return d
161
162
163    class Attribute(Layer):
164        """
165        :doc: li
166        :name: Attribute
167
168        This is used to represent a layer of an LayeredImage that is
169        controlled by an attribute. A single attribute can control
170        multiple layers, in which case all layers corresponding to
171        that attribute will be displayed.
172
173        `group`
174            A string giving the group the attribute is part of. This
175            may be None, in which case a group with the same name as
176            the attribute is created.
177
178        `attribute`
179            A string giving the name of the attribute.
180
181        `image`
182            If not None, this should be a displayable that is displayed when
183            this attribute is shown.
184
185        `default`
186            If True, and no other attribute for the group is selected,
187            this attribute is.
188
189        The following keyword arguments are also known:
190
191        `at`
192            A transform or list of transforms that are applied to the
193            image.
194
195        `if_all`
196            An attribute or list of attributes. The displayable is only shown
197            if all of these are showing.
198
199        `if_any`
200            An attribute or list of attributes. if not empty, the displayable is only shown
201            if any of these are showing.
202
203        `if_not`
204            An attribute or list of attributes. The displayable is only shown
205            if none of these are showing.
206
207        Other keyword arguments are interpreted as transform properties. If
208        any are present, a transform is created that wraps the image. (For
209        example, pos=(100, 200) can be used to offset the image by 100 pixels
210        horizontally and 200 vertically.)
211
212        If the `image` parameter is omitted or None, and the LayeredImage
213        has been given the `image_format` parameter, the image_format is used
214        to generate an image filename.
215        """
216
217        def __init__(self, group, attribute, image=None, default=False, group_args={}, **kwargs):
218
219            prefix = kwargs.pop("prefix", None)
220            variant = kwargs.pop("variant", None)
221
222            super(Attribute, self).__init__(group_args=group_args, **kwargs)
223
224            self.group = group
225
226            self.raw_attribute = attribute
227
228            if prefix is not None:
229                attribute = prefix + "_" + attribute
230
231            self.attribute = attribute
232            self.image = image
233            self.default = default
234            self.variant = variant
235
236        def apply_format(self, ai):
237
238            self.image = self.wrap(ai.format(
239                "Attribute ({!r}, {!r})".format(self.group, self.attribute),
240                group=self.group,
241                variant=self.variant,
242                attribute=self.raw_attribute,
243                image=self.image,
244                ))
245
246        def get_displayable(self, attributes):
247
248            if self.attribute not in attributes:
249                return None
250
251            if not self.check(attributes):
252                return None
253
254            return self.image
255
256
257    class RawAttribute(object):
258
259        def __init__(self, name):
260            self.name = name
261            self.image = None
262            self.properties = OrderedDict()
263
264        def execute(self, group=None, group_properties=None):
265            if group_properties is None:
266                group_properties = {}
267
268            if self.image:
269                image = eval(self.image)
270            else:
271                image = None
272
273            properties = { k : v for k, v in group_properties.items() if k not in ATL_PROPERTIES_SET }
274            group_args = { k : v for k, v in group_properties.items() if k in ATL_PROPERTIES_SET }
275            properties.update({ k : eval(v) for k, v in self.properties.items() })
276
277            return [ Attribute(group, self.name, image, group_args=group_args, **properties) ]
278
279
280    class RawAttributeGroup(object):
281
282        def __init__(self, image_name, group):
283
284            self.image_name = image_name
285            self.group = group
286            self.properties = OrderedDict()
287            self.children = [ ]
288
289        def execute(self):
290
291            properties = { k : eval(v) for k, v in self.properties.items() }
292
293            auto = properties.pop("auto", False)
294            variant = properties.get("variant", None)
295            multiple = properties.pop("multiple", False)
296
297            rv = [ ]
298
299            if multiple:
300                group = None
301            else:
302                group = self.group
303
304            for i in self.children:
305                rv.extend(i.execute(group=group, group_properties=properties))
306
307            if auto:
308                seen = set(i.raw_attribute for i in rv)
309                pattern = self.image_name.replace(" ", "_")  + "_" + self.group + "_"
310
311                if variant:
312                    pattern += variant + "_"
313
314                for i in renpy.list_images():
315
316                    if i.startswith(pattern):
317                        rest = i[len(pattern):]
318                        attrs = rest.split()
319
320                        if len(attrs) == 1:
321                            if attrs[0] not in seen:
322                                rv.append(Attribute(group, attrs[0], renpy.displayable(i), **properties))
323
324            return rv
325
326
327    class Condition(Layer):
328        """
329        :doc: li
330        :name: Condition
331
332        This is used to represent a layer of an LayeredImage that
333        is controlled by a condition. When the condition is true,
334        the layer is displayed. Otherwise, nothing is displayed.
335
336        `condition`
337            This should be a string giving a Python condition that determines
338            if the layer is displayed.
339
340        `image`
341            If not None, this should be a displayable that is displayed when
342            the condition is true.
343
344        `if_all`
345            An attribute or list of attributes. The condition is only evaluated
346            if all of these are showing.
347
348        `if_any`
349            An attribute or list of attributes. If not empty, the condition is only evaluated
350            if any of these are showing.
351
352        `if_not`
353            An attribute or list of attributes. The condition is only evaluated
354            if none of these are showing.
355
356        `at`
357            A transform or list of transforms that are applied to the
358            image.
359
360        Other keyword arguments are interpreted as transform properties. If
361        any are present, a transform is created that wraps the image. (For
362        example, pos=(100, 200) can be used to offset the image by 100 pixels
363        horizontally and 200 vertically.)
364        """
365
366        at = [ ]
367
368        def __init__(self, condition, image, **kwargs):
369            self.condition = condition
370            self.image = image
371
372            super(Condition, self).__init__(**kwargs)
373
374        def apply_format(self, ai):
375
376            self.image = self.wrap(ai.format(
377                "Condition ({})".format(self.condition),
378                group=None,
379                attribute=None,
380                image=self.image,
381                ))
382
383        def get_displayable(self, attributes):
384
385            if not self.check(attributes):
386                return None
387
388            return ConditionSwitch(
389                self.condition, self.image,
390                None, Null(),
391                predict_all=predict_all,
392            )
393
394
395    class RawCondition(object):
396
397        def __init__(self, condition):
398            self.condition = condition
399            self.image = None
400            self.properties = OrderedDict()
401
402        def execute(self):
403            properties = { k : eval(v) for k, v in self.properties.items() }
404            return [ Condition(self.condition, eval(self.image), **properties) ]
405
406
407    class ConditionGroup(Layer):
408        """
409        Combines a list of conditions into a single ConditionSwitch.
410        """
411
412        def __init__(self, conditions):
413            self.conditions = conditions
414
415        def apply_format(self, ai):
416            for i in self.conditions:
417                i.apply_format(ai)
418
419        def get_displayable(self, attributes):
420            args = [ ]
421
422            for i in self.conditions:
423                if not i.check(attributes):
424                    continue
425
426                args.append(i.condition)
427                args.append(i.image)
428
429            args.append(None)
430            args.append(Null())
431
432            return ConditionSwitch(predict_all=predict_all, *args)
433
434    class RawConditionGroup(object):
435
436        def __init__(self):
437            self.conditions = [ ]
438
439        def execute(self):
440
441            l = [ ]
442            for i in self.conditions:
443                l.extend(i.execute())
444
445            return [ ConditionGroup(l) ]
446
447
448    class Always(Layer):
449        """
450        :doc: li
451        :name: Always
452
453        This is used for a displayable that is always shown.
454
455        `image`
456            The displayable to show.
457
458        `default`
459            If True, and no other attribute for the group is selected,
460            this attribute is.
461
462        `at`
463            A transform or list of transforms that are applied to the
464            image.
465
466        `if_all`
467            An attribute or list of attributes. The displayable is only shown
468            if all of these are showing.
469
470        `if_any`
471            An attribute or list of attributes. If not empty, the displayable
472            is only shown if any of these are showing.
473
474        `if_not`
475            An attribute or list of attributes. The displayable is only shown
476            if none of these are showing.
477        """
478
479
480        def __init__(self, image, **kwargs):
481
482            self.image = image
483
484            super(Always, self).__init__(**kwargs)
485
486        def apply_format(self, ai):
487
488            self.image = self.wrap(ai.format(
489                "Always",
490                group=None,
491                attribute=None,
492                image=self.image,
493                ))
494
495        def get_displayable(self, attributes):
496
497            if not self.check(attributes):
498                return None
499
500            return self.image
501
502    class RawAlways(object):
503
504        def __init__(self):
505            self.image = None
506            self.properties = OrderedDict()
507
508        def execute(self):
509
510            if self.image:
511                image = eval(self.image)
512            else:
513                image = None
514
515            properties = { k : eval(v) for k, v in self.properties.items() }
516            return [ Always(image, **properties) ]
517
518    class LayeredImage(object):
519        """
520        :doc: li
521        :name: LayeredImage
522
523        This is an image-like object that, when shown with the proper set of
524        attributes, shows a displayable created by compositing together the
525        displayables associated with those attribute.
526
527        `attributes`
528            This must be a list of Attribute objects. Each Attribute object
529            reflects a displayable that may or may not be displayed as part
530            of the image. The items in this list are in back-to-front order,
531            with the first item further from the viewer and the last
532            closest.
533
534        `at`
535            A transform or list of transforms that are applied to the displayable
536            after it is parameterized.
537
538        `name`
539            The name of the layeredimage. This is used as part of the names
540            of image components.
541
542        `image_format`
543            When a given image is a string, and this is supplied, the image name
544            is interpolated into `image_format` to make an image file. For example,
545            "sprites/eileen/{image}.png" will look for the image in a subdirectory
546            of sprites. (This is not used by auto groups, which look for images and
547            not image files.)
548
549        `format_function`
550            A function that is used instead of `layeredimage.format_function` to format
551            the image information into a displayable.
552
553        `attribute_function`
554            If not None, a function that's called with a set of attributes supplied to
555            the image, and returns the set of attributes used to select layers. This is
556            called when determining the layers to display, after the attribute themselves
557            have been chosen. It can be used to express complex dependencies between attributes
558            or select attributes at random.
559
560        Additional keyword arguments may contain transform properties. If
561        any are present, a transform is created that wraps the result image.
562        Remaining keyword arguments are passed to a Fixed that is created to hold
563        the layer. Unless explicitly overridden, xfit and yfit are set to true on
564        the Fixed, which means it will shrink to the smallest size that fits all
565        of the layer images it is showing.
566
567        A LayeredImage is not a displayable, and can't be used in all the
568        places a displayable can be used. This is because it requires an image
569        name (generally including image attributes) to be provided. As such,
570        it should either be displayed through a scene or show statement, or by
571        an image name string used as a displayable.
572        """
573
574        attribute_function = None
575        transform_args = { }
576
577        def __init__(self, attributes, at=[], name=None, image_format=None, format_function=None, attribute_function=None, **kwargs):
578
579            self.name = name
580            self.image_format = image_format
581            self.format_function = format_function
582            self.attribute_function = attribute_function
583
584            self.attributes = [ ]
585            self.layers = [ ]
586
587            import collections
588            self.attribute_to_groups = collections.defaultdict(set)
589            self.group_to_attributes = collections.defaultdict(set)
590
591            for i in attributes:
592                self.add(i)
593
594            if not isinstance(at, list):
595                at = [ at ]
596
597            self.at = at
598
599            kwargs.setdefault("xfit", True)
600            kwargs.setdefault("yfit", True)
601
602            self.transform_args = {k : kwargs.pop(k) for k, v in list(kwargs.items()) if k not in (renpy.sl2.slproperties.position_property_names + renpy.sl2.slproperties.box_property_names)}
603            self.fixed_args = kwargs
604
605        def format(self, what, attribute=None, group=None, variant=None, image=None):
606
607            ff = format_function
608
609            if self.format_function is not None:
610                ff = self.format_function
611
612            return ff(
613                what=what,
614                name=self.name,
615                group=group,
616                variant=variant,
617                attribute=attribute,
618                image=image,
619                image_format=self.image_format)
620
621        def add(self, a):
622
623            if not isinstance(a, Layer):
624                a = Always(a)
625
626            a.apply_format(self)
627            self.layers.append(a)
628
629            if isinstance(a, Attribute):
630                self.attributes.append(a)
631
632                if a.group is not None:
633                    self.attribute_to_groups[a.attribute].add(a.group)
634                    self.group_to_attributes[a.group].add(a.attribute)
635
636        def get_banned(self, attributes):
637            """
638            Get the set of attributes that are incompatible with those
639            in attributes.
640            """
641
642            rv = set()
643
644            for i in attributes:
645                for g in self.attribute_to_groups[i]:
646                    for j in self.group_to_attributes[g]:
647                        if j != i:
648                            rv.add(j)
649            return rv
650
651        def _duplicate(self, args):
652
653            name = " ".join(args.name + tuple(args.args))
654
655            attributes = set(args.args)
656            unknown = set(args.args)
657            banned = self.get_banned(attributes)
658
659            for a in self.attributes:
660
661                unknown.discard(a.attribute)
662
663                if a.default and (a.attribute not in banned):
664                    attributes.add(a.attribute)
665
666            if self.attribute_function:
667                attributes = set(self.attribute_function(attributes))
668
669                unknown = set([i[1:] if i.startswith('-') else i for i in attributes])
670
671                for a in self.attributes:
672
673                    unknown.discard(a.attribute)
674
675                    if a.variant:
676                        unknown.discard(a.variant)
677
678            rv = Fixed(**self.fixed_args)
679
680            for i in self.layers:
681                d = i.get_displayable(attributes)
682
683                if d is not None:
684
685                    if d._duplicatable:
686                        d = d._duplicate(None)
687
688                    rv.add(d)
689
690            if unknown and args.lint:
691                args = args.copy()
692                args.args = tuple(unknown)
693                args.extraneous()
694
695            if unknown and config.developer:
696
697                message = [" ".join(args.name), "unknown attributes:", " ".join(sorted(unknown))]
698
699                text = Text(
700                    "\n".join(message),
701                    size=16,
702                    xalign=0.5,
703                    yalign=0.5,
704                    text_align=0.5,
705                    color="#fff",
706                    outlines=[ (1, "#0008", 0, 0) ],
707                )
708
709                rv = Fixed(rv, text, fit_first=True)
710
711            for i in self.at:
712                rv = i(rv)
713
714            if self.transform_args:
715                rv = Transform(child=rv, **self.transform_args)
716
717            return rv
718
719        def _list_attributes(self, tag, attributes):
720
721            banned = self.get_banned(attributes)
722
723            group_attr = [ ]
724
725            seen = set()
726
727            group_count = 0
728            old_group = None
729
730            for a in self.attributes:
731
732                if a.group != old_group:
733                    old_group = a.group
734                    group_count += 1
735
736                if a.attribute in banned:
737                    continue
738
739                if a.attribute in seen:
740                    continue
741
742                seen.add(a.attribute)
743                group_attr.append((group_count, a.attribute))
744
745            group_attr.sort()
746
747            return [ i[1] for i in group_attr ]
748
749        def _choose_attributes(self, tag, required, optional):
750
751            rv = list(required)
752
753            required = set(required)
754            banned = self.get_banned(required)
755            both = required & banned
756
757            if both:
758                raise Exception("The attributes for {} conflict: {}".format(tag, " ".join(both)))
759
760            # The set of all available attributes.
761            available_attributes = set(a.attribute for a in self.attributes)
762
763            if optional is not None:
764                optional = set(optional) & available_attributes
765                rv.extend(optional - required - banned)
766
767            # If there is an unknown attribute.
768            if set(rv) - available_attributes:
769                return None
770
771            return tuple(rv)
772
773    class RawLayeredImage(object):
774
775        def __init__(self, name):
776            self.name = name
777            self.children = [ ]
778            self.properties = OrderedDict()
779
780        def execute(self):
781            properties = { k : eval(v) for k, v in self.properties.items() }
782
783
784            l = [ ]
785            for i in self.children:
786                l.extend(i.execute())
787
788            renpy.image(
789                self.name,
790                LayeredImage(l, name=self.name.replace(" ", "_"), **properties),
791            )
792
793    def execute_layeredimage(rai):
794        rai.execute()
795
796
797    def parse_property(l, o, names):
798        """
799        Parses a property, returns True if one is found.
800        """
801
802        checkpoint = l.checkpoint()
803
804        name = l.word()
805
806        if name is None:
807            return False
808
809        if name not in names:
810            l.revert(checkpoint)
811            return False
812
813        if name in o.properties:
814            l.error("Duplicate property " + name)
815
816        if name == "auto" or name == "default" or name == "multiple":
817            expr = "True"
818        elif name == "at":
819            expr = l.require(l.comma_expression)
820        else:
821            expr = l.require(l.simple_expression)
822
823        o.properties[name] = expr
824
825        return True
826
827
828    def parse_attribute(l, parent):
829
830        name = l.require(l.image_name_component)
831
832        a = RawAttribute(name)
833        parent.children.append(a)
834
835        def line(l):
836
837            while True:
838
839                if parse_property(l, a, [ "default" ] + LAYER_PROPERTIES):
840                    continue
841
842                if l.match('null'):
843                    image = "Null()"
844                else:
845                    image = l.simple_expression()
846
847                if image is not None:
848
849                    if a.image is not None:
850                        l.error('An attribute can only have zero or one displayables, two found.')
851
852                    a.image = image
853                    continue
854
855                break
856
857        line(l)
858
859        if not l.match(':'):
860            l.expect_eol()
861            l.expect_noblock('attribute')
862            return
863
864
865        l.expect_block('attribute')
866        l.expect_eol()
867
868        ll = l.subblock_lexer()
869
870        while ll.advance():
871            line(ll)
872            ll.expect_eol()
873            ll.expect_noblock('attribute')
874
875        return
876
877    def parse_always(l, parent):
878
879        a = RawAlways()
880        parent.children.append(a)
881
882        def line(l):
883
884            while True:
885
886                if parse_property(l, a, LAYER_PROPERTIES):
887                    continue
888
889                image = l.simple_expression()
890                if image is not None:
891
892                    if a.image is not None:
893                        l.error('The always statement can only have one displayable, two found.')
894
895                    a.image = image
896                    continue
897
898                break
899
900
901        line(l)
902
903        if not l.match(':'):
904            l.expect_eol()
905            l.expect_noblock('attribute')
906            return
907
908        l.expect_block('attribute')
909        l.expect_eol()
910
911        ll = l.subblock_lexer()
912
913        while ll.advance():
914            line(ll)
915            ll.expect_eol()
916            ll.expect_noblock('attribute')
917
918        if a.image is None:
919            l.error("The always statement must have a displayable.")
920
921        return
922
923
924    def parse_group(l, parent, image_name):
925
926        group = l.require(l.image_name_component)
927
928        rv = RawAttributeGroup(image_name, group)
929        parent.children.append(rv)
930
931        while parse_property(l, rv, [ "auto", "prefix", "variant", "multiple" ] + LAYER_PROPERTIES):
932            pass
933
934        if l.match(':'):
935
936            l.expect_eol()
937            l.expect_block("group")
938
939            ll = l.subblock_lexer()
940
941            while ll.advance():
942                if ll.keyword("attribute"):
943                    parse_attribute(ll, rv)
944                    continue
945
946                while parse_property(ll, rv, [ "auto" ] + LAYER_PROPERTIES):
947                    pass
948
949                ll.expect_eol()
950                ll.expect_noblock('group property')
951
952        else:
953            l.expect_eol()
954            l.expect_noblock("group")
955
956    def parse_condition(l, need_expr):
957
958        l.skip_whitespace()
959
960        if need_expr:
961            condition = l.delimited_python(':')
962        else:
963            condition = None
964
965        l.require(':')
966
967        l.expect_block('condition')
968        l.expect_eol()
969
970        ll = l.subblock_lexer()
971
972        rv = RawCondition(condition)
973
974        while ll.advance():
975
976
977            while True:
978
979                if parse_property(ll, rv, LAYER_PROPERTIES):
980                    continue
981
982                image = ll.simple_expression()
983
984                if image is not None:
985
986                    if rv.image is not None:
987                        ll.error('A condition can only have one displayable, two found.')
988
989                    rv.image = image
990                    continue
991
992                break
993
994            ll.expect_noblock("condition properties")
995            ll.expect_eol()
996
997
998        if rv.image is None:
999            l.error("A condition must have a displayable.")
1000
1001        return rv
1002
1003
1004    def parse_conditions(l, parent):
1005
1006        cg = RawConditionGroup()
1007
1008        cg.conditions.append(parse_condition(l, True))
1009        l.advance()
1010
1011        while l.keyword('elif'):
1012
1013            cg.conditions.append(parse_condition(l, True))
1014            l.advance()
1015
1016        if l.keyword('else'):
1017
1018            cg.conditions.append(parse_condition(l, False))
1019            l.advance()
1020
1021        parent.children.append(cg)
1022
1023
1024    def parse_layeredimage(l):
1025
1026        name = [ l.require(l.image_name_component) ]
1027
1028        while True:
1029            part = l.image_name_component()
1030
1031            if part is None:
1032                break
1033
1034            name.append(part)
1035
1036        l.require(':')
1037        l.expect_block("layeredimage")
1038
1039        ll = l.subblock_lexer()
1040        ll.advance()
1041
1042        name = " ".join(name)
1043        rv = RawLayeredImage(name)
1044
1045        while not ll.eob:
1046
1047            if ll.keyword('attribute'):
1048
1049                parse_attribute(ll, rv)
1050                ll.advance()
1051
1052            elif ll.keyword('group'):
1053
1054                parse_group(ll, rv, name)
1055                ll.advance()
1056
1057            elif ll.keyword('if'):
1058
1059                parse_conditions(ll, rv)
1060                # Advances for us.
1061
1062            elif ll.keyword('always'):
1063                parse_always(ll, rv)
1064                ll.advance()
1065
1066            else:
1067
1068                while parse_property(ll, rv, [ "image_format", "format_function", "attribute_function", "at" ] +
1069                    renpy.sl2.slproperties.position_property_names +
1070                    renpy.sl2.slproperties.box_property_names +
1071                    ATL_PROPERTIES
1072                    ):
1073                    pass
1074
1075                ll.expect_noblock('statement')
1076                ll.expect_eol()
1077                ll.advance()
1078
1079        return rv
1080
1081    renpy.register_statement("layeredimage", parse=parse_layeredimage, execute=execute_layeredimage, init=True, block=True)
1082
1083
1084    class LayeredImageProxy(object):
1085        """
1086        :doc: li_proxy
1087        :name: LayeredImageProxy
1088
1089        This is an image-like object that proxies attributes passed to it to
1090        another layered image.
1091
1092        `name`
1093            A string giving the name of the layered image to proxy to.
1094
1095        `transform`
1096            If given, a transform or list of transforms that are applied to the
1097            image after it has been proxied.
1098        """
1099
1100        def __init__(self, name, transform=None):
1101
1102            self.name = name
1103
1104            if "[" not in self.name:
1105                if renpy.get_registered_image(name) is None:
1106                        raise Exception("{!r} is not a registered image name.".format(self.name))
1107
1108            if transform is None:
1109                self.transform = [ ]
1110
1111            elif isinstance(transform, list):
1112                self.transform = transform
1113
1114            else:
1115                self.transform = [ transform ]
1116
1117        @property
1118        def image(self):
1119
1120            name = self.name
1121
1122            if "[" in name:
1123                name = renpy.substitute(name, translate=False)
1124
1125            image = renpy.get_registered_image(name)
1126
1127            if image is None:
1128                raise Exception("{!r} is not a registered image name, in LayeredImageProxy.".format(name))
1129
1130            return image
1131
1132        def _duplicate(self, args):
1133
1134            rv = self.image._duplicate(args)
1135
1136            for i in self.transform:
1137                rv = i(rv)
1138
1139            return rv
1140
1141        def filter_attributes(self, attributes):
1142
1143            if attributes is None:
1144                return None
1145
1146            name = self.name
1147
1148            if "[" in name:
1149                name = renpy.substitute(name, translate=False)
1150
1151            name = name.split()
1152
1153            return tuple(i for i in attributes if i not in name[1:])
1154
1155        def _choose_attributes(self, tag, attributes, optional):
1156            return self.filter_attributes(self.image._choose_attributes(tag, attributes, optional))
1157
1158        def _list_attributes(self, tag, attributes):
1159            return self.filter_attributes(self.image._list_attributes(tag, attributes))
1160
1161    renpy.store.Attribute = Attribute
1162    renpy.store.LayeredImage = LayeredImage
1163    renpy.store.LayeredImageProxy = LayeredImageProxy
1164    renpy.store.Condition = Condition
1165    renpy.store.ConditionGroup = ConditionGroup
1166