feat(viz): enter a specific seed (S to type, Enter to apply)
Press S to begin seed entry, type digits (HUD shows the buffer), Enter applies and regenerates, Esc cancels, Backspace edits. Reroll still increments. Digit-only input; buffer capped under u64's range. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
ab195306a5
commit
c13fc18d8f
1 changed files with 66 additions and 14 deletions
|
|
@ -201,7 +201,10 @@ fn draw_edges(snapshot: &Snapshot, layout: &Layout) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draws the on-screen HUD: seed, current stage, and the controls hint.
|
/// Draws the on-screen HUD: seed, current stage, and the controls hint.
|
||||||
fn draw_hud(dungeon: &Dungeon) {
|
///
|
||||||
|
/// When `seed_input` is `Some`, the user is typing a seed: the top line becomes
|
||||||
|
/// an entry prompt showing the buffer, and the hint switches to the entry keys.
|
||||||
|
fn draw_hud(dungeon: &Dungeon, seed_input: Option<&str>) {
|
||||||
let stage_count = dungeon.snapshots.len();
|
let stage_count = dungeon.snapshots.len();
|
||||||
let stage_label = dungeon
|
let stage_label = dungeon
|
||||||
.current()
|
.current()
|
||||||
|
|
@ -209,7 +212,17 @@ fn draw_hud(dungeon: &Dungeon) {
|
||||||
.unwrap_or("(empty)");
|
.unwrap_or("(empty)");
|
||||||
let is_final = dungeon.stage + 1 == stage_count;
|
let is_final = dungeon.stage + 1 == stage_count;
|
||||||
|
|
||||||
let seed_line = format!("seed: {}", dungeon.seed);
|
// The top line doubles as the seed-entry prompt while typing.
|
||||||
|
let (top_line, hint) = match seed_input {
|
||||||
|
Some(buf) => (
|
||||||
|
format!("set seed: {buf}_"),
|
||||||
|
"Enter: apply Esc: cancel (digits only)",
|
||||||
|
),
|
||||||
|
None => (
|
||||||
|
format!("seed: {}", dungeon.seed),
|
||||||
|
"Space: reroll Left/Right: scrub stages S: set seed",
|
||||||
|
),
|
||||||
|
};
|
||||||
let stage_line = format!(
|
let stage_line = format!(
|
||||||
"stage {}/{}: {}{}",
|
"stage {}/{}: {}{}",
|
||||||
dungeon.stage + 1,
|
dungeon.stage + 1,
|
||||||
|
|
@ -217,11 +230,11 @@ fn draw_hud(dungeon: &Dungeon) {
|
||||||
stage_label,
|
stage_label,
|
||||||
if is_final { " (final)" } else { "" },
|
if is_final { " (final)" } else { "" },
|
||||||
);
|
);
|
||||||
let hint = "Space: reroll Left/Right: scrub stages";
|
|
||||||
|
|
||||||
// A dark plate behind the text so it stays readable over light floor tiles.
|
// A dark plate behind the text so it stays readable over light floor tiles.
|
||||||
draw_rectangle(0.0, 0.0, screen_width(), 64.0, Color::new(0.0, 0.0, 0.0, 0.55));
|
draw_rectangle(0.0, 0.0, screen_width(), 64.0, Color::new(0.0, 0.0, 0.0, 0.55));
|
||||||
draw_text(&seed_line, 12.0, 22.0, 24.0, WHITE);
|
let top_color = if seed_input.is_some() { GOLD } else { WHITE };
|
||||||
|
draw_text(&top_line, 12.0, 22.0, 24.0, top_color);
|
||||||
draw_text(&stage_line, 12.0, 44.0, 22.0, SKYBLUE);
|
draw_text(&stage_line, 12.0, 44.0, 22.0, SKYBLUE);
|
||||||
draw_text(hint, 12.0, 60.0, 18.0, LIGHTGRAY);
|
draw_text(hint, 12.0, 60.0, 18.0, LIGHTGRAY);
|
||||||
}
|
}
|
||||||
|
|
@ -234,18 +247,57 @@ async fn main() {
|
||||||
// determinism is unaffected — same seed always rebuilds the same dungeon.
|
// determinism is unaffected — same seed always rebuilds the same dungeon.
|
||||||
let mut seed: u64 = 0;
|
let mut seed: u64 = 0;
|
||||||
let mut dungeon = Dungeon::generate(seed);
|
let mut dungeon = Dungeon::generate(seed);
|
||||||
|
// `Some(buffer)` while the user is typing a seed; `None` in normal mode.
|
||||||
|
let mut seed_input: Option<String> = None;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
// --- input ---
|
// --- input ---
|
||||||
if is_key_pressed(KeyCode::Space) {
|
if seed_input.is_none() {
|
||||||
seed = seed.wrapping_add(1);
|
// Normal mode: reroll, scrub, or begin seed entry.
|
||||||
dungeon = Dungeon::generate(seed);
|
if is_key_pressed(KeyCode::Space) {
|
||||||
}
|
seed = seed.wrapping_add(1);
|
||||||
if is_key_pressed(KeyCode::Left) {
|
dungeon = Dungeon::generate(seed);
|
||||||
dungeon.scrub(-1);
|
}
|
||||||
}
|
if is_key_pressed(KeyCode::Left) {
|
||||||
if is_key_pressed(KeyCode::Right) {
|
dungeon.scrub(-1);
|
||||||
dungeon.scrub(1);
|
}
|
||||||
|
if is_key_pressed(KeyCode::Right) {
|
||||||
|
dungeon.scrub(1);
|
||||||
|
}
|
||||||
|
if is_key_pressed(KeyCode::S) {
|
||||||
|
// Begin seed entry; digits are collected on the following frames.
|
||||||
|
seed_input = Some(String::new());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Seed-entry mode: collect digits, apply on Enter, cancel on Esc.
|
||||||
|
let mut exit = false;
|
||||||
|
{
|
||||||
|
let buf = seed_input.as_mut().expect("in seed-entry mode");
|
||||||
|
// Drain this frame's typed chars; keep digits only (so the `S`
|
||||||
|
// that opened the mode and any other keys are ignored). Cap the
|
||||||
|
// length well under u64's 20 digits so it always parses.
|
||||||
|
while let Some(c) = get_char_pressed() {
|
||||||
|
if c.is_ascii_digit() && buf.len() < 19 {
|
||||||
|
buf.push(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if is_key_pressed(KeyCode::Backspace) {
|
||||||
|
buf.pop();
|
||||||
|
}
|
||||||
|
if is_key_pressed(KeyCode::Enter) {
|
||||||
|
// Empty or unparseable buffer simply cancels (no change).
|
||||||
|
if let Ok(parsed) = buf.parse::<u64>() {
|
||||||
|
seed = parsed;
|
||||||
|
dungeon = Dungeon::generate(seed);
|
||||||
|
}
|
||||||
|
exit = true;
|
||||||
|
} else if is_key_pressed(KeyCode::Escape) {
|
||||||
|
exit = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if exit {
|
||||||
|
seed_input = None;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- draw ---
|
// --- draw ---
|
||||||
|
|
@ -271,7 +323,7 @@ async fn main() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
draw_hud(&dungeon);
|
draw_hud(&dungeon, seed_input.as_deref());
|
||||||
|
|
||||||
next_frame().await;
|
next_frame().await;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue