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