@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.
@@ -11144,6 +11144,8 @@ function IsometricCanvas({
11144
11144
  showMinimap = true,
11145
11145
  enableCamera = true,
11146
11146
  unitScale = 1,
11147
+ spriteHeightRatio = 1.5,
11148
+ spriteMaxWidthRatio = 0.6,
11147
11149
  // Asset resolution
11148
11150
  getTerrainSprite,
11149
11151
  getFeatureSprite,
@@ -11534,8 +11536,8 @@ function IsometricCanvas({
11534
11536
  const breatheOffset = 0.8 * scale * (1 + Math.sin(animTime * 2e-3 + (unit.position.x * 3.7 + unit.position.y * 5.3)));
11535
11537
  const unitSpriteUrl = resolveUnitSpriteUrl(unit);
11536
11538
  const img = unitSpriteUrl ? getImage(unitSpriteUrl) : null;
11537
- const unitDrawH = scaledFloorHeight * 1.5 * unitScale;
11538
- const maxUnitW = scaledTileWidth * 0.6 * unitScale;
11539
+ const unitDrawH = scaledFloorHeight * spriteHeightRatio * unitScale;
11540
+ const maxUnitW = scaledTileWidth * spriteMaxWidthRatio * unitScale;
11539
11541
  const ar = img ? img.naturalWidth / img.naturalHeight : 0.5;
11540
11542
  let drawH = unitDrawH;
11541
11543
  let drawW = unitDrawH * ar;
@@ -11911,14 +11913,32 @@ var init_boardEntity = __esm({
11911
11913
  "components/game/organisms/boardEntity.ts"() {
11912
11914
  }
11913
11915
  });
11916
+ function buildDefaultBattleTiles() {
11917
+ const tiles = [];
11918
+ for (let y = 0; y < BATTLE_GRID_H; y++) {
11919
+ for (let x = 0; x < BATTLE_GRID_W; x++) {
11920
+ const variant = (x * 3 + y * 7 + (x ^ y)) % BATTLE_TERRAIN_SPRITES.length;
11921
+ tiles.push({
11922
+ x,
11923
+ y,
11924
+ terrain: ["grass", "dirt", "planks", "stone", "stone"][variant],
11925
+ terrainSprite: BATTLE_TERRAIN_SPRITES[variant],
11926
+ passable: true
11927
+ });
11928
+ }
11929
+ }
11930
+ return tiles;
11931
+ }
11914
11932
  function BattleBoard({
11915
11933
  entity,
11916
11934
  tiles: propTiles,
11917
11935
  units: propUnits,
11918
11936
  features: propFeatures,
11919
11937
  assetManifest: propAssetManifest,
11920
- scale = 0.45,
11938
+ scale = 0.25,
11921
11939
  unitScale = 1,
11940
+ spriteHeightRatio = 1.5,
11941
+ spriteMaxWidthRatio = 0.6,
11922
11942
  header,
11923
11943
  sidebar,
11924
11944
  actions,
@@ -11942,10 +11962,11 @@ function BattleBoard({
11942
11962
  className
11943
11963
  }) {
11944
11964
  const board = boardEntity(entity) ?? {};
11945
- const tiles = propTiles ?? (Array.isArray(board.tiles) ? board.tiles : []);
11965
+ const rawTiles = propTiles ?? (Array.isArray(board.tiles) ? board.tiles : []);
11966
+ const tiles = rawTiles.length === 0 ? DEFAULT_BATTLE_TILES : rawTiles;
11946
11967
  const features = propFeatures ?? (Array.isArray(board.features) ? board.features : []);
11947
- const boardWidth = num(board.gridWidth ?? board.boardWidth, 8);
11948
- const boardHeight = num(board.gridHeight ?? board.boardHeight, 6);
11968
+ const boardWidth = num(board.gridWidth ?? board.boardWidth, BATTLE_GRID_W);
11969
+ const boardHeight = num(board.gridHeight ?? board.boardHeight, BATTLE_GRID_H);
11949
11970
  const assetManifest = propAssetManifest ?? board.assetManifest;
11950
11971
  const backgroundImage = board.backgroundImage;
11951
11972
  const units = rows(board.units);
@@ -12209,7 +12230,9 @@ function BattleBoard({
12209
12230
  hasActiveEffects: hasActiveEffects2,
12210
12231
  effectSpriteUrls,
12211
12232
  resolveUnitFrame,
12212
- unitScale
12233
+ unitScale,
12234
+ spriteHeightRatio,
12235
+ spriteMaxWidthRatio
12213
12236
  }
12214
12237
  ),
12215
12238
  overlay && overlay(ctx)
@@ -12265,6 +12288,7 @@ function BattleBoard({
12265
12288
  ] }) }))
12266
12289
  ] });
