@waica/cli 0.7.0 → 0.9.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/README.md +1 -1
- package/dist/editor/assets/{cssMode-eS4pAOyh.js → cssMode-DM5x3eXD.js} +1 -1
- package/dist/editor/assets/{freemarker2-FMQjZKQk.js → freemarker2-CmlzUOOI.js} +1 -1
- package/dist/editor/assets/{handlebars-dDYOnqat.js → handlebars-Dlz-T3Pu.js} +1 -1
- package/dist/editor/assets/{html-Ba6xZh4T.js → html-B-NfgvUd.js} +1 -1
- package/dist/editor/assets/{htmlMode-DC9qJr2p.js → htmlMode-K4hF3BAu.js} +1 -1
- package/dist/editor/assets/{index-CoyeInOT.js → index-Ca2oPxGt.js} +4626 -1104
- package/dist/editor/assets/{index-Cy5pmBhN.css → index-CzarIYLW.css} +1 -1
- package/dist/editor/assets/{javascript-DOwFvmvM.js → javascript-COj32tw-.js} +1 -1
- package/dist/editor/assets/{jsonMode-CcG7zlSf.js → jsonMode-DyL12Tvg.js} +1 -1
- package/dist/editor/assets/{liquid-Dfp71r7J.js → liquid-CGaRZ9Iu.js} +1 -1
- package/dist/editor/assets/{mdx-Ci7vVeht.js → mdx-L4fRd4xO.js} +1 -1
- package/dist/editor/assets/{python-Bluv3Y9Q.js → python-Z6FCrHUG.js} +1 -1
- package/dist/editor/assets/{razor-BtwTXMaK.js → razor-6XSfF-a3.js} +1 -1
- package/dist/editor/assets/{tsMode-D3_D-H85.js → tsMode-C24rNZgD.js} +1 -1
- package/dist/editor/assets/{typescript-Ch_PS5f7.js → typescript-BcFln1pD.js} +1 -1
- package/dist/editor/assets/waica-iso-hero-BsEdW0V7.png +0 -0
- package/dist/editor/assets/waica-iso-orc-DRK-SaXm.png +0 -0
- package/dist/editor/assets/waica-iso-villager-szDkD_Ag.png +0 -0
- package/dist/editor/assets/{xml-CI_bH_MX.js → xml-DJCue8rm.js} +1 -1
- package/dist/editor/assets/{yaml-LD9GTZyy.js → yaml-DiJf3V99.js} +1 -1
- package/dist/editor/index.html +2 -2
- package/dist/mcp/known-archetypes.js +5 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/assets/ATTRIBUTION.md +93 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/assets/waica-iso-crate.png +0 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/assets/waica-iso-ground.png +0 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/assets/waica-iso-hero.png +0 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/assets/waica-iso-orc.png +0 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/assets/waica-iso-rock.png +0 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/assets/waica-iso-tree.png +0 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/assets/waica-iso-villager.png +0 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/dist/animation.d.ts +7 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/dist/animation.js +17 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/dist/art.d.ts +3 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/dist/art.js +10 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/dist/bundle.d.ts +2 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/dist/bundle.js +9 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/dist/controls.d.ts +3 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/dist/controls.js +16 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/dist/index.d.ts +24 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/dist/index.js +15 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/dist/manifest.d.ts +16 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/dist/manifest.js +30 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/dist/prefabs.d.ts +22 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/dist/prefabs.js +215 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/dist/registry-data.d.ts +3 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/dist/registry-data.js +47 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/dist/registry.d.ts +4 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/dist/registry.js +17 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/dist/scene-default.d.ts +5 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/dist/scene-default.js +59 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/dist/ui.d.ts +1 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/dist/ui.js +30 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/package.json +30 -0
- package/dist/mcp/node_modules/@waica/archetype-platformer/package.json +3 -3
- package/dist/mcp/node_modules/@waica/archetype-topdown/dist/animation.d.ts +2 -1
- package/dist/mcp/node_modules/@waica/archetype-topdown/dist/animation.js +6 -2
- package/dist/mcp/node_modules/@waica/archetype-topdown/dist/registry-data.js +2 -1
- package/dist/mcp/node_modules/@waica/archetype-topdown/dist/ui.js +2 -19
- package/dist/mcp/node_modules/@waica/archetype-topdown/package.json +3 -3
- package/dist/mcp/node_modules/@waica/behaviors/dist/facing.d.ts +18 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/facing.js +56 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/grid-motor.d.ts +64 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/grid-motor.js +77 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/grid-player-role.d.ts +20 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/grid-player-role.js +133 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/health.d.ts +29 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/health.js +79 -3
- package/dist/mcp/node_modules/@waica/behaviors/dist/index.d.ts +4 -1
- package/dist/mcp/node_modules/@waica/behaviors/dist/index.js +4 -1
- package/dist/mcp/node_modules/@waica/behaviors/dist/interactable.d.ts +3 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/interactable.js +25 -2
- package/dist/mcp/node_modules/@waica/behaviors/dist/iso-motor.d.ts +21 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/iso-motor.js +24 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/iso-player-states.d.ts +5 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/iso-player-states.js +9 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/melee-attack.d.ts +48 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/melee-attack.js +83 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/patrol.d.ts +11 -3
- package/dist/mcp/node_modules/@waica/behaviors/dist/patrol.js +49 -5
- package/dist/mcp/node_modules/@waica/behaviors/dist/topdown-motor.d.ts +7 -61
- package/dist/mcp/node_modules/@waica/behaviors/dist/topdown-motor.js +8 -79
- package/dist/mcp/node_modules/@waica/behaviors/dist/topdown-player-states.d.ts +4 -17
- package/dist/mcp/node_modules/@waica/behaviors/dist/topdown-player-states.js +10 -82
- package/dist/mcp/node_modules/@waica/behaviors/package.json +2 -2
- package/dist/mcp/node_modules/@waica/engine/dist/component.d.ts +2 -0
- package/dist/mcp/node_modules/@waica/engine/dist/components/animated-sprite.d.ts +19 -1
- package/dist/mcp/node_modules/@waica/engine/dist/components/animated-sprite.js +35 -7
- package/dist/mcp/node_modules/@waica/engine/dist/components/dynamic-body.js +2 -4
- package/dist/mcp/node_modules/@waica/engine/dist/components/sprite.d.ts +19 -0
- package/dist/mcp/node_modules/@waica/engine/dist/components/sprite.js +44 -8
- package/dist/mcp/node_modules/@waica/engine/dist/components/tilemap.d.ts +113 -0
- package/dist/mcp/node_modules/@waica/engine/dist/components/tilemap.js +316 -0
- package/dist/mcp/node_modules/@waica/engine/dist/entity.d.ts +3 -0
- package/dist/mcp/node_modules/@waica/engine/dist/entity.js +13 -1
- package/dist/mcp/node_modules/@waica/engine/dist/game.d.ts +4 -0
- package/dist/mcp/node_modules/@waica/engine/dist/game.js +41 -9
- package/dist/mcp/node_modules/@waica/engine/dist/index.d.ts +9 -0
- package/dist/mcp/node_modules/@waica/engine/dist/index.js +5 -0
- package/dist/mcp/node_modules/@waica/engine/dist/projection.d.ts +14 -0
- package/dist/mcp/node_modules/@waica/engine/dist/projection.js +30 -0
- package/dist/mcp/node_modules/@waica/engine/dist/scene-solids.d.ts +16 -0
- package/dist/mcp/node_modules/@waica/engine/dist/scene-solids.js +28 -0
- package/dist/mcp/node_modules/@waica/engine/dist/scene.d.ts +3 -1
- package/dist/mcp/node_modules/@waica/engine/dist/solid-axis.js +2 -4
- package/dist/mcp/node_modules/@waica/engine/dist/sprite-placement.d.ts +22 -0
- package/dist/mcp/node_modules/@waica/engine/dist/sprite-placement.js +14 -0
- package/dist/mcp/node_modules/@waica/engine/dist/tilemap-grid.d.ts +26 -0
- package/dist/mcp/node_modules/@waica/engine/dist/tilemap-grid.js +41 -0
- package/dist/mcp/node_modules/@waica/engine/package.json +1 -1
- package/dist/mcp/package-resolver.js +4 -0
- package/dist/mcp/project-component-runner.js +1 -0
- package/dist/mcp/template/src/stats.json +2 -1
- package/package.json +1 -1
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
import * as THREE from 'three';
|
|
2
|
+
import { sheetCell } from '../animation/sheet.js';
|
|
3
|
+
import { Component } from '../component.js';
|
|
4
|
+
import { projectIsometric } from '../projection.js';
|
|
5
|
+
import { SOLID_SOURCE_SYMBOL } from '../scene-solids.js';
|
|
6
|
+
import { cellAt as gridCellAt, cellBounds as gridCellBounds, cellIndex as gridCellIndex, } from '../tilemap-grid.js';
|
|
7
|
+
import { Solid } from './solid.js';
|
|
8
|
+
const loader = new THREE.TextureLoader();
|
|
9
|
+
/** One authorable cell map rendered as a single merged geometry. */
|
|
10
|
+
export class Tilemap extends Component {
|
|
11
|
+
static componentName = 'Tilemap';
|
|
12
|
+
static params = {
|
|
13
|
+
color: { label: 'color' },
|
|
14
|
+
cols: { label: 'tileset columns', min: 1, step: 1 },
|
|
15
|
+
rows: { label: 'tileset rows', min: 1, step: 1 },
|
|
16
|
+
mapWidth: { label: 'map width', min: 1, step: 1 },
|
|
17
|
+
mapHeight: { label: 'map height', min: 1, step: 1 },
|
|
18
|
+
cellSize: { label: 'cell size', min: 0.05, step: 0.25 },
|
|
19
|
+
layer: { label: 'layer', min: -5, max: 5, step: 1 },
|
|
20
|
+
};
|
|
21
|
+
static transient = ['mesh', 'loadedTexture', 'derivedSolids'];
|
|
22
|
+
[SOLID_SOURCE_SYMBOL] = true;
|
|
23
|
+
_texture = '';
|
|
24
|
+
get texture() {
|
|
25
|
+
return this._texture;
|
|
26
|
+
}
|
|
27
|
+
set texture(value) {
|
|
28
|
+
this._texture = value;
|
|
29
|
+
this.rebuildMaterial();
|
|
30
|
+
}
|
|
31
|
+
_color = 0xffffff;
|
|
32
|
+
get color() {
|
|
33
|
+
return this._color;
|
|
34
|
+
}
|
|
35
|
+
set color(value) {
|
|
36
|
+
this._color = value;
|
|
37
|
+
this.rebuildMaterial();
|
|
38
|
+
}
|
|
39
|
+
_cols = 1;
|
|
40
|
+
get cols() {
|
|
41
|
+
return this._cols;
|
|
42
|
+
}
|
|
43
|
+
set cols(value) {
|
|
44
|
+
this._cols = value;
|
|
45
|
+
this.rebuildGeometry();
|
|
46
|
+
}
|
|
47
|
+
_rows = 1;
|
|
48
|
+
get rows() {
|
|
49
|
+
return this._rows;
|
|
50
|
+
}
|
|
51
|
+
set rows(value) {
|
|
52
|
+
this._rows = value;
|
|
53
|
+
this.rebuildGeometry();
|
|
54
|
+
}
|
|
55
|
+
_gridOffsetX = 0;
|
|
56
|
+
get gridOffsetX() {
|
|
57
|
+
return this._gridOffsetX;
|
|
58
|
+
}
|
|
59
|
+
set gridOffsetX(value) {
|
|
60
|
+
this._gridOffsetX = value;
|
|
61
|
+
this.rebuildGeometry();
|
|
62
|
+
}
|
|
63
|
+
_gridOffsetY = 0;
|
|
64
|
+
get gridOffsetY() {
|
|
65
|
+
return this._gridOffsetY;
|
|
66
|
+
}
|
|
67
|
+
set gridOffsetY(value) {
|
|
68
|
+
this._gridOffsetY = value;
|
|
69
|
+
this.rebuildGeometry();
|
|
70
|
+
}
|
|
71
|
+
_spacingX = 0;
|
|
72
|
+
get spacingX() {
|
|
73
|
+
return this._spacingX;
|
|
74
|
+
}
|
|
75
|
+
set spacingX(value) {
|
|
76
|
+
this._spacingX = value;
|
|
77
|
+
this.rebuildGeometry();
|
|
78
|
+
}
|
|
79
|
+
_spacingY = 0;
|
|
80
|
+
get spacingY() {
|
|
81
|
+
return this._spacingY;
|
|
82
|
+
}
|
|
83
|
+
set spacingY(value) {
|
|
84
|
+
this._spacingY = value;
|
|
85
|
+
this.rebuildGeometry();
|
|
86
|
+
}
|
|
87
|
+
_cellWidth = 0;
|
|
88
|
+
get cellWidth() {
|
|
89
|
+
return this._cellWidth;
|
|
90
|
+
}
|
|
91
|
+
set cellWidth(value) {
|
|
92
|
+
this._cellWidth = value;
|
|
93
|
+
this.rebuildGeometry();
|
|
94
|
+
}
|
|
95
|
+
_cellHeight = 0;
|
|
96
|
+
get cellHeight() {
|
|
97
|
+
return this._cellHeight;
|
|
98
|
+
}
|
|
99
|
+
set cellHeight(value) {
|
|
100
|
+
this._cellHeight = value;
|
|
101
|
+
this.rebuildGeometry();
|
|
102
|
+
}
|
|
103
|
+
_pixelArt = true;
|
|
104
|
+
get pixelArt() {
|
|
105
|
+
return this._pixelArt;
|
|
106
|
+
}
|
|
107
|
+
set pixelArt(value) {
|
|
108
|
+
this._pixelArt = value;
|
|
109
|
+
this.rebuildMaterial();
|
|
110
|
+
}
|
|
111
|
+
_mapWidth = 1;
|
|
112
|
+
get mapWidth() {
|
|
113
|
+
return this._mapWidth;
|
|
114
|
+
}
|
|
115
|
+
set mapWidth(value) {
|
|
116
|
+
this._mapWidth = value;
|
|
117
|
+
this.rebuildMap();
|
|
118
|
+
}
|
|
119
|
+
_mapHeight = 1;
|
|
120
|
+
get mapHeight() {
|
|
121
|
+
return this._mapHeight;
|
|
122
|
+
}
|
|
123
|
+
set mapHeight(value) {
|
|
124
|
+
this._mapHeight = value;
|
|
125
|
+
this.rebuildMap();
|
|
126
|
+
}
|
|
127
|
+
_cellSize = 1;
|
|
128
|
+
get cellSize() {
|
|
129
|
+
return this._cellSize;
|
|
130
|
+
}
|
|
131
|
+
set cellSize(value) {
|
|
132
|
+
this._cellSize = value;
|
|
133
|
+
this.rebuildMap();
|
|
134
|
+
}
|
|
135
|
+
_cells = [];
|
|
136
|
+
get cells() {
|
|
137
|
+
return this._cells;
|
|
138
|
+
}
|
|
139
|
+
set cells(value) {
|
|
140
|
+
this._cells = [...value];
|
|
141
|
+
this.rebuildMap();
|
|
142
|
+
}
|
|
143
|
+
_solidTiles = [];
|
|
144
|
+
get solidTiles() {
|
|
145
|
+
return this._solidTiles;
|
|
146
|
+
}
|
|
147
|
+
set solidTiles(value) {
|
|
148
|
+
this._solidTiles = [...value];
|
|
149
|
+
this.rebuildSolids();
|
|
150
|
+
}
|
|
151
|
+
_layer = 0;
|
|
152
|
+
get layer() {
|
|
153
|
+
return this._layer;
|
|
154
|
+
}
|
|
155
|
+
set layer(value) {
|
|
156
|
+
this._layer = value;
|
|
157
|
+
this.rebuildGeometry();
|
|
158
|
+
}
|
|
159
|
+
mesh;
|
|
160
|
+
loadedTexture;
|
|
161
|
+
derivedSolids = [];
|
|
162
|
+
onReady() {
|
|
163
|
+
this.mesh = new THREE.Mesh(new THREE.BufferGeometry(), this.makeMaterial());
|
|
164
|
+
this.entity.node.add(this.mesh);
|
|
165
|
+
this.rebuildMaterial();
|
|
166
|
+
this.rebuildMap();
|
|
167
|
+
}
|
|
168
|
+
onProjectionChange() {
|
|
169
|
+
this.rebuildGeometry();
|
|
170
|
+
}
|
|
171
|
+
onDestroy() {
|
|
172
|
+
this.mesh?.removeFromParent();
|
|
173
|
+
this.mesh?.geometry.dispose();
|
|
174
|
+
this.mesh?.material.dispose();
|
|
175
|
+
this.loadedTexture?.dispose();
|
|
176
|
+
this.loadedTexture = undefined;
|
|
177
|
+
this.derivedSolids = [];
|
|
178
|
+
}
|
|
179
|
+
solids() {
|
|
180
|
+
return this.derivedSolids;
|
|
181
|
+
}
|
|
182
|
+
cellIndex(column, row) {
|
|
183
|
+
return gridCellIndex(this.mapWidth, this.mapHeight, column, row);
|
|
184
|
+
}
|
|
185
|
+
cellAt(logicalX, logicalY) {
|
|
186
|
+
return gridCellAt(this.gridSpec(), logicalX, logicalY);
|
|
187
|
+
}
|
|
188
|
+
cellBounds(column, row) {
|
|
189
|
+
return gridCellBounds(this.gridSpec(), column, row);
|
|
190
|
+
}
|
|
191
|
+
gridSpec() {
|
|
192
|
+
return {
|
|
193
|
+
mapWidth: this.mapWidth,
|
|
194
|
+
mapHeight: this.mapHeight,
|
|
195
|
+
cellSize: this.cellSize,
|
|
196
|
+
originX: this.entity?.position.x ?? 0,
|
|
197
|
+
originY: this.entity?.position.y ?? 0,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
rebuildMap() {
|
|
201
|
+
this.rebuildGeometry();
|
|
202
|
+
this.rebuildSolids();
|
|
203
|
+
}
|
|
204
|
+
rebuildMaterial() {
|
|
205
|
+
const mesh = this.mesh;
|
|
206
|
+
if (!mesh)
|
|
207
|
+
return;
|
|
208
|
+
this.loadedTexture?.dispose();
|
|
209
|
+
this.loadedTexture = undefined;
|
|
210
|
+
mesh.material.dispose();
|
|
211
|
+
mesh.material = this.makeMaterial();
|
|
212
|
+
if (!this.texture)
|
|
213
|
+
return;
|
|
214
|
+
const requested = this.texture;
|
|
215
|
+
const texture = loader.load(requested, () => {
|
|
216
|
+
if (this.loadedTexture === texture && this.texture === requested)
|
|
217
|
+
this.rebuildGeometry();
|
|
218
|
+
});
|
|
219
|
+
if (this.pixelArt) {
|
|
220
|
+
texture.magFilter = THREE.NearestFilter;
|
|
221
|
+
texture.minFilter = THREE.NearestFilter;
|
|
222
|
+
}
|
|
223
|
+
texture.colorSpace = THREE.SRGBColorSpace;
|
|
224
|
+
this.loadedTexture = texture;
|
|
225
|
+
mesh.material.map = texture;
|
|
226
|
+
mesh.material.color.set(0xffffff);
|
|
227
|
+
mesh.material.needsUpdate = true;
|
|
228
|
+
}
|
|
229
|
+
makeMaterial() {
|
|
230
|
+
return new THREE.MeshBasicMaterial({
|
|
231
|
+
color: this.color,
|
|
232
|
+
transparent: true,
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
rebuildGeometry() {
|
|
236
|
+
const mesh = this.mesh;
|
|
237
|
+
if (!mesh)
|
|
238
|
+
return;
|
|
239
|
+
const positions = [];
|
|
240
|
+
const uvs = [];
|
|
241
|
+
const indices = [];
|
|
242
|
+
const width = Math.max(0, Math.floor(this.mapWidth));
|
|
243
|
+
const height = Math.max(0, Math.floor(this.mapHeight));
|
|
244
|
+
const size = this.cellSize;
|
|
245
|
+
if (Number.isFinite(size) && size > 0) {
|
|
246
|
+
const image = this.loadedTexture?.image;
|
|
247
|
+
const imageWidth = image?.width && image.width > 0 ? image.width : Math.max(1, this.cols);
|
|
248
|
+
const imageHeight = image?.height && image.height > 0 ? image.height : Math.max(1, this.rows);
|
|
249
|
+
const frameParams = {
|
|
250
|
+
gridOffsetX: this.gridOffsetX,
|
|
251
|
+
gridOffsetY: this.gridOffsetY,
|
|
252
|
+
spacingX: this.spacingX,
|
|
253
|
+
spacingY: this.spacingY,
|
|
254
|
+
cellWidth: this.cellWidth,
|
|
255
|
+
cellHeight: this.cellHeight,
|
|
256
|
+
};
|
|
257
|
+
for (let index = 0; index < width * height; index++) {
|
|
258
|
+
const tile = this.cells[index] ?? -1;
|
|
259
|
+
if (!Number.isFinite(tile) || tile < 0)
|
|
260
|
+
continue;
|
|
261
|
+
const column = index % width;
|
|
262
|
+
const row = Math.floor(index / width);
|
|
263
|
+
const logicalCenterX = (column + 0.5) * size;
|
|
264
|
+
const logicalCenterY = (row + 0.5) * size;
|
|
265
|
+
const center = this.game.projection === 'isometric'
|
|
266
|
+
? projectIsometric(logicalCenterX, logicalCenterY)
|
|
267
|
+
: { x: logicalCenterX, y: logicalCenterY };
|
|
268
|
+
const halfWidth = this.game.projection === 'isometric' ? size : size / 2;
|
|
269
|
+
const halfHeight = size / 2;
|
|
270
|
+
const z = this.layer * 0.01;
|
|
271
|
+
positions.push(center.x - halfWidth, center.y - halfHeight, z, center.x + halfWidth, center.y - halfHeight, z, center.x + halfWidth, center.y + halfHeight, z, center.x - halfWidth, center.y + halfHeight, z);
|
|
272
|
+
const frame = sheetCell(imageWidth, imageHeight, this.cols, this.rows, tile, frameParams);
|
|
273
|
+
const left = frame.x / imageWidth;
|
|
274
|
+
const right = (frame.x + frame.width) / imageWidth;
|
|
275
|
+
const bottom = 1 - (frame.y + frame.height) / imageHeight;
|
|
276
|
+
const top = 1 - frame.y / imageHeight;
|
|
277
|
+
uvs.push(left, bottom, right, bottom, right, top, left, top);
|
|
278
|
+
const vertex = positions.length / 3 - 4;
|
|
279
|
+
indices.push(vertex, vertex + 1, vertex + 2, vertex, vertex + 2, vertex + 3);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
const geometry = new THREE.BufferGeometry();
|
|
283
|
+
geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
|
|
284
|
+
geometry.setAttribute('uv', new THREE.Float32BufferAttribute(uvs, 2));
|
|
285
|
+
geometry.setIndex(new THREE.BufferAttribute(new Uint32Array(indices), 1));
|
|
286
|
+
mesh.geometry.dispose();
|
|
287
|
+
mesh.geometry = geometry;
|
|
288
|
+
}
|
|
289
|
+
rebuildSolids() {
|
|
290
|
+
if (!this.entity || !this.game)
|
|
291
|
+
return;
|
|
292
|
+
const solids = new Set(this.solidTiles);
|
|
293
|
+
const width = Math.max(0, Math.floor(this.mapWidth));
|
|
294
|
+
const height = Math.max(0, Math.floor(this.mapHeight));
|
|
295
|
+
const next = [];
|
|
296
|
+
for (let index = 0; index < width * height; index++) {
|
|
297
|
+
const tile = this.cells[index] ?? -1;
|
|
298
|
+
if (!solids.has(tile))
|
|
299
|
+
continue;
|
|
300
|
+
const column = index % width;
|
|
301
|
+
const row = Math.floor(index / width);
|
|
302
|
+
const bounds = this.cellBounds(column, row);
|
|
303
|
+
if (!bounds)
|
|
304
|
+
continue;
|
|
305
|
+
const solid = new Solid();
|
|
306
|
+
solid.entity = this.entity;
|
|
307
|
+
solid.game = this.game;
|
|
308
|
+
solid.width = this.cellSize;
|
|
309
|
+
solid.height = this.cellSize;
|
|
310
|
+
solid.offsetX = bounds.centerX - this.entity.position.x;
|
|
311
|
+
solid.offsetY = bounds.centerY - this.entity.position.y;
|
|
312
|
+
next.push(solid);
|
|
313
|
+
}
|
|
314
|
+
this.derivedSolids = next;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
@@ -10,10 +10,13 @@ export declare class Entity {
|
|
|
10
10
|
readonly name: string;
|
|
11
11
|
readonly node: THREE.Group<THREE.Object3DEventMap>;
|
|
12
12
|
readonly components: Component[];
|
|
13
|
+
private logicalPosition;
|
|
13
14
|
private destroyed;
|
|
14
15
|
get alive(): boolean;
|
|
15
16
|
constructor(game: Game, name: string);
|
|
16
17
|
get position(): THREE.Vector3;
|
|
18
|
+
/** Keeps identity scenes zero-copy while projected scenes own a logical transform. */
|
|
19
|
+
setProjected(projected: boolean): void;
|
|
17
20
|
get scale(): THREE.Vector3;
|
|
18
21
|
add<T extends Component>(Class: ComponentClass<T>, props?: Partial<T>): T;
|
|
19
22
|
get<T extends Component>(Class: ComponentClass<T>): T | undefined;
|
|
@@ -8,6 +8,7 @@ export class Entity {
|
|
|
8
8
|
name;
|
|
9
9
|
node = new THREE.Group();
|
|
10
10
|
components = [];
|
|
11
|
+
logicalPosition = null;
|
|
11
12
|
destroyed = false;
|
|
12
13
|
get alive() {
|
|
13
14
|
return !this.destroyed;
|
|
@@ -17,7 +18,18 @@ export class Entity {
|
|
|
17
18
|
this.name = name;
|
|
18
19
|
}
|
|
19
20
|
get position() {
|
|
20
|
-
return this.node.position;
|
|
21
|
+
return this.logicalPosition ?? this.node.position;
|
|
22
|
+
}
|
|
23
|
+
/** Keeps identity scenes zero-copy while projected scenes own a logical transform. */
|
|
24
|
+
setProjected(projected) {
|
|
25
|
+
if (projected === (this.logicalPosition !== null))
|
|
26
|
+
return;
|
|
27
|
+
if (projected) {
|
|
28
|
+
this.logicalPosition = this.node.position.clone();
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
this.node.position.copy(this.logicalPosition);
|
|
32
|
+
this.logicalPosition = null;
|
|
21
33
|
}
|
|
22
34
|
get scale() {
|
|
23
35
|
return this.node.scale;
|
|
@@ -62,6 +62,7 @@ export declare class Game {
|
|
|
62
62
|
private viewHeight;
|
|
63
63
|
private sceneCamera;
|
|
64
64
|
private renderSort;
|
|
65
|
+
private sceneProjection;
|
|
65
66
|
private lastTime;
|
|
66
67
|
private runtimeBridge;
|
|
67
68
|
constructor(options: GameOptions);
|
|
@@ -91,6 +92,8 @@ export declare class Game {
|
|
|
91
92
|
stop(): void;
|
|
92
93
|
/** Internal: called by Entity.destroy(). */
|
|
93
94
|
removeEntity(entity: Entity): void;
|
|
95
|
+
/** The scene's render projection; null keeps logical and render space identical. */
|
|
96
|
+
get projection(): 'isometric' | null;
|
|
94
97
|
/** Visible world height (2D camera zoom). */
|
|
95
98
|
get view(): number;
|
|
96
99
|
setViewHeight(value: number): void;
|
|
@@ -105,6 +108,7 @@ export declare class Game {
|
|
|
105
108
|
private renderSurface;
|
|
106
109
|
private componentUpdateSchedule;
|
|
107
110
|
private updateSceneCamera;
|
|
111
|
+
private renderPoint;
|
|
108
112
|
private dispatchCollisions;
|
|
109
113
|
private resize;
|
|
110
114
|
}
|
|
@@ -8,6 +8,7 @@ import { Emitter } from './events.js';
|
|
|
8
8
|
import { Input } from './input.js';
|
|
9
9
|
import { activeRuntimeBridgeHook, EngineRuntimeBridge, } from './runtime-bridge.js';
|
|
10
10
|
import { RuntimeInspector } from './runtime-inspection.js';
|
|
11
|
+
import { projectIsometric } from './projection.js';
|
|
11
12
|
import { isYSortParticipant, ySortZ } from './render-sort.js';
|
|
12
13
|
import { registryEntry, spawnFromJson } from './scene.js';
|
|
13
14
|
import { Stats } from './stats.js';
|
|
@@ -42,6 +43,7 @@ export class Game {
|
|
|
42
43
|
viewHeight;
|
|
43
44
|
sceneCamera = null;
|
|
44
45
|
renderSort = null;
|
|
46
|
+
sceneProjection = null;
|
|
45
47
|
lastTime = 0;
|
|
46
48
|
runtimeBridge = null;
|
|
47
49
|
constructor(options) {
|
|
@@ -63,6 +65,7 @@ export class Game {
|
|
|
63
65
|
/** Creates a live entity in the scene. */
|
|
64
66
|
spawn(name) {
|
|
65
67
|
const entity = new Entity(this, name);
|
|
68
|
+
entity.setProjected(this.sceneProjection === 'isometric');
|
|
66
69
|
this.entities.push(entity);
|
|
67
70
|
this.scene.add(entity.node);
|
|
68
71
|
return entity;
|
|
@@ -115,6 +118,15 @@ export class Game {
|
|
|
115
118
|
*/
|
|
116
119
|
setSceneRender(json) {
|
|
117
120
|
this.renderSort = json?.sort === 'y' ? 'y' : null;
|
|
121
|
+
const projection = json?.projection === 'isometric' ? 'isometric' : null;
|
|
122
|
+
if (projection === this.sceneProjection)
|
|
123
|
+
return;
|
|
124
|
+
this.sceneProjection = projection;
|
|
125
|
+
for (const entity of this.entities) {
|
|
126
|
+
entity.setProjected(projection === 'isometric');
|
|
127
|
+
for (const component of entity.components)
|
|
128
|
+
component.onProjectionChange?.(projection);
|
|
129
|
+
}
|
|
118
130
|
}
|
|
119
131
|
/**
|
|
120
132
|
* Adopts a scene's camera block: jumps to its framing and, while
|
|
@@ -130,11 +142,11 @@ export class Game {
|
|
|
130
142
|
// With a follow target the declared position is moot: start centered on
|
|
131
143
|
// the target so play begins framed like the editor shows it.
|
|
132
144
|
const followed = this.sceneCamera.follow ? this.find(this.sceneCamera.follow) : undefined;
|
|
133
|
-
const
|
|
134
|
-
?
|
|
135
|
-
: this.sceneCamera.position;
|
|
136
|
-
this.camera.position.x = x;
|
|
137
|
-
this.camera.position.y = y;
|
|
145
|
+
const center = followed
|
|
146
|
+
? this.renderPoint(followed.position.x, followed.position.y)
|
|
147
|
+
: { x: this.sceneCamera.position[0], y: this.sceneCamera.position[1] };
|
|
148
|
+
this.camera.position.x = center.x;
|
|
149
|
+
this.camera.position.y = center.y;
|
|
138
150
|
this.setViewHeight(this.sceneCamera.zoom);
|
|
139
151
|
}
|
|
140
152
|
start() {
|
|
@@ -168,6 +180,10 @@ export class Game {
|
|
|
168
180
|
if (i !== -1)
|
|
169
181
|
this.entities.splice(i, 1);
|
|
170
182
|
}
|
|
183
|
+
/** The scene's render projection; null keeps logical and render space identical. */
|
|
184
|
+
get projection() {
|
|
185
|
+
return this.sceneProjection;
|
|
186
|
+
}
|
|
171
187
|
/** Visible world height (2D camera zoom). */
|
|
172
188
|
get view() {
|
|
173
189
|
return this.viewHeight;
|
|
@@ -233,7 +249,7 @@ export class Game {
|
|
|
233
249
|
for (const component of entity.components) {
|
|
234
250
|
if (isYSortParticipant(component)) {
|
|
235
251
|
participants.push(component);
|
|
236
|
-
entries.push({ layer: component.layer, y: entity.position.y });
|
|
252
|
+
entries.push({ layer: component.layer, y: entity.node.position.y });
|
|
237
253
|
}
|
|
238
254
|
}
|
|
239
255
|
}
|
|
@@ -242,6 +258,13 @@ export class Game {
|
|
|
242
258
|
participant.setSortZ(z[index]);
|
|
243
259
|
}
|
|
244
260
|
renderSurface() {
|
|
261
|
+
if (this.sceneProjection === 'isometric') {
|
|
262
|
+
for (const entity of this.entities) {
|
|
263
|
+
const projected = projectIsometric(entity.position.x, entity.position.y);
|
|
264
|
+
entity.node.position.x = projected.x;
|
|
265
|
+
entity.node.position.y = projected.y;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
245
268
|
if (this.renderSort === 'y')
|
|
246
269
|
this.applyYSort();
|
|
247
270
|
this.ui.setActive(this.simulate);
|
|
@@ -291,19 +314,28 @@ export class Game {
|
|
|
291
314
|
const followed = cam.follow ? this.find(cam.follow) : undefined;
|
|
292
315
|
const provider = followed?.components.find((c) => isCameraVelocityProvider(c));
|
|
293
316
|
const velocity = provider?.getCameraVelocity();
|
|
317
|
+
const target = followed
|
|
318
|
+
? this.renderPoint(followed.position.x, followed.position.y)
|
|
319
|
+
: null;
|
|
320
|
+
const renderVelocity = velocity
|
|
321
|
+
? this.renderPoint(velocity.vx, velocity.vy)
|
|
322
|
+
: { x: 0, y: 0 };
|
|
294
323
|
const next = stepSceneCamera(cam, {
|
|
295
324
|
x: this.camera.position.x,
|
|
296
325
|
y: this.camera.position.y,
|
|
297
326
|
halfW: (this.camera.right - this.camera.left) / 2,
|
|
298
327
|
halfH: this.viewHeight / 2,
|
|
299
|
-
target
|
|
300
|
-
vx:
|
|
301
|
-
vy:
|
|
328
|
+
target,
|
|
329
|
+
vx: renderVelocity.x,
|
|
330
|
+
vy: renderVelocity.y,
|
|
302
331
|
dt,
|
|
303
332
|
});
|
|
304
333
|
this.camera.position.x = next.x;
|
|
305
334
|
this.camera.position.y = next.y;
|
|
306
335
|
}
|
|
336
|
+
renderPoint(x, y) {
|
|
337
|
+
return this.sceneProjection === 'isometric' ? projectIsometric(x, y) : { x, y };
|
|
338
|
+
}
|
|
307
339
|
dispatchCollisions() {
|
|
308
340
|
const boxed = this.entities.filter((e) => e.has(Hitbox));
|
|
309
341
|
for (let i = 0; i < boxed.length; i++) {
|
|
@@ -4,6 +4,10 @@ export { installDirectionalAnimation, installedDirectionalAnimation, isAnimation
|
|
|
4
4
|
export type { AnimationFacingProvider, DirectionalAnimation, DirectionalFallback, ResolvedDirectionalClip, } from './animation/directional.js';
|
|
5
5
|
export { isYSortParticipant, ySortZ } from './render-sort.js';
|
|
6
6
|
export type { YSortEntry, YSortParticipant } from './render-sort.js';
|
|
7
|
+
export { projectIsometric, screenInputToLogical, unprojectIsometric } from './projection.js';
|
|
8
|
+
export type { ProjectedPoint } from './projection.js';
|
|
9
|
+
export { spritePlacement } from './sprite-placement.js';
|
|
10
|
+
export type { SpritePlacement, SpritePlacementInput } from './sprite-placement.js';
|
|
7
11
|
export { CAMERA_DEFAULTS, isCameraVelocityProvider, resolveSceneCamera, stepSceneCamera } from './camera.js';
|
|
8
12
|
export type { SceneCameraJson, CameraLimitsJson, CameraVelocity, CameraVelocityProvider, ResolvedSceneCamera, } from './camera.js';
|
|
9
13
|
export { Entity } from './entity.js';
|
|
@@ -29,9 +33,14 @@ export { Solid } from './components/solid.js';
|
|
|
29
33
|
export { Hitbox } from './components/hitbox.js';
|
|
30
34
|
export { DynamicBody } from './components/dynamic-body.js';
|
|
31
35
|
export { AnimatedSprite } from './components/animated-sprite.js';
|
|
36
|
+
export { Tilemap } from './components/tilemap.js';
|
|
32
37
|
export { aabbOverlap } from './aabb.js';
|
|
33
38
|
export { resolveSolidAxis } from './solid-axis.js';
|
|
34
39
|
export type { CollisionAxis, SolidAxisOptions } from './solid-axis.js';
|
|
40
|
+
export { isSolidSource, sceneSolids, SOLID_SOURCE_SYMBOL } from './scene-solids.js';
|
|
41
|
+
export type { SolidSource } from './scene-solids.js';
|
|
42
|
+
export { cellAt, cellBounds, cellIndex } from './tilemap-grid.js';
|
|
43
|
+
export type { TilemapCell, TilemapCellBounds, TilemapGridSpec, } from './tilemap-grid.js';
|
|
35
44
|
export { COLLISION_SHAPES, DEFAULT_COLLISION_POLYGON, collisionBounds, collisionOverlap, collisionVertices, resolveCollisionPoints, } from './collision-shape.js';
|
|
36
45
|
export type { CollisionBody, CollisionBounds, CollisionPoint, CollisionShape, } from './collision-shape.js';
|
|
37
46
|
export { Emitter } from './events.js';
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export { Game } from './game.js';
|
|
2
2
|
export { installDirectionalAnimation, installedDirectionalAnimation, isAnimationFacingProvider, resolveDirectionalClip, } from './animation/directional.js';
|
|
3
3
|
export { isYSortParticipant, ySortZ } from './render-sort.js';
|
|
4
|
+
export { projectIsometric, screenInputToLogical, unprojectIsometric } from './projection.js';
|
|
5
|
+
export { spritePlacement } from './sprite-placement.js';
|
|
4
6
|
export { CAMERA_DEFAULTS, isCameraVelocityProvider, resolveSceneCamera, stepSceneCamera } from './camera.js';
|
|
5
7
|
export { Entity } from './entity.js';
|
|
6
8
|
export { Component } from './component.js';
|
|
@@ -17,8 +19,11 @@ export { Solid } from './components/solid.js';
|
|
|
17
19
|
export { Hitbox } from './components/hitbox.js';
|
|
18
20
|
export { DynamicBody } from './components/dynamic-body.js';
|
|
19
21
|
export { AnimatedSprite } from './components/animated-sprite.js';
|
|
22
|
+
export { Tilemap } from './components/tilemap.js';
|
|
20
23
|
export { aabbOverlap } from './aabb.js';
|
|
21
24
|
export { resolveSolidAxis } from './solid-axis.js';
|
|
25
|
+
export { isSolidSource, sceneSolids, SOLID_SOURCE_SYMBOL } from './scene-solids.js';
|
|
26
|
+
export { cellAt, cellBounds, cellIndex } from './tilemap-grid.js';
|
|
22
27
|
export { COLLISION_SHAPES, DEFAULT_COLLISION_POLYGON, collisionBounds, collisionOverlap, collisionVertices, resolveCollisionPoints, } from './collision-shape.js';
|
|
23
28
|
export { Emitter } from './events.js';
|
|
24
29
|
export { loadScene, spawnFromJson, resolveEntityComponents, resolveProps } from './scene.js';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface ProjectedPoint {
|
|
2
|
+
x: number;
|
|
3
|
+
y: number;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Maps screen-relative input into the logical square-grid world used by
|
|
7
|
+
* isometric simulation. The orthogonal rotation/reflection preserves input
|
|
8
|
+
* magnitude and angles; vectors longer than one are clamped after mapping.
|
|
9
|
+
*/
|
|
10
|
+
export declare function screenInputToLogical(ix: number, iy: number): ProjectedPoint;
|
|
11
|
+
/** Fixed 2:1 map from logical world coordinates to render coordinates. */
|
|
12
|
+
export declare function projectIsometric(lx: number, ly: number): ProjectedPoint;
|
|
13
|
+
/** Exact inverse of projectIsometric. */
|
|
14
|
+
export declare function unprojectIsometric(x: number, y: number): ProjectedPoint;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const cleanZero = (value) => (value === 0 ? 0 : value);
|
|
2
|
+
/**
|
|
3
|
+
* Maps screen-relative input into the logical square-grid world used by
|
|
4
|
+
* isometric simulation. The orthogonal rotation/reflection preserves input
|
|
5
|
+
* magnitude and angles; vectors longer than one are clamped after mapping.
|
|
6
|
+
*/
|
|
7
|
+
export function screenInputToLogical(ix, iy) {
|
|
8
|
+
let x = (ix - iy) / Math.SQRT2;
|
|
9
|
+
let y = (-ix - iy) / Math.SQRT2;
|
|
10
|
+
const length = Math.hypot(x, y);
|
|
11
|
+
if (length > 1) {
|
|
12
|
+
x /= length;
|
|
13
|
+
y /= length;
|
|
14
|
+
}
|
|
15
|
+
return { x: cleanZero(x), y: cleanZero(y) };
|
|
16
|
+
}
|
|
17
|
+
/** Fixed 2:1 map from logical world coordinates to render coordinates. */
|
|
18
|
+
export function projectIsometric(lx, ly) {
|
|
19
|
+
return {
|
|
20
|
+
x: cleanZero(lx - ly),
|
|
21
|
+
y: cleanZero(-(lx + ly) / 2),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/** Exact inverse of projectIsometric. */
|
|
25
|
+
export function unprojectIsometric(x, y) {
|
|
26
|
+
return {
|
|
27
|
+
x: cleanZero(x / 2 - y),
|
|
28
|
+
y: cleanZero(-x / 2 - y),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Solid } from './components/solid.js';
|
|
2
|
+
import type { Entity } from './entity.js';
|
|
3
|
+
import type { Game } from './game.js';
|
|
4
|
+
/** Explicit opt-in brand for components that derive collision Solids. */
|
|
5
|
+
export declare const SOLID_SOURCE_SYMBOL: unique symbol;
|
|
6
|
+
/** Component seam for geometry that derives one or more collision Solids. */
|
|
7
|
+
export interface SolidSource {
|
|
8
|
+
readonly [SOLID_SOURCE_SYMBOL]: true;
|
|
9
|
+
solids(): readonly Solid[];
|
|
10
|
+
}
|
|
11
|
+
export declare function isSolidSource(value: unknown): value is SolidSource;
|
|
12
|
+
/**
|
|
13
|
+
* All static collision geometry in stable entity/component order. Without a
|
|
14
|
+
* SolidSource this is exactly one direct Solid per entity, as before.
|
|
15
|
+
*/
|
|
16
|
+
export declare function sceneSolids(game: Game, except?: Entity): Solid[];
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Solid } from './components/solid.js';
|
|
2
|
+
/** Explicit opt-in brand for components that derive collision Solids. */
|
|
3
|
+
export const SOLID_SOURCE_SYMBOL = Symbol('waica.solidSource');
|
|
4
|
+
export function isSolidSource(value) {
|
|
5
|
+
if (value === null || typeof value !== 'object')
|
|
6
|
+
return false;
|
|
7
|
+
const candidate = value;
|
|
8
|
+
return candidate[SOLID_SOURCE_SYMBOL] === true && typeof candidate.solids === 'function';
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* All static collision geometry in stable entity/component order. Without a
|
|
12
|
+
* SolidSource this is exactly one direct Solid per entity, as before.
|
|
13
|
+
*/
|
|
14
|
+
export function sceneSolids(game, except) {
|
|
15
|
+
const result = [];
|
|
16
|
+
for (const entity of game.entities) {
|
|
17
|
+
if (entity === except)
|
|
18
|
+
continue;
|
|
19
|
+
const direct = entity.get(Solid);
|
|
20
|
+
if (direct)
|
|
21
|
+
result.push(direct);
|
|
22
|
+
for (const component of entity.components ?? []) {
|
|
23
|
+
if (isSolidSource(component))
|
|
24
|
+
result.push(...component.solids());
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return result;
|
|
28
|
+
}
|
|
@@ -31,10 +31,12 @@ export interface PrefabJson {
|
|
|
31
31
|
/** Scene-wide render options (v3). */
|
|
32
32
|
export interface SceneRenderJson {
|
|
33
33
|
/**
|
|
34
|
-
* 'y' orders same-layer sprites by their
|
|
34
|
+
* 'y' orders same-layer sprites by their render-space Y — lower Y renders
|
|
35
35
|
* in front (top-down depth). Absent: spawn order breaks same-layer ties.
|
|
36
36
|
*/
|
|
37
37
|
sort?: 'y';
|
|
38
|
+
/** Logical positions are projected to the fixed 2:1 render lattice. */
|
|
39
|
+
projection?: 'isometric';
|
|
38
40
|
}
|
|
39
41
|
export interface SceneJson {
|
|
40
42
|
waicaScene: 1 | 2 | 3;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { collisionBounds, collisionOverlap } from './collision-shape.js';
|
|
2
2
|
import { Solid } from './components/solid.js';
|
|
3
|
+
import { sceneSolids } from './scene-solids.js';
|
|
3
4
|
const CONTACT_TOLERANCE = 1e-7;
|
|
4
5
|
function bodyFor(solid) {
|
|
5
6
|
return {
|
|
@@ -19,10 +20,7 @@ function bodyFor(solid) {
|
|
|
19
20
|
export function resolveSolidAxis({ entity, axis, previous, body, onContact, }) {
|
|
20
21
|
const position = entity.position;
|
|
21
22
|
const target = position[axis];
|
|
22
|
-
const solids = entity.game
|
|
23
|
-
.filter((other) => other !== entity)
|
|
24
|
-
.map((other) => other.get(Solid))
|
|
25
|
-
.filter((solid) => solid !== undefined);
|
|
23
|
+
const solids = sceneSolids(entity.game, entity);
|
|
26
24
|
position[axis] = previous;
|
|
27
25
|
// Preserve the established spawn-inside-wall bail: pre-existing overlaps
|
|
28
26
|
// are ignored for this move rather than becoming arbitrary push-out rules.
|