key inv display
This commit is contained in:
@@ -4,12 +4,56 @@ use crate::log::LogLine;
|
|||||||
use crate::script::ScriptHost;
|
use crate::script::ScriptHost;
|
||||||
use crate::utils::{Direction, ObjectId, Player, ScriptArg};
|
use crate::utils::{Direction, ObjectId, Player, ScriptArg};
|
||||||
use crate::world::World;
|
use crate::world::World;
|
||||||
|
use color::Rgba8;
|
||||||
use std::cell::{Ref, RefMut};
|
use std::cell::{Ref, RefMut};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
/// How long a `say()` speech bubble stays on screen, in seconds.
|
/// How long a `say()` speech bubble stays on screen, in seconds.
|
||||||
pub const SAY_DURATION: f64 = 3.0;
|
pub const SAY_DURATION: f64 = 3.0;
|
||||||
|
|
||||||
|
/// The player's key inventory: one boolean per key color.
|
||||||
|
///
|
||||||
|
/// Each field is `true` when the player holds a key of that color. Use
|
||||||
|
/// [`colors`](Keyring::colors) to iterate all eight in display order, each
|
||||||
|
/// paired with its [`Rgba8`] color for rendering.
|
||||||
|
#[derive(Copy, Clone, Default)]
|
||||||
|
pub struct Keyring {
|
||||||
|
/// Blue key.
|
||||||
|
pub blue: bool,
|
||||||
|
/// Green key.
|
||||||
|
pub green: bool,
|
||||||
|
/// Cyan key.
|
||||||
|
pub cyan: bool,
|
||||||
|
/// Red key.
|
||||||
|
pub red: bool,
|
||||||
|
/// Purple key.
|
||||||
|
pub purple: bool,
|
||||||
|
/// Orange key (custom color, not in the standard EGA palette).
|
||||||
|
pub orange: bool,
|
||||||
|
/// Yellow key.
|
||||||
|
pub yellow: bool,
|
||||||
|
/// White key.
|
||||||
|
pub white: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Keyring {
|
||||||
|
/// Returns all eight keys in display order, each paired with its color.
|
||||||
|
///
|
||||||
|
/// Order: blue, green, cyan, red, purple, orange, yellow, white.
|
||||||
|
pub fn colors(&self) -> [(bool, Rgba8); 8] {
|
||||||
|
[
|
||||||
|
(self.blue, Rgba8 { r: 0x00, g: 0x00, b: 0xAA, a: 255 }),
|
||||||
|
(self.green, Rgba8 { r: 0x00, g: 0xAA, b: 0x00, a: 255 }),
|
||||||
|
(self.cyan, Rgba8 { r: 0x00, g: 0xAA, b: 0xAA, a: 255 }),
|
||||||
|
(self.red, Rgba8 { r: 0xAA, g: 0x00, b: 0x00, a: 255 }),
|
||||||
|
(self.purple, Rgba8 { r: 0xAA, g: 0x00, b: 0xAA, a: 255 }),
|
||||||
|
(self.orange, Rgba8 { r: 0xFF, g: 0x88, b: 0x00, a: 255 }),
|
||||||
|
(self.yellow, Rgba8 { r: 0xFF, g: 0xFF, b: 0x55, a: 255 }),
|
||||||
|
(self.white, Rgba8 { r: 0xFF, g: 0xFF, b: 0xFF, a: 255 }),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Re-export ScrollLine so kiln-tui can pattern-match scroll content without
|
// Re-export ScrollLine so kiln-tui can pattern-match scroll content without
|
||||||
// accessing the private `action` module directly.
|
// accessing the private `action` module directly.
|
||||||
pub use crate::action::ScrollLine;
|
pub use crate::action::ScrollLine;
|
||||||
@@ -74,6 +118,8 @@ pub struct GameState {
|
|||||||
/// [`player_health`](GameState::player_health); starts at `0` and is
|
/// [`player_health`](GameState::player_health); starts at `0` and is
|
||||||
/// display-only for now.
|
/// display-only for now.
|
||||||
pub player_gems: u32,
|
pub player_gems: u32,
|
||||||
|
/// The player's key inventory. Game-global; persists across board transitions.
|
||||||
|
pub player_keys: Keyring,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GameState {
|
impl GameState {
|
||||||
@@ -102,6 +148,8 @@ impl GameState {
|
|||||||
board_transition: None,
|
board_transition: None,
|
||||||
player_health: 5,
|
player_health: 5,
|
||||||
player_gems: 0,
|
player_gems: 0,
|
||||||
|
// Test starting keys: red, green, orange, white.
|
||||||
|
player_keys: Keyring { red: true, green: true, orange: true, white: true, ..Default::default() },
|
||||||
};
|
};
|
||||||
state.drain_errors();
|
state.drain_errors();
|
||||||
state
|
state
|
||||||
|
|||||||
@@ -100,6 +100,10 @@ impl EditorState {
|
|||||||
/// Builds an editor session for `world`, starting on its designated start board.
|
/// Builds an editor session for `world`, starting on its designated start board.
|
||||||
pub(crate) fn new(world: World) -> Self {
|
pub(crate) fn new(world: World) -> Self {
|
||||||
let board_name = world.start.clone();
|
let board_name = world.start.clone();
|
||||||
|
let cursor = {
|
||||||
|
let b = world.boards[&board_name].borrow();
|
||||||
|
(b.width as i32 / 2, b.height as i32 / 2)
|
||||||
|
};
|
||||||
Self {
|
Self {
|
||||||
world,
|
world,
|
||||||
board_name,
|
board_name,
|
||||||
@@ -107,7 +111,7 @@ impl EditorState {
|
|||||||
dialog: None,
|
dialog: None,
|
||||||
code_editor: None,
|
code_editor: None,
|
||||||
glyph_dialog: None,
|
glyph_dialog: None,
|
||||||
cursor: (0, 0),
|
cursor,
|
||||||
current_archetype: Archetype::Wall,
|
current_archetype: Archetype::Wall,
|
||||||
current_glyph: Archetype::Wall.default_glyph(),
|
current_glyph: Archetype::Wall.default_glyph(),
|
||||||
draw_mode: false,
|
draw_mode: false,
|
||||||
|
|||||||
@@ -342,7 +342,7 @@ fn draw_play(frame: &mut Frame, game: &GameState, ui: &mut Ui) {
|
|||||||
.spacing(Spacing::Overlap(1))
|
.spacing(Spacing::Overlap(1))
|
||||||
.areas(frame.area());
|
.areas(frame.area());
|
||||||
frame.render_widget(
|
frame.render_widget(
|
||||||
StatusSidebarWidget::new(game.player_health - 2, game.player_gems),
|
StatusSidebarWidget::new(game.player_health - 2, game.player_gems, game.player_keys),
|
||||||
sidebar_area,
|
sidebar_area,
|
||||||
);
|
);
|
||||||
rest
|
rest
|
||||||
|
|||||||
+28
-5
@@ -5,7 +5,9 @@
|
|||||||
//! presentational: it reads `health`/`gems` values handed in at construction and
|
//! presentational: it reads `health`/`gems` values handed in at construction and
|
||||||
//! never mutates game state.
|
//! never mutates game state.
|
||||||
|
|
||||||
|
use std::collections::VecDeque;
|
||||||
use crate::utils::rgba8_to_color;
|
use crate::utils::rgba8_to_color;
|
||||||
|
use kiln_core::game::Keyring;
|
||||||
use kiln_core::{Archetype, Builtin};
|
use kiln_core::{Archetype, Builtin};
|
||||||
use kiln_core::cp437::tile_to_char;
|
use kiln_core::cp437::tile_to_char;
|
||||||
use ratatui::buffer::Buffer;
|
use ratatui::buffer::Buffer;
|
||||||
@@ -24,19 +26,22 @@ const HEART_EMPTY: Color = Color::Rgb(90, 0, 0);
|
|||||||
|
|
||||||
/// A ratatui widget that renders the play-mode status sidebar.
|
/// A ratatui widget that renders the play-mode status sidebar.
|
||||||
///
|
///
|
||||||
/// Shows a `Health:` label above a row of [`MAX_HEARTS`] heart glyphs (the first
|
/// Shows a `Health:` label above a row of [`MAX_HEARTS`] heart glyphs, a
|
||||||
/// `health` filled bright red, the rest dark red) and a `Gems: ♦ N` line.
|
/// `Gems: ♦ N` line, and a `Keys:` row of 8 `♀` glyphs colored when held
|
||||||
|
/// and dark gray when absent.
|
||||||
pub struct StatusSidebarWidget {
|
pub struct StatusSidebarWidget {
|
||||||
/// The player's current health, clamped to [`MAX_HEARTS`] when drawn.
|
/// The player's current health, clamped to [`MAX_HEARTS`] when drawn.
|
||||||
health: u32,
|
health: u32,
|
||||||
/// The number of gems collected, shown as a count.
|
/// The number of gems collected, shown as a count.
|
||||||
gems: u32,
|
gems: u32,
|
||||||
|
/// The player's key inventory.
|
||||||
|
keys: Keyring,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StatusSidebarWidget {
|
impl StatusSidebarWidget {
|
||||||
/// Builds a sidebar widget for the given player stats.
|
/// Builds a sidebar widget for the given player stats.
|
||||||
pub fn new(health: u32, gems: u32) -> Self {
|
pub fn new(health: u32, gems: u32, keys: Keyring) -> Self {
|
||||||
Self { health, gems }
|
Self { health, gems, keys }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,6 +63,22 @@ impl Widget for StatusSidebarWidget {
|
|||||||
let gem_char = tile_to_char(gem_glyph.tile).to_string();
|
let gem_char = tile_to_char(gem_glyph.tile).to_string();
|
||||||
let gem_style = Style::default().fg(rgba8_to_color(gem_glyph.fg));
|
let gem_style = Style::default().fg(rgba8_to_color(gem_glyph.fg));
|
||||||
|
|
||||||
|
// Build the key row: 8 ♀ glyphs, colored when held, near-black when absent.
|
||||||
|
let mut key_spans: VecDeque<_> = self
|
||||||
|
.keys
|
||||||
|
.colors()
|
||||||
|
.iter()
|
||||||
|
.filter_map(|(held, color)| {
|
||||||
|
if *held {
|
||||||
|
let fg = rgba8_to_color(*color);
|
||||||
|
Some(Span::styled("♀", Style::default().fg(fg)))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
key_spans.push_front(Span::raw("Keys: "));
|
||||||
|
|
||||||
let lines = vec![
|
let lines = vec![
|
||||||
Line::from("Health:"),
|
Line::from("Health:"),
|
||||||
Line::from(hearts),
|
Line::from(hearts),
|
||||||
@@ -65,8 +86,10 @@ impl Widget for StatusSidebarWidget {
|
|||||||
Line::from(vec![
|
Line::from(vec![
|
||||||
Span::raw("Gems: "),
|
Span::raw("Gems: "),
|
||||||
Span::styled(gem_char, gem_style),
|
Span::styled(gem_char, gem_style),
|
||||||
Span::raw(format!(" {}", self.gems)),
|
Span::raw(format!("{}", self.gems)),
|
||||||
]),
|
]),
|
||||||
|
Line::from(""),
|
||||||
|
Line::from_iter(key_spans.into_iter()),
|
||||||
];
|
];
|
||||||
|
|
||||||
// A bordered block; `merge_borders` joins its right edge with the board
|
// A bordered block; `merge_borders` joins its right edge with the board
|
||||||
|
|||||||
Reference in New Issue
Block a user