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