Requesting Assistance with Python OOP and a premade code that uses many Global Variables

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
TwiztidSinX
Newbie
Posts: 6
Joined: Fri Mar 08, 2024 10:43 am
Contact:

Requesting Assistance with Python OOP and a premade code that uses many Global Variables

#1 Post by TwiztidSinX »

This is going to be a lengthy post as I want to make sure that all information pertaining to the issue I am currently having is available. As well as what I am trying to achieve. Also what is going wrong, and the paths I have already taken to try to solve the issue on my own before coming here. So that maybe someone will see that I am at my wits end and be willing to assist me.

I am currently creating a new VN, I have created a KN before and understand the basic and slightly advanced form of coding. So I decided I would truly test myself with this new game and try to make it the best I possibly could. I watched over 60 hours of tutorials, literally 62 videos, from various series on youtube. If you have a tutorial on youtube I probably watched it. I was very interested in OOP as it seems to me at least to be the most advanced thing you can do with RenPy and combining RenPys screen language with OOP you can essentially do anything.

So I sat down and decided what I wanted to create. I decided on a game that would be essentially a sandbox. I wanted image buttons to move around the home, and the city as a whole. None of that teleporting around stuff. I wanted an Inventory System with drag and drop items, that works with NPC Venders. I want a scaling battle system. With missions that have scaling rewards and scaling enemies based on the difficulty the player chooses. Rank E missions being the lowest and Rank S the highest. I also wanted the player to have a dynamic party. So if you befriended someone in the game they could join your party. Also those joinable members to have unique skills to add a fun spice to it all.

I was able to get everything working how I wanted it to except for the 2 things that I simply had no clue how to make. The Battle System and Inventory. So I brought up a guide. Followed it to the letter and got it working. The issue was it did not scale well, which the uploader said it wouldn't in the video. Also they did not cover OOP and classes very well in the video so while I was able to get an idea of how it worked I was unable to truly understand it.

I then went and asked on a couple of different websites and even got desperate enough that I went and spent 3 days trying to get something working from chatgpt. When I finally got a response on a forum all I got was. "That's the easiest thing in the word, Look up for loops." To which I sent the code I had, which had for loops already in it. I told them that after going to chatgpt it only made things worse. The only other reply I got was "Thats you fault for going to chatgpt for python code."
After that I just typed what I was looking for into google and saw a reply to a 5 year old post. Where the reply was "Why don't you go to the lemma soft forums, they have a lot of people that can help you, and after a quick search I found 3 battle systems in the RenPy Cookbook section which is prebuilt ready to use code.
I saw that and was extremely excited. So I came right over. Went through the cookbook trying to find something I could edit to work the way I was trying to get it to work. I found 2 and apparently one was based on the other one. The RPG Battle System/Other Misc Features and Animated RPG Battle Engine 1.2

After looking through all the code I thought I had a good enough grasp on it to understand it, well I found it 4 days ago and it still isnt working the way I think it should.

So since the code has a section for a fixed battle I figured I could just copy that and make a new instance of it for each of the "Ranks". Add an unlocked variable to have a character show up as a possible team mate. Add some more monsters, skills, and even code in some actual animations and it would be good to go. Well that is not how it is turning out.

Currently the I have the E and D Rank battles set up. I have it so that the player needs to beat the E Rank battles "4 Giant Bats" 25 times to unlock the D Rank battles "2 Icefall Treants" that part works. Well unlocked the button works, and facing the bats works. But as soon as I beat battle number 25 and move over to the D Rank missions it is still 4 Giant Bats. The Icefall Treants do not work. But if I stay in the E rank until battle 27 then go to D Rank, I get 1 battle Vs the 4 bats then everything after that there is nothing. Like the Treants are "There" they can hit the player. But their images are not there, and they are not in a battle slot so the player can not hit them back.

So now with the backstory all out of the way. I have tried rewritting sections of the code, moving bits around, I noticed if I use the command call load_monsters in the dev console it will give me 1 more random battle with the bats. Which had me thinking about persistent variables. Mainly because I also noticed if I change the monsters names and stats in the script after a save game is made the changes never take effect. But if I create a new game the changes are there. The only explanation I can come up with for that behavior is persistent variables....right? I mean I am not under any false impression that I am amazing at coding. I would classify myself as just barely above a novice. But that makes sense to me? I also noticed in the code that it is calling and defining globals a lot. After many hours of research I came across a post saying that global variables are not persistent for the save, but they are saved until the application is closed? So that made me even more confused.

So what is following is the changes I made to the code and everything that pertains to the E and D Rank battles. I tried to follow the way the original author Habitacle did. Any help that anyone can give, even so much as just pointing me in the right direction for where I can solve this and getting it working the way I want it to would be so very appreciated.

Load Setup area where I added e-sranksets following the fixedset example put in place already.

Code: Select all

label load_setup:
    if not name:
        $ name = "Player"
    $ a.name = name
    $ magicheal.addSkill(a) # add new skills
    $ defenseup.addSkill(c)
    $ magicswap.addSkill(y)
    call load_monsters
    call load_items
    $ party_list = [a,y,c,f,r] # initial party list, including main character
    $ fixedset = "set 1"
    $ erankset = "set 2"
    $ drankset = "set 3"
    $ crankset = "set 4"
    $ brankset = "set 5"
    $ arankset = "set 6"
    $ srankset = "set 7"
    $ wild_monsters = [mon1,mon2,mon3,mon4,mon5,mon6,mon7,mon8,mon9,mon10,mon11]
    $ restorehp()
    $ restoremp()
    return
Load Setup is being called right after label start.
label start:
call load_setup


This section of code covers the Labels and runs a check to make sure it is not too late.

Code: Select all

label erank():
    if timeOfDay >= 5:
        rudy "It is too late I need to sleep"
        jump quest_board
    else:
        call battleE
        $ erankset = "set 2"
        $ restorehp()
        $ restoremp()
        jump quest_board
label drank():
    if timeOfDay >= 5:
        rudy "It is too late I need to sleep"
        jump quest_board
    else:
        call battleD
        $ drankset = "set 3"
        $ restorehp()
        $ restoremp()
        jump quest_board
label crank():
    if timeOfDay >= 5:
        rudy "It is too late I need to sleep"
        jump quest_board
    else:
        call battleC
        $ crankset = "set 4"
        $ restorehp()
        $ restoremp()
        jump quest_board
label brank():
    if timeOfDay >= 5:
        rudy "It is too late I need to sleep"
        jump quest_board
    else:
        call battleB
        $ brankset = "set 5"
        $ restorehp()
        $ restoremp()
        jump quest_board
label arank():
    if timeOfDay >= 5:
        rudy "It is too late I need to sleep"
        jump quest_board
    else:
        call battleA
        $ arankset = "set 6"
        $ restorehp()
        $ restoremp()
        jump quest_board
label srank():
    if timeOfDay >= 5:
        rudy "It is too late I need to sleep"
        jump quest_board
    else:
        call battleS
        $ srankset = "set 7"
        $ restorehp()
        $ restoremp()
        jump quest_board
label quest_board():
        scene questboard with dissolve
        call screen quest_board
        return
screen quest_board():
    use topBar
    if erank_unlocked == True:
        imagebutton auto "erankjob_%s":
            focus_mask True
            tooltip "E-Ranked Mission"
            action Call('erank')
    if drank_unlocked == True:
        imagebutton auto "drankjob_%s":
            focus_mask True
            tooltip "D-Ranked Mission"
            action Call('drank')
    if crank_unlocked == True:
        imagebutton auto "crankjob_%s":
            focus_mask True
            tooltip "C-Ranked Mission"
            action Call('crank')
    if brank_unlocked == True:
        imagebutton auto "brankjob_%s":
            focus_mask True
            tooltip "B-Ranked Mission"
            action Call('brank')
    if arank_unlocked == True:
        imagebutton auto "arankjob_%s":
            focus_mask True
            tooltip "A-Ranked Mission"
            action Call('arank')
    if srank_unlocked == True:
        imagebutton auto "srankjob_%s":
            focus_mask True
            tooltip "S-Ranked Mission"
            action Call('srank')
