Chatbox-like Dialogue

Started by bulka_tarta, Sun 26/02/2017 22:51:31

Previous topic - Next topic

bulka_tarta

Hi,

I'm not sure if this is not going to be too advanced for "Beginners Forum", but I'm a beginner (in AGS and programming!) so it felt appropriate to post it here.

I'm trying to get a custom dialogue to look like a chat box, like you could expect in Skype, Facebook Chat or similar.

Key points that I want it (eventually) to include:
- text stays on screen and it's scrollable
- possibility to divide messages into individual boxes (boxes/labels anything)
- possibility for (at least) two variations of the boxes to determine characters
- non-blocking - so perhaps some kind of a backgroundSay?

I tested out at least 3 different dialogue modules to try and get them to do what I wanted. I also spent all day browsing through various topics. Considering that I'm a total programming noob, it is highly possible that I have seen the answers somewhere on the forums, but have totally misunderstood it or I just didn't notice that it could be of use to me.

I have however, managed to put ttogether a very simple hack

Code: ags

//setting up the function
void MySay(this Character*, String text ,String text2, String text3) {
   
  lblSay2.Visible = false;
  lblSay2.SetPosition(7, 123);
  lblSay3.Visible = false;
  lblSay3.SetPosition(7, 123);
  
  lblSay1.Text = text;
  lblSay2.Text = text2;
  lblSay3.Text = text3;

  gDialog.Visible = true;  
  Wait(40);
  lblSay1.TweenPosition(0.2, 7, 100);
  
  lblSay2.Visible = true;
  Wait(40);
  lblSay1.TweenPosition(0.2, 7, 77, eEaseLinearTween , eNoBlockTween);
  lblSay2.TweenPosition(0.2, 7, 100);
  
  lblSay3.Visible = true;
  Wait(40);
  lblSay2.TweenPosition(0.2, 7, 100);
  
  Wait(40);
  gDialog.Visible = false;

// using MySay();
function cClient1_Talk()
{
  cClient1.MySay("Hi", "What's up", "Blah");
}


All of the above does pretty much what I wanted, but as you can imagine it is extremely limiting and very primitive. I wanted to be able to determine what each individual label (chat box) has to say, where the function would keep creating new labels for each phrase. I was thinking of using some sort of "[" in the text to determine where a new box would be created.


I understand that this may require extensive amount of work, but any kind of pointers would be much appreciated.


EDIT: I just remembered a good example of how it could work like:https://youtu.be/Kek4zO0OF_8?t=121
It's the chat-thing on the left. Player doesn't give any input, it's just triggered at certain points in the game.
I believe the game was made in Gamemaker, but would something similar be achievable in AGS at all?
(BTW the game is pretty damn cool, if you haven't played it already give it a go!)

Khris

You can pretty much create anything in AGS, but you have to draw it yourself, is the gist. And by "draw it yourself" I mean "use DrawingSurface commands".

For this I'd use a long Dynamic Sprite and draw the messages to it as the chat progresses. To display it, I'd create a copy of the DynamicSprite, crop it and set it as GUI background / draw it to the room.
AGS is also fast enough to draw the messages on the fly each frame, in case you want to reproduce the scroll effect that way.

You could also use GUI buttons for the messages (buttons can have arbitrary graphics) and move them up. There are several ways to approach this, but the basic idea is that messages are added to an array, which is then drawn to the screen in the shape of the chat window.

The sky is the limit, pretty much, you could in theory even include emoticons.

Crimson Wizard

TBH I believe this belongs to Advanced Tech forum. Beginner's tech questions is usually about how to make patrolling character :).

bulka_tarta

#3
Hey, thanks for the tips. I was trying to use GUI with bunch of labels and here's what I have so far:
Code: ags

String chatSay[100]; // I will probably never have 100 chat messages at once, but just in case...
bool messageOn[100];
export chatSay;
export messageOn;

//makes all labels invisible
// called in global script game_start() { 
function ChatInfo(){
  lblMessage1.Visible = false;
  lblMessage2.Visible = false;
  lblMessage3.Visible = false;
  lblMessage4.Visible = false;
  lblMessage5.Visible = false;

//defines all chatSay variables and messageOn bool
  chatSay[0] = "";
  chatSay[1] = "";
  chatSay[2] = "";
  chatSay[3] = "";
  chatSay[4] = "";

  messageOn[0] = false;
  messageOn[1] = false;
  messageOn[2] = false;
  messageOn[3] = false;
  messageOn[4] = false;
}

// setting up the chat labels
// called in global script repeatedly_execute()
function ChatLabels(){
  lblMessage1.Text = chatSay[0];
  lblMessage2.Text = chatSay[1];
  lblMessage3.Text = chatSay[2];
  lblMessage4.Text = chatSay[3];
  lblMessage5.Text = chatSay[4];
}

// not the best way, it will require a lot of If statements :/
// called in global script repeatedly_execute()
function ChatMessage(){
  if (messageOn[0] == true){
    gChatDialog.Visible = true;
    lblMessage1.Visible = true;
  }
  if (chatSay[1] != ""){
    gChatDialog.TweenY(0.2, 10, eEaseLinearTween, eNoBlockTween); // some tweening action to move the boxes around, currently just place holder info
    lblMessage2.Visible = true;
  }
   if (chatSay[2] != ""){
    gChatDialog.TweenY(0.2, 10, eEaseLinearTween, eNoBlockTween);
    lblMessage2.Visible = true;
  }
   if (chatSay[3] != ""){
    gChatDialog.TweenY(0.2, 10, eEaseLinearTween, eNoBlockTween);
    lblMessage3.Visible = true;
  } if (chatSay[4] != ""){
    gChatDialog.TweenY(0.2, 10, eEaseLinearTween, eNoBlockTween);
    lblMessage4.Visible = true;
  }
}

//separate script that would later serve as a dialogue editor
//triggered by pressing a button
function Dialog00(){
  messageOn[0] = true;
  chatSay[0] = "Hii";
  messageOn[1] = true;
  chatSay[1] = "Is this really you?!";
  messageOn[2] = true;
  chatSay[2] = "I know where you live";
  messageOn[3] = true;
  chatSay[3] = "Buahahah";
  messageOn[4] = true;
  chatSay[4] = "Say goodbye to your fish";
}


I have a good feeling (might be very misleading) that this would eventually get me what I wanted. I was also planning on substituting "messageOn" to a global int, that triggers later messages "(if chatCount == 1)lblMessage1.Visible = true; if (chatCount 2, message2 etc"). Before I do that, the current issue is that all of the labels display at the same time after triggering the dialogue. If I insert some "Wait" functions (between variables in "Dialog00()", or between the if statements in "ChatMessage()") all that happens is that after clicking the button (triggers dialogue) the game just waits a while, and later all the labels appear all at once. I realize it's some kind of problem of the order of how things are called, but I have no idea how to get it to wait a little between each label being visible.

Anyway, while I was working on this, I realized that there's a new post on the topic. I will look into the Khris' suggestion and read some more about DrawingSurface commands - I imagine his suggestion would be much more efficient, but I will have to see if I can grasp my head around it.

bulka_tarta

#4
Ok... So I had a little go on attempting to create what Khris suggested.

Code: AGS

DynamicSprite* msgDS;    
DrawingSurface* msgSurface[50];
int msgOn; //to turning on different messages 
String msgText[50];

export msgDS;
export msgSurface;
export msgOn;
export msgText;

//called in in Global Script, repeatedly_execute()
function msgChat(){
  msgDS = DynamicSprite.Create(250, 400);  //long Dynamic Sprite
  gDynamic.BackgroundGraphic = msgDS.Graphic; 
  
  if (msgOn == 1){
  msgSurface[0] = msgDS.GetDrawingSurface();     
  msgSurface[0].DrawingColor = 14;          
  msgSurface[0].DrawStringWrapped(0, 0, 200, Game.NormalFont, eAlignLeft, msgText[0]");
  msgSurface[0].Release();
  }
  
  if (msgOn == 2){
  msgSurface[1] = msgDS.GetDrawingSurface();     
  msgSurface[1].DrawingColor = 14;          
  msgSurface[1].DrawStringWrapped(0, 20, 200, Game.NormalFont, eAlignLeft, msgText[1]);
  msgSurface[1].Release();
  }
  
  if (msgOn == 3){
  msgSurface[2] = msgDS.GetDrawingSurface();     
  msgSurface[2].DrawingColor = 14;          
  msgSurface[2].DrawStringWrapped(0, 40, 200, Game.NormalFont, eAlignLeft, msgText[2]);
  msgSurface[2].Release();
  }
}

//In separate script, intended to be used as dialog editor
function Dialog01(){
  msgText[0] = "Hii";
  msgText[1] = "I know where you live";
  msgText[2] = "Say goodbye to your fish";
}

//Button on click to trigger dialogue
  Dialog01();     //changes the msgText strings
  msgOn = 1;      //supposed to trigger first msgText string
  Wait(40);       //supposed to wait before going to second line...
  msgOn = 2;


As you can see, I combined my previous approach, but with DynamicSprite instead. I do prefer the idea of this (no messing around with the GUI labels), but I still have some problems with the code.
1) Most importantly, it doesn't behave exactly as I would like. The Wait(); function doesn't do much - I was hoping that after button is clicked, I can change msgOn to 1, display first Surface, Wait, Change msgOn to 2, Display another line, etc (I would later add some tweening to get that nicer chat-look). For some reason, what happens instead is that the game waits after clicking the button, and goes straight to "msgOn 2" and skips first line altogether.
2) I understand that creating lots of "if" statements for each msgSurface is not the best way to go about it, but I have no idea how to get started with that "copy Dynamic Sprite" thing.

