Chapter XI
Chat
It's time for us to add the ability for the players to call each other names and brag about how great they are. In multiplayer games the players obviously should be able to communicate with each other somehow. Otherwise, a multiplayer game could get aweful boring real fast. The player hits Enter to start chat entry and hits Enter again to process entry.
Let's make a chat panel that will show the last 8 chat entries, as well as, the players current chat entry. This actually takes a bit of figuring to get the chat panel to the correct size depending on what font you are using. I usually do the strings first, get them to where they are displaying like I like, then I play around with the panel until I get it sized right to cover all possible string data. After that, I would actually skin the chat panel how I would like it to look. So that is what I did with the chat panel. Like everything in the tutorial I made the chat panel simple to save time.
Let's go ahead and declare the strings we will need for our chat panel in declaraition under Strings.
string str_player_num; // string to hold player number
string str_number_of_players; // string to hold number of players
// Chat strings
string strChat0[100]; // Chat entries
string strChat1[100];
string strChat2[100];
string strChat3[100];
string strChat4[100];
string strChat5[100];
string strChat6[100];
string strChat7[100];
string strChat8[100];
string strChatEntry[80]; // current chat entry
string strSendChat[100]; // chat to send
Next we will declare the text we will need to display the strings in decalrations under Text.
font fnt_century12;
string str_number_of_players;
}
// text for chat panel
text txtChat
{
pos_x = 2;
pos_y = 0;
layer = 30;
font _a4font;
strings = 9;
string = strChat0, strChat1, strChat2, strChat3, strChat4,
strChat5, strChat6, strChat7, strChat8;
}
// text for chat entry
text txtChatEntry
{
pos_x = 2;
pos_y = 0;
layer = 30;
font _a4font;
string strChatEntry;
}
Now let's add our chat panel pcx declaration under Resources.
// Bmaps
bmap pcxArrow = <arrow.pcx>; // pointer arrow
bmap bmpNuclear = <nuclear.bmp>; // nuclear scientiest panel
bmap bmpBiological = <biological.bmp>; // bio scientist panel
bmap bmpOfficer = <officer.bmp>; // op officer panel
bmap pcxChat = <Chat.pcx>; // Chat Panel
After that we declare our actual chat panel under Panels.
// panel select
panel pnlSelect
{
bmap = pcxSelect;
layer = 18;
pos_x = 0;
pos_y = 0;
flags = overlay,transparent,refresh;
}
// chat panel
panel pnlChat
{
bmap = pcxChat;
layer = 18;
pos_x = 0;
pos_y = 0;
flags = overlay,transparent,refresh;
}
Notice how the transparent flag is set in the chat panel. This will allow the player to see the level through the panel so it won't block his view as much.
Now that we have all of our declarations done, let's intialize our chat panel. Add function init_chat() under function input_scan(). This function will be called during game set up.
wait(1);
}
}
//--------------------------------------------------------------------
// Init_Chat - Initializ chat panel
//--------------------------------------------------------------------
function init_chat()
{
// if host or client set up chat
if (connection == 2 || connection == 3)
{
pnlChat.pos_y = screen_size.y - 100;
txtChat.pos_y = screen_size.y - 100;
txtChatEntry.pos_y = screen_size.y - 15;
pnlChat.visible = ON;
txtChat.visible = ON;
txtChatEntry.visible = OFF;
}
}
In this function we first check if the player's connection is Client or Host. If a player's connection is Server or Single Player we don't need chat so we would skip intitializing it. Then we intiatial where we want our chat panel and text to be located, at the bottom of the screen in this case. Then we turn on the appropriate panels and text. We leave txtChatEntry off until a player is actually typing in an entry line.
Let's add the call to this function in main().
create_player(); // create player
move_camera(); // call simple 3rd Person chase camera
input_scan(); // get inputs
init_chat(); // intitialize chat variables
Now let's add the chat_entry() function right beneath function init_chat().
txtChat.visible = ON;
txtChatEntry.visible = OFF;
}
}
//--------------------------------------------------------------------
// Chat_Entry - get chat input from player
//--------------------------------------------------------------------
function chat_entry()
{
// if host or client
if (connection == 2 || connection == 3)
{
// if inkey already open return
if (inkey_active == TRUE)
{
return;
}
txtChatEntry.visible = ON;
inkey(strChatEntry); // get chat entry
// if entry was empty don't process
if(str_cmp(strChatEntry,""))
{
return;
}
txtChatEntry.visible = OFF;
str_cpy(strSendChat,player_name); // put players name before entry
str_cat(strSendChat,": ");
str_cat(strSendChat,strChatEntry);
// if Host go ahead and do chat manipulation
if (connection == 3)
{
send_string(strSendChat);
str_cpy(strChat0, strChat1);
str_cpy(strChat1, strChat2);
str_cpy(strChat2, strChat3);
str_cpy(strChat3, strChat4);
str_cpy(strChat4, strChat5);
str_cpy(strChat5, strChat6);
str_cpy(strChat6, strChat7);
str_cpy(strChat7, strChat8);
str_cpy(strChat8, strSendChat);
str_cpy(strSendChat,"");
}
else // if client send entry to server for processing
{
send_string(strSendChat);
}
str_cpy(strSendChat,""); // clear the strings for client and
str_cpy(strChatEntry,""); // server called functions
}
}
This is where the player will input his chat entries. He first hit's <Enter> to activate the chat entry (We will add a on_enter = function; to make that happen here soon.), then he types in his entry and hit's <Enter> again to send message.
First we check our connection to see if it a client or host connection. If it is, we allow chat to be entered. Single player and dedicated server do not actually use chat, of course you could change this if you like so a single player can chat with himself (hmmm&ldots;) or chat to NPC's perhaps.
// if host or client
if (connection == 2 || connection == 3)
After that, we check to make sure inkey function isn't already active. If inkey is active we exit function because we are already typing an antery. This probably doesn't need to be here, but since I am not totally familiar with the inkey function itself, I thought better safe than sorry.
// if inkey already open return
if (inkey_active == TRUE)
{
return;
}
Now, since the player's chat is active, we make the chat entry text visible, txtChatEntry.visible = ON; // make chat entry text visible. Then we get the entry by using the inkey fucntion, inkey(strChatEntry); // get chat entry via inkey. When the entry has been made, then we make the text invisible again, txtChatEntry.visible = OFF; // after entry make text invisible.
Ok now we get the player's name using the 3DGS variable player_name. A player's name can be set in the command line when first running game. For an example, a host of a game playing on LAN with his name being Fred, would look like this:
-cl -sv -pl Fred
Now the player's name in the game would be Fred (See Chapter V: Starting a Multiplayer Game). Now, since we have the player's name, we want to add that to the beginning of each entry that shows the last 8 entries in queuve, that way, everyone knows who said what, str_cpy(strSendChat,player_name); // place player name first. Then we add a colon, str_cat(strSendChat,": ");. Then we add the chat entry itself after that, str_cat(strSendChat,strChatEntry); // followed by entry.
Ok we now have the chat entry for the player, but we need to consider how we are going to send and process this information.
Let's consider the Host first. The Host will process the chat information he entered immediately and send the chat entry to all the clients, who will process the chat entry locally using on_client and if (event_type == event_string). We will covering that next.
The Clients will do it differently, they will imediately send the chat entry to Server, who will process it locally and send it back all the Clients, and then the Clients will process it locally. Why do it this way? Let's say a Client player made a text entry, processed it and sent it to Server, the Server still has to send the chat entry back to all the other Clients. Which means it will have to send the data back to the Client who entered it and that Client will process it again. So we do it this way, so the Client who sent the chat entry isn't processing the entry twice locally.
So, if we are host, send the chat entry to clients to be processed, the process the entry locally. If we are a client, send the chat entry to the server.
// if Host go ahead and do chat manipulation
if (connection == 3)
{
send_string(strSendChat);
str_cpy(strChat0, strChat1);
str_cpy(strChat1, strChat2);
str_cpy(strChat2, strChat3);
str_cpy(strChat3, strChat4);
str_cpy(strChat4, strChat5);
str_cpy(strChat5, strChat6);
str_cpy(strChat6, strChat7);
str_cpy(strChat7, strChat8);
str_cpy(strChat8, strSendChat);
str_cpy(strSendChat,"");
}
else // if client send entry to server for processing
{
send_string(strSendChat);
}
str_cpy(strSendChat,""); // clear the strings for client and
str_cpy(strChatEntry,""); // server called functions
By processing, I mean that we are taking the chat entry and placing it at bottom of queuve, moving all other old entries up one in the queve.
It is time to add the code which will actually call the chat_entry() function when the enter key is hit. At the very bottom of wdl file add this line of code.
on_server = server_called; // on server event, call server_called()
on_enter = chat_entry; // on <enter> start chat entry
Now that we have the chat_entry() itself done, but we still need to handle the sending of the entry itself. When the Server sends the entry to be processed by Clients, we need to implement this somehow.
We will use the on _client = function command. We used the on_server = function command earlier. The difference between the on_client and on_server is that the on_client will be called when a Client is sent data (The exception being "The Client is the Server Stupid!" rule).
Let's go ahead and add the function client_called() to our script under server_called().
// some one disconnected
if (event_type == event_leave)
{
ifdef server;
people_connected -= 1; // one less person connected to server
send_var(people_connected); // send number of people connected
endif;
}
}
//--------------------------------------------------------------------
// Function Client_Called(): client was called
//--------------------------------------------------------------------
function client_called()
{
// string event
if (event_type == event_string)
{
// chat entry sent from server, process
if (!str_cmp(strSendChat,""))
{
str_cpy(strChat0, strChat1); // move old chat entries up 1
str_cpy(strChat1, strChat2);
str_cpy(strChat2, strChat3);
str_cpy(strChat3, strChat4);
str_cpy(strChat4, strChat5);
str_cpy(strChat5, strChat6);
str_cpy(strChat6, strChat7);
str_cpy(strChat7, strChat8);
str_cpy(strChat8, strSendChat);
str_cpy(strSendChat,"");
}
}
}
Alrighty, this function will be called when a client is sent data. First we check if the event that got us here was and event_string, meaning a string was sent. If the event was an event_string, we make sure the string is not empty. If it is not empty we place the new entry at the bottom of queve and move all the old entries up one in queve.
Now let's add the call to this function at the bottom of our script file with all of the other on_ command calls.
on_server = server_called; // on server event, call server_called()
on_client = client_called; // on client event, call cleint_called()
on_enter = chat_entry; // on <enter> start chat entry
Now, there is still one more chat entry send we need to handle, when the Client sends the string to the Server. We will use the on_server = function command to handle this. So add the following script to the server_called() function.
// some one disconnected
if (event_type == event_leave)
{
ifdef server;
people_connected -= 1; // one less person connected to server
send_var(people_connected); // send number of people connected
endif;
}
// chat stuff
if (event_type == event_string)
{
if (!str_cmp(strSendChat,""))
{
ifdef server;
send_string(strSendChat); // send string to clients
str_cpy(strChat0, strChat1); // move old chat entries up 1
str_cpy(strChat1, strChat2);
str_cpy(strChat2, strChat3);
str_cpy(strChat3, strChat4);
str_cpy(strChat4, strChat5);
str_cpy(strChat5, strChat6);
str_cpy(strChat6, strChat7);
str_cpy(strChat7, strChat8);
str_cpy(strChat8, strSendChat);
str_cpy(strSendChat,"");
endif;
}
}
There isn't much differnce between the Server getting sent a chat string and the Client, except the Server imediately sends the chat string to all the clients - send_string(strSendChat); // send string to clients.
So, are you ready for a test run? We need to add something to our command line now at game start. It is the -pl PlayerName command. So in WED when you hit Run icon, change the command line to -cl -sv -pl PlayerName. Also in SED for a Test Run you would need to go to Options-Configuration and change the comman line there too. Now when you Publish or Resource and copy over to your secondary computer you will also need to edit the shortcut, changing the command line for the Client to -cl -pl PlayerName.
So give it a test run. You should now be able to chat like this.
Well, I suppose since our player's actually have names now we should go ahead and add some text to diplay that also. Add this string in decalaration under Strings.
string str_player_num; // string to hold player number
string str_number_of_players; // string to hold number of players
string str_player_name; // player name for text display
In declarations add this text declaration under Text.
string strChatEntry;
}
// text to display this player's name
text txt_player_name
{
pos_x = 5;
pos_y = 5;
layer = 15;
font fnt_century12;
string str_player_name;
flags = visible;
}
In display_info() add this code.
function display_info()
{
while(1)
{
str_cpy(str_player_name, "Player Name: ");
str_cat(str_player_name, player_name);
Also add this code to main().
move_camera(); // call simple 3rd Person chase camera
input_scan(); // get inputs
init_chat(); // intitialize chat variables
// wait for this player's entity to be created
while(!player)
{
wait(1);
}
After all of that go ahead and test run on Host, you should now see something like this.
Previous Page Contents Next Page