@almadar/ui 5.60.0 → 5.62.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.
Files changed (41) hide show
  1. package/dist/avl/index.cjs +3233 -1757
  2. package/dist/avl/index.js +2057 -581
  3. package/dist/components/core/molecules/GraphCanvas.d.ts +6 -0
  4. package/dist/components/core/templates/index.d.ts +4 -0
  5. package/dist/components/game/molecules/CardHand.d.ts +35 -0
  6. package/dist/components/game/molecules/DialogueBox.d.ts +8 -2
  7. package/dist/components/game/molecules/index.d.ts +1 -0
  8. package/dist/components/game/molecules/three/index.cjs +229 -120
  9. package/dist/components/game/molecules/three/index.js +229 -120
  10. package/dist/components/game/organisms/CardBattlerBoard.d.ts +46 -0
  11. package/dist/components/game/organisms/CityBuilderBoard.d.ts +63 -0
  12. package/dist/components/game/organisms/TopDownShooterBoard.d.ts +64 -0
  13. package/dist/components/game/organisms/TowerDefenseBoard.d.ts +10 -1
  14. package/dist/components/game/organisms/TraitSlot.d.ts +1 -3
  15. package/dist/components/game/organisms/VisualNovelBoard.d.ts +53 -0
  16. package/dist/components/game/organisms/index.d.ts +4 -0
  17. package/dist/components/game/templates/CardBattlerTemplate.d.ts +35 -0
  18. package/dist/components/game/templates/CityBuilderTemplate.d.ts +40 -0
  19. package/dist/components/game/templates/GameCanvas3DBattleTemplate.d.ts +5 -2
  20. package/dist/components/game/templates/GameCanvas3DCastleTemplate.d.ts +5 -2
  21. package/dist/components/game/templates/GameCanvas3DWorldMapTemplate.d.ts +5 -2
  22. package/dist/components/game/templates/TopDownShooterTemplate.d.ts +42 -0
  23. package/dist/components/game/templates/VisualNovelTemplate.d.ts +28 -0
  24. package/dist/components/game/templates/game3dAssetManifest.d.ts +50 -0
  25. package/dist/components/game/templates/index.d.ts +4 -0
  26. package/dist/components/index.cjs +3048 -1547
  27. package/dist/components/index.js +2019 -527
  28. package/dist/docs/index.cjs +105 -1
  29. package/dist/docs/index.js +105 -1
  30. package/dist/hooks/index.cjs +105 -1
  31. package/dist/hooks/index.js +105 -1
  32. package/dist/locales/index.cjs +315 -3
  33. package/dist/locales/index.js +315 -3
  34. package/dist/providers/index.cjs +3034 -1558
  35. package/dist/providers/index.js +2025 -549
  36. package/dist/runtime/index.cjs +3085 -1609
  37. package/dist/runtime/index.js +2024 -548
  38. package/locales/ar.json +105 -1
  39. package/locales/en.json +105 -1
  40. package/locales/sl.json +105 -1
  41. package/package.json +1 -1
@@ -377,12 +377,18 @@ function ModelLoader({
377
377
  });
378
378
  return cloned;
379
379
  }, [loadedModel, castShadow, receiveShadow]);
