|
|
@ -1,13 +1,13 @@ |
|
|
import { BaseScene } from "@/scene/BaseScene"; |
|
|
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 { 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 } 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">; |
|
|
type ChaseModelLike = Pick<ChaseGameModel, "getState" | "moveThief" | "newRound">; |
|
|
type ChaseSceneOptions = { |
|
|
type ChaseSceneOptions = { |
|
|
model?: ChaseModelLike; |
|
|
model?: ChaseModelLike; |
|
|
modelFactory?: () => ChaseModelLike; |
|
|
modelFactory?: () => ChaseModelLike; |
|
|
@ -18,7 +18,11 @@ export default class ChaseScene extends BaseScene { |
|
|
private model?: ChaseModelLike; |
|
|
private model?: ChaseModelLike; |
|
|
private readonly modelFactory?: () => ChaseModelLike; |
|
|
private readonly modelFactory?: () => ChaseModelLike; |
|
|
private hudText?: Text; |
|
|
private hudText?: Text; |
|
|
|
|
|
private setupText?: Text; |
|
|
private graphLayer = new Container(); |
|
|
private graphLayer = new Container(); |
|
|
|
|
|
private selectedDifficulty: Difficulty = "normal"; |
|
|
|
|
|
private seedInput = ""; |
|
|
|
|
|
private difficultyLocked = false; |
|
|
|
|
|
|
|
|
constructor(options: ChaseSceneOptions = {}) { |
|
|
constructor(options: ChaseSceneOptions = {}) { |
|
|
super("chase", SceneType.Normal); |
|
|
super("chase", SceneType.Normal); |
|
|
@ -41,6 +45,17 @@ export default class ChaseScene extends BaseScene { |
|
|
this.hudText.position.set(24, 24); |
|
|
this.hudText.position.set(24, 24); |
|
|
this.stage.addChild(this.hudText); |
|
|
this.stage.addChild(this.hudText); |
|
|
|
|
|
|
|
|
|
|
|
this.setupText = new Text({ |
|
|
|
|
|
text: "", |
|
|
|
|
|
style: new TextStyle({ |
|
|
|
|
|
fontSize: 16, |
|
|
|
|
|
fill: 0xdbeafe, |
|
|
|
|
|
fontFamily: "'Microsoft YaHei', 'PingFang SC', system-ui, sans-serif", |
|
|
|
|
|
}), |
|
|
|
|
|
}); |
|
|
|
|
|
this.setupText.position.set(24, 54); |
|
|
|
|
|
this.stage.addChild(this.setupText); |
|
|
|
|
|
|
|
|
this.graphLayer.eventMode = "passive"; |
|
|
this.graphLayer.eventMode = "passive"; |
|
|
this.graphLayer.position.set(140, 140); |
|
|
this.graphLayer.position.set(140, 140); |
|
|
this.stage.addChild(this.graphLayer); |
|
|
this.stage.addChild(this.graphLayer); |
|
|
@ -57,12 +72,13 @@ export default class ChaseScene extends BaseScene { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
public refreshView(): void { |
|
|
public refreshView(): void { |
|
|
if (!this.model || !this.hudText) { |
|
|
if (!this.model || !this.hudText || !this.setupText) { |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const state = this.model.getState(); |
|
|
const state = this.model.getState(); |
|
|
this.hudText.text = `Turn ${state.turn} | ${state.snapshot.status} | thief=${state.snapshot.thiefNodeId} guard=${state.snapshot.guardNodeId}`; |
|
|
this.hudText.text = `Turn ${state.turn} | ${state.snapshot.status} | thief=${state.snapshot.thiefNodeId} guard=${state.snapshot.guardNodeId}`; |
|
|
|
|
|
this.setupText.text = `difficulty=${this.selectedDifficulty} | seed=${this.seedInput.trim() || "(auto)"} | locked=${this.difficultyLocked ? "yes" : "no"}`; |
|
|
|
|
|
|
|
|
const oldChildren = this.graphLayer.removeChildren(); |
|
|
const oldChildren = this.graphLayer.removeChildren(); |
|
|
for (const child of oldChildren) { |
|
|
for (const child of oldChildren) { |
|
|
@ -98,4 +114,34 @@ export default class ChaseScene extends BaseScene { |
|
|
this.model.moveThief(targetNodeId); |
|
|
this.model.moveThief(targetNodeId); |
|
|
this.refreshView(); |
|
|
this.refreshView(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public setDifficulty(difficulty: Difficulty): void { |
|
|
|
|
|
if (this.difficultyLocked) { |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
this.selectedDifficulty = difficulty; |
|
|
|
|
|
this.refreshView(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public setSeedInput(seedInput: string): void { |
|
|
|
|
|
this.seedInput = seedInput; |
|
|
|
|
|
this.refreshView(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public startGame(): void { |
|
|
|
|
|
if (!this.model) { |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
const seedText = this.seedInput.trim(); |
|
|
|
|
|
const parsed = Number(seedText); |
|
|
|
|
|
const seed = |
|
|
|
|
|
seedText.length === 0 || !Number.isFinite(parsed) |
|
|
|
|
|
? Math.floor(Math.random() * 0xffffffff) |
|
|
|
|
|
: parsed; |
|
|
|
|
|
|
|
|
|
|
|
this.model.newRound(seed, this.selectedDifficulty); |
|
|
|
|
|
this.difficultyLocked = true; |
|
|
|
|
|
this.seedInput = `${seed}`; |
|
|
|
|
|
this.refreshView(); |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
|