Help with a character list screen [Solved]

Discuss how to use the Ren'Py engine to create visual novels and story-based games. New releases are announced in this section.
Forum rules
This is the right place for Ren'Py help. Please ask one question per thread, use a descriptive subject like 'NotFound error in option.rpy' , and include all the relevant information - especially any relevant code and traceback messages. Use the code tag to format scripts.
Message
Author
jeffster
Veteran
Posts: 461
Joined: Wed Feb 03, 2021 9:55 pm
Contact:

Re: Help with a character list screen

#16 Post by jeffster »

If you use list for characters, as we do in the code above:

Code: Select all

$ characters[MARIA]["desc"] = "Maria is 19 years old now."
where MARIA is her index in the list. For example, if she is the sixth:

Code: Select all

$ characters[5]["desc"] = "Maria is 19 years old now."
Or using the function like "char(char_id)" written above:

Code: Select all

$ char("maria")["desc"] = "Maria is 19 years old now."
Or, to remember indexes, you can define constants:

Code: Select all

define EMILIA = 0
define NOAH = 1
define EMMA = 2
define MATTEO = 3
define SOPHIA = 4
define MARIA = 5
define ELIAS = 6
define HANNAH = 7
define LEON = 8
define MIA = 9
define PAUL = 10
which should be indexes of characters in the list:

Code: Select all

default characters = [
    {ID: "emilia", "name": "Emilia", "desc": "...", "points": 0, "show": True},
    {ID: "noah"... and so on ...},
    {ID: "emma"...},
    {ID: "matteo"...},
    {ID: "sophia"...},
    {ID: "maria"...},
    ]
Then adding a character could be like

Code: Select all

    $ PETER = len(characters)
    $ characters.append({ID: "peter", name: "Peter", ...and so on...})
Note that you use "default characters" (not "define characters" - which was my mistake to write!)...

And of course you don't need to use different ID and "name". I wrote these two values separately so that you can do

Code: Select all

    $ characters.append({ID: "peter", name: "Unknown", ...and so on...})
so a character could easily change the displayed name. Peter would be shown as "Unknown" is the character screen etc. Then you learn his name, and set it:

Code: Select all

    $ characters[PETER]["name"] = "Peter"
If you don't have such tricks, you can have one value instead of two (ID and "name"). For example:

Code: Select all

    {ID: "Emilia", "desc": "My childhood friend, now living in Jakarta.", "points": 12, "show": True},
or

Code: Select all

    {"name": "Emilia", "desc": "My childhood friend, now living in Jakarta.", "points": 12, "show": True},
In any case, we have a list and dict's as its members; so we address their fields like

Code: Select all

    the_list[list_index][its_dict_key] = its_value
    # and
    its_value = the_list[list_index][its_dict_key]
If the problem is solved, please edit the original post and add [SOLVED] to the title. 8)

Rhapsy
Newbie
Posts: 20
Joined: Fri Mar 01, 2024 3:45 pm
itch: Rhapsy
Contact:

Re: Help with a character list screen

#17 Post by Rhapsy »

Ok. and how can I centralize the text in the box?
Like the one in the "desc" section for example.

jeffster
Veteran
Posts: 461
Joined: Wed Feb 03, 2021 9:55 pm
Contact:

Re: Help with a character list screen

#18 Post by jeffster »

"align" for elements. In this case try "xalign 0.5".

Sometimes (e.g. to center multi-line text, like a title string) "text_align" should be used.
If the problem is solved, please edit the original post and add [SOLVED] to the title. 8)

Rhapsy
Newbie
Posts: 20
Joined: Fri Mar 01, 2024 3:45 pm
itch: Rhapsy
Contact:

Re: Help with a character list screen

#19 Post by Rhapsy »

It worked.
I think this is the last question in this thread lol. Although I have not yet been able to test the variables to change the "desc" text due to lack of time.
How can I add new images (not buttons) to this screen? I plan to add custom numbers.png for affinity level, not points.

