@vanduo-oss/vd3-cbun 1.0.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/CHANGELOG.md +50 -0
- package/LICENSE +22 -0
- package/README.md +160 -0
- package/SKILL.md +119 -0
- package/dist/charts/core.d.ts +273 -0
- package/dist/charts/index.cjs +1828 -0
- package/dist/charts/index.cjs.map +7 -0
- package/dist/charts/index.d.ts +65 -0
- package/dist/charts/index.js +1805 -0
- package/dist/charts/index.js.map +7 -0
- package/dist/charts/vd3-charts.css +51 -0
- package/dist/charts/vue.d.ts +86 -0
- package/dist/flowchart/core.d.ts +288 -0
- package/dist/flowchart/index.cjs +3447 -0
- package/dist/flowchart/index.cjs.map +7 -0
- package/dist/flowchart/index.d.ts +54 -0
- package/dist/flowchart/index.js +3424 -0
- package/dist/flowchart/index.js.map +7 -0
- package/dist/flowchart/vd3-flowchart.css +600 -0
- package/dist/flowchart/vue.d.ts +66 -0
- package/dist/hex-grid/core.d.ts +200 -0
- package/dist/hex-grid/hex-math.cjs +162 -0
- package/dist/hex-grid/hex-math.cjs.map +7 -0
- package/dist/hex-grid/hex-math.d.ts +119 -0
- package/dist/hex-grid/hex-math.js +141 -0
- package/dist/hex-grid/hex-math.js.map +7 -0
- package/dist/hex-grid/index.cjs +915 -0
- package/dist/hex-grid/index.cjs.map +7 -0
- package/dist/hex-grid/index.d.ts +15 -0
- package/dist/hex-grid/index.js +894 -0
- package/dist/hex-grid/index.js.map +7 -0
- package/dist/hex-grid/vue.d.ts +14 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +7 -0
- package/dist/meta.json +551 -0
- package/dist/music-player/core.d.ts +88 -0
- package/dist/music-player/index.cjs +1227 -0
- package/dist/music-player/index.cjs.map +7 -0
- package/dist/music-player/index.d.ts +12 -0
- package/dist/music-player/index.js +1204 -0
- package/dist/music-player/index.js.map +7 -0
- package/dist/music-player/vd3-music-player.css +829 -0
- package/dist/music-player/vue.d.ts +32 -0
- package/package.json +105 -0
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
// Hand-written declarations for the hex-grid canvas core (src/hex-grid/core.js),
|
|
2
|
+
// authored from the source JSDoc (the old repo shipped no core .d.ts files).
|
|
3
|
+
|
|
4
|
+
import type { AxialCoord, TerrainYield } from './hex-math.js';
|
|
5
|
+
|
|
6
|
+
/** Hex-grid component version (mirrors component-versions.json). */
|
|
7
|
+
export declare const VD_HEX_VERSION: string;
|
|
8
|
+
|
|
9
|
+
/** Constructor options for {@link VdHexGrid}. */
|
|
10
|
+
export interface VdHexGridOptions {
|
|
11
|
+
/** Container element; a <canvas> child is found or created inside it. */
|
|
12
|
+
element: HTMLElement;
|
|
13
|
+
/** Explicit canvas to render into (otherwise resolved from `element`). */
|
|
14
|
+
canvas?: HTMLCanvasElement;
|
|
15
|
+
/** Hex radius in px. Default 30. */
|
|
16
|
+
size?: number;
|
|
17
|
+
/** Grid columns (number of hexes). Default 10. */
|
|
18
|
+
width?: number;
|
|
19
|
+
/** Grid rows (number of hexes). Default 10. */
|
|
20
|
+
height?: number;
|
|
21
|
+
/** Grid rotation in radians. Default 0. */
|
|
22
|
+
rotation?: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** A single hex cell stored in the grid. */
|
|
26
|
+
export interface HexCell extends AxialCoord {
|
|
27
|
+
/** Hex center X in world (pre-transform) pixel coordinates. */
|
|
28
|
+
x: number;
|
|
29
|
+
/** Hex center Y in world (pre-transform) pixel coordinates. */
|
|
30
|
+
y: number;
|
|
31
|
+
/** Fill color (defaults to the theme's secondary background). */
|
|
32
|
+
fill: string;
|
|
33
|
+
/** Stroke color (defaults to the theme's border color). */
|
|
34
|
+
stroke: string;
|
|
35
|
+
/** The 6 adjacent hex coordinates. */
|
|
36
|
+
adjacent: AxialCoord[];
|
|
37
|
+
/** Terrain type, or null when unset. */
|
|
38
|
+
terrain: string | null;
|
|
39
|
+
/** Custom data attached via setHexData(). */
|
|
40
|
+
data: Record<string, unknown>;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Pan/zoom transform state. */
|
|
44
|
+
export interface HexGridTransform {
|
|
45
|
+
x: number;
|
|
46
|
+
y: number;
|
|
47
|
+
scale: number;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Event payloads for {@link VdHexGrid.on}. */
|
|
51
|
+
export interface VdHexGridEventMap {
|
|
52
|
+
/** A hex was selected by click/tap. */
|
|
53
|
+
select: HexCell;
|
|
54
|
+
/** Zoom level changed (wheel, pinch, zoomIn/zoomOut, resetView). */
|
|
55
|
+
zoom: { scale: number };
|
|
56
|
+
/** Pan position changed (emitted by resetView). */
|
|
57
|
+
pan: { x: number; y: number };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Per-hex custom render hook, called after each base hex is drawn. */
|
|
61
|
+
export type HexRenderCallback = (ctx: CanvasRenderingContext2D, hex: HexCell, size: number) => void;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* A dynamic controllable hex grid rendered on a canvas, with pan/zoom,
|
|
65
|
+
* selection, terrain, per-hex data, pathfinding, rotation, and custom
|
|
66
|
+
* rendering. Theme colors are read from `--vd-*` CSS custom properties with
|
|
67
|
+
* legacy-token and hardcoded fallbacks.
|
|
68
|
+
*/
|
|
69
|
+
export declare class VdHexGrid {
|
|
70
|
+
/** Mirrors VD_HEX_VERSION. */
|
|
71
|
+
static VERSION: string;
|
|
72
|
+
|
|
73
|
+
constructor(options: VdHexGridOptions);
|
|
74
|
+
|
|
75
|
+
element: HTMLElement;
|
|
76
|
+
canvas: HTMLCanvasElement;
|
|
77
|
+
ctx: CanvasRenderingContext2D;
|
|
78
|
+
/** Hex radius in px. */
|
|
79
|
+
size: number;
|
|
80
|
+
/** Grid columns. */
|
|
81
|
+
width: number;
|
|
82
|
+
/** Grid rows. */
|
|
83
|
+
height: number;
|
|
84
|
+
/** Grid rotation in radians. */
|
|
85
|
+
rotation: number;
|
|
86
|
+
/** All hexes, keyed by `"q,r"`. */
|
|
87
|
+
hexes: Map<string, HexCell>;
|
|
88
|
+
/** Currently selected hex, or null. */
|
|
89
|
+
selectedHex: HexCell | null;
|
|
90
|
+
/** Current pan/zoom transform. */
|
|
91
|
+
transform: HexGridTransform;
|
|
92
|
+
/** Custom render callback set via setCustomRender(), or null. */
|
|
93
|
+
customRenderCallback: HexRenderCallback | null;
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Disconnect theme listeners. Call before discarding the instance (for
|
|
97
|
+
* example on SPA navigation) to avoid leaking observers and media-query
|
|
98
|
+
* listeners.
|
|
99
|
+
*/
|
|
100
|
+
destroy(): void;
|
|
101
|
+
|
|
102
|
+
/** Set hex size (regenerates the grid). */
|
|
103
|
+
setSize(size: number): void;
|
|
104
|
+
|
|
105
|
+
/** Set grid dimensions (regenerates the grid). */
|
|
106
|
+
setDimensions(width: number, height: number): void;
|
|
107
|
+
|
|
108
|
+
/** Reset grid to defaults (size 30, 15x10, no rotation, identity view). */
|
|
109
|
+
reset(): void;
|
|
110
|
+
|
|
111
|
+
/** Fill hexes with random colors. */
|
|
112
|
+
fillRandom(): void;
|
|
113
|
+
|
|
114
|
+
/** Get hex by coordinates. */
|
|
115
|
+
getHex(q: number, r: number): HexCell | undefined;
|
|
116
|
+
|
|
117
|
+
/** Get all hexes. */
|
|
118
|
+
getAllHexes(): HexCell[];
|
|
119
|
+
|
|
120
|
+
/** Set hex fill color. */
|
|
121
|
+
setHexFill(q: number, r: number, color: string): void;
|
|
122
|
+
|
|
123
|
+
/** Reset view to the default position (emits 'pan' and 'zoom'). */
|
|
124
|
+
resetView(): void;
|
|
125
|
+
|
|
126
|
+
/** Zoom in one step (emits 'zoom'). */
|
|
127
|
+
zoomIn(): void;
|
|
128
|
+
|
|
129
|
+
/** Zoom out one step (emits 'zoom'). */
|
|
130
|
+
zoomOut(): void;
|
|
131
|
+
|
|
132
|
+
/** Get a copy of the current transform state. */
|
|
133
|
+
getTransform(): HexGridTransform;
|
|
134
|
+
|
|
135
|
+
/** Subscribe to events ('select', 'zoom', 'pan'). */
|
|
136
|
+
on<K extends keyof VdHexGridEventMap>(
|
|
137
|
+
event: K,
|
|
138
|
+
callback: (data: VdHexGridEventMap[K]) => void,
|
|
139
|
+
): void;
|
|
140
|
+
|
|
141
|
+
/** Set terrain type for a hex (e.g. a TerrainType value). */
|
|
142
|
+
setHexTerrain(q: number, r: number, terrainType: string): void;
|
|
143
|
+
|
|
144
|
+
/** Get terrain type for a hex, or null. */
|
|
145
|
+
getHexTerrain(q: number, r: number): string | null;
|
|
146
|
+
|
|
147
|
+
/** Generate random terrain for all hexes. */
|
|
148
|
+
generateRandomTerrain(): void;
|
|
149
|
+
|
|
150
|
+
/** Get terrain yields for a hex (zero yields when no terrain). */
|
|
151
|
+
getHexYields(q: number, r: number): TerrainYield;
|
|
152
|
+
|
|
153
|
+
/** Get movement cost for a hex (999 when no terrain). */
|
|
154
|
+
getHexMovementCost(q: number, r: number): number;
|
|
155
|
+
|
|
156
|
+
/** Check if a hex is passable (false when no terrain). */
|
|
157
|
+
isHexPassable(q: number, r: number): boolean;
|
|
158
|
+
|
|
159
|
+
/** Merge custom data into a hex. */
|
|
160
|
+
setHexData(q: number, r: number, data: Record<string, unknown>): void;
|
|
161
|
+
|
|
162
|
+
/** Get custom data for a hex (empty object when the hex is missing). */
|
|
163
|
+
getHexData(q: number, r: number): Record<string, unknown>;
|
|
164
|
+
|
|
165
|
+
/** Clear custom data for a hex. */
|
|
166
|
+
clearHexData(q: number, r: number): void;
|
|
167
|
+
|
|
168
|
+
/** Calculate distance between two hexes, in hex steps. */
|
|
169
|
+
hexDistance(q1: number, r1: number, q2: number, r2: number): number;
|
|
170
|
+
|
|
171
|
+
/** Get valid adjacent moves from a hex within the given movement points. */
|
|
172
|
+
getValidMoves(q: number, r: number, movementPoints: number): AxialCoord[];
|
|
173
|
+
|
|
174
|
+
/** Get a path between two hexes (simple BFS); empty array when unreachable. */
|
|
175
|
+
getPath(startQ: number, startR: number, endQ: number, endR: number): AxialCoord[];
|
|
176
|
+
|
|
177
|
+
/** Set grid rotation in radians (regenerates the grid). */
|
|
178
|
+
setRotation(rotation: number): void;
|
|
179
|
+
|
|
180
|
+
/** Get current grid rotation in radians. */
|
|
181
|
+
getRotation(): number;
|
|
182
|
+
|
|
183
|
+
/** Set a custom render callback invoked with (ctx, hex, size) per hex. */
|
|
184
|
+
setCustomRender(callback: HexRenderCallback): void;
|
|
185
|
+
|
|
186
|
+
/** Clear the custom render callback. */
|
|
187
|
+
clearCustomRender(): void;
|
|
188
|
+
|
|
189
|
+
/** Check if a hex exists at coordinates. */
|
|
190
|
+
hasHex(q: number, r: number): boolean;
|
|
191
|
+
|
|
192
|
+
/** Get the number of hexes in the grid. */
|
|
193
|
+
getHexCount(): number;
|
|
194
|
+
|
|
195
|
+
/** Export terrain data as a plain object keyed by `"q,r"`. */
|
|
196
|
+
exportTerrainData(): Record<string, string>;
|
|
197
|
+
|
|
198
|
+
/** Import terrain data previously produced by exportTerrainData(). */
|
|
199
|
+
importTerrainData(data: Record<string, string>): void;
|
|
200
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/hex-grid/hex-math.js
|
|
21
|
+
var hex_math_exports = {};
|
|
22
|
+
__export(hex_math_exports, {
|
|
23
|
+
DEFAULT_TERRAIN_COLOR: () => DEFAULT_TERRAIN_COLOR,
|
|
24
|
+
TERRAIN_COLORS: () => TERRAIN_COLORS,
|
|
25
|
+
TERRAIN_MOVEMENT_COSTS: () => TERRAIN_MOVEMENT_COSTS,
|
|
26
|
+
TERRAIN_YIELDS: () => TERRAIN_YIELDS,
|
|
27
|
+
TerrainType: () => TerrainType,
|
|
28
|
+
axialRound: () => axialRound,
|
|
29
|
+
getAdjacentHexes: () => getAdjacentHexes,
|
|
30
|
+
getHexCorners: () => getHexCorners,
|
|
31
|
+
getMovementCost: () => getMovementCost,
|
|
32
|
+
getTerrainColor: () => getTerrainColor,
|
|
33
|
+
getTerrainYields: () => getTerrainYields,
|
|
34
|
+
hexDistance: () => hexDistance,
|
|
35
|
+
hexToPixel: () => hexToPixel,
|
|
36
|
+
isPassable: () => isPassable,
|
|
37
|
+
pixelToHex: () => pixelToHex,
|
|
38
|
+
rotatePoint: () => rotatePoint,
|
|
39
|
+
unrotatePoint: () => unrotatePoint
|
|
40
|
+
});
|
|
41
|
+
module.exports = __toCommonJS(hex_math_exports);
|
|
42
|
+
function rotatePoint(x, y, rotation = 0) {
|
|
43
|
+
if (!rotation) {
|
|
44
|
+
return { x, y };
|
|
45
|
+
}
|
|
46
|
+
const cosRot = Math.cos(rotation);
|
|
47
|
+
const sinRot = Math.sin(rotation);
|
|
48
|
+
return {
|
|
49
|
+
x: x * cosRot - y * sinRot,
|
|
50
|
+
y: x * sinRot + y * cosRot
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function unrotatePoint(x, y, rotation = 0) {
|
|
54
|
+
return rotatePoint(x, y, -rotation);
|
|
55
|
+
}
|
|
56
|
+
function hexToPixel(q, r, size, rotation = 0) {
|
|
57
|
+
const baseX = size * 1.5 * q;
|
|
58
|
+
const baseY = size * Math.sqrt(3) * (r + q * 0.5);
|
|
59
|
+
return rotatePoint(baseX, baseY, rotation);
|
|
60
|
+
}
|
|
61
|
+
function pixelToHex(px, py, size, rotation = 0) {
|
|
62
|
+
const point = unrotatePoint(px, py, rotation);
|
|
63
|
+
const q = 2 / 3 * point.x / size;
|
|
64
|
+
const r = (-1 / 3 * point.x + Math.sqrt(3) / 3 * point.y) / size;
|
|
65
|
+
return axialRound(q, r);
|
|
66
|
+
}
|
|
67
|
+
function axialRound(q, r) {
|
|
68
|
+
const s = -q - r;
|
|
69
|
+
let rq = Math.round(q);
|
|
70
|
+
let rr = Math.round(r);
|
|
71
|
+
const rs = Math.round(s);
|
|
72
|
+
const qDiff = Math.abs(rq - q);
|
|
73
|
+
const rDiff = Math.abs(rr - r);
|
|
74
|
+
const sDiff = Math.abs(rs - s);
|
|
75
|
+
if (qDiff > rDiff && qDiff > sDiff) {
|
|
76
|
+
rq = -rr - rs;
|
|
77
|
+
} else if (rDiff > sDiff) {
|
|
78
|
+
rr = -rq - rs;
|
|
79
|
+
}
|
|
80
|
+
return { q: rq, r: rr };
|
|
81
|
+
}
|
|
82
|
+
function getHexCorners(x, y, size, rotation = 0) {
|
|
83
|
+
const corners = [];
|
|
84
|
+
for (let i = 0; i < 6; i++) {
|
|
85
|
+
const angleRad = Math.PI / 180 * (60 * i) + rotation;
|
|
86
|
+
corners.push({
|
|
87
|
+
x: x + size * Math.cos(angleRad),
|
|
88
|
+
y: y + size * Math.sin(angleRad)
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
return corners;
|
|
92
|
+
}
|
|
93
|
+
function getAdjacentHexes(q, r) {
|
|
94
|
+
return [
|
|
95
|
+
{ q: q + 1, r },
|
|
96
|
+
{ q: q + 1, r: r - 1 },
|
|
97
|
+
{ q, r: r - 1 },
|
|
98
|
+
{ q: q - 1, r },
|
|
99
|
+
{ q: q - 1, r: r + 1 },
|
|
100
|
+
{ q, r: r + 1 }
|
|
101
|
+
];
|
|
102
|
+
}
|
|
103
|
+
function hexDistance(q1, r1, q2, r2) {
|
|
104
|
+
return (Math.abs(q1 - q2) + Math.abs(q1 + r1 - q2 - r2) + Math.abs(r1 - r2)) / 2;
|
|
105
|
+
}
|
|
106
|
+
var TerrainType = Object.freeze({
|
|
107
|
+
GRASSLAND: "Grassland",
|
|
108
|
+
PLAINS: "Plains",
|
|
109
|
+
DESERT: "Desert",
|
|
110
|
+
TUNDRA: "Tundra",
|
|
111
|
+
SNOW: "Snow",
|
|
112
|
+
MOUNTAIN: "Mountain",
|
|
113
|
+
OCEAN: "Ocean",
|
|
114
|
+
COAST: "Coast"
|
|
115
|
+
});
|
|
116
|
+
var TERRAIN_COLORS = Object.freeze({
|
|
117
|
+
[TerrainType.GRASSLAND]: "#47602f",
|
|
118
|
+
[TerrainType.PLAINS]: "#6e6838",
|
|
119
|
+
[TerrainType.DESERT]: "#bd9a60",
|
|
120
|
+
[TerrainType.TUNDRA]: "#75787b",
|
|
121
|
+
[TerrainType.SNOW]: "#cfdce4",
|
|
122
|
+
[TerrainType.MOUNTAIN]: "#464543",
|
|
123
|
+
[TerrainType.OCEAN]: "#1d354c",
|
|
124
|
+
[TerrainType.COAST]: "#295170"
|
|
125
|
+
});
|
|
126
|
+
var DEFAULT_TERRAIN_COLOR = "#FF00FF";
|
|
127
|
+
var TERRAIN_YIELDS = Object.freeze({
|
|
128
|
+
[TerrainType.GRASSLAND]: { food: 2, production: 0, gold: 0 },
|
|
129
|
+
[TerrainType.PLAINS]: { food: 1, production: 1, gold: 0 },
|
|
130
|
+
[TerrainType.DESERT]: { food: 0, production: 1, gold: 0 },
|
|
131
|
+
[TerrainType.TUNDRA]: { food: 1, production: 0, gold: 0 },
|
|
132
|
+
[TerrainType.SNOW]: { food: 0, production: 0, gold: 0 },
|
|
133
|
+
[TerrainType.COAST]: { food: 1, production: 0, gold: 0 },
|
|
134
|
+
[TerrainType.OCEAN]: { food: 0, production: 0, gold: 0 },
|
|
135
|
+
[TerrainType.MOUNTAIN]: { food: 0, production: 0, gold: 0 }
|
|
136
|
+
});
|
|
137
|
+
var TERRAIN_MOVEMENT_COSTS = Object.freeze({
|
|
138
|
+
[TerrainType.GRASSLAND]: 1,
|
|
139
|
+
[TerrainType.PLAINS]: 1,
|
|
140
|
+
[TerrainType.DESERT]: 1,
|
|
141
|
+
[TerrainType.TUNDRA]: 1,
|
|
142
|
+
[TerrainType.SNOW]: 2,
|
|
143
|
+
[TerrainType.COAST]: 1,
|
|
144
|
+
[TerrainType.OCEAN]: 999,
|
|
145
|
+
// Impassable for land units
|
|
146
|
+
[TerrainType.MOUNTAIN]: 999
|
|
147
|
+
// Impassable
|
|
148
|
+
});
|
|
149
|
+
function isPassable(terrainType) {
|
|
150
|
+
const cost = TERRAIN_MOVEMENT_COSTS[terrainType];
|
|
151
|
+
return cost !== void 0 && cost < 999;
|
|
152
|
+
}
|
|
153
|
+
function getMovementCost(terrainType) {
|
|
154
|
+
return TERRAIN_MOVEMENT_COSTS[terrainType] ?? 999;
|
|
155
|
+
}
|
|
156
|
+
function getTerrainYields(terrainType) {
|
|
157
|
+
return TERRAIN_YIELDS[terrainType] || { food: 0, production: 0, gold: 0 };
|
|
158
|
+
}
|
|
159
|
+
function getTerrainColor(terrainType) {
|
|
160
|
+
return TERRAIN_COLORS[terrainType] || DEFAULT_TERRAIN_COLOR;
|
|
161
|
+
}
|
|
162
|
+
//# sourceMappingURL=hex-math.cjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/hex-grid/hex-math.js"],
|
|
4
|
+
"sourcesContent": ["// Hex math utilities adapted for Vanduo framework\n// Based on web-civ utils/hex-math.js\n\n/**\n * Rotate a point around the origin\n * @param {number} x - X coordinate\n * @param {number} y - Y coordinate\n * @param {number} [rotation=0] - Rotation in radians\n * @returns {{x: number, y: number}} Rotated point\n */\nexport function rotatePoint(x, y, rotation = 0) {\n if (!rotation) {\n return { x, y };\n }\n\n const cosRot = Math.cos(rotation);\n const sinRot = Math.sin(rotation);\n\n return {\n x: x * cosRot - y * sinRot,\n y: x * sinRot + y * cosRot,\n };\n}\n\n/**\n * Apply the inverse of a rotation to a point\n * @param {number} x - X coordinate\n * @param {number} y - Y coordinate\n * @param {number} [rotation=0] - Rotation in radians\n * @returns {{x: number, y: number}} Unrotated point\n */\nexport function unrotatePoint(x, y, rotation = 0) {\n return rotatePoint(x, y, -rotation);\n}\n\n/**\n * Convert hex axial coordinates to pixel coordinates (flat-top orientation)\n * @param {number} q - Hex column coordinate\n * @param {number} r - Hex row coordinate\n * @param {number} size - Hex radius\n * @param {number} [rotation=0] - Optional grid rotation in radians\n * @returns {{x: number, y: number}} Pixel coordinates\n */\nexport function hexToPixel(q, r, size, rotation = 0) {\n const baseX = size * 1.5 * q;\n const baseY = size * Math.sqrt(3) * (r + q * 0.5);\n return rotatePoint(baseX, baseY, rotation);\n}\n\n/**\n * Convert pixel coordinates to hex axial coordinates (flat-top orientation)\n * @param {number} px - Pixel X coordinate\n * @param {number} py - Pixel Y coordinate\n * @param {number} size - Hex radius\n * @param {number} [rotation=0] - Optional grid rotation in radians\n * @returns {{q: number, r: number}} Hex coordinates (rounded)\n */\nexport function pixelToHex(px, py, size, rotation = 0) {\n const point = unrotatePoint(px, py, rotation);\n const q = ((2 / 3) * point.x) / size;\n const r = ((-1 / 3) * point.x + (Math.sqrt(3) / 3) * point.y) / size;\n return axialRound(q, r);\n}\n\n/**\n * Round fractional axial coordinates to nearest hex\n * @param {number} q - Fractional q coordinate\n * @param {number} r - Fractional r coordinate\n * @returns {{q: number, r: number}} Rounded hex coordinates\n */\nexport function axialRound(q, r) {\n const s = -q - r;\n let rq = Math.round(q);\n let rr = Math.round(r);\n const rs = Math.round(s);\n const qDiff = Math.abs(rq - q);\n const rDiff = Math.abs(rr - r);\n const sDiff = Math.abs(rs - s);\n if (qDiff > rDiff && qDiff > sDiff) {\n rq = -rr - rs;\n } else if (rDiff > sDiff) {\n rr = -rq - rs;\n }\n return { q: rq, r: rr };\n}\n\n/**\n * Get the 6 corner points of a flat-top hexagon\n * @param {number} x - Center X coordinate\n * @param {number} y - Center Y coordinate\n * @param {number} size - Hex radius\n * @param {number} [rotation=0] - Optional hex rotation in radians\n * @returns {Array<{x: number, y: number}>} Array of 6 corner points\n */\nexport function getHexCorners(x, y, size, rotation = 0) {\n const corners = [];\n for (let i = 0; i < 6; i++) {\n const angleRad = (Math.PI / 180) * (60 * i) + rotation;\n corners.push({\n x: x + size * Math.cos(angleRad),\n y: y + size * Math.sin(angleRad),\n });\n }\n return corners;\n}\n\n/**\n * Get the 6 adjacent hex coordinates from a given hex\n * @param {number} q - Hex column\n * @param {number} r - Hex row\n * @returns {Array<{q: number, r: number}>} Array of 6 adjacent hex coordinates\n */\nexport function getAdjacentHexes(q, r) {\n return [\n { q: q + 1, r: r },\n { q: q + 1, r: r - 1 },\n { q: q, r: r - 1 },\n { q: q - 1, r: r },\n { q: q - 1, r: r + 1 },\n { q: q, r: r + 1 },\n ];\n}\n\n/**\n * Calculate distance between two hexes using axial coordinates\n * @param {number} q1 - First hex q coordinate\n * @param {number} r1 - First hex r coordinate\n * @param {number} q2 - Second hex q coordinate\n * @param {number} r2 - Second hex r coordinate\n * @returns {number} Distance in hex steps\n */\nexport function hexDistance(q1, r1, q2, r2) {\n return (Math.abs(q1 - q2) + Math.abs(q1 + r1 - q2 - r2) + Math.abs(r1 - r2)) / 2;\n}\n\n/**\n * Terrain types available in the system\n */\nexport const TerrainType = Object.freeze({\n GRASSLAND: 'Grassland',\n PLAINS: 'Plains',\n DESERT: 'Desert',\n TUNDRA: 'Tundra',\n SNOW: 'Snow',\n MOUNTAIN: 'Mountain',\n OCEAN: 'Ocean',\n COAST: 'Coast',\n});\n\n/**\n * Terrain colors for rendering\n */\nexport const TERRAIN_COLORS = Object.freeze({\n [TerrainType.GRASSLAND]: '#47602f',\n [TerrainType.PLAINS]: '#6e6838',\n [TerrainType.DESERT]: '#bd9a60',\n [TerrainType.TUNDRA]: '#75787b',\n [TerrainType.SNOW]: '#cfdce4',\n [TerrainType.MOUNTAIN]: '#464543',\n [TerrainType.OCEAN]: '#1d354c',\n [TerrainType.COAST]: '#295170',\n});\n\n/**\n * Default terrain color for unknown types\n */\nexport const DEFAULT_TERRAIN_COLOR = '#FF00FF';\n\n/**\n * Terrain yields - resources generated per turn from each terrain type\n */\nexport const TERRAIN_YIELDS = Object.freeze({\n [TerrainType.GRASSLAND]: { food: 2, production: 0, gold: 0 },\n [TerrainType.PLAINS]: { food: 1, production: 1, gold: 0 },\n [TerrainType.DESERT]: { food: 0, production: 1, gold: 0 },\n [TerrainType.TUNDRA]: { food: 1, production: 0, gold: 0 },\n [TerrainType.SNOW]: { food: 0, production: 0, gold: 0 },\n [TerrainType.COAST]: { food: 1, production: 0, gold: 0 },\n [TerrainType.OCEAN]: { food: 0, production: 0, gold: 0 },\n [TerrainType.MOUNTAIN]: { food: 0, production: 0, gold: 0 },\n});\n\n/**\n * Movement costs for units based on terrain\n * Higher cost = harder to move through\n */\nexport const TERRAIN_MOVEMENT_COSTS = Object.freeze({\n [TerrainType.GRASSLAND]: 1,\n [TerrainType.PLAINS]: 1,\n [TerrainType.DESERT]: 1,\n [TerrainType.TUNDRA]: 1,\n [TerrainType.SNOW]: 2,\n [TerrainType.COAST]: 1,\n [TerrainType.OCEAN]: 999, // Impassable for land units\n [TerrainType.MOUNTAIN]: 999, // Impassable\n});\n\n/**\n * Check if terrain is passable for land units\n * @param {string} terrainType - Terrain type\n * @returns {boolean} True if passable\n */\nexport function isPassable(terrainType) {\n const cost = TERRAIN_MOVEMENT_COSTS[terrainType];\n return cost !== undefined && cost < 999;\n}\n\n/**\n * Get movement cost for terrain\n * @param {string} terrainType - Terrain type\n * @returns {number} Movement cost\n */\nexport function getMovementCost(terrainType) {\n return TERRAIN_MOVEMENT_COSTS[terrainType] ?? 999;\n}\n\n/**\n * Get terrain yields\n * @param {string} terrainType - Terrain type\n * @returns {Object} Yields object {food, production, gold}\n */\nexport function getTerrainYields(terrainType) {\n return TERRAIN_YIELDS[terrainType] || { food: 0, production: 0, gold: 0 };\n}\n\n/**\n * Get terrain color\n * @param {string} terrainType - Terrain type\n * @returns {string} Hex color string\n */\nexport function getTerrainColor(terrainType) {\n return TERRAIN_COLORS[terrainType] || DEFAULT_TERRAIN_COLOR;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
// Hand-written declarations for src/hex-grid/hex-math.js, authored from the
|
|
2
|
+
// source JSDoc (the old repo shipped no core .d.ts files). The module is pure:
|
|
3
|
+
// no DOM or window access, importable in any JavaScript runtime.
|
|
4
|
+
|
|
5
|
+
/** A 2D point in pixel space. */
|
|
6
|
+
export interface Point {
|
|
7
|
+
x: number;
|
|
8
|
+
y: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** Axial hex coordinates. */
|
|
12
|
+
export interface AxialCoord {
|
|
13
|
+
q: number;
|
|
14
|
+
r: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Resources generated per turn from a terrain type. */
|
|
18
|
+
export interface TerrainYield {
|
|
19
|
+
food: number;
|
|
20
|
+
production: number;
|
|
21
|
+
gold: number;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Rotate a point around the origin.
|
|
26
|
+
* @param rotation Rotation in radians (default 0).
|
|
27
|
+
*/
|
|
28
|
+
export declare function rotatePoint(x: number, y: number, rotation?: number): Point;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Apply the inverse of a rotation to a point.
|
|
32
|
+
* @param rotation Rotation in radians (default 0).
|
|
33
|
+
*/
|
|
34
|
+
export declare function unrotatePoint(x: number, y: number, rotation?: number): Point;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Convert hex axial coordinates to pixel coordinates (flat-top orientation).
|
|
38
|
+
* @param size Hex radius.
|
|
39
|
+
* @param rotation Optional grid rotation in radians (default 0).
|
|
40
|
+
*/
|
|
41
|
+
export declare function hexToPixel(q: number, r: number, size: number, rotation?: number): Point;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Convert pixel coordinates to hex axial coordinates (flat-top orientation),
|
|
45
|
+
* rounded to the nearest hex.
|
|
46
|
+
* @param size Hex radius.
|
|
47
|
+
* @param rotation Optional grid rotation in radians (default 0).
|
|
48
|
+
*/
|
|
49
|
+
export declare function pixelToHex(
|
|
50
|
+
px: number,
|
|
51
|
+
py: number,
|
|
52
|
+
size: number,
|
|
53
|
+
rotation?: number,
|
|
54
|
+
): AxialCoord;
|
|
55
|
+
|
|
56
|
+
/** Round fractional axial coordinates to the nearest hex. */
|
|
57
|
+
export declare function axialRound(q: number, r: number): AxialCoord;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Get the 6 corner points of a flat-top hexagon.
|
|
61
|
+
* @param x Center X coordinate.
|
|
62
|
+
* @param y Center Y coordinate.
|
|
63
|
+
* @param size Hex radius.
|
|
64
|
+
* @param rotation Optional hex rotation in radians (default 0).
|
|
65
|
+
*/
|
|
66
|
+
export declare function getHexCorners(
|
|
67
|
+
x: number,
|
|
68
|
+
y: number,
|
|
69
|
+
size: number,
|
|
70
|
+
rotation?: number,
|
|
71
|
+
): Point[];
|
|
72
|
+
|
|
73
|
+
/** Get the 6 adjacent hex coordinates from a given hex. */
|
|
74
|
+
export declare function getAdjacentHexes(q: number, r: number): AxialCoord[];
|
|
75
|
+
|
|
76
|
+
/** Calculate distance between two hexes (axial coordinates), in hex steps. */
|
|
77
|
+
export declare function hexDistance(q1: number, r1: number, q2: number, r2: number): number;
|
|
78
|
+
|
|
79
|
+
/** Terrain types available in the system (frozen). */
|
|
80
|
+
export declare const TerrainType: Readonly<{
|
|
81
|
+
GRASSLAND: 'Grassland';
|
|
82
|
+
PLAINS: 'Plains';
|
|
83
|
+
DESERT: 'Desert';
|
|
84
|
+
TUNDRA: 'Tundra';
|
|
85
|
+
SNOW: 'Snow';
|
|
86
|
+
MOUNTAIN: 'Mountain';
|
|
87
|
+
OCEAN: 'Ocean';
|
|
88
|
+
COAST: 'Coast';
|
|
89
|
+
}>;
|
|
90
|
+
|
|
91
|
+
/** Union of the terrain type values (e.g. 'Grassland'). */
|
|
92
|
+
export type TerrainTypeValue = (typeof TerrainType)[keyof typeof TerrainType];
|
|
93
|
+
|
|
94
|
+
/** Terrain colors for rendering, keyed by terrain type value (frozen). */
|
|
95
|
+
export declare const TERRAIN_COLORS: Readonly<Record<TerrainTypeValue, string>>;
|
|
96
|
+
|
|
97
|
+
/** Default terrain color for unknown types. */
|
|
98
|
+
export declare const DEFAULT_TERRAIN_COLOR: string;
|
|
99
|
+
|
|
100
|
+
/** Terrain yields per terrain type value (frozen). */
|
|
101
|
+
export declare const TERRAIN_YIELDS: Readonly<Record<TerrainTypeValue, TerrainYield>>;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Movement costs for units based on terrain, keyed by terrain type value
|
|
105
|
+
* (frozen). Higher cost = harder to move through; 999 = impassable.
|
|
106
|
+
*/
|
|
107
|
+
export declare const TERRAIN_MOVEMENT_COSTS: Readonly<Record<TerrainTypeValue, number>>;
|
|
108
|
+
|
|
109
|
+
/** Check if terrain is passable for land units (cost defined and < 999). */
|
|
110
|
+
export declare function isPassable(terrainType: string): boolean;
|
|
111
|
+
|
|
112
|
+
/** Get movement cost for terrain (999 for unknown types). */
|
|
113
|
+
export declare function getMovementCost(terrainType: string): number;
|
|
114
|
+
|
|
115
|
+
/** Get terrain yields ({ food: 0, production: 0, gold: 0 } for unknown types). */
|
|
116
|
+
export declare function getTerrainYields(terrainType: string): TerrainYield;
|
|
117
|
+
|
|
118
|
+
/** Get terrain color (DEFAULT_TERRAIN_COLOR for unknown types). */
|
|
119
|
+
export declare function getTerrainColor(terrainType: string): string;
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
// src/hex-grid/hex-math.js
|
|
2
|
+
function rotatePoint(x, y, rotation = 0) {
|
|
3
|
+
if (!rotation) {
|
|
4
|
+
return { x, y };
|
|
5
|
+
}
|
|
6
|
+
const cosRot = Math.cos(rotation);
|
|
7
|
+
const sinRot = Math.sin(rotation);
|
|
8
|
+
return {
|
|
9
|
+
x: x * cosRot - y * sinRot,
|
|
10
|
+
y: x * sinRot + y * cosRot
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
function unrotatePoint(x, y, rotation = 0) {
|
|
14
|
+
return rotatePoint(x, y, -rotation);
|
|
15
|
+
}
|
|
16
|
+
function hexToPixel(q, r, size, rotation = 0) {
|
|
17
|
+
const baseX = size * 1.5 * q;
|
|
18
|
+
const baseY = size * Math.sqrt(3) * (r + q * 0.5);
|
|
19
|
+
return rotatePoint(baseX, baseY, rotation);
|
|
20
|
+
}
|
|
21
|
+
function pixelToHex(px, py, size, rotation = 0) {
|
|
22
|
+
const point = unrotatePoint(px, py, rotation);
|
|
23
|
+
const q = 2 / 3 * point.x / size;
|
|
24
|
+
const r = (-1 / 3 * point.x + Math.sqrt(3) / 3 * point.y) / size;
|
|
25
|
+
return axialRound(q, r);
|
|
26
|
+
}
|
|
27
|
+
function axialRound(q, r) {
|
|
28
|
+
const s = -q - r;
|
|
29
|
+
let rq = Math.round(q);
|
|
30
|
+
let rr = Math.round(r);
|
|
31
|
+
const rs = Math.round(s);
|
|
32
|
+
const qDiff = Math.abs(rq - q);
|
|
33
|
+
const rDiff = Math.abs(rr - r);
|
|
34
|
+
const sDiff = Math.abs(rs - s);
|
|
35
|
+
if (qDiff > rDiff && qDiff > sDiff) {
|
|
36
|
+
rq = -rr - rs;
|
|
37
|
+
} else if (rDiff > sDiff) {
|
|
38
|
+
rr = -rq - rs;
|
|
39
|
+
}
|
|
40
|
+
return { q: rq, r: rr };
|
|
41
|
+
}
|
|
42
|
+
function getHexCorners(x, y, size, rotation = 0) {
|
|
43
|
+
const corners = [];
|
|
44
|
+
for (let i = 0; i < 6; i++) {
|
|
45
|
+
const angleRad = Math.PI / 180 * (60 * i) + rotation;
|
|
46
|
+
corners.push({
|
|
47
|
+
x: x + size * Math.cos(angleRad),
|
|
48
|
+
y: y + size * Math.sin(angleRad)
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
return corners;
|
|
52
|
+
}
|
|
53
|
+
function getAdjacentHexes(q, r) {
|
|
54
|
+
return [
|
|
55
|
+
{ q: q + 1, r },
|
|
56
|
+
{ q: q + 1, r: r - 1 },
|
|
57
|
+
{ q, r: r - 1 },
|
|
58
|
+
{ q: q - 1, r },
|
|
59
|
+
{ q: q - 1, r: r + 1 },
|
|
60
|
+
{ q, r: r + 1 }
|
|
61
|
+
];
|
|
62
|
+
}
|
|
63
|
+
function hexDistance(q1, r1, q2, r2) {
|
|
64
|
+
return (Math.abs(q1 - q2) + Math.abs(q1 + r1 - q2 - r2) + Math.abs(r1 - r2)) / 2;
|
|
65
|
+
}
|
|
66
|
+
var TerrainType = Object.freeze({
|
|
67
|
+
GRASSLAND: "Grassland",
|
|
68
|
+
PLAINS: "Plains",
|
|
69
|
+
DESERT: "Desert",
|
|
70
|
+
TUNDRA: "Tundra",
|
|
71
|
+
SNOW: "Snow",
|
|
72
|
+
MOUNTAIN: "Mountain",
|
|
73
|
+
OCEAN: "Ocean",
|
|
74
|
+
COAST: "Coast"
|
|
75
|
+
});
|
|
76
|
+
var TERRAIN_COLORS = Object.freeze({
|
|
77
|
+
[TerrainType.GRASSLAND]: "#47602f",
|
|
78
|
+
[TerrainType.PLAINS]: "#6e6838",
|
|
79
|
+
[TerrainType.DESERT]: "#bd9a60",
|
|
80
|
+
[TerrainType.TUNDRA]: "#75787b",
|
|
81
|
+
[TerrainType.SNOW]: "#cfdce4",
|
|
82
|
+
[TerrainType.MOUNTAIN]: "#464543",
|
|
83
|
+
[TerrainType.OCEAN]: "#1d354c",
|
|
84
|
+
[TerrainType.COAST]: "#295170"
|
|
85
|
+
});
|
|
86
|
+
var DEFAULT_TERRAIN_COLOR = "#FF00FF";
|
|
87
|
+
var TERRAIN_YIELDS = Object.freeze({
|
|
88
|
+
[TerrainType.GRASSLAND]: { food: 2, production: 0, gold: 0 },
|
|
89
|
+
[TerrainType.PLAINS]: { food: 1, production: 1, gold: 0 },
|
|
90
|
+
[TerrainType.DESERT]: { food: 0, production: 1, gold: 0 },
|
|
91
|
+
[TerrainType.TUNDRA]: { food: 1, production: 0, gold: 0 },
|
|
92
|
+
[TerrainType.SNOW]: { food: 0, production: 0, gold: 0 },
|
|
93
|
+
[TerrainType.COAST]: { food: 1, production: 0, gold: 0 },
|
|
94
|
+
[TerrainType.OCEAN]: { food: 0, production: 0, gold: 0 },
|
|
95
|
+
[TerrainType.MOUNTAIN]: { food: 0, production: 0, gold: 0 }
|
|
96
|
+
});
|
|
97
|
+
var TERRAIN_MOVEMENT_COSTS = Object.freeze({
|
|
98
|
+
[TerrainType.GRASSLAND]: 1,
|
|
99
|
+
[TerrainType.PLAINS]: 1,
|
|
100
|
+
[TerrainType.DESERT]: 1,
|
|
101
|
+
[TerrainType.TUNDRA]: 1,
|
|
102
|
+
[TerrainType.SNOW]: 2,
|
|
103
|
+
[TerrainType.COAST]: 1,
|
|
104
|
+
[TerrainType.OCEAN]: 999,
|
|
105
|
+
// Impassable for land units
|
|
106
|
+
[TerrainType.MOUNTAIN]: 999
|
|
107
|
+
// Impassable
|
|
108
|
+
});
|
|
109
|
+
function isPassable(terrainType) {
|
|
110
|
+
const cost = TERRAIN_MOVEMENT_COSTS[terrainType];
|
|
111
|
+
return cost !== void 0 && cost < 999;
|
|
112
|
+
}
|
|
113
|
+
function getMovementCost(terrainType) {
|
|
114
|
+
return TERRAIN_MOVEMENT_COSTS[terrainType] ?? 999;
|
|
115
|
+
}
|
|
116
|
+
function getTerrainYields(terrainType) {
|
|
117
|
+
return TERRAIN_YIELDS[terrainType] || { food: 0, production: 0, gold: 0 };
|
|
118
|
+
}
|
|
119
|
+
function getTerrainColor(terrainType) {
|
|
120
|
+
return TERRAIN_COLORS[terrainType] || DEFAULT_TERRAIN_COLOR;
|
|
121
|
+
}
|
|
122
|
+
export {
|
|
123
|
+
DEFAULT_TERRAIN_COLOR,
|
|
124
|
+
TERRAIN_COLORS,
|
|
125
|
+
TERRAIN_MOVEMENT_COSTS,
|
|
126
|
+
TERRAIN_YIELDS,
|
|
127
|
+
TerrainType,
|
|
128
|
+
axialRound,
|
|
129
|
+
getAdjacentHexes,
|
|
130
|
+
getHexCorners,
|
|
131
|
+
getMovementCost,
|
|
132
|
+
getTerrainColor,
|
|
133
|
+
getTerrainYields,
|
|
134
|
+
hexDistance,
|
|
135
|
+
hexToPixel,
|
|
136
|
+
isPassable,
|
|
137
|
+
pixelToHex,
|
|
138
|
+
rotatePoint,
|
|
139
|
+
unrotatePoint
|
|
140
|
+
};
|
|
141
|
+
//# sourceMappingURL=hex-math.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/hex-grid/hex-math.js"],
|
|
4
|
+
"sourcesContent": ["// Hex math utilities adapted for Vanduo framework\n// Based on web-civ utils/hex-math.js\n\n/**\n * Rotate a point around the origin\n * @param {number} x - X coordinate\n * @param {number} y - Y coordinate\n * @param {number} [rotation=0] - Rotation in radians\n * @returns {{x: number, y: number}} Rotated point\n */\nexport function rotatePoint(x, y, rotation = 0) {\n if (!rotation) {\n return { x, y };\n }\n\n const cosRot = Math.cos(rotation);\n const sinRot = Math.sin(rotation);\n\n return {\n x: x * cosRot - y * sinRot,\n y: x * sinRot + y * cosRot,\n };\n}\n\n/**\n * Apply the inverse of a rotation to a point\n * @param {number} x - X coordinate\n * @param {number} y - Y coordinate\n * @param {number} [rotation=0] - Rotation in radians\n * @returns {{x: number, y: number}} Unrotated point\n */\nexport function unrotatePoint(x, y, rotation = 0) {\n return rotatePoint(x, y, -rotation);\n}\n\n/**\n * Convert hex axial coordinates to pixel coordinates (flat-top orientation)\n * @param {number} q - Hex column coordinate\n * @param {number} r - Hex row coordinate\n * @param {number} size - Hex radius\n * @param {number} [rotation=0] - Optional grid rotation in radians\n * @returns {{x: number, y: number}} Pixel coordinates\n */\nexport function hexToPixel(q, r, size, rotation = 0) {\n const baseX = size * 1.5 * q;\n const baseY = size * Math.sqrt(3) * (r + q * 0.5);\n return rotatePoint(baseX, baseY, rotation);\n}\n\n/**\n * Convert pixel coordinates to hex axial coordinates (flat-top orientation)\n * @param {number} px - Pixel X coordinate\n * @param {number} py - Pixel Y coordinate\n * @param {number} size - Hex radius\n * @param {number} [rotation=0] - Optional grid rotation in radians\n * @returns {{q: number, r: number}} Hex coordinates (rounded)\n */\nexport function pixelToHex(px, py, size, rotation = 0) {\n const point = unrotatePoint(px, py, rotation);\n const q = ((2 / 3) * point.x) / size;\n const r = ((-1 / 3) * point.x + (Math.sqrt(3) / 3) * point.y) / size;\n return axialRound(q, r);\n}\n\n/**\n * Round fractional axial coordinates to nearest hex\n * @param {number} q - Fractional q coordinate\n * @param {number} r - Fractional r coordinate\n * @returns {{q: number, r: number}} Rounded hex coordinates\n */\nexport function axialRound(q, r) {\n const s = -q - r;\n let rq = Math.round(q);\n let rr = Math.round(r);\n const rs = Math.round(s);\n const qDiff = Math.abs(rq - q);\n const rDiff = Math.abs(rr - r);\n const sDiff = Math.abs(rs - s);\n if (qDiff > rDiff && qDiff > sDiff) {\n rq = -rr - rs;\n } else if (rDiff > sDiff) {\n rr = -rq - rs;\n }\n return { q: rq, r: rr };\n}\n\n/**\n * Get the 6 corner points of a flat-top hexagon\n * @param {number} x - Center X coordinate\n * @param {number} y - Center Y coordinate\n * @param {number} size - Hex radius\n * @param {number} [rotation=0] - Optional hex rotation in radians\n * @returns {Array<{x: number, y: number}>} Array of 6 corner points\n */\nexport function getHexCorners(x, y, size, rotation = 0) {\n const corners = [];\n for (let i = 0; i < 6; i++) {\n const angleRad = (Math.PI / 180) * (60 * i) + rotation;\n corners.push({\n x: x + size * Math.cos(angleRad),\n y: y + size * Math.sin(angleRad),\n });\n }\n return corners;\n}\n\n/**\n * Get the 6 adjacent hex coordinates from a given hex\n * @param {number} q - Hex column\n * @param {number} r - Hex row\n * @returns {Array<{q: number, r: number}>} Array of 6 adjacent hex coordinates\n */\nexport function getAdjacentHexes(q, r) {\n return [\n { q: q + 1, r: r },\n { q: q + 1, r: r - 1 },\n { q: q, r: r - 1 },\n { q: q - 1, r: r },\n { q: q - 1, r: r + 1 },\n { q: q, r: r + 1 },\n ];\n}\n\n/**\n * Calculate distance between two hexes using axial coordinates\n * @param {number} q1 - First hex q coordinate\n * @param {number} r1 - First hex r coordinate\n * @param {number} q2 - Second hex q coordinate\n * @param {number} r2 - Second hex r coordinate\n * @returns {number} Distance in hex steps\n */\nexport function hexDistance(q1, r1, q2, r2) {\n return (Math.abs(q1 - q2) + Math.abs(q1 + r1 - q2 - r2) + Math.abs(r1 - r2)) / 2;\n}\n\n/**\n * Terrain types available in the system\n */\nexport const TerrainType = Object.freeze({\n GRASSLAND: 'Grassland',\n PLAINS: 'Plains',\n DESERT: 'Desert',\n TUNDRA: 'Tundra',\n SNOW: 'Snow',\n MOUNTAIN: 'Mountain',\n OCEAN: 'Ocean',\n COAST: 'Coast',\n});\n\n/**\n * Terrain colors for rendering\n */\nexport const TERRAIN_COLORS = Object.freeze({\n [TerrainType.GRASSLAND]: '#47602f',\n [TerrainType.PLAINS]: '#6e6838',\n [TerrainType.DESERT]: '#bd9a60',\n [TerrainType.TUNDRA]: '#75787b',\n [TerrainType.SNOW]: '#cfdce4',\n [TerrainType.MOUNTAIN]: '#464543',\n [TerrainType.OCEAN]: '#1d354c',\n [TerrainType.COAST]: '#295170',\n});\n\n/**\n * Default terrain color for unknown types\n */\nexport const DEFAULT_TERRAIN_COLOR = '#FF00FF';\n\n/**\n * Terrain yields - resources generated per turn from each terrain type\n */\nexport const TERRAIN_YIELDS = Object.freeze({\n [TerrainType.GRASSLAND]: { food: 2, production: 0, gold: 0 },\n [TerrainType.PLAINS]: { food: 1, production: 1, gold: 0 },\n [TerrainType.DESERT]: { food: 0, production: 1, gold: 0 },\n [TerrainType.TUNDRA]: { food: 1, production: 0, gold: 0 },\n [TerrainType.SNOW]: { food: 0, production: 0, gold: 0 },\n [TerrainType.COAST]: { food: 1, production: 0, gold: 0 },\n [TerrainType.OCEAN]: { food: 0, production: 0, gold: 0 },\n [TerrainType.MOUNTAIN]: { food: 0, production: 0, gold: 0 },\n});\n\n/**\n * Movement costs for units based on terrain\n * Higher cost = harder to move through\n */\nexport const TERRAIN_MOVEMENT_COSTS = Object.freeze({\n [TerrainType.GRASSLAND]: 1,\n [TerrainType.PLAINS]: 1,\n [TerrainType.DESERT]: 1,\n [TerrainType.TUNDRA]: 1,\n [TerrainType.SNOW]: 2,\n [TerrainType.COAST]: 1,\n [TerrainType.OCEAN]: 999, // Impassable for land units\n [TerrainType.MOUNTAIN]: 999, // Impassable\n});\n\n/**\n * Check if terrain is passable for land units\n * @param {string} terrainType - Terrain type\n * @returns {boolean} True if passable\n */\nexport function isPassable(terrainType) {\n const cost = TERRAIN_MOVEMENT_COSTS[terrainType];\n return cost !== undefined && cost < 999;\n}\n\n/**\n * Get movement cost for terrain\n * @param {string} terrainType - Terrain type\n * @returns {number} Movement cost\n */\nexport function getMovementCost(terrainType) {\n return TERRAIN_MOVEMENT_COSTS[terrainType] ?? 999;\n}\n\n/**\n * Get terrain yields\n * @param {string} terrainType - Terrain type\n * @returns {Object} Yields object {food, production, gold}\n */\nexport function getTerrainYields(terrainType) {\n return TERRAIN_YIELDS[terrainType] || { food: 0, production: 0, gold: 0 };\n}\n\n/**\n * Get terrain color\n * @param {string} terrainType - Terrain type\n * @returns {string} Hex color string\n */\nexport function getTerrainColor(terrainType) {\n return TERRAIN_COLORS[terrainType] || DEFAULT_TERRAIN_COLOR;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|