CLIENT: Handle chat in CSQC

This commit is contained in:
cypress 2023-11-19 12:45:50 -05:00
parent fe547187e0
commit bee0e764fc
6 changed files with 202 additions and 5 deletions

View file

@ -8,4 +8,5 @@
../source/client/menu.qc ../source/client/menu.qc
../source/client/achievements.qc ../source/client/achievements.qc
../source/client/hud.qc ../source/client/hud.qc
../source/client/chat.qc
../source/client/main.qc ../source/client/main.qc

143
source/client/chat.qc Normal file
View file

@ -0,0 +1,143 @@
/*
client/chat.qc
Chat interception and custom drawing, based on Nuclide.
Copyright (C) 2021-2023 NZ:P Team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
*/
var float autocvar_cg_chatEnabled = true;
#define CHAT_LINES 10
#define CHAT_TIME 7
var int g_chatpos[2];
var float g_chattime;
var int g_chatlines = -1;
string g_chatbuffer[CHAT_LINES];
string g_chatbuffer_final;
/*
* called every frame pretty much and prints whatever is in the chatbuffer.
* removes lines after some time, one at a time.
*/
void Chat_Draw()
{
int i;
if (autocvar_cg_chatEnabled == false)
return;
g_chatpos[0] = GetUltraWideOffset() + 5;
g_chatpos[1] = 170;
if (g_chatlines < 0) {
return;
}
/* remove messages after a g_chattime has passed */
if (g_chattime < time) {
g_chatbuffer_final = "";
for (i = 0; i < g_chatlines; i++) {
if (g_chatbuffer[i+1] != __NULL__) {
g_chatbuffer[i] = g_chatbuffer[i+1];
}
if (i == 0)
g_chatbuffer_final = g_chatbuffer[i];
else
g_chatbuffer_final = sprintf("%s\n%s", g_chatbuffer_final, g_chatbuffer[i]);
}
g_chatbuffer[g_chatlines] = __NULL__;
g_chatlines--;
g_chattime = time + CHAT_TIME;
}
int barlines = 0;
for(i = 0; i <= g_chatlines; i++) {
if (g_chatbuffer[i] == __NULL__)
continue;
if (strlen(g_chatbuffer[i]) > 48) {
barlines += strlen(g_chatbuffer[i])/48 + 2;
} else {
barlines++;
}
}
drawfill ([g_chatpos[0] - 2, g_chatpos[1] - 2], [290, (barlines) * 8 + 4], [0, 0, 0], 0.65, 0);
drawtextfield([g_chatpos[0], g_chatpos[1]], [290, 280], 1 | 2, g_chatbuffer_final);
}
//
// Chat_GetHexCodeForPlayer(player_id)
// Returns a hex string for the Player Name color
//
string Chat_GetHexCodeForPlayer(float player_id)
{
switch(player_id) {
case 1: return "^xFFF";
case 2: return "^x07E";
case 3: return "^xCA0";
case 4: return "^x5D0";
}
return "^xFFF";
}
//
// Chat_Register(sender, player_id, message)
// Adds the chat message to the chat struct to be
// drawn.
//
void Chat_Register(int sender, int player_id, string message)
{
// Append name+color to the message
string player_hex = Chat_GetHexCodeForPlayer(player_id);
string player_name = getplayerkeyvalue(sender, "name");
message = strcat(player_hex, player_name, "^xCCC: ", message);
if (g_chatlines < (CHAT_LINES - 1)) {
g_chatbuffer[g_chatlines + 1] = message;
g_chatlines++;
} else {
for (int i = 0; i < (CHAT_LINES - 1); i++) {
g_chatbuffer[i] = g_chatbuffer[i + 1];
}
g_chatbuffer[CHAT_LINES - 1] = message;
}
g_chattime = time + CHAT_TIME;
/* we need to be silent */
if (autocvar_cg_chatEnabled == false)
return;
localsound("sounds/misc/talk2.wav");
g_chatbuffer_final = "";
for (int i = 0; i < CHAT_LINES; i++) {
if (i == 0)
g_chatbuffer_final = g_chatbuffer[i];
else
g_chatbuffer_final = sprintf("%s\n%s", g_chatbuffer_final, g_chatbuffer[i]);
}
}

View file

