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