High-res ores with big outlined lumps that stand out from the earth, kept in their own colours; screenshot tool for the desktop build
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
No files matched your search
@@ -122,3 +122,18 @@ python3 tools/make_tile.py /tmp/reinf-zoom.png reinforced --size 128 --colors 24
|
|||||||
The pixel-art atlas (16x16) does not use the picture (scaled that small it turns to mush): its
|
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
|
reinforced wall is drawn in code in the same spirit (`T_REINF` in `gen_atlas`: rubble stones
|
||||||
around a jittered grid, crimson-tinted mortar).
|
around a jittered grid, crimson-tinted mortar).
|
||||||
|
|
||||||
|
## Ores, second version (2026-11-08)
|
||||||
|
|
||||||
|
The first ore textures (`ore-v1/`) were hard to tell from plain earth in the game. Made again
|
||||||
|
from `earth-raw.png` with `fal-ai/nano-banana-pro/edit` (`1:1`, `1K`), bigger lumps with dark
|
||||||
|
outlines; the game keeps their colours (no matching to the pixel-art cells):
|
||||||
|
|
||||||
|
```text
|
||||||
|
Keep this seamless tileable earth texture (same soil colours, same style, same top-down view) and embed {ore} in it: about twenty lumps, many of them large, together covering about a third of the surface, spread evenly over the whole image including the edges, each lump with a dark outline so it stands out clearly from the soil. No veins, no lines - only separate lumps. Keep the texture seamless across all four edges. No text.
|
||||||
|
```
|
||||||
|
|
||||||
|
- gold: `big chunky shining yellow gold nuggets with bright highlights`
|
||||||
|
- silver: `big chunky shining white-silver ore nuggets with bright highlights`
|
||||||
|
- ruby: `big chunky glowing red ruby crystals with bright highlights`
|
||||||
|
- diamond: `big chunky sparkling pale-blue diamond crystals with bright highlights`
|
||||||
|
Before Width: | Height: | Size: 1.6 MiB After Width: | Height: | Size: 1.6 MiB |
|
Before Width: | Height: | Size: 360 KiB After Width: | Height: | Size: 353 KiB |
|
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 99 KiB |
|
Before Width: | Height: | Size: 90 KiB After Width: | Height: | Size: 92 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 1.6 MiB After Width: | Height: | Size: 1.6 MiB |
|
Before Width: | Height: | Size: 364 KiB After Width: | Height: | Size: 358 KiB |
|
Before Width: | Height: | Size: 99 KiB After Width: | Height: | Size: 99 KiB |
|
Before Width: | Height: | Size: 89 KiB After Width: | Height: | Size: 88 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 1.6 MiB |
|
After Width: | Height: | Size: 1.6 MiB |
|
After Width: | Height: | Size: 1.6 MiB |
|
After Width: | Height: | Size: 1.6 MiB |
|
Before Width: | Height: | Size: 1.6 MiB After Width: | Height: | Size: 1.6 MiB |
|
Before Width: | Height: | Size: 364 KiB After Width: | Height: | Size: 358 KiB |
|
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 98 KiB |
|
Before Width: | Height: | Size: 91 KiB After Width: | Height: | Size: 88 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 1.6 MiB After Width: | Height: | Size: 1.6 MiB |
|
Before Width: | Height: | Size: 361 KiB After Width: | Height: | Size: 360 KiB |
|
Before Width: | Height: | Size: 99 KiB After Width: | Height: | Size: 100 KiB |
|
Before Width: | Height: | Size: 89 KiB After Width: | Height: | Size: 90 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 99 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 99 KiB After Width: | Height: | Size: 99 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 98 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 99 KiB After Width: | Height: | Size: 100 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 25 KiB |
@@ -667,6 +667,10 @@ fn gen_hd_atlas(tile: u32) -> Image {
|
|||||||
groups.push((range(base, n), range(base, n)));
|
groups.push((range(base, n), range(base, n)));
|
||||||
}
|
}
|
||||||
for &(first, n, own, _, _, _) in HD_SETS.iter() {
|
for &(first, n, own, _, _, _) in HD_SETS.iter() {
|
||||||
|
// the ores keep their own bright colours: matched, the lumps would sink into the soil
|
||||||
|
if [T_GOLD, T_SILVER, T_RUBY, T_DIAMOND].contains(&first) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
groups.push((range(first, n), range(own, 5)));
|
groups.push((range(first, n), range(own, 5)));
|
||||||
}
|
}
|
||||||
for &(base, n, _, _) in HD_VARS.iter() {
|
for &(base, n, _, _) in HD_VARS.iter() {
|
||||||
|
|||||||
@@ -2188,7 +2188,7 @@ app.pending_briefing = briefing_of(&campaign, i);
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- self test and screenshots (desktop): DC_SHOT=demo or a savegame file starts it right
|
// ---- self test and screenshots (desktop): DC_SHOT=demo, title or a savegame file starts it right
|
||||||
// away, DC_SHOT_STYLE=hd|pixel picks the graphics style (not saved), DC_SHOT_AFTER seconds later
|
// away, DC_SHOT_STYLE=hd|pixel picks the graphics style (not saved), DC_SHOT_AFTER seconds later
|
||||||
// (default 10) the screen goes to DC_SHOT_OUT (default /tmp/dc-shot.png) and the game ends;
|
// (default 10) the screen goes to DC_SHOT_OUT (default /tmp/dc-shot.png) and the game ends;
|
||||||
// DC_SHOT_ZOOM sets the camera zoom, DC_SHOT_YAW its turn, DC_SHOT_AT=x,z where it looks;
|
// DC_SHOT_ZOOM sets the camera zoom, DC_SHOT_YAW its turn, DC_SHOT_AT=x,z where it looks;
|
||||||
@@ -2242,6 +2242,11 @@ impl ShotPlan {
|
|||||||
}
|
}
|
||||||
app.settings.hints = false;
|
app.settings.hints = false;
|
||||||
apply_settings(&app.settings, r3d);
|
apply_settings(&app.settings, r3d);
|
||||||
|
if self.load == "title" {
|
||||||
|
// the title screen as it is (its battle behind the menu)
|
||||||
|
self.started = Some(now);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
if self.load == "demo" {
|
if self.load == "demo" {
|
||||||
app.seed = next_seed(app.seed);
|
app.seed = next_seed(app.seed);
|
||||||
(app.game, app.cam) = Game::new_demo_map(app.seed);
|
(app.game, app.cam) = Game::new_demo_map(app.seed);
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Screenshots for docs/screenshots with the desktop build (no browser needed).
|
||||||
|
|
||||||
|
1. `cargo test --release --bins export_scenes -- --ignored` writes the scene savegames to /tmp/scenes.
|
||||||
|
2. `cargo build --profile desktop`
|
||||||
|
3. `python3 tools/shots_desktop.py [--style hd|pixel] [names...]` runs the game once per scene with
|
||||||
|
DC_SHOT (see the self test in src/main.rs), makes its window float at 1280x800 (Hyprland), and
|
||||||
|
writes docs/screenshots/<name>.png scaled to 1280x800. `title` is the title screen.
|
||||||
|
Needs Pillow (for the scaling) and Hyprland's hyprctl.
|
||||||
|
"""
|
||||||
|
import json, os, subprocess, sys, time
|
||||||
|
|
||||||
|
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
GAME = os.path.join(ROOT, "target", "desktop", "dungeoncraft")
|
||||||
|
|
||||||
|
|
||||||
|
def window_to_1280x800():
|
||||||
|
w = "class:miniquad-application"
|
||||||
|
for _ in range(50):
|
||||||
|
time.sleep(0.3)
|
||||||
|
out = subprocess.run(["hyprctl", "clients", "-j"], capture_output=True, text=True).stdout
|
||||||
|
if any(c.get("class") == "miniquad-application" for c in json.loads(out or "[]")):
|
||||||
|
break
|
||||||
|
for d in (f'hl.dsp.window.float({{ action = "on", window = "{w}" }})',
|
||||||
|
f'hl.dsp.window.resize({{ x = 1280, y = 800, relative = false, window = "{w}" }})',
|
||||||
|
f'hl.dsp.window.center({{ window = "{w}" }})'):
|
||||||
|
subprocess.run(["hyprctl", "dispatch", d], capture_output=True)
|
||||||
|
|
||||||
|
|
||||||
|
def shot(name, style, after=8):
|
||||||
|
out = f"/tmp/shot-{name}.png"
|
||||||
|
env = dict(os.environ, DC_SHOT="title" if name == "title" else f"/tmp/scenes/{name}.json",
|
||||||
|
DC_SHOT_STYLE=style, DC_SHOT_AFTER=str(after), DC_SHOT_OUT=out)
|
||||||
|
p = subprocess.Popen([GAME], env=env, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||||
|
window_to_1280x800()
|
||||||
|
p.wait(timeout=120)
|
||||||
|
from PIL import Image
|
||||||
|
im = Image.open(out).convert("RGB").resize((1280, 800), Image.LANCZOS)
|
||||||
|
dest = os.path.join(ROOT, "docs", "screenshots", ("title-battle" if name == "title" else name) + ".png")
|
||||||
|
im.save(dest, optimize=True)
|
||||||
|
print(dest)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
args = sys.argv[1:]
|
||||||
|
style = "hd"
|
||||||
|
if args[:1] == ["--style"]:
|
||||||
|
style, args = args[1], args[2:]
|
||||||
|
names = args or ["title"] + sorted(f[:-5] for f in os.listdir("/tmp/scenes") if f.endswith(".json"))
|
||||||
|
for n in names:
|
||||||
|
shot(n, style, 14 if n == "title" else 8)
|
||||||