]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/server/miscfunctions.qc
Replaced trunk's centerprint system by the simpler one in branch; this is to
[divverent/nexuiz.git] / data / qcsrc / server / miscfunctions.qc
1 float logfile_open;
2 float logfile;
3
4 void(string s) bcenterprint
5 {
6         entity head;
7         head = find(world, classname, "player");
8         while(head)
9         {
10                 if(clienttype(head) == CLIENTTYPE_REAL)
11                         centerprint(head, s);
12                 head = find(head, classname, "player");
13         }
14 }
15
16 void(string s, float check_dangerous) ServerConsoleEcho =
17 {
18         local string ch;
19         localcmd("echo \"");
20         if(check_dangerous)
21         {
22                 while(strlen(s))
23                 {
24                         ch = substring(s, 0, 1);
25                         if(ch != "\"" && ch != "\r" && ch != "\n")
26                                 localcmd(ch);
27                         s = substring(s, 1, strlen(s) - 1);
28                 }
29         }
30         else
31         {
32                 localcmd(s);
33         }
34         localcmd("\"\n");
35 }
36
37 void(string s, float check_dangerous) GameLogEcho =
38 {
39         string fn;
40         float matches;
41
42         if(cvar("sv_eventlog_files"))
43         {
44                 if(!logfile_open)
45                 {
46                         logfile_open = TRUE;
47                         matches = cvar("sv_eventlog_files_counter") + 1;
48                         cvar_set("sv_eventlog_files_counter", ftos(matches));
49                         fn = ftos(matches);
50                         if(strlen(fn) < 8)
51                                 fn = strcat(substring("00000000", 0, 8 - strlen(fn)), fn);
52                         fn = strcat(cvar_string("sv_eventlog_files_nameprefix"), fn, cvar_string("sv_eventlog_files_namesuffix"));
53                         logfile = fopen(fn, FILE_APPEND);
54                 }
55                 if(logfile >= 0)
56                         fputs(logfile, strcat(s, "\n"));
57         }
58         if(cvar("sv_eventlog_console"))
59         {
60                 ServerConsoleEcho(s, check_dangerous);
61         }
62 }
63
64 void() GameLogInit =
65 {
66         logfile_open = 0;
67         // will be opened later
68 }
69
70 void() GameLogClose =
71 {
72         if(logfile_open && logfile >= 0)
73         {
74                 fclose(logfile);
75                 logfile = -1;
76         }
77 }
78
79 float math_mod(float a, float b)
80 {
81         return a - (floor(a / b) * b);
82 }
83
84 vector find_floor(vector org)
85 {
86         traceline(org + '0 0 5', org - '0 0 255', TRUE, self);
87         if (trace_fraction < 1)
88                 return trace_endpos;
89         else
90                 return org;
91 }
92
93 void relocate_spawnpoint()
94 {
95         vector org, loc;
96         string error_msg;
97
98         error_msg = "spawn point too close to a wall";
99
100         org = find_floor(self.origin) + '0 0 30';
101
102         traceline(org, org - '18 0 0', TRUE, world);
103         if(trace_fraction < 1)
104         {
105                 loc = trace_endpos;
106                 traceline(loc, loc + '36 0 0', TRUE, world);
107                 if(trace_fraction >= 1 && !self.noalign)
108                         org = loc + '18 0 0';
109                 else
110                 {
111                         objerror(error_msg);
112                         return;
113                 }
114         }
115
116         traceline (org, org - '-18 0 0', TRUE, world);
117         if (trace_fraction < 1)
118         {
119                 loc = trace_endpos;
120                 traceline (loc, loc + '-36 0 0', TRUE, world);
121                 if(trace_fraction >= 1 && !self.noalign)
122                         org = loc + '-18 0 0';
123                 else
124                 {
125                         objerror(error_msg);
126                         return;
127                 }
128         }
129
130         traceline (org, org - '0 18 0' , TRUE, world);
131         if (trace_fraction < 1)
132         {
133                 loc = trace_endpos;
134                 traceline (loc, loc + '0 36 0', TRUE, world);
135                 if(trace_fraction >= 1 && !self.noalign)
136                         org = loc + '0 18 0';
137                 else
138                 {
139                         objerror(error_msg);
140                         return;
141                 }
142         }
143
144         traceline (org, org - '0 -18 0', TRUE, world);
145         if (trace_fraction < 1)
146         {
147                 loc = trace_endpos;
148                 traceline (loc, loc + '0 -36 0', TRUE, world);
149                 if(trace_fraction >= 1 && !self.noalign)
150                         org = loc + '0 -18 0';
151                 else
152                 {
153                         objerror(error_msg);
154                         return;
155                 }
156         }
157
158         if(!self.noalign)
159                 setorigin(self, org);
160 }
161
162 // NOTE: DO NOT USE THIS FUNCTION TOO OFTEN.
163 // IT WILL MOST PROBABLY DESTROY _ALL_ OTHER TEMP
164 // STRINGS AND TAKE QUITE LONG. haystack and needle MUST
165 // BE CONSTANT OR strzoneD!
166 float(string haystack, string needle, float offset) strstr =
167 {
168         float len, endpos;
169         string found;
170         len = strlen(needle);
171         endpos = strlen(haystack) - len;
172         while(offset <= endpos)
173         {
174                 found = substring(haystack, offset, len);
175                 if(found == needle)
176                         return offset;
177                 offset = offset + 1;
178         }
179         return -1;
180 }
181
182 float NUM_NEAREST_ENTITIES = 4;
183 entity nearest_entity[NUM_NEAREST_ENTITIES];
184 float nearest_length[NUM_NEAREST_ENTITIES];
185 entity(vector point, .string field, string value, vector axismod) findnearest =
186 {
187         entity localhead;
188         float i;
189         float j;
190         float len;
191         vector dist;
192
193         float num_nearest;
194         num_nearest = 0;
195
196         localhead = find(world, field, value);
197         while(localhead)
198         {
199                 if((localhead.items == IT_KEY1 || localhead.items == IT_KEY2) && localhead.target == "###item###")
200                         dist = localhead.oldorigin;
201                 else
202                         dist = localhead.origin;
203                 dist = dist - point;
204                 dist = dist_x * axismod_x * '1 0 0' + dist_y * axismod_y * '0 1 0' + dist_z * axismod_z * '0 0 1';
205                 len = vlen(dist);
206
207                 for(i = 0; i < num_nearest; ++i)
208                 {
209                         if(len < nearest_length[i])
210                                 break;
211                 }
212
213                 // now i tells us where to insert at
214                 //   INSERTION SORT! YOU'VE SEEN IT! RUN!
215                 if(i < NUM_NEAREST_ENTITIES)
216                 {
217                         for(j = NUM_NEAREST_ENTITIES - 1; j >= i; --j)
218                         {
219                                 nearest_length[j + 1] = nearest_length[j];
220                                 nearest_entity[j + 1] = nearest_entity[j];
221                         }
222                         nearest_length[i] = len;
223                         nearest_entity[i] = localhead;
224                         if(num_nearest < NUM_NEAREST_ENTITIES)
225                                 num_nearest = num_nearest + 1;
226                 }
227
228                 localhead = find(localhead, field, value);
229         }
230
231         // now use the first one from our list that we can see
232         for(i = 0; i < num_nearest; ++i)
233         {
234                 traceline(point, nearest_entity[i].origin, TRUE, world);
235                 if(trace_fraction == 1)
236                 {
237                         if(i != 0)
238                         {
239                                 dprint("Nearest point (");
240                                 dprint(nearest_entity[0].netname);
241                                 dprint(") is not visible, using a visible one.\n");
242                         }
243                         return nearest_entity[i];
244                 }
245         }
246
247         if(num_nearest == 0)
248                 return world;
249
250         dprint("Not seeing any location point, using nearest as fallback.\n");
251         /* DEBUGGING CODE:
252         dprint("Candidates were: ");
253         for(j = 0; j < num_nearest; ++j)
254         {
255                 if(j != 0)
256                         dprint(", ");
257                 dprint(nearest_entity[j].netname);
258         }
259         dprint("\n");
260         */
261
262         return nearest_entity[0];
263 }
264
265 void() target_location =
266 {
267         self.classname = "target_location";
268         // location name in netname
269         // eventually support: count, teamgame selectors, line of sight?
270 };
271
272 void() info_location =
273 {
274         self.classname = "target_location";
275         self.message = self.netname;
276 };
277
278 string NearestLocation(vector p)
279 {
280         entity loc;
281         string ret;
282         ret = "somewhere";
283         loc = findnearest(p, classname, "target_location", '1 1 1');
284         if(loc)
285         {
286                 ret = loc.message;
287         }
288         else
289         {
290                 loc = findnearest(p, target, "###item###", '1 1 4');
291                 if(loc)
292                         ret = loc.netname;
293         }
294         return ret;
295 }
296
297 string(string msg) formatmessage =
298 {
299         float p;
300         float n;
301         string msg_save;
302         string escape;
303         string replacement;
304         msg_save = strzone(msg);
305         p = 0;
306         n = 7;
307         while(1)
308         {
309                 if(n < 1)
310                         break; // too many replacements
311                 n = n - 1;
312                 p = strstr(msg_save, "%", p); // NOTE: this destroys msg as it's a tempstring!
313                 if(p < 0)
314                         break;
315                 replacement = substring(msg_save, p, 2);
316                 escape = substring(msg_save, p + 1, 1);
317                 if(escape == "%")
318                         replacement = "%";
319                 else if(escape == "a")
320                         replacement = ftos(floor(self.armorvalue));
321                 else if(escape == "h")
322                         replacement = ftos(floor(self.health));
323                 else if(escape == "l")
324                         replacement = NearestLocation(self.origin);
325                 else if(escape == "y")
326                         replacement = NearestLocation(self.cursor_trace_endpos);
327                 else if(escape == "d")
328                         replacement = NearestLocation(self.death_origin);
329                 else if(escape == "w")
330                 {
331                         float wep;
332                         wep = self.weapon;
333                         if(!wep)
334                                 wep = self.switchweapon;
335                         if(!wep)
336                                 wep = self.cnt;
337                         replacement = W_Name(wep);
338                 }
339                 else if(escape == "W")
340                 {
341                         if(self.items & IT_SHELLS) replacement = "shells";
342                         else if(self.items & IT_NAILS) replacement = "bullets";
343                         else if(self.items & IT_ROCKETS) replacement = "rockets";
344                         else if(self.items & IT_CELLS) replacement = "cells";
345                         else replacement = "batteries"; // ;)
346                 }
347                 else if(escape == "x")
348                 {
349                         replacement = self.cursor_trace_ent.netname;
350                         if(!replacement || !self.cursor_trace_ent)
351                                 replacement = "nothing";
352                 }
353                 else if(escape == "p")
354                 {
355                         if(self.last_selected_player)
356                                 replacement = self.last_selected_player.netname;
357                         else
358                                 replacement = "(nobody)";
359                 }
360                 msg = strcat(substring(msg_save, 0, p), replacement);
361                 msg = strcat(msg, substring(msg_save, p+2, strlen(msg_save) - (p+2)));
362                 strunzone(msg_save);
363                 msg_save = strzone(msg);
364                 p = p + 2;
365         }
366         msg = strcat(msg_save);
367         strunzone(msg_save);
368         return msg;
369 }
370
371 /*
372 =============
373 GetCvars
374 =============
375 Called with:
376   0:  sends the request
377   >0: receives a cvar from name=argv(f) value=argv(f+1)
378 */
379 void GetCvars_handleString(float f, .string field, string name)
380 {
381         if(f < 0)
382         {
383                 if(self.field)
384                         strunzone(self.field);
385         }
386         else if(f > 0)
387         {
388                 if(argv(f) == name)
389                 {
390                         if(self.field)
391                                 strunzone(self.field);
392                         self.field = strzone(argv(f + 1));
393                 }
394         }
395         else
396                 stuffcmd(self, strcat("cmd reportcvar ", name, " $", name, "\n"));
397 }
398 void GetCvars_handleFloat(float f, .float field, string name)
399 {
400         if(f < 0)
401         {
402         }
403         else if(f > 0)
404         {
405                 if(argv(f) == name)
406                         self.field = stof(argv(f + 1));
407         }
408         else
409                 stuffcmd(self, strcat("cmd reportcvar ", name, " $", name, "\n"));
410 }
411 void GetCvars(float f)
412 {
413         GetCvars_handleFloat(f, cvar_cl_playerdetailreduction, "cl_playerdetailreduction");
414         GetCvars_handleFloat(f, cvar_cl_nogibs, "cl_nogibs");
415         GetCvars_handleFloat(f, cvar_scr_centertime, "scr_centertime");
416         GetCvars_handleFloat(f, cvar_cl_shownames, "cl_shownames");
417         GetCvars_handleString(f, cvar_g_nexuizversion, "g_nexuizversion");
418 }
419
420 float fexists(string f)
421 {
422         float fh;
423         fh = fopen(f, FILE_READ);
424         if(fh < 0)
425                 return FALSE;
426         fclose(fh);
427         return TRUE;
428 }
429
430 void backtrace(string msg)
431 {
432         float dev;
433         dev = cvar("developer");
434         cvar_set("developer", "1");
435         dprint("\n");
436         dprint("--- CUT HERE ---\nWARNING: ");
437         dprint(msg);
438         dprint("\n");
439         remove(world); // isn't there any better way to cause a backtrace?
440         dprint("\n--- CUT UNTIL HERE ---\n");
441         cvar_set("developer", ftos(dev));
442 }
443
444 void DistributeFragsAmongTeam(entity p, float targetteam, float factor)
445 {
446         float f;
447         float d;
448         float nTeam;
449         entity head;
450
451         if(!teams_matter)
452                 return;
453
454         //if(p.frags < 0)
455         //{
456         //      p.frags = 0; // do not harm the new team!
457         //      return; // won't distribute negative scores
458         //}
459
460         if(p.frags == -666)
461                 return;
462         
463         f = ceil(factor * p.frags);
464         p.frags = p.frags - f;
465
466         nTeam = 0;
467         head = find(world, classname, "player");
468         while(head)
469         {
470                 if(head != p)
471                         if(head.team == targetteam)
472                                 nTeam = nTeam + 1;
473                 head = find(head, classname, "player");
474         }
475
476         if(nTeam == 0)
477                 return;
478
479         head = find(world, classname, "player");
480         while(head)
481         {
482                 if(head != p)
483                         if(head.team == targetteam)
484                         {
485                                 d = floor(f / nTeam);
486                                 head.frags = head.frags + d;
487                                 f = f - d;
488                                 nTeam = nTeam - 1;
489                         }
490                 head = find(head, classname, "player");
491         }
492
493         if(nTeam != 0)
494                 error("nPlayers in team changed!");
495         if(f != 0)
496                 error(strcat("There were ", ftos(f), " frags left. BAD!"));
497 }
498
499 string Team_ColorCode(float teamid)
500 {
501         if(teamid == COLOR_TEAM1)
502                 return "^1";
503         else if(teamid == COLOR_TEAM2)
504                 return "^4";
505         else if(teamid == COLOR_TEAM3)
506                 return "^6";
507         else if(teamid == COLOR_TEAM4)
508                 return "^3";
509         else
510                 return "^7";
511 }
512
513 string decolorize(string s)
514 {
515         string out;
516         out = "";
517         while(s != "")
518         {
519                 float n;
520                 string ch1, ch2;
521                 n = 1;
522                 ch1 = substring(s, 0, 1);
523                 ch2 = substring(s, 1, 1);
524                 if(ch1 == "^")
525                 {
526                         n = 2;
527                         if(ch2 == "^")
528                                 out = strcat(out, "^^");
529                         else if(ch2 == "0")
530                                 out = strcat(out);
531                         else if(ch2 == "1")
532                                 out = strcat(out);
533                         else if(ch2 == "2")
534                                 out = strcat(out);
535                         else if(ch2 == "3")
536                                 out = strcat(out);
537                         else if(ch2 == "4")
538                                 out = strcat(out);
539                         else if(ch2 == "5")
540                                 out = strcat(out);
541                         else if(ch2 == "6")
542                                 out = strcat(out);
543                         else if(ch2 == "7")
544                                 out = strcat(out);
545                         else if(ch2 == "8")
546                                 out = strcat(out);
547                         else if(ch2 == "9")
548                                 out = strcat(out);
549                         else
550                         {
551                                 n = 1;
552                                 out = strcat(out, "^^");
553                         }
554                         s = substring(s, n, strlen(s) - n);
555                 }
556                 else
557                 {
558                         s = substring(s, 1, strlen(s) - 1);
559                         out = strcat(out, ch1);
560                 }
561         }
562         return out;
563 }
564
565 #define CENTERPRIO_POINT 1
566 #define CENTERPRIO_NORMAL 5
567 .float centerprint_priority;
568 .float centerprint_expires;
569 void centerprint_atprio(entity e, float prio, string s)
570 {
571         if(time > e.centerprint_expires)
572                 e.centerprint_priority = 0;
573         if(prio >= e.centerprint_priority)
574         {
575                 e.centerprint_priority = prio;
576                 e.centerprint_expires = time + e.cvar_scr_centertime;
577                 centerprint_builtin(e, s);
578         }
579 }
580 void centerprint_expire(entity e, float prio)
581 {
582         if(prio == e.centerprint_priority)
583         {
584                 e.centerprint_priority = 0;
585                 centerprint_builtin(e, "");
586         }
587 }
588 void centerprint(entity e, string s)
589 {
590         centerprint_atprio(e, CENTERPRIO_NORMAL, s);
591 }