This commit is contained in:
2025-09-13 16:14:46 +01:00
parent 339f32b6a0
commit 1f64b6fc82

View File

@@ -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 <item_id> [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 <nick> <item_id>")
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 <id> !inventory !nextduck",
f"{nick} > {self.colors['blue']}Settings:{self.colors['reset']} !output <PUBLIC|NOTICE|PRIVMSG>",
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 <id> !use <id> !give <nick> <id> !nextduck",
f"{nick} > {self.colors['blue']}Settings:{self.colors['reset']} !output <PUBLIC|NOTICE|PRIVMSG>"
]
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"""