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