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 #include "neh.h"
24 
25 extern void	R_Smoke (vec3_t org, int color);
26 int		num_temp_entities;
27 entity_t	cl_temp_entities[MAX_TEMP_ENTITIES];
28 beam_t		cl_beams[MAX_BEAMS];
29 
30 sfx_t		*cl_sfx_wizhit;
31 sfx_t		*cl_sfx_knighthit;
32 sfx_t		*cl_sfx_tink1;
33 sfx_t		*cl_sfx_ric1;
34 sfx_t		*cl_sfx_ric2;
35 sfx_t		*cl_sfx_ric3;
36 sfx_t		*cl_sfx_r_exp3;
37 
38 /*
39 =================
40 CL_ParseTEnt
41 =================
42 */
CL_InitTEnts(void)43 void CL_InitTEnts (void)
44 {
45 	cl_sfx_wizhit = S_PrecacheSound ("wizard/hit.wav");
46 	cl_sfx_knighthit = S_PrecacheSound ("hknight/hit.wav");
47 	cl_sfx_tink1 = S_PrecacheSound ("weapons/tink1.wav");
48 	cl_sfx_ric1 = S_PrecacheSound ("weapons/ric1.wav");
49 	cl_sfx_ric2 = S_PrecacheSound ("weapons/ric2.wav");
50 	cl_sfx_ric3 = S_PrecacheSound ("weapons/ric3.wav");
51 	cl_sfx_r_exp3 = S_PrecacheSound ("weapons/r_exp3.wav");
52 #ifdef QUAKE2
53 	cl_sfx_imp = S_PrecacheSound ("shambler/sattck1.wav");
54 	cl_sfx_rail = S_PrecacheSound ("weapons/lstart.wav");
55 #endif
56 }
57 
58 /*
59 =================
60 CL_ParseBeam
61 =================
62 */
CL_ParseBeam(model_t * m)63 void CL_ParseBeam (model_t *m)
64 {
65 	int		ent;
66 	vec3_t	start, end;
67 	beam_t	*b;
68 	int		i;
69 
70 	ent = MSG_ReadShort ();
71 
72 	start[0] = MSG_ReadCoord ();
73 	start[1] = MSG_ReadCoord ();
74 	start[2] = MSG_ReadCoord ();
75 
76 	end[0] = MSG_ReadCoord ();
77 	end[1] = MSG_ReadCoord ();
78 	end[2] = MSG_ReadCoord ();
79 
80 // override any beam with the same entity
81 	for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++)
82 		if (b->entity == ent)
83 		{
84 			b->entity = ent;
85 			b->model = m;
86 			b->endtime = cl.time + 0.2;
87 			VectorCopy (start, b->start);
88 			VectorCopy (end, b->end);
89 			return;
90 		}
91 
92 // find a free beam
93 	for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++)
94 	{
95 		if (!b->model || b->endtime < cl.time)
96 		{
97 			b->entity = ent;
98 			b->model = m;
99 			b->endtime = cl.time + 0.2;
100 			VectorCopy (start, b->start);
101 			VectorCopy (end, b->end);
102 			return;
103 		}
104 	}
105 	Con_Printf ("beam list overflow (max = %d)\n", MAX_BEAMS);
106 }
107 
108 /*
109 =================
110 CL_ParseTEnt
111 =================
112 */
CL_ParseTEnt(void)113 void CL_ParseTEnt (void)
114 {
115 	int	    type;
116 	vec3_t	    pos;
117 	vec3_t	    endpos;
118 
119 	dlight_t    *dl;
120 	int	    rnd;
121 	int	    colorStart, colorLength;
122 
123 	type = MSG_ReadByte ();
124 	switch (type)
125 	{
126 	case TE_WIZSPIKE:			// spike hitting wall
127 		pos[0] = MSG_ReadCoord ();
128 		pos[1] = MSG_ReadCoord ();
129 		pos[2] = MSG_ReadCoord ();
130 		R_RunParticleEffect (pos, vec3_origin, 20, 30);
131 		S_StartSound (-1, 0, cl_sfx_wizhit, pos, 1, 1);
132 		break;
133 
134 	case TE_KNIGHTSPIKE:			// spike hitting wall
135 		pos[0] = MSG_ReadCoord ();
136 		pos[1] = MSG_ReadCoord ();
137 		pos[2] = MSG_ReadCoord ();
138 		R_RunParticleEffect (pos, vec3_origin, 226, 20);
139 		S_StartSound (-1, 0, cl_sfx_knighthit, pos, 1, 1);
140 		break;
141 
142 	case TE_SPIKE:			// spike hitting wall
143 		pos[0] = MSG_ReadCoord ();
144 		pos[1] = MSG_ReadCoord ();
145 		pos[2] = MSG_ReadCoord ();
146 
147 // LordHavoc's "Dust" effect
148                 R_SparkShower(pos, vec3_origin, 15, 0);
149 //                R_RunParticleEffect (pos, vec3_origin, 0, 10);
150 
151 		if ( rand() % 5 )
152 			S_StartSound (-1, 0, cl_sfx_tink1, pos, 1, 1);
153 		else
154 		{
155 			rnd = rand() & 3;
156 			if (rnd == 1)
157 				S_StartSound (-1, 0, cl_sfx_ric1, pos, 1, 1);
158 			else if (rnd == 2)
159 				S_StartSound (-1, 0, cl_sfx_ric2, pos, 1, 1);
160 			else
161 				S_StartSound (-1, 0, cl_sfx_ric3, pos, 1, 1);
162 		}
163 		break;
164 	case TE_SUPERSPIKE:			// super spike hitting wall
165 		pos[0] = MSG_ReadCoord ();
166 		pos[1] = MSG_ReadCoord ();
167 		pos[2] = MSG_ReadCoord ();
168 
169 // LordHavoc's "dust" effect
170                 R_SparkShower(pos, vec3_origin, 30, 0);
171 //                R_RunParticleEffect (pos, vec3_origin, 0, 20);
172 
173 		if ( rand() % 5 )
174 			S_StartSound (-1, 0, cl_sfx_tink1, pos, 1, 1);
175 		else
176 		{
177 			rnd = rand() & 3;
178 			if (rnd == 1)
179 				S_StartSound (-1, 0, cl_sfx_ric1, pos, 1, 1);
180 			else if (rnd == 2)
181 				S_StartSound (-1, 0, cl_sfx_ric2, pos, 1, 1);
182 			else
183 				S_StartSound (-1, 0, cl_sfx_ric3, pos, 1, 1);
184 		}
185 		break;
186 
187 	case TE_GUNSHOT:			// bullet hitting wall
188 		pos[0] = MSG_ReadCoord ();
189 		pos[1] = MSG_ReadCoord ();
190 		pos[2] = MSG_ReadCoord ();
191 
192 // LordHavoc's "dust" effect
193                 R_SparkShower(pos, vec3_origin, 15, 0);
194 //                R_RunParticleEffect (pos, vec3_origin, 0, 20);
195 		break;
196 
197 	case TE_EXPLOSION:			// rocket explosion
198 		pos[0] = MSG_ReadCoord ();
199 		pos[1] = MSG_ReadCoord ();
200 		pos[2] = MSG_ReadCoord ();
201                 R_ParticleExplosion (pos, gl_rsmoke.value);
202 		dl = CL_AllocDlight (0);
203 		VectorCopy (pos, dl->origin);
204 		dl->radius = 350;
205 		dl->die = cl.time + 0.5;
206 		dl->decay = 300;
207                 dl->color[0] = 0.4; dl->color[1] = 0.2; dl->color[2] = 0.1;
208                 S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
209 		break;
210 
211 	case TE_TAREXPLOSION:			// tarbaby explosion
212 		pos[0] = MSG_ReadCoord ();
213 		pos[1] = MSG_ReadCoord ();
214 		pos[2] = MSG_ReadCoord ();
215 		R_BlobExplosion (pos);
216 
217 		S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
218 		break;
219 
220 	case TE_LIGHTNING1:				// lightning bolts
221 		CL_ParseBeam (Mod_ForName("progs/bolt.mdl", true));
222 		break;
223 
224 	case TE_LIGHTNING2:				// lightning bolts
225 		CL_ParseBeam (Mod_ForName("progs/bolt2.mdl", true));
226 		break;
227 
228 	case TE_LIGHTNING3:				// lightning bolts
229 		CL_ParseBeam (Mod_ForName("progs/bolt3.mdl", true));
230 		break;
231 
232         case TE_LIGHTNING4:                             // lightning bolts
233                 CL_ParseBeam (Mod_ForName(MSG_ReadString(), true));
234 		break;
235 
236 // PGM 01/21/97
237 	case TE_BEAM:				// grappling hook beam
238 		CL_ParseBeam (Mod_ForName("progs/beam.mdl", true));
239 		break;
240 // PGM 01/21/97
241 
242 	case TE_LAVASPLASH:
243 		pos[0] = MSG_ReadCoord ();
244 		pos[1] = MSG_ReadCoord ();
245 		pos[2] = MSG_ReadCoord ();
246 		R_LavaSplash (pos);
247 		break;
248 
249 	case TE_TELEPORT:
250 		pos[0] = MSG_ReadCoord ();
251 		pos[1] = MSG_ReadCoord ();
252 		pos[2] = MSG_ReadCoord ();
253 		R_TeleportSplash (pos);
254 		break;
255 
256 	case TE_EXPLOSION2:				// color mapped explosion
257 		pos[0] = MSG_ReadCoord ();
258 		pos[1] = MSG_ReadCoord ();
259 		pos[2] = MSG_ReadCoord ();
260 		colorStart = MSG_ReadByte ();
261 		colorLength = MSG_ReadByte ();
262 		R_ParticleExplosion2 (pos, colorStart, colorLength);
263 		dl = CL_AllocDlight (0);
264 		VectorCopy (pos, dl->origin);
265 		dl->radius = 350;
266 		dl->die = cl.time + 0.5;
267 		dl->decay = 300;
268 		S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
269 		break;
270 
271         case TE_SMOKE:                      // rocket explosion
272 		pos[0] = MSG_ReadCoord ();
273 		pos[1] = MSG_ReadCoord ();
274 		pos[2] = MSG_ReadCoord ();
275                 R_Smoke(pos, MSG_ReadByte());
276         case TE_EXPLOSION3:                      // rocket explosion
277 		pos[0] = MSG_ReadCoord ();
278 		pos[1] = MSG_ReadCoord ();
279 		pos[2] = MSG_ReadCoord ();
280 		dl = CL_AllocDlight (0);
281 		VectorCopy (pos, dl->origin);
282 		dl->radius = 350;
283 		dl->die = cl.time + 0.5;
284 		dl->decay = 300;
285                 dl->color[0] = MSG_ReadCoord();
286                 dl->color[1] = MSG_ReadCoord();
287                 dl->color[2] = MSG_ReadCoord();
288 		S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
289 		break;
290 
291 	default:
292 		Sys_Error ("CL_ParseTEnt: bad type %d", type);
293 	}
294 }
295 
296 
297 /*
298 =================
299 CL_NewTempEntity
300 =================
301 */
CL_NewTempEntity(void)302 entity_t *CL_NewTempEntity (void)
303 {
304 	entity_t	*ent;
305 	static float	lastmsg1 = 0, lastmsg2 = 0;
306 
307 	if (cl_numvisedicts == MAX_VISEDICTS)
308 	{
309 		if (IsTimeout (&lastmsg1, 2))
310 			Con_DPrintf ("CL_NewTempEntity: too many visedicts (max = %d)\n", MAX_VISEDICTS);
311 
312 		return NULL;
313 	}
314 	if (num_temp_entities == MAX_TEMP_ENTITIES)
315 	{
316 		if (IsTimeout (&lastmsg2, 2))
317 			Con_DPrintf ("CL_NewTempEntity: too many temp_entities (max = %d)\n", MAX_TEMP_ENTITIES);
318 
319 		return NULL;
320 	}
321 	ent = &cl_temp_entities[num_temp_entities];
322 	memset (ent, 0, sizeof(*ent));
323 	num_temp_entities++;
324 	cl_visedicts[cl_numvisedicts] = ent;
325 	cl_numvisedicts++;
326 
327 	ent->colormap = vid.colormap;
328 	return ent;
329 }
330 
331 
332 /*
333 =================
334 CL_UpdateTEnts
335 =================
336 */
CL_UpdateTEnts(void)337 void CL_UpdateTEnts (void)
338 {
339 	int	    i, j;
340 	beam_t	    *b;
341 	vec3_t	    dist, org;
342 	float	    d;
343 	entity_t    *ent;
344 	float	    yaw, pitch;
345 	float	    forward;
346 
347 	if (SV_IsPaused ())
348 		return; // No beams when game paused
349 
350 	num_temp_entities = 0;
351 
352 // update lightning
353 	for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++)
354 	{
355 		if (!b->model || b->endtime < cl.time)
356 			continue;
357 
358 	// if coming from the player, update the start position
359 		if (b->entity == cl.viewentity)
360 		{
361 			VectorCopy (cl_entities[cl.viewentity].origin, b->start);
362 		}
363 
364 	// calculate pitch and yaw
365 		VectorSubtract (b->end, b->start, dist);
366 
367 		if (dist[1] == 0 && dist[0] == 0)
368 		{
369 			yaw = 0;
370 			if (dist[2] > 0)
371 				pitch = 90;
372 			else
373 				pitch = 270;
374 		}
375 		else
376 		{
377 			yaw = (int) (atan2(dist[1], dist[0]) * 180 / M_PI);
378 			if (yaw < 0)
379 				yaw += 360;
380 
381 			forward = sqrt (dist[0]*dist[0] + dist[1]*dist[1]);
382 			pitch = (int) (atan2(dist[2], forward) * 180 / M_PI);
383 			if (pitch < 0)
384 				pitch += 360;
385 		}
386 
387 	// add new entities for the lightning
388 		VectorCopy (b->start, org);
389 		d = VectorNormalize(dist);
390 		while (d > 0)
391 		{
392 			ent = CL_NewTempEntity ();
393 			if (!ent)
394 				return;
395 			VectorCopy (org, ent->origin);
396                         ent->fullbright = 1;
397 			ent->model = b->model;
398 			ent->angles[0] = pitch;
399 			ent->angles[1] = yaw;
400 			ent->angles[2] = rand()%360;
401 
402 			for (j=0 ; j<3 ; j++)
403 				org[j] += dist[j]*30;
404 			d -= 30;
405 		}
406 	}
407 
408 }
409 
410 
411