Layeredimage, tags and character show

AmazonessKing

Amazoness Entrepreneur
Aug 13, 2019
1,898
2,902
I'm having a problem here. I want a single character to be shown as soon as they talk. Problem is that doing this takes me a lot of "Show/Hide" to the point of being ridiculous. I'm trying a lot of things, but using layeredimages is complicating things to me. "For example, I need to define the image twice for it to work, once "normal" and once "side image", which takes extra data, not much, but it does. Secondly, I can't show two side images at the same time, which yes, is kind of the point of using them, but I want one to show on the left, and one on the right, while the right one constantly change.

I changed the side image variant in the screens.rpy to show the MC, in this case char3
Python:
    if not renpy.variant("small"):
        if who == "Player":
            add SideImage() yalign 1.0 xalign 0.0
        else:
            add SideImage() yalign 1.0 xalign 1.0
And here an example
Python:
label start:
    c1 old neutral "Hey Player"
    p "Hey, my face in on the left bottom."
    c1 "Yeah, but I dissappeared and I still have to input all the attributes."
    c2 old neutral "I do appear correctly."
    p "But she dissappears again if I talk."

define p = Character("Player", image="player")
define c1 = Character("char1", image="char1")
define c2 = Character("char2", image="char2")

layeredimage side player:
    always:
        "player"
    group face:
        attribute angry
        attribute happy
        attribute lewd
        attribute neutral default
        attribute sad

layeredimage char1:
    always:
        "char1_base"
    group outfit:
        attribute old
    group face:
        attribute angry
        attribute happy
        attribute lewd
        attribute neutral default
        attribute sad

layeredimage side char1:
    always:
        "char1_base"
    group outfit:
        attribute old
    group face:
        attribute angry
        attribute happy
        attribute lewd
        attribute neutral default
        attribute sad

layeredimage char2:
    always:
        "char2_base"
    group outfit:
        attribute old
    group face:
        attribute angry
        attribute happy
        attribute lewd
        attribute neutral default
        attribute sad

layeredimage side char2:
    always:
        "char2_base"
    group outfit:
        attribute old
    group face:
        attribute angry
        attribute happy
        attribute lewd
        attribute neutral default
        attribute sad

