1 /* vim: set tabstop=4: */
2 /*
3 Copyright (C) 1996-1997 Id Software, Inc.
4 Copyright (C) 2016 Spike
5
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14
15 See the GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20
21 */
22
23 //provides a few convienience extensions, primarily builtins, but also autocvars.
24 //Also note the set+seta features.
25
26
27 #include "quakedef.h"
28 #include "q_ctype.h"
29
PR_GetVMScale(void)30 static float PR_GetVMScale(void)
31 {
32 //sigh, this is horrible (divides glwidth)
33 float s = CLAMP (1.0, scr_sbarscale.value, (float)glwidth / 320.0);
34 return s;
35 }
36
37 //there's a few different aproaches to tempstrings...
38 //the lame way is to just have a single one (vanilla).
39 //the only slightly less lame way is to just cycle between 16 or so (most engines).
40 //one funky way is to allocate a single large buffer and just concatenate it for more tempstring space. don't forget to resize (dp).
41 //alternatively, just allocate them persistently and purge them only when there appear to be no more references to it (fte). makes strzone redundant.
42
43 extern cvar_t sv_gameplayfix_setmodelrealbox, r_fteparticles;
44 cvar_t pr_checkextension = {"pr_checkextension", "1", CVAR_NONE}; //spike - enables qc extensions. if 0 then they're ALL BLOCKED! MWAHAHAHA! *cough* *splutter*
45 static int pr_ext_warned_particleeffectnum; //so these only spam once per map
46
47 static void *PR_FindExtGlobal(int type, const char *name);
48 void SV_CheckVelocity (edict_t *ent);
49
50 typedef enum multicast_e
51 {
52 MULTICAST_ALL_U,
53 MULTICAST_PHS_U,
54 MULTICAST_PVS_U,
55 MULTICAST_ALL_R,
56 MULTICAST_PHS_R,
57 MULTICAST_PVS_R,
58
59 MULTICAST_ONE_U,
60 MULTICAST_ONE_R,
61 MULTICAST_INIT
62 } multicast_t;
63 static void SV_Multicast(multicast_t to, float *org, int msg_entity, unsigned int requireext2);
64
65 #define Z_StrDup(s) strcpy(Z_Malloc(strlen(s)+1), s)
66 #define RETURN_EDICT(e) (((int *)qcvm->globals)[OFS_RETURN] = EDICT_TO_PROG(e))
67
PR_MakeTempString(const char * val)68 int PR_MakeTempString (const char *val)
69 {
70 char *tmp = PR_GetTempString();
71 q_strlcpy(tmp, val, STRINGTEMP_LENGTH);
72 return PR_SetEngineString(tmp);
73 }
74
75 #define ishex(c) ((c>='0' && c<= '9') || (c>='a' && c<='f') || (c>='A' && c<='F'))
dehex(char c)76 static int dehex(char c)
77 {
78 if (c >= '0' && c <= '9')
79 return c-'0';
80 if (c >= 'A' && c <= 'F')
81 return c-('A'-10);
82 if (c >= 'a' && c <= 'f')
83 return c-('a'-10);
84 return 0;
85 }
86 //returns the next char...
87 struct markup_s
88 {
89 const unsigned char *txt;
90 vec4_t tint; //predefined colour that applies to the entire string
91 vec4_t colour; //colour for the specific glyph in question
92 unsigned char mask;
93 };
PR_Markup_Begin(struct markup_s * mu,const char * text,float * rgb,float alpha)94 void PR_Markup_Begin(struct markup_s *mu, const char *text, float *rgb, float alpha)
95 {
96 if (*text == '\1' || *text == '\2')
97 {
98 mu->mask = 128;
99 text++;
100 }
101 else
102 mu->mask = 0;
103 mu->txt = (const unsigned char *)text;
104 VectorCopy(rgb, mu->tint);
105 mu->tint[3] = alpha;
106 VectorCopy(rgb, mu->colour);
107 mu->colour[3] = alpha;
108 }
PR_Markup_Parse(struct markup_s * mu)109 int PR_Markup_Parse(struct markup_s *mu)
110 {
111 static const vec4_t q3rgb[10] = {
112 {0.00,0.00,0.00, 1.0},
113 {1.00,0.33,0.33, 1.0},
114 {0.00,1.00,0.33, 1.0},
115 {1.00,1.00,0.33, 1.0},
116 {0.33,0.33,1.00, 1.0},
117 {0.33,1.00,1.00, 1.0},
118 {1.00,0.33,1.00, 1.0},
119 {1.00,1.00,1.00, 1.0},
120 {1.00,1.00,1.00, 0.5},
121 {0.50,0.50,0.50, 1.0}
122 };
123 unsigned int c;
124 const float *f;
125 while ((c = *mu->txt))
126 {
127 if (c == '^' && pr_checkextension.value)
128 { //parse markup like FTE/DP might.
129 switch(mu->txt[1])
130 {
131 case '^': //doubled up char for escaping.
132 mu->txt++;
133 break;
134 case '0': //black
135 case '1': //red
136 case '2': //green
137 case '3': //yellow
138 case '4': //blue
139 case '5': //cyan
140 case '6': //magenta
141 case '7': //white
142 case '8': //white+half-alpha
143 case '9': //grey
144 f = q3rgb[mu->txt[1]-'0'];
145 mu->colour[0] = mu->tint[0] * f[0];
146 mu->colour[1] = mu->tint[1] * f[1];
147 mu->colour[2] = mu->tint[2] * f[2];
148 mu->colour[3] = mu->tint[3] * f[3];
149 mu->txt+=2;
150 continue;
151 case 'h': //toggle half-alpha
152 if (mu->colour[3] != mu->tint[3] * 0.5)
153 mu->colour[3] = mu->tint[3] * 0.5;
154 else
155 mu->colour[3] = mu->tint[3];
156 mu->txt+=2;
157 continue;
158 case 'd': //reset to defaults (fixme: should reset ^m without resetting \1)
159 mu->colour[0] = mu->tint[0];
160 mu->colour[1] = mu->tint[1];
161 mu->colour[2] = mu->tint[2];
162 mu->colour[3] = mu->tint[3];
163 mu->mask = 0;
164 mu->txt+=2;
165 break;
166 case 'b': //blink
167 case 's': //modstack push
168 case 'r': //modstack restore
169 mu->txt+=2;
170 continue;
171 case 'x': //RGB 12-bit colour
172 if (ishex(mu->txt[2]) && ishex(mu->txt[3]) && ishex(mu->txt[4]))
173 {
174 mu->colour[0] = mu->tint[0] * dehex(mu->txt[2])/15.0;
175 mu->colour[1] = mu->tint[1] * dehex(mu->txt[3])/15.0;
176 mu->colour[2] = mu->tint[2] * dehex(mu->txt[4])/15.0;
177 mu->txt+=5;
178 continue;
179 }
180 break; //malformed
181 case '[': //start fte's ^[text\key\value\key\value^] links
182 case ']': //end link
183 break; //fixme... skip the keys, recolour properly, etc
184 // txt+=2;
185 // continue;
186 case '&':
187 if ((ishex(mu->txt[2])||mu->txt[2]=='-') && (ishex(mu->txt[3])||mu->txt[3]=='-'))
188 { //ignore fte's fore/back ansi colours
189 mu->txt += 4;
190 continue;
191 }
192 break; //malformed
193 case 'a': //alternate charset (read: masked)...
194 case 'm': //toggle masking.
195 mu->txt+=2;
196 mu->mask ^= 128;
197 continue;
198 case 'U': //ucs-2 unicode codepoint
199 if (ishex(mu->txt[2]) && ishex(mu->txt[3]) && ishex(mu->txt[4]) && ishex(mu->txt[5]))
200 {
201 c = (dehex(mu->txt[2])<<12) | (dehex(mu->txt[3])<<8) | (dehex(mu->txt[4])<<4) | dehex(mu->txt[5]);
202 mu->txt += 6;
203
204 if (c >= 0xe000 && c <= 0xe0ff)
205 c &= 0xff; //private-use 0xE0XX maps to quake's chars
206 else if (c >= 0x20 && c <= 0x7f)
207 c &= 0x7f; //ascii is okay too.
208 else
209 c = '?'; //otherwise its some unicode char that we don't know how to handle.
210 return c;
211 }
212 break; //malformed
213 case '{': //full unicode codepoint, for chars up to 0x10ffff
214 mu->txt += 2;
215 c = 0; //no idea
216 while(*mu->txt)
217 {
218 if (*mu->txt == '}')
219 {
220 mu->txt++;
221 break;
222 }
223 if (!ishex(*mu->txt))
224 break;
225 c<<=4;
226 c |= dehex(*mu->txt++);
227 }
228
229 if (c >= 0xe000 && c <= 0xe0ff)
230 c &= 0xff; //private-use 0xE0XX maps to quake's chars
231 else if (c >= 0x20 && c <= 0x7f)
232 c &= 0x7f; //ascii is okay too.
233 //it would be nice to include a table to de-accent latin scripts, as well as translate cyrilic somehow, but not really necessary.
234 else
235 c = '?'; //otherwise its some unicode char that we don't know how to handle.
236 return c;
237 }
238 }
239
240 //regular char
241 mu->txt++;
242 return c|mu->mask;
243 }
244 return 0;
245 }
246
247 #define D(typestr,desc) typestr,desc
248
249 //#define fixme
250
251 //maths stuff
PF_Sin(void)252 static void PF_Sin(void)
253 {
254 G_FLOAT(OFS_RETURN) = sin(G_FLOAT(OFS_PARM0));
255 }
PF_asin(void)256 static void PF_asin(void)
257 {
258 G_FLOAT(OFS_RETURN) = asin(G_FLOAT(OFS_PARM0));
259 }
PF_Cos(void)260 static void PF_Cos(void)
261 {
262 G_FLOAT(OFS_RETURN) = cos(G_FLOAT(OFS_PARM0));
263 }
PF_acos(void)264 static void PF_acos(void)
265 {
266 G_FLOAT(OFS_RETURN) = acos(G_FLOAT(OFS_PARM0));
267 }
PF_tan(void)268 static void PF_tan(void)
269 {
270 G_FLOAT(OFS_RETURN) = tan(G_FLOAT(OFS_PARM0));
271 }
PF_atan(void)272 static void PF_atan(void)
273 {
274 G_FLOAT(OFS_RETURN) = atan(G_FLOAT(OFS_PARM0));
275 }
PF_atan2(void)276 static void PF_atan2(void)
277 {
278 G_FLOAT(OFS_RETURN) = atan2(G_FLOAT(OFS_PARM0), G_FLOAT(OFS_PARM1));
279 }
PF_Sqrt(void)280 static void PF_Sqrt(void)
281 {
282 G_FLOAT(OFS_RETURN) = sqrt(G_FLOAT(OFS_PARM0));
283 }
PF_pow(void)284 static void PF_pow(void)
285 {
286 G_FLOAT(OFS_RETURN) = pow(G_FLOAT(OFS_PARM0), G_FLOAT(OFS_PARM1));
287 }
PF_Logarithm(void)288 static void PF_Logarithm(void)
289 {
290 //log2(v) = ln(v)/ln(2)
291 double r;
292 r = log(G_FLOAT(OFS_PARM0));
293 if (qcvm->argc > 1)
294 r /= log(G_FLOAT(OFS_PARM1));
295 G_FLOAT(OFS_RETURN) = r;
296 }
PF_mod(void)297 static void PF_mod(void)
298 {
299 float a = G_FLOAT(OFS_PARM0);
300 float n = G_FLOAT(OFS_PARM1);
301
302 if (n == 0)
303 {
304 Con_DWarning("PF_mod: mod by zero\n");
305 G_FLOAT(OFS_RETURN) = 0;
306 }
307 else
308 {
309 //because QC is inherantly floaty, lets use floats.
310 G_FLOAT(OFS_RETURN) = a - (n * (int)(a/n));
311 }
312 }
PF_min(void)313 static void PF_min(void)
314 {
315 float r = G_FLOAT(OFS_PARM0);
316 int i;
317 for (i = 1; i < qcvm->argc; i++)
318 {
319 if (r > G_FLOAT(OFS_PARM0 + i*3))
320 r = G_FLOAT(OFS_PARM0 + i*3);
321 }
322 G_FLOAT(OFS_RETURN) = r;
323 }
PF_max(void)324 static void PF_max(void)
325 {
326 float r = G_FLOAT(OFS_PARM0);
327 int i;
328 for (i = 1; i < qcvm->argc; i++)
329 {
330 if (r < G_FLOAT(OFS_PARM0 + i*3))
331 r = G_FLOAT(OFS_PARM0 + i*3);
332 }
333 G_FLOAT(OFS_RETURN) = r;
334 }
PF_bound(void)335 static void PF_bound(void)
336 {
337 float minval = G_FLOAT(OFS_PARM0);
338 float curval = G_FLOAT(OFS_PARM1);
339 float maxval = G_FLOAT(OFS_PARM2);
340 if (curval > maxval)
341 curval = maxval;
342 if (curval < minval)
343 curval = minval;
344 G_FLOAT(OFS_RETURN) = curval;
345 }
PF_anglemod(void)346 static void PF_anglemod(void)
347 {
348 float v = G_FLOAT(OFS_PARM0);
349
350 while (v >= 360)
351 v = v - 360;
352 while (v < 0)
353 v = v + 360;
354
355 G_FLOAT(OFS_RETURN) = v;
356 }
PF_bitshift(void)357 static void PF_bitshift(void)
358 {
359 int bitmask = G_FLOAT(OFS_PARM0);
360 int shift = G_FLOAT(OFS_PARM1);
361 if (shift < 0)
362 bitmask >>= -shift;
363 else
364 bitmask <<= shift;
365 G_FLOAT(OFS_RETURN) = bitmask;
366 }
PF_crossproduct(void)367 static void PF_crossproduct(void)
368 {
369 CrossProduct(G_VECTOR(OFS_PARM0), G_VECTOR(OFS_PARM1), G_VECTOR(OFS_RETURN));
370 }
PF_vectorvectors(void)371 static void PF_vectorvectors(void)
372 {
373 VectorCopy(G_VECTOR(OFS_PARM0), pr_global_struct->v_forward);
374 VectorNormalize(pr_global_struct->v_forward);
375 if (!pr_global_struct->v_forward[0] && !pr_global_struct->v_forward[1])
376 {
377 if (pr_global_struct->v_forward[2])
378 pr_global_struct->v_right[1] = -1;
379 else
380 pr_global_struct->v_right[1] = 0;
381 pr_global_struct->v_right[0] = pr_global_struct->v_right[2] = 0;
382 }
383 else
384 {
385 pr_global_struct->v_right[0] = pr_global_struct->v_forward[1];
386 pr_global_struct->v_right[1] = -pr_global_struct->v_forward[0];
387 pr_global_struct->v_right[2] = 0;
388 VectorNormalize(pr_global_struct->v_right);
389 }
390 CrossProduct(pr_global_struct->v_right, pr_global_struct->v_forward, pr_global_struct->v_up);
391 }
PF_ext_vectoangles(void)392 static void PF_ext_vectoangles(void)
393 { //alternative version of the original builtin, that can deal with roll angles too, by accepting an optional second argument for 'up'.
394 float *value1, *up;
395
396 value1 = G_VECTOR(OFS_PARM0);
397 if (qcvm->argc >= 2)
398 up = G_VECTOR(OFS_PARM1);
399 else
400 up = NULL;
401
402 VectorAngles(value1, up, G_VECTOR(OFS_RETURN));
403 G_VECTOR(OFS_RETURN)[PITCH] *= -1; //this builtin is for use with models. models have an inverted pitch. consistency with makevectors would never do!
404 }
405
406 //string stuff
PF_strlen(void)407 static void PF_strlen(void)
408 { //FIXME: doesn't try to handle utf-8
409 const char *s = G_STRING(OFS_PARM0);
410 G_FLOAT(OFS_RETURN) = strlen(s);
411 }
PF_strcat(void)412 static void PF_strcat(void)
413 {
414 int i;
415 char *out = PR_GetTempString();
416 size_t s;
417
418 out[0] = 0;
419 s = 0;
420 for (i = 0; i < qcvm->argc; i++)
421 {
422 s = q_strlcat(out, G_STRING((OFS_PARM0+i*3)), STRINGTEMP_LENGTH);
423 if (s >= STRINGTEMP_LENGTH)
424 {
425 Con_Warning("PF_strcat: overflow (string truncated)\n");
426 break;
427 }
428 }
429
430 G_INT(OFS_RETURN) = PR_SetEngineString(out);
431 }
PF_substring(void)432 static void PF_substring(void)
433 {
434 int start, length, slen;
435 const char *s;
436 char *string;
437
438 s = G_STRING(OFS_PARM0);
439 start = G_FLOAT(OFS_PARM1);
440 length = G_FLOAT(OFS_PARM2);
441
442 slen = strlen(s); //utf-8 should use chars, not bytes.
443
444 if (start < 0)
445 start = slen+start;
446 if (length < 0)
447 length = slen-start+(length+1);
448 if (start < 0)
449 {
450 // length += start;
451 start = 0;
452 }
453
454 if (start >= slen || length<=0)
455 {
456 G_INT(OFS_RETURN) = PR_SetEngineString("");
457 return;
458 }
459
460 slen -= start;
461 if (length > slen)
462 length = slen;
463 //utf-8 should switch to bytes now.
464 s += start;
465
466 if (length >= STRINGTEMP_LENGTH)
467 {
468 length = STRINGTEMP_LENGTH-1;
469 Con_Warning("PF_substring: truncation\n");
470 }
471
472 string = PR_GetTempString();
473 memcpy(string, s, length);
474 string[length] = '\0';
475 G_INT(OFS_RETURN) = PR_SetEngineString(string);
476 }
477 /*our zoned strings implementation is somewhat specific to quakespasm, so good luck porting*/
PF_strzone(void)478 static void PF_strzone(void)
479 {
480 char *buf;
481 size_t len = 0;
482 const char *s[8];
483 size_t l[8];
484 int i;
485 size_t id;
486
487 for (i = 0; i < qcvm->argc; i++)
488 {
489 s[i] = G_STRING(OFS_PARM0+i*3);
490 l[i] = strlen(s[i]);
491 len += l[i];
492 }
493 len++; /*for the null*/
494
495 buf = Z_Malloc(len);
496 G_INT(OFS_RETURN) = PR_SetEngineString(buf);
497 id = -1-G_INT(OFS_RETURN);
498 if (id >= qcvm->knownzonesize)
499 {
500 qcvm->knownzonesize = (id+32)&~7;
501 qcvm->knownzone = Z_Realloc(qcvm->knownzone, (qcvm->knownzonesize+7)>>3);
502 }
503 qcvm->knownzone[id>>3] |= 1u<<(id&7);
504
505 for (i = 0; i < qcvm->argc; i++)
506 {
507 memcpy(buf, s[i], l[i]);
508 buf += l[i];
509 }
510 *buf = '\0';
511 }
PF_strunzone(void)512 static void PF_strunzone(void)
513 {
514 size_t id;
515 const char *foo = G_STRING(OFS_PARM0);
516
517 if (!G_INT(OFS_PARM0))
518 return; //don't bug out if they gave a null string
519 id = -1-G_INT(OFS_PARM0);
520 if (id < qcvm->knownzonesize && (qcvm->knownzone[id>>3] & (1u<<(id&7))))
521 {
522 qcvm->knownzone[id>>3] &= ~(1u<<(id&7));
523 PR_ClearEngineString(G_INT(OFS_PARM0));
524 Z_Free((void*)foo);
525 }
526 else
527 Con_Warning("PF_strunzone: string wasn't strzoned\n");
528 }
PR_UnzoneAll(void)529 static void PR_UnzoneAll(void)
530 { //called to clean up all zoned strings.
531 while (qcvm->knownzonesize --> 0)
532 {
533 size_t id = qcvm->knownzonesize;
534 if (qcvm->knownzone[id>>3] & (1u<<(id&7)))
535 {
536 string_t s = -1-(int)id;
537 char *ptr = (char*)PR_GetString(s);
538 PR_ClearEngineString(s);
539 Z_Free(ptr);
540 }
541 }
542 if (qcvm->knownzone)
543 Z_Free(qcvm->knownzone);
544 qcvm->knownzonesize = 0;
545 qcvm->knownzone = NULL;
546 }
qc_isascii(unsigned int u)547 static qboolean qc_isascii(unsigned int u)
548 {
549 if (u < 256) //should be just \n and 32-127, but we don't actually support any actual unicode and we don't really want to make things worse.
550 return true;
551 return false;
552 }
PF_str2chr(void)553 static void PF_str2chr(void)
554 {
555 const char *instr = G_STRING(OFS_PARM0);
556 int ofs = (qcvm->argc>1)?G_FLOAT(OFS_PARM1):0;
557
558 if (ofs < 0)
559 ofs = strlen(instr)+ofs;
560
561 if (ofs && (ofs < 0 || ofs > (int)strlen(instr)))
562 G_FLOAT(OFS_RETURN) = '\0';
563 else
564 G_FLOAT(OFS_RETURN) = (unsigned char)instr[ofs];
565 }
PF_chr2str(void)566 static void PF_chr2str(void)
567 {
568 char *ret = PR_GetTempString(), *out;
569 int i;
570 for (i = 0, out=ret; out-ret < STRINGTEMP_LENGTH-6 && i < qcvm->argc; i++)
571 {
572 unsigned int u = G_FLOAT(OFS_PARM0 + i*3);
573 if (u >= 0xe000 && u < 0xe100)
574 *out++ = (unsigned char)u; //quake chars.
575 else if (qc_isascii(u))
576 *out++ = u;
577 else
578 *out++ = '?'; //no unicode support
579 }
580 *out = 0;
581 G_INT(OFS_RETURN) = PR_SetEngineString(ret);
582 }
583 //part of PF_strconv
chrconv_number(int i,int base,int conv)584 static int chrconv_number(int i, int base, int conv)
585 {
586 i -= base;
587 switch (conv)
588 {
589 default:
590 case 5:
591 case 6:
592 case 0:
593 break;
594 case 1:
595 base = '0';
596 break;
597 case 2:
598 base = '0'+128;
599 break;
600 case 3:
601 base = '0'-30;
602 break;
603 case 4:
604 base = '0'+128-30;
605 break;
606 }
607 return i + base;
608 }
609 //part of PF_strconv
chrconv_punct(int i,int base,int conv)610 static int chrconv_punct(int i, int base, int conv)
611 {
612 i -= base;
613 switch (conv)
614 {
615 default:
616 case 0:
617 break;
618 case 1:
619 base = 0;
620 break;
621 case 2:
622 base = 128;
623 break;
624 }
625 return i + base;
626 }
627 //part of PF_strconv
chrchar_alpha(int i,int basec,int baset,int convc,int convt,int charnum)628 static int chrchar_alpha(int i, int basec, int baset, int convc, int convt, int charnum)
629 {
630 //convert case and colour seperatly...
631
632 i -= baset + basec;
633 switch (convt)
634 {
635 default:
636 case 0:
637 break;
638 case 1:
639 baset = 0;
640 break;
641 case 2:
642 baset = 128;
643 break;
644
645 case 5:
646 case 6:
647 baset = 128*((charnum&1) == (convt-5));
648 break;
649 }
650
651 switch (convc)
652 {
653 default:
654 case 0:
655 break;
656 case 1:
657 basec = 'a';
658 break;
659 case 2:
660 basec = 'A';
661 break;
662 }
663 return i + basec + baset;
664 }
665 //FTE_STRINGS
666 //bulk convert a string. change case or colouring.
PF_strconv(void)667 static void PF_strconv (void)
668 {
669 int ccase = G_FLOAT(OFS_PARM0); //0 same, 1 lower, 2 upper
670 int redalpha = G_FLOAT(OFS_PARM1); //0 same, 1 white, 2 red, 5 alternate, 6 alternate-alternate
671 int rednum = G_FLOAT(OFS_PARM2); //0 same, 1 white, 2 red, 3 redspecial, 4 whitespecial, 5 alternate, 6 alternate-alternate
672 const unsigned char *string = (const unsigned char*)PF_VarString(3);
673 int len = strlen((const char*)string);
674 int i;
675 unsigned char *resbuf = (unsigned char*)PR_GetTempString();
676 unsigned char *result = resbuf;
677
678 //UTF-8-FIXME: cope with utf+^U etc
679
680 if (len >= STRINGTEMP_LENGTH)
681 len = STRINGTEMP_LENGTH-1;
682
683 for (i = 0; i < len; i++, string++, result++) //should this be done backwards?
684 {
685 if (*string >= '0' && *string <= '9') //normal numbers...
686 *result = chrconv_number(*string, '0', rednum);
687 else if (*string >= '0'+128 && *string <= '9'+128)
688 *result = chrconv_number(*string, '0'+128, rednum);
689 else if (*string >= '0'+128-30 && *string <= '9'+128-30)
690 *result = chrconv_number(*string, '0'+128-30, rednum);
691 else if (*string >= '0'-30 && *string <= '9'-30)
692 *result = chrconv_number(*string, '0'-30, rednum);
693
694 else if (*string >= 'a' && *string <= 'z') //normal numbers...
695 *result = chrchar_alpha(*string, 'a', 0, ccase, redalpha, i);
696 else if (*string >= 'A' && *string <= 'Z') //normal numbers...
697 *result = chrchar_alpha(*string, 'A', 0, ccase, redalpha, i);
698 else if (*string >= 'a'+128 && *string <= 'z'+128) //normal numbers...
699 *result = chrchar_alpha(*string, 'a', 128, ccase, redalpha, i);
700 else if (*string >= 'A'+128 && *string <= 'Z'+128) //normal numbers...
701 *result = chrchar_alpha(*string, 'A', 128, ccase, redalpha, i);
702
703 else if ((*string & 127) < 16 || !redalpha) //special chars..
704 *result = *string;
705 else if (*string < 128)
706 *result = chrconv_punct(*string, 0, redalpha);
707 else
708 *result = chrconv_punct(*string, 128, redalpha);
709 }
710 *result = '\0';
711
712 G_INT(OFS_RETURN) = PR_SetEngineString((char*)resbuf);
713 }
PF_strpad(void)714 static void PF_strpad(void)
715 {
716 char *destbuf = PR_GetTempString();
717 char *dest = destbuf;
718 int pad = G_FLOAT(OFS_PARM0);
719 const char *src = PF_VarString(1);
720
721 //UTF-8-FIXME: pad is chars not bytes...
722
723 if (pad < 0)
724 { //pad left
725 pad = -pad - strlen(src);
726 if (pad>=STRINGTEMP_LENGTH)
727 pad = STRINGTEMP_LENGTH-1;
728 if (pad < 0)
729 pad = 0;
730
731 q_strlcpy(dest+pad, src, STRINGTEMP_LENGTH-pad);
732 while(pad)
733 {
734 dest[--pad] = ' ';
735 }
736 }
737 else
738 { //pad right
739 if (pad>=STRINGTEMP_LENGTH)
740 pad = STRINGTEMP_LENGTH-1;
741 pad -= strlen(src);
742 if (pad < 0)
743 pad = 0;
744
745 q_strlcpy(dest, src, STRINGTEMP_LENGTH);
746 dest+=strlen(dest);
747
748 while(pad-->0)
749 *dest++ = ' ';
750 *dest = '\0';
751 }
752
753 G_INT(OFS_RETURN) = PR_SetEngineString(destbuf);
754 }
PF_infoadd(void)755 static void PF_infoadd(void)
756 {
757 const char *info = G_STRING(OFS_PARM0);
758 const char *key = G_STRING(OFS_PARM1);
759 const char *value = PF_VarString(2);
760 char *destbuf = PR_GetTempString(), *o = destbuf, *e = destbuf + STRINGTEMP_LENGTH - 1;
761
762 size_t keylen = strlen(key);
763 size_t valuelen = strlen(value);
764 if (!*key)
765 { //error
766 G_INT(OFS_RETURN) = G_INT(OFS_PARM0);
767 return;
768 }
769
770 //copy the string to the output, stripping the named key
771 while(*info)
772 {
773 const char *l = info;
774 if (*info++ != '\\')
775 break; //error / end-of-string
776
777 if (!strncmp(info, key, keylen) && info[keylen] == '\\')
778 {
779 //skip the key name
780 info += keylen+1;
781 //this is the old value for the key. skip over it
782 while (*info && *info != '\\')
783 info++;
784 }
785 else
786 {
787 //skip the key
788 while (*info && *info != '\\')
789 info++;
790
791 //validate that its a value now
792 if (*info++ != '\\')
793 break; //error
794 //skip the value
795 while (*info && *info != '\\')
796 info++;
797
798 //copy them over
799 if (o + (info-l) >= e)
800 break; //exceeds maximum length
801 while (l < info)
802 *o++ = *l++;
803 }
804 }
805
806 if (*info)
807 Con_Warning("PF_infoadd: invalid source info\n");
808 else if (!*value)
809 ; //nothing needed
810 else if (!*key || strchr(key, '\\') || strchr(value, '\\'))
811 Con_Warning("PF_infoadd: invalid key/value\n");
812 else if (o + 2 + keylen + valuelen >= e)
813 Con_Warning("PF_infoadd: length exceeds max\n");
814 else
815 {
816 *o++ = '\\';
817 memcpy(o, key, keylen);
818 o += keylen;
819 *o++ = '\\';
820 memcpy(o, value, valuelen);
821 o += valuelen;
822 }
823
824 *o = 0;
825 G_INT(OFS_RETURN) = PR_SetEngineString(destbuf);
826 }
PF_infoget(void)827 static void PF_infoget(void)
828 {
829 const char *info = G_STRING(OFS_PARM0);
830 const char *key = G_STRING(OFS_PARM1);
831 size_t keylen = strlen(key);
832 while(*info)
833 {
834 if (*info++ != '\\')
835 break; //error / end-of-string
836
837 if (!strncmp(info, key, keylen) && info[keylen] == '\\')
838 {
839 char *destbuf = PR_GetTempString(), *o = destbuf, *e = destbuf + STRINGTEMP_LENGTH - 1;
840
841 //skip the key name
842 info += keylen+1;
843 //this is the old value for the key. copy it to the result
844 while (*info && *info != '\\' && o < e)
845 *o++ = *info++;
846 *o++ = 0;
847
848 //success!
849 G_INT(OFS_RETURN) = PR_SetEngineString(destbuf);
850 return;
851 }
852 else
853 {
854 //skip the key
855 while (*info && *info != '\\')
856 info++;
857
858 //validate that its a value now
859 if (*info++ != '\\')
860 break; //error
861 //skip the value
862 while (*info && *info != '\\')
863 info++;
864 }
865 }
866 G_INT(OFS_RETURN) = 0;
867
868 }
PF_strncmp(void)869 static void PF_strncmp(void)
870 {
871 const char *a = G_STRING(OFS_PARM0);
872 const char *b = G_STRING(OFS_PARM1);
873
874 if (qcvm->argc > 2)
875 {
876 int len = G_FLOAT(OFS_PARM2);
877 int aofs = qcvm->argc>3?G_FLOAT(OFS_PARM3):0;
878 int bofs = qcvm->argc>4?G_FLOAT(OFS_PARM4):0;
879 if (aofs < 0 || (aofs && aofs > (int)strlen(a)))
880 aofs = strlen(a);
881 if (bofs < 0 || (bofs && bofs > (int)strlen(b)))
882 bofs = strlen(b);
883 G_FLOAT(OFS_RETURN) = Q_strncmp(a + aofs, b, len);
884 }
885 else
886 G_FLOAT(OFS_RETURN) = Q_strcmp(a, b);
887 }
PF_strncasecmp(void)888 static void PF_strncasecmp(void)
889 {
890 const char *a = G_STRING(OFS_PARM0);
891 const char *b = G_STRING(OFS_PARM1);
892
893 if (qcvm->argc > 2)
894 {
895 int len = G_FLOAT(OFS_PARM2);
896 int aofs = qcvm->argc>3?G_FLOAT(OFS_PARM3):0;
897 int bofs = qcvm->argc>4?G_FLOAT(OFS_PARM4):0;
898 if (aofs < 0 || (aofs && aofs > (int)strlen(a)))
899 aofs = strlen(a);
900 if (bofs < 0 || (bofs && bofs > (int)strlen(b)))
901 bofs = strlen(b);
902 G_FLOAT(OFS_RETURN) = q_strncasecmp(a + aofs, b, len);
903 }
904 else
905 G_FLOAT(OFS_RETURN) = q_strcasecmp(a, b);
906 }
PF_strstrofs(void)907 static void PF_strstrofs(void)
908 {
909 const char *instr = G_STRING(OFS_PARM0);
910 const char *match = G_STRING(OFS_PARM1);
911 int firstofs = (qcvm->argc>2)?G_FLOAT(OFS_PARM2):0;
912
913 if (firstofs && (firstofs < 0 || firstofs > (int)strlen(instr)))
914 {
915 G_FLOAT(OFS_RETURN) = -1;
916 return;
917 }
918
919 match = strstr(instr+firstofs, match);
920 if (!match)
921 G_FLOAT(OFS_RETURN) = -1;
922 else
923 G_FLOAT(OFS_RETURN) = match - instr;
924 }
PF_strtrim(void)925 static void PF_strtrim(void)
926 {
927 const char *str = G_STRING(OFS_PARM0);
928 const char *end;
929 char *news;
930 size_t len;
931
932 //figure out the new start
933 while (*str == ' ' || *str == '\t' || *str == '\n' || *str == '\r')
934 str++;
935
936 //figure out the new end.
937 end = str + strlen(str);
938 while(end > str && (end[-1] == ' ' || end[-1] == '\t' || end[-1] == '\n' || end[-1] == '\r'))
939 end--;
940
941 //copy that substring into a tempstring.
942 len = end - str;
943 if (len >= STRINGTEMP_LENGTH)
944 len = STRINGTEMP_LENGTH-1;
945
946 news = PR_GetTempString();
947 memcpy(news, str, len);
948 news[len] = 0;
949
950 G_INT(OFS_RETURN) = PR_SetEngineString(news);
951 }
PF_strreplace(void)952 static void PF_strreplace(void)
953 {
954 char *resultbuf = PR_GetTempString();
955 char *result = resultbuf;
956 const char *search = G_STRING(OFS_PARM0);
957 const char *replace = G_STRING(OFS_PARM1);
958 const char *subject = G_STRING(OFS_PARM2);
959 int searchlen = strlen(search);
960 int replacelen = strlen(replace);
961
962 if (searchlen)
963 {
964 while (*subject && result < resultbuf + STRINGTEMP_LENGTH - replacelen - 2)
965 {
966 if (!strncmp(subject, search, searchlen))
967 {
968 subject += searchlen;
969 memcpy(result, replace, replacelen);
970 result += replacelen;
971 }
972 else
973 *result++ = *subject++;
974 }
975 *result = 0;
976 G_INT(OFS_RETURN) = PR_SetEngineString(resultbuf);
977 }
978 else
979 G_INT(OFS_RETURN) = PR_SetEngineString(subject);
980 }
PF_strireplace(void)981 static void PF_strireplace(void)
982 {
983 char *resultbuf = PR_GetTempString();
984 char *result = resultbuf;
985 const char *search = G_STRING(OFS_PARM0);
986 const char *replace = G_STRING(OFS_PARM1);
987 const char *subject = G_STRING(OFS_PARM2);
988 int searchlen = strlen(search);
989 int replacelen = strlen(replace);
990
991 if (searchlen)
992 {
993 while (*subject && result < resultbuf + sizeof(resultbuf) - replacelen - 2)
994 {
995 //UTF-8-FIXME: case insensitivity is awkward...
996 if (!q_strncasecmp(subject, search, searchlen))
997 {
998 subject += searchlen;
999 memcpy(result, replace, replacelen);
1000 result += replacelen;
1001 }
1002 else
1003 *result++ = *subject++;
1004 }
1005 *result = 0;
1006 G_INT(OFS_RETURN) = PR_SetEngineString(resultbuf);
1007 }
1008 else
1009 G_INT(OFS_RETURN) = PR_SetEngineString(subject);
1010 }
1011
1012
PF_sprintf_internal(const char * s,int firstarg,char * outbuf,int outbuflen)1013 static void PF_sprintf_internal (const char *s, int firstarg, char *outbuf, int outbuflen)
1014 {
1015 const char *s0;
1016 char *o = outbuf, *end = outbuf + outbuflen, *err;
1017 int width, precision, thisarg, flags;
1018 char formatbuf[16];
1019 char *f;
1020 int argpos = firstarg;
1021 int isfloat;
1022 static int dummyivec[3] = {0, 0, 0};
1023 static float dummyvec[3] = {0, 0, 0};
1024
1025 #define PRINTF_ALTERNATE 1
1026 #define PRINTF_ZEROPAD 2
1027 #define PRINTF_LEFT 4
1028 #define PRINTF_SPACEPOSITIVE 8
1029 #define PRINTF_SIGNPOSITIVE 16
1030
1031 formatbuf[0] = '%';
1032
1033 #define GETARG_FLOAT(a) (((a)>=firstarg && (a)<qcvm->argc) ? (G_FLOAT(OFS_PARM0 + 3 * (a))) : 0)
1034 #define GETARG_VECTOR(a) (((a)>=firstarg && (a)<qcvm->argc) ? (G_VECTOR(OFS_PARM0 + 3 * (a))) : dummyvec)
1035 #define GETARG_INT(a) (((a)>=firstarg && (a)<qcvm->argc) ? (G_INT(OFS_PARM0 + 3 * (a))) : 0)
1036 #define GETARG_INTVECTOR(a) (((a)>=firstarg && (a)<qcvm->argc) ? ((int*) G_VECTOR(OFS_PARM0 + 3 * (a))) : dummyivec)
1037 #define GETARG_STRING(a) (((a)>=firstarg && (a)<qcvm->argc) ? (G_STRING(OFS_PARM0 + 3 * (a))) : "")
1038
1039 for(;;)
1040 {
1041 s0 = s;
1042 switch(*s)
1043 {
1044 case 0:
1045 goto finished;
1046 case '%':
1047 ++s;
1048
1049 if(*s == '%')
1050 goto verbatim;
1051
1052 // complete directive format:
1053 // %3$*1$.*2$ld
1054
1055 width = -1;
1056 precision = -1;
1057 thisarg = -1;
1058 flags = 0;
1059 isfloat = -1;
1060
1061 // is number following?
1062 if(*s >= '0' && *s <= '9')
1063 {
1064 width = strtol(s, &err, 10);
1065 if(!err)
1066 {
1067 Con_Warning("PF_sprintf: bad format string: %s\n", s0);
1068 goto finished;
1069 }
1070 if(*err == '$')
1071 {
1072 thisarg = width + (firstarg-1);
1073 width = -1;
1074 s = err + 1;
1075 }
1076 else
1077 {
1078 if(*s == '0')
1079 {
1080 flags |= PRINTF_ZEROPAD;
1081 if(width == 0)
1082 width = -1; // it was just a flag
1083 }
1084 s = err;
1085 }
1086 }
1087
1088 if(width < 0)
1089 {
1090 for(;;)
1091 {
1092 switch(*s)
1093 {
1094 case '#': flags |= PRINTF_ALTERNATE; break;
1095 case '0': flags |= PRINTF_ZEROPAD; break;
1096 case '-': flags |= PRINTF_LEFT; break;
1097 case ' ': flags |= PRINTF_SPACEPOSITIVE; break;
1098 case '+': flags |= PRINTF_SIGNPOSITIVE; break;
1099 default:
1100 goto noflags;
1101 }
1102 ++s;
1103 }
1104 noflags:
1105 if(*s == '*')
1106 {
1107 ++s;
1108 if(*s >= '0' && *s <= '9')
1109 {
1110 width = strtol(s, &err, 10);
1111 if(!err || *err != '$')
1112 {
1113 Con_Warning("PF_sprintf: invalid format string: %s\n", s0);
1114 goto finished;
1115 }
1116 s = err + 1;
1117 }
1118 else
1119 width = argpos++;
1120 width = GETARG_FLOAT(width);
1121 if(width < 0)
1122 {
1123 flags |= PRINTF_LEFT;
1124 width = -width;
1125 }
1126 }
1127 else if(*s >= '0' && *s <= '9')
1128 {
1129 width = strtol(s, &err, 10);
1130 if(!err)
1131 {
1132 Con_Warning("PF_sprintf: invalid format string: %s\n", s0);
1133 goto finished;
1134 }
1135 s = err;
1136 if(width < 0)
1137 {
1138 flags |= PRINTF_LEFT;
1139 width = -width;
1140 }
1141 }
1142 // otherwise width stays -1
1143 }
1144
1145 if(*s == '.')
1146 {
1147 ++s;
1148 if(*s == '*')
1149 {
1150 ++s;
1151 if(*s >= '0' && *s <= '9')
1152 {
1153 precision = strtol(s, &err, 10);
1154 if(!err || *err != '$')
1155 {
1156 Con_Warning("PF_sprintf: invalid format string: %s\n", s0);
1157 goto finished;
1158 }
1159 s = err + 1;
1160 }
1161 else
1162 precision = argpos++;
1163 precision = GETARG_FLOAT(precision);
1164 }
1165 else if(*s >= '0' && *s <= '9')
1166 {
1167 precision = strtol(s, &err, 10);
1168 if(!err)
1169 {
1170 Con_Warning("PF_sprintf: invalid format string: %s\n", s0);
1171 goto finished;
1172 }
1173 s = err;
1174 }
1175 else
1176 {
1177 Con_Warning("PF_sprintf: invalid format string: %s\n", s0);
1178 goto finished;
1179 }
1180 }
1181
1182 for(;;)
1183 {
1184 switch(*s)
1185 {
1186 case 'h': isfloat = 1; break;
1187 case 'l': isfloat = 0; break;
1188 case 'L': isfloat = 0; break;
1189 case 'j': break;
1190 case 'z': break;
1191 case 't': break;
1192 default:
1193 goto nolength;
1194 }
1195 ++s;
1196 }
1197 nolength:
1198
1199 // now s points to the final directive char and is no longer changed
1200 if (*s == 'p' || *s == 'P')
1201 {
1202 //%p is slightly different from %x.
1203 //always 8-bytes wide with 0 padding, always ints.
1204 flags |= PRINTF_ZEROPAD;
1205 if (width < 0) width = 8;
1206 if (isfloat < 0) isfloat = 0;
1207 }
1208 else if (*s == 'i')
1209 {
1210 //%i defaults to ints, not floats.
1211 if(isfloat < 0) isfloat = 0;
1212 }
1213
1214 //assume floats, not ints.
1215 if(isfloat < 0)
1216 isfloat = 1;
1217
1218 if(thisarg < 0)
1219 thisarg = argpos++;
1220
1221 if(o < end - 1)
1222 {
1223 f = &formatbuf[1];
1224 if(*s != 's' && *s != 'c')
1225 if(flags & PRINTF_ALTERNATE) *f++ = '#';
1226 if(flags & PRINTF_ZEROPAD) *f++ = '0';
1227 if(flags & PRINTF_LEFT) *f++ = '-';
1228 if(flags & PRINTF_SPACEPOSITIVE) *f++ = ' ';
1229 if(flags & PRINTF_SIGNPOSITIVE) *f++ = '+';
1230 *f++ = '*';
1231 if(precision >= 0)
1232 {
1233 *f++ = '.';
1234 *f++ = '*';
1235 }
1236 if (*s == 'p')
1237 *f++ = 'x';
1238 else if (*s == 'P')
1239 *f++ = 'X';
1240 else if (*s == 'S')
1241 *f++ = 's';
1242 else
1243 *f++ = *s;
1244 *f++ = 0;
1245
1246 if(width < 0) // not set
1247 width = 0;
1248
1249 switch(*s)
1250 {
1251 case 'd': case 'i':
1252 if(precision < 0) // not set
1253 q_snprintf(o, end - o, formatbuf, width, (isfloat ? (int) GETARG_FLOAT(thisarg) : (int) GETARG_INT(thisarg)));
1254 else
1255 q_snprintf(o, end - o, formatbuf, width, precision, (isfloat ? (int) GETARG_FLOAT(thisarg) : (int) GETARG_INT(thisarg)));
1256 o += strlen(o);
1257 break;
1258 case 'o': case 'u': case 'x': case 'X': case 'p': case 'P':
1259 if(precision < 0) // not set
1260 q_snprintf(o, end - o, formatbuf, width, (isfloat ? (unsigned int) GETARG_FLOAT(thisarg) : (unsigned int) GETARG_INT(thisarg)));
1261 else
1262 q_snprintf(o, end - o, formatbuf, width, precision, (isfloat ? (unsigned int) GETARG_FLOAT(thisarg) : (unsigned int) GETARG_INT(thisarg)));
1263 o += strlen(o);
1264 break;
1265 case 'e': case 'E': case 'f': case 'F': case 'g': case 'G':
1266 if(precision < 0) // not set
1267 q_snprintf(o, end - o, formatbuf, width, (isfloat ? (double) GETARG_FLOAT(thisarg) : (double) GETARG_INT(thisarg)));
1268 else
1269 q_snprintf(o, end - o, formatbuf, width, precision, (isfloat ? (double) GETARG_FLOAT(thisarg) : (double) GETARG_INT(thisarg)));
1270 o += strlen(o);
1271 break;
1272 case 'v': case 'V':
1273 f[-2] += 'g' - 'v';
1274 if(precision < 0) // not set
1275 q_snprintf(o, end - o, va("%s %s %s", /* NESTED SPRINTF IS NESTED */ formatbuf, formatbuf, formatbuf),
1276 width, (isfloat ? (double) GETARG_VECTOR(thisarg)[0] : (double) GETARG_INTVECTOR(thisarg)[0]),
1277 width, (isfloat ? (double) GETARG_VECTOR(thisarg)[1] : (double) GETARG_INTVECTOR(thisarg)[1]),
1278 width, (isfloat ? (double) GETARG_VECTOR(thisarg)[2] : (double) GETARG_INTVECTOR(thisarg)[2])
1279 );
1280 else
1281 q_snprintf(o, end - o, va("%s %s %s", /* NESTED SPRINTF IS NESTED */ formatbuf, formatbuf, formatbuf),
1282 width, precision, (isfloat ? (double) GETARG_VECTOR(thisarg)[0] : (double) GETARG_INTVECTOR(thisarg)[0]),
1283 width, precision, (isfloat ? (double) GETARG_VECTOR(thisarg)[1] : (double) GETARG_INTVECTOR(thisarg)[1]),
1284 width, precision, (isfloat ? (double) GETARG_VECTOR(thisarg)[2] : (double) GETARG_INTVECTOR(thisarg)[2])
1285 );
1286 o += strlen(o);
1287 break;
1288 case 'c':
1289 //UTF-8-FIXME: figure it out yourself
1290 // if(flags & PRINTF_ALTERNATE)
1291 {
1292 if(precision < 0) // not set
1293 q_snprintf(o, end - o, formatbuf, width, (isfloat ? (unsigned int) GETARG_FLOAT(thisarg) : (unsigned int) GETARG_INT(thisarg)));
1294 else
1295 q_snprintf(o, end - o, formatbuf, width, precision, (isfloat ? (unsigned int) GETARG_FLOAT(thisarg) : (unsigned int) GETARG_INT(thisarg)));
1296 o += strlen(o);
1297 }
1298 /* else
1299 {
1300 unsigned int c = (isfloat ? (unsigned int) GETARG_FLOAT(thisarg) : (unsigned int) GETARG_INT(thisarg));
1301 char charbuf16[16];
1302 const char *buf = u8_encodech(c, NULL, charbuf16);
1303 if(!buf)
1304 buf = "";
1305 if(precision < 0) // not set
1306 precision = end - o - 1;
1307 o += u8_strpad(o, end - o, buf, (flags & PRINTF_LEFT) != 0, width, precision);
1308 }
1309 */ break;
1310 case 'S':
1311 { //tokenizable string
1312 const char *quotedarg = GETARG_STRING(thisarg);
1313
1314 //try and escape it... hopefully it won't get truncated by precision limits...
1315 char quotedbuf[65536];
1316 size_t l;
1317 l = strlen(quotedarg);
1318 if (strchr(quotedarg, '\"') || strchr(quotedarg, '\n') || strchr(quotedarg, '\r') || l+3 >= sizeof(quotedbuf))
1319 { //our escapes suck...
1320 Con_Warning("PF_sprintf: unable to safely escape arg: %s\n", s0);
1321 quotedarg="";
1322 }
1323 quotedbuf[0] = '\"';
1324 memcpy(quotedbuf+1, quotedarg, l);
1325 quotedbuf[1+l] = '\"';
1326 quotedbuf[1+l+1] = 0;
1327 quotedarg = quotedbuf;
1328
1329 //UTF-8-FIXME: figure it out yourself
1330 // if(flags & PRINTF_ALTERNATE)
1331 {
1332 if(precision < 0) // not set
1333 q_snprintf(o, end - o, formatbuf, width, quotedarg);
1334 else
1335 q_snprintf(o, end - o, formatbuf, width, precision, quotedarg);
1336 o += strlen(o);
1337 }
1338 /* else
1339 {
1340 if(precision < 0) // not set
1341 precision = end - o - 1;
1342 o += u8_strpad(o, end - o, quotedarg, (flags & PRINTF_LEFT) != 0, width, precision);
1343 }
1344 */ }
1345 break;
1346 case 's':
1347 //UTF-8-FIXME: figure it out yourself
1348 // if(flags & PRINTF_ALTERNATE)
1349 {
1350 if(precision < 0) // not set
1351 q_snprintf(o, end - o, formatbuf, width, GETARG_STRING(thisarg));
1352 else
1353 q_snprintf(o, end - o, formatbuf, width, precision, GETARG_STRING(thisarg));
1354 o += strlen(o);
1355 }
1356 /* else
1357 {
1358 if(precision < 0) // not set
1359 precision = end - o - 1;
1360 o += u8_strpad(o, end - o, GETARG_STRING(thisarg), (flags & PRINTF_LEFT) != 0, width, precision);
1361 }
1362 */ break;
1363 default:
1364 Con_Warning("PF_sprintf: invalid format string: %s\n", s0);
1365 goto finished;
1366 }
1367 }
1368 ++s;
1369 break;
1370 default:
1371 verbatim:
1372 if(o < end - 1)
1373 *o++ = *s;
1374 s++;
1375 break;
1376 }
1377 }
1378 finished:
1379 *o = 0;
1380 }
1381
PF_sprintf(void)1382 static void PF_sprintf(void)
1383 {
1384 char *outbuf = PR_GetTempString();
1385 PF_sprintf_internal(G_STRING(OFS_PARM0), 1, outbuf, STRINGTEMP_LENGTH);
1386 G_INT(OFS_RETURN) = PR_SetEngineString(outbuf);
1387 }
1388
1389 //string tokenizing (gah)
1390 #define MAXQCTOKENS 64
1391 static struct {
1392 char *token;
1393 unsigned int start;
1394 unsigned int end;
1395 } qctoken[MAXQCTOKENS];
1396 static unsigned int qctoken_count;
1397
tokenize_flush(void)1398 static void tokenize_flush(void)
1399 {
1400 while(qctoken_count > 0)
1401 {
1402 qctoken_count--;
1403 free(qctoken[qctoken_count].token);
1404 }
1405 qctoken_count = 0;
1406 }
1407
PF_ArgC(void)1408 static void PF_ArgC(void)
1409 {
1410 G_FLOAT(OFS_RETURN) = qctoken_count;
1411 }
1412
tokenizeqc(const char * str,qboolean dpfuckage)1413 static int tokenizeqc(const char *str, qboolean dpfuckage)
1414 {
1415 //FIXME: if dpfuckage, then we should handle punctuation specially, as well as /*.
1416 const char *start = str;
1417 while(qctoken_count > 0)
1418 {
1419 qctoken_count--;
1420 free(qctoken[qctoken_count].token);
1421 }
1422 qctoken_count = 0;
1423 while (qctoken_count < MAXQCTOKENS)
1424 {
1425 /*skip whitespace here so the token's start is accurate*/
1426 while (*str && *(const unsigned char*)str <= ' ')
1427 str++;
1428
1429 if (!*str)
1430 break;
1431
1432 qctoken[qctoken_count].start = str - start;
1433 // if (dpfuckage)
1434 // str = COM_ParseDPFuckage(str);
1435 // else
1436 str = COM_Parse(str);
1437 if (!str)
1438 break;
1439
1440 qctoken[qctoken_count].token = strdup(com_token);
1441
1442 qctoken[qctoken_count].end = str - start;
1443 qctoken_count++;
1444 }
1445 return qctoken_count;
1446 }
1447
1448 /*KRIMZON_SV_PARSECLIENTCOMMAND added these two - note that for compatibility with DP, this tokenize builtin is veeery vauge and doesn't match the console*/
PF_Tokenize(void)1449 static void PF_Tokenize(void)
1450 {
1451 G_FLOAT(OFS_RETURN) = tokenizeqc(G_STRING(OFS_PARM0), true);
1452 }
1453
PF_tokenize_console(void)1454 static void PF_tokenize_console(void)
1455 {
1456 G_FLOAT(OFS_RETURN) = tokenizeqc(G_STRING(OFS_PARM0), false);
1457 }
1458
PF_tokenizebyseparator(void)1459 static void PF_tokenizebyseparator(void)
1460 {
1461 const char *str = G_STRING(OFS_PARM0);
1462 const char *sep[7];
1463 int seplen[7];
1464 int seps = 0, s;
1465 const char *start = str;
1466 int tlen;
1467 qboolean found = true;
1468
1469 while (seps < qcvm->argc - 1 && seps < 7)
1470 {
1471 sep[seps] = G_STRING(OFS_PARM1 + seps*3);
1472 seplen[seps] = strlen(sep[seps]);
1473 seps++;
1474 }
1475
1476 tokenize_flush();
1477
1478 qctoken[qctoken_count].start = 0;
1479 if (*str)
1480 for(;;)
1481 {
1482 found = false;
1483 /*see if its a separator*/
1484 if (!*str)
1485 {
1486 qctoken[qctoken_count].end = str - start;
1487 found = true;
1488 }
1489 else
1490 {
1491 for (s = 0; s < seps; s++)
1492 {
1493 if (!strncmp(str, sep[s], seplen[s]))
1494 {
1495 qctoken[qctoken_count].end = str - start;
1496 str += seplen[s];
1497 found = true;
1498 break;
1499 }
1500 }
1501 }
1502 /*it was, split it out*/
1503 if (found)
1504 {
1505 tlen = qctoken[qctoken_count].end - qctoken[qctoken_count].start;
1506 qctoken[qctoken_count].token = malloc(tlen + 1);
1507 memcpy(qctoken[qctoken_count].token, start + qctoken[qctoken_count].start, tlen);
1508 qctoken[qctoken_count].token[tlen] = 0;
1509
1510 qctoken_count++;
1511
1512 if (*str && qctoken_count < MAXQCTOKENS)
1513 qctoken[qctoken_count].start = str - start;
1514 else
1515 break;
1516 }
1517 str++;
1518 }
1519 G_FLOAT(OFS_RETURN) = qctoken_count;
1520 }
1521
PF_argv_start_index(void)1522 static void PF_argv_start_index(void)
1523 {
1524 int idx = G_FLOAT(OFS_PARM0);
1525
1526 /*negative indexes are relative to the end*/
1527 if (idx < 0)
1528 idx += qctoken_count;
1529
1530 if ((unsigned int)idx >= qctoken_count)
1531 G_FLOAT(OFS_RETURN) = -1;
1532 else
1533 G_FLOAT(OFS_RETURN) = qctoken[idx].start;
1534 }
1535
PF_argv_end_index(void)1536 static void PF_argv_end_index(void)
1537 {
1538 int idx = G_FLOAT(OFS_PARM0);
1539
1540 /*negative indexes are relative to the end*/
1541 if (idx < 0)
1542 idx += qctoken_count;
1543
1544 if ((unsigned int)idx >= qctoken_count)
1545 G_FLOAT(OFS_RETURN) = -1;
1546 else
1547 G_FLOAT(OFS_RETURN) = qctoken[idx].end;
1548 }
1549
PF_ArgV(void)1550 static void PF_ArgV(void)
1551 {
1552 int idx = G_FLOAT(OFS_PARM0);
1553
1554 /*negative indexes are relative to the end*/
1555 if (idx < 0)
1556 idx += qctoken_count;
1557
1558 if ((unsigned int)idx >= qctoken_count)
1559 G_INT(OFS_RETURN) = 0;
1560 else
1561 {
1562 char *ret = PR_GetTempString();
1563 q_strlcpy(ret, qctoken[idx].token, STRINGTEMP_LENGTH);
1564 G_INT(OFS_RETURN) = PR_SetEngineString(ret);
1565 }
1566 }
1567
1568 //conversions (mostly string)
PF_strtoupper(void)1569 static void PF_strtoupper(void)
1570 {
1571 const char *in = G_STRING(OFS_PARM0);
1572 char *out, *result = PR_GetTempString();
1573 for (out = result; *in && out < result+STRINGTEMP_LENGTH-1;)
1574 *out++ = q_toupper(*in++);
1575 *out = 0;
1576 G_INT(OFS_RETURN) = PR_SetEngineString(result);
1577 }
PF_strtolower(void)1578 static void PF_strtolower(void)
1579 {
1580 const char *in = G_STRING(OFS_PARM0);
1581 char *out, *result = PR_GetTempString();
1582 for (out = result; *in && out < result+STRINGTEMP_LENGTH-1;)
1583 *out++ = q_tolower(*in++);
1584 *out = 0;
1585 G_INT(OFS_RETURN) = PR_SetEngineString(result);
1586 }
1587 #include <time.h>
PF_strftime(void)1588 static void PF_strftime(void)
1589 {
1590 const char *in = G_STRING(OFS_PARM1);
1591 char *result = PR_GetTempString();
1592
1593 time_t curtime;
1594 struct tm *tm;
1595
1596 curtime = time(NULL);
1597
1598 if (G_FLOAT(OFS_PARM0))
1599 tm = localtime(&curtime);
1600 else
1601 tm = gmtime(&curtime);
1602
1603 #ifdef _WIN32
1604 //msvc sucks. this is a crappy workaround.
1605 if (!strcmp(in, "%R"))
1606 in = "%H:%M";
1607 else if (!strcmp(in, "%F"))
1608 in = "%Y-%m-%d";
1609 #endif
1610
1611 strftime(result, STRINGTEMP_LENGTH, in, tm);
1612
1613 G_INT(OFS_RETURN) = PR_SetEngineString(result);
1614 }
PF_stof(void)1615 static void PF_stof(void)
1616 {
1617 G_FLOAT(OFS_RETURN) = atof(G_STRING(OFS_PARM0));
1618 }
PF_stov(void)1619 static void PF_stov(void)
1620 {
1621 const char *s = G_STRING(OFS_PARM0);
1622 s = COM_Parse(s);
1623 G_VECTOR(OFS_RETURN)[0] = atof(com_token);
1624 s = COM_Parse(s);
1625 G_VECTOR(OFS_RETURN)[1] = atof(com_token);
1626 s = COM_Parse(s);
1627 G_VECTOR(OFS_RETURN)[2] = atof(com_token);
1628 }
PF_stoi(void)1629 static void PF_stoi(void)
1630 {
1631 G_INT(OFS_RETURN) = atoi(G_STRING(OFS_PARM0));
1632 }
PF_itos(void)1633 static void PF_itos(void)
1634 {
1635 char *result = PR_GetTempString();
1636 q_snprintf(result, STRINGTEMP_LENGTH, "%i", G_INT(OFS_PARM0));
1637 G_INT(OFS_RETURN) = PR_SetEngineString(result);
1638 }
PF_etos(void)1639 static void PF_etos(void)
1640 { //yes, this is lame
1641 char *result = PR_GetTempString();
1642 q_snprintf(result, STRINGTEMP_LENGTH, "entity %i", G_EDICTNUM(OFS_PARM0));
1643 G_INT(OFS_RETURN) = PR_SetEngineString(result);
1644 }
PF_stoh(void)1645 static void PF_stoh(void)
1646 {
1647 G_INT(OFS_RETURN) = strtoul(G_STRING(OFS_PARM0), NULL, 16);
1648 }
PF_htos(void)1649 static void PF_htos(void)
1650 {
1651 char *result = PR_GetTempString();
1652 q_snprintf(result, STRINGTEMP_LENGTH, "%x", G_INT(OFS_PARM0));
1653 G_INT(OFS_RETURN) = PR_SetEngineString(result);
1654 }
PF_ftoi(void)1655 static void PF_ftoi(void)
1656 {
1657 G_INT(OFS_RETURN) = G_FLOAT(OFS_PARM0);
1658 }
PF_itof(void)1659 static void PF_itof(void)
1660 {
1661 G_FLOAT(OFS_RETURN) = G_INT(OFS_PARM0);
1662 }
1663
1664 //collision stuff
PF_tracebox(void)1665 static void PF_tracebox(void)
1666 { //alternative version of traceline that just passes on two extra args. trivial really.
1667 float *v1, *mins, *maxs, *v2;
1668 trace_t trace;
1669 int nomonsters;
1670 edict_t *ent;
1671
1672 v1 = G_VECTOR(OFS_PARM0);
1673 mins = G_VECTOR(OFS_PARM1);
1674 maxs = G_VECTOR(OFS_PARM2);
1675 v2 = G_VECTOR(OFS_PARM3);
1676 nomonsters = G_FLOAT(OFS_PARM4);
1677 ent = G_EDICT(OFS_PARM5);
1678
1679 /* FIXME FIXME FIXME: Why do we hit this with certain progs.dat ?? */
1680 if (developer.value) {
1681 if (IS_NAN(v1[0]) || IS_NAN(v1[1]) || IS_NAN(v1[2]) ||
1682 IS_NAN(v2[0]) || IS_NAN(v2[1]) || IS_NAN(v2[2])) {
1683 Con_Warning ("NAN in traceline:\nv1(%f %f %f) v2(%f %f %f)\nentity %d\n",
1684 v1[0], v1[1], v1[2], v2[0], v2[1], v2[2], NUM_FOR_EDICT(ent));
1685 }
1686 }
1687
1688 if (IS_NAN(v1[0]) || IS_NAN(v1[1]) || IS_NAN(v1[2]))
1689 v1[0] = v1[1] = v1[2] = 0;
1690 if (IS_NAN(v2[0]) || IS_NAN(v2[1]) || IS_NAN(v2[2]))
1691 v2[0] = v2[1] = v2[2] = 0;
1692
1693 trace = SV_Move (v1, mins, maxs, v2, nomonsters, ent);
1694
1695 pr_global_struct->trace_allsolid = trace.allsolid;
1696 pr_global_struct->trace_startsolid = trace.startsolid;
1697 pr_global_struct->trace_fraction = trace.fraction;
1698 pr_global_struct->trace_inwater = trace.inwater;
1699 pr_global_struct->trace_inopen = trace.inopen;
1700 VectorCopy (trace.endpos, pr_global_struct->trace_endpos);
1701 VectorCopy (trace.plane.normal, pr_global_struct->trace_plane_normal);
1702 pr_global_struct->trace_plane_dist = trace.plane.dist;
1703 if (trace.ent)
1704 pr_global_struct->trace_ent = EDICT_TO_PROG(trace.ent);
1705 else
1706 pr_global_struct->trace_ent = EDICT_TO_PROG(qcvm->edicts);
1707 }
PF_TraceToss(void)1708 static void PF_TraceToss(void)
1709 {
1710 extern cvar_t sv_maxvelocity, sv_gravity;
1711 int i;
1712 float gravity;
1713 vec3_t move, end;
1714 trace_t trace;
1715 eval_t *val;
1716
1717 vec3_t origin, velocity;
1718
1719 edict_t *tossent, *ignore;
1720 tossent = G_EDICT(OFS_PARM0);
1721 if (tossent == qcvm->edicts)
1722 Con_Warning("tracetoss: can not use world entity\n");
1723 ignore = G_EDICT(OFS_PARM1);
1724
1725 val = GetEdictFieldValue(tossent, qcvm->extfields.gravity);
1726 if (val && val->_float)
1727 gravity = val->_float;
1728 else
1729 gravity = 1;
1730 gravity *= sv_gravity.value * 0.05;
1731
1732 VectorCopy (tossent->v.origin, origin);
1733 VectorCopy (tossent->v.velocity, velocity);
1734
1735 SV_CheckVelocity (tossent);
1736
1737 for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds
1738 {
1739 velocity[2] -= gravity;
1740 VectorScale (velocity, 0.05, move);
1741 VectorAdd (origin, move, end);
1742 trace = SV_Move (origin, tossent->v.mins, tossent->v.maxs, end, MOVE_NORMAL, tossent);
1743 VectorCopy (trace.endpos, origin);
1744
1745 if (trace.fraction < 1 && trace.ent && trace.ent != ignore)
1746 break;
1747
1748 if (VectorLength(velocity) > sv_maxvelocity.value)
1749 {
1750 // Con_DPrintf("Slowing %s\n", PR_GetString(w->progs, tossent->v->classname));
1751 VectorScale (velocity, sv_maxvelocity.value/VectorLength(velocity), velocity);
1752 }
1753 }
1754
1755 trace.fraction = 0; // not relevant
1756
1757 //and return those as globals.
1758 pr_global_struct->trace_allsolid = trace.allsolid;
1759 pr_global_struct->trace_startsolid = trace.startsolid;
1760 pr_global_struct->trace_fraction = trace.fraction;
1761 pr_global_struct->trace_inwater = trace.inwater;
1762 pr_global_struct->trace_inopen = trace.inopen;
1763 VectorCopy (trace.endpos, pr_global_struct->trace_endpos);
1764 VectorCopy (trace.plane.normal, pr_global_struct->trace_plane_normal);
1765 pr_global_struct->trace_plane_dist = trace.plane.dist;
1766 if (trace.ent)
1767 pr_global_struct->trace_ent = EDICT_TO_PROG(trace.ent);
1768 else
1769 pr_global_struct->trace_ent = EDICT_TO_PROG(qcvm->edicts);
1770 }
1771
1772 //model stuff
1773 void SetMinMaxSize (edict_t *e, float *minvec, float *maxvec, qboolean rotate);
PF_sv_setmodelindex(void)1774 static void PF_sv_setmodelindex(void)
1775 {
1776 edict_t *e = G_EDICT(OFS_PARM0);
1777 unsigned int newidx = G_FLOAT(OFS_PARM1);
1778 qmodel_t *mod = qcvm->GetModel(newidx);
1779 e->v.model = (newidx<MAX_MODELS)?PR_SetEngineString(sv.model_precache[newidx]):0;
1780 e->v.modelindex = newidx;
1781
1782 if (mod)
1783 //johnfitz -- correct physics cullboxes for bmodels
1784 {
1785 if (mod->type == mod_brush || !sv_gameplayfix_setmodelrealbox.value)
1786 SetMinMaxSize (e, mod->clipmins, mod->clipmaxs, true);
1787 else
1788 SetMinMaxSize (e, mod->mins, mod->maxs, true);
1789 }
1790 //johnfitz
1791 else
1792 SetMinMaxSize (e, vec3_origin, vec3_origin, true);
1793 }
PF_cl_setmodelindex(void)1794 static void PF_cl_setmodelindex(void)
1795 {
1796 edict_t *e = G_EDICT(OFS_PARM0);
1797 int newidx = G_FLOAT(OFS_PARM1);
1798 qmodel_t *mod = qcvm->GetModel(newidx);
1799 e->v.model = mod?PR_SetEngineString(mod->name):0; //FIXME: is this going to cause issues with vid_restart?
1800 e->v.modelindex = newidx;
1801
1802 if (mod)
1803 //johnfitz -- correct physics cullboxes for bmodels
1804 {
1805 if (mod->type == mod_brush || !sv_gameplayfix_setmodelrealbox.value)
1806 SetMinMaxSize (e, mod->clipmins, mod->clipmaxs, true);
1807 else
1808 SetMinMaxSize (e, mod->mins, mod->maxs, true);
1809 }
1810 //johnfitz
1811 else
1812 SetMinMaxSize (e, vec3_origin, vec3_origin, true);
1813 }
1814
PF_frameforname(void)1815 static void PF_frameforname(void)
1816 {
1817 unsigned int modelindex = G_FLOAT(OFS_PARM0);
1818 const char *framename = G_STRING(OFS_PARM1);
1819 qmodel_t *mod = qcvm->GetModel(modelindex);
1820 aliashdr_t *alias;
1821
1822 G_FLOAT(OFS_RETURN) = -1;
1823 if (mod && mod->type == mod_alias && (alias = Mod_Extradata(mod)))
1824 {
1825 int i;
1826 for (i = 0; i < alias->numframes; i++)
1827 {
1828 if (!strcmp(alias->frames[i].name, framename))
1829 {
1830 G_FLOAT(OFS_RETURN) = i;
1831 break;
1832 }
1833 }
1834 }
1835 }
PF_frametoname(void)1836 static void PF_frametoname(void)
1837 {
1838 unsigned int modelindex = G_FLOAT(OFS_PARM0);
1839 unsigned int framenum = G_FLOAT(OFS_PARM1);
1840 qmodel_t *mod = qcvm->GetModel(modelindex);
1841 aliashdr_t *alias;
1842
1843 if (mod && mod->type == mod_alias && (alias = Mod_Extradata(mod)) && framenum < (unsigned int)alias->numframes)
1844 G_INT(OFS_RETURN) = PR_SetEngineString(alias->frames[framenum].name);
1845 else
1846 G_INT(OFS_RETURN) = 0;
1847 }
PF_frameduration(void)1848 static void PF_frameduration(void)
1849 {
1850 unsigned int modelindex = G_FLOAT(OFS_PARM0);
1851 unsigned int framenum = G_FLOAT(OFS_PARM1);
1852 qmodel_t *mod = qcvm->GetModel(modelindex);
1853 aliashdr_t *alias;
1854
1855 if (mod && mod->type == mod_alias && (alias = Mod_Extradata(mod)) && framenum < (unsigned int)alias->numframes)
1856 G_FLOAT(OFS_RETURN) = alias->frames[framenum].numposes * alias->frames[framenum].interval;
1857 }
PF_getsurfacenumpoints(void)1858 static void PF_getsurfacenumpoints(void)
1859 {
1860 edict_t *ed = G_EDICT(OFS_PARM0);
1861 unsigned int surfidx = G_FLOAT(OFS_PARM1);
1862 unsigned int modelindex = ed->v.modelindex;
1863 qmodel_t *mod = qcvm->GetModel(modelindex);
1864
1865 if (mod && mod->type == mod_brush && !mod->needload && surfidx < (unsigned int)mod->nummodelsurfaces)
1866 {
1867 surfidx += mod->firstmodelsurface;
1868 G_FLOAT(OFS_RETURN) = mod->surfaces[surfidx].numedges;
1869 }
1870 else
1871 G_FLOAT(OFS_RETURN) = 0;
1872 }
PF_getsurfacevertex(qmodel_t * mod,msurface_t * surf,unsigned int vert)1873 static mvertex_t *PF_getsurfacevertex(qmodel_t *mod, msurface_t *surf, unsigned int vert)
1874 {
1875 signed int edge = mod->surfedges[vert+surf->firstedge];
1876 if (edge >= 0)
1877 return &mod->vertexes[mod->edges[edge].v[0]];
1878 else
1879 return &mod->vertexes[mod->edges[-edge].v[1]];
1880 }
PF_getsurfacepoint(void)1881 static void PF_getsurfacepoint(void)
1882 {
1883 edict_t *ed = G_EDICT(OFS_PARM0);
1884 unsigned int surfidx = G_FLOAT(OFS_PARM1);
1885 unsigned int point = G_FLOAT(OFS_PARM2);
1886 qmodel_t *mod = qcvm->GetModel(ed->v.modelindex);
1887
1888 if (mod && mod->type == mod_brush && !mod->needload && surfidx < (unsigned int)mod->nummodelsurfaces && point < (unsigned int)mod->surfaces[surfidx].numedges)
1889 {
1890 mvertex_t *v = PF_getsurfacevertex(mod, &mod->surfaces[surfidx+mod->firstmodelsurface], point);
1891 VectorCopy(v->position, G_VECTOR(OFS_RETURN));
1892 }
1893 else
1894 {
1895 G_FLOAT(OFS_RETURN+0) = 0;
1896 G_FLOAT(OFS_RETURN+1) = 0;
1897 G_FLOAT(OFS_RETURN+2) = 0;
1898 }
1899 }
PF_getsurfacenumtriangles(void)1900 static void PF_getsurfacenumtriangles(void)
1901 { //for q3bsp compat (which this engine doesn't support, so its fairly simple)
1902 edict_t *ed = G_EDICT(OFS_PARM0);
1903 unsigned int surfidx = G_FLOAT(OFS_PARM1);
1904 qmodel_t *mod = qcvm->GetModel(ed->v.modelindex);
1905
1906 if (mod && mod->type == mod_brush && !mod->needload && surfidx < (unsigned int)mod->nummodelsurfaces)
1907 G_FLOAT(OFS_RETURN) = (mod->surfaces[surfidx+mod->firstmodelsurface].numedges-2); //q1bsp is only triangle fans
1908 else
1909 G_FLOAT(OFS_RETURN) = 0;
1910 }
PF_getsurfacetriangle(void)1911 static void PF_getsurfacetriangle(void)
1912 { //for q3bsp compat (which this engine doesn't support, so its fairly simple)
1913 edict_t *ed = G_EDICT(OFS_PARM0);
1914 unsigned int surfidx = G_FLOAT(OFS_PARM1);
1915 unsigned int triangleidx= G_FLOAT(OFS_PARM2);
1916 qmodel_t *mod = qcvm->GetModel(ed->v.modelindex);
1917
1918 if (mod && mod->type == mod_brush && !mod->needload && surfidx < (unsigned int)mod->nummodelsurfaces && triangleidx < (unsigned int)mod->surfaces[surfidx].numedges-2)
1919 {
1920 G_FLOAT(OFS_RETURN+0) = 0;
1921 G_FLOAT(OFS_RETURN+1) = triangleidx+1;
1922 G_FLOAT(OFS_RETURN+2) = triangleidx+2;
1923 }
1924 else
1925 {
1926 G_FLOAT(OFS_RETURN+0) = 0;
1927 G_FLOAT(OFS_RETURN+1) = 0;
1928 G_FLOAT(OFS_RETURN+2) = 0;
1929 }
1930 }
PF_getsurfacenormal(void)1931 static void PF_getsurfacenormal(void)
1932 {
1933 edict_t *ed = G_EDICT(OFS_PARM0);
1934 unsigned int surfidx = G_FLOAT(OFS_PARM1);
1935 qmodel_t *mod = qcvm->GetModel(ed->v.modelindex);
1936
1937 if (mod && mod->type == mod_brush && !mod->needload && surfidx < (unsigned int)mod->nummodelsurfaces)
1938 {
1939 surfidx += mod->firstmodelsurface;
1940 VectorCopy(mod->surfaces[surfidx].plane->normal, G_VECTOR(OFS_RETURN));
1941 if (mod->surfaces[surfidx].flags & SURF_PLANEBACK)
1942 VectorInverse(G_VECTOR(OFS_RETURN));
1943 }
1944 else
1945 G_FLOAT(OFS_RETURN) = 0;
1946 }
PF_getsurfacetexture(void)1947 static void PF_getsurfacetexture(void)
1948 {
1949 edict_t *ed = G_EDICT(OFS_PARM0);
1950 unsigned int surfidx = G_FLOAT(OFS_PARM1);
1951 qmodel_t *mod = qcvm->GetModel(ed->v.modelindex);
1952
1953 if (mod && mod->type == mod_brush && !mod->needload && surfidx < (unsigned int)mod->nummodelsurfaces)
1954 {
1955 surfidx += mod->firstmodelsurface;
1956 G_INT(OFS_RETURN) = PR_SetEngineString(mod->surfaces[surfidx].texinfo->texture->name);
1957 }
1958 else
1959 G_INT(OFS_RETURN) = 0;
1960 }
1961
1962 #define TriangleNormal(a,b,c,n) ( \
1963 (n)[0] = ((a)[1] - (b)[1]) * ((c)[2] - (b)[2]) - ((a)[2] - (b)[2]) * ((c)[1] - (b)[1]), \
1964 (n)[1] = ((a)[2] - (b)[2]) * ((c)[0] - (b)[0]) - ((a)[0] - (b)[0]) * ((c)[2] - (b)[2]), \
1965 (n)[2] = ((a)[0] - (b)[0]) * ((c)[1] - (b)[1]) - ((a)[1] - (b)[1]) * ((c)[0] - (b)[0]) \
1966 )
getsurface_clippointpoly(qmodel_t * model,msurface_t * surf,vec3_t point,vec3_t bestcpoint,float bestdist)1967 static float getsurface_clippointpoly(qmodel_t *model, msurface_t *surf, vec3_t point, vec3_t bestcpoint, float bestdist)
1968 {
1969 int e, edge;
1970 vec3_t edgedir, edgenormal, cpoint, temp;
1971 mvertex_t *v1, *v2;
1972 float dist = DotProduct(point, surf->plane->normal) - surf->plane->dist;
1973 //don't care about SURF_PLANEBACK, the maths works out the same.
1974
1975 if (dist*dist < bestdist)
1976 { //within a specific range
1977 //make sure it's within the poly
1978 VectorMA(point, dist, surf->plane->normal, cpoint);
1979 for (e = surf->firstedge+surf->numedges; e > surf->firstedge; edge++)
1980 {
1981 edge = model->surfedges[--e];
1982 if (edge < 0)
1983 {
1984 v1 = &model->vertexes[model->edges[-edge].v[0]];
1985 v2 = &model->vertexes[model->edges[-edge].v[1]];
1986 }
1987 else
1988 {
1989 v2 = &model->vertexes[model->edges[edge].v[0]];
1990 v1 = &model->vertexes[model->edges[edge].v[1]];
1991 }
1992
1993 VectorSubtract(v1->position, v2->position, edgedir);
1994 CrossProduct(edgedir, surf->plane->normal, edgenormal);
1995 if (!(surf->flags & SURF_PLANEBACK))
1996 {
1997 VectorSubtract(vec3_origin, edgenormal, edgenormal);
1998 }
1999 VectorNormalize(edgenormal);
2000
2001 dist = DotProduct(v1->position, edgenormal) - DotProduct(cpoint, edgenormal);
2002 if (dist < 0)
2003 VectorMA(cpoint, dist, edgenormal, cpoint);
2004 }
2005
2006 VectorSubtract(cpoint, point, temp);
2007 dist = DotProduct(temp, temp);
2008 if (dist < bestdist)
2009 {
2010 bestdist = dist;
2011 VectorCopy(cpoint, bestcpoint);
2012 }
2013 }
2014 return bestdist;
2015 }
2016
2017 // #438 float(entity e, vector p) getsurfacenearpoint (DP_QC_GETSURFACE)
PF_getsurfacenearpoint(void)2018 static void PF_getsurfacenearpoint(void)
2019 {
2020 qmodel_t *model;
2021 edict_t *ent;
2022 msurface_t *surf;
2023 int i;
2024 float *point;
2025
2026 vec3_t cpoint = {0,0,0};
2027 float bestdist, dist;
2028 int bestsurf = -1;
2029
2030 ent = G_EDICT(OFS_PARM0);
2031 point = G_VECTOR(OFS_PARM1);
2032
2033 G_FLOAT(OFS_RETURN) = -1;
2034
2035 model = qcvm->GetModel(ent->v.modelindex);
2036
2037 if (!model || model->type != mod_brush || model->needload)
2038 return;
2039
2040 bestdist = 256;
2041
2042 //all polies, we can skip parts. special case.
2043 surf = model->surfaces + model->firstmodelsurface;
2044 for (i = 0; i < model->nummodelsurfaces; i++, surf++)
2045 {
2046 dist = getsurface_clippointpoly(model, surf, point, cpoint, bestdist);
2047 if (dist < bestdist)
2048 {
2049 bestdist = dist;
2050 bestsurf = i;
2051 }
2052 }
2053 G_FLOAT(OFS_RETURN) = bestsurf;
2054 }
2055
2056 // #439 vector(entity e, float s, vector p) getsurfaceclippedpoint (DP_QC_GETSURFACE)
PF_getsurfaceclippedpoint(void)2057 static void PF_getsurfaceclippedpoint(void)
2058 {
2059 qmodel_t *model;
2060 edict_t *ent;
2061 msurface_t *surf;
2062 float *point;
2063 int surfnum;
2064
2065 float *result = G_VECTOR(OFS_RETURN);
2066
2067 ent = G_EDICT(OFS_PARM0);
2068 surfnum = G_FLOAT(OFS_PARM1);
2069 point = G_VECTOR(OFS_PARM2);
2070
2071 VectorCopy(point, result);
2072
2073 model = qcvm->GetModel(ent->v.modelindex);
2074
2075 if (!model || model->type != mod_brush || model->needload)
2076 return;
2077 if (surfnum >= model->nummodelsurfaces)
2078 return;
2079
2080 //all polies, we can skip parts. special case.
2081 surf = model->surfaces + model->firstmodelsurface + surfnum;
2082 getsurface_clippointpoly(model, surf, point, result, FLT_MAX);
2083 }
2084
PF_getsurfacepointattribute(void)2085 static void PF_getsurfacepointattribute(void)
2086 {
2087 edict_t *ed = G_EDICT(OFS_PARM0);
2088 unsigned int surfidx = G_FLOAT(OFS_PARM1);
2089 unsigned int point = G_FLOAT(OFS_PARM2);
2090 unsigned int attribute = G_FLOAT(OFS_PARM3);
2091
2092 qmodel_t *mod = qcvm->GetModel(ed->v.modelindex);
2093
2094 if (mod && mod->type == mod_brush && !mod->needload && surfidx < (unsigned int)mod->nummodelsurfaces && point < (unsigned int)mod->surfaces[mod->firstmodelsurface+surfidx].numedges)
2095 {
2096 msurface_t *fa = &mod->surfaces[surfidx+mod->firstmodelsurface];
2097 mvertex_t *v = PF_getsurfacevertex(mod, fa, point);
2098 switch(attribute)
2099 {
2100 default:
2101 Con_Warning("PF_getsurfacepointattribute: attribute %u not supported\n", attribute);
2102 G_FLOAT(OFS_RETURN+0) = 0;
2103 G_FLOAT(OFS_RETURN+1) = 0;
2104 G_FLOAT(OFS_RETURN+2) = 0;
2105 break;
2106 case 0: //xyz coord
2107 VectorCopy(v->position, G_VECTOR(OFS_RETURN));
2108 break;
2109 case 1: //s dir
2110 case 2: //t dir
2111 {
2112 //figure out how similar to the normal it is, and negate any influence, so that its perpendicular
2113 float sc = -DotProduct(fa->plane->normal, fa->texinfo->vecs[attribute-1]);
2114 VectorMA(fa->texinfo->vecs[attribute-1], sc, fa->plane->normal, G_VECTOR(OFS_RETURN));
2115 VectorNormalize(G_VECTOR(OFS_RETURN));
2116 }
2117 break;
2118 case 3: //normal
2119 VectorCopy(fa->plane->normal, G_VECTOR(OFS_RETURN));
2120 if (fa->flags & SURF_PLANEBACK)
2121 VectorInverse(G_VECTOR(OFS_RETURN));
2122 break;
2123 case 4: //st coord
2124 G_FLOAT(OFS_RETURN+0) = (DotProduct(v->position, fa->texinfo->vecs[0]) + fa->texinfo->vecs[0][3]) / fa->texinfo->texture->width;
2125 G_FLOAT(OFS_RETURN+1) = (DotProduct(v->position, fa->texinfo->vecs[1]) + fa->texinfo->vecs[1][3]) / fa->texinfo->texture->height;
2126 G_FLOAT(OFS_RETURN+2) = 0;
2127 break;
2128 case 5: //lmst coord, not actually very useful
2129 G_FLOAT(OFS_RETURN+0) = (DotProduct(v->position, fa->texinfo->vecs[0]) + fa->texinfo->vecs[0][3] - fa->texturemins[0] + (fa->light_s+.5)) / LMBLOCK_WIDTH;
2130 G_FLOAT(OFS_RETURN+1) = (DotProduct(v->position, fa->texinfo->vecs[1]) + fa->texinfo->vecs[1][3] - fa->texturemins[1] + (fa->light_t+.5)) / LMBLOCK_HEIGHT;
2131 G_FLOAT(OFS_RETURN+2) = 0;
2132 break;
2133 case 6: //colour
2134 G_FLOAT(OFS_RETURN+0) = 1;
2135 G_FLOAT(OFS_RETURN+1) = 1;
2136 G_FLOAT(OFS_RETURN+2) = 1;
2137 break;
2138 }
2139 }
2140 else
2141 {
2142 G_FLOAT(OFS_RETURN+0) = 0;
2143 G_FLOAT(OFS_RETURN+1) = 0;
2144 G_FLOAT(OFS_RETURN+2) = 0;
2145 }
2146 }
PF_sv_getlight(void)2147 static void PF_sv_getlight(void)
2148 {
2149 qmodel_t *om = cl.worldmodel;
2150 float *point = G_VECTOR(OFS_PARM0);
2151
2152 cl.worldmodel = qcvm->worldmodel; //R_LightPoint is really clientside, so if its called from ssqc then try to make things work regardless
2153 //FIXME: d_lightstylevalue isn't set on dedicated servers
2154
2155 //FIXME: seems like quakespasm doesn't do lits for model lighting, so we won't either.
2156 G_FLOAT(OFS_RETURN+0) = G_FLOAT(OFS_RETURN+1) = G_FLOAT(OFS_RETURN+2) = R_LightPoint(point, NULL) / 255.0;
2157
2158 cl.worldmodel = om;
2159 }
2160 #define PF_cl_getlight PF_sv_getlight
2161
2162 //server/client stuff
PF_checkcommand(void)2163 static void PF_checkcommand(void)
2164 {
2165 const char *name = G_STRING(OFS_PARM0);
2166 if (Cmd_Exists(name))
2167 G_FLOAT(OFS_RETURN) = 1;
2168 else if (Cmd_AliasExists(name))
2169 G_FLOAT(OFS_RETURN) = 2;
2170 else if (Cvar_FindVar(name))
2171 G_FLOAT(OFS_RETURN) = 3;
2172 else
2173 G_FLOAT(OFS_RETURN) = 0;
2174 }
PF_clientcommand(void)2175 static void PF_clientcommand(void)
2176 {
2177 edict_t *ed = G_EDICT(OFS_PARM0);
2178 const char *str = G_STRING(OFS_PARM1);
2179 unsigned int i = NUM_FOR_EDICT(ed)-1;
2180 if (i < (unsigned int)svs.maxclients && svs.clients[i].active)
2181 {
2182 client_t *ohc = host_client;
2183 host_client = &svs.clients[i];
2184 Cmd_ExecuteString (str, src_client);
2185 host_client = ohc;
2186 }
2187 else
2188 Con_Printf("PF_clientcommand: not a client\n");
2189 }
PF_clienttype(void)2190 static void PF_clienttype(void)
2191 {
2192 edict_t *ed = G_EDICT(OFS_PARM0);
2193 unsigned int i = NUM_FOR_EDICT(ed)-1;
2194 if (i >= (unsigned int)svs.maxclients)
2195 {
2196 G_FLOAT(OFS_RETURN) = 3; //CLIENTTYPE_NOTACLIENT
2197 return;
2198 }
2199 if (svs.clients[i].active)
2200 {
2201 if (svs.clients[i].netconnection)
2202 G_FLOAT(OFS_RETURN) = 1;//CLIENTTYPE_REAL;
2203 else
2204 G_FLOAT(OFS_RETURN) = 2;//CLIENTTYPE_BOT;
2205 }
2206 else
2207 G_FLOAT(OFS_RETURN) = 0;//CLIENTTYPE_DISCONNECTED;
2208 }
PF_spawnclient(void)2209 static void PF_spawnclient(void)
2210 {
2211 edict_t *ent;
2212 unsigned int i;
2213 if (svs.maxclients)
2214 for (i = svs.maxclients; i --> 0; )
2215 {
2216 if (!svs.clients[i].active)
2217 {
2218 svs.clients[i].netconnection = NULL; //botclients have no net connection, obviously.
2219 SV_ConnectClient(i);
2220 svs.clients[i].spawned = true;
2221 ent = svs.clients[i].edict;
2222 memset (&ent->v, 0, qcvm->progs->entityfields * 4);
2223 ent->v.colormap = NUM_FOR_EDICT(ent);
2224 ent->v.team = (svs.clients[i].colors & 15) + 1;
2225 ent->v.netname = PR_SetEngineString(svs.clients[i].name);
2226 RETURN_EDICT(ent);
2227 return;
2228 }
2229 }
2230 RETURN_EDICT(qcvm->edicts);
2231 }
PF_dropclient(void)2232 static void PF_dropclient(void)
2233 {
2234 edict_t *ed = G_EDICT(OFS_PARM0);
2235 unsigned int i = NUM_FOR_EDICT(ed)-1;
2236 if (i < (unsigned int)svs.maxclients && svs.clients[i].active)
2237 { //FIXME: should really set a flag or something, to avoid recursion issues.
2238 client_t *ohc = host_client;
2239 host_client = &svs.clients[i];
2240 SV_DropClient (false);
2241 host_client = ohc;
2242 }
2243 }
2244
2245 //console/cvar stuff
PF_print(void)2246 static void PF_print(void)
2247 {
2248 int i;
2249 for (i = 0; i < qcvm->argc; i++)
2250 Con_Printf("%s", G_STRING(OFS_PARM0 + i*3));
2251 }
PF_cvar_string(void)2252 static void PF_cvar_string(void)
2253 {
2254 const char *name = G_STRING(OFS_PARM0);
2255 cvar_t *var = Cvar_FindVar(name);
2256 if (var && var->string)
2257 {
2258 //cvars can easily change values.
2259 //this would result in leaks/exploits/slowdowns if the qc spams calls to cvar_string+changes.
2260 //so keep performance consistent, even if this is going to be slower.
2261 char *temp = PR_GetTempString();
2262 q_strlcpy(temp, var->string, STRINGTEMP_LENGTH);
2263 G_INT(OFS_RETURN) = PR_SetEngineString(temp);
2264 }
2265 else if (!strcmp(name, "game"))
2266 { //game looks like a cvar in most other respects (and is a cvar in fte). let cvar_string work on it as a way to find out the current gamedir.
2267 char *temp = PR_GetTempString();
2268 q_strlcpy(temp, COM_GetGameNames(true), STRINGTEMP_LENGTH);
2269 G_INT(OFS_RETURN) = PR_SetEngineString(temp);
2270 }
2271 else
2272 G_INT(OFS_RETURN) = 0;
2273 }
PF_cvar_defstring(void)2274 static void PF_cvar_defstring(void)
2275 {
2276 const char *name = G_STRING(OFS_PARM0);
2277 cvar_t *var = Cvar_FindVar(name);
2278 if (var && var->default_string)
2279 G_INT(OFS_RETURN) = PR_SetEngineString(var->default_string);
2280 else
2281 G_INT(OFS_RETURN) = 0;
2282 }
PF_cvar_type(void)2283 static void PF_cvar_type(void)
2284 {
2285 const char *str = G_STRING(OFS_PARM0);
2286 int ret = 0;
2287 cvar_t *v;
2288
2289 v = Cvar_FindVar(str);
2290 if (v)
2291 {
2292 ret |= 1; // CVAR_EXISTS
2293 if(v->flags & CVAR_ARCHIVE)
2294 ret |= 2; // CVAR_TYPE_SAVED
2295 // if(v->flags & CVAR_NOTFROMSERVER)
2296 // ret |= 4; // CVAR_TYPE_PRIVATE
2297 if(!(v->flags & CVAR_USERDEFINED))
2298 ret |= 8; // CVAR_TYPE_ENGINE
2299 // if (v->description)
2300 // ret |= 16; // CVAR_TYPE_HASDESCRIPTION
2301 }
2302 G_FLOAT(OFS_RETURN) = ret;
2303 }
PF_cvar_description(void)2304 static void PF_cvar_description(void)
2305 { //quakespasm does not support cvar descriptions. we provide this stub to avoid crashes.
2306 G_INT(OFS_RETURN) = 0;
2307 }
PF_registercvar(void)2308 static void PF_registercvar(void)
2309 {
2310 const char *name = G_STRING(OFS_PARM0);
2311 const char *value = (qcvm->argc>1)?G_STRING(OFS_PARM0):"";
2312 Cvar_Create(name, value);
2313 }
2314
2315 //temp entities + networking
PF_WriteString2(void)2316 static void PF_WriteString2(void)
2317 { //writes a string without the null. a poor-man's strcat.
2318 const char *string = G_STRING(OFS_PARM0);
2319 SZ_Write (WriteDest(), string, Q_strlen(string));
2320 }
PF_WriteFloat(void)2321 static void PF_WriteFloat(void)
2322 { //curiously, this was missing in vanilla.
2323 MSG_WriteFloat(WriteDest(), G_FLOAT(OFS_PARM0));
2324 }
PF_sv_te_blooddp(void)2325 static void PF_sv_te_blooddp(void)
2326 { //blood is common enough that we should emulate it for when engines do actually support it.
2327 float *org = G_VECTOR(OFS_PARM0);
2328 float *dir = G_VECTOR(OFS_PARM1);
2329 float color = 73;
2330 float count = G_FLOAT(OFS_PARM2);
2331 SV_StartParticle (org, dir, color, count);
2332 }
PF_sv_te_bloodqw(void)2333 static void PF_sv_te_bloodqw(void)
2334 { //qw tried to strip a lot.
2335 float *org = G_VECTOR(OFS_PARM0);
2336 float *dir = vec3_origin;
2337 float color = 73;
2338 float count = G_FLOAT(OFS_PARM1)*20;
2339 SV_StartParticle (org, dir, color, count);
2340 }
PF_sv_te_lightningblood(void)2341 static void PF_sv_te_lightningblood(void)
2342 { //a qw builtin, to replace particle.
2343 float *org = G_VECTOR(OFS_PARM0);
2344 vec3_t dir = {0, 0, -100};
2345 float color = 20;
2346 float count = 225;
2347 SV_StartParticle (org, dir, color, count);
2348 }
PF_sv_te_spike(void)2349 static void PF_sv_te_spike(void)
2350 {
2351 float *org = G_VECTOR(OFS_PARM0);
2352 MSG_WriteByte(&sv.datagram, svc_temp_entity);
2353 MSG_WriteByte(&sv.datagram, TE_SPIKE);
2354 MSG_WriteCoord(&sv.datagram, org[0], sv.protocolflags);
2355 MSG_WriteCoord(&sv.datagram, org[1], sv.protocolflags);
2356 MSG_WriteCoord(&sv.datagram, org[2], sv.protocolflags);
2357 SV_Multicast(MULTICAST_PVS_U, org, 0, 0);
2358 }
PF_cl_te_spike(void)2359 static void PF_cl_te_spike(void)
2360 {
2361 float *pos = G_VECTOR(OFS_PARM0);
2362
2363 if (PScript_RunParticleEffectTypeString(pos, NULL, 1, "TE_SPIKE"))
2364 R_RunParticleEffect (pos, vec3_origin, 0, 10);
2365 if ( rand() % 5 )
2366 S_StartSound (-1, 0, S_PrecacheSound ("weapons/tink1.wav"), pos, 1, 1);
2367 else
2368 {
2369 int rnd = rand() & 3;
2370 if (rnd == 1)
2371 S_StartSound (-1, 0, S_PrecacheSound ("weapons/ric1.wav"), pos, 1, 1);
2372 else if (rnd == 2)
2373 S_StartSound (-1, 0, S_PrecacheSound ("weapons/ric2.wav"), pos, 1, 1);
2374 else
2375 S_StartSound (-1, 0, S_PrecacheSound ("weapons/ric3.wav"), pos, 1, 1);
2376 }
2377 }
PF_sv_te_superspike(void)2378 static void PF_sv_te_superspike(void)
2379 {
2380 float *org = G_VECTOR(OFS_PARM0);
2381 MSG_WriteByte(&sv.datagram, svc_temp_entity);
2382 MSG_WriteByte(&sv.datagram, TE_SUPERSPIKE);
2383 MSG_WriteCoord(&sv.datagram, org[0], sv.protocolflags);
2384 MSG_WriteCoord(&sv.datagram, org[1], sv.protocolflags);
2385 MSG_WriteCoord(&sv.datagram, org[2], sv.protocolflags);
2386 SV_Multicast(MULTICAST_PVS_U, org, 0, 0);
2387 }
PF_cl_te_superspike(void)2388 static void PF_cl_te_superspike(void)
2389 {
2390 float *pos = G_VECTOR(OFS_PARM0);
2391
2392 if (PScript_RunParticleEffectTypeString(pos, NULL, 1, "TE_SUPERSPIKE"))
2393 R_RunParticleEffect (pos, vec3_origin, 0, 20);
2394
2395 if ( rand() % 5 )
2396 S_StartSound (-1, 0, S_PrecacheSound ("weapons/tink1.wav"), pos, 1, 1);
2397 else
2398 {
2399 int rnd = rand() & 3;
2400 if (rnd == 1)
2401 S_StartSound (-1, 0, S_PrecacheSound ("weapons/ric1.wav"), pos, 1, 1);
2402 else if (rnd == 2)
2403 S_StartSound (-1, 0, S_PrecacheSound ("weapons/ric2.wav"), pos, 1, 1);
2404 else
2405 S_StartSound (-1, 0, S_PrecacheSound ("weapons/ric3.wav"), pos, 1, 1);
2406 }
2407 }
PF_sv_te_gunshot(void)2408 static void PF_sv_te_gunshot(void)
2409 {
2410 float *org = G_VECTOR(OFS_PARM0);
2411 //float count = G_FLOAT(OFS_PARM1)*20;
2412 MSG_WriteByte(&sv.datagram, svc_temp_entity);
2413 MSG_WriteByte(&sv.datagram, TE_GUNSHOT);
2414 MSG_WriteCoord(&sv.datagram, org[0], sv.protocolflags);
2415 MSG_WriteCoord(&sv.datagram, org[1], sv.protocolflags);
2416 MSG_WriteCoord(&sv.datagram, org[2], sv.protocolflags);
2417 SV_Multicast(MULTICAST_PVS_U, org, 0, 0);
2418 }
PF_cl_te_gunshot(void)2419 static void PF_cl_te_gunshot(void)
2420 {
2421 float *pos = G_VECTOR(OFS_PARM0);
2422
2423 int rnd = 20;
2424 if (PScript_RunParticleEffectTypeString(pos, NULL, rnd, "TE_GUNSHOT"))
2425 R_RunParticleEffect (pos, vec3_origin, 0, rnd);
2426 }
PF_sv_te_explosion(void)2427 static void PF_sv_te_explosion(void)
2428 {
2429 float *org = G_VECTOR(OFS_PARM0);
2430 MSG_WriteByte(&sv.datagram, svc_temp_entity);
2431 MSG_WriteByte(&sv.datagram, TE_EXPLOSION);
2432 MSG_WriteCoord(&sv.datagram, org[0], sv.protocolflags);
2433 MSG_WriteCoord(&sv.datagram, org[1], sv.protocolflags);
2434 MSG_WriteCoord(&sv.datagram, org[2], sv.protocolflags);
2435 SV_Multicast(MULTICAST_PHS_U, org, 0, 0);
2436 }
PF_cl_te_explosion(void)2437 static void PF_cl_te_explosion(void)
2438 {
2439 float *pos = G_VECTOR(OFS_PARM0);
2440
2441 dlight_t *dl;
2442 if (PScript_RunParticleEffectTypeString(pos, NULL, 1, "TE_EXPLOSION"))
2443 R_ParticleExplosion (pos);
2444 dl = CL_AllocDlight (0);
2445 VectorCopy (pos, dl->origin);
2446 dl->radius = 350;
2447 dl->die = cl.time + 0.5;
2448 dl->decay = 300;
2449 S_StartSound (-1, 0, S_PrecacheSound ("weapons/r_exp3.wav"), pos, 1, 1);
2450 }
PF_sv_te_tarexplosion(void)2451 static void PF_sv_te_tarexplosion(void)
2452 {
2453 float *org = G_VECTOR(OFS_PARM0);
2454 MSG_WriteByte(&sv.datagram, svc_temp_entity);
2455 MSG_WriteByte(&sv.datagram, TE_TAREXPLOSION);
2456 MSG_WriteCoord(&sv.datagram, org[0], sv.protocolflags);
2457 MSG_WriteCoord(&sv.datagram, org[1], sv.protocolflags);
2458 MSG_WriteCoord(&sv.datagram, org[2], sv.protocolflags);
2459 SV_Multicast(MULTICAST_PHS_U, org, 0, 0);
2460 }
PF_cl_te_tarexplosion(void)2461 static void PF_cl_te_tarexplosion(void)
2462 {
2463 float *pos = G_VECTOR(OFS_PARM0);
2464
2465 if (PScript_RunParticleEffectTypeString(pos, NULL, 1, "TE_TAREXPLOSION"))
2466 R_BlobExplosion (pos);
2467 S_StartSound (-1, 0, S_PrecacheSound ("weapons/r_exp3.wav"), pos, 1, 1);
2468 }
PF_sv_te_lightning1(void)2469 static void PF_sv_te_lightning1(void)
2470 {
2471 edict_t *ed = G_EDICT(OFS_PARM0);
2472 float *start = G_VECTOR(OFS_PARM1);
2473 float *end = G_VECTOR(OFS_PARM2);
2474 MSG_WriteByte(&sv.datagram, svc_temp_entity);
2475 MSG_WriteByte(&sv.datagram, TE_LIGHTNING1);
2476 MSG_WriteShort(&sv.datagram, NUM_FOR_EDICT(ed));
2477 MSG_WriteCoord(&sv.datagram, start[0], sv.protocolflags);
2478 MSG_WriteCoord(&sv.datagram, start[1], sv.protocolflags);
2479 MSG_WriteCoord(&sv.datagram, start[2], sv.protocolflags);
2480 MSG_WriteCoord(&sv.datagram, end[0], sv.protocolflags);
2481 MSG_WriteCoord(&sv.datagram, end[1], sv.protocolflags);
2482 MSG_WriteCoord(&sv.datagram, end[2], sv.protocolflags);
2483 SV_Multicast(MULTICAST_PHS_U, start, 0, 0);
2484 }
PF_cl_te_lightning1(void)2485 static void PF_cl_te_lightning1(void)
2486 {
2487 edict_t *ed = G_EDICT(OFS_PARM0);
2488 float *start = G_VECTOR(OFS_PARM1);
2489 float *end = G_VECTOR(OFS_PARM2);
2490
2491 CL_UpdateBeam (Mod_ForName("progs/bolt.mdl", true), "TE_LIGHTNING1", "TE_LIGHTNING1_END", -NUM_FOR_EDICT(ed), start, end);
2492 }
PF_sv_te_lightning2(void)2493 static void PF_sv_te_lightning2(void)
2494 {
2495 edict_t *ed = G_EDICT(OFS_PARM0);
2496 float *start = G_VECTOR(OFS_PARM1);
2497 float *end = G_VECTOR(OFS_PARM2);
2498 MSG_WriteByte(&sv.datagram, svc_temp_entity);
2499 MSG_WriteByte(&sv.datagram, TE_LIGHTNING2);
2500 MSG_WriteShort(&sv.datagram, NUM_FOR_EDICT(ed));
2501 MSG_WriteCoord(&sv.datagram, start[0], sv.protocolflags);
2502 MSG_WriteCoord(&sv.datagram, start[1], sv.protocolflags);
2503 MSG_WriteCoord(&sv.datagram, start[2], sv.protocolflags);
2504 MSG_WriteCoord(&sv.datagram, end[0], sv.protocolflags);
2505 MSG_WriteCoord(&sv.datagram, end[1], sv.protocolflags);
2506 MSG_WriteCoord(&sv.datagram, end[2], sv.protocolflags);
2507 SV_Multicast(MULTICAST_PHS_U, start, 0, 0);
2508 }
PF_cl_te_lightning2(void)2509 static void PF_cl_te_lightning2(void)
2510 {
2511 edict_t *ed = G_EDICT(OFS_PARM0);
2512 float *start = G_VECTOR(OFS_PARM1);
2513 float *end = G_VECTOR(OFS_PARM2);
2514
2515 CL_UpdateBeam (Mod_ForName("progs/bolt2.mdl", true), "TE_LIGHTNING2", "TE_LIGHTNING2_END", -NUM_FOR_EDICT(ed), start, end);
2516 }
PF_sv_te_wizspike(void)2517 static void PF_sv_te_wizspike(void)
2518 {
2519 float *org = G_VECTOR(OFS_PARM0);
2520 MSG_WriteByte(&sv.datagram, svc_temp_entity);
2521 MSG_WriteByte(&sv.datagram, TE_WIZSPIKE);
2522 MSG_WriteCoord(&sv.datagram, org[0], sv.protocolflags);
2523 MSG_WriteCoord(&sv.datagram, org[1], sv.protocolflags);
2524 MSG_WriteCoord(&sv.datagram, org[2], sv.protocolflags);
2525 SV_Multicast(MULTICAST_PHS_U, org, 0, 0);
2526 }
PF_cl_te_wizspike(void)2527 static void PF_cl_te_wizspike(void)
2528 {
2529 float *pos = G_VECTOR(OFS_PARM0);
2530 S_StartSound (-1, 0, S_PrecacheSound ("wizard/hit.wav"), pos, 1, 1);
2531 }
PF_sv_te_knightspike(void)2532 static void PF_sv_te_knightspike(void)
2533 {
2534 float *org = G_VECTOR(OFS_PARM0);
2535 MSG_WriteByte(&sv.datagram, svc_temp_entity);
2536 MSG_WriteByte(&sv.datagram, TE_KNIGHTSPIKE);
2537 MSG_WriteCoord(&sv.datagram, org[0], sv.protocolflags);
2538 MSG_WriteCoord(&sv.datagram, org[1], sv.protocolflags);
2539 MSG_WriteCoord(&sv.datagram, org[2], sv.protocolflags);
2540 SV_Multicast(MULTICAST_PHS_U, org, 0, 0);
2541 }
PF_cl_te_knightspike(void)2542 static void PF_cl_te_knightspike(void)
2543 {
2544 float *pos = G_VECTOR(OFS_PARM0);
2545 S_StartSound (-1, 0, S_PrecacheSound ("hknight/hit.wav"), pos, 1, 1);
2546 }
PF_sv_te_lightning3(void)2547 static void PF_sv_te_lightning3(void)
2548 {
2549 edict_t *ed = G_EDICT(OFS_PARM0);
2550 float *start = G_VECTOR(OFS_PARM1);
2551 float *end = G_VECTOR(OFS_PARM2);
2552 MSG_WriteByte(&sv.datagram, svc_temp_entity);
2553 MSG_WriteByte(&sv.datagram, TE_LIGHTNING3);
2554 MSG_WriteShort(&sv.datagram, NUM_FOR_EDICT(ed));
2555 MSG_WriteCoord(&sv.datagram, start[0], sv.protocolflags);
2556 MSG_WriteCoord(&sv.datagram, start[1], sv.protocolflags);
2557 MSG_WriteCoord(&sv.datagram, start[2], sv.protocolflags);
2558 MSG_WriteCoord(&sv.datagram, end[0], sv.protocolflags);
2559 MSG_WriteCoord(&sv.datagram, end[1], sv.protocolflags);
2560 MSG_WriteCoord(&sv.datagram, end[2], sv.protocolflags);
2561 SV_Multicast(MULTICAST_PHS_U, start, 0, 0);
2562 }
PF_cl_te_lightning3(void)2563 static void PF_cl_te_lightning3(void)
2564 {
2565 edict_t *ed = G_EDICT(OFS_PARM0);
2566 float *start = G_VECTOR(OFS_PARM1);
2567 float *end = G_VECTOR(OFS_PARM2);
2568
2569 CL_UpdateBeam (Mod_ForName("progs/bolt3.mdl", true), "TE_LIGHTNING3", "TE_LIGHTNING3_END", -NUM_FOR_EDICT(ed), start, end);
2570 }
PF_sv_te_lavasplash(void)2571 static void PF_sv_te_lavasplash(void)
2572 {
2573 float *org = G_VECTOR(OFS_PARM0);
2574 MSG_WriteByte(&sv.datagram, svc_temp_entity);
2575 MSG_WriteByte(&sv.datagram, TE_LAVASPLASH);
2576 MSG_WriteCoord(&sv.datagram, org[0], sv.protocolflags);
2577 MSG_WriteCoord(&sv.datagram, org[1], sv.protocolflags);
2578 MSG_WriteCoord(&sv.datagram, org[2], sv.protocolflags);
2579 SV_Multicast(MULTICAST_PHS_U, org, 0, 0);
2580 }
PF_cl_te_lavasplash(void)2581 static void PF_cl_te_lavasplash(void)
2582 {
2583 }
PF_sv_te_teleport(void)2584 static void PF_sv_te_teleport(void)
2585 {
2586 float *org = G_VECTOR(OFS_PARM0);
2587 MSG_WriteByte(&sv.multicast, svc_temp_entity);
2588 MSG_WriteByte(&sv.multicast, TE_TELEPORT);
2589 MSG_WriteCoord(&sv.multicast, org[0], sv.protocolflags);
2590 MSG_WriteCoord(&sv.multicast, org[1], sv.protocolflags);
2591 MSG_WriteCoord(&sv.multicast, org[2], sv.protocolflags);
2592 SV_Multicast(MULTICAST_PHS_U, org, 0, 0);
2593 }
PF_cl_te_teleport(void)2594 static void PF_cl_te_teleport(void)
2595 {
2596 }
PF_sv_te_explosion2(void)2597 static void PF_sv_te_explosion2(void)
2598 {
2599 float *org = G_VECTOR(OFS_PARM0);
2600 int palstart = G_FLOAT(OFS_PARM1);
2601 int palcount = G_FLOAT(OFS_PARM1);
2602 MSG_WriteByte(&sv.multicast, svc_temp_entity);
2603 MSG_WriteByte(&sv.multicast, TE_EXPLOSION2);
2604 MSG_WriteCoord(&sv.multicast, org[0], sv.protocolflags);
2605 MSG_WriteCoord(&sv.multicast, org[1], sv.protocolflags);
2606 MSG_WriteCoord(&sv.multicast, org[2], sv.protocolflags);
2607 MSG_WriteByte(&sv.multicast, palstart);
2608 MSG_WriteByte(&sv.multicast, palcount);
2609 SV_Multicast(MULTICAST_PHS_U, org, 0, 0);
2610 }
PF_cl_te_explosion2(void)2611 static void PF_cl_te_explosion2(void)
2612 {
2613 float *pos = G_VECTOR(OFS_PARM0);
2614 dlight_t *dl;
2615
2616 dl = CL_AllocDlight (0);
2617 VectorCopy (pos, dl->origin);
2618 dl->radius = 350;
2619 dl->die = cl.time + 0.5;
2620 dl->decay = 300;
2621 S_StartSound (-1, 0, S_PrecacheSound ("weapons/r_exp3.wav"), pos, 1, 1);
2622 }
PF_sv_te_beam(void)2623 static void PF_sv_te_beam(void)
2624 {
2625 edict_t *ed = G_EDICT(OFS_PARM0);
2626 float *start = G_VECTOR(OFS_PARM1);
2627 float *end = G_VECTOR(OFS_PARM2);
2628 MSG_WriteByte(&sv.multicast, svc_temp_entity);
2629 MSG_WriteByte(&sv.multicast, TE_BEAM);
2630 MSG_WriteShort(&sv.multicast, NUM_FOR_EDICT(ed));
2631 MSG_WriteCoord(&sv.multicast, start[0], sv.protocolflags);
2632 MSG_WriteCoord(&sv.multicast, start[1], sv.protocolflags);
2633 MSG_WriteCoord(&sv.multicast, start[2], sv.protocolflags);
2634 MSG_WriteCoord(&sv.multicast, end[0], sv.protocolflags);
2635 MSG_WriteCoord(&sv.multicast, end[1], sv.protocolflags);
2636 MSG_WriteCoord(&sv.multicast, end[2], sv.protocolflags);
2637 SV_Multicast(MULTICAST_PHS_U, start, 0, 0);
2638 }
PF_cl_te_beam(void)2639 static void PF_cl_te_beam(void)
2640 {
2641 edict_t *ed = G_EDICT(OFS_PARM0);
2642 float *start = G_VECTOR(OFS_PARM1);
2643 float *end = G_VECTOR(OFS_PARM2);
2644
2645 CL_UpdateBeam (Mod_ForName("progs/beam.mdl", true), "TE_BEAM", "TE_BEAM_END", -NUM_FOR_EDICT(ed), start, end);
2646 }
2647 #ifdef PSET_SCRIPT
PF_sv_te_particlerain(void)2648 static void PF_sv_te_particlerain(void)
2649 {
2650 float *min = G_VECTOR(OFS_PARM0);
2651 float *max = G_VECTOR(OFS_PARM1);
2652 float *velocity = G_VECTOR(OFS_PARM2);
2653 float count = G_FLOAT(OFS_PARM3);
2654 float colour = G_FLOAT(OFS_PARM4);
2655
2656 if (count < 1)
2657 return;
2658 if (count > 65535)
2659 count = 65535;
2660
2661 MSG_WriteByte(&sv.multicast, svc_temp_entity);
2662 MSG_WriteByte(&sv.multicast, TEDP_PARTICLERAIN);
2663 MSG_WriteCoord(&sv.multicast, min[0], sv.protocolflags);
2664 MSG_WriteCoord(&sv.multicast, min[1], sv.protocolflags);
2665 MSG_WriteCoord(&sv.multicast, min[2], sv.protocolflags);
2666 MSG_WriteCoord(&sv.multicast, max[0], sv.protocolflags);
2667 MSG_WriteCoord(&sv.multicast, max[1], sv.protocolflags);
2668 MSG_WriteCoord(&sv.multicast, max[2], sv.protocolflags);
2669 MSG_WriteCoord(&sv.multicast, velocity[0], sv.protocolflags);
2670 MSG_WriteCoord(&sv.multicast, velocity[1], sv.protocolflags);
2671 MSG_WriteCoord(&sv.multicast, velocity[2], sv.protocolflags);
2672 MSG_WriteShort(&sv.multicast, count);
2673 MSG_WriteByte(&sv.multicast, colour);
2674
2675 SV_Multicast (MULTICAST_ALL_U, NULL, 0, PEXT2_REPLACEMENTDELTAS);
2676 }
PF_sv_te_particlesnow(void)2677 static void PF_sv_te_particlesnow(void)
2678 {
2679 float *min = G_VECTOR(OFS_PARM0);
2680 float *max = G_VECTOR(OFS_PARM1);
2681 float *velocity = G_VECTOR(OFS_PARM2);
2682 float count = G_FLOAT(OFS_PARM3);
2683 float colour = G_FLOAT(OFS_PARM4);
2684
2685 if (count < 1)
2686 return;
2687 if (count > 65535)
2688 count = 65535;
2689
2690 MSG_WriteByte(&sv.multicast, svc_temp_entity);
2691 MSG_WriteByte(&sv.multicast, TEDP_PARTICLESNOW);
2692 MSG_WriteCoord(&sv.multicast, min[0], sv.protocolflags);
2693 MSG_WriteCoord(&sv.multicast, min[1], sv.protocolflags);
2694 MSG_WriteCoord(&sv.multicast, min[2], sv.protocolflags);
2695 MSG_WriteCoord(&sv.multicast, max[0], sv.protocolflags);
2696 MSG_WriteCoord(&sv.multicast, max[1], sv.protocolflags);
2697 MSG_WriteCoord(&sv.multicast, max[2], sv.protocolflags);
2698 MSG_WriteCoord(&sv.multicast, velocity[0], sv.protocolflags);
2699 MSG_WriteCoord(&sv.multicast, velocity[1], sv.protocolflags);
2700 MSG_WriteCoord(&sv.multicast, velocity[2], sv.protocolflags);
2701 MSG_WriteShort(&sv.multicast, count);
2702 MSG_WriteByte(&sv.multicast, colour);
2703
2704 SV_Multicast (MULTICAST_ALL_U, NULL, 0, PEXT2_REPLACEMENTDELTAS);
2705 }
2706 #else
2707 #define PF_sv_te_particlerain PF_void_stub
2708 #define PF_sv_te_particlesnow PF_void_stub
2709 #endif
2710 #define PF_sv_te_bloodshower PF_void_stub
2711 #define PF_sv_te_explosionrgb PF_void_stub
2712 #define PF_sv_te_particlecube PF_void_stub
2713 #define PF_sv_te_spark PF_void_stub
2714 #define PF_sv_te_gunshotquad PF_sv_te_gunshot
2715 #define PF_sv_te_spikequad PF_sv_te_spike
2716 #define PF_sv_te_superspikequad PF_sv_te_superspike
2717 #define PF_sv_te_explosionquad PF_sv_te_explosion
2718 #define PF_sv_te_smallflash PF_void_stub
2719 #define PF_sv_te_customflash PF_void_stub
2720 #define PF_sv_te_plasmaburn PF_sv_te_tarexplosion
2721 #define PF_sv_effect PF_void_stub
2722
PF_sv_pointsound(void)2723 static void PF_sv_pointsound(void)
2724 {
2725 float *origin = G_VECTOR(OFS_PARM0);
2726 const char *sample = G_STRING(OFS_PARM1);
2727 float volume = G_FLOAT(OFS_PARM2);
2728 float attenuation = G_FLOAT(OFS_PARM3);
2729 SV_StartSound (qcvm->edicts, origin, 0, sample, volume, attenuation);
2730 }
PF_cl_pointsound(void)2731 static void PF_cl_pointsound(void)
2732 {
2733 float *origin = G_VECTOR(OFS_PARM0);
2734 const char *sample = G_STRING(OFS_PARM1);
2735 float volume = G_FLOAT(OFS_PARM2);
2736 float attenuation = G_FLOAT(OFS_PARM3);
2737 S_StartSound(0, 0, S_PrecacheSound(sample), origin, volume, attenuation);
2738 }
2739 //file stuff
2740
PF_whichpack(void)2741 static void PF_whichpack(void)
2742 {
2743 const char *fname = G_STRING(OFS_PARM0); //uses native paths, as this isn't actually reading anything.
2744 unsigned int path_id;
2745 if (COM_FileExists(fname, &path_id))
2746 {
2747 //FIXME: quakespasm reports which gamedir the file is in, but paks are hidden.
2748 //I'm too lazy to rewrite COM_FindFile, so I'm just going to hack something small to get the gamedir, just not the paks
2749
2750 searchpath_t *path;
2751 for (path = com_searchpaths; path; path = path->next)
2752 if (!path->pack && path->path_id == path_id)
2753 break; //okay, this one looks like one we can report
2754
2755 //sandbox it by stripping the basedir
2756 fname = path->filename;
2757 if (!strncmp(fname, com_basedir, strlen(com_basedir)))
2758 fname += strlen(com_basedir);
2759 else
2760 fname = "?"; //no idea where this came from. something is screwing with us.
2761 while (*fname == '/' || *fname == '\\')
2762 fname++; //small cleanup, just in case
2763 G_INT(OFS_RETURN) = PR_SetEngineString(fname);
2764 }
2765 else
2766 G_INT(OFS_RETURN) = 0;
2767 }
2768
2769 //string buffers
2770
2771 struct strbuf {
2772 qcvm_t *owningvm;
2773 char **strings;
2774 unsigned int used;
2775 unsigned int allocated;
2776 };
2777
2778 #define BUFSTRBASE 1
2779 #define NUMSTRINGBUFS 64u
2780 static struct strbuf strbuflist[NUMSTRINGBUFS];
2781
PF_buf_shutdown(void)2782 static void PF_buf_shutdown(void)
2783 {
2784 unsigned int i, bufno;
2785
2786 for (bufno = 0; bufno < NUMSTRINGBUFS; bufno++)
2787 {
2788 if (strbuflist[bufno].owningvm != qcvm)
2789 continue;
2790
2791 for (i = 0; i < strbuflist[bufno].used; i++)
2792 Z_Free(strbuflist[bufno].strings[i]);
2793 Z_Free(strbuflist[bufno].strings);
2794
2795 strbuflist[bufno].owningvm = NULL;
2796 strbuflist[bufno].strings = NULL;
2797 strbuflist[bufno].used = 0;
2798 strbuflist[bufno].allocated = 0;
2799 }
2800 }
2801
2802 // #440 float() buf_create (DP_QC_STRINGBUFFERS)
PF_buf_create(void)2803 static void PF_buf_create(void)
2804 {
2805 unsigned int i;
2806
2807 const char *type = ((qcvm->argc>0)?G_STRING(OFS_PARM0):"string");
2808 // unsigned int flags = ((pr_argc>1)?G_FLOAT(OFS_PARM1):1);
2809
2810 if (!q_strcasecmp(type, "string"))
2811 ;
2812 else
2813 {
2814 G_FLOAT(OFS_RETURN) = -1;
2815 return;
2816 }
2817
2818 //flags&1 == saved. apparently.
2819
2820 for (i = 0; i < NUMSTRINGBUFS; i++)
2821 {
2822 if (!strbuflist[i].owningvm)
2823 {
2824 strbuflist[i].owningvm = qcvm;
2825 strbuflist[i].used = 0;
2826 strbuflist[i].allocated = 0;
2827 strbuflist[i].strings = NULL;
2828 G_FLOAT(OFS_RETURN) = i+BUFSTRBASE;
2829 return;
2830 }
2831 }
2832 G_FLOAT(OFS_RETURN) = -1;
2833 }
2834 // #441 void(float bufhandle) buf_del (DP_QC_STRINGBUFFERS)
PF_buf_del(void)2835 static void PF_buf_del(void)
2836 {
2837 unsigned int i;
2838 unsigned int bufno = G_FLOAT(OFS_PARM0)-BUFSTRBASE;
2839
2840 if (bufno >= NUMSTRINGBUFS)
2841 return;
2842 if (!strbuflist[bufno].owningvm)
2843 return;
2844
2845 for (i = 0; i < strbuflist[bufno].used; i++)
2846 {
2847 if (strbuflist[bufno].strings[i])
2848 Z_Free(strbuflist[bufno].strings[i]);
2849 }
2850 Z_Free(strbuflist[bufno].strings);
2851
2852 strbuflist[bufno].strings = NULL;
2853 strbuflist[bufno].used = 0;
2854 strbuflist[bufno].allocated = 0;
2855
2856 strbuflist[bufno].owningvm = NULL;
2857 }
2858 // #442 float(float bufhandle) buf_getsize (DP_QC_STRINGBUFFERS)
PF_buf_getsize(void)2859 static void PF_buf_getsize(void)
2860 {
2861 int bufno = G_FLOAT(OFS_PARM0)-BUFSTRBASE;
2862
2863 if ((unsigned int)bufno >= NUMSTRINGBUFS)
2864 return;
2865 if (!strbuflist[bufno].owningvm)
2866 return;
2867
2868 G_FLOAT(OFS_RETURN) = strbuflist[bufno].used;
2869 }
2870 // #443 void(float bufhandle_from, float bufhandle_to) buf_copy (DP_QC_STRINGBUFFERS)
PF_buf_copy(void)2871 static void PF_buf_copy(void)
2872 {
2873 unsigned int buffrom = G_FLOAT(OFS_PARM0)-BUFSTRBASE;
2874 unsigned int bufto = G_FLOAT(OFS_PARM1)-BUFSTRBASE;
2875 unsigned int i;
2876
2877 if (bufto == buffrom) //err...
2878 return;
2879 if (buffrom >= NUMSTRINGBUFS)
2880 return;
2881 if (!strbuflist[buffrom].owningvm)
2882 return;
2883 if (bufto >= NUMSTRINGBUFS)
2884 return;
2885 if (!strbuflist[bufto].owningvm)
2886 return;
2887
2888 //obliterate any and all existing data.
2889 for (i = 0; i < strbuflist[bufto].used; i++)
2890 if (strbuflist[bufto].strings[i])
2891 Z_Free(strbuflist[bufto].strings[i]);
2892 Z_Free(strbuflist[bufto].strings);
2893
2894 //copy new data over.
2895 strbuflist[bufto].used = strbuflist[bufto].allocated = strbuflist[buffrom].used;
2896 strbuflist[bufto].strings = Z_Malloc(strbuflist[buffrom].used * sizeof(char*));
2897 for (i = 0; i < strbuflist[buffrom].used; i++)
2898 strbuflist[bufto].strings[i] = strbuflist[buffrom].strings[i]?Z_StrDup(strbuflist[buffrom].strings[i]):NULL;
2899 }
2900 static int PF_buf_sort_sortprefixlen;
PF_buf_sort_ascending(const void * a,const void * b)2901 static int PF_buf_sort_ascending(const void *a, const void *b)
2902 {
2903 return strncmp(*(char*const*)a, *(char*const*)b, PF_buf_sort_sortprefixlen);
2904 }
PF_buf_sort_descending(const void * b,const void * a)2905 static int PF_buf_sort_descending(const void *b, const void *a)
2906 {
2907 return strncmp(*(char*const*)a, *(char*const*)b, PF_buf_sort_sortprefixlen);
2908 }
2909 // #444 void(float bufhandle, float sortprefixlen, float backward) buf_sort (DP_QC_STRINGBUFFERS)
PF_buf_sort(void)2910 static void PF_buf_sort(void)
2911 {
2912 int bufno = G_FLOAT(OFS_PARM0)-BUFSTRBASE;
2913 int sortprefixlen = G_FLOAT(OFS_PARM1);
2914 int backwards = G_FLOAT(OFS_PARM2);
2915 unsigned int s,d;
2916 char **strings;
2917
2918 if ((unsigned int)bufno >= NUMSTRINGBUFS)
2919 return;
2920 if (!strbuflist[bufno].owningvm)
2921 return;
2922
2923 if (sortprefixlen <= 0)
2924 sortprefixlen = 0x7fffffff;
2925
2926 //take out the nulls first, to avoid weird/crashy sorting
2927 for (s = 0, d = 0, strings = strbuflist[bufno].strings; s < strbuflist[bufno].used; )
2928 {
2929 if (!strings[s])
2930 {
2931 s++;
2932 continue;
2933 }
2934 strings[d++] = strings[s++];
2935 }
2936 strbuflist[bufno].used = d;
2937
2938 //no nulls now, sort it.
2939 PF_buf_sort_sortprefixlen = sortprefixlen; //eww, a global. burn in hell.
2940 if (backwards) //z first
2941 qsort(strings, strbuflist[bufno].used, sizeof(char*), PF_buf_sort_descending);
2942 else //a first
2943 qsort(strings, strbuflist[bufno].used, sizeof(char*), PF_buf_sort_ascending);
2944 }
2945 // #445 string(float bufhandle, string glue) buf_implode (DP_QC_STRINGBUFFERS)
PF_buf_implode(void)2946 static void PF_buf_implode(void)
2947 {
2948 int bufno = G_FLOAT(OFS_PARM0)-BUFSTRBASE;
2949 const char *glue = G_STRING(OFS_PARM1);
2950 unsigned int gluelen = strlen(glue);
2951 unsigned int retlen, l, i;
2952 char **strings;
2953 char *ret;
2954
2955 if ((unsigned int)bufno >= NUMSTRINGBUFS)
2956 return;
2957 if (!strbuflist[bufno].owningvm)
2958 return;
2959
2960 //count neededlength
2961 strings = strbuflist[bufno].strings;
2962 /*
2963 for (i = 0, retlen = 0; i < strbuflist[bufno].used; i++)
2964 {
2965 if (strings[i])
2966 {
2967 if (retlen)
2968 retlen += gluelen;
2969 retlen += strlen(strings[i]);
2970 }
2971 }
2972 ret = malloc(retlen+1);*/
2973
2974 //generate the output
2975 ret = PR_GetTempString();
2976 for (i = 0, retlen = 0; i < strbuflist[bufno].used; i++)
2977 {
2978 if (strings[i])
2979 {
2980 if (retlen)
2981 {
2982 if (retlen+gluelen+1 > STRINGTEMP_LENGTH)
2983 {
2984 Con_Printf("PF_buf_implode: tempstring overflow\n");
2985 break;
2986 }
2987 memcpy(ret+retlen, glue, gluelen);
2988 retlen += gluelen;
2989 }
2990 l = strlen(strings[i]);
2991 if (retlen+l+1 > STRINGTEMP_LENGTH)
2992 {
2993 Con_Printf("PF_buf_implode: tempstring overflow\n");
2994 break;
2995 }
2996 memcpy(ret+retlen, strings[i], l);
2997 retlen += l;
2998 }
2999 }
3000
3001 //add the null and return
3002 ret[retlen] = 0;
3003 G_INT(OFS_RETURN) = PR_SetEngineString(ret);
3004 }
3005 // #446 string(float bufhandle, float string_index) bufstr_get (DP_QC_STRINGBUFFERS)
PF_bufstr_get(void)3006 static void PF_bufstr_get(void)
3007 {
3008 unsigned int bufno = G_FLOAT(OFS_PARM0)-BUFSTRBASE;
3009 unsigned int index = G_FLOAT(OFS_PARM1);
3010 char *ret;
3011
3012 if (bufno >= NUMSTRINGBUFS)
3013 {
3014 G_INT(OFS_RETURN) = 0;
3015 return;
3016 }
3017 if (!strbuflist[bufno].owningvm)
3018 {
3019 G_INT(OFS_RETURN) = 0;
3020 return;
3021 }
3022
3023 if (index >= strbuflist[bufno].used)
3024 {
3025 G_INT(OFS_RETURN) = 0;
3026 return;
3027 }
3028
3029 if (strbuflist[bufno].strings[index])
3030 {
3031 ret = PR_GetTempString();
3032 q_strlcpy(ret, strbuflist[bufno].strings[index], STRINGTEMP_LENGTH);
3033 G_INT(OFS_RETURN) = PR_SetEngineString(ret);
3034 }
3035 else
3036 G_INT(OFS_RETURN) = 0;
3037 }
3038 // #447 void(float bufhandle, float string_index, string str) bufstr_set (DP_QC_STRINGBUFFERS)
PF_bufstr_set(void)3039 static void PF_bufstr_set(void)
3040 {
3041 unsigned int bufno = G_FLOAT(OFS_PARM0)-BUFSTRBASE;
3042 unsigned int index = G_FLOAT(OFS_PARM1);
3043 const char *string = G_STRING(OFS_PARM2);
3044 unsigned int oldcount;
3045
3046 if ((unsigned int)bufno >= NUMSTRINGBUFS)
3047 return;
3048 if (!strbuflist[bufno].owningvm)
3049 return;
3050
3051 if (index >= strbuflist[bufno].allocated)
3052 {
3053 oldcount = strbuflist[bufno].allocated;
3054 strbuflist[bufno].allocated = (index + 256);
3055 strbuflist[bufno].strings = Z_Realloc(strbuflist[bufno].strings, strbuflist[bufno].allocated*sizeof(char*));
3056 memset(strbuflist[bufno].strings+oldcount, 0, (strbuflist[bufno].allocated - oldcount) * sizeof(char*));
3057 }
3058 if (strbuflist[bufno].strings[index])
3059 Z_Free(strbuflist[bufno].strings[index]);
3060 strbuflist[bufno].strings[index] = Z_Malloc(strlen(string)+1);
3061 strcpy(strbuflist[bufno].strings[index], string);
3062
3063 if (index >= strbuflist[bufno].used)
3064 strbuflist[bufno].used = index+1;
3065 }
3066
PF_bufstr_add_internal(unsigned int bufno,const char * string,int appendonend)3067 static int PF_bufstr_add_internal(unsigned int bufno, const char *string, int appendonend)
3068 {
3069 unsigned int index;
3070 if (appendonend)
3071 {
3072 //add on end
3073 index = strbuflist[bufno].used;
3074 }
3075 else
3076 {
3077 //find a hole
3078 for (index = 0; index < strbuflist[bufno].used; index++)
3079 if (!strbuflist[bufno].strings[index])
3080 break;
3081 }
3082
3083 //expand it if needed
3084 if (index >= strbuflist[bufno].allocated)
3085 {
3086 unsigned int oldcount;
3087 oldcount = strbuflist[bufno].allocated;
3088 strbuflist[bufno].allocated = (index + 256);
3089 strbuflist[bufno].strings = Z_Realloc(strbuflist[bufno].strings, strbuflist[bufno].allocated*sizeof(char*));
3090 memset(strbuflist[bufno].strings+oldcount, 0, (strbuflist[bufno].allocated - oldcount) * sizeof(char*));
3091 }
3092
3093 //add in the new string.
3094 if (strbuflist[bufno].strings[index])
3095 Z_Free(strbuflist[bufno].strings[index]);
3096 strbuflist[bufno].strings[index] = Z_Malloc(strlen(string)+1);
3097 strcpy(strbuflist[bufno].strings[index], string);
3098
3099 if (index >= strbuflist[bufno].used)
3100 strbuflist[bufno].used = index+1;
3101
3102 return index;
3103 }
3104
3105 // #448 float(float bufhandle, string str, float order) bufstr_add (DP_QC_STRINGBUFFERS)
PF_bufstr_add(void)3106 static void PF_bufstr_add(void)
3107 {
3108 size_t bufno = G_FLOAT(OFS_PARM0)-BUFSTRBASE;
3109 const char *string = G_STRING(OFS_PARM1);
3110 qboolean ordered = G_FLOAT(OFS_PARM2);
3111
3112 if ((unsigned int)bufno >= NUMSTRINGBUFS)
3113 return;
3114 if (!strbuflist[bufno].owningvm)
3115 return;
3116
3117 G_FLOAT(OFS_RETURN) = PF_bufstr_add_internal(bufno, string, ordered);
3118 }
3119 // #449 void(float bufhandle, float string_index) bufstr_free (DP_QC_STRINGBUFFERS)
PF_bufstr_free(void)3120 static void PF_bufstr_free(void)
3121 {
3122 size_t bufno = G_FLOAT(OFS_PARM0)-BUFSTRBASE;
3123 size_t index = G_FLOAT(OFS_PARM1);
3124
3125 if ((unsigned int)bufno >= NUMSTRINGBUFS)
3126 return;
3127 if (!strbuflist[bufno].owningvm)
3128 return;
3129
3130 if (index >= strbuflist[bufno].used)
3131 return; //not valid anyway.
3132
3133 if (strbuflist[bufno].strings[index])
3134 Z_Free(strbuflist[bufno].strings[index]);
3135 strbuflist[bufno].strings[index] = NULL;
3136 }
3137
PF_buf_cvarlist(void)3138 static void PF_buf_cvarlist(void)
3139 {
3140 size_t bufno = G_FLOAT(OFS_PARM0)-BUFSTRBASE;
3141 const char *pattern = G_STRING(OFS_PARM1);
3142 const char *antipattern = G_STRING(OFS_PARM2);
3143 unsigned int i;
3144 cvar_t *var;
3145 int plen = strlen(pattern), alen = strlen(antipattern);
3146 qboolean pwc = strchr(pattern, '*')||strchr(pattern, '?'),
3147 awc = strchr(antipattern, '*')||strchr(antipattern, '?');
3148
3149 if ((unsigned int)bufno >= NUMSTRINGBUFS)
3150 return;
3151 if (!strbuflist[bufno].owningvm)
3152 return;
3153
3154 //obliterate any and all existing data.
3155 for (i = 0; i < strbuflist[bufno].used; i++)
3156 if (strbuflist[bufno].strings[i])
3157 Z_Free(strbuflist[bufno].strings[i]);
3158 if (strbuflist[bufno].strings)
3159 Z_Free(strbuflist[bufno].strings);
3160 strbuflist[bufno].used = strbuflist[bufno].allocated = 0;
3161
3162 for (var=Cvar_FindVarAfter ("", CVAR_NONE) ; var ; var=var->next)
3163 {
3164 if (plen && (pwc?!wildcmp(pattern, var->name):strncmp(var->name, pattern, plen)))
3165 continue;
3166 if (alen && (awc?wildcmp(antipattern, var->name):!strncmp(var->name, antipattern, alen)))
3167 continue;
3168
3169 PF_bufstr_add_internal(bufno, var->name, true);
3170 }
3171
3172 qsort(strbuflist[bufno].strings, strbuflist[bufno].used, sizeof(char*), PF_buf_sort_ascending);
3173 }
3174
3175 //entity stuff
PF_WasFreed(void)3176 static void PF_WasFreed(void)
3177 {
3178 edict_t *ed = G_EDICT(OFS_PARM0);
3179 G_FLOAT(OFS_RETURN) = ed->free;
3180 }
PF_copyentity(void)3181 static void PF_copyentity(void)
3182 {
3183 edict_t *src = G_EDICT(OFS_PARM0);
3184 edict_t *dst = (qcvm->argc<2)?ED_Alloc():G_EDICT(OFS_PARM1);
3185 if (src->free || dst->free)
3186 Con_Printf("PF_copyentity: entity is free\n");
3187 memcpy(&dst->v, &src->v, qcvm->edict_size - sizeof(entvars_t));
3188 dst->alpha = src->alpha;
3189 dst->sendinterval = src->sendinterval;
3190 SV_LinkEdict(dst, false);
3191
3192 G_INT(OFS_RETURN) = EDICT_TO_PROG(dst);
3193 }
PF_edict_for_num(void)3194 static void PF_edict_for_num(void)
3195 {
3196 G_INT(OFS_RETURN) = EDICT_TO_PROG(EDICT_NUM(G_FLOAT(OFS_PARM0)));
3197 }
PF_num_for_edict(void)3198 static void PF_num_for_edict(void)
3199 {
3200 G_FLOAT(OFS_RETURN) = G_EDICTNUM(OFS_PARM0);
3201 }
PF_findchain(void)3202 static void PF_findchain(void)
3203 {
3204 edict_t *ent, *chain;
3205 int i, f;
3206 const char *s, *t;
3207 int cfld;
3208
3209 chain = (edict_t *)qcvm->edicts;
3210
3211 ent = NEXT_EDICT(qcvm->edicts);
3212 f = G_INT(OFS_PARM0);
3213 s = G_STRING(OFS_PARM1);
3214 if (qcvm->argc > 2)
3215 cfld = G_INT(OFS_PARM2);
3216 else
3217 cfld = &ent->v.chain - (int*)&ent->v;
3218
3219 for (i = 1; i < qcvm->num_edicts; i++, ent = NEXT_EDICT(ent))
3220 {
3221 if (ent->free)
3222 continue;
3223 t = E_STRING(ent,f);
3224 if (strcmp(s, t))
3225 continue;
3226 ((int*)&ent->v)[cfld] = EDICT_TO_PROG(chain);
3227 chain = ent;
3228 }
3229
3230 RETURN_EDICT(chain);
3231 }
PF_findfloat(void)3232 static void PF_findfloat(void)
3233 {
3234 int e;
3235 int f;
3236 float s, t;
3237 edict_t *ed;
3238
3239 e = G_EDICTNUM(OFS_PARM0);
3240 f = G_INT(OFS_PARM1);
3241 s = G_FLOAT(OFS_PARM2);
3242
3243 for (e++ ; e < qcvm->num_edicts ; e++)
3244 {
3245 ed = EDICT_NUM(e);
3246 if (ed->free)
3247 continue;
3248 t = E_FLOAT(ed,f);
3249 if (t == s)
3250 {
3251 RETURN_EDICT(ed);
3252 return;
3253 }
3254 }
3255
3256 RETURN_EDICT(qcvm->edicts);
3257 }
PF_findchainfloat(void)3258 static void PF_findchainfloat(void)
3259 {
3260 edict_t *ent, *chain;
3261 int i, f;
3262 float s, t;
3263 int cfld;
3264
3265 chain = (edict_t *)qcvm->edicts;
3266
3267 ent = NEXT_EDICT(qcvm->edicts);
3268 f = G_INT(OFS_PARM0);
3269 s = G_FLOAT(OFS_PARM1);
3270 if (qcvm->argc > 2)
3271 cfld = G_INT(OFS_PARM2);
3272 else
3273 cfld = &ent->v.chain - (int*)&ent->v;
3274
3275 for (i = 1; i < qcvm->num_edicts; i++, ent = NEXT_EDICT(ent))
3276 {
3277 if (ent->free)
3278 continue;
3279 t = E_FLOAT(ent,f);
3280 if (s != t)
3281 continue;
3282 ((int*)&ent->v)[cfld] = EDICT_TO_PROG(chain);
3283 chain = ent;
3284 }
3285
3286 RETURN_EDICT(chain);
3287 }
PF_findflags(void)3288 static void PF_findflags(void)
3289 {
3290 int e;
3291 int f;
3292 int s, t;
3293 edict_t *ed;
3294
3295 e = G_EDICTNUM(OFS_PARM0);
3296 f = G_INT(OFS_PARM1);
3297 s = G_FLOAT(OFS_PARM2);
3298
3299 for (e++ ; e < qcvm->num_edicts ; e++)
3300 {
3301 ed = EDICT_NUM(e);
3302 if (ed->free)
3303 continue;
3304 t = E_FLOAT(ed,f);
3305 if (t & s)
3306 {
3307 RETURN_EDICT(ed);
3308 return;
3309 }
3310 }
3311
3312 RETURN_EDICT(qcvm->edicts);
3313 }
PF_findchainflags(void)3314 static void PF_findchainflags(void)
3315 {
3316 edict_t *ent, *chain;
3317 int i, f;
3318 int s, t;
3319 int cfld;
3320
3321 chain = (edict_t *)qcvm->edicts;
3322 ent = NEXT_EDICT(qcvm->edicts);
3323
3324 f = G_INT(OFS_PARM0);
3325 s = G_FLOAT(OFS_PARM1);
3326 if (qcvm->argc > 2)
3327 cfld = G_INT(OFS_PARM2);
3328 else
3329 cfld = &ent->v.chain - (int*)&ent->v;
3330
3331 for (i = 1; i < qcvm->num_edicts; i++, ent = NEXT_EDICT(ent))
3332 {
3333 if (ent->free)
3334 continue;
3335 t = E_FLOAT(ent,f);
3336 if (!(s & t))
3337 continue;
3338 ((int*)&ent->v)[cfld] = EDICT_TO_PROG(chain);
3339 chain = ent;
3340 }
3341
3342 RETURN_EDICT(chain);
3343 }
PF_numentityfields(void)3344 static void PF_numentityfields(void)
3345 {
3346 G_FLOAT(OFS_RETURN) = qcvm->progs->numfielddefs;
3347 }
PF_findentityfield(void)3348 static void PF_findentityfield(void)
3349 {
3350 ddef_t *fld = ED_FindField(G_STRING(OFS_PARM0));
3351 if (fld)
3352 G_FLOAT(OFS_RETURN) = fld - qcvm->fielddefs;
3353 else
3354 G_FLOAT(OFS_RETURN) = 0; //the first field is meant to be some dummy placeholder. or it could be modelindex...
3355 }
PF_entityfieldref(void)3356 static void PF_entityfieldref(void)
3357 {
3358 unsigned int fldidx = G_FLOAT(OFS_PARM0);
3359 if (fldidx >= (unsigned int)qcvm->progs->numfielddefs)
3360 G_INT(OFS_RETURN) = 0;
3361 else
3362 G_INT(OFS_RETURN) = qcvm->fielddefs[fldidx].ofs;
3363 }
PF_entityfieldname(void)3364 static void PF_entityfieldname(void)
3365 {
3366 unsigned int fldidx = G_FLOAT(OFS_PARM0);
3367 if (fldidx < (unsigned int)qcvm->progs->numfielddefs)
3368 G_INT(OFS_RETURN) = qcvm->fielddefs[fldidx].s_name;
3369 else
3370 G_INT(OFS_RETURN) = 0;
3371 }
PF_entityfieldtype(void)3372 static void PF_entityfieldtype(void)
3373 {
3374 unsigned int fldidx = G_FLOAT(OFS_PARM0);
3375 if (fldidx >= (unsigned int)qcvm->progs->numfielddefs)
3376 G_FLOAT(OFS_RETURN) = ev_void;
3377 else
3378 G_FLOAT(OFS_RETURN) = qcvm->fielddefs[fldidx].type;
3379 }
PF_getentfldstr(void)3380 static void PF_getentfldstr(void)
3381 {
3382 unsigned int fldidx = G_FLOAT(OFS_PARM0);
3383 edict_t *ent = G_EDICT(OFS_PARM1);
3384 if (fldidx < (unsigned int)qcvm->progs->numfielddefs)
3385 {
3386 char *ret = PR_GetTempString();
3387 const char *val = PR_UglyValueString (qcvm->fielddefs[fldidx].type, (eval_t*)((float*)&ent->v + qcvm->fielddefs[fldidx].ofs));
3388 q_strlcpy(ret, val, STRINGTEMP_LENGTH);
3389 G_INT(OFS_RETURN) = PR_SetEngineString(ret);
3390 }
3391 else
3392 G_INT(OFS_RETURN) = 0;
3393 }
PF_putentfldstr(void)3394 static void PF_putentfldstr(void)
3395 {
3396 unsigned int fldidx = G_FLOAT(OFS_PARM0);
3397 edict_t *ent = G_EDICT(OFS_PARM1);
3398 const char *value = G_STRING(OFS_PARM2);
3399 if (fldidx < (unsigned int)qcvm->progs->numfielddefs)
3400 G_FLOAT(OFS_RETURN) = ED_ParseEpair ((void *)&ent->v, qcvm->fielddefs+fldidx, value, true);
3401 else
3402 G_FLOAT(OFS_RETURN) = false;
3403 }
3404
PF_parseentitydata(void)3405 static void PF_parseentitydata(void)
3406 {
3407 edict_t *ed = G_EDICT(OFS_PARM0);
3408 const char *data = G_STRING(OFS_PARM1), *end;
3409 unsigned int offset = (qcvm->argc>2)?G_FLOAT(OFS_PARM2):0;
3410 if (offset)
3411 {
3412 unsigned int len = strlen(data);
3413 if (offset > len)
3414 offset = len;
3415 }
3416 if (!data[offset])
3417 G_FLOAT(OFS_RETURN) = 0;
3418 else
3419 {
3420 end = COM_Parse(data+offset);
3421 if (!strcmp(com_token, "{"))
3422 {
3423 end = ED_ParseEdict(end, ed);
3424 G_FLOAT(OFS_RETURN) = end - data;
3425 }
3426 else
3427 G_FLOAT(OFS_RETURN) = 0; //doesn't look like an ent to me.
3428 }
3429 }
3430
PF_callfunction(void)3431 static void PF_callfunction(void)
3432 {
3433 dfunction_t *fnc;
3434 const char *fname;
3435 if (!qcvm->argc)
3436 return;
3437 qcvm->argc--;
3438 fname = G_STRING(OFS_PARM0 + qcvm->argc*3);
3439 fnc = ED_FindFunction(fname);
3440 if (fnc && fnc->first_statement > 0)
3441 {
3442 PR_ExecuteProgram(fnc - qcvm->functions);
3443 }
3444 }
PF_isfunction(void)3445 static void PF_isfunction(void)
3446 {
3447 const char *fname = G_STRING(OFS_PARM0);
3448 G_FLOAT(OFS_RETURN) = ED_FindFunction(fname)?true:false;
3449 }
3450
3451 //other stuff
PF_gettime(void)3452 static void PF_gettime (void)
3453 {
3454 int timer = (qcvm->argc > 0)?G_FLOAT(OFS_PARM0):0;
3455 switch(timer)
3456 {
3457 default:
3458 Con_DPrintf("PF_gettime: unsupported timer %i\n", timer);
3459 case 0: //cached time at start of frame
3460 G_FLOAT(OFS_RETURN) = realtime;
3461 break;
3462 case 1: //actual time
3463 G_FLOAT(OFS_RETURN) = Sys_DoubleTime();
3464 break;
3465 //case 2: //highres.. looks like time into the frame. no idea
3466 //case 3: //uptime
3467 //case 4: //cd track
3468 //case 5: //client simtime
3469 }
3470 }
3471 #define STRINGIFY2(x) #x
3472 #define STRINGIFY(x) STRINGIFY2(x)
PF_infokey_internal(qboolean returnfloat)3473 static void PF_infokey_internal(qboolean returnfloat)
3474 {
3475 unsigned int ent = G_EDICTNUM(OFS_PARM0);
3476 const char *key = G_STRING(OFS_PARM1);
3477 const char *r;
3478 char buf[1024];
3479 if (!ent)
3480 {
3481 if (!strcmp(key, "*version"))
3482 {
3483 q_snprintf(buf, sizeof(buf), ENGINE_NAME_AND_VER);
3484 r = buf;
3485 }
3486 else
3487 r = NULL;
3488 }
3489 else if (ent <= (unsigned int)svs.maxclients && svs.clients[ent-1].active)
3490 {
3491 client_t *client = &svs.clients[ent-1];
3492 r = buf;
3493 if (!strcmp(key, "ip"))
3494 r = NET_QSocketGetTrueAddressString(client->netconnection);
3495 else if (!strcmp(key, "ping"))
3496 {
3497 float total = 0;
3498 unsigned int j;
3499 for (j = 0; j < NUM_PING_TIMES; j++)
3500 total+=client->ping_times[j];
3501 total /= NUM_PING_TIMES;
3502 q_snprintf(buf, sizeof(buf), "%f", total);
3503 }
3504 else if (!strcmp(key, "protocol"))
3505 {
3506 switch(sv.protocol)
3507 {
3508 case PROTOCOL_NETQUAKE:
3509 r = "quake";
3510 break;
3511 case PROTOCOL_FITZQUAKE:
3512 r = "fitz666";
3513 break;
3514 case PROTOCOL_RMQ:
3515 r = "rmq999";
3516 break;
3517 default:
3518 r = "";
3519 break;
3520 }
3521 }
3522 else if (!strcmp(key, "name"))
3523 r = client->name;
3524 else if (!strcmp(key, "topcolor"))
3525 q_snprintf(buf, sizeof(buf), "%u", client->colors>>4);
3526 else if (!strcmp(key, "bottomcolor"))
3527 q_snprintf(buf, sizeof(buf), "%u", client->colors&15);
3528 else if (!strcmp(key, "team")) //nq doesn't really do teams. qw does though. yay compat?
3529 q_snprintf(buf, sizeof(buf), "t%u", (client->colors&15)+1);
3530 else if (!strcmp(key, "*VIP"))
3531 r = "";
3532 else if (!strcmp(key, "*spectator"))
3533 r = "";
3534 else if (!strcmp(key, "csqcactive"))
3535 r = "0";
3536 else
3537 r = NULL;
3538 }
3539 else r = NULL;
3540
3541 if (returnfloat)
3542 {
3543 if (r)
3544 G_FLOAT(OFS_RETURN) = atof(r);
3545 else
3546 G_FLOAT(OFS_RETURN) = 0;
3547 }
3548 else
3549 {
3550 if (r)
3551 {
3552 char *temp = PR_GetTempString();
3553 q_strlcpy(temp, r, STRINGTEMP_LENGTH);
3554 G_INT(OFS_RETURN) = PR_SetEngineString(temp);
3555 }
3556 else
3557 G_INT(OFS_RETURN) = 0;
3558 }
3559 }
PF_infokey_s(void)3560 static void PF_infokey_s(void)
3561 {
3562 PF_infokey_internal(false);
3563 }
PF_infokey_f(void)3564 static void PF_infokey_f(void)
3565 {
3566 PF_infokey_internal(true);
3567 }
3568
PF_multicast_internal(qboolean reliable,byte * pvs,unsigned int requireext2)3569 static void PF_multicast_internal(qboolean reliable, byte *pvs, unsigned int requireext2)
3570 {
3571 unsigned int i;
3572 int cluster;
3573 mleaf_t *playerleaf;
3574 if (!pvs)
3575 {
3576 if (!requireext2)
3577 SZ_Write((reliable?&sv.reliable_datagram:&sv.datagram), sv.multicast.data, sv.multicast.cursize);
3578 else
3579 {
3580 for (i = 0; i < (unsigned int)svs.maxclients; i++)
3581 {
3582 if (!svs.clients[i].active)
3583 continue;
3584 if (!(svs.clients[i].protocol_pext2 & requireext2))
3585 continue;
3586 SZ_Write((reliable?&svs.clients[i].message:&svs.clients[i].datagram), sv.multicast.data, sv.multicast.cursize);
3587 }
3588 }
3589 }
3590 else
3591 {
3592 for (i = 0; i < (unsigned int)svs.maxclients; i++)
3593 {
3594 if (!svs.clients[i].active)
3595 continue;
3596
3597 if (requireext2 && !(svs.clients[i].protocol_pext2 & requireext2))
3598 continue;
3599
3600 //figure out which cluster (read: pvs index) to use.
3601 playerleaf = Mod_PointInLeaf(svs.clients[i].edict->v.origin, qcvm->worldmodel);
3602 cluster = playerleaf - qcvm->worldmodel->leafs;
3603 cluster--; //pvs is 1-based, leaf 0 is discarded.
3604 if (cluster < 0 || (pvs[cluster>>3] & (1<<(cluster&7))))
3605 {
3606 //they can see it. add it in to whichever buffer is appropriate.
3607 if (reliable)
3608 SZ_Write(&svs.clients[i].message, sv.multicast.data, sv.multicast.cursize);
3609 else
3610 SZ_Write(&svs.clients[i].datagram, sv.multicast.data, sv.multicast.cursize);
3611 }
3612 }
3613 }
3614 }
3615 //FIXME: shouldn't really be using pext2, but we don't track the earlier extensions, and it should be safe enough.
SV_Multicast(multicast_t to,float * org,int msg_entity,unsigned int requireext2)3616 static void SV_Multicast(multicast_t to, float *org, int msg_entity, unsigned int requireext2)
3617 {
3618 unsigned int i;
3619
3620 if (to == MULTICAST_INIT && sv.state != ss_loading)
3621 {
3622 SZ_Write (&sv.signon, sv.multicast.data, sv.multicast.cursize);
3623 to = MULTICAST_ALL_R; //and send to players that are already on
3624 }
3625
3626 switch(to)
3627 {
3628 case MULTICAST_INIT:
3629 SZ_Write (&sv.signon, sv.multicast.data, sv.multicast.cursize);
3630 break;
3631 case MULTICAST_ALL_R:
3632 case MULTICAST_ALL_U:
3633 PF_multicast_internal(to==MULTICAST_ALL_R, NULL, requireext2);
3634 break;
3635 case MULTICAST_PHS_R:
3636 case MULTICAST_PHS_U:
3637 //we don't support phs, that would require lots of pvs decompression+merging stuff, and many q1bsps have a LOT of leafs.
3638 PF_multicast_internal(to==MULTICAST_PHS_R, NULL/*Mod_LeafPHS(Mod_PointInLeaf(org, qcvm->worldmodel), qcvm->worldmodel)*/, requireext2);
3639 break;
3640 case MULTICAST_PVS_R:
3641 case MULTICAST_PVS_U:
3642 PF_multicast_internal(to==MULTICAST_PVS_R, Mod_LeafPVS(Mod_PointInLeaf(org, qcvm->worldmodel), qcvm->worldmodel), requireext2);
3643 break;
3644 case MULTICAST_ONE_R:
3645 case MULTICAST_ONE_U:
3646 i = msg_entity-1;
3647 if (i >= (unsigned int)svs.maxclients)
3648 break;
3649 //a unicast, which ignores pvs.
3650 //(unlike vanilla this allows unicast unreliables, so woo)
3651 if (svs.clients[i].active)
3652 {
3653 SZ_Write(((to==MULTICAST_ONE_R)?&svs.clients[i].message:&svs.clients[i].datagram), sv.multicast.data, sv.multicast.cursize);
3654 }
3655 break;
3656 default:
3657 break;
3658 }
3659 SZ_Clear(&sv.multicast);
3660 }
PF_multicast(void)3661 static void PF_multicast(void)
3662 {
3663 float *org = G_VECTOR(OFS_PARM0);
3664 multicast_t to = G_FLOAT(OFS_PARM1);
3665 SV_Multicast(to, org, NUM_FOR_EDICT(PROG_TO_EDICT(pr_global_struct->msg_entity)), 0);
3666 }
PF_randomvector(void)3667 static void PF_randomvector(void)
3668 {
3669 vec3_t temp;
3670 do
3671 {
3672 temp[0] = (rand() & 32767) * (2.0 / 32767.0) - 1.0;
3673 temp[1] = (rand() & 32767) * (2.0 / 32767.0) - 1.0;
3674 temp[2] = (rand() & 32767) * (2.0 / 32767.0) - 1.0;
3675 } while (DotProduct(temp, temp) >= 1);
3676 VectorCopy (temp, G_VECTOR(OFS_RETURN));
3677 }
3678 static void PF_checkextension(void);
3679 static void PF_checkbuiltin(void);
3680 static void PF_builtinsupported(void);
3681
PF_uri_escape(void)3682 static void PF_uri_escape(void)
3683 {
3684 static const char *hex = "0123456789ABCDEF";
3685
3686 char *result = PR_GetTempString();
3687 char *o = result;
3688 const unsigned char *s = (const unsigned char*)G_STRING(OFS_PARM0);
3689 *result = 0;
3690 while (*s && o < result+STRINGTEMP_LENGTH-4)
3691 {
3692 if ((*s >= 'a' && *s <= 'z') || (*s >= 'A' && *s <= 'Z') || (*s >= '0' && *s <= '9')
3693 || *s == '.' || *s == '-' || *s == '_')
3694 *o++ = *s++;
3695 else
3696 {
3697 *o++ = '%';
3698 *o++ = hex[*s>>4];
3699 *o++ = hex[*s&0xf];
3700 s++;
3701 }
3702 }
3703 *o = 0;
3704 G_INT(OFS_RETURN) = PR_SetEngineString(result);
3705 }
PF_uri_unescape(void)3706 static void PF_uri_unescape(void)
3707 {
3708 const char *s = G_STRING(OFS_PARM0), *i;
3709 char *resultbuf = PR_GetTempString(), *o;
3710 unsigned char hex;
3711 i = s; o = resultbuf;
3712 while (*i && o < resultbuf+STRINGTEMP_LENGTH-2)
3713 {
3714 if (*i == '%')
3715 {
3716 hex = 0;
3717 if (i[1] >= 'A' && i[1] <= 'F')
3718 hex += i[1]-'A'+10;
3719 else if (i[1] >= 'a' && i[1] <= 'f')
3720 hex += i[1]-'a'+10;
3721 else if (i[1] >= '0' && i[1] <= '9')
3722 hex += i[1]-'0';
3723 else
3724 {
3725 *o++ = *i++;
3726 continue;
3727 }
3728 hex <<= 4;
3729 if (i[2] >= 'A' && i[2] <= 'F')
3730 hex += i[2]-'A'+10;
3731 else if (i[2] >= 'a' && i[2] <= 'f')
3732 hex += i[2]-'a'+10;
3733 else if (i[2] >= '0' && i[2] <= '9')
3734 hex += i[2]-'0';
3735 else
3736 {
3737 *o++ = *i++;
3738 continue;
3739 }
3740 *o++ = hex;
3741 i += 3;
3742 }
3743 else
3744 *o++ = *i++;
3745 }
3746 *o = 0;
3747 G_INT(OFS_RETURN) = PR_SetEngineString(resultbuf);
3748 }
PF_crc16(void)3749 static void PF_crc16(void)
3750 {
3751 qboolean insens = G_FLOAT(OFS_PARM0);
3752 const char *str = PF_VarString(1);
3753 size_t len = strlen(str);
3754
3755 if (insens)
3756 {
3757 unsigned short crc;
3758
3759 CRC_Init (&crc);
3760 while (len--)
3761 CRC_ProcessByte(&crc, q_tolower(*str++));
3762 G_FLOAT(OFS_RETURN) = crc;
3763 }
3764 else
3765 G_FLOAT(OFS_RETURN) = CRC_Block((const byte*)str, len);
3766 }
PF_digest_hex(void)3767 static void PF_digest_hex(void)
3768 {
3769 const char *hashtype = G_STRING(OFS_PARM0);
3770 const byte *data = (const byte*)PF_VarString(1);
3771 size_t len = strlen((const char*)data);
3772 static const char *hex = "0123456789ABCDEF";
3773 char *resultbuf;
3774 byte hashdata[20];
3775
3776 if (!strcmp(hashtype, "CRC16"))
3777 {
3778 int crc = CRC_Block((const byte*)data, len);
3779 hashdata[0] = crc&0xff;
3780 hashdata[1] = (crc>>8)&0xff;
3781 len = 2;
3782 }
3783 else if (!strcmp(hashtype, "MD4"))
3784 {
3785 Com_BlockFullChecksum((void*)data, len, hashdata);
3786 len = 16;
3787 }
3788 else
3789 {
3790 Con_Printf("PF_digest_hex: Unsupported digest %s\n", hashtype);
3791 G_INT(OFS_RETURN) = 0;
3792 return;
3793 }
3794
3795 resultbuf = PR_GetTempString();
3796 G_INT(OFS_RETURN) = PR_SetEngineString(resultbuf);
3797 data = hashdata;
3798 while (len --> 0)
3799 {
3800 *resultbuf++ = hex[*data>>4];
3801 *resultbuf++ = hex[*data&0xf];
3802 data++;
3803 }
3804 *resultbuf = 0;
3805 }
3806
PF_strlennocol(void)3807 static void PF_strlennocol(void)
3808 {
3809 int r = 0;
3810 struct markup_s mu;
3811
3812 PR_Markup_Begin(&mu, G_STRING(OFS_PARM0), vec3_origin, 1);
3813 while (PR_Markup_Parse(&mu))
3814 r++;
3815 G_FLOAT(OFS_RETURN) = r;
3816 }
PF_strdecolorize(void)3817 static void PF_strdecolorize(void)
3818 {
3819 int l, c;
3820 char *r = PR_GetTempString();
3821 struct markup_s mu;
3822
3823 PR_Markup_Begin(&mu, G_STRING(OFS_PARM0), vec3_origin, 1);
3824 for (l = 0; l < STRINGTEMP_LENGTH-1; l++)
3825 {
3826 c = PR_Markup_Parse(&mu);
3827 if (!c)
3828 break;
3829 r[l] = c;
3830 }
3831 r[l] = 0;
3832
3833 G_INT(OFS_RETURN) = PR_SetEngineString(r);
3834 }
PF_setattachment(void)3835 static void PF_setattachment(void)
3836 {
3837 edict_t *ent = G_EDICT(OFS_PARM0);
3838 edict_t *tagent = G_EDICT(OFS_PARM1);
3839 const char *tagname = G_STRING(OFS_PARM2);
3840 eval_t *val;
3841
3842 if (*tagname)
3843 {
3844 //we don't support md3s, or any skeletal formats, so all tag names are logically invalid for us.
3845 Con_DWarning("PF_setattachment: tag %s not found\n", tagname);
3846 }
3847
3848 if ((val = GetEdictFieldValue(ent, qcvm->extfields.tag_entity)))
3849 val->edict = EDICT_TO_PROG(tagent);
3850 if ((val = GetEdictFieldValue(ent, qcvm->extfields.tag_index)))
3851 val->_float = 0;
3852 }
3853
PR_CustomStat(int idx,int type)3854 static struct svcustomstat_s *PR_CustomStat(int idx, int type)
3855 {
3856 size_t i;
3857 if (idx < 0 || idx >= MAX_CL_STATS)
3858 return NULL;
3859 switch(type)
3860 {
3861 case ev_ext_integer:
3862 case ev_float:
3863 case ev_vector:
3864 case ev_entity:
3865 break;
3866 default:
3867 return NULL;
3868 }
3869
3870 for (i = 0; i < sv.numcustomstats; i++)
3871 {
3872 if (sv.customstats[i].idx == idx && (sv.customstats[i].type==ev_string) == (type==ev_string))
3873 break;
3874 }
3875 if (i == sv.numcustomstats)
3876 sv.numcustomstats++;
3877 sv.customstats[i].idx = idx;
3878 sv.customstats[i].type = type;
3879 sv.customstats[i].fld = 0;
3880 sv.customstats[i].ptr = NULL;
3881 return &sv.customstats[i];
3882 }
PF_clientstat(void)3883 static void PF_clientstat(void)
3884 {
3885 int idx = G_FLOAT(OFS_PARM0);
3886 int type = G_FLOAT(OFS_PARM1);
3887 int fldofs = G_INT(OFS_PARM2);
3888 struct svcustomstat_s *stat = PR_CustomStat(idx, type);
3889 if (!stat)
3890 return;
3891 stat->fld = fldofs;
3892 }
PF_isbackbuffered(void)3893 static void PF_isbackbuffered(void)
3894 {
3895 unsigned int plnum = G_EDICTNUM(OFS_PARM0)-1;
3896 G_FLOAT(OFS_RETURN) = true; //assume the connection is clogged.
3897 if (plnum > (unsigned int)svs.maxclients)
3898 return; //make error?
3899 if (!svs.clients[plnum].active)
3900 return; //empty slot
3901 if (svs.clients[plnum].message.cursize > DATAGRAM_MTU)
3902 return;
3903 G_FLOAT(OFS_RETURN) = false; //okay to spam with more reliables.
3904 }
3905
3906 #ifdef PSET_SCRIPT
PF_SV_ForceParticlePrecache(const char * s)3907 int PF_SV_ForceParticlePrecache(const char *s)
3908 {
3909 unsigned int i;
3910 for (i = 1; i < MAX_PARTICLETYPES; i++)
3911 {
3912 if (!sv.particle_precache[i])
3913 {
3914 if (sv.state != ss_loading)
3915 {
3916 MSG_WriteByte(&sv.multicast, svcdp_precache);
3917 MSG_WriteShort(&sv.multicast, i|0x4000);
3918 MSG_WriteString(&sv.multicast, s);
3919 SV_Multicast(MULTICAST_ALL_R, NULL, 0, PEXT2_REPLACEMENTDELTAS); //FIXME
3920 }
3921
3922 sv.particle_precache[i] = strcpy(Hunk_Alloc(strlen(s)+1), s); //weirdness to avoid issues with tempstrings
3923 return i;
3924 }
3925 if (!strcmp(sv.particle_precache[i], s))
3926 return i;
3927 }
3928 return 0;
3929 }
PF_sv_particleeffectnum(void)3930 static void PF_sv_particleeffectnum(void)
3931 {
3932 const char *s;
3933 unsigned int i;
3934 extern cvar_t r_particledesc;
3935
3936 s = G_STRING(OFS_PARM0);
3937 G_FLOAT(OFS_RETURN) = 0;
3938 // PR_CheckEmptyString (s);
3939
3940 if (!*s)
3941 return;
3942
3943 if (!sv.particle_precache[1] && (!strncmp(s, "effectinfo.", 11) || strstr(r_particledesc.string, "effectinfo")))
3944 COM_Effectinfo_Enumerate(PF_SV_ForceParticlePrecache);
3945
3946 for (i = 1; i < MAX_PARTICLETYPES; i++)
3947 {
3948 if (!sv.particle_precache[i])
3949 {
3950 if (sv.state != ss_loading)
3951 {
3952 if (pr_ext_warned_particleeffectnum++ < 3)
3953 Con_Warning ("PF_sv_particleeffectnum(%s): Precache should only be done in spawn functions\n", s);
3954
3955 MSG_WriteByte(&sv.multicast, svcdp_precache);
3956 MSG_WriteShort(&sv.multicast, i|0x4000);
3957 MSG_WriteString(&sv.multicast, s);
3958 SV_Multicast(MULTICAST_ALL_R, NULL, 0, PEXT2_REPLACEMENTDELTAS);
3959 }
3960
3961 sv.particle_precache[i] = strcpy(Hunk_Alloc(strlen(s)+1), s); //weirdness to avoid issues with tempstrings
3962 G_FLOAT(OFS_RETURN) = i;
3963 return;
3964 }
3965 if (!strcmp(sv.particle_precache[i], s))
3966 {
3967 if (sv.state != ss_loading && !pr_checkextension.value)
3968 {
3969 if (pr_ext_warned_particleeffectnum++ < 3)
3970 Con_Warning ("PF_sv_particleeffectnum(%s): Precache should only be done in spawn functions\n", s);
3971 }
3972 G_FLOAT(OFS_RETURN) = i;
3973 return;
3974 }
3975 }
3976 PR_RunError ("PF_sv_particleeffectnum: overflow");
3977 }
PF_sv_trailparticles(void)3978 static void PF_sv_trailparticles(void)
3979 {
3980 int efnum;
3981 int ednum;
3982 float *start = G_VECTOR(OFS_PARM2);
3983 float *end = G_VECTOR(OFS_PARM3);
3984
3985 /*DP gets this wrong, lets try to be compatible*/
3986 if ((unsigned int)G_INT(OFS_PARM1) >= MAX_EDICTS*(unsigned int)qcvm->edict_size)
3987 {
3988 ednum = G_EDICTNUM(OFS_PARM0);
3989 efnum = G_FLOAT(OFS_PARM1);
3990 }
3991 else
3992 {
3993 efnum = G_FLOAT(OFS_PARM0);
3994 ednum = G_EDICTNUM(OFS_PARM1);
3995 }
3996
3997 if (efnum <= 0)
3998 return;
3999
4000 MSG_WriteByte(&sv.multicast, svcdp_trailparticles);
4001 MSG_WriteShort(&sv.multicast, ednum);
4002 MSG_WriteShort(&sv.multicast, efnum);
4003 MSG_WriteCoord(&sv.multicast, start[0], sv.protocolflags);
4004 MSG_WriteCoord(&sv.multicast, start[1], sv.protocolflags);
4005 MSG_WriteCoord(&sv.multicast, start[2], sv.protocolflags);
4006 MSG_WriteCoord(&sv.multicast, end[0], sv.protocolflags);
4007 MSG_WriteCoord(&sv.multicast, end[1], sv.protocolflags);
4008 MSG_WriteCoord(&sv.multicast, end[2], sv.protocolflags);
4009
4010 SV_Multicast(MULTICAST_PHS_U, start, 0, PEXT2_REPLACEMENTDELTAS);
4011 }
PF_sv_pointparticles(void)4012 static void PF_sv_pointparticles(void)
4013 {
4014 int efnum = G_FLOAT(OFS_PARM0);
4015 float *org = G_VECTOR(OFS_PARM1);
4016 float *vel = (qcvm->argc < 3)?vec3_origin:G_VECTOR(OFS_PARM2);
4017 int count = (qcvm->argc < 4)?1:G_FLOAT(OFS_PARM3);
4018
4019 if (efnum <= 0)
4020 return;
4021 if (count > 65535)
4022 count = 65535;
4023 if (count < 1)
4024 return;
4025
4026 if (count == 1 && !vel[0] && !vel[1] && !vel[2])
4027 {
4028 MSG_WriteByte(&sv.multicast, svcdp_pointparticles1);
4029 MSG_WriteShort(&sv.multicast, efnum);
4030 MSG_WriteCoord(&sv.multicast, org[0], sv.protocolflags);
4031 MSG_WriteCoord(&sv.multicast, org[1], sv.protocolflags);
4032 MSG_WriteCoord(&sv.multicast, org[2], sv.protocolflags);
4033 }
4034 else
4035 {
4036 MSG_WriteByte(&sv.multicast, svcdp_pointparticles);
4037 MSG_WriteShort(&sv.multicast, efnum);
4038 MSG_WriteCoord(&sv.multicast, org[0], sv.protocolflags);
4039 MSG_WriteCoord(&sv.multicast, org[1], sv.protocolflags);
4040 MSG_WriteCoord(&sv.multicast, org[2], sv.protocolflags);
4041 MSG_WriteCoord(&sv.multicast, vel[0], sv.protocolflags);
4042 MSG_WriteCoord(&sv.multicast, vel[1], sv.protocolflags);
4043 MSG_WriteCoord(&sv.multicast, vel[2], sv.protocolflags);
4044 MSG_WriteShort(&sv.multicast, count);
4045 }
4046
4047 SV_Multicast(MULTICAST_PVS_U, org, 0, PEXT2_REPLACEMENTDELTAS);
4048 }
4049
PF_CL_ForceParticlePrecache(const char * s)4050 int PF_CL_ForceParticlePrecache(const char *s)
4051 {
4052 int i;
4053
4054 //check if an ssqc one already exists with that name
4055 for (i = 1; i < MAX_PARTICLETYPES; i++)
4056 {
4057 if (!cl.particle_precache[i].name)
4058 break; //nope, no more known
4059 if (!strcmp(cl.particle_precache[i].name, s))
4060 return i;
4061 }
4062
4063 //nope, check for a csqc one, and allocate if needed
4064 for (i = 1; i < MAX_PARTICLETYPES; i++)
4065 {
4066 if (!cl.local_particle_precache[i].name)
4067 {
4068 cl.local_particle_precache[i].name = strcpy(Hunk_Alloc(strlen(s)+1), s); //weirdness to avoid issues with tempstrings
4069 cl.local_particle_precache[i].index = PScript_FindParticleType(cl.local_particle_precache[i].name);
4070 return -i;
4071 }
4072 if (!strcmp(cl.local_particle_precache[i].name, s))
4073 return -i;
4074 }
4075
4076 //err... too many. bum.
4077 return 0;
4078 }
PF_CL_GetParticle(int idx)4079 int PF_CL_GetParticle(int idx)
4080 { //negatives are csqc-originated particles, positives are ssqc-originated, for consistency allowing networking of particles as identifiers
4081 if (!idx)
4082 return P_INVALID;
4083 if (idx < 0)
4084 {
4085 idx = -idx;
4086 if (idx >= MAX_PARTICLETYPES)
4087 return P_INVALID;
4088 return cl.local_particle_precache[idx].index;
4089 }
4090 else
4091 {
4092 if (idx >= MAX_PARTICLETYPES)
4093 return P_INVALID;
4094 return cl.particle_precache[idx].index;
4095 }
4096 }
4097
PF_cl_particleeffectnum(void)4098 static void PF_cl_particleeffectnum(void)
4099 {
4100 const char *s;
4101
4102 s = G_STRING(OFS_PARM0);
4103 G_FLOAT(OFS_RETURN) = 0;
4104 // PR_CheckEmptyString (s);
4105
4106 if (!*s)
4107 return;
4108
4109 G_FLOAT(OFS_RETURN) = PF_CL_ForceParticlePrecache(s);
4110 if (!G_FLOAT(OFS_RETURN))
4111 PR_RunError ("PF_cl_particleeffectnum: overflow");
4112 }
PF_cl_trailparticles(void)4113 static void PF_cl_trailparticles(void)
4114 {
4115 int efnum;
4116 edict_t *ent;
4117 float *start = G_VECTOR(OFS_PARM2);
4118 float *end = G_VECTOR(OFS_PARM3);
4119
4120 if ((unsigned int)G_INT(OFS_PARM1) >= MAX_EDICTS*(unsigned int)qcvm->edict_size)
4121 { /*DP gets this wrong, lets try to be compatible*/
4122 ent = G_EDICT(OFS_PARM0);
4123 efnum = G_FLOAT(OFS_PARM1);
4124 }
4125 else
4126 {
4127 efnum = G_FLOAT(OFS_PARM0);
4128 ent = G_EDICT(OFS_PARM1);
4129 }
4130
4131 if (efnum <= 0)
4132 return;
4133 efnum = PF_CL_GetParticle(efnum);
4134 PScript_ParticleTrail(start, end, efnum, host_frametime, -NUM_FOR_EDICT(ent), NULL, NULL/*&ent->trailstate*/);
4135 }
PF_cl_pointparticles(void)4136 static void PF_cl_pointparticles(void)
4137 {
4138 int efnum = G_FLOAT(OFS_PARM0);
4139 float *org = G_VECTOR(OFS_PARM1);
4140 float *vel = (qcvm->argc < 3)?vec3_origin:G_VECTOR(OFS_PARM2);
4141 int count = (qcvm->argc < 4)?1:G_FLOAT(OFS_PARM3);
4142
4143 if (efnum <= 0)
4144 return;
4145 if (count < 1)
4146 return;
4147 efnum = PF_CL_GetParticle(efnum);
4148 PScript_RunParticleEffectState (org, vel, count, efnum, NULL);
4149 }
4150 #else
4151 #define PF_sv_particleeffectnum PF_void_stub
4152 #define PF_sv_trailparticles PF_void_stub
4153 #define PF_sv_pointparticles PF_void_stub
4154 #define PF_cl_particleeffectnum PF_void_stub
4155 #define PF_cl_trailparticles PF_void_stub
4156 #define PF_cl_pointparticles PF_void_stub
4157 #endif
4158
4159
PF_cl_getstat_int(void)4160 static void PF_cl_getstat_int(void)
4161 {
4162 int stnum = G_FLOAT(OFS_PARM0);
4163 if (stnum < 0 || stnum > countof(cl.stats))
4164 G_INT(OFS_RETURN) = 0;
4165 else
4166 G_INT(OFS_RETURN) = cl.stats[stnum];
4167 }
PF_cl_getstat_float(void)4168 static void PF_cl_getstat_float(void)
4169 {
4170 int stnum = G_FLOAT(OFS_PARM0);
4171 if (stnum < 0 || stnum > countof(cl.stats))
4172 G_FLOAT(OFS_RETURN) = 0;
4173 else if (qcvm->argc > 1)
4174 {
4175 int firstbit = G_FLOAT(OFS_PARM1);
4176 int bitcount = G_FLOAT(OFS_PARM2);
4177 G_FLOAT(OFS_RETURN) = (cl.stats[stnum]>>firstbit) & ((1<<bitcount)-1);
4178 }
4179 else
4180 G_FLOAT(OFS_RETURN) = cl.statsf[stnum];
4181 }
PF_cl_getstat_string(void)4182 static void PF_cl_getstat_string(void)
4183 {
4184 int stnum = G_FLOAT(OFS_PARM0);
4185 if (stnum < 0 || stnum > countof(cl.statss) || !cl.statss[stnum])
4186 G_INT(OFS_RETURN) = 0;
4187 else
4188 {
4189 char *result = PR_GetTempString();
4190 q_strlcpy(result, cl.statss[stnum], STRINGTEMP_LENGTH);
4191 G_INT(OFS_RETURN) = PR_SetEngineString(result);
4192 }
4193 }
4194
4195 static struct
4196 {
4197 char name[MAX_QPATH];
4198 unsigned int flags;
4199 qpic_t *pic;
4200 } *qcpics;
4201 static size_t numqcpics;
4202 static size_t maxqcpics;
PR_ReloadPics(qboolean purge)4203 void PR_ReloadPics(qboolean purge)
4204 {
4205 numqcpics = 0;
4206
4207 free(qcpics);
4208 qcpics = NULL;
4209 maxqcpics = 0;
4210 }
4211 #define PICFLAG_AUTO 0 //value used when no flags known
4212 #define PICFLAG_WAD (1u<<0) //name matches that of a wad lump
4213 #define PICFLAG_WRAP (1u<<2) //make sure npot stuff doesn't break wrapping.
4214 #define PICFLAG_MIPMAP (1u<<3) //disable use of scrap...
4215 #define PICFLAG_BLOCK (1u<<9) //wait until the texture is fully loaded.
4216 #define PICFLAG_NOLOAD (1u<<31)
DrawQC_CachePic(const char * picname,unsigned int flags)4217 static qpic_t *DrawQC_CachePic(const char *picname, unsigned int flags)
4218 { //okay, so this is silly. we've ended up with 3 different cache levels. qcpics, pics, and images.
4219 size_t i;
4220 unsigned int texflags;
4221 for (i = 0; i < numqcpics; i++)
4222 { //binary search? something more sane?
4223 if (!strcmp(picname, qcpics[i].name))
4224 {
4225 if (qcpics[i].pic)
4226 return qcpics[i].pic;
4227 break;
4228 }
4229 }
4230
4231 if (strlen(picname) >= MAX_QPATH)
4232 return NULL; //too long. get lost.
4233
4234 if (flags & PICFLAG_NOLOAD)
4235 return NULL; //its a query, not actually needed.
4236
4237 if (i+1 > maxqcpics)
4238 {
4239 maxqcpics = i + 32;
4240 qcpics = realloc(qcpics, maxqcpics * sizeof(*qcpics));
4241 }
4242
4243 strcpy(qcpics[i].name, picname);
4244 qcpics[i].flags = flags;
4245 qcpics[i].pic = NULL;
4246
4247 texflags = TEXPREF_ALPHA | TEXPREF_PAD | TEXPREF_NOPICMIP;
4248 if (flags & PICFLAG_WRAP)
4249 texflags &= ~TEXPREF_PAD; //don't allow padding if its going to need to wrap (even if we don't enable clamp-to-edge normally). I just hope we have npot support.
4250 if (flags & PICFLAG_MIPMAP)
4251 texflags |= TEXPREF_MIPMAP;
4252
4253 //try to load it from a wad if applicable.
4254 //the extra gfx/ crap is because DP insists on it for wad images. and its a nightmare to get things working in all engines if we don't accept that quirk too.
4255 if (flags & PICFLAG_WAD)
4256 qcpics[i].pic = Draw_PicFromWad2(picname + (strncmp(picname, "gfx/", 4)?0:4), texflags);
4257 else if (!strncmp(picname, "gfx/", 4) && !strchr(picname+4, '.'))
4258 qcpics[i].pic = Draw_PicFromWad2(picname+4, texflags);
4259
4260 //okay, not a wad pic, try and load a lmp/tga/etc
4261 if (!qcpics[i].pic)
4262 qcpics[i].pic = Draw_TryCachePic(picname, texflags);
4263
4264 if (i == numqcpics)
4265 numqcpics++;
4266
4267 return qcpics[i].pic;
4268 }
4269 extern gltexture_t *char_texture;
DrawQC_CharacterQuad(float x,float y,int num,float w,float h,float * rgb,float alpha)4270 static void DrawQC_CharacterQuad (float x, float y, int num, float w, float h, float * rgb, float alpha)
4271 {
4272 float size = 0.0625;
4273 float frow = (num>>4)*size;
4274 float fcol = (num&15)*size;
4275 int i;
4276 qboolean alpha_blend = alpha < 1.0f;
4277 size = 0.0624; //avoid rounding errors...
4278
4279 VkBuffer buffer;
4280 VkDeviceSize buffer_offset;
4281 basicvertex_t * vertices = (basicvertex_t*)R_VertexAllocate(6 * sizeof(basicvertex_t), &buffer, &buffer_offset);
4282
4283 basicvertex_t corner_verts[4];
4284 memset(&corner_verts, 255, sizeof(corner_verts));
4285
4286 corner_verts[0].position[0] = x;
4287 corner_verts[0].position[1] = y;
4288 corner_verts[0].position[2] = 0.0f;
4289 corner_verts[0].texcoord[0] = fcol;
4290 corner_verts[0].texcoord[1] = frow;
4291
4292 corner_verts[1].position[0] = x+8;
4293 corner_verts[1].position[1] = y;
4294 corner_verts[1].position[2] = 0.0f;
4295 corner_verts[1].texcoord[0] = fcol + size;
4296 corner_verts[1].texcoord[1] = frow;
4297
4298 corner_verts[2].position[0] = x+8;
4299 corner_verts[2].position[1] = y+8;
4300 corner_verts[2].position[2] = 0.0f;
4301 corner_verts[2].texcoord[0] = fcol + size;
4302 corner_verts[2].texcoord[1] = frow + size;
4303
4304 corner_verts[3].position[0] = x;
4305 corner_verts[3].position[1] = y+8;
4306 corner_verts[3].position[2] = 0.0f;
4307 corner_verts[3].texcoord[0] = fcol;
4308 corner_verts[3].texcoord[1] = frow + size;
4309
4310 for (i = 0; i<4; ++i)
4311 {
4312 corner_verts[i].color[0] = rgb[0] * 255.0f;
4313 corner_verts[i].color[1] = rgb[1] * 255.0f;
4314 corner_verts[i].color[2] = rgb[2] * 255.0f;
4315 corner_verts[i].color[3] = alpha * 255.0f;
4316 }
4317
4318 vertices[0] = corner_verts[0];
4319 vertices[1] = corner_verts[1];
4320 vertices[2] = corner_verts[2];
4321 vertices[3] = corner_verts[2];
4322 vertices[4] = corner_verts[3];
4323 vertices[5] = corner_verts[0];
4324
4325 vulkan_globals.vk_cmd_bind_vertex_buffers(vulkan_globals.command_buffer, 0, 1, &buffer, &buffer_offset);
4326 if (alpha_blend)
4327 R_BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, vulkan_globals.basic_blend_pipeline[render_pass_index]);
4328 else
4329 R_BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, vulkan_globals.basic_alphatest_pipeline[render_pass_index]);
4330 vulkan_globals.vk_cmd_bind_descriptor_sets(vulkan_globals.command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, vulkan_globals.basic_pipeline_layout.handle, 0, 1, &char_texture->descriptor_set, 0, NULL);
4331 vulkan_globals.vk_cmd_draw(vulkan_globals.command_buffer, 6, 1, 0, 0);
4332 }
PF_cl_drawcharacter(void)4333 static void PF_cl_drawcharacter(void)
4334 {
4335 extern gltexture_t *char_texture;
4336
4337 float *pos = G_VECTOR(OFS_PARM0);
4338 int charcode= (int)G_FLOAT (OFS_PARM1) & 0xff;
4339 float *size = G_VECTOR(OFS_PARM2);
4340 float *rgb = G_VECTOR(OFS_PARM3);
4341 float alpha = G_FLOAT (OFS_PARM4);
4342
4343 if (charcode == 32)
4344 return; //don't waste time on spaces
4345
4346 DrawQC_CharacterQuad (pos[0], pos[1], charcode, size[0], size[1], rgb, alpha);
4347 }
4348
PF_cl_drawrawstring(void)4349 static void PF_cl_drawrawstring(void)
4350 {
4351 float *pos = G_VECTOR(OFS_PARM0);
4352 const char *text = G_STRING (OFS_PARM1);
4353 float *size = G_VECTOR(OFS_PARM2);
4354 float *rgb = G_VECTOR(OFS_PARM3);
4355 float alpha = G_FLOAT (OFS_PARM4);
4356
4357 float x = pos[0];
4358 int c;
4359
4360 if (!*text)
4361 return; //don't waste time on spaces
4362
4363 while ((c = *text++))
4364 {
4365 DrawQC_CharacterQuad (x, pos[1], c, size[0], size[1], rgb, alpha);
4366 x += size[0];
4367 }
4368 }
PF_cl_drawstring(void)4369 static void PF_cl_drawstring(void)
4370 {
4371 float *pos = G_VECTOR(OFS_PARM0);
4372 const char *text = G_STRING (OFS_PARM1);
4373 float *size = G_VECTOR(OFS_PARM2);
4374 float *rgb = G_VECTOR(OFS_PARM3);
4375 float alpha = G_FLOAT (OFS_PARM4);
4376
4377 float x = pos[0];
4378 struct markup_s mu;
4379 int c;
4380
4381 if (!*text)
4382 return; //don't waste time on spaces
4383
4384 PR_Markup_Begin(&mu, text, rgb, alpha);
4385
4386 while ((c = PR_Markup_Parse(&mu)))
4387 {
4388 DrawQC_CharacterQuad (x, pos[1], c, size[0], size[1], rgb, alpha);
4389 x += size[0];
4390 }
4391 }
PF_cl_stringwidth(void)4392 static void PF_cl_stringwidth(void)
4393 {
4394 static const float defaultfontsize[] = {8,8};
4395 const char *text = G_STRING (OFS_PARM0);
4396 qboolean usecolours = G_FLOAT(OFS_PARM1);
4397 const float *fontsize = (qcvm->argc>2)?G_VECTOR (OFS_PARM2):defaultfontsize;
4398 struct markup_s mu;
4399 int r = 0;
4400
4401 if (!usecolours)
4402 r = strlen(text);
4403 else
4404 {
4405 PR_Markup_Begin(&mu, text, vec3_origin, 1);
4406 while (PR_Markup_Parse(&mu))
4407 {
4408 r += 1;
4409 }
4410 }
4411
4412 //primitive and lame, but hey.
4413 G_FLOAT(OFS_RETURN) = fontsize[0] * r;
4414 }
4415
PF_cl_drawsetclip(void)4416 static void PF_cl_drawsetclip(void)
4417 {
4418 float s = PR_GetVMScale();
4419
4420 float x = G_FLOAT(OFS_PARM0)*s;
4421 float y = G_FLOAT(OFS_PARM1)*s;
4422 float w = G_FLOAT(OFS_PARM2)*s;
4423 float h = G_FLOAT(OFS_PARM3)*s;
4424
4425 VkRect2D render_area;
4426 render_area.offset.x = x;
4427 render_area.offset.y = y;
4428 render_area.extent.width = w;
4429 render_area.extent.height = h;
4430 vkCmdSetScissor(vulkan_globals.command_buffer, 0, 1, &render_area);
4431 }
PF_cl_drawresetclip(void)4432 static void PF_cl_drawresetclip(void)
4433 {
4434 VkRect2D render_area;
4435 render_area.offset.x = 0;
4436 render_area.offset.y = 0;
4437 render_area.extent.width = vid.width;
4438 render_area.extent.height = vid.height;
4439 vkCmdSetScissor(vulkan_globals.command_buffer, 0, 1, &render_area);
4440 }
4441
PF_cl_precachepic(void)4442 static void PF_cl_precachepic(void)
4443 {
4444 const char *name = G_STRING(OFS_PARM0);
4445 unsigned int flags = G_FLOAT(OFS_PARM1);
4446
4447 G_INT(OFS_RETURN) = G_INT(OFS_PARM0); //return input string, for convienience
4448
4449 if (!DrawQC_CachePic(name, flags) && (flags & PICFLAG_BLOCK))
4450 G_INT(OFS_RETURN) = 0; //return input string, for convienience
4451 }
PF_cl_iscachedpic(void)4452 static void PF_cl_iscachedpic(void)
4453 {
4454 const char *name = G_STRING(OFS_PARM0);
4455 if (DrawQC_CachePic(name, PICFLAG_NOLOAD))
4456 G_FLOAT(OFS_RETURN) = true;
4457 else
4458 G_FLOAT(OFS_RETURN) = false;
4459 }
4460
PF_cl_drawpic(void)4461 static void PF_cl_drawpic(void)
4462 {
4463 float *pos = G_VECTOR(OFS_PARM0);
4464 qpic_t *pic = DrawQC_CachePic(G_STRING(OFS_PARM1), PICFLAG_AUTO);
4465 float *size = G_VECTOR(OFS_PARM2);
4466 float *rgb = G_VECTOR(OFS_PARM3);
4467 float alpha = G_FLOAT (OFS_PARM4);
4468
4469 if (pic)
4470 Draw_SubPic (pos[0], pos[1], size[0], size[1], pic, 0, 0, 1, 1, rgb, alpha);
4471 }
4472
PF_cl_getimagesize(void)4473 static void PF_cl_getimagesize(void)
4474 {
4475 qpic_t *pic = DrawQC_CachePic(G_STRING(OFS_PARM0), PICFLAG_AUTO);
4476 if (pic)
4477 G_VECTORSET(OFS_RETURN, pic->width, pic->height, 0);
4478 else
4479 G_VECTORSET(OFS_RETURN, 0, 0, 0);
4480 }
4481
PF_cl_drawsubpic(void)4482 static void PF_cl_drawsubpic(void)
4483 {
4484 float *pos = G_VECTOR(OFS_PARM0);
4485 float *size = G_VECTOR(OFS_PARM1);
4486 qpic_t *pic = DrawQC_CachePic(G_STRING(OFS_PARM2), PICFLAG_AUTO);
4487 float *srcpos = G_VECTOR(OFS_PARM3);
4488 float *srcsize = G_VECTOR(OFS_PARM4);
4489 float *rgb = G_VECTOR(OFS_PARM5);
4490 float alpha = G_FLOAT (OFS_PARM6);
4491
4492 if (pic)
4493 Draw_SubPic (pos[0], pos[1], size[0], size[1], pic, srcpos[0], srcpos[1], srcsize[0], srcsize[1], rgb, alpha);
4494 }
4495
PF_cl_drawfill(void)4496 static void PF_cl_drawfill(void)
4497 {
4498 int i;
4499 float *pos = G_VECTOR(OFS_PARM0);
4500 float *size = G_VECTOR(OFS_PARM1);
4501 float *rgb = G_VECTOR(OFS_PARM2);
4502 float alpha = G_FLOAT (OFS_PARM3);
4503
4504 VkBuffer buffer;
4505 VkDeviceSize buffer_offset;
4506 basicvertex_t * vertices = (basicvertex_t*)R_VertexAllocate(6 * sizeof(basicvertex_t), &buffer, &buffer_offset);
4507
4508 basicvertex_t corner_verts[4];
4509 memset(&corner_verts, 255, sizeof(corner_verts));
4510
4511 corner_verts[0].position[0] = pos[0];
4512 corner_verts[0].position[1] = pos[1];
4513 corner_verts[0].position[2] = 0.0f;
4514
4515 corner_verts[1].position[0] = pos[0]+size[0];
4516 corner_verts[1].position[1] = pos[1];
4517 corner_verts[1].position[2] = 0.0f;
4518
4519 corner_verts[2].position[0] = pos[0]+size[0];
4520 corner_verts[2].position[1] = pos[1]+size[1];
4521 corner_verts[2].position[2] = 0.0f;
4522
4523 corner_verts[3].position[0] = pos[0];
4524 corner_verts[3].position[1] = pos[1]+size[1];
4525 corner_verts[3].position[2] = 0.0f;
4526
4527 for (i = 0; i<4; ++i)
4528 {
4529 corner_verts[i].color[0] = rgb[0] * 255.0f;
4530 corner_verts[i].color[1] = rgb[1] * 255.0f;
4531 corner_verts[i].color[2] = rgb[2] * 255.0f;
4532 corner_verts[i].color[3] = alpha * 255.0f;
4533 }
4534
4535 vertices[0] = corner_verts[0];
4536 vertices[1] = corner_verts[1];
4537 vertices[2] = corner_verts[2];
4538 vertices[3] = corner_verts[2];
4539 vertices[4] = corner_verts[3];
4540 vertices[5] = corner_verts[0];
4541
4542 vulkan_globals.vk_cmd_bind_vertex_buffers(vulkan_globals.command_buffer, 0, 1, &buffer, &buffer_offset);
4543 R_BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, vulkan_globals.basic_notex_blend_pipeline[render_pass_index]);
4544 vulkan_globals.vk_cmd_bind_descriptor_sets(vulkan_globals.command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, vulkan_globals.basic_pipeline_layout.handle, 0, 1, &char_texture->descriptor_set, 0, NULL);
4545 vulkan_globals.vk_cmd_draw(vulkan_globals.command_buffer, 6, 1, 0, 0);
4546 }
4547
PF_cl_registercommand(void)4548 static void PF_cl_registercommand(void)
4549 {
4550 const char *cmdname = G_STRING(OFS_PARM0);
4551 Cmd_AddCommand(cmdname, NULL);
4552 }
PF_uri_get(void)4553 static void PF_uri_get(void){G_VECTORSET(OFS_RETURN, 0,0,0);}
4554
PF_touchtriggers(void)4555 static void PF_touchtriggers (void)
4556 {
4557 edict_t *e;
4558 float *org;
4559
4560 e = (qcvm->argc > 0)?G_EDICT(OFS_PARM0):G_EDICT(pr_global_struct->self);
4561 if (qcvm->argc > 1)
4562 {
4563 org = G_VECTOR(OFS_PARM1);
4564 VectorCopy (org, e->v.origin);
4565 }
4566 SV_LinkEdict (e, true);
4567 }
4568
PF_checkpvs(void)4569 static void PF_checkpvs (void)
4570 {
4571 float *org = G_VECTOR(OFS_PARM0);
4572 edict_t *ed = G_EDICT(OFS_PARM1);
4573
4574 mleaf_t *leaf = Mod_PointInLeaf (org, qcvm->worldmodel);
4575 byte *pvs = Mod_LeafPVS (leaf, qcvm->worldmodel); //johnfitz -- worldmodel as a parameter
4576 unsigned int i;
4577
4578 for (i=0 ; i < ed->num_leafs ; i++)
4579 {
4580 if (pvs[ed->leafnums[i] >> 3] & (1 << (ed->leafnums[i]&7) ))
4581 {
4582 G_FLOAT(OFS_RETURN) = true;
4583 return;
4584 }
4585 }
4586
4587 G_FLOAT(OFS_RETURN) = false;
4588 }
4589
4590 //A quick note on number ranges.
4591 //0: automatically assigned. more complicated, but no conflicts over numbers, just names...
4592 // NOTE: #0 is potentially ambiguous - vanilla will interpret it as instruction 0 (which is normally reserved) rather than a builtin.
4593 // if such functions were actually used, this would cause any 64bit engines that switched to unsigned types to crash due to an underflow.
4594 // we do some sneaky hacks to avoid changes to the vm... because we're evil.
4595 //0-199: free for all.
4596 //200-299: fte's random crap
4597 //300-399: csqc's random crap
4598 //400+: dp's random crap
4599 static struct
4600 {
4601 const char *name;
4602 builtin_t ssqcfunc;
4603 builtin_t csqcfunc;
4604 int documentednumber;
4605 const char *typestr;
4606 const char *desc;
4607 int number;
4608 } extensionbuiltins[] =
4609 #define PF_NoSSQC NULL
4610 #define PF_NoCSQC NULL
4611 #define PF_NoMenu NULL
4612 {
4613 {"vectoangles2", PF_ext_vectoangles, PF_ext_vectoangles, 51, D("vector(vector fwd, optional vector up)", "Returns the angles (+x=UP) required to orient an entity to look in the given direction. The 'up' argument is required if you wish to set a roll angle, otherwise it will be limited to just monster-style turning.")},
4614 {"sin", PF_Sin, PF_Sin, 60, "float(float angle)"}, //60
4615 {"cos", PF_Cos, PF_Cos, 61, "float(float angle)"}, //61
4616 {"sqrt", PF_Sqrt, PF_Sqrt, 62, "float(float value)"}, //62
4617 {"tracetoss", PF_TraceToss, PF_TraceToss, 64, "void(entity ent, entity ignore)"},
4618 {"etos", PF_etos, PF_etos, 65, "string(entity ent)"},
4619 {"etof", PF_num_for_edict, PF_num_for_edict, 0, "float(entity ent)"},
4620 {"ftoe", PF_edict_for_num, PF_edict_for_num, 0, "entity(float ent)"},
4621 {"infokey", PF_infokey_s, PF_NoCSQC, 80, D("string(entity e, string key)", "If e is world, returns the field 'key' from either the serverinfo or the localinfo. If e is a player, returns the value of 'key' from the player's userinfo string. There are a few special exceptions, like 'ip' which is not technically part of the userinfo.")}, //80
4622 {"infokeyf", PF_infokey_f, PF_NoCSQC, 0, D("float(entity e, string key)", "Identical to regular infokey, but returns it as a float instead of creating new tempstrings.")}, //80
4623 {"stof", PF_stof, PF_stof, 81, "float(string)"}, //81
4624 {"multicast", PF_multicast, PF_NoCSQC, 82, D("#define unicast(pl,reli) do{msg_entity = pl; multicast('0 0 0', reli?MULITCAST_ONE_R:MULTICAST_ONE);}while(0)\n"
4625 "void(vector where, float set)", "Once the MSG_MULTICAST network message buffer has been filled with data, this builtin is used to dispatch it to the given target, filtering by pvs for reduced network bandwidth.")}, //82
4626 {"tracebox", PF_tracebox, PF_tracebox, 90, D("void(vector start, vector mins, vector maxs, vector end, float nomonsters, entity ent)", "Exactly like traceline, but a box instead of a uselessly thin point. Acceptable sizes are limited by bsp format, q1bsp has strict acceptable size values.")},
4627 {"randomvec", PF_randomvector, PF_randomvector, 91, D("vector()", "Returns a vector with random values. Each axis is independantly a value between -1 and 1 inclusive.")},
4628 {"getlight", PF_sv_getlight, PF_cl_getlight, 92, "vector(vector org)"},// (DP_QC_GETLIGHT),
4629 {"registercvar", PF_registercvar, PF_registercvar, 93, D("float(string cvarname, string defaultvalue)", "Creates a new cvar on the fly. If it does not already exist, it will be given the specified value. If it does exist, this is a no-op.\nThis builtin has the limitation that it does not apply to configs or commandlines. Such configs will need to use the set or seta command causing this builtin to be a noop.\nIn engines that support it, you will generally find the autocvar feature easier and more efficient to use.")},
4630 {"min", PF_min, PF_min, 94, D("float(float a, float b, ...)", "Returns the lowest value of its arguments.")},// (DP_QC_MINMAXBOUND)
4631 {"max", PF_max, PF_max, 95, D("float(float a, float b, ...)", "Returns the highest value of its arguments.")},// (DP_QC_MINMAXBOUND)
4632 {"bound", PF_bound, PF_bound, 96, D("float(float minimum, float val, float maximum)", "Returns val, unless minimum is higher, or maximum is less.")},// (DP_QC_MINMAXBOUND)
4633 {"pow", PF_pow, PF_pow, 97, "float(float value, float exp)"},
4634 {"findfloat", PF_findfloat, PF_findfloat, 98, D("#define findentity findfloat\nentity(entity start, .__variant fld, __variant match)", "Equivelent to the find builtin, but instead of comparing strings contents, this builtin compares the raw values. This builtin requires multiple calls in order to scan all entities - set start to the previous call's return value.\nworld is returned when there are no more entities.")}, // #98 (DP_QC_FINDFLOAT)
4635 {"checkextension", PF_checkextension, PF_checkextension, 99, D("float(string extname)", "Checks for an extension by its name (eg: checkextension(\"FRIK_FILE\") says that its okay to go ahead and use strcat).\nUse cvar(\"pr_checkextension\") to see if this builtin exists.")}, // #99 //darkplaces system - query a string to see if the mod supports X Y and Z.
4636 {"checkbuiltin", PF_checkbuiltin, PF_checkbuiltin, 0, D("float(__variant funcref)", "Checks to see if the specified builtin is supported/mapped. This is intended as a way to check for #0 functions, allowing for simple single-builtin functions.")},
4637 {"builtin_find", PF_builtinsupported, PF_builtinsupported, 100, D("float(string builtinname)", "Looks to see if the named builtin is valid, and returns the builtin number it exists at.")}, // #100 //per builtin system.
4638 {"anglemod", PF_anglemod, PF_anglemod, 102, "float(float value)"}, //telejano
4639 {"strlen", PF_strlen, PF_strlen, 114, "float(string s)"}, // (FRIK_FILE)
4640 {"strcat", PF_strcat, PF_strcat, 115, "string(string s1, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7, optional string s8)"}, // (FRIK_FILE)
4641 {"substring", PF_substring, PF_substring, 116, "string(string s, float start, float length)"}, // (FRIK_FILE)
4642 {"stov", PF_stov, PF_stov, 117, "vector(string s)"}, // (FRIK_FILE)
4643 {"strzone", PF_strzone, PF_strzone, 118, D("string(string s, ...)", "Create a semi-permanent copy of a string that only becomes invalid once strunzone is called on the string (instead of when the engine assumes your string has left scope).")}, // (FRIK_FILE)
4644 {"strunzone", PF_strunzone, PF_strunzone, 119, D("void(string s)", "Destroys a string that was allocated by strunzone. Further references to the string MAY crash the game.")}, // (FRIK_FILE)
4645 {"tokenize_menuqc", PF_Tokenize, PF_Tokenize, 0, "float(string s)"},
4646 {"bitshift", PF_bitshift, PF_bitshift, 218, "float(float number, float quantity)"},
4647 {"te_lightningblood", PF_sv_te_lightningblood, NULL, 219, "void(vector org)"},
4648 {"strstrofs", PF_strstrofs, PF_strstrofs, 221, D("float(string s1, string sub, optional float startidx)", "Returns the 0-based offset of sub within the s1 string, or -1 if sub is not in s1.\nIf startidx is set, this builtin will ignore matches before that 0-based offset.")},
4649 {"str2chr", PF_str2chr, PF_str2chr, 222, D("float(string str, float index)", "Retrieves the character value at offset 'index'.")},
4650 {"chr2str", PF_chr2str, PF_chr2str, 223, D("string(float chr, ...)", "The input floats are considered character values, and are concatenated.")},
4651 {"strconv", PF_strconv, PF_strconv, 224, D("string(float ccase, float redalpha, float redchars, string str, ...)", "Converts quake chars in the input string amongst different representations.\nccase specifies the new case for letters.\n 0: not changed.\n 1: forced to lower case.\n 2: forced to upper case.\nredalpha and redchars switch between colour ranges.\n 0: no change.\n 1: Forced white.\n 2: Forced red.\n 3: Forced gold(low) (numbers only).\n 4: Forced gold (high) (numbers only).\n 5+6: Forced to white and red alternately.\nYou should not use this builtin in combination with UTF-8.")},
4652 {"strpad", PF_strpad, PF_strpad, 225, D("string(float pad, string str1, ...)", "Pads the string with spaces, to ensure its a specific length (so long as a fixed-width font is used, anyway). If pad is negative, the spaces are added on the left. If positive the padding is on the right.")}, //will be moved
4653 {"infoadd", PF_infoadd, PF_infoadd, 226, D("string(infostring old, string key, string value)", "Returns a new tempstring infostring with the named value changed (or added if it was previously unspecified). Key and value may not contain the \\ character.")},
4654 {"infoget", PF_infoget, PF_infoget, 227, D("string(infostring info, string key)", "Reads a named value from an infostring. The returned value is a tempstring")},
4655 {"strncmp", PF_strncmp, PF_strncmp, 228, D("#define strcmp strncmp\nfloat(string s1, string s2, optional float len, optional float s1ofs, optional float s2ofs)", "Compares up to 'len' chars in the two strings. s1ofs allows you to treat s2 as a substring to compare against, or should be 0.\nReturns 0 if the two strings are equal, a negative value if s1 appears numerically lower, and positive if s1 appears numerically higher.")},
4656 {"strcasecmp", PF_strncasecmp, PF_strncasecmp, 229, D("float(string s1, string s2)", "Compares the two strings without case sensitivity.\nReturns 0 if they are equal. The sign of the return value may be significant, but should not be depended upon.")},
4657 {"strncasecmp", PF_strncasecmp, PF_strncasecmp, 230, D("float(string s1, string s2, float len, optional float s1ofs, optional float s2ofs)", "Compares up to 'len' chars in the two strings without case sensitivity. s1ofs allows you to treat s2 as a substring to compare against, or should be 0.\nReturns 0 if they are equal. The sign of the return value may be significant, but should not be depended upon.")},
4658 {"strtrim", PF_strtrim, PF_strtrim, 0, D("string(string s)", "Trims the whitespace from the start+end of the string.")},
4659 {"clientstat", PF_clientstat, PF_NoCSQC, 232, D("void(float num, float type, .__variant fld)", "Specifies what data to use in order to send various stats, in a client-specific way.\n'num' should be a value between 32 and 127, other values are reserved.\n'type' must be set to one of the EV_* constants, one of EV_FLOAT, EV_STRING, EV_INTEGER, EV_ENTITY.\nfld must be a reference to the field used, each player will be sent only their own copy of these fields.")}, //EXT_CSQC
4660 {"isbackbuffered", PF_isbackbuffered, PF_NoCSQC, 234, D("float(entity player)", "Returns if the given player's network buffer will take multiple network frames in order to clear. If this builtin returns non-zero, you should delay or reduce the amount of reliable (and also unreliable) data that you are sending to that client.")},
4661 {"te_bloodqw", PF_sv_te_bloodqw, NULL, 239, "void(vector org, float count)"},
4662 {"checkpvs", PF_checkpvs, PF_checkpvs, 240, "float(vector viewpos, entity entity)"},
4663 {"mod", PF_mod, PF_mod, 245, "float(float a, float n)"},
4664 {"stoi", PF_stoi, PF_stoi, 259, D("int(string)", "Converts the given string into a true integer. Base 8, 10, or 16 is determined based upon the format of the string.")},
4665 {"itos", PF_itos, PF_itos, 260, D("string(int)", "Converts the passed true integer into a base10 string.")},
4666 {"stoh", PF_stoh, PF_stoh, 261, D("int(string)", "Reads a base-16 string (with or without 0x prefix) as an integer. Bugs out if given a base 8 or base 10 string. :P")},
4667 {"htos", PF_htos, PF_htos, 262, D("string(int)", "Formats an integer as a base16 string, with leading 0s and no prefix. Always returns 8 characters.")},
4668 {"ftoi", PF_ftoi, PF_ftoi, 0, D("int(float)", "Converts the given float into a true integer without depending on extended qcvm instructions.")},
4669 {"itof", PF_itof, PF_itof, 0, D("float(int)", "Converts the given true integer into a float without depending on extended qcvm instructions.")},
4670 {"crossproduct", PF_crossproduct, PF_crossproduct, 0, D("#ifndef dotproduct\n#define dotproduct(v1,v2) ((vector)(v1)*(vector)(v2))\n#endif\nvector(vector v1, vector v2)", "Small helper function to calculate the crossproduct of two vectors.")},
4671 {"frameforname", PF_frameforname, PF_frameforname, 276, D("float(float modidx, string framename)", "Looks up a framegroup from a model by name, avoiding the need for hardcoding. Returns -1 on error.")},// (FTE_CSQC_SKELETONOBJECTS)
4672 {"frameduration", PF_frameduration, PF_frameduration, 277, D("float(float modidx, float framenum)", "Retrieves the duration (in seconds) of the specified framegroup.")},// (FTE_CSQC_SKELETONOBJECTS)
4673 {"touchtriggers", PF_touchtriggers, PF_touchtriggers, 279, D("void(optional entity ent, optional vector neworigin)", "Triggers a touch events between self and every SOLID_TRIGGER entity that it is in contact with. This should typically just be the triggers touch functions. Also optionally updates the origin of the moved entity.")},//
4674 {"WriteFloat", PF_WriteFloat, PF_NoCSQC, 280, "void(float buf, float fl)"},
4675 {"frametoname", PF_frametoname, PF_frametoname, 284, "string(float modidx, float framenum)"},
4676 {"checkcommand", PF_checkcommand, PF_checkcommand, 294, D("float(string name)", "Checks to see if the supplied name is a valid command, cvar, or alias. Returns 0 if it does not exist.")},
4677 {"iscachedpic", PF_NoSSQC, PF_cl_iscachedpic, 316, D("float(string name)", "Checks to see if the image is currently loaded. Engines might lie, or cache between maps.")},// (EXT_CSQC)
4678 {"precache_pic", PF_NoSSQC, PF_cl_precachepic, 317, D("string(string name, optional float flags)", "Forces the engine to load the named image. If trywad is specified, the specified name must any lack path and extension.")},// (EXT_CSQC)
4679 {"drawgetimagesize", PF_NoSSQC, PF_cl_getimagesize, 318, D("#define draw_getimagesize drawgetimagesize\nvector(string picname)", "Returns the dimensions of the named image. Images specified with .lmp should give the original .lmp's dimensions even if texture replacements use a different resolution.")},// (EXT_CSQC)
4680 {"drawcharacter", PF_NoSSQC, PF_cl_drawcharacter, 320, D("float(vector position, float character, vector size, vector rgb, float alpha, optional float drawflag)", "Draw the given quake character at the given position.\nIf flag&4, the function will consider the char to be a unicode char instead (or display as a ? if outside the 32-127 range).\nsize should normally be something like '8 8 0'.\nrgb should normally be '1 1 1'\nalpha normally 1.\nSoftware engines may assume the named defaults.\nNote that ALL text may be rescaled on the X axis due to variable width fonts. The X axis may even be ignored completely.")},// (EXT_CSQC, [EXT_CSQC_???])
4681 {"drawrawstring", PF_NoSSQC, PF_cl_drawrawstring, 321, D("float(vector position, string text, vector size, vector rgb, float alpha, optional float drawflag)", "Draws the specified string without using any markup at all, even in engines that support it.\nIf UTF-8 is globally enabled in the engine, then that encoding is used (without additional markup), otherwise it is raw quake chars.\nSoftware engines may assume a size of '8 8 0', rgb='1 1 1', alpha=1, flag&3=0, but it is not an error to draw out of the screen.")},// (EXT_CSQC, [EXT_CSQC_???])
4682 {"drawpic", PF_NoSSQC, PF_cl_drawpic, 322, D("float(vector position, string pic, vector size, vector rgb, float alpha, optional float drawflag)", "Draws an shader within the given 2d screen box. Software engines may omit support for rgb+alpha, but must support rescaling, and must clip to the screen without crashing.")},// (EXT_CSQC, [EXT_CSQC_???])
4683 {"drawfill", PF_NoSSQC, PF_cl_drawfill, 323, D("float(vector position, vector size, vector rgb, float alpha, optional float drawflag)", "Draws a solid block over the given 2d box, with given colour, alpha, and blend mode (specified via flags).\nflags&3=0 simple blend.\nflags&3=1 additive blend")},// (EXT_CSQC, [EXT_CSQC_???])
4684 {"drawsetcliparea", PF_NoSSQC, PF_cl_drawsetclip, 324, D("void(float x, float y, float width, float height)", "Specifies a 2d clipping region (aka: scissor test). 2d draw calls will all be clipped to this 2d box, the area outside will not be modified by any 2d draw call (even 2d polygons).")},// (EXT_CSQC_???)
4685 {"drawresetcliparea", PF_NoSSQC, PF_cl_drawresetclip, 325, D("void(void)", "Reverts the scissor/clip area to the whole screen.")},// (EXT_CSQC_???)
4686 {"drawstring", PF_NoSSQC, PF_cl_drawstring, 326, D("float(vector position, string text, vector size, vector rgb, float alpha, float drawflag)", "Draws a string, interpreting markup and recolouring as appropriate.")},// #326
4687 {"stringwidth", PF_NoSSQC, PF_cl_stringwidth, 327, D("float(string text, float usecolours, vector fontsize='8 8')", "Calculates the width of the screen in virtual pixels. If usecolours is 1, markup that does not affect the string width will be ignored. Will always be decoded as UTF-8 if UTF-8 is globally enabled.\nIf the char size is not specified, '8 8 0' will be assumed.")},// EXT_CSQC_'DARKPLACES'
4688 {"drawsubpic", PF_NoSSQC, PF_cl_drawsubpic, 328, D("void(vector pos, vector sz, string pic, vector srcpos, vector srcsz, vector rgb, float alpha, optional float drawflag)", "Draws a rescaled subsection of an image to the screen.")},// #328 EXT_CSQC_'DARKPLACES'
4689 {"getstati", PF_NoSSQC, PF_cl_getstat_int, 330, D("#define getstati_punf(stnum) (float)(__variant)getstati(stnum)\nint(float stnum)", "Retrieves the numerical value of the given EV_INTEGER or EV_ENTITY stat. Use getstati_punf if you wish to type-pun a float stat as an int to avoid truncation issues in DP.")},// (EXT_CSQC)
4690 {"getstatf", PF_NoSSQC, PF_cl_getstat_float, 331, D("#define getstatbits getstatf\nfloat(float stnum, optional float firstbit, optional float bitcount)", "Retrieves the numerical value of the given EV_FLOAT stat. If firstbit and bitcount are specified, retrieves the upper bits of the STAT_ITEMS stat (converted into a float, so there are no VM dependancies).")},// (EXT_CSQC)
4691 {"getstats", PF_NoSSQC, PF_cl_getstat_string, 332, D("string(float stnum)", "Retrieves the value of the given EV_STRING stat, as a tempstring.\nString stats use a separate pool of stats from numeric ones.\n")},
4692 {"setmodelindex", PF_sv_setmodelindex, PF_cl_setmodelindex, 333, D("void(entity e, float mdlindex)", "Sets a model by precache index instead of by name. Otherwise identical to setmodel.")},//
4693 {"particleeffectnum", PF_sv_particleeffectnum, PF_cl_particleeffectnum, 335, D("float(string effectname)", "Precaches the named particle effect. If your effect name is of the form 'foo.bar' then particles/foo.cfg will be loaded by the client if foo.bar was not already defined.\nDifferent engines will have different particle systems, this specifies the QC API only.")},// (EXT_CSQC)
4694 {"trailparticles", PF_sv_trailparticles, PF_cl_trailparticles, 336, D("void(float effectnum, entity ent, vector start, vector end)", "Draws the given effect between the two named points. If ent is not world, distances will be cached in the entity in order to avoid framerate dependancies. The entity is not otherwise used.")},// (EXT_CSQC),
4695 {"pointparticles", PF_sv_pointparticles, PF_cl_pointparticles, 337, D("void(float effectnum, vector origin, optional vector dir, optional float count)", "Spawn a load of particles from the given effect at the given point traveling or aiming along the direction specified. The number of particles are scaled by the count argument.")},// (EXT_CSQC)
4696 {"print", PF_print, PF_print, 339, D("void(string s, ...)", "Unconditionally print on the local system's console, even in ssqc (doesn't care about the value of the developer cvar).")},//(EXT_CSQC)
4697 {"registercommand", NULL, PF_cl_registercommand, 352, D("void(string cmdname)", "Register the given console command, for easy console use.\nConsole commands that are later used will invoke CSQC_ConsoleCommand.")},//(EXT_CSQC)
4698 {"wasfreed", PF_WasFreed, PF_WasFreed, 353, D("float(entity ent)", "Quickly check to see if the entity is currently free. This function is only valid during the two-second non-reuse window, after that it may give bad results. Try one second to make it more robust.")},//(EXT_CSQC) (should be availabe on server too)
4699 {"copyentity", PF_copyentity, PF_copyentity, 400, D("entity(entity from, optional entity to)", "Copies all fields from one entity to another.")},// (DP_QC_COPYENTITY)
4700 {"findchain", PF_findchain, PF_findchain, 402, "entity(.string field, string match, optional .entity chainfield)"},// (DP_QC_FINDCHAIN)
4701 {"findchainfloat", PF_findchainfloat, PF_findchainfloat, 403, "entity(.float fld, float match, optional .entity chainfield)"},// (DP_QC_FINDCHAINFLOAT)
4702 {"te_blood", PF_sv_te_blooddp, NULL, 405, "void(vector org, vector dir, float count)"},// #405 te_blood
4703 {"te_particlerain", PF_sv_te_particlerain, NULL, 409, "void(vector mincorner, vector maxcorner, vector vel, float howmany, float color)"},// (DP_TE_PARTICLERAIN)
4704 {"te_particlesnow", PF_sv_te_particlesnow, NULL, 410, "void(vector mincorner, vector maxcorner, vector vel, float howmany, float color)"},// (DP_TE_PARTICLESNOW)
4705 {"te_gunshot", PF_sv_te_gunshot, PF_cl_te_gunshot, 418, "void(vector org, optional float count)"},// #418 te_gunshot
4706 {"te_spike", PF_sv_te_spike, PF_cl_te_spike, 419, "void(vector org)"},// #419 te_spike
4707 {"te_superspike", PF_sv_te_superspike, PF_cl_te_superspike, 420, "void(vector org)"},// #420 te_superspike
4708 {"te_explosion", PF_sv_te_explosion, PF_cl_te_explosion, 421, "void(vector org)"},// #421 te_explosion
4709 {"te_tarexplosion", PF_sv_te_tarexplosion, PF_cl_te_tarexplosion, 422, "void(vector org)"},// #422 te_tarexplosion
4710 {"te_wizspike", PF_sv_te_wizspike, PF_cl_te_wizspike, 423, "void(vector org)"},// #423 te_wizspike
4711 {"te_knightspike", PF_sv_te_knightspike, PF_cl_te_knightspike, 424, "void(vector org)"},// #424 te_knightspike
4712 {"te_lavasplash", PF_sv_te_lavasplash, PF_cl_te_lavasplash, 425, "void(vector org)"},// #425 te_lavasplash
4713 {"te_teleport", PF_sv_te_teleport, PF_cl_te_teleport, 426, "void(vector org)"},// #426 te_teleport
4714 {"te_explosion2", PF_sv_te_explosion2, PF_cl_te_explosion2, 427, "void(vector org, float color, float colorlength)"},// #427 te_explosion2
4715 {"te_lightning1", PF_sv_te_lightning1, PF_cl_te_lightning1, 428, "void(entity own, vector start, vector end)"},// #428 te_lightning1
4716 {"te_lightning2", PF_sv_te_lightning2, PF_cl_te_lightning2, 429, "void(entity own, vector start, vector end)"},// #429 te_lightning2
4717 {"te_lightning3", PF_sv_te_lightning3, PF_cl_te_lightning3, 430, "void(entity own, vector start, vector end)"},// #430 te_lightning3
4718 {"te_beam", PF_sv_te_beam, PF_cl_te_beam, 431, "void(entity own, vector start, vector end)"},// #431 te_beam
4719 {"vectorvectors", PF_vectorvectors, PF_vectorvectors, 432, "void(vector dir)"},// (DP_QC_VECTORVECTORS)
4720 {"getsurfacenumpoints", PF_getsurfacenumpoints, PF_getsurfacenumpoints, 434, "float(entity e, float s)"},// (DP_QC_GETSURFACE)
4721 {"getsurfacepoint", PF_getsurfacepoint, PF_getsurfacepoint, 435, "vector(entity e, float s, float n)"},// (DP_QC_GETSURFACE)
4722 {"getsurfacenormal", PF_getsurfacenormal, PF_getsurfacenormal, 436, "vector(entity e, float s)"},// (DP_QC_GETSURFACE)
4723 {"getsurfacetexture", PF_getsurfacetexture, PF_getsurfacetexture, 437, "string(entity e, float s)"},// (DP_QC_GETSURFACE)
4724 {"getsurfacenearpoint", PF_getsurfacenearpoint, PF_getsurfacenearpoint, 438, "float(entity e, vector p)"},// (DP_QC_GETSURFACE)
4725 {"getsurfaceclippedpoint", PF_getsurfaceclippedpoint, PF_getsurfaceclippedpoint, 439, "vector(entity e, float s, vector p)"},// (DP_QC_GETSURFACE)
4726 {"clientcommand", PF_clientcommand, PF_NoCSQC, 440, "void(entity e, string s)"},// (KRIMZON_SV_PARSECLIENTCOMMAND)
4727 {"tokenize", PF_Tokenize, PF_Tokenize, 441, "float(string s)"},// (KRIMZON_SV_PARSECLIENTCOMMAND)
4728 {"argv", PF_ArgV, PF_ArgV, 442, "string(float n)"},// (KRIMZON_SV_PARSECLIENTCOMMAND
4729 {"argc", PF_ArgC, PF_ArgC, 0, "float()"},
4730 {"setattachment", PF_setattachment, PF_setattachment, 443, "void(entity e, entity tagentity, string tagname)", ""},// (DP_GFX_QUAKE3MODELTAGS)
4731 {"cvar_string", PF_cvar_string, PF_cvar_string, 448, "string(string cvarname)"},//DP_QC_CVAR_STRING
4732 {"findflags", PF_findflags, PF_findflags, 449, "entity(entity start, .float fld, float match)"},//DP_QC_FINDFLAGS
4733 {"findchainflags", PF_findchainflags, PF_findchainflags, 450, "entity(.float fld, float match, optional .entity chainfield)"},//DP_QC_FINDCHAINFLAGS
4734 {"dropclient", PF_dropclient, PF_NoCSQC, 453, "void(entity player)"},//DP_SV_BOTCLIENT
4735 {"spawnclient", PF_spawnclient, PF_NoCSQC, 454, "entity()", "Spawns a dummy player entity.\nNote that such dummy players will be carried from one map to the next.\nWarning: DP_SV_CLIENTCOLORS DP_SV_CLIENTNAME are not implemented in quakespasm, so use KRIMZON_SV_PARSECLIENTCOMMAND's clientcommand builtin to change the bot's name/colours/skin/team/etc, in the same way that clients would ask."},//DP_SV_BOTCLIENT
4736 {"clienttype", PF_clienttype, PF_NoCSQC, 455, "float(entity client)"},//botclient
4737 {"WriteUnterminatedString", PF_WriteString2, PF_NoCSQC, 456, "void(float target, string str)"}, //writestring but without the null terminator. makes things a little nicer.
4738 {"edict_num", PF_edict_for_num, PF_edict_for_num, 459, "entity(float entnum)"},//DP_QC_EDICT_NUM
4739 {"buf_create", PF_buf_create, PF_buf_create, 460, "strbuf()"},//DP_QC_STRINGBUFFERS
4740 {"buf_del", PF_buf_del, PF_buf_del, 461, "void(strbuf bufhandle)"},//DP_QC_STRINGBUFFERS
4741 {"buf_getsize", PF_buf_getsize, PF_buf_getsize, 462, "float(strbuf bufhandle)"},//DP_QC_STRINGBUFFERS
4742 {"buf_copy", PF_buf_copy, PF_buf_copy, 463, "void(strbuf bufhandle_from, strbuf bufhandle_to)"},//DP_QC_STRINGBUFFERS
4743 {"buf_sort", PF_buf_sort, PF_buf_sort, 464, "void(strbuf bufhandle, float sortprefixlen, float backward)"},//DP_QC_STRINGBUFFERS
4744 {"buf_implode", PF_buf_implode, PF_buf_implode, 465, "string(strbuf bufhandle, string glue)"},//DP_QC_STRINGBUFFERS
4745 {"bufstr_get", PF_bufstr_get, PF_bufstr_get, 466, "string(strbuf bufhandle, float string_index)"},//DP_QC_STRINGBUFFERS
4746 {"bufstr_set", PF_bufstr_set, PF_bufstr_set, 467, "void(strbuf bufhandle, float string_index, string str)"},//DP_QC_STRINGBUFFERS
4747 {"bufstr_add", PF_bufstr_add, PF_bufstr_add, 468, "float(strbuf bufhandle, string str, float order)"},//DP_QC_STRINGBUFFERS
4748 {"bufstr_free", PF_bufstr_free, PF_bufstr_free, 469, "void(strbuf bufhandle, float string_index)"},//DP_QC_STRINGBUFFERS
4749 {"asin", PF_asin, PF_asin, 471, "float(float s)"},//DP_QC_ASINACOSATANATAN2TAN
4750 {"acos", PF_acos, PF_acos, 472, "float(float c)"},//DP_QC_ASINACOSATANATAN2TAN
4751 {"atan", PF_atan, PF_atan, 473, "float(float t)"},//DP_QC_ASINACOSATANATAN2TAN
4752 {"atan2", PF_atan2, PF_atan2, 474, "float(float c, float s)"},//DP_QC_ASINACOSATANATAN2TAN
4753 {"tan", PF_tan, PF_tan, 475, "float(float a)"},//DP_QC_ASINACOSATANATAN2TAN
4754 {"strlennocol", PF_strlennocol, PF_strlennocol, 476, D("float(string s)", "Returns the number of characters in the string after any colour codes or other markup has been parsed.")},//DP_QC_STRINGCOLORFUNCTIONS
4755 {"strdecolorize", PF_strdecolorize, PF_strdecolorize, 477, D("string(string s)", "Flattens any markup/colours, removing them from the string.")},//DP_QC_STRINGCOLORFUNCTIONS
4756 {"strftime", PF_strftime, PF_strftime, 478, "string(float uselocaltime, string format, ...)"}, //DP_QC_STRFTIME
4757 {"tokenizebyseparator", PF_tokenizebyseparator, PF_tokenizebyseparator, 479, "float(string s, string separator1, ...)"}, //DP_QC_TOKENIZEBYSEPARATOR
4758 {"strtolower", PF_strtolower, PF_strtolower, 480, "string(string s)"}, //DP_QC_STRING_CASE_FUNCTIONS
4759 {"strtoupper", PF_strtoupper, PF_strtoupper, 481, "string(string s)"}, //DP_QC_STRING_CASE_FUNCTIONS
4760 {"cvar_defstring", PF_cvar_defstring, PF_cvar_defstring, 482, "string(string s)"}, //DP_QC_CVAR_DEFSTRING
4761 {"pointsound", PF_sv_pointsound, PF_cl_pointsound, 483, "void(vector origin, string sample, float volume, float attenuation)"},//DP_SV_POINTSOUND
4762 {"strreplace", PF_strreplace, PF_strreplace, 484, "string(string search, string replace, string subject)"},//DP_QC_STRREPLACE
4763 {"strireplace", PF_strireplace, PF_strireplace, 485, "string(string search, string replace, string subject)"},//DP_QC_STRREPLACE
4764 {"getsurfacepointattribute", PF_getsurfacepointattribute, PF_getsurfacepointattribute, 486, "vector(entity e, float s, float n, float a)"},//DP_QC_GETSURFACEPOINTATTRIBUTE
4765 {"crc16", PF_crc16, PF_crc16, 494, "float(float caseinsensitive, string s, ...)"},//DP_QC_CRC16
4766 {"cvar_type", PF_cvar_type, PF_cvar_type, 495, "float(string name)"},//DP_QC_CVAR_TYPE
4767 {"numentityfields", PF_numentityfields, PF_numentityfields, 496, D("float()", "Gives the number of named entity fields. Note that this is not the size of an entity, but rather just the number of unique names (ie: vectors use 4 names rather than 3).")},//DP_QC_ENTITYDATA
4768 {"findentityfield", PF_findentityfield, PF_findentityfield, 0, D("float(string fieldname)", "Find a field index by name.")},
4769 {"entityfieldref", PF_entityfieldref, PF_entityfieldref, 0, D("typedef .__variant field_t;\nfield_t(float fieldnum)", "Returns a field value that can be directly used to read entity fields. Be sure to validate the type with entityfieldtype before using.")},//DP_QC_ENTITYDATA
4770 {"entityfieldname", PF_entityfieldname, PF_entityfieldname, 497, D("string(float fieldnum)", "Retrieves the name of the given entity field.")},//DP_QC_ENTITYDATA
4771 {"entityfieldtype", PF_entityfieldtype, PF_entityfieldtype, 498, D("float(float fieldnum)", "Provides information about the type of the field specified by the field num. Returns one of the EV_ values.")},//DP_QC_ENTITYDATA
4772 {"getentityfieldstring", PF_getentfldstr, PF_getentfldstr, 499, "string(float fieldnum, entity ent)"},//DP_QC_ENTITYDATA
4773 {"putentityfieldstring", PF_putentfldstr, PF_putentfldstr, 500, "float(float fieldnum, entity ent, string s)"},//DP_QC_ENTITYDATA
4774 {"whichpack", PF_whichpack, PF_whichpack, 503, D("string(string filename, optional float makereferenced)", "Returns the pak file name that contains the file specified. progs/player.mdl will generally return something like 'pak0.pak'. If makereferenced is true, clients will automatically be told that the returned package should be pre-downloaded and used, even if allow_download_refpackages is not set.")},//DP_QC_WHICHPACK
4775 {"uri_escape", PF_uri_escape, PF_uri_escape, 510, "string(string in)"},//DP_QC_URI_ESCAPE
4776 {"uri_unescape", PF_uri_unescape, PF_uri_unescape, 511, "string(string in)"},//DP_QC_URI_ESCAPE
4777 {"num_for_edict", PF_num_for_edict, PF_num_for_edict, 512, "float(entity ent)"},//DP_QC_NUM_FOR_EDICT
4778 {"uri_get", PF_uri_get, PF_uri_get, 513, "float(string uril, float id, optional string postmimetype, optional string postdata)", "stub."},//DP_QC_URI_GET
4779 {"tokenize_console", PF_tokenize_console, PF_tokenize_console, 514, D("float(string str)", "Tokenize a string exactly as the console's tokenizer would do so. The regular tokenize builtin became bastardized for convienient string parsing, which resulted in a large disparity that can be exploited to bypass checks implemented in a naive SV_ParseClientCommand function, therefore you can use this builtin to make sure it exactly matches.")},
4780 {"argv_start_index", PF_argv_start_index, PF_argv_start_index, 515, D("float(float idx)", "Returns the character index that the tokenized arg started at.")},
4781 {"argv_end_index", PF_argv_end_index, PF_argv_end_index, 516, D("float(float idx)", "Returns the character index that the tokenized arg stopped at.")},
4782 {"buf_cvarlist", PF_buf_cvarlist, PF_buf_cvarlist, 517, D("void(strbuf strbuf, string pattern, string antipattern)", "Populates the strbuf with a list of known cvar names.")},
4783 {"cvar_description", PF_cvar_description, PF_cvar_description, 518, D("string(string cvarname)", "Retrieves the description of a cvar, which might be useful for tooltips or help files. This may still not be useful.")},
4784 {"gettime", PF_gettime, PF_gettime, 519, "float(optional float timetype)"},
4785 {"log", PF_Logarithm, PF_Logarithm, 532, D("float(float v, optional float base)", "Determines the logarithm of the input value according to the specified base. This can be used to calculate how much something was shifted by.")},
4786 {"callfunction", PF_callfunction, PF_callfunction, 605, D("void(.../*, string funcname*/)", "Invokes the named function. The function name is always passed as the last parameter and must always be present. The others are passed to the named function as-is")},
4787 {"isfunction", PF_isfunction, PF_isfunction, 607, D("float(string s)", "Returns true if the named function exists and can be called with the callfunction builtin.")},
4788 {"parseentitydata", PF_parseentitydata, PF_parseentitydata, 613, D("float(entity e, string s, optional float offset)", "Reads a single entity's fields into an already-spawned entity. s should contain field pairs like in a saved game: {\"foo1\" \"bar\" \"foo2\" \"5\"}. Returns <=0 on failure, otherwise returns the offset in the string that was read to.")},
4789 {"sprintf", PF_sprintf, PF_sprintf, 627, "string(string fmt, ...)"},
4790 {"getsurfacenumtriangles", PF_getsurfacenumtriangles, PF_getsurfacenumtriangles, 628, "float(entity e, float s)"},
4791 {"getsurfacetriangle", PF_getsurfacetriangle, PF_getsurfacetriangle, 629, "vector(entity e, float s, float n)"},
4792 {"digest_hex", PF_digest_hex, PF_digest_hex, 639, "string(string digest, string data, ...)"},
4793 };
4794
PR_Can_Particles(unsigned int prot,unsigned int pext1,unsigned int pext2)4795 qboolean PR_Can_Particles(unsigned int prot, unsigned int pext1, unsigned int pext2)
4796 {
4797 if (r_fteparticles.value == 0)
4798 return false;
4799 if (pext2 || (pext1&PEXT1_CSQC))
4800 return true; //a bit different, but works
4801 else
4802 return false; //sorry. don't report it as supported.
4803 }
PR_Can_Ent_Alpha(unsigned int prot,unsigned int pext1,unsigned int pext2)4804 qboolean PR_Can_Ent_Alpha(unsigned int prot, unsigned int pext1, unsigned int pext2)
4805 {
4806 if (prot != PROTOCOL_NETQUAKE)
4807 return true; //most base protocols support it
4808 else if (pext2 & PEXT2_REPLACEMENTDELTAS)
4809 return true; //as does fte's extensions
4810 else
4811 return false; //sorry. don't report it as supported.
4812 }
PR_Can_Ent_ColorMod(unsigned int prot,unsigned int pext1,unsigned int pext2)4813 qboolean PR_Can_Ent_ColorMod(unsigned int prot, unsigned int pext1, unsigned int pext2)
4814 {
4815 if (pext2 & PEXT2_REPLACEMENTDELTAS)
4816 return true; //as does fte's extensions
4817 else
4818 return false; //sorry. don't report it as supported.
4819 }
PR_Can_Ent_Scale(unsigned int prot,unsigned int pext1,unsigned int pext2)4820 qboolean PR_Can_Ent_Scale(unsigned int prot, unsigned int pext1, unsigned int pext2)
4821 {
4822 if (prot == PROTOCOL_RMQ)
4823 return true; //some base protocols support it
4824 else if (pext2 & PEXT2_REPLACEMENTDELTAS)
4825 return true; //as does fte's extensions
4826 else
4827 return false; //sorry. don't report it as supported.
4828 }
4829 static struct
4830 {
4831 const char *name;
4832 qboolean (*checkextsupported)(unsigned int prot, unsigned int pext1, unsigned int pext2);
4833 } qcextensions[] =
4834 {
4835 {"DP_CON_SET"},
4836 {"DP_CON_SETA"},
4837 {"DP_EF_NOSHADOW"},
4838 {"DP_ENT_ALPHA", PR_Can_Ent_Alpha}, //already in quakespasm, supposedly.
4839 {"DP_ENT_COLORMOD", PR_Can_Ent_ColorMod},
4840 {"DP_ENT_SCALE", PR_Can_Ent_Scale},
4841 {"DP_ENT_TRAILEFFECTNUM", PR_Can_Particles},
4842 {"DP_INPUTBUTTONS"},
4843 {"DP_QC_ASINACOSATANATAN2TAN"},
4844 {"DP_QC_COPYENTITY"},
4845 {"DP_QC_CRC16"},
4846 {"DP_QC_CVAR_DEFSTRING"},
4847 {"DP_QC_CVAR_STRING"},
4848 {"DP_QC_CVAR_TYPE"},
4849 {"DP_QC_EDICT_NUM"},
4850 {"DP_QC_ENTITYDATA"},
4851 {"DP_QC_ETOS"},
4852 {"DP_QC_FINDCHAIN"},
4853 {"DP_QC_FINDCHAINFLAGS"},
4854 {"DP_QC_FINDCHAINFLOAT"},
4855 {"DP_QC_FINDFLAGS"},
4856 {"DP_QC_FINDFLOAT"},
4857 {"DP_QC_GETLIGHT"},
4858 {"DP_QC_GETSURFACE"},
4859 {"DP_QC_GETSURFACETRIANGLE"},
4860 {"DP_QC_GETSURFACEPOINTATTRIBUTE"},
4861 {"DP_QC_MINMAXBOUND"},
4862 {"DP_QC_MULTIPLETEMPSTRINGS"},
4863 {"DP_QC_RANDOMVEC"},
4864 {"DP_QC_SINCOSSQRTPOW"},
4865 {"DP_QC_SPRINTF"},
4866 {"DP_QC_STRFTIME"},
4867 {"DP_QC_STRING_CASE_FUNCTIONS"},
4868 {"DP_QC_STRINGBUFFERS"},
4869 {"DP_QC_STRINGCOLORFUNCTIONS"},
4870 {"DP_QC_STRREPLACE"},
4871 {"DP_QC_TOKENIZEBYSEPARATOR"},
4872 {"DP_QC_TRACEBOX"},
4873 {"DP_QC_TRACETOSS"},
4874 {"DP_QC_TRACE_MOVETYPES"},
4875 {"DP_QC_URI_ESCAPE"},
4876 {"DP_QC_VECTOANGLES_WITH_ROLL"},
4877 {"DP_QC_VECTORVECTORS"},
4878 {"DP_QC_WHICHPACK"},
4879 {"DP_REGISTERCVAR"},
4880 {"DP_SV_BOTCLIENT"},
4881 {"DP_SV_DROPCLIENT"},
4882 {"DP_SV_POINTSOUND"},
4883 {"DP_SV_PRINT"},
4884 {"DP_SV_SPAWNFUNC_PREFIX"},
4885 {"DP_SV_WRITEUNTERMINATEDSTRING"},
4886 #ifdef PSET_SCRIPT
4887 {"DP_TE_PARTICLERAIN", PR_Can_Particles},
4888 {"DP_TE_PARTICLESNOW", PR_Can_Particles},
4889 #endif
4890 {"DP_TE_STANDARDEFFECTBUILTINS"},
4891 {"EXT_BITSHIFT"},
4892 {"FTE_ENT_SKIN_CONTENTS"}, //SOLID_BSP&&skin==CONTENTS_FOO changes CONTENTS_SOLID to CONTENTS_FOO, allowing you to swim in moving ents without qc hacks, as well as correcting view cshifts etc.
4893 #ifdef PSET_SCRIPT
4894 {"FTE_PART_SCRIPT"},
4895 {"FTE_PART_NAMESPACES"},
4896 #ifdef PSET_SCRIPT_EFFECTINFO
4897 {"FTE_PART_NAMESPACE_EFFECTINFO"},
4898 #endif
4899 #endif
4900 {"FTE_QC_CHECKCOMMAND"},
4901 {"FTE_QC_CROSSPRODUCT"},
4902 {"FTE_QC_INFOKEY"},
4903 {"FTE_QC_INTCONV"},
4904 {"FTE_QC_MULTICAST"},
4905 {"FTE_STRINGS"},
4906 #ifdef PSET_SCRIPT
4907 {"FTE_SV_POINTPARTICLES", PR_Can_Particles},
4908 #endif
4909 {"KRIMZON_SV_PARSECLIENTCOMMAND"},
4910 {"ZQ_QC_STRINGS"},
4911 };
4912
PF_checkextension(void)4913 static void PF_checkextension(void)
4914 {
4915 const char *extname = G_STRING(OFS_PARM0);
4916 unsigned int i;
4917 cvar_t *v;
4918 char *cvn;
4919 for (i = 0; i < countof(qcextensions); i++)
4920 {
4921 if (!strcmp(extname, qcextensions[i].name))
4922 {
4923 if (qcextensions[i].checkextsupported)
4924 {
4925 unsigned int prot, pext1, pext2;
4926 extern unsigned int sv_protocol;
4927 extern unsigned int sv_protocol_pext1;
4928 extern unsigned int sv_protocol_pext2;
4929 extern cvar_t cl_nopext;
4930 if (sv.active || qcvm == &sv.qcvm)
4931 { //server or client+server
4932 prot = sv_protocol;
4933 pext1 = sv_protocol_pext1;
4934 pext2 = sv_protocol_pext2;
4935
4936 //if the server seems to be set up for singleplayer then filter by client settings. otherwise just assume the best.
4937 if (!isDedicated && svs.maxclients == 1 && cl_nopext.value)
4938 pext1 = pext2 = 0;
4939 }
4940 else if (cls.state == ca_connected)
4941 { //client only (or demo)
4942 prot = cl.protocol;
4943 pext1 = cl.protocol_pext1;
4944 pext2 = cl.protocol_pext2;
4945 }
4946 else
4947 { //menuqc? ooer
4948 prot = 0;
4949 pext1 = 0;
4950 pext2 = 0;
4951 }
4952 if (!qcextensions[i].checkextsupported(prot, pext1, pext2))
4953 {
4954 if (!pr_checkextension.value)
4955 Con_Printf("Mod queried extension %s, but not enabled\n", extname);
4956 G_FLOAT(OFS_RETURN) = false;
4957 return;
4958 }
4959 }
4960
4961 cvn = va("pr_ext_%s", qcextensions[i].name);
4962 for (i = 0; cvn[i]; i++)
4963 if (cvn[i] >= 'A' && cvn[i] <= 'Z')
4964 cvn[i] = 'a' + (cvn[i]-'A');
4965 v = Cvar_Create(cvn, "1");
4966 if (v && !v->value)
4967 {
4968 if (!pr_checkextension.value)
4969 Con_Printf("Mod queried extension %s, but blocked by cvar\n", extname);
4970 G_FLOAT(OFS_RETURN) = false;
4971 return;
4972 }
4973 if (!pr_checkextension.value)
4974 Con_Printf("Mod found extension %s\n", extname);
4975 G_FLOAT(OFS_RETURN) = true;
4976 return;
4977 }
4978 }
4979 if (!pr_checkextension.value)
4980 Con_DPrintf("Mod tried extension %s\n", extname);
4981 G_FLOAT(OFS_RETURN) = false;
4982 }
4983
PF_builtinsupported(void)4984 static void PF_builtinsupported(void)
4985 {
4986 const char *biname = G_STRING(OFS_PARM0);
4987 unsigned int i;
4988 for (i = 0; i < sizeof(extensionbuiltins) / sizeof(extensionbuiltins[0]); i++)
4989 {
4990 if (!strcmp(extensionbuiltins[i].name, biname))
4991 {
4992 G_FLOAT(OFS_RETURN) = extensionbuiltins[i].number;
4993 }
4994 }
4995 G_FLOAT(OFS_RETURN) = 0;
4996 }
4997
4998
PF_checkbuiltin(void)4999 static void PF_checkbuiltin (void)
5000 {
5001 func_t funcref = G_INT(OFS_PARM0);
5002 if ((unsigned int)funcref < (unsigned int)qcvm->progs->numfunctions)
5003 {
5004 dfunction_t *fnc = &qcvm->functions[(unsigned int)funcref];
5005 // const char *funcname = PR_GetString(fnc->s_name);
5006 int binum = -fnc->first_statement;
5007 unsigned int i;
5008
5009 //qc defines the function at least. nothing weird there...
5010 if (binum > 0 && binum < qcvm->numbuiltins)
5011 {
5012 if (qcvm->builtins[binum] == PF_Fixme)
5013 {
5014 G_FLOAT(OFS_RETURN) = false; //the builtin with that number isn't defined.
5015 for (i = 0; i < sizeof(extensionbuiltins) / sizeof(extensionbuiltins[0]); i++)
5016 {
5017 if (extensionbuiltins[i].number == binum)
5018 { //but it will be defined if its actually executed.
5019 if (extensionbuiltins[i].desc && !strncmp(extensionbuiltins[i].desc, "stub.", 5))
5020 G_FLOAT(OFS_RETURN) = false; //pretend it won't work if it probably won't be useful
5021 else if ((qcvm == &cl.qcvm && !extensionbuiltins[i].csqcfunc)
5022 || (qcvm == &sv.qcvm && !extensionbuiltins[i].ssqcfunc))
5023 G_FLOAT(OFS_RETURN) = false; //works, but not in this module
5024 else
5025 G_FLOAT(OFS_RETURN) = true;
5026 break;
5027 }
5028 }
5029 }
5030 else
5031 {
5032 G_FLOAT(OFS_RETURN) = true; //its defined, within the sane range, mapped, everything. all looks good.
5033 //we should probably go through the available builtins and validate that the qc's name matches what would be expected
5034 //this is really intended more for builtins defined as #0 though, in such cases, mismatched assumptions are impossible.
5035 }
5036 }
5037 else
5038 G_FLOAT(OFS_RETURN) = false; //not a valid builtin (#0 builtins get remapped at load, even if the builtin is activated then)
5039 }
5040 else
5041 { //not valid somehow.
5042 G_FLOAT(OFS_RETURN) = false;
5043 }
5044 }
5045
PF_Fixme(void)5046 void PF_Fixme (void)
5047 {
5048 //interrogate the vm to try to figure out exactly which builtin they just tried to execute.
5049 dstatement_t *st = &qcvm->statements[qcvm->xstatement];
5050 eval_t *glob = (eval_t*)&qcvm->globals[st->a];
5051 if ((unsigned int)glob->function < (unsigned int)qcvm->progs->numfunctions)
5052 {
5053 dfunction_t *fnc = &qcvm->functions[(unsigned int)glob->function];
5054 const char *funcname = PR_GetString(fnc->s_name);
5055 int binum = -fnc->first_statement;
5056 unsigned int i;
5057 if (binum >= 0)
5058 {
5059 //find an extension with the matching number
5060 for (i = 0; i < sizeof(extensionbuiltins) / sizeof(extensionbuiltins[0]); i++)
5061 {
5062 int num = extensionbuiltins[i].number;
5063 if (num == binum)
5064 { //set it up so we're faster next time
5065 builtin_t bi = NULL;
5066 if (qcvm == &sv.qcvm)
5067 bi = extensionbuiltins[i].ssqcfunc;
5068 else if (qcvm == &cl.qcvm)
5069 bi = extensionbuiltins[i].csqcfunc;
5070 if (!bi)
5071 continue;
5072
5073 num = extensionbuiltins[i].documentednumber;
5074 if (!pr_checkextension.value || (extensionbuiltins[i].desc && !strncmp(extensionbuiltins[i].desc, "stub.", 5)))
5075 Con_Warning("Mod is using builtin #%u - %s\n", num, extensionbuiltins[i].name);
5076 else
5077 Con_DPrintf2("Mod uses builtin #%u - %s\n", num, extensionbuiltins[i].name);
5078 qcvm->builtins[binum] = bi;
5079 qcvm->builtins[binum]();
5080 return;
5081 }
5082 }
5083
5084 PR_RunError ("unimplemented builtin #%i - %s", binum, funcname);
5085 }
5086 }
5087 PR_RunError ("PF_Fixme: not a builtin...");
5088 }
5089
5090
5091 //called at map end
PR_ShutdownExtensions(void)5092 void PR_ShutdownExtensions(void)
5093 {
5094 PR_UnzoneAll();
5095 PF_buf_shutdown();
5096 tokenize_flush();
5097 pr_ext_warned_particleeffectnum = 0;
5098 }
5099
PR_FindExtFunction(const char * entryname)5100 func_t PR_FindExtFunction(const char *entryname)
5101 { //depends on 0 being an invalid function,
5102 dfunction_t *func = ED_FindFunction(entryname);
5103 if (func)
5104 return func - qcvm->functions;
5105 return 0;
5106 }
PR_FindExtGlobal(int type,const char * name)5107 static void *PR_FindExtGlobal(int type, const char *name)
5108 {
5109 ddef_t *def = ED_FindGlobal(name);
5110 if (def && (def->type&~DEF_SAVEGLOBAL) == type && def->ofs < qcvm->progs->numglobals)
5111 return qcvm->globals + def->ofs;
5112 return NULL;
5113 }
5114
PR_InitExtensions(void)5115 void PR_InitExtensions(void)
5116 {
5117 size_t i, g,m;
5118 //this only needs to be done once. because we're evil.
5119 //it should help slightly with the 'documentation' above at least.
5120 g = m = sizeof(qcvm->builtins)/sizeof(qcvm->builtins[0]);
5121 for (i = 0; i < sizeof(extensionbuiltins)/sizeof(extensionbuiltins[0]); i++)
5122 {
5123 if (extensionbuiltins[i].documentednumber)
5124 extensionbuiltins[i].number = extensionbuiltins[i].documentednumber;
5125 else
5126 extensionbuiltins[i].number = --g;
5127 }
5128 }
5129
5130 //called at map start
PR_EnableExtensions(ddef_t * pr_globaldefs)5131 void PR_EnableExtensions(ddef_t *pr_globaldefs)
5132 {
5133 unsigned int i, j;
5134
5135 for (i = qcvm->numbuiltins; i < countof(qcvm->builtins); i++)
5136 qcvm->builtins[i] = PF_Fixme;
5137 qcvm->numbuiltins = i;
5138 if (!pr_checkextension.value && qcvm == &sv.qcvm)
5139 {
5140 Con_DPrintf("not enabling qc extensions\n");
5141 return;
5142 }
5143
5144 #define QCEXTFUNC(n,t) qcvm->extfuncs.n = PR_FindExtFunction(#n);
5145 QCEXTFUNCS_COMMON
5146
5147 //replace standard builtins with new replacement extended ones and selectively populate references to module-specific entrypoints.
5148 if (qcvm == &cl.qcvm)
5149 { //csqc
5150 for (i = 0; i < sizeof(extensionbuiltins) / sizeof(extensionbuiltins[0]); i++)
5151 {
5152 int num = (extensionbuiltins[i].documentednumber);
5153 if (num && extensionbuiltins[i].csqcfunc && qcvm->builtins[num] != PF_Fixme)
5154 qcvm->builtins[num] = extensionbuiltins[i].csqcfunc;
5155 }
5156
5157 QCEXTFUNCS_GAME
5158 QCEXTFUNCS_CS
5159 }
5160 else if (qcvm == &sv.qcvm)
5161 { //ssqc
5162 for (i = 0; i < sizeof(extensionbuiltins) / sizeof(extensionbuiltins[0]); i++)
5163 {
5164 int num = (extensionbuiltins[i].documentednumber);
5165 if (num && extensionbuiltins[i].ssqcfunc && qcvm->builtins[num] != PF_Fixme)
5166 qcvm->builtins[num] = extensionbuiltins[i].ssqcfunc;
5167 }
5168
5169 QCEXTFUNCS_GAME
5170 QCEXTFUNCS_SV
5171 }
5172 #undef QCEXTFUNC
5173
5174
5175 #define QCEXTGLOBAL_FLOAT(n) qcvm->extglobals.n = PR_FindExtGlobal(ev_float, #n);
5176 #define QCEXTGLOBAL_INT(n) qcvm->extglobals.n = PR_FindExtGlobal(ev_ext_integer, #n);
5177 #define QCEXTGLOBAL_VECTOR(n) qcvm->extglobals.n = PR_FindExtGlobal(ev_vector, #n);
5178 QCEXTGLOBALS_COMMON
5179 QCEXTGLOBALS_GAME
5180 QCEXTGLOBALS_CSQC
5181 #undef QCEXTGLOBAL_FLOAT
5182 #undef QCEXTGLOBAL_INT
5183 #undef QCEXTGLOBAL_VECTOR
5184
5185 //any #0 functions are remapped to their builtins here, so we don't have to tweak the VM in an obscure potentially-breaking way.
5186 for (i = 0; i < (unsigned int)qcvm->progs->numfunctions; i++)
5187 {
5188 if (qcvm->functions[i].first_statement == 0 && qcvm->functions[i].s_name && !qcvm->functions[i].parm_start && !qcvm->functions[i].locals)
5189 {
5190 const char *name = PR_GetString(qcvm->functions[i].s_name);
5191 for (j = 0; j < sizeof(extensionbuiltins)/sizeof(extensionbuiltins[0]); j++)
5192 {
5193 if (!strcmp(extensionbuiltins[j].name, name))
5194 { //okay, map it
5195 qcvm->functions[i].first_statement = -extensionbuiltins[j].number;
5196 break;
5197 }
5198 }
5199 }
5200 }
5201 }
5202
PR_DumpPlatform_f(void)5203 void PR_DumpPlatform_f(void)
5204 {
5205 char name[MAX_OSPATH];
5206 FILE *f;
5207 const char *outname = NULL;
5208 unsigned int i, j;
5209 enum
5210 {
5211 SS=1,
5212 CS=2,
5213 MN=4,
5214 };
5215 unsigned int targs = 0;
5216 for (i = 1; i < (unsigned)Cmd_Argc(); )
5217 {
5218 const char *arg = Cmd_Argv(i++);
5219 if (!strncmp(arg, "-O", 2))
5220 {
5221 if (arg[2])
5222 outname = arg+2;
5223 else
5224 outname = Cmd_Argv(i++);
5225 }
5226 else if (!q_strcasecmp(arg, "-Tcs"))
5227 targs |= CS;
5228 else if (!q_strcasecmp(arg, "-Tss"))
5229 targs |= SS;
5230 else if (!q_strcasecmp(arg, "-Tmenu"))
5231 targs |= MN;
5232 else
5233 {
5234 Con_Printf("%s: Unknown argument\n", Cmd_Argv(0));
5235 return;
5236 }
5237 }
5238 if (!outname)
5239 outname = ((targs==2)?"qscsextensions":"qsextensions");
5240 if (!targs)
5241 targs = SS|CS;
5242
5243 if (strstr(outname, ".."))
5244 return;
5245 q_snprintf (name, sizeof(name), "%s/src/%s", com_gamedir, outname);
5246 COM_AddExtension (name, ".qc", sizeof(name));
5247
5248 f = fopen (name, "w");
5249 if (!f)
5250 {
5251 Con_Printf("%s: Couldn't write %s\n", Cmd_Argv(0), name);
5252 return;
5253 }
5254 Con_Printf("%s: Writing %s\n", Cmd_Argv(0), name);
5255
5256 fprintf(f,
5257 "/*\n"
5258 "Extensions file for "ENGINE_NAME_AND_VER"\n"
5259 "This file is auto-generated by %s %s.\n"
5260 "You will probably need to use FTEQCC to compile this.\n"
5261 "*/\n"
5262 ,Cmd_Argv(0), Cmd_Args()?Cmd_Args():"with no args");
5263
5264 fprintf(f,
5265 "\n\n//This file only supports csqc, so including this file in some other situation is a user error\n"
5266 "#if defined(QUAKEWORLD) || defined(MENU)\n"
5267 "#error Mixed up module defs\n"
5268 "#endif\n"
5269 );
5270 if (targs & CS)
5271 {
5272 fprintf(f,
5273 "#if !defined(CSQC) && !defined(SSQC) && !defined(MENU)\n"
5274 "#define CSQC\n"
5275 "#ifndef CSQC_SIMPLE\n" //quakespasm's csqc implementation is simplified, and can do huds+menus, but that's about it.
5276 "#define CSQC_SIMPLE\n"
5277 "#endif\n"
5278 "#endif\n"
5279 );
5280 }
5281 else if (targs & SS)
5282 {
5283 fprintf(f,
5284 "#if !defined(CSQC) && !defined(SSQC) && !defined(MENU)\n"
5285 "#define SSQC\n"
5286 "#endif\n"
5287 );
5288 }
5289 else if (targs & MN)
5290 {
5291 fprintf(f,
5292 "#if !defined(CSQC) && !defined(SSQC) && !defined(MENU)\n"
5293 "#define MENU\n"
5294 "#endif\n"
5295 );
5296 }
5297 fprintf(f,
5298 "#ifndef QSSDEP\n"
5299 "#define QSSDEP(reason) __deprecated(reason)\n"
5300 "#endif\n"
5301 );
5302
5303 fprintf(f, "\n\n//List of advertised extensions\n");
5304 for (i = 0; i < countof(qcextensions); i++)
5305 fprintf(f, "//%s\n", qcextensions[i].name);
5306
5307 fprintf(f, "\n\n//Explicitly flag this stuff as probably-not-referenced, meaning fteqcc will shut up about it and silently strip what it can.\n");
5308 fprintf(f, "#pragma noref 1\n");
5309
5310 if (targs & (SS|CS)) //qss uses the same system defs for both ssqc and csqc, as a simplification.
5311 { //I really hope that fteqcc's unused variable logic is up to scratch
5312 fprintf(f, "#if defined(CSQC_SIMPLE) || defined(SSQC)\n");
5313 fprintf(f, "entity self,other,world;\n");
5314 fprintf(f, "float time,frametime,force_retouch;\n");
5315 fprintf(f, "string mapname;\n");
5316 fprintf(f, "float deathmatch,coop,teamplay,serverflags,total_secrets,total_monsters,found_secrets,killed_monsters,parm1, parm2, parm3, parm4, parm5, parm6, parm7, parm8, parm9, parm10, parm11, parm12, parm13, parm14, parm15, parm16;\n");
5317 fprintf(f, "vector v_forward, v_up, v_right;\n");
5318 fprintf(f, "float trace_allsolid,trace_startsolid,trace_fraction;\n");
5319 fprintf(f, "vector trace_endpos,trace_plane_normal;\n");
5320 fprintf(f, "float trace_plane_dist;\n");
5321 fprintf(f, "entity trace_ent;\n");
5322 fprintf(f, "float trace_inopen,trace_inwater;\n");
5323 fprintf(f, "entity msg_entity;\n");
5324 fprintf(f, "void() main,StartFrame,PlayerPreThink,PlayerPostThink,ClientKill,ClientConnect,PutClientInServer,ClientDisconnect,SetNewParms,SetChangeParms;\n");
5325 fprintf(f, "void end_sys_globals;\n\n");
5326 fprintf(f, ".float modelindex;\n");
5327 fprintf(f, ".vector absmin, absmax;\n");
5328 fprintf(f, ".float ltime,movetype,solid;\n");
5329 fprintf(f, ".vector origin,oldorigin,velocity,angles,avelocity,punchangle;\n");
5330 fprintf(f, ".string classname,model;\n");
5331 fprintf(f, ".float frame,skin,effects;\n");
5332 fprintf(f, ".vector mins, maxs,size;\n");
5333 fprintf(f, ".void() touch,use,think,blocked;\n");
5334 fprintf(f, ".float nextthink;\n");
5335 fprintf(f, ".entity groundentity;\n");
5336 fprintf(f, ".float health,frags,weapon;\n");
5337 fprintf(f, ".string weaponmodel;\n");
5338 fprintf(f, ".float weaponframe,currentammo,ammo_shells,ammo_nails,ammo_rockets,ammo_cells,items,takedamage;\n");
5339 fprintf(f, ".entity chain;\n");
5340 fprintf(f, ".float deadflag;\n");
5341 fprintf(f, ".vector view_ofs;\n");
5342 fprintf(f, ".float button0,button1,button2,impulse,fixangle;\n");
5343 fprintf(f, ".vector v_angle;\n");
5344 fprintf(f, ".float idealpitch;\n");
5345 fprintf(f, ".string netname;\n");
5346 fprintf(f, ".entity enemy;\n");
5347 fprintf(f, ".float flags,colormap,team,max_health,teleport_time,armortype,armorvalue,waterlevel,watertype,ideal_yaw,yaw_speed;\n");
5348 fprintf(f, ".entity aiment,goalentity;\n");
5349 fprintf(f, ".float spawnflags;\n");
5350 fprintf(f, ".string target,targetname;\n");
5351 fprintf(f, ".float dmg_take,dmg_save;\n");
5352 fprintf(f, ".entity dmg_inflictor,owner;\n");
5353 fprintf(f, ".vector movedir;\n");
5354 fprintf(f, ".string message;\n");
5355 fprintf(f, ".float sounds;\n");
5356 fprintf(f, ".string noise, noise1, noise2, noise3;\n");
5357 fprintf(f, "void end_sys_fields;\n");
5358 fprintf(f, "#endif\n");
5359 }
5360 if (targs & MN)
5361 {
5362 fprintf(f, "#if defined(MENU)\n");
5363 fprintf(f, "entity self;\n");
5364 fprintf(f, "void end_sys_globals;\n\n");
5365 fprintf(f, "void end_sys_fields;\n");
5366 fprintf(f, "#endif\n");
5367 }
5368
5369 fprintf(f, "\n\n//Some custom types (that might be redefined as accessors by fteextensions.qc, although we don't define any methods here)\n");
5370 fprintf(f, "#ifdef _ACCESSORS\n");
5371 fprintf(f, "accessor strbuf:float;\n");
5372 fprintf(f, "accessor searchhandle:float;\n");
5373 fprintf(f, "accessor hashtable:float;\n");
5374 fprintf(f, "accessor infostring:string;\n");
5375 fprintf(f, "accessor filestream:float;\n");
5376 fprintf(f, "#else\n");
5377 fprintf(f, "#define strbuf float\n");
5378 fprintf(f, "#define searchhandle float\n");
5379 fprintf(f, "#define hashtable float\n");
5380 fprintf(f, "#define infostring string\n");
5381 fprintf(f, "#define filestream float\n");
5382 fprintf(f, "#endif\n");
5383
5384
5385 fprintf(f, "\n\n//Common entry points\n");
5386 #define QCEXTFUNC(n,t) fprintf(f, t " " #n "\n");
5387 QCEXTFUNCS_COMMON
5388 if (targs & (SS|CS))
5389 {
5390 QCEXTFUNCS_GAME
5391 }
5392
5393 if (targs & SS)
5394 {
5395 fprintf(f, "\n\n//Serverside entry points\n");
5396 QCEXTFUNCS_SV
5397 }
5398 if (targs & CS)
5399 {
5400 fprintf(f, "\n\n//CSQC entry points\n");
5401 QCEXTFUNCS_CS
5402 }
5403 #undef QCEXTFUNC
5404
5405 #define QCEXTGLOBAL_INT(n) fprintf(f, "int " #n ";\n");
5406 #define QCEXTGLOBAL_FLOAT(n) fprintf(f, "float " #n ";\n");
5407 #define QCEXTGLOBAL_VECTOR(n) fprintf(f, "vector " #n ";\n");
5408 QCEXTGLOBALS_COMMON
5409 if (targs & (CS|SS))
5410 {
5411 QCEXTGLOBALS_GAME
5412 }
5413 #undef QCEXTGLOBAL_INT
5414 #undef QCEXTGLOBAL_FLOAT
5415 #undef QCEXTGLOBAL_VECTOR
5416
5417 fprintf(f, "const float FALSE = 0;\n");
5418 fprintf(f, "const float TRUE = 1;\n");
5419
5420 if (targs & (CS|SS))
5421 {
5422 fprintf(f, "const float STAT_HEALTH = 0; /* Player's health. */\n");
5423 // fprintf(f, "const float STAT_FRAGS = 1; /* unused */\n");
5424 fprintf(f, "const float STAT_WEAPONMODELI = 2; /* This is the modelindex of the current viewmodel (renamed from the original name 'STAT_WEAPON' due to confusions). */\n");
5425 fprintf(f, "const float STAT_AMMO = 3; /* player.currentammo */\n");
5426 fprintf(f, "const float STAT_ARMOR = 4;\n");
5427 fprintf(f, "const float STAT_WEAPONFRAME = 5;\n");
5428 fprintf(f, "const float STAT_SHELLS = 6;\n");
5429 fprintf(f, "const float STAT_NAILS = 7;\n");
5430 fprintf(f, "const float STAT_ROCKETS = 8;\n");
5431 fprintf(f, "const float STAT_CELLS = 9;\n");
5432 fprintf(f, "const float STAT_ACTIVEWEAPON = 10; /* player.weapon */\n");
5433 fprintf(f, "const float STAT_TOTALSECRETS = 11;\n");
5434 fprintf(f, "const float STAT_TOTALMONSTERS = 12;\n");
5435 fprintf(f, "const float STAT_FOUNDSECRETS = 13;\n");
5436 fprintf(f, "const float STAT_KILLEDMONSTERS = 14;\n");
5437 fprintf(f, "const float STAT_ITEMS = 15; /* self.items | (self.items2<<23). In order to decode this stat properly, you need to use getstatbits(STAT_ITEMS,0,23) to read self.items, and getstatbits(STAT_ITEMS,23,11) to read self.items2 or getstatbits(STAT_ITEMS,28,4) to read the visible part of serverflags, whichever is applicable. */\n");
5438 fprintf(f, "const float STAT_VIEWHEIGHT = 16; /* player.view_ofs_z */\n");
5439 fprintf(f, "const float STAT_VIEW2 = 20; /* This stat contains the number of the entity in the server's .view2 field. */\n");
5440 fprintf(f, "const float STAT_IDEALPITCH = 25;\n");
5441 fprintf(f, "const float STAT_PUNCHANGLE_X = 26;\n");
5442 fprintf(f, "const float STAT_PUNCHANGLE_Y = 27;\n");
5443 fprintf(f, "const float STAT_PUNCHANGLE_Z = 28;\n");
5444
5445 fprintf(f, "const float SOLID_BBOX = %i;\n", SOLID_BBOX);
5446 fprintf(f, "const float SOLID_BSP = %i;\n", SOLID_BSP);
5447 fprintf(f, "const float SOLID_NOT = %i;\n", SOLID_NOT);
5448 fprintf(f, "const float SOLID_SLIDEBOX = %i;\n", SOLID_SLIDEBOX);
5449 fprintf(f, "const float SOLID_TRIGGER = %i;\n", SOLID_TRIGGER);
5450
5451 fprintf(f, "const float MOVETYPE_NONE = %i;\n", MOVETYPE_NONE);
5452 fprintf(f, "const float MOVETYPE_WALK = %i;\n", MOVETYPE_WALK);
5453 fprintf(f, "const float MOVETYPE_STEP = %i;\n", MOVETYPE_STEP);
5454 fprintf(f, "const float MOVETYPE_FLY = %i;\n", MOVETYPE_FLY);
5455 fprintf(f, "const float MOVETYPE_TOSS = %i;\n", MOVETYPE_TOSS);
5456 fprintf(f, "const float MOVETYPE_PUSH = %i;\n", MOVETYPE_PUSH);
5457 fprintf(f, "const float MOVETYPE_NOCLIP = %i;\n", MOVETYPE_NOCLIP);
5458 fprintf(f, "const float MOVETYPE_FLYMISSILE = %i;\n", MOVETYPE_FLYMISSILE);
5459 fprintf(f, "const float MOVETYPE_BOUNCE = %i;\n", MOVETYPE_BOUNCE);
5460
5461 fprintf(f, "const float CONTENT_EMPTY = %i;\n", CONTENTS_EMPTY);
5462 fprintf(f, "const float CONTENT_SOLID = %i;\n", CONTENTS_SOLID);
5463 fprintf(f, "const float CONTENT_WATER = %i;\n", CONTENTS_WATER);
5464 fprintf(f, "const float CONTENT_SLIME = %i;\n", CONTENTS_SLIME);
5465 fprintf(f, "const float CONTENT_LAVA = %i;\n", CONTENTS_LAVA);
5466 fprintf(f, "const float CONTENT_SKY = %i;\n", CONTENTS_SKY);
5467
5468 fprintf(f, "__used var float physics_mode = 2;\n");
5469
5470 fprintf(f, "const float TE_SPIKE = %i;\n", TE_SPIKE);
5471 fprintf(f, "const float TE_SUPERSPIKE = %i;\n", TE_SUPERSPIKE);
5472 fprintf(f, "const float TE_GUNSHOT = %i;\n", TE_GUNSHOT);
5473 fprintf(f, "const float TE_EXPLOSION = %i;\n", TE_EXPLOSION);
5474 fprintf(f, "const float TE_TAREXPLOSION = %i;\n", TE_TAREXPLOSION);
5475 fprintf(f, "const float TE_LIGHTNING1 = %i;\n", TE_LIGHTNING1);
5476 fprintf(f, "const float TE_LIGHTNING2 = %i;\n", TE_LIGHTNING2);
5477 fprintf(f, "const float TE_WIZSPIKE = %i;\n", TE_WIZSPIKE);
5478 fprintf(f, "const float TE_KNIGHTSPIKE = %i;\n", TE_KNIGHTSPIKE);
5479 fprintf(f, "const float TE_LIGHTNING3 = %i;\n", TE_LIGHTNING3);
5480 fprintf(f, "const float TE_LAVASPLASH = %i;\n", TE_LAVASPLASH);
5481 fprintf(f, "const float TE_TELEPORT = %i;\n", TE_TELEPORT);
5482 fprintf(f, "const float TE_EXPLOSION2 = %i;\n", TE_EXPLOSION2);
5483 fprintf(f, "const float TE_BEAM = %i;\n", TE_BEAM);
5484
5485
5486 fprintf(f, "const float MF_ROCKET = %#x;\n", EF_ROCKET);
5487 fprintf(f, "const float MF_GRENADE = %#x;\n", EF_GRENADE);
5488 fprintf(f, "const float MF_GIB = %#x;\n", EF_GIB);
5489 fprintf(f, "const float MF_ROTATE = %#x;\n", EF_ROTATE);
5490 fprintf(f, "const float MF_TRACER = %#x;\n", EF_TRACER);
5491 fprintf(f, "const float MF_ZOMGIB = %#x;\n", EF_ZOMGIB);
5492 fprintf(f, "const float MF_TRACER2 = %#x;\n", EF_TRACER2);
5493 fprintf(f, "const float MF_TRACER3 = %#x;\n", EF_TRACER3);
5494
5495
5496 fprintf(f, "const float EF_BRIGHTFIELD = %i;\n", EF_BRIGHTFIELD);
5497 fprintf(f, "const float EF_MUZZLEFLASH = %i;\n", EF_MUZZLEFLASH);
5498 fprintf(f, "const float EF_BRIGHTLIGHT = %i;\n", EF_BRIGHTLIGHT);
5499 fprintf(f, "const float EF_DIMLIGHT = %i;\n", EF_DIMLIGHT);
5500
5501 fprintf(f, "const float FL_FLY = %i;\n", FL_FLY);
5502 fprintf(f, "const float FL_SWIM = %i;\n", FL_SWIM);
5503 fprintf(f, "const float FL_CLIENT = %i;\n", FL_CLIENT);
5504 fprintf(f, "const float FL_INWATER = %i;\n", FL_INWATER);
5505 fprintf(f, "const float FL_MONSTER = %i;\n", FL_MONSTER);
5506 fprintf(f, "const float FL_GODMODE = %i;\n", FL_GODMODE);
5507 fprintf(f, "const float FL_NOTARGET = %i;\n", FL_NOTARGET);
5508 fprintf(f, "const float FL_ITEM = %i;\n", FL_ITEM);
5509 fprintf(f, "const float FL_ONGROUND = %i;\n", FL_ONGROUND);
5510 fprintf(f, "const float FL_PARTIALGROUND = %i;\n", FL_PARTIALGROUND);
5511 fprintf(f, "const float FL_WATERJUMP = %i;\n", FL_WATERJUMP);
5512 fprintf(f, "const float FL_JUMPRELEASED = %i;\n", FL_JUMPRELEASED);
5513
5514 fprintf(f, "const float ATTN_NONE = %i;\n", 0);
5515 fprintf(f, "const float ATTN_NORM = %i;\n", 1);
5516 fprintf(f, "const float ATTN_IDLE = %i;\n", 2);
5517 fprintf(f, "const float ATTN_STATIC = %i;\n", 3);
5518
5519 fprintf(f, "const float CHAN_AUTO = %i;\n", 0);
5520 fprintf(f, "const float CHAN_WEAPON = %i;\n", 1);
5521 fprintf(f, "const float CHAN_VOICE = %i;\n", 2);
5522 fprintf(f, "const float CHAN_ITEM = %i;\n", 3);
5523 fprintf(f, "const float CHAN_BODY = %i;\n", 4);
5524 }
5525
5526 fprintf(f, "const float STAT_USER = 32; /* Custom user stats start here (lower values are reserved for engine use). */\n");
5527 //these can be used for both custom stats and for reflection
5528 fprintf(f, "const float EV_VOID = %i;\n", ev_void);
5529 fprintf(f, "const float EV_STRING = %i;\n", ev_string);
5530 fprintf(f, "const float EV_FLOAT = %i;\n", ev_float);
5531 fprintf(f, "const float EV_VECTOR = %i;\n", ev_vector);
5532 fprintf(f, "const float EV_ENTITY = %i;\n", ev_entity);
5533 fprintf(f, "const float EV_FIELD = %i;\n", ev_field);
5534 fprintf(f, "const float EV_FUNCTION = %i;\n", ev_function);
5535 fprintf(f, "const float EV_POINTER = %i;\n", ev_pointer);
5536 fprintf(f, "const float EV_INTEGER = %i;\n", ev_ext_integer);
5537
5538 #define QCEXTFIELD(n,t) fprintf(f, "%s %s;\n", t, #n);
5539 //extra fields
5540 fprintf(f, "\n\n//Supported Extension fields\n");
5541 QCEXTFIELDS_ALL
5542 if (targs & (SS|CS)) {QCEXTFIELDS_GAME}
5543 if (targs & (SS)) {QCEXTFIELDS_SS}
5544 #undef QCEXTFIELD
5545
5546 if (targs & SS)
5547 {
5548 fprintf(f, ".float style;\n"); //not used by the engine, but is used by tools etc.
5549 fprintf(f, ".float light_lev;\n"); //ditto.
5550
5551 //extra constants
5552 fprintf(f, "\n\n//Supported Extension Constants\n");
5553
5554 fprintf(f, "const float CLIENTTYPE_DISCONNECT = "STRINGIFY(0)";\n");
5555 fprintf(f, "const float CLIENTTYPE_REAL = "STRINGIFY(1)";\n");
5556 fprintf(f, "const float CLIENTTYPE_BOT = "STRINGIFY(2)";\n");
5557 fprintf(f, "const float CLIENTTYPE_NOTCLIENT = "STRINGIFY(3)";\n");
5558
5559 fprintf(f, "const float DAMAGE_AIM = %i;\n", DAMAGE_AIM);
5560 fprintf(f, "const float DAMAGE_NO = %i;\n", DAMAGE_NO);
5561 fprintf(f, "const float DAMAGE_YES = %i;\n", DAMAGE_YES);
5562
5563 fprintf(f, "const float MSG_BROADCAST = %i;\n", MSG_BROADCAST);
5564 fprintf(f, "const float MSG_ONE = %i;\n", MSG_ONE);
5565 fprintf(f, "const float MSG_ALL = %i;\n", MSG_ALL);
5566 fprintf(f, "const float MSG_INIT = %i;\n", MSG_INIT);
5567 fprintf(f, "const float MSG_MULTICAST = %i;\n", MSG_EXT_MULTICAST);
5568 fprintf(f, "const float MSG_ENTITY = %i;\n", MSG_EXT_ENTITY);
5569
5570 fprintf(f, "const float MSG_MULTICAST = %i;\n", 4);
5571 fprintf(f, "const float MULTICAST_ALL = %i;\n", MULTICAST_ALL_U);
5572 fprintf(f, "const float MULTICAST_PVS = %i;\n", MULTICAST_PVS_U);
5573 fprintf(f, "const float MULTICAST_ONE = %i;\n", MULTICAST_ONE_U);
5574 fprintf(f, "const float MULTICAST_ALL_R = %i;\n", MULTICAST_ALL_R);
5575 fprintf(f, "const float MULTICAST_PVS_R = %i;\n", MULTICAST_PVS_R);
5576 fprintf(f, "const float MULTICAST_ONE_R = %i;\n", MULTICAST_ONE_R);
5577 fprintf(f, "const float MULTICAST_INIT = %i;\n", MULTICAST_INIT);
5578 }
5579
5580 fprintf(f, "const float FILE_READ = "STRINGIFY(0)";\n");
5581 fprintf(f, "const float FILE_APPEND = "STRINGIFY(1)";\n");
5582 fprintf(f, "const float FILE_WRITE = "STRINGIFY(2)";\n");
5583
5584 //this is annoying. builtins from pr_cmds.c are not known here.
5585 if (targs & (CS|SS))
5586 {
5587 const char *conflictprefix = "";//(targs&CS)?"":"//";
5588 fprintf(f, "\n\n//Vanilla Builtin list (reduced, so as to avoid conflicts)\n");
5589 fprintf(f, "void(vector) makevectors = #1;\n");
5590 fprintf(f, "void(entity,vector) setorigin = #2;\n");
5591 fprintf(f, "void(entity,string) setmodel = #3;\n");
5592 fprintf(f, "void(entity,vector,vector) setsize = #4;\n");
5593 fprintf(f, "float() random = #7;\n");
5594 fprintf(f, "%svoid(entity e, float chan, string samp, float vol, float atten, optional float speedpct, optional float flags, optional float timeofs) sound = #8;\n", conflictprefix);
5595 fprintf(f, "vector(vector) normalize = #9;\n");
5596 fprintf(f, "void(string e) error = #10;\n");
5597 fprintf(f, "void(string n) objerror = #11;\n");
5598 fprintf(f, "float(vector) vlen = #12;\n");
5599 fprintf(f, "float(vector fwd) vectoyaw = #13;\n");
5600 fprintf(f, "entity() spawn = #14;\n");
5601 fprintf(f, "void(entity e) remove = #15;\n");
5602 fprintf(f, "void(vector v1, vector v2, float flags, entity ent) traceline = #16;\n");
5603 if (targs&SS)
5604 fprintf(f, "entity() checkclient = #17;\n");
5605 fprintf(f, "entity(entity start, .string fld, string match) find = #18;\n");
5606 fprintf(f, "string(string s) precache_sound = #19;\n");
5607 fprintf(f, "string(string s) precache_model = #20;\n");
5608 if (targs&SS)
5609 fprintf(f, "%svoid(entity client, string s) stuffcmd = #21;\n", conflictprefix);
5610 fprintf(f, "%sentity(vector org, float rad, optional .entity chainfield) findradius = #22;\n", conflictprefix);
5611 if (targs&SS)
5612 {
5613 fprintf(f, "%svoid(string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7, optional string s8) bprint = #23;\n", conflictprefix);
5614 fprintf(f, "%svoid(entity pl, string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7) sprint = #24;\n", conflictprefix);
5615 }
5616 fprintf(f, "void(string,...) dprint = #25;\n");
5617 fprintf(f, "string(float) ftos = #26;\n");
5618 fprintf(f, "string(vector) vtos = #27;\n");
5619 fprintf(f, "%sfloat(float yaw, float dist, optional float settraceglobals) walkmove = #32;\n", conflictprefix);
5620 fprintf(f, "float() droptofloor = #34;\n");
5621 fprintf(f, "%svoid(float lightstyle, string stylestring, optional vector rgb) lightstyle = #35;\n", conflictprefix);
5622 fprintf(f, "float(float n) rint = #36;\n");
5623 fprintf(f, "float(float n) floor = #37;\n");
5624 fprintf(f, "float(float n) ceil = #38;\n");
5625 fprintf(f, "float(entity e) checkbottom = #40;\n");
5626 fprintf(f, "float(vector point) pointcontents = #41;\n");
5627 fprintf(f, "float(float n) fabs = #43;\n");
5628 if (targs&SS)
5629 fprintf(f, "vector(entity e, float speed) aim = #44;\n");
5630 fprintf(f, "float(string) cvar = #45;\n");
5631 fprintf(f, "void(string,...) localcmd = #46;\n");
5632 fprintf(f, "entity(entity) nextent = #47;\n");
5633 fprintf(f, "void(vector o, vector d, float color, float count) particle = #48;\n");
5634 fprintf(f, "void() changeyaw = #49;\n");
5635 fprintf(f, "%svector(vector fwd, optional vector up) vectoangles = #51;\n", conflictprefix);
5636 if (targs&SS)
5637 {
5638 fprintf(f, "void(float to, float val) WriteByte = #52;\n");
5639 fprintf(f, "void(float to, float val) WriteChar = #53;\n");
5640 fprintf(f, "void(float to, float val) WriteShort = #54;\n");
5641 fprintf(f, "void(float to, float val) WriteLong = #55;\n");
5642 fprintf(f, "void(float to, float val) WriteCoord = #56;\n");
5643 fprintf(f, "void(float to, float val) WriteAngle = #57;\n");
5644 fprintf(f, "void(float to, string val) WriteString = #58;\n");
5645 fprintf(f, "void(float to, entity val) WriteEntity = #59;\n");
5646 }
5647 fprintf(f, "void(float step) movetogoal = #67;\n");
5648 fprintf(f, "string(string s) precache_file = #68;\n");
5649 fprintf(f, "void(entity e) makestatic = #69;\n");
5650 if (targs&SS)
5651 fprintf(f, "void(string mapname, optional string newmapstartspot) changelevel = #70;\n");
5652 fprintf(f, "void(string var, string val) cvar_set = #72;\n");
5653 if (targs&SS)
5654 fprintf(f, "void(entity ent, string text, optional string text2, optional string text3, optional string text4, optional string text5, optional string text6, optional string text7) centerprint = #73;\n");
5655 fprintf(f, "void (vector pos, string samp, float vol, float atten) ambientsound = #74;\n");
5656 fprintf(f, "string(string str) precache_model2 = #75;\n");
5657 fprintf(f, "string(string str) precache_sound2 = #76;\n");
5658 fprintf(f, "string(string str) precache_file2 = #77;\n");
5659 if (targs&SS)
5660 fprintf(f, "void(entity player) setspawnparms = #78;\n");
5661 }
5662 if (targs & MN)
5663 {
5664 fprintf(f, "void(string e) error = #2;\n");
5665 fprintf(f, "void(string n) objerror = #3;\n");
5666 fprintf(f, "float(vector) vlen = #9;\n");
5667 fprintf(f, "float(vector fwd) vectoyaw = #10;\n");
5668 fprintf(f, "vector(vector fwd, optional vector up) vectoangles = #11;\n");
5669 fprintf(f, "float() random = #12;\n");
5670 fprintf(f, "void(string,...) localcmd = #13;\n");
5671 fprintf(f, "float(string) cvar = #14;\n");
5672 fprintf(f, "void(string var, string val) cvar_set = #15;\n");
5673 fprintf(f, "void(string,...) dprint = #16;\n");
5674 fprintf(f, "string(float) ftos = #17;\n");
5675 fprintf(f, "float(float n) fabs = #18;\n");
5676 fprintf(f, "string(vector) vtos = #19;\n");
5677 fprintf(f, "entity() spawn = #22;\n");
5678 fprintf(f, "void(entity e) remove = #23;\n");
5679 fprintf(f, "entity(entity start, .string fld, string match) find = #24;\n");
5680 fprintf(f, "string(string s) precache_file = #28;\n");
5681 fprintf(f, "string(string s) precache_sound = #29;\n");
5682 fprintf(f, "float(float n) rint = #34;\n");
5683 fprintf(f, "float(float n) floor = #35;\n");
5684 fprintf(f, "float(float n) ceil = #36;\n");
5685 fprintf(f, "entity(entity) nextent = #37;\n");
5686 fprintf(f, "float() clientstate = #62;\n");
5687 }
5688
5689 for (j = 0; j < 2; j++)
5690 {
5691 if (j)
5692 fprintf(f, "\n\n//Builtin Stubs List (these are present for simpler compatibility, but not properly supported in QuakeSpasm at this time).\n/*\n");
5693 else
5694 fprintf(f, "\n\n//Builtin list\n");
5695 for (i = 0; i < sizeof(extensionbuiltins)/sizeof(extensionbuiltins[0]); i++)
5696 {
5697 if ((targs & CS) && extensionbuiltins[i].csqcfunc)
5698 ;
5699 else if ((targs & SS) && extensionbuiltins[i].ssqcfunc)
5700 ;
5701 else
5702 continue;
5703
5704 if (j != (extensionbuiltins[i].desc?!strncmp(extensionbuiltins[i].desc, "stub.", 5):0))
5705 continue;
5706 fprintf(f, "%s %s = #%i;", extensionbuiltins[i].typestr, extensionbuiltins[i].name, extensionbuiltins[i].documentednumber);
5707 if (extensionbuiltins[i].desc && !j)
5708 {
5709 const char *line = extensionbuiltins[i].desc;
5710 const char *term;
5711 fprintf(f, " /*");
5712 while(*line)
5713 {
5714 fprintf(f, "\n\t\t");
5715 term = line;
5716 while(*term && *term != '\n')
5717 term++;
5718 fwrite(line, 1, term - line, f);
5719 if (*term == '\n')
5720 {
5721 term++;
5722 }
5723 line = term;
5724 }
5725 fprintf(f, " */\n\n");
5726 }
5727 else
5728 fprintf(f, "\n");
5729 }
5730 if (j)
5731 fprintf(f, "*/\n");
5732 }
5733
5734 fprintf(f, "\n\n//Reset this back to normal.\n");
5735 fprintf(f, "#pragma noref 0\n");
5736 fclose(f);
5737 }
5738