]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/common/util.qc
centerprint command available for the client via menu_cmd cp blah, might make a more...
[divverent/nexuiz.git] / data / qcsrc / common / util.qc
1 string wordwrap_buffer;
2
3 void wordwrap_buffer_put(string s)
4 {
5         wordwrap_buffer = strcat(wordwrap_buffer, s);
6 }
7
8 string wordwrap(string s, float l)
9 {
10         string r;
11         wordwrap_buffer = "";
12         wordwrap_cb(s, l, wordwrap_buffer_put);
13         r = wordwrap_buffer;
14         wordwrap_buffer = "";
15         return r;
16 }
17
18 #ifndef MENUQC
19 void wordwrap_buffer_sprint(string s)
20 {
21         wordwrap_buffer = strcat(wordwrap_buffer, s);
22         if(s == "\n")
23         {
24                 sprint(self, wordwrap_buffer);
25                 wordwrap_buffer = "";
26         }
27 }
28
29 void wordwrap_sprint(string s, float l)
30 {
31         wordwrap_buffer = "";
32         wordwrap_cb(s, l, wordwrap_buffer_sprint);
33         if(wordwrap_buffer != "")
34                 sprint(self, strcat(wordwrap_buffer, "\n"));
35         wordwrap_buffer = "";
36         return;
37 }
38 #endif
39
40 string unescape(string in)
41 {
42         local float i, len;
43         local string str, s;
44
45         // but it doesn't seem to be necessary in my tests at least
46         in = strzone(in);
47
48         len = strlen(in);
49         str = "";
50         for(i = 0; i < len; ++i)
51         {
52                 s = substring(in, i, 1);
53                 if(s == "\\")
54                 {
55                         s = substring(in, i+1, 1);
56                         if(s == "n")
57                                 str = strcat(str, "\n");
58                         else if(s == "\\")
59                                 str = strcat(str, "\\");
60                         else
61                                 str = strcat(str, substring(in, i, 2));
62                         ++i;
63                 } else
64                         str = strcat(str, s);
65         }
66
67         strunzone(in);
68         return str;
69 }
70
71 void wordwrap_cb(string s, float l, void(string) callback)
72 {
73         local string c;
74         local float lleft, i, j, wlen;
75
76         s = strzone(s);
77         lleft = l;
78         for (i = 0;i < strlen(s);i++)
79         {
80                 if (substring(s, i, 2) == "\\n")
81                 {
82                         callback("\n");
83                         lleft = l;
84                         i++;
85                 }
86                 else if (substring(s, i, 1) == "\n")
87                 {
88                         callback("\n");
89                         lleft = l;
90                 }
91                 else if (substring(s, i, 1) == " ")
92                 {
93                         if (lleft > 0)
94                         {
95                                 callback(" ");
96                                 lleft = lleft - 1;
97                         }
98                 }
99                 else
100                 {
101                         for (j = i+1;j < strlen(s);j++)
102                                 //    ^^ this skips over the first character of a word, which
103                                 //       is ALWAYS part of the word
104                                 //       this is safe since if i+1 == strlen(s), i will become
105                                 //       strlen(s)-1 at the end of this block and the function
106                                 //       will terminate. A space can't be the first character we
107                                 //       read here, and neither can a \n be the start, since these
108                                 //       two cases have been handled above.
109                         {
110                                 c = substring(s, j, 1);
111                                 if (c == " ")
112                                         break;
113                                 if (c == "\\")
114                                         break;
115                                 if (c == "\n")
116                                         break;
117                                 // we need to keep this tempstring alive even if substring is
118                                 // called repeatedly, so call strcat even though we're not
119                                 // doing anything
120                                 callback("");
121                         }
122                         wlen = j - i;
123                         if (lleft < wlen)
124                         {
125                                 callback("\n");
126                                 lleft = l;
127                         }
128                         callback(substring(s, i, wlen));
129                         lleft = lleft - wlen;
130                         i = j - 1;
131                 }
132         }
133         strunzone(s);
134 }
135
136 float dist_point_line(vector p, vector l0, vector ldir)
137 {
138         ldir = normalize(ldir);
139         
140         // remove the component in line direction
141         p = p - (p * ldir) * ldir;
142
143         // vlen of the remaining vector
144         return vlen(p);
145 }
146
147 void depthfirst(entity start, .entity up, .entity downleft, .entity right, void(entity, entity) funcPre, void(entity, entity) funcPost, entity pass)
148 {
149         entity e;
150         e = start;
151         funcPre(pass, e);
152         while(e.downleft)
153         {
154                 e = e.downleft;
155                 funcPre(pass, e);
156         }
157         funcPost(pass, e);
158         while(e != start)
159         {
160                 if(e.right)
161                 {
162                         e = e.right;
163                         funcPre(pass, e);
164                         while(e.downleft)
165                         {
166                                 e = e.downleft;
167                                 funcPre(pass, e);
168                         }
169                 }
170                 else
171                         e = e.up;
172                 funcPost(pass, e);
173         }
174 }
175
176 float median(float a, float b, float c)
177 {
178         if(a < c)
179                 return bound(a, b, c);
180         return bound(c, b, a);
181 }
182
183 // converts a number to a string with the indicated number of decimals
184 // works for up to 10 decimals!
185 string ftos_decimals(float number, float decimals)
186 {
187         string result;
188         string tmp;
189         float len;
190
191         // if negative, cut off the sign first
192         if(number < 0)
193                 return strcat("-", ftos_decimals(-number, decimals));
194         // it now is always positive!
195
196         // 3.516 -> 352
197         number = floor(number * pow(10, decimals) + 0.5);
198
199         // 352 -> "352"
200         result = ftos(number);
201         len = strlen(result);
202         // does it have a decimal point (should not happen)? If there is one, it is always at len-7)
203                 // if ftos had fucked it up, which should never happen: "34278.000000"
204         if(len >= 7)
205                 if(substring(result, len - 7, 1) == ".")
206                 {
207                         dprint("ftos(integer) has comma? Can't be. Affected result: ", result, "\n");
208                         result = substring(result, 0, len - 7);
209                         len -= 7;
210                 }
211                 // "34278"
212         if(decimals == 0)
213                 return result; // don't insert a point for zero decimals
214         // is it too short? If yes, insert leading zeroes
215         if(len <= decimals)
216         {
217                 result = strcat(substring("0000000000", 0, decimals - len + 1), result);
218                 len = decimals + 1;
219         }
220         // and now... INSERT THE POINT!
221         tmp = substring(result, len - decimals, decimals);
222         result = strcat(substring(result, 0, len - decimals), ".", tmp);
223         return result;
224 }
225
226 float time;
227 vector colormapPaletteColor(float c, float isPants)
228 {
229         switch(c)
230         {
231                 case  0: return '0.733 0.733 0.733';
232                 case  1: return '0.451 0.341 0.122';
233                 case  2: return '0.000 0.733 0.733';
234                 case  3: return '0.000 1.000 0.000';
235                 case  4: return '1.000 0.000 0.000';
236                 case  5: return '0.000 0.502 1.000';
237                 case  6: return '0.812 0.561 0.169';
238                 case  7: return '0.718 0.529 0.420';
239                 case  8: return '0.765 0.545 0.667';
240                 case  9: return '1.000 0.000 1.000';
241                 case 10: return '0.639 0.529 0.482';
242                 case 11: return '0.310 0.388 0.341';
243                 case 12: return '1.000 1.000 0.000';
244                 case 13: return '0.000 0.000 1.000';
245                 case 14: return '1.000 0.502 0.000';
246                 case 15:
247                         if(isPants)
248                                 return
249                                           '1 0 0' * (0.502 + 0.498 * sin(time / 2.7182818285 + 0.0000000000))
250                                         + '0 1 0' * (0.502 + 0.498 * sin(time / 2.7182818285 + 2.0943951024))
251                                         + '0 0 1' * (0.502 + 0.498 * sin(time / 2.7182818285 + 4.1887902048));
252                         else
253                                 return
254                                           '1 0 0' * (0.502 + 0.498 * sin(time / 3.1415926536 + 5.2359877560))
255                                         + '0 1 0' * (0.502 + 0.498 * sin(time / 3.1415926536 + 3.1415926536))
256                                         + '0 0 1' * (0.502 + 0.498 * sin(time / 3.1415926536 + 1.0471975512));
257                 default: return '0.000 0.000 0.000';
258         }
259 }
260
261 // unzone the string, and return it as tempstring. Safe to be called on string_null
262 string fstrunzone(string s)
263 {
264         string sc;
265         if not(s)
266                 return s;
267         sc = strcat(s, "");
268         strunzone(s);
269         return sc;
270 }
271
272 // Databases (hash tables)
273 #define DB_BUCKETS 8192
274 void db_save(float db, string pFilename)
275 {
276         float fh, i, n;
277         fh = fopen(pFilename, FILE_WRITE);
278         if(fh < 0)
279                 error(strcat("Can't write DB to ", pFilename));
280         n = buf_getsize(db);
281         fputs(fh, strcat(ftos(DB_BUCKETS), "\n"));
282         for(i = 0; i < n; ++i)
283                 fputs(fh, strcat(bufstr_get(db, i), "\n"));
284         fclose(fh);
285 }
286
287 float db_create()
288 {
289         return buf_create();
290 }
291
292 float db_load(string pFilename)
293 {
294         float db, fh, i, j, n;
295         string l;
296         db = buf_create();
297         if(db < 0)
298                 return -1;
299         fh = fopen(pFilename, FILE_READ);
300         if(fh < 0)
301                 return db;
302         if(stof(fgets(fh)) == DB_BUCKETS)
303         {
304                 i = 0;
305                 while((l = fgets(fh)))
306                 {
307                         if(l != "")
308                                 bufstr_set(db, i, l);
309                         ++i;
310                 }
311         }
312         else
313         {
314                 // different count of buckets?
315                 // need to reorganize the database then (SLOW)
316                 while((l = fgets(fh)))
317                 {
318                         n = tokenizebyseparator(l, "\\");
319                         for(j = 2; j < n; j += 2)
320                                 db_put(db, uri_unescape(argv(j-1)), uri_unescape(argv(j)));
321                 }
322         }
323         fclose(fh);
324         return db;
325 }
326
327 void db_dump(float db, string pFilename)
328 {
329         float fh, i, j, n, m;
330         fh = fopen(pFilename, FILE_WRITE);
331         if(fh < 0)
332                 error(strcat("Can't dump DB to ", pFilename));
333         n = buf_getsize(db);
334         fputs(fh, "0\n");
335         for(i = 0; i < n; ++i)
336         {
337                 m = tokenizebyseparator(bufstr_get(db, i), "\\");
338                 for(j = 2; j < m; j += 2)
339                         fputs(fh, strcat("\\", argv(j-1), "\\", argv(j), "\n"));
340         }
341         fclose(fh);
342 }
343
344 void db_close(float db)
345 {
346         buf_del(db);
347 }
348
349 string db_get(float db, string pKey)
350 {
351         float h;
352         h = mod(crc16(FALSE, pKey), DB_BUCKETS);
353         return uri_unescape(infoget(bufstr_get(db, h), pKey));
354 }
355
356 void db_put(float db, string pKey, string pValue)
357 {
358         float h;
359         h = mod(crc16(FALSE, pKey), DB_BUCKETS);
360         bufstr_set(db, h, infoadd(bufstr_get(db, h), pKey, uri_escape(pValue)));
361 }
362
363 void db_test()
364 {
365         float db, i;
366         print("LOAD...\n");
367         db = db_load("foo.db");
368         print("LOADED. FILL...\n");
369         for(i = 0; i < DB_BUCKETS; ++i)
370                 db_put(db, ftos(random()), "X");
371         print("FILLED. SAVE...\n");
372         db_save(db, "foo.db");
373         print("SAVED. CLOSE...\n");
374         db_close(db);
375         print("CLOSED.\n");
376 }
377
378 // Multiline text file buffers
379 float buf_load(string pFilename)
380 {
381         float buf, fh, i;
382         string l;
383         buf = buf_create();
384         if(buf < 0)
385                 return -1;
386         fh = fopen(pFilename, FILE_READ);
387         if(fh < 0)
388                 return buf;
389         i = 0;
390         while((l = fgets(fh)))
391         {
392                 bufstr_set(buf, i, l);
393                 ++i;
394         }
395         fclose(fh);
396         return buf;
397 }
398
399 void buf_save(float buf, string pFilename)
400 {
401         float fh, i, n;
402         fh = fopen(pFilename, FILE_WRITE);
403         if(fh < 0)
404                 error(strcat("Can't write buf to ", pFilename));
405         n = buf_getsize(buf);
406         for(i = 0; i < n; ++i)
407                 fputs(fh, strcat(bufstr_get(buf, i), "\n"));
408         fclose(fh);
409 }