]> icculus.org git repositories - divverent/darkplaces.git/blob - prvm_cmds.c
added DP_QC_TRACE_MOVETYPE_HITMODEL extension (and added DP_QC_TRACE_MOVETYPE_WORLDON...
[divverent/darkplaces.git] / prvm_cmds.c
1 // AK
2 // Basically every vm builtin cmd should be in here.
3 // All 3 builtin list and extension lists can be found here
4 // cause large (I think they will) are from pr_cmds the same copyright like in pr_cms
5 // also applies here
6
7
8 /*============================================================================
9 common cmd list:
10 =================
11
12                 checkextension(string)
13                 error(...[string])
14                 objerror(...[string)
15                 print(...[strings])
16                 centerprint(...[string])
17 vector  normalize(vector)
18 float   vlen(vector)
19 float   vectoyaw(vector)
20 vector  vectoangles(vector)
21 float   random()
22                 cmd(string)
23                 float cvar (string)
24                 cvar_set (string,string)
25                 dprint(...[string])
26 string  ftos(float)
27 float   fabs(float)
28 string  vtos(vector)
29 string  etos(entity)
30 float   stof(...[string])
31 entity  spawn()
32 entity  remove()
33 entity  find(entity start, .string field, string match)
34
35 entity  findfloat(entity start, .float field, string match)
36 entity  findentity(entity start, .entity field, string match)
37
38 entity  findchain(.string field, string match)
39
40 entity  findchainfloat(.string field, float match)
41 entity  findchainentity(.string field, entity match)
42
43                 coredump()
44                 traceon()
45                 traceoff()
46                 eprint()
47 float   rint(float)
48 float   floor(float)
49 float   ceil(float)
50 entity  nextent(entity)
51 float   sin(float)
52 float   cos(float)
53 float   sqrt(float)
54                 randomvec()
55 float   registercvar (string name, string value)
56 float   min(float a, float b, ...[float])
57 float   max(float a, float b, ...[float])
58 float   bound(float min, float value, float max)
59 float   pow(float a, float b)
60                 copyentity(entity src, entity dst)
61 float   fopen(string filename, float mode)
62                 fclose(float fhandle)
63 string  fgets(float fhandle)
64                 fputs(float fhandle, string s)
65 float   strlen(string s)
66 string  strcat(string s1, string s2)
67 string  substring(string s, float start, float length)
68 vector  stov(string s)
69 string  strzone(string s)
70                 strzone(string s)
71 float   isserver()
72 float   clientcount()
73 float   clientstate()
74                 clientcommand(float client, string s) (for client and menu)
75 float   tokenize(string s)
76
77 */
78
79 #include "quakedef.h"
80 #include "progdefs.h"
81 #include "clprogdefs.h"
82 #include "mprogdefs.h"
83
84 //============================================================================
85 // nice helper macros
86
87 #ifndef VM_NOPARMCHECK
88 #define VM_SAFEPARMCOUNT(p,f)   if(prog->argc != p) PRVM_ERROR(#f "wrong parameter count (" #p "expected ) !\n")
89 #else
90 #define VM_SAFEPARMCOUNT(p,f)
91 #endif
92
93 #define VM_RETURN_EDICT(e)              (((int *)prog->globals)[OFS_RETURN] = PRVM_EDICT_TO_PROG(e))
94
95 #define VM_STRINGS_MEMPOOL              vm_strings_mempool[PRVM_GetProgNr()]
96
97 #define e10 0,0,0,0,0,0,0,0,0,0
98 #define e100 e10,e10,e10,e10,e10,e10,e10,e10,e10,e10
99 #define e1000 e100,e100,e100,e100,e100,e100,e100,e100,e100,e100
100
101 //============================================================================
102 // Common
103 cvar_t vm_zone_min_strings = {0, "prvm_zone_min_strings", "64"};
104
105 mempool_t *vm_strings_mempool[PRVM_MAXPROGS];
106
107 /*
108 typedef struct vm_string_s
109 {
110         int prog_id;
111         // here follows everything else
112         char string[0];
113 } vm_string_t;
114 */
115
116 // LordHavoc: added this to semi-fix the problem of using many ftos calls in a print
117 #define STRINGTEMP_BUFFERS 16
118 #define STRINGTEMP_LENGTH 4096
119 static char vm_string_temp[STRINGTEMP_BUFFERS][STRINGTEMP_LENGTH];
120 static int vm_string_tempindex = 0;
121
122 static char *VM_GetTempString(void)
123 {
124         char *s;
125         s = vm_string_temp[vm_string_tempindex];
126         vm_string_tempindex = (vm_string_tempindex + 1) % STRINGTEMP_BUFFERS;
127         return s;
128 }
129
130
131 void VM_CheckEmptyString (char *s)
132 {
133         if (s[0] <= ' ')
134                 PRVM_ERROR ("%s: Bad string", PRVM_NAME);
135 }
136
137 //============================================================================
138 //BUILT-IN FUNCTIONS
139
140 void VM_VarString(int first, char *out, int outlength)
141 {
142         int i;
143         const char *s;
144         char *outend;
145
146         outend = out + outlength - 1;
147         for (i = first;i < pr_argc && out < outend;i++)
148         {
149                 s = PRVM_G_STRING((OFS_PARM0+i*3));
150                 while (out < outend && *s)
151                         *out++ = *s++;
152         }
153         *out++ = 0;
154 }
155
156 /*
157 =================
158 VM_checkextension
159
160 returns true if the extension is supported by the server
161
162 checkextension(extensionname)
163 =================
164 */
165
166 // kind of helper function
167 static qboolean checkextension(char *name)
168 {
169         int len;
170         char *e, *start;
171         len = strlen(name);
172
173         for (e = prog->extensionstring;*e;e++)
174         {
175                 while (*e == ' ')
176                         e++;
177                 if (!*e)
178                         break;
179                 start = e;
180                 while (*e && *e != ' ')
181                         e++;
182                 if (e - start == len)
183                         if (!strncasecmp(start, name, len))
184                         {
185                                 return true;
186                         }
187         }
188         return false;
189 }
190
191 void VM_checkextension (void)
192 {
193         VM_SAFEPARMCOUNT(1,VM_checkextension);
194
195         PRVM_G_FLOAT(OFS_RETURN) = checkextension(PRVM_G_STRING(OFS_PARM0));
196 }
197
198 /*
199 =================
200 VM_error
201
202 This is a TERMINAL error, which will kill off the entire prog.
203 Dumps self.
204
205 error(value)
206 =================
207 */
208 void VM_Error (void)
209 {
210         prvm_edict_t    *ed;
211         char string[STRINGTEMP_LENGTH];
212
213         VM_VarString(0, string, sizeof(string));
214         Con_Printf ("======%S ERROR in %s:\n%s\n", PRVM_NAME, PRVM_GetString(prog->xfunction->s_name), string);
215         if(prog->self)
216         {
217                 ed = PRVM_G_EDICT(prog->self->ofs);
218                 PRVM_ED_Print (ed);
219         }
220
221         PRVM_ERROR ("%s: Program error", PRVM_NAME);
222 }
223
224 /*
225 =================
226 VM_objerror
227
228 Dumps out self, then an error message.  The program is aborted and self is
229 removed, but the level can continue.
230
231 objerror(value)
232 =================
233 */
234 void VM_objerror (void)
235 {
236         prvm_edict_t    *ed;
237         char string[STRINGTEMP_LENGTH];
238
239         VM_VarString(0, string, sizeof(string));
240         Con_Printf ("======%s OBJECT ERROR in %s:\n%s\n", PRVM_NAME, PRVM_GetString(prog->xfunction->s_name), string);
241         if(prog->self)
242         {
243                 ed = PRVM_G_EDICT (prog->self->ofs);
244                 PRVM_ED_Print (ed);
245                 PRVM_ED_Free (ed);
246         }
247         else
248                 // objerror has to display the object fields -> else call
249                 PRVM_ERROR ("VM_objecterror: self not defined !\n");
250 }
251
252 /*
253 =================
254 VM_print (actually used only by client and menu)
255
256 print to console
257
258 print(string)
259 =================
260 */
261 void VM_print (void)
262 {
263         char string[STRINGTEMP_LENGTH];
264
265         VM_VarString(0, string, sizeof(string));
266         Con_Print(string);
267 }
268
269 /*
270 =================
271 VM_centerprint
272
273 single print to the screen
274
275 centerprint(clientent, value)
276 =================
277 */
278 void VM_centerprint (void)
279 {
280         char string[STRINGTEMP_LENGTH];
281
282         VM_VarString(0, string, sizeof(string));
283         SCR_CenterPrint(string);
284 }
285
286 /*
287 =================
288 VM_normalize
289
290 vector normalize(vector)
291 =================
292 */
293 void VM_normalize (void)
294 {
295         float   *value1;
296         vec3_t  newvalue;
297         float   new;
298
299         VM_SAFEPARMCOUNT(1,VM_normalize);
300
301         value1 = PRVM_G_VECTOR(OFS_PARM0);
302
303         new = value1[0] * value1[0] + value1[1] * value1[1] + value1[2]*value1[2];
304         new = sqrt(new);
305
306         if (new == 0)
307                 newvalue[0] = newvalue[1] = newvalue[2] = 0;
308         else
309         {
310                 new = 1/new;
311                 newvalue[0] = value1[0] * new;
312                 newvalue[1] = value1[1] * new;
313                 newvalue[2] = value1[2] * new;
314         }
315
316         VectorCopy (newvalue, PRVM_G_VECTOR(OFS_RETURN));
317 }
318
319 /*
320 =================
321 VM_vlen
322
323 scalar vlen(vector)
324 =================
325 */
326 void VM_vlen (void)
327 {
328         float   *value1;
329         float   new;
330
331         VM_SAFEPARMCOUNT(1,VM_vlen);
332
333         value1 = PRVM_G_VECTOR(OFS_PARM0);
334
335         new = value1[0] * value1[0] + value1[1] * value1[1] + value1[2]*value1[2];
336         new = sqrt(new);
337
338         PRVM_G_FLOAT(OFS_RETURN) = new;
339 }
340
341 /*
342 =================
343 VM_vectoyaw
344
345 float vectoyaw(vector)
346 =================
347 */
348 void VM_vectoyaw (void)
349 {
350         float   *value1;
351         float   yaw;
352
353         VM_SAFEPARMCOUNT(1,VM_vectoyaw);
354
355         value1 = PRVM_G_VECTOR(OFS_PARM0);
356
357         if (value1[1] == 0 && value1[0] == 0)
358                 yaw = 0;
359         else
360         {
361                 yaw = (int) (atan2(value1[1], value1[0]) * 180 / M_PI);
362                 if (yaw < 0)
363                         yaw += 360;
364         }
365
366         PRVM_G_FLOAT(OFS_RETURN) = yaw;
367 }
368
369
370 /*
371 =================
372 VM_vectoangles
373
374 vector vectoangles(vector)
375 =================
376 */
377 void VM_vectoangles (void)
378 {
379         float   *value1;
380         float   forward;
381         float   yaw, pitch;
382
383         VM_SAFEPARMCOUNT(1,VM_vectoangles);
384
385         value1 = PRVM_G_VECTOR(OFS_PARM0);
386
387         if (value1[1] == 0 && value1[0] == 0)
388         {
389                 yaw = 0;
390                 if (value1[2] > 0)
391                         pitch = 90;
392                 else
393                         pitch = 270;
394         }
395         else
396         {
397                 // LordHavoc: optimized a bit
398                 if (value1[0])
399                 {
400                         yaw = (atan2(value1[1], value1[0]) * 180 / M_PI);
401                         if (yaw < 0)
402                                 yaw += 360;
403                 }
404                 else if (value1[1] > 0)
405                         yaw = 90;
406                 else
407                         yaw = 270;
408
409                 forward = sqrt(value1[0]*value1[0] + value1[1]*value1[1]);
410                 pitch = (int) (atan2(value1[2], forward) * 180 / M_PI);
411                 if (pitch < 0)
412                         pitch += 360;
413         }
414
415         PRVM_G_FLOAT(OFS_RETURN+0) = pitch;
416         PRVM_G_FLOAT(OFS_RETURN+1) = yaw;
417         PRVM_G_FLOAT(OFS_RETURN+2) = 0;
418 }
419
420 /*
421 =================
422 VM_random
423
424 Returns a number from 0<= num < 1
425
426 float random()
427 =================
428 */
429 void VM_random (void)
430 {
431         float           num;
432
433         VM_SAFEPARMCOUNT(0,VM_random);
434
435         num = (rand ()&0x7fff) / ((float)0x7fff);
436
437         PRVM_G_FLOAT(OFS_RETURN) = num;
438 }
439
440 /*
441 =================
442 PF_sound
443
444 Each entity can have eight independant sound sources, like voice,
445 weapon, feet, etc.
446
447 Channel 0 is an auto-allocate channel, the others override anything
448 already running on that entity/channel pair.
449
450 An attenuation of 0 will play full volume everywhere in the level.
451 Larger attenuations will drop off.
452
453 =================
454 */
455 /*
456 void PF_sound (void)
457 {
458         char            *sample;
459         int                     channel;
460         edict_t         *entity;
461         int             volume;
462         float attenuation;
463
464         entity = G_EDICT(OFS_PARM0);
465         channel = G_FLOAT(OFS_PARM1);
466         sample = G_STRING(OFS_PARM2);
467         volume = G_FLOAT(OFS_PARM3) * 255;
468         attenuation = G_FLOAT(OFS_PARM4);
469
470         if (volume < 0 || volume > 255)
471                 Host_Error ("SV_StartSound: volume = %i", volume);
472
473         if (attenuation < 0 || attenuation > 4)
474                 Host_Error ("SV_StartSound: attenuation = %f", attenuation);
475
476         if (channel < 0 || channel > 7)
477                 Host_Error ("SV_StartSound: channel = %i", channel);
478
479         SV_StartSound (entity, channel, sample, volume, attenuation);
480 }
481 */
482
483 /*
484 =================
485 VM_break
486
487 break()
488 =================
489 */
490 void VM_break (void)
491 {
492         PRVM_ERROR ("%s: break statement", PRVM_NAME);
493 }
494
495 //============================================================================
496
497 int checkpvsbytes;
498 qbyte checkpvs[MAX_MAP_LEAFS/8];
499
500 //============================================================================
501
502 /*
503 =================
504 VM_localcmd
505
506 Sends text over to the client's execution buffer
507
508 [localcmd (string) or]
509 cmd (string)
510 =================
511 */
512 void VM_localcmd (void)
513 {
514         VM_SAFEPARMCOUNT(1,VM_localcmd);
515
516         Cbuf_AddText(PRVM_G_STRING(OFS_PARM0));
517 }
518
519 /*
520 =================
521 VM_cvar
522
523 float cvar (string)
524 =================
525 */
526 void VM_cvar (void)
527 {
528         VM_SAFEPARMCOUNT(1,VM_cvar);
529
530         PRVM_G_FLOAT(OFS_RETURN) = Cvar_VariableValue(PRVM_G_STRING(OFS_PARM0));
531 }
532
533 /*
534 =================
535 VM_cvar_set
536
537 void cvar_set (string,string)
538 =================
539 */
540 void VM_cvar_set (void)
541 {
542         VM_SAFEPARMCOUNT(2,VM_cvar_set);
543
544         Cvar_Set(PRVM_G_STRING(OFS_PARM0), PRVM_G_STRING(OFS_PARM1));
545 }
546
547 /*
548 =========
549 VM_dprint
550
551 dprint(...[string])
552 =========
553 */
554 void VM_dprint (void)
555 {
556         char string[STRINGTEMP_LENGTH];
557         if (developer.integer)
558         {
559                 VM_VarString(0, string, sizeof(string));
560                 Con_Printf("%s: %s", PRVM_NAME, string);
561         }
562 }
563
564 /*
565 =========
566 VM_ftos
567
568 string  ftos(float)
569 =========
570 */
571
572 void VM_ftos (void)
573 {
574         float v;
575         char *s;
576
577         VM_SAFEPARMCOUNT(1, VM_ftos);
578
579         v = PRVM_G_FLOAT(OFS_PARM0);
580
581         s = VM_GetTempString();
582         if ((float)((int)v) == v)
583                 sprintf(s, "%i", (int)v);
584         else
585                 sprintf(s, "%f", v);
586         PRVM_G_INT(OFS_RETURN) = PRVM_SetString(s);
587 }
588
589 /*
590 =========
591 VM_fabs
592
593 float   fabs(float)
594 =========
595 */
596
597 void VM_fabs (void)
598 {
599         float   v;
600
601         VM_SAFEPARMCOUNT(1,VM_fabs);
602
603         v = PRVM_G_FLOAT(OFS_PARM0);
604         PRVM_G_FLOAT(OFS_RETURN) = fabs(v);
605 }
606
607 /*
608 =========
609 VM_vtos
610
611 string  vtos(vector)
612 =========
613 */
614
615 void VM_vtos (void)
616 {
617         char *s;
618
619         VM_SAFEPARMCOUNT(1,VM_vtos);
620
621         s = VM_GetTempString();
622         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]);
623         PRVM_G_INT(OFS_RETURN) = PRVM_SetString(s);
624 }
625
626 /*
627 =========
628 VM_etos
629
630 string  etos(entity)
631 =========
632 */
633
634 void VM_etos (void)
635 {
636         char *s;
637
638         VM_SAFEPARMCOUNT(1, VM_etos);
639
640         s = VM_GetTempString();
641         sprintf (s, "entity %i", PRVM_G_EDICTNUM(OFS_PARM0));
642         PRVM_G_INT(OFS_RETURN) = PRVM_SetString(s);
643 }
644
645 /*
646 =========
647 VM_stof
648
649 float stof(...[string])
650 =========
651 */
652 void VM_stof(void)
653 {
654         char string[STRINGTEMP_LENGTH];
655         VM_VarString(0, string, sizeof(string));
656         PRVM_G_FLOAT(OFS_RETURN) = atof(string);
657 }
658
659 /*
660 =========
661 VM_spawn
662
663 entity spawn()
664 =========
665 */
666
667 void VM_Spawn (void)
668 {
669         prvm_edict_t    *ed;
670         prog->xfunction->builtinsprofile += 20;
671         ed = PRVM_ED_Alloc();
672         VM_RETURN_EDICT(ed);
673 }
674
675 /*
676 =========
677 VM_remove
678
679 entity  remove()
680 =========
681 */
682
683 void VM_remove (void)
684 {
685         prvm_edict_t    *ed;
686         prog->xfunction->builtinsprofile += 20;
687
688         VM_SAFEPARMCOUNT(1, VM_remove);
689
690         ed = PRVM_G_EDICT(OFS_PARM0);
691 //      if (ed == prog->edicts)
692 //              PRVM_ERROR ("remove: tried to remove world\n");
693 //      if (PRVM_NUM_FOR_EDICT(ed) <= sv.maxclients)
694 //              Host_Error("remove: tried to remove a client\n");
695         PRVM_ED_Free (ed);
696 }
697
698 /*
699 =========
700 VM_find
701
702 entity  find(entity start, .string field, string match)
703 =========
704 */
705
706 void VM_find (void)
707 {
708         int             e;
709         int             f;
710         char    *s, *t;
711         prvm_edict_t    *ed;
712
713         VM_SAFEPARMCOUNT(3,VM_find);
714
715         e = PRVM_G_EDICTNUM(OFS_PARM0);
716         f = PRVM_G_INT(OFS_PARM1);
717         s = PRVM_G_STRING(OFS_PARM2);
718
719         if (!s || !s[0])
720         {
721                 // return reserved edict 0 (could be used for whatever the prog wants)
722                 VM_RETURN_EDICT(prog->edicts);
723                 return;
724         }
725
726         for (e++ ; e < prog->num_edicts ; e++)
727         {
728                 prog->xfunction->builtinsprofile++;
729                 ed = PRVM_EDICT_NUM(e);
730                 if (ed->e->free)
731                         continue;
732                 t = PRVM_E_STRING(ed,f);
733                 if (!t)
734                         continue;
735                 if (!strcmp(t,s))
736                 {
737                         VM_RETURN_EDICT(ed);
738                         return;
739                 }
740         }
741
742         VM_RETURN_EDICT(sv.edicts);
743 }
744
745 /*
746 =========
747 VM_findfloat
748
749   entity        findfloat(entity start, .float field, string match)
750   entity        findentity(entity start, .entity field, string match)
751 =========
752 */
753 // LordHavoc: added this for searching float, int, and entity reference fields
754 void VM_findfloat (void)
755 {
756         int             e;
757         int             f;
758         float   s;
759         prvm_edict_t    *ed;
760
761         VM_SAFEPARMCOUNT(3,VM_findfloat);
762
763         e = PRVM_G_EDICTNUM(OFS_PARM0);
764         f = PRVM_G_INT(OFS_PARM1);
765         s = PRVM_G_FLOAT(OFS_PARM2);
766
767         for (e++ ; e < prog->num_edicts ; e++)
768         {
769                 prog->xfunction->builtinsprofile++;
770                 ed = PRVM_EDICT_NUM(e);
771                 if (ed->e->free)
772                         continue;
773                 if (PRVM_E_FLOAT(ed,f) == s)
774                 {
775                         VM_RETURN_EDICT(ed);
776                         return;
777                 }
778         }
779
780         VM_RETURN_EDICT(prog->edicts);
781 }
782
783 /*
784 =========
785 VM_findchain
786
787 entity  findchain(.string field, string match)
788 =========
789 */
790 int PRVM_ED_FindFieldOffset(const char *field);
791 // chained search for strings in entity fields
792 // entity(.string field, string match) findchain = #402;
793 void VM_findchain (void)
794 {
795         int             i;
796         int             f;
797         int             chain_of;
798         char    *s, *t;
799         prvm_edict_t    *ent, *chain;
800
801         VM_SAFEPARMCOUNT(2,VM_findchain);
802
803         // is the same like !(prog->flag & PRVM_FE_CHAIN) - even if the operator precedence is another
804         if(!prog->flag & PRVM_FE_CHAIN)
805                 PRVM_ERROR("VM_findchain: %s doesnt have a chain field !\n", PRVM_NAME);
806
807         chain_of = PRVM_ED_FindFieldOffset ("chain");
808
809         chain = prog->edicts;
810
811         f = PRVM_G_INT(OFS_PARM0);
812         s = PRVM_G_STRING(OFS_PARM1);
813         if (!s || !s[0])
814         {
815                 VM_RETURN_EDICT(prog->edicts);
816                 return;
817         }
818
819         ent = PRVM_NEXT_EDICT(prog->edicts);
820         for (i = 1;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
821         {
822                 prog->xfunction->builtinsprofile++;
823                 if (ent->e->free)
824                         continue;
825                 t = PRVM_E_STRING(ent,f);
826                 if (!t)
827                         continue;
828                 if (strcmp(t,s))
829                         continue;
830
831                 PRVM_E_FLOAT(ent,chain_of) = PRVM_NUM_FOR_EDICT(chain);
832                 chain = ent;
833         }
834
835         VM_RETURN_EDICT(chain);
836 }
837
838 /*
839 =========
840 VM_findchainfloat
841
842 entity  findchainfloat(.string field, float match)
843 entity  findchainentity(.string field, entity match)
844 =========
845 */
846 // LordHavoc: chained search for float, int, and entity reference fields
847 // entity(.string field, float match) findchainfloat = #403;
848 void VM_findchainfloat (void)
849 {
850         int             i;
851         int             f;
852         int             chain_of;
853         float   s;
854         prvm_edict_t    *ent, *chain;
855
856         VM_SAFEPARMCOUNT(2, VM_findchainfloat);
857
858         if(!prog->flag & PRVM_FE_CHAIN)
859                 PRVM_ERROR("VM_findchainfloat: %s doesnt have a chain field !\n", PRVM_NAME);
860
861         chain_of = PRVM_ED_FindFieldOffset ("chain");
862
863         chain = (prvm_edict_t *)prog->edicts;
864
865         f = PRVM_G_INT(OFS_PARM0);
866         s = PRVM_G_FLOAT(OFS_PARM1);
867
868         ent = PRVM_NEXT_EDICT(prog->edicts);
869         for (i = 1;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
870         {
871                 prog->xfunction->builtinsprofile++;
872                 if (ent->e->free)
873                         continue;
874                 if (E_FLOAT(ent,f) != s)
875                         continue;
876
877                 PRVM_E_FLOAT(ent,chain_of) = PRVM_NUM_FOR_EDICT(chain);
878                 chain = ent;
879         }
880
881         VM_RETURN_EDICT(chain);
882 }
883
884 /*
885 =========
886 VM_precache_file
887
888 precache_file
889 =========
890 */
891 void VM_precache_file (void)
892 {       // precache_file is only used to copy files with qcc, it does nothing
893         VM_SAFEPARMCOUNT(1,VM_precache_file);
894
895         PRVM_G_INT(OFS_RETURN) = PRVM_G_INT(OFS_PARM0);
896 }
897
898 /*
899 =========
900 VM_preache_error
901
902 used instead of the other VM_precache_* functions in the builtin list
903 =========
904 */
905
906 void VM_precache_error (void)
907 {
908         PRVM_ERROR ("PF_Precache_*: Precache can only be done in spawn functions");
909 }
910
911 /*
912 =========
913 VM_precache_sound
914
915 =========
916 */
917 /*
918 void VM_precache_sound (void)
919 {
920         char    *s;
921         int             i;
922
923         VM_SAFEPARMCOUNT(1, VM_precache_sound);
924
925         s = PRVM_G_STRING(OFS_PARM0);
926         PRVM_G_INT(OFS_RETURN) = PRVM_G_INT(OFS_PARM0);
927         VM_CheckEmptyString (s);
928
929         for (i=0 ; i < MAX_SOUNDS ; i++)
930         {
931                 if (!sv.sound_precache[i])
932                 {
933                         sv.sound_precache[i] = s;
934                         return;
935                 }
936                 if (!strcmp(sv.sound_precache[i], s))
937                         return;
938         }
939         Host_Error ("PF_precache_sound: overflow");
940 }*/
941
942 /*
943 =========
944 VM_coredump
945
946 coredump()
947 =========
948 */
949 void VM_coredump (void)
950 {
951         VM_SAFEPARMCOUNT(0,VM_coredump);
952
953         PRVM_ED_PrintEdicts_f ();
954 }
955
956 /*
957 =========
958 VM_traceon
959
960 traceon()
961 =========
962 */
963 void VM_traceon (void)
964 {
965         VM_SAFEPARMCOUNT(0,VM_traceon);
966
967         prog->trace = true;
968 }
969
970 /*
971 =========
972 VM_traceoff
973
974 traceoff()
975 =========
976 */
977 void VM_traceoff (void)
978 {
979         VM_SAFEPARMCOUNT(0,VM_traceoff);
980
981         prog->trace = false;
982 }
983
984 /*
985 =========
986 VM_eprint
987
988 eprint()
989 =========
990 */
991 void VM_eprint (void)
992 {
993         VM_SAFEPARMCOUNT(1,VM_eprint);
994
995         PRVM_ED_PrintNum (PRVM_G_EDICTNUM(OFS_PARM0));
996 }
997
998 /*
999 =========
1000 VM_rint
1001
1002 float   rint(float)
1003 =========
1004 */
1005 void VM_rint (void)
1006 {
1007         float   f;
1008
1009         VM_SAFEPARMCOUNT(1,VM_rint);
1010
1011         f = PRVM_G_FLOAT(OFS_PARM0);
1012         if (f > 0)
1013                 PRVM_G_FLOAT(OFS_RETURN) = (int)(f + 0.5);
1014         else
1015                 PRVM_G_FLOAT(OFS_RETURN) = (int)(f - 0.5);
1016 }
1017
1018 /*
1019 =========
1020 VM_floor
1021
1022 float   floor(float)
1023 =========
1024 */
1025 void VM_floor (void)
1026 {
1027         VM_SAFEPARMCOUNT(1,VM_floor);
1028
1029         PRVM_G_FLOAT(OFS_RETURN) = floor(PRVM_G_FLOAT(OFS_PARM0));
1030 }
1031
1032 /*
1033 =========
1034 VM_ceil
1035
1036 float   ceil(float)
1037 =========
1038 */
1039 void VM_ceil (void)
1040 {
1041         VM_SAFEPARMCOUNT(1,VM_ceil);
1042
1043         PRVM_G_FLOAT(OFS_RETURN) = ceil(PRVM_G_FLOAT(OFS_PARM0));
1044 }
1045
1046
1047 /*
1048 =============
1049 VM_nextent
1050
1051 entity  nextent(entity)
1052 =============
1053 */
1054 void VM_nextent (void)
1055 {
1056         int             i;
1057         prvm_edict_t    *ent;
1058
1059         i = PRVM_G_EDICTNUM(OFS_PARM0);
1060         while (1)
1061         {
1062                 prog->xfunction->builtinsprofile++;
1063                 i++;
1064                 if (i == prog->num_edicts)
1065                 {
1066                         VM_RETURN_EDICT(prog->edicts);
1067                         return;
1068                 }
1069                 ent = PRVM_EDICT_NUM(i);
1070                 if (!ent->e->free)
1071                 {
1072                         VM_RETURN_EDICT(ent);
1073                         return;
1074                 }
1075         }
1076 }
1077
1078 //=============================================================================
1079
1080 /*
1081 ==============
1082 PF_changelevel
1083 ==============
1084 */
1085 /*void PF_changelevel (void)
1086 {
1087         char    *s;
1088
1089 // make sure we don't issue two changelevels
1090         if (svs.changelevel_issued)
1091                 return;
1092         svs.changelevel_issued = true;
1093
1094         s = G_STRING(OFS_PARM0);
1095         Cbuf_AddText (va("changelevel %s\n",s));
1096 }*/
1097
1098 /*
1099 =========
1100 VM_sin
1101
1102 float   sin(float)
1103 =========
1104 */
1105 void VM_sin (void)
1106 {
1107         VM_SAFEPARMCOUNT(1,VM_sin);
1108         PRVM_G_FLOAT(OFS_RETURN) = sin(PRVM_G_FLOAT(OFS_PARM0));
1109 }
1110
1111 /*
1112 =========
1113 VM_cos
1114 float   cos(float)
1115 =========
1116 */
1117 void VM_cos (void)
1118 {
1119         VM_SAFEPARMCOUNT(1,VM_cos);
1120         PRVM_G_FLOAT(OFS_RETURN) = cos(PRVM_G_FLOAT(OFS_PARM0));
1121 }
1122
1123 /*
1124 =========
1125 VM_sqrt
1126
1127 float   sqrt(float)
1128 =========
1129 */
1130 void VM_sqrt (void)
1131 {
1132         VM_SAFEPARMCOUNT(1,VM_sqrt);
1133         PRVM_G_FLOAT(OFS_RETURN) = sqrt(PRVM_G_FLOAT(OFS_PARM0));
1134 }
1135
1136 /*
1137 =================
1138 VM_RandomVec
1139
1140 Returns a vector of length < 1 and > 0
1141
1142 randomvec()
1143 =================
1144 */
1145 void VM_randomvec (void)
1146 {
1147         vec3_t          temp;
1148         //float         length;
1149
1150         VM_SAFEPARMCOUNT(0, VM_randomvec);
1151
1152         //// WTF ??
1153         do
1154         {
1155                 temp[0] = (rand()&32767) * (2.0 / 32767.0) - 1.0;
1156                 temp[1] = (rand()&32767) * (2.0 / 32767.0) - 1.0;
1157                 temp[2] = (rand()&32767) * (2.0 / 32767.0) - 1.0;
1158         }
1159         while (DotProduct(temp, temp) >= 1);
1160         VectorCopy (temp, PRVM_G_VECTOR(OFS_RETURN));
1161
1162         /*
1163         temp[0] = (rand()&32767) * (2.0 / 32767.0) - 1.0;
1164         temp[1] = (rand()&32767) * (2.0 / 32767.0) - 1.0;
1165         temp[2] = (rand()&32767) * (2.0 / 32767.0) - 1.0;
1166         // length returned always > 0
1167         length = (rand()&32766 + 1) * (1.0 / 32767.0) / VectorLength(temp);
1168         VectorScale(temp,length, temp);*/
1169         VectorCopy(temp, PRVM_G_VECTOR(OFS_RETURN));
1170 }
1171
1172 //=============================================================================
1173 #define MAX_QC_CVARS 128 * PRVM_MAXPROGS
1174 cvar_t vm_qc_cvar[MAX_QC_CVARS];
1175 int vm_currentqc_cvar;
1176
1177 /*
1178 =========
1179 VM_registercvar
1180
1181 float   registercvar (string name, string value)
1182 =========
1183 */
1184 void VM_registercvar (void)
1185 {
1186         char *name, *value;
1187         cvar_t *variable;
1188
1189         VM_SAFEPARMCOUNT(2,VM_registercvar);
1190
1191         name = PRVM_G_STRING(OFS_PARM0);
1192         value = PRVM_G_STRING(OFS_PARM1);
1193         PRVM_G_FLOAT(OFS_RETURN) = 0;
1194 // first check to see if it has already been defined
1195         if (Cvar_FindVar (name))
1196                 return;
1197
1198 // check for overlap with a command
1199         if (Cmd_Exists (name))
1200         {
1201                 Con_Printf ("VM_registercvar: %s is a command\n", name);
1202                 return;
1203         }
1204
1205         if (vm_currentqc_cvar >= MAX_QC_CVARS)
1206                 PRVM_ERROR ("VM_registercvar: ran out of cvar slots (%i)\n", MAX_QC_CVARS);
1207
1208 // copy the name and value
1209         variable = &vm_qc_cvar[vm_currentqc_cvar++];
1210         variable->name = Z_Malloc (strlen(name)+1);
1211         strcpy (variable->name, name);
1212         variable->string = Z_Malloc (strlen(value)+1);
1213         strcpy (variable->string, value);
1214         variable->value = atof (value);
1215
1216         Cvar_RegisterVariable(variable);
1217         PRVM_G_FLOAT(OFS_RETURN) = 1; // success
1218 }
1219
1220 /*
1221 =================
1222 VM_min
1223
1224 returns the minimum of two supplied floats
1225
1226 float min(float a, float b, ...[float])
1227 =================
1228 */
1229 void VM_min (void)
1230 {
1231         // LordHavoc: 3+ argument enhancement suggested by FrikaC
1232         if (prog->argc == 2)
1233                 PRVM_G_FLOAT(OFS_RETURN) = min(PRVM_G_FLOAT(OFS_PARM0), PRVM_G_FLOAT(OFS_PARM1));
1234         else if (prog->argc >= 3)
1235         {
1236                 int i;
1237                 float f = PRVM_G_FLOAT(OFS_PARM0);
1238                 for (i = 1;i < prog->argc;i++)
1239                         if (PRVM_G_FLOAT((OFS_PARM0+i*3)) < f)
1240                                 f = PRVM_G_FLOAT((OFS_PARM0+i*3));
1241                 PRVM_G_FLOAT(OFS_RETURN) = f;
1242         }
1243         else
1244                 PRVM_ERROR("VM_min: %s must supply at least 2 floats\n", PRVM_NAME);
1245 }
1246
1247 /*
1248 =================
1249 VM_max
1250
1251 returns the maximum of two supplied floats
1252
1253 float   max(float a, float b, ...[float])
1254 =================
1255 */
1256 void VM_max (void)
1257 {
1258         // LordHavoc: 3+ argument enhancement suggested by FrikaC
1259         if (pr_argc == 2)
1260                 PRVM_G_FLOAT(OFS_RETURN) = max(PRVM_G_FLOAT(OFS_PARM0), PRVM_G_FLOAT(OFS_PARM1));
1261         else if (pr_argc >= 3)
1262         {
1263                 int i;
1264                 float f = PRVM_G_FLOAT(OFS_PARM0);
1265                 for (i = 1;i < pr_argc;i++)
1266                         if (PRVM_G_FLOAT((OFS_PARM0+i*3)) > f)
1267                                 f = PRVM_G_FLOAT((OFS_PARM0+i*3));
1268                 G_FLOAT(OFS_RETURN) = f;
1269         }
1270         else
1271                 PRVM_ERROR("VM_max: %s must supply at least 2 floats\n", PRVM_NAME);
1272 }
1273
1274 /*
1275 =================
1276 VM_bound
1277
1278 returns number bounded by supplied range
1279
1280 float   bound(float min, float value, float max)
1281 =================
1282 */
1283 void VM_bound (void)
1284 {
1285         VM_SAFEPARMCOUNT(3,VM_bound);
1286         PRVM_G_FLOAT(OFS_RETURN) = bound(PRVM_G_FLOAT(OFS_PARM0), PRVM_G_FLOAT(OFS_PARM1), PRVM_G_FLOAT(OFS_PARM2));
1287 }
1288
1289 /*
1290 =================
1291 VM_pow
1292
1293 returns a raised to power b
1294
1295 float   pow(float a, float b)
1296 =================
1297 */
1298 void VM_pow (void)
1299 {
1300         VM_SAFEPARMCOUNT(2,VM_pow);
1301         PRVM_G_FLOAT(OFS_RETURN) = pow(PRVM_G_FLOAT(OFS_PARM0), PRVM_G_FLOAT(OFS_PARM1));
1302 }
1303
1304 /*
1305 =================
1306 VM_copyentity
1307
1308 copies data from one entity to another
1309
1310 copyentity(entity src, entity dst)
1311 =================
1312 */
1313 void VM_copyentity (void)
1314 {
1315         prvm_edict_t *in, *out;
1316         VM_SAFEPARMCOUNT(2,VM_copyentity);
1317         in = PRVM_G_EDICT(OFS_PARM0);
1318         out = PRVM_G_EDICT(OFS_PARM1);
1319         memcpy(out->v, in->v, prog->progs->entityfields * 4);
1320 }
1321
1322 /*
1323 =================
1324 VM_setcolor
1325
1326 sets the color of a client and broadcasts the update to all connected clients
1327
1328 setcolor(clientent, value)
1329 =================
1330 */
1331 /*void PF_setcolor (void)
1332 {
1333         client_t *client;
1334         int entnum, i;
1335         eval_t *val;
1336
1337         entnum = G_EDICTNUM(OFS_PARM0);
1338         i = G_FLOAT(OFS_PARM1);
1339
1340         if (entnum < 1 || entnum > svs.maxclients || !svs.clients[entnum-1].active)
1341         {
1342                 Con_Printf ("tried to setcolor a non-client\n");
1343                 return;
1344         }
1345
1346         client = svs.clients + entnum-1;
1347         if ((val = GETEDICTFIELDVALUE(client->edict, eval_clientcolors)))
1348                 val->_float = i;
1349         client->colors = i;
1350         client->old_colors = i;
1351         client->edict->v->team = (i & 15) + 1;
1352
1353         MSG_WriteByte (&sv.reliable_datagram, svc_updatecolors);
1354         MSG_WriteByte (&sv.reliable_datagram, entnum - 1);
1355         MSG_WriteByte (&sv.reliable_datagram, i);
1356 }*/
1357
1358 #define MAX_VMFILES             256
1359 #define MAX_PRVMFILES   MAX_VMFILES * PRVM_MAXPROGS
1360 // old #define VM_FILES(index) vm_files[PRVM_GetProgNr()+(index)]
1361 #define VM_FILES ((qfile_t**)(vm_files + PRVM_GetProgNr() * MAX_VMFILES))
1362
1363 qfile_t *vm_files[MAX_PRVMFILES];
1364
1365 void VM_Files_Init(void)
1366 {
1367         memset(vm_files, 0, sizeof(qfile_t*[MAX_VMFILES]));
1368 }
1369
1370 void VM_Files_CloseAll(void)
1371 {
1372         int i;
1373         for (i = 0;i < MAX_VMFILES;i++)
1374         {
1375                 if (VM_FILES[i])
1376                         FS_Close(VM_FILES[i]);
1377                 //VM_FILES[i] = NULL;
1378         }
1379         memset(VM_FILES,0,sizeof(qfile_t*[MAX_VMFILES])); // this should be faster (is it ?)
1380 }
1381
1382 /*
1383 =========
1384 VM_fopen
1385
1386 float   fopen(string filename, float mode)
1387 =========
1388 */
1389 // float(string filename, float mode) fopen = #110;
1390 // opens a file inside quake/gamedir/data/ (mode is FILE_READ, FILE_APPEND, or FILE_WRITE),
1391 // returns fhandle >= 0 if successful, or fhandle < 0 if unable to open file for any reason
1392 void VM_fopen(void)
1393 {
1394         int filenum, mode;
1395         char *modestring, *filename;
1396
1397         VM_SAFEPARMCOUNT(2,VM_fopen);
1398
1399         for (filenum = 0;filenum < MAX_VMFILES;filenum++)
1400                 if (VM_FILES[filenum] == NULL)
1401                         break;
1402         if (filenum >= MAX_VMFILES)
1403         {
1404                 Con_Printf("VM_fopen: %s ran out of file handles (%i)\n", PRVM_NAME, MAX_VMFILES);
1405                 PRVM_G_FLOAT(OFS_RETURN) = -2;
1406                 return;
1407         }
1408         mode = PRVM_G_FLOAT(OFS_PARM1);
1409         switch(mode)
1410         {
1411         case 0: // FILE_READ
1412                 modestring = "rb";
1413                 break;
1414         case 1: // FILE_APPEND
1415                 modestring = "ab";
1416                 break;
1417         case 2: // FILE_WRITE
1418                 modestring = "wb";
1419                 break;
1420         default:
1421                 Con_Printf ("VM_fopen: %s no such mode %i (valid: 0 = read, 1 = append, 2 = write)\n", PRVM_NAME, mode);
1422                 PRVM_G_FLOAT(OFS_RETURN) = -3;
1423                 return;
1424         }
1425         filename = PRVM_G_STRING(OFS_PARM0);
1426         // .. is parent directory on many platforms
1427         // / is parent directory on Amiga
1428         // : is root of drive on Amiga (also used as a directory separator on Mac, but / works there too, so that's a bad idea)
1429         // \ is a windows-ism (so it's naughty to use it, / works on all platforms)
1430         if ((filename[0] == '.' && filename[1] == '.') || filename[0] == '/' || strrchr(filename, ':') || strrchr(filename, '\\'))
1431         {
1432                 Con_Printf("VM_fopen: dangerous or non-portable filename \"%s\" not allowed. (contains : or \\ or begins with .. or /)\n", filename);
1433                 PRVM_G_FLOAT(OFS_RETURN) = -4;
1434                 return;
1435         }
1436         VM_FILES[filenum] = FS_Open(va("data/%s", filename), modestring, false);
1437         if (VM_FILES[filenum] == NULL)
1438                 PRVM_G_FLOAT(OFS_RETURN) = -1;
1439         else
1440                 PRVM_G_FLOAT(OFS_RETURN) = filenum;
1441 }
1442
1443 /*
1444 =========
1445 VM_fclose
1446
1447 fclose(float fhandle)
1448 =========
1449 */
1450 //void(float fhandle) fclose = #111; // closes a file
1451 void VM_fclose(void)
1452 {
1453         int filenum;
1454
1455         VM_SAFEPARMCOUNT(1,VM_fclose);
1456
1457         filenum = PRVM_G_FLOAT(OFS_PARM0);
1458         if (filenum < 0 || filenum >= MAX_VMFILES)
1459         {
1460                 Con_Printf("VM_fclose: invalid file handle %i used in %s\n", filenum, PRVM_NAME);
1461                 return;
1462         }
1463         if (VM_FILES[filenum] == NULL)
1464         {
1465                 Con_Printf("VM_fclose: no such file handle %i (or file has been closed) in %s\n", filenum, PRVM_NAME);
1466                 return;
1467         }
1468         FS_Close(VM_FILES[filenum]);
1469         VM_FILES[filenum] = NULL;
1470 }
1471
1472 /*
1473 =========
1474 VM_fgets
1475
1476 string  fgets(float fhandle)
1477 =========
1478 */
1479 //string(float fhandle) fgets = #112; // reads a line of text from the file and returns as a tempstring
1480 void VM_fgets(void)
1481 {
1482         int c, end;
1483         static char string[STRINGTEMP_LENGTH];
1484         int filenum;
1485
1486         VM_SAFEPARMCOUNT(1,VM_fgets);
1487
1488         filenum = PRVM_G_FLOAT(OFS_PARM0);
1489         if (filenum < 0 || filenum >= MAX_VMFILES)
1490         {
1491                 Con_Printf("VM_fgets: invalid file handle %i used in %s\n", filenum, PRVM_NAME);
1492                 return;
1493         }
1494         if (VM_FILES[filenum] == NULL)
1495         {
1496                 Con_Printf("VM_fgets: no such file handle %i (or file has been closed) in %s\n", filenum, PRVM_NAME);
1497                 return;
1498         }
1499         end = 0;
1500         for (;;)
1501         {
1502                 c = FS_Getc(VM_FILES[filenum]);
1503                 if (c == '\r' || c == '\n' || c < 0)
1504                         break;
1505                 if (end < STRINGTEMP_LENGTH - 1)
1506                         string[end++] = c;
1507         }
1508         string[end] = 0;
1509         // remove \n following \r
1510         if (c == '\r')
1511                 c = FS_Getc(VM_FILES[filenum]);
1512         if (developer.integer)
1513                 Con_Printf("fgets: %s: %s\n", PRVM_NAME, string);
1514         if (c >= 0)
1515                 PRVM_G_INT(OFS_RETURN) = PRVM_SetString(string);
1516         else
1517                 PRVM_G_INT(OFS_RETURN) = 0;
1518 }
1519
1520 /*
1521 =========
1522 VM_fputs
1523
1524 fputs(float fhandle, string s)
1525 =========
1526 */
1527 //void(float fhandle, string s) fputs = #113; // writes a line of text to the end of the file
1528 void VM_fputs(void)
1529 {
1530         int stringlength;
1531         char string[STRINGTEMP_LENGTH];
1532         int filenum;
1533
1534         VM_SAFEPARMCOUNT(2,VM_fputs);
1535
1536         filenum = PRVM_G_FLOAT(OFS_PARM0);
1537         if (filenum < 0 || filenum >= MAX_VMFILES)
1538         {
1539                 Con_Printf("VM_fputs: invalid file handle %i used in %s\n", filenum, PRVM_NAME);
1540                 return;
1541         }
1542         if (VM_FILES[filenum] == NULL)
1543         {
1544                 Con_Printf("VM_fputs: no such file handle %i (or file has been closed) in %s\n", filenum, PRVM_NAME);
1545                 return;
1546         }
1547         VM_VarString(1, string, sizeof(string));
1548         if ((stringlength = strlen(string)))
1549                 FS_Write(VM_FILES[filenum], string, stringlength);
1550         if (developer.integer)
1551                 Con_Printf("fputs: %s: %s\n", PRVM_NAME, string);
1552 }
1553
1554 /*
1555 =========
1556 VM_strlen
1557
1558 float   strlen(string s)
1559 =========
1560 */
1561 //float(string s) strlen = #114; // returns how many characters are in a string
1562 void VM_strlen(void)
1563 {
1564         char *s;
1565
1566         VM_SAFEPARMCOUNT(1,VM_strlen);
1567
1568         s = PRVM_G_STRING(OFS_PARM0);
1569         if (s)
1570                 PRVM_G_FLOAT(OFS_RETURN) = strlen(s);
1571         else
1572                 PRVM_G_FLOAT(OFS_RETURN) = 0;
1573 }
1574
1575 /*
1576 =========
1577 VM_strcat
1578
1579 string strcat(string s1, string s2)
1580 =========
1581 */
1582 //string(string s1, string s2) strcat = #115;
1583 // concatenates two strings (for example "abc", "def" would return "abcdef")
1584 // and returns as a tempstring
1585 void VM_strcat(void)
1586 {
1587         char *s;
1588
1589         VM_SAFEPARMCOUNT(2,VM_strcat);
1590
1591         s = VM_GetTempString();
1592         VM_VarString(0, s, STRINGTEMP_LENGTH);
1593         PRVM_G_INT(OFS_RETURN) = PRVM_SetString(s);
1594 }
1595
1596 /*
1597 =========
1598 VM_substring
1599
1600 string  substring(string s, float start, float length)
1601 =========
1602 */
1603 // string(string s, float start, float length) substring = #116;
1604 // returns a section of a string as a tempstring
1605 void VM_substring(void)
1606 {
1607         int i, start, length;
1608         char *s, *string;
1609
1610         VM_SAFEPARMCOUNT(3,VM_substring);
1611
1612         string = VM_GetTempString();
1613         s = PRVM_G_STRING(OFS_PARM0);
1614         start = PRVM_G_FLOAT(OFS_PARM1);
1615         length = PRVM_G_FLOAT(OFS_PARM2);
1616         if (!s)
1617                 s = "";
1618         for (i = 0;i < start && *s;i++, s++);
1619         for (i = 0;i < STRINGTEMP_LENGTH - 1 && *s && i < length;i++, s++)
1620                 string[i] = *s;
1621         string[i] = 0;
1622         PRVM_G_INT(OFS_RETURN) = PRVM_SetString(string);
1623 }
1624
1625 /*
1626 =========
1627 VM_stov
1628
1629 vector  stov(string s)
1630 =========
1631 */
1632 //vector(string s) stov = #117; // returns vector value from a string
1633 void VM_stov(void)
1634 {
1635         char string[STRINGTEMP_LENGTH];
1636
1637         VM_SAFEPARMCOUNT(1,VM_stov);
1638
1639         VM_VarString(0, string, sizeof(string));
1640         Math_atov(string, PRVM_G_VECTOR(OFS_RETURN));
1641 }
1642
1643 /*
1644 =========
1645 VM_strzone
1646
1647 string  strzone(string s)
1648 =========
1649 */
1650 //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)
1651 void VM_strzone(void)
1652 {
1653         char *in, *out;
1654
1655         VM_SAFEPARMCOUNT(1,VM_strzone);
1656
1657         in = PRVM_G_STRING(OFS_PARM0);
1658         out = Mem_Alloc(VM_STRINGS_MEMPOOL, strlen(in) + 1);
1659         strcpy(out, in);
1660         PRVM_G_INT(OFS_RETURN) = PRVM_SetString(out);
1661 }
1662
1663 /*
1664 =========
1665 VM_strunzone
1666
1667 strzone(string s)
1668 =========
1669 */
1670 //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!!!)
1671 void VM_strunzone(void)
1672 {
1673         VM_SAFEPARMCOUNT(1,VM_strunzone);
1674
1675         Mem_Free(PRVM_G_STRING(OFS_PARM0));
1676 }
1677
1678 /*
1679 =========
1680 VM_command (used by client and menu)
1681
1682 clientcommand(float client, string s) (for client and menu)
1683 =========
1684 */
1685 //void(entity e, string s) clientcommand = #440; // executes a command string as if it came from the specified client
1686 //this function originally written by KrimZon, made shorter by LordHavoc
1687 void VM_clcommand (void)
1688 {
1689         client_t *temp_client;
1690         int i;
1691
1692         VM_SAFEPARMCOUNT(2,VM_clcommand);
1693
1694         //find client for this entity
1695         i = PRVM_G_FLOAT(OFS_PARM0);
1696         if (!sv.active  || i < 0 || i >= svs.maxclients || !svs.clients[i].active)
1697         {
1698                 Con_Printf("VM_clientcommand: %s: invalid client/server is not active !", PRVM_NAME);
1699                 return;
1700         }
1701
1702         temp_client = host_client;
1703         host_client = svs.clients + i;
1704         Cmd_ExecuteString (PRVM_G_STRING(OFS_PARM1), src_client);
1705         host_client = temp_client;
1706 }
1707
1708
1709 /*
1710 =========
1711 VM_tokenize
1712
1713 float tokenize(string s)
1714 =========
1715 */
1716 //float(string s) tokenize = #441;
1717 // takes apart a string into individal words (access them with argv), returns how many
1718 // this function originally written by KrimZon, made shorter by LordHavoc
1719 static char **tokens = NULL;
1720 static int    max_tokens, num_tokens = 0;
1721 void VM_tokenize (void)
1722 {
1723         const char *p;
1724         char *str;
1725
1726         VM_SAFEPARMCOUNT(1,VM_tokenize);
1727
1728         str = PRVM_G_STRING(OFS_PARM0);
1729
1730         if (tokens != NULL)
1731         {
1732                 int i;
1733                 for (i=0;i<num_tokens;i++)
1734                         Z_Free(tokens[i]);
1735                 Z_Free(tokens);
1736                 num_tokens = 0;
1737         }
1738
1739         tokens = Z_Malloc(strlen(str) * sizeof(char *));
1740         max_tokens = strlen(str);
1741
1742         for (p = str;COM_ParseToken(&p, false) && num_tokens < max_tokens;num_tokens++)
1743         {
1744                 tokens[num_tokens] = Z_Malloc(strlen(com_token) + 1);
1745                 strcpy(tokens[num_tokens], com_token);
1746         }
1747
1748         PRVM_G_FLOAT(OFS_RETURN) = num_tokens;
1749 }
1750
1751 /*
1752 =========
1753 VM_argv
1754
1755 string argv(float n)
1756 =========
1757 */
1758 //string(float n) argv = #442;
1759 // returns a word from the tokenized string (returns nothing for an invalid index)
1760 // this function originally written by KrimZon, made shorter by LordHavoc
1761 void VM_argv (void)
1762 {
1763         int token_num;
1764
1765         VM_SAFEPARMCOUNT(1,VM_argv);
1766
1767         token_num = PRVM_G_FLOAT(OFS_PARM0);
1768         if (token_num >= 0 && token_num < num_tokens)
1769                 PRVM_G_INT(OFS_RETURN) = PRVM_SetString(tokens[token_num]);
1770         else
1771                 PRVM_G_INT(OFS_RETURN) = PRVM_SetString("");
1772 }
1773
1774 /*
1775 //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)
1776 void PF_setattachment (void)
1777 {
1778         edict_t *e = G_EDICT(OFS_PARM0);
1779         edict_t *tagentity = G_EDICT(OFS_PARM1);
1780         char *tagname = G_STRING(OFS_PARM2);
1781         eval_t *v;
1782         int i, modelindex;
1783         model_t *model;
1784
1785         if (tagentity == NULL)
1786                 tagentity = sv.edicts;
1787
1788         v = GETEDICTFIELDVALUE(e, eval_tag_entity);
1789         if (v)
1790                 v->edict = EDICT_TO_PROG(tagentity);
1791
1792         v = GETEDICTFIELDVALUE(e, eval_tag_index);
1793         if (v)
1794                 v->_float = 0;
1795         if (tagentity != NULL && tagentity != sv.edicts && tagname && tagname[0])
1796         {
1797                 modelindex = (int)tagentity->v->modelindex;
1798                 if (modelindex >= 0 && modelindex < MAX_MODELS)
1799                 {
1800                         model = sv.models[modelindex];
1801                         if (model->data_overridetagnamesforskin && (unsigned int)tagentity->v->skin < (unsigned int)model->numskins && model->data_overridetagnamesforskin[(unsigned int)tagentity->v->skin].num_overridetagnames)
1802                                 for (i = 0;i < model->data_overridetagnamesforskin[(unsigned int)tagentity->v->skin].num_overridetagnames;i++)
1803                                         if (!strcmp(tagname, model->data_overridetagnamesforskin[(unsigned int)tagentity->v->skin].data_overridetagnames[i].name))
1804                                                 v->_float = i + 1;
1805                         if (v->_float == 0 && model->alias.aliasnum_tags)
1806                                 for (i = 0;i < model->alias.aliasnum_tags;i++)
1807                                         if (!strcmp(tagname, model->alias.aliasdata_tags[i].name))
1808                                                 v->_float = i + 1;
1809                         if (v->_float == 0)
1810                                 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);
1811                 }
1812                 else
1813                         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));
1814         }
1815 }*/
1816
1817 /*
1818 =========
1819 VM_serverstate
1820
1821 float   isserver()
1822 =========
1823 */
1824 void VM_isserver(void)
1825 {
1826         VM_SAFEPARMCOUNT(0,VM_serverstate);
1827
1828         PRVM_G_FLOAT(OFS_RETURN) = sv.active;
1829 }
1830
1831 /*
1832 =========
1833 VM_clientcount
1834
1835 float   clientcount()
1836 =========
1837 */
1838 void VM_clientcount(void)
1839 {
1840         VM_SAFEPARMCOUNT(0,VM_clientcount);
1841
1842         PRVM_G_FLOAT(OFS_RETURN) = svs.maxclients;
1843 }
1844
1845 /*
1846 =========
1847 VM_clientstate
1848
1849 float   clientstate()
1850 =========
1851 */
1852 void VM_clientstate(void)
1853 {
1854         VM_SAFEPARMCOUNT(0,VM_clientstate);
1855
1856         PRVM_G_FLOAT(OFS_RETURN) = cls.state;
1857 }
1858
1859 void VM_Cmd_Init(void)
1860 {
1861 }
1862
1863 void VM_Cmd_Reset(void)
1864 {
1865 }
1866
1867 //============================================================================
1868 // Server
1869
1870 char *vm_sv_extensions =
1871 "";
1872
1873 prvm_builtin_t vm_sv_builtins[] = {
1874 0  // to be consistent with the old vm
1875 };
1876
1877 const int vm_sv_numbuiltins = sizeof(vm_sv_builtins) / sizeof(prvm_builtin_t);
1878
1879 void VM_SV_Cmd_Init(void)
1880 {
1881 }
1882
1883 void VM_SV_Cmd_Reset(void)
1884 {
1885 }
1886
1887 //============================================================================
1888 // Client
1889
1890 char *vm_cl_extensions =
1891 "";
1892
1893 prvm_builtin_t vm_cl_builtins[] = {
1894 0  // to be consistent with the old vm
1895 };
1896
1897 const int vm_cl_numbuiltins = sizeof(vm_cl_builtins) / sizeof(prvm_builtin_t);
1898
1899 void VM_CL_Cmd_Init(void)
1900 {
1901 }
1902
1903 void VM_CL_Cmd_Reset(void)
1904 {
1905 }
1906
1907 //============================================================================
1908 // Menu
1909
1910 char *vm_m_extensions =
1911 "";
1912
1913 // void setkeydest(float dest)
1914 void VM_M_SetKeyDest(void)
1915 {
1916         VM_SAFEPARMCOUNT(1,VM_M_SetKeyDest);
1917
1918         switch((int)PRVM_G_FLOAT(OFS_PARM0))
1919         {
1920         case 0:
1921                 // key_game
1922                 key_dest = key_game;
1923                 break;
1924         case 2:
1925                 // key_menu
1926                 key_dest = key_menu;
1927                 break;
1928         case 1:
1929                 // key_message
1930                 // key_dest = key_message
1931                 // break;
1932         default:
1933                 PRVM_ERROR("VM_M_SetKeyDest: wrong destination %i !\n",prog->globals[OFS_PARM0]);
1934         }
1935
1936         return;
1937 }
1938
1939 // float getkeydest(void)
1940 void VM_M_GetKeyDest(void)
1941 {
1942         VM_SAFEPARMCOUNT(0,VM_M_GetKeyDest);
1943
1944         // key_game = 0, key_message = 1, key_menu = 2, unknown = 3
1945         switch(key_dest)
1946         {
1947         case key_game:
1948                 PRVM_G_FLOAT(OFS_RETURN) = 0;
1949                 break;
1950         case key_menu:
1951                 PRVM_G_FLOAT(OFS_RETURN) = 2;
1952                 break;
1953         case key_message:
1954                 // not supported
1955                 // PRVM_G_FLOAT(OFS_RETURN) = 1;
1956                 // break;
1957         default:
1958                 PRVM_G_FLOAT(OFS_RETURN) = 3;
1959         }
1960 }
1961
1962 prvm_builtin_t vm_m_builtins[] = {
1963 0, // to be consistent with the old vm
1964 e1000,
1965 VM_M_SetKeyDest,
1966 VM_M_GetKeyDest
1967 };
1968
1969 const int vm_m_numbuiltins = sizeof(vm_m_builtins) / sizeof(prvm_builtin_t);
1970
1971 void VM_M_Cmd_Init(void)
1972 {
1973 }
1974
1975 void VM_M_Cmd_Reset(void)
1976 {
1977 }
1978