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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 455 void CL_UpdateTEnts (void) 456 { 457 CL_UpdateBeams (); 458 CL_UpdateExplosions (); 459 } 460