Ren'Py Creating a new game, tropes coming up? [incest] among others...

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Respected User
Donor
Jun 10, 2017
10,143
14,828
Just my impression about the code (haven't even started the game). Please don't take it bad, it's not a critic of your coding capabilities. I appear that I have a little more knowledge than you, it would be a waste to not share it. Especially since your comments make me hope that it will be a game that I'll love to play.

  • You don't need to use "global" with variables knew by Ren'py (so used outside of a "python" block in a .rpy file).
    Just use "store.[name of the variable]" and you'll access them, without the need to even have a "global store". Bonus, it will remember you that you can also use setattr and getattr.
  • Why " if c == "fM" and not s:" and not negative values for f ?
    For me the combination of a positive value and a flag to negative them latter, is an opened door for a lot of bugs. Same are the way too long lines needed to call the function.
    Apparently you always display the same text for the same action, so manage the said text directly in the function. As for "c" it's useless. You can replace it by "a.startswith( "fM" )" or even a string's slice ( a[:2] ) if you keep the format "NN_action" where "NN" is the two letters nickname of a character.
    You don't have permission to view the spoiler content. Log in or register now.
  • What's the meaning of the whole "if fM_bj >= fM_pussy >= fM_anal:" part in the start label ?
    The name (and documentation) is confusing but the label is really called only when you start a fresh game by clicking on the "start" button on the main menu ; by opposition to "config.start_callbacks" where "start" mean "Ren'py is launching". So, this code will be played only once, when the variables all have their initial value (since they aren't persistent).
    Are you thinking about the "afterload" label ? Which is called each time a game is loaded.
  • "if persistent.skipintro == True:"
    What's the meaning of it if you bypass it anyway ? I mean, you make it at True if the player have seen the intro at least once, which for me mean, "alright, you've already played the game, I'll not show you the introduction another time". But like you still give the choice to the player, it become useless. Whatever "skipintro" is set to True or not set, the game will act exactly the same.
    Are you thinking about "None" (default value for any persistent attribute/variable), which will mean, "you haven't seen the introduction, want you to see it this time", and lead to seeing the introduction only once whatever the number of time you play the game.

That's all I've seen, but like I said, I haven't played the game yet (not really a game to play when your 16yo daughter is at home :D), so I can have missed something which isn't obvious by just reading the code.
 

Studio Errilhl

Member
Game Developer
Oct 16, 2017
315
235
Thanks for the feedback :)

I've started using Ren'Py less than a month ago, and don't really have any real Python experience before that, so most of what I do is "does this work?" "okay, let's do it" - Improvements are coming, with time, but yeah, it's a steep learning curve, especially since the documentation for Ren'Py is... let's just say it leaves a bit to be decided.

As for the "if persistent.skipintro == True:" (which could of course just be done like "if persistent.skipintro:") it's to allow the player to not play the intro again (a choice in the beginning) - or play it again, if s/he wants. This is persistent, to keep it between games as well, so you don't have to watch it if you don't want to when playing again.
 

Studio Errilhl

Member
Game Developer
Oct 16, 2017
315
235
@anne O'nymous Hi, I tried using your code, and it mostly works (thanks for that) - however, I get some errors, and I can't seem to figure out how to fix them. Whenever there's a negative value, like statschangeNotify("nK_rel",-3) it will throw an error:

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

While running game code:
  File "game/script.rpy", line 418, in script
    $ statschangeNotify("nK_rel","-3")
  File "game/script.rpy", line 418, in <module>
    $ statschangeNotify("nK_rel","-3")
  File "game/functions.rpy", line 202, in statschangeNotify
    renpy.pause(.1)
  File "game/screens.rpy", line 1267, in execute
    screen notify(message):
  File "game/screens.rpy", line 1267, in execute
    screen notify(message):
  File "game/screens.rpy", line 1269, in execute
    if not isinstance(message, (str, unicode)):
  File "game/screens.rpy", line 1270, in execute
    $ status = message[1]
  File "game/screens.rpy", line 1270, in <module>
    $ status = message[1]
TypeError: 'NoneType' object has no attribute '__getitem__'

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

Full traceback:
  File "game/script.rpy", line 418, in script
    $ statschangeNotify("nK_rel","-3")
  File "C:\Program Files (x86)\renpy-6.99.13-sdk\renpy\ast.py", line 827, in execute
    renpy.python.py_exec_bytecode(self.code.bytecode, self.hide, store=self.store)
  File "C:\Program Files (x86)\renpy-6.99.13-sdk\renpy\python.py", line 1764, in py_exec_bytecode
    exec bytecode in globals, locals
  File "game/script.rpy", line 418, in <module>
    $ statschangeNotify("nK_rel","-3")
  File "game/functions.rpy", line 202, in statschangeNotify
    renpy.pause(.1)
  File "C:\Program Files (x86)\renpy-6.99.13-sdk\renpy\exports.py", line 1304, in pause
    rv = renpy.ui.interact(mouse='pause', type='pause', roll_forward=roll_forward)
  File "C:\Program Files (x86)\renpy-6.99.13-sdk\renpy\ui.py", line 287, in interact
    rv = renpy.game.interface.interact(roll_forward=roll_forward, **kwargs)
  File "C:\Program Files (x86)\renpy-6.99.13-sdk\renpy\display\core.py", line 2553, in interact
    repeat, rv = self.interact_core(preloads=preloads, trans_pause=trans_pause, **kwargs)
  File "C:\Program Files (x86)\renpy-6.99.13-sdk\renpy\display\core.py", line 2821, in interact_core
    root_widget.visit_all(lambda i : i.per_interact())
  File "C:\Program Files (x86)\renpy-6.99.13-sdk\renpy\display\core.py", line 495, in visit_all
    d.visit_all(callback)
  File "C:\Program Files (x86)\renpy-6.99.13-sdk\renpy\display\core.py", line 495, in visit_all
    d.visit_all(callback)
  File "C:\Program Files (x86)\renpy-6.99.13-sdk\renpy\display\core.py", line 495, in visit_all
    d.visit_all(callback)
  File "C:\Program Files (x86)\renpy-6.99.13-sdk\renpy\display\screen.py", line 401, in visit_all
    callback(self)
  File "C:\Program Files (x86)\renpy-6.99.13-sdk\renpy\display\core.py", line 2821, in <lambda>
    root_widget.visit_all(lambda i : i.per_interact())
  File "C:\Program Files (x86)\renpy-6.99.13-sdk\renpy\display\screen.py", line 411, in per_interact
    self.update()
  File "C:\Program Files (x86)\renpy-6.99.13-sdk\renpy\display\screen.py", line 580, in update
    self.screen.function(**self.scope)
  File "game/screens.rpy", line 1267, in execute
    screen notify(message):
  File "game/screens.rpy", line 1267, in execute
    screen notify(message):
  File "game/screens.rpy", line 1269, in execute
    if not isinstance(message, (str, unicode)):
  File "game/screens.rpy", line 1270, in execute
    $ status = message[1]
  File "game/screens.rpy", line 1270, in <module>
    $ status = message[1]
TypeError: 'NoneType' object has no attribute '__getitem__'

Windows-8-6.2.9200
Ren'Py 6.99.13.2919
High School Shenaningans 0.018-(Alpha)
Any idea what the reason is? I was thinking it had to do with not declaring a specific type, or something, but I've tried adding for instance float() around it, without success.
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Respected User
Donor
Jun 10, 2017
10,143
14,828
Any idea what the reason is?
You've changed something in the screen file since the version 0.17, but I guess that the lines 1269-1270 of your screen.rpy file are these ones :
Code:
    if not isinstance(message, (str, unicode)):
        $ status = message[1]
So, it happen because message (the text in "statschangeNotify") is not of the right type. I don't see these lines in the standard 6.99.13 so I assume you added them and I'll correct two errors.
You don't have permission to view the spoiler content. Log in or register now.

Now, for the error itself, I can only guess and my guess are :
  • You put the negative value as a string :
    Code:
     File "game/script.rpy", line 418, in script
        $ statschangeNotify("nK_rel","-3")
    it should directly be :
    Code:
        $ statschangeNotify("nK_rel",-3)
    It's the same for the float, write them directly :
    Code:
        $ statschangeNotify("nK_rel",0.5)
        $ statschangeNotify("nK_rel",-0.5)
    But I'm pretty sure that it should have thrown another exception from another place.
  • It's a string, but neither a standard string or a unicode string.
    Honestly I don't see why, but who know. Ren'py is strange some times and Python love weird kinds of strings.
It's all I see right now, but start with the correction I put for the notify screen, it will give your more information and perhaps help you understand the problem.

[edit: How can I have forgot the tuples ?]
 
Last edited:
  • Like
Reactions: Studio Errilhl

Studio Errilhl

Member
Game Developer
Oct 16, 2017
315
235
Yeah, I tried both of those (the negative value) - both strings and floats / negative ints, neither worked (although putting them as strings gave me another error) - the one I posted was actually with them written directly. I will test your modified screen-code, and see where it leads me :)

