@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.
@@ -10842,6 +10842,8 @@ function IsometricCanvas({
10842
10842
  showMinimap = true,
10843
10843
  enableCamera = true,
10844
10844
  unitScale = 1,
10845
+ spriteHeightRatio = 1.5,
10846
+ spriteMaxWidthRatio = 0.6,
10845
10847
  // Asset resolution
10846
10848
  getTerrainSprite,
10847
10849
  getFeatureSprite,
@@ -11232,8 +11234,8 @@ function IsometricCanvas({
11232
11234
  const breatheOffset = 0.8 * scale * (1 + Math.sin(animTime * 2e-3 + (unit.position.x * 3.7 + unit.position.y * 5.3)));
11233
11235
  const unitSpriteUrl = resolveUnitSpriteUrl(unit);
11234
11236
  const img = unitSpriteUrl ? getImage(unitSpriteUrl) : null;
11235
- const unitDrawH = scaledFloorHeight * 1.5 * unitScale;
11236
- const maxUnitW = scaledTileWidth * 0.6 * unitScale;
11237
+ const unitDrawH = scaledFloorHeight * spriteHeightRatio * unitScale;
11238
+ const maxUnitW = scaledTileWidth * spriteMaxWidthRatio * unitScale;
11237
11239
  const ar = img ? img.naturalWidth / img.naturalHeight : 0.5;
11238
11240
  let drawH = unitDrawH;
11239
11241
  let drawW = unitDrawH * ar;
@@ -11609,14 +11611,32 @@ var init_boardEntity = __esm({
11609
11611
  "components/game/organisms/boardEntity.ts"() {
11610
11612
  }
11611
11613
  });
11614
+ function buildDefaultBattleTiles() {
11615
+ const tiles = [];
11616
+ for (let y = 0; y < BATTLE_GRID_H; y++) {
11617
+ for (let x = 0; x < BATTLE_GRID_W; x++) {
11618
+ const variant = (x * 3 + y * 7 + (x ^ y)) % BATTLE_TERRAIN_SPRITES.length;
11619
+ tiles.push({
11620
+ x,
11621
+ y,
11622
+ terrain: ["grass", "dirt", "planks", "stone", "stone"][variant],
11623
+ terrainSprite: BATTLE_TERRAIN_SPRITES[variant],
11624
+ passable: true
11625
+ });
11626
+ }
11627
+ }
11628
+ return tiles;
11629
+ }
11612
11630
  function BattleBoard({
11613
11631
  entity,
11614
11632
  tiles: propTiles,
11615
11633
  units: propUnits,
11616
11634
  features: propFeatures,
11617
11635
  assetManifest: propAssetManifest,
11618
- scale = 0.45,
11636
+ scale = 0.25,
11619
11637
  unitScale = 1,
11638
+ spriteHeightRatio = 1.5,
11639
+ spriteMaxWidthRatio = 0.6,
11620
11640
  header,
11621
11641
  sidebar,
11622
11642
  actions,
@@ -11640,10 +11660,11 @@ function BattleBoard({
11640
11660
  className
11641
11661
  }) {
11642
11662
  const board = boardEntity(entity) ?? {};
11643
- const tiles = propTiles ?? (Array.isArray(board.tiles) ? board.tiles : []);
11663
+ const rawTiles = propTiles ?? (Array.isArray(board.tiles) ? board.tiles : []);
11664
+ const tiles = rawTiles.length === 0 ? DEFAULT_BATTLE_TILES : rawTiles;
11644
11665
  const features = propFeatures ?? (Array.isArray(board.features) ? board.features : []);
11645
- const boardWidth = num(board.gridWidth ?? board.boardWidth, 8);
11646
- const boardHeight = num(board.gridHeight ?? board.boardHeight, 6);
11666
+ const boardWidth = num(board.gridWidth ?? board.boardWidth, BATTLE_GRID_W);
11667
+ const boardHeight = num(board.gridHeight ?? board.boardHeight, BATTLE_GRID_H);
11647
11668
  const assetManifest = propAssetManifest ?? board.assetManifest;
11648
11669
  const backgroundImage = board.backgroundImage;
11649
11670
  const units = rows(board.units);
@@ -11907,7 +11928,9 @@ function BattleBoard({
11907
11928
  hasActiveEffects: hasActiveEffects2,
11908
11929
  effectSpriteUrls,
11909
11930
  resolveUnitFrame,
11910
- unitScale
11931
+ unitScale,
11932
+ spriteHeightRatio,
11933
+ spriteMaxWidthRatio
11911
11934
  }
11912
11935
  ),
11913
11936
  overlay && overlay(ctx)
@@ -11963,6 +11986,7 @@ function BattleBoard({
11963
11986
  ] }) }))
11964
11987
  ] });
11965
11988
  }
