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