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>
No files matched your search
@@ -6,3 +6,4 @@ www/mq_js_bundle.js
|
|||||||
play/
|
play/
|
||||||
dist/
|
dist/
|
||||||
art/generated/creature-previews/
|
art/generated/creature-previews/
|
||||||
|
art/generated/sprites/*-raw*.png
|
||||||
@@ -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
|
||||||
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 5.6 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 7.7 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 5.5 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 5.5 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 7.0 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 7.1 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 9.5 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 6.1 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 7.4 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 5.2 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 6.3 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 5.7 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 5.4 KiB |
@@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 5.9 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 9.2 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 7.1 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 6.3 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 5.1 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 5.6 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 5.3 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 6.6 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 6.0 KiB |
@@ -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)
|
||||||
@@ -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
|
||||||
@@ -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)
|
||||||
@@ -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"),
|
||||||
|
}
|
||||||
@@ -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")
|
||||||
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 5.6 KiB |
|
After Width: | Height: | Size: 441 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 5.8 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 5.6 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 7.7 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 5.5 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 5.5 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 7.0 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 7.1 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 9.5 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 6.1 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 7.4 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 5.2 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 6.3 KiB |