@almadar/ui 5.56.0 → 5.58.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.
package/dist/avl/index.js CHANGED
@@ -15303,13 +15303,29 @@ var init_boardEntity = __esm({
15303
15303
  "components/game/organisms/boardEntity.ts"() {
15304
15304
  }
15305
15305
  });
15306
+ function buildDefaultBattleTiles() {
15307
+ const tiles = [];
15308
+ for (let y = 0; y < BATTLE_GRID_H; y++) {
15309
+ for (let x = 0; x < BATTLE_GRID_W; x++) {
15310
+ const variant = (x * 3 + y * 7 + (x ^ y)) % BATTLE_TERRAIN_SPRITES.length;
15311
+ tiles.push({
15312
+ x,
15313
+ y,
15314
+ terrain: ["grass", "dirt", "planks", "stone", "stone"][variant],
15315
+ terrainSprite: BATTLE_TERRAIN_SPRITES[variant],
15316
+ passable: true
15317
+ });
15318
+ }
15319
+ }
15320
+ return tiles;
15321
+ }
15306
15322
  function BattleBoard({
15307
15323
  entity,
15308
15324
  tiles: propTiles,
15309
15325
  units: propUnits,
15310
15326
  features: propFeatures,
15311
15327
  assetManifest: propAssetManifest,
15312
- scale = 0.45,
15328
+ scale = 0.25,
15313
15329
  unitScale = 1,
15314
15330
  spriteHeightRatio = 1.5,
15315
15331
  spriteMaxWidthRatio = 0.6,
@@ -15336,10 +15352,11 @@ function BattleBoard({
15336
15352
  className
15337
15353
  }) {
15338
15354
  const board = boardEntity(entity) ?? {};
15339
- const tiles = propTiles ?? (Array.isArray(board.tiles) ? board.tiles : []);
15355
+ const rawTiles = propTiles ?? (Array.isArray(board.tiles) ? board.tiles : []);
15356
+ const tiles = rawTiles.length === 0 ? DEFAULT_BATTLE_TILES : rawTiles;
15340
15357
  const features = propFeatures ?? (Array.isArray(board.features) ? board.features : []);
15341
- const boardWidth = num(board.gridWidth ?? board.boardWidth, 8);
15342
- const boardHeight = num(board.gridHeight ?? board.boardHeight, 6);
15358
+ const boardWidth = num(board.gridWidth ?? board.boardWidth, BATTLE_GRID_W);
15359
+ const boardHeight = num(board.gridHeight ?? board.boardHeight, BATTLE_GRID_H);
15343
15360
  const assetManifest = propAssetManifest ?? board.assetManifest;
15344
15361
  const backgroundImage = board.backgroundImage;
15345
15362
  const units = rows(board.units);
@@ -15661,6 +15678,7 @@ function BattleBoard({
15661
15678
  ] }) }))
15662
15679
  ] });
15663
15680
  }
