centerprints now are managed independently per line
[divverent/nexuiz.git] / data / qcsrc / server / miscfunctions.qc
1 string W_Name(float weaponid);
2 float(float index) weapon_translateindextoflag;
3
4 float logfile_open;
5 float logfile;
6
7 void(string s) bcenterprint
8 {
9         entity head;
10         head = find(world, classname, "player");
11         while(head)
12         {
13                 if(clienttype(head) == CLIENTTYPE_REAL)
14                         centerprint(head, s);
15                 head = find(head, classname, "player");
16         }
17 }
18
19 void(string s, float check_dangerous) ServerConsoleEcho =
20 {
21         local string ch;
22         localcmd("echo \"");
23         if(check_dangerous)
24         {
25                 while(strlen(s))
26                 {
27                         ch = substring(s, 0, 1);
28                         if(ch != "\"" && ch != "\r" && ch != "\n")
29                                 localcmd(ch);
30                         s = substring(s, 1, strlen(s) - 1);
31                 }
32         }
33         else
34         {
35                 localcmd(s);
36         }
37         localcmd("\"\n");
38 }
39
40 void(string s, float check_dangerous) GameLogEcho =
41 {
42         string fn;
43         float matches;
44
45         if(cvar("sv_eventlog_files"))
46         {
47                 if(!logfile_open)
48                 {
49                         logfile_open = TRUE;
50                         matches = cvar("sv_eventlog_files_counter") + 1;
51                         cvar_set("sv_eventlog_files_counter", ftos(matches));
52                         fn = ftos(matches);
53                         if(strlen(fn) < 8)
54                                 fn = strcat(substring("00000000", 0, 8 - strlen(fn)), fn);
55                         fn = strcat(cvar_string("sv_eventlog_files_nameprefix"), fn, cvar_string("sv_eventlog_files_namesuffix"));
56                         logfile = fopen(fn, FILE_APPEND);
57                 }
58                 if(logfile >= 0)
59                         fputs(logfile, strcat(s, "\n"));
60         }
61         if(cvar("sv_eventlog_console"))
62         {
63                 ServerConsoleEcho(s, check_dangerous);
64         }
65 }
66
67 void() GameLogInit =
68 {
69         logfile_open = 0;
70         // will be opened later
71 }
72
73 void() GameLogClose =
74 {
75         if(logfile_open && logfile >= 0)
76         {
77                 fclose(logfile);
78                 logfile = -1;
79         }
80 }
81
82 float math_mod(float a, float b)
83 {
84         return a - (floor(a / b) * b);
85 }
86
87 vector find_floor(vector org)
88 {
89         traceline(org + '0 0 5', org - '0 0 255', TRUE, self);
90         if (trace_fraction < 1)
91                 return trace_endpos;
92         else
93                 return org;
94 }
95
96 void relocate_spawnpoint()
97 {
98         vector org, loc;
99         string error_msg;
100
101         error_msg = "spawn point too close to a wall";
102
103         org = find_floor(self.origin) + '0 0 30';
104
105         traceline(org, org - '18 0 0', TRUE, world);
106         if(trace_fraction < 1)
107         {
108                 loc = trace_endpos;
109                 traceline(loc, loc + '36 0 0', TRUE, world);
110                 if(trace_fraction >= 1 && !self.noalign)
111                         org = loc + '18 0 0';
112                 else
113                 {
114                         objerror(error_msg);
115                         return;
116                 }
117         }
118
119         traceline (org, org - '-18 0 0', TRUE, world);
120         if (trace_fraction < 1)
121         {
122                 loc = trace_endpos;
123                 traceline (loc, loc + '-36 0 0', TRUE, world);
124                 if(trace_fraction >= 1 && !self.noalign)
125                         org = loc + '-18 0 0';
126                 else
127                 {
128                         objerror(error_msg);
129                         return;
130                 }
131         }
132
133         traceline (org, org - '0 18 0' , TRUE, world);
134         if (trace_fraction < 1)
135         {
136                 loc = trace_endpos;
137                 traceline (loc, loc + '0 36 0', TRUE, world);
138                 if(trace_fraction >= 1 && !self.noalign)
139                         org = loc + '0 18 0';
140                 else
141                 {
142                         objerror(error_msg);
143                         return;
144                 }
145         }
146
147         traceline (org, org - '0 -18 0', TRUE, world);
148         if (trace_fraction < 1)
149         {
150                 loc = trace_endpos;
151                 traceline (loc, loc + '0 -36 0', TRUE, world);
152                 if(trace_fraction >= 1 && !self.noalign)
153                         org = loc + '0 -18 0';
154                 else
155                 {
156                         objerror(error_msg);
157                         return;
158                 }
159         }
160
161         if(!self.noalign)
162                 setorigin(self, org);
163 }
164
165 // NOTE: DO NOT USE THIS FUNCTION TOO OFTEN.
166 // IT WILL MOST PROBABLY DESTROY _ALL_ OTHER TEMP
167 // STRINGS AND TAKE QUITE LONG. haystack and needle MUST
168 // BE CONSTANT OR strzoneD!
169 float(string haystack, string needle, float offset) strstr =
170 {
171         float len, endpos;
172         string found;
173         len = strlen(needle);
174         endpos = strlen(haystack) - len;
175         while(offset < endpos)
176         {
177                 found = substring(haystack, offset, len);
178                 if(found == needle)
179                         return offset;
180                 offset = offset + 1;
181         }
182         return -1;
183 }
184
185 float NUM_NEAREST_ENTITIES = 4;
186 entity nearest_entity[NUM_NEAREST_ENTITIES];
187 float nearest_length[NUM_NEAREST_ENTITIES];
188 entity(vector point, .string field, string value, vector axismod) findnearest =
189 {
190         entity localhead;
191         float i;
192         float j;
193         float len;
194         vector dist;
195
196         float num_nearest;
197         num_nearest = 0;
198
199         localhead = find(world, field, value);
200         while(localhead)
201         {
202                 if((localhead.items == IT_KEY1 || localhead.items == IT_KEY2) && localhead.target == "###item###")
203                         dist = localhead.oldorigin;
204                 else
205                         dist = localhead.origin;
206                 dist = dist - point;
207                 dist = dist_x * axismod_x * '1 0 0' + dist_y * axismod_y * '0 1 0' + dist_z * axismod_z * '0 0 1';
208                 len = vlen(dist);
209
210                 for(i = 0; i < num_nearest; ++i)
211                 {
212                         if(len < nearest_length[i])
213                                 break;
214                 }
215
216                 // now i tells us where to insert at
217                 //   INSERTION SORT! YOU'VE SEEN IT! RUN!
218                 if(i < NUM_NEAREST_ENTITIES)
219                 {
220                         for(j = NUM_NEAREST_ENTITIES - 1; j >= i; --j)
221                         {
222                                 nearest_length[j + 1] = nearest_length[j];
223                                 nearest_entity[j + 1] = nearest_entity[j];
224                         }
225                         nearest_length[i] = len;
226                         nearest_entity[i] = localhead;
227                         if(num_nearest < NUM_NEAREST_ENTITIES)
228                                 num_nearest = num_nearest + 1;
229                 }
230
231                 localhead = find(localhead, field, value);
232         }
233
234         // now use the first one from our list that we can see
235         for(i = 0; i < num_nearest; ++i)
236         {
237                 traceline(point, nearest_entity[i].origin, TRUE, world);
238                 if(trace_fraction == 1)
239                 {
240                         if(i != 0)
241                         {
242                                 dprint("Nearest point (");
243                                 dprint(nearest_entity[0].netname);
244                                 dprint(") is not visible, using a visible one.\n");
245                         }
246                         return nearest_entity[i];
247                 }
248         }
249
250         if(num_nearest == 0)
251                 return world;
252
253         dprint("Not seeing any location point, using nearest as fallback.\n");
254         /* DEBUGGING CODE:
255         dprint("Candidates were: ");
256         for(j = 0; j < num_nearest; ++j)
257         {
258                 if(j != 0)
259                         dprint(", ");
260                 dprint(nearest_entity[j].netname);
261         }
262         dprint("\n");
263         */
264
265         return nearest_entity[0];
266 }
267
268 void() target_location =
269 {
270         self.classname = "target_location";
271         // location name in netname
272         // eventually support: count, teamgame selectors, line of sight?
273 };
274
275 void() info_location =
276 {
277         self.classname = "target_location";
278         self.message = self.netname;
279 };
280
281 string NearestLocation(vector p)
282 {
283         entity loc;
284         string ret;
285         ret = "somewhere";
286         loc = findnearest(p, classname, "target_location", '1 1 1');
287         if(loc)
288         {
289                 ret = loc.message;
290         }
291         else
292         {
293                 loc = findnearest(p, target, "###item###", '1 1 4');
294                 if(loc)
295                         ret = loc.netname;
296         }
297         return ret;
298 }
299
300 string(string msg) formatmessage =
301 {
302         float p;
303         float n;
304         string msg_save;
305         string escape;
306         string replacement;
307         msg_save = strzone(msg);
308         p = 0;
309         n = 7;
310         while(1)
311         {
312                 if(n < 1)
313                         break; // too many replacements
314                 n = n - 1;
315                 p = strstr(msg_save, "%", p); // NOTE: this destroys msg as it's a tempstring!
316                 if(p < 0)
317                         break;
318                 replacement = substring(msg_save, p, 2);
319                 escape = substring(msg_save, p + 1, 1);
320                 if(escape == "%")
321                         replacement = "%";
322                 else if(escape == "a")
323                         replacement = ftos(floor(self.armorvalue));
324                 else if(escape == "h")
325                         replacement = ftos(floor(self.health));
326                 else if(escape == "l")
327                         replacement = NearestLocation(self.origin);
328                 else if(escape == "y")
329                         replacement = NearestLocation(self.cursor_trace_endpos);
330                 else if(escape == "d")
331                         replacement = NearestLocation(self.death_origin);
332                 else if(escape == "w")
333                 {
334                         float wep;
335                         wep = self.weapon;
336                         if(!wep)
337                                 wep = self.switchweapon;
338                         if(!wep)
339                                 wep = self.cnt;
340                         replacement = W_Name(wep);
341                 }
342                 else if(escape == "W")
343                 {
344                         if(self.items & IT_SHELLS) replacement = "shells";
345                         else if(self.items & IT_NAILS) replacement = "bullets";
346                         else if(self.items & IT_ROCKETS) replacement = "rockets";
347                         else if(self.items & IT_CELLS) replacement = "cells";
348                         else replacement = "batteries"; // ;)
349                 }
350                 else if(escape == "x")
351                 {
352                         replacement = self.cursor_trace_ent.netname;
353                         if(!replacement || !self.cursor_trace_ent)
354                                 replacement = "nothing";
355                 }
356                 msg = strcat(substring(msg_save, 0, p), replacement);
357                 msg = strcat(msg, substring(msg_save, p+2, strlen(msg_save) - (p+2)));
358                 strunzone(msg_save);
359                 msg_save = strzone(msg);
360                 p = p + 2;
361         }
362         msg = strcat(msg_save);
363         strunzone(msg_save);
364         return msg;
365 }
366
367 /*
368 =============
369 GetCvars
370 =============
371 Called with:
372   0:  sends the request
373   >0: receives a cvar from name=argv(f) value=argv(f+1)
374 */
375 void GetCvars_handleString(float f, .string field, string name)
376 {
377         if(f)
378         {
379                 if(argv(f) == name)
380                         self.field = argv(f + 1);
381         }
382         else
383                 stuffcmd(self, strcat("cmd reportcvar ", name, " $", name, "\n"));
384 }
385 void GetCvars_handleFloat(float f, .float field, string name)
386 {
387         if(f)
388         {
389                 if(argv(f) == name)
390                         self.field = stof(argv(f + 1));
391         }
392         else
393                 stuffcmd(self, strcat("cmd reportcvar ", name, " $", name, "\n"));
394 }
395 void GetCvars(float f)
396 {
397         GetCvars_handleFloat(f, cvar_cl_playerdetailreduction, "cl_playerdetailreduction");
398         GetCvars_handleFloat(f, cvar_cl_nogibs, "cl_nogibs");
399         GetCvars_handleFloat(f, cvar_scr_centertime, "scr_centertime");
400 }
401
402 float fexists(string f)
403 {
404         float fh;
405         fh = fopen(f, FILE_READ);
406         if(fh < 0)
407                 return FALSE;
408         fclose(fh);
409         return TRUE;
410 }