Detailed imp-style sprites for all 30 creatures in the high-res style; sprite texture in bands

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Richard ZinkandClaude Opus 5.5 committed 2026-11-07 23:21:58 +01:00
1 parent 210f17773a
commit 01e5326721
132 files changed
+1295 -43

No files matched your search

+1
View File
@@ -6,3 +6,4 @@ www/mq_js_bundle.js
play/
dist/
art/generated/creature-previews/
art/generated/sprites/*-raw*.png
+401
View File
@@ -0,0 +1,401 @@
# Creature sprites in the imp's style (fal.ai)
Generated on 2026-11-07: one idle sprite sheet per creature kind of `SPRITE_KINDS` in
`src/gfx.rs` (all except `EK::Worker`, the imp, see `../imp-sprite/`), in the style of the imp's
`raw-1.png` lower row. Each creature has `<id>-32.png` (160x32) and `<id>-64.png` (320x64): five
views side by side (front, front three-quarter turned right, side facing right, back
three-quarter turned away to the right, back), transparent, one palette per sheet, feet (or the
lowest pixel) on the bottom row, the tallest or widest view filling the cell. Ids are the
`unit.<id>` keys; `manifest.json` maps them to the `EK` variants with a quality note.
## Generation
- Model: `fal-ai/nano-banana-pro/edit` (POST https://fal.run/fal-ai/nano-banana-pro/edit)
- `aspect_ratio: 21:9`, `resolution: 2K`, `num_images: 1`, `output_format: png`
- Reference image (`image_urls`, as a data URI): `style-ref.png` = the lower row of
`../imp-sprite/raw-1.png` (crop fractions 0,0.27,1,0.72) scaled to 1600 px wide, the dark
cell frame lines painted over with magenta (the kobold, the first request, still had a
reference with frame lines and copied them).
- Background: flat magenta, flat green (#00FF00) for the purple creatures (spider, mage, bat, trader).
- 34 images: 30 creatures + 4 retries (spider, dwarf, revenant, ram; each had the wrong
number or order of views). For dwarf and ram the retry gave the missing back three-quarter
view but lost another one, so both tries are combined. About $5 at ~$0.15 per 2K image.
- Scripts in `scripts/`: `creatures.py` (descriptions, prompt template), `gen.py` (requests,
4 at a time; reads the key from `~/.config/dungeoncraft/fal-key`), `compose.py` (sets chosen
figures of one or more raw sheets into one new row), `process.py` (runs the pixelize tool for
all creatures with the arguments of `extra.txt`).
Even with "exactly five figures in one row" in the prompt the model often drew two rows (mostly
two copies of the five views, then the top row is used with `--crop 0,0,1,0.5`) or skipped
the back three-quarter view.
### Prompt template
`{thing}` is `creature` or `siege engine`; `{pose}` is `standing, idle`, `hovering in the air,
idle` (bat, gargoyle, owl, wyrm, fairy) or `standing still` (ram, catapult); `{bgname}` is
`magenta (#FF00FF)` or `green (#00FF00)`. The kobold and the first spider request were sent
without "exactly five figures in one row (no second row, no extra figures)," (the layout line
read "the same five views in one single horizontal row,").
```text
The attached image is the style reference: a pixel art sprite sheet of the "Pit Imp" in five views (left to right: front, front three-quarter turned right, side facing right, back three-quarter turned away to the right, back).
Draw a DIFFERENT {thing} in exactly the same style: the "{name}". {desc}
Use exactly the same style as the reference: the same compact chibi proportions (big head, short stocky body, about the same overall size as the imp), the same thick one-pixel near-black outline, the same palette approach (flat colours with one highlight and one shadow tone), the same pixel size (about 32x32 pixels per figure, scaled up with hard square pixels), no anti-aliasing, no gradients, no blur.
Layout: exactly five figures in one single horizontal row (no second row, no extra figures), the same five views, in the same order (1 front view facing the viewer, 2 front three-quarter view turned to the right, 3 side view facing right, 4 back three-quarter view turned away to the right, 5 back view), evenly spaced, all the same size, {pose}, each figure separated from its neighbours by empty background.
Background: flat pure {bgname} everywhere, no ground, no shadows, no text, no labels, no frames, no grid lines, no cell borders.
```
Retries (dwarf, revenant) appended:
```text
Important: there must be exactly FIVE figures in ONE row. Do not skip the fourth one: the back three-quarter view, seen from behind at an angle, the face hidden, the body turned away from the viewer towards the upper right, between the side view and the straight back view.
```
The ram retry appended instead:
```text
Important: there must be exactly FIVE drawings of the machine in ONE row, no second row. 1 front view: the iron-capped end of the ram points straight at the viewer. 2 front three-quarter view: the capped end points towards the viewer and to the right. 3 side view: the capped end points to the right. 4 back three-quarter view: the capped end points away from the viewer towards the upper right, we see the rear end of the machine. 5 back view: we see the rear end straight on.
```
## Pixel grid
`tools/pixelize_sprite.py` (Pillow: `/tmp/sprvenv/bin/python -I`), from the repo root:
```bash
T=tools/pixelize_sprite.py
python $T art/generated/sprites/<id>-raw.png art/generated/sprites/<id> --views 5 --size 32 --colors 16 --feet 31 --outline-weight 0.6 [extra]
python $T art/generated/sprites/<id>-raw.png art/generated/sprites/<id> --views 5 --size 64 --colors 24 --feet 63 --outline-weight 0.6 [extra]
```
(the tool also writes `<id>-<size>-preview.png`; those were deleted). `[extra]` per creature is
listed below. Composed raws were made first with `scripts/compose.py` (run from `scripts/`).
### Tool changes (backwards compatible)
- Background key `--bg auto` (default): the key colours are sampled from the sheet's border and
only colours close to them (and their blends with the dark outline) are removed, so purple
creatures on green survive; `--bg magenta` is the old fixed test, `--bg R,G,B` an explicit key.
- Blends of the key colour with thin figure parts (pinkish edge pixels on a spear) get no vote.
- Frame/grid lines (a column or row at least 97% one flat non-background colour) are removed,
plus their anti-aliased edges.
- `--split auto` (default): figures are found by the empty columns between them, else by
connected parts, else by cutting touching figures at their thinnest column, else equal cells
(`--split cells`, the old behaviour); small pieces of a neighbour at a cell edge are dropped.
- `--row-counts 4,3` (rows with different numbers of figures) and `--pick 0,1,2,m5,3` (build
the output row from chosen figures, `m` mirrors).
- Single pixels without any neighbour are removed (`--no-despeckle` keeps them).
- The imp's `raw-1.png` gives the same sheet as before (42 of 5120 pixels differ in coverage).
## Creatures
### kobold - Kobold (`EK::Kobold`)
Description (standing, magenta background): A small scaly lizard-folk warrior: short and wiry, big lizard-like head with a short snout, bright green scaly skin, glowing yellow eyes, a toothy eager grin with sharp little teeth, a simple dark-green tunic, clawed bare feet, a short tail. It holds an upright wooden spear with a steel tip in its right hand.
Raw: kobold-raw.png
Extra pixelize arguments: none
Result: ok - good - chibi lizard, spear in all views; raw has frame lines (removed by the tool)
### spider - Cave Spider (`EK::Spider`)
Description (standing, green background): A big dungeon spider: a big round body and head in dark violet-purple with lighter purple highlights, a cluster of glowing yellow eyes on the front of the head, eight thick black jointed legs spread around it, small fangs. Low to the ground, compact and menacing, cute-chibi proportions with an oversized head.
Raw: spider-raw-try1.png (2 rows of 3, dropped), spider-raw.png (retry, 2 rows of 4, used)
Extra pixelize arguments: `--grid 2x4 --pick 0,1,2,3,3`
Result: ok - usable - both tries came as 2 rows without a back three-quarter view; the back view is used twice (views 4 and 5 identical)
### mage - Shadow Mage (`EK::Mage`)
Description (standing, green background): A hooded dark sorcerer: a big pointed purple hood that hides most of the oversized head, the face in shadow with only a pale chin and two glowing yellow eyes visible, dark-purple robes with light violet trim down to the feet, a wooden staff with a glowing yellow gem on top held in the right hand.
Raw: mage-raw.png
Extra pixelize arguments: none
Result: ok - good - green key, purple hood and robe survive, glowing eyes, gem staff
### ogre - Stone Ogre (`EK::Ogre`)
Description (standing, magenta background): A hulking ogre with skin like rock: broad and hunched, almost no neck, skin of grey stone with darker grey cracks and patches, small glowing yellow eyes, a heavy jaw with an underbite, enormous arms and fists reaching low, short thick legs, a ragged brown loincloth. No weapon - it fights with its fists. Chibi-stocky, as wide as it is tall.
Raw: ogre-raw.png
Extra pixelize arguments: `--crop 0,0,1,0.5`
Result: ok - good - stocky stone ogre; raw has 2 rows (top row used); back three-quarter drawn almost as a side view
### knight - Knight (`EK::Knight`)
Description (standing, magenta background): A heavily armoured knight of the holy Order: a big polished silver great helm with a dark visor slit, silver shoulder plates and greaves, a blue tabard with a yellow sun emblem on the chest over chainmail, dark boots, a short straight steel sword held upright in the right hand.
Raw: knight-raw.png
Extra pixelize arguments: none
Result: ok - good - great helm, blue tabard with sun, sword
### archer - Archer (`EK::Archer`)
Description (standing, magenta background): A hero archer of the Order: a little green hat with a peak, a friendly human face with brown hair, a green tunic with a brown belt, brown trousers and leather boots, a quiver of arrows on the back, holding a wooden bow upright in the left hand.
Raw: archer-raw.png
Extra pixelize arguments: none
Result: ok - good - a bit taller than the imp, bow and quiver readable
### priest - Priest (`EK::Priest`)
Description (standing, magenta background): A healing priest of the Order: a human with short brown hair and a kind face, a long plain white robe down to the feet with a golden sun emblem on the chest, a small golden halo ring floating above the head, hands folded in prayer.
Raw: priest-raw.png
Extra pixelize arguments: none
Result: ok - good - white robe, sun emblem, floating halo
### dwarf - Dwarf Miner (`EK::Dwarf`)
Description (standing, magenta background): A stout dwarf miner of the Order: very short and broad, a grey steel helmet, a big bushy orange beard, a skin-coloured nose and eyes peeking out, a brown leather jerkin and belt, brown trousers and boots, a steel pickaxe with a wooden handle over the right shoulder.
Raw: dwarf-raw-try1.png, dwarf-raw-try2.png (retry) -> composed dwarf-raw.png
Compose: `python compose.py art/generated/sprites/dwarf-raw.png art/generated/sprites/dwarf-raw-try1.png:1:4:0 art/generated/sprites/dwarf-raw-try1.png:1:4:1 art/generated/sprites/dwarf-raw-try1.png:1:4:2 art/generated/sprites/dwarf-raw-try2.png:1:4:3 art/generated/sprites/dwarf-raw-try1.png:1:4:3`
Extra pixelize arguments: none
Result: ok - good - composed from two tries (each had only 4 views): try 1 front/front 3/4/side/back + try 2 back three-quarter
### paladin - Paladin of Light (`EK::Paladin`)
Description (standing, magenta background): A big glowing holy champion: a golden helmet with a dark visor, shining white plate armour with golden trim and a golden sun on the chest, golden greaves, broad shoulders, a white holy sword with a golden hilt held upright in the right hand. Bulkier than the other characters but still chibi.
Raw: paladin-raw.png
Extra pixelize arguments: none
Result: ok - good - white/gold armour, big sword; very bright
### jailer - Jailer (`EK::Jailer`)
Description (standing, magenta background): A big quiet dungeon jailer: broad-shouldered and heavy, a grey leather executioner's hood with eye holes and glowing yellow eyes, grey-skinned bare muscular arms, a dark-red leather apron over the chest and belly, heavy boots, a coiled iron chain hanging from the right hand.
Raw: jailer-raw.png
Extra pixelize arguments: none
Result: ok - good - hood, grey arms, red apron, chain
### priestess - Flame Priestess (`EK::Priestess`)
Description (standing, magenta background): A fire priestess of the dungeon: a woman with fair skin, flame-orange hair rising like fire, a long dark-red robe to the feet with an orange flame emblem on the chest, a small burning fireball floating above her raised right hand.
Raw: priestess-raw.png
Extra pixelize arguments: none
Result: ok - good - flame hair, red robe, fireball
### chainbreaker - Chainbreaker (`EK::Chainbreaker`)
Description (standing, magenta background): A legendary obsidian demon warrior: a muscular demon of black obsidian with glowing orange-lava cracks across the body, glowing yellow eyes, short curved horns, broken iron shackles with dangling chain links on both wrists, a steel battle axe with a light grey blade in each hand, heavy clawed feet.
Raw: chainbreaker-raw.png
Extra pixelize arguments: `--crop 0,0,1,0.5`
Result: ok - good - obsidian with lava cracks, horns, axes; raw has 2 rows (top row used); dark at 32 px
### bat - Giant Bat (`EK::Bat`)
Description (hovering, green background): A big dungeon bat: a round furry dark-purple body with lighter purple highlights, leathery purple wings spread out with black finger bones, big pointed ears, small glowing red eyes, a little fanged mouth, tiny clawed feet hanging down. Hovering in the air, wings spread but kept compact within its own cell.
Raw: bat-raw.png
Extra pixelize arguments: `--crop 0,0,1,0.5`
Result: ok - good - green key, purple body and wings survive; raw has 2 rows (top row used)
### gargoyle - Chain Gargoyle (`EK::Gargoyle`)
Description (hovering, magenta background): A winged stone gargoyle: a crouching, hunched creature of carved grey stone with darker grey cracks, stone bat wings with pale tips, short curved horns, glowing yellow eyes, a grim mouth with fangs, clawed hands and feet, an iron chain wrapped around one arm. Hovering in the air.
Raw: gargoyle-raw.png
Extra pixelize arguments: `--crop 0,0,1,0.5`
Result: ok - good - grey stone, wings, chain; raw has 2 rows (top row used)
### owl - Arcane Owl (`EK::Owl`)
Description (hovering, magenta background): A wise magical owl: a round brown-feathered owl with lighter brown and cream breast feathers, tufted ear feathers, huge glowing yellow eyes with black pupils, a small yellow beak, wings with deep blue magical tips, yellow talons, a judging expression. Hovering in the air with its wings half spread.
Raw: owl-raw.png
Extra pixelize arguments: `--crop 0,0,1,0.5`
Result: ok - good - round chibi owl, blue wing tips; raw has 2 rows (top row used)
### wyrm - Ember Wyrm (`EK::Wyrm`)
Description (hovering, magenta background): A small fire dragon: a compact red dragon with dark-red scales on the back, a glowing orange belly, dark-red bat wings, a single white horn, glowing yellow eyes, a curled tail, clawed legs, small embers of fire at its mouth. Hovering in the air.
Raw: wyrm-raw.png
Extra pixelize arguments: none
Result: ok - good - red dragon, orange belly, white horn; tall and thin because of the hanging tail
### cogling - Cogling (`EK::Cogling`)
Description (standing, magenta background): A sooty little goblin tinkerer: tiny and round, burnt-orange sooty skin, big brass goggles with glowing yellow lenses over the eyes, a grey work overall over an orange shirt, small boots, a steel hammer held up in the right hand, a few small brass cogs on the belt.
Raw: cogling-raw.png
Extra pixelize arguments: `--crop 0,0,1,0.5`
Result: ok - good - goggles, overall, hammer; raw has 2 rows (top row used)
### revenant - Revenant (`EK::Revenant`)
Description (standing, magenta background): An undead warrior: a gaunt pale corpse with a bony white skull face, hollow eye sockets with glowing green grave-light, tattered grey rags and old rusty armour pieces, thin bony arms and legs, a rusty steel sword held in the right hand.
Raw: revenant-raw-try1.png (4 views, dropped), revenant-raw.png (retry, used)
Extra pixelize arguments: none
Result: ok - good - second try (first had only 4 views); big skull, green eyes, rusty sword
### giant - Giant (`EK::Giant`)
Description (standing, magenta background): A hero giant of the Order: a big broad human brute with fair skin, a small bald head with a dumb frown and small eyes, a brown leather jerkin over a blue shirt, bare muscular arms, heavy brown boots, a big wooden tree-trunk club resting on the right shoulder. Bulkier than the other characters but still chibi.
Raw: giant-raw.png
Extra pixelize arguments: none
Result: ok - good - bald brute with club; drawn chibi, so not much bigger than the others (the game scales it)
### fairy - Fairy Spy (`EK::Fairy`)
Description (hovering, magenta background): A tiny glowing fairy informer: a small figure with fair skin, blonde hair and small pointed ears, glowing yellow eyes, a short blue dress, two pairs of translucent pale-blue dragonfly wings, a few golden sparkles below the feet. Hovering in the air.
Raw: fairy-raw.png
Extra pixelize arguments: none
Result: ok - good - blue dress, pale wings; a few gold sparkles below the feet are part of the sprite, so the bottom row is the sparkles, not the feet
### battlemage - Battle Mage (`EK::BattleMage`)
Description (standing, magenta background): A fire mage of the Order: a tall pointed blue wizard hat, a human face with a stern look and a short grey beard, long blue robes with white trim and a white sash, a wooden staff in the right hand with a burning orange-yellow flame on top.
Raw: battlemage-raw.png
Extra pixelize arguments: none
Result: ok - good - blue hat and robes, burning staff, grey beard
### ram - Battering Ram (`EK::Ram`)
Description (siege engine, magenta background): A wooden siege battering ram, no people visible: a compact wooden frame on four solid wooden wheels with a slanted brown plank roof; under the roof hangs a thick tree trunk with a big grey iron cap on its front end. Brown and dark-brown wood, iron rivets. Chunky toy-like proportions. Show the same five views of the machine (front, front three-quarter turned right, side facing right, back three-quarter, back).
Raw: ram-raw-try1.png, ram-raw-try2.png (retry) -> composed ram-raw.png
Compose: `python compose.py art/generated/sprites/ram-raw.png art/generated/sprites/ram-raw-try1.png:2:3,3:0 art/generated/sprites/ram-raw-try1.png:2:3,3:2 art/generated/sprites/ram-raw-try1.png:2:3,3:1 art/generated/sprites/ram-raw-try2.png:2:3,2:4 art/generated/sprites/ram-raw-try1.png:2:3,3:5`
Extra pixelize arguments: none
Result: ok - usable - composed from two tries (both 2 rows, views out of order): try 1 front/front 3/4/side/back + try 2 back three-quarter; the side view is wide, the 'back' view of try 1 may show the front end again
### catapult - Catapult (`EK::Catapult`)
Description (siege engine, magenta background): A wooden siege catapult, no people visible: a low rectangular wooden base on four wooden wheels, a wooden throwing arm angled upward with a cup at its tip holding a grey stone boulder, ropes and a crossbeam. Brown and dark-brown wood, iron fittings. Chunky toy-like proportions. Show the same five views of the machine (front, front three-quarter turned right, side facing right, back three-quarter, back).
Raw: catapult-raw-orig.png -> composed catapult-raw.png
Compose: `python compose.py art/generated/sprites/catapult-raw.png C:2:4,3:0 C:2:4,3:1 C:2:4,3:2 C:2:4,3:3 C:2:4,3:6::0.8 # C = art/generated/sprites/catapult-raw-orig.png`
Extra pixelize arguments: none
Result: ok - usable - raw came as 2 rows of 4+3; front/front 3/4/side/back 3/4 from the top row, back view from row 2 (scaled 0.8); noisy at 32 px
### cavebear - Cave Bear (`EK::CaveBear`)
Description (standing, magenta background): A shaggy cave bear: big, round and shaggy, thick dark-brown fur with lighter brown highlights, small round ears, small glowing yellow eyes, a light tan muzzle with a black nose, big paws with claws, standing on all four legs, grumpy. Chunky chibi proportions with a big head.
Raw: cavebear-raw.png
Extra pixelize arguments: `--grid 2x4 --row-counts 4,2 --pick 0,1,2,m5,3`
Result: ok - good - four-legged bear; raw has 2 rows of 4+2; back three-quarter is a mirrored figure of row 2
### beetle - Giant Beetle (`EK::Beetle`)
Description (standing, magenta background): A beetle the size of a barrel: a big round shiny dark-green shell with lighter green highlights and a line down the middle, a dark head with glowing yellow eyes, two curved black mandibles and two short antennae, six short black legs. Low on the ground, chunky and round.
Raw: beetle-raw-orig.png -> composed beetle-raw.png
Compose: `python compose.py art/generated/sprites/beetle-raw.png B:2:4,2:0 B:2:4,2:1 B:2:4,2:2 B:2:4,2:5::0.78 B:2:4,2:3 # B = art/generated/sprites/beetle-raw-orig.png`
Extra pixelize arguments: none
Result: ok - good - green shell, yellow eyes; raw has 2 rows of 4+2; back three-quarter from row 2 (scaled 0.78)
### trader - Trader (`EK::Trader`)
Description (standing, green background): A wandering hooded merchant: a deep purple hooded cloak with a friendly human face peeking out, a yellow clasp on the chest, a big brown leather backpack piled high with goods, brown boots, a small glowing lantern held in the right hand.
Raw: trader-raw.png
Extra pixelize arguments: none
Result: ok - good - green key, purple cloak survives, backpack, lantern
### thief - Thief (`EK::Thief`)
Description (standing, magenta background): A sneaky thief: a dark charcoal-grey hood and a black mask over the lower face with only the eyes showing, close-fitting dark-grey clothes with a brown belt and soft brown boots, a short steel dagger in the right hand, a bulging tan sack of gold coins slung over the back.
Raw: thief-raw.png
Extra pixelize arguments: none
Result: ok - good - dark hood and mask, dagger, tan loot sack
### monk - Monk (`EK::Monk`)
Description (standing, magenta background): A fighting monk of the Order: a shaven bald head, calm face with closed eyes, a simple orange robe with darker orange shading, tied with a yellow rope belt, bare arms and bare fists raised in a ready fighting stance, sandals.
Raw: monk-raw.png
Extra pixelize arguments: none
Result: ok - good - bald, orange robe, fists raised
### ranger - Ranger (`EK::Ranger`)
Description (standing, magenta background): A woodsman ranger of the Order: a green hood and green cloak, a human face, a green tunic with a brown leather belt and strap, brown leather trousers and boots, a wooden bow held upright in the left hand, a small iron snare trap hanging from the belt.
Raw: ranger-raw.png
Extra pixelize arguments: none
Result: ok - good - green hood and cloak, bow, snare
### inquisitor - Inquisitor (`EK::Inquisitor`)
Description (standing, magenta background): A frail zealous preacher of the Order: a thin old man with a tall dark-red mitre hat, a pale face with narrowed eyes, long white robes with a red stripe down the middle and red hems, holding open a glowing yellow holy book in front of him.
Raw: inquisitor-raw.png
Extra pixelize arguments: none
Result: ok - good - red mitre, white/red robes, glowing book; looks less frail than described
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

+182
View File
@@ -0,0 +1,182 @@
[
{
"id": "kobold",
"kind": "Kobold",
"ok": true,
"note": "good - chibi lizard, spear in all views; raw has frame lines (removed by the tool)"
},
{
"id": "spider",
"kind": "Spider",
"ok": true,
"note": "usable - both tries came as 2 rows without a back three-quarter view; the back view is used twice (views 4 and 5 identical)"
},
{
"id": "mage",
"kind": "Mage",
"ok": true,
"note": "good - green key, purple hood and robe survive, glowing eyes, gem staff"
},
{
"id": "ogre",
"kind": "Ogre",
"ok": true,
"note": "good - stocky stone ogre; raw has 2 rows (top row used); back three-quarter drawn almost as a side view"
},
{
"id": "knight",
"kind": "Knight",
"ok": true,
"note": "good - great helm, blue tabard with sun, sword"
},
{
"id": "archer",
"kind": "Archer",
"ok": true,
"note": "good - a bit taller than the imp, bow and quiver readable"
},
{
"id": "priest",
"kind": "Priest",
"ok": true,
"note": "good - white robe, sun emblem, floating halo"
},
{
"id": "dwarf",
"kind": "Dwarf",
"ok": true,
"note": "good - composed from two tries (each had only 4 views): try 1 front/front 3/4/side/back + try 2 back three-quarter"
},
{
"id": "paladin",
"kind": "Paladin",
"ok": true,
"note": "good - white/gold armour, big sword; very bright"
},
{
"id": "jailer",
"kind": "Jailer",
"ok": true,
"note": "good - hood, grey arms, red apron, chain"
},
{
"id": "priestess",
"kind": "Priestess",
"ok": true,
"note": "good - flame hair, red robe, fireball"
},
{
"id": "chainbreaker",
"kind": "Chainbreaker",
"ok": true,
"note": "good - obsidian with lava cracks, horns, axes; raw has 2 rows (top row used); dark at 32 px"
},
{
"id": "bat",
"kind": "Bat",
"ok": true,
"note": "good - green key, purple body and wings survive; raw has 2 rows (top row used)"
},
{
"id": "gargoyle",
"kind": "Gargoyle",
"ok": true,
"note": "good - grey stone, wings, chain; raw has 2 rows (top row used)"
},
{
"id": "owl",
"kind": "Owl",
"ok": true,
"note": "good - round chibi owl, blue wing tips; raw has 2 rows (top row used)"
},
{
"id": "wyrm",
"kind": "Wyrm",
"ok": true,
"note": "good - red dragon, orange belly, white horn; tall and thin because of the hanging tail"
},
{
"id": "cogling",
"kind": "Cogling",
"ok": true,
"note": "good - goggles, overall, hammer; raw has 2 rows (top row used)"
},
{
"id": "revenant",
"kind": "Revenant",
"ok": true,
"note": "good - second try (first had only 4 views); big skull, green eyes, rusty sword"
},
{
"id": "giant",
"kind": "Giant",
"ok": true,
"note": "good - bald brute with club; drawn chibi, so not much bigger than the others (the game scales it)"
},
{
"id": "fairy",
"kind": "Fairy",
"ok": true,
"note": "good - blue dress, pale wings; a few gold sparkles below the feet are part of the sprite, so the bottom row is the sparkles, not the feet"
},
{
"id": "battlemage",
"kind": "BattleMage",
"ok": true,
"note": "good - blue hat and robes, burning staff, grey beard"
},
{
"id": "ram",
"kind": "Ram",
"ok": true,
"note": "usable - composed from two tries (both 2 rows, views out of order): try 1 front/front 3/4/side/back + try 2 back three-quarter; the side view is wide, the 'back' view of try 1 may show the front end again"
},
{
"id": "catapult",
"kind": "Catapult",
"ok": true,
"note": "usable - raw came as 2 rows of 4+3; front/front 3/4/side/back 3/4 from the top row, back view from row 2 (scaled 0.8); noisy at 32 px"
},
{
"id": "cavebear",
"kind": "CaveBear",
"ok": true,
"note": "good - four-legged bear; raw has 2 rows of 4+2; back three-quarter is a mirrored figure of row 2"
},
{
"id": "beetle",
"kind": "Beetle",
"ok": true,
"note": "good - green shell, yellow eyes; raw has 2 rows of 4+2; back three-quarter from row 2 (scaled 0.78)"
},
{
"id": "trader",
"kind": "Trader",
"ok": true,
"note": "good - green key, purple cloak survives, backpack, lantern"
},
{
"id": "thief",
"kind": "Thief",
"ok": true,
"note": "good - dark hood and mask, dagger, tan loot sack"
},
{
"id": "monk",
"kind": "Monk",
"ok": true,
"note": "good - bald, orange robe, fists raised"
},
{
"id": "ranger",
"kind": "Ranger",
"ok": true,
"note": "good - green hood and cloak, bow, snare"
},
{
"id": "inquisitor",
"kind": "Inquisitor",
"ok": true,
"note": "good - red mitre, white/red robes, glowing book; looks less frail than described"
}
]
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

+30
View File
@@ -0,0 +1,30 @@
# compose.py out.png raw:rows:rowcounts:index[:m[:scale]] ... - cut chosen figures out of raw sheets and
# set them in one row on flat magenta (feet on one line, wide gaps), for pixelize_sprite.py --views N
import sys
sys.path.insert(0, "/home/zink/Projects/dungeoncraft/tools")
import pixelize_sprite as ps
from PIL import Image, ImageOps
out = sys.argv[1]; figs = []
for spec in sys.argv[2:]:
parts = spec.split(":")
raw, rows, rc, idx = parts[0], int(parts[1]), [int(v) for v in parts[2].split(",")], int(parts[3])
img = Image.open(raw).convert("RGB")
mask, boxes = ps.figure_boxes(img, rows, max(rc), "auto", "auto", rc)
x0, y0, x1, y1 = boxes[idx]
rgba = img.crop((x0, y0, x1, y1)).convert("RGBA")
# background pixels transparent so they become pure magenta on the new sheet
a = Image.new("L", rgba.size, 0)
a.putdata([255 if mask[y][x] else 0 for y in range(y0, y1) for x in range(x0, x1)])
rgba.putalpha(a)
if len(parts) > 4 and parts[4] == "m":
rgba = ImageOps.mirror(rgba)
if len(parts) > 5:
k = float(parts[5])
rgba = rgba.resize((round(rgba.width * k), round(rgba.height * k)), Image.NEAREST)
figs.append(rgba)
gap = 120
W = sum(f.width for f in figs) + gap * (len(figs) + 1); H = max(f.height for f in figs) + 2 * gap
sheet = Image.new("RGBA", (W, H), (255, 0, 255, 255)); x = gap
for f in figs:
sheet.alpha_composite(f, (x, H - gap - f.height)); x += f.width + gap
sheet.convert("RGB").save(out); print(out, sheet.size)
@@ -0,0 +1,91 @@
# Creature descriptions (from the 10x10 sprites and the bestiary), adapted to the imp's chibi style.
GREEN = {"spider", "mage", "bat", "trader"}
HOVER = {"bat", "gargoyle", "owl", "wyrm", "fairy"}
SIEGE = {"ram", "catapult"}
RETRY = {"dwarf", "revenant", "ram"}
CREATURES = [
dict(id="kobold", kind="Kobold", name="Kobold",
desc="A small scaly lizard-folk warrior: short and wiry, big lizard-like head with a short snout, bright green scaly skin, glowing yellow eyes, a toothy eager grin with sharp little teeth, a simple dark-green tunic, clawed bare feet, a short tail. It holds an upright wooden spear with a steel tip in its right hand."),
dict(id="spider", kind="Spider", name="Cave Spider",
desc="A big dungeon spider: a big round body and head in dark violet-purple with lighter purple highlights, a cluster of glowing yellow eyes on the front of the head, eight thick black jointed legs spread around it, small fangs. Low to the ground, compact and menacing, cute-chibi proportions with an oversized head."),
dict(id="mage", kind="Mage", name="Shadow Mage",
desc="A hooded dark sorcerer: a big pointed purple hood that hides most of the oversized head, the face in shadow with only a pale chin and two glowing yellow eyes visible, dark-purple robes with light violet trim down to the feet, a wooden staff with a glowing yellow gem on top held in the right hand."),
dict(id="ogre", kind="Ogre", name="Stone Ogre",
desc="A hulking ogre with skin like rock: broad and hunched, almost no neck, skin of grey stone with darker grey cracks and patches, small glowing yellow eyes, a heavy jaw with an underbite, enormous arms and fists reaching low, short thick legs, a ragged brown loincloth. No weapon - it fights with its fists. Chibi-stocky, as wide as it is tall."),
dict(id="knight", kind="Knight", name="Knight",
desc="A heavily armoured knight of the holy Order: a big polished silver great helm with a dark visor slit, silver shoulder plates and greaves, a blue tabard with a yellow sun emblem on the chest over chainmail, dark boots, a short straight steel sword held upright in the right hand."),
dict(id="archer", kind="Archer", name="Archer",
desc="A hero archer of the Order: a little green hat with a peak, a friendly human face with brown hair, a green tunic with a brown belt, brown trousers and leather boots, a quiver of arrows on the back, holding a wooden bow upright in the left hand."),
dict(id="priest", kind="Priest", name="Priest",
desc="A healing priest of the Order: a human with short brown hair and a kind face, a long plain white robe down to the feet with a golden sun emblem on the chest, a small golden halo ring floating above the head, hands folded in prayer."),
dict(id="dwarf", kind="Dwarf", name="Dwarf Miner",
desc="A stout dwarf miner of the Order: very short and broad, a grey steel helmet, a big bushy orange beard, a skin-coloured nose and eyes peeking out, a brown leather jerkin and belt, brown trousers and boots, a steel pickaxe with a wooden handle over the right shoulder."),
dict(id="paladin", kind="Paladin", name="Paladin of Light",
desc="A big glowing holy champion: a golden helmet with a dark visor, shining white plate armour with golden trim and a golden sun on the chest, golden greaves, broad shoulders, a white holy sword with a golden hilt held upright in the right hand. Bulkier than the other characters but still chibi."),
dict(id="jailer", kind="Jailer", name="Jailer",
desc="A big quiet dungeon jailer: broad-shouldered and heavy, a grey leather executioner's hood with eye holes and glowing yellow eyes, grey-skinned bare muscular arms, a dark-red leather apron over the chest and belly, heavy boots, a coiled iron chain hanging from the right hand."),
dict(id="priestess", kind="Priestess", name="Flame Priestess",
desc="A fire priestess of the dungeon: a woman with fair skin, flame-orange hair rising like fire, a long dark-red robe to the feet with an orange flame emblem on the chest, a small burning fireball floating above her raised right hand."),
dict(id="chainbreaker", kind="Chainbreaker", name="Chainbreaker",
desc="A legendary obsidian demon warrior: a muscular demon of black obsidian with glowing orange-lava cracks across the body, glowing yellow eyes, short curved horns, broken iron shackles with dangling chain links on both wrists, a steel battle axe with a light grey blade in each hand, heavy clawed feet."),
dict(id="bat", kind="Bat", name="Giant Bat",
desc="A big dungeon bat: a round furry dark-purple body with lighter purple highlights, leathery purple wings spread out with black finger bones, big pointed ears, small glowing red eyes, a little fanged mouth, tiny clawed feet hanging down. Hovering in the air, wings spread but kept compact within its own cell."),
dict(id="gargoyle", kind="Gargoyle", name="Chain Gargoyle",
desc="A winged stone gargoyle: a crouching, hunched creature of carved grey stone with darker grey cracks, stone bat wings with pale tips, short curved horns, glowing yellow eyes, a grim mouth with fangs, clawed hands and feet, an iron chain wrapped around one arm. Hovering in the air."),
dict(id="owl", kind="Owl", name="Arcane Owl",
desc="A wise magical owl: a round brown-feathered owl with lighter brown and cream breast feathers, tufted ear feathers, huge glowing yellow eyes with black pupils, a small yellow beak, wings with deep blue magical tips, yellow talons, a judging expression. Hovering in the air with its wings half spread."),
dict(id="wyrm", kind="Wyrm", name="Ember Wyrm",
desc="A small fire dragon: a compact red dragon with dark-red scales on the back, a glowing orange belly, dark-red bat wings, a single white horn, glowing yellow eyes, a curled tail, clawed legs, small embers of fire at its mouth. Hovering in the air."),
dict(id="cogling", kind="Cogling", name="Cogling",
desc="A sooty little goblin tinkerer: tiny and round, burnt-orange sooty skin, big brass goggles with glowing yellow lenses over the eyes, a grey work overall over an orange shirt, small boots, a steel hammer held up in the right hand, a few small brass cogs on the belt."),
dict(id="revenant", kind="Revenant", name="Revenant",
desc="An undead warrior: a gaunt pale corpse with a bony white skull face, hollow eye sockets with glowing green grave-light, tattered grey rags and old rusty armour pieces, thin bony arms and legs, a rusty steel sword held in the right hand."),
dict(id="giant", kind="Giant", name="Giant",
desc="A hero giant of the Order: a big broad human brute with fair skin, a small bald head with a dumb frown and small eyes, a brown leather jerkin over a blue shirt, bare muscular arms, heavy brown boots, a big wooden tree-trunk club resting on the right shoulder. Bulkier than the other characters but still chibi."),
dict(id="fairy", kind="Fairy", name="Fairy Spy",
desc="A tiny glowing fairy informer: a small figure with fair skin, blonde hair and small pointed ears, glowing yellow eyes, a short blue dress, two pairs of translucent pale-blue dragonfly wings, a few golden sparkles below the feet. Hovering in the air."),
dict(id="battlemage", kind="BattleMage", name="Battle Mage",
desc="A fire mage of the Order: a tall pointed blue wizard hat, a human face with a stern look and a short grey beard, long blue robes with white trim and a white sash, a wooden staff in the right hand with a burning orange-yellow flame on top."),
dict(id="ram", kind="Ram", name="Battering Ram",
desc="A wooden siege battering ram, no people visible: a compact wooden frame on four solid wooden wheels with a slanted brown plank roof; under the roof hangs a thick tree trunk with a big grey iron cap on its front end. Brown and dark-brown wood, iron rivets. Chunky toy-like proportions. Show the same five views of the machine (front, front three-quarter turned right, side facing right, back three-quarter, back)."),
dict(id="catapult", kind="Catapult", name="Catapult",
desc="A wooden siege catapult, no people visible: a low rectangular wooden base on four wooden wheels, a wooden throwing arm angled upward with a cup at its tip holding a grey stone boulder, ropes and a crossbeam. Brown and dark-brown wood, iron fittings. Chunky toy-like proportions. Show the same five views of the machine (front, front three-quarter turned right, side facing right, back three-quarter, back)."),
dict(id="cavebear", kind="CaveBear", name="Cave Bear",
desc="A shaggy cave bear: big, round and shaggy, thick dark-brown fur with lighter brown highlights, small round ears, small glowing yellow eyes, a light tan muzzle with a black nose, big paws with claws, standing on all four legs, grumpy. Chunky chibi proportions with a big head."),
dict(id="beetle", kind="Beetle", name="Giant Beetle",
desc="A beetle the size of a barrel: a big round shiny dark-green shell with lighter green highlights and a line down the middle, a dark head with glowing yellow eyes, two curved black mandibles and two short antennae, six short black legs. Low on the ground, chunky and round."),
dict(id="trader", kind="Trader", name="Trader",
desc="A wandering hooded merchant: a deep purple hooded cloak with a friendly human face peeking out, a yellow clasp on the chest, a big brown leather backpack piled high with goods, brown boots, a small glowing lantern held in the right hand."),
dict(id="thief", kind="Thief", name="Thief",
desc="A sneaky thief: a dark charcoal-grey hood and a black mask over the lower face with only the eyes showing, close-fitting dark-grey clothes with a brown belt and soft brown boots, a short steel dagger in the right hand, a bulging tan sack of gold coins slung over the back."),
dict(id="monk", kind="Monk", name="Monk",
desc="A fighting monk of the Order: a shaven bald head, calm face with closed eyes, a simple orange robe with darker orange shading, tied with a yellow rope belt, bare arms and bare fists raised in a ready fighting stance, sandals."),
dict(id="ranger", kind="Ranger", name="Ranger",
desc="A woodsman ranger of the Order: a green hood and green cloak, a human face, a green tunic with a brown leather belt and strap, brown leather trousers and boots, a wooden bow held upright in the left hand, a small iron snare trap hanging from the belt."),
dict(id="inquisitor", kind="Inquisitor", name="Inquisitor",
desc="A frail zealous preacher of the Order: a thin old man with a tall dark-red mitre hat, a pale face with narrowed eyes, long white robes with a red stripe down the middle and red hems, holding open a glowing yellow holy book in front of him."),
]
BY_ID = {c["id"]: c for c in CREATURES}
TEMPLATE = """The attached image is the style reference: a pixel art sprite sheet of the "Pit Imp" in five views (left to right: front, front three-quarter turned right, side facing right, back three-quarter turned away to the right, back).
Draw a DIFFERENT {thing} in exactly the same style: the "{name}". {desc}
Use exactly the same style as the reference: the same compact chibi proportions (big head, short stocky body, about the same overall size as the imp), the same thick one-pixel near-black outline, the same palette approach (flat colours with one highlight and one shadow tone), the same pixel size (about 32x32 pixels per figure, scaled up with hard square pixels), no anti-aliasing, no gradients, no blur.
Layout: exactly five figures in one single horizontal row (no second row, no extra figures), the same five views, in the same order (1 front view facing the viewer, 2 front three-quarter view turned to the right, 3 side view facing right, 4 back three-quarter view turned away to the right, 5 back view), evenly spaced, all the same size, {pose}, each figure separated from its neighbours by empty background.
Background: flat pure {bgname} everywhere, no ground, no shadows, no text, no labels, no frames, no grid lines, no cell borders."""
def prompt_for(cid):
c = BY_ID[cid]
thing = "siege engine" if cid in SIEGE else "creature"
pose = "hovering in the air, idle" if cid in HOVER else ("standing still" if cid in SIEGE else "standing, idle")
bgname = "green (#00FF00)" if cid in GREEN else "magenta (#FF00FF)"
extra = ("\n\nImportant: there must be exactly FIVE figures in ONE row. Do not skip the fourth one: the back three-quarter view, seen from behind at an angle, the face hidden, the body turned away from the viewer towards the upper right, between the side view and the straight back view."
if cid in RETRY else "")
if cid in RETRY and cid in SIEGE:
extra = "\n\nImportant: there must be exactly FIVE drawings of the machine in ONE row, no second row. 1 front view: the iron-capped end of the ram points straight at the viewer. 2 front three-quarter view: the capped end points towards the viewer and to the right. 3 side view: the capped end points to the right. 4 back three-quarter view: the capped end points away from the viewer towards the upper right, we see the rear end of the machine. 5 back view: we see the rear end straight on."
return extra and TEMPLATE.format(thing=thing, name=c["name"], desc=c["desc"], pose=pose, bgname=bgname) + extra or TEMPLATE.format(thing=thing, name=c["name"], desc=c["desc"], pose=pose, bgname=bgname)
+8
View File
@@ -0,0 +1,8 @@
spider --grid 2x4 --pick 0,1,2,3,3
ogre --crop 0,0,1,0.5
chainbreaker --crop 0,0,1,0.5
bat --crop 0,0,1,0.5
gargoyle --crop 0,0,1,0.5
owl --crop 0,0,1,0.5
cogling --crop 0,0,1,0.5
cavebear --grid 2x4 --row-counts 4,2 --pick 0,1,2,m5,3
+45
View File
@@ -0,0 +1,45 @@
#!/usr/bin/env python3
# Generate creature sprite sheets with fal-ai/nano-banana-pro/edit, the imp's lower row as style ref.
import base64, json, os, sys, urllib.request, concurrent.futures as cf
OUT = "/home/zink/Projects/dungeoncraft/art/generated/sprites"
REF = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "style-ref.png")
KEY = open(os.path.expanduser("~/.config/dungeoncraft/fal-key")).read().strip()
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from creatures import CREATURES, prompt_for
ref_uri = "data:image/png;base64," + base64.b64encode(open(REF, "rb").read()).decode()
def gen(cid, suffix=""):
body = {"prompt": prompt_for(cid), "image_urls": [ref_uri], "aspect_ratio": "21:9",
"resolution": "2K", "num_images": 1, "output_format": "png"}
req = urllib.request.Request("https://fal.run/fal-ai/nano-banana-pro/edit",
data=json.dumps(body).encode(),
headers={"Authorization": "Key " + KEY, "Content-Type": "application/json"})
try:
with urllib.request.urlopen(req, timeout=600) as r:
res = json.load(r)
url = res["images"][0]["url"]
with urllib.request.urlopen(url, timeout=300) as r:
data = r.read()
path = f"{OUT}/{cid}-raw{suffix}.png"
open(path, "wb").write(data)
return cid, "ok " + path
except Exception as e:
msg = str(e)
if hasattr(e, "read"):
try:
msg += " " + e.read().decode()[:300]
except Exception:
pass
return cid, "ERROR " + msg
if __name__ == "__main__":
ids = sys.argv[1:] or [c["id"] for c in CREATURES]
suffix = os.environ.get("SUFFIX", "")
with cf.ThreadPoolExecutor(4) as ex:
for cid, st in ex.map(lambda i: gen(i, suffix), ids):
print(cid, st, flush=True)
+32
View File
@@ -0,0 +1,32 @@
NOTES = {
"kobold": (True, "good - chibi lizard, spear in all views; raw has frame lines (removed by the tool)"),
"spider": (True, "usable - both tries came as 2 rows without a back three-quarter view; the back view is used twice (views 4 and 5 identical)"),
"mage": (True, "good - green key, purple hood and robe survive, glowing eyes, gem staff"),
"ogre": (True, "good - stocky stone ogre; raw has 2 rows (top row used); back three-quarter drawn almost as a side view"),
"knight": (True, "good - great helm, blue tabard with sun, sword"),
"archer": (True, "good - a bit taller than the imp, bow and quiver readable"),
"priest": (True, "good - white robe, sun emblem, floating halo"),
"dwarf": (True, "good - composed from two tries (each had only 4 views): try 1 front/front 3/4/side/back + try 2 back three-quarter"),
"paladin": (True, "good - white/gold armour, big sword; very bright"),
"jailer": (True, "good - hood, grey arms, red apron, chain"),
"priestess": (True, "good - flame hair, red robe, fireball"),
"chainbreaker": (True, "good - obsidian with lava cracks, horns, axes; raw has 2 rows (top row used); dark at 32 px"),
"bat": (True, "good - green key, purple body and wings survive; raw has 2 rows (top row used)"),
"gargoyle": (True, "good - grey stone, wings, chain; raw has 2 rows (top row used)"),
"owl": (True, "good - round chibi owl, blue wing tips; raw has 2 rows (top row used)"),
"wyrm": (True, "good - red dragon, orange belly, white horn; tall and thin because of the hanging tail"),
"cogling": (True, "good - goggles, overall, hammer; raw has 2 rows (top row used)"),
"revenant": (True, "good - second try (first had only 4 views); big skull, green eyes, rusty sword"),
"giant": (True, "good - bald brute with club; drawn chibi, so not much bigger than the others (the game scales it)"),
"fairy": (True, "good - blue dress, pale wings; a few gold sparkles below the feet are part of the sprite, so the bottom row is the sparkles, not the feet"),
"battlemage": (True, "good - blue hat and robes, burning staff, grey beard"),
"ram": (True, "usable - composed from two tries (both 2 rows, views out of order): try 1 front/front 3/4/side/back + try 2 back three-quarter; the side view is wide, the 'back' view of try 1 may show the front end again"),
"catapult": (True, "usable - raw came as 2 rows of 4+3; front/front 3/4/side/back 3/4 from the top row, back view from row 2 (scaled 0.8); noisy at 32 px"),
"cavebear": (True, "good - four-legged bear; raw has 2 rows of 4+2; back three-quarter is a mirrored figure of row 2"),
"beetle": (True, "good - green shell, yellow eyes; raw has 2 rows of 4+2; back three-quarter from row 2 (scaled 0.78)"),
"trader": (True, "good - green key, purple cloak survives, backpack, lantern"),
"thief": (True, "good - dark hood and mask, dagger, tan loot sack"),
"monk": (True, "good - bald, orange robe, fists raised"),
"ranger": (True, "good - green hood and cloak, bow, snare"),
"inquisitor": (True, "good - red mitre, white/red robes, glowing book; looks less frail than described"),
}
+47
View File
@@ -0,0 +1,47 @@
#!/usr/bin/env python3
# Pixelize all raw sheets to 32 and 64 px and build contact sheets.
import os, subprocess, sys
from PIL import Image
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from creatures import CREATURES
D = "/home/zink/Projects/dungeoncraft/art/generated/sprites"
T = "/home/zink/Projects/dungeoncraft/tools/pixelize_sprite.py"
PY = "/tmp/sprvenv/bin/python"
EXTRA = {} # id -> extra args
for line in open(os.path.join(os.path.dirname(os.path.abspath(__file__)), "extra.txt")) if os.path.exists(os.path.join(os.path.dirname(os.path.abspath(__file__)), "extra.txt")) else []:
if line.strip():
k, v = line.split(None, 1)
EXTRA[k] = v.split()
ids = sys.argv[1:] or [c["id"] for c in CREATURES]
for cid in ids:
raw = f"{D}/{cid}-raw.png"
if not os.path.exists(raw):
print(cid, "missing raw"); continue
for size, colors in ((32, 16), (64, 24)):
cmd = [PY, "-I", T, raw, f"{D}/{cid}", "--views", "5", "--size", str(size), "--colors", str(colors),
"--feet", str(size - 1), "--outline-weight", "0.6"] + EXTRA.get(cid, [])
r = subprocess.run(cmd, capture_output=True, text=True)
print(cid, size, (r.stdout + r.stderr).strip().splitlines()[-1:])
prev = f"{D}/{cid}-{size}-preview.png"
if os.path.exists(prev):
os.remove(prev)
# contact sheets: 64 px versions at 2x with labels, 32 px at 2x beside
from PIL import ImageDraw
allids = [c["id"] for c in CREATURES]
for part, chunk in enumerate((allids[:10], allids[10:20], allids[20:])):
W = 320 * 2 + 20 + 160 * 2
im = Image.new("RGBA", (W, len(chunk) * 140), (96, 92, 104, 255))
d = ImageDraw.Draw(im)
for i, cid in enumerate(chunk):
y = i * 140
d.text((2, y), cid, fill=(255, 255, 255, 255))
for size, x, sc in ((64, 0, 2), (32, 660, 2)):
p = f"{D}/{cid}-{size}.png"
if os.path.exists(p):
s = Image.open(p).convert("RGBA")
s = s.resize((s.width * sc, s.height * sc), Image.NEAREST)
im.alpha_composite(s, (x, y + 12))
im.save(f"/tmp/sprites-contact-{part}.png")
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 441 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Loaded 100 of 132 files, more files were not shown because too many files have changed in this diff. Show more