Android build v1.1.38

This commit is contained in:
Warren H
2026-07-09 21:57:51 -04:00
parent 8ffa6db317
commit 4766a34cd3
10 changed files with 685 additions and 331 deletions
+20 -22
View File
@@ -22,11 +22,7 @@ import {
Iwt2RoguelikeUpgradeScreen,
Iwt2SettingsScreen,
} from './screens/Iwt2ShellScreens'
import {
loadIwt2Save,
writeIwt2Save,
type Iwt2Save,
} from './save/iwt2Repository'
import { writeIwt2Save, type Iwt2Save } from './save/iwt2Repository'
import { IWT2_BOSS_METADATA, type Iwt2BossId } from './content/bosses'
import { iwt2BossCoinRewardFor } from './content/bossRewards'
import {
@@ -153,15 +149,17 @@ const MENU_ITEMS: Array<{
]
export function IWantToHeal2App({
initialSave,
onlineBackupsAvailable,
onBackToGameSelect,
onExitToSaveSelect,
}: {
initialSave: Iwt2Save
onlineBackupsAvailable: boolean
onBackToGameSelect: () => void
onExitToSaveSelect: () => void
}) {
const { enabled: dualScreenEnabled } = useDualScreen()
const [screen, setScreen] = useState<Iwt2Screen>('menu')
const [save, setSave] = useState<Iwt2Save>(loadIwt2Save)
const [save, setSave] = useState<Iwt2Save>(initialSave)
const [selectedIndex, setSelectedIndex] = useState(0)
const [selectedBossId, setSelectedBossId] = useState<Iwt2BossId>('bulldrome')
const [arenaModeLabel, setArenaModeLabel] = useState('Dungeon')
@@ -205,7 +203,7 @@ export function IWantToHeal2App({
useGameAction((action, device) => {
if (screen !== 'menu' || device !== 'controller') return
if (action === 'back') {
onBackToGameSelect()
onExitToSaveSelect()
return
}
if (action === 'confirm') {
@@ -288,7 +286,7 @@ export function IWantToHeal2App({
if (screen === 'roguelike-upgrade' && roguelikeRun) {
return (
<main className="game-shell iwt2-shell">
<Iwt2Header save={save} onBackToGameSelect={onBackToGameSelect} />
<Iwt2Header save={save} onExitToSaveSelect={onExitToSaveSelect} />
<Iwt2RoguelikeUpgradeScreen
activeBuffSummary={summarizeIwt2Buffs(save, roguelikeRun.buffs)}
activeDebuffSummary={summarizeIwt2Debuffs(save, roguelikeRun.debuffs)}
@@ -312,7 +310,7 @@ export function IWantToHeal2App({
if (screen === 'dungeons') {
return (
<main className="game-shell iwt2-shell">
<Iwt2Header save={save} onBackToGameSelect={onBackToGameSelect} />
<Iwt2Header save={save} onExitToSaveSelect={onExitToSaveSelect} />
<Iwt2DungeonsScreen
difficultySlug={selectedDungeonDifficultySlug}
save={save}
@@ -333,7 +331,7 @@ export function IWantToHeal2App({
if (screen === 'roguelike') {
return (
<main className="game-shell iwt2-shell">
<Iwt2Header save={save} onBackToGameSelect={onBackToGameSelect} />
<Iwt2Header save={save} onExitToSaveSelect={onExitToSaveSelect} />
<Iwt2RoguelikeScreen
contentType={roguelikeContentType}
variant={roguelikeVariant}
@@ -363,7 +361,7 @@ export function IWantToHeal2App({
if (screen === 'raids') {
return (
<main className="game-shell iwt2-shell">
<Iwt2Header save={save} onBackToGameSelect={onBackToGameSelect} />
<Iwt2Header save={save} onExitToSaveSelect={onExitToSaveSelect} />
<Iwt2ModeScreen
difficultySlug={selectedRaidDifficultySlug}
mode="Raids"
@@ -397,7 +395,7 @@ export function IWantToHeal2App({
save={save}
title="Gear Upgrade"
onBack={() => setScreen('menu')}
onBackToGameSelect={onBackToGameSelect}
onExitToSaveSelect={onExitToSaveSelect}
/>
<Iwt2GearUpgradeScreen
save={save}
@@ -411,7 +409,7 @@ export function IWantToHeal2App({
if (screen === 'customize-character') {
return (
<main className="game-shell iwt2-shell">
<Iwt2Header save={save} onBackToGameSelect={onBackToGameSelect} />
<Iwt2Header save={save} onExitToSaveSelect={onExitToSaveSelect} />
<Iwt2CustomizeCharacterScreen
save={save}
onBack={() => setScreen('menu')}
@@ -424,7 +422,7 @@ export function IWantToHeal2App({
if (screen === 'cloud-save') {
return (
<main className="game-shell iwt2-shell">
<Iwt2Header save={save} onBackToGameSelect={onBackToGameSelect} />
<Iwt2Header save={save} onExitToSaveSelect={onExitToSaveSelect} />
<Iwt2CloudSaveScreen
save={save}
onlineBackupsAvailable={onlineBackupsAvailable}
@@ -438,7 +436,7 @@ export function IWantToHeal2App({
if (screen === 'settings') {
return (
<main className="game-shell iwt2-shell">
<Iwt2Header save={save} onBackToGameSelect={onBackToGameSelect} />
<Iwt2Header save={save} onExitToSaveSelect={onExitToSaveSelect} />
<Iwt2SettingsScreen onBack={() => setScreen('menu')} />
</main>
)
@@ -446,7 +444,7 @@ export function IWantToHeal2App({
return (
<main className="game-shell iwt2-shell">
<Iwt2Header save={save} onBackToGameSelect={onBackToGameSelect} />
<Iwt2Header save={save} onExitToSaveSelect={onExitToSaveSelect} />
{screen === 'menu' && (
<section className="iwt2-menu-screen" data-game-nav-active="true">
@@ -692,19 +690,19 @@ function isExtraTargetBuff(choice: Iwt2RoguelikeChoice<Iwt2RoguelikeSelfBuffId>)
function Iwt2Header({
onBack,
onBackToGameSelect,
onExitToSaveSelect,
save,
title,
}: {
onBack?: () => void
onBackToGameSelect: () => void
onExitToSaveSelect: () => void
save: Iwt2Save
title?: string
}) {
return (
<header className="topbar app-header">
<button className="brand-button" onClick={onBackToGameSelect} type="button">
<strong>Games</strong>
<button className="brand-button" onClick={onExitToSaveSelect} type="button">
<strong>Saves</strong>
</button>
{title && <strong className="iwt2-header-title">{title}</strong>}
<div className="character-summary">
+18 -7
View File
@@ -130,7 +130,11 @@ function normalizeSave(value: unknown): Iwt2Save {
if (candidate.version !== 1 && candidate.version !== 2) return createDefaultIwt2Save()
return {
version: 2,
updatedAt: typeof candidate.updatedAt === 'number' ? candidate.updatedAt : Date.now(),
updatedAt: typeof candidate.updatedAt === 'number'
&& Number.isFinite(candidate.updatedAt)
&& candidate.updatedAt > 0
? candidate.updatedAt
: Date.now(),
character: {
name: candidate.character?.name || 'Healer',
level: Math.max(1, Math.floor(candidate.character?.level ?? 1)),
@@ -150,10 +154,16 @@ function normalizeSave(value: unknown): Iwt2Save {
}
export function loadIwt2Save(): Iwt2Save {
return loadExistingIwt2Save() ?? createDefaultIwt2Save()
}
export function loadExistingIwt2Save(): Iwt2Save | null {
const serialized = window.localStorage.getItem(IWT2_SAVE_KEY)
if (!serialized) return null
try {
return normalizeSave(JSON.parse(window.localStorage.getItem(IWT2_SAVE_KEY) ?? 'null'))
return normalizeSave(JSON.parse(serialized))
} catch {
return createDefaultIwt2Save()
return null
}
}
@@ -176,10 +186,11 @@ export async function writeIwt2OnlineSave(save: Iwt2Save): Promise<Iwt2OnlineSav
}
export function writeIwt2Save(save: Iwt2Save) {
window.localStorage.setItem(IWT2_SAVE_KEY, JSON.stringify({
...save,
updatedAt: Date.now(),
}))
replaceIwt2Save(save)
}
export function replaceIwt2Save(save: Iwt2Save) {
window.localStorage.setItem(IWT2_SAVE_KEY, JSON.stringify(normalizeSave(save)))
}
export function recordIwt2BossKill(