Skip to content

Commit 84ae44c

Browse files
committed
Merge generation-safe persistence into feedback polish
2 parents 32dc21b + 47ed8b5 commit 84ae44c

3 files changed

Lines changed: 55 additions & 12 deletions

File tree

index.html

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -181,12 +181,15 @@ <h2 id="journal-title">SIGNAL LOG</h2>
181181
try { progressStorage = window.localStorage; } catch {}
182182
const savedProgress = LittleAndroidProgress.loadProgress(progressStorage);
183183
const feedback = LittleAndroidFeedback.createFeedbackController(window, progressStorage);
184+
let progressGeneration = savedProgress.generation;
184185
for (const fragment of signalFragments) {
185186
fragment.discovered = savedProgress.discoveredFragments.includes(fragment.id);
186187
fragment.collected = savedProgress.collectedFragments.includes(fragment.id);
187188
}
188189
const discoveredNPCs = new Set(savedProgress.discoveredNPCs);
189-
const quest = { complete: savedProgress.complete };
190+
const quest = {
191+
complete: savedProgress.complete || signalFragments.every(fragment => fragment.collected),
192+
};
190193

191194
function isWalkable(tx,ty){
192195
if(tx<0||ty<0||tx>=MW||ty>=MH)return false;
@@ -977,6 +980,7 @@ <h2 id="journal-title">SIGNAL LOG</h2>
977980
function currentProgress() {
978981
return {
979982
version: LittleAndroidProgress.SAVE_VERSION,
983+
generation: progressGeneration,
980984
discoveredFragments: signalFragments.filter(fragment => fragment.discovered).map(fragment => fragment.id),
981985
collectedFragments: signalFragments.filter(fragment => fragment.collected).map(fragment => fragment.id),
982986
discoveredNPCs: [...discoveredNPCs],
@@ -986,8 +990,23 @@ <h2 id="journal-title">SIGNAL LOG</h2>
986990

987991
function persistProgress() {
988992
const latest = LittleAndroidProgress.loadProgress(progressStorage);
989-
const merged = LittleAndroidProgress.mergeProgress(latest, currentProgress());
993+
const merged = LittleAndroidProgress.mergeProgress(
994+
latest,
995+
currentProgress(),
996+
signalFragments.map(fragment => fragment.id),
997+
);
998+
progressGeneration = merged.generation;
999+
for (const fragment of signalFragments) {
1000+
fragment.discovered = merged.discoveredFragments.includes(fragment.id);
1001+
fragment.collected = merged.collectedFragments.includes(fragment.id);
1002+
}
1003+
discoveredNPCs.clear();
1004+
for (const identity of merged.discoveredNPCs) discoveredNPCs.add(identity);
1005+
quest.complete = merged.complete;
1006+
document.body.classList.toggle('world-restored', quest.complete);
1007+
updateQuestUI();
9901008
LittleAndroidProgress.saveProgress(progressStorage, merged);
1009+
return merged;
9911010
}
9921011

9931012
function rememberNPC(npc) {
@@ -1046,7 +1065,9 @@ <h2 id="journal-title">SIGNAL LOG</h2>
10461065
discoveredNPCs.clear();
10471066
quest.complete = false;
10481067
document.body.classList.remove('world-restored');
1049-
LittleAndroidProgress.clearProgress(progressStorage);
1068+
const storedGeneration = LittleAndroidProgress.loadProgress(progressStorage).generation;
1069+
progressGeneration = Math.max(progressGeneration, storedGeneration) + 1;
1070+
LittleAndroidProgress.saveProgress(progressStorage, currentProgress());
10501071
updateQuestUI();
10511072
updateJournal();
10521073
showStatus('SIGNAL SEARCH RESTARTED', 2);
@@ -1059,8 +1080,7 @@ <h2 id="journal-title">SIGNAL LOG</h2>
10591080
confirmReset(message) :
10601081
typeof window.confirm === 'function' && window.confirm(message);
10611082
if (!confirmed) return false;
1062-
resetQuest();
1063-
return true;
1083+
return resetQuest();
10641084
}
10651085

10661086
function performAction() {

src/progress.js

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
function defaultProgress() {
1212
return {
1313
version: SAVE_VERSION,
14+
generation: 0,
1415
discoveredFragments: [],
1516
collectedFragments: [],
1617
discoveredNPCs: [],
@@ -30,6 +31,7 @@
3031
if (!parsed || parsed.version !== SAVE_VERSION) return defaultProgress();
3132
return {
3233
version: SAVE_VERSION,
34+
generation: Number.isSafeInteger(parsed.generation) && parsed.generation >= 0 ? parsed.generation : 0,
3335
discoveredFragments: normalizeStringList(parsed.discoveredFragments),
3436
collectedFragments: normalizeStringList(parsed.collectedFragments),
3537
discoveredNPCs: normalizeStringList(parsed.discoveredNPCs),
@@ -48,24 +50,32 @@
4850
}
4951
}
5052

51-
function mergeProgress(base, incoming) {
53+
function mergeProgress(base, incoming, requiredFragmentIds = []) {
5254
const current = parseProgress(base);
5355
const next = parseProgress(incoming);
56+
if (current.generation !== next.generation) {
57+
return current.generation > next.generation ? current : next;
58+
}
59+
const collectedFragments = normalizeStringList([
60+
...current.collectedFragments,
61+
...next.collectedFragments,
62+
]);
5463
return {
5564
version: SAVE_VERSION,
65+
generation: current.generation,
5666
discoveredFragments: normalizeStringList([
5767
...current.discoveredFragments,
5868
...next.discoveredFragments,
5969
]),
60-
collectedFragments: normalizeStringList([
61-
...current.collectedFragments,
62-
...next.collectedFragments,
63-
]),
70+
collectedFragments,
6471
discoveredNPCs: normalizeStringList([
6572
...current.discoveredNPCs,
6673
...next.discoveredNPCs,
6774
]),
68-
complete: current.complete || next.complete,
75+
complete: current.complete || next.complete || (
76+
requiredFragmentIds.length > 0 &&
77+
requiredFragmentIds.every(id => collectedFragments.includes(id))
78+
),
6979
};
7080
}
7181

tests/progress.test.cjs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ test('progress normalizes lists and ignores unknown fields', () => {
3737
});
3838
assert.deepEqual(parsed, {
3939
version: 1,
40+
generation: 0,
4041
discoveredFragments: ['a'],
4142
collectedFragments: ['a'],
4243
discoveredNPCs: ['scout'],
@@ -64,10 +65,22 @@ test('progress merges monotonically across stale tabs', () => {
6465
...defaultProgress(),
6566
discoveredNPCs: ['OVERSEER'],
6667
};
67-
assert.deepEqual(mergeProgress(firstTab, staleTab), {
68+
assert.deepEqual(mergeProgress(firstTab, staleTab, ['field-coil']), {
6869
...defaultProgress(),
6970
discoveredFragments: ['field-coil'],
7071
collectedFragments: ['field-coil'],
7172
discoveredNPCs: ['OVERSEER'],
73+
complete: true,
7274
});
7375
});
76+
77+
test('a newer reset generation defeats stale tab writes', () => {
78+
const reset = { ...defaultProgress(), generation: 2 };
79+
const stale = {
80+
...defaultProgress(),
81+
generation: 1,
82+
collectedFragments: ['field-coil'],
83+
complete: true,
84+
};
85+
assert.deepEqual(mergeProgress(reset, stale, ['field-coil']), reset);
86+
});

0 commit comments

Comments
 (0)