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