@almadar/ui 5.55.0 → 5.57.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.
@@ -14581,6 +14581,8 @@ function IsometricCanvas({
14581
14581
  showMinimap = true,
14582
14582
  enableCamera = true,
14583
14583
  unitScale = 1,
14584
+ spriteHeightRatio = 1.5,
14585
+ spriteMaxWidthRatio = 0.6,
14584
14586
  // Asset resolution
14585
14587
  getTerrainSprite,
14586
14588
  getFeatureSprite,
@@ -14971,8 +14973,8 @@ function IsometricCanvas({
14971
14973
  const breatheOffset = 0.8 * scale * (1 + Math.sin(animTime * 2e-3 + (unit.position.x * 3.7 + unit.position.y * 5.3)));
14972
14974
  const unitSpriteUrl = resolveUnitSpriteUrl(unit);
14973
14975
  const img = unitSpriteUrl ? getImage(unitSpriteUrl) : null;
14974
- const unitDrawH = scaledFloorHeight * 1.5 * unitScale;
14975
- const maxUnitW = scaledTileWidth * 0.6 * unitScale;
14976
+ const unitDrawH = scaledFloorHeight * spriteHeightRatio * unitScale;
14977
+ const maxUnitW = scaledTileWidth * spriteMaxWidthRatio * unitScale;
14976
14978
  const ar = img ? img.naturalWidth / img.naturalHeight : 0.5;
14977
14979
  let drawH = unitDrawH;
14978
14980
  let drawW = unitDrawH * ar;
@@ -15348,14 +15350,32 @@ var init_boardEntity = __esm({
15348
15350
  "components/game/organisms/boardEntity.ts"() {
15349
15351
  }
15350
15352
  });
15353
+ function buildDefaultBattleTiles() {
15354
+ const tiles = [];
15355
+ for (let y = 0; y < BATTLE_GRID_H; y++) {
15356
+ for (let x = 0; x < BATTLE_GRID_W; x++) {
15357
+ const variant = (x * 3 + y * 7 + (x ^ y)) % BATTLE_TERRAIN_SPRITES.length;
15358
+ tiles.push({
15359
+ x,
15360
+ y,
15361
+ terrain: ["grass", "dirt", "planks", "stone", "stone"][variant],
15362
+ terrainSprite: BATTLE_TERRAIN_SPRITES[variant],
15363
+ passable: true
15364
+ });
15365
+ }
15366
+ }
15367
+ return tiles;
15368
+ }
15351
15369
  function BattleBoard({
15352
15370
  entity,
15353
15371
  tiles: propTiles,
15354
15372
  units: propUnits,
15355
15373
  features: propFeatures,
15356
15374
  assetManifest: propAssetManifest,
15357
- scale = 0.45,
15375
+ scale = 0.25,
15358
15376
  unitScale = 1,
15377
+ spriteHeightRatio = 1.5,
15378
+ spriteMaxWidthRatio = 0.6,
15359
15379
  header,
15360
15380
  sidebar,
15361
15381
  actions,
@@ -15379,10 +15399,11 @@ function BattleBoard({
15379
15399
  className
15380
15400
  }) {
15381
15401
  const board = boardEntity(entity) ?? {};
15382
- const tiles = propTiles ?? (Array.isArray(board.tiles) ? board.tiles : []);
15402
+ const rawTiles = propTiles ?? (Array.isArray(board.tiles) ? board.tiles : []);
15403
+ const tiles = rawTiles.length === 0 ? DEFAULT_BATTLE_TILES : rawTiles;
15383
15404
  const features = propFeatures ?? (Array.isArray(board.features) ? board.features : []);
15384
- const boardWidth = num(board.gridWidth ?? board.boardWidth, 8);
15385
- const boardHeight = num(board.gridHeight ?? board.boardHeight, 6);
15405
+ const boardWidth = num(board.gridWidth ?? board.boardWidth, BATTLE_GRID_W);
15406
+ const boardHeight = num(board.gridHeight ?? board.boardHeight, BATTLE_GRID_H);
15386
15407
  const assetManifest = propAssetManifest ?? board.assetManifest;
15387
15408
  const backgroundImage = board.backgroundImage;
15388
15409
  const units = rows(board.units);
@@ -15646,7 +15667,9 @@ function BattleBoard({
15646
15667
  hasActiveEffects: hasActiveEffects2,
15647
15668
  effectSpriteUrls,
15648
15669
  resolveUnitFrame,
15649
- unitScale
15670
+ unitScale,
15671
+ spriteHeightRatio,
15672
+ spriteMaxWidthRatio
15650
15673
  }
15651
15674
  ),
15652
15675
  overlay && overlay(ctx)
@@ -15702,6 +15725,7 @@ function BattleBoard({
15702
15725
  ] }) }))
15703
15726
  ] });
15704
15727
  }