Edited: I almost forget it. And how do I also add a second desc text? This is to tell the player if they have available events or tasks to do with any characters on the list.

Rhapsy
Newbie
Posts: 20
Joined: Fri Mar 01, 2024 3:45 pm
itch: Rhapsy
Contact:

Re: Help with a character list screen

#20 Post by Rhapsy »

jeffster wrote: Fri Mar 08, 2024 3:34 pm "align" for elements. In this case try "xalign 0.5".

Sometimes (e.g. to center multi-line text, like a title string) "text_align" should be used.
The texts are centralized, but the text box moves to different sides on each page.
It does not remain at a fixed point on the screen.

Code: Select all

hbox:
            xalign 0.79
            yalign 0.38
            text f'{char["desc"]}' text_align 0.5

jeffster
Veteran
Posts: 461
Joined: Wed Feb 03, 2021 9:55 pm
Contact:

Re: Help with a character list screen

#21 Post by jeffster »

Rhapsy wrote: Fri Mar 08, 2024 7:10 pm
jeffster wrote: Fri Mar 08, 2024 3:34 pm "align" for elements. In this case try "xalign 0.5".

Sometimes (e.g. to center multi-line text, like a title string) "text_align" should be used.
The texts are centralized, but the text box moves to different sides on each page.
It does not remain at a fixed point on the screen.

Code: Select all

hbox:
            xalign 0.79
            yalign 0.38
            text f'{char["desc"]}' text_align 0.5
How to add images and texts:
"screen" has an hierarchical structure. It's very similar to HTML.
In "screen", indentation means the indented block is a part of the previous declaration.

For example, look at this code:

Code: Select all

    hbox:
        align (0.79, 0.38)
        text "Aaargh!":
            align 0.5
You see there "hbox" - meaning box (container) that holds elements arranged horizontally.
Inside hbox you see
(1) "align (0.79, 0.38)" - parameter that defines the alignment of this box inside the upper element.
(2) "text" - that text will be put in the box, in a row with other elements.
Inside "text" you see "align" - that sets the text's horizontal alignment in the enclosing container.

Now a little test for you. Can you say what is wrong with this code?

Ready to answer?

OK, the answer:
"text" is situated in the horizontal box according to its place among other elements.
Therefore "align" horizontally kinda conflicts with that, and wouldn't affect much.

More precisely, read:
https://renpy.org/doc/html/screens.html

Now why did you put text in hbox in your code?

To add text or picture, put "text" indented as a part of its parent element; and you can just set position of the element in pixels if you want:

Code: Select all

screen chars():
    add "picture.png":
        pos (1200, 400)
    text "Description":
        pos (1400, 800)
Maybe you wanted to reserve an area of certain size, at a particular position, and center text inside that area.

You can do that with various container elements:

vbox - puts elements as a vertical stack (in column)
hbox - puts elements in a row
grid - puts them filling rows and columns
frame - serves as an enclosing box for a single element

And so on, see
https://renpy.org/doc/html/screens.html

Therefore, if you want to set an area for text and center it in that area, you can use frame.
Set its size and position and background:

Code: Select all

screen chars():
    frame:
        background None
        pos (1200, 400)
        xysize (720, 680)
        text "This is a text centered in frame both vertically and horizontally!":
            align (0.5, 0.5)
If the problem is solved, please edit the original post and add [SOLVED] to the title. 8)

Rhapsy
Newbie
Posts: 20
Joined: Fri Mar 01, 2024 3:45 pm
itch: Rhapsy
Contact:

Re: Help with a character list screen

#22 Post by Rhapsy »

Therefore, if you want to set an area for text and center it in that area, you can use frame.
Set its size and position and background:

Code: Select all

screen chars():
    frame:
        background None
        pos (1200, 400)
        xysize (720, 680)
        text "This is a text centered in frame both vertically and horizontally!":
            align (0.5, 0.5)
This works to centralize the "desc" text. Thanks

