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