Skip to content

Icons by name

Cosmos ships an icon sheet, and until now the only way to reach it was the cell number:

gui_icon("icon_index:111;color:#cc0;")     # ...which one is 111?

Nobody remembers 111, nothing can check it, and a mission that wants its own art has to find and edit every screen that draws one. So every glyph now has a name:

gui_icon_name("wanted", "#cc0")            # the same icon
gui_icon_name("quest.job", "#cc0")         # better: say what it MEANS

A look, or a meaning

There are two kinds of name, and the difference is the whole point.

example what it is
A look square, wanted, bell, sitemap one per drawn cell — what the glyph is
A meaning quest.job, quest.state, list.expand an alias onto a look — what it's for

Ask for the meaning wherever you can. quest.job says why the icon is there; wanted only says what it looks like today. Re-point the meaning once and every screen drawing it changes together.

from sbs_utils.procedural.gui.icon_sheet import ICON_ALIAS
ICON_ALIAS["quest.job"] = "flag"           # every quest log now flags its jobs

The meanings that ship:

meaning look used for
quest.arc sitemap the heading over a run of beats
quest.job wanted work posted for someone to take
quest.objective flag something the crew is to do
quest.beat talks a moment they live through
quest.cue bell a stage direction; fires unseen
quest.state square the state pip, recolored per state
check.on / check.off square / square-outline a checked and an unchecked row
list.expand / list.collapse expand / collapse a fold that opens or closes
list.prev / list.next rewind / forward paging through a list

Color is per use, not per icon

Every built-in glyph is white on transparent, so one glyph serves every state — pass the color at the point of drawing:

for quest in quests:
    gui_icon_name("quest.state", "#6d6" if quest.done else "#888")

Bring your own sheet

A name is not tied to the built-in sheet. Claim the look for a cell of your own and it wins over the built-in index:

from sbs_utils.procedural.gui import gui_icon_add_atlas
from sbs_utils.procedural.media_paths import media_shared

# One 64px cell out of your own sheet, claiming the name "wanted".
gui_icon_add_atlas("wanted", media_shared("icons/quest-sheet"), 0, 0, 64, 64)

From then on every gui_icon_name("quest.job") in the game draws your art — with no edit to the code that draws it. That is what lets a screen be written before its art exists, and lets an add-on re-skin screens it doesn't own.

Or claim a whole sheet at once, laid out row-major:

gui_icon_add_atlas_grid(media_shared("icons/quest-sheet"), 8, 8,
                        ["wanted", "flag", "talks", None, "bell"], cell=64)

Claiming a look has to be deliberate

gui_icon_add_atlas is gui_image_add_atlas(..., domain="icon"), and only the icon domain re-skins. A plain gui_image_add_atlas("square", ...) — a perfectly ordinary thing to call an image — does not become the icon square. Without that scope one image registration could silently re-skin every state pip in the game, and the author would have no way to know why.

Icons written as a fact sheet

A sheet is a catalog, and a catalog is what AMD is for. An image section registers the same keys with no Python at all — Sheet, Cell and the domain are written once on the section, so an entry is one line:

## [Icons](icons)
---
icons
Sheet: icons/quest-sheet
Cell: 64
---
The quest log's glyphs. White silhouettes - color is applied per use.

### [Job](wanted)
---
At: 0, 0
---

### [Beat](talks)
---
At: 1, 0
Color: #888
---
images_load_amd("icons.amd")     # or images_declare_document(doc) for a section of a bigger file

A section whose kind noun is icons registers in the icon domain — so those keys are looks. Any other word (images, art, atlas) registers ordinary atlas keys, which is what a card deck or a set of console backdrops wants:

## [Cards](cards)
---
images
Sheet: casino/terran_deck
Cell: 190, 280
Domain: casino
---

### [Back](card_back)
---
At: 0, 0
---

sbs lint checks these: a sheet that is not on disk, an At: with no Cell: to measure against, and a cell that falls off the edge of the sheet. All three draw a blank widget today with no error anywhere.

Where a custom sheet should live

