Credits Page and Generating Money Every Second is not Working

Question:
So I am making a module and template to easily make an Idle Game. The Credits page worked before and is no longer working I have no idea why. I also have a function that is supposed to generate money every second if you have bought items to do it. I have asked AI but they can’t seem to fix it either.

Repl link:
https://replit.com/@SalladShooter/IdleGameMaker?v=1

__init__.py

from termcolor import colored
from replit import clear
import time

class Game:
    def __init__(self, gameName, author: str, value=None, collectSpeed=1, rebirths=False, rebirthName="Rebirth"):
        self.gameName = gameName
        self.author = author
        self.actions = ["Shop"]
        self.currency = value['symbol'] if value else None
        self.rebirths = rebirths

        if value and value.get('rebirths', False):
            self.rebirths = True
            self.rebirthName = value.get('rebirthName', 'Rebirth')
            self.actions.append(self.rebirthName)

        self.currencyColor = value.get('color', 'white')
        self.collectSpeed = value.get('collectSpeed', 1)
        self.title = colored(f"{self.gameName}\n", self.currencyColor)

        self.items = []
        self._money = 0
        self._perSecond = 0
        self.moneyAdd = 1
        self.type = None
        self.color = None
        self.shop = "closed"
        self.actions.append("Credits")
        self.item_name = []
        self.item_cost = []
        self.item_gives_type = []
        self.item_amount = []
        self.item_color = []
        self.running = True

        self._last_money_update = time.time()

    @property
    def money(self):
        elapsed_time = time.time() - getattr(self, '_last_money_update', time.time())
        self._money += elapsed_time * self._perSecond
        self._last_money_update = time.time()
        return round(self._money)

    @money.setter
    def money(self, value):
        self._last_money_update = time.time()
        self._money = value

    @property
    def perSecond(self):
        return self._perSecond

    @perSecond.setter
    def perSecond(self, value):
        self.money
        self._perSecond = value

    def add(self, type: str, value: dict):
        if type.lower() == "buy":
            item_name = value['name']
            self.item_name.append(item_name)
            item_cost = value['cost']
            self.item_cost.append(item_cost)
            gives_type = value.get('givesType', 'perSecond')
            self.item_color.append(value['color'])
            self.item_gives_type.append(gives_type)
            amount = value.get('amount', 1)
            self.item_amount.append(amount)

    def click(self):
        for i in range(len(self.item_name)):
            if self.item_gives_type[i] == 'perClick':
                self.money += self.moneyAdd

    def buy(self, item):
        item_index = int(item) - 1
        if 0 <= item_index < len(self.item_name):
            item_cost = self.item_cost[item_index]
            item_name = self.item_name[item_index]
            gives_type = self.item_gives_type[item_index]
            amount = self.item_amount[item_index]
            item_color = self.item_color[item_index]
            if self.money >= item_cost:
                self.money -= item_cost
                self.items.append((item_name, item_cost))

                if gives_type == 'perSecond':
                    self.perSecond += amount
                elif gives_type == 'perClick':
                    self.moneyAdd += amount
                clear()
                print(self.title)
                print(f"{self.money} - {colored(self.currency, self.currencyColor)}\n")
            else:
                print(f"You don't have enough {colored(self.currency, self.currencyColor)}'s to buy {colored(item_name, item_color)}.")
                input("Press [" + colored("ENTER", "cyan") + "] to continue...")
        else:
            print("Invalid item selection.")
            input("Press [" + colored("ENTER", "cyan") + "] to continue...")

    def game_loop(self):
        while True:
            clear()
            print(self.title)
            print(f"{self.money} - {colored(self.currency, self.currencyColor)}\n")
            print("Press [" + colored("ENTER", "cyan") + "] to collect " + colored(self.currency, self.currencyColor) + "'s")
            for i, action in enumerate(self.actions, start=1):
                print(f"{i}. {action}")

            choice = input("> ")

            if choice == "":
                self.click()
                clear()
            elif choice.isdigit():
                choice = int(choice)
                if 1 <= choice <= len(self.actions):
                    action = self.actions[choice - 1]
                    if action == "Shop" and self.shop == "closed":
                        clear()
                        print(self.title)
                        print(f"{self.money} - {colored(self.currency, self.currencyColor)}\n")
                        self.display_items()
                        print(f"{len(self.item_name) + 1}. Exit Shop")
                        playerAction = input("> ")
                        if playerAction == str(len(self.item_name) + 1):
                            self.shop = "closed"
                            clear()
                        elif playerAction.isdigit() and 1 <= int(playerAction) <= len(self.item_name):
                            self.buy(playerAction)
                    elif action == "Credits":
                        clear()
                        print(
                            f"""
Game Made By - {self.author}
Made With {colored("Idle Game Maker", "red")} - SalladShooter""")
                        input(f"Press [" + colored("ENTER", "cyan") + "] to continue...")

    def display_items(self):
        for i, item in enumerate(self.item_name, start=1):
            print(
                f"{i}. {colored(item, self.item_color[i - 1])} {colored(self.currency, self.currencyColor)} {self.item_cost[i - 1]} ({self.item_gives_type[i - 1]} {self.item_amount[i - 1]} {colored(self.currency, self.currencyColor)})") 

