4fa4f13558
Co-authored-by: iamlukethedev <iamlukethedev@users.noreply.github.com>
150 lines
4.6 KiB
TypeScript
150 lines
4.6 KiB
TypeScript
import type Phaser from "phaser";
|
|
|
|
import type { OfficeSceneBridge } from "@/features/office/phaser/OfficeSceneBridge";
|
|
|
|
type OfficeBuilderRenderable =
|
|
| Phaser.GameObjects.Rectangle
|
|
| Phaser.GameObjects.Image;
|
|
|
|
export const createOfficeBuilderScene = (params: {
|
|
PhaserLib: typeof import("phaser");
|
|
bridge: OfficeSceneBridge;
|
|
onObjectMoved?: (id: string, x: number, y: number) => void;
|
|
onSelectionChange?: (ids: string[]) => void;
|
|
}): Phaser.Scene => {
|
|
const { PhaserLib, bridge, onObjectMoved, onSelectionChange } = params;
|
|
|
|
class BuilderScene extends PhaserLib.Scene {
|
|
private unsubscribe: (() => void) | null = null;
|
|
private layer = new Map<string, OfficeBuilderRenderable>();
|
|
private selected = new Set<string>();
|
|
private dragId: string | null = null;
|
|
|
|
constructor() {
|
|
super("office-builder-scene");
|
|
}
|
|
|
|
create() {
|
|
this.cameras.main.setBackgroundColor("#0f1a26");
|
|
this.unsubscribe = bridge.subscribe(() => {
|
|
this.renderMap();
|
|
});
|
|
this.renderMap();
|
|
this.input.on("pointerdown", (pointer: Phaser.Input.Pointer) => {
|
|
const target = this.pickObject(pointer.worldX, pointer.worldY);
|
|
const shiftPressed = Boolean(
|
|
pointer.event &&
|
|
typeof pointer.event === "object" &&
|
|
"shiftKey" in pointer.event &&
|
|
(pointer.event as { shiftKey?: unknown }).shiftKey === true,
|
|
);
|
|
if (!target) {
|
|
this.selected.clear();
|
|
onSelectionChange?.([]);
|
|
return;
|
|
}
|
|
if (shiftPressed) {
|
|
if (this.selected.has(target.id)) {
|
|
this.selected.delete(target.id);
|
|
} else {
|
|
this.selected.add(target.id);
|
|
}
|
|
} else {
|
|
this.selected.clear();
|
|
this.selected.add(target.id);
|
|
}
|
|
this.dragId = target.id;
|
|
onSelectionChange?.([...this.selected]);
|
|
this.renderMap();
|
|
});
|
|
this.input.on("pointermove", (pointer: Phaser.Input.Pointer) => {
|
|
if (!pointer.isDown || !this.dragId) return;
|
|
const nextX = Math.round(pointer.worldX / 16) * 16;
|
|
const nextY = Math.round(pointer.worldY / 16) * 16;
|
|
onObjectMoved?.(this.dragId, nextX, nextY);
|
|
});
|
|
this.input.on("pointerup", () => {
|
|
this.dragId = null;
|
|
});
|
|
}
|
|
|
|
shutdown() {
|
|
this.unsubscribe?.();
|
|
this.unsubscribe = null;
|
|
for (const item of this.layer.values()) {
|
|
item.destroy();
|
|
}
|
|
this.layer.clear();
|
|
this.selected.clear();
|
|
}
|
|
|
|
private renderMap() {
|
|
const state = bridge.getState();
|
|
const keep = new Set<string>();
|
|
for (const object of state.map.objects) {
|
|
keep.add(object.id);
|
|
const existing = this.layer.get(object.id);
|
|
if (existing) {
|
|
// Cast to common interface for transform
|
|
const transform =
|
|
existing as unknown as Phaser.GameObjects.Components.Transform;
|
|
transform.setPosition(object.x, object.y);
|
|
transform.setAngle(object.rotation);
|
|
|
|
if (existing instanceof PhaserLib.GameObjects.Rectangle) {
|
|
existing.setSize(32, 32);
|
|
existing.setStrokeStyle(
|
|
this.selected.has(object.id) ? 2 : 0,
|
|
0x79e5ff,
|
|
1,
|
|
);
|
|
existing.setFillStyle(0x4f80af, 0.95);
|
|
} else if (existing instanceof PhaserLib.GameObjects.Image) {
|
|
existing.setAlpha(this.selected.has(object.id) ? 0.8 : 1);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
let sprite: OfficeBuilderRenderable;
|
|
if (object.assetId === "office_bg") {
|
|
const img = this.add.image(object.x, object.y, "office_bg");
|
|
img.setOrigin(0.5, 0.5);
|
|
sprite = img;
|
|
} else {
|
|
const rect = this.add.rectangle(
|
|
object.x,
|
|
object.y,
|
|
32,
|
|
32,
|
|
0x4f80af,
|
|
0.95,
|
|
);
|
|
rect.setOrigin(0.5, 0.5);
|
|
sprite = rect;
|
|
}
|
|
|
|
sprite.setDepth(object.zIndex);
|
|
this.layer.set(object.id, sprite);
|
|
}
|
|
for (const [id, item] of this.layer) {
|
|
if (keep.has(id)) continue;
|
|
item.destroy();
|
|
this.layer.delete(id);
|
|
}
|
|
}
|
|
|
|
private pickObject(x: number, y: number) {
|
|
const map = bridge.getState().map;
|
|
for (let index = map.objects.length - 1; index >= 0; index -= 1) {
|
|
const object = map.objects[index];
|
|
if (Math.abs(x - object.x) <= 16 && Math.abs(y - object.y) <= 16) {
|
|
return object;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
|
|
return new BuilderScene();
|
|
};
|