Skip to content

Commit 96fc8c1

Browse files
committed
historyは状態を全て記録するように変更
1 parent 8074e80 commit 96fc8c1

File tree

4 files changed

+187
-83
lines changed

4 files changed

+187
-83
lines changed

src/ability.ts

Lines changed: 84 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,18 @@ export type AbilityEnableOptions = {
1515
cut: boolean;
1616
};
1717
type 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

2732
function 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);

src/grid.ts

Lines changed: 76 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ export class Grid {
7676
block,
7777
sprite,
7878
objectId: movableBlock.objectId,
79-
// 同グループの基準ブロックに対する相対位置
79+
// 同オブジェクトの基準ブロックに対する相対位置
8080
relativePosition: {
8181
x: movableBlock.relativeX,
8282
y: movableBlock.relativeY,
@@ -106,34 +106,35 @@ export class Grid {
106106
getBlock(x: number, y: number): Block | undefined {
107107
return this.cells[y]?.[x]?.block;
108108
}
109-
getMovableObject(x: number, y: number): MovableObject | undefined {
109+
getMovableObject(
110+
x: number,
111+
y: number,
112+
cx: Context,
113+
): MovableObject | undefined {
110114
const cell = this.cells[y]?.[x];
111115
if (!cell) return undefined;
112-
if (cell.block === Block.movable) {
113-
const objectId = cell.objectId;
114-
const retrievedBlocks = this.movableBlocks.filter(
115-
(block) => block.objectId === objectId,
116-
);
117-
const retrievedObject: MovableObject = {
118-
objectId,
119-
x: retrievedBlocks.filter(
120-
(block) => block.relativeX === 0 && block.relativeY === 0,
121-
)[0].x,
122-
y: retrievedBlocks.filter(
123-
(block) => block.relativeX === 0 && block.relativeY === 0,
124-
)[0].y,
125-
relativePositions: retrievedBlocks.map((block) => ({
126-
x: block.relativeX,
127-
y: block.relativeY,
128-
})),
129-
};
130-
131-
console.log(retrievedObject);
116+
if (cell.block !== Block.movable) return undefined;
117+
const objectId = cell.objectId;
118+
const retrievedBlocks = this.movableBlocks.filter(
119+
(block) => block.objectId === objectId,
120+
);
121+
const retrievedObject: MovableObject = {
122+
objectId,
123+
x: retrievedBlocks.filter(
124+
(block) => block.relativeX === 0 && block.relativeY === 0,
125+
)[0].x,
126+
y: retrievedBlocks.filter(
127+
(block) => block.relativeX === 0 && block.relativeY === 0,
128+
)[0].y,
129+
relativePositions: retrievedBlocks.map((block) => ({
130+
x: block.relativeX,
131+
y: block.relativeY,
132+
})),
133+
};
132134

133-
return retrievedObject;
134-
}
135+
return retrievedObject;
135136
}
136-
setBlock(cx: Context, x: number, y: number, block: Block) {
137+
setBlock(x: number, y: number, block: Block) {
137138
const prev = this.cells[y][x];
138139
if (block === prev.block) return;
139140
if (prev.block !== Block.air) {
@@ -147,9 +148,6 @@ export class Grid {
147148
}
148149
}
149150
setMovableObject(cx: Context, x: number, y: number, object: MovableObject) {
150-
console.log("1");
151-
console.log(object.relativePositions);
152-
console.log(object, object);
153151
const prev = this.cells[y][x];
154152
if (prev.block !== Block.air) {
155153
this.stage.removeChild(prev.sprite);
@@ -174,20 +172,61 @@ export class Grid {
174172
y: i.y,
175173
},
176174
};
175+
this.movableBlocks.push({
176+
x: positionX,
177+
y: positionY,
178+
objectId: object.objectId,
179+
relativeX: i.x,
180+
relativeY: i.y,
181+
});
177182
}
183+
184+
// 座標基準で重複を削除
185+
this.movableBlocks = Array.from(
186+
new Map(this.movableBlocks.map((b) => [`${b.x},${b.y}`, b])).values(),
187+
);
188+
178189
// オブジェクトの座標を更新
179190
object.x = x;
180191
object.y = y;
181-
this.movableBlocks = this.movableBlocks.map((block) => {
182-
if (block.objectId === object.objectId) {
183-
return {
184-
...block,
185-
x,
186-
y,
187-
};
192+
}
193+
clearAllMovableBlocks() {
194+
for (const i of this.movableBlocks) {
195+
const prev = this.cells[i.y][i.x];
196+
if (prev.block !== Block.air) {
197+
this.stage.removeChild(prev.sprite);
188198
}
189-
return block;
190-
});
199+
this.setBlock(i.x, i.y, Block.air);
200+
}
201+
}
202+
setAllMovableBlocks(cx: Context) {
203+
const objectIds = Array.from(
204+
new Set(this.movableBlocks.map((block) => block.objectId)),
205+
);
206+
for (const objectId of objectIds) {
207+
const retrievedBlocks = this.movableBlocks.filter(
208+
(block) => block.objectId === objectId,
209+
);
210+
const movableObject: MovableObject = {
211+
objectId,
212+
x: retrievedBlocks.filter(
213+
(block) => block.relativeX === 0 && block.relativeY === 0,
214+
)[0].x,
215+
y: retrievedBlocks.filter(
216+
(block) => block.relativeX === 0 && block.relativeY === 0,
217+
)[0].y,
218+
relativePositions: retrievedBlocks.map((block) => ({
219+
x: block.relativeX,
220+
y: block.relativeY,
221+
})),
222+
};
223+
cx.grid.setMovableObject(
224+
cx,
225+
movableObject.x,
226+
movableObject.y,
227+
movableObject,
228+
);
229+
}
191230
}
192231
}
193232

0 commit comments

Comments
 (0)