Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/gouvernathor/layeredimage2
A new, revamped and incompatible syntax for layeredimages in Ren'Py
https://github.com/gouvernathor/layeredimage2
Last synced: about 1 month ago
JSON representation
A new, revamped and incompatible syntax for layeredimages in Ren'Py
- Host: GitHub
- URL: https://github.com/gouvernathor/layeredimage2
- Owner: Gouvernathor
- Created: 2023-07-04T18:32:31.000Z (over 1 year ago)
- Default Branch: main
- Last Pushed: 2024-03-27T22:22:19.000Z (9 months ago)
- Last Synced: 2024-10-16T02:12:46.594Z (3 months ago)
- Language: Python
- Size: 96.7 KB
- Stars: 0
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.rst
Awesome Lists containing this project
README
This code implements a new syntax for layeredimages.
::
layeredimage2 new_way:
attribute towel variant back
group pose auto prefix pose
group wear if_attr (!pose_all_inclusive):
attribute tux
attribute swimsuit if_attr (!(pose_swindling|pose_knitting|pose_swatting))
attribute swimsuit variant swindling if_attr (pose_swindling)
attribute swimsuit variant knitting if_attr (pose_knitting)
attribute swimsuit variant swatting if_attr (pose_swatting)
attribute towel if_attr (swimsuit|!pose_swindling)Changes in a nutshell
---------------------#. prefix and variant take a name without quotes
#. multiple groups don't take a name - not required anymore, and not even accepted anymore
#. auto multiple groups (groups which are both auto and multiple at the same time) change in behavior slightly by never using the group name at all (unlikely to have any real-life consequence, but the new behavior is more consistent)
#. attributes can take a variant, if they don't have an explicit displayable and if they're not in a group with a variant
#. if_any, if_all and if_not are gone, and replaced with if_attr and a dedicated, shorter syntax which does away with the quotes and allows a greater variety of conditions to be expressed
#. you can add pass to any block (layeredimage, always, group, attribute), except if/elif/else blocks which can't be empty in the first place
#. you can use the ``at transform:`` syntax with an ATL block like in screen language, in any place where you could use the ``at`` property, and you can use the similar ``image:`` syntax with an ATL block in any place requiring a displayable : inside if/elif/else blocks, and inline and in the blocks of always and attribute statementsMotivation and issues with the legacy syntax
--------------------------------------------- The ``prefix`` and ``variant`` properties require quotes, because they can take arbitrary python expressions. But these expressions are evaluated at init time, so there's no real way of making use of that : you would need a value which changes between boots of the game, but not during gameplay ? How does that make sense.
- now, these take a name - more specifically an image name component, which is basically a python identifier but can start with a digit - so they can do away with the quotes, which makes the script more readable
- Non-``auto`` ``multiple`` groups require a name to be passed, but that name is ignored. This is as weird as if some python statement forced you to write a comment and then, by purpose of it being a comment, didn't read it.
- Now, ``multiple`` works by passing it as the group name : you write it ``group multiple ...``, with potentially other properties or a colon afterwards. That group *is* multiple, and it actually has no name.
- Attribute definition in ``multiple`` groups behave differently between the ``auto`` behavior and the auto attribution of displayables to non-auto attributes. Yes, that's a mouthful.
Basically, there is the group-level ``auto``, and there is attribute-level automatic finding of images when you write an attribute without explicitly giving it a displayable. group-``auto`` takes the defined images and generate attributes from them, when attribute-auto takes the attribute name and finds one matching image. Both make use of the layeredimage name and other informations, and have a common trait of searching for a pattern through renpy's defined images.
The trouble is, the patterns used by the two features differ in one specific corner case. In a ``multiple`` group, group-``auto``'s search will use the group's name, whereas the attribute-auto search will not.
For example in the following code::
layeredimage eileen:
group token multiple auto:
attribute blueA potential "red" attribute would be defined by the ``auto`` behavior if it finds an "eileen_token_red" image, and that image would be attached to it.
However, the non-auto "blue" attribute would be matched with the "eileen_blue" image, not "eileen_token_blue".
As a result, if an "eileen_token_blue" image is defined, it would only be used by the layeredimage if ``attribute blue`` was not written in the group. Toggling between the "eileen_blue" and "eileen_token_blue" images for the same "eileen blue" attribute is very weird.
- Now, both auto and non-auto attributes follow the same rules in ``multiple`` groups, since these groups don't have a name anymore.
The former behavior (of auto-defining a bunch of non-mutually-exclusive attributes from images beginning the same way) can now be done using a ``variant``, see below.
The ``auto`` behavior still disregards the format_function passed to the layeredimage, but now at least it works the same way as the default format_function which treats ``multiple`` groups as being nameless.
- There is no way to provide a ``variant`` to a single attribute outside of a group.
For example of when that would be useful, imagine that you have an element (a scarf for example) that is either present or not present but has no incompatibility with anything else (aka : you wouldn't intuitively put it in a group), and which is partly beneath, and partly over one or several other attributes::
layeredimage eileen:
attribute scarf # back part
group arms auto
attribute scarf # front partThese two layers need to be given two different sprites, otherwise the bottom one is pointless.
You could manually provide the images used by the scarf attributes, but I find that tedious and tend to avoid it whenever possible, as it makes the code twice longer and it becomes quickly unreadable. Let's not do that.
You can't name the back part ``attribute scarf_back``, because then the front part could appear without the back part showing, and vice versa. You could solve that with an ``adjust_attribute`` function, but let's avoid resorting to Python if we can.
And if both attributes are named "scarf", they will both be matched to the same image.
A workaround is to use a single-use ``multiple`` group::
layeredimage eileen:
group _ multiple variant "back":
attribute scarf
group arms auto
attribute scarfNow, the "eileen_back_scarf.png" image is matched to the first attribute, and "eileen_scarf.png" to the second one. Both will be displayed when calling ``show eileen scarf``, with the arms in the middle.
But using a multiple group that way makes a weird syntax.
- Now, you can simply write it like this::
layeredimage eileen:
attribute scarf variant back
group arms auto
attribute scarfThe ``variant`` property is however forbidden on attributes inside groups that already have a variant.
It is also forbidden in attributes that are given an explicit displayable (because in that case the variant would have no effect).
- ``if_any``, ``if_all`` and ``if_not`` (communally the ``if_`` properties) are very handy.
However, a combination of these properties only covers conditionals which can be expressed as ``any(...) and all(...) and not any(...)``.
For example, ``(a or b) and (c or d)`` or ``not (a and b)`` cannot be expressed that way.
Furthermore, you could give a condition to a group, and see one attribute not respect that condition. That's because when the same ``if_`` property is given to an attribute and to its group, the attribute's property *replaces* the group's rather than merging with it. That was `issue #3955 `__ on Ren'Py.
- Now, the ``if_attr`` property offers a lot more freedom, avoids having to use quotes for the attributes, and supports attribute-and-group ifs by using an ``and`` operator to combine them (that's because the ``if_`` property makes things under it appear less often, so when you add another ``if_``, you make it appear even less often).
How to convert to the new syntax
--------------------------------Rephrase the ``if_`` properties using the new ``if_attr`` : ``if_any ["a", "b"] if_all ["c", "d"] if_not ["e", "f"]`` -> ``if_attr ((a | b) & c & d & !(e | f))``
Remove quotes from ``variant`` and ``prefix`` clauses : ``variant "a"`` -> ``variant a``. If that doesn't parse, you were doing something bad.
Remove the name in ``multiple`` groups : ``group a multiple`` -> ``group multiple``. If the group is also ``auto``:
- For auto multiple groups without a variant, add the former group name as a variant : ``group a auto multiple`` -> ``group multiple auto variant a``
- For auto multiple groups with a variant, prepend the variant with the former group name with an underscore : ``group a auto multiple variant "b"`` -> ``group multiple auto variant a_b``
- For auto multiple groups with attributes declared inside them, write the line just above on its own, and then add another multiple group with no variant (or with the former variant), with the same properties except that it is not auto, and put the attributes inside. Yes, it's complex, but you were doing something weird anyway::# before
group a auto multiple:
attribute att
group g auto multiple variant "h":
attribute att2
::# after
group multiple auto variant a
group multiple:
attribute att
group multiple auto variant g_h
group multiple variant h:
attribute att2
Yes, it seems weird and repetitive, but it's reflective of what the code was *actually* doing all along. It's the former behavior that was weird, not mine.