142 lines
3.8 KiB
TypeScript
142 lines
3.8 KiB
TypeScript
// 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()
|