diff --git a/ADMIN_SECURITY.md b/ADMIN_SECURITY.md new file mode 100644 index 0000000..5da2462 --- /dev/null +++ b/ADMIN_SECURITY.md @@ -0,0 +1,46 @@ +# Enhanced Admin Configuration + +For better security, update your `config.json` to use hostmask-based admin authentication: + +## Current (Less Secure) - Nick Only: +```json +{ + "admins": [ + "peorth", + "computertech", + "colby" + ] +} +``` + +## Recommended (More Secure) - Hostmask Based: +```json +{ + "admins": [ + { + "nick": "peorth", + "hostmask": "peorth!*@trusted.domain.com" + }, + { + "nick": "computertech", + "hostmask": "computertech!*@*.isp.net" + }, + { + "nick": "colby", + "hostmask": "colby!user@192.168.*.*" + } + ] +} +``` + +## Migration Notes: +- The bot supports both formats for backward compatibility +- Nick-only authentication generates security warnings in logs +- Hostmask patterns use shell-style wildcards (* and ?) +- Consider using registered nick services for additional security + +## Security Benefits: +- Prevents nick spoofing attacks +- Allows IP/hostname restrictions +- Provides audit logging of admin access +- Maintains backward compatibility during migration \ No newline at end of file diff --git a/CONFIG.md b/CONFIG.md deleted file mode 100644 index a57f682..0000000 --- a/CONFIG.md +++ /dev/null @@ -1,127 +0,0 @@ -# DuckHunt Bot Configuration Guide - -This document explains all configuration options in `config.json`. - -## ๐Ÿ“ก IRC Connection Settings (`connection`) - -| Setting | Description | Example | -|---------|-------------|---------| -| `server` | IRC server hostname | `"irc.rizon.net"` | -| `port` | IRC server port | `6697` (SSL) or `6667` (non-SSL) | -| `nick` | Bot's nickname on IRC | `"DuckHunt"` | -| `channels` | List of channels to auto-join | `["#channel1", "#channel2"]` | -| `ssl` | Use SSL/TLS encryption | `true` (recommended) | -| `password` | Server password (I-Line auth) | Change if server requires auth | -| `max_retries` | Connection retry attempts | `3` | -| `retry_delay` | Seconds between retries | `5` | -| `timeout` | Connection timeout in seconds | `30` | - -## ๐Ÿ” SASL Authentication (`sasl`) - -SASL is used for NickServ authentication on connect. - -| Setting | Description | Example | -|---------|-------------|---------| -| `enabled` | Enable SASL authentication | `false` | -| `username` | Registered nickname | `"your_registered_nick"` | -| `password` | NickServ password | `"your_nickserv_password"` | - -**Note:** Change `password` from the default if enabling SASL! - -## ๐Ÿ‘‘ Bot Administration (`admins`) - -Array of IRC nicknames with admin privileges. - -```json -"admins": ["peorth", "computertech", "colby"] -``` - -## ๐Ÿฆ† Duck Spawning (`duck_spawning`) - -| Setting | Description | Default | -|---------|-------------|---------| -| `spawn_min` | Minimum seconds between spawns | `10` | -| `spawn_max` | Maximum seconds between spawns | `30` | -| `timeout` | Global fallback timeout | `60` | -| `rearm_on_duck_shot` | Auto-rearm guns when duck shot | `true` | - -## ๐ŸŽฏ Duck Types (`duck_types`) - -### Normal Ducks (`normal`) -- `xp`: XP reward (default: `10`) -- `timeout`: Seconds before flying away (default: `60`) - -### Golden Ducks (`golden`) -- `chance`: Spawn probability (default: `0.15` = 15%) -- `min_hp`: Minimum hit points (default: `3`) -- `max_hp`: Maximum hit points (default: `5`) -- `xp`: XP reward (default: `15`) -- `timeout`: Seconds before flying away (default: `60`) - -### Fast Ducks (`fast`) -- `chance`: Spawn probability (default: `0.25` = 25%) -- `timeout`: Seconds before flying away (default: `20`) -- `xp`: XP reward (default: `12`) - -**Note:** Chances are decimal percentages (0.15 = 15%, 0.25 = 25%) - -## ๐Ÿ‘ค New Player Defaults (`player_defaults`) - -Starting values for new players: - -| Setting | Description | Default | -|---------|-------------|---------| -| `accuracy` | Starting accuracy percentage (0-100) | `75` | -| `magazines` | Starting number of magazines | `3` | -| `bullets_per_magazine` | Bullets per magazine | `6` | -| `jam_chance` | Gun jam percentage (0-100) | `15` | -| `xp` | Starting XP (also currency) | `0` | - -## ๐ŸŽฎ Game Mechanics (`gameplay`) - -| Setting | Description | Default | -|---------|-------------|---------| -| `befriend_success_rate` | Base befriend chance (%) | `75` | -| `befriend_xp` | XP from befriending | `5` | -| `accuracy_gain_on_hit` | Accuracy boost per hit | `1` | -| `accuracy_loss_on_miss` | Accuracy loss per miss | `2` | -| `min_accuracy` | Minimum accuracy limit | `10` | -| `max_accuracy` | Maximum accuracy limit | `100` | -| `min_befriend_success_rate` | Min befriend rate | `5` | -| `max_befriend_success_rate` | Max befriend rate | `95` | - -## ๐Ÿ”ง Feature Toggles (`features`) - -| Setting | Description | Default | -|---------|-------------|---------| -| `shop_enabled` | Enable shop system | `true` | -| `inventory_enabled` | Enable inventory system | `true` | -| `auto_rearm_enabled` | Enable auto gun rearming | `true` | - -## โš–๏ธ System Limits (`limits`) - -| Setting | Description | Default | -|---------|-------------|---------| -| `max_inventory_items` | Max items per player | `20` | -| `max_temp_effects` | Max temporary effects | `20` | - ---- - -## ๐Ÿ”ง Configuration Access - -The bot uses dot notation to access nested settings: - -```python -# Examples: -server = bot.get_config('connection.server') -normal_xp = bot.get_config('duck_types.normal.xp') -player_accuracy = bot.get_config('player_defaults.accuracy') -``` - -## ๐Ÿ“ Tips - -1. **Percentages:** Most percentage values use 0-100 scale, but spawn chances use 0.0-1.0 decimals -2. **Authentication:** Set real passwords when using server auth or SASL -3. **Balance:** Adjust XP rewards and duck spawn rates to balance gameplay -4. **Testing:** Change one setting at a time to test effects -5. **Backup:** Keep a backup of working config before major changes \ No newline at end of file diff --git a/config.json b/config.json index 98c07e4..947d82a 100644 --- a/config.json +++ b/config.json @@ -2,7 +2,7 @@ "connection": { "server": "irc.rizon.net", "port": 6697, - "nick": "DuckHunt", + "nick": "DickHunt", "channels": [ "#ct" ], @@ -13,7 +13,7 @@ "timeout": 30 }, "sasl": { - "enabled": true, + "enabled": false, "username": "duckhunt", "password": "duckhunt//789//" }, @@ -24,8 +24,8 @@ "colby" ], "duck_spawning": { - "spawn_min": 10, - "spawn_max": 30, + "spawn_min": 1200, + "spawn_max": 3600, "timeout": 60, "rearm_on_duck_shot": true }, @@ -62,7 +62,8 @@ "min_accuracy": 10, "max_accuracy": 100, "min_befriend_success_rate": 5, - "max_befriend_success_rate": 95 + "max_befriend_success_rate": 95, + "wet_clothes_duration": 300 }, "features": { "shop_enabled": true, diff --git a/duckhunt.json b/duckhunt.json index e6f9d5b..3e041b9 100644 --- a/duckhunt.json +++ b/duckhunt.json @@ -1,19 +1,23 @@ { "players": { "computertech": { - "nick": "ComputerTech", - "xp": 60, - "ducks_shot": 5, + "nick": "computertech", + "xp": 67, + "ducks_shot": 6, "ducks_befriended": 2, - "accuracy": 62, + "shots_fired": 2, + "shots_missed": 1, + "accuracy": 63, "gun_confiscated": false, - "current_ammo": 5, + "current_ammo": 6, "magazines": 3, "bullets_per_magazine": 6, "jam_chance": 5, - "inventory": {}, + "inventory": { + "1": 1 + }, "temporary_effects": [] } }, - "last_save": "1758825127.4272072" + "last_save": "1758909688.9995604" } \ No newline at end of file diff --git a/duckhunt.log b/duckhunt.log index f06dd90..2a1123a 100644 --- a/duckhunt.log +++ b/duckhunt.log @@ -651,3 +651,78 @@ 2025-09-24 18:56:53,434 [INFO ] DuckHuntBot.Shop - load_items:30: Loaded 5 shop items from /home/colby/duckhunt/shop.json 2025-09-24 18:56:53,436 [INFO ] DuckHuntBot.Levels - load_levels:28: Loaded 8 levels from /home/colby/duckhunt/levels.json 2025-09-24 18:56:53,439 [INFO ] DuckHuntBot.DB - load_database:47: Loaded 1 players from duckhunt.json +2025-09-25 18:05:24,467 [INFO ] DuckHuntBot - setup_logger:61: Enhanced logging system initialized with file rotation +2025-09-25 18:05:24,469 [INFO ] DuckHuntBot.DB - load_database:48: Loaded 1 players from duckhunt.json +2025-09-25 18:05:24,470 [INFO ] SASL - setup_logger:61: Enhanced logging system initialized with file rotation +2025-09-25 18:05:24,471 [INFO ] DuckHuntBot.Shop - load_items:30: Loaded 5 shop items from /home/colby/duckhunt/shop.json +2025-09-25 18:05:24,478 [INFO ] DuckHuntBot.Levels - load_levels:28: Loaded 8 levels from /home/colby/duckhunt/levels.json +2025-09-25 18:05:24,479 [INFO ] DuckHuntBot - main:28: ๐Ÿฆ† Starting DuckHunt Bot... +2025-09-25 18:05:24,568 [INFO ] DuckHuntBot - connect:115: Attempting to connect to irc.rizon.net:6697 (attempt 1/3) +2025-09-25 18:05:25,012 [INFO ] DuckHuntBot - connect:126: โœ… Successfully connected to irc.rizon.net:6697 +2025-09-25 18:05:25,013 [INFO ] SASL - start_negotiation:45: SASL authentication enabled +2025-09-25 18:05:25,013 [INFO ] DuckHuntBot - run:774: ๐Ÿฆ† Bot is now running! Press Ctrl+C to stop. +2025-09-25 18:05:27,048 [INFO ] SASL - handle_cap_response:59: Server capabilities: ['away-notify', 'chghost', 'invite-notify', 'multi-prefix', 'sasl', 'userhost-in-names'] +2025-09-25 18:05:27,048 [INFO ] SASL - handle_cap_response:61: SASL capability available +2025-09-25 18:05:27,098 [INFO ] SASL - handle_cap_response:72: SASL capability acknowledged +2025-09-25 18:05:27,098 [INFO ] SASL - handle_cap_response:74: Authenticating via SASL as duckhunt +2025-09-25 18:05:27,099 [INFO ] SASL - handle_sasl:94: Sending AUTHENTICATE PLAIN +2025-09-25 18:05:27,507 [INFO ] SASL - handle_authenticate_response:103: Server ready for SASL authentication +2025-09-25 18:05:27,508 [DEBUG ] SASL - handle_authenticate_response:106: Auth string length: 33 chars +2025-09-25 18:05:27,508 [DEBUG ] SASL - handle_authenticate_response:107: Auth components: user='duckhunt', pass='duc...' +2025-09-25 18:05:27,508 [DEBUG ] SASL - handle_authenticate_response:110: Base64 encoded length: 44 chars +2025-09-25 18:05:27,508 [DEBUG ] SASL - handle_authenticate_response:111: Sending: AUTHENTICATE ZHVja2h1bnQAZHVja2h1... +2025-09-25 18:05:27,918 [INFO ] SASL - handle_sasl_result:123: SASL authentication successful! +2025-09-25 18:05:28,019 [INFO ] DuckHuntBot - handle_message:232: Successfully registered with IRC server +2025-09-25 18:05:47,029 [INFO ] DuckHuntBot.Game - spawn_duck:149: Fast duck (hidden) spawned in #ct +2025-09-25 18:05:51,297 [DEBUG ] DuckHuntBot.DB - save_database:134: Database saved successfully with 1 players +2025-09-25 18:06:06,048 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct +2025-09-25 18:06:13,814 [DEBUG ] DuckHuntBot.DB - save_database:134: Database saved successfully with 1 players +2025-09-25 18:06:24,064 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct +2025-09-25 18:07:26,130 [INFO ] DuckHuntBot.Game - spawn_duck:149: Fast duck (hidden) spawned in #ct +2025-09-25 18:07:56,160 [INFO ] DuckHuntBot.Game - spawn_duck:137: Golden duck (hidden) spawned in #ct with 3 HP +2025-09-25 18:09:17,234 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct +2025-09-25 18:10:24,298 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct +2025-09-25 18:11:42,412 [INFO ] DuckHuntBot.Game - spawn_duck:137: Golden duck (hidden) spawned in #ct with 4 HP +2025-09-25 18:12:55,485 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct +2025-09-25 18:14:06,578 [INFO ] DuckHuntBot.Game - spawn_duck:137: Golden duck (hidden) spawned in #ct with 3 HP +2025-09-25 18:15:14,722 [INFO ] DuckHuntBot.Game - spawn_duck:149: Fast duck (hidden) spawned in #ct +2025-09-25 18:15:54,792 [INFO ] DuckHuntBot.Game - spawn_duck:149: Fast duck (hidden) spawned in #ct +2025-09-25 18:16:15,812 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct +2025-09-25 18:17:22,878 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct +2025-09-25 18:18:31,953 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct +2025-09-25 18:19:49,029 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct +2025-09-25 18:20:57,099 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct +2025-09-25 18:22:04,175 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct +2025-09-25 18:23:19,252 [INFO ] DuckHuntBot.Game - spawn_duck:149: Fast duck (hidden) spawned in #ct +2025-09-25 18:23:46,278 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct +2025-09-25 18:24:57,354 [INFO ] DuckHuntBot.Game - spawn_duck:137: Golden duck (hidden) spawned in #ct with 3 HP +2025-09-25 18:26:17,435 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct +2025-09-25 18:27:24,505 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct +2025-09-25 18:28:33,580 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct +2025-09-25 18:29:37,647 [INFO ] DuckHuntBot.Game - spawn_duck:149: Fast duck (hidden) spawned in #ct +2025-09-25 18:30:17,686 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct +2025-09-25 18:31:35,762 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct +2025-09-25 18:33:01,847 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct +2025-09-25 18:34:13,914 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct +2025-09-25 18:35:27,982 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct +2025-09-25 18:36:39,046 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct +2025-09-25 18:37:41,102 [INFO ] DuckHuntBot.Game - spawn_duck:149: Fast duck (hidden) spawned in #ct +2025-09-25 18:38:18,132 [INFO ] DuckHuntBot.Game - spawn_duck:149: Fast duck (hidden) spawned in #ct +2025-09-25 18:38:44,166 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct +2025-09-25 18:40:12,271 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct +2025-09-25 18:41:23,349 [INFO ] DuckHuntBot.Game - spawn_duck:137: Golden duck (hidden) spawned in #ct with 3 HP +2025-09-25 18:42:28,415 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct +2025-09-25 18:43:52,494 [INFO ] DuckHuntBot.Game - spawn_duck:137: Golden duck (hidden) spawned in #ct with 4 HP +2025-09-25 18:45:03,560 [INFO ] DuckHuntBot.Game - spawn_duck:137: Golden duck (hidden) spawned in #ct with 3 HP +2025-09-25 18:46:04,620 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct +2025-09-25 18:47:20,702 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct +2025-09-25 18:48:29,761 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct +2025-09-25 18:49:35,827 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct +2025-09-25 18:50:48,894 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct +2025-09-25 18:51:51,944 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct +2025-09-25 18:52:58,006 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct +2025-09-25 18:53:59,056 [INFO ] DuckHuntBot.Game - spawn_duck:137: Golden duck (hidden) spawned in #ct with 4 HP +2025-09-25 18:55:08,121 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct +2025-09-25 18:56:27,201 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct +2025-09-25 18:57:44,273 [INFO ] DuckHuntBot.Game - spawn_duck:137: Golden duck (hidden) spawned in #ct with 3 HP + \ No newline at end of file diff --git a/logs/duckhunt.log b/logs/duckhunt.log new file mode 100644 index 0000000..1ab517b --- /dev/null +++ b/logs/duckhunt.log @@ -0,0 +1,412 @@ +19:30:08.709 ๐Ÿ“˜ INFO DuckHuntBot Unified logging system initialized: all logs in duckhunt.log +19:30:08.709 ๐Ÿ“˜ INFO DuckHuntBot Debug mode: ON +19:30:08.710 ๐Ÿ“˜ INFO DuckHuntBot Log everything: YES +19:30:08.710 ๐Ÿ“˜ INFO DuckHuntBot Unified format: YES +19:30:08.710 ๐Ÿ“˜ INFO DuckHuntBot Console level: INFO +19:30:08.710 ๐Ÿ“˜ INFO DuckHuntBot File level: DEBUG +19:30:08.711 ๐Ÿ“˜ INFO DuckHuntBot Main log: /home/colby/duckhunt/logs/duckhunt.log +19:30:08.711 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿค– Initializing DuckHunt Bot components... +19:30:08.712 ๐Ÿ“˜ INFO DuckHuntBot.DB Loaded 1 players from duckhunt.json +19:30:08.713 ๐Ÿ“˜ INFO SASL Unified logging system initialized: all logs in duckhunt.log +19:30:08.713 ๐Ÿ“˜ INFO SASL Debug mode: ON +19:30:08.714 ๐Ÿ“˜ INFO SASL Log everything: YES +19:30:08.714 ๐Ÿ“˜ INFO SASL Unified format: YES +19:30:08.714 ๐Ÿ“˜ INFO SASL Console level: INFO +19:30:08.715 ๐Ÿ“˜ INFO SASL File level: DEBUG +19:30:08.715 ๐Ÿ“˜ INFO SASL Main log: /home/colby/duckhunt/logs/duckhunt.log +19:30:08.715 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿ‘‘ Configured 3 admin(s): peorth, computertech, colby +19:30:08.716 ๐Ÿ“˜ INFO DuckHuntBot.Shop Loaded 5 shop items from /home/colby/duckhunt/shop.json +19:30:08.718 ๐Ÿ“˜ INFO DuckHuntBot.Levels Loaded 8 levels from /home/colby/duckhunt/levels.json +19:30:08.718 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿฆ† Starting DuckHunt Bot... +19:30:08.807 ๐Ÿ“˜ INFO DuckHuntBot Attempting to connect to irc.rizon.net:6697 (attempt 1/3) +19:30:09.028 ๐Ÿ“˜ INFO DuckHuntBot โœ… Successfully connected to irc.rizon.net:6697 +19:30:09.028 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿ” Sending server password +19:30:09.028 ๐Ÿ“˜ INFO SASL SASL authentication enabled +19:30:09.029 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿฆ† Bot is now running! Press Ctrl+C to stop. +19:30:09.416 ๐Ÿ“˜ INFO SASL Server capabilities: ['away-notify', 'chghost', 'invite-notify', 'multi-prefix', 'sasl', 'userhost-in-names'] +19:30:09.417 ๐Ÿ“˜ INFO SASL SASL capability available +19:30:09.476 ๐Ÿ“˜ INFO SASL SASL capability acknowledged +19:30:09.477 ๐Ÿ“˜ INFO SASL Authenticating via SASL as duckhunt +19:30:09.477 ๐Ÿ“˜ INFO SASL Sending AUTHENTICATE PLAIN +19:30:09.887 ๐Ÿ“˜ INFO SASL Server ready for SASL authentication +19:30:09.888 ๐Ÿ” DEBUG SASL Auth string length: 33 chars [handle_authenticate_response:106] +19:30:09.888 ๐Ÿ” DEBUG SASL Auth components: user='duckhunt', pass='duc...' [handle_authenticate_response:107] +19:30:09.888 ๐Ÿ” DEBUG SASL Base64 encoded length: 44 chars [handle_authenticate_response:110] +19:30:09.889 ๐Ÿ” DEBUG SASL Sending: AUTHENTICATE ZHVja2h1bnQAZHVja2h1... [handle_authenticate_response:111] +19:30:10.304 ๐Ÿ“˜ INFO SASL SASL authentication successful! +19:30:10.430 ๐Ÿ“˜ INFO DuckHuntBot Successfully registered with IRC server +19:30:34.049 ๐Ÿ“˜ INFO DuckHuntBot.Game Fast duck (hidden) spawned in #ct +19:31:07.073 ๐Ÿ“˜ INFO DuckHuntBot.Game Normal duck spawned in #ct +19:31:24.462 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿ›‘ Received SIGINT (Ctrl+C), shutting down immediately... +19:31:24.463 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿ”„ Cancelled 5 running tasks +19:31:24.544 ๐Ÿ“˜ INFO DuckHuntBot Message loop cancelled +19:31:24.544 ๐Ÿ“˜ INFO DuckHuntBot Message loop ended +19:31:24.545 ๐Ÿ“˜ INFO DuckHuntBot.Game Duck spawning loop cancelled +19:31:24.545 ๐Ÿ“˜ INFO DuckHuntBot.Game Duck timeout loop cancelled +19:31:24.546 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿ›‘ Main loop cancelled +19:31:24.547 ๐Ÿ“˜ INFO DuckHuntBot.Game Game loops cancelled +19:31:24.570 ๐Ÿ” DEBUG DuckHuntBot.DB Database saved successfully with 1 players [save_database:134] +19:31:24.571 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿ’พ Database saved +19:31:24.773 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿ”Œ IRC connection closed +19:31:24.774 ๐Ÿ“˜ INFO DuckHuntBot โœ… Bot shutdown complete +19:31:25.531 ๐Ÿ“˜ INFO DuckHuntBot Unified logging system initialized: all logs in duckhunt.log +19:31:25.531 ๐Ÿ“˜ INFO DuckHuntBot Debug mode: ON +19:31:25.531 ๐Ÿ“˜ INFO DuckHuntBot Log everything: YES +19:31:25.531 ๐Ÿ“˜ INFO DuckHuntBot Unified format: YES +19:31:25.532 ๐Ÿ“˜ INFO DuckHuntBot Console level: INFO +19:31:25.532 ๐Ÿ“˜ INFO DuckHuntBot File level: DEBUG +19:31:25.532 ๐Ÿ“˜ INFO DuckHuntBot Main log: /home/colby/duckhunt/logs/duckhunt.log +19:31:25.532 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿค– Initializing DuckHunt Bot components... +19:31:25.533 ๐Ÿ“˜ INFO DuckHuntBot.DB Loaded 1 players from duckhunt.json +19:31:25.533 ๐Ÿ“˜ INFO SASL Unified logging system initialized: all logs in duckhunt.log +19:31:25.534 ๐Ÿ“˜ INFO SASL Debug mode: ON +19:31:25.534 ๐Ÿ“˜ INFO SASL Log everything: YES +19:31:25.534 ๐Ÿ“˜ INFO SASL Unified format: YES +19:31:25.534 ๐Ÿ“˜ INFO SASL Console level: INFO +19:31:25.534 ๐Ÿ“˜ INFO SASL File level: DEBUG +19:31:25.535 ๐Ÿ“˜ INFO SASL Main log: /home/colby/duckhunt/logs/duckhunt.log +19:31:25.535 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿ‘‘ Configured 3 admin(s): peorth, computertech, colby +19:31:25.536 ๐Ÿ“˜ INFO DuckHuntBot.Shop Loaded 5 shop items from /home/colby/duckhunt/shop.json +19:31:25.537 ๐Ÿ“˜ INFO DuckHuntBot.Levels Loaded 8 levels from /home/colby/duckhunt/levels.json +19:31:25.537 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿฆ† Starting DuckHunt Bot... +19:31:25.621 ๐Ÿ“˜ INFO DuckHuntBot Attempting to connect to irc.rizon.net:6697 (attempt 1/3) +19:31:25.815 ๐Ÿ“˜ INFO DuckHuntBot โœ… Successfully connected to irc.rizon.net:6697 +19:31:25.815 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿ” Sending server password +19:31:25.816 ๐Ÿ“˜ INFO SASL SASL authentication enabled +19:31:25.816 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿฆ† Bot is now running! Press Ctrl+C to stop. +19:31:27.051 ๐Ÿ“˜ INFO SASL Server capabilities: ['away-notify', 'chghost', 'invite-notify', 'multi-prefix', 'sasl', 'userhost-in-names'] +19:31:27.051 ๐Ÿ“˜ INFO SASL SASL capability available +19:31:27.100 ๐Ÿ“˜ INFO SASL SASL capability acknowledged +19:31:27.103 ๐Ÿ“˜ INFO SASL Authenticating via SASL as duckhunt +19:31:27.103 ๐Ÿ“˜ INFO SASL Sending AUTHENTICATE PLAIN +19:31:27.512 ๐Ÿ“˜ INFO SASL Server ready for SASL authentication +19:31:27.513 ๐Ÿ” DEBUG SASL Auth string length: 33 chars [handle_authenticate_response:106] +19:31:27.513 ๐Ÿ” DEBUG SASL Auth components: user='duckhunt', pass='duc...' [handle_authenticate_response:107] +19:31:27.513 ๐Ÿ” DEBUG SASL Base64 encoded length: 44 chars [handle_authenticate_response:110] +19:31:27.513 ๐Ÿ” DEBUG SASL Sending: AUTHENTICATE ZHVja2h1bnQAZHVja2h1... [handle_authenticate_response:111] +19:31:27.922 ๐Ÿ“˜ INFO SASL SASL authentication successful! +19:31:29.097 ๐Ÿ“˜ INFO DuckHuntBot Successfully registered with IRC server +19:31:46.833 ๐Ÿ“˜ INFO DuckHuntBot.Game Normal duck spawned in #ct +19:32:07.421 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿ›‘ Received SIGINT (Ctrl+C), shutting down immediately... +19:32:07.422 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿ”„ Cancelled 5 running tasks +19:32:07.424 ๐Ÿ“˜ INFO DuckHuntBot Message loop cancelled +19:32:07.424 ๐Ÿ“˜ INFO DuckHuntBot Message loop ended +19:32:07.425 ๐Ÿ“˜ INFO DuckHuntBot.Game Duck spawning loop cancelled +19:32:07.425 ๐Ÿ“˜ INFO DuckHuntBot.Game Duck timeout loop cancelled +19:32:07.425 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿ›‘ Main loop cancelled +19:32:07.426 ๐Ÿ“˜ INFO DuckHuntBot.Game Game loops cancelled +19:32:07.439 ๐Ÿ” DEBUG DuckHuntBot.DB Database saved successfully with 1 players [save_database:134] +19:32:07.439 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿ’พ Database saved +19:32:07.647 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿ”Œ IRC connection closed +19:32:07.650 ๐Ÿ“˜ INFO DuckHuntBot โœ… Bot shutdown complete +17:11:18.874 ๐Ÿ“˜ INFO DuckHuntBot Unified logging system initialized: all logs in duckhunt.log +17:11:18.874 ๐Ÿ“˜ INFO DuckHuntBot Debug mode: ON +17:11:18.874 ๐Ÿ“˜ INFO DuckHuntBot Log everything: YES +17:11:18.875 ๐Ÿ“˜ INFO DuckHuntBot Unified format: YES +17:11:18.875 ๐Ÿ“˜ INFO DuckHuntBot Console level: INFO +17:11:18.875 ๐Ÿ“˜ INFO DuckHuntBot File level: DEBUG +17:11:18.875 ๐Ÿ“˜ INFO DuckHuntBot Main log: /home/colby/duckhunt/logs/duckhunt.log +17:11:18.876 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿค– Initializing DuckHunt Bot components... +17:11:18.883 ๐Ÿ“˜ INFO DuckHuntBot.DB Loaded 1 players from duckhunt.json +17:11:18.884 ๐Ÿ“˜ INFO SASL Unified logging system initialized: all logs in duckhunt.log +17:11:18.884 ๐Ÿ“˜ INFO SASL Debug mode: ON +17:11:18.884 ๐Ÿ“˜ INFO SASL Log everything: YES +17:11:18.885 ๐Ÿ“˜ INFO SASL Unified format: YES +17:11:18.885 ๐Ÿ“˜ INFO SASL Console level: INFO +17:11:18.885 ๐Ÿ“˜ INFO SASL File level: DEBUG +17:11:18.885 ๐Ÿ“˜ INFO SASL Main log: /home/colby/duckhunt/logs/duckhunt.log +17:11:18.885 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿ‘‘ Configured 3 admin(s): peorth, computertech, colby +17:11:18.886 ๐Ÿ“˜ INFO DuckHuntBot.Shop Loaded 6 shop items from /home/colby/duckhunt/shop.json +17:11:18.886 ๐Ÿ“˜ INFO DuckHuntBot.Levels Loaded 8 levels from /home/colby/duckhunt/levels.json +17:11:18.886 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿฆ† Starting DuckHunt Bot... +17:11:18.941 ๐Ÿ“˜ INFO DuckHuntBot Attempting to connect to irc.rizon.net:6697 (attempt 1/3) +17:11:19.228 ๐Ÿ“˜ INFO DuckHuntBot โœ… Successfully connected to irc.rizon.net:6697 +17:11:19.228 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿ” Sending server password +17:11:19.228 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿฆ† Bot is now running! Press Ctrl+C to stop. +17:11:21.111 ๐Ÿ“˜ INFO DuckHuntBot Successfully registered with IRC server +17:11:38.245 ๐Ÿ“˜ INFO DuckHuntBot.Game Normal duck spawned in #ct +17:12:50.310 ๐Ÿ“˜ INFO DuckHuntBot.Game Fast duck (hidden) spawned in #ct +17:13:11.327 ๐Ÿ“˜ INFO DuckHuntBot.Game Normal duck spawned in #ct +17:14:31.393 ๐Ÿ“˜ INFO DuckHuntBot.Game Normal duck spawned in #ct +17:15:37.451 ๐Ÿ“˜ INFO DuckHuntBot.Game Normal duck spawned in #ct +17:16:52.528 ๐Ÿ“˜ INFO DuckHuntBot.Game Golden duck (hidden) spawned in #ct with 5 HP +17:17:56.586 ๐Ÿ“˜ INFO DuckHuntBot.Game Normal duck spawned in #ct +17:18:59.645 ๐Ÿ“˜ INFO DuckHuntBot.Game Fast duck (hidden) spawned in #ct +17:19:06.831 ๐Ÿ” DEBUG DuckHuntBot.DB Database saved successfully with 1 players [save_database:136] +17:19:14.373 ๐Ÿ” DEBUG DuckHuntBot.DB Database saved successfully with 1 players [save_database:136] +17:19:16.665 ๐Ÿ“˜ INFO DuckHuntBot.Game Normal duck spawned in #ct +17:20:39.736 ๐Ÿ“˜ INFO DuckHuntBot.Game Golden duck (hidden) spawned in #ct with 3 HP +17:21:48.800 ๐Ÿ“˜ INFO DuckHuntBot.Game Golden duck (hidden) spawned in #ct with 5 HP +17:22:49.858 ๐Ÿ“˜ INFO DuckHuntBot.Game Normal duck spawned in #ct +17:24:01.922 ๐Ÿ“˜ INFO DuckHuntBot.Game Golden duck (hidden) spawned in #ct with 4 HP +17:25:10.997 ๐Ÿ“˜ INFO DuckHuntBot.Game Normal duck spawned in #ct +17:26:32.071 ๐Ÿ“˜ INFO DuckHuntBot.Game Normal duck spawned in #ct +17:27:35.135 ๐Ÿ“˜ INFO DuckHuntBot.Game Normal duck spawned in #ct +17:28:57.215 ๐Ÿ“˜ INFO DuckHuntBot.Game Normal duck spawned in #ct +17:30:15.284 ๐Ÿ“˜ INFO DuckHuntBot.Game Normal duck spawned in #ct +17:31:30.355 ๐Ÿ“˜ INFO DuckHuntBot.Game Fast duck (hidden) spawned in #ct +17:32:06.393 ๐Ÿ“˜ INFO DuckHuntBot.Game Golden duck (hidden) spawned in #ct with 5 HP +17:33:13.455 ๐Ÿ“˜ INFO DuckHuntBot.Game Normal duck spawned in #ct +17:34:22.518 ๐Ÿ“˜ INFO DuckHuntBot.Game Normal duck spawned in #ct +17:35:24.570 ๐Ÿ“˜ INFO DuckHuntBot.Game Normal duck spawned in #ct +17:36:37.639 ๐Ÿ“˜ INFO DuckHuntBot.Game Normal duck spawned in #ct +17:37:54.733 ๐Ÿ“˜ INFO DuckHuntBot.Game Fast duck (hidden) spawned in #ct +17:38:16.763 ๐Ÿ“˜ INFO DuckHuntBot.Game Golden duck (hidden) spawned in #ct with 5 HP +17:39:18.620 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿ›‘ Received SIGINT (Ctrl+C), shutting down immediately... +17:39:20.268 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿ”„ Cancelled 5 running tasks +17:39:20.271 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿ›‘ Main loop cancelled +17:39:20.272 ๐Ÿ“˜ INFO DuckHuntBot.Game Duck spawning loop cancelled +17:39:20.279 ๐Ÿ“˜ INFO DuckHuntBot.Game Duck timeout loop cancelled +17:39:20.279 ๐Ÿ“˜ INFO DuckHuntBot Message loop cancelled +17:39:20.280 ๐Ÿ“˜ INFO DuckHuntBot Message loop ended +17:39:20.280 ๐Ÿ“˜ INFO DuckHuntBot.Game Game loops cancelled +17:39:20.321 ๐Ÿ” DEBUG DuckHuntBot.DB Database saved successfully with 1 players [save_database:136] +17:39:20.321 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿ’พ Database saved +17:39:20.567 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿ”Œ IRC connection closed +17:39:20.568 ๐Ÿ“˜ INFO DuckHuntBot โœ… Bot shutdown complete +18:37:16.947 ๐Ÿ“˜ INFO DuckHuntBot Unified logging system initialized: all logs in duckhunt.log +18:37:16.948 ๐Ÿ“˜ INFO DuckHuntBot Debug mode: ON +18:37:16.949 ๐Ÿ“˜ INFO DuckHuntBot Log everything: YES +18:37:16.949 ๐Ÿ“˜ INFO DuckHuntBot Unified format: YES +18:37:16.949 ๐Ÿ“˜ INFO DuckHuntBot Console level: INFO +18:37:16.949 ๐Ÿ“˜ INFO DuckHuntBot File level: DEBUG +18:37:16.950 ๐Ÿ“˜ INFO DuckHuntBot Main log: /home/colby/duckhunt/logs/duckhunt.log +18:37:16.950 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿค– Initializing DuckHunt Bot components... +18:37:16.958 ๐Ÿ“˜ INFO DuckHuntBot.DB Loaded 1 players from duckhunt.json +18:37:16.959 ๐Ÿ“˜ INFO SASL Unified logging system initialized: all logs in duckhunt.log +18:37:16.960 ๐Ÿ“˜ INFO SASL Debug mode: ON +18:37:16.960 ๐Ÿ“˜ INFO SASL Log everything: YES +18:37:16.960 ๐Ÿ“˜ INFO SASL Unified format: YES +18:37:16.960 ๐Ÿ“˜ INFO SASL Console level: INFO +18:37:16.960 ๐Ÿ“˜ INFO SASL File level: DEBUG +18:37:16.960 ๐Ÿ“˜ INFO SASL Main log: /home/colby/duckhunt/logs/duckhunt.log +18:37:16.961 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿ‘‘ Configured 3 admin(s): peorth, computertech, colby +18:37:16.962 ๐Ÿ“˜ INFO DuckHuntBot.Shop Loaded 9 shop items from /home/colby/duckhunt/shop.json +18:37:16.962 ๐Ÿ“˜ INFO DuckHuntBot.Levels Loaded 8 levels from /home/colby/duckhunt/levels.json +18:37:16.963 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿฆ† Starting DuckHunt Bot... +18:37:17.032 ๐Ÿ“˜ INFO DuckHuntBot Attempting to connect to irc.rizon.net:6697 (attempt 1/3) +18:37:17.428 ๐Ÿ“˜ INFO DuckHuntBot โœ… Successfully connected to irc.rizon.net:6697 +18:37:17.428 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿ” Sending server password +18:37:17.429 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿฆ† Bot is now running! Press Ctrl+C to stop. +18:37:19.105 ๐Ÿ“˜ INFO DuckHuntBot Successfully registered with IRC server +18:37:32.441 ๐Ÿ“˜ INFO DuckHuntBot.Game Golden duck (hidden) spawned in #ct with 4 HP +18:38:34.495 ๐Ÿ“˜ INFO DuckHuntBot.Game Golden duck (hidden) spawned in #ct with 4 HP +18:39:37.167 ๐Ÿ” DEBUG DuckHuntBot.DB Database saved successfully with 1 players [save_database:142] +18:39:43.570 ๐Ÿ“˜ INFO DuckHuntBot.Game Golden duck (hidden) spawned in #ct with 3 HP +18:40:51.627 ๐Ÿ“˜ INFO DuckHuntBot.Game Fast duck (hidden) spawned in #ct +18:41:14.648 ๐Ÿ“˜ INFO DuckHuntBot.Game Golden duck (hidden) spawned in #ct with 4 HP +18:42:21.722 ๐Ÿ“˜ INFO DuckHuntBot.Game Normal duck spawned in #ct +18:43:25.380 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿ›‘ Received SIGINT (Ctrl+C), shutting down immediately... +18:43:25.389 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿ”„ Cancelled 5 running tasks +18:43:25.411 ๐Ÿ“˜ INFO DuckHuntBot.Game Duck spawning loop cancelled +18:43:25.411 ๐Ÿ“˜ INFO DuckHuntBot.Game Duck timeout loop cancelled +18:43:25.412 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿ›‘ Main loop cancelled +18:43:25.416 ๐Ÿ“˜ INFO DuckHuntBot Message loop cancelled +18:43:25.416 ๐Ÿ“˜ INFO DuckHuntBot Message loop ended +18:43:25.416 ๐Ÿ“˜ INFO DuckHuntBot.Game Game loops cancelled +18:43:25.426 ๐Ÿ” DEBUG DuckHuntBot.DB Database saved successfully with 1 players [save_database:142] +18:43:25.427 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿ’พ Database saved +18:43:25.639 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿ”Œ IRC connection closed +18:43:25.639 ๐Ÿ“˜ INFO DuckHuntBot โœ… Bot shutdown complete +18:43:26.132 ๐Ÿ“˜ INFO DuckHuntBot Unified logging system initialized: all logs in duckhunt.log +18:43:26.132 ๐Ÿ“˜ INFO DuckHuntBot Debug mode: ON +18:43:26.133 ๐Ÿ“˜ INFO DuckHuntBot Log everything: YES +18:43:26.133 ๐Ÿ“˜ INFO DuckHuntBot Unified format: YES +18:43:26.133 ๐Ÿ“˜ INFO DuckHuntBot Console level: INFO +18:43:26.133 ๐Ÿ“˜ INFO DuckHuntBot File level: DEBUG +18:43:26.133 ๐Ÿ“˜ INFO DuckHuntBot Main log: /home/colby/duckhunt/logs/duckhunt.log +18:43:26.133 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿค– Initializing DuckHunt Bot components... +18:43:26.134 ๐Ÿ“˜ INFO DuckHuntBot.DB Loaded 1 players from duckhunt.json +18:43:26.135 ๐Ÿ“˜ INFO SASL Unified logging system initialized: all logs in duckhunt.log +18:43:26.135 ๐Ÿ“˜ INFO SASL Debug mode: ON +18:43:26.136 ๐Ÿ“˜ INFO SASL Log everything: YES +18:43:26.136 ๐Ÿ“˜ INFO SASL Unified format: YES +18:43:26.136 ๐Ÿ“˜ INFO SASL Console level: INFO +18:43:26.136 ๐Ÿ“˜ INFO SASL File level: DEBUG +18:43:26.136 ๐Ÿ“˜ INFO SASL Main log: /home/colby/duckhunt/logs/duckhunt.log +18:43:26.137 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿ‘‘ Configured 3 admin(s): peorth, computertech, colby +18:43:26.137 ๐Ÿ“˜ INFO DuckHuntBot.Shop Loaded 9 shop items from /home/colby/duckhunt/shop.json +18:43:26.138 ๐Ÿ“˜ INFO DuckHuntBot.Levels Loaded 8 levels from /home/colby/duckhunt/levels.json +18:43:26.138 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿฆ† Starting DuckHunt Bot... +18:43:26.215 ๐Ÿ“˜ INFO DuckHuntBot Attempting to connect to irc.rizon.net:6697 (attempt 1/3) +18:43:26.392 ๐Ÿ“˜ INFO DuckHuntBot โœ… Successfully connected to irc.rizon.net:6697 +18:43:26.393 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿ” Sending server password +18:43:26.393 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿฆ† Bot is now running! Press Ctrl+C to stop. +18:43:30.050 ๐Ÿ“˜ INFO DuckHuntBot Successfully registered with IRC server +18:43:53.434 ๐Ÿ“˜ INFO DuckHuntBot.Game Fast duck (hidden) spawned in #ct +18:44:23.468 ๐Ÿ“˜ INFO DuckHuntBot.Game Normal duck spawned in #ct +18:45:10.514 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿ›‘ Received SIGINT (Ctrl+C), shutting down immediately... +18:45:10.515 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿ”„ Cancelled 5 running tasks +18:45:10.519 ๐Ÿ“˜ INFO DuckHuntBot Message loop cancelled +18:45:10.519 ๐Ÿ“˜ INFO DuckHuntBot Message loop ended +18:45:10.519 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿ›‘ Main loop cancelled +18:45:10.520 ๐Ÿ“˜ INFO DuckHuntBot.Game Duck spawning loop cancelled +18:45:10.520 ๐Ÿ“˜ INFO DuckHuntBot.Game Duck timeout loop cancelled +18:45:10.521 ๐Ÿ“˜ INFO DuckHuntBot.Game Game loops cancelled +18:45:10.550 ๐Ÿ” DEBUG DuckHuntBot.DB Database saved successfully with 1 players [save_database:142] +18:45:10.550 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿ’พ Database saved +18:45:10.755 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿ”Œ IRC connection closed +18:45:10.755 ๐Ÿ“˜ INFO DuckHuntBot โœ… Bot shutdown complete +18:45:11.133 ๐Ÿ“˜ INFO DuckHuntBot Unified logging system initialized: all logs in duckhunt.log +18:45:11.133 ๐Ÿ“˜ INFO DuckHuntBot Debug mode: ON +18:45:11.133 ๐Ÿ“˜ INFO DuckHuntBot Log everything: YES +18:45:11.133 ๐Ÿ“˜ INFO DuckHuntBot Unified format: YES +18:45:11.133 ๐Ÿ“˜ INFO DuckHuntBot Console level: INFO +18:45:11.133 ๐Ÿ“˜ INFO DuckHuntBot File level: DEBUG +18:45:11.134 ๐Ÿ“˜ INFO DuckHuntBot Main log: /home/colby/duckhunt/logs/duckhunt.log +18:45:11.134 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿค– Initializing DuckHunt Bot components... +18:45:11.134 ๐Ÿ“˜ INFO DuckHuntBot.DB Loaded 1 players from duckhunt.json +18:45:11.135 ๐Ÿ“˜ INFO SASL Unified logging system initialized: all logs in duckhunt.log +18:45:11.136 ๐Ÿ“˜ INFO SASL Debug mode: ON +18:45:11.136 ๐Ÿ“˜ INFO SASL Log everything: YES +18:45:11.136 ๐Ÿ“˜ INFO SASL Unified format: YES +18:45:11.136 ๐Ÿ“˜ INFO SASL Console level: INFO +18:45:11.136 ๐Ÿ“˜ INFO SASL File level: DEBUG +18:45:11.137 ๐Ÿ“˜ INFO SASL Main log: /home/colby/duckhunt/logs/duckhunt.log +18:45:11.137 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿ‘‘ Configured 3 admin(s): peorth, computertech, colby +18:45:11.137 ๐Ÿ“˜ INFO DuckHuntBot.Shop Loaded 9 shop items from /home/colby/duckhunt/shop.json +18:45:11.138 ๐Ÿ“˜ INFO DuckHuntBot.Levels Loaded 8 levels from /home/colby/duckhunt/levels.json +18:45:11.138 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿฆ† Starting DuckHunt Bot... +18:45:11.187 ๐Ÿ“˜ INFO DuckHuntBot Attempting to connect to irc.rizon.net:6697 (attempt 1/3) +18:45:11.355 ๐Ÿ“˜ INFO DuckHuntBot โœ… Successfully connected to irc.rizon.net:6697 +18:45:11.355 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿ” Sending server password +18:45:11.356 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿฆ† Bot is now running! Press Ctrl+C to stop. +18:45:13.291 ๐Ÿ“˜ INFO DuckHuntBot Successfully registered with IRC server +18:45:34.379 ๐Ÿ“˜ INFO DuckHuntBot.Game Fast duck (hidden) spawned in #ct +18:46:10.413 ๐Ÿ“˜ INFO DuckHuntBot.Game Normal duck spawned in #ct +18:47:06.032 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿ›‘ Received SIGINT (Ctrl+C), shutting down immediately... +18:47:06.041 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿ”„ Cancelled 5 running tasks +18:47:06.082 ๐Ÿ“˜ INFO DuckHuntBot Message loop cancelled +18:47:06.082 ๐Ÿ“˜ INFO DuckHuntBot Message loop ended +18:47:06.082 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿ›‘ Main loop cancelled +18:47:06.083 ๐Ÿ“˜ INFO DuckHuntBot.Game Duck spawning loop cancelled +18:47:06.084 ๐Ÿ“˜ INFO DuckHuntBot.Game Duck timeout loop cancelled +18:47:06.084 ๐Ÿ“˜ INFO DuckHuntBot.Game Game loops cancelled +18:47:06.108 ๐Ÿ” DEBUG DuckHuntBot.DB Database saved successfully with 1 players [save_database:142] +18:47:06.109 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿ’พ Database saved +18:47:06.324 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿ”Œ IRC connection closed +18:47:06.325 ๐Ÿ“˜ INFO DuckHuntBot โœ… Bot shutdown complete +18:47:06.755 ๐Ÿ“˜ INFO DuckHuntBot Unified logging system initialized: all logs in duckhunt.log +18:47:06.755 ๐Ÿ“˜ INFO DuckHuntBot Debug mode: ON +18:47:06.756 ๐Ÿ“˜ INFO DuckHuntBot Log everything: YES +18:47:06.756 ๐Ÿ“˜ INFO DuckHuntBot Unified format: YES +18:47:06.756 ๐Ÿ“˜ INFO DuckHuntBot Console level: INFO +18:47:06.756 ๐Ÿ“˜ INFO DuckHuntBot File level: DEBUG +18:47:06.756 ๐Ÿ“˜ INFO DuckHuntBot Main log: /home/colby/duckhunt/logs/duckhunt.log +18:47:06.757 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿค– Initializing DuckHunt Bot components... +18:47:06.757 ๐Ÿ“˜ INFO DuckHuntBot.DB Loaded 1 players from duckhunt.json +18:47:06.764 ๐Ÿ“˜ INFO SASL Unified logging system initialized: all logs in duckhunt.log +18:47:06.765 ๐Ÿ“˜ INFO SASL Debug mode: ON +18:47:06.765 ๐Ÿ“˜ INFO SASL Log everything: YES +18:47:06.765 ๐Ÿ“˜ INFO SASL Unified format: YES +18:47:06.765 ๐Ÿ“˜ INFO SASL Console level: INFO +18:47:06.765 ๐Ÿ“˜ INFO SASL File level: DEBUG +18:47:06.765 ๐Ÿ“˜ INFO SASL Main log: /home/colby/duckhunt/logs/duckhunt.log +18:47:06.766 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿ‘‘ Configured 3 admin(s): peorth, computertech, colby +18:47:06.766 ๐Ÿ“˜ INFO DuckHuntBot.Shop Loaded 9 shop items from /home/colby/duckhunt/shop.json +18:47:06.766 ๐Ÿ“˜ INFO DuckHuntBot.Levels Loaded 8 levels from /home/colby/duckhunt/levels.json +18:47:06.766 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿฆ† Starting DuckHunt Bot... +18:47:06.813 ๐Ÿ“˜ INFO DuckHuntBot Attempting to connect to irc.rizon.net:6697 (attempt 1/3) +18:47:06.991 ๐Ÿ“˜ INFO DuckHuntBot โœ… Successfully connected to irc.rizon.net:6697 +18:47:06.991 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿ” Sending server password +18:47:06.992 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿฆ† Bot is now running! Press Ctrl+C to stop. +18:47:09.170 ๐Ÿ“˜ INFO DuckHuntBot Successfully registered with IRC server +18:47:23.007 ๐Ÿ“˜ INFO DuckHuntBot.Game Fast duck (hidden) spawned in #ct +18:48:02.049 ๐Ÿ“˜ INFO DuckHuntBot.Game Fast duck (hidden) spawned in #ct +18:48:31.072 ๐Ÿ“˜ INFO DuckHuntBot.Game Normal duck spawned in #ct +18:49:49.143 ๐Ÿ“˜ INFO DuckHuntBot.Game Normal duck spawned in #ct +18:50:38.197 โš ๏ธ WARNING DuckHuntBot Admin access granted via nick-only authentication: ComputerTech!ComputerTe@ComputerTech.Network +18:51:05.220 ๐Ÿ“˜ INFO DuckHuntBot.Game Normal duck spawned in #ct +18:52:09.272 ๐Ÿ“˜ INFO DuckHuntBot.Game Golden duck (hidden) spawned in #ct with 3 HP +18:53:29.365 ๐Ÿ“˜ INFO DuckHuntBot.Game Golden duck (hidden) spawned in #ct with 3 HP +18:54:43.468 ๐Ÿ“˜ INFO DuckHuntBot.Game Fast duck (hidden) spawned in #ct +18:55:15.496 ๐Ÿ“˜ INFO DuckHuntBot.Game Golden duck (hidden) spawned in #ct with 4 HP +18:56:27.559 ๐Ÿ“˜ INFO DuckHuntBot.Game Golden duck (hidden) spawned in #ct with 4 HP +18:56:48.773 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿ›‘ Received SIGINT (Ctrl+C), shutting down immediately... +18:56:48.777 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿ”„ Cancelled 5 running tasks +18:56:48.816 ๐Ÿ“˜ INFO DuckHuntBot Message loop cancelled +18:56:48.816 ๐Ÿ“˜ INFO DuckHuntBot Message loop ended +18:56:48.817 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿ›‘ Main loop cancelled +18:56:48.817 ๐Ÿ“˜ INFO DuckHuntBot.Game Duck spawning loop cancelled +18:56:48.817 ๐Ÿ“˜ INFO DuckHuntBot.Game Duck timeout loop cancelled +18:56:48.818 ๐Ÿ“˜ INFO DuckHuntBot.Game Game loops cancelled +18:56:48.857 ๐Ÿ” DEBUG DuckHuntBot.DB Database saved successfully with 1 players [save_database:142] +18:56:48.857 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿ’พ Database saved +18:56:49.325 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿ”Œ IRC connection closed +18:56:49.326 ๐Ÿ“˜ INFO DuckHuntBot โœ… Bot shutdown complete +18:56:49.936 ๐Ÿ“˜ INFO DuckHuntBot Unified logging system initialized: all logs in duckhunt.log +18:56:49.937 ๐Ÿ“˜ INFO DuckHuntBot Debug mode: ON +18:56:49.938 ๐Ÿ“˜ INFO DuckHuntBot Log everything: YES +18:56:49.938 ๐Ÿ“˜ INFO DuckHuntBot Unified format: YES +18:56:49.939 ๐Ÿ“˜ INFO DuckHuntBot Console level: INFO +18:56:49.939 ๐Ÿ“˜ INFO DuckHuntBot File level: DEBUG +18:56:49.939 ๐Ÿ“˜ INFO DuckHuntBot Main log: /home/colby/duckhunt/logs/duckhunt.log +18:56:49.940 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿค– Initializing DuckHunt Bot components... +18:56:49.940 ๐Ÿ“˜ INFO DuckHuntBot.DB Loaded 1 players from duckhunt.json +18:56:49.941 ๐Ÿ“˜ INFO SASL Unified logging system initialized: all logs in duckhunt.log +18:56:49.941 ๐Ÿ“˜ INFO SASL Debug mode: ON +18:56:49.942 ๐Ÿ“˜ INFO SASL Log everything: YES +18:56:49.942 ๐Ÿ“˜ INFO SASL Unified format: YES +18:56:49.942 ๐Ÿ“˜ INFO SASL Console level: INFO +18:56:49.943 ๐Ÿ“˜ INFO SASL File level: DEBUG +18:56:49.943 ๐Ÿ“˜ INFO SASL Main log: /home/colby/duckhunt/logs/duckhunt.log +18:56:49.943 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿ‘‘ Configured 3 admin(s): peorth, computertech, colby +18:56:49.944 ๐Ÿ“˜ INFO DuckHuntBot.Shop Loaded 9 shop items from /home/colby/duckhunt/shop.json +18:56:49.945 ๐Ÿ“˜ INFO DuckHuntBot.Levels Loaded 8 levels from /home/colby/duckhunt/levels.json +18:56:49.946 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿฆ† Starting DuckHunt Bot... +18:56:50.005 ๐Ÿ“˜ INFO DuckHuntBot Attempting to connect to irc.rizon.net:6697 (attempt 1/3) +18:56:50.174 ๐Ÿ“˜ INFO DuckHuntBot โœ… Successfully connected to irc.rizon.net:6697 +18:56:50.174 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿ” Sending server password +18:56:50.175 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿฆ† Bot is now running! Press Ctrl+C to stop. +18:56:52.108 ๐Ÿ“˜ INFO DuckHuntBot Successfully registered with IRC server +18:57:01.185 ๐Ÿ“˜ INFO DuckHuntBot.Game Golden duck (hidden) spawned in #ct with 4 HP +18:57:11.327 โš ๏ธ WARNING DuckHuntBot Admin access granted via nick-only authentication: ComputerTech!ComputerTe@ComputerTech.Network +18:58:06.241 ๐Ÿ“˜ INFO DuckHuntBot.Game Normal duck spawned in #ct +18:58:38.133 โš ๏ธ WARNING DuckHuntBot Admin access granted via nick-only authentication: ComputerTech!ComputerTe@ComputerTech.Network +18:59:10.630 โš ๏ธ WARNING DuckHuntBot Admin access granted via nick-only authentication: ComputerTech!ComputerTe@ComputerTech.Network +18:59:21.310 ๐Ÿ“˜ INFO DuckHuntBot.Game Normal duck spawned in #ct +19:00:26.365 ๐Ÿ“˜ INFO DuckHuntBot.Game Normal duck spawned in #ct +19:01:20.851 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿ›‘ Received SIGINT (Ctrl+C), shutting down immediately... +19:01:20.860 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿ”„ Cancelled 5 running tasks +19:01:20.861 ๐Ÿ“˜ INFO DuckHuntBot Message loop cancelled +19:01:20.861 ๐Ÿ“˜ INFO DuckHuntBot Message loop ended +19:01:20.862 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿ›‘ Main loop cancelled +19:01:20.862 ๐Ÿ“˜ INFO DuckHuntBot.Game Duck spawning loop cancelled +19:01:20.862 ๐Ÿ“˜ INFO DuckHuntBot.Game Duck timeout loop cancelled +19:01:20.863 ๐Ÿ“˜ INFO DuckHuntBot.Game Game loops cancelled +19:01:20.888 ๐Ÿ” DEBUG DuckHuntBot.DB Database saved successfully with 1 players [save_database:142] +19:01:20.888 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿ’พ Database saved +19:01:21.097 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿ”Œ IRC connection closed +19:01:21.097 ๐Ÿ“˜ INFO DuckHuntBot โœ… Bot shutdown complete +19:01:22.172 ๐Ÿ“˜ INFO DuckHuntBot Unified logging system initialized: all logs in duckhunt.log +19:01:22.172 ๐Ÿ“˜ INFO DuckHuntBot Debug mode: ON +19:01:22.173 ๐Ÿ“˜ INFO DuckHuntBot Log everything: YES +19:01:22.173 ๐Ÿ“˜ INFO DuckHuntBot Unified format: YES +19:01:22.173 ๐Ÿ“˜ INFO DuckHuntBot Console level: INFO +19:01:22.173 ๐Ÿ“˜ INFO DuckHuntBot File level: DEBUG +19:01:22.173 ๐Ÿ“˜ INFO DuckHuntBot Main log: /home/colby/duckhunt/logs/duckhunt.log +19:01:22.174 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿค– Initializing DuckHunt Bot components... +19:01:22.174 ๐Ÿ“˜ INFO DuckHuntBot.DB Loaded 1 players from duckhunt.json +19:01:22.181 ๐Ÿ“˜ INFO SASL Unified logging system initialized: all logs in duckhunt.log +19:01:22.181 ๐Ÿ“˜ INFO SASL Debug mode: ON +19:01:22.181 ๐Ÿ“˜ INFO SASL Log everything: YES +19:01:22.182 ๐Ÿ“˜ INFO SASL Unified format: YES +19:01:22.182 ๐Ÿ“˜ INFO SASL Console level: INFO +19:01:22.182 ๐Ÿ“˜ INFO SASL File level: DEBUG +19:01:22.182 ๐Ÿ“˜ INFO SASL Main log: /home/colby/duckhunt/logs/duckhunt.log +19:01:22.183 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿ‘‘ Configured 3 admin(s): peorth, computertech, colby +19:01:22.184 ๐Ÿ“˜ INFO DuckHuntBot.Shop Loaded 9 shop items from /home/colby/duckhunt/shop.json +19:01:22.185 ๐Ÿ“˜ INFO DuckHuntBot.Levels Loaded 8 levels from /home/colby/duckhunt/levels.json +19:01:22.185 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿฆ† Starting DuckHunt Bot... +19:01:22.231 ๐Ÿ“˜ INFO DuckHuntBot Attempting to connect to irc.rizon.net:6697 (attempt 1/3) +19:01:22.424 ๐Ÿ“˜ INFO DuckHuntBot โœ… Successfully connected to irc.rizon.net:6697 +19:01:22.424 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿ” Sending server password +19:01:22.425 ๐Ÿ“˜ INFO DuckHuntBot ๐Ÿฆ† Bot is now running! Press Ctrl+C to stop. +19:01:24.093 ๐Ÿ“˜ INFO DuckHuntBot Successfully registered with IRC server +19:01:28.998 โš ๏ธ WARNING DuckHuntBot Admin access granted via nick-only authentication: ComputerTech!ComputerTe@ComputerTech.Network +19:01:29.016 ๐Ÿ” DEBUG DuckHuntBot.DB Database saved successfully with 1 players [save_database:142] +19:01:48.445 ๐Ÿ“˜ INFO DuckHuntBot.Game Normal duck spawned in #ct +19:02:49.507 ๐Ÿ“˜ INFO DuckHuntBot.Game Normal duck spawned in #ct +19:04:05.573 ๐Ÿ“˜ INFO DuckHuntBot.Game Normal duck spawned in #ct +19:05:15.648 ๐Ÿ“˜ INFO DuckHuntBot.Game Fast duck (hidden) spawned in #ct +19:05:39.670 ๐Ÿ“˜ INFO DuckHuntBot.Game Normal duck spawned in #ct diff --git a/messages.json b/messages.json index 6a4055a..1d5dd72 100644 --- a/messages.json +++ b/messages.json @@ -12,10 +12,12 @@ "bang_hit_golden_killed": "{nick} > *BANG* You killed the GOLDEN DUCK! [+{xp_gained} xp] [Total ducks: {ducks_shot}]", "bang_hit_fast": "{nick} > *BANG* You shot a FAST DUCK! [+{xp_gained} xp] [Total ducks: {ducks_shot}]", "bang_miss": "{nick} > *BANG* You missed the duck!", + "bang_friendly_fire_penalty": "{nick} > *BANG* You missed and hit {victim}! [GUN CONFISCATED] [LOST {xp_lost} XP]", + "bang_friendly_fire_insured": "{nick} > *BANG* You missed and hit {victim}! [INSURANCE PROTECTED - No penalties]", "bang_no_duck": "{nick} > *BANG* What did you shoot at? There is no duck in the area... [GUN CONFISCATED]", "bang_no_ammo": "{nick} > *click* You're out of ammo! Use !reload", "bang_gun_jammed": "{nick} > *click* Your gun jammed! [AMMO WASTED]", - "bang_not_armed": "{nick} > You are not armed.", + "bang_not_armed": "{nick} > Your gun has been confiscated. Buy it back from the shop.", "bef_success": "{nick} > *befriend* You befriended the duck! [+{xp_gained} xp] [Ducks befriended: {ducks_befriended}]", "bef_failed": "{nick} > *gentle approach* The duck doesn't trust you and flies away...", "bef_no_duck": "{nick} > *gentle approach* There is no duck to befriend in the area...", @@ -47,6 +49,19 @@ "shop_buy_invalid_id": "{nick} > Invalid item ID. Use !shop to see available items.", "shop_buy_usage": "Usage: !shop buy ", "use_attract_ducks": "{nick} > You scattered bread around the pond! Ducks will spawn {spawn_multiplier}x faster for {duration} minutes.", + "use_insurance": "{nick} > You activated Hunter's Insurance! Protected from friendly fire penalties for {duration} hours.", + "use_buy_gun_back": "{nick} > Your gun has been returned with {ammo_restored} bullets and {magazines_restored} magazines.", + "use_buy_gun_back_not_needed": "{nick} > Your gun is not confiscated.", + "bang_wet_clothes": "{nick} > *SPLASH* Your clothes are soaked! You can't shoot until you dry off or buy new clothes.", + "use_splash_water": "{nick} > *SPLASH* You soaked {target_nick} with water! They can't shoot for {duration} minutes.", + "use_dry_clothes": "{nick} > You changed into dry clothes! Ready to hunt again.", + "use_dry_clothes_not_needed": "{nick} > You weren't wet - no need for new clothes.", + "gift_ammo": "{nick} > Gave {amount} bullet(s) to {target_nick}! What a generous hunter.", + "gift_magazine": "{nick} > Gave 1 magazine to {target_nick}! Sharing the ammo love.", + "gift_gun_brush": "{nick} > Gave a gun brush to {target_nick} - keeping their weapon clean!", + "gift_insurance": "{nick} > Gave Hunter's Insurance to {target_nick} - protecting them from friendly fire!", + "gift_dry_clothes": "{nick} > Gave dry clothes to {target_nick} - now they can stay dry!", + "gift_buy_gun_back": "{nick} > Gave a gun license to {target_nick} - helping them get their gun back!", "colours": { "white": "\u00030", diff --git a/shop.json b/shop.json index 9cc4319..d226d05 100644 --- a/shop.json +++ b/shop.json @@ -19,8 +19,7 @@ "price": 10, "description": "Throw sand in target's gun - increases jam chance by 15%", "type": "sabotage_jam", - "amount": 15, - "target_required": true + "amount": 15 }, "4": { "name": "Gun Brush", @@ -36,6 +35,32 @@ "type": "attract_ducks", "duration": 1200, "spawn_multiplier": 2.0 + }, + "6": { + "name": "Hunter's Insurance", + "price": 75, + "description": "Protects against friendly fire penalties for 24 hours - no XP loss or gun confiscation", + "type": "insurance", + "duration": 86400, + "protection": "friendly_fire" + }, + "7": { + "name": "Buy Gun Back", + "price": 40, + "description": "Get your confiscated gun back with the same ammo it had when taken", + "type": "buy_gun_back" + }, + "8": { + "name": "Bucket of Water", + "price": 25, + "description": "Splash another hunter with water - soaks their clothes and prevents shooting until they dry or buy new clothes", + "type": "splash_water" + }, + "9": { + "name": "Dry Clothes", + "price": 30, + "description": "Change into dry clothes - allows shooting again after being soaked", + "type": "dry_clothes" } } } \ No newline at end of file diff --git a/src/__pycache__/db.cpython-312.pyc b/src/__pycache__/db.cpython-312.pyc index 1f8efe8..3eaf357 100644 Binary files a/src/__pycache__/db.cpython-312.pyc and b/src/__pycache__/db.cpython-312.pyc differ diff --git a/src/__pycache__/duckhuntbot.cpython-312.pyc b/src/__pycache__/duckhuntbot.cpython-312.pyc index 21c0762..eb38a1b 100644 Binary files a/src/__pycache__/duckhuntbot.cpython-312.pyc and b/src/__pycache__/duckhuntbot.cpython-312.pyc differ diff --git a/src/__pycache__/game.cpython-312.pyc b/src/__pycache__/game.cpython-312.pyc index 6b86410..f58a32e 100644 Binary files a/src/__pycache__/game.cpython-312.pyc and b/src/__pycache__/game.cpython-312.pyc differ diff --git a/src/__pycache__/levels.cpython-312.pyc b/src/__pycache__/levels.cpython-312.pyc index 3f20233..3646ac2 100644 Binary files a/src/__pycache__/levels.cpython-312.pyc and b/src/__pycache__/levels.cpython-312.pyc differ diff --git a/src/__pycache__/logging_utils.cpython-312.pyc b/src/__pycache__/logging_utils.cpython-312.pyc index e1d49cb..8356053 100644 Binary files a/src/__pycache__/logging_utils.cpython-312.pyc and b/src/__pycache__/logging_utils.cpython-312.pyc differ diff --git a/src/__pycache__/sasl.cpython-312.pyc b/src/__pycache__/sasl.cpython-312.pyc index b8767e6..177f689 100644 Binary files a/src/__pycache__/sasl.cpython-312.pyc and b/src/__pycache__/sasl.cpython-312.pyc differ diff --git a/src/__pycache__/shop.cpython-312.pyc b/src/__pycache__/shop.cpython-312.pyc index 3a2baf3..df02ded 100644 Binary files a/src/__pycache__/shop.cpython-312.pyc and b/src/__pycache__/shop.cpython-312.pyc differ diff --git a/src/__pycache__/utils.cpython-312.pyc b/src/__pycache__/utils.cpython-312.pyc index ff7e9d9..7983bd0 100644 Binary files a/src/__pycache__/utils.cpython-312.pyc and b/src/__pycache__/utils.cpython-312.pyc differ diff --git a/src/db.py b/src/db.py index 2fa3cf3..44a9ab1 100644 --- a/src/db.py +++ b/src/db.py @@ -68,6 +68,8 @@ class DuckDB: sanitized['xp'] = max(0, int(player_data.get('xp', 0))) # Non-negative XP sanitized['ducks_shot'] = max(0, int(player_data.get('ducks_shot', 0))) sanitized['ducks_befriended'] = max(0, int(player_data.get('ducks_befriended', 0))) + sanitized['shots_fired'] = max(0, int(player_data.get('shots_fired', 0))) + sanitized['shots_missed'] = max(0, int(player_data.get('shots_missed', 0))) default_accuracy = self.bot.get_config('default_accuracy', 75) if self.bot else 75 max_accuracy = self.bot.get_config('max_accuracy', 100) if self.bot else 100 sanitized['accuracy'] = max(0, min(max_accuracy, int(player_data.get('accuracy', default_accuracy)))) # 0-max_accuracy range @@ -76,6 +78,12 @@ class DuckDB: # Ammo system with validation sanitized['current_ammo'] = max(0, min(50, int(player_data.get('current_ammo', 6)))) sanitized['magazines'] = max(0, min(20, int(player_data.get('magazines', 3)))) + + # Confiscated ammo (optional fields) + if 'confiscated_ammo' in player_data: + sanitized['confiscated_ammo'] = max(0, min(50, int(player_data.get('confiscated_ammo', 0)))) + if 'confiscated_magazines' in player_data: + sanitized['confiscated_magazines'] = max(0, min(20, int(player_data.get('confiscated_magazines', 0)))) sanitized['bullets_per_magazine'] = max(1, min(50, int(player_data.get('bullets_per_magazine', 6)))) sanitized['jam_chance'] = max(0, min(100, int(player_data.get('jam_chance', 5)))) @@ -237,6 +245,8 @@ class DuckDB: 'xp': xp, 'ducks_shot': 0, 'ducks_befriended': 0, + 'shots_fired': 0, # Total shots fired + 'shots_missed': 0, # Total shots that missed 'current_ammo': bullets_per_mag, # Bullets in current magazine 'magazines': magazines, # Total magazines (including current) 'bullets_per_magazine': bullets_per_mag, # Bullets per magazine diff --git a/src/duckhuntbot.py b/src/duckhuntbot.py index 77192c8..b628030 100644 --- a/src/duckhuntbot.py +++ b/src/duckhuntbot.py @@ -58,11 +58,44 @@ class DuckHuntBot: return value def is_admin(self, user): - """Check if user is admin by nick only""" + """Check if user is admin with enhanced security checks""" if '!' not in user: return False + nick = user.split('!')[0].lower() - return nick in self.admins + + # Check admin configuration - support both nick-only (legacy) and hostmask patterns + admin_config = self.get_config('admins', []) + + # Ensure admin_config is a list + if not isinstance(admin_config, list): + admin_config = [] + + for admin_entry in admin_config: + if isinstance(admin_entry, str): + # Simple nick-based check (less secure but compatible) + if admin_entry.lower() == nick: + self.logger.warning(f"Admin access granted via nick-only authentication: {user}") + return True + elif isinstance(admin_entry, dict): + # Enhanced hostmask-based authentication + if admin_entry.get('nick', '').lower() == nick: + # Check hostmask pattern if provided + required_pattern = admin_entry.get('hostmask') + if required_pattern: + import fnmatch + if fnmatch.fnmatch(user.lower(), required_pattern.lower()): + self.logger.info(f"Admin access granted via hostmask: {user}") + return True + else: + self.logger.warning(f"Admin nick match but hostmask mismatch: {user} vs {required_pattern}") + return False + else: + # Nick-only fallback + self.logger.warning(f"Admin access granted via nick-only (no hostmask configured): {user}") + return True + + return False def _handle_single_target_admin_command(self, args, usage_message_key, action_func, success_message_key, nick, channel): """Helper for admin commands that target a single player""" @@ -307,6 +340,11 @@ class DuckHuntBot: self.logger.error(f"Error getting player data for {nick}: {e}") player = {} + # Track activity for channel membership validation + if channel.startswith('#'): # Only track for channel messages + player['last_activity_channel'] = channel + player['last_activity_time'] = time.time() + # Check if player is ignored (unless it's an admin) try: if player.get('ignored', False) and not self.is_admin(user): @@ -360,6 +398,77 @@ class DuckHuntBot: except Exception as send_error: self.logger.error(f"Error sending error message: {send_error}") + def validate_target_player(self, target_nick, channel): + """ + Validate that a target player is a valid hunter + Returns (is_valid, player_data, error_message) + + TODO: Implement proper channel membership tracking to ensure + the target is actually present in the channel + """ + if not target_nick: + return False, None, "No target specified" + + # Normalize the nickname + target_nick = target_nick.lower().strip() + + # Check if target_nick is empty after normalization + if not target_nick: + return False, None, "Invalid target nickname" + + # Check if player exists in database + player = self.db.get_player(target_nick) + if not player: + return False, None, f"Player '{target_nick}' not found. They need to participate in the game first." + + # Check if player has any game activity (basic validation they're a hunter) + has_activity = ( + player.get('xp', 0) > 0 or + player.get('shots_fired', 0) > 0 or + 'current_ammo' in player or + 'magazines' in player + ) + + if not has_activity: + return False, None, f"Player '{target_nick}' has no hunting activity. They may not be an active hunter." + + # Check if player is currently in the channel (for channel messages only) + if channel.startswith('#'): + is_in_channel = self.is_user_in_channel_sync(target_nick, channel) + if not is_in_channel: + return False, None, f"Player '{target_nick}' is not currently in {channel}." + + return True, player, None + + def is_user_in_channel_sync(self, nick, channel): + """ + Check if a user is likely in the channel based on recent activity (synchronous version) + + This is a practical approach that doesn't require complex IRC response parsing. + We assume if someone has been active recently, they're still in the channel. + """ + try: + player = self.db.get_player(nick) + if not player: + return False + + # Check if they've been active in this channel recently + last_activity_channel = player.get('last_activity_channel') + last_activity_time = player.get('last_activity_time', 0) + current_time = time.time() + + # If they were active in this channel within the last 30 minutes, assume they're still here + if (last_activity_channel == channel and + current_time - last_activity_time < 1800): # 30 minutes + return True + + # If no recent activity in this channel, they might not be here + return False + + except Exception as e: + self.logger.error(f"Error checking channel membership for {nick} in {channel}: {e}") + return True # Default to allowing the command if we can't check + async def handle_bang(self, nick, channel, player): """Handle !bang command""" result = self.game.shoot_duck(nick, channel, player) @@ -409,11 +518,12 @@ class DuckHuntBot: """Handle buying an item from the shop""" target_player = None - # Get target player if specified + # Get target player if specified and validate they're in channel if target_nick: - target_player = self.db.get_player(target_nick) - if not target_player: - message = f"{nick} > Target player '{target_nick}' not found" + # Use the same validation as other commands + is_valid, target_player, error_msg = self.validate_target_player(target_nick, channel) + if not is_valid: + message = f"{nick} > {error_msg}" self.send_message(channel, message) return @@ -460,11 +570,15 @@ class DuckHuntBot: # Apply color formatting bold = self.messages.messages.get('colours', {}).get('bold', '') reset = self.messages.messages.get('colours', {}).get('reset', '') + green = self.messages.messages.get('colours', {}).get('green', '') + blue = self.messages.messages.get('colours', {}).get('blue', '') + yellow = self.messages.messages.get('colours', {}).get('yellow', '') + red = self.messages.messages.get('colours', {}).get('red', '') # Get player level info level_info = self.levels.get_player_level_info(player) level = level_info['level'] - level_name = level_info['level_data']['name'] + level_name = level_info['name'] # Build stats message xp = player.get('xp', 0) @@ -472,23 +586,40 @@ class DuckHuntBot: ducks_befriended = player.get('ducks_befriended', 0) accuracy = player.get('accuracy', self.get_config('player_defaults.accuracy', 75)) + # Calculate additional stats + total_ducks_encountered = ducks_shot + ducks_befriended + shots_missed = player.get('shots_missed', 0) + total_shots = ducks_shot + shots_missed + hit_rate = round((ducks_shot / total_shots * 100) if total_shots > 0 else 0, 1) + + # Get level progression info + xp_needed = level_info.get('needed_for_next', 0) + next_level_name = level_info.get('next_level_name', 'Max Level') + if xp_needed > 0: + xp_progress = f" (Need {xp_needed} XP for {next_level_name})" + else: + xp_progress = " (Max level reached!)" + # Ammo info current_ammo = player.get('current_ammo', 0) magazines = player.get('magazines', 0) bullets_per_mag = player.get('bullets_per_magazine', 6) + jam_chance = player.get('jam_chance', 0) # Gun status - gun_status = "๐Ÿ”ซ Armed" if not player.get('gun_confiscated', False) else "โŒ Confiscated" + gun_status = "Armed" if not player.get('gun_confiscated', False) else "Confiscated" - stats_lines = [ - f"๐Ÿ“Š {bold}Duck Hunt Stats for {nick}{reset}", - f"๐Ÿ† Level {level}: {level_name}", - f"โญ XP: {xp}", - f"๐Ÿฆ† Ducks Shot: {ducks_shot}", - f"๐Ÿ’š Ducks Befriended: {ducks_befriended}", - f"๐ŸŽฏ Accuracy: {accuracy}%", - f"๐Ÿ”ซ Status: {gun_status}", - f"๐Ÿ’€ Ammo: {current_ammo}/{bullets_per_mag} | Magazines: {magazines}" + # Build compact stats message with subtle colors + stats_parts = [ + f"Lv{level} {level_name}", + f"{green}{xp}XP{reset}{xp_progress}", + f"{ducks_shot} shot", + f"{ducks_befriended} befriended", + f"{accuracy}% accuracy", + f"{hit_rate}% hit rate", + f"{green if gun_status == 'Armed' else red}{gun_status}{reset}", + f"{current_ammo}/{bullets_per_mag}|{magazines} mags", + f"{jam_chance}% jam chance" ] # Add inventory if player has items @@ -500,11 +631,18 @@ class DuckHuntBot: if item: items.append(f"{item['name']} x{quantity}") if items: - stats_lines.append(f"๐ŸŽ’ Inventory: {', '.join(items)}") + stats_parts.append(f"Items: {', '.join(items)}") - # Send each line - for line in stats_lines: - self.send_message(channel, line) + # Add temporary effects if any + temp_effects = player.get('temporary_effects', []) + if temp_effects: + active_effects = [effect.get('name', 'Unknown Effect') for effect in temp_effects if isinstance(effect, dict)] + if active_effects: + stats_parts.append(f"Effects:{','.join(active_effects)}") + + # Send as one compact message + stats_message = f"{bold}{nick}{reset}: {' | '.join(stats_parts)}" + self.send_message(channel, stats_message) async def handle_topduck(self, nick, channel): """Handle !topduck command - show leaderboards""" @@ -574,9 +712,9 @@ class DuckHuntBot: # Get target player if specified if target_nick: - target_player = self.db.get_player(target_nick) - if not target_player: - message = f"{nick} > Target player '{target_nick}' not found" + is_valid, target_player, error_msg = self.validate_target_player(target_nick, channel) + if not is_valid: + message = f"{nick} > {error_msg}" self.send_message(channel, message) return @@ -597,8 +735,58 @@ class DuckHuntBot: spawn_multiplier=effect.get('spawn_multiplier', 2.0), duration=effect.get('duration', 10) ) + elif effect_type == 'insurance': + # Use specific message for insurance + message = self.messages.get('use_insurance', + nick=nick, + duration=effect.get('duration', 24) + ) + elif effect_type == 'buy_gun_back': + # Use specific message for buying gun back + if effect.get('restored', False): + message = self.messages.get('use_buy_gun_back', nick=nick, + ammo_restored=effect.get('ammo_restored', 0), + magazines_restored=effect.get('magazines_restored', 0)) + else: + message = self.messages.get('use_buy_gun_back_not_needed', nick=nick) + elif effect_type == 'splash_water': + # Use specific message for water splash + message = self.messages.get('use_splash_water', + nick=nick, + target_nick=target_nick, + duration=effect.get('duration', 5)) + elif effect_type == 'dry_clothes': + # Use specific message for dry clothes + if effect.get('was_wet', False): + message = self.messages.get('use_dry_clothes', nick=nick) + else: + message = self.messages.get('use_dry_clothes_not_needed', nick=nick) elif result.get("target_affected"): - message = f"{nick} > Used {result['item_name']} on {target_nick}!" + # Check if it's a gift (beneficial effect to target) + if effect.get('is_gift', False): + # Use specific gift messages based on item type + if effect_type == 'ammo': + message = self.messages.get('gift_ammo', + nick=nick, target_nick=target_nick, amount=effect.get('amount', 1)) + elif effect_type == 'magazine': + message = self.messages.get('gift_magazine', + nick=nick, target_nick=target_nick) + elif effect_type == 'clean_gun': + message = self.messages.get('gift_gun_brush', + nick=nick, target_nick=target_nick) + elif effect_type == 'insurance': + message = self.messages.get('gift_insurance', + nick=nick, target_nick=target_nick) + elif effect_type == 'dry_clothes': + message = self.messages.get('gift_dry_clothes', + nick=nick, target_nick=target_nick) + elif effect_type == 'buy_gun_back': + message = self.messages.get('gift_buy_gun_back', + nick=nick, target_nick=target_nick) + else: + message = f"{nick} > Gave {result['item_name']} to {target_nick}!" + else: + message = f"{nick} > Used {result['item_name']} on {target_nick}!" else: message = f"{nick} > Used {result['item_name']}!" @@ -618,20 +806,48 @@ class DuckHuntBot: is_private_msg = not channel.startswith('#') if args: - target = args[0].lower() - player = self.db.get_player(target) - if player is None: - player = {} - player['gun_confiscated'] = False + target_nick = args[0] - # Update magazines based on player level + # Validate target player (only for channel messages, skip validation if targeting self) + player = None + if not is_private_msg: + # If targeting self, skip validation since the user is obviously in the channel + if target_nick.lower() == nick.lower(): + target_nick = target_nick.lower() + player = self.db.get_player(target_nick) + if player is None: + player = self.db.create_player(target_nick) + self.db.players[target_nick] = player + else: + is_valid, player, error_msg = self.validate_target_player(target_nick, channel) + if not is_valid: + message = f"{nick} > {error_msg}" + self.send_message(channel, message) + return + # Ensure player is properly stored in database + target_nick = target_nick.lower() + if target_nick not in self.db.players: + self.db.players[target_nick] = player + else: + # For private messages, allow targeting any nick (admin override) + target_nick = target_nick.lower() + player = self.db.get_player(target_nick) + if player is None: + # Create new player data for the target + player = self.db.create_player(target_nick) + self.db.players[target_nick] = player + + # At this point player is guaranteed to be not None + if player is not None: + player['gun_confiscated'] = False # Update magazines based on player level self.levels.update_player_magazines(player) player['current_ammo'] = player.get('bullets_per_magazine', 6) + # Player data is already modified in place and will be saved by save_database() if is_private_msg: - message = f"{nick} > Rearmed {target}" + message = f"{nick} > Rearmed {target_nick}" else: - message = self.messages.get('admin_rearm_player', target=target, admin=nick) + message = self.messages.get('admin_rearm_player', target=target_nick, admin=nick) self.send_message(channel, message) else: if is_private_msg: @@ -665,16 +881,46 @@ class DuckHuntBot: self.send_message(channel, message) return - target = args[0].lower() - player = self.db.get_player(target) - if player is None: - player = {} - player['gun_confiscated'] = True + target_nick = args[0] + + # Validate target player (only for channel messages, skip validation if targeting self) + player = None + if not is_private_msg: + # If targeting self, skip validation since the user is obviously in the channel + if target_nick.lower() == nick.lower(): + target_nick = target_nick.lower() + player = self.db.get_player(target_nick) + if player is None: + player = self.db.create_player(target_nick) + self.db.players[target_nick] = player + else: + is_valid, player, error_msg = self.validate_target_player(target_nick, channel) + if not is_valid: + message = f"{nick} > {error_msg}" + self.send_message(channel, message) + return + # Ensure player is properly stored in database + target_nick = target_nick.lower() + if target_nick not in self.db.players: + self.db.players[target_nick] = player + else: + # For private messages, allow targeting any nick (admin override) + target_nick = target_nick.lower() + player = self.db.get_player(target_nick) + if player is None: + # Create new player data for the target + player = self.db.create_player(target_nick) + self.db.players[target_nick] = player + + # At this point player is guaranteed to be not None + if player is not None: + player['gun_confiscated'] = True + # Player data is already modified in place and will be saved by save_database() if is_private_msg: - message = f"{nick} > Disarmed {target}" + message = f"{nick} > Disarmed {target_nick}" else: - message = self.messages.get('admin_disarm', target=target, admin=nick) + message = self.messages.get('admin_disarm', target=target_nick, admin=nick) self.send_message(channel, message) self.db.save_database() diff --git a/src/game.py b/src/game.py index 06c3f5f..f58be43 100644 --- a/src/game.py +++ b/src/game.py @@ -175,6 +175,14 @@ class DuckGame: 'message_args': {'nick': nick} } + # Check if clothes are wet + if self._is_player_wet(player): + return { + 'success': False, + 'message_key': 'bang_wet_clothes', + 'message_args': {'nick': nick} + } + # Check ammo if player.get('current_ammo', 0) <= 0: return { @@ -197,8 +205,14 @@ class DuckGame: # Check for duck if channel not in self.ducks or not self.ducks[channel]: - # Wild shot - gun confiscated - player['current_ammo'] = player.get('current_ammo', 1) - 1 + # Wild shot - gun confiscated for unsafe shooting + player['shots_fired'] = player.get('shots_fired', 0) + 1 # Track wild shots too + player['shots_missed'] = player.get('shots_missed', 0) + 1 # Wild shots count as misses + # Use ammo for the shot, then store remaining ammo before confiscation + remaining_ammo = player.get('current_ammo', 1) - 1 + player['confiscated_ammo'] = remaining_ammo + player['confiscated_magazines'] = player.get('magazines', 0) + player['current_ammo'] = 0 # No ammo while confiscated player['gun_confiscated'] = True self.db.save_database() return { @@ -209,6 +223,7 @@ class DuckGame: # Shoot at duck player['current_ammo'] = player.get('current_ammo', 1) - 1 + player['shots_fired'] = player.get('shots_fired', 0) + 1 # Track total shots fired # Calculate hit chance using level-modified accuracy modified_accuracy = self.bot.levels.get_modified_accuracy(player) hit_chance = modified_accuracy / 100.0 @@ -268,7 +283,7 @@ class DuckGame: self.bot.levels.update_player_magazines(player) # If config option enabled, rearm all disarmed players when duck is shot - if self.bot.get_config('rearm_on_duck_shot', False): + if self.bot.get_config('duck_spawning.rearm_on_duck_shot', False): self._rearm_all_disarmed_players() self.db.save_database() @@ -284,9 +299,67 @@ class DuckGame: } else: # Miss! Duck stays in the channel - accuracy_loss = self.bot.get_config('accuracy_loss_on_miss', 2) - min_accuracy = self.bot.get_config('min_accuracy', 10) - player['accuracy'] = max(player.get('accuracy', self.bot.get_config('default_accuracy', 75)) - accuracy_loss, min_accuracy) + player['shots_missed'] = player.get('shots_missed', 0) + 1 # Track missed shots + accuracy_loss = self.bot.get_config('gameplay.accuracy_loss_on_miss', 2) + min_accuracy = self.bot.get_config('gameplay.min_accuracy', 10) + player['accuracy'] = max(player.get('accuracy', self.bot.get_config('player_defaults.accuracy', 75)) - accuracy_loss, min_accuracy) + + # Check for friendly fire (chance to hit another hunter) + friendly_fire_chance = 0.15 # 15% chance of hitting another hunter on miss + if random.random() < friendly_fire_chance: + # Get other armed players in the same channel + armed_players = [] + for other_nick, other_player in self.db.players.items(): + if (other_nick.lower() != nick.lower() and + not other_player.get('gun_confiscated', False) and + other_player.get('current_ammo', 0) > 0): + armed_players.append((other_nick, other_player)) + + if armed_players: + # Hit a random armed hunter + victim_nick, victim_player = random.choice(armed_players) + + # Check if shooter has insurance protection + has_insurance = self._check_insurance_protection(player, 'friendly_fire') + + if has_insurance: + # Protected by insurance - no penalties + self.db.save_database() + return { + 'success': True, + 'hit': False, + 'friendly_fire': True, + 'victim': victim_nick, + 'message_key': 'bang_friendly_fire_insured', + 'message_args': { + 'nick': nick, + 'victim': victim_nick + } + } + else: + # Apply friendly fire penalties - gun confiscated for unsafe shooting + xp_loss = min(player.get('xp', 0) // 4, 25) # Lose 25% XP or max 25 XP + player['xp'] = max(0, player.get('xp', 0) - xp_loss) + # Store current ammo state before confiscation (no shot fired yet in friendly fire) + player['confiscated_ammo'] = player.get('current_ammo', 0) + player['confiscated_magazines'] = player.get('magazines', 0) + player['current_ammo'] = 0 # No ammo while confiscated + player['gun_confiscated'] = True + + self.db.save_database() + return { + 'success': True, + 'hit': False, + 'friendly_fire': True, + 'victim': victim_nick, + 'message_key': 'bang_friendly_fire_penalty', + 'message_args': { + 'nick': nick, + 'victim': victim_nick, + 'xp_lost': xp_loss + } + } + self.db.save_database() return { 'success': True, @@ -443,6 +516,35 @@ class DuckGame: self.logger.error(f"Error getting spawn multiplier: {e}") return 1.0 + def _is_player_wet(self, player): + """Check if player has wet clothes that prevent shooting""" + import time + current_time = time.time() + + effects = player.get('temporary_effects', []) + for effect in effects: + if (effect.get('type') == 'wet_clothes' and + effect.get('expires_at', 0) > current_time): + return True + return False + + def _check_insurance_protection(self, player, protection_type): + """Check if player has active insurance protection""" + import time + current_time = time.time() + + try: + effects = player.get('temporary_effects', []) + for effect in effects: + if (effect.get('type') == 'insurance' and + effect.get('protection') == protection_type and + effect.get('expires_at', 0) > current_time): + return True + return False + except Exception as e: + self.logger.error(f"Error checking insurance protection: {e}") + return False + def _clean_expired_effects(self): """Remove expired temporary effects from all players""" import time diff --git a/src/shop.py b/src/shop.py index f5e7626..9c0cf62 100644 --- a/src/shop.py +++ b/src/shop.py @@ -105,10 +105,42 @@ class ShopManager: player['xp'] = player_xp - item['price'] if store_in_inventory: - # Add to inventory + # Add to inventory with bounds checking inventory = player.get('inventory', {}) item_id_str = str(item_id) current_count = inventory.get(item_id_str, 0) + + # Load inventory limits from config + config_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'config.json') + max_per_item = 99 # Default limit per item type + max_total_items = 20 # Default total items limit + try: + with open(config_path, 'r') as f: + config = json.load(f) + max_total_items = config.get('gameplay', {}).get('max_inventory_items', 20) + max_per_item = config.get('gameplay', {}).get('max_per_item_type', 99) + except: + pass # Use defaults + + # Check individual item limit + if current_count >= max_per_item: + return { + "success": False, + "error": "item_limit_reached", + "message": f"Cannot hold more than {max_per_item} {item['name']}s", + "item_name": item['name'] + } + + # Check total inventory size limit + total_items = sum(inventory.values()) + if total_items >= max_total_items: + return { + "success": False, + "error": "inventory_full", + "message": f"Inventory full! (max {max_total_items} items)", + "item_name": item['name'] + } + inventory[item_id_str] = current_count + 1 player['inventory'] = inventory @@ -190,11 +222,11 @@ class ShopManager: elif item_type == 'luck': # Store luck bonus (would be used in duck spawning logic) current_luck = player.get('luck_bonus', 0) - new_luck = current_luck + amount + new_luck = min(max(current_luck + amount, -50), 100) # Bounded between -50 and +100 player['luck_bonus'] = new_luck return { "type": "luck", - "added": amount, + "added": new_luck - current_luck, "new_total": new_luck } @@ -316,7 +348,7 @@ class ShopManager: elif item_type == 'clean_gun': # Clean gun to reduce jamming chance (positive amount reduces jam chance) current_jam = player.get('jam_chance', 5) # Default 5% jam chance - new_jam = max(current_jam + amount, 0) # amount is negative for cleaning + new_jam = min(max(current_jam + amount, 0), 100) # Bounded between 0% and 100% player['jam_chance'] = new_jam return { @@ -346,10 +378,109 @@ class ShopManager: "duration": duration // 60 # return duration in minutes } + elif item_type == 'insurance': + # Add insurance protection against friendly fire + if 'temporary_effects' not in player: + player['temporary_effects'] = [] + + duration = item.get('duration', 86400) # 24 hours default + protection_type = item.get('protection', 'friendly_fire') + + effect = { + 'type': 'insurance', + 'protection': protection_type, + 'expires_at': time.time() + duration, + 'name': 'Hunter\'s Insurance' + } + player['temporary_effects'].append(effect) + + return { + "type": "insurance", + "protection": protection_type, + "duration": duration // 3600 # return duration in hours + } + + elif item_type == 'buy_gun_back': + # Restore confiscated gun with original ammo + was_confiscated = player.get('gun_confiscated', False) + + if was_confiscated: + player['gun_confiscated'] = False + # Restore original ammo and magazines from when gun was confiscated + restored_ammo = player.get('confiscated_ammo', 0) + restored_magazines = player.get('confiscated_magazines', 1) + player['current_ammo'] = restored_ammo + player['magazines'] = restored_magazines + # Clean up the stored values + player.pop('confiscated_ammo', None) + player.pop('confiscated_magazines', None) + + return { + "type": "buy_gun_back", + "restored": True, + "ammo_restored": restored_ammo + } + else: + return { + "type": "buy_gun_back", + "restored": False, + "message": "Your gun is not confiscated" + } + + + + elif item_type == 'dry_clothes': + # Remove wet clothes effect + + # Remove any wet clothes effects + if 'temporary_effects' in player: + original_count = len(player['temporary_effects']) + player['temporary_effects'] = [ + effect for effect in player['temporary_effects'] + if effect.get('type') != 'wet_clothes' + ] + new_count = len(player['temporary_effects']) + was_wet = original_count > new_count + else: + was_wet = False + + return { + "type": "dry_clothes", + "was_wet": was_wet, + "message": "You changed into dry clothes!" if was_wet else "You weren't wet!" + } + else: self.logger.warning(f"Unknown item type: {item_type}") return {"type": "unknown", "message": f"Unknown effect type: {item_type}"} + def _apply_splash_water_effect(self, target_player: Dict[str, Any], item: Dict[str, Any]) -> Dict[str, Any]: + """Apply splash water effect to target player""" + # Load config directly without import issues + config_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'config.json') + try: + with open(config_path, 'r') as f: + config = json.load(f) + wet_duration = config.get('gameplay', {}).get('wet_clothes_duration', 300) # 5 minutes default + except: + wet_duration = 300 # Default 5 minutes + + if 'temporary_effects' not in target_player: + target_player['temporary_effects'] = [] + + # Add wet clothes effect + wet_effect = { + 'type': 'wet_clothes', + 'expires_at': time.time() + wet_duration + } + target_player['temporary_effects'].append(wet_effect) + + return { + "type": "splash_water", + "target_soaked": True, + "duration": wet_duration // 60 # return duration in minutes + } + def use_inventory_item(self, player: Dict[str, Any], item_id: int, target_player: Optional[Dict[str, Any]] = None) -> Dict[str, Any]: """ Use an item from player's inventory @@ -370,12 +501,22 @@ class ShopManager: "item_name": item['name'] } - # Check if item requires a target - if item.get('target_required', False) and not target_player: + # Special restrictions: Some items require targets, bread cannot have targets + if item['type'] == 'attract_ducks' and target_player: + return { + "success": False, + "error": "bread_no_target", + "message": "Bread affects everyone in the channel - you cannot target a specific player", + "item_name": item['name'] + } + + # Items that must have targets when used (but can be stored in inventory) + target_required_items = ['sabotage_jam', 'splash_water'] + if item['type'] in target_required_items and not target_player: return { "success": False, "error": "target_required", - "message": f"{item['name']} requires a target player", + "message": f"{item['name']} requires a target player to use", "item_name": item['name'] } @@ -385,19 +526,31 @@ class ShopManager: del inventory[item_id_str] player['inventory'] = inventory - # Apply effect - if item.get('target_required', False) and target_player: - effect_result = self._apply_item_effect(target_player, item) + # Determine who gets the effect + if target_player: + # Special handling for harmful effects + if item['type'] == 'splash_water': + effect_result = self._apply_splash_water_effect(target_player, item) + target_affected = True + elif item['type'] == 'sabotage_jam': + effect_result = self._apply_item_effect(target_player, item) + target_affected = True + else: + # Beneficial items - give to target (gifting) + effect_result = self._apply_item_effect(target_player, item) + target_affected = True + # Mark as gift in the result + effect_result['is_gift'] = True return { "success": True, "item_name": item['name'], "effect": effect_result, - "target_affected": True, + "target_affected": target_affected, "remaining_in_inventory": inventory.get(item_id_str, 0) } else: - # Apply effect to user + # Apply effect to user (no target specified) effect_result = self._apply_item_effect(player, item) return { diff --git a/test_config.py b/test_config.py deleted file mode 100644 index 912fdd9..0000000 --- a/test_config.py +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/env python3 - -import json -import os - -def test_config(): - """Test the config structure and values""" - config_path = 'config.json' - - if not os.path.exists(config_path): - print("โŒ Config file not found") - return - - with open(config_path, 'r') as f: - config = json.load(f) - - print("๐Ÿงช Testing configuration structure...") - - # Test connection settings - connection = config.get('connection', {}) - print(f"๐ŸŒ Server: {connection.get('server')}") - print(f"๐Ÿ”Œ Port: {connection.get('port')}") - print(f"๐Ÿค– Nick: {connection.get('nick')}") - print(f"๐Ÿ”’ SSL: {connection.get('ssl')}") - - # Check if password is set (not default) - password = connection.get('password') - password_set = password and password != "your_iline_password_here" - print(f"๐Ÿ”‘ Password configured: {'Yes' if password_set else 'No (default placeholder)'}") - - # Test SASL settings - sasl = config.get('sasl', {}) - print(f"๐Ÿ” SASL enabled: {sasl.get('enabled')}") - print(f"๐Ÿ‘ค SASL username: {sasl.get('username')}") - - # Check if SASL password is set (not default) - sasl_password = sasl.get('password') - sasl_password_set = sasl_password and sasl_password != "duckhunt//789//" - print(f"๐Ÿ—๏ธ SASL password configured: {'Yes' if sasl_password_set else 'No (default placeholder)'}") - - # Test channels - channels = connection.get('channels', []) - print(f"๐Ÿ“บ Channels to join: {channels}") - - print("\nโœ… Configuration structure looks good!") - - if not password_set: - print("โš ๏ธ Warning: Server password is still set to placeholder value") - print(" Update 'connection.password' if your server requires authentication") - - if sasl.get('enabled') and not sasl_password_set: - print("โš ๏ธ Warning: SASL is enabled but password is still placeholder") - print(" Update 'sasl.password' with your actual NickServ password") - -if __name__ == "__main__": - test_config() \ No newline at end of file