main.py

from idlegamemaker import idlegamemaker

# Example usage
game = idlegamemaker.Game(
    "Idle Apple Collector",
    value={
        "symbol": "",
        "color": "green",
        "collectSpeed": 0,
        "rebirths": True,
        "rebirthName": "Ascend",
    },
    author="SalladShooter",
)

game.add(
    "buy",
    {
        "name": "Item1",
        "cost": 10,
        "color": "blue",
        "givesType": "perSecond",
        "amount": 1,
    },
)
game.add(
    "buy",
    {
        "name": "Item2",
        "cost": 20,
        "color": "cyan",
        "givesType": "perSecond",
        "amount": 5,
    },
)
game.add(
    "buy",
    {"name": "Item3", "cost": 50, "color": "red", "givesType": "perClick", "amount": 1},
)

game.game_loop()

I think I’ve found the error. There is an identation error in __ init __.py line 37


Just add a space!

@cldprv on my end it was indented correctly, I made sure but it didn’t fix anything.

For the credits, the problem is probably just that the very next step after printing the credits is to clear the console. Just follow the code execution and it goes straight to clear(). A confirmation input() will work fine here.


For the money, I am not entirely sure, but I think it has to do with the variable moneyAdd. What exactly is the purpose of this variable? Let’s look at the four lines in which it is used:

self.moneyAdd = 0  # __init__
self.moneyAdd += amount  # add
self.moneyAdd = per_second_income  # generate_income
self.moneyAdd += amount  # buy

If I am not mistaken, you are never referencing the value (except technically with +=), you are only setting the value. (This is also the only thing that the thread does.) Therefore, adding self.moneyAdd to self.money where appropriate is the fix, if I understand its purpose correctly.

@NuclearPasta0 I fixed the moneyAdd and it doesn’t fix the perSecond. I also fixed the credits with what you said, thanks.

So, this perSecond (btw there is a typo in this variable in __init__), where are you referencing its value? You seem to be also only setting and never referencing this variable.
If you are going to have a perSecond variable, you have to add it to money somewhere too.

@NuclearPasta0 okay I did that, and I checked it is technically generating money but, I have tried different things and it won’t update the money.

Why is the game loop inside the thread? There is no point in having a separate thread if the entire game is going to be executed in one place.
When you call self.game_loop(), it never stops/returns (the only way to stop it is to exit the program). So, the loop in generate_income_thread only runs once because it gets stuck inside the game loop on the first iteration.

I assume the point of the thread is to allow the money to be continuously updated, but this isn’t really necessary for this type of game. It is hacky and uses threading which is unreliable and very prone to complex bugs.

So, I have some suggestions to rework your money “idle rate”. First, remove the thread because we won’t be using that. I’m assuming you are familiar with python properties. Now, realize that the only places/times you need to update the money is when it is accessed, no need to update continuously. Convert self.money into a property that, when accessed (probably on get only, not set), it updates the money using the delta time since the last update, and the perSecond rate. In addition to this, you must also convert perSecond to a property that also updates money on set (just access money to update it) before setting a new rate, because if the rate of money changes, that new rate should not apply to the delta time that accumulated before the change.
Everything is all good now, you don’t have to call a method to update money whenever you want to access it because the property does it for you. You just have to remember to never store a copy of money because that will instantly become ‘stale’.
The point of this is that you don’t update a variable continuously when it is not accessed continuously, but there is one instance where you may have to:
When you print the money, the display obviously doesn’t continuously update with the current money. You would have to create some sort of print loop to update the money display in real time.

1 Like

@NuclearPasta0 so I have the properties and stuff, and whenever I wait and click it increases by the saved perSecond amount. I can’t figure out how to refresh the print, I have tried AI and it doesn’t seem to be able to fix it.

So, the properties work fine?

There is no easy way to refresh the print because input() is a blocking function. You’d probably have to use a third party package to get input without blocking so that you could continuously refresh the display while waiting for input. (This is outside my range of knowledge.)

(I’d recommend closing this topic and making another one about your new problem.)

1 Like

Well, you might actually be able to use threading for this. I am not super sure.

@NuclearPasta0 so I asked some AI’s such as ChatGPT and Phind neither of them can fix it, the program stops immediately:
__init__.py

from termcolor import colored
from replit import clear
import time
import threading

