()
| 1497 | } |
| 1498 | |
| 1499 | createVisualConnections () { |
| 1500 | const visualConnections = this.campaign?.get('visualConnections') || [] |
| 1501 | if (!visualConnections.length) { return } |
| 1502 | |
| 1503 | const map = this.$el.find('.map') |
| 1504 | if (!map.length) { return } |
| 1505 | |
| 1506 | // Clear any existing layer (we use a fixed id to avoid duplicates) |
| 1507 | map.find('#visual-connections-svg').remove() |
| 1508 | this.$el.find('.visual-connection-handle').remove() |
| 1509 | |
| 1510 | // Use actual pixel size of the map as the SVG coordinate system |
| 1511 | const mapWidth = map.width() |
| 1512 | const mapHeight = map.height() |
| 1513 | if (!(mapWidth > 0 && mapHeight > 0)) { return } |
| 1514 | |
| 1515 | const svgNS = 'http://www.w3.org/2000/svg' |
| 1516 | const svg = document.createElementNS(svgNS, 'svg') |
| 1517 | $(svg) |
| 1518 | .attr({ |
| 1519 | id: 'visual-connections-svg', |
| 1520 | width: mapWidth, |
| 1521 | height: mapHeight, |
| 1522 | }) |
| 1523 | .css({ |
| 1524 | position: 'absolute', |
| 1525 | left: 0, |
| 1526 | top: 0, |
| 1527 | width: mapWidth, |
| 1528 | height: mapHeight, |
| 1529 | 'pointer-events': 'none', |
| 1530 | // Render visual connections beneath level markers/flags (z-index 2) but above the map background. |
| 1531 | 'z-index': 1, |
| 1532 | }) |
| 1533 | |
| 1534 | // Defaults from appearance-based colors; functional meaning only at use site |
| 1535 | const defaultConnectionColor = colors.black |
| 1536 | const defaultLockedColor = colors.grey |
| 1537 | const defaultCompleteColor = colors.lightGold |
| 1538 | const defaultActiveColor = colors.red |
| 1539 | const defaultOpacity = 0.7 |
| 1540 | |
| 1541 | // Convert 0–100% campaign coords -> pixels in this SVG. |
| 1542 | // X is from left; Y is from bottom (like level positions), so we flip it |
| 1543 | // before mapping into the SVG's top-left–based pixel space. |
| 1544 | const mapToSvgX = xPercent => (xPercent / 100) * mapWidth |
| 1545 | const mapToSvgY = yPercentFromBottom => { |
| 1546 | const yPercentFromTop = 100 - yPercentFromBottom |
| 1547 | return (yPercentFromTop / 100) * mapHeight |
| 1548 | } |
| 1549 | |
| 1550 | // Container for per-path markers (arrow heads, etc.) |
| 1551 | const defs = document.createElementNS(svgNS, 'defs') |
| 1552 | svg.appendChild(defs) |
| 1553 | |
| 1554 | let connectionIndex = 0 |
| 1555 | for (const conn of visualConnections) { |
| 1556 | const fromPos = conn?.fromPos |
no test coverage detected