@lagless/2d-map-generator 0.0.48
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/LICENSE +26 -0
- package/README.md +373 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +27 -0
- package/dist/lib/collision/rapier-provider.d.ts +54 -0
- package/dist/lib/collision/rapier-provider.d.ts.map +1 -0
- package/dist/lib/collision/rapier-provider.js +67 -0
- package/dist/lib/collision/spatial-grid-provider.d.ts +19 -0
- package/dist/lib/collision/spatial-grid-provider.d.ts.map +1 -0
- package/dist/lib/collision/spatial-grid-provider.js +127 -0
- package/dist/lib/core/generated-map.d.ts +14 -0
- package/dist/lib/core/generated-map.d.ts.map +1 -0
- package/dist/lib/core/generated-map.js +17 -0
- package/dist/lib/core/map-dimensions.d.ts +8 -0
- package/dist/lib/core/map-dimensions.d.ts.map +1 -0
- package/dist/lib/core/map-dimensions.js +7 -0
- package/dist/lib/core/map-generator.d.ts +14 -0
- package/dist/lib/core/map-generator.d.ts.map +1 -0
- package/dist/lib/core/map-generator.js +124 -0
- package/dist/lib/core/terrain-query.d.ts +17 -0
- package/dist/lib/core/terrain-query.d.ts.map +1 -0
- package/dist/lib/core/terrain-query.js +54 -0
- package/dist/lib/features/biome-feature.d.ts +10 -0
- package/dist/lib/features/biome-feature.d.ts.map +1 -0
- package/dist/lib/features/biome-feature.js +9 -0
- package/dist/lib/features/bridge-feature.d.ts +10 -0
- package/dist/lib/features/bridge-feature.d.ts.map +1 -0
- package/dist/lib/features/bridge-feature.js +55 -0
- package/dist/lib/features/grass-feature.d.ts +10 -0
- package/dist/lib/features/grass-feature.d.ts.map +1 -0
- package/dist/lib/features/grass-feature.js +36 -0
- package/dist/lib/features/ground-patch-feature.d.ts +10 -0
- package/dist/lib/features/ground-patch-feature.d.ts.map +1 -0
- package/dist/lib/features/ground-patch-feature.js +57 -0
- package/dist/lib/features/lake-feature.d.ts +10 -0
- package/dist/lib/features/lake-feature.d.ts.map +1 -0
- package/dist/lib/features/lake-feature.js +65 -0
- package/dist/lib/features/object-placement-feature.d.ts +18 -0
- package/dist/lib/features/object-placement-feature.d.ts.map +1 -0
- package/dist/lib/features/object-placement-feature.js +229 -0
- package/dist/lib/features/places-feature.d.ts +10 -0
- package/dist/lib/features/places-feature.d.ts.map +1 -0
- package/dist/lib/features/places-feature.js +14 -0
- package/dist/lib/features/river-feature.d.ts +10 -0
- package/dist/lib/features/river-feature.d.ts.map +1 -0
- package/dist/lib/features/river-feature.js +122 -0
- package/dist/lib/features/shore-feature.d.ts +10 -0
- package/dist/lib/features/shore-feature.d.ts.map +1 -0
- package/dist/lib/features/shore-feature.js +20 -0
- package/dist/lib/math/catmull-rom.d.ts +12 -0
- package/dist/lib/math/catmull-rom.d.ts.map +1 -0
- package/dist/lib/math/catmull-rom.js +42 -0
- package/dist/lib/math/collision-test.d.ts +14 -0
- package/dist/lib/math/collision-test.d.ts.map +1 -0
- package/dist/lib/math/collision-test.js +29 -0
- package/dist/lib/math/jagged-aabb.d.ts +12 -0
- package/dist/lib/math/jagged-aabb.d.ts.map +1 -0
- package/dist/lib/math/jagged-aabb.js +47 -0
- package/dist/lib/math/polygon-utils.d.ts +19 -0
- package/dist/lib/math/polygon-utils.d.ts.map +1 -0
- package/dist/lib/math/polygon-utils.js +79 -0
- package/dist/lib/math/river-polygon.d.ts +12 -0
- package/dist/lib/math/river-polygon.d.ts.map +1 -0
- package/dist/lib/math/river-polygon.js +84 -0
- package/dist/lib/math/spline.d.ts +15 -0
- package/dist/lib/math/spline.d.ts.map +1 -0
- package/dist/lib/math/spline.js +127 -0
- package/dist/lib/physics/canopy-sensor-tag.d.ts +2 -0
- package/dist/lib/physics/canopy-sensor-tag.d.ts.map +1 -0
- package/dist/lib/physics/canopy-sensor-tag.js +1 -0
- package/dist/lib/physics/create-map-colliders.d.ts +12 -0
- package/dist/lib/physics/create-map-colliders.d.ts.map +1 -0
- package/dist/lib/physics/create-map-colliders.js +29 -0
- package/dist/lib/presets/standard-biome.d.ts +3 -0
- package/dist/lib/presets/standard-biome.d.ts.map +1 -0
- package/dist/lib/presets/standard-biome.js +9 -0
- package/dist/lib/presets/standard-map.d.ts +6 -0
- package/dist/lib/presets/standard-map.d.ts.map +1 -0
- package/dist/lib/presets/standard-map.js +61 -0
- package/dist/lib/presets/standard-objects.d.ts +6 -0
- package/dist/lib/presets/standard-objects.d.ts.map +1 -0
- package/dist/lib/presets/standard-objects.js +25 -0
- package/dist/lib/types/collision-provider.d.ts +8 -0
- package/dist/lib/types/collision-provider.d.ts.map +1 -0
- package/dist/lib/types/collision-provider.js +1 -0
- package/dist/lib/types/feature-configs.d.ts +139 -0
- package/dist/lib/types/feature-configs.d.ts.map +1 -0
- package/dist/lib/types/feature-configs.js +8 -0
- package/dist/lib/types/feature.d.ts +31 -0
- package/dist/lib/types/feature.d.ts.map +1 -0
- package/dist/lib/types/feature.js +12 -0
- package/dist/lib/types/generated-map.d.ts +10 -0
- package/dist/lib/types/generated-map.d.ts.map +1 -0
- package/dist/lib/types/generated-map.js +1 -0
- package/dist/lib/types/generated-river.d.ts +23 -0
- package/dist/lib/types/generated-river.d.ts.map +1 -0
- package/dist/lib/types/generated-river.js +1 -0
- package/dist/lib/types/geometry.d.ts +30 -0
- package/dist/lib/types/geometry.d.ts.map +1 -0
- package/dist/lib/types/geometry.js +5 -0
- package/dist/lib/types/index.d.ts +16 -0
- package/dist/lib/types/index.d.ts.map +1 -0
- package/dist/lib/types/index.js +5 -0
- package/dist/lib/types/map-generator-config.d.ts +8 -0
- package/dist/lib/types/map-generator-config.d.ts.map +1 -0
- package/dist/lib/types/map-generator-config.js +1 -0
- package/dist/lib/types/object-def.d.ts +58 -0
- package/dist/lib/types/object-def.d.ts.map +1 -0
- package/dist/lib/types/object-def.js +5 -0
- package/dist/lib/types/placed-object.d.ts +19 -0
- package/dist/lib/types/placed-object.d.ts.map +1 -0
- package/dist/lib/types/placed-object.js +10 -0
- package/dist/lib/types/prng-interface.d.ts +14 -0
- package/dist/lib/types/prng-interface.d.ts.map +1 -0
- package/dist/lib/types/prng-interface.js +1 -0
- package/dist/lib/utils/extract-canopy-zones.d.ts +22 -0
- package/dist/lib/utils/extract-canopy-zones.d.ts.map +1 -0
- package/dist/lib/utils/extract-canopy-zones.js +34 -0
- package/dist/lib/utils/sort-placed-objects.d.ts +3 -0
- package/dist/lib/utils/sort-placed-objects.d.ts.map +1 -0
- package/dist/lib/utils/sort-placed-objects.js +3 -0
- package/dist/tsconfig.lib.tsbuildinfo +1 -0
- package/package.json +42 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { ShapeType } from '../types/geometry.js';
|
|
2
|
+
import { testCircleCircle, testCircleAabb, testAabbAabb } from '../math/collision-test.js';
|
|
3
|
+
export class SpatialGridCollisionProvider {
|
|
4
|
+
_cellSize;
|
|
5
|
+
_gridW;
|
|
6
|
+
_gridH;
|
|
7
|
+
_cells;
|
|
8
|
+
_shapes = new Map();
|
|
9
|
+
_queryId = 0;
|
|
10
|
+
_lastQueried = new Map();
|
|
11
|
+
constructor(width, height, cellSize = 32) {
|
|
12
|
+
this._cellSize = cellSize;
|
|
13
|
+
this._gridW = Math.ceil(width / cellSize);
|
|
14
|
+
this._gridH = Math.ceil(height / cellSize);
|
|
15
|
+
this._cells = new Map();
|
|
16
|
+
}
|
|
17
|
+
addShape(id, shape, posX, posY, rotation, scale) {
|
|
18
|
+
const stored = this._createStoredShape(id, shape, posX, posY, rotation, scale);
|
|
19
|
+
this._shapes.set(id, stored);
|
|
20
|
+
for (let cy = stored.minCellY; cy <= stored.maxCellY; cy++) {
|
|
21
|
+
for (let cx = stored.minCellX; cx <= stored.maxCellX; cx++) {
|
|
22
|
+
const key = cy * this._gridW + cx;
|
|
23
|
+
let cell = this._cells.get(key);
|
|
24
|
+
if (!cell) {
|
|
25
|
+
cell = [];
|
|
26
|
+
this._cells.set(key, cell);
|
|
27
|
+
}
|
|
28
|
+
cell.push(id);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
testShape(shape, posX, posY, rotation, scale) {
|
|
33
|
+
this._queryId++;
|
|
34
|
+
const queryId = this._queryId;
|
|
35
|
+
const test = this._createStoredShape(-1, shape, posX, posY, rotation, scale);
|
|
36
|
+
for (let cy = test.minCellY; cy <= test.maxCellY; cy++) {
|
|
37
|
+
for (let cx = test.minCellX; cx <= test.maxCellX; cx++) {
|
|
38
|
+
const key = cy * this._gridW + cx;
|
|
39
|
+
const cell = this._cells.get(key);
|
|
40
|
+
if (!cell)
|
|
41
|
+
continue;
|
|
42
|
+
for (const existingId of cell) {
|
|
43
|
+
if (this._lastQueried.get(existingId) === queryId)
|
|
44
|
+
continue;
|
|
45
|
+
this._lastQueried.set(existingId, queryId);
|
|
46
|
+
const existing = this._shapes.get(existingId);
|
|
47
|
+
if (!existing)
|
|
48
|
+
continue;
|
|
49
|
+
if (this._testOverlap(test, existing)) {
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
removeShape(id) {
|
|
58
|
+
const stored = this._shapes.get(id);
|
|
59
|
+
if (!stored)
|
|
60
|
+
return;
|
|
61
|
+
for (let cy = stored.minCellY; cy <= stored.maxCellY; cy++) {
|
|
62
|
+
for (let cx = stored.minCellX; cx <= stored.maxCellX; cx++) {
|
|
63
|
+
const key = cy * this._gridW + cx;
|
|
64
|
+
const cell = this._cells.get(key);
|
|
65
|
+
if (!cell)
|
|
66
|
+
continue;
|
|
67
|
+
const idx = cell.indexOf(id);
|
|
68
|
+
if (idx !== -1) {
|
|
69
|
+
cell.splice(idx, 1);
|
|
70
|
+
}
|
|
71
|
+
if (cell.length === 0) {
|
|
72
|
+
this._cells.delete(key);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
this._shapes.delete(id);
|
|
77
|
+
this._lastQueried.delete(id);
|
|
78
|
+
}
|
|
79
|
+
clear() {
|
|
80
|
+
this._cells.clear();
|
|
81
|
+
this._shapes.clear();
|
|
82
|
+
this._lastQueried.clear();
|
|
83
|
+
this._queryId = 0;
|
|
84
|
+
}
|
|
85
|
+
_createStoredShape(id, shape, posX, posY, rotation, scale) {
|
|
86
|
+
let halfExtX;
|
|
87
|
+
let halfExtY;
|
|
88
|
+
if (shape.type === ShapeType.Circle) {
|
|
89
|
+
const r = shape.radius * scale;
|
|
90
|
+
halfExtX = r;
|
|
91
|
+
halfExtY = r;
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
halfExtX = shape.halfWidth * scale;
|
|
95
|
+
halfExtY = shape.halfHeight * scale;
|
|
96
|
+
}
|
|
97
|
+
const minCellX = Math.max(0, Math.floor((posX - halfExtX) / this._cellSize));
|
|
98
|
+
const minCellY = Math.max(0, Math.floor((posY - halfExtY) / this._cellSize));
|
|
99
|
+
const maxCellX = Math.min(this._gridW - 1, Math.floor((posX + halfExtX) / this._cellSize));
|
|
100
|
+
const maxCellY = Math.min(this._gridH - 1, Math.floor((posY + halfExtY) / this._cellSize));
|
|
101
|
+
return {
|
|
102
|
+
id, shape, posX, posY, rotation, scale,
|
|
103
|
+
minCellX, minCellY, maxCellX, maxCellY,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
_testOverlap(a, b) {
|
|
107
|
+
const aShape = a.shape;
|
|
108
|
+
const bShape = b.shape;
|
|
109
|
+
if (aShape.type === ShapeType.Circle && bShape.type === ShapeType.Circle) {
|
|
110
|
+
return testCircleCircle(a.posX, a.posY, aShape.radius * a.scale, b.posX, b.posY, bShape.radius * b.scale);
|
|
111
|
+
}
|
|
112
|
+
if (aShape.type === ShapeType.Cuboid && bShape.type === ShapeType.Cuboid) {
|
|
113
|
+
const aHW = aShape.halfWidth * a.scale;
|
|
114
|
+
const aHH = aShape.halfHeight * a.scale;
|
|
115
|
+
const bHW = bShape.halfWidth * b.scale;
|
|
116
|
+
const bHH = bShape.halfHeight * b.scale;
|
|
117
|
+
return testAabbAabb(a.posX - aHW, a.posY - aHH, a.posX + aHW, a.posY + aHH, b.posX - bHW, b.posY - bHH, b.posX + bHW, b.posY + bHH);
|
|
118
|
+
}
|
|
119
|
+
// Circle vs Cuboid
|
|
120
|
+
const [circ, box] = aShape.type === ShapeType.Circle ? [a, b] : [b, a];
|
|
121
|
+
const circShape = circ.shape;
|
|
122
|
+
const boxShape = box.shape;
|
|
123
|
+
const bHW = boxShape.halfWidth * box.scale;
|
|
124
|
+
const bHH = boxShape.halfHeight * box.scale;
|
|
125
|
+
return testCircleAabb(circ.posX, circ.posY, circShape.radius * circ.scale, box.posX - bHW, box.posY - bHH, box.posX + bHW, box.posY + bHH);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { IGeneratedMap } from '../types/generated-map.js';
|
|
2
|
+
import { FeatureId } from '../types/feature.js';
|
|
3
|
+
export declare class GeneratedMap implements IGeneratedMap {
|
|
4
|
+
readonly width: number;
|
|
5
|
+
readonly height: number;
|
|
6
|
+
readonly gridSize: number;
|
|
7
|
+
private readonly _features;
|
|
8
|
+
constructor(width: number, height: number, gridSize: number);
|
|
9
|
+
setFeatureOutput(featureId: FeatureId, output: unknown): void;
|
|
10
|
+
get<T>(feature: {
|
|
11
|
+
readonly id: FeatureId;
|
|
12
|
+
}): T | undefined;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=generated-map.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generated-map.d.ts","sourceRoot":"","sources":["../../../src/lib/core/generated-map.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAEhD,qBAAa,YAAa,YAAW,aAAa;IAI9C,QAAQ,CAAC,KAAK,EAAE,MAAM;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM;IAL3B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAiC;gBAGhD,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM;IAG3B,gBAAgB,CAAC,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,GAAG,IAAI;IAI7D,GAAG,CAAC,CAAC,EAAE,OAAO,EAAE;QAAE,QAAQ,CAAC,EAAE,EAAE,SAAS,CAAA;KAAE,GAAG,CAAC,GAAG,SAAS;CAG3D"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export class GeneratedMap {
|
|
2
|
+
width;
|
|
3
|
+
height;
|
|
4
|
+
gridSize;
|
|
5
|
+
_features = new Map();
|
|
6
|
+
constructor(width, height, gridSize) {
|
|
7
|
+
this.width = width;
|
|
8
|
+
this.height = height;
|
|
9
|
+
this.gridSize = gridSize;
|
|
10
|
+
}
|
|
11
|
+
setFeatureOutput(featureId, output) {
|
|
12
|
+
this._features.set(featureId, output);
|
|
13
|
+
}
|
|
14
|
+
get(feature) {
|
|
15
|
+
return this._features.get(feature.id);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { MapGeneratorConfig } from '../types/map-generator-config.js';
|
|
2
|
+
export interface MapDimensions {
|
|
3
|
+
readonly width: number;
|
|
4
|
+
readonly height: number;
|
|
5
|
+
readonly gridSize: number;
|
|
6
|
+
}
|
|
7
|
+
export declare function computeDimensions(config: MapGeneratorConfig): MapDimensions;
|
|
8
|
+
//# sourceMappingURL=map-dimensions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"map-dimensions.d.ts","sourceRoot":"","sources":["../../../src/lib/core/map-dimensions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AAE3E,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,kBAAkB,GAAG,aAAa,CAM3E"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { IMapFeature } from '../types/feature.js';
|
|
2
|
+
import type { ISeededRandom } from '../types/prng-interface.js';
|
|
3
|
+
import type { ICollisionProvider } from '../types/collision-provider.js';
|
|
4
|
+
import type { MapGeneratorConfig } from '../types/map-generator-config.js';
|
|
5
|
+
import { GeneratedMap } from './generated-map.js';
|
|
6
|
+
export declare class MapGenerator {
|
|
7
|
+
private readonly _config;
|
|
8
|
+
private readonly _entries;
|
|
9
|
+
constructor(config: MapGeneratorConfig);
|
|
10
|
+
addFeature<TConfig, TOutput>(feature: IMapFeature<TConfig, TOutput>, config: TConfig): this;
|
|
11
|
+
generate(random: ISeededRandom, collision?: ICollisionProvider): GeneratedMap;
|
|
12
|
+
private _topologicalSort;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=map-generator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"map-generator.d.ts","sourceRoot":"","sources":["../../../src/lib/core/map-generator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAqB,MAAM,qBAAqB,CAAC;AAE1E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACzE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AAE3E,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AA8ClD,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAC7C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAsB;gBAEnC,MAAM,EAAE,kBAAkB;IAItC,UAAU,CAAC,OAAO,EAAE,OAAO,EACzB,OAAO,EAAE,WAAW,CAAC,OAAO,EAAE,OAAO,CAAC,EACtC,MAAM,EAAE,OAAO,GACd,IAAI;IAKP,QAAQ,CAAC,MAAM,EAAE,aAAa,EAAE,SAAS,CAAC,EAAE,kBAAkB,GAAG,YAAY;IAuC7E,OAAO,CAAC,gBAAgB;CAqDzB"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { FeatureId } from '../types/feature.js';
|
|
2
|
+
import { GeneratedMap } from './generated-map.js';
|
|
3
|
+
import { computeDimensions } from './map-dimensions.js';
|
|
4
|
+
class GenerationContextImpl {
|
|
5
|
+
width;
|
|
6
|
+
height;
|
|
7
|
+
center;
|
|
8
|
+
random;
|
|
9
|
+
collision;
|
|
10
|
+
_outputs = new Map();
|
|
11
|
+
constructor(width, height, random, collision) {
|
|
12
|
+
this.width = width;
|
|
13
|
+
this.height = height;
|
|
14
|
+
this.center = { x: width / 2, y: height / 2 };
|
|
15
|
+
this.random = random;
|
|
16
|
+
this.collision = collision;
|
|
17
|
+
}
|
|
18
|
+
get(feature) {
|
|
19
|
+
if (!this._outputs.has(feature.id)) {
|
|
20
|
+
throw new Error(`Feature output "${FeatureId[feature.id]}" is not available. It may not have been run yet.`);
|
|
21
|
+
}
|
|
22
|
+
return this._outputs.get(feature.id);
|
|
23
|
+
}
|
|
24
|
+
hasFeature(featureId) {
|
|
25
|
+
return this._outputs.has(featureId);
|
|
26
|
+
}
|
|
27
|
+
setOutput(featureId, output) {
|
|
28
|
+
this._outputs.set(featureId, output);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export class MapGenerator {
|
|
32
|
+
_config;
|
|
33
|
+
_entries = [];
|
|
34
|
+
constructor(config) {
|
|
35
|
+
this._config = config;
|
|
36
|
+
}
|
|
37
|
+
addFeature(feature, config) {
|
|
38
|
+
this._entries.push({ feature, config });
|
|
39
|
+
return this;
|
|
40
|
+
}
|
|
41
|
+
generate(random, collision) {
|
|
42
|
+
const featureMap = new Map();
|
|
43
|
+
for (const entry of this._entries) {
|
|
44
|
+
featureMap.set(entry.feature.id, entry);
|
|
45
|
+
}
|
|
46
|
+
// Validate dependencies
|
|
47
|
+
for (const entry of this._entries) {
|
|
48
|
+
for (const req of entry.feature.requires) {
|
|
49
|
+
if (!featureMap.has(req)) {
|
|
50
|
+
throw new Error(`Feature "${FeatureId[entry.feature.id]}" requires "${FeatureId[req]}" which was not included.`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// Topological sort (Kahn's algorithm)
|
|
55
|
+
const sorted = this._topologicalSort(featureMap);
|
|
56
|
+
// Compute dimensions
|
|
57
|
+
const dims = computeDimensions(this._config);
|
|
58
|
+
// Create collision provider (placeholder noop if none provided)
|
|
59
|
+
const coll = collision ?? createNoopCollision();
|
|
60
|
+
// Create context and run features
|
|
61
|
+
const ctx = new GenerationContextImpl(dims.width, dims.height, random, coll);
|
|
62
|
+
const result = new GeneratedMap(dims.width, dims.height, dims.gridSize);
|
|
63
|
+
for (const entry of sorted) {
|
|
64
|
+
const output = entry.feature.generate(ctx, entry.config);
|
|
65
|
+
ctx.setOutput(entry.feature.id, output);
|
|
66
|
+
result.setFeatureOutput(entry.feature.id, output);
|
|
67
|
+
}
|
|
68
|
+
return result;
|
|
69
|
+
}
|
|
70
|
+
_topologicalSort(featureMap) {
|
|
71
|
+
const inDegree = new Map();
|
|
72
|
+
const adjacency = new Map();
|
|
73
|
+
for (const [id] of featureMap) {
|
|
74
|
+
inDegree.set(id, 0);
|
|
75
|
+
adjacency.set(id, []);
|
|
76
|
+
}
|
|
77
|
+
for (const [id, entry] of featureMap) {
|
|
78
|
+
for (const req of entry.feature.requires) {
|
|
79
|
+
const adj = adjacency.get(req);
|
|
80
|
+
const deg = inDegree.get(id);
|
|
81
|
+
if (adj && deg !== undefined) {
|
|
82
|
+
adj.push(id);
|
|
83
|
+
inDegree.set(id, deg + 1);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
const queue = [];
|
|
88
|
+
for (const [id, degree] of inDegree) {
|
|
89
|
+
if (degree === 0) {
|
|
90
|
+
queue.push(id);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const sorted = [];
|
|
94
|
+
while (queue.length > 0) {
|
|
95
|
+
const id = queue.shift();
|
|
96
|
+
const entry = featureMap.get(id);
|
|
97
|
+
if (entry) {
|
|
98
|
+
sorted.push(entry);
|
|
99
|
+
}
|
|
100
|
+
const neighbors = adjacency.get(id);
|
|
101
|
+
if (neighbors) {
|
|
102
|
+
for (const neighbor of neighbors) {
|
|
103
|
+
const newDegree = (inDegree.get(neighbor) ?? 1) - 1;
|
|
104
|
+
inDegree.set(neighbor, newDegree);
|
|
105
|
+
if (newDegree === 0) {
|
|
106
|
+
queue.push(neighbor);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (sorted.length !== featureMap.size) {
|
|
112
|
+
throw new Error('Circular dependency detected among features.');
|
|
113
|
+
}
|
|
114
|
+
return sorted;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
function createNoopCollision() {
|
|
118
|
+
return {
|
|
119
|
+
addShape() { return; },
|
|
120
|
+
testShape() { return false; },
|
|
121
|
+
removeShape() { return; },
|
|
122
|
+
clear() { return; },
|
|
123
|
+
};
|
|
124
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { TerrainZone } from '../types/placed-object.js';
|
|
2
|
+
import type { ShoreOutput, GrassOutput, RiverOutput, LakeOutput } from '../types/feature-configs.js';
|
|
3
|
+
export interface TerrainQueryInputs {
|
|
4
|
+
shore?: ShoreOutput;
|
|
5
|
+
grass?: GrassOutput;
|
|
6
|
+
river?: RiverOutput;
|
|
7
|
+
lake?: LakeOutput;
|
|
8
|
+
}
|
|
9
|
+
export declare class TerrainQuery {
|
|
10
|
+
private readonly _shore;
|
|
11
|
+
private readonly _grass;
|
|
12
|
+
private readonly _rivers;
|
|
13
|
+
private readonly _lakes;
|
|
14
|
+
constructor(inputs: TerrainQueryInputs);
|
|
15
|
+
classify(x: number, y: number): TerrainZone;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=terrain-query.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terrain-query.d.ts","sourceRoot":"","sources":["../../../src/lib/core/terrain-query.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACxD,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAKrG,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,IAAI,CAAC,EAAE,UAAU,CAAC;CACnB;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA0B;IACjD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA0B;IACjD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA4B;IACpD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA4B;gBAEvC,MAAM,EAAE,kBAAkB;IAOtC,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,WAAW;CA0C5C"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { TerrainZone } from '../types/placed-object.js';
|
|
2
|
+
import { pointInPolygon } from '../math/polygon-utils.js';
|
|
3
|
+
export class TerrainQuery {
|
|
4
|
+
_shore;
|
|
5
|
+
_grass;
|
|
6
|
+
_rivers;
|
|
7
|
+
_lakes;
|
|
8
|
+
constructor(inputs) {
|
|
9
|
+
this._shore = inputs.shore;
|
|
10
|
+
this._grass = inputs.grass;
|
|
11
|
+
this._rivers = inputs.river?.rivers ?? [];
|
|
12
|
+
this._lakes = inputs.lake?.lakes ?? [];
|
|
13
|
+
}
|
|
14
|
+
classify(x, y) {
|
|
15
|
+
// Check lakes first (highest priority water feature)
|
|
16
|
+
for (const lake of this._lakes) {
|
|
17
|
+
if (isInsideAabb(x, y, lake.aabb) && pointInPolygon({ x, y }, lake.waterPoly)) {
|
|
18
|
+
return TerrainZone.Lake;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
// Check rivers
|
|
22
|
+
for (const river of this._rivers) {
|
|
23
|
+
if (!isInsideAabb(x, y, river.aabb))
|
|
24
|
+
continue;
|
|
25
|
+
if (pointInPolygon({ x, y }, river.waterPoly)) {
|
|
26
|
+
return TerrainZone.River;
|
|
27
|
+
}
|
|
28
|
+
if (pointInPolygon({ x, y }, river.shorePoly)) {
|
|
29
|
+
return TerrainZone.RiverShore;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
// Check grass
|
|
33
|
+
if (this._grass) {
|
|
34
|
+
if (isInsideAabb(x, y, this._grass.bounds) && pointInPolygon({ x, y }, this._grass.polygon)) {
|
|
35
|
+
return TerrainZone.Grass;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// Check shore (beach = inside shore but outside grass)
|
|
39
|
+
if (this._shore) {
|
|
40
|
+
if (isInsideAabb(x, y, this._shore.bounds) && pointInPolygon({ x, y }, this._shore.polygon)) {
|
|
41
|
+
return TerrainZone.Beach;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
// No shore feature → default to Grass
|
|
45
|
+
if (!this._shore) {
|
|
46
|
+
return TerrainZone.Grass;
|
|
47
|
+
}
|
|
48
|
+
// Outside shore → ocean
|
|
49
|
+
return TerrainZone.WaterEdge;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function isInsideAabb(x, y, aabb) {
|
|
53
|
+
return x >= aabb.min.x && x <= aabb.max.x && y >= aabb.min.y && y <= aabb.max.y;
|
|
54
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { IMapFeature, GenerationContext } from '../types/feature.js';
|
|
2
|
+
import { FeatureId } from '../types/feature.js';
|
|
3
|
+
import type { BiomeConfig, BiomeOutput } from '../types/feature-configs.js';
|
|
4
|
+
export declare class BiomeFeature implements IMapFeature<BiomeConfig, BiomeOutput> {
|
|
5
|
+
static readonly id = FeatureId.Biome;
|
|
6
|
+
readonly id = FeatureId.Biome;
|
|
7
|
+
readonly requires: readonly FeatureId[];
|
|
8
|
+
generate(_ctx: GenerationContext, config: BiomeConfig): BiomeOutput;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=biome-feature.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"biome-feature.d.ts","sourceRoot":"","sources":["../../../src/lib/features/biome-feature.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAC1E,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAE5E,qBAAa,YAAa,YAAW,WAAW,CAAC,WAAW,EAAE,WAAW,CAAC;IACxE,MAAM,CAAC,QAAQ,CAAC,EAAE,mBAAmB;IACrC,QAAQ,CAAC,EAAE,mBAAmB;IAC9B,QAAQ,CAAC,QAAQ,EAAE,SAAS,SAAS,EAAE,CAAM;IAE7C,QAAQ,CAAC,IAAI,EAAE,iBAAiB,EAAE,MAAM,EAAE,WAAW,GAAG,WAAW;CAGpE"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { IMapFeature, GenerationContext } from '../types/feature.js';
|
|
2
|
+
import { FeatureId } from '../types/feature.js';
|
|
3
|
+
import type { BridgeConfig, BridgeOutput } from '../types/feature-configs.js';
|
|
4
|
+
export declare class BridgeFeature implements IMapFeature<BridgeConfig, BridgeOutput> {
|
|
5
|
+
static readonly id = FeatureId.Bridge;
|
|
6
|
+
readonly id = FeatureId.Bridge;
|
|
7
|
+
readonly requires: readonly FeatureId[];
|
|
8
|
+
generate(ctx: GenerationContext, config: BridgeConfig): BridgeOutput;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=bridge-feature.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge-feature.d.ts","sourceRoot":"","sources":["../../../src/lib/features/bridge-feature.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAC1E,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAe,MAAM,6BAA6B,CAAC;AAO3F,qBAAa,aAAc,YAAW,WAAW,CAAC,YAAY,EAAE,YAAY,CAAC;IAC3E,MAAM,CAAC,QAAQ,CAAC,EAAE,oBAAoB;IACtC,QAAQ,CAAC,EAAE,oBAAoB;IAC/B,QAAQ,CAAC,QAAQ,EAAE,SAAS,SAAS,EAAE,CAAqB;IAE5D,QAAQ,CAAC,GAAG,EAAE,iBAAiB,EAAE,MAAM,EAAE,YAAY,GAAG,YAAY;CAsBrE"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { MathOps } from '@lagless/math';
|
|
2
|
+
import { FeatureId } from '../types/feature.js';
|
|
3
|
+
import { RiverFeature } from './river-feature.js';
|
|
4
|
+
import { TerrainZone } from '../types/placed-object.js';
|
|
5
|
+
import { Spline } from '../math/spline.js';
|
|
6
|
+
export class BridgeFeature {
|
|
7
|
+
static id = FeatureId.Bridge;
|
|
8
|
+
id = FeatureId.Bridge;
|
|
9
|
+
requires = [FeatureId.River];
|
|
10
|
+
generate(ctx, config) {
|
|
11
|
+
const riverOutput = ctx.get(RiverFeature);
|
|
12
|
+
const bridges = [];
|
|
13
|
+
const counts = { medium: 0, large: 0, xlarge: 0 };
|
|
14
|
+
for (const river of riverOutput.normalRivers) {
|
|
15
|
+
if (river.looped)
|
|
16
|
+
continue;
|
|
17
|
+
const size = getBridgeSize(river.waterWidth);
|
|
18
|
+
if (counts[size] >= config.maxPerSize[size])
|
|
19
|
+
continue;
|
|
20
|
+
const bridgeTypeId = config.bridgeTypes[size];
|
|
21
|
+
const placed = placeBridge(ctx, river, bridgeTypeId);
|
|
22
|
+
if (placed) {
|
|
23
|
+
bridges.push(placed);
|
|
24
|
+
counts[size]++;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return { bridges };
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function getBridgeSize(waterWidth) {
|
|
31
|
+
if (waterWidth >= 20)
|
|
32
|
+
return 'xlarge';
|
|
33
|
+
if (waterWidth >= 9)
|
|
34
|
+
return 'large';
|
|
35
|
+
return 'medium';
|
|
36
|
+
}
|
|
37
|
+
function placeBridge(ctx, river, bridgeTypeId) {
|
|
38
|
+
const spline = new Spline(river.splinePoints, false);
|
|
39
|
+
const t = 0.3 + ctx.random.getFloat() * 0.4;
|
|
40
|
+
const pos = spline.getPos(t);
|
|
41
|
+
const tangent = spline.getTangent(t);
|
|
42
|
+
const tangentLen = MathOps.sqrt(tangent.x * tangent.x + tangent.y * tangent.y);
|
|
43
|
+
if (tangentLen === 0)
|
|
44
|
+
return null;
|
|
45
|
+
const rotation = MathOps.atan2(tangent.y, tangent.x);
|
|
46
|
+
return {
|
|
47
|
+
typeId: bridgeTypeId,
|
|
48
|
+
posX: pos.x,
|
|
49
|
+
posY: pos.y,
|
|
50
|
+
rotation,
|
|
51
|
+
scale: 1,
|
|
52
|
+
terrainZone: TerrainZone.Bridge,
|
|
53
|
+
children: [],
|
|
54
|
+
};
|
|
55
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { IMapFeature, GenerationContext } from '../types/feature.js';
|
|
2
|
+
import { FeatureId } from '../types/feature.js';
|
|
3
|
+
import type { GrassConfig, GrassOutput } from '../types/feature-configs.js';
|
|
4
|
+
export declare class GrassFeature implements IMapFeature<GrassConfig, GrassOutput> {
|
|
5
|
+
static readonly id = FeatureId.Grass;
|
|
6
|
+
readonly id = FeatureId.Grass;
|
|
7
|
+
readonly requires: readonly FeatureId[];
|
|
8
|
+
generate(ctx: GenerationContext, config: GrassConfig): GrassOutput;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=grass-feature.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grass-feature.d.ts","sourceRoot":"","sources":["../../../src/lib/features/grass-feature.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAC1E,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAe,MAAM,6BAA6B,CAAC;AAKzF,qBAAa,YAAa,YAAW,WAAW,CAAC,WAAW,EAAE,WAAW,CAAC;IACxE,MAAM,CAAC,QAAQ,CAAC,EAAE,mBAAmB;IACrC,QAAQ,CAAC,EAAE,mBAAmB;IAC9B,QAAQ,CAAC,QAAQ,EAAE,SAAS,SAAS,EAAE,CAAqB;IAE5D,QAAQ,CAAC,GAAG,EAAE,iBAAiB,EAAE,MAAM,EAAE,WAAW,GAAG,WAAW;CAgCnE"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { MathOps } from '@lagless/math';
|
|
2
|
+
import { FeatureId } from '../types/feature.js';
|
|
3
|
+
import { ShoreFeature } from './shore-feature.js';
|
|
4
|
+
import { polygonArea, computePolygonBounds } from '../math/polygon-utils.js';
|
|
5
|
+
export class GrassFeature {
|
|
6
|
+
static id = FeatureId.Grass;
|
|
7
|
+
id = FeatureId.Grass;
|
|
8
|
+
requires = [FeatureId.Shore];
|
|
9
|
+
generate(ctx, config) {
|
|
10
|
+
const shore = ctx.get(ShoreFeature);
|
|
11
|
+
const centerX = ctx.center.x;
|
|
12
|
+
const centerY = ctx.center.y;
|
|
13
|
+
const points = shore.polygon.points.map((pos) => {
|
|
14
|
+
const dx = centerX - pos.x;
|
|
15
|
+
const dy = centerY - pos.y;
|
|
16
|
+
const len = MathOps.sqrt(dx * dx + dy * dy);
|
|
17
|
+
if (len === 0)
|
|
18
|
+
return { x: pos.x, y: pos.y };
|
|
19
|
+
const nx = dx / len;
|
|
20
|
+
const ny = dy / len;
|
|
21
|
+
const variation = (ctx.random.getFloat() * 2 - 1) * config.variation;
|
|
22
|
+
const inset = config.inset + variation;
|
|
23
|
+
return {
|
|
24
|
+
x: pos.x + nx * inset,
|
|
25
|
+
y: pos.y + ny * inset,
|
|
26
|
+
};
|
|
27
|
+
});
|
|
28
|
+
const bounds = computePolygonBounds(points);
|
|
29
|
+
const area = polygonArea(points);
|
|
30
|
+
return {
|
|
31
|
+
polygon: { points, count: points.length },
|
|
32
|
+
bounds,
|
|
33
|
+
area,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { IMapFeature, GenerationContext } from '../types/feature.js';
|
|
2
|
+
import { FeatureId } from '../types/feature.js';
|
|
3
|
+
import type { GroundPatchConfig, GroundPatchOutput } from '../types/feature-configs.js';
|
|
4
|
+
export declare class GroundPatchFeature implements IMapFeature<GroundPatchConfig, GroundPatchOutput> {
|
|
5
|
+
static readonly id = FeatureId.GroundPatch;
|
|
6
|
+
readonly id = FeatureId.GroundPatch;
|
|
7
|
+
readonly requires: readonly FeatureId[];
|
|
8
|
+
generate(ctx: GenerationContext, config: GroundPatchConfig): GroundPatchOutput;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=ground-patch-feature.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ground-patch-feature.d.ts","sourceRoot":"","sources":["../../../src/lib/features/ground-patch-feature.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAC1E,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,KAAK,EAAE,iBAAiB,EAAE,iBAAiB,EAAyB,MAAM,6BAA6B,CAAC;AAI/G,qBAAa,kBAAmB,YAAW,WAAW,CAAC,iBAAiB,EAAE,iBAAiB,CAAC;IAC1F,MAAM,CAAC,QAAQ,CAAC,EAAE,yBAAyB;IAC3C,QAAQ,CAAC,EAAE,yBAAyB;IACpC,QAAQ,CAAC,QAAQ,EAAE,SAAS,SAAS,EAAE,CAA+B;IAEtE,QAAQ,CAAC,GAAG,EAAE,iBAAiB,EAAE,MAAM,EAAE,iBAAiB,GAAG,iBAAiB;CAqD/E"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { MathOps } from '@lagless/math';
|
|
2
|
+
import { FeatureId } from '../types/feature.js';
|
|
3
|
+
import { ObjectPlacementFeature } from './object-placement-feature.js';
|
|
4
|
+
export class GroundPatchFeature {
|
|
5
|
+
static id = FeatureId.GroundPatch;
|
|
6
|
+
id = FeatureId.GroundPatch;
|
|
7
|
+
requires = [FeatureId.ObjectPlacement];
|
|
8
|
+
generate(ctx, config) {
|
|
9
|
+
const placement = ctx.get(ObjectPlacementFeature);
|
|
10
|
+
const patches = [];
|
|
11
|
+
if (config.registry) {
|
|
12
|
+
for (const obj of placement.objects) {
|
|
13
|
+
const def = config.registry.get(obj.typeId);
|
|
14
|
+
if (!def?.groundPatches)
|
|
15
|
+
continue;
|
|
16
|
+
for (const gpDef of def.groundPatches) {
|
|
17
|
+
const scale = obj.scale;
|
|
18
|
+
const rawOx = gpDef.offset.x * scale;
|
|
19
|
+
const rawOy = gpDef.offset.y * scale;
|
|
20
|
+
const cos = MathOps.cos(obj.rotation);
|
|
21
|
+
const sin = MathOps.sin(obj.rotation);
|
|
22
|
+
const ox = rawOx * cos - rawOy * sin;
|
|
23
|
+
const oy = rawOx * sin + rawOy * cos;
|
|
24
|
+
const hx = gpDef.halfExtents.x * scale;
|
|
25
|
+
const hy = gpDef.halfExtents.y * scale;
|
|
26
|
+
patches.push({
|
|
27
|
+
minX: obj.posX + ox - hx,
|
|
28
|
+
minY: obj.posY + oy - hy,
|
|
29
|
+
maxX: obj.posX + ox + hx,
|
|
30
|
+
maxY: obj.posY + oy + hy,
|
|
31
|
+
color: gpDef.color,
|
|
32
|
+
roughness: gpDef.roughness,
|
|
33
|
+
offsetDist: gpDef.offsetDist,
|
|
34
|
+
order: gpDef.order,
|
|
35
|
+
useAsMapShape: gpDef.useAsMapShape,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if (config.extraPatches) {
|
|
41
|
+
for (const gpDef of config.extraPatches) {
|
|
42
|
+
patches.push({
|
|
43
|
+
minX: ctx.center.x + gpDef.offset.x - gpDef.halfExtents.x,
|
|
44
|
+
minY: ctx.center.y + gpDef.offset.y - gpDef.halfExtents.y,
|
|
45
|
+
maxX: ctx.center.x + gpDef.offset.x + gpDef.halfExtents.x,
|
|
46
|
+
maxY: ctx.center.y + gpDef.offset.y + gpDef.halfExtents.y,
|
|
47
|
+
color: gpDef.color,
|
|
48
|
+
roughness: gpDef.roughness,
|
|
49
|
+
offsetDist: gpDef.offsetDist,
|
|
50
|
+
order: gpDef.order,
|
|
51
|
+
useAsMapShape: gpDef.useAsMapShape,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return { patches };
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { IMapFeature, GenerationContext } from '../types/feature.js';
|
|
2
|
+
import { FeatureId } from '../types/feature.js';
|
|
3
|
+
import type { LakeConfig, LakeOutput } from '../types/feature-configs.js';
|
|
4
|
+
export declare class LakeFeature implements IMapFeature<LakeConfig, LakeOutput> {
|
|
5
|
+
static readonly id = FeatureId.Lake;
|
|
6
|
+
readonly id = FeatureId.Lake;
|
|
7
|
+
readonly requires: readonly FeatureId[];
|
|
8
|
+
generate(ctx: GenerationContext, config: LakeConfig): LakeOutput;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=lake-feature.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lake-feature.d.ts","sourceRoot":"","sources":["../../../src/lib/features/lake-feature.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAC1E,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAO1E,qBAAa,WAAY,YAAW,WAAW,CAAC,UAAU,EAAE,UAAU,CAAC;IACrE,MAAM,CAAC,QAAQ,CAAC,EAAE,kBAAkB;IACpC,QAAQ,CAAC,EAAE,kBAAkB;IAC7B,QAAQ,CAAC,QAAQ,EAAE,SAAS,SAAS,EAAE,CAAM;IAE7C,QAAQ,CAAC,GAAG,EAAE,iBAAiB,EAAE,MAAM,EAAE,UAAU,GAAG,UAAU;CAmEjE"}
|