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-04-26 10:37:36 -0700 |
---|---|---|
committer | Catalin Mititiuc <webdevcat@proton.me> | 2024-04-26 10:37:36 -0700 |
commit | 93ef00d18016786ae3491b14c4098c63590e18b9 (patch) | |
tree | b459b06eacb9fcecf24892ddfd995ce56a9c0d3d /src/modules/game | |
parent | b4898daa2f20625890dd5802273b15e8cdbdbbc9 (diff) |
More refactor of firing arcs module
Diffstat (limited to 'src/modules/game')
-rw-r--r-- | src/modules/game/firingArc.js | 137 |
1 files changed, 75 insertions, 62 deletions
diff --git a/src/modules/game/firingArc.js b/src/modules/game/firingArc.js index e37d2a8..cbde0d4 100644 --- a/src/modules/game/firingArc.js +++ b/src/modules/game/firingArc.js @@ -19,7 +19,7 @@ const svgns = "http://www.w3.org/2000/svg", liao: false }, - clippedFiringArcRadius = 100; + clippedFiringArcRadius = 25; let svg; @@ -38,7 +38,7 @@ function calculateAngle(xDiff, yDiff) { return angle; } -function edgePoint([x1, y1], [x2, y2], { minX = 0, minY = 0, maxX, maxY } = {}) { +function edgePoint({ x: x1, y: y1 }, { x: x2, y: y2 }, { x: [minX, maxX], y: [minY, maxY] }) { const xDiff = x2 - x1, yDiff = y2 - y1, xIntercept = y => (y - y1) * xDiff / yDiff + x1, @@ -64,50 +64,69 @@ function edgePoint([x1, y1], [x2, y2], { minX = 0, minY = 0, maxX, maxY } = {}) pointCoords = y <= maxY ? [minX, y] : [xIntercept(maxY), maxY]; } - return pointCoords; + return new Point(...pointCoords); } -function position(activeFiringArc, activeFiringArcOutline, aim, grid, e) { +class Point { + constructor(x = 0, y = 0) { + this.x = +x; + this.y = +y; + } + + toString() { + return `${this.x},${this.y}`; + } +} + +function position(e, firingArc, firingArcOutline, aimLine, grid) { // TODO: handle exactly horizontal and vertical lines? - let pt = new DOMPoint(e.clientX, e.clientY); - let { x: pointerX, y: pointerY } = pt.matrixTransform(grid.getScreenCTM().inverse()); + let pointer = new DOMPoint(e.clientX, e.clientY); + let pointerPt = pointer.matrixTransform(grid.getScreenCTM().inverse()); + + let pivotPt = new Point(aimLine.getAttribute('x1'), aimLine.getAttribute('y1')); - let pivotPt = [x1, y1] = [aim.getAttribute('x1'), aim.getAttribute('y1')].map(n => parseFloat(n)); let { x, y, width, height } = grid.getBBox(); - let [minX, minY, maxX, maxY] = [x, y, x + width, y + height]; - let bounds = { minX, minY, maxX, maxY }; - let cornerPoints = [[x, y], [x + width, y], [x + width, y + height], [x, y + height]]; - let aimPt = [x2, y2] = edgePoint(pivotPt, [pointerX, pointerY], bounds); + const bounds = { + x: [x, x + width], + y: [y, y + height] + } + + let cornerPoints = [ + [x, y], + [x + width, y], + [x + width, y + height], + [x, y + height] + ].map(([x, y]) => new Point(x, y)); - aim.setAttributeNS(null, 'x2', x2); - aim.setAttributeNS(null, 'y2', y2); + let aimPt = edgePoint(pivotPt, pointerPt, bounds); + aimLine.setAttributeNS(null, 'x2', aimPt.x); + aimLine.setAttributeNS(null, 'y2', aimPt.y); - let angle = calculateAngle(x2 - x1, y2 - y1); - let arcAngle = arcSize[activeFiringArc.dataset.size]; - let distance = Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2); + let angle = calculateAngle(aimPt.x - pivotPt.x, aimPt.y - pivotPt.y); + let arcAngle = arcSize[firingArc.dataset.size]; + let distance = Math.sqrt((aimPt.x - pivotPt.x) ** 2 + (aimPt.y - pivotPt.y) ** 2); let yDelta = distance * Math.cos(angle) * Math.tan(arcAngle); let xDelta = distance * Math.sin(angle) * Math.tan(arcAngle); - let [newY1, newX1] = [y2 - yDelta, x2 - xDelta]; - let [newY2, newX2] = [y2 + yDelta, x2 + xDelta]; + let arcPt1 = new Point(aimPt.x - xDelta, aimPt.y - yDelta); + let arcPt2 = new Point(aimPt.x + xDelta, aimPt.y + yDelta); - let arcPt1 = [newX1, newY1] = edgePoint(pivotPt, [newX1, newY1], bounds); - let arcPt2 = [newX2, newY2] = edgePoint(pivotPt, [newX2, newY2], bounds); + arcPt1 = edgePoint(pivotPt, arcPt1, bounds); + arcPt2 = edgePoint(pivotPt, arcPt2, bounds); - activeFiringArcOutline.setAttributeNS(null, 'points', `${newX1},${newY1} ${x1},${y1} ${newX2},${newY2}`); + firingArcOutline.setAttributeNS(null, 'points', `${arcPt1} ${pivotPt} ${arcPt2}`); - function touchSameEdge([x1, y1], [x2, y2]) { + function touchSameEdge({ x: x1, y: y1 }, { x: x2, y: y2 }) { return x1 === x2 || y1 === y2; } - function touchOrthogonalEdges([x1, y1], [x2, y2]) { - let xLimit = [minX, maxX], yLimit = [minY, maxY]; - return (xLimit.includes(x1) && yLimit.includes(y2)) || (yLimit.includes(y1) && xLimit.includes(x2)); + function touchOrthogonalEdges({ x: x1, y: y1 }, { x: x2, y: y2 }) { + return (bounds.x.includes(x1) && bounds.y.includes(y2)) || (bounds.y.includes(y1) && bounds.x.includes(x2)); } - function shareValue([x1, y1], [x2, y2]) { + function shareValue({ x: x1, y: y1 }, { x: x2, y: y2 }) { return x1 === x2 || y1 === y2; } @@ -115,25 +134,24 @@ function position(activeFiringArc, activeFiringArcOutline, aim, grid, e) { // if they share an x value, we will look for corner y value // if they share a y value, we will look for corner x value // is aim pt non-shared value greater or less than arcpt non-shared value? - function findSharedValue(pt, ...pts) { - const sharedValPt = pts.find(([ptX, ptY]) => pt.includes(ptX) || pt.includes(ptY)); + function findWhichCorners(pt, ...pts) { + const ptVals = Object.values(pt), + sharedValPt = pts.find(({ x, y }) => ptVals.includes(x) || ptVals.includes(y)); if (!sharedValPt) { return; } - const limits = [[minX, maxX], [minY, maxY]], - nonSharedValIndex = pt[0] === sharedValPt[0] ? 1 : 0; - + const nonSharedValKey = pt.x === sharedValPt.x ? 'x' : 'y'; let cornerVal; - if (pt[nonSharedValIndex] < sharedValPt[nonSharedValIndex]) { - cornerVal = Math.min(...limits[nonSharedValIndex]) + if (pt[nonSharedValKey] < sharedValPt[nonSharedValKey]) { + cornerVal = Math.min(...bounds[nonSharedValKey]); } else { - cornerVal = Math.max(...limits[nonSharedValIndex]); + cornerVal = Math.max(...bounds[nonSharedValKey]); } - return cornerPoints.filter(cp => cp[nonSharedValIndex] === cornerVal); + return cornerPoints.filter(cp => cp[nonSharedValKey] === cornerVal); } let points; @@ -155,7 +173,7 @@ function position(activeFiringArc, activeFiringArcOutline, aim, grid, e) { } else { if (touchSameEdge(aimPt, arcPt1) || touchSameEdge(aimPt, arcPt2)) { // 2-corner case, aim and an arc point touch the same edge - points = findSharedValue(aimPt, arcPt1, arcPt2); + points = findWhichCorners(aimPt, arcPt1, arcPt2); let index = points.findIndex(cp => shareValue(cp, arcPt2)); points.splice(index + 1, 0, arcPt2, pivotPt, arcPt1); } else { @@ -166,7 +184,7 @@ function position(activeFiringArc, activeFiringArcOutline, aim, grid, e) { } } - activeFiringArc.setAttributeNS(null, 'points', points.join(' ')); + firingArc.setAttributeNS(null, 'points', points.join(' ')); } function setDataAttrs({ dataset: { allegiance, number }}, el) { @@ -179,7 +197,7 @@ function getClipPathId({ dataset: { allegiance, number }}) { } function getUnclipped() { - return svg.querySelectorAll('#firing-arcs polygon:not([clip-path])'); + return svg.querySelectorAll('#firing-arcs :not([clip-path])'); }; export default function (el) { @@ -192,25 +210,23 @@ export default function (el) { let outlineLayer = svg.querySelector('#lines'); let arcContainer = svg.querySelector('#firing-arcs'); let grid = svg.querySelector('.grid'); - let points = `${x},${y} ${x},${y} ${x},${y}`; - let aim = document.createElementNS(svgns, 'line'); - aim.setAttributeNS(null, 'x1', x); - aim.setAttributeNS(null, 'y1', y); - aim.setAttributeNS(null, 'x2', x); - aim.setAttributeNS(null, 'y2', y); - outlineLayer.appendChild(aim); + let aimLine = document.createElementNS(svgns, 'line'); + aimLine.setAttributeNS(null, 'x1', x); + aimLine.setAttributeNS(null, 'y1', y); + aimLine.setAttributeNS(null, 'x2', x); + aimLine.setAttributeNS(null, 'y2', y); + outlineLayer.appendChild(aimLine); let firingArc = document.createElementNS(svgns, 'polygon'); - let firingArcOutline = document.createElementNS(svgns, 'polyline'); - setDataAttrs(counter, firingArc); + firingArc.setAttributeNS(null, 'points', `${x},${y}`); firingArc.dataset.size = size; firingArc.classList.add('firing-arc', 'active'); - firingArc.setAttributeNS(null, 'points', points); + let firingArcOutline = document.createElementNS(svgns, 'polyline'); setDataAttrs(counter, firingArcOutline); - firingArcOutline.setAttributeNS(null, 'points', points); + firingArcOutline.setAttributeNS(null, 'points', `${x},${y}`); let clipShape = document.createElementNS(svgns, 'circle'); clipShape.setAttributeNS(null, 'cx', x); @@ -226,31 +242,29 @@ export default function (el) { arcLayer.appendChild(firingArc); outlineLayer.appendChild(firingArcOutline); - const positionListener = position.bind(svg, firingArc, firingArcOutline, aim, grid); + // const positionListener = position.bind(svg, firingArc, firingArcOutline, aim, grid); + function positionListener(e) { + position(e, firingArc, firingArcOutline, aimLine, grid); + } - let firingArcPlacementListener = e => { - svg.querySelectorAll('.firing-arc.active').forEach(el => el.classList.remove('active')); + let placementListener = e => { grid.removeAttribute('style'); + aimLine.remove(); + svg.querySelectorAll('.firing-arc.active').forEach(el => el.classList.remove('active')); svg.removeEventListener('mousemove', positionListener); - firingArc.removeEventListener('click', firingArcPlacementListener); - firingArc.removeEventListener('contextmenu', cancelFiringArcPlacement); }; - let cancelFiringArcPlacement = e => { + let cancelPlacementListener = e => { e.preventDefault(); - firingArc.removeEventListener('click', firingArcPlacementListener); - firingArc.removeEventListener('contextmenu', cancelFiringArcPlacement); - this.get(counter).forEach(fa => fa.remove()); - grid.removeAttribute('style'); svg.removeEventListener('mousemove', positionListener); }; grid.style.pointerEvents = 'none'; - firingArc.addEventListener('click', firingArcPlacementListener); - firingArc.addEventListener('contextmenu', cancelFiringArcPlacement); + firingArc.addEventListener('click', placementListener, { once: true }); + firingArc.addEventListener('contextmenu', cancelPlacementListener, { once: true }); svg.addEventListener('mousemove', positionListener); }; @@ -281,7 +295,6 @@ export default function (el) { }; this.clipAll = function () { - console.log('clipall') let unclipped = getUnclipped(); unclipped.forEach(el => { |