]> icculus.org git repositories - divverent/darkplaces.git/blob - prvm_cmds.c
todo item done: sounds spawn even if out of hearing range (because the player might...
[divverent/darkplaces.git] / prvm_cmds.c
1 // AK
2 // Basically every vm builtin cmd should be in here.
3 // All 3 builtin and extension lists can be found here
4 // cause large (I think they will) parts are from pr_cmds the same copyright like in pr_cmds
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                 remove(entity e)
36 entity  find(entity start, .string field, string match)
37
38 entity  findfloat(entity start, .float field, float match)
39 entity  findentity(entity start, .entity field, entity 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 e)
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 vector  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,string,...[string])
72 string  substring(string s, float start, float length)
73 vector  stov(string s)
74 string  strzone(string s)
75                 strunzone(string s)
76 float   tokenize(string s)
77 string  argv(float n)
78 float   isserver()
79 float   clientcount()
80 float   clientstate()
81                 clientcommand(float client, string s) (for client and menu)
82                 changelevel(string map)
83                 localsound(string sample)
84 vector  getmousepos()
85 float   gettime()
86                 loadfromdata(string data)
87                 loadfromfile(string file)
88 float   mod(float val, float m)
89 const string    str_cvar (string)
90                 crash()
91                 stackdump()
92                 
93 float   search_begin(string pattern, float caseinsensitive, float quiet)
94 void    search_end(float handle)
95 float   search_getsize(float handle)
96 string  search_getfilename(float handle, float num)
97                 
98 perhaps only : Menu : WriteMsg 
99 ===============================
100
101                 WriteByte(float data, float dest, float desto)
102                 WriteChar(float data, float dest, float desto)
103                 WriteShort(float data, float dest, float desto)
104                 WriteLong(float data, float dest, float desto)
105                 WriteAngle(float data, float dest, float desto)
106                 WriteCoord(float data, float dest, float desto)
107                 WriteString(string data, float dest, float desto)
108                 WriteEntity(entity data, float dest, float desto)
109                 
110 Client & Menu : draw functions 
111 ===============================
112
113 float   iscachedpic(string pic)
114 string  precache_pic(string pic) 
115                 freepic(string s)
116 float   drawcharacter(vector position, float character, vector scale, vector rgb, float alpha, float flag)
117 float   drawstring(vector position, string text, vector scale, vector rgb, float alpha, float flag)
118 float   drawpic(vector position, string pic, vector size, vector rgb, float alpha, float flag)
119 float   drawfill(vector position, vector size, vector rgb, float alpha, float flag)
120                 drawsetcliparea(float x, float y, float width, float height)
121                 drawresetcliparea()
122 vector  getimagesize(string pic)
123                 
124
125 ==============================================================================
126 menu cmd list:
127 ===============
128
129                 setkeydest(float dest)
130 float   getkeydest
131                 setmousetarget(float target)
132 float   getmousetarget(void)
133
134                 callfunction(...,string function_name)
135                 writetofile(float fhandle, entity ent)
136 float   isfunction(string function_name)
137 vector  getresolution(float number)
138 string  keynumtostring(float keynum)
139
140 */
141
142 #include "quakedef.h"
143 #include "progdefs.h"
144 #include "clprogdefs.h"
145 #include "mprogdefs.h"
146
147 //============================================================================
148 // nice helper macros
149
150 #ifndef VM_NOPARMCHECK
151 #define VM_SAFEPARMCOUNT(p,f)   if(prog->argc != p) PRVM_ERROR(#f " wrong parameter count (" #p " expected ) !\n")
152 #else
153 #define VM_SAFEPARMCOUNT(p,f)
154 #endif
155
156 #define VM_RETURN_EDICT(e)              (((int *)prog->globals)[OFS_RETURN] = PRVM_EDICT_TO_PROG(e))
157
158 #define VM_STRINGS_MEMPOOL              vm_strings_mempool[PRVM_GetProgNr()]
159
160 #define e10 0,0,0,0,0,0,0,0,0,0
161 #define e100 e10,e10,e10,e10,e10,e10,e10,e10,e10,e10
162 #define e1000 e100,e100,e100,e100,e100,e100,e100,e100,e100,e100
163
164 //============================================================================
165 // Common
166
167 // string zone mempool
168 mempool_t *vm_strings_mempool[PRVM_MAXPROGS];
169
170 // temp string handling
171 // LordHavoc: added this to semi-fix the problem of using many ftos calls in a print
172 #define VM_STRINGTEMP_BUFFERS 16
173 #define VM_STRINGTEMP_LENGTH 4096
174 static char vm_string_temp[VM_STRINGTEMP_BUFFERS][VM_STRINGTEMP_LENGTH];
175 static int vm_string_tempindex = 0;
176
177 // qc cvar 
178 #define MAX_QC_CVARS 128 * PRVM_MAXPROGS
179 cvar_t vm_qc_cvar[MAX_QC_CVARS];
180 int vm_currentqc_cvar;
181
182 // qc file handling
183 #define MAX_VMFILES             256
184 #define MAX_PRVMFILES   MAX_VMFILES * PRVM_MAXPROGS
185 #define VM_FILES ((qfile_t**)(vm_files + PRVM_GetProgNr() * MAX_VMFILES))
186
187 qfile_t *vm_files[MAX_PRVMFILES];
188
189 // qc fs search handling
190 #define MAX_VMSEARCHES 128
191 #define TOTAL_VMSEARCHES MAX_VMSEARCHES * PRVM_MAXPROGS
192 #define VM_SEARCHLIST ((fssearch_t**)(vm_fssearchlist + PRVM_GetProgNr() * MAX_VMSEARCHES))
193
194 fssearch_t *vm_fssearchlist[TOTAL_VMSEARCHES];
195
196 static char *VM_GetTempString(void)
197 {
198         char *s;
199         s = vm_string_temp[vm_string_tempindex];
200         vm_string_tempindex = (vm_string_tempindex + 1) % VM_STRINGTEMP_BUFFERS;
201         return s;
202 }
203
204 void VM_CheckEmptyString (char *s)
205 {
206         if (s[0] <= ' ')
207                 PRVM_ERROR ("%s: Bad string", PRVM_NAME);
208 }
209
210 //============================================================================
211 //BUILT-IN FUNCTIONS
212
213 void VM_VarString(int first, char *out, int outlength)
214 {
215         int i;
216         const char *s;
217         char *outend;
218
219         outend = out + outlength - 1;
220         for (i = first;i < prog->argc && out < outend;i++)
221         {
222                 s = PRVM_G_STRING((OFS_PARM0+i*3));
223                 while (out < outend && *s)
224                         *out++ = *s++;
225         }
226         *out++ = 0;
227 }
228
229 /*
230 =================
231 VM_checkextension
232
233 returns true if the extension is supported by the server
234
235 checkextension(extensionname)
236 =================
237 */
238
239 // kind of helper function
240 static qboolean checkextension(char *name)
241 {
242         int len;
243         char *e, *start;
244         len = strlen(name);
245
246         for (e = prog->extensionstring;*e;e++)
247         {
248                 while (*e == ' ')
249                         e++;
250                 if (!*e)
251                         break;
252                 start = e;
253                 while (*e && *e != ' ')
254                         e++;
255                 if (e - start == len)
256                         if (!strncasecmp(start, name, len))
257                         {
258                                 return true;
259                         }
260         }
261         return false;
262 }
263
264 void VM_checkextension (void)
265 {
266         VM_SAFEPARMCOUNT(1,VM_checkextension);
267
268         PRVM_G_FLOAT(OFS_RETURN) = checkextension(PRVM_G_STRING(OFS_PARM0));
269 }
270
271 /*
272 =================
273 VM_error
274
275 This is a TERMINAL error, which will kill off the entire prog.
276 Dumps self.
277
278 error(value)
279 =================
280 */
281 void VM_error (void)
282 {
283         prvm_edict_t    *ed;
284         char string[VM_STRINGTEMP_LENGTH];
285
286         VM_VarString(0, string, sizeof(string));
287         Con_Printf ("======%S ERROR in %s:\n%s\n", PRVM_NAME, PRVM_GetString(prog->xfunction->s_name), string);
288         if(prog->self)
289         {
290                 ed = PRVM_G_EDICT(prog->self->ofs);
291                 PRVM_ED_Print (ed);
292         }
293
294         PRVM_ERROR ("%s: Program error", PRVM_NAME);
295 }
296
297 /*
298 =================
299 VM_objerror
300
301 Dumps out self, then an error message.  The program is aborted and self is
302 removed, but the level can continue.
303
304 objerror(value)
305 =================
306 */
307 void VM_objerror (void)
308 {
309         prvm_edict_t    *ed;
310         char string[VM_STRINGTEMP_LENGTH];
311
312         VM_VarString(0, string, sizeof(string));
313         Con_Printf ("======%s OBJECT ERROR in %s:\n%s\n", PRVM_NAME, PRVM_GetString(prog->xfunction->s_name), string);
314         if(prog->self)
315         {
316                 ed = PRVM_G_EDICT (prog->self->ofs);
317                 PRVM_ED_Print (ed);
318
319                 PRVM_ED_Free (ed);
320         }
321         else
322                 // objerror has to display the object fields -> else call
323                 PRVM_ERROR ("VM_objecterror: self not defined !\n");
324 }
325
326 /*
327 =================
328 VM_print (actually used only by client and menu)
329
330 print to console
331
332 print(string)
333 =================
334 */
335 void VM_print (void)
336 {
337         char string[VM_STRINGTEMP_LENGTH];
338
339         VM_VarString(0, string, sizeof(string));
340         Con_Printf(string);
341 }
342
343 /*
344 =================
345 VM_bprint
346
347 broadcast print to everyone on server
348
349 bprint(...[string])
350 =================
351 */
352 void VM_bprint (void)
353 {
354         char string[VM_STRINGTEMP_LENGTH];
355
356         if(!sv.active)
357         {
358                 Con_Printf("VM_bprint: game is not server(%s) !", PRVM_NAME);
359                 return;
360         }
361
362         VM_VarString(0, string, sizeof(string));
363         SV_BroadcastPrintf("%s", string);
364 }
365
366 /*
367 =================
368 VM_sprint (menu & client but only if server.active == true)
369
370 single print to a specific client
371
372 sprint(float clientnum,...[string])
373 =================
374 */
375 void VM_sprint (void)
376 {
377         client_t        *client;
378         int                     clientnum;
379         char string[VM_STRINGTEMP_LENGTH];
380
381         //find client for this entity
382         clientnum = PRVM_G_FLOAT(OFS_PARM0);
383         if (!sv.active  || clientnum < 0 || clientnum >= svs.maxclients || !svs.clients[clientnum].active)
384         {
385                 Con_Printf("VM_sprint: %s: invalid client or server is not active !", PRVM_NAME);
386                 return;
387         }
388         
389         client = svs.clients + clientnum;
390         if (!client->netconnection)
391                 return;
392         VM_VarString(1, string, sizeof(string));
393         MSG_WriteChar(&client->message,svc_print);
394         MSG_WriteString(&client->message, string);
395 }
396
397 /*
398 =================
399 VM_centerprint
400
401 single print to the screen
402
403 centerprint(clientent, value)
404 =================
405 */
406 void VM_centerprint (void)
407 {
408         char string[VM_STRINGTEMP_LENGTH];
409
410         VM_VarString(0, string, sizeof(string));
411         SCR_CenterPrint(string);
412 }
413
414 /*
415 =================
416 VM_normalize
417
418 vector normalize(vector)
419 =================
420 */
421 void VM_normalize (void)
422 {
423         float   *value1;
424         vec3_t  newvalue;
425         float   new;
426
427         VM_SAFEPARMCOUNT(1,VM_normalize);
428
429         value1 = PRVM_G_VECTOR(OFS_PARM0);
430
431         new = value1[0] * value1[0] + value1[1] * value1[1] + value1[2]*value1[2];
432         new = sqrt(new);
433
434         if (new == 0)
435                 newvalue[0] = newvalue[1] = newvalue[2] = 0;
436         else
437         {
438                 new = 1/new;
439                 newvalue[0] = value1[0] * new;
440                 newvalue[1] = value1[1] * new;
441                 newvalue[2] = value1[2] * new;
442         }
443
444         VectorCopy (newvalue, PRVM_G_VECTOR(OFS_RETURN));
445 }
446
447 /*
448 =================
449 VM_vlen
450
451 scalar vlen(vector)
452 =================
453 */
454 void VM_vlen (void)
455 {
456         float   *value1;
457         float   new;
458
459         VM_SAFEPARMCOUNT(1,VM_vlen);
460
461         value1 = PRVM_G_VECTOR(OFS_PARM0);
462
463         new = value1[0] * value1[0] + value1[1] * value1[1] + value1[2]*value1[2];
464         new = sqrt(new);
465
466         PRVM_G_FLOAT(OFS_RETURN) = new;
467 }
468
469 /*
470 =================
471 VM_vectoyaw
472
473 float vectoyaw(vector)
474 =================
475 */
476 void VM_vectoyaw (void)
477 {
478         float   *value1;
479         float   yaw;
480
481         VM_SAFEPARMCOUNT(1,VM_vectoyaw);
482
483         value1 = PRVM_G_VECTOR(OFS_PARM0);
484
485         if (value1[1] == 0 && value1[0] == 0)
486                 yaw = 0;
487         else
488         {
489                 yaw = (int) (atan2(value1[1], value1[0]) * 180 / M_PI);
490                 if (yaw < 0)
491                         yaw += 360;
492         }
493
494         PRVM_G_FLOAT(OFS_RETURN) = yaw;
495 }
496
497
498 /*
499 =================
500 VM_vectoangles
501
502 vector vectoangles(vector)
503 =================
504 */
505 void VM_vectoangles (void)
506 {
507         float   *value1;
508         float   forward;
509         float   yaw, pitch;
510
511         VM_SAFEPARMCOUNT(1,VM_vectoangles);
512
513         value1 = PRVM_G_VECTOR(OFS_PARM0);
514
515         if (value1[1] == 0 && value1[0] == 0)
516         {
517                 yaw = 0;
518                 if (value1[2] > 0)
519                         pitch = 90;
520                 else
521                         pitch = 270;
522         }
523         else
524         {
525                 // LordHavoc: optimized a bit
526                 if (value1[0])
527                 {
528                         yaw = (atan2(value1[1], value1[0]) * 180 / M_PI);
529                         if (yaw < 0)
530                                 yaw += 360;
531                 }
532                 else if (value1[1] > 0)
533                         yaw = 90;
534                 else
535                         yaw = 270;
536
537                 forward = sqrt(value1[0]*value1[0] + value1[1]*value1[1]);
538                 pitch = (int) (atan2(value1[2], forward) * 180 / M_PI);
539                 if (pitch < 0)
540                         pitch += 360;
541         }
542
543         PRVM_G_FLOAT(OFS_RETURN+0) = pitch;
544         PRVM_G_FLOAT(OFS_RETURN+1) = yaw;
545         PRVM_G_FLOAT(OFS_RETURN+2) = 0;
546 }
547
548 /*
549 =================
550 VM_random
551
552 Returns a number from 0<= num < 1
553
554 float random()
555 =================
556 */
557 void VM_random (void)
558 {
559         float           num;
560
561         VM_SAFEPARMCOUNT(0,VM_random);
562
563         num = (rand ()&0x7fff) / ((float)0x7fff);
564
565         PRVM_G_FLOAT(OFS_RETURN) = num;
566 }
567
568 /*
569 =================
570 PF_sound
571
572 Each entity can have eight independant sound sources, like voice,
573 weapon, feet, etc.
574
575 Channel 0 is an auto-allocate channel, the others override anything
576 already running on that entity/channel pair.
577
578 An attenuation of 0 will play full volume everywhere in the level.
579 Larger attenuations will drop off.
580
581 =================
582 */
583 /*
584 void PF_sound (void)
585 {
586         char            *sample;
587         int                     channel;
588         edict_t         *entity;
589         int             volume;
590         float attenuation;
591
592         entity = G_EDICT(OFS_PARM0);
593         channel = G_FLOAT(OFS_PARM1);
594         sample = G_STRING(OFS_PARM2);
595         volume = G_FLOAT(OFS_PARM3) * 255;
596         attenuation = G_FLOAT(OFS_PARM4);
597
598         if (volume < 0 || volume > 255)
599                 Host_Error ("SV_StartSound: volume = %i", volume);
600
601         if (attenuation < 0 || attenuation > 4)
602                 Host_Error ("SV_StartSound: attenuation = %f", attenuation);
603
604         if (channel < 0 || channel > 7)
605                 Host_Error ("SV_StartSound: channel = %i", channel);
606
607         SV_StartSound (entity, channel, sample, volume, attenuation);
608 }
609 */
610
611 /*
612 =========
613 VM_localsound
614
615 localsound(string sample)
616 =========
617 */
618 void VM_localsound(void)
619 {
620         char *s;
621         
622         VM_SAFEPARMCOUNT(1,VM_localsound);
623
624         s = PRVM_G_STRING(OFS_PARM0);
625
626         if(!S_GetCached(s))
627         {
628                 Con_Printf("VM_localsound: %s : %s not cached !\n", PRVM_NAME, s);
629                 PRVM_G_FLOAT(OFS_RETURN) = -4;
630                 return;
631         }               
632
633         S_LocalSound(s);
634         PRVM_G_FLOAT(OFS_RETURN) = 1;
635 }
636
637 /*
638 =================
639 VM_break
640
641 break()
642 =================
643 */
644 void VM_break (void)
645 {
646         PRVM_ERROR ("%s: break statement", PRVM_NAME);
647 }
648
649 //============================================================================
650
651 /*
652 =================
653 VM_localcmd
654
655 Sends text over to the client's execution buffer
656
657 [localcmd (string) or]
658 cmd (string)
659 =================
660 */
661 void VM_localcmd (void)
662 {
663         VM_SAFEPARMCOUNT(1,VM_localcmd);
664
665         Cbuf_AddText(PRVM_G_STRING(OFS_PARM0));
666 }
667
668 /*
669 =================
670 VM_cvar
671
672 float cvar (string)
673 =================
674 */
675 void VM_cvar (void)
676 {
677         VM_SAFEPARMCOUNT(1,VM_cvar);
678
679         PRVM_G_FLOAT(OFS_RETURN) = Cvar_VariableValue(PRVM_G_STRING(OFS_PARM0));
680 }
681
682 /*
683 =================
684 VM_str_cvar
685
686 const string    str_cvar (string)
687 =================
688 */
689 void VM_str_cvar(void) 
690 {
691         char *out, *name;
692         const char *cvar_string;
693         VM_SAFEPARMCOUNT(1,VM_str_cvar);
694
695         name = PRVM_G_STRING(OFS_PARM0);
696
697         if(!name)
698                 PRVM_ERROR("VM_str_cvar: %s: null string\n", PRVM_NAME);
699
700         VM_CheckEmptyString(name);
701
702         out = VM_GetTempString(); 
703
704         cvar_string = Cvar_VariableString(name);
705         
706         strcpy(out, cvar_string);
707
708         PRVM_G_INT(OFS_PARM0) = PRVM_SetString(out);
709 }
710
711 /*
712 =================
713 VM_cvar_set
714
715 void cvar_set (string,string)
716 =================
717 */
718 void VM_cvar_set (void)
719 {
720         VM_SAFEPARMCOUNT(2,VM_cvar_set);
721
722         Cvar_Set(PRVM_G_STRING(OFS_PARM0), PRVM_G_STRING(OFS_PARM1));
723 }
724
725 /*
726 =========
727 VM_dprint
728
729 dprint(...[string])
730 =========
731 */
732 void VM_dprint (void)
733 {
734         char string[VM_STRINGTEMP_LENGTH];
735         if (developer.integer)
736         {
737                 VM_VarString(0, string, sizeof(string));
738                 Con_Printf("%s: %s", PRVM_NAME, string);
739         }
740 }
741
742 /*
743 =========
744 VM_ftos
745
746 string  ftos(float)
747 =========
748 */
749
750 void VM_ftos (void)
751 {
752         float v;
753         char *s;
754
755         VM_SAFEPARMCOUNT(1, VM_ftos);
756
757         v = PRVM_G_FLOAT(OFS_PARM0);
758
759         s = VM_GetTempString();
760         if ((float)((int)v) == v)
761                 sprintf(s, "%i", (int)v);
762         else
763                 sprintf(s, "%f", v);
764         PRVM_G_INT(OFS_RETURN) = PRVM_SetString(s);
765 }
766
767 /*
768 =========
769 VM_fabs
770
771 float   fabs(float)
772 =========
773 */
774
775 void VM_fabs (void)
776 {
777         float   v;
778
779         VM_SAFEPARMCOUNT(1,VM_fabs);
780
781         v = PRVM_G_FLOAT(OFS_PARM0);
782         PRVM_G_FLOAT(OFS_RETURN) = fabs(v);
783 }
784
785 /*
786 =========
787 VM_vtos
788
789 string  vtos(vector)
790 =========
791 */
792
793 void VM_vtos (void)
794 {
795         char *s;
796
797         VM_SAFEPARMCOUNT(1,VM_vtos);
798
799         s = VM_GetTempString();
800         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]);
801         PRVM_G_INT(OFS_RETURN) = PRVM_SetString(s);
802 }
803
804 /*
805 =========
806 VM_etos
807
808 string  etos(entity)
809 =========
810 */
811
812 void VM_etos (void)
813 {
814         char *s;
815
816         VM_SAFEPARMCOUNT(1, VM_etos);
817
818         s = VM_GetTempString();
819         sprintf (s, "entity %i", PRVM_G_EDICTNUM(OFS_PARM0));
820         PRVM_G_INT(OFS_RETURN) = PRVM_SetString(s);
821 }
822
823 /*
824 =========
825 VM_stof
826
827 float stof(...[string])
828 =========
829 */
830 void VM_stof(void)
831 {
832         char string[VM_STRINGTEMP_LENGTH];
833         VM_VarString(0, string, sizeof(string));
834         PRVM_G_FLOAT(OFS_RETURN) = atof(string);
835 }
836
837 /*
838 =========
839 VM_spawn
840
841 entity spawn()
842 =========
843 */
844
845 void VM_spawn (void)
846 {
847         prvm_edict_t    *ed;
848         prog->xfunction->builtinsprofile += 20;
849         ed = PRVM_ED_Alloc();
850         VM_RETURN_EDICT(ed);
851 }
852
853 /*
854 =========
855 VM_remove
856
857 entity  remove()
858 =========
859 */
860
861 void VM_remove (void)
862 {
863         prvm_edict_t    *ed;
864         prog->xfunction->builtinsprofile += 20;
865
866         VM_SAFEPARMCOUNT(1, VM_remove);
867
868         ed = PRVM_G_EDICT(OFS_PARM0);
869 //      if (ed == prog->edicts)
870 //              PRVM_ERROR ("remove: tried to remove world\n");
871 //      if (PRVM_NUM_FOR_EDICT(ed) <= sv.maxclients)
872 //              Host_Error("remove: tried to remove a client\n");
873         PRVM_ED_Free (ed);
874 }
875
876 /*
877 =========
878 VM_find
879
880 entity  find(entity start, .string field, string match)
881 =========
882 */
883
884 void VM_find (void)
885 {
886         int             e;
887         int             f;
888         char    *s, *t;
889         prvm_edict_t    *ed;
890
891         VM_SAFEPARMCOUNT(3,VM_find);
892
893         e = PRVM_G_EDICTNUM(OFS_PARM0);
894         f = PRVM_G_INT(OFS_PARM1);
895         s = PRVM_G_STRING(OFS_PARM2);
896
897         if (!s || !s[0])
898         {
899                 // return reserved edict 0 (could be used for whatever the prog wants)
900                 VM_RETURN_EDICT(prog->edicts);
901                 return;
902         }
903
904         for (e++ ; e < prog->num_edicts ; e++)
905         {
906                 prog->xfunction->builtinsprofile++;
907                 ed = PRVM_EDICT_NUM(e);
908                 if (ed->e->free)
909                         continue;
910                 t = PRVM_E_STRING(ed,f);
911                 if (!t)
912                         continue;
913                 if (!strcmp(t,s))
914                 {
915                         VM_RETURN_EDICT(ed);
916                         return;
917                 }
918         }
919
920         VM_RETURN_EDICT(prog->edicts);
921 }
922
923 /*
924 =========
925 VM_findfloat
926
927   entity        findfloat(entity start, .float field, float match)
928   entity        findentity(entity start, .entity field, entity match)
929 =========
930 */
931 // LordHavoc: added this for searching float, int, and entity reference fields
932 void VM_findfloat (void)
933 {
934         int             e;
935         int             f;
936         float   s;
937         prvm_edict_t    *ed;
938
939         VM_SAFEPARMCOUNT(3,VM_findfloat);
940
941         e = PRVM_G_EDICTNUM(OFS_PARM0);
942         f = PRVM_G_INT(OFS_PARM1);
943         s = PRVM_G_FLOAT(OFS_PARM2);
944
945         for (e++ ; e < prog->num_edicts ; e++)
946         {
947                 prog->xfunction->builtinsprofile++;
948                 ed = PRVM_EDICT_NUM(e);
949                 if (ed->e->free)
950                         continue;
951                 if (PRVM_E_FLOAT(ed,f) == s)
952                 {
953                         VM_RETURN_EDICT(ed);
954                         return;
955                 }
956         }
957
958         VM_RETURN_EDICT(prog->edicts);
959 }
960
961 /*
962 =========
963 VM_findchain
964
965 entity  findchain(.string field, string match)
966 =========
967 */
968 int PRVM_ED_FindFieldOffset(const char *field);
969 // chained search for strings in entity fields
970 // entity(.string field, string match) findchain = #402;
971 void VM_findchain (void)
972 {
973         int             i;
974         int             f;
975         int             chain_of;
976         char    *s, *t;
977         prvm_edict_t    *ent, *chain;
978
979         VM_SAFEPARMCOUNT(2,VM_findchain);
980
981         // is the same like !(prog->flag & PRVM_FE_CHAIN) - even if the operator precedence is another
982         if(!prog->flag & PRVM_FE_CHAIN)
983                 PRVM_ERROR("VM_findchain: %s doesnt have a chain field !\n", PRVM_NAME);
984
985         chain_of = PRVM_ED_FindFieldOffset ("chain");
986
987         chain = prog->edicts;
988
989         f = PRVM_G_INT(OFS_PARM0);
990         s = PRVM_G_STRING(OFS_PARM1);
991         if (!s || !s[0])
992         {
993                 VM_RETURN_EDICT(prog->edicts);
994                 return;
995         }
996
997         ent = PRVM_NEXT_EDICT(prog->edicts);
998         for (i = 1;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
999         {
1000                 prog->xfunction->builtinsprofile++;
1001                 if (ent->e->free)
1002                         continue;
1003                 t = PRVM_E_STRING(ent,f);
1004                 if (!t)
1005                         continue;
1006                 if (strcmp(t,s))
1007                         continue;
1008
1009                 PRVM_E_FLOAT(ent,chain_of) = PRVM_NUM_FOR_EDICT(chain);
1010                 chain = ent;
1011         }
1012
1013         VM_RETURN_EDICT(chain);
1014 }
1015
1016 /*
1017 =========
1018 VM_findchainfloat
1019
1020 entity  findchainfloat(.string field, float match)
1021 entity  findchainentity(.string field, entity match)
1022 =========
1023 */
1024 // LordHavoc: chained search for float, int, and entity reference fields
1025 // entity(.string field, float match) findchainfloat = #403;
1026 void VM_findchainfloat (void)
1027 {
1028         int             i;
1029         int             f;
1030         int             chain_of;
1031         float   s;
1032         prvm_edict_t    *ent, *chain;
1033
1034         VM_SAFEPARMCOUNT(2, VM_findchainfloat);
1035
1036         if(!prog->flag & PRVM_FE_CHAIN)
1037                 PRVM_ERROR("VM_findchainfloat: %s doesnt have a chain field !\n", PRVM_NAME);
1038
1039         chain_of = PRVM_ED_FindFieldOffset ("chain");
1040
1041         chain = (prvm_edict_t *)prog->edicts;
1042
1043         f = PRVM_G_INT(OFS_PARM0);
1044         s = PRVM_G_FLOAT(OFS_PARM1);
1045
1046         ent = PRVM_NEXT_EDICT(prog->edicts);
1047         for (i = 1;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1048         {
1049                 prog->xfunction->builtinsprofile++;
1050                 if (ent->e->free)
1051                         continue;
1052                 if (E_FLOAT(ent,f) != s)
1053                         continue;
1054
1055                 PRVM_E_FLOAT(ent,chain_of) = PRVM_NUM_FOR_EDICT(chain);
1056                 chain = ent;
1057         }
1058
1059         VM_RETURN_EDICT(chain);
1060 }
1061
1062 /*
1063 =========
1064 VM_precache_file
1065
1066 string  precache_file(string)
1067 =========
1068 */
1069 void VM_precache_file (void)
1070 {       // precache_file is only used to copy files with qcc, it does nothing
1071         VM_SAFEPARMCOUNT(1,VM_precache_file);
1072
1073         PRVM_G_INT(OFS_RETURN) = PRVM_G_INT(OFS_PARM0);
1074 }
1075
1076 /*
1077 =========
1078 VM_preache_error
1079
1080 used instead of the other VM_precache_* functions in the builtin list
1081 =========
1082 */
1083
1084 void VM_precache_error (void)
1085 {
1086         PRVM_ERROR ("PF_Precache_*: Precache can only be done in spawn functions");
1087 }
1088
1089 /*
1090 =========
1091 VM_precache_sound
1092
1093 string  precache_sound (string sample)
1094 =========
1095 */
1096 void VM_precache_sound (void)
1097 {
1098         char    *s;
1099
1100         VM_SAFEPARMCOUNT(1, VM_precache_sound);
1101
1102         s = PRVM_G_STRING(OFS_PARM0);
1103         PRVM_G_INT(OFS_RETURN) = PRVM_G_INT(OFS_PARM0);
1104         VM_CheckEmptyString (s);
1105         
1106         if(S_GetCached(s))
1107         {
1108                 Con_Printf("VM_precache_sound: %s already cached (%s)\n", s, PRVM_NAME);
1109                 return;
1110         }
1111         
1112         if(!S_PrecacheSound(s,true))
1113                 Con_Printf("VM_prache_sound: Failed to load %s for %s\n", s, PRVM_NAME);
1114 }
1115
1116 /*
1117 =========
1118 VM_coredump
1119
1120 coredump()
1121 =========
1122 */
1123 void VM_coredump (void)
1124 {
1125         VM_SAFEPARMCOUNT(0,VM_coredump);
1126
1127         Cbuf_AddText("prvm_edicts ");
1128         Cbuf_AddText(PRVM_NAME);
1129         Cbuf_AddText("\n");
1130 }
1131
1132 /*
1133 =========
1134 VM_stackdump
1135
1136 stackdump()
1137 =========
1138 */
1139 void PRVM_StackTrace(void);
1140 void VM_stackdump (void)
1141 {
1142         VM_SAFEPARMCOUNT(0, VM_stackdump);
1143
1144         PRVM_StackTrace();
1145 }
1146
1147 /*
1148 =========
1149 VM_crash
1150
1151 crash()
1152 =========
1153 */
1154
1155 void VM_crash(void) 
1156 {
1157         VM_SAFEPARMCOUNT(0, VM_crash);
1158
1159         PRVM_ERROR("Crash called by %s\n",PRVM_NAME);
1160 }
1161
1162 /*
1163 =========
1164 VM_traceon
1165
1166 traceon()
1167 =========
1168 */
1169 void VM_traceon (void)
1170 {
1171         VM_SAFEPARMCOUNT(0,VM_traceon);
1172
1173         prog->trace = true;
1174 }
1175
1176 /*
1177 =========
1178 VM_traceoff
1179
1180 traceoff()
1181 =========
1182 */
1183 void VM_traceoff (void)
1184 {
1185         VM_SAFEPARMCOUNT(0,VM_traceoff);
1186
1187         prog->trace = false;
1188 }
1189
1190 /*
1191 =========
1192 VM_eprint
1193
1194 eprint(entity e)
1195 =========
1196 */
1197 void VM_eprint (void)
1198 {
1199         VM_SAFEPARMCOUNT(1,VM_eprint);
1200
1201         PRVM_ED_PrintNum (PRVM_G_EDICTNUM(OFS_PARM0));
1202 }
1203
1204 /*
1205 =========
1206 VM_rint
1207
1208 float   rint(float)
1209 =========
1210 */
1211 void VM_rint (void)
1212 {
1213         float   f;
1214
1215         VM_SAFEPARMCOUNT(1,VM_rint);
1216
1217         f = PRVM_G_FLOAT(OFS_PARM0);
1218         if (f > 0)
1219                 PRVM_G_FLOAT(OFS_RETURN) = (int)(f + 0.5);
1220         else
1221                 PRVM_G_FLOAT(OFS_RETURN) = (int)(f - 0.5);
1222 }
1223
1224 /*
1225 =========
1226 VM_floor
1227
1228 float   floor(float)
1229 =========
1230 */
1231 void VM_floor (void)
1232 {
1233         VM_SAFEPARMCOUNT(1,VM_floor);
1234
1235         PRVM_G_FLOAT(OFS_RETURN) = floor(PRVM_G_FLOAT(OFS_PARM0));
1236 }
1237
1238 /*
1239 =========
1240 VM_ceil
1241
1242 float   ceil(float)
1243 =========
1244 */
1245 void VM_ceil (void)
1246 {
1247         VM_SAFEPARMCOUNT(1,VM_ceil);
1248
1249         PRVM_G_FLOAT(OFS_RETURN) = ceil(PRVM_G_FLOAT(OFS_PARM0));
1250 }
1251
1252
1253 /*
1254 =============
1255 VM_nextent
1256
1257 entity  nextent(entity)
1258 =============
1259 */
1260 void VM_nextent (void)
1261 {
1262         int             i;
1263         prvm_edict_t    *ent;
1264
1265         i = PRVM_G_EDICTNUM(OFS_PARM0);
1266         while (1)
1267         {
1268                 prog->xfunction->builtinsprofile++;
1269                 i++;
1270                 if (i == prog->num_edicts)
1271                 {
1272                         VM_RETURN_EDICT(prog->edicts);
1273                         return;
1274                 }
1275                 ent = PRVM_EDICT_NUM(i);
1276                 if (!ent->e->free)
1277                 {
1278                         VM_RETURN_EDICT(ent);
1279                         return;
1280                 }
1281         }
1282 }
1283
1284 /*
1285 ===============================================================================
1286 MESSAGE WRITING
1287
1288 used only for client and menu
1289 severs uses VM_SV_...
1290
1291 Write*(* data, float type, float to)
1292
1293 ===============================================================================
1294 */
1295
1296 #define MSG_BROADCAST   0               // unreliable to all
1297 #define MSG_ONE                 1               // reliable to one (msg_entity)
1298 #define MSG_ALL                 2               // reliable to all
1299 #define MSG_INIT                3               // write to the init string
1300
1301 sizebuf_t *VM_WriteDest (void)
1302 {
1303         int             dest;
1304         int             destclient;
1305
1306         if(!sv.active)
1307                 PRVM_ERROR("VM_WriteDest: game is not server (%s)\n", PRVM_NAME);
1308
1309         dest = G_FLOAT(OFS_PARM1);
1310         switch (dest)
1311         {
1312         case MSG_BROADCAST:
1313                 return &sv.datagram;
1314
1315         case MSG_ONE:
1316                 destclient = (int) PRVM_G_FLOAT(OFS_PARM2);
1317                 if (destclient < 0 || destclient >= svs.maxclients || !svs.clients[destclient].active)
1318                         PRVM_ERROR("VM_clientcommand: %s: invalid client !\n", PRVM_NAME);
1319
1320                 return &svs.clients[destclient].message;
1321
1322         case MSG_ALL:
1323                 return &sv.reliable_datagram;
1324
1325         case MSG_INIT:
1326                 return &sv.signon;
1327
1328         default:
1329                 PRVM_ERROR ("WriteDest: bad destination");
1330                 break;
1331         }
1332
1333         return NULL;
1334 }
1335
1336 void VM_WriteByte (void)
1337 {
1338         MSG_WriteByte (VM_WriteDest(), PRVM_G_FLOAT(OFS_PARM0));
1339 }
1340
1341 void VM_WriteChar (void)
1342 {
1343         MSG_WriteChar (VM_WriteDest(), PRVM_G_FLOAT(OFS_PARM0));
1344 }
1345
1346 void VM_WriteShort (void)
1347 {
1348         MSG_WriteShort (VM_WriteDest(), PRVM_G_FLOAT(OFS_PARM0));
1349 }
1350
1351 void VM_WriteLong (void)
1352 {
1353         MSG_WriteLong (VM_WriteDest(), PRVM_G_FLOAT(OFS_PARM0));
1354 }
1355
1356 void VM_WriteAngle (void)
1357 {
1358         MSG_WriteAngle (VM_WriteDest(), PRVM_G_FLOAT(OFS_PARM0));
1359 }
1360
1361 void VM_WriteCoord (void)
1362 {
1363         MSG_WriteDPCoord (VM_WriteDest(), PRVM_G_FLOAT(OFS_PARM0));
1364 }
1365
1366 void VM_WriteString (void)
1367 {
1368         MSG_WriteString (VM_WriteDest(), PRVM_G_STRING(OFS_PARM0));
1369 }
1370
1371 void VM_WriteEntity (void)
1372 {
1373         MSG_WriteShort (VM_WriteDest(), PRVM_G_EDICTNUM(OFS_PARM0));
1374 }
1375
1376 //=============================================================================
1377
1378 /*
1379 ==============
1380 VM_changelevel
1381 server and menu
1382
1383 changelevel(string map)
1384 ==============
1385 */
1386 void VM_changelevel (void)
1387 {
1388         char    *s;
1389
1390         VM_SAFEPARMCOUNT(1, VM_changelevel);
1391
1392         if(!sv.active)
1393         {
1394                 Con_Printf("VM_changelevel: game is not server (%s)\n", PRVM_NAME); 
1395                 return;
1396         }
1397
1398 // make sure we don't issue two changelevels
1399         if (svs.changelevel_issued)
1400                 return;
1401         svs.changelevel_issued = true;
1402
1403         s = G_STRING(OFS_PARM0);
1404         Cbuf_AddText (va("changelevel %s\n",s));
1405 }
1406
1407 /*
1408 =========
1409 VM_sin
1410
1411 float   sin(float)
1412 =========
1413 */
1414 void VM_sin (void)
1415 {
1416         VM_SAFEPARMCOUNT(1,VM_sin);
1417         PRVM_G_FLOAT(OFS_RETURN) = sin(PRVM_G_FLOAT(OFS_PARM0));
1418 }
1419
1420 /*
1421 =========
1422 VM_cos
1423 float   cos(float)
1424 =========
1425 */
1426 void VM_cos (void)
1427 {
1428         VM_SAFEPARMCOUNT(1,VM_cos);
1429         PRVM_G_FLOAT(OFS_RETURN) = cos(PRVM_G_FLOAT(OFS_PARM0));
1430 }
1431
1432 /*
1433 =========
1434 VM_sqrt
1435
1436 float   sqrt(float)
1437 =========
1438 */
1439 void VM_sqrt (void)
1440 {
1441         VM_SAFEPARMCOUNT(1,VM_sqrt);
1442         PRVM_G_FLOAT(OFS_RETURN) = sqrt(PRVM_G_FLOAT(OFS_PARM0));
1443 }
1444
1445 /*
1446 =================
1447 VM_randomvec
1448
1449 Returns a vector of length < 1 and > 0
1450
1451 vector randomvec()
1452 =================
1453 */
1454 void VM_randomvec (void)
1455 {
1456         vec3_t          temp;
1457         //float         length;
1458
1459         VM_SAFEPARMCOUNT(0, VM_randomvec);
1460
1461         //// WTF ??
1462         do
1463         {
1464                 temp[0] = (rand()&32767) * (2.0 / 32767.0) - 1.0;
1465                 temp[1] = (rand()&32767) * (2.0 / 32767.0) - 1.0;
1466                 temp[2] = (rand()&32767) * (2.0 / 32767.0) - 1.0;
1467         }
1468         while (DotProduct(temp, temp) >= 1);
1469         VectorCopy (temp, PRVM_G_VECTOR(OFS_RETURN));
1470
1471         /*
1472         temp[0] = (rand()&32767) * (2.0 / 32767.0) - 1.0;
1473         temp[1] = (rand()&32767) * (2.0 / 32767.0) - 1.0;
1474         temp[2] = (rand()&32767) * (2.0 / 32767.0) - 1.0;
1475         // length returned always > 0
1476         length = (rand()&32766 + 1) * (1.0 / 32767.0) / VectorLength(temp);
1477         VectorScale(temp,length, temp);*/
1478         //VectorCopy(temp, PRVM_G_VECTOR(OFS_RETURN));
1479 }
1480
1481 //=============================================================================
1482
1483 /*
1484 =========
1485 VM_registercvar
1486
1487 float   registercvar (string name, string value)
1488 =========
1489 */
1490 void VM_registercvar (void)
1491 {
1492         char *name, *value;
1493         cvar_t *variable;
1494
1495         VM_SAFEPARMCOUNT(2,VM_registercvar);
1496
1497         name = PRVM_G_STRING(OFS_PARM0);
1498         value = PRVM_G_STRING(OFS_PARM1);
1499         PRVM_G_FLOAT(OFS_RETURN) = 0;
1500 // first check to see if it has already been defined
1501         if (Cvar_FindVar (name))
1502                 return;
1503
1504 // check for overlap with a command
1505         if (Cmd_Exists (name))
1506         {
1507                 Con_Printf ("VM_registercvar: %s is a command\n", name);
1508                 return;
1509         }
1510
1511         if (vm_currentqc_cvar >= MAX_QC_CVARS)
1512                 PRVM_ERROR ("VM_registercvar: ran out of cvar slots (%i)\n", MAX_QC_CVARS);
1513
1514 // copy the name and value
1515         variable = &vm_qc_cvar[vm_currentqc_cvar++];
1516         variable->name = Z_Malloc (strlen(name)+1);
1517         strcpy (variable->name, name);
1518         variable->string = Z_Malloc (strlen(value)+1);
1519         strcpy (variable->string, value);
1520         variable->value = atof (value);
1521
1522         Cvar_RegisterVariable(variable);
1523         PRVM_G_FLOAT(OFS_RETURN) = 1; // success
1524 }
1525
1526 /*
1527 =================
1528 VM_min
1529
1530 returns the minimum of two supplied floats
1531
1532 float min(float a, float b, ...[float])
1533 =================
1534 */
1535 void VM_min (void)
1536 {
1537         // LordHavoc: 3+ argument enhancement suggested by FrikaC
1538         if (prog->argc == 2)
1539                 PRVM_G_FLOAT(OFS_RETURN) = min(PRVM_G_FLOAT(OFS_PARM0), PRVM_G_FLOAT(OFS_PARM1));
1540         else if (prog->argc >= 3)
1541         {
1542                 int i;
1543                 float f = PRVM_G_FLOAT(OFS_PARM0);
1544                 for (i = 1;i < prog->argc;i++)
1545                         if (PRVM_G_FLOAT((OFS_PARM0+i*3)) < f)
1546                                 f = PRVM_G_FLOAT((OFS_PARM0+i*3));
1547                 PRVM_G_FLOAT(OFS_RETURN) = f;
1548         }
1549         else
1550                 PRVM_ERROR("VM_min: %s must supply at least 2 floats\n", PRVM_NAME);
1551 }
1552
1553 /*
1554 =================
1555 VM_max
1556
1557 returns the maximum of two supplied floats
1558
1559 float   max(float a, float b, ...[float])
1560 =================
1561 */
1562 void VM_max (void)
1563 {
1564         // LordHavoc: 3+ argument enhancement suggested by FrikaC
1565         if (prog->argc == 2)
1566                 PRVM_G_FLOAT(OFS_RETURN) = max(PRVM_G_FLOAT(OFS_PARM0), PRVM_G_FLOAT(OFS_PARM1));
1567         else if (prog->argc >= 3)
1568         {
1569                 int i;
1570                 float f = PRVM_G_FLOAT(OFS_PARM0);
1571                 for (i = 1;i < prog->argc;i++)
1572                         if (PRVM_G_FLOAT((OFS_PARM0+i*3)) > f)
1573                                 f = PRVM_G_FLOAT((OFS_PARM0+i*3));
1574                 G_FLOAT(OFS_RETURN) = f;
1575         }
1576         else
1577                 PRVM_ERROR("VM_max: %s must supply at least 2 floats\n", PRVM_NAME);
1578 }
1579
1580 /*
1581 =================
1582 VM_bound
1583
1584 returns number bounded by supplied range
1585
1586 float   bound(float min, float value, float max)
1587 =================
1588 */
1589 void VM_bound (void)
1590 {
1591         VM_SAFEPARMCOUNT(3,VM_bound);
1592         PRVM_G_FLOAT(OFS_RETURN) = bound(PRVM_G_FLOAT(OFS_PARM0), PRVM_G_FLOAT(OFS_PARM1), PRVM_G_FLOAT(OFS_PARM2));
1593 }
1594
1595 /*
1596 =================
1597 VM_pow
1598
1599 returns a raised to power b
1600
1601 float   pow(float a, float b)
1602 =================
1603 */
1604 void VM_pow (void)
1605 {
1606         VM_SAFEPARMCOUNT(2,VM_pow);
1607         PRVM_G_FLOAT(OFS_RETURN) = pow(PRVM_G_FLOAT(OFS_PARM0), PRVM_G_FLOAT(OFS_PARM1));
1608 }
1609
1610 /*
1611 =================
1612 VM_copyentity
1613
1614 copies data from one entity to another
1615
1616 copyentity(entity src, entity dst)
1617 =================
1618 */
1619 void VM_copyentity (void)
1620 {
1621         prvm_edict_t *in, *out;
1622         VM_SAFEPARMCOUNT(2,VM_copyentity);
1623         in = PRVM_G_EDICT(OFS_PARM0);
1624         out = PRVM_G_EDICT(OFS_PARM1);
1625         memcpy(out->v, in->v, prog->progs->entityfields * 4);
1626 }
1627
1628 /*
1629 =================
1630 VM_setcolor
1631
1632 sets the color of a client and broadcasts the update to all connected clients
1633
1634 setcolor(clientent, value)
1635 =================
1636 */
1637 /*void PF_setcolor (void)
1638 {
1639         client_t *client;
1640         int entnum, i;
1641         eval_t *val;
1642
1643         entnum = G_EDICTNUM(OFS_PARM0);
1644         i = G_FLOAT(OFS_PARM1);
1645
1646         if (entnum < 1 || entnum > svs.maxclients || !svs.clients[entnum-1].active)
1647         {
1648                 Con_Printf ("tried to setcolor a non-client\n");
1649                 return;
1650         }
1651
1652         client = svs.clients + entnum-1;
1653         if ((val = GETEDICTFIELDVALUE(client->edict, eval_clientcolors)))
1654                 val->_float = i;
1655         client->colors = i;
1656         client->old_colors = i;
1657         client->edict->v->team = (i & 15) + 1;
1658
1659         MSG_WriteByte (&sv.reliable_datagram, svc_updatecolors);
1660         MSG_WriteByte (&sv.reliable_datagram, entnum - 1);
1661         MSG_WriteByte (&sv.reliable_datagram, i);
1662 }*/
1663
1664 void VM_Files_Init(void)
1665 {
1666         memset(VM_FILES, 0, sizeof(qfile_t*[MAX_VMFILES]));
1667 }
1668
1669 void VM_Files_CloseAll(void)
1670 {
1671         int i;
1672         for (i = 0;i < MAX_VMFILES;i++)
1673         {
1674                 if (VM_FILES[i])
1675                         FS_Close(VM_FILES[i]);
1676                 //VM_FILES[i] = NULL;
1677         }
1678         memset(VM_FILES,0,sizeof(qfile_t*[MAX_VMFILES])); // this should be faster (is it ?)
1679 }
1680
1681 /*
1682 =========
1683 VM_fopen
1684
1685 float   fopen(string filename, float mode)
1686 =========
1687 */
1688 // float(string filename, float mode) fopen = #110;
1689 // opens a file inside quake/gamedir/data/ (mode is FILE_READ, FILE_APPEND, or FILE_WRITE),
1690 // returns fhandle >= 0 if successful, or fhandle < 0 if unable to open file for any reason
1691 void VM_fopen(void)
1692 {
1693         int filenum, mode;
1694         char *modestring, *filename;
1695
1696         VM_SAFEPARMCOUNT(2,VM_fopen);
1697
1698         for (filenum = 0;filenum < MAX_VMFILES;filenum++)
1699                 if (VM_FILES[filenum] == NULL)
1700                         break;
1701         if (filenum >= MAX_VMFILES)
1702         {
1703                 Con_Printf("VM_fopen: %s ran out of file handles (%i)\n", PRVM_NAME, MAX_VMFILES);
1704                 PRVM_G_FLOAT(OFS_RETURN) = -2;
1705                 return;
1706         }
1707         mode = PRVM_G_FLOAT(OFS_PARM1);
1708         switch(mode)
1709         {
1710         case 0: // FILE_READ
1711                 modestring = "rb";
1712                 break;
1713         case 1: // FILE_APPEND
1714                 modestring = "ab";
1715                 break;
1716         case 2: // FILE_WRITE
1717                 modestring = "wb";
1718                 break;
1719         default:
1720                 Con_Printf ("VM_fopen: %s no such mode %i (valid: 0 = read, 1 = append, 2 = write)\n", PRVM_NAME, mode);
1721                 PRVM_G_FLOAT(OFS_RETURN) = -3;
1722                 return;
1723         }
1724         filename = PRVM_G_STRING(OFS_PARM0);
1725         // .. is parent directory on many platforms
1726         // / is parent directory on Amiga
1727         // : is root of drive on Amiga (also used as a directory separator on Mac, but / works there too, so that's a bad idea)
1728         // \ is a windows-ism (so it's naughty to use it, / works on all platforms)
1729         if ((filename[0] == '.' && filename[1] == '.') || filename[0] == '/' || strrchr(filename, ':') || strrchr(filename, '\\'))
1730         {
1731                 Con_Printf("VM_fopen: %s dangerous or non-portable filename \"%s\" not allowed. (contains : or \\ or begins with .. or /)\n", PRVM_NAME, filename);
1732                 PRVM_G_FLOAT(OFS_RETURN) = -4;
1733                 return;
1734         }
1735         VM_FILES[filenum] = FS_Open(va("data/%s", filename), modestring, false);
1736         if (VM_FILES[filenum] == NULL)
1737                 PRVM_G_FLOAT(OFS_RETURN) = -1;
1738         else
1739                 PRVM_G_FLOAT(OFS_RETURN) = filenum;
1740 }
1741
1742 /*
1743 =========
1744 VM_fclose
1745
1746 fclose(float fhandle)
1747 =========
1748 */
1749 //void(float fhandle) fclose = #111; // closes a file
1750 void VM_fclose(void)
1751 {
1752         int filenum;
1753
1754         VM_SAFEPARMCOUNT(1,VM_fclose);
1755
1756         filenum = PRVM_G_FLOAT(OFS_PARM0);
1757         if (filenum < 0 || filenum >= MAX_VMFILES)
1758         {
1759                 Con_Printf("VM_fclose: invalid file handle %i used in %s\n", filenum, PRVM_NAME);
1760                 return;
1761         }
1762         if (VM_FILES[filenum] == NULL)
1763         {
1764                 Con_Printf("VM_fclose: no such file handle %i (or file has been closed) in %s\n", filenum, PRVM_NAME);
1765                 return;
1766         }
1767         FS_Close(VM_FILES[filenum]);
1768         VM_FILES[filenum] = NULL;
1769 }
1770
1771 /*
1772 =========
1773 VM_fgets
1774
1775 string  fgets(float fhandle)
1776 =========
1777 */
1778 //string(float fhandle) fgets = #112; // reads a line of text from the file and returns as a tempstring
1779 void VM_fgets(void)
1780 {
1781         int c, end;
1782         static char string[VM_STRINGTEMP_LENGTH];
1783         int filenum;
1784
1785         VM_SAFEPARMCOUNT(1,VM_fgets);
1786
1787         filenum = PRVM_G_FLOAT(OFS_PARM0);
1788         if (filenum < 0 || filenum >= MAX_VMFILES)
1789         {
1790                 Con_Printf("VM_fgets: invalid file handle %i used in %s\n", filenum, PRVM_NAME);
1791                 return;
1792         }
1793         if (VM_FILES[filenum] == NULL)
1794         {
1795                 Con_Printf("VM_fgets: no such file handle %i (or file has been closed) in %s\n", filenum, PRVM_NAME);
1796                 return;
1797         }
1798         end = 0;
1799         for (;;)
1800         {
1801                 c = FS_Getc(VM_FILES[filenum]);
1802                 if (c == '\r' || c == '\n' || c < 0)
1803                         break;
1804                 if (end < VM_STRINGTEMP_LENGTH - 1)
1805                         string[end++] = c;
1806         }
1807         string[end] = 0;
1808         // remove \n following \r
1809         if (c == '\r')
1810                 c = FS_Getc(VM_FILES[filenum]);
1811         if (developer.integer)
1812                 Con_Printf("fgets: %s: %s\n", PRVM_NAME, string);
1813         if (c >= 0)
1814                 PRVM_G_INT(OFS_RETURN) = PRVM_SetString(string);
1815         else
1816                 PRVM_G_INT(OFS_RETURN) = 0;
1817 }
1818
1819 /*
1820 =========
1821 VM_fputs
1822
1823 fputs(float fhandle, string s)
1824 =========
1825 */
1826 //void(float fhandle, string s) fputs = #113; // writes a line of text to the end of the file
1827 void VM_fputs(void)
1828 {
1829         int stringlength;
1830         char string[VM_STRINGTEMP_LENGTH];
1831         int filenum;
1832
1833         VM_SAFEPARMCOUNT(2,VM_fputs);
1834
1835         filenum = PRVM_G_FLOAT(OFS_PARM0);
1836         if (filenum < 0 || filenum >= MAX_VMFILES)
1837         {
1838                 Con_Printf("VM_fputs: invalid file handle %i used in %s\n", filenum, PRVM_NAME);
1839                 return;
1840         }
1841         if (VM_FILES[filenum] == NULL)
1842         {
1843                 Con_Printf("VM_fputs: no such file handle %i (or file has been closed) in %s\n", filenum, PRVM_NAME);
1844                 return;
1845         }
1846         VM_VarString(1, string, sizeof(string));
1847         if ((stringlength = strlen(string)))
1848                 FS_Write(VM_FILES[filenum], string, stringlength);
1849         if (developer.integer)
1850                 Con_Printf("fputs: %s: %s\n", PRVM_NAME, string);
1851 }
1852
1853 /*
1854 =========
1855 VM_strlen
1856
1857 float   strlen(string s)
1858 =========
1859 */
1860 //float(string s) strlen = #114; // returns how many characters are in a string
1861 void VM_strlen(void)
1862 {
1863         char *s;
1864
1865         VM_SAFEPARMCOUNT(1,VM_strlen);
1866
1867         s = PRVM_G_STRING(OFS_PARM0);
1868         if (s)
1869                 PRVM_G_FLOAT(OFS_RETURN) = strlen(s);
1870         else
1871                 PRVM_G_FLOAT(OFS_RETURN) = 0;
1872 }
1873
1874 /*
1875 =========
1876 VM_strcat
1877
1878 string strcat(string,string,...[string])
1879 =========
1880 */
1881 //string(string s1, string s2) strcat = #115;
1882 // concatenates two strings (for example "abc", "def" would return "abcdef")
1883 // and returns as a tempstring
1884 void VM_strcat(void)
1885 {
1886         char *s;
1887
1888         if(prog->argc < 2) 
1889                 PRVM_ERROR("VM_strcat wrong parameter count (min. 2 expected ) !\n");
1890         
1891         s = VM_GetTempString();
1892         VM_VarString(0, s, VM_STRINGTEMP_LENGTH);
1893         PRVM_G_INT(OFS_RETURN) = PRVM_SetString(s);
1894 }
1895
1896 /*
1897 =========
1898 VM_substring
1899
1900 string  substring(string s, float start, float length)
1901 =========
1902 */
1903 // string(string s, float start, float length) substring = #116;
1904 // returns a section of a string as a tempstring
1905 void VM_substring(void)
1906 {
1907         int i, start, length;
1908         char *s, *string;
1909
1910         VM_SAFEPARMCOUNT(3,VM_substring);
1911
1912         string = VM_GetTempString();
1913         s = PRVM_G_STRING(OFS_PARM0);
1914         start = PRVM_G_FLOAT(OFS_PARM1);
1915         length = PRVM_G_FLOAT(OFS_PARM2);
1916         if (!s)
1917                 s = "";
1918         for (i = 0;i < start && *s;i++, s++);
1919         for (i = 0;i < VM_STRINGTEMP_LENGTH - 1 && *s && i < length;i++, s++)
1920                 string[i] = *s;
1921         string[i] = 0;
1922         PRVM_G_INT(OFS_RETURN) = PRVM_SetString(string);
1923 }
1924
1925 /*
1926 =========
1927 VM_stov
1928
1929 vector  stov(string s)
1930 =========
1931 */
1932 //vector(string s) stov = #117; // returns vector value from a string
1933 void VM_stov(void)
1934 {
1935         char string[VM_STRINGTEMP_LENGTH];
1936
1937         VM_SAFEPARMCOUNT(1,VM_stov);
1938
1939         VM_VarString(0, string, sizeof(string));
1940         Math_atov(string, PRVM_G_VECTOR(OFS_RETURN));
1941 }
1942
1943 /*
1944 =========
1945 VM_strzone
1946
1947 string  strzone(string s)
1948 =========
1949 */
1950 //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)
1951 void VM_strzone(void)
1952 {
1953         char *in, *out;
1954
1955         VM_SAFEPARMCOUNT(1,VM_strzone);
1956
1957         in = PRVM_G_STRING(OFS_PARM0);
1958         out = Mem_Alloc(VM_STRINGS_MEMPOOL, strlen(in) + 1);
1959         strcpy(out, in);
1960         PRVM_G_INT(OFS_RETURN) = PRVM_SetString(out);
1961 }
1962
1963 /*
1964 =========
1965 VM_strunzone
1966
1967 strunzone(string s)
1968 =========
1969 */
1970 //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!!!)
1971 void VM_strunzone(void)
1972 {
1973         VM_SAFEPARMCOUNT(1,VM_strunzone);
1974
1975         Mem_Free(PRVM_G_STRING(OFS_PARM0));
1976 }
1977
1978 /*
1979 =========
1980 VM_command (used by client and menu)
1981
1982 clientcommand(float client, string s) (for client and menu)
1983 =========
1984 */
1985 //void(entity e, string s) clientcommand = #440; // executes a command string as if it came from the specified client
1986 //this function originally written by KrimZon, made shorter by LordHavoc
1987 void VM_clcommand (void)
1988 {
1989         client_t *temp_client;
1990         int i;
1991
1992         VM_SAFEPARMCOUNT(2,VM_clcommand);
1993
1994         i = PRVM_G_FLOAT(OFS_PARM0);
1995         if (!sv.active  || i < 0 || i >= svs.maxclients || !svs.clients[i].active)
1996         {
1997                 Con_Printf("VM_clientcommand: %s: invalid client/server is not active !", PRVM_NAME);
1998                 return;
1999         }
2000
2001         temp_client = host_client;
2002         host_client = svs.clients + i;
2003         Cmd_ExecuteString (PRVM_G_STRING(OFS_PARM1), src_client);
2004         host_client = temp_client;
2005 }
2006
2007
2008 /*
2009 =========
2010 VM_tokenize
2011
2012 float tokenize(string s)
2013 =========
2014 */
2015 //float(string s) tokenize = #441;
2016 // takes apart a string into individal words (access them with argv), returns how many
2017 // this function originally written by KrimZon, made shorter by LordHavoc
2018 static char **tokens = NULL;
2019 static int    max_tokens, num_tokens = 0;
2020 void VM_tokenize (void)
2021 {
2022         const char *p;
2023         char *str;
2024
2025         VM_SAFEPARMCOUNT(1,VM_tokenize);
2026
2027         str = PRVM_G_STRING(OFS_PARM0);
2028
2029         if (tokens != NULL)
2030         {
2031                 int i;
2032                 for (i=0;i<num_tokens;i++)
2033                         Z_Free(tokens[i]);
2034                 Z_Free(tokens);
2035                 num_tokens = 0;
2036         }
2037
2038         tokens = Z_Malloc(strlen(str) * sizeof(char *));
2039         max_tokens = strlen(str);
2040
2041         for (p = str;COM_ParseToken(&p, false) && num_tokens < max_tokens;num_tokens++)
2042         {
2043                 tokens[num_tokens] = Z_Malloc(strlen(com_token) + 1);
2044                 strcpy(tokens[num_tokens], com_token);
2045         }
2046
2047         PRVM_G_FLOAT(OFS_RETURN) = num_tokens;
2048 }
2049
2050 /*
2051 =========
2052 VM_argv
2053
2054 string argv(float n)
2055 =========
2056 */
2057 //string(float n) argv = #442;
2058 // returns a word from the tokenized string (returns nothing for an invalid index)
2059 // this function originally written by KrimZon, made shorter by LordHavoc
2060 void VM_argv (void)
2061 {
2062         int token_num;
2063
2064         VM_SAFEPARMCOUNT(1,VM_argv);
2065
2066         token_num = PRVM_G_FLOAT(OFS_PARM0);
2067         if (token_num >= 0 && token_num < num_tokens)
2068                 PRVM_G_INT(OFS_RETURN) = PRVM_SetString(tokens[token_num]);
2069         else
2070                 PRVM_G_INT(OFS_RETURN) = PRVM_SetString("");
2071 }
2072
2073 /*
2074 //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)
2075 void PF_setattachment (void)
2076 {
2077         edict_t *e = G_EDICT(OFS_PARM0);
2078         edict_t *tagentity = G_EDICT(OFS_PARM1);
2079         char *tagname = G_STRING(OFS_PARM2);
2080         eval_t *v;
2081         int i, modelindex;
2082         model_t *model;
2083
2084         if (tagentity == NULL)
2085                 tagentity = sv.edicts;
2086
2087         v = GETEDICTFIELDVALUE(e, eval_tag_entity);
2088         if (v)
2089                 v->edict = EDICT_TO_PROG(tagentity);
2090
2091         v = GETEDICTFIELDVALUE(e, eval_tag_index);
2092         if (v)
2093                 v->_float = 0;
2094         if (tagentity != NULL && tagentity != sv.edicts && tagname && tagname[0])
2095         {
2096                 modelindex = (int)tagentity->v->modelindex;
2097                 if (modelindex >= 0 && modelindex < MAX_MODELS)
2098                 {
2099                         model = sv.models[modelindex];
2100                         if (model->data_overridetagnamesforskin && (unsigned int)tagentity->v->skin < (unsigned int)model->numskins && model->data_overridetagnamesforskin[(unsigned int)tagentity->v->skin].num_overridetagnames)
2101                                 for (i = 0;i < model->data_overridetagnamesforskin[(unsigned int)tagentity->v->skin].num_overridetagnames;i++)
2102                                         if (!strcmp(tagname, model->data_overridetagnamesforskin[(unsigned int)tagentity->v->skin].data_overridetagnames[i].name))
2103                                                 v->_float = i + 1;
2104                         if (v->_float == 0 && model->alias.aliasnum_tags)
2105                                 for (i = 0;i < model->alias.aliasnum_tags;i++)
2106                                         if (!strcmp(tagname, model->alias.aliasdata_tags[i].name))
2107                                                 v->_float = i + 1;
2108                         if (v->_float == 0)
2109                                 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);
2110                 }
2111                 else
2112                         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));
2113         }
2114 }*/
2115
2116 /*
2117 =========
2118 VM_isserver
2119
2120 float   isserver()
2121 =========
2122 */
2123 void VM_isserver(void)
2124 {
2125         VM_SAFEPARMCOUNT(0,VM_serverstate);
2126
2127         PRVM_G_FLOAT(OFS_RETURN) = sv.active;
2128 }
2129
2130 /*
2131 =========
2132 VM_clientcount
2133
2134 float   clientcount()
2135 =========
2136 */
2137 void VM_clientcount(void)
2138 {
2139         VM_SAFEPARMCOUNT(0,VM_clientcount);
2140
2141         PRVM_G_FLOAT(OFS_RETURN) = svs.maxclients;
2142 }
2143
2144 /*
2145 =========
2146 VM_clientstate
2147
2148 float   clientstate()
2149 =========
2150 */
2151 void VM_clientstate(void)
2152 {
2153         VM_SAFEPARMCOUNT(0,VM_clientstate);
2154
2155         PRVM_G_FLOAT(OFS_RETURN) = cls.state;
2156 }
2157
2158 /*
2159 =========
2160 VM_getostype
2161
2162 float   getostype(void)
2163 =========
2164 */ // not used at the moment -> not included in the common list
2165 void VM_getostype(void)
2166 {
2167         VM_SAFEPARMCOUNT(0,VM_getostype);
2168
2169         /*
2170         OS_WINDOWS
2171         OS_LINUX
2172         OS_MAC - not supported
2173         */
2174
2175 #ifdef _WIN32
2176         PRVM_G_FLOAT(OFS_RETURN) = 0;
2177 #elif defined _MAC
2178         PRVM_G_FLOAT(OFS_RETURN) = 2;
2179 #else
2180         PRVM_G_FLOAT(OFS_RETURN) = 1;
2181 #endif
2182 }
2183
2184 /*
2185 =========
2186 VM_getmousepos
2187
2188 vector  getmousepos()
2189 =========
2190 */
2191 void VM_getmousepos(void)
2192 {
2193
2194         VM_SAFEPARMCOUNT(0,VM_getmousepos);
2195         
2196         PRVM_G_VECTOR(OFS_RETURN)[0] = in_mouse_x;
2197         PRVM_G_VECTOR(OFS_RETURN)[1] = in_mouse_y;
2198         PRVM_G_VECTOR(OFS_RETURN)[2] = 0;
2199 }
2200
2201 /*
2202 =========
2203 VM_gettime
2204
2205 float   gettime(void)
2206 =========
2207 */
2208 void VM_gettime(void)
2209 {
2210         VM_SAFEPARMCOUNT(0,VM_gettime);
2211
2212         PRVM_G_FLOAT(OFS_RETURN) = (float) *prog->time;
2213 }
2214
2215 /*
2216 =========
2217 VM_loadfromdata
2218
2219 loadfromdata(string data)
2220 =========
2221 */
2222 void VM_loadfromdata(void)
2223 {
2224         VM_SAFEPARMCOUNT(1,VM_loadentsfromfile);
2225
2226         PRVM_ED_LoadFromFile(PRVM_G_STRING(OFS_PARM0));
2227 }
2228
2229 /*
2230 =========
2231 VM_loadfromfile
2232
2233 loadfromfile(string file)
2234 =========
2235 */
2236 void VM_loadfromfile(void)
2237 {
2238         char *filename;
2239         qbyte *data;
2240         
2241         VM_SAFEPARMCOUNT(1,VM_loadfromfile);
2242         
2243         filename = PRVM_G_STRING(OFS_PARM0);
2244         // .. is parent directory on many platforms
2245         // / is parent directory on Amiga
2246         // : is root of drive on Amiga (also used as a directory separator on Mac, but / works there too, so that's a bad idea)
2247         // \ is a windows-ism (so it's naughty to use it, / works on all platforms)
2248         if ((filename[0] == '.' && filename[1] == '.') || filename[0] == '/' || strrchr(filename, ':') || strrchr(filename, '\\'))
2249         {
2250                 Con_Printf("VM_loadfromfile: %s dangerous or non-portable filename \"%s\" not allowed. (contains : or \\ or begins with .. or /)\n", PRVM_NAME, filename);
2251                 PRVM_G_FLOAT(OFS_RETURN) = -4;
2252                 return;
2253         }
2254
2255         // not conform with VM_fopen
2256         data = FS_LoadFile(filename, false);
2257         if (data == NULL)
2258                 PRVM_G_FLOAT(OFS_RETURN) = -1;
2259         
2260         PRVM_ED_LoadFromFile(data);
2261
2262         if(data)
2263                 Mem_Free(data);
2264 }
2265
2266
2267 /*
2268 =========
2269 VM_modulo
2270
2271 float   mod(float val, float m)
2272 =========
2273 */
2274 void VM_modulo(void)
2275 {
2276         int val, m;
2277         VM_SAFEPARMCOUNT(2,VM_module);
2278
2279         val = (int) PRVM_G_FLOAT(OFS_PARM0);
2280         m       = (int) PRVM_G_FLOAT(OFS_PARM1);
2281
2282         PRVM_G_FLOAT(OFS_RETURN) = (float) (val % m);
2283 }
2284
2285 void VM_Search_Init(void)
2286 {
2287         memset(VM_SEARCHLIST,0,sizeof(fssearch_t*[MAX_VMSEARCHES]));
2288 }
2289
2290 void VM_Search_Reset(void)
2291 {
2292         int i;
2293         // reset the fssearch list
2294         for(i = 0; i < MAX_VMSEARCHES; i++)
2295                 if(VM_SEARCHLIST[i])
2296                         FS_FreeSearch(VM_SEARCHLIST[i]);
2297         memset(VM_SEARCHLIST,0,sizeof(fssearch_t*[MAX_VMSEARCHES]));
2298 }
2299
2300 /*
2301 =========
2302 VM_search_begin
2303
2304 float search_begin(string pattern, float caseinsensitive, float quiet)
2305 =========
2306 */
2307 void VM_search_begin(void)
2308 {
2309         int handle;
2310         char *pattern;
2311         int caseinsens, quiet;
2312
2313         VM_SAFEPARMCOUNT(3, VM_search_begin);
2314
2315         pattern = PRVM_G_STRING(OFS_PARM0);
2316
2317         VM_CheckEmptyString(pattern);
2318
2319         caseinsens = PRVM_G_FLOAT(OFS_PARM1);
2320         quiet = PRVM_G_FLOAT(OFS_PARM2);
2321         
2322         for(handle = 0; handle < MAX_VMSEARCHES; handle++)
2323                 if(!VM_SEARCHLIST[handle])
2324                         break;
2325
2326         if(handle >= MAX_VMSEARCHES)
2327         {
2328                 Con_Printf("VM_search_begin: %s ran out of search handles (%i)\n", PRVM_NAME, MAX_VMSEARCHES);
2329                 PRVM_G_FLOAT(OFS_RETURN) = -2;
2330                 return;
2331         }
2332
2333         if(!(VM_SEARCHLIST[handle] = FS_Search(pattern,caseinsens, quiet)))
2334                 PRVM_G_FLOAT(OFS_RETURN) = -1;
2335         else
2336                 PRVM_G_FLOAT(OFS_RETURN) = handle;
2337 }
2338
2339 /*
2340 =========
2341 VM_search_end
2342
2343 void    search_end(float handle)
2344 =========
2345 */
2346 void VM_search_end(void)
2347 {
2348         int handle;
2349         VM_SAFEPARMCOUNT(1, VM_search_end);
2350
2351         handle = PRVM_G_FLOAT(OFS_PARM0);
2352         
2353         if(handle < 0 || handle >= MAX_VMSEARCHES)
2354         {
2355                 Con_Printf("VM_search_end: invalid handle %i used in %s\n", handle, PRVM_NAME);
2356                 return;
2357         }
2358         if(VM_SEARCHLIST[handle] == NULL)
2359         {
2360                 Con_Printf("VM_search_end: no such handle %i in %s\n", handle, PRVM_NAME);
2361                 return;
2362         }
2363
2364         FS_FreeSearch(VM_SEARCHLIST[handle]);
2365         VM_SEARCHLIST[handle] = NULL;
2366 }
2367
2368 /*
2369 =========
2370 VM_search_getsize
2371
2372 float   search_getsize(float handle)
2373 =========
2374 */
2375 void VM_search_getsize(void)
2376 {
2377         int handle;
2378         VM_SAFEPARMCOUNT(1, VM_M_search_getsize);
2379
2380         handle = PRVM_G_FLOAT(OFS_PARM0);
2381
2382         if(handle < 0 || handle >= MAX_VMSEARCHES)
2383         {
2384                 Con_Printf("VM_search_getsize: invalid handle %i used in %s\n", handle, PRVM_NAME);
2385                 return;
2386         }
2387         if(VM_SEARCHLIST[handle] == NULL)
2388         {
2389                 Con_Printf("VM_search_getsize: no such handle %i in %s\n", handle, PRVM_NAME);
2390                 return;
2391         }
2392         
2393         PRVM_G_FLOAT(OFS_RETURN) = VM_SEARCHLIST[handle]->numfilenames;
2394 }
2395
2396 /*
2397 =========
2398 VM_search_getfilename
2399
2400 string  search_getfilename(float handle, float num)
2401 =========
2402 */
2403 void VM_search_getfilename(void)
2404 {
2405         int handle, filenum;
2406         char *tmp;
2407         VM_SAFEPARMCOUNT(2, VM_search_getfilename);
2408
2409         handle = PRVM_G_FLOAT(OFS_PARM0);
2410         filenum = PRVM_G_FLOAT(OFS_PARM1);
2411
2412         if(handle < 0 || handle >= MAX_VMSEARCHES)
2413         {
2414                 Con_Printf("VM_search_getfilename: invalid handle %i used in %s\n", handle, PRVM_NAME);
2415                 return;
2416         }
2417         if(VM_SEARCHLIST[handle] == NULL)
2418         {
2419                 Con_Printf("VM_search_getfilename: no such handle %i in %s\n", handle, PRVM_NAME);
2420                 return;
2421         }
2422         if(filenum < 0 || filenum >= VM_SEARCHLIST[handle]->numfilenames)
2423         {
2424                 Con_Printf("VM_search_getfilename: invalid filenum %i in %s\n", filenum, PRVM_NAME);
2425                 return;
2426         }
2427         
2428         tmp = VM_GetTempString();
2429         strcpy(tmp, VM_SEARCHLIST[handle]->filenames[filenum]);
2430
2431         PRVM_G_INT(OFS_RETURN) = PRVM_SetString(tmp);
2432 }
2433
2434 //=============================================================================
2435 // Draw builtins (client & menu)
2436
2437 /*
2438 =========
2439 VM_iscachedpic
2440
2441 float   iscachedpic(string pic)
2442 =========
2443 */
2444 void VM_iscachedpic(void)
2445 {
2446         VM_SAFEPARMCOUNT(1,VM_iscachedpic);
2447
2448         // drawq hasnt such a function, thus always return true 
2449         PRVM_G_FLOAT(OFS_RETURN) = TRUE;
2450 }
2451
2452 /*
2453 =========
2454 VM_precache_pic
2455
2456 string  precache_pic(string pic) 
2457 =========
2458 */
2459 void VM_precache_pic(void)
2460 {
2461         char    *s;
2462         
2463         VM_SAFEPARMCOUNT(1, VM_precache_pic);
2464         
2465         s = PRVM_G_STRING(OFS_PARM0);
2466         PRVM_G_INT(OFS_RETURN) = PRVM_G_INT(OFS_PARM0);
2467         
2468         if(!s)
2469                 PRVM_ERROR ("VM_precache_pic: %s: NULL\n", PRVM_NAME);
2470
2471         VM_CheckEmptyString (s);
2472         
2473         if(!Draw_CachePic(s))
2474                 PRVM_G_INT(OFS_RETURN) = PRVM_SetString(""); 
2475 }
2476
2477 /*
2478 =========
2479 VM_freepic
2480
2481 freepic(string s)
2482 =========
2483 */
2484 void VM_freepic(void)
2485 {
2486         char *s;
2487
2488         VM_SAFEPARMCOUNT(1,VM_freepic);
2489
2490         s = PRVM_G_STRING(OFS_PARM0);
2491         
2492         if(!s)
2493                 PRVM_ERROR ("VM_freepic: %s: NULL\n");
2494         
2495         VM_CheckEmptyString (s);
2496         
2497         Draw_FreePic(s);
2498 }
2499
2500 /*
2501 =========
2502 VM_drawcharacter
2503
2504 float   drawcharacter(vector position, float character, vector scale, vector rgb, float alpha, float flag)
2505 =========
2506 */
2507 void VM_drawcharacter(void)
2508 {
2509         float *pos,*scale,*rgb;
2510         char   character;
2511         int flag;
2512         VM_SAFEPARMCOUNT(6,VM_drawcharacter);
2513
2514         character = (char) PRVM_G_FLOAT(OFS_PARM1);
2515         if(character == 0)
2516         {
2517                 Con_Printf("VM_drawcharacter: %s passed null character !\n",PRVM_NAME);
2518                 PRVM_G_FLOAT(OFS_RETURN) = -1;
2519                 return;
2520         }
2521         
2522         pos = PRVM_G_VECTOR(OFS_PARM0);
2523         scale = PRVM_G_VECTOR(OFS_PARM2);
2524         rgb = PRVM_G_VECTOR(OFS_PARM3);
2525         flag = (int)PRVM_G_FLOAT(OFS_PARM5);
2526         
2527         if(flag < DRAWFLAG_NORMAL || flag >=DRAWFLAG_NUMFLAGS)
2528         {
2529                 Con_Printf("VM_drawcharacter: %s: wrong DRAWFLAG %i !\n",PRVM_NAME,flag);
2530                 PRVM_G_FLOAT(OFS_RETURN) = -2;
2531                 return;
2532         }
2533         
2534         if(pos[2] || scale[2])
2535                 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"))); 
2536
2537         if(!scale[0] || !scale[1])
2538         {
2539                 Con_Printf("VM_drawcharacter: scale %s is null !\n", (scale[0] == 0) ? ((scale[1] == 0) ? "x and y" : "x") : "y");
2540                 PRVM_G_FLOAT(OFS_RETURN) = -3;
2541                 return;
2542         }
2543
2544         DrawQ_String (pos[0], pos[1], &character, 1, scale[0], scale[1], rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM4), flag);
2545         PRVM_G_FLOAT(OFS_RETURN) = 1;
2546 }       
2547
2548 /*
2549 =========
2550 VM_drawstring
2551
2552 float   drawstring(vector position, string text, vector scale, vector rgb, float alpha, float flag)
2553 =========
2554 */
2555 void VM_drawstring(void)
2556 {
2557         float *pos,*scale,*rgb;
2558         char  *string;
2559         int flag;
2560         VM_SAFEPARMCOUNT(6,VM_drawstring);
2561         
2562         string = PRVM_G_STRING(OFS_PARM1);
2563         if(!string)
2564         {
2565                 Con_Printf("VM_drawstring: %s passed null string !\n",PRVM_NAME);
2566                 PRVM_G_FLOAT(OFS_RETURN) = -1;
2567                 return;
2568         }
2569         
2570         VM_CheckEmptyString(string);
2571         
2572         pos = PRVM_G_VECTOR(OFS_PARM0);
2573         scale = PRVM_G_VECTOR(OFS_PARM2);
2574         rgb = PRVM_G_VECTOR(OFS_PARM3);
2575         flag = (int)PRVM_G_FLOAT(OFS_PARM5);
2576         
2577         if(flag < DRAWFLAG_NORMAL || flag >=DRAWFLAG_NUMFLAGS)
2578         {
2579                 Con_Printf("VM_drawstring: %s: wrong DRAWFLAG %i !\n",PRVM_NAME,flag);
2580                 PRVM_G_FLOAT(OFS_RETURN) = -2;
2581                 return;
2582         }
2583         
2584         if(!scale[0] || !scale[1])
2585         {
2586                 Con_Printf("VM_drawstring: scale %s is null !\n", (scale[0] == 0) ? ((scale[1] == 0) ? "x and y" : "x") : "y");
2587                 PRVM_G_FLOAT(OFS_RETURN) = -3;
2588                 return;
2589         }
2590
2591         if(pos[2] || scale[2])
2592                 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"))); 
2593         
2594         DrawQ_String (pos[0], pos[1], string, 0, scale[0], scale[1], rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM4), flag);
2595         PRVM_G_FLOAT(OFS_RETURN) = 1;
2596 }
2597 /*
2598 =========
2599 VM_drawpic
2600
2601 float   drawpic(vector position, string pic, vector size, vector rgb, float alpha, float flag)
2602 =========
2603 */
2604 void VM_drawpic(void)
2605 {
2606         char *pic;
2607         float *size, *pos, *rgb;
2608         int flag;
2609
2610         VM_SAFEPARMCOUNT(6,VM_drawpic);
2611
2612         pic = PRVM_G_STRING(OFS_PARM1);
2613
2614         if(!pic)
2615         {
2616                 Con_Printf("VM_drawpic: %s passed null picture name !\n", PRVM_NAME);
2617                 PRVM_G_FLOAT(OFS_RETURN) = -1;  
2618                 return;
2619         }
2620
2621         VM_CheckEmptyString (pic);
2622
2623         // is pic cached ? no function yet for that
2624         if(!1)
2625         {
2626                 Con_Printf("VM_drawpic: %s: %s not cached !\n", PRVM_NAME, pic);
2627                 PRVM_G_FLOAT(OFS_RETURN) = -4;
2628                 return;
2629         }
2630         
2631         pos = PRVM_G_VECTOR(OFS_PARM0);
2632         size = PRVM_G_VECTOR(OFS_PARM2);
2633         rgb = PRVM_G_VECTOR(OFS_PARM3);
2634         flag = (int) PRVM_G_FLOAT(OFS_PARM5);
2635
2636         if(flag < DRAWFLAG_NORMAL || flag >=DRAWFLAG_NUMFLAGS)
2637         {
2638                 Con_Printf("VM_drawstring: %s: wrong DRAWFLAG %i !\n",PRVM_NAME,flag);
2639                 PRVM_G_FLOAT(OFS_RETURN) = -2;
2640                 return;
2641         }
2642
2643         if(pos[2] || size[2])
2644                 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"))); 
2645         
2646         DrawQ_Pic(pos[0], pos[1], pic, size[0], size[1], rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM4), flag);
2647         PRVM_G_FLOAT(OFS_RETURN) = 1;
2648 }
2649
2650 /*
2651 =========
2652 VM_drawfill
2653
2654 float drawfill(vector position, vector size, vector rgb, float alpha, float flag)
2655 =========
2656 */
2657 void VM_drawfill(void)
2658 {
2659         float *size, *pos, *rgb;
2660         int flag;
2661         
2662         VM_SAFEPARMCOUNT(5,VM_drawfill);
2663         
2664         
2665         pos = PRVM_G_VECTOR(OFS_PARM0);
2666         size = PRVM_G_VECTOR(OFS_PARM1);
2667         rgb = PRVM_G_VECTOR(OFS_PARM2);
2668         flag = (int) PRVM_G_FLOAT(OFS_PARM4);
2669         
2670         if(flag < DRAWFLAG_NORMAL || flag >=DRAWFLAG_NUMFLAGS)
2671         {
2672                 Con_Printf("VM_drawstring: %s: wrong DRAWFLAG %i !\n",PRVM_NAME,flag);
2673                 PRVM_G_FLOAT(OFS_RETURN) = -2;
2674                 return;
2675         }
2676         
2677         if(pos[2] || size[2])
2678                 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"))); 
2679         
2680         DrawQ_Pic(pos[0], pos[1], 0, size[0], size[1], rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM3), flag);
2681         PRVM_G_FLOAT(OFS_RETURN) = 1;
2682 }
2683
2684 /*
2685 =========
2686 VM_drawsetcliparea
2687
2688 drawsetcliparea(float x, float y, float width, float height)
2689 =========
2690 */
2691 void VM_drawsetcliparea(void)
2692 {
2693         float x,y,w,h;
2694         VM_SAFEPARMCOUNT(4,VM_drawsetcliparea);
2695
2696         x = bound(0,PRVM_G_FLOAT(OFS_PARM0),vid.conwidth);
2697         y = bound(0,PRVM_G_FLOAT(OFS_PARM1),vid.conheight);
2698         w = bound(0,PRVM_G_FLOAT(OFS_PARM2),(vid.conwidth  - x));
2699         h = bound(0,PRVM_G_FLOAT(OFS_PARM3),(vid.conheight - y)); 
2700
2701         DrawQ_SetClipArea(x,y,w,h);
2702 }
2703
2704 /*
2705 =========
2706 VM_drawresetcliparea
2707
2708 drawresetcliparea()
2709 =========
2710 */
2711 void VM_drawresetcliparea(void)
2712 {
2713         VM_SAFEPARMCOUNT(0,VM_drawresetcliparea);
2714
2715         DrawQ_ResetClipArea();
2716 }
2717
2718 /*
2719 =========
2720 VM_getimagesize
2721
2722 vector  getimagesize(string pic)
2723 =========
2724 */
2725 void VM_getimagesize(void)
2726 {
2727         char *p;
2728         cachepic_t *pic;
2729
2730         VM_SAFEPARMCOUNT(1,VM_getimagesize);
2731         
2732         p = PRVM_G_STRING(OFS_PARM0);
2733
2734         if(!p)
2735                 PRVM_ERROR("VM_getimagepos: %s passed null picture name !\n", PRVM_NAME);
2736         
2737         VM_CheckEmptyString (p);
2738
2739         pic = Draw_CachePic (p);
2740
2741         PRVM_G_VECTOR(OFS_RETURN)[0] = pic->width;
2742         PRVM_G_VECTOR(OFS_RETURN)[1] = pic->height;
2743         PRVM_G_VECTOR(OFS_RETURN)[2] = 0;
2744 }
2745
2746 void VM_Cmd_Init(void)
2747 {
2748         // only init the stuff for the current prog
2749         VM_STRINGS_MEMPOOL = Mem_AllocPool(va("vm_stringsmempool[%s]",PRVM_NAME));
2750         VM_Files_Init();
2751         VM_Search_Init();
2752 }
2753
2754 void VM_Cmd_Reset(void)
2755 {
2756         //Mem_EmptyPool(VM_STRINGS_MEMPOOL);
2757         Mem_FreePool(&VM_STRINGS_MEMPOOL);
2758         VM_Search_Reset();
2759         VM_Files_CloseAll();
2760 }
2761
2762 //============================================================================
2763 // Server
2764
2765 char *vm_sv_extensions =
2766 "";
2767
2768 prvm_builtin_t vm_sv_builtins[] = {
2769 0  // to be consistent with the old vm
2770 };
2771
2772 const int vm_sv_numbuiltins = sizeof(vm_sv_builtins) / sizeof(prvm_builtin_t);
2773
2774 void VM_SV_Cmd_Init(void)
2775 {
2776 }
2777
2778 void VM_SV_Cmd_Reset(void)
2779 {
2780 }
2781
2782 //============================================================================
2783 // Client
2784
2785 char *vm_cl_extensions =
2786 "";
2787
2788 prvm_builtin_t vm_cl_builtins[] = {
2789 0  // to be consistent with the old vm
2790 };
2791
2792 const int vm_cl_numbuiltins = sizeof(vm_cl_builtins) / sizeof(prvm_builtin_t);
2793
2794 void VM_CL_Cmd_Init(void)
2795 {
2796 }
2797
2798 void VM_CL_Cmd_Reset(void)
2799 {
2800 }
2801
2802 //============================================================================
2803 // Menu
2804
2805 char *vm_m_extensions =
2806 "";
2807
2808 /*
2809 =========
2810 VM_M_setmousetarget
2811
2812 setmousetarget(float target)
2813 =========
2814 */
2815 void VM_M_setmousetarget(void)
2816 {
2817         VM_SAFEPARMCOUNT(1, VM_M_setmousetarget);
2818
2819         switch((int)PRVM_G_FLOAT(OFS_PARM0))
2820         {
2821         case 1:
2822                 in_client_mouse = false;
2823                 break;
2824         case 2:
2825                 in_client_mouse = true;
2826                 break;
2827         default:
2828                 PRVM_ERROR("VM_M_setmousetarget: wrong destination %i !\n",PRVM_G_FLOAT(OFS_PARM0));
2829         }
2830 }
2831
2832 /*
2833 =========
2834 VM_M_getmousetarget
2835
2836 float   getmousetarget
2837 =========
2838 */
2839 void VM_M_getmousetarget(void)
2840 {
2841         VM_SAFEPARMCOUNT(0,VM_M_getmousetarget);
2842
2843         if(in_client_mouse)
2844                 PRVM_G_FLOAT(OFS_RETURN) = 2;
2845         else
2846                 PRVM_G_FLOAT(OFS_RETURN) = 1;
2847 }
2848         
2849
2850
2851 /*
2852 =========
2853 VM_M_setkeydest
2854
2855 setkeydest(float dest)
2856 =========
2857 */
2858 void VM_M_setkeydest(void)
2859 {
2860         VM_SAFEPARMCOUNT(1,VM_M_setkeydest);
2861
2862         switch((int)PRVM_G_FLOAT(OFS_PARM0))
2863         {
2864         case 0:
2865                 // key_game
2866                 key_dest = key_game;
2867                 break;
2868         case 2:
2869                 // key_menu
2870                 key_dest = key_menu;
2871                 break;
2872         case 1:
2873                 // key_message
2874                 // key_dest = key_message
2875                 // break;
2876         default:
2877                 PRVM_ERROR("VM_M_setkeydest: wrong destination %i !\n",prog->globals[OFS_PARM0]);
2878         }
2879 }
2880
2881 /*
2882 =========
2883 VM_M_getkeydest
2884
2885 float   getkeydest
2886 =========
2887 */
2888 void VM_M_getkeydest(void)
2889 {
2890         VM_SAFEPARMCOUNT(0,VM_M_getkeydest);
2891
2892         // key_game = 0, key_message = 1, key_menu = 2, unknown = 3
2893         switch(key_dest)
2894         {
2895         case key_game:
2896                 PRVM_G_FLOAT(OFS_RETURN) = 0;
2897                 break;
2898         case key_menu:
2899                 PRVM_G_FLOAT(OFS_RETURN) = 2;
2900                 break;
2901         case key_message:
2902                 // not supported
2903                 // PRVM_G_FLOAT(OFS_RETURN) = 1;
2904                 // break;
2905         default:
2906                 PRVM_G_FLOAT(OFS_RETURN) = 3;
2907         }
2908 }
2909
2910 /*
2911 =========
2912 VM_M_callfunction
2913
2914         callfunction(...,string function_name)
2915 =========
2916 */
2917 mfunction_t *PRVM_ED_FindFunction (const char *name);
2918 void VM_M_callfunction(void)
2919 {
2920         mfunction_t *func;
2921         char *s;
2922
2923         if(prog->argc == 0)
2924                 PRVM_ERROR("VM_M_callfunction: 1 parameter is required !\n");
2925
2926         s = PRVM_G_STRING(OFS_PARM0 + (prog->argc - 1));
2927
2928         if(!s)
2929                 PRVM_ERROR("VM_M_callfunction: null string !\n");
2930
2931         VM_CheckEmptyString(s); 
2932
2933         func = PRVM_ED_FindFunction(s);
2934
2935         if(!func)
2936                 PRVM_ERROR("VM_M_callfunciton: function %s not found !\n", s);
2937         else if (func->first_statement < 0)
2938         {
2939                 // negative statements are built in functions
2940                 int builtinnumber = -func->first_statement;
2941                 prog->xfunction->builtinsprofile++;
2942                 if (builtinnumber < prog->numbuiltins && prog->builtins[builtinnumber])
2943                         prog->builtins[builtinnumber]();
2944                 else
2945                         PRVM_ERROR("No such builtin #%i in %s", builtinnumber, PRVM_NAME);
2946         }
2947         else if(func > 0)
2948         {
2949                 prog->argc--;
2950                 PRVM_ExecuteProgram(func - prog->functions,"");
2951                 prog->argc++;
2952         }
2953 }       
2954
2955 /*
2956 =========
2957 VM_M_isfunction
2958
2959 float   isfunction(string function_name)
2960 =========
2961 */
2962 mfunction_t *PRVM_ED_FindFunction (const char *name);
2963 void VM_M_isfunction(void)
2964 {
2965         mfunction_t *func;
2966         char *s;
2967         
2968         VM_SAFEPARMCOUNT(1, VM_M_isfunction);
2969         
2970         s = PRVM_G_STRING(OFS_PARM0);
2971         
2972         if(!s)
2973                 PRVM_ERROR("VM_M_isfunction: null string !\n");
2974         
2975         VM_CheckEmptyString(s); 
2976         
2977         func = PRVM_ED_FindFunction(s);
2978
2979         if(!func)
2980                 PRVM_G_FLOAT(OFS_RETURN) = false;
2981         else
2982                 PRVM_G_FLOAT(OFS_RETURN) = true;
2983 }
2984
2985 /*
2986 =========
2987 VM_M_writetofile
2988
2989         writetofile(float fhandle, entity ent)
2990 =========
2991 */
2992 void VM_M_writetofile(void)
2993 {
2994         prvm_edict_t * ent;
2995         int filenum;
2996
2997         VM_SAFEPARMCOUNT(2, VM_M_writetofile);
2998
2999         filenum = PRVM_G_FLOAT(OFS_PARM0);
3000         if (filenum < 0 || filenum >= MAX_VMFILES)
3001         {
3002                 Con_Printf("VM_fputs: invalid file handle %i used in %s\n", filenum, PRVM_NAME);
3003                 return;
3004         }
3005         if (VM_FILES[filenum] == NULL)
3006         {
3007                 Con_Printf("VM_fputs: no such file handle %i (or file has been closed) in %s\n", filenum, PRVM_NAME);
3008                 return;
3009         }
3010
3011         ent = PRVM_G_EDICT(OFS_PARM1);  
3012         if(ent->e->free)
3013         {
3014                 Con_Printf("VM_M_writetofile: %s: entity %i is free !\n", PRVM_NAME, PRVM_EDICT_NUM(OFS_PARM1));
3015                 return;
3016         }
3017
3018         PRVM_ED_Write (VM_FILES[filenum], ent);
3019 }
3020
3021 /*
3022 =========
3023 VM_M_getresolution
3024
3025 vector  getresolution(float number)
3026 =========
3027 */
3028 extern unsigned short video_resolutions[][2];
3029 void VM_M_getresolution(void)
3030 {
3031         int nr;
3032         VM_SAFEPARMCOUNT(1, VM_getresolution);
3033
3034         nr = PRVM_G_FLOAT(OFS_PARM0);
3035
3036
3037         PRVM_G_VECTOR(OFS_RETURN)[0] = video_resolutions[nr][0];
3038         PRVM_G_VECTOR(OFS_RETURN)[1] = video_resolutions[nr][1];
3039         PRVM_G_VECTOR(OFS_RETURN)[2] = 0;       
3040 }
3041
3042 /*
3043 =========
3044 VM_M_keynumtostring
3045
3046 string keynumtostring(float keynum)
3047 =========
3048 */
3049 void VM_M_keynumtostring(void)
3050 {
3051         int keynum;
3052         char *tmp;
3053         VM_SAFEPARMCOUNT(1, VM_M_keynumtostring);
3054
3055         keynum = PRVM_G_FLOAT(OFS_PARM0);
3056
3057         tmp = VM_GetTempString();
3058         
3059         strcpy(tmp, Key_KeynumToString(keynum));
3060
3061         PRVM_G_INT(OFS_RETURN) = PRVM_SetString(tmp);
3062 }
3063
3064 prvm_builtin_t vm_m_builtins[] = {
3065         0, // to be consistent with the old vm
3066         // common builtings (mostly)
3067         VM_checkextension,
3068         VM_error,
3069         VM_objerror,
3070         VM_print,
3071         VM_bprint,
3072         VM_sprint,
3073         VM_centerprint,
3074         VM_normalize,
3075         VM_vlen,
3076         VM_vectoyaw,    // #10
3077         VM_vectoangles,
3078         VM_random,
3079         VM_localcmd,
3080         VM_cvar,
3081         VM_cvar_set,
3082         VM_dprint,
3083         VM_ftos,
3084         VM_fabs,
3085         VM_vtos,
3086         VM_etos,                // 20
3087         VM_stof,
3088         VM_spawn,
3089         VM_remove,
3090         VM_find,
3091         VM_findfloat,
3092         VM_findchain,
3093         VM_findchainfloat,
3094         VM_precache_file,
3095         VM_precache_sound,
3096         VM_coredump,    // 30
3097         VM_traceon,
3098         VM_traceoff,
3099         VM_eprint,
3100         VM_rint,
3101         VM_floor,
3102         VM_ceil,
3103         VM_nextent,
3104         VM_sin,
3105         VM_cos,
3106         VM_sqrt,                // 40
3107         VM_randomvec,
3108         VM_registercvar,
3109         VM_min,
3110         VM_max,
3111         VM_bound,
3112         VM_pow,
3113         VM_copyentity,
3114         VM_fopen,
3115         VM_fclose,
3116         VM_fgets,               // 50
3117         VM_fputs,
3118         VM_strlen,
3119         VM_strcat,
3120         VM_substring,
3121         VM_stov,
3122         VM_strzone,
3123         VM_strunzone,
3124         VM_tokenize,
3125         VM_argv,
3126         VM_isserver,    // 60
3127         VM_clientcount, 
3128         VM_clientstate, 
3129         VM_clcommand,
3130         VM_changelevel,
3131         VM_localsound,  
3132         VM_getmousepos,
3133         VM_gettime,
3134         VM_loadfromdata,
3135         VM_loadfromfile,
3136         VM_modulo,              // 70
3137         VM_str_cvar,    
3138         VM_crash,
3139         VM_stackdump,   // 73
3140         VM_search_begin,
3141         VM_search_end,
3142         VM_search_getsize,
3143         VM_search_getfilename, // 77
3144         0,0,0,// 80
3145         e10,                    // 90
3146         e10,                    // 100
3147         e100,                   // 200
3148         e100,                   // 300
3149         e100,                   // 400
3150         // msg functions
3151         VM_WriteByte,
3152         VM_WriteChar,
3153         VM_WriteShort,
3154         VM_WriteLong,
3155         VM_WriteAngle,
3156         VM_WriteCoord,
3157         VM_WriteString,
3158         VM_WriteEntity, // 408
3159         0,
3160         0,                              // 410
3161         e10,                    // 420
3162         e10,                    // 430
3163         e10,                    // 440
3164         e10,                    // 450
3165         // draw functions
3166         VM_iscachedpic,
3167         VM_precache_pic,
3168         VM_freepic,
3169         VM_drawcharacter,
3170         VM_drawstring,
3171         VM_drawpic,
3172         VM_drawfill,    
3173         VM_drawsetcliparea,
3174         VM_drawresetcliparea,
3175         VM_getimagesize,// 460
3176         e10,                    // 470
3177         e10,                    // 480
3178         e10,                    // 490
3179         e10,                    // 500
3180         e100,                   // 600
3181         // menu functions
3182         VM_M_setkeydest,
3183         VM_M_getkeydest,
3184         VM_M_setmousetarget,
3185         VM_M_getmousetarget,
3186         VM_M_callfunction,
3187         VM_M_writetofile,
3188         VM_M_isfunction,
3189         VM_M_getresolution,
3190         VM_M_keynumtostring // 609
3191 };
3192
3193 const int vm_m_numbuiltins = sizeof(vm_m_builtins) / sizeof(prvm_builtin_t);
3194
3195 void VM_M_Cmd_Init(void)
3196 {
3197         VM_Cmd_Init();
3198 }
3199
3200 void VM_M_Cmd_Reset(void)
3201 {
3202         //VM_Cmd_Init();
3203         VM_Cmd_Reset();
3204 }