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