diff --git a/.gitignore b/.gitignore index 193710e..b6518bf 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,10 @@ packs/*/LOG.old # The creature currently taken from the illustration queue (tools/mj-queue.mjs). # Machine-local working state, not a fact about the repo. .mj-inflight + +# Raw Midjourney plates and the superseded PNG tokens. Working files, not deliverables: +# tools/make-art.py derives art/portraits/.webp and art/tokens/.webp from these, +# and only the derivatives ship. 47 plates at 2.3 MB each would be 93 MB of history for +# pictures nothing renders at that size. Keep the originals; do not commit them. +art/creatures/ +art/tokens/*.png diff --git a/art/portraits/barghest.webp b/art/portraits/barghest.webp new file mode 100644 index 0000000..6fa011b Binary files /dev/null and b/art/portraits/barghest.webp differ diff --git a/art/portraits/bearward_bear.webp b/art/portraits/bearward_bear.webp new file mode 100644 index 0000000..ca249a0 Binary files /dev/null and b/art/portraits/bearward_bear.webp differ diff --git a/art/portraits/boobrie.webp b/art/portraits/boobrie.webp new file mode 100644 index 0000000..16a8d80 Binary files /dev/null and b/art/portraits/boobrie.webp differ diff --git a/art/portraits/bull_azure.webp b/art/portraits/bull_azure.webp new file mode 100644 index 0000000..cba04b8 Binary files /dev/null and b/art/portraits/bull_azure.webp differ diff --git a/art/portraits/cade_lamb.webp b/art/portraits/cade_lamb.webp new file mode 100644 index 0000000..9a41f19 Binary files /dev/null and b/art/portraits/cade_lamb.webp differ diff --git a/art/portraits/churchgrim.webp b/art/portraits/churchgrim.webp new file mode 100644 index 0000000..d80b062 Binary files /dev/null and b/art/portraits/churchgrim.webp differ diff --git a/art/portraits/cockatrice.webp b/art/portraits/cockatrice.webp new file mode 100644 index 0000000..ac76545 Binary files /dev/null and b/art/portraits/cockatrice.webp differ diff --git a/art/portraits/cu_sidhe.webp b/art/portraits/cu_sidhe.webp new file mode 100644 index 0000000..6709ace Binary files /dev/null and b/art/portraits/cu_sidhe.webp differ diff --git a/art/portraits/drain_heron.webp b/art/portraits/drain_heron.webp new file mode 100644 index 0000000..bdc6b2a Binary files /dev/null and b/art/portraits/drain_heron.webp differ diff --git a/art/portraits/dun_cow.webp b/art/portraits/dun_cow.webp new file mode 100644 index 0000000..cf2e780 Binary files /dev/null and b/art/portraits/dun_cow.webp differ diff --git a/art/portraits/eagle_displayed.webp b/art/portraits/eagle_displayed.webp new file mode 100644 index 0000000..f68bca4 Binary files /dev/null and b/art/portraits/eagle_displayed.webp differ diff --git a/art/portraits/eelmother.webp b/art/portraits/eelmother.webp new file mode 100644 index 0000000..34c3aba Binary files /dev/null and b/art/portraits/eelmother.webp differ diff --git a/art/portraits/enfield.webp b/art/portraits/enfield.webp new file mode 100644 index 0000000..5511014 Binary files /dev/null and b/art/portraits/enfield.webp differ diff --git a/art/portraits/fen_hound.webp b/art/portraits/fen_hound.webp new file mode 100644 index 0000000..b6da014 Binary files /dev/null and b/art/portraits/fen_hound.webp differ diff --git a/art/portraits/gabriel_hounds.webp b/art/portraits/gabriel_hounds.webp new file mode 100644 index 0000000..b83f70e Binary files /dev/null and b/art/portraits/gabriel_hounds.webp differ diff --git a/art/portraits/gaunt_grey.webp b/art/portraits/gaunt_grey.webp new file mode 100644 index 0000000..da6593c Binary files /dev/null and b/art/portraits/gaunt_grey.webp differ diff --git a/art/portraits/gazehound.webp b/art/portraits/gazehound.webp new file mode 100644 index 0000000..43fefaa Binary files /dev/null and b/art/portraits/gazehound.webp differ diff --git a/art/portraits/grey_mare.webp b/art/portraits/grey_mare.webp new file mode 100644 index 0000000..ebc2082 Binary files /dev/null and b/art/portraits/grey_mare.webp differ diff --git a/art/portraits/gryphon-fixed.webp b/art/portraits/gryphon-fixed.webp new file mode 100644 index 0000000..2965f2a Binary files /dev/null and b/art/portraits/gryphon-fixed.webp differ diff --git a/art/portraits/gryphon.webp b/art/portraits/gryphon.webp new file mode 100644 index 0000000..ae2e07f Binary files /dev/null and b/art/portraits/gryphon.webp differ diff --git a/art/portraits/gwrach.webp b/art/portraits/gwrach.webp new file mode 100644 index 0000000..026ef1c Binary files /dev/null and b/art/portraits/gwrach.webp differ diff --git a/art/portraits/harbour_dog.webp b/art/portraits/harbour_dog.webp new file mode 100644 index 0000000..1b74c4b Binary files /dev/null and b/art/portraits/harbour_dog.webp differ diff --git a/art/portraits/heath_cow.webp b/art/portraits/heath_cow.webp new file mode 100644 index 0000000..d81ec99 Binary files /dev/null and b/art/portraits/heath_cow.webp differ diff --git a/art/portraits/hinny.webp b/art/portraits/hinny.webp new file mode 100644 index 0000000..c191db6 Binary files /dev/null and b/art/portraits/hinny.webp differ diff --git a/art/portraits/hollow_hog.webp b/art/portraits/hollow_hog.webp new file mode 100644 index 0000000..9e68edb Binary files /dev/null and b/art/portraits/hollow_hog.webp differ diff --git a/art/portraits/hunky_punk.webp b/art/portraits/hunky_punk.webp new file mode 100644 index 0000000..38a8904 Binary files /dev/null and b/art/portraits/hunky_punk.webp differ diff --git a/art/portraits/kelpie.webp b/art/portraits/kelpie.webp new file mode 100644 index 0000000..888e669 Binary files /dev/null and b/art/portraits/kelpie.webp differ diff --git a/art/portraits/lamprey_host.webp b/art/portraits/lamprey_host.webp new file mode 100644 index 0000000..c881c3c Binary files /dev/null and b/art/portraits/lamprey_host.webp differ diff --git a/art/portraits/piety_pelican.webp b/art/portraits/piety_pelican.webp new file mode 100644 index 0000000..4109ab7 Binary files /dev/null and b/art/portraits/piety_pelican.webp differ diff --git a/art/portraits/pooka.webp b/art/portraits/pooka.webp new file mode 100644 index 0000000..b8499dc Binary files /dev/null and b/art/portraits/pooka.webp differ diff --git a/art/portraits/sea_crow.webp b/art/portraits/sea_crow.webp new file mode 100644 index 0000000..90d956e Binary files /dev/null and b/art/portraits/sea_crow.webp differ diff --git a/art/portraits/sea_mare.webp b/art/portraits/sea_mare.webp new file mode 100644 index 0000000..79b397e Binary files /dev/null and b/art/portraits/sea_mare.webp differ diff --git a/art/portraits/selkie.webp b/art/portraits/selkie.webp new file mode 100644 index 0000000..4c39d44 Binary files /dev/null and b/art/portraits/selkie.webp differ diff --git a/art/portraits/sign_wyvern.webp b/art/portraits/sign_wyvern.webp new file mode 100644 index 0000000..99ac4ea Binary files /dev/null and b/art/portraits/sign_wyvern.webp differ diff --git a/art/portraits/supporter.webp b/art/portraits/supporter.webp new file mode 100644 index 0000000..be39f11 Binary files /dev/null and b/art/portraits/supporter.webp differ diff --git a/art/portraits/talbot.webp b/art/portraits/talbot.webp new file mode 100644 index 0000000..9e9063c Binary files /dev/null and b/art/portraits/talbot.webp differ diff --git a/art/portraits/tangie.webp b/art/portraits/tangie.webp new file mode 100644 index 0000000..fa601d9 Binary files /dev/null and b/art/portraits/tangie.webp differ diff --git a/art/portraits/tatterfoal.webp b/art/portraits/tatterfoal.webp new file mode 100644 index 0000000..c3f7c9f Binary files /dev/null and b/art/portraits/tatterfoal.webp differ diff --git a/art/portraits/the_heron.webp b/art/portraits/the_heron.webp new file mode 100644 index 0000000..9e52b84 Binary files /dev/null and b/art/portraits/the_heron.webp differ diff --git a/art/portraits/the_overflight.webp b/art/portraits/the_overflight.webp new file mode 100644 index 0000000..5ede1f2 Binary files /dev/null and b/art/portraits/the_overflight.webp differ diff --git a/art/portraits/tup.webp b/art/portraits/tup.webp new file mode 100644 index 0000000..7cd340e Binary files /dev/null and b/art/portraits/tup.webp differ diff --git a/art/portraits/turnback.webp b/art/portraits/turnback.webp new file mode 100644 index 0000000..6bc80f9 Binary files /dev/null and b/art/portraits/turnback.webp differ diff --git a/art/portraits/washland_cattle.webp b/art/portraits/washland_cattle.webp new file mode 100644 index 0000000..fe5fa77 Binary files /dev/null and b/art/portraits/washland_cattle.webp differ diff --git a/art/portraits/water_bull.webp b/art/portraits/water_bull.webp new file mode 100644 index 0000000..33fdbef Binary files /dev/null and b/art/portraits/water_bull.webp differ diff --git a/art/portraits/white_hart.webp b/art/portraits/white_hart.webp new file mode 100644 index 0000000..dfe630b Binary files /dev/null and b/art/portraits/white_hart.webp differ diff --git a/art/portraits/wrack_gull.webp b/art/portraits/wrack_gull.webp new file mode 100644 index 0000000..3c781d5 Binary files /dev/null and b/art/portraits/wrack_gull.webp differ diff --git a/art/portraits/yale.webp b/art/portraits/yale.webp new file mode 100644 index 0000000..0b3d7b3 Binary files /dev/null and b/art/portraits/yale.webp differ diff --git a/art/tokens/barghest.webp b/art/tokens/barghest.webp new file mode 100644 index 0000000..5781170 Binary files /dev/null and b/art/tokens/barghest.webp differ diff --git a/art/tokens/bearward_bear.webp b/art/tokens/bearward_bear.webp new file mode 100644 index 0000000..0f1f1cf Binary files /dev/null and b/art/tokens/bearward_bear.webp differ diff --git a/art/tokens/boobrie.webp b/art/tokens/boobrie.webp new file mode 100644 index 0000000..3880892 Binary files /dev/null and b/art/tokens/boobrie.webp differ diff --git a/art/tokens/bull_azure.webp b/art/tokens/bull_azure.webp new file mode 100644 index 0000000..9cc5ea2 Binary files /dev/null and b/art/tokens/bull_azure.webp differ diff --git a/art/tokens/cade_lamb.webp b/art/tokens/cade_lamb.webp new file mode 100644 index 0000000..1aa6175 Binary files /dev/null and b/art/tokens/cade_lamb.webp differ diff --git a/art/tokens/churchgrim.webp b/art/tokens/churchgrim.webp new file mode 100644 index 0000000..4f70e61 Binary files /dev/null and b/art/tokens/churchgrim.webp differ diff --git a/art/tokens/cockatrice.webp b/art/tokens/cockatrice.webp new file mode 100644 index 0000000..0d45a22 Binary files /dev/null and b/art/tokens/cockatrice.webp differ diff --git a/art/tokens/cu_sidhe.webp b/art/tokens/cu_sidhe.webp new file mode 100644 index 0000000..3583850 Binary files /dev/null and b/art/tokens/cu_sidhe.webp differ diff --git a/art/tokens/drain_heron.webp b/art/tokens/drain_heron.webp new file mode 100644 index 0000000..43916e2 Binary files /dev/null and b/art/tokens/drain_heron.webp differ diff --git a/art/tokens/dun_cow.webp b/art/tokens/dun_cow.webp new file mode 100644 index 0000000..bd0374c Binary files /dev/null and b/art/tokens/dun_cow.webp differ diff --git a/art/tokens/eagle_displayed.webp b/art/tokens/eagle_displayed.webp new file mode 100644 index 0000000..342b4e7 Binary files /dev/null and b/art/tokens/eagle_displayed.webp differ diff --git a/art/tokens/eelmother.webp b/art/tokens/eelmother.webp new file mode 100644 index 0000000..880c01b Binary files /dev/null and b/art/tokens/eelmother.webp differ diff --git a/art/tokens/enfield.webp b/art/tokens/enfield.webp new file mode 100644 index 0000000..92446c9 Binary files /dev/null and b/art/tokens/enfield.webp differ diff --git a/art/tokens/fen_hound.webp b/art/tokens/fen_hound.webp new file mode 100644 index 0000000..e03014b Binary files /dev/null and b/art/tokens/fen_hound.webp differ diff --git a/art/tokens/gabriel_hounds.webp b/art/tokens/gabriel_hounds.webp new file mode 100644 index 0000000..1f0a680 Binary files /dev/null and b/art/tokens/gabriel_hounds.webp differ diff --git a/art/tokens/gaunt_grey.webp b/art/tokens/gaunt_grey.webp new file mode 100644 index 0000000..c818b9e Binary files /dev/null and b/art/tokens/gaunt_grey.webp differ diff --git a/art/tokens/gazehound.webp b/art/tokens/gazehound.webp new file mode 100644 index 0000000..bd95dd8 Binary files /dev/null and b/art/tokens/gazehound.webp differ diff --git a/art/tokens/grey_mare.webp b/art/tokens/grey_mare.webp new file mode 100644 index 0000000..a4b67e4 Binary files /dev/null and b/art/tokens/grey_mare.webp differ diff --git a/art/tokens/gryphon-fixed.webp b/art/tokens/gryphon-fixed.webp new file mode 100644 index 0000000..7fe1ae1 Binary files /dev/null and b/art/tokens/gryphon-fixed.webp differ diff --git a/art/tokens/gryphon.webp b/art/tokens/gryphon.webp new file mode 100644 index 0000000..5282f8b Binary files /dev/null and b/art/tokens/gryphon.webp differ diff --git a/art/tokens/gwrach.webp b/art/tokens/gwrach.webp new file mode 100644 index 0000000..cedd4f9 Binary files /dev/null and b/art/tokens/gwrach.webp differ diff --git a/art/tokens/harbour_dog.webp b/art/tokens/harbour_dog.webp new file mode 100644 index 0000000..6f91c97 Binary files /dev/null and b/art/tokens/harbour_dog.webp differ diff --git a/art/tokens/heath_cow.webp b/art/tokens/heath_cow.webp new file mode 100644 index 0000000..05772e5 Binary files /dev/null and b/art/tokens/heath_cow.webp differ diff --git a/art/tokens/hinny.webp b/art/tokens/hinny.webp new file mode 100644 index 0000000..941efcb Binary files /dev/null and b/art/tokens/hinny.webp differ diff --git a/art/tokens/hollow_hog.webp b/art/tokens/hollow_hog.webp new file mode 100644 index 0000000..e5d9041 Binary files /dev/null and b/art/tokens/hollow_hog.webp differ diff --git a/art/tokens/hunky_punk.webp b/art/tokens/hunky_punk.webp new file mode 100644 index 0000000..28d54bd Binary files /dev/null and b/art/tokens/hunky_punk.webp differ diff --git a/art/tokens/kelpie.webp b/art/tokens/kelpie.webp new file mode 100644 index 0000000..301c394 Binary files /dev/null and b/art/tokens/kelpie.webp differ diff --git a/art/tokens/lamprey_host.webp b/art/tokens/lamprey_host.webp new file mode 100644 index 0000000..df74922 Binary files /dev/null and b/art/tokens/lamprey_host.webp differ diff --git a/art/tokens/piety_pelican.webp b/art/tokens/piety_pelican.webp new file mode 100644 index 0000000..1c065b5 Binary files /dev/null and b/art/tokens/piety_pelican.webp differ diff --git a/art/tokens/pooka.webp b/art/tokens/pooka.webp new file mode 100644 index 0000000..6921759 Binary files /dev/null and b/art/tokens/pooka.webp differ diff --git a/art/tokens/sea_crow.webp b/art/tokens/sea_crow.webp new file mode 100644 index 0000000..f645b25 Binary files /dev/null and b/art/tokens/sea_crow.webp differ diff --git a/art/tokens/sea_mare.webp b/art/tokens/sea_mare.webp new file mode 100644 index 0000000..54ac150 Binary files /dev/null and b/art/tokens/sea_mare.webp differ diff --git a/art/tokens/selkie.webp b/art/tokens/selkie.webp new file mode 100644 index 0000000..25c138c Binary files /dev/null and b/art/tokens/selkie.webp differ diff --git a/art/tokens/sign_wyvern.webp b/art/tokens/sign_wyvern.webp new file mode 100644 index 0000000..90689e0 Binary files /dev/null and b/art/tokens/sign_wyvern.webp differ diff --git a/art/tokens/supporter.webp b/art/tokens/supporter.webp new file mode 100644 index 0000000..f245cc3 Binary files /dev/null and b/art/tokens/supporter.webp differ diff --git a/art/tokens/talbot.webp b/art/tokens/talbot.webp new file mode 100644 index 0000000..1075ec4 Binary files /dev/null and b/art/tokens/talbot.webp differ diff --git a/art/tokens/tangie.webp b/art/tokens/tangie.webp new file mode 100644 index 0000000..4a46d52 Binary files /dev/null and b/art/tokens/tangie.webp differ diff --git a/art/tokens/tatterfoal.webp b/art/tokens/tatterfoal.webp new file mode 100644 index 0000000..92dd072 Binary files /dev/null and b/art/tokens/tatterfoal.webp differ diff --git a/art/tokens/the_heron.webp b/art/tokens/the_heron.webp new file mode 100644 index 0000000..bd3a45c Binary files /dev/null and b/art/tokens/the_heron.webp differ diff --git a/art/tokens/the_overflight.webp b/art/tokens/the_overflight.webp new file mode 100644 index 0000000..a3cfc00 Binary files /dev/null and b/art/tokens/the_overflight.webp differ diff --git a/art/tokens/tup.webp b/art/tokens/tup.webp new file mode 100644 index 0000000..079193d Binary files /dev/null and b/art/tokens/tup.webp differ diff --git a/art/tokens/turnback.webp b/art/tokens/turnback.webp new file mode 100644 index 0000000..a4bb6c6 Binary files /dev/null and b/art/tokens/turnback.webp differ diff --git a/art/tokens/washland_cattle.webp b/art/tokens/washland_cattle.webp new file mode 100644 index 0000000..097bd87 Binary files /dev/null and b/art/tokens/washland_cattle.webp differ diff --git a/art/tokens/water_bull.webp b/art/tokens/water_bull.webp new file mode 100644 index 0000000..a15c1cc Binary files /dev/null and b/art/tokens/water_bull.webp differ diff --git a/art/tokens/white_hart.webp b/art/tokens/white_hart.webp new file mode 100644 index 0000000..263b180 Binary files /dev/null and b/art/tokens/white_hart.webp differ diff --git a/art/tokens/wrack_gull.webp b/art/tokens/wrack_gull.webp new file mode 100644 index 0000000..e7c6c92 Binary files /dev/null and b/art/tokens/wrack_gull.webp differ diff --git a/art/tokens/yale.webp b/art/tokens/yale.webp new file mode 100644 index 0000000..2a7d1ae Binary files /dev/null and b/art/tokens/yale.webp differ diff --git a/packs/npcs/000005.ldb b/packs/npcs/000005.ldb index 6e14fbe..b44415c 100644 Binary files a/packs/npcs/000005.ldb and b/packs/npcs/000005.ldb differ diff --git a/packs/npcs/MANIFEST-000004 b/packs/npcs/MANIFEST-000004 index 46534f1..a463ad9 100644 Binary files a/packs/npcs/MANIFEST-000004 and b/packs/npcs/MANIFEST-000004 differ diff --git a/tools/build-packs.mjs b/tools/build-packs.mjs index 39c7bca..1c95a28 100644 --- a/tools/build-packs.mjs +++ b/tools/build-packs.mjs @@ -333,13 +333,19 @@ function buildActor(spec, type) { * Order matters. A hand-made raster always beats the generated diagram, so upgrading a * creature is a matter of dropping the file in; nothing has to be un-said in source. */ const PORTRAIT_EXT = ["webp", "png", "jpg", "svg"]; - const findPortrait = stem => { + /* One resolver, two directories. The token lookup below was a single hardcoded + `.png`, which meant the moment tools/make-art.py started writing .webp — the format + every other piece of art in this repository already uses — every creature silently + fell back to its portrait for a token and Foundry went back to cropping a tall plate + into a circle. The extension list is the same list for both, in the same order. */ + const findArt = (dir, stem) => { for (const ext of PORTRAIT_EXT) { - const rel = `art/portraits/${stem}.${ext}`; + const rel = `art/${dir}/${stem}.${ext}`; if (existsSync(path.join(ROOT, rel))) return rel; } return null; }; + const findPortrait = stem => findArt("portraits", stem); let face = ICON.actor; const found = findPortrait(spec.portrait ?? spec.key); @@ -359,8 +365,8 @@ function buildActor(spec, type) { * squeezing a tall plate into it and showing a torso with the head cut off. Prefer a * real token when tools/make-tokens.py has derived one; fall back to the portrait so * a creature without one still gets a picture rather than the generic silhouette. */ - const tokenRel = `art/tokens/${spec.portrait ?? spec.key}.png`; - const tokenSrc = existsSync(path.join(ROOT, tokenRel)) ? `systems/ringbrp/${tokenRel}` : face; + const tokenRel = findArt("tokens", spec.portrait ?? spec.key); + const tokenSrc = tokenRel ? `systems/ringbrp/${tokenRel}` : face; /* Token footprint, in grid squares, from SIZ. * diff --git a/tools/make-tokens.py b/tools/make-art.py similarity index 51% rename from tools/make-tokens.py rename to tools/make-art.py index 6ebc827..5236c19 100644 --- a/tools/make-tokens.py +++ b/tools/make-art.py @@ -1,21 +1,33 @@ #!/usr/bin/env python3 -"""Derive Foundry tokens from creature illustrations. +"""Derive the shipped creature art from the Midjourney illustrations. -A token is not a portrait. The illustrations are 2:3 field-guide plates — full figure, -plain ground — and build-packs.mjs was pointing prototypeToken.texture at the portrait -file, so Foundry was cropping a tall plate into a round token and showing a torso. +`art/creatures/-u1.png` is the raw plate as Midjourney files it: 2:3, full figure, +plain ground, and around 2.3 MB each. Two things are derived from it, and only the +derivatives are committed: -So the token is DERIVED from the illustration rather than generated separately. Two -reasons. It costs nothing, where a second Midjourney pass would double the spend. And it -cannot disagree with the portrait: it is the same drawing, which is the same argument -mj-queue makes for deriving prompts from statblocks. + art/portraits/.webp the plate, in the format the rest of the repo's art uses + art/tokens/.webp 512x512 RGBA, circular, transparent surround - python3 tools/make-tokens.py every illustration without a token - python3 tools/make-tokens.py --force redo them all - python3 tools/make-tokens.py ... just these +WHY WEBP, AND WHY DERIVED. Every one of the 309 art files already in this repository is +a .webp of 160-220 KB; the raw plates are 2.3 MB, and 47 of them would have added 93 MB +to a 29 MB repository, permanently, for pictures nothing renders at that size. Re-encoded +at quality 88 a plate is around 300 KB with the pencil hatching intact. -Input art/creatures/-u1.png (Midjourney, or any illustration) -Output art/tokens/.png 512x512 RGBA, circular, transparent surround +WHY PORTRAITS AND NOT CREATURES. build-packs.mjs resolves a creature's picture by stem +and extension under `art/portraits/`, preferring .webp, and says in as many words that +upgrading a creature should be a matter of dropping a file in. The plates were sitting in +`art/creatures/`, where nothing looked for them, so none of them reached the game. + +A token is not a portrait. The plates are full-figure 2:3, and prototypeToken.texture was +pointing at the portrait file, so Foundry cropped a tall plate into a round token and +showed a torso. The token is DERIVED rather than generated separately for two reasons: it +costs nothing, where a second Midjourney pass would double the spend; and it cannot +disagree with the portrait, because it is the same drawing — the same argument mj-queue +makes for deriving prompts from statblocks. + + python3 tools/make-art.py every illustration missing a derivative + python3 tools/make-art.py --force redo them all + python3 tools/make-art.py ... just these """ import math import sys @@ -23,8 +35,13 @@ from pathlib import Path from PIL import Image, ImageDraw, ImageFilter ROOT = Path(__file__).resolve().parent.parent -SRC, DST = ROOT / "art" / "creatures", ROOT / "art" / "tokens" +SRC = ROOT / "art" / "creatures" +DST = ROOT / "art" / "tokens" +PLATES = ROOT / "art" / "portraits" SIZE, RING = 512, 6 +# Quality 88 keeps the pencil hatching and lands a plate near the 160-220 KB the rest of +# the repository's art occupies. 80 visibly softens the finest cross-hatching. +PLATE_Q, TOKEN_Q = 88, 90 def subject_bbox(im, tol=236): """Bounding box of everything that is not the plain ground. @@ -72,7 +89,15 @@ def token_from(path, out): ImageDraw.Draw(ring).ellipse((RING // 2, RING // 2, SIZE - 1 - RING // 2, SIZE - 1 - RING // 2), outline=(26, 22, 18, 235), width=RING) out.parent.mkdir(parents=True, exist_ok=True) - Image.alpha_composite(crop, ring).save(out, "PNG", optimize=True) + Image.alpha_composite(crop, ring).save(out, "WEBP", quality=TOKEN_Q, method=6) + +def plate_from(src, out): + """The illustration itself, re-encoded. No crop, no resize — only the format.""" + im = Image.open(src) + if im.mode not in ("RGB", "RGBA"): + im = im.convert("RGB") + out.parent.mkdir(parents=True, exist_ok=True) + im.save(out, "WEBP", quality=PLATE_Q, method=6) def main(argv): force = "--force" in argv @@ -80,16 +105,19 @@ def main(argv): srcs = sorted(SRC.glob("*-u1.png")) if keys: srcs = [s for s in srcs if s.name[:-7] in keys] - made = skipped = 0 + made_t = made_p = skipped = 0 for s in srcs: key = s.name[:-7] # strip "-u1.png" - out = DST / f"{key}.png" - if out.exists() and not force: + token, plate = DST / f"{key}.webp", PLATES / f"{key}.webp" + did = False + if force or not token.exists(): + token_from(s, token); made_t += 1; did = True + if force or not plate.exists(): + plate_from(s, plate); made_p += 1; did = True + if not did: skipped += 1 - continue - token_from(s, out) - made += 1 - print(f"make-tokens: wrote {made}, skipped {skipped} existing, of {len(srcs)} illustration(s)") + print(f"make-art: {made_p} portrait(s), {made_t} token(s), " + f"{skipped} already derived, of {len(srcs)} illustration(s)") if __name__ == "__main__": main(sys.argv[1:]) diff --git a/tools/mj-queue.mjs b/tools/mj-queue.mjs index 1f28f23..0bcf735 100644 --- a/tools/mj-queue.mjs +++ b/tools/mj-queue.mjs @@ -103,17 +103,46 @@ const buildWords = ch => bandOf((Number(ch?.str) || 12) / (Number(ch?.siz) || 12 /* Heavy natural armour does not look the same on a heraldic beast as on a far-side thing. The single band list read "plated like machinery" at 6+, which drew the - guildhall gryphon — six centuries of carved stone and feather — as a mech. Era already - answers "when"; let it answer what the plating is made of. */ + guildhall gryphon — six centuries of carved stone and feather — as a mech. + + That was fixed once, by branching the SIMILE on era, and the gryphon came back as a + mech again: the branch changed what the plating was compared to while both arms still + opened with the word "armoured", and that word is what Midjourney draws. Changing the + reasoning is not the same as changing the cause. + + So no natural creature's description contains "armour" at all now. A beast's armour is + its hide, and the clause says hide. The word survives only where it is meant literally + — a far-side thing that really is plated — and `negativesFor` below asks for worn + armour to be left out, because a word choice is an argument and a --no is closer to a + guarantee. */ const armourWords = (n, era = "antique") => bandOf(Number(n) || 0, [ - [0, "no armour, bare"], + [0, "bare, unprotected"], [3, "tough hide"], - [6, "thick natural plating"], + [6, "thick natural plating, part of the animal"], [99, era === "future" || era === "anomalous" ? "heavily armoured, plated like machinery" - : "heavily armoured, hide and scale thick as carved stone, weathered and old"] + : "hide and scale thick as carved stone, weathered and old, its own body its only protection"] ]); +/** + * What must not be in the picture, beyond the house negatives. + * + * A creature that is old and heavy reads to Midjourney as a thing in plate, and a + * creature described as strong reads as a thing holding a weapon. Both are stated + * positively elsewhere in the prompt and neither can be talked out of the image. + */ +function negativesFor(spec, era, isPerson) { + if (isPerson) return []; + const out = []; + if (era !== "future" && era !== "anomalous") { + out.push("worn armour", "plate armour", "armour plating", "machinery", "robot", "mecha"); + } + // "no weapon — it uses its own limbs" is already in the prompt, positively. An animal + // is still handed a spear often enough to be worth stating twice. + if (!(spec.weapons ?? []).length) out.push("weapons", "swords", "armour straps"); + return out; +} + /** * The anatomy clause, read out of the creature's own hit-location table. This is the * whole point of the tool: a Vesh prompt cannot ask for a head, because the table it is @@ -276,7 +305,12 @@ export function promptFor(spec, kind = "npc") { token ].filter(Boolean); - const prompt = expand(clauses.join(", ").replace(/\s+/g, " ").trim()); + /* The style token ends with `--no photography, photorealism`, and --no takes a comma + list, so anything appended after expansion continues that list rather than starting + a new clause. This is the only reason the extra negatives can be added here. */ + const base = expand(clauses.join(", ").replace(/\s+/g, " ").trim()); + const extra = negativesFor(spec, era, isPerson); + const prompt = extra.length ? `${base}, ${extra.join(", ")}` : base; const warnings = BANNED.filter(b => prompt.toLowerCase().includes(b)); return { prompt, token, warnings }; }