$ tooltip = GetTooltip()
if tooltip:
    text "[tooltip]"
    return
Labels that cover which battle scenario should be called.

Code: Select all

label battleS:
    $ stopEvent()
    if srankset:
        $ monstersSRank()
        $ monster_slot = [m1,m2,m3,m4,m5,m6,m7,m8]
        $ srankset = None
    $ asignPos()
    $ row1btn = False
    $ row2btn = False
    $ missed_t = []
    $ win = False
    $ battleEnd = False
    $ monsters_dead = 0
    $ currentplayer = None
    show screen battle_tooltip

label battleA:
    $ stopEvent()
    if arankset:
        $ monstersARank()
        $ monster_slot = [m1,m2,m3,m4,m5,m6,m7,m8]
        $ arankset = None
    $ asignPos()
    $ row1btn = False
    $ row2btn = False
    $ missed_t = []
    $ win = False
    $ battleEnd = False
    $ monsters_dead = 0
    $ currentplayer = None
    show screen battle_tooltip

label battleB:
    $ stopEvent()
    if brankset:
        $ monstersBRank()
        $ monster_slot = [m1,m2,m3,m4,m5,m6,m7,m8]
        $ brankset = None
    $ asignPos()
    $ row1btn = False
    $ row2btn = False
    $ missed_t = []
    $ win = False
    $ battleEnd = False
    $ monsters_dead = 0
    $ currentplayer = None
    show screen battle_tooltip

label battleC:
    $ stopEvent()
    if crankset:
        $ monstersCRank()
        $ monster_slot = [m1,m2,m3,m4,m5,m6,m7,m8]
        $ crankset = None
    $ asignPos()
    $ row1btn = False
    $ row2btn = False
    $ missed_t = []
    $ win = False
    $ battleEnd = False
    $ monsters_dead = 0
    $ currentplayer = None
    show screen battle_tooltip

label battleD:
    $ stopEvent()
    $ drankset
    call load_monsters
    $ monstersDRank()
    $ monster_slot = [m1,m2,m3,m4,m5,m6,m7,m8]
    $ drankset = None
    $ asignPos()
    $ row1btn = False
    $ row2btn = False
    $ missed_t = []
    $ win = False
    $ battleEnd = False
    $ monsters_dead = 0
    $ currentplayer = None
    show screen battle_tooltip
#
#
label battleE:
    $ stopEvent()
    $ erankset
    call load_monsters
    $ monstersERank()
    $ monster_slot = [m1,m2,m3,m4,m5,m6,m7,m8]
    $ erankset = None
    $ asignPos()
    $ row1btn = False
    $ row2btn = False
    $ missed_t = []
    $ win = False
    $ battleEnd = False
    $ monsters_dead = 0
    $ currentplayer = None
    show screen battle_tooltip
I Also tried doing the same thing but like this and it did not work so I commented it out.

Code: Select all

#label battle:
#    $ stopEvent()
#    if fixedset:
#        $ monstersFixed()
#        $ monster_slot = [m1,m2,m3,m4,m5,m6,m7,m8]
#        $ fixedset = None
#    if erankset:
#        $ monstersERank()
#        $ monster_slot = [m1,m2,m3,m4,m5,m6,m7,m8]
#        $ erankset = None
#    if drankset:
#        $ monstersDRank()
#        $ monster_slot = [m1,m2,m3,m4,m5,m6,m7,m8]
#        $ drankset = None
#    if crankset:
#        $ monstersCRank()
#        $ monster_slot = [m1,m2,m3,m4,m5,m6,m7,m8]
#        $ crankset = None
#   if brankset:
#        $ monstersBRank()
#        $ monster_slot = [m1,m2,m3,m4,m5,m6,m7,m8]
#        $ brankset = None
#    if arankset:
#        $ monstersARank()
#        $ monster_slot = [m1,m2,m3,m4,m5,m6,m7,m8]
#        $ arankset = None
#    if srankset:
#        $ monstersSRank()
#        $ monster_slot = [m1,m2,m3,m4,m5,m6,m7,m8]
#        $ srankset = None
#    $ asignPos()
#    $ row1btn = False
#    $ row2btn = False
#    $ missed_t = []
#    $ win = False
#    $ battleEnd = False
#    $ monsters_dead = 0
#    $ currentplayer = None
#    show screen battle_tooltip
This section covers what happens at the end of each battle.

Code: Select all

label end_battle:
    hide screen battle_overlay
    with dissolve
    if win:
        stop music
        play sound fanfare
        "You win!"
        stop sound
        $ expFormula()
        $ Energy -= 10
        $ clean -= 10
        $ food -=10
        $ timeOfDay += 1
        if erank_unlocked and erank_mission <= 26:
            $ erank_mission += 1
        if erank_mission == 25:
            $ drank_unlocked = True
            $ renpy.notify("Promoted to D-Rank adventurer!")
        if drank_unlocked == True and drank_mission <= 26:
            $ drank_mission += 1
        if drank_mission == 25:
            $ crank_unlocked = True
            $ renpy.notify("Promoted to C-Rank adventurer!")
        if crank_unlocked == True and crank_mission <= 26:
            $ crank_mission += 1
        if crank_mission == 25:
            $ brank_unlocked = True
            $ renpy.notify("Promoted to B-Rank adventurer!")
        if brank_unlocked == True and brank_mission <= 26:
            $ brank_mission += 1
        if brank_mission == 25:
            $ arank_unlocked = True
            $ renpy.notify("Promoted to A-Rank adventurer!")
        if arank_unlocked == True and arank_mission <= 26:
            $ arank_mission += 1
        if arank_mission == 25:
            $ srank_unlocked = True
            $ renpy.notify("Promoted to S-Rank adventurer!")
        if srank_unlocked:
            $ srank_mission += 1
This section covers the defines for the battle section, with monster locations on screen, which monsters they should be, and all that.

Code: Select all

