Label Undefined when Calling from For Loop (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.
Post Reply
Message
Author
User avatar
noeinan
Eileen-Class Veteran
Posts: 1153
Joined: Sun Apr 04, 2010 10:10 pm
Projects: Ren'Py QuickStart, Crimson Rue
Organization: Statistically Unlikely Games
Deviantart: noeinan
Github: noeinan
Location: Washington State, USA
Contact:

Label Undefined when Calling from For Loop (solved)

#1 Post by noeinan »

Troubleshooting how best to integrate for loops to my existing code, so I can run the whole block with multiple NPCs. While trying to figure out something else, I came up with the idea that instead of messing with my code, I can just put the for loop in the section where I call the label instead. However, when calling a label with an argument, I keep getting an error saying that the label is undefined. Any ideas on what I'm doing wrong here?

Code calling the labels:

Code: Select all

python:
                for npc in npc_dict.values():
                    renpy.call(partner_action_desc(npc))
                    renpy.call(partner_status(npc))
definition for the label:

Code: Select all

label partner_action_desc(npc):
Based on these two examples, it doesn't seem like the syntax is right...
viewtopic.php?t=36142
viewtopic.php?t=31699

I also tried with quotes, but got this error: ScriptError: could not find label 'partner_action_desc(npc)'.

I learned in another thread that you can use while loops without python, and I don't get an error when doing it that way:

Code: Select all

            while i < len(npc_dict):
                call partner_action_desc(npc_dict[i])
                call partner_status(npc_dict[i])
                i += 1
but it also doesn't seem to be passing the argument properly to my label...
Last edited by noeinan on Sat Nov 28, 2020 10:54 pm, edited 1 time in total.
Image

Image
Image

User avatar
Alex
Lemma-Class Veteran
Posts: 3094
Joined: Fri Dec 11, 2009 5:25 pm
Contact:

Re: Label Undefined when Calling from For Loop

#2 Post by Alex »

Should be

Code: Select all

renpy.call("partner_status", npc)
https://www.renpy.org/doc/html/statemen ... .html#call

Code: Select all

label test(x=0):
    "[x]"
    return
    
# The game starts here.

label start:
    "..."
    $ renpy.call("test")
    "... ..."
    $ renpy.call("test", 7)
    "?!"

User avatar
noeinan
Eileen-Class Veteran
Posts: 1153
Joined: Sun Apr 04, 2010 10:10 pm
Projects: Ren'Py QuickStart, Crimson Rue
Organization: Statistically Unlikely Games
Deviantart: noeinan
Github: noeinan
Location: Washington State, USA
Contact:

Re: Label Undefined when Calling from For Loop

#3 Post by noeinan »

Oh! Thank you. I think that is working, I can almost finish testing it I have just run into one more error related to the changes.
Image

Image
Image

User avatar
noeinan
Eileen-Class Veteran
Posts: 1153
Joined: Sun Apr 04, 2010 10:10 pm
Projects: Ren'Py QuickStart, Crimson Rue
Organization: Statistically Unlikely Games
Deviantart: noeinan
Github: noeinan
Location: Washington State, USA
Contact:

Re: Label Undefined when Calling from For Loop

#4 Post by noeinan »

That syntax seems to work for the most part, the only issue I'm running into now is... I'm not getting any error messages, but the label that I'm calling is not doing anything at all. Lines were showing up before implementing the for loop, but after the conversion just nothing happens.

I've got this code in for the for loop:

Code: Select all

python:
                for npc in npc_dict.values():
                    renpy.call("partner_action_desc", npc)
                    renpy.call("partner_status", npc)
Then my label looks like this:

Code: Select all

label partner_action_desc(npc):
    if npc.intro == 0:
        "" #show intro for this NPC
        $ npc.intro = 1
    else:
        pass
        
    if "thing" in list(npc.listofthings):
        if npc.variable1 == False:
            if variable2 == False:
                if variable3 == False:
                    $ variable4 += 1
                    if npc.thing_state == "lhand" or npc.thing_state == "rhand":
                        extend "[npc.They] are holding the thing. {nw}"
                    elif npc.thing_state == "hands":
                        extend "[npc.They] are holding the thing with both hands. {nw}"
                    elif npc.thing_state == "thighs":
                        extend "[npc.They] drop the thing and it starts falling to the ground. {nw}"
                        $ npc.thing_state = 0
                        $ thigh_use = 0
                    elif npc.thing_state == "floorentrance":
                        extend "The thing is moments from hitting the ground. {nw}"
                    elif npc.thing_state == "floorimminent":
                        extend "The thing hits the ground. {nw}"
                        $ npc.thing_state = "floorentrance"
                        $ floor_state = "thingentrance"
                    elif npc.thing_state == "floorpenetrated":
                        if npc.thingsize <= 1:
                            extend "The thing smashes through the floor. {nw}"
                            $ npc.thing_state = "floorentrance"
                            $ floor_state = "thingentrance"
                        elif npc.thingsize <= 3:
                            $ rng = renpy.random.randint(1,100)
                            if rng >= 50:
                                extend "The thing smashes through the floor. {nw}"
                                $ npc.thing_state = "floorentrance"
                                $ floor_state = "thingentrance"
                            else:
                                extend "The thing smashes through the floor. {nw}"
                        else:
                            extend "The thing smashes through the floor. {nw}"
                else:
                    pass
            else:
                pass
        else:
            pass
    else:
        pass
So, the intro lines will show, but every line after that does not show for some reason. Probably just a problem somewhere, I'll reorganize my code and run some tests and can probably fix it.

[Edit] After reorganizing my code, I'm not having this problem anymore thankfully! No idea what was wrong before, but I'm not sweating over it.
Last edited by noeinan on Sat Nov 28, 2020 10:54 pm, edited 1 time in total.
Image

Image
Image

User avatar
gas
Miko-Class Veteran
Posts: 842
Joined: Mon Jan 26, 2009 7:21 pm
Contact:

Re: Label Undefined when Calling from For Loop

#5 Post by gas »

Sorry, really sorry. The whole thing is so wrong on multiple POV's that giving you a solution is only aggravating the issue.
It's easier to me to give you some pseudocode to see how to rewrite the thing.

Ok, the first wrong thing is your object approach.
Let's simplify it.

Code: Select all

init python:
    class What(object):
        bla bla bla

default object1 = What()
default object2 = What()
default object3 = What()
default npc_list = [object1, object2, object3]
I dunno how you've structured your paramaters, should be interesting to see it.

THEN try to think inversely instead of rushing to code.
Always mind this: python devs already thought of this problem and surely created a method for that, a linear easy method. And is true, they did.

You want for any of such object to be checked if they own "thing" and if npc variable1 is False.
So, eliminate the ones that doesn't pass such requirements.

Code: Select all

label whatwhat:
    $ enemies = [x for x in npc_list if "thing" in x.equipments and not x.variable1]
This create a list of just the objects that pass the first check.
Now let's begin the cycle.

Code: Select all

    while enemies:
        $ this_enemy = enemies.pop[0]
        if not this_enemy.variable2 and not this_enemy.variable3: 
            # whatever you want to happen in normal renpy code...
    "finished!"
...and done.
JUST that.
Really.
No python blocks.

You've learned:
  • you need less python that you think (python is high level language, usually do a lot of stuff with limited number of statements).
    list comprehension (that [x for x] thing).
    that SORTING out stuff from lists (like in such list comprehension) is probably 85% of python you'll ever need, even if no one tell.
    sorting is lightweight, as you manage just the object references, not the entire objects, so sort out as much as you want instead of using IF to exclude elements.
    aliasing allow you to abstract the code a lot.
    while cycle work better than FOR, sometime.
    that if something is True, you can check it by just "if this_thing:". To check if False, just "if not this_thing:".
    that a void list return False.
    that pop[0] pick and remove the first item in a list (you don't need that list, after the loop).
I hope that this incite some curiosity into abstract python coding, the moment such level of abstraction trigger it become incredibly satisfying!
If you want to debate on a reply I gave to your posts, please QUOTE ME or i'll not be notified about. << now red so probably you'll see it.

10 ? "RENPY"
20 GOTO 10

RUN

User avatar
noeinan
Eileen-Class Veteran
Posts: 1153
Joined: Sun Apr 04, 2010 10:10 pm
Projects: Ren'Py QuickStart, Crimson Rue
Organization: Statistically Unlikely Games
Deviantart: noeinan
Github: noeinan
Location: Washington State, USA
Contact:

Re: Label Undefined when Calling from For Loop

#6 Post by noeinan »

No need to apologize at all, I actually super appreciate the corrections. So, I'm having a bit of a hard time understanding and part of it is that there is 18+ stuff in the code and so every time I've posted problems I've had to go through and change everything to make it PG, but due to that it becomes kinda hard to understand and follow. It's actually waaaay easier to follow in its original format, but I cannot use both the code and spoiler tags at the same time so it's just impossible to share in the questions forum without like, accidentally blindsiding innocent passerby.

I can't see any solution to that, so I'll just do my best to go over my code a bit more in depth and maybe try to figure out how to restructure this better? (Just didn't really understand the way it was phrased before, which is just on me being derpy.)

First, I have this really huge class for NPC generation. NPCs have a ton of different variables and stuff, so within each NPC I can change a bunch of different variables. (And different variables interact with each other.)

To get an idea of how the character generator works, here's a few pieces of it:

Code: Select all

init python:
    partnerno = 0

    class NPCList():

        def __init__(self, racelist, typelist, procaselist, level):

            self.race = renpy.random.choice(racelist) #Random race as determined by the area

            if self.race == "human" or self.race == "elf":
                self.racesize = 2
            elif self.race == "gnome":
                self.racesize = 1
            elif self.race == "fairy":
                self.racesize = 0
            elif self.race == "ogre":
                self.racesize = 3
            elif self.race == "centaur":
                self.racesize = 4
            elif self.race == "giant":
                self.racesize = 5
            elif self.race == "demon" or self.race == "spirit":
                self.racesize = renpy.random.randint(0,5)
            else:
                self.racesize = -1
To generate my NPCs, I literally just do this:

$ npc_dict = GenerateMultipleNPCs(3)

the number being how many I'm generating.

This will end up with me having something that is like this: npc_dict = [npc1, npc2, npc3]

It all works pretty much identical to what you did with the What(object) class, it's just more filled in.

I have a few different labels that feed into each other, allowing me to get a replayable "combat" type turn based mechanic. It basically goes like this:

Map (player moves to a new grid/square on the map)
v
event checker
v
event label
v
combat_start label (this is really more of the frame for everything)
v
next_turn label (changes variables every turn based on conditions, checks to see if you've met requirements for ending combat)
v
keep doing next_turn until conditions met for ending combat, then...
v
combat_finish label
v
goes back to combat_start and resets all necessary variables before exiting back to the map.

The code I'm working on is basically, inside the next_turn label, every turn there is a description of the NPCs actions and the player's actions for that turn. This is pretty elaborate, as npcs have different personality types that make them act different under different conditions and there's a bunch of different sections of the code for different actions they can take. (They don't necessarily only take one action per turn, they can take multiple actions under different conditions.)

I was originally considering rewriting everything so that the loop was inside the action description label, which seems like that's what's going on with that while loop you mentioned, but found that it was faster and easier to just loop the description label instead. Since everything is really elaborate, I'm not actually sure how much space/efficiency I'd save by switching to doing it that way? Also, I don't have any python blocks inside the action description, I only have it prior to the for loop. (But, if I used while, I understand I wouldn't need python at all, I just don't feel one small python block is in the way?)
Image

Image
Image

Post Reply

Who is online

Users browsing this forum: Lucyper