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