Well... that took care of the game-breaking errors... however, it still doesn't work. Now the notification returns the error in plain text, instead of breaking the game (it returns <type 'NoneType'>:None - seems it doesn't register the variables or something? Positive stat-changes work fine. I'm gonna include the code I'm using for the statschangeNotify, the one you gave me - maybe I've made a mistake there, somewhere:

Code:
 def statschangeNotify(a,f,p=False):
        # a = attribute to change
        # f = negative or positive value representing the change
        # p = longer pause or not

        # This function have no reason to be used by anything else, so keep it internal to "statschangeNotify".
        def _generateText( what, who, f ):
            if f < 0:
                action = "deteriorated"
            else:
                action = "increased" if what in [ "dom", "aro" ] else "improved"

            # Each variables in "format" by their order. So {0} = who, {1} = action, {2} = absolute value of "f".
                if what == "rel":
                    return "Your relationship with {0} has {1} by {2}".format( who, action, abs( f ) )
                elif what == "dom":
                    return "Your dominance over {0} has {1} by {2}".format( who, action, abs( f ) )
                elif what == "aro":
                    return "{0}'s arousal has {1} by {2}".format( who, action, abs( f ) )
                elif what == "anal":
                    return "{0}'s acceptance of anal sex has {1} by {2}".format( who, action, abs( f ) )
                elif what == "pussy":
                    return "{0}'s acceptance of regular sex has {1} by {2}".format( who, action, abs( f ) )
                elif what == "bj":
                    return "{0}'s acceptance of giving blowjobs has {1} by {2}".format( who, action, abs( f ) )
                return "Oop's something really weird happen !"

        # A single line for all the possible variables, as well as both positive and negative values.
        # Isn't it better than a line for each single possibilities ?
        setattr( store, a, getattr( store, a ) + f )

        # a[:2] all characters in the "a" string between the position 0 (included) and the position 2 (excluded).
        # Said otherwise, the first two characters of the string.
        # a[3:] the same, but between the position 3 (included) and the implied last position (included because of the "imply" part).
        # Said otherwise, anything except the first three characters of the string.
        if a[:2] in [ "fM", "nK", "fS", "sN" ]:
            # Using an internal function make it easier to read.
            text = _generateText( a[3:], str( getattr( store, a[:2] ) ), f )
        elif a == "fP_rebel":
            if f < 0:
                text = "Your rebel status has decreased by {0}".format( abs( f ) )
            else:
                text = "Your rebel status has increased by {0}".format( abs( f ) )
        elif a == "fP_law":
            if f < 0:
                text = "Your respect for the law has decreased by {0}".format( abs( f ) )
            else:
                text = "Your respect for the law has increased by {0}".format( abs( f ) )
        elif a == "mc_b":
            if f < 0:
                text = "Your motorcycle build decreased by {0}".format( abs( f ) )
            else:
                text = "Your motorcycle build increased by {0}".format( abs( f ) )
        elif a == "punishment_late":
            text = "You've gotten a mark for being late. Current amount is {0}. If you get more than 5 marks, you will get in trouble".format( store.punishment_late )
        else:
            text = "Oop's something really weird happen !"

        renpy.notify( text )

        if p:
            renpy.pause(.5)
        else:
            renpy.pause(.1)
