@almadar/ui 5.63.2 → 5.65.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.cjs +89 -24
- package/dist/avl/index.js +89 -24
- package/dist/components/game/molecules/IsometricCanvas.d.ts +4 -1
- package/dist/components/game/molecules/index.d.ts +1 -0
- package/dist/components/game/molecules/three/index.cjs +5 -2
- package/dist/components/game/molecules/three/index.js +5 -2
- package/dist/components/game/organisms/HexStrategyBoard.d.ts +37 -0
- package/dist/components/game/organisms/index.d.ts +2 -0
- package/dist/components/game/organisms/utils/isometric.d.ts +20 -7
- package/dist/components/index.cjs +91 -24
- package/dist/components/index.js +91 -25
- package/dist/components/marketing/organisms/HeroOrganism.d.ts +2 -2
- package/dist/components/marketing/organisms/ShowcaseOrganism.d.ts +2 -2
- package/dist/components/marketing/organisms/TeamOrganism.d.ts +2 -2
- package/dist/providers/index.cjs +89 -24
- package/dist/providers/index.js +89 -24
- package/dist/runtime/index.cjs +89 -24
- package/dist/runtime/index.js +89 -24
- package/package.json +1 -1
|
@@ -6,6 +6,7 @@ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
|
6
6
|
import { OrbitControls, Billboard, Grid, Stars, Sparkles, Html, RoundedBox } from '@react-three/drei';
|
|
7
7
|
import { createLogger } from '@almadar/logger';
|
|
8
8
|
import { GLTFLoader as GLTFLoader$1 } from 'three/examples/jsm/loaders/GLTFLoader';
|
|
9
|
+
import { clone } from 'three/examples/jsm/utils/SkeletonUtils';
|
|
9
10
|
import { OrbitControls as OrbitControls$1 } from 'three/examples/jsm/controls/OrbitControls.js';
|
|
10
11
|
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
|
|
11
12
|
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader.js';
|
|
@@ -368,7 +369,8 @@ function ModelLoader({
|
|
|
368
369
|
const { model: loadedModel, isLoading, error } = useGLTFModel(url, resourceBasePath);
|
|
369
370
|
const model = useMemo(() => {
|
|
370
371
|
if (!loadedModel) return null;
|
|
371
|
-
const cloned =
|
|
372
|
+
const cloned = clone(loadedModel);
|
|
373
|
+
cloned.updateMatrixWorld(true);
|
|
372
374
|
cloned.traverse((child) => {
|
|
373
375
|
if (child instanceof THREE6.Mesh) {
|
|
374
376
|
child.castShadow = castShadow;
|
|
@@ -383,7 +385,8 @@ function ModelLoader({
|
|
|
383
385
|
const size = new THREE6.Vector3();
|
|
384
386
|
box.getSize(size);
|
|
385
387
|
const maxDim = Math.max(size.x, size.y, size.z);
|
|
386
|
-
|
|
388
|
+
if (!Number.isFinite(maxDim) || maxDim < 0.05) return 1;
|
|
389
|
+
return 1 / maxDim;
|
|
387
390
|
}, [model]);
|
|
388
391
|
const scaleArray = useMemo(() => {
|
|
389
392
|
const base = typeof scale === "number" ? [scale, scale, scale] : scale;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { AssetUrl, EventEmit } from '@almadar/core';
|
|
3
|
+
import type { IsometricTile, IsometricUnit, IsometricFeature } from './types/isometric';
|
|
4
|
+
import type { DisplayStateProps } from '../../core/organisms/types';
|
|
5
|
+
import type { IsometricCanvasProps } from '../molecules/IsometricCanvas';
|
|
6
|
+
export interface HexStrategyBoardProps extends DisplayStateProps {
|
|
7
|
+
/** Hex grid tiles */
|
|
8
|
+
tiles?: IsometricTile[];
|
|
9
|
+
/** Units on the board */
|
|
10
|
+
units?: IsometricUnit[];
|
|
11
|
+
/** Features (resources, structures, etc.) on the board */
|
|
12
|
+
features?: IsometricFeature[];
|
|
13
|
+
/** Asset sprite manifest (same shape as IsometricCanvas.assetManifest) */
|
|
14
|
+
assetManifest?: IsometricCanvasProps['assetManifest'];
|
|
15
|
+
/** Base URL prepended to manifest sprite paths */
|
|
16
|
+
assetBaseUrl?: AssetUrl;
|
|
17
|
+
/** Render scale */
|
|
18
|
+
scale?: number;
|
|
19
|
+
/** Show minimap overlay */
|
|
20
|
+
showMinimap?: boolean;
|
|
21
|
+
/** Enable camera pan/zoom controls */
|
|
22
|
+
enableCamera?: boolean;
|
|
23
|
+
/** Declarative event: emits UI:{tileClickEvent} with { x, y } on tile click */
|
|
24
|
+
tileClickEvent?: EventEmit<{
|
|
25
|
+
x: number;
|
|
26
|
+
y: number;
|
|
27
|
+
}>;
|
|
28
|
+
/** Declarative event: emits UI:{unitClickEvent} with { unitId } on unit click */
|
|
29
|
+
unitClickEvent?: EventEmit<{
|
|
30
|
+
unitId: string;
|
|
31
|
+
}>;
|
|
32
|
+
}
|
|
33
|
+
export declare function HexStrategyBoard({ tiles, units, features, assetManifest, assetBaseUrl, scale, showMinimap, enableCamera, tileClickEvent, unitClickEvent, isLoading, error, className, }: HexStrategyBoardProps): React.ReactElement;
|
|
34
|
+
export declare namespace HexStrategyBoard {
|
|
35
|
+
var displayName: string;
|
|
36
|
+
}
|
|
37
|
+
export default HexStrategyBoard;
|
|
@@ -18,6 +18,7 @@ export { useSpriteAnimations, type UseSpriteAnimationsResult, type UseSpriteAnim
|
|
|
18
18
|
export { usePhysics2D, type UsePhysics2DOptions, type UsePhysics2DReturn, } from './hooks/usePhysics2D';
|
|
19
19
|
export { PhysicsManager, type Physics2DState, type PhysicsBounds, type PhysicsConfig, } from './managers/PhysicsManager';
|
|
20
20
|
export { isoToScreen, screenToIso, TILE_WIDTH, TILE_HEIGHT, FLOOR_HEIGHT, DIAMOND_TOP_Y, FEATURE_COLORS, } from './utils/isometric';
|
|
21
|
+
export type { TileLayout } from './utils/isometric';
|
|
21
22
|
export { inferDirection, resolveSheetDirection, getCurrentFrame, resolveFrame, createUnitAnimationState, transitionAnimation, tickAnimationState, } from './utils/spriteAnimation';
|
|
22
23
|
export { SPRITE_SHEET_LAYOUT, SHEET_COLUMNS } from './utils/spriteSheetConstants';
|
|
23
24
|
export { BattleBoard, type BattleBoardProps, type BattlePhase, type BattleSlotContext, } from './BattleBoard';
|
|
@@ -33,6 +34,7 @@ export { CityBuilderBoard, type CityBuilderBoardProps, } from './CityBuilderBoar
|
|
|
33
34
|
export { GameBoard3D, type GameBoard3DProps, } from './GameBoard3D';
|
|
34
35
|
export { VisualNovelBoard, type VisualNovelBoardProps, type VisualNovelNode, type VisualNovelChoice, } from './VisualNovelBoard';
|
|
35
36
|
export { CardBattlerBoard, type CardBattlerBoardProps, type CardBattlerCard, } from './CardBattlerBoard';
|
|
37
|
+
export { HexStrategyBoard, type HexStrategyBoardProps, } from './HexStrategyBoard';
|
|
36
38
|
export { TraitStateViewer, type TraitStateViewerProps, type TraitStateMachineDefinition, type TraitTransition, } from './TraitStateViewer';
|
|
37
39
|
export { TraitSlot, type TraitSlotProps, type SlotItemData, } from './TraitSlot';
|
|
38
40
|
export { CollapsibleSection, EditorSlider, EditorSelect, EditorCheckbox, EditorTextInput, StatusBar, TerrainPalette, EditorToolbar, TERRAIN_COLORS, FEATURE_TYPES, type EditorMode, type CollapsibleSectionProps, type EditorSliderProps, type EditorSelectProps, type EditorCheckboxProps, type EditorTextInputProps, type StatusBarProps, type TerrainPaletteProps, type EditorToolbarProps, } from './editor';
|
|
@@ -27,20 +27,32 @@ export declare const DIAMOND_TOP_Y = 374;
|
|
|
27
27
|
* Feature type → fallback color mapping (when sprites not loaded).
|
|
28
28
|
*/
|
|
29
29
|
export declare const FEATURE_COLORS: Record<string, string>;
|
|
30
|
+
/**
|
|
31
|
+
* Tile layout mode. 'isometric' = 2:1 diamond projection (default).
|
|
32
|
+
* 'hex' = pointy-top offset-row hex grid.
|
|
33
|
+
*
|
|
34
|
+
* Hex constants (at scale=1):
|
|
35
|
+
* w = TILE_WIDTH (256) — horizontal cell width
|
|
36
|
+
* h = FLOOR_HEIGHT (128) — vertical row stride (= w/2; hex row-to-row = h*0.75 = 96)
|
|
37
|
+
* Forward: screenX = col*w + (row&1)*(w/2) + baseOffsetX
|
|
38
|
+
* screenY = row * (h * 0.75)
|
|
39
|
+
* Inverse: row = round(screenY / (h*0.75)); col = round((screenX - (row&1)*(w/2) - baseOffsetX) / w)
|
|
40
|
+
*/
|
|
41
|
+
export type TileLayout = 'isometric' | 'hex';
|
|
30
42
|
/**
|
|
31
43
|
* Convert tile grid coordinates to screen pixel coordinates.
|
|
32
44
|
*
|
|
33
|
-
*
|
|
34
|
-
* -
|
|
35
|
-
* - Y increases to the lower-left
|
|
45
|
+
* For 'isometric' (default): 2:1 diamond projection.
|
|
46
|
+
* For 'hex': pointy-top offset-row projection (odd rows shifted right by w/2).
|
|
36
47
|
*
|
|
37
|
-
* @param tileX - Grid X coordinate
|
|
38
|
-
* @param tileY - Grid Y coordinate
|
|
48
|
+
* @param tileX - Grid X (col) coordinate
|
|
49
|
+
* @param tileY - Grid Y (row) coordinate
|
|
39
50
|
* @param scale - Render scale factor
|
|
40
51
|
* @param baseOffsetX - Horizontal offset to center the grid
|
|
52
|
+
* @param layout - Tile layout mode (default: 'isometric')
|
|
41
53
|
* @returns Screen position { x, y } of the tile's top-left corner
|
|
42
54
|
*/
|
|
43
|
-
export declare function isoToScreen(tileX: number, tileY: number, scale: number, baseOffsetX: number): {
|
|
55
|
+
export declare function isoToScreen(tileX: number, tileY: number, scale: number, baseOffsetX: number, layout?: TileLayout): {
|
|
44
56
|
x: number;
|
|
45
57
|
y: number;
|
|
46
58
|
};
|
|
@@ -53,9 +65,10 @@ export declare function isoToScreen(tileX: number, tileY: number, scale: number,
|
|
|
53
65
|
* @param screenY - Screen Y in pixels
|
|
54
66
|
* @param scale - Render scale factor
|
|
55
67
|
* @param baseOffsetX - Horizontal offset used in isoToScreen
|
|
68
|
+
* @param layout - Tile layout mode (default: 'isometric')
|
|
56
69
|
* @returns Snapped grid position { x, y }
|
|
57
70
|
*/
|
|
58
|
-
export declare function screenToIso(screenX: number, screenY: number, scale: number, baseOffsetX: number): {
|
|
71
|
+
export declare function screenToIso(screenX: number, screenY: number, scale: number, baseOffsetX: number, layout?: TileLayout): {
|
|
59
72
|
x: number;
|
|
60
73
|
y: number;
|
|
61
74
|
};
|
|
@@ -45,6 +45,7 @@ var THREE3 = require('three');
|
|
|
45
45
|
var GLTFLoader_js = require('three/examples/jsm/loaders/GLTFLoader.js');
|
|
46
46
|
var OBJLoader_js = require('three/examples/jsm/loaders/OBJLoader.js');
|
|
47
47
|
var GLTFLoader = require('three/examples/jsm/loaders/GLTFLoader');
|
|
48
|
+
var SkeletonUtils = require('three/examples/jsm/utils/SkeletonUtils');
|
|
48
49
|
var fiber = require('@react-three/fiber');
|
|
49
50
|
var drei = require('@react-three/drei');
|
|
50
51
|
var patterns = require('@almadar/patterns');
|
|
@@ -9922,16 +9923,26 @@ var init_useUnitSpriteAtlas = __esm({
|
|
|
9922
9923
|
});
|
|
9923
9924
|
|
|
9924
9925
|
// components/game/organisms/utils/isometric.ts
|
|
9925
|
-
function isoToScreen(tileX, tileY, scale, baseOffsetX) {
|
|
9926
|
+
function isoToScreen(tileX, tileY, scale, baseOffsetX, layout = "isometric") {
|
|
9926
9927
|
const scaledTileWidth = exports.TILE_WIDTH * scale;
|
|
9927
9928
|
const scaledFloorHeight = exports.FLOOR_HEIGHT * scale;
|
|
9929
|
+
if (layout === "hex") {
|
|
9930
|
+
const screenX2 = tileX * scaledTileWidth + (tileY & 1) * (scaledTileWidth / 2) + baseOffsetX;
|
|
9931
|
+
const screenY2 = tileY * (scaledFloorHeight * 0.75);
|
|
9932
|
+
return { x: screenX2, y: screenY2 };
|
|
9933
|
+
}
|
|
9928
9934
|
const screenX = (tileX - tileY) * (scaledTileWidth / 2) + baseOffsetX;
|
|
9929
9935
|
const screenY = (tileX + tileY) * (scaledFloorHeight / 2);
|
|
9930
9936
|
return { x: screenX, y: screenY };
|
|
9931
9937
|
}
|
|
9932
|
-
function screenToIso(screenX, screenY, scale, baseOffsetX) {
|
|
9938
|
+
function screenToIso(screenX, screenY, scale, baseOffsetX, layout = "isometric") {
|
|
9933
9939
|
const scaledTileWidth = exports.TILE_WIDTH * scale;
|
|
9934
9940
|
const scaledFloorHeight = exports.FLOOR_HEIGHT * scale;
|
|
9941
|
+
if (layout === "hex") {
|
|
9942
|
+
const row = Math.round(screenY / (scaledFloorHeight * 0.75));
|
|
9943
|
+
const col = Math.round((screenX - (row & 1) * (scaledTileWidth / 2) - baseOffsetX) / scaledTileWidth);
|
|
9944
|
+
return { x: col, y: row };
|
|
9945
|
+
}
|
|
9935
9946
|
const adjustedX = screenX - baseOffsetX;
|
|
9936
9947
|
const tileX = (adjustedX / (scaledTileWidth / 2) + screenY / (scaledFloorHeight / 2)) / 2;
|
|
9937
9948
|
const tileY = (screenY / (scaledFloorHeight / 2) - adjustedX / (scaledTileWidth / 2)) / 2;
|
|
@@ -9981,6 +9992,7 @@ function IsometricCanvas({
|
|
|
9981
9992
|
tileHoverEvent,
|
|
9982
9993
|
tileLeaveEvent,
|
|
9983
9994
|
// Rendering options
|
|
9995
|
+
tileLayout = "isometric",
|
|
9984
9996
|
scale = 0.4,
|
|
9985
9997
|
debug: debug2 = false,
|
|
9986
9998
|
backgroundImage = "",
|
|
@@ -10050,13 +10062,17 @@ function IsometricCanvas({
|
|
|
10050
10062
|
);
|
|
10051
10063
|
const sortedTiles = React79.useMemo(() => {
|
|
10052
10064
|
const tiles = [...tilesProp];
|
|
10053
|
-
|
|
10054
|
-
|
|
10055
|
-
|
|
10056
|
-
|
|
10057
|
-
|
|
10065
|
+
if (tileLayout === "hex") {
|
|
10066
|
+
tiles.sort((a, b) => a.y !== b.y ? a.y - b.y : a.x - b.x);
|
|
10067
|
+
} else {
|
|
10068
|
+
tiles.sort((a, b) => {
|
|
10069
|
+
const depthA = a.x + a.y;
|
|
10070
|
+
const depthB = b.x + b.y;
|
|
10071
|
+
return depthA !== depthB ? depthA - depthB : a.y - b.y;
|
|
10072
|
+
});
|
|
10073
|
+
}
|
|
10058
10074
|
return tiles;
|
|
10059
|
-
}, [tilesProp]);
|
|
10075
|
+
}, [tilesProp, tileLayout]);
|
|
10060
10076
|
const gridWidth = React79.useMemo(() => {
|
|
10061
10077
|
if (sortedTiles.length === 0) return 0;
|
|
10062
10078
|
return Math.max(...sortedTiles.map((t2) => t2.x)) + 1;
|
|
@@ -10170,7 +10186,7 @@ function IsometricCanvas({
|
|
|
10170
10186
|
miniCanvas.width = mW;
|
|
10171
10187
|
miniCanvas.height = mH;
|
|
10172
10188
|
mCtx.clearRect(0, 0, mW, mH);
|
|
10173
|
-
const allScreenPos = sortedTiles.map((t2) => isoToScreen(t2.x, t2.y, scale, baseOffsetX));
|
|
10189
|
+
const allScreenPos = sortedTiles.map((t2) => isoToScreen(t2.x, t2.y, scale, baseOffsetX, tileLayout));
|
|
10174
10190
|
const minX = Math.min(...allScreenPos.map((p2) => p2.x));
|
|
10175
10191
|
const maxX = Math.max(...allScreenPos.map((p2) => p2.x + scaledTileWidth));
|
|
10176
10192
|
const minY = Math.min(...allScreenPos.map((p2) => p2.y));
|
|
@@ -10181,7 +10197,7 @@ function IsometricCanvas({
|
|
|
10181
10197
|
const offsetMx = (mW - worldW * scaleM) / 2;
|
|
10182
10198
|
const offsetMy = (mH - worldH * scaleM) / 2;
|
|
10183
10199
|
for (const tile of sortedTiles) {
|
|
10184
|
-
const pos = isoToScreen(tile.x, tile.y, scale, baseOffsetX);
|
|
10200
|
+
const pos = isoToScreen(tile.x, tile.y, scale, baseOffsetX, tileLayout);
|
|
10185
10201
|
const mx = (pos.x - minX) * scaleM + offsetMx;
|
|
10186
10202
|
const my = (pos.y - minY) * scaleM + offsetMy;
|
|
10187
10203
|
const mTileW = scaledTileWidth * scaleM;
|
|
@@ -10197,7 +10213,7 @@ function IsometricCanvas({
|
|
|
10197
10213
|
}
|
|
10198
10214
|
for (const unit of units) {
|
|
10199
10215
|
if (!unit.position) continue;
|
|
10200
|
-
const pos = isoToScreen(unit.position.x, unit.position.y, scale, baseOffsetX);
|
|
10216
|
+
const pos = isoToScreen(unit.position.x, unit.position.y, scale, baseOffsetX, tileLayout);
|
|
10201
10217
|
const mx = (pos.x + scaledTileWidth / 2 - minX) * scaleM + offsetMx;
|
|
10202
10218
|
const my = (pos.y + scaledTileHeight / 2 - minY) * scaleM + offsetMy;
|
|
10203
10219
|
mCtx.fillStyle = unit.team === "player" ? "#60a5fa" : unit.team === "enemy" ? "#f87171" : "#9ca3af";
|
|
@@ -10213,7 +10229,7 @@ function IsometricCanvas({
|
|
|
10213
10229
|
mCtx.strokeStyle = "rgba(255, 255, 255, 0.8)";
|
|
10214
10230
|
mCtx.lineWidth = 1;
|
|
10215
10231
|
mCtx.strokeRect(vLeft, vTop, vW, vH);
|
|
10216
|
-
}, [showMinimap, sortedTiles, units, scale, baseOffsetX, scaledTileWidth, scaledTileHeight, scaledFloorHeight, viewportSize, cameraRef]);
|
|
10232
|
+
}, [showMinimap, sortedTiles, units, tileLayout, scale, baseOffsetX, scaledTileWidth, scaledTileHeight, scaledFloorHeight, viewportSize, cameraRef]);
|
|
10217
10233
|
const draw = React79.useCallback((animTime) => {
|
|
10218
10234
|
const canvas = canvasRef.current;
|
|
10219
10235
|
if (!canvas) return;
|
|
@@ -10253,7 +10269,7 @@ function IsometricCanvas({
|
|
|
10253
10269
|
const visTop = cam.y - viewportSize.height / cam.zoom;
|
|
10254
10270
|
const visBottom = cam.y + viewportSize.height * 2 / cam.zoom;
|
|
10255
10271
|
for (const tile of sortedTiles) {
|
|
10256
|
-
const pos = isoToScreen(tile.x, tile.y, scale, baseOffsetX);
|
|
10272
|
+
const pos = isoToScreen(tile.x, tile.y, scale, baseOffsetX, tileLayout);
|
|
10257
10273
|
if (pos.x + scaledTileWidth < visLeft || pos.x > visRight || pos.y + scaledTileHeight < visTop || pos.y > visBottom) {
|
|
10258
10274
|
continue;
|
|
10259
10275
|
}
|
|
@@ -10333,12 +10349,13 @@ function IsometricCanvas({
|
|
|
10333
10349
|
}
|
|
10334
10350
|
}
|
|
10335
10351
|
const sortedFeatures = [...features].sort((a, b) => {
|
|
10352
|
+
if (tileLayout === "hex") return a.y !== b.y ? a.y - b.y : a.x - b.x;
|
|
10336
10353
|
const depthA = a.x + a.y;
|
|
10337
10354
|
const depthB = b.x + b.y;
|
|
10338
10355
|
return depthA !== depthB ? depthA - depthB : a.y - b.y;
|
|
10339
10356
|
});
|
|
10340
10357
|
for (const feature of sortedFeatures) {
|
|
10341
|
-
const pos = isoToScreen(feature.x, feature.y, scale, baseOffsetX);
|
|
10358
|
+
const pos = isoToScreen(feature.x, feature.y, scale, baseOffsetX, tileLayout);
|
|
10342
10359
|
if (pos.x + scaledTileWidth < visLeft || pos.x > visRight || pos.y + scaledTileHeight < visTop || pos.y > visBottom) {
|
|
10343
10360
|
continue;
|
|
10344
10361
|
}
|
|
@@ -10373,12 +10390,13 @@ function IsometricCanvas({
|
|
|
10373
10390
|
}
|
|
10374
10391
|
const unitsWithPosition = units.filter((u) => !!u.position);
|
|
10375
10392
|
const sortedUnits = [...unitsWithPosition].sort((a, b) => {
|
|
10393
|
+
if (tileLayout === "hex") return a.position.y !== b.position.y ? a.position.y - b.position.y : a.position.x - b.position.x;
|
|
10376
10394
|
const depthA = a.position.x + a.position.y;
|
|
10377
10395
|
const depthB = b.position.x + b.position.y;
|
|
10378
10396
|
return depthA !== depthB ? depthA - depthB : a.position.y - b.position.y;
|
|
10379
10397
|
});
|
|
10380
10398
|
for (const unit of sortedUnits) {
|
|
10381
|
-
const pos = isoToScreen(unit.position.x, unit.position.y, scale, baseOffsetX);
|
|
10399
|
+
const pos = isoToScreen(unit.position.x, unit.position.y, scale, baseOffsetX, tileLayout);
|
|
10382
10400
|
if (pos.x + scaledTileWidth < visLeft || pos.x > visRight || pos.y + scaledTileHeight < visTop || pos.y > visBottom) {
|
|
10383
10401
|
continue;
|
|
10384
10402
|
}
|
|
@@ -10398,7 +10416,7 @@ function IsometricCanvas({
|
|
|
10398
10416
|
drawH = maxUnitW / ar;
|
|
10399
10417
|
}
|
|
10400
10418
|
if (unit.previousPosition && (unit.previousPosition.x !== unit.position.x || unit.previousPosition.y !== unit.position.y)) {
|
|
10401
|
-
const ghostPos = isoToScreen(unit.previousPosition.x, unit.previousPosition.y, scale, baseOffsetX);
|
|
10419
|
+
const ghostPos = isoToScreen(unit.previousPosition.x, unit.previousPosition.y, scale, baseOffsetX, tileLayout);
|
|
10402
10420
|
const ghostCenterX = ghostPos.x + scaledTileWidth / 2;
|
|
10403
10421
|
const ghostGroundY = ghostPos.y + scaledDiamondTopY + scaledFloorHeight * 0.5;
|
|
10404
10422
|
ctx.save();
|
|
@@ -10527,6 +10545,7 @@ function IsometricCanvas({
|
|
|
10527
10545
|
units,
|
|
10528
10546
|
features,
|
|
10529
10547
|
selectedUnitId,
|
|
10548
|
+
tileLayout,
|
|
10530
10549
|
scale,
|
|
10531
10550
|
debug2,
|
|
10532
10551
|
resolveTerrainSpriteUrl,
|
|
@@ -10555,14 +10574,14 @@ function IsometricCanvas({
|
|
|
10555
10574
|
if (!selectedUnitId) return;
|
|
10556
10575
|
const unit = units.find((u) => u.id === selectedUnitId);
|
|
10557
10576
|
if (!unit?.position) return;
|
|
10558
|
-
const pos = isoToScreen(unit.position.x, unit.position.y, scale, baseOffsetX);
|
|
10577
|
+
const pos = isoToScreen(unit.position.x, unit.position.y, scale, baseOffsetX, tileLayout);
|
|
10559
10578
|
const centerX = pos.x + scaledTileWidth / 2;
|
|
10560
10579
|
const centerY = pos.y + scaledDiamondTopY + scaledFloorHeight / 2;
|
|
10561
10580
|
targetCameraRef.current = {
|
|
10562
10581
|
x: centerX - viewportSize.width / 2,
|
|
10563
10582
|
y: centerY - viewportSize.height / 2
|
|
10564
10583
|
};
|
|
10565
|
-
}, [selectedUnitId, units, scale, baseOffsetX, scaledTileWidth, scaledDiamondTopY, scaledFloorHeight, viewportSize, targetCameraRef]);
|
|
10584
|
+
}, [selectedUnitId, units, tileLayout, scale, baseOffsetX, scaledTileWidth, scaledDiamondTopY, scaledFloorHeight, viewportSize, targetCameraRef]);
|
|
10566
10585
|
React79.useEffect(() => {
|
|
10567
10586
|
const hasAnimations = units.length > 0 || validMoves.length > 0 || attackTargets.length > 0 || selectedUnitId != null || targetCameraRef.current != null || hasActiveEffects2 || pendingCount > 0 || atlasPending > 0;
|
|
10568
10587
|
draw(animTimeRef.current);
|
|
@@ -10595,13 +10614,13 @@ function IsometricCanvas({
|
|
|
10595
10614
|
const world = screenToWorld(e.clientX, e.clientY, canvasRef.current, viewportSize);
|
|
10596
10615
|
const adjustedX = world.x - scaledTileWidth / 2;
|
|
10597
10616
|
const adjustedY = world.y - scaledDiamondTopY - scaledFloorHeight / 2;
|
|
10598
|
-
const isoPos = screenToIso(adjustedX, adjustedY, scale, baseOffsetX);
|
|
10617
|
+
const isoPos = screenToIso(adjustedX, adjustedY, scale, baseOffsetX, tileLayout);
|
|
10599
10618
|
const tileExists = tilesProp.some((t2) => t2.x === isoPos.x && t2.y === isoPos.y);
|
|
10600
10619
|
if (tileExists) {
|
|
10601
10620
|
if (tileHoverEvent) eventBus.emit(`UI:${tileHoverEvent}`, { x: isoPos.x, y: isoPos.y });
|
|
10602
10621
|
onTileHover?.(isoPos.x, isoPos.y);
|
|
10603
10622
|
}
|
|
10604
|
-
}, [onTileHover, screenToWorld, viewportSize, scaledTileWidth, scaledDiamondTopY, scaledFloorHeight, scale, baseOffsetX, tilesProp, tileHoverEvent, eventBus]);
|
|
10623
|
+
}, [onTileHover, screenToWorld, viewportSize, scaledTileWidth, scaledDiamondTopY, scaledFloorHeight, tileLayout, scale, baseOffsetX, tilesProp, tileHoverEvent, eventBus]);
|
|
10605
10624
|
const handleCanvasPointerUp = React79.useCallback((e) => {
|
|
10606
10625
|
singlePointerActiveRef.current = false;
|
|
10607
10626
|
if (enableCamera) handlePointerUp();
|
|
@@ -10610,7 +10629,7 @@ function IsometricCanvas({
|
|
|
10610
10629
|
const world = screenToWorld(e.clientX, e.clientY, canvasRef.current, viewportSize);
|
|
10611
10630
|
const adjustedX = world.x - scaledTileWidth / 2;
|
|
10612
10631
|
const adjustedY = world.y - scaledDiamondTopY - scaledFloorHeight / 2;
|
|
10613
|
-
const isoPos = screenToIso(adjustedX, adjustedY, scale, baseOffsetX);
|
|
10632
|
+
const isoPos = screenToIso(adjustedX, adjustedY, scale, baseOffsetX, tileLayout);
|
|
10614
10633
|
const clickedUnit = units.find((u) => u.position?.x === isoPos.x && u.position?.y === isoPos.y);
|
|
10615
10634
|
if (clickedUnit && (onUnitClick || unitClickEvent)) {
|
|
10616
10635
|
if (unitClickEvent) eventBus.emit(`UI:${unitClickEvent}`, { unitId: clickedUnit.id });
|
|
@@ -10622,7 +10641,7 @@ function IsometricCanvas({
|
|
|
10622
10641
|
onTileClick?.(isoPos.x, isoPos.y);
|
|
10623
10642
|
}
|
|
10624
10643
|
}
|
|
10625
|
-
}, [enableCamera, handlePointerUp, dragDistance, screenToWorld, viewportSize, scaledTileWidth, scaledDiamondTopY, scaledFloorHeight, scale, baseOffsetX, units, tilesProp, onUnitClick, onTileClick, unitClickEvent, tileClickEvent, eventBus]);
|
|
10644
|
+
}, [enableCamera, handlePointerUp, dragDistance, screenToWorld, viewportSize, scaledTileWidth, scaledDiamondTopY, scaledFloorHeight, tileLayout, scale, baseOffsetX, units, tilesProp, onUnitClick, onTileClick, unitClickEvent, tileClickEvent, eventBus]);
|
|
10626
10645
|
const handleCanvasPointerLeave = React79.useCallback(() => {
|
|
10627
10646
|
handleMouseLeave();
|
|
10628
10647
|
if (tileLeaveEvent) eventBus.emit(`UI:${tileLeaveEvent}`, {});
|
|
@@ -44014,7 +44033,8 @@ function ModelLoader({
|
|
|
44014
44033
|
const { model: loadedModel, isLoading, error } = useGLTFModel(url, resourceBasePath);
|
|
44015
44034
|
const model = React79.useMemo(() => {
|
|
44016
44035
|
if (!loadedModel) return null;
|
|
44017
|
-
const cloned =
|
|
44036
|
+
const cloned = SkeletonUtils.clone(loadedModel);
|
|
44037
|
+
cloned.updateMatrixWorld(true);
|
|
44018
44038
|
cloned.traverse((child) => {
|
|
44019
44039
|
if (child instanceof THREE3__namespace.Mesh) {
|
|
44020
44040
|
child.castShadow = castShadow;
|
|
@@ -44029,7 +44049,8 @@ function ModelLoader({
|
|
|
44029
44049
|
const size = new THREE3__namespace.Vector3();
|
|
44030
44050
|
box.getSize(size);
|
|
44031
44051
|
const maxDim = Math.max(size.x, size.y, size.z);
|
|
44032
|
-
|
|
44052
|
+
if (!Number.isFinite(maxDim) || maxDim < 0.05) return 1;
|
|
44053
|
+
return 1 / maxDim;
|
|
44033
44054
|
}, [model]);
|
|
44034
44055
|
const scaleArray = React79.useMemo(() => {
|
|
44035
44056
|
const base = typeof scale === "number" ? [scale, scale, scale] : scale;
|
|
@@ -45311,6 +45332,48 @@ var init_HeroOrganism = __esm({
|
|
|
45311
45332
|
_HeroClickInterceptor.displayName = "_HeroClickInterceptor";
|
|
45312
45333
|
}
|
|
45313
45334
|
});
|
|
45335
|
+
function HexStrategyBoard({
|
|
45336
|
+
tiles,
|
|
45337
|
+
units,
|
|
45338
|
+
features,
|
|
45339
|
+
assetManifest,
|
|
45340
|
+
assetBaseUrl,
|
|
45341
|
+
scale = 0.45,
|
|
45342
|
+
showMinimap = true,
|
|
45343
|
+
enableCamera = true,
|
|
45344
|
+
tileClickEvent,
|
|
45345
|
+
unitClickEvent,
|
|
45346
|
+
isLoading,
|
|
45347
|
+
error,
|
|
45348
|
+
className
|
|
45349
|
+
}) {
|
|
45350
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("hex-strategy-board relative w-full h-full", className), children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
45351
|
+
IsometricCanvas_default,
|
|
45352
|
+
{
|
|
45353
|
+
tileLayout: "hex",
|
|
45354
|
+
tiles,
|
|
45355
|
+
units,
|
|
45356
|
+
features,
|
|
45357
|
+
assetManifest,
|
|
45358
|
+
assetBaseUrl,
|
|
45359
|
+
scale,
|
|
45360
|
+
showMinimap,
|
|
45361
|
+
enableCamera,
|
|
45362
|
+
tileClickEvent,
|
|
45363
|
+
unitClickEvent,
|
|
45364
|
+
isLoading,
|
|
45365
|
+
error
|
|
45366
|
+
}
|
|
45367
|
+
) });
|
|
45368
|
+
}
|
|
45369
|
+
var init_HexStrategyBoard = __esm({
|
|
45370
|
+
"components/game/organisms/HexStrategyBoard.tsx"() {
|
|
45371
|
+
"use client";
|
|
45372
|
+
init_cn();
|
|
45373
|
+
init_IsometricCanvas();
|
|
45374
|
+
HexStrategyBoard.displayName = "HexStrategyBoard";
|
|
45375
|
+
}
|
|
45376
|
+
});
|
|
45314
45377
|
exports.LandingPageTemplate = void 0;
|
|
45315
45378
|
var init_LandingPageTemplate = __esm({
|
|
45316
45379
|
"components/marketing/templates/LandingPageTemplate.tsx"() {
|
|
@@ -53619,6 +53682,7 @@ var init_component_registry_generated = __esm({
|
|
|
53619
53682
|
init_HealthPanel();
|
|
53620
53683
|
init_HeroOrganism();
|
|
53621
53684
|
init_HeroSection();
|
|
53685
|
+
init_HexStrategyBoard();
|
|
53622
53686
|
init_Icon();
|
|
53623
53687
|
init_InfiniteScrollSentinel();
|
|
53624
53688
|
init_Input();
|
|
@@ -53955,6 +54019,7 @@ var init_component_registry_generated = __esm({
|
|
|
53955
54019
|
"HealthPanel": HealthPanel,
|
|
53956
54020
|
"HeroOrganism": exports.HeroOrganism,
|
|
53957
54021
|
"HeroSection": exports.HeroSection,
|
|
54022
|
+
"HexStrategyBoard": HexStrategyBoard,
|
|
53958
54023
|
"Icon": exports.Icon,
|
|
53959
54024
|
"InfiniteScrollSentinel": exports.InfiniteScrollSentinel,
|
|
53960
54025
|
"Input": exports.Input,
|
|
@@ -55600,6 +55665,7 @@ init_CityBuilderBoard();
|
|
|
55600
55665
|
init_GameBoard3D();
|
|
55601
55666
|
init_VisualNovelBoard();
|
|
55602
55667
|
init_CardBattlerBoard();
|
|
55668
|
+
init_HexStrategyBoard();
|
|
55603
55669
|
init_TraitStateViewer();
|
|
55604
55670
|
init_TraitSlot();
|
|
55605
55671
|
|
|
@@ -56241,6 +56307,7 @@ exports.GameMenu = GameMenu;
|
|
|
56241
56307
|
exports.GameOverScreen = GameOverScreen;
|
|
56242
56308
|
exports.HealthBar = HealthBar;
|
|
56243
56309
|
exports.HealthPanel = HealthPanel;
|
|
56310
|
+
exports.HexStrategyBoard = HexStrategyBoard;
|
|
56244
56311
|
exports.InventoryGrid = InventoryGrid;
|
|
56245
56312
|
exports.InventoryPanel = InventoryPanel;
|
|
56246
56313
|
exports.IsometricCanvas = IsometricCanvas;
|