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