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