explodorama / files / tools / compose-icons.sh
compose-icons.sh
99869630 2026-10-23 Pavel Safronov | 1 | #!/bin/sh |
| 2 | # Builds the power-up icons that are a known sprite plus a modifier badge in the upper right | |
| 3 | # (user request, 2026-10-22): a green disc with a dark rim, the modifier's white symbol on it. | |
| 4 | # Sources are the Kenney files already in assets/; the results go to assets/composed/ and are | |
| 5 | # committed, so the build needs nothing extra. Needs ImageMagick 7 (`magick`). | |
| 6 | # | |
| 7 | # sh tools/compose-icons.sh | |
| 8 | set -e | |
| 9 | cd "$(dirname "$0")/.." | |
| 10 | K=assets/kenney | |
| 11 | I=assets/kenney-icons/White | |
| 12 | B=assets/kenney-board-icons | |
| 13 | OUT=assets/composed | |
| 14 | mkdir -p "$OUT" | |
| 15 | ||
| 16 | # ink <white icon> <name>: the same shape in the UI's ink colour, for light backgrounds (CSS | |
| 17 | # turns it white again where the background is dark: class "mono" in controls.ts). | |
52d68b59 2026-10-24 Pavel Safronov | 18 | # Always 128 × 128, like every board sprite: LittleJS cuts tiles of 128 out of each texture, and a |
| 19 | # 100 px Game Icons file (the padlock) made its tile invalid, which crashed the game (2026-10-23). | |
99869630 2026-10-23 Pavel Safronov | 20 | ink() { |
52d68b59 2026-10-24 Pavel Safronov | 21 | magick "$1" -resize 128x128 -fill '#353541' -colorize 100 "$OUT/$2.png" |
99869630 2026-10-23 Pavel Safronov | 22 | echo " $OUT/$2.png" |
| 23 | } | |
| 24 | ||
| 25 | # The Move and Wait buttons (user request, 2026-10-22: the astronaut was confusing next to the | |
| 26 | # player). Wait is the hourglass (it was the figure with an ✕ for a while). | |
| 27 | ink "$B/character.png" icon_move | |
| 28 | ink "$B/hourglass.png" icon_wait | |
| 29 | # The fuse in the top bar and its info: the timer of the Fuse +1 badge (user request, 2026-10-22). | |
| 30 | ink "$B/timer_CW_75.png" icon_fuse | |
| 31 | ||
| 32 | # badge <base sprite> <symbol> <name>: 128 × 128, the badge 56 px across at the top right. | |
| 33 | badge() { | |
| 34 | magick "$1" \ | |
| 35 | \( -size 128x128 xc:none -fill '#2cc987' -stroke '#353541' -strokewidth 6 -draw 'circle 96,32 96,4' \) -composite \ | |
| 36 | \( "$2" -resize 36x36 \) -geometry +78+14 -composite \ | |
| 37 | "$OUT/$3.png" | |
| 38 | echo " $OUT/$3.png" | |
| 39 | } | |
| 40 | ||
| 41 | badge "$K/bomb.png" "$I/plus.png" powerup_bomb_plus | |
| 42 | badge "$K/bomb.png" "$I/larger.png" powerup_fire_plus | |
| 43 | badge "$K/bomb.png" "$B/timer_CW_75.png" powerup_fuse_plus | |
| 44 | badge "$OUT/icon_move.png" "$I/fastForward.png" powerup_run | |
| 45 | badge "$K/bomb.png" "$I/arrowRight.png" powerup_kick | |
| 46 | badge "$K/bomb.png" "$I/upRight.png" powerup_throw | |
| 47 | badge "$OUT/icon_move.png" "$I/export.png" powerup_teleport | |
52d68b59 2026-10-24 Pavel Safronov | 48 | # Diagonal: the Move figure with four diagonal arrows (a charge until 2026-10-23). |
| 49 | badge "$OUT/icon_move.png" "$B/arrow_diagonal_cross.png" powerup_diagonal | |
99869630 2026-10-23 Pavel Safronov | 50 | badge "$K/bomb.png" "$I/exclamation.png" powerup_detonator |
| 51 | badge "$K/block_planks.png" "$I/cross.png" powerup_wall_pass | |
| 52 | badge "$K/bomb.png" "$B/shield.png" powerup_bomb_pass | |
| 53 | badge "$K/fireball.png" "$B/shield.png" powerup_flame_pass | |
52d68b59 2026-10-24 Pavel Safronov | 54 | |
| 55 | # The charges (user request, 2026-10-23: they work on pickup now). Pierce and Power reuse the | |
ebcd0a8f 2026-11-02 Pavel Safronov | 56 | # Crate Pass and Fire+ icons above; the rest: |
52d68b59 2026-10-24 Pavel Safronov | 57 | badge "$K/bomb.png" "$B/skull.png" charge_nuke |
| 58 | ink "$I/locked.png" charge_freeze | |
| 59 | ink "$B/pawns.png" charge_decoy | |
7f242d36 2026-10-25 Pavel Safronov | 60 | # The power-ups and charge added 2026-10-24 (user request): RPG, a bomb with a forward arrow; |
| 61 | # Proximity alert, a bomb with a warning sign; Slide, the Move figure with a two-way arrow; the | |
| 62 | # ICBM charge, a bomb with a target. | |
| 63 | badge "$K/bomb.png" "$I/forward.png" powerup_rpg | |
| 64 | badge "$K/bomb.png" "$I/warning.png" powerup_proximity | |
| 65 | badge "$OUT/icon_move.png" "$B/arrow_horizontal.png" powerup_slide | |
| 66 | badge "$K/bomb.png" "$I/target.png" charge_icbm | |
e6bb7c42 2026-10-25 Pavel Safronov | 67 | # TNT (user request, 2026-10-25): Fire +2 — the fireball itself with the Fire +1 "larger" badge. |
| 68 | badge "$K/fireball.png" "$I/larger.png" powerup_tnt | |
5f066960 2026-10-26 Pavel Safronov | 69 | # Acid (PLAN.md §19.4, 2026-10-25): a bomb with a flask. |
| 70 | badge "$K/bomb.png" "$B/flask_full.png" charge_acid | |
| 71 | ||
| 72 | # A metal crate (PLAN.md §19.3): the strong crate in steel grey. | |
| 73 | magick "$K/block_strong_empty.png" -modulate 165,6 -fill '#9fb4cc' -tint 70 "$OUT/block_metal.png" | |
| 74 | echo " $OUT/block_metal.png" | |
| 75 | ||
| 76 | # Oil (PLAN.md §19.3): a splat from the Splat Pack, near black, with a glossy highlight, so it | |
| 77 | # can't be taken for a stain. | |
| 78 | magick assets/kenney-splat/splat05.png -fill '#2b2631' -colorize 100 \ | |
| 79 | -fill 'rgba(190,200,230,0.55)' -stroke none -draw 'ellipse 52,46 16,7 0,360' \ | |
| 80 | -fill 'rgba(190,200,230,0.35)' -draw 'ellipse 76,70 7,3 0,360' \ | |
| 81 | \( assets/kenney-splat/splat05.png -alpha extract \) -compose copyopacity -composite PNG32:"$OUT/oil.png" | |
| 82 | echo " $OUT/oil.png" | |
7f242d36 2026-10-25 Pavel Safronov | 83 | |
05e79f5f 2026-10-27 Pavel Safronov | 84 | # Bushes (user request, 2026-10-26): Foliage Sprites' white shaded shapes, tinted green (its shading |
| 85 | # kept by multiplying) and given the platformer art's dark outline, at 128 × 128. Tall grass tufts, | |
| 86 | # and round canopies; a few of each, so a clump doesn't repeat one picture. | |
| 87 | FOL=assets/kenney-foliage | |
| 88 | foliage() { | |
| 89 | magick "$FOL/sprite_00$1.png" -colorspace sRGB -type TrueColorAlpha -resize 112x112 -background none -gravity center -extent 128x128 PNG32:"$TMPF/shape.png" | |
| 90 | magick "$TMPF/shape.png" \( +clone -fill "$2" -colorize 100 \) -compose multiply -composite \ | |
| 91 | \( "$TMPF/shape.png" -alpha extract \) -compose copyopacity -composite PNG32:"$TMPF/tinted.png" | |
| 92 | magick "$TMPF/tinted.png" \( +clone -alpha extract -morphology dilate disk:5 -blur 0x0.6 -level 40%,60% \ | |
| 93 | -background '#353541' -alpha shape \) +swap -compose over -composite PNG32:"$OUT/$3.png" | |
| 94 | echo " $OUT/$3.png" | |
| 95 | } | |
| 96 | TMPF=$(mktemp -d) | |
| 97 | foliage 52 '#45c060' grass_a | |
| 98 | foliage 53 '#3db457' grass_b | |
| 99 | foliage 62 '#4cc866' grass_c | |
| 100 | foliage 63 '#41b85b' grass_d | |
| 101 | foliage 90 '#45c060' canopy_a | |
| 102 | foliage 91 '#3db457' canopy_b | |
| 103 | rm -r "$TMPF" | |
| 104 | ||
52d68b59 2026-10-24 Pavel Safronov | 105 | # Shield: the Move figure inside an empty ring (the board-game timer at 0). |
| 106 | magick "$B/timer_0.png" -fill '#353541' -colorize 100 \ | |
| 107 | \( "$OUT/icon_move.png" -resize 84x84 \) -gravity center -composite \ | |
| 108 | "$OUT/charge_shield.png" | |
| 109 | echo " $OUT/charge_shield.png" | |
| 110 | ||
6e94f995 2026-10-24 Pavel Safronov | 111 | # Developer mode's extra gem (user request, 2026-10-24: a very special gem, so it can't be taken |
| 112 | # for the level's own): the green gem's body in a rainbow, red to violet from left to right, | |
| 113 | # blended over its own shading in grey (Overlay keeps the facets); the dark outline stays as it is. | |
| 114 | TMP=$(mktemp -d) | |
| 115 | magick "$K/gem_green.png" -modulate 100,0 "$TMP/grey.png" | |
| 116 | magick -size 24x128 xc:'#ff4848' \ | |
| 117 | \( -size 128x20 gradient:'#ff4848'-'#ffd23b' -rotate -90 \) \ | |
| 118 | \( -size 128x20 gradient:'#ffd23b'-'#3bd96b' -rotate -90 \) \ | |
| 119 | \( -size 128x20 gradient:'#3bd96b'-'#3ba8ff' -rotate -90 \) \ | |
| 120 | \( -size 128x20 gradient:'#3ba8ff'-'#c060ff' -rotate -90 \) \ | |
| 121 | -size 24x128 xc:'#c060ff' +append \ | |
| 122 | "$TMP/grey.png" -compose overlay -composite "$TMP/rainbow.png" | |
| 123 | magick "$K/gem_green.png" "$TMP/rainbow.png" \( "$TMP/grey.png" -alpha off -threshold 30% \) -compose over -composite \ | |
| 124 | \( "$K/gem_green.png" -alpha extract \) -compose copyopacity -composite PNG32:"$OUT/gem_rainbow.png" | |
| 125 | rm -r "$TMP" | |
| 126 | echo " $OUT/gem_rainbow.png" | |
| 127 | ||
52d68b59 2026-10-24 Pavel Safronov | 128 | # The app's icons for installing it (user request, 2026-10-23): the bomb on the sky blue, in |
| 129 | # static/icons/. "Maskable" keeps the bomb inside the middle 60%, so any mask shape fits round it. | |
| 130 | mkdir -p static/icons | |
| 131 | appicon() { | |
| 132 | magick -size "$1x$1" xc:'#c3e3ff' \( "$K/bomb.png" -resize "$2x$2" \) -gravity center -composite "static/icons/$3.png" | |
| 133 | echo " static/icons/$3.png" | |
| 134 | } | |
| 135 | appicon 192 150 icon-192 | |
| 136 | appicon 512 400 icon-512 | |
| 137 | appicon 512 300 icon-maskable-512 | |
| 138 | appicon 180 140 apple-touch-icon |