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