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