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