-
Notifications
You must be signed in to change notification settings - Fork 5
Loom (Alpha) #1 of 4: skeleton + theme + Project Manager launcher #292
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,182 @@ | ||
| // ============================================================================= | ||
| // Loom.bb -- Loom World Editor (Alpha) | ||
| // ============================================================================= | ||
| // | ||
| // A drop-in alternative to GUE, sharing the on-disk data formats but with | ||
| // a fresh UI built around the Loom design concept (see | ||
| // .claude/skills/loom-design-brief/ and the prototype handoff bundle). | ||
| // | ||
| // Architecture overview (the multi-PR roadmap; this commit ships only #1): | ||
| // | ||
| // #1 Skeleton + theme + Project Manager launcher | ||
| // Loom.exe compiles, Project Manager launches it, | ||
| // shows a themed splash, exits cleanly. THIS COMMIT. | ||
| // | ||
| // #2 Data loading + atlas | ||
| // Loom uses GUE's existing data modules (Items.bb, Actors.bb, | ||
| // Spells.bb, ServerAreas.bb, ...) via Include. After load, | ||
| // the atlas surface lists every zone in the project. | ||
| // | ||
| // #3 World view | ||
| // Picking a zone in the atlas renders it in a 3D viewport | ||
| // using Blitz3D's engine (the same engine GUE's Zones tab uses). | ||
| // Click an entity to select it. | ||
| // | ||
| // #4 Composer | ||
| // Right-side property panel that paints the focused entity's | ||
| // data (faction, level, mesh, equipped items) using Loom theme | ||
| // primitives. Read-only for the alpha. | ||
| // | ||
| // Design intent for the alpha as a whole: "Loom can open my existing | ||
| // Realm Crafter project and let me look at my world through a different | ||
| // lens." Editing comes in beta. | ||
| // ============================================================================= | ||
|
|
||
|
|
||
| // ----------------------------------------------------------------------------- | ||
| // Bootstrap globals (mirrors GUE.bb's startup so the relative paths work | ||
| // identically -- both binaries live in bin/ and are launched with CWD set to | ||
| // <project>/Data/). | ||
| // ----------------------------------------------------------------------------- | ||
| Global rcceVersion$ = "2.0.0" | ||
| Global componentName$ = "loom" | ||
| Global RootDir$ = "..\" | ||
|
|
||
| ChangeDir RootDir$ | ||
|
|
||
|
|
||
| // ----------------------------------------------------------------------------- | ||
| // Includes -- minimum surface for the skeleton. | ||
| // | ||
| // PR #1 deliberately does NOT include the data modules (Items, Actors, | ||
| // Spells, etc.) or F-UI. The skeleton's only job is to prove the build | ||
| // pipeline and the Project Manager hook work. PR #2 will add Logging- | ||
| // adjacent data loaders. PR #3 brings in Blitz3D's 3D pipeline for the | ||
| // world view. | ||
| // ----------------------------------------------------------------------------- | ||
| Include "Modules\Logging.bb" | ||
| Include "Modules\Loom\Theme.bb" | ||
|
|
||
|
|
||
| // ----------------------------------------------------------------------------- | ||
| // Graphics mode -- match GUE's window sizing so the two editors feel sibling. | ||
| // ----------------------------------------------------------------------------- | ||
| Local Loom_width# = GetSystemMetrics(0) * 0.9 | ||
| Local Loom_height# = GetSystemMetrics(1) * 0.8 | ||
| If (Loom_width < 1280 And Loom_height < 800) | ||
| Loom_width = 1280 | ||
| Loom_height = 800 | ||
| EndIf | ||
|
|
||
| Graphics3D(Loom_width, Loom_height, 0, 2) | ||
| SetBuffer(BackBuffer()) | ||
| AppTitle("Loom -- World Editor (Alpha) -- Realm Crafter " + rcceVersion$) | ||
|
|
||
|
|
||
| // ----------------------------------------------------------------------------- | ||
| // Log -- written to Data\Logs\Loom Log.txt (relative to project root, the | ||
| // same place GUE writes its log). | ||
| // ----------------------------------------------------------------------------- | ||
| Global LoomLog = StartLog("Loom Log", False) | ||
| WriteLog(LoomLog, "** Loom startup begins **", True, True) | ||
| WriteLog(LoomLog, "Resolution: " + Str(Loom_width) + "x" + Str(Loom_height)) | ||
|
|
||
|
|
||
| // ----------------------------------------------------------------------------- | ||
| // Resolve project name from the working directory. When PM launches us, CWD | ||
| // has been set to <project>/Data/ and then ChangeDir "..\" walked us up to | ||
| // <project>/. The leaf folder name is the project's display name. | ||
| // ----------------------------------------------------------------------------- | ||
| Local cwd$ = CurrentDir$() | ||
| Local projectName$ = LoomGetLeafDir(cwd$) | ||
| WriteLog(LoomLog, "Project root: " + cwd$) | ||
| WriteLog(LoomLog, "Project name: " + projectName$) | ||
|
|
||
| LoomTheme_Init() | ||
|
|
||
|
|
||
| // ----------------------------------------------------------------------------- | ||
| // Splash screen loop. Runs until Esc. | ||
| // PR #2 replaces this with the atlas as the boot surface. | ||
| // ----------------------------------------------------------------------------- | ||
| WriteLog(LoomLog, "** Splash loop running **") | ||
|
|
||
| Repeat | ||
| Cls | ||
| LoomRenderSplash(Loom_width, Loom_height, projectName$) | ||
| Flip | ||
| Until KeyHit(1) | ||
|
|
||
| WriteLog(LoomLog, "** Loom shutdown **") | ||
| CloseAllLogs() | ||
| End | ||
|
|
||
|
|
||
| // ============================================================================= | ||
| // LoomRenderSplash -- paint the alpha splash surface. | ||
| // | ||
| // Layout: | ||
| // - Full-screen vertical gradient stone_900 -> stone_950 | ||
| // - Centered "LOOM" title in parchment | ||
| // - "WORLD EDITOR" subtitle in brass, spaced | ||
| // - Brass divider rule | ||
| // - Project context line | ||
| // - Footer instruction | ||
| // ============================================================================= | ||
| Function LoomRenderSplash(sw, sh, projectName$) | ||
| // Background gradient (BlitzForge does not support line continuation, | ||
| // so these calls are intentionally long single lines.) | ||
| LoomGradientV(0, 0, sw, sh, LOOM_STONE_900_R, LOOM_STONE_900_G, LOOM_STONE_900_B, LOOM_STONE_950_R, LOOM_STONE_950_G, LOOM_STONE_950_B) | ||
|
|
||
| Local cx = sw / 2 | ||
| Local cy = sh / 2 | ||
|
|
||
| // Title -- "LOOM" centered, drawn twice with a 1px offset to fake bolder | ||
| // weight on top of the Blitz default font. Real display fonts arrive in | ||
| // a later PR. | ||
| LoomTextCentered(cx, cy - 90, "LOOM", LOOM_PARCHMENT_100_R, LOOM_PARCHMENT_100_G, LOOM_PARCHMENT_100_B) | ||
| LoomTextCentered(cx + 1, cy - 90, "LOOM", LOOM_PARCHMENT_100_R, LOOM_PARCHMENT_100_G, LOOM_PARCHMENT_100_B) | ||
|
|
||
| // Subtitle | ||
| LoomTextCentered(cx, cy - 64, "W O R L D E D I T O R", LOOM_BRASS_500_R, LOOM_BRASS_500_G, LOOM_BRASS_500_B) | ||
|
|
||
| // Brass divider (triple rule for an ornamented bar) | ||
| LoomHRule(cx - 180, cy - 40, 360, LOOM_BRASS_700_R, LOOM_BRASS_700_G, LOOM_BRASS_700_B) | ||
| LoomHRule(cx - 180, cy - 39, 360, LOOM_BRASS_500_R, LOOM_BRASS_500_G, LOOM_BRASS_500_B) | ||
| LoomHRule(cx - 180, cy - 38, 360, LOOM_BRASS_700_R, LOOM_BRASS_700_G, LOOM_BRASS_700_B) | ||
|
|
||
| // Project context | ||
| LoomTextCentered(cx, cy - 16, "Alpha for " + projectName$, LOOM_STONE_200_R, LOOM_STONE_200_G, LOOM_STONE_200_B) | ||
| LoomTextCentered(cx, cy + 2, "Realm Crafter Community Edition " + rcceVersion$, LOOM_STONE_300_R, LOOM_STONE_300_G, LOOM_STONE_300_B) | ||
|
|
||
| // Skeleton-stage notice (will be removed in PR #2 when the atlas becomes | ||
| // the boot surface). | ||
| LoomTextCentered(cx, cy + 60, "skeleton build -- atlas, world view, and composer arrive in subsequent PRs", LOOM_STONE_300_R, LOOM_STONE_300_G, LOOM_STONE_300_B) | ||
|
|
||
| // Footer | ||
| LoomTextCentered(cx, sh - 40, "Esc to exit", LOOM_STONE_300_R, LOOM_STONE_300_G, LOOM_STONE_300_B) | ||
| End Function | ||
|
|
||
|
|
||
| // ============================================================================= | ||
| // LoomGetLeafDir -- return the leaf folder name from a directory path. | ||
| // E.g. "C:\rcce2\projects\Embergloom" -> "Embergloom". | ||
| // Falls back to the whole path if no separator is found. | ||
| // ============================================================================= | ||
| Function LoomGetLeafDir$(path$) | ||
| Local trimmed$ = path$ | ||
| // Strip trailing slashes / backslashes so the leaf isn't an empty string. | ||
| While Len(trimmed$) > 1 And (Right$(trimmed$, 1) = "\" Or Right$(trimmed$, 1) = "/") | ||
| trimmed$ = Left$(trimmed$, Len(trimmed$) - 1) | ||
| Wend | ||
|
|
||
| Local lastSep = 0 | ||
| Local i = 0 | ||
| For i = 1 To Len(trimmed$) | ||
| Local ch$ = Mid$(trimmed$, i, 1) | ||
| If ch$ = "\" Or ch$ = "/" Then lastSep = i | ||
| Next | ||
|
|
||
| If lastSep = 0 Then Return trimmed$ | ||
| Return Mid$(trimmed$, lastSep + 1) | ||
| End Function |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,158 @@ | ||
| // ============================================================================= | ||
| // Loom/Theme.bb -- color palette and 2D drawing primitives | ||
| // ============================================================================= | ||
| // | ||
| // The Loom design (see ../../design/loom-prototype/) uses a dark-fantasy | ||
| // palette pulled from three places: deep-navy night sky, stone-gray panels | ||
| // with brass trim, and parchment cream for body text. This file encodes the | ||
| // design tokens as RGB triplets, and provides drawing helpers that wrap | ||
| // Blitz3D's primitive Color/Rect/Line/Text calls so the rest of Loom can | ||
| // paint surfaces without restating colors at every site. | ||
| // | ||
| // Why custom-draw at all (vs. F-UI for everything): | ||
| // The Loom aesthetic is gradient-heavy and uses ornamented type. F-UI | ||
| // renders flat stone-gray panels with system fonts; it cannot express the | ||
| // look without a fork. Building one custom-draw layer here lets every | ||
| // surface (atlas, world view, composer, ribbon, palette) share the same | ||
| // visual language. F-UI is reserved for things it is genuinely good at | ||
| // (file dialogs, native text input). | ||
| // | ||
| // Public API (everything here is called Loom_*): | ||
| // | ||
| // Color tokens (use with LoomColor or directly): | ||
| // LOOM_STONE_950, LOOM_STONE_900, ..., LOOM_STONE_100 | ||
| // LOOM_ARCANE_900 ... LOOM_ARCANE_300 (primary accent) | ||
| // LOOM_BRASS_800 ... LOOM_BRASS_300 (ornament, ranks) | ||
| // LOOM_PARCHMENT_100, LOOM_PARCHMENT_200 (body text) | ||
| // LOOM_INK_900, LOOM_INK_700 (text on parchment) | ||
| // LOOM_SUCCESS, LOOM_WARNING, LOOM_DANGER (semantic) | ||
| // | ||
| // LoomColor(r, g, b) -- thin wrapper for Blitz Color | ||
| // LoomFill(x, y, w, h, r, g, b) -- filled rectangle | ||
| // LoomBorder(x, y, w, h, r, g, b) -- 1px outline rectangle | ||
| // LoomGradientV(x, y, w, h, r1, g1, b1, r2, g2, b2) | ||
| // -- vertical gradient, top -> bottom | ||
| // LoomHRule(x, y, w, r, g, b) -- 1px horizontal divider | ||
| // LoomText(x, y, text$, r, g, b, align=0) | ||
| // -- single-line text. align: 0=left, 1=center, 2=right | ||
| // LoomTextCentered(cx, y, text$, r, g, b) | ||
| // -- text centered horizontally at cx | ||
| // | ||
| // LoomTheme_Init() -- one-time setup. Loads any fonts we need | ||
| // (current alpha: none -- uses default font). | ||
| // ============================================================================= | ||
|
|
||
|
|
||
| // ----------------------------------------------------------------------------- | ||
| // COLOR TOKENS -- packed RGB triplets exposed as separate r/g/b constants so | ||
| // callers can write `LoomFill(..., LOOM_STONE_900_R, LOOM_STONE_900_G, | ||
| // LOOM_STONE_900_B)`. Blitz3D's Color() takes three ints; we'd rather have | ||
| // the readable token name at every call site than a single packed int that | ||
| // has to be unpacked. | ||
| // ----------------------------------------------------------------------------- | ||
|
|
||
| // Stone & shadow (panels, backgrounds) | ||
| Const LOOM_STONE_950_R = 8 : Const LOOM_STONE_950_G = 9 : Const LOOM_STONE_950_B = 15 // #08090f deep void | ||
| Const LOOM_STONE_900_R = 14 : Const LOOM_STONE_900_G = 16 : Const LOOM_STONE_900_B = 24 // #0e1018 canvas | ||
| Const LOOM_STONE_850_R = 20 : Const LOOM_STONE_850_G = 23 : Const LOOM_STONE_850_B = 42 // #14172a card surface | ||
| Const LOOM_STONE_800_R = 28 : Const LOOM_STONE_800_G = 34 : Const LOOM_STONE_800_B = 56 // #1c2238 raised | ||
| Const LOOM_STONE_700_R = 38 : Const LOOM_STONE_700_G = 42 : Const LOOM_STONE_700_B = 58 // #262a3a sunken | ||
| Const LOOM_STONE_500_R = 79 : Const LOOM_STONE_500_G = 75 : Const LOOM_STONE_500_B = 72 // #4f4b48 mortar | ||
| Const LOOM_STONE_300_R = 138 : Const LOOM_STONE_300_G = 133 : Const LOOM_STONE_300_B = 128 // #8a8580 dust | ||
| Const LOOM_STONE_200_R = 179 : Const LOOM_STONE_200_G = 174 : Const LOOM_STONE_200_B = 164 // #b3aea4 dry stone | ||
| Const LOOM_STONE_100_R = 216 : Const LOOM_STONE_100_G = 209 : Const LOOM_STONE_100_B = 194 // #d8d1c2 lit limestone | ||
|
|
||
| // Arcane blue (primary accent) | ||
| Const LOOM_ARCANE_900_R = 17 : Const LOOM_ARCANE_900_G = 38 : Const LOOM_ARCANE_900_B = 79 // #11264f | ||
| Const LOOM_ARCANE_700_R = 30 : Const LOOM_ARCANE_700_G = 80 : Const LOOM_ARCANE_700_B = 153 // #1e5099 | ||
| Const LOOM_ARCANE_500_R = 61 : Const LOOM_ARCANE_500_G = 166 : Const LOOM_ARCANE_500_B = 245 // #3da6f5 signature | ||
| Const LOOM_ARCANE_300_R = 168 : Const LOOM_ARCANE_300_G = 220 : Const LOOM_ARCANE_300_B = 255 // #a8dcff | ||
|
|
||
| // Brass / gold (ornament) | ||
| Const LOOM_BRASS_800_R = 85 : Const LOOM_BRASS_800_G = 59 : Const LOOM_BRASS_800_B = 19 // #553b13 | ||
| Const LOOM_BRASS_700_R = 122 : Const LOOM_BRASS_700_G = 88 : Const LOOM_BRASS_700_B = 33 // #7a5821 | ||
| Const LOOM_BRASS_500_R = 201 : Const LOOM_BRASS_500_G = 164 : Const LOOM_BRASS_500_B = 74 // #c9a44a signature | ||
| Const LOOM_BRASS_300_R = 243 : Const LOOM_BRASS_300_G = 220 : Const LOOM_BRASS_300_B = 160 // #f3dca0 | ||
|
|
||
| // Parchment & ink | ||
| Const LOOM_PARCHMENT_100_R = 246 : Const LOOM_PARCHMENT_100_G = 239 : Const LOOM_PARCHMENT_100_B = 220 // #f6efdc body | ||
| Const LOOM_PARCHMENT_200_R = 235 : Const LOOM_PARCHMENT_200_G = 223 : Const LOOM_PARCHMENT_200_B = 185 // #ebdfb9 muted | ||
| Const LOOM_INK_900_R = 22 : Const LOOM_INK_900_G = 17 : Const LOOM_INK_900_B = 10 // #16110a | ||
| Const LOOM_INK_700_R = 58 : Const LOOM_INK_700_G = 46 : Const LOOM_INK_700_B = 29 // #3a2e1d | ||
|
|
||
| // Semantic | ||
| Const LOOM_SUCCESS_R = 76 : Const LOOM_SUCCESS_G = 174 : Const LOOM_SUCCESS_B = 79 | ||
| Const LOOM_WARNING_R = 230 : Const LOOM_WARNING_G = 162 : Const LOOM_WARNING_B = 58 | ||
| Const LOOM_DANGER_R = 184 : Const LOOM_DANGER_G = 48 : Const LOOM_DANGER_B = 42 | ||
|
|
||
|
|
||
| // ----------------------------------------------------------------------------- | ||
| // State: fonts loaded by LoomTheme_Init. For the alpha skeleton we just use | ||
| // the Blitz default font; later phases will load MedievalSharp / Cinzel / | ||
| // Cormorant Garamond ttf files from Data/Loom/Fonts/ when those are added. | ||
| // ----------------------------------------------------------------------------- | ||
| Global LoomFont_Body = 0 | ||
| Global LoomFont_Display = 0 | ||
|
|
||
|
|
||
| // ============================================================================= | ||
| // LoomTheme_Init -- one-time setup. Safe to call multiple times. | ||
| // ============================================================================= | ||
| Function LoomTheme_Init() | ||
| // Future: LoadFont "Data/Loom/Fonts/MedievalSharp.ttf", 48 etc. | ||
| // For the alpha, leave both as 0 -- Blitz uses the default system font. | ||
| End Function | ||
|
|
||
|
|
||
| // ============================================================================= | ||
| // Drawing primitives | ||
| // ============================================================================= | ||
|
|
||
| Function LoomColor(r, g, b) | ||
| Color r, g, b | ||
| End Function | ||
|
|
||
| Function LoomFill(x, y, w, h, r, g, b) | ||
| Color r, g, b | ||
| Rect x, y, w, h, True | ||
| End Function | ||
|
|
||
| Function LoomBorder(x, y, w, h, r, g, b) | ||
| Color r, g, b | ||
| Rect x, y, w, h, False | ||
| End Function | ||
|
|
||
| // Vertical gradient by drawing N horizontal stripes with interpolated color. | ||
| // Step size is 1 pixel for smoothness; if h is large and perf matters, callers | ||
| // can fake bigger steps. This is fine for splashes and panels. | ||
| Function LoomGradientV(x, y, w, h, r1, g1, b1, r2, g2, b2) | ||
| If h <= 0 Then Return | ||
| Local i = 0 | ||
| Local rr, gg, bb | ||
| For i = 0 To h - 1 | ||
| // Interpolate between (r1,g1,b1) at i=0 and (r2,g2,b2) at i=h-1. | ||
| // Multiply-then-divide to keep ints in range. | ||
| rr = r1 + ((r2 - r1) * i) / (h - 1) | ||
| gg = g1 + ((g2 - g1) * i) / (h - 1) | ||
| bb = b1 + ((b2 - b1) * i) / (h - 1) | ||
| Color rr, gg, bb | ||
| Line x, y + i, x + w - 1, y + i | ||
| Next | ||
| End Function | ||
|
|
||
| Function LoomHRule(x, y, w, r, g, b) | ||
| Color r, g, b | ||
| Line x, y, x + w - 1, y | ||
| End Function | ||
|
|
||
| // align: 0 = left, 1 = center, 2 = right (relative to x). | ||
| Function LoomText(x, y, txt$, r, g, b, align = 0) | ||
| Color r, g, b | ||
| Text x, y, txt$, align, 0 | ||
| End Function | ||
|
|
||
| // Convenience: text centered horizontally at cx. Vertically aligned by y top. | ||
| Function LoomTextCentered(cx, y, txt$, r, g, b) | ||
| Color r, g, b | ||
| Text cx, y, txt$, 1, 0 | ||
| End Function | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LoomGradientVdivides by(h - 1)for color interpolation, which triggers an arithmetic exception whenh = 1. A 1-pixel-tall fill is a valid input for this helper (e.g., thin bars or collapsed panels), so this can crash the editor as soon as such a call is added. Add a fast path forh = 1(draw one line with either endpoint color) before entering the interpolation loop.Useful? React with 👍 / 👎.