return
What I want is to add for the image on the right to not disappear if I show the image on the left. As I said, layered images with extra tags don't work correctly, I guess I can try to hard define them with blocks instead of leaving it to ren'py, but it would still disappear because of the same tag. I was thinking on adding a custom tag to the images (Because I don't mind if they are or are not side images, I only want the player image to be a side image), but no idea on how to make a custom tag while defining layeredimage.
 

ishigreensa

New Member
Jul 6, 2019
9
1
If you don't need the other image to be a side image:
try this?

screen say(who, what):
style_prefix "say"

window:
id "window"

if who is not None:

window:
id "namebox"
style "namebox"
text who id "who"

text what id "what"


## If there's a side image, display it above the text. Do not display on the
## phone variant - there's no room.
if not renpy.variant("small"):
add SideImage() xalign 0.0 yalign 1.0 ## This is where your side image was added....
add "images/Sideimages/[Nonspeaker]_[prop]_[prop].png":
## You will have to have nonspeaker as a variable of who is being talked to. you will also need ## variables for the two prop parts if you want to be able to have different posings.##
xalign 1.0 yalign 1.0



Then in init:
init:
$ nonspeaker = "Joe B "
$ propa = "standing"
$ propb = "smiling"

make sure you have the image Joe_standing_smiling.png in the image folder and under that, in the Sideimages folder?
 

ishigreensa

New Member
Jul 6, 2019
9
1
The underlined part is what I add to the say screen. I'm not sure if it will work, but I think it will?
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Respected User
Donor
Jun 10, 2017
10,094
14,737
Code:
layeredimage side player:
    always:
        "player"
    group face:
        attribute angry
        attribute happy
        attribute lewd
        attribute neutral default
        attribute sad
Are your images correctly named ? They should be named "side_player_face_angry", "side_player_face_happy", etc.

Else you need to implicitly give the name associated to the attributes :
Code:
layeredimage side player:
    always:
        "player"
    group face:
        attribute angry
            "player_angry"
        attribute happy
            "player_happy"
[...]
Also, do you really need the always image ? It's an image that will always be displayed as background for the layered image, then the different groups will be displayed on top of it.

This being said, the side image already have its own tag mechanism and don't really need the use of a layered image ; at least not when it's a plain image.


As for your problem, basically the solution looks like :
Python:
init python:
    def altSideImage( who ):
        if who == "Player":
            return "images/path/to/image/name_of_image.ext"
        elif who == "Someone else":
            return "images/another/path/to/image/another_name_of_image.ext"
         else:
            return none

screen say[...]
[...]

   if not renpy.variant("small"):
        # The effective side image that should change every time and be displayed on the right.
        add SideImage() yalign 1.0 xalign 1.0
       # The other side image, the one that stay the same and is displayed on the left
       add altSideImage( who ) yalign 1.0 xalign 0.0
 

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,555
2,170
Looking briefly at your messages so far... it seems you want two lots of side images.
A side image shown on the left which represents the player and a side image on right which represents the character they are speaking to. Beyond that... you want the side images to change dependant on the "mood" of the character.

Why not let RenPy take care of the "other" characters using the standard RenPy logic and you do all your messing about for the player side image only. (Which I just noticed is what Anne suggested).

Plus all this layeredimage thing is beyond me, but I'm still reasonably sure I could get something 90% there with my basic knowledge.

Something like:
(this code is untested, apologies for any brain farts on my part)

Python:
define a = Character("Adam", image="adam")
define e = Character("Eve", image="eve")

image side adam = side_adam_neutral
image side adam happy = side_adam_happy   # where this matches an image "side_adam_happy.png"
image side adam neutral = side_adam_neutral
image side adam sad = side_adam_sad
image side adam jeans happy = side_adam_jeans_happy
image side adam jeans neutral = side_adam_jeans_neutral
image side adam jeans sad = side_adam_jeans_sad
image side adam suit happy = side_adam_suit_happy
image side adam suit neutral = side_adam_suit_neutral
image side adam suit sad = side_adam_suit_sad

# A similar set of side images for Eve too.

# Each group of side images matching what they are wearing and their mood for facial expression.
# With extra side images for generic side images based on mood only as a catch-all/fallback.
# Normally both "attire" and "mood" would be specified, but where they aren't (or mismatched?)...
# ... at least there's a fallback image to display instead.

label start:

    scene black with fade

    # normally you'd expect something like [ a "Generic greeting." ]
    # ... but by adding additional tags, it auto picks the matching side image to display

    a jeans neutral "Generic greeting."   # which shows the text and shows the appropriate side image
The tags associated with a side image aren't just limited to dialogue either. If you have a game which uses static background images and overlayed foreground images for things like characters... and you correctly name the displayables associated with those characters... the tagging system works with show and scene too.

Python:
# blah, blah, images....

label start:

    scene black with fade

    show adam jeans neutral with dissolve at left
    show eve whitedress neutral with dissolve at right

    a jeans happy "Nice dress"
    a "I really like it"    # this would either continue to use "side adam jeans happy" or just "side adam" whichever is the better match

    e whitedress mad "Hrmmmph. Men!"    # assuming an "side eve whitedress mad" exists of course
    hide eve with moveoutright
I'm adlibbing here... it'd need testing.

If I'm remembering things correctly, the names like side_adam_neutral are just displayable names anyway, auto assigned by RenPy during startup.

If you weren't already aware, RenPy searches the /game/ and subfolders for images (either during startup or at build time) and assigns displayable id's to anything it finds which it decides is an image.
It's why you do something like scene moonlight_mountain with dissolve works.
An implicit image moonlight_mountain = "/images/backgrounds/moonlight_mountain.jpg" has been done for you.

However, RenPy has an advanced form of that, which can be switched on using these two lines coded somewhere (ideally in the options.rpy file).

Python:
define config.automatic_images = [ '/', ':', '_', '-' ]
define config.automatic_images_strip = [ "images" ]
Which would now automatically generates an implied image backgrounds moonlight mountain = "/images/backgrounds/moonlight_mountain.jpg" instead. Creating a fully displayable tagged name/id based on the original filename and it's directory names.

(A better convention would be /game/images/bg/moonlight_mountain.jpg, which better matches the RenPy documentation.)

Really handy if you'd rather bypass having to code all those side image names and instead just have files like:
/game/images/side/adam/neutral.png
/game/images/side/adam/jeans_neutral.png
/game/images/side/adam/suit_neutral.png

But because my original image side adam jeans happy = side_adam_jeans_happy is really just one displayable being linked to another displayable... I don't believe there's any reason why you couldn't link them to more complex displayables... like layedimages or compositeimages.

My point though is that RenPy already takes care of displaying side images automatically when characters speak as well as swapping those image based on displayable tags (I picked clothing and mood - though you could just use mood or any other criteria). If you use that standard side image logic to handle all the NPCs in game, displayed there on the right... you need only manually take care of the MC's side image... which would be a lot simpler.

Oh... and as far as I'm aware... The order of the tags beyond the first one doesn't matter. So whilst I went with side adam jeans neutral, side adam neutral jeans would work equally well... and both would work if you did a neutral jeans "Generic greeting.".

Finally, something tangentially linked to this sort of image name tagging. The first part of the displayable id can be considered it's "primary tag". Only one displayable can be shown on the screen at a time with a matching primary tag. Which is one of the reasons the RenPy examples of all of this have background image prefixed with "bg", since only one "bg" object can be displayed at once - you can swap one background image for another without hiding the first one. (Plus "bg" is quicker to type than "background").

I know this doesn't directly answer the question you asked... but I think it's probably the solution you were looking for - before you decided to do it the harder way... or not... I'm making a lot of assumptions here.
 
Last edited:

AmazonessKing

Amazoness Entrepreneur
Aug 13, 2019
1,898
2,902
Thank you all for replying, and oh boy, I did a lot of shit in the time I didn't have a reply, although my plans changed a bit because of it... I'm sure it's not the most efficient or better way to do it and depending on how hard it results, I may switch to your suggestions, that will serve me in the future anyway. Here, let me show you.

Here's what I ended up as a system to constantly show the speaker by using callback at the beginning of every sentence they make. The speaker is shown as soon as it speaks, and it hides is someone else talk. I had to define a new layer to add the characters and a new layer where I moved the say screen to. In the "character" layer, I show the sprites, while the "UI" layer has my personalized UI. The scene statement clears both the master layer, as usual, but also the character layer, which I really needed (I also made it so the H key also hides all these new layers for the scenes if needed, but that's quite long and in a different part of the code anyway/). I make it so the specific scenes with the tag "see" won't show the sprites if they are talking, since the biggest drawback of this system is that I can't choose when to show them, they show themselves automatically, and when they are not talking, they are moved offscreen to they retain their variables instead of hiding them. I can arguably improve this with a few more lines, but I'm ok with it the way it is right now. Arguably, now I don't need the character on the left, but I think I can easily change some lines so the character on the left is always shown as well, I can also add some other fancy effects like having the character move out while the new speaking character moves in:
Python:
init -1 python:

    renpy.add_layer("character", below="transient", menu_clear=True)
    renpy.add_layer("ui_layer", below="character", menu_clear=True)
    def talkingcall(event, interact=True, **kwargs):
        showing_tags = renpy.get_showing_tags(layer="character", sort=True )
        current_tag = renpy.get_say_image_tag()
        all_character_tags =  ["titanite_talk", "angelite_talk", "seraphinite_talk", "garnet_talk", "amethyst_talk", "pearl_talk", "connie_talk", "lars_talk", "sadie_talk"]
        character_tags = [
            t for t in showing_tags
            if t in all_character_tags
            and t != current_tag ]
        if renpy.showing("see"):
            pass
            return
        if current_tag and event == "begin":
            for tag in character_tags:
                renpy.show(tag, layer='character', at_list=[offscreenright])
            renpy.show(current_tag, at_list=[right])
            renpy.with_statement(Dissolve(0.1))
        return (), kwargs
    def cust_scene(*args):
            renpy.scene(layer='character')
            renpy.scene(layer='master')
            return
    config.scene = cust_scene

