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/game/firingArc.js')
-rw-r--r-- | src/modules/game/firingArc.js | 305 |
1 files changed, 75 insertions, 230 deletions
diff --git a/src/modules/game/firingArc.js b/src/modules/game/firingArc.js index adfb580..fb39aba 100644 --- a/src/modules/game/firingArc.js +++ b/src/modules/game/firingArc.js @@ -17,7 +17,9 @@ const svgns = "http://www.w3.org/2000/svg", firingArcVisibility = { davion: false, liao: false - }; + }, + + clippedFiringArcRadius = 100; let svg; @@ -37,12 +39,13 @@ function calculateAngle(xDiff, yDiff) { } function edgePoint(x1, y1, x2, y2, maxX, maxY, minX = 0, minY = 0) { - let pointCoords, - xDiff = x2 - x1, + const xDiff = x2 - x1, yDiff = y2 - y1, xIntercept = y => (y - y1) * xDiff / yDiff + x1, yIntercept = x => (x - x1) * yDiff / xDiff + y1; + let pointCoords; + if (xDiff > 0 && yDiff > 0) { let x = xIntercept(maxY); @@ -67,21 +70,28 @@ function edgePoint(x1, y1, x2, y2, maxX, maxY, minX = 0, minY = 0) { let cornerPoints; function position(e) { - // let activeFiringArc = this.querySelector('polygon.firing-arc.active'); - let activeFiringArc; - let size = this.querySelector('polygon.firing-arc.active').dataset.size; + // TODO: handle exactly horizontal and vertical lines? - // TODO: handle exactly horizontal and vertical lines + let arcLayer = this.querySelector('#shapes'); + const activeFiringArc = arcLayer.querySelector('polygon.firing-arc.active'); - let grid = this.querySelector('.grid'); - let testAim = this.querySelector('#test-arcs line'); - let testOutline = this.querySelector('#test-arcs polyline'); - let testArea = this.querySelector('#test-arcs polygon'); + if (!activeFiringArc) { + return; + } + + let outlineLayer = this.querySelector('#lines'); + const { dataset: { allegiance, number }} = activeFiringArc, + outlineSelector = `polyline[data-number="${number}"][data-allegiance="${allegiance}"]`, + activeFiringArcOutline = outlineLayer.querySelector(outlineSelector); + + let grid = this.querySelector('.grid'); + let aim = outlineLayer.querySelector('line'); let pt = new DOMPoint(e.clientX, e.clientY); let { x, y } = pt.matrixTransform(grid.getScreenCTM().inverse()); - let pivotPt = [x1, y1] = [testAim.getAttribute('x1'), testAim.getAttribute('y1')].map(n => parseFloat(n)); + let pivotPt = [x1, y1] = [aim.getAttribute('x1'), aim.getAttribute('y1')].map(n => parseFloat(n)); + let [maxX, maxY, minX, minY] = [ grid.getBBox().x + grid.getBBox().width, grid.getBBox().y + grid.getBBox().height, @@ -89,20 +99,14 @@ function position(e) { grid.getBBox().y ].map(n => parseFloat(n)); - let edgePointArgs = [testAim.getAttribute('x1'), testAim.getAttribute('y1'), x, y, maxX, maxY, minX, minY].map(n => parseFloat(n)) - + let edgePointArgs = [aim.getAttribute('x1'), aim.getAttribute('y1'), x, y, maxX, maxY, minX, minY].map(n => parseFloat(n)) let aimPt = [x2, y2] = edgePoint(...edgePointArgs); - testAim.setAttributeNS(null, 'x2', x2); - testAim.setAttributeNS(null, 'y2', y2); - - // console.log(testAim); - + aim.setAttributeNS(null, 'x2', x2); + aim.setAttributeNS(null, 'y2', y2); let angle = calculateAngle(x2 - x1, y2 - y1); - - let arcAngle = arcSize[size]; - // let arcAngle = Math.PI / 4; + let arcAngle = arcSize[activeFiringArc.dataset.size]; let distance = Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2); let yDelta = distance * Math.cos(angle) * Math.tan(arcAngle); let xDelta = distance * Math.sin(angle) * Math.tan(arcAngle); @@ -113,13 +117,9 @@ function position(e) { let arcPt1 = [newX1, newY1] = edgePoint(x1, y1, newX1, newY1, maxX, maxY, minX, minY); let arcPt2 = [newX2, newY2] = edgePoint(x1, y1, newX2, newY2, maxX, maxY, minX, minY); - testOutline.setAttributeNS(null, 'points', `${newX1},${newY1} ${x1},${y1} ${newX2},${newY2}`); - - // console.log('area ', testArea.getAttribute('points').split(' ').map(n => n.split(',').map(n => Math.round(parseFloat(n)))).join(' \t')); - // console.log('lines ', testOutline.getAttribute('points').split(' ').map(n => n.split(',').map(n => Math.round(parseFloat(n)))).join(' \t')); + activeFiringArcOutline.setAttributeNS(null, 'points', `${newX1},${newY1} ${x1},${y1} ${newX2},${newY2}`); function touchSameEdge([x1, y1], [x2, y2]) { - // console.log('touchSameEdge', x1, y1, x2, y2); return x1 === x2 || y1 === y2; } @@ -132,192 +132,62 @@ function position(e) { return x1 === x2 || y1 === y2; } + // which arcpt does aimpt share a value with? + // 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) { - // which arcpt does aimpt share a value with? - - // 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? - const sharedValPt = pts.find(([ptX, ptY]) => pt.includes(ptX) || pt.includes(ptY)); - let limits = [[minX, maxX], [minY, maxY]]; if (!sharedValPt) { return; } - const nonSharedValIndex = pt[0] === sharedValPt[0] ? 1 : 0; - const cornerVal = pt[nonSharedValIndex] < sharedValPt[nonSharedValIndex] ? Math.min(...limits[nonSharedValIndex]) : Math.max(...limits[nonSharedValIndex]); - let cps = cornerPoints.filter(cp => cp[nonSharedValIndex] === cornerVal); + const limits = [[minX, maxX], [minY, maxY]], + nonSharedValIndex = pt[0] === sharedValPt[0] ? 1 : 0; + + let cornerVal; - return cps; + if (pt[nonSharedValIndex] < sharedValPt[nonSharedValIndex]) { + cornerVal = Math.min(...limits[nonSharedValIndex]) + } else { + cornerVal = Math.max(...limits[nonSharedValIndex]); + } + + return cornerPoints.filter(cp => cp[nonSharedValIndex] === cornerVal); } - console.log('cornerPoints', cornerPoints); + let points; if (touchSameEdge(arcPt1, arcPt2)) { - // 0 corner case - console.log('0-corner case'); - testArea.setAttributeNS(null, 'points', [pivotPt, arcPt1, arcPt2].join(' ')); + // 0-corner case + points = [arcPt2, pivotPt, arcPt1]; } else if (touchOrthogonalEdges(arcPt1, arcPt2)) { - // 1 corner or 3 corner case if (touchSameEdge(aimPt, arcPt1) || touchSameEdge(aimPt, arcPt2)) { - console.log('1-corner case'); + // 1-corner case let cp = cornerPoints.find(cp => shareValue(cp, arcPt1) && shareValue(cp, arcPt2)); - testArea.setAttributeNS(null, 'points', [pivotPt, arcPt1, cp, arcPt2].join(' ')); + points = [arcPt2, pivotPt, arcPt1, cp]; } else { - console.log('3-corner case'); - console.log('cornerPoints', cornerPoints); - let cps = cornerPoints.filter(cp => !shareValue(cp, arcPt1) || !shareValue(cp, arcPt2)); - let index = cps.findIndex(cp => shareValue(cp, arcPt2)); - console.log('cps', cps, 'index', index, 'arcPt2', arcPt2, 'arcPt1', arcPt1); - cps.splice(index + 1, 0, arcPt2, pivotPt, arcPt1); - console.log('after splice', cps); - // testArea.setAttributeNS(null, 'points', [pivotPt, arcPt1, cps.join(' '), arcPt2].join(' ')); - testArea.setAttributeNS(null, 'points', cps.join(' ')); + // 3-corner case + points = cornerPoints.filter(cp => !shareValue(cp, arcPt1) || !shareValue(cp, arcPt2)); + let index = points.findIndex(cp => shareValue(cp, arcPt2)); + points.splice(index + 1, 0, arcPt2, pivotPt, arcPt1); } } else { - // 2 corner case if (touchSameEdge(aimPt, arcPt1) || touchSameEdge(aimPt, arcPt2)) { - console.log('2-corner case, edge shared'); - let cps = findSharedValue(aimPt, arcPt1, arcPt2); - let index = cps.findIndex(cp => shareValue(cp, arcPt2)); - // console.log('cps', cps, 'index', index, 'arcPt2', arcPt2, 'arcPt1', arcPt1); - cps.splice(index + 1, 0, arcPt2, pivotPt, arcPt1); - - // testArea.setAttributeNS(null, 'points', [pivotPt, arcPt1, cps.join(' '), arcPt2].join(' ')); - testArea.setAttributeNS(null, 'points', cps.join(' ')); + // 2-corner case, aim and an arc point touch the same edge + points = findSharedValue(aimPt, arcPt1, arcPt2); + let index = points.findIndex(cp => shareValue(cp, arcPt2)); + points.splice(index + 1, 0, arcPt2, pivotPt, arcPt1); } else { - console.log('2-corner case, all separate edges'); - let cps = cornerPoints.filter(cp => shareValue(cp, aimPt) || shareValue(cp, aimPt)); - let index = cps.findIndex(cp => shareValue(cp, arcPt2)); - cps.splice(index + 1, 0, arcPt2, pivotPt, arcPt1); - - // testArea.setAttributeNS(null, 'points', [pivotPt, arcPt1, cps.join(' '), arcPt2].join(' ')); - testArea.setAttributeNS(null, 'points', cps.join(' ')); + // 2-corner case, aim and both arc points all touch different edges + points = cornerPoints.filter(cp => shareValue(cp, aimPt) || shareValue(cp, aimPt)); + let index = points.findIndex(cp => shareValue(cp, arcPt2)); + points.splice(index + 1, 0, arcPt2, pivotPt, arcPt1); } } - // testArea.setAttributeNS(null, 'points', cornerPoints.join(' ')); - - - - - - - - - - - - - if (activeFiringArc) { - let activeFiringArcOutline = this.querySelector(`#lines polygon[data-number="${activeFiringArc.dataset.number}"][data-allegiance="${activeFiringArc.dataset.allegiance}"]`), - board = this.querySelector('#image-maps'), - { width, height } = board.getBBox(), - pt = new DOMPoint(e.clientX, e.clientY), - { x: pointerX, y: pointerY } = pt.matrixTransform(board.getScreenCTM().inverse()), - [maxXpx, maxYpx] = [width, height], - { x: x1px, y: y1px } = activeFiringArc.points[0]; - - let [x2px, y2px] = [ - pointerX / width * maxXpx, - pointerY / height * maxYpx - ]; - - let xDiff = x2px - x1px; - let yDiff = y2px - y1px; - let angle = calculateAngle(xDiff, yDiff); - - 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); - - let [newY1, newX1] = [y2px + yDelta, x2px + xDelta]; - let [newY2, newX2] = [y2px - yDelta, x2px - xDelta]; - - [newX1, newY1] = edgePoint(x1px, y1px, newX1, newY1, maxXpx, maxYpx); - [newX2, newY2] = edgePoint(x1px, y1px, newX2, newY2, maxXpx, maxYpx); - - let oppositeEdgeConditions = [ - newX1 == 0 && newX2 == maxXpx, - newX2 == 0 && newX1 == maxXpx, - newY1 == 0 && newY2 == maxYpx, - newY2 == 0 && newY1 == maxYpx - ] - - let orthogonalEdgeConditions = [ - (newX1 == 0 || newX1 == maxXpx) && (newY2 == 0 || newY2 == maxYpx), - (newX2 == 0 || newX2 == maxXpx) && (newY1 == 0 || newY1 == maxYpx), - ] - - let points; - - if (oppositeEdgeConditions.some(e => e)) { - let cornerPoints; - - if (xDiff > 0 && yDiff > 0) { - if ((newY1 == 0 && newY2 == maxYpx) || (newY1 == maxYpx && newY2 == 0)) { - cornerPoints = [[maxXpx, 0], [maxXpx, maxYpx]]; - } else { - cornerPoints = [[maxXpx, maxYpx], [0, maxYpx]]; - } - } else if (xDiff > 0 && yDiff < 0) { - if ((newY1 == 0 && newY2 == maxYpx) || (newY1 == maxYpx && newY2 == 0)) { - cornerPoints = [[maxXpx, 0], [maxXpx, maxYpx]]; - } else { - cornerPoints = [[0, 0], [maxXpx, 0]]; - } - - } else if (xDiff < 0 && yDiff > 0) { - if ((newY1 == 0 && newY2 == maxYpx) || (newY1 == maxYpx && newY2 == 0)) { - cornerPoints = [[0, maxYpx], [0, 0]]; - } else { - cornerPoints = [[maxXpx, maxYpx], [0, maxYpx]]; - } - - } else { - if ((newY1 == 0 && newY2 == maxYpx) || (newY1 == maxYpx && newY2 == 0)) { - cornerPoints = [[0, maxYpx], [0, 0]]; - } else { - cornerPoints = [[0, 0], [maxXpx, 0]]; - } - - } - points = `${x1px},${y1px} ${newX1},${newY1} ${cornerPoints[1]} ${cornerPoints[0]} ${newX2},${newY2}`; - } else if (orthogonalEdgeConditions.some(e => e)) { - let cornerPoints = []; - let cp1, cp2; - - if (newX1 == 0 || newX1 == maxXpx) { - cp1 = [newX1, yDiff > 0 ? maxYpx : 0]; - } else { - cp1 = [xDiff > 0 ? maxXpx : 0, newY1]; - } - - if (newX2 == 0 || newX2 == maxXpx) { - cp2 = [newX2, yDiff > 0 ? maxYpx : 0]; - } else { - cp2 = [xDiff > 0 ? maxXpx : 0, newY2]; - } - - if (cp1[0] == cp2[0] && cp1[1] == cp2[1]) { - cornerPoints.push(cp1); - } else { - cornerPoints.push(cp1); - cornerPoints.push([xDiff > 0 ? maxXpx : 0, yDiff > 0 ? maxYpx : 0]) - cornerPoints.push(cp2); - } - - points = `${x1px},${y1px} ${newX1},${newY1} ${cornerPoints.join(' ')} ${newX2},${newY2}`; - } else { - points = `${x1px},${y1px} ${newX1},${newY1} ${newX2},${newY2}`; - } - - activeFiringArcOutline.setAttributeNS(null, 'points', points); - activeFiringArc.setAttributeNS(null, 'points', points); - } + activeFiringArc.setAttributeNS(null, 'points', points.join(' ')); } function setDataAttrs({ dataset: { allegiance, number }}, el) { @@ -342,60 +212,35 @@ export default function (el) { let arcLayer = svg.querySelector('#shapes'); let outlineLayer = svg.querySelector('#lines'); let arcContainer = svg.querySelector('#firing-arcs'); + let grid = svg.querySelector('.grid'); - - - - let testArcs = svg.querySelector('#test-arcs'); - let testAim = document.createElementNS(svgns, 'line'); - testAim.setAttributeNS(null, 'x1', x); - testAim.setAttributeNS(null, 'y1', y); - testAim.setAttributeNS(null, 'x2', x); - testAim.setAttributeNS(null, 'y2', y); - testArcs.appendChild(testAim); - - let testOutline = document.createElementNS(svgns, 'polyline'); - testOutline.setAttributeNS(null, 'points', `${x},${y} ${x},${y} ${x},${y}`); - testArcs.appendChild(testOutline); - - let testGrid = svg.querySelector('.grid'); - let gridArea = document.createElementNS(svgns, 'polygon'); + 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); cornerPoints = (function () { - let { x, y, width, height } = testGrid.getBBox(); + let { x, y, width, height } = grid.getBBox(); return [[x, y], [x + width, y], [x + width, y + height], [x, y + height]]; })(); - gridArea.setAttributeNS(null, 'points', cornerPoints.join(' ')); - testArcs.appendChild(gridArea); - - - - - // console.log(testArcs); - - 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); - let tPt = pt.matrixTransform(mtx); - - let pivotPoint = [tPt.x, tPt.y]; let firingArc = document.createElementNS(svgns, 'polygon'); - let firingArcOutline = document.createElementNS(svgns, 'polygon'); + let firingArcOutline = document.createElementNS(svgns, 'polyline'); setDataAttrs(counter, firingArc); firingArc.dataset.size = size; firingArc.classList.add('firing-arc', 'active'); - firingArc.setAttributeNS(null, 'points', `${pivotPoint} ${pivotPoint} ${pivotPoint}`); + firingArc.setAttributeNS(null, 'points', `${x},${y} ${x},${y} ${x},${y}`); setDataAttrs(counter, firingArcOutline); - firingArcOutline.setAttributeNS(null, 'points', `${pivotPoint} ${pivotPoint} ${pivotPoint}`); + firingArcOutline.setAttributeNS(null, 'points', `${x},${y} ${x},${y} ${x},${y}`); let clipShape = document.createElementNS(svgns, 'circle'); - clipShape.setAttributeNS(null, 'cx', tPt.x); - clipShape.setAttributeNS(null, 'cy', tPt.y); - clipShape.setAttributeNS(null, 'r', 100); + clipShape.setAttributeNS(null, 'cx', x); + clipShape.setAttributeNS(null, 'cy', y); + clipShape.setAttributeNS(null, 'r', clippedFiringArcRadius); let clipPath = document.createElementNS(svgns, 'clipPath'); setDataAttrs(counter, clipPath); @@ -438,7 +283,7 @@ export default function (el) { }; this.get = function ({ dataset: { allegiance, number }}) { - return svg.querySelectorAll(`#firing-arcs polygon[data-number="${number}"][data-allegiance="${allegiance}"]`); + return svg.querySelectorAll(`#firing-arcs [data-number="${number}"][data-allegiance="${allegiance}"], #firing-arcs line`); }; this.toggleVisibility = function (allegiance) { |