class Game:
    def __init__(self, gameName, author: str, value=None, collectSpeed=1, rebirths=False, rebirthName="Rebirth"):
        self.gameName = gameName
        self.author = author
        self.actions = ["Shop"]
        self.currency = value['symbol'] if value else None
        self.rebirths = rebirths

        if value and value.get('rebirths', False):
            self.rebirths = True
            self.rebirthName = value.get('rebirthName', 'Rebirth')
            self.actions.append(self.rebirthName)

        self.currencyColor = value.get('color', 'white')
        self.collectSpeed = value.get('collectSpeed', 1)
        self.title = colored(f"{self.gameName}\n", self.currencyColor)

        self.items = []
        self._money = 0
        self._perSecond = 0
        self.moneyAdd = 1
        self.type = None
        self.color = None
        self.shop = "closed"
        self.actions.append("Credits")
        self.item_name = []
        self.item_cost = []
        self.item_gives_type = []
        self.item_amount = []
        self.item_color = []
        self.running = True

        self._last_money_update = time.time()
        self.stop_event = threading.Event()

    @property
    def money(self):
        elapsed_time = time.time() - getattr(self, '_last_money_update', time.time())
        self._money += elapsed_time * self._perSecond
        self._last_money_update = time.time()
        return round(self._money)

    @money.setter
    def money(self, value):
        self._last_money_update = time.time()
        self._money = value

    @property
    def perSecond(self):
        return self._perSecond

    @perSecond.setter
    def perSecond(self, value):
        self.money
        self._perSecond = value

    def add(self, type: str, value: dict):
        if type.lower() == "buy":
            item_name = value['name']
            self.item_name.append(item_name)
            item_cost = value['cost']
            self.item_cost.append(item_cost)
            gives_type = value.get('givesType', 'perSecond')
            self.item_color.append(value['color'])
            self.item_gives_type.append(gives_type)
            amount = value.get('amount', 1)
            self.item_amount.append(amount)

    def click(self):
        for i in range(len(self.item_name)):
            if self.item_gives_type[i] == 'perClick':
                self.money += self.moneyAdd

    def buy(self, item):
        item_index = int(item) - 1
        if 0 <= item_index < len(self.item_name):
            item_cost = self.item_cost[item_index]
            item_name = self.item_name[item_index]
            gives_type = self.item_gives_type[item_index]
            amount = self.item_amount[item_index]
            item_color = self.item_color[item_index]
            if self.money >= item_cost:
                self.money -= item_cost
                self.items.append((item_name, item_cost))

                if gives_type == 'perSecond':
                    self.perSecond += amount
                elif gives_type == 'perClick':
                    self.moneyAdd += amount
                clear()
                print(self.title)
                print(f"{self.money} - {colored(self.currency, self.currencyColor)}\n")
            else:
                print(f"You don't have enough {colored(self.currency, self.currencyColor)}'s to buy {colored(item_name, item_color)}.")
                input("Press [" + colored("ENTER", "cyan") + "] to continue...")
        else:
            print("Invalid item selection.")
            input("Press [" + colored("ENTER", "cyan") + "] to continue...")

    def input_thread(self):
       while not self.stop_event.is_set():
           print("stop_event is not set")
           choice = input()
           if choice:
               self.handle_input(choice)
                   
    def handle_input(self, choice):
        if choice.isdigit():
            choice = int(choice)
            if 1 <= choice <= len(self.actions):
                action = self.actions[choice - 1]
                if action == "Shop" and self.shop == "closed":
                    clear()
                    print(self.title)
                    print(f"{round(self.money)} - {colored(self.currency, self.currencyColor)}\n")
                    self.display_items()
                    print(f"{len(self.item_name) + 1}. Exit Shop")
                    player_action = input("> ")
                    if player_action == str(len(self.item_name) + 1):
                        self.shop = "closed"
                        clear()
                    elif player_action.isdigit() and 1 <= int(player_action) <= len(self.item_name):
                        self.buy(player_action)
                elif action == "Credits":
                    clear()
                    print(
                        f"""
    Game Made By - {self.author}
    Made With {colored("Idle Game Maker", "red")} - SalladShooter""")
                    input(f"Press [" + colored("ENTER", "cyan") + "] to continue...")

    def game_loop(self):
           threading.Thread(target=self.input_thread, daemon=True).start()
           
    def display_items(self):
        for i, item in enumerate(self.item_name, start=1):
            print(
                f"{i}. {colored(item, self.item_color[i - 1])} {colored(self.currency, self.currencyColor)} {self.item_cost[i - 1]} ({self.item_gives_type[i - 1]} {self.item_amount[i - 1]} {colored(self.currency, self.currencyColor)})") 

Sorry, I do not know enough about threading or especially console I/O to help out here. It’s out of my range of knowledge, but I know others on ask have a lot of knowledge about this kind of stuff.

1 Like

@NuclearPasta0 thank you so much for your help thus far!

1 Like

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.