1 /*
2 * cl_tent.c -- Client side temporary entity effects.
3 * $Id: cl_tent.c 4767 2012-06-16 20:48:51Z sezero $
4 *
5 * Copyright (C) 1996-1997 Id Software, Inc.
6 * Copyright (C) 1997-1998 Raven Software Corp.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or (at
11 * your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16 *
17 * See the GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 */
23
24
25 // HEADER FILES ------------------------------------------------------------
26
27 #include "quakedef.h"
28
29 // MACROS ------------------------------------------------------------------
30
31 #define MAX_STREAMS 32
32 #define MAX_STREAM_ENTITIES 128
33 #define STREAM_ATTACHED 16
34 #define STREAM_TRANSLUCENT 32
35
36 #define TE_SPIKE 0
37 #define TE_SUPERSPIKE 1
38 #define TE_GUNSHOT 2
39 #define TE_EXPLOSION 3
40 //#define TE_TAREXPLOSION 4
41 #define TE_LIGHTNING1 5
42 #define TE_LIGHTNING2 6
43 #define TE_WIZSPIKE 7
44 #define TE_KNIGHTSPIKE 8
45 #define TE_LIGHTNING3 9
46 #define TE_LAVASPLASH 10
47 #define TE_TELEPORT 11
48 //#define TE_EXPLOSION2 12
49 #define TE_STREAM_CHAIN 25
50 #define TE_STREAM_SUNSTAFF1 26
51 #define TE_STREAM_SUNSTAFF2 27
52 #define TE_STREAM_LIGHTNING 28
53 #define TE_STREAM_COLORBEAM 29
54 #define TE_STREAM_ICECHUNKS 30
55 #define TE_STREAM_GAZE 31
56 #define TE_STREAM_FAMINE 32
57 #define TE_STREAM_LIGHTNING_SMALL 24
58
59 // TYPES -------------------------------------------------------------------
60
61 typedef struct
62 {
63 int type;
64 int entity;
65 int tag;
66 int flags;
67 int skin;
68 struct qmodel_s *models[4];
69 vec3_t source;
70 vec3_t dest;
71 vec3_t offset;
72 float endTime;
73 float lastTrailTime;
74 } stream_t;
75
76 // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
77
78 // PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
79
80 // PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
81
82 static void ParseStream(int type);
83 static stream_t *NewStream(int ent, int tag);
84 static entity_t *NewStreamEntity(void);
85
86 // EXTERNAL DATA DECLARATIONS ----------------------------------------------
87
88 // PUBLIC DATA DEFINITIONS -------------------------------------------------
89
90 // PRIVATE DATA DEFINITIONS ------------------------------------------------
91
92 static stream_t cl_Streams[MAX_STREAMS];
93 static entity_t StreamEntities[MAX_STREAM_ENTITIES];
94 static int StreamEntityCount;
95 //static sfx_t *cl_sfx_wizhit;
96 //static sfx_t *cl_sfx_knighthit;
97 static sfx_t *cl_sfx_tink1;
98 static sfx_t *cl_sfx_ric1;
99 static sfx_t *cl_sfx_ric2;
100 static sfx_t *cl_sfx_ric3;
101 static sfx_t *cl_sfx_r_exp3;
102 #ifdef QUAKE2
103 static sfx_t *cl_sfx_imp;
104 static sfx_t *cl_sfx_rail;
105 #endif
106
107 // CODE --------------------------------------------------------------------
108
109 //==========================================================================
110 //
111 // CL_InitTEnts
112 //
113 //==========================================================================
114
CL_InitTEnts(void)115 void CL_InitTEnts(void)
116 {
117 cl_sfx_tink1 = S_PrecacheSound ("weapons/tink1.wav");
118 cl_sfx_ric1 = S_PrecacheSound ("weapons/ric1.wav");
119 cl_sfx_ric2 = S_PrecacheSound ("weapons/ric2.wav");
120 cl_sfx_ric3 = S_PrecacheSound ("weapons/ric3.wav");
121 cl_sfx_r_exp3 = S_PrecacheSound ("weapons/r_exp3.wav");
122 #ifdef QUAKE2
123 cl_sfx_imp = S_PrecacheSound ("shambler/sattck1.wav");
124 cl_sfx_rail = S_PrecacheSound ("weapons/lstart.wav");
125 #endif
126 }
127
128 //==========================================================================
129 //
130 // CL_ClearTEnts
131 //
132 //==========================================================================
133
CL_ClearTEnts(void)134 void CL_ClearTEnts(void)
135 {
136 memset(cl_Streams, 0, sizeof(cl_Streams));
137 }
138
139 //==========================================================================
140 //
141 // CL_ParseTEnt
142 //
143 //==========================================================================
144
CL_ParseTEnt(void)145 void CL_ParseTEnt(void)
146 {
147 int type;
148 vec3_t pos;
149 #ifdef QUAKE2
150 vec3_t endpos;
151 #endif
152 dlight_t *dl;
153 int rnd;
154 // int colorStart, colorLength;
155
156 type = MSG_ReadByte();
157 switch (type)
158 {
159 case TE_WIZSPIKE: // spike hitting wall
160 pos[0] = MSG_ReadCoord ();
161 pos[1] = MSG_ReadCoord ();
162 pos[2] = MSG_ReadCoord ();
163 R_RunParticleEffect (pos, vec3_origin, 20, 30);
164 // S_StartSound (-1, 0, cl_sfx_wizhit, pos, 1, 1);
165 break;
166
167 case TE_KNIGHTSPIKE: // spike hitting wall
168 pos[0] = MSG_ReadCoord ();
169 pos[1] = MSG_ReadCoord ();
170 pos[2] = MSG_ReadCoord ();
171 R_RunParticleEffect (pos, vec3_origin, 226, 20);
172 // S_StartSound (-1, 0, cl_sfx_knighthit, pos, 1, 1);
173 break;
174
175 case TE_SPIKE: // spike hitting wall
176 pos[0] = MSG_ReadCoord ();
177 pos[1] = MSG_ReadCoord ();
178 pos[2] = MSG_ReadCoord ();
179 R_RunParticleEffect (pos, vec3_origin, 0, 10);
180 if (rand() % 5)
181 S_StartSound (-1, 0, cl_sfx_tink1, pos, 1, 1);
182 else
183 {
184 rnd = rand() & 3;
185 if (rnd == 1)
186 S_StartSound (-1, 0, cl_sfx_ric1, pos, 1, 1);
187 else if (rnd == 2)
188 S_StartSound (-1, 0, cl_sfx_ric2, pos, 1, 1);
189 else
190 S_StartSound (-1, 0, cl_sfx_ric3, pos, 1, 1);
191 }
192 break;
193
194 case TE_SUPERSPIKE: // super spike hitting wall
195 pos[0] = MSG_ReadCoord ();
196 pos[1] = MSG_ReadCoord ();
197 pos[2] = MSG_ReadCoord ();
198 R_RunParticleEffect (pos, vec3_origin, 0, 20);
199
200 if (rand() % 5)
201 S_StartSound (-1, 0, cl_sfx_tink1, pos, 1, 1);
202 else
203 {
204 rnd = rand() & 3;
205 if (rnd == 1)
206 S_StartSound (-1, 0, cl_sfx_ric1, pos, 1, 1);
207 else if (rnd == 2)
208 S_StartSound (-1, 0, cl_sfx_ric2, pos, 1, 1);
209 else
210 S_StartSound (-1, 0, cl_sfx_ric3, pos, 1, 1);
211 }
212 break;
213
214 case TE_GUNSHOT: // bullet hitting wall
215 pos[0] = MSG_ReadCoord ();
216 pos[1] = MSG_ReadCoord ();
217 pos[2] = MSG_ReadCoord ();
218 R_RunParticleEffect (pos, vec3_origin, 0, 20);
219 break;
220
221 case TE_EXPLOSION: // rocket explosion
222 pos[0] = MSG_ReadCoord ();
223 pos[1] = MSG_ReadCoord ();
224 pos[2] = MSG_ReadCoord ();
225 R_ParticleExplosion (pos);
226 // break; // why was this here???
227 dl = CL_AllocDlight (0);
228 VectorCopy (pos, dl->origin);
229 dl->radius = 350;
230 dl->die = cl.time + 0.5;
231 dl->decay = 300;
232 # ifdef GLQUAKE
233 if (gl_colored_dynamic_lights.integer)
234 { // Make the dynamic light red
235 dl->color[0] = 0.8;
236 dl->color[1] = 0.2;
237 dl->color[2] = 0.2;
238 dl->color[3] = 0.7;
239 }
240 # endif
241 S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
242 break;
243
244 case TE_LIGHTNING1:
245 case TE_LIGHTNING2:
246 case TE_LIGHTNING3:
247 MSG_ReadShort();
248 MSG_ReadCoord();
249 MSG_ReadCoord();
250 MSG_ReadCoord();
251 MSG_ReadCoord();
252 MSG_ReadCoord();
253 MSG_ReadCoord();
254 break;
255
256 case TE_STREAM_CHAIN:
257 case TE_STREAM_SUNSTAFF1:
258 case TE_STREAM_SUNSTAFF2:
259 case TE_STREAM_LIGHTNING:
260 case TE_STREAM_LIGHTNING_SMALL:
261 case TE_STREAM_COLORBEAM:
262 case TE_STREAM_ICECHUNKS:
263 case TE_STREAM_GAZE:
264 case TE_STREAM_FAMINE:
265 ParseStream(type);
266 break;
267
268 case TE_LAVASPLASH:
269 pos[0] = MSG_ReadCoord ();
270 pos[1] = MSG_ReadCoord ();
271 pos[2] = MSG_ReadCoord ();
272 R_LavaSplash (pos);
273 break;
274
275 case TE_TELEPORT:
276 pos[0] = MSG_ReadCoord ();
277 pos[1] = MSG_ReadCoord ();
278 pos[2] = MSG_ReadCoord ();
279 R_TeleportSplash (pos);
280 break;
281 default:
282 Sys_Error ("%s: bad type", __thisfunc__);
283 }
284 }
285
286 //==========================================================================
287 //
288 // ParseStream
289 //
290 //==========================================================================
291
ParseStream(int type)292 static void ParseStream(int type)
293 {
294 int ent, tag, flags, skin;
295 vec3_t source, dest;
296 float duration;
297 stream_t *stream;
298 qmodel_t *models[4];
299
300 ent = MSG_ReadShort();
301 flags = MSG_ReadByte();
302 tag = flags&15;
303 duration = (float)MSG_ReadByte()*0.05;
304 skin = 0;
305 if (type == TE_STREAM_COLORBEAM)
306 skin = MSG_ReadByte();
307 source[0] = MSG_ReadCoord();
308 source[1] = MSG_ReadCoord();
309 source[2] = MSG_ReadCoord();
310 dest[0] = MSG_ReadCoord();
311 dest[1] = MSG_ReadCoord();
312 dest[2] = MSG_ReadCoord();
313
314 models[1] = models[2] = models[3] = NULL;
315 switch (type)
316 {
317 case TE_STREAM_CHAIN:
318 models[0] = Mod_ForName("models/stchain.mdl", true);
319 break;
320 case TE_STREAM_SUNSTAFF1:
321 models[0] = Mod_ForName("models/stsunsf1.mdl", true);
322 models[1] = Mod_ForName("models/stsunsf2.mdl", true);
323 models[2] = Mod_ForName("models/stsunsf3.mdl", true);
324 models[3] = Mod_ForName("models/stsunsf4.mdl", true);
325 break;
326 case TE_STREAM_SUNSTAFF2:
327 models[0] = Mod_ForName("models/stsunsf5.mdl", true);
328 models[2] = Mod_ForName("models/stsunsf3.mdl", true);
329 models[3] = Mod_ForName("models/stsunsf4.mdl", true);
330 break;
331 case TE_STREAM_LIGHTNING:
332 models[0] = Mod_ForName("models/stlghtng.mdl", true);
333 // duration *= 2;
334 break;
335 case TE_STREAM_LIGHTNING_SMALL:
336 models[0] = Mod_ForName("models/stltng2.mdl", true);
337 // duration *= 2;
338 break;
339 case TE_STREAM_FAMINE:
340 models[0] = Mod_ForName("models/fambeam.mdl", true);
341 break;
342 case TE_STREAM_COLORBEAM:
343 models[0] = Mod_ForName("models/stclrbm.mdl", true);
344 break;
345 case TE_STREAM_ICECHUNKS:
346 models[0] = Mod_ForName("models/stice.mdl", true);
347 break;
348 case TE_STREAM_GAZE:
349 models[0] = Mod_ForName("models/stmedgaz.mdl", true);
350 break;
351 default:
352 models[0] = NULL;
353 break;
354 }
355 if (models[0] == NULL)
356 Sys_Error("%s: bad type", __thisfunc__);
357
358 if ((stream = NewStream(ent, tag)) == NULL)
359 {
360 Con_Printf("stream list overflow\n");
361 return;
362 }
363 stream->type = type;
364 stream->tag = tag;
365 stream->flags = flags;
366 stream->entity = ent;
367 stream->skin = skin;
368 stream->models[0] = models[0];
369 stream->models[1] = models[1];
370 stream->models[2] = models[2];
371 stream->models[3] = models[3];
372 stream->endTime = cl.time+duration;
373 stream->lastTrailTime = 0;
374 VectorCopy(source, stream->source);
375 VectorCopy(dest, stream->dest);
376 if (flags & STREAM_ATTACHED)
377 {
378 VectorSubtract(source, cl_entities[ent].origin, stream->offset);
379 }
380 }
381
382 //==========================================================================
383 //
384 // NewStream
385 //
386 //==========================================================================
387
NewStream(int ent,int tag)388 static stream_t *NewStream(int ent, int tag)
389 {
390 stream_t *stream;
391 int i;
392
393 // Search for a stream with matching entity and tag
394 for (i = 0, stream = cl_Streams; i < MAX_STREAMS; i++, stream++)
395 {
396 if (stream->entity == ent && stream->tag == tag)
397 return stream;
398 }
399 // Search for a free stream
400 for (i = 0, stream = cl_Streams; i < MAX_STREAMS; i++, stream++)
401 {
402 if (!stream->models[0] || stream->endTime < cl.time)
403 return stream;
404 }
405 return NULL;
406 }
407
408 //==========================================================================
409 //
410 // CL_UpdateTEnts
411 //
412 //==========================================================================
413
CL_UpdateTEnts(void)414 void CL_UpdateTEnts(void)
415 {
416 int i, j, offset;
417 stream_t *stream;
418 vec3_t dist, org;
419 float d;
420 entity_t *ent;
421 float yaw, pitch, forward;
422
423 // Update streams
424 StreamEntityCount = 0;
425 for (i = 0, stream = cl_Streams; i < MAX_STREAMS; i++, stream++)
426 {
427 if (!stream->models[0])// || stream->endTime < cl.time)
428 { // Inactive
429 continue;
430 }
431 if (stream->endTime < cl.time)
432 { // Inactive
433 if (stream->type != TE_STREAM_LIGHTNING && stream->type != TE_STREAM_LIGHTNING_SMALL)
434 continue;
435 else if (stream->endTime + 0.25 < cl.time)
436 continue;
437 }
438
439 if (stream->flags & STREAM_ATTACHED && stream->endTime >= cl.time)
440 { // Attach the start position to owner
441 VectorAdd(cl_entities[stream->entity].origin, stream->offset, stream->source);
442 }
443
444 VectorSubtract(stream->dest, stream->source, dist);
445 if (dist[1] == 0 && dist[0] == 0)
446 {
447 yaw = 0;
448 if (dist[2] > 0)
449 pitch = 90;
450 else pitch = 270;
451 }
452 else
453 {
454 yaw = (int)(atan2(dist[1], dist[0])*180/M_PI);
455 if (yaw < 0)
456 yaw += 360;
457 forward = sqrt(dist[0]*dist[0]+dist[1]*dist[1]);
458 pitch = (int)(atan2(dist[2], forward)*180/M_PI);
459 if (pitch < 0)
460 pitch += 360;
461 }
462
463 VectorCopy(stream->source, org);
464 d = VectorNormalize(dist);
465
466 if (stream->type == TE_STREAM_ICECHUNKS)
467 {
468 offset = (int)(cl.time*40)%30;
469 for (j = 0; j < 3; j++)
470 org[j] += dist[j]*offset;
471 }
472
473 while (d > 0)
474 {
475 ent = NewStreamEntity();
476 if (!ent)
477 return;
478
479 VectorCopy(org, ent->origin);
480 ent->model = stream->models[0];
481 ent->angles[0] = pitch;
482 ent->angles[1] = yaw;
483
484 switch (stream->type)
485 {
486 case TE_STREAM_CHAIN:
487 ent->angles[2] = 0;
488 ent->drawflags = MLS_ABSLIGHT;
489 ent->abslight = 128;
490 break;
491 case TE_STREAM_SUNSTAFF1:
492 ent->angles[2] = (int)(cl.time*10)%360;
493 ent->drawflags = MLS_ABSLIGHT;
494 ent->abslight = 128;
495 //ent->frame = (int)(cl.time*20)%20;
496
497 ent = NewStreamEntity();
498 if (!ent)
499 return;
500 VectorCopy(org, ent->origin);
501 ent->model = stream->models[1];
502 ent->angles[0] = pitch;
503 ent->angles[1] = yaw;
504 ent->angles[2] = (int)(cl.time*50)%360;
505 ent->drawflags = MLS_ABSLIGHT|DRF_TRANSLUCENT;
506 ent->abslight = 128;
507 break;
508 case TE_STREAM_SUNSTAFF2:
509 ent->angles[2] = (int)(cl.time*10)%360;
510 ent->drawflags = MLS_ABSLIGHT;
511 ent->abslight = 128;
512 ent->frame = (int)(cl.time*10)%8;
513 break;
514 case TE_STREAM_LIGHTNING:
515 if (stream->endTime < cl.time)
516 {//fixme: keep last non-translucent frame and angle
517 ent->drawflags = MLS_ABSLIGHT|DRF_TRANSLUCENT;
518 ent->abslight = 128 + (stream->endTime - cl.time)*192;
519 }
520 else
521 {
522 ent->angles[2] = rand() % 360;
523 ent->drawflags = MLS_ABSLIGHT;
524 ent->abslight = 128;
525 ent->frame = rand() % 6;
526 }
527 break;
528 case TE_STREAM_LIGHTNING_SMALL:
529 if (stream->endTime < cl.time)
530 {
531 ent->drawflags = MLS_ABSLIGHT|DRF_TRANSLUCENT;
532 ent->abslight = 128 + (stream->endTime - cl.time)*192;
533 }
534 else
535 {
536 ent->angles[2] = rand() % 360;
537 ent->frame = rand() % 6;
538 ent->drawflags = MLS_ABSLIGHT;
539 ent->abslight = 128;
540 }
541 break;
542 case TE_STREAM_FAMINE:
543 ent->angles[2] = rand() % 360;
544 ent->drawflags = MLS_ABSLIGHT;
545 ent->abslight = 128;
546 ent->frame = 0;
547 break;
548 case TE_STREAM_COLORBEAM:
549 ent->angles[2] = 0;
550 ent->drawflags = MLS_ABSLIGHT;
551 ent->abslight = 128;
552 ent->skinnum = stream->skin;
553 break;
554 case TE_STREAM_GAZE:
555 ent->angles[2] = 0;
556 ent->drawflags = MLS_ABSLIGHT;
557 ent->abslight = 128;
558 ent->frame = (int)(cl.time*40)%36;
559 break;
560 case TE_STREAM_ICECHUNKS:
561 ent->angles[2] = rand() % 360;
562 ent->drawflags = MLS_ABSLIGHT;
563 ent->abslight = 128;
564 ent->frame = rand() % 5;
565 break;
566
567 default:
568 ent->angles[2] = 0;
569 }
570
571 for (j = 0; j < 3; j++)
572 org[j] += dist[j]*30;
573
574 d -= 30;
575 }
576
577 if (stream->type == TE_STREAM_SUNSTAFF1 || stream->type == TE_STREAM_SUNSTAFF2)
578 {
579 if (stream->lastTrailTime+0.2 < cl.time)
580 {
581 stream->lastTrailTime = cl.time;
582 R_SunStaffTrail(stream->source, stream->dest);
583 }
584
585 ent = NewStreamEntity();
586 if (ent == NULL)
587 return;
588
589 VectorCopy(stream->dest, ent->origin);
590 ent->model = stream->models[2];
591 ent->drawflags = MLS_ABSLIGHT;
592 ent->abslight = 128;
593 ent->scale = 80 + (rand() & 15);
594 //ent->frame = (int)(cl.time*20)%20;
595
596 ent = NewStreamEntity();
597 if (ent == NULL)
598 return;
599
600 VectorCopy(stream->dest, ent->origin);
601 ent->model = stream->models[3];
602 ent->drawflags = MLS_ABSLIGHT|DRF_TRANSLUCENT;
603 ent->abslight = 128;
604 ent->scale = 150 + (rand() & 15);
605 }
606 }
607 }
608
609 //==========================================================================
610 //
611 // NewStreamEntity
612 //
613 //==========================================================================
614
NewStreamEntity(void)615 static entity_t *NewStreamEntity(void)
616 {
617 entity_t *ent;
618
619 if (cl_numvisedicts == MAX_VISEDICTS)
620 return NULL;
621 if (StreamEntityCount == MAX_STREAM_ENTITIES)
622 return NULL;
623
624 ent = &StreamEntities[StreamEntityCount++];
625 memset(ent, 0, sizeof(*ent));
626 cl_visedicts[cl_numvisedicts++] = ent;
627 ent->colormap = vid.colormap;
628
629 return ent;
630 }
631
632