]> git.rm.cloudns.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Move server-side chat handling to its own file, and add a note about miscfunctions
authorMario <mario.mario@y7mail.com>
Fri, 25 Sep 2020 07:58:16 +0000 (17:58 +1000)
committerMario <mario.mario@y7mail.com>
Fri, 25 Sep 2020 07:58:16 +0000 (17:58 +1000)
12 files changed:
qcsrc/common/effects/qc/globalsound.qh
qcsrc/server/_mod.inc
qcsrc/server/_mod.qh
qcsrc/server/chat.qc [new file with mode: 0644]
qcsrc/server/chat.qh [new file with mode: 0644]
qcsrc/server/client.qc
qcsrc/server/client.qh
qcsrc/server/command/cmd.qc
qcsrc/server/command/common.qc
qcsrc/server/matrix.qc
qcsrc/server/miscfunctions.qc
qcsrc/server/miscfunctions.qh

index 5460d72ac07d90671728f64e9a46e8d696fc4aec..cec501c5589f0c2fd41c9cdae784cb5a11e16edc 100644 (file)
@@ -1,5 +1,9 @@
 #pragma once
 
+#if defined(SVQC)
+       #include <server/chat.qh>
+#endif
+
 #ifdef SVQC
        /** Use new sound handling. TODO: use when sounds play correctly on clients */
        bool autocvar_g_debug_globalsounds = false;
index cdd7d0d26389ba51ef98a5f761f432734d4581c2..d6bc0528b834b40f4d5c91722bf3468c408a597c 100644 (file)
@@ -2,6 +2,7 @@
 #include <server/anticheat.qc>
 #include <server/antilag.qc>
 #include <server/campaign.qc>
+#include <server/chat.qc>
 #include <server/cheats.qc>
 #include <server/client.qc>
 #include <server/clientkill.qc>
index 7d1728f80f32965131c5848c47ce93140a6b458e..e881b403d7e9b0648860b11126655940a93c5900 100644 (file)
@@ -2,6 +2,7 @@
 #include <server/anticheat.qh>
 #include <server/antilag.qh>
 #include <server/campaign.qh>
+#include <server/chat.qh>
 #include <server/cheats.qh>
 #include <server/client.qh>
 #include <server/clientkill.qh>
