@@ -211,47 +211,199 @@ function calculateAdjacency(tileIdx, tileId, tiles, tileset, w, h) {
211211const tilesetMap = new Map ( )
212212const imgMap = new Map ( )
213213
214- async function loadTileset ( tilesetPath ) {
215- if ( tilesetMap . has ( tilesetPath ) ) return tilesetMap . get ( tilesetPath )
216- const res = await fetch ( tilesetPath )
217- const rawJson = await res . json ( )
218- const tilesetJson = rawJson . tiles
219- const tileset = { }
220- const path = rawJson . path
221-
222- const promises = tilesetJson . map ( async ( def ) => {
214+ async function loadSpriteSheetTileset ( manifest ) {
215+ const tileset = [ ]
216+ const raw = await fetch ( manifest . spritesheetPath )
217+ const blob = await raw . blob ( )
218+
219+ const spriteSheet = await new Promise ( ( resolve , reject ) => {
223220 const img = new Image ( )
224- img . src = path + def . file
225- await new Promise ( resolve => {
226- img . onload = resolve
227- img . onerror = resolve
228- } )
221+ img . onload = ( ) => resolve ( img )
222+ img . onerror = reject
223+ img . src = manifest . spritesheetPath
224+ } )
229225
230- tileset [ def . id ] = { ...def , triggerAdjacency : def . triggerAdjacency , image : img , images : [ ] }
231-
232- if ( def . type == "adjacency" || def . type == "rotation" ) {
233- const w = img . naturalHeight
234- if ( w > 0 ) {
235- const count = Math . floor ( img . naturalWidth / w )
236- for ( let i = 0 ; i < count ; i ++ ) {
237- const canvas = document . createElement ( 'canvas' )
238- canvas . width = w
239- canvas . height = w
240- const ctx = canvas . getContext ( '2d' )
241- ctx . drawImage ( img , i * w , 0 , w , w , 0 , 0 , w , w )
242-
243- const sliceImg = new Image ( )
244- sliceImg . src = canvas . toDataURL ( )
245- tileset [ def . id ] . images [ i ] = sliceImg
226+ const tileWidth = manifest . tileWidth
227+ const width = spriteSheet . naturalWidth / tileWidth
228+ console . log ( `width: ${ width } ` )
229+ for ( const tile of manifest . tiles ) {
230+ const dpr = window . devicePixelRatio
231+ const canvas = document . createElement ( "canvas" )
232+ const ctx = canvas . getContext ( '2d' )
233+ canvas . width = width
234+ canvas . height = width
235+ ctx . imageSmoothingEnabled = false
236+ canvas . style . imageRendering = 'pixelated'
237+
238+ ctx . drawImage ( spriteSheet , tile . x * width , tile . y * width , width , width , 0 , 0 , width , width )
239+
240+ const images = [ ]
241+
242+ if ( tile . type === "adjacency" ) {
243+ for ( let i = 0 ; i < 16 ; i ++ ) {
244+ const subCanvas = document . createElement ( "canvas" )
245+ const subCtx = subCanvas . getContext ( "2d" )
246+ subCtx . setTransform ( dpr , 0 , 0 , dpr , 0 , 0 )
247+ subCanvas . width = width
248+ subCanvas . height = width
249+ subCtx . imageSmoothingEnabled = false
250+ subCanvas . style . imageRendering = 'pixelated'
251+
252+ let x = tile . x + i
253+ let y = tile . y
254+
255+ if ( x >= tileWidth ) {
256+ y = Math . floor ( x / tileWidth ) + y
257+ x = x % tileWidth
246258 }
259+ subCtx . drawImage ( spriteSheet , x * width , y * width , width , width , 0 , 0 , width , width )
260+ images . push ( subCanvas )
261+ }
262+ } else if ( tile . type === "rotation" ) {
263+ for ( let i = 0 ; i < 4 ; i ++ ) {
264+ const subCanvas = document . createElement ( "canvas" )
265+ const subCtx = subCanvas . getContext ( "2d" )
266+ subCtx . setTransform ( dpr , 0 , 0 , dpr , 0 , 0 )
267+ subCanvas . width = width
268+ subCanvas . height = width
269+ subCtx . imageSmoothingEnabled = false
270+ subCanvas . style . imageRendering = 'pixelated'
271+
272+ let x = tile . x + i
273+ let y = tile . y
274+
275+ if ( x >= tileWidth ) {
276+ y = Math . floor ( x / tileWidth ) + y
277+ x = x % tileWidth
278+ }
279+ subCtx . drawImage ( spriteSheet , x * width , y * width , width , width , 0 , 0 , width , width )
280+ images . push ( subCanvas )
247281 }
248282 }
249- } )
250- await Promise . all ( promises )
251- tilesetMap . set ( tilesetPath , tileset )
283+
284+ let minimapColor = 'rgba(0, 0, 0, 0)'
285+ try {
286+ const imgData = ctx . getImageData ( 0 , 0 , width , width ) . data
287+ const colorCounts = { }
288+ let maxCount = 0
289+ for ( let i = 0 ; i < imgData . length ; i += 4 ) {
290+ const r = imgData [ i ]
291+ const g = imgData [ i + 1 ]
292+ const b = imgData [ i + 2 ]
293+ const a = imgData [ i + 3 ]
294+
295+ if ( a < 128 ) continue
296+ const rgb = `rgb(${ r } , ${ g } , ${ b } )`
297+ colorCounts [ rgb ] = ( colorCounts [ rgb ] || 0 ) + 1
298+
299+ if ( colorCounts [ rgb ] > maxCount ) {
300+ maxCount = colorCounts [ rgb ]
301+ minimapColor = rgb
302+ }
303+ }
304+ } catch ( e ) {
305+ console . warn ( "could not calculate minimap color" , e )
306+ }
307+
308+ const tileObject = {
309+ ...tile ,
310+ minimapColor : minimapColor ,
311+ image : canvas ,
312+ }
313+ if ( images . length > 0 ) {
314+ tileObject . images = images
315+ }
316+ tileset . push ( tileObject )
317+ }
318+ console . log ( tileset )
252319 return tileset
253320}
254321
322+ export async function loadTileset ( manifestPath ) {
323+ if ( manifestPath === "/assets/medium-spritesheet.json" ) manifestPath = "/assets/medium.json"
324+ if ( tilesetMap . has ( manifestPath ) ) {
325+ const tileset = tilesetMap . get ( manifestPath )
326+ return tileset
327+ }
328+
329+ const fetchPromise = fetch ( manifestPath )
330+ . then ( response => response . json ( ) )
331+ . then ( async ( manifest ) => {
332+ if ( manifest . type == "spritesheet" ) {
333+ const tileset = await loadSpriteSheetTileset ( manifest )
334+
335+ const characterImage = await new Promise ( ( resolve ) => {
336+ const img = new Image ( )
337+ const prefix = manifest . path + "/"
338+ img . onload = ( ) => resolve ( img )
339+ img . onerror = ( e ) => {
340+ console . error ( `failed to load character image from: ${ srcPath } ` , e )
341+ resolve ( null )
342+ }
343+ img . src = prefix + manifest . characterFile
344+ } )
345+
346+ tilesetMap . set ( manifestPath , tileset )
347+ console . log ( characterImage )
348+ return tileset
349+ }
350+
351+ let loadedCount = 0
352+ const totalCount = manifest . tiles . length + 1
353+
354+ function updateProgress ( ) {
355+ loadedCount ++
356+ window . dispatchEvent ( new CustomEvent ( 'loading:progress' , {
357+ detail : { loaded : loadedCount , total : totalCount }
358+ } ) )
359+ }
360+
361+ const promises = manifest . tiles . map ( tileData => {
362+
363+ if ( ! tileData . file ) {
364+ updateProgress ( )
365+ return Promise . resolve ( tileData )
366+ }
367+ return new Promise ( ( resolve , reject ) => {
368+ const img = new Image ( )
369+ img . src = manifest . path + tileData . file
370+ img . onload = ( ) => {
371+ const canvas = document . createElement ( 'canvas' )
372+ const ctx = canvas . getContext ( '2d' )
373+ canvas . width = img . height || 1
374+ canvas . height = img . height || 1
375+ ctx . drawImage ( img , 0 , 0 )
376+
377+ updateProgress ( )
378+ resolve ( { ...tileData , image : img } )
379+ }
380+ img . onerror = ( e ) => {
381+ updateProgress ( )
382+ reject ( e )
383+ }
384+ } )
385+ } )
386+
387+ return Promise . all ( promises )
388+ . then ( ( items ) => {
389+ const tileset = [ ]
390+ items . forEach ( item => {
391+ tileset [ item . id ] = item
392+ } )
393+
394+ tilesetMap . set ( manifestPath , tileset )
395+ return tileset
396+ } )
397+
398+ } )
399+
400+ tilesetMap . set ( manifestPath , fetchPromise )
401+
402+ return fetchPromise
403+
404+ }
405+
406+
255407export async function renderLevelPreview ( canvas , levelData ) {
256408 const tilesetPath = levelData . data ? levelData . data . tilesetPath : "/assets/medium.json"
257409 let tileset = await loadTileset ( tilesetPath )
0 commit comments