I do prefer using DynamicSprites over GUI with multiple labels, but I guess I have hit a brick wall at this stage. I will keep digging through the forums, but as usual, any help is greatly appreciated.


Snarky

You say you're calling msgChat() from repeatedly_execute(). The problem is that repeatedly_execute() doesn't run when the game is blocked, which it is during a Wait().  Use repeatedly_execute_always() instead.

Khris

In general, (almost) any integer constant appearing in your code can be replaced with an integer variable.

Which means you can do this:
Code: ags
  msgSurface[msgOn] = msgDS.GetDrawingSurface();     
  msgSurface[msgOn].DrawingColor = 14;          
  msgSurface[msgOn].DrawStringWrapped(0, msgOn * 20, 200, Game.NormalFont, eAlignLeft, msgText[msgOn]);
  msgSurface[msgOn].Release();


However, I'd separate the logic that draws the chat and the logic that displays it. You can easily crop a DynamicSprite, so you can draw the entire chat in advance, then keep showing more of it. You also don't need 50 DrawingSurfaces; in any situation where you did, you'd definitely also declare 50 DynamicSprites (and draw to them in parallel). For this, all you need to do is to get the DrawingSurface of msgDS, draw all the messages to it, then release it again. Now set the sprite as BackgroundGraphic of the GUI.
Then keep showing more of the GUI in repeatedly_execute, by moving the GUI up in repeatedly_execute:
Code: ags
  if (gDynamic.Visible && gDynamic.Y > 0) gDynamic.Y -= 1;

