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