diff --git a/compile.bat b/compile.bat index f6e2376a..1ff4998f 100644 --- a/compile.bat +++ b/compile.bat @@ -76,6 +76,7 @@ if %RCCE%==1 ( "%BLITZPATH%\bin\blitzcc.exe" -o "%ROOTDIR%\bin\Server.exe" "%ROOTDIR%\src\Server.bb" || (cd /d "%ROOTDIR%" & endlocal & exit /b 1) "%BLITZPATH%\bin\blitzcc.exe" -o "%ROOTDIR%\Project Manager.exe" -n "%ROOTDIR%\res\Icon.ico" "%ROOTDIR%\src\Project Manager.bb" || (cd /d "%ROOTDIR%" & endlocal & exit /b 1) "%BLITZPATH%\bin\blitzcc.exe" -o "%ROOTDIR%\bin\GUE.exe" -n "%ROOTDIR%\res\Icon.ico" "%ROOTDIR%\src\GUE.bb" || (cd /d "%ROOTDIR%" & endlocal & exit /b 1) + "%BLITZPATH%\bin\blitzcc.exe" -o "%ROOTDIR%\bin\Loom.exe" -n "%ROOTDIR%\res\Icon.ico" "%ROOTDIR%\src\Loom.bb" || (cd /d "%ROOTDIR%" & endlocal & exit /b 1) "%BLITZPATH%\bin\blitzcc.exe" -o "%ROOTDIR%\bin\Client.exe" -n "%ROOTDIR%\res\Icon.ico" "%ROOTDIR%\src\Client.bb" || (cd /d "%ROOTDIR%" & endlocal & exit /b 1) ) diff --git a/compile.sh b/compile.sh index 1421c404..fb55988a 100755 --- a/compile.sh +++ b/compile.sh @@ -101,6 +101,8 @@ if [[ "${RCCE}" -eq 1 ]]; then "${ROOTDIR}/src/Project Manager.bb" "${BLITZCC}" -o "${ROOTDIR}/bin/GUE${EXE_SUFFIX}" "${ICON_FLAG[@]}" \ "${ROOTDIR}/src/GUE.bb" + "${BLITZCC}" -o "${ROOTDIR}/bin/Loom${EXE_SUFFIX}" "${ICON_FLAG[@]}" \ + "${ROOTDIR}/src/Loom.bb" "${BLITZCC}" -o "${ROOTDIR}/bin/Client${EXE_SUFFIX}" "${ICON_FLAG[@]}" \ "${ROOTDIR}/src/Client.bb" fi diff --git a/src/Loom.bb b/src/Loom.bb new file mode 100644 index 00000000..fb006e32 --- /dev/null +++ b/src/Loom.bb @@ -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 +// /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 /Data/ and then ChangeDir "..\" walked us up to +// /. 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 diff --git a/src/Modules/Loom/Theme.bb b/src/Modules/Loom/Theme.bb new file mode 100644 index 00000000..eafed6a9 --- /dev/null +++ b/src/Modules/Loom/Theme.bb @@ -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 diff --git a/src/Project Manager.bb b/src/Project Manager.bb index 11e4e5d2..842153bc 100644 --- a/src/Project Manager.bb +++ b/src/Project Manager.bb @@ -271,6 +271,7 @@ local PMH$ = RootDir$ + "res\Help.txt" local SWH$ = RootDir$ + "bin\tools\RC Spell Wizard\RC Spell Wizard Documentation.pdf" local GUE$ = RootDir$ + "bin\GUE.exe" +local LOOM$ = RootDir$ + "bin\Loom.exe" local CLI$ = RootDir$ + "bin\Client.exe" local SER$ = RootDir$ + "bin\Server.exe" @@ -370,8 +371,12 @@ local BOSC = FUI_Button(TProject, 170 + 75.5 + 75.5, 200, 70.5, 25, "Scripts") FUI_ImageBox(TEngine, 160, 27, 380, 112, Ptr LogoTex) ;Editors -local LED = FUI_GroupBox(TEngine, 5, 20, 150, 80, "Editors") -local BGUE = FUI_Button(TEngine, 15, 40, 130, 50, "Game Unified Editor") +local LED = FUI_GroupBox(TEngine, 5, 20, 150, 115, "Editors") +local BGUE = FUI_Button(TEngine, 15, 40, 130, 50, "Game Unified Editor") +;Loom alpha button -- launches the in-progress Loom redesign of GUE. Same +;data layer, different UI shell. Labelled "Alpha" so users know it's not +;production-ready. +local BLOOM = FUI_Button(TEngine, 15, 95, 130, 30, "Loom (Alpha)") local LTK = FUI_GroupBox(TEngine, 160, 140, 240, 100, "Tool Kit") local TOOL1 = FUI_Button(TEngine, 170, 165, 70.5, 25, "Gubbin") @@ -389,6 +394,12 @@ if FileType(B3D$) = 0 FUI_DisableGadget(BB3D) EndIf +;Disable the Loom alpha button if Loom.exe has not been built yet (e.g. fresh +;clone where the user hasn't run compile.bat with the new target). +if FileType(LOOM$) = 0 + FUI_DisableGadget(BLOOM) +EndIf + ;Support Tab ;Logo FUI_ImageBox(TSupport, 440, 173, 100, 67, Ptr LogoTex2) @@ -508,6 +519,8 @@ Repeat ;Project Tab Case BGUE ExecFile(GUE$, "", GameDir$ + "Data\") + Case BLOOM + ExecFile(LOOM$, "", GameDir$ + "Data\") Case BCLI ExecFile(CLI$, "", GameDir$ + "Data\") Case BSER