bulka_tarta

#7
When I thought I get this, I get stuck again (you're dealing with first-time programmer here, sorry :P).

Code: ags

DynamicSprite* msgDS[50];   //changed 50 DrawingSurfaces into 50 DynamicSprites
DrawingSurface* msgSurface;
int msgOn;
String msgText[50];
int msgIndex;

export msgDS;
export msgSurface;
export msgOn;
export msgText;
export msgIndex;

function msgChat(){
  msgIndex = 0;
  
//sets up all of the DynamicSprites
//makes most sense to put it in Game Start, to set up the sprites inititally
  while (msgIndex < 49){     
  msgDS[msgIndex] = DynamicSprite.Create(100, 50);   
  gDynamic.BackgroundGraphic = msgDS[msgIndex].Graphic;  //Is this the problem? Can you have multiple DynamicSprites as one GUI background?
  
  msgOn = 0;
  msgText[msgOn] = "123";  //sets the reference for messages
  
  msgSurface = msgDS[msgIndex].GetDrawingSurface();     
  msgSurface.DrawingColor = 14;          
  msgSurface.DrawStringWrapped(0, msgOn * 20, 200, Game.NormalFont, eAlignLeft, msgText[msgOn]);
  msgIndex ++;
  msgOn ++;
  }
}

function msgChatRelease(){
  if (msgText[msgOn] != "123"){  //if the messaged is changed, release the message
  msgSurface.Release();
  }
}

//separate script, changes the messages, releases them
//triggered through clicking a button
function Dialog01(){
  msgText[0] = "Hii";
  msgText[1] = "I know where you live";
  msgText[2] = "Say goodbye to your fish";
  msgChatRelease();
}



With that code, I would imagine that the messages should display only once the button is clicked. However, all I get is one sprite constantly displayed as "123", which is weird, since I'm not releasing the sprite until I click the button. To my (still pretty limited) understanding, clicking the button should at least change the msgText of the displayed sprite.

Once I get to display all of the messages in correct manner (and once I'm able to edit them), everything should be pretty simple. I imagine the cropping function would be pretty easy to set up:
Code: ags
 msgDS[msgOn].Crop(int x, int y, int width, int height);
And moving the entire GUI shouldn't be a problem too.

EDIT: The only thing that comes to my mind, is that perhaps I can't assign so many different sprites as a one GUI background, and there should be some separate objects/buttons to assign the text to.

EDIT2: So I realized a bunch of things:
1) The "msgOn = 0;" and  "msgText[msgOn] = "123";" need to be outside the while loop, otherwise they both just keep setting themselves back to original values. I have now put them above the while loop, but it triggers error: "Null string referenced" on line 27. I'm not exactly sure how or why...
2) If the gDynamic.BackgroundGraphic is in while loop, it will just keep re-assigning the GUI background to the latest msgDS[].Graphic.

