@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.
@@ -9728,6 +9728,8 @@ function IsometricCanvas({
9728
9728
  showMinimap = true,
9729
9729
  enableCamera = true,
9730
9730
  unitScale = 1,
9731
+ spriteHeightRatio = 1.5,
9732
+ spriteMaxWidthRatio = 0.6,
9731
9733
  // Asset resolution
9732
9734
  getTerrainSprite,
9733
9735
  getFeatureSprite,
@@ -10119,8 +10121,8 @@ function IsometricCanvas({
10119
10121
  const breatheOffset = 0.8 * scale * (1 + Math.sin(animTime * 2e-3 + (unit.position.x * 3.7 + unit.position.y * 5.3)));
10120
10122
  const unitSpriteUrl = resolveUnitSpriteUrl(unit);
10121
10123
  const img = unitSpriteUrl ? getImage(unitSpriteUrl) : null;
10122
- const unitDrawH = scaledFloorHeight * 1.5 * unitScale;
10123
- const maxUnitW = scaledTileWidth * 0.6 * unitScale;
10124
+ const unitDrawH = scaledFloorHeight * spriteHeightRatio * unitScale;
10125
+ const maxUnitW = scaledTileWidth * spriteMaxWidthRatio * unitScale;
10124
10126
  const ar = img ? img.naturalWidth / img.naturalHeight : 0.5;
10125
10127
  let drawH = unitDrawH;
10126
10128
  let drawW = unitDrawH * ar;
@@ -10496,14 +10498,32 @@ var init_boardEntity = __esm({
10496
10498
  "components/game/organisms/boardEntity.ts"() {
10497
10499
  }
10498
10500
  });
10501
+ function buildDefaultBattleTiles() {
10502
+ const tiles = [];
10503
+ for (let y = 0; y < BATTLE_GRID_H; y++) {
10504
+ for (let x = 0; x < BATTLE_GRID_W; x++) {
10505
+ const variant = (x * 3 + y * 7 + (x ^ y)) % BATTLE_TERRAIN_SPRITES.length;
10506
+ tiles.push({
10507
+ x,
10508
+ y,
10509
+ terrain: ["grass", "dirt", "planks", "stone", "stone"][variant],
10510
+ terrainSprite: BATTLE_TERRAIN_SPRITES[variant],
10511
+ passable: true
10512
+ });
10513
+ }
10514
+ }
10515
+ return tiles;
10516
+ }
10499
10517
  function BattleBoard({
10500
10518
  entity,
10501
10519
  tiles: propTiles,
10502
10520
  units: propUnits,
10503
10521
  features: propFeatures,
10504
10522
  assetManifest: propAssetManifest,
10505
- scale = 0.45,
10523
+ scale = 0.25,
10506
10524
  unitScale = 1,
10525
+ spriteHeightRatio = 1.5,
10526
+ spriteMaxWidthRatio = 0.6,
10507
10527
  header,
10508
10528
  sidebar,
10509
10529
  actions,
@@ -10527,10 +10547,11 @@ function BattleBoard({
10527
10547
  className
10528
10548
  }) {
10529
10549
  const board = boardEntity(entity) ?? {};
10530
- const tiles = propTiles ?? (Array.isArray(board.tiles) ? board.tiles : []);
10550
+ const rawTiles = propTiles ?? (Array.isArray(board.tiles) ? board.tiles : []);
10551
+ const tiles = rawTiles.length === 0 ? DEFAULT_BATTLE_TILES : rawTiles;
10531
10552
  const features = propFeatures ?? (Array.isArray(board.features) ? board.features : []);
10532
- const boardWidth = num(board.gridWidth ?? board.boardWidth, 8);
10533
- const boardHeight = num(board.gridHeight ?? board.boardHeight, 6);
10553
+ const boardWidth = num(board.gridWidth ?? board.boardWidth, BATTLE_GRID_W);
10554
+ const boardHeight = num(board.gridHeight ?? board.boardHeight, BATTLE_GRID_H);
10534
10555
  const assetManifest = propAssetManifest ?? board.assetManifest;
10535
10556
  const backgroundImage = board.backgroundImage;
10536
10557
  const units = rows(board.units);
@@ -10794,7 +10815,9 @@ function BattleBoard({
10794
10815
  hasActiveEffects: hasActiveEffects2,
10795
10816
  effectSpriteUrls,
10796
10817
  resolveUnitFrame,
10797
- unitScale
10818
+ unitScale,
10819
+ spriteHeightRatio,
10820
+ spriteMaxWidthRatio
10798
10821
  }
10799
10822
  ),
10800
10823
  overlay && overlay(ctx)
@@ -10850,6 +10873,7 @@ function BattleBoard({
10850
10873
  ] }) }))
10851
10874
  ] });
10852
10875
  }
