Inside3D!
     

CSQC Chat Messages

 
Post new topic   Reply to topic    Inside3d Forums Forum Index -> QuakeC Programming
View previous topic :: View next topic  
Author Message
GiffE



Joined: 08 Oct 2006
Posts: 141
Location: USA, CT

PostPosted: Fri Oct 10, 2008 3:23 pm    Post subject: CSQC Chat Messages Reply with quote

Something I did a few days ago:
This is not the most effective way of doing things but it sure is easy.
Also NOTE: I did not put any "flood control" on this yet, so it is not exactly secure.

It makes csqc and ssqc handle the chat messages, csqc draws it in the bottom left hand corner. It also fades after 10 seconds of no new messages.
This is a more cosmetic addition but it helps separate chat from death and server messages.
A similar approach can be done for the actual typing of say messages. This would require you to check for "say" in csqc then accept all keyboard strokes and draw a string.

I have tested this in Darkplaces only, I may have missed something when writing this although.

Here's the result:


First the server:
credit: Parts of this were taken from Nexiuz.

in client.qc
Code:
#define FOR_EACH_CLIENT(v) for(v = world; (v = findflags(v, flags, FL_CLIENT)) != world; )
#define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(clienttype(v) == CLIENTTYPE_REAL)
   
void Say(entity source, string msgin)
{
   local string msgstr;
   local entity head;
   msgstr = strzone(strcat("(say_msg)\{1}", source.netname, "^7: ", msgin, "\n"));
   FOR_EACH_REALCLIENT(head) if(head.classname == "player")
   {
            //sprint(head, msgstr);
            centerprint(head, msgstr);
   }

   strunzone(msgstr);
}

void(string s) SV_ParseClientCommand
{
      // Acquire Command
   local string strCmd;
   tokenize(s);
   strCmd = argv(0);
   switch(strCmd)
   {
      case "say":
         Say(self, substring(s, 4, strlen(s) - 1));
      break;
      default:
         clientcommand(self,s);
      break;
   }
}

It takes the message, tacks on "(say_msg)" and your name to the beginning, then centerprints it to the client.
Thats it for the server. Once again it lacks flood protection.

The rest of this is in CSQC:

At the top of Main.qc add:
Code:
float MAX_CHATMSG = 6;
string messagestack[MAX_CHATMSG];
float messagestack_fade;
float msg_alpha;

void push_message(string msg)
{
   local float i;
   i=MAX_CHATMSG-1;
   while (i>0)
   {
      messagestack[i]=messagestack[i-1];
      i--;
   }
   messagestack[i]=msg;
   msg_alpha = 1;
   messagestack_fade = time + 10;
}


All the messages will be stored in the messagestack array. So on a new message we can push out the old message to make room for the new.

in function: CSQC_Parse_CenterPrint(string strMessage) found in Main.qc:
Code:
// CSQC_Parse_CenterPrint : Provides the centerprint string in the first parameter that the server provided.  To execute standard behavior, simply execute cprint with the string.
void CSQC_Parse_CenterPrint(string strMessage)
{
   string msg,saymsg;
   saymsg = substring(strMessage,0,9);
   if(saymsg == "(say_msg)")
   {
      msg = substring(strMessage,9,strlen(strMessage));
      push_message(strzone(msg));
   }
   else
      cprint(strMessage);
}


The idea is server accepts the say command and puts "(say_msg)" in front of it. It then centerprints it to all the clients.
CSQC has the ability to parse centerprints so we can check if its a "say"

Now open up View.qc and at the top add the following function:
Code:
void DrawChat(void)
{
   local vector pos;
   local float i;
   pos_x = 20;
   pos_y = vid_conheight - 30;
   
  if(messagestack_fade < time && msg_alpha >0)
      msg_alpha -= 0.02;
      
   for(i=0;i<MAX_CHATMSG;i++)
   {
      if(messagestack[i] != "")
         drawcolorcodedstring(pos - ('0 10 0' * i), messagestack[i], '7 7', msg_alpha, FALSE);
   }
}



Find the function void CSQC_UpdateView(void). After:
Code:
R_RenderScene();


add:
Code:
DrawChat();


And you are done Very Happy

If your having trouble getting the parse centerprint function to work this is because the csqc base code by default has it commented out.
make sure you:
#define USE_CSQC_OPTIONALFUNCTIONS
Also this method lacks the talk sound, this can be easily solved by adding a pointsound of "sound/misc/talk.wav"