diff --git a/qcsrc/server/chat.qc b/qcsrc/server/chat.qc
new file mode 100644 (file)
index 0000000..2ed7fa1
--- /dev/null
@@ -0,0 +1,579 @@
+#include "chat.qh"
+
+#include <common/gamemodes/_mod.qh>
+#include <common/mapobjects/target/location.qh>
+#include <common/mapobjects/triggers.qh>
+#include <common/teams.qh>
+#include <common/util.qh>
+#include <common/weapons/weapon.qh>
+#include <common/wepent.qh>
+#include <server/command/common.qh>
+#include <server/gamelog.qh>
+#include <server/main.qh>
+#include <server/mapvoting.qh>
+#include <server/miscfunctions.qh>
+
+/**
+ * message "": do not say, just test flood control
+ * return value:
+ *   1 = accept
+ *   0 = reject
+ *  -1 = fake accept
+ */
+int Say(entity source, int teamsay, entity privatesay, string msgin, bool floodcontrol)
+{
+       if (!teamsay && !privatesay && substring(msgin, 0, 1) == " ")
+               msgin = substring(msgin, 1, -1); // work around DP say bug (say_team does not have this!)
+
+       if (source)
+               msgin = formatmessage(source, msgin);
+
+       string colorstr;
+       if (!(IS_PLAYER(source) || source.caplayer))
+               colorstr = "^0"; // black for spectators
+       else if(teamplay)
+               colorstr = Team_ColorCode(source.team);
+       else
+       {
+               colorstr = "";
+               teamsay = false;
+       }
+
+       if (!source) {
+               colorstr = "";
+               teamsay = false;
+       }
+
+       if(msgin != "")
+               msgin = trigger_magicear_processmessage_forallears(source, teamsay, privatesay, msgin);
+
+       /*
+        * using bprint solves this... me stupid
+       // how can we prevent the message from appearing in a listen server?
+       // for now, just give "say" back and only handle say_team
+       if(!teamsay)
+       {
+               clientcommand(source, strcat("say ", msgin));
+               return;
+       }
+       */
+
+       string namestr = "";
+       if (source)
+               namestr = playername(source, autocvar_g_chat_teamcolors);
+
+       string colorprefix = (strdecolorize(namestr) == namestr) ? "^3" : "^7";
+
+       string msgstr = "", cmsgstr = "";
+       string privatemsgprefix = string_null;
+       int privatemsgprefixlen = 0;
+       if (msgin != "")
+       {
+               bool found_me = false;
+               if(strstrofs(msgin, "/me", 0) >= 0)
+               {
+                       string newmsgin = "";
+                       string newnamestr = ((teamsay) ? strcat(colorstr, "(", colorprefix, namestr, colorstr, ")", "^7") : strcat(colorprefix, namestr, "^7"));
+                       FOREACH_WORD(msgin, true,
+                       {
+                               if(strdecolorize(it) == "/me")
+                               {
+                                       found_me = true;
+                                       newmsgin = cons(newmsgin, newnamestr);
+                               }
+                               else
+                                       newmsgin = cons(newmsgin, it);
+                       });
+                       msgin = newmsgin;
+               }
+
+               if(privatesay)
+               {
+                       msgstr = strcat("\{1}\{13}* ", colorprefix, namestr, "^3 tells you: ^7");
+                       privatemsgprefixlen = strlen(msgstr);
+                       msgstr = strcat(msgstr, msgin);
+                       cmsgstr = strcat(colorstr, colorprefix, namestr, "^3 tells you:\n^7", msgin);
+                       privatemsgprefix = strcat("\{1}\{13}* ^3You tell ", playername(privatesay, autocvar_g_chat_teamcolors), ": ^7");
+               }
+               else if(teamsay)
+               {
+                       if(found_me)
+                       {
+                               //msgin = strreplace("/me", "", msgin);
+                               //msgin = substring(msgin, 3, strlen(msgin));
+                               //msgin = strreplace("/me", strcat(colorstr, "(", colorprefix, namestr, colorstr, ")^7"), msgin);
+                               msgstr = strcat("\{1}\{13}^4* ", "^7", msgin);
+                       }
+                       else
+                               msgstr = strcat("\{1}\{13}", colorstr, "(", colorprefix, namestr, colorstr, ") ^7", msgin);
+                       cmsgstr = strcat(colorstr, "(", colorprefix, namestr, colorstr, ")\n^7", msgin);
+               }
+               else
+               {
+                       if(found_me)
+                       {
+                               //msgin = strreplace("/me", "", msgin);
+                               //msgin = substring(msgin, 3, strlen(msgin));
+                               //msgin = strreplace("/me", strcat(colorprefix, namestr), msgin);
+                               msgstr = strcat("\{1}^4* ^7", msgin);
+                       }
+                       else {
+                               msgstr = "\{1}";
+                               msgstr = strcat(msgstr, (namestr != "") ? strcat(colorprefix, namestr, "^7: ") : "^7");
+                               msgstr = strcat(msgstr, msgin);
+                       }
+                       cmsgstr = "";
+               }
+               msgstr = strcat(strreplace("\n", " ", msgstr), "\n"); // newlines only are good for centerprint
+       }
+
+       string fullmsgstr = msgstr;
+       string fullcmsgstr = cmsgstr;
+
+       // FLOOD CONTROL
+       int flood = 0;
+       var .float flood_field = floodcontrol_chat;
+       if(floodcontrol && source)
+       {
+               float flood_spl, flood_burst, flood_lmax;
+               if(privatesay)
+               {
+                       flood_spl = autocvar_g_chat_flood_spl_tell;
+                       flood_burst = autocvar_g_chat_flood_burst_tell;
+                       flood_lmax = autocvar_g_chat_flood_lmax_tell;
+                       flood_field = floodcontrol_chattell;
+               }
+               else if(teamsay)
+               {
+                       flood_spl = autocvar_g_chat_flood_spl_team;
+                       flood_burst = autocvar_g_chat_flood_burst_team;
+                       flood_lmax = autocvar_g_chat_flood_lmax_team;
+                       flood_field = floodcontrol_chatteam;
+               }
+               else
+               {
+                       flood_spl = autocvar_g_chat_flood_spl;
+                       flood_burst = autocvar_g_chat_flood_burst;
+                       flood_lmax = autocvar_g_chat_flood_lmax;
+                       flood_field = floodcontrol_chat;
+               }
+               flood_burst = max(0, flood_burst - 1);
+               // to match explanation in default.cfg, a value of 3 must allow three-line bursts and not four!
+
+               // do flood control for the default line size
+               if(msgstr != "")
+               {
+                       getWrappedLine_remaining = msgstr;
+                       msgstr = "";
+                       int lines = 0;
+                       while(getWrappedLine_remaining && (!flood_lmax || lines <= flood_lmax))
+                       {
+                               msgstr = strcat(msgstr, " ", getWrappedLineLen(82.4289758859709, strlennocol)); // perl averagewidth.pl < gfx/vera-sans.width
+                               ++lines;
+                       }
+                       msgstr = substring(msgstr, 1, strlen(msgstr) - 1);
+
+                       if(getWrappedLine_remaining != "")
+                       {
+                               msgstr = strcat(msgstr, "\n");
+                               flood = 2;
+                       }
+
+                       if (time >= source.(flood_field))
+                       {
+                               source.(flood_field) = max(time - flood_burst * flood_spl, source.(flood_field)) + lines * flood_spl;
+                       }
+                       else
+                       {
+                               flood = 1;
+                               msgstr = fullmsgstr;
+                       }
+               }
+               else
+               {
+                       if (time >= source.(flood_field))
+                               source.(flood_field) = max(time - flood_burst * flood_spl, source.(flood_field)) + flood_spl;
+                       else
+                               flood = 1;
+               }
+
+               if (timeout_status == TIMEOUT_ACTIVE) // when game is paused, no flood protection
+                       source.(flood_field) = flood = 0;
+       }
+
+       string sourcemsgstr, sourcecmsgstr;
+       if(flood == 2) // cannot happen for empty msgstr
+       {
+               if(autocvar_g_chat_flood_notify_flooder)
+               {
+                       sourcemsgstr = strcat(msgstr, "\n^3FLOOD CONTROL: ^7message too long, trimmed\n");
+                       sourcecmsgstr = "";
+               }
+               else
+               {
+                       sourcemsgstr = fullmsgstr;
+                       sourcecmsgstr = fullcmsgstr;
+               }
+               cmsgstr = "";
+       }
+       else
+       {
+               sourcemsgstr = msgstr;
+               sourcecmsgstr = cmsgstr;
+       }
+
+       if (!privatesay && source && !(IS_PLAYER(source) || source.caplayer) && !game_stopped
+               && (teamsay || CHAT_NOSPECTATORS()))
+       {
+               teamsay = -1; // spectators
+       }
+
+       if(flood)
+               LOG_INFO("NOTE: ", playername(source, true), "^7 is flooding.");
+
+       // build sourcemsgstr by cutting off a prefix and replacing it by the other one
+       if(privatesay)
+               sourcemsgstr = strcat(privatemsgprefix, substring(sourcemsgstr, privatemsgprefixlen, -1));
+
+       int ret;
+       if(source && CS(source).muted)
+       {
+               // always fake the message
+               ret = -1;
+       }
+       else if(flood == 1)
+       {
+               if (autocvar_g_chat_flood_notify_flooder)
+               {
+                       sprint(source, strcat("^3FLOOD CONTROL: ^7wait ^1", ftos(source.(flood_field) - time), "^3 seconds\n"));
+                       ret = 0;
+               }
+               else
+                       ret = -1;
+       }
+       else
+       {
+               ret = 1;
+       }
+
+       if (privatesay && source && !(IS_PLAYER(source) || source.caplayer) && !game_stopped
+               && (IS_PLAYER(privatesay) || privatesay.caplayer) && CHAT_NOSPECTATORS())
+       {
+               ret = -1; // just hide the message completely
+       }
+
+       MUTATOR_CALLHOOK(ChatMessage, source, ret);
+       ret = M_ARGV(1, int);
+
+       string event_log_msg = "";
+
+       if(sourcemsgstr != "" && ret != 0)
+       {
+               if(ret < 0) // faked message, because the player is muted
+               {
+                       sprint(source, sourcemsgstr);
+                       if(sourcecmsgstr != "" && !privatesay)
+                               centerprint(source, sourcecmsgstr);
+               }
+               else if(privatesay) // private message, between 2 people only
+               {
+                       sprint(source, sourcemsgstr);
+                       if (!autocvar_g_chat_tellprivacy) { dedicated_print(msgstr); } // send to server console too if "tellprivacy" is disabled
+                       if(!MUTATOR_CALLHOOK(ChatMessageTo, privatesay, source))
+                       {
+                               sprint(privatesay, msgstr);
+                               if(cmsgstr != "")
+                                       centerprint(privatesay, cmsgstr);
+                       }
+               }
+               else if ( teamsay && CS(source).active_minigame )
+               {
+                       sprint(source, sourcemsgstr);
+                       dedicated_print(msgstr); // send to server console too
+                       FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != source && CS(it).active_minigame == CS(source).active_minigame && !MUTATOR_CALLHOOK(ChatMessageTo, it, source), {
+                               sprint(it, msgstr);
+                       });
+                       event_log_msg = sprintf(":chat_minigame:%d:%s:%s", source.playerid, CS(source).active_minigame.netname, msgin);
+
+               }
+               else if(teamsay > 0) // team message, only sent to team mates
+               {
+                       sprint(source, sourcemsgstr);
+                       dedicated_print(msgstr); // send to server console too
+                       if(sourcecmsgstr != "")
+                               centerprint(source, sourcecmsgstr);
+                       FOREACH_CLIENT((IS_PLAYER(it) || it.caplayer) && IS_REAL_CLIENT(it) && it != source && it.team == source.team && !MUTATOR_CALLHOOK(ChatMessageTo, it, source), {
+                               sprint(it, msgstr);
+                               if(cmsgstr != "")
+                                       centerprint(it, cmsgstr);
+                       });
+                       event_log_msg = sprintf(":chat_team:%d:%d:%s", source.playerid, source.team, strreplace("\n", " ", msgin));
+               }
+               else if(teamsay < 0) // spectator message, only sent to spectators
+               {
+                       sprint(source, sourcemsgstr);
+                       dedicated_print(msgstr); // send to server console too
+                       FOREACH_CLIENT(!(IS_PLAYER(it) || it.caplayer) && IS_REAL_CLIENT(it) && it != source && !MUTATOR_CALLHOOK(ChatMessageTo, it, source), {
+                               sprint(it, msgstr);
+                       });
+                       event_log_msg = sprintf(":chat_spec:%d:%s", source.playerid, strreplace("\n", " ", msgin));
+               }
+               else
+               {
+                       if (source) {
+                               sprint(source, sourcemsgstr);
+                               dedicated_print(msgstr); // send to server console too
+                               MX_Say(strcat(playername(source, true), "^7: ", msgin));
+                       }
+                       FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != source && !MUTATOR_CALLHOOK(ChatMessageTo, it, source), {
+                               sprint(it, msgstr);
+                       });
+                       event_log_msg = sprintf(":chat:%d:%s", source.playerid, strreplace("\n", " ", msgin));
+               }
+       }
+
+       if (autocvar_sv_eventlog && (event_log_msg != "")) {
+               GameLogEcho(event_log_msg);
+       }
+
+       return ret;
+}
+
+entity findnearest(vector point, bool checkitems, vector axismod)
+{
+    vector dist;
+    int num_nearest = 0;
+
+    IL_EACH(((checkitems) ? g_items : g_locations), ((checkitems) ? (it.target == "###item###") : (it.classname == "target_location")),
+    {
+       if ((it.items == IT_KEY1 || it.items == IT_KEY2) && it.target == "###item###")
+            dist = it.oldorigin;
+        else
+            dist = it.origin;
+        dist = dist - point;
+        dist = dist.x * axismod.x * '1 0 0' + dist.y * axismod.y * '0 1 0' + dist.z * axismod.z * '0 0 1';
+        float len = vlen2(dist);
+
+        int l;
+        for (l = 0; l < num_nearest; ++l)
+        {
+            if (len < nearest_length[l])
+                break;
+        }
+
+        // now i tells us where to insert at
+        //   INSERTION SORT! YOU'VE SEEN IT! RUN!
+        if (l < NUM_NEAREST_ENTITIES)
+        {
+            for (int j = NUM_NEAREST_ENTITIES - 1; j >= l; --j)
+            {
+                nearest_length[j + 1] = nearest_length[j];
+                nearest_entity[j + 1] = nearest_entity[j];
+            }
+            nearest_length[l] = len;
+            nearest_entity[l] = it;
+            if (num_nearest < NUM_NEAREST_ENTITIES)
+                num_nearest = num_nearest + 1;
+        }
+    });
+
+    // now use the first one from our list that we can see
+    for (int j = 0; j < num_nearest; ++j)
+    {
+        traceline(point, nearest_entity[j].origin, true, NULL);
+        if (trace_fraction == 1)
+        {
+            if (j != 0)
+                LOG_TRACEF("Nearest point (%s) is not visible, using a visible one.", nearest_entity[0].netname);
+            return nearest_entity[j];
+        }
+    }
+
+    if (num_nearest == 0)
+        return NULL;
+
+    LOG_TRACE("Not seeing any location point, using nearest as fallback.");
+    /* DEBUGGING CODE:
+    dprint("Candidates were: ");
+    for(j = 0; j < num_nearest; ++j)
+    {
+       if(j != 0)
+               dprint(", ");
+       dprint(nearest_entity[j].netname);
+    }
+    dprint("\n");
+    */
+
+    return nearest_entity[0];
+}
+
+string NearestLocation(vector p)
+{
+    string ret = "somewhere";
+    entity loc = findnearest(p, false, '1 1 1');
+    if (loc)
+        ret = loc.message;
+    else
+    {
+        loc = findnearest(p, true, '1 1 4');
+        if (loc)
+            ret = loc.netname;
+    }
+    return ret;
+}
+
+string PlayerHealth(entity this)
+{
+       float myhealth = floor(GetResource(this, RES_HEALTH));
+       if(myhealth == -666)
+               return "spectating";
+       else if(myhealth == -2342 || (myhealth == 2342 && mapvote_initialized))
+               return "observing";
+       else if(myhealth <= 0 || IS_DEAD(this))
+               return "dead";
+       return ftos(myhealth);
+}
+
+string WeaponNameFromWeaponentity(entity this, .entity weaponentity)
+{
+       entity wepent = this.(weaponentity);
+       if(!wepent)
+               return "none";
+       else if(wepent.m_weapon != WEP_Null)
+               return wepent.m_weapon.m_name;
+       else if(wepent.m_switchweapon != WEP_Null)
+               return wepent.m_switchweapon.m_name;
+       return "none"; //REGISTRY_GET(Weapons, wepent.cnt).m_name;
+}
+
+string formatmessage(entity this, string msg)
+{
+       float p, p1, p2;
+       float n;
+       vector cursor = '0 0 0';
+       entity cursor_ent = NULL;
+       string escape;
+       string replacement;
+       p = 0;
+       n = 7;
+       bool traced = false;
+
+       MUTATOR_CALLHOOK(PreFormatMessage, this, msg);
+       msg = M_ARGV(1, string);
+
+       while (1) {
+               if (n < 1)
+                       break; // too many replacements
+
+               n = n - 1;
+               p1 = strstrofs(msg, "%", p); // NOTE: this destroys msg as it's a tempstring!
+               p2 = strstrofs(msg, "\\", p); // NOTE: this destroys msg as it's a tempstring!
+
+               if (p1 < 0)
+                       p1 = p2;
+
+               if (p2 < 0)
+                       p2 = p1;
+
+               p = min(p1, p2);
+
+               if (p < 0)
+                       break;
+
+               if(!traced)
+               {
+                       WarpZone_crosshair_trace_plusvisibletriggers(this);
+                       cursor = trace_endpos;
+                       cursor_ent = trace_ent;
+                       traced = true;
+               }
+
+               replacement = substring(msg, p, 2);
+               escape = substring(msg, p + 1, 1);
+
+               .entity weaponentity = weaponentities[0]; // TODO: unhardcode
+
+               switch(escape)
+               {
+                       case "%": replacement = "%"; break;
+                       case "\\":replacement = "\\"; break;
+                       case "n": replacement = "\n"; break;
+                       case "a": replacement = ftos(floor(GetResource(this, RES_ARMOR))); break;
+                       case "h": replacement = PlayerHealth(this); break;
+                       case "l": replacement = NearestLocation(this.origin); break;
+                       case "y": replacement = NearestLocation(cursor); break;
+                       case "d": replacement = NearestLocation(this.death_origin); break;
+                       case "w": replacement = WeaponNameFromWeaponentity(this, weaponentity); break;
+                       case "W": replacement = GetAmmoName(this.(weaponentity).m_weapon.ammo_type); break;
+                       case "x": replacement = ((cursor_ent.netname == "" || !cursor_ent) ? "nothing" : cursor_ent.netname); break;
+                       case "s": replacement = ftos(vlen(this.velocity - this.velocity_z * '0 0 1')); break;
+                       case "S": replacement = ftos(vlen(this.velocity)); break;
+                       case "t": replacement = seconds_tostring(ceil(max(0, autocvar_timelimit * 60 + game_starttime - time))); break;
+                       case "T": replacement = seconds_tostring(floor(time - game_starttime)); break;
+                       default:
+                       {
+                               MUTATOR_CALLHOOK(FormatMessage, this, escape, replacement, msg);
+                               replacement = M_ARGV(2, string);
+                               break;
+                       }
+               }
+
+               msg = strcat(substring(msg, 0, p), replacement, substring(msg, p+2, strlen(msg) - (p+2)));
+               p = p + strlen(replacement);
+       }
+       return msg;
+}
+
+ERASEABLE
+void PrintToChat(entity client, string text)
+{
+       text = strcat("\{1}^7", text, "\n");
+       sprint(client, text);
+}
+
+ERASEABLE
+void DebugPrintToChat(entity client, string text)
+{
+       if (autocvar_developer > 0)
+       {
+               PrintToChat(client, text);
+       }
+}
+
+ERASEABLE
+void PrintToChatAll(string text)
+{
+       text = strcat("\{1}^7", text, "\n");
+       bprint(text);
+}
+
+ERASEABLE
+void DebugPrintToChatAll(string text)
+{
+       if (autocvar_developer > 0)
+       {
+               PrintToChatAll(text);
+       }
+}
+
+ERASEABLE
+void PrintToChatTeam(int team_num, string text)
+{
+       text = strcat("\{1}^7", text, "\n");
+       FOREACH_CLIENT(IS_REAL_CLIENT(it),
+       {
+               if (it.team == team_num)
+               {
+                       sprint(it, text);
+               }
+       });
+}
+
+ERASEABLE
+void DebugPrintToChatTeam(int team_num, string text)
+{
+       if (autocvar_developer > 0)
+       {
+               PrintToChatTeam(team_num, text);
+       }
+}
diff --git a/qcsrc/server/chat.qh b/qcsrc/server/chat.qh
new file mode 100644 (file)
index 0000000..9c30e89
--- /dev/null
@@ -0,0 +1,50 @@
+#pragma once
+
+const float NUM_NEAREST_ENTITIES = 4;
+entity nearest_entity[NUM_NEAREST_ENTITIES];
+float nearest_length[NUM_NEAREST_ENTITIES];
+
+.float floodcontrol_chat;
+.float floodcontrol_chatteam;
+.float floodcontrol_chattell;
+.float floodcontrol_voice;
+.float floodcontrol_voiceteam;
+
+#define CHAT_NOSPECTATORS() ((autocvar_g_chat_nospectators == 1) || (autocvar_g_chat_nospectators == 2 && !warmup_stage))
+
+int Say(entity source, int teamsay, entity privatesay, string msgin, bool floodcontrol);
+
+string NearestLocation(vector p);
+
+string formatmessage(entity this, string msg);
+
+/// \brief Print the string to the client's chat.
+/// \param[in] client Client to print to.
+/// \param[in] text Text to print.
+void PrintToChat(entity client, string text);
+
+/// \brief Print the string to the client's chat if the server cvar "developer"
+/// is not 0.
+/// \param[in] client Client to print to.
+/// \param[in] text Text to print.
+void DebugPrintToChat(entity client, string text);
+
+/// \brief Prints the string to all clients' chat.
+/// \param[in] text Text to print.
+void PrintToChatAll(string text);
+
+/// \brief Prints the string to all clients' chat if the server cvar "developer"
+/// is not 0.
+/// \param[in] text Text to print.
+void DebugPrintToChatAll(string text);
+
+/// \brief Print the string to chat of all clients of the specified team.
+/// \param[in] team_num Team to print to. See NUM_TEAM constants.
+/// \param[in] text Text to print.
+void PrintToChatTeam(int team_num, string text);
+
+/// \brief Print the string to chat of all clients of the specified team if the
+/// server cvar "developer" is not 0.
+/// \param[in] team_num Team to print to. See NUM_TEAM constants.
+/// \param[in] text Text to print.
+void DebugPrintToChatTeam(int team_num, string text);
index d403f2c111fc58002f86fa98dc6eef9c1429e7fd..04fba80931100cd0c171d10b10bf2800167c39b9 100644 (file)
@@ -2,6 +2,7 @@
 
 #include <common/weapons/_all.qh>
 #include <common/stats.qh>
