feat(goldens): freeze the wall clock; 126 device+landscape goldens (A4)

- AppClock seam: production reads the system clock; tests freeze it. All
  app seeds/time reads route through it, so entry frames are deterministic.
- GoldenCaptureTest now covers every app at device 480x272 and adaptive
  landscape (126 goldens) with the clock frozen at 2024-06-15T12:34Z; the
  four clock-screen exclusions are gone.
- Checklist regenerated (goldens 126/126, flow crawl 63/63).
- Audit §8 records the Track A hardening results. 1,477/1,477 tests.
This commit is contained in:
John Smith
2026-09-11 14:21:44 -04:00
parent d4be623ec7
commit 39d6626b53
110 changed files with 262 additions and 166 deletions
+1 -1
View File
@@ -31,7 +31,7 @@ The current port faithfully reproduces the device across **home + quickplay
9 card/board titles with AI, 23 casual/puzzle games, 5 touch/toy/physics
titles, 6 big engines (3D racing, skating, marble, bowling, music-surf and
arena), and 7 dead-service local UIs (plus the social shell). Everything is
design-language-correct, tokenized, and audited by 1,410 unit/UI tests
design-language-correct, tokenized, and audited by 1,477 unit/UI tests
(including a 63-app smoke suite, layout-bounds gate, back-contract tests,
screenshot goldens and an emulator crawl) plus a source-level design-invariant
scanner.
@@ -359,7 +359,7 @@ fun AlarmClockApp() {
var engine by remember { mutableStateOf(AlarmClockEngine.initial()) }
var loaded by remember { mutableStateOf(false) }
var nowMinute by remember {
mutableStateOf(LocalTime.now().let { it.hour * 60 + it.minute })
mutableStateOf(AppClock.localTime().let { it.hour * 60 + it.minute })
}
val pager = rememberPagerState(initialPage = 0, pageCount = { 4 })
// Short pivot labels so all four tabs fit the crossbar without the last
@@ -405,7 +405,7 @@ fun AlarmClockApp() {
// sleep countdown; buzzer/ping audio is synthesized per second.
LaunchedEffect(Unit) {
while (isActive) {
val now = LocalTime.now()
val now = AppClock.localTime()
nowMinute = now.hour * 60 + now.minute
engine = AlarmClockEngine.tick(
engine,
@@ -0,0 +1,31 @@
package com.heretek.dorado_hd.ui.apps
import java.time.Instant
import java.time.LocalDate
import java.time.LocalTime
import java.time.YearMonth
import java.time.ZoneId
/**
* Wall-clock seam for mini-apps. Production uses the system clock; tests
* freeze it so clock-bearing entry screens (alarm, calendar, weather, notes)
* can be captured as deterministic golden screenshots.
*/
object AppClock {
@Volatile
var millis: () -> Long = { System.currentTimeMillis() }
@Volatile
var zone: ZoneId = ZoneId.systemDefault()
fun localTime(): LocalTime = Instant.ofEpochMilli(millis()).atZone(zone).toLocalTime()
fun localDate(): LocalDate = Instant.ofEpochMilli(millis()).atZone(zone).toLocalDate()
fun yearMonth(): YearMonth = YearMonth.from(localDate())
fun reset() {
millis = { System.currentTimeMillis() }
zone = ZoneId.systemDefault()
}
}
@@ -230,8 +230,8 @@ fun CalendarApp() {
val colors = LocalDoradoColors.current
val appts by graph.calendar.appointments().collectAsState(initial = emptyList())
val zone = remember { ZoneId.systemDefault() }
var monthStart by remember { mutableStateOf(YearMonth.now()) }
var selectedDate by remember { mutableStateOf(LocalDate.now()) }
var monthStart by remember { mutableStateOf(AppClock.yearMonth()) }
var selectedDate by remember { mutableStateOf(AppClock.localDate()) }
var view by remember { mutableStateOf(CalendarView.DAY) }
val dayStart = selectedDate.atStartOfDay(zone).toInstant().toEpochMilli()
@@ -343,8 +343,8 @@ fun CalendarApp() {
modifier = Modifier
.combinedClickable(
onClick = {
selectedDate = LocalDate.now()
monthStart = YearMonth.now()
selectedDate = AppClock.localDate()
monthStart = AppClock.yearMonth()
},
onLongClick = {},
)
@@ -292,7 +292,7 @@ fun MetronomeApp() {
modifier = Modifier
.combinedClickable(
onClick = {
metro = MetronomeEngine.tapTempo(metro, System.currentTimeMillis())
metro = MetronomeEngine.tapTempo(metro, AppClock.millis())
bank.play("tick")
},
onLongClick = {},
@@ -104,7 +104,7 @@ fun MusicQuizApp() {
val state = QuizEngine.startRound(
tracks = pool,
difficulty = difficulty,
seed = System.currentTimeMillis(),
seed = AppClock.millis(),
bonusEnabled = bonusEnabled,
mixName = mixName,
)
@@ -245,7 +245,7 @@ fun NotesApp() {
}
val docs = remember(notes) { notes.map { NotesEngine.decode(it.id, it.title, it.body, it.modifiedAt) } }
val now = System.currentTimeMillis()
val now = AppClock.millis()
if (selected != null) {
val start = selected!!
@@ -256,7 +256,7 @@ fun NotesApp() {
}
val persist: () -> Unit = {
val edited = start.copy(title = title, body = body, items = items.toList())
val saved = NotesEngine.saveOnExit(if (start.id == 0L) null else start, edited, System.currentTimeMillis())
val saved = NotesEngine.saveOnExit(if (start.id == 0L) null else start, edited, AppClock.millis())
scope.launch(kotlinx.coroutines.NonCancellable) {
if (saved == null) {
if (start.id != 0L) graph.notes.delete(start.id)
@@ -483,7 +483,7 @@ fun NotesApp() {
.combinedClickable(
onClick = {
query = ""
val nowMs = System.currentTimeMillis()
val nowMs = AppClock.millis()
selected = if (page == NoteKind.NOTE) {
NoteDoc(0L, "", "", emptyList(), nowMs, nowMs, NoteKind.NOTE)
} else {
@@ -319,12 +319,12 @@ fun PianoApp() {
}
fun playKey(key: PianoEngine.PianoKey) {
piano = PianoEngine.press(piano, key, System.currentTimeMillis())
piano = PianoEngine.press(piano, key, AppClock.millis())
voices.play(key.midi)
}
fun releaseMidi(midi: Int) {
piano = PianoEngine.release(piano, midi, System.currentTimeMillis())
piano = PianoEngine.release(piano, midi, AppClock.millis())
}
DetailScaffold(title = "piano") {
@@ -365,7 +365,7 @@ fun PianoApp() {
color = if (piano.sustain) colors.accent else colors.textSecondary,
modifier = Modifier.appTap(label = "sustain") {
piano = if (piano.sustain) {
PianoEngine.unsustain(piano.copy(sustain = false), System.currentTimeMillis())
PianoEngine.unsustain(piano.copy(sustain = false), AppClock.millis())
} else {
piano.copy(sustain = true)
}
@@ -508,7 +508,7 @@ private fun PianoKeyboard(
},
) {
val offset = state.whiteOffset
val audible = PianoEngine.audibleMidis(state, System.currentTimeMillis())
val audible = PianoEngine.audibleMidis(state, AppClock.millis())
drawRect(color = colors.elevated)
val first = floor(offset).toInt().coerceAtLeast(0)
val last = (first + (size.width / whiteWidth).toInt() + 2).coerceAtMost(PianoEngine.WHITE_KEYS - 1)
@@ -874,10 +874,10 @@ fun DrumMachineApp() {
LaunchedEffect(machine.mode) {
if (machine.mode != DrumMachineEngine.Mode.PLAYBACK) return@LaunchedEffect
playbackStart = System.currentTimeMillis()
playbackStart = AppClock.millis()
while (true) {
delay(16)
val elapsed = System.currentTimeMillis() - playbackStart
val elapsed = AppClock.millis() - playbackStart
val (due, next) = DrumMachineEngine.dueEvents(machine, elapsed)
due.forEach { event ->
machine.pads.firstOrNull { it.id == event.padId }?.let { bank.play(it, event.zone) }
@@ -904,7 +904,7 @@ fun DrumMachineApp() {
fun strike(pad: DrumMachineEngine.DrumPad, zone: DrumMachineEngine.Zone) {
bank.play(pad, zone)
if (machine.mode == DrumMachineEngine.Mode.RECORD) {
machine = DrumMachineEngine.record(machine, System.currentTimeMillis(), pad.id, zone)
machine = DrumMachineEngine.record(machine, AppClock.millis(), pad.id, zone)
}
}
@@ -946,7 +946,7 @@ fun DrumMachineApp() {
machine = if (machine.mode == DrumMachineEngine.Mode.RECORD) {
machine.copy(mode = DrumMachineEngine.Mode.PLAY)
} else {
DrumMachineEngine.beginRecording(machine, System.currentTimeMillis())
DrumMachineEngine.beginRecording(machine, AppClock.millis())
}
},
)
@@ -307,7 +307,7 @@ fun ShuffleByAlbumApp() {
var state by remember { mutableStateOf<ShuffleEngine.ShuffleState?>(null) }
var overlay by remember { mutableStateOf(ShuffleOverlay.NONE) }
var dragX by remember { mutableStateOf(0f) }
var lastTouchAt by remember { mutableStateOf(System.currentTimeMillis()) }
var lastTouchAt by remember { mutableStateOf(AppClock.millis()) }
var saver by remember { mutableStateOf(false) }
var prefs by remember { mutableStateOf(ShuffleEngine.ShufflePrefs(false, ShuffleEngine.Scope.ALL, null)) }
@@ -351,12 +351,12 @@ fun ShuffleByAlbumApp() {
LaunchedEffect(Unit) {
while (true) {
delay(500)
if (System.currentTimeMillis() - lastTouchAt >= DoradoTokens.IDLE_SCREENSAVER_MS) saver = true
if (AppClock.millis() - lastTouchAt >= DoradoTokens.IDLE_SCREENSAVER_MS) saver = true
}
}
fun touch() {
lastTouchAt = System.currentTimeMillis()
lastTouchAt = AppClock.millis()
saver = false
}
@@ -296,12 +296,12 @@ fun StopwatchApp() {
var mode by remember { mutableStateOf(StopwatchEngine.MODE_STOPWATCH) }
var sw by remember { mutableStateOf(StopwatchState()) }
var cd by remember { mutableStateOf(CountdownState()) }
var nowMs by remember { mutableStateOf(System.currentTimeMillis()) }
var nowMs by remember { mutableStateOf(AppClock.millis()) }
var loaded by remember { mutableStateOf(false) }
LaunchedEffect(Unit) {
graph.appState.get("stopwatch")?.let { blob ->
val restored = StopwatchEngine.restore(blob, System.currentTimeMillis())
val restored = StopwatchEngine.restore(blob, AppClock.millis())
sw = restored.stopwatch
cd = restored.countdown
mode = restored.mode
@@ -334,18 +334,18 @@ fun StopwatchApp() {
}
LaunchedEffect(sw.running, cd.running) {
while (sw.running || cd.running) {
nowMs = System.currentTimeMillis()
nowMs = AppClock.millis()
if (sw.running) sw = StopwatchEngine.tick(sw, nowMs)
if (cd.running) cd = StopwatchEngine.tickCountdown(cd, nowMs)
delay(33)
}
nowMs = System.currentTimeMillis()
nowMs = AppClock.millis()
}
LaunchedEffect(cd.alarming, cd.alarmStartedAtWall) {
if (!cd.alarming) return@LaunchedEffect
synth.start()
while (isActive) {
val phase = StopwatchEngine.alarmPhase(System.currentTimeMillis() - cd.alarmStartedAtWall)
val phase = StopwatchEngine.alarmPhase(AppClock.millis() - cd.alarmStartedAtWall)
if (phase.expired) {
cd = StopwatchEngine.stopAlarm(cd)
break
@@ -377,14 +377,14 @@ fun StopwatchApp() {
nowMs = nowMs,
onStartPause = {
if (sw.running) {
sw = StopwatchEngine.pause(sw, System.currentTimeMillis())
sw = StopwatchEngine.pause(sw, AppClock.millis())
} else {
sw = StopwatchEngine.start(sw, System.currentTimeMillis())
sw = StopwatchEngine.start(sw, AppClock.millis())
bank.play("select")
}
},
onLap = {
sw = StopwatchEngine.lap(sw, System.currentTimeMillis())
sw = StopwatchEngine.lap(sw, AppClock.millis())
bank.play("tick")
},
onReset = { sw = StopwatchEngine.reset(sw) },
@@ -396,9 +396,9 @@ fun StopwatchApp() {
onWheel = { unit, delta -> cd = StopwatchEngine.adjustWheel(cd, unit, delta) },
onStartPause = {
if (cd.running) {
cd = StopwatchEngine.pauseCountdown(cd, System.currentTimeMillis())
cd = StopwatchEngine.pauseCountdown(cd, AppClock.millis())
} else {
cd = StopwatchEngine.startCountdown(cd, System.currentTimeMillis())
cd = StopwatchEngine.startCountdown(cd, AppClock.millis())
bank.play("select")
}
},
@@ -35,6 +35,7 @@ import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.heretek.dorado_hd.ui.apps.AppClock
import com.heretek.dorado_hd.design.DoradoAccent
import com.heretek.dorado_hd.design.DoradoColors
import com.heretek.dorado_hd.design.DoradoTokens
@@ -93,7 +94,7 @@ fun AnimalgramsApp() {
}
fun openRound(key: String) {
game = AnimalgramsEngine.newRound(key, seed = (System.currentTimeMillis() and 0x7FFFFFFF).toInt())
game = AnimalgramsEngine.newRound(key, seed = (AppClock.millis() and 0x7FFFFFFF).toInt())
recorded = false
screen = "game"
}
@@ -39,6 +39,7 @@ import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.heretek.dorado_hd.ui.apps.AppClock
import com.heretek.dorado_hd.design.DoradoAccent
import com.heretek.dorado_hd.design.DoradoColors
import com.heretek.dorado_hd.design.DoradoTokens
@@ -146,7 +147,7 @@ fun BbqBattleApp() {
}
fun startNew() {
val fresh = BbqBattleEngine.newGame(System.currentTimeMillis().toInt(), easyMode = easy)
val fresh = BbqBattleEngine.newGame(AppClock.millis().toInt(), easyMode = easy)
game = fresh
saved = null
recorded = false
@@ -40,6 +40,7 @@ import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.heretek.dorado_hd.ui.apps.AppClock
import com.heretek.dorado_hd.design.DoradoAccent
import com.heretek.dorado_hd.design.DoradoColors
import com.heretek.dorado_hd.design.DoradoTokens
@@ -127,7 +128,7 @@ fun BeanstalkApp() {
}
fun startRun() {
run = BeanstalkEngine.newRun(seed = (System.currentTimeMillis() and 0x7FFFFFFF).toInt())
run = BeanstalkEngine.newRun(seed = (AppClock.millis() and 0x7FFFFFFF).toInt())
tap = null
paused = false
recorded = false
@@ -40,6 +40,7 @@ import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.heretek.dorado_hd.ui.apps.AppClock
import com.heretek.dorado_hd.design.DoradoAccent
import com.heretek.dorado_hd.design.DoradoColors
import com.heretek.dorado_hd.design.DoradoTokens
@@ -156,7 +157,7 @@ fun BeesApp() {
}
fun startLevel(id: Int, mode: BeesMode) {
val fresh = BeesEngine.newGame(id, mode, System.currentTimeMillis().toInt(), progress.upgrades)
val fresh = BeesEngine.newGame(id, mode, AppClock.millis().toInt(), progress.upgrades)
game = fresh
saved = null
recorded = false
@@ -34,6 +34,7 @@ import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.heretek.dorado_hd.ui.apps.AppClock
import com.heretek.dorado_hd.data.db.GameScoreEntity
import com.heretek.dorado_hd.design.DoradoTokens
import com.heretek.dorado_hd.design.LocalDoradoColors
@@ -205,7 +206,7 @@ fun BowlingApp() {
}
fun startGame() {
match = BowlingEngine.newMatch(mode, length, lane, ball, rival, seed = System.currentTimeMillis().toInt())
match = BowlingEngine.newMatch(mode, length, lane, ball, rival, seed = AppClock.millis().toInt())
recorded = false
paused = false
aim = 0f
@@ -39,6 +39,7 @@ import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.heretek.dorado_hd.ui.apps.AppClock
import com.heretek.dorado_hd.design.DoradoAccent
import com.heretek.dorado_hd.design.DoradoColors
import com.heretek.dorado_hd.design.DoradoTokens
@@ -155,7 +156,7 @@ fun CastlesApp() {
}
fun startLevel(number: Int) {
battle = CastlesEngine.newBattle(number, progress, System.currentTimeMillis().toInt())
battle = CastlesEngine.newBattle(number, progress, AppClock.millis().toInt())
camera = 0f
paused = false
recorded = false
@@ -34,6 +34,7 @@ import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.heretek.dorado_hd.ui.apps.AppClock
import com.heretek.dorado_hd.design.DoradoAccent
import com.heretek.dorado_hd.design.DoradoColors
import com.heretek.dorado_hd.design.DoradoTokens
@@ -115,7 +116,7 @@ fun ColorSpillApp() {
}
fun start(level: Int) {
game = ColorSpillEngine.newGame(level = level, seed = System.currentTimeMillis().toInt())
game = ColorSpillEngine.newGame(level = level, seed = AppClock.millis().toInt())
recorded = false
screen = "game"
}
@@ -41,6 +41,7 @@ import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.heretek.dorado_hd.ui.apps.AppClock
import com.heretek.dorado_hd.design.DoradoAccent
import com.heretek.dorado_hd.design.DoradoColors
import com.heretek.dorado_hd.design.DoradoTokens
@@ -160,7 +161,7 @@ fun EchoesApp() {
}
fun startNew(level: Int) {
game = EchoesEngine.newGame(mode, difficulty, System.currentTimeMillis().toInt(), level, ECHOES_COUNTDOWN_MS)
game = EchoesEngine.newGame(mode, difficulty, AppClock.millis().toInt(), level, ECHOES_COUNTDOWN_MS)
recorded = false
paused = false
stickOrigin = null
@@ -31,6 +31,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.heretek.dorado_hd.ui.apps.AppClock
import com.heretek.dorado_hd.design.DoradoTokens
import com.heretek.dorado_hd.design.LocalDoradoColors
import com.heretek.dorado_hd.design.Selawik
@@ -81,8 +82,8 @@ fun FanPredictionApp() {
val deviceId = graph.appState.get("fan-prediction-device")
?: UUID.randomUUID().toString().also { graph.appState.put("fan-prediction-device", it) }
val loaded = savedBlob?.let { FanPredictionEngine.decode(it) }
?: FanPredictionEngine.newState(deviceId, System.currentTimeMillis(), System.currentTimeMillis().toInt())
state = FanPredictionEngine.refresh(loaded, System.currentTimeMillis())
?: FanPredictionEngine.newState(deviceId, AppClock.millis(), AppClock.millis().toInt())
state = FanPredictionEngine.refresh(loaded, AppClock.millis())
}
LaunchedEffect(state) {
val current = state ?: return@LaunchedEffect
@@ -92,7 +93,7 @@ fun FanPredictionApp() {
while (true) {
delay(30_000)
val current = state ?: continue
state = FanPredictionEngine.apply(current, FanEvent.Refresh(System.currentTimeMillis()))
state = FanPredictionEngine.apply(current, FanEvent.Refresh(AppClock.millis()))
}
}
@@ -130,7 +131,7 @@ fun FanPredictionApp() {
onStandings = { screen = "standings"; playCue("select") },
onSettings = { nicknameDraft = current.profile.nickname; screen = "settings"; playCue("select") },
onAbout = { screen = "about"; playCue("select") },
onRefresh = { dispatch(FanEvent.Refresh(System.currentTimeMillis())) },
onRefresh = { dispatch(FanEvent.Refresh(AppClock.millis())) },
)
}
@@ -46,6 +46,7 @@ import androidx.compose.ui.unit.sp
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.compose.LocalLifecycleOwner
import com.heretek.dorado_hd.ui.apps.AppClock
import com.heretek.dorado_hd.design.DoradoAccent
import com.heretek.dorado_hd.design.DoradoColors
import com.heretek.dorado_hd.design.DoradoTokens
@@ -171,7 +172,7 @@ fun FingerpaintApp() {
}
fun startParty() {
val seed = System.currentTimeMillis().toInt()
val seed = AppClock.millis().toInt()
val names = (1..playerCount).map { "player $it" }
session = FingerpaintEngine.startRound(FingerpaintEngine.newSession(names, seed), seed)
brush = FingerpaintBrush()
@@ -183,7 +184,7 @@ fun FingerpaintApp() {
fun restart() {
val names = session?.players?.map { it.name } ?: return
val seed = System.currentTimeMillis().toInt()
val seed = AppClock.millis().toInt()
session = FingerpaintEngine.startRound(FingerpaintEngine.newSession(names, seed), seed)
brush = FingerpaintBrush()
liveStrokes.clear()
@@ -205,7 +206,7 @@ fun FingerpaintApp() {
saved = saved,
best = scores.firstOrNull()?.score ?: 0,
onSolo = {
val seed = System.currentTimeMillis().toInt()
val seed = AppClock.millis().toInt()
session = FingerpaintEngine.soloSession(seed)
brush = FingerpaintBrush()
liveStrokes.clear()
@@ -35,6 +35,7 @@ import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.heretek.dorado_hd.ui.apps.AppClock
import com.heretek.dorado_hd.design.DoradoAccent
import com.heretek.dorado_hd.design.DoradoColors
import com.heretek.dorado_hd.design.DoradoTokens
@@ -149,7 +150,7 @@ fun GooSplatApp() {
}
fun startNew() {
game = GooSplatEngine.newGame(System.currentTimeMillis().toInt())
game = GooSplatEngine.newGame(AppClock.millis().toInt())
recorded = false
paused = false
pendingTaps.clear()
@@ -31,6 +31,7 @@ import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.heretek.dorado_hd.ui.apps.AppClock
import com.heretek.dorado_hd.design.DoradoTokens
import com.heretek.dorado_hd.design.LocalDoradoColors
import com.heretek.dorado_hd.design.Selawik
@@ -129,7 +130,7 @@ fun HairballApp() {
}
fun startNew() {
game = HairballEngine.newGame(System.currentTimeMillis().toInt())
game = HairballEngine.newGame(AppClock.millis().toInt())
recorded = false
paused = false
runId++
@@ -34,6 +34,7 @@ import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.heretek.dorado_hd.ui.apps.AppClock
import com.heretek.dorado_hd.design.DoradoAccent
import com.heretek.dorado_hd.design.DoradoColors
import com.heretek.dorado_hd.design.DoradoTokens
@@ -112,7 +113,7 @@ fun HexicApp() {
}
fun startNew() {
val fresh = HexicEngine.newGame(mode, difficulty, seed = System.currentTimeMillis().toInt())
val fresh = HexicEngine.newGame(mode, difficulty, seed = AppClock.millis().toInt())
game = fresh
cursor = HexCursor(HexPos(0, 0))
recorded = false
@@ -38,6 +38,7 @@ import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.heretek.dorado_hd.ui.apps.AppClock
import com.heretek.dorado_hd.design.DoradoColors
import com.heretek.dorado_hd.design.DoradoTokens
import com.heretek.dorado_hd.design.LocalDoradoColors
@@ -324,7 +325,7 @@ fun PenaltyApp() {
fun startTournament(teamId: Int) {
saved = null
val t = PenaltyEngine.newTournament(teamId, System.currentTimeMillis().toInt())
val t = PenaltyEngine.newTournament(teamId, AppClock.millis().toInt())
tournament = t
tournamentRecorded = false
screen = "bracket"
@@ -332,14 +333,14 @@ fun PenaltyApp() {
}
fun startMad() {
mad = PenaltyEngine.newMadMinute(System.currentTimeMillis().toInt())
mad = PenaltyEngine.newMadMinute(AppClock.millis().toInt())
madRecorded = false
screen = "mad"
playCue("select")
}
fun startStreak() {
streak = PenaltyEngine.newGoalStreak(System.currentTimeMillis().toInt())
streak = PenaltyEngine.newGoalStreak(AppClock.millis().toInt())
screen = "streak"
playCue("select")
}
@@ -429,7 +430,7 @@ fun PenaltyApp() {
onSwipe = { swipe -> takePlayerShot(swipe, forPractice = false) },
onZone = { zone ->
heldZone = zone
pressedAt = System.currentTimeMillis()
pressedAt = AppClock.millis()
playCue("click")
},
onContinue = {
@@ -530,7 +531,7 @@ fun PenaltyApp() {
onSwipe = { swipe -> takePlayerShot(swipe, forPractice = true) },
onZone = { zone ->
heldZone = zone
pressedAt = System.currentTimeMillis()
pressedAt = AppClock.millis()
playCue("click")
if (lesson == 2) {
tutorialLesson = 3
@@ -35,6 +35,7 @@ import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.heretek.dorado_hd.ui.apps.AppClock
import com.heretek.dorado_hd.design.DoradoTokens
import com.heretek.dorado_hd.design.LocalDoradoColors
import com.heretek.dorado_hd.design.Selawik
@@ -158,7 +159,7 @@ fun PgrApp() {
laps = laps,
eventId = event?.id,
gateCount = event?.gateCount ?: 0,
seed = System.currentTimeMillis().toInt(),
seed = AppClock.millis().toInt(),
)
pendingEvent = event
outcome = null
@@ -26,6 +26,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.heretek.dorado_hd.ui.apps.AppClock
import com.heretek.dorado_hd.design.DoradoTokens
import com.heretek.dorado_hd.design.LocalDoradoColors
import com.heretek.dorado_hd.design.Selawik
@@ -235,7 +236,7 @@ fun PicturePuzzleApp() {
onGrid = { gridSize = it },
onSides = { twoSided = it },
onCategory = { category = it },
onStart = { startGame(seed = (System.currentTimeMillis() and 0x7fffffff).toInt()) },
onStart = { startGame(seed = (AppClock.millis() and 0x7fffffff).toInt()) },
)
PuzzleScreen.MIXUP -> PuzzleOverlay(
@@ -51,6 +51,7 @@ import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.heretek.dorado_hd.ui.apps.AppClock
import com.heretek.dorado_hd.design.DoradoAccent
import com.heretek.dorado_hd.design.DoradoColors
import com.heretek.dorado_hd.design.DoradoTokens
@@ -89,7 +90,7 @@ fun ShellGameApp() {
onDispose { synth.stop() }
}
var shell by remember { mutableStateOf(ShellGameEngine.idle(seed = System.currentTimeMillis().toInt())) }
var shell by remember { mutableStateOf(ShellGameEngine.idle(seed = AppClock.millis().toInt())) }
var paused by remember { mutableStateOf(false) }
var about by remember { mutableStateOf(false) }
var volume by remember { mutableStateOf(2) }
@@ -108,7 +109,7 @@ fun ShellGameApp() {
val unlocked = graph.appState.get("shell-game.trophies")
?.split(",")?.filter { it.isNotBlank() }?.toSet() ?: emptySet()
volume = graph.appState.get("shell-game.volume")?.toIntOrNull() ?: 2
shell = ShellGameEngine.idle(unlocked, System.currentTimeMillis().toInt())
shell = ShellGameEngine.idle(unlocked, AppClock.millis().toInt())
loaded = true
}
@@ -32,6 +32,7 @@ import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.heretek.dorado_hd.ui.apps.AppClock
import com.heretek.dorado_hd.design.DoradoTokens
import com.heretek.dorado_hd.design.LocalDoradoColors
import com.heretek.dorado_hd.design.Selawik
@@ -251,7 +252,7 @@ fun SkateApp() {
skaterId = career.skaterId,
boardId = career.boardId,
wheelId = career.wheelId,
seed = (System.currentTimeMillis() and 0x7fffffff).toInt(),
seed = (AppClock.millis() and 0x7fffffff).toInt(),
)
pendingEvent = event
recorded = false
@@ -35,6 +35,7 @@ import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.heretek.dorado_hd.ui.apps.AppClock
import com.heretek.dorado_hd.design.DoradoAccent
import com.heretek.dorado_hd.design.DoradoColors
import com.heretek.dorado_hd.design.DoradoTokens
@@ -191,7 +192,7 @@ fun SnowballApp() {
}
}
fun startCampaign(level: Int, seed: Int = System.currentTimeMillis().toInt()) {
fun startCampaign(level: Int, seed: Int = AppClock.millis().toInt()) {
game = SnowballEngine.newCampaign(level, seed)
showTutorial = !progress.tutorialCampaignSeen
countdownMs = if (showTutorial) 0L else SNOW_COUNTDOWN_TOTAL_MS
@@ -203,7 +204,7 @@ fun SnowballApp() {
}
fun startSurvival() {
game = SnowballEngine.newSurvival(System.currentTimeMillis().toInt())
game = SnowballEngine.newSurvival(AppClock.millis().toInt())
showTutorial = !progress.tutorialSurvivalSeen
countdownMs = if (showTutorial) 0L else SNOW_COUNTDOWN_TOTAL_MS
paused = false
@@ -45,6 +45,7 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.heretek.dorado_hd.ui.apps.AppClock
import com.heretek.dorado_hd.design.DoradoAccent
import com.heretek.dorado_hd.design.DoradoColors
import com.heretek.dorado_hd.design.DoradoTokens
@@ -192,7 +193,7 @@ fun SpaceBattleApp() {
stageId = min(id, SpaceBattleEngine.NUM_STAGES),
difficulty = difficulty,
build = progress.build,
seed = System.currentTimeMillis().toInt(),
seed = AppClock.millis().toInt(),
)
game = if (carry == null) {
fresh
@@ -217,7 +218,7 @@ fun SpaceBattleApp() {
stageId = 1,
difficulty = difficulty,
build = progress.build,
seed = System.currentTimeMillis().toInt(),
seed = AppClock.millis().toInt(),
mode = SpaceBattleEngine.GameMode.RACE,
raceId = trackId,
ghost = progress.ghosts[trackId],
@@ -36,6 +36,7 @@ import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.heretek.dorado_hd.ui.apps.AppClock
import com.heretek.dorado_hd.design.DoradoAccent
import com.heretek.dorado_hd.design.DoradoColors
import com.heretek.dorado_hd.design.DoradoTokens
@@ -150,7 +151,7 @@ fun SplatterBugApp() {
}
fun startNew() {
game = SplatterBugEngine.newGame(System.currentTimeMillis().toInt())
game = SplatterBugEngine.newGame(AppClock.millis().toInt())
recorded = false
paused = false
pendingTaps.clear()
@@ -34,6 +34,7 @@ import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.heretek.dorado_hd.ui.apps.AppClock
import com.heretek.dorado_hd.design.DoradoAccent
import com.heretek.dorado_hd.design.DoradoColors
import com.heretek.dorado_hd.design.DoradoTokens
@@ -152,7 +153,7 @@ fun SupernovaApp() {
}
fun startLevel(level: Int) {
game = SupernovaEngine.newLevel(mode, level, System.currentTimeMillis().toInt())
game = SupernovaEngine.newLevel(mode, level, AppClock.millis().toInt())
recorded = false
intro = true
paused = false
@@ -35,6 +35,7 @@ import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.heretek.dorado_hd.ui.apps.AppClock
import com.heretek.dorado_hd.design.DoradoTokens
import com.heretek.dorado_hd.design.LocalDoradoColors
import com.heretek.dorado_hd.design.Selawik
@@ -122,7 +123,7 @@ fun TilesApp() {
}
fun start(mode: TilesMode) {
game = TilesEngine.newGame(mode, System.currentTimeMillis().toInt())
game = TilesEngine.newGame(mode, AppClock.millis().toInt())
recorded = false
confirm = null
screen = "game"
@@ -40,6 +40,7 @@ import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.heretek.dorado_hd.ui.apps.AppClock
import com.heretek.dorado_hd.design.DoradoColors
import com.heretek.dorado_hd.design.DoradoTokens
import com.heretek.dorado_hd.design.LocalDoradoColors
@@ -135,7 +136,7 @@ fun TrashThrowApp() {
}
fun startScene(scene: TrashScene) {
game = TrashThrowEngine.newGame(scene, System.currentTimeMillis().toInt())
game = TrashThrowEngine.newGame(scene, AppClock.millis().toInt())
paused = false
screen = "game"
}
@@ -215,7 +216,7 @@ fun TrashThrowApp() {
.pointerInput(current.phase, paused) {
detectDragGestures(
onDragStart = {
dragStartMs = System.currentTimeMillis()
dragStartMs = AppClock.millis()
dragX = 0f
dragY = 0f
},
@@ -224,7 +225,7 @@ fun TrashThrowApp() {
dragY += drag.y
},
onDragEnd = {
val ticks = ((System.currentTimeMillis() - dragStartMs) / 16L).toInt()
val ticks = ((AppClock.millis() - dragStartMs) / 16L).toInt()
val currentGame = game
if (currentGame != null &&
TrashThrowEngine.isSwipe(dragX, dragY, ticks) &&
@@ -37,6 +37,7 @@ import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.heretek.dorado_hd.ui.apps.AppClock
import com.heretek.dorado_hd.design.DoradoAccent
import com.heretek.dorado_hd.design.DoradoColors
import com.heretek.dorado_hd.design.DoradoTokens
@@ -191,7 +192,7 @@ fun TugOWarApp() {
fun pull(side: TugSide) {
val current = game ?: return
val next = TugOWarEngine.pull(current, side, System.currentTimeMillis())
val next = TugOWarEngine.pull(current, side, AppClock.millis())
if (next !== current) {
game = next
bank.play("click")
@@ -41,6 +41,7 @@ import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.heretek.dorado_hd.ui.apps.AppClock
import com.heretek.dorado_hd.design.DoradoAccent
import com.heretek.dorado_hd.design.DoradoColors
import com.heretek.dorado_hd.design.DoradoTokens
@@ -143,7 +144,7 @@ fun VineClimbApp() {
}
fun startGame() {
game = VineClimbEngine.newGame(seed = System.currentTimeMillis().toInt())
game = VineClimbEngine.newGame(seed = AppClock.millis().toInt())
screen = "game"
paused = false
recorded = false
@@ -44,6 +44,7 @@ import androidx.compose.ui.text.drawText
import androidx.compose.ui.text.rememberTextMeasurer
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.heretek.dorado_hd.ui.apps.AppClock
import com.heretek.dorado_hd.design.DoradoAccent
import com.heretek.dorado_hd.design.DoradoColors
import com.heretek.dorado_hd.design.DoradoTokens
@@ -129,7 +130,7 @@ fun WordMongerApp() {
}
fun startGame(fresh: Boolean) {
val next = if (fresh) WordMongerEngine.newGame(1, (System.currentTimeMillis() and 0x7FFFFFFF).toInt(), tutorial) else saved
val next = if (fresh) WordMongerEngine.newGame(1, (AppClock.millis() and 0x7FFFFFFF).toInt(), tutorial) else saved
game = next
saved = null
paused = false
@@ -45,6 +45,7 @@ import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.heretek.dorado_hd.design.DoradoTokens
import com.heretek.dorado_hd.ui.apps.AppClock
import com.heretek.dorado_hd.design.LocalDoradoColors
import com.heretek.dorado_hd.design.Selawik
import com.heretek.dorado_hd.design.components.EdgeCropText
@@ -120,7 +121,7 @@ private fun CityPagerScreen(
onOpenSettings: () -> Unit,
) {
val colors = LocalDoradoColors.current
val now = remember { System.currentTimeMillis() }
val now = remember { AppClock.millis() }
val pages = state.cities.size.coerceAtLeast(1)
val pager = rememberPagerState(initialPage = state.selected.coerceIn(0, pages - 1)) { pages }
@@ -163,7 +164,7 @@ private fun CityPagerScreen(
color = colors.accent,
modifier = Modifier
.clickable {
val at = System.currentTimeMillis()
val at = AppClock.millis()
onState(
state.copy(
cities = state.cities.mapIndexed { index, city ->
Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Some files were not shown because too many files have changed in this diff Show More