@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.
@@ -9774,6 +9774,8 @@ function IsometricCanvas({
9774
9774
  showMinimap = true,
9775
9775
  enableCamera = true,
9776
9776
  unitScale = 1,
9777
+ spriteHeightRatio = 1.5,
9778
+ spriteMaxWidthRatio = 0.6,
9777
9779
  // Asset resolution
9778
9780
  getTerrainSprite,
9779
9781
  getFeatureSprite,
@@ -10165,8 +10167,8 @@ function IsometricCanvas({
10165
10167
  const breatheOffset = 0.8 * scale * (1 + Math.sin(animTime * 2e-3 + (unit.position.x * 3.7 + unit.position.y * 5.3)));
10166
10168
  const unitSpriteUrl = resolveUnitSpriteUrl(unit);
10167
10169
  const img = unitSpriteUrl ? getImage(unitSpriteUrl) : null;
10168
- const unitDrawH = scaledFloorHeight * 1.5 * unitScale;
10169
- const maxUnitW = scaledTileWidth * 0.6 * unitScale;
10170
+ const unitDrawH = scaledFloorHeight * spriteHeightRatio * unitScale;
10171
+ const maxUnitW = scaledTileWidth * spriteMaxWidthRatio * unitScale;
10170
10172
  const ar = img ? img.naturalWidth / img.naturalHeight : 0.5;
10171
10173
  let drawH = unitDrawH;
10172
10174
  let drawW = unitDrawH * ar;
@@ -10542,14 +10544,32 @@ var init_boardEntity = __esm({
10542
10544
  "components/game/organisms/boardEntity.ts"() {
10543
10545
  }
10544
10546
  });
10547
+ function buildDefaultBattleTiles() {
10548
+ const tiles = [];
10549
+ for (let y = 0; y < BATTLE_GRID_H; y++) {
10550
+ for (let x = 0; x < BATTLE_GRID_W; x++) {
10551
+ const variant = (x * 3 + y * 7 + (x ^ y)) % BATTLE_TERRAIN_SPRITES.length;
10552
+ tiles.push({
10553
+ x,
10554
+ y,
10555
+ terrain: ["grass", "dirt", "planks", "stone", "stone"][variant],
10556
+ terrainSprite: BATTLE_TERRAIN_SPRITES[variant],
10557
+ passable: true
10558
+ });
10559
+ }
10560
+ }
10561
+ return tiles;
10562
+ }
10545
10563
  function BattleBoard({
10546
10564
  entity,
10547
10565
  tiles: propTiles,
10548
10566
  units: propUnits,
10549
10567
  features: propFeatures,
10550
10568
  assetManifest: propAssetManifest,
10551
- scale = 0.45,
10569
+ scale = 0.25,
10552
10570
  unitScale = 1,
10571
+ spriteHeightRatio = 1.5,
10572
+ spriteMaxWidthRatio = 0.6,
10553
10573
  header,
10554
10574
  sidebar,
10555
10575
  actions,
@@ -10573,10 +10593,11 @@ function BattleBoard({
10573
10593
  className
10574
10594
  }) {
10575
10595
  const board = boardEntity(entity) ?? {};
10576
- const tiles = propTiles ?? (Array.isArray(board.tiles) ? board.tiles : []);
10596
+ const rawTiles = propTiles ?? (Array.isArray(board.tiles) ? board.tiles : []);
10597
+ const tiles = rawTiles.length === 0 ? DEFAULT_BATTLE_TILES : rawTiles;
10577
10598
  const features = propFeatures ?? (Array.isArray(board.features) ? board.features : []);
10578
- const boardWidth = num(board.gridWidth ?? board.boardWidth, 8);
10579
- const boardHeight = num(board.gridHeight ?? board.boardHeight, 6);
10599
+ const boardWidth = num(board.gridWidth ?? board.boardWidth, BATTLE_GRID_W);
10600
+ const boardHeight = num(board.gridHeight ?? board.boardHeight, BATTLE_GRID_H);
10580
10601
  const assetManifest = propAssetManifest ?? board.assetManifest;
10581
10602
  const backgroundImage = board.backgroundImage;
10582
10603
  const units = rows(board.units);
@@ -10840,7 +10861,9 @@ function BattleBoard({
10840
10861
  hasActiveEffects: hasActiveEffects2,
10841
10862
  effectSpriteUrls,
10842
10863
  resolveUnitFrame,
10843
- unitScale
10864
+ unitScale,
10865
+ spriteHeightRatio,
10866
+ spriteMaxWidthRatio
10844
10867
  }
10845
10868
  ),
10846
10869
  overlay && overlay(ctx)
@@ -10896,6 +10919,7 @@ function BattleBoard({
10896
10919
  ] }) }))
10897
10920
  ] });
10898
10921
  }