bulka_tarta

#8
O...K...

I (somehow?!) managed to display all the messages on screen at the same time (well, at least ten of them for testing).
Code: ags

//called in on Game Start, global script
function msgChat(){
  msgIndex = 0;
  msgOn = 0;
 
  Test = DynamicSprite.Create(200, 500);
  Test.Crop(0, 0, 150, 50);               //can't work out these numbers, but works oO
  TestSurface = Test.GetDrawingSurface();
  gDynamic.BackgroundGraphic = Test.Graphic;

  while (msgOn < 49){
    msgText[msgOn] = "123";
    msgOn ++;
  }
}

//triggered by a button
function msgChatRelease(){
  if (dialogOn == false){
  TestSurface.DrawingColor = 14;
    while (chatCount < 49){
    TestSurface.DrawStringWrapped(10, chatCount * 20, 200, Game.NormalFont, eAlignLeft, msgText[chatCount]); 
    chatCount ++;
    }
  }

  dialogOn = true;
  
  if (dialogOn == true){
  TestSurface.Release();
  }
}


Above code does (almost!) all I needed. I get ten messages displayed when I hit the button in the scene. The crop works, so that I can display just the part that I'm actually interested in. Moving the GUI around... is not as simple as I thought it would be...

This...
Code: ags

if (gDynamic.Visible && gDynamic.Y > 0) gDynamic.Y -= 1;


Moves the entire GUI with all of the messages AND the crop. This means that it doesn't show "more messages" just moves the ones visible to the top of the screen. I need to find a way to lock the Cropped area or the Dynamic Sprite in place when the GUI is moving, or move the DynamicSprite without moving the GUI. Any idea how to approach this? In the manual I found "Resize (dynamic sprite)" and "Rotate (dynamic sprite)" - why is there no simply "Move (dynamic sprite)" ;(

EDIT: Just for clarification, I'm not thinking of doing a chat dialogue exactly as in the example in first post (full screen height). I need to crop just a small portion of the screen - I'm intending to make it look like a window on your desktop, so to speak.

Khris

I still don't know exactly *how* you want to display the messages.
It sounds like you want a small window, and the messages will appear at the bottom, pushing older messages out at the top?

In that case,
1. create a copy of the DynamicSprite that contains the entire dialog: windowSprite = DynamicSprite.CreateFromExistingSprite(chatSprite.Graphic);
2. crop the copy using a variable as Y-coordinate: windowSprite.Crop(0, chatY, width, height);
3. set the copy's Graphic as GUI background
4. increase chatY and go to step 1

You can also draw the chatSprite's .Graphic to the DrawingSurface of the (smaller) windowSprite at a negative Y coordinate, afaik.

bulka_tarta

#10
Thanks! I will check out the solution as soon as I can.

Regarding how it should look like, it would be kind of a merge between Mainlining chat (as on example earlier) and something like Papers, Please. Here's a quick mock up:

Messages would go from the bottom to top of the screen, but only a certain part in the middle (more or less) would be visible - just enough space to show about 3 messages. Ideally, the player should be able to scroll (like in Mainlining) to see the previous messages if needed.

If there's a new message, it would ideally push all the other ones up by one "block" (one message height).

What I have at the moment, is that the "visible message area" is actually going up with the GUI, and I need it to stay in the one place, while pushing the messages higher.


