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