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