@ntmats/hc-ui 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -14
- package/dist/index.d.ts +64 -56
- package/dist/index.js +165 -164
- package/dist/styles.css +90 -67
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -103,7 +103,7 @@ function SegmentedControl({
|
|
|
103
103
|
);
|
|
104
104
|
}
|
|
105
105
|
var classes = (...names) => names.filter(Boolean).join(" ");
|
|
106
|
-
var Button = forwardRef(function Button2({ variant = "secondary", size = "md", icon, iconEnd, fullWidth = false, loading = false, type = "button", className, children, onClick, ...props }, ref) {
|
|
106
|
+
var Button = forwardRef(function Button2({ variant = "secondary", size = "md", icon, iconEnd, fullWidth = false, loading = false, wrap = false, type = "button", className, children, onClick, ...props }, ref) {
|
|
107
107
|
const handleClick = (event) => {
|
|
108
108
|
if (loading) event.preventDefault();
|
|
109
109
|
else onClick?.(event);
|
|
@@ -121,6 +121,7 @@ var Button = forwardRef(function Button2({ variant = "secondary", size = "md", i
|
|
|
121
121
|
"data-variant": variant,
|
|
122
122
|
"data-size": size,
|
|
123
123
|
"data-full-width": fullWidth || void 0,
|
|
124
|
+
"data-wrap": wrap || void 0,
|
|
124
125
|
children: [
|
|
125
126
|
loading ? /* @__PURE__ */ jsx("span", { className: "hc-ui-button__spinner", "aria-hidden": "true" }) : icon && /* @__PURE__ */ jsx("span", { className: "hc-ui-button__icon", "aria-hidden": "true", children: icon }),
|
|
126
127
|
/* @__PURE__ */ jsx("span", { className: "hc-ui-button__label", children }),
|
|
@@ -647,10 +648,9 @@ function useRovingRadios(options, value) {
|
|
|
647
648
|
});
|
|
648
649
|
return item;
|
|
649
650
|
}
|
|
650
|
-
function SheetOptions({ label, options, value, onChange,
|
|
651
|
+
function SheetOptions({ label, options, value, onChange, className, ...props }) {
|
|
651
652
|
const item = useRovingRadios(options, value);
|
|
652
|
-
|
|
653
|
-
return /* @__PURE__ */ jsx("div", { ...props, className: classes("hc-ui-sheet-options", className), role: "radiogroup", "aria-label": label, "data-variant": list ? "list" : void 0, children: options.map((option, index) => {
|
|
653
|
+
return /* @__PURE__ */ jsx("div", { ...props, className: classes("hc-ui-sheet-options", className), role: "radiogroup", "aria-label": label, children: options.map((option, index) => {
|
|
654
654
|
const on = option.value === value;
|
|
655
655
|
return /* @__PURE__ */ jsxs(
|
|
656
656
|
"button",
|
|
@@ -669,7 +669,7 @@ function SheetOptions({ label, options, value, onChange, variant = "card", class
|
|
|
669
669
|
/* @__PURE__ */ jsx("span", { className: "hc-ui-sheet-options__label", children: option.label }),
|
|
670
670
|
option.hint && /* @__PURE__ */ jsx("span", { className: "hc-ui-sheet-options__hint", children: option.hint })
|
|
671
671
|
] }),
|
|
672
|
-
/* @__PURE__ */ jsx("span", { className: "hc-ui-sheet-options__check", "aria-hidden": "true", children: on && /* @__PURE__ */ jsx("svg", { width:
|
|
672
|
+
/* @__PURE__ */ jsx("span", { className: "hc-ui-sheet-options__check", "aria-hidden": "true", children: on && /* @__PURE__ */ jsx("svg", { width: 18, height: 18, viewBox: "0 0 24 24", fill: "none", strokeWidth: 2.4, "aria-hidden": "true", focusable: "false", children: /* @__PURE__ */ jsx("path", { d: "M5 13L9 17L19 7", stroke: "currentColor", strokeLinecap: "round", strokeLinejoin: "round" }) }) })
|
|
673
673
|
]
|
|
674
674
|
},
|
|
675
675
|
option.value
|
|
@@ -1555,6 +1555,7 @@ var CombatDialView = forwardRef(function CombatDialView2({
|
|
|
1555
1555
|
currentClick,
|
|
1556
1556
|
labels,
|
|
1557
1557
|
presentation = "default",
|
|
1558
|
+
density = "comfortable",
|
|
1558
1559
|
onClickChange,
|
|
1559
1560
|
onSelectPoint,
|
|
1560
1561
|
tip,
|
|
@@ -1658,7 +1659,7 @@ var CombatDialView = forwardRef(function CombatDialView2({
|
|
|
1658
1659
|
COMBAT_DIAL_STATS.map((stat) => /* @__PURE__ */ jsx("div", { className: "hc-ui-combat-dial__cell", "data-kind": "ko", children: labels.koCell }, stat))
|
|
1659
1660
|
] })
|
|
1660
1661
|
] }, rowIndex);
|
|
1661
|
-
return /* @__PURE__ */ jsxs("section", { ref, className: "hc-ui-combat-dial", "data-presentation": presentation, "data-static": flag(!tracksClick), children: [
|
|
1662
|
+
return /* @__PURE__ */ jsxs("section", { ref, className: "hc-ui-combat-dial", "data-presentation": presentation, "data-density": density === "compact" ? "compact" : void 0, "data-static": flag(!tracksClick), children: [
|
|
1662
1663
|
presentation !== "printed" && /* @__PURE__ */ jsxs("header", { className: "hc-ui-combat-dial__header", children: [
|
|
1663
1664
|
/* @__PURE__ */ jsxs("div", { className: "hc-ui-combat-dial__header-top", children: [
|
|
1664
1665
|
/* @__PURE__ */ jsx("span", { className: "hc-ui-combat-dial__label", children: labels.heading }),
|
|
@@ -1693,11 +1694,11 @@ var CombatDialView = forwardRef(function CombatDialView2({
|
|
|
1693
1694
|
tip && tipSlot
|
|
1694
1695
|
] });
|
|
1695
1696
|
});
|
|
1696
|
-
var
|
|
1697
|
-
var
|
|
1698
|
-
var
|
|
1699
|
-
var
|
|
1700
|
-
function
|
|
1697
|
+
var PHYSICAL_CARD_WIDTH = 375;
|
|
1698
|
+
var PHYSICAL_CARD_HEIGHT = 525;
|
|
1699
|
+
var PHYSICAL_CARD_BACK_TABLE_HEIGHT = 440;
|
|
1700
|
+
var PHYSICAL_CARD_BYSTANDER_DIAL_STEP = { x: 7, y: 24 };
|
|
1701
|
+
function nextPhysicalCardDistribution(pages, overflowing) {
|
|
1701
1702
|
const last = pages.length ? Math.max(...pages) : 0;
|
|
1702
1703
|
for (let table = 0; table <= last; table += 1) {
|
|
1703
1704
|
if (!overflowing(table)) continue;
|
|
@@ -1709,7 +1710,7 @@ function nextDistribution(pages, overflowing) {
|
|
|
1709
1710
|
}
|
|
1710
1711
|
return null;
|
|
1711
1712
|
}
|
|
1712
|
-
function
|
|
1713
|
+
function physicalCardBackCount(pages) {
|
|
1713
1714
|
const last = pages.length ? Math.max(...pages) : 0;
|
|
1714
1715
|
return Math.max(1, Math.ceil(last / 2));
|
|
1715
1716
|
}
|
|
@@ -1721,7 +1722,7 @@ var CAN_DEVICE_TILT = "(hover: none) and (pointer: coarse) and (prefers-reduced-
|
|
|
1721
1722
|
var DEVICE_RANGE_DEG = 18;
|
|
1722
1723
|
var BASELINE_DRIFT = 0.01;
|
|
1723
1724
|
var DEVICE_SMOOTHING = 0.2;
|
|
1724
|
-
function
|
|
1725
|
+
function physicalCardTiltAt(x, y, damping = 1) {
|
|
1725
1726
|
const offsetX = (x - 0.5) * 2 * damping;
|
|
1726
1727
|
const offsetY = (y - 0.5) * 2 * damping;
|
|
1727
1728
|
return {
|
|
@@ -1736,7 +1737,7 @@ function tiltAt(x, y, damping = 1) {
|
|
|
1736
1737
|
var PARALLAX_ART_PX = 4;
|
|
1737
1738
|
var PARALLAX_HEADER_PX = 1.5;
|
|
1738
1739
|
var CARD_ART_SELECTOR = ".hc-ui-physical-card__token, .hc-ui-physical-card__terrain-art, .hc-ui-physical-card__bystander-token";
|
|
1739
|
-
function
|
|
1740
|
+
function physicalCardTiltStyles(tilt) {
|
|
1740
1741
|
return {
|
|
1741
1742
|
card: `perspective(1100px) rotateX(${tilt.rotateX}deg) rotateY(${tilt.rotateY}deg)`,
|
|
1742
1743
|
art: `${tilt.parallaxX * PARALLAX_ART_PX}px ${tilt.parallaxY * PARALLAX_ART_PX}px`,
|
|
@@ -1748,7 +1749,7 @@ var eachIn = (root, selector, write) => {
|
|
|
1748
1749
|
root.querySelectorAll(selector).forEach(write);
|
|
1749
1750
|
};
|
|
1750
1751
|
function applyTilt(root, tilt) {
|
|
1751
|
-
const styles =
|
|
1752
|
+
const styles = physicalCardTiltStyles(tilt);
|
|
1752
1753
|
root.dataset.tilt = "";
|
|
1753
1754
|
eachIn(root, ".hc-ui-physical-card-tilt", (element) => {
|
|
1754
1755
|
element.style.transform = styles.card;
|
|
@@ -1772,7 +1773,7 @@ function clearTilt(root) {
|
|
|
1772
1773
|
element.style.translate = "";
|
|
1773
1774
|
});
|
|
1774
1775
|
}
|
|
1775
|
-
function
|
|
1776
|
+
function physicalCardDevicePosition(beta, gamma, baseline, screenAngle = 0) {
|
|
1776
1777
|
const clampAxis = (value) => Math.max(-1, Math.min(1, value / DEVICE_RANGE_DEG));
|
|
1777
1778
|
const front = clampAxis(beta - baseline.beta);
|
|
1778
1779
|
const side = clampAxis(gamma - baseline.gamma);
|
|
@@ -1780,7 +1781,7 @@ function devicePosition(beta, gamma, baseline, screenAngle = 0) {
|
|
|
1780
1781
|
return { x: (horizontal + 1) / 2, y: (vertical + 1) / 2 };
|
|
1781
1782
|
}
|
|
1782
1783
|
var motionAsked = false;
|
|
1783
|
-
function
|
|
1784
|
+
function requestPhysicalCardMotionAccess() {
|
|
1784
1785
|
const request = typeof DeviceOrientationEvent === "undefined" ? void 0 : DeviceOrientationEvent.requestPermission;
|
|
1785
1786
|
if (motionAsked || !request) return;
|
|
1786
1787
|
motionAsked = true;
|
|
@@ -1801,10 +1802,10 @@ function useDeviceTilt(ref, enabled) {
|
|
|
1801
1802
|
baseline ?? (baseline = { beta: event.beta, gamma: event.gamma });
|
|
1802
1803
|
baseline.beta += (event.beta - baseline.beta) * BASELINE_DRIFT;
|
|
1803
1804
|
baseline.gamma += (event.gamma - baseline.gamma) * BASELINE_DRIFT;
|
|
1804
|
-
const target =
|
|
1805
|
+
const target = physicalCardDevicePosition(event.beta, event.gamma, baseline, screen.orientation?.angle ?? 0);
|
|
1805
1806
|
position = { x: position.x + (target.x - position.x) * DEVICE_SMOOTHING, y: position.y + (target.y - position.y) * DEVICE_SMOOTHING };
|
|
1806
1807
|
cancelAnimationFrame(frame);
|
|
1807
|
-
frame = requestAnimationFrame(() => applyTilt(element,
|
|
1808
|
+
frame = requestAnimationFrame(() => applyTilt(element, physicalCardTiltAt(position.x, position.y)));
|
|
1808
1809
|
};
|
|
1809
1810
|
const onTouchStart = () => {
|
|
1810
1811
|
touching = true;
|
|
@@ -1835,7 +1836,7 @@ function useCardTilt() {
|
|
|
1835
1836
|
const slot = event.currentTarget;
|
|
1836
1837
|
const rect = slot.getBoundingClientRect();
|
|
1837
1838
|
const overDial = event.target.closest(CARD_DIAL_SELECTOR);
|
|
1838
|
-
const tilt =
|
|
1839
|
+
const tilt = physicalCardTiltAt((event.clientX - rect.left) / rect.width, (event.clientY - rect.top) / rect.height, overDial ? DIAL_DAMPING : 1);
|
|
1839
1840
|
cancelAnimationFrame(frame.current);
|
|
1840
1841
|
frame.current = requestAnimationFrame(() => applyTilt(slot, tilt));
|
|
1841
1842
|
};
|
|
@@ -1874,9 +1875,9 @@ function useCardScale(fitHeight, focus, scrollerSelector) {
|
|
|
1874
1875
|
if (scroller) observer.observe(scroller);
|
|
1875
1876
|
return () => observer.disconnect();
|
|
1876
1877
|
}, [fitHeight, focus, scrollerSelector]);
|
|
1877
|
-
const columns = !focus && width >=
|
|
1878
|
-
const byWidth = width ? Math.min(MAX_CARD_SCALE, (width - CARD_GAP * (columns - 1)) / columns /
|
|
1879
|
-
const scale = (fitHeight || focus) && height > 0 ? Math.max(0.6, Math.min(byWidth, height /
|
|
1878
|
+
const columns = !focus && width >= PHYSICAL_CARD_WIDTH * SIDE_BY_SIDE_MIN_SCALE * 2 + CARD_GAP ? 2 : 1;
|
|
1879
|
+
const byWidth = width ? Math.min(MAX_CARD_SCALE, (width - CARD_GAP * (columns - 1)) / columns / PHYSICAL_CARD_WIDTH) : 1;
|
|
1880
|
+
const scale = (fitHeight || focus) && height > 0 ? Math.max(0.6, Math.min(byWidth, height / PHYSICAL_CARD_HEIGHT)) : byWidth;
|
|
1880
1881
|
return { ref, scale };
|
|
1881
1882
|
}
|
|
1882
1883
|
function useFitText(text, max, min) {
|
|
@@ -1951,9 +1952,9 @@ function PhysicalCardFocusView({ label, onClose, children }) {
|
|
|
1951
1952
|
}, 0);
|
|
1952
1953
|
window.addEventListener("pointerup", release, { once: true });
|
|
1953
1954
|
window.addEventListener("pointercancel", release, { once: true });
|
|
1954
|
-
window.addEventListener("touchend",
|
|
1955
|
+
window.addEventListener("touchend", requestPhysicalCardMotionAccess);
|
|
1955
1956
|
return () => {
|
|
1956
|
-
window.removeEventListener("touchend",
|
|
1957
|
+
window.removeEventListener("touchend", requestPhysicalCardMotionAccess);
|
|
1957
1958
|
window.removeEventListener("pointerup", release);
|
|
1958
1959
|
window.removeEventListener("pointercancel", release);
|
|
1959
1960
|
dialog.close();
|
|
@@ -2035,7 +2036,7 @@ var PhysicalCardView = forwardRef(function PhysicalCardView2({
|
|
|
2035
2036
|
const pages = distribution.key === contentKey && distribution.pages.length === rules.length ? distribution.pages : zeros();
|
|
2036
2037
|
const tables = useRef([]);
|
|
2037
2038
|
useLayoutEffect(() => {
|
|
2038
|
-
const next =
|
|
2039
|
+
const next = nextPhysicalCardDistribution(pages, (table) => {
|
|
2039
2040
|
const element = tables.current[table];
|
|
2040
2041
|
return !!element && element.scrollHeight > element.clientHeight + 1;
|
|
2041
2042
|
});
|
|
@@ -2171,17 +2172,17 @@ var PhysicalCardView = forwardRef(function PhysicalCardView2({
|
|
|
2171
2172
|
] }),
|
|
2172
2173
|
/* @__PURE__ */ jsx("div", { className: "hc-ui-physical-card__bystander-name", children: bystander.name }),
|
|
2173
2174
|
bystander.dial && /* @__PURE__ */ jsx("div", { className: "hc-ui-physical-card__bystander-dial", role: "group", "aria-label": bystander.dial.label, children: bystander.dial.cells.map((cell, row) => {
|
|
2174
|
-
const position = { left: row *
|
|
2175
|
+
const position = { left: row * PHYSICAL_CARD_BYSTANDER_DIAL_STEP.x, top: row * PHYSICAL_CARD_BYSTANDER_DIAL_STEP.y };
|
|
2175
2176
|
const cellStyle = { ...position, backgroundColor: cell.background, color: cell.color };
|
|
2176
2177
|
const cellProps = { className: "hc-ui-physical-card__bystander-value", "data-special": flag(cell.special), style: cellStyle, "aria-label": cell.label };
|
|
2177
2178
|
if (!cell.tip) return /* @__PURE__ */ jsx("span", { ...cellProps, role: "img", children: cell.value }, cell.stat);
|
|
2178
2179
|
return /* @__PURE__ */ jsx("button", { type: "button", ...cellProps, "data-tip": "", ...tipProps(cell.tip), children: cell.value }, cell.stat);
|
|
2179
2180
|
}) })
|
|
2180
2181
|
] }) });
|
|
2181
|
-
const backs =
|
|
2182
|
+
const backs = physicalCardBackCount(pages);
|
|
2182
2183
|
const shownSide = Math.min(side, backs);
|
|
2183
2184
|
const nextSide = (shownSide + 1) % (backs + 1);
|
|
2184
|
-
const style = { "--hc-card-scale": scale, "--hc-card-front-table-height": `${layout.frontTableHeight}px`, "--hc-card-back-table-height": `${
|
|
2185
|
+
const style = { "--hc-card-scale": scale, "--hc-card-front-table-height": `${layout.frontTableHeight}px`, "--hc-card-back-table-height": `${PHYSICAL_CARD_BACK_TABLE_HEIGHT}px`, "--hc-card-width": `${PHYSICAL_CARD_WIDTH}px`, "--hc-card-height": `${PHYSICAL_CARD_HEIGHT}px` };
|
|
2185
2186
|
return /* @__PURE__ */ jsxs("div", { className: "hc-ui-physical-cards", "data-focus": flag(focus), ref: setWrapRef, style, ...longPress, children: [
|
|
2186
2187
|
/* @__PURE__ */ jsxs(CardFrame, { side: "front", backgroundSrc: images.front, legacySrc: images.legacy, label: labels.front, away: focus && shownSide !== 0, children: [
|
|
2187
2188
|
layout.teamUp && /* @__PURE__ */ jsx("span", { className: "hc-ui-physical-card__team-up", "aria-hidden": true }),
|
|
@@ -2238,8 +2239,8 @@ var PhysicalCardView = forwardRef(function PhysicalCardView2({
|
|
|
2238
2239
|
tip && tipSlot
|
|
2239
2240
|
] });
|
|
2240
2241
|
});
|
|
2241
|
-
var
|
|
2242
|
-
var EDGE_SIZE =
|
|
2242
|
+
var MAP_TILE_SIZE = 64;
|
|
2243
|
+
var EDGE_SIZE = MAP_TILE_SIZE / 16;
|
|
2243
2244
|
var WALL_SIZE = EDGE_SIZE * 2;
|
|
2244
2245
|
var MAX_EDGES = 4;
|
|
2245
2246
|
var HINDERING_TILE_COLOR = "#B4ECB4";
|
|
@@ -2249,9 +2250,9 @@ var OBSCURING_TILE_COLOR = "#E8E8E8";
|
|
|
2249
2250
|
var WINDOW_TILE_COLOR = "#dee8eb";
|
|
2250
2251
|
var SPECIAL_TILE_COLOR = "#FFD394";
|
|
2251
2252
|
var STARTING_ZONE_TILE_COLOR = "#fff8fa";
|
|
2252
|
-
var
|
|
2253
|
-
var
|
|
2254
|
-
clear:
|
|
2253
|
+
var MAP_ELEVATION_TILE_COLORS = ["#FFFFFF", "#FB7A7F", "#D0424C", "#B02020", "#902020", "#702020"];
|
|
2254
|
+
var MAP_TERRAIN_TILE_COLORS = {
|
|
2255
|
+
clear: MAP_ELEVATION_TILE_COLORS[0],
|
|
2255
2256
|
hindering: HINDERING_TILE_COLOR,
|
|
2256
2257
|
blocking: BLOCKING_TILE_COLOR,
|
|
2257
2258
|
water: WATER_TILE_COLOR,
|
|
@@ -2260,7 +2261,7 @@ var TERRAIN_TILE_COLORS = {
|
|
|
2260
2261
|
special: SPECIAL_TILE_COLOR,
|
|
2261
2262
|
special2: SPECIAL_TILE_COLOR
|
|
2262
2263
|
};
|
|
2263
|
-
var
|
|
2264
|
+
var MAP_TERRAIN_EDGE_COLORS = {
|
|
2264
2265
|
clear: "#FFFFFF",
|
|
2265
2266
|
hindering: "#3ACE3A",
|
|
2266
2267
|
blocking: "#76491E",
|
|
@@ -2271,9 +2272,9 @@ var TERRAIN_EDGE_COLORS = {
|
|
|
2271
2272
|
special2: "#FF7600"
|
|
2272
2273
|
};
|
|
2273
2274
|
var STARTING_ZONE_EDGE_COLOR = "#D560A9";
|
|
2274
|
-
var
|
|
2275
|
-
var
|
|
2276
|
-
var
|
|
2275
|
+
var MAP_ELEVATION_EDGE_COLOR = "#FF0000";
|
|
2276
|
+
var MAP_INDOOR_EDGE_COLOR = "#FFFF00";
|
|
2277
|
+
var MAP_WALL_COLORS = {
|
|
2277
2278
|
normal: "#000000",
|
|
2278
2279
|
glass: "#E59400",
|
|
2279
2280
|
glass2: "#33BBFF",
|
|
@@ -2316,8 +2317,8 @@ var Tile = class {
|
|
|
2316
2317
|
else if (this.terrain === "window") this.fillStyle = WINDOW_TILE_COLOR;
|
|
2317
2318
|
else if (this.terrain === "special" || this.terrain === "special2") this.fillStyle = SPECIAL_TILE_COLOR;
|
|
2318
2319
|
else if (this.isStartingZone || this.isStartingZone4p) this.fillStyle = STARTING_ZONE_TILE_COLOR;
|
|
2319
|
-
else if (typeof this.elevation === "string") this.fillStyle =
|
|
2320
|
-
else this.fillStyle =
|
|
2320
|
+
else if (typeof this.elevation === "string") this.fillStyle = MAP_ELEVATION_TILE_COLORS[1];
|
|
2321
|
+
else this.fillStyle = MAP_ELEVATION_TILE_COLORS[this.elevation - 1] ?? MAP_ELEVATION_TILE_COLORS[0];
|
|
2321
2322
|
}
|
|
2322
2323
|
addEdge(edgeType, strokeStyle, dashed) {
|
|
2323
2324
|
while (this.edges[edgeType]) edgeType += 4;
|
|
@@ -2332,7 +2333,7 @@ var Tile = class {
|
|
|
2332
2333
|
}
|
|
2333
2334
|
draw(ctx) {
|
|
2334
2335
|
ctx.fillStyle = this.fillStyle;
|
|
2335
|
-
ctx.fillRect(this.x *
|
|
2336
|
+
ctx.fillRect(this.x * MAP_TILE_SIZE, this.y * MAP_TILE_SIZE, MAP_TILE_SIZE, MAP_TILE_SIZE);
|
|
2336
2337
|
}
|
|
2337
2338
|
drawEdges(ctx) {
|
|
2338
2339
|
const edgeLevels = [-1, -1, -1, -1];
|
|
@@ -2366,27 +2367,27 @@ var Tile = class {
|
|
|
2366
2367
|
const oBottom = this.hasEdge(3 /* BOTTOM */ + level * 4) ? 0 : overhangSize;
|
|
2367
2368
|
switch (edgeType) {
|
|
2368
2369
|
case 0 /* LEFT */:
|
|
2369
|
-
x0 = x1 = this.x *
|
|
2370
|
-
y0 = this.y *
|
|
2371
|
-
y1 = (this.y + 1) *
|
|
2370
|
+
x0 = x1 = this.x * MAP_TILE_SIZE + EDGE_SIZE / 2 + EDGE_SIZE * level;
|
|
2371
|
+
y0 = this.y * MAP_TILE_SIZE - oTop;
|
|
2372
|
+
y1 = (this.y + 1) * MAP_TILE_SIZE + oBottom;
|
|
2372
2373
|
lineDashOffset = oTop;
|
|
2373
2374
|
break;
|
|
2374
2375
|
case 1 /* RIGHT */:
|
|
2375
|
-
x0 = x1 = (this.x + 1) *
|
|
2376
|
-
y0 = this.y *
|
|
2377
|
-
y1 = (this.y + 1) *
|
|
2376
|
+
x0 = x1 = (this.x + 1) * MAP_TILE_SIZE - EDGE_SIZE / 2 - EDGE_SIZE * level;
|
|
2377
|
+
y0 = this.y * MAP_TILE_SIZE - oTop;
|
|
2378
|
+
y1 = (this.y + 1) * MAP_TILE_SIZE + oBottom;
|
|
2378
2379
|
lineDashOffset = oTop;
|
|
2379
2380
|
break;
|
|
2380
2381
|
case 2 /* TOP */:
|
|
2381
|
-
x0 = this.x *
|
|
2382
|
-
y0 = y1 = this.y *
|
|
2383
|
-
x1 = (this.x + 1) *
|
|
2382
|
+
x0 = this.x * MAP_TILE_SIZE - oLeft;
|
|
2383
|
+
y0 = y1 = this.y * MAP_TILE_SIZE + EDGE_SIZE / 2 + EDGE_SIZE * level;
|
|
2384
|
+
x1 = (this.x + 1) * MAP_TILE_SIZE + oRight;
|
|
2384
2385
|
lineDashOffset = oLeft;
|
|
2385
2386
|
break;
|
|
2386
2387
|
case 3 /* BOTTOM */:
|
|
2387
|
-
x0 = this.x *
|
|
2388
|
-
y0 = y1 = (this.y + 1) *
|
|
2389
|
-
x1 = (this.x + 1) *
|
|
2388
|
+
x0 = this.x * MAP_TILE_SIZE - oLeft;
|
|
2389
|
+
y0 = y1 = (this.y + 1) * MAP_TILE_SIZE - EDGE_SIZE / 2 - EDGE_SIZE * level;
|
|
2390
|
+
x1 = (this.x + 1) * MAP_TILE_SIZE + oRight;
|
|
2390
2391
|
lineDashOffset = oLeft;
|
|
2391
2392
|
break;
|
|
2392
2393
|
}
|
|
@@ -2410,33 +2411,33 @@ var Tile = class {
|
|
|
2410
2411
|
}
|
|
2411
2412
|
drawRamp(ctx, angle) {
|
|
2412
2413
|
ctx.save();
|
|
2413
|
-
ctx.translate(this.x *
|
|
2414
|
+
ctx.translate(this.x * MAP_TILE_SIZE + MAP_TILE_SIZE / 2, this.y * MAP_TILE_SIZE + MAP_TILE_SIZE / 2);
|
|
2414
2415
|
ctx.rotate(angle);
|
|
2415
2416
|
ctx.beginPath();
|
|
2416
2417
|
ctx.moveTo(0, EDGE_SIZE);
|
|
2417
|
-
ctx.lineTo(-(
|
|
2418
|
-
ctx.lineTo(
|
|
2418
|
+
ctx.lineTo(-(MAP_TILE_SIZE / 2 - EDGE_SIZE * 2), MAP_TILE_SIZE / 2 - EDGE_SIZE);
|
|
2419
|
+
ctx.lineTo(MAP_TILE_SIZE / 2 - EDGE_SIZE * 2, MAP_TILE_SIZE / 2 - EDGE_SIZE);
|
|
2419
2420
|
ctx.closePath();
|
|
2420
2421
|
ctx.lineWidth = EDGE_SIZE * 2;
|
|
2421
|
-
ctx.strokeStyle =
|
|
2422
|
+
ctx.strokeStyle = MAP_ELEVATION_EDGE_COLOR;
|
|
2422
2423
|
ctx.stroke();
|
|
2423
|
-
ctx.fillStyle =
|
|
2424
|
+
ctx.fillStyle = MAP_ELEVATION_TILE_COLORS[1];
|
|
2424
2425
|
ctx.fill();
|
|
2425
2426
|
ctx.font = "18px Arial";
|
|
2426
2427
|
ctx.fillStyle = "#000000";
|
|
2427
2428
|
ctx.textAlign = "center";
|
|
2428
2429
|
ctx.textBaseline = "middle";
|
|
2429
|
-
ctx.fillText(String(this.elevation), 0,
|
|
2430
|
+
ctx.fillText(String(this.elevation), 0, MAP_TILE_SIZE / 4 + 4);
|
|
2430
2431
|
ctx.restore();
|
|
2431
2432
|
}
|
|
2432
2433
|
drawLabel(ctx) {
|
|
2433
2434
|
if (this.hideLabel || this.x !== 0 && this.y !== 0 && !this.label) return;
|
|
2434
2435
|
let text = "";
|
|
2435
2436
|
ctx.save();
|
|
2436
|
-
ctx.translate(this.x *
|
|
2437
|
+
ctx.translate(this.x * MAP_TILE_SIZE + MAP_TILE_SIZE / 2, this.y * MAP_TILE_SIZE + MAP_TILE_SIZE / 2);
|
|
2437
2438
|
if (this.label) {
|
|
2438
2439
|
text = this.label.text;
|
|
2439
|
-
ctx.fillStyle = this.label.color ??
|
|
2440
|
+
ctx.fillStyle = this.label.color ?? MAP_ELEVATION_EDGE_COLOR;
|
|
2440
2441
|
ctx.rotate((this.label.rotate ?? 0) * Math.PI / 180);
|
|
2441
2442
|
} else {
|
|
2442
2443
|
if (this.y === 0) text = String.fromCharCode("A".charCodeAt(0) + this.x);
|
|
@@ -2503,10 +2504,10 @@ var HCMap = class {
|
|
|
2503
2504
|
}
|
|
2504
2505
|
tryAddEdge(t0, t1, edgeType) {
|
|
2505
2506
|
if (this.type === "indoorOutdoor" && t0.type === "indoor" && (t1 == null || t1.type === "outdoor")) {
|
|
2506
|
-
t0.addEdge(edgeType,
|
|
2507
|
+
t0.addEdge(edgeType, MAP_INDOOR_EDGE_COLOR, false);
|
|
2507
2508
|
}
|
|
2508
2509
|
if (typeof t0.elevation === "number" && t0.elevation > 1 && (t1 == null || t0.elevation !== t1.elevation)) {
|
|
2509
|
-
t0.addEdge(edgeType,
|
|
2510
|
+
t0.addEdge(edgeType, MAP_ELEVATION_EDGE_COLOR, false);
|
|
2510
2511
|
}
|
|
2511
2512
|
if (t0.isStartingZone && (t1 == null || !t1.isStartingZone)) {
|
|
2512
2513
|
t0.addEdge(edgeType, STARTING_ZONE_EDGE_COLOR, false);
|
|
@@ -2516,7 +2517,7 @@ var HCMap = class {
|
|
|
2516
2517
|
}
|
|
2517
2518
|
if (t0.terrain !== "clear" && (t1 == null || t0.terrain !== t1.terrain || t0.type !== t1.type || t0.elevation !== t1.elevation || t0.isStartingZone !== t1.isStartingZone || t0.isStartingZone4p !== t1.isStartingZone4p || t0.group !== t1.group)) {
|
|
2518
2519
|
const dashed = t0.terrain === "window" || t0.terrain === "special2";
|
|
2519
|
-
t0.addEdge(edgeType,
|
|
2520
|
+
t0.addEdge(edgeType, MAP_TERRAIN_EDGE_COLORS[t0.terrain] ?? "#FFFFFF", dashed);
|
|
2520
2521
|
}
|
|
2521
2522
|
}
|
|
2522
2523
|
draw(ctx) {
|
|
@@ -2531,7 +2532,7 @@ var HCMap = class {
|
|
|
2531
2532
|
ctx.lineWidth = 1;
|
|
2532
2533
|
for (let y = 0; y < this.height; y++) {
|
|
2533
2534
|
for (let x = 0; x < this.width; x++) {
|
|
2534
|
-
ctx.strokeRect(x *
|
|
2535
|
+
ctx.strokeRect(x * MAP_TILE_SIZE, y * MAP_TILE_SIZE, MAP_TILE_SIZE, MAP_TILE_SIZE);
|
|
2535
2536
|
}
|
|
2536
2537
|
}
|
|
2537
2538
|
ctx.restore();
|
|
@@ -2542,12 +2543,12 @@ var HCMap = class {
|
|
|
2542
2543
|
}
|
|
2543
2544
|
for (const z of this.ziplines) {
|
|
2544
2545
|
ctx.save();
|
|
2545
|
-
ctx.strokeStyle =
|
|
2546
|
+
ctx.strokeStyle = MAP_WALL_COLORS.normal;
|
|
2546
2547
|
ctx.lineCap = "round";
|
|
2547
2548
|
ctx.lineWidth = 4;
|
|
2548
2549
|
ctx.beginPath();
|
|
2549
|
-
ctx.moveTo(z.x0 *
|
|
2550
|
-
ctx.lineTo(z.x1 *
|
|
2550
|
+
ctx.moveTo(z.x0 * MAP_TILE_SIZE + MAP_TILE_SIZE / 2, z.y0 * MAP_TILE_SIZE + MAP_TILE_SIZE / 2);
|
|
2551
|
+
ctx.lineTo(z.x1 * MAP_TILE_SIZE + MAP_TILE_SIZE / 2, z.y1 * MAP_TILE_SIZE + MAP_TILE_SIZE / 2);
|
|
2551
2552
|
ctx.stroke();
|
|
2552
2553
|
ctx.restore();
|
|
2553
2554
|
}
|
|
@@ -2556,15 +2557,15 @@ var HCMap = class {
|
|
|
2556
2557
|
const type = wall.type ?? "normal";
|
|
2557
2558
|
ctx.lineWidth = WALL_SIZE;
|
|
2558
2559
|
if (type === "levelSeparator") {
|
|
2559
|
-
ctx.strokeStyle =
|
|
2560
|
+
ctx.strokeStyle = MAP_WALL_COLORS.normal;
|
|
2560
2561
|
ctx.lineWidth = WALL_SIZE * 2;
|
|
2561
2562
|
ctx.beginPath();
|
|
2562
|
-
ctx.moveTo(wall.x0 *
|
|
2563
|
-
ctx.lineTo(wall.x1 *
|
|
2563
|
+
ctx.moveTo(wall.x0 * MAP_TILE_SIZE, wall.y0 * MAP_TILE_SIZE);
|
|
2564
|
+
ctx.lineTo(wall.x1 * MAP_TILE_SIZE, wall.y1 * MAP_TILE_SIZE);
|
|
2564
2565
|
ctx.stroke();
|
|
2565
2566
|
ctx.lineWidth = WALL_SIZE;
|
|
2566
2567
|
}
|
|
2567
|
-
ctx.strokeStyle =
|
|
2568
|
+
ctx.strokeStyle = MAP_WALL_COLORS[type] ?? MAP_WALL_COLORS.normal;
|
|
2568
2569
|
if (DASHED_WALLS.has(type)) {
|
|
2569
2570
|
ctx.setLineDash([WALL_SIZE, WALL_SIZE]);
|
|
2570
2571
|
ctx.lineDashOffset = WALL_SIZE / 2;
|
|
@@ -2572,8 +2573,8 @@ var HCMap = class {
|
|
|
2572
2573
|
ctx.lineCap = "round";
|
|
2573
2574
|
}
|
|
2574
2575
|
ctx.beginPath();
|
|
2575
|
-
ctx.moveTo(wall.x0 *
|
|
2576
|
-
ctx.lineTo(wall.x1 *
|
|
2576
|
+
ctx.moveTo(wall.x0 * MAP_TILE_SIZE, wall.y0 * MAP_TILE_SIZE);
|
|
2577
|
+
ctx.lineTo(wall.x1 * MAP_TILE_SIZE, wall.y1 * MAP_TILE_SIZE);
|
|
2577
2578
|
ctx.stroke();
|
|
2578
2579
|
}
|
|
2579
2580
|
};
|
|
@@ -2590,14 +2591,14 @@ function mapSquare(map, x, y) {
|
|
|
2590
2591
|
label: tile.label?.text
|
|
2591
2592
|
};
|
|
2592
2593
|
}
|
|
2593
|
-
function
|
|
2594
|
+
function mapElevationLevel(elevation) {
|
|
2594
2595
|
const value = typeof elevation === "number" ? elevation : Number.parseInt(elevation, 10);
|
|
2595
2596
|
return Number.isFinite(value) ? Math.max(1, value) : 2;
|
|
2596
2597
|
}
|
|
2597
2598
|
function mapMaxLevel(map) {
|
|
2598
2599
|
let level = 1;
|
|
2599
2600
|
for (let y = 0; y < map.height; y++) {
|
|
2600
|
-
for (let x = 0; x < map.width; x++) level = Math.max(level,
|
|
2601
|
+
for (let x = 0; x < map.width; x++) level = Math.max(level, mapElevationLevel(mapSquare(map, x, y).elevation));
|
|
2601
2602
|
}
|
|
2602
2603
|
return level;
|
|
2603
2604
|
}
|
|
@@ -2628,7 +2629,7 @@ function inspectMap(map, x, y, tolerance = 0.16) {
|
|
|
2628
2629
|
}
|
|
2629
2630
|
return { square };
|
|
2630
2631
|
}
|
|
2631
|
-
function
|
|
2632
|
+
function mapSquareCoordinate(square) {
|
|
2632
2633
|
let index = square.x + 1, column = "";
|
|
2633
2634
|
while (index > 0) {
|
|
2634
2635
|
index--;
|
|
@@ -2637,13 +2638,13 @@ function squareCoordinate(square) {
|
|
|
2637
2638
|
}
|
|
2638
2639
|
return `${column}${square.y + 1}`;
|
|
2639
2640
|
}
|
|
2640
|
-
var
|
|
2641
|
-
var
|
|
2641
|
+
var MAP_SCENE_LEVEL_HEIGHT = 0.62;
|
|
2642
|
+
var MAP_SCENE_BASE_Z = -0.34;
|
|
2642
2643
|
var WATER_DROP = 0.1;
|
|
2643
2644
|
var TERRAIN_VOLUME = { obscuring: 0.42, window: 0.42 };
|
|
2644
2645
|
var WALL_HEIGHT = { levelSeparator: 0.12, door: 0.62, door2: 0.62 };
|
|
2645
2646
|
var DEFAULT_WALL_HEIGHT = 0.62;
|
|
2646
|
-
function
|
|
2647
|
+
function mapSceneCell(scene, x, y) {
|
|
2647
2648
|
if (x < 0 || y < 0 || x >= scene.width || y >= scene.height) return null;
|
|
2648
2649
|
return scene.cells[y * scene.width + x];
|
|
2649
2650
|
}
|
|
@@ -2654,9 +2655,9 @@ function buildMapScene(map) {
|
|
|
2654
2655
|
for (let y = 0; y < height; y++) {
|
|
2655
2656
|
for (let x = 0; x < width; x++) {
|
|
2656
2657
|
const square = mapSquare(map, x, y);
|
|
2657
|
-
const level =
|
|
2658
|
-
const floor = (level - 1) *
|
|
2659
|
-
const top = square.terrain !== "blocking" ? floor + (TERRAIN_VOLUME[square.terrain] ?? 0) : square.environment === "indoor" ? maxLevel *
|
|
2658
|
+
const level = mapElevationLevel(square.elevation);
|
|
2659
|
+
const floor = (level - 1) * MAP_SCENE_LEVEL_HEIGHT - (square.terrain === "water" ? WATER_DROP : 0);
|
|
2660
|
+
const top = square.terrain !== "blocking" ? floor + (TERRAIN_VOLUME[square.terrain] ?? 0) : square.environment === "indoor" ? maxLevel * MAP_SCENE_LEVEL_HEIGHT : floor + MAP_SCENE_LEVEL_HEIGHT;
|
|
2660
2661
|
cells.push({ ...square, level, floor, top });
|
|
2661
2662
|
}
|
|
2662
2663
|
}
|
|
@@ -2671,7 +2672,7 @@ function buildMapScene(map) {
|
|
|
2671
2672
|
const x1 = wall.x0 + (wall.x1 - wall.x0) * (i + 1) / pieces, y1 = wall.y0 + (wall.y1 - wall.y0) * (i + 1) / pieces;
|
|
2672
2673
|
const mx = (x0 + x1) / 2, my = (y0 + y1) / 2;
|
|
2673
2674
|
const nx = length ? (y1 - y0) / (length / pieces) : 0, ny = length ? (x0 - x1) / (length / pieces) : 0;
|
|
2674
|
-
const sides = [[mx + nx * 0.25, my + ny * 0.25], [mx - nx * 0.25, my - ny * 0.25]].map(([px, py]) =>
|
|
2675
|
+
const sides = [[mx + nx * 0.25, my + ny * 0.25], [mx - nx * 0.25, my - ny * 0.25]].map(([px, py]) => mapSceneCell(scene, Math.floor(px), Math.floor(py))).filter((cell) => !!cell);
|
|
2675
2676
|
const floors = sides.length ? sides.map((cell) => cell.floor) : [0];
|
|
2676
2677
|
const z1 = Math.max(...floors) + (WALL_HEIGHT[type] ?? DEFAULT_WALL_HEIGHT);
|
|
2677
2678
|
walls.push({ x0, y0, x1, y1, type, z0: Math.min(...floors), z1 });
|
|
@@ -2679,7 +2680,7 @@ function buildMapScene(map) {
|
|
|
2679
2680
|
}
|
|
2680
2681
|
const ramps = [];
|
|
2681
2682
|
for (const ramp of map.ramps ?? []) {
|
|
2682
|
-
const a =
|
|
2683
|
+
const a = mapSceneCell(scene, ramp.x0, ramp.y0), b = mapSceneCell(scene, ramp.x1, ramp.y1);
|
|
2683
2684
|
if (!a || !b || Math.abs(a.x - b.x) + Math.abs(a.y - b.y) !== 1 || a.floor === b.floor) continue;
|
|
2684
2685
|
const [low, high] = a.floor < b.floor ? [a, b] : [b, a];
|
|
2685
2686
|
ramps.push({ x: low.x, y: low.y, dx: high.x - low.x, dy: high.y - low.y, from: low.floor, to: high.floor });
|
|
@@ -2689,21 +2690,21 @@ function buildMapScene(map) {
|
|
|
2689
2690
|
y0: line.y0 + 0.5,
|
|
2690
2691
|
x1: line.x1 + 0.5,
|
|
2691
2692
|
y1: line.y1 + 0.5,
|
|
2692
|
-
z: Math.max(
|
|
2693
|
+
z: Math.max(mapSceneCell(scene, line.x0, line.y0)?.top ?? 0, mapSceneCell(scene, line.x1, line.y1)?.top ?? 0) + 0.7
|
|
2693
2694
|
}));
|
|
2694
2695
|
const maxTop = Math.max(...cells.map((cell) => cell.top), ...walls.map((wall) => wall.z1), ...ziplines.map((line) => line.z));
|
|
2695
2696
|
return { width, height, cells, walls, ramps, ziplines, maxTop };
|
|
2696
2697
|
}
|
|
2697
|
-
var
|
|
2698
|
-
var
|
|
2699
|
-
var
|
|
2700
|
-
var
|
|
2701
|
-
function
|
|
2698
|
+
var MAP_DEFAULT_CAMERA = { azimuth: -32, tilt: 52, zoom: 1, panX: 0, panY: 0 };
|
|
2699
|
+
var MAP_TILT_RANGE = [6, 90];
|
|
2700
|
+
var MAP_ZOOM_RANGE = [1, 6];
|
|
2701
|
+
var clampMapValue = (value, min, max) => Math.min(max, Math.max(min, value));
|
|
2702
|
+
function makeMapProjection(scene, camera, viewport) {
|
|
2702
2703
|
const rad = camera.azimuth * Math.PI / 180, tilt = camera.tilt * Math.PI / 180;
|
|
2703
2704
|
const sin = Math.sin(rad), cos = Math.cos(rad), sinT = Math.sin(tilt), cosT = Math.cos(tilt);
|
|
2704
|
-
const cx2 = scene.width / 2, cy = scene.height / 2, midZ = (scene.maxTop +
|
|
2705
|
+
const cx2 = scene.width / 2, cy = scene.height / 2, midZ = (scene.maxTop + MAP_SCENE_BASE_Z) / 2;
|
|
2705
2706
|
const halfWidth = Math.hypot(cx2, cy);
|
|
2706
|
-
const halfHeight = Math.hypot(halfWidth, (scene.maxTop -
|
|
2707
|
+
const halfHeight = Math.hypot(halfWidth, (scene.maxTop - MAP_SCENE_BASE_Z) / 2);
|
|
2707
2708
|
const fit = Math.min(viewport.width / (halfWidth * 2.06), viewport.height / (halfHeight * 2.06));
|
|
2708
2709
|
const scale = fit * camera.zoom;
|
|
2709
2710
|
const ox = viewport.width / 2 + camera.panX, oy = viewport.height / 2 + camera.panY;
|
|
@@ -2724,21 +2725,21 @@ function makeProjection(scene, camera, viewport) {
|
|
|
2724
2725
|
}
|
|
2725
2726
|
};
|
|
2726
2727
|
}
|
|
2727
|
-
function
|
|
2728
|
-
const limit = Math.hypot(scene.width, scene.height) / 2 *
|
|
2729
|
-
return { ...camera, panX:
|
|
2728
|
+
function clampMapPan(scene, camera, viewport) {
|
|
2729
|
+
const limit = Math.hypot(scene.width, scene.height) / 2 * makeMapProjection(scene, camera, viewport).scale;
|
|
2730
|
+
return { ...camera, panX: clampMapValue(camera.panX, -limit, limit), panY: clampMapValue(camera.panY, -limit, limit) };
|
|
2730
2731
|
}
|
|
2731
|
-
function
|
|
2732
|
-
const zoom =
|
|
2732
|
+
function zoomMapCamera(scene, camera, viewport, factor, anchor = { x: 0, y: 0 }) {
|
|
2733
|
+
const zoom = clampMapValue(camera.zoom * factor, MAP_ZOOM_RANGE[0], MAP_ZOOM_RANGE[1]);
|
|
2733
2734
|
const applied = zoom / camera.zoom;
|
|
2734
|
-
return
|
|
2735
|
+
return clampMapPan(scene, {
|
|
2735
2736
|
...camera,
|
|
2736
2737
|
zoom,
|
|
2737
2738
|
panX: anchor.x + (camera.panX - anchor.x) * applied,
|
|
2738
2739
|
panY: anchor.y + (camera.panY - anchor.y) * applied
|
|
2739
2740
|
}, viewport);
|
|
2740
2741
|
}
|
|
2741
|
-
var
|
|
2742
|
+
var MAP_CELL_SIDES = [
|
|
2742
2743
|
{ ax: 0, ay: 1, bx: 0, by: 0, dx: -1, dy: 0 },
|
|
2743
2744
|
{ ax: 1, ay: 0, bx: 1, by: 1, dx: 1, dy: 0 },
|
|
2744
2745
|
{ ax: 0, ay: 0, bx: 1, by: 0, dx: 0, dy: -1 },
|
|
@@ -2752,7 +2753,7 @@ var inside = (px, py, polygon2) => {
|
|
|
2752
2753
|
}
|
|
2753
2754
|
return hit;
|
|
2754
2755
|
};
|
|
2755
|
-
function
|
|
2756
|
+
function pickMapScene(scene, view, px, py) {
|
|
2756
2757
|
const items = [];
|
|
2757
2758
|
for (const wall of scene.walls) {
|
|
2758
2759
|
items.push({
|
|
@@ -2762,7 +2763,7 @@ function pickScene(scene, view, px, py) {
|
|
|
2762
2763
|
const c = view.point(wall.x1, wall.y1, wall.z1), d = view.point(wall.x0, wall.y0, wall.z1);
|
|
2763
2764
|
if (!inside(px, py, [a, b, c, d])) return null;
|
|
2764
2765
|
const length2 = (b[0] - a[0]) ** 2 + (b[1] - a[1]) ** 2;
|
|
2765
|
-
const t = length2 ?
|
|
2766
|
+
const t = length2 ? clampMapValue(((px - a[0]) * (b[0] - a[0]) + (py - a[1]) * (b[1] - a[1])) / length2, 0, 1) : 0.5;
|
|
2766
2767
|
return { x: wall.x0 + (wall.x1 - wall.x0) * t, y: wall.y0 + (wall.y1 - wall.y0) * t };
|
|
2767
2768
|
}
|
|
2768
2769
|
});
|
|
@@ -2778,12 +2779,12 @@ function pickScene(scene, view, px, py) {
|
|
|
2778
2779
|
const det = ax * by - ay * bx;
|
|
2779
2780
|
if (!det) return { x: cell.x + 0.5, y: cell.y + 0.5 };
|
|
2780
2781
|
const u = ((px - p00[0]) * by - (py - p00[1]) * bx) / det, v = ((py - p00[1]) * ax - (px - p00[0]) * ay) / det;
|
|
2781
|
-
return { x: cell.x +
|
|
2782
|
+
return { x: cell.x + clampMapValue(u, 0, 1), y: cell.y + clampMapValue(v, 0, 1) };
|
|
2782
2783
|
}
|
|
2783
|
-
for (const side of
|
|
2784
|
+
for (const side of MAP_CELL_SIDES) {
|
|
2784
2785
|
if (side.dx * view.sin + side.dy * view.cos <= 0) continue;
|
|
2785
|
-
const neighbor =
|
|
2786
|
-
const low = neighbor ? neighbor.top :
|
|
2786
|
+
const neighbor = mapSceneCell(scene, cell.x + side.dx, cell.y + side.dy);
|
|
2787
|
+
const low = neighbor ? neighbor.top : MAP_SCENE_BASE_Z;
|
|
2787
2788
|
if (low >= cell.top) continue;
|
|
2788
2789
|
const a = view.point(cell.x + side.ax, cell.y + side.ay, low), b = view.point(cell.x + side.bx, cell.y + side.by, low);
|
|
2789
2790
|
const c = view.point(cell.x + side.bx, cell.y + side.by, cell.top), d = view.point(cell.x + side.ax, cell.y + side.ay, cell.top);
|
|
@@ -2802,7 +2803,7 @@ function pickScene(scene, view, px, py) {
|
|
|
2802
2803
|
}
|
|
2803
2804
|
return null;
|
|
2804
2805
|
}
|
|
2805
|
-
function
|
|
2806
|
+
function mapFootprintOutline({ x, y, width, height, cells }, boardWidth, boardHeight) {
|
|
2806
2807
|
const inside2 = (cx2, cy) => cx2 >= x && cy >= y && cx2 < x + width && cy < y + height && cx2 >= 0 && cy >= 0 && cx2 < boardWidth && cy < boardHeight && (!cells || !!cells[cy - y]?.[cx2 - x]);
|
|
2807
2808
|
const squares = [];
|
|
2808
2809
|
const edges = [];
|
|
@@ -2857,27 +2858,27 @@ function face(ctx, points, color) {
|
|
|
2857
2858
|
ctx.stroke();
|
|
2858
2859
|
}
|
|
2859
2860
|
function sideColor(cell) {
|
|
2860
|
-
if (cell.terrain === "blocking") return
|
|
2861
|
-
if (cell.level > 1) return
|
|
2862
|
-
if (cell.terrain === "water") return
|
|
2863
|
-
if (cell.terrain === "hindering") return
|
|
2864
|
-
if (cell.terrain === "obscuring") return
|
|
2865
|
-
if (cell.terrain === "window") return
|
|
2861
|
+
if (cell.terrain === "blocking") return MAP_TERRAIN_EDGE_COLORS.blocking;
|
|
2862
|
+
if (cell.level > 1) return MAP_ELEVATION_TILE_COLORS[Math.min(cell.level, MAP_ELEVATION_TILE_COLORS.length) - 1];
|
|
2863
|
+
if (cell.terrain === "water") return MAP_TERRAIN_EDGE_COLORS.water;
|
|
2864
|
+
if (cell.terrain === "hindering") return MAP_TERRAIN_EDGE_COLORS.hindering;
|
|
2865
|
+
if (cell.terrain === "obscuring") return MAP_TERRAIN_EDGE_COLORS.obscuring;
|
|
2866
|
+
if (cell.terrain === "window") return MAP_TERRAIN_EDGE_COLORS.window;
|
|
2866
2867
|
return SIDE_COLORS[cell.environment] ?? SIDE_COLORS.unknown;
|
|
2867
2868
|
}
|
|
2868
2869
|
function drawCellSides(ctx, scene, view, cell) {
|
|
2869
2870
|
const { point } = view;
|
|
2870
|
-
for (const side of
|
|
2871
|
+
for (const side of MAP_CELL_SIDES) {
|
|
2871
2872
|
if (side.dx * view.sin + side.dy * view.cos <= 0) continue;
|
|
2872
|
-
const neighbor =
|
|
2873
|
-
const low = neighbor ? neighbor.top :
|
|
2873
|
+
const neighbor = mapSceneCell(scene, cell.x + side.dx, cell.y + side.dy);
|
|
2874
|
+
const low = neighbor ? neighbor.top : MAP_SCENE_BASE_Z;
|
|
2874
2875
|
if (low >= cell.top) continue;
|
|
2875
2876
|
const ax = cell.x + side.ax, ay = cell.y + side.ay, bx = cell.x + side.bx, by = cell.y + side.by;
|
|
2876
2877
|
const light = faceLight(view, side.dx, side.dy);
|
|
2877
2878
|
face(ctx, [point(ax, ay, low), point(bx, by, low), point(bx, by, cell.top), point(ax, ay, cell.top)], shade(sideColor(cell), light));
|
|
2878
2879
|
ctx.strokeStyle = "rgba(0,0,0,.26)";
|
|
2879
2880
|
ctx.lineWidth = 1;
|
|
2880
|
-
for (let z = 0; z < cell.top - 0.01; z +=
|
|
2881
|
+
for (let z = 0; z < cell.top - 0.01; z += MAP_SCENE_LEVEL_HEIGHT) {
|
|
2881
2882
|
if (z <= low + 0.01) continue;
|
|
2882
2883
|
const [x0, y0] = point(ax, ay, z), [x1, y1] = point(bx, by, z);
|
|
2883
2884
|
ctx.beginPath();
|
|
@@ -2891,13 +2892,13 @@ function drawCellTop(ctx, scene, view, cell, texture, shadows) {
|
|
|
2891
2892
|
const p00 = view.point(cell.x, cell.y, cell.top), p10 = view.point(cell.x + 1, cell.y, cell.top), p01 = view.point(cell.x, cell.y + 1, cell.top);
|
|
2892
2893
|
ctx.save();
|
|
2893
2894
|
ctx.transform(p10[0] - p00[0], p10[1] - p00[1], p01[0] - p00[0], p01[1] - p00[1], p00[0], p00[1]);
|
|
2894
|
-
ctx.drawImage(texture, cell.x *
|
|
2895
|
+
ctx.drawImage(texture, cell.x * MAP_TILE_SIZE, cell.y * MAP_TILE_SIZE, MAP_TILE_SIZE, MAP_TILE_SIZE, -0.012, -0.012, 1.024, 1.024);
|
|
2895
2896
|
if (cell.environment === "indoor") {
|
|
2896
2897
|
ctx.fillStyle = (cell.x + cell.y) % 2 ? "rgba(52,62,92,.13)" : "rgba(52,62,92,.07)";
|
|
2897
2898
|
ctx.fillRect(0, 0, 1, 1);
|
|
2898
2899
|
}
|
|
2899
|
-
|
|
2900
|
-
const neighbor =
|
|
2900
|
+
MAP_CELL_SIDES.forEach((side, index) => {
|
|
2901
|
+
const neighbor = mapSceneCell(scene, cell.x + side.dx, cell.y + side.dy);
|
|
2901
2902
|
if (!neighbor || neighbor.top <= cell.top + 0.01) return;
|
|
2902
2903
|
ctx.fillStyle = shadows[index];
|
|
2903
2904
|
ctx.fillRect(0, 0, 1, 1);
|
|
@@ -2938,7 +2939,7 @@ function drawRamp(ctx, view, ramp) {
|
|
|
2938
2939
|
const sx = ramp.dy * 0.2, sy = ramp.dx * 0.2, lx = -ramp.dx * 0.38, ly = -ramp.dy * 0.38;
|
|
2939
2940
|
const a = [cx2 - sx, cy - sy], b = [cx2 + sx, cy + sy];
|
|
2940
2941
|
const c = [cx2 + sx + lx, cy + sy + ly], d = [cx2 - sx + lx, cy - sy + ly];
|
|
2941
|
-
const color =
|
|
2942
|
+
const color = MAP_ELEVATION_TILE_COLORS[1];
|
|
2942
2943
|
face(ctx, [point(a[0], a[1], ramp.from), point(d[0], d[1], ramp.from), point(a[0], a[1], ramp.to)], shade(color, 0.66));
|
|
2943
2944
|
face(ctx, [point(b[0], b[1], ramp.from), point(c[0], c[1], ramp.from), point(b[0], b[1], ramp.to)], shade(color, 0.66));
|
|
2944
2945
|
polygon(ctx, [point(a[0], a[1], ramp.to), point(b[0], b[1], ramp.to), point(c[0], c[1], ramp.from), point(d[0], d[1], ramp.from)]);
|
|
@@ -2958,7 +2959,7 @@ function drawWall(ctx, view, wall) {
|
|
|
2958
2959
|
const x0 = Math.min(wall.x0, wall.x1) - ox, x1 = Math.max(wall.x0, wall.x1) + ox;
|
|
2959
2960
|
const y0 = Math.min(wall.y0, wall.y1) - oy, y1 = Math.max(wall.y0, wall.y1) + oy;
|
|
2960
2961
|
const alpha = TRANSLUCENT_WALLS[wall.type];
|
|
2961
|
-
const color = GLASS_COLORS[wall.type] ?? (wall.type === "normal" ? "#30343c" :
|
|
2962
|
+
const color = GLASS_COLORS[wall.type] ?? (wall.type === "normal" ? "#30343c" : MAP_WALL_COLORS[wall.type] ?? "#30343c");
|
|
2962
2963
|
ctx.save();
|
|
2963
2964
|
if (alpha) ctx.globalAlpha = alpha;
|
|
2964
2965
|
if (wall.type === "door" || wall.type === "door2") {
|
|
@@ -2974,10 +2975,10 @@ function drawWall(ctx, view, wall) {
|
|
|
2974
2975
|
ctx.restore();
|
|
2975
2976
|
}
|
|
2976
2977
|
function drawIndoorCurb(ctx, scene, view, cell) {
|
|
2977
|
-
ctx.strokeStyle =
|
|
2978
|
+
ctx.strokeStyle = MAP_INDOOR_EDGE_COLOR;
|
|
2978
2979
|
ctx.lineWidth = 2;
|
|
2979
|
-
for (const side of
|
|
2980
|
-
const neighbor =
|
|
2980
|
+
for (const side of MAP_CELL_SIDES) {
|
|
2981
|
+
const neighbor = mapSceneCell(scene, cell.x + side.dx, cell.y + side.dy);
|
|
2981
2982
|
if (neighbor?.environment !== "outdoor") continue;
|
|
2982
2983
|
const [x0, y0] = view.point(cell.x + side.ax, cell.y + side.ay, cell.top), [x1, y1] = view.point(cell.x + side.bx, cell.y + side.by, cell.top);
|
|
2983
2984
|
ctx.beginPath();
|
|
@@ -2987,7 +2988,7 @@ function drawIndoorCurb(ctx, scene, view, cell) {
|
|
|
2987
2988
|
}
|
|
2988
2989
|
}
|
|
2989
2990
|
function drawFootprint(ctx, scene, view, footprint) {
|
|
2990
|
-
const { squares, edges, corners } =
|
|
2991
|
+
const { squares, edges, corners } = mapFootprintOutline(footprint, scene.width, scene.height);
|
|
2991
2992
|
if (!squares.length) return;
|
|
2992
2993
|
const { top, valid } = footprint;
|
|
2993
2994
|
const color = valid ? MOVE_VALID_COLOR : MOVE_INVALID_COLOR;
|
|
@@ -3034,7 +3035,7 @@ function drawHighlight(ctx, scene, view, highlight) {
|
|
|
3034
3035
|
if (highlight.line) {
|
|
3035
3036
|
const { x0, y0, x1, y1 } = highlight.line;
|
|
3036
3037
|
const mx = (x0 + x1) / 2, my = (y0 + y1) / 2;
|
|
3037
|
-
const near = [[Math.floor(mx - 0.01), Math.floor(my - 0.01)], [Math.floor(mx + 0.01), Math.floor(my + 0.01)]].map(([x, y]) =>
|
|
3038
|
+
const near = [[Math.floor(mx - 0.01), Math.floor(my - 0.01)], [Math.floor(mx + 0.01), Math.floor(my + 0.01)]].map(([x, y]) => mapSceneCell(scene, x, y)?.top ?? 0);
|
|
3038
3039
|
const z = Math.max(...near);
|
|
3039
3040
|
const [ax, ay] = view.point(x0, y0, z), [bx, by] = view.point(x1, y1, z);
|
|
3040
3041
|
ctx.strokeStyle = "rgba(0,0,0,.6)";
|
|
@@ -3047,7 +3048,7 @@ function drawHighlight(ctx, scene, view, highlight) {
|
|
|
3047
3048
|
ctx.lineCap = "butt";
|
|
3048
3049
|
return;
|
|
3049
3050
|
}
|
|
3050
|
-
const cell = highlight.square &&
|
|
3051
|
+
const cell = highlight.square && mapSceneCell(scene, highlight.square.x, highlight.square.y);
|
|
3051
3052
|
if (!cell) return;
|
|
3052
3053
|
polygon(ctx, [view.point(cell.x, cell.y, cell.top), view.point(cell.x + 1, cell.y, cell.top), view.point(cell.x + 1, cell.y + 1, cell.top), view.point(cell.x, cell.y + 1, cell.top)]);
|
|
3053
3054
|
ctx.fillStyle = "rgba(0,0,0,.16)";
|
|
@@ -3058,7 +3059,7 @@ function drawHighlight(ctx, scene, view, highlight) {
|
|
|
3058
3059
|
}
|
|
3059
3060
|
function drawMapScene(ctx, scene, view, texture, highlight, { tufts = true } = {}) {
|
|
3060
3061
|
const pad = 0.35;
|
|
3061
|
-
polygon(ctx, [view.point(-pad, -pad,
|
|
3062
|
+
polygon(ctx, [view.point(-pad, -pad, MAP_SCENE_BASE_Z - 0.16), view.point(scene.width + pad, -pad, MAP_SCENE_BASE_Z - 0.16), view.point(scene.width + pad, scene.height + pad, MAP_SCENE_BASE_Z - 0.16), view.point(-pad, scene.height + pad, MAP_SCENE_BASE_Z - 0.16)]);
|
|
3062
3063
|
ctx.fillStyle = "rgba(0,0,0,.14)";
|
|
3063
3064
|
ctx.fill();
|
|
3064
3065
|
const shadowDepth = 0.24;
|
|
@@ -3107,8 +3108,8 @@ var MAP_THUMBNAIL_HEIGHT = 192;
|
|
|
3107
3108
|
function renderMapThumbnail(json, height = MAP_THUMBNAIL_HEIGHT) {
|
|
3108
3109
|
if (!json?.width || !json?.height) return null;
|
|
3109
3110
|
const full = document.createElement("canvas");
|
|
3110
|
-
full.width = json.width *
|
|
3111
|
-
full.height = json.height *
|
|
3111
|
+
full.width = json.width * MAP_TILE_SIZE;
|
|
3112
|
+
full.height = json.height * MAP_TILE_SIZE;
|
|
3112
3113
|
const fctx = full.getContext("2d");
|
|
3113
3114
|
if (!fctx) return null;
|
|
3114
3115
|
new HCMap(json).draw(fctx);
|
|
@@ -3157,7 +3158,7 @@ var MapBoardView = forwardRef(function MapBoardView2({ map, canvasLabel, selecti
|
|
|
3157
3158
|
if (event.pointerType !== "touch") onLeave?.();
|
|
3158
3159
|
},
|
|
3159
3160
|
children: [
|
|
3160
|
-
/* @__PURE__ */ jsx("canvas", { ref: canvasRef, width: map.width *
|
|
3161
|
+
/* @__PURE__ */ jsx("canvas", { ref: canvasRef, width: map.width * MAP_TILE_SIZE, height: map.height * MAP_TILE_SIZE, role: "img", "aria-label": canvasLabel }),
|
|
3161
3162
|
selection && /* @__PURE__ */ jsx("svg", { className: "hc-ui-map-board__selection", viewBox: `0 0 ${map.width} ${map.height}`, "aria-hidden": "true", children: selection.line ? /* @__PURE__ */ jsx("line", { x1: selection.line.x0, y1: selection.line.y0, x2: selection.line.x1, y2: selection.line.y1 }) : /* @__PURE__ */ jsx("rect", { x: selection.square.x, y: selection.square.y, width: "1", height: "1" }) })
|
|
3162
3163
|
]
|
|
3163
3164
|
}
|
|
@@ -3226,31 +3227,33 @@ function MapEnvironmentView({ types, icons, labels, className }) {
|
|
|
3226
3227
|
}
|
|
3227
3228
|
export {
|
|
3228
3229
|
AvatarView,
|
|
3229
|
-
BACK_TABLE_HEIGHT,
|
|
3230
|
-
BASE_Z,
|
|
3231
|
-
BYSTANDER_DIAL_STEP,
|
|
3232
3230
|
Button,
|
|
3233
|
-
CARD_HEIGHT,
|
|
3234
|
-
CARD_WIDTH,
|
|
3235
|
-
CELL_SIDES,
|
|
3236
3231
|
COMBAT_DIAL_STATS,
|
|
3237
3232
|
Checkbox,
|
|
3238
3233
|
Chip,
|
|
3239
3234
|
CombatDialView,
|
|
3240
3235
|
CommandPalette,
|
|
3241
|
-
DEFAULT_CAMERA,
|
|
3242
3236
|
Dialog,
|
|
3243
|
-
ELEVATION_EDGE_COLOR,
|
|
3244
|
-
ELEVATION_TILE_COLORS,
|
|
3245
3237
|
EquipmentIcon,
|
|
3246
3238
|
Field,
|
|
3247
3239
|
FilterBar,
|
|
3248
3240
|
HCMap,
|
|
3249
|
-
INDOOR_EDGE_COLOR,
|
|
3250
3241
|
IconButton,
|
|
3251
|
-
LEVEL_HEIGHT,
|
|
3252
3242
|
ListRow,
|
|
3243
|
+
MAP_CELL_SIDES,
|
|
3244
|
+
MAP_DEFAULT_CAMERA,
|
|
3245
|
+
MAP_ELEVATION_EDGE_COLOR,
|
|
3246
|
+
MAP_ELEVATION_TILE_COLORS,
|
|
3247
|
+
MAP_INDOOR_EDGE_COLOR,
|
|
3248
|
+
MAP_SCENE_BASE_Z,
|
|
3249
|
+
MAP_SCENE_LEVEL_HEIGHT,
|
|
3250
|
+
MAP_TERRAIN_EDGE_COLORS,
|
|
3251
|
+
MAP_TERRAIN_TILE_COLORS,
|
|
3253
3252
|
MAP_THUMBNAIL_HEIGHT,
|
|
3253
|
+
MAP_TILE_SIZE,
|
|
3254
|
+
MAP_TILT_RANGE,
|
|
3255
|
+
MAP_WALL_COLORS,
|
|
3256
|
+
MAP_ZOOM_RANGE,
|
|
3254
3257
|
MINI_DIAL_STATS,
|
|
3255
3258
|
MapBoardPlaceholder,
|
|
3256
3259
|
MapBoardView,
|
|
@@ -3267,7 +3270,11 @@ export {
|
|
|
3267
3270
|
MenuSeparator,
|
|
3268
3271
|
Meter,
|
|
3269
3272
|
MiniDialView,
|
|
3273
|
+
PHYSICAL_CARD_BACK_TABLE_HEIGHT,
|
|
3274
|
+
PHYSICAL_CARD_BYSTANDER_DIAL_STEP,
|
|
3275
|
+
PHYSICAL_CARD_HEIGHT,
|
|
3270
3276
|
PHYSICAL_CARD_STATS,
|
|
3277
|
+
PHYSICAL_CARD_WIDTH,
|
|
3271
3278
|
PageHeader,
|
|
3272
3279
|
PanelHeader,
|
|
3273
3280
|
PhysicalCardFocusView,
|
|
@@ -3285,35 +3292,29 @@ export {
|
|
|
3285
3292
|
StatGlyphView,
|
|
3286
3293
|
StatIcon,
|
|
3287
3294
|
Surface,
|
|
3288
|
-
TERRAIN_EDGE_COLORS,
|
|
3289
|
-
TERRAIN_TILE_COLORS,
|
|
3290
|
-
TILE_SIZE,
|
|
3291
|
-
TILT_RANGE,
|
|
3292
3295
|
Table,
|
|
3293
|
-
WALL_COLORS,
|
|
3294
|
-
ZOOM_RANGE,
|
|
3295
|
-
backCount,
|
|
3296
3296
|
buildMapScene,
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
devicePosition,
|
|
3297
|
+
clampMapPan,
|
|
3298
|
+
clampMapValue,
|
|
3300
3299
|
drawMapScene,
|
|
3301
|
-
elevationLevel,
|
|
3302
|
-
footprintOutline,
|
|
3303
3300
|
inspectMap,
|
|
3304
|
-
|
|
3301
|
+
makeMapProjection,
|
|
3302
|
+
mapElevationLevel,
|
|
3305
3303
|
mapEnvironments,
|
|
3304
|
+
mapFootprintOutline,
|
|
3306
3305
|
mapMaxLevel,
|
|
3306
|
+
mapSceneCell,
|
|
3307
3307
|
mapSquare,
|
|
3308
|
-
|
|
3309
|
-
|
|
3308
|
+
mapSquareCoordinate,
|
|
3309
|
+
nextPhysicalCardDistribution,
|
|
3310
|
+
physicalCardBackCount,
|
|
3311
|
+
physicalCardDevicePosition,
|
|
3312
|
+
physicalCardTiltAt,
|
|
3313
|
+
physicalCardTiltStyles,
|
|
3314
|
+
pickMapScene,
|
|
3310
3315
|
placePopup,
|
|
3311
3316
|
renderMapThumbnail,
|
|
3312
|
-
|
|
3313
|
-
sceneCell,
|
|
3314
|
-
squareCoordinate,
|
|
3315
|
-
tiltAt,
|
|
3316
|
-
tiltStyles,
|
|
3317
|
+
requestPhysicalCardMotionAccess,
|
|
3317
3318
|
useImageChain,
|
|
3318
|
-
|
|
3319
|
+
zoomMapCamera
|
|
3319
3320
|
};
|