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