Ren'Py How to create switchable main characters in renpy?

Apr 4, 2022
21
4
Hello. The game I am working on a game which has two main characters. We are thinking of giving the player an option to switch to a character whenever they want to. The two different characters will have different stats and different inventories. Although they have the same story, they will have different quests/missions. Although its seems a good idea, I am having difficulties about how to switch characters. The moment the player chooses to switch characters, the game has to show different quests, different screens for different stats and inventory. How do I do this?
 

osanaiko

Engaged Member
Modder
Jul 4, 2017
2,336
4,068
1. have a "AdvancedCharacter" class that represents a main character in your game that has accessor methods for stuff like inventory, quests etc. It would probably be best to extend the built-in character class. Create two instances of the character object for the two main chars. Populate the two objects' data independently.

2. have a "get current character" global function that will return whichever character object is selected. use a global variable to hold the "selected character" indicator

3. Any screen that will have different information per character, add a "character object" parameter and then inside the screen refer to all the inventory, quest etc information via the accessor methods on the passed object.

4. whenever you need to show a screen, call it like

call screen my_screen(get_selected_character())


If all that doesn't make much sense, then you might not yet have enough programming experience to build a game with a "two switchable main characters" level of complexity. Can you find a programmer to collaborate?
 
  • Like
Reactions: Wildest Fantasies
Apr 4, 2022
21
4
sorry
1. have a "AdvancedCharacter" class that represents a main character in your game that has accessor methods for stuff like inventory, quests etc. It would probably be best to extend the built-in character class. Create two instances of the character object for the two main chars. Populate the two objects' data independently.

2. have a "get current character" global function that will return whichever character object is selected. use a global variable to hold the "selected character" indicator

3. Any screen that will have different information per character, add a "character object" parameter and then inside the screen refer to all the inventory, quest etc information via the accessor methods on the passed object.

4. whenever you need to show a screen, call it like

call screen my_screen(get_selected_character())


If all that doesn't make much sense, then you might not yet have enough programming experience to build a game with a "two switchable main characters" level of complexity. Can you find a programmer to collaborate?
I am pretty well acquainted with python but relatively new to renpy
 
  • Like
Reactions: osanaiko

osanaiko

Engaged Member
Modder
Jul 4, 2017
2,336
4,068
sorry


I am pretty well acquainted with python but relatively new to renpy
That's good then you might have a chance!! haha. Renpy is a curious beast that layers four separate DSLs (dialogue script, Screen language, a declarative Style language and the ATL animation language) on top of a Pygame core, while still exposing a python-ish direct coding interface. I have great respect for Pytom and collaborators for making something that works well for many basic VN functions while still being flexible enough to let advanced developers extend and customize it.

One tip: Be careful about how renpy handles making Save Games that correctly store the internal state of your objects. There's a whole section in the manual specifically about this because it does not work "out of the box". Many of the normal python datatypes such as list or dict are silently replaced with custom extension such as RevertableList to get around the need to have rollback-ability, and your objects need to have the same functionality added.
 
  • Like
Reactions: gojira667

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,515
15,427
2. have a "get current character" global function that will return whichever character object is selected. use a global variable to hold the "selected character" indicator
Mandatory side note:
It's tempting to directly copy the character in the "selected character" indicator. Something like selectedCharacter = character2 if selectedCharacter == character1 else character1.
But Ren'Py rely on pickle to generate, then load the save files. This mean that when you'll load a save file, "selectedCharacter" would be a new object independent from both "character1" and "character2".

What mean that, like you said it effectively need both an indicator and a way to get the character. But they don't necessarily need to be separated, nor to rely on a function call.

In fact, the best way to do it is to rely on a proxy object that will be constant from starts to stop of the game. It's it that will decide which "AdvancedCharacter" object to use each time a call is done.
Something that would looks like this:
/!\ It's late and I wrote it on the fly, there's perhaps some typos /!\
Python:
init python:

    class AdvancedCharacter( renpy.python.RevertableObject ):
        def __init__( self, [...] ):
            [...]

        def getQuest( self ):
            [...]

        @property
        def health( self ):
            return self.health


    class ProxyCharacter( renpy.python.RevertableObject ):
        def __init__( self ):
            self.current = 0

        def switch( self ):
            self.current = 1 if self.current == 0 else 0

        def __getattr__( self, aName ):
            if aName in ["current", "switch"]:
                return super( ProxyCharacter, self).__getattr__( aName )
            else:
                return getattr( store.characters[self.current], aName )

default characters = [ AdvancedCharacter( [...] ), AdvancedCharacter( [...] ) ]
default mc = ProxyCharacter()
And now the whole game can be developed without having to care what character is currently selected. Rely on "mc" from starts to stop, and it's it that will give you the correct information depending on what character is actually selected.
The only constraint is to not randomly permit to switch from one character to the other. Switching can only be done on "safe place" ; a safe place being an eventless label, like MC's bedroom, the map, or anything like this.


Edit: Typo and formatting error
 
Last edited:

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,586
2,232
I'd offer a slightly lower tech solution... Just change the MC character's name each time you want to switch characters, then use the current character name as the deciding factor in picking what quests/inventory/etc to show. RenPy will always show the MC, but the player will see two characters.

Python:
default name1 = "Unknown"
default name2 = "Unknown"

default current_name = "Not Set"

define mc = Character("[current_name]")

label start:

    scene black
    "*** START ***"

label enter_name1:

    $ name1 = renpy.input("What is the name of the first character? {i}(Press ENTER for 'Wayne'){/i}", exclude="[]{}")
    $ name1 = name1.strip() or "Wayne"

label enter_name2:

    $ name2 = renpy.input("What is the name of the second character? {i}(Press ENTER for 'Garth'){/i}", exclude="[]{}")
    $ name2 = name2.strip() or "Garth"

    if name1 == name2:
        "*** The names must be different ***"
        jump enter_name1

    $ current_name = name1

    jump main_start

label swap_character:

    if current_name == name1:
        $ current_name = name2
    else:
        $ current_name = name1

    return

label main_start:

    menu:
        "Swap character":
            call swap_character

        "Exit game":
            jump the_end

    mc "I am now known as [mc]."

    jump main_start

label the_end:

    "*** THE END ***"
    return

Then make all other decisions based on the value of current_name.

Depending on how you store your inventory(s) and quests - you could use current_name as the key to a series of . Dictionaries are just one way you could be storing the inventory, etc. Again... lower tech... you could simply do if current_name == name1: checks everywhere in your game, including the screens which show quests/inventory.
 
Last edited:
  • Like
Reactions: osanaiko