define config.tag_layer = {'titanite_talk':'character', 'angelite_talk':'character', 'seraphinite_talk':'character', 'garnet_talk':'character', 'amethyst_talk':'character', 'pearl_talk':'character', 'connie_talk':'character', 'lars_talk':'character', 'sadie_talk':'character'}  #tag it so every cohan image is automatically place on the 'character' layer. Alternatively, you can use "onlayer" to manually put him in there every time
define config.clear_layers = ['character', 'ui_layer'] # clear it so the char will disappear when enter game screen, otherwise he will awkwardly stay there
define config.menu_clear_layers = ['character', 'ui_layer'] # clear it so the char will disappear when enter game screen, otherwise he will awkwardly stay there
define config.say_layer = "ui_layer"
define config.choice_layer = "ui_layer"
As for the layered characters, I'm still not convinced about the way I did it. First, my layers are divided by Base > Arms > Clothes > Faces. The biggest problem is that the arms are divided into two poses, up and down, and so are the clothes. I want to make it in a way that if the arms are up, only the up variant of the clothes will be shown. Right now I played a bit with the "if_not", although I think it would be simpler with just the if_any, I'm not entirely sure how it works yet without trying further. So far it works well enough, I use "null null" to undress them and so ren'py doesn't check for a piece of clothing called null, and it works well enough, but I'm not sure how well it will work with more clothes:
1600379663556.png
Python:
default garnet_bush = True
layeredimage garnet:
    always:
        "garnet_base"
    if garnet_bush:
        "garnet_bush"
    group arms if_not "up":
        attribute down default
    group arms if_not "down":
        attribute up
    group outfit if_not "up":
        attribute old_down default
        attribute null null
    group outfit if_not "down":
        attribute old_up default
        attribute null null
    attribute blush
    group face:
        attribute angry
        attribute happy
        attribute sad
        attribute neutral default
