Skip to content

Commit 47ed8b5

Browse files
committed
Make cross-tab progress and resets monotonic
1 parent 3d8ee74 commit 47ed8b5

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
@@ -172,12 +172,15 @@ <h2 id="journal-title">SIGNAL LOG</h2>
172172
let progressStorage = null;
173173
try { progressStorage = window.localStorage; } catch {}
174174
const savedProgress = LittleAndroidProgress.loadProgress(progressStorage);
175+
let progressGeneration = savedProgress.generation;
175176
for (const fragment of signalFragments) {
176177
fragment.discovered = savedProgress.discoveredFragments.includes(fragment.id);
177178
fragment.collected = savedProgress.collectedFragments.includes(fragment.id);
178179
}
179180
const discoveredNPCs = new Set(savedProgress.discoveredNPCs);
180-
const quest = { complete: savedProgress.complete };
181+
const quest = {
182+
complete: savedProgress.complete || signalFragments.every(fragment => fragment.collected),
183+
};
181184

182185
function isWalkable(tx,ty){
183186
if(tx<0||ty<0||tx>=MW||ty>=MH)return false;
@@ -963,6 +966,7 @@ <h2 id="journal-title">SIGNAL LOG</h2>
963966
function currentProgress() {
964967
return {
965968
version: LittleAndroidProgress.SAVE_VERSION,
969+
generation: progressGeneration,
966970
discoveredFragments: signalFragments.filter(fragment => fragment.discovered).map(fragment => fragment.id),
967971
collectedFragments: signalFragments.filter(fragment => fragment.collected).map(fragment => fragment.id),
968972
discoveredNPCs: [...discoveredNPCs],
@@ -972,8 +976,23 @@ <h2 id="journal-title">SIGNAL LOG</h2>
972976

973977
function persistProgress() {
974978
const latest = LittleAndroidProgress.loadProgress(progressStorage);
975-
const merged = LittleAndroidProgress.mergeProgress(latest, currentProgress());
979+
const merged = LittleAndroidProgress.mergeProgress(
980+
latest,
981+
currentProgress(),
982+
signalFragments.map(fragment => fragment.id),
983+
);
984+
progressGeneration = merged.generation;
985+
for (const fragment of signalFragments) {
986+
fragment.discovered = merged.discoveredFragments.includes(fragment.id);
987+
fragment.collected = merged.collectedFragments.includes(fragment.id);
988+
}
989+
discoveredNPCs.clear();
990+
for (const identity of merged.discoveredNPCs) discoveredNPCs.add(identity);
991+
quest.complete = merged.complete;
992+
document.body.classList.toggle('world-restored', quest.complete);
993+
updateQuestUI();
976994
LittleAndroidProgress.saveProgress(progressStorage, merged);
995+
return merged;
977996
}
978997

979998
function rememberNPC(npc) {
@@ -1028,7 +1047,9 @@ <h2 id="journal-title">SIGNAL LOG</h2>
10281047
discoveredNPCs.clear();
10291048
quest.complete = false;
10301049
document.body.classList.remove('world-restored');
1031-
LittleAndroidProgress.clearProgress(progressStorage);
1050+
const storedGeneration = LittleAndroidProgress.loadProgress(progressStorage).generation;
1051+
progressGeneration = Math.max(progressGeneration, storedGeneration) + 1;
1052+
LittleAndroidProgress.saveProgress(progressStorage, currentProgress());
10321053
updateQuestUI();
10331054
updateJournal();
10341055
showStatus('SIGNAL SEARCH RESTARTED', 2);
@@ -1041,8 +1062,7 @@ <h2 id="journal-title">SIGNAL LOG</h2>
10411062
confirmReset(message) :
10421063
typeof window.confirm === 'function' && window.confirm(message);
10431064
if (!confirmed) return false;
1044-
resetQuest();
1045-
return true;
1065+
return resetQuest();
10461066
}
10471067

10481068
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)