From 6ca624bd2faa91fe29add15d8af5bba62053fde5 Mon Sep 17 00:00:00 2001 From: ComputerTech312 Date: Wed, 24 Sep 2025 16:35:45 +0100 Subject: [PATCH] Add hidden duck types: Normal, Golden, and Fast ducks Features: - Three duck types spawn randomly with configurable chances - All duck types use same spawn message - type is hidden until shot/timeout - Golden ducks: 3-5 HP, 15 XP base, reveal type when hit but still alive - Fast ducks: 1 HP, 12 XP, fly away in 30s instead of 60s - Normal ducks: 1 HP, 10 XP, standard 60s timeout Configuration options: - golden_duck_chance: 0.15 (15% spawn rate) - fast_duck_chance: 0.25 (25% spawn rate) - golden_duck_xp: 15, golden_duck_min/max_hp: 3-5 - fast_duck_xp: 12, fast_duck_timeout: 30s Duck type is revealed when: - Shot (different messages for each type) - Flies away (type-specific fly away messages) - Golden ducks reveal immediately when hit (before death) Maintains backward compatibility with existing game mechanics. --- config.json | 11 ++++- messages.json | 5 +++ src/game.py | 117 +++++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 116 insertions(+), 17 deletions(-) diff --git a/config.json b/config.json index cc4d7b3..b8aaab0 100644 --- a/config.json +++ b/config.json @@ -16,6 +16,15 @@ "duck_spawn_max": 30, "duck_timeout": 60, "befriend_success_rate": 75, - "rearm_on_duck_shot": true + "rearm_on_duck_shot": true, + + "golden_duck_chance": 0.15, + "golden_duck_min_hp": 3, + "golden_duck_max_hp": 5, + "golden_duck_xp": 15, + + "fast_duck_chance": 0.25, + "fast_duck_timeout": 30, + "fast_duck_xp": 12 } \ No newline at end of file diff --git a/messages.json b/messages.json index 6f1eaee..0e00990 100644 --- a/messages.json +++ b/messages.json @@ -5,7 +5,12 @@ "・゜゜・。。・゜゜\\_O> {bold}*flap flap*{reset}" ], "duck_flies_away": "The {bold}duck{reset} flies away. ·°'`'°-.,¸¸.·°'`", + "fast_duck_flies_away": "The {cyan}fast duck{reset} quickly flies away! ·°'`'°-.,¸¸.·°'`", + "golden_duck_flies_away": "The {yellow}{bold}golden duck{reset} flies away majestically. ·°'`'°-.,¸¸.·°'`", "bang_hit": "{nick} > {green}*BANG*{reset} You shot the duck! [{green}+{xp_gained} xp{reset}] [Total ducks: {bold}{ducks_shot}{reset}]", + "bang_hit_golden": "{nick} > {green}*BANG*{reset} You shot a {yellow}{bold}GOLDEN DUCK{reset}! [{yellow}{hp_remaining} HP remaining{reset}] [{green}+{xp_gained} xp{reset}]", + "bang_hit_golden_killed": "{nick} > {green}*BANG*{reset} You killed the {yellow}{bold}GOLDEN DUCK{reset}! [{green}+{xp_gained} xp{reset}] [Total ducks: {bold}{ducks_shot}{reset}]", + "bang_hit_fast": "{nick} > {green}*BANG*{reset} You shot a {cyan}{bold}FAST DUCK{reset}! [{green}+{xp_gained} xp{reset}] [Total ducks: {bold}{ducks_shot}{reset}]", "bang_miss": "{nick} > {red}*BANG*{reset} You missed the {cyan}duck{reset}!", "bang_no_duck": "{nick} > {red}*BANG*{reset} What did you shoot at? There is {red}no duck{reset} in the area... [{red}GUN CONFISCATED{reset}]", "bang_no_ammo": "{nick} > {orange}*click*{reset} You're out of ammo! Use {blue}!reload{reset}", diff --git a/src/game.py b/src/game.py index bf202ba..3e94ea3 100644 --- a/src/game.py +++ b/src/game.py @@ -65,14 +65,24 @@ class DuckGame: for channel, ducks in self.ducks.items(): ducks_to_remove = [] for duck in ducks: - if current_time - duck['spawn_time'] > self.bot.get_config('duck_timeout', 60): + # Different timeouts for different duck types + duck_type = duck.get('duck_type', 'normal') + if duck_type == 'fast': + timeout = self.bot.get_config('fast_duck_timeout', 30) + else: + timeout = self.bot.get_config('duck_timeout', 60) + + if current_time - duck['spawn_time'] > timeout: ducks_to_remove.append(duck) for duck in ducks_to_remove: ducks.remove(duck) - # Use appropriate fly away message based on duck type - if duck.get('is_golden', False): + # Use appropriate fly away message based on duck type - revealing the type! + duck_type = duck.get('duck_type', 'normal') + if duck_type == 'golden': message = self.bot.messages.get('golden_duck_flies_away') + elif duck_type == 'fast': + message = self.bot.messages.get('fast_duck_flies_away') else: message = self.bot.messages.get('duck_flies_away') self.bot.send_message(channel, message) @@ -97,16 +107,53 @@ class DuckGame: if self.ducks[channel]: return - duck = { - 'id': f"duck_{int(time.time())}_{random.randint(1000, 9999)}", - 'spawn_time': time.time(), - 'channel': channel, - 'max_hp': 1, - 'current_hp': 1 - } - # Send regular duck spawn message + # Determine duck type randomly + golden_chance = self.bot.get_config('golden_duck_chance', 0.15) + fast_chance = self.bot.get_config('fast_duck_chance', 0.25) + + rand = random.random() + if rand < golden_chance: + # Golden duck - high HP, high XP + min_hp = self.bot.get_config('golden_duck_min_hp', 3) + max_hp = self.bot.get_config('golden_duck_max_hp', 5) + hp = random.randint(min_hp, max_hp) + duck_type = 'golden' + duck = { + 'id': f"golden_duck_{int(time.time())}_{random.randint(1000, 9999)}", + 'spawn_time': time.time(), + 'channel': channel, + 'duck_type': duck_type, + 'max_hp': hp, + 'current_hp': hp + } + self.logger.info(f"Golden duck (hidden) spawned in {channel} with {hp} HP") + elif rand < golden_chance + fast_chance: + # Fast duck - normal HP, flies away faster + duck_type = 'fast' + duck = { + 'id': f"fast_duck_{int(time.time())}_{random.randint(1000, 9999)}", + 'spawn_time': time.time(), + 'channel': channel, + 'duck_type': duck_type, + 'max_hp': 1, + 'current_hp': 1 + } + self.logger.info(f"Fast duck (hidden) spawned in {channel}") + else: + # Normal duck + duck_type = 'normal' + duck = { + 'id': f"duck_{int(time.time())}_{random.randint(1000, 9999)}", + 'spawn_time': time.time(), + 'channel': channel, + 'duck_type': duck_type, + 'max_hp': 1, + 'current_hp': 1 + } + self.logger.info(f"Normal duck spawned in {channel}") + + # All duck types use the same spawn message - type is hidden! message = self.bot.messages.get('duck_spawn') - self.logger.info(f"Regular duck spawned in {channel}") self.ducks[channel].append(duck) self.bot.send_message(channel, message) @@ -158,13 +205,51 @@ class DuckGame: modified_accuracy = self.bot.levels.get_modified_accuracy(player) hit_chance = modified_accuracy / 100.0 if random.random() < hit_chance: - # Hit! Get the duck - self.ducks[channel].pop(0) - xp_gained = 10 + # Hit! Get the duck and reveal its type + duck = self.ducks[channel][0] + duck_type = duck.get('duck_type', 'normal') + + if duck_type == 'golden': + # Golden duck - multi-hit with high XP + duck['current_hp'] -= 1 + xp_gained = self.bot.get_config('golden_duck_xp', 15) + + if duck['current_hp'] > 0: + # Still alive, reveal it's golden but don't remove + player['accuracy'] = min(player.get('accuracy', 65) + 1, 100) + self.db.save_database() + return { + 'success': True, + 'hit': True, + 'message_key': 'bang_hit_golden', + 'message_args': { + 'nick': nick, + 'hp_remaining': duck['current_hp'], + 'xp_gained': xp_gained + } + } + else: + # Golden duck killed! + self.ducks[channel].pop(0) + xp_gained = xp_gained * duck['max_hp'] # Bonus XP for killing + message_key = 'bang_hit_golden_killed' + elif duck_type == 'fast': + # Fast duck - normal HP but higher XP + self.ducks[channel].pop(0) + xp_gained = self.bot.get_config('fast_duck_xp', 12) + message_key = 'bang_hit_fast' + else: + # Normal duck + self.ducks[channel].pop(0) + xp_gained = 10 + message_key = 'bang_hit' + + # Apply XP and level changes old_level = self.bot.levels.calculate_player_level(player) player['xp'] = player.get('xp', 0) + xp_gained player['ducks_shot'] = player.get('ducks_shot', 0) + 1 player['accuracy'] = min(player.get('accuracy', 65) + 1, 100) + # Check if player leveled up and update magazines if needed new_level = self.bot.levels.calculate_player_level(player) if new_level != old_level: @@ -178,7 +263,7 @@ class DuckGame: return { 'success': True, 'hit': True, - 'message_key': 'bang_hit', + 'message_key': message_key, 'message_args': { 'nick': nick, 'xp_gained': xp_gained,