Its more of a method than anything else, and still needs work (say_team and flood). But, Hope it helps anyone Very Happy
_________________
http://www.giffe-bin.net/
Back to top
View user's profile Send private message Visit poster's website
Spike



Joined: 05 Nov 2004
Posts: 944
Location: UK

PostPosted: Fri Oct 10, 2008 10:14 pm    Post subject: Reply with quote

Does it still work if they're chatting in the console, and not using message mode stuff?
Other than that, cool!

Don't forget to add support for smilies! :D
_________________
What's a signature?
Back to top
View user's profile Send private message Visit poster's website
Wazat



Joined: 15 Oct 2004
Posts: 732
Location: Middle 'o the desert, USA

PostPosted: Fri Oct 10, 2008 11:44 pm    Post subject: Reply with quote

This is cool. It adds shiny new pvp talk that stands out from all the other chatter and server messages.
_________________
When my computer inevitably explodes and kills me, my cat inherits everything I own. He may be the only one capable of continuing my work.
Back to top
View user's profile Send private message MSN Messenger
GiffE



Joined: 08 Oct 2006
Posts: 141
Location: USA, CT

PostPosted: Sat Oct 11, 2008 12:06 am    Post subject: Reply with quote

Spike wrote:
Does it still work if they're chatting in the console, and not using message mode stuff?
Other than that, cool!

Don't forget to add support for smilies! Very Happy


Never tested but I don't see why it wouldn't work. It uses the say command.
_________________
http://www.giffe-bin.net/
Back to top
View user's profile Send private message Visit poster's website
GiffE



Joined: 08 Oct 2006
Posts: 141
Location: USA, CT

PostPosted: Thu Jun 17, 2010 3:38 am    Post subject: Reply with quote

Here's an update to this code if anyone still wants to use it Very Happy I ported over the flood protect control from Nexuiz. I also added a rudimentary word wrap.

NOTE: I changed the cvars to hard coded values. Obviously you can uncomment these and make them work via cvar.

Replace the Say method with:
Code:

#define WORDWRAP_MAX 50
.float floodcontrol_chat;
void Say(entity source, string msgin)
{
   local string msgstr;
   local entity head;
   local float flood;
   
   if(msgin == "" || msgin == " ")
      return;
   
   msgstr = strzone(strcat("(say_msg)", source.netname, "^7: ", msgin));
   // FLOOD CONTROL
   flood = 0;
   {
      float flood_spl, flood_burst, flood_lmax,lines;
      flood_spl = 3;//cvar("g_chat_flood_spl");
      flood_burst = 2;//max(0,cvar("g_chat_flood_burst") - 1);
      flood_lmax = 2; //cvar("g_chat_flood_lmax");
      
      // to match explanation in default.cfg, a value of 3 must allow three-line bursts and not four!
      lines = ceil(strlennocol(msgstr) / WORDWRAP_MAX);
      if(lines > flood_lmax )
         flood = 2;
      else if(time >= self.floodcontrol_chat)
         self.floodcontrol_chat = max(time - flood_burst * flood_spl, self.floodcontrol_chat) + lines * flood_spl;
      else
         flood = 1;
   }
   
   if(flood)
   {
      if(flood == 1)
         sprint(self, strcat("^3FLOOD CONTROL: ^7wait ^1", ftos(self.floodcontrol_chat - time), "^3 seconds\n"));
      else if(flood == 2)
         sprint(self, "^3FLOOD CONTROL: ^7message too long\n");
   }
   else {
      FOR_EACH_REALCLIENT(head) if(head.classname == "player" || head.classname == "spectator")
      {
         centerprint(head, msgstr);
      }
   }
   strunzone(msgstr);
}


Then replace CSQC_Parse_CenterPrint with:
Code:

#define WORDWRAP_MAX 50
void CSQC_Parse_CenterPrint(string strMessage)
{
   string msg;
   float f,i;
   f = strstrofs(strMessage,"(say_msg)",0);
   if(f != -1) //right at the begining
   {
      msg = substring(strMessage,f+9,strlen(strMessage));
      f = ceil(strlennocol(msg) / WORDWRAP_MAX);
      for(i=0;i<f;i++) {
         if(strlennocol(msg) > WORDWRAP_MAX)
            push_message(strzone(substring(msg,i*WORDWRAP_MAX,WORDWRAP_MAX)));
         else
            push_message(strzone(substring(msg,i*WORDWRAP_MAX,-1)));
      }
   }
   else
      cprint(strMessage);
}

