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