+#include <server/chat.qh>
 #include <server/miscfunctions.qh>
 #include <common/effects/all.qh>
 #include "anticheat.qh"
@@ -84,8 +85,6 @@
 
 #include <common/weapons/weapon/vortex.qh>
 
-#define CHAT_NOSPECTATORS() ((autocvar_g_chat_nospectators == 1) || (autocvar_g_chat_nospectators == 2 && !warmup_stage))
-
 STATIC_METHOD(Client, Add, void(Client this, int _team))
 {
     ClientConnect(this);
@@ -1444,60 +1443,6 @@ void respawn(entity this)
        PutClientInServer(this);
 }
 
-ERASEABLE
-void PrintToChat(entity client, string text)
-{
-       text = strcat("\{1}^7", text, "\n");
-       sprint(client, text);
-}
-
-ERASEABLE
-void DebugPrintToChat(entity client, string text)
-{
-       if (autocvar_developer > 0)
-       {
-               PrintToChat(client, text);
-       }
-}
-
-ERASEABLE
-void PrintToChatAll(string text)
-{
-       text = strcat("\{1}^7", text, "\n");
-       bprint(text);
-}
-
-ERASEABLE
-void DebugPrintToChatAll(string text)
-{
-       if (autocvar_developer > 0)
-       {
-               PrintToChatAll(text);
-       }
-}
-
-ERASEABLE
-void PrintToChatTeam(int team_num, string text)
-{
-       text = strcat("\{1}^7", text, "\n");
-       FOREACH_CLIENT(IS_REAL_CLIENT(it),
-       {
-               if (it.team == team_num)
-               {
-                       sprint(it, text);
-               }
-       });
-}
-
-ERASEABLE
-void DebugPrintToChatTeam(int team_num, string text)
-{
-       if (autocvar_developer > 0)
-       {
-               PrintToChatTeam(team_num, text);
-       }
-}
-
 void play_countdown(entity this, float finished, Sound samp)
 {
        TC(Sound, samp);
@@ -2840,332 +2785,6 @@ void PlayerPostThink (entity this)
        CSQCMODEL_AUTOUPDATE(this);
 }
 
