54bc31c619
availableVariants fell back to all three variants when the player had no stored preferences. A fresh offline native launch boots with no profile, so New Game exposed the English game before the player opted in — contrary to the backend's Erudit-only new-account default (docs/FUNCTIONAL.md). Fall back to DEFAULT_VARIANTS (Erudit only) instead; all dictionaries stay bundled.
97 lines
4.8 KiB
TypeScript
97 lines
4.8 KiB
TypeScript
// Game variants offered on New Game, and the gating of that choice by the
|
|
// languages the sign-in service supports. Kept out of the .svelte screen so the
|
|
// gating is unit-testable (the project's node-env Vitest layer).
|
|
|
|
import type { MessageKey } from './i18n/index.svelte';
|
|
import type { Variant } from './model';
|
|
|
|
// VariantOption is a selectable game variant with its i18n label key.
|
|
export interface VariantOption {
|
|
id: Variant;
|
|
label: MessageKey;
|
|
}
|
|
|
|
// ALL_VARIANTS lists every variant in display order (Erudit first — the default
|
|
// preference and the product's primary variant). The labels are display names keyed by
|
|
// the game's alphabet, not the interface language: the English-alphabet game is always
|
|
// "Scrabble" and the Russian-alphabet Scrabble always "Скрэббл" (both unlocalized, so the
|
|
// two never collide whatever the UI language); Erudit is localized "Erudite"/"Эрудит".
|
|
export const ALL_VARIANTS: VariantOption[] = [
|
|
{ id: 'erudit_ru', label: 'new.erudit' },
|
|
{ id: 'scrabble_ru', label: 'new.russian' },
|
|
{ id: 'scrabble_en', label: 'new.english' },
|
|
];
|
|
|
|
// variantNameKey returns the i18n key for a variant's display name (used by the in-game
|
|
// title and the lobby cards).
|
|
export function variantNameKey(v: Variant): MessageKey {
|
|
return ALL_VARIANTS.find((o) => o.id === v)?.label ?? 'new.english';
|
|
}
|
|
|
|
// VARIANT_RULES is the i18n key for each variant's one-line rules summary on the New Game
|
|
// buttons (bag size, the ё rule, bonus differences), sourced from the engine rulesets.
|
|
export const VARIANT_RULES: Record<Variant, MessageKey> = {
|
|
scrabble_en: 'new.rulesEnglish',
|
|
scrabble_ru: 'new.rulesRussian',
|
|
erudit_ru: 'new.rulesErudit',
|
|
};
|
|
|
|
// VARIANT_FLAG is the flag shown on a variant button: an emoji for the Scrabble variants;
|
|
// Erudit uses the bundled USSR flag SVG (public/flag-ussr.svg), so its entry is empty.
|
|
export const VARIANT_FLAG: Record<Variant, string> = {
|
|
scrabble_en: '🇺🇸',
|
|
scrabble_ru: '🇷🇺',
|
|
erudit_ru: '',
|
|
};
|
|
|
|
// VARIANT_LANGUAGE maps each variant to its game language. en -> English;
|
|
// ru -> Russian + Эрудит.
|
|
export const VARIANT_LANGUAGE: Record<Variant, 'en' | 'ru'> = { scrabble_en: 'en', scrabble_ru: 'ru', erudit_ru: 'ru' };
|
|
|
|
// BLANK_STAR is the glyph drawn on an Эрудит blank tile: the variant's blank is the
|
|
// "звёздочка" (star) chip, so it carries a star rather than a bare face. U+273B HEAVY
|
|
// TEARDROP-SPOKED ASTERISK.
|
|
export const BLANK_STAR = '✻';
|
|
|
|
// usesStarBlank reports whether a variant marks its blank tiles with BLANK_STAR. Only
|
|
// Эрудит does: an empty rack blank shows the star centred, and a placed blank carries it
|
|
// in the value corner (the corner is free — a blank has no point value). The Scrabble
|
|
// variants leave the blank unmarked (an empty rack face; a placed blank shown by its
|
|
// designated letter alone).
|
|
export function usesStarBlank(v: Variant): boolean {
|
|
return v === 'erudit_ru';
|
|
}
|
|
|
|
// DEFAULT_VARIANTS is the variant set a player sees before any preference is stored — a fresh
|
|
// or offline native client whose profile has not been synced yet (the device-local guest boots
|
|
// with no profile at all). It mirrors the backend's new-account default (the account service
|
|
// seeds Erudit only), so the local guest and a later synced account agree on what is enabled.
|
|
// Every dictionary is still bundled; the other variants are simply off until the player turns
|
|
// them on in Settings.
|
|
export const DEFAULT_VARIANTS: Variant[] = ['erudit_ru'];
|
|
|
|
// availableVariants gates ALL_VARIANTS by the player's variant preferences (the set they
|
|
// enabled in Settings). An empty or absent set falls back to DEFAULT_VARIANTS rather than every
|
|
// variant: a real profile always carries at least one preference, and a profileless client (a
|
|
// fresh offline native launch) must match the server's Erudit-only default instead of exposing
|
|
// the English game before the player opts in.
|
|
export function availableVariants(preferences: Variant[] | undefined): VariantOption[] {
|
|
const prefs = preferences ?? [];
|
|
const effective = prefs.length === 0 ? DEFAULT_VARIANTS : prefs;
|
|
return ALL_VARIANTS.filter((v) => effective.includes(v.id));
|
|
}
|
|
|
|
// supportsMultipleWordsToggle reports whether the New Game "multiple words per turn" toggle
|
|
// applies to a variant. Only Russian games choose the rule; English is always standard, so
|
|
// its toggle is not shown.
|
|
export function supportsMultipleWordsToggle(v: Variant): boolean {
|
|
return VARIANT_LANGUAGE[v] === 'ru';
|
|
}
|
|
|
|
// multipleWordsForRequest resolves the per-turn word rule sent when starting a game of the
|
|
// variant: Russian games carry the toggle's value, English games are silently standard
|
|
// (true), so matchmaking and game creation stay one uniform path.
|
|
export function multipleWordsForRequest(v: Variant, toggle: boolean): boolean {
|
|
return supportsMultipleWordsToggle(v) ? toggle : true;
|
|
}
|