init python:
    def monstersFixed():
        global monsters_total
        global battle_monsters
        global m1
        global m2
        global m3
        global m4
        global m5
        global m6
        global m7
        global m8
        m1 = copy.deepcopy(empty)
        m2 = copy.deepcopy(empty)
        m3 = copy.deepcopy(empty)
        m4 = copy.deepcopy(empty)
        m5 = copy.deepcopy(empty)
        m6 = copy.deepcopy(empty)
        m7 = copy.deepcopy(empty)
        m8 = copy.deepcopy(empty)
        if fixedset == "set 1":
            m2 = copy.deepcopy(mon4)
            m3 = copy.deepcopy(mon5)
            m4 = copy.deepcopy(mon6)
            m5 = copy.deepcopy(mon7)
            m6 = copy.deepcopy(mon8)
            m7 = copy.deepcopy(mon3)
            battle_monsters = [m2,m3,m4,m5,m6,m7]
        else:
            m1 = copy.deepcopy(mon1)
            battle_monsters = [m1]
        monsters_total = len(battle_monsters)
        for m in battle_monsters:
            if m.name:
                m._hp = m.hpmax
    def monstersERank():
        global monsters_total
        global battle_monsters
        global m1
        global m2
        global m3
        global m4
        global m5
        global m6
        global m7
        global m8
        m1 = copy.deepcopy(empty)
        m2 = copy.deepcopy(empty)
        m3 = copy.deepcopy(empty)
        m4 = copy.deepcopy(empty)
        m5 = copy.deepcopy(empty)
        m6 = copy.deepcopy(empty)
        m7 = copy.deepcopy(empty)
        m8 = copy.deepcopy(empty)
        if erankset == "set 2":
            m1 = copy.deepcopy(mon2)
            m2 = copy.deepcopy(mon2)
            m3 = copy.deepcopy(mon2)
            m4 = copy.deepcopy(mon2)
            battle_monsters = [m1,m2,m3,m4]
        monsters_total = len(battle_monsters)
        for m in battle_monsters:
            if m.name:
                m._hp = m.hpmax
    def monstersDRank():
        global monsters_total
        global battle_monsters
        global m1
        global m2
        global m3
        global m4
        global m5
        global m6
        global m7
        global m8
        m1 = copy.deepcopy(empty)
        m2 = copy.deepcopy(empty)
        m3 = copy.deepcopy(empty)
        m4 = copy.deepcopy(empty)
        m5 = copy.deepcopy(empty)
        m6 = copy.deepcopy(empty)
        m7 = copy.deepcopy(empty)
        m8 = copy.deepcopy(empty)
        if drankset == "set 3":
            m5 = copy.deepcopy(mon3)
            m6 = copy.deepcopy(mon3)
            battle_monsters = [m5,m6]
        monsters_total = len(battle_monsters)
        for m in battle_monsters:
            if m.name:
                m._hp = m.hpmax
    def monstersCRank():
        global monsters_total
        global battle_monsters
        global m1
        global m2
        global m3
        global m4
        global m5
        global m6
        global m7
        global m8
        m1 = copy.deepcopy(empty)
        m2 = copy.deepcopy(empty)
        m3 = copy.deepcopy(empty)
        m4 = copy.deepcopy(empty)
        m5 = copy.deepcopy(empty)
        m6 = copy.deepcopy(empty)
        m7 = copy.deepcopy(empty)
        m8 = copy.deepcopy(empty)
        if crankset == "set 4":
            m1 = copy.deepcopy(mon4)
            m2 = copy.deepcopy(mon4)
            m3 = copy.deepcopy(mon4)
            m4 = copy.deepcopy(mon4)
            m5 = copy.deepcopy(mon4)
            m6 = copy.deepcopy(mon4)
            m7 = copy.deepcopy(mon4)
            m8 = copy.deepcopy(mon4)
            battle_monsters = [m1,m2,m3,m4,m5,m6,m7,m8]
        else:
            m1 = copy.deepcopy(mon1)
            battle_monsters = [m1]
        monsters_total = len(battle_monsters)
        for m in battle_monsters:
            if m.name:
                m._hp = m.hpmax
    def monstersBRank():
        global monsters_total
        global battle_monsters
        global m1
        global m2
        global m3
        global m4
        global m5
        global m6
        global m7
        global m8
        m1 = copy.deepcopy(empty)
        m2 = copy.deepcopy(empty)
        m3 = copy.deepcopy(empty)
        m4 = copy.deepcopy(empty)
        m5 = copy.deepcopy(empty)
        m6 = copy.deepcopy(empty)
        m7 = copy.deepcopy(empty)
        m8 = copy.deepcopy(empty)
        if brankset == "set 5":
            m1 = copy.deepcopy(mon5)
            m2 = copy.deepcopy(mon5)
            m3 = copy.deepcopy(mon5)
            m4 = copy.deepcopy(mon5)
            m5 = copy.deepcopy(mon5)
            m6 = copy.deepcopy(mon5)
            battle_monsters = [m1,m2,m3,m4,m5,m6]
        else:
            m1 = copy.deepcopy(mon1)
            battle_monsters = [m1]
        monsters_total = len(battle_monsters)
        for m in battle_monsters:
            if m.name:
                m._hp = m.hpmax
    def monstersARank():
        global monsters_total
        global battle_monsters
        global m1
        global m2
        global m3
        global m4
        global m5
        global m6
        global m7
        global m8
        m1 = copy.deepcopy(empty)
        m2 = copy.deepcopy(empty)
        m3 = copy.deepcopy(empty)
        m4 = copy.deepcopy(empty)
        m5 = copy.deepcopy(empty)
        m6 = copy.deepcopy(empty)
        m7 = copy.deepcopy(empty)
        m8 = copy.deepcopy(empty)
        if arankset == "set 6":
            m1 = copy.deepcopy(mon6)
            m2 = copy.deepcopy(mon6)
            m3 = copy.deepcopy(mon6)
            m4 = copy.deepcopy(mon6)
            m5 = copy.deepcopy(mon6)
            battle_monsters = [m1,m2,m3,m4,m5]
        else:
            m1 = copy.deepcopy(mon1)
            battle_monsters = [m1]
        monsters_total = len(battle_monsters)
        for m in battle_monsters:
            if m.name:
                m._hp = m.hpmax
    def monstersSRank():
        global monsters_total
        global battle_monsters
        global m1
        global m2
        global m3
        global m4
        global m5
        global m6
        global m7
        global m8
        m1 = copy.deepcopy(empty)
        m2 = copy.deepcopy(empty)
        m3 = copy.deepcopy(empty)
        m4 = copy.deepcopy(empty)
        m5 = copy.deepcopy(empty)
        m6 = copy.deepcopy(empty)
        m7 = copy.deepcopy(empty)
        m8 = copy.deepcopy(empty)
        if srankset == "set 7":
            m1 = copy.deepcopy(mon7)
            m2 = copy.deepcopy(mon7)
            battle_monsters = [m1,m2]
        else:
            m1 = copy.deepcopy(mon1)
            battle_monsters = [m1]
        monsters_total = len(battle_monsters)
        for m in battle_monsters:
            if m.name:
                m._hp = m.hpmax
This section really only covers the defaults, I am mainly including it so anyone reading knows that I did give the additions I made defaults in the same place as what I was following.

Code: Select all

default srankset = None
default arankset = None
default brankset = None
default crankset = None
default drankset = None
default erankset = None
default fixedset = None
default tt_timer = False
default damage = 0
default m_damage = 0
default dropitem = None
default atk_sfx = None
default mp_lost = 0
default hp_lost = 0
default players = 1
default monsters_total = 0
default monsters_dead = 0
default b_skill = "none"
default message = "none"
default target = "none"
default picked_targs = []
default party_list = []
default wild_monsters = []
default battle_players = []
default alive_players = []
default battle_monsters = []
default misstext_list = ["MiSs!", "MisS!", "mISs!", "mIsS!"]
default diss = Dissolve(.2)
And since the issue does in a way have to do with the monsters I figured I should add in the code that pertains to them as well

Code: Select all

label load_monsters:
    # var = Monster(name, hpmax, atk, dfn, exp, lvl, img, sfx_atk, anim, skills)
    $ empty = Monster(None, None, None, None, None, None, None, None, dead=True)
    $ mon1 = Monster("Treant", 20, 15, 1.0, 50, 3, "1", "water", anim=slow_sway, skills=[arrowhail])
    $ mon2 = Monster("Giant Bat", 50, 20, 6.0, 50, 5, "2", "pound", anim=squeeze, skills=[lifedrain])
    $ mon3 = Monster("Icefall Treant", 300, 40, 5.0, 75, 10, "3", "tackle", anim=idle_shake, skills=[lifedrain])
    $ mon4 = Monster("Snow Buffalo", 250, 10, 10.0, 100, 20, "4", "water", anim=idle_y, skills=[deathmissile])
    $ mon5 = Monster("Luster Grizzly", 450, 35, 10.0, 75, 30, "5", "thunder", anim=idle_xy, skills=[rockthrow])
    $ mon6 = Monster("Snow Drake", 700, 75, 15.0, 100, 40, "6", "fire", anim=idle_x, skills=[swordofdeath])
    $ mon7 = Monster("Snow Dragon", 1500, 100, 20.0, 500, 50, "7", "cut", anim=idle_shake, skills=[lavaburst])
    $ mon8 = Monster("Elder Treant", 55, 25, 2.0, 50, 4, "8", "scratch", anim=idle_shake, skills=[mindburn])
    $ mon9 = Monster("Earth Dragon", 60, 40, 3.0, 50, 6, "9", "leaf", anim=idle_xy, skills=[lavaburst])
    $ mon10 = Monster("Charizard", 90, 95, 4.0, 50, 8, "10", "fire", anim=idle_shake, skills=[deathmissile])
    $ mon11 = Monster("Sea Dragon", 85, 85, 5.0, 50, 5, "11", "water", anim=idle_x, skills=[thunderbolt])
    return