10922
+ var BATTLE_CDN, BATTLE_GRID_W, BATTLE_GRID_H, BATTLE_TERRAIN_SPRITES, DEFAULT_BATTLE_TILES;
10899
10923
  var init_BattleBoard = __esm({
10900
10924
  "components/game/organisms/BattleBoard.tsx"() {
10901
10925
  "use client";
@@ -10908,6 +10932,17 @@ var init_BattleBoard = __esm({
10908
10932
  init_IsometricCanvas();
10909
10933
  init_boardEntity();
10910
10934
  init_isometric();
10935
+ BATTLE_CDN = "https://almadar-kflow-assets.web.app/shared";
10936
+ BATTLE_GRID_W = 16;
10937
+ BATTLE_GRID_H = 16;
10938
+ BATTLE_TERRAIN_SPRITES = [
10939
+ `${BATTLE_CDN}/isometric-dungeon/Isometric/dirt_E.png`,
10940
+ `${BATTLE_CDN}/isometric-dungeon/Isometric/dirtTiles_E.png`,
10941
+ `${BATTLE_CDN}/isometric-dungeon/Isometric/planks_E.png`,
10942
+ `${BATTLE_CDN}/isometric-dungeon/Isometric/stoneTile_E.png`,
10943
+ `${BATTLE_CDN}/isometric-dungeon/Isometric/stoneInset_E.png`
10944
+ ];
10945
+ DEFAULT_BATTLE_TILES = buildDefaultBattleTiles();
10911
10946
  BattleBoard.displayName = "BattleBoard";
10912
10947
  }
10913
10948
  });
@@ -10915,7 +10950,7 @@ function BattleTemplate({
10915
10950
  entity,
10916
10951
  scale = 0.45,
10917
10952
  unitScale = 1,
10918
- tiles = DEFAULT_BATTLE_TILES,
10953
+ tiles = DEFAULT_BATTLE_TILES2,
10919
10954
  units = DEFAULT_BATTLE_UNITS,
10920
10955
  features = DEFAULT_BATTLE_FEATURES,
10921
10956
  assetManifest = DEFAULT_BATTLE_MANIFEST,
@@ -10944,12 +10979,12 @@ function BattleTemplate({
10944
10979
  }
10945
10980
  );
10946
10981
  }
10947
- var CDN, DEFAULT_BATTLE_TILES, DEFAULT_BATTLE_UNITS, DEFAULT_BATTLE_FEATURES, DEFAULT_BATTLE_MANIFEST;
10982
+ var CDN, DEFAULT_BATTLE_TILES2, DEFAULT_BATTLE_UNITS, DEFAULT_BATTLE_FEATURES, DEFAULT_BATTLE_MANIFEST;
10948
10983
  var init_BattleTemplate = __esm({
10949
10984
  "components/game/templates/BattleTemplate.tsx"() {
10950
10985
  init_BattleBoard();
10951
10986
  CDN = "https://almadar-kflow-assets.web.app/shared";
10952
- DEFAULT_BATTLE_TILES = [
10987
+ DEFAULT_BATTLE_TILES2 = [
10953
10988
  { x: 0, y: 0, terrain: "stone", passable: false, terrainSprite: `${CDN}/isometric-blocks/PNG/Abstract tiles/abstractTile_07.png` },
10954
10989
  { x: 1, y: 0, terrain: "stone", passable: false, terrainSprite: `${CDN}/isometric-blocks/PNG/Abstract tiles/abstractTile_07.png` },
10955
10990
  { x: 2, y: 0, terrain: "stone", passable: false, terrainSprite: `${CDN}/isometric-blocks/PNG/Abstract tiles/abstractTile_07.png` },
@@ -42926,6 +42961,7 @@ var init_GameCanvas3D2 = __esm({
42926
42961
  attackTargets = [],
42927
42962
  selectedTileIds = [],
42928
42963
  selectedUnitId = null,
42964
+ unitScale = 1,
42929
42965
  children
42930
42966
  }, ref) => {
42931
42967
  const containerRef = React76.useRef(null);
@@ -43142,12 +43178,18 @@ var init_GameCanvas3D2 = __esm({
43142
43178
  },
43143
43179
  [selectedTileIds, hoveredTile, validMoves, attackTargets, handleTileClick, handleTileHover]
43144
43180
  );
43181
+ const UNIT_BASE_MODEL_SCALE = 0.5;
43182
+ const UNIT_BASE_BILLBOARD_HEIGHT = 1.2;
43183
+ const UNIT_BASE_PRIMITIVE_RADIUS = 0.3;
43145
43184
  const DefaultUnitRenderer = React76.useCallback(
43146
43185
  ({ unit, position }) => {
43147
43186
  const isSelected = selectedUnitId === unit.id;
43148
43187
  const color = unit.faction === "player" ? 4491519 : unit.faction === "enemy" ? 16729156 : 16777028;
43149
43188
  const hasAtlas = unitAtlasUrl(unit) !== null;
43150
43189
  const initialFrame = hasAtlas ? resolveUnitFrame(unit.id) : null;
43190
+ const modelScale = UNIT_BASE_MODEL_SCALE * unitScale;
43191
+ const billboardHeight = UNIT_BASE_BILLBOARD_HEIGHT * unitScale;
43192
+ const primitiveRadius = UNIT_BASE_PRIMITIVE_RADIUS * unitScale;
43151
43193
  return /* @__PURE__ */ jsxRuntime.jsxs(
43152
43194
  "group",
43153
43195
  {
@@ -43165,7 +43207,8 @@ var init_GameCanvas3D2 = __esm({
43165
43207
  UnitSpriteBillboard,
43166
43208
  {
43167
43209
  sheetUrl: initialFrame.sheetUrl,
43168
- resolveFrame: () => resolveUnitFrame(unit.id)
43210
+ resolveFrame: () => resolveUnitFrame(unit.id),
43211
+ height: billboardHeight
43169
43212
  }
43170
43213
  ) })
43171
43214
  ) : unit.modelUrl ? (
@@ -43174,22 +43217,22 @@ var init_GameCanvas3D2 = __esm({
43174
43217
  ModelLoader,
43175
43218
  {
43176
43219
  url: unit.modelUrl,
43177
- scale: 0.5,
43220
+ scale: modelScale,
43178
43221
  fallbackGeometry: "box",
43179
43222
  castShadow: true
43180
43223
  }
43181
43224
  )
43182
43225
  ) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
43183
- /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.3, 0], children: [
43184
- /* @__PURE__ */ jsxRuntime.jsx("cylinderGeometry", { args: [0.3, 0.3, 0.1, 8] }),
43226
+ /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, primitiveRadius, 0], children: [
43227
+ /* @__PURE__ */ jsxRuntime.jsx("cylinderGeometry", { args: [primitiveRadius, primitiveRadius, primitiveRadius * 0.33, 8] }),
43185
43228
  /* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color })
43186
43229
  ] }),
43187
- /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.6, 0], children: [
43188
- /* @__PURE__ */ jsxRuntime.jsx("capsuleGeometry", { args: [0.2, 0.4, 4, 8] }),
43230
+ /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, primitiveRadius * 2, 0], children: [
43231
+ /* @__PURE__ */ jsxRuntime.jsx("capsuleGeometry", { args: [primitiveRadius * 0.67, primitiveRadius * 1.33, 4, 8] }),
43189
43232
  /* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color })
43190
43233
  ] }),
43191
- /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.9, 0], children: [
43192
- /* @__PURE__ */ jsxRuntime.jsx("sphereGeometry", { args: [0.12, 8, 8] }),
43234
+ /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, primitiveRadius * 3, 0], children: [
43235
+ /* @__PURE__ */ jsxRuntime.jsx("sphereGeometry", { args: [primitiveRadius * 0.4, 8, 8] }),
43193
43236
  /* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color })
43194
43237
  ] })
43195
43238
  ] }),
@@ -43222,7 +43265,7 @@ var init_GameCanvas3D2 = __esm({
43222
43265
  }
43223
43266
  );
43224
43267
  },
43225
- [selectedUnitId, handleUnitClick, resolveUnitFrame]
43268
+ [selectedUnitId, handleUnitClick, resolveUnitFrame, unitScale]
43226
43269
  );
43227
43270
  const DefaultFeatureRenderer = React76.useCallback(
43228
43271
  ({
@@ -43365,33 +43408,35 @@ var init_GameCanvas3D2 = __esm({
43365
43408
  fadeStrength: 1
43366
43409
  }
43367
43410
  ),
43368
- tiles.map((tile, index) => {
43369
- const position = gridToWorld(
43370
- tile.x,
43371
- tile.z ?? tile.y ?? 0,
43372
- tile.elevation ?? 0
43373
- );
43374
- const Renderer = CustomTileRenderer || DefaultTileRenderer;
43375
- return /* @__PURE__ */ jsxRuntime.jsx(Renderer, { tile, position }, tile.id ?? `tile-${index}`);
43376
- }),
43377
- features.map((feature, index) => {
43378
- const position = gridToWorld(
43379
- feature.x,
43380
- feature.z ?? feature.y ?? 0,
43381
- (feature.elevation ?? 0) + 0.5
43382
- );
43383
- const Renderer = CustomFeatureRenderer || DefaultFeatureRenderer;
43384
- return /* @__PURE__ */ jsxRuntime.jsx(Renderer, { feature, position }, feature.id ?? `feature-${index}`);
43385
- }),
43386
- units.map((unit) => {
43387
- const position = gridToWorld(
43388
- unit.x ?? 0,
43389
- unit.z ?? unit.y ?? 0,
43390
- (unit.elevation ?? 0) + 0.5
43391
- );
43392
- const Renderer = CustomUnitRenderer || DefaultUnitRenderer;
43393
- return /* @__PURE__ */ jsxRuntime.jsx(Renderer, { unit, position }, unit.id);
43394
- }),
43411
+ /* @__PURE__ */ jsxRuntime.jsxs("group", { children: [
43412
+ tiles.map((tile, index) => {
43413
+ const position = gridToWorld(
43414
+ tile.x,
43415
+ tile.z ?? tile.y ?? 0,
43416
+ tile.elevation ?? 0
43417
+ );
43418
+ const Renderer = CustomTileRenderer || DefaultTileRenderer;
43419
+ return /* @__PURE__ */ jsxRuntime.jsx(Renderer, { tile, position }, tile.id ?? `tile-${index}`);
43420
+ }),
43421
+ features.map((feature, index) => {
43422
+ const position = gridToWorld(
43423
+ feature.x,
43424
+ feature.z ?? feature.y ?? 0,
43425
+ (feature.elevation ?? 0) + 0.5
43426
+ );
43427
+ const Renderer = CustomFeatureRenderer || DefaultFeatureRenderer;
43428
+ return /* @__PURE__ */ jsxRuntime.jsx(Renderer, { feature, position }, feature.id ?? `feature-${index}`);
43429
+ }),
43430
+ units.map((unit) => {
43431
+ const position = gridToWorld(
43432
+ unit.x ?? 0,
43433
+ unit.z ?? unit.y ?? 0,
43434
+ (unit.elevation ?? 0) + 0.5
43435
+ );
43436
+ const Renderer = CustomUnitRenderer || DefaultUnitRenderer;
43437
+ return /* @__PURE__ */ jsxRuntime.jsx(Renderer, { unit, position }, unit.id);
43438
+ })
43439
+ ] }),
43395
43440
  children,
43396
43441
  /* @__PURE__ */ jsxRuntime.jsx(
43397
43442
  drei.OrbitControls,
@@ -43434,6 +43479,8 @@ function GameBoard3D({
43434
43479
  features = [],
43435
43480
  cameraMode = "perspective",
43436
43481
  backgroundColor = "#2a1a1a",
43482
+ unitScale = 1,
43483
+ scale = 0.45,
43437
43484
  tileClickEvent,
43438
43485
  unitClickEvent,
43439
43486
  attackEvent,
@@ -43534,6 +43581,8 @@ function GameBoard3D({
43534
43581
  selectedUnitId,
43535
43582
  validMoves,
43536
43583
  attackTargets,
43584
+ unitScale,
43585
+ scale,
43537
43586
  className: "game-board-3d__canvas w-full min-h-[85vh]"
43538
43587
  }
43539
43588
  ),
@@ -45676,6 +45725,35 @@ var init_ResourceCounter = __esm({
45676
45725
  ResourceCounter.displayName = "ResourceCounter";
45677
45726
  }
45678
45727
  });
45728
+ function dungeonTerrain(x, y, stairsX, stairsY) {
45729
+ const isBorder = x === 0 || y === 0 || x === DUNGEON_GRID_W - 1 || y === DUNGEON_GRID_H - 1;
45730
+ const isPillar = x % 4 === 0 && y % 4 === 0 && !isBorder;
45731
+ const isWall = isBorder || isPillar;
45732
+ if (x === stairsX && y === stairsY) {
45733
+ return { terrain: "stairs", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stairs_E.png` };
45734
+ }
45735
+ if (isWall) {
45736
+ return { terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` };
45737
+ }
45738
+ const variant = (x * 3 + y * 7) % 5;
45739
+ const sprites = [
45740
+ `${CDN4}/isometric-dungeon/Isometric/dirt_E.png`,
45741
+ `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png`,
45742
+ `${CDN4}/isometric-dungeon/Isometric/planks_E.png`,
45743
+ `${CDN4}/isometric-dungeon/Isometric/stoneTile_E.png`,
45744
+ `${CDN4}/isometric-dungeon/Isometric/dirt_E.png`
45745
+ ];
45746
+ return { terrain: "floor", passable: true, terrainSprite: sprites[variant] };
45747
+ }
45748
+ function buildDefaultDungeonTiles(stairsX, stairsY) {
45749
+ const tiles = [];
45750
+ for (let y = 0; y < DUNGEON_GRID_H; y++) {
45751
+ for (let x = 0; x < DUNGEON_GRID_W; x++) {
45752
+ tiles.push({ x, y, ...dungeonTerrain(x, y, stairsX, stairsY) });
45753
+ }
45754
+ }
45755
+ return tiles;
45756
+ }
45679
45757
  function RoguelikeBoard({
45680
45758
  entity,
45681
45759
  tiles: propTiles,
@@ -45691,15 +45769,19 @@ function RoguelikeBoard({
45691
45769
  stairsX: propStairsX,
45692
45770
  stairsY: propStairsY,
45693
45771
  assetManifest: propAssetManifest,
45694
- scale = 0.45,
45772
+ scale = 0.25,
45695
45773
  unitScale = 1,
45774
+ spriteHeightRatio = 1.5,
45775
+ spriteMaxWidthRatio = 0.6,
45696
45776
  moveEvent,
45697
45777
  playAgainEvent,
45698
45778
  gameEndEvent,
45699
45779
  className
45700
45780
  }) {
45701
45781
  const board = boardEntity(entity) ?? {};
45702
- const tiles = propTiles ?? (Array.isArray(board.tiles) ? board.tiles : DEFAULT_TILES2);
45782
+ const resolvedStairsX = propStairsX ?? num(board.stairsX, 14);
45783
+ const resolvedStairsY = propStairsY ?? num(board.stairsY, 14);
45784
+ const tiles = propTiles ?? (Array.isArray(board.tiles) && board.tiles.length >= DUNGEON_GRID_W * DUNGEON_GRID_H ? board.tiles : buildDefaultDungeonTiles(resolvedStairsX, resolvedStairsY));
45703
45785
  const enemies = propEnemies ?? (Array.isArray(board.enemies) ? board.enemies : DEFAULT_ENEMIES);
45704
45786
  const items = propItems ?? (Array.isArray(board.items) ? board.items : DEFAULT_ITEMS);
45705
45787
  const player = propPlayer ?? board.player ?? { x: 1, y: 1 };
@@ -45709,8 +45791,8 @@ function RoguelikeBoard({
45709
45791
  const depth = propDepth ?? num(board.depth, 1);
45710
45792
  const result = propResult ?? (str(board.result) || "none");
45711
45793
  const phase = propPhase ?? (str(board.phase) || "player_turn");
45712
- propStairsX ?? num(board.stairsX, 3);
45713
- propStairsY ?? num(board.stairsY, 3);
45794
+ propStairsX ?? num(board.stairsX, 14);
45795
+ propStairsY ?? num(board.stairsY, 14);
45714
45796
  const eventBus = useEventBus();
45715
45797
  const { t } = hooks.useTranslate();
45716
45798
  const [hoveredTile, setHoveredTile] = React76.useState(null);
@@ -45840,7 +45922,9 @@ function RoguelikeBoard({
45840
45922
  scale,
45841
45923
  assetBaseUrl: propAssetManifest?.baseUrl ?? CDN4,
45842
45924
  assetManifest: propAssetManifest,
45843
- unitScale
45925
+ unitScale,
45926
+ spriteHeightRatio,
45927
+ spriteMaxWidthRatio
45844
45928
  }
45845
45929
  ) }),
45846
45930
  !isGameOver && /* @__PURE__ */ jsxRuntime.jsx(exports.HStack, { className: "justify-center gap-2 p-3", gap: "none", children: [
@@ -45884,7 +45968,7 @@ function RoguelikeBoard({
45884
45968
  ] }) })
45885
45969
  ] });
45886
45970
  }
45887
- var CDN4, DEFAULT_TILES2, DEFAULT_ENEMIES, DEFAULT_ITEMS, FEATURE_SPRITE;
45971
+ var CDN4, DUNGEON_GRID_W, DUNGEON_GRID_H, DEFAULT_ENEMIES, DEFAULT_ITEMS, FEATURE_SPRITE;
45888
45972
  var init_RoguelikeBoard = __esm({
45889
45973
  "components/game/organisms/RoguelikeBoard.tsx"() {
45890
45974
  "use client";
@@ -45897,39 +45981,19 @@ var init_RoguelikeBoard = __esm({
45897
45981
  init_IsometricCanvas();
45898
45982
  init_boardEntity();
45899
45983
  CDN4 = "https://almadar-kflow-assets.web.app/shared";
45900
- DEFAULT_TILES2 = [
45901
- { x: 0, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45902
- { x: 1, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45903
- { x: 2, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45904
- { x: 3, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45905
- { x: 4, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45906
- { x: 0, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45907
- { x: 1, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
45908
- { x: 2, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
45909
- { x: 3, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
45910
- { x: 4, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45911
- { x: 0, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45912
- { x: 1, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png` },
45913
- { x: 2, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png` },
45914
- { x: 3, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
45915
- { x: 4, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45916
- { x: 0, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45917
- { x: 1, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
45918
- { x: 2, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png` },
45919
- { x: 3, y: 3, terrain: "stairs", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stairs_E.png` },
45920
- { x: 4, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45921
- { x: 0, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45922
- { x: 1, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45923
- { x: 2, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45924
- { x: 3, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45925
- { x: 4, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` }
45926
- ];
45984
+ DUNGEON_GRID_W = 16;
45985
+ DUNGEON_GRID_H = 16;
45986
+ buildDefaultDungeonTiles(14, 14);
45927
45987
  DEFAULT_ENEMIES = [
45928
- { id: "e1", x: 3, y: 1, hp: 5, attack: 2 },
45929
- { id: "e2", x: 1, y: 3, hp: 4, attack: 1 }
45988
+ { id: "e1", x: 5, y: 2, hp: 5, attack: 2 },
45989
+ { id: "e2", x: 9, y: 6, hp: 4, attack: 1 },
45990
+ { id: "e3", x: 3, y: 10, hp: 6, attack: 2 },
45991
+ { id: "e4", x: 12, y: 8, hp: 5, attack: 3 }
45930
45992
  ];
45931
45993
  DEFAULT_ITEMS = [
45932
- { id: "i1", x: 2, y: 2, kind: "health_potion" }
45994
+ { id: "i1", x: 3, y: 3, kind: "health_potion" },
45995
+ { id: "i2", x: 7, y: 9, kind: "sword" },
45996
+ { id: "i3", x: 11, y: 5, kind: "shield" }
45933
45997
  ];
45934
45998
  FEATURE_SPRITE = {
45935
45999
  health_potion: `${CDN4}/isometric-dungeon/Isometric/chestClosed_E.png`,
@@ -50378,6 +50442,22 @@ var init_ToastSlot = __esm({
50378
50442
  exports.ToastSlot.displayName = "ToastSlot";
50379
50443
  }
50380
50444
  });
50445
+ function buildDefaultTDTiles() {
50446
+ const pathSet = new Set(DEFAULT_TD_PATH.map((p2) => `${p2.x},${p2.y}`));
50447
+ const tiles = [];
50448
+ for (let y = 0; y < TD_GRID_H; y++) {
50449
+ for (let x = 0; x < TD_GRID_W; x++) {
50450
+ if (pathSet.has(`${x},${y}`)) {
50451
+ tiles.push({ x, y, terrain: "path", passable: false, terrainSprite: TERRAIN_SPRITES.path });
50452
+ } else {
50453
+ const variant = (x * 3 + y * 5 + (x ^ y)) % 3;
50454
+ const terrainKey = ["ground", "grass", "dirt"][variant];
50455
+ tiles.push({ x, y, terrain: terrainKey, passable: true, terrainSprite: TERRAIN_SPRITES[terrainKey] ?? TERRAIN_SPRITES.ground });
50456
+ }
50457
+ }
50458
+ }
50459
+ return tiles;
50460
+ }
50381
50461
  function tilesToIso(tiles) {
50382
50462
  return tiles.map((t) => ({
50383
50463
  x: t.x,
@@ -50433,7 +50513,10 @@ function TowerDefenseBoard({
50433
50513
  result: propResult,
50434
50514
  waveActive: propWaveActive,
50435
50515
  towerCost = 25,
50436
- scale = 0.45,
50516
+ scale = 0.25,
50517
+ unitScale = 1,
50518
+ spriteHeightRatio = 1.5,
50519
+ spriteMaxWidthRatio = 0.6,
50437
50520
  placeTowerEvent,
50438
50521
  startWaveEvent,
50439
50522
  playAgainEvent,
@@ -50443,8 +50526,10 @@ function TowerDefenseBoard({
50443
50526
  const board = boardEntity(entity) ?? {};
50444
50527
  const eventBus = useEventBus();
50445
50528
  const { t } = hooks.useTranslate();
50446
- const tiles = propTiles ?? rows(board.tiles);
50447
- const path = propPath ?? rows(board.path);
50529
+ const rawTiles = propTiles ?? rows(board.tiles);
50530
+ const tiles = rawTiles.length >= TD_GRID_W * TD_GRID_H ? rawTiles : DEFAULT_TD_TILES;
50531
+ const rawPath = propPath ?? rows(board.path);
50532
+ const path = rawPath.length > 0 ? rawPath : DEFAULT_TD_PATH;
50448
50533
  const towers = propTowers ?? rows(board.towers);
50449
50534
  const creeps = propCreeps ?? rows(board.creeps);
50450
50535
  const gold = propGold ?? num(board.gold, 100);
@@ -50541,7 +50626,10 @@ function TowerDefenseBoard({
50541
50626
  onTileClick: handleTileClick,
50542
50627
  onTileHover: (x, y) => setHoveredTile({ x, y }),
50543
50628
  onTileLeave: () => setHoveredTile(null),
50544
- scale
50629
+ scale,
50630
+ unitScale,
50631
+ spriteHeightRatio,
50632
+ spriteMaxWidthRatio
50545
50633
  }
50546
50634
  ),
50547
50635
  hoveredTile && canPlaceTower && validMoves.some((m) => m.x === hoveredTile.x && m.y === hoveredTile.y) && /* @__PURE__ */ jsxRuntime.jsx(exports.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(exports.Typography, { variant: "small", children: t("td.placeTower") ?? `Place tower (${towerCost} gold)` }) })
@@ -50571,7 +50659,7 @@ function TowerDefenseBoard({
50571
50659
  ] }) })
50572
50660
  ] });
50573
50661
  }
50574
- var CDN6, TERRAIN_SPRITES, TOWER_SPRITE, CREEP_SPRITE;
50662
+ var CDN6, TD_GRID_W, TD_GRID_H, TERRAIN_SPRITES, DEFAULT_TD_PATH, DEFAULT_TD_TILES, TOWER_SPRITE, CREEP_SPRITE;
50575
50663
  var init_TowerDefenseBoard = __esm({
50576
50664
  "components/game/organisms/TowerDefenseBoard.tsx"() {
50577
50665
  "use client";
@@ -50584,11 +50672,67 @@ var init_TowerDefenseBoard = __esm({
50584
50672
  init_IsometricCanvas();
50585
50673
  init_boardEntity();
50586
50674
  CDN6 = "https://almadar-kflow-assets.web.app/shared/";
50675
+ TD_GRID_W = 16;
50676
+ TD_GRID_H = 16;
50587
50677
  TERRAIN_SPRITES = {
50588
50678
  ground: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_01.png`,
50589
50679
  path: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_09.png`,
50590
- wall: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_05.png`
50591
- };
50680
+ wall: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_05.png`,
50681
+ grass: `${CDN6}isometric-dungeon/Isometric/dirt_E.png`,
50682
+ dirt: `${CDN6}isometric-dungeon/Isometric/dirtTiles_E.png`
50683
+ };
50684
+ DEFAULT_TD_PATH = [
50685
+ { x: 2, y: 0 },
50686
+ { x: 2, y: 1 },
50687
+ { x: 2, y: 2 },
50688
+ { x: 2, y: 3 },
50689
+ { x: 3, y: 3 },
50690
+ { x: 4, y: 3 },
50691
+ { x: 5, y: 3 },
50692
+ { x: 6, y: 3 },
50693
+ { x: 7, y: 3 },
50694
+ { x: 8, y: 3 },
50695
+ { x: 9, y: 3 },
50696
+ { x: 10, y: 3 },
50697
+ { x: 11, y: 3 },
50698
+ { x: 12, y: 3 },
50699
+ { x: 13, y: 3 },
50700
+ { x: 13, y: 4 },
50701
+ { x: 13, y: 5 },
50702
+ { x: 13, y: 6 },
50703
+ { x: 13, y: 7 },
50704
+ { x: 12, y: 7 },
50705
+ { x: 11, y: 7 },
50706
+ { x: 10, y: 7 },
50707
+ { x: 9, y: 7 },
50708
+ { x: 8, y: 7 },
50709
+ { x: 7, y: 7 },
50710
+ { x: 6, y: 7 },
50711
+ { x: 5, y: 7 },
50712
+ { x: 4, y: 7 },
50713
+ { x: 3, y: 7 },
50714
+ { x: 2, y: 7 },
50715
+ { x: 2, y: 8 },
50716
+ { x: 2, y: 9 },
50717
+ { x: 2, y: 10 },
50718
+ { x: 2, y: 11 },
50719
+ { x: 3, y: 11 },
50720
+ { x: 4, y: 11 },
50721
+ { x: 5, y: 11 },
50722
+ { x: 6, y: 11 },
50723
+ { x: 7, y: 11 },
50724
+ { x: 8, y: 11 },
50725
+ { x: 9, y: 11 },
50726
+ { x: 10, y: 11 },
50727
+ { x: 11, y: 11 },
50728
+ { x: 12, y: 11 },
50729
+ { x: 13, y: 11 },
50730
+ { x: 13, y: 12 },
50731
+ { x: 13, y: 13 },
50732
+ { x: 13, y: 14 },
50733
+ { x: 13, y: 15 }
50734
+ ];
50735
+ DEFAULT_TD_TILES = buildDefaultTDTiles();
50592
50736
  TOWER_SPRITE = `${CDN6}units/guardian.png`;
50593
50737
  CREEP_SPRITE = `${CDN6}units/scrapper.png`;
50594
50738
  TowerDefenseBoard.displayName = "TowerDefenseBoard";
@@ -50865,6 +51009,17 @@ function heroPosition(h) {
50865
51009
  function heroMovement(h) {
50866
51010
  return num(h.movement);
50867
51011
  }
51012
+ function buildDefaultWorldTiles() {
51013
+ const tiles = [];
51014
+ for (let y = 0; y < WORLD_GRID_H; y++) {
51015
+ for (let x = 0; x < WORLD_GRID_W; x++) {
51016
+ const isBorder = x === 0 || y === 0 || x === WORLD_GRID_W - 1 || y === WORLD_GRID_H - 1;
51017
+ const def = isBorder ? WORLD_TERRAIN_DEFS[3] : WORLD_TERRAIN_DEFS[(x * 5 + y * 3 + (x ^ y)) % (WORLD_TERRAIN_DEFS.length - 1)];
51018
+ tiles.push({ x, y, terrain: def.terrain, terrainSprite: def.sprite, passable: def.passable });
51019
+ }
51020
+ }
51021
+ return tiles;
51022
+ }
50868
51023
  function defaultIsInRange(from, to, range) {
50869
51024
  return Math.abs(from.x - to.x) + Math.abs(from.y - to.y) <= range;
50870
51025
  }
@@ -50875,8 +51030,10 @@ function WorldMapBoard({
50875
51030
  features: propFeatures,
50876
51031
  assetManifest: propAssetManifest,
50877
51032
  isLoading,
50878
- scale = 0.4,
51033
+ scale = 0.25,
50879
51034
  unitScale = 2.5,
51035
+ spriteHeightRatio = 1.5,
51036
+ spriteMaxWidthRatio = 0.6,
50880
51037
  allowMoveAllHeroes = false,
50881
51038
  isInRange = defaultIsInRange,
50882
51039
  heroSelectEvent,
@@ -50917,7 +51074,8 @@ function WorldMapBoard({
50917
51074
  })),
50918
51075
  [entityTiles]
50919
51076
  );
50920
- const tiles = propTiles ?? derivedTiles;
51077
+ const rawTiles = propTiles ?? (derivedTiles.length > 0 ? derivedTiles : null);
51078
+ const tiles = rawTiles != null && rawTiles.length >= WORLD_GRID_W * WORLD_GRID_H ? rawTiles : DEFAULT_WORLD_TILES;
50921
51079
  const baseUnits = React76.useMemo(
50922
51080
  () => propUnits ?? entityUnits.map((u) => ({
50923
51081
  id: str(u.id),
@@ -51114,6 +51272,8 @@ function WorldMapBoard({
51114
51272
  effectSpriteUrls,
51115
51273
  resolveUnitFrame,
51116
51274
  unitScale,
51275
+ spriteHeightRatio,
51276
+ spriteMaxWidthRatio,
51117
51277
  diamondTopY,
51118
51278
  enableCamera
51119
51279
  }
@@ -51125,6 +51285,7 @@ function WorldMapBoard({
51125
51285
  footer && footer(ctx)
51126
51286
  ] });
51127
51287
  }
51288
+ var WORLD_CDN, WORLD_GRID_W, WORLD_GRID_H, WORLD_TERRAIN_DEFS, DEFAULT_WORLD_TILES;
51128
51289
  var init_WorldMapBoard = __esm({
51129
51290
  "components/game/organisms/WorldMapBoard.tsx"() {
51130
51291
  "use client";
@@ -51135,6 +51296,17 @@ var init_WorldMapBoard = __esm({
51135
51296
  init_IsometricCanvas();
51136
51297
  init_boardEntity();
51137
51298
  init_isometric();
51299
+ WORLD_CDN = "https://almadar-kflow-assets.web.app/shared";
51300
+ WORLD_GRID_W = 16;
51301
+ WORLD_GRID_H = 16;
51302
+ WORLD_TERRAIN_DEFS = [
51303
+ { terrain: "grass", sprite: `${WORLD_CDN}/isometric-dungeon/Isometric/dirt_E.png`, passable: true },
51304
+ { terrain: "dirt", sprite: `${WORLD_CDN}/isometric-dungeon/Isometric/dirtTiles_E.png`, passable: true },
51305
+ { terrain: "forest", sprite: `${WORLD_CDN}/isometric-dungeon/Isometric/planks_E.png`, passable: true },
51306
+ { terrain: "stone", sprite: `${WORLD_CDN}/isometric-dungeon/Isometric/stoneInset_E.png`, passable: false },
51307
+ { terrain: "castle", sprite: `${WORLD_CDN}/isometric-dungeon/Isometric/stoneTile_E.png`, passable: true }
51308
+ ];
51309
+ DEFAULT_WORLD_TILES = buildDefaultWorldTiles();
51138
51310
  WorldMapBoard.displayName = "WorldMapBoard";
51139
51311
  }
51140
51312
  });