Morning, I understood almost 90% of everything (omitting the variables part which is for another thread.)
It only confuses me in the part to add new text that works as another description or section for events in the character list.
I need this new text to change its content on each page like "desc".
To indicate if the player has a task to do with a character or if they no longer have events with other characters. For example

And test? noo lol. But I will share the code here when it is finished. Or can I share the .zip file when this is finished?

Rhapsy
Newbie
Posts: 20
Joined: Fri Mar 01, 2024 3:45 pm
itch: Rhapsy
Contact:

Re: Help with a character list screen

#23 Post by Rhapsy »

To add text or picture, put "text" indented as a part of its parent element; and you can just set position of the element in pixels if you want:

Code: Select all

screen chars():
    add "picture.png":
        pos (1200, 400)
    text "Description":
        pos (1400, 800)
This worked with images.
But for the image to change on each page of the character list I had to keep the original code and create a "photo" subfolder in "screens" (In this case for the character photos) because it would get into errors with files from other images because they are both images.png.
Maybe there was another less complicated way to solve it.

Code: Select all

screen char_list(char_n=0):
    $ char = characters[char_n]

    frame:
        background "screens/screenlist_bg.png"
        xfill True
        yfill True
        add f"screens/{char[ID]}.png" pos (806,200) #Custom name titles
        add f"screens/photo/{char[ID]}.png" pos (0,0) #Character photo
        

jeffster
Veteran
Posts: 461
Joined: Wed Feb 03, 2021 9:55 pm
Contact:

Re: Help with a character list screen

#24 Post by jeffster »

Rhapsy wrote: Sat Mar 09, 2024 12:46 pm It only confuses me in the part to add new text that works as another description or section for events in the character list.
I need this new text to change its content on each page like "desc".
To indicate if the player has a task to do with a character or if they no longer have events with other characters. For example
There is a good learning help for Ren'Py in its own Ren'Py Tutorial.
Run Ren'Py 8 SDK Launcher and select "Tutorial" project to run.

Another is documentation
https://renpy.org/doc/html/
See "Quickstart", and you can search too, for any Ren'Py elements you have questions about.

In case of those texts (and any other information), it's like in all programming languages.
You can keep any info you need.
The information that can change is kept in variables.
(You can also hear funny expressions like "constant variable", but that's just a variable which content doesn't change).

So that's the miracle of programming: just create a variable - which has its name and its content - and you can use it at your service.

In the case of "quest" text for every character, you keep those texts in variables, and you could do that with different approaches.
One approach is to have a separate dict of those texts:

Code: Select all

default quests = {
    "cat": "Feed the cat.",
    "dog": "Walk the dog.",
    "bug": "Squash the bug.",
    }
This means we create a variable named "quests". It is of type "dict", which means it contains pairs of key: value, like dictionaries have article heading and article content.

Addressing elements of dict, we use keys:

Code: Select all

    $ quests["cat"] = "Wash the cat."
This means we address dict `quests` with the key "cat" and assigned it a new value.
Now instead of "Feed the cat." it's "Wash the cat.".

Next time when we read quests["cat"], we'll get "Wash the cat.".

Therefore it's easy to show a list of quests like this:

Code: Select all

screen quests():
    vbox:
        for q in quests:
            text quests[q]
That code means "make a screen with vertical box (i.e. column) and show there every element of `quests` as a text.

Construction for q in quests, applied to a dict, means "loop through every key of this dict, assigning this key to a temporary variable". In this case this variable is named q.
Hence in this loop, q first time is "cat", next time it's "dog", and so on until all dict elements are passed.

In practice, Python programmers often use a bit different construction:

Code: Select all

        for key, val in quests.items():
            text "[val]"
dict.items() means "transform the dict into a list of pairs of that dict's keys and values".

Therefore for key, val in quests.items(): means "iterate through `quests`, taking pairs of key, value".

This method is kinda convenient that instead of quests[key] you can use just val.

Back to your question:
If you need those "quest texts" in the character screen, you can put this somewhere in the screen:

Code: Select all

text quests[char[ID]]
Because char[ID] is the identifier of the current character ("cat" or "dog" etc.), quests[char[ID]] will be the current quest text for that character.

Then the char_list screen can look like this:

Code: Select all

screen char_list(char_n=0):
    $ char = characters[char_n]

    frame:
        background "screens/screenlist_bg.png"
        xfill True
        yfill True
        add f"screens/{char[ID]}.png" pos (806,200) #Custom name titles
        add f"screens/photo/{char[ID]}.png" pos (0,0) #Character photo
        frame:
            pos (806, 300)
            xysize (1000, 400)
            background None
            text quests[char[ID]]:
                xalign 0.5
                text_align 0.5
meaning that the area for quest text would be at position (806, 300) and of size 1000x400.

Or, if you want to put that text with the screen width, at maybe y=800,

Code: Select all

screen char_list(char_n=0):
    $ char = characters[char_n]
    frame:
        background "screens/screenlist_bg.png"
        xfill True
        yfill True
        add f"screens/{char[ID]}.png" pos (806,200) #Custom name titles
        add f"screens/photo/{char[ID]}.png" pos (0,0) #Character photo
        text quests[char[ID]] ypos 800 xalign 0.5 text_align 0.5
Another way to keep the quest text for every character is to add a field to that character's dict:

Code: Select all

default characters = [
    {ID: "cat", ...all those fields from before, "quest": "Feed the cat."},
    {ID: "dog", ...all those fields from before, "quest": "Walk the dog."},
    ]
Then you access that quest text the same way as "desc", only instead of key "desc" use "quest".
I mean, it's your program, and you are free to organize your data in any manner: add key-values, remove, change etc.
But for the image to change on each page of the character list I had to keep the original code and create a "photo" subfolder in "screens" (In this case for the character photos) because it would get into errors with files from other images because they are both images.png.
Maybe there was another less complicated way to solve it.
Nobody is forcing you to keep pictures in "screen" subfolder. Keep them in "images" folder or create "chars" folder right in "game" folder, whatever.
If the problem is solved, please edit the original post and add [SOLVED] to the title. 8)

Rhapsy
Newbie
Posts: 20
Joined: Fri Mar 01, 2024 3:45 pm
itch: Rhapsy
Contact:

Re: Help with a character list screen

#25 Post by Rhapsy »

Thank you. I understood most of the concepts.
Hiding characters with "show" doesn't work. But I was able to add new characters in the other way that seemed more complicated to me.
Questions:
1_ Can I alter the order of the character list? To place a new character between "cat" and "dog" in the list (In the game)
2_ This whole command is not going to "explode" at any point in the game? ha ha ha. I just published a demo of my project and plan to share updates. It shouldn't affect any of them, right?

I share here a project where I apply, I think, everything mentioned above to create this list.
ScreenCharList.zip
proyect
(4.66 MiB) Downloaded 7 times

jeffster
Veteran
Posts: 461
Joined: Wed Feb 03, 2021 9:55 pm
Contact:

Re: Help with a character list screen

#26 Post by jeffster »

Rhapsy wrote: Sat Mar 09, 2024 8:12 pm Hiding characters with "show" doesn't work. But I was able to add new characters in the other way that seemed more complicated to me.
Hiding characters with "show" does work, if you do it properly.
I described how to create a filtered list - of only those who should be shown.
Maybe you forgot to use that filtered list in stead of the original one, or something.
1_ Can I alter the order of the character list? To place a new character between "cat" and "dog" in the list (In the game)
Yes, but that will change the characters' indexes, starting from that inserted one. That would make impractical to address individual list items (characters) by indexes. And constants like PETER = 10 would stop working, because 10 would become an index of someone else, not Peter.

Therefore to access a character (get or set their info) by ID, you would need to use more complex methods than just indexes - preferably hiding that complexity into a function. Like the function I wrote before:

Code: Select all

