index : btroops | |
Virtual board game-aid for BattleTroops, an infantry combat simulator wargame published by FASA in 1989. |
aboutsummaryrefslogtreecommitdiff |
diff options
-rw-r--r-- | index.html | 5 | ||||
-rw-r--r-- | index.js | 202 | ||||
-rw-r--r-- | style.css | 38 |
3 files changed, 216 insertions, 29 deletions
@@ -150,8 +150,8 @@ </defs> <rect id="background" x="-1" y="-1" width="2287" height="3087" /> - <image id="map2" class="map-scans" href="scans/map2.jpg" width="2284" height="1518" x="2" y="2" /> - <image id="map3" class="map-scans" href="scans/map3.jpg" width="2284" height="1518" x="4" y="1564" /> + <!-- <image id="map2" class="map-scans" href="scans/map2.jpg" width="2284" height="1518" x="2" y="2" /> + <image id="map3" class="map-scans" href="scans/map3.jpg" width="2284" height="1518" x="4" y="1564" /> --> <g id="firing-arcs"></g> <rect id="map" x="-1" y="-1" width="2287" height="3087" /> <g id="points"></g> @@ -159,6 +159,7 @@ </svg> <div id="content"> + <input type="checkbox" class="visible" checked /> <div> Set firing arc: <button type="button" class="set-firing-arc" data-size="small"> @@ -63,14 +63,17 @@ const svgns = "http://www.w3.org/2000/svg", svg = document.querySelector('svg'), map = document.querySelector('rect#map'), hex = document.getElementById('point'), - pointsGroup = document.getElementById('points'), - settingsPanel = document.getElementById('panel'); + ptGrp = document.getElementById('points'), + settingsPanel = document.getElementById('panel'), + recordSheetVisibility = document.querySelector('#content input[type="checkbox"].visible'); const { x: VIEWBOX_X, y: VIEWBOX_Y, width: VIEWBOX_WIDTH, height: VIEWBOX_HEIGHT } = svg.viewBox.baseVal; -const COLUMN_COUNT = 33, - ROW_COUNT = 51, +// const COLUMN_COUNT = 33, +// ROW_COUNT = 51, +const COLUMN_COUNT = 20, + ROW_COUNT = 20, HORZ_POINT_DISTANCE = 1.005, VERT_POINT_DISTANCE = Math.sqrt(3) * HORZ_POINT_DISTANCE / 2, ALTERNATING_OFFSET = HORZ_POINT_DISTANCE / 2, @@ -85,9 +88,21 @@ const FIRING_ARC_SIZE = { 'large': Math.atan((21 * HORZ_POINT_DISTANCE) / (6 * VERT_POINT_DISTANCE)) } -Object.values(settingsPanel.querySelectorAll('fieldset')).forEach(fieldset => { +let prevVb = localStorage.getItem('viewBox'); +let recVis = localStorage.getItem('recordsVisibility'); + +if (prevVb) { + svg.setAttributeNS(null, 'viewBox', prevVb); +} + +if (recVis == 'false') { + recordSheetVisibility.checked = false; +} + +// Object.values(settingsPanel.querySelectorAll('fieldset')).forEach(fieldset => { +[].forEach(fieldset => { const target = document.getElementById(fieldset.name); - const transform = window.getComputedStyle(target).transform.match(/-?\d+\.?\d*/g); + const transform = getComputedStyle(target).transform.match(/-?\d+\.?\d*/g); const inputs = fieldset.querySelectorAll('input'); if (transform) { @@ -131,9 +146,12 @@ POINTS.forEach((row, index) => row.forEach(([x, y]) => { cy = y * 3 / 2 * CIRCUMRADIUS, point = document.createElementNS(svgns, 'use'); + cx = parseFloat(cx.toFixed(1)); + cy = parseFloat(cy.toFixed(1)); + point.setAttributeNS(null, 'href', `#point`); - point.setAttributeNS(null, 'x', `${parseFloat(cx.toFixed(1))}`); - point.setAttributeNS(null, 'y', `${parseFloat(cy.toFixed(1))}`); + point.setAttributeNS(null, 'x', cx); + point.setAttributeNS(null, 'y', cy); point.dataset.x = x; point.dataset.y = y; @@ -146,21 +164,20 @@ POINTS.forEach((row, index) => row.forEach(([x, y]) => { {troopNumber, troopAllegiance} = selectedSoldier.dataset, selector = troopSelector(troopNumber, troopAllegiance); - let matrix = window.getComputedStyle(pointsGroup).transform.match(/-?\d+\.?\d*/g), - svgMatrix = new DOMMatrix(matrix || ''), + let transProp = getComputedStyle(ptGrp).transform.match(/-?\d+\.?\d*/g), + mtx = new DOMMatrix(transProp || ''), pt = new DOMPoint(point.x.baseVal.value, point.y.baseVal.value), - svgP = pt.matrixTransform(svgMatrix); + svgP = pt.matrixTransform(mtx); - counter.setAttributeNS(null, 'cx', `${parseFloat(svgP.x.toFixed(1))}`); - counter.setAttributeNS(null, 'cy', `${parseFloat(svgP.y.toFixed(1))}`); + counter.setAttributeNS(null, 'cx', svgP.x); + counter.setAttributeNS(null, 'cy', svgP.y); counter.setAttributeNS(null, 'r', '0.25in'); counter.dataset.troopNumber = troopNumber; counter.dataset.troopAllegiance = troopAllegiance; counter.classList.add('counter'); - text.setAttributeNS(null, 'text-anchor', 'middle'); - text.setAttributeNS(null, 'x', `${svgP.x}`); - text.setAttributeNS(null, 'y', `${svgP.y}`); + text.setAttributeNS(null, 'x', svgP.x); + text.setAttributeNS(null, 'y', svgP.y); text.dataset.troopNumber = troopNumber; text.dataset.troopAllegiance = troopAllegiance; text.textContent = troopNumber; @@ -233,9 +250,146 @@ POINTS.forEach((row, index) => row.forEach(([x, y]) => { point.addEventListener('mouseout', e => e.target.removeAttribute('class')); - pointsGroup.appendChild(point); + ptGrp.appendChild(point); + + text = document.createElementNS(svgns, 'text'), + + text.setAttributeNS(null, 'x', cx); + text.setAttributeNS(null, 'y', cy); + text.textContent = `${point.dataset.x},${point.dataset.y}`; + + ptGrp.appendChild(text); })); +(function debug() { + function drawLine(x1, y1, x2, y2) { + let start = ptGrp.querySelector(`[data-x="${x1}"][data-y="${y1}"]`); + let end = ptGrp.querySelector(`[data-x="${x2}"][data-y="${y2}"]`); + let [startX, startY] = [start.x.baseVal.value, start.y.baseVal.value]; + let [endX, endY] = [end.x.baseVal.value, end.y.baseVal.value]; + + let startP = new DOMPoint(startX, startY); + let endP = new DOMPoint(endX, endY); + + let tStart = startP.matrixTransform(mtx); + let tEnd = endP.matrixTransform(mtx); + + let line = document.createElementNS(svgns, 'line'); + line.setAttributeNS(null, 'x1', tStart.x); + line.setAttributeNS(null, 'y1', tStart.y); + line.setAttributeNS(null, 'x2', tEnd.x); + line.setAttributeNS(null, 'y2', tEnd.y); + line.classList.add('debug'); + + line.setAttributeNS(null, 'stroke', 'green'); + line.setAttributeNS(null, 'stroke-width', 10); + svg.appendChild(line); + + return line; + } + + let transProp = getComputedStyle(ptGrp).transform.match(/-?\d+\.?\d*/g), + mtx = new DOMMatrix(transProp || ''), + circR = (new DOMPoint(0, 3 * CIRCUMRADIUS / 2)).matrixTransform(mtx).y * 2 / 3; + + // let l1 = drawLine(1, 1, 1, 2); + + drawLine(0, 0, 5, 4); + // drawLine(1, 1, 1, 3); + + // let pt = l1.getPointAtLength(circR); + + // let c = document.createElementNS(svgns, 'circle'); + // c.setAttributeNS(null, 'cx', pt.x); + // c.setAttributeNS(null, 'cy', pt.y); + // c.setAttributeNS(null, 'r', 20); + // c.setAttributeNS(null, 'fill', 'red'); + // c.setAttributeNS(null, 'opacity', '0.2'); + // svg.appendChild(c); +})(); + +function evenr_to_axial(x, y) { + return {q: x - (y + (y & 1)) / 2, r: y}; +} + +function axial_to_evenr(q, r) { + return {x: q + (r + (r & 1)) / 2, y: r}; +} + +function axial_distance(q1, r1, q2, r2) { + return (Math.abs(q1 - q2) + Math.abs(q1 + r1 - q2 - r2) + Math.abs(r1 - r2)) / 2; +} + +function offset_distance(x1, y1, x2, y2) { + let { q: q1, r: r1 } = evenr_to_axial(x1, y1), + { q: q2, r: r2 } = evenr_to_axial(x2, y2); + + return axial_distance(q1, r1, q2, r2); +} + +function cube_to_axial(q, r, s) { + return { q: q, r: r }; +} + +function axial_to_cube(q, r) { + return { q: q, r: r, s: -q - r}; +} + +function cube_round(q, r, s) { + rQ = Math.round(q); + rR = Math.round(r); + rS = Math.round(s); + + let q_diff = Math.abs(rQ - q), + r_diff = Math.abs(rR - r), + s_diff = Math.abs(rS - s); + + if (q_diff > r_diff && q_diff > s_diff) { + rQ = -rR - rS; + } else if (r_diff > s_diff) { + rR = -rQ - rS; + } else { + rS = -rQ - rR; + } + + return {q: rQ, r: rR, s: rS}; +} + +function axial_round(q, r) { + let cube = axial_to_cube(q, r), + round = cube_round(cube.q, cube.r, cube.s), + axial = cube_to_axial(round.q, round.r, round.s); + + return {q: axial.q, r: axial.r}; +} + +function lerp(a, b, t) { + return a + (b - a) * t; +} + +function axial_lerp(q1, r1, q2, r2, t) { + return { q: lerp(q1, q2, t), r: lerp(r1, r2, t) }; +} + +function linedraw(x1, y1, x2, y2) { + let axial1 = evenr_to_axial(x1, y1), + axial2 = evenr_to_axial(x2, y2), + n = offset_distance(x1, y1, x2, y2), + results = []; + + for (let i = 0; i <= n; i++) { + let lerp = axial_lerp(axial1.q, axial1.r, axial2.q, axial2.r, 1.0 / n * i), + round = axial_round(lerp.q, lerp.r), + { x, y } = axial_to_evenr(round.q, round.r) + + console.log(x, y); + + results.push([x, y]); + } + + return results; +} + map.addEventListener('mousemove', e => { let boundingRect = e.target.getBoundingClientRect(); let pointerX = e.clientX - boundingRect.left; @@ -462,7 +616,10 @@ svg.addEventListener('wheel', e => { // newX = newX < VIEWBOX_X ? VIEWBOX_X : newX; // newY = newY < VIEWBOX_Y ? VIEWBOX_Y : newY; - svg.setAttributeNS(null, 'viewBox', `${newX} ${newY} ${newWidth} ${newHeight}`); + let vb = `${newX} ${newY} ${newWidth} ${newHeight}` + + localStorage.setItem('viewBox', vb); + svg.setAttributeNS(null, 'viewBox', vb); }); svg.addEventListener('pointerdown', e => { @@ -493,7 +650,10 @@ svg.addEventListener('pointerdown', e => { moveX = parseInt(svgStartPt.x - svgMovePt.x + x), moveY = parseInt(svgStartPt.y - svgMovePt.y + y); - svg.setAttributeNS(null, 'viewBox', `${moveX} ${moveY} ${width} ${height}`); + let vb = `${moveX} ${moveY} ${width} ${height}`; + + localStorage.setItem('viewBox', vb); + svg.setAttributeNS(null, 'viewBox', vb); } } @@ -505,4 +665,8 @@ svg.addEventListener('pointerdown', e => { svg.addEventListener('pointermove', pointerMove); svg.addEventListener('pointerup', pointerUp); +}); + +recordSheetVisibility.addEventListener('input', e => { + localStorage.setItem('recordsVisibility', recordSheetVisibility.checked); });
\ No newline at end of file @@ -8,14 +8,21 @@ body { svg { background-color: darkgray; flex-basis: 100%; - /* transform: rotate(-90deg); - transform-origin: center; */ /* max-height: 50vh; */ /* max-height: 100vw; */ } svg text { user-select: none; + font-size: 4px; + fill: white; + stroke: black; + stroke-width: 0.2px; + font-weight: bold; + transform: translateY(6px); + font-family: monospace; + text-anchor: middle; + /* display: none; */ } div#content { @@ -27,6 +34,7 @@ div#content { max-height: 100vh; flex-direction: column; /* padding: 2px; */ + position: relative; } #content > div:first-of-type { @@ -34,6 +42,23 @@ div#content { border-bottom: 1px solid gray; } +#content > div { + display: none; +} + +#content input[type="checkbox"].visible { + position: absolute; + right: 0; +} + +#content:has(input[type="checkbox"].visible:checked) > div { + display: block; +} + +#content:has(input[type="checkbox"].visible:checked) div#record-sheet { + display: flex; +} + #record-sheet { /* max-height: 100%; */ overflow-y: auto; @@ -73,13 +98,14 @@ div#content { } svg > defs > #point { - fill: transparent; + fill: teal; + fill-opacity: 0.5; stroke: black; stroke-width: 0.5px; } use[href="#point"] { - opacity: 0; + opacity: 0.2; } use[href="#point"].active { @@ -90,10 +116,6 @@ g#points { transform: translate(19px, 31px) scale(4); } -g#test { - transform: scale(2); -} - #background { fill: #bacae3; } |