diff --git a/Street_Fighter/docs/README.md b/Street_Fighter/docs/README.md index 2ff27a478e8..9f75afd829b 100644 --- a/Street_Fighter/docs/README.md +++ b/Street_Fighter/docs/README.md @@ -1,129 +1,349 @@ -# Street Fighter -![download](https://github.com/user-attachments/assets/1395caef-363b-4485-8c0a-8d738f3cd379) +
+# 🥊 Street Fighter +### *The Ultimate Python Fighting Experience* -**Street Fighter** is an engaging two-player fighting game built with Python and Pygame. This project features exciting gameplay mechanics, unique characters, and dynamic animations, making it a perfect choice for retro game enthusiasts and developers interested in Python-based game development. +![Street Fighter Banner](https://github.com/user-attachments/assets/1395caef-363b-4485-8c0a-8d738f3cd379) -## Features -- **Two Distinct Fighters**: - - **Warrior**: A melee combatant with powerful sword attacks. - - **Wizard**: A magic wielder with spell-based attacks. - -- **Gameplay Mechanics**: - - Health bars for each fighter. - - Smooth animations for idle, run, jump, attack, hit, and death actions. - - Scoring system to track player victories. - -- **Dynamic Background**: - - Blurred background effects during the main menu for a cinematic feel. +Typing Animation -- **Sound Effects and Music**: - - Immersive soundtracks and attack effects. +[![Python](https://img.shields.io/badge/Python-3.7+-3776AB?style=for-the-badge&logo=python&logoColor=white)](https://python.org) +[![Pygame](https://img.shields.io/badge/Pygame-2.0+-00A86B?style=for-the-badge&logo=pygame&logoColor=white)](https://pygame.org) +[![License](https://img.shields.io/badge/License-MIT-yellow?style=for-the-badge&logo=open-source-initiative&logoColor=white)](LICENSE) +[![Stars](https://img.shields.io/github/stars/AadityaPanda/Street_Fighter?style=for-the-badge&logo=github&color=FFD700)](https://github.com/AadityaPanda/Street_Fighter/stargazers) -- **Responsive UI**: - - Main menu with start, score, and exit options. - - Victory screen for the winning fighter. +
-- **Custom Controls** for two players. +--- + +## 🎮 About The Game + +**Street Fighter** is an electrifying two-player fighting game that brings classic arcade action to your desktop! Built with Python and Pygame, this project showcases advanced game development techniques with smooth animations, dynamic combat systems, and immersive audio-visual effects. + +> *"Enter the arena, choose your destiny, and prove your fighting prowess!"* ⚔️ + +--- + +## ✨ Game Features + + + + + + +
+ +### 🗡️ **The Warrior** +![Warrior](https://img.shields.io/badge/Melee-Fighter-red?style=for-the-badge&logo=sword&logoColor=white) + +**Master of Blades** +- ⚔️ Devastating sword attacks +- 🛡️ Heavy armor protection +- 💪 High damage output +- 🏃‍♂️ Agile combat moves + + + +### 🧙‍♂️ **The Wizard** +![Wizard](https://img.shields.io/badge/Magic-Caster-blue?style=for-the-badge&logo=magic&logoColor=white) + +**Arcane Spellcaster** +- ✨ Powerful spell attacks +- 🔮 Magical projectiles +- 🌟 Elemental damage +- 🎭 Mystical abilities + +
+ +### 🎯 **Core Gameplay Features** + +
+ +| Feature | Description | Status | +|---------|-------------|--------| +| 💚 **Health System** | Dynamic health bars with real-time updates | ✅ Active | +| 🎬 **Smooth Animations** | Fluid character movements and transitions | ✅ Active | +| 🏆 **Scoring System** | Track victories and tournament progress | ✅ Active | +| 🎵 **Audio Experience** | Immersive soundtracks and combat effects | ✅ Active | +| 🖼️ **Dynamic Backgrounds** | Cinematic blur effects and visual polish | ✅ Active | +| 🎮 **Responsive Controls** | Intuitive two-player control scheme | ✅ Active | + +
+ +--- + +## 🎨 Visual Showcase + +
+ +### 🌟 **Game Highlights** + +```ascii + ⚔️ COMBAT SYSTEM ⚔️ + ╔═══════════════════╗ + ║ • Melee Attacks ║ + ║ • Magic Spells ║ + ║ • Combo System ║ + ║ • Victory Moves ║ + ╚═══════════════════╝ +``` + +
+ +--- ## 📋 Table of Contents -- [Street Fighter](#street-fighter) - - [Features](#features) - - [📋 Table of Contents](#-table-of-contents) - - [Requirements](#requirements) - - [Installation](#installation) - - [Gameplay Instructions](#gameplay-instructions) - - [Player Controls:](#player-controls) - - [Downloads](#downloads) - - [License](#license) - - [Credits](#credits) - - [Contributing](#contributing) - - [Contact](#contact) - -## Requirements -- Python 3.7 or higher -- Required Python libraries: - - `pygame` - - `numpy` - - `opencv-python` - -## Installation - -Follow these steps to install and run the game: - -1. **Clone the Repository**: - ```bash - git clone https://github.com/AadityaPanda/Street_Fighter.git - cd Streer_Fighter - ``` -2. **Install Dependencies**: - ```bash - pip install -r - ``` +
-3. **Run the Game**: - ```bash - python src/main.py - ``` +| Section | Quick Links | +|---------|-------------| +| 🎮 | [Features](#-game-features) | +| ⚙️ | [Requirements](#-requirements) | +| 🚀 | [Installation](#-installation) | +| 🎯 | [Gameplay](#-gameplay-instructions) | +| 📦 | [Downloads](#-downloads) | +| 📄 | [License](#-license) | +| 🙏 | [Credits](#-credits) | +| 🤝 | [Contributing](#-contributing) | +| 📧 | [Contact](#-contact) | + +
+ +--- + +## ⚙️ Requirements + +
+ +### 🛠️ **System Requirements** + +
+ +| Component | Minimum Requirement | +|-----------|-------------------| +| 🐍 **Python** | Version 3.7 or higher | +| 💾 **RAM** | 4GB recommended | +| 💿 **Storage** | 100MB free space | +| 🖥️ **Display** | 1024x768 resolution | + +### 📚 **Required Libraries** + +```python +# Core Dependencies +pygame>=2.0.0 # Game engine +numpy>=1.19.0 # Mathematical operations +opencv-python>=4.5.0 # Image processing +``` + +--- + +## 🚀 Installation + +
+ +### 🎯 **Quick Start Guide** + +
+ +**Step 1: Clone the Repository** +```bash +git clone https://github.com/AadityaPanda/Street_Fighter.git +cd Street_Fighter +``` + +**Step 2: Install Dependencies** +```bash +pip install -r docs/requirements.txt +``` + +**Step 3: Launch the Game** +```bash +python src/main.py +``` + +
+ +🎉 **Ready to Fight!** 🎉 + +
+ +--- -## Gameplay Instructions +## 🎯 Gameplay Instructions -### Player Controls: -- **Player 1**: - - Move: `A` (Left), `D` (Right) - - Jump: `W` - - Attack: `R` (Attack 1), `T` (Attack 2) +
-- **Player 2**: - - Move: Left Arrow (`←`), Right Arrow (`→`) - - Jump: Up Arrow (`↑`) - - Attack: `M` (Attack 1), `N` (Attack 2) +### 🎮 **Master the Controls** -**Objective**: Reduce your opponent's health to zero to win the round. Victory is celebrated with a dynamic win screen! +
-## Downloads + + + + + +
-You can download the latest release of **Street Fighter** from the following link: +### 👤 **Player 1 (Warrior)** +![Player 1](https://img.shields.io/badge/Player-1-red?style=for-the-badge&logo=user&logoColor=white) -[![Version](https://img.shields.io/github/v/release/AadityaPanda/Street_Fighter?color=%230567ff&label=Latest%20Release&style=for-the-badge)](https://github.com/AadityaPanda/Street_Fighter/releases/latest) Download +| Action | Key | +|--------|-----| +| ⬅️ Move Left | `A` | +| ➡️ Move Right | `D` | +| ⬆️ Jump | `W` | +| ⚔️ Attack 1 | `R` | +| 🗡️ Attack 2 | `T` | -## License + -This project is licensed under the [MIT License](LICENSE). Feel free to use, modify, and distribute it in your projects. +### 👤 **Player 2 (Wizard)** +![Player 2](https://img.shields.io/badge/Player-2-blue?style=for-the-badge&logo=user&logoColor=white) -## Credits +| Action | Key | +|--------|-----| +| ⬅️ Move Left | `←` | +| ➡️ Move Right | `→` | +| ⬆️ Jump | `↑` | +| ✨ Spell 1 | `M` | +| 🔮 Spell 2 | `N` | -- **Developer**: Aaditya Panda -- **Assets**: - - Background music and sound effects: [Free Music Archive](https://freemusicarchive.org/) - - Fonts: [Turok Font](https://www.fontspace.com/turok-font) - - Sprites: Custom-designed and modified from open-source assets. +
-## Contributing +
-Contributions are welcome! Here's how you can help: -1. Fork the repository. -2. Create a new branch: +### 🏆 **Victory Conditions** + +```diff ++ Reduce opponent's health to ZERO ++ Unleash devastating combo attacks ++ Master timing and positioning ++ Claim victory in the arena! +``` + +
+ +--- + +## 📦 Downloads + +
+ +### 🎯 **Get the Latest Release** + +[![Latest Release](https://img.shields.io/github/v/release/AadityaPanda/Street_Fighter?color=%230567ff&label=Latest%20Release&style=for-the-badge&logo=github)](https://github.com/AadityaPanda/Street_Fighter/releases/latest) + +[![Download Game](https://custom-icon-badges.demolab.com/badge/-Download%20Game-0B6623?style=for-the-badge&logo=download&logoColor=white)](https://github.com/AadityaPanda/Street_Fighter/releases/download/v1.1/Game.zip) + +[![Download Source](https://img.shields.io/badge/Download-Source%20Code-blue?style=for-the-badge&logo=github&logoColor=white)](https://github.com/AadityaPanda/Street_Fighter/archive/refs/heads/main.zip) + +
+ +--- + +## 📄 License + +
+ +This project is licensed under the **MIT License** + +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=for-the-badge)](https://opensource.org/licenses/MIT) + +*Feel free to use, modify, and distribute in your projects!* + +
+ +--- + +## 🙏 Credits + +
+ +### 👨‍💻 **Development Team** + +
+ +| Role | Contributor | +|------|-------------| +| 🎮 **Lead Developer** | [Aaditya Panda](https://github.com/AadityaPanda) | +| 🎨 **Game Design** | Aaditya Panda | +| 🔧 **Technical Lead** | Aaditya Panda | + +### 🎨 **Assets & Resources** + +| Asset Type | Source | +|------------|--------| +| 🎵 **Background Music** | [Free Music Archive](https://freemusicarchive.org/) | +| 🔤 **Fonts** | [Turok Font](https://www.fontspace.com/turok-font) | +| 🎭 **Sprites** | Custom-designed + Open-source assets | +| 🎧 **Sound Effects** | Royalty-free audio libraries | + +--- + +## 🤝 Contributing + +
+ +### 🌟 **Join the Development!** + +We welcome contributions from the community! + +
+ +**🚀 Quick Contribution Guide:** + +1. **Fork** the repository + ```bash + git fork https://github.com/AadityaPanda/Street_Fighter.git + ``` + +2. **Create** a feature branch ```bash - git checkout -b feature/YourFeatureName + git checkout -b feature/AmazingFeature ``` -3. Commit your changes: + +3. **Commit** your changes ```bash - git commit -m "Add YourFeatureName" + git commit -m "Add AmazingFeature" ``` -4. Push to the branch: + +4. **Push** to the branch ```bash - git push origin feature/YourFeatureName + git push origin feature/AmazingFeature ``` -5. Open a pull request. -Check the [CONTRIBUTING.md](CONTRIBUTING.md) for detailed guidelines. +5. **Open** a Pull Request + +
+ +[![Contributors](https://img.shields.io/github/contributors/AadityaPanda/Street_Fighter?style=for-the-badge&logo=github)](https://github.com/AadityaPanda/Street_Fighter/graphs/contributors) + +*Check [CONTRIBUTING.md](CONTRIBUTING.md) for detailed guidelines* + +
+ +--- + +## 📧 Contact + +
+ +### 🌐 **Let's Connect!** + +[![Email](https://img.shields.io/badge/Email-aadityapanda23@gmail.com-D14836?style=for-the-badge&logo=gmail&logoColor=white)](mailto:aadityapanda23@gmail.com) +[![GitHub](https://img.shields.io/badge/GitHub-AadityaPanda-181717?style=for-the-badge&logo=github&logoColor=white)](https://github.com/AadityaPanda) +[![LinkedIn](https://img.shields.io/badge/LinkedIn-Connect-0077B5?style=for-the-badge&logo=linkedin&logoColor=white)](https://www.linkedin.com/in/aadityapanda/) + +--- + +### 💫 *"Try something new every day!"* + +Closing Message -## Contact +
-- **Developer**: Aaditya Panda -- **Email**: [aadityapanda23@gmail.com](mailto:aadityapanda23@gmail.com) -- **GitHub**: [AadityaPanda](https://github.com/AadityaPanda) +![Made with Love](https://img.shields.io/badge/Made%20with-❤️-red?style=for-the-badge) +![Python](https://img.shields.io/badge/Built%20with-Python-blue?style=for-the-badge&logo=python&logoColor=white) -Try somehting new everyday!!! +
diff --git a/Street_Fighter/src/fighter.py b/Street_Fighter/src/fighter.py index 94fc68abd16..3c22eabecdd 100644 --- a/Street_Fighter/src/fighter.py +++ b/Street_Fighter/src/fighter.py @@ -1,6 +1,6 @@ import pygame - - +import random +import math class Fighter: def __init__(self, player, x, y, flip, data, sprite_sheet, animation_steps, sound): self.player = player @@ -24,6 +24,14 @@ def __init__(self, player, x, y, flip, data, sprite_sheet, animation_steps, soun self.hit = False self.health = 100 self.alive = True + # AI attributes for computer player + self.ai_enabled = (player == 2) # Player 2 is AI-controlled + self.ai_reaction_time = 0 + self.ai_decision_cooldown = 0 + self.ai_aggression = 0.85 # Increased aggression (0-1 scale, higher = more aggressive) + self.ai_defense_threshold = 25 # Lower threshold for defensive behavior + self.ai_reaction_speed = 5 # Faster reaction time (lower = faster) + self.ai_predictive_aim = True # Enable predictive aiming def load_images(self, sprite_sheet, animation_steps): # extract images from spritesheet @@ -31,18 +39,108 @@ def load_images(self, sprite_sheet, animation_steps): for y, animation in enumerate(animation_steps): temp_img_list = [] for x in range(animation): - temp_img = sprite_sheet.subsurface( - x * self.size, y * self.size, self.size, self.size - ) + temp_img = sprite_sheet.subsurface(x * self.size, y * self.size, self.size, self.size) temp_img_list.append( - pygame.transform.scale( - temp_img, - (self.size * self.image_scale, self.size * self.image_scale), - ) - ) + pygame.transform.scale(temp_img, (self.size * self.image_scale, self.size * self.image_scale))) animation_list.append(temp_img_list) return animation_list + def ai_make_decision(self, target, screen_width, screen_height): + """Enhanced AI decision making for computer-controlled fighter""" + if not self.ai_enabled or not self.alive: + return 0, 0, False, 0 # dx, dy, jump, attack_type + + # Reduce decision cooldown + if self.ai_decision_cooldown > 0: + self.ai_decision_cooldown -= 1 + return 0, 0, False, 0 + + # Calculate distance to target + distance = abs(target.rect.centerx - self.rect.centerx) + vertical_distance = abs(target.rect.centery - self.rect.centery) + + # Predictive aiming - anticipate player movement + target_velocity_x = 0 + if hasattr(target, 'running') and target.running: + target_velocity_x = 10 if target.rect.centerx > self.rect.centerx else -10 + + # AI behavior based on health + is_defensive = self.health < self.ai_defense_threshold + + # Movement decisions + dx = 0 + dy = 0 + should_jump = False + attack_type = 0 + + # Enhanced movement logic + if distance > 180: # Too far - approach aggressively + if target.rect.centerx > self.rect.centerx: + dx = 12 # Move right faster + else: + dx = -12 # Move left faster + self.running = True + elif distance < 60: # Too close - maintain optimal distance + if target.rect.centerx > self.rect.centerx: + dx = -8 # Move left + else: + dx = 8 # Move right + self.running = True + elif distance < 120: # Optimal range - circle around opponent + if random.random() < 0.3: # 30% chance to circle + if target.rect.centerx > self.rect.centerx: + dx = -3 + else: + dx = 3 + self.running = True + + # Enhanced jump decisions + jump_chance = 0.4 if not is_defensive else 0.6 # More likely to jump when defensive + if (random.random() < jump_chance and not self.jump and + (distance < 200 or vertical_distance > 50 or target.jump)): + should_jump = True + + # Counter-attack logic - attack when opponent is vulnerable + should_attack = False + if distance < 140 and self.attack_cooldown == 0: + # Attack when opponent is jumping or attacking + if target.jump or target.attacking: + should_attack = True + # Normal attack decision + elif random.random() < self.ai_aggression: + should_attack = True + + if should_attack: + # Smart attack type selection + if target.jump: + attack_type = 2 # Use long range attack against jumping opponent + elif distance < 80: + attack_type = 1 # Close range attack + else: + attack_type = 2 # Long range attack + + # Faster decision cooldown for more aggressive AI + self.ai_decision_cooldown = random.randint(5, 15) + + # Enhanced defensive behavior + if is_defensive: + # Increase distance when health is low + if distance < 250: + if target.rect.centerx > self.rect.centerx: + dx = -10 + else: + dx = 10 + self.running = True + # More likely to jump to escape + if random.random() < 0.5 and not self.jump: + should_jump = True + # Block/evade more often + if target.attacking and distance < 100: + if random.random() < 0.7: # 70% chance to evade + should_jump = True + + return dx, dy, should_jump, attack_type + def move(self, screen_width, screen_height, target, round_over): SPEED = 10 GRAVITY = 2 @@ -51,12 +149,12 @@ def move(self, screen_width, screen_height, target, round_over): self.running = False self.attack_type = 0 - # get keypresses + # get keypresses for human player key = pygame.key.get_pressed() # can only perform other actions if not currently attacking if self.attacking == False and self.alive == True and round_over == False: - # check player 1 controls + # check player 1 controls (human player) if self.player == 1: # movement if key[pygame.K_a]: @@ -78,27 +176,25 @@ def move(self, screen_width, screen_height, target, round_over): if key[pygame.K_t]: self.attack_type = 2 - # check player 2 controls - if self.player == 2: - # movement - if key[pygame.K_LEFT]: - dx = -SPEED - self.running = True - if key[pygame.K_RIGHT]: - dx = SPEED + # check player 2 controls (AI player) + elif self.player == 2 and self.ai_enabled: + # Get AI decisions + ai_dx, ai_dy, should_jump, attack_type = self.ai_make_decision(target, screen_width, screen_height) + + # Apply AI movement + if ai_dx != 0: + dx = ai_dx self.running = True - # jump - if key[pygame.K_UP] and self.jump == False: + + # Apply AI jump + if should_jump and self.jump == False: self.vel_y = -30 self.jump = True - # attack - if key[pygame.K_m] or key[pygame.K_n]: + + # Apply AI attack + if attack_type > 0: self.attack(target) - # determine which attack type was used - if key[pygame.K_m]: - self.attack_type = 1 - if key[pygame.K_n]: - self.attack_type = 2 + self.attack_type = attack_type # apply gravity self.vel_y += GRAVITY @@ -179,12 +275,8 @@ def attack(self, target): # execute attack self.attacking = True self.attack_sound.play() - attacking_rect = pygame.Rect( - self.rect.centerx - (2 * self.rect.width * self.flip), - self.rect.y, - 2 * self.rect.width, - self.rect.height, - ) + attacking_rect = pygame.Rect(self.rect.centerx - (2 * self.rect.width * self.flip), self.rect.y, + 2 * self.rect.width, self.rect.height) if attacking_rect.colliderect(target.rect): target.health -= 10 target.hit = True @@ -199,10 +291,4 @@ def update_action(self, new_action): def draw(self, surface): img = pygame.transform.flip(self.image, self.flip, False) - surface.blit( - img, - ( - self.rect.x - (self.offset[0] * self.image_scale), - self.rect.y - (self.offset[1] * self.image_scale), - ), - ) + surface.blit(img, (self.rect.x - (self.offset[0] * self.image_scale), self.rect.y - (self.offset[1] * self.image_scale))) diff --git a/Street_Fighter/src/main.py b/Street_Fighter/src/main.py index 62778cee3b3..1d000dcb2a0 100644 --- a/Street_Fighter/src/main.py +++ b/Street_Fighter/src/main.py @@ -1,23 +1,26 @@ import math import pygame from pygame import mixer +from pygame import font import cv2 import numpy as np import os import sys from fighter import Fighter - # Helper Function for Bundled Assets def resource_path(relative_path): try: base_path = sys._MEIPASS except Exception: - base_path = os.path.abspath(".") + # When running from src directory, look in parent directory for assets + if os.path.basename(os.getcwd()) == "src": + base_path = os.path.abspath("..") + else: + base_path = os.path.abspath(".") return os.path.join(base_path, relative_path) - mixer.init() pygame.init() @@ -43,21 +46,13 @@ def resource_path(relative_path): # Load Assets bg_image = cv2.imread(resource_path("assets/images/bg1.jpg")) -victory_img = pygame.image.load( - resource_path("assets/images/victory.png") -).convert_alpha() -warrior_victory_img = pygame.image.load( - resource_path("assets/images/warrior.png") -).convert_alpha() -wizard_victory_img = pygame.image.load( - resource_path("assets/images/wizard.png") -).convert_alpha() +victory_img = pygame.image.load(resource_path("assets/images/victory.png")).convert_alpha() +warrior_victory_img = pygame.image.load(resource_path("assets/images/warrior.png")).convert_alpha() +wizard_victory_img = pygame.image.load(resource_path("assets/images/wizard.png")).convert_alpha() # Fonts menu_font = pygame.font.Font(resource_path("assets/fonts/turok.ttf"), 50) -menu_font_title = pygame.font.Font( - resource_path("assets/fonts/turok.ttf"), 100 -) # Larger font for title +menu_font_title = pygame.font.Font(resource_path("assets/fonts/turok.ttf"), 100) # Larger font for title count_font = pygame.font.Font(resource_path("assets/fonts/turok.ttf"), 80) score_font = pygame.font.Font(resource_path("assets/fonts/turok.ttf"), 30) @@ -71,12 +66,8 @@ def resource_path(relative_path): magic_fx.set_volume(0.75) # Load Fighter Spritesheets -warrior_sheet = pygame.image.load( - resource_path("assets/images/warrior.png") -).convert_alpha() -wizard_sheet = pygame.image.load( - resource_path("assets/images/wizard.png") -).convert_alpha() +warrior_sheet = pygame.image.load(resource_path("assets/images/warrior.png")).convert_alpha() +wizard_sheet = pygame.image.load(resource_path("assets/images/wizard.png")).convert_alpha() # Define Animation Steps WARRIOR_ANIMATION_STEPS = [10, 8, 1, 7, 7, 3, 7] @@ -131,24 +122,13 @@ def draw_button(text, font, text_col, button_col, x, y, width, height): def victory_screen(winner_img): start_time = pygame.time.get_ticks() while pygame.time.get_ticks() - start_time < ROUND_OVER_COOLDOWN: - resized_victory_img = pygame.transform.scale( - victory_img, (victory_img.get_width() * 2, victory_img.get_height() * 2) - ) - screen.blit( - resized_victory_img, - ( - SCREEN_WIDTH // 2 - resized_victory_img.get_width() // 2, - SCREEN_HEIGHT // 2 - resized_victory_img.get_height() // 2 - 50, - ), - ) - - screen.blit( - winner_img, - ( - SCREEN_WIDTH // 2 - winner_img.get_width() // 2, - SCREEN_HEIGHT // 2 - winner_img.get_height() // 2 + 100, - ), - ) + + resized_victory_img = pygame.transform.scale(victory_img, (victory_img.get_width() * 2, victory_img.get_height() * 2)) + screen.blit(resized_victory_img, (SCREEN_WIDTH // 2 - resized_victory_img.get_width() // 2, + SCREEN_HEIGHT // 2 - resized_victory_img.get_height() // 2 - 50)) + + screen.blit(winner_img, (SCREEN_WIDTH // 2 - winner_img.get_width() // 2, + SCREEN_HEIGHT // 2 - winner_img.get_height() // 2 + 100)) pygame.display.update() @@ -176,9 +156,7 @@ def main_menu(): elapsed_time = (pygame.time.get_ticks() - animation_start_time) / 1000 scale_factor = 1 + 0.05 * math.sin(elapsed_time * 2 * math.pi) # Slight scaling - scaled_font = pygame.font.Font( - "assets/fonts/turok.ttf", int(100 * scale_factor) - ) + scaled_font = pygame.font.Font(resource_path("assets/fonts/turok.ttf"), int(100 * scale_factor)) title_text = "STREET FIGHTER" colors = [BLUE, GREEN, YELLOW] @@ -187,57 +165,26 @@ def main_menu(): title_y = SCREEN_HEIGHT // 6 shadow_offset = 5 - draw_text( - title_text, - scaled_font, - shadow_color, - title_x + shadow_offset, - title_y + shadow_offset, - ) + draw_text(title_text, scaled_font, shadow_color, title_x + shadow_offset, title_y + shadow_offset) draw_gradient_text(title_text, scaled_font, title_x, title_y, colors) button_width = 280 button_height = 60 button_spacing = 30 - start_button_y = ( - SCREEN_HEIGHT // 2 - (button_height + button_spacing) * 1.5 + 50 - ) - scores_button_y = ( - SCREEN_HEIGHT // 2 - (button_height + button_spacing) * 0.5 + 50 - ) - exit_button_y = SCREEN_HEIGHT // 2 + (button_height + button_spacing) * 0.5 + 50 - - start_button = draw_button( - "START GAME", - menu_font, - BLACK, - GREEN, - SCREEN_WIDTH // 2 - button_width // 2, - start_button_y, - button_width, - button_height, - ) - scores_button = draw_button( - "SCORES", - menu_font, - BLACK, - GREEN, - SCREEN_WIDTH // 2 - button_width // 2, - scores_button_y, - button_width, - button_height, - ) - exit_button = draw_button( - "EXIT", - menu_font, - BLACK, - GREEN, - SCREEN_WIDTH // 2 - button_width // 2, - exit_button_y, - button_width, - button_height, - ) + start_button_y = SCREEN_HEIGHT // 2 - (button_height + button_spacing) * 2 + 50 + controls_button_y = SCREEN_HEIGHT // 2 - (button_height + button_spacing) * 1 + 50 + scores_button_y = SCREEN_HEIGHT // 2 + (button_height + button_spacing) * 0 + 50 + exit_button_y = SCREEN_HEIGHT // 2 + (button_height + button_spacing) * 1 + 50 + + start_button = draw_button("START GAME", menu_font, BLACK, GREEN, SCREEN_WIDTH // 2 - button_width // 2, + start_button_y, button_width, button_height) + controls_button = draw_button("CONTROLS", menu_font, BLACK, GREEN, SCREEN_WIDTH // 2 - button_width // 2, + controls_button_y, button_width, button_height) + scores_button = draw_button("SCORES", menu_font, BLACK, GREEN, SCREEN_WIDTH // 2 - button_width // 2, + scores_button_y, button_width, button_height) + exit_button = draw_button("EXIT", menu_font, BLACK, GREEN, SCREEN_WIDTH // 2 - button_width // 2, + exit_button_y, button_width, button_height) for event in pygame.event.get(): if event.type == pygame.QUIT: @@ -246,6 +193,8 @@ def main_menu(): if event.type == pygame.MOUSEBUTTONDOWN: if start_button.collidepoint(event.pos): return "START" + if controls_button.collidepoint(event.pos): + return "CONTROLS" if scores_button.collidepoint(event.pos): return "SCORES" if exit_button.collidepoint(event.pos): @@ -261,57 +210,76 @@ def scores_screen(): draw_bg(bg_image) scores_title = "SCORES" - draw_text( - scores_title, - menu_font_title, - RED, - SCREEN_WIDTH // 2 - menu_font_title.size(scores_title)[0] // 2, - 50, - ) - - score_font_large = pygame.font.Font( - "assets/fonts/turok.ttf", 60 - ) # Increased size for scores + draw_text(scores_title, menu_font_title, RED, SCREEN_WIDTH // 2 - menu_font_title.size(scores_title)[0] // 2, 50) + + score_font_large = pygame.font.Font(resource_path("assets/fonts/turok.ttf"), 60) # Increased size for scores p1_text = f"P1: {score[0]}" p2_text = f"P2: {score[1]}" shadow_offset = 5 p1_text_x = SCREEN_WIDTH // 2 - score_font_large.size(p1_text)[0] // 2 p1_text_y = SCREEN_HEIGHT // 2 - 50 - draw_text( - p1_text, - score_font_large, - BLACK, - p1_text_x + shadow_offset, - p1_text_y + shadow_offset, - ) # Shadow - draw_gradient_text( - p1_text, score_font_large, p1_text_x, p1_text_y, [BLUE, GREEN] - ) # Gradient + draw_text(p1_text, score_font_large, BLACK, p1_text_x + shadow_offset, p1_text_y + shadow_offset) # Shadow + draw_gradient_text(p1_text, score_font_large, p1_text_x, p1_text_y, [BLUE, GREEN]) # Gradient p2_text_x = SCREEN_WIDTH // 2 - score_font_large.size(p2_text)[0] // 2 p2_text_y = SCREEN_HEIGHT // 2 + 50 - draw_text( - p2_text, - score_font_large, - BLACK, - p2_text_x + shadow_offset, - p2_text_y + shadow_offset, - ) # Shadow - draw_gradient_text( - p2_text, score_font_large, p2_text_x, p2_text_y, [RED, YELLOW] - ) # Gradient - - return_button = draw_button( - "RETURN TO MAIN MENU", - menu_font, - BLACK, - GREEN, - SCREEN_WIDTH // 2 - 220, - 700, - 500, - 50, - ) + draw_text(p2_text, score_font_large, BLACK, p2_text_x + shadow_offset, p2_text_y + shadow_offset) # Shadow + draw_gradient_text(p2_text, score_font_large, p2_text_x, p2_text_y, [RED, YELLOW]) # Gradient + + return_button = draw_button("RETURN TO MAIN MENU", menu_font, BLACK, GREEN, SCREEN_WIDTH // 2 - 220, 700, 500, 50) + + for event in pygame.event.get(): + if event.type == pygame.QUIT: + pygame.quit() + exit() + if event.type == pygame.MOUSEBUTTONDOWN: + if return_button.collidepoint(event.pos): + return + + pygame.display.update() + clock.tick(FPS) + + +def controls_screen(): + while True: + draw_bg(bg_image) + + # Title + controls_title = "CONTROLS GUIDE" + draw_text(controls_title, menu_font_title, GREEN, SCREEN_WIDTH // 2 - menu_font_title.size(controls_title)[0] // 2, 50) + + # Controls information + controls_font = pygame.font.Font(resource_path("assets/fonts/turok.ttf"), 35) + small_font = pygame.font.Font(resource_path("assets/fonts/turok.ttf"), 25) + + # Movement controls + draw_text("MOVEMENT:", controls_font, WHITE, 100, 150) + draw_text("A - Move Left", small_font, BLUE, 100, 190) + draw_text("D - Move Right", small_font, BLUE, 100, 220) + draw_text("W - Jump", small_font, BLUE, 100, 250) + + # Attack controls + draw_text("ATTACKS:", controls_font, WHITE, 100, 300) + draw_text("R - Close Range Attack", small_font, RED, 100, 340) + draw_text("T - Long Range Attack", small_font, RED, 100, 370) + + # Game info + draw_text("GAME INFO:", controls_font, WHITE, 500, 150) + draw_text("• You play as the WARRIOR (left)", small_font, YELLOW, 500, 190) + draw_text("• Computer plays as WIZARD (right)", small_font, (255, 100, 0), 500, 220) + draw_text("• Each attack deals 10 damage", small_font, WHITE, 500, 250) + draw_text("• First to lose all health loses", small_font, WHITE, 500, 280) + + # Tips + draw_text("TIPS:", controls_font, WHITE, 500, 330) + draw_text("• Jump to avoid attacks", small_font, GREEN, 500, 370) + draw_text("• Use close attacks when near", small_font, GREEN, 500, 400) + draw_text("• Use long attacks at distance", small_font, GREEN, 500, 430) + draw_text("• Watch for AI patterns!", small_font, GREEN, 500, 460) + + # Return button + return_button = draw_button("RETURN TO MAIN MENU", menu_font, BLACK, GREEN, SCREEN_WIDTH // 2 - 220, 550, 500, 50) for event in pygame.event.get(): if event.type == pygame.QUIT: @@ -327,19 +295,8 @@ def scores_screen(): def reset_game(): global fighter_1, fighter_2 - fighter_1 = Fighter( - 1, - 200, - 310, - False, - WARRIOR_DATA, - warrior_sheet, - WARRIOR_ANIMATION_STEPS, - sword_fx, - ) - fighter_2 = Fighter( - 2, 700, 310, True, WIZARD_DATA, wizard_sheet, WIZARD_ANIMATION_STEPS, magic_fx - ) + fighter_1 = Fighter(1, 200, 310, False, WARRIOR_DATA, warrior_sheet, WARRIOR_ANIMATION_STEPS, sword_fx) + fighter_2 = Fighter(2, 700, 310, True, WIZARD_DATA, wizard_sheet, WIZARD_ANIMATION_STEPS, magic_fx) def draw_health_bar(health, x, y): @@ -350,7 +307,7 @@ def draw_health_bar(health, x, y): def countdown(): - countdown_font = pygame.font.Font("assets/fonts/turok.ttf", 100) + countdown_font = pygame.font.Font(resource_path("assets/fonts/turok.ttf"), 100) countdown_texts = ["3", "2", "1", "FIGHT!"] for text in countdown_texts: @@ -380,12 +337,16 @@ def game_loop(): draw_text(f"P1: {score[0]}", score_font, RED, 20, 20) draw_text(f"P2: {score[1]}", score_font, RED, SCREEN_WIDTH - 220, 20) + + # Add player identification labels + player_label_font = pygame.font.Font(resource_path("assets/fonts/turok.ttf"), 25) + draw_text("USER", player_label_font, BLUE, 20, 85) + draw_text("COMPUTER", player_label_font, (255, 100, 0), SCREEN_WIDTH - 220, 85) + draw_health_bar(fighter_1.health, 20, 50) draw_health_bar(fighter_2.health, SCREEN_WIDTH - 220, 50) - exit_button = draw_button( - "MAIN MENU", menu_font, BLACK, YELLOW, SCREEN_WIDTH // 2 - 150, 20, 300, 50 - ) + exit_button = draw_button("MAIN MENU", menu_font, BLACK, YELLOW, SCREEN_WIDTH // 2 - 150, 20, 300, 50) if not round_over: fighter_1.move(SCREEN_WIDTH, SCREEN_HEIGHT, fighter_2, round_over) @@ -426,5 +387,7 @@ def game_loop(): if menu_selection == "START": game_loop() + elif menu_selection == "CONTROLS": + controls_screen() elif menu_selection == "SCORES": scores_screen()