Fixed windows input (bad,bad hack)
[divverent/darkplaces.git] / prvm_cmds.c
1 // AK
2 // Basically every vm builtin cmd should be in here.
3 // All 3 builtin list and extension lists can be found here
4 // cause large (I think they will) are from pr_cmds the same copyright like in pr_cms
5 // also applies here
6
7
8 /*
9 ============================================================================
10 common cmd list:
11 =================
12
13                 checkextension(string)
14                 error(...[string])
15                 objerror(...[string)
16                 print(...[strings])
17                 bprint(...[string])
18                 sprint(float clientnum,...[string])
19                 centerprint(...[string])
20 vector  normalize(vector)
21 float   vlen(vector)
22 float   vectoyaw(vector)
23 vector  vectoangles(vector)
24 float   random()
25                 cmd(string)
26                 float cvar (string)
27                 cvar_set (string,string)
28                 dprint(...[string])
29 string  ftos(float)
30 float   fabs(float)
31 string  vtos(vector)
32 string  etos(entity)
33 float   stof(...[string])
34 entity  spawn()
35 entity  remove()
36 entity  find(entity start, .string field, string match)
37
38 entity  findfloat(entity start, .float field, string match)
39 entity  findentity(entity start, .entity field, string match)
40
41 entity  findchain(.string field, string match)
42
43 entity  findchainfloat(.string field, float match)
44 entity  findchainentity(.string field, entity match)
45   
46 string  precache_file(string)
47 string  precache_sound (string sample)
48                 coredump()
49                 traceon()
50                 traceoff()
51                 eprint(entity float)
52 float   rint(float)
53 float   floor(float)
54 float   ceil(float)
55 entity  nextent(entity)
56 float   sin(float)
57 float   cos(float)
58 float   sqrt(float)
59                 randomvec()
60 float   registercvar (string name, string value)
61 float   min(float a, float b, ...[float])
62 float   max(float a, float b, ...[float])
63 float   bound(float min, float value, float max)
64 float   pow(float a, float b)
65                 copyentity(entity src, entity dst)
66 float   fopen(string filename, float mode)
67                 fclose(float fhandle)
68 string  fgets(float fhandle)
69                 fputs(float fhandle, string s)
70 float   strlen(string s)
71 string  strcat(string s1, string s2)
72 string  substring(string s, float start, float length)
73 vector  stov(string s)
74 string  strzone(string s)
75                 strzone(string s)
76 float   isserver()
77 float   clientcount()
78 float   clientstate()
79                 clientcommand(float client, string s) (for client and menu)
80 float   tokenize(string s)
81                 changelevel(string map)
82                 localsound(string sample)
83
84 perhaps only : Menu : WriteMsg 
85 ===============================
86
87                 WriteByte(float data, float dest, float desto)
88                 WriteChar(float data, float dest, float desto)
89                 WriteShort(float data, float dest, float desto)
90                 WriteLong(float data, float dest, float desto)
91                 WriteAngle(float data, float dest, float desto)
92                 WriteCoord(float data, float dest, float desto)
93                 WriteString(string data, float dest, float desto)
94                 WriteEntity(entity data, float dest, float desto)
95                 
96 Client & Menu : draw functions 
97 ===============================
98
99 float   iscachedpic(string pic)
100 string  precache_pic(string pic) 
101                 freepic(string s)
102 float   drawcharacter(vector position, float character, vector scale, vector rgb, float alpha, float flag)
103 float   drawstring(vector position, string text, vector scale, vector rgb, float alpha, float flag)
104 float   drawpic(vector position, string pic, vector size, vector rgb, float alpha, float flag)
105 float   drawfill(vector position, vector size, vector rgb, float alpha, float flag)
106
107 */
108
109 #include "quakedef.h"
110 #include "progdefs.h"
111 #include "clprogdefs.h"
112 #include "mprogdefs.h"
113
114 //============================================================================
115 // nice helper macros
116
117 #ifndef VM_NOPARMCHECK
118 #define VM_SAFEPARMCOUNT(p,f)   if(prog->argc != p) PRVM_ERROR(#f "wrong parameter count (" #p "expected ) !\n")
119 #else
120 #define VM_SAFEPARMCOUNT(p,f)
121 #endif
122
123 #define VM_RETURN_EDICT(e)              (((int *)prog->globals)[OFS_RETURN] = PRVM_EDICT_TO_PROG(e))
124
125 #define VM_STRINGS_MEMPOOL              vm_strings_mempool[PRVM_GetProgNr()]
126
127 #define e10 0,0,0,0,0,0,0,0,0,0
128 #define e100 e10,e10,e10,e10,e10,e10,e10,e10,e10,e10
129 #define e1000 e100,e100,e100,e100,e100,e100,e100,e100,e100,e100
130
131 //============================================================================
132 // Common
133
134 // string zone mempool
135 mempool_t *vm_strings_mempool[PRVM_MAXPROGS];
136
137 // temp string handling
138 // LordHavoc: added this to semi-fix the problem of using many ftos calls in a print
139 #define STRINGTEMP_BUFFERS 16
140 #define STRINGTEMP_LENGTH 4096
141 static char vm_string_temp[STRINGTEMP_BUFFERS][STRINGTEMP_LENGTH];
142 static int vm_string_tempindex = 0;
143
144 // qc cvar 
145 #define MAX_QC_CVARS 128 * PRVM_MAXPROGS
146 cvar_t vm_qc_cvar[MAX_QC_CVARS];
147 int vm_currentqc_cvar;
148
149 // qc file handling
150 #define MAX_VMFILES             256
151 #define MAX_PRVMFILES   MAX_VMFILES * PRVM_MAXPROGS
152 #define VM_FILES ((qfile_t**)(vm_files + PRVM_GetProgNr() * MAX_VMFILES))
153
154 qfile_t *vm_files[MAX_PRVMFILES];
155
156 static char *VM_GetTempString(void)
157 {
158         char *s;
159         s = vm_string_temp[vm_string_tempindex];
160         vm_string_tempindex = (vm_string_tempindex + 1) % STRINGTEMP_BUFFERS;
161         return s;
162 }
163
164 void VM_CheckEmptyString (char *s)
165 {
166         if (s[0] <= ' ')
167                 PRVM_ERROR ("%s: Bad string", PRVM_NAME);
168 }
169
170 //============================================================================
171 //BUILT-IN FUNCTIONS
172
173 void VM_VarString(int first, char *out, int outlength)
174 {
175         int i;
176         const char *s;
177         char *outend;
178
179         outend = out + outlength - 1;
180         for (i = first;i < pr_argc && out < outend;i++)
181         {
182                 s = PRVM_G_STRING((OFS_PARM0+i*3));
183                 while (out < outend && *s)
184                         *out++ = *s++;
185         }
186         *out++ = 0;
187 }
188
189 /*
190 =================
191 VM_checkextension
192
193 returns true if the extension is supported by the server
194
195 checkextension(extensionname)
196 =================
197 */
198
199 // kind of helper function
200 static qboolean checkextension(char *name)
201 {
202         int len;
203         char *e, *start;
204         len = strlen(name);
205
206         for (e = prog->extensionstring;*e;e++)
207         {
208                 while (*e == ' ')
209                         e++;
210                 if (!*e)
211                         break;
212                 start = e;
213                 while (*e && *e != ' ')
214                         e++;
215                 if (e - start == len)
216                         if (!strncasecmp(start, name, len))
217                         {
218                                 return true;
219                         }
220         }
221         return false;
222 }
223
224 void VM_checkextension (void)
225 {
226         VM_SAFEPARMCOUNT(1,VM_checkextension);
227
228         PRVM_G_FLOAT(OFS_RETURN) = checkextension(PRVM_G_STRING(OFS_PARM0));
229 }
230
231 /*
232 =================
233 VM_error
234
235 This is a TERMINAL error, which will kill off the entire prog.
236 Dumps self.
237
238 error(value)
239 =================
240 */
241 void VM_error (void)
242 {
243         prvm_edict_t    *ed;
244         char string[STRINGTEMP_LENGTH];
245
246         VM_VarString(0, string, sizeof(string));
247         Con_Printf ("======%S ERROR in %s:\n%s\n", PRVM_NAME, PRVM_GetString(prog->xfunction->s_name), string);
248         if(prog->self)
249         {
250                 ed = PRVM_G_EDICT(prog->self->ofs);
251                 PRVM_ED_Print (ed);
252         }
253
254         PRVM_ERROR ("%s: Program error", PRVM_NAME);
255 }
256
257 /*
258 =================
259 VM_objerror
260
261 Dumps out self, then an error message.  The program is aborted and self is
262 removed, but the level can continue.
263
264 objerror(value)
265 =================
266 */
267 void VM_objerror (void)
268 {
269         prvm_edict_t    *ed;
270         char string[STRINGTEMP_LENGTH];
271
272         VM_VarString(0, string, sizeof(string));
273         Con_Printf ("======%s OBJECT ERROR in %s:\n%s\n", PRVM_NAME, PRVM_GetString(prog->xfunction->s_name), string);
274         if(prog->self)
275         {
276                 ed = PRVM_G_EDICT (prog->self->ofs);
277                 PRVM_ED_Print (ed);
278
279                 PRVM_ED_Free (ed);
280         }
281         else
282                 // objerror has to display the object fields -> else call
283                 PRVM_ERROR ("VM_objecterror: self not defined !\n");
284 }
285
286 /*
287 =================
288 VM_print (actually used only by client and menu)
289
290 print to console
291
292 print(string)
293 =================
294 */
295 void VM_print (void)
296 {
297         char string[STRINGTEMP_LENGTH];
298
299         VM_VarString(0, string, sizeof(string));
300         Con_Print(string);
301 }
302
303 /*
304 =================
305 VM_bprint
306
307 broadcast print to everyone on server
308
309 bprint(...[string])
310 =================
311 */
312 void VM_bprint (void)
313 {
314         char string[STRINGTEMP_LENGTH];
315
316         if(!sv.active)
317         {
318                 Con_Printf("VM_bprint: game is not server(%s) !", PRVM_NAME);
319                 return;
320         }
321
322         VM_VarString(0, string, sizeof(string));
323         SV_BroadcastPrintf("%s", string);
324 }
325
326 /*
327 =================
328 VM_sprint (menu & client but only if server.active == true)
329
330 single print to a specific client
331
332 sprint(float clientnum,...[string])
333 =================
334 */
335 void VM_sprint (void)
336 {
337         client_t        *client;
338         int                     clientnum;
339         char string[STRINGTEMP_LENGTH];
340
341         //find client for this entity
342         clientnum = PRVM_G_FLOAT(OFS_PARM0);
343         if (!sv.active  || clientnum < 0 || clientnum >= svs.maxclients || !svs.clients[clientnum].active)
344         {
345                 Con_Printf("VM_sprint: %s: invalid client or server is not active !", PRVM_NAME);
346                 return;
347         }
348         
349         client = svs.clients + clientnum;
350         if (!client->netconnection)
351                 return;
352         VM_VarString(1, string, sizeof(string));
353         MSG_WriteChar(&client->message,svc_print);
354         MSG_WriteString(&client->message, string);
355 }
356
357 /*
358 =================
359 VM_centerprint
360
361 single print to the screen
362
363 centerprint(clientent, value)
364 =================
365 */
366 void VM_centerprint (void)
367 {
368         char string[STRINGTEMP_LENGTH];
369
370         VM_VarString(0, string, sizeof(string));
371         SCR_CenterPrint(string);
372 }
373
374 /*
375 =================
376 VM_normalize
377
378 vector normalize(vector)
379 =================
380 */
381 void VM_normalize (void)
382 {
383         float   *value1;
384         vec3_t  newvalue;
385         float   new;
386
387         VM_SAFEPARMCOUNT(1,VM_normalize);
388
389         value1 = PRVM_G_VECTOR(OFS_PARM0);
390
391         new = value1[0] * value1[0] + value1[1] * value1[1] + value1[2]*value1[2];
392         new = sqrt(new);
393
394         if (new == 0)
395                 newvalue[0] = newvalue[1] = newvalue[2] = 0;
396         else
397         {
398                 new = 1/new;
399                 newvalue[0] = value1[0] * new;
400                 newvalue[1] = value1[1] * new;
401                 newvalue[2] = value1[2] * new;
402         }
403
404         VectorCopy (newvalue, PRVM_G_VECTOR(OFS_RETURN));
405 }
406
407 /*
408 =================
409 VM_vlen
410
411 scalar vlen(vector)
412 =================
413 */
414 void VM_vlen (void)
415 {
416         float   *value1;
417         float   new;
418
419         VM_SAFEPARMCOUNT(1,VM_vlen);
420
421         value1 = PRVM_G_VECTOR(OFS_PARM0);
422
423         new = value1[0] * value1[0] + value1[1] * value1[1] + value1[2]*value1[2];
424         new = sqrt(new);
425
426         PRVM_G_FLOAT(OFS_RETURN) = new;
427 }
428
429 /*
430 =================
431 VM_vectoyaw
432
433 float vectoyaw(vector)
434 =================
435 */
436 void VM_vectoyaw (void)
437 {
438         float   *value1;
439         float   yaw;
440
441         VM_SAFEPARMCOUNT(1,VM_vectoyaw);
442
443         value1 = PRVM_G_VECTOR(OFS_PARM0);
444
445         if (value1[1] == 0 && value1[0] == 0)
446                 yaw = 0;
447         else
448         {
449                 yaw = (int) (atan2(value1[1], value1[0]) * 180 / M_PI);
450                 if (yaw < 0)
451                         yaw += 360;
452         }
453
454         PRVM_G_FLOAT(OFS_RETURN) = yaw;
455 }
456
457
458 /*
459 =================
460 VM_vectoangles
461
462 vector vectoangles(vector)
463 =================
464 */
465 void VM_vectoangles (void)
466 {
467         float   *value1;
468         float   forward;
469         float   yaw, pitch;
470
471         VM_SAFEPARMCOUNT(1,VM_vectoangles);
472
473         value1 = PRVM_G_VECTOR(OFS_PARM0);
474
475         if (value1[1] == 0 && value1[0] == 0)
476         {
477                 yaw = 0;
478                 if (value1[2] > 0)
479                         pitch = 90;
480                 else
481                         pitch = 270;
482         }
483         else
484         {
485                 // LordHavoc: optimized a bit
486                 if (value1[0])
487                 {
488                         yaw = (atan2(value1[1], value1[0]) * 180 / M_PI);
489                         if (yaw < 0)
490                                 yaw += 360;
491                 }
492                 else if (value1[1] > 0)
493                         yaw = 90;
494                 else
495                         yaw = 270;
496
497                 forward = sqrt(value1[0]*value1[0] + value1[1]*value1[1]);
498                 pitch = (int) (atan2(value1[2], forward) * 180 / M_PI);
499                 if (pitch < 0)
500                         pitch += 360;
501         }
502
503         PRVM_G_FLOAT(OFS_RETURN+0) = pitch;
504         PRVM_G_FLOAT(OFS_RETURN+1) = yaw;
505         PRVM_G_FLOAT(OFS_RETURN+2) = 0;
506 }
507
508 /*
509 =================
510 VM_random
511
512 Returns a number from 0<= num < 1
513
514 float random()
515 =================
516 */
517 void VM_random (void)
518 {
519         float           num;
520
521         VM_SAFEPARMCOUNT(0,VM_random);
522
523         num = (rand ()&0x7fff) / ((float)0x7fff);
524
525         PRVM_G_FLOAT(OFS_RETURN) = num;
526 }
527
528 /*
529 =================
530 PF_sound
531
532 Each entity can have eight independant sound sources, like voice,
533 weapon, feet, etc.
534
535 Channel 0 is an auto-allocate channel, the others override anything
536 already running on that entity/channel pair.
537
538 An attenuation of 0 will play full volume everywhere in the level.
539 Larger attenuations will drop off.
540
541 =================
542 */
543 /*
544 void PF_sound (void)
545 {
546         char            *sample;
547         int                     channel;
548         edict_t         *entity;
549         int             volume;
550         float attenuation;
551
552         entity = G_EDICT(OFS_PARM0);
553         channel = G_FLOAT(OFS_PARM1);
554         sample = G_STRING(OFS_PARM2);
555         volume = G_FLOAT(OFS_PARM3) * 255;
556         attenuation = G_FLOAT(OFS_PARM4);
557
558         if (volume < 0 || volume > 255)
559                 Host_Error ("SV_StartSound: volume = %i", volume);
560
561         if (attenuation < 0 || attenuation > 4)
562                 Host_Error ("SV_StartSound: attenuation = %f", attenuation);
563
564         if (channel < 0 || channel > 7)
565                 Host_Error ("SV_StartSound: channel = %i", channel);
566
567         SV_StartSound (entity, channel, sample, volume, attenuation);
568 }
569 */
570
571 /*
572 =========
573 VM_localsound
574
575 localsound(string sample)
576 =========
577 */
578 void VM_localsound(void)
579 {
580         char *s;
581         
582         VM_SAFEPARMCOUNT(1,VM_localsound);
583
584         s = PRVM_G_STRING(OFS_PARM0);
585
586         if(!S_GetCached(s))
587         {
588                 Con_Printf("VM_localsound: %s : %s not cached !\n", PRVM_NAME, s);
589                 PRVM_G_FLOAT(OFS_RETURN) = -4;
590                 return;
591         }               
592
593         S_LocalSound(s);
594         PRVM_G_FLOAT(OFS_RETURN) = 1;
595 }
596
597 /*
598 =================
599 VM_break
600
601 break()
602 =================
603 */
604 void VM_break (void)
605 {
606         PRVM_ERROR ("%s: break statement", PRVM_NAME);
607 }
608
609 //============================================================================
610
611 /*
612 =================
613 VM_localcmd
614
615 Sends text over to the client's execution buffer
616
617 [localcmd (string) or]
618 cmd (string)
619 =================
620 */
621 void VM_localcmd (void)
622 {
623         VM_SAFEPARMCOUNT(1,VM_localcmd);
624
625         Cbuf_AddText(PRVM_G_STRING(OFS_PARM0));
626 }
627
628 /*
629 =================
630 VM_cvar
631
632 float cvar (string)
633 =================
634 */
635 void VM_cvar (void)
636 {
637         VM_SAFEPARMCOUNT(1,VM_cvar);
638
639         PRVM_G_FLOAT(OFS_RETURN) = Cvar_VariableValue(PRVM_G_STRING(OFS_PARM0));
640 }
641
642 /*
643 =================
644 VM_cvar_set
645
646 void cvar_set (string,string)
647 =================
648 */
649 void VM_cvar_set (void)
650 {
651         VM_SAFEPARMCOUNT(2,VM_cvar_set);
652
653         Cvar_Set(PRVM_G_STRING(OFS_PARM0), PRVM_G_STRING(OFS_PARM1));
654 }
655
656 /*
657 =========
658 VM_dprint
659
660 dprint(...[string])
661 =========
662 */
663 void VM_dprint (void)
664 {
665         char string[STRINGTEMP_LENGTH];
666         if (developer.integer)
667         {
668                 VM_VarString(0, string, sizeof(string));
669                 Con_Printf("%s: %s", PRVM_NAME, string);
670         }
671 }
672
673 /*
674 =========
675 VM_ftos
676
677 string  ftos(float)
678 =========
679 */
680
681 void VM_ftos (void)
682 {
683         float v;
684         char *s;
685
686         VM_SAFEPARMCOUNT(1, VM_ftos);
687
688         v = PRVM_G_FLOAT(OFS_PARM0);
689
690         s = VM_GetTempString();
691         if ((float)((int)v) == v)
692                 sprintf(s, "%i", (int)v);
693         else
694                 sprintf(s, "%f", v);
695         PRVM_G_INT(OFS_RETURN) = PRVM_SetString(s);
696 }
697
698 /*
699 =========
700 VM_fabs
701
702 float   fabs(float)
703 =========
704 */
705
706 void VM_fabs (void)
707 {
708         float   v;
709
710         VM_SAFEPARMCOUNT(1,VM_fabs);
711
712         v = PRVM_G_FLOAT(OFS_PARM0);
713         PRVM_G_FLOAT(OFS_RETURN) = fabs(v);
714 }
715
716 /*
717 =========
718 VM_vtos
719
720 string  vtos(vector)
721 =========
722 */
723
724 void VM_vtos (void)
725 {
726         char *s;
727
728         VM_SAFEPARMCOUNT(1,VM_vtos);
729
730         s = VM_GetTempString();
731         sprintf (s, "'%5.1f %5.1f %5.1f'", PRVM_G_VECTOR(OFS_PARM0)[0], PRVM_G_VECTOR(OFS_PARM0)[1], PRVM_G_VECTOR(OFS_PARM0)[2]);
732         PRVM_G_INT(OFS_RETURN) = PRVM_SetString(s);
733 }
734
735 /*
736 =========
737 VM_etos
738
739 string  etos(entity)
740 =========
741 */
742
743 void VM_etos (void)
744 {
745         char *s;
746
747         VM_SAFEPARMCOUNT(1, VM_etos);
748
749         s = VM_GetTempString();
750         sprintf (s, "entity %i", PRVM_G_EDICTNUM(OFS_PARM0));
751         PRVM_G_INT(OFS_RETURN) = PRVM_SetString(s);
752 }
753
754 /*
755 =========
756 VM_stof
757
758 float stof(...[string])
759 =========
760 */
761 void VM_stof(void)
762 {
763         char string[STRINGTEMP_LENGTH];
764         VM_VarString(0, string, sizeof(string));
765         PRVM_G_FLOAT(OFS_RETURN) = atof(string);
766 }
767
768 /*
769 =========
770 VM_spawn
771
772 entity spawn()
773 =========
774 */
775
776 void VM_spawn (void)
777 {
778         prvm_edict_t    *ed;
779         prog->xfunction->builtinsprofile += 20;
780         ed = PRVM_ED_Alloc();
781         VM_RETURN_EDICT(ed);
782 }
783
784 /*
785 =========
786 VM_remove
787
788 entity  remove()
789 =========
790 */
791
792 void VM_remove (void)
793 {
794         prvm_edict_t    *ed;
795         prog->xfunction->builtinsprofile += 20;
796
797         VM_SAFEPARMCOUNT(1, VM_remove);
798
799         ed = PRVM_G_EDICT(OFS_PARM0);
800 //      if (ed == prog->edicts)
801 //              PRVM_ERROR ("remove: tried to remove world\n");
802 //      if (PRVM_NUM_FOR_EDICT(ed) <= sv.maxclients)
803 //              Host_Error("remove: tried to remove a client\n");
804         PRVM_ED_Free (ed);
805 }
806
807 /*
808 =========
809 VM_find
810
811 entity  find(entity start, .string field, string match)
812 =========
813 */
814
815 void VM_find (void)
816 {
817         int             e;
818         int             f;
819         char    *s, *t;
820         prvm_edict_t    *ed;
821
822         VM_SAFEPARMCOUNT(3,VM_find);
823
824         e = PRVM_G_EDICTNUM(OFS_PARM0);
825         f = PRVM_G_INT(OFS_PARM1);
826         s = PRVM_G_STRING(OFS_PARM2);
827
828         if (!s || !s[0])
829         {
830                 // return reserved edict 0 (could be used for whatever the prog wants)
831                 VM_RETURN_EDICT(prog->edicts);
832                 return;
833         }
834
835         for (e++ ; e < prog->num_edicts ; e++)
836         {
837                 prog->xfunction->builtinsprofile++;
838                 ed = PRVM_EDICT_NUM(e);
839                 if (ed->e->free)
840                         continue;
841                 t = PRVM_E_STRING(ed,f);
842                 if (!t)
843                         continue;
844                 if (!strcmp(t,s))
845                 {
846                         VM_RETURN_EDICT(ed);
847                         return;
848                 }
849         }
850
851         VM_RETURN_EDICT(sv.edicts);
852 }
853
854 /*
855 =========
856 VM_findfloat
857
858   entity        findfloat(entity start, .float field, string match)
859   entity        findentity(entity start, .entity field, string match)
860 =========
861 */
862 // LordHavoc: added this for searching float, int, and entity reference fields
863 void VM_findfloat (void)
864 {
865         int             e;
866         int             f;
867         float   s;
868         prvm_edict_t    *ed;
869
870         VM_SAFEPARMCOUNT(3,VM_findfloat);
871
872         e = PRVM_G_EDICTNUM(OFS_PARM0);
873         f = PRVM_G_INT(OFS_PARM1);
874         s = PRVM_G_FLOAT(OFS_PARM2);
875
876         for (e++ ; e < prog->num_edicts ; e++)
877         {
878                 prog->xfunction->builtinsprofile++;
879                 ed = PRVM_EDICT_NUM(e);
880                 if (ed->e->free)
881                         continue;
882                 if (PRVM_E_FLOAT(ed,f) == s)
883                 {
884                         VM_RETURN_EDICT(ed);
885                         return;
886                 }
887         }
888
889         VM_RETURN_EDICT(prog->edicts);
890 }
891
892 /*
893 =========
894 VM_findchain
895
896 entity  findchain(.string field, string match)
897 =========
898 */
899 int PRVM_ED_FindFieldOffset(const char *field);
900 // chained search for strings in entity fields
901 // entity(.string field, string match) findchain = #402;
902 void VM_findchain (void)
903 {
904         int             i;
905         int             f;
906         int             chain_of;
907         char    *s, *t;
908         prvm_edict_t    *ent, *chain;
909
910         VM_SAFEPARMCOUNT(2,VM_findchain);
911
912         // is the same like !(prog->flag & PRVM_FE_CHAIN) - even if the operator precedence is another
913         if(!prog->flag & PRVM_FE_CHAIN)
914                 PRVM_ERROR("VM_findchain: %s doesnt have a chain field !\n", PRVM_NAME);
915
916         chain_of = PRVM_ED_FindFieldOffset ("chain");
917
918         chain = prog->edicts;
919
920         f = PRVM_G_INT(OFS_PARM0);
921         s = PRVM_G_STRING(OFS_PARM1);
922         if (!s || !s[0])
923         {
924                 VM_RETURN_EDICT(prog->edicts);
925                 return;
926         }
927
928         ent = PRVM_NEXT_EDICT(prog->edicts);
929         for (i = 1;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
930         {
931                 prog->xfunction->builtinsprofile++;
932                 if (ent->e->free)
933                         continue;
934                 t = PRVM_E_STRING(ent,f);
935                 if (!t)
936                         continue;
937                 if (strcmp(t,s))
938                         continue;
939
940                 PRVM_E_FLOAT(ent,chain_of) = PRVM_NUM_FOR_EDICT(chain);
941                 chain = ent;
942         }
943
944         VM_RETURN_EDICT(chain);
945 }
946
947 /*
948 =========
949 VM_findchainfloat
950
951 entity  findchainfloat(.string field, float match)
952 entity  findchainentity(.string field, entity match)
953 =========
954 */
955 // LordHavoc: chained search for float, int, and entity reference fields
956 // entity(.string field, float match) findchainfloat = #403;
957 void VM_findchainfloat (void)
958 {
959         int             i;
960         int             f;
961         int             chain_of;
962         float   s;
963         prvm_edict_t    *ent, *chain;
964
965         VM_SAFEPARMCOUNT(2, VM_findchainfloat);
966
967         if(!prog->flag & PRVM_FE_CHAIN)
968                 PRVM_ERROR("VM_findchainfloat: %s doesnt have a chain field !\n", PRVM_NAME);
969
970         chain_of = PRVM_ED_FindFieldOffset ("chain");
971
972         chain = (prvm_edict_t *)prog->edicts;
973
974         f = PRVM_G_INT(OFS_PARM0);
975         s = PRVM_G_FLOAT(OFS_PARM1);
976
977         ent = PRVM_NEXT_EDICT(prog->edicts);
978         for (i = 1;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
979         {
980                 prog->xfunction->builtinsprofile++;
981                 if (ent->e->free)
982                         continue;
983                 if (E_FLOAT(ent,f) != s)
984                         continue;
985
986                 PRVM_E_FLOAT(ent,chain_of) = PRVM_NUM_FOR_EDICT(chain);
987                 chain = ent;
988         }
989
990         VM_RETURN_EDICT(chain);
991 }
992
993 /*
994 =========
995 VM_precache_file
996
997 string  precache_file(string)
998 =========
999 */
1000 void VM_precache_file (void)
1001 {       // precache_file is only used to copy files with qcc, it does nothing
1002         VM_SAFEPARMCOUNT(1,VM_precache_file);
1003
1004         PRVM_G_INT(OFS_RETURN) = PRVM_G_INT(OFS_PARM0);
1005 }
1006
1007 /*
1008 =========
1009 VM_preache_error
1010
1011 used instead of the other VM_precache_* functions in the builtin list
1012 =========
1013 */
1014
1015 void VM_precache_error (void)
1016 {
1017         PRVM_ERROR ("PF_Precache_*: Precache can only be done in spawn functions");
1018 }
1019
1020 /*
1021 =========
1022 VM_precache_sound
1023
1024 string  precache_sound (string sample)
1025 =========
1026 */
1027 void VM_precache_sound (void)
1028 {
1029         char    *s;
1030
1031         VM_SAFEPARMCOUNT(1, VM_precache_sound);
1032
1033         s = PRVM_G_STRING(OFS_PARM0);
1034         PRVM_G_INT(OFS_RETURN) = PRVM_G_INT(OFS_PARM0);
1035         VM_CheckEmptyString (s);
1036         
1037         if(S_GetCached(s))
1038         {
1039                 Con_Printf("VM_precache_sound: %s already cached (%s)\n", s, PRVM_NAME);
1040                 return;
1041         }
1042         
1043         if(!S_PrecacheSound(s,true))
1044                 Con_Printf("VM_prache_sound: Failed to load %s for %s\n", s, PRVM_NAME);
1045 }
1046
1047 /*
1048 =========
1049 VM_coredump
1050
1051 coredump()
1052 =========
1053 */
1054 void VM_coredump (void)
1055 {
1056         VM_SAFEPARMCOUNT(0,VM_coredump);
1057
1058         PRVM_ED_PrintEdicts_f ();
1059 }
1060
1061 /*
1062 =========
1063 VM_traceon
1064
1065 traceon()
1066 =========
1067 */
1068 void VM_traceon (void)
1069 {
1070         VM_SAFEPARMCOUNT(0,VM_traceon);
1071
1072         prog->trace = true;
1073 }
1074
1075 /*
1076 =========
1077 VM_traceoff
1078
1079 traceoff()
1080 =========
1081 */
1082 void VM_traceoff (void)
1083 {
1084         VM_SAFEPARMCOUNT(0,VM_traceoff);
1085
1086         prog->trace = false;
1087 }
1088
1089 /*
1090 =========
1091 VM_eprint
1092
1093 eprint(entity float)
1094 =========
1095 */
1096 void VM_eprint (void)
1097 {
1098         VM_SAFEPARMCOUNT(1,VM_eprint);
1099
1100         PRVM_ED_PrintNum (PRVM_G_EDICTNUM(OFS_PARM0));
1101 }
1102
1103 /*
1104 =========
1105 VM_rint
1106
1107 float   rint(float)
1108 =========
1109 */
1110 void VM_rint (void)
1111 {
1112         float   f;
1113
1114         VM_SAFEPARMCOUNT(1,VM_rint);
1115
1116         f = PRVM_G_FLOAT(OFS_PARM0);
1117         if (f > 0)
1118                 PRVM_G_FLOAT(OFS_RETURN) = (int)(f + 0.5);
1119         else
1120                 PRVM_G_FLOAT(OFS_RETURN) = (int)(f - 0.5);
1121 }
1122
1123 /*
1124 =========
1125 VM_floor
1126
1127 float   floor(float)
1128 =========
1129 */
1130 void VM_floor (void)
1131 {
1132         VM_SAFEPARMCOUNT(1,VM_floor);
1133
1134         PRVM_G_FLOAT(OFS_RETURN) = floor(PRVM_G_FLOAT(OFS_PARM0));
1135 }
1136
1137 /*
1138 =========
1139 VM_ceil
1140
1141 float   ceil(float)
1142 =========
1143 */
1144 void VM_ceil (void)
1145 {
1146         VM_SAFEPARMCOUNT(1,VM_ceil);
1147
1148         PRVM_G_FLOAT(OFS_RETURN) = ceil(PRVM_G_FLOAT(OFS_PARM0));
1149 }
1150
1151
1152 /*
1153 =============
1154 VM_nextent
1155
1156 entity  nextent(entity)
1157 =============
1158 */
1159 void VM_nextent (void)
1160 {
1161         int             i;
1162         prvm_edict_t    *ent;
1163
1164         i = PRVM_G_EDICTNUM(OFS_PARM0);
1165         while (1)
1166         {
1167                 prog->xfunction->builtinsprofile++;
1168                 i++;
1169                 if (i == prog->num_edicts)
1170                 {
1171                         VM_RETURN_EDICT(prog->edicts);
1172                         return;
1173                 }
1174                 ent = PRVM_EDICT_NUM(i);
1175                 if (!ent->e->free)
1176                 {
1177                         VM_RETURN_EDICT(ent);
1178                         return;
1179                 }
1180         }
1181 }
1182
1183 /*
1184 ===============================================================================
1185 MESSAGE WRITING
1186
1187 used only for client and menu
1188 severs uses VM_SV_...
1189
1190 Write*(* data, float type, float to)
1191
1192 ===============================================================================
1193 */
1194
1195 #define MSG_BROADCAST   0               // unreliable to all
1196 #define MSG_ONE                 1               // reliable to one (msg_entity)
1197 #define MSG_ALL                 2               // reliable to all
1198 #define MSG_INIT                3               // write to the init string
1199
1200 sizebuf_t *VM_WriteDest (void)
1201 {
1202         int             dest;
1203         int             destclient;
1204
1205         if(!sv.active)
1206                 PRVM_ERROR("VM_WriteDest: game is not server (%s)\n", PRVM_NAME);
1207
1208         dest = G_FLOAT(OFS_PARM1);
1209         switch (dest)
1210         {
1211         case MSG_BROADCAST:
1212                 return &sv.datagram;
1213
1214         case MSG_ONE:
1215                 destclient = (int) PRVM_G_FLOAT(OFS_PARM2);
1216                 if (!sv.active  || destclient < 0 || destclient >= svs.maxclients || !svs.clients[destclient].active)
1217                         PRVM_ERROR("VM_clientcommand: %s: invalid client/server is not active !", PRVM_NAME);
1218
1219                 return &svs.clients[destclient].message;
1220
1221         case MSG_ALL:
1222                 return &sv.reliable_datagram;
1223
1224         case MSG_INIT:
1225                 return &sv.signon;
1226
1227         default:
1228                 PRVM_ERROR ("WriteDest: bad destination");
1229                 break;
1230         }
1231
1232         return NULL;
1233 }
1234
1235 void VM_WriteByte (void)
1236 {
1237         MSG_WriteByte (VM_WriteDest(), PRVM_G_FLOAT(OFS_PARM0));
1238 }
1239
1240 void VM_WriteChar (void)
1241 {
1242         MSG_WriteChar (VM_WriteDest(), PRVM_G_FLOAT(OFS_PARM0));
1243 }
1244
1245 void VM_WriteShort (void)
1246 {
1247         MSG_WriteShort (VM_WriteDest(), PRVM_G_FLOAT(OFS_PARM0));
1248 }
1249
1250 void VM_WriteLong (void)
1251 {
1252         MSG_WriteLong (VM_WriteDest(), PRVM_G_FLOAT(OFS_PARM0));
1253 }
1254
1255 void VM_WriteAngle (void)
1256 {
1257         MSG_WriteAngle (VM_WriteDest(), PRVM_G_FLOAT(OFS_PARM0));
1258 }
1259
1260 void VM_WriteCoord (void)
1261 {
1262         MSG_WriteDPCoord (VM_WriteDest(), PRVM_G_FLOAT(OFS_PARM0));
1263 }
1264
1265 void VM_WriteString (void)
1266 {
1267         MSG_WriteString (VM_WriteDest(), PRVM_G_STRING(OFS_PARM0));
1268 }
1269
1270 void VM_WriteEntity (void)
1271 {
1272         MSG_WriteShort (VM_WriteDest(), PRVM_G_EDICTNUM(OFS_PARM0));
1273 }
1274
1275 //=============================================================================
1276
1277 /*
1278 ==============
1279 VM_changelevel
1280 server and menu
1281
1282 changelevel(string map)
1283 ==============
1284 */
1285 void VM_changelevel (void)
1286 {
1287         char    *s;
1288
1289         VM_SAFEPARMCOUNT(1, VM_changelevel);
1290
1291         if(!sv.active)
1292         {
1293                 Con_Printf("VM_changelevel: game is not server (%s)\n", PRVM_NAME); 
1294                 return;
1295         }
1296
1297 // make sure we don't issue two changelevels
1298         if (svs.changelevel_issued)
1299                 return;
1300         svs.changelevel_issued = true;
1301
1302         s = G_STRING(OFS_PARM0);
1303         Cbuf_AddText (va("changelevel %s\n",s));
1304 }
1305
1306 /*
1307 =========
1308 VM_sin
1309
1310 float   sin(float)
1311 =========
1312 */
1313 void VM_sin (void)
1314 {
1315         VM_SAFEPARMCOUNT(1,VM_sin);
1316         PRVM_G_FLOAT(OFS_RETURN) = sin(PRVM_G_FLOAT(OFS_PARM0));
1317 }
1318
1319 /*
1320 =========
1321 VM_cos
1322 float   cos(float)
1323 =========
1324 */
1325 void VM_cos (void)
1326 {
1327         VM_SAFEPARMCOUNT(1,VM_cos);
1328         PRVM_G_FLOAT(OFS_RETURN) = cos(PRVM_G_FLOAT(OFS_PARM0));
1329 }
1330
1331 /*
1332 =========
1333 VM_sqrt
1334
1335 float   sqrt(float)
1336 =========
1337 */
1338 void VM_sqrt (void)
1339 {
1340         VM_SAFEPARMCOUNT(1,VM_sqrt);
1341         PRVM_G_FLOAT(OFS_RETURN) = sqrt(PRVM_G_FLOAT(OFS_PARM0));
1342 }
1343
1344 /*
1345 =================
1346 VM_randomvec
1347
1348 Returns a vector of length < 1 and > 0
1349
1350 randomvec()
1351 =================
1352 */
1353 void VM_randomvec (void)
1354 {
1355         vec3_t          temp;
1356         //float         length;
1357
1358         VM_SAFEPARMCOUNT(0, VM_randomvec);
1359
1360         //// WTF ??
1361         do
1362         {
1363                 temp[0] = (rand()&32767) * (2.0 / 32767.0) - 1.0;
1364                 temp[1] = (rand()&32767) * (2.0 / 32767.0) - 1.0;
1365                 temp[2] = (rand()&32767) * (2.0 / 32767.0) - 1.0;
1366         }
1367         while (DotProduct(temp, temp) >= 1);
1368         VectorCopy (temp, PRVM_G_VECTOR(OFS_RETURN));
1369
1370         /*
1371         temp[0] = (rand()&32767) * (2.0 / 32767.0) - 1.0;
1372         temp[1] = (rand()&32767) * (2.0 / 32767.0) - 1.0;
1373         temp[2] = (rand()&32767) * (2.0 / 32767.0) - 1.0;
1374         // length returned always > 0
1375         length = (rand()&32766 + 1) * (1.0 / 32767.0) / VectorLength(temp);
1376         VectorScale(temp,length, temp);*/
1377         //VectorCopy(temp, PRVM_G_VECTOR(OFS_RETURN));
1378 }
1379
1380 //=============================================================================
1381
1382 /*
1383 =========
1384 VM_registercvar
1385
1386 float   registercvar (string name, string value)
1387 =========
1388 */
1389 void VM_registercvar (void)
1390 {
1391         char *name, *value;
1392         cvar_t *variable;
1393
1394         VM_SAFEPARMCOUNT(2,VM_registercvar);
1395
1396         name = PRVM_G_STRING(OFS_PARM0);
1397         value = PRVM_G_STRING(OFS_PARM1);
1398         PRVM_G_FLOAT(OFS_RETURN) = 0;
1399 // first check to see if it has already been defined
1400         if (Cvar_FindVar (name))
1401                 return;
1402
1403 // check for overlap with a command
1404         if (Cmd_Exists (name))
1405         {
1406                 Con_Printf ("VM_registercvar: %s is a command\n", name);
1407                 return;
1408         }
1409
1410         if (vm_currentqc_cvar >= MAX_QC_CVARS)
1411                 PRVM_ERROR ("VM_registercvar: ran out of cvar slots (%i)\n", MAX_QC_CVARS);
1412
1413 // copy the name and value
1414         variable = &vm_qc_cvar[vm_currentqc_cvar++];
1415         variable->name = Z_Malloc (strlen(name)+1);
1416         strcpy (variable->name, name);
1417         variable->string = Z_Malloc (strlen(value)+1);
1418         strcpy (variable->string, value);
1419         variable->value = atof (value);
1420
1421         Cvar_RegisterVariable(variable);
1422         PRVM_G_FLOAT(OFS_RETURN) = 1; // success
1423 }
1424
1425 /*
1426 =================
1427 VM_min
1428
1429 returns the minimum of two supplied floats
1430
1431 float min(float a, float b, ...[float])
1432 =================
1433 */
1434 void VM_min (void)
1435 {
1436         // LordHavoc: 3+ argument enhancement suggested by FrikaC
1437         if (prog->argc == 2)
1438                 PRVM_G_FLOAT(OFS_RETURN) = min(PRVM_G_FLOAT(OFS_PARM0), PRVM_G_FLOAT(OFS_PARM1));
1439         else if (prog->argc >= 3)
1440         {
1441                 int i;
1442                 float f = PRVM_G_FLOAT(OFS_PARM0);
1443                 for (i = 1;i < prog->argc;i++)
1444                         if (PRVM_G_FLOAT((OFS_PARM0+i*3)) < f)
1445                                 f = PRVM_G_FLOAT((OFS_PARM0+i*3));
1446                 PRVM_G_FLOAT(OFS_RETURN) = f;
1447         }
1448         else
1449                 PRVM_ERROR("VM_min: %s must supply at least 2 floats\n", PRVM_NAME);
1450 }
1451
1452 /*
1453 =================
1454 VM_max
1455
1456 returns the maximum of two supplied floats
1457
1458 float   max(float a, float b, ...[float])
1459 =================
1460 */
1461 void VM_max (void)
1462 {
1463         // LordHavoc: 3+ argument enhancement suggested by FrikaC
1464         if (pr_argc == 2)
1465                 PRVM_G_FLOAT(OFS_RETURN) = max(PRVM_G_FLOAT(OFS_PARM0), PRVM_G_FLOAT(OFS_PARM1));
1466         else if (pr_argc >= 3)
1467         {
1468                 int i;
1469                 float f = PRVM_G_FLOAT(OFS_PARM0);
1470                 for (i = 1;i < pr_argc;i++)
1471                         if (PRVM_G_FLOAT((OFS_PARM0+i*3)) > f)
1472                                 f = PRVM_G_FLOAT((OFS_PARM0+i*3));
1473                 G_FLOAT(OFS_RETURN) = f;
1474         }
1475         else
1476                 PRVM_ERROR("VM_max: %s must supply at least 2 floats\n", PRVM_NAME);
1477 }
1478
1479 /*
1480 =================
1481 VM_bound
1482
1483 returns number bounded by supplied range
1484
1485 float   bound(float min, float value, float max)
1486 =================
1487 */
1488 void VM_bound (void)
1489 {
1490         VM_SAFEPARMCOUNT(3,VM_bound);
1491         PRVM_G_FLOAT(OFS_RETURN) = bound(PRVM_G_FLOAT(OFS_PARM0), PRVM_G_FLOAT(OFS_PARM1), PRVM_G_FLOAT(OFS_PARM2));
1492 }
1493
1494 /*
1495 =================
1496 VM_pow
1497
1498 returns a raised to power b
1499
1500 float   pow(float a, float b)
1501 =================
1502 */
1503 void VM_pow (void)
1504 {
1505         VM_SAFEPARMCOUNT(2,VM_pow);
1506         PRVM_G_FLOAT(OFS_RETURN) = pow(PRVM_G_FLOAT(OFS_PARM0), PRVM_G_FLOAT(OFS_PARM1));
1507 }
1508
1509 /*
1510 =================
1511 VM_copyentity
1512
1513 copies data from one entity to another
1514
1515 copyentity(entity src, entity dst)
1516 =================
1517 */
1518 void VM_copyentity (void)
1519 {
1520         prvm_edict_t *in, *out;
1521         VM_SAFEPARMCOUNT(2,VM_copyentity);
1522         in = PRVM_G_EDICT(OFS_PARM0);
1523         out = PRVM_G_EDICT(OFS_PARM1);
1524         memcpy(out->v, in->v, prog->progs->entityfields * 4);
1525 }
1526
1527 /*
1528 =================
1529 VM_setcolor
1530
1531 sets the color of a client and broadcasts the update to all connected clients
1532
1533 setcolor(clientent, value)
1534 =================
1535 */
1536 /*void PF_setcolor (void)
1537 {
1538         client_t *client;
1539         int entnum, i;
1540         eval_t *val;
1541
1542         entnum = G_EDICTNUM(OFS_PARM0);
1543         i = G_FLOAT(OFS_PARM1);
1544
1545         if (entnum < 1 || entnum > svs.maxclients || !svs.clients[entnum-1].active)
1546         {
1547                 Con_Printf ("tried to setcolor a non-client\n");
1548                 return;
1549         }
1550
1551         client = svs.clients + entnum-1;
1552         if ((val = GETEDICTFIELDVALUE(client->edict, eval_clientcolors)))
1553                 val->_float = i;
1554         client->colors = i;
1555         client->old_colors = i;
1556         client->edict->v->team = (i & 15) + 1;
1557
1558         MSG_WriteByte (&sv.reliable_datagram, svc_updatecolors);
1559         MSG_WriteByte (&sv.reliable_datagram, entnum - 1);
1560         MSG_WriteByte (&sv.reliable_datagram, i);
1561 }*/
1562
1563 void VM_Files_Init(void)
1564 {
1565         memset(VM_FILES, 0, sizeof(qfile_t*[MAX_VMFILES]));
1566 }
1567
1568 void VM_Files_CloseAll(void)
1569 {
1570         int i;
1571         for (i = 0;i < MAX_VMFILES;i++)
1572         {
1573                 if (VM_FILES[i])
1574                         FS_Close(VM_FILES[i]);
1575                 //VM_FILES[i] = NULL;
1576         }
1577         memset(VM_FILES,0,sizeof(qfile_t*[MAX_VMFILES])); // this should be faster (is it ?)
1578 }
1579
1580 /*
1581 =========
1582 VM_fopen
1583
1584 float   fopen(string filename, float mode)
1585 =========
1586 */
1587 // float(string filename, float mode) fopen = #110;
1588 // opens a file inside quake/gamedir/data/ (mode is FILE_READ, FILE_APPEND, or FILE_WRITE),
1589 // returns fhandle >= 0 if successful, or fhandle < 0 if unable to open file for any reason
1590 void VM_fopen(void)
1591 {
1592         int filenum, mode;
1593         char *modestring, *filename;
1594
1595         VM_SAFEPARMCOUNT(2,VM_fopen);
1596
1597         for (filenum = 0;filenum < MAX_VMFILES;filenum++)
1598                 if (VM_FILES[filenum] == NULL)
1599                         break;
1600         if (filenum >= MAX_VMFILES)
1601         {
1602                 Con_Printf("VM_fopen: %s ran out of file handles (%i)\n", PRVM_NAME, MAX_VMFILES);
1603                 PRVM_G_FLOAT(OFS_RETURN) = -2;
1604                 return;
1605         }
1606         mode = PRVM_G_FLOAT(OFS_PARM1);
1607         switch(mode)
1608         {
1609         case 0: // FILE_READ
1610                 modestring = "rb";
1611                 break;
1612         case 1: // FILE_APPEND
1613                 modestring = "ab";
1614                 break;
1615         case 2: // FILE_WRITE
1616                 modestring = "wb";
1617                 break;
1618         default:
1619                 Con_Printf ("VM_fopen: %s no such mode %i (valid: 0 = read, 1 = append, 2 = write)\n", PRVM_NAME, mode);
1620                 PRVM_G_FLOAT(OFS_RETURN) = -3;
1621                 return;
1622         }
1623         filename = PRVM_G_STRING(OFS_PARM0);
1624         // .. is parent directory on many platforms
1625         // / is parent directory on Amiga
1626         // : is root of drive on Amiga (also used as a directory separator on Mac, but / works there too, so that's a bad idea)
1627         // \ is a windows-ism (so it's naughty to use it, / works on all platforms)
1628         if ((filename[0] == '.' && filename[1] == '.') || filename[0] == '/' || strrchr(filename, ':') || strrchr(filename, '\\'))
1629         {
1630                 Con_Printf("VM_fopen: dangerous or non-portable filename \"%s\" not allowed. (contains : or \\ or begins with .. or /)\n", filename);
1631                 PRVM_G_FLOAT(OFS_RETURN) = -4;
1632                 return;
1633         }
1634         VM_FILES[filenum] = FS_Open(va("data/%s", filename), modestring, false);
1635         if (VM_FILES[filenum] == NULL)
1636                 PRVM_G_FLOAT(OFS_RETURN) = -1;
1637         else
1638                 PRVM_G_FLOAT(OFS_RETURN) = filenum;
1639 }
1640
1641 /*
1642 =========
1643 VM_fclose
1644
1645 fclose(float fhandle)
1646 =========
1647 */
1648 //void(float fhandle) fclose = #111; // closes a file
1649 void VM_fclose(void)
1650 {
1651         int filenum;
1652
1653         VM_SAFEPARMCOUNT(1,VM_fclose);
1654
1655         filenum = PRVM_G_FLOAT(OFS_PARM0);
1656         if (filenum < 0 || filenum >= MAX_VMFILES)
1657         {
1658                 Con_Printf("VM_fclose: invalid file handle %i used in %s\n", filenum, PRVM_NAME);
1659                 return;
1660         }
1661         if (VM_FILES[filenum] == NULL)
1662         {
1663                 Con_Printf("VM_fclose: no such file handle %i (or file has been closed) in %s\n", filenum, PRVM_NAME);
1664                 return;
1665         }
1666         FS_Close(VM_FILES[filenum]);
1667         VM_FILES[filenum] = NULL;
1668 }
1669
1670 /*
1671 =========
1672 VM_fgets
1673
1674 string  fgets(float fhandle)
1675 =========
1676 */
1677 //string(float fhandle) fgets = #112; // reads a line of text from the file and returns as a tempstring
1678 void VM_fgets(void)
1679 {
1680         int c, end;
1681         static char string[STRINGTEMP_LENGTH];
1682         int filenum;
1683
1684         VM_SAFEPARMCOUNT(1,VM_fgets);
1685
1686         filenum = PRVM_G_FLOAT(OFS_PARM0);
1687         if (filenum < 0 || filenum >= MAX_VMFILES)
1688         {
1689                 Con_Printf("VM_fgets: invalid file handle %i used in %s\n", filenum, PRVM_NAME);
1690                 return;
1691         }
1692         if (VM_FILES[filenum] == NULL)
1693         {
1694                 Con_Printf("VM_fgets: no such file handle %i (or file has been closed) in %s\n", filenum, PRVM_NAME);
1695                 return;
1696         }
1697         end = 0;
1698         for (;;)
1699         {
1700                 c = FS_Getc(VM_FILES[filenum]);
1701                 if (c == '\r' || c == '\n' || c < 0)
1702                         break;
1703                 if (end < STRINGTEMP_LENGTH - 1)
1704                         string[end++] = c;
1705         }
1706         string[end] = 0;
1707         // remove \n following \r
1708         if (c == '\r')
1709                 c = FS_Getc(VM_FILES[filenum]);
1710         if (developer.integer)
1711                 Con_Printf("fgets: %s: %s\n", PRVM_NAME, string);
1712         if (c >= 0)
1713                 PRVM_G_INT(OFS_RETURN) = PRVM_SetString(string);
1714         else
1715                 PRVM_G_INT(OFS_RETURN) = 0;
1716 }
1717
1718 /*
1719 =========
1720 VM_fputs
1721
1722 fputs(float fhandle, string s)
1723 =========
1724 */
1725 //void(float fhandle, string s) fputs = #113; // writes a line of text to the end of the file
1726 void VM_fputs(void)
1727 {
1728         int stringlength;
1729         char string[STRINGTEMP_LENGTH];
1730         int filenum;
1731
1732         VM_SAFEPARMCOUNT(2,VM_fputs);
1733
1734         filenum = PRVM_G_FLOAT(OFS_PARM0);
1735         if (filenum < 0 || filenum >= MAX_VMFILES)
1736         {
1737                 Con_Printf("VM_fputs: invalid file handle %i used in %s\n", filenum, PRVM_NAME);
1738                 return;
1739         }
1740         if (VM_FILES[filenum] == NULL)
1741         {
1742                 Con_Printf("VM_fputs: no such file handle %i (or file has been closed) in %s\n", filenum, PRVM_NAME);
1743                 return;
1744         }
1745         VM_VarString(1, string, sizeof(string));
1746         if ((stringlength = strlen(string)))
1747                 FS_Write(VM_FILES[filenum], string, stringlength);
1748         if (developer.integer)
1749                 Con_Printf("fputs: %s: %s\n", PRVM_NAME, string);
1750 }
1751
1752 /*
1753 =========
1754 VM_strlen
1755
1756 float   strlen(string s)
1757 =========
1758 */
1759 //float(string s) strlen = #114; // returns how many characters are in a string
1760 void VM_strlen(void)
1761 {
1762         char *s;
1763
1764         VM_SAFEPARMCOUNT(1,VM_strlen);
1765
1766         s = PRVM_G_STRING(OFS_PARM0);
1767         if (s)
1768                 PRVM_G_FLOAT(OFS_RETURN) = strlen(s);
1769         else
1770                 PRVM_G_FLOAT(OFS_RETURN) = 0;
1771 }
1772
1773 /*
1774 =========
1775 VM_strcat
1776
1777 string strcat(string s1, string s2)
1778 =========
1779 */
1780 //string(string s1, string s2) strcat = #115;
1781 // concatenates two strings (for example "abc", "def" would return "abcdef")
1782 // and returns as a tempstring
1783 void VM_strcat(void)
1784 {
1785         char *s;
1786
1787         VM_SAFEPARMCOUNT(2,VM_strcat);
1788
1789         s = VM_GetTempString();
1790         VM_VarString(0, s, STRINGTEMP_LENGTH);
1791         PRVM_G_INT(OFS_RETURN) = PRVM_SetString(s);
1792 }
1793
1794 /*
1795 =========
1796 VM_substring
1797
1798 string  substring(string s, float start, float length)
1799 =========
1800 */
1801 // string(string s, float start, float length) substring = #116;
1802 // returns a section of a string as a tempstring
1803 void VM_substring(void)
1804 {
1805         int i, start, length;
1806         char *s, *string;
1807
1808         VM_SAFEPARMCOUNT(3,VM_substring);
1809
1810         string = VM_GetTempString();
1811         s = PRVM_G_STRING(OFS_PARM0);
1812         start = PRVM_G_FLOAT(OFS_PARM1);
1813         length = PRVM_G_FLOAT(OFS_PARM2);
1814         if (!s)
1815                 s = "";
1816         for (i = 0;i < start && *s;i++, s++);
1817         for (i = 0;i < STRINGTEMP_LENGTH - 1 && *s && i < length;i++, s++)
1818                 string[i] = *s;
1819         string[i] = 0;
1820         PRVM_G_INT(OFS_RETURN) = PRVM_SetString(string);
1821 }
1822
1823 /*
1824 =========
1825 VM_stov
1826
1827 vector  stov(string s)
1828 =========
1829 */
1830 //vector(string s) stov = #117; // returns vector value from a string
1831 void VM_stov(void)
1832 {
1833         char string[STRINGTEMP_LENGTH];
1834
1835         VM_SAFEPARMCOUNT(1,VM_stov);
1836
1837         VM_VarString(0, string, sizeof(string));
1838         Math_atov(string, PRVM_G_VECTOR(OFS_RETURN));
1839 }
1840
1841 /*
1842 =========
1843 VM_strzone
1844
1845 string  strzone(string s)
1846 =========
1847 */
1848 //string(string s) strzone = #118; // makes a copy of a string into the string zone and returns it, this is often used to keep around a tempstring for longer periods of time (tempstrings are replaced often)
1849 void VM_strzone(void)
1850 {
1851         char *in, *out;
1852
1853         VM_SAFEPARMCOUNT(1,VM_strzone);
1854
1855         in = PRVM_G_STRING(OFS_PARM0);
1856         out = Mem_Alloc(VM_STRINGS_MEMPOOL, strlen(in) + 1);
1857         strcpy(out, in);
1858         PRVM_G_INT(OFS_RETURN) = PRVM_SetString(out);
1859 }
1860
1861 /*
1862 =========
1863 VM_strunzone
1864
1865 strzone(string s)
1866 =========
1867 */
1868 //void(string s) strunzone = #119; // removes a copy of a string from the string zone (you can not use that string again or it may crash!!!)
1869 void VM_strunzone(void)
1870 {
1871         VM_SAFEPARMCOUNT(1,VM_strunzone);
1872
1873         Mem_Free(PRVM_G_STRING(OFS_PARM0));
1874 }
1875
1876 /*
1877 =========
1878 VM_command (used by client and menu)
1879
1880 clientcommand(float client, string s) (for client and menu)
1881 =========
1882 */
1883 //void(entity e, string s) clientcommand = #440; // executes a command string as if it came from the specified client
1884 //this function originally written by KrimZon, made shorter by LordHavoc
1885 void VM_clcommand (void)
1886 {
1887         client_t *temp_client;
1888         int i;
1889
1890         VM_SAFEPARMCOUNT(2,VM_clcommand);
1891
1892         i = PRVM_G_FLOAT(OFS_PARM0);
1893         if (!sv.active  || i < 0 || i >= svs.maxclients || !svs.clients[i].active)
1894         {
1895                 Con_Printf("VM_clientcommand: %s: invalid client/server is not active !", PRVM_NAME);
1896                 return;
1897         }
1898
1899         temp_client = host_client;
1900         host_client = svs.clients + i;
1901         Cmd_ExecuteString (PRVM_G_STRING(OFS_PARM1), src_client);
1902         host_client = temp_client;
1903 }
1904
1905
1906 /*
1907 =========
1908 VM_tokenize
1909
1910 float tokenize(string s)
1911 =========
1912 */
1913 //float(string s) tokenize = #441;
1914 // takes apart a string into individal words (access them with argv), returns how many
1915 // this function originally written by KrimZon, made shorter by LordHavoc
1916 static char **tokens = NULL;
1917 static int    max_tokens, num_tokens = 0;
1918 void VM_tokenize (void)
1919 {
1920         const char *p;
1921         char *str;
1922
1923         VM_SAFEPARMCOUNT(1,VM_tokenize);
1924
1925         str = PRVM_G_STRING(OFS_PARM0);
1926
1927         if (tokens != NULL)
1928         {
1929                 int i;
1930                 for (i=0;i<num_tokens;i++)
1931                         Z_Free(tokens[i]);
1932                 Z_Free(tokens);
1933                 num_tokens = 0;
1934         }
1935
1936         tokens = Z_Malloc(strlen(str) * sizeof(char *));
1937         max_tokens = strlen(str);
1938
1939         for (p = str;COM_ParseToken(&p, false) && num_tokens < max_tokens;num_tokens++)
1940         {
1941                 tokens[num_tokens] = Z_Malloc(strlen(com_token) + 1);
1942                 strcpy(tokens[num_tokens], com_token);
1943         }
1944
1945         PRVM_G_FLOAT(OFS_RETURN) = num_tokens;
1946 }
1947
1948 /*
1949 =========
1950 VM_argv
1951
1952 string argv(float n)
1953 =========
1954 */
1955 //string(float n) argv = #442;
1956 // returns a word from the tokenized string (returns nothing for an invalid index)
1957 // this function originally written by KrimZon, made shorter by LordHavoc
1958 void VM_argv (void)
1959 {
1960         int token_num;
1961
1962         VM_SAFEPARMCOUNT(1,VM_argv);
1963
1964         token_num = PRVM_G_FLOAT(OFS_PARM0);
1965         if (token_num >= 0 && token_num < num_tokens)
1966                 PRVM_G_INT(OFS_RETURN) = PRVM_SetString(tokens[token_num]);
1967         else
1968                 PRVM_G_INT(OFS_RETURN) = PRVM_SetString("");
1969 }
1970
1971 /*
1972 //void(entity e, entity tagentity, string tagname) setattachment = #443; // attachs e to a tag on tagentity (note: use "" to attach to entity origin/angles instead of a tag)
1973 void PF_setattachment (void)
1974 {
1975         edict_t *e = G_EDICT(OFS_PARM0);
1976         edict_t *tagentity = G_EDICT(OFS_PARM1);
1977         char *tagname = G_STRING(OFS_PARM2);
1978         eval_t *v;
1979         int i, modelindex;
1980         model_t *model;
1981
1982         if (tagentity == NULL)
1983                 tagentity = sv.edicts;
1984
1985         v = GETEDICTFIELDVALUE(e, eval_tag_entity);
1986         if (v)
1987                 v->edict = EDICT_TO_PROG(tagentity);
1988
1989         v = GETEDICTFIELDVALUE(e, eval_tag_index);
1990         if (v)
1991                 v->_float = 0;
1992         if (tagentity != NULL && tagentity != sv.edicts && tagname && tagname[0])
1993         {
1994                 modelindex = (int)tagentity->v->modelindex;
1995                 if (modelindex >= 0 && modelindex < MAX_MODELS)
1996                 {
1997                         model = sv.models[modelindex];
1998                         if (model->data_overridetagnamesforskin && (unsigned int)tagentity->v->skin < (unsigned int)model->numskins && model->data_overridetagnamesforskin[(unsigned int)tagentity->v->skin].num_overridetagnames)
1999                                 for (i = 0;i < model->data_overridetagnamesforskin[(unsigned int)tagentity->v->skin].num_overridetagnames;i++)
2000                                         if (!strcmp(tagname, model->data_overridetagnamesforskin[(unsigned int)tagentity->v->skin].data_overridetagnames[i].name))
2001                                                 v->_float = i + 1;
2002                         if (v->_float == 0 && model->alias.aliasnum_tags)
2003                                 for (i = 0;i < model->alias.aliasnum_tags;i++)
2004                                         if (!strcmp(tagname, model->alias.aliasdata_tags[i].name))
2005                                                 v->_float = i + 1;
2006                         if (v->_float == 0)
2007                                 Con_DPrintf("setattachment(edict %i, edict %i, string \"%s\"): tried to find tag named \"%s\" on entity %i (model \"%s\") but could not find it\n", NUM_FOR_EDICT(e), NUM_FOR_EDICT(tagentity), tagname, tagname, NUM_FOR_EDICT(tagentity), model->name);
2008                 }
2009                 else
2010                         Con_DPrintf("setattachment(edict %i, edict %i, string \"%s\"): tried to find tag named \"%s\" on entity %i but it has no model\n", NUM_FOR_EDICT(e), NUM_FOR_EDICT(tagentity), tagname, tagname, NUM_FOR_EDICT(tagentity));
2011         }
2012 }*/
2013
2014 /*
2015 =========
2016 VM_serverstate
2017
2018 float   isserver()
2019 =========
2020 */
2021 void VM_isserver(void)
2022 {
2023         VM_SAFEPARMCOUNT(0,VM_serverstate);
2024
2025         PRVM_G_FLOAT(OFS_RETURN) = sv.active;
2026 }
2027
2028 /*
2029 =========
2030 VM_clientcount
2031
2032 float   clientcount()
2033 =========
2034 */
2035 void VM_clientcount(void)
2036 {
2037         VM_SAFEPARMCOUNT(0,VM_clientcount);
2038
2039         PRVM_G_FLOAT(OFS_RETURN) = svs.maxclients;
2040 }
2041
2042 /*
2043 =========
2044 VM_clientstate
2045
2046 float   clientstate()
2047 =========
2048 */
2049 void VM_clientstate(void)
2050 {
2051         VM_SAFEPARMCOUNT(0,VM_clientstate);
2052
2053         PRVM_G_FLOAT(OFS_RETURN) = cls.state;
2054 }
2055
2056 //=============================================================================
2057 // Draw builtins (client & menu)
2058
2059 /*
2060 =========
2061 VM_iscachedpic
2062
2063 float   iscachedpic(string pic)
2064 =========
2065 */
2066 void VM_iscachedpic(void)
2067 {
2068         VM_SAFEPARMCOUNT(1,VM_iscachedpic);
2069
2070         // drawq hasnt such a function, thus always return true 
2071         PRVM_G_FLOAT(OFS_RETURN) = TRUE;
2072 }
2073
2074 /*
2075 =========
2076 VM_precache_pic
2077
2078 string  precache_pic(string pic) 
2079 =========
2080 */
2081 void VM_precache_pic(void)
2082 {
2083         char    *s;
2084         
2085         VM_SAFEPARMCOUNT(1, VM_precache_pic);
2086         
2087         s = PRVM_G_STRING(OFS_PARM0);
2088         PRVM_G_INT(OFS_RETURN) = PRVM_G_INT(OFS_PARM0);
2089         
2090         if(!s)
2091                 PRVM_ERROR ("VM_precache_pic: %s: NULL\n");
2092
2093         VM_CheckEmptyString (s);
2094         
2095         Draw_CachePic(s); 
2096 }
2097
2098 /*
2099 =========
2100 VM_freepic
2101
2102 freepic(string s)
2103 =========
2104 */
2105 void VM_freepic(void)
2106 {
2107         char *s;
2108
2109         VM_SAFEPARMCOUNT(1,VM_freepic);
2110
2111         s = PRVM_G_STRING(OFS_PARM0);
2112         
2113         if(!s)
2114                 PRVM_ERROR ("VM_freepic: %s: NULL\n");
2115         
2116         VM_CheckEmptyString (s);
2117         
2118         Draw_FreePic(s);
2119 }
2120
2121 /*
2122 =========
2123 VM_drawcharacter
2124
2125 float   drawcharacter(vector position, float character, vector scale, vector rgb, float alpha, float flag)
2126 =========
2127 */
2128 void VM_drawcharacter(void)
2129 {
2130         float *pos,*scale,*rgb;
2131         char   character;
2132         int flag;
2133         VM_SAFEPARMCOUNT(6,VM_drawcharacter);
2134
2135         character = (char) PRVM_G_FLOAT(OFS_PARM1);
2136         if(character == 0)
2137         {
2138                 Con_Printf("VM_drawcharacter: %s passed null character !\n",PRVM_NAME);
2139                 PRVM_G_FLOAT(OFS_RETURN) = -1;
2140                 return;
2141         }
2142         
2143         pos = PRVM_G_VECTOR(OFS_PARM0);
2144         scale = PRVM_G_VECTOR(OFS_PARM2);
2145         rgb = PRVM_G_VECTOR(OFS_PARM3);
2146         flag = (int)PRVM_G_FLOAT(OFS_PARM5);
2147         
2148         if(flag < DRAWFLAG_NORMAL || flag >=DRAWFLAG_NUMFLAGS)
2149         {
2150                 Con_Printf("VM_drawcharacter: %s: wrong DRAWFLAG %i !\n",PRVM_NAME,flag);
2151                 PRVM_G_FLOAT(OFS_RETURN) = -2;
2152                 return;
2153         }
2154         
2155         if(pos[2] || scale[2])
2156                 Con_Printf("VM_drawcharacter: z value%c from %s discarded",(pos[2] && scale[2]) ? 's' : 0,((pos[2] && scale[2]) ? "pos and scale" : (pos[2] ? "pos" : "scale"))); 
2157
2158         if(!scale[0] || !scale[1])
2159         {
2160                 Con_Printf("VM_drawcharacter: scale %s is null !\n", (scale[0] == 0) ? ((scale[1] == 0) ? "x and y" : "x") : "y");
2161                 PRVM_G_FLOAT(OFS_RETURN) = -3;
2162                 return;
2163         }
2164
2165         DrawQ_String (pos[0], pos[1], &character, 1, scale[0], scale[1], rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM4), flag);
2166         PRVM_G_FLOAT(OFS_RETURN) = 1;
2167 }       
2168
2169 /*
2170 =========
2171 VM_drawstring
2172
2173 float   drawstring(vector position, string text, vector scale, vector rgb, float alpha, float flag)
2174 =========
2175 */
2176 void VM_drawstring(void)
2177 {
2178         float *pos,*scale,*rgb;
2179         char  *string;
2180         int flag;
2181         VM_SAFEPARMCOUNT(6,VM_drawstring);
2182         
2183         string = PRVM_G_STRING(OFS_PARM1);
2184         if(!string)
2185         {
2186                 Con_Printf("VM_drawstring: %s passed null string !\n",PRVM_NAME);
2187                 PRVM_G_FLOAT(OFS_RETURN) = -1;
2188                 return;
2189         }
2190         
2191         VM_CheckEmptyString(string);
2192         
2193         pos = PRVM_G_VECTOR(OFS_PARM0);
2194         scale = PRVM_G_VECTOR(OFS_PARM2);
2195         rgb = PRVM_G_VECTOR(OFS_PARM3);
2196         flag = (int)PRVM_G_FLOAT(OFS_PARM5);
2197         
2198         if(flag < DRAWFLAG_NORMAL || flag >=DRAWFLAG_NUMFLAGS)
2199         {
2200                 Con_Printf("VM_drawstring: %s: wrong DRAWFLAG %i !\n",PRVM_NAME,flag);
2201                 PRVM_G_FLOAT(OFS_RETURN) = -2;
2202                 return;
2203         }
2204         
2205         if(!scale[0] || !scale[1])
2206         {
2207                 Con_Printf("VM_drawstring: scale %s is null !\n", (scale[0] == 0) ? ((scale[1] == 0) ? "x and y" : "x") : "y");
2208                 PRVM_G_FLOAT(OFS_RETURN) = -3;
2209                 return;
2210         }
2211
2212         if(pos[2] || scale[2])
2213                 Con_Printf("VM_drawstring: z value%c from %s discarded",(pos[2] && scale[2]) ? 's' : 0,((pos[2] && scale[2]) ? "pos and scale" : (pos[2] ? "pos" : "scale"))); 
2214         
2215         DrawQ_String (pos[0], pos[1], string, 0, scale[0], scale[1], rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM4), flag);
2216         PRVM_G_FLOAT(OFS_RETURN) = 1;
2217 }
2218 /*
2219 =========
2220 VM_drawpic
2221
2222 float   drawpic(vector position, string pic, vector size, vector rgb, float alpha, float flag)
2223 =========
2224 */
2225 void VM_drawpic(void)
2226 {
2227         char *pic;
2228         float *size, *pos, *rgb;
2229         int flag;
2230
2231         VM_SAFEPARMCOUNT(6,VM_drawpic);
2232
2233         pic = PRVM_G_STRING(OFS_PARM1);
2234
2235         if(!pic)
2236         {
2237                 Con_Printf("VM_drawpic: %s passed null picture name !\n", PRVM_NAME);
2238                 PRVM_G_FLOAT(OFS_RETURN) = -1;  
2239                 return;
2240         }
2241
2242         VM_CheckEmptyString (pic);
2243
2244         // is pic cached ?
2245         if(!1)
2246         {
2247                 Con_Printf("VM_drawpic: %s: %s not cached !\n", PRVM_NAME, pic);
2248                 PRVM_G_FLOAT(OFS_RETURN) = -4;
2249                 return;
2250         }
2251         
2252         pos = PRVM_G_VECTOR(OFS_PARM0);
2253         size = PRVM_G_VECTOR(OFS_PARM2);
2254         rgb = PRVM_G_VECTOR(OFS_PARM3);
2255         flag = (int) PRVM_G_FLOAT(OFS_PARM5);
2256
2257         if(flag < DRAWFLAG_NORMAL || flag >=DRAWFLAG_NUMFLAGS)
2258         {
2259                 Con_Printf("VM_drawstring: %s: wrong DRAWFLAG %i !\n",PRVM_NAME,flag);
2260                 PRVM_G_FLOAT(OFS_RETURN) = -2;
2261                 return;
2262         }
2263
2264         if(pos[2] || size[2])
2265                 Con_Printf("VM_drawstring: z value%c from %s discarded",(pos[2] && size[2]) ? 's' : 0,((pos[2] && size[2]) ? "pos and size" : (pos[2] ? "pos" : "size"))); 
2266         
2267         DrawQ_Pic(pos[0], pos[1], pic, size[0], size[1], rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM4), flag);
2268         PRVM_G_FLOAT(OFS_RETURN) = 1;
2269 }
2270
2271 /*
2272 =========
2273 VM_drawfill
2274
2275 float drawfill(vector position, vector size, vector rgb, float alpha, float flag)
2276 =========
2277 */
2278 void VM_drawfill(void)
2279 {
2280         float *size, *pos, *rgb;
2281         int flag;
2282         
2283         VM_SAFEPARMCOUNT(5,VM_drawfill);
2284         
2285         
2286         pos = PRVM_G_VECTOR(OFS_PARM0);
2287         size = PRVM_G_VECTOR(OFS_PARM1);
2288         rgb = PRVM_G_VECTOR(OFS_PARM2);
2289         flag = (int) PRVM_G_FLOAT(OFS_PARM4);
2290         
2291         if(flag < DRAWFLAG_NORMAL || flag >=DRAWFLAG_NUMFLAGS)
2292         {
2293                 Con_Printf("VM_drawstring: %s: wrong DRAWFLAG %i !\n",PRVM_NAME,flag);
2294                 PRVM_G_FLOAT(OFS_RETURN) = -2;
2295                 return;
2296         }
2297         
2298         if(pos[2] || size[2])
2299                 Con_Printf("VM_drawstring: z value%c from %s discarded",(pos[2] && size[2]) ? 's' : 0,((pos[2] && size[2]) ? "pos and size" : (pos[2] ? "pos" : "size"))); 
2300         
2301         DrawQ_Pic(pos[0], pos[1], 0, size[0], size[1], rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM3), flag);
2302         PRVM_G_FLOAT(OFS_RETURN) = 1;
2303 }
2304
2305 void VM_Cmd_Init(void)
2306 {
2307         // only init the stuff for the current prog
2308         VM_STRINGS_MEMPOOL = Mem_AllocPool(va("vm_stringsmempool[%s]",PRVM_NAME));
2309         VM_Files_Init();                
2310 }
2311
2312 void VM_Cmd_Reset(void)
2313 {
2314         Mem_EmptyPool(VM_STRINGS_MEMPOOL);
2315         VM_Files_CloseAll();
2316 }
2317
2318 //============================================================================
2319 // Server
2320
2321 char *vm_sv_extensions =
2322 "";
2323
2324 prvm_builtin_t vm_sv_builtins[] = {
2325 0  // to be consistent with the old vm
2326 };
2327
2328 const int vm_sv_numbuiltins = sizeof(vm_sv_builtins) / sizeof(prvm_builtin_t);
2329
2330 void VM_SV_Cmd_Init(void)
2331 {
2332 }
2333
2334 void VM_SV_Cmd_Reset(void)
2335 {
2336 }
2337
2338 //============================================================================
2339 // Client
2340
2341 char *vm_cl_extensions =
2342 "";
2343
2344 prvm_builtin_t vm_cl_builtins[] = {
2345 0  // to be consistent with the old vm
2346 };
2347
2348 const int vm_cl_numbuiltins = sizeof(vm_cl_builtins) / sizeof(prvm_builtin_t);
2349
2350 void VM_CL_Cmd_Init(void)
2351 {
2352 }
2353
2354 void VM_CL_Cmd_Reset(void)
2355 {
2356 }
2357
2358 //============================================================================
2359 // Menu
2360
2361 char *vm_m_extensions =
2362 "";
2363
2364 // void setkeydest(float dest)
2365 void VM_M_setkeydest(void)
2366 {
2367         VM_SAFEPARMCOUNT(1,VM_M_SetKeyDest);
2368
2369         switch((int)PRVM_G_FLOAT(OFS_PARM0))
2370         {
2371         case 0:
2372                 // key_game
2373                 key_dest = key_game;
2374                 break;
2375         case 2:
2376                 // key_menu
2377                 key_dest = key_menu;
2378                 break;
2379         case 1:
2380                 // key_message
2381                 // key_dest = key_message
2382                 // break;
2383         default:
2384                 PRVM_ERROR("VM_M_SetKeyDest: wrong destination %i !\n",prog->globals[OFS_PARM0]);
2385         }
2386
2387         return;
2388 }
2389
2390 // float getkeydest(void)
2391 void VM_M_getkeydest(void)
2392 {
2393         VM_SAFEPARMCOUNT(0,VM_M_GetKeyDest);
2394
2395         // key_game = 0, key_message = 1, key_menu = 2, unknown = 3
2396         switch(key_dest)
2397         {
2398         case key_game:
2399                 PRVM_G_FLOAT(OFS_RETURN) = 0;
2400                 break;
2401         case key_menu:
2402                 PRVM_G_FLOAT(OFS_RETURN) = 2;
2403                 break;
2404         case key_message:
2405                 // not supported
2406                 // PRVM_G_FLOAT(OFS_RETURN) = 1;
2407                 // break;
2408         default:
2409                 PRVM_G_FLOAT(OFS_RETURN) = 3;
2410         }
2411 }
2412
2413 prvm_builtin_t vm_m_builtins[] = {
2414         0, // to be consistent with the old vm
2415         // common builtings (mostly)
2416         VM_checkextension,
2417         VM_error,
2418         VM_objerror,
2419         VM_print,
2420         VM_bprint,
2421         VM_sprint,
2422         VM_centerprint,
2423         VM_normalize,
2424         VM_vlen,
2425         VM_vectoyaw,    // #10
2426         VM_vectoangles,
2427         VM_random,
2428         VM_localcmd,
2429         VM_cvar,
2430         VM_cvar_set,
2431         VM_dprint,
2432         VM_ftos,
2433         VM_fabs,
2434         VM_vtos,
2435         VM_etos,                // 20
2436         VM_stof,
2437         VM_spawn,
2438         VM_remove,
2439         VM_find,
2440         VM_findfloat,
2441         VM_findchain,
2442         VM_findchainfloat,
2443         VM_precache_file,
2444         VM_precache_sound,
2445         VM_coredump,    // 30
2446         VM_traceon,
2447         VM_traceoff,
2448         VM_eprint,
2449         VM_rint,
2450         VM_floor,
2451         VM_ceil,
2452         VM_nextent,
2453         VM_sin,
2454         VM_cos,
2455         VM_sqrt,                // 40
2456         VM_randomvec,
2457         VM_registercvar,
2458         VM_min,
2459         VM_max,
2460         VM_bound,
2461         VM_pow,
2462         VM_copyentity,
2463         VM_fopen,
2464         VM_fclose,
2465         VM_fgets,               // 50
2466         VM_fputs,
2467         VM_strlen,
2468         VM_strcat,
2469         VM_substring,
2470         VM_stov,
2471         VM_strzone,
2472         VM_strunzone,
2473         VM_isserver,
2474         VM_clientcount,
2475         VM_clientstate, // 60
2476         VM_clcommand,
2477         VM_tokenize,
2478         VM_changelevel,
2479         VM_localsound,  // 64
2480         0,
2481         0,
2482         0,
2483         0,
2484         0,
2485         0,                              // 70
2486         e10,                    // 80
2487         e10,                    // 90
2488         e10,                    // 100
2489         e100,                   // 200
2490         e100,                   // 300
2491         e100,                   // 400
2492         // msg functions
2493         VM_WriteByte,
2494         VM_WriteChar,
2495         VM_WriteShort,
2496         VM_WriteLong,
2497         VM_WriteAngle,
2498         VM_WriteCoord,
2499         VM_WriteString,
2500         VM_WriteEntity,
2501         0,
2502         0,                              // 410
2503         e10,                    // 420
2504         e10,                    // 430
2505         e10,                    // 440
2506         e10,                    // 450
2507         // draw functions
2508         VM_iscachedpic,
2509         VM_precache_pic,
2510         VM_freepic,
2511         VM_drawcharacter,
2512         VM_drawstring,
2513         VM_drawpic,
2514         VM_drawfill,    // 457
2515         0,
2516         0,
2517         0,                              // 460
2518         e10,                    // 470
2519         e10,                    // 480
2520         e10,                    // 490
2521         e10,                    // 500
2522         // menu functions
2523         VM_M_setkeydest,
2524         VM_M_getkeydest
2525 };
2526
2527 const int vm_m_numbuiltins = sizeof(vm_m_builtins) / sizeof(prvm_builtin_t);
2528
2529 void VM_M_Cmd_Init(void)
2530 {
2531         VM_Cmd_Init();
2532 }
2533
2534 void VM_M_Cmd_Reset(void)
2535 {
2536         VM_Cmd_Init();
2537 }
2538