add username balloons under agents
This commit is contained in:
parent
55ce8b67f5
commit
e9504c6359
3 changed files with 41 additions and 10 deletions
|
@ -51,16 +51,25 @@ class AgentAnimationState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum AgentWordBalloonPosition {
|
||||||
|
AboveCentered,
|
||||||
|
BelowCentered
|
||||||
|
}
|
||||||
|
|
||||||
class AgentWordBalloonState {
|
class AgentWordBalloonState {
|
||||||
char: Agent;
|
char: Agent;
|
||||||
text: string;
|
text: string;
|
||||||
|
hasTip: boolean;
|
||||||
|
position: AgentWordBalloonPosition;
|
||||||
|
|
||||||
balloonCanvas: HTMLCanvasElement;
|
balloonCanvas: HTMLCanvasElement;
|
||||||
balloonCanvasCtx: CanvasRenderingContext2D;
|
balloonCanvasCtx: CanvasRenderingContext2D;
|
||||||
|
|
||||||
constructor(char: Agent, text: string) {
|
constructor(char: Agent, text: string, hasTip: boolean, position: AgentWordBalloonPosition) {
|
||||||
this.char = char;
|
this.char = char;
|
||||||
this.text = text;
|
this.text = text;
|
||||||
|
this.hasTip = hasTip;
|
||||||
|
this.position = position;
|
||||||
this.balloonCanvas = document.createElement('canvas');
|
this.balloonCanvas = document.createElement('canvas');
|
||||||
this.balloonCanvasCtx = this.balloonCanvas.getContext('2d')!;
|
this.balloonCanvasCtx = this.balloonCanvas.getContext('2d')!;
|
||||||
|
|
||||||
|
@ -74,13 +83,13 @@ class AgentWordBalloonState {
|
||||||
// hack fix for above
|
// hack fix for above
|
||||||
this.balloonCanvas.style.pointerEvents = 'none';
|
this.balloonCanvas.style.pointerEvents = 'none';
|
||||||
|
|
||||||
let rect = wordballoonDrawText(this.balloonCanvasCtx, { x: 0, y: 0 }, this.text, 20);
|
let rect = wordballoonDrawText(this.balloonCanvasCtx, { x: 0, y: 0 }, this.text, 20, hasTip);
|
||||||
|
|
||||||
// Second pass, actually set the element to the right width and stuffs
|
// Second pass, actually set the element to the right width and stuffs
|
||||||
this.balloonCanvas.width = rect.w;
|
this.balloonCanvas.width = rect.w;
|
||||||
this.balloonCanvas.height = rect.h;
|
this.balloonCanvas.height = rect.h;
|
||||||
|
|
||||||
wordballoonDrawText(this.balloonCanvasCtx, { x: 0, y: 0 }, this.text, 20);
|
wordballoonDrawText(this.balloonCanvasCtx, { x: 0, y: 0 }, this.text, 20, hasTip);
|
||||||
|
|
||||||
this.char.getElement().appendChild(this.balloonCanvas);
|
this.char.getElement().appendChild(this.balloonCanvas);
|
||||||
|
|
||||||
|
@ -101,9 +110,17 @@ class AgentWordBalloonState {
|
||||||
|
|
||||||
positionUpdated() {
|
positionUpdated() {
|
||||||
let size = this.char.getSize();
|
let size = this.char.getSize();
|
||||||
|
|
||||||
this.balloonCanvas.style.top = -(this.balloonCanvas.height) + 'px';
|
|
||||||
this.balloonCanvas.style.left = -((this.balloonCanvas.width / 2) - (size.w / 2)) + 'px';
|
this.balloonCanvas.style.left = -((this.balloonCanvas.width / 2) - (size.w / 2)) + 'px';
|
||||||
|
switch (this.position) {
|
||||||
|
case AgentWordBalloonPosition.AboveCentered: {
|
||||||
|
this.balloonCanvas.style.top = -(this.balloonCanvas.height) + 'px';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case AgentWordBalloonPosition.BelowCentered: {
|
||||||
|
this.balloonCanvas.style.bottom = -(this.balloonCanvas.height) + 'px';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,6 +136,7 @@ export class Agent {
|
||||||
|
|
||||||
private animState: AgentAnimationState | null = null;
|
private animState: AgentAnimationState | null = null;
|
||||||
private wordballoonState: AgentWordBalloonState | null = null;
|
private wordballoonState: AgentWordBalloonState | null = null;
|
||||||
|
private usernameBalloonState: AgentWordBalloonState | null = null;
|
||||||
|
|
||||||
constructor(data: AcsData) {
|
constructor(data: AcsData) {
|
||||||
this.data = data;
|
this.data = data;
|
||||||
|
@ -262,12 +280,22 @@ export class Agent {
|
||||||
if (index !== -1) this.playAnimation(index, finishCallback);
|
if (index !== -1) this.playAnimation(index, finishCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setUsername(username: string) {
|
||||||
|
if (this.usernameBalloonState !== null) {
|
||||||
|
this.usernameBalloonState.finish();
|
||||||
|
this.usernameBalloonState = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.usernameBalloonState = new AgentWordBalloonState(this, username, false, AgentWordBalloonPosition.BelowCentered);
|
||||||
|
this.usernameBalloonState.show();
|
||||||
|
}
|
||||||
|
|
||||||
speak(text: string) {
|
speak(text: string) {
|
||||||
if (this.wordballoonState != null) {
|
if (this.wordballoonState != null) {
|
||||||
this.stopSpeaking();
|
this.stopSpeaking();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.wordballoonState = new AgentWordBalloonState(this, text);
|
this.wordballoonState = new AgentWordBalloonState(this, text, true, AgentWordBalloonPosition.AboveCentered);
|
||||||
this.wordballoonState.positionUpdated();
|
this.wordballoonState.positionUpdated();
|
||||||
this.wordballoonState.show();
|
this.wordballoonState.show();
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,7 @@ export async function wordballoonInit() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function returns a rect which is the usable inner contents of the box.
|
// This function returns a rect which is the usable inner contents of the box.
|
||||||
export function wordballoonDraw(ctx: CanvasRenderingContext2D, at: Point, size: Size): Rect {
|
export function wordballoonDraw(ctx: CanvasRenderingContext2D, at: Point, size: Size, hasTip: boolean = true): Rect {
|
||||||
// Snap the size to a clean 12x12 system,
|
// Snap the size to a clean 12x12 system,
|
||||||
// so we stay (as close to) pixel perfect as possible.
|
// so we stay (as close to) pixel perfect as possible.
|
||||||
// This is "lazy" but oh well. It works!
|
// This is "lazy" but oh well. It works!
|
||||||
|
@ -93,6 +93,7 @@ export function wordballoonDraw(ctx: CanvasRenderingContext2D, at: Point, size:
|
||||||
// For now, we always simply use the center of the bottom..
|
// For now, we always simply use the center of the bottom..
|
||||||
|
|
||||||
// Draw the tip.
|
// Draw the tip.
|
||||||
|
if (hasTip)
|
||||||
spriteDraw(ctx, tip_sprite, at.x + size.w / 2, at.y + 12 * (j + 1) - 1);
|
spriteDraw(ctx, tip_sprite, at.x + size.w / 2, at.y + 12 * (j + 1) - 1);
|
||||||
|
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
|
@ -128,7 +129,7 @@ function wordWrapToStringList(text: string, maxLength: number) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// This draws a wordballoon with text. This function respects the current context's font settings and does *not* modify them.
|
// This draws a wordballoon with text. This function respects the current context's font settings and does *not* modify them.
|
||||||
export function wordballoonDrawText(ctx: CanvasRenderingContext2D, at: Point, text: string, maxLen: number = 20): Rect {
|
export function wordballoonDrawText(ctx: CanvasRenderingContext2D, at: Point, text: string, maxLen: number = 20, hasTip: boolean = true): Rect {
|
||||||
let lines = wordWrapToStringList(text, maxLen);
|
let lines = wordWrapToStringList(text, maxLen);
|
||||||
|
|
||||||
// Create metrics for each line
|
// Create metrics for each line
|
||||||
|
@ -158,7 +159,7 @@ export function wordballoonDrawText(ctx: CanvasRenderingContext2D, at: Point, te
|
||||||
size.h = Math.floor(size.h);
|
size.h = Math.floor(size.h);
|
||||||
|
|
||||||
// Draw the word balloon and get the inner rect
|
// Draw the word balloon and get the inner rect
|
||||||
let rectInner = wordballoonDraw(ctx, at, size);
|
let rectInner = wordballoonDraw(ctx, at, size, hasTip);
|
||||||
|
|
||||||
// Draw all the lines of text
|
// Draw all the lines of text
|
||||||
let y = 0;
|
let y = 0;
|
||||||
|
|
|
@ -142,6 +142,7 @@ export class MSAgentClient {
|
||||||
this.charlimit = initMsg.data.charlimit;
|
this.charlimit = initMsg.data.charlimit;
|
||||||
for (let _user of initMsg.data.users) {
|
for (let _user of initMsg.data.users) {
|
||||||
let agent = await agentCreateCharacterFromUrl(this.url + '/api/agents/' + _user.agent);
|
let agent = await agentCreateCharacterFromUrl(this.url + '/api/agents/' + _user.agent);
|
||||||
|
agent.setUsername(_user.username);
|
||||||
agent.addToDom(this.agentContainer);
|
agent.addToDom(this.agentContainer);
|
||||||
agent.show();
|
agent.show();
|
||||||
let user = new User(_user.username, agent);
|
let user = new User(_user.username, agent);
|
||||||
|
@ -153,6 +154,7 @@ export class MSAgentClient {
|
||||||
case MSAgentProtocolMessageType.AddUser: {
|
case MSAgentProtocolMessageType.AddUser: {
|
||||||
let addUserMsg = msg as MSAgentAddUserMessage;
|
let addUserMsg = msg as MSAgentAddUserMessage;
|
||||||
let agent = await agentCreateCharacterFromUrl(this.url + '/api/agents/' + addUserMsg.data.agent);
|
let agent = await agentCreateCharacterFromUrl(this.url + '/api/agents/' + addUserMsg.data.agent);
|
||||||
|
agent.setUsername(addUserMsg.data.username);
|
||||||
agent.addToDom(this.agentContainer);
|
agent.addToDom(this.agentContainer);
|
||||||
agent.show();
|
agent.show();
|
||||||
let user = new User(addUserMsg.data.username, agent);
|
let user = new User(addUserMsg.data.username, agent);
|
||||||
|
|
Loading…
Reference in a new issue