index : btroops | |
Virtual board game-aid for BattleTroops, an infantry combat simulator wargame published by FASA in 1989. |
aboutsummaryrefslogtreecommitdiff |
diff options
author | Catalin Mititiuc <webdevcat@proton.me> | 2024-08-08 13:45:24 -0700 |
---|---|---|
committer | Catalin Mititiuc <webdevcat@proton.me> | 2024-08-08 13:45:24 -0700 |
commit | 0b27586a7f3c45016cab169661d914154a4d81ef (patch) | |
tree | e182e2c39e714737b0ab53b853a1986dfaead89b | |
parent | 9d76272f7b57f9d5271b3c5e2c6d0683ff80d019 (diff) |
WIP: auto-pan map to selected counter
-rw-r--r-- | node_modules/pan-zoom/index.js | 2 | ||||
-rw-r--r-- | node_modules/pan-zoom/src/modules/pan.js | 86 | ||||
-rw-r--r-- | src/modules/gameboard.js | 53 | ||||
-rw-r--r-- | src/modules/pan-zoom.js | 3 |
4 files changed, 123 insertions, 21 deletions
diff --git a/node_modules/pan-zoom/index.js b/node_modules/pan-zoom/index.js new file mode 100644 index 0000000..ab80f47 --- /dev/null +++ b/node_modules/pan-zoom/index.js @@ -0,0 +1,2 @@ +export { default as pan, manualPan } from './src/modules/pan.js'; +export { default as zoom } from './src/modules/zoom.js'; diff --git a/node_modules/pan-zoom/src/modules/pan.js b/node_modules/pan-zoom/src/modules/pan.js new file mode 100644 index 0000000..844524d --- /dev/null +++ b/node_modules/pan-zoom/src/modules/pan.js @@ -0,0 +1,86 @@ +import getComputedTransformMatrix from './utils.js'; + +const minDistanceThreshold = 5; + +function distanceBetween({ x: x1, y: y1 }, { x: x2, y: y2 }) { + return Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2); +} + +function minDistanceThresholdIsMet(startPt, endPt) { + return distanceBetween(startPt, endPt) >= minDistanceThreshold; +} + +function stopEventPropagationToChildren(el, type) { + el.addEventListener(type, e => e.stopPropagation(), { capture: true, once: true }); +} + +function getTranslateMatrix(startPt, movePt) { + const translateMatrix = new DOMMatrix(); + + return translateMatrix.translate(movePt.x - startPt.x, movePt.y - startPt.y); +} + +export function manualPan(gb, counter) { + const mtx = getComputedTransformMatrix(gb), + inverseScreenCTM = gb.getScreenCTM().inverse(); + + const counterRect = counter.getBoundingClientRect(); + const mapRect = document.querySelector('object').getBoundingClientRect(); + + let startPt = new DOMPoint(counterRect.x, counterRect.y); + let movePt = new DOMPoint(mapRect.width / 2, mapRect.height / 2); + + startPt = startPt.matrixTransform(inverseScreenCTM); + movePt = movePt.matrixTransform(inverseScreenCTM); + + gb.style.transition = 'transform 0.5s'; + gb.style.transform = mtx.multiply(getTranslateMatrix(startPt, movePt)); + gb.addEventListener('transitionend', () => gb.style.transition = ''); +} + +export default function (svg, el, e) { + e.preventDefault(); + + const mtx = getComputedTransformMatrix(el), + inverseScreenCTM = el.getScreenCTM().inverse(); + + let startPt = new DOMPoint(e.clientX, e.clientY), + movePt = new DOMPoint(), + isPanning = false; + + function pointerMove(e) { + movePt.x = e.clientX; + movePt.y = e.clientY; + + if (!isPanning && minDistanceThresholdIsMet(startPt, movePt)) { + isPanning = true; + //e.target.setPointerCapture(e.pointerId); + + startPt.x = e.clientX; + startPt.y = e.clientY; + startPt = startPt.matrixTransform(inverseScreenCTM); + + stopEventPropagationToChildren(el, 'click'); + } + + if (isPanning) { + movePt.x = e.clientX; + movePt.y = e.clientY; + movePt = movePt.matrixTransform(inverseScreenCTM); + + el.style.transform = mtx.multiply(getTranslateMatrix(startPt, movePt)); + } + } + + svg.addEventListener('pointermove', pointerMove); + + svg.addEventListener( + 'pointerup', + () => { + svg.removeEventListener('pointermove', pointerMove); + const t1Rect = t1.getBoundingClientRect(); + console.log('trooper1 DOM x,y', t1Rect.x, t1Rect.y); + }, + { once: true } + ); +} diff --git a/src/modules/gameboard.js b/src/modules/gameboard.js index b28f188..e8fe575 100644 --- a/src/modules/gameboard.js +++ b/src/modules/gameboard.js @@ -1,15 +1,14 @@ import * as firingArc from './game/firing_arc.js'; import * as sightLine from './game/sight_line.js'; import * as soldier from './game/soldier.js'; -import { Observable } from "./observable"; +import { Observable } from './observable'; + + +import { manualPan } from 'pan-zoom'; let svg, placing = []; -const top = { - collection: new Map() -}; - const frontmostStore = new Map(); function getCellContents(cell) { @@ -194,6 +193,11 @@ function select(data) { if (isSelected || !data) return; counter.classList.add(soldier.getSelectedClass()); + const gb = svg.querySelector('.gameboard'); + + // TODO check if counter is on board before calling + manualPan(gb, counter); + firingArc.get(svg, counter).forEach(el => el.removeAttribute('clip-path')); placing.push(counter); } @@ -215,8 +219,8 @@ export function start(el) { // For when the pointer leaves the window document.querySelector('object').addEventListener('pointerout', e => { if (clearHexDialog.open) return; - console.log('object pointerout'); - console.log('Left map... CLEARING HOVERS'); + //console.log('object pointerout'); + //console.log('Left map... CLEARING HOVERS'); svg.querySelectorAll('.hover').forEach(el => el.classList.remove('hover')); [...frontmost.children].forEach(child => { @@ -236,7 +240,7 @@ export function start(el) { // Pointer moves outside the edge of the grid if (!targetCell) { - console.log('No target cell... CLEARING HOVERS'); + //console.log('No target cell... CLEARING HOVERS'); svg.querySelectorAll('.hover').forEach(el => el.classList.remove('hover')); [...frontmost.children].forEach(child => { @@ -257,7 +261,7 @@ export function start(el) { // 's contents that is in frontmost, whose parent cell is not already highlighted !(targetCell.classList.contains('frontmost') && frontmostStore.get(e.target.closest('.frontmost > *')).classList.contains('hover')) ].every(e => e)) { - console.log('Target cell missing hover... CLEARING HOVERS AND ADDING TO TARGET CELL'); + //console.log('Target cell missing hover... CLEARING HOVERS AND ADDING TO TARGET CELL'); svg.querySelectorAll('.hover').forEach(el => el.classList.remove('hover')); if (placing[0]?.getAttributeNS(null, 'class') === 'mech-template') { @@ -299,7 +303,7 @@ export function start(el) { getActiveSightLine(svg) && clearSightLine(); } - console.log('frontmost contents', frontmost.children); + //console.log('frontmost contents', frontmost.children); }); grid.addEventListener('click', clickHandler); @@ -378,17 +382,24 @@ export function start(el) { //cell2.classList.add('hover'); // - //soldier.place( - // svg, - // soldier.createCounter({ dataset: { allegiance: 'attacker', number: 2, squad: 1 }}, 'hsplaser'), - // getCell(-2, 3, -1, 0) - //); - // - //soldier.place( - // svg, - // soldier.createCounter({ dataset: { allegiance: 'attacker', number: 2, squad: 5 }}, 'rifle'), - // getCell(-3, 3, 0, 0) - //); + const trooper1 = soldier.createCounter({ dataset: { allegiance: 'attacker', number: 1, squad: 1 }}, 'blazer'); + + + soldier.place(svg, trooper1, getCell(-3, 9, -6, 0)); + + soldier.place( + svg, + soldier.createCounter({ dataset: { allegiance: 'defender', number: 1, squad: 1 }}, 'blazer'), + getCell(1, -8, 7, 0) + ); + + const t1Rect = trooper1.getBoundingClientRect(); + + setTimeout(() => { + const t1Rect = trooper1.getBoundingClientRect(); + console.log('trooper', trooper1, trooper1.getBoundingClientRect()); + + }, 10); // Add some counters in an unoccupied cell //const countersCell = getCell(-1, 1, 0, 0); diff --git a/src/modules/pan-zoom.js b/src/modules/pan-zoom.js index d13c90f..5e224e8 100644 --- a/src/modules/pan-zoom.js +++ b/src/modules/pan-zoom.js @@ -19,6 +19,9 @@ function addEventListeners(svg, el) { pan(svg, el, e), { passive: false }; } }); + svg.addEventListener('pointermove', e => { + console.log('clientX,clientY', `${e.clientX},${e.clientY}`); + }); } function storePanZoomVal(transformMatrix) { |