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