@@ -40,7 +40,13 @@ export type GridCell =
4040 }
4141 | {
4242 // switches / triggerable blocks
43- block : Block . switch | Block . switchingBlockOFF | Block . switchingBlockON | Block . switchPressed ;
43+ block :
44+ | Block . switch
45+ | Block . switchingBlockOFF
46+ | Block . switchingBlockON
47+ | Block . inverseSwitchingBlockOFF
48+ | Block . inverseSwitchingBlockON
49+ | Block . switchPressed ;
4450 switchId ?: string ; // optional でいいの?
4551 objectId ?: unknown ;
4652 } ;
@@ -49,6 +55,7 @@ export class Grid {
4955 __vsom : VirtualSOM ;
5056 marginY : number ; // windowの上端とy=0上端の距離(px)
5157 oobSprites : Sprite [ ] ; // グリッド定義の外を埋めるやつ
58+ oobFallableSprites : { sprite : Sprite | null ; vy : number } [ ] ; // グリッド定義外に落ちたfallable
5259 constructor (
5360 cx : {
5461 _stage_container : Container ;
@@ -61,6 +68,7 @@ export class Grid {
6168 ) {
6269 const stage = cx . _stage_container ;
6370 this . oobSprites = [ ] ;
71+ this . oobFallableSprites = [ ] ;
6472 this . marginY = ( height - cellSize * stageDefinition . stage . length ) / 2 ;
6573 const vsprites : VirtualSOM = [ ] ;
6674
@@ -104,6 +112,7 @@ export class Grid {
104112 } ) ;
105113 break ;
106114 }
115+ case Block . inverseSwitchingBlockOFF :
107116 case Block . switchingBlockOFF : {
108117 const switchId = (
109118 get ( cx . state ) . cells [ y ] [ x ] as {
@@ -126,6 +135,7 @@ export class Grid {
126135 } ) ;
127136 break ;
128137 }
138+ case Block . inverseSwitchingBlockON :
129139 case Block . switchingBlockON :
130140 case Block . switchPressed :
131141 throw new Error ( `[Grid.constructor]: block is not supported: ${ dblock } ` ) ;
@@ -226,6 +236,15 @@ export class Grid {
226236 }
227237 }
228238 this . initOOBSprites ( cx , cellSize ) ;
239+ this . clearFallableSprites ( cx ) ;
240+ }
241+ // 画面外のfallableはステージ内のfallableのコピーでありhistoryの対象ではないので、
242+ // undoなどでステージ全体に変更を加えるときに使う
243+ clearFallableSprites ( cx : { _stage_container : Container } ) {
244+ for ( const sprite of this . oobFallableSprites ) {
245+ if ( sprite . sprite ) cx . _stage_container . removeChild ( sprite . sprite ) ;
246+ }
247+ this . oobFallableSprites = [ ] ;
229248 }
230249 getBlock ( cx : Context , x : number , y : number ) : Block | null {
231250 return get ( cx . state ) . cells [ y ] ?. [ x ] ?. block ?? null ;
@@ -307,7 +326,7 @@ export class Grid {
307326 vprev . block = Block . switchPressed ;
308327 vprev . dy = 0 ;
309328 vprev . vy = 0 ;
310- } else if ( vprev ?. block === Block . switchPressed ) {
329+ } else if ( vprev ?. block === Block . switchPressed && cNewCell . block === Block . switch ) {
311330 // switchがプレイヤーに押されているのが戻るとき
312331 assert (
313332 cprev . block === Block . switchPressed || cprev . block === Block . switch ,
@@ -328,7 +347,7 @@ export class Grid {
328347 vprev . vy = 0 ;
329348 }
330349 // switch上にオブジェクトを置くとき
331- else if ( vprev ?. block === Block . switch ) {
350+ else if ( vprev ?. block === Block . switch || vprev ?. block === Block . switchPressed ) {
332351 if ( cNewCell . block !== Block . movable && cNewCell . block !== Block . fallable ) {
333352 console . warn ( "No block other than movable cannot be placed on the switch" ) ;
334353 console . log ( "cell.block" , cNewCell . block ) ;
@@ -338,7 +357,10 @@ export class Grid {
338357 stage . addChild ( movableSprite ) ;
339358 assert ( cNewCell . objectId !== undefined , "movable block must have objectId" ) ;
340359 assert (
341- ( cprev . block === Block . switch || cprev . block === Block . movable || cprev . block === Block . fallable ) &&
360+ ( cprev . block === Block . switch ||
361+ cprev . block === Block . switchPressed ||
362+ cprev . block === Block . movable ||
363+ cprev . block === Block . fallable ) &&
342364 cprev . switchId !== undefined ,
343365 "block is not switch" ,
344366 ) ;
@@ -354,6 +376,7 @@ export class Grid {
354376 get ( cx . state ) . switches . filter ( ( s ) => {
355377 if ( s . x === x && s . y === y ) {
356378 s . pressedByBlock = true ;
379+ s . pressedByPlayer = false ;
357380 }
358381 return s ;
359382 } ) ;
@@ -391,50 +414,60 @@ export class Grid {
391414 } ) ;
392415 }
393416 // switchingBlockOFFがONに切り替わるとき
394- else if ( vprev ?. block === Block . switchingBlockOFF ) {
395- if ( cNewCell . block !== Block . switchingBlockON ) {
417+ else if ( vprev ?. block === Block . switchingBlockOFF || vprev ?. block === Block . inverseSwitchingBlockOFF ) {
418+ if ( cNewCell . block !== Block . switchingBlockON && cNewCell . block !== Block . inverseSwitchingBlockON ) {
396419 console . warn ( "No block other than switchingBlockON cannot replace the switchingBlockOFF" ) ;
397420 return ;
398421 }
399422 assert (
400- cprev . block === Block . switchingBlockOFF || cprev . block === Block . switchingBlockON ,
423+ cprev . block === Block . switchingBlockOFF ||
424+ cprev . block === Block . switchingBlockON ||
425+ cprev . block === Block . inverseSwitchingBlockOFF ||
426+ cprev . block === Block . inverseSwitchingBlockON ,
401427 "block is not switchingBlock" ,
402428 ) ;
429+ const inversed = vprev . block === Block . inverseSwitchingBlockOFF ;
430+ const switchONBlock = inversed ? Block . inverseSwitchingBlockON : Block . switchingBlockON ;
403431 const switchId = cprev . switchId ;
404432 if ( ! switchId ) throw new Error ( "switchId is undefined" ) ;
405- const blockSprite = createSprite ( blockSize , Block . switchingBlockON , x , y , marginY , switchColor ( switchId ) ) ;
433+ const blockSprite = createSprite ( blockSize , switchONBlock , x , y , marginY , switchColor ( switchId ) ) ;
406434 stage . addChild ( blockSprite ) ;
407435 cells [ y ] [ x ] = {
408- block : Block . switchingBlockON ,
436+ block : switchONBlock ,
409437 switchId,
410438 objectId : undefined ,
411439 } ;
412440 vprev . sprite = blockSprite ;
413- vprev . block = Block . switchingBlockON ;
441+ vprev . block = switchONBlock ;
414442 vprev . dy = 0 ;
415443 vprev . vy = 0 ;
416444 }
417445 // switchingBlockONがOFFに切り替わるとき
418- else if ( vprev ?. block === Block . switchingBlockON ) {
419- if ( cNewCell . block !== Block . switchingBlockOFF ) {
446+ else if ( vprev ?. block === Block . switchingBlockON || vprev ?. block === Block . inverseSwitchingBlockON ) {
447+ if ( cNewCell . block !== Block . switchingBlockOFF && cNewCell . block !== Block . inverseSwitchingBlockOFF ) {
420448 console . warn ( "No block other than switchingBlockOFF cannot replace the switchingBlockON" ) ;
421449 return ;
422450 }
423451 assert (
424- cprev . block === Block . switchingBlockOFF || cprev . block === Block . switchingBlockON ,
452+ cprev . block === Block . switchingBlockOFF ||
453+ cprev . block === Block . switchingBlockON ||
454+ cprev . block === Block . inverseSwitchingBlockOFF ||
455+ cprev . block === Block . inverseSwitchingBlockON ,
425456 "block is not switchingBlock" ,
426457 ) ;
458+ const inversed = vprev . block === Block . inverseSwitchingBlockON ;
459+ const switchOFFBlock = inversed ? Block . inverseSwitchingBlockOFF : Block . switchingBlockOFF ;
427460 const switchId = cprev . switchId ;
428461 if ( ! switchId ) throw new Error ( "switchId is undefined" ) ;
429- const blockSprite = createSprite ( blockSize , Block . switchingBlockOFF , x , y , marginY , switchColor ( switchId ) ) ;
462+ const blockSprite = createSprite ( blockSize , switchOFFBlock , x , y , marginY , switchColor ( switchId ) ) ;
430463 stage . addChild ( blockSprite ) ;
431464 cells [ y ] [ x ] = {
432- block : Block . switchingBlockOFF ,
465+ block : switchOFFBlock ,
433466 switchId,
434467 objectId : undefined ,
435468 } ;
436469 vprev . sprite = blockSprite ;
437- vprev . block = Block . switchingBlockOFF ;
470+ vprev . block = switchOFFBlock ;
438471 vprev . dy = 0 ;
439472 vprev . vy = 0 ;
440473 } else {
@@ -507,6 +540,19 @@ export class Grid {
507540 tick ( cx : Context , ticker : Ticker ) {
508541 const { blockSize, gridX, gridY, marginY } = get ( cx . config ) ;
509542 const cells = get ( cx . state ) . cells ;
543+
544+ for ( const s of this . oobFallableSprites ) {
545+ if ( s . sprite ) {
546+ s . sprite . y += s . vy * ticker . deltaTime ;
547+ s . vy += consts . gravity * blockSize * ticker . deltaTime ;
548+ if ( s . sprite . y > gridY * blockSize + marginY * 2 ) {
549+ // 画面外に出たら消す
550+ s . sprite . parent ?. removeChild ( s . sprite ) ;
551+ s . sprite = null ;
552+ }
553+ }
554+ }
555+
510556 for ( let y = gridY - 1 ; y >= 0 ; y -- ) {
511557 for ( let x = 0 ; x < gridX ; x ++ ) {
512558 const vcell = this . __vsom [ y ] [ x ] ;
@@ -522,14 +568,20 @@ export class Grid {
522568 }
523569
524570 let swapDiff = 0 ;
571+ let goOOB = false ;
525572 while ( swapDiff * blockSize <= vcell . dy ) {
526573 // 下にブロックがあるなどの要因で止まる
527574 if ( ! isAvail ( cells , x , y + swapDiff + 1 ) ) break ;
528575 vcell . dy -= blockSize ;
529576 swapDiff ++ ;
577+ if ( y + swapDiff >= cells . length ) {
578+ goOOB = true ;
579+ break ;
580+ }
530581 }
582+
531583 // これ以上下に行けない
532- if ( ! isAvail ( cells , x , y + swapDiff ) ) {
584+ if ( ! goOOB && ! isAvail ( cells , x , y + swapDiff ) ) {
533585 // 着地 (dy はたいてい 0 未満なので、別で判定が必要)
534586 if ( vcell . dy >= 0 ) {
535587 vcell . dy = 0 ;
@@ -538,7 +590,18 @@ export class Grid {
538590 }
539591
540592 vcell . sprite . y = y * blockSize + marginY + vcell . dy ;
541- if ( swapDiff > 0 ) {
593+
594+ if ( goOOB ) {
595+ // vcell.sprite はsetBlockで消えてしまうので、あたらしく作る
596+ const oobSprite = createSprite ( blockSize , Block . fallable , x , y , marginY ) ;
597+ oobSprite . y = ( y + swapDiff ) * blockSize + marginY + vcell . dy ;
598+ cx . _stage_container . addChild ( oobSprite ) ;
599+ this . oobFallableSprites . push ( {
600+ sprite : oobSprite ,
601+ vy : vcell . vy ,
602+ } ) ;
603+ this . setBlock ( cx , x , y , { block : null } ) ;
604+ } else if ( swapDiff > 0 ) {
542605 this . setBlock ( cx , x , y , { block : null } ) ;
543606 this . setBlock ( cx , x , y + swapDiff , ccell ) ;
544607 const vSwapCell = this . __vsom [ y + swapDiff ] [ x ] ;
@@ -561,8 +624,7 @@ function isAvail(cells: GridCell[][], x: number, y: number) {
561624 switch ( true ) {
562625 case y >= cells . length :
563626 // 床が抜けている
564- // どうする?
565- return false ;
627+ return true ;
566628 case cell . block === null || cell . block === Block . switch :
567629 // 下にブロックがない
568630 return true ;
@@ -609,6 +671,8 @@ export function createCellsFromStageDefinition(stageDefinition: StageDefinition)
609671 }
610672 // switches
611673 case Block . switch :
674+ case Block . inverseSwitchingBlockON :
675+ case Block . inverseSwitchingBlockOFF :
612676 case Block . switchingBlockON :
613677 case Block . switchingBlockOFF : {
614678 const group = stageDefinition . switchGroups . find ( ( b ) => b . x === x && b . y === y ) ;
@@ -681,13 +745,15 @@ function createSprite(
681745 updateSprite ( switchBaseSprite , blockSize , x , y , marginY , 0 ) ;
682746 return switchBaseSprite ;
683747 }
748+ case Block . inverseSwitchingBlockON :
684749 case Block . switchingBlockOFF : {
685750 const sprite = new Sprite ( rockTexture ) ;
686751 if ( switchColor ) sprite . tint = switchColor ;
687752 else sprite . tint = 0xffa500 ;
688753 updateSprite ( sprite , blockSize , x , y , marginY , 0 ) ;
689754 return sprite ;
690755 }
756+ case Block . inverseSwitchingBlockOFF :
691757 case Block . switchingBlockON : {
692758 const sprite = new Sprite ( rockTexture ) ;
693759 if ( switchColor ) sprite . tint = switchColor ;
0 commit comments