10876
+ var BATTLE_CDN, BATTLE_GRID_W, BATTLE_GRID_H, BATTLE_TERRAIN_SPRITES, DEFAULT_BATTLE_TILES;
10853
10877
  var init_BattleBoard = __esm({
10854
10878
  "components/game/organisms/BattleBoard.tsx"() {
10855
10879
  "use client";
@@ -10862,6 +10886,17 @@ var init_BattleBoard = __esm({
10862
10886
  init_IsometricCanvas();
10863
10887
  init_boardEntity();
10864
10888
  init_isometric();
10889
+ BATTLE_CDN = "https://almadar-kflow-assets.web.app/shared";
10890
+ BATTLE_GRID_W = 16;
10891
+ BATTLE_GRID_H = 16;
10892
+ BATTLE_TERRAIN_SPRITES = [
10893
+ `${BATTLE_CDN}/isometric-dungeon/Isometric/dirt_E.png`,
10894
+ `${BATTLE_CDN}/isometric-dungeon/Isometric/dirtTiles_E.png`,
10895
+ `${BATTLE_CDN}/isometric-dungeon/Isometric/planks_E.png`,
10896
+ `${BATTLE_CDN}/isometric-dungeon/Isometric/stoneTile_E.png`,
10897
+ `${BATTLE_CDN}/isometric-dungeon/Isometric/stoneInset_E.png`
10898
+ ];
10899
+ DEFAULT_BATTLE_TILES = buildDefaultBattleTiles();
10865
10900
  BattleBoard.displayName = "BattleBoard";
10866
10901
  }
10867
10902
  });
@@ -10869,7 +10904,7 @@ function BattleTemplate({
10869
10904
  entity,
10870
10905
  scale = 0.45,
10871
10906
  unitScale = 1,
10872
- tiles = DEFAULT_BATTLE_TILES,
10907
+ tiles = DEFAULT_BATTLE_TILES2,
10873
10908
  units = DEFAULT_BATTLE_UNITS,
10874
10909
  features = DEFAULT_BATTLE_FEATURES,
10875
10910
  assetManifest = DEFAULT_BATTLE_MANIFEST,
@@ -10898,12 +10933,12 @@ function BattleTemplate({
10898
10933
  }
10899
10934
  );
10900
10935
  }
10901
- var CDN, DEFAULT_BATTLE_TILES, DEFAULT_BATTLE_UNITS, DEFAULT_BATTLE_FEATURES, DEFAULT_BATTLE_MANIFEST;
10936
+ var CDN, DEFAULT_BATTLE_TILES2, DEFAULT_BATTLE_UNITS, DEFAULT_BATTLE_FEATURES, DEFAULT_BATTLE_MANIFEST;
10902
10937
  var init_BattleTemplate = __esm({
10903
10938
  "components/game/templates/BattleTemplate.tsx"() {
10904
10939
  init_BattleBoard();
10905
10940
  CDN = "https://almadar-kflow-assets.web.app/shared";
10906
- DEFAULT_BATTLE_TILES = [
10941
+ DEFAULT_BATTLE_TILES2 = [
10907
10942
  { x: 0, y: 0, terrain: "stone", passable: false, terrainSprite: `${CDN}/isometric-blocks/PNG/Abstract tiles/abstractTile_07.png` },
10908
10943
  { x: 1, y: 0, terrain: "stone", passable: false, terrainSprite: `${CDN}/isometric-blocks/PNG/Abstract tiles/abstractTile_07.png` },
10909
10944
  { x: 2, y: 0, terrain: "stone", passable: false, terrainSprite: `${CDN}/isometric-blocks/PNG/Abstract tiles/abstractTile_07.png` },
@@ -42880,6 +42915,7 @@ var init_GameCanvas3D2 = __esm({
42880
42915
  attackTargets = [],
42881
42916
  selectedTileIds = [],
42882
42917
  selectedUnitId = null,
42918
+ unitScale = 1,
42883
42919
  children
42884
42920
  }, ref) => {
42885
42921
  const containerRef = useRef(null);
@@ -43096,12 +43132,18 @@ var init_GameCanvas3D2 = __esm({
43096
43132
  },
43097
43133
  [selectedTileIds, hoveredTile, validMoves, attackTargets, handleTileClick, handleTileHover]
43098
43134
  );
43135
+ const UNIT_BASE_MODEL_SCALE = 0.5;
43136
+ const UNIT_BASE_BILLBOARD_HEIGHT = 1.2;
43137
+ const UNIT_BASE_PRIMITIVE_RADIUS = 0.3;
43099
43138
  const DefaultUnitRenderer = useCallback(
43100
43139
  ({ unit, position }) => {
43101
43140
  const isSelected = selectedUnitId === unit.id;
43102
43141
  const color = unit.faction === "player" ? 4491519 : unit.faction === "enemy" ? 16729156 : 16777028;
43103
43142
  const hasAtlas = unitAtlasUrl(unit) !== null;
43104
43143
  const initialFrame = hasAtlas ? resolveUnitFrame(unit.id) : null;
43144
+ const modelScale = UNIT_BASE_MODEL_SCALE * unitScale;
43145
+ const billboardHeight = UNIT_BASE_BILLBOARD_HEIGHT * unitScale;
43146
+ const primitiveRadius = UNIT_BASE_PRIMITIVE_RADIUS * unitScale;
43105
43147
  return /* @__PURE__ */ jsxs(
43106
43148
  "group",
43107
43149
  {
@@ -43119,7 +43161,8 @@ var init_GameCanvas3D2 = __esm({
43119
43161
  UnitSpriteBillboard,
43120
43162
  {
43121
43163
  sheetUrl: initialFrame.sheetUrl,
43122
- resolveFrame: () => resolveUnitFrame(unit.id)
43164
+ resolveFrame: () => resolveUnitFrame(unit.id),
43165
+ height: billboardHeight
43123
43166
  }
43124
43167
  ) })
43125
43168
  ) : unit.modelUrl ? (
@@ -43128,22 +43171,22 @@ var init_GameCanvas3D2 = __esm({
43128
43171
  ModelLoader,
43129
43172
  {
43130
43173
  url: unit.modelUrl,
43131
- scale: 0.5,
43174
+ scale: modelScale,
43132
43175
  fallbackGeometry: "box",
43133
43176
  castShadow: true
43134
43177
  }
43135
43178
  )
43136
43179
  ) : /* @__PURE__ */ jsxs(Fragment, { children: [
43137
- /* @__PURE__ */ jsxs("mesh", { position: [0, 0.3, 0], children: [
43138
- /* @__PURE__ */ jsx("cylinderGeometry", { args: [0.3, 0.3, 0.1, 8] }),
43180
+ /* @__PURE__ */ jsxs("mesh", { position: [0, primitiveRadius, 0], children: [
43181
+ /* @__PURE__ */ jsx("cylinderGeometry", { args: [primitiveRadius, primitiveRadius, primitiveRadius * 0.33, 8] }),
43139
43182
  /* @__PURE__ */ jsx("meshStandardMaterial", { color })
43140
43183
  ] }),
43141
- /* @__PURE__ */ jsxs("mesh", { position: [0, 0.6, 0], children: [
43142
- /* @__PURE__ */ jsx("capsuleGeometry", { args: [0.2, 0.4, 4, 8] }),
43184
+ /* @__PURE__ */ jsxs("mesh", { position: [0, primitiveRadius * 2, 0], children: [
43185
+ /* @__PURE__ */ jsx("capsuleGeometry", { args: [primitiveRadius * 0.67, primitiveRadius * 1.33, 4, 8] }),
43143
43186
  /* @__PURE__ */ jsx("meshStandardMaterial", { color })
43144
43187
  ] }),
43145
- /* @__PURE__ */ jsxs("mesh", { position: [0, 0.9, 0], children: [
43146
- /* @__PURE__ */ jsx("sphereGeometry", { args: [0.12, 8, 8] }),
43188
+ /* @__PURE__ */ jsxs("mesh", { position: [0, primitiveRadius * 3, 0], children: [
43189
+ /* @__PURE__ */ jsx("sphereGeometry", { args: [primitiveRadius * 0.4, 8, 8] }),
43147
43190
  /* @__PURE__ */ jsx("meshStandardMaterial", { color })
43148
43191
  ] })
43149
43192
  ] }),
@@ -43176,7 +43219,7 @@ var init_GameCanvas3D2 = __esm({
43176
43219
  }
43177
43220
  );
43178
43221
  },
43179
- [selectedUnitId, handleUnitClick, resolveUnitFrame]
43222
+ [selectedUnitId, handleUnitClick, resolveUnitFrame, unitScale]
43180
43223
  );
43181
43224
  const DefaultFeatureRenderer = useCallback(
43182
43225
  ({
@@ -43319,33 +43362,35 @@ var init_GameCanvas3D2 = __esm({
43319
43362
  fadeStrength: 1
43320
43363
  }
43321
43364
  ),
43322
- tiles.map((tile, index) => {
43323
- const position = gridToWorld(
43324
- tile.x,
43325
- tile.z ?? tile.y ?? 0,
43326
- tile.elevation ?? 0
43327
- );
43328
- const Renderer = CustomTileRenderer || DefaultTileRenderer;
43329
- return /* @__PURE__ */ jsx(Renderer, { tile, position }, tile.id ?? `tile-${index}`);
43330
- }),
43331
- features.map((feature, index) => {
43332
- const position = gridToWorld(
43333
- feature.x,
43334
- feature.z ?? feature.y ?? 0,
43335
- (feature.elevation ?? 0) + 0.5
43336
- );
43337
- const Renderer = CustomFeatureRenderer || DefaultFeatureRenderer;
43338
- return /* @__PURE__ */ jsx(Renderer, { feature, position }, feature.id ?? `feature-${index}`);
43339
- }),
43340
- units.map((unit) => {
43341
- const position = gridToWorld(
43342
- unit.x ?? 0,
43343
- unit.z ?? unit.y ?? 0,
43344
- (unit.elevation ?? 0) + 0.5
43345
- );
43346
- const Renderer = CustomUnitRenderer || DefaultUnitRenderer;
43347
- return /* @__PURE__ */ jsx(Renderer, { unit, position }, unit.id);
43348
- }),
43365
+ /* @__PURE__ */ jsxs("group", { children: [
43366
+ tiles.map((tile, index) => {
43367
+ const position = gridToWorld(
43368
+ tile.x,
43369
+ tile.z ?? tile.y ?? 0,
43370
+ tile.elevation ?? 0
43371
+ );
43372
+ const Renderer = CustomTileRenderer || DefaultTileRenderer;
43373
+ return /* @__PURE__ */ jsx(Renderer, { tile, position }, tile.id ?? `tile-${index}`);
43374
+ }),
43375
+ features.map((feature, index) => {
43376
+ const position = gridToWorld(
43377
+ feature.x,
43378
+ feature.z ?? feature.y ?? 0,
43379
+ (feature.elevation ?? 0) + 0.5
43380
+ );
43381
+ const Renderer = CustomFeatureRenderer || DefaultFeatureRenderer;
43382
+ return /* @__PURE__ */ jsx(Renderer, { feature, position }, feature.id ?? `feature-${index}`);
43383
+ }),
43384
+ units.map((unit) => {
43385
+ const position = gridToWorld(
43386
+ unit.x ?? 0,
43387
+ unit.z ?? unit.y ?? 0,
43388
+ (unit.elevation ?? 0) + 0.5
43389
+ );
43390
+ const Renderer = CustomUnitRenderer || DefaultUnitRenderer;
43391
+ return /* @__PURE__ */ jsx(Renderer, { unit, position }, unit.id);
43392
+ })
43393
+ ] }),
43349
43394
  children,
43350
43395
  /* @__PURE__ */ jsx(
43351
43396
  OrbitControls,
@@ -43388,6 +43433,8 @@ function GameBoard3D({
43388
43433
  features = [],
43389
43434
  cameraMode = "perspective",
43390
43435
  backgroundColor = "#2a1a1a",
43436
+ unitScale = 1,
43437
+ scale = 0.45,
43391
43438
  tileClickEvent,
43392
43439
  unitClickEvent,
43393
43440
  attackEvent,
@@ -43488,6 +43535,8 @@ function GameBoard3D({
43488
43535
  selectedUnitId,
43489
43536
  validMoves,
43490
43537
  attackTargets,
43538
+ unitScale,
43539
+ scale,
43491
43540
  className: "game-board-3d__canvas w-full min-h-[85vh]"
43492
43541
  }
43493
43542
  ),
@@ -45630,6 +45679,35 @@ var init_ResourceCounter = __esm({
45630
45679
  ResourceCounter.displayName = "ResourceCounter";
45631
45680
  }
45632
45681
  });
45682
+ function dungeonTerrain(x, y, stairsX, stairsY) {
45683
+ const isBorder = x === 0 || y === 0 || x === DUNGEON_GRID_W - 1 || y === DUNGEON_GRID_H - 1;
45684
+ const isPillar = x % 4 === 0 && y % 4 === 0 && !isBorder;
45685
+ const isWall = isBorder || isPillar;
45686
+ if (x === stairsX && y === stairsY) {
45687
+ return { terrain: "stairs", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stairs_E.png` };
45688
+ }
45689
+ if (isWall) {
45690
+ return { terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` };
45691
+ }
45692
+ const variant = (x * 3 + y * 7) % 5;
45693
+ const sprites = [
45694
+ `${CDN4}/isometric-dungeon/Isometric/dirt_E.png`,
45695
+ `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png`,
45696
+ `${CDN4}/isometric-dungeon/Isometric/planks_E.png`,
45697
+ `${CDN4}/isometric-dungeon/Isometric/stoneTile_E.png`,
45698
+ `${CDN4}/isometric-dungeon/Isometric/dirt_E.png`
45699
+ ];
45700
+ return { terrain: "floor", passable: true, terrainSprite: sprites[variant] };
45701
+ }
45702
+ function buildDefaultDungeonTiles(stairsX, stairsY) {
45703
+ const tiles = [];
45704
+ for (let y = 0; y < DUNGEON_GRID_H; y++) {
45705
+ for (let x = 0; x < DUNGEON_GRID_W; x++) {
45706
+ tiles.push({ x, y, ...dungeonTerrain(x, y, stairsX, stairsY) });
45707
+ }
45708
+ }
45709
+ return tiles;
45710
+ }
45633
45711
  function RoguelikeBoard({
45634
45712
  entity,
45635
45713
  tiles: propTiles,
@@ -45645,15 +45723,19 @@ function RoguelikeBoard({
45645
45723
  stairsX: propStairsX,
45646
45724
  stairsY: propStairsY,
45647
45725
  assetManifest: propAssetManifest,
45648
- scale = 0.45,
45726
+ scale = 0.25,
45649
45727
  unitScale = 1,
45728
+ spriteHeightRatio = 1.5,
45729
+ spriteMaxWidthRatio = 0.6,
45650
45730
  moveEvent,
45651
45731
  playAgainEvent,
45652
45732
  gameEndEvent,
45653
45733
  className
45654
45734
  }) {
45655
45735
  const board = boardEntity(entity) ?? {};
45656
- const tiles = propTiles ?? (Array.isArray(board.tiles) ? board.tiles : DEFAULT_TILES2);
45736
+ const resolvedStairsX = propStairsX ?? num(board.stairsX, 14);
45737
+ const resolvedStairsY = propStairsY ?? num(board.stairsY, 14);
45738
+ const tiles = propTiles ?? (Array.isArray(board.tiles) && board.tiles.length >= DUNGEON_GRID_W * DUNGEON_GRID_H ? board.tiles : buildDefaultDungeonTiles(resolvedStairsX, resolvedStairsY));
45657
45739
  const enemies = propEnemies ?? (Array.isArray(board.enemies) ? board.enemies : DEFAULT_ENEMIES);
45658
45740
  const items = propItems ?? (Array.isArray(board.items) ? board.items : DEFAULT_ITEMS);
45659
45741
  const player = propPlayer ?? board.player ?? { x: 1, y: 1 };
@@ -45663,8 +45745,8 @@ function RoguelikeBoard({
45663
45745
  const depth = propDepth ?? num(board.depth, 1);
45664
45746
  const result = propResult ?? (str(board.result) || "none");
45665
45747
  const phase = propPhase ?? (str(board.phase) || "player_turn");
45666
- propStairsX ?? num(board.stairsX, 3);
45667
- propStairsY ?? num(board.stairsY, 3);
45748
+ propStairsX ?? num(board.stairsX, 14);
45749
+ propStairsY ?? num(board.stairsY, 14);
45668
45750
  const eventBus = useEventBus();
45669
45751
  const { t } = useTranslate();
45670
45752
  const [hoveredTile, setHoveredTile] = useState(null);
@@ -45794,7 +45876,9 @@ function RoguelikeBoard({
45794
45876
  scale,
45795
45877
  assetBaseUrl: propAssetManifest?.baseUrl ?? CDN4,
45796
45878
  assetManifest: propAssetManifest,
45797
- unitScale
45879
+ unitScale,
45880
+ spriteHeightRatio,
45881
+ spriteMaxWidthRatio
45798
45882
  }
45799
45883
  ) }),
45800
45884
  !isGameOver && /* @__PURE__ */ jsx(HStack, { className: "justify-center gap-2 p-3", gap: "none", children: [
@@ -45838,7 +45922,7 @@ function RoguelikeBoard({
45838
45922
  ] }) })
45839
45923
  ] });
45840
45924
  }
45841
- var CDN4, DEFAULT_TILES2, DEFAULT_ENEMIES, DEFAULT_ITEMS, FEATURE_SPRITE;
45925
+ var CDN4, DUNGEON_GRID_W, DUNGEON_GRID_H, DEFAULT_ENEMIES, DEFAULT_ITEMS, FEATURE_SPRITE;
45842
45926
  var init_RoguelikeBoard = __esm({
45843
45927
  "components/game/organisms/RoguelikeBoard.tsx"() {
45844
45928
  "use client";
@@ -45851,39 +45935,19 @@ var init_RoguelikeBoard = __esm({
45851
45935
  init_IsometricCanvas();
45852
45936
  init_boardEntity();
45853
45937
  CDN4 = "https://almadar-kflow-assets.web.app/shared";
45854
- DEFAULT_TILES2 = [
45855
- { x: 0, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45856
- { x: 1, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45857
- { x: 2, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45858
- { x: 3, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45859
- { x: 4, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45860
- { x: 0, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45861
- { x: 1, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
45862
- { x: 2, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
45863
- { x: 3, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
45864
- { x: 4, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45865
- { x: 0, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45866
- { x: 1, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png` },
45867
- { x: 2, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png` },
45868
- { x: 3, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
45869
- { x: 4, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45870
- { x: 0, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45871
- { x: 1, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
45872
- { x: 2, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png` },
45873
- { x: 3, y: 3, terrain: "stairs", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stairs_E.png` },
45874
- { x: 4, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45875
- { x: 0, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45876
- { x: 1, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45877
- { x: 2, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45878
- { x: 3, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45879
- { x: 4, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` }
45880
- ];
45938
+ DUNGEON_GRID_W = 16;
45939
+ DUNGEON_GRID_H = 16;
45940
+ buildDefaultDungeonTiles(14, 14);
45881
45941
  DEFAULT_ENEMIES = [
45882
- { id: "e1", x: 3, y: 1, hp: 5, attack: 2 },
45883
- { id: "e2", x: 1, y: 3, hp: 4, attack: 1 }
45942
+ { id: "e1", x: 5, y: 2, hp: 5, attack: 2 },
45943
+ { id: "e2", x: 9, y: 6, hp: 4, attack: 1 },
45944
+ { id: "e3", x: 3, y: 10, hp: 6, attack: 2 },
45945
+ { id: "e4", x: 12, y: 8, hp: 5, attack: 3 }
45884
45946
  ];
45885
45947
  DEFAULT_ITEMS = [
45886
- { id: "i1", x: 2, y: 2, kind: "health_potion" }
45948
+ { id: "i1", x: 3, y: 3, kind: "health_potion" },
45949
+ { id: "i2", x: 7, y: 9, kind: "sword" },
45950
+ { id: "i3", x: 11, y: 5, kind: "shield" }
45887
45951
  ];
45888
45952
  FEATURE_SPRITE = {
45889
45953
  health_potion: `${CDN4}/isometric-dungeon/Isometric/chestClosed_E.png`,
@@ -50332,6 +50396,22 @@ var init_ToastSlot = __esm({
50332
50396
  ToastSlot.displayName = "ToastSlot";
50333
50397
  }
50334
50398
  });
50399
+ function buildDefaultTDTiles() {
50400
+ const pathSet = new Set(DEFAULT_TD_PATH.map((p2) => `${p2.x},${p2.y}`));
50401
+ const tiles = [];
50402
+ for (let y = 0; y < TD_GRID_H; y++) {
50403
+ for (let x = 0; x < TD_GRID_W; x++) {
50404
+ if (pathSet.has(`${x},${y}`)) {
50405
+ tiles.push({ x, y, terrain: "path", passable: false, terrainSprite: TERRAIN_SPRITES.path });
50406
+ } else {
50407
+ const variant = (x * 3 + y * 5 + (x ^ y)) % 3;
50408
+ const terrainKey = ["ground", "grass", "dirt"][variant];
50409
+ tiles.push({ x, y, terrain: terrainKey, passable: true, terrainSprite: TERRAIN_SPRITES[terrainKey] ?? TERRAIN_SPRITES.ground });
50410
+ }
50411
+ }
50412
+ }
50413
+ return tiles;
50414
+ }
50335
50415
  function tilesToIso(tiles) {
50336
50416
  return tiles.map((t) => ({
50337
50417
  x: t.x,
@@ -50387,7 +50467,10 @@ function TowerDefenseBoard({
50387
50467
  result: propResult,
50388
50468
  waveActive: propWaveActive,
50389
50469
  towerCost = 25,
50390
- scale = 0.45,
50470
+ scale = 0.25,
50471
+ unitScale = 1,
50472
+ spriteHeightRatio = 1.5,
50473
+ spriteMaxWidthRatio = 0.6,
50391
50474
  placeTowerEvent,
50392
50475
  startWaveEvent,
50393
50476
  playAgainEvent,
@@ -50397,8 +50480,10 @@ function TowerDefenseBoard({
50397
50480
  const board = boardEntity(entity) ?? {};
50398
50481
  const eventBus = useEventBus();
50399
50482
  const { t } = useTranslate();
50400
- const tiles = propTiles ?? rows(board.tiles);
50401
- const path = propPath ?? rows(board.path);
50483
+ const rawTiles = propTiles ?? rows(board.tiles);
50484
+ const tiles = rawTiles.length >= TD_GRID_W * TD_GRID_H ? rawTiles : DEFAULT_TD_TILES;
50485
+ const rawPath = propPath ?? rows(board.path);
50486
+ const path = rawPath.length > 0 ? rawPath : DEFAULT_TD_PATH;
50402
50487
  const towers = propTowers ?? rows(board.towers);
50403
50488
  const creeps = propCreeps ?? rows(board.creeps);
50404
50489
  const gold = propGold ?? num(board.gold, 100);
@@ -50495,7 +50580,10 @@ function TowerDefenseBoard({
50495
50580
  onTileClick: handleTileClick,
50496
50581
  onTileHover: (x, y) => setHoveredTile({ x, y }),
50497
50582
  onTileLeave: () => setHoveredTile(null),
50498
- scale
50583
+ scale,
50584
+ unitScale,
50585
+ spriteHeightRatio,
50586
+ spriteMaxWidthRatio
50499
50587
  }
50500
50588
  ),
50501
50589
  hoveredTile && canPlaceTower && validMoves.some((m) => m.x === hoveredTile.x && m.y === hoveredTile.y) && /* @__PURE__ */ 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__ */ jsx(Typography, { variant: "small", children: t("td.placeTower") ?? `Place tower (${towerCost} gold)` }) })
@@ -50525,7 +50613,7 @@ function TowerDefenseBoard({
50525
50613
  ] }) })
50526
50614
  ] });
50527
50615
  }
50528
- var CDN6, TERRAIN_SPRITES, TOWER_SPRITE, CREEP_SPRITE;
50616
+ var CDN6, TD_GRID_W, TD_GRID_H, TERRAIN_SPRITES, DEFAULT_TD_PATH, DEFAULT_TD_TILES, TOWER_SPRITE, CREEP_SPRITE;
50529
50617
  var init_TowerDefenseBoard = __esm({
50530
50618
  "components/game/organisms/TowerDefenseBoard.tsx"() {
50531
50619
  "use client";
@@ -50538,11 +50626,67 @@ var init_TowerDefenseBoard = __esm({
50538
50626
  init_IsometricCanvas();
50539
50627
  init_boardEntity();
50540
50628
  CDN6 = "https://almadar-kflow-assets.web.app/shared/";
50629
+ TD_GRID_W = 16;
50630
+ TD_GRID_H = 16;
50541
50631
  TERRAIN_SPRITES = {
50542
50632
  ground: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_01.png`,
50543
50633
  path: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_09.png`,
50544
- wall: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_05.png`
50545
- };
50634
+ wall: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_05.png`,
50635
+ grass: `${CDN6}isometric-dungeon/Isometric/dirt_E.png`,
50636
+ dirt: `${CDN6}isometric-dungeon/Isometric/dirtTiles_E.png`
50637
+ };
50638
+ DEFAULT_TD_PATH = [
50639
+ { x: 2, y: 0 },
50640
+ { x: 2, y: 1 },
50641
+ { x: 2, y: 2 },
50642
+ { x: 2, y: 3 },
50643
+ { x: 3, y: 3 },
50644
+ { x: 4, y: 3 },
50645
+ { x: 5, y: 3 },
50646
+ { x: 6, y: 3 },
50647
+ { x: 7, y: 3 },
50648
+ { x: 8, y: 3 },
50649
+ { x: 9, y: 3 },
50650
+ { x: 10, y: 3 },
50651
+ { x: 11, y: 3 },
50652
+ { x: 12, y: 3 },
50653
+ { x: 13, y: 3 },
50654
+ { x: 13, y: 4 },
50655
+ { x: 13, y: 5 },
50656
+ { x: 13, y: 6 },
50657
+ { x: 13, y: 7 },
50658
+ { x: 12, y: 7 },
50659
+ { x: 11, y: 7 },
50660
+ { x: 10, y: 7 },
50661
+ { x: 9, y: 7 },
50662
+ { x: 8, y: 7 },
50663
+ { x: 7, y: 7 },
50664
+ { x: 6, y: 7 },
50665
+ { x: 5, y: 7 },
50666
+ { x: 4, y: 7 },
50667
+ { x: 3, y: 7 },
50668
+ { x: 2, y: 7 },
50669
+ { x: 2, y: 8 },
50670
+ { x: 2, y: 9 },
50671
+ { x: 2, y: 10 },
50672
+ { x: 2, y: 11 },
50673
+ { x: 3, y: 11 },
50674
+ { x: 4, y: 11 },
50675
+ { x: 5, y: 11 },
50676
+ { x: 6, y: 11 },
50677
+ { x: 7, y: 11 },
50678
+ { x: 8, y: 11 },
50679
+ { x: 9, y: 11 },
50680
+ { x: 10, y: 11 },
50681
+ { x: 11, y: 11 },
50682
+ { x: 12, y: 11 },
50683
+ { x: 13, y: 11 },
50684
+ { x: 13, y: 12 },
50685
+ { x: 13, y: 13 },
50686
+ { x: 13, y: 14 },
50687
+ { x: 13, y: 15 }
50688
+ ];
50689
+ DEFAULT_TD_TILES = buildDefaultTDTiles();
50546
50690
  TOWER_SPRITE = `${CDN6}units/guardian.png`;
50547
50691
  CREEP_SPRITE = `${CDN6}units/scrapper.png`;
50548
50692
  TowerDefenseBoard.displayName = "TowerDefenseBoard";
@@ -50819,6 +50963,17 @@ function heroPosition(h) {
50819
50963
  function heroMovement(h) {
50820
50964
  return num(h.movement);
50821
50965
  }
50966
+ function buildDefaultWorldTiles() {
50967
+ const tiles = [];
50968
+ for (let y = 0; y < WORLD_GRID_H; y++) {
50969
+ for (let x = 0; x < WORLD_GRID_W; x++) {
50970
+ const isBorder = x === 0 || y === 0 || x === WORLD_GRID_W - 1 || y === WORLD_GRID_H - 1;
50971
+ const def = isBorder ? WORLD_TERRAIN_DEFS[3] : WORLD_TERRAIN_DEFS[(x * 5 + y * 3 + (x ^ y)) % (WORLD_TERRAIN_DEFS.length - 1)];
50972
+ tiles.push({ x, y, terrain: def.terrain, terrainSprite: def.sprite, passable: def.passable });
50973
+ }
50974
+ }
50975
+ return tiles;
50976
+ }
50822
50977
  function defaultIsInRange(from, to, range) {
50823
50978
  return Math.abs(from.x - to.x) + Math.abs(from.y - to.y) <= range;
50824
50979
  }
@@ -50829,8 +50984,10 @@ function WorldMapBoard({
50829
50984
  features: propFeatures,
50830
50985
  assetManifest: propAssetManifest,
50831
50986
  isLoading,
50832
- scale = 0.4,
50987
+ scale = 0.25,
50833
50988
  unitScale = 2.5,
50989
+ spriteHeightRatio = 1.5,
50990
+ spriteMaxWidthRatio = 0.6,
50834
50991
  allowMoveAllHeroes = false,
50835
50992
  isInRange = defaultIsInRange,
50836
50993
  heroSelectEvent,
@@ -50871,7 +51028,8 @@ function WorldMapBoard({
50871
51028
  })),
50872
51029
  [entityTiles]
50873
51030
  );
50874
- const tiles = propTiles ?? derivedTiles;
51031
+ const rawTiles = propTiles ?? (derivedTiles.length > 0 ? derivedTiles : null);
51032
+ const tiles = rawTiles != null && rawTiles.length >= WORLD_GRID_W * WORLD_GRID_H ? rawTiles : DEFAULT_WORLD_TILES;
50875
51033
  const baseUnits = useMemo(
50876
51034
  () => propUnits ?? entityUnits.map((u) => ({
50877
51035
  id: str(u.id),
@@ -51068,6 +51226,8 @@ function WorldMapBoard({
51068
51226
  effectSpriteUrls,
51069
51227
  resolveUnitFrame,
51070
51228
  unitScale,
51229
+ spriteHeightRatio,
51230
+ spriteMaxWidthRatio,
51071
51231
  diamondTopY,
51072
51232
  enableCamera
51073
51233
  }
@@ -51079,6 +51239,7 @@ function WorldMapBoard({
51079
51239
  footer && footer(ctx)
51080
51240
  ] });
51081
51241
  }
51242
+ var WORLD_CDN, WORLD_GRID_W, WORLD_GRID_H, WORLD_TERRAIN_DEFS, DEFAULT_WORLD_TILES;
51082
51243
  var init_WorldMapBoard = __esm({
51083
51244
  "components/game/organisms/WorldMapBoard.tsx"() {
51084
51245
  "use client";
@@ -51089,6 +51250,17 @@ var init_WorldMapBoard = __esm({
51089
51250
  init_IsometricCanvas();
51090
51251
  init_boardEntity();
51091
51252
  init_isometric();
51253
+ WORLD_CDN = "https://almadar-kflow-assets.web.app/shared";
51254
+ WORLD_GRID_W = 16;
51255
+ WORLD_GRID_H = 16;
51256
+ WORLD_TERRAIN_DEFS = [
51257
+ { terrain: "grass", sprite: `${WORLD_CDN}/isometric-dungeon/Isometric/dirt_E.png`, passable: true },
51258
+ { terrain: "dirt", sprite: `${WORLD_CDN}/isometric-dungeon/Isometric/dirtTiles_E.png`, passable: true },
51259
+ { terrain: "forest", sprite: `${WORLD_CDN}/isometric-dungeon/Isometric/planks_E.png`, passable: true },
51260
+ { terrain: "stone", sprite: `${WORLD_CDN}/isometric-dungeon/Isometric/stoneInset_E.png`, passable: false },
51261
+ { terrain: "castle", sprite: `${WORLD_CDN}/isometric-dungeon/Isometric/stoneTile_E.png`, passable: true }
51262
+ ];
51263
+ DEFAULT_WORLD_TILES = buildDefaultWorldTiles();
51092
51264
  WorldMapBoard.displayName = "WorldMapBoard";
51093
51265
  }
51094
51266
  });