@dreamboard-games/sdk 0.2.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/LICENSE.md +96 -0
- package/README.md +12 -0
- package/dist/HandView-ncJIVLhN.d.ts +193 -0
- package/dist/ResourceCounter-CTREyF73.d.ts +102 -0
- package/dist/ThemeProvider-fy0_QzgO.d.ts +99 -0
- package/dist/bundle-TIZcw8LB.d.ts +281 -0
- package/dist/cards-Sl3b40Mv.d.ts +13 -0
- package/dist/chunk-7YAHLYBR.js +481 -0
- package/dist/chunk-7YAHLYBR.js.map +1 -0
- package/dist/chunk-FDNZTDD6.js +8085 -0
- package/dist/chunk-FDNZTDD6.js.map +1 -0
- package/dist/chunk-GKKBPPSW.js +598 -0
- package/dist/chunk-GKKBPPSW.js.map +1 -0
- package/dist/chunk-I46YJSOD.js +1 -0
- package/dist/chunk-I46YJSOD.js.map +1 -0
- package/dist/chunk-KAELH4KC.js +104 -0
- package/dist/chunk-KAELH4KC.js.map +1 -0
- package/dist/chunk-PZ5AY32C.js +10 -0
- package/dist/chunk-PZ5AY32C.js.map +1 -0
- package/dist/chunk-T3ZKNUZ7.js +1 -0
- package/dist/chunk-T3ZKNUZ7.js.map +1 -0
- package/dist/chunk-T52J5RMF.js +1 -0
- package/dist/chunk-T52J5RMF.js.map +1 -0
- package/dist/chunk-TDSWKVZ4.js +5401 -0
- package/dist/chunk-TDSWKVZ4.js.map +1 -0
- package/dist/chunk-U5C6BONG.js +34 -0
- package/dist/chunk-U5C6BONG.js.map +1 -0
- package/dist/chunk-VDXOF4FW.js +69 -0
- package/dist/chunk-VDXOF4FW.js.map +1 -0
- package/dist/chunk-VFTAA4WO.js +115 -0
- package/dist/chunk-VFTAA4WO.js.map +1 -0
- package/dist/chunk-WN74KVNY.js +17 -0
- package/dist/chunk-WN74KVNY.js.map +1 -0
- package/dist/chunk-WYPQ3GG5.js +10990 -0
- package/dist/chunk-WYPQ3GG5.js.map +1 -0
- package/dist/components-D5ZRE2Hl.d.ts +1451 -0
- package/dist/generated/runtime/primitives.d.ts +12 -0
- package/dist/generated/runtime/primitives.js +180 -0
- package/dist/generated/runtime/primitives.js.map +1 -0
- package/dist/generated/runtime-api.d.ts +3 -0
- package/dist/generated/runtime-api.js +2 -0
- package/dist/generated/runtime-api.js.map +1 -0
- package/dist/generated/runtime.d.ts +14 -0
- package/dist/generated/runtime.js +18 -0
- package/dist/generated/runtime.js.map +1 -0
- package/dist/generated/workspace-contract.d.ts +14 -0
- package/dist/generated/workspace-contract.js +14 -0
- package/dist/generated/workspace-contract.js.map +1 -0
- package/dist/hex-board-view-D_07hO6O.d.ts +933 -0
- package/dist/hex-color-MhOyuY-o.d.ts +8 -0
- package/dist/index-BwqPQtBu.d.ts +1433 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/infrastructure/reducer-bundle-abi.d.ts +1083 -0
- package/dist/infrastructure/reducer-bundle-abi.js +14 -0
- package/dist/infrastructure/reducer-bundle-abi.js.map +1 -0
- package/dist/infrastructure/workspace-codegen.d.ts +53 -0
- package/dist/infrastructure/workspace-codegen.js +44 -0
- package/dist/infrastructure/workspace-codegen.js.map +1 -0
- package/dist/manifest-contract-BNHVGFtU.d.ts +9 -0
- package/dist/package-set.d.ts +13 -0
- package/dist/package-set.js +12 -0
- package/dist/package-set.js.map +1 -0
- package/dist/primitive-props-DpKs-GCr.d.ts +11 -0
- package/dist/reducer.d.ts +3786 -0
- package/dist/reducer.js +8131 -0
- package/dist/reducer.js.map +1 -0
- package/dist/runtime/primitives.d.ts +226 -0
- package/dist/runtime/primitives.js +180 -0
- package/dist/runtime/primitives.js.map +1 -0
- package/dist/runtime/types/runtime-api.d.ts +1 -0
- package/dist/runtime/types/runtime-api.js +2 -0
- package/dist/runtime/types/runtime-api.js.map +1 -0
- package/dist/runtime/workspace-contract.d.ts +172 -0
- package/dist/runtime/workspace-contract.js +14 -0
- package/dist/runtime/workspace-contract.js.map +1 -0
- package/dist/runtime-api-3dshj6kK.d.ts +101 -0
- package/dist/runtime-api-DWxvTr-O.d.ts +379 -0
- package/dist/runtime.d.ts +58 -0
- package/dist/runtime.js +13 -0
- package/dist/runtime.js.map +1 -0
- package/dist/slots-1GPGihk8.d.ts +8 -0
- package/dist/testing.d.ts +149 -0
- package/dist/testing.js +513 -0
- package/dist/testing.js.map +1 -0
- package/dist/types.d.ts +496 -0
- package/dist/types.js +28 -0
- package/dist/types.js.map +1 -0
- package/dist/ui/components.d.ts +16 -0
- package/dist/ui/components.js +192 -0
- package/dist/ui/components.js.map +1 -0
- package/dist/ui/defaults.d.ts +19 -0
- package/dist/ui/defaults.js +104 -0
- package/dist/ui/defaults.js.map +1 -0
- package/dist/ui/plugin-styles.css +250 -0
- package/dist/ui/types/player-state.d.ts +365 -0
- package/dist/ui/types/player-state.js +1 -0
- package/dist/ui/types/player-state.js.map +1 -0
- package/dist/ui-contract-iQfTtUSL.d.ts +1161 -0
- package/dist/ui.d.ts +320 -0
- package/dist/ui.js +253 -0
- package/dist/ui.js.map +1 -0
- package/package.json +199 -0
- package/src/generated/reducer-contract/builders.ts +90 -0
- package/src/generated/reducer-contract/version.ts +9 -0
- package/src/generated/reducer-contract/wire.ts +100 -0
- package/src/generated/reducer-contract/zod.ts +101 -0
- package/src/generated/runtime/primitives.ts +2 -0
- package/src/generated/runtime-api.ts +5 -0
- package/src/generated/runtime.ts +35 -0
- package/src/generated/workspace-contract.ts +2 -0
- package/src/index.ts +7 -0
- package/src/infrastructure/reducer-bundle-abi.ts +8 -0
- package/src/infrastructure/reducer-contract/bundle.ts +37 -0
- package/src/infrastructure/workspace-codegen/hex-geometry.ts +69 -0
- package/src/infrastructure/workspace-codegen/index.ts +64 -0
- package/src/infrastructure/workspace-codegen/manifest-contract.ts +6632 -0
- package/src/infrastructure/workspace-codegen/manifest-validation.ts +795 -0
- package/src/infrastructure/workspace-codegen/ownership.ts +131 -0
- package/src/infrastructure/workspace-codegen/preset-card-sets.ts +169 -0
- package/src/infrastructure/workspace-codegen/seeds.ts +1705 -0
- package/src/infrastructure/workspace-codegen.ts +1 -0
- package/src/package-set.ts +19 -0
- package/src/reducer/authoring/contract.ts +157 -0
- package/src/reducer/authoring/effect.ts +224 -0
- package/src/reducer/authoring/game.ts +23 -0
- package/src/reducer/authoring/interaction.ts +98 -0
- package/src/reducer/authoring/phase.ts +300 -0
- package/src/reducer/authoring/types.ts +70 -0
- package/src/reducer/authoring/validation.ts +382 -0
- package/src/reducer/authoring/view-stage.ts +68 -0
- package/src/reducer/authoring.ts +29 -0
- package/src/reducer/bundle/ingress-bundle.ts +491 -0
- package/src/reducer/bundle/trusted/engine-instruction-resolver.ts +254 -0
- package/src/reducer/bundle/trusted/flow-instruction-resolver.ts +73 -0
- package/src/reducer/bundle/trusted/instruction-runner.ts +414 -0
- package/src/reducer/bundle/trusted/interaction-authorization.ts +137 -0
- package/src/reducer/bundle/trusted/interaction-collectors.ts +859 -0
- package/src/reducer/bundle/trusted/interaction-decision.ts +747 -0
- package/src/reducer/bundle/trusted/interaction-resolver.ts +95 -0
- package/src/reducer/bundle/trusted/interaction-types.ts +171 -0
- package/src/reducer/bundle/trusted/lifecycle-runner.ts +427 -0
- package/src/reducer/bundle/trusted/projection-builder.ts +356 -0
- package/src/reducer/bundle/trusted/projection-context.ts +39 -0
- package/src/reducer/bundle/trusted/rng-sampler.ts +150 -0
- package/src/reducer/bundle/trusted/runtime-registry.ts +120 -0
- package/src/reducer/bundle/trusted/runtime-scope.ts +336 -0
- package/src/reducer/bundle/trusted/simultaneous-player.ts +97 -0
- package/src/reducer/bundle/trusted/stage-resolver.ts +87 -0
- package/src/reducer/bundle/trusted/static-projection.ts +116 -0
- package/src/reducer/bundle/trusted/trusted-runtime-args.ts +97 -0
- package/src/reducer/bundle/trusted/trusted-runtime-result.ts +39 -0
- package/src/reducer/bundle/trusted/trusted-setup-profiles.ts +43 -0
- package/src/reducer/bundle/trusted/trusted-state-codec.ts +48 -0
- package/src/reducer/bundle/trusted-bundle.ts +97 -0
- package/src/reducer/bundle/types.ts +171 -0
- package/src/reducer/bundle.ts +2 -0
- package/src/reducer/client-param-schemas.ts +57 -0
- package/src/reducer/compose.ts +34 -0
- package/src/reducer/core/runtime-input.ts +30 -0
- package/src/reducer/core/runtime-instruction.ts +59 -0
- package/src/reducer/core/types.ts +62 -0
- package/src/reducer/definition-index.ts +277 -0
- package/src/reducer/derived.ts +106 -0
- package/src/reducer/effects.ts +92 -0
- package/src/reducer/engine/runtime-instruction-engine.ts +155 -0
- package/src/reducer/ingress/decode-runtime-input.ts +7 -0
- package/src/reducer/ingress/decode-session-state.ts +9 -0
- package/src/reducer/ingress/encode-session-state.ts +6 -0
- package/src/reducer/ingress/input-codec.ts +18 -0
- package/src/reducer/ingress/phase-schemas.ts +62 -0
- package/src/reducer/ingress/raw-types.ts +107 -0
- package/src/reducer/ingress/runtime-codec.ts +14 -0
- package/src/reducer/ingress/runtime-payload.ts +13 -0
- package/src/reducer/ingress/session-codec.ts +392 -0
- package/src/reducer/ingress/types.ts +6 -0
- package/src/reducer/inputs/boardInput.ts +217 -0
- package/src/reducer/inputs/boardTarget.ts +190 -0
- package/src/reducer/inputs/cardInput.ts +86 -0
- package/src/reducer/inputs/cardTarget.ts +101 -0
- package/src/reducer/inputs/choiceTarget.ts +104 -0
- package/src/reducer/inputs/defineInputs.ts +71 -0
- package/src/reducer/inputs/formInput.ts +809 -0
- package/src/reducer/inputs/many.ts +120 -0
- package/src/reducer/inputs/promptInput.ts +87 -0
- package/src/reducer/inputs/rngInput.ts +58 -0
- package/src/reducer/inputs/targetRule.ts +123 -0
- package/src/reducer/inputs.ts +41 -0
- package/src/reducer/model/definition.ts +1072 -0
- package/src/reducer/model/extract.ts +745 -0
- package/src/reducer/model/manifest.ts +570 -0
- package/src/reducer/model/queries.ts +641 -0
- package/src/reducer/model/runtime.ts +264 -0
- package/src/reducer/model/spec.ts +1386 -0
- package/src/reducer/model/table.ts +260 -0
- package/src/reducer/model.ts +7 -0
- package/src/reducer/ops.ts +1034 -0
- package/src/reducer/parse-utils.ts +28 -0
- package/src/reducer/per-player.ts +422 -0
- package/src/reducer/rng.ts +69 -0
- package/src/reducer/schema-helpers.ts +185 -0
- package/src/reducer/setup-bootstrap-helpers.ts +171 -0
- package/src/reducer/setup-bootstrap.ts +481 -0
- package/src/reducer/table-ops.ts +2671 -0
- package/src/reducer/table-queries.ts +372 -0
- package/src/reducer/transaction.ts +120 -0
- package/src/reducer.ts +314 -0
- package/src/runtime/primitives.ts +1 -0
- package/src/runtime/types/runtime-api.ts +1 -0
- package/src/runtime/workspace-contract.ts +32 -0
- package/src/runtime-internal/components/InteractionForm.tsx +1309 -0
- package/src/runtime-internal/components/PluginRuntime.tsx +103 -0
- package/src/runtime-internal/components/board/target-layer.ts +70 -0
- package/src/runtime-internal/context/ClientParamSchemaContext.tsx +44 -0
- package/src/runtime-internal/context/InteractionDraftContext.tsx +279 -0
- package/src/runtime-internal/context/PluginSessionContext.tsx +47 -0
- package/src/runtime-internal/context/PluginStateContext.tsx +262 -0
- package/src/runtime-internal/context/RuntimeContext.tsx +96 -0
- package/src/runtime-internal/defaults/components.tsx +409 -0
- package/src/runtime-internal/defaults/index.ts +11 -0
- package/src/runtime-internal/errors/ValidationError.ts +29 -0
- package/src/runtime-internal/hooks/useActivePlayers.ts +33 -0
- package/src/runtime-internal/hooks/useBoardInteractions.ts +665 -0
- package/src/runtime-internal/hooks/useGameSelector.ts +105 -0
- package/src/runtime-internal/hooks/useGameView.ts +9 -0
- package/src/runtime-internal/hooks/useInteractionByKey.ts +354 -0
- package/src/runtime-internal/hooks/useInteractionHandle.ts +438 -0
- package/src/runtime-internal/hooks/useIsMyTurn.ts +20 -0
- package/src/runtime-internal/hooks/useLobby.ts +76 -0
- package/src/runtime-internal/hooks/useMe.ts +48 -0
- package/src/runtime-internal/hooks/usePlayerInfo.ts +28 -0
- package/src/runtime-internal/hooks/usePlayerTurnOrder.ts +23 -0
- package/src/runtime-internal/hooks/usePluginRuntime.ts +147 -0
- package/src/runtime-internal/hooks/useSeatInbox.ts +61 -0
- package/src/runtime-internal/hooks/useSimultaneousPhase.ts +10 -0
- package/src/runtime-internal/index.ts +42 -0
- package/src/runtime-internal/internal.ts +43 -0
- package/src/runtime-internal/plugin-styles.css +250 -0
- package/src/runtime-internal/primitives/board.tsx +459 -0
- package/src/runtime-internal/primitives/dialog-lifecycle.ts +58 -0
- package/src/runtime-internal/primitives/dice.tsx +79 -0
- package/src/runtime-internal/primitives/game-ui-provider.tsx +35 -0
- package/src/runtime-internal/primitives/game.tsx +387 -0
- package/src/runtime-internal/primitives/hand-intent-adapter.ts +147 -0
- package/src/runtime-internal/primitives/hand-surface.tsx +594 -0
- package/src/runtime-internal/primitives/index.ts +196 -0
- package/src/runtime-internal/primitives/interaction-form-binding.tsx +56 -0
- package/src/runtime-internal/primitives/interaction-submit.ts +90 -0
- package/src/runtime-internal/primitives/interaction.tsx +987 -0
- package/src/runtime-internal/primitives/phase.tsx +43 -0
- package/src/runtime-internal/primitives/player-roster.tsx +302 -0
- package/src/runtime-internal/primitives/primitive-props.tsx +101 -0
- package/src/runtime-internal/primitives/prompt.tsx +255 -0
- package/src/runtime-internal/primitives/ui.tsx +60 -0
- package/src/runtime-internal/primitives/zone.tsx +791 -0
- package/src/runtime-internal/reducer.ts +30 -0
- package/src/runtime-internal/runtime/createPluginRuntimeAPI.ts +605 -0
- package/src/runtime-internal/types/plugin-state.ts +508 -0
- package/src/runtime-internal/types/reducer-state.ts +24 -0
- package/src/runtime-internal/types/runtime-api.ts +114 -0
- package/src/runtime-internal/ui-contract.ts +519 -0
- package/src/runtime-internal/utils/card-intent-adapter.ts +546 -0
- package/src/runtime-internal/utils/interaction-inputs.ts +492 -0
- package/src/runtime-internal/utils/interaction-labels.ts +23 -0
- package/src/runtime-internal/utils/interaction-router.ts +273 -0
- package/src/runtime-internal/utils/interaction-status.ts +74 -0
- package/src/runtime-internal/workspace-contract.ts +1170 -0
- package/src/runtime.ts +34 -0
- package/src/testing/create-expect-api.ts +352 -0
- package/src/testing/create-test-runtime.ts +381 -0
- package/src/testing/definitions.ts +127 -0
- package/src/testing/index.ts +3 -0
- package/src/testing.ts +1 -0
- package/src/type-stubs/manifest-contract.d.ts +42 -0
- package/src/type-stubs/manifest-contract.js +72 -0
- package/src/type-stubs/ui-contract.d.ts +5 -0
- package/src/type-stubs/ui-contract.js +1 -0
- package/src/types/authoring-card-properties.type-test.ts +266 -0
- package/src/types/authoring.ts +1282 -0
- package/src/types/cards.ts +19 -0
- package/src/types/contracts.ts +1550 -0
- package/src/types/generated-helpers.ts +35 -0
- package/src/types/index.ts +147 -0
- package/src/types/slots.ts +11 -0
- package/src/types.ts +1 -0
- package/src/ui/components/ActionButton.tsx +97 -0
- package/src/ui/components/ActionPanel.tsx +315 -0
- package/src/ui/components/Card.tsx +378 -0
- package/src/ui/components/CardDragSurface.tsx +1076 -0
- package/src/ui/components/ChromeSuppressionContext.tsx +70 -0
- package/src/ui/components/CostDisplay.tsx +145 -0
- package/src/ui/components/DiceRoller.tsx +581 -0
- package/src/ui/components/Drawer.tsx +180 -0
- package/src/ui/components/ErrorBoundary.tsx +275 -0
- package/src/ui/components/GameEndDisplay.tsx +398 -0
- package/src/ui/components/GameSkeleton.tsx +260 -0
- package/src/ui/components/Hand.tsx +468 -0
- package/src/ui/components/HandDock.tsx +299 -0
- package/src/ui/components/HandView.tsx +441 -0
- package/src/ui/components/MobileHandTray.tsx +381 -0
- package/src/ui/components/MoreActions.tsx +143 -0
- package/src/ui/components/PhaseIndicator.tsx +341 -0
- package/src/ui/components/PlayArea.tsx +146 -0
- package/src/ui/components/PrimaryActionButton.tsx +336 -0
- package/src/ui/components/PrimaryButton.tsx +45 -0
- package/src/ui/components/ResourceCounter.tsx +270 -0
- package/src/ui/components/StagingZone.tsx +134 -0
- package/src/ui/components/ThemedButton.tsx +113 -0
- package/src/ui/components/Toast.tsx +264 -0
- package/src/ui/components/board/HexGrid.tsx +1294 -0
- package/src/ui/components/board/NetworkGraph.tsx +476 -0
- package/src/ui/components/board/SlotSystem.tsx +388 -0
- package/src/ui/components/board/SquareGrid.tsx +1165 -0
- package/src/ui/components/board/TrackBoard.tsx +496 -0
- package/src/ui/components/board/ZoneMap.tsx +448 -0
- package/src/ui/components/board/hex-board-view.ts +123 -0
- package/src/ui/components/board/index.ts +142 -0
- package/src/ui/components/board/interaction-accessibility.ts +21 -0
- package/src/ui/components/board/target-layer.ts +66 -0
- package/src/ui/components/card-render-content.type-test.ts +27 -0
- package/src/ui/components/hand-layout-math.ts +163 -0
- package/src/ui/components/hand-pointer-engine.ts +413 -0
- package/src/ui/components/index.ts +245 -0
- package/src/ui/components.ts +1 -0
- package/src/ui/defaults/components.tsx +106 -0
- package/src/ui/defaults/index.ts +8 -0
- package/src/ui/defaults.ts +1 -0
- package/src/ui/errors/ValidationError.ts +29 -0
- package/src/ui/helpers/cards.ts +19 -0
- package/src/ui/helpers/track-board.ts +211 -0
- package/src/ui/hooks/useBoardTopology.ts +316 -0
- package/src/ui/hooks/useCards.ts +10 -0
- package/src/ui/hooks/useHandCardPointer.ts +381 -0
- package/src/ui/hooks/useHandLayout.ts +378 -0
- package/src/ui/hooks/useHandPresentation.ts +121 -0
- package/src/ui/hooks/useHexBoard.ts +74 -0
- package/src/ui/hooks/useHexGrid.ts +185 -0
- package/src/ui/hooks/useIsMobile.ts +35 -0
- package/src/ui/hooks/usePanZoom.ts +278 -0
- package/src/ui/hooks/useSquareBoard.ts +124 -0
- package/src/ui/hooks/useSquareGrid.ts +328 -0
- package/src/ui/index.ts +98 -0
- package/src/ui/internal/ui/alert.tsx +51 -0
- package/src/ui/internal/ui/button.tsx +58 -0
- package/src/ui/internal/ui/dialog.tsx +134 -0
- package/src/ui/internal/ui/input.tsx +21 -0
- package/src/ui/internal/ui/label.tsx +21 -0
- package/src/ui/internal/ui/select.tsx +129 -0
- package/src/ui/internal/ui/tooltip.tsx +54 -0
- package/src/ui/internal/ui/utils.ts +5 -0
- package/src/ui/plugin-styles.css +250 -0
- package/src/ui/primitives/dialog-lifecycle.ts +58 -0
- package/src/ui/primitives/dice.tsx +79 -0
- package/src/ui/primitives/primitive-props.tsx +101 -0
- package/src/ui/theme/ThemeProvider.tsx +252 -0
- package/src/ui/theme/board.ts +61 -0
- package/src/ui/theme/css-vars.ts +105 -0
- package/src/ui/theme/derive.ts +240 -0
- package/src/ui/theme/index.ts +61 -0
- package/src/ui/theme/presets/arcade.ts +261 -0
- package/src/ui/theme/presets/studio.ts +261 -0
- package/src/ui/theme/presets/tabletop.ts +266 -0
- package/src/ui/theme/tokens.ts +392 -0
- package/src/ui/types/hex-color.ts +20 -0
- package/src/ui/types/player-state.ts +463 -0
- package/src/ui/types/tiled-board.ts +785 -0
- package/src/ui/types/visual-state.ts +137 -0
- package/src/ui.ts +1 -0
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SVG-based area control visualization for territory games (Risk, Small World, Diplomacy).
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { useMemo, type ReactNode } from "react";
|
|
6
|
+
import { clsx } from "clsx";
|
|
7
|
+
import { usePanZoom } from "../../hooks/usePanZoom.js";
|
|
8
|
+
import { handleKeyboardActivation } from "./interaction-accessibility.js";
|
|
9
|
+
|
|
10
|
+
export interface ZoneShape {
|
|
11
|
+
type: "polygon" | "path" | "circle";
|
|
12
|
+
points?: Array<{ x: number; y: number }>;
|
|
13
|
+
/** SVG path data */
|
|
14
|
+
path?: string;
|
|
15
|
+
center?: { x: number; y: number };
|
|
16
|
+
radius?: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface ZoneDefinition {
|
|
20
|
+
id: string;
|
|
21
|
+
name: string;
|
|
22
|
+
adjacentTo: string[];
|
|
23
|
+
shape?: ZoneShape;
|
|
24
|
+
value?: number;
|
|
25
|
+
type?: string;
|
|
26
|
+
data?: Record<string, unknown>;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface ZonePiece {
|
|
30
|
+
id: string;
|
|
31
|
+
zoneId: string;
|
|
32
|
+
type: string;
|
|
33
|
+
owner?: string;
|
|
34
|
+
/** Count for stackable pieces (armies) */
|
|
35
|
+
count?: number;
|
|
36
|
+
data?: Record<string, unknown>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface ZoneMapProps {
|
|
40
|
+
zones: ZoneDefinition[];
|
|
41
|
+
pieces: ZonePiece[];
|
|
42
|
+
renderZone: (zone: ZoneDefinition, pieces: ZonePiece[]) => ReactNode;
|
|
43
|
+
backgroundImage?: string;
|
|
44
|
+
width?: number | string;
|
|
45
|
+
height?: number | string;
|
|
46
|
+
enablePanZoom?: boolean;
|
|
47
|
+
initialZoom?: number;
|
|
48
|
+
minZoom?: number;
|
|
49
|
+
maxZoom?: number;
|
|
50
|
+
className?: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// ============================================================================
|
|
54
|
+
// Helper Components
|
|
55
|
+
// ============================================================================
|
|
56
|
+
|
|
57
|
+
export type ZoneHighlightType =
|
|
58
|
+
| "valid"
|
|
59
|
+
| "selected"
|
|
60
|
+
| "attack"
|
|
61
|
+
| "defend"
|
|
62
|
+
| "neutral";
|
|
63
|
+
|
|
64
|
+
// Highlight colors by type
|
|
65
|
+
const HIGHLIGHT_COLORS: Record<ZoneHighlightType, string> = {
|
|
66
|
+
valid: "rgba(34, 197, 94, 0.4)", // Green
|
|
67
|
+
selected: "rgba(59, 130, 246, 0.4)", // Blue
|
|
68
|
+
attack: "rgba(239, 68, 68, 0.4)", // Red
|
|
69
|
+
defend: "rgba(234, 179, 8, 0.4)", // Yellow
|
|
70
|
+
neutral: "rgba(148, 163, 184, 0.4)", // Slate
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const HIGHLIGHT_STROKES: Record<ZoneHighlightType, string> = {
|
|
74
|
+
valid: "#22c55e",
|
|
75
|
+
selected: "#3b82f6",
|
|
76
|
+
attack: "#ef4444",
|
|
77
|
+
defend: "#eab308",
|
|
78
|
+
neutral: "#94a3b8",
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const EMPTY_PLAYER_COLORS: Record<string, string> = {};
|
|
82
|
+
|
|
83
|
+
export interface DefaultZoneProps {
|
|
84
|
+
zone: ZoneDefinition;
|
|
85
|
+
fill?: string;
|
|
86
|
+
stroke?: string;
|
|
87
|
+
strokeWidth?: number;
|
|
88
|
+
isHighlighted?: boolean;
|
|
89
|
+
highlightType?: ZoneHighlightType;
|
|
90
|
+
isSelected?: boolean;
|
|
91
|
+
showLabel?: boolean;
|
|
92
|
+
showValue?: boolean;
|
|
93
|
+
onClick?: () => void;
|
|
94
|
+
onHover?: (hovering: boolean) => void;
|
|
95
|
+
className?: string;
|
|
96
|
+
children?: ReactNode;
|
|
97
|
+
}
|
|
98
|
+
export function DefaultZone({
|
|
99
|
+
zone,
|
|
100
|
+
fill = "rgba(100, 116, 139, 0.2)",
|
|
101
|
+
stroke = "#475569",
|
|
102
|
+
strokeWidth = 1,
|
|
103
|
+
isHighlighted = false,
|
|
104
|
+
highlightType,
|
|
105
|
+
isSelected = false,
|
|
106
|
+
showLabel = true,
|
|
107
|
+
showValue = false,
|
|
108
|
+
onClick,
|
|
109
|
+
onHover,
|
|
110
|
+
className,
|
|
111
|
+
children,
|
|
112
|
+
}: DefaultZoneProps) {
|
|
113
|
+
// Calculate colors based on state
|
|
114
|
+
let computedFill = fill;
|
|
115
|
+
let computedStroke = stroke;
|
|
116
|
+
let computedStrokeWidth = strokeWidth;
|
|
117
|
+
|
|
118
|
+
if (isHighlighted && highlightType && HIGHLIGHT_COLORS[highlightType]) {
|
|
119
|
+
computedFill = HIGHLIGHT_COLORS[highlightType];
|
|
120
|
+
computedStroke = HIGHLIGHT_STROKES[highlightType];
|
|
121
|
+
computedStrokeWidth = 3;
|
|
122
|
+
} else if (isSelected) {
|
|
123
|
+
computedFill = HIGHLIGHT_COLORS.selected;
|
|
124
|
+
computedStroke = HIGHLIGHT_STROKES.selected;
|
|
125
|
+
computedStrokeWidth = 3;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Render zone shape
|
|
129
|
+
const renderShape = () => {
|
|
130
|
+
if (!zone.shape) return null;
|
|
131
|
+
|
|
132
|
+
switch (zone.shape.type) {
|
|
133
|
+
case "polygon":
|
|
134
|
+
if (!zone.shape.points) return null;
|
|
135
|
+
return (
|
|
136
|
+
<polygon
|
|
137
|
+
points={zone.shape.points.map((p) => `${p.x},${p.y}`).join(" ")}
|
|
138
|
+
fill={computedFill}
|
|
139
|
+
stroke={computedStroke}
|
|
140
|
+
strokeWidth={computedStrokeWidth}
|
|
141
|
+
/>
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
case "path":
|
|
145
|
+
if (!zone.shape.path) return null;
|
|
146
|
+
return (
|
|
147
|
+
<path
|
|
148
|
+
d={zone.shape.path}
|
|
149
|
+
fill={computedFill}
|
|
150
|
+
stroke={computedStroke}
|
|
151
|
+
strokeWidth={computedStrokeWidth}
|
|
152
|
+
/>
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
case "circle":
|
|
156
|
+
if (!zone.shape.center) return null;
|
|
157
|
+
return (
|
|
158
|
+
<circle
|
|
159
|
+
cx={zone.shape.center.x}
|
|
160
|
+
cy={zone.shape.center.y}
|
|
161
|
+
r={zone.shape.radius || 30}
|
|
162
|
+
fill={computedFill}
|
|
163
|
+
stroke={computedStroke}
|
|
164
|
+
strokeWidth={computedStrokeWidth}
|
|
165
|
+
/>
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
default:
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
return (
|
|
174
|
+
<g
|
|
175
|
+
onClick={onClick}
|
|
176
|
+
onMouseEnter={() => onHover?.(true)}
|
|
177
|
+
onMouseLeave={() => onHover?.(false)}
|
|
178
|
+
onKeyDown={(event) => handleKeyboardActivation(event, onClick)}
|
|
179
|
+
className={clsx(onClick && "cursor-pointer", className)}
|
|
180
|
+
role={onClick ? "button" : "listitem"}
|
|
181
|
+
aria-label={zone.name}
|
|
182
|
+
tabIndex={onClick ? 0 : undefined}
|
|
183
|
+
>
|
|
184
|
+
{/* Zone shape */}
|
|
185
|
+
{renderShape()}
|
|
186
|
+
|
|
187
|
+
{/* Zone label */}
|
|
188
|
+
{showLabel && zone.shape?.center && (
|
|
189
|
+
<text
|
|
190
|
+
x={zone.shape.center.x}
|
|
191
|
+
y={zone.shape.center.y - (showValue ? 8 : 0)}
|
|
192
|
+
textAnchor="middle"
|
|
193
|
+
dominantBaseline="middle"
|
|
194
|
+
fill="white"
|
|
195
|
+
fontSize={12}
|
|
196
|
+
fontWeight="bold"
|
|
197
|
+
style={{ textShadow: "1px 1px 2px rgba(0,0,0,0.8)" }}
|
|
198
|
+
>
|
|
199
|
+
{zone.name}
|
|
200
|
+
</text>
|
|
201
|
+
)}
|
|
202
|
+
|
|
203
|
+
{/* Zone value */}
|
|
204
|
+
{showValue && zone.value !== undefined && zone.shape?.center && (
|
|
205
|
+
<text
|
|
206
|
+
x={zone.shape.center.x}
|
|
207
|
+
y={zone.shape.center.y + 8}
|
|
208
|
+
textAnchor="middle"
|
|
209
|
+
dominantBaseline="middle"
|
|
210
|
+
fill="#fbbf24"
|
|
211
|
+
fontSize={10}
|
|
212
|
+
fontWeight="bold"
|
|
213
|
+
style={{ textShadow: "1px 1px 2px rgba(0,0,0,0.8)" }}
|
|
214
|
+
>
|
|
215
|
+
+{zone.value}
|
|
216
|
+
</text>
|
|
217
|
+
)}
|
|
218
|
+
|
|
219
|
+
{/* Children (pieces) */}
|
|
220
|
+
{children}
|
|
221
|
+
</g>
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export interface DefaultZonePiecesProps {
|
|
226
|
+
pieces: ZonePiece[];
|
|
227
|
+
zone: ZoneDefinition;
|
|
228
|
+
playerColors?: Record<string, string>;
|
|
229
|
+
radius?: number;
|
|
230
|
+
spacing?: number;
|
|
231
|
+
yOffset?: number;
|
|
232
|
+
className?: string;
|
|
233
|
+
}
|
|
234
|
+
export function DefaultZonePieces({
|
|
235
|
+
pieces,
|
|
236
|
+
zone,
|
|
237
|
+
playerColors = EMPTY_PLAYER_COLORS,
|
|
238
|
+
radius = 14,
|
|
239
|
+
spacing = 25,
|
|
240
|
+
yOffset = 20,
|
|
241
|
+
className,
|
|
242
|
+
}: DefaultZonePiecesProps) {
|
|
243
|
+
if (pieces.length === 0 || !zone.shape?.center) return null;
|
|
244
|
+
|
|
245
|
+
const centerX = zone.shape.center.x;
|
|
246
|
+
const centerY = zone.shape.center.y;
|
|
247
|
+
|
|
248
|
+
// Group pieces by owner
|
|
249
|
+
const piecesByOwner: Record<string, ZonePiece[]> = {};
|
|
250
|
+
pieces.forEach((p) => {
|
|
251
|
+
const owner = p.owner || "neutral";
|
|
252
|
+
const existing = piecesByOwner[owner];
|
|
253
|
+
if (existing) {
|
|
254
|
+
existing.push(p);
|
|
255
|
+
} else {
|
|
256
|
+
piecesByOwner[owner] = [p];
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
const owners = Object.keys(piecesByOwner);
|
|
261
|
+
const startOffset = -((owners.length - 1) * spacing) / 2;
|
|
262
|
+
|
|
263
|
+
return (
|
|
264
|
+
<g className={clsx("zone-pieces", className)}>
|
|
265
|
+
{owners.map((owner, i) => {
|
|
266
|
+
const ownerPieces = piecesByOwner[owner] || [];
|
|
267
|
+
const totalCount = ownerPieces.reduce(
|
|
268
|
+
(sum, p) => sum + (p.count || 1),
|
|
269
|
+
0,
|
|
270
|
+
);
|
|
271
|
+
const offsetX = startOffset + i * spacing;
|
|
272
|
+
|
|
273
|
+
return (
|
|
274
|
+
<g
|
|
275
|
+
key={owner}
|
|
276
|
+
transform={`translate(${centerX + offsetX}, ${centerY + yOffset})`}
|
|
277
|
+
>
|
|
278
|
+
{/* Piece circle */}
|
|
279
|
+
<circle
|
|
280
|
+
r={radius}
|
|
281
|
+
fill={playerColors[owner] || "#64748b"}
|
|
282
|
+
stroke="white"
|
|
283
|
+
strokeWidth={2}
|
|
284
|
+
/>
|
|
285
|
+
{/* Count */}
|
|
286
|
+
<text
|
|
287
|
+
textAnchor="middle"
|
|
288
|
+
dominantBaseline="central"
|
|
289
|
+
fill="white"
|
|
290
|
+
fontSize={10}
|
|
291
|
+
fontWeight="bold"
|
|
292
|
+
>
|
|
293
|
+
{totalCount}
|
|
294
|
+
</text>
|
|
295
|
+
</g>
|
|
296
|
+
);
|
|
297
|
+
})}
|
|
298
|
+
</g>
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
export interface DefaultZonePieceProps {
|
|
303
|
+
piece: ZonePiece;
|
|
304
|
+
x?: number;
|
|
305
|
+
y?: number;
|
|
306
|
+
radius?: number;
|
|
307
|
+
color?: string;
|
|
308
|
+
onClick?: () => void;
|
|
309
|
+
className?: string;
|
|
310
|
+
}
|
|
311
|
+
export function DefaultZonePiece({
|
|
312
|
+
piece,
|
|
313
|
+
x = 0,
|
|
314
|
+
y = 0,
|
|
315
|
+
radius = 14,
|
|
316
|
+
color = "#64748b",
|
|
317
|
+
onClick,
|
|
318
|
+
className,
|
|
319
|
+
}: DefaultZonePieceProps) {
|
|
320
|
+
const count = piece.count || 1;
|
|
321
|
+
|
|
322
|
+
return (
|
|
323
|
+
<g
|
|
324
|
+
transform={`translate(${x}, ${y})`}
|
|
325
|
+
onClick={(e) => {
|
|
326
|
+
e.stopPropagation();
|
|
327
|
+
onClick?.();
|
|
328
|
+
}}
|
|
329
|
+
onKeyDown={(event) =>
|
|
330
|
+
handleKeyboardActivation(event, onClick, { stopPropagation: true })
|
|
331
|
+
}
|
|
332
|
+
className={clsx(onClick && "cursor-pointer", className)}
|
|
333
|
+
role={onClick ? "button" : undefined}
|
|
334
|
+
tabIndex={onClick ? 0 : undefined}
|
|
335
|
+
aria-label={`Piece ${piece.id}`}
|
|
336
|
+
>
|
|
337
|
+
<circle r={radius} fill={color} stroke="white" strokeWidth={2} />
|
|
338
|
+
<text
|
|
339
|
+
textAnchor="middle"
|
|
340
|
+
dominantBaseline="central"
|
|
341
|
+
fill="white"
|
|
342
|
+
fontSize={10}
|
|
343
|
+
fontWeight="bold"
|
|
344
|
+
>
|
|
345
|
+
{count}
|
|
346
|
+
</text>
|
|
347
|
+
</g>
|
|
348
|
+
);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// ============================================================================
|
|
352
|
+
// Main Component
|
|
353
|
+
// ============================================================================
|
|
354
|
+
|
|
355
|
+
export function ZoneMap({
|
|
356
|
+
zones,
|
|
357
|
+
pieces,
|
|
358
|
+
renderZone,
|
|
359
|
+
backgroundImage,
|
|
360
|
+
width = 800,
|
|
361
|
+
height = 600,
|
|
362
|
+
enablePanZoom = false,
|
|
363
|
+
initialZoom = 1,
|
|
364
|
+
minZoom = 0.5,
|
|
365
|
+
maxZoom = 3,
|
|
366
|
+
className,
|
|
367
|
+
}: ZoneMapProps) {
|
|
368
|
+
// Use the unified pan/zoom hook
|
|
369
|
+
const { transform, bind, isDragging } = usePanZoom({
|
|
370
|
+
enabled: enablePanZoom,
|
|
371
|
+
initialZoom,
|
|
372
|
+
minZoom,
|
|
373
|
+
maxZoom,
|
|
374
|
+
mode: "viewbox",
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
// Group pieces by zone
|
|
378
|
+
const piecesByZone = useMemo(() => {
|
|
379
|
+
const map: Record<string, ZonePiece[]> = {};
|
|
380
|
+
pieces.forEach((p) => {
|
|
381
|
+
const existing = map[p.zoneId];
|
|
382
|
+
if (existing) {
|
|
383
|
+
existing.push(p);
|
|
384
|
+
} else {
|
|
385
|
+
map[p.zoneId] = [p];
|
|
386
|
+
}
|
|
387
|
+
});
|
|
388
|
+
return map;
|
|
389
|
+
}, [pieces]);
|
|
390
|
+
|
|
391
|
+
// Calculate viewBox dimensions
|
|
392
|
+
const baseWidth = typeof width === "number" ? width : 800;
|
|
393
|
+
const baseHeight = typeof height === "number" ? height : 600;
|
|
394
|
+
const viewBoxWidth = baseWidth / transform.zoom;
|
|
395
|
+
const viewBoxHeight = baseHeight / transform.zoom;
|
|
396
|
+
const viewBoxX = (baseWidth - viewBoxWidth) / 2 - transform.pan.x;
|
|
397
|
+
const viewBoxY = (baseHeight - viewBoxHeight) / 2 - transform.pan.y;
|
|
398
|
+
|
|
399
|
+
return (
|
|
400
|
+
<svg
|
|
401
|
+
width={width}
|
|
402
|
+
height={height}
|
|
403
|
+
viewBox={`${viewBoxX} ${viewBoxY} ${viewBoxWidth} ${viewBoxHeight}`}
|
|
404
|
+
className={clsx(
|
|
405
|
+
"overflow-visible",
|
|
406
|
+
enablePanZoom && "touch-none",
|
|
407
|
+
isDragging && "cursor-grabbing",
|
|
408
|
+
enablePanZoom && !isDragging && "cursor-grab",
|
|
409
|
+
className,
|
|
410
|
+
)}
|
|
411
|
+
{...bind()}
|
|
412
|
+
role="img"
|
|
413
|
+
aria-label="Zone map"
|
|
414
|
+
>
|
|
415
|
+
{/* Background image */}
|
|
416
|
+
{backgroundImage && (
|
|
417
|
+
<image href={backgroundImage} width={width} height={height} />
|
|
418
|
+
)}
|
|
419
|
+
|
|
420
|
+
{/* Zones */}
|
|
421
|
+
<g className="zones" role="list" aria-label="Map zones">
|
|
422
|
+
{zones.map((zone) => {
|
|
423
|
+
const zonePieces = piecesByZone[zone.id] || [];
|
|
424
|
+
return <g key={zone.id}>{renderZone(zone, zonePieces)}</g>;
|
|
425
|
+
})}
|
|
426
|
+
</g>
|
|
427
|
+
|
|
428
|
+
{/* Zoom indicator */}
|
|
429
|
+
{enablePanZoom && transform.zoom !== 1 && (
|
|
430
|
+
<g
|
|
431
|
+
transform={`translate(${viewBoxX + 10}, ${viewBoxY + viewBoxHeight - 30})`}
|
|
432
|
+
>
|
|
433
|
+
<rect
|
|
434
|
+
x={0}
|
|
435
|
+
y={0}
|
|
436
|
+
width={60}
|
|
437
|
+
height={20}
|
|
438
|
+
rx={4}
|
|
439
|
+
fill="rgba(0,0,0,0.6)"
|
|
440
|
+
/>
|
|
441
|
+
<text x={30} y={14} textAnchor="middle" fill="white" fontSize={12}>
|
|
442
|
+
{Math.round(transform.zoom * 100)}%
|
|
443
|
+
</text>
|
|
444
|
+
</g>
|
|
445
|
+
)}
|
|
446
|
+
</svg>
|
|
447
|
+
);
|
|
448
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed adapter for joining static hex-board topology with a dynamic
|
|
3
|
+
* per-space view overlay. Use this to feed `HexGrid` a single board
|
|
4
|
+
* value whose tiles carry both static geometry (`q`, `r`, `id`) and
|
|
5
|
+
* the projected view fields the UI cares about (e.g. `terrain`,
|
|
6
|
+
* `numberToken`).
|
|
7
|
+
*
|
|
8
|
+
* The runtime is intentionally strict: every static space must have
|
|
9
|
+
* exactly one overlay, duplicates throw, and overlays for ids that
|
|
10
|
+
* are not on the board throw. There is no relaxed fallback in v1 —
|
|
11
|
+
* silent merge bugs are far more expensive than a loud throw at
|
|
12
|
+
* dev/CI time.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import type {
|
|
16
|
+
AnyHexBoardInput,
|
|
17
|
+
BoardIdOf,
|
|
18
|
+
BoardSpaceIdOf,
|
|
19
|
+
NormalizedHexTileOf,
|
|
20
|
+
} from "../../types/tiled-board.js";
|
|
21
|
+
import { normalizeHexBoardInput } from "../../types/tiled-board.js";
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Per-tile result produced by {@link createHexBoardView}. Each tile
|
|
25
|
+
* carries the static topology (`id`, `q`, `r`) plus the matched
|
|
26
|
+
* overlay row in `view`.
|
|
27
|
+
*
|
|
28
|
+
* Static board fields/properties are preserved from `TBoard`.
|
|
29
|
+
* Consumers should use `tile.properties` for authored static space
|
|
30
|
+
* fields and `tile.view` for dynamic per-space projection data.
|
|
31
|
+
*/
|
|
32
|
+
export type HexBoardViewTile<
|
|
33
|
+
TBoard extends AnyHexBoardInput,
|
|
34
|
+
TSpaceView extends { id: BoardSpaceIdOf<TBoard> },
|
|
35
|
+
> = Omit<NormalizedHexTileOf<TBoard>, "view"> & { view: TSpaceView };
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Result of {@link createHexBoardView}. Shaped like an authored hex
|
|
39
|
+
* board so it can be passed straight to `<HexGrid board={...} />`
|
|
40
|
+
* without any further adapter work.
|
|
41
|
+
*
|
|
42
|
+
* The type-level board id and space id are preserved from `TBoard`,
|
|
43
|
+
* so `tile.id` stays narrow inside `renderTile`. `tile.view` is the
|
|
44
|
+
* authored overlay row, fully typed.
|
|
45
|
+
*/
|
|
46
|
+
export interface HexBoardView<
|
|
47
|
+
TBoard extends AnyHexBoardInput,
|
|
48
|
+
TSpaceView extends { id: BoardSpaceIdOf<TBoard> },
|
|
49
|
+
> {
|
|
50
|
+
id: BoardIdOf<TBoard>;
|
|
51
|
+
layout?: "hex";
|
|
52
|
+
orientation?: "pointy-top" | "flat-top";
|
|
53
|
+
tiles: ReadonlyArray<HexBoardViewTile<TBoard, TSpaceView>>;
|
|
54
|
+
edges: TBoard["edges"];
|
|
55
|
+
vertices: TBoard["vertices"];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
interface CreateHexBoardViewOptions<TSpaceView> {
|
|
59
|
+
spaces: readonly TSpaceView[];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Join a static hex board topology with a dynamic per-space overlay.
|
|
64
|
+
*
|
|
65
|
+
* The result is suitable for direct use as the `board` prop on
|
|
66
|
+
* `<HexGrid>`. Every overlay is enforced 1-to-1 against the board's
|
|
67
|
+
* spaces:
|
|
68
|
+
*
|
|
69
|
+
* - missing overlay for a board space → throws
|
|
70
|
+
* - duplicate overlay (same `id` twice) → throws
|
|
71
|
+
* - overlay `id` not present on the board → throws
|
|
72
|
+
*
|
|
73
|
+
* Each tile in the result carries a `view` field with the matched
|
|
74
|
+
* overlay row.
|
|
75
|
+
*/
|
|
76
|
+
export function createHexBoardView<
|
|
77
|
+
const TBoard extends AnyHexBoardInput,
|
|
78
|
+
const TSpaceView extends { id: BoardSpaceIdOf<TBoard> },
|
|
79
|
+
>(
|
|
80
|
+
board: TBoard,
|
|
81
|
+
options: CreateHexBoardViewOptions<TSpaceView>,
|
|
82
|
+
): HexBoardView<TBoard, TSpaceView> {
|
|
83
|
+
const overlayById = new Map<string, TSpaceView>();
|
|
84
|
+
for (const overlay of options.spaces) {
|
|
85
|
+
if (overlayById.has(overlay.id)) {
|
|
86
|
+
throw new Error(
|
|
87
|
+
`createHexBoardView: duplicate overlay for space '${overlay.id}'.`,
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
overlayById.set(overlay.id, overlay);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const consumed = new Set<string>();
|
|
94
|
+
const normalizedBoard = normalizeHexBoardInput(board);
|
|
95
|
+
const tiles: Array<HexBoardViewTile<TBoard, TSpaceView>> =
|
|
96
|
+
normalizedBoard.tiles.map((tile) => {
|
|
97
|
+
const overlay = overlayById.get(tile.id);
|
|
98
|
+
if (!overlay) {
|
|
99
|
+
throw new Error(
|
|
100
|
+
`createHexBoardView: missing overlay for space '${tile.id}'.`,
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
consumed.add(tile.id);
|
|
104
|
+
return { ...tile, view: overlay };
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
for (const id of overlayById.keys()) {
|
|
108
|
+
if (!consumed.has(id)) {
|
|
109
|
+
throw new Error(
|
|
110
|
+
`createHexBoardView: overlay '${id}' is not on the board.`,
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
id: normalizedBoard.id,
|
|
117
|
+
layout: "hex",
|
|
118
|
+
orientation: board.orientation,
|
|
119
|
+
tiles,
|
|
120
|
+
edges: board.edges,
|
|
121
|
+
vertices: board.vertices,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Board Primitives
|
|
3
|
+
*
|
|
4
|
+
* Reusable board visualization components for different game types.
|
|
5
|
+
*
|
|
6
|
+
* Components:
|
|
7
|
+
* - NetworkGraph: For route-building games (Ticket to Ride, Pandemic)
|
|
8
|
+
* - HexGrid: For hex-based games (Catan, wargames)
|
|
9
|
+
* - SquareGrid: For grid-based games (Chess, Go, Checkers)
|
|
10
|
+
* - ZoneMap: For area control games (Risk, Small World)
|
|
11
|
+
* - TrackBoard: For track/racing games (Monopoly, Game of Life)
|
|
12
|
+
* - SlotSystem: For worker placement games (Agricola, Viticulture)
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
// NetworkGraph - Route building games
|
|
16
|
+
export {
|
|
17
|
+
NetworkGraph,
|
|
18
|
+
// Pre-built helper components for easy customization
|
|
19
|
+
DefaultNetworkNode,
|
|
20
|
+
DefaultNetworkEdge,
|
|
21
|
+
DefaultNetworkPiece,
|
|
22
|
+
type NetworkGraphProps,
|
|
23
|
+
type NetworkNode,
|
|
24
|
+
type NetworkEdge,
|
|
25
|
+
type NetworkPiece,
|
|
26
|
+
type DefaultNetworkNodeProps,
|
|
27
|
+
type DefaultNetworkEdgeProps,
|
|
28
|
+
type DefaultNetworkPieceProps,
|
|
29
|
+
} from "./NetworkGraph.js";
|
|
30
|
+
|
|
31
|
+
// HexGrid - Hex-based games (Catan, wargames)
|
|
32
|
+
export {
|
|
33
|
+
HexGrid,
|
|
34
|
+
hexUtils,
|
|
35
|
+
// Pre-built helper components for easy customization
|
|
36
|
+
DefaultHexTile,
|
|
37
|
+
DefaultHexEdge,
|
|
38
|
+
DefaultHexVertex,
|
|
39
|
+
type HexGridBoardProps,
|
|
40
|
+
type HexGridProps,
|
|
41
|
+
type HexOrientation,
|
|
42
|
+
type HexTileGeometry,
|
|
43
|
+
type InteractiveHexEdge,
|
|
44
|
+
type InteractiveHexSpace,
|
|
45
|
+
type InteractiveHexVertex,
|
|
46
|
+
type EdgePosition,
|
|
47
|
+
type DefaultHexTileProps,
|
|
48
|
+
type DefaultHexEdgeProps,
|
|
49
|
+
type DefaultHexVertexProps,
|
|
50
|
+
} from "./HexGrid.js";
|
|
51
|
+
|
|
52
|
+
export type {
|
|
53
|
+
InteractiveTargetLayer,
|
|
54
|
+
InteractiveTargetRenderState,
|
|
55
|
+
} from "./target-layer.js";
|
|
56
|
+
|
|
57
|
+
export {
|
|
58
|
+
createHexBoardView,
|
|
59
|
+
type HexBoardView,
|
|
60
|
+
type HexBoardViewTile,
|
|
61
|
+
} from "./hex-board-view.js";
|
|
62
|
+
|
|
63
|
+
// SquareGrid - Grid-based games (Chess, Go, Checkers)
|
|
64
|
+
export {
|
|
65
|
+
SquareGrid,
|
|
66
|
+
// Pre-built helper components for easy customization
|
|
67
|
+
DefaultGridCell,
|
|
68
|
+
DefaultGridPiece,
|
|
69
|
+
DefaultChessPiece,
|
|
70
|
+
// Utility functions
|
|
71
|
+
toAlgebraic,
|
|
72
|
+
toNumeric,
|
|
73
|
+
type SquareGridBoardProps,
|
|
74
|
+
type SquareGridProps,
|
|
75
|
+
type InteractiveSquareEdge,
|
|
76
|
+
type InteractiveSquareSpace,
|
|
77
|
+
type InteractiveSquareVertex,
|
|
78
|
+
type SquareEdgePosition,
|
|
79
|
+
type SquareVertexPosition,
|
|
80
|
+
type DefaultGridCellProps,
|
|
81
|
+
type DefaultGridPieceProps,
|
|
82
|
+
type DefaultChessPieceProps,
|
|
83
|
+
} from "./SquareGrid.js";
|
|
84
|
+
|
|
85
|
+
// ZoneMap - Area control games
|
|
86
|
+
export {
|
|
87
|
+
ZoneMap,
|
|
88
|
+
// Pre-built helper components for easy customization
|
|
89
|
+
DefaultZone,
|
|
90
|
+
DefaultZonePieces,
|
|
91
|
+
DefaultZonePiece,
|
|
92
|
+
type ZoneMapProps,
|
|
93
|
+
type ZoneDefinition,
|
|
94
|
+
type ZonePiece,
|
|
95
|
+
type ZoneShape,
|
|
96
|
+
type ZoneHighlightType,
|
|
97
|
+
type DefaultZoneProps,
|
|
98
|
+
type DefaultZonePiecesProps,
|
|
99
|
+
type DefaultZonePieceProps,
|
|
100
|
+
} from "./ZoneMap.js";
|
|
101
|
+
|
|
102
|
+
// TrackBoard - Track/racing games
|
|
103
|
+
export {
|
|
104
|
+
TrackBoard,
|
|
105
|
+
// Pre-built helper components for easy customization
|
|
106
|
+
DefaultTrackSpace,
|
|
107
|
+
DefaultTrackPiece,
|
|
108
|
+
DefaultTrackConnection,
|
|
109
|
+
DefaultTrackJump,
|
|
110
|
+
type TrackBoardProps,
|
|
111
|
+
type TrackSpace,
|
|
112
|
+
type TrackPiece,
|
|
113
|
+
type DefaultTrackSpaceProps,
|
|
114
|
+
type DefaultTrackPieceProps,
|
|
115
|
+
type DefaultTrackConnectionProps,
|
|
116
|
+
type DefaultTrackJumpProps,
|
|
117
|
+
} from "./TrackBoard.js";
|
|
118
|
+
|
|
119
|
+
// SlotSystem - Worker placement games
|
|
120
|
+
export {
|
|
121
|
+
SlotSystem,
|
|
122
|
+
// Pre-built helper components for easy customization
|
|
123
|
+
DefaultSlotItem,
|
|
124
|
+
DefaultSlotOccupant,
|
|
125
|
+
DefaultEmptySlot,
|
|
126
|
+
type SlotSystemProps,
|
|
127
|
+
type SlotDefinition,
|
|
128
|
+
type SlotOccupant,
|
|
129
|
+
type DefaultSlotItemProps,
|
|
130
|
+
type DefaultSlotOccupantProps,
|
|
131
|
+
type DefaultEmptySlotProps,
|
|
132
|
+
} from "./SlotSystem.js";
|
|
133
|
+
|
|
134
|
+
export type {
|
|
135
|
+
HexTileState,
|
|
136
|
+
HexEdgeState,
|
|
137
|
+
HexVertexState,
|
|
138
|
+
SquareCellState,
|
|
139
|
+
SquareEdgeState,
|
|
140
|
+
SquarePieceState,
|
|
141
|
+
SquareVertexState,
|
|
142
|
+
} from "../../types/player-state.js";
|