11989
+ var BATTLE_CDN, BATTLE_GRID_W, BATTLE_GRID_H, BATTLE_TERRAIN_SPRITES, DEFAULT_BATTLE_TILES;
11966
11990
  var init_BattleBoard = __esm({
11967
11991
  "components/game/organisms/BattleBoard.tsx"() {
11968
11992
  "use client";
@@ -11975,6 +11999,17 @@ var init_BattleBoard = __esm({
11975
11999
  init_IsometricCanvas();
11976
12000
  init_boardEntity();
11977
12001
  init_isometric();
12002
+ BATTLE_CDN = "https://almadar-kflow-assets.web.app/shared";
12003
+ BATTLE_GRID_W = 16;
12004
+ BATTLE_GRID_H = 16;
12005
+ BATTLE_TERRAIN_SPRITES = [
12006
+ `${BATTLE_CDN}/isometric-dungeon/Isometric/dirt_E.png`,
12007
+ `${BATTLE_CDN}/isometric-dungeon/Isometric/dirtTiles_E.png`,
12008
+ `${BATTLE_CDN}/isometric-dungeon/Isometric/planks_E.png`,
12009
+ `${BATTLE_CDN}/isometric-dungeon/Isometric/stoneTile_E.png`,
12010
+ `${BATTLE_CDN}/isometric-dungeon/Isometric/stoneInset_E.png`
12011
+ ];
12012
+ DEFAULT_BATTLE_TILES = buildDefaultBattleTiles();
11978
12013
  BattleBoard.displayName = "BattleBoard";
11979
12014
  }
11980
12015
  });
@@ -11982,7 +12017,7 @@ function BattleTemplate({
11982
12017
  entity,
11983
12018
  scale = 0.45,
11984
12019
  unitScale = 1,
11985
- tiles = DEFAULT_BATTLE_TILES,
12020
+ tiles = DEFAULT_BATTLE_TILES2,
11986
12021
  units = DEFAULT_BATTLE_UNITS,
11987
12022
  features = DEFAULT_BATTLE_FEATURES,
11988
12023
  assetManifest = DEFAULT_BATTLE_MANIFEST,
@@ -12011,12 +12046,12 @@ function BattleTemplate({
12011
12046
  }
12012
12047
  );
12013
12048
  }
12014
- var CDN, DEFAULT_BATTLE_TILES, DEFAULT_BATTLE_UNITS, DEFAULT_BATTLE_FEATURES, DEFAULT_BATTLE_MANIFEST;
12049
+ var CDN, DEFAULT_BATTLE_TILES2, DEFAULT_BATTLE_UNITS, DEFAULT_BATTLE_FEATURES, DEFAULT_BATTLE_MANIFEST;
12015
12050
  var init_BattleTemplate = __esm({
12016
12051
  "components/game/templates/BattleTemplate.tsx"() {
12017
12052
  init_BattleBoard();
12018
12053
  CDN = "https://almadar-kflow-assets.web.app/shared";
12019
- DEFAULT_BATTLE_TILES = [
12054
+ DEFAULT_BATTLE_TILES2 = [
12020
12055
  { x: 0, y: 0, terrain: "stone", passable: false, terrainSprite: `${CDN}/isometric-blocks/PNG/Abstract tiles/abstractTile_07.png` },
12021
12056
  { x: 1, y: 0, terrain: "stone", passable: false, terrainSprite: `${CDN}/isometric-blocks/PNG/Abstract tiles/abstractTile_07.png` },
12022
12057
  { x: 2, y: 0, terrain: "stone", passable: false, terrainSprite: `${CDN}/isometric-blocks/PNG/Abstract tiles/abstractTile_07.png` },
@@ -41878,6 +41913,7 @@ var init_GameCanvas3D2 = __esm({
41878
41913
  attackTargets = [],
41879
41914
  selectedTileIds = [],
41880
41915
  selectedUnitId = null,
41916
+ unitScale = 1,
41881
41917
  children
41882
41918
  }, ref) => {
41883
41919
  const containerRef = React81.useRef(null);
@@ -42094,12 +42130,18 @@ var init_GameCanvas3D2 = __esm({
42094
42130
  },
42095
42131
  [selectedTileIds, hoveredTile, validMoves, attackTargets, handleTileClick, handleTileHover]
42096
42132
  );
42133
+ const UNIT_BASE_MODEL_SCALE = 0.5;
42134
+ const UNIT_BASE_BILLBOARD_HEIGHT = 1.2;
42135
+ const UNIT_BASE_PRIMITIVE_RADIUS = 0.3;
42097
42136
  const DefaultUnitRenderer = React81.useCallback(
42098
42137
  ({ unit, position }) => {
42099
42138
  const isSelected = selectedUnitId === unit.id;
42100
42139
  const color = unit.faction === "player" ? 4491519 : unit.faction === "enemy" ? 16729156 : 16777028;
42101
42140
  const hasAtlas = unitAtlasUrl(unit) !== null;
42102
42141
  const initialFrame = hasAtlas ? resolveUnitFrame(unit.id) : null;
42142
+ const modelScale = UNIT_BASE_MODEL_SCALE * unitScale;
42143
+ const billboardHeight = UNIT_BASE_BILLBOARD_HEIGHT * unitScale;
42144
+ const primitiveRadius = UNIT_BASE_PRIMITIVE_RADIUS * unitScale;
42103
42145
  return /* @__PURE__ */ jsxRuntime.jsxs(
42104
42146
  "group",
42105
42147
  {
@@ -42117,7 +42159,8 @@ var init_GameCanvas3D2 = __esm({
42117
42159
  UnitSpriteBillboard,
42118
42160
  {
42119
42161
  sheetUrl: initialFrame.sheetUrl,
42120
- resolveFrame: () => resolveUnitFrame(unit.id)
42162
+ resolveFrame: () => resolveUnitFrame(unit.id),
42163
+ height: billboardHeight
42121
42164
  }
42122
42165
  ) })
42123
42166
  ) : unit.modelUrl ? (
@@ -42126,22 +42169,22 @@ var init_GameCanvas3D2 = __esm({
42126
42169
  ModelLoader,
42127
42170
  {
42128
42171
  url: unit.modelUrl,
42129
- scale: 0.5,
42172
+ scale: modelScale,
42130
42173
  fallbackGeometry: "box",
42131
42174
  castShadow: true
42132
42175
  }
42133
42176
  )
42134
42177
  ) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
42135
- /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.3, 0], children: [
42136
- /* @__PURE__ */ jsxRuntime.jsx("cylinderGeometry", { args: [0.3, 0.3, 0.1, 8] }),
42178
+ /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, primitiveRadius, 0], children: [
42179
+ /* @__PURE__ */ jsxRuntime.jsx("cylinderGeometry", { args: [primitiveRadius, primitiveRadius, primitiveRadius * 0.33, 8] }),
42137
42180
  /* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color })
42138
42181
  ] }),
42139
- /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.6, 0], children: [
42140
- /* @__PURE__ */ jsxRuntime.jsx("capsuleGeometry", { args: [0.2, 0.4, 4, 8] }),
42182
+ /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, primitiveRadius * 2, 0], children: [
42183
+ /* @__PURE__ */ jsxRuntime.jsx("capsuleGeometry", { args: [primitiveRadius * 0.67, primitiveRadius * 1.33, 4, 8] }),
42141
42184
  /* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color })
42142
42185
  ] }),
42143
- /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.9, 0], children: [
42144
- /* @__PURE__ */ jsxRuntime.jsx("sphereGeometry", { args: [0.12, 8, 8] }),
42186
+ /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, primitiveRadius * 3, 0], children: [
42187
+ /* @__PURE__ */ jsxRuntime.jsx("sphereGeometry", { args: [primitiveRadius * 0.4, 8, 8] }),
42145
42188
  /* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color })
42146
42189
  ] })
42147
42190
  ] }),
@@ -42174,7 +42217,7 @@ var init_GameCanvas3D2 = __esm({
42174
42217
  }
42175
42218
  );
42176
42219
  },
42177
- [selectedUnitId, handleUnitClick, resolveUnitFrame]
42220
+ [selectedUnitId, handleUnitClick, resolveUnitFrame, unitScale]
42178
42221
  );
42179
42222
  const DefaultFeatureRenderer = React81.useCallback(
42180
42223
  ({
@@ -42317,33 +42360,35 @@ var init_GameCanvas3D2 = __esm({
42317
42360
  fadeStrength: 1
42318
42361
  }
42319
42362
  ),
42320
- tiles.map((tile, index) => {
42321
- const position = gridToWorld(
42322
- tile.x,
42323
- tile.z ?? tile.y ?? 0,
42324
- tile.elevation ?? 0
42325
- );
42326
- const Renderer = CustomTileRenderer || DefaultTileRenderer;
42327
- return /* @__PURE__ */ jsxRuntime.jsx(Renderer, { tile, position }, tile.id ?? `tile-${index}`);
42328
- }),
42329
- features.map((feature, index) => {
42330
- const position = gridToWorld(
42331
- feature.x,
42332
- feature.z ?? feature.y ?? 0,
42333
- (feature.elevation ?? 0) + 0.5
42334
- );
42335
- const Renderer = CustomFeatureRenderer || DefaultFeatureRenderer;
42336
- return /* @__PURE__ */ jsxRuntime.jsx(Renderer, { feature, position }, feature.id ?? `feature-${index}`);
42337
- }),
42338
- units.map((unit) => {
42339
- const position = gridToWorld(
42340
- unit.x ?? 0,
42341
- unit.z ?? unit.y ?? 0,
42342
- (unit.elevation ?? 0) + 0.5
42343
- );
42344
- const Renderer = CustomUnitRenderer || DefaultUnitRenderer;
42345
- return /* @__PURE__ */ jsxRuntime.jsx(Renderer, { unit, position }, unit.id);
42346
- }),
42363
+ /* @__PURE__ */ jsxRuntime.jsxs("group", { children: [
42364
+ tiles.map((tile, index) => {
42365
+ const position = gridToWorld(
42366
+ tile.x,
42367
+ tile.z ?? tile.y ?? 0,
42368
+ tile.elevation ?? 0
42369
+ );
42370
+ const Renderer = CustomTileRenderer || DefaultTileRenderer;
42371
+ return /* @__PURE__ */ jsxRuntime.jsx(Renderer, { tile, position }, tile.id ?? `tile-${index}`);
42372
+ }),
42373
+ features.map((feature, index) => {
42374
+ const position = gridToWorld(
42375
+ feature.x,
42376
+ feature.z ?? feature.y ?? 0,
42377
+ (feature.elevation ?? 0) + 0.5
42378
+ );
42379
+ const Renderer = CustomFeatureRenderer || DefaultFeatureRenderer;
42380
+ return /* @__PURE__ */ jsxRuntime.jsx(Renderer, { feature, position }, feature.id ?? `feature-${index}`);
42381
+ }),
42382
+ units.map((unit) => {
42383
+ const position = gridToWorld(
42384
+ unit.x ?? 0,
42385
+ unit.z ?? unit.y ?? 0,
42386
+ (unit.elevation ?? 0) + 0.5
42387
+ );
42388
+ const Renderer = CustomUnitRenderer || DefaultUnitRenderer;
42389
+ return /* @__PURE__ */ jsxRuntime.jsx(Renderer, { unit, position }, unit.id);
42390
+ })
42391
+ ] }),
42347
42392
  children,
42348
42393
  /* @__PURE__ */ jsxRuntime.jsx(
42349
42394
  drei.OrbitControls,
@@ -42386,6 +42431,8 @@ function GameBoard3D({
42386
42431
  features = [],
42387
42432
  cameraMode = "perspective",
42388
42433
  backgroundColor = "#2a1a1a",
42434
+ unitScale = 1,
42435
+ scale = 0.45,
42389
42436
  tileClickEvent,
42390
42437
  unitClickEvent,
42391
42438
  attackEvent,
@@ -42486,6 +42533,8 @@ function GameBoard3D({
42486
42533
  selectedUnitId,
42487
42534
  validMoves,
42488
42535
  attackTargets,
42536
+ unitScale,
42537
+ scale,
42489
42538
  className: "game-board-3d__canvas w-full min-h-[85vh]"
42490
42539
  }
42491
42540
  ),
@@ -44454,6 +44503,35 @@ var init_PricingPageTemplate = __esm({
44454
44503
  PricingPageTemplate.displayName = "PricingPageTemplate";
44455
44504
  }
44456
44505
  });
44506
+ function dungeonTerrain(x, y, stairsX, stairsY) {
44507
+ const isBorder = x === 0 || y === 0 || x === DUNGEON_GRID_W - 1 || y === DUNGEON_GRID_H - 1;
44508
+ const isPillar = x % 4 === 0 && y % 4 === 0 && !isBorder;
44509
+ const isWall = isBorder || isPillar;
44510
+ if (x === stairsX && y === stairsY) {
44511
+ return { terrain: "stairs", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stairs_E.png` };
44512
+ }
44513
+ if (isWall) {
44514
+ return { terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` };
44515
+ }
44516
+ const variant = (x * 3 + y * 7) % 5;
44517
+ const sprites = [
44518
+ `${CDN4}/isometric-dungeon/Isometric/dirt_E.png`,
44519
+ `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png`,
44520
+ `${CDN4}/isometric-dungeon/Isometric/planks_E.png`,
44521
+ `${CDN4}/isometric-dungeon/Isometric/stoneTile_E.png`,
44522
+ `${CDN4}/isometric-dungeon/Isometric/dirt_E.png`
44523
+ ];
44524
+ return { terrain: "floor", passable: true, terrainSprite: sprites[variant] };
44525
+ }
44526
+ function buildDefaultDungeonTiles(stairsX, stairsY) {
44527
+ const tiles = [];
44528
+ for (let y = 0; y < DUNGEON_GRID_H; y++) {
44529
+ for (let x = 0; x < DUNGEON_GRID_W; x++) {
44530
+ tiles.push({ x, y, ...dungeonTerrain(x, y, stairsX, stairsY) });
44531
+ }
44532
+ }
44533
+ return tiles;
44534
+ }
44457
44535
  function RoguelikeBoard({
44458
44536
  entity,
44459
44537
  tiles: propTiles,
@@ -44469,15 +44547,19 @@ function RoguelikeBoard({
44469
44547
  stairsX: propStairsX,
44470
44548
  stairsY: propStairsY,
44471
44549
  assetManifest: propAssetManifest,
44472
- scale = 0.45,
44550
+ scale = 0.25,
44473
44551
  unitScale = 1,
44552
+ spriteHeightRatio = 1.5,
44553
+ spriteMaxWidthRatio = 0.6,
44474
44554
  moveEvent,
44475
44555
  playAgainEvent,
44476
44556
  gameEndEvent,
44477
44557
  className
44478
44558
  }) {
44479
44559
  const board = boardEntity(entity) ?? {};
44480
- const tiles = propTiles ?? (Array.isArray(board.tiles) ? board.tiles : DEFAULT_TILES2);
44560
+ const resolvedStairsX = propStairsX ?? num(board.stairsX, 14);
44561
+ const resolvedStairsY = propStairsY ?? num(board.stairsY, 14);
44562
+ const tiles = propTiles ?? (Array.isArray(board.tiles) && board.tiles.length >= DUNGEON_GRID_W * DUNGEON_GRID_H ? board.tiles : buildDefaultDungeonTiles(resolvedStairsX, resolvedStairsY));
44481
44563
  const enemies = propEnemies ?? (Array.isArray(board.enemies) ? board.enemies : DEFAULT_ENEMIES);
44482
44564
  const items = propItems ?? (Array.isArray(board.items) ? board.items : DEFAULT_ITEMS);
44483
44565
  const player = propPlayer ?? board.player ?? { x: 1, y: 1 };
@@ -44487,8 +44569,8 @@ function RoguelikeBoard({
44487
44569
  const depth = propDepth ?? num(board.depth, 1);
44488
44570
  const result = propResult ?? (str(board.result) || "none");
44489
44571
  const phase = propPhase ?? (str(board.phase) || "player_turn");
44490
- propStairsX ?? num(board.stairsX, 3);
44491
- propStairsY ?? num(board.stairsY, 3);
44572
+ propStairsX ?? num(board.stairsX, 14);
44573
+ propStairsY ?? num(board.stairsY, 14);
44492
44574
  const eventBus = useEventBus();
44493
44575
  const { t } = hooks.useTranslate();
44494
44576
  const [hoveredTile, setHoveredTile] = React81.useState(null);
@@ -44618,7 +44700,9 @@ function RoguelikeBoard({
44618
44700
  scale,
44619
44701
  assetBaseUrl: propAssetManifest?.baseUrl ?? CDN4,
44620
44702
  assetManifest: propAssetManifest,
44621
- unitScale
44703
+ unitScale,
44704
+ spriteHeightRatio,
44705
+ spriteMaxWidthRatio
44622
44706
  }
44623
44707
  ) }),
44624
44708
  !isGameOver && /* @__PURE__ */ jsxRuntime.jsx(HStack, { className: "justify-center gap-2 p-3", gap: "none", children: [
@@ -44662,7 +44746,7 @@ function RoguelikeBoard({
44662
44746
  ] }) })
44663
44747
  ] });
44664
44748
  }
44665
- var CDN4, DEFAULT_TILES2, DEFAULT_ENEMIES, DEFAULT_ITEMS, FEATURE_SPRITE;
44749
+ var CDN4, DUNGEON_GRID_W, DUNGEON_GRID_H, DEFAULT_ENEMIES, DEFAULT_ITEMS, FEATURE_SPRITE;
44666
44750
  var init_RoguelikeBoard = __esm({
44667
44751
  "components/game/organisms/RoguelikeBoard.tsx"() {
44668
44752
  "use client";
@@ -44675,39 +44759,19 @@ var init_RoguelikeBoard = __esm({
44675
44759
  init_IsometricCanvas();
44676
44760
  init_boardEntity();
44677
44761
  CDN4 = "https://almadar-kflow-assets.web.app/shared";
44678
- DEFAULT_TILES2 = [
44679
- { x: 0, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
44680
- { x: 1, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
44681
- { x: 2, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
44682
- { x: 3, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
44683
- { x: 4, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
44684
- { x: 0, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
44685
- { x: 1, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
44686
- { x: 2, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
44687
- { x: 3, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
44688
- { x: 4, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
44689
- { x: 0, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
44690
- { x: 1, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png` },
44691
- { x: 2, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png` },
44692
- { x: 3, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
44693
- { x: 4, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
44694
- { x: 0, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
44695
- { x: 1, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
44696
- { x: 2, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png` },
44697
- { x: 3, y: 3, terrain: "stairs", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stairs_E.png` },
44698
- { x: 4, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
44699
- { x: 0, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
44700
- { x: 1, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
44701
- { x: 2, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
44702
- { x: 3, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
44703
- { x: 4, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` }
44704
- ];
44762
+ DUNGEON_GRID_W = 16;
44763
+ DUNGEON_GRID_H = 16;
44764
+ buildDefaultDungeonTiles(14, 14);
44705
44765
  DEFAULT_ENEMIES = [
44706
- { id: "e1", x: 3, y: 1, hp: 5, attack: 2 },
44707
- { id: "e2", x: 1, y: 3, hp: 4, attack: 1 }
44766
+ { id: "e1", x: 5, y: 2, hp: 5, attack: 2 },
44767
+ { id: "e2", x: 9, y: 6, hp: 4, attack: 1 },
44768
+ { id: "e3", x: 3, y: 10, hp: 6, attack: 2 },
44769
+ { id: "e4", x: 12, y: 8, hp: 5, attack: 3 }
44708
44770
  ];
44709
44771
  DEFAULT_ITEMS = [
44710
- { id: "i1", x: 2, y: 2, kind: "health_potion" }
44772
+ { id: "i1", x: 3, y: 3, kind: "health_potion" },
44773
+ { id: "i2", x: 7, y: 9, kind: "sword" },
44774
+ { id: "i3", x: 11, y: 5, kind: "shield" }
44711
44775
  ];
44712
44776
  FEATURE_SPRITE = {
44713
44777
  health_potion: `${CDN4}/isometric-dungeon/Isometric/chestClosed_E.png`,
@@ -48934,6 +48998,22 @@ var init_ToastSlot = __esm({
48934
48998
  ToastSlot.displayName = "ToastSlot";
48935
48999
  }
48936
49000
  });
49001
+ function buildDefaultTDTiles() {
49002
+ const pathSet = new Set(DEFAULT_TD_PATH.map((p2) => `${p2.x},${p2.y}`));
49003
+ const tiles = [];
49004
+ for (let y = 0; y < TD_GRID_H; y++) {
49005
+ for (let x = 0; x < TD_GRID_W; x++) {
49006
+ if (pathSet.has(`${x},${y}`)) {
49007
+ tiles.push({ x, y, terrain: "path", passable: false, terrainSprite: TERRAIN_SPRITES.path });
49008
+ } else {
49009
+ const variant = (x * 3 + y * 5 + (x ^ y)) % 3;
49010
+ const terrainKey = ["ground", "grass", "dirt"][variant];
49011
+ tiles.push({ x, y, terrain: terrainKey, passable: true, terrainSprite: TERRAIN_SPRITES[terrainKey] ?? TERRAIN_SPRITES.ground });
49012
+ }
49013
+ }
49014
+ }
49015
+ return tiles;
49016
+ }
48937
49017
  function tilesToIso(tiles) {
48938
49018
  return tiles.map((t) => ({
48939
49019
  x: t.x,
@@ -48989,7 +49069,10 @@ function TowerDefenseBoard({
48989
49069
  result: propResult,
48990
49070
  waveActive: propWaveActive,
48991
49071
  towerCost = 25,
48992
- scale = 0.45,
49072
+ scale = 0.25,
49073
+ unitScale = 1,
49074
+ spriteHeightRatio = 1.5,
49075
+ spriteMaxWidthRatio = 0.6,
48993
49076
  placeTowerEvent,
48994
49077
  startWaveEvent,
48995
49078
  playAgainEvent,
@@ -48999,8 +49082,10 @@ function TowerDefenseBoard({
48999
49082
  const board = boardEntity(entity) ?? {};
49000
49083
  const eventBus = useEventBus();
49001
49084
  const { t } = hooks.useTranslate();
49002
- const tiles = propTiles ?? rows(board.tiles);
49003
- const path = propPath ?? rows(board.path);
49085
+ const rawTiles = propTiles ?? rows(board.tiles);
49086
+ const tiles = rawTiles.length >= TD_GRID_W * TD_GRID_H ? rawTiles : DEFAULT_TD_TILES;
49087
+ const rawPath = propPath ?? rows(board.path);
49088
+ const path = rawPath.length > 0 ? rawPath : DEFAULT_TD_PATH;
49004
49089
  const towers = propTowers ?? rows(board.towers);
49005
49090
  const creeps = propCreeps ?? rows(board.creeps);
49006
49091
  const gold = propGold ?? num(board.gold, 100);
@@ -49097,7 +49182,10 @@ function TowerDefenseBoard({
49097
49182
  onTileClick: handleTileClick,
49098
49183
  onTileHover: (x, y) => setHoveredTile({ x, y }),
49099
49184
  onTileLeave: () => setHoveredTile(null),
49100
- scale
49185
+ scale,
49186
+ unitScale,
49187
+ spriteHeightRatio,
49188
+ spriteMaxWidthRatio
49101
49189
  }
49102
49190
  ),
49103
49191
  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)` }) })
@@ -49127,7 +49215,7 @@ function TowerDefenseBoard({
49127
49215
  ] }) })
49128
49216
  ] });
49129
49217
  }
49130
- var CDN6, TERRAIN_SPRITES, TOWER_SPRITE, CREEP_SPRITE;
49218
+ var CDN6, TD_GRID_W, TD_GRID_H, TERRAIN_SPRITES, DEFAULT_TD_PATH, DEFAULT_TD_TILES, TOWER_SPRITE, CREEP_SPRITE;
49131
49219
  var init_TowerDefenseBoard = __esm({
49132
49220
  "components/game/organisms/TowerDefenseBoard.tsx"() {
49133
49221
  "use client";
@@ -49140,11 +49228,67 @@ var init_TowerDefenseBoard = __esm({
49140
49228
  init_IsometricCanvas();
49141
49229
  init_boardEntity();
49142
49230
  CDN6 = "https://almadar-kflow-assets.web.app/shared/";
49231
+ TD_GRID_W = 16;
49232
+ TD_GRID_H = 16;
49143
49233
  TERRAIN_SPRITES = {
49144
49234
  ground: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_01.png`,
49145
49235
  path: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_09.png`,
49146
- wall: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_05.png`
49147
- };
49236
+ wall: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_05.png`,
49237
+ grass: `${CDN6}isometric-dungeon/Isometric/dirt_E.png`,
49238
+ dirt: `${CDN6}isometric-dungeon/Isometric/dirtTiles_E.png`
49239
+ };
49240
+ DEFAULT_TD_PATH = [
49241
+ { x: 2, y: 0 },
49242
+ { x: 2, y: 1 },
49243
+ { x: 2, y: 2 },
49244
+ { x: 2, y: 3 },
49245
+ { x: 3, y: 3 },
49246
+ { x: 4, y: 3 },
49247
+ { x: 5, y: 3 },
49248
+ { x: 6, y: 3 },
49249
+ { x: 7, y: 3 },
49250
+ { x: 8, y: 3 },
49251
+ { x: 9, y: 3 },
49252
+ { x: 10, y: 3 },
49253
+ { x: 11, y: 3 },
49254
+ { x: 12, y: 3 },
49255
+ { x: 13, y: 3 },
49256
+ { x: 13, y: 4 },
49257
+ { x: 13, y: 5 },
49258
+ { x: 13, y: 6 },
49259
+ { x: 13, y: 7 },
49260
+ { x: 12, y: 7 },
49261
+ { x: 11, y: 7 },
49262
+ { x: 10, y: 7 },
49263
+ { x: 9, y: 7 },
49264
+ { x: 8, y: 7 },
49265
+ { x: 7, y: 7 },
49266
+ { x: 6, y: 7 },
49267
+ { x: 5, y: 7 },
49268
+ { x: 4, y: 7 },
49269
+ { x: 3, y: 7 },
49270
+ { x: 2, y: 7 },
49271
+ { x: 2, y: 8 },
49272
+ { x: 2, y: 9 },
49273
+ { x: 2, y: 10 },
49274
+ { x: 2, y: 11 },
49275
+ { x: 3, y: 11 },
49276
+ { x: 4, y: 11 },
49277
+ { x: 5, y: 11 },
49278
+ { x: 6, y: 11 },
49279
+ { x: 7, y: 11 },
49280
+ { x: 8, y: 11 },
49281
+ { x: 9, y: 11 },
49282
+ { x: 10, y: 11 },
49283
+ { x: 11, y: 11 },
49284
+ { x: 12, y: 11 },
49285
+ { x: 13, y: 11 },
49286
+ { x: 13, y: 12 },
49287
+ { x: 13, y: 13 },
49288
+ { x: 13, y: 14 },
49289
+ { x: 13, y: 15 }
49290
+ ];
49291
+ DEFAULT_TD_TILES = buildDefaultTDTiles();
49148
49292
  TOWER_SPRITE = `${CDN6}units/guardian.png`;
49149
49293
  CREEP_SPRITE = `${CDN6}units/scrapper.png`;
49150
49294
  TowerDefenseBoard.displayName = "TowerDefenseBoard";
@@ -49421,6 +49565,17 @@ function heroPosition(h) {
49421
49565
  function heroMovement(h) {
49422
49566
  return num(h.movement);
49423
49567
  }
49568
+ function buildDefaultWorldTiles() {
49569
+ const tiles = [];
49570
+ for (let y = 0; y < WORLD_GRID_H; y++) {
49571
+ for (let x = 0; x < WORLD_GRID_W; x++) {
49572
+ const isBorder = x === 0 || y === 0 || x === WORLD_GRID_W - 1 || y === WORLD_GRID_H - 1;
49573
+ const def = isBorder ? WORLD_TERRAIN_DEFS[3] : WORLD_TERRAIN_DEFS[(x * 5 + y * 3 + (x ^ y)) % (WORLD_TERRAIN_DEFS.length - 1)];
49574
+ tiles.push({ x, y, terrain: def.terrain, terrainSprite: def.sprite, passable: def.passable });
49575
+ }
49576
+ }
49577
+ return tiles;
49578
+ }
49424
49579
  function defaultIsInRange(from, to, range) {
49425
49580
  return Math.abs(from.x - to.x) + Math.abs(from.y - to.y) <= range;
49426
49581
  }
@@ -49431,8 +49586,10 @@ function WorldMapBoard({
49431
49586
  features: propFeatures,
49432
49587
  assetManifest: propAssetManifest,
49433
49588
  isLoading,
49434
- scale = 0.4,
49589
+ scale = 0.25,
49435
49590
  unitScale = 2.5,
49591
+ spriteHeightRatio = 1.5,
49592
+ spriteMaxWidthRatio = 0.6,
49436
49593
  allowMoveAllHeroes = false,
49437
49594
  isInRange = defaultIsInRange,
49438
49595
  heroSelectEvent,
@@ -49473,7 +49630,8 @@ function WorldMapBoard({
49473
49630
  })),
49474
49631
  [entityTiles]
49475
49632
  );
49476
- const tiles = propTiles ?? derivedTiles;
49633
+ const rawTiles = propTiles ?? (derivedTiles.length > 0 ? derivedTiles : null);
49634
+ const tiles = rawTiles != null && rawTiles.length >= WORLD_GRID_W * WORLD_GRID_H ? rawTiles : DEFAULT_WORLD_TILES;
49477
49635
  const baseUnits = React81.useMemo(
49478
49636
  () => propUnits ?? entityUnits.map((u) => ({
49479
49637
  id: str(u.id),
@@ -49670,6 +49828,8 @@ function WorldMapBoard({
49670
49828
  effectSpriteUrls,
49671
49829
  resolveUnitFrame,
49672
49830
  unitScale,
49831
+ spriteHeightRatio,
49832
+ spriteMaxWidthRatio,
49673
49833
  diamondTopY,
49674
49834
  enableCamera
49675
49835
  }
@@ -49681,6 +49841,7 @@ function WorldMapBoard({
49681
49841
  footer && footer(ctx)
49682
49842
  ] });
49683
49843
  }
49844
+ var WORLD_CDN, WORLD_GRID_W, WORLD_GRID_H, WORLD_TERRAIN_DEFS, DEFAULT_WORLD_TILES;
49684
49845
  var init_WorldMapBoard = __esm({
49685
49846
  "components/game/organisms/WorldMapBoard.tsx"() {
49686
49847
  "use client";
@@ -49691,6 +49852,17 @@ var init_WorldMapBoard = __esm({
49691
49852
  init_IsometricCanvas();
49692
49853
  init_boardEntity();
49693
49854
  init_isometric();
49855
+ WORLD_CDN = "https://almadar-kflow-assets.web.app/shared";
49856
+ WORLD_GRID_W = 16;
49857
+ WORLD_GRID_H = 16;
49858
+ WORLD_TERRAIN_DEFS = [
49859
+ { terrain: "grass", sprite: `${WORLD_CDN}/isometric-dungeon/Isometric/dirt_E.png`, passable: true },
49860
+ { terrain: "dirt", sprite: `${WORLD_CDN}/isometric-dungeon/Isometric/dirtTiles_E.png`, passable: true },
49861
+ { terrain: "forest", sprite: `${WORLD_CDN}/isometric-dungeon/Isometric/planks_E.png`, passable: true },
49862
+ { terrain: "stone", sprite: `${WORLD_CDN}/isometric-dungeon/Isometric/stoneInset_E.png`, passable: false },
49863
+ { terrain: "castle", sprite: `${WORLD_CDN}/isometric-dungeon/Isometric/stoneTile_E.png`, passable: true }
49864
+ ];
49865
+ DEFAULT_WORLD_TILES = buildDefaultWorldTiles();
49694
49866
  WorldMapBoard.displayName = "WorldMapBoard";
49695
49867
  }
49696
49868
  });
@@ -51911,7 +52083,10 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
51911
52083
  if (binding.config) {
51912
52084
  guardCtx.config = binding.config;
51913
52085
  }
51914
- const passed = runtime.interpolateValue(tick.guard, runtime.createContextFromBindings(guardCtx));
52086
+ const passed = evaluator.evaluateGuard(
52087
+ tick.guard,
52088
+ runtime.createContextFromBindings(guardCtx)
52089
+ );
51915
52090
  if (!passed) {
51916
52091
  tickLog.debug("guard-blocked", { traitName, tick: tick.name, state: currentState });
51917
52092
  return;