init python:
   def char(char_id):
       for n in range(0, len(characters)):
           if characters[n][ID] == char_id:
               return characters[n]
Hence if you plan to alter the order of characters, access them always using functions like char(char_id), because particular index values don't matter to such functions.

To insert a new character in the middle (e.g. before "dog"), you can use this function:

Code: Select all

init python:
   def insert_before(char_id, new_char):
       for n in range(0, len(characters)):
           if characters[n][ID] == char_id:
               break
       store.characters.insert(n, new_char)
like this:

Code: Select all

    bat "Hello! I'm Batty."
    insert_before("dog", {ID: "bat", "name": "Batty", "desc": "Crazee!", "quest": "Catch it.", "show": True})
2_ This whole command is not going to "explode" at any point in the game?
What I advised was just a correct way I think. There could be errors, and trying to implement it you could add more errors.
It's useful to test everything with various kinds of data, and always understand what you are doing.

For example, function "char" described above would likely give an error if you try to call it with a non-existing ID.
Let's hope that such errors would be caught in the development and corrected timely.

I'll look into the attachment you posted.

PS. Yes, the script is OK.

(1)
I noticed that I over-complicated things in these 2 lines:

Code: Select all

        text f'{char["desc"]}' text_align 0.5:

        #...

        text f'{char["quest"]}' text_align 0.5:
f-strings are to format values, usually inserting them into other strings. Here it's much simpler: we must show only the values.
Therefore change these strings like this:

Code: Select all

        text char["desc"] text_align 0.5:

        #...

        text char["quest"] text_align 0.5:
(2)
Another thing is that, if you need to "hide" or "show" characters, add filtering like this:

Code: Select all

label char_list:
    $ _return = 0
    $ ch_filtered = [x for x in characters if x["show"]]
label char_list_call:
    call screen char_list(_return)
which creates list ch_filtered containing only those characters that have "show": True.
And then in screen char_list you will have to replace all three mentions of `character` with `ch_filtered`.

(3)
Like I said, if you plan to insert characters in the middle, add those functions from my previous comment, `char` and `insert_before`.
Then you wouldn't use this construction:

Code: Select all

    $ characters[0]["quest"] = "The cat is happy!"
But you would do this:

Code: Select all

    $ char("cat")["quest"] = "The cat is happy!"
If the problem is solved, please edit the original post and add [SOLVED] to the title. 8)

jeffster
Veteran
Posts: 461
Joined: Wed Feb 03, 2021 9:55 pm
Contact:

Re: Help with a character list screen

#27 Post by jeffster »

PS. Here's the resulting code:

Code: Select all


########################### CHARACTER LIST SCREEN ##############################

screen button:
    vbox xalign 0.95 yalign 0.05:
        textbutton "List" action ui.callsinnewcontext("char_list")

define ID = 0

default characters = [
    {ID: "cat", "name": "Kitty", "desc": "Kitty is an independent and quiet cat.", "points": 5, "quest": "Feed the cat.", "show": False}, #"Show" doesnt work
    {ID: "dog", "name": "Doggy", "desc": "Doggy is a brave and curious boy.", "points": 10, "quest": "Help him find his brother to take revenge.", "show": True},
    {ID: "owl", "name": "Owl", "desc": "Owl is a retired professor, an avid knitter.", "points": 15, "quest": "a lot of teeeeeeext{p}teeeext", "show": True},
    {ID: "man", "name": "The Man", "desc": 'As The Man likes to say, "Oh boy!"', "points": 1, "quest": "teeeeext{p}text", "show": True},
    ]

default ch_filtered = [x for x in characters if x["show"]]

init python:
   def char(char_id):
       for n in range(0, len(characters)):
           if characters[n][ID] == char_id:
               return characters[n]

   def insert_before(char_id, new_char):
       for n in range(0, len(characters)):
           if characters[n][ID] == char_id:
               break
       store.characters.insert(n, new_char)

