diff --git a/assets/fonts/m6x11.ttf b/assets/fonts/m6x11.ttf new file mode 100644 index 0000000..cb21224 Binary files /dev/null and b/assets/fonts/m6x11.ttf differ diff --git a/src/games/balatro/BalatroGame.js b/src/games/balatro/BalatroGame.js index d1dbbb7..b30d00f 100644 --- a/src/games/balatro/BalatroGame.js +++ b/src/games/balatro/BalatroGame.js @@ -239,7 +239,7 @@ export default class BalatroGame extends Phaser.Scene { text(x, y, str, size, color = C.ink, opts = {}) { const t = this.add.text(x, y, str, { - fontFamily: opts.font || '"Julius Sans One"', fontSize: `${size}px`, color, + fontFamily: opts.font || 'm6x11, "Julius Sans One"', fontSize: `${size}px`, color, align: opts.align || 'left', wordWrap: opts.wrap ? { width: opts.wrap } : undefined, fontStyle: opts.bold ? 'bold' : 'normal', }).setOrigin(opts.ox ?? 0, opts.oy ?? 0); @@ -266,7 +266,7 @@ export default class BalatroGame extends Phaser.Scene { toast(msg) { if (this._toastText) this._toastText.destroy(); const t = this.add.text(GAME_WIDTH / 2, 640, msg, { - fontFamily: '"Julius Sans One"', fontSize: '30px', color: '#ffd2c8', + fontFamily: 'm6x11, "Julius Sans One"', fontSize: '30px', color: '#ffd2c8', backgroundColor: '#301418', padding: { x: 18, y: 10 }, }).setOrigin(0.5).setDepth(90); this.fxLayer.add(t); @@ -326,22 +326,22 @@ export default class BalatroGame extends Phaser.Scene { if (enh !== 'stone') { const color = SUIT_COLOR[card.suit]; const rank = this.add.text(-w / 2 + 10, -h / 2 + 6, RANK_NAMES[card.rank], { - fontFamily: 'Georgia, serif', fontSize: `${Math.round(h * 0.21)}px`, color, fontStyle: 'bold', + fontFamily: 'm6x11, Georgia, serif', fontSize: `${Math.round(h * 0.21)}px`, color, fontStyle: 'bold', }); const suitSm = this.add.text(-w / 2 + 12, -h / 2 + 8 + h * 0.21, SUIT_GLYPH[card.suit], { - fontFamily: 'Georgia, serif', fontSize: `${Math.round(h * 0.16)}px`, color, + fontFamily: 'm6x11, Georgia, serif', fontSize: `${Math.round(h * 0.16)}px`, color, }); const suitBig = this.add.text(w * 0.16, h * 0.16, SUIT_GLYPH[card.suit], { - fontFamily: 'Georgia, serif', fontSize: `${Math.round(h * 0.42)}px`, color, + fontFamily: 'm6x11, Georgia, serif', fontSize: `${Math.round(h * 0.42)}px`, color, }).setOrigin(0.5).setAlpha(0.9); cont.add([rank, suitSm, suitBig]); if (enh === 'wild') { - const wl = this.add.text(0, h * 0.38, 'WILD', { fontFamily: '"Julius Sans One"', fontSize: `${Math.round(h * 0.1)}px`, color: '#5a3a10', fontStyle: 'bold' }).setOrigin(0.5); + const wl = this.add.text(0, h * 0.38, 'WILD', { fontFamily: 'm6x11, "Julius Sans One"', fontSize: `${Math.round(h * 0.1)}px`, color: '#5a3a10', fontStyle: 'bold' }).setOrigin(0.5); cont.add(wl); } } else { - const st = this.add.text(0, 0, '◼', { fontFamily: 'Georgia, serif', fontSize: `${Math.round(h * 0.32)}px`, color: '#4c4a45' }).setOrigin(0.5); - const lbl = this.add.text(0, h * 0.3, 'STONE', { fontFamily: '"Julius Sans One"', fontSize: `${Math.round(h * 0.1)}px`, color: '#3d3b37' }).setOrigin(0.5); + const st = this.add.text(0, 0, '◼', { fontFamily: 'm6x11, Georgia, serif', fontSize: `${Math.round(h * 0.32)}px`, color: '#4c4a45' }).setOrigin(0.5); + const lbl = this.add.text(0, h * 0.3, 'STONE', { fontFamily: 'm6x11, "Julius Sans One"', fontSize: `${Math.round(h * 0.1)}px`, color: '#3d3b37' }).setOrigin(0.5); cont.add([st, lbl]); } if (card.seal) { @@ -351,19 +351,19 @@ export default class BalatroGame extends Phaser.Scene { } if (card.edition) { const tag = this.add.text(0, -h / 2 + 4, EDITIONS[card.edition].name.toUpperCase(), { - fontFamily: '"Julius Sans One"', fontSize: `${Math.round(h * 0.07)}px`, color: '#ffffff', + fontFamily: 'm6x11, "Julius Sans One"', fontSize: `${Math.round(h * 0.07)}px`, color: '#ffffff', }).setOrigin(0.5, 0).setAlpha(0.85); cont.add(tag); } if (card.permaChips) { const pc = this.add.text(-w / 2 + 8, h / 2 - 26, `+${card.permaChips}`, { - fontFamily: '"Julius Sans One"', fontSize: `${Math.round(h * 0.09)}px`, color: C.chipsHex, + fontFamily: 'm6x11, "Julius Sans One"', fontSize: `${Math.round(h * 0.09)}px`, color: C.chipsHex, }); cont.add(pc); } if (debuffed) { const shade = this.add.rectangle(0, 0, w, h, 0x1a1420, 0.62); - const xg = this.add.text(0, 0, '✕', { fontFamily: 'Georgia, serif', fontSize: `${Math.round(h * 0.3)}px`, color: '#8a4550' }).setOrigin(0.5); + const xg = this.add.text(0, 0, '✕', { fontFamily: 'm6x11, Georgia, serif', fontSize: `${Math.round(h * 0.3)}px`, color: '#8a4550' }).setOrigin(0.5); cont.add([shade, xg]); } cont.setSize(w, h); @@ -387,11 +387,11 @@ export default class BalatroGame extends Phaser.Scene { g.lineStyle(2, C.rarity[def.rarity], 1); g.strokeRoundedRect(-w / 2, -h / 2, w, h, 10); cont.add(g); const name = this.add.text(0, -h / 2 + 15, def.name, { - fontFamily: '"Julius Sans One"', fontSize: `${Math.min(17, Math.round(280 / def.name.length))}px`, color: '#141019', fontStyle: 'bold', + fontFamily: 'm6x11, "Julius Sans One"', fontSize: `${Math.min(17, Math.round(280 / def.name.length))}px`, color: '#141019', fontStyle: 'bold', }).setOrigin(0.5); const desc = typeof def.desc === 'function' ? def.desc(opts.inst || { state: def.initState ? def.initState() : {} }) : def.desc; const body = this.add.text(0, 8, desc, { - fontFamily: '"Julius Sans One"', fontSize: '15px', color: C.ink, align: 'center', + fontFamily: 'm6x11, "Julius Sans One"', fontSize: '15px', color: C.ink, align: 'center', wordWrap: { width: w - 16 }, }).setOrigin(0.5); cont.add([name, body]); @@ -403,14 +403,14 @@ export default class BalatroGame extends Phaser.Scene { eg.lineStyle(4, edColor, 0.95); eg.strokeRoundedRect(-w / 2, -h / 2, w, h, 10); cont.add(eg); const tag = this.add.text(0, h / 2 - 16, EDITIONS[edition].name.toUpperCase(), { - fontFamily: '"Julius Sans One"', fontSize: '11px', color: '#ffffff', + fontFamily: 'm6x11, "Julius Sans One"', fontSize: '11px', color: '#ffffff', }).setOrigin(0.5).setAlpha(0.9); cont.add(tag); if (edition === 'negative') cont.setAlpha(0.8); } if (opts.inst && opts.inst.debuffedByBoss) { cont.add(this.add.rectangle(0, 0, w, h, 0x1a1420, 0.6)); - cont.add(this.add.text(0, 0, 'DISABLED', { fontFamily: '"Julius Sans One"', fontSize: '16px', color: '#c86a6a' }).setOrigin(0.5)); + cont.add(this.add.text(0, 0, 'DISABLED', { fontFamily: 'm6x11, "Julius Sans One"', fontSize: '16px', color: '#c86a6a' }).setOrigin(0.5)); } cont.setSize(w, h); return cont; @@ -433,16 +433,16 @@ export default class BalatroGame extends Phaser.Scene { g.lineStyle(1, tint, 0.6); g.strokeRoundedRect(-w / 2 + 6, -h / 2 + 6, w - 12, h - 12, 8); cont.add(g); const name = this.add.text(0, -h / 2 + 20, def.name, { - fontFamily: '"Julius Sans One"', fontSize: `${Math.min(16, Math.round(300 / def.name.length))}px`, + fontFamily: 'm6x11, "Julius Sans One"', fontSize: `${Math.min(16, Math.round(300 / def.name.length))}px`, color: `#${tint.toString(16).padStart(6, '0')}`, fontStyle: 'bold', align: 'center', wordWrap: { width: w - 14 }, }).setOrigin(0.5, 0); const descText = kind === 'planet' ? `+1 level: ${HAND_BY_ID[def.hand].name}` : def.desc; const body = this.add.text(0, 16, descText, { - fontFamily: '"Julius Sans One"', fontSize: '14px', color: C.ink, align: 'center', wordWrap: { width: w - 18 }, + fontFamily: 'm6x11, "Julius Sans One"', fontSize: '14px', color: C.ink, align: 'center', wordWrap: { width: w - 18 }, }).setOrigin(0.5); cont.add([name, body]); const glyph = { tarot: '☽', planet: '✶', spectral: '✧' }[kind]; - cont.add(this.add.text(0, h / 2 - 22, glyph, { fontFamily: 'Georgia, serif', fontSize: '20px', color: `#${tint.toString(16).padStart(6, '0')}` }).setOrigin(0.5)); + cont.add(this.add.text(0, h / 2 - 22, glyph, { fontFamily: 'm6x11, Georgia, serif', fontSize: '20px', color: `#${tint.toString(16).padStart(6, '0')}` }).setOrigin(0.5)); } cont.setSize(w, h); return cont; @@ -460,14 +460,14 @@ export default class BalatroGame extends Phaser.Scene { this.fxLayer.add(cont); let y = PAD; const title = this.add.text(PAD, y, info.title, { - fontFamily: '"Julius Sans One"', fontSize: '22px', color: info.titleColor || C.ink, + fontFamily: 'm6x11, "Julius Sans One"', fontSize: '22px', color: info.titleColor || C.ink, fontStyle: 'bold', wordWrap: { width: W - PAD * 2 }, }); cont.add(title); y += title.height + 4; if (info.tag) { const tag = this.add.text(PAD, y, info.tag, { - fontFamily: '"Julius Sans One"', fontSize: '14px', color: info.tagColor || C.muted, + fontFamily: 'm6x11, "Julius Sans One"', fontSize: '14px', color: info.tagColor || C.muted, }); cont.add(tag); y += tag.height + 8; @@ -476,7 +476,7 @@ export default class BalatroGame extends Phaser.Scene { } for (const line of info.lines || []) { const t = this.add.text(PAD, y, line.text, { - fontFamily: '"Julius Sans One"', fontSize: '16px', color: line.color || C.muted, + fontFamily: 'm6x11, "Julius Sans One"', fontSize: '16px', color: line.color || C.muted, wordWrap: { width: W - PAD * 2 }, }); cont.add(t); @@ -701,7 +701,7 @@ export default class BalatroGame extends Phaser.Scene { g.fillStyle(C.panel, 0.97); g.fillRoundedRect(GAME_WIDTH / 2 - 240, 360, 480, 360, 16); g.lineStyle(2, C.panelEdge, 1); g.strokeRoundedRect(GAME_WIDTH / 2 - 240, 360, 480, 360, 16); ov.add([shade, g]); - const t = this.add.text(GAME_WIDTH / 2, 400, 'Options', { fontFamily: '"Julius Sans One"', fontSize: '32px', color: C.ink }).setOrigin(0.5); + const t = this.add.text(GAME_WIDTH / 2, 400, 'Options', { fontFamily: 'm6x11, "Julius Sans One"', fontSize: '32px', color: C.ink }).setOrigin(0.5); ov.add(t); const mk = (y, label, cb) => { const b = new Button(this, GAME_WIDTH / 2, y, label, cb, { width: 320, height: 56, fontSize: 22 }); @@ -775,8 +775,8 @@ export default class BalatroGame extends Phaser.Scene { g.lineStyle(2, C.rarity[def.rarity], 1); g.strokeRoundedRect(px, py, pw, ph, 12); cont.add(g); const desc = typeof def.desc === 'function' ? def.desc(inst) : def.desc; - cont.add(this.add.text(px + 20, py + 14, `${def.name}${inst.edition ? ` (${EDITIONS[inst.edition].name})` : ''}`, { fontFamily: '"Julius Sans One"', fontSize: '24px', color: C.ink })); - cont.add(this.add.text(px + 20, py + 52, desc, { fontFamily: '"Julius Sans One"', fontSize: '17px', color: C.muted, wordWrap: { width: pw - 40 } })); + cont.add(this.add.text(px + 20, py + 14, `${def.name}${inst.edition ? ` (${EDITIONS[inst.edition].name})` : ''}`, { fontFamily: 'm6x11, "Julius Sans One"', fontSize: '24px', color: C.ink })); + cont.add(this.add.text(px + 20, py + 52, desc, { fontFamily: 'm6x11, "Julius Sans One"', fontSize: '17px', color: C.muted, wordWrap: { width: pw - 40 } })); const sell = new Button(this, px + pw - 110, py + ph - 40, `Sell $${inst.sellValue}`, () => { const r = sellJoker(this.run, inst.uid); if (r.ok) { playSound(this, SFX.COINS); this.save(); this.renderView(); } @@ -823,11 +823,11 @@ export default class BalatroGame extends Phaser.Scene { const tint = { tarot: C.tarot, planet: C.planet, spectral: C.spectral }[inst.kind]; g.lineStyle(2, tint, 1); g.strokeRoundedRect(px, py, pw, ph, 12); cont.add(g); - cont.add(this.add.text(px + 20, py + 14, def.name, { fontFamily: '"Julius Sans One"', fontSize: '24px', color: C.ink })); + cont.add(this.add.text(px + 20, py + 14, def.name, { fontFamily: 'm6x11, "Julius Sans One"', fontSize: '24px', color: C.ink })); const descText = inst.kind === 'planet' ? `+1 level: ${HAND_BY_ID[def.hand].name}` : def.desc; - cont.add(this.add.text(px + 20, py + 52, descText, { fontFamily: '"Julius Sans One"', fontSize: '17px', color: C.muted, wordWrap: { width: pw - 40 } })); + cont.add(this.add.text(px + 20, py + 52, descText, { fontFamily: 'm6x11, "Julius Sans One"', fontSize: '17px', color: C.muted, wordWrap: { width: pw - 40 } })); const needs = def.targets || 0; - if (needs) cont.add(this.add.text(px + 20, py + ph - 96, `Select up to ${needs} card${needs > 1 ? 's' : ''} in hand first`, { fontFamily: '"Julius Sans One"', fontSize: '15px', color: '#a8d8ff' })); + if (needs) cont.add(this.add.text(px + 20, py + ph - 96, `Select up to ${needs} card${needs > 1 ? 's' : ''} in hand first`, { fontFamily: 'm6x11, "Julius Sans One"', fontSize: '15px', color: '#a8d8ff' })); const use = new Button(this, px + pw - 110, py + ph - 40, 'Use', () => { const r = useConsumable(this.run, inst.uid, this.selected.slice(0, Math.max(needs, 2))); if (!r.ok) { this.toast(r.error); return; } @@ -897,7 +897,7 @@ export default class BalatroGame extends Phaser.Scene { cont.add(glow); } if (run.bossState && run.bossState.forcedUid === card.uid) { - const lock = this.add.text(0, -h / 2 - 20, '⛓ forced', { fontFamily: '"Julius Sans One"', fontSize: '15px', color: '#ff8a6a' }).setOrigin(0.5); + const lock = this.add.text(0, -h / 2 - 20, '⛓ forced', { fontFamily: 'm6x11, "Julius Sans One"', fontSize: '15px', color: '#ff8a6a' }).setOrigin(0.5); cont.add(lock); } if (!frozen) { @@ -1140,7 +1140,7 @@ export default class BalatroGame extends Phaser.Scene { const x = sprite ? this.fxX(sprite) : 980; const y = sprite ? this.fxY(sprite) - 90 : 400; const t = this.add.text(x, y, textStr, { - fontFamily: '"Julius Sans One"', fontSize: '34px', color, fontStyle: 'bold', + fontFamily: 'm6x11, "Julius Sans One"', fontSize: '34px', color, fontStyle: 'bold', stroke: '#141019', strokeThickness: 5, }).setOrigin(0.5).setDepth(85); this.fxLayer.add(t); @@ -1189,7 +1189,7 @@ export default class BalatroGame extends Phaser.Scene { const score = res.trace.score; // Slam: big score text at the played row. const slam = this.add.text(centerX, py - 170, fmtChips(score), { - fontFamily: '"Julius Sans One"', fontSize: '72px', color: '#ffffff', fontStyle: 'bold', + fontFamily: 'm6x11, "Julius Sans One"', fontSize: '72px', color: '#ffffff', fontStyle: 'bold', stroke: '#c8342c', strokeThickness: 8, }).setOrigin(0.5).setScale(1.6).setAlpha(0).setDepth(86); this.fxLayer.add(slam); diff --git a/src/games/balatro/BalatroViews.js b/src/games/balatro/BalatroViews.js index 2c8ccdf..19067ac 100644 --- a/src/games/balatro/BalatroViews.js +++ b/src/games/balatro/BalatroViews.js @@ -64,7 +64,7 @@ function confirmNewRun(scene) { g.fillStyle(C.panel, 0.97); g.fillRoundedRect(CX - 280, 420, 560, 240, 16); g.lineStyle(2, C.panelEdge, 1); g.strokeRoundedRect(CX - 280, 420, 560, 240, 16); ov.add([shade, g]); - ov.add(scene.add.text(CX, 470, 'Abandon the saved run?', { fontFamily: '"Julius Sans One"', fontSize: '28px', color: C.ink }).setOrigin(0.5)); + ov.add(scene.add.text(CX, 470, 'Abandon the saved run?', { fontFamily: 'm6x11, "Julius Sans One"', fontSize: '28px', color: C.ink }).setOrigin(0.5)); const yes = scene.button(CX - 110, 590, 'Yes', () => { scene._savedRun = null; store.write('balatro:run', null); ov.destroy(); scene.setView('deckselect'); }, { width: 160, height: 54, fontSize: 22 }); const no = scene.button(CX + 110, 590, 'No', () => ov.destroy(), { width: 160, height: 54, fontSize: 22, variant: 'ghost' }); ov.add([yes, no]); @@ -94,8 +94,8 @@ export function renderDeckSelect(scene) { bg.lineStyle(1, 0xffffff, 0.25); bg.strokeRoundedRect(-48, -130, 96, 140, 6); cont.add(bg); } - cont.add(scene.add.text(0, 52, deck.name, { fontFamily: '"Julius Sans One"', fontSize: '26px', color: C.ink, fontStyle: 'bold' }).setOrigin(0.5)); - cont.add(scene.add.text(0, 108, deck.desc, { fontFamily: '"Julius Sans One"', fontSize: '17px', color: C.muted, align: 'center', wordWrap: { width: w - 30 } }).setOrigin(0.5)); + cont.add(scene.add.text(0, 52, deck.name, { fontFamily: 'm6x11, "Julius Sans One"', fontSize: '26px', color: C.ink, fontStyle: 'bold' }).setOrigin(0.5)); + cont.add(scene.add.text(0, 108, deck.desc, { fontFamily: 'm6x11, "Julius Sans One"', fontSize: '17px', color: C.muted, align: 'center', wordWrap: { width: w - 30 } }).setOrigin(0.5)); cont.setSize(w, h); scene.add2(cont); scene.addHoverTilt(cont); @@ -248,7 +248,7 @@ export function renderShop(scene) { g.lineStyle(3, 0xffffff, 0.5); g.strokeRoundedRect(-90, -120, 180, 240, 12); g.fillStyle(0x141019, 0.35); g.fillRoundedRect(-90, -30, 180, 60, 0); cont.add(g); - cont.add(scene.add.text(0, 0, def.name.replace(' Pack', '\nPack'), { fontFamily: '"Julius Sans One"', fontSize: '22px', color: '#ffffff', align: 'center', fontStyle: 'bold' }).setOrigin(0.5)); + cont.add(scene.add.text(0, 0, def.name.replace(' Pack', '\nPack'), { fontFamily: 'm6x11, "Julius Sans One"', fontSize: '22px', color: '#ffffff', align: 'center', fontStyle: 'bold' }).setOrigin(0.5)); } cont.setSize(180, 240); scene.add2(cont); @@ -284,8 +284,8 @@ export function renderShop(scene) { g.lineStyle(3, C.voucher, 1); g.strokeRoundedRect(-95, -125, 190, 250, 10); g.lineStyle(1, C.voucher, 0.5); g.strokeRoundedRect(-85, -115, 170, 230, 8); cont.add(g); - cont.add(scene.add.text(0, -80, v.name, { fontFamily: '"Julius Sans One"', fontSize: '22px', color: '#7ce0c0', align: 'center', fontStyle: 'bold', wordWrap: { width: 170 } }).setOrigin(0.5)); - cont.add(scene.add.text(0, 20, v.desc, { fontFamily: '"Julius Sans One"', fontSize: '16px', color: C.ink, align: 'center', wordWrap: { width: 165 } }).setOrigin(0.5)); + cont.add(scene.add.text(0, -80, v.name, { fontFamily: 'm6x11, "Julius Sans One"', fontSize: '22px', color: '#7ce0c0', align: 'center', fontStyle: 'bold', wordWrap: { width: 170 } }).setOrigin(0.5)); + cont.add(scene.add.text(0, 20, v.desc, { fontFamily: 'm6x11, "Julius Sans One"', fontSize: '16px', color: C.ink, align: 'center', wordWrap: { width: 165 } }).setOrigin(0.5)); } cont.setSize(190, 250); scene.add2(cont); diff --git a/src/scenes/PreloadScene.js b/src/scenes/PreloadScene.js index 4a91139..99558c7 100644 --- a/src/scenes/PreloadScene.js +++ b/src/scenes/PreloadScene.js @@ -261,9 +261,10 @@ export default class PreloadScene extends Phaser.Scene { }); } - // Warm the handwritten Scrabble notepad font so its first paint isn't a - // fallback face. Best effort — never block startup on it. - try { await Promise.race([document.fonts.load('48px YummyCupcakes'), new Promise(r => setTimeout(r, 1500))]); } catch { /* ignore */ } + // Warm the handwritten Scrabble notepad font and Balatro's pixel font so + // their first paint isn't a fallback face. Best effort — never block + // startup on them. + try { await Promise.race([Promise.all([document.fonts.load('48px YummyCupcakes'), document.fonts.load('48px m6x11')]), new Promise(r => setTimeout(r, 1500))]); } catch { /* ignore */ } await auth.refresh(); this.scene.start('Landing'); diff --git a/styles.css b/styles.css index 98c85dd..e4d5b37 100644 --- a/styles.css +++ b/styles.css @@ -26,6 +26,13 @@ font-style: normal; } +@font-face { + font-family: 'm6x11'; + src: url('assets/fonts/m6x11.ttf') format('truetype'); + font-weight: normal; + font-style: normal; +} + html, body { margin: 0; padding: 0;