Skip to content

Commit 9b6cbe1

Browse files
committed
fix: ctrl Y properly works now
1 parent 6fdae23 commit 9b6cbe1

File tree

3 files changed

+105
-106
lines changed

3 files changed

+105
-106
lines changed

src/ability.ts

Lines changed: 34 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@ import { get } from "svelte/store";
22
import { Block, Facing } from "./constants.ts";
33
import { printCells } from "./grid.ts";
44
import { createSnapshot } from "./history.ts";
5+
import * as History from "./history.ts";
56
import type {
67
AbilityInit,
78
Context,
89
Coords,
910
MovableObject,
1011
} from "./public-types.ts";
11-
import type { StateSnapshot } from "./public-types.ts";
1212

1313
export function init(cx: Context, options?: AbilityInit) {
1414
cx.state.update((prev) => ({
@@ -37,29 +37,24 @@ export function init(cx: Context, options?: AbilityInit) {
3737
e.preventDefault();
3838
if (get(cx.state).usage.paste > 0 && onGround) paste(cx);
3939
});
40-
// TODO: use proper event names, because it won't work
41-
document.addEventListener("undo", (e) => {
42-
e.preventDefault();
43-
undo(cx);
44-
});
45-
document.addEventListener("redo", (e) => {
46-
e.preventDefault();
47-
redo(cx);
40+
41+
// there is no such thing as undo / redo event
42+
document.addEventListener("keydown", (e) => {
43+
if (!e.ctrlKey && !e.metaKey) return;
44+
switch (e.key) {
45+
case "z":
46+
e.preventDefault();
47+
History.undo(cx);
48+
break;
49+
case "y":
50+
e.preventDefault();
51+
History.redo(cx);
52+
break;
53+
}
4854
});
49-
const ss = createSnapshot(cx);
50-
const cfg = get(cx.config);
51-
ss.playerX = cfg.initialPlayerX;
52-
ss.playerY = cfg.initialPlayerY;
53-
console.log(ss);
54-
pushHistory(cx, ss);
55+
History.record(cx);
5556
}
5657

57-
export function setInventory(cx: Context, inventory: MovableObject | null) {
58-
cx.state.update((prev) => ({
59-
...prev,
60-
inventory: structuredClone(inventory),
61-
}));
62-
}
6358
export function focusCoord(playerAt: Coords, facing: Facing) {
6459
let dx: number;
6560
switch (facing) {
@@ -89,14 +84,14 @@ export function copy(cx: Context) {
8984
const movableObject = cx.grid.getMovableObject(x, y);
9085
if (!movableObject) return;
9186

92-
pushHistory(cx, createSnapshot(cx));
87+
History.record(cx);
9388

94-
// コピー元とは別のオブジェクトとして管理する (これ必要?)
95-
movableObject.objectId = self.crypto.randomUUID();
96-
97-
setInventory(cx, movableObject);
89+
cx.state.update((prev) => {
90+
prev.inventory = movableObject;
91+
return prev;
92+
});
9893

99-
pushHistory(cx, createSnapshot(cx));
94+
History.record(cx);
10095
}
10196
export function paste(cx: Context) {
10297
const state = get(cx.state);
@@ -126,14 +121,17 @@ export function paste(cx: Context) {
126121
}
127122
}
128123

129-
pushHistory(cx, createSnapshot(cx));
124+
History.record(cx);
130125
placeMovableObject(cx, x, y, inventory);
131126
if (!get(cx.state).inventoryIsInfinite) {
132-
setInventory(cx, null);
127+
cx.state.update((prev) => {
128+
prev.inventory = null;
129+
return prev;
130+
});
133131
}
134132

135133
printCells(createSnapshot(cx).game.cells, "paste");
136-
pushHistory(cx, createSnapshot(cx));
134+
History.record(cx);
137135
}
138136
export function cut(cx: Context) {
139137
const { focus } = cx.dynamic;
@@ -147,86 +145,18 @@ export function cut(cx: Context) {
147145
const movableObject = cx.grid.getMovableObject(x, y);
148146
if (!movableObject) return;
149147

150-
pushHistory(cx, createSnapshot(cx));
151-
setInventory(cx, movableObject);
148+
History.record(cx);
152149

150+
cx.state.update((prev) => {
151+
prev.inventory = movableObject;
152+
return prev;
153+
});
153154
cx.grid.update(cx, (prev) =>
154155
prev.objectId === movableObject.objectId ? { block: Block.air } : prev,
155156
);
156157

157158
printCells(createSnapshot(cx).game.cells, "cut");
158-
pushHistory(cx, createSnapshot(cx));
159-
}
160-
161-
// History については、 `docs/history-stack.png` を参照のこと
162-
export function pushHistory(cx: Context, h: StateSnapshot) {
163-
printCells(h.game.cells, "pushHistory");
164-
cx.history.update((prev) => {
165-
if (prev.tree.length > prev.index + 1) {
166-
// undo した後に pushHistory をする場合、履歴を切り詰める
167-
prev.tree.length = prev.index + 1;
168-
}
169-
prev.tree.push(h);
170-
prev.index = prev.tree.length - 1;
171-
return prev;
172-
});
173-
}
174-
function undo(cx: Context): { x: number; y: number } | undefined {
175-
const history = get(cx.history);
176-
if (history.index <= 0) return undefined;
177-
const grid = cx.grid;
178-
history.index--;
179-
const snapshot = history.tree[history.index];
180-
printCells(snapshot.game.cells);
181-
182-
// オブジェクトを配置
183-
setInventory(cx, snapshot.game.inventory);
184-
grid.diffAndUpdateTo(cx, snapshot.game.cells);
185-
printCells(snapshot.game.cells, "undo");
186-
cx.state.update((prev) => {
187-
return {
188-
...prev,
189-
usage: snapshot.game.usage,
190-
inventory: snapshot.game.inventory,
191-
inventoryIsInfinite: snapshot.game.inventoryIsInfinite,
192-
};
193-
});
194-
cx.dynamic.player.x = snapshot.playerX;
195-
cx.dynamic.player.y = snapshot.playerY;
196-
197-
cx.history.set(history);
198-
199-
console.log(`history: ${history.index} / ${history.tree.length - 1}`);
200-
return {
201-
x: history.tree[history.index].playerX,
202-
y: history.tree[history.index].playerY,
203-
};
204-
}
205-
206-
function redo(cx: Context): { x: number; y: number } | undefined {
207-
// TODO: プレイヤーの座標の履歴を「いい感じ」にするため、 history を二重管理する
208-
const history = get(cx.history);
209-
if (history.index >= history.tree.length - 1) return undefined;
210-
const snapshot = history.tree[history.index];
211-
printCells(snapshot.game.cells, "redo");
212-
history.index++; // redo は、巻き戻し前の index
213-
const grid = cx.grid;
214-
215-
// 状態を巻き戻す
216-
grid.diffAndUpdateTo(cx, snapshot.game.cells);
217-
setInventory(cx, snapshot.game.inventory);
218-
219-
cx.state.update((prev) => ({
220-
...prev,
221-
usage: snapshot.game.usage,
222-
}));
223-
224-
console.log(`history: ${history.index} / ${history.tree.length - 1}`);
225-
cx.history.set(history);
226-
return {
227-
x: history.tree[history.index].playerX,
228-
y: history.tree[history.index].playerY,
229-
};
159+
History.record(cx);
230160
}
231161

232162
export function placeMovableObject(

src/history.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,68 @@
11
import { get } from "svelte/store";
2+
import { printCells } from "./grid.ts";
23
import type { Context, StateSnapshot } from "./public-types.ts";
34

5+
// History については、 `docs/history-stack.png` を参照のこと
6+
export function record(cx: Context) {
7+
const h = createSnapshot(cx);
8+
cx.history.update((prev) => {
9+
if (prev.tree.length > prev.index + 1) {
10+
// undo した後に record をする場合、履歴を切り詰める
11+
prev.tree.length = prev.index + 1;
12+
}
13+
prev.tree.push(h);
14+
prev.index = prev.tree.length - 1;
15+
return prev;
16+
});
17+
}
18+
export function undo(cx: Context): { x: number; y: number } | undefined {
19+
const history = get(cx.history);
20+
if (history.index <= 0) return undefined;
21+
const grid = cx.grid;
22+
history.index--;
23+
const snapshot = history.tree[history.index];
24+
25+
printCells(snapshot.game.cells, "undo");
26+
if (history.index === 0) {
27+
console.log("undo to last", snapshot);
28+
}
29+
30+
// 状態を巻き戻す
31+
cx.state.set(snapshot.game);
32+
grid.diffAndUpdateTo(cx, snapshot.game.cells);
33+
34+
cx.dynamic.player.x = snapshot.playerX;
35+
cx.dynamic.player.y = snapshot.playerY;
36+
37+
cx.history.set(history);
38+
console.log(`history: ${history.index} / ${history.tree.length - 1}`);
39+
return {
40+
x: history.tree[history.index].playerX,
41+
y: history.tree[history.index].playerY,
42+
};
43+
}
44+
45+
export function redo(cx: Context): { x: number; y: number } | undefined {
46+
// TODO: プレイヤーの座標の履歴を「いい感じ」にするため、 history を二重管理する
47+
const history = get(cx.history);
48+
if (history.index >= history.tree.length - 1) return undefined;
49+
history.index++; // redo は、巻き戻し前の index
50+
const snapshot = history.tree[history.index];
51+
printCells(snapshot.game.cells, "redo");
52+
const grid = cx.grid;
53+
54+
// 状態を巻き戻す
55+
cx.state.set(snapshot.game);
56+
grid.diffAndUpdateTo(cx, snapshot.game.cells);
57+
58+
console.log(`history: ${history.index} / ${history.tree.length - 1}`);
59+
cx.history.set(history);
60+
return {
61+
x: history.tree[history.index].playerX,
62+
y: history.tree[history.index].playerY,
63+
};
64+
}
65+
466
export function createSnapshot(cx: Context): StateSnapshot {
567
const game = get(cx.state);
668
const playerX = cx.dynamic.player.x;

src/player.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,17 @@ export function init(
1919
sprite.anchor.set(0.5, 1);
2020
const { blockSize, initialPlayerX, initialPlayerY, marginY } = get(cx.config);
2121

22-
sprite.x = blockSize * initialPlayerX;
23-
sprite.y = blockSize * initialPlayerY + marginY;
22+
const coords = {
23+
x: blockSize * initialPlayerX,
24+
y: blockSize * initialPlayerY + marginY,
25+
};
26+
27+
sprite.x = coords.x;
28+
sprite.y = coords.y;
2429
sprite.width = consts.playerWidth * blockSize;
2530
sprite.height = consts.playerHeight * blockSize;
31+
cx.dynamic.player.x = coords.x;
32+
cx.dynamic.player.y = coords.y;
2633
cx._stage_container.addChild(sprite);
2734

2835
// Move the sprite to the center of the screen

0 commit comments

Comments
 (0)