ninelands-reddit/src/client/splash.ts
Lukas Müllner 51d47cd0d0 feat: Realm-Auswahl (Farbe, Volk, Thema, Banner), Freischaltungen, Flair, Ergebnis als Kommentar
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 17:33:53 +01:00

142 lines
3.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Startbildschirm im Post (Inline): Name, Nummer, Datum, Vorschau der Karte, eigene Werte, Spielen-Knopf.
import './style.css'
import './splash.css'
import {requestExpandedMode} from '@devvit/web/client'
import {renderMiniBoard} from '../engine/app/board/BoardView.ts'
import type {WorldTrait} from '../engine/core/WorldRules.ts'
import type {GetDayRsp} from '../shared/api.ts'
import {dailyBoard, dailyMap, dayLabel} from '../shared/daily.ts'
import {fetchDay} from './api.ts'
import {byId, h} from './dom.ts'
import {appReady} from './journeys.ts'
import {applyLook, openRealm} from './realm.ts'
import {leaderTable} from './result.ts'
import {lawName, starsLine} from './texts.ts'
const app = byId<HTMLDivElement>('app')
async function main(): Promise<void> {
app.replaceChildren(h('div.loading', 'Loading…'))
let day: GetDayRsp
try {
day = await fetchDay()
} catch (err) {
app.replaceChildren(
h('div.error', `Could not load the map. ${(err as Error).message}`),
)
return
}
render(day)
appReady()
}
function render(day: GetDayRsp): void {
const map = dailyMap(new Date(`${day.day}T00:00:00Z`))
applyLook(day.look)
const mini = h('div.mini')
renderMiniBoard(mini, dailyBoard(map))
const realm = h('button.icon.realm-btn', '🎨 Realm')
realm.title = 'Customise your realm'
realm.addEventListener('click', () =>
openRealm({
look: day.look,
progress: {
bestStreak: day.stats?.bestStreak ?? 0,
totalStars: day.stats?.totalStars ?? 0,
},
day: dailyMap(new Date()).day,
loggedIn: day.loggedIn,
onChange: look => {
day.look = look
applyLook(look)
renderMiniBoard(mini, dailyBoard(map))
},
}),
)
const play = h('button.primary', day.mine ? 'Play again' : 'Play')
play.addEventListener('click', ev => requestExpandedMode(ev, 'game'))
const info = h('button.icon.info', 'i')
info.title = 'Credits'
info.addEventListener('click', showCredits)
const mine = day.mine
const status = mine
? h(
'p.pixel.mine',
h('span.stars', starsLine(mine.stars)),
` ${mine.gold} gold · #${mine.rank} of ${day.players}`,
)
: h(
'p.goals-line',
h('span.stars', '★'),
` ${day.goals.gold} gold `,
h('span.stars', '★'),
` ${day.goals.lands} lands `,
h('span.stars', '★'),
' both',
)
const streak = day.stats?.streak ?? 0
app.replaceChildren(
h(
'header.head',
h('h1', 'Ninelands'),
h(
'p.muted.sub',
`#${day.number} · ${dayLabel(day.day)}${day.today ? '' : ' · past map'}`,
),
realm,
info,
),
h(
'main.body',
mini,
h(
'div.side',
h('p.law', 'Law: ', h('b', lawName(day.trait as WorldTrait))),
status,
play,
streak > 0 ? h('p.pixel.streak', `🔥 ${streak}-day streak`) : null,
h(
'div.top',
h(
'p.pixel.top-title',
day.players > 0
? `Today's best · ${day.players} playing`
: "Today's best",
),
leaderTable(day.top.slice(0, 3), mine?.rank ?? 0),
),
),
),
)
}
function showCredits(): void {
const close = h('button.primary', 'Close')
const overlay = h(
'div.overlay',
h(
'div.panel',
h('h2', 'Credits'),
h(
'p',
'Ninelands by StackTeam – a daily port of the strategy game Ninelands.',
),
h(
'p',
'Music: "Dreamscape (part I)" by Vee Ray, CC BY-SA 4.0 (re-encoded).',
),
h('p', 'Graphics: "Mini World Sprites" by Shade and octoshrimpy, CC0.'),
h('p', 'Font: Pixelify Sans, SIL Open Font License 1.1.'),
h('div.row.end', close),
),
)
close.addEventListener('click', () => overlay.remove())
document.body.append(overlay)
}
void main()