1 /*
2 ** p_effect.cpp
3 ** Particle effects
4 **
5 **---------------------------------------------------------------------------
6 ** Copyright 1998-2006 Randy Heit
7 ** All rights reserved.
8 **
9 ** Redistribution and use in source and binary forms, with or without
10 ** modification, are permitted provided that the following conditions
11 ** are met:
12 **
13 ** 1. Redistributions of source code must retain the above copyright
14 **    notice, this list of conditions and the following disclaimer.
15 ** 2. Redistributions in binary form must reproduce the above copyright
16 **    notice, this list of conditions and the following disclaimer in the
17 **    documentation and/or other materials provided with the distribution.
18 ** 3. The name of the author may not be used to endorse or promote products
19 **    derived from this software without specific prior written permission.
20 **
21 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 **---------------------------------------------------------------------------
32 **
33 ** If particles used real sprites instead of blocks, they could be much
34 ** more useful.
35 */
36 
37 #include "doomtype.h"
38 #include "doomstat.h"
39 #include "i_system.h"
40 #include "c_cvars.h"
41 #include "actor.h"
42 #include "m_argv.h"
43 #include "p_effect.h"
44 #include "p_local.h"
45 #include "g_level.h"
46 #include "v_video.h"
47 #include "m_random.h"
48 #include "r_defs.h"
49 #include "s_sound.h"
50 #include "templates.h"
51 #include "gi.h"
52 #include "v_palette.h"
53 #include "colormatcher.h"
54 
55 CVAR (Int, cl_rockettrails, 1, CVAR_ARCHIVE);
56 CVAR (Bool, r_rail_smartspiral, 0, CVAR_ARCHIVE);
57 CVAR (Int, r_rail_spiralsparsity, 1, CVAR_ARCHIVE);
58 CVAR (Int, r_rail_trailsparsity, 1, CVAR_ARCHIVE);
59 CVAR (Bool, r_particles, true, 0);
60 
61 FRandom pr_railtrail("RailTrail");
62 
63 #define FADEFROMTTL(a)	(255/(a))
64 
65 // [RH] particle globals
66 WORD			NumParticles;
67 WORD			ActiveParticles;
68 WORD			InactiveParticles;
69 particle_t		*Particles;
70 TArray<WORD>	ParticlesInSubsec;
71 
72 static int grey1, grey2, grey3, grey4, red, green, blue, yellow, black,
73 		   red1, green1, blue1, yellow1, purple, purple1, white,
74 		   rblue1, rblue2, rblue3, rblue4, orange, yorange, dred, grey5,
75 		   maroon1, maroon2, blood1, blood2;
76 
77 static const struct ColorList {
78 	int *color;
79 	BYTE r, g, b;
80 } Colors[] = {
81 	{&grey1,	85,  85,  85 },
82 	{&grey2,	171, 171, 171},
83 	{&grey3,	50,  50,  50 },
84 	{&grey4,	210, 210, 210},
85 	{&grey5,	128, 128, 128},
86 	{&red,		255, 0,   0  },
87 	{&green,	0,   200, 0  },
88 	{&blue,		0,   0,   255},
89 	{&yellow,	255, 255, 0  },
90 	{&black,	0,   0,   0  },
91 	{&red1,		255, 127, 127},
92 	{&green1,	127, 255, 127},
93 	{&blue1,	127, 127, 255},
94 	{&yellow1,	255, 255, 180},
95 	{&purple,	120, 0,   160},
96 	{&purple1,	200, 30,  255},
97 	{&white, 	255, 255, 255},
98 	{&rblue1,	81,  81,  255},
99 	{&rblue2,	0,   0,   227},
100 	{&rblue3,	0,   0,   130},
101 	{&rblue4,	0,   0,   80 },
102 	{&orange,	255, 120, 0  },
103 	{&yorange,	255, 170, 0  },
104 	{&dred,		80,  0,   0  },
105 	{&maroon1,	154, 49,  49 },
106 	{&maroon2,	125, 24,  24 },
107 	{NULL, 0, 0, 0 }
108 };
109 
NewParticle(void)110 inline particle_t *NewParticle (void)
111 {
112 	particle_t *result = NULL;
113 	if (InactiveParticles != NO_PARTICLE)
114 	{
115 		result = Particles + InactiveParticles;
116 		InactiveParticles = result->tnext;
117 		result->tnext = ActiveParticles;
118 		ActiveParticles = WORD(result - Particles);
119 	}
120 	return result;
121 }
122 
123 //
124 // [RH] Particle functions
125 //
126 void P_InitParticles ();
127 void P_DeinitParticles ();
128 
129 // [BC] Allow the maximum number of particles to be specified by a cvar (so people
130 // with lots of nice hardware can have lots of particles!).
131 CUSTOM_CVAR( Int, r_maxparticles, 4000, CVAR_ARCHIVE )
132 {
133 	if ( self == 0 )
134 		self = 4000;
135 	else if (self > 65535)
136 		self = 65535;
137 	else if (self < 100)
138 		self = 100;
139 
140 	if ( gamestate != GS_STARTUP )
141 	{
142 		P_DeinitParticles( );
143 		P_InitParticles( );
144 	}
145 }
146 
P_InitParticles()147 void P_InitParticles ()
148 {
149 	const char *i;
150 
151 	if ((i = Args->CheckValue ("-numparticles")))
152 		NumParticles = atoi (i);
153 	// [BC] Use r_maxparticles now.
154 	else
155 		NumParticles = r_maxparticles;
156 
157 	// This should be good, but eh...
158 	NumParticles = clamp<WORD>(NumParticles, 100, 65535);
159 
160 	P_DeinitParticles();
161 	Particles = new particle_t[NumParticles];
162 	P_ClearParticles ();
163 	atterm (P_DeinitParticles);
164 }
165 
P_DeinitParticles()166 void P_DeinitParticles()
167 {
168 	if (Particles != NULL)
169 	{
170 		delete[] Particles;
171 		Particles = NULL;
172 	}
173 }
174 
P_ClearParticles()175 void P_ClearParticles ()
176 {
177 	int i;
178 
179 	memset (Particles, 0, NumParticles * sizeof(particle_t));
180 	ActiveParticles = NO_PARTICLE;
181 	InactiveParticles = 0;
182 	for (i = 0; i < NumParticles-1; i++)
183 		Particles[i].tnext = i + 1;
184 	Particles[i].tnext = NO_PARTICLE;
185 }
186 
187 // Group particles by subsectors. Because particles are always
188 // in motion, there is little benefit to caching this information
189 // from one frame to the next.
190 
P_FindParticleSubsectors()191 void P_FindParticleSubsectors ()
192 {
193 	if (ParticlesInSubsec.Size() < (size_t)numsubsectors)
194 	{
195 		ParticlesInSubsec.Reserve (numsubsectors - ParticlesInSubsec.Size());
196 	}
197 
198 	clearbufshort (&ParticlesInSubsec[0], numsubsectors, NO_PARTICLE);
199 
200 	if (!r_particles)
201 	{
202 		return;
203 	}
204 	for (WORD i = ActiveParticles; i != NO_PARTICLE; i = Particles[i].tnext)
205 	{
206 		subsector_t *ssec = R_PointInSubsector (Particles[i].x, Particles[i].y);
207 		int ssnum = int(ssec-subsectors);
208 		Particles[i].subsector = ssec;
209 		Particles[i].snext = ParticlesInSubsec[ssnum];
210 		ParticlesInSubsec[ssnum] = i;
211 	}
212 }
213 
214 static TMap<int, int> ColorSaver;
215 
ParticleColor(int rgb)216 static uint32 ParticleColor(int rgb)
217 {
218 	int *val;
219 	int stuff;
220 
221 	val = ColorSaver.CheckKey(rgb);
222 	if (val != NULL)
223 	{
224 		return *val;
225 	}
226 	stuff = rgb | (ColorMatcher.Pick(RPART(rgb), GPART(rgb), BPART(rgb)) << 24);
227 	ColorSaver[rgb] = stuff;
228 	return stuff;
229 }
230 
ParticleColor(int r,int g,int b)231 static uint32 ParticleColor(int r, int g, int b)
232 {
233 	return ParticleColor(MAKERGB(r, g, b));
234 }
235 
P_InitEffects()236 void P_InitEffects ()
237 {
238 	const struct ColorList *color = Colors;
239 
240 	P_InitParticles();
241 	while (color->color)
242 	{
243 		*(color->color) = ParticleColor(color->r, color->g, color->b);
244 		color++;
245 	}
246 
247 	int kind = gameinfo.defaultbloodparticlecolor;
248 	blood1 = ParticleColor(kind);
249 	blood2 = ParticleColor(RPART(kind)/3, GPART(kind)/3, BPART(kind)/3);
250 }
251 
252 
P_ThinkParticles()253 void P_ThinkParticles ()
254 {
255 	int i;
256 	particle_t *particle, *prev;
257 
258 	i = ActiveParticles;
259 	prev = NULL;
260 	while (i != NO_PARTICLE)
261 	{
262 		BYTE oldtrans;
263 
264 		particle = Particles + i;
265 		i = particle->tnext;
266 		oldtrans = particle->trans;
267 		particle->trans -= particle->fade;
268 		if (oldtrans < particle->trans || --particle->ttl == 0)
269 		{ // The particle has expired, so free it
270 			memset (particle, 0, sizeof(particle_t));
271 			if (prev)
272 				prev->tnext = i;
273 			else
274 				ActiveParticles = i;
275 			particle->tnext = InactiveParticles;
276 			InactiveParticles = (int)(particle - Particles);
277 			continue;
278 		}
279 		particle->x += particle->velx;
280 		particle->y += particle->vely;
281 		particle->z += particle->velz;
282 		particle->velx += particle->accx;
283 		particle->vely += particle->accy;
284 		particle->velz += particle->accz;
285 		prev = particle;
286 	}
287 }
288 
P_SpawnParticle(fixed_t x,fixed_t y,fixed_t z,fixed_t velx,fixed_t vely,fixed_t velz,PalEntry color,bool fullbright,BYTE startalpha,BYTE lifetime,WORD size,int fadestep,fixed_t accelx,fixed_t accely,fixed_t accelz)289 void P_SpawnParticle(fixed_t x, fixed_t y, fixed_t z, fixed_t velx, fixed_t vely, fixed_t velz, PalEntry color, bool fullbright, BYTE startalpha, BYTE lifetime, WORD size, int fadestep, fixed_t accelx, fixed_t accely, fixed_t accelz)
290 {
291 	particle_t *particle = NewParticle();
292 
293 	if (particle)
294 	{
295 		particle->x = x;
296 		particle->y = y;
297 		particle->z = z;
298 		particle->velx = velx;
299 		particle->vely = vely;
300 		particle->velz = velz;
301 		particle->color = ParticleColor(color);
302 		particle->trans = startalpha;
303 		if (fadestep < 0) fadestep = FADEFROMTTL(lifetime);
304 		particle->fade = fadestep;
305 		particle->ttl = lifetime;
306 		particle->accx = accelx;
307 		particle->accy = accely;
308 		particle->accz = accelz;
309 		particle->bright = fullbright;
310 		particle->size = size;
311 	}
312 }
313 
314 //
315 // P_RunEffects
316 //
317 // Run effects on all actors in the world
318 //
P_RunEffects()319 void P_RunEffects ()
320 {
321 	if (players[consoleplayer].camera == NULL) return;
322 
323 	int	pnum = int(players[consoleplayer].camera->Sector - sectors) * numsectors;
324 
325 	AActor *actor;
326 	TThinkerIterator<AActor> iterator;
327 
328 	while ( (actor = iterator.Next ()) )
329 	{
330 		if (actor->effects)
331 		{
332 			// Only run the effect if the actor is potentially visible
333 			int rnum = pnum + int(actor->Sector - sectors);
334 			if (rejectmatrix == NULL || !(rejectmatrix[rnum>>3] & (1 << (rnum & 7))))
335 				P_RunEffect (actor, actor->effects);
336 		}
337 	}
338 }
339 
340 //
341 // JitterParticle
342 //
343 // Creates a particle with "jitter"
344 //
JitterParticle(int ttl)345 particle_t *JitterParticle (int ttl)
346 {
347 	return JitterParticle (ttl, 1.0);
348 }
349 // [XA] Added "drift speed" multiplier setting for enhanced railgun stuffs.
JitterParticle(int ttl,float drift)350 particle_t *JitterParticle (int ttl, float drift)
351 {
352 	particle_t *particle = NewParticle ();
353 
354 	if (particle) {
355 		fixed_t *val = &particle->velx;
356 		int i;
357 
358 		// Set initial velocities
359 		for (i = 3; i; i--, val++)
360 			*val = (int)((FRACUNIT/4096) * (M_Random () - 128) * drift);
361 		// Set initial accelerations
362 		for (i = 3; i; i--, val++)
363 			*val = (int)((FRACUNIT/16384) * (M_Random () - 128) * drift);
364 
365 		particle->trans = 255;	// fully opaque
366 		particle->ttl = ttl;
367 		particle->fade = FADEFROMTTL(ttl);
368 	}
369 	return particle;
370 }
371 
MakeFountain(AActor * actor,int color1,int color2)372 static void MakeFountain (AActor *actor, int color1, int color2)
373 {
374 	particle_t *particle;
375 
376 	if (!(level.time & 1))
377 		return;
378 
379 	particle = JitterParticle (51);
380 
381 	if (particle)
382 	{
383 		angle_t an = M_Random()<<(24-ANGLETOFINESHIFT);
384 		fixed_t out = FixedMul (actor->radius, M_Random()<<8);
385 
386 		fixedvec3 pos = actor->Vec3Offset(FixedMul(out, finecosine[an]), FixedMul(out, finesine[an]), actor->height + FRACUNIT);
387 		particle->x = pos.x;
388 		particle->y = pos.y;
389 		particle->z = pos.z;
390 		if (out < actor->radius/8)
391 			particle->velz += FRACUNIT*10/3;
392 		else
393 			particle->velz += FRACUNIT*3;
394 		particle->accz -= FRACUNIT/11;
395 		if (M_Random() < 30) {
396 			particle->size = 4;
397 			particle->color = color2;
398 		} else {
399 			particle->size = 6;
400 			particle->color = color1;
401 		}
402 	}
403 }
404 
P_RunEffect(AActor * actor,int effects)405 void P_RunEffect (AActor *actor, int effects)
406 {
407 	angle_t moveangle;
408 
409 	// 512 is the limit below which R_PointToAngle2 does no longer returns usable values.
410 	if (abs(actor->velx) > 512 || abs(actor->vely) > 512)
411 	{
412 		moveangle = R_PointToAngle2(0,0,actor->velx,actor->vely);
413 	}
414 	else
415 	{
416 		moveangle = actor->angle;
417 	}
418 
419 	particle_t *particle;
420 	int i;
421 
422 	if ((effects & FX_ROCKET) && (cl_rockettrails & 1))
423 	{
424 		// Rocket trail
425 
426 
427 		fixed_t backx = - FixedMul (finecosine[(moveangle)>>ANGLETOFINESHIFT], actor->radius*2);
428 		fixed_t backy = - FixedMul (finesine[(moveangle)>>ANGLETOFINESHIFT], actor->radius*2);
429 		fixed_t backz = - (actor->height>>3) * (actor->velz>>16) + (2*actor->height)/3;
430 
431 		angle_t an = (moveangle + ANG90) >> ANGLETOFINESHIFT;
432 		int speed;
433 
434 		particle = JitterParticle (3 + (M_Random() & 31));
435 		if (particle) {
436 			fixed_t pathdist = M_Random()<<8;
437 			fixedvec3 pos = actor->Vec3Offset(
438 				backx - FixedMul(actor->velx, pathdist),
439 				backy - FixedMul(actor->vely, pathdist),
440 				backz - FixedMul(actor->velz, pathdist));
441 			particle->x = pos.x;
442 			particle->y = pos.y;
443 			particle->z = pos.z;
444 			speed = (M_Random () - 128) * (FRACUNIT/200);
445 			particle->velx += FixedMul (speed, finecosine[an]);
446 			particle->vely += FixedMul (speed, finesine[an]);
447 			particle->velz -= FRACUNIT/36;
448 			particle->accz -= FRACUNIT/20;
449 			particle->color = yellow;
450 			particle->size = 2;
451 		}
452 		for (i = 6; i; i--) {
453 			particle_t *particle = JitterParticle (3 + (M_Random() & 31));
454 			if (particle) {
455 				fixed_t pathdist = M_Random()<<8;
456 				fixedvec3 pos = actor->Vec3Offset(
457 					backx - FixedMul(actor->velx, pathdist),
458 					backy - FixedMul(actor->vely, pathdist),
459 					backz - FixedMul(actor->velz, pathdist) + (M_Random() << 10));
460 				particle->x = pos.x;
461 				particle->y = pos.y;
462 				particle->z = pos.z;
463 				speed = (M_Random () - 128) * (FRACUNIT/200);
464 				particle->velx += FixedMul (speed, finecosine[an]);
465 				particle->vely += FixedMul (speed, finesine[an]);
466 				particle->velz += FRACUNIT/80;
467 				particle->accz += FRACUNIT/40;
468 				if (M_Random () & 7)
469 					particle->color = grey2;
470 				else
471 					particle->color = grey1;
472 				particle->size = 3;
473 			} else
474 				break;
475 		}
476 	}
477 	if ((effects & FX_GRENADE) && (cl_rockettrails & 1))
478 	{
479 		// Grenade trail
480 
481 		fixedvec3 pos = actor->Vec3Angle(-actor->radius * 2, moveangle,
482 			-(actor->height >> 3) * (actor->velz >> 16) + (2 * actor->height) / 3);
483 
484 		P_DrawSplash2 (6, pos.x, pos.y, pos.z,
485 			moveangle + ANG180, 2, 2);
486 	}
487 	if (effects & FX_FOUNTAINMASK)
488 	{
489 		// Particle fountain
490 
491 		static const int *fountainColors[16] =
492 			{ &black,	&black,
493 			  &red,		&red1,
494 			  &green,	&green1,
495 			  &blue,	&blue1,
496 			  &yellow,	&yellow1,
497 			  &purple,	&purple1,
498 			  &black,	&grey3,
499 			  &grey4,	&white
500 			};
501 		int color = (effects & FX_FOUNTAINMASK) >> 15;
502 		MakeFountain (actor, *fountainColors[color], *fountainColors[color+1]);
503 	}
504 	if (effects & FX_RESPAWNINVUL)
505 	{
506 		// Respawn protection
507 
508 		static const int *protectColors[2] = { &yellow1, &white };
509 
510 		for (i = 3; i > 0; i--)
511 		{
512 			particle = JitterParticle (16);
513 			if (particle != NULL)
514 			{
515 				angle_t ang = M_Random () << (32-ANGLETOFINESHIFT-8);
516 				fixedvec3 pos = actor->Vec3Offset(FixedMul (actor->radius, finecosine[ang]), FixedMul (actor->radius, finesine[ang]), 0);
517 				particle->x = pos.x;
518 				particle->y = pos.y;
519 				particle->z = pos.z;
520 				particle->color = *protectColors[M_Random() & 1];
521 				particle->velz = FRACUNIT;
522 				particle->accz = M_Random () << 7;
523 				particle->size = 1;
524 				if (M_Random () < 128)
525 				{ // make particle fall from top of actor
526 					particle->z += actor->height;
527 					particle->velz = -particle->velz;
528 					particle->accz = -particle->accz;
529 				}
530 			}
531 		}
532 	}
533 }
534 
P_DrawSplash(int count,fixed_t x,fixed_t y,fixed_t z,angle_t angle,int kind)535 void P_DrawSplash (int count, fixed_t x, fixed_t y, fixed_t z, angle_t angle, int kind)
536 {
537 	int color1, color2;
538 
539 	switch (kind)
540 	{
541 	case 1:		// Spark
542 		color1 = orange;
543 		color2 = yorange;
544 		break;
545 	default:
546 		return;
547 	}
548 
549 	for (; count; count--)
550 	{
551 		particle_t *p = JitterParticle (10);
552 		angle_t an;
553 
554 		if (!p)
555 			break;
556 
557 		p->size = 2;
558 		p->color = M_Random() & 0x80 ? color1 : color2;
559 		p->velz -= M_Random () * 512;
560 		p->accz -= FRACUNIT/8;
561 		p->accx += (M_Random () - 128) * 8;
562 		p->accy += (M_Random () - 128) * 8;
563 		p->z = z - M_Random () * 1024;
564 		an = (angle + (M_Random() << 21)) >> ANGLETOFINESHIFT;
565 		p->x = x + (M_Random () & 15)*finecosine[an];
566 		p->y = y + (M_Random () & 15)*finesine[an];
567 	}
568 }
569 
P_DrawSplash2(int count,fixed_t x,fixed_t y,fixed_t z,angle_t angle,int updown,int kind)570 void P_DrawSplash2 (int count, fixed_t x, fixed_t y, fixed_t z, angle_t angle, int updown, int kind)
571 {
572 	int color1, color2, zvel, zspread, zadd;
573 
574 	switch (kind)
575 	{
576 	case 0:		// Blood
577 		color1 = blood1;
578 		color2 = blood2;
579 		break;
580 	case 1:		// Gunshot
581 		color1 = grey3;
582 		color2 = grey5;
583 		break;
584 	case 2:		// Smoke
585 		color1 = grey3;
586 		color2 = grey1;
587 		break;
588 	default:	// colorized blood
589 		color1 = ParticleColor(kind);
590 		color2 = ParticleColor(RPART(kind)/3, GPART(kind)/3, BPART(kind)/3);
591 		break;
592 	}
593 
594 	zvel = -128;
595 	zspread = updown ? -6000 : 6000;
596 	zadd = (updown == 2) ? -128 : 0;
597 
598 	for (; count; count--)
599 	{
600 		particle_t *p = NewParticle ();
601 		angle_t an;
602 
603 		if (!p)
604 			break;
605 
606 		p->ttl = 12;
607 		p->fade = FADEFROMTTL(12);
608 		p->trans = 255;
609 		p->size = 4;
610 		p->color = M_Random() & 0x80 ? color1 : color2;
611 		p->velz = M_Random () * zvel;
612 		p->accz = -FRACUNIT/22;
613 		if (kind) {
614 			an = (angle + ((M_Random() - 128) << 23)) >> ANGLETOFINESHIFT;
615 			p->velx = (M_Random () * finecosine[an]) >> 11;
616 			p->vely = (M_Random () * finesine[an]) >> 11;
617 			p->accx = p->velx >> 4;
618 			p->accy = p->vely >> 4;
619 		}
620 		p->z = z + (M_Random () + zadd - 128) * zspread;
621 		an = (angle + ((M_Random() - 128) << 22)) >> ANGLETOFINESHIFT;
622 		p->x = x + ((M_Random () & 31)-15)*finecosine[an];
623 		p->y = y + ((M_Random () & 31)-15)*finesine[an];
624 	}
625 }
626 
P_DrawRailTrail(AActor * source,const TVector3<double> & start,const TVector3<double> & end,int color1,int color2,double maxdiff,int flags,const PClass * spawnclass,angle_t angle,int duration,double sparsity,double drift,int SpiralOffset)627 void P_DrawRailTrail(AActor *source, const TVector3<double> &start, const TVector3<double> &end, int color1, int color2, double maxdiff, int flags, const PClass *spawnclass, angle_t angle, int duration, double sparsity, double drift, int SpiralOffset)
628 {
629 	double length, lengthsquared;
630 	int steps, i;
631 	TAngle<double> deg;
632 	TVector3<double> step, dir, pos, extend;
633 	bool fullbright;
634 
635 	dir = end - start;
636 	lengthsquared = dir | dir;
637 	length = sqrt(lengthsquared);
638 	steps = xs_FloorToInt(length / 3);
639 	fullbright = !!(flags & RAF_FULLBRIGHT);
640 
641 	if (steps)
642 	{
643 		if (!(flags & RAF_SILENT))
644 		{
645 			FSoundID sound;
646 
647 			// Allow other sounds than 'weapons/railgf'!
648 			if (!source->player) sound = source->AttackSound;
649 			else if (source->player->ReadyWeapon) sound = source->player->ReadyWeapon->AttackSound;
650 			else sound = 0;
651 			if (!sound) sound = "weapons/railgf";
652 
653 			// The railgun's sound is special. It gets played from the
654 			// point on the slug's trail that is closest to the hearing player.
655 			AActor *mo = players[consoleplayer].camera;
656 			TVector3<double> point;
657 			double r;
658 			double dirz;
659 
660 			if (abs(mo->X() - FLOAT2FIXED(start.X)) < 20 * FRACUNIT
661 				&& (mo->Y() - FLOAT2FIXED(start.Y)) < 20 * FRACUNIT)
662 			{ // This player (probably) fired the railgun
663 				S_Sound (mo, CHAN_WEAPON, sound, 1, ATTN_NORM);
664 			}
665 			else
666 			{
667 
668 				// Only consider sound in 2D (for now, anyway)
669 				// [BB] You have to divide by lengthsquared here, not multiply with it.
670 
671 				r = ((start.Y - FIXED2DBL(mo->Y())) * (-dir.Y) - (start.X - FIXED2DBL(mo->X())) * (dir.X)) / lengthsquared;
672 				r = clamp<double>(r, 0., 1.);
673 
674 				dirz = dir.Z;
675 				dir.Z = 0;
676 				point = start + r * dir;
677 				dir.Z = dirz;
678 
679 				S_Sound (FLOAT2FIXED(point.X), FLOAT2FIXED(point.Y), viewz,
680 					CHAN_WEAPON, sound, 1, ATTN_NORM);
681 			}
682 		}
683 	}
684 	else
685 	{
686 		// line is 0 length, so nothing to do
687 		return;
688 	}
689 
690 	dir /= length;
691 
692 	//Calculate PerpendicularVector (extend, dir):
693 	double minelem = 1;
694 	int epos;
695 	for (epos = 0, i = 0; i < 3; ++i)
696 	{
697 		if (fabs(dir[i]) < minelem)
698 		{
699 			epos = i;
700 			minelem = fabs(dir[i]);
701 		}
702 	}
703 	TVector3<double> tempvec(0, 0, 0);
704 	tempvec[epos] = 1;
705 	extend = tempvec - (dir | tempvec) * dir;
706 	//
707 
708 	extend *= 3;
709 	step = dir * 3;
710 
711 	// Create the outer spiral.
712 	if (color1 != -1 && (!r_rail_smartspiral || color2 == -1) && r_rail_spiralsparsity > 0 && (spawnclass == NULL))
713 	{
714 		TVector3<double> spiral_step = step * r_rail_spiralsparsity * sparsity;
715 		int spiral_steps = (int)(steps * r_rail_spiralsparsity / sparsity);
716 
717 		color1 = color1 == 0 ? -1 : ParticleColor(color1);
718 		pos = start;
719 		deg = TAngle<double>(SpiralOffset);
720 		for (i = spiral_steps; i; i--)
721 		{
722 			particle_t *p = NewParticle ();
723 			TVector3<double> tempvec;
724 
725 			if (!p)
726 				return;
727 
728 			int spiralduration = (duration == 0) ? 35 : duration;
729 
730 			p->trans = 255;
731 			p->ttl = duration;
732 			p->fade = FADEFROMTTL(spiralduration);
733 			p->size = 3;
734 			p->bright = fullbright;
735 
736 			tempvec = TMatrix3x3<double>(dir, deg) * extend;
737 			p->velx = FLOAT2FIXED(tempvec.X * drift)>>4;
738 			p->vely = FLOAT2FIXED(tempvec.Y * drift)>>4;
739 			p->velz = FLOAT2FIXED(tempvec.Z * drift)>>4;
740 			tempvec += pos;
741 			p->x = FLOAT2FIXED(tempvec.X);
742 			p->y = FLOAT2FIXED(tempvec.Y);
743 			p->z = FLOAT2FIXED(tempvec.Z);
744 			pos += spiral_step;
745 			deg += TAngle<double>(r_rail_spiralsparsity * 14);
746 
747 			if (color1 == -1)
748 			{
749 				int rand = M_Random();
750 
751 				if (rand < 155)
752 					p->color = rblue2;
753 				else if (rand < 188)
754 					p->color = rblue1;
755 				else if (rand < 222)
756 					p->color = rblue3;
757 				else
758 					p->color = rblue4;
759 			}
760 			else
761 			{
762 				p->color = color1;
763 			}
764 		}
765 	}
766 
767 	// Create the inner trail.
768 	if (color2 != -1 && r_rail_trailsparsity > 0 && spawnclass == NULL)
769 	{
770 		TVector3<double> trail_step = step * r_rail_trailsparsity * sparsity;
771 		int trail_steps = xs_FloorToInt(steps * r_rail_trailsparsity / sparsity);
772 
773 		color2 = color2 == 0 ? -1 : ParticleColor(color2);
774 		TVector3<double> diff(0, 0, 0);
775 
776 		pos = start;
777 		for (i = trail_steps; i; i--)
778 		{
779 			// [XA] inner trail uses a different default duration (33).
780 			int innerduration = (duration == 0) ? 33 : duration;
781 			particle_t *p = JitterParticle (innerduration, (float)drift);
782 
783 			if (!p)
784 				return;
785 
786 			if (maxdiff > 0)
787 			{
788 				int rnd = M_Random ();
789 				if (rnd & 1)
790 					diff.X = clamp<double>(diff.X + ((rnd & 8) ? 1 : -1), -maxdiff, maxdiff);
791 				if (rnd & 2)
792 					diff.Y = clamp<double>(diff.Y + ((rnd & 16) ? 1 : -1), -maxdiff, maxdiff);
793 				if (rnd & 4)
794 					diff.Z = clamp<double>(diff.Z + ((rnd & 32) ? 1 : -1), -maxdiff, maxdiff);
795 			}
796 
797 			TVector3<double> postmp = pos + diff;
798 
799 			p->size = 2;
800 			p->x = FLOAT2FIXED(postmp.X);
801 			p->y = FLOAT2FIXED(postmp.Y);
802 			p->z = FLOAT2FIXED(postmp.Z);
803 			if (color1 != -1)
804 				p->accz -= FRACUNIT/4096;
805 			pos += trail_step;
806 
807 			p->bright = fullbright;
808 
809 			if (color2 == -1)
810 			{
811 				int rand = M_Random();
812 
813 				if (rand < 85)
814 					p->color = grey4;
815 				else if (rand < 170)
816 					p->color = grey2;
817 				else
818 					p->color = grey1;
819 			}
820 			else
821 			{
822 				p->color = color2;
823 			}
824 		}
825 	}
826 	// create actors
827 	if (spawnclass != NULL)
828 	{
829 		if (sparsity < 1)
830 			sparsity = 32;
831 
832 		TVector3<double> trail_step = (step / 3) * sparsity;
833 		int trail_steps = (int)((steps * 3) / sparsity);
834 		TVector3<double> diff(0, 0, 0);
835 
836 		pos = start;
837 		for (i = trail_steps; i; i--)
838 		{
839 			if (maxdiff > 0)
840 			{
841 				int rnd = pr_railtrail();
842 				if (rnd & 1)
843 					diff.X = clamp<double>(diff.X + ((rnd & 8) ? 1 : -1), -maxdiff, maxdiff);
844 				if (rnd & 2)
845 					diff.Y = clamp<double>(diff.Y + ((rnd & 16) ? 1 : -1), -maxdiff, maxdiff);
846 				if (rnd & 4)
847 					diff.Z = clamp<double>(diff.Z + ((rnd & 32) ? 1 : -1), -maxdiff, maxdiff);
848 			}
849 			TVector3<double> postmp = pos + diff;
850 
851 			AActor *thing = Spawn (spawnclass, FLOAT2FIXED(postmp.X), FLOAT2FIXED(postmp.Y), FLOAT2FIXED(postmp.Z), ALLOW_REPLACE);
852 			if (thing)
853 				thing->angle = angle;
854 			pos += trail_step;
855 		}
856 	}
857 }
858 
P_DisconnectEffect(AActor * actor)859 void P_DisconnectEffect (AActor *actor)
860 {
861 	int i;
862 
863 	if (actor == NULL)
864 		return;
865 
866 	for (i = 64; i; i--)
867 	{
868 		particle_t *p = JitterParticle (TICRATE*2);
869 
870 		if (!p)
871 			break;
872 
873 
874 		fixed_t xo = ((M_Random() - 128) << 9) * (actor->radius >> FRACBITS);
875 		fixed_t yo = ((M_Random() - 128) << 9) * (actor->radius >> FRACBITS);
876 		fixed_t zo = (M_Random() << 8) * (actor->height >> FRACBITS);
877 		fixedvec3 pos = actor->Vec3Offset(xo, yo, zo);
878 		p->x = pos.x;
879 		p->y = pos.y;
880 		p->z = pos.z;
881 		p->accz -= FRACUNIT/4096;
882 		p->color = M_Random() < 128 ? maroon1 : maroon2;
883 		p->size = 4;
884 	}
885 }
886