_________________
http://www.giffe-bin.net/
Back to top
View user's profile Send private message Visit poster's website
GiffE



Joined: 08 Oct 2006
Posts: 141
Location: USA, CT

PostPosted: Sat Jun 19, 2010 3:11 pm    Post subject: Reply with quote



To have csqc handle the message mode, a good amount of work has to be done to get the keypresses to translate into their character, as holding shift does not change the key value you have to do it manually.
Code:

float shiftheld;
float capslock;
string toUpper(float chr) {
   string ltr;
   if(chr > 96 && chr < 122)
      ltr = strtoupper(chr2str(chr));
   else if(chr > 43 && chr <= 57)
      ltr = substring("<_>?)!@#$%^&*(",chr - 44,1);
   else {
      switch(chr) {
         case 61: ltr = "+"; break;
         case 92: ltr = "|"; break;
         case 96: ltr = "~"; break;
         case 91: ltr = "{"; break;
         case 93: ltr = "}"; break;
         case 59: ltr = ":"; break;
         case 39: ltr = "\""; break;
         case 32: ltr = " "; break;
      }
   }
   return ltr;
}
string lowerChr(float chr) {
   if(chr > 126 && chr < 134)
      return string_null;
   if(chr > 146 && chr < 154)
      return string_null;
   switch(chr) {
      case 9:
         return string_null;
   }
   return chr2str(chr);
}


// CSQC_InputEvent : Used to perform actions based on any key pressed or released by the client.
// Return value should be 1 if CSQC handled the key press, otherwise return 0 to have the key passed to the engine.
float CSQC_InputEvent(float bKeyReleased, float nKey)
{
   local float bSkipKey;
      bSkipKey = false;
      
   if(messagemode) {
      bSkipKey = true;
      if(nKey == 27 || nKey == 13) { // ESC
         messagemode = false;
         if(nKey == 13) {
            localcmd("cmd say ",msg_txt,"\n");
            msg_txt = string_null;
         }
         return bSkipKey;
      }
      if(nKey == 127 && !bKeyReleased) {
         msg_txt = strzone(substring(msg_txt,0,-2));
      }
      if(nKey == 134) // Shift
         shiftheld = (!bKeyReleased);
      if(nKey == 0 && (!bKeyReleased))
         capslock = (capslock) ? 0 : 1;
      if(!bKeyReleased) {
         string chr;
         chr = (shiftheld || (capslock && nKey > 96 && nKey < 122)) ? toUpper(nKey) : lowerChr(nKey);
         chr = strcat(msg_txt,chr);
         if(msg_txt)
            strunzone(msg_txt);
         msg_txt = strzone(chr);
      }
   }
   
   return bSkipKey;
}


Then for the drawing:
Code:
void DrawMessageMode(void)
{
   local vector pos;
   pos = Vec2f(15,vid_size_y*0.5);
   drawfill(pos, Vec2f(vid_size_x - 30,15), COLOR_BLACK, 0.7, 0);
   pos_y = pos_y + 3.5;
   drawfont = FONT_INFOBAR;
   drawstring(pos + '3.5 0', "Say:", '8 8', COLOR_WHITE, 1, 0);
   pos_x = pos_x + 35.5;
   drawcolorcodedstring(pos, msg_txt, '8 8', 1, FALSE);
   drawfont = FONT_DEFAULT;
};


This code needs support for scrolling or a hard limit, as if his sentence gets longer it will just keep on going off the screen and not scroll with it. This should be handled in the draw function
Also, this code doesn't have support for a cursor so you can only backspace from the end and cannot go back otherwise.

Also, I noticed my old code for chat messages doesn't ever unzone the strings.

add:
Code:

if(messagestack[MAX_CHATMSG-1])
      strunzone(messagestack[MAX_CHATMSG-1]);

to void push_message(string msg)
right above
Code:
messagestack[i]=msg;
   msg_alpha = 1;

_________________
http://www.giffe-bin.net/
Back to top
View user's profile Send private message Visit poster's website
Display posts from previous:   
Post new topic   Reply to topic    Inside3d Forums Forum Index -> QuakeC Programming All times are GMT
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2004 phpBB Group