@ntmats/hc-ui 0.4.0 → 0.6.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 +27 -14
- package/dist/index.d.ts +184 -101
- package/dist/index.js +206 -187
- package/dist/styles.css +151 -74
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6,8 +6,8 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
6
6
|
import { cloneElement, createContext, forwardRef, Fragment, useCallback, useContext, useEffect, useId, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
7
7
|
import { createPortal } from "react-dom";
|
|
8
8
|
import { Fragment as Fragment2, jsx, jsxs } from "react/jsx-runtime";
|
|
9
|
-
function Surface({ tone = "content", radius, className, ...props }) {
|
|
10
|
-
return /* @__PURE__ */ jsx(
|
|
9
|
+
function Surface({ tone = "content", radius, as: Element = "div", className, ...props }) {
|
|
10
|
+
return /* @__PURE__ */ jsx(Element, { ...props, className: ["hc-ui-surface", className].filter(Boolean).join(" "), "data-tone": tone, "data-radius": radius });
|
|
11
11
|
}
|
|
12
12
|
function SegmentedControl({
|
|
13
13
|
label,
|
|
@@ -20,6 +20,7 @@ function SegmentedControl({
|
|
|
20
20
|
indicator = "static",
|
|
21
21
|
equalWidth = false,
|
|
22
22
|
optionWidth,
|
|
23
|
+
optionMinWidth,
|
|
23
24
|
className,
|
|
24
25
|
style,
|
|
25
26
|
...props
|
|
@@ -33,7 +34,8 @@ function SegmentedControl({
|
|
|
33
34
|
const layout = {
|
|
34
35
|
...equal ? { "--hc-segmented-count": options.length } : {},
|
|
35
36
|
...slide ? { "--hc-segmented-index": Math.max(knobIndex, 0) } : {},
|
|
36
|
-
...optionWidth !== void 0 ? { "--hc-segmented-option-width": `${optionWidth}px` } : {}
|
|
37
|
+
...optionWidth !== void 0 ? { "--hc-segmented-option-width": `${optionWidth}px` } : {},
|
|
38
|
+
...optionMinWidth !== void 0 ? { "--hc-segmented-option-min-width": `${optionMinWidth}px` } : {}
|
|
37
39
|
};
|
|
38
40
|
const move = (index, key) => {
|
|
39
41
|
if (!["ArrowRight", "ArrowLeft", "ArrowDown", "ArrowUp", "Home", "End"].includes(key)) return false;
|
|
@@ -103,7 +105,7 @@ function SegmentedControl({
|
|
|
103
105
|
);
|
|
104
106
|
}
|
|
105
107
|
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) {
|
|
108
|
+
var Button = forwardRef(function Button2({ variant = "secondary", tone = "default", size = "md", icon, iconEnd, fullWidth = false, loading = false, wrap = false, type = "button", className, children, onClick, ...props }, ref) {
|
|
107
109
|
const handleClick = (event) => {
|
|
108
110
|
if (loading) event.preventDefault();
|
|
109
111
|
else onClick?.(event);
|
|
@@ -120,7 +122,9 @@ var Button = forwardRef(function Button2({ variant = "secondary", size = "md", i
|
|
|
120
122
|
"aria-disabled": loading || props["aria-disabled"],
|
|
121
123
|
"data-variant": variant,
|
|
122
124
|
"data-size": size,
|
|
125
|
+
"data-tone": tone === "default" ? void 0 : tone,
|
|
123
126
|
"data-full-width": fullWidth || void 0,
|
|
127
|
+
"data-wrap": wrap || void 0,
|
|
124
128
|
children: [
|
|
125
129
|
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
130
|
/* @__PURE__ */ jsx("span", { className: "hc-ui-button__label", children }),
|
|
@@ -152,31 +156,33 @@ var glyph = (size, d) => /* @__PURE__ */ jsx("svg", { width: size, height: size,
|
|
|
152
156
|
var CHEVRON_DOWN = "M6 9L12 15L18 9";
|
|
153
157
|
var XMARK = "M6.75827 17.2426L12.0009 12M17.2435 6.75736L12.0009 12M12.0009 12L6.75827 6.75736M12.0009 12L17.2435 17.2426";
|
|
154
158
|
var visible = (node) => node !== void 0 && node !== null && node !== false && node !== "";
|
|
155
|
-
var Chip = forwardRef(function Chip2({ selected, icon, size = "md", variant = "outline", applied = false, count, badge, chevron, onClear, clearLabel, type = "button", className, children, ...props }, ref) {
|
|
159
|
+
var Chip = forwardRef(function Chip2({ selected, icon, size = "md", variant = "outline", applied = false, count, badge, badgePlacement = "inline", informative = false, chevron, onClear, clearLabel, type = "button", className, children, ...props }, ref) {
|
|
156
160
|
const popup = props["aria-haspopup"];
|
|
157
161
|
const withChevron = chevron ?? (popup !== void 0 && popup !== false && popup !== "false");
|
|
158
162
|
const look = {
|
|
159
163
|
"data-size": size === "md" ? void 0 : size,
|
|
160
164
|
"data-variant": variant === "outline" ? void 0 : variant,
|
|
161
|
-
"data-applied": applied || void 0
|
|
165
|
+
"data-applied": applied || void 0,
|
|
166
|
+
"data-icon-only": icon && !visible(children) || void 0,
|
|
167
|
+
"data-informative": informative || void 0
|
|
162
168
|
};
|
|
163
169
|
const content = /* @__PURE__ */ jsxs(Fragment2, { children: [
|
|
164
170
|
icon && /* @__PURE__ */ jsx("span", { className: "hc-ui-chip__icon", "aria-hidden": "true", children: icon }),
|
|
165
171
|
children,
|
|
166
172
|
visible(count) && /* @__PURE__ */ jsx("span", { className: "hc-ui-chip__count", children: count }),
|
|
167
|
-
visible(badge) && /* @__PURE__ */ jsx("span", { className: "hc-ui-chip__badge", children: badge }),
|
|
173
|
+
visible(badge) && /* @__PURE__ */ jsx("span", { className: "hc-ui-chip__badge", "data-placement": badgePlacement === "corner" ? "corner" : void 0, children: badge }),
|
|
168
174
|
withChevron && /* @__PURE__ */ jsx("span", { className: "hc-ui-chip__chevron", "aria-hidden": "true", children: glyph(size === "xs" ? 12 : 14, CHEVRON_DOWN) })
|
|
169
175
|
] });
|
|
170
176
|
if (!onClear) {
|
|
171
|
-
return /* @__PURE__ */ jsx("button", { ...props, ref, type, "aria-pressed": selected, className: classes("hc-ui-chip", className), ...look, children: content });
|
|
177
|
+
return /* @__PURE__ */ jsx("button", { ...props, ...informative ? { disabled: true } : {}, ref, type, "aria-pressed": selected, className: classes("hc-ui-chip", className), ...look, children: content });
|
|
172
178
|
}
|
|
173
179
|
const { style, id, onClick, disabled, ...rest } = props;
|
|
174
180
|
return /* @__PURE__ */ jsxs("span", { className: classes("hc-ui-chip", className), style, ...look, "data-clearable": "", "data-selected": selected || void 0, "data-disabled": disabled || void 0, children: [
|
|
175
|
-
onClick ? /* @__PURE__ */ jsx("button", { ...rest, id, ref, type, disabled, "aria-pressed": selected, className: "hc-ui-chip__main", onClick, children: content }) : /* @__PURE__ */ jsx("span", { id, className: "hc-ui-chip__main", children: content }),
|
|
181
|
+
onClick ? /* @__PURE__ */ jsx("button", { ...rest, id, ref, type, disabled: informative || disabled, "aria-pressed": selected, className: "hc-ui-chip__main", onClick, children: content }) : /* @__PURE__ */ jsx("span", { id, className: "hc-ui-chip__main", children: content }),
|
|
176
182
|
/* @__PURE__ */ jsx("button", { type: "button", className: "hc-ui-chip__clear", "aria-label": clearLabel, title: clearLabel, disabled, onClick: onClear, children: glyph(12, XMARK) })
|
|
177
183
|
] });
|
|
178
184
|
});
|
|
179
|
-
var FilterBar = forwardRef(function FilterBar2({ label, start, end, footer, layout = "auto", density = "comfortable", sticky = false, className, children, ...props }, ref) {
|
|
185
|
+
var FilterBar = forwardRef(function FilterBar2({ label, start, end, footer, layout = "auto", density = "comfortable", sticky = false, bleed = false, rowClassName, className, children, ...props }, ref) {
|
|
180
186
|
return /* @__PURE__ */ jsxs(
|
|
181
187
|
"div",
|
|
182
188
|
{
|
|
@@ -188,10 +194,11 @@ var FilterBar = forwardRef(function FilterBar2({ label, start, end, footer, layo
|
|
|
188
194
|
"data-layout": layout,
|
|
189
195
|
"data-density": density === "comfortable" ? void 0 : density,
|
|
190
196
|
"data-sticky": sticky || void 0,
|
|
197
|
+
"data-bleed": bleed || void 0,
|
|
191
198
|
children: [
|
|
192
199
|
/* @__PURE__ */ jsxs("div", { className: "hc-ui-filter-bar__line", children: [
|
|
193
200
|
start && /* @__PURE__ */ jsx("div", { className: "hc-ui-filter-bar__start", children: start }),
|
|
194
|
-
/* @__PURE__ */ jsx("div", { className: "hc-ui-filter-bar__row", children }),
|
|
201
|
+
/* @__PURE__ */ jsx("div", { className: classes("hc-ui-filter-bar__row", rowClassName), children }),
|
|
195
202
|
end && /* @__PURE__ */ jsx("div", { className: "hc-ui-filter-bar__end", children: end })
|
|
196
203
|
] }),
|
|
197
204
|
footer && /* @__PURE__ */ jsx("div", { className: "hc-ui-filter-bar__footer", children: footer })
|
|
@@ -322,7 +329,7 @@ var outside = (element, x, y) => {
|
|
|
322
329
|
const box = element.getBoundingClientRect();
|
|
323
330
|
return x < box.left || x > box.right || y < box.top || y > box.bottom;
|
|
324
331
|
};
|
|
325
|
-
var
|
|
332
|
+
var DialogBase = forwardRef(function Dialog({
|
|
326
333
|
open,
|
|
327
334
|
onClose,
|
|
328
335
|
title,
|
|
@@ -333,6 +340,8 @@ var Dialog = forwardRef(function Dialog2({
|
|
|
333
340
|
closeIcon,
|
|
334
341
|
size = "md",
|
|
335
342
|
dismissible = true,
|
|
343
|
+
bodyClassName,
|
|
344
|
+
headerless = false,
|
|
336
345
|
className,
|
|
337
346
|
onClick,
|
|
338
347
|
onPointerDown,
|
|
@@ -347,6 +356,7 @@ var Dialog = forwardRef(function Dialog2({
|
|
|
347
356
|
}, [ref]);
|
|
348
357
|
const id = useId();
|
|
349
358
|
const titleId = `${id}-title`;
|
|
359
|
+
const bodyId = `${id}-body`;
|
|
350
360
|
const descriptionId = description ? `${id}-description` : void 0;
|
|
351
361
|
const [mounted, setMounted] = useState(open);
|
|
352
362
|
const [closing, setClosing] = useState(false);
|
|
@@ -361,7 +371,7 @@ var Dialog = forwardRef(function Dialog2({
|
|
|
361
371
|
});
|
|
362
372
|
const keepFocus = useRef(null);
|
|
363
373
|
const handleMount = useCallback(() => {
|
|
364
|
-
const dialog = document.getElementById(titleId)?.closest("dialog");
|
|
374
|
+
const dialog = (document.getElementById(titleId) ?? document.getElementById(bodyId))?.closest("dialog");
|
|
365
375
|
if (!dialog) return () => {
|
|
366
376
|
};
|
|
367
377
|
const trigger = document.activeElement instanceof HTMLElement ? document.activeElement : null;
|
|
@@ -380,7 +390,7 @@ var Dialog = forwardRef(function Dialog2({
|
|
|
380
390
|
if (lost && !dialog.open && trigger?.isConnected) trigger.focus({ preventScroll: true });
|
|
381
391
|
});
|
|
382
392
|
};
|
|
383
|
-
}, [titleId]);
|
|
393
|
+
}, [titleId, bodyId]);
|
|
384
394
|
useEffect(() => {
|
|
385
395
|
const dialog = dialogRef.current;
|
|
386
396
|
if (!closing || !dialog) return;
|
|
@@ -427,7 +437,7 @@ var Dialog = forwardRef(function Dialog2({
|
|
|
427
437
|
if (fromBackdrop && latest.current.dismissible && !latest.current.closing) latest.current.onClose();
|
|
428
438
|
};
|
|
429
439
|
if (!mounted) return null;
|
|
430
|
-
const hasBody = Boolean(description) || children !== void 0 && children !== null && children !== false;
|
|
440
|
+
const hasBody = headerless || Boolean(description) || children !== void 0 && children !== null && children !== false;
|
|
431
441
|
return /* @__PURE__ */ jsxs(
|
|
432
442
|
"dialog",
|
|
433
443
|
{
|
|
@@ -436,7 +446,8 @@ var Dialog = forwardRef(function Dialog2({
|
|
|
436
446
|
className: classes("hc-ui-dialog", className),
|
|
437
447
|
"data-size": size,
|
|
438
448
|
"data-closing": closing || void 0,
|
|
439
|
-
"
|
|
449
|
+
"data-headerless": headerless || void 0,
|
|
450
|
+
"aria-labelledby": headerless ? void 0 : titleId,
|
|
440
451
|
"aria-describedby": classes(descriptionId, describedBy) || void 0,
|
|
441
452
|
onCancel: handleCancel,
|
|
442
453
|
onClose: handleNativeClose,
|
|
@@ -444,11 +455,11 @@ var Dialog = forwardRef(function Dialog2({
|
|
|
444
455
|
onClick: handleClick,
|
|
445
456
|
children: [
|
|
446
457
|
/* @__PURE__ */ jsx(DialogMount, { onMount: handleMount }),
|
|
447
|
-
/* @__PURE__ */ jsxs("div", { className: "hc-ui-dialog__header", children: [
|
|
458
|
+
!headerless && /* @__PURE__ */ jsxs("div", { className: "hc-ui-dialog__header", children: [
|
|
448
459
|
/* @__PURE__ */ jsx("h2", { className: "hc-ui-dialog__title", id: titleId, children: title }),
|
|
449
460
|
closeLabel && /* @__PURE__ */ jsx(IconButton, { className: "hc-ui-dialog__close", label: closeLabel, icon: closeIcon ?? closeGlyph, size: "sm", tone: "secondary", onClick: () => latest.current.onClose() })
|
|
450
461
|
] }),
|
|
451
|
-
hasBody && /* @__PURE__ */ jsxs("div", { className: "hc-ui-dialog__body", children: [
|
|
462
|
+
hasBody && /* @__PURE__ */ jsxs("div", { className: classes("hc-ui-dialog__body", bodyClassName), id: headerless ? bodyId : void 0, children: [
|
|
452
463
|
description && /* @__PURE__ */ jsx("p", { className: "hc-ui-dialog__description", id: descriptionId, children: description }),
|
|
453
464
|
children
|
|
454
465
|
] }),
|
|
@@ -457,8 +468,9 @@ var Dialog = forwardRef(function Dialog2({
|
|
|
457
468
|
}
|
|
458
469
|
);
|
|
459
470
|
});
|
|
471
|
+
var Dialog2 = DialogBase;
|
|
460
472
|
var SHEET_EASE = "cubic-bezier(0.32, 0.72, 0, 1)";
|
|
461
|
-
var NO_DRAG = 'input, textarea, select, [contenteditable="true"], [data-hc-sheet-nodrag]';
|
|
473
|
+
var NO_DRAG = 'input, textarea, select, [contenteditable="true"], [data-hc-sheet-nodrag], .hc-ui-portal';
|
|
462
474
|
function useSheetDrag(sheet, latest) {
|
|
463
475
|
useEffect(() => {
|
|
464
476
|
const el = sheet;
|
|
@@ -590,7 +602,7 @@ function useSheetDrag(sheet, latest) {
|
|
|
590
602
|
};
|
|
591
603
|
}, [sheet, latest]);
|
|
592
604
|
}
|
|
593
|
-
var Sheet = forwardRef(function Sheet2({ presentation = "auto", width = 440, footer, dismissible = true, onClose, className, style, ...props }, ref) {
|
|
605
|
+
var Sheet = forwardRef(function Sheet2({ presentation = "auto", width = 440, footer, dismissible = true, header = true, onClose, className, style, ...props }, ref) {
|
|
594
606
|
const [node, setNode] = useState(null);
|
|
595
607
|
const setRef = useCallback((element) => {
|
|
596
608
|
setNode(element);
|
|
@@ -603,7 +615,7 @@ var Sheet = forwardRef(function Sheet2({ presentation = "auto", width = 440, foo
|
|
|
603
615
|
useSheetDrag(node, latest);
|
|
604
616
|
const sized = width === 440 ? style : { ...style, "--hc-sheet-width": `${width}px` };
|
|
605
617
|
return /* @__PURE__ */ jsx(
|
|
606
|
-
|
|
618
|
+
DialogBase,
|
|
607
619
|
{
|
|
608
620
|
...props,
|
|
609
621
|
ref: setRef,
|
|
@@ -611,6 +623,7 @@ var Sheet = forwardRef(function Sheet2({ presentation = "auto", width = 440, foo
|
|
|
611
623
|
actions: footer,
|
|
612
624
|
dismissible,
|
|
613
625
|
onClose,
|
|
626
|
+
headerless: !header,
|
|
614
627
|
className: classes("hc-ui-sheet", className),
|
|
615
628
|
style: sized,
|
|
616
629
|
"data-presentation": presentation
|
|
@@ -647,10 +660,9 @@ function useRovingRadios(options, value) {
|
|
|
647
660
|
});
|
|
648
661
|
return item;
|
|
649
662
|
}
|
|
650
|
-
function SheetOptions({ label, options, value, onChange,
|
|
663
|
+
function SheetOptions({ label, options, value, onChange, className, ...props }) {
|
|
651
664
|
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) => {
|
|
665
|
+
return /* @__PURE__ */ jsx("div", { ...props, className: classes("hc-ui-sheet-options", className), role: "radiogroup", "aria-label": label, children: options.map((option, index) => {
|
|
654
666
|
const on = option.value === value;
|
|
655
667
|
return /* @__PURE__ */ jsxs(
|
|
656
668
|
"button",
|
|
@@ -669,14 +681,14 @@ function SheetOptions({ label, options, value, onChange, variant = "card", class
|
|
|
669
681
|
/* @__PURE__ */ jsx("span", { className: "hc-ui-sheet-options__label", children: option.label }),
|
|
670
682
|
option.hint && /* @__PURE__ */ jsx("span", { className: "hc-ui-sheet-options__hint", children: option.hint })
|
|
671
683
|
] }),
|
|
672
|
-
/* @__PURE__ */ jsx("span", { className: "hc-ui-sheet-options__check", "aria-hidden": "true", children: on && /* @__PURE__ */ jsx("svg", { width:
|
|
684
|
+
/* @__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
685
|
]
|
|
674
686
|
},
|
|
675
687
|
option.value
|
|
676
688
|
);
|
|
677
689
|
}) });
|
|
678
690
|
}
|
|
679
|
-
function SheetChips({ label, options, value, onChange, className, ...props }) {
|
|
691
|
+
function SheetChips({ label, options, value, onChange, size = "md", className, ...props }) {
|
|
680
692
|
const item = useRovingRadios(options, value);
|
|
681
693
|
return /* @__PURE__ */ jsx("div", { ...props, className: classes("hc-ui-sheet-chips", className), role: "radiogroup", "aria-label": label, children: options.map((option, index) => /* @__PURE__ */ jsx(
|
|
682
694
|
"button",
|
|
@@ -687,6 +699,7 @@ function SheetChips({ label, options, value, onChange, className, ...props }) {
|
|
|
687
699
|
"aria-checked": option.value === value,
|
|
688
700
|
disabled: option.disabled,
|
|
689
701
|
className: "hc-ui-chip",
|
|
702
|
+
"data-size": size === "md" ? void 0 : size,
|
|
690
703
|
onClick: () => onChange(option.value),
|
|
691
704
|
children: option.label
|
|
692
705
|
},
|
|
@@ -716,6 +729,10 @@ function placePopup(trigger, size, boundary, side, align) {
|
|
|
716
729
|
const finalAlign = !fits(align) && fits(align === "end" ? "start" : "end") ? align === "end" ? "start" : "end" : align;
|
|
717
730
|
return { side: finalSide, align: finalAlign };
|
|
718
731
|
}
|
|
732
|
+
function portalContainerFor(node) {
|
|
733
|
+
if (typeof document === "undefined") return null;
|
|
734
|
+
return node?.closest("dialog[open]") ?? document.body;
|
|
735
|
+
}
|
|
719
736
|
var layers = [];
|
|
720
737
|
function themeOf(element) {
|
|
721
738
|
const scoped = element?.closest("[data-theme]")?.getAttribute("data-theme");
|
|
@@ -761,7 +778,7 @@ function usePopup({ kind, trigger, open: openProp, defaultOpen = false, onOpenCh
|
|
|
761
778
|
setContainer(null);
|
|
762
779
|
return;
|
|
763
780
|
}
|
|
764
|
-
const element = portal instanceof HTMLElement ? portal : triggerRef.current
|
|
781
|
+
const element = portal instanceof HTMLElement ? portal : portalContainerFor(triggerRef.current) ?? document.body;
|
|
765
782
|
setContainer({ element, theme: theme ?? themeOf(triggerRef.current) });
|
|
766
783
|
}, [open, portal, theme]);
|
|
767
784
|
const place = useCallback(() => {
|
|
@@ -1555,6 +1572,7 @@ var CombatDialView = forwardRef(function CombatDialView2({
|
|
|
1555
1572
|
currentClick,
|
|
1556
1573
|
labels,
|
|
1557
1574
|
presentation = "default",
|
|
1575
|
+
density = "comfortable",
|
|
1558
1576
|
onClickChange,
|
|
1559
1577
|
onSelectPoint,
|
|
1560
1578
|
tip,
|
|
@@ -1658,7 +1676,7 @@ var CombatDialView = forwardRef(function CombatDialView2({
|
|
|
1658
1676
|
COMBAT_DIAL_STATS.map((stat) => /* @__PURE__ */ jsx("div", { className: "hc-ui-combat-dial__cell", "data-kind": "ko", children: labels.koCell }, stat))
|
|
1659
1677
|
] })
|
|
1660
1678
|
] }, rowIndex);
|
|
1661
|
-
return /* @__PURE__ */ jsxs("section", { ref, className: "hc-ui-combat-dial", "data-presentation": presentation, "data-static": flag(!tracksClick), children: [
|
|
1679
|
+
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
1680
|
presentation !== "printed" && /* @__PURE__ */ jsxs("header", { className: "hc-ui-combat-dial__header", children: [
|
|
1663
1681
|
/* @__PURE__ */ jsxs("div", { className: "hc-ui-combat-dial__header-top", children: [
|
|
1664
1682
|
/* @__PURE__ */ jsx("span", { className: "hc-ui-combat-dial__label", children: labels.heading }),
|
|
@@ -1693,11 +1711,11 @@ var CombatDialView = forwardRef(function CombatDialView2({
|
|
|
1693
1711
|
tip && tipSlot
|
|
1694
1712
|
] });
|
|
1695
1713
|
});
|
|
1696
|
-
var
|
|
1697
|
-
var
|
|
1698
|
-
var
|
|
1699
|
-
var
|
|
1700
|
-
function
|
|
1714
|
+
var PHYSICAL_CARD_WIDTH = 375;
|
|
1715
|
+
var PHYSICAL_CARD_HEIGHT = 525;
|
|
1716
|
+
var PHYSICAL_CARD_BACK_TABLE_HEIGHT = 440;
|
|
1717
|
+
var PHYSICAL_CARD_BYSTANDER_DIAL_STEP = { x: 7, y: 24 };
|
|
1718
|
+
function nextPhysicalCardDistribution(pages, overflowing) {
|
|
1701
1719
|
const last = pages.length ? Math.max(...pages) : 0;
|
|
1702
1720
|
for (let table = 0; table <= last; table += 1) {
|
|
1703
1721
|
if (!overflowing(table)) continue;
|
|
@@ -1709,7 +1727,7 @@ function nextDistribution(pages, overflowing) {
|
|
|
1709
1727
|
}
|
|
1710
1728
|
return null;
|
|
1711
1729
|
}
|
|
1712
|
-
function
|
|
1730
|
+
function physicalCardBackCount(pages) {
|
|
1713
1731
|
const last = pages.length ? Math.max(...pages) : 0;
|
|
1714
1732
|
return Math.max(1, Math.ceil(last / 2));
|
|
1715
1733
|
}
|
|
@@ -1721,7 +1739,7 @@ var CAN_DEVICE_TILT = "(hover: none) and (pointer: coarse) and (prefers-reduced-
|
|
|
1721
1739
|
var DEVICE_RANGE_DEG = 18;
|
|
1722
1740
|
var BASELINE_DRIFT = 0.01;
|
|
1723
1741
|
var DEVICE_SMOOTHING = 0.2;
|
|
1724
|
-
function
|
|
1742
|
+
function physicalCardTiltAt(x, y, damping = 1) {
|
|
1725
1743
|
const offsetX = (x - 0.5) * 2 * damping;
|
|
1726
1744
|
const offsetY = (y - 0.5) * 2 * damping;
|
|
1727
1745
|
return {
|
|
@@ -1736,7 +1754,7 @@ function tiltAt(x, y, damping = 1) {
|
|
|
1736
1754
|
var PARALLAX_ART_PX = 4;
|
|
1737
1755
|
var PARALLAX_HEADER_PX = 1.5;
|
|
1738
1756
|
var CARD_ART_SELECTOR = ".hc-ui-physical-card__token, .hc-ui-physical-card__terrain-art, .hc-ui-physical-card__bystander-token";
|
|
1739
|
-
function
|
|
1757
|
+
function physicalCardTiltStyles(tilt) {
|
|
1740
1758
|
return {
|
|
1741
1759
|
card: `perspective(1100px) rotateX(${tilt.rotateX}deg) rotateY(${tilt.rotateY}deg)`,
|
|
1742
1760
|
art: `${tilt.parallaxX * PARALLAX_ART_PX}px ${tilt.parallaxY * PARALLAX_ART_PX}px`,
|
|
@@ -1748,7 +1766,7 @@ var eachIn = (root, selector, write) => {
|
|
|
1748
1766
|
root.querySelectorAll(selector).forEach(write);
|
|
1749
1767
|
};
|
|
1750
1768
|
function applyTilt(root, tilt) {
|
|
1751
|
-
const styles =
|
|
1769
|
+
const styles = physicalCardTiltStyles(tilt);
|
|
1752
1770
|
root.dataset.tilt = "";
|
|
1753
1771
|
eachIn(root, ".hc-ui-physical-card-tilt", (element) => {
|
|
1754
1772
|
element.style.transform = styles.card;
|
|
@@ -1772,7 +1790,7 @@ function clearTilt(root) {
|
|
|
1772
1790
|
element.style.translate = "";
|
|
1773
1791
|
});
|
|
1774
1792
|
}
|
|
1775
|
-
function
|
|
1793
|
+
function physicalCardDevicePosition(beta, gamma, baseline, screenAngle = 0) {
|
|
1776
1794
|
const clampAxis = (value) => Math.max(-1, Math.min(1, value / DEVICE_RANGE_DEG));
|
|
1777
1795
|
const front = clampAxis(beta - baseline.beta);
|
|
1778
1796
|
const side = clampAxis(gamma - baseline.gamma);
|
|
@@ -1780,7 +1798,7 @@ function devicePosition(beta, gamma, baseline, screenAngle = 0) {
|
|
|
1780
1798
|
return { x: (horizontal + 1) / 2, y: (vertical + 1) / 2 };
|
|
1781
1799
|
}
|
|
1782
1800
|
var motionAsked = false;
|
|
1783
|
-
function
|
|
1801
|
+
function requestPhysicalCardMotionAccess() {
|
|
1784
1802
|
const request = typeof DeviceOrientationEvent === "undefined" ? void 0 : DeviceOrientationEvent.requestPermission;
|
|
1785
1803
|
if (motionAsked || !request) return;
|
|
1786
1804
|
motionAsked = true;
|
|
@@ -1801,10 +1819,10 @@ function useDeviceTilt(ref, enabled) {
|
|
|
1801
1819
|
baseline ?? (baseline = { beta: event.beta, gamma: event.gamma });
|
|
1802
1820
|
baseline.beta += (event.beta - baseline.beta) * BASELINE_DRIFT;
|
|
1803
1821
|
baseline.gamma += (event.gamma - baseline.gamma) * BASELINE_DRIFT;
|
|
1804
|
-
const target =
|
|
1822
|
+
const target = physicalCardDevicePosition(event.beta, event.gamma, baseline, screen.orientation?.angle ?? 0);
|
|
1805
1823
|
position = { x: position.x + (target.x - position.x) * DEVICE_SMOOTHING, y: position.y + (target.y - position.y) * DEVICE_SMOOTHING };
|
|
1806
1824
|
cancelAnimationFrame(frame);
|
|
1807
|
-
frame = requestAnimationFrame(() => applyTilt(element,
|
|
1825
|
+
frame = requestAnimationFrame(() => applyTilt(element, physicalCardTiltAt(position.x, position.y)));
|
|
1808
1826
|
};
|
|
1809
1827
|
const onTouchStart = () => {
|
|
1810
1828
|
touching = true;
|
|
@@ -1835,7 +1853,7 @@ function useCardTilt() {
|
|
|
1835
1853
|
const slot = event.currentTarget;
|
|
1836
1854
|
const rect = slot.getBoundingClientRect();
|
|
1837
1855
|
const overDial = event.target.closest(CARD_DIAL_SELECTOR);
|
|
1838
|
-
const tilt =
|
|
1856
|
+
const tilt = physicalCardTiltAt((event.clientX - rect.left) / rect.width, (event.clientY - rect.top) / rect.height, overDial ? DIAL_DAMPING : 1);
|
|
1839
1857
|
cancelAnimationFrame(frame.current);
|
|
1840
1858
|
frame.current = requestAnimationFrame(() => applyTilt(slot, tilt));
|
|
1841
1859
|
};
|
|
@@ -1874,9 +1892,9 @@ function useCardScale(fitHeight, focus, scrollerSelector) {
|
|
|
1874
1892
|
if (scroller) observer.observe(scroller);
|
|
1875
1893
|
return () => observer.disconnect();
|
|
1876
1894
|
}, [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 /
|
|
1895
|
+
const columns = !focus && width >= PHYSICAL_CARD_WIDTH * SIDE_BY_SIDE_MIN_SCALE * 2 + CARD_GAP ? 2 : 1;
|
|
1896
|
+
const byWidth = width ? Math.min(MAX_CARD_SCALE, (width - CARD_GAP * (columns - 1)) / columns / PHYSICAL_CARD_WIDTH) : 1;
|
|
1897
|
+
const scale = (fitHeight || focus) && height > 0 ? Math.max(0.6, Math.min(byWidth, height / PHYSICAL_CARD_HEIGHT)) : byWidth;
|
|
1880
1898
|
return { ref, scale };
|
|
1881
1899
|
}
|
|
1882
1900
|
function useFitText(text, max, min) {
|
|
@@ -1951,9 +1969,9 @@ function PhysicalCardFocusView({ label, onClose, children }) {
|
|
|
1951
1969
|
}, 0);
|
|
1952
1970
|
window.addEventListener("pointerup", release, { once: true });
|
|
1953
1971
|
window.addEventListener("pointercancel", release, { once: true });
|
|
1954
|
-
window.addEventListener("touchend",
|
|
1972
|
+
window.addEventListener("touchend", requestPhysicalCardMotionAccess);
|
|
1955
1973
|
return () => {
|
|
1956
|
-
window.removeEventListener("touchend",
|
|
1974
|
+
window.removeEventListener("touchend", requestPhysicalCardMotionAccess);
|
|
1957
1975
|
window.removeEventListener("pointerup", release);
|
|
1958
1976
|
window.removeEventListener("pointercancel", release);
|
|
1959
1977
|
dialog.close();
|
|
@@ -2035,7 +2053,7 @@ var PhysicalCardView = forwardRef(function PhysicalCardView2({
|
|
|
2035
2053
|
const pages = distribution.key === contentKey && distribution.pages.length === rules.length ? distribution.pages : zeros();
|
|
2036
2054
|
const tables = useRef([]);
|
|
2037
2055
|
useLayoutEffect(() => {
|
|
2038
|
-
const next =
|
|
2056
|
+
const next = nextPhysicalCardDistribution(pages, (table) => {
|
|
2039
2057
|
const element = tables.current[table];
|
|
2040
2058
|
return !!element && element.scrollHeight > element.clientHeight + 1;
|
|
2041
2059
|
});
|
|
@@ -2171,17 +2189,17 @@ var PhysicalCardView = forwardRef(function PhysicalCardView2({
|
|
|
2171
2189
|
] }),
|
|
2172
2190
|
/* @__PURE__ */ jsx("div", { className: "hc-ui-physical-card__bystander-name", children: bystander.name }),
|
|
2173
2191
|
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 *
|
|
2192
|
+
const position = { left: row * PHYSICAL_CARD_BYSTANDER_DIAL_STEP.x, top: row * PHYSICAL_CARD_BYSTANDER_DIAL_STEP.y };
|
|
2175
2193
|
const cellStyle = { ...position, backgroundColor: cell.background, color: cell.color };
|
|
2176
2194
|
const cellProps = { className: "hc-ui-physical-card__bystander-value", "data-special": flag(cell.special), style: cellStyle, "aria-label": cell.label };
|
|
2177
2195
|
if (!cell.tip) return /* @__PURE__ */ jsx("span", { ...cellProps, role: "img", children: cell.value }, cell.stat);
|
|
2178
2196
|
return /* @__PURE__ */ jsx("button", { type: "button", ...cellProps, "data-tip": "", ...tipProps(cell.tip), children: cell.value }, cell.stat);
|
|
2179
2197
|
}) })
|
|
2180
2198
|
] }) });
|
|
2181
|
-
const backs =
|
|
2199
|
+
const backs = physicalCardBackCount(pages);
|
|
2182
2200
|
const shownSide = Math.min(side, backs);
|
|
2183
2201
|
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": `${
|
|
2202
|
+
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
2203
|
return /* @__PURE__ */ jsxs("div", { className: "hc-ui-physical-cards", "data-focus": flag(focus), ref: setWrapRef, style, ...longPress, children: [
|
|
2186
2204
|
/* @__PURE__ */ jsxs(CardFrame, { side: "front", backgroundSrc: images.front, legacySrc: images.legacy, label: labels.front, away: focus && shownSide !== 0, children: [
|
|
2187
2205
|
layout.teamUp && /* @__PURE__ */ jsx("span", { className: "hc-ui-physical-card__team-up", "aria-hidden": true }),
|
|
@@ -2238,8 +2256,8 @@ var PhysicalCardView = forwardRef(function PhysicalCardView2({
|
|
|
2238
2256
|
tip && tipSlot
|
|
2239
2257
|
] });
|
|
2240
2258
|
});
|
|
2241
|
-
var
|
|
2242
|
-
var EDGE_SIZE =
|
|
2259
|
+
var MAP_TILE_SIZE = 64;
|
|
2260
|
+
var EDGE_SIZE = MAP_TILE_SIZE / 16;
|
|
2243
2261
|
var WALL_SIZE = EDGE_SIZE * 2;
|
|
2244
2262
|
var MAX_EDGES = 4;
|
|
2245
2263
|
var HINDERING_TILE_COLOR = "#B4ECB4";
|
|
@@ -2249,9 +2267,9 @@ var OBSCURING_TILE_COLOR = "#E8E8E8";
|
|
|
2249
2267
|
var WINDOW_TILE_COLOR = "#dee8eb";
|
|
2250
2268
|
var SPECIAL_TILE_COLOR = "#FFD394";
|
|
2251
2269
|
var STARTING_ZONE_TILE_COLOR = "#fff8fa";
|
|
2252
|
-
var
|
|
2253
|
-
var
|
|
2254
|
-
clear:
|
|
2270
|
+
var MAP_ELEVATION_TILE_COLORS = ["#FFFFFF", "#FB7A7F", "#D0424C", "#B02020", "#902020", "#702020"];
|
|
2271
|
+
var MAP_TERRAIN_TILE_COLORS = {
|
|
2272
|
+
clear: MAP_ELEVATION_TILE_COLORS[0],
|
|
2255
2273
|
hindering: HINDERING_TILE_COLOR,
|
|
2256
2274
|
blocking: BLOCKING_TILE_COLOR,
|
|
2257
2275
|
water: WATER_TILE_COLOR,
|
|
@@ -2260,7 +2278,7 @@ var TERRAIN_TILE_COLORS = {
|
|
|
2260
2278
|
special: SPECIAL_TILE_COLOR,
|
|
2261
2279
|
special2: SPECIAL_TILE_COLOR
|
|
2262
2280
|
};
|
|
2263
|
-
var
|
|
2281
|
+
var MAP_TERRAIN_EDGE_COLORS = {
|
|
2264
2282
|
clear: "#FFFFFF",
|
|
2265
2283
|
hindering: "#3ACE3A",
|
|
2266
2284
|
blocking: "#76491E",
|
|
@@ -2271,9 +2289,9 @@ var TERRAIN_EDGE_COLORS = {
|
|
|
2271
2289
|
special2: "#FF7600"
|
|
2272
2290
|
};
|
|
2273
2291
|
var STARTING_ZONE_EDGE_COLOR = "#D560A9";
|
|
2274
|
-
var
|
|
2275
|
-
var
|
|
2276
|
-
var
|
|
2292
|
+
var MAP_ELEVATION_EDGE_COLOR = "#FF0000";
|
|
2293
|
+
var MAP_INDOOR_EDGE_COLOR = "#FFFF00";
|
|
2294
|
+
var MAP_WALL_COLORS = {
|
|
2277
2295
|
normal: "#000000",
|
|
2278
2296
|
glass: "#E59400",
|
|
2279
2297
|
glass2: "#33BBFF",
|
|
@@ -2316,8 +2334,8 @@ var Tile = class {
|
|
|
2316
2334
|
else if (this.terrain === "window") this.fillStyle = WINDOW_TILE_COLOR;
|
|
2317
2335
|
else if (this.terrain === "special" || this.terrain === "special2") this.fillStyle = SPECIAL_TILE_COLOR;
|
|
2318
2336
|
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 =
|
|
2337
|
+
else if (typeof this.elevation === "string") this.fillStyle = MAP_ELEVATION_TILE_COLORS[1];
|
|
2338
|
+
else this.fillStyle = MAP_ELEVATION_TILE_COLORS[this.elevation - 1] ?? MAP_ELEVATION_TILE_COLORS[0];
|
|
2321
2339
|
}
|
|
2322
2340
|
addEdge(edgeType, strokeStyle, dashed) {
|
|
2323
2341
|
while (this.edges[edgeType]) edgeType += 4;
|
|
@@ -2332,7 +2350,7 @@ var Tile = class {
|
|
|
2332
2350
|
}
|
|
2333
2351
|
draw(ctx) {
|
|
2334
2352
|
ctx.fillStyle = this.fillStyle;
|
|
2335
|
-
ctx.fillRect(this.x *
|
|
2353
|
+
ctx.fillRect(this.x * MAP_TILE_SIZE, this.y * MAP_TILE_SIZE, MAP_TILE_SIZE, MAP_TILE_SIZE);
|
|
2336
2354
|
}
|
|
2337
2355
|
drawEdges(ctx) {
|
|
2338
2356
|
const edgeLevels = [-1, -1, -1, -1];
|
|
@@ -2366,27 +2384,27 @@ var Tile = class {
|
|
|
2366
2384
|
const oBottom = this.hasEdge(3 /* BOTTOM */ + level * 4) ? 0 : overhangSize;
|
|
2367
2385
|
switch (edgeType) {
|
|
2368
2386
|
case 0 /* LEFT */:
|
|
2369
|
-
x0 = x1 = this.x *
|
|
2370
|
-
y0 = this.y *
|
|
2371
|
-
y1 = (this.y + 1) *
|
|
2387
|
+
x0 = x1 = this.x * MAP_TILE_SIZE + EDGE_SIZE / 2 + EDGE_SIZE * level;
|
|
2388
|
+
y0 = this.y * MAP_TILE_SIZE - oTop;
|
|
2389
|
+
y1 = (this.y + 1) * MAP_TILE_SIZE + oBottom;
|
|
2372
2390
|
lineDashOffset = oTop;
|
|
2373
2391
|
break;
|
|
2374
2392
|
case 1 /* RIGHT */:
|
|
2375
|
-
x0 = x1 = (this.x + 1) *
|
|
2376
|
-
y0 = this.y *
|
|
2377
|
-
y1 = (this.y + 1) *
|
|
2393
|
+
x0 = x1 = (this.x + 1) * MAP_TILE_SIZE - EDGE_SIZE / 2 - EDGE_SIZE * level;
|
|
2394
|
+
y0 = this.y * MAP_TILE_SIZE - oTop;
|
|
2395
|
+
y1 = (this.y + 1) * MAP_TILE_SIZE + oBottom;
|
|
2378
2396
|
lineDashOffset = oTop;
|
|
2379
2397
|
break;
|
|
2380
2398
|
case 2 /* TOP */:
|
|
2381
|
-
x0 = this.x *
|
|
2382
|
-
y0 = y1 = this.y *
|
|
2383
|
-
x1 = (this.x + 1) *
|
|
2399
|
+
x0 = this.x * MAP_TILE_SIZE - oLeft;
|
|
2400
|
+
y0 = y1 = this.y * MAP_TILE_SIZE + EDGE_SIZE / 2 + EDGE_SIZE * level;
|
|
2401
|
+
x1 = (this.x + 1) * MAP_TILE_SIZE + oRight;
|
|
2384
2402
|
lineDashOffset = oLeft;
|
|
2385
2403
|
break;
|
|
2386
2404
|
case 3 /* BOTTOM */:
|
|
2387
|
-
x0 = this.x *
|
|
2388
|
-
y0 = y1 = (this.y + 1) *
|
|
2389
|
-
x1 = (this.x + 1) *
|
|
2405
|
+
x0 = this.x * MAP_TILE_SIZE - oLeft;
|
|
2406
|
+
y0 = y1 = (this.y + 1) * MAP_TILE_SIZE - EDGE_SIZE / 2 - EDGE_SIZE * level;
|
|
2407
|
+
x1 = (this.x + 1) * MAP_TILE_SIZE + oRight;
|
|
2390
2408
|
lineDashOffset = oLeft;
|
|
2391
2409
|
break;
|
|
2392
2410
|
}
|
|
@@ -2410,33 +2428,33 @@ var Tile = class {
|
|
|
2410
2428
|
}
|
|
2411
2429
|
drawRamp(ctx, angle) {
|
|
2412
2430
|
ctx.save();
|
|
2413
|
-
ctx.translate(this.x *
|
|
2431
|
+
ctx.translate(this.x * MAP_TILE_SIZE + MAP_TILE_SIZE / 2, this.y * MAP_TILE_SIZE + MAP_TILE_SIZE / 2);
|
|
2414
2432
|
ctx.rotate(angle);
|
|
2415
2433
|
ctx.beginPath();
|
|
2416
2434
|
ctx.moveTo(0, EDGE_SIZE);
|
|
2417
|
-
ctx.lineTo(-(
|
|
2418
|
-
ctx.lineTo(
|
|
2435
|
+
ctx.lineTo(-(MAP_TILE_SIZE / 2 - EDGE_SIZE * 2), MAP_TILE_SIZE / 2 - EDGE_SIZE);
|
|
2436
|
+
ctx.lineTo(MAP_TILE_SIZE / 2 - EDGE_SIZE * 2, MAP_TILE_SIZE / 2 - EDGE_SIZE);
|
|
2419
2437
|
ctx.closePath();
|
|
2420
2438
|
ctx.lineWidth = EDGE_SIZE * 2;
|
|
2421
|
-
ctx.strokeStyle =
|
|
2439
|
+
ctx.strokeStyle = MAP_ELEVATION_EDGE_COLOR;
|
|
2422
2440
|
ctx.stroke();
|
|
2423
|
-
ctx.fillStyle =
|
|
2441
|
+
ctx.fillStyle = MAP_ELEVATION_TILE_COLORS[1];
|
|
2424
2442
|
ctx.fill();
|
|
2425
2443
|
ctx.font = "18px Arial";
|
|
2426
2444
|
ctx.fillStyle = "#000000";
|
|
2427
2445
|
ctx.textAlign = "center";
|
|
2428
2446
|
ctx.textBaseline = "middle";
|
|
2429
|
-
ctx.fillText(String(this.elevation), 0,
|
|
2447
|
+
ctx.fillText(String(this.elevation), 0, MAP_TILE_SIZE / 4 + 4);
|
|
2430
2448
|
ctx.restore();
|
|
2431
2449
|
}
|
|
2432
2450
|
drawLabel(ctx) {
|
|
2433
2451
|
if (this.hideLabel || this.x !== 0 && this.y !== 0 && !this.label) return;
|
|
2434
2452
|
let text = "";
|
|
2435
2453
|
ctx.save();
|
|
2436
|
-
ctx.translate(this.x *
|
|
2454
|
+
ctx.translate(this.x * MAP_TILE_SIZE + MAP_TILE_SIZE / 2, this.y * MAP_TILE_SIZE + MAP_TILE_SIZE / 2);
|
|
2437
2455
|
if (this.label) {
|
|
2438
2456
|
text = this.label.text;
|
|
2439
|
-
ctx.fillStyle = this.label.color ??
|
|
2457
|
+
ctx.fillStyle = this.label.color ?? MAP_ELEVATION_EDGE_COLOR;
|
|
2440
2458
|
ctx.rotate((this.label.rotate ?? 0) * Math.PI / 180);
|
|
2441
2459
|
} else {
|
|
2442
2460
|
if (this.y === 0) text = String.fromCharCode("A".charCodeAt(0) + this.x);
|
|
@@ -2503,10 +2521,10 @@ var HCMap = class {
|
|
|
2503
2521
|
}
|
|
2504
2522
|
tryAddEdge(t0, t1, edgeType) {
|
|
2505
2523
|
if (this.type === "indoorOutdoor" && t0.type === "indoor" && (t1 == null || t1.type === "outdoor")) {
|
|
2506
|
-
t0.addEdge(edgeType,
|
|
2524
|
+
t0.addEdge(edgeType, MAP_INDOOR_EDGE_COLOR, false);
|
|
2507
2525
|
}
|
|
2508
2526
|
if (typeof t0.elevation === "number" && t0.elevation > 1 && (t1 == null || t0.elevation !== t1.elevation)) {
|
|
2509
|
-
t0.addEdge(edgeType,
|
|
2527
|
+
t0.addEdge(edgeType, MAP_ELEVATION_EDGE_COLOR, false);
|
|
2510
2528
|
}
|
|
2511
2529
|
if (t0.isStartingZone && (t1 == null || !t1.isStartingZone)) {
|
|
2512
2530
|
t0.addEdge(edgeType, STARTING_ZONE_EDGE_COLOR, false);
|
|
@@ -2516,7 +2534,7 @@ var HCMap = class {
|
|
|
2516
2534
|
}
|
|
2517
2535
|
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
2536
|
const dashed = t0.terrain === "window" || t0.terrain === "special2";
|
|
2519
|
-
t0.addEdge(edgeType,
|
|
2537
|
+
t0.addEdge(edgeType, MAP_TERRAIN_EDGE_COLORS[t0.terrain] ?? "#FFFFFF", dashed);
|
|
2520
2538
|
}
|
|
2521
2539
|
}
|
|
2522
2540
|
draw(ctx) {
|
|
@@ -2531,7 +2549,7 @@ var HCMap = class {
|
|
|
2531
2549
|
ctx.lineWidth = 1;
|
|
2532
2550
|
for (let y = 0; y < this.height; y++) {
|
|
2533
2551
|
for (let x = 0; x < this.width; x++) {
|
|
2534
|
-
ctx.strokeRect(x *
|
|
2552
|
+
ctx.strokeRect(x * MAP_TILE_SIZE, y * MAP_TILE_SIZE, MAP_TILE_SIZE, MAP_TILE_SIZE);
|
|
2535
2553
|
}
|
|
2536
2554
|
}
|
|
2537
2555
|
ctx.restore();
|
|
@@ -2542,12 +2560,12 @@ var HCMap = class {
|
|
|
2542
2560
|
}
|
|
2543
2561
|
for (const z of this.ziplines) {
|
|
2544
2562
|
ctx.save();
|
|
2545
|
-
ctx.strokeStyle =
|
|
2563
|
+
ctx.strokeStyle = MAP_WALL_COLORS.normal;
|
|
2546
2564
|
ctx.lineCap = "round";
|
|
2547
2565
|
ctx.lineWidth = 4;
|
|
2548
2566
|
ctx.beginPath();
|
|
2549
|
-
ctx.moveTo(z.x0 *
|
|
2550
|
-
ctx.lineTo(z.x1 *
|
|
2567
|
+
ctx.moveTo(z.x0 * MAP_TILE_SIZE + MAP_TILE_SIZE / 2, z.y0 * MAP_TILE_SIZE + MAP_TILE_SIZE / 2);
|
|
2568
|
+
ctx.lineTo(z.x1 * MAP_TILE_SIZE + MAP_TILE_SIZE / 2, z.y1 * MAP_TILE_SIZE + MAP_TILE_SIZE / 2);
|
|
2551
2569
|
ctx.stroke();
|
|
2552
2570
|
ctx.restore();
|
|
2553
2571
|
}
|
|
@@ -2556,15 +2574,15 @@ var HCMap = class {
|
|
|
2556
2574
|
const type = wall.type ?? "normal";
|
|
2557
2575
|
ctx.lineWidth = WALL_SIZE;
|
|
2558
2576
|
if (type === "levelSeparator") {
|
|
2559
|
-
ctx.strokeStyle =
|
|
2577
|
+
ctx.strokeStyle = MAP_WALL_COLORS.normal;
|
|
2560
2578
|
ctx.lineWidth = WALL_SIZE * 2;
|
|
2561
2579
|
ctx.beginPath();
|
|
2562
|
-
ctx.moveTo(wall.x0 *
|
|
2563
|
-
ctx.lineTo(wall.x1 *
|
|
2580
|
+
ctx.moveTo(wall.x0 * MAP_TILE_SIZE, wall.y0 * MAP_TILE_SIZE);
|
|
2581
|
+
ctx.lineTo(wall.x1 * MAP_TILE_SIZE, wall.y1 * MAP_TILE_SIZE);
|
|
2564
2582
|
ctx.stroke();
|
|
2565
2583
|
ctx.lineWidth = WALL_SIZE;
|
|
2566
2584
|
}
|
|
2567
|
-
ctx.strokeStyle =
|
|
2585
|
+
ctx.strokeStyle = MAP_WALL_COLORS[type] ?? MAP_WALL_COLORS.normal;
|
|
2568
2586
|
if (DASHED_WALLS.has(type)) {
|
|
2569
2587
|
ctx.setLineDash([WALL_SIZE, WALL_SIZE]);
|
|
2570
2588
|
ctx.lineDashOffset = WALL_SIZE / 2;
|
|
@@ -2572,8 +2590,8 @@ var HCMap = class {
|
|
|
2572
2590
|
ctx.lineCap = "round";
|
|
2573
2591
|
}
|
|
2574
2592
|
ctx.beginPath();
|
|
2575
|
-
ctx.moveTo(wall.x0 *
|
|
2576
|
-
ctx.lineTo(wall.x1 *
|
|
2593
|
+
ctx.moveTo(wall.x0 * MAP_TILE_SIZE, wall.y0 * MAP_TILE_SIZE);
|
|
2594
|
+
ctx.lineTo(wall.x1 * MAP_TILE_SIZE, wall.y1 * MAP_TILE_SIZE);
|
|
2577
2595
|
ctx.stroke();
|
|
2578
2596
|
}
|
|
2579
2597
|
};
|
|
@@ -2590,14 +2608,14 @@ function mapSquare(map, x, y) {
|
|
|
2590
2608
|
label: tile.label?.text
|
|
2591
2609
|
};
|
|
2592
2610
|
}
|
|
2593
|
-
function
|
|
2611
|
+
function mapElevationLevel(elevation) {
|
|
2594
2612
|
const value = typeof elevation === "number" ? elevation : Number.parseInt(elevation, 10);
|
|
2595
2613
|
return Number.isFinite(value) ? Math.max(1, value) : 2;
|
|
2596
2614
|
}
|
|
2597
2615
|
function mapMaxLevel(map) {
|
|
2598
2616
|
let level = 1;
|
|
2599
2617
|
for (let y = 0; y < map.height; y++) {
|
|
2600
|
-
for (let x = 0; x < map.width; x++) level = Math.max(level,
|
|
2618
|
+
for (let x = 0; x < map.width; x++) level = Math.max(level, mapElevationLevel(mapSquare(map, x, y).elevation));
|
|
2601
2619
|
}
|
|
2602
2620
|
return level;
|
|
2603
2621
|
}
|
|
@@ -2628,7 +2646,7 @@ function inspectMap(map, x, y, tolerance = 0.16) {
|
|
|
2628
2646
|
}
|
|
2629
2647
|
return { square };
|
|
2630
2648
|
}
|
|
2631
|
-
function
|
|
2649
|
+
function mapSquareCoordinate(square) {
|
|
2632
2650
|
let index = square.x + 1, column = "";
|
|
2633
2651
|
while (index > 0) {
|
|
2634
2652
|
index--;
|
|
@@ -2637,13 +2655,13 @@ function squareCoordinate(square) {
|
|
|
2637
2655
|
}
|
|
2638
2656
|
return `${column}${square.y + 1}`;
|
|
2639
2657
|
}
|
|
2640
|
-
var
|
|
2641
|
-
var
|
|
2658
|
+
var MAP_SCENE_LEVEL_HEIGHT = 0.62;
|
|
2659
|
+
var MAP_SCENE_BASE_Z = -0.34;
|
|
2642
2660
|
var WATER_DROP = 0.1;
|
|
2643
2661
|
var TERRAIN_VOLUME = { obscuring: 0.42, window: 0.42 };
|
|
2644
2662
|
var WALL_HEIGHT = { levelSeparator: 0.12, door: 0.62, door2: 0.62 };
|
|
2645
2663
|
var DEFAULT_WALL_HEIGHT = 0.62;
|
|
2646
|
-
function
|
|
2664
|
+
function mapSceneCell(scene, x, y) {
|
|
2647
2665
|
if (x < 0 || y < 0 || x >= scene.width || y >= scene.height) return null;
|
|
2648
2666
|
return scene.cells[y * scene.width + x];
|
|
2649
2667
|
}
|
|
@@ -2654,9 +2672,9 @@ function buildMapScene(map) {
|
|
|
2654
2672
|
for (let y = 0; y < height; y++) {
|
|
2655
2673
|
for (let x = 0; x < width; x++) {
|
|
2656
2674
|
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 *
|
|
2675
|
+
const level = mapElevationLevel(square.elevation);
|
|
2676
|
+
const floor = (level - 1) * MAP_SCENE_LEVEL_HEIGHT - (square.terrain === "water" ? WATER_DROP : 0);
|
|
2677
|
+
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
2678
|
cells.push({ ...square, level, floor, top });
|
|
2661
2679
|
}
|
|
2662
2680
|
}
|
|
@@ -2671,7 +2689,7 @@ function buildMapScene(map) {
|
|
|
2671
2689
|
const x1 = wall.x0 + (wall.x1 - wall.x0) * (i + 1) / pieces, y1 = wall.y0 + (wall.y1 - wall.y0) * (i + 1) / pieces;
|
|
2672
2690
|
const mx = (x0 + x1) / 2, my = (y0 + y1) / 2;
|
|
2673
2691
|
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]) =>
|
|
2692
|
+
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
2693
|
const floors = sides.length ? sides.map((cell) => cell.floor) : [0];
|
|
2676
2694
|
const z1 = Math.max(...floors) + (WALL_HEIGHT[type] ?? DEFAULT_WALL_HEIGHT);
|
|
2677
2695
|
walls.push({ x0, y0, x1, y1, type, z0: Math.min(...floors), z1 });
|
|
@@ -2679,7 +2697,7 @@ function buildMapScene(map) {
|
|
|
2679
2697
|
}
|
|
2680
2698
|
const ramps = [];
|
|
2681
2699
|
for (const ramp of map.ramps ?? []) {
|
|
2682
|
-
const a =
|
|
2700
|
+
const a = mapSceneCell(scene, ramp.x0, ramp.y0), b = mapSceneCell(scene, ramp.x1, ramp.y1);
|
|
2683
2701
|
if (!a || !b || Math.abs(a.x - b.x) + Math.abs(a.y - b.y) !== 1 || a.floor === b.floor) continue;
|
|
2684
2702
|
const [low, high] = a.floor < b.floor ? [a, b] : [b, a];
|
|
2685
2703
|
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 +2707,21 @@ function buildMapScene(map) {
|
|
|
2689
2707
|
y0: line.y0 + 0.5,
|
|
2690
2708
|
x1: line.x1 + 0.5,
|
|
2691
2709
|
y1: line.y1 + 0.5,
|
|
2692
|
-
z: Math.max(
|
|
2710
|
+
z: Math.max(mapSceneCell(scene, line.x0, line.y0)?.top ?? 0, mapSceneCell(scene, line.x1, line.y1)?.top ?? 0) + 0.7
|
|
2693
2711
|
}));
|
|
2694
2712
|
const maxTop = Math.max(...cells.map((cell) => cell.top), ...walls.map((wall) => wall.z1), ...ziplines.map((line) => line.z));
|
|
2695
2713
|
return { width, height, cells, walls, ramps, ziplines, maxTop };
|
|
2696
2714
|
}
|
|
2697
|
-
var
|
|
2698
|
-
var
|
|
2699
|
-
var
|
|
2700
|
-
var
|
|
2701
|
-
function
|
|
2715
|
+
var MAP_DEFAULT_CAMERA = { azimuth: -32, tilt: 52, zoom: 1, panX: 0, panY: 0 };
|
|
2716
|
+
var MAP_TILT_RANGE = [6, 90];
|
|
2717
|
+
var MAP_ZOOM_RANGE = [1, 6];
|
|
2718
|
+
var clampMapValue = (value, min, max) => Math.min(max, Math.max(min, value));
|
|
2719
|
+
function makeMapProjection(scene, camera, viewport) {
|
|
2702
2720
|
const rad = camera.azimuth * Math.PI / 180, tilt = camera.tilt * Math.PI / 180;
|
|
2703
2721
|
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 +
|
|
2722
|
+
const cx2 = scene.width / 2, cy = scene.height / 2, midZ = (scene.maxTop + MAP_SCENE_BASE_Z) / 2;
|
|
2705
2723
|
const halfWidth = Math.hypot(cx2, cy);
|
|
2706
|
-
const halfHeight = Math.hypot(halfWidth, (scene.maxTop -
|
|
2724
|
+
const halfHeight = Math.hypot(halfWidth, (scene.maxTop - MAP_SCENE_BASE_Z) / 2);
|
|
2707
2725
|
const fit = Math.min(viewport.width / (halfWidth * 2.06), viewport.height / (halfHeight * 2.06));
|
|
2708
2726
|
const scale = fit * camera.zoom;
|
|
2709
2727
|
const ox = viewport.width / 2 + camera.panX, oy = viewport.height / 2 + camera.panY;
|
|
@@ -2724,21 +2742,21 @@ function makeProjection(scene, camera, viewport) {
|
|
|
2724
2742
|
}
|
|
2725
2743
|
};
|
|
2726
2744
|
}
|
|
2727
|
-
function
|
|
2728
|
-
const limit = Math.hypot(scene.width, scene.height) / 2 *
|
|
2729
|
-
return { ...camera, panX:
|
|
2745
|
+
function clampMapPan(scene, camera, viewport) {
|
|
2746
|
+
const limit = Math.hypot(scene.width, scene.height) / 2 * makeMapProjection(scene, camera, viewport).scale;
|
|
2747
|
+
return { ...camera, panX: clampMapValue(camera.panX, -limit, limit), panY: clampMapValue(camera.panY, -limit, limit) };
|
|
2730
2748
|
}
|
|
2731
|
-
function
|
|
2732
|
-
const zoom =
|
|
2749
|
+
function zoomMapCamera(scene, camera, viewport, factor, anchor = { x: 0, y: 0 }) {
|
|
2750
|
+
const zoom = clampMapValue(camera.zoom * factor, MAP_ZOOM_RANGE[0], MAP_ZOOM_RANGE[1]);
|
|
2733
2751
|
const applied = zoom / camera.zoom;
|
|
2734
|
-
return
|
|
2752
|
+
return clampMapPan(scene, {
|
|
2735
2753
|
...camera,
|
|
2736
2754
|
zoom,
|
|
2737
2755
|
panX: anchor.x + (camera.panX - anchor.x) * applied,
|
|
2738
2756
|
panY: anchor.y + (camera.panY - anchor.y) * applied
|
|
2739
2757
|
}, viewport);
|
|
2740
2758
|
}
|
|
2741
|
-
var
|
|
2759
|
+
var MAP_CELL_SIDES = [
|
|
2742
2760
|
{ ax: 0, ay: 1, bx: 0, by: 0, dx: -1, dy: 0 },
|
|
2743
2761
|
{ ax: 1, ay: 0, bx: 1, by: 1, dx: 1, dy: 0 },
|
|
2744
2762
|
{ ax: 0, ay: 0, bx: 1, by: 0, dx: 0, dy: -1 },
|
|
@@ -2752,7 +2770,7 @@ var inside = (px, py, polygon2) => {
|
|
|
2752
2770
|
}
|
|
2753
2771
|
return hit;
|
|
2754
2772
|
};
|
|
2755
|
-
function
|
|
2773
|
+
function pickMapScene(scene, view, px, py) {
|
|
2756
2774
|
const items = [];
|
|
2757
2775
|
for (const wall of scene.walls) {
|
|
2758
2776
|
items.push({
|
|
@@ -2762,7 +2780,7 @@ function pickScene(scene, view, px, py) {
|
|
|
2762
2780
|
const c = view.point(wall.x1, wall.y1, wall.z1), d = view.point(wall.x0, wall.y0, wall.z1);
|
|
2763
2781
|
if (!inside(px, py, [a, b, c, d])) return null;
|
|
2764
2782
|
const length2 = (b[0] - a[0]) ** 2 + (b[1] - a[1]) ** 2;
|
|
2765
|
-
const t = length2 ?
|
|
2783
|
+
const t = length2 ? clampMapValue(((px - a[0]) * (b[0] - a[0]) + (py - a[1]) * (b[1] - a[1])) / length2, 0, 1) : 0.5;
|
|
2766
2784
|
return { x: wall.x0 + (wall.x1 - wall.x0) * t, y: wall.y0 + (wall.y1 - wall.y0) * t };
|
|
2767
2785
|
}
|
|
2768
2786
|
});
|
|
@@ -2778,12 +2796,12 @@ function pickScene(scene, view, px, py) {
|
|
|
2778
2796
|
const det = ax * by - ay * bx;
|
|
2779
2797
|
if (!det) return { x: cell.x + 0.5, y: cell.y + 0.5 };
|
|
2780
2798
|
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 +
|
|
2799
|
+
return { x: cell.x + clampMapValue(u, 0, 1), y: cell.y + clampMapValue(v, 0, 1) };
|
|
2782
2800
|
}
|
|
2783
|
-
for (const side of
|
|
2801
|
+
for (const side of MAP_CELL_SIDES) {
|
|
2784
2802
|
if (side.dx * view.sin + side.dy * view.cos <= 0) continue;
|
|
2785
|
-
const neighbor =
|
|
2786
|
-
const low = neighbor ? neighbor.top :
|
|
2803
|
+
const neighbor = mapSceneCell(scene, cell.x + side.dx, cell.y + side.dy);
|
|
2804
|
+
const low = neighbor ? neighbor.top : MAP_SCENE_BASE_Z;
|
|
2787
2805
|
if (low >= cell.top) continue;
|
|
2788
2806
|
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
2807
|
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 +2820,7 @@ function pickScene(scene, view, px, py) {
|
|
|
2802
2820
|
}
|
|
2803
2821
|
return null;
|
|
2804
2822
|
}
|
|
2805
|
-
function
|
|
2823
|
+
function mapFootprintOutline({ x, y, width, height, cells }, boardWidth, boardHeight) {
|
|
2806
2824
|
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
2825
|
const squares = [];
|
|
2808
2826
|
const edges = [];
|
|
@@ -2857,27 +2875,27 @@ function face(ctx, points, color) {
|
|
|
2857
2875
|
ctx.stroke();
|
|
2858
2876
|
}
|
|
2859
2877
|
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
|
|
2878
|
+
if (cell.terrain === "blocking") return MAP_TERRAIN_EDGE_COLORS.blocking;
|
|
2879
|
+
if (cell.level > 1) return MAP_ELEVATION_TILE_COLORS[Math.min(cell.level, MAP_ELEVATION_TILE_COLORS.length) - 1];
|
|
2880
|
+
if (cell.terrain === "water") return MAP_TERRAIN_EDGE_COLORS.water;
|
|
2881
|
+
if (cell.terrain === "hindering") return MAP_TERRAIN_EDGE_COLORS.hindering;
|
|
2882
|
+
if (cell.terrain === "obscuring") return MAP_TERRAIN_EDGE_COLORS.obscuring;
|
|
2883
|
+
if (cell.terrain === "window") return MAP_TERRAIN_EDGE_COLORS.window;
|
|
2866
2884
|
return SIDE_COLORS[cell.environment] ?? SIDE_COLORS.unknown;
|
|
2867
2885
|
}
|
|
2868
2886
|
function drawCellSides(ctx, scene, view, cell) {
|
|
2869
2887
|
const { point } = view;
|
|
2870
|
-
for (const side of
|
|
2888
|
+
for (const side of MAP_CELL_SIDES) {
|
|
2871
2889
|
if (side.dx * view.sin + side.dy * view.cos <= 0) continue;
|
|
2872
|
-
const neighbor =
|
|
2873
|
-
const low = neighbor ? neighbor.top :
|
|
2890
|
+
const neighbor = mapSceneCell(scene, cell.x + side.dx, cell.y + side.dy);
|
|
2891
|
+
const low = neighbor ? neighbor.top : MAP_SCENE_BASE_Z;
|
|
2874
2892
|
if (low >= cell.top) continue;
|
|
2875
2893
|
const ax = cell.x + side.ax, ay = cell.y + side.ay, bx = cell.x + side.bx, by = cell.y + side.by;
|
|
2876
2894
|
const light = faceLight(view, side.dx, side.dy);
|
|
2877
2895
|
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
2896
|
ctx.strokeStyle = "rgba(0,0,0,.26)";
|
|
2879
2897
|
ctx.lineWidth = 1;
|
|
2880
|
-
for (let z = 0; z < cell.top - 0.01; z +=
|
|
2898
|
+
for (let z = 0; z < cell.top - 0.01; z += MAP_SCENE_LEVEL_HEIGHT) {
|
|
2881
2899
|
if (z <= low + 0.01) continue;
|
|
2882
2900
|
const [x0, y0] = point(ax, ay, z), [x1, y1] = point(bx, by, z);
|
|
2883
2901
|
ctx.beginPath();
|
|
@@ -2891,13 +2909,13 @@ function drawCellTop(ctx, scene, view, cell, texture, shadows) {
|
|
|
2891
2909
|
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
2910
|
ctx.save();
|
|
2893
2911
|
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 *
|
|
2912
|
+
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
2913
|
if (cell.environment === "indoor") {
|
|
2896
2914
|
ctx.fillStyle = (cell.x + cell.y) % 2 ? "rgba(52,62,92,.13)" : "rgba(52,62,92,.07)";
|
|
2897
2915
|
ctx.fillRect(0, 0, 1, 1);
|
|
2898
2916
|
}
|
|
2899
|
-
|
|
2900
|
-
const neighbor =
|
|
2917
|
+
MAP_CELL_SIDES.forEach((side, index) => {
|
|
2918
|
+
const neighbor = mapSceneCell(scene, cell.x + side.dx, cell.y + side.dy);
|
|
2901
2919
|
if (!neighbor || neighbor.top <= cell.top + 0.01) return;
|
|
2902
2920
|
ctx.fillStyle = shadows[index];
|
|
2903
2921
|
ctx.fillRect(0, 0, 1, 1);
|
|
@@ -2938,7 +2956,7 @@ function drawRamp(ctx, view, ramp) {
|
|
|
2938
2956
|
const sx = ramp.dy * 0.2, sy = ramp.dx * 0.2, lx = -ramp.dx * 0.38, ly = -ramp.dy * 0.38;
|
|
2939
2957
|
const a = [cx2 - sx, cy - sy], b = [cx2 + sx, cy + sy];
|
|
2940
2958
|
const c = [cx2 + sx + lx, cy + sy + ly], d = [cx2 - sx + lx, cy - sy + ly];
|
|
2941
|
-
const color =
|
|
2959
|
+
const color = MAP_ELEVATION_TILE_COLORS[1];
|
|
2942
2960
|
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
2961
|
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
2962
|
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 +2976,7 @@ function drawWall(ctx, view, wall) {
|
|
|
2958
2976
|
const x0 = Math.min(wall.x0, wall.x1) - ox, x1 = Math.max(wall.x0, wall.x1) + ox;
|
|
2959
2977
|
const y0 = Math.min(wall.y0, wall.y1) - oy, y1 = Math.max(wall.y0, wall.y1) + oy;
|
|
2960
2978
|
const alpha = TRANSLUCENT_WALLS[wall.type];
|
|
2961
|
-
const color = GLASS_COLORS[wall.type] ?? (wall.type === "normal" ? "#30343c" :
|
|
2979
|
+
const color = GLASS_COLORS[wall.type] ?? (wall.type === "normal" ? "#30343c" : MAP_WALL_COLORS[wall.type] ?? "#30343c");
|
|
2962
2980
|
ctx.save();
|
|
2963
2981
|
if (alpha) ctx.globalAlpha = alpha;
|
|
2964
2982
|
if (wall.type === "door" || wall.type === "door2") {
|
|
@@ -2974,10 +2992,10 @@ function drawWall(ctx, view, wall) {
|
|
|
2974
2992
|
ctx.restore();
|
|
2975
2993
|
}
|
|
2976
2994
|
function drawIndoorCurb(ctx, scene, view, cell) {
|
|
2977
|
-
ctx.strokeStyle =
|
|
2995
|
+
ctx.strokeStyle = MAP_INDOOR_EDGE_COLOR;
|
|
2978
2996
|
ctx.lineWidth = 2;
|
|
2979
|
-
for (const side of
|
|
2980
|
-
const neighbor =
|
|
2997
|
+
for (const side of MAP_CELL_SIDES) {
|
|
2998
|
+
const neighbor = mapSceneCell(scene, cell.x + side.dx, cell.y + side.dy);
|
|
2981
2999
|
if (neighbor?.environment !== "outdoor") continue;
|
|
2982
3000
|
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
3001
|
ctx.beginPath();
|
|
@@ -2987,7 +3005,7 @@ function drawIndoorCurb(ctx, scene, view, cell) {
|
|
|
2987
3005
|
}
|
|
2988
3006
|
}
|
|
2989
3007
|
function drawFootprint(ctx, scene, view, footprint) {
|
|
2990
|
-
const { squares, edges, corners } =
|
|
3008
|
+
const { squares, edges, corners } = mapFootprintOutline(footprint, scene.width, scene.height);
|
|
2991
3009
|
if (!squares.length) return;
|
|
2992
3010
|
const { top, valid } = footprint;
|
|
2993
3011
|
const color = valid ? MOVE_VALID_COLOR : MOVE_INVALID_COLOR;
|
|
@@ -3034,7 +3052,7 @@ function drawHighlight(ctx, scene, view, highlight) {
|
|
|
3034
3052
|
if (highlight.line) {
|
|
3035
3053
|
const { x0, y0, x1, y1 } = highlight.line;
|
|
3036
3054
|
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]) =>
|
|
3055
|
+
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
3056
|
const z = Math.max(...near);
|
|
3039
3057
|
const [ax, ay] = view.point(x0, y0, z), [bx, by] = view.point(x1, y1, z);
|
|
3040
3058
|
ctx.strokeStyle = "rgba(0,0,0,.6)";
|
|
@@ -3047,7 +3065,7 @@ function drawHighlight(ctx, scene, view, highlight) {
|
|
|
3047
3065
|
ctx.lineCap = "butt";
|
|
3048
3066
|
return;
|
|
3049
3067
|
}
|
|
3050
|
-
const cell = highlight.square &&
|
|
3068
|
+
const cell = highlight.square && mapSceneCell(scene, highlight.square.x, highlight.square.y);
|
|
3051
3069
|
if (!cell) return;
|
|
3052
3070
|
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
3071
|
ctx.fillStyle = "rgba(0,0,0,.16)";
|
|
@@ -3058,7 +3076,7 @@ function drawHighlight(ctx, scene, view, highlight) {
|
|
|
3058
3076
|
}
|
|
3059
3077
|
function drawMapScene(ctx, scene, view, texture, highlight, { tufts = true } = {}) {
|
|
3060
3078
|
const pad = 0.35;
|
|
3061
|
-
polygon(ctx, [view.point(-pad, -pad,
|
|
3079
|
+
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
3080
|
ctx.fillStyle = "rgba(0,0,0,.14)";
|
|
3063
3081
|
ctx.fill();
|
|
3064
3082
|
const shadowDepth = 0.24;
|
|
@@ -3107,8 +3125,8 @@ var MAP_THUMBNAIL_HEIGHT = 192;
|
|
|
3107
3125
|
function renderMapThumbnail(json, height = MAP_THUMBNAIL_HEIGHT) {
|
|
3108
3126
|
if (!json?.width || !json?.height) return null;
|
|
3109
3127
|
const full = document.createElement("canvas");
|
|
3110
|
-
full.width = json.width *
|
|
3111
|
-
full.height = json.height *
|
|
3128
|
+
full.width = json.width * MAP_TILE_SIZE;
|
|
3129
|
+
full.height = json.height * MAP_TILE_SIZE;
|
|
3112
3130
|
const fctx = full.getContext("2d");
|
|
3113
3131
|
if (!fctx) return null;
|
|
3114
3132
|
new HCMap(json).draw(fctx);
|
|
@@ -3157,7 +3175,7 @@ var MapBoardView = forwardRef(function MapBoardView2({ map, canvasLabel, selecti
|
|
|
3157
3175
|
if (event.pointerType !== "touch") onLeave?.();
|
|
3158
3176
|
},
|
|
3159
3177
|
children: [
|
|
3160
|
-
/* @__PURE__ */ jsx("canvas", { ref: canvasRef, width: map.width *
|
|
3178
|
+
/* @__PURE__ */ jsx("canvas", { ref: canvasRef, width: map.width * MAP_TILE_SIZE, height: map.height * MAP_TILE_SIZE, role: "img", "aria-label": canvasLabel }),
|
|
3161
3179
|
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
3180
|
]
|
|
3163
3181
|
}
|
|
@@ -3226,31 +3244,33 @@ function MapEnvironmentView({ types, icons, labels, className }) {
|
|
|
3226
3244
|
}
|
|
3227
3245
|
export {
|
|
3228
3246
|
AvatarView,
|
|
3229
|
-
BACK_TABLE_HEIGHT,
|
|
3230
|
-
BASE_Z,
|
|
3231
|
-
BYSTANDER_DIAL_STEP,
|
|
3232
3247
|
Button,
|
|
3233
|
-
CARD_HEIGHT,
|
|
3234
|
-
CARD_WIDTH,
|
|
3235
|
-
CELL_SIDES,
|
|
3236
3248
|
COMBAT_DIAL_STATS,
|
|
3237
3249
|
Checkbox,
|
|
3238
3250
|
Chip,
|
|
3239
3251
|
CombatDialView,
|
|
3240
3252
|
CommandPalette,
|
|
3241
|
-
|
|
3242
|
-
Dialog,
|
|
3243
|
-
ELEVATION_EDGE_COLOR,
|
|
3244
|
-
ELEVATION_TILE_COLORS,
|
|
3253
|
+
Dialog2 as Dialog,
|
|
3245
3254
|
EquipmentIcon,
|
|
3246
3255
|
Field,
|
|
3247
3256
|
FilterBar,
|
|
3248
3257
|
HCMap,
|
|
3249
|
-
INDOOR_EDGE_COLOR,
|
|
3250
3258
|
IconButton,
|
|
3251
|
-
LEVEL_HEIGHT,
|
|
3252
3259
|
ListRow,
|
|
3260
|
+
MAP_CELL_SIDES,
|
|
3261
|
+
MAP_DEFAULT_CAMERA,
|
|
3262
|
+
MAP_ELEVATION_EDGE_COLOR,
|
|
3263
|
+
MAP_ELEVATION_TILE_COLORS,
|
|
3264
|
+
MAP_INDOOR_EDGE_COLOR,
|
|
3265
|
+
MAP_SCENE_BASE_Z,
|
|
3266
|
+
MAP_SCENE_LEVEL_HEIGHT,
|
|
3267
|
+
MAP_TERRAIN_EDGE_COLORS,
|
|
3268
|
+
MAP_TERRAIN_TILE_COLORS,
|
|
3253
3269
|
MAP_THUMBNAIL_HEIGHT,
|
|
3270
|
+
MAP_TILE_SIZE,
|
|
3271
|
+
MAP_TILT_RANGE,
|
|
3272
|
+
MAP_WALL_COLORS,
|
|
3273
|
+
MAP_ZOOM_RANGE,
|
|
3254
3274
|
MINI_DIAL_STATS,
|
|
3255
3275
|
MapBoardPlaceholder,
|
|
3256
3276
|
MapBoardView,
|
|
@@ -3267,7 +3287,11 @@ export {
|
|
|
3267
3287
|
MenuSeparator,
|
|
3268
3288
|
Meter,
|
|
3269
3289
|
MiniDialView,
|
|
3290
|
+
PHYSICAL_CARD_BACK_TABLE_HEIGHT,
|
|
3291
|
+
PHYSICAL_CARD_BYSTANDER_DIAL_STEP,
|
|
3292
|
+
PHYSICAL_CARD_HEIGHT,
|
|
3270
3293
|
PHYSICAL_CARD_STATS,
|
|
3294
|
+
PHYSICAL_CARD_WIDTH,
|
|
3271
3295
|
PageHeader,
|
|
3272
3296
|
PanelHeader,
|
|
3273
3297
|
PhysicalCardFocusView,
|
|
@@ -3285,35 +3309,30 @@ export {
|
|
|
3285
3309
|
StatGlyphView,
|
|
3286
3310
|
StatIcon,
|
|
3287
3311
|
Surface,
|
|
3288
|
-
TERRAIN_EDGE_COLORS,
|
|
3289
|
-
TERRAIN_TILE_COLORS,
|
|
3290
|
-
TILE_SIZE,
|
|
3291
|
-
TILT_RANGE,
|
|
3292
3312
|
Table,
|
|
3293
|
-
WALL_COLORS,
|
|
3294
|
-
ZOOM_RANGE,
|
|
3295
|
-
backCount,
|
|
3296
3313
|
buildMapScene,
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
devicePosition,
|
|
3314
|
+
clampMapPan,
|
|
3315
|
+
clampMapValue,
|
|
3300
3316
|
drawMapScene,
|
|
3301
|
-
elevationLevel,
|
|
3302
|
-
footprintOutline,
|
|
3303
3317
|
inspectMap,
|
|
3304
|
-
|
|
3318
|
+
makeMapProjection,
|
|
3319
|
+
mapElevationLevel,
|
|
3305
3320
|
mapEnvironments,
|
|
3321
|
+
mapFootprintOutline,
|
|
3306
3322
|
mapMaxLevel,
|
|
3323
|
+
mapSceneCell,
|
|
3307
3324
|
mapSquare,
|
|
3308
|
-
|
|
3309
|
-
|
|
3325
|
+
mapSquareCoordinate,
|
|
3326
|
+
nextPhysicalCardDistribution,
|
|
3327
|
+
physicalCardBackCount,
|
|
3328
|
+
physicalCardDevicePosition,
|
|
3329
|
+
physicalCardTiltAt,
|
|
3330
|
+
physicalCardTiltStyles,
|
|
3331
|
+
pickMapScene,
|
|
3310
3332
|
placePopup,
|
|
3333
|
+
portalContainerFor,
|
|
3311
3334
|
renderMapThumbnail,
|
|
3312
|
-
|
|
3313
|
-
sceneCell,
|
|
3314
|
-
squareCoordinate,
|
|
3315
|
-
tiltAt,
|
|
3316
|
-
tiltStyles,
|
|
3335
|
+
requestPhysicalCardMotionAccess,
|
|
3317
3336
|
useImageChain,
|
|
3318
|
-
|
|
3337
|
+
zoomMapCamera
|
|
3319
3338
|
};
|