I'm not sure if that would change your current suggestion Khris, but I will give it a go tomorrow and see what I can do with it. Thanks! :)

bulka_tarta

#11
I'm getting defeated by this every day  :~(

That's what my latest efforts managed to produce:  (warning, contains flashing images) http://i.imgur.com/COIKvNW.gifv
Well, at least it made me laugh when I first saw that bug :P

Code: ags

DynamicSprite* msgDS;
String msgText[50];
int msgIndex;
int textIndex;
int chatY;

export msgDS;
export msgText;
export msgIndex;
export textIndex;
export chatY;

//setting up the text strings
//called in on game_start
function msgReady() {
  textIndex = 0;
  
  while (textIndex < 49){
    msgText[textIndex] = "123";
    textIndex ++;
 }
}

//if in repeatedly execute, it creates the abomination from the link above
//if in game_start just shows the AGS cup (default AGS image)
function msgChat(){
 msgIndex = 0;
 chatY = 0;
 
 msgDS = DynamicSprite.Create(System.ViewportWidth, System.ViewportHeight);

 DynamicSprite *windowSprite = DynamicSprite.CreateFromExistingSprite(msgDS.Graphic);
 windowSprite.Crop(10, chatY, 10, 10);
 gDynamic.BackgroundGraphic = windowSprite.Graphic;
 DrawingSurface *surface = msgDS.GetDrawingSurface();
 
 while (msgIndex < 49){
 surface.DrawStringWrapped(10, msgIndex *10, 100, Game.NormalFont, eAlignLeft, msgText[msgIndex]);
 msgIndex ++;
 }
 
 surface.Release();
 windowSprite.Delete();
}



I'm not sure what I'm doing wrong. I know Khris has mentioned setting up Array for the DynamiSprite (msgDS) but that complicates things even more for me. I once even crashed AGS completely (white screen when playing the game, Alt+F4 doesn't work - crazy stuff!). I'm going to keep trying with this, but meanwhile maybe someone has an idea of why it messes up so badly.


EDIT: the only way I manged to get it to work was by drawing all the messages at once (as previously) and simply placing another GUI on top. The GUI would basically look like the game's background, but it had a hole in the middle, where the text would show through. It's really a quick cheat, I'm not sure how bad it would be to actually use it like this.

Khris

#12
I whipped something up: https://mega.nz/#!JVwUBabY!6IVQ2WEozVytOjZfcGc3R003ZI_wl84elk5X0-AmMhk

To use the module, put a button on a GUI, then create a new chat instance:
Code: ags
  globalHandleInt = Chat.Create(btnChat, eFontWestwood [, bgSprite, width, height]);

bgSprite is a sprite slot for the background, it gets auto-tiled if it's smaller than the button. You can also state width and height, in that case the button is resized to those dimensions.

Optionally add sound:
Code: ags
  Chat.SetSound(globalHandleInt, aBlip);

Pass null to disable sound.

To add a message, just call:
Code: ags
  Chat.Add(globalHandleInt, "The message", "sender" [, hour, minute]);

If you put an asterisk at the start of the sender, like "*Alice", the message appears on the right. You can optionally state a timestamp, or omit for user's local time.

Adding a message makes the chat scroll down automatically. You can also scroll using the mousewheel, while hovering over the chat.

The chat is automatically drawn and scrolled if the GUI and button are visible.

It's also possible to prepare multiple messages, just call Chat.Prepare() instead. Show them one by one using Chat.Advance(globalHandleInt); or all at once using Chat.ShowAllPrepared(globalHandleInt);.
(Calling .Add() will simply call .Prepare(), then .Advance(), so make sure you have advanced over all prepared messages before using .Add(), or you will get unexpected results ;) )

It is recommended to call Chat.CleanUp() upon quitting the game, this will delete all the DynamicSprites, avoiding a warnings log in the game folder.

Given that most people will probably want to customize the look of the chat, I've moved the function that draws a message to the very top of the module script.
It gets passed all relevant parameters:
Code: ags
DynamicSprite *CustomMessage(int chatWidth, Alignment align, String author, int hour, int minute, String text, FontType font) 

and must return a new DynamicSprite containing the message.

Preview of default look:
Spoiler
[close]
My example room script: http://pastebin.com/raw/BvruHqr7

Snarky

Nice one, Khris. Off topic, but can I ask how you went about recording/converting the GIF demo? When I've tried it in the past, the process I used was extremely tedious.

