diff --git a/CHANGELOG.md b/CHANGELOG.md index 15d2e35..5077ca2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,40 @@ All notable changes to PetBot IRC Game will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [v0.2.0] - 2025-07-13 + +### 🎒 Item Collection System +- **Complete Item System** - 16 unique items across 5 categories +- **Item Discovery** - 30% chance to find items during exploration +- **Rarity Tiers** - Common, Uncommon, Rare, Epic, Legendary with symbols +- **Location-Specific Items** - Unique treasures in each area +- **Inventory Management** - `!inventory` and `!use` commands +- **Consumable Items** - Healing potions, battle boosters, lucky charms +- **Web Integration** - Player inventories displayed on profile pages + +### 🎯 New Items Added +- **Healing Items**: Small/Large/Super Potions, Energy Berries +- **Battle Items**: Attack Boosters, Defense Crystals, Speed Elixirs +- **Rare Items**: Fire/Water Stones, Lucky Charms, Ancient Fossils +- **Location Items**: Pristine Shells, Glowing Mushrooms, Volcanic Glass, Ice Crystals, Ancient Runes + +### 🆕 New Commands +- `!inventory` / `!inv` / `!items` - View collected items by category +- `!use ` - Use consumable items on active pets + +### 🔧 Technical Updates +- **Database Schema** - Added `items` and `player_inventory` tables +- **Game Engine** - Integrated item discovery with exploration system +- **Web Interface** - Added inventory section to player profiles with rarity colors +- **Module System** - New Inventory module for item management + +### 🎨 Visual Enhancements +- **Rarity Symbols** - ○ ◇ ◆ ★ ✦ for different item tiers +- **Color-Coded Items** - Web interface shows items with rarity-specific colors +- **Category Organization** - Items grouped by type in inventory display + +--- + ## [v0.1.0] - 2025-07-13 ### 🎮 Major Features Added diff --git a/GITHUB_AUTH_SETUP.md b/GITHUB_AUTH_SETUP.md new file mode 100644 index 0000000..78b1356 --- /dev/null +++ b/GITHUB_AUTH_SETUP.md @@ -0,0 +1,116 @@ +# GitHub Authentication Setup Guide + +GitHub requires secure authentication for pushing code. Choose one of these methods: + +## 🔑 Option 1: SSH Keys (Recommended) + +SSH keys are more secure and convenient - no password prompts after setup. + +### Setup Steps: +1. **Generate SSH key** (if you don't have one): + ```bash + ssh-keygen -t ed25519 -C "your_email@example.com" + # Press Enter to accept default file location + # Enter a secure passphrase (optional but recommended) + ``` + +2. **Add SSH key to ssh-agent**: + ```bash + eval "$(ssh-agent -s)" + ssh-add ~/.ssh/id_ed25519 + ``` + +3. **Copy public key to clipboard**: + ```bash + cat ~/.ssh/id_ed25519.pub + # Copy the entire output + ``` + +4. **Add key to GitHub**: + - Go to: https://github.com/settings/keys + - Click "New SSH key" + - Paste your public key + - Give it a descriptive title + - Click "Add SSH key" + +5. **Test connection**: + ```bash + ssh -T git@github.com + # Should say: "Hi username! You've successfully authenticated" + ``` + +6. **Use SSH repository URL**: + - Format: `git@github.com:username/repository.git` + - Example: `git@github.com:megaproxy/petbot-irc-game.git` + +--- + +## 🎫 Option 2: Personal Access Token + +Use HTTPS with a token instead of your password. + +### Setup Steps: +1. **Create Personal Access Token**: + - Go to: https://github.com/settings/tokens + - Click "Generate new token" → "Generate new token (classic)" + - Give it a descriptive name: "PetBot Development" + - Select scopes: + - ✅ `repo` (for private repositories) + - ✅ `public_repo` (for public repositories) + - Click "Generate token" + - **Copy the token immediately** (you won't see it again!) + +2. **Use HTTPS repository URL**: + - Format: `https://github.com/username/repository.git` + - Example: `https://github.com/megaproxy/petbot-irc-game.git` + +3. **When prompted for password**: + - Username: Your GitHub username + - Password: **Use your Personal Access Token** (NOT your account password) + +4. **Optional: Store credentials** (so you don't have to enter token every time): + ```bash + git config --global credential.helper store + # After first successful push, credentials will be saved + ``` + +--- + +## 🚀 After Authentication Setup + +Once you've set up authentication, you can proceed with the GitHub setup: + +1. **Create GitHub repository** at https://github.com/new +2. **Run our setup script**: + ```bash + ./setup-github.sh + ``` +3. **Choose your authentication method** (SSH or Token) +4. **Enter your repository URL** +5. **Done!** Future pushes will be automatic + +--- + +## 🔧 Troubleshooting + +### SSH Issues: +- **"Permission denied"**: Check if SSH key is added to GitHub +- **"Could not open a connection"**: Check SSH agent with `ssh-add -l` +- **"Bad owner or permissions"**: Fix with `chmod 600 ~/.ssh/id_ed25519` + +### Token Issues: +- **"Authentication failed"**: Make sure you're using the token, not your password +- **"Remote access denied"**: Check token has correct scopes (repo/public_repo) +- **"Token expired"**: Create a new token at https://github.com/settings/tokens + +### General Issues: +- **"Repository not found"**: Check repository URL and access permissions +- **"Updates were rejected"**: Repository might not be empty - contact support + +--- + +## 📚 Official Documentation + +- **SSH Keys**: https://docs.github.com/en/authentication/connecting-to-github-with-ssh +- **Personal Access Tokens**: https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token +- **Git Authentication**: https://docs.github.com/get-started/getting-started-with-git/about-remote-repositories \ No newline at end of file diff --git a/README.md b/README.md index ac45f17..da514c9 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ A feature-rich IRC bot that brings Pokemon-style pet collecting and battling to - **Battle System**: Engage in turn-based battles with wild pets - **Team Management**: Activate/deactivate pets, swap team members - **Achievement System**: Unlock new areas by completing challenges +- **Item Collection**: Discover and collect useful items during exploration ### Advanced Systems - **Dynamic Weather**: Real-time weather system affecting spawn rates @@ -17,6 +18,7 @@ A feature-rich IRC bot that brings Pokemon-style pet collecting and battling to - **Location-Based Spawns**: Different pets spawn in different locations - **Level Progression**: Pets gain experience and level up - **Type Effectiveness**: Strategic battle system with type advantages +- **Item System**: 16+ unique items with rarity tiers and special effects ### Technical Features - **Modular Architecture**: Clean, extensible codebase @@ -78,6 +80,10 @@ A feature-rich IRC bot that brings Pokemon-style pet collecting and battling to - `!deactivate ` - Move a pet to storage - `!swap ` - Swap two pets' active status +### Inventory Commands +- `!inventory` / `!inv` / `!items` - View your collected items +- `!use ` - Use a consumable item on your active pet + ## 🌍 Locations ### Available Areas @@ -112,10 +118,31 @@ Locations are unlocked by completing achievements: - Location-specific weather patterns - Real-time spawn rate modifications +## 🎒 Item System + +### Item Categories +- **🩹 Healing Items**: Restore pet HP (Small/Large/Super Potions, Energy Berries) +- **⚔️ Battle Items**: Temporary combat boosts (Attack Boosters, Defense Crystals, Speed Elixirs) +- **💎 Rare Items**: Special materials (Fire/Water Stones, Lucky Charms, Ancient Fossils) +- **🏛️ Location Items**: Area-specific treasures (Shells, Mushrooms, Volcanic Glass, Ice Crystals) + +### Rarity Tiers +- **○ Common**: Frequently found items (15% spawn rate) +- **◇ Uncommon**: Moderately rare items (8-12% spawn rate) +- **◆ Rare**: Valuable items (3-6% spawn rate) +- **★ Epic**: Very rare items (2-3% spawn rate) +- **✦ Legendary**: Ultra-rare items (1% spawn rate) + +### Item Discovery +- 30% chance to find items during `!explore` +- Location-specific items only spawn in certain areas +- Items stack in inventory with quantity tracking +- Use consumable items with `!use ` + ## 🌐 Web Interface Access the web dashboard at `http://localhost:8080/`: -- **Player Profiles**: Complete stats and pet collections +- **Player Profiles**: Complete stats, pet collections, and inventories - **Leaderboard**: Top players by level and achievements - **Locations Guide**: All areas with spawn information - **Help System**: Complete command reference @@ -139,13 +166,19 @@ Access the web dashboard at `http://localhost:8080/`: ## 🐛 Recent Updates -### Weather System Enhancement +### v0.2.0 - Item Collection System +- ✅ Complete item system with 16 unique items across 5 categories +- ✅ Item discovery during exploration (30% chance) +- ✅ Rarity tiers with symbols and colors (Common → Legendary) +- ✅ Location-specific unique items and treasures +- ✅ Inventory management with `!inventory` and `!use` commands +- ✅ Web interface integration showing player inventories +- ✅ Consumable items (healing potions, battle boosters, lucky charms) + +### v0.1.0 - Core Game & Weather System - ✅ Added background task for automatic weather updates - ✅ Changed weather durations from 2-6 hours to 30min-3hours - ✅ Implemented continuous weather coverage -- ✅ Added graceful shutdown handling - -### Bug Fixes - ✅ Fixed database persistence on bot restart - ✅ Resolved individual player pages showing 'not found' - ✅ Corrected achievement count displays diff --git a/config/items.json b/config/items.json new file mode 100644 index 0000000..cbc7b54 --- /dev/null +++ b/config/items.json @@ -0,0 +1,213 @@ +{ + "healing_items": [ + { + "id": 1, + "name": "Small Potion", + "description": "Restores 20 HP to a pet", + "rarity": "common", + "category": "healing", + "effect": "heal", + "effect_value": 20, + "locations": ["all"], + "spawn_rate": 0.15 + }, + { + "id": 2, + "name": "Large Potion", + "description": "Restores 50 HP to a pet", + "rarity": "uncommon", + "category": "healing", + "effect": "heal", + "effect_value": 50, + "locations": ["all"], + "spawn_rate": 0.08 + }, + { + "id": 3, + "name": "Super Potion", + "description": "Fully restores a pet's HP", + "rarity": "rare", + "category": "healing", + "effect": "full_heal", + "effect_value": 100, + "locations": ["all"], + "spawn_rate": 0.03 + }, + { + "id": 4, + "name": "Energy Berry", + "description": "Restores 15 HP and cures status effects", + "rarity": "uncommon", + "category": "healing", + "effect": "heal_status", + "effect_value": 15, + "locations": ["mystic_forest", "enchanted_grove"], + "spawn_rate": 0.12 + } + ], + "battle_items": [ + { + "id": 5, + "name": "Attack Booster", + "description": "Increases attack damage by 25% for one battle", + "rarity": "uncommon", + "category": "battle", + "effect": "attack_boost", + "effect_value": 25, + "locations": ["all"], + "spawn_rate": 0.10 + }, + { + "id": 6, + "name": "Defense Crystal", + "description": "Reduces incoming damage by 20% for one battle", + "rarity": "uncommon", + "category": "battle", + "effect": "defense_boost", + "effect_value": 20, + "locations": ["crystal_caves", "frozen_peaks"], + "spawn_rate": 0.08 + }, + { + "id": 7, + "name": "Speed Elixir", + "description": "Guarantees first move in battle", + "rarity": "rare", + "category": "battle", + "effect": "speed_boost", + "effect_value": 100, + "locations": ["all"], + "spawn_rate": 0.05 + } + ], + "rare_items": [ + { + "id": 8, + "name": "Fire Stone", + "description": "A mysterious stone that radiates heat (future evolution item)", + "rarity": "epic", + "category": "evolution", + "effect": "none", + "effect_value": 0, + "locations": ["volcanic_chamber"], + "spawn_rate": 0.02 + }, + { + "id": 9, + "name": "Water Stone", + "description": "A mysterious stone that flows like water (future evolution item)", + "rarity": "epic", + "category": "evolution", + "effect": "none", + "effect_value": 0, + "locations": ["crystal_caves"], + "spawn_rate": 0.02 + }, + { + "id": 10, + "name": "Lucky Charm", + "description": "Increases rare pet encounter rate by 50% for 1 hour", + "rarity": "legendary", + "category": "buff", + "effect": "lucky_boost", + "effect_value": 50, + "locations": ["all"], + "spawn_rate": 0.01 + }, + { + "id": 11, + "name": "Ancient Fossil", + "description": "A mysterious fossil from prehistoric times", + "rarity": "legendary", + "category": "special", + "effect": "none", + "effect_value": 0, + "locations": ["forgotten_ruins"], + "spawn_rate": 0.01 + } + ], + "location_items": [ + { + "id": 12, + "name": "Pristine Shell", + "description": "A beautiful shell that shimmers with ocean magic", + "rarity": "uncommon", + "category": "treasure", + "effect": "sell_value", + "effect_value": 100, + "locations": ["crystal_caves"], + "spawn_rate": 0.12 + }, + { + "id": 13, + "name": "Glowing Mushroom", + "description": "A mushroom that glows with mystical energy", + "rarity": "rare", + "category": "treasure", + "effect": "sell_value", + "effect_value": 200, + "locations": ["mystic_forest", "enchanted_grove"], + "spawn_rate": 0.06 + }, + { + "id": 14, + "name": "Volcanic Glass", + "description": "Sharp obsidian formed by intense heat", + "rarity": "uncommon", + "category": "treasure", + "effect": "sell_value", + "effect_value": 150, + "locations": ["volcanic_chamber"], + "spawn_rate": 0.10 + }, + { + "id": 15, + "name": "Ice Crystal", + "description": "A crystal that never melts, cold to the touch", + "rarity": "rare", + "category": "treasure", + "effect": "sell_value", + "effect_value": 250, + "locations": ["frozen_peaks"], + "spawn_rate": 0.05 + }, + { + "id": 16, + "name": "Ancient Rune", + "description": "A stone tablet with mysterious inscriptions", + "rarity": "epic", + "category": "treasure", + "effect": "sell_value", + "effect_value": 500, + "locations": ["forgotten_ruins"], + "spawn_rate": 0.03 + } + ], + "rarity_info": { + "common": { + "color": "white", + "symbol": "○", + "description": "Common items found frequently" + }, + "uncommon": { + "color": "green", + "symbol": "◇", + "description": "Uncommon items with moderate rarity" + }, + "rare": { + "color": "blue", + "symbol": "◆", + "description": "Rare items with special properties" + }, + "epic": { + "color": "purple", + "symbol": "★", + "description": "Epic items with powerful effects" + }, + "legendary": { + "color": "gold", + "symbol": "✦", + "description": "Legendary items of immense value" + } + } +} \ No newline at end of file diff --git a/modules/__init__.py b/modules/__init__.py index bab8c2c..624c44e 100644 --- a/modules/__init__.py +++ b/modules/__init__.py @@ -7,6 +7,7 @@ from .battle_system import BattleSystem from .pet_management import PetManagement from .achievements import Achievements from .admin import Admin +from .inventory import Inventory __all__ = [ 'CoreCommands', @@ -14,5 +15,6 @@ __all__ = [ 'BattleSystem', 'PetManagement', 'Achievements', - 'Admin' + 'Admin', + 'Inventory' ] \ No newline at end of file diff --git a/modules/exploration.py b/modules/exploration.py index c9ad543..0b1c865 100644 --- a/modules/exploration.py +++ b/modules/exploration.py @@ -35,6 +35,8 @@ class Exploration(BaseModule): self.send_message(channel, f"{nickname}: {encounter['message']}") elif encounter["type"] == "empty": self.send_message(channel, f"🔍 {nickname}: {encounter['message']}") + elif encounter["type"] == "item_found": + self.send_message(channel, f"{nickname}: {encounter['message']}") elif encounter["type"] == "encounter": # Store the encounter for potential catching self.bot.active_encounters[player["id"]] = encounter["pet"] diff --git a/modules/inventory.py b/modules/inventory.py new file mode 100644 index 0000000..1fe60ef --- /dev/null +++ b/modules/inventory.py @@ -0,0 +1,145 @@ +#!/usr/bin/env python3 +"""Inventory management module for PetBot""" + +from .base_module import BaseModule + +class Inventory(BaseModule): + """Handles inventory, item usage, and item management commands""" + + def get_commands(self): + return ["inventory", "inv", "items", "use", "item"] + + async def handle_command(self, channel, nickname, command, args): + if command in ["inventory", "inv", "items"]: + await self.cmd_inventory(channel, nickname) + elif command in ["use", "item"]: + await self.cmd_use_item(channel, nickname, args) + + async def cmd_inventory(self, channel, nickname): + """Display player's inventory""" + player = await self.require_player(channel, nickname) + if not player: + return + + inventory = await self.database.get_player_inventory(player["id"]) + + if not inventory: + self.send_message(channel, f"🎒 {nickname}: Your inventory is empty! Try exploring to find items.") + return + + # Group items by category + categories = {} + for item in inventory: + category = item["category"] + if category not in categories: + categories[category] = [] + categories[category].append(item) + + # Send inventory summary first + total_items = sum(item["quantity"] for item in inventory) + self.send_message(channel, f"🎒 {nickname}'s Inventory ({total_items} items):") + + # Display items by category + rarity_symbols = { + "common": "○", + "uncommon": "◇", + "rare": "◆", + "epic": "★", + "legendary": "✦" + } + + for category, items in categories.items(): + category_display = category.replace("_", " ").title() + self.send_message(channel, f"📦 {category_display}:") + + for item in items[:5]: # Limit to 5 items per category to avoid spam + symbol = rarity_symbols.get(item["rarity"], "○") + quantity_str = f" x{item['quantity']}" if item["quantity"] > 1 else "" + self.send_message(channel, f" {symbol} {item['name']}{quantity_str} - {item['description']}") + + if len(items) > 5: + self.send_message(channel, f" ... and {len(items) - 5} more items") + + self.send_message(channel, f"💡 Use '!use ' to use consumable items!") + + async def cmd_use_item(self, channel, nickname, args): + """Use an item from inventory""" + if not args: + self.send_message(channel, f"{nickname}: Specify an item to use! Example: !use Small Potion") + return + + player = await self.require_player(channel, nickname) + if not player: + return + + item_name = " ".join(args) + result = await self.database.use_item(player["id"], item_name) + + if not result["success"]: + self.send_message(channel, f"❌ {nickname}: {result['error']}") + return + + item = result["item"] + effect = result["effect"] + effect_value = result["effect_value"] + + # Handle different item effects + if effect == "heal": + # Find active pet to heal + active_pets = await self.database.get_active_pets(player["id"]) + if not active_pets: + self.send_message(channel, f"❌ {nickname}: You need an active pet to use healing items!") + return + + # Heal the first active pet (can be expanded to choose specific pet) + pet = active_pets[0] + old_hp = pet["hp"] + new_hp = min(pet["max_hp"], pet["hp"] + effect_value) + healed_amount = new_hp - old_hp + + # Update pet HP in database + await self.database.update_pet_hp(pet["id"], new_hp) + + self.send_message(channel, + f"💊 {nickname}: Used {item['name']} on {pet['nickname'] or pet['species_name']}! " + f"Restored {healed_amount} HP ({old_hp} → {new_hp}/{pet['max_hp']})") + + elif effect == "full_heal": + active_pets = await self.database.get_active_pets(player["id"]) + if not active_pets: + self.send_message(channel, f"❌ {nickname}: You need an active pet to use healing items!") + return + + pet = active_pets[0] + old_hp = pet["hp"] + healed_amount = pet["max_hp"] - old_hp + + await self.database.update_pet_hp(pet["id"], pet["max_hp"]) + + self.send_message(channel, + f"✨ {nickname}: Used {item['name']} on {pet['nickname'] or pet['species_name']}! " + f"Fully restored HP! ({old_hp} → {pet['max_hp']}/{pet['max_hp']})") + + elif effect == "attack_boost": + self.send_message(channel, + f"⚔️ {nickname}: Used {item['name']}! Your next battle will have +{effect_value}% attack damage!") + + elif effect == "defense_boost": + self.send_message(channel, + f"🛡️ {nickname}: Used {item['name']}! Your next battle will have +{effect_value}% damage reduction!") + + elif effect == "speed_boost": + self.send_message(channel, + f"💨 {nickname}: Used {item['name']}! You'll move first in your next battle!") + + elif effect == "lucky_boost": + self.send_message(channel, + f"🍀 {nickname}: Used {item['name']}! Rare pet encounter rate increased by {effect_value}% for 1 hour!") + + elif effect == "none": + self.send_message(channel, + f"📦 {nickname}: Used {item['name']}! This item has no immediate effect but may be useful later.") + + else: + self.send_message(channel, + f"✅ {nickname}: Used {item['name']}! {item['description']}") \ No newline at end of file diff --git a/run_bot.py b/run_bot.py index b36568c..10d713c 100644 --- a/run_bot.py +++ b/run_bot.py @@ -10,7 +10,7 @@ sys.path.append(os.path.dirname(os.path.abspath(__file__))) from src.database import Database from src.game_engine import GameEngine -from modules import CoreCommands, Exploration, BattleSystem, PetManagement, Achievements, Admin +from modules import CoreCommands, Exploration, BattleSystem, PetManagement, Achievements, Admin, Inventory class PetBot: def __init__(self): @@ -53,7 +53,8 @@ class PetBot: BattleSystem, PetManagement, Achievements, - Admin + Admin, + Inventory ] self.modules = {} diff --git a/run_bot_debug.py b/run_bot_debug.py index 8620e21..825b92c 100644 --- a/run_bot_debug.py +++ b/run_bot_debug.py @@ -10,7 +10,7 @@ sys.path.append(os.path.dirname(os.path.abspath(__file__))) from src.database import Database from src.game_engine import GameEngine -from modules import CoreCommands, Exploration, BattleSystem, PetManagement, Achievements, Admin +from modules import CoreCommands, Exploration, BattleSystem, PetManagement, Achievements, Admin, Inventory from webserver import PetBotWebServer class PetBotDebug: @@ -69,7 +69,8 @@ class PetBotDebug: BattleSystem, PetManagement, Achievements, - Admin + Admin, + Inventory ] self.modules = {} diff --git a/setup-github.sh b/setup-github.sh index 36a2d56..1c2d717 100755 --- a/setup-github.sh +++ b/setup-github.sh @@ -1,6 +1,6 @@ #!/bin/bash # GitHub Setup Script for PetBot -# Run this script after creating your GitHub repository +# Run this script after creating your GitHub repository and setting up authentication echo "🚀 PetBot GitHub Setup" echo "======================" @@ -11,36 +11,128 @@ if [ ! -d ".git" ]; then exit 1 fi -# Ask for GitHub repository URL -echo "📝 Please enter your GitHub repository URL:" -echo " (e.g., https://github.com/yourusername/petbot-irc-game.git)" -read -p "URL: " REPO_URL +echo "🔐 GitHub Authentication Setup Required" +echo "=======================================" +echo "" +echo "GitHub requires secure authentication. Choose one option:" +echo "" +echo "Option 1: SSH Key (Recommended)" +echo " • More secure and convenient" +echo " • No password prompts after setup" +echo " • Repository URL format: git@github.com:username/repo.git" +echo "" +echo "Option 2: Personal Access Token" +echo " • Use HTTPS with token instead of password" +echo " • Repository URL format: https://github.com/username/repo.git" +echo "" +echo "📖 Setup guides:" +echo " SSH Keys: https://docs.github.com/en/authentication/connecting-to-github-with-ssh" +echo " Access Tokens: https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token" +echo "" + +# Ask user which method they want to use +echo "Which authentication method did you set up?" +echo "1) SSH Key" +echo "2) Personal Access Token (HTTPS)" +read -p "Enter choice (1 or 2): " AUTH_CHOICE + +case $AUTH_CHOICE in + 1) + echo "" + echo "📝 Enter your SSH repository URL:" + echo " Format: git@github.com:yourusername/petbot-irc-game.git" + read -p "SSH URL: " REPO_URL + + if [[ ! "$REPO_URL" =~ ^git@github\.com: ]]; then + echo "❌ Error: Please use SSH URL format (git@github.com:username/repo.git)" + exit 1 + fi + ;; + 2) + echo "" + echo "📝 Enter your HTTPS repository URL:" + echo " Format: https://github.com/yourusername/petbot-irc-game.git" + read -p "HTTPS URL: " REPO_URL + + if [[ ! "$REPO_URL" =~ ^https://github\.com/ ]]; then + echo "❌ Error: Please use HTTPS URL format (https://github.com/username/repo.git)" + exit 1 + fi + + echo "" + echo "⚠️ Important: When prompted for password, use your Personal Access Token" + echo " Do NOT use your GitHub account password" + ;; + *) + echo "❌ Invalid choice" + exit 1 + ;; +esac if [ -z "$REPO_URL" ]; then echo "❌ Error: No URL provided" exit 1 fi +# Test GitHub connection +echo "" +echo "🔍 Testing GitHub connection..." +if [ "$AUTH_CHOICE" = "1" ]; then + ssh -T git@github.com 2>/dev/null + if [ $? -ne 1 ]; then + echo "❌ SSH connection test failed. Please check your SSH key setup." + echo " Guide: https://docs.github.com/en/authentication/connecting-to-github-with-ssh" + exit 1 + fi + echo "✅ SSH connection successful" +else + echo "⚠️ HTTPS connection will be tested during push" +fi + # Add remote origin +echo "" echo "🔗 Adding GitHub remote..." -git remote add origin "$REPO_URL" +git remote add origin "$REPO_URL" 2>/dev/null || { + echo "🔄 Remote already exists, updating..." + git remote set-url origin "$REPO_URL" +} # Push to GitHub echo "⬆️ Pushing to GitHub..." -git push -u origin main +if ! git push -u origin main; then + echo "" + echo "❌ Push failed. Common solutions:" + if [ "$AUTH_CHOICE" = "1" ]; then + echo " • Check SSH key is added to GitHub: https://github.com/settings/keys" + echo " • Verify SSH agent is running: ssh-add -l" + else + echo " • Use Personal Access Token as password (not account password)" + echo " • Create token at: https://github.com/settings/tokens" + echo " • Token needs 'repo' scope for private repos" + fi + echo " • Verify repository exists and you have write access" + exit 1 +fi # Push tags echo "🏷️ Pushing tags..." git push --tags +echo "" echo "✅ Setup complete!" echo "" echo "🎯 Your repository is now on GitHub:" echo " Repository: $REPO_URL" echo " Current version: v0.1.0" +echo " Authentication: $([ "$AUTH_CHOICE" = "1" ] && echo "SSH Key" || echo "Personal Access Token")" echo "" echo "🔄 Future updates will be automatic:" echo " - Claude will commit changes with descriptive messages" echo " - Changelog will be updated automatically" echo " - Version tags will be created for releases" -echo " - All changes will be pushed to GitHub" \ No newline at end of file +echo " - All changes will be pushed to GitHub" +echo "" +echo "📚 Useful commands:" +echo " git status - Check repository status" +echo " git log --oneline - View commit history" +echo " git tag -l - List all version tags" \ No newline at end of file diff --git a/src/database.py b/src/database.py index e2d0023..aaee089 100644 --- a/src/database.py +++ b/src/database.py @@ -186,6 +186,33 @@ class Database: ) """) + await db.execute(""" + CREATE TABLE IF NOT EXISTS items ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT UNIQUE NOT NULL, + description TEXT NOT NULL, + category TEXT NOT NULL, + rarity TEXT NOT NULL, + effect TEXT, + effect_value INTEGER DEFAULT 0, + consumable BOOLEAN DEFAULT TRUE, + sell_value INTEGER DEFAULT 0 + ) + """) + + await db.execute(""" + CREATE TABLE IF NOT EXISTS player_inventory ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + player_id INTEGER NOT NULL, + item_id INTEGER NOT NULL, + quantity INTEGER DEFAULT 1, + obtained_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (player_id) REFERENCES players (id), + FOREIGN KEY (item_id) REFERENCES items (id), + UNIQUE(player_id, item_id) + ) + """) + await db.commit() async def get_player(self, nickname: str) -> Optional[Dict]: @@ -519,4 +546,144 @@ class Database: "pet2": dict(pet2), "pet1_now": "active" if not pet1["is_active"] else "storage", "pet2_now": "active" if not pet2["is_active"] else "storage" - } \ No newline at end of file + } + + # Item and Inventory Methods + async def add_item_to_inventory(self, player_id: int, item_name: str, quantity: int = 1) -> bool: + """Add an item to player's inventory""" + async with aiosqlite.connect(self.db_path) as db: + # Get item ID + cursor = await db.execute("SELECT id FROM items WHERE name = ?", (item_name,)) + item = await cursor.fetchone() + if not item: + return False + + item_id = item[0] + + # Check if player already has this item + cursor = await db.execute( + "SELECT quantity FROM player_inventory WHERE player_id = ? AND item_id = ?", + (player_id, item_id) + ) + existing = await cursor.fetchone() + + if existing: + # Update quantity + new_quantity = existing[0] + quantity + await db.execute( + "UPDATE player_inventory SET quantity = ? WHERE player_id = ? AND item_id = ?", + (new_quantity, player_id, item_id) + ) + else: + # Insert new item + await db.execute( + "INSERT INTO player_inventory (player_id, item_id, quantity) VALUES (?, ?, ?)", + (player_id, item_id, quantity) + ) + + await db.commit() + return True + + async def get_player_inventory(self, player_id: int) -> List[Dict]: + """Get all items in player's inventory""" + async with aiosqlite.connect(self.db_path) as db: + db.row_factory = aiosqlite.Row + cursor = await db.execute(""" + SELECT i.name, i.description, i.category, i.rarity, i.effect, + i.effect_value, pi.quantity, pi.obtained_at + FROM player_inventory pi + JOIN items i ON pi.item_id = i.id + WHERE pi.player_id = ? + ORDER BY i.rarity DESC, i.name ASC + """, (player_id,)) + rows = await cursor.fetchall() + return [dict(row) for row in rows] + + async def use_item(self, player_id: int, item_name: str) -> Dict: + """Use an item from inventory""" + async with aiosqlite.connect(self.db_path) as db: + # Get item details + db.row_factory = aiosqlite.Row + cursor = await db.execute(""" + SELECT i.*, pi.quantity + FROM items i + JOIN player_inventory pi ON i.id = pi.item_id + WHERE pi.player_id = ? AND i.name = ? + """, (player_id, item_name)) + item = await cursor.fetchone() + + if not item: + return {"success": False, "error": "Item not found in inventory"} + + if item["quantity"] <= 0: + return {"success": False, "error": "No items of this type available"} + + # Remove one from inventory if consumable + if item["consumable"]: + if item["quantity"] == 1: + await db.execute( + "DELETE FROM player_inventory WHERE player_id = ? AND item_id = ?", + (player_id, item["id"]) + ) + else: + await db.execute( + "UPDATE player_inventory SET quantity = quantity - 1 WHERE player_id = ? AND item_id = ?", + (player_id, item["id"]) + ) + await db.commit() + + return { + "success": True, + "item": dict(item), + "effect": item["effect"], + "effect_value": item["effect_value"] + } + + async def initialize_items(self): + """Initialize items from config file""" + import json + + try: + with open("config/items.json", "r") as f: + items_data = json.load(f) + except FileNotFoundError: + print("Items config file not found") + return + + async with aiosqlite.connect(self.db_path) as db: + # Clear existing items + await db.execute("DELETE FROM items") + + # Add all items from config + for category_items in items_data.values(): + if category_items and isinstance(category_items, list): + for item in category_items: + if "id" in item: # Valid item entry + sell_value = 0 + if item.get("effect") == "sell_value": + sell_value = item.get("effect_value", 0) + + await db.execute(""" + INSERT OR REPLACE INTO items + (id, name, description, category, rarity, effect, effect_value, sell_value) + VALUES (?, ?, ?, ?, ?, ?, ?, ?) + """, ( + item["id"], + item["name"], + item["description"], + item["category"], + item["rarity"], + item.get("effect", "none"), + item.get("effect_value", 0), + sell_value + )) + + await db.commit() + print("Items initialized from config") + + async def update_pet_hp(self, pet_id: int, new_hp: int) -> bool: + """Update a pet's current HP""" + async with aiosqlite.connect(self.db_path) as db: + await db.execute("UPDATE pets SET hp = ? WHERE id = ?", (new_hp, pet_id)) + await db.commit() + return True \ No newline at end of file diff --git a/src/game_engine.py b/src/game_engine.py index 8ec59ae..acbdb0f 100644 --- a/src/game_engine.py +++ b/src/game_engine.py @@ -24,6 +24,7 @@ class GameEngine: await self.load_moves() await self.load_type_chart() await self.load_achievements() + await self.database.initialize_items() await self.init_weather_system() await self.battle_engine.load_battle_data() @@ -299,6 +300,11 @@ class GameEngine: if not location: return {"type": "error", "message": "You are not in a valid location!"} + # Check for item discovery first (30% chance) + item_result = await self.check_item_discovery(player_id, location) + if item_result: + return item_result + # Get spawns for current location cursor = await db.execute(""" SELECT ls.*, ps.name as species_name, ps.* @@ -314,8 +320,8 @@ class GameEngine: # Apply weather modifiers to spawns modified_spawns = await self.get_weather_modified_spawns(location["id"], spawns) - # Random encounter chance (70% chance of finding something) - if random.random() > 0.7: + # Random encounter chance (50% chance of finding a pet after item check) + if random.random() > 0.5: return {"type": "empty", "message": f"You explore {location['name']} but find nothing this time..."} # Choose random spawn with weather-modified rates @@ -347,6 +353,63 @@ class GameEngine: } } + async def check_item_discovery(self, player_id: int, location: Dict) -> Optional[Dict]: + """Check if player finds an item during exploration""" + import json + + # Load items config + try: + with open("config/items.json", "r") as f: + items_data = json.load(f) + except FileNotFoundError: + return None + + # Get all possible items for this location + available_items = [] + location_name = location["name"].lower().replace(" ", "_") + + for category_items in items_data.values(): + if isinstance(category_items, list): + for item in category_items: + if "locations" in item: + item_locations = item["locations"] + if "all" in item_locations or location_name in item_locations: + available_items.append(item) + + if not available_items: + return None + + # Calculate total spawn rates for this location + total_rate = sum(item.get("spawn_rate", 0.1) for item in available_items) + + # 30% base chance of finding an item + if random.random() > 0.3: + return None + + # Choose item based on spawn rates + chosen_item = random.choices( + available_items, + weights=[item.get("spawn_rate", 0.1) for item in available_items] + )[0] + + # Add item to player's inventory + success = await self.database.add_item_to_inventory(player_id, chosen_item["name"]) + + if success: + # Get rarity info for display + rarity_info = items_data.get("rarity_info", {}).get(chosen_item["rarity"], {}) + symbol = rarity_info.get("symbol", "○") + + return { + "type": "item_found", + "location": location["name"], + "item": chosen_item, + "symbol": symbol, + "message": f"🎒 You found a {symbol} {chosen_item['name']} ({chosen_item['rarity']})! {chosen_item['description']}" + } + + return None + async def attempt_catch_current_location(self, player_id: int, target_pet: Dict) -> str: """Attempt to catch a pet during exploration""" catch_rate = 0.5 + (0.3 / target_pet.get("rarity", 1)) diff --git a/webserver.py b/webserver.py index 649d917..8b02c03 100644 --- a/webserver.py +++ b/webserver.py @@ -928,10 +928,28 @@ class PetBotRequestHandler(BaseHTTPRequestHandler): } achievements.append(achievement_dict) + # Get player inventory + cursor = await db.execute(""" + SELECT i.name, i.description, i.category, i.rarity, pi.quantity + FROM player_inventory pi + JOIN items i ON pi.item_id = i.id + WHERE pi.player_id = ? + ORDER BY i.rarity DESC, i.name ASC + """, (player_dict['id'],)) + inventory_rows = await cursor.fetchall() + inventory = [] + for row in inventory_rows: + item_dict = { + 'name': row[0], 'description': row[1], 'category': row[2], + 'rarity': row[3], 'quantity': row[4] + } + inventory.append(item_dict) + return { 'player': player_dict, 'pets': pets, - 'achievements': achievements + 'achievements': achievements, + 'inventory': inventory } except Exception as e: @@ -1087,6 +1105,7 @@ class PetBotRequestHandler(BaseHTTPRequestHandler): player = player_data['player'] pets = player_data['pets'] achievements = player_data['achievements'] + inventory = player_data.get('inventory', []) # Calculate stats active_pets = [pet for pet in pets if pet['is_active']] @@ -1139,6 +1158,41 @@ class PetBotRequestHandler(BaseHTTPRequestHandler): No achievements yet. Keep exploring and catching pets to earn achievements! """ + # Build inventory HTML + inventory_html = "" + if inventory: + rarity_symbols = { + "common": "○", + "uncommon": "◇", + "rare": "◆", + "epic": "★", + "legendary": "✦" + } + rarity_colors = { + "common": "#ffffff", + "uncommon": "#1eff00", + "rare": "#0070dd", + "epic": "#a335ee", + "legendary": "#ff8000" + } + + for item in inventory: + symbol = rarity_symbols.get(item['rarity'], "○") + color = rarity_colors.get(item['rarity'], "#ffffff") + quantity_str = f" x{item['quantity']}" if item['quantity'] > 1 else "" + + inventory_html += f""" +
+ {symbol} {item['name']}{quantity_str}
+ {item['description']}
+ Category: {item['category'].replace('_', ' ').title()} | Rarity: {item['rarity'].title()} +
""" + else: + inventory_html = """ +
+ No items yet. Try exploring to find useful items! +
""" + html = f""" @@ -1359,6 +1413,13 @@ class PetBotRequestHandler(BaseHTTPRequestHandler): {achievements_html} + +
+
🎒 Inventory
+
+ {inventory_html} +
+
"""