Skip to content

Commit 7732e4f

Browse files
committed
複数ブロックからなるオブジェクトのcut,pasteに対応
1 parent 539357c commit 7732e4f

File tree

3 files changed

+243
-28
lines changed

3 files changed

+243
-28
lines changed

src/ability.ts

Lines changed: 49 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import { Block, Facing } from "./constants.ts";
1+
import { Block, Facing, type MovableBlock } from "./constants.ts";
22
import type { Context } from "./context.ts";
3+
import type { MovableBlocks, MovableObject } from "./grid.ts";
34

45
export type Coords = {
56
x: number;
@@ -15,17 +16,22 @@ export type AbilityEnableOptions = {
1516
};
1617
type History = {
1718
at: { x: number; y: number };
18-
from: Block;
19-
to: Block;
19+
from: MovableObject | Block;
20+
to: MovableObject | Block;
2021
inventory: {
21-
before: Block | null;
22-
after: Block | null;
22+
before: MovableObject | null;
23+
after: MovableObject | null;
2324
};
2425
};
26+
27+
function isMovableObject(obj: MovableObject | Block): obj is MovableObject {
28+
return (obj as MovableObject).objectId !== undefined;
29+
}
30+
2531
export class AbilityControl {
2632
history: History[] = [];
2733
historyIndex = 0;
28-
inventory: Block | null = null;
34+
inventory: MovableObject | null = null;
2935
inventoryIsInfinite = false;
3036
enabled: AbilityEnableOptions;
3137
focused: Coords | undefined;
@@ -56,17 +62,25 @@ export class AbilityControl {
5662
}
5763
copy(cx: Context) {
5864
if (!this.focused) return;
59-
const target = cx.grid.getBlock(this.focused.x, this.focused.y);
65+
const x = this.focused.x;
66+
const y = this.focused.y;
67+
const target = cx.grid.getBlock(x, y);
6068
if (!target || target !== Block.movable) return;
61-
this.inventory = target;
69+
const movableObject = cx.grid.getMovableObject(x, y);
70+
if (!movableObject) return;
71+
this.inventory = movableObject;
6272
}
6373
paste(cx: Context) {
6474
if (!this.focused) return;
65-
if (!this.inventory || this.inventory === Block.air) return;
75+
if (!this.inventory /*|| this.inventory === Block.air*/) return;
76+
const x = this.focused.x;
77+
const y = this.focused.y;
6678
const target = cx.grid.getBlock(this.focused.x, this.focused.y);
6779
if (!target || target !== Block.air) return;
6880
const prevInventory = this.inventory;
69-
cx.grid.setBlock(cx, this.focused.x, this.focused.y, this.inventory);
81+
82+
cx.grid.setMovableObject(cx, x, y, this.inventory);
83+
7084
if (!this.inventoryIsInfinite) {
7185
this.inventory = null;
7286
}
@@ -83,20 +97,31 @@ export class AbilityControl {
8397
}
8498
cut(cx: Context) {
8599
if (!this.focused) return;
86-
const target = cx.grid.getBlock(this.focused.x, this.focused.y);
100+
const x = this.focused.x;
101+
const y = this.focused.y;
102+
const target = cx.grid.getBlock(x, y);
87103
// removable 以外はカットできない
88104
if (!target || target !== Block.movable) return;
105+
const movableObject = cx.grid.getMovableObject(x, y);
106+
if (!movableObject) return;
89107
const prevInventory = this.inventory;
90-
this.inventory = target;
91-
cx.grid.setBlock(cx, this.focused.x, this.focused.y, Block.air);
108+
this.inventory = movableObject;
109+
110+
for (const i of movableObject.relativePositions) {
111+
const positionX = movableObject.x + i.x;
112+
const positionY = movableObject.y + i.y;
113+
cx.grid.setBlock(cx, positionX, positionY, Block.air);
114+
}
115+
116+
// cx.grid.setBlock(cx, this.focused.x, this.focused.y, Block.air);
92117

93118
this.pushHistory({
94119
at: { ...this.focused },
95120
from: target,
96121
to: Block.air,
97122
inventory: {
98123
before: prevInventory,
99-
after: target,
124+
after: movableObject,
100125
},
101126
});
102127
}
@@ -112,7 +137,11 @@ export class AbilityControl {
112137
if (this.historyIndex <= 0) return;
113138
this.historyIndex--; // undo は、巻き戻し後の index で計算する
114139
const op = this.history[this.historyIndex];
115-
cx.grid.setBlock(cx, op.at.x, op.at.y, op.from);
140+
if (!isMovableObject(op.from)) {
141+
cx.grid.setBlock(cx, op.at.x, op.at.y, op.from);
142+
} else {
143+
cx.grid.setMovableObject(cx, op.at.x, op.at.y, op.from);
144+
}
116145
this.inventory = op.inventory.before;
117146
console.log(`history: ${this.historyIndex} / ${this.history.length}`);
118147
}
@@ -121,7 +150,11 @@ export class AbilityControl {
121150
const op = this.history[this.historyIndex];
122151
this.historyIndex++; // redo は、巻き戻し前の index
123152
this.inventory = op.inventory.after;
124-
cx.grid.setBlock(cx, op.at.x, op.at.y, op.to);
153+
if (!isMovableObject(op.to)) {
154+
cx.grid.setBlock(cx, op.at.x, op.at.y, op.to);
155+
} else {
156+
cx.grid.setMovableObject(cx, op.at.x, op.at.y, op.to);
157+
}
125158
console.log(`history: ${this.historyIndex} / ${this.history.length}`);
126159
}
127160
handleKeyDown(cx: Context, e: KeyboardEvent, onGround: boolean) {

src/grid.ts

Lines changed: 124 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,53 @@ import type { StageDefinition } from "./stages.ts";
66

77
type GridCell =
88
| {
9-
block: Block.block | Block.movable;
9+
block: Block.block;
1010
sprite: Sprite;
1111
}
12+
| {
13+
block: Block.movable;
14+
sprite: Sprite;
15+
objectId: number;
16+
relativePosition: {
17+
x: number;
18+
y: number;
19+
};
20+
}
1221
| {
1322
block: Block.air;
1423
sprite: null;
1524
};
1625

26+
export type MovableBlocks = {
27+
x: number;
28+
y: number;
29+
objectId: number;
30+
// 基準ブロックからの相対位置
31+
relativeX: number;
32+
relativeY: number;
33+
}[];
34+
35+
export type MovableObject = {
36+
objectId: number;
37+
x: number;
38+
y: number;
39+
relativePositions: {
40+
x: number;
41+
y: number;
42+
}[];
43+
};
44+
1745
export class Grid {
1846
private stage: Container;
1947
cells: GridCell[][];
48+
movableBlocks: MovableBlocks;
2049
constructor(
2150
stage: Container,
2251
cellSize: number,
2352
stageDefinition: StageDefinition,
2453
) {
2554
this.stage = stage;
55+
this.movableBlocks = stageDefinition.movableBlocks;
2656
const cells: GridCell[][] = [];
2757
for (let y = 0; y < stageDefinition.stage.length; y++) {
2858
const rowDefinition = stageDefinition.stage[y].split("");
@@ -36,6 +66,23 @@ export class Grid {
3666
sprite: null,
3767
};
3868
row.push(cell);
69+
} else if (block === Block.movable) {
70+
const sprite = createSprite(cellSize, block, x, y);
71+
stage.addChild(sprite);
72+
const movableBlock = stageDefinition.movableBlocks.filter(
73+
(block) => block.x === x && block.y === y,
74+
)[0];
75+
const cell: GridCell = {
76+
block,
77+
sprite,
78+
objectId: movableBlock.objectId,
79+
// 同グループの基準ブロックに対する相対位置
80+
relativePosition: {
81+
x: movableBlock.relativeX,
82+
y: movableBlock.relativeY,
83+
},
84+
};
85+
row.push(cell);
3986
} else {
4087
const sprite = createSprite(cellSize, block, x, y);
4188
stage.addChild(sprite);
@@ -59,13 +106,39 @@ export class Grid {
59106
getBlock(x: number, y: number): Block | undefined {
60107
return this.cells[y]?.[x]?.block;
61108
}
62-
getMovableBlock(x: number, y: number): Block.movable | undefined {
109+
getMovableObject(x: number, y: number): MovableObject | undefined {
63110
const cell = this.cells[y]?.[x];
64111
if (!cell) return undefined;
65112
if (cell.block === Block.movable) {
66-
return cell.block;
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);
132+
133+
return retrievedObject;
67134
}
68135
}
136+
// setAirBlock(cx: Context, x: number, y: number) {
137+
// this.cells[y][x] = {
138+
// block: Block.air,
139+
// sprite: null,
140+
// }
141+
// }
69142
setBlock(cx: Context, x: number, y: number, block: Block) {
70143
const prev = this.cells[y][x];
71144
if (block === prev.block) return;
@@ -77,14 +150,58 @@ export class Grid {
77150
block,
78151
sprite: null,
79152
};
80-
} else {
81-
const sprite = createSprite(cx.blockSize, block, x, y);
153+
}
154+
// else {
155+
// const sprite = createSprite(cx.blockSize, block, x, y);
156+
// this.stage.addChild(sprite);
157+
// this.cells[y][x] = {
158+
// block,
159+
// sprite,
160+
// };
161+
// }
162+
}
163+
setMovableObject(cx: Context, x: number, y: number, object: MovableObject) {
164+
console.log("1");
165+
console.log(object.relativePositions);
166+
console.log(object, object);
167+
const prev = this.cells[y][x];
168+
if (prev.block !== Block.air) {
169+
this.stage.removeChild(prev.sprite);
170+
}
171+
for (const i of object.relativePositions) {
172+
console.log(i);
173+
const positionX = x + i.x;
174+
const positionY = y + i.y;
175+
const sprite = createSprite(
176+
cx.blockSize,
177+
Block.movable,
178+
positionX,
179+
positionY,
180+
);
82181
this.stage.addChild(sprite);
83-
this.cells[y][x] = {
84-
block,
182+
this.cells[positionY][positionX] = {
183+
block: Block.movable,
85184
sprite,
185+
objectId: object.objectId,
186+
relativePosition: {
187+
x: i.x,
188+
y: i.y,
189+
},
86190
};
87191
}
192+
// オブジェクトの座標を更新
193+
object.x = x;
194+
object.y = y;
195+
this.movableBlocks = this.movableBlocks.map((block) => {
196+
if (block.objectId === object.objectId) {
197+
return {
198+
...block,
199+
x,
200+
y,
201+
};
202+
}
203+
return block;
204+
});
88205
}
89206
}
90207

0 commit comments

Comments
 (0)