Skip to content

Commit 8d6f314

Browse files
authored
Merge pull request #64 from Visual-Intelligence-UMN/harry-react-dev
Harry react dev
2 parents 6164510 + 0be4c87 commit 8d6f314

File tree

13 files changed

+235
-3
lines changed

13 files changed

+235
-3
lines changed

src/App.tsx

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,21 @@ export interface Report{
1313
department: string,
1414
}
1515

16+
export interface AgentInformation{
17+
mssg: string,
18+
agent: string,
19+
}
20+
21+
22+
1623
function App()
1724
{
1825
const [report, setReport] = useState<Report[]>([]);
1926
const [isOpen, setIsOpen] = useState(false);
2027
const [currentReport, setCurrentReport] = useState("");
2128
const [htmlReport, setHtmlReport] = useState<any>("");
2229
const [isConstitutionOpen, setIsConstitutionOpen] = useState(false);
30+
const [agentProfiles, setAgentProfiles] = useState<AgentInformation[]>([]);
2331

2432

2533
const [charts, setCharts] = useState<{id: string; code: string}[]>([]);
@@ -47,6 +55,25 @@ function App()
4755
// setIsOpen(true);
4856
};
4957

58+
const handleAgentInformation = (data: {agent: string, mssg: string}) =>{
59+
console.log("Agent information received", data.agent, data.mssg);
60+
const curReport:AgentInformation = {
61+
mssg: data.mssg,
62+
agent: data.agent,
63+
}
64+
// check if the report'department is already in the list, if yes, update the report; if no, add the report
65+
const index = agentProfiles.findIndex((r) => r.agent === data.agent);
66+
if(index !== -1){
67+
agentProfiles[index] = curReport;
68+
setAgentProfiles([...agentProfiles]);
69+
}else{
70+
agentProfiles.push(curReport);
71+
setAgentProfiles([...agentProfiles]);
72+
}
73+
74+
console.log("agentProfiles", agentProfiles);
75+
}
76+
5077
// have a handler for setting currentReport and open the reporting window
5178
const handleReportOpen = (data: { department: string}) => {
5279
const index = report.findIndex((r) => r.department === data.department);
@@ -100,6 +127,45 @@ function App()
100127
// embedding the markdown into the draggable window
101128
}
102129

130+
131+
const handleAgentInformationOpen = (data: { agent: string}) => {
132+
const index = agentProfiles.findIndex((r) => r.agent === data.agent);
133+
if(index !== -1){
134+
setCurrentReport(agentProfiles[index].mssg);
135+
136+
marked.use({
137+
extensions: [
138+
{
139+
name: 'highlight',
140+
level: 'inline',
141+
start(src) { return src.indexOf("=="); },
142+
tokenizer(src, tokens) {
143+
const rule = /^==([^=]+)==/;
144+
const match = rule.exec(src);
145+
if (match) {
146+
return {
147+
type: 'highlight',
148+
raw: match[0],
149+
text: match[1],
150+
tokens: this.lexer.inlineTokens(match[1]),
151+
};
152+
}
153+
},
154+
renderer(token: any) {
155+
return `<mark>${marked.parser(token.tokens)}</mark>`;
156+
},
157+
},
158+
],
159+
});
160+
161+
console.log("agentProfiles[index].mssg", agentProfiles[index].mssg);
162+
163+
setHtmlReport(agentProfiles[index].mssg);
164+
if(!isOpen)setIsOpen(true);
165+
}
166+
}
167+
168+
103169
// EventBus.on("d3-code", (data: {d3Code: string, id: string}) => {
104170
// setCharts(prev => [
105171
// ...prev,
@@ -125,6 +191,10 @@ function App()
125191

126192
EventBus.on("final-report", handleReportReceiving);
127193
EventBus.on("open-report", handleReportOpen);
194+
195+
EventBus.on("agent-information", handleAgentInformation);
196+
EventBus.on("open-agent-information", handleAgentInformationOpen);
197+
128198
EventBus.on("d3-code", handleD3Code);
129199
EventBus.on("open-constitution", handleConstitutionOpen);
130200

199 Bytes
Loading
205 Bytes
Loading

src/game/assets/sprites/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,5 @@ export { default as dialog_icon } from "./dialog_char_icon.png"
1919
export { default as idle_icon } from "./idle_char_icon.png"
2020
export { default as record_icon } from "./record_char_icon.png"
2121
export { default as pdf } from "./pdf.png"
22+
export { default as agent_mssg } from "./agent-mssg.png"
23+
export { default as agent_idle } from "./agent-idle.png"

src/game/constants/key.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ const image = {
3232
voting: "voting",
3333
single_agent: "single_agent",
3434
pdfIcon: 'pdfIcon',
35+
agent_mssg: 'agent_mssg',
36+
agent_idle: 'agent_idle',
3537
} as const;
3638

