got rid of cl_glowinglightning cvar, lightning no longer glows
[divverent/darkplaces.git] / cl_tent.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
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.
8
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.
12
13 See the GNU General Public License for more details.
14
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.
18
19 */
20 // cl_tent.c -- client side temporary entities
21
22 #include "quakedef.h"
23
24 int                     num_temp_entities;
25 entity_t        cl_temp_entities[MAX_TEMP_ENTITIES];
26 beam_t          cl_beams[MAX_BEAMS];
27
28 model_t         *cl_model_bolt = NULL;
29 model_t         *cl_model_bolt2 = NULL;
30 model_t         *cl_model_bolt3 = NULL;
31 model_t         *cl_model_beam = NULL;
32
33 sfx_t           *cl_sfx_wizhit;
34 sfx_t           *cl_sfx_knighthit;
35 sfx_t           *cl_sfx_tink1;
36 sfx_t           *cl_sfx_ric1;
37 sfx_t           *cl_sfx_ric2;
38 sfx_t           *cl_sfx_ric3;
39 sfx_t           *cl_sfx_r_exp3;
40
41 /*
42 =================
43 CL_ParseTEnt
44 =================
45 */
46 void CL_InitTEnts (void)
47 {
48         cl_sfx_wizhit = S_PrecacheSound ("wizard/hit.wav");
49         cl_sfx_knighthit = S_PrecacheSound ("hknight/hit.wav");
50         cl_sfx_tink1 = S_PrecacheSound ("weapons/tink1.wav");
51         cl_sfx_ric1 = S_PrecacheSound ("weapons/ric1.wav");
52         cl_sfx_ric2 = S_PrecacheSound ("weapons/ric2.wav");
53         cl_sfx_ric3 = S_PrecacheSound ("weapons/ric3.wav");
54         cl_sfx_r_exp3 = S_PrecacheSound ("weapons/r_exp3.wav");
55 }
56
57 /*
58 =================
59 CL_ParseBeam
60 =================
61 */
62 void CL_ParseBeam (model_t *m)
63 {
64         int             ent;
65         vec3_t  start, end;
66         beam_t  *b;
67         int             i;
68
69         ent = MSG_ReadShort ();
70         MSG_ReadVector(start);
71         MSG_ReadVector(end);
72
73 // override any beam with the same entity
74         for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++)
75                 if (b->entity == ent)
76                 {
77                         b->entity = ent;
78                         b->model = m;
79                         b->endtime = cl.time + 0.2;
80                         VectorCopy (start, b->start);
81                         VectorCopy (end, b->end);
82                         return;
83                 }
84
85 // find a free beam
86         for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++)
87         {
88                 if (!b->model || b->endtime < cl.time)
89                 {
90                         b->entity = ent;
91                         b->model = m;
92                         b->endtime = cl.time + 0.2;
93                         VectorCopy (start, b->start);
94                         VectorCopy (end, b->end);
95                         return;
96                 }
97         }
98         Con_Printf ("beam list overflow!\n");   
99 }
100
101
102 /*
103 =================
104 CL_ParseTEnt
105 =================
106 */
107 void CL_ParseTEnt (void)
108 {
109         int             type;
110         vec3_t  pos;
111         vec3_t  dir;
112         vec3_t  pos2;
113         vec3_t  color;
114         int             rnd;
115         int             colorStart, colorLength, count;
116         float   velspeed, radius;
117         byte *tempcolor;
118
119         type = MSG_ReadByte ();
120         switch (type)
121         {
122         case TE_WIZSPIKE:                       // spike hitting wall
123                 MSG_ReadVector(pos);
124                 CL_RunParticleEffect (pos, vec3_origin, 20, 30);
125                 S_StartSound (-1, 0, cl_sfx_wizhit, pos, 1, 1);
126                 break;
127                 
128         case TE_KNIGHTSPIKE:                    // spike hitting wall
129                 MSG_ReadVector(pos);
130                 CL_RunParticleEffect (pos, vec3_origin, 226, 20);
131                 S_StartSound (-1, 0, cl_sfx_knighthit, pos, 1, 1);
132                 break;
133                 
134         case TE_SPIKE:                  // spike hitting wall
135                 MSG_ReadVector(pos);
136                 // LordHavoc: changed to spark shower
137                 CL_SparkShower(pos, vec3_origin, 15);
138                 //CL_RunParticleEffect (pos, vec3_origin, 0, 10);
139                 if ( rand() % 5 )
140                         S_StartSound (-1, 0, cl_sfx_tink1, pos, 1, 1);
141                 else
142                 {
143                         rnd = rand() & 3;
144                         if (rnd == 1)
145                                 S_StartSound (-1, 0, cl_sfx_ric1, pos, 1, 1);
146                         else if (rnd == 2)
147                                 S_StartSound (-1, 0, cl_sfx_ric2, pos, 1, 1);
148                         else
149                                 S_StartSound (-1, 0, cl_sfx_ric3, pos, 1, 1);
150                 }
151                 break;
152         case TE_SPIKEQUAD:                      // quad spike hitting wall
153                 MSG_ReadVector(pos);
154                 // LordHavoc: changed to spark shower
155                 CL_SparkShower(pos, vec3_origin, 15);
156                 //CL_RunParticleEffect (pos, vec3_origin, 0, 10);
157                 CL_AllocDlight (NULL, pos, 200, 0.1f, 0.1f, 1.0f, 1000, 0.2);
158                 S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
159                 if ( rand() % 5 )
160                         S_StartSound (-1, 0, cl_sfx_tink1, pos, 1, 1);
161                 else
162                 {
163                         rnd = rand() & 3;
164                         if (rnd == 1)
165                                 S_StartSound (-1, 0, cl_sfx_ric1, pos, 1, 1);
166                         else if (rnd == 2)
167                                 S_StartSound (-1, 0, cl_sfx_ric2, pos, 1, 1);
168                         else
169                                 S_StartSound (-1, 0, cl_sfx_ric3, pos, 1, 1);
170                 }
171                 break;
172         case TE_SUPERSPIKE:                     // super spike hitting wall
173                 MSG_ReadVector(pos);
174                 // LordHavoc: changed to dust shower
175                 CL_SparkShower(pos, vec3_origin, 30);
176                 //CL_RunParticleEffect (pos, vec3_origin, 0, 20);
177                 if ( rand() % 5 )
178                         S_StartSound (-1, 0, cl_sfx_tink1, pos, 1, 1);
179                 else
180                 {
181                         rnd = rand() & 3;
182                         if (rnd == 1)
183                                 S_StartSound (-1, 0, cl_sfx_ric1, pos, 1, 1);
184                         else if (rnd == 2)
185                                 S_StartSound (-1, 0, cl_sfx_ric2, pos, 1, 1);
186                         else
187                                 S_StartSound (-1, 0, cl_sfx_ric3, pos, 1, 1);
188                 }
189                 break;
190         case TE_SUPERSPIKEQUAD:                 // quad super spike hitting wall
191                 MSG_ReadVector(pos);
192                 // LordHavoc: changed to dust shower
193                 CL_SparkShower(pos, vec3_origin, 30);
194                 //CL_RunParticleEffect (pos, vec3_origin, 0, 20);
195                 CL_AllocDlight (NULL, pos, 200, 0.1f, 0.1f, 1.0f, 1000, 0.2);
196                 if ( rand() % 5 )
197                         S_StartSound (-1, 0, cl_sfx_tink1, pos, 1, 1);
198                 else
199                 {
200                         rnd = rand() & 3;
201                         if (rnd == 1)
202                                 S_StartSound (-1, 0, cl_sfx_ric1, pos, 1, 1);
203                         else if (rnd == 2)
204                                 S_StartSound (-1, 0, cl_sfx_ric2, pos, 1, 1);
205                         else
206                                 S_StartSound (-1, 0, cl_sfx_ric3, pos, 1, 1);
207                 }
208                 break;
209                 // LordHavoc: added for improved blood splatters
210         case TE_BLOOD:  // blood puff
211                 MSG_ReadVector(pos);
212                 dir[0] = MSG_ReadChar ();
213                 dir[1] = MSG_ReadChar ();
214                 dir[2] = MSG_ReadChar ();
215                 count = MSG_ReadByte (); // amount of particles
216                 CL_BloodPuff(pos, dir, count);
217                 break;
218         case TE_BLOOD2: // blood puff
219                 MSG_ReadVector(pos);
220                 CL_BloodPuff(pos, vec3_origin, 10);
221                 break;
222         case TE_SPARK:  // spark shower
223                 MSG_ReadVector(pos);
224                 dir[0] = MSG_ReadChar ();
225                 dir[1] = MSG_ReadChar ();
226                 dir[2] = MSG_ReadChar ();
227                 count = MSG_ReadByte (); // amount of particles
228                 CL_SparkShower(pos, dir, count);
229                 break;
230                 // LordHavoc: added for improved gore
231         case TE_BLOODSHOWER:    // vaporized body
232                 MSG_ReadVector(pos); // mins
233                 MSG_ReadVector(pos2); // maxs
234                 velspeed = MSG_ReadCoord (); // speed
235                 count = MSG_ReadShort (); // number of particles
236                 CL_BloodShower(pos, pos2, velspeed, count);
237                 break;
238         case TE_PARTICLECUBE:   // general purpose particle effect
239                 MSG_ReadVector(pos); // mins
240                 MSG_ReadVector(pos2); // maxs
241                 MSG_ReadVector(dir); // dir
242                 count = MSG_ReadShort (); // number of particles
243                 colorStart = MSG_ReadByte (); // color
244                 colorLength = MSG_ReadByte (); // gravity (1 or 0)
245                 velspeed = MSG_ReadCoord (); // randomvel
246                 CL_ParticleCube(pos, pos2, dir, count, colorStart, colorLength, velspeed);
247                 break;
248
249         case TE_PARTICLERAIN:   // general purpose particle effect
250                 MSG_ReadVector(pos); // mins
251                 MSG_ReadVector(pos2); // maxs
252                 MSG_ReadVector(dir); // dir
253                 count = MSG_ReadShort (); // number of particles
254                 colorStart = MSG_ReadByte (); // color
255                 CL_ParticleRain(pos, pos2, dir, count, colorStart, 0);
256                 break;
257
258         case TE_PARTICLESNOW:   // general purpose particle effect
259                 MSG_ReadVector(pos); // mins
260                 MSG_ReadVector(pos2); // maxs
261                 MSG_ReadVector(dir); // dir
262                 count = MSG_ReadShort (); // number of particles
263                 colorStart = MSG_ReadByte (); // color
264                 CL_ParticleRain(pos, pos2, dir, count, colorStart, 1);
265                 break;
266
267         case TE_GUNSHOT:                        // bullet hitting wall
268                 MSG_ReadVector(pos);
269                 // LordHavoc: changed to dust shower
270                 CL_SparkShower(pos, vec3_origin, 15);
271                 //CL_RunParticleEffect (pos, vec3_origin, 0, 20);
272                 break;
273
274         case TE_GUNSHOTQUAD:                    // quad bullet hitting wall
275                 MSG_ReadVector(pos);
276                 CL_SparkShower(pos, vec3_origin, 15);
277                 CL_AllocDlight (NULL, pos, 200, 0.1f, 0.1f, 1.0f, 1000, 0.2);
278                 break;
279
280         case TE_EXPLOSION:                      // rocket explosion
281                 MSG_ReadVector(pos);
282                 Mod_FindNonSolidLocation(pos, cl.worldmodel);
283                 CL_ParticleExplosion (pos, false);
284 //              CL_BlastParticles (pos, 120, 120);
285                 // LordHavoc: boosted color from 1.0, 0.8, 0.4 to 1.25, 1.0, 0.5
286                 CL_AllocDlight (NULL, pos, 350, 1.25f, 1.0f, 0.5f, 700, 0.5);
287                 S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
288                 break;
289
290         case TE_EXPLOSIONQUAD:                  // quad rocket explosion
291                 MSG_ReadVector(pos);
292                 Mod_FindNonSolidLocation(pos, cl.worldmodel);
293                 CL_ParticleExplosion (pos, false);
294 //              CL_BlastParticles (pos, 120, 480);
295                 CL_AllocDlight (NULL, pos, 600, 0.5f, 0.4f, 1.0f, 1200, 0.5);
296                 S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
297                 break;
298
299                 /*
300         case TE_SMOKEEXPLOSION:                 // rocket explosion with a cloud of smoke
301                 MSG_ReadVector(pos);
302                 Mod_FindNonSolidLocation(pos, cl.worldmodel);
303                 CL_ParticleExplosion (pos, true);
304                 CL_AllocDlight (NULL, pos, 350, 1.0f, 0.8f, 0.4f, 700, 0.5);
305                 S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
306                 break;
307                 */
308
309         case TE_EXPLOSION3:                             // Nehahra movie colored lighting explosion
310                 MSG_ReadVector(pos);
311                 Mod_FindNonSolidLocation(pos, cl.worldmodel);
312                 CL_ParticleExplosion (pos, false);
313 //              CL_BlastParticles (pos, 120, 120);
314                 CL_AllocDlight (NULL, pos, 350, MSG_ReadCoord(), MSG_ReadCoord(), MSG_ReadCoord(), 700, 0.5);
315                 S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
316                 break;
317
318         case TE_EXPLOSIONRGB:                   // colored lighting explosion
319                 MSG_ReadVector(pos);
320                 Mod_FindNonSolidLocation(pos, cl.worldmodel);
321                 CL_ParticleExplosion (pos, false);
322 //              CL_BlastParticles (pos, 120, 120);
323                 color[0] = MSG_ReadByte() * (1.0 / 255.0);
324                 color[1] = MSG_ReadByte() * (1.0 / 255.0);
325                 color[2] = MSG_ReadByte() * (1.0 / 255.0);
326                 CL_AllocDlight (NULL, pos, 350, color[0], color[1], color[2], 700, 0.5);
327                 S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
328                 break;
329
330         case TE_TAREXPLOSION:                   // tarbaby explosion
331                 MSG_ReadVector(pos);
332                 Mod_FindNonSolidLocation(pos, cl.worldmodel);
333                 CL_BlobExplosion (pos);
334 //              CL_BlastParticles (pos, 120, 120);
335
336                 S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
337                 CL_AllocDlight (NULL, pos, 600, 0.8f, 0.4f, 1.0f, 1200, 0.5);
338                 S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
339                 break;
340
341         case TE_SMALLFLASH:
342                 MSG_ReadVector(pos);
343                 Mod_FindNonSolidLocation(pos, cl.worldmodel);
344                 CL_AllocDlight (NULL, pos, 200, 1, 1, 1, 1000, 0.2);
345                 break;
346
347         case TE_CUSTOMFLASH:
348                 MSG_ReadVector(pos);
349                 Mod_FindNonSolidLocation(pos, cl.worldmodel);
350                 radius = MSG_ReadByte() * 8;
351                 velspeed = (MSG_ReadByte() + 1) * (1.0 / 256.0);
352                 color[0] = MSG_ReadByte() * (1.0 / 255.0);
353                 color[1] = MSG_ReadByte() * (1.0 / 255.0);
354                 color[2] = MSG_ReadByte() * (1.0 / 255.0);
355                 CL_AllocDlight (NULL, pos, radius, color[0], color[1], color[2], radius / velspeed, velspeed);
356                 break;
357
358         case TE_FLAMEJET:
359                 MSG_ReadVector(pos);
360                 MSG_ReadVector(dir);
361                 count = MSG_ReadByte();
362                 CL_Flames(pos, dir, count);
363                 break;
364
365         case TE_LIGHTNING1:                             // lightning bolts
366                 if (!cl_model_bolt)
367                         cl_model_bolt = Mod_ForName("progs/bolt.mdl", true, false, false);
368                 CL_ParseBeam (cl_model_bolt);
369                 break;
370
371         case TE_LIGHTNING2:                             // lightning bolts
372                 if (!cl_model_bolt2)
373                         cl_model_bolt2 = Mod_ForName("progs/bolt2.mdl", true, false, false);
374                 CL_ParseBeam (cl_model_bolt2);
375                 break;
376
377         case TE_LIGHTNING3:                             // lightning bolts
378                 if (!cl_model_bolt3)
379                         cl_model_bolt3 = Mod_ForName("progs/bolt3.mdl", true, false, false);
380                 CL_ParseBeam (cl_model_bolt3);
381                 break;
382
383 // PGM 01/21/97
384         case TE_BEAM:                           // grappling hook beam
385                 if (!cl_model_beam)
386                         cl_model_beam = Mod_ForName("progs/beam.mdl", true, false, false);
387                 CL_ParseBeam (cl_model_beam);
388                 break;
389 // PGM 01/21/97
390
391 // LordHavoc: for compatibility with the Nehahra movie...
392         case TE_LIGHTNING4NEH:
393                 CL_ParseBeam (Mod_ForName(MSG_ReadString(), true, false, false));
394                 break;
395
396         case TE_LAVASPLASH:     
397                 pos[0] = MSG_ReadCoord ();
398                 pos[1] = MSG_ReadCoord ();
399                 pos[2] = MSG_ReadCoord ();
400                 CL_LavaSplash (pos);
401                 break;
402         
403         case TE_TELEPORT:
404                 pos[0] = MSG_ReadCoord ();
405                 pos[1] = MSG_ReadCoord ();
406                 pos[2] = MSG_ReadCoord ();
407                 CL_TeleportSplash (pos);
408                 break;
409                 
410         case TE_EXPLOSION2:                             // color mapped explosion
411                 MSG_ReadVector(pos);
412                 Mod_FindNonSolidLocation(pos, cl.worldmodel);
413                 colorStart = MSG_ReadByte ();
414                 colorLength = MSG_ReadByte ();
415                 CL_ParticleExplosion2 (pos, colorStart, colorLength);
416 //              CL_BlastParticles (pos, 80, 80);
417                 tempcolor = (byte *)&d_8to24table[(rand()%colorLength) + colorStart];
418                 CL_AllocDlight (NULL, pos, 350, tempcolor[0] * (1.0f / 255.0f), tempcolor[1] * (1.0f / 255.0f), tempcolor[2] * (1.0f / 255.0f), 700, 0.5);
419                 S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
420                 break;
421                 
422         default:
423                 Host_Error ("CL_ParseTEnt: bad type %d", type);
424         }
425 }
426
427
428 /*
429 =================
430 CL_NewTempEntity
431 =================
432 */
433 entity_t *CL_NewTempEntity (void)
434 {
435         entity_t        *ent;
436
437         if (r_refdef.numentities >= MAX_VISEDICTS)
438                 return NULL;
439         if (num_temp_entities >= MAX_TEMP_ENTITIES)
440                 return NULL;
441         ent = &cl_temp_entities[num_temp_entities++];
442         memset (ent, 0, sizeof(*ent));
443         r_refdef.entities[r_refdef.numentities++] = &ent->render;
444
445         ent->render.colormap = -1; // no special coloring
446         ent->render.scale = 1;
447         ent->render.alpha = 1;
448         return ent;
449 }
450
451
452 /*
453 =================
454 CL_UpdateTEnts
455 =================
456 */
457 void CL_UpdateTEnts (void)
458 {
459         int                     i;
460         beam_t          *b;
461         vec3_t          dist, org;
462         float           d;
463         entity_t        *ent;
464         float           yaw, pitch;
465         float           forward;
466
467         num_temp_entities = 0;
468
469 // update lightning
470         for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++)
471         {
472                 if (!b->model || b->endtime < cl.time)
473                         continue;
474
475                 // if coming from the player, update the start position
476                 if (b->entity == cl.viewentity)
477                         VectorCopy (cl_entities[cl.viewentity].render.origin, b->start);
478
479                 // calculate pitch and yaw
480                 VectorSubtract (b->end, b->start, dist);
481
482                 if (dist[1] == 0 && dist[0] == 0)
483                 {
484                         yaw = 0;
485                         if (dist[2] > 0)
486                                 pitch = 90;
487                         else
488                                 pitch = 270;
489                 }
490                 else
491                 {
492                         yaw = (int) (atan2(dist[1], dist[0]) * 180 / M_PI);
493                         if (yaw < 0)
494                                 yaw += 360;
495         
496                         forward = sqrt (dist[0]*dist[0] + dist[1]*dist[1]);
497                         pitch = (int) (atan2(dist[2], forward) * 180 / M_PI);
498                         if (pitch < 0)
499                                 pitch += 360;
500                 }
501
502                 // add new entities for the lightning
503                 VectorCopy (b->start, org);
504                 d = VectorNormalizeLength(dist);
505                 while (d > 0)
506                 {
507                         ent = CL_NewTempEntity ();
508                         if (!ent)
509                                 return;
510                         VectorCopy (org, ent->render.origin);
511                         ent->render.model = b->model;
512                         ent->render.effects = EF_FULLBRIGHT;
513                         ent->render.angles[0] = pitch;
514                         ent->render.angles[1] = yaw;
515                         ent->render.angles[2] = rand()%360;
516                         VectorMA(org, 30, dist, org);
517                         d -= 30;
518                 }
519         }
520
521 }
522
523