particles: volume weighting = negative count, absolute weighting = positive count
[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 #ifndef CSQC
20 void wordwrap_buffer_sprint(string s)
21 {
22         wordwrap_buffer = strcat(wordwrap_buffer, s);
23         if(s == "\n")
24         {
25                 sprint(self, wordwrap_buffer);
26                 wordwrap_buffer = "";
27         }
28 }
29
30 void wordwrap_sprint(string s, float l)
31 {
32         wordwrap_buffer = "";
33         wordwrap_cb(s, l, wordwrap_buffer_sprint);
34         if(wordwrap_buffer != "")
35                 sprint(self, strcat(wordwrap_buffer, "\n"));
36         wordwrap_buffer = "";
37         return;
38 }
39 #endif
40 #endif
41
42 string unescape(string in)
43 {
44         local float i, len;
45         local string str, s;
46
47         // but it doesn't seem to be necessary in my tests at least
48         in = strzone(in);
49
50         len = strlen(in);
51         str = "";
52         for(i = 0; i < len; ++i)
53         {
54                 s = substring(in, i, 1);
55                 if(s == "\\")
56                 {
57                         s = substring(in, i+1, 1);
58                         if(s == "n")
59                                 str = strcat(str, "\n");
60                         else if(s == "\\")
61                                 str = strcat(str, "\\");
62                         else
63                                 str = strcat(str, substring(in, i, 2));
64                         ++i;
65                 } else
66                         str = strcat(str, s);
67         }
68
69         strunzone(in);
70         return str;
71 }
72
73 void wordwrap_cb(string s, float l, void(string) callback)
74 {
75         local string c;
76         local float lleft, i, j, wlen;
77
78         s = strzone(s);
79         lleft = l;
80         for (i = 0;i < strlen(s);++i)
81         {
82                 if (substring(s, i, 2) == "\\n")
83                 {
84                         callback("\n");
85                         lleft = l;
86                         ++i;
87                 }
88                 else if (substring(s, i, 1) == "\n")
89                 {
90                         callback("\n");
91                         lleft = l;
92                 }
93                 else if (substring(s, i, 1) == " ")
94                 {
95                         if (lleft > 0)
96                         {
97                                 callback(" ");
98                                 lleft = lleft - 1;
99                         }
100                 }
101                 else
102                 {
103                         for (j = i+1;j < strlen(s);++j)
104                                 //    ^^ this skips over the first character of a word, which
105                                 //       is ALWAYS part of the word
106                                 //       this is safe since if i+1 == strlen(s), i will become
107                                 //       strlen(s)-1 at the end of this block and the function
108                                 //       will terminate. A space can't be the first character we
109                                 //       read here, and neither can a \n be the start, since these
110                                 //       two cases have been handled above.
111                         {
112                                 c = substring(s, j, 1);
113                                 if (c == " ")
114                                         break;
115                                 if (c == "\\")
116                                         break;
117                                 if (c == "\n")
118                                         break;
119                                 // we need to keep this tempstring alive even if substring is
120                                 // called repeatedly, so call strcat even though we're not
121                                 // doing anything
122                                 callback("");
123                         }
124                         wlen = j - i;
125                         if (lleft < wlen)
126                         {
127                                 callback("\n");
128                                 lleft = l;
129                         }
130                         callback(substring(s, i, wlen));
131                         lleft = lleft - wlen;
132                         i = j - 1;
133                 }
134         }
135         strunzone(s);
136 }
137
138 float dist_point_line(vector p, vector l0, vector ldir)
139 {
140         ldir = normalize(ldir);
141         
142         // remove the component in line direction
143         p = p - (p * ldir) * ldir;
144
145         // vlen of the remaining vector
146         return vlen(p);
147 }
148
149 void depthfirst(entity start, .entity up, .entity downleft, .entity right, void(entity, entity) funcPre, void(entity, entity) funcPost, entity pass)
150 {
151         entity e;
152         e = start;
153         funcPre(pass, e);
154         while(e.downleft)
155         {
156                 e = e.downleft;
157                 funcPre(pass, e);
158         }
159         funcPost(pass, e);
160         while(e != start)
161         {
162                 if(e.right)
163                 {
164                         e = e.right;
165                         funcPre(pass, e);
166                         while(e.downleft)
167                         {
168                                 e = e.downleft;
169                                 funcPre(pass, e);
170                         }
171                 }
172                 else
173                         e = e.up;
174                 funcPost(pass, e);
175         }
176 }
177
178 float median(float a, float b, float c)
179 {
180         if(a < c)
181                 return bound(a, b, c);
182         return bound(c, b, a);
183 }
184
185 // converts a number to a string with the indicated number of decimals
186 // works for up to 10 decimals!
187 string ftos_decimals(float number, float decimals)
188 {
189         string result;
190         string tmp;
191         float len;
192
193         // if negative, cut off the sign first
194         if(number < 0)
195                 return strcat("-", ftos_decimals(-number, decimals));
196         // it now is always positive!
197
198         // 3.516 -> 352
199         number = floor(number * pow(10, decimals) + 0.5);
200
201         // 352 -> "352"
202         result = ftos(number);
203         len = strlen(result);
204         // does it have a decimal point (should not happen)? If there is one, it is always at len-7)
205                 // if ftos had fucked it up, which should never happen: "34278.000000"
206         if(len >= 7)
207                 if(substring(result, len - 7, 1) == ".")
208                 {
209                         dprint("ftos(integer) has comma? Can't be. Affected result: ", result, "\n");
210                         result = substring(result, 0, len - 7);
211                         len -= 7;
212                 }
213                 // "34278"
214         if(decimals == 0)
215                 return result; // don't insert a point for zero decimals
216         // is it too short? If yes, insert leading zeroes
217         if(len <= decimals)
218         {
219                 result = strcat(substring("0000000000", 0, decimals - len + 1), result);
220                 len = decimals + 1;
221         }
222         // and now... INSERT THE POINT!
223         tmp = substring(result, len - decimals, decimals);
224         result = strcat(substring(result, 0, len - decimals), ".", tmp);
225         return result;
226 }
227
228 float time;
229 vector colormapPaletteColor(float c, float isPants)
230 {
231         switch(c)
232         {
233                 case  0: return '0.800000 0.800000 0.800000';
234                 case  1: return '0.600000 0.400000 0.000000';
235                 case  2: return '0.000000 1.000000 0.501961';
236                 case  3: return '0.000000 1.000000 0.000000';
237                 case  4: return '1.000000 0.000000 0.000000';
238                 case  5: return '0.000000 0.501961 1.000000';
239                 case  6: return '0.000000 1.000000 1.000000';
240                 case  7: return '0.501961 1.000000 0.000000';
241                 case  8: return '0.501961 0.000000 1.000000';
242                 case  9: return '1.000000 0.000000 1.000000';
243                 case 10: return '1.000000 0.000000 0.501961';
244                 case 11: return '0.600000 0.600000 0.600000';
245                 case 12: return '1.000000 1.000000 0.000000';
246                 case 13: return '0.000000 0.000000 1.000000';
247                 case 14: return '1.000000 0.501961 0.000000';
248                 case 15:
249                         if(isPants)
250                                 return
251                                           '1 0 0' * (0.502 + 0.498 * sin(time / 2.7182818285 + 0.0000000000))
252                                         + '0 1 0' * (0.502 + 0.498 * sin(time / 2.7182818285 + 2.0943951024))
253                                         + '0 0 1' * (0.502 + 0.498 * sin(time / 2.7182818285 + 4.1887902048));
254                         else
255                                 return
256                                           '1 0 0' * (0.502 + 0.498 * sin(time / 3.1415926536 + 5.2359877560))
257                                         + '0 1 0' * (0.502 + 0.498 * sin(time / 3.1415926536 + 3.1415926536))
258                                         + '0 0 1' * (0.502 + 0.498 * sin(time / 3.1415926536 + 1.0471975512));
259                 default: return '0.000 0.000 0.000';
260         }
261 }
262
263 // unzone the string, and return it as tempstring. Safe to be called on string_null
264 string fstrunzone(string s)
265 {
266         string sc;
267         if not(s)
268                 return s;
269         sc = strcat(s, "");
270         strunzone(s);
271         return sc;
272 }
273
274 // Databases (hash tables)
275 #define DB_BUCKETS 8192
276 void db_save(float db, string pFilename)
277 {
278         float fh, i, n;
279         fh = fopen(pFilename, FILE_WRITE);
280         if(fh < 0)
281                 error(strcat("Can't write DB to ", pFilename));
282         n = buf_getsize(db);
283         fputs(fh, strcat(ftos(DB_BUCKETS), "\n"));
284         for(i = 0; i < n; ++i)
285                 fputs(fh, strcat(bufstr_get(db, i), "\n"));
286         fclose(fh);
287 }
288
289 float db_create()
290 {
291         return buf_create();
292 }
293
294 float db_load(string pFilename)
295 {
296         float db, fh, i, j, n;
297         string l;
298         db = buf_create();
299         if(db < 0)
300                 return -1;
301         fh = fopen(pFilename, FILE_READ);
302         if(fh < 0)
303                 return db;
304         if(stof(fgets(fh)) == DB_BUCKETS)
305         {
306                 i = 0;
307                 while((l = fgets(fh)))
308                 {
309                         if(l != "")
310                                 bufstr_set(db, i, l);
311                         ++i;
312                 }
313         }
314         else
315         {
316                 // different count of buckets?
317                 // need to reorganize the database then (SLOW)
318                 while((l = fgets(fh)))
319                 {
320                         n = tokenizebyseparator(l, "\\");
321                         for(j = 2; j < n; j += 2)
322                                 db_put(db, uri_unescape(argv(j-1)), uri_unescape(argv(j)));
323                 }
324         }
325         fclose(fh);
326         return db;
327 }
328
329 void db_dump(float db, string pFilename)
330 {
331         float fh, i, j, n, m;
332         fh = fopen(pFilename, FILE_WRITE);
333         if(fh < 0)
334                 error(strcat("Can't dump DB to ", pFilename));
335         n = buf_getsize(db);
336         fputs(fh, "0\n");
337         for(i = 0; i < n; ++i)
338         {
339                 m = tokenizebyseparator(bufstr_get(db, i), "\\");
340                 for(j = 2; j < m; j += 2)
341                         fputs(fh, strcat("\\", argv(j-1), "\\", argv(j), "\n"));
342         }
343         fclose(fh);
344 }
345
346 void db_close(float db)
347 {
348         buf_del(db);
349 }
350
351 string db_get(float db, string pKey)
352 {
353         float h;
354         h = mod(crc16(FALSE, pKey), DB_BUCKETS);
355         return uri_unescape(infoget(bufstr_get(db, h), pKey));
356 }
357
358 void db_put(float db, string pKey, string pValue)
359 {
360         float h;
361         h = mod(crc16(FALSE, pKey), DB_BUCKETS);
362         bufstr_set(db, h, infoadd(bufstr_get(db, h), pKey, uri_escape(pValue)));
363 }
364
365 void db_test()
366 {
367         float db, i;
368         print("LOAD...\n");
369         db = db_load("foo.db");
370         print("LOADED. FILL...\n");
371         for(i = 0; i < DB_BUCKETS; ++i)
372                 db_put(db, ftos(random()), "X");
373         print("FILLED. SAVE...\n");
374         db_save(db, "foo.db");
375         print("SAVED. CLOSE...\n");
376         db_close(db);
377         print("CLOSED.\n");
378 }
379
380 // Multiline text file buffers
381 float buf_load(string pFilename)
382 {
383         float buf, fh, i;
384         string l;
385         buf = buf_create();
386         if(buf < 0)
387                 return -1;
388         fh = fopen(pFilename, FILE_READ);
389         if(fh < 0)
390                 return buf;
391         i = 0;
392         while((l = fgets(fh)))
393         {
394                 bufstr_set(buf, i, l);
395                 ++i;
396         }
397         fclose(fh);
398         return buf;
399 }
400
401 void buf_save(float buf, string pFilename)
402 {
403         float fh, i, n;
404         fh = fopen(pFilename, FILE_WRITE);
405         if(fh < 0)
406                 error(strcat("Can't write buf to ", pFilename));
407         n = buf_getsize(buf);
408         for(i = 0; i < n; ++i)
409                 fputs(fh, strcat(bufstr_get(buf, i), "\n"));
410         fclose(fh);
411 }
412
413 string GametypeNameFromType(float g)
414 {
415         if      (g == GAME_DEATHMATCH) return "dm";
416         else if (g == GAME_TEAM_DEATHMATCH) return "tdm";
417         else if (g == GAME_DOMINATION) return "dom";
418         else if (g == GAME_CTF) return "ctf";
419         else if (g == GAME_RUNEMATCH) return "rune";
420         else if (g == GAME_LMS) return "lms";
421         else if (g == GAME_ARENA) return "arena";
422         else if (g == GAME_KEYHUNT) return "kh";
423         else if (g == GAME_ONSLAUGHT) return "ons";
424         else if (g == GAME_ASSAULT) return "as";
425         else if (g == GAME_RACE) return "race";
426         return "dm";
427 }
428
429 string mmsss(float tenths)
430 {
431         float minutes;
432         string s;
433         tenths = floor(tenths + 0.5);
434         minutes = floor(tenths / 600);
435         tenths -= minutes * 600;
436         s = ftos(1000 + tenths);
437         return strcat(ftos(minutes), ":", substring(s, 1, 2), ".", substring(s, 3, 1));
438 }
439
440 string ScoreString(float vflags, float value)
441 {
442         string valstr;
443         float l;
444
445         value = floor(value + 0.5); // round
446
447         if((value == 0) && (vflags & (SFL_HIDE_ZERO | SFL_RANK | SFL_TIME)))
448                 valstr = "";
449         else if(vflags & SFL_RANK)
450         {
451                 valstr = ftos(value);
452                 l = strlen(valstr);
453                 if((l >= 2) && (substring(valstr, l - 2, 1) == "1"))
454                         valstr = strcat(valstr, "th");
455                 else if(substring(valstr, l - 1, 1) == "1")
456                         valstr = strcat(valstr, "st");
457                 else if(substring(valstr, l - 1, 1) == "2")
458                         valstr = strcat(valstr, "nd");
459                 else if(substring(valstr, l - 1, 1) == "3")
460                         valstr = strcat(valstr, "rd");
461                 else
462                         valstr = strcat(valstr, "th");
463         }
464         else if(vflags & SFL_TIME)
465                 valstr = mmsss(value);
466         else
467                 valstr = ftos(value);
468         
469         return valstr;
470 }
471
472 vector cross(vector a, vector b)
473 {
474         return
475                 '1 0 0' * (a_y * b_z - a_z * b_y)
476         +       '0 1 0' * (a_z * b_x - a_x * b_z)
477         +       '0 0 1' * (a_x * b_y - a_y * b_x);
478 }