image garnet_talk = LayeredImageProxy("garnet", Transform(crop=(0,0,751,1150)))
If you don't need the other image to be a side image:
try this?
I tried something similar. It seems far simpler than my final solution. I may use this one for a different game, but I'd have to test it first.

They should be named "side_player_face_angry", "side_player_face_happy", etc.
Oh, alright, I thought side was a keyword/tag that would make it exclusive. I didn't know I needed to name them side as well in the filename for it to correctly show itself. That will be useful later.
As for your problem, basically the solution looks like :
Well, that's way better than my spaghetti.

Plus all this layeredimage thing is beyond me, but I'm still reasonably sure I could get something 90% there with my basic knowledge.
It's surprisingly easy, as shown in my code, all you need is the LayeredImageProxy() line, which just takes an existing layered image to change the tag and even add transform properties if needed (And I needed to). This is VERY useful for side images using layered images since any given arguments work as if you were using the regular layeredimage, or any character related image and emotion, to be absolutely honest.

I'd have to take a look further to your code later, though, since it's quite long.
 

hottap

Newbie
Dec 23, 2017
19
6
Hi everybody. I recently started using Ren'py and there's a little something that's driving me nuts, so if any of you good sirs happen to have an answer to that I'd be grateful.

As I was saying I started using layered images and it looks great but I have a problem with "variable management". As in many VN of this kind, I'm allowing player to choose costumes for the characters. But my characters also have different poses (moving arms and so on).
So basically when I call the layered image of a character, I want to be able to call my layered image with the pose and expression in parameters (and this works) but I also want to have a global variable that stores what costume is selected, so that the layered image displays a sprite depending both on pose and costume.

I hope my explanation is not too confusing.
Thanks in advance
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Respected User
Donor
Jun 10, 2017
10,094
14,737
So basically when I call the layered image of a character, I want to be able to call my layered image with the pose and expression in parameters (and this works) but I also want to have a global variable that stores what costume is selected, so that the layered image displays a sprite depending both on pose and costume.
Doesn't layered images support text interpolation into image names ? I seem to remember that, but don't really have the time to try or search.