init python:
    class Monster(object):
        def __init__(self, name, hpmax, atk, dfn, exp, lvl, img, sfx_atk, anim=idle_shake, skills=[], state=None, dead=False, finaldmg=0, slot=1, sprite_pos=0, dmg_pos=(0,0)):
            self.name = name
            self.hpmax = hpmax
            self._hp = 0
            self._mp = 0
            self.atk = atk
            self.dfn = dfn
            #self.vel = vel
            self.state = state
            self.lvl = lvl
            self.exp = exp
            self.dead = dead
            self.skills = skills
            self.img = img
            self.sfx_atk = sfx_atk
            #self.sfx_cry = sfx_cry
            #self.sfx_die = sfx_die
            self.finaldmg = finaldmg
            self.slot = slot
            self.anim = anim
            self.sprite_pos = sprite_pos
            self.dmg_pos = dmg_pos
            #self.rarity = rarity
        @property
        def hp(self):
            value = self._hp
            if not ( 0 <= value <= self.hpmax ):
                value = max( 0, min( self.hpmax, value ) )
                self._hp = value
            return self._hp
Ok that should just about cover everything, If anyone is willing to help again I am truly thankful, if I did not provide enough of the code I am more than happy to just send all of it, but as these are the values I adjusted I am assuming that I made a mistake somewhere, so I hope this is enough.

* I edited the Subject as I felt it may not have been clear enough.

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

Re: Requesting Assistance with Python OOP and a premade code that uses many Global Variables

#2 Post by jeffster »

The code seems to be complicated. Following examples and tutorials is useful to understand how to do things, but note that they can present not the best ways.

Please continue attempts to improve the code. If they don't work, try to understand why.

A few bits of advice:

1. Instead of many strings starting with "$", use "python:" blocks.

2. Why this doesn't work?:

Code: Select all

label battle:
    python:
        stopEvent()
        if fixedset:
            monstersFixed()
            monster_slot = [m1,m2,m3,m4,m5,m6,m7,m8]
            fixedset = None
        if erankset:
            monstersERank()
            monster_slot = [m1,m2,m3,m4,m5,m6,m7,m8]
            erankset = None
Because probably fixedset has some value that makes "if fixedset:" true, and its block runs.
And then probably erankset has some value that makes "if erankset:" true, and its block runs.
And so on; all "if" blocks run, and they rewrite the same data, one after another (monster_slot, for example).
You have to use "elif" instead of "if" (or jump somewhere at the end of the "if" block), if you want the blocks to work as choosing alternatives.

But even then, what exactly should that code do?

What exactly do those variables do: fixedset, erankset etc?

You should understand the logic, the mechanism - how it works - then you could see how to improve it and how to correct errors.

3. Again, in this code:

Code: Select all

label battleS:
    $ stopEvent()
    if srankset:
        $ monstersSRank()
        $ monster_slot = [m1,m2,m3,m4,m5,m6,m7,m8]
        $ srankset = None
    $ asignPos()
    $ row1btn = False
    $ row2btn = False
    $ missed_t = []
    $ win = False
    $ battleEnd = False
    $ monsters_dead = 0
    $ currentplayer = None
    show screen battle_tooltip

label battleA:
After the block of "label battleS" runs, the next line goes to be executed, and it's "label battleA".
Therefore "label battleA" runs too. Correct?
There it rewrites again some data set in "label battleS".
Why?
Or did you just drop there pieces of code that they only look like continuous script?

4. Functions like this don't make much sense:

Code: Select all

    def monstersSRank():
        global monsters_total
        global battle_monsters
        global m1
        global m2
        global m3
        global m4
        global m5
        global m6
        global m7
        global m8
        m1 = copy.deepcopy(empty)
        m2 = copy.deepcopy(empty)
        m3 = copy.deepcopy(empty)
        m4 = copy.deepcopy(empty)
        m5 = copy.deepcopy(empty)
        m6 = copy.deepcopy(empty)
        m7 = copy.deepcopy(empty)
        m8 = copy.deepcopy(empty)
        if srankset == "set 7":
            m1 = copy.deepcopy(mon7)
            m2 = copy.deepcopy(mon7)
            battle_monsters = [m1,m2]
        else:
            m1 = copy.deepcopy(mon1)
            battle_monsters = [m1]
        monsters_total = len(battle_monsters)
        for m in battle_monsters:
            if m.name:
                m._hp = m.hpmax
What are you trying to do?
Thoroughly filling global lists with empty values, and then never using those values?
And doing it again and again, when you call such functions... What for?

5. You are correct that you don't need those globals.

6. You set values with "default" statement, and immediately (at the game start) rewrite them with other values (at "label load_setup:").

What do these values mean?:

Code: Select all

    $ fixedset = "set 1"
    $ erankset = "set 2"
    $ drankset = "set 3"
    $ crankset = "set 4"
    $ brankset = "set 5"
    $ arankset = "set 6"
    $ srankset = "set 7"
What for do you do that?

7. To solve a complex task, try to split the task into small parts and test them one by one.

For example, you have some function to do battle.

Which parameters should it take to do battle at rank E?
At rank D?

If you know that, create a little test program that would give you those parameters.
If you make that script work, you could use it to feed proper data to your battle function.

I could imagine it like this:

Code: Select all

define RANKE = 0
define RANKD = 1
define RANKC = 2
define RANKB = 3
define RANKA = 4
define RANKS = 5

default current_rank = RANKE

label fight:
    python:
        result = battle(current_rank)
        if result == 0:
            # Battle lost
            progress += 1
        else:
            # Battle won
            progress += 2

        if progress >= 25 and current_rank < RANKS:
            current_rank += 1
            progress = 0
            renpy.notify("Your mastery improved. Expect harder battles!")
    return

init python:
    def battle(rank=0):
        monsters = copy.deepcopy(rank_monsters[rank])
        return renpy.call_screen("battle", monsters)

define rank_monsters = [
    [
        Monster("Giant Bat", 50, 20, 6.0, 50, 5, "2", "pound", anim=squeeze, skills=[lifedrain]),
        Monster("Giant Bat", 50, 20, 6.0, 50, 5, "2", "pound", anim=squeeze, skills=[lifedrain])
        ],
    [
        Monster("Icefall Treant", 300, 40, 5.0, 75, 10, "3", "tackle", anim=idle_shake, skills=[lifedrain]),
        Monster("Icefall Treant", 300, 40, 5.0, 75, 10, "3", "tackle", anim=idle_shake, skills=[lifedrain])
        ],
    # And so on for ranks C, B, A, S
    ]

screen battle(monsters):
    add monsters[0].picture at left
    add monsters[1].picture at right
    hbox:
        texbutton "Win" action Return(1)
        texbutton "Lose" action Return(0)
Of course this is a simplified example: the screen "battle" should just show the monsters for your rank battle, so you can check that it gets proper data.

And I assumed that Monster() object would have ".picture" property, maybe a string of a file name depicting that monster.
If the problem is solved, please edit the original post and add [SOLVED] to the title. 8)

TwiztidSinX
Newbie
Posts: 6
Joined: Fri Mar 08, 2024 10:43 am
Contact:

Re: Requesting Assistance with Python OOP and a premade code that uses many Global Variables

#3 Post by TwiztidSinX »

Yea as I said I truly do not understand the code in it's entirety which is why I am asking for help. Essentially it is code from the renpy cookbook. What I want it to do is you walk up to a wall with papers on the wall. The papers are image buttons, one for each rank. But the image buttons are not clickable until they are unlocked by getting 25 wins in the rank before it. So 25x beating rank e unlocks rank d and so on. To the best of my knowledge by tracking where the code bounces around it should work like this:

At the start of the game it runs the Load Setup.

Code: Select all

label start:
    call load_setup
The set up defines players name, and loads the monsters and items, while also assigning the player list, skills, and initializing the sets?

Code: Select all

label load_setup:
    if not name:
        $ name = "Player"
    $ a.name = name
    $ magicheal.addSkill(a) # add new skills
    $ defenseup.addSkill(c)
    $ magicswap.addSkill(y)
    call load_monsters
    call load_items
    $ party_list = [a,y,c,f,r] # initial party list, including main character
    $ fixedset = "set 1"
    $ erankset = "set 2"
    $ drankset = "set 3"
    $ crankset = "set 4"
    $ brankset = "set 5"
    $ arankset = "set 6"
    $ srankset = "set 7"
    $ wild_monsters = [mon1,mon2,mon3,mon4,mon5,mon6,mon7,mon8,mon9,mon10,mon11]
    $ restorehp()
    $ restoremp()
    return
Then you enter into the quest board area and get the quest board screen

Code: Select all

label quest_board():
        scene questboard with dissolve
        call screen quest_board
        return
Which will make different imagebuttons visible based on the missions completed per rank variables.

Code: Select all

screen quest_board():
    use topBar
    if erank_unlocked == True:
        imagebutton auto "erankjob_%s":
            focus_mask True
            tooltip "E-Ranked Mission"
            action Call('erank')
    if drank_unlocked == True:
        imagebutton auto "drankjob_%s":
            focus_mask True
            tooltip "D-Ranked Mission"
            action Call('drank')
    if crank_unlocked == True:
        imagebutton auto "crankjob_%s":
            focus_mask True
            tooltip "C-Ranked Mission"
            action Call('crank')
    if brank_unlocked == True:
        imagebutton auto "brankjob_%s":
            focus_mask True
            tooltip "B-Ranked Mission"
            action Call('brank')
    if arank_unlocked == True:
        imagebutton auto "arankjob_%s":
            focus_mask True
            tooltip "A-Ranked Mission"
            action Call('arank')
    if srank_unlocked == True:
        imagebutton auto "srankjob_%s":
            focus_mask True
            tooltip "S-Ranked Mission"
            action Call('srank')
$ tooltip = GetTooltip()
if tooltip:
    text "[tooltip]"
    return
By clicking for example (mainly as these are the only 2 I finished before I saw it all breaking apart...) E or D rank missions. For this example lets say we clicked on D rank. If it is greater than or equal to time variable 5, it tells the player to go to sleep and returns to quest board screen otherwise it calls battleD and sets itself to the drank set. While also restoring player hp and mp.

Code: Select all

label erank():
    if timeOfDay >= 5:
        rudy "It is too late I need to sleep"
        jump quest_board
    else:
        call battleE
        $ erankset = "set 2"
        $ restorehp()
        $ restoremp()
        jump quest_board
label drank():
    if timeOfDay >= 5:
        rudy "It is too late I need to sleep"
        jump quest_board
    else:
        call battleD
        $ drankset = "set 3"
        $ restorehp()
        $ restoremp()
        jump quest_board
Which brings us to this label where it is stopping event? Assigning the drankset again? or maybe verifying its the drank set? reloading the monsters to ensure its the right ones. Setting the variable for monstersDRank, Saying that monsters can be in any slot 1-8, I have tried with and without that drankset = none thing it doesn't seem to do anything? But it was in the original code so I left it in? It assigns the positions on the screen for where the monsters are, and sets variables for rows, missing, and win?

Code: Select all

label battleD:
    $ stopEvent()
    $ drankset
    call load_monsters
    $ monstersDRank()
    $ monster_slot = [m1,m2,m3,m4,m5,m6,m7,m8]
    $ drankset = None
    $ asignPos()
    $ row1btn = False
    $ row2btn = False
    $ missed_t = []
    $ win = False
    $ battleEnd = False
    $ monsters_dead = 0
    $ currentplayer = None
    show screen battle_tooltip
And this is where I get completely lost. All I can tell whats happening here is it is defining what monstersDRank is. All the globals is way to advanced for me and I have no clue what it does. It looks like it is defining what m1-m8 are though? then I no clue what copy.deepcopy is? but Again looks like its defining what m1-m8 are and its one of those where it was in the code for the fixed battle so I just copied it.
Next it is only running the next bit if drank is infact equal to set 3. Then in slots m5, and m6 it is calling mon3 Which is the icefall treant.
It is also saying the monsters_total = the length of the battle monsters, then for m in battle monsters m.name which I am assuming means the monsters name? The monsters hp = the max hp.

Code: Select all

    def monstersDRank():
        global monsters_total
        global battle_monsters
        global m1
        global m2
        global m3
        global m4
        global m5
        global m6
        global m7
        global m8
        m1 = copy.deepcopy(empty)
        m2 = copy.deepcopy(empty)
        m3 = copy.deepcopy(empty)
        m4 = copy.deepcopy(empty)
        m5 = copy.deepcopy(empty)
        m6 = copy.deepcopy(empty)
        m7 = copy.deepcopy(empty)
        m8 = copy.deepcopy(empty)
        if drankset == "set 3":
            m5 = copy.deepcopy(mon3)
            m6 = copy.deepcopy(mon3)
            battle_monsters = [m5,m6]
        monsters_total = len(battle_monsters)
        for m in battle_monsters:
            if m.name:
                m._hp = m.hpmax
I mean I think that is how it is supposed to work? Also I tried the elif thing before, and that made it so that it did not matter which imagebutton I clicked it would always go with the last one in the list. Since runs code sequentially. Which is why I was trying to break the code up to force it to have to run different labels trying to seperate it all but it didn't work out like I thought. Though you mentioned jumps. Can I do jumps in python blocks? Or is that like a RenPy only thing? Because that makes sense and theoretically that should work perfectly. This is one of those times where you stare at something so long and you need another viewing angle to see how to solve it. Though I am still super stumped by these globals what do they do? All I know is when I removed them the code no longer worked?

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

Re: Requesting Assistance with Python OOP and a premade code that uses many Global Variables

#4 Post by jeffster »

I think you could try to start building your own code, from the ground up. That way you will understand better and maybe come to the result faster than trying to beat your head against something alien.

Also you need to gain better understanding of Python and Ren'Py. It takes some time, and again, the best way is to practice with smaller and simpler examples.

In particular, it should be easy to create your own "quest_board" screen. The one that you have is incorrect here:

Code: Select all

action Call('erank')
calls label erank. "Call" means to go there and do script until "return" statement. Then you return to the place where "call" originated.

But in your script, instead of "return" at the end of "label erank" block, you have "jump".

Remember:
* "call" a label - and you must "return" in the end.
* "jump" to a label - and afterwards you can "jump" somewhere else.

call-return is a pair, where neither of the members can be replaced by jump, because "call" saves the return address to the stack; and "return" pops back that address from the stack. If you "call" and then "jump" back instead of "return", then stack gets piled up with unused return addresses, which can result in anything including program crash, eventually.
https://renpy.org/doc/html/label.html

Maybe it's better to use Jump() instead of Call() there in the screen, and after the battle block jump somewhere else.

Another error in "screen quest_board()" is that the tooltip part is not indented, so it's not a part of the screen. It doesn't work, right?

How you could do that screen:

Code: Select all

screen quest_board():
    use topBar

    imagebutton auto "erankjob_%s":
        focus_mask True
        tooltip "E-Ranked Mission"
        action Jump('erank')

    if current_rank >= RANKD:
        imagebutton auto "drankjob_%s":
            focus_mask True
            tooltip "D-Ranked Mission"
            action Jump('drank')

    if current_rank >= RANKC:
        imagebutton auto "crankjob_%s":
            focus_mask True
            tooltip "C-Ranked Mission"
            action Jump('crank')

    # ... and so on ...

    $ tooltip = GetTooltip()
    if tooltip:
        text "[tooltip]"
