UI in device pixels on the character grid with fixed integer font sizes, ASCII bars; 3D render scale for a pixel look
- text only at 1x/2x/4x times an integer interface factor (auto or 1-4 in the settings) - high DPI rendering, own 2D camera in device pixels, mouse converted - toolbar buttons 6 columns, integer icon scale, minimap integer pixels per tile - all bars (top bar, unit/tower/gate HP) as CP437 block characters - 3D world optionally rendered at 1/2, 1/3, 1/4 and upscaled with nearest filtering Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
889ca66dbf
commit
50d013bb28
8 files changed
+344
-213
No files matched your search
+58
-48
@@ -5,7 +5,7 @@ use crate::gfx::*;
|
||||
use crate::i18n::{languages, tr, trf};
|
||||
use crate::map::{MAX_SIZE, MIN_SIZE};
|
||||
use crate::settings::{all_actions, key_name, Action, Settings};
|
||||
use crate::ui::{tool_info, CH, CW, DIM, FRAME, FRAME_HI, GOLD, S, TOOLS, TXT};
|
||||
use crate::ui::{grid_center, grid_center_y, mouse, sh, sw, tool_info, CH, CW, DIM, FRAME, FRAME_HI, GOLD, S, SMALL, TITLE, TOOLS, TXT};
|
||||
use macroquad::prelude::*;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
@@ -34,6 +34,7 @@ pub enum Item {
|
||||
WallDetail,
|
||||
Lights,
|
||||
UiScale,
|
||||
RenderScale,
|
||||
GameSpeed,
|
||||
Hints,
|
||||
}
|
||||
@@ -43,7 +44,7 @@ fn tab_items(tab: usize) -> &'static [Item] {
|
||||
match tab {
|
||||
0 => &[Language, Volume, MusicVolume, Mute],
|
||||
1 => &[RotateSpeed, ZoomSpeed, EdgeScroll, InvertMouse],
|
||||
2 => &[Outlines, WallDetail, Lights, UiScale],
|
||||
2 => &[Outlines, WallDetail, Lights, UiScale, RenderScale],
|
||||
3 => &[GameSpeed, Hints],
|
||||
_ => &[],
|
||||
}
|
||||
@@ -63,6 +64,7 @@ fn item_label(it: Item) -> String {
|
||||
Item::WallDetail => "set.wall_detail",
|
||||
Item::Lights => "set.lights",
|
||||
Item::UiScale => "set.ui_scale",
|
||||
Item::RenderScale => "set.render_scale",
|
||||
Item::GameSpeed => "set.game_speed",
|
||||
Item::Hints => "set.hints",
|
||||
})
|
||||
@@ -93,7 +95,14 @@ fn item_value(it: Item, s: &Settings) -> String {
|
||||
_ => "val.medium",
|
||||
}),
|
||||
Item::Lights => s.max_lights.to_string(),
|
||||
Item::UiScale => tr(if s.ui_scale >= 3 { "val.large" } else { "val.normal" }),
|
||||
Item::UiScale => {
|
||||
if s.ui_factor == 0 {
|
||||
trf("val.auto", &[crate::ui::F().to_string()])
|
||||
} else {
|
||||
format!("{}x", s.ui_factor)
|
||||
}
|
||||
}
|
||||
Item::RenderScale => format!("1/{}", s.render_scale.max(1)),
|
||||
Item::GameSpeed => format!("{}x", s.game_speed as i32),
|
||||
Item::Hints => onoff(s.hints),
|
||||
}
|
||||
@@ -126,7 +135,8 @@ fn item_change(it: Item, s: &mut Settings, d: i32) {
|
||||
Item::Outlines => s.outlines = !s.outlines,
|
||||
Item::WallDetail => s.wall_detail = cycle(&[1u8, 2, 3], s.wall_detail, d),
|
||||
Item::Lights => s.max_lights = cycle(&[8u8, 16, 24], s.max_lights, d),
|
||||
Item::UiScale => s.ui_scale = cycle(&[2u8, 3], s.ui_scale, d),
|
||||
Item::UiScale => s.ui_factor = cycle(&[0u8, 1, 2, 3, 4], s.ui_factor, d),
|
||||
Item::RenderScale => s.render_scale = cycle(&[1u8, 2, 3, 4], s.render_scale.max(1), d),
|
||||
Item::GameSpeed => s.game_speed = cycle(&[1.0f32, 2.0], s.game_speed, d),
|
||||
Item::Hints => s.hints = !s.hints,
|
||||
}
|
||||
@@ -285,8 +295,8 @@ impl Menu {
|
||||
pub fn draw(&mut self, gfx: &Gfx, settings: &Settings, status: Option<&str>) {
|
||||
self.buttons.clear();
|
||||
let Some(page) = self.page else { return };
|
||||
let sw = screen_width();
|
||||
let sh = screen_height();
|
||||
let sw = sw();
|
||||
let sh = sh();
|
||||
draw_rectangle(0.0, 0.0, sw, sh, Color::new(0.0, 0.0, 0.0, if page == Page::Title { 0.55 } else { 0.45 }));
|
||||
match page {
|
||||
Page::Title => self.draw_title(gfx),
|
||||
@@ -299,8 +309,8 @@ impl Menu {
|
||||
let a = all_actions()[action];
|
||||
let text = trf("set.press_key", &[action_label(a)]);
|
||||
let cols = (text.chars().count() as i32 + 8).max(36);
|
||||
let x = (sw - cols as f32 * CW()) * 0.5;
|
||||
let y = sh * 0.4;
|
||||
let x = grid_center(sw, cols as f32 * CW());
|
||||
let y = (sh * 0.4 / CH()).floor() * CH();
|
||||
gfx.window(x, y, cols, 5, S(), true, FRAME_HI, Color::new(0.05, 0.03, 0.04, 0.98));
|
||||
gfx.text(&text, x + CW() * 4.0, y + CH() * 1.5, S(), TXT);
|
||||
let esc = tr("set.esc_cancel");
|
||||
@@ -309,7 +319,7 @@ impl Menu {
|
||||
}
|
||||
if let Some(st) = status {
|
||||
let w = gfx.measure(st, S()) + CW() * 4.0;
|
||||
let x = (sw - w) * 0.5;
|
||||
let x = grid_center(sw, w);
|
||||
let y = sh - CH() * 9.0;
|
||||
draw_rectangle(x, y, w, CH() * 2.0, Color::new(0.05, 0.03, 0.04, 0.95));
|
||||
draw_rectangle_lines(x, y, w, CH() * 2.0, 2.0, GOLD);
|
||||
@@ -318,24 +328,24 @@ impl Menu {
|
||||
}
|
||||
|
||||
fn column(&mut self, gfx: &Gfx, x: f32, y: f32, w: f32, items: &[(Btn, String)]) {
|
||||
let (mx, my) = mouse_position();
|
||||
let (mx, my) = mouse();
|
||||
for (k, (b, label)) in items.iter().enumerate() {
|
||||
let r = Rect::new(x, y + k as f32 * CH() * 3.0, w, CH() * 2.2);
|
||||
let r = Rect::new(x, y + k as f32 * CH() * 3.0, w, CH() * 2.0);
|
||||
button(gfx, r, label, r.contains(vec2(mx, my)), FRAME);
|
||||
self.buttons.push((r, *b));
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_title(&mut self, gfx: &Gfx) {
|
||||
let sw = screen_width();
|
||||
let sh = screen_height();
|
||||
let big = (S() * 4.0).min((sw / (GW * 14.0)).floor()).max(S());
|
||||
let sw = sw();
|
||||
let sh = sh();
|
||||
let big = TITLE();
|
||||
let title = "DungeonCraft";
|
||||
let ty = sh * 0.12;
|
||||
gfx.text(title, (sw - gfx.measure(title, big)) * 0.5 + big, ty + big, big, Color::new(0.0, 0.0, 0.0, 0.8));
|
||||
gfx.text(title, (sw - gfx.measure(title, big)) * 0.5, ty, big, FRAME_HI);
|
||||
let ty = (sh * 0.12 / CH()).floor() * CH();
|
||||
gfx.text(title, grid_center(sw, gfx.measure(title, big)) + big, ty + big, big, Color::new(0.0, 0.0, 0.0, 0.8));
|
||||
gfx.text(title, grid_center(sw, gfx.measure(title, big)), ty, big, FRAME_HI);
|
||||
let sub = tr("menu.subtitle");
|
||||
gfx.text(&sub, (sw - gfx.measure(&sub, S())) * 0.5, ty + GH * big + CH(), S(), TXT);
|
||||
gfx.text(&sub, grid_center(sw, gfx.measure(&sub, S())), ty + GH * big + CH(), S(), TXT);
|
||||
let mut items = Vec::new();
|
||||
if self.has_autosave {
|
||||
items.push((Btn::Continue, tr("menu.continue")));
|
||||
@@ -346,35 +356,35 @@ impl Menu {
|
||||
items.push((Btn::Help, tr("menu.help")));
|
||||
let w = 26.0 * CW();
|
||||
let y = ty + GH * big + CH() * 4.0;
|
||||
self.column(gfx, (sw - w) * 0.5, y, w, &items);
|
||||
self.column(gfx, grid_center(sw, w), y, w, &items);
|
||||
let hint = tr("menu.drop_hint");
|
||||
gfx.text(&hint, (sw - gfx.measure(&hint, (S() / 2.0).max(1.0))) * 0.5, sh - CH() * 7.5, (S() / 2.0).max(1.0), DIM);
|
||||
gfx.text(&hint, grid_center(sw, gfx.measure(&hint, SMALL())), sh - CH() * 8.0, SMALL(), DIM);
|
||||
let credit = tr("help.font");
|
||||
gfx.text(&credit, (sw - gfx.measure(&credit, (S() / 2.0).max(1.0))) * 0.5, sh - CH() * 6.5, (S() / 2.0).max(1.0), DIM);
|
||||
gfx.text(&credit, grid_center(sw, gfx.measure(&credit, SMALL())), sh - CH() * 7.0, SMALL(), DIM);
|
||||
}
|
||||
|
||||
// New game: choose the map size (width and height separately)
|
||||
fn draw_new_game(&mut self, gfx: &Gfx, s: &Settings) {
|
||||
let sw = screen_width();
|
||||
let sh = screen_height();
|
||||
let (mx, my) = mouse_position();
|
||||
let sw = sw();
|
||||
let sh = sh();
|
||||
let (mx, my) = mouse();
|
||||
let cols = 44;
|
||||
let rows = 17;
|
||||
let x = (sw - cols as f32 * CW()) * 0.5;
|
||||
let y = ((sh - rows as f32 * CH()) * 0.5).max(0.0);
|
||||
let x = grid_center(sw, cols as f32 * CW());
|
||||
let y = (grid_center_y(sh, rows as f32 * CH())).max(0.0);
|
||||
gfx.window_titled(x, y, cols, rows, S(), true, FRAME_HI, Color::new(0.05, 0.03, 0.04, 0.97), &tr("menu.new"), FRAME_HI);
|
||||
let lines = [("newgame.width", s.map_w, 0), ("newgame.height", s.map_h, 1)];
|
||||
for (k, &(key, val, which)) in lines.iter().enumerate() {
|
||||
let by = y + CH() * (2.0 + k as f32 * 2.5);
|
||||
gfx.text(&tr(key), x + CW() * 3.0, by + CH() * 0.4, S(), TXT);
|
||||
gfx.text(&tr(key), x + CW() * 3.0, by + CH() * 0.5, S(), TXT);
|
||||
let vw = 14.0 * CW();
|
||||
let vx = x + cols as f32 * CW() - vw - CW() * 3.0;
|
||||
let left = Rect::new(vx, by, CW() * 2.5, CH() * 1.8);
|
||||
let right = Rect::new(vx + vw - CW() * 2.5, by, CW() * 2.5, CH() * 1.8);
|
||||
let left = Rect::new(vx, by, CW() * 2.5, CH() * 2.0);
|
||||
let right = Rect::new(vx + vw - CW() * 2.5, by, CW() * 2.5, CH() * 2.0);
|
||||
button(gfx, left, "<", left.contains(vec2(mx, my)), FRAME);
|
||||
button(gfx, right, ">", right.contains(vec2(mx, my)), FRAME);
|
||||
let v = val.to_string();
|
||||
gfx.text(&v, vx + (vw - gfx.measure(&v, S())) * 0.5, by + CH() * 0.4, S(), GOLD);
|
||||
gfx.text(&v, vx + (vw - gfx.measure(&v, S())) * 0.5, by + CH() * 0.5, S(), GOLD);
|
||||
let (l, r) = if which == 0 { (Btn::MapW(-1), Btn::MapW(1)) } else { (Btn::MapH(-1), Btn::MapH(1)) };
|
||||
self.buttons.push((left, l));
|
||||
self.buttons.push((right, r));
|
||||
@@ -389,8 +399,8 @@ impl Menu {
|
||||
}
|
||||
|
||||
fn draw_pause(&mut self, gfx: &Gfx) {
|
||||
let sw = screen_width();
|
||||
let sh = screen_height();
|
||||
let sw = sw();
|
||||
let sh = sh();
|
||||
let items = vec![
|
||||
(Btn::Resume, tr("menu.resume")),
|
||||
(Btn::Save, tr("menu.save")),
|
||||
@@ -401,21 +411,21 @@ impl Menu {
|
||||
];
|
||||
let cols = 32;
|
||||
let rows = items.len() as i32 * 3 + 4;
|
||||
let x = (sw - cols as f32 * CW()) * 0.5;
|
||||
let y = ((sh - rows as f32 * CH()) * 0.5).max(0.0);
|
||||
let x = grid_center(sw, cols as f32 * CW());
|
||||
let y = (grid_center_y(sh, rows as f32 * CH())).max(0.0);
|
||||
gfx.window_titled(x, y, cols, rows, S(), true, FRAME_HI, Color::new(0.05, 0.03, 0.04, 0.97), &tr("menu.paused"), FRAME_HI);
|
||||
let w = (cols - 6) as f32 * CW();
|
||||
self.column(gfx, x + CW() * 3.0, y + CH() * 2.0, w, &items);
|
||||
}
|
||||
|
||||
fn draw_settings(&mut self, gfx: &Gfx, s: &Settings, tab: usize) {
|
||||
let sw = screen_width();
|
||||
let sh = screen_height();
|
||||
let (mx, my) = mouse_position();
|
||||
let sw = sw();
|
||||
let sh = sh();
|
||||
let (mx, my) = mouse();
|
||||
let cols = ((sw / CW()) as i32 - 2).min(66);
|
||||
let rows = ((sh / CH()) as i32 - 2).min(34);
|
||||
let x = ((sw - cols as f32 * CW()) * 0.5).max(0.0);
|
||||
let y = ((sh - rows as f32 * CH()) * 0.5).max(0.0);
|
||||
let x = (grid_center(sw, cols as f32 * CW())).max(0.0);
|
||||
let y = (grid_center_y(sh, rows as f32 * CH())).max(0.0);
|
||||
gfx.window_titled(x, y, cols, rows, S(), true, FRAME_HI, Color::new(0.05, 0.03, 0.04, 0.97), &tr("menu.settings"), FRAME_HI);
|
||||
// tabs
|
||||
let mut tx = x + CW() * 2.0;
|
||||
@@ -437,7 +447,7 @@ impl Menu {
|
||||
let acts = all_actions();
|
||||
let half = (acts.len() + 1) / 2;
|
||||
let colw = (cols as f32 - 6.0) * CW() * 0.5;
|
||||
let rowh = CH() * 1.35;
|
||||
let rowh = CH();
|
||||
for (k, a) in acts.iter().enumerate() {
|
||||
let c = k / half;
|
||||
let r = k % half;
|
||||
@@ -456,32 +466,32 @@ impl Menu {
|
||||
self.buttons.push((rect, Btn::Key(k)));
|
||||
}
|
||||
let hint = tr("set.keys_hint");
|
||||
gfx.text(&hint, x + CW() * 2.0, top + half as f32 * rowh + CH() * 0.5, (S() / 2.0).max(1.0), DIM);
|
||||
gfx.text(&hint, x + CW() * 2.0, top + half as f32 * rowh + CH() * 0.5, SMALL(), DIM);
|
||||
let rw = gfx.measure(&tr("set.reset_keys"), S()) + CW() * 4.0;
|
||||
let rr = Rect::new(x + cols as f32 * CW() - rw - CW() * 2.0, y + (rows - 4) as f32 * CH(), rw, CH() * 2.2);
|
||||
let rr = Rect::new(x + cols as f32 * CW() - rw - CW() * 2.0, y + (rows - 4) as f32 * CH(), rw, CH() * 2.0);
|
||||
button(gfx, rr, &tr("set.reset_keys"), rr.contains(vec2(mx, my)), FRAME);
|
||||
self.buttons.push((rr, Btn::ResetKeys));
|
||||
} else {
|
||||
for (i, &it) in tab_items(tab).iter().enumerate() {
|
||||
let by = top + i as f32 * CH() * 2.5;
|
||||
gfx.text(&item_label(it), x + CW() * 3.0, by + CH() * 0.4, S(), TXT);
|
||||
gfx.text(&item_label(it), x + CW() * 3.0, by + CH() * 0.5, S(), TXT);
|
||||
// value with < > buttons
|
||||
let vw = 18.0 * CW();
|
||||
let vx = x + cols as f32 * CW() - vw - CW() * 3.0;
|
||||
let left = Rect::new(vx, by, CW() * 2.5, CH() * 1.8);
|
||||
let right = Rect::new(vx + vw - CW() * 2.5, by, CW() * 2.5, CH() * 1.8);
|
||||
let left = Rect::new(vx, by, CW() * 2.5, CH() * 2.0);
|
||||
let right = Rect::new(vx + vw - CW() * 2.5, by, CW() * 2.5, CH() * 2.0);
|
||||
button(gfx, left, "<", left.contains(vec2(mx, my)), FRAME);
|
||||
button(gfx, right, ">", right.contains(vec2(mx, my)), FRAME);
|
||||
let val = item_value(it, s);
|
||||
gfx.text(&val, vx + (vw - gfx.measure(&val, S())) * 0.5, by + CH() * 0.4, S(), GOLD);
|
||||
let mid = Rect::new(vx + CW() * 2.5, by, vw - CW() * 5.0, CH() * 1.8);
|
||||
gfx.text(&val, vx + (vw - gfx.measure(&val, S())) * 0.5, by + CH() * 0.5, S(), GOLD);
|
||||
let mid = Rect::new(vx + CW() * 2.5, by, vw - CW() * 5.0, CH() * 2.0);
|
||||
self.buttons.push((left, Btn::Change(i, -1)));
|
||||
self.buttons.push((right, Btn::Change(i, 1)));
|
||||
self.buttons.push((mid, Btn::Change(i, 1)));
|
||||
}
|
||||
}
|
||||
let bw = gfx.measure(&tr("menu.back"), S()) + CW() * 6.0;
|
||||
let br = Rect::new(x + CW() * 2.0, y + (rows - 4) as f32 * CH(), bw, CH() * 2.2);
|
||||
let br = Rect::new(x + CW() * 2.0, y + (rows - 4) as f32 * CH(), bw, CH() * 2.0);
|
||||
button(gfx, br, &tr("menu.back"), br.contains(vec2(mx, my)), FRAME);
|
||||
self.buttons.push((br, Btn::Back));
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user