diff --git a/art/generated/textures/PROMPTS.md b/art/generated/textures/PROMPTS.md index 6c6a5cf..69e7d78 100644 --- a/art/generated/textures/PROMPTS.md +++ b/art/generated/textures/PROMPTS.md @@ -101,3 +101,24 @@ python3 tools/make_tile.py -raw.png --size 128 --colors 32 --band 1 `-v64.png` / `-v128.png` are copied to `assets/textures/`; `-v128-preview.png` shows a floor laid out at random. Water and lava keep their 2x2 blocks (they flow). + +## Reinforced wall (2026-11-07) + +Roughly hewn stone instead of bricks. `fal-ai/flux-2-pro` (`square_hd`, `seed: 4244`), the +sentence followed by the shared style text: + +```text +A uniform all-over pattern of a fortified dungeon wall seen straight on: roughly hewn, irregular grey stone blocks of clearly different sizes and shapes, chiselled rough faces with tool marks and chipped edges, fitted tightly with thin dark mortar that has a faint dark-crimson tint, very solid and heavy - not bricks, not a regular brick pattern, no frame, evenly spread from edge to edge. +``` + +The stones came out small, so the middle of the picture (pixels 100-500) is used: + +```bash +python3 -I -c "from PIL import Image; Image.open('reinforced-raw.png').crop((100,100,500,500)).save('/tmp/reinf-zoom.png')" +python3 tools/make_tile.py /tmp/reinf-zoom.png reinforced --size 256 --colors 32 --band 28 --variants 5 --bright 1.25 +python3 tools/make_tile.py /tmp/reinf-zoom.png reinforced --size 128 --colors 24 --band 14 --variants 5 --bright 1.25 +``` + +The pixel-art atlas (16x16) does not use the picture (scaled that small it turns to mush): its +reinforced wall is drawn in code in the same spirit (`T_REINF` in `gen_atlas`: rubble stones +around a jittered grid, crimson-tinted mortar). diff --git a/art/generated/textures/reinforced-raw.png b/art/generated/textures/reinforced-raw.png new file mode 100644 index 0000000..690d583 Binary files /dev/null and b/art/generated/textures/reinforced-raw.png differ diff --git a/art/generated/textures/reinforced-v128-preview.png b/art/generated/textures/reinforced-v128-preview.png new file mode 100644 index 0000000..81b2caa Binary files /dev/null and b/art/generated/textures/reinforced-v128-preview.png differ diff --git a/art/generated/textures/reinforced-v128.png b/art/generated/textures/reinforced-v128.png new file mode 100644 index 0000000..b524996 Binary files /dev/null and b/art/generated/textures/reinforced-v128.png differ diff --git a/art/generated/textures/reinforced-v64-preview.png b/art/generated/textures/reinforced-v64-preview.png new file mode 100644 index 0000000..ca325e5 Binary files /dev/null and b/art/generated/textures/reinforced-v64-preview.png differ diff --git a/art/generated/textures/reinforced-v64.png b/art/generated/textures/reinforced-v64.png new file mode 100644 index 0000000..5e27982 Binary files /dev/null and b/art/generated/textures/reinforced-v64.png differ diff --git a/assets/textures/reinforced-v128.png b/assets/textures/reinforced-v128.png new file mode 100644 index 0000000..b524996 Binary files /dev/null and b/assets/textures/reinforced-v128.png differ diff --git a/assets/textures/reinforced-v64.png b/assets/textures/reinforced-v64.png new file mode 100644 index 0000000..5e27982 Binary files /dev/null and b/assets/textures/reinforced-v64.png differ diff --git a/src/gfx.rs b/src/gfx.rs index 9a35b13..b3584fb 100644 --- a/src/gfx.rs +++ b/src/gfx.rs @@ -363,7 +363,7 @@ const HD_DECALS: [(u32, &[u8], &[u8]); 2] = [ (D_RUBBLE, include_bytes!("../assets/textures/decal-rubble.png"), include_bytes!("../assets/textures/decal-rubble-128.png")), (D_CRACK, include_bytes!("../assets/textures/decal-crack.png"), include_bytes!("../assets/textures/decal-crack-128.png")), ]; -const HD_SETS: [(u32, u32, u32, &[u8], &[u8], [u32; 5]); 11] = [ +const HD_SETS: [(u32, u32, u32, &[u8], &[u8], [u32; 5]); 12] = [ (T_CLAIM, 4, T_HDSET, include_bytes!("../assets/textures/floor-player.png"), include_bytes!("../assets/textures/floor-player-128.png"), [1; 5]), (T_RIVAL1, 4, T_HDSET + 5, include_bytes!("../assets/textures/floor-mossgrim.png"), include_bytes!("../assets/textures/floor-mossgrim-128.png"), [1; 5]), (T_RIVAL2, 4, T_HDSET + 10, include_bytes!("../assets/textures/floor-nightwell.png"), include_bytes!("../assets/textures/floor-nightwell-128.png"), [1; 5]), @@ -375,6 +375,7 @@ const HD_SETS: [(u32, u32, u32, &[u8], &[u8], [u32; 5]); 11] = [ (T_SILVER, 2, T_HDSET + 40, include_bytes!("../assets/textures/silver-v64.png"), include_bytes!("../assets/textures/silver-v128.png"), [1; 5]), (T_RUBY, 2, T_HDSET + 45, include_bytes!("../assets/textures/ruby-v64.png"), include_bytes!("../assets/textures/ruby-v128.png"), [1; 5]), (T_DIAMOND, 2, T_HDSET + 50, include_bytes!("../assets/textures/diamond-v64.png"), include_bytes!("../assets/textures/diamond-v128.png"), [1; 5]), + (T_REINF, 4, T_HDSET + 55, include_bytes!("../assets/textures/reinforced-v64.png"), include_bytes!("../assets/textures/reinforced-v128.png"), [1; 5]), ]; // the cell a tile at (x, z) shows in the high-res style: the kinds with a set pick one of its @@ -652,14 +653,41 @@ fn gen_atlas() -> Image { }); // reinforced walls: masonry in dark stone with the player's red mortar for v in 0..4u32 { + // reinforced wall: roughly hewn grey stones of different sizes (a jittered grid of + // stone centres, their borders the mortar; wrapped, so the cell is seamless), dark mortar + // with a crimson tint, the upper rim of every stone lit, the lower rim in shadow cv.cell(T_REINF + v, |x, y| { - let row = y / 4; - let off = if row % 2 == 1 { 4 } else { 0 }; - if y % 4 == 3 || (x + off) % 8 == 7 { - return Some(shade((70, 26, 30), (rnd(x, y, v + 90) * 8.0) as i32)); + // (distance to the nearest centre, to the second nearest, the nearest stone, its centre's y) + let mut best = (99.0f32, 99.0f32, 0i32, 0.0f32); + for gy in 0..3 { + for gx in 0..3 { + let cx = gx as f32 * 5.33 + 1.0 + (hash(gx, gy, v + 95) % 4) as f32; + let cy = gy as f32 * 5.33 + 1.0 + (hash(gy, gx, v + 96) % 4) as f32; + for wx in [-16.0f32, 0.0, 16.0] { + for wy in [-16.0f32, 0.0, 16.0] { + // stones a little wider than high, like coursed rubble + let (dx, dy) = ((x as f32 + 0.5 - cx - wx) * 0.8, y as f32 + 0.5 - cy - wy); + let d = (dx * dx + dy * dy).sqrt(); + if d < best.0 { + best = (d, best.0, gy * 3 + gx, cy + wy); + } else if d < best.1 { + best.1 = d; + } + } + } + } } - let brick = hash((x + off) / 8, row, v + 91) % 4; - Some(shade((84, 76, 74), (rnd(x, y, v + 92) * 14.0) as i32 - 7 + brick as i32 * 5 - 6 + if y % 4 == 0 { 10 } else { 0 })) + let (d1, d2, id, cy) = best; + if d2 - d1 < 0.9 { + return Some(shade((44, 30, 34), (rnd(x, y, v + 97) * 8.0) as i32)); + } + let tone = (hash(id, 7, v + 98) % 5) as i32 * 5 - 10; + let rim = if d2 - d1 < 2.0 { + if (y as f32 + 0.5) < cy { 14 } else { -14 } + } else { + 0 + }; + Some(shade((92, 90, 96), tone + rim + (rnd(x, y, v + 99) * 10.0) as i32 - 5)) }); // torture chamber: dark stones with rusty iron grates cv.cell(T_TORTURE + v, |x, y| { @@ -2830,6 +2858,32 @@ mod hd_atlas_tests { } } +#[cfg(test)] +mod atlas_dump { + use super::*; + + // `cargo test --bins dump_atlas_cell -- --ignored` writes a 16x16 cell kind (env DUMP_CELL, + // default T_REINF: its four variants at random, 6x4 tiles) scaled up 8x to /tmp/atlas-cell.png + #[test] + #[ignore] + fn dump_atlas_cell() { + let base: u32 = std::env::var("DUMP_CELL").ok().and_then(|v| v.parse().ok()).unwrap_or(T_REINF); + let atlas = gen_atlas(); + let (w, h) = (6 * 16 * 8, 4 * 16 * 8); + let mut out = Image::gen_image_color(w as u16, h as u16, BLACK); + for t in 0..24u32 { + let id = base + hash(t as i32, 3, 5) % 4; + let (ox, oy) = ((id % ATLAS_COLS) * 16, (id / ATLAS_COLS) * 16); + for y in 0..128 { + for x in 0..128 { + out.set_pixel((t % 6) * 128 + x, h - 1 - ((t / 6) * 128 + y), atlas.get_pixel(ox + x / 8, oy + y / 8)); + } + } + } + out.export_png("/tmp/atlas-cell.png"); + } +} + #[cfg(test)] mod sprite_tests { use super::*;