12267
12290
  }
12291
+ var BATTLE_CDN, BATTLE_GRID_W, BATTLE_GRID_H, BATTLE_TERRAIN_SPRITES, DEFAULT_BATTLE_TILES;
12268
12292
  var init_BattleBoard = __esm({
12269
12293
  "components/game/organisms/BattleBoard.tsx"() {
12270
12294
  "use client";
@@ -12277,6 +12301,17 @@ var init_BattleBoard = __esm({
12277
12301
  init_IsometricCanvas();
12278
12302
  init_boardEntity();
12279
12303
  init_isometric();
12304
+ BATTLE_CDN = "https://almadar-kflow-assets.web.app/shared";
12305
+ BATTLE_GRID_W = 16;
12306
+ BATTLE_GRID_H = 16;
12307
+ BATTLE_TERRAIN_SPRITES = [
12308
+ `${BATTLE_CDN}/isometric-dungeon/Isometric/dirt_E.png`,
12309
+ `${BATTLE_CDN}/isometric-dungeon/Isometric/dirtTiles_E.png`,
12310
+ `${BATTLE_CDN}/isometric-dungeon/Isometric/planks_E.png`,
12311
+ `${BATTLE_CDN}/isometric-dungeon/Isometric/stoneTile_E.png`,
12312
+ `${BATTLE_CDN}/isometric-dungeon/Isometric/stoneInset_E.png`
12313
+ ];
12314
+ DEFAULT_BATTLE_TILES = buildDefaultBattleTiles();
12280
12315
  BattleBoard.displayName = "BattleBoard";
12281
12316
  }
12282
12317
  });
@@ -12284,7 +12319,7 @@ function BattleTemplate({
12284
12319
  entity,
12285
12320
  scale = 0.45,
12286
12321
  unitScale = 1,
12287
- tiles = DEFAULT_BATTLE_TILES,
12322
+ tiles = DEFAULT_BATTLE_TILES2,
12288
12323
  units = DEFAULT_BATTLE_UNITS,
12289
12324
  features = DEFAULT_BATTLE_FEATURES,
12290
12325
  assetManifest = DEFAULT_BATTLE_MANIFEST,
@@ -12313,12 +12348,12 @@ function BattleTemplate({
12313
12348
  }
12314
12349
  );
12315
12350
  }
12316
- var CDN, DEFAULT_BATTLE_TILES, DEFAULT_BATTLE_UNITS, DEFAULT_BATTLE_FEATURES, DEFAULT_BATTLE_MANIFEST;
12351
+ var CDN, DEFAULT_BATTLE_TILES2, DEFAULT_BATTLE_UNITS, DEFAULT_BATTLE_FEATURES, DEFAULT_BATTLE_MANIFEST;
12317
12352
  var init_BattleTemplate = __esm({
12318
12353
  "components/game/templates/BattleTemplate.tsx"() {
12319
12354
  init_BattleBoard();
12320
12355
  CDN = "https://almadar-kflow-assets.web.app/shared";
12321
- DEFAULT_BATTLE_TILES = [
12356
+ DEFAULT_BATTLE_TILES2 = [
12322
12357
  { x: 0, y: 0, terrain: "stone", passable: false, terrainSprite: `${CDN}/isometric-blocks/PNG/Abstract tiles/abstractTile_07.png` },
12323
12358
  { x: 1, y: 0, terrain: "stone", passable: false, terrainSprite: `${CDN}/isometric-blocks/PNG/Abstract tiles/abstractTile_07.png` },
12324
12359
  { x: 2, y: 0, terrain: "stone", passable: false, terrainSprite: `${CDN}/isometric-blocks/PNG/Abstract tiles/abstractTile_07.png` },
@@ -42180,6 +42215,7 @@ var init_GameCanvas3D2 = __esm({
42180
42215
  attackTargets = [],
42181
42216
  selectedTileIds = [],
42182
42217
  selectedUnitId = null,
42218
+ unitScale = 1,
42183
42219
  children
42184
42220
  }, ref) => {
42185
42221
  const containerRef = React82.useRef(null);
@@ -42396,12 +42432,18 @@ var init_GameCanvas3D2 = __esm({
42396
42432
  },
42397
42433
  [selectedTileIds, hoveredTile, validMoves, attackTargets, handleTileClick, handleTileHover]
42398
42434
  );
42435
+ const UNIT_BASE_MODEL_SCALE = 0.5;
42436
+ const UNIT_BASE_BILLBOARD_HEIGHT = 1.2;
42437
+ const UNIT_BASE_PRIMITIVE_RADIUS = 0.3;
42399
42438
  const DefaultUnitRenderer = React82.useCallback(
42400
42439
  ({ unit, position }) => {
42401
42440
  const isSelected = selectedUnitId === unit.id;
42402
42441
  const color = unit.faction === "player" ? 4491519 : unit.faction === "enemy" ? 16729156 : 16777028;
42403
42442
  const hasAtlas = unitAtlasUrl(unit) !== null;
42404
42443
  const initialFrame = hasAtlas ? resolveUnitFrame(unit.id) : null;
42444
+ const modelScale = UNIT_BASE_MODEL_SCALE * unitScale;
42445
+ const billboardHeight = UNIT_BASE_BILLBOARD_HEIGHT * unitScale;
42446
+ const primitiveRadius = UNIT_BASE_PRIMITIVE_RADIUS * unitScale;
42405
42447
  return /* @__PURE__ */ jsxRuntime.jsxs(
42406
42448
  "group",
42407
42449
  {
@@ -42419,7 +42461,8 @@ var init_GameCanvas3D2 = __esm({
42419
42461
  UnitSpriteBillboard,
42420
42462
  {
42421
42463
  sheetUrl: initialFrame.sheetUrl,
42422
- resolveFrame: () => resolveUnitFrame(unit.id)
42464
+ resolveFrame: () => resolveUnitFrame(unit.id),
42465
+ height: billboardHeight
42423
42466
  }
42424
42467
  ) })
42425
42468
  ) : unit.modelUrl ? (
@@ -42428,22 +42471,22 @@ var init_GameCanvas3D2 = __esm({
42428
42471
  ModelLoader,
42429
42472
  {
42430
42473
  url: unit.modelUrl,
42431
- scale: 0.5,
42474
+ scale: modelScale,
42432
42475
  fallbackGeometry: "box",
42433
42476
  castShadow: true
42434
42477
  }
42435
42478
  )
42436
42479
  ) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
42437
- /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.3, 0], children: [
42438
- /* @__PURE__ */ jsxRuntime.jsx("cylinderGeometry", { args: [0.3, 0.3, 0.1, 8] }),
42480
+ /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, primitiveRadius, 0], children: [
42481
+ /* @__PURE__ */ jsxRuntime.jsx("cylinderGeometry", { args: [primitiveRadius, primitiveRadius, primitiveRadius * 0.33, 8] }),
42439
42482
  /* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color })
42440
42483
  ] }),
42441
- /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.6, 0], children: [
42442
- /* @__PURE__ */ jsxRuntime.jsx("capsuleGeometry", { args: [0.2, 0.4, 4, 8] }),
42484
+ /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, primitiveRadius * 2, 0], children: [
42485
+ /* @__PURE__ */ jsxRuntime.jsx("capsuleGeometry", { args: [primitiveRadius * 0.67, primitiveRadius * 1.33, 4, 8] }),
42443
42486
  /* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color })
42444
42487
  ] }),
42445
- /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.9, 0], children: [
42446
- /* @__PURE__ */ jsxRuntime.jsx("sphereGeometry", { args: [0.12, 8, 8] }),
42488
+ /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, primitiveRadius * 3, 0], children: [
42489
+ /* @__PURE__ */ jsxRuntime.jsx("sphereGeometry", { args: [primitiveRadius * 0.4, 8, 8] }),
42447
42490
  /* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color })
42448
42491
  ] })
42449
42492
  ] }),
@@ -42476,7 +42519,7 @@ var init_GameCanvas3D2 = __esm({
42476
42519
  }
42477
42520
  );
42478
42521
  },
42479
- [selectedUnitId, handleUnitClick, resolveUnitFrame]
42522
+ [selectedUnitId, handleUnitClick, resolveUnitFrame, unitScale]
42480
42523
  );
42481
42524
  const DefaultFeatureRenderer = React82.useCallback(
42482
42525
  ({
@@ -42619,33 +42662,35 @@ var init_GameCanvas3D2 = __esm({
42619
42662
  fadeStrength: 1
42620
42663
  }
42621
42664
  ),
42622
- tiles.map((tile, index) => {
42623
- const position = gridToWorld(
42624
- tile.x,
42625
- tile.z ?? tile.y ?? 0,
42626
- tile.elevation ?? 0
42627
- );
42628
- const Renderer = CustomTileRenderer || DefaultTileRenderer;
42629
- return /* @__PURE__ */ jsxRuntime.jsx(Renderer, { tile, position }, tile.id ?? `tile-${index}`);
42630
- }),
42631
- features.map((feature, index) => {
42632
- const position = gridToWorld(
42633
- feature.x,
42634
- feature.z ?? feature.y ?? 0,
42635
- (feature.elevation ?? 0) + 0.5
42636
- );
42637
- const Renderer = CustomFeatureRenderer || DefaultFeatureRenderer;
42638
- return /* @__PURE__ */ jsxRuntime.jsx(Renderer, { feature, position }, feature.id ?? `feature-${index}`);
42639
- }),
42640
- units.map((unit) => {
42641
- const position = gridToWorld(
42642
- unit.x ?? 0,
42643
- unit.z ?? unit.y ?? 0,
42644
- (unit.elevation ?? 0) + 0.5
42645
- );
42646
- const Renderer = CustomUnitRenderer || DefaultUnitRenderer;
42647
- return /* @__PURE__ */ jsxRuntime.jsx(Renderer, { unit, position }, unit.id);
42648
- }),
42665
+ /* @__PURE__ */ jsxRuntime.jsxs("group", { children: [
42666
+ tiles.map((tile, index) => {
42667
+ const position = gridToWorld(
42668
+ tile.x,
42669
+ tile.z ?? tile.y ?? 0,
42670
+ tile.elevation ?? 0
42671
+ );
42672
+ const Renderer = CustomTileRenderer || DefaultTileRenderer;
42673
+ return /* @__PURE__ */ jsxRuntime.jsx(Renderer, { tile, position }, tile.id ?? `tile-${index}`);
42674
+ }),
42675
+ features.map((feature, index) => {
42676
+ const position = gridToWorld(
42677
+ feature.x,
42678
+ feature.z ?? feature.y ?? 0,
42679
+ (feature.elevation ?? 0) + 0.5
42680
+ );
42681
+ const Renderer = CustomFeatureRenderer || DefaultFeatureRenderer;
42682
+ return /* @__PURE__ */ jsxRuntime.jsx(Renderer, { feature, position }, feature.id ?? `feature-${index}`);
42683
+ }),
42684
+ units.map((unit) => {
42685
+ const position = gridToWorld(
42686
+ unit.x ?? 0,
42687
+ unit.z ?? unit.y ?? 0,
42688
+ (unit.elevation ?? 0) + 0.5
42689
+ );
42690
+ const Renderer = CustomUnitRenderer || DefaultUnitRenderer;
42691
+ return /* @__PURE__ */ jsxRuntime.jsx(Renderer, { unit, position }, unit.id);
42692
+ })
42693
+ ] }),
42649
42694
  children,
42650
42695
  /* @__PURE__ */ jsxRuntime.jsx(
42651
42696
  drei.OrbitControls,
@@ -42688,6 +42733,8 @@ function GameBoard3D({
42688
42733
  features = [],
42689
42734
  cameraMode = "perspective",
42690
42735
  backgroundColor = "#2a1a1a",
42736
+ unitScale = 1,
42737
+ scale = 0.45,
42691
42738
  tileClickEvent,
42692
42739
  unitClickEvent,
42693
42740
  attackEvent,
@@ -42788,6 +42835,8 @@ function GameBoard3D({
42788
42835
  selectedUnitId,
42789
42836
  validMoves,
42790
42837
  attackTargets,
42838
+ unitScale,
42839
+ scale,
42791
42840
  className: "game-board-3d__canvas w-full min-h-[85vh]"
42792
42841
  }
42793
42842
  ),
@@ -44756,6 +44805,35 @@ var init_PricingPageTemplate = __esm({
44756
44805
  PricingPageTemplate.displayName = "PricingPageTemplate";
44757
44806
  }
44758
44807
  });
44808
+ function dungeonTerrain(x, y, stairsX, stairsY) {
44809
+ const isBorder = x === 0 || y === 0 || x === DUNGEON_GRID_W - 1 || y === DUNGEON_GRID_H - 1;
44810
+ const isPillar = x % 4 === 0 && y % 4 === 0 && !isBorder;
44811
+ const isWall = isBorder || isPillar;
44812
+ if (x === stairsX && y === stairsY) {
44813
+ return { terrain: "stairs", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stairs_E.png` };
44814
+ }
44815
+ if (isWall) {
44816
+ return { terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` };
44817
+ }
44818
+ const variant = (x * 3 + y * 7) % 5;
44819
+ const sprites = [
44820
+ `${CDN4}/isometric-dungeon/Isometric/dirt_E.png`,
44821
+ `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png`,
44822
+ `${CDN4}/isometric-dungeon/Isometric/planks_E.png`,
44823
+ `${CDN4}/isometric-dungeon/Isometric/stoneTile_E.png`,
44824
+ `${CDN4}/isometric-dungeon/Isometric/dirt_E.png`
44825
+ ];
44826
+ return { terrain: "floor", passable: true, terrainSprite: sprites[variant] };
44827
+ }
44828
+ function buildDefaultDungeonTiles(stairsX, stairsY) {
44829
+ const tiles = [];
44830
+ for (let y = 0; y < DUNGEON_GRID_H; y++) {
44831
+ for (let x = 0; x < DUNGEON_GRID_W; x++) {
44832
+ tiles.push({ x, y, ...dungeonTerrain(x, y, stairsX, stairsY) });
44833
+ }
44834
+ }
44835
+ return tiles;
44836
+ }
44759
44837
  function RoguelikeBoard({
44760
44838
  entity,
44761
44839
  tiles: propTiles,
@@ -44771,15 +44849,19 @@ function RoguelikeBoard({
44771
44849
  stairsX: propStairsX,
44772
44850
  stairsY: propStairsY,
44773
44851
  assetManifest: propAssetManifest,
44774
- scale = 0.45,
44852
+ scale = 0.25,
44775
44853
  unitScale = 1,
44854
+ spriteHeightRatio = 1.5,
44855
+ spriteMaxWidthRatio = 0.6,
44776
44856
  moveEvent,
44777
44857
  playAgainEvent,
44778
44858
  gameEndEvent,
44779
44859
  className
44780
44860
  }) {
44781
44861
  const board = boardEntity(entity) ?? {};
44782
- const tiles = propTiles ?? (Array.isArray(board.tiles) ? board.tiles : DEFAULT_TILES2);
44862
+ const resolvedStairsX = propStairsX ?? num(board.stairsX, 14);
44863
+ const resolvedStairsY = propStairsY ?? num(board.stairsY, 14);
44864
+ const tiles = propTiles ?? (Array.isArray(board.tiles) && board.tiles.length >= DUNGEON_GRID_W * DUNGEON_GRID_H ? board.tiles : buildDefaultDungeonTiles(resolvedStairsX, resolvedStairsY));
44783
44865
  const enemies = propEnemies ?? (Array.isArray(board.enemies) ? board.enemies : DEFAULT_ENEMIES);
44784
44866
  const items = propItems ?? (Array.isArray(board.items) ? board.items : DEFAULT_ITEMS);
44785
44867
  const player = propPlayer ?? board.player ?? { x: 1, y: 1 };
@@ -44789,8 +44871,8 @@ function RoguelikeBoard({
44789
44871
  const depth = propDepth ?? num(board.depth, 1);
44790
44872
  const result = propResult ?? (str(board.result) || "none");
44791
44873
  const phase = propPhase ?? (str(board.phase) || "player_turn");
44792
- propStairsX ?? num(board.stairsX, 3);
44793
- propStairsY ?? num(board.stairsY, 3);
44874
+ propStairsX ?? num(board.stairsX, 14);
44875
+ propStairsY ?? num(board.stairsY, 14);
44794
44876
  const eventBus = useEventBus();
44795
44877
  const { t } = hooks.useTranslate();
44796
44878
  const [hoveredTile, setHoveredTile] = React82.useState(null);
@@ -44920,7 +45002,9 @@ function RoguelikeBoard({
44920
45002
  scale,
44921
45003
  assetBaseUrl: propAssetManifest?.baseUrl ?? CDN4,
44922
45004
  assetManifest: propAssetManifest,
44923
- unitScale
45005
+ unitScale,
45006
+ spriteHeightRatio,
45007
+ spriteMaxWidthRatio
44924
45008
  }
44925
45009
  ) }),
44926
45010
  !isGameOver && /* @__PURE__ */ jsxRuntime.jsx(HStack, { className: "justify-center gap-2 p-3", gap: "none", children: [
@@ -44964,7 +45048,7 @@ function RoguelikeBoard({
44964
45048
  ] }) })
44965
45049
  ] });
44966
45050
  }
44967
- var CDN4, DEFAULT_TILES2, DEFAULT_ENEMIES, DEFAULT_ITEMS, FEATURE_SPRITE;
45051
+ var CDN4, DUNGEON_GRID_W, DUNGEON_GRID_H, DEFAULT_ENEMIES, DEFAULT_ITEMS, FEATURE_SPRITE;
44968
45052
  var init_RoguelikeBoard = __esm({
44969
45053
  "components/game/organisms/RoguelikeBoard.tsx"() {
44970
45054
  "use client";
@@ -44977,39 +45061,19 @@ var init_RoguelikeBoard = __esm({
44977
45061
  init_IsometricCanvas();
44978
45062
  init_boardEntity();
44979
45063
  CDN4 = "https://almadar-kflow-assets.web.app/shared";
44980
- DEFAULT_TILES2 = [
44981
- { x: 0, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
44982
- { x: 1, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
44983
- { x: 2, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
44984
- { x: 3, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
44985
- { x: 4, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
44986
- { x: 0, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
44987
- { x: 1, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
44988
- { x: 2, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
44989
- { x: 3, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
44990
- { x: 4, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
44991
- { x: 0, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
44992
- { x: 1, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png` },
44993
- { x: 2, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png` },
44994
- { x: 3, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
44995
- { x: 4, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
44996
- { x: 0, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
44997
- { x: 1, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
44998
- { x: 2, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png` },
44999
- { x: 3, y: 3, terrain: "stairs", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stairs_E.png` },
45000
- { x: 4, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45001
- { x: 0, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45002
- { x: 1, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45003
- { x: 2, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45004
- { x: 3, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45005
- { x: 4, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` }
45006
- ];
45064
+ DUNGEON_GRID_W = 16;
45065
+ DUNGEON_GRID_H = 16;
45066
+ buildDefaultDungeonTiles(14, 14);
45007
45067
  DEFAULT_ENEMIES = [
45008
- { id: "e1", x: 3, y: 1, hp: 5, attack: 2 },
45009
- { id: "e2", x: 1, y: 3, hp: 4, attack: 1 }
45068
+ { id: "e1", x: 5, y: 2, hp: 5, attack: 2 },
45069
+ { id: "e2", x: 9, y: 6, hp: 4, attack: 1 },
45070
+ { id: "e3", x: 3, y: 10, hp: 6, attack: 2 },
45071
+ { id: "e4", x: 12, y: 8, hp: 5, attack: 3 }
45010
45072
  ];
45011
45073
  DEFAULT_ITEMS = [
45012
- { id: "i1", x: 2, y: 2, kind: "health_potion" }
45074
+ { id: "i1", x: 3, y: 3, kind: "health_potion" },
45075
+ { id: "i2", x: 7, y: 9, kind: "sword" },
45076
+ { id: "i3", x: 11, y: 5, kind: "shield" }
45013
45077
  ];
45014
45078
  FEATURE_SPRITE = {
45015
45079
  health_potion: `${CDN4}/isometric-dungeon/Isometric/chestClosed_E.png`,
@@ -49217,6 +49281,22 @@ var init_ToastSlot = __esm({
49217
49281
  ToastSlot.displayName = "ToastSlot";
49218
49282
  }
49219
49283
  });
49284
+ function buildDefaultTDTiles() {
49285
+ const pathSet = new Set(DEFAULT_TD_PATH.map((p2) => `${p2.x},${p2.y}`));
49286
+ const tiles = [];
49287
+ for (let y = 0; y < TD_GRID_H; y++) {
49288
+ for (let x = 0; x < TD_GRID_W; x++) {
49289
+ if (pathSet.has(`${x},${y}`)) {
49290
+ tiles.push({ x, y, terrain: "path", passable: false, terrainSprite: TERRAIN_SPRITES.path });
49291
+ } else {
49292
+ const variant = (x * 3 + y * 5 + (x ^ y)) % 3;
49293
+ const terrainKey = ["ground", "grass", "dirt"][variant];
49294
+ tiles.push({ x, y, terrain: terrainKey, passable: true, terrainSprite: TERRAIN_SPRITES[terrainKey] ?? TERRAIN_SPRITES.ground });
49295
+ }
49296
+ }
49297
+ }
49298
+ return tiles;
49299
+ }
49220
49300
  function tilesToIso(tiles) {
49221
49301
  return tiles.map((t) => ({
49222
49302
  x: t.x,
@@ -49272,7 +49352,10 @@ function TowerDefenseBoard({
49272
49352
  result: propResult,
49273
49353
  waveActive: propWaveActive,
49274
49354
  towerCost = 25,
49275
- scale = 0.45,
49355
+ scale = 0.25,
49356
+ unitScale = 1,
49357
+ spriteHeightRatio = 1.5,
49358
+ spriteMaxWidthRatio = 0.6,
49276
49359
  placeTowerEvent,
49277
49360
  startWaveEvent,
49278
49361
  playAgainEvent,
@@ -49282,8 +49365,10 @@ function TowerDefenseBoard({
49282
49365
  const board = boardEntity(entity) ?? {};
49283
49366
  const eventBus = useEventBus();
49284
49367
  const { t } = hooks.useTranslate();
49285
- const tiles = propTiles ?? rows(board.tiles);
49286
- const path = propPath ?? rows(board.path);
49368
+ const rawTiles = propTiles ?? rows(board.tiles);
49369
+ const tiles = rawTiles.length >= TD_GRID_W * TD_GRID_H ? rawTiles : DEFAULT_TD_TILES;
49370
+ const rawPath = propPath ?? rows(board.path);
49371
+ const path = rawPath.length > 0 ? rawPath : DEFAULT_TD_PATH;
49287
49372
  const towers = propTowers ?? rows(board.towers);
49288
49373
  const creeps = propCreeps ?? rows(board.creeps);
49289
49374
  const gold = propGold ?? num(board.gold, 100);
@@ -49380,7 +49465,10 @@ function TowerDefenseBoard({
49380
49465
  onTileClick: handleTileClick,
49381
49466
  onTileHover: (x, y) => setHoveredTile({ x, y }),
49382
49467
  onTileLeave: () => setHoveredTile(null),
49383
- scale
49468
+ scale,
49469
+ unitScale,
49470
+ spriteHeightRatio,
49471
+ spriteMaxWidthRatio
49384
49472
  }
49385
49473
  ),
49386
49474
  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)` }) })
@@ -49410,7 +49498,7 @@ function TowerDefenseBoard({
49410
49498
  ] }) })
49411
49499
  ] });
49412
49500
  }
49413
- var CDN6, TERRAIN_SPRITES, TOWER_SPRITE, CREEP_SPRITE;
49501
+ var CDN6, TD_GRID_W, TD_GRID_H, TERRAIN_SPRITES, DEFAULT_TD_PATH, DEFAULT_TD_TILES, TOWER_SPRITE, CREEP_SPRITE;
49414
49502
  var init_TowerDefenseBoard = __esm({
49415
49503
  "components/game/organisms/TowerDefenseBoard.tsx"() {
49416
49504
  "use client";
@@ -49423,11 +49511,67 @@ var init_TowerDefenseBoard = __esm({
49423
49511
  init_IsometricCanvas();
49424
49512
  init_boardEntity();
49425
49513
  CDN6 = "https://almadar-kflow-assets.web.app/shared/";
49514
+ TD_GRID_W = 16;
49515
+ TD_GRID_H = 16;
49426
49516
  TERRAIN_SPRITES = {
49427
49517
  ground: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_01.png`,
49428
49518
  path: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_09.png`,
49429
- wall: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_05.png`
49430
- };
49519
+ wall: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_05.png`,
49520
+ grass: `${CDN6}isometric-dungeon/Isometric/dirt_E.png`,
49521
+ dirt: `${CDN6}isometric-dungeon/Isometric/dirtTiles_E.png`
49522
+ };
49523
+ DEFAULT_TD_PATH = [
49524
+ { x: 2, y: 0 },
49525
+ { x: 2, y: 1 },
49526
+ { x: 2, y: 2 },
49527
+ { x: 2, y: 3 },
49528
+ { x: 3, y: 3 },
49529
+ { x: 4, y: 3 },
49530
+ { x: 5, y: 3 },
49531
+ { x: 6, y: 3 },
49532
+ { x: 7, y: 3 },
49533
+ { x: 8, y: 3 },
49534
+ { x: 9, y: 3 },
49535
+ { x: 10, y: 3 },
49536
+ { x: 11, y: 3 },
49537
+ { x: 12, y: 3 },
49538
+ { x: 13, y: 3 },
49539
+ { x: 13, y: 4 },
49540
+ { x: 13, y: 5 },
49541
+ { x: 13, y: 6 },
49542
+ { x: 13, y: 7 },
49543
+ { x: 12, y: 7 },
49544
+ { x: 11, y: 7 },
49545
+ { x: 10, y: 7 },
49546
+ { x: 9, y: 7 },
49547
+ { x: 8, y: 7 },
49548
+ { x: 7, y: 7 },
49549
+ { x: 6, y: 7 },
49550
+ { x: 5, y: 7 },
49551
+ { x: 4, y: 7 },
49552
+ { x: 3, y: 7 },
49553
+ { x: 2, y: 7 },
49554
+ { x: 2, y: 8 },
49555
+ { x: 2, y: 9 },
49556
+ { x: 2, y: 10 },
49557
+ { x: 2, y: 11 },
49558
+ { x: 3, y: 11 },
49559
+ { x: 4, y: 11 },
49560
+ { x: 5, y: 11 },
49561
+ { x: 6, y: 11 },
49562
+ { x: 7, y: 11 },
49563
+ { x: 8, y: 11 },
49564
+ { x: 9, y: 11 },
49565
+ { x: 10, y: 11 },
49566
+ { x: 11, y: 11 },
49567
+ { x: 12, y: 11 },
49568
+ { x: 13, y: 11 },
49569
+ { x: 13, y: 12 },
49570
+ { x: 13, y: 13 },
49571
+ { x: 13, y: 14 },
49572
+ { x: 13, y: 15 }
49573
+ ];
49574
+ DEFAULT_TD_TILES = buildDefaultTDTiles();
49431
49575
  TOWER_SPRITE = `${CDN6}units/guardian.png`;
49432
49576
  CREEP_SPRITE = `${CDN6}units/scrapper.png`;
49433
49577
  TowerDefenseBoard.displayName = "TowerDefenseBoard";
@@ -49704,6 +49848,17 @@ function heroPosition(h) {
49704
49848
  function heroMovement(h) {
49705
49849
  return num(h.movement);
49706
49850
  }
49851
+ function buildDefaultWorldTiles() {
49852
+ const tiles = [];
49853
+ for (let y = 0; y < WORLD_GRID_H; y++) {
49854
+ for (let x = 0; x < WORLD_GRID_W; x++) {
49855
+ const isBorder = x === 0 || y === 0 || x === WORLD_GRID_W - 1 || y === WORLD_GRID_H - 1;
49856
+ const def = isBorder ? WORLD_TERRAIN_DEFS[3] : WORLD_TERRAIN_DEFS[(x * 5 + y * 3 + (x ^ y)) % (WORLD_TERRAIN_DEFS.length - 1)];
49857
+ tiles.push({ x, y, terrain: def.terrain, terrainSprite: def.sprite, passable: def.passable });
49858
+ }
49859
+ }
49860
+ return tiles;
49861
+ }
49707
49862
  function defaultIsInRange(from, to, range) {
49708
49863
  return Math.abs(from.x - to.x) + Math.abs(from.y - to.y) <= range;
49709
49864
  }
@@ -49714,8 +49869,10 @@ function WorldMapBoard({
49714
49869
  features: propFeatures,
49715
49870
  assetManifest: propAssetManifest,
49716
49871
  isLoading,
49717
- scale = 0.4,
49872
+ scale = 0.25,
49718
49873
  unitScale = 2.5,
49874
+ spriteHeightRatio = 1.5,
49875
+ spriteMaxWidthRatio = 0.6,
49719
49876
  allowMoveAllHeroes = false,
49720
49877
  isInRange = defaultIsInRange,
49721
49878
  heroSelectEvent,
@@ -49756,7 +49913,8 @@ function WorldMapBoard({
49756
49913
  })),
49757
49914
  [entityTiles]
49758
49915
  );
49759
- const tiles = propTiles ?? derivedTiles;
49916
+ const rawTiles = propTiles ?? (derivedTiles.length > 0 ? derivedTiles : null);
49917
+ const tiles = rawTiles != null && rawTiles.length >= WORLD_GRID_W * WORLD_GRID_H ? rawTiles : DEFAULT_WORLD_TILES;
49760
49918
  const baseUnits = React82.useMemo(
49761
49919
  () => propUnits ?? entityUnits.map((u) => ({
49762
49920
  id: str(u.id),
@@ -49953,6 +50111,8 @@ function WorldMapBoard({
49953
50111
  effectSpriteUrls,
49954
50112
  resolveUnitFrame,
49955
50113
  unitScale,
50114
+ spriteHeightRatio,
50115
+ spriteMaxWidthRatio,
49956
50116
  diamondTopY,
49957
50117
  enableCamera
49958
50118
  }
@@ -49964,6 +50124,7 @@ function WorldMapBoard({
49964
50124
  footer && footer(ctx)
49965
50125
  ] });
49966
50126
  }
50127
+ var WORLD_CDN, WORLD_GRID_W, WORLD_GRID_H, WORLD_TERRAIN_DEFS, DEFAULT_WORLD_TILES;
49967
50128
  var init_WorldMapBoard = __esm({
49968
50129
  "components/game/organisms/WorldMapBoard.tsx"() {
49969
50130
  "use client";
@@ -49974,6 +50135,17 @@ var init_WorldMapBoard = __esm({
49974
50135
  init_IsometricCanvas();
49975
50136
  init_boardEntity();
49976
50137
  init_isometric();
50138
+ WORLD_CDN = "https://almadar-kflow-assets.web.app/shared";
50139
+ WORLD_GRID_W = 16;
50140
+ WORLD_GRID_H = 16;
50141
+ WORLD_TERRAIN_DEFS = [
50142
+ { terrain: "grass", sprite: `${WORLD_CDN}/isometric-dungeon/Isometric/dirt_E.png`, passable: true },
50143
+ { terrain: "dirt", sprite: `${WORLD_CDN}/isometric-dungeon/Isometric/dirtTiles_E.png`, passable: true },
50144
+ { terrain: "forest", sprite: `${WORLD_CDN}/isometric-dungeon/Isometric/planks_E.png`, passable: true },
50145
+ { terrain: "stone", sprite: `${WORLD_CDN}/isometric-dungeon/Isometric/stoneInset_E.png`, passable: false },
50146
+ { terrain: "castle", sprite: `${WORLD_CDN}/isometric-dungeon/Isometric/stoneTile_E.png`, passable: true }
50147
+ ];
50148
+ DEFAULT_WORLD_TILES = buildDefaultWorldTiles();
49977
50149
  WorldMapBoard.displayName = "WorldMapBoard";
49978
50150
  }
49979
50151
  });