From 074e7326df223af03b0158afe67750786f958438 Mon Sep 17 00:00:00 2001 From: Queue runner Date: Thu, 22 Oct 2026 19:01:02 +1300 Subject: [PATCH] Board: wool letters say 'fine fleece' until numbers unlock --- packages/game/src/ui/board.ts | 10 +++++----- packages/game/src/ui/report.ts | 4 ++-- packages/game/src/ui/util.ts | 5 +++++ 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/packages/game/src/ui/board.ts b/packages/game/src/ui/board.ts index 97de4dd..b8c8bd2 100644 --- a/packages/game/src/ui/board.ts +++ b/packages/game/src/ui/board.ts @@ -6,7 +6,7 @@ import { import { myAnimalsHtml } from "./pets.js"; import { litterWords } from "./forecast.js"; import { crossCached } from "./cache.js"; -import { dueWords, esc, has, hex, pips, prop, stars, swatch } from "./util.js"; +import { dueWords, esc, has, hex, letterText, pips, prop, stars, swatch } from "./util.js"; import { btn, head, icon, iconBtn, iconize, more, nm, tag, type IconName } from "./felt/index.js"; import type { View } from "./view.js"; import { actTrack } from "./track.js"; @@ -59,14 +59,14 @@ export function boardHtml(state: GameState, _view: View): string { const orders = accepted.map((o) => { let f = { pFill: 0, text: "" }; try { f = forecastOrderFor(state, o); } catch { /* keep blank */ } - return `
  • ${orderIcon(o)}${esc(o.text)}
    ${icon("calendar", "inl")} ${esc(dueWords(state, o.deadline))}
    ${pips(state, f.pFill, f.text, true)}
  • `; + return `
  • ${orderIcon(o)}${esc(letterText(state, o.text))}
    ${icon("calendar", "inl")} ${esc(dueWords(state, o.deadline))}
    ${pips(state, f.pFill, f.text, true)}
  • `; }).join(""); const news = upcoming(state); let lastSeason = -Infinity; const diary = state.log.slice(-10).reverse().map((l) => { const head = l.season !== lastSeason ? `
  • ${esc(seasonLabel(l.season))}
  • ` : ""; lastSeason = l.season; - return `${head}
  • ${iconize(esc(l.text))}
  • `; + return `${head}
  • ${iconize(esc(letterText(state, l.text)))}
  • `; }).join(""); const animals = myAnimalsHtml(state); return `${head("board", "The shed board", 2)} @@ -98,7 +98,7 @@ function orderCard(state: GameState, o: Order, mode: "open" | "accepted"): strin return `
    ${orderIcon(o)}
    -
    “${esc(o.text)}”
    +
    “${esc(letterText(state, o.text))}”
    ${tag(`${o.reward}`, { icon: "coin", tone: "butter", title: "Reward in coins" })}${o.reputation ? tag("★".repeat(o.reputation).replace(/★/g, icon("star", "sm")), { tone: "butter", title: "Reputation" }) : ""}${tag(esc(dueWords(state, o.deadline).replace(/^due by /, "")), { icon: "calendar", tone: "sky", title: "Due by" })}
    Can you fill it?${pips(state, f.pFill, `Chance you can fill it: ${f.text}`, true)}
    @@ -116,5 +116,5 @@ export function ordersHtml(state: GameState, _view: View): string { return `${head("mail", "Letters", 2)} ${open.length ? open.map((o) => orderCard(state, o, "open")).join("") : `

    No new letters. More arrive with the seasons.

    `} ${accepted.length ? `${head("check", `Promised · ${accepted.length} of ${MAX_ACCEPTED_ORDERS}`)}${accepted.map((o) => orderCard(state, o, "accepted")).join("")}` : ""} - ${past.length ? more("orders-past", "Past letters", ``) : ""}`; + ${past.length ? more("orders-past", "Past letters", ``) : ""}`; } diff --git a/packages/game/src/ui/report.ts b/packages/game/src/ui/report.ts index c9d914c..a520620 100644 --- a/packages/game/src/ui/report.ts +++ b/packages/game/src/ui/report.ts @@ -4,7 +4,7 @@ import { } from "../core/index.js"; import { lambTile, litterLooks } from "./forecast.js"; import { flipLine } from "./craft.js"; -import { cap, discoveryText, discoveryTitle, esc, portrait, UNLOCK_ICON, UNLOCK_WORDS } from "./util.js"; +import { cap, discoveryText, discoveryTitle, esc, letterText, portrait, UNLOCK_ICON, UNLOCK_WORDS } from "./util.js"; import { btn, head, icon, iconize, nm, type IconName } from "./felt/index.js"; import type { View } from "./view.js"; @@ -76,7 +76,7 @@ function matingRows(state: GameState, view: View, r: SeasonReport): string { function latestSummary(state: GameState): string { const last = state.log.filter((l) => l.season === state.season).slice(-8); return `${head("calendar", esc(seasonLabel(state.season)), 2)} - ${last.length ? `` : `

    Nothing yet this season. Plan some matings, then press Next season.

    `} + ${last.length ? `` : `

    Nothing yet this season. Plan some matings, then press Next season.

    `}
    ${btn("Carry on", { kind: "primary", icon: "check", data: { close: "" } })}
    `; } diff --git a/packages/game/src/ui/util.ts b/packages/game/src/ui/util.ts index 1146253..abed871 100644 --- a/packages/game/src/ui/util.ts +++ b/packages/game/src/ui/util.ts @@ -27,6 +27,11 @@ export function woolSwatch(s: Sheep, extra = ""): string { return swatch(woolOf(s).hex, extra); } +/** Wool-letter text with its fineness ask in plain words until the numbers unlock. */ +export function letterText(state: GameState, text: string): string { + return numbersOn(state) ? text : text.replace(/fleece finer than [\d.]+ µm/g, "fine fleece"); +} + export function numbersOn(state: GameState): boolean { return state.unlocks.includes("numbers"); }