Five freely combinable variants for floor, earth, rock and ore textures

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Richard ZinkandClaude Opus 5.5 committed 2026-11-07 22:02:55 +01:00
1 parent ad668ec123
commit 362f4e7670
59 files changed
+101 -40

No files matched your search

+42 -38
View File
@@ -6,7 +6,7 @@ use macroquad::prelude::*;
pub const CELL: f32 = 16.0;
pub const ATLAS_COLS: u32 = 16;
pub const ATLAS_ROWS: u32 = 16; // rows 14-15: cells only the high-res atlases fill (see HD_FLOORS)
pub const ATLAS_ROWS: u32 = 18; // rows 14-17: cells only the high-res atlases fill (see HD_SETS)
// atlas indices (4 variants each)
pub const T_ROCK: u32 = 0;
@@ -337,39 +337,42 @@ fn earth_px(x: i32, y: i32, v: u32) -> Color {
// tiles and has no seams), for kinds with two variants one seamless tile for both.
pub const HD_TILE: u32 = 64;
pub const HD_TILE2: u32 = 128;
// (first cell, variants, 64x64 level, 128x128 level)
const HD_TILES: [(u32, u32, &[u8], &[u8]); 9] = [
(T_FLOOR, 4, include_bytes!("../assets/textures/floor.png"), include_bytes!("../assets/textures/floor-256.png")),
(T_EARTH, 4, include_bytes!("../assets/textures/earth.png"), include_bytes!("../assets/textures/earth-256.png")),
(T_ROCK, 4, include_bytes!("../assets/textures/rock.png"), include_bytes!("../assets/textures/rock-256.png")),
(T_GOLD, 4, include_bytes!("../assets/textures/gold.png"), include_bytes!("../assets/textures/gold-256.png")),
// the liquids: (first cell, variants, 64x64 level, 128x128 level); a seamless 2x2 block of
// tiles each (variant k = quarter k % 2, k / 2), which the shader lays out by position while
// the texture flows
const HD_TILES: [(u32, u32, &[u8], &[u8]); 2] = [
(T_WATER, 4, include_bytes!("../assets/textures/water.png"), include_bytes!("../assets/textures/water-256.png")),
(T_LAVA, 4, include_bytes!("../assets/textures/lava.png"), include_bytes!("../assets/textures/lava-256.png")),
(T_SILVER, 2, include_bytes!("../assets/textures/silver.png"), include_bytes!("../assets/textures/silver-128.png")),
(T_RUBY, 2, include_bytes!("../assets/textures/ruby.png"), include_bytes!("../assets/textures/ruby-128.png")),
(T_DIAMOND, 2, include_bytes!("../assets/textures/diamond.png"), include_bytes!("../assets/textures/diamond-128.png")),
];
// The masters' claimed floors in the high-res style: the same grey, worn-smooth castle
// flagstones for everybody (cut so they fit in any order), every tile with a polished inlay in
// its master's colour and an engraving of their own (the player: a demon skull, Mossgrim: a
// mushroom, Lady Nightwell: a lily under the moon, Saffrax: an alchemical sun). Five tiles each:
// plain, cracked, chipped, broken, scratched. They go into cells of their own (from T_HDFLOOR on,
// five per master); a tile picks one by its position, weighted (plain ones most often).
// (the claimed floor's cells, the first own cell, 64x64 tiles side by side, 128x128 ones, weights)
pub const T_HDFLOOR: u32 = 224;
const HD_FLOORS: [(u32, u32, &[u8], &[u8], [u32; 5]); 4] = [
(T_CLAIM, T_HDFLOOR, include_bytes!("../assets/textures/floor-player.png"), include_bytes!("../assets/textures/floor-player-128.png"), [4, 2, 2, 1, 3]),
(T_RIVAL1, T_HDFLOOR + 5, include_bytes!("../assets/textures/floor-mossgrim.png"), include_bytes!("../assets/textures/floor-mossgrim-128.png"), [4, 2, 2, 1, 3]),
(T_RIVAL2, T_HDFLOOR + 10, include_bytes!("../assets/textures/floor-nightwell.png"), include_bytes!("../assets/textures/floor-nightwell-128.png"), [4, 2, 2, 1, 3]),
(T_RIVAL3, T_HDFLOOR + 15, include_bytes!("../assets/textures/floor-saffrax.png"), include_bytes!("../assets/textures/floor-saffrax-128.png"), [4, 2, 2, 1, 3]),
// Sets of five tiles that fit together in any order, so a floor or a wall shows no pattern:
// the ground kinds (all five tiles share one seamless border, their middles differ; see
// tools/make_tile.py --variants) and the masters' claimed floors (the same grey, worn-smooth
// castle flagstones for everybody, every tile with a polished inlay in its master's colour and
// an engraving of their own: the player a demon skull, Mossgrim a mushroom, Lady Nightwell a lily
// under the moon, Saffrax an alchemical sun; plain, cracked, chipped, broken, scratched). They go
// into cells of their own from T_HDSET on, five per set; a tile picks one by its position,
// weighted. (the kind's cells: first and count, the first own cell, 64x64 tiles side by side,
// 128x128 ones, weights)
pub const T_HDSET: u32 = 224;
const HD_SETS: [(u32, u32, u32, &[u8], &[u8], [u32; 5]); 11] = [
(T_CLAIM, 4, T_HDSET, include_bytes!("../assets/textures/floor-player.png"), include_bytes!("../assets/textures/floor-player-128.png"), [4, 2, 2, 1, 3]),
(T_RIVAL1, 4, T_HDSET + 5, include_bytes!("../assets/textures/floor-mossgrim.png"), include_bytes!("../assets/textures/floor-mossgrim-128.png"), [4, 2, 2, 1, 3]),
(T_RIVAL2, 4, T_HDSET + 10, include_bytes!("../assets/textures/floor-nightwell.png"), include_bytes!("../assets/textures/floor-nightwell-128.png"), [4, 2, 2, 1, 3]),
(T_RIVAL3, 4, T_HDSET + 15, include_bytes!("../assets/textures/floor-saffrax.png"), include_bytes!("../assets/textures/floor-saffrax-128.png"), [4, 2, 2, 1, 3]),
(T_FLOOR, 4, T_HDSET + 20, include_bytes!("../assets/textures/floor-v64.png"), include_bytes!("../assets/textures/floor-v128.png"), [1; 5]),
(T_EARTH, 4, T_HDSET + 25, include_bytes!("../assets/textures/earth-v64.png"), include_bytes!("../assets/textures/earth-v128.png"), [1; 5]),
(T_ROCK, 4, T_HDSET + 30, include_bytes!("../assets/textures/rock-v64.png"), include_bytes!("../assets/textures/rock-v128.png"), [1; 5]),
(T_GOLD, 4, T_HDSET + 35, include_bytes!("../assets/textures/gold-v64.png"), include_bytes!("../assets/textures/gold-v128.png"), [1; 5]),
(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]),
];
// the cell a tile at (x, z) shows in the high-res style: generated textures with four variants
// are laid out by the position (see HD_TILES), the masters' floors pick one of their five slabs
// (HD_FLOORS), everything else stays
// the cell a tile at (x, z) shows in the high-res style: the kinds with a set pick one of its
// five tiles (HD_SETS), the liquids are laid out by the position (HD_TILES), everything else stays
pub fn hd_tile(id: u32, x: i32, z: i32) -> u32 {
if let Some(&(_, own, _, _, w)) = HD_FLOORS.iter().find(|f| (f.0..f.0 + 4).contains(&id)) {
if let Some(&(_, _, own, _, _, w)) = HD_SETS.iter().find(|f| (f.0..f.0 + f.1).contains(&id)) {
let h = (x as u32).wrapping_mul(73_856_093) ^ (z as u32).wrapping_mul(19_349_663);
let h = (h ^ (h >> 13)).wrapping_mul(0x5bd1_e995);
let mut r = (h >> 8) % w.iter().sum::<u32>();
@@ -410,8 +413,8 @@ fn gen_hd_atlas(tile: u32) -> Image {
}
}
}
for &(_, own, png, png2, _) in HD_FLOORS.iter() {
let src = Image::from_file_with_format(if tile == HD_TILE2 { png2 } else { png }, Some(ImageFormat::Png)).expect("hd floor");
for &(_, _, own, png, png2, _) in HD_SETS.iter() {
let src = Image::from_file_with_format(if tile == HD_TILE2 { png2 } else { png }, Some(ImageFormat::Png)).expect("hd set");
for v in 0..5 {
let id = own + v;
let (ox, oy) = ((id % ATLAS_COLS) * tile, (id / ATLAS_COLS) * tile);
@@ -2768,8 +2771,8 @@ mod hd_atlas_tests {
assert_eq!(px(&hd, ox + 7, oy + 40), px(&src, sx + 7, sy + 40));
}
}
// the masters' floors: five slabs each in their own cells (inside the atlas)
for &(_, own, png, png2, _) in HD_FLOORS.iter() {
// the sets: five tiles each in their own cells (inside the atlas)
for &(_, _, own, png, png2, _) in HD_SETS.iter() {
let src = Image::from_file_with_format(if tile == HD_TILE2 { png2 } else { png }, Some(ImageFormat::Png)).unwrap();
assert_eq!((src.width as u32, src.height as u32), (5 * tile, tile));
assert!(own + 5 <= ATLAS_COLS * ATLAS_ROWS);
@@ -2785,17 +2788,18 @@ mod hd_atlas_tests {
for z in 0..60 {
for x in 0..60 {
let id = hd_tile(T_CLAIM + (x as u32 % 4), x, z);
assert!((T_HDFLOOR..T_HDFLOOR + 5).contains(&id));
n[(id - T_HDFLOOR) as usize] += 1;
assert!((T_HDSET..T_HDSET + 5).contains(&id));
n[(id - T_HDSET) as usize] += 1;
}
}
assert!(n.iter().all(|&k| k > 100), "{n:?}");
assert!(n[0] > n[3] * 2, "{n:?}");
assert!((T_HDFLOOR + 15..T_HDFLOOR + 20).contains(&hd_tile(T_RIVAL3 + 2, 3, 7)));
// four variants by position, two variants keep theirs, others stay
assert_eq!(hd_tile(T_FLOOR + 3, 0, 0), T_FLOOR);
assert_eq!(hd_tile(T_FLOOR, 1, 1), T_FLOOR + 3);
assert_eq!(hd_tile(T_RUBY + 1, 1, 0), T_RUBY + 1);
assert!((T_HDSET + 15..T_HDSET + 20).contains(&hd_tile(T_RIVAL3 + 2, 3, 7)));
assert!((T_HDSET + 50..T_HDSET + 55).contains(&hd_tile(T_DIAMOND + 1, 8, 2)));
assert!((T_HDSET + 25..T_HDSET + 30).contains(&hd_tile(T_EARTH + 3, 5, 5)));
// the liquids: four variants by position; others stay
assert_eq!(hd_tile(T_WATER + 3, 0, 0), T_WATER);
assert_eq!(hd_tile(T_LAVA, 1, 1), T_LAVA + 3);
assert_eq!(hd_tile(T_TREASURY + 2, 1, 0), T_TREASURY + 2);
}
}