-/**
- * message "": do not say, just test flood control
- * return value:
- *   1 = accept
- *   0 = reject
- *  -1 = fake accept
- */
-int Say(entity source, int teamsay, entity privatesay, string msgin, bool floodcontrol)
-{
-       if (!teamsay && !privatesay && substring(msgin, 0, 1) == " ")
-               msgin = substring(msgin, 1, -1); // work around DP say bug (say_team does not have this!)
-
-       if (source)
-               msgin = formatmessage(source, msgin);
-
-       string colorstr;
-       if (!(IS_PLAYER(source) || source.caplayer))
-               colorstr = "^0"; // black for spectators
-       else if(teamplay)
-               colorstr = Team_ColorCode(source.team);
-       else
-       {
-               colorstr = "";
-               teamsay = false;
-       }
-
-       if (!source) {
-               colorstr = "";
-               teamsay = false;
-       }
-
-       if(msgin != "")
-               msgin = trigger_magicear_processmessage_forallears(source, teamsay, privatesay, msgin);
-
-       /*
-        * using bprint solves this... me stupid
-       // how can we prevent the message from appearing in a listen server?
-       // for now, just give "say" back and only handle say_team
-       if(!teamsay)
-       {
-               clientcommand(source, strcat("say ", msgin));
-               return;
-       }
-       */
-
-       string namestr = "";
-       if (source)
-               namestr = playername(source, autocvar_g_chat_teamcolors);
-
-       string colorprefix = (strdecolorize(namestr) == namestr) ? "^3" : "^7";
-
-       string msgstr = "", cmsgstr = "";
-       string privatemsgprefix = string_null;
-       int privatemsgprefixlen = 0;
-       if (msgin != "")
-       {
-               bool found_me = false;
-               if(strstrofs(msgin, "/me", 0) >= 0)
-               {
-                       string newmsgin = "";
-                       string newnamestr = ((teamsay) ? strcat(colorstr, "(", colorprefix, namestr, colorstr, ")", "^7") : strcat(colorprefix, namestr, "^7"));
-                       FOREACH_WORD(msgin, true,
-                       {
-                               if(strdecolorize(it) == "/me")
-                               {
-                                       found_me = true;
-                                       newmsgin = cons(newmsgin, newnamestr);
-                               }
-                               else
-                                       newmsgin = cons(newmsgin, it);
-                       });
-                       msgin = newmsgin;
-               }
-
-               if(privatesay)
-               {
-                       msgstr = strcat("\{1}\{13}* ", colorprefix, namestr, "^3 tells you: ^7");
-                       privatemsgprefixlen = strlen(msgstr);
-                       msgstr = strcat(msgstr, msgin);
-                       cmsgstr = strcat(colorstr, colorprefix, namestr, "^3 tells you:\n^7", msgin);
-                       privatemsgprefix = strcat("\{1}\{13}* ^3You tell ", playername(privatesay, autocvar_g_chat_teamcolors), ": ^7");
-               }
-               else if(teamsay)
-               {
-                       if(found_me)
-                       {
-                               //msgin = strreplace("/me", "", msgin);
-                               //msgin = substring(msgin, 3, strlen(msgin));
-                               //msgin = strreplace("/me", strcat(colorstr, "(", colorprefix, namestr, colorstr, ")^7"), msgin);
-                               msgstr = strcat("\{1}\{13}^4* ", "^7", msgin);
-                       }
-                       else
-                               msgstr = strcat("\{1}\{13}", colorstr, "(", colorprefix, namestr, colorstr, ") ^7", msgin);
-                       cmsgstr = strcat(colorstr, "(", colorprefix, namestr, colorstr, ")\n^7", msgin);
-               }
-               else
-               {
-                       if(found_me)
-                       {
-                               //msgin = strreplace("/me", "", msgin);
-                               //msgin = substring(msgin, 3, strlen(msgin));
-                               //msgin = strreplace("/me", strcat(colorprefix, namestr), msgin);
-                               msgstr = strcat("\{1}^4* ^7", msgin);
-                       }
-                       else {
-                               msgstr = "\{1}";
-                               msgstr = strcat(msgstr, (namestr != "") ? strcat(colorprefix, namestr, "^7: ") : "^7");
-                               msgstr = strcat(msgstr, msgin);
-                       }
-                       cmsgstr = "";
-               }
-               msgstr = strcat(strreplace("\n", " ", msgstr), "\n"); // newlines only are good for centerprint
-       }
-
-       string fullmsgstr = msgstr;
-       string fullcmsgstr = cmsgstr;
-
-       // FLOOD CONTROL
-       int flood = 0;
-       var .float flood_field = floodcontrol_chat;
-       if(floodcontrol && source)
-       {
-               float flood_spl, flood_burst, flood_lmax;
-               if(privatesay)
-               {
-                       flood_spl = autocvar_g_chat_flood_spl_tell;
-                       flood_burst = autocvar_g_chat_flood_burst_tell;
-                       flood_lmax = autocvar_g_chat_flood_lmax_tell;
-                       flood_field = floodcontrol_chattell;
-               }
-               else if(teamsay)
-               {
-                       flood_spl = autocvar_g_chat_flood_spl_team;
-                       flood_burst = autocvar_g_chat_flood_burst_team;
-                       flood_lmax = autocvar_g_chat_flood_lmax_team;
-                       flood_field = floodcontrol_chatteam;
-               }
-               else
-               {
-                       flood_spl = autocvar_g_chat_flood_spl;
-                       flood_burst = autocvar_g_chat_flood_burst;
-                       flood_lmax = autocvar_g_chat_flood_lmax;
-                       flood_field = floodcontrol_chat;
-               }
-               flood_burst = max(0, flood_burst - 1);
-               // to match explanation in default.cfg, a value of 3 must allow three-line bursts and not four!
-
-               // do flood control for the default line size
-               if(msgstr != "")
-               {
-                       getWrappedLine_remaining = msgstr;
-                       msgstr = "";
-                       int lines = 0;
-                       while(getWrappedLine_remaining && (!flood_lmax || lines <= flood_lmax))
-                       {
-                               msgstr = strcat(msgstr, " ", getWrappedLineLen(82.4289758859709, strlennocol)); // perl averagewidth.pl < gfx/vera-sans.width
-                               ++lines;
-                       }
-                       msgstr = substring(msgstr, 1, strlen(msgstr) - 1);
-
-                       if(getWrappedLine_remaining != "")
-                       {
-                               msgstr = strcat(msgstr, "\n");
-                               flood = 2;
-                       }
-
-                       if (time >= source.(flood_field))
-                       {
-                               source.(flood_field) = max(time - flood_burst * flood_spl, source.(flood_field)) + lines * flood_spl;
-                       }
-                       else
-                       {
-                               flood = 1;
-                               msgstr = fullmsgstr;
-                       }
-               }
-               else
-               {
-                       if (time >= source.(flood_field))
-                               source.(flood_field) = max(time - flood_burst * flood_spl, source.(flood_field)) + flood_spl;
-                       else
-                               flood = 1;
-               }
-
-               if (timeout_status == TIMEOUT_ACTIVE) // when game is paused, no flood protection
-                       source.(flood_field) = flood = 0;
-       }
-
-       string sourcemsgstr, sourcecmsgstr;
-       if(flood == 2) // cannot happen for empty msgstr
-       {
-               if(autocvar_g_chat_flood_notify_flooder)
-               {
-                       sourcemsgstr = strcat(msgstr, "\n^3FLOOD CONTROL: ^7message too long, trimmed\n");
-                       sourcecmsgstr = "";
-               }
-               else
-               {
-                       sourcemsgstr = fullmsgstr;
-                       sourcecmsgstr = fullcmsgstr;
-               }
-               cmsgstr = "";
-       }
-       else
-       {
-               sourcemsgstr = msgstr;
-               sourcecmsgstr = cmsgstr;
-       }
-
-       if (!privatesay && source && !(IS_PLAYER(source) || source.caplayer) && !game_stopped
-               && (teamsay || CHAT_NOSPECTATORS()))
-       {
-               teamsay = -1; // spectators
-       }
-
-       if(flood)
-               LOG_INFO("NOTE: ", playername(source, true), "^7 is flooding.");
-
-       // build sourcemsgstr by cutting off a prefix and replacing it by the other one
-       if(privatesay)
-               sourcemsgstr = strcat(privatemsgprefix, substring(sourcemsgstr, privatemsgprefixlen, -1));
-
-       int ret;
-       if(source && CS(source).muted)
-       {
-               // always fake the message
-               ret = -1;
-       }
-       else if(flood == 1)
-       {
-               if (autocvar_g_chat_flood_notify_flooder)
-               {
-                       sprint(source, strcat("^3FLOOD CONTROL: ^7wait ^1", ftos(source.(flood_field) - time), "^3 seconds\n"));
-                       ret = 0;
-               }
-               else
-                       ret = -1;
-       }
-       else
-       {
-               ret = 1;
-       }
-
-       if (privatesay && source && !(IS_PLAYER(source) || source.caplayer) && !game_stopped
-               && (IS_PLAYER(privatesay) || privatesay.caplayer) && CHAT_NOSPECTATORS())
-       {
-               ret = -1; // just hide the message completely
-       }
-
-       MUTATOR_CALLHOOK(ChatMessage, source, ret);
-       ret = M_ARGV(1, int);
-
-       string event_log_msg = "";
-
-       if(sourcemsgstr != "" && ret != 0)
-       {
-               if(ret < 0) // faked message, because the player is muted
-               {
-                       sprint(source, sourcemsgstr);
-                       if(sourcecmsgstr != "" && !privatesay)
-                               centerprint(source, sourcecmsgstr);
-               }
-               else if(privatesay) // private message, between 2 people only
-               {
-                       sprint(source, sourcemsgstr);
-                       if (!autocvar_g_chat_tellprivacy) { dedicated_print(msgstr); } // send to server console too if "tellprivacy" is disabled
-                       if(!MUTATOR_CALLHOOK(ChatMessageTo, privatesay, source))
-                       {
-                               sprint(privatesay, msgstr);
-                               if(cmsgstr != "")
-                                       centerprint(privatesay, cmsgstr);
-                       }
-               }
-               else if ( teamsay && CS(source).active_minigame )
-               {
-                       sprint(source, sourcemsgstr);
-                       dedicated_print(msgstr); // send to server console too
-                       FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != source && CS(it).active_minigame == CS(source).active_minigame && !MUTATOR_CALLHOOK(ChatMessageTo, it, source), {
-                               sprint(it, msgstr);
-                       });
-                       event_log_msg = sprintf(":chat_minigame:%d:%s:%s", source.playerid, CS(source).active_minigame.netname, msgin);
-
-               }
-               else if(teamsay > 0) // team message, only sent to team mates
-               {
-                       sprint(source, sourcemsgstr);
-                       dedicated_print(msgstr); // send to server console too
-                       if(sourcecmsgstr != "")
-                               centerprint(source, sourcecmsgstr);
-                       FOREACH_CLIENT((IS_PLAYER(it) || it.caplayer) && IS_REAL_CLIENT(it) && it != source && it.team == source.team && !MUTATOR_CALLHOOK(ChatMessageTo, it, source), {
-                               sprint(it, msgstr);
-                               if(cmsgstr != "")
-                                       centerprint(it, cmsgstr);
-                       });
-                       event_log_msg = sprintf(":chat_team:%d:%d:%s", source.playerid, source.team, strreplace("\n", " ", msgin));
-               }
-               else if(teamsay < 0) // spectator message, only sent to spectators
-               {
-                       sprint(source, sourcemsgstr);
-                       dedicated_print(msgstr); // send to server console too
-                       FOREACH_CLIENT(!(IS_PLAYER(it) || it.caplayer) && IS_REAL_CLIENT(it) && it != source && !MUTATOR_CALLHOOK(ChatMessageTo, it, source), {
-                               sprint(it, msgstr);
-                       });
-                       event_log_msg = sprintf(":chat_spec:%d:%s", source.playerid, strreplace("\n", " ", msgin));
-               }
-               else
-               {
-                       if (source) {
-                               sprint(source, sourcemsgstr);
-                               dedicated_print(msgstr); // send to server console too
-                               MX_Say(strcat(playername(source, true), "^7: ", msgin));
-                       }
-                       FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != source && !MUTATOR_CALLHOOK(ChatMessageTo, it, source), {
-                               sprint(it, msgstr);
-                       });
-                       event_log_msg = sprintf(":chat:%d:%s", source.playerid, strreplace("\n", " ", msgin));
-               }
-       }
-
-       if (autocvar_sv_eventlog && (event_log_msg != "")) {
-               GameLogEcho(event_log_msg);
-       }
-
-       return ret;
-}
-
 // hack to copy the button fields from the client entity to the Client State
 void PM_UpdateButtons(entity this, entity store)
 {
index d074ebe4b5a9b94381770e5d4b022115e80b3518..abda1204aba38bed656a63abf7fd76bfce5a62de 100644 (file)
@@ -268,11 +268,6 @@ bool independent_players;
 //flood fields
 .float nickspamtime; // time of last nick change
 .float nickspamcount;
-.float floodcontrol_chat;
-.float floodcontrol_chatteam;
-.float floodcontrol_chattell;
-.float floodcontrol_voice;
-.float floodcontrol_voiceteam;
 
 // respawning
 .int respawn_flags;
@@ -329,37 +324,6 @@ void FixClientCvars(entity e);
 IntrusiveList g_initforplayer;
 STATIC_INIT(g_initforplayer) { g_initforplayer = IL_NEW(); }
 
-/// \brief Print the string to the client's chat.
-/// \param[in] client Client to print to.
-/// \param[in] text Text to print.
-void PrintToChat(entity client, string text);
-
-/// \brief Print the string to the client's chat if the server cvar "developer"
-/// is not 0.
-/// \param[in] client Client to print to.
-/// \param[in] text Text to print.
-void DebugPrintToChat(entity client, string text);
-
-/// \brief Prints the string to all clients' chat.
-/// \param[in] text Text to print.
-void PrintToChatAll(string text);
-
-/// \brief Prints the string to all clients' chat if the server cvar "developer"
-/// is not 0.
-/// \param[in] text Text to print.
-void DebugPrintToChatAll(string text);
-
-/// \brief Print the string to chat of all clients of the specified team.
-/// \param[in] team_num Team to print to. See NUM_TEAM constants.
-/// \param[in] text Text to print.
-void PrintToChatTeam(int team_num, string text);
-
-/// \brief Print the string to chat of all clients of the specified team if the
-/// server cvar "developer" is not 0.
-/// \param[in] team_num Team to print to. See NUM_TEAM constants.
-/// \param[in] text Text to print.
-void DebugPrintToChatTeam(int team_num, string text);
-
 void play_countdown(entity this, float finished, Sound samp);
 
 void RotRegen(entity this, float current, float regenstable, float regenfactor, float regenlinear, float regenframetime, float rotstable, float rotfactor, float rotlinear, float rotframetime, float limit_mod);
@@ -386,6 +350,4 @@ void Join(entity this);
 #define SPECTATE_COPY() ACCUMULATE void SpectateCopy(entity this, entity spectatee)
 #define SPECTATE_COPYFIELD(fld) SPECTATE_COPY() { this.(fld) = spectatee.(fld); }
 
-int Say(entity source, int teamsay, entity privatesay, string msgin, bool floodcontrol);
-
 const int MAX_SPECTATORS = 7;
index 67ac6d641c5f58bc8c67eaca8dab6b930f28394b..c2c7c099287c0dfd3cdb60dfea664ae796935adb 100644 (file)
@@ -1,5 +1,6 @@
 #include "cmd.qh"
 
+#include <server/chat.qh>
 #include <server/world.qh>
 #include <server/miscfunctions.qh>
 
index 01fb5ad48048a6e7605636ffa0c37211a73db5f2..c5622ead3bc8ed6bd22cc1f52f1a62ee4d5591a4 100644 (file)
@@ -1,5 +1,6 @@
 #include "common.qh"
 
+#include <server/chat.qh>
 #include <server/client.qh>
 #include <common/weapons/_all.qh>
 #include <common/stats.qh>
index b495b4b0d8e8ab3b8cab12800380a615b2750559..ca8dd3230c6f4075c0aed660b951d5b9fa09f09a 100644 (file)
@@ -1,6 +1,6 @@
 #include "matrix.qh"
 
-#include "client.qh"
+#include <server/chat.qh>
 
 var void MX_Handle(int buf, string ancestor)
 {
index b748c17a00ccd96fd25ac4efc93f2ee242d7e1a0..9a0c4ae6531df63260b0581f1d630f841e5fd89c 100644 (file)
@@ -1,3 +1,5 @@
+// NOTE: Please do NOT add new functions to this file! It is a dumping ground that is in the process of being cleaned up, please find a proper home for your code!
+
 #include "miscfunctions.qh"
 
 #include "antilag.qh"
@@ -88,191 +90,6 @@ void WarpZone_crosshair_trace(entity pl)
        WarpZone_traceline_antilag(pl, CS(pl).cursor_trace_start, CS(pl).cursor_trace_start + normalize(CS(pl).cursor_trace_endpos - CS(pl).cursor_trace_start) * max_shot_distance, MOVE_NORMAL, pl, ANTILAG_LATENCY(pl));
 }
 
-entity findnearest(vector point, bool checkitems, vector axismod)
-{
-    vector dist;
-    int num_nearest = 0;
-
-    IL_EACH(((checkitems) ? g_items : g_locations), ((checkitems) ? (it.target == "###item###") : (it.classname == "target_location")),
-    {
-       if ((it.items == IT_KEY1 || it.items == IT_KEY2) && it.target == "###item###")
-            dist = it.oldorigin;
-        else
-            dist = it.origin;
-        dist = dist - point;
-        dist = dist.x * axismod.x * '1 0 0' + dist.y * axismod.y * '0 1 0' + dist.z * axismod.z * '0 0 1';
-        float len = vlen2(dist);
-
-        int l;
-        for (l = 0; l < num_nearest; ++l)
-        {
-            if (len < nearest_length[l])
-                break;
-        }
-
-        // now i tells us where to insert at
-        //   INSERTION SORT! YOU'VE SEEN IT! RUN!
-        if (l < NUM_NEAREST_ENTITIES)
-        {
-            for (int j = NUM_NEAREST_ENTITIES - 1; j >= l; --j)
-            {
-                nearest_length[j + 1] = nearest_length[j];
-                nearest_entity[j + 1] = nearest_entity[j];
-            }
-            nearest_length[l] = len;
-            nearest_entity[l] = it;
-            if (num_nearest < NUM_NEAREST_ENTITIES)
-                num_nearest = num_nearest + 1;
-        }
-    });
-
-    // now use the first one from our list that we can see
-    for (int j = 0; j < num_nearest; ++j)
-    {
-        traceline(point, nearest_entity[j].origin, true, NULL);
-        if (trace_fraction == 1)
-        {
-            if (j != 0)
-                LOG_TRACEF("Nearest point (%s) is not visible, using a visible one.", nearest_entity[0].netname);
-            return nearest_entity[j];
-        }
-    }
-
-    if (num_nearest == 0)
-        return NULL;
-
-    LOG_TRACE("Not seeing any location point, using nearest as fallback.");
-    /* DEBUGGING CODE:
-    dprint("Candidates were: ");
-    for(j = 0; j < num_nearest; ++j)
-    {
-       if(j != 0)
-               dprint(", ");
-       dprint(nearest_entity[j].netname);
-    }
-    dprint("\n");
-    */
-
-    return nearest_entity[0];
-}
-
-string NearestLocation(vector p)
-{
-    string ret = "somewhere";
-    entity loc = findnearest(p, false, '1 1 1');
-    if (loc)
-        ret = loc.message;
-    else
-    {
-        loc = findnearest(p, true, '1 1 4');
-        if (loc)
-            ret = loc.netname;
-    }
-    return ret;
-}
-
-string PlayerHealth(entity this)
-{
-       float myhealth = floor(GetResource(this, RES_HEALTH));
-       if(myhealth == -666)
-               return "spectating";
-       else if(myhealth == -2342 || (myhealth == 2342 && mapvote_initialized))
-               return "observing";
-       else if(myhealth <= 0 || IS_DEAD(this))
-               return "dead";
-       return ftos(myhealth);
-}
-
-string WeaponNameFromWeaponentity(entity this, .entity weaponentity)
-{
-       entity wepent = this.(weaponentity);
-       if(!wepent)
-               return "none";
-       else if(wepent.m_weapon != WEP_Null)
-               return wepent.m_weapon.m_name;
-       else if(wepent.m_switchweapon != WEP_Null)
-               return wepent.m_switchweapon.m_name;
-       return "none"; //REGISTRY_GET(Weapons, wepent.cnt).m_name;
-}
-
-string formatmessage(entity this, string msg)
-{
-       float p, p1, p2;
-       float n;
-       vector cursor = '0 0 0';
-       entity cursor_ent = NULL;
-       string escape;
-       string replacement;
-       p = 0;
-       n = 7;
-       bool traced = false;
-
-       MUTATOR_CALLHOOK(PreFormatMessage, this, msg);
-       msg = M_ARGV(1, string);
-
-       while (1) {
-               if (n < 1)
-                       break; // too many replacements
-
-               n = n - 1;
-               p1 = strstrofs(msg, "%", p); // NOTE: this destroys msg as it's a tempstring!
-               p2 = strstrofs(msg, "\\", p); // NOTE: this destroys msg as it's a tempstring!
-
-               if (p1 < 0)
-                       p1 = p2;
-
-               if (p2 < 0)
-                       p2 = p1;
-
-               p = min(p1, p2);
-
-               if (p < 0)
-                       break;
-
-               if(!traced)
-               {
-                       WarpZone_crosshair_trace_plusvisibletriggers(this);
-                       cursor = trace_endpos;
-                       cursor_ent = trace_ent;
-                       traced = true;
-               }
-
-               replacement = substring(msg, p, 2);
-               escape = substring(msg, p + 1, 1);
-
-               .entity weaponentity = weaponentities[0]; // TODO: unhardcode
-
-               switch(escape)
-               {
-                       case "%": replacement = "%"; break;
-                       case "\\":replacement = "\\"; break;
-                       case "n": replacement = "\n"; break;
-                       case "a": replacement = ftos(floor(GetResource(this, RES_ARMOR))); break;
-                       case "h": replacement = PlayerHealth(this); break;
-                       case "l": replacement = NearestLocation(this.origin); break;
-                       case "y": replacement = NearestLocation(cursor); break;
-                       case "d": replacement = NearestLocation(this.death_origin); break;
-                       case "w": replacement = WeaponNameFromWeaponentity(this, weaponentity); break;
-                       case "W": replacement = GetAmmoName(this.(weaponentity).m_weapon.ammo_type); break;
-                       case "x": replacement = ((cursor_ent.netname == "" || !cursor_ent) ? "nothing" : cursor_ent.netname); break;
-                       case "s": replacement = ftos(vlen(this.velocity - this.velocity_z * '0 0 1')); break;
-                       case "S": replacement = ftos(vlen(this.velocity)); break;
-                       case "t": replacement = seconds_tostring(ceil(max(0, autocvar_timelimit * 60 + game_starttime - time))); break;
-                       case "T": replacement = seconds_tostring(floor(time - game_starttime)); break;
-                       default:
-                       {
-                               MUTATOR_CALLHOOK(FormatMessage, this, escape, replacement, msg);
-                               replacement = M_ARGV(2, string);
-                               break;
-                       }
-               }
-
-               msg = strcat(substring(msg, 0, p), replacement, substring(msg, p+2, strlen(msg) - (p+2)));
-               p = p + strlen(replacement);
-       }
-       return msg;
-}
-
 /*
 =============
 GetCvars
index 0f37cfac73a32df98095a984de87d97bb53d0db8..87b02725ba07a998512b0022f3846d1b5df48647 100644 (file)
@@ -46,8 +46,6 @@ void detach_sameorigin(entity e);
 
 void follow_sameorigin(entity e, entity to);
 
-string formatmessage(entity this, string msg);
-
 void GetCvars(entity this, entity store, int f);
 
 float LostMovetypeFollow(entity ent);
@@ -58,8 +56,6 @@ bool MoveToRandomLocationWithinBounds(entity e, vector boundmin, vector boundmax
 
 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance);
 
-string NearestLocation(vector p);
-
 string playername(entity p, bool team_colorize);
 
 void SetMovetypeFollow(entity ent, entity e);
@@ -77,10 +73,6 @@ void WarpZone_traceline_antilag (entity source, vector v1, vector v2, float nomo
 // copies a string to a tempstring (so one can strunzone it)
 string strcat1(string s) = #115; // FRIK_FILE
 
-const float NUM_NEAREST_ENTITIES = 4;
-entity nearest_entity[NUM_NEAREST_ENTITIES];
-float nearest_length[NUM_NEAREST_ENTITIES];
-
 
 //#NO AUTOCVARS START