And here's a couple examples on where this is being called with negative values:
Code:
$ statschangeNotify("nK_rel",-3)
$ statschangeNotify("sN_dom",-1,True)
 
Last edited:

Studio Errilhl

Member
Game Developer
Oct 16, 2017
315
235
And... I'm SUCH an idiot. Or... well. Errormessages aren't that good :) I'll blame the error-messages. Of course, for the more trained eye, the error is probably obvious, but it basically stemmed from a too deep indent on the if/else's after # Each variables in "format" by their order. So {0} = who, {1} = action, {2} = absolute value of "f". Didn't see it (and Renpy didn't throw an error over that, of course, until I put a renpy.watch(action) after the else above that line... It works now, now I just to make some minor adjustments, and I think it will be ready for next release! Thanks, @anne O'nymous
 
  • Like
Reactions: anne O'nymous

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Respected User
Donor
Jun 10, 2017
10,143
14,828
And... I'm SUCH an idiot. Or... well. Errormessages aren't that good :) I'll blame the error-messages.
You can blame the error-messages. I seen it because I past so many times figuring out what the said error-messages tried to tell me ;)
 

NotaBullRobo

Newbie
Nov 24, 2017
36
55
"Any other tropes, apart from incest, that should be avoided?"

Honestly, I hate the trope that everyone does everything with the door slightly open, or that they would never notice someone opening a door to peek while they're in the bathroom or bedroom.
 

