Maturity: Draft. How Cyberpunk 2077 draws its UI (ink), how a mod builds widgets from script, matches the game's look, takes input, adds to every standard screen, draws over the world, adds map pins and markers, and renders 3D into UI; plus the answer on an XF-owned ImGui overlay (§8, from the 30 September feasibility study). Consolidated on 29 September 2026 from the decompiled 2.31 script bundle (r6/, SHA-256 2119046f…ee86, redscript-cli 0.5.31 decompile -f into a private scratch folder; paths below are that tree's), the source of Codeware v1.20.4 (613a1cb8), Red Hot Tools (b4d3415), Equipment-EX (3208ff4), Cyber Engine Tweaks 1.37.1 (9a8522f), Mod Settings (e904815) and Let There Be Flight (6d7c1b7), and the installed script files of mods that inject UI (Dark Future, Vehicle Navigation System, Sheng Noods Ramenshop; read-only in the MO2 mods folder). No claim on this page has been seen in game yet except where marked runtime; the XF Runtime Bridge 0.5.3 demos (§9) are the first in-game test, on the ink demo rows (group F of the session-7 checks). The route map for the in-game ambitions is in-game UI design. Grades follow the knowledge rules.
1. The short answers
| Question | Answer | Grade |
|---|---|---|
| What is ink? | The engine's retained-mode UI: a tree of widgets (inkWidget: canvases, panels, text, images, shapes, video) in layers (inkHUDLayer, inkMenuLayer, inkGameNotificationsLayer, inkWorldLayer, inkLoadingLayer, inkWatermarksLayer and more), driven by game controllers (inkGameController, one per screen or HUD module) and logic controllers (inkLogicController, per widget). Layouts normally come from .inkwidget library resources; styles from .inkstyle; fonts from .inkfontfamily; images from .inkatlas | source 2.31 core/; layer classes in Codeware scripts/; Red Hot Tools src/ |
| Can a mod build widgets from script alone? | Yes, in vanilla redscript: new inkText(), new inkCanvas()… then Reparent(parent) or AddChildWidget. What vanilla lacks is (a) a way to reach a layer or screen you don't already hook, and (b) initialising logic controllers attached to script-built widgets: Codeware adds both (GameInstance.GetInkSystem(), inkComponent, inkCustomController, and a hook after Reparent/AddChild from script that attaches the widget tree to the game systems) | source 2.31 abstractWidgets.script:225-236; Codeware src/, scripts/, wiki "User Interface" |
| How do we match the game's look? | Its font family base\ (styles Regular, Medium, Semi-Bold, Bold) and its colour tokens: SetStyle(r"base\ then BindProperty(n"tintColor", n"MainColors.Red") (also Blue, ActiveRed, MildRed, PanelRed, Green, Yellow, Grey, White…; fontSize tokens MainColors.ReadableSmall and friends). Bound properties follow the game's own theme | source 2.31 scripts (e.g. BindProperty(n"tintColor", n"MainColors.Red") 14 times) and installed mods (main_colors 251 uses, raj 184) |
| How does a HUD element attach? | The HUD layer spawns its HUD entries (each a window with a slot, from the HUD's entry list) with their game controllers (inkHUDGameController subclasses). Mods either wrap an existing HUD controller and add widgets under its root, or (Codeware) add widgets to the HUD layer's own Root | source Red Hot Tools InkWidgetCollector.cpp:207-221 (hudLayer->entries, hudEntryName, slotParams.slotID); Dark Future DFHUDSystem.reds, VNS VNS.reds |
| World-space UI? | Two kinds. Render-to-texture on a mesh: a worlduiWidgetComponent on an entity (terminals, screens, ads, vehicle dashboards) hosts an ink game controller whose window is drawn onto the mesh's UI material. Screen-space, world-anchored: projected HUD controllers (inkProjectedHUDGameController: nameplates, damage digits, mappins) register world points and the engine places widgets every frame | source 2.31 core/, orphans.script inkProjectedHUDGameController, inkScreenProjectionData |
| Custom map pins? | MappinSystem.RegisterMappin(MappinData, position) or RegisterMappinWithObject(data, object, slot, offset) with type Mappins.DefaultStaticMappin and a variant; the variant decides the map filter and icon. A custom icon means wrapping the mappin controllers' UpdateIcon and replacing the icon (an atlas of your own, or ink-drawn) | source 2.31 dropPointSystem.script:380-389, mappin scripts; Sheng Noods Ramenshop's mappinShengNoodsRamen.reds |
| 3D into UI? | Preview game controllers render a puppet or item from a separate preview scene into a widget: inkInventoryPuppetPreviewGameController (the paperdoll), inkItemPreviewGameController, character-creation and gender-selection puppets, inkEntityPreviewGameController, the 3D world map (inkWorldMapPreviewGameController). The puppet is an ordinary entity a script can reach (gameuiInGameMenuGameController.OnPuppetReady, Codeware PlayerSystem.GetInventoryPuppet()) | source 2.31 core/; Equipment-EX scripts/ |
| An own ImGui overlay? | Feasible and recommended for development tools: chain a hook on the engine's own present (GpuApi::Present, where CET draws), submit on the engine's direct queue from the SDK's device data, and take the mouse through a window-procedure subclass in a hotkey interact mode that yields to CET. Not built; about two weeks for three stages (§8) | source CET src/, src/; RED4ext SDK GpuApi/ |
2. Widgets from script
2.1 The widget classes
| Class | Kind | Notes | Grade |
|---|---|---|---|
inkCanvas | compound | absolute placement by anchor, anchor point, margin and translation | source orphans.script:35976 |
inkVerticalPanel, inkHorizontalPanel, inkFlex, inkUniformGrid, inkScrollArea (Codeware) | compound | stacking layouts; SetChildMargin, SetFitToContent | source |
inkText | leaf | SetText, SetLocalizedText(locKey), SetFontFamily(path, style), SetFontStyle, SetFontSize, SetLetterCase, SetTracking, SetWrappingAtPosition, EnableAutoScroll; Codeware adds wrapping, overflow, justification and line height setters | source core/; Codeware scripts/ |
inkImage | leaf | SetAtlasResource(atlas) + SetTexturePart(part), tile and mirror modes, RequestSetImage(iconReference) (async), SetDynamicTextureUV | source orphans.script:31606-31657 |
inkRectangle, inkCircle, inkShape | leaf shapes | a tinted rectangle is the usual panel background, divider and diamond; inkShape takes vertex lists (Codeware documents its fields) | source |
inkMask | leaf | masks siblings by texture or by a dynamic widget | source orphans.script:51050 |
inkVideo | leaf | Bink (.bk2): SetVideoPath, Play, Stop, SetLoop, JumpToTime, SetAudioEvent | source orphans.script:44099-44129 |
inkVectorGraphicWidget, inkLinePattern, inkBorder, inkCacheWidget | leaf / compound | vector art, patterned lines, borders; the cache widget renders its subtree once into a texture (cheap for static, complex panels) | source |
Every widget: SetName, SetVisible, SetOpacity, SetTintColor(HDRColor), SetAnchor(inkEAnchor), SetAnchorPoint, SetMargin, SetPadding, SetSize, SetHAlign/, SetScale, SetRotation, SetShear, SetTranslation, SetRenderTransformPivot, SetInteractive, SetStyle, BindProperty, SetState, PlayAnimation(WithOptions), RegisterToCallback(event, object, function), SetEffectEnabled/SetEffectParamValue (per-widget effects such as blur, glitch and colour correction), and Reparent(parent, opt index) source abstractWidgets.script:2-227. Compound widgets add AddChild(typeName), AddChildWidget, GetWidgetByPathName, RemoveChild, ReorderChild, SetChildOrder source :230-276.
Library widgets. A controller spawns items from its own library (SpawnFromLocal) or another .inkwidget (SpawnFromExternal, async variants), which is how every game screen is built source core/. ArchiveXL's ink-spawner extension (and Codeware's) accept item:Controller to attach a script controller class to the spawned root source (terminals §4). A mod that ships its own .inkwidget gets designer layouts; one that builds in script needs no archive (the XF demos build in script).
2.2 Reaching layers and screens
- Vanilla: only from inside a controller you already hook (
this.GetRootCompoundWidget()in a wrappedOnInitialize), or through objects a system hands you source. - Codeware:
GameInstance.GetInkSystem().GetLayers(),GetLayer(n"inkHUDLayer"),GetWorldWidgets(); a layer wrapper'sGetVirtualWindow(),GetGameController(),GetGameControllers()source Codewarescripts/,UI/ inkSystem.reds src/. Its wiki's own example reparents a text straight into the HUD layer's virtual window source CodewareApp/ UI/ inkSystem.hpp wiki/"Layers and windows".Home.md - Installed practice: Dark Future and Vehicle Navigation System reparent a 3840×2160
inkCanvasintoGetLayer(n"inkHUDLayer").GetVirtualWindow().GetWidgetByPathName(n"Root"), check first whether their named canvas is already there (a layer outlives a session), and scale it with Codeware'sVirtualResolutionWatchersourceDFHUDSystem.reds(CreateFullScreenSlot),VNS.reds(MinimapContainerController.InitializePlayerwrap). The watcher scales by screen size / 3840×2160 per axis, which implies the HUD root is in screen pixels [hypothesis from runtime-proven mods]; per-axis scaling squashes text on ultrawide screens, so the XF overlay scales uniformly by height (2160 units high, as wide as the aspect makes it).
2.3 Styles, fonts and colours
SetStyle(styleResPath)attaches an.inkstyle;BindProperty(propertyName, styleVariable)bindstintColor,fontSize,fontStyle/fontWeightoropacityto a named value in it sourceabstractWidgets.script:24, 221.- The shared style sheets:
base\(the palette),gameplay\ gui\ common\ main_ colors.inkstyle base\,gameplay\ gui\ fullscreen\ fullscreen_ main_ colors.inkstyle …\,common\ dialogs_ popups.inkstyle …\andcommon\ tooltip\ tooltip_ menu.inkstyle tooltip_,hud.inkstyle …\,fullscreen\ hub_ menu\ hub_ menu_ style.inkstyle …\andcommon\ components\ toggles_ style.inkstyle slots_,style.inkstyle …\source path counts in the 2.31 scripts and installed mods.widgets\ notifications\ notification.inkstyle - The HUD's grammar in practice: headers in
MainColors.Red(upper case, Semi-Bold), body text inMainColors.Blue(the cyan), warningsActiveRed, successGreen/ActiveGreen, promptsYellow, dark translucent panels with a thin red edge source token use in 2.31 scripts; the look itself hypothesis until compared with captures. - Codeware keeps a few literal HDR colours (
ThemeColors.ElectricBlue,Bittersweet,Dandelion…) and recommends bindingmain_instead sourcecolors.inkstyle scripts/. HDR colours go above 1.0 for glow (the game's red is aboutUI/ Styles/ ThemeColors.reds 1.176, 0.381, 0.348).
2.4 Animation
inkAnimDef holds interpolators (inkAnimTransparency, inkAnimTranslation, inkAnimScale, inkAnimRotation, inkAnimColor, inkAnimSize, inkAnimMargin, inkAnimPadding, inkAnimShear, inkAnimTextInterpolator, inkAnimEffect), each with duration, delay, easing (SetType, SetMode) and direction; PlayAnimationWithOptions(def, inkAnimOptions) takes loop type (Cycle, PingPong), loop count or infinite, markers and whether it follows time dilation; the proxy's RegisterToCallback(inkanimEventType.OnFinish | OnEndLoop | …, object, function) calls back source orphans.script:6512-6524, 31496-31589, 35211-35229. Library animations play by name (PlayLibraryAnimation). An endless looping animation is a UI-clock tick: the game itself drives text scrolling and minigame buffers from OnEndLoop source core/, networkMinigameBufferController.script:57. With dependsOnTimeDilation false it keeps running while the world is paused [source; in game hypothesis]. The XF overlay ticks this way (about 30 Hz), which ties the tick's lifetime to its widget.
2.5 Input on ink
- Pointer and buttons: a widget set interactive receives
OnHoverOver,OnHoverOut,OnPress,OnRelease(withinkPointerEvent.IsAction(n"click")),OnAxisthroughRegisterToCallbacksource e.g.pauseMenu.script:251-260. Game controllers hear everything throughRegisterToGlobalInputCallback(n"OnPostOnRelease" | n"OnPostOnPress" | n"OnPostOnAxis" | n"OnPostOnRelative", …)(66, 22, 21 and 14 uses in 2.31) source. - The cursor belongs to the layer: a menu layer shows the game's cursor (
CursorGameController, whoseProcessCursorContextpicks the cursor's look per context) sourcecyberpunk/. The HUD layer normally has none; input there comes as gameplay actions. Red Hot Tools makes the topmost layer (UI/ cursors/ cursorGameController.script:111, 343 inkWatermarksLayer) interactive with its own game cursor (spawned frombase\, input contextgameplay\ gui\ widgets\ cursors\ default_ cursor.inkwidget UINotifications) to pick widgets under the pointer sourceInkWidgetCollector.cpp:462-496. That is a native route to an ink "interact mode" without opening a menu; it needs RED4ext. - Keys: Codeware's callback system delivers raw
Input/andKey Input/events, and Input Loader registers new input actions from XML; a script listens for input actions withAxis GameObject.RegisterInputListener(listener, name)source Codewarewiki/(callback events),Home.md scripts/; 2.31Callback/ Events/ KeyInputEvent.reds core/.entity/ gameObject.script:152 - Text entry: Codeware's
TextInput/HubTextInputcomponents sourcescripts/.UI/ TextInput
2.6 Performance
- Ink is retained: a static widget costs layout and draw only when invalidated; text changes re-shape one string. Changing text, tint or translation every frame on a few dozen widgets is what the game's own nameplates do [source; cost unmeasured].
- Controllers spawned from libraries load their
.inkwidgetresources (async where theAsyncspawn is used); script-built widgets load nothing but fonts and atlases already resident source. inkCacheWidgetrenders a static subtree once; the world map, scanner and damage digits use projected controllers so the engine does the per-frame placement source.- The XF overlay: one native call (
XFBridge_, a string of a few hundred bytes) every sixth tick (about 5 Hz), and for nameplates threeHud ProjectPointcalls plus one per visible head per tick [offline design; cost in game unmeasured].
3. Injecting into the standard screens
The general pattern: @wrapMethod the screen's controller OnInitialize (or the function that fills it), call the game first, then add widgets under this.GetRootCompoundWidget() or a named container; add a scenario event with @addMethod(MenuScenario_ to switch to your own menu; tear down in OnUninitialize. Controllers below are source 2.31 unless noted; the "Mods" column names installed or cloned mods that hook them source.
| Screen | Controller(s) | Attach point and hook | Risks | Mods doing it |
|---|---|---|---|---|
| HUD (always-on modules) | inkHUDGameController subclasses: healthbarWidgetGameController, MinimapContainerController, QuestTrackerGameController, hudCarController, buffListGameController, OnscreenMessageGameController… | Codeware: the HUD layer's Root; vanilla: a wrapped HUD controller's root. Forced visibility of HUD entries: UISystem.SetHudEntryForcedVisibility(entryName, worlduiEntryVisibility) | Entries respawn per session; widgets under Root may outlive the session (check by name first); HUD hidden in menus, photo mode, scenes | Dark Future (DFHUDSystem.reds), VNS (wraps MinimapContainerController.InitializePlayer), Let There Be Flight (hudCarController value wraps) |
| Projected HUD (world-anchored) | inkProjectedHUDGameController: NpcNameplateGameController, DamageDigitsGameController, MappinsContainerController, BaseSubtitlesGameController, scannerBorderGameController | RegisterScreenProjection(inkScreenProjectionData) (entity + slot, or staticWorldPosition, fixedWorldOffset, userData) inside such a controller; OnScreenProjectionUpdate places the widgets | RegisterScreenProjection is protected: only code in the controller's class (an @addMethod) can call it, and the controller's update handler must be taught to skip ours | none found |
| Pause menu | PauseMenuGameController (gameuiMenuItemListGameController), scenario MenuScenario_ | wrap PopulateMenuItemList and call AddMenuItem(label, n"OnXF…"); handle it with @addMethod(MenuScenario_ → SwitchMenu(n"…") or your own action | Mod Settings replaces PopulateMenuItemList (a wrap still composes on top); order of added items across mods | Mod Settings (Extensions/, MenuScenario_); Character Customization Anywhere (the pause menu's event) |
| Main menu | SingleplayerMenuGameController (MainMenuGameController), MenuScenario_ | as the pause menu | pre-game: no player, no game systems; scripts may still run | Mod Settings (MenuScenario_) |
| Hub menu (the inventory/map/journal tabs) | MenuHubGameController, HubMenuUtils | Codeware's HubMenuButton/HubLinkButton components, or wraps of the hub's button setup | the hub's layout is fixed width; more tabs may overflow | Equipment-EX, Virtual Atelier (via Browser Extension) |
| Map | WorldMapMenuGameController, BaseWorldMapMappinController, WorldMapTooltipController | wrap UpdateIcon per pin, WorldMapTooltipController.SetData for the tooltip; the 3D map is inkWorldMapPreviewGameController (camera modes, zoom, yaw, pitch) | the map's fast-travel action keys on FastTravelVariant (worldMap.script:811, 1041): never give a custom pin that variant | Sheng Noods Ramenshop, Virtual Atelier Delivery, Ripperdoc Vendor UI Enhancements (tooltips); 8 mods wrap WorldMapTooltipController.SetData (mod survey) |
| Inventory and paperdoll | gameuiInventoryGameController, BackpackMainGameController, inkInventoryPuppetPreviewGameController; the puppet comes from gameuiInGameMenuGameController.OnPuppetReady(sceneName, puppet) ("inventory") | wraps of OnInitialize/refresh functions; the puppet is a normal entity for equipment and component changes | Equipment-EX replaces several inventory functions; order matters | Equipment-EX, Revised Backpack, Stash and Backpack Search, Inventory Zoom |
| Character creator | characterCreationBodyMorphMenu, characterCreationSummaryMenu (BaseCharacterCreationController), inkCharacterCreationPuppetPreviewGameController; scenarios MenuScenario_ | wraps of the menu's option setup and row controllers (photo mode §3, creator presentation) | CCXL and Character Customization Anywhere hook the same menus; the creator is a pre-game-style menu with its own puppet | CCXL (ArchiveXL), Character Customization Anywhere; the XF bridge (cc.*) |
| Settings | SettingsMainGameController (gameuiSettingsMenuGameController), SettingsCategoryController, SettingsSelectorController* | Mod Settings wraps SettingsCategoryController.Setup and the selectors; Native Settings UI does it from CET | the two frameworks touch the same controllers (conflict list) | Mod Settings, Native Settings UI, Redscript Configuration Framework |
| Phone, calls and messages | NewHudPhoneGameController, HudPhoneGameController, MessengerGameController, PhoneMessageNotificationsGameController, JournalNotificationQueue | wraps of the phone's contact and message population; messages themselves are journal entries (terminals §3.3) | phone state is quest-driven; adding contacts needs journal data | NightlyNow Core, Dark Future (phone icon checks) |
| Journal | questLogGameController, CodexGameController, ShardsMenuGameController | wraps of entry population; entries are journal resources (ArchiveXL merges .journal) | quest facts and journal state persist in saves | quest mods through ArchiveXL |
| Photo mode | gameuiPhotoModeMenuController, PhotoModeMenuListItem, gameuiPhotoModeStickersController | AddMenuItem(label, attributeKey, page, isAdditional), the setup callbacks (photo mode) | many photo-mode mods; the bridge already drives it | Photo Mode Ex, Equipment-EX, Customisable Photo Mode UI |
| Vendors, crafting, perks, ripperdoc, wardrobe, time skip | FullscreenVendorGameController, CraftingMainGameController, NewPerksCategoriesGameController, RipperDocGameController, WardrobeUIGameController, TimeskipGameController | wraps of their setup | five mods wrap TimeskipGameController.OnInitialize; Equipment-EX replaces the wardrobe | Immersive Timeskip, Enhanced Craft, Cyberware-EX, Thread Locker |
| Popups and notifications | PopupsManager, gameuiGenericNotificationGameController, Codeware InGamePopup/InMenuPopup/GenericMessageNotification | Codeware's popup components on the notifications layer | modal input capture | Virtual Atelier Delivery, Redscript Configuration Framework |
| Loading screens | inkLoadingLayer, InitialLoadingScreenLogicController | the loading layer (Codeware can reach it) | scripts are detaching and attaching: never touch game systems here (game crashes §2.3) | none found |
Rule for every injected screen [source, from the bridge's crash]: UI code runs on the main thread inside the screen's lifecycle, so it is safe to call game systems while the screen is live, but a callback that outlives its screen (a delay callback, an animation proxy, a listener) must be released in OnUninitialize/OnDetach.
4. The HUD in detail
- Layers: the HUD layer (
inkHUDLayer, a full-screen layer) holds entries, each a window with a name and a slot (hudEntryName,slotParams.slotID), spawned with itsinkHUDGameControllersource Red Hot ToolsInkWidgetCollector.cpp:207-221.UISystem.SetHudEntryForcedVisibility(entryName, visibility)forces an entry shown or hidden sourcecore/.systems/ uiSystem.script:126 - Showing and hiding:
inkHUDGameController.ShowRequest/HideRequestwith intro and outro animations (GetIntroAnimation,GetOutroAnimation) and context changes (combat, vehicles, scenes) sourcecore/. The whole HUD hides in menus, photo mode and the creator.ui/ baseControllers/ hudGameController.script:20-108 - Positioning: HUD entries sit in safe-zone slots (the HUD safe-zone editor,
gameuiHudSafezonesEditorGameController); a mod's widget underRootis placed by anchor and margin in the root's units (screen pixels hypothesis, §2.2). - An XF HUD module can be: widgets under
Root(built, Demo A); a wrapped vanilla entry's root (couples to that entry's visibility); or a true HUD entry (needs the HUD's entry list and an.inkwidget; not researched further).
5. World-space ink
5.1 Render-to-texture on a surface (terminals, screens, holograms)
- An entity carries a
worlduiWidgetComponent(IWorldWidgetComponent):widgetResource(an.inkwidgetlibrary),itemNameToSpawn,sceneWidgetProperties,meshTargetBinding(the mesh and its UI material the window is drawn onto),screenAreaMultiplier,spawnDistanceOverride,tintColor,glitchValue,isEnabled; script reaches the spawned tree throughGetWidget()andGetGameController()source 2.31core/, Codewarecomponents/ uiComponents.script:2-63 scripts/,Base/ Addons/ IWorldWidgetComponent.reds worlduiWidgetComponent.reds. Devices request it by name inOnRequestComponents(n"ui") source e.g.apartmentScreen.script:7,arcadeMachine.script:32. How a computer builds its screen and takes cursor input: terminals §2. - The world layer (
inkWorldLayer) keeps every such component's window; Codeware'sGetWorldWidgets()lists their controllers source Codewaresrc/.App/ UI/ inkSystem.hpp:40-60 - Adding one to our own entity: either in the entity template (an archive change: the showroom build would add a component bound to a flat plate mesh with a UI material and a small
.inkwidget), or at assembly time through Codeware's entity events (Entity/→Assemble AddComponent) source Codeware wiki "Components" ("add components at the right moment, for example when entity is assembled"). No mod was found doing the second for a world widget; Let There Be Flight's attempt is commented out sourcesrc/. Untested, the route of the pedestal labels' next step (design).redscript/ Extensions/ VehicleObject.reds:8-20
5.2 World-anchored, screen-space (nameplates, floating labels)
- Engine projection: a projected HUD controller registers
inkScreenProjectionData(an entity and slot, or a static world position, a fixed world offset, distance adjustment) and getsOnScreenProjectionUpdatewith the placed projections;ApplyProjectionMarginOnWidgetpositions a widget sourceorphans.script:44765-44775, 51626-51647; used by NPC nameplates, damage digits and mappins. - Script projection:
CameraSystem.ProjectPoint(worldPoint)with the active camera's transform, forward, right and up sourceorphans.script:26738-26740. Which screen space it answers in is not settled, so the caller detects it from a point straight ahead (the bridge'sphoto.framedoes,tools/) source. This is what Demo B uses: a tick behind the game's own projection by at most one UI tick [hypothesis: visible lag when the camera moves fast].api/ framing.ts
6. Map pins, markers and the compass
- Registering:
MappinData { mappinType: TweakDBID, variant: gamedataMappinVariant, active, debugCaption, visibleThroughWalls, scriptData };MappinSystem.RegisterMappin(data, position),RegisterMappinWithObject(data, object, opt slotName, opt offset),SetMappinPosition,SetMappinActive,ChangeMappinVariant,UnregisterMappin,GetMappins(targetType)sourceorphans.script:17783-17856. The game's drop points useMappins.DropPointStaticMappin; mods useMappins.DefaultStaticMappinsourcedropPointSystem.script:384-387; AMMModules/(autil.lua FastTravelVariantmarker over a spawned object), Sheng Noods Ramenshop. - What a pin shows: its variant maps to an icon part (
MappinUIUtils.MappinToTexturePart), a colour token (MappinToColor), a label key and a world-map filter group (GetFilterGroup(variant)) sourcecyberpunk/.UI/ mappins/ mappinUtils.script IMappin.GetDisplayName()answers the data'sdebugCaptionfor a runtime pin [source: mods rely on it; in game hypothesis for XF]. - Where it shows: world map (
BaseWorldMapMappinController), minimap (MinimapPOIMappinController,MinimapDeviceMappinController), in the world (QuestMappinController,GameplayMappinController, which clamp to the screen edge, scale with distance, show distance and a name line), and tooltips (WorldMapTooltipController.SetData) source. The compass ribbon is the minimap's container; tracked pins drive the GPS path (GetManuallyTrackedMappinID,SetMappinTrackingAlternative) source. - Custom icons: each controller's
UpdateIconsets the icon from the variant; a wrap that runs after it can swap the atlas and part (a mod's own.inkatlas) or hide the icon and draw ink instead; identify your pins byGetNewMappinID()or by a caption convention source Sheng Noods Ramenshop's wraps (its Nexus page credits Hepero for the map-icon script). - Persistence: runtime pins belong to the session's mappin system [hypothesis: gone after a load; session 7 checks]; points of interest keep saved phase and variant (
GetPointOfInterestMappinSavedState) source. - Quest markers come from journal objectives (
GetMappinFromObjective, quest mappins with phases); a real XF quest marker means a journal entry and quest phase (ArchiveXL), not a runtime pin source.
7. 3D in UI, video and render targets
- Paperdoll:
inkInventoryPuppetPreviewGameController(aninkPuppetPreviewGameController:GetGamePuppet(), rotation by axis input,AnimFeature_for its pose) renders the puppet the in-game menu spawns for the "inventory" scene;Paperdoll gameuiInGameMenuGameController.OnPuppetReady(sceneName, puppet)hands it over and Equipment-EX dresses it there sourceentityPreviewGameController.script:2-80; Equipment-EX. The camera setup comes fromgameuiPuppetPreview_source Codeware addon of that name.SetCameraSetupEvent - Other 3D-in-UI: item previews (
inkItemPreviewGameController.PreviewItem(itemID): weapons and clothes), crafting garment previews, the character creator's and gender selection's puppets,inkEntityPreviewGameController(vehicles), the 3D world map, the tarot preview source. - A holographic V with per-limb damage [hypothesis, the route]: the paperdoll puppet is an entity, so its components can be changed after
OnPuppetReady(CodewareGetComponents,meshAppearance+LoadAppearance(), orchunkMask); a hologram look needs mesh appearances with a hologram material (an XF archive adding appearances to V's body meshes through ArchiveXL, or extra mesh components per limb added to the puppet only) and per-limb states as appearance switches, since runtime material parameters are per resource, not per instance. The preview scene's lighting and post-process are its own, which suits an emissive hologram. First test: switch the puppet's body appearance in the inventory and confirm the gameplay V is unchanged. - Video:
inkVideoplays Bink (.bk2) in UI;BinkComponentplays video in the world; journal briefings and internet pages name videos sourceorphans.script:44099-44129; Codeware addonsBinkComponent.reds,JournalBriefingVideoSection.reds. - Dynamic images:
inkImage.RequestSetImage(inkIconReference)loads icons asynchronously; the gallery shows screenshots through its own preview controller;inkCacheWidgetrenders ink into a cached texture. No script-reachable way to render an arbitrary camera into a widget was found [source; searchedgalleryfullpreview.script, preview controllers].
8. An XF ImGui host beside CET
The full study, with the component diagram, prototype plan and test card, is ImGui overlay feasibility (30 September 2026). Grades as above; [external] marks a public issue report or vendor document.
| Question | Answer | Grade |
|---|---|---|
| Where does CET draw? | Inside the engine's own present wrapper, GpuApi::Present (address-library hash 2468877568), hooked with MinHook from DllMain. It records ImGui into its own command list and submits it on the engine's direct queue, before calling the original; it never hooks DXGI or ExecuteCommandLists. It also hooks GpuApi::ResizeBackbuffer (239671909), dropping its back-buffer references before the original runs, and an unnamed render shutdown (3192982283) | source CET 9a8522f src/, D3D12_ |
| Where do the swap chain and queue come from? | CET uses a hand-written struct with hard-coded offsets; the RED4ext SDK now decodes the same memory as GpuApi::GetDeviceData() (swap chains with their HDR mode, fullscreen flag and window; the device; the direct queue), identical in the tag the bridge pins | source CET src/; SDK GpuApi/, GpuApi/ |
| What sits below that point? | Swap-chain proxies (ReShade's dxgi.dll, Streamline's interposer for DLSS frame generation, FSR frame generation) and the OS overlays. An engine-level overlay is drawn before all of them: ReShade's effects apply to it, and frame generation, which warps a hudless buffer and may recompose a tagged UI buffer, knows nothing of its pixels, so it may ghost or flicker | source ReShade 7bf9de8 source/; Streamline docs/ §5.1, §12.0; effect hypothesis |
| Why not hook DXGI or the queue? | They are the proxies' territory, DLSS-G presents from another thread, and a second queue is fatal with some overlays: ReShade records that DLSS-G uses its own presentation queue and that the GOG Galaxy overlay, guessing the queue from the first ExecuteCommandLists, gets the device removed when it guesses wrong | source ReShade source/ |
Can two hooks share GpuApi::Present? | Yes. RED4ext attaches plugin hooks with Detours after CET's DllMain MinHook, so an XF detour runs first and chains into CET's; Detours relocates CET's leading jump. The danger is unhooking out of order, so an XF detour is never detached at run time (a flag makes it a pass-through) and RED4ext's exit detach runs before CET's | source RED4ext c52c8d8 src/, src/; Detours 9764ceb src/ |
| Does CET offer a native draw callback? | No: no exports, only Lua's onDraw, whose windows take input only while CET's overlay is open | source src/ |
| How does CET take input? | A window-procedure subclass of the W2ViewportClass window; while its overlay is open it swallows every mouse, keyboard and WM_ message and shows the cursor with the engine's ForceCursor(reason "ImGui") (hash 2130646213). Its hotkeys are read from raw keyboard records | source src/, src/, src/ |
| How would an XF interact mode work? | A hotkey (default F10, unbound in the 2.31 defaults) toggles a mode that swallows only the mouse's Raw Input, shows the cursor with an XF reason and leaves the keyboard to the game (V keeps moving, time runs) unless an XF text field has focus; key-ups whose key-downs reached the game always pass, so nothing sticks; focus loss, the kill switch and CET's overlay opening (reported by the bridge's CET layer) drop it to off | design; key-up rule source ReShade source/; keys resource r6/ |
| HDR? | The game has SDR, PQ and scRGB outputs. CET draws with an R8G8B8A8_ pipeline whatever the format, and its overlay is "excessively bright in HDR" (open issue #955). ReShade's overlay encodes its colours to scRGB or PQ at a paper-white level in its pixel shader, the model for an XF renderer | source CET D3D12_; [external] CET #955; source ReShade res/ |
| Fonts and uploads? | Dear ImGui 1.92's DX12 backend uploads new glyphs inside the draw call and waits on a fence with no timeout, on the render thread: pre-bake glyphs, then take texture updates out of the draw call | source Dear ImGui v1.92.9-docking backends/ |
| Anti-cheat? | None in the single-player game (no EasyAntiCheat or BattlEye files in the install) | resource |
| Frame generation risks? | Beyond ghosting, CET has open crash-on-exit reports with FSR 3.1 frame generation enabled (2.3 and 2.31) | [external] CET #1025, #1047 |
Verdict: feasible, recommended for development tools (dense, immediate-mode panels, live values, gizmos) beside, not instead of, the ink route for player-facing UI. Confidence is high for drawing, medium for interact mode and medium-low for frame generation and HDR until a session. First prototype: draw-only, off by default, refusing in HDR (about 4 days plus a session), then interact mode (about 3 days), then an own HDR-aware renderer and the panel kit (about 5 days).
9. The XF Runtime Bridge's ink demos (0.5.3, temporary test features)
| Demo | What | Route | Status |
|---|---|---|---|
| A: XF HUD panel | The bridge's state and the coordinator's ui.message lines in the game's font and colour tokens, a dark panel with a red edge, top right by default; ui.hud shows, hides, moves, scales it and picks its layer; the CET label stays behind [ui] cet_ | Codeware's HUD-layer Root; the overlay pulls a tab-separated frame from the plugin (XFBridge_, core/) about 5 times a second; a looping ink animation is its tick; the plugin never calls into it (no RB-76 exposure) | [offline] unit tests and self-test; [unverified] in game |
| B: pedestal nameplates | Each showroom head's number and preset name on its pedestal's front, scaled and faded with distance, up to 15 m | Script projection (§5.2) on the same tick; world queries only while the frame's script gate says live 1 | [unverified] |
| C: XF map pins | world.pin at a point, above a showroom head (bound to it) or above V; an ink-drawn XF badge and label on the world map, minimap and in the world, the tooltip names it; world.pin.clear, the kill switch and a load clear them | MappinSystem with Mappins.DefaultStaticMappin and an allowlisted variant (never fast travel); UpdateIcon wraps on four controllers and the tooltip | [offline] parameter checks and simulated pins; [unverified] in game |
Code: redscript/, redscript/, native/ in projects/.
Open questions
- Is the HUD layer's
Rootin screen pixels (the overlay's uniform scale assumes it), and does a widget left under it survive a load? - Which layers stay visible in photo mode and menus:
inkGameNotificationsLayer,inkWatermarksLayer? Is the watermarks layer drawn while it is not "active"? - Does an infinite looping ink animation keep ticking while the HUD is hidden, in menus and in photo mode?
- Which screen space does
CameraSystem.ProjectPointanswer in (shared with runtime access, question 9)? - Which controller draws a
DefaultStaticMappinin the world for each variant, and where on its root does a centred child sit relative to the icon? - Do runtime mappins vanish with a load, and does
RegisterMappinWithObjectfollow a Codeware static entity? - Can a
worlduiWidgetComponentbe added at assembly time to a spawned entity and draw on a plate mesh (§5.1)? - Does switching the inventory puppet's appearance affect only the preview (§7)?
- For an XF ImGui host (§8): does the game register Raw Input with legacy messages off; does
ForceCursorkeep a second reason independent of CET's; do XF pixels ghost or flicker under DLSS or FSR frame generation; is the exit order of the two present hooks safe? (feasibility §9)
Related pages
Terminals, browser and arcade · Photo mode · Runtime access · Game crashes · In-game UI design · Mod ecosystem survey · Runtime bridge design · ImGui overlay feasibility