Put it in a shared media pack rather than in each mission that draws it. media_shared() finds it wherever it was unpacked, so the same call works in a clone and in a fetched copy.

Sub-rects are pixels

When you cut cells out of your own sheet, gui_image_add_atlas(key, file, l, t, r, b) takes pixel coordinates, not 0–1 texture coordinates. A 64px grid is (col*64, row*64, (col+1)*64, (row+1)*64).

Things worth knowing

  • An unknown name draws nothing and logs a warning once, rather than falling back to some arbitrary glyph. A wrong icon is worse than a missing one, because it looks deliberate.
  • Both backings lay out the same. A built-in name goes out as an engine icon; a custom one goes out as an image (the engine has no icon concept for art it didn't ship). Both are square columns sized off the row height, so re-skinning a name cannot shift a layout.
  • click_tag works on the built-in path only today. A clickable icon whose name may be re-skinned isn't supported yet — use gui_icon directly if you need the click.
  • icon_names() lists everything that resolves; icon_resolve(name) returns (icon_index, atlas_key) with exactly one of the two set.

The whole sheet

176 named glyphs. The number after each name is the raw icon_index, in case you're reading older code that used it. Names in italics are meanings that point at that look.

Science & space

atom0
wheel1
propeller2
swirl3
magnet4
brain5
trefoil6
vortex7
pinwheel8
rings9
satellite-dish11
gem12
globe13
waveform15
ram17
radioactive29
honeycomb30
triskelion44
asteroid51
molecule54
microscope63
specimen64
reactor125
dome138
fallout139
globe-grid159

Ship systems & engineering

sawblade19
recycle20
capsule21
gears28
battery35
gear55
gear-solid56
gears-two57
maintenance58
turbine59
factory60
pipes61
press62
device65
damaged66
gear-ring67
circuit68
circuit-maze69
mechanism70
forge113
fountain114
elevator115

Combat

turret36
lightning39
bullets40
bullets-plus41
bullet-plus42
bullseye43
flame45
spider49
fighter52
fist53
cavalry75
valkyrie76
knight77
goblin78
crosshair112
rifle117
skull170
skull-horned171
sword172
helm-spartan173

People

squad37
teleport46
run47
muscle71
robot72
king73
hero74
walk79
crowd80
gauntlet81
portrait82
person83
bandit119
meeting131
talks132quest.beat
handshake175

Medical

first-aid25
medic-up84
heart-minus85
heart-plus86
hospital87
medical-cross100
cross-box102
medic-down124
caduceus133

Places & cargo

supplies32
shell48
hex-cargo50
observatory88
chest-open89
barrel90
chest99
tavern104
home107
container126
chef127
mess141
bunks142
cards143

Signals, orders & the map

arrow-curve10
tread14
zoom-in16
chevrons-right18
hourglass22
flag23quest.objective
hand24
wrench26
shield27
sitemap31quest.arc
radar33
antenna34
folder92
satellite93
export94
import95
burst96
claws103
shield-plain106
bishop108
shield-broken109
helm-wheel110
wanted111quest.job
stop120
tablet130
phone134
patrol-badge140
python144
bell145quest.cue
ship-sail146
acorn147

Rank pips

chevrons-circle38
rank-star91
rank-398
rank-2105
rank-1116
rank-down-2122
rank-down123
rank-4128
rank-cone135

Emblems

emblem-compass164
emblem-diamond165
emblem-hex166
emblem-bars167
emblem-axe168
emblem-blade169
laurel174

Shapes & widget furniture

circle97
square101check.on, quest.state
cursor118
square-outline121check.off
circle-outline129
move136
menu137
arrow-up148
arrow-right149
arrow-down150
arrow-left151
rewind152list.prev
forward153list.next
expand154list.expand
collapse155list.collapse
plus156
minus157
ban158
magnifier160
magnifier-large161
magnifier-thin162
magnifier-small163

Icons from game-icons.net. The gallery is generated from icon_sheet.py by mkdocs/gen_icon_gallery.py — re-run it after adding a name.