But instead of all those labels - erank, drank etc., it's better to use the same script.

Then the screen could be like that:

Code: Select all

screen quest_board():
    use topBar

    imagebutton auto "erankjob_%s":
        focus_mask True
        tooltip "E-Ranked Mission"
        action Return(RANKE)

    imagebutton auto "drankjob_%s":
        focus_mask True
        tooltip "D-Ranked Mission"
        action Return(RANKD)
        sensitive current_rank >= RANKD

    imagebutton auto "crankjob_%s":
        focus_mask True
        tooltip "C-Ranked Mission"
        action Return(RANKC)
        sensitive current_rank >= RANKC

    imagebutton auto "brankjob_%s":
        focus_mask True
        tooltip "B-Ranked Mission"
        action Return(RANKB)
        sensitive current_rank >= RANKB

    # ... and so on ...

    $ tooltip = GetTooltip()
    if tooltip:
        text "[tooltip]"
where "sensitive" sets the condition to define if the button is clickable or grayed out.

PS. Now as we used Return(rank) in the screen above, we call that screen thusly:

Code: Select all

label quest_board():
        scene questboard with dissolve
        call screen quest_board

        # Now variable "_return" has the return value (RANKE or RANKD and so on).
        # We can do like this:
        # if _return == RANKE:
        #     call erank
        # elif _return == RANKD:
        #     call drank
        # And so on.

        # But like I said in the previous comment,
        # it's better to use the same screen or function for all ranks:
        call fight(_return)

        # ...and so on. See the previous comment.

        jump quest_board     # or somewhere else
Can I do jumps in python blocks? Or is that like a RenPy only thing?
Nope, unlike many other languages Python doesn't have jumps. Only various loops and function calls. But you can jump after the python block:

Code: Select all

label some:
    python:
        this = that
        call_me(with_something)
        blah_blah = "Wow!"
    jump somewhere_else
About "global" and "copy.deepcopy", please search and read some Python manuals, they are more informative than my brief attempts to explain.

PS. I didn't mean that you can just delete those lines ("global m1" etc.)
I meant you don't need those variables at all, if you rewrite the code properly.
Sorry for being unclear...
If the problem is solved, please edit the original post and add [SOLVED] to the title. 8)

TwiztidSinX
Newbie
Posts: 6
Joined: Fri Mar 08, 2024 10:43 am
Contact:

Re: Requesting Assistance with Python OOP and a premade code that uses many Global Variables

#5 Post by TwiztidSinX »

Yea I actually tried making my own for a few days first and I couldn't get it to work the way I wanted it to which was when I decided to shift to trying to learn a prebuilt one that was built by someone with much more knowledge than I had. Also I do know the call return thing. But when I had returns there instead it was just returning me to the main menu. So I swapped it out for jump and that solved that issue. Though Yes I did not know the thing about having to many call jumps would crash the game. So that was 1 thing you taught me that was new the second thing you taught me was sensitive I had no idea that was a thing. I had it the way I did because when I had it as if elif statements it was turning the imagebuttons off, as I reached a new rank. Like when I hit 25 E rank missions complete it would unlock D rank and that button would appear but the E rank would stop working. When I swapped it to just a bunch of if statements instead it worked.

