index : btroops | |
Virtual board game-aid for BattleTroops, an infantry combat simulator wargame published by FASA in 1989. |
aboutsummaryrefslogtreecommitdiff |
diff options
-rw-r--r-- | public/index.html | 21 | ||||
-rw-r--r-- | public/map.css | 5 | ||||
-rw-r--r-- | public/map1.svg (renamed from public/map.svg) | 0 | ||||
-rw-r--r-- | public/map2.svg | 84 | ||||
-rw-r--r-- | src/index.js | 169 | ||||
-rw-r--r-- | src/modules/gameboard.js | 16 | ||||
-rw-r--r-- | src/modules/record_sheet.js | 4 |
7 files changed, 214 insertions, 85 deletions
diff --git a/public/index.html b/public/index.html index ef12273..d8a91f9 100644 --- a/public/index.html +++ b/public/index.html @@ -104,7 +104,7 @@ Loading... </div> - <object type="image/svg+xml" data="map.svg"></object> + <object type="image/svg+xml" data="map1.svg"></object> <div id="status"> <span id="hex-counter">Distance: <span id="hex-count">-</span></span> @@ -134,6 +134,25 @@ <span class="inning-top">◓</span> <span class="inning-bottom">◒</span> </span> + + <dialog id="map-dialog"> + <form> + <p> + <label> + Map: + <select> + <option value="map1">Map1</option> + <option value="map2">Map2</option> + </select> + </label> + </p> + <div> + <button value="cancel" formmethod="dialog">Cancel</button> + <button id="confirm-btn" value="default">Confirm</button> + </div> + </form> + </dialog> + <button id="show-dialog">Change map</button> </div> <div id="record-sheet"> diff --git a/public/map.css b/public/map.css index 932d224..dbabab9 100644 --- a/public/map.css +++ b/public/map.css @@ -203,11 +203,11 @@ g.selected use { transform: translate(19px, 31px) scale(4); } -g.start-locations > g:first-child { +g.start-locations > g:first-child:not([data-y]) { --i: -2; } -g.start-locations > g:last-child { +g.start-locations > g:last-child:not([data-y]) { --i: 52; } @@ -249,6 +249,7 @@ g[data-y]:nth-child(odd) { transform: translateX(calc(var(--x-step) * var(--i))) scale(var(--scale)); } +g[data-y="-2"] { --i: -2; } g[data-y="0"] { --i: 0; } g[data-y="1"] { --i: 1; } g[data-y="2"] { --i: 2; } diff --git a/public/map.svg b/public/map1.svg index d927ae4..d927ae4 100644 --- a/public/map.svg +++ b/public/map1.svg diff --git a/public/map2.svg b/public/map2.svg new file mode 100644 index 0000000..1d0f8c4 --- /dev/null +++ b/public/map2.svg @@ -0,0 +1,84 @@ +<?xml version="1.0" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" + "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg viewBox="-25 -150 400 570" xmlns="http://www.w3.org/2000/svg"> + <link xmlns="http://www.w3.org/1999/xhtml" rel="stylesheet" href="map.css" type="text/css"/> + <defs> + <polygon id="hex" points="0,10 8.66,5 8.66,-5 0,-10 -8.66,-5 -8.66,5"/> + + <circle id="counter-base" cx="0" cy="0" r="5"/> + + <g id="t-1" class="troop-counter-template"><use href="#counter-base"/><text>1</text></g> + <g id="t-2" class="troop-counter-template"><use href="#counter-base"/><text>2</text></g> + <g id="t-3" class="troop-counter-template"><use href="#counter-base"/><text>3</text></g> + <g id="t-4" class="troop-counter-template"><use href="#counter-base"/><text>4</text></g> + <g id="t-5" class="troop-counter-template"><use href="#counter-base"/><text>5</text></g> + <g id="t-6" class="troop-counter-template"><use href="#counter-base"/><text>6</text></g> + <g id="t-7" class="troop-counter-template"><use href="#counter-base"/><text>7</text></g> + + <image id="counter-prone" href="counter_prone.jpg" width="10"/> + <image id="counter-grenade" href="counter_grenade.jpg" width="10"/> + </defs> + + <rect id="background" x="-18" y="-10" width="386" height="322"/> + + <g class="board"> + <g id="firing-arcs"> + <g id="shapes"/> + <g id="lines"/> + </g> + <g class="start-locations"> + <g data-y="-2"> + <g data-x="4" class="counter" data-allegiance="liao" data-number="1"><use href="#t-1"/></g> + <g data-x="3" class="counter" data-allegiance="liao" data-number="2"><use href="#t-2"/></g> + <g data-x="2" class="counter" data-allegiance="liao" data-number="3"><use href="#t-3"/></g> + <g data-x="1" class="counter" data-allegiance="liao" data-number="4"><use href="#t-4"/></g> + <g data-x="0" class="counter" data-allegiance="liao" data-number="5"><use href="#t-5"/></g> + </g> + <g data-y="6"> + <g data-x="0" class="counter" data-allegiance="davion" data-number="1"><use href="#t-1"/></g> + <g data-x="1" class="counter" data-allegiance="davion" data-number="2"><use href="#t-2"/></g> + <g data-x="2" class="counter" data-allegiance="davion" data-number="3"><use href="#t-3"/></g> + <g data-x="3" class="counter" data-allegiance="davion" data-number="4"><use href="#t-4"/></g> + <g data-x="4" class="counter" data-allegiance="davion" data-number="5"><use href="#t-5"/></g> + </g> + </g> + <g class="grid"> + <g data-y="0"> + <g data-x="0"><use href="#hex"/></g> + <g data-x="1"><use href="#hex"/></g> + <g data-x="2"><use href="#hex"/></g> + <g data-x="3"><use href="#hex"/></g> + <g data-x="4"><use href="#hex"/></g> + </g> + <g data-y="1"> + <g data-x="0"><use href="#hex"/></g> + <g data-x="1"><use href="#hex"/></g> + <g data-x="2"><use href="#hex"/></g> + <g data-x="3"><use href="#hex"/></g> + <g data-x="4"><use href="#hex"/></g> + </g> + <g data-y="2"> + <g data-x="0"><use href="#hex"/></g> + <g data-x="1"><use href="#hex"/></g> + <g data-x="2"><use href="#hex"/></g> + <g data-x="3"><use href="#hex"/></g> + <g data-x="4"><use href="#hex"/></g> + </g> + <g data-y="3"> + <g data-x="0"><use href="#hex"/></g> + <g data-x="1"><use href="#hex"/></g> + <g data-x="2"><use href="#hex"/></g> + <g data-x="3"><use href="#hex"/></g> + <g data-x="4"><use href="#hex"/></g> + </g> + <g data-y="4"> + <g data-x="0"><use href="#hex"/></g> + <g data-x="1"><use href="#hex"/></g> + <g data-x="2"><use href="#hex"/></g> + <g data-x="3"><use href="#hex"/></g> + <g data-x="4"><use href="#hex"/></g> + </g> + </g> + </g> +</svg> diff --git a/src/index.js b/src/index.js index 40ee397..a8b766a 100644 --- a/src/index.js +++ b/src/index.js @@ -6,13 +6,89 @@ globalThis.svgns = "http://www.w3.org/2000/svg"; const mapPlaceholder = document.querySelector('.map-placeholder'), distanceOutput = document.getElementById('status'), - proneToggle = document.getElementById('toggle-prone-counter'); + proneToggle = document.getElementById('toggle-prone-counter'), + object = document.querySelector('object'); -document.querySelector('object').addEventListener('load', function () { +object.addEventListener('load', function () { mapPlaceholder.remove(); this.style.opacity = 1; + + const svg = this.contentDocument.querySelector('svg'); + panzoom.start(svg); + gameboard.start(svg); +}); + +gameboard.setDistanceCallback((count = '-') => { + distanceOutput.querySelector('#hex-count').textContent = count; + distanceOutput.style.display = count === '-' ? 'none' : 'block'; +}); + +gameboard.setProneFlagCallback(checked => proneToggle.checked = checked); +gameboard.setSelectCallback(data => recordSheet.select(data)); + +document.querySelectorAll('.soldier-record').forEach(el => + el.addEventListener('click', () => { + if (el.classList.contains('selected')) { + el.classList.remove('selected'); + gameboard.unSelect(); + recordSheet.unSelect(); + } else { + gameboard.select(el); + } + }) +); + +document.querySelectorAll('.end-move').forEach(el => el.addEventListener('click', () => { + recordSheet.endMove(); + gameboard.endMove(); +})); + +document.querySelectorAll('.end-turn').forEach(el => + el.addEventListener('click', ({ target: { dataset: { allegiance }}}) => { + const dataSelector = `[data-allegiance="${allegiance}"]`, + records = Array.from(document.querySelectorAll(`.soldier-record${dataSelector}`)), + turnCounter = document.getElementById('turn-count'), + { dataset: { update }} = turnCounter; + + el.setAttribute('disabled', ''); + + document + .querySelector(`button.end-turn:not([data-allegiance="${allegiance}"])`) + .removeAttribute('disabled'); + + if (update === '1') { + turnCounter.children.namedItem('count').textContent++ + turnCounter.dataset.update = '0'; + } else { + turnCounter.dataset.update = '1'; + } + + records + .sort((el1, el2) => el1.dataset.number > el2.dataset.number) + .forEach(el => el.classList.remove('movement-ended')); + + gameboard.endTurn(allegiance); + gameboard.select(records.at(0)); + }) +); + +document.querySelectorAll('.set-firing-arc').forEach(el => + el.addEventListener('click', gameboard.setFiringArc) +); + +document.querySelector('.set-grenade').addEventListener('click', gameboard.setGrenade); + +document.querySelectorAll('#toggle-firing-arc-vis input').forEach(el => + el.addEventListener('input', gameboard.toggleFiringArcVisibility) +); + +document.getElementById('toggle-prone-counter').addEventListener('input', function () { + const selected = recordSheet.getSelected(); + selected && gameboard.toggleProne(); }); +object.data = `${localStorage.getItem('map') || 'map1'}.svg`; + document .querySelector('#content input[type="checkbox"].visible') .addEventListener('input', function () { @@ -27,78 +103,27 @@ document }); }); -window.addEventListener('load', () => { - const svg = document.querySelector('object').contentDocument.querySelector('svg'); - - gameboard.start(svg); - panzoom.start(svg); - - gameboard.setDistanceCallback((count = '-') => { - distanceOutput.querySelector('#hex-count').textContent = count; - distanceOutput.style.display = count === '-' ? 'none' : 'block'; - }); - - gameboard.setProneFlagCallback(checked => proneToggle.checked = checked); - gameboard.setSelectCallback(data => recordSheet.select(data)); - - document.querySelectorAll('.soldier-record').forEach(el => - el.addEventListener('click', () => { - if (el.classList.contains('selected')) { - el.classList.remove('selected'); - gameboard.unSelect(); - recordSheet.unSelect(); - } else { - gameboard.select(el); - } - }) - ); - - document.querySelectorAll('.end-move').forEach(el => el.addEventListener('click', () => { - recordSheet.endMove(); - gameboard.endMove(); - })); - - document.querySelectorAll('.end-turn').forEach(el => - el.addEventListener('click', ({ target: { dataset: { allegiance }}}) => { - const dataSelector = `[data-allegiance="${allegiance}"]`, - records = Array.from(document.querySelectorAll(`.soldier-record${dataSelector}`)), - turnCounter = document.getElementById('turn-count'), - { dataset: { update }} = turnCounter; - - el.setAttribute('disabled', ''); - - document - .querySelector(`button.end-turn:not([data-allegiance="${allegiance}"])`) - .removeAttribute('disabled'); - - if (update === '1') { - turnCounter.children.namedItem('count').textContent++ - turnCounter.dataset.update = '0'; - } else { - turnCounter.dataset.update = '1'; - } - - records - .sort((el1, el2) => el1.dataset.number > el2.dataset.number) - .forEach(el => el.classList.remove('movement-ended')); - - gameboard.endTurn(allegiance); - gameboard.select(records.at(0)); - }) - ); +const showButton = document.getElementById('show-dialog'), + mapDialog = document.getElementById('map-dialog'), + selectEl = mapDialog.querySelector('select'), + confirmBtn = mapDialog.querySelector('#confirm-btn'); - document.querySelectorAll('.set-firing-arc').forEach(el => - el.addEventListener('click', gameboard.setFiringArc) - ); +mapDialog.querySelectorAll('option').forEach(option => + option.value === localStorage.getItem('map') && (option.selected = true) +); - document.querySelector('.set-grenade').addEventListener('click', gameboard.setGrenade); +showButton.addEventListener('click', () => { + mapDialog.showModal(); +}); - document.querySelectorAll('#toggle-firing-arc-vis input').forEach(el => - el.addEventListener('input', gameboard.toggleFiringArcVisibility) - ); +selectEl.addEventListener('change', () => { + confirmBtn.value = selectEl.value; +}); - document.getElementById('toggle-prone-counter').addEventListener('input', function () { - const selected = recordSheet.getSelected(); - selected && gameboard.toggleProne(); - }); +confirmBtn.addEventListener('click', e => { + e.preventDefault(); + localStorage.removeItem('pan-zoom'); + localStorage.setItem('map', selectEl.value); + document.querySelector('object').data = `${selectEl.value}.svg`; + mapDialog.close(); }); diff --git a/src/modules/gameboard.js b/src/modules/gameboard.js index 9f8723e..25db4ad 100644 --- a/src/modules/gameboard.js +++ b/src/modules/gameboard.js @@ -19,14 +19,14 @@ function getCellOccupant(cell) { } function getCells(svg) { - return svg.querySelectorAll('g[data-y] > g[data-x]'); + return svg.querySelectorAll('g.grid > g[data-y] > g[data-x]'); } function getLockedSightLine(svg) { return svg.querySelector('line.sight-line:not(.active)'); } -function getSightLine(svg) { +export function getSightLine(svg) { return svg.querySelector('line.sight-line'); } @@ -63,7 +63,7 @@ function getCellPosition(cell) { } function getCell(x, y) { - return svg.querySelector(`g[data-y="${y}"] > g[data-x="${x}"]`); + return svg.querySelector(`g.grid > g[data-y="${y}"] > g[data-x="${x}"]`); } function getCounterAtGridIndex(x, y) { @@ -85,7 +85,7 @@ function updateSightLine(cell) { { dataset: { x: tX }, parentElement: { dataset: { y: tY }}} = sightLine.getLockTarget(); const selector = sightLine.calcIndexes(+sX, +sY, +tX, +tY) - .map(([x, y]) => `g[data-y="${y}"] g[data-x="${x}"] use[href="#hex"]`) + .map(([x, y]) => `g.grid g[data-y="${y}"] g[data-x="${x}"] use[href="#hex"]`) .join(', '); const hexes = svg.querySelectorAll(selector); @@ -99,7 +99,7 @@ function drawSightLine(sourceCell, targetCell) { { dataset: { x: tX }, parentElement: { dataset: { y: tY }}} = targetCell; const selector = sightLine.calcIndexes(+sX, +sY, +tX, +tY) - .map(([x, y]) => `g[data-y="${y}"] g[data-x="${x}"] use[href="#hex"]`) + .map(([x, y]) => `g.grid g[data-y="${y}"] g[data-x="${x}"] use[href="#hex"]`) .join(', '); const hexes = svg.querySelectorAll(selector); @@ -277,9 +277,9 @@ export function start(el) { }); // debug - const c = soldier.getCounter(svg, { dataset: { allegiance: 'davion', number: '1' }}); - soldier.place(svg, c, getCell(17, 25)); - select(c); + // const c = soldier.getCounter(svg, { dataset: { allegiance: 'davion', number: '1' }}); + // soldier.place(svg, c, getCell(17, 25)); + // select(c); } export function select(selected) { diff --git a/src/modules/record_sheet.js b/src/modules/record_sheet.js index 99af18f..e5e8de6 100644 --- a/src/modules/record_sheet.js +++ b/src/modules/record_sheet.js @@ -14,9 +14,9 @@ export function getSelected() { export function select(data) { const selector = - `#record-sheet .soldier-record[data-number="${data.number}"][data-allegiance="${data.allegiance}"]` + `#record-sheet .soldier-record[data-number="${data.number}"][data-allegiance="${data.allegiance}"]` - unSelect(); + unSelect(); document.querySelector(selector).classList.add('selected'); document.getElementById('toggle-prone-counter').checked = data.prone; } |