diff --git a/.github/assets/SourceHanSansSC-Regular.ttf b/.github/assets/SourceHanSansSC-Regular.ttf new file mode 100644 index 0000000..c8457b4 Binary files /dev/null and b/.github/assets/SourceHanSansSC-Regular.ttf differ diff --git a/.github/assets/arial.ttf b/.github/assets/arial.ttf new file mode 100644 index 0000000..ff0815c Binary files /dev/null and b/.github/assets/arial.ttf differ diff --git a/.github/assets/font.zip b/.github/assets/font.zip deleted file mode 100644 index ab74197..0000000 Binary files a/.github/assets/font.zip and /dev/null differ diff --git a/.github/assets/index.html b/.github/assets/index.html index 739cfdb..9dfd2e4 100644 --- a/.github/assets/index.html +++ b/.github/assets/index.html @@ -57,6 +57,70 @@ opacity: 0.85; margin: 0; } + #overlay-progress { + margin: 18px auto 0; + width: 100%; + max-width: 420px; + text-align: left; + } + #overlay-progress-track { + position: relative; + width: 100%; + height: 8px; + border-radius: 999px; + background: rgba(215, 229, 255, 0.16); + overflow: hidden; + box-shadow: inset 0 1px 3px rgba(6, 12, 28, 0.45); + } + #overlay-progress-fill { + width: 0%; + height: 100%; + border-radius: inherit; + background: linear-gradient(135deg, rgba(126, 188, 255, 0.9), rgba(56, 116, 218, 0.9)); + box-shadow: 0 0 12px rgba(96, 158, 255, 0.4); + transition: width 0.3s ease; + } + #overlay-progress-label { + display: block; + margin-top: 8px; + font-size: 0.75rem; + letter-spacing: 0.12em; + text-transform: uppercase; + text-align: right; + color: rgba(215, 229, 255, 0.72); + } + #font-permission-btn { + display: none; + margin: 18px auto 0; + padding: 12px 26px; + border-radius: 999px; + border: 1px solid rgba(126, 188, 255, 0.55); + background: linear-gradient(135deg, rgba(28, 48, 92, 0.9), rgba(16, 24, 48, 0.9)); + color: #d7e5ff; + font-size: 0.95rem; + letter-spacing: 0.08em; + text-transform: uppercase; + font-weight: 600; + cursor: pointer; + box-shadow: 0 10px 24px rgba(10, 22, 48, 0.4); + transition: transform 0.18s ease, opacity 0.18s ease, border-color 0.18s ease; + } + #font-permission-btn.visible { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 10px; + } + #font-permission-btn:hover:not(:disabled), + #font-permission-btn:focus-visible:not(:disabled) { + transform: translateY(-1px); + border-color: rgba(156, 208, 255, 0.85); + outline: none; + } + #font-permission-btn:disabled { + cursor: wait; + opacity: 0.6; + } #metrics { position: fixed; bottom: 24px; @@ -194,6 +258,13 @@
+' + text + '
').join(''); sourceLinkEl.setAttribute('aria-label', strings.sourceAriaLabel); + setProgress(0); - function setStatus(message) { + function setProgress(value) { + if (!progressEl || !progressFillEl || !progressLabelEl) { + return; + } + const clamped = Math.max(0, Math.min(100, value)); + const nextValue = Math.max(lastProgressValue, clamped); + lastProgressValue = nextValue; + const rounded = Math.round(nextValue); + progressFillEl.style.width = nextValue + '%'; + progressEl.setAttribute('aria-valuenow', String(rounded)); + progressEl.setAttribute('aria-valuetext', rounded + '%'); + progressLabelEl.textContent = rounded + '%'; + } + + function setStatus(message, progressValue) { statusEl.textContent = message; + if (typeof progressValue === 'number') { + setProgress(progressValue); + } } function showError(title, detail) { @@ -343,7 +645,7 @@ }); } - setStatus(strings.checkingWebGPU); + setStatus(strings.checkingWebGPU, 5); hasWebGPU().then(function (supported) { if (!supported) { @@ -351,7 +653,7 @@ return; } - setStatus(strings.loadingResources); + setStatus(strings.loadingResources, 12); window.Module = { arguments: ["zipfile=/data/main.zip:/data/font.zip"], @@ -393,7 +695,7 @@ } Module.addRunDependency(runDependency); - setStatus(strings.loadingMainArchive); + setStatus(strings.loadingMainArchive, 28); fetch('./main.zip') .then(function (response) { if (!response.ok) { @@ -405,6 +707,7 @@ const data = new Uint8Array(buffer); Module.FS.writeFile('/data/main.zip', data, { canOwn: true }); console.log('main.zip loaded:', Module.FS.readdir('/data')); + setProgress(48); }) .catch(function (err) { console.error('Failed to load main.zip', err); @@ -414,30 +717,102 @@ Module.removeRunDependency(runDependency); }); Module.addRunDependency(fontDependency); - setStatus(strings.loadingFontArchive); - fetch('./font.zip') - .then(function (response) { - if (!response.ok) { - throw new Error('HTTP ' + response.status + ' while fetching font.zip'); - } - return response.arrayBuffer(); - }) - .then(function (buffer) { - const data = new Uint8Array(buffer); - Module.FS.writeFile('/data/font.zip', data, { canOwn: true }); - console.log('font.zip loaded:', Module.FS.readdir('/data')); - }) - .catch(function (err) { - console.error('Failed to load font.zip', err); - throw err; - }) - .finally(function () { + + let fontDependencyResolved = false; + function resolveFontDependency() { + if (!fontDependencyResolved) { + fontDependencyResolved = true; + setProgress(90); Module.removeRunDependency(fontDependency); + } + } + + function loadBundledFontZip() { + (async () => { + try { + setStatus(strings.loadingEnglishFont, 68); + const englishEntry = await fetchFontEntry('./arial.ttf', 'arial.ttf'); + setProgress(72); + setStatus(strings.loadingChineseFont, 78); + const chineseEntry = await fetchFontEntry('./SourceHanSansSC-Regular.ttf', 'SourceHanSansSC-Regular.ttf'); + setProgress(82); + writeFontZipToModule([englishEntry, chineseEntry]); + setStatus(strings.loadingFontArchive, 85); + } catch (err) { + console.error('Failed to load fallback font files', err); + throw err; + } finally { + resolveFontDependency(); + } + })().catch(err => { + console.error('Unhandled fallback font error', err); }); + } + + function handleLocalFontAuthorization() { + if (!supportsLocalFontAPI || !fontPermissionBtn) { + loadBundledFontZip(); + return; + } + + fontPermissionBtn.textContent = strings.localFontButton; + fontPermissionBtn.setAttribute('aria-label', strings.localFontButton); + fontPermissionBtn.classList.add('visible'); + fontPermissionBtn.disabled = false; + setStatus(strings.waitingLocalFontAuthorization, 52); + + const requestLocalFonts = async () => { + fontPermissionBtn.disabled = true; + setStatus(strings.requestingLocalFont, 58); + try { + const fontEntries = []; + + let englishEntry = await getLocalFontEntry(ENGLISH_FONT_POSTSCRIPT_NAMES); + if (englishEntry) { + fontEntries.push(englishEntry); + setProgress(68); + } else { + setStatus(strings.loadingEnglishFont, 68); + englishEntry = await fetchFontEntry('./arial.ttf', 'arial.ttf'); + fontEntries.push(englishEntry); + setProgress(72); + } + + let chineseEntry = await getLocalFontEntry(CHINESE_FONT_POSTSCRIPT_NAMES); + if (chineseEntry) { + fontEntries.push(chineseEntry); + setProgress(78); + } else { + setStatus(strings.loadingChineseFont, 78); + chineseEntry = await fetchFontEntry('./SourceHanSansSC-Regular.ttf', 'SourceHanSansSC-Regular.ttf'); + fontEntries.push(chineseEntry); + setProgress(82); + } + + writeFontZipToModule(fontEntries); + setStatus(strings.loadingFontArchive, 85); + fontPermissionBtn.classList.remove('visible'); + resolveFontDependency(); + } catch (err) { + console.warn('Local font access failed, falling back to bundled font files', err); + fontPermissionBtn.classList.remove('visible'); + setStatus(strings.localFontUnavailable, 60); + loadBundledFontZip(); + } + }; + + fontPermissionBtn.addEventListener('click', () => { + requestLocalFonts().catch(err => { + console.error('Unexpected error during local font request', err); + }); + }, { once: true }); + } + + handleLocalFontAuthorization(); }], onRuntimeInitialized: function () { console.log('Soluna runtime ready'); - setStatus(strings.runtimeReady); + setStatus(strings.runtimeReady, 100); setTimeout(hideOverlay, 400); }, onExit: function (status) { diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 85d0594..ac18807 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -33,7 +33,7 @@ jobs: cp "${{ steps.build.outputs.SOLUNA_JS_PATH }}" ./build/ zip -r ./build/main.zip asset core gameplay localization service visual main.game main.lua cp .github/assets/index.html ./build/ - cp .github/assets/font.zip ./build/ + cp .github/assets/*.ttf ./build/ cp .github/assets/coi-serviceworker.min.js ./build/ - name: Upload static files as artifact id: deployment diff --git a/localization/setting.dl b/localization/setting.dl index 16d931f..12a7e8c 100644 --- a/localization/setting.dl +++ b/localization/setting.dl @@ -2,18 +2,32 @@ setting : schinese : name : 简体中文 english_name : "Simplified Chinese" - fontfile : asset/font/WenQuanWeiMiHei-1.ttf + fontfile : + asset/font/MicrosoftYaHei.ttf + asset/font/STHeiti.ttf + asset/font/HeitiSC.ttf + asset/font/SourceHanSansSC-Regular.ttf font : 微软雅黑 + "Microsoft YaHei" "Yuanti SC" + "STHeiti" + "Heiti SC" + "Source Han Sans SC Regular" "WenQuanYi Micro Hei" timefmt : "%Y/%m/%d %H:%M" homepage : "https://blog.codingnow.com/2025/07/deep_future.html" english: name : English - fontfile : asset/font/arial.ttf + fontfile : + asset/font/arial.ttf + asset/font/ArialMT.ttf + asset/font/LiberationSerif.ttf + asset/font/Helvetica.ttf font : Arial - "Noto Sans" + "Arial MT" + "Liberation Serif" + Helvetica timefmt : "%b %d, %Y %H:%M" homepage : "https://boardgamegeek.com/boardgame/194986/deep-future"