15681
+ var BATTLE_CDN, BATTLE_GRID_W, BATTLE_GRID_H, BATTLE_TERRAIN_SPRITES, DEFAULT_BATTLE_TILES;
15664
15682
  var init_BattleBoard = __esm({
15665
15683
  "components/game/organisms/BattleBoard.tsx"() {
15666
15684
  "use client";
@@ -15673,6 +15691,17 @@ var init_BattleBoard = __esm({
15673
15691
  init_IsometricCanvas();
15674
15692
  init_boardEntity();
15675
15693
  init_isometric();
15694
+ BATTLE_CDN = "https://almadar-kflow-assets.web.app/shared";
15695
+ BATTLE_GRID_W = 16;
15696
+ BATTLE_GRID_H = 16;
15697
+ BATTLE_TERRAIN_SPRITES = [
15698
+ `${BATTLE_CDN}/isometric-dungeon/Isometric/dirt_E.png`,
15699
+ `${BATTLE_CDN}/isometric-dungeon/Isometric/dirtTiles_E.png`,
15700
+ `${BATTLE_CDN}/isometric-dungeon/Isometric/planks_E.png`,
15701
+ `${BATTLE_CDN}/isometric-dungeon/Isometric/stoneTile_E.png`,
15702
+ `${BATTLE_CDN}/isometric-dungeon/Isometric/stoneInset_E.png`
15703
+ ];
15704
+ DEFAULT_BATTLE_TILES = buildDefaultBattleTiles();
15676
15705
  BattleBoard.displayName = "BattleBoard";
15677
15706
  }
15678
15707
  });
@@ -15680,7 +15709,7 @@ function BattleTemplate({
15680
15709
  entity,
15681
15710
  scale = 0.45,
15682
15711
  unitScale = 1,
15683
- tiles = DEFAULT_BATTLE_TILES,
15712
+ tiles = DEFAULT_BATTLE_TILES2,
15684
15713
  units = DEFAULT_BATTLE_UNITS,
15685
15714
  features = DEFAULT_BATTLE_FEATURES,
15686
15715
  assetManifest = DEFAULT_BATTLE_MANIFEST,
@@ -15709,12 +15738,12 @@ function BattleTemplate({
15709
15738
  }
15710
15739
  );
15711
15740
  }
15712
- var CDN, DEFAULT_BATTLE_TILES, DEFAULT_BATTLE_UNITS, DEFAULT_BATTLE_FEATURES, DEFAULT_BATTLE_MANIFEST;
15741
+ var CDN, DEFAULT_BATTLE_TILES2, DEFAULT_BATTLE_UNITS, DEFAULT_BATTLE_FEATURES, DEFAULT_BATTLE_MANIFEST;
15713
15742
  var init_BattleTemplate = __esm({
15714
15743
  "components/game/templates/BattleTemplate.tsx"() {
15715
15744
  init_BattleBoard();
15716
15745
  CDN = "https://almadar-kflow-assets.web.app/shared";
15717
- DEFAULT_BATTLE_TILES = [
15746
+ DEFAULT_BATTLE_TILES2 = [
15718
15747
  { x: 0, y: 0, terrain: "stone", passable: false, terrainSprite: `${CDN}/isometric-blocks/PNG/Abstract tiles/abstractTile_07.png` },
15719
15748
  { x: 1, y: 0, terrain: "stone", passable: false, terrainSprite: `${CDN}/isometric-blocks/PNG/Abstract tiles/abstractTile_07.png` },
15720
15749
  { x: 2, y: 0, terrain: "stone", passable: false, terrainSprite: `${CDN}/isometric-blocks/PNG/Abstract tiles/abstractTile_07.png` },
@@ -32054,13 +32083,13 @@ var init_MapView = __esm({
32054
32083
  shadowSize: [41, 41]
32055
32084
  });
32056
32085
  L.Marker.prototype.options.icon = defaultIcon;
32057
- const { useEffect: useEffect85, useRef: useRef77, useCallback: useCallback125, useState: useState121 } = React90__default;
32086
+ const { useEffect: useEffect86, useRef: useRef77, useCallback: useCallback125, useState: useState121 } = React90__default;
32058
32087
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
32059
32088
  const { useEventBus: useEventBus4 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
32060
32089
  function MapUpdater({ centerLat, centerLng, zoom }) {
32061
32090
  const map = useMap();
32062
32091
  const prevRef = useRef77({ centerLat, centerLng, zoom });
32063
- useEffect85(() => {
32092
+ useEffect86(() => {
32064
32093
  const prev = prevRef.current;
32065
32094
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
32066
32095
  map.setView([centerLat, centerLng], zoom);
@@ -32071,7 +32100,7 @@ var init_MapView = __esm({
32071
32100
  }
32072
32101
  function MapClickHandler({ onMapClick }) {
32073
32102
  const map = useMap();
32074
- useEffect85(() => {
32103
+ useEffect86(() => {
32075
32104
  if (!onMapClick) return;
32076
32105
  const handler = (e) => {
32077
32106
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -44339,7 +44368,6 @@ var init_GameCanvas3D2 = __esm({
44339
44368
  selectedTileIds = [],
44340
44369
  selectedUnitId = null,
44341
44370
  unitScale = 1,
44342
- scale = 0.45,
44343
44371
  children
44344
44372
  }, ref) => {
44345
44373
  const containerRef = useRef(null);
@@ -44786,7 +44814,7 @@ var init_GameCanvas3D2 = __esm({
44786
44814
  fadeStrength: 1
44787
44815
  }
44788
44816
  ),
44789
- /* @__PURE__ */ jsxs("group", { scale, children: [
44817
+ /* @__PURE__ */ jsxs("group", { children: [
44790
44818
  tiles.map((tile, index) => {
44791
44819
  const position = gridToWorld(
44792
44820
  tile.x,
@@ -46929,6 +46957,35 @@ var init_PricingPageTemplate = __esm({
46929
46957
  PricingPageTemplate.displayName = "PricingPageTemplate";
46930
46958
  }
46931
46959
  });
46960
+ function dungeonTerrain(x, y, stairsX, stairsY) {
46961
+ const isBorder = x === 0 || y === 0 || x === DUNGEON_GRID_W - 1 || y === DUNGEON_GRID_H - 1;
46962
+ const isPillar = x % 4 === 0 && y % 4 === 0 && !isBorder;
46963
+ const isWall = isBorder || isPillar;
46964
+ if (x === stairsX && y === stairsY) {
46965
+ return { terrain: "stairs", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stairs_E.png` };
46966
+ }
46967
+ if (isWall) {
46968
+ return { terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` };
46969
+ }
46970
+ const variant = (x * 3 + y * 7) % 5;
46971
+ const sprites = [
46972
+ `${CDN4}/isometric-dungeon/Isometric/dirt_E.png`,
46973
+ `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png`,
46974
+ `${CDN4}/isometric-dungeon/Isometric/planks_E.png`,
46975
+ `${CDN4}/isometric-dungeon/Isometric/stoneTile_E.png`,
46976
+ `${CDN4}/isometric-dungeon/Isometric/dirt_E.png`
46977
+ ];
46978
+ return { terrain: "floor", passable: true, terrainSprite: sprites[variant] };
46979
+ }
46980
+ function buildDefaultDungeonTiles(stairsX, stairsY) {
46981
+ const tiles = [];
46982
+ for (let y = 0; y < DUNGEON_GRID_H; y++) {
46983
+ for (let x = 0; x < DUNGEON_GRID_W; x++) {
46984
+ tiles.push({ x, y, ...dungeonTerrain(x, y, stairsX, stairsY) });
46985
+ }
46986
+ }
46987
+ return tiles;
46988
+ }
46932
46989
  function RoguelikeBoard({
46933
46990
  entity,
46934
46991
  tiles: propTiles,
@@ -46944,7 +47001,7 @@ function RoguelikeBoard({
46944
47001
  stairsX: propStairsX,
46945
47002
  stairsY: propStairsY,
46946
47003
  assetManifest: propAssetManifest,
46947
- scale = 0.45,
47004
+ scale = 0.25,
46948
47005
  unitScale = 1,
46949
47006
  spriteHeightRatio = 1.5,
46950
47007
  spriteMaxWidthRatio = 0.6,
@@ -46954,7 +47011,9 @@ function RoguelikeBoard({
46954
47011
  className
46955
47012
  }) {
46956
47013
  const board = boardEntity(entity) ?? {};
46957
- const tiles = propTiles ?? (Array.isArray(board.tiles) ? board.tiles : DEFAULT_TILES2);
47014
+ const resolvedStairsX = propStairsX ?? num(board.stairsX, 14);
47015
+ const resolvedStairsY = propStairsY ?? num(board.stairsY, 14);
47016
+ const tiles = propTiles ?? (Array.isArray(board.tiles) && board.tiles.length >= DUNGEON_GRID_W * DUNGEON_GRID_H ? board.tiles : buildDefaultDungeonTiles(resolvedStairsX, resolvedStairsY));
46958
47017
  const enemies = propEnemies ?? (Array.isArray(board.enemies) ? board.enemies : DEFAULT_ENEMIES);
46959
47018
  const items = propItems ?? (Array.isArray(board.items) ? board.items : DEFAULT_ITEMS);
46960
47019
  const player = propPlayer ?? board.player ?? { x: 1, y: 1 };
@@ -46964,8 +47023,8 @@ function RoguelikeBoard({
46964
47023
  const depth = propDepth ?? num(board.depth, 1);
46965
47024
  const result = propResult ?? (str(board.result) || "none");
46966
47025
  const phase = propPhase ?? (str(board.phase) || "player_turn");
46967
- propStairsX ?? num(board.stairsX, 3);
46968
- propStairsY ?? num(board.stairsY, 3);
47026
+ propStairsX ?? num(board.stairsX, 14);
47027
+ propStairsY ?? num(board.stairsY, 14);
46969
47028
  const eventBus = useEventBus();
46970
47029
  const { t } = useTranslate();
46971
47030
  const [hoveredTile, setHoveredTile] = useState(null);
@@ -47141,7 +47200,7 @@ function RoguelikeBoard({
47141
47200
  ] }) })
47142
47201
  ] });
47143
47202
  }
47144
- var CDN4, DEFAULT_TILES2, DEFAULT_ENEMIES, DEFAULT_ITEMS, FEATURE_SPRITE;
47203
+ var CDN4, DUNGEON_GRID_W, DUNGEON_GRID_H, DEFAULT_ENEMIES, DEFAULT_ITEMS, FEATURE_SPRITE;
47145
47204
  var init_RoguelikeBoard = __esm({
47146
47205
  "components/game/organisms/RoguelikeBoard.tsx"() {
47147
47206
  "use client";
@@ -47154,39 +47213,19 @@ var init_RoguelikeBoard = __esm({
47154
47213
  init_IsometricCanvas();
47155
47214
  init_boardEntity();
47156
47215
  CDN4 = "https://almadar-kflow-assets.web.app/shared";
47157
- DEFAULT_TILES2 = [
47158
- { x: 0, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
47159
- { x: 1, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
47160
- { x: 2, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
47161
- { x: 3, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
47162
- { x: 4, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
47163
- { x: 0, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
47164
- { x: 1, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
47165
- { x: 2, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
47166
- { x: 3, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
47167
- { x: 4, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
47168
- { x: 0, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
47169
- { x: 1, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png` },
47170
- { x: 2, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png` },
47171
- { x: 3, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
47172
- { x: 4, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
47173
- { x: 0, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
47174
- { x: 1, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
47175
- { x: 2, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png` },
47176
- { x: 3, y: 3, terrain: "stairs", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stairs_E.png` },
47177
- { x: 4, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
47178
- { x: 0, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
47179
- { x: 1, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
47180
- { x: 2, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
47181
- { x: 3, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
47182
- { x: 4, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` }
47183
- ];
47216
+ DUNGEON_GRID_W = 16;
47217
+ DUNGEON_GRID_H = 16;
47218
+ buildDefaultDungeonTiles(14, 14);
47184
47219
  DEFAULT_ENEMIES = [
47185
- { id: "e1", x: 3, y: 1, hp: 5, attack: 2 },
47186
- { id: "e2", x: 1, y: 3, hp: 4, attack: 1 }
47220
+ { id: "e1", x: 5, y: 2, hp: 5, attack: 2 },
47221
+ { id: "e2", x: 9, y: 6, hp: 4, attack: 1 },
47222
+ { id: "e3", x: 3, y: 10, hp: 6, attack: 2 },
47223
+ { id: "e4", x: 12, y: 8, hp: 5, attack: 3 }
47187
47224
  ];
47188
47225
  DEFAULT_ITEMS = [
47189
- { id: "i1", x: 2, y: 2, kind: "health_potion" }
47226
+ { id: "i1", x: 3, y: 3, kind: "health_potion" },
47227
+ { id: "i2", x: 7, y: 9, kind: "sword" },
47228
+ { id: "i3", x: 11, y: 5, kind: "shield" }
47190
47229
  ];
47191
47230
  FEATURE_SPRITE = {
47192
47231
  health_potion: `${CDN4}/isometric-dungeon/Isometric/chestClosed_E.png`,
@@ -51413,6 +51452,22 @@ var init_ToastSlot = __esm({
51413
51452
  ToastSlot.displayName = "ToastSlot";
51414
51453
  }
51415
51454
  });
51455
+ function buildDefaultTDTiles() {
51456
+ const pathSet = new Set(DEFAULT_TD_PATH.map((p2) => `${p2.x},${p2.y}`));
51457
+ const tiles = [];
51458
+ for (let y = 0; y < TD_GRID_H; y++) {
51459
+ for (let x = 0; x < TD_GRID_W; x++) {
51460
+ if (pathSet.has(`${x},${y}`)) {
51461
+ tiles.push({ x, y, terrain: "path", passable: false, terrainSprite: TERRAIN_SPRITES.path });
51462
+ } else {
51463
+ const variant = (x * 3 + y * 5 + (x ^ y)) % 3;
51464
+ const terrainKey = ["ground", "grass", "dirt"][variant];
51465
+ tiles.push({ x, y, terrain: terrainKey, passable: true, terrainSprite: TERRAIN_SPRITES[terrainKey] ?? TERRAIN_SPRITES.ground });
51466
+ }
51467
+ }
51468
+ }
51469
+ return tiles;
51470
+ }
51416
51471
  function tilesToIso(tiles) {
51417
51472
  return tiles.map((t) => ({
51418
51473
  x: t.x,
@@ -51455,12 +51510,25 @@ function pathToFeatures(path) {
51455
51510
  sprite: `${CDN6}world-map/road_straight.png`
51456
51511
  }));
51457
51512
  }
51513
+ function heroToUnit(hero) {
51514
+ return {
51515
+ id: hero.id,
51516
+ position: { x: hero.x, y: hero.y },
51517
+ name: "Hero",
51518
+ team: "player",
51519
+ sprite: HERO_SPRITE,
51520
+ unitType: "amir",
51521
+ health: 1,
51522
+ maxHealth: 1
51523
+ };
51524
+ }
51458
51525
  function TowerDefenseBoard({
51459
51526
  entity,
51460
51527
  tiles: propTiles,
51461
51528
  path: propPath,
51462
51529
  towers: propTowers,
51463
51530
  creeps: propCreeps,
51531
+ hero: propHero,
51464
51532
  gold: propGold,
51465
51533
  lives: propLives,
51466
51534
  wave: propWave,
@@ -51468,7 +51536,7 @@ function TowerDefenseBoard({
51468
51536
  result: propResult,
51469
51537
  waveActive: propWaveActive,
51470
51538
  towerCost = 25,
51471
- scale = 0.45,
51539
+ scale = 0.25,
51472
51540
  unitScale = 1,
51473
51541
  spriteHeightRatio = 1.5,
51474
51542
  spriteMaxWidthRatio = 0.6,
@@ -51476,15 +51544,19 @@ function TowerDefenseBoard({
51476
51544
  startWaveEvent,
51477
51545
  playAgainEvent,
51478
51546
  gameEndEvent,
51547
+ moveHeroEvent,
51479
51548
  className
51480
51549
  }) {
51481
51550
  const board = boardEntity(entity) ?? {};
51482
51551
  const eventBus = useEventBus();
51483
51552
  const { t } = useTranslate();
51484
- const tiles = propTiles ?? rows(board.tiles);
51485
- const path = propPath ?? rows(board.path);
51553
+ const rawTiles = propTiles ?? rows(board.tiles);
51554
+ const tiles = rawTiles.length >= TD_GRID_W * TD_GRID_H ? rawTiles : DEFAULT_TD_TILES;
51555
+ const rawPath = propPath ?? rows(board.path);
51556
+ const path = rawPath.length > 0 ? rawPath : DEFAULT_TD_PATH;
51486
51557
  const towers = propTowers ?? rows(board.towers);
51487
51558
  const creeps = propCreeps ?? rows(board.creeps);
51559
+ const hero = propHero ?? { id: "hero", x: 8, y: 8 };
51488
51560
  const gold = propGold ?? num(board.gold, 100);
51489
51561
  const lives = propLives ?? num(board.lives, 20);
51490
51562
  const wave = propWave ?? num(board.wave, 1);
@@ -51502,6 +51574,31 @@ function TowerDefenseBoard({
51502
51574
  if (result === "none") {
51503
51575
  emittedGameEnd.current = false;
51504
51576
  }
51577
+ const moveHeroEventRef = useRef(moveHeroEvent);
51578
+ moveHeroEventRef.current = moveHeroEvent;
51579
+ const resultRef = useRef(result);
51580
+ resultRef.current = result;
51581
+ useEffect(() => {
51582
+ function onKeyDown(e) {
51583
+ const evt = moveHeroEventRef.current;
51584
+ if (!evt || resultRef.current !== "none") return;
51585
+ let dx = 0;
51586
+ let dy = 0;
51587
+ if (e.key === "ArrowUp") {
51588
+ dy = -1;
51589
+ } else if (e.key === "ArrowDown") {
51590
+ dy = 1;
51591
+ } else if (e.key === "ArrowLeft") {
51592
+ dx = -1;
51593
+ } else if (e.key === "ArrowRight") {
51594
+ dx = 1;
51595
+ } else return;
51596
+ e.preventDefault();
51597
+ eventBus.emit(`UI:${evt}`, { dx, dy });
51598
+ }
51599
+ window.addEventListener("keydown", onKeyDown);
51600
+ return () => window.removeEventListener("keydown", onKeyDown);
51601
+ }, [eventBus]);
51505
51602
  const towerPositions = useMemo(() => new Set(towers.map((t2) => `${t2.x},${t2.y}`)), [towers]);
51506
51603
  const pathPositions = useMemo(() => new Set(path.map((p2) => `${p2.x},${p2.y}`)), [path]);
51507
51604
  const validMoves = useMemo(() => {
@@ -51513,7 +51610,8 @@ function TowerDefenseBoard({
51513
51610
  const isoTiles = useMemo(() => tilesToIso(tiles), [tiles]);
51514
51611
  const towerUnits = useMemo(() => towersToUnits(towers), [towers]);
51515
51612
  const creepUnits = useMemo(() => creepsToUnits(creeps), [creeps]);
51516
- const isoUnits = useMemo(() => [...towerUnits, ...creepUnits], [towerUnits, creepUnits]);
51613
+ const heroUnit = useMemo(() => heroToUnit(hero), [hero]);
51614
+ const isoUnits = useMemo(() => [...towerUnits, ...creepUnits, heroUnit], [towerUnits, creepUnits, heroUnit]);
51517
51615
  const pathFeatures = useMemo(() => pathToFeatures(path), [path]);
51518
51616
  const handleTileClick = useCallback((x, y) => {
51519
51617
  if (result !== "none") return;
@@ -51612,7 +51710,7 @@ function TowerDefenseBoard({
51612
51710
  ] }) })
51613
51711
  ] });
51614
51712
  }
51615
- var CDN6, TERRAIN_SPRITES, TOWER_SPRITE, CREEP_SPRITE;
51713
+ var CDN6, TD_GRID_W, TD_GRID_H, TERRAIN_SPRITES, DEFAULT_TD_PATH, DEFAULT_TD_TILES, TOWER_SPRITE, CREEP_SPRITE, HERO_SPRITE;
51616
51714
  var init_TowerDefenseBoard = __esm({
51617
51715
  "components/game/organisms/TowerDefenseBoard.tsx"() {
51618
51716
  "use client";
@@ -51625,13 +51723,70 @@ var init_TowerDefenseBoard = __esm({
51625
51723
  init_IsometricCanvas();
51626
51724
  init_boardEntity();
51627
51725
  CDN6 = "https://almadar-kflow-assets.web.app/shared/";
51726
+ TD_GRID_W = 16;
51727
+ TD_GRID_H = 16;
51628
51728
  TERRAIN_SPRITES = {
51629
51729
  ground: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_01.png`,
51630
51730
  path: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_09.png`,
51631
- wall: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_05.png`
51632
- };
51633
- TOWER_SPRITE = `${CDN6}units/guardian.png`;
51634
- CREEP_SPRITE = `${CDN6}units/scrapper.png`;
51731
+ wall: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_05.png`,
51732
+ grass: `${CDN6}isometric-dungeon/Isometric/dirt_E.png`,
51733
+ dirt: `${CDN6}isometric-dungeon/Isometric/dirtTiles_E.png`
51734
+ };
51735
+ DEFAULT_TD_PATH = [
51736
+ { x: 2, y: 0 },
51737
+ { x: 2, y: 1 },
51738
+ { x: 2, y: 2 },
51739
+ { x: 2, y: 3 },
51740
+ { x: 3, y: 3 },
51741
+ { x: 4, y: 3 },
51742
+ { x: 5, y: 3 },
51743
+ { x: 6, y: 3 },
51744
+ { x: 7, y: 3 },
51745
+ { x: 8, y: 3 },
51746
+ { x: 9, y: 3 },
51747
+ { x: 10, y: 3 },
51748
+ { x: 11, y: 3 },
51749
+ { x: 12, y: 3 },
51750
+ { x: 13, y: 3 },
51751
+ { x: 13, y: 4 },
51752
+ { x: 13, y: 5 },
51753
+ { x: 13, y: 6 },
51754
+ { x: 13, y: 7 },
51755
+ { x: 12, y: 7 },
51756
+ { x: 11, y: 7 },
51757
+ { x: 10, y: 7 },
51758
+ { x: 9, y: 7 },
51759
+ { x: 8, y: 7 },
51760
+ { x: 7, y: 7 },
51761
+ { x: 6, y: 7 },
51762
+ { x: 5, y: 7 },
51763
+ { x: 4, y: 7 },
51764
+ { x: 3, y: 7 },
51765
+ { x: 2, y: 7 },
51766
+ { x: 2, y: 8 },
51767
+ { x: 2, y: 9 },
51768
+ { x: 2, y: 10 },
51769
+ { x: 2, y: 11 },
51770
+ { x: 3, y: 11 },
51771
+ { x: 4, y: 11 },
51772
+ { x: 5, y: 11 },
51773
+ { x: 6, y: 11 },
51774
+ { x: 7, y: 11 },
51775
+ { x: 8, y: 11 },
51776
+ { x: 9, y: 11 },
51777
+ { x: 10, y: 11 },
51778
+ { x: 11, y: 11 },
51779
+ { x: 12, y: 11 },
51780
+ { x: 13, y: 11 },
51781
+ { x: 13, y: 12 },
51782
+ { x: 13, y: 13 },
51783
+ { x: 13, y: 14 },
51784
+ { x: 13, y: 15 }
51785
+ ];
51786
+ DEFAULT_TD_TILES = buildDefaultTDTiles();
51787
+ TOWER_SPRITE = `${CDN6}sprite-sheets/guardian-sprite-sheet-se.png`;
51788
+ CREEP_SPRITE = `${CDN6}sprite-sheets/scrapper-sprite-sheet-se.png`;
51789
+ HERO_SPRITE = `${CDN6}sprite-sheets/amir-sprite-sheet-se.png`;
51635
51790
  TowerDefenseBoard.displayName = "TowerDefenseBoard";
51636
51791
  }
51637
51792
  });
@@ -51906,6 +52061,17 @@ function heroPosition(h) {
51906
52061
  function heroMovement(h) {
51907
52062
  return num(h.movement);
51908
52063
  }
52064
+ function buildDefaultWorldTiles() {
52065
+ const tiles = [];
52066
+ for (let y = 0; y < WORLD_GRID_H; y++) {
52067
+ for (let x = 0; x < WORLD_GRID_W; x++) {
52068
+ const isBorder = x === 0 || y === 0 || x === WORLD_GRID_W - 1 || y === WORLD_GRID_H - 1;
52069
+ const def = isBorder ? WORLD_TERRAIN_DEFS[3] : WORLD_TERRAIN_DEFS[(x * 5 + y * 3 + (x ^ y)) % (WORLD_TERRAIN_DEFS.length - 1)];
52070
+ tiles.push({ x, y, terrain: def.terrain, terrainSprite: def.sprite, passable: def.passable });
52071
+ }
52072
+ }
52073
+ return tiles;
52074
+ }
51909
52075
  function defaultIsInRange(from, to, range) {
51910
52076
  return Math.abs(from.x - to.x) + Math.abs(from.y - to.y) <= range;
51911
52077
  }
@@ -51916,7 +52082,7 @@ function WorldMapBoard({
51916
52082
  features: propFeatures,
51917
52083
  assetManifest: propAssetManifest,
51918
52084
  isLoading,
51919
- scale = 0.4,
52085
+ scale = 0.25,
51920
52086
  unitScale = 2.5,
51921
52087
  spriteHeightRatio = 1.5,
51922
52088
  spriteMaxWidthRatio = 0.6,
@@ -51960,7 +52126,8 @@ function WorldMapBoard({
51960
52126
  })),
51961
52127
  [entityTiles]
51962
52128
  );
51963
- const tiles = propTiles ?? derivedTiles;
52129
+ const rawTiles = propTiles ?? (derivedTiles.length > 0 ? derivedTiles : null);
52130
+ const tiles = rawTiles != null && rawTiles.length >= WORLD_GRID_W * WORLD_GRID_H ? rawTiles : DEFAULT_WORLD_TILES;
51964
52131
  const baseUnits = useMemo(
51965
52132
  () => propUnits ?? entityUnits.map((u) => ({
51966
52133
  id: str(u.id),
@@ -52170,6 +52337,7 @@ function WorldMapBoard({
52170
52337
  footer && footer(ctx)
52171
52338
  ] });
52172
52339
  }
52340
+ var WORLD_CDN, WORLD_GRID_W, WORLD_GRID_H, WORLD_TERRAIN_DEFS, DEFAULT_WORLD_TILES;
52173
52341
  var init_WorldMapBoard = __esm({
52174
52342
  "components/game/organisms/WorldMapBoard.tsx"() {
52175
52343
  "use client";
@@ -52180,6 +52348,17 @@ var init_WorldMapBoard = __esm({
52180
52348
  init_IsometricCanvas();
52181
52349
  init_boardEntity();
52182
52350
  init_isometric();
52351
+ WORLD_CDN = "https://almadar-kflow-assets.web.app/shared";
52352
+ WORLD_GRID_W = 16;
52353
+ WORLD_GRID_H = 16;
52354
+ WORLD_TERRAIN_DEFS = [
52355
+ { terrain: "grass", sprite: `${WORLD_CDN}/isometric-dungeon/Isometric/dirt_E.png`, passable: true },
52356
+ { terrain: "dirt", sprite: `${WORLD_CDN}/isometric-dungeon/Isometric/dirtTiles_E.png`, passable: true },
52357
+ { terrain: "forest", sprite: `${WORLD_CDN}/isometric-dungeon/Isometric/planks_E.png`, passable: true },
52358
+ { terrain: "stone", sprite: `${WORLD_CDN}/isometric-dungeon/Isometric/stoneInset_E.png`, passable: false },
52359
+ { terrain: "castle", sprite: `${WORLD_CDN}/isometric-dungeon/Isometric/stoneTile_E.png`, passable: true }
52360
+ ];
52361
+ DEFAULT_WORLD_TILES = buildDefaultWorldTiles();
52183
52362
  WorldMapBoard.displayName = "WorldMapBoard";
52184
52363
  }
52185
52364
  });
@@ -2584,7 +2584,6 @@ var GameCanvas3D = React11.forwardRef(
2584
2584
  selectedTileIds = [],
2585
2585
  selectedUnitId = null,
2586
2586
  unitScale = 1,
2587
- scale = 0.45,
2588
2587
  children
2589
2588
  }, ref) => {
2590
2589
  const containerRef = React11.useRef(null);
@@ -3031,7 +3030,7 @@ var GameCanvas3D = React11.forwardRef(
3031
3030
  fadeStrength: 1
3032
3031
  }
3033
3032
  ),
3034
- /* @__PURE__ */ jsxRuntime.jsxs("group", { scale, children: [
3033
+ /* @__PURE__ */ jsxRuntime.jsxs("group", { children: [
3035
3034
  tiles.map((tile, index) => {
3036
3035
  const position = gridToWorld2(
3037
3036
  tile.x,
@@ -2560,7 +2560,6 @@ var GameCanvas3D = forwardRef(
2560
2560
  selectedTileIds = [],
2561
2561
  selectedUnitId = null,
2562
2562
  unitScale = 1,
2563
- scale = 0.45,
2564
2563
  children
2565
2564
  }, ref) => {
2566
2565
  const containerRef = useRef(null);
@@ -3007,7 +3006,7 @@ var GameCanvas3D = forwardRef(
3007
3006
  fadeStrength: 1
3008
3007
  }
3009
3008
  ),
3010
- /* @__PURE__ */ jsxs("group", { scale, children: [
3009
+ /* @__PURE__ */ jsxs("group", { children: [
3011
3010
  tiles.map((tile, index) => {
3012
3011
  const position = gridToWorld2(
3013
3012
  tile.x,
@@ -30,6 +30,11 @@ export interface TowerDefenseCreep {
30
30
  pathIndex: number;
31
31
  speed: number;
32
32
  }
33
+ export interface TowerDefenseHero {
34
+ id: string;
35
+ x: number;
36
+ y: number;
37
+ }
33
38
  export interface TowerDefenseBoardProps extends DisplayStateProps {
34
39
  entity?: EntityRow | readonly EntityRow[];
35
40
  tiles?: TowerDefenseTile[];
@@ -42,6 +47,7 @@ export interface TowerDefenseBoardProps extends DisplayStateProps {
42
47
  maxWaves?: number;
43
48
  result?: 'none' | 'won' | 'lost';
44
49
  waveActive?: boolean;
50
+ hero?: TowerDefenseHero;
45
51
  towerCost?: number;
46
52
  scale?: number;
47
53
  unitScale?: number;
@@ -58,9 +64,13 @@ export interface TowerDefenseBoardProps extends DisplayStateProps {
58
64
  gameEndEvent?: EventEmit<{
59
65
  result: 'won' | 'lost';
60
66
  }>;
67
+ moveHeroEvent?: EventEmit<{
68
+ dx: number;
69
+ dy: number;
70
+ }>;
61
71
  className?: string;
62
72
  }
63
- export declare function TowerDefenseBoard({ entity, tiles: propTiles, path: propPath, towers: propTowers, creeps: propCreeps, gold: propGold, lives: propLives, wave: propWave, maxWaves: propMaxWaves, result: propResult, waveActive: propWaveActive, towerCost, scale, unitScale, spriteHeightRatio, spriteMaxWidthRatio, placeTowerEvent, startWaveEvent, playAgainEvent, gameEndEvent, className, }: TowerDefenseBoardProps): React.JSX.Element;
73
+ export declare function TowerDefenseBoard({ entity, tiles: propTiles, path: propPath, towers: propTowers, creeps: propCreeps, hero: propHero, gold: propGold, lives: propLives, wave: propWave, maxWaves: propMaxWaves, result: propResult, waveActive: propWaveActive, towerCost, scale, unitScale, spriteHeightRatio, spriteMaxWidthRatio, placeTowerEvent, startWaveEvent, playAgainEvent, gameEndEvent, moveHeroEvent, className, }: TowerDefenseBoardProps): React.JSX.Element;
64
74
  export declare namespace TowerDefenseBoard {
65
75
  var displayName: string;
66
76
  }