Basically, if your image definition looks like this:
Code:
layeredimage girlsSprite:
    always:
        "girl base"
    group face:
        attribute angry
            "girl angry"
        attribute happy
            "girl happy"
    group pose:
        attribute seated
            "girl seated"
        attribute standing
            "girl standing"
you can include the clothes variables that way (note that you've now to use fully qualified names for the images):
Code:
layeredimage girlsSprite:
    always:
        "images/character/girl/[clothes]/girl base.png"
    group face:
        attribute angry
            "images/character/girl/girl angry.png"
        attribute happy
            "images/character/girl/girl happy.png"
    group pose:
        attribute seated
            "images/character/girl/[clothes]/girl seated.png"
        attribute standing
            "images/character/girl/[clothes]/girl standing.png"
Ren'py will search the image in the "images/character/girl/" sub directory corresponding to the value of clothes
 
  • Wow
Reactions: AmazonessKing

AmazonessKing

Amazoness Entrepreneur
Aug 13, 2019
1,898
2,902
I'm already doing this. All you need is a ConditionSwitch, and a conditional with the outfit stored. This is as simple as I can do it, since multiple poses variations requires extra code, but basically:

Python:
default coolgirl_bush = True
default coolgirl_outfit = "casual"
layeredimage coolgirl:
    always:
        "coolgirl_base"
    if coolgirl_bush == True:
        "coolgirl_bush"
    group outfit:
        attribute clothed default:
            ConditionSwitch(
                "coolgirl_outfit == 'casual'", "portraits/cool/girl/coolgirl_outfit_casual.png",
                "coolgirl_outfit == 'swimsuit'", "portraits/cool/girl/coolgirl_outfit_swimsuit.png",
                "True", "naked_displayable")
        attribute casual
        attribute swimsuit
        attribute naked:
            null
    attribute blush
    group face:
        attribute angry
        attribute happy
        attribute sad
        attribute neutral default

label start:

    show coolgirl
    cogi "I should be using my casual clothes."
    $ coolgirl_outfit = "swimsuit"
    cogi "Now I should permanently have my swimsuit."
    cogi casual "But I can change to be casual if I want to."
    cogi naked "Or naked."
    cogi clothed "Can also go back to my default state, which is swimsuit, like this."
    $ coolgirl_outfit = "naked"
    cogi "Or I can stay permanently naked."
    return
 

AmazonessKing

Amazoness Entrepreneur
Aug 13, 2019
1,898
2,902
Doesn't layered images support text interpolation into image names ? I seem to remember that, but don't really have the time to try or search.

Basically, if your image definition looks like this:
Code:
layeredimage girlsSprite:
    always:
        "girl base"
    group face:
        attribute angry
            "girl angry"
        attribute happy
            "girl happy"
    group pose:
        attribute seated
            "girl seated"
        attribute standing
            "girl standing"
you can include the clothes variables that way (note that you've now to use fully qualified names for the images):
Code:
layeredimage girlsSprite:
    always:
        "images/character/girl/[clothes]/girl base.png"
    group face:
        attribute angry
            "images/character/girl/girl angry.png"
        attribute happy
            "images/character/girl/girl happy.png"
    group pose:
        attribute seated
            "images/character/girl/[clothes]/girl seated.png"
        attribute standing
            "images/character/girl/[clothes]/girl standing.png"
Ren'py will search the image in the "images/character/girl/" sub directory corresponding to the value of clothes
That's pretty simple. Wow.
 

hottap

Newbie
Dec 23, 2017
19
6
Ren'py will search the image in the "images/character/girl/" sub directory corresponding to the value of clothes
HOLY RAVIOLY that worked ! that's what I tried to do at first but I couldn't find the way to empty the content into a string, I tried with '+' and all but I guess my Python is a little too rusty.

I'm already doing this. All you need is a ConditionSwitch, and a conditional with the outfit stored.
My second attempt was something like this, trying to include conditions within my attributes statements, but it failed everytime. I was using if statements so that's maybe where it went wrong. Also I don't get why after the switch you 'declare' attributes again, guess I still have to learn more about layered images. I'll keep your solution in mind for more complex cases.

Anyway thanks to both of you, it has been really helpful and really fast as well !