Khris

Thanks!
I used HyperCam to record gameplay, then used Zamzar to convert it: http://www.zamzar.com/convert/avi-to-gif
The recorded 640x480 video was about 7MB in size, and it only took a few minutes to get the email back from Zamzar (that contained the GIF download link).

Snarky


bulka_tarta

#16
Ok... WOW. A huge thanks to Khris, this is incredible!

With my last post, I thought I am getting better at programming, and that maybe I will eventually get this to work, but I didn't expect someone to actually create a module like this!

I saw the module yesterday morning, and I wanted to check it out so bad I couldn't wait until I finish work. When I sat down and tried to get it to work... Let's just say I had some problems. I didn't want to say anything on the forums, as I was embarrassed that I can't even get a module to work (apparently I'm not getting better at programming at all :P).

Anyway, I got it to work in the end (witchcraft? luck?), and I'm planning to dissect it over the weekend, hopefully learning something in the process.

Huge thanks, once again! I'm going to dig in into AGS now and maybe make something cool one day!


EDIT:
Ekhem.. So I went to try and create a new room with the chat dialogue and all I get is just a button with standard graphic displaying. To test if I'm not missing anything, I copied and pasted the code from the room where I got it actually working, but it just doesn't like the new room (same code works in the first room).

Spoiler

Code: ags
int chatOne;
String me, ma;

function room_Load()
{
  player.y = 0;
  
  chatOne = Chat.Create(btnChat, eFontFont3, 41);
  
  me = "*Tom";
  ma = "Jerry";
  
  Chat.Prepare(chatOne, "12321321312321",me);
  Chat.Prepare(chatOne, "vgdcvbdfbvdfbfbdfbdfb", ma);
  Chat.Prepare(chatOne, "13213213213213213213", me);
  Chat.Prepare(chatOne, "fgdfgdfgdfgdfgdfgda", ma);
  Chat.Prepare(chatOne, "3423423", me);
}

function room_RepExec()
{
String lm, nm;
  if (IsTimerExpired(1)) {
    Chat.Advance(chatOne);
    lm = Chat.GetLastMessage(chatOne);
    nm = Chat.GetNextMessage(chatOne);
    if (nm != null) SetTimer(1, (GetGameSpeed() * (lm.Length + nm.Length)) / 20);
    else SetTimer(2, 80);
  }
}
[close]

I followed the module instructions, and I also followed the example room code. The above works like it should in one room, but it doesn't work at all in another. I don't get any error messages, the chat is just not displaying.
Also... When I tried copying the example room script into one of the rooms I get error: "Unable to create local script: Runtime error: unresolved import 'btnFirstChat'" - I made a GUI that includes a "btnFirstChat" button, so it shouldn't be causing a problem...

Anyway, I feel bad enough for taking Khris' time to do the module, so I will try and work out what the problem depends on - I got it working once, so I can do this again! <extremely determined>

EDIT2: This is really silly. Whenever I do a script in a new room, I did copy the code over, but I forgot to set up the right Events on the room properties - sorry about that!!!

Khris

You're welcome, and writing this was fun ;-D

EDIT2 sounds like you got it to work?

bulka_tarta

Yes, it's all working!  :-D

I'm currently working out how how to add more message to the active chat though. So for example, I start the chat with couple of messages. Later, the player does x,y and z, and I want to add more messages to the chat to continue the dialogue. I'm also working on simple "yes" or "no" dialogue options - or in other words, if player clicks button "X", the chat will display "abc". If player clicks "Y", chat displays "123".

The thing is... If I simply do "Chat.Add" it clears all the messages and creates new ones. Same thing if I do "Chat.Prepare" and then "Chat.Advance".  I only want the chat to clear, and start a new one (with a different character), once I tell it to.

Anyway, I think I should be able to work something out.

Khris

It works fine when I test it.

I assume you're adding messages in a new room and have copied the initialization code to the new room script? That's not necessary, and will in fact create a new, empty chat.

All you need to do is
a) use a global variable to store the chat instance (remove "int firstChat;" from the top of the room script, then add it back as global variable)
b) in the next room, call Chat.Add(firstChat, ...)

Btw, if you're only going to have a single chat in the entire game (as opposed to two or more being visible at the same time), you can simply use 0 (the number zero) instead of firstChat.

SMF spam blocked by CleanTalk