draft tutorial revisions
This commit is contained in:
parent
838da4571d
commit
48359b5a48
70 changed files with 6216 additions and 28 deletions
330
docs/templates/complete/ui.py
vendored
Normal file
330
docs/templates/complete/ui.py
vendored
Normal file
|
|
@ -0,0 +1,330 @@
|
|||
"""
|
||||
ui.py - User Interface Components for McRogueFace Roguelike
|
||||
|
||||
Contains the health bar and message log UI elements.
|
||||
"""
|
||||
|
||||
from typing import List, Tuple, Optional
|
||||
from dataclasses import dataclass
|
||||
import mcrfpy
|
||||
|
||||
from constants import (
|
||||
HP_BAR_X, HP_BAR_Y, HP_BAR_WIDTH, HP_BAR_HEIGHT,
|
||||
MSG_LOG_X, MSG_LOG_Y, MSG_LOG_WIDTH, MSG_LOG_HEIGHT, MSG_LOG_MAX_LINES,
|
||||
LEVEL_DISPLAY_X, LEVEL_DISPLAY_Y,
|
||||
COLOR_UI_BG, COLOR_UI_BORDER, COLOR_TEXT,
|
||||
COLOR_HP_BAR_BG, COLOR_HP_BAR_FILL, COLOR_HP_BAR_WARNING, COLOR_HP_BAR_CRITICAL,
|
||||
COLOR_MSG_DEFAULT
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class Message:
|
||||
"""A message in the message log."""
|
||||
text: str
|
||||
color: Tuple[int, int, int, int]
|
||||
|
||||
|
||||
class HealthBar:
|
||||
"""
|
||||
Visual health bar displaying player HP.
|
||||
|
||||
Uses nested Frames: an outer background frame and an inner fill frame
|
||||
that resizes based on HP percentage.
|
||||
"""
|
||||
|
||||
def __init__(self, x: int = HP_BAR_X, y: int = HP_BAR_Y,
|
||||
width: int = HP_BAR_WIDTH, height: int = HP_BAR_HEIGHT,
|
||||
font: mcrfpy.Font = None):
|
||||
"""
|
||||
Create a health bar.
|
||||
|
||||
Args:
|
||||
x: X position
|
||||
y: Y position
|
||||
width: Total width of the bar
|
||||
height: Height of the bar
|
||||
font: Font for the HP text
|
||||
"""
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.width = width
|
||||
self.height = height
|
||||
self.font = font or mcrfpy.default_font
|
||||
|
||||
# Background frame
|
||||
self.bg_frame = mcrfpy.Frame(x, y, width, height)
|
||||
self.bg_frame.fill_color = mcrfpy.Color(*COLOR_HP_BAR_BG)
|
||||
self.bg_frame.outline = 2
|
||||
self.bg_frame.outline_color = mcrfpy.Color(*COLOR_UI_BORDER)
|
||||
|
||||
# Fill frame (inside background)
|
||||
self.fill_frame = mcrfpy.Frame(x + 2, y + 2, width - 4, height - 4)
|
||||
self.fill_frame.fill_color = mcrfpy.Color(*COLOR_HP_BAR_FILL)
|
||||
self.fill_frame.outline = 0
|
||||
|
||||
# HP text
|
||||
self.hp_text = mcrfpy.Caption("HP: 0 / 0", self.font, x + 8, y + 4)
|
||||
self.hp_text.fill_color = mcrfpy.Color(*COLOR_TEXT)
|
||||
|
||||
self._max_fill_width = width - 4
|
||||
|
||||
def add_to_scene(self, ui: mcrfpy.UICollection) -> None:
|
||||
"""Add all health bar components to a scene."""
|
||||
ui.append(self.bg_frame)
|
||||
ui.append(self.fill_frame)
|
||||
ui.append(self.hp_text)
|
||||
|
||||
def update(self, current_hp: int, max_hp: int) -> None:
|
||||
"""
|
||||
Update the health bar display.
|
||||
|
||||
Args:
|
||||
current_hp: Current hit points
|
||||
max_hp: Maximum hit points
|
||||
"""
|
||||
# Calculate fill percentage
|
||||
if max_hp <= 0:
|
||||
percent = 0.0
|
||||
else:
|
||||
percent = max(0.0, min(1.0, current_hp / max_hp))
|
||||
|
||||
# Update fill bar width
|
||||
self.fill_frame.w = int(self._max_fill_width * percent)
|
||||
|
||||
# Update color based on HP percentage
|
||||
if percent > 0.6:
|
||||
color = COLOR_HP_BAR_FILL
|
||||
elif percent > 0.3:
|
||||
color = COLOR_HP_BAR_WARNING
|
||||
else:
|
||||
color = COLOR_HP_BAR_CRITICAL
|
||||
|
||||
self.fill_frame.fill_color = mcrfpy.Color(*color)
|
||||
|
||||
# Update text
|
||||
self.hp_text.text = f"HP: {current_hp} / {max_hp}"
|
||||
|
||||
|
||||
class MessageLog:
|
||||
"""
|
||||
Scrolling message log displaying game events.
|
||||
|
||||
Uses a Frame container with Caption children for each line.
|
||||
"""
|
||||
|
||||
def __init__(self, x: int = MSG_LOG_X, y: int = MSG_LOG_Y,
|
||||
width: int = MSG_LOG_WIDTH, height: int = MSG_LOG_HEIGHT,
|
||||
max_lines: int = MSG_LOG_MAX_LINES,
|
||||
font: mcrfpy.Font = None):
|
||||
"""
|
||||
Create a message log.
|
||||
|
||||
Args:
|
||||
x: X position
|
||||
y: Y position
|
||||
width: Width of the log
|
||||
height: Height of the log
|
||||
max_lines: Maximum number of visible lines
|
||||
font: Font for the messages
|
||||
"""
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.width = width
|
||||
self.height = height
|
||||
self.max_lines = max_lines
|
||||
self.font = font or mcrfpy.default_font
|
||||
|
||||
# Container frame
|
||||
self.frame = mcrfpy.Frame(x, y, width, height)
|
||||
self.frame.fill_color = mcrfpy.Color(*COLOR_UI_BG)
|
||||
self.frame.outline = 1
|
||||
self.frame.outline_color = mcrfpy.Color(*COLOR_UI_BORDER)
|
||||
|
||||
# Message storage
|
||||
self.messages: List[Message] = []
|
||||
self.captions: List[mcrfpy.Caption] = []
|
||||
|
||||
# Line height (approximate based on font)
|
||||
self.line_height = 18
|
||||
|
||||
# Create caption objects for each line
|
||||
self._init_captions()
|
||||
|
||||
def _init_captions(self) -> None:
|
||||
"""Initialize caption objects for message display."""
|
||||
for i in range(self.max_lines):
|
||||
caption = mcrfpy.Caption(
|
||||
"",
|
||||
self.font,
|
||||
self.x + 5,
|
||||
self.y + 5 + i * self.line_height
|
||||
)
|
||||
caption.fill_color = mcrfpy.Color(*COLOR_MSG_DEFAULT)
|
||||
self.captions.append(caption)
|
||||
|
||||
def add_to_scene(self, ui: mcrfpy.UICollection) -> None:
|
||||
"""Add the message log to a scene."""
|
||||
ui.append(self.frame)
|
||||
for caption in self.captions:
|
||||
ui.append(caption)
|
||||
|
||||
def add_message(self, text: str,
|
||||
color: Tuple[int, int, int, int] = COLOR_MSG_DEFAULT) -> None:
|
||||
"""
|
||||
Add a message to the log.
|
||||
|
||||
Args:
|
||||
text: Message text
|
||||
color: Text color as (R, G, B, A)
|
||||
"""
|
||||
self.messages.append(Message(text, color))
|
||||
|
||||
# Trim old messages
|
||||
if len(self.messages) > 100:
|
||||
self.messages = self.messages[-100:]
|
||||
|
||||
# Update display
|
||||
self._update_display()
|
||||
|
||||
def _update_display(self) -> None:
|
||||
"""Update the displayed messages."""
|
||||
# Get the most recent messages
|
||||
recent = self.messages[-self.max_lines:]
|
||||
|
||||
for i, caption in enumerate(self.captions):
|
||||
if i < len(recent):
|
||||
msg = recent[i]
|
||||
caption.text = msg.text
|
||||
caption.fill_color = mcrfpy.Color(*msg.color)
|
||||
else:
|
||||
caption.text = ""
|
||||
|
||||
def clear(self) -> None:
|
||||
"""Clear all messages."""
|
||||
self.messages.clear()
|
||||
self._update_display()
|
||||
|
||||
|
||||
class LevelDisplay:
|
||||
"""Simple display showing current dungeon level."""
|
||||
|
||||
def __init__(self, x: int = LEVEL_DISPLAY_X, y: int = LEVEL_DISPLAY_Y,
|
||||
font: mcrfpy.Font = None):
|
||||
"""
|
||||
Create a level display.
|
||||
|
||||
Args:
|
||||
x: X position
|
||||
y: Y position
|
||||
font: Font for the text
|
||||
"""
|
||||
self.font = font or mcrfpy.default_font
|
||||
|
||||
self.caption = mcrfpy.Caption("Level: 1", self.font, x, y)
|
||||
self.caption.fill_color = mcrfpy.Color(*COLOR_TEXT)
|
||||
|
||||
def add_to_scene(self, ui: mcrfpy.UICollection) -> None:
|
||||
"""Add to a scene."""
|
||||
ui.append(self.caption)
|
||||
|
||||
def update(self, level: int) -> None:
|
||||
"""Update the displayed level."""
|
||||
self.caption.text = f"Dungeon Level: {level}"
|
||||
|
||||
|
||||
class GameUI:
|
||||
"""
|
||||
Container for all UI elements.
|
||||
|
||||
Provides a single point of access for updating the entire UI.
|
||||
"""
|
||||
|
||||
def __init__(self, font: mcrfpy.Font = None):
|
||||
"""
|
||||
Create the game UI.
|
||||
|
||||
Args:
|
||||
font: Font for all UI elements
|
||||
"""
|
||||
self.font = font or mcrfpy.default_font
|
||||
|
||||
# Create UI components
|
||||
self.health_bar = HealthBar(font=self.font)
|
||||
self.message_log = MessageLog(font=self.font)
|
||||
self.level_display = LevelDisplay(font=self.font)
|
||||
|
||||
def add_to_scene(self, ui: mcrfpy.UICollection) -> None:
|
||||
"""Add all UI elements to a scene."""
|
||||
self.health_bar.add_to_scene(ui)
|
||||
self.message_log.add_to_scene(ui)
|
||||
self.level_display.add_to_scene(ui)
|
||||
|
||||
def update_hp(self, current_hp: int, max_hp: int) -> None:
|
||||
"""Update the health bar."""
|
||||
self.health_bar.update(current_hp, max_hp)
|
||||
|
||||
def add_message(self, text: str,
|
||||
color: Tuple[int, int, int, int] = COLOR_MSG_DEFAULT) -> None:
|
||||
"""Add a message to the log."""
|
||||
self.message_log.add_message(text, color)
|
||||
|
||||
def update_level(self, level: int) -> None:
|
||||
"""Update the dungeon level display."""
|
||||
self.level_display.update(level)
|
||||
|
||||
def clear_messages(self) -> None:
|
||||
"""Clear the message log."""
|
||||
self.message_log.clear()
|
||||
|
||||
|
||||
class DeathScreen:
|
||||
"""Game over screen shown when player dies."""
|
||||
|
||||
def __init__(self, font: mcrfpy.Font = None):
|
||||
"""
|
||||
Create the death screen.
|
||||
|
||||
Args:
|
||||
font: Font for text
|
||||
"""
|
||||
self.font = font or mcrfpy.default_font
|
||||
self.elements: List = []
|
||||
|
||||
# Semi-transparent overlay
|
||||
self.overlay = mcrfpy.Frame(0, 0, 1024, 768)
|
||||
self.overlay.fill_color = mcrfpy.Color(0, 0, 0, 180)
|
||||
self.elements.append(self.overlay)
|
||||
|
||||
# Death message
|
||||
self.death_text = mcrfpy.Caption(
|
||||
"YOU HAVE DIED",
|
||||
self.font,
|
||||
362, 300
|
||||
)
|
||||
self.death_text.fill_color = mcrfpy.Color(255, 0, 0, 255)
|
||||
self.death_text.outline = 2
|
||||
self.death_text.outline_color = mcrfpy.Color(0, 0, 0, 255)
|
||||
self.elements.append(self.death_text)
|
||||
|
||||
# Restart prompt
|
||||
self.restart_text = mcrfpy.Caption(
|
||||
"Press R to restart",
|
||||
self.font,
|
||||
400, 400
|
||||
)
|
||||
self.restart_text.fill_color = mcrfpy.Color(200, 200, 200, 255)
|
||||
self.elements.append(self.restart_text)
|
||||
|
||||
def add_to_scene(self, ui: mcrfpy.UICollection) -> None:
|
||||
"""Add death screen elements to a scene."""
|
||||
for element in self.elements:
|
||||
ui.append(element)
|
||||
|
||||
def remove_from_scene(self, ui: mcrfpy.UICollection) -> None:
|
||||
"""Remove death screen elements from a scene."""
|
||||
for element in self.elements:
|
||||
try:
|
||||
ui.remove(element)
|
||||
except (ValueError, RuntimeError):
|
||||
pass
|
||||
Loading…
Add table
Add a link
Reference in a new issue