5 #include "cl_collision.h"
10 //============================================================================
12 //[515]: unsolved PROBLEMS
13 //- finish player physics code (cs_runplayerphysics)
15 //- RF_DEPTHHACK is not like it should be
16 //- add builtin that sets cl.viewangles instead of reading "input_angles" global
17 //- finish lines support for R_Polygon***
18 //- insert selecttraceline into traceline somehow
20 //4 feature darkplaces csqc: add builtin to clientside qc for reading triangles of model meshes (useful to orient a ui along a triangle of a model mesh)
21 //4 feature darkplaces csqc: add builtins to clientside qc for gl calls
23 extern cvar_t v_flipped;
24 extern cvar_t r_equalize_entities_fullbright;
26 r_refdef_view_t csqc_original_r_refdef_view;
27 r_refdef_view_t csqc_main_r_refdef_view;
29 sfx_t *S_FindName(const char *name);
30 int Sbar_GetSortedPlayerIndex (int index);
31 void Sbar_SortFrags (void);
32 void CL_FindNonSolidLocation(const vec3_t in, vec3_t out, vec_t radius);
33 void CSQC_RelinkAllEntities (int drawmask);
34 void CSQC_RelinkCSQCEntities (void);
36 // #1 void(vector ang) makevectors
37 static void VM_CL_makevectors (void)
39 VM_SAFEPARMCOUNT(1, VM_CL_makevectors);
40 AngleVectors (PRVM_G_VECTOR(OFS_PARM0), PRVM_clientglobalvector(v_forward), PRVM_clientglobalvector(v_right), PRVM_clientglobalvector(v_up));
43 // #2 void(entity e, vector o) setorigin
44 void VM_CL_setorigin (void)
48 VM_SAFEPARMCOUNT(2, VM_CL_setorigin);
50 e = PRVM_G_EDICT(OFS_PARM0);
51 if (e == prog->edicts)
53 VM_Warning("setorigin: can not modify world entity\n");
56 if (e->priv.required->free)
58 VM_Warning("setorigin: can not modify free entity\n");
61 org = PRVM_G_VECTOR(OFS_PARM1);
62 VectorCopy (org, PRVM_clientedictvector(e, origin));
63 if(e->priv.required->mark == PRVM_EDICT_MARK_WAIT_FOR_SETORIGIN)
64 e->priv.required->mark = PRVM_EDICT_MARK_SETORIGIN_CAUGHT;
68 static void SetMinMaxSize (prvm_edict_t *e, float *min, float *max)
74 PRVM_ERROR("SetMinMaxSize: backwards mins/maxs");
77 VectorCopy (min, PRVM_clientedictvector(e, mins));
78 VectorCopy (max, PRVM_clientedictvector(e, maxs));
79 VectorSubtract (max, min, PRVM_clientedictvector(e, size));
84 // #3 void(entity e, string m) setmodel
85 void VM_CL_setmodel (void)
92 VM_SAFEPARMCOUNT(2, VM_CL_setmodel);
94 e = PRVM_G_EDICT(OFS_PARM0);
95 PRVM_clientedictfloat(e, modelindex) = 0;
96 PRVM_clientedictstring(e, model) = 0;
98 m = PRVM_G_STRING(OFS_PARM1);
100 for (i = 0;i < MAX_MODELS && cl.csqc_model_precache[i];i++)
102 if (!strcmp(cl.csqc_model_precache[i]->name, m))
104 mod = cl.csqc_model_precache[i];
105 PRVM_clientedictstring(e, model) = PRVM_SetEngineString(mod->name);
106 PRVM_clientedictfloat(e, modelindex) = -(i+1);
112 for (i = 0;i < MAX_MODELS;i++)
114 mod = cl.model_precache[i];
115 if (mod && !strcmp(mod->name, m))
117 PRVM_clientedictstring(e, model) = PRVM_SetEngineString(mod->name);
118 PRVM_clientedictfloat(e, modelindex) = i;
125 // TODO: check if this breaks needed consistency and maybe add a cvar for it too?? [1/10/2008 Black]
126 //SetMinMaxSize (e, mod->normalmins, mod->normalmaxs);
130 SetMinMaxSize (e, vec3_origin, vec3_origin);
131 VM_Warning ("setmodel: model '%s' not precached\n", m);
135 // #4 void(entity e, vector min, vector max) setsize
136 static void VM_CL_setsize (void)
140 VM_SAFEPARMCOUNT(3, VM_CL_setsize);
142 e = PRVM_G_EDICT(OFS_PARM0);
143 if (e == prog->edicts)
145 VM_Warning("setsize: can not modify world entity\n");
148 if (e->priv.server->free)
150 VM_Warning("setsize: can not modify free entity\n");
153 min = PRVM_G_VECTOR(OFS_PARM1);
154 max = PRVM_G_VECTOR(OFS_PARM2);
156 SetMinMaxSize( e, min, max );
161 // #8 void(entity e, float chan, string samp, float volume, float atten) sound
162 static void VM_CL_sound (void)
166 prvm_edict_t *entity;
173 VM_SAFEPARMCOUNTRANGE(5, 7, VM_CL_sound);
175 entity = PRVM_G_EDICT(OFS_PARM0);
176 channel = (int)PRVM_G_FLOAT(OFS_PARM1);
177 sample = PRVM_G_STRING(OFS_PARM2);
178 volume = PRVM_G_FLOAT(OFS_PARM3);
179 attenuation = PRVM_G_FLOAT(OFS_PARM4);
181 if (volume < 0 || volume > 1)
183 VM_Warning("VM_CL_sound: volume must be in range 0-1\n");
187 if (attenuation < 0 || attenuation > 4)
189 VM_Warning("VM_CL_sound: attenuation must be in range 0-4\n");
196 pitchchange = PRVM_G_FLOAT(OFS_PARM5);
197 // ignoring prog->argc < 7 for now (no flags supported yet)
202 flags = PRVM_G_FLOAT(OFS_PARM6);
204 channel = CHAN_USER2ENGINE(channel);
206 if (!IS_CHAN(channel))
208 VM_Warning("VM_CL_sound: channel must be in range 0-127\n");
212 CL_VM_GetEntitySoundOrigin(MAX_EDICTS + PRVM_NUM_FOR_EDICT(entity), org);
213 S_StartSound_StartPosition_Flags(MAX_EDICTS + PRVM_NUM_FOR_EDICT(entity), channel, S_FindName(sample), org, volume, attenuation, 0, flags, pitchchange > 0.0f ? pitchchange * 0.01f : 1.0f);
216 // #483 void(vector origin, string sample, float volume, float attenuation) pointsound
217 static void VM_CL_pointsound(void)
224 VM_SAFEPARMCOUNT(4, VM_CL_pointsound);
226 VectorCopy( PRVM_G_VECTOR(OFS_PARM0), org);
227 sample = PRVM_G_STRING(OFS_PARM1);
228 volume = PRVM_G_FLOAT(OFS_PARM2);
229 attenuation = PRVM_G_FLOAT(OFS_PARM3);
231 if (volume < 0 || volume > 1)
233 VM_Warning("VM_CL_pointsound: volume must be in range 0-1\n");
237 if (attenuation < 0 || attenuation > 4)
239 VM_Warning("VM_CL_pointsound: attenuation must be in range 0-4\n");
243 // Send World Entity as Entity to Play Sound (for CSQC, that is MAX_EDICTS)
244 S_StartSound(MAX_EDICTS, 0, S_FindName(sample), org, volume, attenuation);
247 // #14 entity() spawn
248 static void VM_CL_spawn (void)
251 ed = PRVM_ED_Alloc();
255 void CL_VM_SetTraceGlobals(const trace_t *trace, int svent)
257 VM_SetTraceGlobals(trace);
258 PRVM_clientglobalfloat(trace_networkentity) = svent;
261 #define CL_HitNetworkBrushModels(move) !((move) == MOVE_WORLDONLY)
262 #define CL_HitNetworkPlayers(move) !((move) == MOVE_WORLDONLY || (move) == MOVE_NOMONSTERS)
264 // #16 void(vector v1, vector v2, float movetype, entity ignore) traceline
265 static void VM_CL_traceline (void)
272 // R_TimeReport("pretraceline");
274 VM_SAFEPARMCOUNTRANGE(4, 4, VM_CL_traceline);
276 prog->xfunction->builtinsprofile += 30;
278 v1 = PRVM_G_VECTOR(OFS_PARM0);
279 v2 = PRVM_G_VECTOR(OFS_PARM1);
280 move = (int)PRVM_G_FLOAT(OFS_PARM2);
281 ent = PRVM_G_EDICT(OFS_PARM3);
283 if (IS_NAN(v1[0]) || IS_NAN(v1[1]) || IS_NAN(v1[2]) || IS_NAN(v2[0]) || IS_NAN(v2[1]) || IS_NAN(v2[2]))
284 PRVM_ERROR("%s: NAN errors detected in traceline('%f %f %f', '%f %f %f', %i, entity %i)\n", PRVM_NAME, v1[0], v1[1], v1[2], v2[0], v2[1], v2[2], move, PRVM_EDICT_TO_PROG(ent));
286 trace = CL_TraceLine(v1, v2, move, ent, CL_GenericHitSuperContentsMask(ent), CL_HitNetworkBrushModels(move), CL_HitNetworkPlayers(move), &svent, true, false);
288 CL_VM_SetTraceGlobals(&trace, svent);
289 // R_TimeReport("traceline");
296 Used for use tracing and shot targeting
297 Traces are blocked by bbox and exact bsp entityes, and also slide box entities
298 if the tryents flag is set.
300 tracebox (vector1, vector mins, vector maxs, vector2, tryents)
303 // LordHavoc: added this for my own use, VERY useful, similar to traceline
304 static void VM_CL_tracebox (void)
306 float *v1, *v2, *m1, *m2;
311 // R_TimeReport("pretracebox");
312 VM_SAFEPARMCOUNTRANGE(6, 8, VM_CL_tracebox); // allow more parameters for future expansion
314 prog->xfunction->builtinsprofile += 30;
316 v1 = PRVM_G_VECTOR(OFS_PARM0);
317 m1 = PRVM_G_VECTOR(OFS_PARM1);
318 m2 = PRVM_G_VECTOR(OFS_PARM2);
319 v2 = PRVM_G_VECTOR(OFS_PARM3);
320 move = (int)PRVM_G_FLOAT(OFS_PARM4);
321 ent = PRVM_G_EDICT(OFS_PARM5);
323 if (IS_NAN(v1[0]) || IS_NAN(v1[1]) || IS_NAN(v1[2]) || IS_NAN(v2[0]) || IS_NAN(v2[1]) || IS_NAN(v2[2]))
324 PRVM_ERROR("%s: NAN errors detected in tracebox('%f %f %f', '%f %f %f', '%f %f %f', '%f %f %f', %i, entity %i)\n", PRVM_NAME, v1[0], v1[1], v1[2], m1[0], m1[1], m1[2], m2[0], m2[1], m2[2], v2[0], v2[1], v2[2], move, PRVM_EDICT_TO_PROG(ent));
326 trace = CL_TraceBox(v1, m1, m2, v2, move, ent, CL_GenericHitSuperContentsMask(ent), CL_HitNetworkBrushModels(move), CL_HitNetworkPlayers(move), &svent, true);
328 CL_VM_SetTraceGlobals(&trace, svent);
329 // R_TimeReport("tracebox");
332 trace_t CL_Trace_Toss (prvm_edict_t *tossent, prvm_edict_t *ignore, int *svent)
337 vec3_t original_origin;
338 vec3_t original_velocity;
339 vec3_t original_angles;
340 vec3_t original_avelocity;
343 VectorCopy(PRVM_clientedictvector(tossent, origin) , original_origin );
344 VectorCopy(PRVM_clientedictvector(tossent, velocity) , original_velocity );
345 VectorCopy(PRVM_clientedictvector(tossent, angles) , original_angles );
346 VectorCopy(PRVM_clientedictvector(tossent, avelocity), original_avelocity);
348 gravity = PRVM_clientedictfloat(tossent, gravity);
351 gravity *= cl.movevars_gravity * 0.05;
353 for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds
355 PRVM_clientedictvector(tossent, velocity)[2] -= gravity;
356 VectorMA (PRVM_clientedictvector(tossent, angles), 0.05, PRVM_clientedictvector(tossent, avelocity), PRVM_clientedictvector(tossent, angles));
357 VectorScale (PRVM_clientedictvector(tossent, velocity), 0.05, move);
358 VectorAdd (PRVM_clientedictvector(tossent, origin), move, end);
359 trace = CL_TraceBox(PRVM_clientedictvector(tossent, origin), PRVM_clientedictvector(tossent, mins), PRVM_clientedictvector(tossent, maxs), end, MOVE_NORMAL, tossent, CL_GenericHitSuperContentsMask(tossent), true, true, NULL, true);
360 VectorCopy (trace.endpos, PRVM_clientedictvector(tossent, origin));
362 if (trace.fraction < 1)
366 VectorCopy(original_origin , PRVM_clientedictvector(tossent, origin) );
367 VectorCopy(original_velocity , PRVM_clientedictvector(tossent, velocity) );
368 VectorCopy(original_angles , PRVM_clientedictvector(tossent, angles) );
369 VectorCopy(original_avelocity, PRVM_clientedictvector(tossent, avelocity));
374 static void VM_CL_tracetoss (void)
378 prvm_edict_t *ignore;
381 prog->xfunction->builtinsprofile += 600;
383 VM_SAFEPARMCOUNT(2, VM_CL_tracetoss);
385 ent = PRVM_G_EDICT(OFS_PARM0);
386 if (ent == prog->edicts)
388 VM_Warning("tracetoss: can not use world entity\n");
391 ignore = PRVM_G_EDICT(OFS_PARM1);
393 trace = CL_Trace_Toss (ent, ignore, &svent);
395 CL_VM_SetTraceGlobals(&trace, svent);
399 // #20 void(string s) precache_model
400 void VM_CL_precache_model (void)
406 VM_SAFEPARMCOUNT(1, VM_CL_precache_model);
408 name = PRVM_G_STRING(OFS_PARM0);
409 for (i = 0;i < MAX_MODELS && cl.csqc_model_precache[i];i++)
411 if(!strcmp(cl.csqc_model_precache[i]->name, name))
413 PRVM_G_FLOAT(OFS_RETURN) = -(i+1);
417 PRVM_G_FLOAT(OFS_RETURN) = 0;
418 m = Mod_ForName(name, false, false, name[0] == '*' ? cl.model_name[1] : NULL);
421 for (i = 0;i < MAX_MODELS;i++)
423 if (!cl.csqc_model_precache[i])
425 cl.csqc_model_precache[i] = (dp_model_t*)m;
426 PRVM_G_FLOAT(OFS_RETURN) = -(i+1);
430 VM_Warning("VM_CL_precache_model: no free models\n");
433 VM_Warning("VM_CL_precache_model: model \"%s\" not found\n", name);
436 int CSQC_EntitiesInBox (vec3_t mins, vec3_t maxs, int maxlist, prvm_edict_t **list)
441 ent = PRVM_NEXT_EDICT(prog->edicts);
442 for(k=0,i=1; i<prog->num_edicts ;i++, ent = PRVM_NEXT_EDICT(ent))
444 if (ent->priv.required->free)
446 if(BoxesOverlap(mins, maxs, PRVM_clientedictvector(ent, absmin), PRVM_clientedictvector(ent, absmax)))
452 // #22 entity(vector org, float rad) findradius
453 static void VM_CL_findradius (void)
455 prvm_edict_t *ent, *chain;
456 vec_t radius, radius2;
457 vec3_t org, eorg, mins, maxs;
458 int i, numtouchedicts;
459 static prvm_edict_t *touchedicts[MAX_EDICTS];
462 VM_SAFEPARMCOUNTRANGE(2, 3, VM_CL_findradius);
465 chainfield = PRVM_G_INT(OFS_PARM2);
467 chainfield = prog->fieldoffsets.chain;
469 PRVM_ERROR("VM_findchain: %s doesnt have the specified chain field !", PRVM_NAME);
471 chain = (prvm_edict_t *)prog->edicts;
473 VectorCopy(PRVM_G_VECTOR(OFS_PARM0), org);
474 radius = PRVM_G_FLOAT(OFS_PARM1);
475 radius2 = radius * radius;
477 mins[0] = org[0] - (radius + 1);
478 mins[1] = org[1] - (radius + 1);
479 mins[2] = org[2] - (radius + 1);
480 maxs[0] = org[0] + (radius + 1);
481 maxs[1] = org[1] + (radius + 1);
482 maxs[2] = org[2] + (radius + 1);
483 numtouchedicts = CSQC_EntitiesInBox(mins, maxs, MAX_EDICTS, touchedicts);
484 if (numtouchedicts > MAX_EDICTS)
486 // this never happens //[515]: for what then ?
487 Con_Printf("CSQC_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
488 numtouchedicts = MAX_EDICTS;
490 for (i = 0;i < numtouchedicts;i++)
492 ent = touchedicts[i];
493 // Quake did not return non-solid entities but darkplaces does
494 // (note: this is the reason you can't blow up fallen zombies)
495 if (PRVM_clientedictfloat(ent, solid) == SOLID_NOT && !sv_gameplayfix_blowupfallenzombies.integer)
497 // LordHavoc: compare against bounding box rather than center so it
498 // doesn't miss large objects, and use DotProduct instead of Length
499 // for a major speedup
500 VectorSubtract(org, PRVM_clientedictvector(ent, origin), eorg);
501 if (sv_gameplayfix_findradiusdistancetobox.integer)
503 eorg[0] -= bound(PRVM_clientedictvector(ent, mins)[0], eorg[0], PRVM_clientedictvector(ent, maxs)[0]);
504 eorg[1] -= bound(PRVM_clientedictvector(ent, mins)[1], eorg[1], PRVM_clientedictvector(ent, maxs)[1]);
505 eorg[2] -= bound(PRVM_clientedictvector(ent, mins)[2], eorg[2], PRVM_clientedictvector(ent, maxs)[2]);
508 VectorMAMAM(1, eorg, -0.5f, PRVM_clientedictvector(ent, mins), -0.5f, PRVM_clientedictvector(ent, maxs), eorg);
509 if (DotProduct(eorg, eorg) < radius2)
511 PRVM_EDICTFIELDEDICT(ent, chainfield) = PRVM_EDICT_TO_PROG(chain);
516 VM_RETURN_EDICT(chain);
519 // #34 float() droptofloor
520 static void VM_CL_droptofloor (void)
526 VM_SAFEPARMCOUNTRANGE(0, 2, VM_CL_droptofloor); // allow 2 parameters because the id1 defs.qc had an incorrect prototype
528 // assume failure if it returns early
529 PRVM_G_FLOAT(OFS_RETURN) = 0;
531 ent = PRVM_PROG_TO_EDICT(PRVM_clientglobaledict(self));
532 if (ent == prog->edicts)
534 VM_Warning("droptofloor: can not modify world entity\n");
537 if (ent->priv.server->free)
539 VM_Warning("droptofloor: can not modify free entity\n");
543 VectorCopy (PRVM_clientedictvector(ent, origin), end);
546 trace = CL_TraceBox(PRVM_clientedictvector(ent, origin), PRVM_clientedictvector(ent, mins), PRVM_clientedictvector(ent, maxs), end, MOVE_NORMAL, ent, CL_GenericHitSuperContentsMask(ent), true, true, NULL, true);
548 if (trace.fraction != 1)
550 VectorCopy (trace.endpos, PRVM_clientedictvector(ent, origin));
551 PRVM_clientedictfloat(ent, flags) = (int)PRVM_clientedictfloat(ent, flags) | FL_ONGROUND;
552 PRVM_clientedictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
553 PRVM_G_FLOAT(OFS_RETURN) = 1;
554 // if support is destroyed, keep suspended (gross hack for floating items in various maps)
555 // ent->priv.server->suspendedinairflag = true;
559 // #35 void(float style, string value) lightstyle
560 static void VM_CL_lightstyle (void)
565 VM_SAFEPARMCOUNT(2, VM_CL_lightstyle);
567 i = (int)PRVM_G_FLOAT(OFS_PARM0);
568 c = PRVM_G_STRING(OFS_PARM1);
569 if (i >= cl.max_lightstyle)
571 VM_Warning("VM_CL_lightstyle >= MAX_LIGHTSTYLES\n");
574 strlcpy (cl.lightstyle[i].map, c, sizeof (cl.lightstyle[i].map));
575 cl.lightstyle[i].map[MAX_STYLESTRING - 1] = 0;
576 cl.lightstyle[i].length = (int)strlen(cl.lightstyle[i].map);
579 // #40 float(entity e) checkbottom
580 static void VM_CL_checkbottom (void)
582 static int cs_yes, cs_no;
584 vec3_t mins, maxs, start, stop;
589 VM_SAFEPARMCOUNT(1, VM_CL_checkbottom);
590 ent = PRVM_G_EDICT(OFS_PARM0);
591 PRVM_G_FLOAT(OFS_RETURN) = 0;
593 VectorAdd (PRVM_clientedictvector(ent, origin), PRVM_clientedictvector(ent, mins), mins);
594 VectorAdd (PRVM_clientedictvector(ent, origin), PRVM_clientedictvector(ent, maxs), maxs);
596 // if all of the points under the corners are solid world, don't bother
597 // with the tougher checks
598 // the corners must be within 16 of the midpoint
599 start[2] = mins[2] - 1;
600 for (x=0 ; x<=1 ; x++)
601 for (y=0 ; y<=1 ; y++)
603 start[0] = x ? maxs[0] : mins[0];
604 start[1] = y ? maxs[1] : mins[1];
605 if (!(CL_PointSuperContents(start) & (SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY)))
610 PRVM_G_FLOAT(OFS_RETURN) = true;
611 return; // we got out easy
616 // check it for real...
620 // the midpoint must be within 16 of the bottom
621 start[0] = stop[0] = (mins[0] + maxs[0])*0.5;
622 start[1] = stop[1] = (mins[1] + maxs[1])*0.5;
623 stop[2] = start[2] - 2*sv_stepheight.value;
624 trace = CL_TraceLine(start, stop, MOVE_NORMAL, ent, CL_GenericHitSuperContentsMask(ent), true, true, NULL, true, false);
626 if (trace.fraction == 1.0)
629 mid = bottom = trace.endpos[2];
631 // the corners must be within 16 of the midpoint
632 for (x=0 ; x<=1 ; x++)
633 for (y=0 ; y<=1 ; y++)
635 start[0] = stop[0] = x ? maxs[0] : mins[0];
636 start[1] = stop[1] = y ? maxs[1] : mins[1];
638 trace = CL_TraceLine(start, stop, MOVE_NORMAL, ent, CL_GenericHitSuperContentsMask(ent), true, true, NULL, true, false);
640 if (trace.fraction != 1.0 && trace.endpos[2] > bottom)
641 bottom = trace.endpos[2];
642 if (trace.fraction == 1.0 || mid - trace.endpos[2] > sv_stepheight.value)
647 PRVM_G_FLOAT(OFS_RETURN) = true;
650 // #41 float(vector v) pointcontents
651 static void VM_CL_pointcontents (void)
653 VM_SAFEPARMCOUNT(1, VM_CL_pointcontents);
654 PRVM_G_FLOAT(OFS_RETURN) = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, CL_PointSuperContents(PRVM_G_VECTOR(OFS_PARM0)));
657 // #48 void(vector o, vector d, float color, float count) particle
658 static void VM_CL_particle (void)
663 VM_SAFEPARMCOUNT(4, VM_CL_particle);
665 org = PRVM_G_VECTOR(OFS_PARM0);
666 dir = PRVM_G_VECTOR(OFS_PARM1);
667 color = (int)PRVM_G_FLOAT(OFS_PARM2);
668 count = (int)PRVM_G_FLOAT(OFS_PARM3);
669 CL_ParticleEffect(EFFECT_SVC_PARTICLE, count, org, org, dir, dir, NULL, color);
672 // #74 void(vector pos, string samp, float vol, float atten) ambientsound
673 static void VM_CL_ambientsound (void)
677 VM_SAFEPARMCOUNT(4, VM_CL_ambientsound);
678 s = S_FindName(PRVM_G_STRING(OFS_PARM0));
679 f = PRVM_G_VECTOR(OFS_PARM1);
680 S_StaticSound (s, f, PRVM_G_FLOAT(OFS_PARM2), PRVM_G_FLOAT(OFS_PARM3)*64);
683 // #92 vector(vector org[, float lpflag]) getlight (DP_QC_GETLIGHT)
684 static void VM_CL_getlight (void)
686 vec3_t ambientcolor, diffusecolor, diffusenormal;
689 VM_SAFEPARMCOUNTRANGE(1, 3, VM_CL_getlight);
691 p = PRVM_G_VECTOR(OFS_PARM0);
692 VectorClear(ambientcolor);
693 VectorClear(diffusecolor);
694 VectorClear(diffusenormal);
696 R_CompleteLightPoint(ambientcolor, diffusecolor, diffusenormal, p, PRVM_G_FLOAT(OFS_PARM1));
697 else if (cl.worldmodel && cl.worldmodel->brush.LightPoint)
698 cl.worldmodel->brush.LightPoint(cl.worldmodel, p, ambientcolor, diffusecolor, diffusenormal);
699 VectorMA(ambientcolor, 0.5, diffusecolor, PRVM_G_VECTOR(OFS_RETURN));
700 if (PRVM_clientglobalvector(getlight_ambient))
701 VectorCopy(ambientcolor, PRVM_clientglobalvector(getlight_ambient));
702 if (PRVM_clientglobalvector(getlight_diffuse))
703 VectorCopy(diffusecolor, PRVM_clientglobalvector(getlight_diffuse));
704 if (PRVM_clientglobalvector(getlight_dir))
705 VectorCopy(diffusenormal, PRVM_clientglobalvector(getlight_dir));
708 //============================================================================
709 //[515]: SCENE MANAGER builtins
710 extern qboolean CSQC_AddRenderEdict (prvm_edict_t *ed, int edictnum);//csprogs.c
712 void CSQC_R_RecalcView (void)
714 extern matrix4x4_t viewmodelmatrix_nobob;
715 extern matrix4x4_t viewmodelmatrix_withbob;
716 Matrix4x4_CreateFromQuakeEntity(&r_refdef.view.matrix, cl.csqc_vieworigin[0], cl.csqc_vieworigin[1], cl.csqc_vieworigin[2], cl.csqc_viewangles[0], cl.csqc_viewangles[1], cl.csqc_viewangles[2], 1);
717 Matrix4x4_Copy(&viewmodelmatrix_nobob, &r_refdef.view.matrix);
718 Matrix4x4_ConcatScale(&viewmodelmatrix_nobob, cl_viewmodel_scale.value);
719 Matrix4x4_Concat(&viewmodelmatrix_withbob, &r_refdef.view.matrix, &cl.csqc_viewmodelmatrixfromengine);
722 void CL_RelinkLightFlashes(void);
723 //#300 void() clearscene (EXT_CSQC)
724 void VM_CL_R_ClearScene (void)
726 VM_SAFEPARMCOUNT(0, VM_CL_R_ClearScene);
727 // clear renderable entity and light lists
728 r_refdef.scene.numentities = 0;
729 r_refdef.scene.numlights = 0;
730 // restore the view settings to the values that VM_CL_UpdateView received from the client code
731 r_refdef.view = csqc_original_r_refdef_view;
732 VectorCopy(cl.csqc_vieworiginfromengine, cl.csqc_vieworigin);
733 VectorCopy(cl.csqc_viewanglesfromengine, cl.csqc_viewangles);
734 cl.csqc_vidvars.drawworld = r_drawworld.integer != 0;
735 cl.csqc_vidvars.drawenginesbar = false;
736 cl.csqc_vidvars.drawcrosshair = false;
740 //#301 void(float mask) addentities (EXT_CSQC)
741 extern void CSQC_Predraw (prvm_edict_t *ed);//csprogs.c
742 extern void CSQC_Think (prvm_edict_t *ed);//csprogs.c
743 void VM_CL_R_AddEntities (void)
745 double t = Sys_DoubleTime();
748 VM_SAFEPARMCOUNT(1, VM_CL_R_AddEntities);
749 drawmask = (int)PRVM_G_FLOAT(OFS_PARM0);
750 CSQC_RelinkAllEntities(drawmask);
751 CL_RelinkLightFlashes();
753 PRVM_clientglobalfloat(time) = cl.time;
754 for(i=1;i<prog->num_edicts;i++)
756 // so we can easily check if CSQC entity #edictnum is currently drawn
757 cl.csqcrenderentities[i].entitynumber = 0;
758 ed = &prog->edicts[i];
759 if(ed->priv.required->free)
762 if(ed->priv.required->free)
764 // note that for RF_USEAXIS entities, Predraw sets v_forward/v_right/v_up globals that are read by CSQC_AddRenderEdict
766 if(ed->priv.required->free)
768 if(!((int)PRVM_clientedictfloat(ed, drawmask) & drawmask))
770 CSQC_AddRenderEdict(ed, i);
773 // callprofile fixing hack: do not include this time in what is counted for CSQC_UpdateView
774 prog->functions[PRVM_clientfunction(CSQC_UpdateView)].totaltime -= Sys_DoubleTime() - t;
777 //#302 void(entity ent) addentity (EXT_CSQC)
778 void VM_CL_R_AddEntity (void)
780 double t = Sys_DoubleTime();
781 VM_SAFEPARMCOUNT(1, VM_CL_R_AddEntity);
782 CSQC_AddRenderEdict(PRVM_G_EDICT(OFS_PARM0), 0);
783 prog->functions[PRVM_clientfunction(CSQC_UpdateView)].totaltime -= Sys_DoubleTime() - t;
786 //#303 float(float property, ...) setproperty (EXT_CSQC)
787 //#303 float(float property) getproperty
788 //#303 vector(float property) getpropertyvec
789 //#309 float(float property) getproperty
790 //#309 vector(float property) getpropertyvec
791 // VorteX: make this function be able to return previously set property if new value is not given
792 void VM_CL_R_SetView (void)
798 VM_SAFEPARMCOUNTRANGE(1, 3, VM_CL_R_SetView);
800 c = (int)PRVM_G_FLOAT(OFS_PARM0);
808 VectorSet(PRVM_G_VECTOR(OFS_RETURN), r_refdef.view.x, r_refdef.view.y, 0);
811 PRVM_G_FLOAT(OFS_RETURN) = r_refdef.view.x;
814 PRVM_G_FLOAT(OFS_RETURN) = r_refdef.view.y;
817 VectorSet(PRVM_G_VECTOR(OFS_RETURN), r_refdef.view.width, r_refdef.view.height, 0);
820 PRVM_G_FLOAT(OFS_RETURN) = r_refdef.view.width;
823 PRVM_G_FLOAT(OFS_RETURN) = r_refdef.view.height;
826 VM_Warning("VM_CL_R_GetView : VF_VIEWPORT can't be retrieved, use VF_MIN/VF_SIZE instead\n");
829 VectorSet(PRVM_G_VECTOR(OFS_RETURN), r_refdef.view.ortho_x, r_refdef.view.ortho_y, 0);
832 PRVM_G_FLOAT(OFS_RETURN) = r_refdef.view.ortho_x;
835 PRVM_G_FLOAT(OFS_RETURN) = r_refdef.view.ortho_y;
838 VectorCopy(cl.csqc_vieworigin, PRVM_G_VECTOR(OFS_RETURN));
841 PRVM_G_FLOAT(OFS_RETURN) = cl.csqc_vieworigin[0];
844 PRVM_G_FLOAT(OFS_RETURN) = cl.csqc_vieworigin[1];
847 PRVM_G_FLOAT(OFS_RETURN) = cl.csqc_vieworigin[2];
850 VectorCopy(cl.csqc_viewangles, PRVM_G_VECTOR(OFS_RETURN));
853 PRVM_G_FLOAT(OFS_RETURN) = cl.csqc_viewangles[0];
856 PRVM_G_FLOAT(OFS_RETURN) = cl.csqc_viewangles[1];
859 PRVM_G_FLOAT(OFS_RETURN) = cl.csqc_viewangles[2];
862 PRVM_G_FLOAT(OFS_RETURN) = cl.csqc_vidvars.drawworld;
864 case VF_DRAWENGINESBAR:
865 PRVM_G_FLOAT(OFS_RETURN) = cl.csqc_vidvars.drawenginesbar;
867 case VF_DRAWCROSSHAIR:
868 PRVM_G_FLOAT(OFS_RETURN) = cl.csqc_vidvars.drawcrosshair;
870 case VF_CL_VIEWANGLES:
871 VectorCopy(cl.viewangles, PRVM_G_VECTOR(OFS_RETURN));;
873 case VF_CL_VIEWANGLES_X:
874 PRVM_G_FLOAT(OFS_RETURN) = cl.viewangles[0];
876 case VF_CL_VIEWANGLES_Y:
877 PRVM_G_FLOAT(OFS_RETURN) = cl.viewangles[1];
879 case VF_CL_VIEWANGLES_Z:
880 PRVM_G_FLOAT(OFS_RETURN) = cl.viewangles[2];
883 PRVM_G_FLOAT(OFS_RETURN) = r_refdef.view.useperspective;
886 PRVM_G_FLOAT(OFS_RETURN) = r_refdef.view.isoverlay;
889 PRVM_G_FLOAT(OFS_RETURN) = r_refdef.view.ismain;
892 PRVM_G_FLOAT(OFS_RETURN) = r_refdef.fog_density;
895 PRVM_G_VECTOR(OFS_RETURN)[0] = r_refdef.fog_red;
896 PRVM_G_VECTOR(OFS_RETURN)[1] = r_refdef.fog_green;
897 PRVM_G_VECTOR(OFS_RETURN)[2] = r_refdef.fog_blue;
900 PRVM_G_VECTOR(OFS_RETURN)[0] = r_refdef.fog_red;
903 PRVM_G_VECTOR(OFS_RETURN)[1] = r_refdef.fog_green;
906 PRVM_G_VECTOR(OFS_RETURN)[2] = r_refdef.fog_blue;
909 PRVM_G_FLOAT(OFS_RETURN) = r_refdef.fog_alpha;
912 PRVM_G_FLOAT(OFS_RETURN) = r_refdef.fog_start;
915 PRVM_G_FLOAT(OFS_RETURN) = r_refdef.fog_end;
918 PRVM_G_FLOAT(OFS_RETURN) = r_refdef.fog_height;
920 case VF_FOG_FADEDEPTH:
921 PRVM_G_FLOAT(OFS_RETURN) = r_refdef.fog_fadedepth;
924 PRVM_G_FLOAT(OFS_RETURN) = 0;
925 VM_Warning("VM_CL_R_GetView : unknown parm %i\n", c);
931 f = PRVM_G_VECTOR(OFS_PARM1);
932 k = PRVM_G_FLOAT(OFS_PARM1);
936 r_refdef.view.x = (int)(f[0]);
937 r_refdef.view.y = (int)(f[1]);
941 r_refdef.view.x = (int)(k);
945 r_refdef.view.y = (int)(k);
949 r_refdef.view.width = (int)(f[0]);
950 r_refdef.view.height = (int)(f[1]);
954 r_refdef.view.width = (int)(k);
958 r_refdef.view.height = (int)(k);
962 r_refdef.view.x = (int)(f[0]);
963 r_refdef.view.y = (int)(f[1]);
964 f = PRVM_G_VECTOR(OFS_PARM2);
965 r_refdef.view.width = (int)(f[0]);
966 r_refdef.view.height = (int)(f[1]);
970 r_refdef.view.frustum_x = tan(f[0] * M_PI / 360.0);r_refdef.view.ortho_x = f[0];
971 r_refdef.view.frustum_y = tan(f[1] * M_PI / 360.0);r_refdef.view.ortho_y = f[1];
974 r_refdef.view.frustum_x = tan(k * M_PI / 360.0);r_refdef.view.ortho_x = k;
977 r_refdef.view.frustum_y = tan(k * M_PI / 360.0);r_refdef.view.ortho_y = k;
980 VectorCopy(f, cl.csqc_vieworigin);
984 cl.csqc_vieworigin[0] = k;
988 cl.csqc_vieworigin[1] = k;
992 cl.csqc_vieworigin[2] = k;
996 VectorCopy(f, cl.csqc_viewangles);
1000 cl.csqc_viewangles[0] = k;
1001 CSQC_R_RecalcView();
1004 cl.csqc_viewangles[1] = k;
1005 CSQC_R_RecalcView();
1008 cl.csqc_viewangles[2] = k;
1009 CSQC_R_RecalcView();
1012 cl.csqc_vidvars.drawworld = ((k != 0) && r_drawworld.integer);
1014 case VF_DRAWENGINESBAR:
1015 cl.csqc_vidvars.drawenginesbar = k != 0;
1017 case VF_DRAWCROSSHAIR:
1018 cl.csqc_vidvars.drawcrosshair = k != 0;
1020 case VF_CL_VIEWANGLES:
1021 VectorCopy(f, cl.viewangles);
1023 case VF_CL_VIEWANGLES_X:
1024 cl.viewangles[0] = k;
1026 case VF_CL_VIEWANGLES_Y:
1027 cl.viewangles[1] = k;
1029 case VF_CL_VIEWANGLES_Z:
1030 cl.viewangles[2] = k;
1032 case VF_PERSPECTIVE:
1033 r_refdef.view.useperspective = k != 0;
1035 case VF_CLEARSCREEN:
1036 r_refdef.view.isoverlay = !k;
1039 PRVM_G_FLOAT(OFS_RETURN) = r_refdef.view.ismain;
1041 case VF_FOG_DENSITY:
1042 r_refdef.fog_density = k;
1045 r_refdef.fog_red = f[0];
1046 r_refdef.fog_green = f[1];
1047 r_refdef.fog_blue = f[2];
1049 case VF_FOG_COLOR_R:
1050 r_refdef.fog_red = k;
1052 case VF_FOG_COLOR_G:
1053 r_refdef.fog_green = k;
1055 case VF_FOG_COLOR_B:
1056 r_refdef.fog_blue = k;
1059 r_refdef.fog_alpha = k;
1062 r_refdef.fog_start = k;
1065 r_refdef.fog_end = k;
1068 r_refdef.fog_height = k;
1070 case VF_FOG_FADEDEPTH:
1071 r_refdef.fog_fadedepth = k;
1074 PRVM_G_FLOAT(OFS_RETURN) = 0;
1075 VM_Warning("VM_CL_R_SetView : unknown parm %i\n", c);
1078 PRVM_G_FLOAT(OFS_RETURN) = 1;
1081 //#305 void(vector org, float radius, vector lightcolours[, float style, string cubemapname, float pflags]) adddynamiclight (EXT_CSQC)
1082 void VM_CL_R_AddDynamicLight (void)
1084 double t = Sys_DoubleTime();
1089 const char *cubemapname = NULL;
1090 int pflags = PFLAGS_CORONA | PFLAGS_FULLDYNAMIC;
1091 float coronaintensity = 1;
1092 float coronasizescale = 0.25;
1093 qboolean castshadow = true;
1094 float ambientscale = 0;
1095 float diffusescale = 1;
1096 float specularscale = 1;
1098 vec3_t forward, left, up;
1099 VM_SAFEPARMCOUNTRANGE(3, 8, VM_CL_R_AddDynamicLight);
1101 // if we've run out of dlights, just return
1102 if (r_refdef.scene.numlights >= MAX_DLIGHTS)
1105 org = PRVM_G_VECTOR(OFS_PARM0);
1106 radius = PRVM_G_FLOAT(OFS_PARM1);
1107 col = PRVM_G_VECTOR(OFS_PARM2);
1108 if (prog->argc >= 4)
1110 style = (int)PRVM_G_FLOAT(OFS_PARM3);
1111 if (style >= MAX_LIGHTSTYLES)
1113 Con_DPrintf("VM_CL_R_AddDynamicLight: out of bounds lightstyle index %i\n", style);
1117 if (prog->argc >= 5)
1118 cubemapname = PRVM_G_STRING(OFS_PARM4);
1119 if (prog->argc >= 6)
1120 pflags = (int)PRVM_G_FLOAT(OFS_PARM5);
1121 coronaintensity = (pflags & PFLAGS_CORONA) != 0;
1122 castshadow = (pflags & PFLAGS_NOSHADOW) == 0;
1124 VectorScale(PRVM_clientglobalvector(v_forward), radius, forward);
1125 VectorScale(PRVM_clientglobalvector(v_right), -radius, left);
1126 VectorScale(PRVM_clientglobalvector(v_up), radius, up);
1127 Matrix4x4_FromVectors(&matrix, forward, left, up, org);
1129 R_RTLight_Update(&r_refdef.scene.templights[r_refdef.scene.numlights], false, &matrix, col, style, cubemapname, castshadow, coronaintensity, coronasizescale, ambientscale, diffusescale, specularscale, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
1130 r_refdef.scene.lights[r_refdef.scene.numlights] = &r_refdef.scene.templights[r_refdef.scene.numlights];r_refdef.scene.numlights++;
1131 prog->functions[PRVM_clientfunction(CSQC_UpdateView)].totaltime -= Sys_DoubleTime() - t;
1134 //============================================================================
1136 //#310 vector (vector v) cs_unproject (EXT_CSQC)
1137 static void VM_CL_unproject (void)
1142 VM_SAFEPARMCOUNT(1, VM_CL_unproject);
1143 f = PRVM_G_VECTOR(OFS_PARM0);
1146 (-1.0 + 2.0 * (f[0] / vid_conwidth.integer)) * f[2] * -r_refdef.view.frustum_x,
1147 (-1.0 + 2.0 * (f[1] / vid_conheight.integer)) * f[2] * -r_refdef.view.frustum_y);
1148 if(v_flipped.integer)
1150 Matrix4x4_Transform(&r_refdef.view.matrix, temp, PRVM_G_VECTOR(OFS_RETURN));
1153 //#311 vector (vector v) cs_project (EXT_CSQC)
1154 static void VM_CL_project (void)
1160 VM_SAFEPARMCOUNT(1, VM_CL_project);
1161 f = PRVM_G_VECTOR(OFS_PARM0);
1162 Matrix4x4_Invert_Simple(&m, &r_refdef.view.matrix);
1163 Matrix4x4_Transform(&m, f, v);
1164 if(v_flipped.integer)
1166 VectorSet(PRVM_G_VECTOR(OFS_RETURN),
1167 vid_conwidth.integer * (0.5*(1.0+v[1]/v[0]/-r_refdef.view.frustum_x)),
1168 vid_conheight.integer * (0.5*(1.0+v[2]/v[0]/-r_refdef.view.frustum_y)),
1171 // after transforming, relative position to viewport (0..1) = 0.5 * (1 + v[2]/v[0]/-frustum_{x \or y})
1172 // as 2D drawing honors the viewport too, to get the same pixel, we simply multiply this by conwidth/height
1175 //#330 float(float stnum) getstatf (EXT_CSQC)
1176 static void VM_CL_getstatf (void)
1184 VM_SAFEPARMCOUNT(1, VM_CL_getstatf);
1185 i = (int)PRVM_G_FLOAT(OFS_PARM0);
1186 if(i < 0 || i >= MAX_CL_STATS)
1188 VM_Warning("VM_CL_getstatf: index>=MAX_CL_STATS or index<0\n");
1191 dat.l = cl.stats[i];
1192 PRVM_G_FLOAT(OFS_RETURN) = dat.f;
1195 //#331 float(float stnum) getstati (EXT_CSQC)
1196 static void VM_CL_getstati (void)
1199 int firstbit, bitcount;
1201 VM_SAFEPARMCOUNTRANGE(1, 3, VM_CL_getstati);
1203 index = (int)PRVM_G_FLOAT(OFS_PARM0);
1206 firstbit = (int)PRVM_G_FLOAT(OFS_PARM1);
1208 bitcount = (int)PRVM_G_FLOAT(OFS_PARM2);
1218 if(index < 0 || index >= MAX_CL_STATS)
1220 VM_Warning("VM_CL_getstati: index>=MAX_CL_STATS or index<0\n");
1223 i = cl.stats[index];
1224 if (bitcount != 32) //32 causes the mask to overflow, so there's nothing to subtract from.
1225 i = (((unsigned int)i)&(((1<<bitcount)-1)<<firstbit))>>firstbit;
1226 PRVM_G_FLOAT(OFS_RETURN) = i;
1229 //#332 string(float firststnum) getstats (EXT_CSQC)
1230 static void VM_CL_getstats (void)
1234 VM_SAFEPARMCOUNT(1, VM_CL_getstats);
1235 i = (int)PRVM_G_FLOAT(OFS_PARM0);
1236 if(i < 0 || i > MAX_CL_STATS-4)
1238 PRVM_G_INT(OFS_RETURN) = OFS_NULL;
1239 VM_Warning("VM_CL_getstats: index>MAX_CL_STATS-4 or index<0\n");
1242 strlcpy(t, (char*)&cl.stats[i], sizeof(t));
1243 PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(t);
1246 //#333 void(entity e, float mdlindex) setmodelindex (EXT_CSQC)
1247 static void VM_CL_setmodelindex (void)
1251 struct model_s *model;
1253 VM_SAFEPARMCOUNT(2, VM_CL_setmodelindex);
1255 t = PRVM_G_EDICT(OFS_PARM0);
1257 i = (int)PRVM_G_FLOAT(OFS_PARM1);
1259 PRVM_clientedictstring(t, model) = 0;
1260 PRVM_clientedictfloat(t, modelindex) = 0;
1265 model = CL_GetModelByIndex(i);
1268 VM_Warning("VM_CL_setmodelindex: null model\n");
1271 PRVM_clientedictstring(t, model) = PRVM_SetEngineString(model->name);
1272 PRVM_clientedictfloat(t, modelindex) = i;
1274 // TODO: check if this breaks needed consistency and maybe add a cvar for it too?? [1/10/2008 Black]
1277 SetMinMaxSize (t, model->normalmins, model->normalmaxs);
1280 SetMinMaxSize (t, vec3_origin, vec3_origin);
1283 //#334 string(float mdlindex) modelnameforindex (EXT_CSQC)
1284 static void VM_CL_modelnameforindex (void)
1288 VM_SAFEPARMCOUNT(1, VM_CL_modelnameforindex);
1290 PRVM_G_INT(OFS_RETURN) = OFS_NULL;
1291 model = CL_GetModelByIndex((int)PRVM_G_FLOAT(OFS_PARM0));
1292 PRVM_G_INT(OFS_RETURN) = model ? PRVM_SetEngineString(model->name) : 0;
1295 //#335 float(string effectname) particleeffectnum (EXT_CSQC)
1296 static void VM_CL_particleeffectnum (void)
1299 VM_SAFEPARMCOUNT(1, VM_CL_particleeffectnum);
1300 i = CL_ParticleEffectIndexForName(PRVM_G_STRING(OFS_PARM0));
1303 PRVM_G_FLOAT(OFS_RETURN) = i;
1306 // #336 void(entity ent, float effectnum, vector start, vector end[, float color]) trailparticles (EXT_CSQC)
1307 static void VM_CL_trailparticles (void)
1312 VM_SAFEPARMCOUNTRANGE(4, 5, VM_CL_trailparticles);
1314 t = PRVM_G_EDICT(OFS_PARM0);
1315 i = (int)PRVM_G_FLOAT(OFS_PARM1);
1316 start = PRVM_G_VECTOR(OFS_PARM2);
1317 end = PRVM_G_VECTOR(OFS_PARM3);
1321 CL_ParticleEffect(i, 1, start, end, PRVM_clientedictvector(t, velocity), PRVM_clientedictvector(t, velocity), NULL, prog->argc >= 5 ? (int)PRVM_G_FLOAT(OFS_PARM4) : 0);
1324 //#337 void(float effectnum, vector origin, vector dir, float count[, float color]) pointparticles (EXT_CSQC)
1325 static void VM_CL_pointparticles (void)
1330 VM_SAFEPARMCOUNTRANGE(4, 5, VM_CL_pointparticles);
1331 i = (int)PRVM_G_FLOAT(OFS_PARM0);
1332 f = PRVM_G_VECTOR(OFS_PARM1);
1333 v = PRVM_G_VECTOR(OFS_PARM2);
1334 n = PRVM_G_FLOAT(OFS_PARM3);
1337 CL_ParticleEffect(i, n, f, f, v, v, NULL, prog->argc >= 5 ? (int)PRVM_G_FLOAT(OFS_PARM4) : 0);
1340 //#502 void(float effectnum, entity own, vector origin_from, vector origin_to, vector dir_from, vector dir_to, float count, float extflags) boxparticles (DP_CSQC_BOXPARTICLES)
1341 static void VM_CL_boxparticles (void)
1344 // prvm_edict_t *own;
1345 float *origin_from, *origin_to, *dir_from, *dir_to;
1348 float tintmins[4], tintmaxs[4];
1349 VM_SAFEPARMCOUNTRANGE(7, 8, VM_CL_boxparticles);
1351 effectnum = (int)PRVM_G_FLOAT(OFS_PARM0);
1352 // own = PRVM_G_EDICT(OFS_PARM1); // TODO find use for this
1353 origin_from = PRVM_G_VECTOR(OFS_PARM2);
1354 origin_to = PRVM_G_VECTOR(OFS_PARM3);
1355 dir_from = PRVM_G_VECTOR(OFS_PARM4);
1356 dir_to = PRVM_G_VECTOR(OFS_PARM5);
1357 count = PRVM_G_FLOAT(OFS_PARM6);
1359 flags = PRVM_G_FLOAT(OFS_PARM7);
1362 Vector4Set(tintmins, 1, 1, 1, 1);
1363 Vector4Set(tintmaxs, 1, 1, 1, 1);
1364 if(flags & 1) // read alpha
1366 tintmins[3] = PRVM_clientglobalfloat(particles_alphamin);
1367 tintmaxs[3] = PRVM_clientglobalfloat(particles_alphamax);
1369 if(flags & 2) // read color
1371 VectorCopy(PRVM_clientglobalvector(particles_colormin), tintmins);
1372 VectorCopy(PRVM_clientglobalvector(particles_colormax), tintmaxs);
1376 CL_ParticleTrail(effectnum, count, origin_from, origin_to, dir_from, dir_to, NULL, 0, true, true, tintmins, tintmaxs);
1379 //#531 void(float pause) setpause
1380 static void VM_CL_setpause(void)
1382 VM_SAFEPARMCOUNT(1, VM_CL_setpause);
1383 if ((int)PRVM_G_FLOAT(OFS_PARM0) != 0)
1384 cl.csqc_paused = true;
1386 cl.csqc_paused = false;
1389 //#343 void(float usecursor) setcursormode (DP_CSQC)
1390 static void VM_CL_setcursormode (void)
1392 VM_SAFEPARMCOUNT(1, VM_CL_setcursormode);
1393 cl.csqc_wantsmousemove = PRVM_G_FLOAT(OFS_PARM0) != 0;
1394 cl_ignoremousemoves = 2;
1397 //#344 vector() getmousepos (DP_CSQC)
1398 static void VM_CL_getmousepos(void)
1400 VM_SAFEPARMCOUNT(0,VM_CL_getmousepos);
1402 if (key_consoleactive || key_dest != key_game)
1403 VectorSet(PRVM_G_VECTOR(OFS_RETURN), 0, 0, 0);
1404 else if (cl.csqc_wantsmousemove)
1405 VectorSet(PRVM_G_VECTOR(OFS_RETURN), in_windowmouse_x * vid_conwidth.integer / vid.width, in_windowmouse_y * vid_conheight.integer / vid.height, 0);
1407 VectorSet(PRVM_G_VECTOR(OFS_RETURN), in_mouse_x * vid_conwidth.integer / vid.width, in_mouse_y * vid_conheight.integer / vid.height, 0);
1410 //#345 float(float framenum) getinputstate (EXT_CSQC)
1411 static void VM_CL_getinputstate (void)
1414 VM_SAFEPARMCOUNT(1, VM_CL_getinputstate);
1415 frame = (int)PRVM_G_FLOAT(OFS_PARM0);
1416 PRVM_G_FLOAT(OFS_RETURN) = false;
1417 for (i = 0;i < CL_MAX_USERCMDS;i++)
1419 if (cl.movecmd[i].sequence == frame)
1421 VectorCopy(cl.movecmd[i].viewangles, PRVM_clientglobalvector(input_angles));
1422 PRVM_clientglobalfloat(input_buttons) = cl.movecmd[i].buttons; // FIXME: this should not be directly exposed to csqc (translation layer needed?)
1423 PRVM_clientglobalvector(input_movevalues)[0] = cl.movecmd[i].forwardmove;
1424 PRVM_clientglobalvector(input_movevalues)[1] = cl.movecmd[i].sidemove;
1425 PRVM_clientglobalvector(input_movevalues)[2] = cl.movecmd[i].upmove;
1426 PRVM_clientglobalfloat(input_timelength) = cl.movecmd[i].frametime;
1427 if(cl.movecmd[i].crouch)
1429 VectorCopy(cl.playercrouchmins, PRVM_clientglobalvector(pmove_mins));
1430 VectorCopy(cl.playercrouchmaxs, PRVM_clientglobalvector(pmove_maxs));
1434 VectorCopy(cl.playerstandmins, PRVM_clientglobalvector(pmove_mins));
1435 VectorCopy(cl.playerstandmaxs, PRVM_clientglobalvector(pmove_maxs));
1437 PRVM_G_FLOAT(OFS_RETURN) = true;
1442 //#346 void(float sens) setsensitivityscaler (EXT_CSQC)
1443 static void VM_CL_setsensitivityscale (void)
1445 VM_SAFEPARMCOUNT(1, VM_CL_setsensitivityscale);
1446 cl.sensitivityscale = PRVM_G_FLOAT(OFS_PARM0);
1449 //#347 void() runstandardplayerphysics (EXT_CSQC)
1450 static void VM_CL_runplayerphysics (void)
1454 //#348 string(float playernum, string keyname) getplayerkeyvalue (EXT_CSQC)
1455 static void VM_CL_getplayerkey (void)
1461 VM_SAFEPARMCOUNT(2, VM_CL_getplayerkey);
1463 i = (int)PRVM_G_FLOAT(OFS_PARM0);
1464 c = PRVM_G_STRING(OFS_PARM1);
1465 PRVM_G_INT(OFS_RETURN) = OFS_NULL;
1469 i = Sbar_GetSortedPlayerIndex(-1-i);
1470 if(i < 0 || i >= cl.maxclients)
1475 if(!strcasecmp(c, "name"))
1476 strlcpy(t, cl.scores[i].name, sizeof(t));
1478 if(!strcasecmp(c, "frags"))
1479 dpsnprintf(t, sizeof(t), "%i", cl.scores[i].frags);
1481 if(!strcasecmp(c, "ping"))
1482 dpsnprintf(t, sizeof(t), "%i", cl.scores[i].qw_ping);
1484 if(!strcasecmp(c, "pl"))
1485 dpsnprintf(t, sizeof(t), "%i", cl.scores[i].qw_packetloss);
1487 if(!strcasecmp(c, "movementloss"))
1488 dpsnprintf(t, sizeof(t), "%i", cl.scores[i].qw_movementloss);
1490 if(!strcasecmp(c, "entertime"))
1491 dpsnprintf(t, sizeof(t), "%f", cl.scores[i].qw_entertime);
1493 if(!strcasecmp(c, "colors"))
1494 dpsnprintf(t, sizeof(t), "%i", cl.scores[i].colors);
1496 if(!strcasecmp(c, "topcolor"))
1497 dpsnprintf(t, sizeof(t), "%i", cl.scores[i].colors & 0xf0);
1499 if(!strcasecmp(c, "bottomcolor"))
1500 dpsnprintf(t, sizeof(t), "%i", (cl.scores[i].colors &15)<<4);
1502 if(!strcasecmp(c, "viewentity"))
1503 dpsnprintf(t, sizeof(t), "%i", i+1);
1505 if(gamemode == GAME_XONOTIC && !strcasecmp(c, "TEMPHACK_origin"))
1507 // PLEASE REMOVE THIS once deltalisten() of EXT_CSQC_1
1508 // is implemented, or Xonotic uses CSQC-networked
1509 // players, whichever comes first
1510 entity_t *e = cl.entities + (i+1);
1511 if(e->state_current.active)
1514 Matrix4x4_OriginFromMatrix(&e->render.matrix, origin);
1515 dpsnprintf(t, sizeof(t), "%.9g %.9g %.9g", origin[0], origin[1], origin[2]);
1520 PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(t);
1523 //#351 void(vector origin, vector forward, vector right, vector up) SetListener (EXT_CSQC)
1524 static void VM_CL_setlistener (void)
1526 VM_SAFEPARMCOUNT(4, VM_CL_setlistener);
1527 Matrix4x4_FromVectors(&cl.csqc_listenermatrix, PRVM_G_VECTOR(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM2), PRVM_G_VECTOR(OFS_PARM3), PRVM_G_VECTOR(OFS_PARM0));
1528 cl.csqc_usecsqclistener = true; //use csqc listener at this frame
1531 //#352 void(string cmdname) registercommand (EXT_CSQC)
1532 static void VM_CL_registercmd (void)
1535 VM_SAFEPARMCOUNT(1, VM_CL_registercmd);
1536 if(!Cmd_Exists(PRVM_G_STRING(OFS_PARM0)))
1540 alloclen = strlen(PRVM_G_STRING(OFS_PARM0)) + 1;
1541 t = (char *)Z_Malloc(alloclen);
1542 memcpy(t, PRVM_G_STRING(OFS_PARM0), alloclen);
1543 Cmd_AddCommand(t, NULL, "console command created by QuakeC");
1546 Cmd_AddCommand(PRVM_G_STRING(OFS_PARM0), NULL, "console command created by QuakeC");
1550 //#360 float() readbyte (EXT_CSQC)
1551 static void VM_CL_ReadByte (void)
1553 VM_SAFEPARMCOUNT(0, VM_CL_ReadByte);
1554 PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadByte();
1557 //#361 float() readchar (EXT_CSQC)
1558 static void VM_CL_ReadChar (void)
1560 VM_SAFEPARMCOUNT(0, VM_CL_ReadChar);
1561 PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadChar();
1564 //#362 float() readshort (EXT_CSQC)
1565 static void VM_CL_ReadShort (void)
1567 VM_SAFEPARMCOUNT(0, VM_CL_ReadShort);
1568 PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadShort();
1571 //#363 float() readlong (EXT_CSQC)
1572 static void VM_CL_ReadLong (void)
1574 VM_SAFEPARMCOUNT(0, VM_CL_ReadLong);
1575 PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadLong();
1578 //#364 float() readcoord (EXT_CSQC)
1579 static void VM_CL_ReadCoord (void)
1581 VM_SAFEPARMCOUNT(0, VM_CL_ReadCoord);
1582 PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadCoord(cls.protocol);
1585 //#365 float() readangle (EXT_CSQC)
1586 static void VM_CL_ReadAngle (void)
1588 VM_SAFEPARMCOUNT(0, VM_CL_ReadAngle);
1589 PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadAngle(cls.protocol);
1592 //#366 string() readstring (EXT_CSQC)
1593 static void VM_CL_ReadString (void)
1595 VM_SAFEPARMCOUNT(0, VM_CL_ReadString);
1596 PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(MSG_ReadString());
1599 //#367 float() readfloat (EXT_CSQC)
1600 static void VM_CL_ReadFloat (void)
1602 VM_SAFEPARMCOUNT(0, VM_CL_ReadFloat);
1603 PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadFloat();
1606 //#501 string() readpicture (DP_CSQC_READWRITEPICTURE)
1607 extern cvar_t cl_readpicture_force;
1608 static void VM_CL_ReadPicture (void)
1611 unsigned char *data;
1617 VM_SAFEPARMCOUNT(0, VM_CL_ReadPicture);
1619 name = MSG_ReadString();
1620 size = MSG_ReadShort();
1622 // check if a texture of that name exists
1623 // if yes, it is used and the data is discarded
1624 // if not, the (low quality) data is used to build a new texture, whose name will get returned
1626 pic = Draw_CachePic_Flags (name, CACHEPICFLAG_NOTPERSISTENT);
1630 if(pic->tex == r_texture_notexture)
1631 pic->tex = NULL; // don't overwrite the notexture by Draw_NewPic
1632 if(pic->tex && !cl_readpicture_force.integer)
1634 // texture found and loaded
1635 // skip over the jpeg as we don't need it
1636 for(i = 0; i < size; ++i)
1637 (void) MSG_ReadByte();
1641 // texture not found
1642 // use the attached jpeg as texture
1643 buf = (unsigned char *) Mem_Alloc(tempmempool, size);
1644 MSG_ReadBytes(size, buf);
1645 data = JPEG_LoadImage_BGRA(buf, size, NULL);
1647 Draw_NewPic(name, image_width, image_height, false, data);
1652 PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(name);
1655 //////////////////////////////////////////////////////////
1657 static void VM_CL_makestatic (void)
1661 VM_SAFEPARMCOUNT(1, VM_CL_makestatic);
1663 ent = PRVM_G_EDICT(OFS_PARM0);
1664 if (ent == prog->edicts)
1666 VM_Warning("makestatic: can not modify world entity\n");
1669 if (ent->priv.server->free)
1671 VM_Warning("makestatic: can not modify free entity\n");
1675 if (cl.num_static_entities < cl.max_static_entities)
1678 entity_t *staticent = &cl.static_entities[cl.num_static_entities++];
1680 // copy it to the current state
1681 memset(staticent, 0, sizeof(*staticent));
1682 staticent->render.model = CL_GetModelByIndex((int)PRVM_clientedictfloat(ent, modelindex));
1683 staticent->render.framegroupblend[0].frame = (int)PRVM_clientedictfloat(ent, frame);
1684 staticent->render.framegroupblend[0].lerp = 1;
1685 // make torchs play out of sync
1686 staticent->render.framegroupblend[0].start = lhrandom(-10, -1);
1687 staticent->render.skinnum = (int)PRVM_clientedictfloat(ent, skin);
1688 staticent->render.effects = (int)PRVM_clientedictfloat(ent, effects);
1689 staticent->render.alpha = PRVM_clientedictfloat(ent, alpha);
1690 staticent->render.scale = PRVM_clientedictfloat(ent, scale);
1691 VectorCopy(PRVM_clientedictvector(ent, colormod), staticent->render.colormod);
1692 VectorCopy(PRVM_clientedictvector(ent, glowmod), staticent->render.glowmod);
1695 if (!staticent->render.alpha)
1696 staticent->render.alpha = 1.0f;
1697 if (!staticent->render.scale)
1698 staticent->render.scale = 1.0f;
1699 if (!VectorLength2(staticent->render.colormod))
1700 VectorSet(staticent->render.colormod, 1, 1, 1);
1701 if (!VectorLength2(staticent->render.glowmod))
1702 VectorSet(staticent->render.glowmod, 1, 1, 1);
1704 renderflags = (int)PRVM_clientedictfloat(ent, renderflags);
1705 if (renderflags & RF_USEAXIS)
1708 VectorNegate(PRVM_clientglobalvector(v_right), left);
1709 Matrix4x4_FromVectors(&staticent->render.matrix, PRVM_clientglobalvector(v_forward), left, PRVM_clientglobalvector(v_up), PRVM_clientedictvector(ent, origin));
1710 Matrix4x4_Scale(&staticent->render.matrix, staticent->render.scale, 1);
1713 Matrix4x4_CreateFromQuakeEntity(&staticent->render.matrix, PRVM_clientedictvector(ent, origin)[0], PRVM_clientedictvector(ent, origin)[1], PRVM_clientedictvector(ent, origin)[2], PRVM_clientedictvector(ent, angles)[0], PRVM_clientedictvector(ent, angles)[1], PRVM_clientedictvector(ent, angles)[2], staticent->render.scale);
1715 // either fullbright or lit
1716 if(!r_fullbright.integer)
1718 if (!(staticent->render.effects & EF_FULLBRIGHT))
1719 staticent->render.flags |= RENDER_LIGHT;
1720 else if(r_equalize_entities_fullbright.integer)
1721 staticent->render.flags |= RENDER_LIGHT | RENDER_EQUALIZE;
1723 // turn off shadows from transparent objects
1724 if (!(staticent->render.effects & (EF_NOSHADOW | EF_ADDITIVE | EF_NODEPTHTEST)) && (staticent->render.alpha >= 1))
1725 staticent->render.flags |= RENDER_SHADOW;
1726 if (staticent->render.effects & EF_NODEPTHTEST)
1727 staticent->render.flags |= RENDER_NODEPTHTEST;
1728 if (staticent->render.effects & EF_ADDITIVE)
1729 staticent->render.flags |= RENDER_ADDITIVE;
1730 if (staticent->render.effects & EF_DOUBLESIDED)
1731 staticent->render.flags |= RENDER_DOUBLESIDED;
1733 staticent->render.allowdecals = true;
1734 CL_UpdateRenderEntity(&staticent->render);
1737 Con_Printf("Too many static entities");
1739 // throw the entity away now
1743 //=================================================================//
1749 copies data from one entity to another
1751 copyentity(src, dst)
1754 static void VM_CL_copyentity (void)
1756 prvm_edict_t *in, *out;
1757 VM_SAFEPARMCOUNT(2, VM_CL_copyentity);
1758 in = PRVM_G_EDICT(OFS_PARM0);
1759 if (in == prog->edicts)
1761 VM_Warning("copyentity: can not read world entity\n");
1764 if (in->priv.server->free)
1766 VM_Warning("copyentity: can not read free entity\n");
1769 out = PRVM_G_EDICT(OFS_PARM1);
1770 if (out == prog->edicts)
1772 VM_Warning("copyentity: can not modify world entity\n");
1775 if (out->priv.server->free)
1777 VM_Warning("copyentity: can not modify free entity\n");
1780 memcpy(out->fields.vp, in->fields.vp, prog->entityfields * 4);
1784 //=================================================================//
1786 // #404 void(vector org, string modelname, float startframe, float endframe, float framerate) effect (DP_SV_EFFECT)
1787 static void VM_CL_effect (void)
1789 VM_SAFEPARMCOUNT(5, VM_CL_effect);
1790 CL_Effect(PRVM_G_VECTOR(OFS_PARM0), (int)PRVM_G_FLOAT(OFS_PARM1), (int)PRVM_G_FLOAT(OFS_PARM2), (int)PRVM_G_FLOAT(OFS_PARM3), PRVM_G_FLOAT(OFS_PARM4));
1793 // #405 void(vector org, vector velocity, float howmany) te_blood (DP_TE_BLOOD)
1794 static void VM_CL_te_blood (void)
1798 VM_SAFEPARMCOUNT(3, VM_CL_te_blood);
1799 if (PRVM_G_FLOAT(OFS_PARM2) < 1)
1801 pos = PRVM_G_VECTOR(OFS_PARM0);
1802 CL_FindNonSolidLocation(pos, pos2, 4);
1803 CL_ParticleEffect(EFFECT_TE_BLOOD, PRVM_G_FLOAT(OFS_PARM2), pos2, pos2, PRVM_G_VECTOR(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM1), NULL, 0);
1806 // #406 void(vector mincorner, vector maxcorner, float explosionspeed, float howmany) te_bloodshower (DP_TE_BLOODSHOWER)
1807 static void VM_CL_te_bloodshower (void)
1811 VM_SAFEPARMCOUNT(4, VM_CL_te_bloodshower);
1812 if (PRVM_G_FLOAT(OFS_PARM3) < 1)
1814 speed = PRVM_G_FLOAT(OFS_PARM2);
1821 CL_ParticleEffect(EFFECT_TE_BLOOD, PRVM_G_FLOAT(OFS_PARM3), PRVM_G_VECTOR(OFS_PARM0), PRVM_G_VECTOR(OFS_PARM1), vel1, vel2, NULL, 0);
1824 // #407 void(vector org, vector color) te_explosionrgb (DP_TE_EXPLOSIONRGB)
1825 static void VM_CL_te_explosionrgb (void)
1829 matrix4x4_t tempmatrix;
1830 VM_SAFEPARMCOUNT(2, VM_CL_te_explosionrgb);
1831 pos = PRVM_G_VECTOR(OFS_PARM0);
1832 CL_FindNonSolidLocation(pos, pos2, 10);
1833 CL_ParticleExplosion(pos2);
1834 Matrix4x4_CreateTranslate(&tempmatrix, pos2[0], pos2[1], pos2[2]);
1835 CL_AllocLightFlash(NULL, &tempmatrix, 350, PRVM_G_VECTOR(OFS_PARM1)[0], PRVM_G_VECTOR(OFS_PARM1)[1], PRVM_G_VECTOR(OFS_PARM1)[2], 700, 0.5, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
1838 // #408 void(vector mincorner, vector maxcorner, vector vel, float howmany, float color, float gravityflag, float randomveljitter) te_particlecube (DP_TE_PARTICLECUBE)
1839 static void VM_CL_te_particlecube (void)
1841 VM_SAFEPARMCOUNT(7, VM_CL_te_particlecube);
1842 CL_ParticleCube(PRVM_G_VECTOR(OFS_PARM0), PRVM_G_VECTOR(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM2), (int)PRVM_G_FLOAT(OFS_PARM3), (int)PRVM_G_FLOAT(OFS_PARM4), PRVM_G_FLOAT(OFS_PARM5), PRVM_G_FLOAT(OFS_PARM6));
1845 // #409 void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlerain (DP_TE_PARTICLERAIN)
1846 static void VM_CL_te_particlerain (void)
1848 VM_SAFEPARMCOUNT(5, VM_CL_te_particlerain);
1849 CL_ParticleRain(PRVM_G_VECTOR(OFS_PARM0), PRVM_G_VECTOR(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM2), (int)PRVM_G_FLOAT(OFS_PARM3), (int)PRVM_G_FLOAT(OFS_PARM4), 0);
1852 // #410 void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlesnow (DP_TE_PARTICLESNOW)
1853 static void VM_CL_te_particlesnow (void)
1855 VM_SAFEPARMCOUNT(5, VM_CL_te_particlesnow);
1856 CL_ParticleRain(PRVM_G_VECTOR(OFS_PARM0), PRVM_G_VECTOR(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM2), (int)PRVM_G_FLOAT(OFS_PARM3), (int)PRVM_G_FLOAT(OFS_PARM4), 1);
1859 // #411 void(vector org, vector vel, float howmany) te_spark
1860 static void VM_CL_te_spark (void)
1864 VM_SAFEPARMCOUNT(3, VM_CL_te_spark);
1866 pos = PRVM_G_VECTOR(OFS_PARM0);
1867 CL_FindNonSolidLocation(pos, pos2, 4);
1868 CL_ParticleEffect(EFFECT_TE_SPARK, PRVM_G_FLOAT(OFS_PARM2), pos2, pos2, PRVM_G_VECTOR(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM1), NULL, 0);
1871 extern cvar_t cl_sound_ric_gunshot;
1872 // #412 void(vector org) te_gunshotquad (DP_QUADEFFECTS1)
1873 static void VM_CL_te_gunshotquad (void)
1878 VM_SAFEPARMCOUNT(1, VM_CL_te_gunshotquad);
1880 pos = PRVM_G_VECTOR(OFS_PARM0);
1881 CL_FindNonSolidLocation(pos, pos2, 4);
1882 CL_ParticleEffect(EFFECT_TE_GUNSHOTQUAD, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
1883 if(cl_sound_ric_gunshot.integer >= 2)
1885 if (rand() % 5) S_StartSound(-1, 0, cl.sfx_tink1, pos2, 1, 1);
1889 if (rnd == 1) S_StartSound(-1, 0, cl.sfx_ric1, pos2, 1, 1);
1890 else if (rnd == 2) S_StartSound(-1, 0, cl.sfx_ric2, pos2, 1, 1);
1891 else S_StartSound(-1, 0, cl.sfx_ric3, pos2, 1, 1);
1896 // #413 void(vector org) te_spikequad (DP_QUADEFFECTS1)
1897 static void VM_CL_te_spikequad (void)
1902 VM_SAFEPARMCOUNT(1, VM_CL_te_spikequad);
1904 pos = PRVM_G_VECTOR(OFS_PARM0);
1905 CL_FindNonSolidLocation(pos, pos2, 4);
1906 CL_ParticleEffect(EFFECT_TE_SPIKEQUAD, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
1907 if (rand() % 5) S_StartSound(-1, 0, cl.sfx_tink1, pos2, 1, 1);
1911 if (rnd == 1) S_StartSound(-1, 0, cl.sfx_ric1, pos2, 1, 1);
1912 else if (rnd == 2) S_StartSound(-1, 0, cl.sfx_ric2, pos2, 1, 1);
1913 else S_StartSound(-1, 0, cl.sfx_ric3, pos2, 1, 1);
1917 // #414 void(vector org) te_superspikequad (DP_QUADEFFECTS1)
1918 static void VM_CL_te_superspikequad (void)
1923 VM_SAFEPARMCOUNT(1, VM_CL_te_superspikequad);
1925 pos = PRVM_G_VECTOR(OFS_PARM0);
1926 CL_FindNonSolidLocation(pos, pos2, 4);
1927 CL_ParticleEffect(EFFECT_TE_SUPERSPIKEQUAD, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
1928 if (rand() % 5) S_StartSound(-1, 0, cl.sfx_tink1, pos, 1, 1);
1932 if (rnd == 1) S_StartSound(-1, 0, cl.sfx_ric1, pos2, 1, 1);
1933 else if (rnd == 2) S_StartSound(-1, 0, cl.sfx_ric2, pos2, 1, 1);
1934 else S_StartSound(-1, 0, cl.sfx_ric3, pos2, 1, 1);
1938 // #415 void(vector org) te_explosionquad (DP_QUADEFFECTS1)
1939 static void VM_CL_te_explosionquad (void)
1943 VM_SAFEPARMCOUNT(1, VM_CL_te_explosionquad);
1945 pos = PRVM_G_VECTOR(OFS_PARM0);
1946 CL_FindNonSolidLocation(pos, pos2, 10);
1947 CL_ParticleEffect(EFFECT_TE_EXPLOSIONQUAD, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
1948 S_StartSound(-1, 0, cl.sfx_r_exp3, pos2, 1, 1);
1951 // #416 void(vector org) te_smallflash (DP_TE_SMALLFLASH)
1952 static void VM_CL_te_smallflash (void)
1956 VM_SAFEPARMCOUNT(1, VM_CL_te_smallflash);
1958 pos = PRVM_G_VECTOR(OFS_PARM0);
1959 CL_FindNonSolidLocation(pos, pos2, 10);
1960 CL_ParticleEffect(EFFECT_TE_SMALLFLASH, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
1963 // #417 void(vector org, float radius, float lifetime, vector color) te_customflash (DP_TE_CUSTOMFLASH)
1964 static void VM_CL_te_customflash (void)
1968 matrix4x4_t tempmatrix;
1969 VM_SAFEPARMCOUNT(4, VM_CL_te_customflash);
1971 pos = PRVM_G_VECTOR(OFS_PARM0);
1972 CL_FindNonSolidLocation(pos, pos2, 4);
1973 Matrix4x4_CreateTranslate(&tempmatrix, pos2[0], pos2[1], pos2[2]);
1974 CL_AllocLightFlash(NULL, &tempmatrix, PRVM_G_FLOAT(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM3)[0], PRVM_G_VECTOR(OFS_PARM3)[1], PRVM_G_VECTOR(OFS_PARM3)[2], PRVM_G_FLOAT(OFS_PARM1) / PRVM_G_FLOAT(OFS_PARM2), PRVM_G_FLOAT(OFS_PARM2), 0, -1, true, 1, 0.25, 1, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
1977 // #418 void(vector org) te_gunshot (DP_TE_STANDARDEFFECTBUILTINS)
1978 static void VM_CL_te_gunshot (void)
1983 VM_SAFEPARMCOUNT(1, VM_CL_te_gunshot);
1985 pos = PRVM_G_VECTOR(OFS_PARM0);
1986 CL_FindNonSolidLocation(pos, pos2, 4);
1987 CL_ParticleEffect(EFFECT_TE_GUNSHOT, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
1988 if(cl_sound_ric_gunshot.integer == 1 || cl_sound_ric_gunshot.integer == 3)
1990 if (rand() % 5) S_StartSound(-1, 0, cl.sfx_tink1, pos2, 1, 1);
1994 if (rnd == 1) S_StartSound(-1, 0, cl.sfx_ric1, pos2, 1, 1);
1995 else if (rnd == 2) S_StartSound(-1, 0, cl.sfx_ric2, pos2, 1, 1);
1996 else S_StartSound(-1, 0, cl.sfx_ric3, pos2, 1, 1);
2001 // #419 void(vector org) te_spike (DP_TE_STANDARDEFFECTBUILTINS)
2002 static void VM_CL_te_spike (void)
2007 VM_SAFEPARMCOUNT(1, VM_CL_te_spike);
2009 pos = PRVM_G_VECTOR(OFS_PARM0);
2010 CL_FindNonSolidLocation(pos, pos2, 4);
2011 CL_ParticleEffect(EFFECT_TE_SPIKE, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
2012 if (rand() % 5) S_StartSound(-1, 0, cl.sfx_tink1, pos2, 1, 1);
2016 if (rnd == 1) S_StartSound(-1, 0, cl.sfx_ric1, pos2, 1, 1);
2017 else if (rnd == 2) S_StartSound(-1, 0, cl.sfx_ric2, pos2, 1, 1);
2018 else S_StartSound(-1, 0, cl.sfx_ric3, pos2, 1, 1);
2022 // #420 void(vector org) te_superspike (DP_TE_STANDARDEFFECTBUILTINS)
2023 static void VM_CL_te_superspike (void)
2028 VM_SAFEPARMCOUNT(1, VM_CL_te_superspike);
2030 pos = PRVM_G_VECTOR(OFS_PARM0);
2031 CL_FindNonSolidLocation(pos, pos2, 4);
2032 CL_ParticleEffect(EFFECT_TE_SUPERSPIKE, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
2033 if (rand() % 5) S_StartSound(-1, 0, cl.sfx_tink1, pos2, 1, 1);
2037 if (rnd == 1) S_StartSound(-1, 0, cl.sfx_ric1, pos2, 1, 1);
2038 else if (rnd == 2) S_StartSound(-1, 0, cl.sfx_ric2, pos2, 1, 1);
2039 else S_StartSound(-1, 0, cl.sfx_ric3, pos2, 1, 1);
2043 // #421 void(vector org) te_explosion (DP_TE_STANDARDEFFECTBUILTINS)
2044 static void VM_CL_te_explosion (void)
2048 VM_SAFEPARMCOUNT(1, VM_CL_te_explosion);
2050 pos = PRVM_G_VECTOR(OFS_PARM0);
2051 CL_FindNonSolidLocation(pos, pos2, 10);
2052 CL_ParticleEffect(EFFECT_TE_EXPLOSION, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
2053 S_StartSound(-1, 0, cl.sfx_r_exp3, pos2, 1, 1);
2056 // #422 void(vector org) te_tarexplosion (DP_TE_STANDARDEFFECTBUILTINS)
2057 static void VM_CL_te_tarexplosion (void)
2061 VM_SAFEPARMCOUNT(1, VM_CL_te_tarexplosion);
2063 pos = PRVM_G_VECTOR(OFS_PARM0);
2064 CL_FindNonSolidLocation(pos, pos2, 10);
2065 CL_ParticleEffect(EFFECT_TE_TAREXPLOSION, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
2066 S_StartSound(-1, 0, cl.sfx_r_exp3, pos2, 1, 1);
2069 // #423 void(vector org) te_wizspike (DP_TE_STANDARDEFFECTBUILTINS)
2070 static void VM_CL_te_wizspike (void)
2074 VM_SAFEPARMCOUNT(1, VM_CL_te_wizspike);
2076 pos = PRVM_G_VECTOR(OFS_PARM0);
2077 CL_FindNonSolidLocation(pos, pos2, 4);
2078 CL_ParticleEffect(EFFECT_TE_WIZSPIKE, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
2079 S_StartSound(-1, 0, cl.sfx_wizhit, pos2, 1, 1);
2082 // #424 void(vector org) te_knightspike (DP_TE_STANDARDEFFECTBUILTINS)
2083 static void VM_CL_te_knightspike (void)
2087 VM_SAFEPARMCOUNT(1, VM_CL_te_knightspike);
2089 pos = PRVM_G_VECTOR(OFS_PARM0);
2090 CL_FindNonSolidLocation(pos, pos2, 4);
2091 CL_ParticleEffect(EFFECT_TE_KNIGHTSPIKE, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
2092 S_StartSound(-1, 0, cl.sfx_knighthit, pos2, 1, 1);
2095 // #425 void(vector org) te_lavasplash (DP_TE_STANDARDEFFECTBUILTINS)
2096 static void VM_CL_te_lavasplash (void)
2098 VM_SAFEPARMCOUNT(1, VM_CL_te_lavasplash);
2099 CL_ParticleEffect(EFFECT_TE_LAVASPLASH, 1, PRVM_G_VECTOR(OFS_PARM0), PRVM_G_VECTOR(OFS_PARM0), vec3_origin, vec3_origin, NULL, 0);
2102 // #426 void(vector org) te_teleport (DP_TE_STANDARDEFFECTBUILTINS)
2103 static void VM_CL_te_teleport (void)
2105 VM_SAFEPARMCOUNT(1, VM_CL_te_teleport);
2106 CL_ParticleEffect(EFFECT_TE_TELEPORT, 1, PRVM_G_VECTOR(OFS_PARM0), PRVM_G_VECTOR(OFS_PARM0), vec3_origin, vec3_origin, NULL, 0);
2109 // #427 void(vector org, float colorstart, float colorlength) te_explosion2 (DP_TE_STANDARDEFFECTBUILTINS)
2110 static void VM_CL_te_explosion2 (void)
2114 matrix4x4_t tempmatrix;
2115 int colorStart, colorLength;
2116 unsigned char *tempcolor;
2117 VM_SAFEPARMCOUNT(3, VM_CL_te_explosion2);
2119 pos = PRVM_G_VECTOR(OFS_PARM0);
2120 colorStart = (int)PRVM_G_FLOAT(OFS_PARM1);
2121 colorLength = (int)PRVM_G_FLOAT(OFS_PARM2);
2122 CL_FindNonSolidLocation(pos, pos2, 10);
2123 CL_ParticleExplosion2(pos2, colorStart, colorLength);
2124 tempcolor = palette_rgb[(rand()%colorLength) + colorStart];
2125 color[0] = tempcolor[0] * (2.0f / 255.0f);
2126 color[1] = tempcolor[1] * (2.0f / 255.0f);
2127 color[2] = tempcolor[2] * (2.0f / 255.0f);
2128 Matrix4x4_CreateTranslate(&tempmatrix, pos2[0], pos2[1], pos2[2]);
2129 CL_AllocLightFlash(NULL, &tempmatrix, 350, color[0], color[1], color[2], 700, 0.5, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
2130 S_StartSound(-1, 0, cl.sfx_r_exp3, pos2, 1, 1);
2134 // #428 void(entity own, vector start, vector end) te_lightning1 (DP_TE_STANDARDEFFECTBUILTINS)
2135 static void VM_CL_te_lightning1 (void)
2137 VM_SAFEPARMCOUNT(3, VM_CL_te_lightning1);
2138 CL_NewBeam(PRVM_G_EDICTNUM(OFS_PARM0), PRVM_G_VECTOR(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM2), cl.model_bolt, true);
2141 // #429 void(entity own, vector start, vector end) te_lightning2 (DP_TE_STANDARDEFFECTBUILTINS)
2142 static void VM_CL_te_lightning2 (void)
2144 VM_SAFEPARMCOUNT(3, VM_CL_te_lightning2);
2145 CL_NewBeam(PRVM_G_EDICTNUM(OFS_PARM0), PRVM_G_VECTOR(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM2), cl.model_bolt2, true);
2148 // #430 void(entity own, vector start, vector end) te_lightning3 (DP_TE_STANDARDEFFECTBUILTINS)
2149 static void VM_CL_te_lightning3 (void)
2151 VM_SAFEPARMCOUNT(3, VM_CL_te_lightning3);
2152 CL_NewBeam(PRVM_G_EDICTNUM(OFS_PARM0), PRVM_G_VECTOR(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM2), cl.model_bolt3, false);
2155 // #431 void(entity own, vector start, vector end) te_beam (DP_TE_STANDARDEFFECTBUILTINS)
2156 static void VM_CL_te_beam (void)
2158 VM_SAFEPARMCOUNT(3, VM_CL_te_beam);
2159 CL_NewBeam(PRVM_G_EDICTNUM(OFS_PARM0), PRVM_G_VECTOR(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM2), cl.model_beam, false);
2162 // #433 void(vector org) te_plasmaburn (DP_TE_PLASMABURN)
2163 static void VM_CL_te_plasmaburn (void)
2167 VM_SAFEPARMCOUNT(1, VM_CL_te_plasmaburn);
2169 pos = PRVM_G_VECTOR(OFS_PARM0);
2170 CL_FindNonSolidLocation(pos, pos2, 4);
2171 CL_ParticleEffect(EFFECT_TE_PLASMABURN, 1, pos2, pos2, vec3_origin, vec3_origin, NULL, 0);
2174 // #457 void(vector org, vector velocity, float howmany) te_flamejet (DP_TE_FLAMEJET)
2175 static void VM_CL_te_flamejet (void)
2179 VM_SAFEPARMCOUNT(3, VM_CL_te_flamejet);
2180 if (PRVM_G_FLOAT(OFS_PARM2) < 1)
2182 pos = PRVM_G_VECTOR(OFS_PARM0);
2183 CL_FindNonSolidLocation(pos, pos2, 4);
2184 CL_ParticleEffect(EFFECT_TE_FLAMEJET, PRVM_G_FLOAT(OFS_PARM2), pos2, pos2, PRVM_G_VECTOR(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM1), NULL, 0);
2188 // #443 void(entity e, entity tagentity, string tagname) setattachment
2189 void VM_CL_setattachment (void)
2192 prvm_edict_t *tagentity;
2193 const char *tagname;
2197 VM_SAFEPARMCOUNT(3, VM_CL_setattachment);
2199 e = PRVM_G_EDICT(OFS_PARM0);
2200 tagentity = PRVM_G_EDICT(OFS_PARM1);
2201 tagname = PRVM_G_STRING(OFS_PARM2);
2203 if (e == prog->edicts)
2205 VM_Warning("setattachment: can not modify world entity\n");
2208 if (e->priv.server->free)
2210 VM_Warning("setattachment: can not modify free entity\n");
2214 if (tagentity == NULL)
2215 tagentity = prog->edicts;
2218 if (tagentity != NULL && tagentity != prog->edicts && tagname && tagname[0])
2220 modelindex = (int)PRVM_clientedictfloat(tagentity, modelindex);
2221 model = CL_GetModelByIndex(modelindex);
2224 tagindex = Mod_Alias_GetTagIndexForName(model, (int)PRVM_clientedictfloat(tagentity, skin), tagname);
2226 Con_DPrintf("setattachment(edict %i, edict %i, string \"%s\"): tried to find tag named \"%s\" on entity %i (model \"%s\") but could not find it\n", PRVM_NUM_FOR_EDICT(e), PRVM_NUM_FOR_EDICT(tagentity), tagname, tagname, PRVM_NUM_FOR_EDICT(tagentity), model->name);
2229 Con_DPrintf("setattachment(edict %i, edict %i, string \"%s\"): tried to find tag named \"%s\" on entity %i but it has no model\n", PRVM_NUM_FOR_EDICT(e), PRVM_NUM_FOR_EDICT(tagentity), tagname, tagname, PRVM_NUM_FOR_EDICT(tagentity));
2232 PRVM_clientedictedict(e, tag_entity) = PRVM_EDICT_TO_PROG(tagentity);
2233 PRVM_clientedictfloat(e, tag_index) = tagindex;
2236 /////////////////////////////////////////
2237 // DP_MD3_TAGINFO extension coded by VorteX
2239 int CL_GetTagIndex (prvm_edict_t *e, const char *tagname)
2241 dp_model_t *model = CL_GetModelFromEdict(e);
2243 return Mod_Alias_GetTagIndexForName(model, (int)PRVM_clientedictfloat(e, skin), tagname);
2248 int CL_GetExtendedTagInfo (prvm_edict_t *e, int tagindex, int *parentindex, const char **tagname, matrix4x4_t *tag_localmatrix)
2255 Matrix4x4_CreateIdentity(tag_localmatrix);
2258 && (model = CL_GetModelFromEdict(e))
2259 && model->animscenes)
2261 r = Mod_Alias_GetExtendedTagInfoForIndex(model, (int)PRVM_clientedictfloat(e, skin), e->priv.server->frameblend, &e->priv.server->skeleton, tagindex - 1, parentindex, tagname, tag_localmatrix);
2272 int CL_GetPitchSign(prvm_edict_t *ent)
2275 if ((model = CL_GetModelFromEdict(ent)) && model->type == mod_alias)
2280 void CL_GetEntityMatrix (prvm_edict_t *ent, matrix4x4_t *out, qboolean viewmatrix)
2283 float pitchsign = 1;
2285 scale = PRVM_clientedictfloat(ent, scale);
2290 *out = r_refdef.view.matrix;
2291 else if ((int)PRVM_clientedictfloat(ent, renderflags) & RF_USEAXIS)
2297 VectorScale(PRVM_clientglobalvector(v_forward), scale, forward);
2298 VectorScale(PRVM_clientglobalvector(v_right), -scale, left);
2299 VectorScale(PRVM_clientglobalvector(v_up), scale, up);
2300 VectorCopy(PRVM_clientedictvector(ent, origin), origin);
2301 Matrix4x4_FromVectors(out, forward, left, up, origin);
2305 pitchsign = CL_GetPitchSign(ent);
2306 Matrix4x4_CreateFromQuakeEntity(out, PRVM_clientedictvector(ent, origin)[0], PRVM_clientedictvector(ent, origin)[1], PRVM_clientedictvector(ent, origin)[2], pitchsign * PRVM_clientedictvector(ent, angles)[0], PRVM_clientedictvector(ent, angles)[1], PRVM_clientedictvector(ent, angles)[2], scale);
2310 int CL_GetEntityLocalTagMatrix(prvm_edict_t *ent, int tagindex, matrix4x4_t *out)
2314 && (model = CL_GetModelFromEdict(ent))
2315 && model->animscenes)
2317 VM_GenerateFrameGroupBlend(ent->priv.server->framegroupblend, ent);
2318 VM_FrameBlendFromFrameGroupBlend(ent->priv.server->frameblend, ent->priv.server->framegroupblend, model);
2319 VM_UpdateEdictSkeleton(ent, model, ent->priv.server->frameblend);
2320 return Mod_Alias_GetTagMatrix(model, ent->priv.server->frameblend, &ent->priv.server->skeleton, tagindex, out);
2322 *out = identitymatrix;
2326 // Warnings/errors code:
2327 // 0 - normal (everything all-right)
2330 // 3 - null or non-precached model
2331 // 4 - no tags with requested index
2332 // 5 - runaway loop at attachment chain
2333 extern cvar_t cl_bob;
2334 extern cvar_t cl_bobcycle;
2335 extern cvar_t cl_bobup;
2336 int CL_GetTagMatrix (matrix4x4_t *out, prvm_edict_t *ent, int tagindex)
2340 matrix4x4_t entitymatrix, tagmatrix, attachmatrix;
2343 *out = identitymatrix; // warnings and errors return identical matrix
2345 if (ent == prog->edicts)
2347 if (ent->priv.server->free)
2350 model = CL_GetModelFromEdict(ent);
2354 tagmatrix = identitymatrix;
2358 if(attachloop >= 256)
2360 // apply transformation by child's tagindex on parent entity and then
2361 // by parent entity itself
2362 ret = CL_GetEntityLocalTagMatrix(ent, tagindex - 1, &attachmatrix);
2363 if(ret && attachloop == 0)
2365 CL_GetEntityMatrix(ent, &entitymatrix, false);
2366 Matrix4x4_Concat(&tagmatrix, &attachmatrix, out);
2367 Matrix4x4_Concat(out, &entitymatrix, &tagmatrix);
2368 // next iteration we process the parent entity
2369 if (PRVM_clientedictedict(ent, tag_entity))
2371 tagindex = (int)PRVM_clientedictfloat(ent, tag_index);
2372 ent = PRVM_EDICT_NUM(PRVM_clientedictedict(ent, tag_entity));
2379 // RENDER_VIEWMODEL magic
2380 if ((int)PRVM_clientedictfloat(ent, renderflags) & RF_VIEWMODEL)
2382 Matrix4x4_Copy(&tagmatrix, out);
2384 CL_GetEntityMatrix(prog->edicts, &entitymatrix, true);
2385 Matrix4x4_Concat(out, &entitymatrix, &tagmatrix);
2388 // Cl_bob, ported from rendering code
2389 if (PRVM_clientedictfloat(ent, health) > 0 && cl_bob.value && cl_bobcycle.value)
2392 // LordHavoc: this code is *weird*, but not replacable (I think it
2393 // should be done in QC on the server, but oh well, quake is quake)
2394 // LordHavoc: figured out bobup: the time at which the sin is at 180
2395 // degrees (which allows lengthening or squishing the peak or valley)
2396 cycle = cl.time/cl_bobcycle.value;
2397 cycle -= (int)cycle;
2398 if (cycle < cl_bobup.value)
2399 cycle = sin(M_PI * cycle / cl_bobup.value);
2401 cycle = sin(M_PI + M_PI * (cycle-cl_bobup.value)/(1.0 - cl_bobup.value));
2402 // bob is proportional to velocity in the xy plane
2403 // (don't count Z, or jumping messes it up)
2404 bob = sqrt(PRVM_clientedictvector(ent, velocity)[0]*PRVM_clientedictvector(ent, velocity)[0] + PRVM_clientedictvector(ent, velocity)[1]*PRVM_clientedictvector(ent, velocity)[1])*cl_bob.value;
2405 bob = bob*0.3 + bob*0.7*cycle;
2406 Matrix4x4_AdjustOrigin(out, 0, 0, bound(-7, bob, 4));
2413 // #451 float(entity ent, string tagname) gettagindex (DP_QC_GETTAGINFO)
2414 void VM_CL_gettagindex (void)
2417 const char *tag_name;
2420 VM_SAFEPARMCOUNT(2, VM_CL_gettagindex);
2422 ent = PRVM_G_EDICT(OFS_PARM0);
2423 tag_name = PRVM_G_STRING(OFS_PARM1);
2424 if (ent == prog->edicts)
2426 VM_Warning("VM_CL_gettagindex(entity #%i): can't affect world entity\n", PRVM_NUM_FOR_EDICT(ent));
2429 if (ent->priv.server->free)
2431 VM_Warning("VM_CL_gettagindex(entity #%i): can't affect free entity\n", PRVM_NUM_FOR_EDICT(ent));
2436 if (!CL_GetModelFromEdict(ent))
2437 Con_DPrintf("VM_CL_gettagindex(entity #%i): null or non-precached model\n", PRVM_NUM_FOR_EDICT(ent));
2440 tag_index = CL_GetTagIndex(ent, tag_name);
2442 Con_DPrintf("VM_CL_gettagindex(entity #%i): tag \"%s\" not found\n", PRVM_NUM_FOR_EDICT(ent), tag_name);
2444 PRVM_G_FLOAT(OFS_RETURN) = tag_index;
2447 // #452 vector(entity ent, float tagindex) gettaginfo (DP_QC_GETTAGINFO)
2448 void VM_CL_gettaginfo (void)
2452 matrix4x4_t tag_matrix;
2453 matrix4x4_t tag_localmatrix;
2455 const char *tagname;
2457 vec3_t fo, le, up, trans;
2458 const dp_model_t *model;
2460 VM_SAFEPARMCOUNT(2, VM_CL_gettaginfo);
2462 e = PRVM_G_EDICT(OFS_PARM0);
2463 tagindex = (int)PRVM_G_FLOAT(OFS_PARM1);
2464 returncode = CL_GetTagMatrix(&tag_matrix, e, tagindex);
2465 Matrix4x4_ToVectors(&tag_matrix, PRVM_clientglobalvector(v_forward), le, PRVM_clientglobalvector(v_up), PRVM_G_VECTOR(OFS_RETURN));
2466 VectorScale(le, -1, PRVM_clientglobalvector(v_right));
2467 model = CL_GetModelFromEdict(e);
2468 VM_GenerateFrameGroupBlend(e->priv.server->framegroupblend, e);
2469 VM_FrameBlendFromFrameGroupBlend(e->priv.server->frameblend, e->priv.server->framegroupblend, model);
2470 VM_UpdateEdictSkeleton(e, model, e->priv.server->frameblend);
2471 CL_GetExtendedTagInfo(e, tagindex, &parentindex, &tagname, &tag_localmatrix);
2472 Matrix4x4_ToVectors(&tag_localmatrix, fo, le, up, trans);
2474 PRVM_clientglobalfloat(gettaginfo_parent) = parentindex;
2475 PRVM_clientglobalstring(gettaginfo_name) = tagname ? PRVM_SetTempString(tagname) : 0;
2476 VectorCopy(trans, PRVM_clientglobalvector(gettaginfo_offset));
2477 VectorCopy(fo, PRVM_clientglobalvector(gettaginfo_forward));
2478 VectorScale(le, -1, PRVM_clientglobalvector(gettaginfo_right));
2479 VectorCopy(up, PRVM_clientglobalvector(gettaginfo_up));
2484 VM_Warning("gettagindex: can't affect world entity\n");
2487 VM_Warning("gettagindex: can't affect free entity\n");
2490 Con_DPrintf("CL_GetTagMatrix(entity #%i): null or non-precached model\n", PRVM_NUM_FOR_EDICT(e));
2493 Con_DPrintf("CL_GetTagMatrix(entity #%i): model has no tag with requested index %i\n", PRVM_NUM_FOR_EDICT(e), tagindex);
2496 Con_DPrintf("CL_GetTagMatrix(entity #%i): runaway loop at attachment chain\n", PRVM_NUM_FOR_EDICT(e));
2501 //============================================================================
2503 //====================
2504 // DP_CSQC_SPAWNPARTICLE
2505 // a QC hook to engine's CL_NewParticle
2506 //====================
2508 // particle theme struct
2509 typedef struct vmparticletheme_s
2511 unsigned short typeindex;
2512 qboolean initialized;
2514 porientation_t orientation;
2525 float liquidfriction;
2527 float velocityjitter;
2528 qboolean qualityreduction;
2537 float delaycollision;
2543 typedef struct vmparticlespawner_s
2546 qboolean initialized;
2548 vmparticletheme_t *themes;
2551 float *particle_type;
2552 float *particle_blendmode;
2553 float *particle_orientation;
2554 float *particle_color1;
2555 float *particle_color2;
2556 float *particle_tex;
2557 float *particle_size;
2558 float *particle_sizeincrease;
2559 float *particle_alpha;
2560 float *particle_alphafade;
2561 float *particle_time;
2562 float *particle_gravity;
2563 float *particle_bounce;
2564 float *particle_airfriction;
2565 float *particle_liquidfriction;
2566 float *particle_originjitter;
2567 float *particle_velocityjitter;
2568 float *particle_qualityreduction;
2569 float *particle_stretch;
2570 float *particle_staincolor1;
2571 float *particle_staincolor2;
2572 float *particle_stainalpha;
2573 float *particle_stainsize;
2574 float *particle_staintex;
2575 float *particle_delayspawn;
2576 float *particle_delaycollision;
2577 float *particle_angle;
2578 float *particle_spin;
2579 }vmparticlespawner_t;
2581 vmparticlespawner_t vmpartspawner;
2583 // TODO: automatic max_themes grow
2584 static void VM_InitParticleSpawner (int maxthemes)
2586 // bound max themes to not be an insane value
2589 if (maxthemes > 2048)
2591 // allocate and set up structure
2592 if (vmpartspawner.initialized) // reallocate
2594 Mem_FreePool(&vmpartspawner.pool);
2595 memset(&vmpartspawner, 0, sizeof(vmparticlespawner_t));
2597 vmpartspawner.pool = Mem_AllocPool("VMPARTICLESPAWNER", 0, NULL);
2598 vmpartspawner.themes = (vmparticletheme_t *)Mem_Alloc(vmpartspawner.pool, sizeof(vmparticletheme_t)*maxthemes);
2599 vmpartspawner.max_themes = maxthemes;
2600 vmpartspawner.initialized = true;
2601 vmpartspawner.verified = true;
2602 // get field addresses for fast querying (we can do 1000 calls of spawnparticle in a frame)
2603 vmpartspawner.particle_type = &PRVM_clientglobalfloat(particle_type);
2604 vmpartspawner.particle_blendmode = &PRVM_clientglobalfloat(particle_blendmode);
2605 vmpartspawner.particle_orientation = &PRVM_clientglobalfloat(particle_orientation);
2606 vmpartspawner.particle_color1 = PRVM_clientglobalvector(particle_color1);
2607 vmpartspawner.particle_color2 = PRVM_clientglobalvector(particle_color2);
2608 vmpartspawner.particle_tex = &PRVM_clientglobalfloat(particle_tex);
2609 vmpartspawner.particle_size = &PRVM_clientglobalfloat(particle_size);
2610 vmpartspawner.particle_sizeincrease = &PRVM_clientglobalfloat(particle_sizeincrease);
2611 vmpartspawner.particle_alpha = &PRVM_clientglobalfloat(particle_alpha);
2612 vmpartspawner.particle_alphafade = &PRVM_clientglobalfloat(particle_alphafade);
2613 vmpartspawner.particle_time = &PRVM_clientglobalfloat(particle_time);
2614 vmpartspawner.particle_gravity = &PRVM_clientglobalfloat(particle_gravity);
2615 vmpartspawner.particle_bounce = &PRVM_clientglobalfloat(particle_bounce);
2616 vmpartspawner.particle_airfriction = &PRVM_clientglobalfloat(particle_airfriction);
2617 vmpartspawner.particle_liquidfriction = &PRVM_clientglobalfloat(particle_liquidfriction);
2618 vmpartspawner.particle_originjitter = &PRVM_clientglobalfloat(particle_originjitter);
2619 vmpartspawner.particle_velocityjitter = &PRVM_clientglobalfloat(particle_velocityjitter);
2620 vmpartspawner.particle_qualityreduction = &PRVM_clientglobalfloat(particle_qualityreduction);
2621 vmpartspawner.particle_stretch = &PRVM_clientglobalfloat(particle_stretch);
2622 vmpartspawner.particle_staincolor1 = PRVM_clientglobalvector(particle_staincolor1);
2623 vmpartspawner.particle_staincolor2 = PRVM_clientglobalvector(particle_staincolor2);
2624 vmpartspawner.particle_stainalpha = &PRVM_clientglobalfloat(particle_stainalpha);
2625 vmpartspawner.particle_stainsize = &PRVM_clientglobalfloat(particle_stainsize);
2626 vmpartspawner.particle_staintex = &PRVM_clientglobalfloat(particle_staintex);
2627 vmpartspawner.particle_staintex = &PRVM_clientglobalfloat(particle_staintex);
2628 vmpartspawner.particle_delayspawn = &PRVM_clientglobalfloat(particle_delayspawn);
2629 vmpartspawner.particle_delaycollision = &PRVM_clientglobalfloat(particle_delaycollision);
2630 vmpartspawner.particle_angle = &PRVM_clientglobalfloat(particle_angle);
2631 vmpartspawner.particle_spin = &PRVM_clientglobalfloat(particle_spin);
2633 #undef getglobalvector
2636 // reset particle theme to default values
2637 static void VM_ResetParticleTheme (vmparticletheme_t *theme)
2639 theme->initialized = true;
2640 theme->typeindex = pt_static;
2641 theme->blendmode = PBLEND_ADD;
2642 theme->orientation = PARTICLE_BILLBOARD;
2643 theme->color1 = 0x808080;
2644 theme->color2 = 0xFFFFFF;
2647 theme->sizeincrease = 0;
2649 theme->alphafade = 512;
2650 theme->gravity = 0.0f;
2651 theme->bounce = 0.0f;
2652 theme->airfriction = 1.0f;
2653 theme->liquidfriction = 4.0f;
2654 theme->originjitter = 0.0f;
2655 theme->velocityjitter = 0.0f;
2656 theme->qualityreduction = false;
2657 theme->lifetime = 4;
2659 theme->staincolor1 = -1;
2660 theme->staincolor2 = -1;
2661 theme->staintex = -1;
2662 theme->delayspawn = 0.0f;
2663 theme->delaycollision = 0.0f;
2664 theme->angle = 0.0f;
2668 // particle theme -> QC globals
2669 void VM_CL_ParticleThemeToGlobals(vmparticletheme_t *theme)
2671 *vmpartspawner.particle_type = theme->typeindex;
2672 *vmpartspawner.particle_blendmode = theme->blendmode;
2673 *vmpartspawner.particle_orientation = theme->orientation;
2674 vmpartspawner.particle_color1[0] = (theme->color1 >> 16) & 0xFF; // VorteX: int only can store 0-255, not 0-256 which means 0 - 0,99609375...
2675 vmpartspawner.particle_color1[1] = (theme->color1 >> 8) & 0xFF;
2676 vmpartspawner.particle_color1[2] = (theme->color1 >> 0) & 0xFF;
2677 vmpartspawner.particle_color2[0] = (theme->color2 >> 16) & 0xFF;
2678 vmpartspawner.particle_color2[1] = (theme->color2 >> 8) & 0xFF;
2679 vmpartspawner.particle_color2[2] = (theme->color2 >> 0) & 0xFF;
2680 *vmpartspawner.particle_tex = (float)theme->tex;
2681 *vmpartspawner.particle_size = theme->size;
2682 *vmpartspawner.particle_sizeincrease = theme->sizeincrease;
2683 *vmpartspawner.particle_alpha = theme->alpha/256;
2684 *vmpartspawner.particle_alphafade = theme->alphafade/256;
2685 *vmpartspawner.particle_time = theme->lifetime;
2686 *vmpartspawner.particle_gravity = theme->gravity;
2687 *vmpartspawner.particle_bounce = theme->bounce;
2688 *vmpartspawner.particle_airfriction = theme->airfriction;
2689 *vmpartspawner.particle_liquidfriction = theme->liquidfriction;
2690 *vmpartspawner.particle_originjitter = theme->originjitter;
2691 *vmpartspawner.particle_velocityjitter = theme->velocityjitter;
2692 *vmpartspawner.particle_qualityreduction = theme->qualityreduction;
2693 *vmpartspawner.particle_stretch = theme->stretch;
2694 vmpartspawner.particle_staincolor1[0] = ((int)theme->staincolor1 >> 16) & 0xFF;
2695 vmpartspawner.particle_staincolor1[1] = ((int)theme->staincolor1 >> 8) & 0xFF;
2696 vmpartspawner.particle_staincolor1[2] = ((int)theme->staincolor1 >> 0) & 0xFF;
2697 vmpartspawner.particle_staincolor2[0] = ((int)theme->staincolor2 >> 16) & 0xFF;
2698 vmpartspawner.particle_staincolor2[1] = ((int)theme->staincolor2 >> 8) & 0xFF;
2699 vmpartspawner.particle_staincolor2[2] = ((int)theme->staincolor2 >> 0) & 0xFF;
2700 *vmpartspawner.particle_staintex = (float)theme->staintex;
2701 *vmpartspawner.particle_stainalpha = (float)theme->stainalpha/256;
2702 *vmpartspawner.particle_stainsize = (float)theme->stainsize;
2703 *vmpartspawner.particle_delayspawn = theme->delayspawn;
2704 *vmpartspawner.particle_delaycollision = theme->delaycollision;
2705 *vmpartspawner.particle_angle = theme->angle;
2706 *vmpartspawner.particle_spin = theme->spin;
2709 // QC globals -> particle theme
2710 void VM_CL_ParticleThemeFromGlobals(vmparticletheme_t *theme)
2712 theme->typeindex = (unsigned short)*vmpartspawner.particle_type;
2713 theme->blendmode = (pblend_t)(int)*vmpartspawner.particle_blendmode;
2714 theme->orientation = (porientation_t)(int)*vmpartspawner.particle_orientation;
2715 theme->color1 = ((int)vmpartspawner.particle_color1[0] << 16) + ((int)vmpartspawner.particle_color1[1] << 8) + ((int)vmpartspawner.particle_color1[2]);
2716 theme->color2 = ((int)vmpartspawner.particle_color2[0] << 16) + ((int)vmpartspawner.particle_color2[1] << 8) + ((int)vmpartspawner.particle_color2[2]);
2717 theme->tex = (int)*vmpartspawner.particle_tex;
2718 theme->size = *vmpartspawner.particle_size;
2719 theme->sizeincrease = *vmpartspawner.particle_sizeincrease;
2720 theme->alpha = *vmpartspawner.particle_alpha*256;
2721 theme->alphafade = *vmpartspawner.particle_alphafade*256;
2722 theme->lifetime = *vmpartspawner.particle_time;
2723 theme->gravity = *vmpartspawner.particle_gravity;
2724 theme->bounce = *vmpartspawner.particle_bounce;
2725 theme->airfriction = *vmpartspawner.particle_airfriction;
2726 theme->liquidfriction = *vmpartspawner.particle_liquidfriction;
2727 theme->originjitter = *vmpartspawner.particle_originjitter;
2728 theme->velocityjitter = *vmpartspawner.particle_velocityjitter;
2729 theme->qualityreduction = (*vmpartspawner.particle_qualityreduction) ? true : false;
2730 theme->stretch = *vmpartspawner.particle_stretch;
2731 theme->staincolor1 = ((int)vmpartspawner.particle_staincolor1[0])*65536 + (int)(vmpartspawner.particle_staincolor1[1])*256 + (int)(vmpartspawner.particle_staincolor1[2]);
2732 theme->staincolor2 = (int)(vmpartspawner.particle_staincolor2[0])*65536 + (int)(vmpartspawner.particle_staincolor2[1])*256 + (int)(vmpartspawner.particle_staincolor2[2]);
2733 theme->staintex =(int)*vmpartspawner.particle_staintex;
2734 theme->stainalpha = *vmpartspawner.particle_stainalpha*256;
2735 theme->stainsize = *vmpartspawner.particle_stainsize;
2736 theme->delayspawn = *vmpartspawner.particle_delayspawn;
2737 theme->delaycollision = *vmpartspawner.particle_delaycollision;
2738 theme->angle = *vmpartspawner.particle_angle;
2739 theme->spin = *vmpartspawner.particle_spin;
2742 // init particle spawner interface
2743 // # float(float max_themes) initparticlespawner
2744 void VM_CL_InitParticleSpawner (void)
2746 VM_SAFEPARMCOUNTRANGE(0, 1, VM_CL_InitParticleSpawner);
2747 VM_InitParticleSpawner((int)PRVM_G_FLOAT(OFS_PARM0));
2748 vmpartspawner.themes[0].initialized = true;
2749 VM_ResetParticleTheme(&vmpartspawner.themes[0]);
2750 PRVM_G_FLOAT(OFS_RETURN) = (vmpartspawner.verified == true) ? 1 : 0;
2753 // void() resetparticle
2754 void VM_CL_ResetParticle (void)
2756 VM_SAFEPARMCOUNT(0, VM_CL_ResetParticle);
2757 if (vmpartspawner.verified == false)
2759 VM_Warning("VM_CL_ResetParticle: particle spawner not initialized\n");
2762 VM_CL_ParticleThemeToGlobals(&vmpartspawner.themes[0]);
2765 // void(float themenum) particletheme
2766 void VM_CL_ParticleTheme (void)
2770 VM_SAFEPARMCOUNT(1, VM_CL_ParticleTheme);
2771 if (vmpartspawner.verified == false)
2773 VM_Warning("VM_CL_ParticleTheme: particle spawner not initialized\n");
2776 themenum = (int)PRVM_G_FLOAT(OFS_PARM0);
2777 if (themenum < 0 || themenum >= vmpartspawner.max_themes)
2779 VM_Warning("VM_CL_ParticleTheme: bad theme number %i\n", themenum);
2780 VM_CL_ParticleThemeToGlobals(&vmpartspawner.themes[0]);
2783 if (vmpartspawner.themes[themenum].initialized == false)
2785 VM_Warning("VM_CL_ParticleTheme: theme #%i not exists\n", themenum);
2786 VM_CL_ParticleThemeToGlobals(&vmpartspawner.themes[0]);
2789 // load particle theme into globals
2790 VM_CL_ParticleThemeToGlobals(&vmpartspawner.themes[themenum]);
2793 // float() saveparticletheme
2794 // void(float themenum) updateparticletheme
2795 void VM_CL_ParticleThemeSave (void)
2799 VM_SAFEPARMCOUNTRANGE(0, 1, VM_CL_ParticleThemeSave);
2800 if (vmpartspawner.verified == false)
2802 VM_Warning("VM_CL_ParticleThemeSave: particle spawner not initialized\n");
2805 // allocate new theme, save it and return
2808 for (themenum = 0; themenum < vmpartspawner.max_themes; themenum++)
2809 if (vmpartspawner.themes[themenum].initialized == false)
2811 if (themenum >= vmpartspawner.max_themes)
2813 if (vmpartspawner.max_themes == 2048)
2814 VM_Warning("VM_CL_ParticleThemeSave: no free theme slots\n");
2816 VM_Warning("VM_CL_ParticleThemeSave: no free theme slots, try initparticlespawner() with highter max_themes\n");
2817 PRVM_G_FLOAT(OFS_RETURN) = -1;
2820 vmpartspawner.themes[themenum].initialized = true;
2821 VM_CL_ParticleThemeFromGlobals(&vmpartspawner.themes[themenum]);
2822 PRVM_G_FLOAT(OFS_RETURN) = themenum;
2825 // update existing theme
2826 themenum = (int)PRVM_G_FLOAT(OFS_PARM0);
2827 if (themenum < 0 || themenum >= vmpartspawner.max_themes)
2829 VM_Warning("VM_CL_ParticleThemeSave: bad theme number %i\n", themenum);
2832 vmpartspawner.themes[themenum].initialized = true;
2833 VM_CL_ParticleThemeFromGlobals(&vmpartspawner.themes[themenum]);
2836 // void(float themenum) freeparticletheme
2837 void VM_CL_ParticleThemeFree (void)
2841 VM_SAFEPARMCOUNT(1, VM_CL_ParticleThemeFree);
2842 if (vmpartspawner.verified == false)
2844 VM_Warning("VM_CL_ParticleThemeFree: particle spawner not initialized\n");
2847 themenum = (int)PRVM_G_FLOAT(OFS_PARM0);
2849 if (themenum <= 0 || themenum >= vmpartspawner.max_themes)
2851 VM_Warning("VM_CL_ParticleThemeFree: bad theme number %i\n", themenum);
2854 if (vmpartspawner.themes[themenum].initialized == false)
2856 VM_Warning("VM_CL_ParticleThemeFree: theme #%i already freed\n", themenum);
2857 VM_CL_ParticleThemeToGlobals(&vmpartspawner.themes[0]);
2861 VM_ResetParticleTheme(&vmpartspawner.themes[themenum]);
2862 vmpartspawner.themes[themenum].initialized = false;
2865 // float(vector org, vector dir, [float theme]) particle
2866 // returns 0 if failed, 1 if succesful
2867 void VM_CL_SpawnParticle (void)
2870 vmparticletheme_t *theme;
2874 VM_SAFEPARMCOUNTRANGE(2, 3, VM_CL_SpawnParticle2);
2875 if (vmpartspawner.verified == false)
2877 VM_Warning("VM_CL_SpawnParticle: particle spawner not initialized\n");
2878 PRVM_G_FLOAT(OFS_RETURN) = 0;
2881 org = PRVM_G_VECTOR(OFS_PARM0);
2882 dir = PRVM_G_VECTOR(OFS_PARM1);
2884 if (prog->argc < 3) // global-set particle
2886 part = CL_NewParticle(org, (unsigned short)*vmpartspawner.particle_type, ((int)(vmpartspawner.particle_color1[0]) << 16) + ((int)(vmpartspawner.particle_color1[1]) << 8) + ((int)(vmpartspawner.particle_color1[2])), ((int)vmpartspawner.particle_color2[0] << 16) + ((int)vmpartspawner.particle_color2[1] << 8) + ((int)vmpartspawner.particle_color2[2]), (int)*vmpartspawner.particle_tex, *vmpartspawner.particle_size, *vmpartspawner.particle_sizeincrease, *vmpartspawner.particle_alpha*256, *vmpartspawner.particle_alphafade*256, *vmpartspawner.particle_gravity, *vmpartspawner.particle_bounce, org[0], org[1], org[2], dir[0], dir[1], dir[2], *vmpartspawner.particle_airfriction, *vmpartspawner.particle_liquidfriction, *vmpartspawner.particle_originjitter, *vmpartspawner.particle_velocityjitter, (*vmpartspawner.particle_qualityreduction) ? true : false, *vmpartspawner.particle_time, *vmpartspawner.particle_stretch, (pblend_t)(int)*vmpartspawner.particle_blendmode, (porientation_t)(int)*vmpartspawner.particle_orientation, (int)(vmpartspawner.particle_staincolor1[0])*65536 + (int)(vmpartspawner.particle_staincolor1[1])*256 + (int)(vmpartspawner.particle_staincolor1[2]), (int)(vmpartspawner.particle_staincolor2[0])*65536 + (int)(vmpartspawner.particle_staincolor2[1])*256 + (int)(vmpartspawner.particle_staincolor2[2]), (int)*vmpartspawner.particle_staintex, *vmpartspawner.particle_stainalpha*256, *vmpartspawner.particle_stainsize, *vmpartspawner.particle_angle, *vmpartspawner.particle_spin, NULL);
2889 PRVM_G_FLOAT(OFS_RETURN) = 0;
2892 if (*vmpartspawner.particle_delayspawn)
2893 part->delayedspawn = cl.time + *vmpartspawner.particle_delayspawn;
2894 //if (*vmpartspawner.particle_delaycollision)
2895 // part->delayedcollisions = cl.time + *vmpartspawner.particle_delaycollision;
2897 else // quick themed particle
2899 themenum = (int)PRVM_G_FLOAT(OFS_PARM2);
2900 if (themenum <= 0 || themenum >= vmpartspawner.max_themes)
2902 VM_Warning("VM_CL_SpawnParticle: bad theme number %i\n", themenum);
2903 PRVM_G_FLOAT(OFS_RETURN) = 0;
2906 theme = &vmpartspawner.themes[themenum];
2907 part = CL_NewParticle(org, theme->typeindex, theme->color1, theme->color2, theme->tex, theme->size, theme->sizeincrease, theme->alpha, theme->alphafade, theme->gravity, theme->bounce, org[0], org[1], org[2], dir[0], dir[1], dir[2], theme->airfriction, theme->liquidfriction, theme->originjitter, theme->velocityjitter, theme->qualityreduction, theme->lifetime, theme->stretch, theme->blendmode, theme->orientation, theme->staincolor1, theme->staincolor2, theme->staintex, theme->stainalpha, theme->stainsize, theme->angle, theme->spin, NULL);
2910 PRVM_G_FLOAT(OFS_RETURN) = 0;
2913 if (theme->delayspawn)
2914 part->delayedspawn = cl.time + theme->delayspawn;
2915 //if (theme->delaycollision)
2916 // part->delayedcollisions = cl.time + theme->delaycollision;
2918 PRVM_G_FLOAT(OFS_RETURN) = 1;
2921 // float(vector org, vector dir, float spawndelay, float collisiondelay, [float theme]) delayedparticle
2922 // returns 0 if failed, 1 if success
2923 void VM_CL_SpawnParticleDelayed (void)
2926 vmparticletheme_t *theme;
2930 VM_SAFEPARMCOUNTRANGE(4, 5, VM_CL_SpawnParticle2);
2931 if (vmpartspawner.verified == false)
2933 VM_Warning("VM_CL_SpawnParticle: particle spawner not initialized\n");
2934 PRVM_G_FLOAT(OFS_RETURN) = 0;
2937 org = PRVM_G_VECTOR(OFS_PARM0);
2938 dir = PRVM_G_VECTOR(OFS_PARM1);
2939 if (prog->argc < 5) // global-set particle
2940 part = CL_NewParticle(org, (unsigned short)*vmpartspawner.particle_type, ((int)vmpartspawner.particle_color1[0] << 16) + ((int)vmpartspawner.particle_color1[1] << 8) + ((int)vmpartspawner.particle_color1[2]), ((int)vmpartspawner.particle_color2[0] << 16) + ((int)vmpartspawner.particle_color2[1] << 8) + ((int)vmpartspawner.particle_color2[2]), (int)*vmpartspawner.particle_tex, *vmpartspawner.particle_size, *vmpartspawner.particle_sizeincrease, *vmpartspawner.particle_alpha*256, *vmpartspawner.particle_alphafade*256, *vmpartspawner.particle_gravity, *vmpartspawner.particle_bounce, org[0], org[1], org[2], dir[0], dir[1], dir[2], *vmpartspawner.particle_airfriction, *vmpartspawner.particle_liquidfriction, *vmpartspawner.particle_originjitter, *vmpartspawner.particle_velocityjitter, (*vmpartspawner.particle_qualityreduction) ? true : false, *vmpartspawner.particle_time, *vmpartspawner.particle_stretch, (pblend_t)(int)*vmpartspawner.particle_blendmode, (porientation_t)(int)*vmpartspawner.particle_orientation, ((int)vmpartspawner.particle_staincolor1[0] << 16) + ((int)vmpartspawner.particle_staincolor1[1] << 8) + ((int)vmpartspawner.particle_staincolor1[2]), ((int)vmpartspawner.particle_staincolor2[0] << 16) + ((int)vmpartspawner.particle_staincolor2[1] << 8) + ((int)vmpartspawner.particle_staincolor2[2]), (int)*vmpartspawner.particle_staintex, *vmpartspawner.particle_stainalpha*256, *vmpartspawner.particle_stainsize, *vmpartspawner.particle_angle, *vmpartspawner.particle_spin, NULL);
2941 else // themed particle
2943 themenum = (int)PRVM_G_FLOAT(OFS_PARM4);
2944 if (themenum <= 0 || themenum >= vmpartspawner.max_themes)
2946 VM_Warning("VM_CL_SpawnParticle: bad theme number %i\n", themenum);
2947 PRVM_G_FLOAT(OFS_RETURN) = 0;
2950 theme = &vmpartspawner.themes[themenum];
2951 part = CL_NewParticle(org, theme->typeindex, theme->color1, theme->color2, theme->tex, theme->size, theme->sizeincrease, theme->alpha, theme->alphafade, theme->gravity, theme->bounce, org[0], org[1], org[2], dir[0], dir[1], dir[2], theme->airfriction, theme->liquidfriction, theme->originjitter, theme->velocityjitter, theme->qualityreduction, theme->lifetime, theme->stretch, theme->blendmode, theme->orientation, theme->staincolor1, theme->staincolor2, theme->staintex, theme->stainalpha, theme->stainsize, theme->angle, theme->spin, NULL);
2955 PRVM_G_FLOAT(OFS_RETURN) = 0;
2958 part->delayedspawn = cl.time + PRVM_G_FLOAT(OFS_PARM2);
2959 //part->delayedcollisions = cl.time + PRVM_G_FLOAT(OFS_PARM3);
2960 PRVM_G_FLOAT(OFS_RETURN) = 0;
2963 //====================
2964 //CSQC engine entities query
2965 //====================
2967 // float(float entitynum, float whatfld) getentity;
2968 // vector(float entitynum, float whatfld) getentityvec;
2969 // querying engine-drawn entity
2970 // VorteX: currently it's only tested with whatfld = 1..7
2971 void VM_CL_GetEntity (void)
2973 int entnum, fieldnum;
2974 float org[3], v1[3], v2[3];
2975 VM_SAFEPARMCOUNT(2, VM_CL_GetEntityVec);
2977 entnum = PRVM_G_FLOAT(OFS_PARM0);
2978 if (entnum < 0 || entnum >= cl.num_entities)
2980 PRVM_G_FLOAT(OFS_RETURN) = 0;
2983 fieldnum = PRVM_G_FLOAT(OFS_PARM1);
2986 case 0: // active state
2987 PRVM_G_FLOAT(OFS_RETURN) = cl.entities_active[entnum];
2990 Matrix4x4_OriginFromMatrix(&cl.entities[entnum].render.matrix, PRVM_G_VECTOR(OFS_RETURN));
2993 Matrix4x4_ToVectors(&cl.entities[entnum].render.matrix, PRVM_G_VECTOR(OFS_RETURN), v1, v2, org);
2996 Matrix4x4_ToVectors(&cl.entities[entnum].render.matrix, v1, PRVM_G_VECTOR(OFS_RETURN), v2, org);
2999 Matrix4x4_ToVectors(&cl.entities[entnum].render.matrix, v1, v2, PRVM_G_VECTOR(OFS_RETURN), org);
3002 PRVM_G_FLOAT(OFS_RETURN) = Matrix4x4_ScaleFromMatrix(&cl.entities[entnum].render.matrix);
3004 case 6: // origin + v_forward, v_right, v_up
3005 Matrix4x4_ToVectors(&cl.entities[entnum].render.matrix, PRVM_clientglobalvector(v_forward), PRVM_clientglobalvector(v_right), PRVM_clientglobalvector(v_up), PRVM_G_VECTOR(OFS_RETURN));
3008 PRVM_G_FLOAT(OFS_RETURN) = cl.entities[entnum].render.alpha;
3011 VectorCopy(cl.entities[entnum].render.colormod, PRVM_G_VECTOR(OFS_RETURN));
3013 case 9: // pants colormod
3014 VectorCopy(cl.entities[entnum].render.colormap_pantscolor, PRVM_G_VECTOR(OFS_RETURN));
3016 case 10: // shirt colormod
3017 VectorCopy(cl.entities[entnum].render.colormap_shirtcolor, PRVM_G_VECTOR(OFS_RETURN));
3020 PRVM_G_FLOAT(OFS_RETURN) = cl.entities[entnum].render.skinnum;
3023 VectorCopy(cl.entities[entnum].render.mins, PRVM_G_VECTOR(OFS_RETURN));
3026 VectorCopy(cl.entities[entnum].render.maxs, PRVM_G_VECTOR(OFS_RETURN));
3029 Matrix4x4_OriginFromMatrix(&cl.entities[entnum].render.matrix, org);
3030 VectorAdd(cl.entities[entnum].render.mins, org, PRVM_G_VECTOR(OFS_RETURN));
3033 Matrix4x4_OriginFromMatrix(&cl.entities[entnum].render.matrix, org);
3034 VectorAdd(cl.entities[entnum].render.maxs, org, PRVM_G_VECTOR(OFS_RETURN));
3037 VectorMA(cl.entities[entnum].render.modellight_ambient, 0.5, cl.entities[entnum].render.modellight_diffuse, PRVM_G_VECTOR(OFS_RETURN));
3040 PRVM_G_FLOAT(OFS_RETURN) = 0;
3045 //====================
3046 //QC POLYGON functions
3047 //====================
3049 #define VMPOLYGONS_MAXPOINTS 64
3051 typedef struct vmpolygons_triangle_s
3053 rtexture_t *texture;
3056 unsigned short elements[3];
3057 }vmpolygons_triangle_t;
3059 typedef struct vmpolygons_s
3062 qboolean initialized;
3063 double progstarttime;
3067 float *data_vertex3f;
3068 float *data_color4f;
3069 float *data_texcoord2f;
3073 vmpolygons_triangle_t *data_triangles;
3074 unsigned short *data_sortedelement3s;
3076 qboolean begin_active;
3078 rtexture_t *begin_texture;
3081 float begin_vertex[VMPOLYGONS_MAXPOINTS][3];
3082 float begin_color[VMPOLYGONS_MAXPOINTS][4];
3083 float begin_texcoord[VMPOLYGONS_MAXPOINTS][2];
3084 qboolean begin_texture_hasalpha;
3087 // FIXME: make VM_CL_R_Polygon functions use Debug_Polygon functions?
3088 vmpolygons_t vmpolygons[PRVM_MAXPROGS];
3090 //#304 void() renderscene (EXT_CSQC)
3091 // moved that here to reset the polygons,
3092 // resetting them earlier causes R_Mesh_Draw to be called with numvertices = 0
3094 void VM_CL_R_RenderScene (void)
3096 double t = Sys_DoubleTime();
3097 vmpolygons_t* polys = vmpolygons + PRVM_GetProgNr();
3098 VM_SAFEPARMCOUNT(0, VM_CL_R_RenderScene);
3101 if(r_refdef.view.ismain)
3103 // set the main view
3104 csqc_main_r_refdef_view = r_refdef.view;
3106 // clear the flags so no other view becomes "main" unless CSQC sets VF_MAINVIEW
3107 r_refdef.view.ismain = false;
3108 csqc_original_r_refdef_view.ismain = false;
3111 // we need to update any RENDER_VIEWMODEL entities at this point because
3112 // csqc supplies its own view matrix
3113 CL_UpdateViewEntities();
3118 polys->num_vertices = polys->num_triangles = 0;
3119 polys->progstarttime = prog->starttime;
3121 // callprofile fixing hack: do not include this time in what is counted for CSQC_UpdateView
3122 prog->functions[PRVM_clientfunction(CSQC_UpdateView)].totaltime -= Sys_DoubleTime() - t;
3125 static void VM_ResizePolygons(vmpolygons_t *polys)
3127 float *oldvertex3f = polys->data_vertex3f;
3128 float *oldcolor4f = polys->data_color4f;
3129 float *oldtexcoord2f = polys->data_texcoord2f;
3130 vmpolygons_triangle_t *oldtriangles = polys->data_triangles;
3131 unsigned short *oldsortedelement3s = polys->data_sortedelement3s;
3132 polys->max_vertices = min(polys->max_triangles*3, 65536);
3133 polys->data_vertex3f = (float *)Mem_Alloc(polys->pool, polys->max_vertices*sizeof(float[3]));
3134 polys->data_color4f = (float *)Mem_Alloc(polys->pool, polys->max_vertices*sizeof(float[4]));
3135 polys->data_texcoord2f = (float *)Mem_Alloc(polys->pool, polys->max_vertices*sizeof(float[2]));
3136 polys->data_triangles = (vmpolygons_triangle_t *)Mem_Alloc(polys->pool, polys->max_triangles*sizeof(vmpolygons_triangle_t));
3137 polys->data_sortedelement3s = (unsigned short *)Mem_Alloc(polys->pool, polys->max_triangles*sizeof(unsigned short[3]));
3138 if (polys->num_vertices)
3140 memcpy(polys->data_vertex3f, oldvertex3f, polys->num_vertices*sizeof(float[3]));
3141 memcpy(polys->data_color4f, oldcolor4f, polys->num_vertices*sizeof(float[4]));
3142 memcpy(polys->data_texcoord2f, oldtexcoord2f, polys->num_vertices*sizeof(float[2]));
3144 if (polys->num_triangles)
3146 memcpy(polys->data_triangles, oldtriangles, polys->num_triangles*sizeof(vmpolygons_triangle_t));
3147 memcpy(polys->data_sortedelement3s, oldsortedelement3s, polys->num_triangles*sizeof(unsigned short[3]));
3150 Mem_Free(oldvertex3f);
3152 Mem_Free(oldcolor4f);
3154 Mem_Free(oldtexcoord2f);
3156 Mem_Free(oldtriangles);
3157 if (oldsortedelement3s)
3158 Mem_Free(oldsortedelement3s);
3161 static void VM_InitPolygons (vmpolygons_t* polys)
3163 memset(polys, 0, sizeof(*polys));
3164 polys->pool = Mem_AllocPool("VMPOLY", 0, NULL);
3165 polys->max_triangles = 1024;
3166 VM_ResizePolygons(polys);
3167 polys->initialized = true;
3170 static void VM_DrawPolygonCallback (const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfacelist)
3172 int surfacelistindex;
3173 vmpolygons_t* polys = vmpolygons + PRVM_GetProgNr();
3174 if(polys->progstarttime != prog->starttime) // from other progs? won't draw these (this can cause crashes!)
3176 // R_Mesh_ResetTextureState();
3177 R_EntityMatrix(&identitymatrix);
3178 GL_CullFace(GL_NONE);
3179 GL_DepthTest(true); // polys in 3D space shall always have depth test
3180 GL_DepthRange(0, 1);
3181 R_Mesh_PrepareVertices_Generic_Arrays(polys->num_vertices, polys->data_vertex3f, polys->data_color4f, polys->data_texcoord2f);
3183 for (surfacelistindex = 0;surfacelistindex < numsurfaces;)
3185 int numtriangles = 0;
3186 rtexture_t *tex = polys->data_triangles[surfacelist[surfacelistindex]].texture;
3187 int drawflag = polys->data_triangles[surfacelist[surfacelistindex]].drawflag;
3188 DrawQ_ProcessDrawFlag(drawflag, polys->data_triangles[surfacelist[surfacelistindex]].hasalpha);
3189 R_SetupShader_Generic(tex, NULL, GL_MODULATE, 1, false, false, false);
3191 for (;surfacelistindex < numsurfaces;surfacelistindex++)
3193 if (polys->data_triangles[surfacelist[surfacelistindex]].texture != tex || polys->data_triangles[surfacelist[surfacelistindex]].drawflag != drawflag)
3195 VectorCopy(polys->data_triangles[surfacelist[surfacelistindex]].elements, polys->data_sortedelement3s + 3*numtriangles);
3198 R_Mesh_Draw(0, polys->num_vertices, 0, numtriangles, NULL, NULL, 0, polys->data_sortedelement3s, NULL, 0);
3202 void VMPolygons_Store(vmpolygons_t *polys)
3207 // detect if we have alpha
3208 hasalpha = polys->begin_texture_hasalpha;
3209 for(i = 0; !hasalpha && (i < polys->begin_vertices); ++i)
3210 if(polys->begin_color[i][3] < 1)
3213 if (polys->begin_draw2d)
3215 // draw the polygon as 2D immediately
3216 drawqueuemesh_t mesh;
3217 mesh.texture = polys->begin_texture;
3218 mesh.num_vertices = polys->begin_vertices;
3219 mesh.num_triangles = polys->begin_vertices-2;
3220 mesh.data_element3i = polygonelement3i;
3221 mesh.data_element3s = polygonelement3s;
3222 mesh.data_vertex3f = polys->begin_vertex[0];
3223 mesh.data_color4f = polys->begin_color[0];
3224 mesh.data_texcoord2f = polys->begin_texcoord[0];
3225 DrawQ_Mesh(&mesh, polys->begin_drawflag, hasalpha);
3229 // queue the polygon as 3D for sorted transparent rendering later
3231 if (polys->max_triangles < polys->num_triangles + polys->begin_vertices-2)
3233 while (polys->max_triangles < polys->num_triangles + polys->begin_vertices-2)
3234 polys->max_triangles *= 2;
3235 VM_ResizePolygons(polys);
3237 if (polys->num_vertices + polys->begin_vertices <= polys->max_vertices)
3239 // needle in a haystack!
3240 // polys->num_vertices was used for copying where we actually want to copy begin_vertices
3241 // that also caused it to not render the first polygon that is added
3243 memcpy(polys->data_vertex3f + polys->num_vertices * 3, polys->begin_vertex[0], polys->begin_vertices * sizeof(float[3]));
3244 memcpy(polys->data_color4f + polys->num_vertices * 4, polys->begin_color[0], polys->begin_vertices * sizeof(float[4]));
3245 memcpy(polys->data_texcoord2f + polys->num_vertices * 2, polys->begin_texcoord[0], polys->begin_vertices * sizeof(float[2]));
3246 for (i = 0;i < polys->begin_vertices-2;i++)
3248 polys->data_triangles[polys->num_triangles].texture = polys->begin_texture;
3249 polys->data_triangles[polys->num_triangles].drawflag = polys->begin_drawflag;
3250 polys->data_triangles[polys->num_triangles].elements[0] = polys->num_vertices;
3251 polys->data_triangles[polys->num_triangles].elements[1] = polys->num_vertices + i+1;
3252 polys->data_triangles[polys->num_triangles].elements[2] = polys->num_vertices + i+2;
3253 polys->data_triangles[polys->num_triangles].hasalpha = hasalpha;
3254 polys->num_triangles++;
3256 polys->num_vertices += polys->begin_vertices;
3259 polys->begin_active = false;
3262 // TODO: move this into the client code and clean-up everything else, too! [1/6/2008 Black]
3263 // LordHavoc: agreed, this is a mess
3264 void VM_CL_AddPolygonsToMeshQueue (void)
3267 vmpolygons_t* polys = vmpolygons + PRVM_GetProgNr();
3270 // only add polygons of the currently active prog to the queue - if there is none, we're done
3274 if (!polys->num_triangles)
3277 for (i = 0;i < polys->num_triangles;i++)
3279 VectorMAMAM(1.0f / 3.0f, polys->data_vertex3f + 3*polys->data_triangles[i].elements[0], 1.0f / 3.0f, polys->data_vertex3f + 3*polys->data_triangles[i].elements[1], 1.0f / 3.0f, polys->data_vertex3f + 3*polys->data_triangles[i].elements[2], center);
3280 R_MeshQueue_AddTransparent(center, VM_DrawPolygonCallback, NULL, i, NULL);
3283 /*polys->num_triangles = 0; // now done after rendering the scene,
3284 polys->num_vertices = 0; // otherwise it's not rendered at all and prints an error message --blub */
3287 //void(string texturename, float flag[, float is2d]) R_BeginPolygon
3288 void VM_CL_R_PolygonBegin (void)
3290 const char *picname;
3292 vmpolygons_t* polys = vmpolygons + PRVM_GetProgNr();
3295 // TODO instead of using skinframes here (which provides the benefit of
3296 // better management of flags, and is more suited for 3D rendering), what
3297 // about supporting Q3 shaders?
3299 VM_SAFEPARMCOUNTRANGE(2, 3, VM_CL_R_PolygonBegin);
3301 if (!polys->initialized)
3302 VM_InitPolygons(polys);
3303 if(polys->progstarttime != prog->starttime)
3305 // from another progs? then reset the polys first (fixes crashes on map change, because that can make skinframe textures invalid)
3306 polys->num_vertices = polys->num_triangles = 0;
3307 polys->progstarttime = prog->starttime;
3309 if (polys->begin_active)
3311 VM_Warning("VM_CL_R_PolygonBegin: called twice without VM_CL_R_PolygonBegin after first\n");
3314 picname = PRVM_G_STRING(OFS_PARM0);
3320 if((int)PRVM_G_FLOAT(OFS_PARM1) & DRAWFLAG_MIPMAP)
3325 sf = R_SkinFrame_FindNextByName(sf, picname);
3327 while(sf && sf->textureflags != tf);
3329 if(!sf || !sf->base)
3330 sf = R_SkinFrame_LoadExternal(picname, tf, true);
3333 R_SkinFrame_MarkUsed(sf);
3336 polys->begin_texture = (sf && sf->base) ? sf->base : r_texture_white;
3337 polys->begin_texture_hasalpha = (sf && sf->base) ? sf->hasalpha : false;
3338 polys->begin_drawflag = (int)PRVM_G_FLOAT(OFS_PARM1) & DRAWFLAG_MASK;
3339 polys->begin_vertices = 0;
3340 polys->begin_active = true;
3341 polys->begin_draw2d = (prog->argc >= 3 ? (int)PRVM_G_FLOAT(OFS_PARM2) : r_refdef.draw2dstage);
3344 //void(vector org, vector texcoords, vector rgb, float alpha) R_PolygonVertex
3345 void VM_CL_R_PolygonVertex (void)
3347 vmpolygons_t* polys = vmpolygons + PRVM_GetProgNr();
3349 VM_SAFEPARMCOUNT(4, VM_CL_R_PolygonVertex);
3351 if (!polys->begin_active)
3353 VM_Warning("VM_CL_R_PolygonVertex: VM_CL_R_PolygonBegin wasn't called\n");
3357 if (polys->begin_vertices >= VMPOLYGONS_MAXPOINTS)
3359 VM_Warning("VM_CL_R_PolygonVertex: may have %i vertices max\n", VMPOLYGONS_MAXPOINTS);
3363 polys->begin_vertex[polys->begin_vertices][0] = PRVM_G_VECTOR(OFS_PARM0)[0];
3364 polys->begin_vertex[polys->begin_vertices][1] = PRVM_G_VECTOR(OFS_PARM0)[1];
3365 polys->begin_vertex[polys->begin_vertices][2] = PRVM_G_VECTOR(OFS_PARM0)[2];
3366 polys->begin_texcoord[polys->begin_vertices][0] = PRVM_G_VECTOR(OFS_PARM1)[0];
3367 polys->begin_texcoord[polys->begin_vertices][1] = PRVM_G_VECTOR(OFS_PARM1)[1];
3368 polys->begin_color[polys->begin_vertices][0] = PRVM_G_VECTOR(OFS_PARM2)[0];
3369 polys->begin_color[polys->begin_vertices][1] = PRVM_G_VECTOR(OFS_PARM2)[1];
3370 polys->begin_color[polys->begin_vertices][2] = PRVM_G_VECTOR(OFS_PARM2)[2];
3371 polys->begin_color[polys->begin_vertices][3] = PRVM_G_FLOAT(OFS_PARM3);
3372 polys->begin_vertices++;
3375 //void() R_EndPolygon
3376 void VM_CL_R_PolygonEnd (void)
3378 vmpolygons_t* polys = vmpolygons + PRVM_GetProgNr();
3380 VM_SAFEPARMCOUNT(0, VM_CL_R_PolygonEnd);
3381 if (!polys->begin_active)
3383 VM_Warning("VM_CL_R_PolygonEnd: VM_CL_R_PolygonBegin wasn't called\n");
3386 polys->begin_active = false;
3387 if (polys->begin_vertices >= 3)
3388 VMPolygons_Store(polys);
3390 VM_Warning("VM_CL_R_PolygonEnd: %i vertices isn't a good choice\n", polys->begin_vertices);
3393 static vmpolygons_t debugPolys;
3395 void Debug_PolygonBegin(const char *picname, int drawflag)
3397 if(!debugPolys.initialized)
3398 VM_InitPolygons(&debugPolys);
3399 if(debugPolys.begin_active)
3401 Con_Printf("Debug_PolygonBegin: called twice without Debug_PolygonEnd after first\n");
3404 debugPolys.begin_texture = picname[0] ? Draw_CachePic_Flags (picname, CACHEPICFLAG_NOTPERSISTENT)->tex : r_texture_white;
3405 debugPolys.begin_drawflag = drawflag;
3406 debugPolys.begin_vertices = 0;
3407 debugPolys.begin_active = true;
3410 void Debug_PolygonVertex(float x, float y, float z, float s, float t, float r, float g, float b, float a)
3412 if(!debugPolys.begin_active)
3414 Con_Printf("Debug_PolygonVertex: Debug_PolygonBegin wasn't called\n");
3418 if(debugPolys.begin_vertices > VMPOLYGONS_MAXPOINTS)
3420 Con_Printf("Debug_PolygonVertex: may have %i vertices max\n", VMPOLYGONS_MAXPOINTS);
3424 debugPolys.begin_vertex[debugPolys.begin_vertices][0] = x;
3425 debugPolys.begin_vertex[debugPolys.begin_vertices][1] = y;
3426 debugPolys.begin_vertex[debugPolys.begin_vertices][2] = z;
3427 debugPolys.begin_texcoord[debugPolys.begin_vertices][0] = s;
3428 debugPolys.begin_texcoord[debugPolys.begin_vertices][1] = t;
3429 debugPolys.begin_color[debugPolys.begin_vertices][0] = r;
3430 debugPolys.begin_color[debugPolys.begin_vertices][1] = g;
3431 debugPolys.begin_color[debugPolys.begin_vertices][2] = b;
3432 debugPolys.begin_color[debugPolys.begin_vertices][3] = a;
3433 debugPolys.begin_vertices++;
3436 void Debug_PolygonEnd(void)
3438 if (!debugPolys.begin_active)
3440 Con_Printf("Debug_PolygonEnd: Debug_PolygonBegin wasn't called\n");
3443 debugPolys.begin_active = false;
3444 if (debugPolys.begin_vertices >= 3)
3445 VMPolygons_Store(&debugPolys);
3447 Con_Printf("Debug_PolygonEnd: %i vertices isn't a good choice\n", debugPolys.begin_vertices);
3454 Returns false if any part of the bottom of the entity is off an edge that
3459 qboolean CL_CheckBottom (prvm_edict_t *ent)
3461 vec3_t mins, maxs, start, stop;
3466 VectorAdd (PRVM_clientedictvector(ent, origin), PRVM_clientedictvector(ent, mins), mins);
3467 VectorAdd (PRVM_clientedictvector(ent, origin), PRVM_clientedictvector(ent, maxs), maxs);
3469 // if all of the points under the corners are solid world, don't bother
3470 // with the tougher checks
3471 // the corners must be within 16 of the midpoint
3472 start[2] = mins[2] - 1;
3473 for (x=0 ; x<=1 ; x++)
3474 for (y=0 ; y<=1 ; y++)
3476 start[0] = x ? maxs[0] : mins[0];
3477 start[1] = y ? maxs[1] : mins[1];
3478 if (!(CL_PointSuperContents(start) & (SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY)))
3482 return true; // we got out easy
3486 // check it for real...
3490 // the midpoint must be within 16 of the bottom
3491 start[0] = stop[0] = (mins[0] + maxs[0])*0.5;
3492 start[1] = stop[1] = (mins[1] + maxs[1])*0.5;
3493 stop[2] = start[2] - 2*sv_stepheight.value;
3494 trace = CL_TraceLine(start, stop, MOVE_NOMONSTERS, ent, CL_GenericHitSuperContentsMask(ent), true, false, NULL, true, false);
3496 if (trace.fraction == 1.0)
3498 mid = bottom = trace.endpos[2];
3500 // the corners must be within 16 of the midpoint
3501 for (x=0 ; x<=1 ; x++)
3502 for (y=0 ; y<=1 ; y++)
3504 start[0] = stop[0] = x ? maxs[0] : mins[0];
3505 start[1] = stop[1] = y ? maxs[1] : mins[1];
3507 trace = CL_TraceLine(start, stop, MOVE_NOMONSTERS, ent, CL_GenericHitSuperContentsMask(ent), true, false, NULL, true, false);
3509 if (trace.fraction != 1.0 && trace.endpos[2] > bottom)
3510 bottom = trace.endpos[2];
3511 if (trace.fraction == 1.0 || mid - trace.endpos[2] > sv_stepheight.value)
3522 Called by monster program code.
3523 The move will be adjusted for slopes and stairs, but if the move isn't
3524 possible, no move is done and false is returned
3527 qboolean CL_movestep (prvm_edict_t *ent, vec3_t move, qboolean relink, qboolean noenemy, qboolean settrace)
3530 vec3_t oldorg, neworg, end, traceendpos;
3533 prvm_edict_t *enemy;
3536 VectorCopy (PRVM_clientedictvector(ent, origin), oldorg);
3537 VectorAdd (PRVM_clientedictvector(ent, origin), move, neworg);
3539 // flying monsters don't step up
3540 if ( (int)PRVM_clientedictfloat(ent, flags) & (FL_SWIM | FL_FLY) )
3542 // try one move with vertical motion, then one without
3543 for (i=0 ; i<2 ; i++)
3545 VectorAdd (PRVM_clientedictvector(ent, origin), move, neworg);
3546 enemy = PRVM_PROG_TO_EDICT(PRVM_clientedictedict(ent, enemy));
3547 if (i == 0 && enemy != prog->edicts)
3549 dz = PRVM_clientedictvector(ent, origin)[2] - PRVM_clientedictvector(PRVM_PROG_TO_EDICT(PRVM_clientedictedict(ent, enemy)), origin)[2];
3555 trace = CL_TraceBox(PRVM_clientedictvector(ent, origin), PRVM_clientedictvector(ent, mins), PRVM_clientedictvector(ent, maxs), neworg, MOVE_NORMAL, ent, CL_GenericHitSuperContentsMask(ent), true, true, &svent, true);
3557 CL_VM_SetTraceGlobals(&trace, svent);
3559 if (trace.fraction == 1)
3561 VectorCopy(trace.endpos, traceendpos);
3562 if (((int)PRVM_clientedictfloat(ent, flags) & FL_SWIM) && !(CL_PointSuperContents(traceendpos) & SUPERCONTENTS_LIQUIDSMASK))
3563 return false; // swim monster left water
3565 VectorCopy (traceendpos, PRVM_clientedictvector(ent, origin));
3571 if (enemy == prog->edicts)
3578 // push down from a step height above the wished position
3579 neworg[2] += sv_stepheight.value;
3580 VectorCopy (neworg, end);
3581 end[2] -= sv_stepheight.value*2;
3583 trace = CL_TraceBox(neworg, PRVM_clientedictvector(ent, mins), PRVM_clientedictvector(ent, maxs), end, MOVE_NORMAL, ent, CL_GenericHitSuperContentsMask(ent), true, true, &svent, true);
3585 CL_VM_SetTraceGlobals(&trace, svent);
3587 if (trace.startsolid)
3589 neworg[2] -= sv_stepheight.value;
3590 trace = CL_TraceBox(neworg, PRVM_clientedictvector(ent, mins), PRVM_clientedictvector(ent, maxs), end, MOVE_NORMAL, ent, CL_GenericHitSuperContentsMask(ent), true, true, &svent, true);
3592 CL_VM_SetTraceGlobals(&trace, svent);
3593 if (trace.startsolid)
3596 if (trace.fraction == 1)
3598 // if monster had the ground pulled out, go ahead and fall
3599 if ( (int)PRVM_clientedictfloat(ent, flags) & FL_PARTIALGROUND )
3601 VectorAdd (PRVM_clientedictvector(ent, origin), move, PRVM_clientedictvector(ent, origin));
3604 PRVM_clientedictfloat(ent, flags) = (int)PRVM_clientedictfloat(ent, flags) & ~FL_ONGROUND;
3608 return false; // walked off an edge
3611 // check point traces down for dangling corners
3612 VectorCopy (trace.endpos, PRVM_clientedictvector(ent, origin));
3614 if (!CL_CheckBottom (ent))
3616 if ( (int)PRVM_clientedictfloat(ent, flags) & FL_PARTIALGROUND )
3617 { // entity had floor mostly pulled out from underneath it
3618 // and is trying to correct
3623 VectorCopy (oldorg, PRVM_clientedictvector(ent, origin));
3627 if ( (int)PRVM_clientedictfloat(ent, flags) & FL_PARTIALGROUND )
3628 PRVM_clientedictfloat(ent, flags) = (int)PRVM_clientedictfloat(ent, flags) & ~FL_PARTIALGROUND;
3630 PRVM_clientedictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
3642 float(float yaw, float dist[, settrace]) walkmove
3645 static void VM_CL_walkmove (void)
3654 VM_SAFEPARMCOUNTRANGE(2, 3, VM_CL_walkmove);
3656 // assume failure if it returns early
3657 PRVM_G_FLOAT(OFS_RETURN) = 0;
3659 ent = PRVM_PROG_TO_EDICT(PRVM_clientglobaledict(self));
3660 if (ent == prog->edicts)
3662 VM_Warning("walkmove: can not modify world entity\n");
3665 if (ent->priv.server->free)
3667 VM_Warning("walkmove: can not modify free entity\n");
3670 yaw = PRVM_G_FLOAT(OFS_PARM0);
3671 dist = PRVM_G_FLOAT(OFS_PARM1);
3672 settrace = prog->argc >= 3 && PRVM_G_FLOAT(OFS_PARM2);
3674 if ( !( (int)PRVM_clientedictfloat(ent, flags) & (FL_ONGROUND|FL_FLY|FL_SWIM) ) )
3677 yaw = yaw*M_PI*2 / 360;
3679 move[0] = cos(yaw)*dist;
3680 move[1] = sin(yaw)*dist;
3683 // save program state, because CL_movestep may call other progs
3684 oldf = prog->xfunction;
3685 oldself = PRVM_clientglobaledict(self);
3687 PRVM_G_FLOAT(OFS_RETURN) = CL_movestep(ent, move, true, false, settrace);
3690 // restore program state
3691 prog->xfunction = oldf;
3692 PRVM_clientglobaledict(self) = oldself;
3699 string(string key) serverkey
3702 void VM_CL_serverkey(void)
3704 char string[VM_STRINGTEMP_LENGTH];
3705 VM_SAFEPARMCOUNT(1, VM_CL_serverkey);
3706 InfoString_GetValue(cl.qw_serverinfo, PRVM_G_STRING(OFS_PARM0), string, sizeof(string));
3707 PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(string);
3714 Checks if an entity is in a point's PVS.
3715 Should be fast but can be inexact.
3717 float checkpvs(vector viewpos, entity viewee) = #240;
3720 static void VM_CL_checkpvs (void)
3723 prvm_edict_t *viewee;
3729 unsigned char fatpvs[MAX_MAP_LEAFS/8];
3732 VM_SAFEPARMCOUNT(2, VM_SV_checkpvs);
3733 VectorCopy(PRVM_G_VECTOR(OFS_PARM0), viewpos);
3734 viewee = PRVM_G_EDICT(OFS_PARM1);
3736 if(viewee->priv.required->free)
3738 VM_Warning("checkpvs: can not check free entity\n");
3739 PRVM_G_FLOAT(OFS_RETURN) = 4;
3743 VectorAdd(PRVM_serveredictvector(viewee, origin), PRVM_serveredictvector(viewee, mins), mi);
3744 VectorAdd(PRVM_serveredictvector(viewee, origin), PRVM_serveredictvector(viewee, maxs), ma);
3747 if(!sv.worldmodel->brush.GetPVS || !sv.worldmodel->brush.BoxTouchingPVS)
3749 // no PVS support on this worldmodel... darn
3750 PRVM_G_FLOAT(OFS_RETURN) = 3;
3753 pvs = sv.worldmodel->brush.GetPVS(sv.worldmodel, viewpos);
3756 // viewpos isn't in any PVS... darn
3757 PRVM_G_FLOAT(OFS_RETURN) = 2;
3760 PRVM_G_FLOAT(OFS_RETURN) = sv.worldmodel->brush.BoxTouchingPVS(sv.worldmodel, pvs, mi, ma);
3762 // using fat PVS like FTEQW does (slow)
3763 if(!sv.worldmodel->brush.FatPVS || !sv.worldmodel->brush.BoxTouchingPVS)
3765 // no PVS support on this worldmodel... darn
3766 PRVM_G_FLOAT(OFS_RETURN) = 3;
3769 fatpvsbytes = sv.worldmodel->brush.FatPVS(sv.worldmodel, viewpos, 8, fatpvs, sizeof(fatpvs), false);
3772 // viewpos isn't in any PVS... darn
3773 PRVM_G_FLOAT(OFS_RETURN) = 2;
3776 PRVM_G_FLOAT(OFS_RETURN) = sv.worldmodel->brush.BoxTouchingPVS(sv.worldmodel, fatpvs, mi, ma);
3780 // #263 float(float modlindex) skel_create = #263; // (FTE_CSQC_SKELETONOBJECTS) create a skeleton (be sure to assign this value into .skeletonindex for use), returns skeleton index (1 or higher) on success, returns 0 on failure (for example if the modelindex is not skeletal), it is recommended that you create a new skeleton if you change modelindex.
3781 static void VM_CL_skel_create(void)
3783 int modelindex = (int)PRVM_G_FLOAT(OFS_PARM0);
3784 dp_model_t *model = CL_GetModelByIndex(modelindex);
3785 skeleton_t *skeleton;
3787 PRVM_G_FLOAT(OFS_RETURN) = 0;
3788 if (!model || !model->num_bones)
3790 for (i = 0;i < MAX_EDICTS;i++)
3791 if (!prog->skeletons[i])
3793 if (i == MAX_EDICTS)
3795 prog->skeletons[i] = skeleton = (skeleton_t *)Mem_Alloc(cls.levelmempool, sizeof(skeleton_t) + model->num_bones * sizeof(matrix4x4_t));
3796 PRVM_G_FLOAT(OFS_RETURN) = i + 1;
3797 skeleton->model = model;
3798 skeleton->relativetransforms = (matrix4x4_t *)(skeleton+1);
3799 // initialize to identity matrices
3800 for (i = 0;i < skeleton->model->num_bones;i++)
3801 skeleton->relativetransforms[i] = identitymatrix;
3804 // #264 float(float skel, entity ent, float modlindex, float retainfrac, float firstbone, float lastbone) skel_build = #264; // (FTE_CSQC_SKELETONOBJECTS) blend in a percentage of standard animation, 0 replaces entirely, 1 does nothing, 0.5 blends half, etc, and this only alters the bones in the specified range for which out of bounds values like 0,100000 are safe (uses .frame, .frame2, .frame3, .frame4, .lerpfrac, .lerpfrac3, .lerpfrac4, .frame1time, .frame2time, .frame3time, .frame4time), returns skel on success, 0 on failure
3805 static void VM_CL_skel_build(void)
3807 int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
3808 skeleton_t *skeleton;
3809 prvm_edict_t *ed = PRVM_G_EDICT(OFS_PARM1);
3810 int modelindex = (int)PRVM_G_FLOAT(OFS_PARM2);
3811 float retainfrac = PRVM_G_FLOAT(OFS_PARM3);
3812 int firstbone = PRVM_G_FLOAT(OFS_PARM4) - 1;
3813 int lastbone = PRVM_G_FLOAT(OFS_PARM5) - 1;
3814 dp_model_t *model = CL_GetModelByIndex(modelindex);
3819 framegroupblend_t framegroupblend[MAX_FRAMEGROUPBLENDS];
3820 frameblend_t frameblend[MAX_FRAMEBLENDS];
3821 matrix4x4_t blendedmatrix;
3823 PRVM_G_FLOAT(OFS_RETURN) = 0;
3824 if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
3826 firstbone = max(0, firstbone);
3827 lastbone = min(lastbone, model->num_bones - 1);
3828 lastbone = min(lastbone, skeleton->model->num_bones - 1);
3829 VM_GenerateFrameGroupBlend(framegroupblend, ed);
3830 VM_FrameBlendFromFrameGroupBlend(frameblend, framegroupblend, model);
3831 blendfrac = 1.0f - retainfrac;
3832 for (numblends = 0;numblends < MAX_FRAMEBLENDS && frameblend[numblends].lerp;numblends++)
3833 frameblend[numblends].lerp *= blendfrac;
3834 for (bonenum = firstbone;bonenum <= lastbone;bonenum++)
3836 memset(&blendedmatrix, 0, sizeof(blendedmatrix));
3837 Matrix4x4_Accumulate(&blendedmatrix, &skeleton->relativetransforms[bonenum], retainfrac);
3838 for (blendindex = 0;blendindex < numblends;blendindex++)
3840 Matrix4x4_FromBonePose6s(&matrix, model->num_posescale, model->data_poses6s + 6 * (frameblend[blendindex].subframe * model->num_bones + bonenum));
3841 Matrix4x4_Accumulate(&blendedmatrix, &matrix, frameblend[blendindex].lerp);
3843 skeleton->relativetransforms[bonenum] = blendedmatrix;
3845 PRVM_G_FLOAT(OFS_RETURN) = skeletonindex + 1;
3848 // #265 float(float skel) skel_get_numbones = #265; // (FTE_CSQC_SKELETONOBJECTS) returns how many bones exist in the created skeleton
3849 static void VM_CL_skel_get_numbones(void)
3851 int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
3852 skeleton_t *skeleton;
3853 PRVM_G_FLOAT(OFS_RETURN) = 0;
3854 if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
3856 PRVM_G_FLOAT(OFS_RETURN) = skeleton->model->num_bones;
3859 // #266 string(float skel, float bonenum) skel_get_bonename = #266; // (FTE_CSQC_SKELETONOBJECTS) returns name of bone (as a tempstring)
3860 static void VM_CL_skel_get_bonename(void)
3862 int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
3863 int bonenum = (int)PRVM_G_FLOAT(OFS_PARM1) - 1;
3864 skeleton_t *skeleton;
3865 PRVM_G_INT(OFS_RETURN) = 0;
3866 if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
3868 if (bonenum < 0 || bonenum >= skeleton->model->num_bones)
3870 PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(skeleton->model->data_bones[bonenum].name);
3873 // #267 float(float skel, float bonenum) skel_get_boneparent = #267; // (FTE_CSQC_SKELETONOBJECTS) returns parent num for supplied bonenum, 0 if bonenum has no parent or bone does not exist (returned value is always less than bonenum, you can loop on this)
3874 static void VM_CL_skel_get_boneparent(void)
3876 int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
3877 int bonenum = (int)PRVM_G_FLOAT(OFS_PARM1) - 1;
3878 skeleton_t *skeleton;
3879 PRVM_G_FLOAT(OFS_RETURN) = 0;
3880 if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
3882 if (bonenum < 0 || bonenum >= skeleton->model->num_bones)
3884 PRVM_G_FLOAT(OFS_RETURN) = skeleton->model->data_bones[bonenum].parent + 1;
3887 // #268 float(float skel, string tagname) skel_find_bone = #268; // (FTE_CSQC_SKELETONOBJECTS) get number of bone with specified name, 0 on failure, tagindex (bonenum+1) on success, same as using gettagindex on the modelindex
3888 static void VM_CL_skel_find_bone(void)
3890 int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
3891 const char *tagname = PRVM_G_STRING(OFS_PARM1);
3892 skeleton_t *skeleton;
3893 PRVM_G_FLOAT(OFS_RETURN) = 0;
3894 if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
3896 PRVM_G_FLOAT(OFS_RETURN) = Mod_Alias_GetTagIndexForName(skeleton->model, 0, tagname);
3899 // #269 vector(float skel, float bonenum) skel_get_bonerel = #269; // (FTE_CSQC_SKELETONOBJECTS) get matrix of bone in skeleton relative to its parent - sets v_forward, v_right, v_up, returns origin (relative to parent bone)
3900 static void VM_CL_skel_get_bonerel(void)
3902 int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
3903 int bonenum = (int)PRVM_G_FLOAT(OFS_PARM1) - 1;
3904 skeleton_t *skeleton;
3906 vec3_t forward, left, up, origin;
3907 VectorClear(PRVM_G_VECTOR(OFS_RETURN));
3908 VectorClear(PRVM_clientglobalvector(v_forward));
3909 VectorClear(PRVM_clientglobalvector(v_right));
3910 VectorClear(PRVM_clientglobalvector(v_up));
3911 if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
3913 if (bonenum < 0 || bonenum >= skeleton->model->num_bones)
3915 matrix = skeleton->relativetransforms[bonenum];
3916 Matrix4x4_ToVectors(&matrix, forward, left, up, origin);
3917 VectorCopy(forward, PRVM_clientglobalvector(v_forward));
3918 VectorNegate(left, PRVM_clientglobalvector(v_right));
3919 VectorCopy(up, PRVM_clientglobalvector(v_up));
3920 VectorCopy(origin, PRVM_G_VECTOR(OFS_RETURN));
3923 // #270 vector(float skel, float bonenum) skel_get_boneabs = #270; // (FTE_CSQC_SKELETONOBJECTS) get matrix of bone in skeleton in model space - sets v_forward, v_right, v_up, returns origin (relative to entity)
3924 static void VM_CL_skel_get_boneabs(void)
3926 int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
3927 int bonenum = (int)PRVM_G_FLOAT(OFS_PARM1) - 1;
3928 skeleton_t *skeleton;
3931 vec3_t forward, left, up, origin;
3932 VectorClear(PRVM_G_VECTOR(OFS_RETURN));
3933 VectorClear(PRVM_clientglobalvector(v_forward));
3934 VectorClear(PRVM_clientglobalvector(v_right));
3935 VectorClear(PRVM_clientglobalvector(v_up));
3936 if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
3938 if (bonenum < 0 || bonenum >= skeleton->model->num_bones)
3940 matrix = skeleton->relativetransforms[bonenum];
3941 // convert to absolute
3942 while ((bonenum = skeleton->model->data_bones[bonenum].parent) >= 0)
3945 Matrix4x4_Concat(&matrix, &skeleton->relativetransforms[bonenum], &temp);
3947 Matrix4x4_ToVectors(&matrix, forward, left, up, origin);
3948 VectorCopy(forward, PRVM_clientglobalvector(v_forward));
3949 VectorNegate(left, PRVM_clientglobalvector(v_right));
3950 VectorCopy(up, PRVM_clientglobalvector(v_up));
3951 VectorCopy(origin, PRVM_G_VECTOR(OFS_RETURN));
3954 // #271 void(float skel, float bonenum, vector org) skel_set_bone = #271; // (FTE_CSQC_SKELETONOBJECTS) set matrix of bone relative to its parent, reads v_forward, v_right, v_up, takes origin as parameter (relative to parent bone)
3955 static void VM_CL_skel_set_bone(void)
3957 int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
3958 int bonenum = (int)PRVM_G_FLOAT(OFS_PARM1) - 1;
3959 vec3_t forward, left, up, origin;
3960 skeleton_t *skeleton;
3962 if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
3964 if (bonenum < 0 || bonenum >= skeleton->model->num_bones)
3966 VectorCopy(PRVM_clientglobalvector(v_forward), forward);
3967 VectorNegate(PRVM_clientglobalvector(v_right), left);
3968 VectorCopy(PRVM_clientglobalvector(v_up), up);
3969 VectorCopy(PRVM_G_VECTOR(OFS_PARM2), origin);
3970 Matrix4x4_FromVectors(&matrix, forward, left, up, origin);
3971 skeleton->relativetransforms[bonenum] = matrix;
3974 // #272 void(float skel, float bonenum, vector org) skel_mul_bone = #272; // (FTE_CSQC_SKELETONOBJECTS) transform bone matrix (relative to its parent) by the supplied matrix in v_forward, v_right, v_up, takes origin as parameter (relative to parent bone)
3975 static void VM_CL_skel_mul_bone(void)
3977 int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
3978 int bonenum = (int)PRVM_G_FLOAT(OFS_PARM1) - 1;
3979 vec3_t forward, left, up, origin;
3980 skeleton_t *skeleton;
3983 if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
3985 if (bonenum < 0 || bonenum >= skeleton->model->num_bones)
3987 VectorCopy(PRVM_G_VECTOR(OFS_PARM2), origin);
3988 VectorCopy(PRVM_clientglobalvector(v_forward), forward);
3989 VectorNegate(PRVM_clientglobalvector(v_right), left);
3990 VectorCopy(PRVM_clientglobalvector(v_up), up);
3991 Matrix4x4_FromVectors(&matrix, forward, left, up, origin);
3992 temp = skeleton->relativetransforms[bonenum];
3993 Matrix4x4_Concat(&skeleton->relativetransforms[bonenum], &matrix, &temp);
3996 // #273 void(float skel, float startbone, float endbone, vector org) skel_mul_bones = #273; // (FTE_CSQC_SKELETONOBJECTS) transform bone matrices (relative to their parents) by the supplied matrix in v_forward, v_right, v_up, takes origin as parameter (relative to parent bones)
3997 static void VM_CL_skel_mul_bones(void)
3999 int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
4000 int firstbone = PRVM_G_FLOAT(OFS_PARM1) - 1;
4001 int lastbone = PRVM_G_FLOAT(OFS_PARM2) - 1;
4003 vec3_t forward, left, up, origin;
4004 skeleton_t *skeleton;
4007 if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
4009 VectorCopy(PRVM_G_VECTOR(OFS_PARM3), origin);
4010 VectorCopy(PRVM_clientglobalvector(v_forward), forward);
4011 VectorNegate(PRVM_clientglobalvector(v_right), left);
4012 VectorCopy(PRVM_clientglobalvector(v_up), up);
4013 Matrix4x4_FromVectors(&matrix, forward, left, up, origin);
4014 firstbone = max(0, firstbone);
4015 lastbone = min(lastbone, skeleton->model->num_bones - 1);
4016 for (bonenum = firstbone;bonenum <= lastbone;bonenum++)
4018 temp = skeleton->relativetransforms[bonenum];
4019 Matrix4x4_Concat(&skeleton->relativetransforms[bonenum], &matrix, &temp);
4023 // #274 void(float skeldst, float skelsrc, float startbone, float endbone) skel_copybones = #274; // (FTE_CSQC_SKELETONOBJECTS) copy bone matrices (relative to their parents) from one skeleton to another, useful for copying a skeleton to a corpse
4024 static void VM_CL_skel_copybones(void)
4026 int skeletonindexdst = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
4027 int skeletonindexsrc = (int)PRVM_G_FLOAT(OFS_PARM1) - 1;
4028 int firstbone = PRVM_G_FLOAT(OFS_PARM2) - 1;
4029 int lastbone = PRVM_G_FLOAT(OFS_PARM3) - 1;
4031 skeleton_t *skeletondst;
4032 skeleton_t *skeletonsrc;
4033 if (skeletonindexdst < 0 || skeletonindexdst >= MAX_EDICTS || !(skeletondst = prog->skeletons[skeletonindexdst]))
4035 if (skeletonindexsrc < 0 || skeletonindexsrc >= MAX_EDICTS || !(skeletonsrc = prog->skeletons[skeletonindexsrc]))
4037 firstbone = max(0, firstbone);
4038 lastbone = min(lastbone, skeletondst->model->num_bones - 1);
4039 lastbone = min(lastbone, skeletonsrc->model->num_bones - 1);
4040 for (bonenum = firstbone;bonenum <= lastbone;bonenum++)
4041 skeletondst->relativetransforms[bonenum] = skeletonsrc->relativetransforms[bonenum];
4044 // #275 void(float skel) skel_delete = #275; // (FTE_CSQC_SKELETONOBJECTS) deletes skeleton at the beginning of the next frame (you can add the entity, delete the skeleton, renderscene, and it will still work)
4045 static void VM_CL_skel_delete(void)
4047 int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
4048 skeleton_t *skeleton;
4049 if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
4052 prog->skeletons[skeletonindex] = NULL;
4055 // #276 float(float modlindex, string framename) frameforname = #276; // (FTE_CSQC_SKELETONOBJECTS) finds number of a specified frame in the animation, returns -1 if no match found
4056 static void VM_CL_frameforname(void)
4058 int modelindex = (int)PRVM_G_FLOAT(OFS_PARM0);
4059 dp_model_t *model = CL_GetModelByIndex(modelindex);
4060 const char *name = PRVM_G_STRING(OFS_PARM1);
4062 PRVM_G_FLOAT(OFS_RETURN) = -1;
4063 if (!model || !model->animscenes)
4065 for (i = 0;i < model->numframes;i++)
4067 if (!strcasecmp(model->animscenes[i].name, name))
4069 PRVM_G_FLOAT(OFS_RETURN) = i;
4075 // #277 float(float modlindex, float framenum) frameduration = #277; // (FTE_CSQC_SKELETONOBJECTS) returns the intended play time (in seconds) of the specified framegroup, if it does not exist the result is 0, if it is a single frame it may be a small value around 0.1 or 0.
4076 static void VM_CL_frameduration(void)
4078 int modelindex = (int)PRVM_G_FLOAT(OFS_PARM0);
4079 dp_model_t *model = CL_GetModelByIndex(modelindex);
4080 int framenum = (int)PRVM_G_FLOAT(OFS_PARM1);
4081 PRVM_G_FLOAT(OFS_RETURN) = 0;
4082 if (!model || !model->animscenes || framenum < 0 || framenum >= model->numframes)
4084 if (model->animscenes[framenum].framerate)
4085 PRVM_G_FLOAT(OFS_RETURN) = model->animscenes[framenum].framecount / model->animscenes[framenum].framerate;
4088 void VM_CL_RotateMoves(void)
4091 * Obscure builtin used by GAME_XONOTIC.
4093 * Edits the input history of cl_movement by rotating all move commands
4094 * currently in the queue using the given transform.
4096 * The vector passed is an "angles transform" as used by warpzonelib, i.e.
4097 * v_angle-like (non-inverted) euler angles that perform the rotation
4098 * of the space that is to be done.
4100 * This is meant to be used as a fixangle replacement after passing
4101 * through a warpzone/portal: the client is told about the warp transform,
4102 * and calls this function in the same frame as the one on which the
4103 * client's origin got changed by the serverside teleport. Then this code
4104 * transforms the pre-warp input (which matches the empty space behind
4105 * the warp plane) into post-warp input (which matches the target area
4106 * of the warp). Also, at the same time, the client has to use
4107 * R_SetView to adjust VF_CL_VIEWANGLES according to the same transform.
4109 * This together allows warpzone motion to be perfectly predicted by
4112 * Furthermore, for perfect warpzone behaviour, the server side also
4113 * has to detect input the client sent before it received the origin
4114 * update, but after the warp occurred on the server, and has to adjust
4115 * input appropriately.
4118 vec3_t v = {0, 0, 0};
4120 VM_SAFEPARMCOUNT(1, VM_CL_RotateMoves);
4121 AngleVectorsFLU(PRVM_G_VECTOR(OFS_PARM0), x, y, z);
4122 Matrix4x4_FromVectors(&m, x, y, z, v);
4126 // #358 void(string cubemapname) loadcubemap
4127 static void VM_CL_loadcubemap(void)
4131 VM_SAFEPARMCOUNT(1, VM_CL_loadcubemap);
4132 name = PRVM_G_STRING(OFS_PARM0);
4136 //============================================================================
4138 // To create a almost working builtin file from this replace:
4139 // "^NULL.*" with ""
4140 // "^{.*//.*}:Wh\(.*\)" with "\1"
4142 // "^.*//:Wh{\#:d*}:Wh{.*}" with "\2 = \1;"
4143 // "\n\n+" with "\n\n"
4145 prvm_builtin_t vm_cl_builtins[] = {
4146 NULL, // #0 NULL function (not callable) (QUAKE)
4147 VM_CL_makevectors, // #1 void(vector ang) makevectors (QUAKE)
4148 VM_CL_setorigin, // #2 void(entity e, vector o) setorigin (QUAKE)
4149 VM_CL_setmodel, // #3 void(entity e, string m) setmodel (QUAKE)
4150 VM_CL_setsize, // #4 void(entity e, vector min, vector max) setsize (QUAKE)
4151 NULL, // #5 void(entity e, vector min, vector max) setabssize (QUAKE)
4152 VM_break, // #6 void() break (QUAKE)
4153 VM_random, // #7 float() random (QUAKE)
4154 VM_CL_sound, // #8 void(entity e, float chan, string samp) sound (QUAKE)
4155 VM_normalize, // #9 vector(vector v) normalize (QUAKE)
4156 VM_error, // #10 void(string e) error (QUAKE)
4157 VM_objerror, // #11 void(string e) objerror (QUAKE)
4158 VM_vlen, // #12 float(vector v) vlen (QUAKE)
4159 VM_vectoyaw, // #13 float(vector v) vectoyaw (QUAKE)
4160 VM_CL_spawn, // #14 entity() spawn (QUAKE)
4161 VM_remove, // #15 void(entity e) remove (QUAKE)
4162 VM_CL_traceline, // #16 void(vector v1, vector v2, float tryents, entity ignoreentity) traceline (QUAKE)
4163 NULL, // #17 entity() checkclient (QUAKE)
4164 VM_find, // #18 entity(entity start, .string fld, string match) find (QUAKE)
4165 VM_precache_sound, // #19 void(string s) precache_sound (QUAKE)
4166 VM_CL_precache_model, // #20 void(string s) precache_model (QUAKE)
4167 NULL, // #21 void(entity client, string s, ...) stuffcmd (QUAKE)
4168 VM_CL_findradius, // #22 entity(vector org, float rad) findradius (QUAKE)
4169 NULL, // #23 void(string s, ...) bprint (QUAKE)
4170 NULL, // #24 void(entity client, string s, ...) sprint (QUAKE)
4171 VM_dprint, // #25 void(string s, ...) dprint (QUAKE)
4172 VM_ftos, // #26 string(float f) ftos (QUAKE)
4173 VM_vtos, // #27 string(vector v) vtos (QUAKE)
4174 VM_coredump, // #28 void() coredump (QUAKE)
4175 VM_traceon, // #29 void() traceon (QUAKE)
4176 VM_traceoff, // #30 void() traceoff (QUAKE)
4177 VM_eprint, // #31 void(entity e) eprint (QUAKE)
4178 VM_CL_walkmove, // #32 float(float yaw, float dist[, float settrace]) walkmove (QUAKE)
4179 NULL, // #33 (QUAKE)
4180 VM_CL_droptofloor, // #34 float() droptofloor (QUAKE)
4181 VM_CL_lightstyle, // #35 void(float style, string value) lightstyle (QUAKE)
4182 VM_rint, // #36 float(float v) rint (QUAKE)
4183 VM_floor, // #37 float(float v) floor (QUAKE)
4184 VM_ceil, // #38 float(float v) ceil (QUAKE)
4185 NULL, // #39 (QUAKE)
4186 VM_CL_checkbottom, // #40 float(entity e) checkbottom (QUAKE)
4187 VM_CL_pointcontents, // #41 float(vector v) pointcontents (QUAKE)
4188 NULL, // #42 (QUAKE)
4189 VM_fabs, // #43 float(float f) fabs (QUAKE)
4190 NULL, // #44 vector(entity e, float speed) aim (QUAKE)
4191 VM_cvar, // #45 float(string s) cvar (QUAKE)
4192 VM_localcmd, // #46 void(string s) localcmd (QUAKE)
4193 VM_nextent, // #47 entity(entity e) nextent (QUAKE)
4194 VM_CL_particle, // #48 void(vector o, vector d, float color, float count) particle (QUAKE)
4195 VM_changeyaw, // #49 void() ChangeYaw (QUAKE)
4196 NULL, // #50 (QUAKE)
4197 VM_vectoangles, // #51 vector(vector v) vectoangles (QUAKE)
4198 NULL, // #52 void(float to, float f) WriteByte (QUAKE)
4199 NULL, // #53 void(float to, float f) WriteChar (QUAKE)
4200 NULL, // #54 void(float to, float f) WriteShort (QUAKE)
4201 NULL, // #55 void(float to, float f) WriteLong (QUAKE)
4202 NULL, // #56 void(float to, float f) WriteCoord (QUAKE)
4203 NULL, // #57 void(float to, float f) WriteAngle (QUAKE)
4204 NULL, // #58 void(float to, string s) WriteString (QUAKE)
4205 NULL, // #59 (QUAKE)
4206 VM_sin, // #60 float(float f) sin (DP_QC_SINCOSSQRTPOW)
4207 VM_cos, // #61 float(float f) cos (DP_QC_SINCOSSQRTPOW)
4208 VM_sqrt, // #62 float(float f) sqrt (DP_QC_SINCOSSQRTPOW)
4209 VM_changepitch, // #63 void(entity ent) changepitch (DP_QC_CHANGEPITCH)
4210 VM_CL_tracetoss, // #64 void(entity e, entity ignore) tracetoss (DP_QC_TRACETOSS)
4211 VM_etos, // #65 string(entity ent) etos (DP_QC_ETOS)
4212 NULL, // #66 (QUAKE)
4213 NULL, // #67 void(float step) movetogoal (QUAKE)
4214 VM_precache_file, // #68 string(string s) precache_file (QUAKE)
4215 VM_CL_makestatic, // #69 void(entity e) makestatic (QUAKE)
4216 NULL, // #70 void(string s) changelevel (QUAKE)
4217 NULL, // #71 (QUAKE)
4218 VM_cvar_set, // #72 void(string var, string val) cvar_set (QUAKE)
4219 NULL, // #73 void(entity client, strings) centerprint (QUAKE)
4220 VM_CL_ambientsound, // #74 void(vector pos, string samp, float vol, float atten) ambientsound (QUAKE)
4221 VM_CL_precache_model, // #75 string(string s) precache_model2 (QUAKE)
4222 VM_precache_sound, // #76 string(string s) precache_sound2 (QUAKE)
4223 VM_precache_file, // #77 string(string s) precache_file2 (QUAKE)
4224 NULL, // #78 void(entity e) setspawnparms (QUAKE)
4225 NULL, // #79 void(entity killer, entity killee) logfrag (QUAKEWORLD)
4226 NULL, // #80 string(entity e, string keyname) infokey (QUAKEWORLD)
4227 VM_stof, // #81 float(string s) stof (FRIK_FILE)
4228 NULL, // #82 void(vector where, float set) multicast (QUAKEWORLD)
4229 NULL, // #83 (QUAKE)
4230 NULL, // #84 (QUAKE)
4231 NULL, // #85 (QUAKE)
4232 NULL, // #86 (QUAKE)
4233 NULL, // #87 (QUAKE)
4234 NULL, // #88 (QUAKE)
4235 NULL, // #89 (QUAKE)
4236 VM_CL_tracebox, // #90 void(vector v1, vector min, vector max, vector v2, float nomonsters, entity forent) tracebox (DP_QC_TRACEBOX)
4237 VM_randomvec, // #91 vector() randomvec (DP_QC_RANDOMVEC)
4238 VM_CL_getlight, // #92 vector(vector org) getlight (DP_QC_GETLIGHT)
4239 VM_registercvar, // #93 float(string name, string value) registercvar (DP_REGISTERCVAR)
4240 VM_min, // #94 float(float a, floats) min (DP_QC_MINMAXBOUND)
4241 VM_max, // #95 float(float a, floats) max (DP_QC_MINMAXBOUND)
4242 VM_bound, // #96 float(float minimum, float val, float maximum) bound (DP_QC_MINMAXBOUND)
4243 VM_pow, // #97 float(float f, float f) pow (DP_QC_SINCOSSQRTPOW)
4244 VM_findfloat, // #98 entity(entity start, .float fld, float match) findfloat (DP_QC_FINDFLOAT)
4245 VM_checkextension, // #99 float(string s) checkextension (the basis of the extension system)
4246 // FrikaC and Telejano range #100-#199
4257 VM_fopen, // #110 float(string filename, float mode) fopen (FRIK_FILE)
4258 VM_fclose, // #111 void(float fhandle) fclose (FRIK_FILE)
4259 VM_fgets, // #112 string(float fhandle) fgets (FRIK_FILE)
4260 VM_fputs, // #113 void(float fhandle, string s) fputs (FRIK_FILE)
4261 VM_strlen, // #114 float(string s) strlen (FRIK_FILE)
4262 VM_strcat, // #115 string(string s1, string s2, ...) strcat (FRIK_FILE)
4263 VM_substring, // #116 string(string s, float start, float length) substring (FRIK_FILE)
4264 VM_stov, // #117 vector(string) stov (FRIK_FILE)
4265 VM_strzone, // #118 string(string s) strzone (FRIK_FILE)
4266 VM_strunzone, // #119 void(string s) strunzone (FRIK_FILE)
4347 // FTEQW range #200-#299
4366 VM_bitshift, // #218 float(float number, float quantity) bitshift (EXT_BITSHIFT)
4369 VM_strstrofs, // #221 float(string str, string sub[, float startpos]) strstrofs (FTE_STRINGS)
4370 VM_str2chr, // #222 float(string str, float ofs) str2chr (FTE_STRINGS)
4371 VM_chr2str, // #223 string(float c, ...) chr2str (FTE_STRINGS)
4372 VM_strconv, // #224 string(float ccase, float calpha, float cnum, string s, ...) strconv (FTE_STRINGS)
4373 VM_strpad, // #225 string(float chars, string s, ...) strpad (FTE_STRINGS)
4374 VM_infoadd, // #226 string(string info, string key, string value, ...) infoadd (FTE_STRINGS)
4375 VM_infoget, // #227 string(string info, string key) infoget (FTE_STRINGS)
4376 VM_strncmp, // #228 float(string s1, string s2, float len) strncmp (FTE_STRINGS)
4377 VM_strncasecmp, // #229 float(string s1, string s2) strcasecmp (FTE_STRINGS)
4378 VM_strncasecmp, // #230 float(string s1, string s2, float len) strncasecmp (FTE_STRINGS)
4380 NULL, // #232 void(float index, float type, .void field) SV_AddStat (EXT_CSQC)
4388 VM_CL_checkpvs, // #240
4411 VM_CL_skel_create, // #263 float(float modlindex) skel_create = #263; // (FTE_CSQC_SKELETONOBJECTS) create a skeleton (be sure to assign this value into .skeletonindex for use), returns skeleton index (1 or higher) on success, returns 0 on failure (for example if the modelindex is not skeletal), it is recommended that you create a new skeleton if you change modelindex.
4412 VM_CL_skel_build, // #264 float(float skel, entity ent, float modlindex, float retainfrac, float firstbone, float lastbone) skel_build = #264; // (FTE_CSQC_SKELETONOBJECTS) blend in a percentage of standard animation, 0 replaces entirely, 1 does nothing, 0.5 blends half, etc, and this only alters the bones in the specified range for which out of bounds values like 0,100000 are safe (uses .frame, .frame2, .frame3, .frame4, .lerpfrac, .lerpfrac3, .lerpfrac4, .frame1time, .frame2time, .frame3time, .frame4time), returns skel on success, 0 on failure
4413 VM_CL_skel_get_numbones, // #265 float(float skel) skel_get_numbones = #265; // (FTE_CSQC_SKELETONOBJECTS) returns how many bones exist in the created skeleton
4414 VM_CL_skel_get_bonename, // #266 string(float skel, float bonenum) skel_get_bonename = #266; // (FTE_CSQC_SKELETONOBJECTS) returns name of bone (as a tempstring)
4415 VM_CL_skel_get_boneparent, // #267 float(float skel, float bonenum) skel_get_boneparent = #267; // (FTE_CSQC_SKELETONOBJECTS) returns parent num for supplied bonenum, -1 if bonenum has no parent or bone does not exist (returned value is always less than bonenum, you can loop on this)
4416 VM_CL_skel_find_bone, // #268 float(float skel, string tagname) skel_find_bone = #268; // (FTE_CSQC_SKELETONOBJECTS) get number of bone with specified name, 0 on failure, tagindex (bonenum+1) on success, same as using gettagindex on the modelindex
4417 VM_CL_skel_get_bonerel, // #269 vector(float skel, float bonenum) skel_get_bonerel = #269; // (FTE_CSQC_SKELETONOBJECTS) get matrix of bone in skeleton relative to its parent - sets v_forward, v_right, v_up, returns origin (relative to parent bone)
4418 VM_CL_skel_get_boneabs, // #270 vector(float skel, float bonenum) skel_get_boneabs = #270; // (FTE_CSQC_SKELETONOBJECTS) get matrix of bone in skeleton in model space - sets v_forward, v_right, v_up, returns origin (relative to entity)
4419 VM_CL_skel_set_bone, // #271 void(float skel, float bonenum, vector org) skel_set_bone = #271; // (FTE_CSQC_SKELETONOBJECTS) set matrix of bone relative to its parent, reads v_forward, v_right, v_up, takes origin as parameter (relative to parent bone)
4420 VM_CL_skel_mul_bone, // #272 void(float skel, float bonenum, vector org) skel_mul_bone = #272; // (FTE_CSQC_SKELETONOBJECTS) transform bone matrix (relative to its parent) by the supplied matrix in v_forward, v_right, v_up, takes origin as parameter (relative to parent bone)
4421 VM_CL_skel_mul_bones, // #273 void(float skel, float startbone, float endbone, vector org) skel_mul_bones = #273; // (FTE_CSQC_SKELETONOBJECTS) transform bone matrices (relative to their parents) by the supplied matrix in v_forward, v_right, v_up, takes origin as parameter (relative to parent bones)
4422 VM_CL_skel_copybones, // #274 void(float skeldst, float skelsrc, float startbone, float endbone) skel_copybones = #274; // (FTE_CSQC_SKELETONOBJECTS) copy bone matrices (relative to their parents) from one skeleton to another, useful for copying a skeleton to a corpse
4423 VM_CL_skel_delete, // #275 void(float skel) skel_delete = #275; // (FTE_CSQC_SKELETONOBJECTS) deletes skeleton at the beginning of the next frame (you can add the entity, delete the skeleton, renderscene, and it will still work)
4424 VM_CL_frameforname, // #276 float(float modlindex, string framename) frameforname = #276; // (FTE_CSQC_SKELETONOBJECTS) finds number of a specified frame in the animation, returns -1 if no match found
4425 VM_CL_frameduration, // #277 float(float modlindex, float framenum) frameduration = #277; // (FTE_CSQC_SKELETONOBJECTS) returns the intended play time (in seconds) of the specified framegroup, if it does not exist the result is 0, if it is a single frame it may be a small value around 0.1 or 0.
4448 // CSQC range #300-#399
4449 VM_CL_R_ClearScene, // #300 void() clearscene (EXT_CSQC)
4450 VM_CL_R_AddEntities, // #301 void(float mask) addentities (EXT_CSQC)
4451 VM_CL_R_AddEntity, // #302 void(entity ent) addentity (EXT_CSQC)
4452 VM_CL_R_SetView, // #303 float(float property, ...) setproperty (EXT_CSQC)
4453 VM_CL_R_RenderScene, // #304 void() renderscene (EXT_CSQC)
4454 VM_CL_R_AddDynamicLight, // #305 void(vector org, float radius, vector lightcolours) adddynamiclight (EXT_CSQC)
4455 VM_CL_R_PolygonBegin, // #306 void(string texturename, float flag, float is2d[NYI: , float lines]) R_BeginPolygon
4456 VM_CL_R_PolygonVertex, // #307 void(vector org, vector texcoords, vector rgb, float alpha) R_PolygonVertex
4457 VM_CL_R_PolygonEnd, // #308 void() R_EndPolygon
4458 VM_CL_R_SetView, // #309 float(float property) getproperty (EXT_CSQC)
4459 VM_CL_unproject, // #310 vector (vector v) cs_unproject (EXT_CSQC)
4460 VM_CL_project, // #311 vector (vector v) cs_project (EXT_CSQC)
4464 VM_drawline, // #315 void(float width, vector pos1, vector pos2, float flag) drawline (EXT_CSQC)
4465 VM_iscachedpic, // #316 float(string name) iscachedpic (EXT_CSQC)
4466 VM_precache_pic, // #317 string(string name, float trywad) precache_pic (EXT_CSQC)
4467 VM_getimagesize, // #318 vector(string picname) draw_getimagesize (EXT_CSQC)
4468 VM_freepic, // #319 void(string name) freepic (EXT_CSQC)
4469 VM_drawcharacter, // #320 float(vector position, float character, vector scale, vector rgb, float alpha, float flag) drawcharacter (EXT_CSQC)
4470 VM_drawstring, // #321 float(vector position, string text, vector scale, vector rgb, float alpha[, float flag]) drawstring (EXT_CSQC, DP_CSQC)
4471 VM_drawpic, // #322 float(vector position, string pic, vector size, vector rgb, float alpha[, float flag]) drawpic (EXT_CSQC)
4472 VM_drawfill, // #323 float(vector position, vector size, vector rgb, float alpha, float flag) drawfill (EXT_CSQC)
4473 VM_drawsetcliparea, // #324 void(float x, float y, float width, float height) drawsetcliparea
4474 VM_drawresetcliparea, // #325 void(void) drawresetcliparea
4475 VM_drawcolorcodedstring, // #326 float drawcolorcodedstring(vector position, string text, vector scale, vector rgb, float alpha, float flag) (EXT_CSQC)
4476 VM_stringwidth, // #327 // FIXME is this okay?
4477 VM_drawsubpic, // #328 // FIXME is this okay?
4478 VM_drawrotpic, // #329 // FIXME is this okay?
4479 VM_CL_getstatf, // #330 float(float stnum) getstatf (EXT_CSQC)
4480 VM_CL_getstati, // #331 float(float stnum) getstati (EXT_CSQC)
4481 VM_CL_getstats, // #332 string(float firststnum) getstats (EXT_CSQC)
4482 VM_CL_setmodelindex, // #333 void(entity e, float mdlindex) setmodelindex (EXT_CSQC)
4483 VM_CL_modelnameforindex, // #334 string(float mdlindex) modelnameforindex (EXT_CSQC)
4484 VM_CL_particleeffectnum, // #335 float(string effectname) particleeffectnum (EXT_CSQC)
4485 VM_CL_trailparticles, // #336 void(entity ent, float effectnum, vector start, vector end) trailparticles (EXT_CSQC)
4486 VM_CL_pointparticles, // #337 void(float effectnum, vector origin [, vector dir, float count]) pointparticles (EXT_CSQC)
4487 VM_centerprint, // #338 void(string s, ...) centerprint (EXT_CSQC)
4488 VM_print, // #339 void(string s, ...) print (EXT_CSQC, DP_SV_PRINT)
4489 VM_keynumtostring, // #340 string(float keynum) keynumtostring (EXT_CSQC)
4490 VM_stringtokeynum, // #341 float(string keyname) stringtokeynum (EXT_CSQC)
4491 VM_getkeybind, // #342 string(float keynum[, float bindmap]) getkeybind (EXT_CSQC)
4492 VM_CL_setcursormode, // #343 void(float usecursor) setcursormode (DP_CSQC)
4493 VM_CL_getmousepos, // #344 vector() getmousepos (DP_CSQC)
4494 VM_CL_getinputstate, // #345 float(float framenum) getinputstate (EXT_CSQC)
4495 VM_CL_setsensitivityscale, // #346 void(float sens) setsensitivityscale (EXT_CSQC)
4496 VM_CL_runplayerphysics, // #347 void() runstandardplayerphysics (EXT_CSQC)
4497 VM_CL_getplayerkey, // #348 string(float playernum, string keyname) getplayerkeyvalue (EXT_CSQC)
4498 VM_CL_isdemo, // #349 float() isdemo (EXT_CSQC)
4499 VM_isserver, // #350 float() isserver (EXT_CSQC)
4500 VM_CL_setlistener, // #351 void(vector origin, vector forward, vector right, vector up) SetListener (EXT_CSQC)
4501 VM_CL_registercmd, // #352 void(string cmdname) registercommand (EXT_CSQC)
4502 VM_wasfreed, // #353 float(entity ent) wasfreed (EXT_CSQC) (should be availabe on server too)
4503 VM_CL_serverkey, // #354 string(string key) serverkey (EXT_CSQC)
4504 VM_CL_videoplaying, // #355
4505 VM_findfont, // #356 float(string fontname) loadfont (DP_GFX_FONTS)
4506 VM_loadfont, // #357 float(string fontname, string fontmaps, string sizes, float slot) loadfont (DP_GFX_FONTS)
4507 VM_CL_loadcubemap, // #358 void(string cubemapname) loadcubemap (DP_GFX_)
4509 VM_CL_ReadByte, // #360 float() readbyte (EXT_CSQC)
4510 VM_CL_ReadChar, // #361 float() readchar (EXT_CSQC)
4511 VM_CL_ReadShort, // #362 float() readshort (EXT_CSQC)
4512 VM_CL_ReadLong, // #363 float() readlong (EXT_CSQC)
4513 VM_CL_ReadCoord, // #364 float() readcoord (EXT_CSQC)
4514 VM_CL_ReadAngle, // #365 float() readangle (EXT_CSQC)
4515 VM_CL_ReadString, // #366 string() readstring (EXT_CSQC)
4516 VM_CL_ReadFloat, // #367 float() readfloat (EXT_CSQC)
4549 // LordHavoc's range #400-#499
4550 VM_CL_copyentity, // #400 void(entity from, entity to) copyentity (DP_QC_COPYENTITY)
4551 NULL, // #401 void(entity ent, float colors) setcolor (DP_QC_SETCOLOR)
4552 VM_findchain, // #402 entity(.string fld, string match) findchain (DP_QC_FINDCHAIN)
4553 VM_findchainfloat, // #403 entity(.float fld, float match) findchainfloat (DP_QC_FINDCHAINFLOAT)
4554 VM_CL_effect, // #404 void(vector org, string modelname, float startframe, float endframe, float framerate) effect (DP_SV_EFFECT)
4555 VM_CL_te_blood, // #405 void(vector org, vector velocity, float howmany) te_blood (DP_TE_BLOOD)
4556 VM_CL_te_bloodshower, // #406 void(vector mincorner, vector maxcorner, float explosionspeed, float howmany) te_bloodshower (DP_TE_BLOODSHOWER)
4557 VM_CL_te_explosionrgb, // #407 void(vector org, vector color) te_explosionrgb (DP_TE_EXPLOSIONRGB)
4558 VM_CL_te_particlecube, // #408 void(vector mincorner, vector maxcorner, vector vel, float howmany, float color, float gravityflag, float randomveljitter) te_particlecube (DP_TE_PARTICLECUBE)
4559 VM_CL_te_particlerain, // #409 void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlerain (DP_TE_PARTICLERAIN)
4560 VM_CL_te_particlesnow, // #410 void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlesnow (DP_TE_PARTICLESNOW)
4561 VM_CL_te_spark, // #411 void(vector org, vector vel, float howmany) te_spark (DP_TE_SPARK)
4562 VM_CL_te_gunshotquad, // #412 void(vector org) te_gunshotquad (DP_QUADEFFECTS1)
4563 VM_CL_te_spikequad, // #413 void(vector org) te_spikequad (DP_QUADEFFECTS1)
4564 VM_CL_te_superspikequad, // #414 void(vector org) te_superspikequad (DP_QUADEFFECTS1)
4565 VM_CL_te_explosionquad, // #415 void(vector org) te_explosionquad (DP_QUADEFFECTS1)
4566 VM_CL_te_smallflash, // #416 void(vector org) te_smallflash (DP_TE_SMALLFLASH)
4567 VM_CL_te_customflash, // #417 void(vector org, float radius, float lifetime, vector color) te_customflash (DP_TE_CUSTOMFLASH)
4568 VM_CL_te_gunshot, // #418 void(vector org) te_gunshot (DP_TE_STANDARDEFFECTBUILTINS)
4569 VM_CL_te_spike, // #419 void(vector org) te_spike (DP_TE_STANDARDEFFECTBUILTINS)
4570 VM_CL_te_superspike, // #420 void(vector org) te_superspike (DP_TE_STANDARDEFFECTBUILTINS)
4571 VM_CL_te_explosion, // #421 void(vector org) te_explosion (DP_TE_STANDARDEFFECTBUILTINS)
4572 VM_CL_te_tarexplosion, // #422 void(vector org) te_tarexplosion (DP_TE_STANDARDEFFECTBUILTINS)
4573 VM_CL_te_wizspike, // #423 void(vector org) te_wizspike (DP_TE_STANDARDEFFECTBUILTINS)
4574 VM_CL_te_knightspike, // #424 void(vector org) te_knightspike (DP_TE_STANDARDEFFECTBUILTINS)
4575 VM_CL_te_lavasplash, // #425 void(vector org) te_lavasplash (DP_TE_STANDARDEFFECTBUILTINS)
4576 VM_CL_te_teleport, // #426 void(vector org) te_teleport (DP_TE_STANDARDEFFECTBUILTINS)
4577 VM_CL_te_explosion2, // #427 void(vector org, float colorstart, float colorlength) te_explosion2 (DP_TE_STANDARDEFFECTBUILTINS)
4578 VM_CL_te_lightning1, // #428 void(entity own, vector start, vector end) te_lightning1 (DP_TE_STANDARDEFFECTBUILTINS)
4579 VM_CL_te_lightning2, // #429 void(entity own, vector start, vector end) te_lightning2 (DP_TE_STANDARDEFFECTBUILTINS)
4580 VM_CL_te_lightning3, // #430 void(entity own, vector start, vector end) te_lightning3 (DP_TE_STANDARDEFFECTBUILTINS)
4581 VM_CL_te_beam, // #431 void(entity own, vector start, vector end) te_beam (DP_TE_STANDARDEFFECTBUILTINS)
4582 VM_vectorvectors, // #432 void(vector dir) vectorvectors (DP_QC_VECTORVECTORS)
4583 VM_CL_te_plasmaburn, // #433 void(vector org) te_plasmaburn (DP_TE_PLASMABURN)
4584 VM_getsurfacenumpoints, // #434 float(entity e, float s) getsurfacenumpoints (DP_QC_GETSURFACE)
4585 VM_getsurfacepoint, // #435 vector(entity e, float s, float n) getsurfacepoint (DP_QC_GETSURFACE)
4586 VM_getsurfacenormal, // #436 vector(entity e, float s) getsurfacenormal (DP_QC_GETSURFACE)
4587 VM_getsurfacetexture, // #437 string(entity e, float s) getsurfacetexture (DP_QC_GETSURFACE)
4588 VM_getsurfacenearpoint, // #438 float(entity e, vector p) getsurfacenearpoint (DP_QC_GETSURFACE)
4589 VM_getsurfaceclippedpoint, // #439 vector(entity e, float s, vector p) getsurfaceclippedpoint (DP_QC_GETSURFACE)
4590 NULL, // #440 void(entity e, string s) clientcommand (KRIMZON_SV_PARSECLIENTCOMMAND)
4591 VM_tokenize, // #441 float(string s) tokenize (KRIMZON_SV_PARSECLIENTCOMMAND)
4592 VM_argv, // #442 string(float n) argv (KRIMZON_SV_PARSECLIENTCOMMAND)
4593 VM_CL_setattachment, // #443 void(entity e, entity tagentity, string tagname) setattachment (DP_GFX_QUAKE3MODELTAGS)
4594 VM_search_begin, // #444 float(string pattern, float caseinsensitive, float quiet) search_begin (DP_QC_FS_SEARCH)
4595 VM_search_end, // #445 void(float handle) search_end (DP_QC_FS_SEARCH)
4596 VM_search_getsize, // #446 float(float handle) search_getsize (DP_QC_FS_SEARCH)
4597 VM_search_getfilename, // #447 string(float handle, float num) search_getfilename (DP_QC_FS_SEARCH)
4598 VM_cvar_string, // #448 string(string s) cvar_string (DP_QC_CVAR_STRING)
4599 VM_findflags, // #449 entity(entity start, .float fld, float match) findflags (DP_QC_FINDFLAGS)
4600 VM_findchainflags, // #450 entity(.float fld, float match) findchainflags (DP_QC_FINDCHAINFLAGS)
4601 VM_CL_gettagindex, // #451 float(entity ent, string tagname) gettagindex (DP_QC_GETTAGINFO)
4602 VM_CL_gettaginfo, // #452 vector(entity ent, float tagindex) gettaginfo (DP_QC_GETTAGINFO)
4603 NULL, // #453 void(entity clent) dropclient (DP_SV_DROPCLIENT)
4604 NULL, // #454 entity() spawnclient (DP_SV_BOTCLIENT)
4605 NULL, // #455 float(entity clent) clienttype (DP_SV_BOTCLIENT)
4606 NULL, // #456 void(float to, string s) WriteUnterminatedString (DP_SV_WRITEUNTERMINATEDSTRING)
4607 VM_CL_te_flamejet, // #457 void(vector org, vector vel, float howmany) te_flamejet (DP_TE_FLAMEJET)
4609 VM_ftoe, // #459 entity(float num) entitybyindex (DP_QC_EDICT_NUM)
4610 VM_buf_create, // #460 float() buf_create (DP_QC_STRINGBUFFERS)
4611 VM_buf_del, // #461 void(float bufhandle) buf_del (DP_QC_STRINGBUFFERS)
4612 VM_buf_getsize, // #462 float(float bufhandle) buf_getsize (DP_QC_STRINGBUFFERS)
4613 VM_buf_copy, // #463 void(float bufhandle_from, float bufhandle_to) buf_copy (DP_QC_STRINGBUFFERS)
4614 VM_buf_sort, // #464 void(float bufhandle, float sortpower, float backward) buf_sort (DP_QC_STRINGBUFFERS)
4615 VM_buf_implode, // #465 string(float bufhandle, string glue) buf_implode (DP_QC_STRINGBUFFERS)
4616 VM_bufstr_get, // #466 string(float bufhandle, float string_index) bufstr_get (DP_QC_STRINGBUFFERS)
4617 VM_bufstr_set, // #467 void(float bufhandle, float string_index, string str) bufstr_set (DP_QC_STRINGBUFFERS)
4618 VM_bufstr_add, // #468 float(float bufhandle, string str, float order) bufstr_add (DP_QC_STRINGBUFFERS)
4619 VM_bufstr_free, // #469 void(float bufhandle, float string_index) bufstr_free (DP_QC_STRINGBUFFERS)
4620 NULL, // #470 void(float index, float type, .void field) SV_AddStat (EXT_CSQC)
4621 VM_asin, // #471 float(float s) VM_asin (DP_QC_ASINACOSATANATAN2TAN)
4622 VM_acos, // #472 float(float c) VM_acos (DP_QC_ASINACOSATANATAN2TAN)
4623 VM_atan, // #473 float(float t) VM_atan (DP_QC_ASINACOSATANATAN2TAN)
4624 VM_atan2, // #474 float(float c, float s) VM_atan2 (DP_QC_ASINACOSATANATAN2TAN)
4625 VM_tan, // #475 float(float a) VM_tan (DP_QC_ASINACOSATANATAN2TAN)
4626 VM_strlennocol, // #476 float(string s) : DRESK - String Length (not counting color codes) (DP_QC_STRINGCOLORFUNCTIONS)
4627 VM_strdecolorize, // #477 string(string s) : DRESK - Decolorized String (DP_QC_STRINGCOLORFUNCTIONS)
4628 VM_strftime, // #478 string(float uselocaltime, string format, ...) (DP_QC_STRFTIME)
4629 VM_tokenizebyseparator, // #479 float(string s) tokenizebyseparator (DP_QC_TOKENIZEBYSEPARATOR)
4630 VM_strtolower, // #480 string(string s) VM_strtolower (DP_QC_STRING_CASE_FUNCTIONS)
4631 VM_strtoupper, // #481 string(string s) VM_strtoupper (DP_QC_STRING_CASE_FUNCTIONS)
4632 VM_cvar_defstring, // #482 string(string s) cvar_defstring (DP_QC_CVAR_DEFSTRING)
4633 VM_CL_pointsound, // #483 void(vector origin, string sample, float volume, float attenuation) pointsound (DP_SV_POINTSOUND)
4634 VM_strreplace, // #484 string(string search, string replace, string subject) strreplace (DP_QC_STRREPLACE)
4635 VM_strireplace, // #485 string(string search, string replace, string subject) strireplace (DP_QC_STRREPLACE)
4636 VM_getsurfacepointattribute,// #486 vector(entity e, float s, float n, float a) getsurfacepointattribute
4637 VM_gecko_create, // #487 float gecko_create( string name )
4638 VM_gecko_destroy, // #488 void gecko_destroy( string name )
4639 VM_gecko_navigate, // #489 void gecko_navigate( string name, string URI )
4640 VM_gecko_keyevent, // #490 float gecko_keyevent( string name, float key, float eventtype )
4641 VM_gecko_movemouse, // #491 void gecko_mousemove( string name, float x, float y )
4642 VM_gecko_resize, // #492 void gecko_resize( string name, float w, float h )
4643 VM_gecko_get_texture_extent, // #493 vector gecko_get_texture_extent( string name )
4644 VM_crc16, // #494 float(float caseinsensitive, string s, ...) crc16 = #494 (DP_QC_CRC16)
4645 VM_cvar_type, // #495 float(string name) cvar_type = #495; (DP_QC_CVAR_TYPE)
4646 VM_numentityfields, // #496 float() numentityfields = #496; (QP_QC_ENTITYDATA)
4647 VM_entityfieldname, // #497 string(float fieldnum) entityfieldname = #497; (DP_QC_ENTITYDATA)
4648 VM_entityfieldtype, // #498 float(float fieldnum) entityfieldtype = #498; (DP_QC_ENTITYDATA)
4649 VM_getentityfieldstring, // #499 string(float fieldnum, entity ent) getentityfieldstring = #499; (DP_QC_ENTITYDATA)
4650 VM_putentityfieldstring, // #500 float(float fieldnum, entity ent, string s) putentityfieldstring = #500; (DP_QC_ENTITYDATA)
4651 VM_CL_ReadPicture, // #501 string() ReadPicture = #501;
4652 VM_CL_boxparticles, // #502 void(float effectnum, entity own, vector origin_from, vector origin_to, vector dir_from, vector dir_to, float count) boxparticles (DP_CSQC_BOXPARTICLES)
4653 VM_whichpack, // #503 string(string) whichpack = #503;
4654 VM_CL_GetEntity, // #504 float(float entitynum, float fldnum) getentity = #504; vector(float entitynum, float fldnum) getentityvec = #504;
4660 VM_uri_escape, // #510 string(string in) uri_escape = #510;
4661 VM_uri_unescape, // #511 string(string in) uri_unescape = #511;
4662 VM_etof, // #512 float(entity ent) num_for_edict = #512 (DP_QC_NUM_FOR_EDICT)
4663 VM_uri_get, // #513 float(string uri, float id, [string post_contenttype, string post_delim, [float buf]]) uri_get = #513; (DP_QC_URI_GET, DP_QC_URI_POST)
4664 VM_tokenize_console, // #514 float(string str) tokenize_console = #514; (DP_QC_TOKENIZE_CONSOLE)
4665 VM_argv_start_index, // #515 float(float idx) argv_start_index = #515; (DP_QC_TOKENIZE_CONSOLE)
4666 VM_argv_end_index, // #516 float(float idx) argv_end_index = #516; (DP_QC_TOKENIZE_CONSOLE)
4667 VM_buf_cvarlist, // #517 void(float buf, string prefix, string antiprefix) buf_cvarlist = #517; (DP_QC_STRINGBUFFERS_CVARLIST)
4668 VM_cvar_description, // #518 float(string name) cvar_description = #518; (DP_QC_CVAR_DESCRIPTION)
4669 VM_gettime, // #519 float(float timer) gettime = #519; (DP_QC_GETTIME)
4670 VM_keynumtostring, // #520 string keynumtostring(float keynum)
4671 VM_findkeysforcommand, // #521 string findkeysforcommand(string command[, float bindmap])
4672 VM_CL_InitParticleSpawner, // #522 void(float max_themes) initparticlespawner (DP_CSQC_SPAWNPARTICLE)
4673 VM_CL_ResetParticle, // #523 void() resetparticle (DP_CSQC_SPAWNPARTICLE)
4674 VM_CL_ParticleTheme, // #524 void(float theme) particletheme (DP_CSQC_SPAWNPARTICLE)
4675 VM_CL_ParticleThemeSave, // #525 void() particlethemesave, void(float theme) particlethemeupdate (DP_CSQC_SPAWNPARTICLE)
4676 VM_CL_ParticleThemeFree, // #526 void() particlethemefree (DP_CSQC_SPAWNPARTICLE)
4677 VM_CL_SpawnParticle, // #527 float(vector org, vector vel, [float theme]) particle (DP_CSQC_SPAWNPARTICLE)
4678 VM_CL_SpawnParticleDelayed, // #528 float(vector org, vector vel, float delay, float collisiondelay, [float theme]) delayedparticle (DP_CSQC_SPAWNPARTICLE)
4679 VM_loadfromdata, // #529
4680 VM_loadfromfile, // #530
4681 VM_CL_setpause, // #531 float(float ispaused) setpause = #531 (DP_CSQC_SETPAUSE)
4683 VM_getsoundtime, // #533 float(entity e, float channel) getsoundtime = #533; (DP_SND_GETSOUNDTIME)
4684 VM_soundlength, // #534 float(string sample) soundlength = #534; (DP_SND_GETSOUNDTIME)
4690 VM_physics_enable, // #540 void(entity e, float physics_enabled) physics_enable = #540; (DP_PHYSICS_ODE)
4691 VM_physics_addforce, // #541 void(entity e, vector force, vector relative_ofs) physics_addforce = #541; (DP_PHYSICS_ODE)
4692 VM_physics_addtorque, // #542 void(entity e, vector torque) physics_addtorque = #542; (DP_PHYSICS_ODE)
4755 VM_callfunction, // #605
4756 VM_writetofile, // #606
4757 VM_isfunction, // #607
4760 VM_findkeysforcommand, // #610 string findkeysforcommand(string command[, float bindmap])
4763 VM_parseentitydata, // #613
4774 VM_CL_getextresponse, // #624 string getextresponse(void)
4777 VM_sprintf, // #627 string sprintf(string format, ...)
4778 VM_getsurfacenumtriangles, // #628 float(entity e, float s) getsurfacenumpoints (DP_QC_GETSURFACETRIANGLE)
4779 VM_getsurfacetriangle, // #629 vector(entity e, float s, float n) getsurfacepoint (DP_QC_GETSURFACETRIANGLE)
4780 VM_setkeybind, // #630 float(float key, string bind[, float bindmap]) setkeybind
4781 VM_getbindmaps, // #631 vector(void) getbindmap
4782 VM_setbindmaps, // #632 float(vector bm) setbindmap
4788 VM_CL_RotateMoves, // #638
4789 VM_digest_hex, // #639
4793 const int vm_cl_numbuiltins = sizeof(vm_cl_builtins) / sizeof(prvm_builtin_t);
4795 void VM_Polygons_Reset(void)
4797 vmpolygons_t* polys = vmpolygons + PRVM_GetProgNr();
4799 // TODO: replace vm_polygons stuff with a more general debugging polygon system, and make vm_polygons functions use that system
4800 if(polys->initialized)
4802 Mem_FreePool(&polys->pool);
4803 polys->initialized = false;
4807 void VM_CL_Cmd_Init(void)
4810 VM_Polygons_Reset();
4813 void VM_CL_Cmd_Reset(void)
4815 World_End(&cl.world);
4817 VM_Polygons_Reset();