From 178c9d83cd7ada22f186c187e04c807959b031b7 Mon Sep 17 00:00:00 2001 From: Ben Boonstra Date: Sat, 28 Sep 2024 17:30:39 -0500 Subject: [PATCH] 1.4.0 Nanobot --- idlegame/battle.py | 52 +++++++++++++++++++++++++++++ idlegame/config.py | 22 ++++++++++++- idlegame/idle.py | 77 +++++++++++++++++++++++++++++++++---------- idlegame/main.py | 2 ++ idlegame/nanobots.py | 6 ++-- idlegame/profile.py | 2 +- requirements.txt | 1 - savedata.pickle | Bin 462 -> 413 bytes setup.py | 2 +- 9 files changed, 141 insertions(+), 23 deletions(-) create mode 100644 idlegame/battle.py diff --git a/idlegame/battle.py b/idlegame/battle.py new file mode 100644 index 0000000..343fbaa --- /dev/null +++ b/idlegame/battle.py @@ -0,0 +1,52 @@ +from typing import List +from idlegame.data import AutosavedPlayer +from idlegame.nanobots import Nanobot +import random + +def simulate_defense(player: AutosavedPlayer, defending_bots: List[Nanobot]) -> int: + """Simulate defense against invasions using the given nanobots. + + Args: + player (AutosavedPlayer): The player who owns the nanobots. + defending_bots (List[Nanobot]): The nanobots assigned to defend. + + Returns: + int: The number of nanobots that were broken during the defense. + """ + if not defending_bots: + return 0 # No bots to defend + + # Calculate total defense power from the defending bots + total_defense_power = sum(bot.defense_rating for bot in defending_bots) + + # Simulate the invasion strength (could be a random number or based on game state) + invasion_strength = random.randint(1, 10) # Example invasion strength + print(f"Invasion strength: {invasion_strength}, Total defense power: {total_defense_power}") + + # Calculate the ratio of defense power to invasion strength + defense_ratio = total_defense_power / invasion_strength if invasion_strength > 0 else 1 + + # Determine the chance of breaking bots based on defense ratio + # If defense_ratio is high, break chance is low; if low, break chance is high + # Exponential scaling for break chance: + if defense_ratio >= 2: + break_chance = 0 # Strong advantage + elif defense_ratio >= 1: + break_chance = (1 - (defense_ratio - 1) / 1) * 0.5 # Linear decrease + else: + # Exponential growth in break chance for heavily disadvantaged scenario + break_chance = min(1, 0.5 * (1 - defense_ratio)) # Break chance up to 50% max + + bots_broken = 0 + + # Determine how many bots break based on calculated break chance + for bot in defending_bots: + if random.random() < break_chance: + player.nanos.remove(bot) # Remove the broken bot from the player's list + print(f"One of your defending bots, '{bot.name}', was broken during an invasion!") + bots_broken += 1 + + if bots_broken == 0: + print(f"Your defenses held against an invasion (Strength: {invasion_strength}).") + + return bots_broken diff --git a/idlegame/config.py b/idlegame/config.py index f8b89db..528d224 100644 --- a/idlegame/config.py +++ b/idlegame/config.py @@ -1,2 +1,22 @@ +from datetime import timedelta + save_file = 'savedata.pickle' -disallowed_usernames = ['cancel'] \ No newline at end of file +disallowed_usernames = ['cancel'] +base_mining_rate = 1 +sim_chunk_duration = 600 # 600 seconds = 10 minutes +invasion_chance_per_chunk = 0.05 # 5% per 10 mins + +def handle_sudo(player, *args, **kwargs): + key = kwargs.get('k', '') + if key.lower() != "verysecure": + print("Invalid command. Try 'man commands'!") + return + + ts = kwargs.get('lengthen-uptime', 0) + + if ts: + # Subtract the specified number of seconds from the last claim timestamp + player.last_claim_timestamp -= timedelta(seconds=int(ts)) + print(f"Added {int(ts)/60} mins to uptime") + + diff --git a/idlegame/idle.py b/idlegame/idle.py index 020f09a..7f745b3 100644 --- a/idlegame/idle.py +++ b/idlegame/idle.py @@ -1,42 +1,85 @@ -from datetime import datetime, timezone -from .data import AutosavedPlayer +from datetime import datetime, timezone, timedelta +from idlegame.data import AutosavedPlayer +from idlegame.battle import simulate_defense +import idlegame.config as config +import random def handle_claim(player: AutosavedPlayer, *args, **kwargs) -> None: - """Claim rewards in the game. + """See what occurred while you were offline! Usage: - claim [--reward ] [--silent] + uptime [--silent] Options: - --reward Specify the reward to claim. --silent Suppress output messages. """ + now = datetime.now(timezone.utc) # Use UTC time # Check for the --silent option silent_mode = kwargs.get('silent', False) if player.last_claim_timestamp is None: - # If the player has never claimed, just set it to now + # If the player has never claimed, initialize last claim timestamp and give 1 core. player.last_claim_timestamp = now - player.save() - print("You have claimed your first reward of 1 regular nanocore!") player.nano_cores['normal'] += 1 + player.save() + print("You have claimed your first reward of 1 regular nano core!") return # Calculate time difference since last claim time_offline = now - player.last_claim_timestamp - minutes_offline = int(time_offline.total_seconds() // 60) - gold_gain = minutes_offline - if minutes_offline < 1: + total_seconds_offline = int(time_offline.total_seconds()) + + if total_seconds_offline < 600: if not silent_mode: - print("You haven't been offline for long enough to get any rewards!") + print("Nothing has happened yet. You need to wait for at least 10 minutes to claim rewards!") return - player.gold += gold_gain - - # Update last claim timestamp to now - player.last_claim_timestamp = now + num_chunks = total_seconds_offline // config.sim_chunk_duration + + gold_gathered = 0 + invasions_handled = 0 + nanobots_broken = 0 + + # Simulate each 10-minute chunk + for _ in range(num_chunks): + invasion_occurred = random.random() < config.invasion_chance_per_chunk + + # Collect all defenders for this chunk + defending_bots = [] + + for bot in player.nanos: + # Do event actions if applicable + for event, action in bot.event_actions.items(): + if event.lower() in ['battle.defense', 'defense', 'invasion'] and action in ['defend', 'join', 'support', 'defense'] and invasion_occurred: + defending_bots.append(bot) + print(bot.name) + continue + else: + # Otherwise do idle actions + if bot.idle_action == 'mine': + gold_gathered += config.base_mining_rate # mine resources for this chunk + + # Simulate defense if an invasion occurred + if invasion_occurred: + invasions_handled += 1 + nanobots_broken += simulate_defense(player, defending_bots) + # TODO logic if you lose + + # Add gathered gold to player + player.gold += gold_gathered + + # Update last claim timestamp to reflect only complete chunked increments + leftover_time = total_seconds_offline % config.sim_chunk_duration + player.last_claim_timestamp = now - timedelta(seconds=leftover_time) + player.save() + + # Print the results unless in silent mode if not silent_mode: - print(f"You have claimed {gold_gain} gold for being offline for {minutes_offline} minutes!") + print(f"You were offline for {total_seconds_offline // 60} minutes.") + print(f"You mined {gold_gathered} gold!") + print(f"{invasions_handled} invasions were handled during your offline time.") + if nanobots_broken > 0: + print(f"{nanobots_broken} of your bots was broken during the invasions.") diff --git a/idlegame/main.py b/idlegame/main.py index a375f94..540547b 100644 --- a/idlegame/main.py +++ b/idlegame/main.py @@ -5,6 +5,7 @@ from idlegame.idle import handle_claim from idlegame.profile import handle_profile from idlegame.nanobots import handle_nano, handle_list, handle_remove +from idlegame.config import handle_sudo import colorama as c c.init() class CommandLineInterface(cmd.Cmd): @@ -25,6 +26,7 @@ def __init__(self, player): "nano": handle_nano, "rm": handle_remove, "ls": handle_list, + "sudo": handle_sudo, } def handle_alias(self, player, *args, **kwargs): diff --git a/idlegame/nanobots.py b/idlegame/nanobots.py index 1cbe1d8..5fb8ac6 100644 --- a/idlegame/nanobots.py +++ b/idlegame/nanobots.py @@ -1,4 +1,3 @@ -from prompt_toolkit import prompt from idlegame.data import AutosavedPlayer from enum import Enum @@ -15,6 +14,9 @@ def __init__(self, name: str, logic: str, type: Nanotype): self.idle_action = None self.event_actions = {} self.type = type + self.defense_rating = 1 + if self.type == Nanotype.FIGHTER: + self.defense_rating += 1 self.parse_logic(logic) def parse_logic(self, logic: str) -> None: @@ -58,7 +60,7 @@ def handle_nano(player: AutosavedPlayer, *args, **kwargs) -> None: Use the `on` parameter for an event-driven job Use the `done` parameter to finish scripting Example: - idle harvest + idle mine on attacking attack on defending defend done diff --git a/idlegame/profile.py b/idlegame/profile.py index ef7eac3..2f16a57 100644 --- a/idlegame/profile.py +++ b/idlegame/profile.py @@ -1,4 +1,4 @@ -from .data import AutosavedPlayer +from idlegame.data import AutosavedPlayer def handle_profile(player: AutosavedPlayer, *args, **kwargs) -> None: """Check your profile. diff --git a/requirements.txt b/requirements.txt index 08ae493..6388540 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,2 @@ colorama==0.4.6 setuptools==75.1.0 -prompt_toolkit==3.0.48 \ No newline at end of file diff --git a/savedata.pickle b/savedata.pickle index 1f0f93b2a2d58d75506066371031e045e8fe9f2f..3a9c7a9dc7969966413b226ac1e6ce28dc3afc94 100644 GIT binary patch delta 155 zcmX@dJeQfJfo1BXi7ZBpDih63*~G-P8H*hz7WqhV; x3sR?KDE4sp0SyInCQr$bYMl}^rH3yiH7zx-I5oZ~u_QAueTp|@X;NaT9su`pI;j8v delta 183 zcmbQse2$r=fo1C9i7ZBp3=_>w*+d1c`2yZdEb`%I%$w4~l9!m5Kc#r$UMWV#i8oT} z*@Lq)lQL5>r}S`VrsSl?CnlF<<^$ES24|+{CQj+$O)X2!D}f3ZPpO@v(ZiWoQj(aQ zotc+DrH2i|p3=jalA4y9mjV`K2eXS)rdMP6%1z9f P(mI7bXi8~PVyPYgFTF-; diff --git a/setup.py b/setup.py index e5792cf..a653b1f 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ setup( name='idlegame', - version='1.3.2', + version='1.4.0', packages=find_packages(), install_requires=[ 'colorama',