1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3 Copyright (C) 2016      Spike
4 
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 
14 See the GNU General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19 
20 */
21 
22 /*
23 The aim of this particle system is to have as much as possible configurable.
24 Some parts still fail here, and are marked FIXME
25 Effects are flushed on new maps.
26 The engine has a few builtins.
27 */
28 
29 #include "quakedef.h"
30 
31 cvar_t	r_fteparticles = {"r_fteparticles","1", CVAR_ARCHIVE};
32 
33 #ifdef PSET_SCRIPT
34 #define USE_DECALS
35 #define Con_Printf Con_SafePrintf
36 
37 #define frandom() (rand()*(1.0f/(float)RAND_MAX))
38 #define crandom() (rand()*(2.0f/(float)RAND_MAX)-1.0f)
39 #define hrandom() (rand()*(1.0f/(float)RAND_MAX)-0.5f)
40 #define particle_s fparticle_s
41 #define particle_t fparticle_t
42 typedef vec_t	vec2_t[2];
43 #define FloatInterpolate(a, bness, b, c) ((c) = (a) + (b - a)*bness)
44 #define Vector2Copy(a,b) do{(b)[0]=(a)[0];(b)[1]=(a)[1];}while(0)
45 #define Vector2Set(r,x,y) do{(r)[0] = x; (r)[1] = y;}while(0)
46 #define VectorClear(a)			((a)[0]=(a)[1]=(a)[2]=0)
47 #define VectorInterpolate(a, bness, b, c) FloatInterpolate((a)[0], bness, (b)[0], (c)[0]),FloatInterpolate((a)[1], bness, (b)[1], (c)[1]),FloatInterpolate((a)[2], bness, (b)[2], (c)[2])
48 #define VectorSet(r,x,y,z) do{(r)[0] = x; (r)[1] = y;(r)[2] = z;}while(0)
49 #define Vector4Clear(a)			((a)[0]=(a)[1]=(a)[2]=(a)[3]=0)
50 #define Vector4Copy(a,b) do{(b)[0]=(a)[0];(b)[1]=(a)[1];(b)[2]=(a)[2];(b)[3]=(a)[3];}while(0)
51 #define Vector4Scale(in,scale,out)		((out)[0]=(in)[0]*scale,(out)[1]=(in)[1]*scale,(out)[2]=(in)[2]*scale,(out)[3]=(in)[3]*scale)
52 #define FloatToColor(a,b) do { \
53 	(b)=(byte)(CLAMP(0.0f, (a), 1.0f) * 255.0f); \
54 } while(0)
55 #define Vector3ToColor(a,b) do { \
56 	(b)[0]=(byte)(CLAMP(0.0f, (a)[0], 1.0f) * 255.0f); \
57 	(b)[1]=(byte)(CLAMP(0.0f, (a)[1], 1.0f) * 255.0f); \
58 	(b)[2]=(byte)(CLAMP(0.0f, (a)[2], 1.0f) * 255.0f); \
59 } while(0)
60 #define Vector4ToColor(a,b) do { \
61 	(b)[0]=(byte)(CLAMP(0.0f, (a)[0], 1.0f) * 255.0f); \
62 	(b)[1]=(byte)(CLAMP(0.0f, (a)[1], 1.0f) * 255.0f); \
63 	(b)[2]=(byte)(CLAMP(0.0f, (a)[2], 1.0f) * 255.0f); \
64 	(b)[3]=(byte)(CLAMP(0.0f, (a)[3], 1.0f) * 255.0f); \
65 } while(0)
VectorNormalize2(const vec3_t v,vec3_t out)66 vec_t VectorNormalize2 (const vec3_t v, vec3_t out)
67 {
68 	float	length, ilength;
69 
70 	length = v[0]*v[0] + v[1]*v[1] + v[2]*v[2];
71 
72 	if (length)
73 	{
74 		length = sqrt (length);		// FIXME
75 		ilength = 1/length;
76 		out[0] = v[0]*ilength;
77 		out[1] = v[1]*ilength;
78 		out[2] = v[2]*ilength;
79 	}
80 	else
81 	{
82 		VectorClear (out);
83 	}
84 
85 	return length;
86 }
VectorVectors(const vec3_t forward,vec3_t right,vec3_t up)87 void VectorVectors(const vec3_t forward, vec3_t right, vec3_t up)
88 {
89 	if (!forward[0] && !forward[1])
90 	{
91 		if (forward[2])
92 			right[1] = -1;
93 		else
94 			right[1] = 0;
95 		right[0] = right[2] = 0;
96 	}
97 	else
98 	{
99 		right[0] = forward[1];
100 		right[1] = -forward[0];
101 		right[2] = 0;
102 		VectorNormalize(right);
103 	}
104 	CrossProduct(right, forward, up);
105 }
106 typedef enum { BM_BLEND/*SRC_ALPHA ONE_MINUS_SRC_ALPHA*/, BM_BLENDCOLOUR/*SRC_COLOR ONE_MINUS_SRC_COLOR*/, BM_ADDA/*SRC_ALPHA ONE*/, BM_ADDC/*GL_SRC_COLOR GL_ONE*/, BM_SUBTRACT/*SRC_ALPHA ONE_MINUS_SRC_COLOR*/, BM_INVMODA/*ZERO ONE_MINUS_SRC_ALPHA*/, BM_INVMODC/*ZERO ONE_MINUS_SRC_COLOR*/, BM_PREMUL/*ONE ONE_MINUS_SRC_ALPHA*/} blendmode_t;
107 typedef struct trailstate_s {
108 	struct trailstate_s **key;  // key to check if ts has been overwriten
109 	struct trailstate_s *assoc; // assoc linked trail
110 	struct beamseg_s *lastbeam; // last beam pointer (flagged with BS_LASTSEG)
111 	union {
112 		float lastdist;			// last distance used with particle effect
113 		float statetime;		// time to emit effect again (used by spawntime field)
114 	} state1;
115 	union {
116 		float laststop;			// last stopping point for particle effect
117 		float emittime;			// used by r_effect emitters
118 	} state2;
119 } trailstate_t;
120 #define CON_WARNING "Warning: "
121 entity_t	*CL_EntityNum (int num);
122 #define BEF_LINES 1
123 
124 #define Z_Malloc malloc
125 #define Z_Free free
126 #define Z_Realloc realloc
127 
128 extern int PClassic_PointFile(int c, vec3_t point);
129 
130 #define PART_VALID(part) ((part) >= 0 && (part) < numparticletypes)
131 
132 static int pe_default = P_INVALID;
133 static int pe_size2 = P_INVALID;
134 static int pe_size3 = P_INVALID;
135 static int pe_defaulttrail = P_INVALID;
136 
137 #define SINTABLE_ENTRIES 128
138 static float psintable[SINTABLE_ENTRIES];
139 static float pcostable[SINTABLE_ENTRIES];
140 
141 int r_trace_line_cache_counter;
142 
143 int PScript_RunParticleEffectState (vec3_t org, vec3_t dir, float count, int typenum, trailstate_t **tsk);
144 int PScript_ParticleTrail (vec3_t startpos, vec3_t end, int type, float timeinterval, int dlkey, vec3_t axis[3], trailstate_t **tsk);
145 static qboolean P_LoadParticleSet(char *name, qboolean implicit, qboolean showwarning);
146 static void R_Particles_KillAllEffects(void);
147 
buildsintable(void)148 static void buildsintable(void)
149 {
150 	int i;
151 	for (i = 0; i < SINTABLE_ENTRIES; i++)
152 	{
153 		psintable[i] = sin((i*M_PI)/(SINTABLE_ENTRIES/2));
154 		pcostable[i] = cos((i*M_PI)/(SINTABLE_ENTRIES/2));
155 	}
156 }
157 #define sin(x) (psintable[(size_t)(int)((x)*((SINTABLE_ENTRIES/2)/M_PI)) % SINTABLE_ENTRIES])
158 #define cos(x) (pcostable[(size_t)(int)((x)*((SINTABLE_ENTRIES/2)/M_PI)) % SINTABLE_ENTRIES])
159 
160 typedef struct particle_s
161 {
162 	struct particle_s	*next;
163 	float		die;
164 
165 // driver-usable fields
166 	vec3_t		org;
167 	vec4_t		rgba;
168 	float		scale;
169 	float		s1, t1, s2, t2;
170 
171 	vec3_t		oldorg;	//to throttle traces
172 	vec3_t		vel;	//renderer uses for sparks
173 	float		angle;
174 	union {
175 		float nextemit;
176 		trailstate_t *trailstate;
177 	} state;
178 // drivers never touch the following fields
179 	float		rotationspeed;
180 } particle_t;
181 
182 typedef struct clippeddecal_s
183 {
184 	struct clippeddecal_s	*next;
185 	float		die;
186 
187 	int entity;		//>0 is a lerpentity, <0 is a csqc ent. 0 is world. woot.
188 	qmodel_t *model;	//just for paranoia
189 
190 	vec3_t		vertex[3];
191 	vec2_t		texcoords[3];
192 	float		valpha[3];
193 
194 	vec4_t		rgba;
195 } clippeddecal_t;
196 
197 #define BS_LASTSEG 0x1 // no draw to next, no delete
198 #define BS_DEAD    0x2 // segment is dead
199 #define BS_NODRAW  0x4 // only used for lerp switching
200 
201 typedef struct beamseg_s
202 {
203 	struct beamseg_s *next;  // next in beamseg list
204 
205 	particle_t *p;
206 	int    flags;            // flags for beamseg
207 	vec3_t dir;
208 
209 	float texture_s;
210 } beamseg_t;
211 
212 typedef struct skytris_s {
213 	struct skytris_s *next;
214 	vec3_t org;
215 	vec3_t x;
216 	vec3_t y;
217 	float area;
218 	double nexttime;
219 	int ptype;
220 	struct msurface_s *face;
221 } skytris_t;
222 
223 typedef struct skytriblock_s
224 {
225 	struct skytriblock_s *next;
226 	unsigned int count;
227 	skytris_t tris[1024];
228 } skytriblock_t;
229 
230 //this is the required render state for each particle
231 //dynamic per-particle stuff isn't important. only static state.
232 typedef struct {
233 	enum {PT_NORMAL, PT_SPARK, PT_SPARKFAN, PT_TEXTUREDSPARK, PT_BEAM, PT_CDECAL, PT_UDECAL, PT_INVISIBLE} type;
234 
235 	blendmode_t blendmode;
236 	gltexture_t *texture;
237 	qboolean	nearest;
238 
239 	float scalefactor;
240 	float invscalefactor;
241 	float stretch;
242 	float minstretch;	//limits the particle's length to a multiple of its width.
243 	int premul;	//0: direct rgba. 1: rgb*a,a (blend). 2: rgb*a,0 (add).
244 } plooks_t;
245 
246 //these could be deltas or absolutes depending on ramping mode.
247 typedef struct {
248 	vec3_t rgb;
249 	float alpha;
250 	float scale;
251 	float rotation;
252 } ramp_t;
253 typedef struct {
254 	char name[MAX_QPATH];
255 	float vol;
256 	float atten;
257 	float delay;
258 	float pitch;
259 	float weight;
260 } partsounds_t;
261 // TODO: merge in alpha with rgb to gain benefit of vector opts
262 typedef struct part_type_s {
263 	char name[MAX_QPATH];
264 	char config[MAX_QPATH];
265 	char texname[MAX_QPATH];
266 
267 	int numsounds;
268 	partsounds_t *sounds;
269 
270 	vec3_t rgb;	//initial colour
271 	float alpha;
272 	vec3_t rgbchange;	//colour delta (per second)
273 	float alphachange;
274 	vec3_t rgbrand;		//random rgb colour to start with
275 	float alpharand;
276 	int colorindex;		//get colour from a palette
277 	int colorrand;		//and add up to this amount
278 	float rgbchangetime;//colour stops changing at this time
279 	vec3_t rgbrandsync;	//like rgbrand, but a single random value instead of separate (can mix)
280 	float scale;		//initial scale
281 	float scalerand;	//with up to this much extra
282 	float die, randdie;	//how long it lasts (plus some rand)
283 	float veladd, randomveladd;		//scale the incoming velocity by this much
284 	float orgadd, randomorgadd;		//spawn the particle this far along its velocity direction
285 	float spawnvel, spawnvelvert; //spawn the particle with a velocity based upon its spawn type (generally so it flies outwards)
286 	vec3_t orgbias;		//static 3d world-coord bias
287 	vec3_t velbias;
288 	vec3_t orgwrand;	//3d world-coord randomisation without relation to spawn mode
289 	vec3_t velwrand;	//3d world-coord randomisation without relation to spawn mode
290 	float viewspacefrac;
291 	float flurry;
292 	int surfflagmatch;	//this decal only spawns on these surfaces
293 	int surfflagmask;	//this decal only spawns on these surfaces
294 
295 	float s1, t1, s2, t2;	//texture coords
296 	float texsstride;	//addition for s for each random slot.
297 	int randsmax;	//max times the stride can be added
298 
299 	plooks_t *slooks;	//shared looks, so state switches don't apply between particles so much.
300 	plooks_t looks;		//
301 
302 	float spawntime;	//time limit for trails
303 	float spawnchance;	//if < 0, particles might not spawn so many
304 
305 	float rotationstartmin, rotationstartrand;
306 	float rotationmin, rotationrand;
307 
308 	float scaledelta;
309 	float countextra;
310 	float count;
311 	float countrand;
312 	float countspacing; //for trails.
313 	float countoverflow; //for badly-designed effects, instead of depending on trail state.
314 	float rainfrequency;	//surface emitter multiplier
315 
316 	int assoc;
317 	int cliptype;
318 	int inwater;
319 	float clipcount;
320 	int emit;
321 	float emittime;
322 	float emitrand;
323 	float emitstart;
324 
325 	float areaspread;
326 	float areaspreadvert;
327 
328 	float spawnparam1;
329 	float spawnparam2;
330 /*	float spawnparam3; */
331 
332 	enum {
333 		SM_BOX, //box = even spread within the area
334 		SM_CIRCLE, //circle = around edge of a circle
335 		SM_BALL, //ball = filled sphere
336 		SM_SPIRAL, //spiral = spiral trail
337 		SM_TRACER, //tracer = tracer trail
338 		SM_TELEBOX, //telebox = q1-style telebox
339 		SM_LAVASPLASH, //lavasplash = q1-style lavasplash
340 		SM_UNICIRCLE, //unicircle = uniform circle
341 		SM_FIELD, //field = synced field (brightfield, etc)
342 		SM_DISTBALL, // uneven distributed ball
343 		SM_MESHSURFACE //distributed roughly evenly over the surface of the mesh
344 	} spawnmode;
345 
346 	float gravity;
347 	vec3_t friction;
348 	float clipbounce;
349 	float stainonimpact;
350 
351 	vec3_t dl_rgb;
352 	float dl_radius[2];
353 	float dl_time;
354 	vec4_t dl_decay;
355 	float dl_corona_intensity;
356 	float dl_corona_scale;
357 	vec3_t dl_scales;
358 	//PT_NODLSHADOW
359 	int dl_cubemapnum;
360 
361 	enum {RAMP_NONE, RAMP_DELTA, RAMP_NEAREST, RAMP_LERP} rampmode;
362 	int rampindexes;
363 	ramp_t *ramp;
364 
365 	int loaded;	//0 if not loaded, 1 if automatically loaded, 2 if user loaded
366 	particle_t	*particles;
367 	clippeddecal_t *clippeddecals;
368 	beamseg_t *beams;
369 	struct part_type_s *nexttorun;
370 
371 	unsigned int flags;
372 #define PT_VELOCITY			0x0001	// has velocity modifiers
373 #define PT_FRICTION			0x0002	// has friction modifiers
374 #define PT_CHANGESCOLOUR	0x0004
375 #define PT_CITRACER			0x0008	// Q1-style tracer behavior for colorindex
376 #define PT_INVFRAMETIME		0x0010	// apply inverse frametime to count (causes emits to be per frame)
377 #define PT_AVERAGETRAIL		0x0020	// average trail points from start to end, useful with t_lightning, etc
378 #define PT_NOSTATE			0x0040	// don't use trailstate for this emitter (careful with assoc...)
379 #define PT_NOSPREADFIRST	0x0080	// don't randomize org/vel for first generated particle
380 #define PT_NOSPREADLAST		0x0100	// don't randomize org/vel for last generated particle
381 #define PT_TROVERWATER		0x0200	// don't spawn if underwater
382 #define PT_TRUNDERWATER		0x0400	// don't spawn if overwater
383 #define PT_NODLSHADOW		0x0800	// dlights from this effect don't cast shadows.
384 #define PT_WORLDSPACERAND	0x1000	// effect has orgwrand or velwrand properties
385 	unsigned int fluidmask;
386 
387 	unsigned int state;
388 #define PS_INRUNLIST 0x1 // particle type is currently in execution list
389 } part_type_t;
390 
391 typedef struct pcfg_s
392 {
393 	struct pcfg_s *next;
394 	char name[1];
395 } pcfg_t;
396 static pcfg_t *loadedconfigs;
397 
398 #ifndef TYPESONLY
399 
400 #define crand() (rand()%32767/16383.5f-1)
401 
402 #define MAX_BEAMSEGS			(1<<11)	// default max # of beam segments
403 #define MAX_PARTICLES			(1<<18)	// max # of particles at one time
404 #define MAX_DECALS				(1<<18)	// max # of decal fragments at one time
405 #define MAX_TRAILSTATES			(1<<10)	// default max # of trailstates
406 
407 static particle_t	*free_particles;
408 static particle_t	*particles;	//contains the initial list of alloced particles.
409 static int			r_numparticles;
410 static int			r_particlerecycle;
411 
412 static beamseg_t	*free_beams;
413 static beamseg_t	*beams;
414 static int			r_numbeams;
415 
416 static clippeddecal_t	*free_decals;
417 static clippeddecal_t	*decals;
418 static int			r_numdecals;
419 static int			r_decalrecycle;
420 
421 static trailstate_t *trailstates;
422 static int			ts_cycle; // current cyclic index of trailstates
423 static int			r_numtrailstates;
424 
425 static		qboolean r_plooksdirty;	//a particle effect was changed, reevaluate shared looks.
426 
427 static void FinishParticleType(part_type_t *ptype);
428 
429 static void R_ParticleDesc_Callback(struct cvar_s *var);
430 static cvar_t r_bouncysparks = {"r_bouncysparks", "1"};
431 static cvar_t r_part_rain = {"r_part_rain", "1"};
432 static cvar_t r_decal_noperpendicular = {"r_decal_noperpendicular", "1"};
433 cvar_t r_particledesc = {"r_particledesc", "classic"};
434 static cvar_t r_part_rain_quantity = {"r_part_rain_quantity", "1"};
435 static cvar_t r_particle_tracelimit = {"r_particle_tracelimit", "16777216"};
436 static cvar_t r_part_sparks = {"r_part_sparks", "1"};
437 static cvar_t r_part_sparks_trifan = {"r_part_sparks_trifan", "1"};
438 static cvar_t r_part_sparks_textured = {"r_part_sparks_textured", "1"};
439 static cvar_t r_part_beams = {"r_part_beams", "1"};
440 static cvar_t r_part_contentswitch = {"r_part_contentswitch", "1"};
441 static cvar_t r_part_density = {"r_part_density", "1"};
442 static cvar_t r_part_maxparticles = {"r_part_maxparticles", "65536"};
443 static cvar_t r_part_maxdecals = {"r_part_maxdecals", "8192"};
444 static cvar_t r_lightflicker = {"r_lightflicker", "1"};
445 extern cvar_t r_showtris;
446 extern cvar_t r_particles;
447 
448 static float particletime;
449 
450 typedef struct
451 {
452 	int firstidx;
453 	int firstvert;
454 	int numidx;
455 	int numvert;
456 
457 	gltexture_t *texture;
458 	blendmode_t blendmode;
459 	int beflags;
460 } scenetris_t;
461 
462 #define MAX_INDICES 0xffff
463 #define INITIAL_NUM_VERTICES 100000
464 #define INITIAL_NUM_INDICES 150000
465 
466 static scenetris_t *cl_stris;
467 static unsigned int cl_numstris;
468 static unsigned int cl_maxstris;
469 static basicvertex_t * cl_strisvert[2];
470 static basicvertex_t * cl_curstrisvert;
471 static unsigned int cl_numstrisvert;
472 static unsigned int cl_maxstrisvert[2];
473 static unsigned short *cl_strisidx[2];
474 static unsigned short *cl_curstrisidx;
475 static unsigned int cl_numstrisidx;
476 static unsigned int cl_maxstrisidx[2];
477 
478 /*
479 Q1BSP_RecursiveHullTrace
480 Optimised version of vanilla's SV_RecursiveHullCheck that avoids the excessive pointcontents calls by using the traceline itself to check for contents.
481 call Q1BSP_RecursiveHullCheck for a drop-in replacement of SV_RecursiveHullCheck, if desired.
482 */
483 enum
484 {
485 	rht_solid,
486 	rht_empty,
487 	rht_impact
488 };
489 struct rhtctx_s
490 {
491 	vec3_t start, end;
492 	mclipnode_t	*clipnodes;
493 	mplane_t	*planes;
494 };
Q1BSP_RecursiveHullTrace(struct rhtctx_s * ctx,int num,float p1f,float p2f,vec3_t p1,vec3_t p2,trace_t * trace)495 static int Q1BSP_RecursiveHullTrace (struct rhtctx_s *ctx, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, trace_t *trace)
496 {
497 	mclipnode_t	*node;
498 	mplane_t	*plane;
499 	float		t1, t2;
500 	vec3_t		mid;
501 	int			side;
502 	float		midf;
503 	int rht;
504 
505 reenter:
506 
507 	if (num < 0)
508 	{
509 		/*hit a leaf*/
510 		if (num == CONTENTS_SOLID)
511 		{
512 			if (trace->allsolid)
513 				trace->startsolid = true;
514 			return rht_solid;
515 		}
516 		else
517 		{
518 			trace->allsolid = false;
519 			if (num == CONTENTS_EMPTY)
520 				trace->inopen = true;
521 			else
522 				trace->inwater = true;
523 			return rht_empty;
524 		}
525 	}
526 
527 	/*its a node*/
528 
529 	/*get the node info*/
530 	node = ctx->clipnodes + num;
531 	plane = ctx->planes + node->planenum;
532 
533 	if (plane->type < 3)
534 	{
535 		t1 = p1[plane->type] - plane->dist;
536 		t2 = p2[plane->type] - plane->dist;
537 	}
538 	else
539 	{
540 		t1 = DotProduct (plane->normal, p1) - plane->dist;
541 		t2 = DotProduct (plane->normal, p2) - plane->dist;
542 	}
543 
544 	/*if its completely on one side, resume on that side*/
545 	if (t1 >= 0 && t2 >= 0)
546 	{
547 		num = node->children[0];
548 		goto reenter;
549 	}
550 	if (t1 < 0 && t2 < 0)
551 	{
552 		num = node->children[1];
553 		goto reenter;
554 	}
555 
556 	if (plane->type < 3)
557 	{
558 		t1 = ctx->start[plane->type] - plane->dist;
559 		t2 = ctx->end[plane->type] - plane->dist;
560 	}
561 	else
562 	{
563 		t1 = DotProduct (plane->normal, ctx->start) - plane->dist;
564 		t2 = DotProduct (plane->normal, ctx->end) - plane->dist;
565 	}
566 
567 	side = t1 < 0;
568 
569 	midf = t1 / (t1 - t2);
570 	if (midf < p1f) midf = p1f;
571 	if (midf > p2f) midf = p2f;
572 	VectorInterpolate(ctx->start, midf, ctx->end, mid);
573 
574 	rht = Q1BSP_RecursiveHullTrace(ctx, node->children[side], p1f, midf, p1, mid, trace);
575 	if (rht != rht_empty && !trace->allsolid)
576 		return rht;
577 	rht = Q1BSP_RecursiveHullTrace(ctx, node->children[side^1], midf, p2f, mid, p2, trace);
578 	if (rht != rht_solid)
579 		return rht;
580 
581 	if (side)
582 	{
583 		/*we impacted the back of the node, so flip the plane*/
584 		trace->plane.dist = -plane->dist;
585 		VectorScale(plane->normal, -1, trace->plane.normal);
586 		midf = (t1 + DIST_EPSILON) / (t1 - t2);
587 	}
588 	else
589 	{
590 		/*we impacted the front of the node*/
591 		trace->plane.dist = plane->dist;
592 		VectorCopy(plane->normal, trace->plane.normal);
593 		midf = (t1 - DIST_EPSILON) / (t1 - t2);
594 	}
595 
596 	t1 = DotProduct (trace->plane.normal, ctx->start) - trace->plane.dist;
597 	t2 = DotProduct (trace->plane.normal, ctx->end) - trace->plane.dist;
598 	midf = (t1 - DIST_EPSILON) / (t1 - t2);
599 	if (midf < 0)
600 		midf = 0;
601 	if (midf > 1)
602 		midf = 1;
603 	trace->fraction = midf;
604 	VectorCopy (mid, trace->endpos);
605 	VectorInterpolate(ctx->start, midf, ctx->end, trace->endpos);
606 
607 	return rht_impact;
608 }
Q1BSP_RecursiveHullCheck(hull_t * hull,int num,float p1f,float p2f,vec3_t p1,vec3_t p2,trace_t * trace)609 static qboolean Q1BSP_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, trace_t *trace)
610 {
611 	//this function is basicall meant as a drop-in replacement for fte's SV_RecursiveHullCheck. p1f and p2f must be 0+1 respectively, num must be hull->firstclipnode
612 	struct rhtctx_s ctx;
613 	VectorCopy(p1, ctx.start);
614 	VectorCopy(p2, ctx.end);
615 	ctx.clipnodes = hull->clipnodes;
616 	ctx.planes = hull->planes;
617 	return Q1BSP_RecursiveHullTrace(&ctx, num, p1f, p2f, p1, p2, trace) != rht_impact;
618 }
619 
CL_TraceLine(vec3_t start,vec3_t end,vec3_t impact,vec3_t normal,int * entnum)620 float CL_TraceLine (vec3_t start, vec3_t end, vec3_t impact, vec3_t normal, int *entnum)
621 {	//FIXME: not sure what to do about startsolid.
622 	int i;
623 	trace_t	trace;
624 	float frac = 1;
625 	entity_t *ent;
626 	vec3_t relstart, relend;
627 	VectorCopy (end, impact);
628 	VectorSet(normal, 0, 0, 1);
629 
630 	static int num_trace_line_ents;
631 	static int trace_line_ents[MAX_EDICTS];
632 	static int cache_valid_count = -1;
633 	if (cache_valid_count != r_trace_line_cache_counter)
634 	{
635 		num_trace_line_ents = 0;
636 		for (i = 0; i < cl.num_entities; i++)
637 		{
638 			ent = &cl.entities[i];
639 			if (!ent->model || ent->model->needload || ent->model->type != mod_brush)
640 				continue;
641 			trace_line_ents[num_trace_line_ents++] = i;
642 		}
643 		cache_valid_count = r_trace_line_cache_counter;
644 	}
645 
646 	if (entnum)
647 		*entnum = 0;
648 	for (i = 0; i < num_trace_line_ents; i++)
649 	{
650 		ent = &cl.entities[trace_line_ents[i]];
651 
652 		//FIXME: deal with rotations
653 		VectorSubtract(start, ent->origin, relstart);
654 		VectorSubtract(end, ent->origin, relend);
655 
656 		memset (&trace, 0, sizeof(trace));
657 		trace.fraction = 1;
658 		Q1BSP_RecursiveHullCheck(&ent->model->hulls[0], ent->model->hulls[0].firstclipnode, 0, 1, relstart, relend, &trace);
659 
660 		if (frac > trace.fraction)
661 		{
662 			frac = trace.fraction;
663 
664 			//FIXME: deal with rotations.
665 			VectorAdd(trace.endpos, ent->origin, impact);
666 			VectorCopy (trace.plane.normal, normal);
667 
668 			if (entnum)
669 				*entnum = i;
670 			if (frac <= 0)
671 				break;
672 		}
673 	}
674 	return frac;
675 }
676 
677 //these are not the actual values, but they'll do
678 #define FTECONTENTS_EMPTY		0
679 #define FTECONTENTS_SOLID		1
680 #define FTECONTENTS_WATER		2
681 #define FTECONTENTS_SLIME		4
682 #define FTECONTENTS_LAVA		8
683 #define	FTECONTENTS_SKY			16
684 #define	FTECONTENTS_FLUID		(FTECONTENTS_WATER|FTECONTENTS_SLIME|FTECONTENTS_LAVA|FTECONTENTS_SKY)
685 #define	FTECONTENTS_PLAYERCLIP	0
686 
687 int SV_HullPointContents (hull_t *hull, int num, vec3_t p);
CL_PointContentsMask(vec3_t p)688 static unsigned int CL_PointContentsMask (vec3_t p)
689 {
690 	static const unsigned int cont_qtof[] =
691 	{
692 		0,	//invalid
693 		FTECONTENTS_EMPTY,
694 		FTECONTENTS_SOLID,
695 		FTECONTENTS_WATER,
696 		FTECONTENTS_SLIME,
697 		FTECONTENTS_LAVA,
698 		FTECONTENTS_SKY
699 	};
700 
701 	unsigned int		cont;
702 
703 	cont = -SV_HullPointContents (&cl.worldmodel->hulls[0], 0, p);
704 	if (cont < sizeof(cont_qtof)/sizeof(cont_qtof[0]))
705 		return cont_qtof[cont];
706 	else
707 		return cont_qtof[-(CONTENTS_WATER)];	//assume water
708 }
709 
710 static int numparticletypes;
711 static part_type_t *part_type;
712 static part_type_t *part_run_list;
713 
714 static struct {
715 	char *oldn;
716 	char *newn;
717 } legacynames[] =
718 {
719 	{"t_rocket",	"TR_ROCKET"},
720 	{"t_grenade",	"TR_GRENADE"},
721 	{"t_gib",		"TR_BLOOD"},
722 
723 	{"te_plasma",	"TE_TEI_PLASMAHIT"},
724 	{"te_smoke",	"TE_TEI_SMOKE"},
725 
726 	{NULL}
727 };
728 
729 static struct partalias_s
730 {
731 	struct partalias_s *next;
732 	const char *from;
733 	const char *to;
734 } *partaliaslist;
735 typedef struct associatedeffect_s
736 {
737 	struct associatedeffect_s *next;
738 	char mname[MAX_QPATH];
739 	char pname[MAX_QPATH];
740 	unsigned int flags;
741 	enum
742 	{
743 		AE_TRAIL,
744 		AE_EMIT,
745 	} type;
746 } associatedeffect_t;
747 static associatedeffect_t *associatedeffect;
PScript_AssociateEffect_f(void)748 static void PScript_AssociateEffect_f(void)
749 {
750 	const char *modelname = Cmd_Argv(1);
751 	const char *effectname = Cmd_Argv(2);
752 	unsigned int flags = 0;
753 	int type;
754 	associatedeffect_t *ae;
755 	int i;
756 
757 	if (!strcmp(Cmd_Argv(0), "r_trail"))
758 		type = AE_TRAIL;
759 	else
760 	{
761 		type = AE_EMIT;
762 		for (i = 3; i < Cmd_Argc(); i++)
763 		{
764 			const char *fn = Cmd_Argv(i);
765 			if (!strcmp(fn, "replace") || !strcmp(fn, "1"))
766 				flags |= MOD_EMITREPLACE;
767 			else if (!strcmp(fn, "forwards") || !strcmp(fn, "forward"))
768 				flags |= MOD_EMITFORWARDS;
769 			else if (!strcmp(fn, "0"))
770 				;	//1 or 0 are legacy, meaning replace or not
771 			else
772 				Con_DPrintf("%s %s: unknown flag %s\n", Cmd_Argv(0), modelname, fn);
773 		}
774 	}
775 
776 	if (
777 		strstr(modelname, "player") ||
778 		strstr(modelname, "eyes") ||
779 		strstr(modelname, "flag") ||
780 		strstr(modelname, "tf_stan") ||
781 		strstr(modelname, ".bsp") ||
782 		strstr(modelname, "turr"))
783 	{
784 		//there is a very real possibility of attaching 'large' effects to models so that they become more visible (eg: a stream of particles passing through walls showing you the entity that they're eminating from)
785 		Con_Printf("Sorry: Not allowed to attach effects to model \"%s\"\n", modelname);
786 		return;
787 	}
788 
789 	if (strlen (modelname) >= MAX_QPATH || strlen(effectname) >= MAX_QPATH)
790 		return;
791 
792 	/*replace the old one if it exists*/
793 	for(ae = associatedeffect; ae; ae = ae->next)
794 	{
795 		if (!strcmp(ae->mname, modelname))
796 			if ((ae->type==AE_TRAIL) == (type==AE_TRAIL))
797 				break;
798 	}
799 	if (!ae)
800 	{
801 		ae = Z_Malloc(sizeof(*ae));
802 		strcpy(ae->mname, modelname);
803 		ae->next = associatedeffect;
804 		associatedeffect = ae;
805 	}
806 	strcpy(ae->pname, effectname);
807 	ae->type = type;
808 	ae->flags = flags;
809 
810 	r_plooksdirty = true;
811 }
P_PartRedirect_f(void)812 static void P_PartRedirect_f(void)
813 {
814 	struct partalias_s **link, *l;
815 	const char *from = Cmd_Argv(1);
816 	const char *to = Cmd_Argv(2);
817 
818 	//user wants to list all
819 	if (!*from)
820 	{
821 		for (l = partaliaslist; l; l = l->next)
822 		{
823 			Con_Printf("%s -> %s\n", l->from, l->to);
824 		}
825 		return;
826 	}
827 
828 	//unlink the current value
829 	for (link = &partaliaslist; (l=*link); link = &(*link)->next)
830 	{
831 		if (!q_strcasecmp(l->from, from))
832 		{
833 			//they didn't specify a to, so just print out this one effect without removing it.
834 			if (Cmd_Argc() == 2)
835 			{
836 				Con_Printf("particle %s is currently remapped to %s\n", l->from, l->to);
837 				return;
838 			}
839 			*link = l->next;
840 			Z_Free(l);
841 			break;
842 		}
843 	}
844 
845 	//create a new entry.
846 	if (*to && q_strcasecmp(from, to))
847 	{
848 		l = Z_Malloc(sizeof(*l) + strlen(from) + strlen(to) + 2);
849 		l->from = (char*)(l + 1);
850 		strcpy((char*)l->from, from);
851 		l->to = l->from + strlen(l->from)+1;
852 		strcpy((char*)l->to, to);
853 		l->next = partaliaslist;
854 		partaliaslist = l;
855 	}
856 
857 	r_plooksdirty = true;
858 }
PScript_UpdateModelEffects(qmodel_t * mod)859 void PScript_UpdateModelEffects(qmodel_t *mod)
860 {
861 	associatedeffect_t *ae;
862 	mod->emiteffect = P_INVALID;
863 	mod->traileffect = P_INVALID;
864 	for(ae = associatedeffect; ae; ae = ae->next)
865 	{
866 		if (!strcmp(ae->mname, mod->name))
867 		{
868 			switch(ae->type)
869 			{
870 			case AE_TRAIL:
871 				mod->traileffect = PScript_FindParticleType(ae->pname);
872 				break;
873 			case AE_EMIT:
874 				mod->emiteffect = PScript_FindParticleType(ae->pname);
875 				mod->flags &= ~(MOD_EMITREPLACE|MOD_EMITFORWARDS);
876 				mod->flags |= ae->flags;
877 				break;
878 			}
879 		}
880 	}
881 }
882 
P_GetParticleType(const char * config,const char * name)883 static part_type_t *P_GetParticleType(const char *config, const char *name)
884 {
885 	int i;
886 	part_type_t *ptype;
887 	part_type_t *oldlist = part_type;
888 	char cfgbuf[MAX_QPATH];
889 	char *dot = strchr(name, '.');
890 	if (dot && (dot - name) < MAX_QPATH-1)
891 	{
892 		config = cfgbuf;
893 		memcpy(cfgbuf, name, dot - name);
894 		cfgbuf[dot - name] = 0;
895 		name = dot+1;
896 	}
897 
898 	for (i = 0; legacynames[i].oldn; i++)
899 	{
900 		if (!strcmp(name, legacynames[i].oldn))
901 		{
902 			name = legacynames[i].newn;
903 			break;
904 		}
905 	}
906 	for (i = 0; i < numparticletypes; i++)
907 	{
908 		ptype = &part_type[i];
909 		if (!q_strcasecmp(ptype->name, name))
910 			if (!q_strcasecmp(ptype->config, config))	//must be an exact match.
911 				return ptype;
912 	}
913 	part_type = Z_Realloc(part_type, sizeof(part_type_t)*(numparticletypes+1));
914 	ptype = &part_type[numparticletypes++];
915 	memset(ptype, 0, sizeof(*ptype));
916 	q_strlcpy(ptype->name, name, sizeof(ptype->name));
917 	q_strlcpy(ptype->config, config, sizeof(ptype->config));
918 	ptype->assoc = P_INVALID;
919 	ptype->inwater = P_INVALID;
920 	ptype->cliptype = P_INVALID;
921 	ptype->emit = P_INVALID;
922 
923 	if (oldlist)
924 	{
925 		if (part_run_list)
926 			part_run_list = (part_type_t*)((char*)part_run_list - (char*)oldlist + (char*)part_type);
927 
928 		for (i = 0; i < numparticletypes; i++)
929 			if (part_type[i].nexttorun)
930 				part_type[i].nexttorun = (part_type_t*)((char*)part_type[i].nexttorun - (char*)oldlist + (char*)part_type);
931 	}
932 
933 	ptype->loaded = 0;
934 	ptype->ramp = NULL;
935 	ptype->particles = NULL;
936 	ptype->beams = NULL;
937 
938 	r_plooksdirty = true;
939 	return ptype;
940 }
941 
942 //unconditionally allocates a particle object. this allows out-of-order allocations.
P_AllocateParticleType(const char * config,const char * name)943 static int P_AllocateParticleType(const char *config, const char *name)	//guarentees that the particle type exists, returning it's index.
944 {
945 	part_type_t *pt = P_GetParticleType(config, name);
946 	return pt - part_type;
947 }
948 
PScript_RetintEffect(part_type_t * to,part_type_t * from,const char * colourcodes)949 static void PScript_RetintEffect(part_type_t *to, part_type_t *from, const char *colourcodes)
950 {
951 	char name[sizeof(to->name)];
952 	char config[sizeof(to->config)];
953 
954 	q_strlcpy(name, to->name, sizeof(to->name));
955 	q_strlcpy(config, to->config, sizeof(to->config));
956 
957 	//'to' was already purged, so we don't need to care about that.
958 	memcpy(to, from, sizeof(*to));
959 
960 	q_strlcpy(to->name, name, sizeof(to->name));
961 	q_strlcpy(to->config, config, sizeof(to->config));
962 
963 	//make sure 'to' has its own copy of any lists, so that we don't have issues when freeing this memory again.
964 	if (to->sounds)
965 	{
966 		to->sounds = Z_Malloc(to->numsounds * sizeof(*to->sounds));
967 		memcpy(to->sounds, from->sounds, to->numsounds * sizeof(*to->sounds));
968 	}
969 	if (to->ramp)
970 	{
971 		to->ramp = Z_Malloc(to->rampindexes * sizeof(*to->ramp));
972 		memcpy(to->ramp, from->ramp, to->rampindexes * sizeof(*to->ramp));
973 	}
974 
975 	//'from' might still have some links so we need to clear those out.
976 	to->nexttorun = NULL;
977 	to->particles = NULL;
978 	to->clippeddecals = NULL;
979 	to->beams = NULL;
980 	to->slooks = &to->looks;
981 	r_plooksdirty = true;
982 
983 	to->colorindex = strtoul(colourcodes, (char**)&colourcodes, 10);
984 	if (*colourcodes == '_')
985 		colourcodes++;
986 	to->colorrand = strtoul(colourcodes, (char**)&colourcodes, 10);
987 }
988 
989 //public interface. get without creating.
PScript_FindParticleType(const char * fullname)990 int PScript_FindParticleType(const char *fullname)
991 {
992 	int i;
993 	part_type_t *ptype = NULL;
994 	char cfg[MAX_QPATH];
995 	char *dot;
996 	const char *name = fullname;
997 
998 	//check particle aliases, mostly for tex_sky1 -> weather.te_rain for example, or whatever
999 	struct partalias_s *l;
1000 	int recurselimit = 5;
1001 	for (l = partaliaslist; l; )
1002 	{
1003 		if (!q_strcasecmp(l->from, name))
1004 		{
1005 			name = l->to;
1006 
1007 			if (recurselimit --> 0)
1008 				l = partaliaslist;
1009 			else
1010 				return P_INVALID;
1011 		}
1012 		else
1013 			l = l->next;
1014 	}
1015 
1016 	dot = strchr(name, '.');
1017 	if (dot && (dot - name) < MAX_QPATH-1)
1018 	{
1019 		memcpy(cfg, name, dot - name);
1020 		cfg[dot-name] = 0;
1021 		name = dot+1;
1022 	}
1023 	else
1024 		*cfg = 0;
1025 
1026 	for (i = 0; legacynames[i].oldn; i++)
1027 	{
1028 		if (!strcmp(name, legacynames[i].oldn))
1029 		{
1030 			name = legacynames[i].newn;
1031 			break;
1032 		}
1033 	}
1034 
1035 	if (*cfg)
1036 	{	//favour the namespace if one is specified
1037 		for (i = 0; i < numparticletypes; i++)
1038 		{
1039 			if (!q_strcasecmp(part_type[i].name, name))
1040 			{
1041 				if (!q_strcasecmp(part_type[i].config, cfg))
1042 				{
1043 					ptype = &part_type[i];
1044 					break;
1045 				}
1046 			}
1047 		}
1048 	}
1049 	else
1050 	{
1051 		//but be prepared to load it from any namespace if its not got a namespace specified.
1052 		for (i = 0; i < numparticletypes; i++)
1053 		{
1054 			if (!q_strcasecmp(part_type[i].name, name))
1055 			{
1056 				ptype = &part_type[i];
1057 				if (ptype->loaded)	//(mostly) ignore ones that are not currently loaded
1058 					break;
1059 			}
1060 		}
1061 	}
1062 	if (!ptype || !ptype->loaded)
1063 	{
1064 		if (!q_strncasecmp(name, "te_explosion2_", 14))
1065 		{
1066 			int from = PScript_FindParticleType(va("%s.te_explosion2", cfg));
1067 			if (from != P_INVALID)
1068 			{
1069 				int to = P_AllocateParticleType(cfg, name);
1070 				PScript_RetintEffect(&part_type[to], &part_type[from], name+14);
1071 				return to;
1072 			}
1073 		}
1074 		if (*cfg)
1075 			if (P_LoadParticleSet(cfg, true, true))
1076 				return PScript_FindParticleType(fullname);
1077 
1078 		return P_INVALID;
1079 	}
1080 	return i;
1081 }
1082 
CheckAssosiation(const char * config,const char * name,int from)1083 static int CheckAssosiation(const char *config, const char *name, int from)
1084 {
1085 	int to, orig;
1086 
1087 	orig = to = P_AllocateParticleType(config, name);
1088 
1089 	while(to != P_INVALID)
1090 	{
1091 		if (to == from)
1092 		{
1093 			Con_Printf("Assosiation of %s would cause infinate loop\n", name);
1094 			return P_INVALID;
1095 		}
1096 		to = part_type[to].assoc;
1097 	}
1098 	return orig;
1099 }
1100 
P_LoadTexture(part_type_t * ptype,qboolean warn)1101 static void P_LoadTexture(part_type_t *ptype, qboolean warn)
1102 {
1103 	if (*ptype->texname)
1104 	{
1105 		byte *data = NULL;
1106 		char filename[MAX_QPATH];
1107 		int fwidth=0, fheight=0;
1108 		int hunkmark;
1109 		char *texname = va("%s%s%s", ptype->texname, ptype->looks.premul?"_premul":"", ptype->looks.nearest?"_nearest":"");
1110 
1111 		ptype->looks.texture = TexMgr_FindTexture(NULL, texname);
1112 		if (!ptype->looks.texture)
1113 		{
1114 			hunkmark = Hunk_LowMark();
1115 			if (!data)
1116 			{
1117 				q_snprintf (filename, sizeof(filename), "textures/%s", ptype->texname);
1118 				data = Image_LoadImage (filename, &fwidth, &fheight);
1119 			}
1120 			if (!data)
1121 			{
1122 				q_snprintf (filename, sizeof(filename), "%s", ptype->texname);
1123 				data = Image_LoadImage (filename, &fwidth, &fheight);
1124 			}
1125 
1126 			if (data)
1127 			{
1128 				ptype->looks.texture = TexMgr_LoadImage(NULL, texname, fwidth, fheight, SRC_RGBA, data, filename, 0, (ptype->looks.premul?TEXPREF_PREMULTIPLY:0)|(ptype->looks.nearest?TEXPREF_NEAREST:TEXPREF_LINEAR)|TEXPREF_NOPICMIP|TEXPREF_ALPHA);
1129 			}
1130 			Hunk_FreeToLowMark(hunkmark);
1131 		}
1132 	}
1133 	else
1134 		ptype->looks.texture = 0;
1135 
1136 	if (!ptype->looks.texture)
1137 	{
1138 		//the specified texture isn't valid. make something up based upon the particle's type
1139 		ptype->s1 = 0;
1140 		ptype->t1 = 0;
1141 		ptype->s2 = 1;
1142 		ptype->t2 = 1;
1143 		ptype->randsmax = 1;
1144 
1145 #define PARTICLETEXTURESIZE 64
1146 		if (ptype->looks.type == PT_SPARK)
1147 		{
1148 			static gltexture_t *thetex;
1149 			if (!thetex)
1150 			{
1151 				static byte data[4*4*4];
1152 				memset(data, 0xff, sizeof(data));
1153 				thetex = TexMgr_LoadImage(NULL, "particles/white", 4, 4, SRC_RGBA, data, "", (src_offset_t)data, TEXPREF_PERSIST|TEXPREF_NOPICMIP|TEXPREF_LINEAR|TEXPREF_ALPHA);
1154 			}
1155 			ptype->looks.texture = thetex;
1156 		}
1157 		else if (ptype->looks.type == PT_BEAM) //untextured beams get a single continuous blob
1158 		{
1159 			static gltexture_t *thetex;
1160 			if (!thetex)
1161 			{
1162 				int y, x;
1163 				float dy, d;
1164 				static byte data[PARTICLETEXTURESIZE*PARTICLETEXTURESIZE*4];
1165 				memset(data, 0xff, sizeof(data));
1166 				for (y = 0;y < PARTICLETEXTURESIZE;y++)
1167 				{
1168 					dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1169 					d = 256 * (1 - (dy*dy));
1170 					if (d < 0) d = 0;
1171 					for (x = 0;x < PARTICLETEXTURESIZE;x++)
1172 					{
1173 						data[(y*PARTICLETEXTURESIZE+x)*4+3] = (byte) d;
1174 					}
1175 				}
1176 				thetex = TexMgr_LoadImage(NULL, "particles/beamtexture", PARTICLETEXTURESIZE, PARTICLETEXTURESIZE, SRC_RGBA, data, "", (src_offset_t)data, TEXPREF_PERSIST|TEXPREF_NOPICMIP|TEXPREF_LINEAR|TEXPREF_ALPHA);
1177 			}
1178 			ptype->looks.texture = thetex;
1179 		}
1180 		else if (ptype->looks.type == PT_SPARKFAN) 	//untextured beams get a single continuous blob
1181 		{
1182 			static gltexture_t *thetex;
1183 			if (!thetex)
1184 			{
1185 				int y, x;
1186 				float dy, dx, d;
1187 				static byte data[PARTICLETEXTURESIZE*PARTICLETEXTURESIZE*4];
1188 				for (y = 0;y < PARTICLETEXTURESIZE;y++)
1189 				{
1190 					dy = y / (PARTICLETEXTURESIZE*0.5f-1);
1191 					for (x = 0;x < PARTICLETEXTURESIZE;x++)
1192 					{
1193 						dx = x / (PARTICLETEXTURESIZE*0.5f-1);
1194 						d = 256 * (1 - (dx+dy));
1195 						if (d < 0) d = 0;
1196 						data[(y*PARTICLETEXTURESIZE+x)*4+0] = (byte) d;
1197 						data[(y*PARTICLETEXTURESIZE+x)*4+1] = (byte) d;
1198 						data[(y*PARTICLETEXTURESIZE+x)*4+2] = (byte) d;
1199 						data[(y*PARTICLETEXTURESIZE+x)*4+3] = (byte) d/2;
1200 					}
1201 				}
1202 				thetex = TexMgr_LoadImage(NULL, "particles/ptritexture", PARTICLETEXTURESIZE, PARTICLETEXTURESIZE, SRC_RGBA, data, "", (src_offset_t)data, TEXPREF_PERSIST|TEXPREF_NOPICMIP|TEXPREF_LINEAR|TEXPREF_ALPHA);
1203 			}
1204 			ptype->looks.texture = thetex;
1205 		}
1206 		else if (strstr(ptype->texname, "classicparticle"))
1207 		{
1208 			extern gltexture_t *particletexture1;
1209 			ptype->looks.texture = particletexture1;
1210 			ptype->s2 = 0.5;
1211 			ptype->t2 = 0.5;
1212 		}
1213 		else if (strstr(ptype->texname, "glow") || strstr(ptype->texname, "ball") || ptype->looks.type == PT_TEXTUREDSPARK) //sparks and special names get a nice circular texture.
1214 		{
1215 			static gltexture_t *thetex;
1216 			if (!thetex)
1217 			{
1218 				int y, x;
1219 				float dy, dx, d;
1220 				static byte data[PARTICLETEXTURESIZE*PARTICLETEXTURESIZE*4];
1221 				memset(data, 0xff, sizeof(data));
1222 				for (y = 0;y < PARTICLETEXTURESIZE;y++)
1223 				{
1224 					dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1225 					for (x = 0;x < PARTICLETEXTURESIZE;x++)
1226 					{
1227 						dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1228 						d = 255 * (1 - (dx*dx+dy*dy));
1229 						if (d < 0) d = 0;
1230 						data[(y*PARTICLETEXTURESIZE+x)*4+3] = (byte) d;
1231 					}
1232 				}
1233 				thetex = TexMgr_LoadImage(NULL, "particles/balltexture", PARTICLETEXTURESIZE, PARTICLETEXTURESIZE, SRC_RGBA, data, "", (src_offset_t)data, TEXPREF_PERSIST|TEXPREF_NOPICMIP|TEXPREF_LINEAR|TEXPREF_ALPHA);
1234 			}
1235 			ptype->looks.texture = thetex;
1236 		}
1237 		else //anything else gets a fuzzy texture
1238 		{
1239 			static gltexture_t *thetex;
1240 			if (!thetex)
1241 			{
1242 				int y, x;
1243 				static byte	exptexture[16][16] =
1244 				{
1245 					{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
1246 					{0,0,0,0,1,0,0,0,1,0,0,1,0,0,0,0},
1247 					{0,0,0,1,1,1,1,1,3,1,1,2,1,0,0,0},
1248 					{0,0,0,1,1,1,1,4,4,4,5,4,2,1,1,0},
1249 					{0,0,1,1,6,5,5,8,6,8,3,6,3,2,1,0},
1250 					{0,0,1,5,6,7,5,6,8,8,8,3,3,1,0,0},
1251 					{0,0,0,1,6,8,9,9,9,9,4,6,3,1,0,0},
1252 					{0,0,2,1,7,7,9,9,9,9,5,3,1,0,0,0},
1253 					{0,0,2,4,6,8,9,9,9,9,8,6,1,0,0,0},
1254 					{0,0,2,2,3,5,6,8,9,8,8,4,4,1,0,0},
1255 					{0,0,1,2,4,1,8,7,8,8,6,5,4,1,0,0},
1256 					{0,1,1,1,7,8,1,6,7,5,4,7,1,0,0,0},
1257 					{0,1,2,1,1,5,1,3,4,3,1,1,0,0,0,0},
1258 					{0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0},
1259 					{0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0},
1260 					{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
1261 				};
1262 				static byte data[16*16*4];
1263 				for (x=0 ; x<16 ; x++)
1264 				{
1265 					for (y=0 ; y<16 ; y++)
1266 					{
1267 						data[(y*16+x)*4+0] = 255;
1268 						data[(y*16+x)*4+1] = 255;
1269 						data[(y*16+x)*4+2] = 255;
1270 						data[(y*16+x)*4+3] = exptexture[x][y]*255/9.0;
1271 					}
1272 				}
1273 				thetex = TexMgr_LoadImage(NULL, "particles/fuzzyparticle", 16, 16, SRC_RGBA, data, "", (src_offset_t)data, TEXPREF_PERSIST|TEXPREF_NOPICMIP|TEXPREF_LINEAR|TEXPREF_ALPHA);
1274 			}
1275 			ptype->looks.texture = thetex;
1276 		}
1277 	}
1278 }
1279 
P_ResetToDefaults(part_type_t * ptype)1280 static void P_ResetToDefaults(part_type_t *ptype)
1281 {
1282 	particle_t *parts;
1283 	part_type_t *torun;
1284 	char tnamebuf[sizeof(ptype->name)];
1285 	char tconfbuf[sizeof(ptype->config)];
1286 
1287 	// go with a lazy clear of list.. mark everything as DEAD and let
1288 	// the beam rendering handle removing nodes
1289 	beamseg_t *beamsegs = ptype->beams;
1290 	while (beamsegs)
1291 	{
1292 		beamsegs->flags |= BS_DEAD;
1293 		beamsegs = beamsegs->next;
1294 	}
1295 
1296 	// forget any particles before its wiped
1297 	while (ptype->particles)
1298 	{
1299 		parts = ptype->particles->next;
1300 		ptype->particles->next = free_particles;
1301 		free_particles = ptype->particles;
1302 		ptype->particles = parts;
1303 	}
1304 
1305 	// if we're in the runstate loop through and remove from linked list
1306 	if (ptype->state & PS_INRUNLIST)
1307 	{
1308 		if (part_run_list == ptype)
1309 			part_run_list = part_run_list->nexttorun;
1310 		else
1311 		{
1312 			for (torun = part_run_list; torun != NULL; torun = torun->nexttorun)
1313 			{
1314 				if (torun->nexttorun == ptype)
1315 					torun->nexttorun = torun->nexttorun->nexttorun;
1316 			}
1317 		}
1318 	}
1319 
1320 	//some things need to be preserved before we clear everything.
1321 	beamsegs = ptype->beams;
1322 	strcpy(tnamebuf, ptype->name);
1323 	strcpy(tconfbuf, ptype->config);
1324 
1325 	//free uneeded info
1326 	if (ptype->ramp)
1327 		Z_Free(ptype->ramp);
1328 	if (ptype->sounds)
1329 		Z_Free(ptype->sounds);
1330 
1331 	//reset everything we're too lazy to specifically set
1332 	memset(ptype, 0, sizeof(*ptype));
1333 
1334 	//now set any non-0 defaults.
1335 
1336 	ptype->beams = beamsegs;
1337 	ptype->rainfrequency = 1;
1338 	strcpy(ptype->name, tnamebuf);
1339 	strcpy(ptype->config, tconfbuf);
1340 	ptype->assoc=P_INVALID;
1341 	ptype->inwater = P_INVALID;
1342 	ptype->cliptype = P_INVALID;
1343 	ptype->emit = P_INVALID;
1344 	ptype->fluidmask = FTECONTENTS_FLUID;
1345 	ptype->alpha = 1;
1346 	ptype->alphachange = 1;
1347 	ptype->clipbounce = 0.8;
1348 	ptype->clipcount = 1;
1349 	ptype->colorindex = -1;
1350 	ptype->rotationstartmin = -M_PI;	//start with a random angle
1351 	ptype->rotationstartrand = M_PI-ptype->rotationstartmin;
1352 	ptype->spawnchance = 1;
1353 	ptype->dl_time = 0;
1354 	VectorSet(ptype->dl_rgb, 1, 1, 1);
1355 	ptype->dl_corona_intensity = 0.25;
1356 	ptype->dl_corona_scale = 0.5;
1357 	VectorSet(ptype->dl_scales, 0, 1, 1);
1358 	ptype->looks.stretch = 0.05;
1359 
1360 	ptype->randsmax = 1;
1361 	ptype->s2 = 1;
1362 	ptype->t2 = 1;
1363 }
1364 
PScript_ReadLine(char * buffer,size_t buffersize,const char * filedata,size_t filesize,size_t * offset)1365 char *PScript_ReadLine(char *buffer, size_t buffersize, const char *filedata, size_t filesize, size_t *offset)
1366 {
1367 	const char *start = filedata + *offset;
1368 	const char *f = start;
1369 	const char *e = filedata+filesize;
1370 	if (f >= e)
1371 		return NULL;	//eof
1372 	while (f < e)
1373 	{
1374 		if (*f++ == '\n')
1375 			break;
1376 	}
1377 
1378 	*offset = f-filedata;
1379 
1380 	buffersize--;
1381 	if (buffersize >= (size_t)(f-start))
1382 		buffersize = f-start;
1383 	memcpy(buffer, start, buffersize);
1384 	buffer[buffersize] = 0;	//null terminate it
1385 
1386 	return buffer;
1387 }
1388 
1389 //This is the function that loads the effect descriptions.
PScript_ParseParticleEffectFile(const char * config,qboolean part_parseweak,char * context,size_t filesize)1390 void PScript_ParseParticleEffectFile(const char *config, qboolean part_parseweak, char *context, size_t filesize)
1391 {
1392 	const char *var, *value;
1393 	char *buf;
1394 	qboolean settype;
1395 	qboolean setalphadelta;
1396 	qboolean setbeamlen;
1397 
1398 	part_type_t *ptype;
1399 	int pnum, assoc;
1400 	char line[512];
1401 	char part_parsenamespace[MAX_QPATH];
1402 
1403 	byte *palrgba = (byte *)d_8to24table;
1404 	size_t offset = 0;
1405 
1406 	q_strlcpy(part_parsenamespace, config, sizeof(part_parsenamespace));
1407 	config = part_parsenamespace;
1408 
1409 nexteffect:
1410 
1411 	if (!PScript_ReadLine(line, sizeof(line), context, filesize, &offset))
1412 		return;	//eof
1413 reparse:
1414 
1415 	Cmd_TokenizeString(line);
1416 
1417 	var = Cmd_Argv(0);
1418 
1419 
1420 	if (!strcmp(var, "r_effect") || !strcmp(var, "r_trail"))
1421 	{	//add an emit/trail effect to all ents using said model
1422 		PScript_AssociateEffect_f();
1423 		goto nexteffect;
1424 	}
1425 	else if (!strcmp(var, "r_partredirect"))
1426 	{	//add an emit/trail effect to all ents using said model
1427 		P_PartRedirect_f();
1428 		goto nexteffect;
1429 	}
1430 	else if (strcmp(var, "r_part"))
1431 	{
1432 		if (*var)
1433 			Con_SafePrintf("Unknown particle command \"%s\"\n", var);
1434 		goto nexteffect;
1435 	}
1436 
1437 	settype = false;
1438 	setalphadelta = false;
1439 	setbeamlen = false;
1440 
1441 	if (Cmd_Argc()!=2)
1442 	{
1443 		if (!strcmp(Cmd_Argv(1), "namespace"))
1444 		{
1445 			q_strlcpy(part_parsenamespace, Cmd_Argv(2), sizeof(part_parsenamespace));
1446 			if (Cmd_Argc() >= 4)
1447 				part_parseweak = atoi(Cmd_Argv(3));
1448 			goto nexteffect;
1449 		}
1450 		Con_Printf("No name for particle effect\n");
1451 		goto nexteffect;
1452 	}
1453 
1454 	buf = PScript_ReadLine(line, sizeof(line), context, filesize, &offset);
1455 	if (!buf)
1456 		return;	//eof
1457 	while (*buf && *buf <= ' ')
1458 		buf++;	//no whitespace please.
1459 	if (*buf != '{')
1460 	{
1461 		Con_Printf("This is a multiline command and should be used within config files\n");
1462 		goto reparse;
1463 	}
1464 
1465 	var = Cmd_Argv(1);
1466 	if (*var == '+')
1467 		ptype = P_GetParticleType(config, var+1);
1468 	else
1469 		ptype = P_GetParticleType(config, var);
1470 
1471 	//'weak' configs do not replace 'strong' configs
1472 	//we allow weak to replace weak as a solution to the +assoc chain thing (to add, we effectively need to 'replace').
1473 	if ((part_parseweak && ptype->loaded==2))
1474 	{
1475 		int depth = 1;
1476 		while(1)
1477 		{
1478 			buf = PScript_ReadLine(line, sizeof(line), context, filesize, &offset);
1479 			if (!buf)
1480 				return;
1481 
1482 			while (*buf && *buf <= ' ')
1483 				buf++;	//no whitespace please.
1484 			if (*buf == '{')
1485 				depth++;
1486 			else if (*buf == '}')
1487 			{
1488 				if (--depth == 0)
1489 					break;
1490 			}
1491 		}
1492 		goto nexteffect;
1493 	}
1494 
1495 	if (*var == '+')
1496 	{
1497 		if (ptype->loaded)
1498 		{
1499 			int i, parenttype;
1500 			char newname[256];
1501 			for (i = 0; i < 64; i++)
1502 			{
1503 				parenttype = ptype - part_type;
1504 				q_snprintf(newname, sizeof(newname), "+%i%s", i, ptype->name);
1505 				ptype = P_GetParticleType(config, newname);
1506 				if (!ptype->loaded)
1507 				{
1508 					if (part_type[parenttype].assoc != P_INVALID)
1509 						Con_Printf("warning: assoc on particle chain %s overridden\n", var+1);
1510 					part_type[parenttype].assoc = ptype - part_type;
1511 					break;
1512 				}
1513 			}
1514 			if (i == 64)
1515 			{
1516 				Con_Printf("Too many duplicate names, gave up\n");
1517 				return;
1518 			}
1519 		}
1520 	}
1521 	else
1522 	{
1523 		if (ptype->loaded)
1524 		{
1525 			assoc = ptype->assoc;
1526 			while (assoc != P_INVALID && assoc < numparticletypes)
1527 			{
1528 				if (*part_type[assoc].name == '+')
1529 				{
1530 					part_type[assoc].loaded = false;
1531 					assoc = part_type[assoc].assoc;
1532 				}
1533 				else
1534 					break;
1535 			}
1536 		}
1537 	}
1538 	if (!ptype)
1539 	{
1540 		Con_Printf("Bad name\n");
1541 		return;
1542 	}
1543 
1544 	pnum = ptype-part_type;
1545 
1546 	P_ResetToDefaults(ptype);
1547 
1548 	while(1)
1549 	{
1550 		buf = PScript_ReadLine(line, sizeof(line), context, filesize, &offset);
1551 		if (!buf)
1552 		{
1553 			Con_Printf("Unexpected end of buffer with effect %s\n", ptype->name);
1554 			return;
1555 		}
1556 skipread:
1557 		while (*buf && *buf <= ' ')
1558 			buf++;	//no whitespace please.
1559 		if (*buf == '}')
1560 			break;
1561 
1562 		Cmd_TokenizeString(buf);
1563 		var = Cmd_Argv(0);
1564 		value = Cmd_Argv(1);
1565 
1566 		// TODO: switch this mess to some sort of binary tree to increase parse speed
1567 		if (!strcmp(var, "shader"))
1568 		{
1569 			q_strlcpy(ptype->texname, ptype->name, sizeof(ptype->texname));
1570 
1571 			buf = PScript_ReadLine(line, sizeof(line), context, filesize, &offset);
1572 			if (!buf)
1573 				continue;
1574 			while (*buf && *buf <= ' ')
1575 				buf++;	//no leading whitespace please.
1576 			if (*buf == '{')
1577 			{
1578 				int nest = 1;
1579 				char *str = Z_Malloc(3);
1580 				int slen = 2;
1581 				str[0] = '{';
1582 				str[1] = '\n';
1583 				str[2] = 0;
1584 				while(nest)
1585 				{
1586 					buf = PScript_ReadLine(line, sizeof(line), context, filesize, &offset);
1587 					if (!buf)
1588 					{
1589 						Con_Printf("Unexpected end of buffer with effect %s\n", ptype->name);
1590 						break;
1591 					}
1592 					while (*buf && *buf <= ' ')
1593 						buf++;	//no leading whitespace please.
1594 					if (*buf == '}')
1595 						--nest;
1596 					if (*buf == '{')
1597 						nest++;
1598 					str = Z_Realloc(str, slen + strlen(buf) + 2);
1599 					strcpy(str + slen, buf);
1600 					slen += strlen(str + slen);
1601 					str[slen++] = '\n';
1602 				}
1603 				str[slen] = 0;
1604 				Z_Free(str);
1605 			}
1606 			else
1607 				goto skipread;
1608 		}
1609 		else if (!strcmp(var, "texture") || !strcmp(var, "linear_texture") || !strcmp(var, "nearest_texture") || !strcmp(var, "nearesttexture"))
1610 		{
1611 			q_strlcpy(ptype->texname, value, sizeof(ptype->texname));
1612 			ptype->looks.nearest = !strncmp(var, "nearest", 7);
1613 		}
1614 		else if (!strcmp(var, "tcoords"))
1615 		{
1616 			float tscale;
1617 
1618 			tscale = atof(Cmd_Argv(5));
1619 			if (tscale <= 0)
1620 				tscale = 1;
1621 
1622 			ptype->s1 = atof(value)/tscale;
1623 			ptype->t1 = atof(Cmd_Argv(2))/tscale;
1624 			ptype->s2 = atof(Cmd_Argv(3))/tscale;
1625 			ptype->t2 = atof(Cmd_Argv(4))/tscale;
1626 
1627 			ptype->randsmax = atoi(Cmd_Argv(6));
1628 			if (Cmd_Argc()>7)
1629 				ptype->texsstride = atof(Cmd_Argv(7));/*FIXME: divide-by-tscale missing */
1630 			else
1631 				ptype->texsstride = 1/tscale;
1632 
1633 			if (ptype->randsmax < 1 || ptype->texsstride == 0)
1634 				ptype->randsmax = 1;
1635 		}
1636 		else if (!strcmp(var, "atlas"))
1637 		{	//atlas countineachaxis first [last]
1638 			int dims;
1639 			int i;
1640 			int m;
1641 
1642 			dims = atof(Cmd_Argv(1));
1643 			i = atoi(Cmd_Argv(2));
1644 			m = atoi(Cmd_Argv(3));
1645 			if (dims < 1)
1646 				dims = 1;
1647 
1648 			if (m > (m/dims)*dims+dims-1)
1649 			{
1650 				m = (m/dims)*dims+dims-1;
1651 				Con_Printf("effect %s wraps across an atlased line\n", ptype->name);
1652 			}
1653 			if (m < i)
1654 				m = i;
1655 
1656 			ptype->s1 = 1.0/dims * (i%dims);
1657 			ptype->s2 = 1.0/dims * (1+(i%dims));
1658 			ptype->t1 = 1.0/dims * (i/dims);
1659 			ptype->t2 = 1.0/dims * (1+(i/dims));
1660 
1661 			ptype->randsmax = m-i;
1662 			ptype->texsstride = ptype->s2-ptype->s1;
1663 
1664 			//its modulo
1665 			ptype->randsmax++;
1666 		}
1667 		else if (!strcmp(var, "rotation"))
1668 		{
1669 			ptype->rotationstartmin = atof(value)*M_PI/180;
1670 			if (Cmd_Argc()>2)
1671 				ptype->rotationstartrand = atof(Cmd_Argv(2))*M_PI/180-ptype->rotationstartmin;
1672 			else
1673 				ptype->rotationstartrand = 0;
1674 
1675 			ptype->rotationmin = atof(Cmd_Argv(3))*M_PI/180;
1676 			if (Cmd_Argc()>4)
1677 				ptype->rotationrand = atof(Cmd_Argv(4))*M_PI/180-ptype->rotationmin;
1678 			else
1679 				ptype->rotationrand = 0;
1680 		}
1681 		else if (!strcmp(var, "rotationstart"))
1682 		{
1683 			ptype->rotationstartmin = atof(value)*M_PI/180;
1684 			if (Cmd_Argc()>2)
1685 				ptype->rotationstartrand = atof(Cmd_Argv(2))*M_PI/180-ptype->rotationstartmin;
1686 			else
1687 				ptype->rotationstartrand = 0;
1688 		}
1689 		else if (!strcmp(var, "rotationspeed"))
1690 		{
1691 			ptype->rotationmin = atof(value)*M_PI/180;
1692 			if (Cmd_Argc()>2)
1693 				ptype->rotationrand = atof(Cmd_Argv(2))*M_PI/180-ptype->rotationmin;
1694 			else
1695 				ptype->rotationrand = 0;
1696 		}
1697 		else if (!strcmp(var, "beamtexstep"))
1698 		{
1699 			ptype->rotationstartmin = 1/atof(value);
1700 			ptype->rotationstartrand = 0;
1701 			setbeamlen = true;
1702 		}
1703 		else if (!strcmp(var, "beamtexspeed"))
1704 		{
1705 			ptype->rotationmin = atof(value);
1706 		}
1707 		else if (!strcmp(var, "scale"))
1708 		{
1709 			ptype->scale = atof(value);
1710 			if (Cmd_Argc()>2)
1711 				ptype->scalerand = atof(Cmd_Argv(2)) - ptype->scale;
1712 		}
1713 		else if (!strcmp(var, "scalerand"))
1714 			ptype->scalerand = atof(value);
1715 
1716 		else if (!strcmp(var, "scalefactor"))
1717 			ptype->looks.scalefactor = atof(value);
1718 		else if (!strcmp(var, "scaledelta"))
1719 			ptype->scaledelta = atof(value);
1720 		else if (!strcmp(var, "stretchfactor"))	//affects sparks
1721 		{
1722 			ptype->looks.stretch = atof(value);
1723 			ptype->looks.minstretch = (Cmd_Argc()>2)?atof(Cmd_Argv(2)):0;
1724 		}
1725 
1726 		else if (!strcmp(var, "step"))
1727 		{
1728 			ptype->countspacing = atof(value);
1729 			ptype->count = 1/atof(value);
1730 			if (Cmd_Argc()>2)
1731 				ptype->countrand = 1/atof(Cmd_Argv(2));
1732 			if (Cmd_Argc()>3)
1733 				ptype->countextra = atof(Cmd_Argv(3));
1734 		}
1735 		else if (!strcmp(var, "count"))
1736 		{
1737 			ptype->countspacing = 0;
1738 			ptype->count = atof(value);
1739 			if (Cmd_Argc()>2)
1740 				ptype->countrand = atof(Cmd_Argv(2));
1741 			if (Cmd_Argc()>3)
1742 				ptype->countextra = atof(Cmd_Argv(3));
1743 		}
1744 		else if (!strcmp(var, "rainfrequency"))
1745 		{	//multiplier to ramp up the effect or whatever (without affecting spawn patterns).
1746 			ptype->rainfrequency = atof(value);
1747 		}
1748 
1749 		else if (!strcmp(var, "alpha"))
1750 			ptype->alpha = atof(value);
1751 		else if (!strcmp(var, "alpharand"))
1752 			ptype->alpharand = atof(value);
1753 #ifndef NOLEGACY
1754 		else if (!strcmp(var, "alphachange"))
1755 		{
1756 			Con_DPrintf("%s.%s: alphachange is deprecated, use alphadelta\n", ptype->config, ptype->name);
1757 			ptype->alphachange = atof(value);
1758 		}
1759 #endif
1760 		else if (!strcmp(var, "alphadelta"))
1761 		{
1762 			ptype->alphachange = atof(value);
1763 			setalphadelta = true;
1764 		}
1765 		else if (!strcmp(var, "die"))
1766 		{
1767 			ptype->die = atof(value);
1768 			if (Cmd_Argc()>2)
1769 			{
1770 				float mn=ptype->die,mx=atof(Cmd_Argv(2));
1771 				if (mn > mx)
1772 				{
1773 					mn = mx;
1774 					mx = ptype->die;
1775 				}
1776 				ptype->die = mx;
1777 				ptype->randdie = mx-mn;
1778 			}
1779 		}
1780 #ifndef NOLEGACY
1781 		else if (!strcmp(var, "diesubrand"))
1782 		{
1783 			Con_DPrintf("%s.%s: diesubrand is deprecated, use die with two arguments\n", ptype->config, ptype->name);
1784 			ptype->randdie = atof(value);
1785 		}
1786 #endif
1787 
1788 		else if (!strcmp(var, "randomvel"))
1789 		{	//shortcut for velwrand (and velbias for z bias)
1790 			ptype->velbias[0] = ptype->velbias[1] = 0;
1791 			ptype->velwrand[0] = ptype->velwrand[1] = atof(value);
1792 			if (Cmd_Argc()>3)
1793 			{
1794 				ptype->velbias[2] = atof(Cmd_Argv(2));
1795 				ptype->velwrand[2] = atof(Cmd_Argv(3));
1796 				ptype->velwrand[2] -= ptype->velbias[2]; /*make vert be the total range*/
1797 				ptype->velwrand[2] /= 2; /*vert is actually +/- 1, not 0 to 1, so rescale it*/
1798 				ptype->velbias[2] += ptype->velwrand[2]; /*and bias must be centered to the range*/
1799 			}
1800 			else if (Cmd_Argc()>2)
1801 			{
1802 				ptype->velwrand[2] = atof(Cmd_Argv(2));
1803 				ptype->velbias[2] = 0;
1804 			}
1805 			else
1806 			{
1807 				ptype->velwrand[2] = ptype->velwrand[0];
1808 				ptype->velbias[2] = 0;
1809 			}
1810 		}
1811 		else if (!strcmp(var, "veladd"))
1812 		{
1813 			ptype->veladd = atof(value);
1814 			ptype->randomveladd = 0;
1815 			if (Cmd_Argc()>2)
1816 				ptype->randomveladd = atof(Cmd_Argv(2)) - ptype->veladd;
1817 		}
1818 		else if (!strcmp(var, "orgadd"))
1819 		{
1820 			ptype->orgadd = atof(value);
1821 			ptype->randomorgadd = 0;
1822 			if (Cmd_Argc()>2)
1823 				ptype->randomorgadd = atof(Cmd_Argv(2)) - ptype->orgadd;
1824 		}
1825 
1826 		else if (!strcmp(var, "orgbias"))
1827 		{
1828 			ptype->orgbias[0] = atof(value);
1829 			ptype->orgbias[1] = atof(Cmd_Argv(2));
1830 			ptype->orgbias[2] = atof(Cmd_Argv(3));
1831 		}
1832 		else if (!strcmp(var, "orgwrand"))
1833 		{
1834 			ptype->orgwrand[0] = atof(value);
1835 			ptype->orgwrand[1] = atof(Cmd_Argv(2));
1836 			ptype->orgwrand[2] = atof(Cmd_Argv(3));
1837 		}
1838 
1839 		else if (!strcmp(var, "velbias"))
1840 		{
1841 			ptype->velbias[0] = atof(value);
1842 			ptype->velbias[1] = atof(Cmd_Argv(2));
1843 			ptype->velbias[2] = atof(Cmd_Argv(3));
1844 		}
1845 		else if (!strcmp(var, "velwrand"))
1846 		{
1847 			ptype->velwrand[0] = atof(value);
1848 			ptype->velwrand[1] = atof(Cmd_Argv(2));
1849 			ptype->velwrand[2] = atof(Cmd_Argv(3));
1850 		}
1851 
1852 		else if (!strcmp(var, "friction"))
1853 		{
1854 			ptype->friction[2] = ptype->friction[1] = ptype->friction[0] = atof(value);
1855 
1856 			if (Cmd_Argc()>3)
1857 			{
1858 				ptype->friction[2] = atof(Cmd_Argv(3));
1859 				ptype->friction[1] = atof(Cmd_Argv(2));
1860 			}
1861 			else if (Cmd_Argc()>2)
1862 			{
1863 				ptype->friction[2] = atof(Cmd_Argv(2));
1864 			}
1865 		}
1866 		else if (!strcmp(var, "gravity"))
1867 			ptype->gravity = atof(value);
1868 		else if (!strcmp(var, "flurry"))
1869 			ptype->flurry = atof(value);
1870 
1871 		else if (!strcmp(var, "assoc"))
1872 		{
1873 			assoc = CheckAssosiation(config, value, pnum);	//careful - this can realloc all the particle types
1874 			ptype = &part_type[pnum];
1875 			ptype->assoc = assoc;
1876 		}
1877 		else if (!strcmp(var, "inwater"))
1878 		{
1879 			// the underwater effect switch should only occur for
1880 			// 1 level so the standard assoc check works
1881 			assoc = CheckAssosiation(config, value, pnum);
1882 			ptype = &part_type[pnum];
1883 			ptype->inwater = assoc;
1884 		}
1885 		else if (!strcmp(var, "underwater"))
1886 		{
1887 			ptype->flags |= PT_TRUNDERWATER;
1888 
1889 parsefluid:
1890 			if ((ptype->flags & (PT_TRUNDERWATER|PT_TROVERWATER)) == (PT_TRUNDERWATER|PT_TROVERWATER))
1891 			{
1892 				ptype->flags &= ~PT_TRUNDERWATER;
1893 				Con_Printf("%s.%s: both over and under water\n", ptype->config, ptype->name);
1894 			}
1895 			if (Cmd_Argc() == 1)
1896 				ptype->fluidmask = FTECONTENTS_FLUID;
1897 			else
1898 			{
1899 				int i = Cmd_Argc();
1900 				ptype->fluidmask = 0;
1901 				while (i --> 1)
1902 				{
1903 					const char *value_i = Cmd_Argv(i);
1904 					if (!strcmp(value_i, "water"))
1905 						ptype->fluidmask |= FTECONTENTS_WATER;
1906 					else if (!strcmp(value_i, "slime"))
1907 						ptype->fluidmask |= FTECONTENTS_SLIME;
1908 					else if (!strcmp(value_i, "lava"))
1909 						ptype->fluidmask |= FTECONTENTS_LAVA;
1910 					else if (!strcmp(value_i, "sky"))
1911 						ptype->fluidmask |= FTECONTENTS_SKY;
1912 					else if (!strcmp(value_i, "fluid"))
1913 						ptype->fluidmask |= FTECONTENTS_FLUID;
1914 					else if (!strcmp(value_i, "solid"))
1915 						ptype->fluidmask |= FTECONTENTS_SOLID;
1916 					else if (!strcmp(value_i, "playerclip"))
1917 						ptype->fluidmask |= FTECONTENTS_PLAYERCLIP;
1918 					else if (!strcmp(value_i, "none"))
1919 						ptype->fluidmask |= 0;
1920 					else
1921 						Con_Printf("%s.%s: unknown contents: %s\n", ptype->config, ptype->name, value_i);
1922 				}
1923 			}
1924 		}
1925 		else if (!strcmp(var, "notunderwater"))
1926 		{
1927 			ptype->flags |= PT_TROVERWATER;
1928 			goto parsefluid;
1929 		}
1930 		else if (!strcmp(var, "model"))
1931 		{
1932 			Con_DPrintf("%s.%s: model particles are not supported in this build\n", ptype->config, ptype->name);
1933 		}
1934 		else if (!strcmp(var, "sound"))
1935 		{
1936 			const char *e;
1937 			ptype->sounds = Z_Realloc(ptype->sounds, sizeof(partsounds_t)*(ptype->numsounds+1));
1938 			q_strlcpy(ptype->sounds[ptype->numsounds].name, Cmd_Argv(1), sizeof(ptype->sounds[ptype->numsounds].name));
1939 			if (*ptype->sounds[ptype->numsounds].name)
1940 				S_PrecacheSound(ptype->sounds[ptype->numsounds].name);
1941 
1942 			ptype->sounds[ptype->numsounds].vol = 1;
1943 			ptype->sounds[ptype->numsounds].atten = 1;
1944 			ptype->sounds[ptype->numsounds].pitch = 100;
1945 			ptype->sounds[ptype->numsounds].delay = 0;
1946 			ptype->sounds[ptype->numsounds].weight = 0;
1947 
1948 			strtoul(Cmd_Argv(2), (char**)&e, 0);
1949 			while(*e == ' ' || *e == '\t')
1950 				e++;
1951 			if (*e)
1952 			{
1953 				int p;
1954 				for(p = 2; p < Cmd_Argc(); p++)
1955 				{
1956 					e = Cmd_Argv(p);
1957 
1958 					if (!q_strncasecmp(e, "vol=", 4) || !q_strncasecmp(e, "volume=", 7))
1959 						ptype->sounds[ptype->numsounds].vol = atof(strchr(e, '=')+1);
1960 					else if (!q_strncasecmp(e, "attn=", 5) || !q_strncasecmp(e, "atten=", 6) || !q_strncasecmp(e, "attenuation=", 12))
1961 					{
1962 						e = strchr(e, '=')+1;
1963 						if (!strcmp(e, "none"))
1964 							ptype->sounds[ptype->numsounds].atten = 0;
1965 						else if (!strcmp(e, "normal"))
1966 							ptype->sounds[ptype->numsounds].atten = 1;
1967 						else
1968 							ptype->sounds[ptype->numsounds].atten = atof(e);
1969 					}
1970 					else if (!q_strncasecmp(e, "pitch=", 6))
1971 						ptype->sounds[ptype->numsounds].pitch = atof(strchr(e, '=')+1);
1972 					else if (!q_strncasecmp(e, "delay=", 6))
1973 						ptype->sounds[ptype->numsounds].delay = atof(strchr(e, '=')+1);
1974 					else if (!q_strncasecmp(e, "weight=", 7))
1975 						ptype->sounds[ptype->numsounds].weight = atof(strchr(e, '=')+1);
1976 					else
1977 						Con_Printf("Bad named argument: %s\n", e);
1978 				}
1979 			}
1980 			else
1981 			{
1982 				ptype->sounds[ptype->numsounds].vol = atof(Cmd_Argv(2));
1983 				if (!ptype->sounds[ptype->numsounds].vol)
1984 					ptype->sounds[ptype->numsounds].vol = 1;
1985 				ptype->sounds[ptype->numsounds].atten = atof(Cmd_Argv(3));
1986 				if (!ptype->sounds[ptype->numsounds].atten)
1987 					ptype->sounds[ptype->numsounds].atten = 1;
1988 				ptype->sounds[ptype->numsounds].pitch = atof(Cmd_Argv(4));
1989 				if (!ptype->sounds[ptype->numsounds].pitch)
1990 					ptype->sounds[ptype->numsounds].pitch = 100;
1991 				ptype->sounds[ptype->numsounds].delay = atof(Cmd_Argv(5));
1992 				if (!ptype->sounds[ptype->numsounds].delay)
1993 					ptype->sounds[ptype->numsounds].delay = 0;
1994 				ptype->sounds[ptype->numsounds].weight = atof(Cmd_Argv(6));
1995 			}
1996 			if (!ptype->sounds[ptype->numsounds].weight)
1997 				ptype->sounds[ptype->numsounds].weight = 1;
1998 			ptype->numsounds++;
1999 		}
2000 		else if (!strcmp(var, "colorindex"))
2001 		{
2002 			if (Cmd_Argc()>2)
2003 				ptype->colorrand = strtoul(Cmd_Argv(2), NULL, 0);
2004 			ptype->colorindex = strtoul(value, NULL, 0);
2005 		}
2006 		else if (!strcmp(var, "colorrand"))
2007 			ptype->colorrand = atoi(value); // now obsolete
2008 		else if (!strcmp(var, "citracer"))
2009 			ptype->flags |= PT_CITRACER;
2010 
2011 		else if (!strcmp(var, "red"))
2012 			ptype->rgb[0] = atof(value)/255;
2013 		else if (!strcmp(var, "green"))
2014 			ptype->rgb[1] = atof(value)/255;
2015 		else if (!strcmp(var, "blue"))
2016 			ptype->rgb[2] = atof(value)/255;
2017 		else if (!strcmp(var, "rgb"))
2018 		{	//byte version
2019 			ptype->rgb[0] = ptype->rgb[1] = ptype->rgb[2] = atof(value)/255;
2020 			if (Cmd_Argc()>3)
2021 			{
2022 				ptype->rgb[1] = atof(Cmd_Argv(2))/255;
2023 				ptype->rgb[2] = atof(Cmd_Argv(3))/255;
2024 			}
2025 		}
2026 		else if (!strcmp(var, "rgbf"))
2027 		{	//float version
2028 			ptype->rgb[0] = ptype->rgb[1] = ptype->rgb[2] = atof(value);
2029 			if (Cmd_Argc()>3)
2030 			{
2031 				ptype->rgb[1] = atof(Cmd_Argv(2));
2032 				ptype->rgb[2] = atof(Cmd_Argv(3));
2033 			}
2034 		}
2035 
2036 		else if (!strcmp(var, "reddelta"))
2037 		{
2038 			ptype->rgbchange[0] = atof(value)/255;
2039 			if (!ptype->rgbchangetime)
2040 				ptype->rgbchangetime = ptype->die;
2041 		}
2042 		else if (!strcmp(var, "greendelta"))
2043 		{
2044 			ptype->rgbchange[1] = atof(value)/255;
2045 			if (!ptype->rgbchangetime)
2046 				ptype->rgbchangetime = ptype->die;
2047 		}
2048 		else if (!strcmp(var, "bluedelta"))
2049 		{
2050 			ptype->rgbchange[2] = atof(value)/255;
2051 			if (!ptype->rgbchangetime)
2052 				ptype->rgbchangetime = ptype->die;
2053 		}
2054 		else if (!strcmp(var, "rgbdelta"))
2055 		{	//byte version
2056 			ptype->rgbchange[0] = ptype->rgbchange[1] = ptype->rgbchange[2] = atof(value)/255;
2057 			if (Cmd_Argc()>3)
2058 			{
2059 				ptype->rgbchange[1] = atof(Cmd_Argv(2))/255;
2060 				ptype->rgbchange[2] = atof(Cmd_Argv(3))/255;
2061 			}
2062 			if (!ptype->rgbchangetime)
2063 				ptype->rgbchangetime = ptype->die;
2064 		}
2065 		else if (!strcmp(var, "rgbdeltaf"))
2066 		{	//float version
2067 			ptype->rgbchange[0] = ptype->rgbchange[1] = ptype->rgbchange[2] = atof(value);
2068 			if (Cmd_Argc()>3)
2069 			{
2070 				ptype->rgbchange[1] = atof(Cmd_Argv(2));
2071 				ptype->rgbchange[2] = atof(Cmd_Argv(3));
2072 			}
2073 			if (!ptype->rgbchangetime)
2074 				ptype->rgbchangetime = ptype->die;
2075 		}
2076 		else if (!strcmp(var, "rgbdeltatime"))
2077 			ptype->rgbchangetime = atof(value);
2078 
2079 		else if (!strcmp(var, "redrand"))
2080 			ptype->rgbrand[0] = atof(value)/255;
2081 		else if (!strcmp(var, "greenrand"))
2082 			ptype->rgbrand[1] = atof(value)/255;
2083 		else if (!strcmp(var, "bluerand"))
2084 			ptype->rgbrand[2] = atof(value)/255;
2085 		else if (!strcmp(var, "rgbrand"))
2086 		{	//byte version
2087 			ptype->rgbrand[0] = ptype->rgbrand[1] = ptype->rgbrand[2] = atof(value)/255;
2088 			if (Cmd_Argc()>3)
2089 			{
2090 				ptype->rgbrand[1] = atof(Cmd_Argv(2))/255;
2091 				ptype->rgbrand[2] = atof(Cmd_Argv(3))/255;
2092 			}
2093 		}
2094 		else if (!strcmp(var, "rgbrandf"))
2095 		{	//float version
2096 			ptype->rgbrand[0] = ptype->rgbrand[1] = ptype->rgbrand[2] = atof(value);
2097 			if (Cmd_Argc()>3)
2098 			{
2099 				ptype->rgbrand[1] = atof(Cmd_Argv(2));
2100 				ptype->rgbrand[2] = atof(Cmd_Argv(3));
2101 			}
2102 		}
2103 
2104 		else if (!strcmp(var, "rgbrandsync"))
2105 		{
2106 			ptype->rgbrandsync[0] = ptype->rgbrandsync[1] = ptype->rgbrandsync[2] = atof(value);
2107 			if (Cmd_Argc()>3)
2108 			{
2109 				ptype->rgbrandsync[1] = atof(Cmd_Argv(2));
2110 				ptype->rgbrandsync[2] = atof(Cmd_Argv(3));
2111 			}
2112 		}
2113 		else if (!strcmp(var, "redrandsync"))
2114 			ptype->rgbrandsync[0] = atof(value);
2115 		else if (!strcmp(var, "greenrandsync"))
2116 			ptype->rgbrandsync[1] = atof(value);
2117 		else if (!strcmp(var, "bluerandsync"))
2118 			ptype->rgbrandsync[2] = atof(value);
2119 
2120 		else if (!strcmp(var, "stains"))
2121 			ptype->stainonimpact = atof(value);
2122 		else if (!strcmp(var, "blend"))
2123 		{
2124 			//small note: use premultiplied alpha where possible. this reduces the required state switches.
2125 			ptype->looks.premul = false;
2126 			if (!strcmp(value, "adda") || !strcmp(value, "add"))
2127 				ptype->looks.blendmode = BM_ADDA;
2128 			else if (!strcmp(value, "addc"))
2129 				ptype->looks.blendmode = BM_ADDC;
2130 			else if (!strcmp(value, "subtract"))
2131 				ptype->looks.blendmode = BM_SUBTRACT;
2132 			else if (!strcmp(value, "invmoda") || !strcmp(value, "invmod"))
2133 				ptype->looks.blendmode = BM_INVMODA;
2134 			else if (!strcmp(value, "invmodc"))
2135 				ptype->looks.blendmode = BM_INVMODC;
2136 			else if (!strcmp(value, "blendcolour") || !strcmp(value, "blendcolor"))
2137 				ptype->looks.blendmode = BM_BLENDCOLOUR;
2138 			else if (!strcmp(value, "blendalpha") || !strcmp(value, "blend"))
2139 				ptype->looks.blendmode = BM_BLEND;
2140 			else if (!strcmp(value, "premul_subtract"))
2141 			{
2142 				ptype->looks.premul = 1;
2143 				ptype->looks.blendmode = BM_INVMODC;
2144 			}
2145 			else if (!strcmp(value, "premul_add"))
2146 			{
2147 				ptype->looks.premul = 2;
2148 				ptype->looks.blendmode = BM_PREMUL;
2149 			}
2150 			else if (!strcmp(value, "premul_blend"))
2151 			{
2152 				ptype->looks.premul = 1;
2153 				ptype->looks.blendmode = BM_PREMUL;
2154 			}
2155 			else
2156 			{
2157 				Con_DPrintf("%s.%s: uses unknown blend type '%s', assuming legacy 'blendalpha'\n", ptype->config, ptype->name, value);
2158 				ptype->looks.blendmode = BM_BLEND;	//fallback
2159 			}
2160 		}
2161 		else if (!strcmp(var, "spawnmode"))
2162 		{
2163 			if (!strcmp(value, "circle"))
2164 				ptype->spawnmode = SM_CIRCLE;
2165 			else if (!strcmp(value, "ball"))
2166 				ptype->spawnmode = SM_BALL;
2167 			else if (!strcmp(value, "spiral"))
2168 				ptype->spawnmode = SM_SPIRAL;
2169 			else if (!strcmp(value, "tracer"))
2170 				ptype->spawnmode = SM_TRACER;
2171 			else if (!strcmp(value, "telebox"))
2172 				ptype->spawnmode = SM_TELEBOX;
2173 			else if (!strcmp(value, "lavasplash"))
2174 				ptype->spawnmode = SM_LAVASPLASH;
2175 			else if (!strcmp(value, "uniformcircle"))
2176 				ptype->spawnmode = SM_UNICIRCLE;
2177 			else if (!strcmp(value, "syncfield"))
2178 			{
2179 				ptype->spawnmode = SM_FIELD;
2180 #ifndef NOLEGACY
2181 				ptype->spawnparam1 = 16;
2182 				ptype->spawnparam2 = 0;
2183 #endif
2184 			}
2185 			else if (!strcmp(value, "distball"))
2186 				ptype->spawnmode = SM_DISTBALL;
2187 			else if (!strcmp(value, "box"))
2188 				ptype->spawnmode = SM_BOX;
2189 			else
2190 			{
2191 				Con_DPrintf("%s.%s: uses unknown spawn type '%s', assuming 'box'\n", ptype->config, ptype->name, value);
2192 				ptype->spawnmode = SM_BOX;
2193 			}
2194 
2195 			if (Cmd_Argc()>2)
2196 			{
2197 				if (Cmd_Argc()>3)
2198 					ptype->spawnparam2 = atof(Cmd_Argv(3));
2199 				ptype->spawnparam1 = atof(Cmd_Argv(2));
2200 			}
2201 		}
2202 		else if (!strcmp(var, "type"))
2203 		{
2204 			if (!strcmp(value, "beam"))
2205 				ptype->looks.type = PT_BEAM;
2206 			else if (!strcmp(value, "spark") || !strcmp(value, "linespark"))
2207 				ptype->looks.type = PT_SPARK;
2208 			else if (!strcmp(value, "sparkfan") || !strcmp(value, "trianglefan"))
2209 				ptype->looks.type = PT_SPARKFAN;
2210 			else if (!strcmp(value, "texturedspark"))
2211 				ptype->looks.type = PT_TEXTUREDSPARK;
2212 			else if (!strcmp(value, "decal") || !strcmp(value, "cdecal"))
2213 				ptype->looks.type = PT_CDECAL;
2214 			else if (!strcmp(value, "udecal"))
2215 				ptype->looks.type = PT_UDECAL;
2216 			else if (!strcmp(value, "normal"))
2217 				ptype->looks.type = PT_NORMAL;
2218 			else
2219 			{
2220 				Con_DPrintf("%s.%s: uses unknown render type '%s', assuming 'normal'\n", ptype->config, ptype->name, value);
2221 				ptype->looks.type = PT_NORMAL;	//fallback
2222 			}
2223 			settype = true;
2224 		}
2225 		else if (!strcmp(var, "clippeddecal"))	//mask, match
2226 		{
2227 			if (Cmd_Argc()>=2)
2228 			{//decal only appears where: (surfflags&mask)==match
2229 				ptype->surfflagmatch = ptype->surfflagmask = strtoul(Cmd_Argv(1), NULL, 0);
2230 				if (Cmd_Argc()>=3)
2231 					ptype->surfflagmatch = strtoul(Cmd_Argv(2), NULL, 0);
2232 			}
2233 			ptype->looks.type = PT_CDECAL;
2234 			settype = true;
2235 		}
2236 #ifndef NOLEGACY
2237 		else if (!strcmp(var, "isbeam"))
2238 		{
2239 			Con_DPrintf("%s.%s: isbeam is deprecated, use type beam\n", ptype->config, ptype->name);
2240 			ptype->looks.type = PT_BEAM;
2241 			settype = true;
2242 		}
2243 #endif
2244 		else if (!strcmp(var, "spawntime"))
2245 			ptype->spawntime = atof(value);
2246 		else if (!strcmp(var, "spawnchance"))
2247 			ptype->spawnchance = atof(value);
2248 		else if (!strcmp(var, "cliptype"))
2249 		{
2250 			assoc = P_AllocateParticleType(config, value);//careful - this can realloc all the particle types
2251 			ptype = &part_type[pnum];
2252 			ptype->cliptype = assoc;
2253 		}
2254 		else if (!strcmp(var, "clipcount"))
2255 			ptype->clipcount = atof(value);
2256 		else if (!strcmp(var, "clipbounce"))
2257 		{
2258 			ptype->clipbounce = atof(value);
2259 			if (ptype->clipbounce < 0 && ptype->cliptype == P_INVALID)
2260 				ptype->cliptype = pnum;
2261 		}
2262 		else if (!strcmp(var, "bounce"))
2263 		{
2264 			ptype->cliptype = pnum;
2265 			ptype->clipbounce = atof(value);
2266 		}
2267 
2268 		else if (!strcmp(var, "emit"))
2269 		{
2270 			assoc = P_AllocateParticleType(config, value);//careful - this can realloc all the particle types
2271 			ptype = &part_type[pnum];
2272 			ptype->emit = assoc;
2273 		}
2274 		else if (!strcmp(var, "emitinterval"))
2275 			ptype->emittime = atof(value);
2276 		else if (!strcmp(var, "emitintervalrand"))
2277 			ptype->emitrand = atof(value);
2278 		else if (!strcmp(var, "emitstart"))
2279 			ptype->emitstart = atof(value);
2280 
2281 #ifndef NOLEGACY
2282 		// old names
2283 		else if (!strcmp(var, "areaspread"))
2284 		{
2285 			Con_DPrintf("%s.%s: areaspread is deprecated, use spawnorg\n", ptype->config, ptype->name);
2286 			ptype->areaspread = atof(value);
2287 		}
2288 		else if (!strcmp(var, "areaspreadvert"))
2289 		{
2290 			Con_DPrintf("%s.%s: areaspreadvert is deprecated, use spawnorg\n", ptype->config, ptype->name);
2291 			ptype->areaspreadvert = atof(value);
2292 		}
2293 		else if (!strcmp(var, "offsetspread"))
2294 		{
2295 			Con_DPrintf("%s.%s: offsetspread is deprecated, use spawnvel\n", ptype->config, ptype->name);
2296 			ptype->spawnvel = atof(value);
2297 		}
2298 		else if (!strcmp(var, "offsetspreadvert"))
2299 		{
2300 			Con_DPrintf("%s.%s: offsetspreadvert is deprecated, use spawnvel\n", ptype->config, ptype->name);
2301 			ptype->spawnvelvert  = atof(value);
2302 		}
2303 #endif
2304 
2305 		// current names
2306 		else if (!strcmp(var, "spawnorg"))
2307 		{
2308 			ptype->areaspreadvert = ptype->areaspread = atof(value);
2309 
2310 			if (Cmd_Argc()>2)
2311 				ptype->areaspreadvert = atof(Cmd_Argv(2));
2312 		}
2313 		else if (!strcmp(var, "spawnvel"))
2314 		{
2315 			ptype->spawnvelvert = ptype->spawnvel = atof(value);
2316 
2317 			if (Cmd_Argc()>2)
2318 				ptype->spawnvelvert = atof(Cmd_Argv(2));
2319 		}
2320 
2321 #ifndef NOLEGACY
2322 		// spawn mode param fields
2323 		else if (!strcmp(var, "spawnparam1"))
2324 		{
2325 			ptype->spawnparam1 = atof(value);
2326 			Con_DPrintf("%s.%s: 'spawnparam1' is deprecated, use 'spawnmode foo X'\n", ptype->config, ptype->name);
2327 		}
2328 		else if (!strcmp(var, "spawnparam2"))
2329 		{
2330 			ptype->spawnparam2 = atof(value);
2331 			Con_DPrintf("%s.%s: 'spawnparam2' is deprecated, use 'spawnmode foo X Y'\n", ptype->config, ptype->name);
2332 		}
2333 /*		else if (!strcmp(var, "spawnparam3"))
2334 			ptype->spawnparam3 = atof(value); */
2335 		else if (!strcmp(var, "up"))
2336 		{
2337 			ptype->orgbias[2] = atof(value);
2338 			Con_DPrintf("%s.%s: up is deprecated, use orgbias 0 0 Z\n", ptype->config, ptype->name);
2339 		}
2340 #endif
2341 
2342 		else if (!strcmp(var, "rampmode"))
2343 		{
2344 			if (!strcmp(value, "none"))
2345 				ptype->rampmode = RAMP_NONE;
2346 #ifndef NOLEGACY
2347 			else if (!strcmp(value, "absolute"))
2348 			{
2349 				Con_DPrintf("%s.%s: 'rampmode absolute' is deprecated, use 'rampmode nearest'\n", ptype->config, ptype->name);
2350 				ptype->rampmode = RAMP_NEAREST;
2351 			}
2352 #endif
2353 			else if (!strcmp(value, "nearest"))
2354 				ptype->rampmode = RAMP_NEAREST;
2355 			else if (!strcmp(value, "lerp"))	//don't use the name 'linear'. ramps are there to avoid linear...
2356 				ptype->rampmode = RAMP_LERP;
2357 			else if (!strcmp(value, "delta"))
2358 				ptype->rampmode = RAMP_DELTA;
2359 			else
2360 			{
2361 				Con_DPrintf("%s.%s: uses unknown ramp mode '%s', assuming 'delta'\n", ptype->config, ptype->name, value);
2362 				ptype->rampmode = RAMP_DELTA;
2363 			}
2364 		}
2365 		else if (!strcmp(var, "rampindexlist"))
2366 		{ // better not use this with delta ramps...
2367 			int cidx, i;
2368 
2369 			i = 1;
2370 			while (i < Cmd_Argc())
2371 			{
2372 				ptype->ramp = Z_Realloc(ptype->ramp, sizeof(ramp_t)*(ptype->rampindexes+1));
2373 
2374 				cidx = atoi(Cmd_Argv(i));
2375 				ptype->ramp[ptype->rampindexes].alpha = cidx > 255 ? 0.5 : 1;
2376 
2377 				cidx = (cidx & 0xff) * 4;
2378 				ptype->ramp[ptype->rampindexes].rgb[0] = palrgba[cidx] * (1/255.0);
2379 				ptype->ramp[ptype->rampindexes].rgb[1] = palrgba[cidx+1] * (1/255.0);
2380 				ptype->ramp[ptype->rampindexes].rgb[2] = palrgba[cidx+2] * (1/255.0);
2381 
2382 				ptype->ramp[ptype->rampindexes].scale = ptype->scale;
2383 
2384 				ptype->rampindexes++;
2385 				i++;
2386 			}
2387 		}
2388 		else if (!strcmp(var, "rampindex"))
2389 		{
2390 			int cidx;
2391 			ptype->ramp = Z_Realloc(ptype->ramp, sizeof(ramp_t)*(ptype->rampindexes+1));
2392 
2393 			cidx = atoi(value);
2394 			ptype->ramp[ptype->rampindexes].alpha = cidx > 255 ? 0.5 : 1;
2395 
2396 			if (Cmd_Argc() > 2) // they gave alpha
2397 				ptype->ramp[ptype->rampindexes].alpha *= atof(Cmd_Argv(2));
2398 
2399 			cidx = (cidx & 0xff) * 4;
2400 			ptype->ramp[ptype->rampindexes].rgb[0] = palrgba[cidx] * (1/255.0);
2401 			ptype->ramp[ptype->rampindexes].rgb[1] = palrgba[cidx+1] * (1/255.0);
2402 			ptype->ramp[ptype->rampindexes].rgb[2] = palrgba[cidx+2] * (1/255.0);
2403 
2404 			if (Cmd_Argc() > 3) // they gave scale
2405 				ptype->ramp[ptype->rampindexes].scale = atof(Cmd_Argv(3));
2406 			else
2407 				ptype->ramp[ptype->rampindexes].scale = ptype->scale;
2408 
2409 
2410 			ptype->rampindexes++;
2411 		}
2412 		else if (!strcmp(var, "ramp"))
2413 		{
2414 			ptype->ramp = Z_Realloc(ptype->ramp, sizeof(ramp_t)*(ptype->rampindexes+1));
2415 
2416 			ptype->ramp[ptype->rampindexes].rgb[0] = atof(value)/255;
2417 			if (Cmd_Argc()>3)	//seperate rgb
2418 			{
2419 				ptype->ramp[ptype->rampindexes].rgb[1] = atof(Cmd_Argv(2))/255;
2420 				ptype->ramp[ptype->rampindexes].rgb[2] = atof(Cmd_Argv(3))/255;
2421 
2422 				if (Cmd_Argc()>4)	//have we alpha and scale changes?
2423 				{
2424 					ptype->ramp[ptype->rampindexes].alpha = atof(Cmd_Argv(4));
2425 					if (Cmd_Argc()>5)	//have we scale changes?
2426 						ptype->ramp[ptype->rampindexes].scale = atof(Cmd_Argv(5));
2427 					else
2428 						ptype->ramp[ptype->rampindexes].scale = ptype->scaledelta;
2429 				}
2430 				else
2431 				{
2432 					ptype->ramp[ptype->rampindexes].alpha = ptype->alpha;
2433 					ptype->ramp[ptype->rampindexes].scale = ptype->scaledelta;
2434 				}
2435 			}
2436 			else	//they only gave one value
2437 			{
2438 				ptype->ramp[ptype->rampindexes].rgb[1] = ptype->ramp[ptype->rampindexes].rgb[0];
2439 				ptype->ramp[ptype->rampindexes].rgb[2] = ptype->ramp[ptype->rampindexes].rgb[0];
2440 
2441 				ptype->ramp[ptype->rampindexes].alpha = ptype->alpha;
2442 				ptype->ramp[ptype->rampindexes].scale = ptype->scaledelta;
2443 			}
2444 
2445 			ptype->rampindexes++;
2446 		}
2447 		else if (!strcmp(var, "viewspace"))
2448 		{
2449 			Con_DPrintf("%s.%s: viewspace particles are not supported in this build\n", ptype->config, ptype->name);
2450 		}
2451 		else if (!strcmp(var, "perframe"))
2452 			ptype->flags |= PT_INVFRAMETIME;
2453 		else if (!strcmp(var, "averageout"))
2454 			ptype->flags |= PT_AVERAGETRAIL;
2455 		else if (!strcmp(var, "nostate"))
2456 			ptype->flags |= PT_NOSTATE;
2457 		else if (!strcmp(var, "nospreadfirst"))
2458 			ptype->flags |= PT_NOSPREADFIRST;
2459 		else if (!strcmp(var, "nospreadlast"))
2460 			ptype->flags |= PT_NOSPREADLAST;
2461 
2462 		else if (!strcmp(var, "lightradius"))
2463 		{	//float version
2464 			ptype->dl_radius[0] = ptype->dl_radius[1] = atof(value);
2465 			if (Cmd_Argc()>2)
2466 				ptype->dl_radius[1] = atof(Cmd_Argv(2));
2467 			ptype->dl_radius[1] -= ptype->dl_radius[0];
2468 		}
2469 		else if (!strcmp(var, "lightradiusfade"))
2470 			ptype->dl_decay[3] = atof(value);
2471 		else if (!strcmp(var, "lightrgb"))
2472 		{
2473 			ptype->dl_rgb[0] = atof(value);
2474 			ptype->dl_rgb[1] = atof(Cmd_Argv(2));
2475 			ptype->dl_rgb[2] = atof(Cmd_Argv(3));
2476 		}
2477 		else if (!strcmp(var, "lightrgbfade"))
2478 		{
2479 			ptype->dl_decay[0] = atof(value);
2480 			ptype->dl_decay[1] = atof(Cmd_Argv(2));
2481 			ptype->dl_decay[2] = atof(Cmd_Argv(3));
2482 		}
2483 		else if (!strcmp(var, "lightcorona"))
2484 		{
2485 			ptype->dl_corona_intensity = atof(value);
2486 			ptype->dl_corona_scale = atof(Cmd_Argv(2));
2487 		}
2488 		else if (!strcmp(var, "lighttime"))
2489 			ptype->dl_time = atof(value);
2490 		else if (!strcmp(var, "lightshadows"))
2491 			ptype->flags = (ptype->flags & ~PT_NODLSHADOW) | (atof(value)?0:PT_NODLSHADOW);
2492 		else if (!strcmp(var, "lightcubemap"))
2493 			ptype->dl_cubemapnum = atoi(value);
2494 		else if (!strcmp(var, "lightscales"))
2495 		{	//ambient diffuse specular
2496 			ptype->dl_scales[0] = atof(value);
2497 			ptype->dl_scales[1] = atof(Cmd_Argv(2));
2498 			ptype->dl_scales[2] = atof(Cmd_Argv(3));
2499 		}
2500 		else if (!strcmp(var, "spawnstain"))
2501 		{
2502 			Con_DPrintf("%s.%s: spawnstain is not supported in this build\n", ptype->config, ptype->name);
2503 		}
2504 		else if (Cmd_Argc())
2505 			Con_DPrintf("%s.%s: %s is not a recognised particle type field\n", ptype->config, ptype->name, var);
2506 	}
2507 	ptype->loaded = part_parseweak?1:2;
2508 	if (ptype->clipcount < 1)
2509 		ptype->clipcount = 1;
2510 
2511 	if (!settype)
2512 	{
2513 		if (ptype->looks.type == PT_NORMAL && !*ptype->texname)
2514 		{
2515 			if (ptype->scale)
2516 			{
2517 				ptype->looks.type = PT_SPARKFAN;
2518 				Con_DPrintf("%s.%s: effect lacks a texture. assuming type sparkfan.\n", ptype->config, ptype->name);
2519 			}
2520 			else
2521 			{
2522 				ptype->looks.type = PT_SPARK;
2523 				Con_DPrintf("%s.%s: effect lacks a texture. assuming type spark.\n", ptype->config, ptype->name);
2524 			}
2525 		}
2526 		else if (ptype->looks.type == PT_SPARK)
2527 		{
2528 			if (*ptype->texname)
2529 				ptype->looks.type = PT_TEXTUREDSPARK;
2530 			else if (ptype->scale)
2531 				ptype->looks.type = PT_SPARKFAN;
2532 		}
2533 	}
2534 
2535 	// use old behavior if not using alphadelta
2536 	if (!setalphadelta)
2537 		ptype->alphachange = (-ptype->alphachange / ptype->die) * ptype->alpha;
2538 
2539 	FinishParticleType(ptype);
2540 
2541 	if (ptype->looks.type == PT_BEAM && !setbeamlen)
2542 		ptype->rotationstartmin = 1/128.0;
2543 
2544 	goto nexteffect;
2545 }
2546 
2547 #if 1//_DEBUG
2548 // R_BeamInfo_f - debug junk
P_BeamInfo_f(void)2549 static void P_BeamInfo_f (void)
2550 {
2551 	beamseg_t *bs;
2552 	int i, j, k, l, m;
2553 
2554 	i = 0;
2555 
2556 	for (bs = free_beams; bs; bs = bs->next)
2557 		i++;
2558 
2559 	Con_Printf("%i free beams\n", i);
2560 
2561 	for (i = 0; i < numparticletypes; i++)
2562 	{
2563 		m = l = k = j = 0;
2564 		for (bs = part_type[i].beams; bs; bs = bs->next)
2565 		{
2566 			if (!bs->p)
2567 				k++;
2568 
2569 			if (bs->flags & BS_DEAD)
2570 				l++;
2571 
2572 			if (bs->flags & BS_LASTSEG)
2573 				m++;
2574 
2575 			j++;
2576 		}
2577 
2578 		if (j)
2579 			Con_Printf("Type %i = %i NULL p, %i DEAD, %i LASTSEG, %i total\n", i, k, l, m, j);
2580 	}
2581 }
2582 
P_PartInfo_f(void)2583 static void P_PartInfo_f (void)
2584 {
2585 	particle_t *p;
2586 	clippeddecal_t *d;
2587 	part_type_t *ptype;
2588 	int totalp = 0, totald = 0, freep, freed, runningp=0, runningd=0, runninge=0, runningt=0;
2589 
2590 	int i, j, k;
2591 
2592 	Con_DPrintf("Full list of  effects:\n");
2593 	for (i = 0; i < numparticletypes; i++)
2594 	{
2595 		j = 0;
2596 		for (p = part_type[i].particles; p; p = p->next)
2597 			j++;
2598 		totalp += j;
2599 
2600 		k = 0;
2601 		for (d = part_type[i].clippeddecals; d; d = d->next)
2602 			k++;
2603 		totald += k;
2604 
2605 		if (j||k)
2606 		{
2607 			Con_DPrintf("Type %s.%s = %i+%i total\n", part_type[i].config, part_type[i].name, j,k);
2608 			if (!(part_type[i].state & PS_INRUNLIST))
2609 				Con_Printf(CON_WARNING "%s.%s NOT RUNNING\n", part_type[i].config, part_type[i].name);
2610 		}
2611 	}
2612 
2613 	Con_Printf("Running effects:\n");
2614 	// maintain run list
2615 	for (ptype = part_run_list; ptype; ptype = ptype->nexttorun)
2616 	{
2617 		Con_Printf("Type %s.%s", ptype->config, ptype->name);
2618 
2619 		j = 0;
2620 		for (p = ptype->particles; p; p = p->next)
2621 			j++;
2622 		if (j)
2623 		{
2624 			Con_Printf("\t%i particles", j);
2625 			if (ptype->cliptype >= 0 || ptype->stainonimpact)
2626 			{
2627 				Con_Printf("(+traceline)");
2628 				runningt += j;
2629 			}
2630 		}
2631 		runningp += j;
2632 
2633 		k = 0;
2634 		for (d = ptype->clippeddecals; d; d = d->next)
2635 			k++;
2636 		if (k)
2637 			Con_Printf("%s%i decals", ptype->particles?", ":"\t", k);
2638 		runningd += k;
2639 
2640 		Con_Printf("\n");
2641 		runninge++;
2642 	}
2643 	Con_Printf("End of list\n");
2644 
2645 	for (p = free_particles, freep = 0; p; p = p->next)
2646 		freep++;
2647 	for (d = free_decals, freed = 0; d; d = d->next)
2648 		freed++;
2649 
2650 	Con_DPrintf("%i running effects.\n", runninge);
2651 	Con_Printf("%i particles, %i free, %i traces.\n", runningp, freep, runningt);
2652 	Con_Printf("%i decals, %i free.\n", runningd, freed);
2653 
2654 	if (totalp != runningp)
2655 		Con_Printf("%i particles unaccounted for\n", totalp - runningp);
2656 	if (totald != runningd)
2657 		Con_Printf("%i decals unaccounted for\n", totald - runningd);
2658 }
2659 #endif
2660 
FinishParticleType(part_type_t * ptype)2661 static void FinishParticleType(part_type_t *ptype)
2662 {
2663 	//if there is a chance that it moves
2664 	if (ptype->gravity || ptype->veladd || ptype->spawnvel || ptype->spawnvelvert || DotProduct(ptype->velwrand,ptype->velwrand) || DotProduct(ptype->velbias,ptype->velbias) || ptype->flurry)
2665 		ptype->flags |= PT_VELOCITY;
2666 	if (DotProduct(ptype->velbias,ptype->velbias) || DotProduct(ptype->velwrand,ptype->velwrand) || DotProduct(ptype->orgwrand,ptype->orgwrand))
2667 		ptype->flags |= PT_WORLDSPACERAND;
2668 	//if it has friction
2669 	if (ptype->friction[0] || ptype->friction[1] || ptype->friction[2])
2670 		ptype->flags |= PT_FRICTION;
2671 
2672 	P_LoadTexture(ptype, true);
2673 	if (ptype->dl_decay[3] && !ptype->dl_time)
2674 		ptype->dl_time = ptype->dl_radius[0] / ptype->dl_decay[3];
2675 	if (ptype->looks.scalefactor > 1 && !ptype->looks.invscalefactor)
2676 	{
2677 		ptype->scale *= ptype->looks.scalefactor;
2678 		ptype->scalerand *= ptype->looks.scalefactor;
2679 		/*too lazy to go through ramps*/
2680 		ptype->looks.scalefactor = 1;
2681 	}
2682 	ptype->looks.invscalefactor = 1-ptype->looks.scalefactor;
2683 
2684 	if (ptype->looks.type == PT_TEXTUREDSPARK && !ptype->looks.stretch)
2685 		ptype->looks.stretch = 0.05;	//the old default.
2686 
2687 	if (ptype->looks.type == PT_SPARK && r_part_sparks.value<0)
2688 		ptype->looks.type = PT_INVISIBLE;
2689 	if (ptype->looks.type == PT_TEXTUREDSPARK && !r_part_sparks_textured.value)
2690 		ptype->looks.type = PT_SPARK;
2691 	if (ptype->looks.type == PT_SPARKFAN && !r_part_sparks_trifan.value)
2692 		ptype->looks.type = PT_SPARK;
2693 	if (ptype->looks.type == PT_SPARK && !r_part_sparks.value)
2694 		ptype->looks.type = PT_INVISIBLE;
2695 	if (ptype->looks.type == PT_BEAM && r_part_beams.value <= 0)
2696 		ptype->looks.type = PT_INVISIBLE;
2697 
2698 	if (ptype->rampmode && !ptype->ramp)
2699 	{
2700 		ptype->rampmode = RAMP_NONE;
2701 		Con_Printf("%s.%s: Particle has a ramp mode but no ramp\n", ptype->config, ptype->name);
2702 	}
2703 	else if (ptype->ramp && !ptype->rampmode)
2704 	{
2705 		Con_Printf("%s.%s: Particle has a ramp but no ramp mode\n", ptype->config, ptype->name);
2706 	}
2707 	r_plooksdirty = true;
2708 }
2709 
2710 #ifdef PSET_SCRIPT_EFFECTINFO
FinishEffectinfoParticleType(part_type_t * ptype,qboolean blooddecalonimpact)2711 static void FinishEffectinfoParticleType(part_type_t *ptype, qboolean blooddecalonimpact)
2712 {
2713 	if (ptype->looks.type == PT_CDECAL)
2714 	{
2715 		if (ptype->die == 9999)
2716 			ptype->die = 20;
2717 		ptype->alphachange = -(ptype->alpha / ptype->die);
2718 	}
2719 	else if (ptype->looks.type == PT_UDECAL)
2720 	{
2721 		//dp's decals have a size as a radius. fte's udecals are 'just' quads.
2722 		//also, dp uses 'stretch'.
2723 		ptype->looks.stretch *= 1/1.414213562373095;
2724 		ptype->scale *= ptype->looks.stretch;
2725 		ptype->scalerand *= ptype->looks.stretch;
2726 		ptype->scaledelta *= ptype->looks.stretch;
2727 		ptype->looks.stretch = 1;
2728 	}
2729 	else if (ptype->looks.type == PT_NORMAL)
2730 	{
2731 		//fte's textured particles are *0.25 for some reason.
2732 		//but fte also uses radiuses, while dp uses total size so we only need to double it here..
2733 		ptype->scale *= 2*ptype->looks.stretch;
2734 		ptype->scalerand *= 2*ptype->looks.stretch;
2735 		ptype->scaledelta *= 2*2*ptype->looks.stretch;
2736 		ptype->looks.stretch = 1;
2737 	}
2738 	if (blooddecalonimpact)	//DP blood particles generate decals unconditionally (and prevent blood from bouncing)
2739 		ptype->clipbounce = -2;
2740 	if (ptype->looks.type == PT_TEXTUREDSPARK)
2741 	{
2742 		ptype->looks.stretch *= 0.04;
2743 		if (ptype->looks.stretch < 0)
2744 			ptype->looks.stretch = 0.000001;
2745 	}
2746 
2747 	if (ptype->die == 9999)	//internal: means unspecified.
2748 	{
2749 		if (ptype->alphachange)
2750 			ptype->die = (ptype->alpha+ptype->alpharand)/-ptype->alphachange;
2751 		else
2752 			ptype->die = 15;
2753 	}
2754 	ptype->looks.minstretch = 0.5;
2755 	FinishParticleType(ptype);
2756 }
P_ImportEffectInfo(const char * config,char * line,qboolean part_parseweak)2757 static void P_ImportEffectInfo(const char *config, char *line, qboolean part_parseweak)
2758 {
2759 	part_type_t *ptype = NULL;
2760 	int parenttype;
2761 	char arg[8][1024];
2762 	unsigned int args = 0;
2763 	qboolean blooddecalonimpact = false;	//tracked separately because it needs to override another field
2764 
2765 	float teximages[256][4];
2766 
2767 	{
2768 		int i;
2769 		char *file;
2770 		const char *font_line;
2771 		char linebuf[1024];
2772 		//default assumes 8*8 grid, but we allow more
2773 		for (i = 0; i < 256; i++)
2774 		{
2775 			teximages[i][0] = 1/8.0 * (i & 7);
2776 			teximages[i][1] = 1/8.0 * (1+(i & 7));
2777 			teximages[i][2] = 1/8.0 * (1+(i>>3));
2778 			teximages[i][3] = 1/8.0 * (i>>3);
2779 		}
2780 
2781 		file = (char*)COM_LoadMallocFile("particles/particlefont.txt", NULL);
2782 		if (file)
2783 		{
2784 			size_t offset = 0;
2785 			while (PScript_ReadLine(linebuf, sizeof(linebuf), file, com_filesize, &offset))
2786 			{
2787 				float s1,s2,t1,t2;
2788 				font_line = COM_Parse(linebuf);
2789 				i = atoi(com_token);
2790 				font_line = COM_Parse(font_line);
2791 				s1 = atof(com_token);
2792 				font_line = COM_Parse(font_line);
2793 				t1 = atof(com_token);
2794 				font_line = COM_Parse(font_line);
2795 				s2 = atof(com_token);
2796 				font_line = COM_Parse(font_line);
2797 				t2 = atof(com_token);
2798 				if (font_line)
2799 				{
2800 					teximages[i][0] = s1;
2801 					teximages[i][1] = s2;
2802 					teximages[i][2] = t2;
2803 					teximages[i][3] = t1;
2804 				}
2805 			}
2806 			free(file);
2807 		}
2808 	}
2809 
2810 	for (;line && *line;)
2811 	{
2812 		char *eol;
2813 
2814 		//multi-line comments need special handling.
2815 		while (*line == ' ' || *line == '\t')
2816 			line++;
2817 		if (line[0] == '/' && line[1] == '*')
2818 		{
2819 			line += 2;
2820 			while (*line)
2821 			{
2822 				if (line[0] == '*' && line[1] == '/')
2823 				{
2824 					line+=2;
2825 					break;
2826 				}
2827 				line++;
2828 			}
2829 			continue;
2830 		}
2831 
2832 		eol = strchr(line, '\n');
2833 		args = 0;
2834 		if (eol)
2835 			*eol++=0;
2836 		for (args = 0; line; )
2837 		{
2838 			line = (char*)COM_Parse(line);
2839 			if (line && args < sizeof(arg)/sizeof(arg[args]))
2840 			{
2841 				q_strlcpy(arg[args], com_token, sizeof(arg[args]));
2842 				args++;
2843 			}
2844 		}
2845 		line = eol;
2846 
2847 		if (args <= 0)
2848 			continue;
2849 
2850 		if (!strcmp(arg[0], "effect"))
2851 		{
2852 			char newname[64];
2853 			int i;
2854 
2855 			if (ptype)
2856 				FinishEffectinfoParticleType(ptype, blooddecalonimpact);
2857 			blooddecalonimpact = false;
2858 
2859 			ptype = P_GetParticleType(config, arg[1]);
2860 			if (ptype->loaded)
2861 			{
2862 				for (i = 0; i < 64; i++)
2863 				{
2864 					parenttype = ptype - part_type;
2865 					q_snprintf(newname, sizeof(newname), "%i+%s", i, arg[1]);
2866 					ptype = P_GetParticleType(config, newname);
2867 					if (!ptype->loaded)
2868 					{
2869 						part_type[parenttype].assoc = ptype - part_type;
2870 						break;
2871 					}
2872 				}
2873 				if (i == 64)
2874 				{
2875 					Con_Printf("Too many duplicate names, gave up\n");
2876 					break;
2877 				}
2878 			}
2879 			P_ResetToDefaults(ptype);
2880 			ptype->loaded = part_parseweak?1:2;
2881 			ptype->scale = 1;
2882 			ptype->alpha = 0;
2883 			ptype->alpharand = 1;
2884 			ptype->alphachange = -1;
2885 			ptype->die = 9999;
2886 			strcpy(ptype->texname, "particles/particlefont");
2887 			ptype->rgb[0] = 1;
2888 			ptype->rgb[1] = 1;
2889 			ptype->rgb[2] = 1;
2890 
2891 //			ptype->spawnmode = SM_BALL;
2892 
2893 			ptype->colorindex = -1;
2894 			ptype->spawnchance = 1;
2895 			ptype->looks.scalefactor = 2;
2896 			ptype->looks.invscalefactor = 0;
2897 			ptype->looks.type = PT_NORMAL;
2898 			ptype->looks.blendmode = BM_PREMUL;
2899 			ptype->looks.premul = 1;
2900 			ptype->looks.stretch = 1;
2901 
2902 			ptype->dl_time = 0;
2903 
2904 			i = 63; //default texture is 63.
2905 			ptype->s1 = teximages[i][0];
2906 			ptype->s2 = teximages[i][1];
2907 			ptype->t1 = teximages[i][2];
2908 			ptype->t2 = teximages[i][3];
2909 			ptype->texsstride = 0;
2910 			ptype->randsmax = 1;
2911 		}
2912 		else if (!ptype)
2913 		{
2914 			Con_Printf("Bad effectinfo file\n");
2915 			break;
2916 		}
2917 		else if (!strcmp(arg[0], "countabsolute") && args == 2)
2918 			ptype->countextra = atof(arg[1]);
2919 		else if (!strcmp(arg[0], "count") && args == 2)
2920 			ptype->count = atof(arg[1]);
2921 		else if (!strcmp(arg[0], "type") && args == 2)
2922 		{
2923 			if (!strcmp(arg[1], "decal") || !strcmp(arg[1], "cdecal"))
2924 			{
2925 				ptype->looks.type = PT_CDECAL;
2926 				ptype->looks.blendmode = BM_INVMODC;
2927 				ptype->looks.premul = 2;
2928 			}
2929 			else if (!strcmp(arg[1], "udecal"))
2930 			{
2931 				ptype->looks.type = PT_UDECAL;
2932 				ptype->looks.blendmode = BM_INVMODC;
2933 				ptype->looks.premul = 2;
2934 			}
2935 			else if (!strcmp(arg[1], "alphastatic"))
2936 			{
2937 				ptype->looks.type = PT_NORMAL;
2938 				ptype->looks.blendmode = BM_PREMUL;//BM_BLEND;
2939 				ptype->looks.premul = 1;
2940 			}
2941 			else if (!strcmp(arg[1], "static"))
2942 			{
2943 				ptype->looks.type = PT_NORMAL;
2944 				ptype->looks.blendmode = BM_PREMUL;//BM_ADDA;
2945 				ptype->looks.premul = 2;
2946 			}
2947 			else if (!strcmp(arg[1], "smoke"))
2948 			{
2949 				ptype->looks.type = PT_NORMAL;
2950 				ptype->looks.blendmode = BM_PREMUL;//BM_ADDA;
2951 				ptype->looks.premul = 2;
2952 			}
2953 			else if (!strcmp(arg[1], "spark"))
2954 			{
2955 				ptype->looks.type = PT_TEXTUREDSPARK;
2956 				ptype->looks.blendmode = BM_PREMUL;//BM_ADDA;
2957 				ptype->looks.premul = 2;
2958 			}
2959 			else if (!strcmp(arg[1], "bubble"))
2960 			{
2961 				ptype->looks.type = PT_NORMAL;
2962 				ptype->looks.blendmode = BM_PREMUL;//BM_ADDA;
2963 				ptype->looks.premul = 2;
2964 			}
2965 			else if (!strcmp(arg[1], "blood"))
2966 			{
2967 				ptype->looks.type = PT_NORMAL;
2968 				ptype->looks.blendmode = BM_INVMODC;
2969 				ptype->looks.premul = 2;
2970 				ptype->gravity = 800*1;
2971 				blooddecalonimpact = true;
2972 			}
2973 			else if (!strcmp(arg[1], "beam"))
2974 			{
2975 				ptype->looks.type = PT_BEAM;
2976 				ptype->looks.blendmode = BM_PREMUL;//BM_ADDA;
2977 				ptype->looks.premul = 2;
2978 			}
2979 			else if (!strcmp(arg[1], "snow"))
2980 			{
2981 				ptype->looks.type = PT_NORMAL;
2982 				ptype->looks.blendmode = BM_PREMUL;//BM_ADDA;
2983 				ptype->looks.premul = 2;
2984 				ptype->flurry = 32;	//may not still be valid later, but at least it would be an obvious issue with the original.
2985 			}
2986 			else
2987 			{
2988 				Con_Printf("effectinfo type %s not supported\n", arg[1]);
2989 			}
2990 		}
2991 		else if (!strcmp(arg[0], "tex") && args == 3)
2992 		{
2993 			int mini = atoi(arg[1]);
2994 			int maxi = atoi(arg[2]);
2995 			ptype->s1 = teximages[mini][0];
2996 			ptype->s2 = teximages[mini][1];
2997 			ptype->t1 = teximages[mini][2];
2998 			ptype->t2 = teximages[mini][3];
2999 			ptype->texsstride = teximages[(mini+1)&(sizeof(teximages)/sizeof(teximages[0])-1)][0] - teximages[mini][0];
3000 			ptype->randsmax = (maxi - mini);
3001 			if (ptype->randsmax < 1)
3002 				ptype->randsmax = 1;
3003 		}
3004 		else if (!strcmp(arg[0], "size") && args == 3)
3005 		{
3006 			float s1 = atof(arg[1]), s2 = atof(arg[2]);
3007 			ptype->scale = s1;
3008 			ptype->scalerand = (s2-s1);
3009 		}
3010 		else if (!strcmp(arg[0], "sizeincrease") && args == 2)
3011 			ptype->scaledelta = atof(arg[1]);
3012 		else if (!strcmp(arg[0], "color") && args == 3)
3013 		{
3014 			unsigned int rgb1 = strtoul(arg[1], NULL, 0), rgb2 = strtoul(arg[2], NULL, 0);
3015 			int i;
3016 			for (i = 0; i < 3; i++)
3017 			{
3018 				ptype->rgb[i] = ((rgb1>>(16-i*8)) & 0xff)/255.0;
3019 				ptype->rgbrand[i] = (int)(((rgb2>>(16-i*8)) & 0xff) - ((rgb1>>(16-i*8)) & 0xff))/255.0;
3020 				ptype->rgbrandsync[i] = 1;
3021 			}
3022 		}
3023 		else if (!strcmp(arg[0], "alpha") && args == 4)
3024 		{
3025 			float a1 = atof(arg[1]), a2 = atof(arg[2]), f = atof(arg[3]);
3026 			if (a1 > a2)
3027 			{	//backwards
3028 				ptype->alpha = a2/256;
3029 				ptype->alpharand = (a1-a2)/256;
3030 			}
3031 			else
3032 			{
3033 				ptype->alpha = a1/256;
3034 				ptype->alpharand = (a2-a1)/256;
3035 			}
3036 			ptype->alphachange = -f/256;
3037 		}
3038 		else if (!strcmp(arg[0], "velocityoffset") && args == 4)
3039 		{	/*a 3d world-coord addition*/
3040 			ptype->velbias[0] = atof(arg[1]);
3041 			ptype->velbias[1] = atof(arg[2]);
3042 			ptype->velbias[2] = atof(arg[3]);
3043 		}
3044 		else if (!strcmp(arg[0], "velocityjitter") && args == 4)
3045 		{
3046 			ptype->velwrand[0] = atof(arg[1]);
3047 			ptype->velwrand[1] = atof(arg[2]);
3048 			ptype->velwrand[2] = atof(arg[3]);
3049 		}
3050 		else if (!strcmp(arg[0], "originoffset") && args == 4)
3051 		{	/*a 3d world-coord addition*/
3052 			ptype->orgbias[0] = atof(arg[1]);
3053 			ptype->orgbias[1] = atof(arg[2]);
3054 			ptype->orgbias[2] = atof(arg[3]);
3055 		}
3056 		else if (!strcmp(arg[0], "originjitter") && args == 4)
3057 		{
3058 			ptype->orgwrand[0] = atof(arg[1]);
3059 			ptype->orgwrand[1] = atof(arg[2]);
3060 			ptype->orgwrand[2] = atof(arg[3]);
3061 		}
3062 		else if (!strcmp(arg[0], "gravity") && args == 2)
3063 		{
3064 			ptype->gravity = 800*atof(arg[1]);
3065 		}
3066 		else if (!strcmp(arg[0], "bounce") && args == 2)
3067 		{
3068 			ptype->clipbounce = atof(arg[1]);
3069 			if (ptype->clipbounce < 0)
3070 				ptype->cliptype = ptype - part_type;
3071 		}
3072 		else if (!strcmp(arg[0], "airfriction") && args == 2)
3073 			ptype->friction[2] = ptype->friction[1] = ptype->friction[0] = atof(arg[1]);
3074 		else if (!strcmp(arg[0], "liquidfriction") && args == 2)
3075 			;
3076 		else if (!strcmp(arg[0], "underwater") && args == 1)
3077 			ptype->flags |= PT_TRUNDERWATER;
3078 		else if (!strcmp(arg[0], "notunderwater") && args == 1)
3079 			ptype->flags |= PT_TROVERWATER;
3080 		else if (!strcmp(arg[0], "velocitymultiplier") && args == 2)
3081 			ptype->veladd = atof(arg[1]);
3082 		else if (!strcmp(arg[0], "trailspacing") && args == 2)
3083 		{
3084 			ptype->countspacing = atof(arg[1]);
3085 			ptype->count = 1 / ptype->countspacing;
3086 		}
3087 		else if (!strcmp(arg[0], "time") && args == 3)
3088 		{
3089 			ptype->die = atof(arg[1]);
3090 			ptype->randdie = atof(arg[2]) - ptype->die;
3091 			if (ptype->randdie < 0)
3092 			{
3093 				ptype->die = atof(arg[2]);
3094 				ptype->randdie = atof(arg[1]) - ptype->die;
3095 			}
3096 		}
3097 		else if (!strcmp(arg[0], "stretchfactor") && args == 2)
3098 			ptype->looks.stretch = atof(arg[1]);
3099 		else if (!strcmp(arg[0], "blend") && args == 2)
3100 		{
3101 			if (!strcmp(arg[1], "invmod"))
3102 			{
3103 				ptype->looks.blendmode = BM_INVMODC;
3104 				ptype->looks.premul = 2;
3105 			}
3106 			else if (!strcmp(arg[1], "alpha"))
3107 			{
3108 				ptype->looks.blendmode = BM_PREMUL;
3109 				ptype->looks.premul = 1;
3110 			}
3111 			else if (!strcmp(arg[1], "add"))
3112 			{
3113 				ptype->looks.blendmode = BM_PREMUL;
3114 				ptype->looks.premul = 2;
3115 			}
3116 			else
3117 				Con_Printf("effectinfo 'blend %s' not supported\n", arg[1]);
3118 		}
3119 		else if (!strcmp(arg[0], "orientation") && args == 2)
3120 		{
3121 			if (!strcmp(arg[1], "billboard"))
3122 				ptype->looks.type = PT_NORMAL;
3123 			else if (!strcmp(arg[1], "spark"))
3124 				ptype->looks.type = PT_TEXTUREDSPARK;
3125 			else if (!strcmp(arg[1], "oriented"))	//FIXME: not sure this points the right way. also, its double-sided in dp.
3126 			{
3127 				if (ptype->looks.type != PT_CDECAL)
3128 					ptype->looks.type = PT_UDECAL;
3129 			}
3130 			else if (!strcmp(arg[1], "beam"))
3131 				ptype->looks.type = PT_BEAM;
3132 			else
3133 				Con_Printf("effectinfo 'orientation %s' not supported\n", arg[1]);
3134 		}
3135 		else if (!strcmp(arg[0], "lightradius") && args == 2)
3136 		{
3137 			ptype->dl_radius[0] = atof(arg[1]);
3138 			ptype->dl_radius[1] = 0;
3139 		}
3140 		else if (!strcmp(arg[0], "lightradiusfade") && args == 2)
3141 			ptype->dl_decay[3] = atof(arg[1]);
3142 		else if (!strcmp(arg[0], "lightcolor") && args == 4)
3143 		{
3144 			ptype->dl_rgb[0] = atof(arg[1]);
3145 			ptype->dl_rgb[1] = atof(arg[2]);
3146 			ptype->dl_rgb[2] = atof(arg[3]);
3147 		}
3148 		else if (!strcmp(arg[0], "lighttime") && args == 2)
3149 			ptype->dl_time = atof(arg[1]);
3150 		else if (!strcmp(arg[0], "lightshadow") && args == 2)
3151 			ptype->flags = (ptype->flags & ~PT_NODLSHADOW) | (!atoi(arg[1])?PT_NODLSHADOW:0);
3152 		else if (!strcmp(arg[0], "lightcubemapnum") && args == 2)
3153 			ptype->dl_cubemapnum = atoi(arg[1]);
3154 		else if (!strcmp(arg[0], "lightcorona") && args == 3)
3155 		{
3156 			ptype->dl_corona_intensity = atof(arg[1])*0.25;	//dp scales them by 0.25
3157 			ptype->dl_corona_scale = atof(arg[2]);
3158 		}
3159 #if 1
3160 		else if (!strcmp(arg[0], "staincolor") && args == 3)	//stainmaps multiplier
3161 			Con_DPrintf2("Particle effect token %s not supported\n", arg[0]);
3162 		else if (!strcmp(arg[0], "stainalpha") && args == 3)	//affects stainmaps AND stain-decals.
3163 			Con_DPrintf2("Particle effect token %s not supported\n", arg[0]);
3164 		else if (!strcmp(arg[0], "stainsize") && args == 3)		//affects stainmaps AND stain-decals.
3165 			Con_DPrintf2("Particle effect token %s not supported\n", arg[0]);
3166 		else if (!strcmp(arg[0], "staintex") && args == 3)		//actually spawns a decal
3167 			Con_DPrintf2("Particle effect token %s not supported\n", arg[0]);
3168 		else if (!strcmp(arg[0], "stainless") && args == 2)
3169 			Con_DPrintf2("Particle effect token %s not supported\n", arg[0]);
3170 #endif
3171 		else if (!strcmp(arg[0], "rotate") && args == 5)
3172 		{
3173 			ptype->rotationstartmin		= atof(arg[1]);
3174 			ptype->rotationstartrand	= atof(arg[2]) - ptype->rotationstartmin;
3175 			ptype->rotationmin			= atof(arg[3]);
3176 			ptype->rotationrand			= atof(arg[4]) - ptype->rotationmin;
3177 			ptype->rotationstartmin		*= M_PI/180;
3178 			ptype->rotationstartrand	*= M_PI/180;
3179 			ptype->rotationmin			*= M_PI/180;
3180 			ptype->rotationrand			*= M_PI/180;
3181 			ptype->rotationstartmin += M_PI/4;
3182 		}
3183 		else
3184 			Con_Printf("Particle effect token not recognised, or invalid args: %s %s %s %s %s %s\n", arg[0], args<2?"":arg[1], args<3?"":arg[2], args<4?"":arg[3], args<5?"":arg[4], args<6?"":arg[5]);
3185 		args = 0;
3186 	}
3187 
3188 	if (ptype)
3189 		FinishEffectinfoParticleType(ptype, blooddecalonimpact);
3190 
3191 	r_plooksdirty = true;
3192 }
3193 
P_ImportEffectInfo_Name(char * config)3194 static qboolean P_ImportEffectInfo_Name(char *config)
3195 {
3196 	char *file;
3197 
3198 	file = (char*)COM_LoadMallocFile(va("%s.txt", config), NULL);
3199 	if (!file)
3200 	{
3201 		Con_Printf("%s.txt not found\n", config);
3202 		return false;
3203 	}
3204 	P_ImportEffectInfo(config, file, false);
3205 	free(file);
3206 	return true;
3207 }
3208 #endif
3209 
3210 /*
3211 ===============
3212 R_InitParticles
3213 ===============
3214 */
PScript_InitParticles(void)3215 void PScript_InitParticles (void)
3216 {
3217 	Cvar_RegisterVariable(&r_fteparticles); //johnfitz
3218 	Cvar_RegisterVariable(&r_bouncysparks);
3219 	Cvar_RegisterVariable(&r_part_rain);
3220 	Cvar_RegisterVariable(&r_decal_noperpendicular);
3221 	Cvar_RegisterVariable(&r_particledesc);
3222 	Cvar_RegisterVariable(&r_part_rain_quantity);
3223 	Cvar_RegisterVariable(&r_particle_tracelimit);
3224 	Cvar_RegisterVariable(&r_part_sparks);
3225 	Cvar_RegisterVariable(&r_part_sparks_trifan);
3226 	Cvar_RegisterVariable(&r_part_sparks_textured);
3227 	Cvar_RegisterVariable(&r_part_beams);
3228 	Cvar_RegisterVariable(&r_part_contentswitch);
3229 	Cvar_RegisterVariable(&r_part_density);
3230 	Cvar_RegisterVariable(&r_part_maxparticles);
3231 	Cvar_RegisterVariable(&r_part_maxdecals);
3232 	Cvar_RegisterVariable(&r_lightflicker);
3233 
3234 	Cmd_AddCommand("r_partredirect", P_PartRedirect_f);
3235 
3236 //#if _DEBUG
3237 	Cmd_AddCommand("r_partinfo", P_PartInfo_f);
3238 	Cmd_AddCommand("r_beaminfo", P_BeamInfo_f);
3239 //#endif
3240 }
3241 
PScript_ClearSurfaceParticles(qmodel_t * mod)3242 void PScript_ClearSurfaceParticles(qmodel_t *mod)
3243 {
3244 	mod->skytime = 0;
3245 	mod->skytris = NULL;
3246 	while(mod->skytrimem)
3247 	{
3248 		void *f = mod->skytrimem;
3249 		mod->skytrimem = mod->skytrimem->next;
3250 		Z_Free(f);
3251 	}
3252 }
PScript_ClearAllSurfaceParticles(void)3253 static void PScript_ClearAllSurfaceParticles(void)
3254 {	//make sure we hit all models, even ones from the previous map. maybe this is overkill
3255 	extern qmodel_t	mod_known[];
3256 	extern int		mod_numknown;
3257 	int i;
3258 	for (i = 0; i < mod_numknown; i++)
3259 		PScript_ClearSurfaceParticles(&mod_known[i]);
3260 }
3261 
PScript_Shutdown(void)3262 void PScript_Shutdown (void)
3263 {
3264 	Cvar_SetCallback(&r_particledesc, NULL);
3265 
3266 	CL_ClearTrailStates();
3267 
3268 	pe_default			= P_INVALID;
3269 	pe_size2			= P_INVALID;
3270 	pe_size3			= P_INVALID;
3271 	pe_defaulttrail		= P_INVALID;
3272 
3273 	while(loadedconfigs)
3274 	{
3275 		pcfg_t *cfg;
3276 		cfg = loadedconfigs;
3277 		loadedconfigs = cfg->next;
3278 		Z_Free(cfg);
3279 	}
3280 
3281 	while (numparticletypes > 0)
3282 	{
3283 		numparticletypes--;
3284 		if (part_type[numparticletypes].sounds)
3285 			Z_Free(part_type[numparticletypes].sounds);
3286 		if (part_type[numparticletypes].ramp)
3287 			Z_Free(part_type[numparticletypes].ramp);
3288 	}
3289 	Z_Free (part_type);
3290 	part_type = NULL;
3291 	part_run_list = NULL;
3292 
3293 	Z_Free (particles);
3294 	particles = NULL;
3295 	Z_Free (beams);
3296 	beams = NULL;
3297 	Z_Free (decals);
3298 	decals = NULL;
3299 	Z_Free (trailstates);
3300 	trailstates = NULL;
3301 
3302 	free_particles = NULL;
3303 	free_decals = NULL;
3304 	free_beams = NULL;
3305 
3306 	PScript_ClearAllSurfaceParticles();
3307 
3308 	r_numparticles = 0;
3309 	r_numdecals = 0;
3310 }
3311 
PScript_Startup(void)3312 qboolean PScript_Startup (void)
3313 {
3314 	int newmaxp, newmaxd;
3315 
3316 	newmaxp = r_part_maxparticles.value;
3317 	if (newmaxp < 1)
3318 		newmaxp = 1;
3319 	if (newmaxp > MAX_PARTICLES)
3320 		newmaxp = MAX_PARTICLES;
3321 	newmaxd = r_part_maxdecals.value;
3322 	if (newmaxd < 1)
3323 		newmaxd = 1;
3324 	if (newmaxd > MAX_DECALS)
3325 		newmaxd = MAX_DECALS;
3326 
3327 	if (!r_numparticles)	//already inited
3328 	{
3329 		r_numparticles = newmaxp;
3330 		r_numdecals = newmaxd;
3331 
3332 		buildsintable();
3333 
3334 		r_numbeams = MAX_BEAMSEGS;
3335 		r_numtrailstates = MAX_TRAILSTATES;
3336 
3337 		particles = (particle_t *)Z_Malloc (r_numparticles * sizeof(particle_t));
3338 
3339 		beams = (beamseg_t *)Z_Malloc (r_numbeams * sizeof(beamseg_t));
3340 
3341 		decals = (clippeddecal_t *)Z_Malloc (r_numdecals * sizeof(clippeddecal_t));
3342 
3343 		trailstates = (trailstate_t *)Z_Malloc (r_numtrailstates * sizeof(trailstate_t));
3344 		memset(trailstates, 0, r_numtrailstates * sizeof(trailstate_t));
3345 		ts_cycle = 0;
3346 
3347 		Cvar_SetCallback(&r_particledesc, R_ParticleDesc_Callback);
3348 	}
3349 	r_particledesc.callback(&r_particledesc);
3350 
3351 	return true;
3352 }
3353 
PScript_RecalculateSkyTris(void)3354 void PScript_RecalculateSkyTris (void)
3355 {
3356 	qmodel_t *m = cl.worldmodel;
3357 	size_t modidx;
3358 
3359 	PScript_ClearAllSurfaceParticles();
3360 
3361 	for (modidx = 0; modidx < MAX_MODELS; modidx++)
3362 	{
3363 		m = cl.model_precache[modidx];
3364 
3365 		if (m && !m->needload && m->type == mod_brush)
3366 		{
3367 			int t;
3368 			int i;
3369 			int ptype;
3370 			msurface_t *surf;
3371 			char key[128];
3372 			const char *data = COM_Parse(m->entities);
3373 			int *remaps;
3374 			remaps = malloc(sizeof(*remaps)*m->numtextures);
3375 			if (!remaps)
3376 				break;
3377 			for (t = 0; t < m->numtextures; t++)
3378 				remaps[t] = P_INVALID;
3379 
3380 			//parse the worldspawn entity fields for "_texpart_FOO" keys to give texture "FOO" particles from the effect specified by the value
3381 			if (data && com_token[0] == '{')
3382 			{
3383 				while (1)
3384 				{
3385 					data = COM_Parse(data);
3386 					if (!data)
3387 						break; // error
3388 					if (com_token[0] == '}')
3389 						break; // end of worldspawn
3390 					if (com_token[0] == '_')
3391 						strcpy(key, com_token + 1);
3392 					else
3393 						strcpy(key, com_token);
3394 					while (key[strlen(key)-1] == ' ') // remove trailing spaces
3395 						key[strlen(key)-1] = 0;
3396 					data = COM_Parse(data);
3397 					if (!data)
3398 						break; // error
3399 					if (!q_strncasecmp("texpart_", key, 8))
3400 					{
3401 						/*in quakespasm there are always two textures added on the end (rather than pointing to textures outside the model)*/
3402 						for (t = 0; t < m->numtextures-2; t++)
3403 						{
3404 							if (!m->textures[t])
3405 								continue;
3406 							if (!q_strcasecmp(key+8, m->textures[t]->name))
3407 								remaps[t] = PScript_FindParticleType(com_token);
3408 						}
3409 					}
3410 				}
3411 			}
3412 
3413 			for (t = 0; t < m->numtextures; t++)
3414 			{
3415 				ptype = remaps[t];
3416 				if (ptype == P_INVALID && m->textures[t])
3417 					ptype = PScript_FindParticleType(va("tex_%s", m->textures[t]->name));
3418 
3419 				if (ptype >= 0)
3420 				{
3421 					for (i=0; i<m->nummodelsurfaces; i++)
3422 					{
3423 						surf = m->surfaces + i + m->firstmodelsurface;
3424 						if (surf->texinfo->texture == m->textures[t])
3425 						{
3426 							/*FIXME: it would be a good idea to determine the surface's (midpoint) pvs cluster so that we're not spamming for the entire map*/
3427 							PScript_EmitSkyEffectTris(m, surf, ptype);
3428 						}
3429 					}
3430 				}
3431 			}
3432 			free(remaps);
3433 		}
3434 	}
3435 }
3436 /*
3437 ===============
3438 P_ClearParticles
3439 ===============
3440 */
PScript_ClearParticles(void)3441 void PScript_ClearParticles (void)
3442 {
3443 	int		i;
3444 
3445 	PScript_Startup();
3446 
3447 	free_particles = &particles[0];
3448 	for (i=0 ;i<r_numparticles ; i++)
3449 		particles[i].next = &particles[i+1];
3450 	particles[r_numparticles-1].next = NULL;
3451 
3452 	free_decals = &decals[0];
3453 	for (i=0 ;i<r_numdecals ; i++)
3454 		decals[i].next = &decals[i+1];
3455 	decals[r_numdecals-1].next = NULL;
3456 
3457 	free_beams = &beams[0];
3458 	for (i=0 ;i<r_numbeams ; i++)
3459 	{
3460 		beams[i].p = NULL;
3461 		beams[i].flags = BS_DEAD;
3462 		beams[i].next = &beams[i+1];
3463 	}
3464 	beams[r_numbeams-1].next = NULL;
3465 
3466 	particletime = cl.time;
3467 
3468 	for (i = 0; i < numparticletypes; i++)
3469 	{
3470 		P_LoadTexture(&part_type[i], false);
3471 	}
3472 
3473 	for (i = 0; i < numparticletypes; i++)
3474 	{
3475 		part_type[i].clippeddecals = NULL;
3476 		part_type[i].particles = NULL;
3477 		part_type[i].beams = NULL;
3478 	}
3479 
3480 	PScript_ClearAllSurfaceParticles();
3481 	r_plooksdirty = true;
3482 
3483 	CL_ClearTrailStates();
3484 }
3485 
P_LoadParticleSet(char * name,qboolean implicit,qboolean showwarning)3486 static qboolean P_LoadParticleSet(char *name, qboolean implicit, qboolean showwarning)
3487 {
3488 	char *file;
3489 	pcfg_t *cfg;
3490 
3491 	if (!*name)
3492 		return false;
3493 
3494 	//protect against configs being loaded multiple times. this can easily happen with namespaces (especially if an effect is missing).
3495 	for (cfg = loadedconfigs; cfg; cfg = cfg->next)
3496 	{
3497 		//already loaded?
3498 		if (!strcmp(cfg->name, name))
3499 			return false;
3500 	}
3501 	cfg = Z_Malloc(sizeof(*cfg) + strlen(name));
3502 	if (!cfg)
3503 		return false;
3504 	strcpy(cfg->name, name);
3505 	cfg->next = loadedconfigs;
3506 	loadedconfigs = cfg;
3507 
3508 	if (!strcmp(name, "classic"))
3509 	{
3510 #ifdef PSET_CLASSIC
3511 		if (fallback)
3512 			fallback->ShutdownParticles();
3513 		fallback = &pe_classic;
3514 		if (fallback)
3515 		{
3516 			fallback->InitParticles();
3517 			fallback->ClearParticles();
3518 		}
3519 #endif
3520 		return true;
3521 	}
3522 
3523 	file = (char*)COM_LoadMallocFile(va("particles/%s.cfg", name), NULL);
3524 	if (!file)
3525 		file = (char*)COM_LoadMallocFile(va("%s.cfg", name), NULL);
3526 	if (file)
3527 	{
3528 		PScript_ParseParticleEffectFile(name, implicit, file, com_filesize);
3529 		free(file);
3530 	}
3531 	else
3532 	{
3533 #ifdef PSET_SCRIPT_EFFECTINFO
3534 		if (!strcmp(name, "effectinfo") || !strncmp(name, "effectinfo_", 11))
3535 		{
3536 			//FIXME: we're loading this too early to deal with per-map stuff.
3537 			//FIXME: wait until after particle precache info has been received, and only reload if the loaded configs actually changed.
3538 			P_ImportEffectInfo_Name(name);
3539 			return true;
3540 		}
3541 #endif
3542 		if (showwarning)
3543 			Con_Printf(CON_WARNING "Couldn't find particle description %s\n", name);
3544 		return false;
3545 	}
3546 	return true;
3547 }
3548 
R_Particles_KillAllEffects(void)3549 static void R_Particles_KillAllEffects(void)
3550 {
3551 	int i;
3552 	pcfg_t *cfg;
3553 
3554 	for (i = 0; i < numparticletypes; i++)
3555 	{
3556 		*part_type[i].texname = '\0';
3557 		part_type[i].scale = 0;
3558 		part_type[i].loaded = 0;
3559 		if (part_type->ramp)
3560 			Z_Free(part_type->ramp);
3561 		part_type->ramp = NULL;
3562 		part_type->rampmode = RAMP_NONE;
3563 	}
3564 
3565 	while(loadedconfigs)
3566 	{
3567 		cfg = loadedconfigs;
3568 		loadedconfigs = cfg->next;
3569 		Z_Free(cfg);
3570 	}
3571 }
3572 
R_ParticleDesc_Callback(struct cvar_s * var)3573 static void R_ParticleDesc_Callback(struct cvar_s *var)
3574 {
3575 	const char *c;
3576 
3577 	R_Particles_KillAllEffects();
3578 	r_plooksdirty = true;
3579 
3580 	for (c = var->string; (c=COM_Parse(c)); )
3581 	{
3582 		if (*com_token)
3583 			P_LoadParticleSet(com_token, false, true);
3584 	}
3585 
3586 	if (cls.state == ca_connected && cl.model_precache[1])
3587 	{
3588 		//per-map configs. because we can.
3589 		memcpy(com_token, "map_", 4);
3590 		COM_FileBase(cl.model_precache[1]->name, com_token+4, sizeof(com_token)-4);
3591 		P_LoadParticleSet(com_token, false, false);
3592 	}
3593 
3594 	//make sure nothing is stale.
3595 	CL_RegisterParticles();
3596 }
3597 
P_AddRainParticles(qmodel_t * mod,vec3_t axis[3],vec3_t eorg,float contribution)3598 static void P_AddRainParticles(qmodel_t *mod, vec3_t axis[3], vec3_t eorg, float contribution)
3599 {
3600 	float x;
3601 	float y;
3602 	part_type_t *type;
3603 
3604 	vec3_t org, vdist, worg, wnorm;
3605 
3606 	skytris_t *st;
3607 	if (!r_part_rain_quantity.value)
3608 		return;
3609 
3610 	mod->skytime += contribution;
3611 
3612 	for (st = mod->skytris; st; st = st->next)
3613 	{
3614 		if ((unsigned int)st->ptype >= (unsigned int)numparticletypes)
3615 			continue;
3616 		type = &part_type[st->ptype];
3617 		if (!type->loaded)	//woo, batch skipping.
3618 			continue;
3619 
3620 		while (st->nexttime < mod->skytime)
3621 		{
3622 			if (!free_particles)
3623 				return;
3624 
3625 			st->nexttime += 10000.0/(st->area*r_part_rain_quantity.value*type->rainfrequency);
3626 
3627 			x = frandom()*frandom();
3628 			y = frandom() * (1-x);
3629 			VectorMA(st->org, x, st->x, org);
3630 			VectorMA(org, y, st->y, org);
3631 
3632 			worg[0] = DotProduct(org, axis[0]) + eorg[0];
3633 			worg[1] = -DotProduct(org, axis[1]) + eorg[1];
3634 			worg[2] = DotProduct(org, axis[2]) + eorg[2];
3635 
3636 			//ignore it if its too far away
3637 			VectorSubtract(worg, r_refdef.vieworg, vdist);
3638 			if (VectorLength(vdist) > (1024+512)*frandom())
3639 				continue;
3640 
3641 			if (st->face->flags & SURF_PLANEBACK)
3642 				VectorScale(st->face->plane->normal, -1, vdist);
3643 			else
3644 				VectorCopy(st->face->plane->normal, vdist);
3645 
3646 			wnorm[0] = DotProduct(vdist, axis[0]);
3647 			wnorm[1] = -DotProduct(vdist, axis[1]);
3648 			wnorm[2] = DotProduct(vdist, axis[2]);
3649 
3650 			VectorMA(worg, 0.5, wnorm, worg);
3651 			if (!(CL_PointContentsMask(worg) & FTECONTENTS_SOLID))	//should be paranoia, at least for the world.
3652 			{
3653 				PScript_RunParticleEffectState(worg, wnorm, 1, st->ptype, NULL);
3654 			}
3655 		}
3656 	}
3657 }
3658 
R_Part_SkyTri(qmodel_t * mod,float * v1,float * v2,float * v3,msurface_t * surf,int ptype)3659 static void R_Part_SkyTri(qmodel_t *mod, float *v1, float *v2, float *v3, msurface_t *surf, int ptype)
3660 {
3661 	float dot;
3662 	float xm;
3663 	float ym;
3664 	float theta;
3665 	vec3_t xd;
3666 	vec3_t yd;
3667 
3668 	skytris_t *st;
3669 
3670 	skytriblock_t *mem = mod->skytrimem;
3671 	if (!mem || mem->count == sizeof(mem->tris)/sizeof(mem->tris[0]))
3672 	{
3673 		mod->skytrimem = Z_Malloc(sizeof(*mod->skytrimem));
3674 		mod->skytrimem->next = mem;
3675 		mod->skytrimem->count = 0;
3676 		mem = mod->skytrimem;
3677 	}
3678 
3679 	st = &mem->tris[mem->count];
3680 	VectorCopy(v1, st->org);
3681 	VectorSubtract(v2, st->org, st->x);
3682 	VectorSubtract(v3, st->org, st->y);
3683 
3684 	VectorCopy(st->x, xd);
3685 	VectorCopy(st->y, yd);
3686 
3687 	xm = VectorLength(xd);
3688 	ym = VectorLength(yd);
3689 
3690 	dot = DotProduct(xd, yd);
3691 	theta = acos(dot/(xm*ym));
3692 	st->area = sin(theta)*xm*ym;
3693 	st->nexttime = mod->skytime;
3694 	st->face = surf;
3695 	st->ptype = ptype;
3696 
3697 	if (st->area<=0)
3698 		return;//bummer.
3699 	mem->count++;
3700 
3701 	st->next = mod->skytris;
3702 	mod->skytris = st;
3703 }
3704 
PScript_EmitSkyEffectTris(qmodel_t * mod,msurface_t * fa,int ptype)3705 void PScript_EmitSkyEffectTris(qmodel_t *mod, msurface_t 	*fa, int ptype)
3706 {
3707 	vec3_t		verts[64];
3708 	int v1;
3709 	int v2;
3710 	int v3;
3711 	int numverts;
3712 	int i, lindex;
3713 	float *vec;
3714 
3715 	if (ptype < 0 || ptype >= numparticletypes)
3716 		return;
3717 
3718 	//
3719 	// convert edges back to a normal polygon
3720 	//
3721 	numverts = 0;
3722 	for (i=0 ; i<fa->numedges ; i++)
3723 	{
3724 		lindex = mod->surfedges[fa->firstedge + i];
3725 
3726 		if (lindex > 0)
3727 			vec = mod->vertexes[mod->edges[lindex].v[0]].position;
3728 		else
3729 			vec = mod->vertexes[mod->edges[-lindex].v[1]].position;
3730 		VectorCopy (vec, verts[numverts]);
3731 		numverts++;
3732 
3733 		if (numverts>=64)
3734 		{
3735 			Con_Printf("Too many verts on sky surface\n");
3736 			return;
3737 		}
3738 	}
3739 
3740 	v1 = 0;
3741 	v2 = 1;
3742 	for (v3 = 2; v3 < numverts; v3++)
3743 	{
3744 		R_Part_SkyTri(mod, verts[v1], verts[v2], verts[v3], fa, ptype);
3745 
3746 		v2 = v3;
3747 	}
3748 }
3749 
3750 // Trailstate functions
P_CleanTrailstate(trailstate_t * ts)3751 static void P_CleanTrailstate(trailstate_t *ts)
3752 {
3753 	// clear LASTSEG flag from lastbeam so it can be reused
3754 	if (ts->lastbeam)
3755 	{
3756 		ts->lastbeam->flags &= ~BS_LASTSEG;
3757 		ts->lastbeam->flags |= BS_NODRAW;
3758 	}
3759 
3760 	// clean structure
3761 	memset(ts, 0, sizeof(trailstate_t));
3762 }
3763 
PScript_DelinkTrailstate(trailstate_t ** tsk)3764 void PScript_DelinkTrailstate(trailstate_t **tsk)
3765 {
3766 	trailstate_t *ts;
3767 	trailstate_t *assoc;
3768 
3769 	if (*tsk == NULL)
3770 		return; // not linked to a trailstate
3771 
3772 	ts = *tsk; // store old pointer
3773 	*tsk = NULL; // clear pointer
3774 
3775 	if (ts->key != tsk)
3776 		return; // prevent overwrite
3777 
3778 	assoc = ts->assoc; // store assoc
3779 	P_CleanTrailstate(ts); // clean directly linked trailstate
3780 
3781 	// clean trailstates assoc linked
3782 	while (assoc)
3783 	{
3784 		ts = assoc->assoc;
3785 		P_CleanTrailstate(assoc);
3786 		assoc = ts;
3787 	}
3788 }
3789 
P_NewTrailstate(trailstate_t ** key)3790 static trailstate_t *P_NewTrailstate(trailstate_t **key)
3791 {
3792 	trailstate_t *ts;
3793 
3794 	// bounds check here in case r_numtrailstates changed
3795 	if (ts_cycle >= r_numtrailstates)
3796 		ts_cycle = 0;
3797 
3798 	// get trailstate
3799 	ts = trailstates + ts_cycle;
3800 
3801 	// clear trailstate
3802 	P_CleanTrailstate(ts);
3803 
3804 	// set key
3805 	ts->key = key;
3806 
3807 	// advance index cycle
3808 	ts_cycle++;
3809 
3810 	// return clean trailstate
3811 	return ts;
3812 }
3813 
3814 #define NUMVERTEXNORMALS	162
3815 static float	r_avertexnormals[NUMVERTEXNORMALS][3] = {
3816 #include "anorms.h"
3817 };
3818 static vec2_t	avelocities[NUMVERTEXNORMALS];
3819 #define BEAMLENGTH 16
3820 
PScript_EffectSpawned(part_type_t * ptype,vec3_t org,vec3_t axis[3],int dlkey,float countscale)3821 static void PScript_EffectSpawned(part_type_t *ptype, vec3_t org, vec3_t axis[3], int dlkey, float countscale)
3822 {
3823 	if (ptype->dl_radius[0] || ptype->dl_radius[1])// && r_rocketlight.value)
3824 	{
3825 		float radius;
3826 		dlight_t *dl;
3827 
3828 		static int flickertime;
3829 		static int flicker;
3830 		int i = realtime*20;
3831 		if (flickertime != i)
3832 		{
3833 			flickertime = i;
3834 			flicker = rand();
3835 		}
3836 		radius = ptype->dl_radius[0] + (r_lightflicker.value?((flicker + dlkey*2000)&0xffff)*(1.0f/0xffff):0.5)*ptype->dl_radius[1];
3837 
3838 		dl = CL_AllocDlight (dlkey);
3839 		VectorCopy (org,  dl->origin);
3840 		dl->radius = radius;
3841 		dl->minlight = 0;
3842 		dl->die = cl.time + ptype->dl_time;
3843 		dl->decay = ptype->dl_decay[3];
3844 		VectorCopy(ptype->dl_rgb, dl->color);
3845 	}
3846 	if (ptype->numsounds)
3847 	{
3848 		int i;
3849 		float w,tw;
3850 		for (i = 0, tw = 0; i < ptype->numsounds; i++)
3851 			tw += ptype->sounds[i].weight;
3852 		w = frandom() * tw;	//select the sound by weight
3853 		//and figure out which one that weight corresponds to
3854 		for (i = 0, tw = 0; i < ptype->numsounds; i++)
3855 		{
3856 			tw += ptype->sounds[i].weight;
3857 			if (w <= tw)
3858 			{
3859 				if (*ptype->sounds[i].name && ptype->sounds[i].vol > 0)
3860 				{	//FIXME: no delay, no pitch
3861 					S_StartSound(0, 0, S_PrecacheSound(ptype->sounds[i].name), org, ptype->sounds[i].vol, ptype->sounds[i].atten);
3862 				}
3863 				break;
3864 			}
3865 		}
3866 	}
3867 }
3868 
3869 #ifdef USE_DECALS
3870 typedef struct
3871 {
3872 	part_type_t *ptype;
3873 	int entity;
3874 	qmodel_t *model;
3875 	vec3_t center;
3876 	vec3_t normal;
3877 	vec3_t tangent1;
3878 	vec3_t tangent2;
3879 
3880 	float scale0;
3881 	float scale1;
3882 	float scale2;
3883 
3884 	float bias1;
3885 	float bias2;
3886 } decalctx_t;
PScript_AddDecals(void * vctx,vec3_t * points,size_t numtris)3887 static void PScript_AddDecals(void *vctx, vec3_t *points, size_t numtris)
3888 {
3889 	decalctx_t *ctx = vctx;
3890 	part_type_t *ptype = ctx->ptype;
3891 	clippeddecal_t *d;
3892 	unsigned int i;
3893 	vec3_t vec;
3894 	byte *palrgba = (byte *)d_8to24table;
3895 	while(numtris-->0)
3896 	{
3897 		if (!free_decals)
3898 			break;
3899 
3900 		d = free_decals;
3901 		free_decals = d->next;
3902 		d->next = ptype->clippeddecals;
3903 		ptype->clippeddecals = d;
3904 
3905 		for (i = 0; i < 3; i++)
3906 		{
3907 			VectorCopy(points[i], d->vertex[i]);
3908 			VectorSubtract(d->vertex[i], ctx->center, vec);
3909 			d->texcoords[i][0] = (DotProduct(vec, ctx->tangent1)*ctx->scale1)+ctx->bias1;
3910 			d->texcoords[i][1] = (DotProduct(vec, ctx->tangent2)*ctx->scale2)+ctx->bias2;
3911 			if (r_decal_noperpendicular.value)
3912 			{
3913 				//the decal code is already making sure the surfaces are mostly aligned, which should solve some issues.
3914 				//this means we can make sure that there's NO fading at all, so no issues if the center of the effect is not actually aligned with any surface (yay inprecision).
3915 				d->valpha[i] = 1;
3916 			}
3917 			else
3918 			{
3919 				//fade the alpha depending on the distance from the center)
3920 				//FIXME: should be fabsed by glsl so that linear interpolation works correctly
3921 				d->valpha[i] = 1 - fabs((DotProduct(vec, ctx->normal)*ctx->scale0));
3922 			}
3923 		}
3924 		points += 3;
3925 
3926 		d->entity = ctx->entity;
3927 		d->model = ctx->model;
3928 		d->die = ptype->randdie*frandom();
3929 
3930 		if (ptype->die)
3931 			d->rgba[3] = ptype->alpha + d->die*ptype->alphachange;
3932 		else
3933 			d->rgba[3] = ptype->alpha;
3934 		d->rgba[3] += ptype->alpharand*frandom();
3935 
3936 		if (ptype->colorindex >= 0)
3937 		{
3938 			int cidx;
3939 			cidx = ptype->colorrand > 0 ? rand() % ptype->colorrand : 0;
3940 			cidx = ptype->colorindex + cidx;
3941 			if (cidx > 255)
3942 				d->rgba[3] = d->rgba[3] / 2; // Hexen 2 style transparency
3943 			cidx = (cidx & 0xff) * 4;
3944 			d->rgba[0] = palrgba[cidx] * (1/255.0);
3945 			d->rgba[1] = palrgba[cidx+1] * (1/255.0);
3946 			d->rgba[2] = palrgba[cidx+2] * (1/255.0);
3947 		}
3948 		else
3949 			VectorCopy(ptype->rgb, d->rgba);
3950 
3951 		vec[2] = frandom();
3952 		vec[0] = vec[2]*ptype->rgbrandsync[0] + frandom()*(1-ptype->rgbrandsync[0]);
3953 		vec[1] = vec[2]*ptype->rgbrandsync[1] + frandom()*(1-ptype->rgbrandsync[1]);
3954 		vec[2] = vec[2]*ptype->rgbrandsync[2] + frandom()*(1-ptype->rgbrandsync[2]);
3955 		d->rgba[0] += vec[0]*ptype->rgbrand[0] + ptype->rgbchange[0]*d->die;
3956 		d->rgba[1] += vec[1]*ptype->rgbrand[1] + ptype->rgbchange[1]*d->die;
3957 		d->rgba[2] += vec[2]*ptype->rgbrand[2] + ptype->rgbchange[2]*d->die;
3958 
3959 		d->die = particletime + ptype->die - d->die;
3960 
3961 		if (ptype->looks.type != PT_CDECAL)
3962 			d->die += 20;
3963 
3964 		// maintain run list
3965 		if (!(ptype->state & PS_INRUNLIST))
3966 		{
3967 			ptype->nexttorun = part_run_list;
3968 			part_run_list = ptype;
3969 			ptype->state |= PS_INRUNLIST;
3970 		}
3971 	}
3972 }
3973 
3974 
3975 typedef struct fragmentdecal_s fragmentdecal_t;
3976 static void Mod_ClipDecal(qmodel_t *mod, vec3_t center, vec3_t normal, vec3_t tangent1, vec3_t tangent2, float size, unsigned int surfflagmask, unsigned int surfflagmatch, void (*callback)(void *ctx, vec3_t *points, size_t numpoints), void *ctx);
3977 
3978 //clipped decals actually work by defining the area of the decal with some planes, and then chopping away the entirety of the world based upon those planes (hurrah for bsp to trivially reject most of it)
3979 //the decal is then textured according to some texture projection.
3980 #define MAXFRAGMENTVERTS (128*3)
3981 struct fragmentdecal_s
3982 {
3983 	vec3_t center;
3984 
3985 	vec3_t normal;
3986 	vec3_t planenorm[6];
3987 	float planedist[6];
3988 	int numplanes;
3989 
3990 	vec_t radius;
3991 
3992 	//will only appear on surfaces with the matching surfaceflag
3993 	unsigned int surfflagmask;
3994 	unsigned int surfflagmatch;
3995 
3996 	void (*callback)(void *ctx, vec3_t *points, size_t numpoints);
3997 	void *ctx;
3998 };
Fragment_ClipPolyToPlane(vec3_t * inverts,vec3_t * outverts,int incount,float * plane,float planedist)3999 static int Fragment_ClipPolyToPlane(vec3_t *inverts, vec3_t *outverts, int incount, float *plane, float planedist)
4000 {
4001 	float dotv[MAXFRAGMENTVERTS+1];
4002 	char keep[MAXFRAGMENTVERTS+1];
4003 #define KEEP_KILL 0
4004 #define KEEP_KEEP 1
4005 #define KEEP_BORDER 2
4006 	int i;
4007 	int outcount = 0;
4008 	int clippedcount = 0;
4009 	float d;
4010 	float *p1, *p2;
4011 	float *out;
4012 #define FRAG_EPSILON (1.0/32) //0.5
4013 
4014 	for (i = 0; i < incount; i++)
4015 	{
4016 		dotv[i] = DotProduct(inverts[i], plane) - planedist;
4017 		if (dotv[i]<-FRAG_EPSILON)
4018 		{
4019 			keep[i] = KEEP_KILL;
4020 			clippedcount++;
4021 		}
4022 		else if (dotv[i] > FRAG_EPSILON)
4023 			keep[i] = KEEP_KEEP;
4024 		else
4025 			keep[i] = KEEP_BORDER;
4026 	}
4027 	dotv[i] = dotv[0];
4028 	keep[i] = keep[0];
4029 
4030 	if (clippedcount == incount)
4031 		return 0;	//all were clipped
4032 	if (clippedcount == 0)
4033 	{	//none were clipped
4034 		for (i = 0; i < incount; i++)
4035 			VectorCopy(inverts[i], outverts[i]);
4036 		return incount;
4037 	}
4038 
4039 	for (i = 0; i < incount; i++)
4040 	{
4041 		p1 = inverts[i];
4042 		if (keep[i] == KEEP_BORDER)
4043 		{
4044 			out = outverts[outcount++];
4045 			VectorCopy(p1, out);
4046 			continue;
4047 		}
4048 		if (keep[i] == KEEP_KEEP)
4049 		{
4050 			out = outverts[outcount++];
4051 			VectorCopy(p1, out);
4052 		}
4053 		if (keep[i+1] == KEEP_BORDER || keep[i] == keep[i+1])
4054 			continue;
4055 		p2 = inverts[(i+1)%incount];
4056 		d = dotv[i] - dotv[i+1];
4057 		if (d)
4058 			d = dotv[i] / d;
4059 
4060 		out = outverts[outcount++];
4061 		VectorInterpolate(p1, d, p2, out);
4062 	}
4063 	return outcount;
4064 }
Fragment_ClipPoly(fragmentdecal_t * dec,int numverts,vec3_t * inverts)4065 static void Fragment_ClipPoly(fragmentdecal_t *dec, int numverts, vec3_t *inverts)
4066 {
4067 	//emit the triangle, and clip it's fragments.
4068 	int p;
4069 	vec3_t verts[2][MAXFRAGMENTVERTS];
4070 	vec3_t decalfragmentverts[MAXFRAGMENTVERTS];
4071 	int flip;
4072 	vec3_t d1, d2, n;
4073 	size_t numtris;
4074 
4075 	if (numverts > MAXFRAGMENTVERTS)
4076 		return;
4077 
4078 	if (r_decal_noperpendicular.value)
4079 	{
4080 		VectorSubtract(inverts[1], inverts[0], d1);
4081 		for (p = 2; ; p++)
4082 		{
4083 			if (p >= numverts)
4084 				return;
4085 			VectorSubtract(inverts[p], inverts[0], d2);
4086 			CrossProduct(d1, d2, n);
4087 			if (DotProduct(n,n)>.1)
4088 				break;
4089 		}
4090 		VectorNormalizeFast(n);
4091 		if (DotProduct(n, dec->normal) < 0.1)
4092 			return;	//faces too far way from the normal
4093 	}
4094 
4095 	flip = 0;
4096 	//clip to the first plane specially, so we don't have extra copys
4097 	numverts = Fragment_ClipPolyToPlane(inverts, verts[flip], numverts, dec->planenorm[0], dec->planedist[0]);
4098 
4099 	if (numverts < 3)	//totally clipped.
4100 		return;
4101 
4102 	//clip the polygon to the 6 planes.
4103 	for (p = 1; p < dec->numplanes; p++)
4104 	{
4105 		numverts = Fragment_ClipPolyToPlane(verts[flip], verts[flip^1], numverts, dec->planenorm[p], dec->planedist[p]);
4106 		flip^=1;
4107 
4108 		if (numverts < 3)	//totally clipped.
4109 			return;
4110 	}
4111 
4112 	//decompose the resulting polygon into triangles.
4113 
4114 	numtris = 0;
4115 	while(numverts-->2)
4116 	{
4117 		if (numtris+3 > MAXFRAGMENTVERTS)
4118 		{
4119 			dec->callback(dec->ctx, decalfragmentverts, numtris);
4120 			numtris = 0;
4121 			break;
4122 		}
4123 
4124 		VectorCopy(verts[flip][0],			decalfragmentverts[numtris*3+0]);
4125 		VectorCopy(verts[flip][numverts-1],	decalfragmentverts[numtris*3+1]);
4126 		VectorCopy(verts[flip][numverts],	decalfragmentverts[numtris*3+2]);
4127 		numtris++;
4128 	}
4129 	if (numtris)
4130 		dec->callback(dec->ctx, decalfragmentverts, numtris);
4131 }
4132 //this could be inlined, but I'm lazy.
Q1BSP_Fragment_Surface(fragmentdecal_t * dec,msurface_t * surf)4133 static void Q1BSP_Fragment_Surface (fragmentdecal_t *dec, msurface_t *surf)
4134 {
4135 	int i;
4136 	vec3_t verts[MAXFRAGMENTVERTS];
4137 	glpoly_t *poly;
4138 	float *poly_vert;
4139 
4140 	//water and sky should not get decals.
4141 	if (surf->flags & (SURF_DRAWSKY|SURF_DRAWTURB))
4142 		return;
4143 
4144 	for (poly = surf->polys; poly; poly = poly->next)
4145 	{
4146 		if (poly->numverts > MAXFRAGMENTVERTS)
4147 			continue;
4148 
4149 		for (i = 0; i < poly->numverts; i++)
4150 		{
4151 			poly_vert = &poly->verts[0][0] + (i * VERTEXSIZE);
4152 			VectorCopy(poly_vert, verts[i]);
4153 		}
4154 		Fragment_ClipPoly(dec, i, verts);
4155 	}
4156 }
Q1BSP_ClipDecalToNodes(qmodel_t * mod,fragmentdecal_t * dec,mnode_t * node)4157 static void Q1BSP_ClipDecalToNodes (qmodel_t *mod, fragmentdecal_t *dec, mnode_t *node)
4158 {
4159 	mplane_t		*splitplane;
4160 	float			dist;
4161 	msurface_t		*surf;
4162 	unsigned int	i;
4163 
4164 	if (node->contents < 0)
4165 		return;
4166 
4167 	splitplane = node->plane;
4168 	dist = DotProduct (dec->center, splitplane->normal) - splitplane->dist;
4169 
4170 	if (dist > dec->radius)
4171 	{
4172 		Q1BSP_ClipDecalToNodes (mod, dec, node->children[0]);
4173 		return;
4174 	}
4175 	if (dist < -dec->radius)
4176 	{
4177 		Q1BSP_ClipDecalToNodes (mod, dec, node->children[1]);
4178 		return;
4179 	}
4180 
4181 // mark the polygons
4182 	surf = mod->surfaces + node->firstsurface;
4183 	if (r_decal_noperpendicular.value)
4184 	{
4185 		for (i=0 ; i<node->numsurfaces ; i++, surf++)
4186 		{
4187 			if (surf->flags & SURF_PLANEBACK)
4188 			{
4189 				if (-DotProduct(surf->plane->normal, dec->normal) > -0.5)
4190 					continue;
4191 			}
4192 			else
4193 			{
4194 				if (DotProduct(surf->plane->normal, dec->normal) > -0.5)
4195 					continue;
4196 			}
4197 			Q1BSP_Fragment_Surface(dec, surf);
4198 		}
4199 	}
4200 	else
4201 	{
4202 		for (i=0 ; i<node->numsurfaces ; i++, surf++)
4203 			Q1BSP_Fragment_Surface(dec, surf);
4204 	}
4205 
4206 	Q1BSP_ClipDecalToNodes (mod, dec, node->children[0]);
4207 	Q1BSP_ClipDecalToNodes (mod, dec, node->children[1]);
4208 }
4209 
4210 
Mod_ClipDecal(qmodel_t * mod,vec3_t center,vec3_t normal,vec3_t tangent1,vec3_t tangent2,float size,unsigned int surfflagmask,unsigned int surfflagmatch,void (* callback)(void * ctx,vec3_t * points,size_t numpoints),void * ctx)4211 static void Mod_ClipDecal(qmodel_t *mod, vec3_t center, vec3_t normal, vec3_t tangent1, vec3_t tangent2, float size, unsigned int surfflagmask, unsigned int surfflagmatch, void (*callback)(void *ctx, vec3_t *points, size_t numpoints), void *ctx)
4212 {	//quad marks a full, independant quad
4213 	int p;
4214 	float r;
4215 	fragmentdecal_t dec;
4216 
4217 	VectorCopy(center, dec.center);
4218 	VectorCopy(normal, dec.normal);
4219 	dec.radius = 0;
4220 	dec.callback = callback;
4221 	dec.ctx = ctx;
4222 	dec.surfflagmask = surfflagmask;
4223 	dec.surfflagmatch = surfflagmatch;
4224 
4225 	VectorCopy(tangent1,	dec.planenorm[0]);
4226 	VectorScale(tangent1,	-1, dec.planenorm[1]);
4227 	VectorCopy(tangent2,	dec.planenorm[2]);
4228 	VectorScale(tangent2,	-1, dec.planenorm[3]);
4229 	VectorCopy(dec.normal,		dec.planenorm[4]);
4230 	VectorScale(dec.normal,	-1, dec.planenorm[5]);
4231 	for (p = 0; p < 6; p++)
4232 	{
4233 		r = sqrt(DotProduct(dec.planenorm[p], dec.planenorm[p]));
4234 		VectorScale(dec.planenorm[p], 1/r, dec.planenorm[p]);
4235 		r*= size/2;
4236 		if (r > dec.radius)
4237 			dec.radius = r;
4238 		dec.planedist[p] = -(r - DotProduct(dec.center, dec.planenorm[p]));
4239 	}
4240 	dec.numplanes = 6;
4241 
4242 	if (mod && !mod->needload && mod->type == mod_brush)
4243 		Q1BSP_ClipDecalToNodes(mod, &dec, mod->nodes + mod->hulls[0].firstclipnode);
4244 }
4245 #endif
4246 
4247 void PerpendicularVector( vec3_t dst, const vec3_t src );
4248 
PScript_RunParticleEffectState(vec3_t org,vec3_t dir,float count,int typenum,trailstate_t ** tsk)4249 int PScript_RunParticleEffectState (vec3_t org, vec3_t dir, float count, int typenum, trailstate_t **tsk)
4250 {
4251 	part_type_t *ptype = &part_type[typenum];
4252 	int i, j, k, l, spawnspc;
4253 	float m, pcount;//, orgadd, veladd;
4254 	vec3_t axis[3]={{1,0,0},{0,1,0},{0,0,-1}};
4255 	particle_t	*p;
4256 	beamseg_t *b, *bfirst;
4257 	vec3_t ofsvec, arsvec; // offsetspread vec, areaspread vec
4258 	vec3_t bestdir;
4259 
4260 	float orgadd, veladd;
4261 	trailstate_t *ts;
4262 	byte *palrgba = (byte *)d_8to24table;
4263 
4264 	if (typenum < 0 || typenum >= numparticletypes)
4265 		return 1;
4266 
4267 	if (!ptype->loaded)
4268 		return 1;
4269 
4270 	// inwater check, switch only once
4271 	if (r_part_contentswitch.value && ptype->inwater >= 0 && cl.worldmodel)
4272 	{
4273 		unsigned int cont;
4274 		cont = CL_PointContentsMask(org);
4275 
4276 		if (cont & FTECONTENTS_FLUID)
4277 			ptype = &part_type[ptype->inwater];
4278 	}
4279 
4280 	// eliminate trailstate if flag set
4281 	if (ptype->flags & PT_NOSTATE)
4282 		tsk = NULL;
4283 
4284 	// trailstate allocation/deallocation
4285 	if (tsk)
4286 	{
4287 		// if *tsk = NULL get a new one
4288 		if (*tsk == NULL)
4289 		{
4290 			ts = P_NewTrailstate(tsk);
4291 			*tsk = ts;
4292 		}
4293 		else
4294 		{
4295 			ts = *tsk;
4296 
4297 			if (ts->key != tsk) // trailstate was overwritten
4298 			{
4299 				ts = P_NewTrailstate(tsk); // so get a new one
4300 				*tsk = ts;
4301 			}
4302 		}
4303 	}
4304 	else
4305 		ts = NULL;
4306 
4307 	// get msvc to shut up
4308 	j = k = l = 0;
4309 	m = 0;
4310 
4311 	while(ptype)
4312 	{
4313 		if (r_part_contentswitch.value && (ptype->flags & (PT_TRUNDERWATER | PT_TROVERWATER)) && cl.worldmodel)
4314 		{
4315 			unsigned int cont;
4316 			cont = CL_PointContentsMask(org);
4317 
4318 			if ((ptype->flags & PT_TROVERWATER) && (cont & ptype->fluidmask))
4319 				goto skip;
4320 			if ((ptype->flags & PT_TRUNDERWATER) && !(cont & ptype->fluidmask))
4321 				goto skip;
4322 		}
4323 
4324 		if (dir && (dir[0] || dir[1] || dir[2]))
4325 		{
4326 			VectorCopy(dir, axis[2]);
4327 			VectorNormalize(axis[2]);
4328 			PerpendicularVector(axis[0], axis[2]);
4329 			VectorNormalize(axis[0]);
4330 			CrossProduct(axis[2], axis[0], axis[1]);
4331 			VectorNormalize(axis[1]);
4332 		}
4333 		PScript_EffectSpawned(ptype, org, axis, 0, count);
4334 
4335 		if (ptype->looks.type == PT_CDECAL)
4336 		{
4337 #ifdef USE_DECALS
4338 			vec3_t vec={0.5, 0.5, 0.5};
4339 			int n;
4340 			decalctx_t ctx;
4341 			vec3_t start, end;
4342 
4343 			if (!free_decals)
4344 				return 0;
4345 
4346 			ctx.entity = 0;
4347 
4348 			VectorCopy(org, ctx.center);
4349 			if (!dir || (dir[0] == 0 && dir[1] == 0 && dir[2] == 0))
4350 			{
4351 				float bestfrac = 1;
4352 				float frac;
4353 				vec3_t impact, normal;
4354 				int what;
4355 				bestdir[0] = 0;
4356 				bestdir[1] = 0.73;
4357 				bestdir[2] = 0.73;
4358 				VectorNormalize(bestdir);
4359 				for (n = 0; n < 6; n++)
4360 				{
4361 					if (n >= 3)
4362 					{
4363 						end[0] = (n==3)*16;
4364 						end[1] = (n==4)*16;
4365 						end[2] = (n==5)*16;
4366 					}
4367 					else
4368 					{
4369 						end[0] = -(n==0)*16;
4370 						end[1] = -(n==1)*16;
4371 						end[2] = -(n==2)*16;
4372 					}
4373 					VectorSubtract(org, end, start);
4374 					VectorAdd(org, end, end);
4375 
4376 
4377 					frac = CL_TraceLine(start, end, impact, normal, &what);
4378 					if (bestfrac > frac)
4379 					{
4380 						bestfrac = frac;
4381 						VectorCopy(normal, bestdir);
4382 						VectorCopy(impact, ctx.center);
4383 						ctx.entity = what;
4384 					}
4385 				}
4386 				dir = bestdir;
4387 			}
4388 			else
4389 			{	//try to get it exactly on the plane, otherwise network or collision inprecisions can leave us further away from the surface than the radius of the decal
4390 				VectorSubtract(org, dir, start);
4391 				VectorAdd(org, dir, end);
4392 				CL_TraceLine(start, end, ctx.center, bestdir, &ctx.entity);
4393 			}
4394 			if (ctx.entity)
4395 			{
4396 				entity_t *ent = CL_EntityNum(ctx.entity);
4397 				if (ent->model)	//looks like its active.
4398 				{
4399 					ctx.model = ent->model;
4400 					//FIXME: rotate normal
4401 					VectorSubtract(ctx.center, ent->origin, ctx.center);
4402 				}
4403 				else
4404 				{
4405 					ctx.entity = 0;
4406 					ctx.model = cl.worldmodel;
4407 				}
4408 			}
4409 			else
4410 			{
4411 				ctx.entity = 0;
4412 				ctx.model = cl.worldmodel;
4413 			}
4414 			if (!ctx.model)
4415 				return 0;
4416 
4417 			VectorScale(dir, -1, ctx.normal);
4418 			VectorNormalize(ctx.normal);
4419 
4420 			//we know the normal now. pick two random tangents.
4421 			VectorNormalize(vec);
4422 			CrossProduct(ctx.normal, vec, ctx.tangent1);
4423 			RotatePointAroundVector(ctx.tangent2, ctx.normal, ctx.tangent1, frandom()*360);
4424 			CrossProduct(ctx.normal, ctx.tangent2, ctx.tangent1);
4425 
4426 			VectorNormalize(ctx.tangent1);
4427 			VectorNormalize(ctx.tangent2);
4428 
4429 			ctx.ptype = ptype;
4430 			ctx.scale1 = ptype->s2 - ptype->s1;
4431 			ctx.bias1 = ptype->s1 + ctx.scale1/2;
4432 			ctx.scale2 = ptype->t2 - ptype->t1;
4433 			ctx.bias2 = ptype->t1 + ctx.scale2/2;
4434 			m = ptype->scale + frandom() * ptype->scalerand;
4435 			ctx.scale0 = 2.0 / m;
4436 			ctx.scale1 /= m;
4437 			ctx.scale2 /= m;
4438 
4439 			if (ptype->randsmax!=1)
4440 				ctx.bias1 += ptype->texsstride * (rand()%ptype->randsmax);
4441 
4442 			//inserts decals through a callback.
4443 			Mod_ClipDecal(ctx.model, ctx.center, ctx.normal, ctx.tangent2, ctx.tangent1, m, ptype->surfflagmask, ptype->surfflagmatch, PScript_AddDecals, &ctx);
4444 #endif
4445 			if (ptype->assoc < 0)
4446 				break;
4447 			ptype = &part_type[ptype->assoc];
4448 			continue;
4449 		}
4450 		// init spawn specific variables
4451 		b = bfirst = NULL;
4452 		spawnspc = 8;
4453 		pcount = ptype->countextra + r_part_density.value*count*(ptype->count+ptype->countrand*frandom());
4454 		if (ptype->flags & PT_INVFRAMETIME)
4455 			pcount /= host_frametime;
4456 		if (ts)
4457 			pcount += ts->state2.emittime;
4458 
4459 		switch (ptype->spawnmode)
4460 		{
4461 		case SM_UNICIRCLE:
4462 			m = pcount;
4463 			if (ptype->looks.type == PT_BEAM)
4464 				m--;
4465 
4466 			if (m < 1)
4467 				m = 0;
4468 			else
4469 				m = (M_PI*2)/m;
4470 
4471 			if (ptype->spawnparam1) /* use for weird shape hacks */
4472 				m *= ptype->spawnparam1;
4473 			break;
4474 		case SM_TELEBOX:
4475 			spawnspc = 4;
4476 			l = -ptype->areaspreadvert;
4477 		case SM_LAVASPLASH:
4478 			j = k = -ptype->areaspread;
4479 			if (ptype->spawnparam1)
4480 				m = ptype->spawnparam1;
4481 			else
4482 				m = 0.55752; /* default weird number for tele/lavasplash used in vanilla Q1 */
4483 
4484 			if (ptype->spawnparam2)
4485 				spawnspc = (int)ptype->spawnparam2;
4486 			break;
4487 		case SM_FIELD:
4488 			if (!avelocities[0][0])
4489 			{
4490 				for (j=0 ; j<NUMVERTEXNORMALS ; j++)
4491 				{
4492 					avelocities[j][0] = (rand()&255) * 0.01;
4493 					avelocities[j][1] = (rand()&255) * 0.01;
4494 				}
4495 			}
4496 
4497 			j = 0;
4498 			m = 0;
4499 			break;
4500 		default:	//others don't need intitialisation
4501 			break;
4502 		}
4503 
4504 		// time limit (for completeness)
4505 		if (ptype->spawntime && ts)
4506 		{
4507 			if (ts->state1.statetime > particletime)
4508 				return 0; // timelimit still in effect
4509 
4510 			ts->state1.statetime = particletime + ptype->spawntime; // record old time
4511 		}
4512 
4513 		// random chance for point effects
4514 		if (ptype->spawnchance < frandom())
4515 		{
4516 			i = ceil(pcount);
4517 			break;
4518 		}
4519 
4520 		/*this is a hack, use countextra=1, count=0*/
4521 		if (!ptype->die && ptype->count == 1 && ptype->countrand == 0 && pcount < 1)
4522 			pcount = 1;
4523 
4524 		// particle spawning loop
4525 		for (i = 0; i < pcount; i++)
4526 		{
4527 			if (!free_particles)
4528 				break;
4529 			p = free_particles;
4530 			if (ptype->looks.type == PT_BEAM)
4531 			{
4532 				if (!free_beams)
4533 					break;
4534 				if (b)
4535 				{
4536 					b = b->next = free_beams;
4537 					free_beams = free_beams->next;
4538 				}
4539 				else
4540 				{
4541 					b = bfirst = free_beams;
4542 					free_beams = free_beams->next;
4543 				}
4544 				b->texture_s = i; // TODO: FIX THIS NUMBER
4545 				b->flags = 0;
4546 				b->p = p;
4547 				VectorClear(b->dir);
4548 			}
4549 			free_particles = p->next;
4550 			p->next = ptype->particles;
4551 			ptype->particles = p;
4552 
4553 			p->die = ptype->randdie*frandom();
4554 			p->scale = ptype->scale+ptype->scalerand*frandom();
4555 			if (ptype->die)
4556 				p->rgba[3] = ptype->alpha+p->die*ptype->alphachange;
4557 			else
4558 				p->rgba[3] = ptype->alpha;
4559 			p->rgba[3] += ptype->alpharand*frandom();
4560 			// p->color = 0;
4561 			if (ptype->emittime < 0)
4562 				p->state.trailstate = NULL;
4563 			else
4564 				p->state.nextemit = particletime + ptype->emitstart - p->die;
4565 
4566 			p->rotationspeed = ptype->rotationmin + frandom()*ptype->rotationrand;
4567 			p->angle = ptype->rotationstartmin + frandom()*ptype->rotationstartrand;
4568 			p->s1 = ptype->s1;
4569 			p->t1 = ptype->t1;
4570 			p->s2 = ptype->s2;
4571 			p->t2 = ptype->t2;
4572 			if (ptype->randsmax!=1)
4573 			{
4574 				m = ptype->texsstride * (rand()%ptype->randsmax);
4575 				p->s1 += m;
4576 				p->s2 += m;
4577 			}
4578 
4579 			if (ptype->colorindex >= 0)
4580 			{
4581 				int cidx;
4582 				cidx = ptype->colorrand > 0 ? rand() % ptype->colorrand : 0;
4583 				cidx = ptype->colorindex + cidx;
4584 				if (cidx > 255)
4585 					p->rgba[3] = p->rgba[3] / 2; // Hexen 2 style transparency
4586 				cidx = (cidx & 0xff) * 4;
4587 				p->rgba[0] = palrgba[cidx] * (1/255.0);
4588 				p->rgba[1] = palrgba[cidx+1] * (1/255.0);
4589 				p->rgba[2] = palrgba[cidx+2] * (1/255.0);
4590 			}
4591 			else
4592 				VectorCopy(ptype->rgb, p->rgba);
4593 
4594 			// use org temporarily for rgbsync
4595 			p->org[2] = frandom();
4596 			p->org[0] = p->org[2]*ptype->rgbrandsync[0] + frandom()*(1-ptype->rgbrandsync[0]);
4597 			p->org[1] = p->org[2]*ptype->rgbrandsync[1] + frandom()*(1-ptype->rgbrandsync[1]);
4598 			p->org[2] = p->org[2]*ptype->rgbrandsync[2] + frandom()*(1-ptype->rgbrandsync[2]);
4599 
4600 			p->rgba[0] += p->org[0]*ptype->rgbrand[0] + ptype->rgbchange[0]*p->die;
4601 			p->rgba[1] += p->org[1]*ptype->rgbrand[1] + ptype->rgbchange[1]*p->die;
4602 			p->rgba[2] += p->org[2]*ptype->rgbrand[2] + ptype->rgbchange[2]*p->die;
4603 
4604 			p->vel[0] = 0;
4605 			p->vel[1] = 0;
4606 			p->vel[2] = 0;
4607 
4608 			// handle spawn modes (org/vel)
4609 			switch (ptype->spawnmode)
4610 			{
4611 			case SM_BOX:
4612 				ofsvec[0] = crandom();
4613 				ofsvec[1] = crandom();
4614 				ofsvec[2] = crandom();
4615 
4616 				arsvec[0] = ofsvec[0]*ptype->areaspread;
4617 				arsvec[1] = ofsvec[1]*ptype->areaspread;
4618 				arsvec[2] = ofsvec[2]*ptype->areaspreadvert;
4619 				break;
4620 			case SM_TELEBOX:
4621 				ofsvec[0] = k;
4622 				ofsvec[1] = j;
4623 				ofsvec[2] = l+4;
4624 				VectorNormalize(ofsvec);
4625 				VectorScale(ofsvec, 1.0-(frandom())*m, ofsvec);
4626 
4627 				// org is just like the original
4628 				arsvec[0] = j + (rand()%spawnspc);
4629 				arsvec[1] = k + (rand()%spawnspc);
4630 				arsvec[2] = l + (rand()%spawnspc);
4631 
4632 				// advance telebox loop
4633 				j += spawnspc;
4634 				if (j >= ptype->areaspread)
4635 				{
4636 					j = -ptype->areaspread;
4637 					k += spawnspc;
4638 					if (k >= ptype->areaspread)
4639 					{
4640 						k = -ptype->areaspread;
4641 						l += spawnspc;
4642 						if (l >= ptype->areaspreadvert)
4643 							l = -ptype->areaspreadvert;
4644 					}
4645 				}
4646 				break;
4647 			case SM_LAVASPLASH:
4648 				// calc directions, org with temp vector
4649 				ofsvec[0] = k + (rand()%spawnspc);
4650 				ofsvec[1] = j + (rand()%spawnspc);
4651 				ofsvec[2] = 256;
4652 
4653 				arsvec[0] = ofsvec[0];
4654 				arsvec[1] = ofsvec[1];
4655 				arsvec[2] = frandom()*ptype->areaspreadvert;
4656 
4657 				VectorNormalize(ofsvec);
4658 				VectorScale(ofsvec, 1.0-(frandom())*m, ofsvec);
4659 
4660 				// advance splash loop
4661 				j += spawnspc;
4662 				if (j >= ptype->areaspread)
4663 				{
4664 					j = -ptype->areaspread;
4665 					k += spawnspc;
4666 					if (k >= ptype->areaspread)
4667 						k = -ptype->areaspread;
4668 				}
4669 				break;
4670 			case SM_UNICIRCLE:
4671 				ofsvec[0] = cos(m*i);
4672 				ofsvec[1] = sin(m*i);
4673 				ofsvec[2] = 0;
4674 				VectorScale(ofsvec, ptype->areaspread, arsvec);
4675 				break;
4676 			case SM_FIELD:
4677 				arsvec[0] = (cl.time * avelocities[i][0]) + m;
4678 				arsvec[1] = (cl.time * avelocities[i][1]) + m;
4679 				arsvec[2] = cos(arsvec[1]);
4680 
4681 				ofsvec[0] = arsvec[2]*cos(arsvec[0]);
4682 				ofsvec[1] = arsvec[2]*sin(arsvec[0]);
4683 				ofsvec[2] = -sin(arsvec[1]);
4684 
4685 				orgadd = ptype->spawnparam2 * sin(cl.time+j+m);
4686 				arsvec[0] = r_avertexnormals[j][0]*(ptype->areaspread+orgadd) + ofsvec[0]*ptype->spawnparam1;
4687 				arsvec[1] = r_avertexnormals[j][1]*(ptype->areaspread+orgadd) + ofsvec[1]*ptype->spawnparam1;
4688 				arsvec[2] = r_avertexnormals[j][2]*(ptype->areaspreadvert+orgadd) + ofsvec[2]*ptype->spawnparam1;
4689 
4690 				VectorNormalize(ofsvec);
4691 
4692 				j++;
4693 				if (j >= NUMVERTEXNORMALS)
4694 				{
4695 					j = 0;
4696 					m += 0.1762891; // some BS number to try to "randomize" things
4697 				}
4698 				break;
4699 			case SM_DISTBALL:
4700 				{
4701 					float rdist;
4702 
4703 					rdist = ptype->spawnparam2 - crandom()*(1-(crandom() * ptype->spawnparam1));
4704 
4705 					// this is a strange spawntype, which is based on the fact that
4706 					// crandom()*crandom() provides something similar to an exponential
4707 					// probability curve
4708 					ofsvec[0] = hrandom();
4709 					ofsvec[1] = hrandom();
4710 					if (ptype->areaspreadvert)
4711 						ofsvec[2] = hrandom();
4712 					else
4713 						ofsvec[2] = 0;
4714 
4715 					VectorNormalize(ofsvec);
4716 					VectorScale(ofsvec, rdist, ofsvec);
4717 
4718 					arsvec[0] = ofsvec[0]*ptype->areaspread;
4719 					arsvec[1] = ofsvec[1]*ptype->areaspread;
4720 					arsvec[2] = ofsvec[2]*ptype->areaspreadvert;
4721 				}
4722 				break;
4723 			default: // SM_BALL, SM_CIRCLE
4724 				{
4725 					ofsvec[0] = hrandom();
4726 					ofsvec[1] = hrandom();
4727 					if (ptype->areaspreadvert)
4728 						ofsvec[2] = hrandom();
4729 					else
4730 						ofsvec[2] = 0;
4731 
4732 					VectorNormalize(ofsvec);
4733 					if (ptype->spawnmode != SM_CIRCLE)
4734 						VectorScale(ofsvec, frandom(), ofsvec);
4735 
4736 					arsvec[0] = ofsvec[0]*ptype->areaspread;
4737 					arsvec[1] = ofsvec[1]*ptype->areaspread;
4738 					arsvec[2] = ofsvec[2]*ptype->areaspreadvert;
4739 				}
4740 				break;
4741 			}
4742 
4743 			// apply arsvec+ofsvec
4744 			orgadd = ptype->orgadd + frandom()*ptype->randomorgadd;
4745 			veladd = ptype->veladd + frandom()*ptype->randomveladd;
4746 
4747 			if (dir)
4748 				veladd *= VectorLength(dir);
4749 			VectorMA(p->vel, ofsvec[0]*ptype->spawnvel, axis[0], p->vel);
4750 			VectorMA(p->vel, ofsvec[1]*ptype->spawnvel, axis[1], p->vel);
4751 			VectorMA(p->vel, veladd+ofsvec[2]*ptype->spawnvelvert, axis[2], p->vel);
4752 
4753 			VectorMA(org, arsvec[0], axis[0], p->org);
4754 			VectorMA(p->org, arsvec[1], axis[1], p->org);
4755 			VectorMA(p->org, orgadd+arsvec[2], axis[2], p->org);
4756 
4757 			if (ptype->flags & PT_WORLDSPACERAND)
4758 			{
4759 				do
4760 				{
4761 					ofsvec[0] = crand();
4762 					ofsvec[1] = crand();
4763 					ofsvec[2] = crand();
4764 				} while(DotProduct(ofsvec,ofsvec)>1);	//crap, but I'm trying to mimic dp
4765 				p->org[0] += ofsvec[0] * ptype->orgwrand[0];
4766 				p->org[1] += ofsvec[1] * ptype->orgwrand[1];
4767 				p->org[2] += ofsvec[2] * ptype->orgwrand[2];
4768 				p->vel[0] += ofsvec[0] * ptype->velwrand[0];
4769 				p->vel[1] += ofsvec[1] * ptype->velwrand[1];
4770 				p->vel[2] += ofsvec[2] * ptype->velwrand[2];
4771 				VectorAdd(p->vel, ptype->velbias, p->vel);
4772 			}
4773 			VectorAdd(p->org, ptype->orgbias, p->org);
4774 
4775 			p->die = particletime + ptype->die - p->die;
4776 
4777 			VectorCopy(p->org, p->oldorg);
4778 		}
4779 
4780 		// update beam list
4781 		if (ptype->looks.type == PT_BEAM)
4782 		{
4783 			if (b)
4784 			{
4785 				// update dir for bfirst for certain modes since it will never get updated
4786 				switch (ptype->spawnmode)
4787 				{
4788 				case SM_UNICIRCLE:
4789 					// kinda hackish here, assuming ofsvec contains the point at i-1
4790 					arsvec[0] = cos(m*(i-2));
4791 					arsvec[1] = sin(m*(i-2));
4792 					arsvec[2] = 0;
4793 					VectorSubtract(b->p->org, arsvec, bfirst->dir);
4794 					VectorNormalize(bfirst->dir);
4795 					break;
4796 				default:
4797 					break;
4798 				}
4799 
4800 				b->flags |= BS_NODRAW;
4801 				b->next = ptype->beams;
4802 				ptype->beams = bfirst;
4803 			}
4804 		}
4805 
4806 		// save off emit times in trailstate
4807 		if (ts)
4808 			ts->state2.emittime = pcount - i;
4809 
4810 		// maintain run list
4811 		if (!(ptype->state & PS_INRUNLIST) && (ptype->particles || ptype->clippeddecals))
4812 		{
4813 			if (part_run_list)
4814 			{
4815 				//insert after, to try to avoid edge-case weirdness
4816 				ptype->nexttorun = part_run_list->nexttorun;
4817 				part_run_list->nexttorun = ptype;
4818 			}
4819 			else
4820 			{
4821 				ptype->nexttorun = part_run_list;
4822 				part_run_list = ptype;
4823 			}
4824 			ptype->state |= PS_INRUNLIST;
4825 		}
4826 
4827 skip:
4828 
4829 		// go to next associated effect
4830 		if (ptype->assoc < 0)
4831 			break;
4832 
4833 		// new trailstate
4834 		if (ts)
4835 		{
4836 			tsk = &(ts->assoc);
4837 			// if *tsk = NULL get a new one
4838 			if (*tsk == NULL)
4839 			{
4840 				ts = P_NewTrailstate(tsk);
4841 				*tsk = ts;
4842 			}
4843 			else
4844 			{
4845 				ts = *tsk;
4846 
4847 				if (ts->key != tsk) // trailstate was overwritten
4848 				{
4849 					ts = P_NewTrailstate(tsk); // so get a new one
4850 					*tsk = ts;
4851 				}
4852 			}
4853 		}
4854 
4855 		ptype = &part_type[ptype->assoc];
4856 	}
4857 
4858 	return 0;
4859 }
4860 
PScript_RunParticleEffectTypeString(vec3_t org,vec3_t dir,float count,const char * name)4861 int PScript_RunParticleEffectTypeString (vec3_t org, vec3_t dir, float count, const char *name)
4862 {
4863 	if (r_fteparticles.value == 0)
4864 		return 1;
4865 	int type = PScript_FindParticleType(name);
4866 	if (type < 0)
4867 		return 1;
4868 
4869 	return PScript_RunParticleEffectState(org, dir, count, type, NULL);
4870 }
4871 
PScript_EntParticleTrail(vec3_t oldorg,entity_t * ent,const char * name)4872 int PScript_EntParticleTrail(vec3_t oldorg, entity_t *ent, const char *name)
4873 {
4874 	if (r_fteparticles.value == 0)
4875 		return 1;
4876 	float timeinterval = cl.time - cl.oldtime;
4877 	vec3_t axis[3];
4878 	int type = PScript_FindParticleType(name);
4879 	if (type < 0)
4880 		return 1;
4881 
4882 	AngleVectors(ent->angles, axis[0], axis[1], axis[2]);
4883 	return PScript_ParticleTrail(oldorg, ent->origin, type, timeinterval, ent-cl.entities, axis, &ent->trailstate);
4884 }
4885 
4886 /*
4887 ===============
4888 P_RunParticleEffect
4889 
4890 ===============
4891 */
PScript_RunParticleEffect(vec3_t org,vec3_t dir,int color,int count)4892 int PScript_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count)
4893 {
4894 	if (r_fteparticles.value == 0)
4895 		return false;
4896 	int ptype;
4897 
4898 	ptype = PScript_FindParticleType(va("pe_%i", color));
4899 	if (PScript_RunParticleEffectState(org, dir, count, ptype, NULL))
4900 	{
4901 		if (count > 130 && PART_VALID(pe_size3))
4902 		{
4903 			part_type[pe_size3].colorindex = color & ~0x7;
4904 			part_type[pe_size3].colorrand = 8;
4905 			return PScript_RunParticleEffectState(org, dir, count, pe_size3, NULL);
4906 		}
4907 		else if (count > 20 && PART_VALID(pe_size2))
4908 		{
4909 			part_type[pe_size2].colorindex = color & ~0x7;
4910 			part_type[pe_size2].colorrand = 8;
4911 			return PScript_RunParticleEffectState(org, dir, count, pe_size2, NULL);
4912 		}
4913 		else if (PART_VALID(pe_default))
4914 		{
4915 			part_type[pe_default].colorindex = color & ~0x7;
4916 			part_type[pe_default].colorrand = 8;
4917 			return PScript_RunParticleEffectState(org, dir, count, pe_default, NULL);
4918 		}
4919 		return true;
4920 	}
4921 	return false;
4922 }
4923 
PScript_RunParticleWeather(vec3_t minb,vec3_t maxb,vec3_t dir,float count,int colour,const char * efname)4924 void PScript_RunParticleWeather(vec3_t minb, vec3_t maxb, vec3_t dir, float count, int colour, const char *efname)
4925 {
4926 	vec3_t org;
4927 	int			i, j;
4928 	float		num;
4929 	float invcount;
4930 
4931 	int ptype = PScript_FindParticleType(va("te_%s_%i", efname, colour));
4932 	if (!PART_VALID(ptype))
4933 	{
4934 		ptype = PScript_FindParticleType(va("te_%s", efname));
4935 		if (!PART_VALID(ptype))
4936 			ptype = pe_default;
4937 		if (!PART_VALID(ptype))
4938 			return;
4939 		part_type[ptype].colorindex = colour;
4940 	}
4941 
4942 	invcount = 1/part_type[ptype].count; // using this to get R_RPET to always spawn 1
4943 	count = count * part_type[ptype].count;
4944 
4945 	for (i=0 ; i<count ; i++)
4946 	{
4947 		if (!free_particles)
4948 			return;
4949 
4950 		for (j=0 ; j<3 ; j++)
4951 		{
4952 			num = rand() / (float)RAND_MAX;
4953 			org[j] = minb[j] + num*(maxb[j]-minb[j]);
4954 		}
4955 		PScript_RunParticleEffectState(org, dir, invcount, ptype, NULL);
4956 	}
4957 }
4958 
PScript_ParticleTrailSpawn(vec3_t startpos,vec3_t end,part_type_t * ptype,float timeinterval,trailstate_t ** tsk,int dlkey,vec3_t dlaxis[3])4959 static void PScript_ParticleTrailSpawn (vec3_t startpos, vec3_t end, part_type_t *ptype, float timeinterval, trailstate_t **tsk, int dlkey, vec3_t dlaxis[3])
4960 {
4961 	vec3_t	vec, vstep, right, up, start;
4962 	float	len;
4963 	int			tcount;
4964 	particle_t	*p;
4965 	beamseg_t   *b;
4966 	beamseg_t   *bfirst;
4967 	trailstate_t *ts;
4968 	float count;
4969 
4970 	float veladd = -ptype->veladd;
4971 	float step;
4972 	float stop;
4973 	float tdegree = 2.0*M_PI/256; /* MSVC whine */
4974 	float sdegree = 0;
4975 	float nrfirst, nrlast;
4976 	byte *palrgba = (byte *)d_8to24table;
4977 
4978 	VectorCopy(startpos, start);
4979 
4980 	// eliminate trailstate if flag set
4981 	if (ptype->flags & PT_NOSTATE)
4982 		tsk = NULL;
4983 
4984 	// trailstate allocation/deallocation
4985 	if (tsk)
4986 	{
4987 		// if *tsk = NULL get a new one
4988 		if (*tsk == NULL)
4989 		{
4990 			ts = P_NewTrailstate(tsk);
4991 			*tsk = ts;
4992 		}
4993 		else
4994 		{
4995 			ts = *tsk;
4996 
4997 			if (ts->key != tsk) // trailstate was overwritten
4998 			{
4999 				ts = P_NewTrailstate(tsk); // so get a new one
5000 				*tsk = ts;
5001 			}
5002 		}
5003 	}
5004 	else
5005 		ts = NULL;
5006 
5007 	PScript_EffectSpawned(ptype, start, dlaxis, dlkey, 1);
5008 
5009 	if (ptype->assoc>=0)
5010 	{
5011 		if (ts)
5012 			PScript_ParticleTrail(start, end, ptype->assoc, timeinterval, dlkey, NULL, &(ts->assoc));
5013 		else
5014 			PScript_ParticleTrail(start, end, ptype->assoc, timeinterval, dlkey, NULL, NULL);
5015 	}
5016 
5017 	if (r_part_contentswitch.value && (ptype->flags & (PT_TRUNDERWATER | PT_TROVERWATER)) && cl.worldmodel)
5018 	{
5019 		unsigned int cont;
5020 		cont = CL_PointContentsMask(startpos);
5021 
5022 		if ((ptype->flags & PT_TROVERWATER) && (cont & ptype->fluidmask))
5023 			return;
5024 		if ((ptype->flags & PT_TRUNDERWATER) && !(cont & ptype->fluidmask))
5025 			return;
5026 	}
5027 
5028 	// time limit for trails
5029 	if (ptype->spawntime && ts)
5030 	{
5031 		if (ts->state1.statetime > particletime)
5032 			return; // timelimit still in effect
5033 
5034 		ts->state1.statetime = particletime + ptype->spawntime; // record old time
5035 		ts = NULL; // clear trailstate so we don't save length/lastseg
5036 	}
5037 
5038 	// random chance for trails
5039 	if (ptype->spawnchance < frandom())
5040 		return; // don't spawn but return success
5041 
5042 	if (!ptype->die)
5043 		ts = NULL;
5044 
5045 	VectorSubtract (end, start, vec);
5046 	len = VectorNormalize (vec);
5047 
5048 	// use ptype step to calc step vector and step size
5049 	if (ptype->countspacing)
5050 	{
5051 		step = ptype->countspacing;					//particles per qu
5052 		step /= r_part_density.value;				//scaled...
5053 
5054 		if (ptype->countextra)
5055 		{
5056 			count = ptype->countextra;
5057 			if (step>0)
5058 				count += len/step;
5059 			step = len/count;
5060 		}
5061 	}
5062 	else
5063 	{
5064 		step = ptype->count * r_part_density.value * timeinterval;
5065 		step += ptype->countextra;					//particles per frame
5066 		step += ptype->countoverflow;
5067 		count = (int)step;
5068 		ptype->countoverflow = step-count;	//the part that we're forgetting, to add to the next frame...
5069 		if (count<=0)
5070 			return;
5071 		else
5072 			step = len/count;				//particles per second
5073 	}
5074 
5075 	if (ptype->flags & PT_AVERAGETRAIL)
5076 	{
5077 		float tavg;
5078 		// mangle len/step to get last point to be at end
5079 		tavg = len / step;
5080 		tavg = tavg / ceil(tavg);
5081 		step *= tavg;
5082 		len += step;
5083 	}
5084 
5085 	VectorScale(vec, step, vstep);
5086 
5087 	// add offset
5088 //	VectorAdd(start, ptype->orgbias, start);
5089 
5090 	// spawn mode precalculations
5091 	if (ptype->spawnmode == SM_SPIRAL)
5092 	{
5093 		VectorVectors(vec, right, up);
5094 
5095 		// precalculate degree of rotation
5096 		if (ptype->spawnparam1)
5097 			tdegree = 2.0*M_PI/ptype->spawnparam1; /* distance per rotation inversed */
5098 		sdegree = ptype->spawnparam2*(M_PI/180);
5099 	}
5100 	else if (ptype->spawnmode == SM_CIRCLE)
5101 	{
5102 		VectorVectors(vec, right, up);
5103 	}
5104 
5105 	// store last stop here for lack of a better solution besides vectors
5106 	if (ts)
5107 	{
5108 		ts->state2.laststop = stop = ts->state2.laststop + len;	//when to stop
5109 		len = ts->state1.lastdist;
5110 	}
5111 	else
5112 	{
5113 		stop = len;
5114 		len = 0;
5115 	}
5116 
5117 //	len = ts->lastdist/step;
5118 //	len = (len - (int)len)*step;
5119 //	VectorMA (start, -len, vec, start);
5120 
5121 	if (ptype->flags & PT_NOSPREADFIRST)
5122 		nrfirst = len + step*1.5;
5123 	else
5124 		nrfirst = len;
5125 
5126 	if (ptype->flags & PT_NOSPREADLAST)
5127 		nrlast = stop;
5128 	else
5129 		nrlast = stop + step;
5130 
5131 	b = bfirst = NULL;
5132 
5133 	if (len < stop)
5134 		count = (stop-len) / step;
5135 	else
5136 	{
5137 		count = 0;
5138 		step = 0;
5139 		VectorClear(vstep);
5140 	}
5141 //	count += ptype->countextra;
5142 
5143 	while (count-->0)//len < stop)
5144 	{
5145 		len += step;
5146 
5147 		if (!free_particles)
5148 		{
5149 			len = stop;
5150 			break;
5151 		}
5152 
5153 		p = free_particles;
5154 		if (ptype->looks.type == PT_BEAM)
5155 		{
5156 			if (!free_beams)
5157 			{
5158 				len = stop;
5159 				break;
5160 			}
5161 			if (b)
5162 			{
5163 				b = b->next = free_beams;
5164 				free_beams = free_beams->next;
5165 			}
5166 			else
5167 			{
5168 				b = bfirst = free_beams;
5169 				free_beams = free_beams->next;
5170 			}
5171 			b->texture_s = len; // not sure how to calc this
5172 			b->flags = 0;
5173 			b->p = p;
5174 			VectorCopy(vec, b->dir);
5175 		}
5176 
5177 		free_particles = p->next;
5178 		p->next = ptype->particles;
5179 		ptype->particles = p;
5180 
5181 		p->die = ptype->randdie*frandom();
5182 		p->scale = ptype->scale+ptype->scalerand*frandom();
5183 		if (ptype->die)
5184 			p->rgba[3] = ptype->alpha+p->die*ptype->alphachange;
5185 		else
5186 			p->rgba[3] = ptype->alpha;
5187 		p->rgba[3] += ptype->alpharand*frandom();
5188 //		p->color = 0;
5189 
5190 //		if (ptype->spawnmode == SM_TRACER)
5191 		if (ptype->spawnparam1)
5192 			tcount = (int)(len * ptype->count / ptype->spawnparam1);
5193 		else
5194 			tcount = (int)(len * ptype->count);
5195 
5196 		if (ptype->colorindex >= 0)
5197 		{
5198 			int cidx;
5199 			cidx = ptype->colorrand > 0 ? rand() % ptype->colorrand : 0;
5200 			if (ptype->flags & PT_CITRACER) // colorindex behavior as per tracers in std Q1
5201 				cidx += ((tcount & 4) << 1);
5202 
5203 			cidx = ptype->colorindex + cidx;
5204 			if (cidx > 255)
5205 				p->rgba[3] = p->rgba[3] / 2;
5206 			cidx = (cidx & 0xff) * 4;
5207 			p->rgba[0] = palrgba[cidx] * (1/255.0);
5208 			p->rgba[1] = palrgba[cidx+1] * (1/255.0);
5209 			p->rgba[2] = palrgba[cidx+2] * (1/255.0);
5210 		}
5211 		else
5212 			VectorCopy(ptype->rgb, p->rgba);
5213 
5214 		// use org temporarily for rgbsync
5215 		p->org[2] = frandom();
5216 		p->org[0] = p->org[2]*ptype->rgbrandsync[0] + frandom()*(1-ptype->rgbrandsync[0]);
5217 		p->org[1] = p->org[2]*ptype->rgbrandsync[1] + frandom()*(1-ptype->rgbrandsync[1]);
5218 		p->org[2] = p->org[2]*ptype->rgbrandsync[2] + frandom()*(1-ptype->rgbrandsync[2]);
5219 
5220 		p->rgba[0] += p->org[0]*ptype->rgbrand[0] + ptype->rgbchange[0]*p->die;
5221 		p->rgba[1] += p->org[1]*ptype->rgbrand[1] + ptype->rgbchange[1]*p->die;
5222 		p->rgba[2] += p->org[2]*ptype->rgbrand[2] + ptype->rgbchange[2]*p->die;
5223 
5224 		VectorClear (p->vel);
5225 		if (ptype->emittime < 0)
5226 			p->state.trailstate = NULL; // init trailstate
5227 		else
5228 			p->state.nextemit = particletime + ptype->emitstart - p->die;
5229 
5230 		p->rotationspeed = ptype->rotationmin + frandom()*ptype->rotationrand;
5231 		p->angle = ptype->rotationstartmin + frandom()*ptype->rotationstartrand;
5232 		p->s1 = ptype->s1;
5233 		p->t1 = ptype->t1;
5234 		p->s2 = ptype->s2;
5235 		p->t2 = ptype->t2;
5236 		if (ptype->randsmax!=1)
5237 		{
5238 			float offs;
5239 			offs = ptype->texsstride * (rand()%ptype->randsmax);
5240 			p->s1 += offs;
5241 			p->s2 += offs;
5242 			while (p->s1 >= 1)
5243 			{
5244 				p->s1 -= 1;
5245 				p->s2 -= 1;
5246 				p->t1 += ptype->texsstride;
5247 				p->t2 += ptype->texsstride;
5248 			}
5249 		}
5250 
5251 		if (len < nrfirst || len >= nrlast)
5252 		{
5253 			// no offset or areaspread for these particles...
5254 			p->vel[0] = vec[0]*veladd;
5255 			p->vel[1] = vec[1]*veladd;
5256 			p->vel[2] = vec[2]*veladd;
5257 
5258 			VectorCopy(start, p->org);
5259 		}
5260 		else
5261 		{
5262 			switch(ptype->spawnmode)
5263 			{
5264 			case SM_TRACER:
5265 				if (tcount & 1)
5266 				{
5267 					p->vel[0] = vec[1]*ptype->spawnvel;
5268 					p->vel[1] = -vec[0]*ptype->spawnvel;
5269 					p->org[0] = vec[1]*ptype->areaspread;
5270 					p->org[1] = -vec[0]*ptype->areaspread;
5271 				}
5272 				else
5273 				{
5274 					p->vel[0] = -vec[1]*ptype->spawnvel;
5275 					p->vel[1] = vec[0]*ptype->spawnvel;
5276 					p->org[0] = -vec[1]*ptype->areaspread;
5277 					p->org[1] = vec[0]*ptype->areaspread;
5278 				}
5279 
5280 				p->vel[0] += vec[0]*veladd;
5281 				p->vel[1] += vec[1]*veladd;
5282 				p->vel[2] = vec[2]*veladd;
5283 
5284 				p->org[0] += start[0];
5285 				p->org[1] += start[1];
5286 				p->org[2] = start[2];
5287 				break;
5288 			case SM_SPIRAL:
5289 				{
5290 					float tsin, tcos;
5291 					float tright, tup;
5292 
5293 					tcos = cos(len*tdegree+sdegree);
5294 					tsin = sin(len*tdegree+sdegree);
5295 
5296 					tright = tcos*ptype->areaspread;
5297 					tup = tsin*ptype->areaspread;
5298 
5299 					p->org[0] = start[0] + right[0]*tright + up[0]*tup;
5300 					p->org[1] = start[1] + right[1]*tright + up[1]*tup;
5301 					p->org[2] = start[2] + right[2]*tright + up[2]*tup;
5302 
5303 					tright = tcos*ptype->spawnvel;
5304 					tup = tsin*ptype->spawnvel;
5305 
5306 					p->vel[0] = vec[0]*veladd + right[0]*tright + up[0]*tup;
5307 					p->vel[1] = vec[1]*veladd + right[1]*tright + up[1]*tup;
5308 					p->vel[2] = vec[2]*veladd + right[2]*tright + up[2]*tup;
5309 				}
5310 				break;
5311 			// TODO: directionalize SM_BALL/SM_CIRCLE/SM_DISTBALL
5312 			case SM_BALL:
5313 				p->org[0] = crandom();
5314 				p->org[1] = crandom();
5315 				p->org[2] = crandom();
5316 				VectorNormalize(p->org);
5317 				VectorScale(p->org, frandom(), p->org);
5318 
5319 				p->vel[0] = vec[0]*veladd + p->org[0]*ptype->spawnvel;
5320 				p->vel[1] = vec[1]*veladd + p->org[1]*ptype->spawnvel;
5321 				p->vel[2] = vec[2]*veladd + p->org[2]*ptype->spawnvelvert;
5322 
5323 				p->org[0] = p->org[0]*ptype->areaspread + start[0];
5324 				p->org[1] = p->org[1]*ptype->areaspread + start[1];
5325 				p->org[2] = p->org[2]*ptype->areaspreadvert + start[2];
5326 				break;
5327 
5328 			case SM_CIRCLE:
5329 				{
5330 					float tsin, tcos;
5331 
5332 					tcos = cos(len*tdegree)*ptype->areaspread;
5333 					tsin = sin(len*tdegree)*ptype->areaspread;
5334 
5335 					p->org[0] = start[0] + right[0]*tcos + up[0]*tsin + vstep[0] * (len*tdegree);
5336 					p->org[1] = start[1] + right[1]*tcos + up[1]*tsin + vstep[1] * (len*tdegree);
5337 					p->org[2] = start[2] + right[2]*tcos + up[2]*tsin + vstep[2] * (len*tdegree)*50;
5338 
5339 					tcos = cos(len*tdegree)*ptype->spawnvel;
5340 					tsin = sin(len*tdegree)*ptype->spawnvel;
5341 
5342 					p->vel[0] = vec[0]*veladd + right[0]*tcos + up[0]*tsin;
5343 					p->vel[1] = vec[1]*veladd + right[1]*tcos + up[1]*tsin;
5344 					p->vel[2] = vec[2]*veladd + right[2]*tcos + up[2]*tsin;
5345 				}
5346 				break;
5347 
5348 			case SM_DISTBALL:
5349 				{
5350 					float rdist;
5351 
5352 					rdist = ptype->spawnparam2 - crandom()*(1-(crandom() * ptype->spawnparam1));
5353 
5354 					// this is a strange spawntype, which is based on the fact that
5355 					// crandom()*crandom() provides something similar to an exponential
5356 					// probability curve
5357 					p->org[0] = crandom();
5358 					p->org[1] = crandom();
5359 					p->org[2] = crandom();
5360 
5361 					VectorNormalize(p->org);
5362 					VectorScale(p->org, rdist, p->org);
5363 
5364 					p->vel[0] = vec[0]*veladd + p->org[0]*ptype->spawnvel;
5365 					p->vel[1] = vec[1]*veladd + p->org[1]*ptype->spawnvel;
5366 					p->vel[2] = vec[2]*veladd + p->org[2]*ptype->spawnvelvert;
5367 
5368 					p->org[0] = p->org[0]*ptype->areaspread + start[0];
5369 					p->org[1] = p->org[1]*ptype->areaspread + start[1];
5370 					p->org[2] = p->org[2]*ptype->areaspreadvert + start[2];
5371 				}
5372 				break;
5373 			default:
5374 				p->org[0] = crandom();
5375 				p->org[1] = crandom();
5376 				p->org[2] = crandom();
5377 
5378 				p->vel[0] = vec[0]*veladd + p->org[0]*ptype->spawnvel;
5379 				p->vel[1] = vec[1]*veladd + p->org[1]*ptype->spawnvel;
5380 				p->vel[2] = vec[2]*veladd + p->org[2]*ptype->spawnvelvert;
5381 
5382 				p->org[0] = p->org[0]*ptype->areaspread + start[0];
5383 				p->org[1] = p->org[1]*ptype->areaspread + start[1];
5384 				p->org[2] = p->org[2]*ptype->areaspreadvert + start[2];
5385 				break;
5386 			}
5387 
5388 			if (ptype->orgadd)
5389 			{
5390 				p->org[0] += vec[0]*ptype->orgadd;
5391 				p->org[1] += vec[1]*ptype->orgadd;
5392 				p->org[2] += vec[2]*ptype->orgadd;
5393 			}
5394 		}
5395 		if (ptype->flags & PT_WORLDSPACERAND)
5396 		{
5397 			vec3_t vtmp;
5398 			do
5399 			{
5400 				vtmp[0] = crand();
5401 				vtmp[1] = crand();
5402 				vtmp[2] = crand();
5403 			} while(DotProduct(vtmp,vtmp)>1);	//crap, but I'm trying to mimic dp
5404 			p->org[0] += vtmp[0] * ptype->orgwrand[0];
5405 			p->org[1] += vtmp[1] * ptype->orgwrand[1];
5406 			p->org[2] += vtmp[2] * ptype->orgwrand[2];
5407 			p->vel[0] += vtmp[0] * ptype->velwrand[0];
5408 			p->vel[1] += vtmp[1] * ptype->velwrand[1];
5409 			p->vel[2] += vtmp[2] * ptype->velwrand[2];
5410 			VectorAdd(p->vel, ptype->velbias, p->vel);
5411 		}
5412 		VectorAdd(p->org, ptype->orgbias, p->org);
5413 
5414 		VectorAdd (start, vstep, start);
5415 
5416 		if (ptype->countrand)
5417 		{
5418 			float rstep = frandom() / ptype->countrand;
5419 			VectorMA(start, rstep, vec, start);
5420 			step += rstep;
5421 		}
5422 
5423 		p->die = particletime + ptype->die - p->die;
5424 		VectorCopy(p->org, p->oldorg);
5425 	}
5426 
5427 	if (ts)
5428 	{
5429 		ts->state1.lastdist = len;
5430 
5431 		// update beamseg list
5432 		if (ptype->looks.type == PT_BEAM)
5433 		{
5434 			if (b)
5435 			{
5436 				if (ptype->beams)
5437 				{
5438 					if (ts->lastbeam)
5439 					{
5440 						b->next = ts->lastbeam->next;
5441 						ts->lastbeam->next = bfirst;
5442 						ts->lastbeam->flags &= ~BS_LASTSEG;
5443 					}
5444 					else
5445 					{
5446 						b->next = ptype->beams;
5447 						ptype->beams = bfirst;
5448 					}
5449 				}
5450 				else
5451 				{
5452 					ptype->beams = bfirst;
5453 					b->next = NULL;
5454 				}
5455 
5456 				b->flags |= BS_LASTSEG;
5457 				ts->lastbeam = b;
5458 			}
5459 
5460 			if ((!free_particles || !free_beams) && ts->lastbeam)
5461 			{
5462 				ts->lastbeam->flags &= ~BS_LASTSEG;
5463 				ts->lastbeam->flags |= BS_NODRAW;
5464 				ts->lastbeam = NULL;
5465 			}
5466 		}
5467 	}
5468 	else if (ptype->looks.type == PT_BEAM)
5469 	{
5470 		if (b)
5471 		{
5472 			b->flags |= BS_NODRAW;
5473 			b->next = ptype->beams;
5474 			ptype->beams = bfirst;
5475 		}
5476 	}
5477 
5478 	// maintain run list
5479 	if (!(ptype->state & PS_INRUNLIST))
5480 	{
5481 		ptype->nexttorun = part_run_list;
5482 		part_run_list = ptype;
5483 		ptype->state |= PS_INRUNLIST;
5484 	}
5485 
5486 	return;
5487 }
5488 
PScript_ParticleTrail(vec3_t startpos,vec3_t end,int type,float timeinterval,int dlkey,vec3_t axis[3],trailstate_t ** tsk)5489 int PScript_ParticleTrail (vec3_t startpos, vec3_t end, int type, float timeinterval, int dlkey, vec3_t axis[3], trailstate_t **tsk)
5490 {
5491 	part_type_t *ptype = &part_type[type];
5492 
5493 	if (r_fteparticles.value == 0)
5494 		return 1;
5495 
5496 	if (type < 0 || type >= numparticletypes)
5497 		return 1;	//bad value
5498 
5499 	if (!ptype->loaded)
5500 		return 1;
5501 
5502 	// inwater check, switch only once
5503 	if (r_part_contentswitch.value && ptype->inwater >= 0 && cl.worldmodel)
5504 	{
5505 		unsigned int cont;
5506 		cont = CL_PointContentsMask(startpos);
5507 
5508 		if (cont & FTECONTENTS_FLUID)
5509 			ptype = &part_type[ptype->inwater];
5510 	}
5511 
5512 	PScript_ParticleTrailSpawn (startpos, end, ptype, timeinterval, tsk, dlkey, axis);
5513 	return 0;
5514 }
5515 
5516 static int current_buffer_index = 0;
5517 static VkBuffer vertex_buffers[2] = {VK_NULL_HANDLE, VK_NULL_HANDLE};
5518 static vulkan_memory_t vertex_buffers_memory[2] = {{VK_NULL_HANDLE, 0, 0}, {VK_NULL_HANDLE, 0, 0}};
5519 static VkBuffer index_buffers[2] = {VK_NULL_HANDLE, VK_NULL_HANDLE};
5520 static vulkan_memory_t index_buffers_memory[2] = {{VK_NULL_HANDLE, 0, 0}, {VK_NULL_HANDLE, 0, 0}};
5521 
ReallocateVertexBuffer()5522 static void ReallocateVertexBuffer()
5523 {
5524 	VkResult err;
5525 
5526 	vulkan_memory_t old_memory = vertex_buffers_memory[current_buffer_index];
5527 	const basicvertex_t* old_cl_curstrisvert = cl_curstrisvert;
5528 	const int old_maxstrisvert = cl_maxstrisvert[current_buffer_index];
5529 
5530 	cl_maxstrisvert[current_buffer_index] = q_max(cl_maxstrisvert[current_buffer_index] * 2, INITIAL_NUM_VERTICES);
5531 	const VkDeviceSize new_size = cl_maxstrisvert[current_buffer_index] * sizeof(basicvertex_t);
5532 	Sys_Printf("Reallocating FTE particle vertex buffer (%u KB)\n", (int)(new_size / 1024));
5533 
5534 	VkBufferCreateInfo buffer_create_info;
5535 	memset(&buffer_create_info, 0, sizeof(buffer_create_info));
5536 	buffer_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
5537 	buffer_create_info.size = new_size;
5538 	buffer_create_info.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
5539 
5540 	err = vkCreateBuffer(vulkan_globals.device, &buffer_create_info, NULL, &vertex_buffers[current_buffer_index]);
5541 	if (err != VK_SUCCESS)
5542 		Sys_Error("vkCreateBuffer failed");
5543 	GL_SetObjectName((uint64_t)vertex_buffers[current_buffer_index], VK_OBJECT_TYPE_BUFFER, "FTE Particle Vertex Buffer");
5544 
5545 	VkMemoryRequirements memory_requirements;
5546 	vkGetBufferMemoryRequirements(vulkan_globals.device, vertex_buffers[current_buffer_index], &memory_requirements);
5547 
5548 	const int align_mod = memory_requirements.size % memory_requirements.alignment;
5549 	const int aligned_size = ((memory_requirements.size % memory_requirements.alignment) == 0)
5550 		? memory_requirements.size
5551 		: (memory_requirements.size + memory_requirements.alignment - align_mod);
5552 
5553 	VkMemoryAllocateInfo memory_allocate_info;
5554 	memset(&memory_allocate_info, 0, sizeof(memory_allocate_info));
5555 	memory_allocate_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
5556 	memory_allocate_info.allocationSize = aligned_size;
5557 	memory_allocate_info.memoryTypeIndex = GL_MemoryTypeFromProperties(memory_requirements.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, VK_MEMORY_PROPERTY_HOST_CACHED_BIT);
5558 
5559 	R_AllocateVulkanMemory(&vertex_buffers_memory[current_buffer_index], &memory_allocate_info, VULKAN_MEMORY_TYPE_HOST);
5560 	GL_SetObjectName((uint64_t)vertex_buffers_memory[current_buffer_index].handle, VK_OBJECT_TYPE_DEVICE_MEMORY, "FTE Particle Vertex Buffer");
5561 
5562 	err = vkBindBufferMemory(vulkan_globals.device, vertex_buffers[current_buffer_index], vertex_buffers_memory[current_buffer_index].handle, 0);
5563 	if (err != VK_SUCCESS)
5564 		Sys_Error("vkBindBufferMemory failed");
5565 
5566 	err = vkMapMemory(vulkan_globals.device, vertex_buffers_memory[current_buffer_index].handle, 0, new_size, 0, (void**)&cl_curstrisvert);
5567 	if (err != VK_SUCCESS)
5568 		Sys_Error("vkMapMemory failed");
5569 	cl_strisvert[current_buffer_index] = cl_curstrisvert;
5570 
5571 	if (old_memory.handle != VK_NULL_HANDLE)
5572 	{
5573 		// Copy over data from old buffer
5574 		memcpy(cl_curstrisvert, old_cl_curstrisvert, old_maxstrisvert * sizeof(basicvertex_t));
5575 
5576 		vkUnmapMemory(vulkan_globals.device, old_memory.handle);
5577 		R_FreeVulkanMemory(&old_memory);
5578 	}
5579 }
5580 
ReallocateIndexBuffer()5581 static void ReallocateIndexBuffer()
5582 {
5583 	VkResult err;
5584 
5585 	vulkan_memory_t old_memory = index_buffers_memory[current_buffer_index];
5586 	const unsigned short * old_cl_curstrisidx = cl_curstrisidx;
5587 	const int old_maxstrisidx = cl_maxstrisidx[current_buffer_index];
5588 
5589 	cl_maxstrisidx[current_buffer_index] = q_max(cl_maxstrisidx[current_buffer_index] * 2, INITIAL_NUM_INDICES);
5590 	const VkDeviceSize new_size = cl_maxstrisidx[current_buffer_index] * sizeof(unsigned short);
5591 	Sys_Printf("Reallocating FTE particle index buffer (%u KB)\n", (int)(new_size / 1024));
5592 
5593 	VkBufferCreateInfo buffer_create_info;
5594 	memset(&buffer_create_info, 0, sizeof(buffer_create_info));
5595 	buffer_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
5596 	buffer_create_info.size = new_size;
5597 	buffer_create_info.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
5598 
5599 	err = vkCreateBuffer(vulkan_globals.device, &buffer_create_info, NULL, &index_buffers[current_buffer_index]);
5600 	if (err != VK_SUCCESS)
5601 		Sys_Error("vkCreateBuffer failed");
5602 	GL_SetObjectName((uint64_t)index_buffers[current_buffer_index], VK_OBJECT_TYPE_BUFFER, "FTE Particle Index Buffer");
5603 
5604 	VkMemoryRequirements memory_requirements;
5605 	vkGetBufferMemoryRequirements(vulkan_globals.device, index_buffers[current_buffer_index], &memory_requirements);
5606 
5607 	const int align_mod = memory_requirements.size % memory_requirements.alignment;
5608 	const int aligned_size = ((memory_requirements.size % memory_requirements.alignment) == 0)
5609 		? memory_requirements.size
5610 		: (memory_requirements.size + memory_requirements.alignment - align_mod);
5611 
5612 	VkMemoryAllocateInfo memory_allocate_info;
5613 	memset(&memory_allocate_info, 0, sizeof(memory_allocate_info));
5614 	memory_allocate_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
5615 	memory_allocate_info.allocationSize = aligned_size;
5616 	memory_allocate_info.memoryTypeIndex = GL_MemoryTypeFromProperties(memory_requirements.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, VK_MEMORY_PROPERTY_HOST_CACHED_BIT);
5617 
5618 	R_AllocateVulkanMemory(&index_buffers_memory[current_buffer_index], &memory_allocate_info, VULKAN_MEMORY_TYPE_HOST);
5619 	GL_SetObjectName((uint64_t)index_buffers_memory[current_buffer_index].handle, VK_OBJECT_TYPE_DEVICE_MEMORY, "FTE Particle index Buffer");
5620 
5621 	err = vkBindBufferMemory(vulkan_globals.device, index_buffers[current_buffer_index], index_buffers_memory[current_buffer_index].handle, 0);
5622 	if (err != VK_SUCCESS)
5623 		Sys_Error("vkBindBufferMemory failed");
5624 
5625 	err = vkMapMemory(vulkan_globals.device, index_buffers_memory[current_buffer_index].handle, 0, new_size, 0, (void**)&cl_curstrisidx);
5626 	if (err != VK_SUCCESS)
5627 		Sys_Error("vkMapMemory failed");
5628 	cl_strisidx[current_buffer_index] = cl_curstrisidx;
5629 
5630 	if (old_memory.handle != VK_NULL_HANDLE)
5631 	{
5632 		// Copy over data from old buffer
5633 		memcpy(cl_curstrisidx, old_cl_curstrisidx, old_maxstrisidx * sizeof(unsigned short));
5634 
5635 		vkUnmapMemory(vulkan_globals.device, old_memory.handle);
5636 		R_FreeVulkanMemory(&old_memory);
5637 	}
5638 }
5639 
5640 static vec3_t pright, pup;
5641 
R_AddFanSparkParticle(scenetris_t * t,particle_t * p,plooks_t * type)5642 static void R_AddFanSparkParticle(scenetris_t *t, particle_t *p, plooks_t *type)
5643 {
5644 	vec3_t v, cr, o2;
5645 	float scale;
5646 
5647 	if (cl_numstrisvert+3 > cl_maxstrisvert[current_buffer_index])
5648 		ReallocateVertexBuffer();
5649 
5650 	scale = (p->org[0] - r_origin[0])*vpn[0] + (p->org[1] - r_origin[1])*vpn[1]
5651 		+ (p->org[2] - r_origin[2])*vpn[2];
5652 	scale = (scale*p->scale)*(type->invscalefactor) + p->scale * (type->scalefactor*250);
5653 	if (scale < 20)
5654 		scale = 0.05;
5655 	else
5656 		scale = 0.05 + scale * 0.0001;
5657 
5658 	if (type->premul)
5659 	{
5660 		vec4_t rgba;
5661 		float a = p->rgba[3];
5662 		if (a > 1)
5663 			a = 1;
5664 		a *= 255.0f;
5665 		rgba[0] = p->rgba[0] * a;
5666 		rgba[1] = p->rgba[1] * a;
5667 		rgba[2] = p->rgba[2] * a;
5668 		rgba[3] = (type->premul==2)?0:a;
5669 		Vector4ToColor(rgba, cl_curstrisvert[cl_numstrisvert+0].color);
5670 		Vector4ToColor(rgba, cl_curstrisvert[cl_numstrisvert+1].color);
5671 		Vector4ToColor(rgba, cl_curstrisvert[cl_numstrisvert+2].color);
5672 	}
5673 	else
5674 	{
5675 		Vector4ToColor(p->rgba, cl_curstrisvert[cl_numstrisvert+0].color);
5676 		Vector4ToColor(p->rgba, cl_curstrisvert[cl_numstrisvert+1].color);
5677 		Vector4ToColor(p->rgba, cl_curstrisvert[cl_numstrisvert+2].color);
5678 	}
5679 
5680 	Vector2Set(cl_curstrisvert[cl_numstrisvert+0].texcoord, p->s1, p->t1);
5681 	Vector2Set(cl_curstrisvert[cl_numstrisvert+1].texcoord, p->s1, p->t2);
5682 	Vector2Set(cl_curstrisvert[cl_numstrisvert+2].texcoord, p->s2, p->t1);
5683 
5684 
5685 	VectorMA(p->org, -scale, p->vel, o2);
5686 	VectorSubtract(r_refdef.vieworg, o2, v);
5687 	CrossProduct(v, p->vel, cr);
5688 	VectorNormalize(cr);
5689 
5690 	VectorCopy(p->org, cl_curstrisvert[cl_numstrisvert+0].position);
5691 	VectorMA(o2, -p->scale, cr, cl_curstrisvert[cl_numstrisvert+1].position);
5692 	VectorMA(o2, p->scale, cr, cl_curstrisvert[cl_numstrisvert+2].position);
5693 
5694 	if (cl_numstrisidx+3 > cl_maxstrisidx[current_buffer_index])
5695 		ReallocateIndexBuffer();
5696 
5697 	cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 0;
5698 	cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 1;
5699 	cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 2;
5700 
5701 	cl_numstrisvert += 3;
5702 
5703 	t->numvert += 3;
5704 	t->numidx += 3;
5705 
5706 }
5707 
R_AddLineSparkParticle(scenetris_t * t,particle_t * p,plooks_t * type)5708 static void R_AddLineSparkParticle(scenetris_t *t, particle_t *p, plooks_t *type)
5709 {
5710 	if (cl_numstrisvert+2 > cl_maxstrisvert[current_buffer_index])
5711 		ReallocateVertexBuffer();
5712 
5713 	if (type->premul)
5714 	{
5715 		vec4_t scaled_color;
5716 		float a = p->rgba[3];
5717 		if (a > 1)
5718 			a = 1;
5719 		VectorScale(p->rgba, a, scaled_color);
5720 		Vector3ToColor(scaled_color, cl_curstrisvert[cl_numstrisvert+0].color);
5721 		FloatToColor((type->premul==2)?0:a, cl_curstrisvert[cl_numstrisvert+0].color[3]);
5722 		Vector4Clear(cl_curstrisvert[cl_numstrisvert+1].color);
5723 	}
5724 	else
5725 	{
5726 		Vector4ToColor(p->rgba, cl_curstrisvert[cl_numstrisvert+0].color);
5727 		Vector3ToColor(p->rgba, cl_curstrisvert[cl_numstrisvert+1].color);
5728 		cl_curstrisvert[cl_numstrisvert+1].color[3] = 0;
5729 	}
5730 	Vector2Set(cl_curstrisvert[cl_numstrisvert+0].texcoord, p->s1, p->t1);
5731 	Vector2Set(cl_curstrisvert[cl_numstrisvert+1].texcoord, p->s2, p->t2);
5732 
5733 	VectorCopy(p->org, cl_curstrisvert[cl_numstrisvert+0].position);
5734 	VectorMA(p->org, -1.0/10, p->vel, cl_curstrisvert[cl_numstrisvert+1].position);
5735 
5736 	if (cl_numstrisidx+2 > cl_maxstrisidx[current_buffer_index])
5737 		ReallocateIndexBuffer();
5738 
5739 	cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 0;
5740 	cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 1;
5741 
5742 	cl_numstrisvert += 2;
5743 
5744 	t->numvert += 2;
5745 	t->numidx += 2;
5746 }
5747 
R_AddTSparkParticle(scenetris_t * t,particle_t * p,plooks_t * type)5748 static void R_AddTSparkParticle(scenetris_t *t, particle_t *p, plooks_t *type)
5749 {
5750 	vec3_t v, cr, o2;
5751 
5752 	if (cl_numstrisvert+4 > cl_maxstrisvert[current_buffer_index])
5753 		ReallocateVertexBuffer();
5754 
5755 	if (type->premul)
5756 	{
5757 		vec4_t rgba;
5758 		float a = p->rgba[3];
5759 		if (a > 1)
5760 			a = 1;
5761 		rgba[0] = p->rgba[0] * a;
5762 		rgba[1] = p->rgba[1] * a;
5763 		rgba[2] = p->rgba[2] * a;
5764 		rgba[3] = (type->premul==2)?0:a;
5765 		Vector4ToColor(rgba, cl_curstrisvert[cl_numstrisvert+0].color);
5766 		Vector4ToColor(rgba, cl_curstrisvert[cl_numstrisvert+1].color);
5767 		Vector4ToColor(rgba, cl_curstrisvert[cl_numstrisvert+2].color);
5768 		Vector4ToColor(rgba, cl_curstrisvert[cl_numstrisvert+3].color);
5769 	}
5770 	else
5771 	{
5772 		Vector4ToColor(p->rgba, cl_curstrisvert[cl_numstrisvert+0].color);
5773 		Vector4ToColor(p->rgba, cl_curstrisvert[cl_numstrisvert+1].color);
5774 		Vector4ToColor(p->rgba, cl_curstrisvert[cl_numstrisvert+2].color);
5775 		Vector4ToColor(p->rgba, cl_curstrisvert[cl_numstrisvert+3].color);
5776 	}
5777 
5778 	Vector2Set(cl_curstrisvert[cl_numstrisvert+0].texcoord, p->s1, p->t1);
5779 	Vector2Set(cl_curstrisvert[cl_numstrisvert+1].texcoord, p->s1, p->t2);
5780 	Vector2Set(cl_curstrisvert[cl_numstrisvert+2].texcoord, p->s2, p->t2);
5781 	Vector2Set(cl_curstrisvert[cl_numstrisvert+3].texcoord, p->s2, p->t1);
5782 
5783 	{
5784 		vec3_t movedir;
5785 		float halfscale = p->scale*0.5;
5786 		float length = VectorNormalize2(p->vel, movedir);
5787 		if (type->stretch < 0)
5788 			length = -type->stretch;	//fixed lengths
5789 		else if (type->stretch)
5790 			length *= type->stretch;	//velocity multiplier
5791 		else
5792 			Sys_Error("type->stretch should be 0.05\n");
5793 //			length *= 0.05;				//fallback
5794 
5795 		if (length < halfscale * type->minstretch)
5796 			length = halfscale * type->minstretch;
5797 
5798 		VectorMA(p->org, -length, movedir, o2);
5799 		VectorSubtract(r_refdef.vieworg, o2, v);
5800 		CrossProduct(v, p->vel, cr);
5801 		VectorNormalize(cr);
5802 		VectorMA(o2, -p->scale/2, cr, cl_curstrisvert[cl_numstrisvert+0].position);
5803 		VectorMA(o2, p->scale/2, cr, cl_curstrisvert[cl_numstrisvert+1].position);
5804 
5805 		VectorMA(p->org, length, movedir, o2);
5806 	}
5807 
5808 	VectorSubtract(r_refdef.vieworg, o2, v);
5809 	CrossProduct(v, p->vel, cr);
5810 	VectorNormalize(cr);
5811 
5812 	VectorMA(o2, p->scale*0.5, cr, cl_curstrisvert[cl_numstrisvert+2].position);
5813 	VectorMA(o2, -p->scale*0.5, cr, cl_curstrisvert[cl_numstrisvert+3].position);
5814 
5815 
5816 
5817 	if (cl_numstrisidx+6 > cl_maxstrisidx[current_buffer_index])
5818 		ReallocateIndexBuffer();
5819 
5820 	cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 0;
5821 	cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 1;
5822 	cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 2;
5823 	cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 0;
5824 	cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 2;
5825 	cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 3;
5826 
5827 	cl_numstrisvert += 4;
5828 
5829 	t->numvert += 4;
5830 	t->numidx += 6;
5831 }
5832 
R_DrawParticleBeam(scenetris_t * t,beamseg_t * b,plooks_t * type)5833 static void R_DrawParticleBeam(scenetris_t *t, beamseg_t *b, plooks_t *type)
5834 {
5835 	vec3_t v;
5836 	vec3_t cr;
5837 	beamseg_t *c;
5838 	particle_t *p;
5839 	particle_t *q;
5840 	float ts;
5841 
5842 	c = b->next;
5843 
5844 	q = c->p;
5845 	if (!q)
5846 		return;
5847 	p = b->p;
5848 
5849 	if (cl_numstrisvert+4 > cl_maxstrisvert[current_buffer_index])
5850 		ReallocateVertexBuffer();
5851 
5852 	VectorSubtract(r_refdef.vieworg, q->org, v);
5853 	VectorNormalize(v);
5854 	CrossProduct(c->dir, v, cr);
5855 	VectorNormalize(cr);
5856 	ts = c->texture_s*q->angle + particletime*q->rotationspeed;
5857 	Vector4ToColor(q->rgba, cl_curstrisvert[cl_numstrisvert+0].color);
5858 	Vector4ToColor(q->rgba, cl_curstrisvert[cl_numstrisvert+1].color);
5859 	Vector2Set(cl_curstrisvert[cl_numstrisvert+0].texcoord, ts, p->t1);
5860 	Vector2Set(cl_curstrisvert[cl_numstrisvert+1].texcoord, ts, p->t2);
5861 	VectorMA(q->org, -q->scale, cr, cl_curstrisvert[cl_numstrisvert+0].position);
5862 	VectorMA(q->org, q->scale, cr, cl_curstrisvert[cl_numstrisvert+1].position);
5863 
5864 	VectorSubtract(r_refdef.vieworg, p->org, v);
5865 	VectorNormalize(v);
5866 	CrossProduct(b->dir, v, cr); // replace with old p->dir?
5867 	VectorNormalize(cr);
5868 	ts = b->texture_s*p->angle + particletime*p->rotationspeed;
5869 	Vector4ToColor(p->rgba, cl_curstrisvert[cl_numstrisvert+2].color);
5870 	Vector4ToColor(p->rgba, cl_curstrisvert[cl_numstrisvert+3].color);
5871 	Vector2Set(cl_curstrisvert[cl_numstrisvert+2].texcoord, ts, p->t2);
5872 	Vector2Set(cl_curstrisvert[cl_numstrisvert+3].texcoord, ts, p->t1);
5873 	VectorMA(p->org, p->scale, cr, cl_curstrisvert[cl_numstrisvert+2].position);
5874 	VectorMA(p->org, -p->scale, cr, cl_curstrisvert[cl_numstrisvert+3].position);
5875 
5876 	t->numvert += 4;
5877 
5878 	if (cl_numstrisidx+6 > cl_maxstrisidx[current_buffer_index])
5879 		ReallocateIndexBuffer();
5880 
5881 	cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 0;
5882 	cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 1;
5883 	cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 2;
5884 	cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 0;
5885 	cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 2;
5886 	cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 3;
5887 	cl_numstrisvert += 4;
5888 	t->numidx += 4;
5889 }
5890 
R_AddClippedDecal(scenetris_t * t,clippeddecal_t * d,plooks_t * type)5891 static void R_AddClippedDecal(scenetris_t *t, clippeddecal_t *d, plooks_t *type)
5892 {
5893 	if (cl_numstrisvert+4 > cl_maxstrisvert[current_buffer_index])
5894 		ReallocateVertexBuffer();
5895 
5896 	if (d->entity > 0)
5897 	{
5898 		entity_t *le = CL_EntityNum(d->entity);
5899 		if (le->angles[0] || le->angles[1] || le->angles[2])
5900 		{	//FIXME: deal with rotated entities.
5901 			d->die = -1;
5902 			return;
5903 		}
5904 		VectorAdd(d->vertex[0], le->origin, cl_curstrisvert[cl_numstrisvert+0].position);
5905 		VectorAdd(d->vertex[1], le->origin, cl_curstrisvert[cl_numstrisvert+1].position);
5906 		VectorAdd(d->vertex[2], le->origin, cl_curstrisvert[cl_numstrisvert+2].position);
5907 	}
5908 	else
5909 	{
5910 		VectorCopy(d->vertex[0], cl_curstrisvert[cl_numstrisvert+0].position);
5911 		VectorCopy(d->vertex[1], cl_curstrisvert[cl_numstrisvert+1].position);
5912 		VectorCopy(d->vertex[2], cl_curstrisvert[cl_numstrisvert+2].position);
5913 	}
5914 
5915 	if (type->premul)
5916 	{
5917 		vec4_t rgba;
5918 		vec4_t scaled_color;
5919 		float a = d->rgba[3];
5920 		if (a > 1)
5921 			a = 1;
5922 		rgba[0] = d->rgba[0] * a;
5923 		rgba[1] = d->rgba[1] * a;
5924 		rgba[2] = d->rgba[2] * a;
5925 		rgba[3] = (type->premul==2)?0:a;
5926 		Vector4Scale(rgba, d->valpha[0], scaled_color);
5927 		Vector4ToColor(scaled_color, cl_curstrisvert[cl_numstrisvert+0].color);
5928 		Vector4Scale(rgba, d->valpha[1], scaled_color);
5929 		Vector4ToColor(scaled_color, cl_curstrisvert[cl_numstrisvert+1].color);
5930 		Vector4Scale(rgba, d->valpha[2], scaled_color);
5931 		Vector4ToColor(scaled_color, cl_curstrisvert[cl_numstrisvert+2].color);
5932 	}
5933 	else
5934 	{
5935 		vec4_t rgba;
5936 		rgba[0] = d->rgba[0];
5937 		rgba[1] = d->rgba[1];
5938 		rgba[2] = d->rgba[2];
5939 		rgba[3] = d->rgba[3] * d->valpha[0];
5940 		Vector4ToColor(rgba, cl_curstrisvert[cl_numstrisvert+0].color);
5941 		rgba[3] = d->rgba[3] * d->valpha[1];
5942 		Vector4ToColor(rgba, cl_curstrisvert[cl_numstrisvert+1].color);
5943 		rgba[3] = d->rgba[3] * d->valpha[2];
5944 		Vector4ToColor(rgba, cl_curstrisvert[cl_numstrisvert+2].color);
5945 	}
5946 
5947 	Vector2Copy(d->texcoords[0], cl_curstrisvert[cl_numstrisvert+0].texcoord);
5948 	Vector2Copy(d->texcoords[1], cl_curstrisvert[cl_numstrisvert+1].texcoord);
5949 	Vector2Copy(d->texcoords[2], cl_curstrisvert[cl_numstrisvert+2].texcoord);
5950 
5951 	if (cl_numstrisidx+3 > cl_maxstrisidx[current_buffer_index])
5952 		ReallocateIndexBuffer();
5953 
5954 	cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 0;
5955 	cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 1;
5956 	cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 2;
5957 
5958 	cl_numstrisvert += 3;
5959 
5960 	t->numvert += 3;
5961 	t->numidx += 3;
5962 }
5963 
R_AddUnclippedDecal(scenetris_t * t,particle_t * p,plooks_t * type)5964 static void R_AddUnclippedDecal(scenetris_t *t, particle_t *p, plooks_t *type)
5965 {
5966 	float x, y;
5967 	vec3_t sdir, tdir;
5968 
5969 	if (cl_numstrisvert+4 > cl_maxstrisvert[current_buffer_index])
5970 		ReallocateVertexBuffer();
5971 
5972 	if (type->premul)
5973 	{
5974 		vec4_t rgba;
5975 		float a = p->rgba[3];
5976 		if (a > 1)
5977 			a = 1;
5978 		rgba[0] = p->rgba[0] * a;
5979 		rgba[1] = p->rgba[1] * a;
5980 		rgba[2] = p->rgba[2] * a;
5981 		rgba[3] = (type->premul==2)?0:a;
5982 		Vector4ToColor(rgba, cl_curstrisvert[cl_numstrisvert+0].color);
5983 		Vector4ToColor(rgba, cl_curstrisvert[cl_numstrisvert+1].color);
5984 		Vector4ToColor(rgba, cl_curstrisvert[cl_numstrisvert+2].color);
5985 		Vector4ToColor(rgba, cl_curstrisvert[cl_numstrisvert+3].color);
5986 	}
5987 	else
5988 	{
5989 		Vector4ToColor(p->rgba, cl_curstrisvert[cl_numstrisvert+0].color);
5990 		Vector4ToColor(p->rgba, cl_curstrisvert[cl_numstrisvert+1].color);
5991 		Vector4ToColor(p->rgba, cl_curstrisvert[cl_numstrisvert+2].color);
5992 		Vector4ToColor(p->rgba, cl_curstrisvert[cl_numstrisvert+3].color);
5993 	}
5994 
5995 	Vector2Set(cl_curstrisvert[cl_numstrisvert+0].texcoord, p->s1, p->t1);
5996 	Vector2Set(cl_curstrisvert[cl_numstrisvert+1].texcoord, p->s1, p->t2);
5997 	Vector2Set(cl_curstrisvert[cl_numstrisvert+2].texcoord, p->s2, p->t2);
5998 	Vector2Set(cl_curstrisvert[cl_numstrisvert+3].texcoord, p->s2, p->t1);
5999 
6000 //	if (p->vel[1] == 1)
6001 	{
6002 		VectorSet(sdir, 1, 0, 0);
6003 		VectorSet(tdir, 0, 1, 0);
6004 	}
6005 
6006 	if (p->angle)
6007 	{
6008 		x = sin(p->angle)*p->scale;
6009 		y = cos(p->angle)*p->scale;
6010 
6011 		cl_curstrisvert[cl_numstrisvert+0].position[0] = p->org[0] - x*sdir[0] - y*tdir[0];
6012 		cl_curstrisvert[cl_numstrisvert+0].position[1] = p->org[1] - x*sdir[1] - y*tdir[1];
6013 		cl_curstrisvert[cl_numstrisvert+0].position[2] = p->org[2] - x*sdir[2] - y*tdir[2];
6014 		cl_curstrisvert[cl_numstrisvert+1].position[0] = p->org[0] - y*sdir[0] + x*tdir[0];
6015 		cl_curstrisvert[cl_numstrisvert+1].position[1] = p->org[1] - y*sdir[1] + x*tdir[1];
6016 		cl_curstrisvert[cl_numstrisvert+1].position[2] = p->org[2] - y*sdir[2] + x*tdir[2];
6017 		cl_curstrisvert[cl_numstrisvert+2].position[0] = p->org[0] + x*sdir[0] + y*tdir[0];
6018 		cl_curstrisvert[cl_numstrisvert+2].position[1] = p->org[1] + x*sdir[1] + y*tdir[1];
6019 		cl_curstrisvert[cl_numstrisvert+2].position[2] = p->org[2] + x*sdir[2] + y*tdir[2];
6020 		cl_curstrisvert[cl_numstrisvert+3].position[0] = p->org[0] + y*sdir[0] - x*tdir[0];
6021 		cl_curstrisvert[cl_numstrisvert+3].position[1] = p->org[1] + y*sdir[1] - x*tdir[1];
6022 		cl_curstrisvert[cl_numstrisvert+3].position[2] = p->org[2] + y*sdir[2] - x*tdir[2];
6023 	}
6024 	else
6025 	{
6026 		VectorMA(p->org, -p->scale, tdir, cl_curstrisvert[cl_numstrisvert+0].position);
6027 		VectorMA(p->org, -p->scale, sdir, cl_curstrisvert[cl_numstrisvert+1].position);
6028 		VectorMA(p->org, p->scale, tdir, cl_curstrisvert[cl_numstrisvert+2].position);
6029 		VectorMA(p->org, p->scale, sdir, cl_curstrisvert[cl_numstrisvert+3].position);
6030 	}
6031 
6032 	if (cl_numstrisidx+6 > cl_maxstrisidx[current_buffer_index])
6033 		ReallocateIndexBuffer();
6034 
6035 	cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 0;
6036 	cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 1;
6037 	cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 2;
6038 	cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 0;
6039 	cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 2;
6040 	cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 3;
6041 
6042 	cl_numstrisvert += 4;
6043 
6044 	t->numvert += 4;
6045 	t->numidx += 6;
6046 }
6047 
R_AddTexturedParticle(scenetris_t * t,particle_t * p,plooks_t * type)6048 static void R_AddTexturedParticle(scenetris_t *t, particle_t *p, plooks_t *type)
6049 {
6050 	float scale, x, y;
6051 
6052 	if (cl_numstrisvert+4 > cl_maxstrisvert[current_buffer_index])
6053 		ReallocateVertexBuffer();
6054 
6055 	if (type->scalefactor == 1)
6056 		scale = p->scale*0.25;
6057 	else
6058 	{
6059 		scale = (p->org[0] - r_origin[0])*vpn[0] + (p->org[1] - r_origin[1])*vpn[1]
6060 			+ (p->org[2] - r_origin[2])*vpn[2];
6061 		scale = (scale*p->scale)*(type->invscalefactor) + p->scale * (type->scalefactor*250);
6062 		if (scale < 20)
6063 			scale = 0.25;
6064 		else
6065 			scale = 0.25 + scale * 0.001;
6066 	}
6067 
6068 	if (type->premul)
6069 	{
6070 		vec4_t rgba;
6071 		float a = p->rgba[3];
6072 		if (a > 1)
6073 			a = 1;
6074 		rgba[0] = p->rgba[0] * a;
6075 		rgba[1] = p->rgba[1] * a;
6076 		rgba[2] = p->rgba[2] * a;
6077 		rgba[3] = (type->premul==2)?0:a;
6078 		Vector4ToColor(rgba, cl_curstrisvert[cl_numstrisvert+0].color);
6079 		Vector4ToColor(rgba, cl_curstrisvert[cl_numstrisvert+1].color);
6080 		Vector4ToColor(rgba, cl_curstrisvert[cl_numstrisvert+2].color);
6081 		Vector4ToColor(rgba, cl_curstrisvert[cl_numstrisvert+3].color);
6082 	}
6083 	else
6084 	{
6085 		Vector4ToColor(p->rgba, cl_curstrisvert[cl_numstrisvert+0].color);
6086 		Vector4ToColor(p->rgba, cl_curstrisvert[cl_numstrisvert+1].color);
6087 		Vector4ToColor(p->rgba, cl_curstrisvert[cl_numstrisvert+2].color);
6088 		Vector4ToColor(p->rgba, cl_curstrisvert[cl_numstrisvert+3].color);
6089 	}
6090 
6091 	Vector2Set(cl_curstrisvert[cl_numstrisvert+0].texcoord, p->s1, p->t1);
6092 	Vector2Set(cl_curstrisvert[cl_numstrisvert+1].texcoord, p->s1, p->t2);
6093 	Vector2Set(cl_curstrisvert[cl_numstrisvert+2].texcoord, p->s2, p->t2);
6094 	Vector2Set(cl_curstrisvert[cl_numstrisvert+3].texcoord, p->s2, p->t1);
6095 
6096 	if (p->angle)
6097 	{
6098 		x = sin(p->angle)*scale;
6099 		y = cos(p->angle)*scale;
6100 
6101 		cl_curstrisvert[cl_numstrisvert+0].position[0] = p->org[0] - x*pright[0] - y*pup[0];
6102 		cl_curstrisvert[cl_numstrisvert+0].position[1] = p->org[1] - x*pright[1] - y*pup[1];
6103 		cl_curstrisvert[cl_numstrisvert+0].position[2] = p->org[2] - x*pright[2] - y*pup[2];
6104 		cl_curstrisvert[cl_numstrisvert+1].position[0] = p->org[0] - y*pright[0] + x*pup[0];
6105 		cl_curstrisvert[cl_numstrisvert+1].position[1] = p->org[1] - y*pright[1] + x*pup[1];
6106 		cl_curstrisvert[cl_numstrisvert+1].position[2] = p->org[2] - y*pright[2] + x*pup[2];
6107 		cl_curstrisvert[cl_numstrisvert+2].position[0] = p->org[0] + x*pright[0] + y*pup[0];
6108 		cl_curstrisvert[cl_numstrisvert+2].position[1] = p->org[1] + x*pright[1] + y*pup[1];
6109 		cl_curstrisvert[cl_numstrisvert+2].position[2] = p->org[2] + x*pright[2] + y*pup[2];
6110 		cl_curstrisvert[cl_numstrisvert+3].position[0] = p->org[0] + y*pright[0] - x*pup[0];
6111 		cl_curstrisvert[cl_numstrisvert+3].position[1] = p->org[1] + y*pright[1] - x*pup[1];
6112 		cl_curstrisvert[cl_numstrisvert+3].position[2] = p->org[2] + y*pright[2] - x*pup[2];
6113 	}
6114 	else
6115 	{
6116 		VectorMA(p->org, -scale, pup, cl_curstrisvert[cl_numstrisvert+0].position);
6117 		VectorMA(p->org, -scale, pright, cl_curstrisvert[cl_numstrisvert+1].position);
6118 		VectorMA(p->org, scale, pup, cl_curstrisvert[cl_numstrisvert+2].position);
6119 		VectorMA(p->org, scale, pright, cl_curstrisvert[cl_numstrisvert+3].position);
6120 	}
6121 
6122 	if (cl_numstrisidx+6 > cl_maxstrisidx[current_buffer_index])
6123 		ReallocateIndexBuffer();
6124 
6125 	cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 0;
6126 	cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 1;
6127 	cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 2;
6128 	cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 0;
6129 	cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 2;
6130 	cl_curstrisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 3;
6131 
6132 	cl_numstrisvert += 4;
6133 
6134 	t->numvert += 4;
6135 	t->numidx += 6;
6136 }
6137 
PScript_DrawParticleTypes(float pframetime)6138 static void PScript_DrawParticleTypes (float pframetime)
6139 {
6140 	void (*bdraw)(scenetris_t *t, beamseg_t *p, plooks_t *type);
6141 	void (*tdraw)(scenetris_t *t, particle_t *p, plooks_t *type);
6142 
6143 	vec3_t oldorg;
6144 	vec3_t stop, normal;
6145 	part_type_t *type, *lastvalidtype;
6146 	particle_t		*p, *kill;
6147 	clippeddecal_t *d, *dkill;
6148 	ramp_t *ramp;
6149 	float grav;
6150 	vec3_t friction;
6151 	scenetris_t *scenetri;
6152 	float dist;
6153 	particle_t *kill_list, *kill_first;	//the kill list is to stop particles from being freed and reused whilst still in this loop
6154 										//which is bad because beams need to find out when particles died. Reuse can do wierd things.
6155 										//remember that they're not drawn instantly either.
6156 	beamseg_t *b, *bkill;
6157 
6158 	int traces=r_particle_tracelimit.value;
6159 	int rampind;
6160 	static float flurrytime;
6161 	qboolean doflurry;
6162 	int batchflags;
6163 	unsigned int i, o;
6164 
6165 	if (r_plooksdirty)
6166 	{
6167 		int j, k;
6168 
6169 		pe_default			= PScript_FindParticleType("PE_DEFAULT");
6170 		pe_size2			= PScript_FindParticleType("PE_SIZE2");
6171 		pe_size3			= PScript_FindParticleType("PE_SIZE3");
6172 		pe_defaulttrail		= PScript_FindParticleType("PE_DEFAULTTRAIL");
6173 
6174 		for (j = 0; j < numparticletypes; j++)
6175 		{
6176 			//set the fallback
6177 			part_type[j].slooks = &part_type[j].looks;
6178 			for (k = j-1; k-- > 0;)
6179 			{
6180 				if (!memcmp(&part_type[j].looks, &part_type[k].looks, sizeof(plooks_t)))
6181 				{
6182 					part_type[j].slooks = part_type[k].slooks;
6183 					break;
6184 				}
6185 			}
6186 		}
6187 		r_plooksdirty = false;
6188 		CL_RegisterParticles();
6189 		PScript_RecalculateSkyTris();
6190 	}
6191 
6192 	VectorScale (vup, 1.5, pup);
6193 	VectorScale (vright, 1.5, pright);
6194 
6195 	kill_list = kill_first = NULL;
6196 
6197 	flurrytime -= pframetime;
6198 	if (flurrytime < 0)
6199 	{
6200 		doflurry = true;
6201 		flurrytime = 0.1+frandom()*0.3;
6202 	}
6203 	else
6204 		doflurry = false;
6205 
6206 
6207 	if (!free_decals)
6208 	{
6209 		//mark some as dead, so we can keep spawning new ones next frame.
6210 		for (i = 0; i < 256; i++)
6211 		{
6212 			decals[r_decalrecycle].die = -1;
6213 			if (++r_decalrecycle >= r_numdecals)
6214 				r_decalrecycle = 0;
6215 		}
6216 	}
6217 	if (!free_particles)
6218 	{
6219 		//mark some as dead.
6220 		for (i = 0; i < 256; i++)
6221 		{
6222 			particles[r_particlerecycle].die = -1;
6223 			if (++r_particlerecycle >= r_numparticles)
6224 				r_particlerecycle = 0;
6225 		}
6226 	}
6227 
6228 	for (type = part_run_list, lastvalidtype = NULL; type != NULL; type = type->nexttorun)
6229 	{
6230 		if (type->clippeddecals)
6231 		{
6232 			if (cl_numstris && cl_stris[cl_numstris-1].texture == type->looks.texture && cl_stris[cl_numstris-1].blendmode == type->looks.blendmode && cl_stris[cl_numstris-1].beflags == 0)
6233 				scenetri = &cl_stris[cl_numstris-1];
6234 			else
6235 			{
6236 				if (cl_numstris == cl_maxstris)
6237 				{
6238 					cl_maxstris+=8;
6239 					cl_stris = Z_Realloc(cl_stris, sizeof(*cl_stris)*cl_maxstris);
6240 				}
6241 				scenetri = &cl_stris[cl_numstris++];
6242 				scenetri->texture = type->looks.texture;
6243 				scenetri->blendmode = type->looks.blendmode;
6244 				scenetri->beflags = 0;
6245 				scenetri->firstidx = cl_numstrisidx;
6246 				scenetri->firstvert = cl_numstrisvert;
6247 				scenetri->numvert = 0;
6248 				scenetri->numidx = 0;
6249 			}
6250 
6251 			for ( ;; )
6252 			{
6253 				dkill = type->clippeddecals;
6254 				if (dkill && dkill->die < particletime)
6255 				{
6256 					type->clippeddecals = dkill->next;
6257 					dkill->next = free_decals;
6258 					free_decals = dkill;
6259 					continue;
6260 				}
6261 				break;
6262 			}
6263 			for (d=type->clippeddecals ; d ; d=d->next)
6264 			{
6265 				for ( ;; )
6266 				{
6267 					dkill = d->next;
6268 					if (dkill && dkill->die < particletime)
6269 					{
6270 						d->next = dkill->next;
6271 						dkill->next = free_decals;
6272 						free_decals = dkill;
6273 						continue;
6274 					}
6275 					break;
6276 				}
6277 
6278 
6279 				if (d->die - particletime <= type->die)
6280 				{
6281 					switch (type->rampmode)
6282 					{
6283 					case RAMP_NEAREST:
6284 						rampind = (int)(type->rampindexes * (type->die - (d->die - particletime)) / type->die);
6285 						if (rampind >= type->rampindexes)
6286 							rampind = type->rampindexes - 1;
6287 						ramp = type->ramp + rampind;
6288 						VectorCopy(ramp->rgb, d->rgba);
6289 						d->rgba[3] = ramp->alpha;
6290 						break;
6291 					case RAMP_LERP:
6292 						{
6293 							float frac = (type->rampindexes * (type->die - (d->die - particletime)) / type->die);
6294 							int s1, s2;
6295 							s1 = frac;
6296 							s2 = s1+1;
6297 							if (s1 > type->rampindexes-1)
6298 								s1 = type->rampindexes-1;
6299 							if (s2 > type->rampindexes-1)
6300 								s2 = type->rampindexes-1;
6301 							frac -= s1;
6302 							VectorInterpolate(type->ramp[s1].rgb, frac, type->ramp[s2].rgb, d->rgba);
6303 							FloatInterpolate(type->ramp[s1].alpha, frac, type->ramp[s2].alpha, d->rgba[3]);
6304 						}
6305 						break;
6306 					case RAMP_DELTA:	//particle ramps
6307 						ramp = type->ramp + (int)(type->rampindexes * (type->die - (d->die - particletime)) / type->die);
6308 						VectorMA(d->rgba, pframetime, ramp->rgb, d->rgba);
6309 						d->rgba[3] -= pframetime*ramp->alpha;
6310 						break;
6311 					case RAMP_NONE:	//particle changes acording to it's preset properties.
6312 						if (particletime < (d->die-type->die+type->rgbchangetime))
6313 						{
6314 							d->rgba[0] += pframetime*type->rgbchange[0];
6315 							d->rgba[1] += pframetime*type->rgbchange[1];
6316 							d->rgba[2] += pframetime*type->rgbchange[2];
6317 						}
6318 						d->rgba[3] += pframetime*type->alphachange;
6319 					}
6320 				}
6321 
6322 				if (cl_numstrisvert - scenetri->firstvert >= MAX_INDICES-6)
6323 				{
6324 					//generate a new mesh if the old one overflowed. yay smc...
6325 					if (cl_numstris == cl_maxstris)
6326 					{
6327 						cl_maxstris+=8;
6328 						cl_stris = Z_Realloc(cl_stris, sizeof(*cl_stris)*cl_maxstris);
6329 					}
6330 					scenetri = &cl_stris[cl_numstris++];
6331 					scenetri->texture = scenetri[-1].texture;
6332 					scenetri->blendmode = scenetri[-1].blendmode;
6333 					scenetri->beflags = scenetri[-1].beflags;
6334 					scenetri->firstidx = cl_numstrisidx;
6335 					scenetri->firstvert = cl_numstrisvert;
6336 					scenetri->numvert = 0;
6337 					scenetri->numidx = 0;
6338 				}
6339 				R_AddClippedDecal(scenetri, d, type->slooks);
6340 			}
6341 		}
6342 
6343 		bdraw = NULL;
6344 		tdraw = NULL;
6345 		batchflags = 0;
6346 
6347 		// set drawing methods by type and cvars and hope branch
6348 		// prediction takes care of the rest
6349 		switch(type->looks.type)
6350 		{
6351 		default:
6352 		case PT_INVISIBLE:
6353 			break;
6354 		case PT_BEAM:
6355 			bdraw = R_DrawParticleBeam;
6356 			break;
6357 		case PT_CDECAL:
6358 			break;
6359 		case PT_UDECAL:
6360 			tdraw = R_AddUnclippedDecal;
6361 			break;
6362 		case PT_NORMAL:
6363 			tdraw = R_AddTexturedParticle;
6364 			break;
6365 		case PT_SPARK:
6366 			tdraw = R_AddLineSparkParticle;
6367 			batchflags = BEF_LINES;
6368 			break;
6369 		case PT_SPARKFAN:
6370 			tdraw = R_AddFanSparkParticle;
6371 			break;
6372 		case PT_TEXTUREDSPARK:
6373 			tdraw = R_AddTSparkParticle;
6374 			break;
6375 		}
6376 
6377 		if (cl_numstris && cl_stris[cl_numstris-1].texture == type->looks.texture && cl_stris[cl_numstris-1].blendmode == type->looks.blendmode && cl_stris[cl_numstris-1].beflags == batchflags)
6378 			scenetri = &cl_stris[cl_numstris-1];
6379 		else
6380 		{
6381 			if (cl_numstris == cl_maxstris)
6382 			{
6383 				cl_maxstris+=8;
6384 				cl_stris = Z_Realloc(cl_stris, sizeof(*cl_stris)*cl_maxstris);
6385 			}
6386 			scenetri = &cl_stris[cl_numstris++];
6387 			scenetri->texture = type->looks.texture;
6388 			scenetri->blendmode = type->looks.blendmode;
6389 			scenetri->beflags = batchflags;
6390 			scenetri->firstidx = cl_numstrisidx;
6391 			scenetri->firstvert = cl_numstrisvert;
6392 			scenetri->numvert = 0;
6393 			scenetri->numidx = 0;
6394 		}
6395 
6396 		if (!type->die)
6397 		{
6398 			while ((p=type->particles))
6399 			{
6400 				if (scenetri && tdraw)
6401 				{
6402 					if (cl_numstrisvert - scenetri->firstvert >= MAX_INDICES-6)
6403 					{
6404 						//generate a new mesh if the old one overflowed. yay smc...
6405 						if (cl_numstris == cl_maxstris)
6406 						{
6407 							cl_maxstris+=8;
6408 							cl_stris = Z_Realloc(cl_stris, sizeof(*cl_stris)*cl_maxstris);
6409 						}
6410 						scenetri = &cl_stris[cl_numstris++];
6411 						scenetri->texture = scenetri[-1].texture;
6412 						scenetri->blendmode = scenetri[-1].blendmode;
6413 						scenetri->beflags = scenetri[-1].beflags;
6414 						scenetri->firstidx = cl_numstrisidx;
6415 						scenetri->firstvert = cl_numstrisvert;
6416 						scenetri->numvert = 0;
6417 						scenetri->numidx = 0;
6418 					}
6419 					tdraw(scenetri, p, type->slooks);
6420 				}
6421 
6422 				// make sure emitter runs at least once
6423 				if (type->emit >= 0 && type->emitstart <= 0)
6424 					PScript_RunParticleEffectState(p->org, p->vel, 1, type->emit, NULL);
6425 
6426 				type->particles = p->next;
6427 				p->next = kill_list;
6428 				kill_list = p;
6429 				if (!kill_first) // branch here is probably faster than list traversal later
6430 					kill_first = p;
6431 			}
6432 
6433 			if (type->beams)
6434 			{
6435 				b = type->beams;
6436 			}
6437 
6438 			while ((b=type->beams) && (b->flags & BS_DEAD))
6439 			{
6440 				type->beams = b->next;
6441 				b->next = free_beams;
6442 				free_beams = b;
6443 			}
6444 
6445 			while (b)
6446 			{
6447 				if (!(b->flags & BS_NODRAW))
6448 				{
6449 					// no BS_NODRAW implies b->next != NULL
6450 					// BS_NODRAW should imply b->next == NULL or b->next->flags & BS_DEAD
6451 					VectorCopy(b->next->p->org, stop);
6452 					VectorCopy(b->p->org, oldorg);
6453 					VectorSubtract(stop, oldorg, b->next->dir);
6454 					VectorNormalize(b->next->dir);
6455 					if (bdraw)
6456 						bdraw(scenetri, b, type->slooks);
6457 				}
6458 
6459 				// clean up dead entries ahead of current
6460 				for ( ;; )
6461 				{
6462 					bkill = b->next;
6463 					if (bkill && (bkill->flags & BS_DEAD))
6464 					{
6465 						b->next = bkill->next;
6466 						bkill->next = free_beams;
6467 						free_beams = bkill;
6468 						continue;
6469 					}
6470 					break;
6471 				}
6472 
6473 				b->flags |= BS_DEAD;
6474 				b = b->next;
6475 			}
6476 
6477 			goto endtype;
6478 		}
6479 
6480 		//kill off early ones.
6481 		if (type->emittime < 0)
6482 		{
6483 			for ( ;; )
6484 			{
6485 				kill = type->particles;
6486 				if (kill && kill->die < particletime)
6487 				{
6488 					PScript_DelinkTrailstate(&kill->state.trailstate);
6489 					type->particles = kill->next;
6490 					kill->next = kill_list;
6491 					kill_list = kill;
6492 					if (!kill_first)
6493 						kill_first = kill;
6494 					continue;
6495 				}
6496 				break;
6497 			}
6498 		}
6499 		else
6500 		{
6501 			for ( ;; )
6502 			{
6503 				kill = type->particles;
6504 				if (kill && kill->die < particletime)
6505 				{
6506 					type->particles = kill->next;
6507 					kill->next = kill_list;
6508 					kill_list = kill;
6509 					if (!kill_first)
6510 						kill_first = kill;
6511 					continue;
6512 				}
6513 				break;
6514 			}
6515 		}
6516 
6517 		grav = type->gravity*pframetime;
6518 		friction[0] = 1 - type->friction[0]*pframetime;
6519 		friction[1] = 1 - type->friction[1]*pframetime;
6520 		friction[2] = 1 - type->friction[2]*pframetime;
6521 
6522 		for (p=type->particles ; p ; p=p->next)
6523 		{
6524 			if (type->emittime < 0)
6525 			{
6526 				for ( ;; )
6527 				{
6528 					kill = p->next;
6529 					if (kill && kill->die < particletime)
6530 					{
6531 						PScript_DelinkTrailstate(&kill->state.trailstate);
6532 						p->next = kill->next;
6533 						kill->next = kill_list;
6534 						kill_list = kill;
6535 						if (!kill_first)
6536 							kill_first = kill;
6537 						continue;
6538 					}
6539 					break;
6540 				}
6541 			}
6542 			else
6543 			{
6544 				for ( ;; )
6545 				{
6546 					kill = p->next;
6547 					if (kill && kill->die < particletime)
6548 					{
6549 						p->next = kill->next;
6550 						kill->next = kill_list;
6551 						kill_list = kill;
6552 						if (!kill_first)
6553 							kill_first = kill;
6554 						continue;
6555 					}
6556 					break;
6557 				}
6558 			}
6559 
6560 			VectorCopy(p->org, oldorg);
6561 			if (type->flags & PT_VELOCITY)
6562 			{
6563 				p->org[0] += p->vel[0]*pframetime;
6564 				p->org[1] += p->vel[1]*pframetime;
6565 				p->org[2] += p->vel[2]*pframetime;
6566 				p->vel[2] -= grav;
6567 				if (type->flags & PT_FRICTION)
6568 				{
6569 					p->vel[0] *= friction[0];
6570 					p->vel[1] *= friction[1];
6571 					p->vel[2] *= friction[2];
6572 				}
6573 				if (type->flurry && doflurry)
6574 				{	//these should probably be partially synced,
6575 					p->vel[0] += crandom() * type->flurry;
6576 					p->vel[1] += crandom() * type->flurry;
6577 				}
6578 			}
6579 
6580 			p->angle += p->rotationspeed*pframetime;
6581 
6582 			switch (type->rampmode)
6583 			{
6584 			case RAMP_NEAREST:
6585 				rampind = (int)(type->rampindexes * (type->die - (p->die - particletime)) / type->die);
6586 				if (rampind >= type->rampindexes)
6587 					rampind = type->rampindexes - 1;
6588 				ramp = type->ramp + rampind;
6589 				VectorCopy(ramp->rgb, p->rgba);
6590 				p->rgba[3] = ramp->alpha;
6591 				p->scale = ramp->scale;
6592 				break;
6593 			case RAMP_LERP:
6594 				{
6595 					float frac = (type->rampindexes * (type->die - (p->die - particletime)) / type->die);
6596 					int s1, s2;
6597 					s1 = frac;
6598 					s2 = s1+1;
6599 					if (s1 > type->rampindexes-1)
6600 						s1 = type->rampindexes-1;
6601 					if (s2 > type->rampindexes-1)
6602 						s2 = type->rampindexes-1;
6603 					frac -= s1;
6604 					VectorInterpolate(type->ramp[s1].rgb, frac, type->ramp[s2].rgb, p->rgba);
6605 					FloatInterpolate(type->ramp[s1].alpha, frac, type->ramp[s2].alpha, p->rgba[3]);
6606 					FloatInterpolate(type->ramp[s1].scale, frac, type->ramp[s2].scale, p->scale);
6607 				}
6608 				break;
6609 			case RAMP_DELTA:	//particle ramps
6610 				rampind = (int)(type->rampindexes * (type->die - (p->die - particletime)) / type->die);
6611 				if (rampind >= type->rampindexes)
6612 					rampind = type->rampindexes - 1;
6613 				ramp = type->ramp + rampind;
6614 				VectorMA(p->rgba, pframetime, ramp->rgb, p->rgba);
6615 				p->rgba[3] -= pframetime*ramp->alpha;
6616 				p->scale += pframetime*ramp->scale;
6617 				break;
6618 			case RAMP_NONE:	//particle changes acording to it's preset properties.
6619 				if (particletime < (p->die-type->die+type->rgbchangetime))
6620 				{
6621 					p->rgba[0] += pframetime*type->rgbchange[0];
6622 					p->rgba[1] += pframetime*type->rgbchange[1];
6623 					p->rgba[2] += pframetime*type->rgbchange[2];
6624 				}
6625 				p->rgba[3] += pframetime*type->alphachange;
6626 				p->scale += pframetime*type->scaledelta;
6627 			}
6628 
6629 			if (type->emit >= 0)
6630 			{
6631 				if (type->emittime < 0)
6632 					PScript_ParticleTrail(oldorg, p->org, type->emit, pframetime, 0, NULL, &p->state.trailstate);
6633 				else if (p->state.nextemit < particletime)
6634 				{
6635 					p->state.nextemit = particletime + type->emittime + frandom()*type->emitrand;
6636 					PScript_RunParticleEffectState(p->org, p->vel, 1, type->emit, NULL);
6637 				}
6638 			}
6639 
6640 			if (type->cliptype>=0 && r_bouncysparks.value)
6641 			{
6642 				VectorSubtract(p->org, p->oldorg, stop);
6643 				if (!type->clipbounce || DotProduct(stop,stop) > 10*10)
6644 				{
6645 					int e;
6646 					if (traces-->0&&CL_TraceLine(p->oldorg, p->org, stop, normal, &e)<1)
6647 					{
6648 						if (type->clipbounce < 0)
6649 						{
6650 							p->die = -1;
6651 #ifdef USE_DECALS
6652 							if (type->clipbounce == -2)
6653 							{	//this type of particle splatters itself as a decal when it hits a wall.
6654 								decalctx_t ctx;
6655 								float m;
6656 								vec3_t vec={0.5, 0.5, 0.431};
6657 								qmodel_t *model;
6658 
6659 								ctx.entity = e;
6660 								if (!ctx.entity)
6661 								{
6662 									model = cl.worldmodel;
6663 									VectorCopy(p->org, ctx.center);
6664 								}
6665 								else if (e)
6666 								{	//this trace hit a door or something.
6667 									entity_t *ent = CL_EntityNum(e);
6668 									model = ent->model;
6669 									VectorSubtract(p->org, ent->origin, ctx.center);
6670 									//FIXME: rotate center+normal around entity.
6671 								}
6672 								else
6673 									continue;	//err, no idea.
6674 
6675 								VectorScale(normal, -1, ctx.normal);
6676 								VectorNormalize(ctx.normal);
6677 
6678 								VectorNormalize(vec);
6679 								CrossProduct(ctx.normal, vec, ctx.tangent1);
6680 								RotatePointAroundVector(ctx.tangent2, ctx.normal, ctx.tangent1, frandom()*360);
6681 								CrossProduct(ctx.normal, ctx.tangent2, ctx.tangent1);
6682 
6683 								VectorNormalize(ctx.tangent1);
6684 								VectorNormalize(ctx.tangent2);
6685 
6686 								ctx.ptype = type;
6687 								ctx.scale1 = type->s2 - type->s1;
6688 								ctx.bias1 = type->s1 + (ctx.scale1*0.5);
6689 								ctx.scale2 = type->t2 - type->t1;
6690 								ctx.bias2 = type->t1 + (ctx.scale2*0.5);
6691 								m = p->scale*(1.5+frandom()*0.5)*0.5;	//decals should be a little bigger, for some reason.
6692 								ctx.scale0 = 2.0 / m;
6693 								ctx.scale1 /= m;
6694 								ctx.scale2 /= m;
6695 
6696 								//inserts decals through a callback.
6697 								Mod_ClipDecal(model, ctx.center, ctx.normal, ctx.tangent2, ctx.tangent1, m, type->surfflagmask, type->surfflagmatch, PScript_AddDecals, &ctx);
6698 							}
6699 #endif
6700 							continue;
6701 						}
6702 						else if (part_type + type->cliptype == type)
6703 						{	//bounce
6704 							dist = DotProduct(p->vel, normal);// * (-1-(rand()/(float)0x7fff)/2);
6705 							dist *= -type->clipbounce;
6706 							VectorMA(p->vel, dist, normal, p->vel);
6707 							VectorCopy(stop, p->org);
6708 
6709 							if (!*type->texname && VectorLength(p->vel)<1000*pframetime && type->looks.type == PT_NORMAL)
6710 							{
6711 								p->die = -1;
6712 								continue;
6713 							}
6714 						}
6715 						else
6716 						{
6717 							p->die = -1;
6718 							VectorNormalize(p->vel);
6719 
6720 							if (type->clipbounce)
6721 							{
6722 								VectorScale(normal, type->clipbounce, normal);
6723 								PScript_RunParticleEffectState(stop, normal, type->clipcount/part_type[type->cliptype].count, type->cliptype, NULL);
6724 							}
6725 							else
6726 								PScript_RunParticleEffectState(stop, p->vel, type->clipcount/part_type[type->cliptype].count, type->cliptype, NULL);
6727 							continue;
6728 						}
6729 					}
6730 					VectorCopy(p->org, p->oldorg);
6731 				}
6732 			}
6733 			if (scenetri && tdraw)
6734 			{
6735 				if (cl_numstrisvert - scenetri->firstvert >= MAX_INDICES-6)
6736 				{
6737 					//generate a new mesh if the old one overflowed. yay smc...
6738 					if (cl_numstris == cl_maxstris)
6739 					{
6740 						cl_maxstris+=8;
6741 						cl_stris = Z_Realloc(cl_stris, sizeof(*cl_stris)*cl_maxstris);
6742 					}
6743 					scenetri = &cl_stris[cl_numstris++];
6744 					scenetri->texture = scenetri[-1].texture;
6745 					scenetri->blendmode = scenetri[-1].blendmode;
6746 					scenetri->beflags = scenetri[-1].beflags;
6747 					scenetri->firstidx = cl_numstrisidx;
6748 					scenetri->firstvert = cl_numstrisvert;
6749 					scenetri->numvert = 0;
6750 					scenetri->numidx = 0;
6751 				}
6752 				tdraw(scenetri, p, type->slooks);
6753 			}
6754 		}
6755 
6756 		// beams are dealt with here
6757 
6758 		// kill early entries
6759 		for ( ;; )
6760 		{
6761 			bkill = type->beams;
6762 			if (bkill && (bkill->flags & BS_DEAD || bkill->p->die < particletime) && !(bkill->flags & BS_LASTSEG))
6763 			{
6764 				type->beams = bkill->next;
6765 				bkill->next = free_beams;
6766 				free_beams = bkill;
6767 				continue;
6768 			}
6769 			break;
6770 		}
6771 
6772 
6773 		b = type->beams;
6774 		if (b)
6775 		{
6776 			for ( ;; )
6777 			{
6778 				if (b->next)
6779 				{
6780 					// mark dead entries
6781 					if (b->flags & (BS_LASTSEG|BS_DEAD|BS_NODRAW))
6782 					{
6783 						// kill some more dead entries
6784 						for ( ;; )
6785 						{
6786 							bkill = b->next;
6787 							if (bkill && (bkill->flags & BS_DEAD) && !(bkill->flags & BS_LASTSEG))
6788 							{
6789 								b->next = bkill->next;
6790 								bkill->next = free_beams;
6791 								free_beams = bkill;
6792 								continue;
6793 							}
6794 							break;
6795 						}
6796 
6797 						if (!bkill) // have to check so we don't hit NULL->next
6798 							continue;
6799 					}
6800 					else
6801 					{
6802 						if (!(b->next->flags & BS_DEAD))
6803 						{
6804 							VectorCopy(b->next->p->org, stop);
6805 							VectorCopy(b->p->org, oldorg);
6806 							VectorSubtract(stop, oldorg, b->next->dir);
6807 							VectorNormalize(b->next->dir);
6808 							if (bdraw)
6809 							{
6810 								VectorAdd(stop, oldorg, stop);
6811 								VectorScale(stop, 0.5, stop);
6812 							}
6813 						}
6814 
6815 						if (b->p->die < particletime)
6816 							b->flags |= BS_DEAD;
6817 					}
6818 				}
6819 				else
6820 				{
6821 					if (b->p->die < particletime) // end of the list check
6822 						b->flags |= BS_DEAD;
6823 
6824 					break;
6825 				}
6826 
6827 				if (b->p->die < particletime)
6828 					b->flags |= BS_DEAD;
6829 
6830 				b = b->next;
6831 			}
6832 		}
6833 
6834 endtype:
6835 
6836 		// delete from run list if necessary
6837 		if (!type->particles && !type->beams && !type->clippeddecals)
6838 		{
6839 			if (!lastvalidtype)
6840 				part_run_list = type->nexttorun;
6841 			else if (lastvalidtype->nexttorun == type)
6842 				lastvalidtype->nexttorun = type->nexttorun;
6843 			else
6844 				lastvalidtype->nexttorun->nexttorun = type->nexttorun;
6845 			type->state &= ~PS_INRUNLIST;
6846 		}
6847 		else
6848 			lastvalidtype = type;
6849 	}
6850 
6851 	// lazy delete for particles is done here
6852 	if (kill_list)
6853 	{
6854 		kill_first->next = free_particles;
6855 		free_particles = kill_list;
6856 	}
6857 
6858 	particletime += pframetime;
6859 
6860 	if (!cl_numstris)
6861 		return;
6862 
6863 	R_BeginDebugUtilsLabel ("FTE Particles");
6864 	Fog_DisableGFog ();
6865 
6866 	for (o = 0; o < 3; o++)
6867 	{
6868 		static int blend_modes_order[] = {1, 1, 2, 2, 0, 0, 0, 2};
6869 		for (i = 0; i < cl_numstris; i++)
6870 		{
6871 			scenetris_t* tris = &cl_stris[i];
6872 			const int blend_mode = tris->blendmode;
6873 			if (blend_modes_order[blend_mode] != o)
6874 				continue;
6875 			const qboolean draw_lines = ((tris->beflags & BEF_LINES) != 0);
6876 			if (!vulkan_globals.non_solid_fill && draw_lines)
6877 				continue; // Can't draw lines
6878 
6879 			const vulkan_pipeline_t pipeline = vulkan_globals.fte_particle_pipelines[blend_mode + (draw_lines ? 8 : 0)];
6880 			R_BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
6881 			gltexture_t* tex = (tris->beflags & BEF_LINES) ? whitetexture : tris->texture;
6882 
6883 			const int num_indices = tris->numidx;
6884 			const VkDeviceSize vertex_buffer_offset = 0;
6885 			vulkan_globals.vk_cmd_bind_index_buffer(vulkan_globals.command_buffer, index_buffers[current_buffer_index], 0, VK_INDEX_TYPE_UINT16);
6886 			vulkan_globals.vk_cmd_bind_vertex_buffers(vulkan_globals.command_buffer, 0, 1, &vertex_buffers[current_buffer_index], &vertex_buffer_offset);
6887 			vulkan_globals.vk_cmd_bind_descriptor_sets(vulkan_globals.command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.layout.handle, 0, 1, &tex->descriptor_set, 0, NULL);
6888 			vulkan_globals.vk_cmd_draw_indexed(vulkan_globals.command_buffer, num_indices, 1, tris->firstidx, tris->firstvert, 0);
6889 		}
6890 	}
6891 	R_EndDebugUtilsLabel ();
6892 }
6893 
6894 /*
6895 ===============
6896 PScript_DrawParticles
6897 ===============
6898 */
PScript_DrawParticles(void)6899 void PScript_DrawParticles (void)
6900 {
6901 	int i;
6902 	entity_t *ent;
6903 	vec3_t axis[3];
6904 	float pframetime;
6905 	static float oldtime;
6906 
6907 	pframetime = cl.time - oldtime;
6908 	if (pframetime < 0)
6909 		pframetime = 0;
6910 	if (pframetime > 1)
6911 		pframetime = 1;
6912 	oldtime = cl.time;
6913 
6914 	current_buffer_index = (current_buffer_index + 1) % 2;
6915 	cl_numstris = 0;
6916 	cl_numstrisvert = 0;
6917 	cl_numstrisidx = 0;
6918 	cl_curstrisvert = cl_strisvert[current_buffer_index];
6919 	cl_curstrisidx = cl_strisidx[current_buffer_index];
6920 
6921 	if (!r_particles.value)
6922 		return;
6923 
6924 	if (r_part_rain.value && r_fteparticles.value)
6925 	{
6926 		for (i = 0; i < cl.num_entities; i++)
6927 		{
6928 			ent = &cl.entities[i];
6929 			if (!ent->model || ent->model->needload)
6930 				continue;
6931 			if (!ent->model->skytris)
6932 				continue;
6933 			AngleVectors(ent->angles, axis[0], axis[1], axis[2]);
6934 			//this timer, as well as the per-tri timer, are unable to deal with certain rates+sizes. it would be good to fix that...
6935 			//it would also be nice to do mdls too...
6936 			P_AddRainParticles(ent->model, axis, ent->origin, pframetime);
6937 		}
6938 	}
6939 
6940 	PScript_DrawParticleTypes(pframetime);
6941 }
6942 
6943 /*
6944 ===============
6945 R_DrawParticles_ShowTris
6946 ===============
6947 */
PScript_DrawParticles_ShowTris(void)6948 void PScript_DrawParticles_ShowTris (void)
6949 {
6950 	if (r_showtris.value == 1)
6951 		R_BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, vulkan_globals.showtris_pipeline);
6952 	else
6953 		R_BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, vulkan_globals.showtris_depth_test_pipeline);
6954 
6955 	for (unsigned int i = 0; i < cl_numstris; i++)
6956 	{
6957 		scenetris_t* tris = &cl_stris[i];
6958 		const int num_indices = tris->numidx;
6959 		const VkDeviceSize vertex_buffer_offset = 0;
6960 		vulkan_globals.vk_cmd_bind_index_buffer(vulkan_globals.command_buffer, index_buffers[current_buffer_index], 0, VK_INDEX_TYPE_UINT16);
6961 		vulkan_globals.vk_cmd_bind_vertex_buffers(vulkan_globals.command_buffer, 0, 1, &vertex_buffers[current_buffer_index], &vertex_buffer_offset);
6962 		vulkan_globals.vk_cmd_draw_indexed(vulkan_globals.command_buffer, num_indices, 1, tris->firstidx, tris->firstvert, 0);
6963 	}
6964 }
6965 
6966 #endif
6967 #endif
6968 
6969 
6970