@ -3,7 +3,7 @@
main csqc code main csqc code
Copyright (C) 2021 NZ:P Team Copyright (C) 2021-2023 NZ:P Team
This program is free software; you can redistribute it and/or This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License modify it under the terms of the GNU General Public License
@ -1086,6 +1086,7 @@ noref void(float width, float height, float menushown) CSQC_UpdateView =
else else
{ {
HUD_Draw(g_width, g_height); HUD_Draw(g_width, g_height);
Chat_Draw();
} }
}; };
@ -1367,6 +1368,12 @@ noref void() CSQC_Parse_Event =
pointparticles(particleeffectnum("blood.gibs"), gloc, '0 0 0', 1); pointparticles(particleeffectnum("blood.gibs"), gloc, '0 0 0', 1);
} }
break; break;
case EVENT_CHATMESSAGE:
int sender = readbyte();
int player_id = readbyte();
string message = readstring();
Chat_Register(sender, player_id, message);
break;
case EVENT_WEAPONCHANGE: case EVENT_WEAPONCHANGE:
local float to; local float to;
to = readbyte(); to = readbyte();

View file

@ -536,6 +536,15 @@ void (float achievement_id, optional entity who) GiveAchievement =
#ifdef FTE #ifdef FTE
void CSQC_SendChatMessage(float player_id, string message) {
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
WriteByte(MSG_MULTICAST, EVENT_CHATMESSAGE);
WriteByte(MSG_MULTICAST, num_for_edict(self) - 1);
WriteByte(MSG_MULTICAST, player_id);
WriteString(MSG_MULTICAST, message);
multicast('0 0 0', MULTICAST_ALL);
}
/* /*
================= =================
Player_SendEntity Player_SendEntity

View file

@ -180,6 +180,26 @@ float(string params) Command_tracedmgmultiplier =
return COMMAND_SUCCESS; return COMMAND_SUCCESS;
} }
//
// Command_say()
// Sends message via CSQC to other clients (FTE-only)
//
float(string params) Command_say =
{
// Grab parameters.
tokenize(params);
string value = argv(0);
#ifdef FTE
CSQC_SendChatMessage(self.playernum, params);
#endif // FTE
client_parse_override = false;
return COMMAND_SUCCESS;
}
// //
// Server command table // Server command table
// command_name : Command string entered into developer console. // command_name : Command string entered into developer console.
@ -201,15 +221,31 @@ var struct {
{"god", Command_godmode, false, "Toggles God Mode.\n"}, {"god", Command_godmode, false, "Toggles God Mode.\n"},
{"noclip", Command_noclip, false, "Toggles No Clip.\n"}, {"noclip", Command_noclip, false, "Toggles No Clip.\n"},
{"sv_tracedmgmultiplier", Command_tracedmgmultiplier, true, "Multiplies damage output with weapons that fire Traces.\n"}, {"sv_tracedmgmultiplier", Command_tracedmgmultiplier, true, "Multiplies damage output with weapons that fire Traces.\n"},
{"spawn_pu", Command_powerup, true, "Spawns a Power-Up of a given ID in front of you. -1 for random.\n"} {"spawn_pu", Command_powerup, true, "Usage: spawn_pu <powerup>\n Spawns a Power-Up of a given ID in front of you. -1 for random.\n"},
{"say", Command_say, true, "Usage: say <message>\n Sends a chat message to CSQC for other clients.\n"}
}; };
//
// SV_IsCommand(command_string)
// Takes a single string and references if it is a
// registered command, returns true if it is. Useful
// for cl_chatmode interferring with custom commands.
//
float(string command_string) SV_IsCommand =
{
for (float i = 0; i < server_commands.length; i++) {
if (command_string == server_commands[i].command_name)
return true;
}
return false;
}
// //
// SV_ParseClientCommand(command_string) // SV_ParseClientCommand(command_string)
// Server-Side client command parser to add special // Server-Side client command parser to add special
// gameplay commands that interact with QuakeC. // gameplay commands that interact with QuakeC.
// //
void(string command_string) SV_ParseClientCommand = void(string command_string) SV_ParseClientCommand =
{ {
// If true, we will avoid sending the command info // If true, we will avoid sending the command info
@ -225,7 +261,7 @@ void(string command_string) SV_ParseClientCommand =
// Check for 'say' prefix (`cl_chatmode 2` will append it // Check for 'say' prefix (`cl_chatmode 2` will append it
// to everything unregistered by the client). Re-tokenize // to everything unregistered by the client). Re-tokenize
// if found. // if found.
if (command == "say") { if (command == "say" && SV_IsCommand(argv(1))) {
string fixed_command_string = argv(1); string fixed_command_string = argv(1);
tokenize(fixed_command_string); tokenize(fixed_command_string);
command = argv(0); command = argv(0);

View file

@ -58,6 +58,7 @@ const float EVENT_SONGPLAY = 41;
const float EVENT_GRENADEPULSE = 42; const float EVENT_GRENADEPULSE = 42;
const float EVENT_BETTYPROMPT = 43; const float EVENT_BETTYPROMPT = 43;
const float EVENT_LIMBGIB = 44; const float EVENT_LIMBGIB = 44;
const float EVENT_CHATMESSAGE = 45;
// Define our FTE platform // Define our FTE platform
#ifndef STANDARD #ifndef STANDARD