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