Studio Errilhl

Member
Game Developer
Oct 16, 2017
315
235
"Any other tropes, apart from incest, that should be avoided?"

Honestly, I hate the trope that everyone does everything with the door slightly open, or that they would never notice someone opening a door to peek while they're in the bathroom or bedroom.
Ahahah :D Yeah, I know... okay, I can understand sometimes that opening a door might go unnoticed, if they're... very busy... but... in the bathroom, in the shower? Have any of these guys ever taken a shower and have the door open? You will notice the draft. And so on. Well, I'm not doing that... well, I actually am, sort of, but that's mostly by accident (leaning against a door which wasn't completely closed, so it flies open), and that is only one time. :D
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Respected User
Donor
Jun 10, 2017
10,143
14,828
Honestly, I hate the trope that everyone does everything with the door slightly open, or that they would never notice someone opening a door to peek while they're in the bathroom or bedroom.
There's one game who do it correctly and use the keyhole to peep on the girls when they are on the bathroom. The result is interesting and the frustration you get when you know that there's a girl inside, but you can't see her through the said keyhole, is part of this interest.

The door/keyhole can even be a full part of the game, giving a better feeling of progression than the usual "she yell at you", "she's always on her underwear", "she's always naked", "she always ask for sex". In the same time the said progression is more natural and have a justification.
When the game start, she have no reason to tease you and she care about her privacy. So, the door is locked and you need chance to see just a small part of her body in her undies, because she change in a part she know that can't be really seen from the keyhole.
Then with time she become more teasing, she care less about her privacy. She start to change in front of the locked door if it's just to change clothes without removing her undies. Then for this she don't even bother to lock the door ; but still do it and change in a hidden place, when she need to be fully naked. Then the same step apply for her naked.

In the end you've six to eight step of peeping, all coming with a rational justification. And it's not even really difficult to do. Just use a full black picture with the keyhole as whole transparent, and place it in front of the pictures.
Then, when rendering the picture, do it so when the keyhole mask is on the left side, you see nothing, on the middle, you see some part of the body, and on the right side, you see almost the whole body. Finally, remove the keyhole when you want to represent the fact that the door is now unlocked.
With the exact same amount of pictures, you have four different step of peeping and the player have a better feeling of progression, which come with a sense of victory when he finally can see the whole pictures.