Something went wrong. Try again.
HEXWORK - A Lantern in the Deep: 3D ASCII terminal roguelike RPG in Rust.
Entirely designed and built by Muse Spark 1.3 to evaluate if I like the model.
Something went wrong. Try again.
4.7 kB · 170 lines
Rust
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171//! hexwork-term: terminal backend abstraction (docs/04 §4.6, docs/05 §5.3).//! Normalizes kitty/wezterm/xterm/gnome-terminal + SSH behind one trait.
pub mod backend;pub mod keymap;pub mod remap;
/// Terminal capabilities probed at boot.#[derive(Clone, Debug)]pub struct Capabilities { pub truecolor: bool, pub unicode: bool, pub mouse: bool, pub size: (u16, u16),}
impl Default for Capabilities { fn default() -> Self { Self { truecolor: true, unicode: true, mouse: false, size: (120, 36), } }}
/// Minimum supported size: 80×24 with a kind refusal below it.pub const MIN_W: u16 = 80;pub const MIN_H: u16 = 24;
pub fn size_ok(w: u16, h: u16) -> bool { w >= MIN_W && h >= MIN_H}
/// Normalized input events. Every mouse action has a key twin.#[derive(Clone, Copy, Debug, PartialEq, Eq)]pub enum Input { Up, Down, Left, Right, UpLeft, UpRight, DownLeft, DownRight, Wait, Quit, Look, Inventory, Contracts, Map, Chapel, /// Transfer verbs (Phase 12 T.2): pick up a loose item at the player's /// own cell / drop the explicitly selected carried item. Bound `g` and /// `-` by default; optional in custom keymaps, so they may be unbound. Pickup, Drop, CameraToggle, TiltDown, TiltUp, SliceUp, SliceDown, Recenter, LookReset, /// Terminal dimensions, columns then rows; never a canonical action. Resize(u16, u16), /// Zero-based terminal column and row of a left-button event. MouseDown(u16, u16), MouseDrag(u16, u16), MouseUp, FocusLost, PauseStep, /// Contextual interaction/menu confirmation (Enter). Confirm, /// Menu/chargen digit pick (1-6 → 0-5). Play treats as no-op; menus /// consume. Digits 7-9 map to Unknown (reserved). ChargenPick(usize), /// Explicitly toggle continuous travel (toggle-walk). A second press, or an /// active stop key (`Space`/`Esc`), clears it. Needed for terminals that do /// not report key repeats/releases, so continuous travel never depends on /// guessing a release. ToggleWalk, Unknown,}
/// A movement direction currently held down, carried by the terminal's key/// repeat (`KeyEventKind::Repeat`). This is a physical-input fact, not a game/// action: the frontend converts it to a world direction and paces one Move per/// tick. Releases clear it; terminals that never report repeats simply leave it/// `None`, and continuous travel falls back to [`Input::ToggleWalk`].#[derive(Clone, Copy, Debug, PartialEq, Eq)]pub enum HeldMove { Right, DownRight, Down, DownLeft, Left, UpLeft, Up, UpRight,}
impl HeldMove { /// The discrete movement [`Input`] this held key mirrors. pub fn as_input(self) -> Input { match self { HeldMove::Right => Input::Right, HeldMove::DownRight => Input::DownRight, HeldMove::Down => Input::Down, HeldMove::DownLeft => Input::DownLeft, HeldMove::Left => Input::Left, HeldMove::UpLeft => Input::UpLeft, HeldMove::Up => Input::Up, HeldMove::UpRight => Input::UpRight, } }
/// The held key mirroring `input`, if `input` is a movement key. pub fn from_input(input: Input) -> Option<HeldMove> { Some(match input { Input::Right => HeldMove::Right, Input::DownRight => HeldMove::DownRight, Input::Down => HeldMove::Down, Input::DownLeft => HeldMove::DownLeft, Input::Left => HeldMove::Left, Input::UpLeft => HeldMove::UpLeft, Input::Up => HeldMove::Up, Input::UpRight => HeldMove::UpRight, _ => return None, }) }}
#[cfg(test)]mod tests { use super::*;
#[test] fn refuses_tiny_terminals() { assert!(!size_ok(79, 24)); assert!(!size_ok(80, 23)); assert!(size_ok(80, 24)); }
#[test] fn held_move_round_trips_through_input() { for held in [ HeldMove::Right, HeldMove::DownRight, HeldMove::Down, HeldMove::DownLeft, HeldMove::Left, HeldMove::UpLeft, HeldMove::Up, HeldMove::UpRight, ] { assert_eq!(HeldMove::from_input(held.as_input()), Some(held)); } for input in [ Input::Wait, Input::Look, Input::ToggleWalk, Input::PauseStep, Input::Unknown, ] { assert_eq!(HeldMove::from_input(input), None); } }}