From 1f64b6fc8258ef6c01e41d5af7365c5884f336bf Mon Sep 17 00:00:00 2001 From: ComputerTech312 Date: Sat, 13 Sep 2025 16:14:46 +0100 Subject: [PATCH] test --- duckhunt/simple_duckhunt.py | 175 +++++++++++++++++++++++++++++++++--- 1 file changed, 164 insertions(+), 11 deletions(-) diff --git a/duckhunt/simple_duckhunt.py b/duckhunt/simple_duckhunt.py index a1b6c7a..3d99e28 100644 --- a/duckhunt/simple_duckhunt.py +++ b/duckhunt/simple_duckhunt.py @@ -1217,6 +1217,13 @@ class SimpleIRCBot: await self.handle_use(nick, channel, item_id, user, target_nick) else: self.send_message(channel, f"{nick} > Usage: !use [target_nick]") + elif full_cmd.startswith('!give '): + parts = full_cmd[6:].split() + if len(parts) >= 2: + target_nick, item_id = parts[0], parts[1] + await self.handle_give(nick, channel, user, target_nick, item_id) + else: + self.send_message(channel, f"{nick} > Usage: !give ") elif full_cmd.startswith('!sell '): item_id = full_cmd[6:].strip() await self.handle_sell(nick, channel, item_id, user) @@ -1607,9 +1614,8 @@ class SimpleIRCBot: async def handle_help(self, nick, channel): help_lines = [ f"{nick} > {self.colors['cyan']}🦆 DUCKHUNT COMMANDS 🦆{self.colors['reset']}", - f"{nick} > {self.colors['green']}Game:{self.colors['reset']} !bang !bef !reload !duckstats !topduck !shop !buy !inventory !nextduck", - f"{nick} > {self.colors['blue']}Settings:{self.colors['reset']} !output ", - f"{nick} > {self.colors['yellow']}Info:{self.colors['reset']} Golden ducks: 50 XP | Gun jamming & ricochets ON | Timeout: {self.duck_timeout_min}-{self.duck_timeout_max}s" + f"{nick} > {self.colors['green']}Game:{self.colors['reset']} !bang !bef !reload !duckstats !topduck !shop !buy !use !give !nextduck", + f"{nick} > {self.colors['blue']}Settings:{self.colors['reset']} !output " ] if self.is_admin(f"{nick}!*@*"): # Check if admin help_lines.append(f"{nick} > {self.colors['red']}Admin:{self.colors['reset']} !duck !golden !ban !reset !resetdb !rearm !giveitem !givexp !ignore !unignore | /msg {self.config['nick']} restart|quit") @@ -1852,11 +1858,17 @@ class SimpleIRCBot: # Check for bread channel limit if item == '21': # Bread - # Count existing bread in channel - channel_bread_count = 0 - if hasattr(self, 'channel_bread') and channel in self.channel_bread: - channel_bread_count = len(self.channel_bread[channel]) + # Initialize and clean up old bread + if channel not in self.channel_bread: + self.channel_bread[channel] = [] + # Clean up expired bread (30 minutes) + import time + current_time = time.time() + self.channel_bread[channel] = [b for b in self.channel_bread[channel] if current_time - b['time'] < 1800] + + # Check limit after cleanup + channel_bread_count = len(self.channel_bread[channel]) if channel_bread_count >= 3: self.send_message(channel, f"{nick} > Maximum 3 bread items allowed in channel! Current: {channel_bread_count}") return @@ -2047,6 +2059,30 @@ class SimpleIRCBot: self.send_message(channel, f"{nick} > *POCKET SAND!* You threw sand in {target_nick}'s eyes! Their accuracy reduced by 20%!") else: self.send_message(channel, f"{nick} > You threw sand in your own eyes... brilliant strategy!") + elif effect == 'bread': + # Bread - deploy in channel to attract ducks faster + if using_on_other: + self.send_message(channel, f"{nick} > You can't use bread on other players! Deploy it in the channel.") + return + + # Initialize channel bread if needed + if channel not in self.channel_bread: + self.channel_bread[channel] = [] + + # Check limit (should have been checked in buy, but double-check) + if len(self.channel_bread[channel]) >= 3: + self.send_message(channel, f"{nick} > Maximum 3 bread items already deployed in this channel!") + return + + # Deploy bread + import time + bread_info = { + 'time': time.time(), + 'owner': nick + } + self.channel_bread[channel].append(bread_info) + + self.send_message(channel, f"{nick} > *CRUMBLE CRUMBLE* You scattered bread crumbs around the channel! Ducks will be attracted faster. ({len(self.channel_bread[channel])}/3 bread deployed)") # Add more effects as needed... else: # Default effects for other items @@ -2059,6 +2095,84 @@ class SimpleIRCBot: target_user = f"{target_nick.lower()}!user@host" # Simplified - would need real user data self.save_database() + async def handle_give(self, nick, channel, user, target_nick, item_id): + """Give an item from inventory to another player""" + # Get giver's player data + player = self.get_player(user) + if not player: + self.send_message(channel, f"{nick} > Player data not found!") + return + + # Check if giver has the item + if item_id not in player['inventory'] or player['inventory'][item_id] <= 0: + self.send_message(channel, f"{nick} > You don't have that item! Check !duckstats to see your inventory.") + return + + # Find target player + target_nick_lower = target_nick.lower() + if target_nick_lower not in self.players: + self.send_message(channel, f"{nick} > Player {target_nick} not found!") + return + + # Can't give to yourself + if target_nick_lower == nick.lower(): + self.send_message(channel, f"{nick} > You can't give items to yourself!") + return + + target_player = self.players[target_nick_lower] + + # Get shop item data for reference + shop_items = { + '1': {'name': 'Extra bullet', 'effect': 'ammo'}, + '2': {'name': 'Extra clip', 'effect': 'max_ammo'}, + '3': {'name': 'AP ammo', 'effect': 'accuracy'}, + '4': {'name': 'Explosive ammo', 'effect': 'explosive'}, + '5': {'name': 'Repurchase confiscated gun', 'effect': 'gun'}, + '6': {'name': 'Grease', 'effect': 'reliability'}, + '7': {'name': 'Sight', 'effect': 'accuracy'}, + '8': {'name': 'Infrared detector', 'effect': 'detector'}, + '9': {'name': 'Silencer', 'effect': 'silencer'}, + '10': {'name': 'Four-leaf clover', 'effect': 'luck'}, + '11': {'name': 'Shotgun', 'effect': 'shotgun'}, + '12': {'name': 'Assault rifle', 'effect': 'rifle'}, + '13': {'name': 'Sniper rifle', 'effect': 'sniper'}, + '14': {'name': 'Automatic shotgun', 'effect': 'auto_shotgun'}, + '15': {'name': 'Handful of sand', 'effect': 'sand'}, + '16': {'name': 'Water bucket', 'effect': 'water'}, + '17': {'name': 'Sabotage', 'effect': 'sabotage'}, + '18': {'name': 'Life insurance', 'effect': 'life_insurance'}, + '19': {'name': 'Liability insurance', 'effect': 'liability'}, + '20': {'name': 'Decoy', 'effect': 'decoy'}, + '21': {'name': 'Piece of bread', 'effect': 'bread'}, + '22': {'name': 'Ducks detector', 'effect': 'duck_detector'}, + '23': {'name': 'Mechanical duck', 'effect': 'mechanical'} + } + + if item_id not in shop_items: + self.send_message(channel, f"{nick} > Invalid item ID! Use shop numbers 1-23.") + return + + shop_item = shop_items[item_id] + + # Remove item from giver + player['inventory'][item_id] -= 1 + if player['inventory'][item_id] <= 0: + del player['inventory'][item_id] + + # Add item to target player + if 'inventory' not in target_player: + target_player['inventory'] = {} + if item_id not in target_player['inventory']: + target_player['inventory'][item_id] = 0 + target_player['inventory'][item_id] += 1 + + # Announce the gift + self.send_message(channel, f"{nick} > Gave {shop_item['name']} to {target_nick}!") + + # Save both players + self.save_player(user) + self.save_database() + async def handle_trade(self, nick, channel, user, target_nick, item, amount): """Trade items with other players""" player = self.get_player(user) @@ -2396,6 +2510,11 @@ class SimpleIRCBot: # Add duck to channel self.ducks[channel].append(duck) + # Consume bread when duck spawns (bread gets eaten!) + if channel in self.channel_bread and self.channel_bread[channel]: + consumed_bread = self.channel_bread[channel].pop(0) # Remove oldest bread + self.logger.info(f"Duck consumed bread from {consumed_bread['owner']} in {channel}") + # Send spawn message self.send_message(channel, duck_info['msg']) self.logger.info(f"Admin spawned {duck_info['type']} duck {duck_id} in {channel}") @@ -2413,6 +2532,22 @@ class SimpleIRCBot: while not self.shutdown_requested: wait_time = random.randint(self.duck_spawn_min, self.duck_spawn_max) + + # Apply bread effects - reduce spawn time + for channel in self.channels_joined: + if channel in self.channel_bread and self.channel_bread[channel]: + # Clean up old bread (expires after 30 minutes) + current_time = time.time() + self.channel_bread[channel] = [b for b in self.channel_bread[channel] if current_time - b['time'] < 1800] + + if self.channel_bread[channel]: # If any bread remains after cleanup + # Each bread reduces spawn time by 20%, max 60% reduction + bread_count = len(self.channel_bread[channel]) + reduction = min(0.6, bread_count * 0.2) # Max 60% reduction + wait_time = int(wait_time * (1 - reduction)) + self.logger.info(f"Bread effect: {bread_count} bread in {channel}, {reduction*100:.0f}% spawn time reduction") + break # Apply effect based on first channel with bread + self.logger.info(f"Waiting {wait_time//60}m {wait_time%60}s for next duck") # Set next spawn time for all channels @@ -2427,7 +2562,7 @@ class SimpleIRCBot: return await asyncio.sleep(1) - # Spawn only one duck per channel if no alive ducks exist + # Check each channel for possible duck spawning for channel in self.channels_joined: if self.shutdown_requested: return @@ -2436,10 +2571,28 @@ class SimpleIRCBot: channel_ducks = self.ducks.get(channel, []) alive_ducks = [duck for duck in channel_ducks if duck.get('alive')] - # Only spawn if no ducks are alive (one duck at a time naturally) + # Only consider spawning if no ducks are alive if not alive_ducks: - await self.spawn_duck_now(channel) - break # Only spawn in the first available channel + # Calculate spawn chance based on bread + base_chance = 0.3 # 30% base chance per spawn cycle + bread_count = 0 + + # Count and clean up bread + if channel in self.channel_bread and self.channel_bread[channel]: + current_time = time.time() + self.channel_bread[channel] = [b for b in self.channel_bread[channel] if current_time - b['time'] < 1800] + bread_count = len(self.channel_bread[channel]) + + # Each bread adds 25% spawn chance + spawn_chance = base_chance + (bread_count * 0.25) + spawn_chance = min(0.95, spawn_chance) # Cap at 95% + + # Roll for spawn + if random.random() < spawn_chance: + await self.spawn_duck_now(channel) + bread_msg = f" (bread boosted: {bread_count} bread = {spawn_chance*100:.0f}% chance)" if bread_count > 0 else "" + self.logger.info(f"Duck spawned in {channel}{bread_msg}") + break # Only spawn in one channel per cycle async def duck_timeout_checker(self): """Remove ducks that have been around too long"""