Interface.bb
The client-side 2D HUD substrate. Defines every per-window gadget handle (W*, B*, L*, T*, S*), the unified key-binding state (Key_* globals + LoadControlBindings / SaveControlBindings file I/O), the InterfaceComponent Type used by the persistent layout system (LoadInterfaceSettings / SaveInterfaceSettings), and the ControlHit / ControlDown / ControlName$ cross-device input dispatch.
This module is declaration-heavy, logic-light. Most of it is Global W<Window>, Global B<Button>, Global L<Label>, Dim arrays for inventory / spell-book / action-bar slots, etc. — the file is the central registry of UI gadget handles, not the place where UI behavior lives. Per-window event handling lives in MainMenu.bb, ClientNet.bb, and other consumers.
| Type | Purpose |
|---|---|
Dialog |
NPC-conversation modal. Win, 14 text lines (TextLines[13] / TextText$[13] + per-line R/G/B and OptionNum[]), an ActorInstance link, a ScriptHandle. Per-NPC dialog ID. |
TextInput |
Single-line text-entry modal. Win, TextBox, AcceptButton, ScriptHandle. Used by BVM_GETSTRING and similar script prompts. |
Bubble |
Floating chat-bubble entity that follows an actor for a short time. EN, Width# / Height#, Timer, ActorInstance. |
CurrentChat |
Transient chat-line entry (Dat$, cR/cG/cB, Timer) — the "fades after N seconds" chat layer. Walked alongside ChatHistory$ to render the volatile chat overlay. |
EffectIcon / EffectIconSlot |
Buff-icon HUD entries — EffectIcon is the catalog entry (Name$, ID, TextureID); EffectIconSlot is the rendered slot pointing back at one (EN, Effect.EffectIcon). |
InterfaceComponent |
Persistent layout descriptor (X/Y/W/H in fraction-of-screen, Alpha, Component handle, Texture, R/G/B). Backs Chat, ChatEntry, BuffsArea, Radar, Compass, the per-attribute bar array, inventory window/drop/eat/gold, and the inventory-slot button array. Read/written via ReadInterfaceComponent / WriteInterfaceComponent. |
Dialog is addressed by Handle via DialogScriptHandle(Han) at Interface.bb:196-201, which returns the Dialog\ScriptHandle field for downstream BVM dispatch. TextInput has no equivalent handle-walking helper in this file — script-side TextInput lookups go through a different path. (Adding TextInputScriptHandle(Han) would close the asymmetry; not currently present.)
The bulk of this file is one large registry of Global handles, grouped by HUD region:
| Group | Gadgets | Touched by |
|---|---|---|
| Action bar | XPEN, BChat / BMap / BInventory / BSpells / BCharStats / BQuestLog / BParty / BHelp, BNextBar / BPrevBar, ActionBarSlots(35), BActionBar(11), ActionBarStart, ActionBarUpTex / DownTex |
UI input loop; P_SetActionBar packet handler |
| Character interaction window | WCharInteract, SCharInteractHealth, LCharInteractTalk, CharInteract.ActorInstance, LCharInteractFaction / Level / Reputation |
NPC right-click / examine flow |
| Tooltip | WTooltip (created-on-the-fly), WTooltipReturn, LTooltip |
hover detection in every window |
| Party | WParty, BPartyLeave, PartyName$(6), LPartyName(6) |
P_PartyUpdate packet handler |
| Menu (added 2014) | WMenu, BMenu, BLogOut / BCharSelect / BExit / BOptions |
escape-key handler |
| Help | WHelp, BHelp, SHelpScroll, HelpText$(99), LHelp(14) |
/help chat command |
| Radar + map | ShowRadar, WLargeMap, LargeMapVisible (see Radar.bb for the actual renderer) |
|
| Inventory | WInventory, LInventoryGold, BInventoryDrop / Eat, WAmount + amount-prompt support, MouseSlot* drag-state, BSlots(Slots_Inventory), WItemWindow |
inventory-window event loop |
| Trading | WTrading, LTradingGold / Cost, BTradingOK / Cancel, TradeType, BCostUp / Down, parallel mine/his slot arrays (32 each) |
trade-window event loop; P_InventoryUpdate "T" |
| Char stats | WCharStats, attribute-name/value label arrays |
P_StatUpdate consumer |
| Spells | WSpells, BNextSpells / PrevSpells, LSpellsPage, WSpellRemove + WSpellError modals, BSpellImgs(9) etc., FirstSpell cursor, LastSpellRecharge |
P_SpellUpdate consumer |
| Quest log | WQuestLog, BCompleteQuests / Next / Prev, FirstQuest, LQuestLines(16) |
P_StandardUpdate quest-entry consumer |
| Chat | ChatHistory$(1999) (permanent history), ChatHistoryColour(1999), ChatLines(0) (resized on screen-resolution change), MaxChatLine, history mode toggle |
P_ChatMessage consumer |
ChatHistory$ is a 2000-slot ring; ChatLines is dynamically Dim-resized to whatever fits the current resolution. A future high-DPI / wide-screen aware UI overhaul would touch this Dim ChatLines(N) call site.
Key_* globals store integer control codes; LoadControlBindings / SaveControlBindings persist them via plain ReadInt / WriteInt (no SafeWrite — the file is small and a corrupt control-bindings file resets to defaults harmlessly on next launch).
ControlHit / ControlDown / ControlName$ partition the integer control space into three ranges:
| Range | Device | Implementation |
|---|---|---|
1..499 |
Keyboard | Direct KeyHit(Ctrl) / KeyDown(Ctrl) pass-through. Keys numbered by Blitz3D's standard scan-code table. |
501..509 |
Mouse | MouseHit(1/2/3) for buttons; MXSpeed / MYSpeed / MZSpeed axis tests for "Mouse Up/Down/Left/Right" and scroll-wheel up/down. |
1001..1008 |
Joystick buttons | JoyHit(Ctrl - 1000). |
1009..1016 |
Joystick hat / axes | JoyHat() direction matching for "Hat Up/Down/Left/Right"; JoyXDir() / JoyYDir() for analog stick directions. |
| else | Unknown | ControlName$ returns LanguageString$(LS_Unknown). |
The hat-direction Cases use edge detection: each direction has a JoyHatUp / JoyHatDown / JoyHatLeft / JoyHatRight Global flag that latches True when the hat enters the zone (returning True once), suppresses further hits while held, and resets to False when the hat leaves the zone — so the next entry fires True again. Analog axes (JoyXDir() / JoyYDir()) and buttons (JoyHit) use the standard Blitz idioms — only the hat branch has explicit edge-detection state.
Historical bug — closed by PR #324. Pre-PR-#324, the four hat flags were not declared as
Globalanywhere; under Blitz3D's non-Strict semantics they were function-local variables re-initialized to 0 on everyControlHitcall. The intended edge-detect collapsed to "always return True while in zone" — identical toControlDown. Same hazard family as theLS_SKick/LS_SCGMtypos closed by PR #323; see [[feedback_blitz_nonstrict_undeclared_zero]] agent-memory note. Fix: declare the four flags as file-scopeGlobals and add anElsebranch on each Case to clear the flag when the hat leaves the zone.
ControlName$ is a switch-statement of every supported control number to a human-readable label. Used to populate the key-binding UI in the options menu.
LoadInterfaceSettings / SaveInterfaceSettings (re)build the persistent layout. The full sequence (positional — read order must equal write order) is: Chat + its Chat\Texture (extra ReadShort/WriteShort), ChatEntry, AttributeDisplays(0..39), BuffsArea, Radar, Compass, InventoryWindow, InventoryDrop, InventoryEat, InventoryGold, InventoryButtons(0..Slots_Inventory). Save uses atomic SafeWriteOpen$ / SafeWriteCommit% (Interface.bb:340-368) so an interrupted save can't leave a truncated layout file. LoadInterfaceSettings is a plain ReadFile (Interface.bb:298-337) — no atomicity needed on the read side.
The component shape on disk is 8 fields per record:
WriteFloat X, Y, Width, Height, Alpha ; 5 floats
WriteByte R, G, B ; 3 bytes
Chat carries an extra Texture field (read/written separately as a Short); all other components are pure layout.
Linear scan backward from MaxChars for the last space character — gives the split index for the next line in a word-wrapped paragraph. Returns MaxChars (mid-word break) if no space found in the search window. Returns 0 if the string is shorter than the limit (caller's signal: "no wrap needed").
This is a Blitz-friendly implementation of basic word-wrap — the LTooltip and dialog-multi-line rendering use it.
- A new UI window means a new
Global W<Name>here, paired with allB<Name>/L<Name>button/label handles, and ideally aDimarray if the window has repeating slots. Don't scatter window-state globals across consumer files. InterfaceComponent-backed elements need read/write entries inLoad/SaveInterfaceSettings— otherwise the user's layout customization isn't persisted. The on-disk record order is positional (no length-prefix / tag), so never reorder the read/write sequence — matchRead*order toWrite*order.SaveInterfaceSettingsusesSafeWriteOpen/SafeWriteCommit. If you add new persistent-layout state, follow the same atomic-write pattern (CLAUDE.md → "Atomic writes"). DirectWriteFileto the production path is forbidden.ControlHitvsControlDownreturns slightly different shapes for joystick hat directions (edge-detected via staticTrueflags vs immediate true while held). Don't assume they're symmetric — useControlHitfor menu-navigation single-press,ControlDownfor held-movement input.- Adding a new control number requires entries in
ControlHit,ControlDown, andControlName$. Missing any one breaks UI rebinding or input dispatch. ControlName$returnsLanguageString$(LS_Unknown)for unrecognised control numbers — never hard-code "Unknown" or similar strings; route through theLanguage.bbregistry.
Language.bb—LS_*constants for UI labels;LanguageString$(LS_Unknown)is the fallback forControlName$.Logging.bb— provides theSafeWriteOpen$/SafeWriteCommit%atomic-write helpers used bySaveInterfaceSettings.MainMenu.bb— heaviest consumer; the in-game UI event loop dispatches on the gadget globals declared here.Gooey.bb— provides the underlying Gooey UI primitives (CreateWindow,CreateLabel,CreateListBox,CreateButton, etc.). TheW*/B*/L*handles here are Gooey-allocated.F-UI.bb— the alternate Float-UI system used byMediaDialogs.bb. Coexists with Gooey for editor tools.Radar.bb— theRadar.InterfaceComponentdeclared here describes the placement of the radar HUD overlay; the actual renderer lives inRadar.bb.Interface3D.bb— the 3D-world HUD pieces (chat bubbles in world space, name labels above actor heads) that complement the 2D UI here.
- CLAUDE.md → "Atomic writes" — the canonical
SafeWriteOpen/SafeWriteCommitpattern. P_ChatMessage.md— wire layer feedingChatHistory$()/ChatLines().P_StatUpdate.md— wire layer feeding the per-attribute display bars inAttributeDisplays(39).P_InventoryUpdate.md— wire layer feedingBSlots(Slots_Inventory)and the trading parallel-slot arrays.
This module's source is mostly handle declarations with a handful of small functions. The 11 functions are: DialogScriptHandle, LoadControlBindings, SaveControlBindings, WriteInterfaceComponent, ReadInterfaceComponent, LoadInterfaceSettings, SaveInterfaceSettings, WordWrap, ControlHit, ControlDown, ControlName$. Read the source at src/Modules/Interface.bb for full signatures.