@@ -15,13 +15,18 @@ export type AbilityEnableOptions = {
1515 cut : boolean ;
1616} ;
1717type History = {
18- at : { x : number ; y : number } ;
19- from : MovableObject | Block ;
20- to : MovableObject | Block ;
21- inventory : {
22- before : MovableObject | null ;
23- after : MovableObject | null ;
24- } ;
18+ playerX : number ;
19+ playerY : number ;
20+ playerFacing : Facing ;
21+ inventory : MovableObject | null ;
22+ movableBlocks : {
23+ x : number ;
24+ y : number ;
25+ objectId : number ;
26+ // 基準ブロックからの相対位置
27+ relativeX : number ;
28+ relativeY : number ;
29+ } [ ] ;
2530} ;
2631
2732function isMovableObject ( obj : MovableObject | Block ) : obj is MovableObject {
@@ -66,13 +71,28 @@ export class AbilityControl {
6671 const y = this . focused . y ;
6772 const target = cx . grid . getBlock ( x , y ) ;
6873 if ( ! target || target !== Block . movable ) return ;
69- const movableObject = cx . grid . getMovableObject ( x , y ) ;
74+ const movableObject = cx . grid . getMovableObject ( x , y , cx ) ;
7075 if ( ! movableObject ) return ;
7176 this . inventory = movableObject ;
77+ // cx.gridとinventryは重複しないように
78+ cx . grid . movableBlocks = cx . grid . movableBlocks . filter (
79+ ( block ) => block . objectId !== movableObject . objectId ,
80+ ) ;
7281 }
73- paste ( cx : Context , facing : Facing ) {
82+ paste ( cx : Context , facing : Facing , playerAt : Coords ) {
7483 if ( ! this . focused ) return ;
7584 if ( ! this . inventory /*|| this.inventory === Block.air*/ ) return ;
85+ const objectId = this . inventory . objectId ;
86+
87+ this . pushHistory ( {
88+ playerX : playerAt . x ,
89+ playerY : playerAt . y ,
90+ inventory : this . inventory ? this . inventory : null ,
91+ playerFacing : facing ,
92+ movableBlocks : cx . grid . movableBlocks ,
93+ } ) ;
94+
95+ // 左向きのときにブロックを配置する位置を変更するのに使用
7696 const width =
7797 this . inventory . relativePositions . reduce (
7898 ( acc , i ) => Math . max ( acc , i . x ) ,
@@ -107,93 +127,112 @@ export class AbilityControl {
107127 }
108128
109129 this . pushHistory ( {
110- at : { ...this . focused } ,
111- from : Block . air ,
112- to : prevInventory ,
113- inventory : {
114- before : prevInventory ,
115- after : this . inventory ,
116- } ,
130+ playerX : playerAt . x ,
131+ playerY : playerAt . y ,
132+ inventory : this . inventory ? this . inventory : null ,
133+ playerFacing : facing ,
134+ movableBlocks : cx . grid . movableBlocks ,
117135 } ) ;
118136 }
119- cut ( cx : Context ) {
137+ cut ( cx : Context , facing : Facing , playerAt : Coords ) {
120138 if ( ! this . focused ) return ;
139+
140+ this . pushHistory ( {
141+ playerX : playerAt . x ,
142+ playerY : playerAt . y ,
143+ inventory : this . inventory ? this . inventory : null ,
144+ playerFacing : facing ,
145+ movableBlocks : cx . grid . movableBlocks ,
146+ } ) ;
147+ console . log ( this . history ) ;
148+
121149 const x = this . focused . x ;
122150 const y = this . focused . y ;
123151 const target = cx . grid . getBlock ( x , y ) ;
124152 // removable 以外はカットできない
125153 if ( ! target || target !== Block . movable ) return ;
126- const movableObject = cx . grid . getMovableObject ( x , y ) ;
154+ const movableObject = cx . grid . getMovableObject ( x , y , cx ) ;
127155 if ( ! movableObject ) return ;
128- const prevInventory = this . inventory ;
156+ // const prevInventory = this.inventory;
129157 this . inventory = movableObject ;
130-
158+ // cx.gridとinventryは重複しないように
159+ cx . grid . movableBlocks = cx . grid . movableBlocks . filter (
160+ ( block ) => block . objectId !== movableObject . objectId ,
161+ ) ;
131162 for ( const i of movableObject . relativePositions ) {
132163 const positionX = movableObject . x + i . x ;
133164 const positionY = movableObject . y + i . y ;
134- cx . grid . setBlock ( cx , positionX , positionY , Block . air ) ;
165+ cx . grid . setBlock ( positionX , positionY , Block . air ) ;
135166 }
136167
137- // cx.grid.setBlock(cx, this.focused.x, this.focused.y, Block.air);
138-
139168 this . pushHistory ( {
140- at : { ...this . focused } ,
141- from : target ,
142- to : Block . air ,
143- inventory : {
144- before : prevInventory ,
145- after : movableObject ,
146- } ,
169+ playerX : playerAt . x ,
170+ playerY : playerAt . y ,
171+ inventory : this . inventory ? this . inventory : null ,
172+ playerFacing : facing ,
173+ movableBlocks : cx . grid . movableBlocks ,
147174 } ) ;
148175 }
149176
150177 // History については、 `docs/history-stack.png` を参照のこと
151178 pushHistory ( h : History ) {
152179 this . history = this . history . slice ( 0 , this . historyIndex ) ;
153- this . history . push ( h ) ;
180+ this . history . push ( JSON . parse ( JSON . stringify ( h ) ) ) ;
154181 this . historyIndex = this . history . length ;
155182 console . log ( `history: ${ this . historyIndex } / ${ this . history . length } ` ) ;
156183 }
157184 undo ( cx : Context ) {
158185 if ( this . historyIndex <= 0 ) return ;
159186 this . historyIndex -- ; // undo は、巻き戻し後の index で計算する
160187 const op = this . history [ this . historyIndex ] ;
161- if ( ! isMovableObject ( op . from ) ) {
162- cx . grid . setBlock ( cx , op . at . x , op . at . y , op . from ) ;
163- } else {
164- cx . grid . setMovableObject ( cx , op . at . x , op . at . y , op . from ) ;
165- }
166- this . inventory = op . inventory . before ;
188+
189+ // すべてのオブジェクトを削除
190+ cx . grid . clearAllMovableBlocks ( ) ;
191+
192+ // オブジェクトを配置
193+ this . inventory = op . inventory
194+ ? JSON . parse ( JSON . stringify ( op . inventory ) )
195+ : null ;
196+ cx . grid . movableBlocks = JSON . parse ( JSON . stringify ( op . movableBlocks ) ) ;
197+ cx . grid . setAllMovableBlocks ( cx ) ;
198+
167199 console . log ( `history: ${ this . historyIndex } / ${ this . history . length } ` ) ;
168200 }
169201 redo ( cx : Context ) {
170202 if ( this . historyIndex >= this . history . length ) return ;
171203 const op = this . history [ this . historyIndex ] ;
172204 this . historyIndex ++ ; // redo は、巻き戻し前の index
173- this . inventory = op . inventory . after ;
174- if ( ! isMovableObject ( op . to ) ) {
175- cx . grid . setBlock ( cx , op . at . x , op . at . y , op . to ) ;
176- } else {
177- cx . grid . setMovableObject ( cx , op . at . x , op . at . y , op . to ) ;
178- }
205+
206+ // すべてのオブジェクトを削除
207+ cx . grid . clearAllMovableBlocks ( ) ;
208+
209+ // オブジェクトを配置
210+ this . inventory = op . inventory
211+ ? JSON . parse ( JSON . stringify ( op . inventory ) )
212+ : null ;
213+ cx . grid . movableBlocks = JSON . parse ( JSON . stringify ( op . movableBlocks ) ) ;
214+ cx . grid . setAllMovableBlocks ( cx ) ;
215+
179216 console . log ( `history: ${ this . historyIndex } / ${ this . history . length } ` ) ;
180217 }
181218 handleKeyDown (
182219 cx : Context ,
183220 e : KeyboardEvent ,
184221 onGround : boolean ,
185222 facing : Facing ,
223+ history : History [ ] ,
224+ playerAt : Coords ,
186225 ) {
187226 if ( ! ( e . ctrlKey || e . metaKey ) ) return ;
188227
189228 if ( this . enabled . paste && onGround && e . key === "v" ) {
190- this . paste ( cx , facing ) ;
229+ this . paste ( cx , facing , playerAt ) ;
191230 }
192231 if ( this . enabled . copy && onGround && e . key === "c" ) {
193232 this . copy ( cx ) ;
194233 }
195234 if ( this . enabled . cut && onGround && e . key === "x" ) {
196- this . cut ( cx ) ;
235+ this . cut ( cx , facing , playerAt ) ;
197236 }
198237 if ( e . key === "z" ) {
199238 this . undo ( cx ) ;
0 commit comments