diff --git a/lang/de.lang b/lang/de.lang index 2eead33..5694db0 100644 --- a/lang/de.lang +++ b/lang/de.lang @@ -528,3 +528,4 @@ unit.owl = Arkane Eule unit.wyrm = Glutwyrm job.rest = Erholt sich im Horst job.torment = Bearbeitet einen Gefangenen +menu.license = DungeonCraft - MIT-Lizenz - (c) 2026 Richard Zink diff --git a/lang/en.lang b/lang/en.lang index 0876d68..e474f7d 100644 --- a/lang/en.lang +++ b/lang/en.lang @@ -529,3 +529,4 @@ unit.owl = Arcane Owl unit.wyrm = Ember Wyrm job.rest = Recovering in the aerie job.torment = Working on a prisoner +menu.license = DungeonCraft - MIT License - (c) 2026 Richard Zink diff --git a/src/main.rs b/src/main.rs index c833195..aa9db3b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -678,6 +678,7 @@ async fn main() { ui.show_help = false; autosave_t = 0.0; } + MenuAction::OpenRepo => platform::open_url(menu::REPO_URL), MenuAction::EditName => { if let Some(n) = platform::prompt(&i18n::tr("saves.name"), &menu.save_name) { menu.save_name = n.chars().filter(|c| !c.is_control()).take(40).collect(); diff --git a/src/menu.rs b/src/menu.rs index fb63855..bbc467b 100644 --- a/src/menu.rs +++ b/src/menu.rs @@ -201,8 +201,11 @@ enum Btn { Mission(usize), StartMission, Goals, + Repo, } +pub const REPO_URL: &str = "https://git.familie-zink.org/zink/dungeoncraft"; + pub enum MenuAction { None, Consumed, @@ -221,6 +224,7 @@ pub enum MenuAction { CloudDelete(String), EditName, // the name field was tapped (touch: ask with a browser dialog) StartMission(usize), + OpenRepo, } pub struct Menu { @@ -403,6 +407,7 @@ impl Menu { MenuAction::Consumed } Some(Btn::NameField) => MenuAction::EditName, + Some(Btn::Repo) => MenuAction::OpenRepo, Some(Btn::Campaign) => { self.page = Some(Page::Campaign); self.scroll = 0; @@ -502,6 +507,22 @@ impl Menu { gfx.text(&hint, grid_center(sw, gfx.measure(&hint, SMALL())), sh - CH() * 8.0, SMALL(), DIM); let credit = tr("help.font"); gfx.text(&credit, grid_center(sw, gfx.measure(&credit, SMALL())), sh - CH() * 7.0, SMALL(), DIM); + self.draw_license(gfx); + } + + // licence and repository link at the bottom of the screen (title and login) + pub fn draw_license(&mut self, gfx: &Gfx) { + let (sw, sh) = (sw(), sh()); + let (mx, my) = mouse(); + let lic = tr("menu.license"); + gfx.text(&lic, grid_center(sw, gfx.measure(&lic, SMALL())), sh - CH() * 2.5, SMALL(), DIM); + let url = REPO_URL.trim_start_matches("https://"); + let w = gfx.measure(url, SMALL()); + let r = Rect::new(grid_center(sw, w), sh - CH() * 1.5, w, GH * SMALL() + 2.0); + let hot = r.contains(vec2(mx, my)); + gfx.text(url, r.x, r.y, SMALL(), if hot { FRAME_HI } else { Color::new(0.5, 0.7, 1.0, 1.0) }); + draw_rectangle(r.x, r.y + GH * SMALL(), r.w, (SMALL()).max(1.0), if hot { FRAME_HI } else { Color::new(0.5, 0.7, 1.0, 0.6) }); + self.buttons.push((r, Btn::Repo)); } // New game: choose the map size (width and height separately) diff --git a/src/platform.rs b/src/platform.rs index 059bab8..332cc47 100644 --- a/src/platform.rs +++ b/src/platform.rs @@ -15,6 +15,7 @@ mod imp { fn dc_storage_get(key: *const u8, key_len: u32) -> i32; fn dc_storage_take(dst: *mut u8); fn dc_coarse_pointer() -> i32; + fn dc_open_url(u: *const u8, ul: u32); fn dc_prompt(t: *const u8, tl: u32, d: *const u8, dl: u32) -> i32; fn dc_storage_set(key: *const u8, key_len: u32, val: *const u8, val_len: u32); fn dc_cache_set(key: *const u8, key_len: u32, val: *const u8, val_len: u32); @@ -62,6 +63,10 @@ mod imp { unsafe { dc_coarse_pointer() != 0 } } + pub fn open_url(url: &str) { + unsafe { dc_open_url(url.as_ptr(), url.len() as u32) } + } + // asks for a text with the browser's dialog; None when cancelled pub fn prompt(title: &str, default: &str) -> Option { unsafe { @@ -122,6 +127,7 @@ mod imp { pub fn coarse_pointer() -> bool { false } + pub fn open_url(_url: &str) {} pub fn storage_set(key: &str, val: &str) { STORE.with(|s| s.borrow_mut().insert(key.to_string(), val.to_string())); } diff --git a/www/dc_plugin.js b/www/dc_plugin.js index 7b8badd..05896e1 100644 --- a/www/dc_plugin.js +++ b/www/dc_plugin.js @@ -109,6 +109,10 @@ pending = new TextEncoder().encode(v); return pending.length; }; + // opens a web page in a new tab (repository link) + env.dc_open_url = function (up, ul) { + try { window.open(str(up, ul), "_blank", "noopener"); } catch (e) { } + }; // the device's main pointer is a finger (phones, tablets) env.dc_coarse_pointer = function () { try { return window.matchMedia("(pointer: coarse)").matches ? 1 : 0; } catch (e) { return 0; }