diff --git a/index.html b/index.html
index eb083b4..22d02b6 100644
--- a/index.html
+++ b/index.html
@@ -145,12 +145,18 @@
-
-
-
- 1
+
+
+ 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+
@@ -179,7 +185,6 @@
-
@@ -228,7 +233,7 @@
-
+
1
Rifle
4L
diff --git a/index.js b/index.js
index f33d1e2..e7ce14a 100644
--- a/index.js
+++ b/index.js
@@ -59,387 +59,6 @@ let getPointCoords = (x, y) => {
return [point.x.baseVal.value, point.y.baseVal.value]
};
-const svgns = "http://www.w3.org/2000/svg",
- svg = document.querySelector('svg'),
- hex = document.getElementById('point'),
- ptGrp = document.getElementById('points'),
- cntrGrp = document.getElementById('counters'),
- settingsPanel = document.getElementById('panel'),
- recordSheetVisibility = document.querySelector('#content input[type="checkbox"].visible');
-
-const q = s => document.querySelector(s),
- qA = s => document.querySelectorAll(s);
-
-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 = 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,
- CIRCUMRADIUS = Math.max(...[...new Set(Object.values(hex.points).flatMap(({x, y}) => [x, y]))]),
- INRADIUS = CIRCUMRADIUS * Math.sqrt(3) / 2,
- [COLUMNS, ROWS] = [COLUMN_COUNT, ROW_COUNT].map(n => [...Array(n).keys()]),
- POINTS = ROWS.map(y => COLUMNS.map(x => [x, y]));
-
-const 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))
- }
-
-let prevVb = localStorage.getItem('viewBox');
-let recVis = localStorage.getItem('recordsVisibility');
-
-if (prevVb) {
- svg.setAttributeNS(null, 'viewBox', prevVb);
-}
-
-if (recVis == 'false') {
- recordSheetVisibility.checked = false;
-}
-
-let info = document.getElementById('status');
-
-// Object.values(settingsPanel.querySelectorAll('fieldset')).forEach(fieldset => {
-[].forEach(fieldset => {
- const target = document.getElementById(fieldset.name);
- const transform = getComputedStyle(target).transform.match(/-?\d+\.?\d*/g);
- const inputs = fieldset.querySelectorAll('input');
-
- if (transform) {
- const [a, b, c, d, e, f] = transform.map(n => parseFloat(n));
-
- // a c e
- // b d f
-
- const scaleX = Math.sqrt(a**2 + c**2);
- const scaleY = Math.sqrt(b**2 + d**2);
-
- let values = {
- scale: Math.round(scaleX * 10) / 10,
- translateX: e,
- translateY: f,
- rotate: Math.round(radToDeg((Math.acos(a / scaleX) + Math.asin(b / scaleY)) / 2) * 10) / 10
- }
-
- inputs.forEach(input => input.value = values[input.name]);
- }
-
- inputs.forEach(input => {
- input.addEventListener('pointerenter', e => e.target.focus());
-
- input.addEventListener('input', e => {
- let { scale, translateX, translateY, rotate} = Object.values(inputs).reduce((acc, input) => {
- acc[input.name] = input.value;
- return acc;
- }, {});
-
- let transform = `translate(${translateX}px, ${translateY}px) rotate(${rotate}deg) scale(${scale})`;
- target.style.transform = transform;
- });
-
- input.addEventListener('pointerleave', () => document.activeElement.blur());
- });
-});
-
-function ptGrpToSvgPt(x, y) {
- let transProp = getComputedStyle(ptGrp).transform.match(/-?\d+\.?\d*/g),
- mtx = new DOMMatrix(transProp || ''),
- pt = new DOMPoint(x, y),
- svgP = pt.matrixTransform(mtx);
-
- return svgP;
-}
-
-POINTS.forEach((row, index) => row.forEach(([x, y]) => {
- var cx = x * INRADIUS * 2 + (isEven(index) ? INRADIUS : 0),
- 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', cx);
- point.setAttributeNS(null, 'y', cy);
- point.dataset.x = x;
- point.dataset.y = y;
-
- point.addEventListener('dblclick', e => {
- let selectedSoldier = document.querySelector('.soldier-record.selected');
- let existingOccupant =
- svg.querySelector(`.counter[data-x="${point.dataset.x}"][data-y="${point.dataset.y}"]`);
-
- if (selectedSoldier && !existingOccupant) {
- let counter = document.createElementNS(svgns, 'circle'),
- text = document.createElementNS(svgns, 'text'),
- {troopNumber, troopAllegiance} = selectedSoldier.dataset,
- selector = troopSelector(troopNumber, troopAllegiance);
-
- info.querySelector('#hex-count').textContent = '-';
- info.style.display = 'none';
- ptGrp.querySelectorAll('.active').forEach(el => el.removeAttribute('class'));
- svg.querySelectorAll('.sight-line').forEach(el => el.remove());
-
- counter.setAttributeNS(null, 'cx', cx);
- counter.setAttributeNS(null, 'cy', cy);
- counter.setAttributeNS(null, 'r', '5');
- counter.dataset.troopNumber = troopNumber;
- counter.dataset.troopAllegiance = troopAllegiance;
- counter.dataset.x = point.dataset.x;
- counter.dataset.y = point.dataset.y;
- counter.classList.add('counter');
-
- text.setAttributeNS(null, 'x', cx);
- text.setAttributeNS(null, 'y', cy);
- text.dataset.troopNumber = troopNumber;
- text.dataset.troopAllegiance = troopAllegiance;
- text.textContent = troopNumber;
- text.classList.add('counter');
-
- document.querySelectorAll(`.counter${selector}`).forEach(el => el.remove());
-
- counter.addEventListener('dblclick', e => {
- let selectedSoldier = document.querySelector('.soldier-record.selected');
-
- if (selectedSoldier) {
- let {troopNumber, troopAllegiance} = selectedSoldier.dataset,
- selector = troopSelector(troopNumber, troopAllegiance),
- targetIsSelectedSoldier = [
- e.target.dataset.troopNumber == troopNumber,
- e.target.dataset.troopAllegiance == troopAllegiance
- ].every(el => el);
-
- if (targetIsSelectedSoldier) {
- document.querySelectorAll(`.counter${selector}`).forEach(el => el.remove());
- document.querySelectorAll(`#firing-arcs ${selector}`).forEach(el => el.remove());
- }
- }
- });
-
- // counter.addEventListener('mouseenter', e => {
- // let selectedSoldier = document.querySelector('.soldier-record.selected');
-
- // if (selectedSoldier) {
- // let {troopNumber, troopAllegiance} = selectedSoldier.dataset,
- // selector = troopSelector(troopNumber, troopAllegiance),
- // source = document.querySelector(`circle.counter${selector}`),
-
- // // TODO: use isEqualNode() method instead
- // sourceAndTargetAreNotTheSame = [
- // troopNumber != e.target.dataset.troopNumber,
- // troopAllegiance != e.target.dataset.troopAllegiance
- // ].some(el => el);
-
- // if (source && sourceAndTargetAreNotTheSame) {
- // let sightLine = document.createElementNS(svgns, 'line');
-
- // sightLine.classList.add('sight-line');
- // sightLine.setAttributeNS(null, 'x1', source.getAttribute('cx'));
- // sightLine.setAttributeNS(null, 'y1', source.getAttribute('cy'));
- // sightLine.setAttributeNS(null, 'x2', e.target.getAttribute('cx'));
- // sightLine.setAttributeNS(null, 'y2', e.target.getAttribute('cy'));
-
- // svg.appendChild(sightLine);
- // }
- // }
- // });
-
- // counter.addEventListener('mouseleave', e => {
- // document.querySelectorAll('.sight-line').forEach(el => el.remove());
- // });
-
- // svg.insertBefore(counter, ptGrp);
-
- // let symbCtr = document.createElementNS(svgns, 'use');
-
- // symbCtr.setAttributeNS(null, 'href', '#troop-counter');
- // symbCtr.setAttributeNS(null, 'x', cx);
- // symbCtr.setAttributeNS(null, 'y', cy);
-
- // cntrGrp.appendChild(symbCtr);
-
- cntrGrp.appendChild(counter);
- cntrGrp.appendChild(text);
- }
- });
-
- point.addEventListener('mouseover', e => {
- let selectedSoldier = document.querySelector('.soldier-record.selected');
-
- if (selectedSoldier) {
- // e.target.classList.add('active');
- let { troopNumber: tn, troopAllegiance: ta } = selectedSoldier.dataset;
-
- let counter = svg.querySelector(`circle.counter[data-troop-number="${tn}"][data-troop-allegiance="${ta}"]`);
- let sl = svg.querySelector('.sight-line');
-
- if (counter && (!sl || sl.classList.contains('active'))) {
- if (sl) {
- info.querySelector('#hex-count').textContent = '-';
- info.style.display = 'none';
- ptGrp.querySelectorAll('.active').forEach(el => el.removeAttribute('class'));
- svg.querySelectorAll('.sight-line').forEach(el => el.remove());
- }
-
- let source = ptGrp.querySelector(`use[data-x="${counter.dataset.x}"][data-y="${counter.dataset.y}"]`);
- let [x1, y1] = [source.x.baseVal.value, source.y.baseVal.value];
- let [x2, y2] = [e.target.x.baseVal.value, e.target.y.baseVal.value];
-
- if (x1 !== x2 || y1 !== y2) {
- let { x: svgX1, y: svgY1 } = ptGrpToSvgPt(x1, y1);
- let { x: svgX2, y: svgY2 } = ptGrpToSvgPt(x2, y2);
-
- let sightLine = document.createElementNS(svgns, 'line');
-
- sightLine.classList.add('sight-line');
- sightLine.classList.add('active');
- sightLine.setAttributeNS(null, 'x1', svgX1);
- sightLine.setAttributeNS(null, 'y1', svgY1);
- sightLine.setAttributeNS(null, 'x2', svgX2);
- sightLine.setAttributeNS(null, 'y2', svgY2);
-
- // svg.insertBefore(sightLine, ptGrp);
- document.getElementById('grid').appendChild(sightLine);
-
- let coords = [
- counter.dataset.x,
- counter.dataset.y,
- e.target.dataset.x,
- e.target.dataset.y
- ].map(n => parseInt(n));
-
- info.querySelector('#hex-count').textContent = offset_distance(...coords);
- info.style.display = 'block';
-
- let lineCoords = linedraw(...coords);
- lineCoords.shift();
- let s = lineCoords.map(([x, y]) => `use[data-x="${x}"][data-y="${y}"]`).join(', ');
- ptGrp.querySelectorAll(s).forEach(p => p.classList.add('active'));
- }
- }
- }
- });
-
- // point.addEventListener('mouseout', e => {
- // let sl = svg.querySelector('.sight-line.active');
-
- // if (sl) {
- // info.querySelector('#hex-count').textContent = '-';
- // info.style.display = 'none';
- // ptGrp.querySelectorAll('.active').forEach(el => el.removeAttribute('class'));
- // svg.querySelectorAll('.sight-line').forEach(el => el.remove());
- // }
- // });
-
- point.addEventListener('click', e => {
- let sl = svg.querySelector('.sight-line');
-
- if (sl) {
- sl.classList.toggle('active');
-
- if (sl.classList.contains('active')) {
- point.dispatchEvent(new MouseEvent('mouseout'));
- point.dispatchEvent(new MouseEvent('mouseover'));
- }
- }
- });
-
- 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 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);
-
- results.push([x, y]);
- }
-
- return results;
-}
-
function positionFiringArc(e) {
let activeFiringArc = document.querySelector('polygon.firing-arc.active');
@@ -554,6 +173,497 @@ function positionFiringArc(e) {
}
}
+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);
+
+ results.push([x, y]);
+ }
+
+ return results;
+}
+
+const svgns = "http://www.w3.org/2000/svg",
+ svg = document.querySelector('svg'),
+ hex = document.getElementById('point'),
+ ptGrp = document.getElementById('points'),
+ cntrGrp = document.getElementById('counters'),
+ settingsPanel = document.getElementById('panel'),
+ recordSheetVisibility = document.querySelector('#content input[type="checkbox"].visible');
+
+const q = s => document.querySelector(s),
+ qA = s => document.querySelectorAll(s);
+
+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 = 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,
+ CIRCUMRADIUS = Math.max(...[...new Set(Object.values(hex.points).flatMap(({x, y}) => [x, y]))]),
+ INRADIUS = CIRCUMRADIUS * Math.sqrt(3) / 2,
+ [COLUMNS, ROWS] = [COLUMN_COUNT, ROW_COUNT].map(n => [...Array(n).keys()]),
+ POINTS = ROWS.map(y => COLUMNS.map(x => [x, y]));
+
+const 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))
+ }
+
+let prevVb = localStorage.getItem('viewBox');
+let recVis = localStorage.getItem('recordsVisibility');
+
+if (prevVb) {
+ svg.setAttributeNS(null, 'viewBox', prevVb);
+}
+
+if (recVis == 'false') {
+ recordSheetVisibility.checked = false;
+}
+
+let info = document.getElementById('status');
+
+// Object.values(settingsPanel.querySelectorAll('fieldset')).forEach(fieldset => {
+[].forEach(fieldset => {
+ const target = document.getElementById(fieldset.name);
+ const transform = getComputedStyle(target).transform.match(/-?\d+\.?\d*/g);
+ const inputs = fieldset.querySelectorAll('input');
+
+ if (transform) {
+ const [a, b, c, d, e, f] = transform.map(n => parseFloat(n));
+
+ // a c e
+ // b d f
+
+ const scaleX = Math.sqrt(a**2 + c**2);
+ const scaleY = Math.sqrt(b**2 + d**2);
+
+ let values = {
+ scale: Math.round(scaleX * 10) / 10,
+ translateX: e,
+ translateY: f,
+ rotate: Math.round(radToDeg((Math.acos(a / scaleX) + Math.asin(b / scaleY)) / 2) * 10) / 10
+ }
+
+ inputs.forEach(input => input.value = values[input.name]);
+ }
+
+ inputs.forEach(input => {
+ input.addEventListener('pointerenter', e => e.target.focus());
+
+ input.addEventListener('input', e => {
+ let { scale, translateX, translateY, rotate} = Object.values(inputs).reduce((acc, input) => {
+ acc[input.name] = input.value;
+ return acc;
+ }, {});
+
+ let transform = `translate(${translateX}px, ${translateY}px) rotate(${rotate}deg) scale(${scale})`;
+ target.style.transform = transform;
+ });
+
+ input.addEventListener('pointerleave', () => document.activeElement.blur());
+ });
+});
+
+function ptGrpToSvgPt(x, y) {
+ let transProp = getComputedStyle(ptGrp).transform.match(/-?\d+\.?\d*/g),
+ mtx = new DOMMatrix(transProp || ''),
+ pt = new DOMPoint(x, y),
+ svgP = pt.matrixTransform(mtx);
+
+ return svgP;
+}
+
+POINTS.forEach((row, index) => row.forEach(([x, y]) => {
+ var cx = x * INRADIUS * 2 + (isEven(index) ? INRADIUS : 0),
+ 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', cx);
+ point.setAttributeNS(null, 'y', cy);
+ point.dataset.x = x;
+ point.dataset.y = y;
+
+ point.addEventListener('click', e => {
+ let selectedSoldier = document.querySelector('.soldier-record.selected');
+ let existingOccupant =
+ svg.querySelector(`.counter[data-x="${point.dataset.x}"][data-y="${point.dataset.y}"]`);
+
+ if (selectedSoldier && !existingOccupant) {
+ let counter, points,
+ { troopNumber, troopAllegiance } = selectedSoldier.dataset,
+ selector = troopSelector(troopNumber, troopAllegiance),
+ counterNodeList = cntrGrp.querySelectorAll(`use${selector}`);
+
+ if (counterNodeList.length > 0) {
+ let counters = Array.from(counterNodeList),
+ original = counters.find(el => !el.classList.contains('clone')),
+ count = counters.filter(el => el.classList.contains('clone')).length,
+ trace = cntrGrp.querySelector(`polyline.move-trace${selector}`);
+
+ let current = {
+ x: point.dataset.x,
+ y: point.dataset.y,
+ xAttr: point.getAttribute('x'),
+ yAttr: point.getAttribute('y')
+ },
+ previous = {
+ x: original.dataset.x,
+ y: original.dataset.y,
+ xAttr: original.getAttribute('x'),
+ yAttr: original.getAttribute('y')
+ }
+
+ counter = original.cloneNode();
+ counter.setAttributeNS(null, 'x', previous.xAttr);
+ counter.setAttributeNS(null, 'y', previous.yAttr);
+ counter.dataset.x = previous.x;
+ counter.dataset.y = previous.y;
+ counter.dataset.cloneOrder = count + 1;
+ counter.classList.add('clone');
+
+ original.setAttributeNS(null, 'x', current.xAttr);
+ original.setAttributeNS(null, 'y', current.yAttr);
+ original.dataset.x = current.x;
+ original.dataset.y = current.y;
+
+ if (!trace) {
+ trace = document.createElementNS(svgns, 'polyline');
+ points = `${previous.xAttr},${previous.yAttr} ${current.xAttr},${current.yAttr}`;
+
+ trace.dataset.troopNumber = troopNumber;
+ trace.dataset.troopAllegiance = troopAllegiance;
+ trace.classList.add('move-trace');
+
+ cntrGrp.prepend(trace);
+ } else {
+ points = `${trace.getAttribute('points')} ${current.xAttr},${current.yAttr}`;
+ }
+
+ trace.setAttributeNS(null, 'points', points);
+
+ counter.addEventListener('click', e => {
+ let selectedSoldier = document.querySelector('.soldier-record.selected');
+
+ if (selectedSoldier) {
+ let { troopNumber, troopAllegiance } = selectedSoldier.dataset,
+ selector = troopSelector(troopNumber, troopAllegiance);
+
+ if (counter.dataset.troopAllegiance == troopAllegiance && counter.dataset.troopNumber == troopNumber) {
+ let [xAttr, yAttr] = [counter.getAttribute('x'), counter.getAttribute('y')];
+ let points = trace.getAttribute('points').split(' ');
+
+ if (`${xAttr},${yAttr}` == points.at(0)) {
+ original.setAttributeNS(null, 'x', xAttr);
+ original.setAttributeNS(null, 'y', yAttr);
+ original.dataset.x = counter.dataset.x;
+ original.dataset.y = counter.dataset.y;
+
+ cntrGrp.querySelectorAll(`use${selector}.clone`).forEach(el => el.remove());
+ trace.remove();
+ } else {
+ let points = trace.getAttribute('points').split(' ').filter(p => p != `${xAttr},${yAttr}`);
+
+ trace.setAttributeNS(null, 'points', points.join(' '));
+ }
+
+ counter.remove();
+ }
+ }
+ });
+
+ } else {
+ counter = document.createElementNS(svgns, 'use'),
+
+ counter.setAttributeNS(null, 'href', `#t-${troopNumber}`);
+ counter.classList.add('counter');
+ counter.setAttributeNS(null, 'x', cx);
+ counter.setAttributeNS(null, 'y', cy);
+ counter.dataset.troopNumber = troopNumber;
+ counter.dataset.troopAllegiance = troopAllegiance;
+ counter.dataset.x = point.dataset.x;
+ counter.dataset.y = point.dataset.y;
+
+ counter.addEventListener('dblclick', e => {
+ let selectedSoldier = document.querySelector('.soldier-record.selected');
+
+ if (selectedSoldier) {
+ let trace = cntrGrp.querySelector(`polyline.move-trace${selector}`);
+ if (!trace) {
+ let {troopNumber, troopAllegiance} = selectedSoldier.dataset,
+ selector = troopSelector(troopNumber, troopAllegiance),
+ targetIsSelectedSoldier = [
+ e.target.dataset.troopNumber == troopNumber,
+ e.target.dataset.troopAllegiance == troopAllegiance
+ ].every(el => el);
+
+ if (targetIsSelectedSoldier) {
+ cntrGrp.querySelectorAll(`${selector}`).forEach(el => el.remove());
+ document.querySelectorAll(`#firing-arcs ${selector}`).forEach(el => el.remove());
+ }
+ }
+ }
+ });
+
+ counter.addEventListener('click', e => {
+ let selectedSoldier = document.querySelector('.soldier-record.selected');
+
+ if (selectedSoldier) {
+ let { troopNumber, troopAllegiance } = selectedSoldier.dataset,
+ selector = troopSelector(troopNumber, troopAllegiance);
+
+ if (counter.dataset.troopAllegiance == troopAllegiance && counter.dataset.troopNumber == troopNumber) {
+ let trace = cntrGrp.querySelector(`polyline.move-trace${selector}`);
+
+ if (trace) {
+ let points = trace.getAttribute('points').split(' ');
+ let [xAttr, yAttr] = points.at(-2).split(',');
+ let clone = cntrGrp.querySelector(`${selector}[x="${xAttr}"][y="${yAttr}"].clone`);
+
+ points.pop()
+
+ if (points.length >= 2) {
+ trace.setAttributeNS(null, 'points', points.join(' '));
+ } else {
+ trace.remove();
+ }
+
+ counter.setAttributeNS(null, 'x', clone.getAttribute('x'));
+ counter.setAttributeNS(null, 'y', clone.getAttribute('y'));
+ counter.dataset.x = clone.dataset.x;
+ counter.dataset.y = clone.dataset.y;
+
+ clone.remove();
+ }
+ }
+ }
+ });
+ }
+
+ // info.querySelector('#hex-count').textContent = '-';
+ // info.style.display = 'none';
+ // ptGrp.querySelectorAll('.active').forEach(el => el.removeAttribute('class'));
+ // svg.querySelectorAll('.sight-line').forEach(el => el.remove());
+
+ // document.querySelectorAll(`.counter${selector}`).forEach(el => el.remove());
+
+ // counter.addEventListener('mouseenter', e => {
+ // let selectedSoldier = document.querySelector('.soldier-record.selected');
+
+ // if (selectedSoldier) {
+ // let {troopNumber, troopAllegiance} = selectedSoldier.dataset,
+ // selector = troopSelector(troopNumber, troopAllegiance),
+ // source = document.querySelector(`circle.counter${selector}`),
+
+ // // TODO: use isEqualNode() method instead
+ // sourceAndTargetAreNotTheSame = [
+ // troopNumber != e.target.dataset.troopNumber,
+ // troopAllegiance != e.target.dataset.troopAllegiance
+ // ].some(el => el);
+
+ // if (source && sourceAndTargetAreNotTheSame) {
+ // let sightLine = document.createElementNS(svgns, 'line');
+
+ // sightLine.classList.add('sight-line');
+ // sightLine.setAttributeNS(null, 'x1', source.getAttribute('cx'));
+ // sightLine.setAttributeNS(null, 'y1', source.getAttribute('cy'));
+ // sightLine.setAttributeNS(null, 'x2', e.target.getAttribute('cx'));
+ // sightLine.setAttributeNS(null, 'y2', e.target.getAttribute('cy'));
+
+ // svg.appendChild(sightLine);
+ // }
+ // }
+ // });
+
+ // counter.addEventListener('mouseleave', e => {
+ // document.querySelectorAll('.sight-line').forEach(el => el.remove());
+ // });
+
+ // svg.insertBefore(counter, ptGrp);
+
+ // let symbCtr = document.createElementNS(svgns, 'use');
+
+ // symbCtr.setAttributeNS(null, 'href', '#troop-counter');
+ // symbCtr.setAttributeNS(null, 'x', cx);
+ // symbCtr.setAttributeNS(null, 'y', cy);
+
+ // cntrGrp.appendChild(symbCtr);
+
+ cntrGrp.appendChild(counter);
+ }
+ });
+
+ point.addEventListener('mouseover', e => {
+ let selectedSoldier = document.querySelector('.soldier-record.selected');
+
+ if (selectedSoldier) {
+ // e.target.classList.add('active');
+ let { troopNumber: tn, troopAllegiance: ta } = selectedSoldier.dataset;
+
+ let counter = svg.querySelector(`circle.counter[data-troop-number="${tn}"][data-troop-allegiance="${ta}"]`);
+ let sl = svg.querySelector('.sight-line');
+
+ if (counter && (!sl || sl.classList.contains('active'))) {
+ if (sl) {
+ info.querySelector('#hex-count').textContent = '-';
+ info.style.display = 'none';
+ ptGrp.querySelectorAll('.active').forEach(el => el.removeAttribute('class'));
+ svg.querySelectorAll('.sight-line').forEach(el => el.remove());
+ }
+
+ let source = ptGrp.querySelector(`use[data-x="${counter.dataset.x}"][data-y="${counter.dataset.y}"]`);
+ let [x1, y1] = [source.x.baseVal.value, source.y.baseVal.value];
+ let [x2, y2] = [e.target.x.baseVal.value, e.target.y.baseVal.value];
+
+ if (x1 !== x2 || y1 !== y2) {
+ let { x: svgX1, y: svgY1 } = ptGrpToSvgPt(x1, y1);
+ let { x: svgX2, y: svgY2 } = ptGrpToSvgPt(x2, y2);
+
+ let sightLine = document.createElementNS(svgns, 'line');
+
+ sightLine.classList.add('sight-line');
+ sightLine.classList.add('active');
+ sightLine.setAttributeNS(null, 'x1', svgX1);
+ sightLine.setAttributeNS(null, 'y1', svgY1);
+ sightLine.setAttributeNS(null, 'x2', svgX2);
+ sightLine.setAttributeNS(null, 'y2', svgY2);
+
+ // svg.insertBefore(sightLine, ptGrp);
+ document.getElementById('grid').appendChild(sightLine);
+
+ let coords = [
+ counter.dataset.x,
+ counter.dataset.y,
+ e.target.dataset.x,
+ e.target.dataset.y
+ ].map(n => parseInt(n));
+
+ info.querySelector('#hex-count').textContent = offset_distance(...coords);
+ info.style.display = 'block';
+
+ let lineCoords = linedraw(...coords);
+ lineCoords.shift();
+ let s = lineCoords.map(([x, y]) => `use[data-x="${x}"][data-y="${y}"]`).join(', ');
+ ptGrp.querySelectorAll(s).forEach(p => p.classList.add('active'));
+ }
+ }
+ }
+ });
+
+ // point.addEventListener('mouseout', e => {
+ // let sl = svg.querySelector('.sight-line.active');
+
+ // if (sl) {
+ // info.querySelector('#hex-count').textContent = '-';
+ // info.style.display = 'none';
+ // ptGrp.querySelectorAll('.active').forEach(el => el.removeAttribute('class'));
+ // svg.querySelectorAll('.sight-line').forEach(el => el.remove());
+ // }
+ // });
+
+ point.addEventListener('click', e => {
+ let sl = svg.querySelector('.sight-line');
+
+ if (sl) {
+ sl.classList.toggle('active');
+
+ if (sl.classList.contains('active')) {
+ point.dispatchEvent(new MouseEvent('mouseout'));
+ point.dispatchEvent(new MouseEvent('mouseover'));
+ }
+ }
+ });
+
+ 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);
+}));
+
document.querySelectorAll('.soldier-record').forEach(el =>
el.addEventListener('click', e => {
if (el.classList.contains('selected')) {
@@ -740,6 +850,17 @@ document.querySelectorAll('.end-move').forEach(el => el.addEventListener('click'
let selectedSoldier = document.querySelector('.soldier-record.selected');
if (selectedSoldier) {
+ let { troopNumber, troopAllegiance } = selectedSoldier.dataset;
+ let selector = troopSelector(troopNumber, troopAllegiance);
+ let trace = cntrGrp.querySelector(`polyline.move-trace${selector}`);
+
+ if (trace) {
+ trace.remove();
+ }
+
+ counterNodeList = cntrGrp.querySelectorAll(`use.counter.clone${selector}`);
+ counterNodeList.forEach(el => el.remove());
+
selectedSoldier.classList.toggle('selected');
selectedSoldier.classList.toggle('movement-ended');
}
diff --git a/style.css b/style.css
index aba6b7e..449405d 100644
--- a/style.css
+++ b/style.css
@@ -153,6 +153,11 @@ use[href="#point"].active {
opacity: 1;
}
+polyline.move-trace {
+ stroke: lightcoral;
+ fill: none;
+}
+
g#grid {
transform: translate(19px, 31px) scale(4);
}
@@ -193,12 +198,55 @@ image.map-scans {
stroke-width: 0.5in;
} */
-circle.counter[data-troop-allegiance="liao"] {
+use.counter.clone {
+ /* filter: saturate(40%) brightness(4); */
+ /* filter: grayscale(0.8) */
+}
+
+g.troop-counter {
+ /* fill: inherit; */
+ /* transform: translate(-7px, -7px); */
+ /* opacity: 0.5; */
+}
+
+g.troop-counter use[href="#counter-base"] {
+ /* transform: translate(-7px, -7px); */
+ transform: translate(-5px, -5px);
+}
+
+g.troop-counter text {
+ fill: white;
+ font-size: 12px;
+ font-weight: bold;
+ font-family: monospace;
+ cursor: default;
+ text-anchor: middle;
+ pointer-events: none;
+ user-select: none;
+ transform: translateY(4px);
+ stroke: none;
+}
+
+g#counters use[data-troop-allegiance="davion"] {
+ fill: red;
+}
+
+g#counters use[data-troop-allegiance="liao"] {
fill: green;
}
-circle.counter[data-troop-allegiance="davion"] {
- fill: red;
+g#counters use.clone {
+ stroke: white;
+ stroke-width: 0.5px;
+ stroke-dasharray: 1;
+}
+
+g#counters use[data-troop-allegiance="davion"].clone {
+ fill: rgb(255, 126, 126);
+}
+
+g#counters use[data-troop-allegiance="liao"].clone {
+ fill: rgb(130, 190, 130);
}
text.counter, #troop-counter text {