diff --git a/js/core/core.js b/js/core/core.js index 11610cc6e..c2f924c3a 100644 --- a/js/core/core.js +++ b/js/core/core.js @@ -46,6 +46,9 @@ const SAVED_TARGETS_KEY = const SAVE_ARTILLERY_KEY = 'wardogs-save-artillery-position'; +const MAP_POINTS_KEY = + 'wardogs-map-points'; + /* ========================= ZOOM diff --git a/js/events.js b/js/events.js index 037f7ada9..5ad8edf6a 100644 --- a/js/events.js +++ b/js/events.js @@ -46,6 +46,13 @@ function bindEvents() { 'custom'; } + if ( + typeof loadMapPoints === + 'function' + ) { + loadMapPoints(); + } + clamp( S.origin ); diff --git a/js/features/saved-targets.js b/js/features/saved-targets.js index fd048ffd2..16fa52d0b 100644 --- a/js/features/saved-targets.js +++ b/js/features/saved-targets.js @@ -164,6 +164,163 @@ function persistSavedTargets() { ); } +/* ========================= + ARTILLERY / TARGET POSITIONS + ========================= */ + +/* + * Where the two points sit is worth keeping across a reload: coming back + * to a gun laid on the wrong side of the map means placing it again every + * single time. + * + * Every map keeps its own entry, keyed by map id, because the coordinates + * are meaningless on a different map. Switching maps restores that map's + * pair and leaves the others untouched. + */ +const MAP_POINTS_WRITE_DELAY_MS = 300; + +let mapPointsWriteTimer = null; + +function persistMapPoints() { + + /* + * inputs() runs on every frame of a drag, so the write trails the + * gesture instead of hitting localStorage a hundred times across it. + */ + if (mapPointsWriteTimer) { + return; + } + + mapPointsWriteTimer = setTimeout( + () => { + mapPointsWriteTimer = null; + writeMapPoints(); + }, + MAP_POINTS_WRITE_DELAY_MS + ); +} + +function readMapPointsStore() { + + const raw = + localStorage.getItem( + MAP_POINTS_KEY + ); + + if (!raw) { + return {}; + } + + let parsed = null; + + try { + parsed = + JSON.parse(raw); + } catch (error) { + return {}; + } + + if ( + !parsed || + typeof parsed !== 'object' + ) { + return {}; + } + + /* + * The first release stored a single { map, origin, target } object; + * fold that lone map into the keyed shape instead of dropping it. + */ + if ( + typeof parsed.map === 'string' + ) { + + return { + [parsed.map]: { + origin: parsed.origin, + target: parsed.target + } + }; + } + + return parsed; +} + +function writeMapPoints() { + + try { + const store = + readMapPointsStore(); + + store[S.map] = { + origin: { + x: S.origin.x, + y: S.origin.y + }, + target: { + x: S.target.x, + y: S.target.y + } + }; + + localStorage.setItem( + MAP_POINTS_KEY, + JSON.stringify(store) + ); + } catch (error) { + console.warn( + 'Failed to save map points:', + error + ); + } +} + +function readStoredPoint(value) { + + return ( + value && + Number.isFinite(Number(value.x)) && + Number.isFinite(Number(value.y)) + ) + ? { + x: Number(value.x), + y: Number(value.y) + } + : null; +} + +function loadMapPoints() { + + try { + const stored = + readMapPointsStore()[S.map]; + + if (!stored) { + return; + } + + const origin = + readStoredPoint(stored.origin); + + const target = + readStoredPoint(stored.target); + + if (origin) { + S.origin = origin; + } + + if (target) { + S.target = target; + } + + } catch (error) { + console.warn( + 'Failed to load map points:', + error + ); + } +} + function getSaveArtilleryPreference() { return ( diff --git a/js/main.js b/js/main.js index aa98a19dd..8d834f2b1 100644 --- a/js/main.js +++ b/js/main.js @@ -207,6 +207,12 @@ async function init() { initLayout(); + /* + * Before the clamp below, so points restored from a previous + * visit are pulled inside the map's bounds like any other. + */ + loadMapPoints(); + /* * Sync initial state with the * selected preset map after the diff --git a/js/ui/inputs.js b/js/ui/inputs.js index 92c3657cf..8cfa7fa23 100644 --- a/js/ui/inputs.js +++ b/js/ui/inputs.js @@ -24,6 +24,19 @@ function inputs() { $('h').value = S.h; + /* + * Origin and target are written from six different places (map drags, + * the coordinate inputs, saved-target restore, undo, coordinate + * search). They all land here, so one throttled write covers them all + * instead of a hook at each site. + */ + if ( + typeof persistMapPoints === + 'function' + ) { + persistMapPoints(); + } + /* * The saved-target highlight is derived from where the target sits, * so every writer of S.target refreshes it by arriving here.