15728
+ var BATTLE_CDN, BATTLE_GRID_W, BATTLE_GRID_H, BATTLE_TERRAIN_SPRITES, DEFAULT_BATTLE_TILES;
15705
15729
  var init_BattleBoard = __esm({
15706
15730
  "components/game/organisms/BattleBoard.tsx"() {
15707
15731
  "use client";
@@ -15714,6 +15738,17 @@ var init_BattleBoard = __esm({
15714
15738
  init_IsometricCanvas();
15715
15739
  init_boardEntity();
15716
15740
  init_isometric();
15741
+ BATTLE_CDN = "https://almadar-kflow-assets.web.app/shared";
15742
+ BATTLE_GRID_W = 16;
15743
+ BATTLE_GRID_H = 16;
15744
+ BATTLE_TERRAIN_SPRITES = [
15745
+ `${BATTLE_CDN}/isometric-dungeon/Isometric/dirt_E.png`,
15746
+ `${BATTLE_CDN}/isometric-dungeon/Isometric/dirtTiles_E.png`,
15747
+ `${BATTLE_CDN}/isometric-dungeon/Isometric/planks_E.png`,
15748
+ `${BATTLE_CDN}/isometric-dungeon/Isometric/stoneTile_E.png`,
15749
+ `${BATTLE_CDN}/isometric-dungeon/Isometric/stoneInset_E.png`
15750
+ ];
15751
+ DEFAULT_BATTLE_TILES = buildDefaultBattleTiles();
15717
15752
  BattleBoard.displayName = "BattleBoard";
15718
15753
  }
15719
15754
  });
@@ -15721,7 +15756,7 @@ function BattleTemplate({
15721
15756
  entity,
15722
15757
  scale = 0.45,
15723
15758
  unitScale = 1,
15724
- tiles = DEFAULT_BATTLE_TILES,
15759
+ tiles = DEFAULT_BATTLE_TILES2,
15725
15760
  units = DEFAULT_BATTLE_UNITS,
15726
15761
  features = DEFAULT_BATTLE_FEATURES,
15727
15762
  assetManifest = DEFAULT_BATTLE_MANIFEST,
@@ -15750,12 +15785,12 @@ function BattleTemplate({
15750
15785
  }
15751
15786
  );
15752
15787
  }
15753
- var CDN, DEFAULT_BATTLE_TILES, DEFAULT_BATTLE_UNITS, DEFAULT_BATTLE_FEATURES, DEFAULT_BATTLE_MANIFEST;
15788
+ var CDN, DEFAULT_BATTLE_TILES2, DEFAULT_BATTLE_UNITS, DEFAULT_BATTLE_FEATURES, DEFAULT_BATTLE_MANIFEST;
15754
15789
  var init_BattleTemplate = __esm({
15755
15790
  "components/game/templates/BattleTemplate.tsx"() {
15756
15791
  init_BattleBoard();
15757
15792
  CDN = "https://almadar-kflow-assets.web.app/shared";
15758
- DEFAULT_BATTLE_TILES = [
15793
+ DEFAULT_BATTLE_TILES2 = [
15759
15794
  { x: 0, y: 0, terrain: "stone", passable: false, terrainSprite: `${CDN}/isometric-blocks/PNG/Abstract tiles/abstractTile_07.png` },
15760
15795
  { x: 1, y: 0, terrain: "stone", passable: false, terrainSprite: `${CDN}/isometric-blocks/PNG/Abstract tiles/abstractTile_07.png` },
15761
15796
  { x: 2, y: 0, terrain: "stone", passable: false, terrainSprite: `${CDN}/isometric-blocks/PNG/Abstract tiles/abstractTile_07.png` },
@@ -44379,6 +44414,7 @@ var init_GameCanvas3D2 = __esm({
44379
44414
  attackTargets = [],
44380
44415
  selectedTileIds = [],
44381
44416
  selectedUnitId = null,
44417
+ unitScale = 1,
44382
44418
  children
44383
44419
  }, ref) => {
44384
44420
  const containerRef = React90.useRef(null);
@@ -44595,12 +44631,18 @@ var init_GameCanvas3D2 = __esm({
44595
44631
  },
44596
44632
  [selectedTileIds, hoveredTile, validMoves, attackTargets, handleTileClick, handleTileHover]
44597
44633
  );
44634
+ const UNIT_BASE_MODEL_SCALE = 0.5;
44635
+ const UNIT_BASE_BILLBOARD_HEIGHT = 1.2;
44636
+ const UNIT_BASE_PRIMITIVE_RADIUS = 0.3;
44598
44637
  const DefaultUnitRenderer = React90.useCallback(
44599
44638
  ({ unit, position }) => {
44600
44639
  const isSelected = selectedUnitId === unit.id;
44601
44640
  const color = unit.faction === "player" ? 4491519 : unit.faction === "enemy" ? 16729156 : 16777028;
44602
44641
  const hasAtlas = unitAtlasUrl(unit) !== null;
44603
44642
  const initialFrame = hasAtlas ? resolveUnitFrame(unit.id) : null;
44643
+ const modelScale = UNIT_BASE_MODEL_SCALE * unitScale;
44644
+ const billboardHeight = UNIT_BASE_BILLBOARD_HEIGHT * unitScale;
44645
+ const primitiveRadius = UNIT_BASE_PRIMITIVE_RADIUS * unitScale;
44604
44646
  return /* @__PURE__ */ jsxRuntime.jsxs(
44605
44647
  "group",
44606
44648
  {
@@ -44618,7 +44660,8 @@ var init_GameCanvas3D2 = __esm({
44618
44660
  UnitSpriteBillboard,
44619
44661
  {
44620
44662
  sheetUrl: initialFrame.sheetUrl,
44621
- resolveFrame: () => resolveUnitFrame(unit.id)
44663
+ resolveFrame: () => resolveUnitFrame(unit.id),
44664
+ height: billboardHeight
44622
44665
  }
44623
44666
  ) })
44624
44667
  ) : unit.modelUrl ? (
@@ -44627,22 +44670,22 @@ var init_GameCanvas3D2 = __esm({
44627
44670
  ModelLoader,
44628
44671
  {
44629
44672
  url: unit.modelUrl,
44630
- scale: 0.5,
44673
+ scale: modelScale,
44631
44674
  fallbackGeometry: "box",
44632
44675
  castShadow: true
44633
44676
  }
44634
44677
  )
44635
44678
  ) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
44636
- /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.3, 0], children: [
44637
- /* @__PURE__ */ jsxRuntime.jsx("cylinderGeometry", { args: [0.3, 0.3, 0.1, 8] }),
44679
+ /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, primitiveRadius, 0], children: [
44680
+ /* @__PURE__ */ jsxRuntime.jsx("cylinderGeometry", { args: [primitiveRadius, primitiveRadius, primitiveRadius * 0.33, 8] }),
44638
44681
  /* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color })
44639
44682
  ] }),
44640
- /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.6, 0], children: [
44641
- /* @__PURE__ */ jsxRuntime.jsx("capsuleGeometry", { args: [0.2, 0.4, 4, 8] }),
44683
+ /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, primitiveRadius * 2, 0], children: [
44684
+ /* @__PURE__ */ jsxRuntime.jsx("capsuleGeometry", { args: [primitiveRadius * 0.67, primitiveRadius * 1.33, 4, 8] }),
44642
44685
  /* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color })
44643
44686
  ] }),
44644
- /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.9, 0], children: [
44645
- /* @__PURE__ */ jsxRuntime.jsx("sphereGeometry", { args: [0.12, 8, 8] }),
44687
+ /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, primitiveRadius * 3, 0], children: [
44688
+ /* @__PURE__ */ jsxRuntime.jsx("sphereGeometry", { args: [primitiveRadius * 0.4, 8, 8] }),
44646
44689
  /* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color })
44647
44690
  ] })
44648
44691
  ] }),
@@ -44675,7 +44718,7 @@ var init_GameCanvas3D2 = __esm({
44675
44718
  }
44676
44719
  );
44677
44720
  },
44678
- [selectedUnitId, handleUnitClick, resolveUnitFrame]
44721
+ [selectedUnitId, handleUnitClick, resolveUnitFrame, unitScale]
44679
44722
  );
44680
44723
  const DefaultFeatureRenderer = React90.useCallback(
44681
44724
  ({
@@ -44818,33 +44861,35 @@ var init_GameCanvas3D2 = __esm({
44818
44861
  fadeStrength: 1
44819
44862
  }
44820
44863
  ),
44821
- tiles.map((tile, index) => {
44822
- const position = gridToWorld(
44823
- tile.x,
44824
- tile.z ?? tile.y ?? 0,
44825
- tile.elevation ?? 0
44826
- );
44827
- const Renderer = CustomTileRenderer || DefaultTileRenderer;
44828
- return /* @__PURE__ */ jsxRuntime.jsx(Renderer, { tile, position }, tile.id ?? `tile-${index}`);
44829
- }),
44830
- features.map((feature, index) => {
44831
- const position = gridToWorld(
44832
- feature.x,
44833
- feature.z ?? feature.y ?? 0,
44834
- (feature.elevation ?? 0) + 0.5
44835
- );
44836
- const Renderer = CustomFeatureRenderer || DefaultFeatureRenderer;
44837
- return /* @__PURE__ */ jsxRuntime.jsx(Renderer, { feature, position }, feature.id ?? `feature-${index}`);
44838
- }),
44839
- units.map((unit) => {
44840
- const position = gridToWorld(
44841
- unit.x ?? 0,
44842
- unit.z ?? unit.y ?? 0,
44843
- (unit.elevation ?? 0) + 0.5
44844
- );
44845
- const Renderer = CustomUnitRenderer || DefaultUnitRenderer;
44846
- return /* @__PURE__ */ jsxRuntime.jsx(Renderer, { unit, position }, unit.id);
44847
- }),
44864
+ /* @__PURE__ */ jsxRuntime.jsxs("group", { children: [
44865
+ tiles.map((tile, index) => {
44866
+ const position = gridToWorld(
44867
+ tile.x,
44868
+ tile.z ?? tile.y ?? 0,
44869
+ tile.elevation ?? 0
44870
+ );
44871
+ const Renderer = CustomTileRenderer || DefaultTileRenderer;
44872
+ return /* @__PURE__ */ jsxRuntime.jsx(Renderer, { tile, position }, tile.id ?? `tile-${index}`);
44873
+ }),
44874
+ features.map((feature, index) => {
44875
+ const position = gridToWorld(
44876
+ feature.x,
44877
+ feature.z ?? feature.y ?? 0,
44878
+ (feature.elevation ?? 0) + 0.5
44879
+ );
44880
+ const Renderer = CustomFeatureRenderer || DefaultFeatureRenderer;
44881
+ return /* @__PURE__ */ jsxRuntime.jsx(Renderer, { feature, position }, feature.id ?? `feature-${index}`);
44882
+ }),
44883
+ units.map((unit) => {
44884
+ const position = gridToWorld(
44885
+ unit.x ?? 0,
44886
+ unit.z ?? unit.y ?? 0,
44887
+ (unit.elevation ?? 0) + 0.5
44888
+ );
44889
+ const Renderer = CustomUnitRenderer || DefaultUnitRenderer;
44890
+ return /* @__PURE__ */ jsxRuntime.jsx(Renderer, { unit, position }, unit.id);
44891
+ })
44892
+ ] }),
44848
44893
  children,
44849
44894
  /* @__PURE__ */ jsxRuntime.jsx(
44850
44895
  drei.OrbitControls,
@@ -44887,6 +44932,8 @@ function GameBoard3D({
44887
44932
  features = [],
44888
44933
  cameraMode = "perspective",
44889
44934
  backgroundColor = "#2a1a1a",
44935
+ unitScale = 1,
44936
+ scale = 0.45,
44890
44937
  tileClickEvent,
44891
44938
  unitClickEvent,
44892
44939
  attackEvent,
@@ -44987,6 +45034,8 @@ function GameBoard3D({
44987
45034
  selectedUnitId,
44988
45035
  validMoves,
44989
45036
  attackTargets,
45037
+ unitScale,
45038
+ scale,
44990
45039
  className: "game-board-3d__canvas w-full min-h-[85vh]"
44991
45040
  }
44992
45041
  ),
@@ -46955,6 +47004,35 @@ var init_PricingPageTemplate = __esm({
46955
47004
  PricingPageTemplate.displayName = "PricingPageTemplate";
46956
47005
  }
46957
47006
  });
47007
+ function dungeonTerrain(x, y, stairsX, stairsY) {
47008
+ const isBorder = x === 0 || y === 0 || x === DUNGEON_GRID_W - 1 || y === DUNGEON_GRID_H - 1;
47009
+ const isPillar = x % 4 === 0 && y % 4 === 0 && !isBorder;
47010
+ const isWall = isBorder || isPillar;
47011
+ if (x === stairsX && y === stairsY) {
47012
+ return { terrain: "stairs", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stairs_E.png` };
47013
+ }
47014
+ if (isWall) {
47015
+ return { terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` };
47016
+ }
47017
+ const variant = (x * 3 + y * 7) % 5;
47018
+ const sprites = [
47019
+ `${CDN4}/isometric-dungeon/Isometric/dirt_E.png`,
47020
+ `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png`,
47021
+ `${CDN4}/isometric-dungeon/Isometric/planks_E.png`,
47022
+ `${CDN4}/isometric-dungeon/Isometric/stoneTile_E.png`,
47023
+ `${CDN4}/isometric-dungeon/Isometric/dirt_E.png`
47024
+ ];
47025
+ return { terrain: "floor", passable: true, terrainSprite: sprites[variant] };
47026
+ }
47027
+ function buildDefaultDungeonTiles(stairsX, stairsY) {
47028
+ const tiles = [];
47029
+ for (let y = 0; y < DUNGEON_GRID_H; y++) {
47030
+ for (let x = 0; x < DUNGEON_GRID_W; x++) {
47031
+ tiles.push({ x, y, ...dungeonTerrain(x, y, stairsX, stairsY) });
47032
+ }
47033
+ }
47034
+ return tiles;
47035
+ }
46958
47036
  function RoguelikeBoard({
46959
47037
  entity,
46960
47038
  tiles: propTiles,
@@ -46970,15 +47048,19 @@ function RoguelikeBoard({
46970
47048
  stairsX: propStairsX,
46971
47049
  stairsY: propStairsY,
46972
47050
  assetManifest: propAssetManifest,
46973
- scale = 0.45,
47051
+ scale = 0.25,
46974
47052
  unitScale = 1,
47053
+ spriteHeightRatio = 1.5,
47054
+ spriteMaxWidthRatio = 0.6,
46975
47055
  moveEvent,
46976
47056
  playAgainEvent,
46977
47057
  gameEndEvent,
46978
47058
  className
46979
47059
  }) {
46980
47060
  const board = boardEntity(entity) ?? {};
46981
- const tiles = propTiles ?? (Array.isArray(board.tiles) ? board.tiles : DEFAULT_TILES2);
47061
+ const resolvedStairsX = propStairsX ?? num(board.stairsX, 14);
47062
+ const resolvedStairsY = propStairsY ?? num(board.stairsY, 14);
47063
+ const tiles = propTiles ?? (Array.isArray(board.tiles) && board.tiles.length >= DUNGEON_GRID_W * DUNGEON_GRID_H ? board.tiles : buildDefaultDungeonTiles(resolvedStairsX, resolvedStairsY));
46982
47064
  const enemies = propEnemies ?? (Array.isArray(board.enemies) ? board.enemies : DEFAULT_ENEMIES);
46983
47065
  const items = propItems ?? (Array.isArray(board.items) ? board.items : DEFAULT_ITEMS);
46984
47066
  const player = propPlayer ?? board.player ?? { x: 1, y: 1 };
@@ -46988,8 +47070,8 @@ function RoguelikeBoard({
46988
47070
  const depth = propDepth ?? num(board.depth, 1);
46989
47071
  const result = propResult ?? (str(board.result) || "none");
46990
47072
  const phase = propPhase ?? (str(board.phase) || "player_turn");
46991
- propStairsX ?? num(board.stairsX, 3);
46992
- propStairsY ?? num(board.stairsY, 3);
47073
+ propStairsX ?? num(board.stairsX, 14);
47074
+ propStairsY ?? num(board.stairsY, 14);
46993
47075
  const eventBus = useEventBus();
46994
47076
  const { t } = hooks.useTranslate();
46995
47077
  const [hoveredTile, setHoveredTile] = React90.useState(null);
@@ -47119,7 +47201,9 @@ function RoguelikeBoard({
47119
47201
  scale,
47120
47202
  assetBaseUrl: propAssetManifest?.baseUrl ?? CDN4,
47121
47203
  assetManifest: propAssetManifest,
47122
- unitScale
47204
+ unitScale,
47205
+ spriteHeightRatio,
47206
+ spriteMaxWidthRatio
47123
47207
  }
47124
47208
  ) }),
47125
47209
  !isGameOver && /* @__PURE__ */ jsxRuntime.jsx(HStack, { className: "justify-center gap-2 p-3", gap: "none", children: [
@@ -47163,7 +47247,7 @@ function RoguelikeBoard({
47163
47247
  ] }) })
47164
47248
  ] });
47165
47249
  }
47166
- var CDN4, DEFAULT_TILES2, DEFAULT_ENEMIES, DEFAULT_ITEMS, FEATURE_SPRITE;
47250
+ var CDN4, DUNGEON_GRID_W, DUNGEON_GRID_H, DEFAULT_ENEMIES, DEFAULT_ITEMS, FEATURE_SPRITE;
47167
47251
  var init_RoguelikeBoard = __esm({
47168
47252
  "components/game/organisms/RoguelikeBoard.tsx"() {
47169
47253
  "use client";
@@ -47176,39 +47260,19 @@ var init_RoguelikeBoard = __esm({
47176
47260
  init_IsometricCanvas();
47177
47261
  init_boardEntity();
47178
47262
  CDN4 = "https://almadar-kflow-assets.web.app/shared";
47179
- DEFAULT_TILES2 = [
47180
- { x: 0, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
47181
- { x: 1, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
47182
- { x: 2, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
47183
- { x: 3, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
47184
- { x: 4, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
47185
- { x: 0, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
47186
- { x: 1, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
47187
- { x: 2, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
47188
- { x: 3, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
47189
- { x: 4, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
47190
- { x: 0, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
47191
- { x: 1, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png` },
47192
- { x: 2, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png` },
47193
- { x: 3, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
47194
- { x: 4, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
47195
- { x: 0, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
47196
- { x: 1, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
47197
- { x: 2, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png` },
47198
- { x: 3, y: 3, terrain: "stairs", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stairs_E.png` },
47199
- { x: 4, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
47200
- { x: 0, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
47201
- { x: 1, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
47202
- { x: 2, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
47203
- { x: 3, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
47204
- { x: 4, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` }
47205
- ];
47263
+ DUNGEON_GRID_W = 16;
47264
+ DUNGEON_GRID_H = 16;
47265
+ buildDefaultDungeonTiles(14, 14);
47206
47266
  DEFAULT_ENEMIES = [
47207
- { id: "e1", x: 3, y: 1, hp: 5, attack: 2 },
47208
- { id: "e2", x: 1, y: 3, hp: 4, attack: 1 }
47267
+ { id: "e1", x: 5, y: 2, hp: 5, attack: 2 },
47268
+ { id: "e2", x: 9, y: 6, hp: 4, attack: 1 },
47269
+ { id: "e3", x: 3, y: 10, hp: 6, attack: 2 },
47270
+ { id: "e4", x: 12, y: 8, hp: 5, attack: 3 }
47209
47271
  ];
47210
47272
  DEFAULT_ITEMS = [
47211
- { id: "i1", x: 2, y: 2, kind: "health_potion" }
47273
+ { id: "i1", x: 3, y: 3, kind: "health_potion" },
47274
+ { id: "i2", x: 7, y: 9, kind: "sword" },
47275
+ { id: "i3", x: 11, y: 5, kind: "shield" }
47212
47276
  ];
47213
47277
  FEATURE_SPRITE = {
47214
47278
  health_potion: `${CDN4}/isometric-dungeon/Isometric/chestClosed_E.png`,
@@ -51435,6 +51499,22 @@ var init_ToastSlot = __esm({
51435
51499
  ToastSlot.displayName = "ToastSlot";
51436
51500
  }
51437
51501
  });
51502
+ function buildDefaultTDTiles() {
51503
+ const pathSet = new Set(DEFAULT_TD_PATH.map((p2) => `${p2.x},${p2.y}`));
51504
+ const tiles = [];
51505
+ for (let y = 0; y < TD_GRID_H; y++) {
51506
+ for (let x = 0; x < TD_GRID_W; x++) {
51507
+ if (pathSet.has(`${x},${y}`)) {
51508
+ tiles.push({ x, y, terrain: "path", passable: false, terrainSprite: TERRAIN_SPRITES.path });
51509
+ } else {
51510
+ const variant = (x * 3 + y * 5 + (x ^ y)) % 3;
51511
+ const terrainKey = ["ground", "grass", "dirt"][variant];
51512
+ tiles.push({ x, y, terrain: terrainKey, passable: true, terrainSprite: TERRAIN_SPRITES[terrainKey] ?? TERRAIN_SPRITES.ground });
51513
+ }
51514
+ }
51515
+ }
51516
+ return tiles;
51517
+ }
51438
51518
  function tilesToIso(tiles) {
51439
51519
  return tiles.map((t) => ({
51440
51520
  x: t.x,
@@ -51490,7 +51570,10 @@ function TowerDefenseBoard({
51490
51570
  result: propResult,
51491
51571
  waveActive: propWaveActive,
51492
51572
  towerCost = 25,
51493
- scale = 0.45,
51573
+ scale = 0.25,
51574
+ unitScale = 1,
51575
+ spriteHeightRatio = 1.5,
51576
+ spriteMaxWidthRatio = 0.6,
51494
51577
  placeTowerEvent,
51495
51578
  startWaveEvent,
51496
51579
  playAgainEvent,
@@ -51500,8 +51583,10 @@ function TowerDefenseBoard({
51500
51583
  const board = boardEntity(entity) ?? {};
51501
51584
  const eventBus = useEventBus();
51502
51585
  const { t } = hooks.useTranslate();
51503
- const tiles = propTiles ?? rows(board.tiles);
51504
- const path = propPath ?? rows(board.path);
51586
+ const rawTiles = propTiles ?? rows(board.tiles);
51587
+ const tiles = rawTiles.length >= TD_GRID_W * TD_GRID_H ? rawTiles : DEFAULT_TD_TILES;
51588
+ const rawPath = propPath ?? rows(board.path);
51589
+ const path = rawPath.length > 0 ? rawPath : DEFAULT_TD_PATH;
51505
51590
  const towers = propTowers ?? rows(board.towers);
51506
51591
  const creeps = propCreeps ?? rows(board.creeps);
51507
51592
  const gold = propGold ?? num(board.gold, 100);
@@ -51598,7 +51683,10 @@ function TowerDefenseBoard({
51598
51683
  onTileClick: handleTileClick,
51599
51684
  onTileHover: (x, y) => setHoveredTile({ x, y }),
51600
51685
  onTileLeave: () => setHoveredTile(null),
51601
- scale
51686
+ scale,
51687
+ unitScale,
51688
+ spriteHeightRatio,
51689
+ spriteMaxWidthRatio
51602
51690
  }
51603
51691
  ),
51604
51692
  hoveredTile && canPlaceTower && validMoves.some((m) => m.x === hoveredTile.x && m.y === hoveredTile.y) && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "absolute bottom-4 left-1/2 -translate-x-1/2 z-10 bg-background/80 rounded px-3 py-1 backdrop-blur-sm pointer-events-none", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", children: t("td.placeTower") ?? `Place tower (${towerCost} gold)` }) })
@@ -51628,7 +51716,7 @@ function TowerDefenseBoard({
51628
51716
  ] }) })
51629
51717
  ] });
51630
51718
  }
51631
- var CDN6, TERRAIN_SPRITES, TOWER_SPRITE, CREEP_SPRITE;
51719
+ var CDN6, TD_GRID_W, TD_GRID_H, TERRAIN_SPRITES, DEFAULT_TD_PATH, DEFAULT_TD_TILES, TOWER_SPRITE, CREEP_SPRITE;
51632
51720
  var init_TowerDefenseBoard = __esm({
51633
51721
  "components/game/organisms/TowerDefenseBoard.tsx"() {
51634
51722
  "use client";
@@ -51641,11 +51729,67 @@ var init_TowerDefenseBoard = __esm({
51641
51729
  init_IsometricCanvas();
51642
51730
  init_boardEntity();
51643
51731
  CDN6 = "https://almadar-kflow-assets.web.app/shared/";
51732
+ TD_GRID_W = 16;
51733
+ TD_GRID_H = 16;
51644
51734
  TERRAIN_SPRITES = {
51645
51735
  ground: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_01.png`,
51646
51736
  path: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_09.png`,
51647
- wall: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_05.png`
51648
- };
51737
+ wall: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_05.png`,
51738
+ grass: `${CDN6}isometric-dungeon/Isometric/dirt_E.png`,
51739
+ dirt: `${CDN6}isometric-dungeon/Isometric/dirtTiles_E.png`
51740
+ };
51741
+ DEFAULT_TD_PATH = [
51742
+ { x: 2, y: 0 },
51743
+ { x: 2, y: 1 },
51744
+ { x: 2, y: 2 },
51745
+ { x: 2, y: 3 },
51746
+ { x: 3, y: 3 },
51747
+ { x: 4, y: 3 },
51748
+ { x: 5, y: 3 },
51749
+ { x: 6, y: 3 },
51750
+ { x: 7, y: 3 },
51751
+ { x: 8, y: 3 },
51752
+ { x: 9, y: 3 },
51753
+ { x: 10, y: 3 },
51754
+ { x: 11, y: 3 },
51755
+ { x: 12, y: 3 },
51756
+ { x: 13, y: 3 },
51757
+ { x: 13, y: 4 },
51758
+ { x: 13, y: 5 },
51759
+ { x: 13, y: 6 },
51760
+ { x: 13, y: 7 },
51761
+ { x: 12, y: 7 },
51762
+ { x: 11, y: 7 },
51763
+ { x: 10, y: 7 },
51764
+ { x: 9, y: 7 },
51765
+ { x: 8, y: 7 },
51766
+ { x: 7, y: 7 },
51767
+ { x: 6, y: 7 },
51768
+ { x: 5, y: 7 },
51769
+ { x: 4, y: 7 },
51770
+ { x: 3, y: 7 },
51771
+ { x: 2, y: 7 },
51772
+ { x: 2, y: 8 },
51773
+ { x: 2, y: 9 },
51774
+ { x: 2, y: 10 },
51775
+ { x: 2, y: 11 },
51776
+ { x: 3, y: 11 },
51777
+ { x: 4, y: 11 },
51778
+ { x: 5, y: 11 },
51779
+ { x: 6, y: 11 },
51780
+ { x: 7, y: 11 },
51781
+ { x: 8, y: 11 },
51782
+ { x: 9, y: 11 },
51783
+ { x: 10, y: 11 },
51784
+ { x: 11, y: 11 },
51785
+ { x: 12, y: 11 },
51786
+ { x: 13, y: 11 },
51787
+ { x: 13, y: 12 },
51788
+ { x: 13, y: 13 },
51789
+ { x: 13, y: 14 },
51790
+ { x: 13, y: 15 }
51791
+ ];
51792
+ DEFAULT_TD_TILES = buildDefaultTDTiles();
51649
51793
  TOWER_SPRITE = `${CDN6}units/guardian.png`;
51650
51794
  CREEP_SPRITE = `${CDN6}units/scrapper.png`;
51651
51795
  TowerDefenseBoard.displayName = "TowerDefenseBoard";
@@ -51922,6 +52066,17 @@ function heroPosition(h) {
51922
52066
  function heroMovement(h) {
51923
52067
  return num(h.movement);
51924
52068
  }
52069
+ function buildDefaultWorldTiles() {
52070
+ const tiles = [];
52071
+ for (let y = 0; y < WORLD_GRID_H; y++) {
52072
+ for (let x = 0; x < WORLD_GRID_W; x++) {
52073
+ const isBorder = x === 0 || y === 0 || x === WORLD_GRID_W - 1 || y === WORLD_GRID_H - 1;
52074
+ const def = isBorder ? WORLD_TERRAIN_DEFS[3] : WORLD_TERRAIN_DEFS[(x * 5 + y * 3 + (x ^ y)) % (WORLD_TERRAIN_DEFS.length - 1)];
52075
+ tiles.push({ x, y, terrain: def.terrain, terrainSprite: def.sprite, passable: def.passable });
52076
+ }
52077
+ }
52078
+ return tiles;
52079
+ }
51925
52080
  function defaultIsInRange(from, to, range) {
51926
52081
  return Math.abs(from.x - to.x) + Math.abs(from.y - to.y) <= range;
51927
52082
  }
@@ -51932,8 +52087,10 @@ function WorldMapBoard({
51932
52087
  features: propFeatures,
51933
52088
  assetManifest: propAssetManifest,
51934
52089
  isLoading,
51935
- scale = 0.4,
52090
+ scale = 0.25,
51936
52091
  unitScale = 2.5,
52092
+ spriteHeightRatio = 1.5,
52093
+ spriteMaxWidthRatio = 0.6,
51937
52094
  allowMoveAllHeroes = false,
51938
52095
  isInRange = defaultIsInRange,
51939
52096
  heroSelectEvent,
@@ -51974,7 +52131,8 @@ function WorldMapBoard({
51974
52131
  })),
51975
52132
  [entityTiles]
51976
52133
  );
51977
- const tiles = propTiles ?? derivedTiles;
52134
+ const rawTiles = propTiles ?? (derivedTiles.length > 0 ? derivedTiles : null);
52135
+ const tiles = rawTiles != null && rawTiles.length >= WORLD_GRID_W * WORLD_GRID_H ? rawTiles : DEFAULT_WORLD_TILES;
51978
52136
  const baseUnits = React90.useMemo(
51979
52137
  () => propUnits ?? entityUnits.map((u) => ({
51980
52138
  id: str(u.id),
@@ -52171,6 +52329,8 @@ function WorldMapBoard({
52171
52329
  effectSpriteUrls,
52172
52330
  resolveUnitFrame,
52173
52331
  unitScale,
52332
+ spriteHeightRatio,
52333
+ spriteMaxWidthRatio,
52174
52334
  diamondTopY,
52175
52335
  enableCamera
52176
52336
  }
@@ -52182,6 +52342,7 @@ function WorldMapBoard({
52182
52342
  footer && footer(ctx)
52183
52343
  ] });
52184
52344
  }
52345
+ var WORLD_CDN, WORLD_GRID_W, WORLD_GRID_H, WORLD_TERRAIN_DEFS, DEFAULT_WORLD_TILES;
52185
52346
  var init_WorldMapBoard = __esm({
52186
52347
  "components/game/organisms/WorldMapBoard.tsx"() {
52187
52348
  "use client";
@@ -52192,6 +52353,17 @@ var init_WorldMapBoard = __esm({
52192
52353
  init_IsometricCanvas();
52193
52354
  init_boardEntity();
52194
52355
  init_isometric();
52356
+ WORLD_CDN = "https://almadar-kflow-assets.web.app/shared";
52357
+ WORLD_GRID_W = 16;
52358
+ WORLD_GRID_H = 16;
52359
+ WORLD_TERRAIN_DEFS = [
52360
+ { terrain: "grass", sprite: `${WORLD_CDN}/isometric-dungeon/Isometric/dirt_E.png`, passable: true },
52361
+ { terrain: "dirt", sprite: `${WORLD_CDN}/isometric-dungeon/Isometric/dirtTiles_E.png`, passable: true },
52362
+ { terrain: "forest", sprite: `${WORLD_CDN}/isometric-dungeon/Isometric/planks_E.png`, passable: true },
52363
+ { terrain: "stone", sprite: `${WORLD_CDN}/isometric-dungeon/Isometric/stoneInset_E.png`, passable: false },
52364
+ { terrain: "castle", sprite: `${WORLD_CDN}/isometric-dungeon/Isometric/stoneTile_E.png`, passable: true }
52365
+ ];
52366
+ DEFAULT_WORLD_TILES = buildDefaultWorldTiles();
52195
52367
  WorldMapBoard.displayName = "WorldMapBoard";
52196
52368
  }
52197
52369
  });
@@ -57857,7 +58029,10 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
57857
58029
  if (binding.config) {
57858
58030
  guardCtx.config = binding.config;
57859
58031
  }
57860
- const passed = runtime.interpolateValue(tick.guard, runtime.createContextFromBindings(guardCtx));
58032
+ const passed = evaluator.evaluateGuard(
58033
+ tick.guard,
58034
+ runtime.createContextFromBindings(guardCtx)
58035
+ );
57861
58036
  if (!passed) {
57862
58037
  tickLog.debug("guard-blocked", { traitName, tick: tick.name, state: currentState });
57863
58038
  return;