Movement history
This commit is contained in:
parent
b03ab287e0
commit
fbb9663183
17
index.html
17
index.html
@ -145,12 +145,18 @@
|
|||||||
<defs>
|
<defs>
|
||||||
<polygon id="point" points="0,10 8.66,5 8.66,-5 0,-10 -8.66,-5 -8.66,5" />
|
<polygon id="point" points="0,10 8.66,5 8.66,-5 0,-10 -8.66,-5 -8.66,5" />
|
||||||
|
|
||||||
<symbol id="troop-counter" viewBox="-8 -8 16 16" width="15" height="15">
|
<symbol id="counter-base" viewBox="-5 -5 10 10" width="10" height="10">
|
||||||
<circle class="outer" cx="0" cy="0" r="8" />
|
<circle cx="0" cy="0" r="5" />
|
||||||
<circle class="inner" cx="0" cy="0" r="6" />
|
|
||||||
<text>1</text>
|
|
||||||
</symbol>
|
</symbol>
|
||||||
|
|
||||||
|
<g id="t-1" class="troop-counter"><use href="#counter-base" /><text>1</text></g>
|
||||||
|
<g id="t-2" class="troop-counter"><use href="#counter-base" /><text>2</text></g>
|
||||||
|
<g id="t-3" class="troop-counter"><use href="#counter-base" /><text>3</text></g>
|
||||||
|
<g id="t-4" class="troop-counter"><use href="#counter-base" /><text>4</text></g>
|
||||||
|
<g id="t-5" class="troop-counter"><use href="#counter-base" /><text>5</text></g>
|
||||||
|
<g id="t-6" class="troop-counter"><use href="#counter-base" /><text>6</text></g>
|
||||||
|
<g id="t-7" class="troop-counter"><use href="#counter-base" /><text>7</text></g>
|
||||||
|
|
||||||
<image id="numbers" href="rendered_numbers.png" width="182" height="22" />
|
<image id="numbers" href="rendered_numbers.png" width="182" height="22" />
|
||||||
|
|
||||||
<symbol id="n1" viewBox="1 0 17 22" width="17" height="22"><use href="#numbers" /></symbol>
|
<symbol id="n1" viewBox="1 0 17 22" width="17" height="22"><use href="#numbers" /></symbol>
|
||||||
@ -179,7 +185,6 @@
|
|||||||
|
|
||||||
<g id="grid">
|
<g id="grid">
|
||||||
<g id="points"></g>
|
<g id="points"></g>
|
||||||
<!-- <g id="firing-arcs"></g> -->
|
|
||||||
<g id="counters"></g>
|
<g id="counters"></g>
|
||||||
</g>
|
</g>
|
||||||
|
|
||||||
@ -228,7 +233,7 @@
|
|||||||
<!-- 1st Squad, 3rd Platoon, Bravo Company, 2nd Battalion<br>
|
<!-- 1st Squad, 3rd Platoon, Bravo Company, 2nd Battalion<br>
|
||||||
17th Kestral Mechanized Infantry -->
|
17th Kestral Mechanized Infantry -->
|
||||||
</p>
|
</p>
|
||||||
<div is="soldier-record-block" class="soldier-record" data-troop-number="1" data-troop-allegiance="davion">
|
<div is="soldier-record-block" class="soldier-record selected" data-troop-number="1" data-troop-allegiance="davion">
|
||||||
<span slot="troop-number">1</span>
|
<span slot="troop-number">1</span>
|
||||||
<span slot="primary-weapon-type">Rifle</span>
|
<span slot="primary-weapon-type">Rifle</span>
|
||||||
<span slot="primary-weapon-damage">4L</span>
|
<span slot="primary-weapon-damage">4L</span>
|
||||||
|
883
index.js
883
index.js
@ -59,387 +59,6 @@ let getPointCoords = (x, y) => {
|
|||||||
return [point.x.baseVal.value, point.y.baseVal.value]
|
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) {
|
function positionFiringArc(e) {
|
||||||
let activeFiringArc = document.querySelector('polygon.firing-arc.active');
|
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 =>
|
document.querySelectorAll('.soldier-record').forEach(el =>
|
||||||
el.addEventListener('click', e => {
|
el.addEventListener('click', e => {
|
||||||
if (el.classList.contains('selected')) {
|
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');
|
let selectedSoldier = document.querySelector('.soldier-record.selected');
|
||||||
|
|
||||||
if (selectedSoldier) {
|
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('selected');
|
||||||
selectedSoldier.classList.toggle('movement-ended');
|
selectedSoldier.classList.toggle('movement-ended');
|
||||||
}
|
}
|
||||||
|
54
style.css
54
style.css
@ -153,6 +153,11 @@ use[href="#point"].active {
|
|||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
polyline.move-trace {
|
||||||
|
stroke: lightcoral;
|
||||||
|
fill: none;
|
||||||
|
}
|
||||||
|
|
||||||
g#grid {
|
g#grid {
|
||||||
transform: translate(19px, 31px) scale(4);
|
transform: translate(19px, 31px) scale(4);
|
||||||
}
|
}
|
||||||
@ -193,12 +198,55 @@ image.map-scans {
|
|||||||
stroke-width: 0.5in;
|
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;
|
fill: green;
|
||||||
}
|
}
|
||||||
|
|
||||||
circle.counter[data-troop-allegiance="davion"] {
|
g#counters use.clone {
|
||||||
fill: red;
|
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 {
|
text.counter, #troop-counter text {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user