|
|
@ -2,15 +2,23 @@ import { BaseScene } from "@/scene/BaseScene"; |
|
|
import { SceneType } from "@/enums/SceneType"; |
|
|
import { SceneType } from "@/enums/SceneType"; |
|
|
import { ChaseGameModel } from "@/game/chase/model"; |
|
|
import { ChaseGameModel } from "@/game/chase/model"; |
|
|
import type { Difficulty, NodeId } from "@/game/chase/types"; |
|
|
import type { Difficulty, NodeId } from "@/game/chase/types"; |
|
|
import { Container, Graphics, Text, TextStyle } from "pixi.js"; |
|
|
import { Container, Graphics, Text, TextStyle, type Ticker } from "pixi.js"; |
|
|
|
|
|
|
|
|
const NODE_RADIUS = 18; |
|
|
const NODE_RADIUS = 18; |
|
|
const NODE_GAP = 58; |
|
|
const NODE_GAP = 58; |
|
|
|
|
|
|
|
|
type ChaseModelLike = Pick<ChaseGameModel, "getState" | "moveThief" | "newRound">; |
|
|
type ChaseModelLike = Pick<ChaseGameModel, "getState" | "moveThief" | "newRound">; |
|
|
|
|
|
type DocumentLike = { |
|
|
|
|
|
body: { |
|
|
|
|
|
appendChild: (node: HTMLInputElement) => void; |
|
|
|
|
|
contains: (node: HTMLInputElement) => boolean; |
|
|
|
|
|
}; |
|
|
|
|
|
createElement: (tag: string) => HTMLInputElement; |
|
|
|
|
|
}; |
|
|
type ChaseSceneOptions = { |
|
|
type ChaseSceneOptions = { |
|
|
model?: ChaseModelLike; |
|
|
model?: ChaseModelLike; |
|
|
modelFactory?: () => ChaseModelLike; |
|
|
modelFactory?: () => ChaseModelLike; |
|
|
|
|
|
documentRef?: DocumentLike; |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
export default class ChaseScene extends BaseScene { |
|
|
export default class ChaseScene extends BaseScene { |
|
|
@ -28,14 +36,17 @@ export default class ChaseScene extends BaseScene { |
|
|
}; |
|
|
}; |
|
|
private startButton = new Graphics(); |
|
|
private startButton = new Graphics(); |
|
|
private seedInputElement?: HTMLInputElement; |
|
|
private seedInputElement?: HTMLInputElement; |
|
|
|
|
|
private readonly documentRef?: DocumentLike; |
|
|
private selectedDifficulty: Difficulty = "normal"; |
|
|
private selectedDifficulty: Difficulty = "normal"; |
|
|
private seedInput = ""; |
|
|
private seedInput = ""; |
|
|
private difficultyLocked = false; |
|
|
private difficultyLocked = false; |
|
|
|
|
|
private inputAttached = false; |
|
|
|
|
|
|
|
|
constructor(options: ChaseSceneOptions = {}) { |
|
|
constructor(options: ChaseSceneOptions = {}) { |
|
|
super("chase", SceneType.Normal); |
|
|
super("chase", SceneType.Normal); |
|
|
this.model = options.model; |
|
|
this.model = options.model; |
|
|
this.modelFactory = options.modelFactory; |
|
|
this.modelFactory = options.modelFactory; |
|
|
|
|
|
this.documentRef = options.documentRef; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
protected async onSceneLayout(): Promise<void> { |
|
|
protected async onSceneLayout(): Promise<void> { |
|
|
@ -85,13 +96,17 @@ export default class ChaseScene extends BaseScene { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
protected onSceneEnter(): void { |
|
|
protected onSceneEnter(): void { |
|
|
this.attachSeedInputBridge(); |
|
|
this.syncSeedInputBridge(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
protected onSceneExit(): void { |
|
|
protected onSceneExit(): void { |
|
|
this.detachSeedInputBridge(); |
|
|
this.detachSeedInputBridge(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
update(_dt: number, _name: string, _ticker: Ticker): void { |
|
|
|
|
|
this.syncSeedInputBridge(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
public refreshView(): void { |
|
|
public refreshView(): void { |
|
|
if (!this.model || !this.hudText || !this.setupText) { |
|
|
if (!this.model || !this.hudText || !this.setupText) { |
|
|
return; |
|
|
return; |
|
|
@ -173,6 +188,15 @@ export default class ChaseScene extends BaseScene { |
|
|
this.refreshView(); |
|
|
this.refreshView(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public resetSetup(): void { |
|
|
|
|
|
this.difficultyLocked = false; |
|
|
|
|
|
this.seedInput = ""; |
|
|
|
|
|
if (this.seedInputElement) { |
|
|
|
|
|
this.seedInputElement.value = ""; |
|
|
|
|
|
} |
|
|
|
|
|
this.refreshView(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
private buildSetupControls(): void { |
|
|
private buildSetupControls(): void { |
|
|
this.setupLayer.removeChildren(); |
|
|
this.setupLayer.removeChildren(); |
|
|
const difficulties: Difficulty[] = ["easy", "normal", "hard"]; |
|
|
const difficulties: Difficulty[] = ["easy", "normal", "hard"]; |
|
|
@ -240,10 +264,11 @@ export default class ChaseScene extends BaseScene { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private attachSeedInputBridge(): void { |
|
|
private attachSeedInputBridge(): void { |
|
|
if (typeof document === "undefined" || this.seedInputElement) { |
|
|
const doc = this.resolveDocument(); |
|
|
|
|
|
if (!doc || this.seedInputElement || this.inputAttached) { |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
const input = document.createElement("input"); |
|
|
const input = doc.createElement("input"); |
|
|
input.type = "text"; |
|
|
input.type = "text"; |
|
|
input.placeholder = "Seed (optional)"; |
|
|
input.placeholder = "Seed (optional)"; |
|
|
input.value = this.seedInput; |
|
|
input.value = this.seedInput; |
|
|
@ -253,17 +278,31 @@ export default class ChaseScene extends BaseScene { |
|
|
input.style.zIndex = "10"; |
|
|
input.style.zIndex = "10"; |
|
|
input.style.width = "180px"; |
|
|
input.style.width = "180px"; |
|
|
input.addEventListener("input", this.handleSeedInputEvent); |
|
|
input.addEventListener("input", this.handleSeedInputEvent); |
|
|
document.body.appendChild(input); |
|
|
doc.body.appendChild(input); |
|
|
this.seedInputElement = input; |
|
|
this.seedInputElement = input; |
|
|
|
|
|
this.inputAttached = true; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private detachSeedInputBridge(): void { |
|
|
private detachSeedInputBridge(): void { |
|
|
if (!this.seedInputElement) { |
|
|
if (!this.seedInputElement) { |
|
|
|
|
|
this.inputAttached = false; |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
this.seedInputElement.removeEventListener("input", this.handleSeedInputEvent); |
|
|
this.seedInputElement.removeEventListener("input", this.handleSeedInputEvent); |
|
|
this.seedInputElement.remove(); |
|
|
this.seedInputElement.remove(); |
|
|
this.seedInputElement = undefined; |
|
|
this.seedInputElement = undefined; |
|
|
|
|
|
this.inputAttached = false; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private syncSeedInputBridge(): void { |
|
|
|
|
|
if (!this.resolveDocument()) { |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
if (this.stage.visible) { |
|
|
|
|
|
this.attachSeedInputBridge(); |
|
|
|
|
|
} else { |
|
|
|
|
|
this.detachSeedInputBridge(); |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private readonly handleSeedInputEvent = (event: Event): void => { |
|
|
private readonly handleSeedInputEvent = (event: Event): void => { |
|
|
@ -271,4 +310,14 @@ export default class ChaseScene extends BaseScene { |
|
|
this.seedInput = target?.value ?? ""; |
|
|
this.seedInput = target?.value ?? ""; |
|
|
this.refreshView(); |
|
|
this.refreshView(); |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
private resolveDocument(): DocumentLike | undefined { |
|
|
|
|
|
if (this.documentRef) { |
|
|
|
|
|
return this.documentRef; |
|
|
|
|
|
} |
|
|
|
|
|
if (typeof document !== "undefined") { |
|
|
|
|
|
return document; |
|
|
|
|
|
} |
|
|
|
|
|
return undefined; |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
|