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