added FRIK_FILE (aka QSG_FILE) extension, this brings string handling and text file...
[divverent/darkplaces.git] / pr_cmds.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20
21 #include "quakedef.h"
22
23 cvar_t sv_aim = {CVAR_SAVE, "sv_aim", "2"}; //"0.93"}; // LordHavoc: disabled autoaim by default
24 cvar_t pr_zone_min_strings = {0, "pr_zone_min_strings", "64"};
25
26 mempool_t *pr_strings_mempool;
27
28 #define MAX_VARSTRING 4096
29
30 char pr_varstring_temp[MAX_VARSTRING];
31
32 #define RETURN_EDICT(e) (((int *)pr_globals)[OFS_RETURN] = EDICT_TO_PROG(e))
33
34
35 /*
36 ===============================================================================
37
38                                                 BUILT-IN FUNCTIONS
39
40 ===============================================================================
41 */
42
43
44 char *PF_VarString (int first)
45 {
46         int i, j, end;
47         char *s;
48
49         end = 0;
50         for (i = first;i < pr_argc;i++)
51         {
52                 // LordHavoc: FIXME: this is just a strlcat inlined
53                 s = G_STRING((OFS_PARM0+i*3));
54                 j = strlen(s);
55                 if (j > MAX_VARSTRING - 1 - end)
56                         j = MAX_VARSTRING - 1 - end;
57                 if (j > 0)
58                 {
59                         memcpy(pr_varstring_temp + end, s, j);
60                         end += j;
61                 }
62         }
63         pr_varstring_temp[end] = 0;
64         return pr_varstring_temp;
65 }
66
67 char *ENGINE_EXTENSIONS =
68 "DP_ENT_ALPHA "
69 "DP_ENT_CUSTOMCOLORMAP "
70 "DP_ENT_EXTERIORMODELTOCLIENT "
71 "DP_ENT_LOWPRECISION "
72 "DP_ENT_GLOW "
73 "DP_ENT_SCALE "
74 "DP_ENT_VIEWMODEL "
75 "DP_GFX_FOG "
76 "DP_HALFLIFE_MAP "
77 "DP_INPUTBUTTONS "
78 "DP_MONSTERWALK "
79 "DP_MOVETYPEFOLLOW "
80 "DP_QC_CHANGEPITCH "
81 "DP_QC_COPYENTITY "
82 "DP_QC_ETOS "
83 "DP_QC_FINDCHAIN "
84 "DP_QC_FINDCHAINFLOAT "
85 "DP_QC_FINDFLOAT "
86 "DP_QC_GETLIGHT "
87 "DP_QC_MINMAXBOUND "
88 "DP_QC_RANDOMVEC "
89 "DP_QC_SINCOSSQRTPOW "
90 "DP_QC_TRACEBOX "
91 "DP_QC_TRACETOSS "
92 "DP_QC_VECTORVECTORS "
93 "DP_QUAKE2_MODEL "
94 "DP_REGISTERCVAR "
95 "DP_SOLIDCORPSE "
96 "DP_SPRITE32 "
97 "DP_SV_DRAWONLYTOCLIENT "
98 "DP_SV_EFFECT "
99 "DP_SV_EXTERIORMODELTOCLIENT "
100 "DP_SV_NODRAWTOCLIENT "
101 "DP_SV_PLAYERPHYSICS "
102 "DP_SV_SETCOLOR "
103 "DP_SV_SLOWMO "
104 "DP_TE_BLOOD "
105 "DP_TE_BLOODSHOWER "
106 "DP_TE_EXPLOSIONRGB "
107 "DP_TE_PARTICLECUBE "
108 "DP_TE_PARTICLERAIN "
109 "DP_TE_PARTICLESNOW "
110 "DP_TE_SPARK "
111 "FRIK_FILE "
112 "NEH_CMD_PLAY2 "
113 "NEH_RESTOREGAME "
114 "QSG_FILE "
115 "TW_SV_STEPCONTROL "
116 ;
117
118 qboolean checkextension(char *name)
119 {
120         int len;
121         char *e, *start;
122         len = strlen(name);
123         for (e = ENGINE_EXTENSIONS;*e;e++)
124         {
125                 while (*e == ' ')
126                         e++;
127                 if (!*e)
128                         break;
129                 start = e;
130                 while (*e && *e != ' ')
131                         e++;
132                 if (e - start == len)
133                         if (!strncasecmp(start, name, len))
134                                 return true;
135         }
136         return false;
137 }
138
139 /*
140 =================
141 PF_checkextension
142
143 returns true if the extension is supported by the server
144
145 checkextension(extensionname)
146 =================
147 */
148 void PF_checkextension (void)
149 {
150         G_FLOAT(OFS_RETURN) = checkextension(G_STRING(OFS_PARM0));
151 }
152
153 /*
154 =================
155 PF_error
156
157 This is a TERMINAL error, which will kill off the entire server.
158 Dumps self.
159
160 error(value)
161 =================
162 */
163 void PF_error (void)
164 {
165         char    *s;
166         edict_t *ed;
167
168         s = PF_VarString(0);
169         Con_Printf ("======SERVER ERROR in %s:\n%s\n", PR_GetString(pr_xfunction->s_name), s);
170         ed = PROG_TO_EDICT(pr_global_struct->self);
171         ED_Print (ed);
172
173         Host_Error ("Program error");
174 }
175
176 /*
177 =================
178 PF_objerror
179
180 Dumps out self, then an error message.  The program is aborted and self is
181 removed, but the level can continue.
182
183 objerror(value)
184 =================
185 */
186 void PF_objerror (void)
187 {
188         char    *s;
189         edict_t *ed;
190
191         s = PF_VarString(0);
192         Con_Printf ("======OBJECT ERROR in %s:\n%s\n", PR_GetString(pr_xfunction->s_name), s);
193         ed = PROG_TO_EDICT(pr_global_struct->self);
194         ED_Print (ed);
195         ED_Free (ed);
196 }
197
198
199 /*
200 ==============
201 PF_makevectors
202
203 Writes new values for v_forward, v_up, and v_right based on angles
204 makevectors(vector)
205 ==============
206 */
207 void PF_makevectors (void)
208 {
209         AngleVectors (G_VECTOR(OFS_PARM0), pr_global_struct->v_forward, pr_global_struct->v_right, pr_global_struct->v_up);
210 }
211
212 /*
213 ==============
214 PF_vectorvectors
215
216 Writes new values for v_forward, v_up, and v_right based on the given forward vector
217 vectorvectors(vector, vector)
218 ==============
219 */
220 void PF_vectorvectors (void)
221 {
222         VectorNormalize2(G_VECTOR(OFS_PARM0), pr_global_struct->v_forward);
223         VectorVectors(pr_global_struct->v_forward, pr_global_struct->v_right, pr_global_struct->v_up);
224 }
225
226 /*
227 =================
228 PF_setorigin
229
230 This is the only valid way to move an object without using the physics of the world (setting velocity and waiting).  Directly changing origin will not set internal links correctly, so clipping would be messed up.  This should be called when an object is spawned, and then only if it is teleported.
231
232 setorigin (entity, origin)
233 =================
234 */
235 void PF_setorigin (void)
236 {
237         edict_t *e;
238         float   *org;
239
240         e = G_EDICT(OFS_PARM0);
241         org = G_VECTOR(OFS_PARM1);
242         VectorCopy (org, e->v->origin);
243         SV_LinkEdict (e, false);
244 }
245
246
247 void SetMinMaxSize (edict_t *e, float *min, float *max, qboolean rotate)
248 {
249         int             i;
250
251         for (i=0 ; i<3 ; i++)
252                 if (min[i] > max[i])
253                         Host_Error ("backwards mins/maxs");
254
255 // set derived values
256         VectorCopy (min, e->v->mins);
257         VectorCopy (max, e->v->maxs);
258         VectorSubtract (max, min, e->v->size);
259
260         SV_LinkEdict (e, false);
261 }
262
263 /*
264 =================
265 PF_setsize
266
267 the size box is rotated by the current angle
268 LordHavoc: no it isn't...
269
270 setsize (entity, minvector, maxvector)
271 =================
272 */
273 void PF_setsize (void)
274 {
275         edict_t *e;
276         float   *min, *max;
277
278         e = G_EDICT(OFS_PARM0);
279         min = G_VECTOR(OFS_PARM1);
280         max = G_VECTOR(OFS_PARM2);
281         SetMinMaxSize (e, min, max, false);
282 }
283
284
285 /*
286 =================
287 PF_setmodel
288
289 setmodel(entity, model)
290 =================
291 */
292 void PF_setmodel (void)
293 {
294         edict_t *e;
295         char    *m, **check;
296         model_t *mod;
297         int             i;
298
299         e = G_EDICT(OFS_PARM0);
300         m = G_STRING(OFS_PARM1);
301
302 // check to see if model was properly precached
303         for (i=0, check = sv.model_precache ; *check ; i++, check++)
304                 if (!strcmp(*check, m))
305                         break;
306
307         if (!*check)
308                 Host_Error ("no precache: %s\n", m);
309
310
311         e->v->model = PR_SetString(*check);
312         e->v->modelindex = i;
313
314         mod = sv.models[ (int)e->v->modelindex];
315
316         if (mod)
317                 SetMinMaxSize (e, mod->normalmins, mod->normalmaxs, true);
318         else
319                 SetMinMaxSize (e, vec3_origin, vec3_origin, true);
320 }
321
322 /*
323 =================
324 PF_bprint
325
326 broadcast print to everyone on server
327
328 bprint(value)
329 =================
330 */
331 void PF_bprint (void)
332 {
333         char            *s;
334
335         s = PF_VarString(0);
336         SV_BroadcastPrintf ("%s", s);
337 }
338
339 /*
340 =================
341 PF_sprint
342
343 single print to a specific client
344
345 sprint(clientent, value)
346 =================
347 */
348 void PF_sprint (void)
349 {
350         char            *s;
351         client_t        *client;
352         int                     entnum;
353
354         entnum = G_EDICTNUM(OFS_PARM0);
355         s = PF_VarString(1);
356
357         if (entnum < 1 || entnum > svs.maxclients)
358         {
359                 Con_Printf ("tried to sprint to a non-client\n");
360                 return;
361         }
362
363         client = &svs.clients[entnum-1];
364
365         MSG_WriteChar (&client->message,svc_print);
366         MSG_WriteString (&client->message, s );
367 }
368
369
370 /*
371 =================
372 PF_centerprint
373
374 single print to a specific client
375
376 centerprint(clientent, value)
377 =================
378 */
379 void PF_centerprint (void)
380 {
381         char            *s;
382         client_t        *client;
383         int                     entnum;
384
385         entnum = G_EDICTNUM(OFS_PARM0);
386         s = PF_VarString(1);
387
388         if (entnum < 1 || entnum > svs.maxclients)
389         {
390                 Con_Printf ("tried to sprint to a non-client\n");
391                 return;
392         }
393
394         client = &svs.clients[entnum-1];
395
396         MSG_WriteChar (&client->message,svc_centerprint);
397         MSG_WriteString (&client->message, s );
398 }
399
400
401 /*
402 =================
403 PF_normalize
404
405 vector normalize(vector)
406 =================
407 */
408 void PF_normalize (void)
409 {
410         float   *value1;
411         vec3_t  newvalue;
412         float   new;
413
414         value1 = G_VECTOR(OFS_PARM0);
415
416         new = value1[0] * value1[0] + value1[1] * value1[1] + value1[2]*value1[2];
417         new = sqrt(new);
418
419         if (new == 0)
420                 newvalue[0] = newvalue[1] = newvalue[2] = 0;
421         else
422         {
423                 new = 1/new;
424                 newvalue[0] = value1[0] * new;
425                 newvalue[1] = value1[1] * new;
426                 newvalue[2] = value1[2] * new;
427         }
428
429         VectorCopy (newvalue, G_VECTOR(OFS_RETURN));
430 }
431
432 /*
433 =================
434 PF_vlen
435
436 scalar vlen(vector)
437 =================
438 */
439 void PF_vlen (void)
440 {
441         float   *value1;
442         float   new;
443
444         value1 = G_VECTOR(OFS_PARM0);
445
446         new = value1[0] * value1[0] + value1[1] * value1[1] + value1[2]*value1[2];
447         new = sqrt(new);
448
449         G_FLOAT(OFS_RETURN) = new;
450 }
451
452 /*
453 =================
454 PF_vectoyaw
455
456 float vectoyaw(vector)
457 =================
458 */
459 void PF_vectoyaw (void)
460 {
461         float   *value1;
462         float   yaw;
463
464         value1 = G_VECTOR(OFS_PARM0);
465
466         if (value1[1] == 0 && value1[0] == 0)
467                 yaw = 0;
468         else
469         {
470                 yaw = (int) (atan2(value1[1], value1[0]) * 180 / M_PI);
471                 if (yaw < 0)
472                         yaw += 360;
473         }
474
475         G_FLOAT(OFS_RETURN) = yaw;
476 }
477
478
479 /*
480 =================
481 PF_vectoangles
482
483 vector vectoangles(vector)
484 =================
485 */
486 void PF_vectoangles (void)
487 {
488         float   *value1;
489         float   forward;
490         float   yaw, pitch;
491
492         value1 = G_VECTOR(OFS_PARM0);
493
494         if (value1[1] == 0 && value1[0] == 0)
495         {
496                 yaw = 0;
497                 if (value1[2] > 0)
498                         pitch = 90;
499                 else
500                         pitch = 270;
501         }
502         else
503         {
504                 // LordHavoc: optimized a bit
505                 if (value1[0])
506                 {
507                         yaw = (atan2(value1[1], value1[0]) * 180 / M_PI);
508                         if (yaw < 0)
509                                 yaw += 360;
510                 }
511                 else if (value1[1] > 0)
512                         yaw = 90;
513                 else
514                         yaw = 270;
515
516                 forward = sqrt(value1[0]*value1[0] + value1[1]*value1[1]);
517                 pitch = (int) (atan2(value1[2], forward) * 180 / M_PI);
518                 if (pitch < 0)
519                         pitch += 360;
520         }
521
522         G_FLOAT(OFS_RETURN+0) = pitch;
523         G_FLOAT(OFS_RETURN+1) = yaw;
524         G_FLOAT(OFS_RETURN+2) = 0;
525 }
526
527 /*
528 =================
529 PF_Random
530
531 Returns a number from 0<= num < 1
532
533 random()
534 =================
535 */
536 void PF_random (void)
537 {
538         float           num;
539
540         num = (rand ()&0x7fff) / ((float)0x7fff);
541
542         G_FLOAT(OFS_RETURN) = num;
543 }
544
545 /*
546 =================
547 PF_particle
548
549 particle(origin, color, count)
550 =================
551 */
552 void PF_particle (void)
553 {
554         float           *org, *dir;
555         float           color;
556         float           count;
557
558         org = G_VECTOR(OFS_PARM0);
559         dir = G_VECTOR(OFS_PARM1);
560         color = G_FLOAT(OFS_PARM2);
561         count = G_FLOAT(OFS_PARM3);
562         SV_StartParticle (org, dir, color, count);
563 }
564
565
566 /*
567 =================
568 PF_ambientsound
569
570 =================
571 */
572 void PF_ambientsound (void)
573 {
574         char            **check;
575         char            *samp;
576         float           *pos;
577         float           vol, attenuation;
578         int                     i, soundnum, large;
579
580         pos = G_VECTOR (OFS_PARM0);
581         samp = G_STRING(OFS_PARM1);
582         vol = G_FLOAT(OFS_PARM2);
583         attenuation = G_FLOAT(OFS_PARM3);
584
585 // check to see if samp was properly precached
586         for (soundnum=0, check = sv.sound_precache ; *check ; check++, soundnum++)
587                 if (!strcmp(*check,samp))
588                         break;
589
590         if (!*check)
591         {
592                 Con_Printf ("no precache: %s\n", samp);
593                 return;
594         }
595
596         large = false;
597         if (soundnum >= 256)
598                 large = true;
599
600         // add an svc_spawnambient command to the level signon packet
601
602         if (large)
603                 MSG_WriteByte (&sv.signon, svc_spawnstaticsound2);
604         else
605                 MSG_WriteByte (&sv.signon, svc_spawnstaticsound);
606
607         for (i=0 ; i<3 ; i++)
608                 MSG_WriteDPCoord(&sv.signon, pos[i]);
609
610         if (large)
611                 MSG_WriteShort (&sv.signon, soundnum);
612         else
613                 MSG_WriteByte (&sv.signon, soundnum);
614
615         MSG_WriteByte (&sv.signon, vol*255);
616         MSG_WriteByte (&sv.signon, attenuation*64);
617
618 }
619
620 /*
621 =================
622 PF_sound
623
624 Each entity can have eight independant sound sources, like voice,
625 weapon, feet, etc.
626
627 Channel 0 is an auto-allocate channel, the others override anything
628 already running on that entity/channel pair.
629
630 An attenuation of 0 will play full volume everywhere in the level.
631 Larger attenuations will drop off.
632
633 =================
634 */
635 void PF_sound (void)
636 {
637         char            *sample;
638         int                     channel;
639         edict_t         *entity;
640         int             volume;
641         float attenuation;
642
643         entity = G_EDICT(OFS_PARM0);
644         channel = G_FLOAT(OFS_PARM1);
645         sample = G_STRING(OFS_PARM2);
646         volume = G_FLOAT(OFS_PARM3) * 255;
647         attenuation = G_FLOAT(OFS_PARM4);
648
649         if (volume < 0 || volume > 255)
650                 Host_Error ("SV_StartSound: volume = %i", volume);
651
652         if (attenuation < 0 || attenuation > 4)
653                 Host_Error ("SV_StartSound: attenuation = %f", attenuation);
654
655         if (channel < 0 || channel > 7)
656                 Host_Error ("SV_StartSound: channel = %i", channel);
657
658         SV_StartSound (entity, channel, sample, volume, attenuation);
659 }
660
661 /*
662 =================
663 PF_break
664
665 break()
666 =================
667 */
668 void PF_break (void)
669 {
670         Host_Error ("break statement");
671 }
672
673 /*
674 =================
675 PF_traceline
676
677 Used for use tracing and shot targeting
678 Traces are blocked by bbox and exact bsp entityes, and also slide box entities
679 if the tryents flag is set.
680
681 traceline (vector1, vector2, tryents)
682 =================
683 */
684 void PF_traceline (void)
685 {
686         float   *v1, *v2;
687         trace_t trace;
688         int             nomonsters;
689         edict_t *ent;
690
691         pr_xfunction->builtinsprofile += 30;
692
693         v1 = G_VECTOR(OFS_PARM0);
694         v2 = G_VECTOR(OFS_PARM1);
695         nomonsters = G_FLOAT(OFS_PARM2);
696         ent = G_EDICT(OFS_PARM3);
697
698         trace = SV_Move (v1, vec3_origin, vec3_origin, v2, nomonsters ? MOVE_NOMONSTERS : MOVE_NORMAL, ent);
699
700         pr_global_struct->trace_allsolid = trace.allsolid;
701         pr_global_struct->trace_startsolid = trace.startsolid;
702         pr_global_struct->trace_fraction = trace.fraction;
703         pr_global_struct->trace_inwater = trace.inwater;
704         pr_global_struct->trace_inopen = trace.inopen;
705         VectorCopy (trace.endpos, pr_global_struct->trace_endpos);
706         VectorCopy (trace.plane.normal, pr_global_struct->trace_plane_normal);
707         pr_global_struct->trace_plane_dist =  trace.plane.dist;
708         if (trace.ent)
709                 pr_global_struct->trace_ent = EDICT_TO_PROG(trace.ent);
710         else
711                 pr_global_struct->trace_ent = EDICT_TO_PROG(sv.edicts);
712         // FIXME: add trace_endcontents
713 }
714
715
716 /*
717 =================
718 PF_tracebox
719
720 Used for use tracing and shot targeting
721 Traces are blocked by bbox and exact bsp entityes, and also slide box entities
722 if the tryents flag is set.
723
724 tracebox (vector1, vector mins, vector maxs, vector2, tryents)
725 =================
726 */
727 // LordHavoc: added this for my own use, VERY useful, similar to traceline
728 void PF_tracebox (void)
729 {
730         float   *v1, *v2, *m1, *m2;
731         trace_t trace;
732         int             nomonsters;
733         edict_t *ent;
734
735         pr_xfunction->builtinsprofile += 30;
736
737         v1 = G_VECTOR(OFS_PARM0);
738         m1 = G_VECTOR(OFS_PARM1);
739         m2 = G_VECTOR(OFS_PARM2);
740         v2 = G_VECTOR(OFS_PARM3);
741         nomonsters = G_FLOAT(OFS_PARM4);
742         ent = G_EDICT(OFS_PARM5);
743
744         trace = SV_Move (v1, m1, m2, v2, nomonsters ? MOVE_NOMONSTERS : MOVE_NORMAL, ent);
745
746         pr_global_struct->trace_allsolid = trace.allsolid;
747         pr_global_struct->trace_startsolid = trace.startsolid;
748         pr_global_struct->trace_fraction = trace.fraction;
749         pr_global_struct->trace_inwater = trace.inwater;
750         pr_global_struct->trace_inopen = trace.inopen;
751         VectorCopy (trace.endpos, pr_global_struct->trace_endpos);
752         VectorCopy (trace.plane.normal, pr_global_struct->trace_plane_normal);
753         pr_global_struct->trace_plane_dist =  trace.plane.dist;
754         if (trace.ent)
755                 pr_global_struct->trace_ent = EDICT_TO_PROG(trace.ent);
756         else
757                 pr_global_struct->trace_ent = EDICT_TO_PROG(sv.edicts);
758 }
759
760 extern trace_t SV_Trace_Toss (edict_t *ent, edict_t *ignore);
761 void PF_TraceToss (void)
762 {
763         trace_t trace;
764         edict_t *ent;
765         edict_t *ignore;
766
767         pr_xfunction->builtinsprofile += 600;
768
769         ent = G_EDICT(OFS_PARM0);
770         ignore = G_EDICT(OFS_PARM1);
771
772         trace = SV_Trace_Toss (ent, ignore);
773
774         pr_global_struct->trace_allsolid = trace.allsolid;
775         pr_global_struct->trace_startsolid = trace.startsolid;
776         pr_global_struct->trace_fraction = trace.fraction;
777         pr_global_struct->trace_inwater = trace.inwater;
778         pr_global_struct->trace_inopen = trace.inopen;
779         VectorCopy (trace.endpos, pr_global_struct->trace_endpos);
780         VectorCopy (trace.plane.normal, pr_global_struct->trace_plane_normal);
781         pr_global_struct->trace_plane_dist =  trace.plane.dist;
782         if (trace.ent)
783                 pr_global_struct->trace_ent = EDICT_TO_PROG(trace.ent);
784         else
785                 pr_global_struct->trace_ent = EDICT_TO_PROG(sv.edicts);
786 }
787
788
789 /*
790 =================
791 PF_checkpos
792
793 Returns true if the given entity can move to the given position from it's
794 current position by walking or rolling.
795 FIXME: make work...
796 scalar checkpos (entity, vector)
797 =================
798 */
799 void PF_checkpos (void)
800 {
801 }
802
803 //============================================================================
804
805 qbyte checkpvs[MAX_MAP_LEAFS/8];
806
807 int PF_newcheckclient (int check)
808 {
809         int             i;
810         qbyte   *pvs;
811         edict_t *ent;
812         mleaf_t *leaf;
813         vec3_t  org;
814
815 // cycle to the next one
816
817         if (check < 1)
818                 check = 1;
819         if (check > svs.maxclients)
820                 check = svs.maxclients;
821
822         if (check == svs.maxclients)
823                 i = 1;
824         else
825                 i = check + 1;
826
827         for ( ;  ; i++)
828         {
829                 pr_xfunction->builtinsprofile++;
830                 if (i == svs.maxclients+1)
831                         i = 1;
832
833                 ent = EDICT_NUM(i);
834
835                 if (i == check)
836                         break;  // didn't find anything else
837
838                 if (ent->free)
839                         continue;
840                 if (ent->v->health <= 0)
841                         continue;
842                 if ((int)ent->v->flags & FL_NOTARGET)
843                         continue;
844
845         // anything that is a client, or has a client as an enemy
846                 break;
847         }
848
849 // get the PVS for the entity
850         VectorAdd (ent->v->origin, ent->v->view_ofs, org);
851         leaf = Mod_PointInLeaf (org, sv.worldmodel);
852         pvs = Mod_LeafPVS (leaf, sv.worldmodel);
853         memcpy (checkpvs, pvs, (sv.worldmodel->numleafs+7)>>3 );
854
855         return i;
856 }
857
858 /*
859 =================
860 PF_checkclient
861
862 Returns a client (or object that has a client enemy) that would be a
863 valid target.
864
865 If there is more than one valid option, they are cycled each frame
866
867 If (self.origin + self.viewofs) is not in the PVS of the current target,
868 it is not returned at all.
869
870 name checkclient ()
871 =================
872 */
873 int c_invis, c_notvis;
874 void PF_checkclient (void)
875 {
876         edict_t *ent, *self;
877         mleaf_t *leaf;
878         int             l;
879         vec3_t  view;
880
881         // find a new check if on a new frame
882         if (sv.time - sv.lastchecktime >= 0.1)
883         {
884                 sv.lastcheck = PF_newcheckclient (sv.lastcheck);
885                 sv.lastchecktime = sv.time;
886         }
887
888         // return check if it might be visible
889         ent = EDICT_NUM(sv.lastcheck);
890         if (ent->free || ent->v->health <= 0)
891         {
892                 RETURN_EDICT(sv.edicts);
893                 return;
894         }
895
896         // if current entity can't possibly see the check entity, return 0
897         self = PROG_TO_EDICT(pr_global_struct->self);
898         VectorAdd (self->v->origin, self->v->view_ofs, view);
899         leaf = Mod_PointInLeaf (view, sv.worldmodel);
900         if (leaf)
901         {
902                 l = (leaf - sv.worldmodel->leafs) - 1;
903                 if ( (l<0) || !(checkpvs[l>>3] & (1<<(l&7)) ) )
904                 {
905                         c_notvis++;
906                         RETURN_EDICT(sv.edicts);
907                         return;
908                 }
909         }
910
911         // might be able to see it
912         c_invis++;
913         RETURN_EDICT(ent);
914 }
915
916 //============================================================================
917
918
919 /*
920 =================
921 PF_stuffcmd
922
923 Sends text over to the client's execution buffer
924
925 stuffcmd (clientent, value)
926 =================
927 */
928 void PF_stuffcmd (void)
929 {
930         int             entnum;
931         char    *str;
932         client_t        *old;
933
934         entnum = G_EDICTNUM(OFS_PARM0);
935         if (entnum < 1 || entnum > svs.maxclients)
936                 Host_Error ("Parm 0 not a client");
937         str = G_STRING(OFS_PARM1);
938
939         old = host_client;
940         host_client = &svs.clients[entnum-1];
941         Host_ClientCommands ("%s", str);
942         host_client = old;
943 }
944
945 /*
946 =================
947 PF_localcmd
948
949 Sends text over to the client's execution buffer
950
951 localcmd (string)
952 =================
953 */
954 void PF_localcmd (void)
955 {
956         char    *str;
957
958         str = G_STRING(OFS_PARM0);
959         Cbuf_AddText (str);
960 }
961
962 /*
963 =================
964 PF_cvar
965
966 float cvar (string)
967 =================
968 */
969 void PF_cvar (void)
970 {
971         char    *str;
972
973         str = G_STRING(OFS_PARM0);
974
975         G_FLOAT(OFS_RETURN) = Cvar_VariableValue (str);
976 }
977
978 /*
979 =================
980 PF_cvar_set
981
982 float cvar (string)
983 =================
984 */
985 void PF_cvar_set (void)
986 {
987         char    *var, *val;
988
989         var = G_STRING(OFS_PARM0);
990         val = G_STRING(OFS_PARM1);
991
992         Cvar_Set (var, val);
993 }
994
995 /*
996 =================
997 PF_findradius
998
999 Returns a chain of entities that have origins within a spherical area
1000
1001 findradius (origin, radius)
1002 =================
1003 */
1004 void PF_findradius (void)
1005 {
1006         edict_t *ent, *chain;
1007         float radius;
1008         float radius2;
1009         float *org;
1010         float eorg[3];
1011         int i;
1012
1013         chain = (edict_t *)sv.edicts;
1014
1015         org = G_VECTOR(OFS_PARM0);
1016         radius = G_FLOAT(OFS_PARM1);
1017         radius2 = radius * radius;
1018
1019         ent = NEXT_EDICT(sv.edicts);
1020         for (i=1 ; i<sv.num_edicts ; i++, ent = NEXT_EDICT(ent))
1021         {
1022                 pr_xfunction->builtinsprofile++;
1023                 if (ent->free)
1024                         continue;
1025                 if (ent->v->solid == SOLID_NOT)
1026                         continue;
1027
1028                 // LordHavoc: compare against bounding box rather than center,
1029                 // and use DotProduct instead of Length, major speedup
1030                 eorg[0] = (org[0] - ent->v->origin[0]) - bound(ent->v->mins[0], (org[0] - ent->v->origin[0]), ent->v->maxs[0]);
1031                 eorg[1] = (org[1] - ent->v->origin[1]) - bound(ent->v->mins[1], (org[1] - ent->v->origin[1]), ent->v->maxs[1]);
1032                 eorg[2] = (org[2] - ent->v->origin[2]) - bound(ent->v->mins[2], (org[2] - ent->v->origin[2]), ent->v->maxs[2]);
1033                 if (DotProduct(eorg, eorg) > radius2)
1034                         continue;
1035
1036                 ent->v->chain = EDICT_TO_PROG(chain);
1037                 chain = ent;
1038         }
1039
1040         RETURN_EDICT(chain);
1041 }
1042
1043
1044 /*
1045 =========
1046 PF_dprint
1047 =========
1048 */
1049 void PF_dprint (void)
1050 {
1051         Con_DPrintf ("%s",PF_VarString(0));
1052 }
1053
1054 // LordHavoc: added this to semi-fix the problem of using many ftos calls in a print
1055 #define STRINGTEMP_BUFFERS 16
1056 #define STRINGTEMP_LENGTH 128
1057 static char pr_string_temp[STRINGTEMP_BUFFERS][STRINGTEMP_LENGTH];
1058 static int pr_string_tempindex = 0;
1059
1060 static char *PR_GetTempString(void)
1061 {
1062         char *s;
1063         s = pr_string_temp[pr_string_tempindex];
1064         pr_string_tempindex = (pr_string_tempindex + 1) % STRINGTEMP_BUFFERS;
1065         return s;
1066 }
1067
1068 void PF_ftos (void)
1069 {
1070         float v;
1071         char *s;
1072         v = G_FLOAT(OFS_PARM0);
1073
1074         s = PR_GetTempString();
1075         // LordHavoc: ftos improvement
1076         sprintf (s, "%g", v);
1077         G_INT(OFS_RETURN) = PR_SetString(s);
1078 }
1079
1080 void PF_fabs (void)
1081 {
1082         float   v;
1083         v = G_FLOAT(OFS_PARM0);
1084         G_FLOAT(OFS_RETURN) = fabs(v);
1085 }
1086
1087 void PF_vtos (void)
1088 {
1089         char *s;
1090         s = PR_GetTempString();
1091         sprintf (s, "'%5.1f %5.1f %5.1f'", G_VECTOR(OFS_PARM0)[0], G_VECTOR(OFS_PARM0)[1], G_VECTOR(OFS_PARM0)[2]);
1092         G_INT(OFS_RETURN) = PR_SetString(s);
1093 }
1094
1095 void PF_etos (void)
1096 {
1097         char *s;
1098         s = PR_GetTempString();
1099         sprintf (s, "entity %i", G_EDICTNUM(OFS_PARM0));
1100         G_INT(OFS_RETURN) = PR_SetString(s);
1101 }
1102
1103 void PF_Spawn (void)
1104 {
1105         edict_t *ed;
1106         pr_xfunction->builtinsprofile += 20;
1107         ed = ED_Alloc();
1108         RETURN_EDICT(ed);
1109 }
1110
1111 void PF_Remove (void)
1112 {
1113         edict_t *ed;
1114         pr_xfunction->builtinsprofile += 20;
1115
1116         ed = G_EDICT(OFS_PARM0);
1117         if (ed == sv.edicts)
1118                 Host_Error("remove: tried to remove world\n");
1119         if (NUM_FOR_EDICT(ed) <= svs.maxclients)
1120                 Host_Error("remove: tried to remove a client\n");
1121         ED_Free (ed);
1122 }
1123
1124
1125 // entity (entity start, .string field, string match) find = #5;
1126 void PF_Find (void)
1127 {
1128         int             e;
1129         int             f;
1130         char    *s, *t;
1131         edict_t *ed;
1132
1133         e = G_EDICTNUM(OFS_PARM0);
1134         f = G_INT(OFS_PARM1);
1135         s = G_STRING(OFS_PARM2);
1136         if (!s || !s[0])
1137         {
1138                 RETURN_EDICT(sv.edicts);
1139                 return;
1140         }
1141
1142         for (e++ ; e < sv.num_edicts ; e++)
1143         {
1144                 pr_xfunction->builtinsprofile++;
1145                 ed = EDICT_NUM(e);
1146                 if (ed->free)
1147                         continue;
1148                 t = E_STRING(ed,f);
1149                 if (!t)
1150                         continue;
1151                 if (!strcmp(t,s))
1152                 {
1153                         RETURN_EDICT(ed);
1154                         return;
1155                 }
1156         }
1157
1158         RETURN_EDICT(sv.edicts);
1159 }
1160
1161 // LordHavoc: added this for searching float, int, and entity reference fields
1162 void PF_FindFloat (void)
1163 {
1164         int             e;
1165         int             f;
1166         float   s;
1167         edict_t *ed;
1168
1169         e = G_EDICTNUM(OFS_PARM0);
1170         f = G_INT(OFS_PARM1);
1171         s = G_FLOAT(OFS_PARM2);
1172
1173         for (e++ ; e < sv.num_edicts ; e++)
1174         {
1175                 pr_xfunction->builtinsprofile++;
1176                 ed = EDICT_NUM(e);
1177                 if (ed->free)
1178                         continue;
1179                 if (E_FLOAT(ed,f) == s)
1180                 {
1181                         RETURN_EDICT(ed);
1182                         return;
1183                 }
1184         }
1185
1186         RETURN_EDICT(sv.edicts);
1187 }
1188
1189 // chained search for strings in entity fields
1190 // entity(.string field, string match) findchain = #402;
1191 void PF_findchain (void)
1192 {
1193         int             i;
1194         int             f;
1195         char    *s, *t;
1196         edict_t *ent, *chain;
1197
1198         chain = (edict_t *)sv.edicts;
1199
1200         f = G_INT(OFS_PARM0);
1201         s = G_STRING(OFS_PARM1);
1202         if (!s || !s[0])
1203         {
1204                 RETURN_EDICT(sv.edicts);
1205                 return;
1206         }
1207
1208         ent = NEXT_EDICT(sv.edicts);
1209         for (i = 1;i < sv.num_edicts;i++, ent = NEXT_EDICT(ent))
1210         {
1211                 pr_xfunction->builtinsprofile++;
1212                 if (ent->free)
1213                         continue;
1214                 t = E_STRING(ent,f);
1215                 if (!t)
1216                         continue;
1217                 if (strcmp(t,s))
1218                         continue;
1219
1220                 ent->v->chain = EDICT_TO_PROG(chain);
1221                 chain = ent;
1222         }
1223
1224         RETURN_EDICT(chain);
1225 }
1226
1227 // LordHavoc: chained search for float, int, and entity reference fields
1228 // entity(.string field, float match) findchainfloat = #403;
1229 void PF_findchainfloat (void)
1230 {
1231         int             i;
1232         int             f;
1233         float   s;
1234         edict_t *ent, *chain;
1235
1236         chain = (edict_t *)sv.edicts;
1237
1238         f = G_INT(OFS_PARM0);
1239         s = G_FLOAT(OFS_PARM1);
1240
1241         ent = NEXT_EDICT(sv.edicts);
1242         for (i = 1;i < sv.num_edicts;i++, ent = NEXT_EDICT(ent))
1243         {
1244                 pr_xfunction->builtinsprofile++;
1245                 if (ent->free)
1246                         continue;
1247                 if (E_FLOAT(ent,f) != s)
1248                         continue;
1249
1250                 ent->v->chain = EDICT_TO_PROG(chain);
1251                 chain = ent;
1252         }
1253
1254         RETURN_EDICT(chain);
1255 }
1256
1257 void PR_CheckEmptyString (char *s)
1258 {
1259         if (s[0] <= ' ')
1260                 Host_Error ("Bad string");
1261 }
1262
1263 void PF_precache_file (void)
1264 {       // precache_file is only used to copy files with qcc, it does nothing
1265         G_INT(OFS_RETURN) = G_INT(OFS_PARM0);
1266 }
1267
1268 void PF_precache_sound (void)
1269 {
1270         char    *s;
1271         int             i;
1272
1273         if (sv.state != ss_loading)
1274                 Host_Error ("PF_Precache_*: Precache can only be done in spawn functions");
1275
1276         s = G_STRING(OFS_PARM0);
1277         G_INT(OFS_RETURN) = G_INT(OFS_PARM0);
1278         PR_CheckEmptyString (s);
1279
1280         for (i=0 ; i<MAX_SOUNDS ; i++)
1281         {
1282                 if (!sv.sound_precache[i])
1283                 {
1284                         sv.sound_precache[i] = s;
1285                         return;
1286                 }
1287                 if (!strcmp(sv.sound_precache[i], s))
1288                         return;
1289         }
1290         Host_Error ("PF_precache_sound: overflow");
1291 }
1292
1293 void PF_precache_model (void)
1294 {
1295         char    *s;
1296         int             i;
1297
1298         if (sv.state != ss_loading)
1299                 Host_Error ("PF_Precache_*: Precache can only be done in spawn functions");
1300
1301         s = G_STRING(OFS_PARM0);
1302         if (sv.worldmodel->ishlbsp && ((!s) || (!s[0])))
1303                 return;
1304         G_INT(OFS_RETURN) = G_INT(OFS_PARM0);
1305         PR_CheckEmptyString (s);
1306
1307         for (i=0 ; i<MAX_MODELS ; i++)
1308         {
1309                 if (!sv.model_precache[i])
1310                 {
1311                         sv.model_precache[i] = s;
1312                         sv.models[i] = Mod_ForName (s, true, false, false);
1313                         return;
1314                 }
1315                 if (!strcmp(sv.model_precache[i], s))
1316                         return;
1317         }
1318         Host_Error ("PF_precache_model: overflow");
1319 }
1320
1321
1322 void PF_coredump (void)
1323 {
1324         ED_PrintEdicts ();
1325 }
1326
1327 void PF_traceon (void)
1328 {
1329         pr_trace = true;
1330 }
1331
1332 void PF_traceoff (void)
1333 {
1334         pr_trace = false;
1335 }
1336
1337 void PF_eprint (void)
1338 {
1339         ED_PrintNum (G_EDICTNUM(OFS_PARM0));
1340 }
1341
1342 /*
1343 ===============
1344 PF_walkmove
1345
1346 float(float yaw, float dist) walkmove
1347 ===============
1348 */
1349 void PF_walkmove (void)
1350 {
1351         edict_t *ent;
1352         float   yaw, dist;
1353         vec3_t  move;
1354         mfunction_t     *oldf;
1355         int     oldself;
1356
1357         ent = PROG_TO_EDICT(pr_global_struct->self);
1358         yaw = G_FLOAT(OFS_PARM0);
1359         dist = G_FLOAT(OFS_PARM1);
1360
1361         if ( !( (int)ent->v->flags & (FL_ONGROUND|FL_FLY|FL_SWIM) ) )
1362         {
1363                 G_FLOAT(OFS_RETURN) = 0;
1364                 return;
1365         }
1366
1367         yaw = yaw*M_PI*2 / 360;
1368
1369         move[0] = cos(yaw)*dist;
1370         move[1] = sin(yaw)*dist;
1371         move[2] = 0;
1372
1373 // save program state, because SV_movestep may call other progs
1374         oldf = pr_xfunction;
1375         oldself = pr_global_struct->self;
1376
1377         G_FLOAT(OFS_RETURN) = SV_movestep(ent, move, true);
1378
1379
1380 // restore program state
1381         pr_xfunction = oldf;
1382         pr_global_struct->self = oldself;
1383 }
1384
1385 /*
1386 ===============
1387 PF_droptofloor
1388
1389 void() droptofloor
1390 ===============
1391 */
1392 void PF_droptofloor (void)
1393 {
1394         edict_t         *ent;
1395         vec3_t          end;
1396         trace_t         trace;
1397
1398         ent = PROG_TO_EDICT(pr_global_struct->self);
1399
1400         VectorCopy (ent->v->origin, end);
1401         end[2] -= 256;
1402
1403         trace = SV_Move (ent->v->origin, ent->v->mins, ent->v->maxs, end, MOVE_NORMAL, ent);
1404
1405         if (trace.fraction == 1)
1406                 G_FLOAT(OFS_RETURN) = 0;
1407         else
1408         {
1409                 VectorCopy (trace.endpos, ent->v->origin);
1410                 SV_LinkEdict (ent, false);
1411                 ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
1412                 ent->v->groundentity = EDICT_TO_PROG(trace.ent);
1413                 G_FLOAT(OFS_RETURN) = 1;
1414                 // if support is destroyed, keep suspended (gross hack for floating items in various maps)
1415                 ent->suspendedinairflag = true;
1416         }
1417 }
1418
1419 /*
1420 ===============
1421 PF_lightstyle
1422
1423 void(float style, string value) lightstyle
1424 ===============
1425 */
1426 void PF_lightstyle (void)
1427 {
1428         int             style;
1429         char    *val;
1430         client_t        *client;
1431         int                     j;
1432
1433         style = G_FLOAT(OFS_PARM0);
1434         val = G_STRING(OFS_PARM1);
1435
1436 // change the string in sv
1437         sv.lightstyles[style] = val;
1438
1439 // send message to all clients on this server
1440         if (sv.state != ss_active)
1441                 return;
1442
1443         for (j=0, client = svs.clients ; j<svs.maxclients ; j++, client++)
1444                 if (client->active || client->spawned)
1445                 {
1446                         MSG_WriteChar (&client->message, svc_lightstyle);
1447                         MSG_WriteChar (&client->message,style);
1448                         MSG_WriteString (&client->message, val);
1449                 }
1450 }
1451
1452 void PF_rint (void)
1453 {
1454         float   f;
1455         f = G_FLOAT(OFS_PARM0);
1456         if (f > 0)
1457                 G_FLOAT(OFS_RETURN) = (int)(f + 0.5);
1458         else
1459                 G_FLOAT(OFS_RETURN) = (int)(f - 0.5);
1460 }
1461 void PF_floor (void)
1462 {
1463         G_FLOAT(OFS_RETURN) = floor(G_FLOAT(OFS_PARM0));
1464 }
1465 void PF_ceil (void)
1466 {
1467         G_FLOAT(OFS_RETURN) = ceil(G_FLOAT(OFS_PARM0));
1468 }
1469
1470
1471 /*
1472 =============
1473 PF_checkbottom
1474 =============
1475 */
1476 void PF_checkbottom (void)
1477 {
1478         G_FLOAT(OFS_RETURN) = SV_CheckBottom (G_EDICT(OFS_PARM0));
1479 }
1480
1481 /*
1482 =============
1483 PF_pointcontents
1484 =============
1485 */
1486 void PF_pointcontents (void)
1487 {
1488         G_FLOAT(OFS_RETURN) = Mod_PointContents(G_VECTOR(OFS_PARM0), sv.worldmodel);
1489 }
1490
1491 /*
1492 =============
1493 PF_nextent
1494
1495 entity nextent(entity)
1496 =============
1497 */
1498 void PF_nextent (void)
1499 {
1500         int             i;
1501         edict_t *ent;
1502
1503         i = G_EDICTNUM(OFS_PARM0);
1504         while (1)
1505         {
1506                 pr_xfunction->builtinsprofile++;
1507                 i++;
1508                 if (i == sv.num_edicts)
1509                 {
1510                         RETURN_EDICT(sv.edicts);
1511                         return;
1512                 }
1513                 ent = EDICT_NUM(i);
1514                 if (!ent->free)
1515                 {
1516                         RETURN_EDICT(ent);
1517                         return;
1518                 }
1519         }
1520 }
1521
1522 /*
1523 =============
1524 PF_aim
1525
1526 Pick a vector for the player to shoot along
1527 vector aim(entity, missilespeed)
1528 =============
1529 */
1530 void PF_aim (void)
1531 {
1532         edict_t *ent, *check, *bestent;
1533         vec3_t  start, dir, end, bestdir;
1534         int             i, j;
1535         trace_t tr;
1536         float   dist, bestdist;
1537         float   speed;
1538
1539         ent = G_EDICT(OFS_PARM0);
1540         speed = G_FLOAT(OFS_PARM1);
1541
1542         VectorCopy (ent->v->origin, start);
1543         start[2] += 20;
1544
1545 // try sending a trace straight
1546         VectorCopy (pr_global_struct->v_forward, dir);
1547         VectorMA (start, 2048, dir, end);
1548         tr = SV_Move (start, vec3_origin, vec3_origin, end, MOVE_NORMAL, ent);
1549         if (tr.ent && ((edict_t *)tr.ent)->v->takedamage == DAMAGE_AIM
1550         && (!teamplay.integer || ent->v->team <=0 || ent->v->team != ((edict_t *)tr.ent)->v->team) )
1551         {
1552                 VectorCopy (pr_global_struct->v_forward, G_VECTOR(OFS_RETURN));
1553                 return;
1554         }
1555
1556
1557 // try all possible entities
1558         VectorCopy (dir, bestdir);
1559         bestdist = sv_aim.value;
1560         bestent = NULL;
1561
1562         check = NEXT_EDICT(sv.edicts);
1563         for (i=1 ; i<sv.num_edicts ; i++, check = NEXT_EDICT(check) )
1564         {
1565                 pr_xfunction->builtinsprofile++;
1566                 if (check->v->takedamage != DAMAGE_AIM)
1567                         continue;
1568                 if (check == ent)
1569                         continue;
1570                 if (teamplay.integer && ent->v->team > 0 && ent->v->team == check->v->team)
1571                         continue;       // don't aim at teammate
1572                 for (j=0 ; j<3 ; j++)
1573                         end[j] = check->v->origin[j]
1574                         + 0.5*(check->v->mins[j] + check->v->maxs[j]);
1575                 VectorSubtract (end, start, dir);
1576                 VectorNormalize (dir);
1577                 dist = DotProduct (dir, pr_global_struct->v_forward);
1578                 if (dist < bestdist)
1579                         continue;       // to far to turn
1580                 tr = SV_Move (start, vec3_origin, vec3_origin, end, MOVE_NORMAL, ent);
1581                 if (tr.ent == check)
1582                 {       // can shoot at this one
1583                         bestdist = dist;
1584                         bestent = check;
1585                 }
1586         }
1587
1588         if (bestent)
1589         {
1590                 VectorSubtract (bestent->v->origin, ent->v->origin, dir);
1591                 dist = DotProduct (dir, pr_global_struct->v_forward);
1592                 VectorScale (pr_global_struct->v_forward, dist, end);
1593                 end[2] = dir[2];
1594                 VectorNormalize (end);
1595                 VectorCopy (end, G_VECTOR(OFS_RETURN));
1596         }
1597         else
1598         {
1599                 VectorCopy (bestdir, G_VECTOR(OFS_RETURN));
1600         }
1601 }
1602
1603 /*
1604 ==============
1605 PF_changeyaw
1606
1607 This was a major timewaster in progs, so it was converted to C
1608 ==============
1609 */
1610 void PF_changeyaw (void)
1611 {
1612         edict_t         *ent;
1613         float           ideal, current, move, speed;
1614
1615         ent = PROG_TO_EDICT(pr_global_struct->self);
1616         current = ANGLEMOD(ent->v->angles[1]);
1617         ideal = ent->v->ideal_yaw;
1618         speed = ent->v->yaw_speed;
1619
1620         if (current == ideal)
1621                 return;
1622         move = ideal - current;
1623         if (ideal > current)
1624         {
1625                 if (move >= 180)
1626                         move = move - 360;
1627         }
1628         else
1629         {
1630                 if (move <= -180)
1631                         move = move + 360;
1632         }
1633         if (move > 0)
1634         {
1635                 if (move > speed)
1636                         move = speed;
1637         }
1638         else
1639         {
1640                 if (move < -speed)
1641                         move = -speed;
1642         }
1643
1644         ent->v->angles[1] = ANGLEMOD (current + move);
1645 }
1646
1647 /*
1648 ==============
1649 PF_changepitch
1650 ==============
1651 */
1652 void PF_changepitch (void)
1653 {
1654         edict_t         *ent;
1655         float           ideal, current, move, speed;
1656         eval_t          *val;
1657
1658         ent = G_EDICT(OFS_PARM0);
1659         current = ANGLEMOD( ent->v->angles[0] );
1660         if ((val = GETEDICTFIELDVALUE(ent, eval_idealpitch)))
1661                 ideal = val->_float;
1662         else
1663         {
1664                 Host_Error ("PF_changepitch: .float idealpitch and .float pitch_speed must be defined to use changepitch");
1665                 return;
1666         }
1667         if ((val = GETEDICTFIELDVALUE(ent, eval_pitch_speed)))
1668                 speed = val->_float;
1669         else
1670         {
1671                 Host_Error ("PF_changepitch: .float idealpitch and .float pitch_speed must be defined to use changepitch");
1672                 return;
1673         }
1674
1675         if (current == ideal)
1676                 return;
1677         move = ideal - current;
1678         if (ideal > current)
1679         {
1680                 if (move >= 180)
1681                         move = move - 360;
1682         }
1683         else
1684         {
1685                 if (move <= -180)
1686                         move = move + 360;
1687         }
1688         if (move > 0)
1689         {
1690                 if (move > speed)
1691                         move = speed;
1692         }
1693         else
1694         {
1695                 if (move < -speed)
1696                         move = -speed;
1697         }
1698
1699         ent->v->angles[0] = ANGLEMOD (current + move);
1700 }
1701
1702 /*
1703 ===============================================================================
1704
1705 MESSAGE WRITING
1706
1707 ===============================================================================
1708 */
1709
1710 #define MSG_BROADCAST   0               // unreliable to all
1711 #define MSG_ONE                 1               // reliable to one (msg_entity)
1712 #define MSG_ALL                 2               // reliable to all
1713 #define MSG_INIT                3               // write to the init string
1714
1715 sizebuf_t *WriteDest (void)
1716 {
1717         int             entnum;
1718         int             dest;
1719         edict_t *ent;
1720
1721         dest = G_FLOAT(OFS_PARM0);
1722         switch (dest)
1723         {
1724         case MSG_BROADCAST:
1725                 return &sv.datagram;
1726
1727         case MSG_ONE:
1728                 ent = PROG_TO_EDICT(pr_global_struct->msg_entity);
1729                 entnum = NUM_FOR_EDICT(ent);
1730                 if (entnum < 1 || entnum > svs.maxclients)
1731                         Host_Error ("WriteDest: not a client");
1732                 return &svs.clients[entnum-1].message;
1733
1734         case MSG_ALL:
1735                 return &sv.reliable_datagram;
1736
1737         case MSG_INIT:
1738                 return &sv.signon;
1739
1740         default:
1741                 Host_Error ("WriteDest: bad destination");
1742                 break;
1743         }
1744
1745         return NULL;
1746 }
1747
1748 void PF_WriteByte (void)
1749 {
1750         MSG_WriteByte (WriteDest(), G_FLOAT(OFS_PARM1));
1751 }
1752
1753 void PF_WriteChar (void)
1754 {
1755         MSG_WriteChar (WriteDest(), G_FLOAT(OFS_PARM1));
1756 }
1757
1758 void PF_WriteShort (void)
1759 {
1760         MSG_WriteShort (WriteDest(), G_FLOAT(OFS_PARM1));
1761 }
1762
1763 void PF_WriteLong (void)
1764 {
1765         MSG_WriteLong (WriteDest(), G_FLOAT(OFS_PARM1));
1766 }
1767
1768 void PF_WriteAngle (void)
1769 {
1770         MSG_WriteAngle (WriteDest(), G_FLOAT(OFS_PARM1));
1771 }
1772
1773 void PF_WriteCoord (void)
1774 {
1775         MSG_WriteDPCoord (WriteDest(), G_FLOAT(OFS_PARM1));
1776 }
1777
1778 void PF_WriteString (void)
1779 {
1780         MSG_WriteString (WriteDest(), G_STRING(OFS_PARM1));
1781 }
1782
1783
1784 void PF_WriteEntity (void)
1785 {
1786         MSG_WriteShort (WriteDest(), G_EDICTNUM(OFS_PARM1));
1787 }
1788
1789 //=============================================================================
1790
1791 void PF_makestatic (void)
1792 {
1793         edict_t *ent;
1794         int i, large;
1795
1796         ent = G_EDICT(OFS_PARM0);
1797
1798         large = false;
1799         if (ent->v->modelindex >= 256 || ent->v->frame >= 256)
1800                 large = true;
1801
1802         if (large)
1803         {
1804                 MSG_WriteByte (&sv.signon,svc_spawnstatic2);
1805                 MSG_WriteShort (&sv.signon, ent->v->modelindex);
1806                 MSG_WriteShort (&sv.signon, ent->v->frame);
1807         }
1808         else
1809         {
1810                 MSG_WriteByte (&sv.signon,svc_spawnstatic);
1811                 MSG_WriteByte (&sv.signon, ent->v->modelindex);
1812                 MSG_WriteByte (&sv.signon, ent->v->frame);
1813         }
1814
1815         MSG_WriteByte (&sv.signon, ent->v->colormap);
1816         MSG_WriteByte (&sv.signon, ent->v->skin);
1817         for (i=0 ; i<3 ; i++)
1818         {
1819                 MSG_WriteDPCoord(&sv.signon, ent->v->origin[i]);
1820                 MSG_WriteAngle(&sv.signon, ent->v->angles[i]);
1821         }
1822
1823 // throw the entity away now
1824         ED_Free (ent);
1825 }
1826
1827 //=============================================================================
1828
1829 /*
1830 ==============
1831 PF_setspawnparms
1832 ==============
1833 */
1834 void PF_setspawnparms (void)
1835 {
1836         edict_t *ent;
1837         int             i;
1838         client_t        *client;
1839
1840         ent = G_EDICT(OFS_PARM0);
1841         i = NUM_FOR_EDICT(ent);
1842         if (i < 1 || i > svs.maxclients)
1843                 Host_Error ("Entity is not a client");
1844
1845         // copy spawn parms out of the client_t
1846         client = svs.clients + (i-1);
1847
1848         for (i=0 ; i< NUM_SPAWN_PARMS ; i++)
1849                 (&pr_global_struct->parm1)[i] = client->spawn_parms[i];
1850 }
1851
1852 /*
1853 ==============
1854 PF_changelevel
1855 ==============
1856 */
1857 void PF_changelevel (void)
1858 {
1859         char    *s;
1860
1861 // make sure we don't issue two changelevels
1862         if (svs.changelevel_issued)
1863                 return;
1864         svs.changelevel_issued = true;
1865
1866         s = G_STRING(OFS_PARM0);
1867         Cbuf_AddText (va("changelevel %s\n",s));
1868 }
1869
1870 void PF_sin (void)
1871 {
1872         G_FLOAT(OFS_RETURN) = sin(G_FLOAT(OFS_PARM0));
1873 }
1874
1875 void PF_cos (void)
1876 {
1877         G_FLOAT(OFS_RETURN) = cos(G_FLOAT(OFS_PARM0));
1878 }
1879
1880 void PF_sqrt (void)
1881 {
1882         G_FLOAT(OFS_RETURN) = sqrt(G_FLOAT(OFS_PARM0));
1883 }
1884
1885 /*
1886 =================
1887 PF_RandomVec
1888
1889 Returns a vector of length < 1
1890
1891 randomvec()
1892 =================
1893 */
1894 void PF_randomvec (void)
1895 {
1896         vec3_t          temp;
1897         do
1898         {
1899                 temp[0] = (rand()&32767) * (2.0 / 32767.0) - 1.0;
1900                 temp[1] = (rand()&32767) * (2.0 / 32767.0) - 1.0;
1901                 temp[2] = (rand()&32767) * (2.0 / 32767.0) - 1.0;
1902         }
1903         while (DotProduct(temp, temp) >= 1);
1904         VectorCopy (temp, G_VECTOR(OFS_RETURN));
1905 }
1906
1907 void SV_LightPoint (vec3_t color, vec3_t p);
1908 /*
1909 =================
1910 PF_GetLight
1911
1912 Returns a color vector indicating the lighting at the requested point.
1913
1914 (Internal Operation note: actually measures the light beneath the point, just like
1915                           the model lighting on the client)
1916
1917 getlight(vector)
1918 =================
1919 */
1920 void PF_GetLight (void)
1921 {
1922         vec3_t          color;
1923         vec_t*          p;
1924         p = G_VECTOR(OFS_PARM0);
1925         SV_LightPoint (color, p);
1926         VectorCopy (color, G_VECTOR(OFS_RETURN));
1927 }
1928
1929 #define MAX_QC_CVARS 128
1930 cvar_t qc_cvar[MAX_QC_CVARS];
1931 int currentqc_cvar;
1932
1933 void PF_registercvar (void)
1934 {
1935         char *name, *value;
1936         cvar_t *variable;
1937         name = G_STRING(OFS_PARM0);
1938         value = G_STRING(OFS_PARM1);
1939         G_FLOAT(OFS_RETURN) = 0;
1940 // first check to see if it has already been defined
1941         if (Cvar_FindVar (name))
1942                 return;
1943
1944 // check for overlap with a command
1945         if (Cmd_Exists (name))
1946         {
1947                 Con_Printf ("PF_registercvar: %s is a command\n", name);
1948                 return;
1949         }
1950
1951         if (currentqc_cvar >= MAX_QC_CVARS)
1952                 Host_Error ("PF_registercvar: ran out of cvar slots (%i)\n", MAX_QC_CVARS);
1953
1954 // copy the name and value
1955         variable = &qc_cvar[currentqc_cvar++];
1956         variable->name = Z_Malloc (strlen(name)+1);
1957         strcpy (variable->name, name);
1958         variable->string = Z_Malloc (strlen(value)+1);
1959         strcpy (variable->string, value);
1960         variable->value = atof (value);
1961
1962         Cvar_RegisterVariable(variable);
1963         G_FLOAT(OFS_RETURN) = 1; // success
1964 }
1965
1966 /*
1967 =================
1968 PF_min
1969
1970 returns the minimum of two supplied floats
1971
1972 min(a, b)
1973 =================
1974 */
1975 void PF_min (void)
1976 {
1977         // LordHavoc: 3+ argument enhancement suggested by FrikaC
1978         if (pr_argc == 2)
1979                 G_FLOAT(OFS_RETURN) = min(G_FLOAT(OFS_PARM0), G_FLOAT(OFS_PARM1));
1980         else if (pr_argc >= 3)
1981         {
1982                 int i;
1983                 float f = G_FLOAT(OFS_PARM0);
1984                 for (i = 1;i < pr_argc;i++)
1985                         if (G_FLOAT((OFS_PARM0+i*3)) < f)
1986                                 f = G_FLOAT((OFS_PARM0+i*3));
1987                 G_FLOAT(OFS_RETURN) = f;
1988         }
1989         else
1990                 Host_Error("min: must supply at least 2 floats\n");
1991 }
1992
1993 /*
1994 =================
1995 PF_max
1996
1997 returns the maximum of two supplied floats
1998
1999 max(a, b)
2000 =================
2001 */
2002 void PF_max (void)
2003 {
2004         // LordHavoc: 3+ argument enhancement suggested by FrikaC
2005         if (pr_argc == 2)
2006                 G_FLOAT(OFS_RETURN) = max(G_FLOAT(OFS_PARM0), G_FLOAT(OFS_PARM1));
2007         else if (pr_argc >= 3)
2008         {
2009                 int i;
2010                 float f = G_FLOAT(OFS_PARM0);
2011                 for (i = 1;i < pr_argc;i++)
2012                         if (G_FLOAT((OFS_PARM0+i*3)) > f)
2013                                 f = G_FLOAT((OFS_PARM0+i*3));
2014                 G_FLOAT(OFS_RETURN) = f;
2015         }
2016         else
2017                 Host_Error("max: must supply at least 2 floats\n");
2018 }
2019
2020 /*
2021 =================
2022 PF_bound
2023
2024 returns number bounded by supplied range
2025
2026 min(min, value, max)
2027 =================
2028 */
2029 void PF_bound (void)
2030 {
2031         G_FLOAT(OFS_RETURN) = bound(G_FLOAT(OFS_PARM0), G_FLOAT(OFS_PARM1), G_FLOAT(OFS_PARM2));
2032 }
2033
2034 /*
2035 =================
2036 PF_pow
2037
2038 returns a raised to power b
2039
2040 pow(a, b)
2041 =================
2042 */
2043 void PF_pow (void)
2044 {
2045         G_FLOAT(OFS_RETURN) = pow(G_FLOAT(OFS_PARM0), G_FLOAT(OFS_PARM1));
2046 }
2047
2048 /*
2049 =================
2050 PF_copyentity
2051
2052 copies data from one entity to another
2053
2054 copyentity(src, dst)
2055 =================
2056 */
2057 void PF_copyentity (void)
2058 {
2059         edict_t *in, *out;
2060         in = G_EDICT(OFS_PARM0);
2061         out = G_EDICT(OFS_PARM1);
2062         memcpy(out->v, in->v, progs->entityfields * 4);
2063 }
2064
2065 /*
2066 =================
2067 PF_setcolors
2068
2069 sets the color of a client and broadcasts the update to all connected clients
2070
2071 setcolors(clientent, value)
2072 =================
2073 */
2074 void PF_setcolors (void)
2075 {
2076         client_t        *client;
2077         int                     entnum, i;
2078
2079         entnum = G_EDICTNUM(OFS_PARM0);
2080         i = G_FLOAT(OFS_PARM1);
2081
2082         if (entnum < 1 || entnum > svs.maxclients)
2083         {
2084                 Con_Printf ("tried to setcolor a non-client\n");
2085                 return;
2086         }
2087
2088         client = &svs.clients[entnum-1];
2089         client->colors = i;
2090         client->edict->v->team = (i & 15) + 1;
2091
2092         MSG_WriteByte (&sv.reliable_datagram, svc_updatecolors);
2093         MSG_WriteByte (&sv.reliable_datagram, entnum - 1);
2094         MSG_WriteByte (&sv.reliable_datagram, i);
2095 }
2096
2097 /*
2098 =================
2099 PF_effect
2100
2101 effect(origin, modelname, startframe, framecount, framerate)
2102 =================
2103 */
2104 void PF_effect (void)
2105 {
2106         char *s;
2107         s = G_STRING(OFS_PARM1);
2108         if (!s || !s[0])
2109                 Host_Error("effect: no model specified\n");
2110
2111         SV_StartEffect(G_VECTOR(OFS_PARM0), SV_ModelIndex(s), G_FLOAT(OFS_PARM2), G_FLOAT(OFS_PARM3), G_FLOAT(OFS_PARM4));
2112 }
2113
2114 void PF_te_blood (void)
2115 {
2116         if (G_FLOAT(OFS_PARM2) < 1)
2117                 return;
2118         MSG_WriteByte(&sv.datagram, svc_temp_entity);
2119         MSG_WriteByte(&sv.datagram, TE_BLOOD);
2120         // origin
2121         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[0]);
2122         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[1]);
2123         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[2]);
2124         // velocity
2125         MSG_WriteByte(&sv.datagram, bound(-128, (int) G_VECTOR(OFS_PARM1)[0], 127));
2126         MSG_WriteByte(&sv.datagram, bound(-128, (int) G_VECTOR(OFS_PARM1)[1], 127));
2127         MSG_WriteByte(&sv.datagram, bound(-128, (int) G_VECTOR(OFS_PARM1)[2], 127));
2128         // count
2129         MSG_WriteByte(&sv.datagram, bound(0, (int) G_FLOAT(OFS_PARM2), 255));
2130 }
2131
2132 void PF_te_bloodshower (void)
2133 {
2134         if (G_FLOAT(OFS_PARM3) < 1)
2135                 return;
2136         MSG_WriteByte(&sv.datagram, svc_temp_entity);
2137         MSG_WriteByte(&sv.datagram, TE_BLOODSHOWER);
2138         // min
2139         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[0]);
2140         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[1]);
2141         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[2]);
2142         // max
2143         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM1)[0]);
2144         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM1)[1]);
2145         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM1)[2]);
2146         // speed
2147         MSG_WriteDPCoord(&sv.datagram, G_FLOAT(OFS_PARM2));
2148         // count
2149         MSG_WriteShort(&sv.datagram, bound(0, G_FLOAT(OFS_PARM3), 65535));
2150 }
2151
2152 void PF_te_explosionrgb (void)
2153 {
2154         MSG_WriteByte(&sv.datagram, svc_temp_entity);
2155         MSG_WriteByte(&sv.datagram, TE_EXPLOSIONRGB);
2156         // origin
2157         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[0]);
2158         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[1]);
2159         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[2]);
2160         // color
2161         MSG_WriteByte(&sv.datagram, bound(0, (int) (G_VECTOR(OFS_PARM1)[0] * 255), 255));
2162         MSG_WriteByte(&sv.datagram, bound(0, (int) (G_VECTOR(OFS_PARM1)[1] * 255), 255));
2163         MSG_WriteByte(&sv.datagram, bound(0, (int) (G_VECTOR(OFS_PARM1)[2] * 255), 255));
2164 }
2165
2166 void PF_te_particlecube (void)
2167 {
2168         if (G_FLOAT(OFS_PARM3) < 1)
2169                 return;
2170         MSG_WriteByte(&sv.datagram, svc_temp_entity);
2171         MSG_WriteByte(&sv.datagram, TE_PARTICLECUBE);
2172         // min
2173         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[0]);
2174         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[1]);
2175         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[2]);
2176         // max
2177         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM1)[0]);
2178         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM1)[1]);
2179         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM1)[2]);
2180         // velocity
2181         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM2)[0]);
2182         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM2)[1]);
2183         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM2)[2]);
2184         // count
2185         MSG_WriteShort(&sv.datagram, bound(0, G_FLOAT(OFS_PARM3), 65535));
2186         // color
2187         MSG_WriteByte(&sv.datagram, G_FLOAT(OFS_PARM4));
2188         // gravity true/false
2189         MSG_WriteByte(&sv.datagram, ((int) G_FLOAT(OFS_PARM5)) != 0);
2190         // randomvel
2191         MSG_WriteDPCoord(&sv.datagram, G_FLOAT(OFS_PARM6));
2192 }
2193
2194 void PF_te_particlerain (void)
2195 {
2196         if (G_FLOAT(OFS_PARM3) < 1)
2197                 return;
2198         MSG_WriteByte(&sv.datagram, svc_temp_entity);
2199         MSG_WriteByte(&sv.datagram, TE_PARTICLERAIN);
2200         // min
2201         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[0]);
2202         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[1]);
2203         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[2]);
2204         // max
2205         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM1)[0]);
2206         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM1)[1]);
2207         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM1)[2]);
2208         // velocity
2209         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM2)[0]);
2210         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM2)[1]);
2211         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM2)[2]);
2212         // count
2213         MSG_WriteShort(&sv.datagram, bound(0, G_FLOAT(OFS_PARM3), 65535));
2214         // color
2215         MSG_WriteByte(&sv.datagram, G_FLOAT(OFS_PARM4));
2216 }
2217
2218 void PF_te_particlesnow (void)
2219 {
2220         if (G_FLOAT(OFS_PARM3) < 1)
2221                 return;
2222         MSG_WriteByte(&sv.datagram, svc_temp_entity);
2223         MSG_WriteByte(&sv.datagram, TE_PARTICLESNOW);
2224         // min
2225         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[0]);
2226         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[1]);
2227         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[2]);
2228         // max
2229         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM1)[0]);
2230         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM1)[1]);
2231         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM1)[2]);
2232         // velocity
2233         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM2)[0]);
2234         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM2)[1]);
2235         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM2)[2]);
2236         // count
2237         MSG_WriteShort(&sv.datagram, bound(0, G_FLOAT(OFS_PARM3), 65535));
2238         // color
2239         MSG_WriteByte(&sv.datagram, G_FLOAT(OFS_PARM4));
2240 }
2241
2242 void PF_te_spark (void)
2243 {
2244         if (G_FLOAT(OFS_PARM2) < 1)
2245                 return;
2246         MSG_WriteByte(&sv.datagram, svc_temp_entity);
2247         MSG_WriteByte(&sv.datagram, TE_SPARK);
2248         // origin
2249         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[0]);
2250         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[1]);
2251         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[2]);
2252         // velocity
2253         MSG_WriteByte(&sv.datagram, bound(-128, (int) G_VECTOR(OFS_PARM1)[0], 127));
2254         MSG_WriteByte(&sv.datagram, bound(-128, (int) G_VECTOR(OFS_PARM1)[1], 127));
2255         MSG_WriteByte(&sv.datagram, bound(-128, (int) G_VECTOR(OFS_PARM1)[2], 127));
2256         // count
2257         MSG_WriteByte(&sv.datagram, bound(0, (int) G_FLOAT(OFS_PARM2), 255));
2258 }
2259
2260 void PF_te_gunshotquad (void)
2261 {
2262         MSG_WriteByte(&sv.datagram, svc_temp_entity);
2263         MSG_WriteByte(&sv.datagram, TE_GUNSHOTQUAD);
2264         // origin
2265         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[0]);
2266         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[1]);
2267         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[2]);
2268 }
2269
2270 void PF_te_spikequad (void)
2271 {
2272         MSG_WriteByte(&sv.datagram, svc_temp_entity);
2273         MSG_WriteByte(&sv.datagram, TE_SPIKEQUAD);
2274         // origin
2275         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[0]);
2276         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[1]);
2277         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[2]);
2278 }
2279
2280 void PF_te_superspikequad (void)
2281 {
2282         MSG_WriteByte(&sv.datagram, svc_temp_entity);
2283         MSG_WriteByte(&sv.datagram, TE_SUPERSPIKEQUAD);
2284         // origin
2285         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[0]);
2286         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[1]);
2287         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[2]);
2288 }
2289
2290 void PF_te_explosionquad (void)
2291 {
2292         MSG_WriteByte(&sv.datagram, svc_temp_entity);
2293         MSG_WriteByte(&sv.datagram, TE_EXPLOSIONQUAD);
2294         // origin
2295         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[0]);
2296         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[1]);
2297         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[2]);
2298 }
2299
2300 void PF_te_smallflash (void)
2301 {
2302         MSG_WriteByte(&sv.datagram, svc_temp_entity);
2303         MSG_WriteByte(&sv.datagram, TE_SMALLFLASH);
2304         // origin
2305         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[0]);
2306         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[1]);
2307         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[2]);
2308 }
2309
2310 void PF_te_customflash (void)
2311 {
2312         if (G_FLOAT(OFS_PARM1) < 8 || G_FLOAT(OFS_PARM2) < (1.0 / 256.0))
2313                 return;
2314         MSG_WriteByte(&sv.datagram, svc_temp_entity);
2315         MSG_WriteByte(&sv.datagram, TE_CUSTOMFLASH);
2316         // origin
2317         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[0]);
2318         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[1]);
2319         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[2]);
2320         // radius
2321         MSG_WriteByte(&sv.datagram, bound(0, G_FLOAT(OFS_PARM1) / 8 - 1, 255));
2322         // lifetime
2323         MSG_WriteByte(&sv.datagram, bound(0, G_FLOAT(OFS_PARM2) / 256 - 1, 255));
2324         // color
2325         MSG_WriteByte(&sv.datagram, bound(0, G_VECTOR(OFS_PARM3)[0] * 255, 255));
2326         MSG_WriteByte(&sv.datagram, bound(0, G_VECTOR(OFS_PARM3)[1] * 255, 255));
2327         MSG_WriteByte(&sv.datagram, bound(0, G_VECTOR(OFS_PARM3)[2] * 255, 255));
2328 }
2329
2330 void PF_te_gunshot (void)
2331 {
2332         MSG_WriteByte(&sv.datagram, svc_temp_entity);
2333         MSG_WriteByte(&sv.datagram, TE_GUNSHOT);
2334         // origin
2335         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[0]);
2336         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[1]);
2337         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[2]);
2338 }
2339
2340 void PF_te_spike (void)
2341 {
2342         MSG_WriteByte(&sv.datagram, svc_temp_entity);
2343         MSG_WriteByte(&sv.datagram, TE_SPIKE);
2344         // origin
2345         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[0]);
2346         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[1]);
2347         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[2]);
2348 }
2349
2350 void PF_te_superspike (void)
2351 {
2352         MSG_WriteByte(&sv.datagram, svc_temp_entity);
2353         MSG_WriteByte(&sv.datagram, TE_SUPERSPIKE);
2354         // origin
2355         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[0]);
2356         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[1]);
2357         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[2]);
2358 }
2359
2360 void PF_te_explosion (void)
2361 {
2362         MSG_WriteByte(&sv.datagram, svc_temp_entity);
2363         MSG_WriteByte(&sv.datagram, TE_EXPLOSION);
2364         // origin
2365         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[0]);
2366         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[1]);
2367         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[2]);
2368 }
2369
2370 void PF_te_tarexplosion (void)
2371 {
2372         MSG_WriteByte(&sv.datagram, svc_temp_entity);
2373         MSG_WriteByte(&sv.datagram, TE_TAREXPLOSION);
2374         // origin
2375         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[0]);
2376         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[1]);
2377         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[2]);
2378 }
2379
2380 void PF_te_wizspike (void)
2381 {
2382         MSG_WriteByte(&sv.datagram, svc_temp_entity);
2383         MSG_WriteByte(&sv.datagram, TE_WIZSPIKE);
2384         // origin
2385         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[0]);
2386         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[1]);
2387         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[2]);
2388 }
2389
2390 void PF_te_knightspike (void)
2391 {
2392         MSG_WriteByte(&sv.datagram, svc_temp_entity);
2393         MSG_WriteByte(&sv.datagram, TE_KNIGHTSPIKE);
2394         // origin
2395         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[0]);
2396         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[1]);
2397         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[2]);
2398 }
2399
2400 void PF_te_lavasplash (void)
2401 {
2402         MSG_WriteByte(&sv.datagram, svc_temp_entity);
2403         MSG_WriteByte(&sv.datagram, TE_LAVASPLASH);
2404         // origin
2405         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[0]);
2406         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[1]);
2407         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[2]);
2408 }
2409
2410 void PF_te_teleport (void)
2411 {
2412         MSG_WriteByte(&sv.datagram, svc_temp_entity);
2413         MSG_WriteByte(&sv.datagram, TE_TELEPORT);
2414         // origin
2415         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[0]);
2416         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[1]);
2417         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[2]);
2418 }
2419
2420 void PF_te_explosion2 (void)
2421 {
2422         MSG_WriteByte(&sv.datagram, svc_temp_entity);
2423         MSG_WriteByte(&sv.datagram, TE_EXPLOSION2);
2424         // origin
2425         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[0]);
2426         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[1]);
2427         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[2]);
2428         // color
2429         MSG_WriteByte(&sv.datagram, G_FLOAT(OFS_PARM1));
2430 }
2431
2432 void PF_te_lightning1 (void)
2433 {
2434         MSG_WriteByte(&sv.datagram, svc_temp_entity);
2435         MSG_WriteByte(&sv.datagram, TE_LIGHTNING1);
2436         // owner entity
2437         MSG_WriteShort(&sv.datagram, G_EDICTNUM(OFS_PARM0));
2438         // start
2439         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM1)[0]);
2440         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM1)[1]);
2441         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM1)[2]);
2442         // end
2443         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM2)[0]);
2444         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM2)[1]);
2445         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM2)[2]);
2446 }
2447
2448 void PF_te_lightning2 (void)
2449 {
2450         MSG_WriteByte(&sv.datagram, svc_temp_entity);
2451         MSG_WriteByte(&sv.datagram, TE_LIGHTNING2);
2452         // owner entity
2453         MSG_WriteShort(&sv.datagram, G_EDICTNUM(OFS_PARM0));
2454         // start
2455         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM1)[0]);
2456         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM1)[1]);
2457         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM1)[2]);
2458         // end
2459         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM2)[0]);
2460         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM2)[1]);
2461         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM2)[2]);
2462 }
2463
2464 void PF_te_lightning3 (void)
2465 {
2466         MSG_WriteByte(&sv.datagram, svc_temp_entity);
2467         MSG_WriteByte(&sv.datagram, TE_LIGHTNING3);
2468         // owner entity
2469         MSG_WriteShort(&sv.datagram, G_EDICTNUM(OFS_PARM0));
2470         // start
2471         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM1)[0]);
2472         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM1)[1]);
2473         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM1)[2]);
2474         // end
2475         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM2)[0]);
2476         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM2)[1]);
2477         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM2)[2]);
2478 }
2479
2480 void PF_te_beam (void)
2481 {
2482         MSG_WriteByte(&sv.datagram, svc_temp_entity);
2483         MSG_WriteByte(&sv.datagram, TE_BEAM);
2484         // owner entity
2485         MSG_WriteShort(&sv.datagram, G_EDICTNUM(OFS_PARM0));
2486         // start
2487         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM1)[0]);
2488         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM1)[1]);
2489         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM1)[2]);
2490         // end
2491         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM2)[0]);
2492         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM2)[1]);
2493         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM2)[2]);
2494 }
2495
2496 void PF_te_plasmaburn (void)
2497 {
2498         MSG_WriteByte(&sv.datagram, svc_temp_entity);
2499         MSG_WriteByte(&sv.datagram, TE_PLASMABURN);
2500         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[0]);
2501         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[1]);
2502         MSG_WriteDPCoord(&sv.datagram, G_VECTOR(OFS_PARM0)[2]);
2503 }
2504
2505 static void clippointtosurface(msurface_t *surf, vec3_t p, vec3_t out)
2506 {
2507         int i, j;
2508         vec3_t v1, clipplanenormal, normal;
2509         vec_t clipplanedist, clipdist;
2510         VectorCopy(p, out);
2511         if (surf->flags & SURF_PLANEBACK)
2512                 VectorNegate(surf->plane->normal, normal);
2513         else
2514                 VectorCopy(surf->plane->normal, normal);
2515         for (i = 0, j = surf->poly_numverts - 1;i < surf->poly_numverts;j = i, i++)
2516         {
2517                 VectorSubtract(&surf->poly_verts[j * 3], &surf->poly_verts[i * 3], v1);
2518                 VectorNormalizeFast(v1);
2519                 CrossProduct(v1, normal, clipplanenormal);
2520                 clipplanedist = DotProduct(&surf->poly_verts[i * 3], clipplanenormal);
2521                 clipdist = DotProduct(out, clipplanenormal) - clipplanedist;
2522                 if (clipdist > 0)
2523                 {
2524                         clipdist = -clipdist;
2525                         VectorMA(out, clipdist, clipplanenormal, out);
2526                 }
2527         }
2528 }
2529
2530 static msurface_t *getsurface(edict_t *ed, int surfnum)
2531 {
2532         int modelindex;
2533         model_t *model;
2534         if (!ed || ed->free)
2535                 return NULL;
2536         modelindex = ed->v->modelindex;
2537         if (modelindex < 1 || modelindex >= MAX_MODELS)
2538                 return NULL;
2539         model = sv.models[modelindex];
2540         if (model->type != mod_brush)
2541                 return NULL;
2542         if (surfnum < 0 || surfnum >= model->nummodelsurfaces)
2543                 return NULL;
2544         return model->surfaces + surfnum + model->firstmodelsurface;
2545 }
2546
2547
2548 //PF_getsurfacenumpoints, // #434 float(entity e, float s) getsurfacenumpoints = #434;
2549 void PF_getsurfacenumpoints(void)
2550 {
2551         msurface_t *surf;
2552         // return 0 if no such surface
2553         if (!(surf = getsurface(G_EDICT(OFS_PARM0), G_FLOAT(OFS_PARM1))))
2554         {
2555                 G_FLOAT(OFS_RETURN) = 0;
2556                 return;
2557         }
2558
2559         G_FLOAT(OFS_RETURN) = surf->poly_numverts;
2560 }
2561 //PF_getsurfacepoint,     // #435 vector(entity e, float s, float n) getsurfacepoint = #435;
2562 void PF_getsurfacepoint(void)
2563 {
2564         edict_t *ed;
2565         msurface_t *surf;
2566         int pointnum;
2567         VectorClear(G_VECTOR(OFS_RETURN));
2568         ed = G_EDICT(OFS_PARM0);
2569         if (!ed || ed->free)
2570                 return;
2571         if (!(surf = getsurface(ed, G_FLOAT(OFS_PARM1))))
2572                 return;
2573         pointnum = G_FLOAT(OFS_PARM2);
2574         if (pointnum < 0 || pointnum >= surf->poly_numverts)
2575                 return;
2576         // FIXME: implement rotation/scaling
2577         VectorAdd(&surf->poly_verts[pointnum * 3], ed->v->origin, G_VECTOR(OFS_RETURN));
2578 }
2579 //PF_getsurfacenormal,    // #436 vector(entity e, float s) getsurfacenormal = #436;
2580 void PF_getsurfacenormal(void)
2581 {
2582         msurface_t *surf;
2583         VectorClear(G_VECTOR(OFS_RETURN));
2584         if (!(surf = getsurface(G_EDICT(OFS_PARM0), G_FLOAT(OFS_PARM1))))
2585                 return;
2586         // FIXME: implement rotation/scaling
2587         if (surf->flags & SURF_PLANEBACK)
2588                 VectorNegate(surf->plane->normal, G_VECTOR(OFS_RETURN));
2589         else
2590                 VectorCopy(surf->plane->normal, G_VECTOR(OFS_RETURN));
2591 }
2592 //PF_getsurfacetexture,   // #437 string(entity e, float s) getsurfacetexture = #437;
2593 void PF_getsurfacetexture(void)
2594 {
2595         msurface_t *surf;
2596         G_INT(OFS_RETURN) = 0;
2597         if (!(surf = getsurface(G_EDICT(OFS_PARM0), G_FLOAT(OFS_PARM1))))
2598                 return;
2599         G_INT(OFS_RETURN) = PR_SetString(surf->texinfo->texture->name);
2600 }
2601 //PF_getsurfacenearpoint, // #438 float(entity e, vector p) getsurfacenearpoint = #438;
2602 void PF_getsurfacenearpoint(void)
2603 {
2604         int surfnum, best, modelindex;
2605         vec3_t clipped, p;
2606         vec_t dist, bestdist;
2607         edict_t *ed;
2608         model_t *model;
2609         msurface_t *surf;
2610         vec_t *point;
2611         G_FLOAT(OFS_RETURN) = -1;
2612         ed = G_EDICT(OFS_PARM0);
2613         point = G_VECTOR(OFS_PARM1);
2614
2615         if (!ed || ed->free)
2616                 return;
2617         modelindex = ed->v->modelindex;
2618         if (modelindex < 1 || modelindex >= MAX_MODELS)
2619                 return;
2620         model = sv.models[modelindex];
2621         if (model->type != mod_brush)
2622                 return;
2623
2624         // FIXME: implement rotation/scaling
2625         VectorSubtract(point, ed->v->origin, p);
2626         best = -1;
2627         bestdist = 1000000000;
2628         for (surfnum = 0;surfnum < model->nummodelsurfaces;surfnum++)
2629         {
2630                 surf = model->surfaces + surfnum + model->firstmodelsurface;
2631                 dist = PlaneDiff(p, surf->plane);
2632                 dist = dist * dist;
2633                 if (dist < bestdist)
2634                 {
2635                         clippointtosurface(surf, p, clipped);
2636                         VectorSubtract(clipped, p, clipped);
2637                         dist += DotProduct(clipped, clipped);
2638                         if (dist < bestdist)
2639                         {
2640                                 best = surfnum;
2641                                 bestdist = dist;
2642                         }
2643                 }
2644         }
2645         G_FLOAT(OFS_RETURN) = best;
2646 }
2647 //PF_getsurfaceclippedpoint, // #439 vector(entity e, float s, vector p) getsurfaceclippedpoint = #439;
2648 void PF_getsurfaceclippedpoint(void)
2649 {
2650         edict_t *ed;
2651         msurface_t *surf;
2652         vec3_t p, out;
2653         VectorClear(G_VECTOR(OFS_RETURN));
2654         ed = G_EDICT(OFS_PARM0);
2655         if (!ed || ed->free)
2656                 return;
2657         if (!(surf = getsurface(ed, G_FLOAT(OFS_PARM1))))
2658                 return;
2659         // FIXME: implement rotation/scaling
2660         VectorSubtract(G_VECTOR(OFS_PARM2), ed->v->origin, p);
2661         clippointtosurface(surf, p, out);
2662         // FIXME: implement rotation/scaling
2663         VectorAdd(out, ed->v->origin, G_VECTOR(OFS_RETURN));
2664 }
2665
2666 void PF_Fixme (void)
2667 {
2668         Host_Error ("unimplemented QC builtin"); // LordHavoc: was misspelled (bulitin)
2669 }
2670
2671 #define MAX_PRFILES 256
2672
2673 qfile_t *pr_files[MAX_PRFILES];
2674
2675 void PR_Files_Init(void)
2676 {
2677         memset(pr_files, 0, sizeof(pr_files));
2678 }
2679
2680 void PR_Files_CloseAll(void)
2681 {
2682         int i;
2683         for (i = 0;i < MAX_PRFILES;i++)
2684         {
2685                 if (pr_files[i])
2686                         FS_Close(pr_files[i]);
2687                 pr_files[i] = NULL;
2688         }
2689 }
2690
2691 //float(string s) stof = #81; // get numerical value from a string
2692 void PF_stof(void)
2693 {
2694         char *s = PF_VarString(0);
2695         G_FLOAT(OFS_RETURN) = atof(s);
2696 }
2697
2698 //float(string filename, float mode) fopen = #110; // opens a file inside quake/gamedir/data/ (mode is FILE_READ, FILE_APPEND, or FILE_WRITE), returns fhandle >= 0 if successful, or fhandle < 0 if unable to open file for any reason
2699 void PF_fopen(void)
2700 {
2701         int filenum, mode;
2702         char *modestring, *filename;
2703         for (filenum = 0;filenum < MAX_PRFILES;filenum++)
2704                 if (pr_files[filenum] == NULL)
2705                         break;
2706         if (filenum >= MAX_PRFILES)
2707         {
2708                 Con_Printf("PF_fopen: ran out of file handles (%i)\n", MAX_PRFILES);
2709                 G_FLOAT(OFS_RETURN) = -2;
2710                 return;
2711         }
2712         mode = G_FLOAT(OFS_PARM1);
2713         switch(mode)
2714         {
2715         case 0: // FILE_READ
2716                 modestring = "rb";
2717                 break;
2718         case 1: // FILE_APPEND
2719                 modestring = "ab";
2720                 break;
2721         case 2: // FILE_WRITE
2722                 modestring = "wb";
2723                 break;
2724         default:
2725                 Con_Printf("PF_fopen: no such mode %i (valid: 0 = read, 1 = append, 2 = write)\n", mode);
2726                 G_FLOAT(OFS_RETURN) = -3;
2727                 return;
2728         }
2729         filename = G_STRING(OFS_PARM0);
2730         // .. is parent directory on many platforms
2731         // / is parent directory on Amiga
2732         // : is root of drive on Amiga (also used as a directory separator on Mac, but / works there too, so that's a bad idea)
2733         // \ is a windows-ism (so it's naughty to use it, / works on all platforms)
2734         if ((filename[0] == '.' && filename[1] == '.') || filename[0] == '/' || strrchr(filename, ':') || strrchr(filename, '\\'))
2735         {
2736                 Con_Printf("PF_fopen: dangerous or non-portable filename \"%s\" not allowed. (contains : or \\ or begins with .. or /)\n", filename);
2737                 G_FLOAT(OFS_RETURN) = -4;
2738                 return;
2739         }
2740         pr_files[filenum] = FS_Open(va("data/%s", filename), modestring, false);
2741         if (pr_files[filenum] == NULL)
2742                 G_FLOAT(OFS_RETURN) = -1;
2743         else
2744                 G_FLOAT(OFS_RETURN) = filenum;
2745 }
2746
2747 //void(float fhandle) fclose = #111; // closes a file
2748 void PF_fclose(void)
2749 {
2750         int filenum = G_FLOAT(OFS_PARM0);
2751         if (filenum < 0 || filenum >= MAX_PRFILES)
2752         {
2753                 Con_Printf("PF_fclose: invalid file handle %i\n", filenum);
2754                 return;
2755         }
2756         if (pr_files[filenum] == NULL)
2757         {
2758                 Con_Printf("PF_fclose: no such file handle %i (or file has been closed)\n", filenum);
2759                 return;
2760         }
2761         FS_Close(pr_files[filenum]);
2762         pr_files[filenum] = NULL;
2763 }
2764
2765 //string(float fhandle) fgets = #112; // reads a line of text from the file and returns as a tempstring
2766 void PF_fgets(void)
2767 {
2768         int c, end;
2769         static char string[MAX_VARSTRING];
2770         int filenum = G_FLOAT(OFS_PARM0);
2771         if (filenum < 0 || filenum >= MAX_PRFILES)
2772         {
2773                 Con_Printf("PF_fgets: invalid file handle %i\n", filenum);
2774                 return;
2775         }
2776         if (pr_files[filenum] == NULL)
2777         {
2778                 Con_Printf("PF_fgets: no such file handle %i (or file has been closed)\n", filenum);
2779                 return;
2780         }
2781         end = 0;
2782         for (;;)
2783         {
2784                 c = FS_Getc(pr_files[filenum]);
2785                 if (c == '\r' || c == '\n' || c < 0)
2786                         break;
2787                 if (end < MAX_VARSTRING - 1)
2788                         string[end++] = c;
2789         }
2790         string[end] = 0;
2791         // remove \n following \r
2792         if (c == '\r')
2793                 c = FS_Getc(pr_files[filenum]);
2794         if (developer.integer)
2795                 Con_Printf("fgets: %s\n", string);
2796         if (c >= 0)
2797                 G_INT(OFS_RETURN) = PR_SetString(string);
2798         else
2799                 G_INT(OFS_RETURN) = 0;
2800 }
2801
2802 //void(float fhandle, string s) fputs = #113; // writes a line of text to the end of the file
2803 void PF_fputs(void)
2804 {
2805         int stringlength;
2806         char *s = PF_VarString(1);
2807         int filenum = G_FLOAT(OFS_PARM0);
2808         if (filenum < 0 || filenum >= MAX_PRFILES)
2809         {
2810                 Con_Printf("PF_fputs: invalid file handle %i\n", filenum);
2811                 return;
2812         }
2813         if (pr_files[filenum] == NULL)
2814         {
2815                 Con_Printf("PF_fputs: no such file handle %i (or file has been closed)\n", filenum);
2816                 return;
2817         }
2818         if ((stringlength = strlen(s)))
2819                 FS_Write(pr_files[filenum], s, stringlength);
2820         if (developer.integer)
2821                 Con_Printf("fputs: %s\n", s);
2822 }
2823
2824 //float(string s) strlen = #114; // returns how many characters are in a string
2825 void PF_strlen(void)
2826 {
2827         char *s;
2828         s = G_STRING(OFS_PARM0);
2829         if (s)
2830                 G_FLOAT(OFS_RETURN) = strlen(s);
2831         else
2832                 G_FLOAT(OFS_RETURN) = 0;
2833 }
2834
2835 //string(string s1, string s2) strcat = #115; // concatenates two strings (for example "abc", "def" would return "abcdef") and returns as a tempstring
2836 void PF_strcat(void)
2837 {
2838         char *s = PF_VarString(0);
2839         G_INT(OFS_RETURN) = PR_SetString(s);
2840 }
2841
2842 //string(string s, float start, float length) substring = #116; // returns a section of a string as a tempstring
2843 void PF_substring(void)
2844 {
2845         int end, start, length, slen;
2846         char *s;
2847         char string[MAX_VARSTRING];
2848         s = G_STRING(OFS_PARM0);
2849         start = G_FLOAT(OFS_PARM1);
2850         length = G_FLOAT(OFS_PARM2);
2851         if (s)
2852                 slen = strlen(s);
2853         else
2854                 slen = 0;
2855         if (start < 0)
2856                 start = 0;
2857         if (length > slen - start)
2858                 length = slen - start;
2859         if (length > MAX_VARSTRING - 1)
2860                 length = MAX_VARSTRING - 1;
2861         end = 0;
2862         if (length > 0)
2863         {
2864                 memcpy(string, s + start, length);
2865                 end = length;
2866         }
2867         string[end] = 0;
2868         G_INT(OFS_RETURN) = PR_SetString(string);
2869 }
2870
2871 //vector(string s) stov = #117; // returns vector value from a string
2872 void PF_stov(void)
2873 {
2874         Math_atov(PF_VarString(0), G_VECTOR(OFS_RETURN));
2875 }
2876
2877 //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)
2878 void PF_strzone(void)
2879 {
2880         char *in, *out;
2881         in = G_STRING(OFS_PARM0);
2882         out = Mem_Alloc(pr_strings_mempool, strlen(in) + 1);
2883         strcpy(out, in);
2884         G_INT(OFS_RETURN) = PR_SetString(out);
2885 }
2886
2887 //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!!!)
2888 void PF_strunzone(void)
2889 {
2890         Mem_Free(G_STRING(OFS_PARM0));
2891 }
2892
2893 builtin_t pr_builtin[] =
2894 {
2895 PF_Fixme,
2896 PF_makevectors, // void(entity e)       makevectors             = #1;
2897 PF_setorigin,   // void(entity e, vector o) setorigin   = #2;
2898 PF_setmodel,    // void(entity e, string m) setmodel    = #3;
2899 PF_setsize,     // void(entity e, vector min, vector max) setsize = #4;
2900 PF_Fixme,       // void(entity e, vector min, vector max) setabssize = #5;
2901 PF_break,       // void() break                                         = #6;
2902 PF_random,      // float() random                                               = #7;
2903 PF_sound,       // void(entity e, float chan, string samp) sound = #8;
2904 PF_normalize,   // vector(vector v) normalize                   = #9;
2905 PF_error,       // void(string e) error                         = #10;
2906 PF_objerror,    // void(string e) objerror                              = #11;
2907 PF_vlen,        // float(vector v) vlen                         = #12;
2908 PF_vectoyaw,    // float(vector v) vectoyaw             = #13;
2909 PF_Spawn,       // entity() spawn                                               = #14;
2910 PF_Remove,      // void(entity e) remove                                = #15;
2911 PF_traceline,   // float(vector v1, vector v2, float tryents) traceline = #16;
2912 PF_checkclient, // entity() clientlist                                  = #17;
2913 PF_Find,        // entity(entity start, .string fld, string match) find = #18;
2914 PF_precache_sound,      // void(string s) precache_sound                = #19;
2915 PF_precache_model,      // void(string s) precache_model                = #20;
2916 PF_stuffcmd,    // void(entity client, string s)stuffcmd = #21;
2917 PF_findradius,  // entity(vector org, float rad) findradius = #22;
2918 PF_bprint,      // void(string s) bprint                                = #23;
2919 PF_sprint,      // void(entity client, string s) sprint = #24;
2920 PF_dprint,      // void(string s) dprint                                = #25;
2921 PF_ftos,        // void(string s) ftos                          = #26;
2922 PF_vtos,        // void(string s) vtos                          = #27;
2923 PF_coredump,
2924 PF_traceon,
2925 PF_traceoff,
2926 PF_eprint,      // void(entity e) debug print an entire entity
2927 PF_walkmove, // float(float yaw, float dist) walkmove
2928 PF_Fixme, // float(float yaw, float dist) walkmove
2929 PF_droptofloor,
2930 PF_lightstyle,
2931 PF_rint,
2932 PF_floor,
2933 PF_ceil,
2934 PF_Fixme,
2935 PF_checkbottom,
2936 PF_pointcontents,
2937 PF_Fixme,
2938 PF_fabs,
2939 PF_aim,
2940 PF_cvar,
2941 PF_localcmd,
2942 PF_nextent,
2943 PF_particle,
2944 PF_changeyaw,
2945 PF_Fixme,
2946 PF_vectoangles,
2947
2948 PF_WriteByte,
2949 PF_WriteChar,
2950 PF_WriteShort,
2951 PF_WriteLong,
2952 PF_WriteCoord,
2953 PF_WriteAngle,
2954 PF_WriteString,
2955 PF_WriteEntity,
2956
2957 PF_sin,
2958 PF_cos,
2959 PF_sqrt,
2960 PF_changepitch,
2961 PF_TraceToss,
2962 PF_etos,
2963 PF_Fixme,
2964
2965 SV_MoveToGoal,
2966 PF_precache_file,
2967 PF_makestatic,
2968
2969 PF_changelevel,
2970 PF_Fixme,
2971
2972 PF_cvar_set,
2973 PF_centerprint,
2974
2975 PF_ambientsound,
2976
2977 PF_precache_model,
2978 PF_precache_sound,              // precache_sound2 is different only for qcc
2979 PF_precache_file,
2980
2981 PF_setspawnparms,
2982
2983 PF_Fixme,                               // #79 LordHavoc: dunno who owns 79-89, so these are just padding
2984 PF_Fixme,                               // #80
2985 PF_stof,                                // #81 float(string s) stof = #81;
2986 PF_Fixme,                               // #82
2987 PF_Fixme,                               // #83
2988 PF_Fixme,                               // #84
2989 PF_Fixme,                               // #85
2990 PF_Fixme,                               // #86
2991 PF_Fixme,                               // #87
2992 PF_Fixme,                               // #88
2993 PF_Fixme,                               // #89
2994
2995 PF_tracebox,                    // #90 LordHavoc builtin range (9x)
2996 PF_randomvec,                   // #91
2997 PF_GetLight,                    // #92
2998 PF_registercvar,                // #93
2999 PF_min,                                 // #94
3000 PF_max,                                 // #95
3001 PF_bound,                               // #96
3002 PF_pow,                                 // #97
3003 PF_FindFloat,                   // #98
3004 PF_checkextension,              // #99
3005 #define a PF_Fixme, PF_Fixme, PF_Fixme, PF_Fixme, PF_Fixme, PF_Fixme, PF_Fixme, PF_Fixme, PF_Fixme, PF_Fixme,
3006 a                                               // #100-109
3007 PF_fopen,                               // #110 float(string filename, float mode) fopen = #110;
3008 PF_fclose,                              // #111 void(float fhandle) fclose = #111;
3009 PF_fgets,                               // #112 string(float fhandle) fgets = #112;
3010 PF_fputs,                               // #113 void(float fhandle, string s) fputs = #113;
3011 PF_strlen,                              // #114 float(string s) strlen = #114;
3012 PF_strcat,                              // #115 string(string s1, string s2) strcat = #115;
3013 PF_substring,                   // #116 string(string s, float start, float length) substring = #116;
3014 PF_stov,                                // #117 vector(string) stov = #117;
3015 PF_strzone,                             // #118 string(string s) strzone = #118;
3016 PF_strunzone,                   // #119 void(string s) strunzone = #119;
3017 a                                               // #120-129
3018 a                                               // #130-139
3019 a                                               // #140-149
3020 a                                               // #150-159
3021 a                                               // #160-169
3022 a                                               // #170-179
3023 a                                               // #180-189
3024 a                                               // #190-199
3025 a a a a a a a a a a             // #200-299
3026 a a a a a a a a a a             // #300-399
3027 PF_copyentity,                  // #400 LordHavoc: builtin range (4xx)
3028 PF_setcolors,                   // #401
3029 PF_findchain,                   // #402
3030 PF_findchainfloat,              // #403
3031 PF_effect,                              // #404
3032 PF_te_blood,                    // #405
3033 PF_te_bloodshower,              // #406
3034 PF_te_explosionrgb,             // #407
3035 PF_te_particlecube,             // #408
3036 PF_te_particlerain,             // #409
3037 PF_te_particlesnow,             // #410
3038 PF_te_spark,                    // #411
3039 PF_te_gunshotquad,              // #412
3040 PF_te_spikequad,                // #413
3041 PF_te_superspikequad,   // #414
3042 PF_te_explosionquad,    // #415
3043 PF_te_smallflash,               // #416
3044 PF_te_customflash,              // #417
3045 PF_te_gunshot,                  // #418
3046 PF_te_spike,                    // #419
3047 PF_te_superspike,               // #420
3048 PF_te_explosion,                // #421
3049 PF_te_tarexplosion,             // #422
3050 PF_te_wizspike,                 // #423
3051 PF_te_knightspike,              // #424
3052 PF_te_lavasplash,               // #425
3053 PF_te_teleport,                 // #426
3054 PF_te_explosion2,               // #427
3055 PF_te_lightning1,               // #428
3056 PF_te_lightning2,               // #429
3057 PF_te_lightning3,               // #430
3058 PF_te_beam,                             // #431
3059 PF_vectorvectors,               // #432
3060 PF_te_plasmaburn,               // #433
3061 PF_getsurfacenumpoints, // #434 float(entity e, float s) getsurfacenumpoints = #434;
3062 PF_getsurfacepoint,     // #435 vector(entity e, float s, float n) getsurfacepoint = #435;
3063 PF_getsurfacenormal,    // #436 vector(entity e, float s) getsurfacenormal = #436;
3064 PF_getsurfacetexture,   // #437 string(entity e, float s) getsurfacetexture = #437;
3065 PF_getsurfacenearpoint, // #438 float(entity e, vector p) getsurfacenearpoint = #438;
3066 PF_getsurfaceclippedpoint,// #439 vector(entity e, float s, vector p) getsurfaceclippedpoint = #439;
3067 };
3068
3069 builtin_t *pr_builtins = pr_builtin;
3070 int pr_numbuiltins = sizeof(pr_builtin)/sizeof(pr_builtin[0]);
3071
3072 void PR_Cmd_Init(void)
3073 {
3074         pr_strings_mempool = Mem_AllocPool("pr_stringszone");
3075         PR_Files_Init();
3076 }
3077
3078 void PR_Cmd_Reset(void)
3079 {
3080         Mem_EmptyPool(pr_strings_mempool);
3081         PR_Files_CloseAll();
3082 }
3083