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