Essentially I have been slamming my head against the wall first for 6 days trying to build my own from scratch when I was trying to teach myself classes and OOPs.
Then 4 more days now with this prebuilt one. But I am not one to give up I will continue to learn until I am good at it. Which is why I want to force myself to learn this system since it is clearly more advanced than I am. Which makes me feel like if I can learn it I can learn anything right? (God next someone is going to tell me this isn't actually that advanced and make me see the vast difference in knowledge again.....lol)

So all I really need at this point to make things work the way they should work, is to figure out why it seems to be saving the E rank monsters and using those when D rank is unlocked. And why when D Rank finally works the monsters do not exist. When I can figure that little bit out then I should be able to solve all the issues with this and I can finally move on to building an inventory system. Another terrifying task that I will have to learn. Since I am being picky about it and I want it to be draggable so that I can add in vending machines and Shops into the world that have buying menus kinda like skyrim.

Oh also Idk why but yea the tool tip was in the wrong indentation but it was still working just fine for some reason. Actually when I put it in where I knew it should be that is actually when I was getting errors. Looking back that should have been hint number one something was off with this code. Like it's doing something that RenPy is not happy about. Also if I try to run the setup label after setup has started it crashes renpy with an error saying that m1-m8 is not defined. So thats also confusing because are they not being defined in global? So I think what I need to do is find a way to seperate the code so it does not go from 1 ranks block to the next. I wonder if I put them in different .rpy files if that will get around that issue? Still wont solve the monsters being invisible issue. Though each day it seems like I spend 12 hours solving 1 issue, solve that only to find 3 more.

TwiztidSinX
Newbie
Posts: 6
Joined: Fri Mar 08, 2024 10:43 am
Contact:

Re: Requesting Assistance with Python OOP and a premade code that uses many Global Variables

#6 Post by TwiztidSinX »

After some more trial and error I was able to confirm 2 things that are not good and probably why I was using the wrong code in the first place. As well as 1 very good thing that I was able to do. So 1 by indenting the tooltip as suggested the game crashes saying that tool tip is not a valid child of the screen argument or something similar. Sorry 10 days straight of very little sleep I can barely stay awake at this point. But I am sure you know what I am trying to say. Second thing is by putting the proper returns in, it does not return to the last call. It is returning to main menu. So apparently I did need the jumps there. I am pretty sure I tried that before and thats why I swapped it in the first place?

Code: Select all

label erank():
    if timeOfDay >= 5:
        rudy "It is too late I need to sleep"
        return
    else:
        call battleE
        $ erankset = "set 2"
        $ restorehp()
        $ restoremp()
        jump quest_board
label drank():
    if timeOfDay >= 5:
        rudy "It is too late I need to sleep"
        return quest_board
    else:
        call battleD
        $ drankset = "set 3"
        $ restorehp()
        $ restoremp()
        jump quest_board
But the good news is by moving down the list to after where it assigns which battle version should happen and making it a new label then adding a jump after each instance of battle version it is properly skipping to the bottom and it seems that the battles are showing up properly. This is the change I made

Code: Select all

label battleD:
    $ stopEvent()
    $ drankset
    call load_monsters
    $ monstersDRank()
    $ monster_slot = [m1,m2,m3,m4,m5,m6,m7,m8]
    $ drankset = None
    $ asignPos()
    $ row1btn = False
    $ row2btn = False
    $ missed_t = []
    $ win = False
    $ battleEnd = False
    $ monsters_dead = 0
    $ currentplayer = None
    show screen battle_tooltip
    jump battle
#
#
label battleE:
    $ stopEvent()
    $ erankset
    call load_monsters
    $ monstersERank()
    $ monster_slot = [m1,m2,m3,m4,m5,m6,m7,m8]
    $ erankset = None
    $ asignPos()
    $ row1btn = False
    $ row2btn = False
    $ missed_t = []
    $ win = False
    $ battleEnd = False
    $ monsters_dead = 0
    $ currentplayer = None
    show screen battle_tooltip
    jump battle
label battle:
    call battle_music

    random:
        scene bb1
        scene bb2
        scene bb3
    with pixellate

    call player_select
    show screen display_monsters with diss
    show screen battle_message
    show screen battle_overlay with diss
    jump battling

label player_select:
Though now I am back to an issue I had earlier that I did have solved then I broke it again. Well by solving it, it annoyed me so I tried a different method of solving it and that broke it. So originally I had a cap of 25 for the erank missions variable, and a renpy.notify telling the player they unlocked the d rank missions while also opening up that imagebutton. The issue I had before was any time I completed a mission after that it would ALWAYS notify about d rank missions being unlocked and not specifically ONLY when the player hit 25 for the first time. Which is when I changed it to 26 max and had the way I do now. But simply by changing that one variable now it isn't unlocking the D rank missions. (Side note this is my own coding nothing to do with the premade code so 100% me making an error here....)

Code: Select all

label end_battle:
    hide screen battle_overlay
    with dissolve
    if win:
        stop music
        play sound fanfare
        "You win!"
        stop sound
        $ expFormula()
        $ Energy -= 10
        $ clean -= 10
        $ food -=10
        $ timeOfDay += 1
        if erank_unlocked and erank_mission <= 26:
            $ erank_mission += 1
        if erank_mission == 25:
            $ drank_unlocked = True
            $ renpy.notify("Promoted to D-Rank adventurer!")
        if drank_unlocked == True and drank_mission <= 26:
            $ drank_mission += 1
        if drank_mission == 25:
            $ crank_unlocked = True
            $ renpy.notify("Promoted to C-Rank adventurer!")
        if crank_unlocked == True and crank_mission <= 26:
            $ crank_mission += 1
        if crank_mission == 25:
            $ brank_unlocked = True
            $ renpy.notify("Promoted to B-Rank adventurer!")
        if brank_unlocked == True and brank_mission <= 26:
            $ brank_mission += 1
        if brank_mission == 25:
            $ arank_unlocked = True
            $ renpy.notify("Promoted to A-Rank adventurer!")
        if arank_unlocked == True and arank_mission <= 26:
            $ arank_mission += 1
        if arank_mission == 25:
            $ srank_unlocked = True
            $ renpy.notify("Promoted to S-Rank adventurer!")
        if srank_unlocked:
            $ srank_mission += 1
    hide screen battle_message
    hide screen display_monsters[
    $ partyRevive()
    return

I also tried adding it separately to the bottom of the code for the renpy.notify and that also doesnt seem to work either. It feels like one of those times when you know your making a dumb novice mistake but you are so laser focused on one thing that you don't see what right in front of you until someone points it out.

Code: Select all

if drank_unlocked == True:
    $ renpy.notify("Promoted to D-Rank adventurer!")
if crank_unlocked == True:
    $ renpy.notify("Promoted to C-Rank adventurer!")
if brank_unlocked == True:
    $ renpy.notify("Promoted to B-Rank adventurer!")
if arank_unlocked == True:
    $ renpy.notify("Promoted to A-Rank adventurer!")
if srank_unlocked == True:
    $ renpy.notify("Promoted to S-Rank adventurer!")

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

Re: Requesting Assistance with Python OOP and a premade code that uses many Global Variables

#7 Post by jeffster »

TwiztidSinX wrote: Fri Mar 08, 2024 5:56 pm So 1 by indenting the tooltip as suggested the game crashes saying that tool tip is not a valid child of the screen argument or something similar. Sorry 10 days straight of very little sleep I can barely stay awake at this point. But I am sure you know what I am trying to say.
OK, young fellow, listen please to one of the greatest failed big time game developers. I used to be like you, obsessed with pressing and overcoming, but the lack of sleep just burned my brains down, and now I'm not quite able to do any long-time task.

So please learn from my mistake and set a healthy regime of work and life. In my experience, well rested brains work 3-10 times more efficiently. Often after I rested I realized whole previous day was wasted as I didn't see some obvious solution.

So please learn this one thing from me.

Have rest, fresh air and a bit of exercise. Even just a good body stretch can work miracles.

About tooltip - you probably forgot to start the line with "$". RenPy was trying to understand it as one of the screen directives, while it was a variable name.

Or you removed indentation of tooltip of some element, therefore making it a part of the enclosing "screen" when it had to be a part of that element.
When I can figure that little bit out then I should be able to solve all the issues with this and I can finally move on to building an inventory system. Another terrifying task that I will have to learn.
My advice: try to make the inventory system first. It's simple, so you can learn from it easier.

There are several good inventory systems in RenPy Cookbook here, one or two of them by myself, so I can attest to their quality.
Try this:
viewtopic.php?t=66699

If you learn it and modify it as you like, you could take on the battles with more skills and knowledge.
Second thing is by putting the proper returns in, it does not return to the last call. It is returning to main menu. So apparently I did need the jumps there. I am pretty sure I tried that before and thats why I swapped it in the first place?
It looks like something was messed up with the code, and to "make it work" you mess it up even more.

Yeah, it might work until you try to change the code (add something etc.), and it suddenly stops working.

And then to make it work you will have to dig up and correct all the previous mess-ups, which would be much harder than doing everything correct from the start.

In this particular case, I would guess that returns work properly, and having returned to the screen, the program progresses after that screen call, and there it ends (or meets an extra "return" that makes the program finish).

In other words, the wrong "return" is this one:

Code: Select all

label quest_board():
        scene questboard with dissolve
        call screen quest_board
        return
or maybe where you return from "label quest_board()".

I think you shouldn't use "screen quest_board" as the "main hub", the "base of operations" of your game. But if you do, OK, maybe it will work; just replace then Call() with Jump().

Code: Select all

        if erank_unlocked and erank_mission <= 26:
            $ erank_mission += 1
        if erank_mission == 25:
            $ drank_unlocked = True
            $ renpy.notify("Promoted to D-Rank adventurer!")
        if drank_unlocked == True and drank_mission <= 26:
            $ drank_mission += 1
        if drank_mission == 25:
Like I explained before, this is just a mess. You need to do one thing:

* increase progress
* if it reaches the target value, increase the rank

It must be one if, not a dozen of them.

Re-read my comment with "progress" and "current_rank", it's how to do it neatly.

Programming in a neat and simple manner is not a whim. It's how to make your code easily readable and bug-resilient.

Good luck!
If the problem is solved, please edit the original post and add [SOLVED] to the title. 8)

TwiztidSinX
Newbie
Posts: 6
Joined: Fri Mar 08, 2024 10:43 am
Contact:

Re: Requesting Assistance with Python OOP and a premade code that uses many Global Variables

#8 Post by TwiztidSinX »

Wow thank you for all of the helpful advice. Let me try to answer each point and/or comment on everything I can. If I miss something I do apologize.
I actually smiled when you said young fella been a LONG time since anyone called me that. I am 36 so to some I am young I just havent been called young in a long time lol. That is just how my brain has always worked I see a problem and I can't sleep well until I solve it. Example with the battle thing I was trying to get the different tiers of battles to work but it kept defaulting back to the E rank battles. I was struggling with that one thing for 4 days. Each day I was sleeping 1-3 hours max before my brain forced me awake to try to solve it again. Though by adding in the jump that I did yesterday I went to bed at 9pm and no joke just woke up at 1:30PM. So I mean I do sleep a bit too much when I don't have something I am stressing over.

I also am going to be swapping to the inventory system here soon. Though today I think I will be finishing up the main core of battle system. Which all I have left for it is pretty simple. Rename skills currently in the game to reflect spells used in Jobless Reincarnation. Add a Notify when spells are used by certain characters showing that most characters can not silently spellcast. The notify will be the spells chant that is supposed to be chanted before spells are cast but meh a notify works more than well enough. I also need to make the rewards scale based on difficulty that is a bit of a tough one to figure out. Though after a good nights sleep I can think of a way to make it work. The ideal way will be using if statements and just have one per the difficulty but if I can't get that to work I can always use another method. That would be to copy the end turn label. Duplicate it 1x for each difficulty and just redirect the codes flow individually to get it to end up where I need it to . Essentially duplicating the entire battle system 1 for each difficulty that would ensure it works how I want it to. But also would make for some speghetti code that I would rather prefer to avoid.

Next point, tackling these returns. Well I did not realize they work retroactively like that. So your saying... Hmm.... How can I get this from my brain to text format in a way that makes sense......

Label->code stuff happens code calls something we go (return is here)-> we go to new code and stuff happens we return.
Now we go in reverse back to where the return was. But since the code there has already been ran it just defaults to using the return only. Which would make it main menu? I guess this would make more sense with images huh. I will take some and post them at the bottom for now next point.

The quest board isn't actually a main hub. It is meant to be 1 of many adventurer guild locations that the player can interact with to accept quests. This just so happens to be the first one. Essentially I am trying to recreate the entire Jobless Reincarnation world infrastructure. Starting in Ranoa. I want a way for players to go between countries, and have different adventurer guilds in each area the main thing I was aiming for was to get one running properly as different guilds are in different climates meaning different enemies. (As you may be able to tell... I saw this coming before and yes I see it as an incoming headache.)

Luckily NONE of the other guilds matter as at the time of the base games release I only intend on having the one guild. The rest will be adding in updates over time. Hmm I can not seem to find the section you are referring to when you say progress and current rank? I mean don't get me wrong I see the section about changing the stuff to sensitive and I think that is the section you are talking about but I can't be 100% sure mainly because it would only cover unlocking buttons and not adding to the integers, Having those integers is actually vital and can not be removed in any way.
https://www.reddit.com/r/sixfacedworld/ ... venturers/
Mainly because of this link here. It is the in universe way to increase a job rank which I am trying to add in the my game as much of the canon world building as possible. Even if it makes the coding more difficult. Because it makes it more realistic as well.

I wanted to add images here. But apparently I am too dumb to figure that out... I am assuming that it would just be using standard html like Image But that would also require me to take images and upload them to img bucket or something and I don't think its that big of a deal to understand it. Essentially the main hub is the Main house. The main house is built of image button transitions, and backgrounds that change based on the variable timeOfDay. With the people in the house also moving around based on the timeOfDay and they are also image buttons to initiate dialogue. There is a mainUI aka the TopBar menu that you can see bits of in the code I sent. The TopBar hold the macrostats. As well as the code for the map. Opening the map and clicking on the Adventurers Wall icon is what brings us to the battle scene stuff.

Essentially I noticed a lot of games tend to use the teleport around format. That is fine if that is what the dev likes. For me personally I like the feel of image buttons the point and click aspect of your in a main hall click the left arrow camera pans to the left. you see stairs and an arrow under the stairs, click stairs, you climb the stairs, there is a door in front of you or a hallway beside you. click door. You are in the livingroom. so on and so forth thats what I mean when I say I prefer that movement style to just being in my room and the UI has an image of living room click that boom in livingroom.

I think that covers everything In the reply? If I missed something I apologize. But yes I shall speedrun this last section of code and spend a few hours looking over an inventory system. I have what I want in mind for that too. A togglable menu that is just on the UI that opens the inventory screen. I want everything to be drag and droppable in the menu and if I drop it outside the menu it throws the item on the ground. I want the same inventory to appear when I talk to an npc that is classified as a vender and you drag items from their inventory to yours and it automatically removes the cost of said item from the players currency. That inventory also needs to have a gift function so that I can buy gifts that are tied with affection stats and give the gifts to Rudeus's wives to make them happier. As the game has a tiered romance system as well. So I know I am looking for a very specific inventory I will have to start with the things I don't know how to do and try to find one that already has those. Then modify it with the things I know how to do. I.E. I have no clue how to do a drag and drop system. So that will be step one I guess.

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

Re: Requesting Assistance with Python OOP and a premade code that uses many Global Variables

#9 Post by jeffster »

TwiztidSinX wrote: Sat Mar 09, 2024 4:21 pm The ideal way will be using if statements and just have one per the difficulty but if I can't get that to work I can always use another method. That would be to copy the end turn label. Duplicate it 1x for each difficulty and just redirect the codes flow individually to get it to end up where I need it to . Essentially duplicating the entire battle system 1 for each difficulty that would ensure it works how I want it to. But also would make for some speghetti code that I would rather prefer to avoid.
The most Pythonic way (i.e. elegant, readable and simple) is to use the same structure (like "if" or "jump"), when the difference in data allows that. For example, if reward depends just on the battle's rank, you can put all rewards in a list, with the battle rank as the index for that list:

Code: Select all

define RANKE = 0
define RANKD = 1
define RANKC = 2
define RANKB = 3
define RANKA = 4
define RANKS = 5
define rewards = [1, 2, 3, 5, 8, 13]  # Set rewards for each battle rank
default battle_rank = RANKE
Now watch my hands:

Code: Select all

    my_reward = rewards[battle_rank]
    # battle_rank == RANKE, my_reward == 1

    #...

    battle_rank += 1
    my_reward = rewards[battle_rank]
    # battle_rank == RANKD, my_reward == 2

    #...

    battle_rank += 1
    my_reward = rewards[battle_rank]
    # battle_rank == RANKC, my_reward == 3
You see? The same code rewards[battle_rank] gives the reward depending on the battle rank. No "if" was necessary at all.
Next point, tackling these returns. Well I did not realize they work retroactively like that. So your saying... Hmm.... How can I get this from my brain to text format in a way that makes sense......

Label->code stuff happens code calls something we go (return is here)-> we go to new code and stuff happens we return.
Now we go in reverse back to where the return was. But since the code there has already been ran it just defaults to using the return only. Which would make it main menu?
Each call+return is like visiting the next floor. You went up one floor, you have to go down one floor.

When the script starts (at the "label start"), if you do "return", the game ends. You go to the Main Menu.

That's simple.
Luckily NONE of the other guilds matter as at the time of the base games release I only intend on having the one guild. The rest will be adding in updates over time. Hmm I can not seem to find the section you are referring to when you say progress and current rank?
Point 7 here:
viewtopic.php?p=566024#p566024

I'll repeat here a part of that code.
It shows that you don't need all the variables like "erank_unlocked" or something. You have just one variable,
current_rank,
and that unlocks all battles that have ranks less or equal to your current rank:

Code: Select all

# Your possible ranks:
define RANKE = 0
define RANKD = 1
define RANKC = 2
define RANKB = 3
define RANKA = 4
define RANKS = 5

# Your initial rank:
default current_rank = RANKE

# Your progress toward the next rank:
default progress = 0

label fight:
    python:
        # In this example I assume "battle(current_rank)" shows a battle;
        # it's just an example
        result = battle(current_rank)

        if result == 0:
            # Battle lost
            progress += 1

        else:
            # Battle won
            progress += 2

        if progress >= 25 and current_rank < RANKS:
            current_rank += 1
            progress = 0
            renpy.notify("Your mastery improved. Expect harder battles!")
    return
That's the way to go from spaghetti to super-duper code.
Essentially the main hub is the Main house. The main house is built of image button transitions, and backgrounds that change based on the variable timeOfDay. With the people in the house also moving around based on the timeOfDay and they are also image buttons to initiate dialogue. There is a mainUI aka the TopBar menu that you can see bits of in the code I sent. The TopBar hold the macrostats. As well as the code for the map. Opening the map and clicking on the Adventurers Wall icon is what brings us to the battle scene stuff.
...Hmm. OK, it's like a separate topic, and there's no need to discuss it at this point.
So I know I am looking for a very specific inventory I will have to start with the things I don't know how to do and try to find one that already has those. Then modify it with the things I know how to do. I.E. I have no clue how to do a drag and drop system. So that will be step one I guess.
Well, I already gave you a link to something very similar.

Good luck, young man. :-)
If the problem is solved, please edit the original post and add [SOLVED] to the title. 8)

TwiztidSinX
Newbie
Posts: 6
Joined: Fri Mar 08, 2024 10:43 am
Contact:

Re: Requesting Assistance with Python OOP and a premade code that uses many Global Variables

#10 Post by TwiztidSinX »

I actually realized when I checked the link you sent. I did try adding that to the game already and I removed it. Only because, and I think its due to a revision in RenPys logic or something? But the locations for where items are does not work well with the location "boxes" of the background image used. They are like halfway below and to the right of where they should be. I looked at the code got confused and went on to working on the battle one. I will have to take another look at it since I have learned quite a bit in the last 5 days I may be able to fix it now.

Post Reply

Who is online

Users browsing this forum: No registered users