index : btroops | |
Virtual board game-aid for BattleTroops, an infantry combat simulator wargame published by FASA in 1989. |
aboutsummaryrefslogtreecommitdiff |
diff options
Diffstat (limited to 'src/modules')
-rw-r--r-- | src/modules/game.js | 8 | ||||
-rw-r--r-- | src/modules/game/counter.js (renamed from src/modules/counter.js) | 0 | ||||
-rw-r--r-- | src/modules/game/firingArc.js (renamed from src/modules/firingArc.js) | 140 | ||||
-rw-r--r-- | src/modules/game/sightLine.js (renamed from src/modules/sightLine.js) | 0 | ||||
-rw-r--r-- | src/modules/panzoom.js | 51 |
5 files changed, 100 insertions, 99 deletions
diff --git a/src/modules/game.js b/src/modules/game.js index f9ce991..c22dc9d 100644 --- a/src/modules/game.js +++ b/src/modules/game.js @@ -1,6 +1,6 @@ -import FiringArc from './firingArc.js'; -import SightLine from './sightLine.js'; -import Counter from './counter.js'; +import firingArc from './game/firingArc.js'; +import SightLine from './game/sightLine.js'; +import Counter from './game/counter.js'; const svgns = "http://www.w3.org/2000/svg"; @@ -10,7 +10,7 @@ export default class Game { constructor(svg) { this.svg = svg; - this.firingArc = new FiringArc(svg); + this.firingArc = new firingArc(svg); this.sightLine = new SightLine(svg); this.counter = new Counter(svg); diff --git a/src/modules/counter.js b/src/modules/game/counter.js index 69552d8..69552d8 100644 --- a/src/modules/counter.js +++ b/src/modules/game/counter.js diff --git a/src/modules/firingArc.js b/src/modules/game/firingArc.js index 5e5d84a..d2f18a5 100644 --- a/src/modules/firingArc.js +++ b/src/modules/game/firingArc.js @@ -1,11 +1,25 @@ -const HORZ_POINT_DISTANCE = 1.005, - VERT_POINT_DISTANCE = Math.sqrt(3) * HORZ_POINT_DISTANCE / 2, +// https://www.redblobgames.com/grids/hexagons/ - FIRING_ARC_SIZE = { - 'small': Math.atan(HORZ_POINT_DISTANCE / (6 * VERT_POINT_DISTANCE)), - 'medium': Math.atan((HORZ_POINT_DISTANCE / 2) / VERT_POINT_DISTANCE), - 'large': Math.atan((21 * HORZ_POINT_DISTANCE) / (6 * VERT_POINT_DISTANCE)) -}; +// Horizontal distance between hex centers is sqrt(3) * size. The vertical +// distance is 3 / 2 * size. When we calculate horzDist / vertDist, the size +// cancels out, leaving us with a unitless ratio of sqrt(3) / (3 / 2), or +// 2 * sqrt(3) / 3. + +const svgns = "http://www.w3.org/2000/svg", + horzToVertDistRatio = 2 * Math.sqrt(3) / 3, + + arcSize = { + 'small': Math.atan(horzToVertDistRatio / 6), + 'medium': Math.atan(horzToVertDistRatio / 2), + 'large': Math.atan(7 * horzToVertDistRatio / 2) + }, + + firingArcVisibility = { + davion: false, + liao: false + }; + +let svg; function calculateAngle(xDiff, yDiff) { yDiff = -yDiff; @@ -73,7 +87,7 @@ function position(e) { let yDiff = y2px - y1px; let angle = calculateAngle(xDiff, yDiff); - let arcAngle = FIRING_ARC_SIZE[activeFiringArc.dataset.size]; + let arcAngle = arcSize[activeFiringArc.dataset.size]; let distance = Math.sqrt((x2px - x1px) ** 2 + (y2px - y1px) ** 2); let yDelta = distance * Math.cos(angle) * Math.tan(arcAngle); let xDelta = distance * Math.sin(angle) * Math.tan(arcAngle); @@ -164,30 +178,30 @@ function position(e) { } } -export default class FiringArc { - #firingArcVisibility = { - davion: false, - liao: false - }; +function setDataAttrs({ dataset: { allegiance, number }}, el) { + el.dataset.allegiance = allegiance; + el.dataset.number = number; +} - constructor(svg) { - this.svg = svg; - } +function getClipPathId({ dataset: { allegiance, number }}) { + return `clip-path-${allegiance}-${number}`; +} - set(size, { dataset: { allegiance, number }}, { x, y }) { - const svgns = "http://www.w3.org/2000/svg"; +function getUnclipped() { + return svg.querySelectorAll('#firing-arcs polygon:not([clip-path])'); +}; - let existingArcs = this.svg.querySelectorAll( - `#firing-arcs [data-number="${number}"][data-allegiance="${allegiance}"]` - ); +export default function (el) { + svg = el; - existingArcs.forEach(el => el.remove()); + this.set = function (size, counter, { x, y }) { + this.get(counter).forEach(fa => fa.remove()); - let arcLayer = this.svg.querySelector('#shapes'); - let outlineLayer = this.svg.querySelector('#lines'); - let arcContainer = this.svg.querySelector('#firing-arcs'); + let arcLayer = svg.querySelector('#shapes'); + let outlineLayer = svg.querySelector('#lines'); + let arcContainer = svg.querySelector('#firing-arcs'); - let grid = this.svg.querySelector('.board'); + let grid = svg.querySelector('.board'); const transform = getComputedStyle(grid).transform.match(/-?\d+\.?\d*/g); const pt = new DOMPoint(x, y); const mtx = new DOMMatrix(transform); @@ -197,14 +211,12 @@ export default class FiringArc { let firingArc = document.createElementNS(svgns, 'polygon'); let firingArcOutline = document.createElementNS(svgns, 'polygon'); - firingArc.classList.add('firing-arc', 'active'); - firingArc.dataset.number = number; - firingArc.dataset.allegiance = allegiance; + setDataAttrs(counter, firingArc); firingArc.dataset.size = size; + firingArc.classList.add('firing-arc', 'active'); firingArc.setAttributeNS(null, 'points', `${pivotPoint} ${pivotPoint} ${pivotPoint}`); - firingArcOutline.dataset.number = number; - firingArcOutline.dataset.allegiance = allegiance; + setDataAttrs(counter, firingArcOutline); firingArcOutline.setAttributeNS(null, 'points', `${pivotPoint} ${pivotPoint} ${pivotPoint}`); let clipShape = document.createElementNS(svgns, 'circle'); @@ -213,9 +225,8 @@ export default class FiringArc { clipShape.setAttributeNS(null, 'r', 100); let clipPath = document.createElementNS(svgns, 'clipPath'); - clipPath.setAttributeNS(null, 'id', `clip-path-${allegiance}-${number}`); - clipPath.dataset.number = number; - clipPath.dataset.allegiance = allegiance; + setDataAttrs(counter, clipPath); + clipPath.setAttributeNS(null, 'id', getClipPathId(counter)); clipPath.appendChild(clipShape); arcContainer.appendChild(clipPath); @@ -223,9 +234,9 @@ export default class FiringArc { outlineLayer.appendChild(firingArcOutline); let firingArcPlacementListener = e => { - this.svg.querySelectorAll('.firing-arc.active').forEach(el => el.classList.remove('active')); + svg.querySelectorAll('.firing-arc.active').forEach(el => el.classList.remove('active')); grid.removeAttribute('style'); - this.svg.removeEventListener('mousemove', position); + svg.removeEventListener('mousemove', position); firingArc.removeEventListener('click', firingArcPlacementListener); firingArc.removeEventListener('contextmenu', cancelFiringArcPlacement); }; @@ -236,65 +247,58 @@ export default class FiringArc { firingArc.removeEventListener('click', firingArcPlacementListener); firingArc.removeEventListener('contextmenu', cancelFiringArcPlacement); - let existingArcs = this.svg.querySelectorAll( - `#firing-arcs [data-number="${number}"][data-allegiance="${allegiance}"]` - ); - - existingArcs.forEach(el => el.remove()); + this.get(counter).forEach(fa => fa.remove()); grid.removeAttribute('style'); - this.svg.removeEventListener('mousemove', position); + svg.removeEventListener('mousemove', position); }; grid.style.pointerEvents = 'none'; - this.svg.addEventListener('mousemove', position); + svg.addEventListener('mousemove', position); firingArc.addEventListener('click', firingArcPlacementListener); firingArc.addEventListener('contextmenu', cancelFiringArcPlacement); - } + }; - clear(allegiance) { + this.clear = function (allegiance) { const selector = `#firing-arcs [data-allegiance="${allegiance}"]`; - this.svg.querySelectorAll(selector).forEach(el => el.remove()); - } - - get({ dataset: { allegiance, number }}) { - return this.svg.querySelectorAll(`#firing-arcs polygon[data-number="${number}"][data-allegiance="${allegiance}"]`); - } + svg.querySelectorAll(selector).forEach(el => el.remove()); + }; - getUnclipped() { - return this.svg.querySelectorAll('#firing-arcs polygon:not([clip-path])'); - } + this.get = function ({ dataset: { allegiance, number }}) { + return svg.querySelectorAll(`#firing-arcs polygon[data-number="${number}"][data-allegiance="${allegiance}"]`); + }; - toggleVisibility(allegiance) { - const vis = this.#firingArcVisibility[allegiance], - clipPaths = this.svg.querySelectorAll(`clipPath[data-allegiance="${allegiance}"]`); + this.toggleVisibility = function (allegiance) { + const vis = firingArcVisibility[allegiance], + clipPaths = svg.querySelectorAll(`clipPath[data-allegiance="${allegiance}"]`); clipPaths.forEach(cp => cp.style.display = !vis ? 'none' : ''); - this.#firingArcVisibility[allegiance] = !vis; - } + firingArcVisibility[allegiance] = !vis; + }; - toggleCounterVisibility({ dataset: { number, allegiance }}, vis) { - const cp = this.svg.querySelector(`#clip-path-${allegiance}-${number}`), + this.toggleCounterVisibility = function ({ dataset: { number, allegiance }}, vis) { + const cp = svg.querySelector(`#clip-path-${allegiance}-${number}`), display = vis ? 'none' : ''; if (cp) { - cp.style.display = this.#firingArcVisibility[allegiance] ? 'none' : display; + cp.style.display = firingArcVisibility[allegiance] ? 'none' : display; } - } + }; - clipAll() { - let unclipped = this.getUnclipped(); + this.clipAll = function () { + console.log('clipall') + let unclipped = getUnclipped(); unclipped.forEach(el => { const { number, allegiance } = el.dataset, clipPathId = `clip-path-${allegiance}-${number}`, - isVisible = this.#firingArcVisibility[allegiance]; + isVisible = firingArcVisibility[allegiance]; if (isVisible) { - this.svg.querySelector(`#${clipPathId}`).style.display = 'none'; + svg.querySelector(`#${clipPathId}`).style.display = 'none'; } el.setAttributeNS(null, 'clip-path', `url(#${clipPathId})`); }); - } + }; } diff --git a/src/modules/sightLine.js b/src/modules/game/sightLine.js index 4790bd8..4790bd8 100644 --- a/src/modules/sightLine.js +++ b/src/modules/game/sightLine.js diff --git a/src/modules/panzoom.js b/src/modules/panzoom.js index ae95171..dcc6b34 100644 --- a/src/modules/panzoom.js +++ b/src/modules/panzoom.js @@ -1,36 +1,33 @@ import { pan, zoom } from 'pan-zoom'; -export default class PanZoom { - #storageKey = 'pan-zoom'; - #zoomFactor = 0.25; - - constructor(svg) { - this.#restorePanZoomVal(svg); - this.#addEventListeners(svg); - this.#observePanZoomChanges(svg); - } +const storageKey = 'pan-zoom', + zoomFactor = 0.25; + +function restorePanZoomVal(svg) { + const storedPanZoomVal = localStorage.getItem(storageKey); - #storePanZoomVal(transformMatrix) { - localStorage.setItem(this.#storageKey, transformMatrix); + if (storedPanZoomVal) { + svg.style.transform = storedPanZoomVal; } +} - #observePanZoomChanges(svg) { - const observer = - new MutationObserver(() => this.#storePanZoomVal(svg.style.transform)); +function addEventListeners(svg) { + svg.addEventListener('wheel', e => zoom(svg, e, zoomFactor), { passive: false }); + svg.addEventListener('pointerdown', e => pan(svg, e), { passive: false }); +} - observer.observe(svg, { attributeFilter: ['style'] }); - } +function storePanZoomVal(transformMatrix) { + localStorage.setItem(storageKey, transformMatrix); +} - #restorePanZoomVal(svg) { - const storedPanZoomVal = localStorage.getItem(this.#storageKey); +function observePanZoomChanges(svg) { + const observer = new MutationObserver(() => storePanZoomVal(svg.style.transform)); - if (storedPanZoomVal) { - svg.style.transform = storedPanZoomVal; - } - } + observer.observe(svg, { attributeFilter: ['style'] }); +} - #addEventListeners(svg) { - svg.addEventListener('wheel', e => zoom(svg, e, this.#zoomFactor), { passive: false }); - svg.addEventListener('pointerdown', e => pan(svg, e), { passive: false }); - } -}; +export default function (svg) { + restorePanZoomVal(svg); + addEventListeners(svg); + observePanZoomChanges(svg); +} |