RenPy Beginner - Questions

webmasterjunkie

New Member
Jul 23, 2023
3
1
Hi All,

Working on whether or not I can create a VN. I am just running into things that don't make sense. Hoping this can be a place I ask some pretty basic questions and get some guidance on. Probably the first hiccup I am seeing is around OOP in my code. I am just working on functionality bits to ensure I can build the VN I am thinking about. I am creating an "Actor" class:

Code:
### Actor Object
init python:
    class Actor:
        def __init__(self, character, name, relationship, affection = 0):
            self.character = character
            self.name = name
            self.relationship = relationship

        def affectionUp(self, amount):
            self.affection += amount
            renpy.notify(self.name + " liked that!")

        def affectionDown(self, amount):
            self.affection -= amount
            renpy.notify(self.name + " will remember that!")
I have a pretty basic statement for it that looks like this:

Code:
define mel = Actor(
    Character("Melissa", who_color = "#213fe9"),
    "Melissa",
    "Neighbor"
    )
This all works fine up until here:

Code:
    mel.character "[mc.name], do you like me?"
    menu:
        "Yes":
            $ mel.affectionUp(1)
            mel.character "Thanks!"

        "No":
            $ mel.affectionDown(1)
            mel.character "Ouch"
RenPy tosses me this:

Code:
I'm sorry, but an uncaught exception occurred.

While running game code:
  File "game/story/intro.rpy", line 28, in script
    $ mel.affectionUp(1)
  File "game/story/intro.rpy", line 28, in <module>
    $ mel.affectionUp(1)
AttributeError: 'Actor' object has no attribute 'affectionUp'

-- Full Traceback ------------------------------------------------------------

Full traceback:
  File "game/story/intro.rpy", line 28, in script
    $ mel.affectionUp(1)
  File "C:\Program Files\RenPy\renpy\ast.py", line 823, in execute
    renpy.python.py_exec_bytecode(self.code.bytecode, self.hide, store=self.store)
  File "C:\Program Files\RenPy\renpy\python.py", line 1178, in py_exec_bytecode
    exec(bytecode, globals, locals)
  File "game/story/intro.rpy", line 28, in <module>
    $ mel.affectionUp(1)
AttributeError: 'Actor' object has no attribute 'affectionUp'

Windows-10-10.0.22631 AMD64
Ren'Py 8.2.3.24061702
Anyone have any insight into what could be causing this? I have checked my indentation but I am using all spaces and indented as needed I think.
 

webmasterjunkie

New Member
Jul 23, 2023
3
1
Disregard. Seems VS Code was creating a running cache of my files and RenPy wasn't getting my latest version. Mod can delete post if they like!
 

osanaiko

Engaged Member
Modder
Jul 4, 2017
2,281
3,918
"define" keyword is for constants that will not change. So when you did "define mel = Actor(...)" it's not going to do what you intend because later when you save games the changes to the object properties won't be persisted, which can lead to some confusing bugs.

tl;dr: Should use "default mel = Actor(...)"
 

osanaiko

Engaged Member
Modder
Jul 4, 2017
2,281
3,918
Disregard. Seems VS Code was creating a running cache of my files and RenPy wasn't getting my latest version. Mod can delete post if they like!
I'm not sure it would be vscode "caching" stuff, unles you are using it in a way I don't understand (or not saving edited files?)

A running Renpy game does not pickup changes in the game script files at runtime unless you have enabled "Auto reload" mode by:
a) enabling developer mode (which is on by default if you launch the game from the Renpy SDK app)
b) and hit "shift-R" in the game. You'll see the window title change to "<game title> - autoreload"
 
  • Like
Reactions: webmasterjunkie

gojira667

Member
Sep 9, 2019
272
253
I like to keep the Character a define: .
Python:
define character.mel = Character("Melissa", who_color = "#213fe9")

default mel = Actor(
    "Melissa",
    "Neighbor"
    )

    menu:
        "Yes":
            $ mel.affectionUp(1)
            mel "Thanks!"
 

webmasterjunkie

New Member
Jul 23, 2023
3
1
"define" keyword is for constants that will not change. So when you did "define mel = Actor(...)" it's not going to do what you intend because later when you save games the changes to the object properties won't be persisted, which can lead to some confusing bugs.

tl;dr: Should use "default mel = Actor(...)"
Ah. This is good to know. Just seemed like the logical route to go to have it all under one object. Appreciate you saving me headache later down the road!
 
  • Like
Reactions: osanaiko

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,579
2,210
Python:
init python:
    class Actor:
        def __init__(self, character, name, relationship, affection = 0):
            self.character = character
            self.name = name
            self.relationship = relationship
Code:
I'm sorry, but an uncaught exception occurred.
[...]
AttributeError: 'Actor' object has no attribute 'affectionUp'

Okay. It sounds like you have already corrected it without noticing the original error, but the code you've posted here has an affection parameter passed as part of the Actor object creation. But what it doesn't have is an affection attribute.

I think what you wanted was:

Python:
init python:
    class Actor:
        def __init__(self, character, name, relationship, affection = 0):
            self.character = character
            self.name = name
            self.relationship = relationship
            self.affection = affection              # <--- note this extra line

... and... as others have pointed out, you definitely want to use default when creating your Actor() objects, since (at the very least) the affection attribute is expected to change while the game is running.

And to avoid any confusion, the Character() objects use define because their values never change while the game is running. Even those games that change the MC's name only change the name variable, not the Character() attributes themselves.

For example...

Python:
default persistent.mc_stdname = "Brian"
default mc_name = "Unknown"

define mc = Character("[mc_name"])

label start:

    scene black with fade

    "Welcome to [config.name]."

    $ mc_name = renpy.input("What is your name? {i}(Press ENTER for [persistent.mc_stdname]){/i}", exclude="[]{}")
    $ mc_name = mc_name or persistent.mc_stdname
    $ persistent.mc_stdname = mc_name

    mc "Hi, my name is [mc]."

    return

When the player picks a new name, the value of mc_name changes, but the mc.name attribute of the Character() object is still "[mc_name]". A little confusing since one is mc_name and one is mc.name, but it's the sort of thing you don't even notice once you understand why.

Basic rule of thumb... If a variable will change while the game is being played, use default, otherwise use define.
define statements can still be changed anytime... by the programmer, not due to the actions of the player.
 

peterppp

Active Member
Mar 5, 2020
562
946
having the same name declared in two places in bad practice though. you already have the name in the character. no need to add it as a separate parameter to the actor
 

osanaiko

Engaged Member
Modder
Jul 4, 2017
2,281
3,918
having the same name declared in two places in bad practice though. you already have the name in the character. no need to add it as a separate parameter to the actor
True, but you might have the actor.name be an "internal name" to be used for something like dynamic image file references, while the Character name is for display purposes. "johnotoole" vs "John O'Toole".