2 Copyright (C) 1996-1997 Id Software, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 // cl_tent.c -- client side temporary entities
24 cvar_t r_glowinglightning = {"r_glowinglightning", "1", true};
26 int num_temp_entities;
27 entity_t cl_temp_entities[MAX_TEMP_ENTITIES];
28 beam_t cl_beams[MAX_BEAMS];
31 sfx_t *cl_sfx_knighthit;
43 void CL_InitTEnts (void)
45 Cvar_RegisterVariable(&r_glowinglightning);
46 cl_sfx_wizhit = S_PrecacheSound ("wizard/hit.wav");
47 cl_sfx_knighthit = S_PrecacheSound ("hknight/hit.wav");
48 cl_sfx_tink1 = S_PrecacheSound ("weapons/tink1.wav");
49 cl_sfx_ric1 = S_PrecacheSound ("weapons/ric1.wav");
50 cl_sfx_ric2 = S_PrecacheSound ("weapons/ric2.wav");
51 cl_sfx_ric3 = S_PrecacheSound ("weapons/ric3.wav");
52 cl_sfx_r_exp3 = S_PrecacheSound ("weapons/r_exp3.wav");
60 void CL_ParseBeam (model_t *m)
67 ent = MSG_ReadShort ();
69 start[0] = MSG_ReadCoord ();
70 start[1] = MSG_ReadCoord ();
71 start[2] = MSG_ReadCoord ();
73 end[0] = MSG_ReadCoord ();
74 end[1] = MSG_ReadCoord ();
75 end[2] = MSG_ReadCoord ();
77 // override any beam with the same entity
78 for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++)
83 b->endtime = cl.time + 0.2;
84 VectorCopy (start, b->start);
85 VectorCopy (end, b->end);
90 for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++)
92 if (!b->model || b->endtime < cl.time)
96 b->endtime = cl.time + 0.2;
97 VectorCopy (start, b->start);
98 VectorCopy (end, b->end);
102 Con_Printf ("beam list overflow!\n");
105 //void R_BlastParticles(vec3_t org, vec_t radius, vec_t power);
106 void R_BloodShower (vec3_t mins, vec3_t maxs, float velspeed, int count);
107 void R_ParticleCube (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int gravity, int randomvel);
108 void R_ParticleRain (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int type);
110 // attempts to find the nearest non-solid location, used for explosions mainly
111 void FindNonSolidLocation(vec3_t pos)
113 if (SV_HullPointContents (cl.worldmodel->hulls, 0, pos) != CONTENTS_SOLID) return;
114 pos[0]-=1;if (SV_HullPointContents (cl.worldmodel->hulls, 0, pos) != CONTENTS_SOLID) return;
115 pos[0]+=2;if (SV_HullPointContents (cl.worldmodel->hulls, 0, pos) != CONTENTS_SOLID) return;
117 pos[1]-=1;if (SV_HullPointContents (cl.worldmodel->hulls, 0, pos) != CONTENTS_SOLID) return;
118 pos[1]+=2;if (SV_HullPointContents (cl.worldmodel->hulls, 0, pos) != CONTENTS_SOLID) return;
120 pos[2]-=1;if (SV_HullPointContents (cl.worldmodel->hulls, 0, pos) != CONTENTS_SOLID) return;
121 pos[2]+=2;if (SV_HullPointContents (cl.worldmodel->hulls, 0, pos) != CONTENTS_SOLID) return;
130 void CL_ParseTEnt (void)
138 int colorStart, colorLength, count;
142 type = MSG_ReadByte ();
145 case TE_WIZSPIKE: // spike hitting wall
146 pos[0] = MSG_ReadCoord ();
147 pos[1] = MSG_ReadCoord ();
148 pos[2] = MSG_ReadCoord ();
149 R_RunParticleEffect (pos, vec3_origin, 20, 30);
150 S_StartSound (-1, 0, cl_sfx_wizhit, pos, 1, 1);
153 case TE_KNIGHTSPIKE: // spike hitting wall
154 pos[0] = MSG_ReadCoord ();
155 pos[1] = MSG_ReadCoord ();
156 pos[2] = MSG_ReadCoord ();
157 R_RunParticleEffect (pos, vec3_origin, 226, 20);
158 S_StartSound (-1, 0, cl_sfx_knighthit, pos, 1, 1);
161 case TE_SPIKE: // spike hitting wall
162 pos[0] = MSG_ReadCoord ();
163 pos[1] = MSG_ReadCoord ();
164 pos[2] = MSG_ReadCoord ();
165 // LordHavoc: changed to spark shower
166 R_SparkShower(pos, vec3_origin, 15);
167 //R_RunParticleEffect (pos, vec3_origin, 0, 10);
169 S_StartSound (-1, 0, cl_sfx_tink1, pos, 1, 1);
174 S_StartSound (-1, 0, cl_sfx_ric1, pos, 1, 1);
176 S_StartSound (-1, 0, cl_sfx_ric2, pos, 1, 1);
178 S_StartSound (-1, 0, cl_sfx_ric3, pos, 1, 1);
181 case TE_SPIKEQUAD: // quad spike hitting wall
182 pos[0] = MSG_ReadCoord ();
183 pos[1] = MSG_ReadCoord ();
184 pos[2] = MSG_ReadCoord ();
185 // LordHavoc: changed to spark shower
186 R_SparkShower(pos, vec3_origin, 15);
187 //R_RunParticleEffect (pos, vec3_origin, 0, 10);
188 dl = CL_AllocDlight (0);
189 VectorCopy (pos, dl->origin);
191 dl->die = cl.time + 0.2;
193 dl->color[0] = 0.05;dl->color[1] = 0.05;dl->color[2] = 0.8;
194 S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
196 S_StartSound (-1, 0, cl_sfx_tink1, pos, 1, 1);
201 S_StartSound (-1, 0, cl_sfx_ric1, pos, 1, 1);
203 S_StartSound (-1, 0, cl_sfx_ric2, pos, 1, 1);
205 S_StartSound (-1, 0, cl_sfx_ric3, pos, 1, 1);
208 case TE_SUPERSPIKE: // super spike hitting wall
209 pos[0] = MSG_ReadCoord ();
210 pos[1] = MSG_ReadCoord ();
211 pos[2] = MSG_ReadCoord ();
212 // LordHavoc: changed to dust shower
213 R_SparkShower(pos, vec3_origin, 30);
214 //R_RunParticleEffect (pos, vec3_origin, 0, 20);
216 S_StartSound (-1, 0, cl_sfx_tink1, pos, 1, 1);
221 S_StartSound (-1, 0, cl_sfx_ric1, pos, 1, 1);
223 S_StartSound (-1, 0, cl_sfx_ric2, pos, 1, 1);
225 S_StartSound (-1, 0, cl_sfx_ric3, pos, 1, 1);
228 case TE_SUPERSPIKEQUAD: // quad super spike hitting wall
229 pos[0] = MSG_ReadCoord ();
230 pos[1] = MSG_ReadCoord ();
231 pos[2] = MSG_ReadCoord ();
232 // LordHavoc: changed to dust shower
233 R_SparkShower(pos, vec3_origin, 30);
234 //R_RunParticleEffect (pos, vec3_origin, 0, 20);
235 dl = CL_AllocDlight (0);
236 VectorCopy (pos, dl->origin);
238 dl->die = cl.time + 0.2;
240 dl->color[0] = 0.05;dl->color[1] = 0.05;dl->color[2] = 0.8;
242 S_StartSound (-1, 0, cl_sfx_tink1, pos, 1, 1);
247 S_StartSound (-1, 0, cl_sfx_ric1, pos, 1, 1);
249 S_StartSound (-1, 0, cl_sfx_ric2, pos, 1, 1);
251 S_StartSound (-1, 0, cl_sfx_ric3, pos, 1, 1);
254 // LordHavoc: added for improved blood splatters
255 case TE_BLOOD: // blood puff
256 pos[0] = MSG_ReadCoord ();
257 pos[1] = MSG_ReadCoord ();
258 pos[2] = MSG_ReadCoord ();
259 dir[0] = MSG_ReadChar ();
260 dir[1] = MSG_ReadChar ();
261 dir[2] = MSG_ReadChar ();
262 count = MSG_ReadByte (); // amount of particles
265 case TE_BLOOD2: // blood puff
266 pos[0] = MSG_ReadCoord ();
267 pos[1] = MSG_ReadCoord ();
268 pos[2] = MSG_ReadCoord ();
271 case TE_SPARK: // spark shower
272 pos[0] = MSG_ReadCoord ();
273 pos[1] = MSG_ReadCoord ();
274 pos[2] = MSG_ReadCoord ();
275 dir[0] = MSG_ReadChar ();
276 dir[1] = MSG_ReadChar ();
277 dir[2] = MSG_ReadChar ();
278 count = MSG_ReadByte (); // amount of particles
279 R_SparkShower(pos, dir, count);
281 // LordHavoc: added for improved gore
282 case TE_BLOODSHOWER: // vaporized body
283 pos[0] = MSG_ReadCoord (); // mins
284 pos[1] = MSG_ReadCoord ();
285 pos[2] = MSG_ReadCoord ();
286 dir[0] = MSG_ReadCoord (); // maxs
287 dir[1] = MSG_ReadCoord ();
288 dir[2] = MSG_ReadCoord ();
289 velspeed = MSG_ReadCoord (); // speed
290 count = MSG_ReadShort (); // number of particles
291 R_BloodShower(pos, dir, velspeed, count);
293 case TE_PARTICLECUBE: // general purpose particle effect
294 pos[0] = MSG_ReadCoord (); // mins
295 pos[1] = MSG_ReadCoord ();
296 pos[2] = MSG_ReadCoord ();
297 pos2[0] = MSG_ReadCoord (); // maxs
298 pos2[1] = MSG_ReadCoord ();
299 pos2[2] = MSG_ReadCoord ();
300 dir[0] = MSG_ReadCoord (); // dir
301 dir[1] = MSG_ReadCoord ();
302 dir[2] = MSG_ReadCoord ();
303 count = MSG_ReadShort (); // number of particles
304 colorStart = MSG_ReadByte (); // color
305 colorLength = MSG_ReadByte (); // gravity (1 or 0)
306 velspeed = MSG_ReadCoord (); // randomvel
307 R_ParticleCube(pos, pos2, dir, count, colorStart, colorLength, velspeed);
310 case TE_PARTICLERAIN: // general purpose particle effect
311 pos[0] = MSG_ReadCoord (); // mins
312 pos[1] = MSG_ReadCoord ();
313 pos[2] = MSG_ReadCoord ();
314 pos2[0] = MSG_ReadCoord (); // maxs
315 pos2[1] = MSG_ReadCoord ();
316 pos2[2] = MSG_ReadCoord ();
317 dir[0] = MSG_ReadCoord (); // dir
318 dir[1] = MSG_ReadCoord ();
319 dir[2] = MSG_ReadCoord ();
320 count = MSG_ReadShort (); // number of particles
321 colorStart = MSG_ReadByte (); // color
322 R_ParticleRain(pos, pos2, dir, count, colorStart, 0);
325 case TE_PARTICLESNOW: // general purpose particle effect
326 pos[0] = MSG_ReadCoord (); // mins
327 pos[1] = MSG_ReadCoord ();
328 pos[2] = MSG_ReadCoord ();
329 pos2[0] = MSG_ReadCoord (); // maxs
330 pos2[1] = MSG_ReadCoord ();
331 pos2[2] = MSG_ReadCoord ();
332 dir[0] = MSG_ReadCoord (); // dir
333 dir[1] = MSG_ReadCoord ();
334 dir[2] = MSG_ReadCoord ();
335 count = MSG_ReadShort (); // number of particles
336 colorStart = MSG_ReadByte (); // color
337 R_ParticleRain(pos, pos2, dir, count, colorStart, 1);
340 case TE_GUNSHOT: // bullet hitting wall
341 pos[0] = MSG_ReadCoord ();
342 pos[1] = MSG_ReadCoord ();
343 pos[2] = MSG_ReadCoord ();
344 // LordHavoc: changed to dust shower
345 R_SparkShower(pos, vec3_origin, 15);
346 //R_RunParticleEffect (pos, vec3_origin, 0, 20);
349 case TE_GUNSHOTQUAD: // quad bullet hitting wall
350 pos[0] = MSG_ReadCoord ();
351 pos[1] = MSG_ReadCoord ();
352 pos[2] = MSG_ReadCoord ();
353 R_SparkShower(pos, vec3_origin, 15);
354 dl = CL_AllocDlight (0);
355 VectorCopy (pos, dl->origin);
357 dl->die = cl.time + 0.2;
359 dl->color[0] = 0.05;dl->color[1] = 0.05;dl->color[2] = 0.8;
362 case TE_EXPLOSION: // rocket explosion
363 pos[0] = MSG_ReadCoord ();
364 pos[1] = MSG_ReadCoord ();
365 pos[2] = MSG_ReadCoord ();
366 FindNonSolidLocation(pos);
367 R_ParticleExplosion (pos, false);
368 // R_BlastParticles (pos, 120, 120);
369 dl = CL_AllocDlight (0);
370 VectorCopy (pos, dl->origin);
372 dl->die = cl.time + 0.5;
374 dl->color[0] = 1.0;dl->color[1] = 0.8;dl->color[2] = 0.4;
375 S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
378 case TE_EXPLOSIONQUAD: // quad rocket explosion
379 pos[0] = MSG_ReadCoord ();
380 pos[1] = MSG_ReadCoord ();
381 pos[2] = MSG_ReadCoord ();
382 FindNonSolidLocation(pos);
383 R_ParticleExplosion (pos, false);
384 // R_BlastParticles (pos, 120, 480);
385 dl = CL_AllocDlight (0);
386 VectorCopy (pos, dl->origin);
388 dl->die = cl.time + 0.5;
390 dl->color[0] = 0.5;dl->color[1] = 0.4;dl->color[2] = 1.0;
391 S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
395 case TE_SMOKEEXPLOSION: // rocket explosion with a cloud of smoke
396 pos[0] = MSG_ReadCoord ();
397 pos[1] = MSG_ReadCoord ();
398 pos[2] = MSG_ReadCoord ();
399 FindNonSolidLocation(pos);
400 R_ParticleExplosion (pos, true);
401 dl = CL_AllocDlight (0);
402 VectorCopy (pos, dl->origin);
404 dl->die = cl.time + 0.5;
406 dl->color[0] = 1.0;dl->color[1] = 0.8;dl->color[2] = 0.4;
407 S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
411 case TE_EXPLOSION3: // Nehahra movie colored lighting explosion
412 pos[0] = MSG_ReadCoord ();
413 pos[1] = MSG_ReadCoord ();
414 pos[2] = MSG_ReadCoord ();
415 FindNonSolidLocation(pos);
416 R_ParticleExplosion (pos, false);
417 // R_BlastParticles (pos, 120, 120);
418 dl = CL_AllocDlight (0);
419 VectorCopy (pos, dl->origin);
421 dl->die = cl.time + 0.5;
423 dl->color[0] = MSG_ReadCoord();dl->color[1] = MSG_ReadCoord();dl->color[2] = MSG_ReadCoord();
424 S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
427 case TE_EXPLOSIONRGB: // colored lighting explosion
428 pos[0] = MSG_ReadCoord ();
429 pos[1] = MSG_ReadCoord ();
430 pos[2] = MSG_ReadCoord ();
431 FindNonSolidLocation(pos);
432 R_ParticleExplosion (pos, false);
433 // R_BlastParticles (pos, 120, 120);
434 dl = CL_AllocDlight (0);
435 VectorCopy (pos, dl->origin);
437 dl->die = cl.time + 0.5;
439 dl->color[0] = MSG_ReadByte() * (1.0 / 255.0);dl->color[1] = MSG_ReadByte() * (1.0 / 255.0);dl->color[2] = MSG_ReadByte() * (1.0 / 255.0);
440 S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
443 case TE_TAREXPLOSION: // tarbaby explosion
444 pos[0] = MSG_ReadCoord ();
445 pos[1] = MSG_ReadCoord ();
446 pos[2] = MSG_ReadCoord ();
447 FindNonSolidLocation(pos);
448 R_BlobExplosion (pos);
449 // R_BlastParticles (pos, 120, 120);
451 S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
452 dl = CL_AllocDlight (0);
453 VectorCopy (pos, dl->origin);
455 dl->die = cl.time + 0.5;
457 dl->color[0] = 0.8;dl->color[1] = 0.4;dl->color[2] = 1.0;
458 S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
461 case TE_LIGHTNING1: // lightning bolts
462 CL_ParseBeam (Mod_ForName("progs/bolt.mdl", true));
465 case TE_LIGHTNING2: // lightning bolts
466 CL_ParseBeam (Mod_ForName("progs/bolt2.mdl", true));
469 case TE_LIGHTNING3: // lightning bolts
470 CL_ParseBeam (Mod_ForName("progs/bolt3.mdl", true));
474 case TE_BEAM: // grappling hook beam
475 CL_ParseBeam (Mod_ForName("progs/beam.mdl", true));
479 // LordHavoc: for compatibility with the Nehahra movie...
480 case TE_LIGHTNING4NEH:
481 CL_ParseBeam (Mod_ForName(MSG_ReadString(), true));
485 pos[0] = MSG_ReadCoord ();
486 pos[1] = MSG_ReadCoord ();
487 pos[2] = MSG_ReadCoord ();
492 pos[0] = MSG_ReadCoord ();
493 pos[1] = MSG_ReadCoord ();
494 pos[2] = MSG_ReadCoord ();
495 R_TeleportSplash (pos);
498 case TE_EXPLOSION2: // color mapped explosion
499 pos[0] = MSG_ReadCoord ();
500 pos[1] = MSG_ReadCoord ();
501 pos[2] = MSG_ReadCoord ();
502 FindNonSolidLocation(pos);
503 colorStart = MSG_ReadByte ();
504 colorLength = MSG_ReadByte ();
505 R_ParticleExplosion2 (pos, colorStart, colorLength);
506 // R_BlastParticles (pos, 80, 80);
507 dl = CL_AllocDlight (0);
508 VectorCopy (pos, dl->origin);
510 dl->die = cl.time + 0.5;
512 tempcolor = (byte *)&d_8to24table[(rand()%colorLength) + colorStart];
513 dl->color[0] = tempcolor[0] * (1.0f / 255.0f);dl->color[1] = tempcolor[1] * (1.0f / 255.0f);dl->color[2] = tempcolor[2] * (1.0f / 255.0f);
514 S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
518 Host_Error ("CL_ParseTEnt: bad type %d", type);
528 entity_t *CL_NewTempEntity (void)
532 if (cl_numvisedicts == MAX_VISEDICTS)
534 if (num_temp_entities == MAX_TEMP_ENTITIES)
536 ent = &cl_temp_entities[num_temp_entities];
537 memset (ent, 0, sizeof(*ent));
539 cl_visedicts[cl_numvisedicts] = ent;
542 ent->colormap = -1; // no special coloring
545 ent->colormod[0] = ent->colormod[1] = ent->colormod[2] = 1;
555 void CL_UpdateTEnts (void)
566 num_temp_entities = 0;
569 for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++)
571 if (!b->model || b->endtime < cl.time)
574 // if coming from the player, update the start position
575 if (b->entity == cl.viewentity)
577 VectorCopy (cl_entities[cl.viewentity].origin, b->start);
580 // calculate pitch and yaw
581 VectorSubtract (b->end, b->start, dist);
583 if (dist[1] == 0 && dist[0] == 0)
593 yaw = (int) (atan2(dist[1], dist[0]) * 180 / M_PI);
597 forward = sqrt (dist[0]*dist[0] + dist[1]*dist[1]);
598 pitch = (int) (atan2(dist[2], forward) * 180 / M_PI);
603 // add new entities for the lightning
604 VectorCopy (b->start, org);
605 d = VectorNormalizeLength(dist);
608 ent = CL_NewTempEntity ();
611 VectorCopy (org, ent->origin);
612 ent->model = b->model;
613 ent->effects = EF_FULLBRIGHT;
614 ent->angles[0] = pitch;
615 ent->angles[1] = yaw;
616 ent->angles[2] = rand()%360;
618 if (r_glowinglightning.value)
620 dl = CL_AllocDlight (0);
621 VectorCopy (ent->origin, dl->origin);
622 dl->radius = 100 + (rand()&31);
623 dl->die = cl.time + 0.001;
624 dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 1;
627 VectorMA(org, 30, dist, org);