380
+ const normFactor = useMemo(() => {
381
+ if (!model) return 1;
382
+ const box = new THREE6.Box3().setFromObject(model);
383
+ const size = new THREE6.Vector3();
384
+ box.getSize(size);
385
+ const maxDim = Math.max(size.x, size.y, size.z);
386
+ return maxDim > 0 && Number.isFinite(maxDim) ? 1 / maxDim : 1;
387
+ }, [model]);
380
388
  const scaleArray = useMemo(() => {
381
- if (typeof scale === "number") {
382
- return [scale, scale, scale];
383
- }
384
- return scale;
385
- }, [scale]);
389
+ const base = typeof scale === "number" ? [scale, scale, scale] : scale;
390
+ return [base[0] * normFactor, base[1] * normFactor, base[2] * normFactor];
391
+ }, [scale, normFactor]);
386
392
  const rotationRad = useMemo(() => {
387
393
  return [
388
394
  rotation[0] * Math.PI / 180,
@@ -2566,6 +2572,29 @@ var GameCanvas3D = forwardRef(
2566
2572
  const controlsRef = useRef(null);
2567
2573
  const [hoveredTile, setHoveredTile] = useState(null);
2568
2574
  const [internalError, setInternalError] = useState(null);
2575
+ useEffect(() => {
2576
+ const el = containerRef.current;
2577
+ if (!el) return;
2578
+ let node = el;
2579
+ let depth = 0;
2580
+ while (node && depth < 8) {
2581
+ const cs = window.getComputedStyle(node);
2582
+ const rect = node.getBoundingClientRect();
2583
+ console.log("[almadar:ui:game:3d-height]", {
2584
+ depth,
2585
+ tag: node.tagName,
2586
+ id: node.id || void 0,
2587
+ className: node.className?.slice?.(0, 60) || void 0,
2588
+ rectHeight: rect.height,
2589
+ computedHeight: cs.height,
2590
+ computedMinHeight: cs.minHeight,
2591
+ computedFlex: cs.flex,
2592
+ computedOverflow: cs.overflow
2593
+ });
2594
+ node = node.parentElement;
2595
+ depth++;
2596
+ }
2597
+ }, []);
2569
2598
  const { sheetUrls: atlasSheetUrls, resolveUnitFrame } = useUnitSpriteAtlas(units);
2570
2599
  const preloadUrls = useMemo(() => [...preloadAssets, ...atlasSheetUrls], [preloadAssets, atlasSheetUrls]);
2571
2600
  const { isLoading: assetsLoading, progress, loaded, total } = useAssetLoader({
@@ -2694,24 +2723,28 @@ var GameCanvas3D = forwardRef(
2694
2723
  const cameraConfig = useMemo(() => {
2695
2724
  const size = Math.max(
2696
2725
  gridBounds.maxX - gridBounds.minX,
2697
- gridBounds.maxZ - gridBounds.minZ
2726
+ gridBounds.maxZ - gridBounds.minZ,
2727
+ 4
2728
+ // minimum framing distance so a tiny board isn't zoomed in
2698
2729
  );
2699
- const distance = size * 1.5;
2730
+ const cx = (gridBounds.minX + gridBounds.maxX) / 2;
2731
+ const cz = (gridBounds.minZ + gridBounds.maxZ) / 2;
2732
+ const d = size * 1.5;
2700
2733
  switch (cameraMode) {
2701
2734
  case "isometric":
2702
2735
  return {
2703
- position: [distance, distance * 0.8, distance],
2736
+ position: [cx + d, d * 0.8, cz + d],
2704
2737
  fov: 45
2705
2738
  };
2706
2739
  case "top-down":
2707
2740
  return {
2708
- position: [0, distance * 2, 0],
2741
+ position: [cx, d * 2, cz],
2709
2742
  fov: 45
2710
2743
  };
2711
2744
  case "perspective":
2712
2745
  default:
2713
2746
  return {
2714
- position: [distance, distance, distance],
2747
+ position: [cx + d, d, cz + d],
2715
2748
  fov: 45
2716
2749
  };
2717
2750
  }
@@ -2950,7 +2983,8 @@ var GameCanvas3D = forwardRef(
2950
2983
  "div",
2951
2984
  {
2952
2985
  ref: containerRef,
2953
- className: cn("game-canvas-3d relative w-full h-full min-h-[85vh] overflow-hidden", className),
2986
+ className: cn("game-canvas-3d relative w-full overflow-hidden", className),
2987
+ style: { height: "85vh" },
2954
2988
  "data-orientation": orientation,
2955
2989
  "data-camera-mode": cameraMode,
2956
2990
  "data-overlay": overlay,
@@ -2965,7 +2999,7 @@ var GameCanvas3D = forwardRef(
2965
2999
  near: 0.1,
2966
3000
  far: 1e3
2967
3001
  },
2968
- style: { background: backgroundColor },
3002
+ style: { background: backgroundColor, position: "absolute", inset: 0 },
2969
3003
  onClick: (e) => {
2970
3004
  if (e.target === e.currentTarget) {
2971
3005
  eventHandlers.handleCanvasClick(e);
@@ -4566,36 +4600,113 @@ var Box = React11.forwardRef(
4566
4600
  }
4567
4601
  );
4568
4602
  Box.displayName = "Box";
4569
- var CDN = "https://almadar-kflow-assets.web.app/shared/3d/dungeon/floor";
4570
- var FLOOR_WALL = `${CDN}/template-floor-detail-a.glb`;
4571
- var FLOOR_DIRT = `${CDN}/template-floor-detail.glb`;
4572
- var FLOOR_OPEN = `${CDN}/template-floor.glb`;
4573
- var DEFAULT_3D_BATTLE_TILES = [
4574
- { id: "t00", x: 0, y: 0, z: 0, type: "stone", passable: false, modelUrl: FLOOR_WALL },
4575
- { id: "t10", x: 1, y: 0, z: 0, type: "stone", passable: false, modelUrl: FLOOR_WALL },
4576
- { id: "t20", x: 2, y: 0, z: 0, type: "stone", passable: false, modelUrl: FLOOR_WALL },
4577
- { id: "t30", x: 3, y: 0, z: 0, type: "stone", passable: false, modelUrl: FLOOR_WALL },
4578
- { id: "t40", x: 4, y: 0, z: 0, type: "stone", passable: false, modelUrl: FLOOR_WALL },
4579
- { id: "t01", x: 0, y: 1, z: 1, type: "stone", passable: false, modelUrl: FLOOR_WALL },
4580
- { id: "t11", x: 1, y: 1, z: 1, type: "dirt", passable: true, modelUrl: FLOOR_DIRT },
4581
- { id: "t21", x: 2, y: 1, z: 1, type: "grass", passable: true, modelUrl: FLOOR_OPEN },
4582
- { id: "t31", x: 3, y: 1, z: 1, type: "grass", passable: true, modelUrl: FLOOR_OPEN },
4583
- { id: "t41", x: 4, y: 1, z: 1, type: "stone", passable: false, modelUrl: FLOOR_WALL },
4584
- { id: "t02", x: 0, y: 2, z: 2, type: "stone", passable: false, modelUrl: FLOOR_WALL },
4585
- { id: "t12", x: 1, y: 2, z: 2, type: "grass", passable: true, modelUrl: FLOOR_OPEN },
4586
- { id: "t22", x: 2, y: 2, z: 2, type: "dirt", passable: true, modelUrl: FLOOR_DIRT },
4587
- { id: "t32", x: 3, y: 2, z: 2, type: "grass", passable: true, modelUrl: FLOOR_OPEN },
4588
- { id: "t42", x: 4, y: 2, z: 2, type: "stone", passable: false, modelUrl: FLOOR_WALL },
4589
- { id: "t03", x: 0, y: 3, z: 3, type: "stone", passable: false, modelUrl: FLOOR_WALL },
4590
- { id: "t13", x: 1, y: 3, z: 3, type: "grass", passable: true, modelUrl: FLOOR_OPEN },
4591
- { id: "t23", x: 2, y: 3, z: 3, type: "grass", passable: true, modelUrl: FLOOR_OPEN },
4592
- { id: "t33", x: 3, y: 3, z: 3, type: "dirt", passable: true, modelUrl: FLOOR_DIRT },
4593
- { id: "t43", x: 4, y: 3, z: 3, type: "stone", passable: false, modelUrl: FLOOR_WALL },
4594
- { id: "t04", x: 0, y: 4, z: 4, type: "stone", passable: false, modelUrl: FLOOR_WALL },
4595
- { id: "t14", x: 1, y: 4, z: 4, type: "stone", passable: false, modelUrl: FLOOR_WALL },
4596
- { id: "t24", x: 2, y: 4, z: 4, type: "stone", passable: false, modelUrl: FLOOR_WALL },
4597
- { id: "t34", x: 3, y: 4, z: 4, type: "stone", passable: false, modelUrl: FLOOR_WALL },
4598
- { id: "t44", x: 4, y: 4, z: 4, type: "stone", passable: false, modelUrl: FLOOR_WALL }
4603
+
4604
+ // components/game/templates/game3dAssetManifest.ts
4605
+ function resolveManifestUrl(manifest, relative) {
4606
+ if (relative == null) return void 0;
4607
+ if (/^https?:\/\//.test(relative)) return relative;
4608
+ const base = manifest?.baseUrl;
4609
+ if (base == null) return relative;
4610
+ const cleanBase = base.replace(/\/$/, "");
4611
+ const cleanRel = relative.replace(/^\//, "");
4612
+ return `${cleanBase}/${cleanRel}`;
4613
+ }
4614
+ function tileKind(type, passable) {
4615
+ if (passable === false) return "wall";
4616
+ if (type === "dirt" || type === "road") return "dirt";
4617
+ return "open";
4618
+ }
4619
+ function resolveTilesWithModels(layout, manifest) {
4620
+ return layout.map((t) => {
4621
+ const kind = t.kind || tileKind(t.type, t.passable);
4622
+ return {
4623
+ id: t.id,
4624
+ x: t.x,
4625
+ y: t.y,
4626
+ z: t.z,
4627
+ type: t.type,
4628
+ passable: t.passable,
4629
+ modelUrl: resolveManifestUrl(manifest, manifest?.models?.[kind])
4630
+ };
4631
+ });
4632
+ }
4633
+ function resolvePropTiles(tiles, manifest) {
4634
+ return tiles.map((t) => {
4635
+ const kind = tileKind(t.type ?? "", t.passable);
4636
+ return {
4637
+ ...t,
4638
+ modelUrl: t.modelUrl ?? resolveManifestUrl(manifest, manifest?.models?.[kind])
4639
+ };
4640
+ });
4641
+ }
4642
+ function resolveEntityTiles(entity, manifest) {
4643
+ const row = boardEntity(entity);
4644
+ if (!row) return [];
4645
+ return rows(row.tiles).map((r) => {
4646
+ const type = str(r.type) || str(r.terrain);
4647
+ const passable = r.passable !== false;
4648
+ const kind = tileKind(type, passable);
4649
+ const explicitModel = r.modelUrl == null ? void 0 : str(r.modelUrl);
4650
+ return {
4651
+ id: r.id == null ? void 0 : str(r.id),
4652
+ x: num(r.x),
4653
+ y: num(r.y),
4654
+ z: r.z == null ? void 0 : num(r.z),
4655
+ type,
4656
+ passable,
4657
+ modelUrl: explicitModel ?? resolveManifestUrl(manifest, manifest?.models?.[kind])
4658
+ };
4659
+ });
4660
+ }
4661
+ function resolveFeaturesWithModels(features, manifest) {
4662
+ return features.map((f) => ({
4663
+ ...f,
4664
+ assetUrl: f.assetUrl ?? resolveManifestUrl(manifest, manifest?.features?.[f.type])
4665
+ }));
4666
+ }
4667
+ function resolveEntityFeatures(entity, manifest) {
4668
+ const row = boardEntity(entity);
4669
+ if (!row) return [];
4670
+ return rows(row.features).map((r) => {
4671
+ const type = str(r.type);
4672
+ const explicit = r.assetUrl == null ? void 0 : str(r.assetUrl);
4673
+ return {
4674
+ id: r.id == null ? void 0 : str(r.id),
4675
+ x: num(r.x),
4676
+ y: num(r.y),
4677
+ z: r.z == null ? void 0 : num(r.z),
4678
+ type,
4679
+ color: r.color == null ? void 0 : str(r.color),
4680
+ assetUrl: explicit ?? resolveManifestUrl(manifest, manifest?.features?.[type])
4681
+ };
4682
+ });
4683
+ }
4684
+ var DEFAULT_3D_BATTLE_LAYOUT = [
4685
+ { id: "t00", x: 0, y: 0, z: 0, type: "stone", passable: false, kind: "wall" },
4686
+ { id: "t10", x: 1, y: 0, z: 0, type: "stone", passable: false, kind: "wall" },
4687
+ { id: "t20", x: 2, y: 0, z: 0, type: "stone", passable: false, kind: "wall" },
4688
+ { id: "t30", x: 3, y: 0, z: 0, type: "stone", passable: false, kind: "wall" },
4689
+ { id: "t40", x: 4, y: 0, z: 0, type: "stone", passable: false, kind: "wall" },
4690
+ { id: "t01", x: 0, y: 1, z: 1, type: "stone", passable: false, kind: "wall" },
4691
+ { id: "t11", x: 1, y: 1, z: 1, type: "dirt", passable: true, kind: "dirt" },
4692
+ { id: "t21", x: 2, y: 1, z: 1, type: "grass", passable: true, kind: "open" },
4693
+ { id: "t31", x: 3, y: 1, z: 1, type: "grass", passable: true, kind: "open" },
4694
+ { id: "t41", x: 4, y: 1, z: 1, type: "stone", passable: false, kind: "wall" },
4695
+ { id: "t02", x: 0, y: 2, z: 2, type: "stone", passable: false, kind: "wall" },
4696
+ { id: "t12", x: 1, y: 2, z: 2, type: "grass", passable: true, kind: "open" },
4697
+ { id: "t22", x: 2, y: 2, z: 2, type: "dirt", passable: true, kind: "dirt" },
4698
+ { id: "t32", x: 3, y: 2, z: 2, type: "grass", passable: true, kind: "open" },
4699
+ { id: "t42", x: 4, y: 2, z: 2, type: "stone", passable: false, kind: "wall" },
4700
+ { id: "t03", x: 0, y: 3, z: 3, type: "stone", passable: false, kind: "wall" },
4701
+ { id: "t13", x: 1, y: 3, z: 3, type: "grass", passable: true, kind: "open" },
4702
+ { id: "t23", x: 2, y: 3, z: 3, type: "grass", passable: true, kind: "open" },
4703
+ { id: "t33", x: 3, y: 3, z: 3, type: "dirt", passable: true, kind: "dirt" },
4704
+ { id: "t43", x: 4, y: 3, z: 3, type: "stone", passable: false, kind: "wall" },
4705
+ { id: "t04", x: 0, y: 4, z: 4, type: "stone", passable: false, kind: "wall" },
4706
+ { id: "t14", x: 1, y: 4, z: 4, type: "stone", passable: false, kind: "wall" },
4707
+ { id: "t24", x: 2, y: 4, z: 4, type: "stone", passable: false, kind: "wall" },
4708
+ { id: "t34", x: 3, y: 4, z: 4, type: "stone", passable: false, kind: "wall" },
4709
+ { id: "t44", x: 4, y: 4, z: 4, type: "stone", passable: false, kind: "wall" }
4599
4710
  ];
4600
4711
  var DEFAULT_3D_BATTLE_FEATURES = [
4601
4712
  { id: "f1", x: 2, y: 2, z: 2, type: "gold_mine", color: "#f4c542" },
@@ -4603,8 +4714,9 @@ var DEFAULT_3D_BATTLE_FEATURES = [
4603
4714
  ];
4604
4715
  function GameCanvas3DBattleTemplate({
4605
4716
  entity,
4606
- tiles: propTiles = DEFAULT_3D_BATTLE_TILES,
4717
+ tiles: propTiles,
4607
4718
  features: propFeatures = DEFAULT_3D_BATTLE_FEATURES,
4719
+ assetManifest,
4608
4720
  cameraMode = "perspective",
4609
4721
  backgroundColor = "#2a1a1a",
4610
4722
  tileClickEvent,
@@ -4618,11 +4730,12 @@ function GameCanvas3DBattleTemplate({
4618
4730
  scale,
4619
4731
  className
4620
4732
  }) {
4621
- const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
4622
- const tiles = resolved ? Array.isArray(resolved.tiles) ? resolved.tiles : [] : propTiles;
4623
- const features = resolved ? Array.isArray(resolved.features) ? resolved.features : [] : propFeatures;
4624
- const currentTurn = resolved?.currentTeam;
4625
- const round = resolved?.turn == null ? void 0 : Number(resolved.turn);
4733
+ const row = boardEntity(entity);
4734
+ const hasEntityTiles = !!row && Array.isArray(row.tiles) && row.tiles.length > 0;
4735
+ const tiles = hasEntityTiles ? resolveEntityTiles(entity, assetManifest) : propTiles ? resolvePropTiles(propTiles, assetManifest) : resolveTilesWithModels(DEFAULT_3D_BATTLE_LAYOUT, assetManifest);
4736
+ const features = row && Array.isArray(row.features) && row.features.length > 0 ? resolveEntityFeatures(entity, assetManifest) : resolveFeaturesWithModels(propFeatures, assetManifest);
4737
+ const currentTurn = row?.currentTeam == null ? void 0 : String(row.currentTeam);
4738
+ const round = row?.turn == null ? void 0 : Number(row.turn);
4626
4739
  return /* @__PURE__ */ jsxs(
4627
4740
  Box,
4628
4741
  {
@@ -4668,36 +4781,32 @@ function GameCanvas3DBattleTemplate({
4668
4781
  );
4669
4782
  }
4670
4783
  GameCanvas3DBattleTemplate.displayName = "GameCanvas3DBattleTemplate";
4671
- var CDN2 = "https://almadar-kflow-assets.web.app/shared/3d/dungeon/floor";
4672
- var FLOOR_WALL2 = `${CDN2}/template-floor-detail-a.glb`;
4673
- var FLOOR_DIRT2 = `${CDN2}/template-floor-detail.glb`;
4674
- var FLOOR_OPEN2 = `${CDN2}/template-floor.glb`;
4675
- var DEFAULT_3D_CASTLE_TILES = [
4676
- { id: "t00", x: 0, y: 0, z: 0, type: "wall", passable: false, modelUrl: FLOOR_WALL2 },
4677
- { id: "t10", x: 1, y: 0, z: 0, type: "wall", passable: false, modelUrl: FLOOR_WALL2 },
4678
- { id: "t20", x: 2, y: 0, z: 0, type: "wall", passable: false, modelUrl: FLOOR_WALL2 },
4679
- { id: "t30", x: 3, y: 0, z: 0, type: "wall", passable: false, modelUrl: FLOOR_WALL2 },
4680
- { id: "t40", x: 4, y: 0, z: 0, type: "wall", passable: false, modelUrl: FLOOR_WALL2 },
4681
- { id: "t01", x: 0, y: 1, z: 1, type: "wall", passable: false, modelUrl: FLOOR_WALL2 },
4682
- { id: "t11", x: 1, y: 1, z: 1, type: "floor", passable: true, modelUrl: FLOOR_DIRT2 },
4683
- { id: "t21", x: 2, y: 1, z: 1, type: "floor", passable: true, modelUrl: FLOOR_OPEN2 },
4684
- { id: "t31", x: 3, y: 1, z: 1, type: "floor", passable: true, modelUrl: FLOOR_OPEN2 },
4685
- { id: "t41", x: 4, y: 1, z: 1, type: "wall", passable: false, modelUrl: FLOOR_WALL2 },
4686
- { id: "t02", x: 0, y: 2, z: 2, type: "wall", passable: false, modelUrl: FLOOR_WALL2 },
4687
- { id: "t12", x: 1, y: 2, z: 2, type: "floor", passable: true, modelUrl: FLOOR_OPEN2 },
4688
- { id: "t22", x: 2, y: 2, z: 2, type: "floor", passable: true, modelUrl: FLOOR_DIRT2 },
4689
- { id: "t32", x: 3, y: 2, z: 2, type: "floor", passable: true, modelUrl: FLOOR_OPEN2 },
4690
- { id: "t42", x: 4, y: 2, z: 2, type: "wall", passable: false, modelUrl: FLOOR_WALL2 },
4691
- { id: "t03", x: 0, y: 3, z: 3, type: "wall", passable: false, modelUrl: FLOOR_WALL2 },
4692
- { id: "t13", x: 1, y: 3, z: 3, type: "floor", passable: true, modelUrl: FLOOR_OPEN2 },
4693
- { id: "t23", x: 2, y: 3, z: 3, type: "floor", passable: true, modelUrl: FLOOR_OPEN2 },
4694
- { id: "t33", x: 3, y: 3, z: 3, type: "floor", passable: true, modelUrl: FLOOR_DIRT2 },
4695
- { id: "t43", x: 4, y: 3, z: 3, type: "wall", passable: false, modelUrl: FLOOR_WALL2 },
4696
- { id: "t04", x: 0, y: 4, z: 4, type: "wall", passable: false, modelUrl: FLOOR_WALL2 },
4697
- { id: "t14", x: 1, y: 4, z: 4, type: "wall", passable: false, modelUrl: FLOOR_WALL2 },
4698
- { id: "t24", x: 2, y: 4, z: 4, type: "wall", passable: false, modelUrl: FLOOR_WALL2 },
4699
- { id: "t34", x: 3, y: 4, z: 4, type: "wall", passable: false, modelUrl: FLOOR_WALL2 },
4700
- { id: "t44", x: 4, y: 4, z: 4, type: "wall", passable: false, modelUrl: FLOOR_WALL2 }
4784
+ var DEFAULT_3D_CASTLE_LAYOUT = [
4785
+ { id: "t00", x: 0, y: 0, z: 0, type: "wall", passable: false, kind: "wall" },
4786
+ { id: "t10", x: 1, y: 0, z: 0, type: "wall", passable: false, kind: "wall" },
4787
+ { id: "t20", x: 2, y: 0, z: 0, type: "wall", passable: false, kind: "wall" },
4788
+ { id: "t30", x: 3, y: 0, z: 0, type: "wall", passable: false, kind: "wall" },
4789
+ { id: "t40", x: 4, y: 0, z: 0, type: "wall", passable: false, kind: "wall" },
4790
+ { id: "t01", x: 0, y: 1, z: 1, type: "wall", passable: false, kind: "wall" },
4791
+ { id: "t11", x: 1, y: 1, z: 1, type: "floor", passable: true, kind: "dirt" },
4792
+ { id: "t21", x: 2, y: 1, z: 1, type: "floor", passable: true, kind: "open" },
4793
+ { id: "t31", x: 3, y: 1, z: 1, type: "floor", passable: true, kind: "open" },
4794
+ { id: "t41", x: 4, y: 1, z: 1, type: "wall", passable: false, kind: "wall" },
4795
+ { id: "t02", x: 0, y: 2, z: 2, type: "wall", passable: false, kind: "wall" },
4796
+ { id: "t12", x: 1, y: 2, z: 2, type: "floor", passable: true, kind: "open" },
4797
+ { id: "t22", x: 2, y: 2, z: 2, type: "floor", passable: true, kind: "dirt" },
4798
+ { id: "t32", x: 3, y: 2, z: 2, type: "floor", passable: true, kind: "open" },
4799
+ { id: "t42", x: 4, y: 2, z: 2, type: "wall", passable: false, kind: "wall" },
4800
+ { id: "t03", x: 0, y: 3, z: 3, type: "wall", passable: false, kind: "wall" },
4801
+ { id: "t13", x: 1, y: 3, z: 3, type: "floor", passable: true, kind: "open" },
4802
+ { id: "t23", x: 2, y: 3, z: 3, type: "floor", passable: true, kind: "open" },
4803
+ { id: "t33", x: 3, y: 3, z: 3, type: "floor", passable: true, kind: "dirt" },
4804
+ { id: "t43", x: 4, y: 3, z: 3, type: "wall", passable: false, kind: "wall" },
4805
+ { id: "t04", x: 0, y: 4, z: 4, type: "wall", passable: false, kind: "wall" },
4806
+ { id: "t14", x: 1, y: 4, z: 4, type: "wall", passable: false, kind: "wall" },
4807
+ { id: "t24", x: 2, y: 4, z: 4, type: "wall", passable: false, kind: "wall" },
4808
+ { id: "t34", x: 3, y: 4, z: 4, type: "wall", passable: false, kind: "wall" },
4809
+ { id: "t44", x: 4, y: 4, z: 4, type: "wall", passable: false, kind: "wall" }
4701
4810
  ];
4702
4811
  var DEFAULT_3D_CASTLE_FEATURES = [
4703
4812
  { id: "f1", x: 2, y: 2, z: 2, type: "chest", color: "#f4c542" },
@@ -4705,8 +4814,9 @@ var DEFAULT_3D_CASTLE_FEATURES = [
4705
4814
  ];
4706
4815
  function GameCanvas3DCastleTemplate({
4707
4816
  entity,
4708
- tiles: propTiles = DEFAULT_3D_CASTLE_TILES,
4817
+ tiles: propTiles,
4709
4818
  features: propFeatures = DEFAULT_3D_CASTLE_FEATURES,
4819
+ assetManifest,
4710
4820
  cameraMode = "isometric",
4711
4821
  backgroundColor = "#1e1e2e",
4712
4822
  buildingClickEvent,
@@ -4721,13 +4831,14 @@ function GameCanvas3DCastleTemplate({
4721
4831
  scale,
4722
4832
  className
4723
4833
  }) {
4724
- const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
4725
- const tiles = resolved ? Array.isArray(resolved.tiles) ? resolved.tiles : [] : propTiles;
4726
- const features = resolved ? Array.isArray(resolved.features) ? resolved.features : [] : propFeatures;
4727
- const name = resolved?.name == null ? void 0 : String(resolved.name);
4728
- const level = resolved?.level == null ? void 0 : Number(resolved.level);
4729
- const owner = resolved?.owner == null ? void 0 : String(resolved.owner);
4730
- const unitCount = resolved && Array.isArray(resolved.units) ? resolved.units.length : 0;
4834
+ const row = boardEntity(entity);
4835
+ const hasEntityTiles = !!row && Array.isArray(row.tiles) && row.tiles.length > 0;
4836
+ const tiles = hasEntityTiles ? resolveEntityTiles(entity, assetManifest) : propTiles ? resolvePropTiles(propTiles, assetManifest) : resolveTilesWithModels(DEFAULT_3D_CASTLE_LAYOUT, assetManifest);
4837
+ const features = row && Array.isArray(row.features) && row.features.length > 0 ? resolveEntityFeatures(entity, assetManifest) : resolveFeaturesWithModels(propFeatures, assetManifest);
4838
+ const name = row?.name == null ? void 0 : str(row.name);
4839
+ const level = row?.level == null ? void 0 : num(row.level);
4840
+ const owner = row?.owner == null ? void 0 : str(row.owner);
4841
+ const unitCount = row && Array.isArray(row.units) ? row.units.length : 0;
4731
4842
  return /* @__PURE__ */ jsxs(
4732
4843
  VStack,
4733
4844
  {
@@ -4773,36 +4884,32 @@ function GameCanvas3DCastleTemplate({
4773
4884
  );
4774
4885
  }
4775
4886
  GameCanvas3DCastleTemplate.displayName = "GameCanvas3DCastleTemplate";
4776
- var CDN3 = "https://almadar-kflow-assets.web.app/shared/3d/dungeon/floor";
4777
- var FLOOR_WALL3 = `${CDN3}/template-floor-detail-a.glb`;
4778
- var FLOOR_DIRT3 = `${CDN3}/template-floor-detail.glb`;
4779
- var FLOOR_OPEN3 = `${CDN3}/template-floor.glb`;
4780
- var DEFAULT_3D_WORLDMAP_TILES = [
4781
- { id: "t00", x: 0, y: 0, z: 0, type: "mountain", passable: false, modelUrl: FLOOR_WALL3 },
4782
- { id: "t10", x: 1, y: 0, z: 0, type: "mountain", passable: false, modelUrl: FLOOR_WALL3 },
4783
- { id: "t20", x: 2, y: 0, z: 0, type: "water", passable: false, modelUrl: FLOOR_WALL3 },
4784
- { id: "t30", x: 3, y: 0, z: 0, type: "mountain", passable: false, modelUrl: FLOOR_WALL3 },
4785
- { id: "t40", x: 4, y: 0, z: 0, type: "mountain", passable: false, modelUrl: FLOOR_WALL3 },
4786
- { id: "t01", x: 0, y: 1, z: 1, type: "mountain", passable: false, modelUrl: FLOOR_WALL3 },
4787
- { id: "t11", x: 1, y: 1, z: 1, type: "grass", passable: true, modelUrl: FLOOR_OPEN3 },
4788
- { id: "t21", x: 2, y: 1, z: 1, type: "grass", passable: true, modelUrl: FLOOR_OPEN3 },
4789
- { id: "t31", x: 3, y: 1, z: 1, type: "grass", passable: true, modelUrl: FLOOR_OPEN3 },
4790
- { id: "t41", x: 4, y: 1, z: 1, type: "water", passable: false, modelUrl: FLOOR_WALL3 },
4791
- { id: "t02", x: 0, y: 2, z: 2, type: "water", passable: false, modelUrl: FLOOR_WALL3 },
4792
- { id: "t12", x: 1, y: 2, z: 2, type: "grass", passable: true, modelUrl: FLOOR_OPEN3 },
4793
- { id: "t22", x: 2, y: 2, z: 2, type: "road", passable: true, modelUrl: FLOOR_DIRT3 },
4794
- { id: "t32", x: 3, y: 2, z: 2, type: "grass", passable: true, modelUrl: FLOOR_OPEN3 },
4795
- { id: "t42", x: 4, y: 2, z: 2, type: "mountain", passable: false, modelUrl: FLOOR_WALL3 },
4796
- { id: "t03", x: 0, y: 3, z: 3, type: "mountain", passable: false, modelUrl: FLOOR_WALL3 },
4797
- { id: "t13", x: 1, y: 3, z: 3, type: "grass", passable: true, modelUrl: FLOOR_OPEN3 },
4798
- { id: "t23", x: 2, y: 3, z: 3, type: "grass", passable: true, modelUrl: FLOOR_OPEN3 },
4799
- { id: "t33", x: 3, y: 3, z: 3, type: "road", passable: true, modelUrl: FLOOR_DIRT3 },
4800
- { id: "t43", x: 4, y: 3, z: 3, type: "mountain", passable: false, modelUrl: FLOOR_WALL3 },
4801
- { id: "t04", x: 0, y: 4, z: 4, type: "mountain", passable: false, modelUrl: FLOOR_WALL3 },
4802
- { id: "t14", x: 1, y: 4, z: 4, type: "water", passable: false, modelUrl: FLOOR_WALL3 },
4803
- { id: "t24", x: 2, y: 4, z: 4, type: "grass", passable: true, modelUrl: FLOOR_OPEN3 },
4804
- { id: "t34", x: 3, y: 4, z: 4, type: "mountain", passable: false, modelUrl: FLOOR_WALL3 },
4805
- { id: "t44", x: 4, y: 4, z: 4, type: "mountain", passable: false, modelUrl: FLOOR_WALL3 }
4887
+ var DEFAULT_3D_WORLDMAP_LAYOUT = [
4888
+ { id: "t00", x: 0, y: 0, z: 0, type: "mountain", passable: false, kind: "wall" },
4889
+ { id: "t10", x: 1, y: 0, z: 0, type: "mountain", passable: false, kind: "wall" },
4890
+ { id: "t20", x: 2, y: 0, z: 0, type: "water", passable: false, kind: "wall" },
4891
+ { id: "t30", x: 3, y: 0, z: 0, type: "mountain", passable: false, kind: "wall" },
4892
+ { id: "t40", x: 4, y: 0, z: 0, type: "mountain", passable: false, kind: "wall" },
4893
+ { id: "t01", x: 0, y: 1, z: 1, type: "mountain", passable: false, kind: "wall" },
4894
+ { id: "t11", x: 1, y: 1, z: 1, type: "grass", passable: true, kind: "open" },
4895
+ { id: "t21", x: 2, y: 1, z: 1, type: "grass", passable: true, kind: "open" },
4896
+ { id: "t31", x: 3, y: 1, z: 1, type: "grass", passable: true, kind: "open" },
4897
+ { id: "t41", x: 4, y: 1, z: 1, type: "water", passable: false, kind: "wall" },
4898
+ { id: "t02", x: 0, y: 2, z: 2, type: "water", passable: false, kind: "wall" },
4899
+ { id: "t12", x: 1, y: 2, z: 2, type: "grass", passable: true, kind: "open" },
4900
+ { id: "t22", x: 2, y: 2, z: 2, type: "road", passable: true, kind: "dirt" },
4901
+ { id: "t32", x: 3, y: 2, z: 2, type: "grass", passable: true, kind: "open" },
4902
+ { id: "t42", x: 4, y: 2, z: 2, type: "mountain", passable: false, kind: "wall" },
4903
+ { id: "t03", x: 0, y: 3, z: 3, type: "mountain", passable: false, kind: "wall" },
4904
+ { id: "t13", x: 1, y: 3, z: 3, type: "grass", passable: true, kind: "open" },
4905
+ { id: "t23", x: 2, y: 3, z: 3, type: "grass", passable: true, kind: "open" },
4906
+ { id: "t33", x: 3, y: 3, z: 3, type: "road", passable: true, kind: "dirt" },
4907
+ { id: "t43", x: 4, y: 3, z: 3, type: "mountain", passable: false, kind: "wall" },
4908
+ { id: "t04", x: 0, y: 4, z: 4, type: "mountain", passable: false, kind: "wall" },
4909
+ { id: "t14", x: 1, y: 4, z: 4, type: "water", passable: false, kind: "wall" },
4910
+ { id: "t24", x: 2, y: 4, z: 4, type: "grass", passable: true, kind: "open" },
4911
+ { id: "t34", x: 3, y: 4, z: 4, type: "mountain", passable: false, kind: "wall" },
4912
+ { id: "t44", x: 4, y: 4, z: 4, type: "mountain", passable: false, kind: "wall" }
4806
4913
  ];
4807
4914
  var DEFAULT_3D_WORLDMAP_FEATURES = [
4808
4915
  { id: "f1", x: 2, y: 2, z: 2, type: "capital", color: "#f4c542" },
@@ -4810,8 +4917,9 @@ var DEFAULT_3D_WORLDMAP_FEATURES = [
4810
4917
  ];
4811
4918
  function GameCanvas3DWorldMapTemplate({
4812
4919
  entity,
4813
- tiles: propTiles = DEFAULT_3D_WORLDMAP_TILES,
4920
+ tiles: propTiles,
4814
4921
  features: propFeatures = DEFAULT_3D_WORLDMAP_FEATURES,
4922
+ assetManifest,
4815
4923
  cameraMode = "isometric",
4816
4924
  backgroundColor = "#1a1a2e",
4817
4925
  tileClickEvent,
@@ -4825,9 +4933,10 @@ function GameCanvas3DWorldMapTemplate({
4825
4933
  scale,
4826
4934
  className
4827
4935
  }) {
4828
- const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
4829
- const tiles = resolved ? Array.isArray(resolved.tiles) ? resolved.tiles : [] : propTiles;
4830
- const features = resolved ? Array.isArray(resolved.features) ? resolved.features : [] : propFeatures;
4936
+ const row = boardEntity(entity);
4937
+ const hasEntityTiles = !!row && Array.isArray(row.tiles) && row.tiles.length > 0;
4938
+ const tiles = hasEntityTiles ? resolveEntityTiles(entity, assetManifest) : propTiles ? resolvePropTiles(propTiles, assetManifest) : resolveTilesWithModels(DEFAULT_3D_WORLDMAP_LAYOUT, assetManifest);
4939
+ const features = row && Array.isArray(row.features) && row.features.length > 0 ? resolveEntityFeatures(entity, assetManifest) : resolveFeaturesWithModels(propFeatures, assetManifest);
4831
4940
  return (
4832
4941
  /* GameBoard3D reads selectedUnitId/validMoves/attackTargets from entity */
4833
4942
  /* @__PURE__ */ jsx(
@@ -0,0 +1,46 @@
1
+ import React from 'react';
2
+ import type { AssetUrl, EventEmit, EntityRow } from '@almadar/core';
3
+ import type { DisplayStateProps } from '../../core/organisms/types';
4
+ /** Manifest of asset base-url + per-card sprite map (UI value DTO). */
5
+ type CardBattlerAssetManifest = {
6
+ baseUrl?: AssetUrl;
7
+ cards?: Record<string, AssetUrl>;
8
+ };
9
+ /** One card as carried on the entity / passed as a prop. */
10
+ export interface CardBattlerCard {
11
+ id: string;
12
+ iconKey?: string;
13
+ title?: string;
14
+ cost?: number;
15
+ attack?: number;
16
+ defense?: number;
17
+ }
18
+ export interface CardBattlerBoardProps extends DisplayStateProps {
19
+ entity?: EntityRow | readonly EntityRow[];
20
+ /** Cards remaining in the draw deck (count-only view). */
21
+ deck?: CardBattlerCard[];
22
+ /** Cards currently in the player's hand. */
23
+ hand?: CardBattlerCard[];
24
+ /** Cards played onto the board this match. */
25
+ board?: CardBattlerCard[];
26
+ mana?: number;
27
+ maxMana?: number;
28
+ turn?: number;
29
+ result?: 'none' | 'won' | 'lost';
30
+ /** Base-url + per-iconKey card sprite map (organism owns asset choice). */
31
+ assetManifest?: CardBattlerAssetManifest;
32
+ playCardEvent?: EventEmit<{
33
+ cardId: string;
34
+ }>;
35
+ endTurnEvent?: EventEmit<Record<string, never>>;
36
+ playAgainEvent?: EventEmit<Record<string, never>>;
37
+ gameEndEvent?: EventEmit<{
38
+ result: 'won' | 'lost';
39
+ }>;
40
+ className?: string;
41
+ }
42
+ export declare function CardBattlerBoard({ entity, deck: propDeck, hand: propHand, board: propBoard, mana: propMana, maxMana: propMaxMana, turn: propTurn, result: propResult, assetManifest: propAssetManifest, playCardEvent, endTurnEvent, playAgainEvent, gameEndEvent, className, }: CardBattlerBoardProps): React.JSX.Element;
43
+ export declare namespace CardBattlerBoard {
44
+ var displayName: string;
45
+ }
46
+ export default CardBattlerBoard;
@@ -0,0 +1,63 @@
1
+ import React from 'react';
2
+ import type { AssetUrl, EventEmit, EntityRow } from '@almadar/core';
3
+ import type { DisplayStateProps } from '../../core/organisms/types';
4
+ /** Manifest of asset base-url + per-kind sprite maps (UI value DTO). */
5
+ type CityBuilderAssetManifest = {
6
+ baseUrl?: AssetUrl;
7
+ terrains?: Record<string, AssetUrl>;
8
+ units?: Record<string, AssetUrl>;
9
+ features?: Record<string, AssetUrl>;
10
+ };
11
+ export interface CityBuilderTile {
12
+ x: number;
13
+ y: number;
14
+ terrain?: string;
15
+ terrainSprite?: AssetUrl;
16
+ passable?: boolean;
17
+ }
18
+ export interface CityBuilderBuilding {
19
+ id: string;
20
+ x: number;
21
+ y: number;
22
+ type: string;
23
+ }
24
+ /** A placeable building type shown in the build palette (UI value DTO). */
25
+ export interface CityBuilderBuildType {
26
+ type: string;
27
+ label: string;
28
+ cost: number;
29
+ }
30
+ export interface CityBuilderBoardProps extends DisplayStateProps {
31
+ entity?: EntityRow | readonly EntityRow[];
32
+ tiles?: CityBuilderTile[];
33
+ buildings?: CityBuilderBuilding[];
34
+ /** Asset base-url + terrain/unit/feature sprite maps (organism owns asset choice). */
35
+ assetManifest?: CityBuilderAssetManifest;
36
+ buildTypes?: CityBuilderBuildType[];
37
+ pendingBuildType?: string;
38
+ gold?: number;
39
+ wood?: number;
40
+ population?: number;
41
+ tick?: number;
42
+ result?: 'none' | 'won' | 'lost';
43
+ scale?: number;
44
+ unitScale?: number;
45
+ spriteHeightRatio?: number;
46
+ spriteMaxWidthRatio?: number;
47
+ selectBuildTypeEvent?: EventEmit<{
48
+ buildType: string;
49
+ }>;
50
+ placeBuildingEvent?: EventEmit<{
51
+ x: number;
52
+ y: number;
53
+ }>;
54
+ gameEndEvent?: EventEmit<{
55
+ result: 'won' | 'lost';
56
+ }>;
57
+ className?: string;
58
+ }
59
+ export declare function CityBuilderBoard({ entity, tiles: propTiles, buildings: propBuildings, assetManifest: propAssetManifest, buildTypes: propBuildTypes, pendingBuildType: propPendingBuildType, gold: propGold, wood: propWood, population: propPopulation, result: propResult, scale, unitScale, spriteHeightRatio, spriteMaxWidthRatio, selectBuildTypeEvent, placeBuildingEvent, gameEndEvent, className, }: CityBuilderBoardProps): React.JSX.Element;
60
+ export declare namespace CityBuilderBoard {
61
+ var displayName: string;
62
+ }
63
+ export default CityBuilderBoard;