diff --git a/idlegame/data.py b/idlegame/data.py index 959e4ed..da06c67 100644 --- a/idlegame/data.py +++ b/idlegame/data.py @@ -22,6 +22,8 @@ class AutosavedPlayer: 'last_claim_timestamp': None, 'settings': {}, 'aliases': {}, + 'nano_cores': {'normal': 1, 'miner': 0, 'fighter': 0, 'super': 0, 'warper': 0}, + 'nanos': [], } def __init__(self) -> None: diff --git a/idlegame/idle.py b/idlegame/idle.py index cf79ffe..020f09a 100644 --- a/idlegame/idle.py +++ b/idlegame/idle.py @@ -20,8 +20,8 @@ def handle_claim(player: AutosavedPlayer, *args, **kwargs) -> None: # If the player has never claimed, just set it to now player.last_claim_timestamp = now player.save() - print("You have claimed your first reward of 100 gold!") - player.gold += 100 + print("You have claimed your first reward of 1 regular nanocore!") + player.nano_cores['normal'] += 1 return # Calculate time difference since last claim diff --git a/idlegame/main.py b/idlegame/main.py index abc12de..a375f94 100644 --- a/idlegame/main.py +++ b/idlegame/main.py @@ -4,6 +4,7 @@ from idlegame.data import handle_login from idlegame.idle import handle_claim from idlegame.profile import handle_profile +from idlegame.nanobots import handle_nano, handle_list, handle_remove import colorama as c c.init() class CommandLineInterface(cmd.Cmd): @@ -14,11 +15,16 @@ def __init__(self, player): self.player = player self.commands = { "logout": self.handle_exit, + "bye": self.handle_exit, + "exit": self.handle_exit, "man": self.handle_man, "uptime": handle_claim, "idlegame": self.handle_info, "alias": self.handle_alias, "whoami": handle_profile, + "nano": handle_nano, + "rm": handle_remove, + "ls": handle_list, } def handle_alias(self, player, *args, **kwargs): @@ -133,7 +139,7 @@ def default(self, line): if command in self.player.aliases.keys(): command = self.player.aliases[command] if command not in self.commands: - print(f"Alias '{args[0]}' is not a valid command.") + print(f"Your alias set to '{command}', but that is not a valid command.") return if command in self.commands: diff --git a/idlegame/nanobots.py b/idlegame/nanobots.py new file mode 100644 index 0000000..1cbe1d8 --- /dev/null +++ b/idlegame/nanobots.py @@ -0,0 +1,196 @@ +from prompt_toolkit import prompt +from idlegame.data import AutosavedPlayer +from enum import Enum + +class Nanotype(Enum): + NORMAL = "normal" + MINER = "miner" + FIGHTER = "fighter" + SUPER = "super" + WARPER = "warper" + +class Nanobot: + def __init__(self, name: str, logic: str, type: Nanotype): + self.name = name + self.idle_action = None + self.event_actions = {} + self.type = type + self.parse_logic(logic) + + def parse_logic(self, logic: str) -> None: + """Parse the nanobot logic and set idle and event actions.""" + lines = logic.strip().splitlines() + for line in lines: + line = line.strip() + if line.startswith("idle "): + self.idle_action = line[5:].strip() + elif line.startswith("on "): + # Extract event and action + parts = line[3:].split() + event_name = parts[0] if parts else None + action = parts[1] if len(parts) > 1 else None + if event_name and action: + self.event_actions[event_name] = action + + def get_current_action(self, event: str = None) -> str: + """Perform the action based on the current event or idle.""" + if event in self.event_actions: + return f"Performing {self.event_actions[event]} due to event '{event}'" + elif self.idle_action: + return f"Performing idle action: {self.idle_action}" + return "IDLE" + + def __repr__(self): + return f"Nanobot(name={self.name}, idle_action={self.idle_action}, event_actions={self.event_actions})" + +def handle_nano(player: AutosavedPlayer, *args, **kwargs) -> None: + """Write a new nanobot. + + Usage: + nano [--type ] [--name ] [-y] + + Requires: + 1 Nano Core + --type: 1 typed core + + Scripting: + Use the `idle` parameter for an idle job + Use the `on` parameter for an event-driven job + Use the `done` parameter to finish scripting + Example: + idle harvest + on attacking attack + on defending defend + done + + """ + + bot_type = kwargs.get('type', None) + bot_name = kwargs.get('name', None) + auto_accept = kwargs.get('y', False) is not False + + if player.nano_cores.get('normal', 0) < 1: + print("You need at least 1 nano core to create a new nanobot.") + return + + if bot_type and player.nano_cores.get(bot_type.lower(), 0) < 1: + print(f"You need at least 1 {bot_type.lower()} core to create a specialized nanobot.") + return + + # Determine the nanobot type + if bot_type: + nanobot_type = Nanotype[bot_type.upper()] + else: + nanobot_type = Nanotype.NORMAL + + if not auto_accept: + create_nano = input(f"Do you want to create a new {nanobot_type.name.lower()} nanobot? (yes/no): ").strip().lower() + if create_nano not in ['yes', 'y']: + print("No new nanobot created.") + return + + if not bot_name: + bot_name = input("Enter a name for your nanobot: ") + + if len(bot_name) > 15 or any(bot.name == bot_name for bot in player.nanos): + print("Invalid name. Names must be unique and less than 16 characters long.") + return + + # Interactive session for nano logic input + print("Write the logic for your nanobot (type 'done' on a new line to finish):") + nano_logic_lines = [] + + while True: + line = input() + if line.strip().lower() == 'done': + break + nano_logic_lines.append(line) + + # Join the lines into a single string + nano_logic = '\n'.join(nano_logic_lines) + + # Create a new Nanobot instance and add it to the player's nanos list + new_nanobot = Nanobot(name=bot_name, logic=nano_logic.strip(), type=nanobot_type) + player.nanos.append(new_nanobot) + player.nano_cores['normal'] -= 1 # Deduct a nano core for creating a new nanobot + if bot_type: + player.nano_cores[bot_type.lower()] -= 1 # Deduct a specialized core if applicable + player.save() # Save changes to the player's data + + print(f"Nanobot '{bot_name}' created with logic:\nIdle action: {new_nanobot.idle_action}\nEvent actions: {new_nanobot.event_actions}.") + + +def handle_remove(player: AutosavedPlayer, *args, **kwargs) -> None: + """Remove a nanobot and reclaim its nano core(s). + + Usage: + rm + """ + + # Check if the bot name is provided + if len(args) == 0: + print("Please provide the name of the nanobot to remove. Usage: rm ") + return + + bot_name = args[0] # Get the bot name from the arguments + + # Find the nanobot by name + nanobot_to_destroy = next((bot for bot in player.nanos if bot.name == bot_name), None) + + if nanobot_to_destroy is None: + print(f"No nanobot found with the name '{bot_name}'. Find its name with `ls`!") + return + + # Reclaim the core(s) based on the nanobot type + player.nano_cores['normal'] += 1 # Reclaiming a normal core + + core_type = None + if nanobot_to_destroy.type != Nanotype.NORMAL: + core_type = nanobot_to_destroy.type.name.lower() + player.nano_cores[core_type] += 1 # Reclaiming the typed core if applicable + + # Remove the nanobot from the list + player.nanos.remove(nanobot_to_destroy) + player.save() # Save changes to the player's data + + # Print the appropriate message based on nanobot type + if nanobot_to_destroy.type != Nanotype.NORMAL: + print(f"Nanobot '{bot_name}' destroyed. You have reclaimed 1 normal core and 1 {core_type} core.") + else: + print(f"Nanobot '{bot_name}' destroyed. You have reclaimed 1 normal core.") + +def handle_list(player: AutosavedPlayer, *args, **kwargs) -> None: + """List all nanobots and their current logic. + + Usage: + ls + """ + + if not player.nanos: + print("You have no nanobots.") + return + + # Header + print(f"{'Name':<15}{'Type':<10}{'Idle Action':<15}{'Event Actions':<30}{'Current Action':<30}") + print("-" * 100) + + # Loop through each nanobot and display relevant details + for bot in player.nanos: + idle_action = bot.idle_action or "None" + current_action = bot.get_current_action() or "None" + event_actions = list(bot.event_actions.items()) + + # Print the first event action on the same line as the bot's other details + if event_actions: + first_event, first_action = event_actions[0] + first_event_text = f"On {first_event}: {first_action}" + print(f"{bot.name:<15}{bot.type.name.capitalize():<10}{idle_action.capitalize():<15}{first_event_text:<30}{current_action:<30}") + else: + print(f"{bot.name:<15}{bot.type.name.capitalize():<10}{idle_action.capitalize():<15}{'None':<30}{current_action:<30}") + + # Print additional event actions (if any) on new lines, aligned with the event actions column + for event, action in event_actions[1:]: + text = f"On {event}: {action}" + print(f"{'':<15}{'':<10}{'':<15}{text:<60}") + + print("-" * 100) diff --git a/requirements.txt b/requirements.txt index 0d9a5a2..08ae493 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ colorama==0.4.6 -setuptools==75.1.0 \ No newline at end of file +setuptools==75.1.0 +prompt_toolkit==3.0.48 \ No newline at end of file diff --git a/savedata.pickle b/savedata.pickle index 90c2b4c..1f0f93b 100644 Binary files a/savedata.pickle and b/savedata.pickle differ