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