Switch repo to English, add i18n with EN/DE language files and first-start language selection

- in-game texts moved to lang/en.lang and lang/de.lang (key = text, placeholders)
- game logic emits message keys + args, translated at display time
- localStorage access via own miniquad JS plugin (www/dc_plugin.js)
- code comments, DESIGN.md, CREDITS.md translated; README.md added

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Richard ZinkandClaude Opus 5.5 committed 2026-11-02 22:53:03 +01:00
1 parent a101f6ddc4
commit fbf6f223fc
18 files changed
+1072 -362

No files matched your search

+30 -30
View File
@@ -1,4 +1,4 @@
// Prozedural erzeugte Pixelart: Kachel-Atlas und Figuren-Sprites.
// Procedurally generated pixel art: tile atlas, unit sprites and the bitmap font.
use crate::game::EK;
use crate::map::Kind;
@@ -8,7 +8,7 @@ pub const CELL: f32 = 16.0;
const ATLAS_COLS: u32 = 16;
const ATLAS_ROWS: u32 = 5;
// Atlas-Indizes (jeweils 4 Varianten)
// atlas indices (4 variants each)
pub const T_ROCK: u32 = 0;
pub const T_EARTH: u32 = 4;
pub const T_GOLD: u32 = 8;
@@ -23,7 +23,7 @@ pub const T_LIBRARY: u32 = 40;
pub const T_COREFLOOR: u32 = 44;
pub const T_PORTALFLOOR: u32 = 48;
pub const T_GATEFLOOR: u32 = 52;
// Einzelzellen
// single cells
pub const T_FACE_EARTH: u32 = 56;
pub const T_FACE_ROCK: u32 = 57;
pub const T_FACE_GOLD: u32 = 58;
@@ -44,19 +44,19 @@ pub const I_SUMMON: u32 = 71;
pub struct Gfx {
pub atlas: Texture2D,
pub sprites: Texture2D,
pub font: Texture2D, // DINOBYTE, CP437, 16x16 Zeichen à 6x8 px
pub font: Texture2D, // DINOBYTE, CP437, 16x16 glyphs of 6x8 px
}
// ------------------------------------------------------------------ Bitmap-Schrift (CP437)
// ------------------------------------------------------------------ bitmap font (CP437)
pub const GW: f32 = 6.0; // Glyphbreite
pub const GH: f32 = 8.0; // Glyphhöhe
pub const GW: f32 = 6.0; // glyph width
pub const GH: f32 = 8.0; // glyph height
const CP437_LOW: &str = " ☺☻♥♦♣♠•◘○◙♂♀♪♫☼►◄↕‼¶§▬↨↑↓→←∟↔▲▼";
const CP437_HIGH: &str = "ÇüéâäàåçêëèïîìÄÅÉæÆôöòûùÿÖÜ¢£¥₧ƒáíóúñѪº¿⌐¬½¼¡«»░▒▓│┤╡╢╖╕╣║╗╝╜╛┐└┴┬├─┼╞╟╚╔╩╦╠═╬╧╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀αßΓπΣσµτΦΘΩδ∞φε∩≡±≥≤⌠⌡÷≈°∙·√ⁿ²■\u{a0}";
pub fn cp437(ch: char) -> u8 {
// In DINOBYTE sind ö (0x94) und ò (0x95) vertauscht
// DINOBYTE has ö (0x94) and ò (0x95) swapped
match ch {
'ö' => return 0x95,
'ò' => return 0x94,
@@ -88,7 +88,7 @@ pub fn cp437(ch: char) -> u8 {
}
}
// Rahmenzeichen: [oben-links, oben-rechts, unten-links, unten-rechts, waagrecht, senkrecht]
// frame glyphs: [top-left, top-right, bottom-left, bottom-right, horizontal, vertical]
pub const BOX_SINGLE: [u8; 6] = [0xDA, 0xBF, 0xC0, 0xD9, 0xC4, 0xB3];
pub const BOX_DOUBLE: [u8; 6] = [0xC9, 0xBB, 0xC8, 0xBC, 0xCD, 0xBA];
@@ -126,7 +126,7 @@ impl Canvas {
}
}
// Pflasterstein-Muster mit Fugen
// flagstone pattern with grout
fn stones(x: i32, y: i32, v: u32, base: (i32, i32, i32), grout: (i32, i32, i32), var: i32) -> Color {
let row = y / 8;
let off = if row % 2 == 1 { 4 } else { 0 };
@@ -136,7 +136,7 @@ fn stones(x: i32, y: i32, v: u32, base: (i32, i32, i32), grout: (i32, i32, i32),
}
let stone = hash((x + off) / 8, row, v + 3) % 5;
let d = (rnd(x, y, v) * var as f32) as i32 - var / 2 + stone as i32 * 3 - 6;
// Lichtkante oben/links, Schatten unten/rechts
// highlight top/left, shadow bottom/right
let edge = if y % 8 == 1 || sx % 8 == 1 {
10
} else if y % 8 == 7 || sx % 8 == 7 {
@@ -163,7 +163,7 @@ fn earth_px(x: i32, y: i32, v: u32) -> Color {
fn gen_atlas() -> Image {
let mut cv = Canvas { img: Image::gen_image_color((ATLAS_COLS * 16) as u16, (ATLAS_ROWS * 16) as u16, Color::new(0.0, 0.0, 0.0, 0.0)) };
for v in 0..4u32 {
// Fels
// rock
cv.cell(T_ROCK + v, |x, y| {
let blk = rnd(x / 4 + (y / 4) % 2, y / 4, v + 21) * 20.0 - 10.0;
let fine = rnd(x, y, v + 5) * 10.0 - 5.0;
@@ -174,9 +174,9 @@ fn gen_atlas() -> Image {
Some(shade((80, 80, 92), (blk + fine) as i32))
}
});
// Erde
// earth
cv.cell(T_EARTH + v, |x, y| Some(earth_px(x, y, v)));
// Gold
// gold
cv.cell(T_GOLD + v, |x, y| {
let mut best = 99.0f32;
for k in 0..6 {
@@ -198,7 +198,7 @@ fn gen_atlas() -> Image {
cv.cell(T_FLOOR + v, |x, y| Some(stones(x, y, v, (64, 56, 50), (34, 30, 28), 10)));
cv.cell(T_CLAIM + v, |x, y| {
let col = stones(x, y, v + 10, (78, 44, 58), (36, 20, 28), 10);
// kleines Hoheitszeichen in der Mitte jedes zweiten Steins
// small emblem in the middle of every other stone
if (x % 8 == 4 && y % 8 == 4) && hash(x / 8, y / 8, v) % 2 == 0 {
Some(c(150, 60, 80))
} else {
@@ -268,7 +268,7 @@ fn gen_atlas() -> Image {
}
});
}
// Wandfronten (Pseudo-3D)
// wall faces
cv.cell(T_FACE_EARTH, |x, y| {
let streak = (hash(x, 0, 70) % 3) as i32 * 5;
Some(shade((62, 42, 28), (rnd(x, y, 71) * 10.0) as i32 - streak - y / 2))
@@ -284,7 +284,7 @@ fn gen_atlas() -> Image {
Some(shade((62, 42, 28), (rnd(x, y, 75) * 10.0) as i32 - y / 2))
}
});
// Grab-Markierung: diagonale Streifen
// dig marker: diagonal stripes
cv.cell(T_MARK, |x, y| {
if (x + y) % 6 < 2 {
Some(Color::from_rgba(255, 220, 60, 150))
@@ -294,7 +294,7 @@ fn gen_atlas() -> Image {
None
}
});
// Bett
// bed
cv.cell(D_BED, |x, y| {
let dx = (x as f32 - 7.5) / 6.5;
let dy = (y as f32 - 8.0) / 5.5;
@@ -307,7 +307,7 @@ fn gen_atlas() -> Image {
None
}
});
// Pilze
// mushrooms
cv.cell(D_MUSHROOM, |x, y| {
let caps = [(4, 6, 3), (11, 5, 2), (8, 11, 3)];
for &(cx, cy, r) in &caps {
@@ -322,7 +322,7 @@ fn gen_atlas() -> Image {
}
None
});
// Trainingspuppe
// training dummy
cv.cell(D_DUMMY, |x, y| {
if (x == 7 || x == 8) && y >= 4 {
Some(c(90, 60, 36))
@@ -336,7 +336,7 @@ fn gen_atlas() -> Image {
None
}
});
// Goldhaufen
// gold pile
cv.cell(D_COINS, |x, y| {
let dx = (x as f32 - 7.5) / 7.0;
let dy = (y as f32 - 11.0) / 5.0;
@@ -348,7 +348,7 @@ fn gen_atlas() -> Image {
None
}
});
// Bücherregal
// bookshelf
cv.cell(D_SHELF, |x, y| {
if y > 9 || x == 0 || x == 15 {
return if y <= 10 { Some(c(60, 36, 22)) } else { None };
@@ -365,7 +365,7 @@ fn gen_atlas() -> Image {
_ => c(100, 60, 120),
})
});
// Werkzeug-Icons
// tool icons
cv.cell(I_PICK, |x, y| {
let handle = (x + y == 16 || x + y == 17) && x >= 3;
let head = (y == 3 || y == 4) && (2..=13).contains(&x) && (x - 8).abs() + (y - 2) * 2 < 9;
@@ -599,7 +599,7 @@ pub fn sprite_index(k: EK) -> u32 {
SPRITE_KINDS.iter().position(|&s| s == k).unwrap_or(0) as u32
}
// Zeile 0: normale Sprites, Zeile 1: weiße Silhouetten (Treffer-Blitz)
// row 0: normal sprites, row 1: white silhouettes (hit flash)
fn gen_sprites() -> Image {
let n = SPRITE_KINDS.len() as u32;
let mut img = Image::gen_image_color((n * SPR_CELL) as u16, (2 * SPR_CELL) as u16, Color::new(0.0, 0.0, 0.0, 0.0));
@@ -625,7 +625,7 @@ impl Gfx {
atlas.set_filter(FilterMode::Nearest);
let sprites = Texture2D::from_image(&gen_sprites());
sprites.set_filter(FilterMode::Nearest);
// Schrift laden; Hintergrund transparent machen, Glyphen weiß (zum Einfärben)
// load the font; transparent background, white glyphs (for tinting)
let mut img = Image::from_file_with_format(include_bytes!("../assets/dbyte_1x.png"), Some(ImageFormat::Png)).expect("Schrift");
for px in img.get_image_data_mut().iter_mut() {
let on = px[3] > 128 && px[0] > 128;
@@ -681,7 +681,7 @@ impl Gfx {
);
}
// Einzelnes CP437-Zeichen zeichnen (x, y = linke obere Ecke)
// draw a single CP437 glyph (x, y = top-left corner)
pub fn glyph(&self, code: u8, x: f32, y: f32, scale: f32, color: Color) {
let src = Rect::new((code % 16) as f32 * GW, (code / 16) as f32 * GH, GW, GH);
draw_texture_ex(
@@ -693,7 +693,7 @@ impl Gfx {
);
}
// Text zeichnen (x, y = linke obere Ecke); scale sollte ganzzahlig sein
// draw text (x, y = top-left corner); scale should be an integer
pub fn text(&self, s: &str, x: f32, y: f32, scale: f32, color: Color) {
let mut cx = x;
for ch in s.chars() {
@@ -708,13 +708,13 @@ impl Gfx {
s.chars().count() as f32 * GW * scale
}
// Text mit dunklem Schatten
// text with a dark shadow
pub fn text_sh(&self, s: &str, x: f32, y: f32, scale: f32, color: Color) {
self.text(s, x + scale, y + scale, scale, Color::new(0.0, 0.0, 0.0, color.a * 0.85));
self.text(s, x, y, scale, color);
}
// Fenster mit CP437-Rahmen; Größe in Zeichen (cols x rows inkl. Rahmen)
// window with a CP437 frame; size in characters (cols x rows incl. frame)
pub fn window(&self, x: f32, y: f32, cols: i32, rows: i32, scale: f32, double: bool, frame: Color, bg: Color) {
let cw = GW * scale;
let ch = GH * scale;
@@ -740,7 +740,7 @@ impl Gfx {
}
}
// Fenster mit Titel in der oberen Rahmenlinie
// window with a title in the top frame line
pub fn window_titled(&self, x: f32, y: f32, cols: i32, rows: i32, scale: f32, double: bool, frame: Color, bg: Color, title: &str, tcol: Color) {
self.window(x, y, cols, rows, scale, double, frame, bg);
if !title.is_empty() {