1 // Emacs style mode select   -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // $Id: c_effect.cpp 4469 2014-01-03 23:38:29Z dr_sean $
5 //
6 // Copyright (C) 1998-2006 by Randy Heit (ZDoom).
7 // Copyright (C) 2006-2014 by The Odamex Team.
8 //
9 // This program is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU General Public License
11 // as published by the Free Software Foundation; either version 2
12 // of the License, or (at your option) any later version.
13 //
14 // This program is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 // GNU General Public License for more details.
18 //
19 // DESCRIPTION:
20 //	COMMON Particle effect thinkers
21 //
22 //-----------------------------------------------------------------------------
23 
24 
25 #include "doomtype.h"
26 #include "doomstat.h"
27 #include "c_cvars.h"
28 #include "actor.h"
29 #include "c_effect.h"
30 #include "p_local.h"
31 #include "g_level.h"
32 #include "v_video.h"
33 #include "m_random.h"
34 #include "r_defs.h"
35 #include "r_things.h"
36 #include "s_sound.h"
37 
38 EXTERN_CVAR (cl_rockettrails)
39 
40 // [RH] particle globals
41 int				NumParticles;
42 int				ActiveParticles;
43 int				InactiveParticles;
44 particle_t		*Particles;
45 
46 #define FADEFROMTTL(a)	(255/(a))
47 
48 static int	grey1, grey2, grey3, grey4, red, green, blue, yellow, black,
49 		   red1, green1, blue1, yellow1, purple, purple1, white,
50 		   rblue1, rblue2, rblue3, rblue4, orange, yorange, dred, grey5,
51 		   maroon1, maroon2;
52 
53 static const struct ColorList {
54 	int *color, r, g, b;
55 } Colors[] = {
56 	{&grey1,	85,  85,  85 },
57 	{&grey2,	171, 171, 171},
58 	{&grey3,	50,  50,  50 },
59 	{&grey4,	210, 210, 210},
60 	{&grey5,	128, 128, 128},
61 	{&red,		255, 0,   0  },
62 	{&green,	0,   200, 0  },
63 	{&blue,		0,   0,   255},
64 	{&yellow,	255, 255, 0  },
65 	{&black,	0,   0,   0  },
66 	{&red1,		255, 127, 127},
67 	{&green1,	127, 255, 127},
68 	{&blue1,	127, 127, 255},
69 	{&yellow1,	255, 255, 180},
70 	{&purple,	120, 0,   160},
71 	{&purple1,	200, 30,  255},
72 	{&white, 	255, 255, 255},
73 	{&rblue1,	81,  81,  255},
74 	{&rblue2,	0,   0,   227},
75 	{&rblue3,	0,   0,   130},
76 	{&rblue4,	0,   0,   80 },
77 	{&orange,	255, 120, 0  },
78 	{&yorange,	255, 170, 0  },
79 	{&dred,		80,  0,   0  },
80 	{&maroon1,	154, 49,  49 },
81 	{&maroon2,	125, 24,  24 },
82 	{NULL}
83 };
84 
85 
P_InitEffects(void)86 void P_InitEffects (void)
87 {
88 	const struct ColorList *color = Colors;
89 	DWORD *palette = GetDefaultPalette()->basecolors;
90 	int numcolors = GetDefaultPalette()->numcolors;
91 
92 	while (color->color) {
93 		*(color->color) = BestColor (palette, color->r, color->g, color->b, numcolors);
94 		color++;
95 	}
96 }
97 
P_DrawSplash(int count,fixed_t x,fixed_t y,fixed_t z,angle_t angle,int kind)98 void P_DrawSplash (int count, fixed_t x, fixed_t y, fixed_t z, angle_t angle, int kind)
99 {
100 	if (!clientside)
101 		return;
102 
103 	int color1, color2;
104 
105 	switch (kind) {
106 		case 1:		// Spark
107 			color1 = orange;
108 			color2 = yorange;
109 			break;
110 		default:
111 			return;
112 	}
113 
114 	for (; count; count--) {
115 		particle_t *p = JitterParticle (10);
116 		angle_t an;
117 
118 		if (!p)
119 			break;
120 
121 		p->size = 2;
122 		p->color = M_Random() & 0x80 ? color1 : color2;
123 		p->velz -= M_Random () * 512;
124 		p->accz -= FRACUNIT/8;
125 		p->accx += (M_Random () - 128) * 8;
126 		p->accy += (M_Random () - 128) * 8;
127 		p->z = z - M_Random () * 1024;
128 		an = (angle + (M_Random() << 21)) >> ANGLETOFINESHIFT;
129 		p->x = x + (M_Random () & 15)*finecosine[an];
130 		p->y = y + (M_Random () & 15)*finesine[an];
131 	}
132 }
133 
P_DrawSplash2(int count,fixed_t x,fixed_t y,fixed_t z,angle_t angle,int updown,int kind)134 void P_DrawSplash2 (int count, fixed_t x, fixed_t y, fixed_t z, angle_t angle, int updown, int kind)
135 {/*
136 	int color1, color2, zvel, zspread, zadd;
137 
138 	switch (kind) {
139 		case 0:		// Blood
140 			color1 = red;
141 			color2 = dred;
142 			break;
143 		case 1:		// Gunshot
144 			color1 = grey3;
145 			color2 = grey5;
146 			break;
147 		case 2:		// Smoke
148 			color1 = grey3;
149 			color2 = grey1;
150 			break;
151 		default:
152 			return;
153 	}
154 
155 	zvel = -128;
156 	zspread = updown ? -6000 : 6000;
157 	zadd = (updown == 2) ? -128 : 0;
158 
159 	for (; count; count--) {
160 		particle_t *p = NewParticle ();
161 		angle_t an;
162 
163 		if (!p)
164 			break;
165 
166 		p->ttl = 12;
167 		p->fade = FADEFROMTTL(12);
168 		p->trans = 255;
169 		p->size = 4;
170 		p->color = M_Random() & 0x80 ? color1 : color2;
171 		p->velz = M_Random () * zvel;
172 		p->accz = -FRACUNIT/22;
173 		if (kind) {
174 			an = (angle + ((M_Random() - 128) << 23)) >> ANGLETOFINESHIFT;
175 			p->velx = (M_Random () * finecosine[an]) >> 11;
176 			p->vely = (M_Random () * finesine[an]) >> 11;
177 			p->accx = p->velx >> 4;
178 			p->accy = p->vely >> 4;
179 		}
180 		p->z = z + (M_Random () + zadd) * zspread;
181 		an = (angle + ((M_Random() - 128) << 22)) >> ANGLETOFINESHIFT;
182 		p->x = x + (M_Random () & 31)*finecosine[an];
183 		p->y = y + (M_Random () & 31)*finesine[an];
184 	}
185 */}
186 
187 //
188 // [RH] Particle functions
189 //
190 #ifndef _MSC_VER
191 // inlined with VC++
NewParticle(void)192 particle_t *NewParticle (void)
193 {
194 	if (!clientside)
195 		return NULL;
196 
197 	particle_t *result = NULL;
198 	if (InactiveParticles != NO_PARTICLE)
199 	{
200 		result = Particles + InactiveParticles;
201 		InactiveParticles = result->next;
202 		result->next = ActiveParticles;
203 		ActiveParticles = result - Particles;
204 	}
205 	return result;
206 }
207 #endif
208 
MakeFountain(AActor * actor,int color1,int color2)209 static void MakeFountain (AActor *actor, int color1, int color2)
210 {
211 	if (!clientside)
212 		return;
213 
214 	particle_t *particle;
215 
216 	if (!(level.time & 1))
217 		return;
218 
219 	particle = JitterParticle (51);
220 
221 	if (particle) {
222 		angle_t an = M_Random()<<(24-ANGLETOFINESHIFT);
223 		fixed_t out = FixedMul (actor->radius, M_Random()<<8);
224 
225 		particle->x = actor->x + FixedMul (out, finecosine[an]);
226 		particle->y = actor->y + FixedMul (out, finesine[an]);
227 		particle->z = actor->z + actor->height + FRACUNIT;
228 		if (out < actor->radius/8)
229 			particle->velz += FRACUNIT*10/3;
230 		else
231 			particle->velz += FRACUNIT*3;
232 		particle->accz -= FRACUNIT/11;
233 		if (M_Random() < 30) {
234 			particle->size = 4;
235 			particle->color = color2;
236 		} else {
237 			particle->size = 6;
238 			particle->color = color1;
239 		}
240 	}
241 }
242 
243 //
244 // AddParticle
245 //
246 // Creates a particle with "jitter"
247 //
JitterParticle(int ttl)248 particle_t *JitterParticle (int ttl)
249 {
250 	if (!clientside)
251 		return NULL;
252 
253 	particle_t *particle = NewParticle ();
254 
255 	if (particle) {
256 		fixed_t *val = &particle->velx;
257 		int i;
258 
259 		// Set initial velocities
260 		for (i = 3; i; i--, val++)
261 			*val = (FRACUNIT/4096) * (M_Random () - 128);
262 		// Set initial accelerations
263 		for (i = 3; i; i--, val++)
264 			*val = (FRACUNIT/16384) * (M_Random () - 128);
265 
266 		particle->trans = 255;	// fully opaque
267 		particle->ttl = ttl;
268 		particle->fade = FADEFROMTTL(ttl);
269 	}
270 	return particle;
271 }
272 
273 //
274 // P_RunEffects
275 //
276 // Run effects on all mobjs in world
277 //
P_RunEffects(void)278 void P_RunEffects (void)
279 {
280 	if (!clientside)
281 		return;
282 
283 	//int pnum = 0;/* = (consoleplayer().camera->subsector->sector - sectors) * numsectors*/;
284 	AActor *actor;
285 	TThinkerIterator<AActor> iterator;
286 
287 	while ( (actor = iterator.Next ()) )
288 	{
289 		if (actor->effects)
290 		{
291 			// Only run the effect if the mobj is potentially visible
292 			//int rnum = pnum + (actor->subsector->sector - sectors);
293 			//if (rejectempty || !(rejectmatrix[rnum>>3] & (1 << (rnum & 7))))
294 			{
295 				P_RunEffect (actor, actor->effects);
296 			}
297 		}
298 	}
299 }
300 
P_RunEffect(AActor * actor,int effects)301 void P_RunEffect (AActor *actor, int effects)
302 {
303 	if (!actor || !clientside)
304 		return;
305 
306 	angle_t moveangle = R_PointToAngle2(0,0,actor->momx,actor->momy);
307 	particle_t *particle;
308 
309 	if ((effects & FX_ROCKET) && cl_rockettrails) {
310 		// Rocket trail
311 
312 		fixed_t backx = actor->x - FixedMul (finecosine[(moveangle)>>ANGLETOFINESHIFT], actor->radius*2);
313 		fixed_t backy = actor->y - FixedMul (finesine[(moveangle)>>ANGLETOFINESHIFT], actor->radius*2);
314 		fixed_t backz = actor->z - (actor->height>>3) * (actor->momz>>16) + (2*actor->height)/3;
315 
316 		angle_t an = (moveangle + ANG90) >> ANGLETOFINESHIFT;
317 		int i, speed;
318 
319 		particle = JitterParticle (3 + (M_Random() & 31));
320 		if (particle) {
321 			fixed_t pathdist = M_Random()<<8;
322 			particle->x = backx - FixedMul(actor->momx, pathdist);
323 			particle->y = backy - FixedMul(actor->momy, pathdist);
324 			particle->z = backz - FixedMul(actor->momz, pathdist);
325 			speed = (M_Random () - 128) * (FRACUNIT/200);
326 			particle->velx += FixedMul (speed, finecosine[an]);
327 			particle->vely += FixedMul (speed, finesine[an]);
328 			particle->velz -= FRACUNIT/36;
329 			particle->accz -= FRACUNIT/20;
330 			particle->color = yellow;
331 			particle->size = 2;
332 		}
333 		for (i = 6; i; i--) {
334 			particle_t *particle = JitterParticle (3 + (M_Random() & 31));
335 			if (particle) {
336 				fixed_t pathdist = M_Random()<<8;
337 				particle->x = backx - FixedMul(actor->momx, pathdist);
338 				particle->y = backy - FixedMul(actor->momy, pathdist);
339 				particle->z = backz - FixedMul(actor->momz, pathdist) + (M_Random() << 10);
340 				speed = (M_Random () - 128) * (FRACUNIT/200);
341 				particle->velx += FixedMul (speed, finecosine[an]);
342 				particle->vely += FixedMul (speed, finesine[an]);
343 				particle->velz += FRACUNIT/80;
344 				particle->accz += FRACUNIT/40;
345 				if (M_Random () & 7)
346 					particle->color = grey2;
347 				else
348 					particle->color = grey1;
349 				particle->size = 3;
350 			} else
351 				break;
352 		}
353 	}
354 	if ((effects & FX_GRENADE) && (cl_rockettrails)) {
355 		// Grenade trail
356 
357 		P_DrawSplash2 (6,
358 			actor->x - FixedMul (finecosine[(moveangle)>>ANGLETOFINESHIFT], actor->radius*2),
359 			actor->y - FixedMul (finesine[(moveangle)>>ANGLETOFINESHIFT], actor->radius*2),
360 			actor->z - (actor->height>>3) * (actor->momz>>16) + (2*actor->height)/3,
361 			moveangle + ANG180, 2, 2);
362 	}
363 	if (effects & FX_FOUNTAINMASK) {
364 		// Particle fountain
365 
366 		static const int *fountainColors[16] =
367 			{ &black,	&black,
368 			  &red,		&red1,
369 			  &green,	&green1,
370 			  &blue,	&blue1,
371 			  &yellow,	&yellow1,
372 			  &purple,	&purple1,
373 			  &black,	&grey3,
374 			  &grey4,	&white
375 			};
376 		int color = (effects & FX_FOUNTAINMASK) >> 15;
377 		MakeFountain (actor, *fountainColors[color], *fountainColors[color+1]);
378 	}
379 }
380 
P_ThinkParticles(void)381 void P_ThinkParticles (void)
382 {
383 	if (!clientside)
384 		return;
385 
386 	int i;
387 	particle_t *particle, *prev;
388 
389 	i = ActiveParticles;
390 	prev = NULL;
391 	while (i != NO_PARTICLE) {
392 		byte oldtrans;
393 
394 		particle = Particles + i;
395 		i = particle->next;
396 		oldtrans = particle->trans;
397 		particle->trans -= particle->fade;
398 		if (oldtrans < particle->trans || --particle->ttl == 0) {
399 			memset (particle, 0, sizeof(particle_t));
400 			if (prev)
401 				prev->next = i;
402 			else
403 				ActiveParticles = i;
404 			particle->next = InactiveParticles;
405 			InactiveParticles = particle - Particles;
406 			continue;
407 		}
408 		particle->x += particle->velx;
409 		particle->y += particle->vely;
410 		particle->z += particle->velz;
411 		particle->velx += particle->accx;
412 		particle->vely += particle->accy;
413 		particle->velz += particle->accz;
414 		prev = particle;
415 	}
416 }
417 
418 
P_DrawRailTrail(v3double_t & start,v3double_t & end)419 void P_DrawRailTrail(v3double_t &start, v3double_t &end)
420 {
421 	if (!clientside)
422 		return;
423 
424 	v3double_t step, dir, pos, extend, point;
425 
426 	M_SubVec3(&dir, &end, &start);
427 
428 	double length = M_LengthVec3(&dir);
429 	int steps = (int)(length*0.3333);
430 
431 	if (!length)	// line is 0 length, so nothing to do
432 		return;
433 
434 	// The railgun's sound is a special case. It gets played from the
435 	// point on the slug's trail that is closest to the hearing player.
436 	AActor *mo = consoleplayer().camera;
437 
438 	double ilength = 1.0 / length;
439 
440 	if (abs(mo->x - FLOAT2FIXED(start.x)) < 20 * FRACUNIT &&
441 		abs(mo->y - FLOAT2FIXED(start.y)) < 20 * FRACUNIT)
442 	{
443 		// This player (probably) fired the railgun
444 		S_Sound (mo, CHAN_WEAPON, "weapons/railgf", 1, ATTN_NORM);
445 	}
446 	else
447 	{
448 		// Only consider sound in 2D (for now, anyway)
449 		double r = ((start.y - FIXED2FLOAT(mo->y)) * (-dir.y) -
450 				(start.x - FIXED2FLOAT(mo->x)) * (dir.x)) * ilength * ilength;
451 
452 		M_ScaleVec3(&point, &dir, r);
453 		M_AddVec3(&point, &start, &point);
454 		point.z = start.z;
455 
456 		S_Sound (FLOAT2FIXED(point.x), FLOAT2FIXED(point.y),
457 			CHAN_WEAPON, "weapons/railgf", 1, ATTN_NORM);
458 	}
459 
460 	if (!clientside)
461 		return;
462 
463 	M_ScaleVec3(&dir, &dir, ilength);
464 	M_PerpendicularVec3(&extend, &dir);
465 	M_ScaleVec3(&extend, &extend, 3.0);
466 	M_ScaleVec3(&step, &dir, 3.0);
467 
468 	pos = start;
469 
470 	float deg = 270.0f;
471 	for (int i = steps; i; i--) {
472 		particle_t *p = NewParticle ();
473 
474 		if (!p)
475 			return;
476 
477 		p->trans = 255;
478 		p->ttl = 35;
479 		p->fade = FADEFROMTTL(35);
480 		p->size = 3;
481 
482 		v3double_t tempvec;
483 		M_RotatePointAroundVector(&tempvec, &dir, &extend, deg);
484 
485 		p->velx = FLOAT2FIXED(tempvec.x)>>4;
486 		p->vely = FLOAT2FIXED(tempvec.y)>>4;
487 		p->velz = FLOAT2FIXED(tempvec.z)>>4;
488 		M_AddVec3(&tempvec, &tempvec, &pos);
489 		deg += 14;
490 		if (deg >= 360)
491 			deg -= 360;
492 		p->x = FLOAT2FIXED(tempvec.x);
493 		p->y = FLOAT2FIXED(tempvec.y);
494 		p->z = FLOAT2FIXED(tempvec.z);
495 		M_AddVec3(&pos, &pos, &step);
496 
497 		int rand = M_Random();
498 
499 		if (rand < 155)
500 			p->color = rblue2;
501 		else if (rand < 188)
502 			p->color = rblue1;
503 		else if (rand < 222)
504 			p->color = rblue3;
505 		else
506 			p->color = rblue4;
507 	}
508 
509 	pos = start;
510 
511 	for (int i = steps; i; i--) {
512 		particle_t *p = JitterParticle (33);
513 
514 		if (!p)
515 			return;
516 
517 		p->size = 2;
518 		p->x = FLOAT2FIXED(pos.x);
519 		p->y = FLOAT2FIXED(pos.y);
520 		p->z = FLOAT2FIXED(pos.z);
521 		p->accz -= FRACUNIT/4096;
522 		M_AddVec3(&pos, &pos, &step);
523 
524 		int rand = M_Random();
525 
526 		if (rand < 85)
527 			p->color = orange;
528 		else if (rand < 170)
529 			p->color = grey2;
530 		else
531 			p->color = yorange;
532 
533 		p->color = yellow;
534 	}
535 }
536 
P_DisconnectEffect(AActor * actor)537 void P_DisconnectEffect (AActor *actor)
538 {
539 	if (!actor || !clientside)
540 		return;
541 
542 	int i;
543 
544 	for (i = 64; i; i--) {
545 		particle_t *p = JitterParticle (TICRATE*2);
546 
547 		if (!p)
548 			break;
549 
550 		p->x = actor->x + ((M_Random()-128)<<9) * (actor->radius>>FRACBITS);
551 		p->y = actor->y + ((M_Random()-128)<<9) * (actor->radius>>FRACBITS);
552 		p->z = actor->z + (M_Random()<<8) * (actor->height>>FRACBITS);
553 		p->accz -= FRACUNIT/4096;
554 		p->color = M_Random() < 128 ? maroon1 : maroon2;
555 		p->size = 4;
556 	}
557 }
558 
559 VERSION_CONTROL (p_effect_cpp, "$Id: c_effect.cpp 4469 2014-01-03 23:38:29Z dr_sean $")
560