3739
const scene = {

src/game/scenes/Boot.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ export class Boot extends Scene {
2828
this.load.image(key.image.interior, assets.tilesets.interior);
2929
this.load.image(key.image.exterior, assets.tilesets.exterior);
3030
this.load.image(key.image.coinIcon, assets.sprites.coinIcon);
31+
this.load.image(key.image.agent_mssg, assets.sprites.agent_mssg);
32+
this.load.image(key.image.agent_idle, assets.sprites.agent_idle);
3133

3234
this.load.image(key.image.dialog_icon, assets.sprites.dialog_icon);
3335
this.load.image(key.image.idle_icon, assets.sprites.idle_icon);

src/game/sprites/Agent.ts

Lines changed: 125 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import Phaser from 'phaser';
22

33
import { key } from '../constants';
44
import { Inventory } from './Player';
5+
import { EventBus } from '../EventBus';
56

67
enum Animation {
78
Left = 'player_left',
@@ -31,6 +32,7 @@ export class Agent extends Phaser.Physics.Arcade.Sprite {
3132
private instruction: string = "";
3233
private bias: string = "";
3334
private isBiased: boolean = false;
35+
private mssgSprite: Phaser.GameObjects.Image | null = null;
3436

3537
private wasDragged: boolean = false; // if user drag the agent now
3638

@@ -40,13 +42,129 @@ export class Agent extends Phaser.Physics.Arcade.Sprite {
4042

4143
public static currentBiasedAgent: Agent | null = null;
4244

45+
46+
private agentInformation:string = "aaaaaaa";
47+
48+
public addMssgSprite(scene: Phaser.Scene, texture: string, frame?: string | number) {
49+
if (this.mssgSprite) {
50+
console.log("Updating message sprite for agent:", this.name);
51+
this.mssgSprite.setTexture(texture, frame);
52+
53+
this.mssgSprite.removeAllListeners();
54+
this.mssgSprite.disableInteractive();
55+
56+
if (texture === "agent_mssg") {
57+
this.mssgSprite.setInteractive({ useHandCursor: true });
58+
this.mssgSprite.on('pointerdown', () => {
59+
console.log(`Message sprite of ${this.name} clicked!`);
60+
this.changeNameTagColor('#00ff00');
61+
EventBus.emit("open-agent-information", {
62+
agent: this.name
63+
});
64+
});
65+
}
66+
return;
67+
}
68+
69+
console.log("Adding message sprite to agent:", this.name);
70+
this.mssgSprite = scene.add.image(this.x, this.y, texture, frame)
71+
.setOrigin(0.5, 1)
72+
.setDepth(10);
73+
74+
if (texture === "agent_mssg") {
75+
this.mssgSprite.setInteractive({ useHandCursor: true });
76+
this.mssgSprite.on('pointerdown', () => {
77+
console.log(`Message sprite of ${this.name} clicked!`);
78+
this.changeNameTagColor('#00ff00');
79+
EventBus.emit("open-agent-information", {
80+
agent: this.name
81+
});
82+
});
83+
}
84+
}
85+
86+
87+
4388

4489
// Add reset method of the calculation of the biased agents
4590
public static resetBiasedAgentsCount() {
4691
Agent.biasedAgentsCount = 0;
4792
Agent.currentBiasedAgent = null;
4893
}
4994

95+
public getAgentInformation(){
96+
return this.agentInformation;
97+
}
98+
99+
public setAgentInformation(info: string) {
100+
this.agentInformation = info;
101+
EventBus.emit("agent-information", {
102+
agent: this.name,
103+
mssg: this.getAgentInformation()
104+
});
105+
}
106+
107+
public playDialogue(
108+
scene: Phaser.Scene,
109+
text: string,
110+
speed: number = 50,
111+
sentencePause: number = 1500
112+
) {
113+
// 1. 按标点切分成句子数组(支持中英文)
114+
const sentences = text.match(/[^.!?]+[.!?]/g) || [text];
115+
116+
// 2. 在 Agent 右上角添加文本
117+
let textObj = scene.add.text(this.x + 40, this.y - 40, "", {
118+
fontSize: "16px",
119+
color: "#ffffff",
120+
backgroundColor: "rgba(0,0,0,0.5)",
121+
wordWrap: { width: 200 }
122+
}).setDepth(20);
123+
124+
let currentSentenceIndex = 0;
125+
let charIndex = 0;
126+
let isPaused = false;
127+
128+
scene.time.addEvent({
129+
delay: speed,
130+
loop: true,
131+
callback: () => {
132+
if (isPaused) return;
133+
134+
const sentence = sentences[currentSentenceIndex];
135+
136+
if (charIndex < sentence.length) {
137+
// 逐字输出
138+
textObj.text += sentence[charIndex];
139+
charIndex++;
140+
} else {
141+
// 一句话播完 → 停顿 → 清空 → 下一句
142+
isPaused = true;
143+
scene.time.delayedCall(sentencePause, () => {
144+
currentSentenceIndex++;
145+
charIndex = 0;
146+
147+
if (currentSentenceIndex < sentences.length) {
148+
textObj.text = ""; // ✅ 清空上句
149+
isPaused = false; // 开始下一句
150+
} else {
151+
console.log(`✅ Agent ${this.name} 所有句子播放完毕`);
152+
textObj.destroy(); // 最后一条播完移除
153+
}
154+
});
155+
}
156+
}
157+
});
158+
159+
// 让气泡位置始终跟随 Agent
160+
scene.events.on("update", () => {
161+
if (textObj && textObj.active) {
162+
textObj.setPosition(this.x + 40, this.y - 40);
163+
}
164+
});
165+
}
166+
167+
50168

51169
public assignToWorkplace: boolean = false;
52170
private activationFunction: (state: any) => any = (state: any) => {
@@ -139,8 +257,8 @@ export class Agent extends Phaser.Physics.Arcade.Sprite {
139257

140258
}
141259

142-
update() {
143-
// this.nameTag.setPosition(this.x, this.y - 25);
260+
update() {
261+
this.mssgSprite?.setPosition(this.x - 15, this.y);
144262
}
145263

146264
public getName(){
@@ -234,6 +352,9 @@ export class Agent extends Phaser.Physics.Arcade.Sprite {
234352
if (gameObject === this) {
235353
console.log(`Agent ${this.name} clicked!`);
236354

355+
const agentInfo = this.getAgentInformation();
356+
console.log(`Agent Information: ${agentInfo}`);
357+
237358
// If there is already another biased agent, restore it first.
238359
if (Agent.currentBiasedAgent && Agent.currentBiasedAgent !== this) {
239360
Agent.currentBiasedAgent.setToUnbiased();
@@ -284,6 +405,8 @@ export class Agent extends Phaser.Physics.Arcade.Sprite {
284405
}
285406

286407

408+
409+
287410
private createWorkAnimations(atlasKey: string) {
288411

289412
// console.log("✅ texture keys:", this.scene.textures.getTextureKeys());

src/game/utils/hudUtils.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ export function addRoomHiringMenuHUD(this: any) {
164164
const { x: agentX, y: agentY } = generateNonCollidingAgentPosition(this.controllableCharacters, bounds);
165165

166166
const agent = new Agent(this, agentX, agentY, 'player', 'misa-front', 'Agent '+this.controllableCharacters.length);
167+
agent.addMssgSprite(this, "agent_mssg");
167168
this.agentGroup.add(agent);
168169
this.controllableCharacters.push(agent);
169170
this.agentList.set(agent.getName(), agent);
@@ -179,6 +180,8 @@ export function addRoomHiringMenuHUD(this: any) {
179180
const { x: agentX, y: agentY } = generateNonCollidingAgentPosition(this.controllableCharacters, bounds);
180181

181182
const agent = new Agent(this, agentX, agentY, 'player', 'misa-front', 'Agent '+this.controllableCharacters.length);
183+
184+
agent.addMssgSprite(this, "agent_mssg");
182185
this.agentGroup.add(agent);
183186
this.controllableCharacters.push(agent);
184187
this.agentList.set(agent.getName(), agent);

src/game/utils/sceneUtils.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,10 @@ export function addAgentsBasedOnSpawningPoints(
136136
bias
137137
);
138138

139+
// agent.playDialogue(scene, "hello. nice. nihao.")
140+
141+
agent.addMssgSprite(scene, "agent_idle");
142+
139143
if(spawningPoint.name.includes("analysis_")){
140144
//agent.setToBiased();
141145
}

src/langgraph/agents.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,9 @@ export function createJournalist(
167167
const originalAgent1Y = agent.y;
168168

169169
// await updateStateIcons(zones, "mail", 0);
170+
//await agent.playDialogue(scene, msg.content);
171+
await agent.setAgentInformation(msg.content);
172+
await agent.addMssgSprite(scene, "agent_mssg");
170173
console.log("debug agent pos", destination.x, destination.y);
171174
await autoControlAgent(scene, agent, tilemap, (destination.x as number), (destination.y as number), "Send Message");
172175
await autoControlAgent(scene, agent, tilemap, originalAgent1X, originalAgent1Y, "Return to Office");
@@ -299,6 +302,9 @@ export function createManager(
299302
console.log("graph:3rd agent msg:", msg.content);
300303
// await updateStateIcons(zones, "idle", 0);
301304
await agent.setAgentState("idle");
305+
//await agent.playDialogue(scene, msg.content);
306+
await agent.setAgentInformation(msg.content);
307+
await agent.addMssgSprite(scene, "agent_mssg");
302308

303309
await createReport(scene, "chaining", index, destination.x, destination.y);
304310
const report = await createReport(scene, "chaining", index, destination.x, destination.y);
@@ -431,7 +437,10 @@ export function createWriter(
431437
const originalAgent2Y = agent.y;
432438

433439
// await updateStateIcons(zones, "mail", 1);
434-
// await updateStateIcons(scene.chainingZones, "mail");
440+
// await updateStateIcons(scene.chainingZones, "mail");
441+
//await agent.playDialogue(scene, msg.content);
442+
await agent.setAgentInformation(msg.content);
443+
await agent.addMssgSprite(scene, "agent_mssg");
435444

436445
await autoControlAgent(scene, agent, tilemap, destination.x, destination.y, "Send Report to Final Location");
437446

0 commit comments

Comments
 (0)