11import { PLAYER_GEOMETRY , WORLD } from "./constants" ;
22import { EntityWorld } from "./entities/EntityWorld" ;
33import { type Aabb , LevelEntitySpec , SpikeDirection } from "./entities/types" ;
4+ import { resolveGroundedSpawnPoint } from "./spawn" ;
45
56export type RoomDirection = "left" | "right" | "up" | "down" ;
67
@@ -48,8 +49,8 @@ const ROOM_BLUEPRINTS: readonly LevelRoomBlueprint[] = [
4849 "XXXXX..................................." ,
4950 "XXXXX..................................." ,
5051 "XXXXX.....................XXXX.........." ,
51- "XXS....................................." ,
5252 "XX......................................" ,
53+ "XXS....................................." ,
5354 "XXXXXXXX................................" ,
5455 "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" ,
5556 "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" ,
@@ -84,9 +85,9 @@ const ROOM_BLUEPRINTS: readonly LevelRoomBlueprint[] = [
8485 "...................................XXXXX" ,
8586 "...................................XXXXX" ,
8687 "...................................XXXXX" ,
87- "S..................................XXXXX" ,
8888 "...................................XXXXX" ,
89- "......................XXXXXXX......XXXXX" ,
89+ "...................................XXXXX" ,
90+ "S.....................XXXXXXX......XXXXX" ,
9091 "XXXXXXXXXXXXXXXXXX...XXXXXXXXXXXXXXXXXXX" ,
9192 "XXXXXXXXXXXXXXXXXX...XXXXXXXXXXXXXXXXXXX" ,
9293 "XXXXXXXXXXXXXXXXXX...XXXXXXXXXXXXXXXXXXX" ,
@@ -121,8 +122,8 @@ const ROOM_BLUEPRINTS: readonly LevelRoomBlueprint[] = [
121122 "XXXX...........XXXXXXXX...XXXXXXX..................XXXX" ,
122123 "XXXXX..........XXXXXXXX......XXXX.............=====XXXX" ,
123124 "XXXXX..........XXXXXXXX.....XXXXX..................XXXX" ,
124- "XXXXXS .........XXXXXXXX............................XXXX" ,
125- "XXXXX. .........XXXXXXXX...XXXXXXX..................XXXX" ,
125+ "XXXXX. .........XXXXXXXX............................XXXX" ,
126+ "XXXXXS .........XXXXXXXX...XXXXXXX..................XXXX" ,
126127 "XXXXXXXXX^^^^^^XXXXXXXX^^^XXXXXXX.......XXXX.......XXXX" ,
127128 "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.......XXXX" ,
128129 "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX^^^^^^^XXXX" ,
@@ -142,8 +143,8 @@ const ROOM_BLUEPRINTS: readonly LevelRoomBlueprint[] = [
142143 "X.......................................XXXXX" ,
143144 ".......^^^^^^^^^^^^^^^^^^...............XXXXX" ,
144145 ".......XXXXXXXXXXXXXXXXXX...............XXXXX" ,
145- "S.......................................XXXXX" ,
146146 "........................................XXXXX" ,
147+ "S.......................................XXXXX" ,
147148 "XX....................................XXXXXXX" ,
148149 "XX....................................XXXXXXX" ,
149150 "XX........XXXXXXX...................XXXXXXXXX" ,
@@ -186,7 +187,7 @@ export function buildLevelFromBlueprints(
186187 const spawnInsetX = Math . floor ( ( WORLD . tile - PLAYER_GEOMETRY . hitboxW ) * 0.5 ) ;
187188 let worldCols = 0 ;
188189 let worldRows = 0 ;
189- let startCheckpoint : { x : number ; y : number } | null = null ;
190+ let startRoomId : string | null = null ;
190191 let hasExplicitInitialSpawn = false ;
191192
192193 for ( const blueprint of blueprints ) {
@@ -214,10 +215,10 @@ export function buildLevelFromBlueprints(
214215 if ( ch === "S" ) {
215216 checkpoint ??= {
216217 x : col * WORLD . tile + spawnInsetX + PLAYER_GEOMETRY . hitboxW * 0.5 ,
217- y : row * WORLD . tile + PLAYER_GEOMETRY . hitboxH ,
218+ y : row * WORLD . tile + WORLD . tile ,
218219 } ;
219- if ( ! hasExplicitInitialSpawn ) {
220- startCheckpoint ??= checkpoint ;
220+ if ( ! hasExplicitInitialSpawn && startRoomId === null ) {
221+ startRoomId = blueprint . id ;
221222 }
222223 continue ;
223224 }
@@ -258,7 +259,7 @@ export function buildLevelFromBlueprints(
258259 if ( checkpoint === null ) {
259260 throw new Error ( `Room "${ blueprint . id } " is marked as the initial spawn room but has no checkpoint` ) ;
260261 }
261- startCheckpoint = checkpoint ;
262+ startRoomId = blueprint . id ;
262263 hasExplicitInitialSpawn = true ;
263264 }
264265
@@ -273,15 +274,27 @@ export function buildLevelFromBlueprints(
273274 } ) ;
274275 }
275276
276- if ( startCheckpoint === null ) {
277+ if ( startRoomId === null ) {
277278 throw new Error ( "Level requires at least one room checkpoint marked with 'S'" ) ;
278279 }
279280
281+ const world = EntityWorld . fromSpecs ( worldCols , worldRows , entities ) ;
282+ const resolvedRooms = rooms . map ( ( room ) => ( {
283+ ...room ,
284+ checkpoint : room . checkpoint === null
285+ ? null
286+ : resolveGroundedSpawnPoint ( world , room . bounds , room . checkpoint ) ,
287+ } ) ) ;
288+ const startRoom = resolvedRooms . find ( ( room ) => room . id === startRoomId ) ;
289+ if ( startRoom ?. checkpoint === null || startRoom === undefined ) {
290+ throw new Error ( `Resolved initial spawn room "${ startRoomId } " has no checkpoint` ) ;
291+ }
292+
280293 return {
281- world : EntityWorld . fromSpecs ( worldCols , worldRows , entities ) ,
282- rooms,
283- spawnX : startCheckpoint . x ,
284- spawnY : startCheckpoint . y ,
294+ world,
295+ rooms : resolvedRooms ,
296+ spawnX : startRoom . checkpoint . x ,
297+ spawnY : startRoom . checkpoint . y ,
285298 } ;
286299}
287300
0 commit comments