screen char_list(char_n=0):
    $ char = ch_filtered[char_n]

    frame:
        background "screens/screenlist_bg.png"
        xfill True
        yfill True
        add f"screens/{char[ID]}.png" pos (806,200) #Name
        add f"screens/photos/{char[ID]}.png" pos (50,40) #char photo with frame

        if char_n > 0:
            imagebutton auto "screens/prev_%s.png" pos (796,464) action Return(char_n - 1)

        if char_n < len(ch_filtered) - 1:
            imagebutton auto "screens/next_%s.png" pos (1696,464) action Return(char_n + 1)

        button:
            imagebutton auto "screens/back_%s.png" pos (1223,757) action Return(True)

    frame:
        background None
        pos (959, 80)
        xysize (720, 680)
        text f'{char["desc"]}' text_align 0.5:
            align (0.5, 0.5)

    frame:
        background None
        pos (960, 675)
        xysize (720, 380)
        text f'{char["quest"]}' text_align 0.5:
            align (0.5, 0,5)

    bar:
        value StaticValue(ch_filtered[char_n]["points"], 20)
        #thumb
        xysize (600,100)
        xalign 0.774
        yalign 0.55


label char_list:
    $ _return = 0
    $ ch_filtered = [x for x in characters if x["show"]]
label char_list_call:
    call screen char_list(_return)
    if type(_return) is bool:
        # "Back" button was pressed
        return
    # "Previous" or "Next" button was pressed:
    jump char_list_call

########################### END - CHARACTER LIST SCREEN ##############################

define r = Character("Rhapsy")
define k = Character("Kitty")
define p = Character("Porky")

label start:

    show screen button # List button in the game

    "Look at your character list. Porky's not there."
    "..."
    r "Hello?"

    p "Hello! My name is Porky."

    $ insert_before("man", {ID: "pig", "name": "Porky", "desc": "Its a pet, not ham.","quest": "teeeeext{p}text", "points": 4, "show": True})

    "Look at the list. Porky is already there."

    "But where is Kitty?"
    k "Meooow"

    $ char("cat")["show"] = True

    "Kitty came home! Look at your list. You must feed Kitty."
    "..."
    r "My cat kitty is hungry."
    p "ohh...{p}Bye bye!"
    "The little pig left."
    "You give your cat snacks."

    $ char("cat")["quest"] = "The cat is happy!"

    "Kitty's quest is done. Look at the list."

    "END"

    return
Last edited by jeffster on Sat Mar 09, 2024 11:01 pm, edited 1 time in total.
If the problem is solved, please edit the original post and add [SOLVED] to the title. 8)

Rhapsy
Newbie
Posts: 20
Joined: Fri Mar 01, 2024 3:45 pm
itch: Rhapsy
Contact:

Re: Help with a character list screen

#28 Post by Rhapsy »

Thank you so much!
With this I can now consider this thread resolved.

Ps: I still need to add photo galleries and a days system. I hope it's not as complicated as this. lol

jeffster
Veteran
Posts: 461
Joined: Wed Feb 03, 2021 9:55 pm
Contact:

Re: Help with a character list screen

#29 Post by jeffster »

One tiny correction: right after "default characters" definition I added this:

Code: Select all

default ch_filtered = [x for x in characters if x["show"]]
(My previous comment is already edited with that). Otherwise there could be error like "ch_filtered is not defined" - probably because Ren'Py predicts screens and thus runs them in advance.
Rhapsy wrote: Sat Mar 09, 2024 10:36 pm Thank you so much!
With this I can now consider this thread resolved.

Ps: I still need to add photo galleries and a days system. I hope it's not as complicated as this. lol
Image Gallery is a common thing and it's described in documentation:
https://renpy.org/doc/html/rooms.html

"Days system" can be made in many different ways... Some examples were discussed here and in Cookbook. But it's so simple that it might be easier just to make your own than studying others' advanced systems...
If the problem is solved, please edit the original post and add [SOLVED] to the title. 8)

Post Reply

Who is online

Users browsing this forum: Ahrefs [Bot], Google [Bot]