Initial commit: WarioWare Black and WAH game

This commit is contained in:
TotorRuns
2025-11-09 11:27:36 +01:00
commit 74113ca143
51 changed files with 1896 additions and 0 deletions
+108
View File
@@ -0,0 +1,108 @@
name: Build and Release
on:
push:
branches:
- main
- master
tags:
- 'v*'
workflow_dispatch:
jobs:
build:
name: Build on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
include:
- os: ubuntu-latest
artifact_name: WarioWare-Black-and-WAH-Linux
asset_name: WarioWare-Black-and-WAH-Linux.zip
- os: windows-latest
artifact_name: WarioWare-Black-and-WAH-Windows
asset_name: WarioWare-Black-and-WAH-Windows.zip
- os: macos-latest
artifact_name: WarioWare-Black-and-WAH-macOS
asset_name: WarioWare-Black-and-WAH-macOS.zip
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pygame pyinstaller
- name: Build with PyInstaller (Windows)
if: matrix.os == 'windows-latest'
run: |
pyinstaller --onefile --windowed --name "WarioWare-Black-and-WAH" --add-data "Assets;Assets" main.py
- name: Build with PyInstaller (Linux/macOS)
if: matrix.os != 'windows-latest'
run: |
pyinstaller --onefile --windowed --name "WarioWare-Black-and-WAH" --add-data "Assets:Assets" main.py
- name: Package build (Windows)
if: matrix.os == 'windows-latest'
run: |
Compress-Archive -Path dist/* -DestinationPath ${{ matrix.asset_name }}
- name: Package build (Linux/macOS)
if: matrix.os != 'windows-latest'
run: |
cd dist
zip -r ../${{ matrix.asset_name }} *
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.artifact_name }}
path: ${{ matrix.asset_name }}
release:
name: Create Release
needs: build
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/')
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
- name: Generate release tag
id: tag
run: |
if [[ "${{ github.ref }}" == refs/tags/* ]]; then
echo "tag=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
else
echo "tag=build-$(date +'%Y%m%d-%H%M%S')-${GITHUB_SHA::7}" >> $GITHUB_OUTPUT
fi
- name: Create Release
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ steps.tag.outputs.tag }}
name: Release ${{ steps.tag.outputs.tag }}
draft: false
prerelease: ${{ !startsWith(github.ref, 'refs/tags/v') }}
generate_release_notes: true
files: |
artifacts/WarioWare-Black-and-WAH-Linux/WarioWare-Black-and-WAH-Linux.zip
artifacts/WarioWare-Black-and-WAH-Windows/WarioWare-Black-and-WAH-Windows.zip
artifacts/WarioWare-Black-and-WAH-macOS/WarioWare-Black-and-WAH-macOS.zip
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+146
View File
@@ -0,0 +1,146 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
.python-version
# pipenv
Pipfile.lock
# PEP 582
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# IDEs
.vscode/
.idea/
*.swp
*.swo
*~
# OS
.DS_Store
Thumbs.db
Desktop.ini
# Game specific
*.pyc
# Project documentation and notes
concept.txt
gamelist.txt
gemini.md
resources.txt
statements.txt
2025-10-19 08_10_42-Documents.png
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 520 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 466 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1002 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 741 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.
Binary file not shown.
+63
View File
@@ -0,0 +1,63 @@
# WarioWare: Black & Wah!
A WarioWare-style microgame collection built with Pygame.
## Description
WarioWare: Black & Wah! is a fast-paced collection of microgames inspired by the classic WarioWare series. Players must quickly complete a series of short, quirky challenges with simple controls and increasing difficulty.
## Features
- Multiple unique microgames
- Fast-paced gameplay with time limits
- Increasing difficulty as you progress
- Score tracking and lives system
- Retro-style graphics and sound
## Requirements
- Python 3.x
- Pygame
## Installation
1. Clone this repository:
```bash
git clone https://github.com/yourusername/WarioWare-Black-and-WAH.git
cd WarioWare-Black-and-WAH
```
2. Install dependencies:
```bash
pip install pygame
```
## How to Play
Run the game:
```bash
python main.py
```
Follow the on-screen instructions for each microgame. You typically have just a few seconds to complete each challenge!
## Controls
Controls vary by microgame and are displayed at the start of each challenge.
## Project Structure
- `main.py` - Main game entry point
- `game_manager.py` - Manages game flow and microgame selection
- `settings.py` - Game configuration and constants
- `engine/` - Core game engine components
- `microgames/` - Individual microgame implementations
- `Assets/` - Game assets (images, sounds, etc.)
## License
This project is for educational and entertainment purposes.
## Credits
Inspired by the WarioWare series by Nintendo.
+46
View File
@@ -0,0 +1,46 @@
{
"wario": [
{
"id": "spot_the_bright_one",
"difficulty": 1
},
{
"id": "light_it_up",
"difficulty": 1
},
{
"id": "match_the_mood",
"difficulty": 1
},
{
"id": "hot_or_cold",
"difficulty": 1
},
{
"id": "truth_or_lie",
"difficulty": 1
}
],
"ninevolt": [
{
"id": "insert_cartridge",
"difficulty": 1
},
{
"id": "jump_plumber",
"difficulty": 1
},
{
"id": "catch_the_disk",
"difficulty": 1
},
{
"id": "retro_or_modern",
"difficulty": 1
},
{
"id": "pixel_paint",
"difficulty": 1
}
]
}
View File
+57
View File
@@ -0,0 +1,57 @@
import pygame
from engine.scene import Scene
from settings import *
from engine.utils import scale_to_fit
class Menu(Scene):
def __init__(self, manager):
super().__init__(manager)
self.wario_button = pygame.Rect(SCREEN_WIDTH // 4, SCREEN_HEIGHT // 2 - 50, SCREEN_WIDTH // 2, 100)
self.ninevolt_button = pygame.Rect(SCREEN_WIDTH // 4, SCREEN_HEIGHT // 2 + 100, SCREEN_WIDTH // 2, 100)
self.logo_image = None
self.logo_rect = None
try:
logo_path = resource_path("Assets/images/title/logo.png")
logo_image_raw = pygame.image.load(logo_path)
# Define a bounding box for the logo
logo_bounds = pygame.Rect(0, 0, SCREEN_WIDTH * 0.75, SCREEN_HEIGHT * 0.4)
logo_bounds.center = (SCREEN_WIDTH // 2, SCREEN_HEIGHT // 4)
self.logo_image, self.logo_rect = scale_to_fit(logo_image_raw, logo_bounds)
except (pygame.error, FileNotFoundError) as e:
print(f"Error loading title screen logo: {e}")
self.logo_image = None
def update(self, dt):
mouse_pos = pygame.mouse.get_pos()
mouse_pressed = pygame.mouse.get_pressed()
if mouse_pressed[0]:
if self.wario_button.collidepoint(mouse_pos):
self.manager.start_game('wario')
elif self.ninevolt_button.collidepoint(mouse_pos):
self.manager.start_game('ninevolt')
def draw(self, screen):
screen.fill(BLACK)
# Draw logo
if self.logo_image and self.logo_rect:
screen.blit(self.logo_image, self.logo_rect)
# Wario button
pygame.draw.rect(screen, BLACK, self.wario_button)
font = pygame.font.Font(None, 50)
text = font.render("Wario's Opposites Attack!", True, WHITE)
text_rect = text.get_rect(center=self.wario_button.center)
screen.blit(text, text_rect)
# 9-Volt button
pygame.draw.rect(screen, BLACK, self.ninevolt_button)
font = pygame.font.Font(None, 50)
text = font.render("9-Volt's Retro Reload", True, WHITE)
text_rect = text.get_rect(center=self.ninevolt_button.center)
screen.blit(text, text_rect)
+24
View File
@@ -0,0 +1,24 @@
# engine/prompt_scene.py
import pygame
from engine.scene import Scene
from settings import *
class PromptScene(Scene):
def __init__(self, manager, prompt, next_scene):
super().__init__(manager)
self.prompt = prompt
self.next_scene = next_scene
self.timer = 2.0 # 2 seconds
def update(self, dt):
self.timer -= dt
if self.timer <= 0:
self.manager.current_scene = self.next_scene
self.manager.current_scene.start()
def draw(self, screen):
screen.fill(BLACK)
font = pygame.font.Font(None, 80)
text = font.render(self.prompt, True, WHITE)
text_rect = text.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2))
screen.blit(text, text_rect)
+32
View File
@@ -0,0 +1,32 @@
import pygame
from engine.scene import Scene
from settings import WHITE, GREEN, RED, SCREEN_WIDTH, SCREEN_HEIGHT, FONT_LG, WARIO_BG_COLOR, NINEVOLT_BG_COLOR
class ResultScene(Scene):
def __init__(self, manager, success, game_type=None):
super().__init__(manager)
self.success = success
self.game_type = game_type
self.display_time = 1.5 # Display for 1.5 seconds
self.timer = self.display_time
def update(self, dt, time_left=None):
self.timer -= dt
if self.timer <= 0:
# Transition back to GameManager to load next microgame
self.manager.load_next_microgame()
def draw(self, screen):
if self.game_type == 'wario':
screen.fill(WARIO_BG_COLOR)
elif self.game_type == 'ninevolt':
screen.fill(NINEVOLT_BG_COLOR)
else:
screen.fill(BLACK) # Default background
if self.success:
text = FONT_LG.render("Success!", True, GREEN)
else:
text = FONT_LG.render("Failure!", True, RED)
text_rect = text.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2))
screen.blit(text, text_rect)
+25
View File
@@ -0,0 +1,25 @@
# engine/scene.py
class Scene:
def __init__(self, manager):
self.manager = manager
def start(self):
"""Called when the scene starts."""
pass
def update(self, dt, time_left=None):
"""Called every frame."""
pass
def draw(self, screen):
"""Draw the scene on the screen."""
pass
def end(self, success):
"""Called when the scene ends."""
pass
def handle_event(self, event):
"""Called for each pygame event."""
pass
+19
View File
@@ -0,0 +1,19 @@
import pygame
def init_mixer():
pygame.mixer.init()
def load_music(path):
try:
pygame.mixer.music.load(path)
except pygame.error as e:
print(f"Error loading music {path}: {e}")
def play_music(loops=-1):
pygame.mixer.music.play(loops)
def stop_music():
pygame.mixer.music.stop()
def fadeout_music(time):
pygame.mixer.music.fadeout(time)
+40
View File
@@ -0,0 +1,40 @@
# engine/transitions.py
import pygame
from settings import *
class Transition:
def __init__(self, fade_speed):
self.fade_speed = fade_speed
self.fade_surface = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT))
self.fade_surface.fill(BLACK)
self.alpha = 0
self.fading_out = False
self.fading_in = False
def fade_out(self):
self.fading_out = True
self.alpha = 0
def fade_in(self):
self.fading_in = True
self.alpha = 255
def update(self, dt):
if self.fading_out:
self.alpha += self.fade_speed * dt
if self.alpha >= 255:
self.alpha = 255
self.fading_out = False
return True # Fade out complete
elif self.fading_in:
self.alpha -= self.fade_speed * dt
if self.alpha <= 0:
self.alpha = 0
self.fading_in = False
return True # Fade in complete
return False
def draw(self, screen):
if self.alpha > 0:
self.fade_surface.set_alpha(self.alpha)
screen.blit(self.fade_surface, (0, 0))
+10
View File
@@ -0,0 +1,10 @@
import pygame
def scale_to_fit(image, rect):
"""Scales a pygame surface to fit inside a rect, preserving aspect ratio."""
img_rect = image.get_rect()
scale_factor = min(rect.width / img_rect.width, rect.height / img_rect.height)
new_size = (int(img_rect.width * scale_factor), int(img_rect.height * scale_factor))
scaled_image = pygame.transform.smoothscale(image, new_size)
new_rect = scaled_image.get_rect(center=rect.center)
return scaled_image, new_rect
+213
View File
@@ -0,0 +1,213 @@
# game_manager.py
import pygame
import json
import importlib
import random
from engine.scene import Scene
from engine.menu import Menu
from engine.transitions import Transition
from settings import *
from engine.sound import init_mixer, load_music, play_music, stop_music
from engine.prompt_scene import PromptScene
from engine.result_scene import ResultScene
import pygame
import random
import json
from engine.sound import play_music, stop_music, fadeout_music
from settings import FONT_MD, FONT_LG, WHITE, BLACK, SCREEN_WIDTH, SCREEN_HEIGHT, resource_path
# Import all microgame classes
from microgames.wario.spot_the_bright_one import SpotTheBrightOne
from microgames.wario.light_it_up import LightItUp
from microgames.wario.match_the_mood import MatchTheMood
from microgames.wario.hot_or_cold import HotOrCold
from microgames.wario.truth_or_lie import TruthOrLie
from microgames.ninevolt.insert_cartridge import InsertCartridge
from microgames.ninevolt.jump_plumber import JumpPlumber
from microgames.ninevolt.catch_the_disk import CatchTheDisk
from microgames.ninevolt.pixel_paint import PixelPaint
from microgames.ninevolt.retro_or_modern import RetroOrModern
MICROGAME_CLASSES = {
'microgames.wario.spot_the_bright_one': SpotTheBrightOne,
'microgames.wario.light_it_up': LightItUp,
'microgames.wario.match_the_mood': MatchTheMood,
'microgames.wario.hot_or_cold': HotOrCold,
'microgames.wario.truth_or_lie': TruthOrLie,
'microgames.ninevolt.insert_cartridge': InsertCartridge,
'microgames.ninevolt.jump_plumber': JumpPlumber,
'microgames.ninevolt.catch_the_disk': CatchTheDisk,
'microgames.ninevolt.pixel_paint': PixelPaint,
'microgames.ninevolt.retro_or_modern': RetroOrModern,
}
class GameManager:
def __init__(self, screen, game_type=None):
self.screen = screen
self.game_type = game_type # 'wario' or 'ninevolt'
self.current_microgame = None
self.microgame_start_time = 0
self.score = 0
self.lives = 3
self.game_over = False
self.speed_level = 0 # 0, 1, 2 for normal, speedup 1, speedup 2
self.max_speed_levels = 2
self.initial_microgame_time_limit = 4000 # 4 seconds
self.current_microgame_time_limit = self.initial_microgame_time_limit
self.current_music_type = None # Track currently playing music type
self.load_microgame_list()
self.load_next_microgame()
@property
def speed(self):
return 1 + (self.speed_level * 0.2) # Increase speed by 20% per level
def load_microgame_list(self):
with open(resource_path('data/microgame_list.json'), 'r') as f:
self.microgame_list = json.load(f)
def load_next_microgame(self):
if self.current_microgame:
# self.current_microgame.unload() # No unload method in Scene class
pass
available_microgames = []
for module_name in MICROGAME_CLASSES.keys():
if self.game_type == 'wario' and 'wario' in module_name:
available_microgames.append(module_name)
elif self.game_type == 'ninevolt' and 'ninevolt' in module_name:
available_microgames.append(module_name)
elif self.game_type is None: # If no specific game_type, allow all
available_microgames.append(module_name)
if not available_microgames:
print(f"No microgames found for game_type: {self.game_type}. Loading all microgames.")
available_microgames = list(MICROGAME_CLASSES.keys())
microgame_module_name = random.choice(available_microgames)
MicrogameClass = MICROGAME_CLASSES[microgame_module_name]
self.current_microgame = MicrogameClass(self)
self.current_microgame.start()
self.microgame_start_time = pygame.time.get_ticks()
# Dynamically set time limit based on microgame's timer and end_timer
# Add a small buffer (e.g., 500ms) to ensure GameManager doesn't time out prematurely
self.current_microgame_time_limit = int((self.current_microgame.timer + 0.5) * 1000)
# Load and play music based on microgame type
current_microgame_type = 'wario' if 'wario' in microgame_module_name else 'ninevolt'
if current_microgame_type != self.current_music_type:
if current_microgame_type == 'wario':
load_music(resource_path("Assets/music/wario.ogg"))
elif current_microgame_type == 'ninevolt':
load_music(resource_path("Assets/music/9volt.ogg"))
play_music()
self.current_music_type = current_microgame_type
def update(self, dt):
if self.game_over:
return
current_time = pygame.time.get_ticks()
time_left = max(0, self.current_microgame_time_limit - (current_time - self.microgame_start_time))
self.current_microgame.update(dt, time_left)
def draw(self, screen):
if self.game_over:
self._draw_game_over()
else:
self.current_microgame.draw(self.screen)
self._draw_ui(screen)
def handle_event(self, event):
if self.game_over:
return
self.current_microgame.handle_event(event)
def microgame_finished(self, success, game_type=None):
if self.game_over:
return
if success:
self.score += 1
if self.score % 5 == 0 and self.speed_level < self.max_speed_levels:
self._update_game_speed()
else:
self.lives -= 1
self._check_game_over()
if not self.game_over:
self.current_microgame = ResultScene(self, success, game_type=game_type)
def _update_game_speed(self):
self.speed_level += 1
# The time limit is recalculated in load_next_microgame
def _check_game_over(self):
if self.lives <= 0:
self.game_over = True
fadeout_music(2000) # Fade out music over 2 seconds
def _draw_ui(self, screen):
score_text = FONT_MD.render(f"Score: {self.score}", True, WHITE)
lives_text = FONT_MD.render(f"Lives: {self.lives}", True, WHITE)
screen.blit(score_text, (10, 10))
screen.blit(lives_text, (SCREEN_WIDTH - lives_text.get_width() - 10, 10))
# Draw microgame prompt
if self.current_microgame and hasattr(self.current_microgame, 'prompt'):
prompt_text = FONT_LG.render(self.current_microgame.prompt, True, WHITE)
prompt_rect = prompt_text.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 8))
screen.blit(prompt_text, prompt_rect)
# Draw time bar
time_left = max(0, self.current_microgame_time_limit - (pygame.time.get_ticks() - self.microgame_start_time))
bar_width = int((time_left / self.current_microgame_time_limit) * SCREEN_WIDTH)
pygame.draw.rect(screen, WHITE, (0, SCREEN_HEIGHT - 20, bar_width, 10))
def _draw_game_over(self):
game_over_text = FONT_LG.render("GAME OVER", True, WHITE)
restart_text = FONT_MD.render("Press R to Restart", True, WHITE)
self.screen.blit(game_over_text, (SCREEN_WIDTH // 2 - game_over_text.get_width() // 2, SCREEN_HEIGHT // 2 - game_over_text.get_height() // 2))
self.screen.blit(restart_text, (SCREEN_WIDTH // 2 - restart_text.get_width() // 2, SCREEN_HEIGHT // 2 + restart_text.get_height()))
class GameOverScene(Scene):
def __init__(self, manager):
super().__init__(manager)
def update(self, dt):
pass # No continuous update needed for game over screen
def handle_event(self, event):
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1: # Left click
stop_music() # Stop music when returning to menu
self.manager.game_state = "menu"
self.manager.current_scene = Menu(self.manager)
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_r:
stop_music() # Stop music when returning to menu
self.manager.game_state = "menu"
self.manager.current_scene = Menu(self.manager)
def draw(self, screen):
screen.fill(BLACK)
font = pygame.font.Font(None, 100)
text = font.render("Game Over", True, RED)
text_rect = text.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 3))
screen.blit(text, text_rect)
font = pygame.font.Font(None, 50)
score_text = font.render(f"Score: {self.manager.score}", True, WHITE)
score_rect = score_text.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2))
screen.blit(score_text, score_rect)
font = pygame.font.Font(None, 30)
continue_text = font.render("Press R to Restart or Click to continue", True, WHITE)
continue_rect = continue_text.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT * 2 // 3))
screen.blit(continue_text, continue_rect)
""
+48
View File
@@ -0,0 +1,48 @@
import pygame
from settings import *
from game_manager import GameManager
from engine.sound import init_mixer, play_music, load_music
from engine.menu import Menu
class Game:
def __init__(self):
pygame.init()
init_mixer()
self.screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("WarioWare: Black & Wah!")
# Set window icon
try:
icon_image = pygame.image.load(resource_path("Assets/images/title/logo.png"))
pygame.display.set_icon(icon_image)
except (pygame.error, FileNotFoundError) as e:
print(f"Error loading window icon: {e}")
self.current_scene = Menu(self) # Start with the menu
self.running = True
self.clock = pygame.time.Clock()
def start_game(self, game_type=None):
self.current_scene = GameManager(self.screen, game_type)
def run(self):
while self.running:
dt = self.clock.tick(FPS) / 1000.0
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.running = False
if hasattr(self.current_scene, 'handle_event'):
self.current_scene.handle_event(event)
self.current_scene.update(dt)
self.screen.fill(BLACK)
self.current_scene.draw(self.screen)
pygame.display.flip()
pygame.quit()
if __name__ == '__main__':
game = Game()
game.run()
View File
View File
+116
View File
@@ -0,0 +1,116 @@
# microgames/ninevolt/catch_the_disk.py
import pygame
import random
from engine.scene import Scene
from settings import *
from engine.utils import scale_to_fit
class CatchTheDisk(Scene):
def __init__(self, manager):
super().__init__(manager)
self.prompt = "Catch the disk!"
self.disk_rect = pygame.Rect(0, 0, 80, 30) # Placeholder
self.character_rect = pygame.Rect(0, 0, 100, 50) # Placeholder
self.disk_speed = 400 * self.manager.speed
self.timer = 3.0 / self.manager.speed
self.state = "playing"
self.end_timer = 2.0
self.disk_image_raw = None
self.character_image_raw = None
# Load raw images and define display rects
try:
self.disk_image_raw = pygame.image.load(resource_path("Assets/images/9volt/disk.png")).convert_alpha()
self.character_image_raw = pygame.image.load(resource_path("Assets/images/9volt/character.png")).convert_alpha()
# Define display rects based on screen proportions
disk_height = SCREEN_HEIGHT * 0.05
self.disk_display_rect = pygame.Rect(0, 0, 100, disk_height) # Added placeholder width
character_height = SCREEN_HEIGHT * 0.1
self.character_display_rect = pygame.Rect(0, 0, 100, character_height) # Added placeholder width
except (pygame.error, FileNotFoundError) as e:
print(f"Error loading images for CatchTheDisk: {e}")
def start(self):
self.timer = 3.0 / self.manager.speed
self.disk_speed = 400 * self.manager.speed
self.state = "playing"
self.end_timer = 2.0
# Scale and position disk
if self.disk_image_raw:
scaled_img, scaled_rect = scale_to_fit(self.disk_image_raw, self.disk_display_rect)
self.disk_rect = scaled_rect
self.disk_rect.midbottom = (random.randint(50, SCREEN_WIDTH - 50), 0)
else:
self.disk_rect.midbottom = (random.randint(50, SCREEN_WIDTH - 50), 0)
# Scale and position character
if self.character_image_raw:
scaled_img, scaled_rect = scale_to_fit(self.character_image_raw, self.character_display_rect)
self.character_rect = scaled_rect
self.character_rect.midbottom = (SCREEN_WIDTH // 2, SCREEN_HEIGHT - 10)
else:
self.character_rect.midbottom = (SCREEN_WIDTH // 2, SCREEN_HEIGHT - 10)
def update(self, dt, time_left=None):
if self.state == "playing":
if time_left is not None and time_left <= 0:
self.end(False) # Failure due to timeout
# Move disk
self.disk_rect.y += self.disk_speed * dt
# Move character with mouse
mouse_x, _ = pygame.mouse.get_pos()
self.character_rect.centerx = mouse_x
# Collision check
if self.character_rect.colliderect(self.disk_rect):
self.end(True)
# Missed disk
if self.disk_rect.top > SCREEN_HEIGHT:
self.end(False)
elif self.state == "success" or self.state == "failure":
self.end_timer -= dt
if self.end_timer <= 0:
self.manager.microgame_finished(self.state == "success", 'ninevolt')
def draw(self, screen):
screen.fill(NINEVOLT_BG_COLOR)
if self.state == "playing":
# Draw disk
if self.disk_image_raw:
scaled_img, scaled_rect = scale_to_fit(self.disk_image_raw, self.disk_rect) # Use disk_rect for target
screen.blit(scaled_img, scaled_rect)
else:
pygame.draw.ellipse(screen, (200, 200, 0), self.disk_rect)
# Draw character
if self.character_image_raw:
scaled_img, scaled_rect = scale_to_fit(self.character_image_raw, self.character_rect) # Use character_rect for target
screen.blit(scaled_img, scaled_rect)
else:
pygame.draw.rect(screen, (0, 200, 200), self.character_rect)
# Timer is now drawn by GameManager
pass
else:
font = pygame.font.Font(None, 100)
if self.state == "success":
text = font.render("Success!", True, GREEN)
else:
text = font.render("Failure!", True, RED)
text_rect = text.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2))
screen.blit(text, text_rect)
def end(self, success):
if success:
self.state = "success"
else:
self.state = "failure"
+85
View File
@@ -0,0 +1,85 @@
# microgames/ninevolt/insert_cartridge.py
import pygame
from engine.scene import Scene
from settings import *
from engine.utils import scale_to_fit
class InsertCartridge(Scene):
def __init__(self, manager):
super().__init__(manager)
self.prompt = "Insert the cartridge!"
self.cartridge_rect = pygame.Rect(0, 0, 100, 100)
self.console_rect = pygame.Rect(0, 0, 200, 50)
self.cartridge_dragging = False
self.timer = 4.0 / self.manager.speed
self.state = "playing"
self.end_timer = 2.0
self.cartridge_image_raw = None
self.console_image_raw = None
# Load raw images
try:
self.cartridge_image_raw = pygame.image.load(resource_path("Assets/images/9volt/cartridge.png")).convert_alpha()
self.console_image_raw = pygame.image.load(resource_path("Assets/images/9volt/console.png")).convert_alpha()
except (pygame.error, FileNotFoundError) as e:
print(f"Error loading images for InsertCartridge: {e}")
def start(self):
self.timer = 4.0 / self.manager.speed
self.state = "playing"
self.end_timer = 2.0
self.cartridge_rect.center = (SCREEN_WIDTH // 2, SCREEN_HEIGHT // 4)
self.console_rect.center = (SCREEN_WIDTH // 2, SCREEN_HEIGHT * 3 // 4)
self.cartridge_dragging = False
def update(self, dt, time_left=None):
if self.state == "playing":
if time_left is not None and time_left <= 0:
self.end(False) # Failure due to timeout
mouse_pos = pygame.mouse.get_pos()
mouse_pressed = pygame.mouse.get_pressed()
if mouse_pressed[0]:
if self.cartridge_rect.collidepoint(mouse_pos):
self.cartridge_dragging = True
else:
self.cartridge_dragging = False
if self.cartridge_dragging:
self.cartridge_rect.center = mouse_pos
if self.console_rect.colliderect(self.cartridge_rect):
self.end(True) # Success
elif self.state == "success" or self.state == "failure":
self.end_timer -= dt
if self.end_timer <= 0:
self.manager.microgame_finished(self.state == "success", 'ninevolt')
def draw(self, screen):
screen.fill(NINEVOLT_BG_COLOR)
if self.state == "playing":
# Draw console
if self.console_image_raw:
scaled_img, scaled_rect = scale_to_fit(self.console_image_raw, self.console_rect)
screen.blit(scaled_img, scaled_rect)
else:
pygame.draw.rect(screen, (50, 50, 50), self.console_rect)
# Draw cartridge
if self.cartridge_image_raw:
scaled_img, scaled_rect = scale_to_fit(self.cartridge_image_raw, self.cartridge_rect)
screen.blit(scaled_img, scaled_rect)
else:
pygame.draw.rect(screen, (150, 150, 150), self.cartridge_rect)
# Timer is now drawn by GameManager
pass
def end(self, success):
if success:
self.state = "success"
else:
self.state = "failure"
+102
View File
@@ -0,0 +1,102 @@
# microgames/ninevolt/jump_plumber.py
import pygame
from engine.scene import Scene
from settings import *
from engine.utils import scale_to_fit
class JumpPlumber(Scene):
def __init__(self, manager):
super().__init__(manager)
self.prompt = "Jump!"
self.plumber_rect = pygame.Rect(0, 0, 50, 100)
self.obstacle_rect = pygame.Rect(0, 0, 50, 50)
self.plumber_y_velocity = 0
self.gravity = 2000
self.jump_strength = -800
self.is_jumping = False
self.timer = 5.0 / self.manager.speed
self.state = "playing"
self.end_timer = 2.0
self.plumber_image_raw = None
self.obstacle_image_raw = None
# Load raw images
try:
self.plumber_image_raw = pygame.image.load(resource_path("Assets/images/9volt/plumber.png")).convert_alpha()
self.obstacle_image_raw = pygame.image.load(resource_path("Assets/images/9volt/obstacle.png")).convert_alpha()
except (pygame.error, FileNotFoundError) as e:
print(f"Error loading images for JumpPlumber: {e}")
def start(self):
self.timer = 5.0 / self.manager.speed
self.state = "playing"
self.end_timer = 2.0
self.plumber_rect.midbottom = (SCREEN_WIDTH // 4, SCREEN_HEIGHT - 50)
self.obstacle_rect.midbottom = (SCREEN_WIDTH + 100, SCREEN_HEIGHT - 50)
self.plumber_y_velocity = 0
self.is_jumping = False
def update(self, dt, time_left=None):
if self.state == "playing":
if time_left is not None and time_left <= 0:
self.end(False) # Failure due to timeout
# Move obstacle
self.obstacle_rect.x -= 400 * dt * self.manager.speed
# Plumber jump
mouse_pressed = pygame.mouse.get_pressed()
if mouse_pressed[0] and not self.is_jumping:
self.is_jumping = True
self.plumber_y_velocity = self.jump_strength
if self.is_jumping:
self.plumber_y_velocity += self.gravity * dt
self.plumber_rect.y += self.plumber_y_velocity * dt
if self.plumber_rect.bottom >= SCREEN_HEIGHT - 50:
self.plumber_rect.bottom = SCREEN_HEIGHT - 50
self.is_jumping = False
# Collision check
if self.plumber_rect.colliderect(self.obstacle_rect):
self.end(False) # Failure
# Success condition
if self.obstacle_rect.right < self.plumber_rect.left:
self.end(True) # Success
elif self.state == "success" or self.state == "failure":
self.end_timer -= dt
if self.end_timer <= 0:
self.manager.microgame_finished(self.state == "success", 'ninevolt')
def draw(self, screen):
screen.fill(NINEVOLT_BG_COLOR)
if self.state == "playing":
# Draw ground
pygame.draw.rect(screen, (100, 100, 100), (0, SCREEN_HEIGHT - 50, SCREEN_WIDTH, 50))
# Draw plumber
if self.plumber_image_raw:
scaled_img, scaled_rect = scale_to_fit(self.plumber_image_raw, self.plumber_rect)
screen.blit(scaled_img, scaled_rect)
else:
pygame.draw.rect(screen, (255, 0, 0), self.plumber_rect)
# Draw obstacle
if self.obstacle_image_raw:
scaled_img, scaled_rect = scale_to_fit(self.obstacle_image_raw, self.obstacle_rect)
screen.blit(scaled_img, scaled_rect)
else:
pygame.draw.rect(screen, (0, 255, 0), self.obstacle_rect)
# Timer is now drawn by GameManager
pass
def end(self, success):
if success:
self.state = "success"
else:
self.state = "failure"
+84
View File
@@ -0,0 +1,84 @@
# microgames/ninevolt/pixel_paint.py
import pygame
from engine.scene import Scene
from settings import *
class PixelPaint(Scene):
def __init__(self, manager):
super().__init__(manager)
self.prompt = "Paint the pixels!"
self.grid_size = 8
self.pixels = []
self.pixels_to_paint = 0
self.timer = 5.0 / self.manager.speed
self.state = "playing"
self.end_timer = 2.0
def start(self):
self.timer = 5.0 / self.manager.speed
self.state = "playing"
self.end_timer = 2.0
self.pixels = []
self.pixels_to_paint = 0
pixel_size = 30
grid_width = self.grid_size * pixel_size
grid_height = self.grid_size * pixel_size
start_x = (SCREEN_WIDTH - grid_width) // 2
start_y = (SCREEN_HEIGHT - grid_height) // 2
for i in range(self.grid_size * self.grid_size):
row = i // self.grid_size
col = i % self.grid_size
x = start_x + col * pixel_size
y = start_y + row * pixel_size
rect = pygame.Rect(x, y, pixel_size, pixel_size)
# Simple heart shape
if (row == 1 and col in [2, 5]) or \
(row == 2 and col in [1, 3, 4, 6]) or \
(row == 3 and col in [1, 2, 3, 4, 5, 6]) or \
(row == 4 and col in [2, 3, 4, 5]) or \
(row == 5 and col in [3, 4]):
self.pixels.append({"rect": rect, "painted": False})
self.pixels_to_paint += 1
else:
self.pixels.append({"rect": rect, "painted": True}) # Already painted
def update(self, dt, time_left=None):
if self.state == "playing":
if time_left is not None and time_left <= 0:
self.end(False) # Failure due to timeout
mouse_pos = pygame.mouse.get_pos()
mouse_pressed = pygame.mouse.get_pressed()
if mouse_pressed[0]:
for pixel in self.pixels:
if not pixel["painted"] and pixel["rect"].collidepoint(mouse_pos):
pixel["painted"] = True
self.pixels_to_paint -= 1
if self.pixels_to_paint == 0:
self.end(True)
elif self.state == "success" or self.state == "failure":
self.end_timer -= dt
if self.end_timer <= 0:
self.manager.microgame_finished(self.state == "success", 'ninevolt')
def draw(self, screen):
screen.fill(NINEVOLT_BG_COLOR)
if self.state == "playing":
# Draw pixels
for pixel in self.pixels:
color = RED if pixel["painted"] else (50, 50, 50)
pygame.draw.rect(screen, color, pixel["rect"])
pygame.draw.rect(screen, BLACK, pixel["rect"], 1)
# Timer is now drawn by GameManager
pass
def end(self, success):
if success:
self.state = "success"
else:
self.state = "failure"
+105
View File
@@ -0,0 +1,105 @@
# microgames/ninevolt/retro_or_modern.py
import pygame
import random
from engine.scene import Scene
from settings import *
class RetroOrModern(Scene):
def __init__(self, manager):
super().__init__(manager)
self.prompt = "Retro or modern?"
self.games = [
("Super Mario Bros. (NES, 1985)", "retro"),
("The Legend of Zelda (NES, 1986)", "retro"),
("Metroid (NES, 1986)", "retro"),
("Donkey Kong (Arcade/NES, 1981/1983)", "retro"),
("Super Mario World (SNES, 1990)", "retro"),
("The Legend of Zelda: A Link to the Past (SNES, 1991)", "retro"),
("Super Metroid (SNES, 1994)", "retro"),
("Kirby's Adventure (NES, 1993)", "retro"),
("Pokémon Red and Blue (Game Boy, 1996)", "retro"),
("Mario Kart 64 (Nintendo 64, 1996)", "retro"),
("The Legend of Zelda: Ocarina of Time (Nintendo 64, 1998)", "retro"),
("Super Smash Bros. (Nintendo 64, 1999)", "retro"),
("Luigi's Mansion (GameCube, 2001)", "retro"),
("Metroid Prime (GameCube, 2002)", "retro"),
("The Legend of Zelda: The Wind Waker (GameCube, 2002)", "retro"),
("The Legend of Zelda: Breath of the Wild (Switch, 2017)", "modern"),
("Super Mario Odyssey (Switch, 2017)", "modern"),
("Animal Crossing: New Horizons (Switch, 2020)", "modern"),
("Splatoon 3 (Switch, 2022)", "modern"),
("Super Smash Bros. Ultimate (Switch, 2018)", "modern"),
("Metroid Dread (Switch, 2021)", "modern"),
("The Legend of Zelda: Tears of the Kingdom (Switch, 2023)", "modern"),
("Mario Kart 8 Deluxe (Switch, 2017)", "modern"),
("Pokémon Scarlet and Violet (Switch, 2022)", "modern"),
("Super Mario Bros. Wonder (Switch, 2023)", "modern"),
("Pikmin 4 (Switch, 2023)", "modern"),
("Luigi's Mansion 3 (Switch, 2019)", "modern"),
]
self.game_title = None
self.answer = None
self.retro_button = pygame.Rect(0, 0, 150, 80)
self.modern_button = pygame.Rect(0, 0, 150, 80)
self.timer = 3.0 / self.manager.speed
self.state = "playing"
self.end_timer = 2.0
def start(self):
self.timer = 3.0 / self.manager.speed
self.state = "playing"
self.end_timer = 2.0
self.game_title, self.answer = random.choice(self.games)
self.retro_button.midtop = (SCREEN_WIDTH // 3, SCREEN_HEIGHT * 2 // 3)
self.modern_button.midtop = (SCREEN_WIDTH * 2 // 3, SCREEN_HEIGHT * 2 // 3)
def update(self, dt, time_left=None):
if self.state == "playing":
if time_left is not None and time_left <= 0:
self.end(False) # Failure due to timeout
mouse_pos = pygame.mouse.get_pos()
mouse_pressed = pygame.mouse.get_pressed()
if mouse_pressed[0]:
if self.retro_button.collidepoint(mouse_pos):
self.end(self.answer == "retro")
elif self.modern_button.collidepoint(mouse_pos):
self.end(self.answer == "modern")
elif self.state == "success" or self.state == "failure":
self.end_timer -= dt
if self.end_timer <= 0:
self.manager.microgame_finished(self.state == "success", 'ninevolt')
def draw(self, screen):
screen.fill(NINEVOLT_BG_COLOR)
if self.state == "playing":
# Draw game title
font_size = 60
if len(self.game_title) > 30: # Adjust threshold as needed
font_size = 40
font = pygame.font.Font(None, font_size)
text = font.render(self.game_title, True, WHITE)
text_rect = text.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 3))
screen.blit(text, text_rect)
# Draw buttons
pygame.draw.rect(screen, BLACK, self.retro_button)
pygame.draw.rect(screen, BLACK, self.modern_button)
font = pygame.font.Font(None, 50)
retro_text = font.render("Retro", True, WHITE)
modern_text = font.render("Modern", True, WHITE)
retro_rect = retro_text.get_rect(center=self.retro_button.center)
modern_rect = modern_text.get_rect(center=self.modern_button.center)
screen.blit(retro_text, retro_rect)
screen.blit(modern_text, modern_rect)
# Timer is now drawn by GameManager
pass
def end(self, success):
if success:
self.state = "success"
else:
self.state = "failure"
View File
+145
View File
@@ -0,0 +1,145 @@
# microgames/wario/hot_or_cold.py
import pygame
import random
from engine.scene import Scene
from settings import *
from engine.utils import scale_to_fit
class HotOrCold(Scene):
def __init__(self, manager):
super().__init__(manager)
self.prompt = "Hot or cold?"
self.character_state = None
self.character_rect = pygame.Rect(0, 0, 100, 200) # Initial placeholder size
self.items = []
self.dragging_item = None
self.timer = 4.0 / self.manager.speed
self.state = "playing"
self.end_timer = 2.0
self.character_images_raw = {}
self.item_images_raw = {}
# Load raw images and get their original sizes
try:
# Character images
self.character_images_raw["cold"] = pygame.image.load(resource_path("Assets/images/wario/shivering_char.png")).convert_alpha()
self.character_images_raw["hot"] = pygame.image.load(resource_path("Assets/images/wario/sweating_char.png")).convert_alpha()
# Determine character display size based on screen height
char_height = SCREEN_HEIGHT * 0.3
self.character_display_rect = pygame.Rect(0, 0, 100, char_height) # Height-driven scaling, added placeholder width
# Item images
item_paths = {
"blanket": resource_path("Assets/images/wario/blanket.png"),
"fan": resource_path("Assets/images/wario/fan.png"),
"book": resource_path("Assets/images/wario/book.png"),
"plant": resource_path("Assets/images/wario/plant.png")
}
self.item_display_rects = {}
item_height = SCREEN_HEIGHT * 0.15
for name, path in item_paths.items():
self.item_images_raw[name] = pygame.image.load(path).convert_alpha()
self.item_display_rects[name] = pygame.Rect(0, 0, 100, item_height) # Height-driven scaling, added placeholder width
except (pygame.error, FileNotFoundError) as e:
print(f"Error loading images for HotOrCold: {e}")
self.character_images_raw = {}
self.item_images_raw = {}
def start(self):
self.timer = 4.0 / self.manager.speed
self.state = "playing"
self.end_timer = 2.0
self.character_state = random.choice(["hot", "cold"])
# Position character based on its display rect
if self.character_images_raw:
# Scale to fit the display rect to get actual dimensions
scaled_img, scaled_rect = scale_to_fit(self.character_images_raw[self.character_state], self.character_display_rect)
self.character_rect = scaled_rect
self.character_rect.center = (SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2)
else:
self.character_rect.center = (SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2)
self.dragging_item = None
self.items = []
item_names = ["blanket", "fan", "book", "plant"]
random.shuffle(item_names)
for i, name in enumerate(item_names):
item_rect = pygame.Rect(0, 0, 0, 0) # Placeholder
if name in self.item_images_raw:
scaled_img, scaled_rect = scale_to_fit(self.item_images_raw[name], self.item_display_rects[name])
item_rect = scaled_rect
else:
item_rect = pygame.Rect(0, 0, 100, 100) # Fallback size
x = (SCREEN_WIDTH // (len(item_names) + 1)) * (i + 1)
item_rect.midbottom = (x, SCREEN_HEIGHT - 20)
self.items.append({"name": name, "rect": item_rect, "color": BLACK})
def update(self, dt, time_left=None):
if self.state == "playing":
if time_left is not None and time_left <= 0:
self.end(False) # Failure due to timeout
mouse_pos = pygame.mouse.get_pos()
mouse_pressed = pygame.mouse.get_pressed()
if mouse_pressed[0]:
if self.dragging_item is None:
for item in self.items:
if item["rect"].collidepoint(mouse_pos):
self.dragging_item = item
else:
if self.dragging_item is not None:
if self.character_rect.colliderect(self.dragging_item["rect"]):
if self.character_state == "cold" and self.dragging_item["name"] == "blanket":
self.end(True)
elif self.character_state == "hot" and self.dragging_item["name"] == "fan":
self.end(True)
else:
self.end(False)
self.dragging_item = None
if self.dragging_item is not None:
self.dragging_item["rect"].center = mouse_pos
elif self.state == "success" or self.state == "failure":
self.end_timer -= dt
if self.end_timer <= 0:
self.manager.microgame_finished(self.state == "success", 'wario')
def draw(self, screen):
screen.fill(WARIO_BG_COLOR)
if self.state == "playing":
# Draw character
char_image_raw = self.character_images_raw.get(self.character_state)
if char_image_raw:
scaled_img, scaled_rect = scale_to_fit(char_image_raw, self.character_rect)
screen.blit(scaled_img, scaled_rect)
else:
color = BLUE if self.character_state == "cold" else RED
pygame.draw.rect(screen, color, self.character_rect)
# Draw items
for item in self.items:
item_image_raw = self.item_images_raw.get(item["name"])
if item_image_raw:
scaled_img, scaled_rect = scale_to_fit(item_image_raw, item["rect"])
screen.blit(scaled_img, scaled_rect)
else:
pygame.draw.rect(screen, item["color"], item["rect"])
# Timer is now drawn by GameManager
# Removed old success/failure drawing logic
def end(self, success):
if success:
self.state = "success"
else:
self.state = "failure"
+102
View File
@@ -0,0 +1,102 @@
# microgames/wario/light_it_up.py
import pygame
from engine.scene import Scene
from settings import *
from engine.utils import scale_to_fit
class LightItUp(Scene):
def __init__(self, manager):
super().__init__(manager)
self.prompt = "Light it up!"
self.light_bulb_rect = pygame.Rect(0, 0, 100, 100)
self.chain_rect = pygame.Rect(0, 0, 50, 200) # Adjusted width for better scaling
self.light_on = False
self.chain_dragging = False
self.initial_chain_y = 0
self.timer = 3.0 / self.manager.speed # Keep for GameManager time limit calculation
self.state = "playing"
self.end_timer = 2.0
# Load raw images
self.light_off_image_raw = None
self.light_on_image_raw = None
self.chain_image_raw = None
try:
self.light_off_image_raw = pygame.image.load(resource_path("Assets/images/wario/light_off.png")).convert_alpha()
self.light_on_image_raw = pygame.image.load(resource_path("Assets/images/wario/light_on.png")).convert_alpha()
self.chain_image_raw = pygame.image.load(resource_path("Assets/images/wario/chain.png")).convert_alpha()
if self.chain_image_raw:
original_width, original_height = self.chain_image_raw.get_size()
desired_height = 200
aspect_ratio = original_width / original_height
desired_width = int(desired_height * aspect_ratio)
self.chain_rect = pygame.Rect(0, 0, desired_width, desired_height)
else:
self.chain_rect = pygame.Rect(0, 0, 50, 200) # Fallback if image fails to load
except (pygame.error, FileNotFoundError) as e:
print(f"Error loading images for LightItUp: {e}")
def start(self):
self.timer = 3.0 / self.manager.speed
self.state = "playing"
self.end_timer = 2.0
self.light_on = False
self.light_bulb_rect.center = (SCREEN_WIDTH // 2, SCREEN_HEIGHT // 4)
self.chain_rect.midtop = self.light_bulb_rect.midbottom
self.initial_chain_y = self.chain_rect.y
self.chain_dragging = False
def update(self, dt, time_left=None):
if self.state == "playing":
if time_left is not None and time_left <= 0:
self.end(False) # Failure due to timeout
mouse_pos = pygame.mouse.get_pos()
mouse_pressed = pygame.mouse.get_pressed()
if mouse_pressed[0]:
if self.chain_rect.collidepoint(mouse_pos):
self.chain_dragging = True
else:
self.chain_dragging = False
if self.chain_dragging:
self.chain_rect.y = mouse_pos[1]
if self.chain_rect.y > self.initial_chain_y + 100:
self.light_on = True
self.end(True) # Success
elif self.state == "success" or self.state == "failure":
self.end_timer -= dt
if self.end_timer <= 0:
self.manager.microgame_finished(self.state == "success", 'wario')
def draw(self, screen):
screen.fill(WARIO_BG_COLOR)
if self.state == "playing":
# Draw light bulb
if self.light_on and self.light_on_image_raw:
scaled_img, scaled_rect = scale_to_fit(self.light_on_image_raw, self.light_bulb_rect)
screen.blit(scaled_img, scaled_rect)
elif self.light_off_image_raw:
scaled_img, scaled_rect = scale_to_fit(self.light_off_image_raw, self.light_bulb_rect)
screen.blit(scaled_img, scaled_rect)
else:
color = YELLOW if self.light_on else (50, 50, 50)
pygame.draw.rect(screen, color, self.light_bulb_rect)
# Draw chain
if self.chain_image_raw:
scaled_img, scaled_rect = scale_to_fit(self.chain_image_raw, self.chain_rect)
screen.blit(scaled_img, scaled_rect)
else:
pygame.draw.rect(screen, (100, 100, 100), self.chain_rect)
# Timer is now drawn by GameManager
pass
def end(self, success):
if success:
self.state = "success"
else:
self.state = "failure"
+107
View File
@@ -0,0 +1,107 @@
# microgames/wario/match_the_mood.py
import pygame
import random
from engine.scene import Scene
from settings import *
from engine.utils import scale_to_fit
class MatchTheMood(Scene):
def __init__(self, manager):
super().__init__(manager)
self.prompt = "Match the mood!"
self.moods = ["happy", "sad", "angry", "surprised"]
self.mood = None
self.emoji_rect = pygame.Rect(0, 0, 150, 150)
self.buttons = []
self.timer = 3.0 / self.manager.speed
self.state = "playing"
self.end_timer = 2.0
self.emoji_images_raw = {}
# Load raw images
try:
mood_images_paths = {
"happy": resource_path("Assets/images/wario/happy_emoji.png"),
"sad": resource_path("Assets/images/wario/sad_emoji.png"),
"angry": resource_path("Assets/images/wario/angry_emoji.png"),
"surprised": resource_path("Assets/images/wario/surprised_emoji.png")
}
for mood, path in mood_images_paths.items():
self.emoji_images_raw[mood] = pygame.image.load(path).convert_alpha()
except (pygame.error, FileNotFoundError) as e:
print(f"Error loading images for MatchTheMood: {e}")
self.emoji_images_raw = {}
def start(self):
self.timer = 3.0 / self.manager.speed
self.state = "playing"
self.end_timer = 2.0
self.mood = random.choice(self.moods)
self.emoji_rect.center = (SCREEN_WIDTH // 2, SCREEN_HEIGHT // 3)
mood_colors = {"happy": BLACK, "sad": BLACK, "angry": BLACK, "surprised": BLACK}
shuffled_moods = random.sample(self.moods, len(self.moods)) # Shuffle moods for button order
for i, mood_name in enumerate(shuffled_moods):
x = (SCREEN_WIDTH // (len(self.moods) + 1)) * (i + 1)
button_rect = pygame.Rect(0, 0, 150, 80)
button_rect.midtop = (x, SCREEN_HEIGHT * 2 // 3)
self.buttons.append({"rect": button_rect, "mood": mood_name, "color": mood_colors[mood_name]})
def update(self, dt, time_left=None):
if self.state == "playing":
if time_left is not None and time_left <= 0:
self.end(False) # Failure due to timeout
mouse_pos = pygame.mouse.get_pos()
mouse_pressed = pygame.mouse.get_pressed()
if mouse_pressed[0]:
for button in self.buttons:
if button["rect"].collidepoint(mouse_pos):
self.end(self.mood == button["mood"])
elif self.state == "success" or self.state == "failure":
self.end_timer -= dt
if self.end_timer <= 0:
self.manager.microgame_finished(self.state == "success", 'wario')
def draw(self, screen):
screen.fill(WARIO_BG_COLOR)
if self.state == "playing":
# Draw emoji
mood_image_raw = self.emoji_images_raw.get(self.mood)
if mood_image_raw:
scaled_img, scaled_rect = scale_to_fit(mood_image_raw, self.emoji_rect)
screen.blit(scaled_img, scaled_rect)
else:
# Fallback drawing logic if images fail to load
mood_colors = {"happy": YELLOW, "sad": BLUE, "angry": RED, "surprised": PURPLE}
if self.mood in mood_colors:
pygame.draw.circle(screen, mood_colors[self.mood], self.emoji_rect.center, 75)
if self.mood == "happy":
pygame.draw.arc(screen, BLACK, self.emoji_rect.inflate(-50, -50), 3.14, 0, 5)
elif self.mood == "sad":
pygame.draw.arc(screen, BLACK, self.emoji_rect.inflate(-50, -50).move(0, 30), 0, 3.14, 5)
elif self.mood == "angry":
pygame.draw.line(screen, BLACK, (self.emoji_rect.centerx - 20, self.emoji_rect.centery - 10), (self.emoji_rect.centerx - 40, self.emoji_rect.centery - 30), 5)
pygame.draw.line(screen, BLACK, (self.emoji_rect.centerx + 20, self.emoji_rect.centery - 10), (self.emoji_rect.centerx + 40, self.emoji_rect.centery - 30), 5)
elif self.mood == "surprised":
pygame.draw.circle(screen, BLACK, self.emoji_rect.center, 20)
# Draw buttons
for button in self.buttons:
pygame.draw.rect(screen, button["color"], button["rect"])
font = pygame.font.Font(None, 30)
text = font.render(button["mood"].capitalize(), True, WHITE)
text_rect = text.get_rect(center=button["rect"].center)
screen.blit(text, text_rect)
# Timer is now drawn by GameManager
pass
def end(self, success):
if success:
self.state = "success"
else:
self.state = "failure"
+76
View File
@@ -0,0 +1,76 @@
# microgames/wario/spot_the_bright_one.py
import pygame
import random
from engine.scene import Scene
from settings import *
class SpotTheBrightOne(Scene):
def __init__(self, manager):
super().__init__(manager)
self.prompt = "Spot the bright one!"
self.grid_size = 3
self.shapes = []
self.bright_one_index = 0
self.timer = 3.0 / self.manager.speed # 3 seconds
self.state = "playing"
self.end_timer = 2.0 # 2 seconds
def start(self):
self.timer = 3.0 / self.manager.speed
self.state = "playing"
self.end_timer = 2.0
self.shapes = []
self.bright_one_index = random.randint(0, self.grid_size * self.grid_size - 1)
shape_size = 100
padding = 20
grid_width = self.grid_size * shape_size + (self.grid_size - 1) * padding
grid_height = self.grid_size * shape_size + (self.grid_size - 1) * padding
start_x = (SCREEN_WIDTH - grid_width) // 2
start_y = (SCREEN_HEIGHT - grid_height) // 2
for i in range(self.grid_size * self.grid_size):
row = i // self.grid_size
col = i % self.grid_size
x = start_x + col * (shape_size + padding)
y = start_y + row * (shape_size + padding)
rect = pygame.Rect(x, y, shape_size, shape_size)
self.shapes.append(rect)
def update(self, dt, time_left=None):
if self.state == "playing":
if time_left is not None and time_left <= 0:
self.end(False) # Failure due to timeout
mouse_pos = pygame.mouse.get_pos()
mouse_pressed = pygame.mouse.get_pressed()
if mouse_pressed[0]: # Left mouse button
for i, shape in enumerate(self.shapes):
if shape.collidepoint(mouse_pos):
if i == self.bright_one_index:
self.end(True) # Success
else:
self.end(False) # Failure
elif self.state == "success" or self.state == "failure":
self.end_timer -= dt
if self.end_timer <= 0:
self.manager.microgame_finished(self.state == "success", 'wario')
def draw(self, screen):
screen.fill(WARIO_BG_COLOR)
if self.state == "playing":
for i, shape in enumerate(self.shapes):
color = WHITE if i == self.bright_one_index else BLACK
pygame.draw.rect(screen, color, shape)
# Timer is now drawn by GameManager
pass
def end(self, success):
if success:
self.state = "success"
else:
self.state = "failure"
+100
View File
@@ -0,0 +1,100 @@
# microgames/wario/truth_or_lie.py
import pygame
import random
from engine.scene import Scene
from settings import *
class TruthOrLie(Scene):
def __init__(self, manager):
super().__init__(manager)
self.prompt = "Truth or lie?"
self.statements = [
# True statements
("Im-a Wario, Im-a gonna win!", True),
("Wahaha! All the treasure is mine!", True),
("Garlic makes me strong!", True),
("Nobody can beat Wario!", True),
("Im the best!", True),
("Move it, weaklings!", True),
("I dont need friends, I need gold!", True),
("My muscles are powered by money!", True),
("Losers give up, winners like Wario take everything!", True),
("Wahaha! Wario time!", True),
# False statements
("I just want to help people!", False),
("Sharing is caring!", False),
("Mario deserves all the credit.", False),
("I dont care about money.", False),
("Garlic? Yuck!", False),
("Im on a diet.", False),
("Lets be nice to everyone!", False),
("Luigi is stronger than me.", False),
("Im afraid to get dirty.", False),
("Winning isnt everything.", False),
]
self.statement = None
self.answer = None
self.true_button = pygame.Rect(0, 0, 150, 80)
self.false_button = pygame.Rect(0, 0, 150, 80)
self.timer = 3.0 / self.manager.speed
self.state = "playing"
self.end_timer = 2.0
def start(self):
self.timer = 3.0 / self.manager.speed
self.state = "playing"
self.end_timer = 2.0
self.statement, self.answer = random.choice(self.statements)
self.true_button.midtop = (SCREEN_WIDTH // 3, SCREEN_HEIGHT * 2 // 3)
self.false_button.midtop = (SCREEN_WIDTH * 2 // 3, SCREEN_HEIGHT * 2 // 3)
def update(self, dt, time_left=None):
if self.state == "playing":
if time_left is not None and time_left <= 0:
self.end(False) # Failure due to timeout
mouse_pos = pygame.mouse.get_pos()
mouse_pressed = pygame.mouse.get_pressed()
if mouse_pressed[0]:
if self.true_button.collidepoint(mouse_pos):
self.end(self.answer == True)
elif self.false_button.collidepoint(mouse_pos):
self.end(self.answer == False)
elif self.state == "success" or self.state == "failure":
self.end_timer -= dt
if self.end_timer <= 0:
self.manager.microgame_finished(self.state == "success", 'wario')
def draw(self, screen):
screen.fill(WARIO_BG_COLOR)
if self.state == "playing":
# Draw statement
font_size = 60
if len(self.statement) > 30: # Adjust threshold as needed
font_size = 40
font = pygame.font.Font(None, font_size)
text = font.render(self.statement, True, WHITE)
text_rect = text.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 3))
screen.blit(text, text_rect)
# Draw buttons
pygame.draw.rect(screen, BLACK, self.true_button)
pygame.draw.rect(screen, BLACK, self.false_button)
font = pygame.font.Font(None, 50)
true_text = font.render("True", True, WHITE)
false_text = font.render("False", True, WHITE)
true_rect = true_text.get_rect(center=self.true_button.center)
false_rect = false_text.get_rect(center=self.false_button.center)
screen.blit(true_text, true_rect)
screen.blit(false_text, false_rect)
# Timer is now drawn by GameManager
pass
def end(self, success):
if success:
self.state = "success"
else:
self.state = "failure"
+43
View File
@@ -0,0 +1,43 @@
import pygame
import os
import sys
# settings.py
# Screen dimensions
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
FPS = 60
# Colors
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
YELLOW = (255, 255, 0)
ORANGE = (255, 165, 0)
PURPLE = (128, 0, 128)
def resource_path(relative_path):
""" Get absolute path to resource, works for dev and for PyInstaller """
try:
# PyInstaller creates a temp folder and stores path in _MEIPASS
base_path = sys._MEIPASS
except Exception:
base_path = os.path.abspath(".")
return os.path.join(base_path, relative_path)
# Fonts
pygame.font.init()
FONT_MD = pygame.font.Font(resource_path("Assets/fonts/DotGothic16-Regular.ttf"), 24)
FONT_LG = pygame.font.Font(resource_path("Assets/fonts/DotGothic16-Regular.ttf"), 48)
# Wario's Opposites Attack!
WARIO_BG_COLOR = BLACK
WARIO_SUCCESS_COLOR = (255, 255, 0) # Yellow
# 9-Volt's Retro Reload
NINEVOLT_BG_COLOR = (20, 20, 20)
NINEVOLT_SUCCESS_COLOR = (0, 255, 255) # Cyan