Initial commit: WarioWare Black and WAH game
@@ -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 }}
|
||||
@@ -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
|
||||
|
After Width: | Height: | Size: 520 KiB |
|
After Width: | Height: | Size: 89 KiB |
|
After Width: | Height: | Size: 131 KiB |
|
After Width: | Height: | Size: 466 KiB |
|
After Width: | Height: | Size: 1002 B |
|
After Width: | Height: | Size: 741 B |
|
After Width: | Height: | Size: 1.6 MiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 82 KiB |
|
After Width: | Height: | Size: 45 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 58 KiB |
|
After Width: | Height: | Size: 96 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 69 KiB |
|
After Width: | Height: | Size: 71 KiB |
|
After Width: | Height: | Size: 17 KiB |
@@ -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.
|
||||
@@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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))
|
||||
@@ -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
|
||||
@@ -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)
|
||||
""
|
||||
@@ -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()
|
||||
@@ -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"
|
||||
@@ -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"
|
||||
@@ -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"
|
||||
@@ -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"
|
||||
@@ -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"
|
||||
@@ -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"
|
||||
@@ -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"
|
||||
@@ -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"
|
||||
@@ -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"
|
||||
@@ -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
|
||||
("I’m-a Wario, I’m-a gonna win!", True),
|
||||
("Wahaha! All the treasure is mine!", True),
|
||||
("Garlic makes me strong!", True),
|
||||
("Nobody can beat Wario!", True),
|
||||
("I’m the best!", True),
|
||||
("Move it, weaklings!", True),
|
||||
("I don’t 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 don’t care about money.", False),
|
||||
("Garlic? Yuck!", False),
|
||||
("I’m on a diet.", False),
|
||||
("Let’s be nice to everyone!", False),
|
||||
("Luigi is stronger than me.", False),
|
||||
("I’m afraid to get dirty.", False),
|
||||
("Winning isn’t 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"
|
||||
@@ -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
|
||||