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