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
25 #define MAX_BEAMS 8
26 typedef struct
27 {
28 int entity;
29 struct model_s *model;
30 float endtime;
31 vec3_t start, end;
32 } beam_t;
33
34 beam_t cl_beams[MAX_BEAMS];
35
36 #define MAX_EXPLOSIONS 8
37 typedef struct
38 {
39 vec3_t origin;
40 float start;
41 model_t *model;
42 } explosion_t;
43
44 explosion_t cl_explosions[MAX_EXPLOSIONS];
45
46
47 sfx_t *cl_sfx_wizhit;
48 sfx_t *cl_sfx_knighthit;
49 sfx_t *cl_sfx_tink1;
50 sfx_t *cl_sfx_ric1;
51 sfx_t *cl_sfx_ric2;
52 sfx_t *cl_sfx_ric3;
53 sfx_t *cl_sfx_r_exp3;
54
55 /*
56 =================
57 CL_ParseTEnts
58 =================
59 */
CL_InitTEnts(void)60 void CL_InitTEnts (void)
61 {
62 cl_sfx_wizhit = S_PrecacheSound ("wizard/hit.wav");
63 cl_sfx_knighthit = S_PrecacheSound ("hknight/hit.wav");
64 cl_sfx_tink1 = S_PrecacheSound ("weapons/tink1.wav");
65 cl_sfx_ric1 = S_PrecacheSound ("weapons/ric1.wav");
66 cl_sfx_ric2 = S_PrecacheSound ("weapons/ric2.wav");
67 cl_sfx_ric3 = S_PrecacheSound ("weapons/ric3.wav");
68 cl_sfx_r_exp3 = S_PrecacheSound ("weapons/r_exp3.wav");
69 }
70
71 /*
72 =================
73 CL_ClearTEnts
74 =================
75 */
CL_ClearTEnts(void)76 void CL_ClearTEnts (void)
77 {
78 memset (&cl_beams, 0, sizeof(cl_beams));
79 memset (&cl_explosions, 0, sizeof(cl_explosions));
80 }
81
82 /*
83 =================
84 CL_AllocExplosion
85 =================
86 */
CL_AllocExplosion(void)87 explosion_t *CL_AllocExplosion (void)
88 {
89 int i;
90 float time;
91 int index;
92
93 for (i=0 ; i<MAX_EXPLOSIONS ; i++)
94 if (!cl_explosions[i].model)
95 return &cl_explosions[i];
96 // find the oldest explosion
97 time = cl.time;
98 index = 0;
99
100 for (i=0 ; i<MAX_EXPLOSIONS ; i++)
101 if (cl_explosions[i].start < time)
102 {
103 time = cl_explosions[i].start;
104 index = i;
105 }
106 return &cl_explosions[index];
107 }
108
109 /*
110 =================
111 CL_ParseBeam
112 =================
113 */
CL_ParseBeam(model_t * m)114 void CL_ParseBeam (model_t *m)
115 {
116 int ent;
117 vec3_t start, end;
118 beam_t *b;
119 int i;
120
121 ent = MSG_ReadShort ();
122
123 start[0] = MSG_ReadCoord ();
124 start[1] = MSG_ReadCoord ();
125 start[2] = MSG_ReadCoord ();
126
127 end[0] = MSG_ReadCoord ();
128 end[1] = MSG_ReadCoord ();
129 end[2] = MSG_ReadCoord ();
130
131 // override any beam with the same entity
132 for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++)
133 if (b->entity == ent)
134 {
135 b->entity = ent;
136 b->model = m;
137 b->endtime = cl.time + 0.2;
138 VectorCopy (start, b->start);
139 VectorCopy (end, b->end);
140 return;
141 }
142
143 // find a free beam
144 for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++)
145 {
146 if (!b->model || b->endtime < cl.time)
147 {
148 b->entity = ent;
149 b->model = m;
150 b->endtime = cl.time + 0.2;
151 VectorCopy (start, b->start);
152 VectorCopy (end, b->end);
153 return;
154 }
155 }
156 Con_Printf ("beam list overflow!\n");
157 }
158
159 /*
160 =================
161 CL_ParseTEnt
162 =================
163 */
CL_ParseTEnt(void)164 void CL_ParseTEnt (void)
165 {
166 int type;
167 vec3_t pos;
168 dlight_t *dl;
169 int rnd;
170 explosion_t *ex;
171 int cnt;
172
173 type = MSG_ReadByte ();
174 switch (type)
175 {
176 case TE_WIZSPIKE: // spike hitting wall
177 pos[0] = MSG_ReadCoord ();
178 pos[1] = MSG_ReadCoord ();
179 pos[2] = MSG_ReadCoord ();
180 R_RunParticleEffect (pos, vec3_origin, 20, 30);
181 S_StartSound (-1, 0, cl_sfx_wizhit, pos, 1, 1);
182 break;
183
184 case TE_KNIGHTSPIKE: // spike hitting wall
185 pos[0] = MSG_ReadCoord ();
186 pos[1] = MSG_ReadCoord ();
187 pos[2] = MSG_ReadCoord ();
188 R_RunParticleEffect (pos, vec3_origin, 226, 20);
189 S_StartSound (-1, 0, cl_sfx_knighthit, pos, 1, 1);
190 break;
191
192 case TE_SPIKE: // spike hitting wall
193 pos[0] = MSG_ReadCoord ();
194 pos[1] = MSG_ReadCoord ();
195 pos[2] = MSG_ReadCoord ();
196 R_RunParticleEffect (pos, vec3_origin, 0, 10);
197
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 case TE_SUPERSPIKE: // super spike hitting wall
212 pos[0] = MSG_ReadCoord ();
213 pos[1] = MSG_ReadCoord ();
214 pos[2] = MSG_ReadCoord ();
215 R_RunParticleEffect (pos, vec3_origin, 0, 20);
216
217 if ( rand() % 5 )
218 S_StartSound (-1, 0, cl_sfx_tink1, pos, 1, 1);
219 else
220 {
221 rnd = rand() & 3;
222 if (rnd == 1)
223 S_StartSound (-1, 0, cl_sfx_ric1, pos, 1, 1);
224 else if (rnd == 2)
225 S_StartSound (-1, 0, cl_sfx_ric2, pos, 1, 1);
226 else
227 S_StartSound (-1, 0, cl_sfx_ric3, pos, 1, 1);
228 }
229 break;
230
231 case TE_EXPLOSION: // rocket explosion
232 // particles
233 pos[0] = MSG_ReadCoord ();
234 pos[1] = MSG_ReadCoord ();
235 pos[2] = MSG_ReadCoord ();
236 R_ParticleExplosion (pos);
237
238 // light
239 dl = CL_AllocDlight (0);
240 VectorCopy (pos, dl->origin);
241 dl->radius = 350;
242 dl->die = cl.time + 0.5;
243 dl->decay = 300;
244 dl->color[0] = 0.2;
245 dl->color[1] = 0.1;
246 dl->color[2] = 0.05;
247 dl->color[3] = 0.7;
248
249 // sound
250 S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
251
252 // sprite
253 ex = CL_AllocExplosion ();
254 VectorCopy (pos, ex->origin);
255 ex->start = cl.time;
256 ex->model = Mod_ForName ("progs/s_explod.spr", true);
257 break;
258
259 case TE_TAREXPLOSION: // tarbaby explosion
260 pos[0] = MSG_ReadCoord ();
261 pos[1] = MSG_ReadCoord ();
262 pos[2] = MSG_ReadCoord ();
263 R_BlobExplosion (pos);
264
265 S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
266 break;
267
268 case TE_LIGHTNING1: // lightning bolts
269 CL_ParseBeam (Mod_ForName("progs/bolt.mdl", true));
270 break;
271
272 case TE_LIGHTNING2: // lightning bolts
273 CL_ParseBeam (Mod_ForName("progs/bolt2.mdl", true));
274 break;
275
276 case TE_LIGHTNING3: // lightning bolts
277 CL_ParseBeam (Mod_ForName("progs/bolt3.mdl", true));
278 break;
279
280 case TE_LAVASPLASH:
281 pos[0] = MSG_ReadCoord ();
282 pos[1] = MSG_ReadCoord ();
283 pos[2] = MSG_ReadCoord ();
284 R_LavaSplash (pos);
285 break;
286
287 case TE_TELEPORT:
288 pos[0] = MSG_ReadCoord ();
289 pos[1] = MSG_ReadCoord ();
290 pos[2] = MSG_ReadCoord ();
291 R_TeleportSplash (pos);
292 break;
293
294 case TE_GUNSHOT: // bullet hitting wall
295 cnt = MSG_ReadByte ();
296 pos[0] = MSG_ReadCoord ();
297 pos[1] = MSG_ReadCoord ();
298 pos[2] = MSG_ReadCoord ();
299 R_RunParticleEffect (pos, vec3_origin, 0, 20*cnt);
300 break;
301
302 case TE_BLOOD: // bullets hitting body
303 cnt = MSG_ReadByte ();
304 pos[0] = MSG_ReadCoord ();
305 pos[1] = MSG_ReadCoord ();
306 pos[2] = MSG_ReadCoord ();
307 R_RunParticleEffect (pos, vec3_origin, 73, 20*cnt);
308 break;
309
310 case TE_LIGHTNINGBLOOD: // lightning hitting body
311 pos[0] = MSG_ReadCoord ();
312 pos[1] = MSG_ReadCoord ();
313 pos[2] = MSG_ReadCoord ();
314 R_RunParticleEffect (pos, vec3_origin, 225, 50);
315 break;
316
317 default:
318 Sys_Error ("CL_ParseTEnt: bad type");
319 }
320 }
321
322
323 /*
324 =================
325 CL_NewTempEntity
326 =================
327 */
CL_NewTempEntity(void)328 entity_t *CL_NewTempEntity (void)
329 {
330 entity_t *ent;
331
332 if (cl_numvisedicts == MAX_VISEDICTS)
333 return NULL;
334 ent = &cl_visedicts[cl_numvisedicts];
335 cl_numvisedicts++;
336 ent->keynum = 0;
337
338 memset (ent, 0, sizeof(*ent));
339
340 ent->colormap = vid.colormap;
341 return ent;
342 }
343
344
345 /*
346 =================
347 CL_UpdateBeams
348 =================
349 */
CL_UpdateBeams(void)350 void CL_UpdateBeams (void)
351 {
352 int i;
353 beam_t *b;
354 vec3_t dist, org;
355 float d;
356 entity_t *ent;
357 float yaw, pitch;
358 float forward;
359
360 // update lightning
361 for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++)
362 {
363 if (!b->model || b->endtime < cl.time)
364 continue;
365
366 // if coming from the player, update the start position
367 if (b->entity == cl.playernum+1) // entity 0 is the world
368 {
369 VectorCopy (cl.simorg, b->start);
370 // b->start[2] -= 22; // adjust for view height
371 }
372
373 // calculate pitch and yaw
374 VectorSubtract (b->end, b->start, dist);
375
376 if (dist[1] == 0 && dist[0] == 0)
377 {
378 yaw = 0;
379 if (dist[2] > 0)
380 pitch = 90;
381 else
382 pitch = 270;
383 }
384 else
385 {
386 yaw = (int) (atan2(dist[1], dist[0]) * 180 / M_PI);
387 if (yaw < 0)
388 yaw += 360;
389
390 forward = sqrt (dist[0]*dist[0] + dist[1]*dist[1]);
391 pitch = (int) (atan2(dist[2], forward) * 180 / M_PI);
392 if (pitch < 0)
393 pitch += 360;
394 }
395
396 // add new entities for the lightning
397 VectorCopy (b->start, org);
398 d = VectorNormalize(dist);
399 while (d > 0)
400 {
401 ent = CL_NewTempEntity ();
402 if (!ent)
403 return;
404 VectorCopy (org, ent->origin);
405 ent->model = b->model;
406 ent->angles[0] = pitch;
407 ent->angles[1] = yaw;
408 ent->angles[2] = rand()%360;
409
410 for (i=0 ; i<3 ; i++)
411 org[i] += dist[i]*30;
412 d -= 30;
413 }
414 }
415
416 }
417
418 /*
419 =================
420 CL_UpdateExplosions
421 =================
422 */
CL_UpdateExplosions(void)423 void CL_UpdateExplosions (void)
424 {
425 int i;
426 int f;
427 explosion_t *ex;
428 entity_t *ent;
429
430 for (i=0, ex=cl_explosions ; i< MAX_EXPLOSIONS ; i++, ex++)
431 {
432 if (!ex->model)
433 continue;
434 f = 10*(cl.time - ex->start);
435 if (f >= ex->model->numframes)
436 {
437 ex->model = NULL;
438 continue;
439 }
440
441 ent = CL_NewTempEntity ();
442 if (!ent)
443 return;
444 VectorCopy (ex->origin, ent->origin);
445 ent->model = ex->model;
446 ent->frame = f;
447 }
448 }
449
450 /*
451 =================
452 CL_UpdateTEnts
453 =================
454 */
CL_UpdateTEnts(void)455 void CL_UpdateTEnts (void)
456 {
457 CL_UpdateBeams ();
458 CL_UpdateExplosions ();
459 }
460