Claimed floors reworked: roughly hewn castle flagstones worn smooth, no visible tile grid

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Richard ZinkandClaude Opus 5.5 committed 2026-11-07 21:52:33 +01:00
1 parent aab466cfbe
commit 72c44ae8d0
35 files changed
+54 -28

No files matched your search

+20 -15
View File
@@ -1,53 +1,58 @@
# The masters' claimed floors (high-res) # The masters' claimed floors (high-res)
Generated on 2026-11-07 with fal.ai: `fal-ai/nano-banana-pro`, `aspect_ratio: 3:2`, Generated on 2026-11-07 with fal.ai: `fal-ai/nano-banana-pro`, `aspect_ratio: 3:2`,
`resolution: 2K`, one image per master. Each sheet has six slabs in a 3x2 grid on magenta; five `resolution: 2K`, one image per master. Each sheet has six tiles in a 3x2 grid on magenta; tiles
are used (`HD_FLOORS` in `src/gfx.rs`, with weights so plain slabs come more often). 1-5 are used (plain, engraving, cracks, chipped, broken; `HD_FLOORS` in `src/gfx.rs`, weighted
4:1:3:2:1 so plain tiles come most often).
The first version (`v1/`) had regular slabs with thick grout frames and looked too even; this
one asks for roughly hewn castle flagstones worn smooth by many feet.
Prompt template (`{floor}` per master): Prompt template (`{floor}` per master):
```text ```text
A texture sheet of six square floor tiles for a dark-fantasy dungeon strategy game, arranged in a grid of 3 columns and 2 rows, the tiles separated by thin flat pure magenta (#FF00FF) gaps, magenta also around the outside. Every tile is seen straight from above (orthographic top-down, no perspective), flat even lighting, no shadows, and fills its square completely. Every tile is one square floor slab made of a few fitted flagstones, with a dark grout line along all four edges, so the tiles fit together in any arrangement. All six tiles use the same stone, colours and style: {floor} The six tiles differ: tile 1 intact and clean; tile 2 with a large carved engraving in the middle; tile 3 with fine hairline cracks; tile 4 with a chipped corner and a few missing stone splinters; tile 5 with a broken flagstone, cracked through, a piece sunk and loose rubble; tile 6 with small engravings and worn edges. Style: hand-painted pixel art, chunky clear shapes, crisp edges, readable at small size. No text, no labels, no numbers. A texture sheet of six square floor tiles for a dark-fantasy dungeon strategy game, arranged in a grid of 3 columns and 2 rows, the tiles separated by thin flat pure magenta (#FF00FF) gaps, magenta also around the outside. Every tile is seen straight from above (orthographic top-down, no perspective), flat even lighting, no shadows, and fills its square completely. Every tile is a piece of the floor of an old medieval castle dungeon: irregular, roughly hewn flagstones of clearly different sizes and shapes (some large, some small, uneven outlines, slightly rounded chipped edges), laid with uneven mortar joints of varying width - not a regular grid. Centuries of feet have worn the stones smooth and polished: soft sheen on the walked-on surfaces, rounded worn edges, shallow dips. Along the four edges of every tile runs a mortar joint as thin and irregular as the ones inside, so the tiles fit together in any arrangement without a visible grid. All six tiles use the same stone, colours and style: {floor} The six tiles differ: tile 1 plain worn stones; tile 2 with a carved engraving on one large stone, worn half smooth; tile 3 with hairline cracks; tile 4 with a chipped stone corner and missing splinters; tile 5 with a broken flagstone, cracked through, a piece sunk and loose rubble; tile 6 with a few small worn engravings. Style: hand-painted pixel art, crisp edges, muted natural stone colours, readable at small size. No text, no labels, no numbers.
``` ```
## player ## player
```text ```text
polished flagstones of medium crimson-red stone with a purple tint (clearly brighter than black), the engravings are glowing-edged demonic runes, horned skulls and pentagram-like sigils of an evil dungeon master. dark red-brown stone with a crimson-purple tint (medium brightness, not black); the engravings are worn demonic runes and horned skulls of an evil dungeon master, the grooves faintly glowing red.
``` ```
## mossgrim ## mossgrim
```text ```text
damp mossy green-grey flagstones; the engravings show mushrooms, spores and coiling mould tendrils, green fungus and moss grow out of the grout lines and cracks, a mouldy cellar master's floor. grey-green stone, damp; moss and small fungi grow in the mortar joints and cracks; the engravings are worn mushrooms and coiling mould tendrils.
``` ```
## nightwell ## nightwell
```text ```text
elegant deep-blue marble flagstones with fine silver veins, the engravings are refined lilies, crescent moons and elegant curling filigree inlaid with silver, the floor of a cold, cultivated and vain lady of the night. cold blue-grey slate and marble stones with faint silver veins; the engravings are worn lilies and crescent moons with remnants of silver inlay.
``` ```
## saffrax ## saffrax
```text ```text
warm ochre-yellow sandstone flagstones with brass inlays, the engravings are alchemical sigils, suns and sulphur symbols, a few scorch marks, the floor of a greedy alchemist master of sulphur and gold. warm ochre-brown sandstone with a few brass studs; the engravings are worn alchemical sigils and suns, a few sooty scorch marks and yellow sulphur stains.
``` ```
## Processing ## Processing
`tools/make_floor_tiles.py` crops the slabs, fills magenta inside them (chipped corners, holes) `tools/make_floor_tiles.py` crops the tiles, fills magenta inside them (chipped corners, holes)
with a dark void, brightens if asked, scales and reduces the colours: with a dark void, cuts a thin edge off (the joints and frames the model drew around every tile),
evens out the light (no vignette, all tiles equally bright, so the grid does not show), scales
and reduces the colours:
```bash ```bash
T=tools/make_floor_tiles.py T=tools/make_floor_tiles.py
python3 $T player-raw.png player --grid 2x3 --pick 1,2,3,5,6 --size 128 --bright 1.45 # and --size 64 python3 $T player-raw.png player --grid 2x3 --pick 1,2,3,4,5 --size 128 --bright 1.1 --inset 0.03 --flatten
python3 $T mossgrim-raw.png mossgrim --grid 2x3 --pick 1,2,3,4,5 --size 128 python3 $T mossgrim-raw.png mossgrim --grid 2x3 --pick 1,2,3,4,5 --size 128 --inset 0.03 --flatten
python3 $T nightwell-raw.png nightwell --grid 2x3 --pick 1,2,3,4,5 --size 128 --bright 1.15 python3 $T nightwell-raw.png nightwell --grid 2x3 --pick 1,2,3,4,5 --size 128 --inset 0.045 --flatten
python3 $T saffrax-raw.png saffrax --grid 2x3 --pick 1,2,3,4,5 --size 128 --bright 0.82 python3 $T saffrax-raw.png saffrax --grid 2x3 --pick 1,2,3,4,5 --size 128 --inset 0.03 --flatten
# the same with --size 64
``` ```
The player's sheet came out too dark (brightened by 1.45); its fourth slab repeats the big skull
with a chipped corner and is left out. Saffrax' sandstone was too glaring (darkened by 0.82).
`<name>-64.png` / `<name>-128.png` are copied to `assets/textures/floor-<name>.png` / `<name>-64.png` / `<name>-128.png` are copied to `assets/textures/floor-<name>.png` /
`floor-<name>-128.png`; `<name>-128-preview.png` shows a floor laid out at random. `floor-<name>-128.png`; `<name>-128-preview.png` shows a floor laid out at random.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 277 KiB

After

Width:  |  Height:  |  Size: 368 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 MiB

After

Width:  |  Height:  |  Size: 4.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 397 KiB

After

Width:  |  Height:  |  Size: 310 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 KiB

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 114 KiB

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 MiB

After

Width:  |  Height:  |  Size: 4.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 327 KiB

After

Width:  |  Height:  |  Size: 333 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 105 KiB

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 MiB

After

Width:  |  Height:  |  Size: 4.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 326 KiB

After

Width:  |  Height:  |  Size: 330 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 KiB

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 MiB

After

Width:  |  Height:  |  Size: 4.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 KiB

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 28 KiB

+9 -8
View File
@@ -350,21 +350,22 @@ const HD_TILES: [(u32, u32, &[u8], &[u8]); 9] = [
(T_DIAMOND, 2, include_bytes!("../assets/textures/diamond.png"), include_bytes!("../assets/textures/diamond-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: five single slabs each (with a grout line // The masters' claimed floors in the high-res style: five single tiles each of roughly hewn,
// along the edges, so they fit in any order), in the master's colour with engravings of their // worn-smooth castle flagstones (cut so they fit in any order), in the master's colour with
// own and some of them cracked, chipped or broken. They go into cells of their own (from // engravings of their own and some of them cracked, chipped or broken. They go into cells of their own (from
// T_HDFLOOR on, five per master); a tile picks one by its position, weighted (plain slabs more // T_HDFLOOR on, five per master); a tile picks one by its position, weighted (plain slabs more
// often than the big engravings and the broken ones). // often than the big engravings and the broken ones).
// (the claimed floor's cells, the first own cell, 64x64 slabs side by side, 128x128 ones, weights) // (the claimed floor's cells, the first own cell, 64x64 slabs side by side, 128x128 ones, weights)
pub const T_HDFLOOR: u32 = 224; pub const T_HDFLOOR: u32 = 224;
const HD_FLOORS: [(u32, u32, &[u8], &[u8], [u32; 5]); 4] = [ const HD_FLOORS: [(u32, u32, &[u8], &[u8], [u32; 5]); 4] = [
// the player: crimson stone, demonic runes and horned skulls (plain, big engraving, cracks, broken, small engravings) // every floor: plain, engraving, cracks, chipped, broken
(T_CLAIM, T_HDFLOOR, include_bytes!("../assets/textures/floor-player.png"), include_bytes!("../assets/textures/floor-player-128.png"), [4, 1, 3, 1, 2]), // the player: red-brown stone, demonic runes and horned skulls
// Mossgrim: mossy green slabs, mushrooms and mould (plain, big engraving, cracks, chipped, broken) (T_CLAIM, T_HDFLOOR, include_bytes!("../assets/textures/floor-player.png"), include_bytes!("../assets/textures/floor-player-128.png"), [4, 1, 3, 2, 1]),
// Mossgrim: damp grey-green stone, moss in the joints, mushrooms and mould
(T_RIVAL1, T_HDFLOOR + 5, include_bytes!("../assets/textures/floor-mossgrim.png"), include_bytes!("../assets/textures/floor-mossgrim-128.png"), [4, 1, 3, 2, 1]), (T_RIVAL1, T_HDFLOOR + 5, include_bytes!("../assets/textures/floor-mossgrim.png"), include_bytes!("../assets/textures/floor-mossgrim-128.png"), [4, 1, 3, 2, 1]),
// Lady Nightwell: blue marble, silver lilies and moons // Lady Nightwell: blue-grey slate and marble, lilies and moons
(T_RIVAL2, T_HDFLOOR + 10, include_bytes!("../assets/textures/floor-nightwell.png"), include_bytes!("../assets/textures/floor-nightwell-128.png"), [4, 1, 3, 2, 1]), (T_RIVAL2, T_HDFLOOR + 10, include_bytes!("../assets/textures/floor-nightwell.png"), include_bytes!("../assets/textures/floor-nightwell-128.png"), [4, 1, 3, 2, 1]),
// Saffrax: ochre sandstone, brass and alchemical sigils // Saffrax: ochre sandstone, brass studs, alchemical sigils and suns
(T_RIVAL3, T_HDFLOOR + 15, include_bytes!("../assets/textures/floor-saffrax.png"), include_bytes!("../assets/textures/floor-saffrax-128.png"), [4, 1, 3, 2, 1]), (T_RIVAL3, T_HDFLOOR + 15, include_bytes!("../assets/textures/floor-saffrax.png"), include_bytes!("../assets/textures/floor-saffrax-128.png"), [4, 1, 3, 2, 1]),
]; ];
+25 -5
View File
@@ -6,10 +6,13 @@ palette shared by all tiles of the sheet.
Usage: Usage:
make_floor_tiles.py raw.png out_prefix --grid 2x3 --pick 1,2,3,5,6 [--size 128] make_floor_tiles.py raw.png out_prefix --grid 2x3 --pick 1,2,3,5,6 [--size 128]
[--colors 48] [--bright 1.0] [--colors 48] [--bright 1.0] [--inset 0.0] [--flatten]
--pick which cells to keep, numbered row by row from 1 --pick which cells to keep, numbered row by row from 1
--bright factor on the colours (above 1: lighter) --bright factor on the colours (above 1: lighter)
--inset cut this share off every side (a frame or edge joint the model drew around the tile)
--flatten even out the light: no vignette towards the edges, all tiles equally bright, so
the grid of tiles does not show on the floor
Writes out_prefix-<size>.png: the picked tiles side by side, and out_prefix-<size>-preview.png Writes out_prefix-<size>.png: the picked tiles side by side, and out_prefix-<size>-preview.png
(a 6x4 floor of them laid out at random, scaled up 2x). Needs Pillow. (a 6x4 floor of them laid out at random, scaled up 2x). Needs Pillow.
@@ -17,7 +20,7 @@ Writes out_prefix-<size>.png: the picked tiles side by side, and out_prefix-<siz
import argparse import argparse
import random import random
from PIL import Image, ImageEnhance from PIL import Image, ImageEnhance, ImageFilter, ImageStat
VOID = (22, 14, 18) VOID = (22, 14, 18)
@@ -36,6 +39,8 @@ def main():
ap.add_argument("--size", type=int, default=128) ap.add_argument("--size", type=int, default=128)
ap.add_argument("--colors", type=int, default=48) ap.add_argument("--colors", type=int, default=48)
ap.add_argument("--bright", type=float, default=1.0) ap.add_argument("--bright", type=float, default=1.0)
ap.add_argument("--inset", type=float, default=0.0)
ap.add_argument("--flatten", action="store_true")
a = ap.parse_args() a = ap.parse_args()
rows, cols = (int(v) for v in a.grid.split("x")) rows, cols = (int(v) for v in a.grid.split("x"))
pick = [int(v) - 1 for v in a.pick.split(",")] pick = [int(v) - 1 for v in a.pick.split(",")]
@@ -56,9 +61,24 @@ def main():
for x in range(tile.width): for x in range(tile.width):
if magenta(tp[x, y]): if magenta(tp[x, y]):
tp[x, y] = VOID tp[x, y] = VOID
if a.bright != 1.0: if a.inset > 0:
tile = ImageEnhance.Brightness(tile).enhance(a.bright) d = int(tile.width * a.inset)
tiles.append(tile.resize((a.size, a.size), Image.LANCZOS)) tile = tile.crop((d, d, tile.width - d, tile.height - d))
tiles.append(tile)
if a.flatten:
# divide by the large-scale brightness: every tile gets the same, even brightness (the
# sheet's mean), so neither a vignette nor a lighter or darker tile shows the grid
blurs = [t.convert("L").filter(ImageFilter.GaussianBlur(t.width / 5)) for t in tiles]
mean = sum(ImageStat.Stat(b).mean[0] for b in blurs) / len(blurs)
for t, blur in zip(tiles, blurs):
bp, tp = blur.load(), t.load()
for y in range(t.height):
for x in range(t.width):
f = mean / max(bp[x, y], 1)
tp[x, y] = tuple(min(255, int(c * f)) for c in tp[x, y])
if a.bright != 1.0:
tiles = [ImageEnhance.Brightness(t).enhance(a.bright) for t in tiles]
tiles = [t.resize((a.size, a.size), Image.LANCZOS) for t in tiles]
strip = Image.new("RGB", (a.size * len(tiles), a.size)) strip = Image.new("RGB", (a.size * len(tiles), a.size))
for k, t in enumerate(tiles): for k, t in enumerate(tiles):