1/* 2Copyright (C) 1996-1997 Id Software, Inc. 3 4This program is free software; you can redistribute it and/or 5modify it under the terms of the GNU General Public License 6as published by the Free Software Foundation; either version 2 7of the License, or (at your option) any later version. 8 9This program is distributed in the hope that it will be useful, 10but WITHOUT ANY WARRANTY; without even the implied warranty of 11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 13See the GNU General Public License for more details. 14 15You should have received a copy of the GNU General Public License 16along with this program; if not, write to the Free Software 17Foundation, 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 24int num_temp_entities; 25entity_t cl_temp_entities[MAX_TEMP_ENTITIES]; 26beam_t cl_beams[MAX_BEAMS]; 27 28sfx_t *cl_sfx_wizhit; 29sfx_t *cl_sfx_knighthit; 30sfx_t *cl_sfx_tink1; 31sfx_t *cl_sfx_ric1; 32sfx_t *cl_sfx_ric2; 33sfx_t *cl_sfx_ric3; 34sfx_t *cl_sfx_r_exp3; 35#ifdef QUAKE2 36sfx_t *cl_sfx_imp; 37sfx_t *cl_sfx_rail; 38#endif 39 40/* 41================= 42CL_ParseTEnt 43================= 44*/ 45void CL_InitTEnts (void) 46{ 47 cl_sfx_wizhit = S_PrecacheSound ("wizard/hit.wav"); 48 cl_sfx_knighthit = S_PrecacheSound ("hknight/hit.wav"); 49 cl_sfx_tink1 = S_PrecacheSound ("weapons/tink1.wav"); 50 cl_sfx_ric1 = S_PrecacheSound ("weapons/ric1.wav"); 51 cl_sfx_ric2 = S_PrecacheSound ("weapons/ric2.wav"); 52 cl_sfx_ric3 = S_PrecacheSound ("weapons/ric3.wav"); 53 cl_sfx_r_exp3 = S_PrecacheSound ("weapons/r_exp3.wav"); 54#ifdef QUAKE2 55 cl_sfx_imp = S_PrecacheSound ("shambler/sattck1.wav"); 56 cl_sfx_rail = S_PrecacheSound ("weapons/lstart.wav"); 57#endif 58} 59 60/* 61================= 62CL_ParseBeam 63================= 64*/ 65void CL_ParseBeam (model_t *m) 66{ 67 int ent; 68 vec3_t start, end; 69 beam_t *b; 70 int i; 71 72 ent = MSG_ReadShort (); 73 74 start[0] = MSG_ReadCoord (); 75 start[1] = MSG_ReadCoord (); 76 start[2] = MSG_ReadCoord (); 77 78 end[0] = MSG_ReadCoord (); 79 end[1] = MSG_ReadCoord (); 80 end[2] = MSG_ReadCoord (); 81 82// override any beam with the same entity 83 for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++) 84 if (b->entity == ent) 85 { 86 b->entity = ent; 87 b->model = m; 88 b->endtime = cl.time + 0.2; 89 VectorCopy (start, b->start); 90 VectorCopy (end, b->end); 91 return; 92 } 93 94// find a free beam 95 for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++) 96 { 97 if (!b->model || b->endtime < cl.time) 98 { 99 b->entity = ent; 100 b->model = m; 101 b->endtime = cl.time + 0.2; 102 VectorCopy (start, b->start); 103 VectorCopy (end, b->end); 104 return; 105 } 106 } 107 Con_Printf ("beam list overflow!\n"); 108} 109 110/* 111================= 112CL_ParseTEnt 113================= 114*/ 115void CL_ParseTEnt (void) 116{ 117 int type; 118 vec3_t pos; 119#ifdef QUAKE2 120 vec3_t endpos; 121#endif 122 dlight_t *dl; 123 int rnd; 124 int colorStart, colorLength; 125 126 type = MSG_ReadByte (); 127 switch (type) 128 { 129 case TE_WIZSPIKE: // spike hitting wall 130 pos[0] = MSG_ReadCoord (); 131 pos[1] = MSG_ReadCoord (); 132 pos[2] = MSG_ReadCoord (); 133 R_RunParticleEffect (pos, vec3_origin, 20, 30); 134 S_StartSound (-1, 0, cl_sfx_wizhit, pos, 1, 1); 135 break; 136 137 case TE_KNIGHTSPIKE: // spike hitting wall 138 pos[0] = MSG_ReadCoord (); 139 pos[1] = MSG_ReadCoord (); 140 pos[2] = MSG_ReadCoord (); 141 R_RunParticleEffect (pos, vec3_origin, 226, 20); 142 S_StartSound (-1, 0, cl_sfx_knighthit, pos, 1, 1); 143 break; 144 145 case TE_SPIKE: // spike hitting wall 146 pos[0] = MSG_ReadCoord (); 147 pos[1] = MSG_ReadCoord (); 148 pos[2] = MSG_ReadCoord (); 149#ifdef GLTEST 150 Test_Spawn (pos); 151#else 152 R_RunParticleEffect (pos, vec3_origin, 0, 10); 153#endif 154 if ( rand() % 5 ) 155 S_StartSound (-1, 0, cl_sfx_tink1, pos, 1, 1); 156 else 157 { 158 rnd = rand() & 3; 159 if (rnd == 1) 160 S_StartSound (-1, 0, cl_sfx_ric1, pos, 1, 1); 161 else if (rnd == 2) 162 S_StartSound (-1, 0, cl_sfx_ric2, pos, 1, 1); 163 else 164 S_StartSound (-1, 0, cl_sfx_ric3, pos, 1, 1); 165 } 166 break; 167 case TE_SUPERSPIKE: // super spike hitting wall 168 pos[0] = MSG_ReadCoord (); 169 pos[1] = MSG_ReadCoord (); 170 pos[2] = MSG_ReadCoord (); 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 R_RunParticleEffect (pos, vec3_origin, 0, 20); 192 break; 193 194 case TE_EXPLOSION: // rocket explosion 195 pos[0] = MSG_ReadCoord (); 196 pos[1] = MSG_ReadCoord (); 197 pos[2] = MSG_ReadCoord (); 198 R_ParticleExplosion (pos); 199 dl = CL_AllocDlight (0); 200 VectorCopy (pos, dl->origin); 201 dl->radius = 350; 202 dl->die = cl.time + 0.5; 203 dl->decay = 300; 204 S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1); 205 break; 206 207 case TE_TAREXPLOSION: // tarbaby explosion 208 pos[0] = MSG_ReadCoord (); 209 pos[1] = MSG_ReadCoord (); 210 pos[2] = MSG_ReadCoord (); 211 R_BlobExplosion (pos); 212 213 S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1); 214 break; 215 216 case TE_LIGHTNING1: // lightning bolts 217 CL_ParseBeam (Mod_ForName("progs/bolt.mdl", true)); 218 break; 219 220 case TE_LIGHTNING2: // lightning bolts 221 CL_ParseBeam (Mod_ForName("progs/bolt2.mdl", true)); 222 break; 223 224 case TE_LIGHTNING3: // lightning bolts 225 CL_ParseBeam (Mod_ForName("progs/bolt3.mdl", true)); 226 break; 227 228// PGM 01/21/97 229 case TE_BEAM: // grappling hook beam 230 CL_ParseBeam (Mod_ForName("progs/beam.mdl", true)); 231 break; 232// PGM 01/21/97 233 234 case TE_LAVASPLASH: 235 pos[0] = MSG_ReadCoord (); 236 pos[1] = MSG_ReadCoord (); 237 pos[2] = MSG_ReadCoord (); 238 R_LavaSplash (pos); 239 break; 240 241 case TE_TELEPORT: 242 pos[0] = MSG_ReadCoord (); 243 pos[1] = MSG_ReadCoord (); 244 pos[2] = MSG_ReadCoord (); 245 R_TeleportSplash (pos); 246 break; 247 248 case TE_EXPLOSION2: // color mapped explosion 249 pos[0] = MSG_ReadCoord (); 250 pos[1] = MSG_ReadCoord (); 251 pos[2] = MSG_ReadCoord (); 252 colorStart = MSG_ReadByte (); 253 colorLength = MSG_ReadByte (); 254 R_ParticleExplosion2 (pos, colorStart, colorLength); 255 dl = CL_AllocDlight (0); 256 VectorCopy (pos, dl->origin); 257 dl->radius = 350; 258 dl->die = cl.time + 0.5; 259 dl->decay = 300; 260 S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1); 261 break; 262 263#ifdef QUAKE2 264 case TE_IMPLOSION: 265 pos[0] = MSG_ReadCoord (); 266 pos[1] = MSG_ReadCoord (); 267 pos[2] = MSG_ReadCoord (); 268 S_StartSound (-1, 0, cl_sfx_imp, pos, 1, 1); 269 break; 270 271 case TE_RAILTRAIL: 272 pos[0] = MSG_ReadCoord (); 273 pos[1] = MSG_ReadCoord (); 274 pos[2] = MSG_ReadCoord (); 275 endpos[0] = MSG_ReadCoord (); 276 endpos[1] = MSG_ReadCoord (); 277 endpos[2] = MSG_ReadCoord (); 278 S_StartSound (-1, 0, cl_sfx_rail, pos, 1, 1); 279 S_StartSound (-1, 1, cl_sfx_r_exp3, endpos, 1, 1); 280 R_RocketTrail (pos, endpos, 0+128); 281 R_ParticleExplosion (endpos); 282 dl = CL_AllocDlight (-1); 283 VectorCopy (endpos, dl->origin); 284 dl->radius = 350; 285 dl->die = cl.time + 0.5; 286 dl->decay = 300; 287 break; 288#endif 289 290 default: 291 Sys_Error ("CL_ParseTEnt: bad type"); 292 } 293} 294 295 296/* 297================= 298CL_NewTempEntity 299================= 300*/ 301entity_t *CL_NewTempEntity (void) 302{ 303 entity_t *ent; 304 305 if (cl_numvisedicts == MAX_VISEDICTS) 306 return NULL; 307 if (num_temp_entities == MAX_TEMP_ENTITIES) 308 return NULL; 309 ent = &cl_temp_entities[num_temp_entities]; 310 memset (ent, 0, sizeof(*ent)); 311 num_temp_entities++; 312 cl_visedicts[cl_numvisedicts] = ent; 313 cl_numvisedicts++; 314 315 ent->colormap = vid.colormap; 316 return ent; 317} 318 319 320/* 321================= 322CL_UpdateTEnts 323================= 324*/ 325void CL_UpdateTEnts (void) 326{ 327 int i; 328 beam_t *b; 329 vec3_t dist, org; 330 float d; 331 entity_t *ent; 332 float yaw, pitch; 333 float forward; 334 335 num_temp_entities = 0; 336 337// update lightning 338 for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++) 339 { 340 if (!b->model || b->endtime < cl.time) 341 continue; 342 343 // if coming from the player, update the start position 344 if (b->entity == cl.viewentity) 345 { 346 VectorCopy (cl_entities[cl.viewentity].origin, b->start); 347 } 348 349 // calculate pitch and yaw 350 VectorSubtract (b->end, b->start, dist); 351 352 if (dist[1] == 0 && dist[0] == 0) 353 { 354 yaw = 0; 355 if (dist[2] > 0) 356 pitch = 90; 357 else 358 pitch = 270; 359 } 360 else 361 { 362 yaw = (int) (atan2(dist[1], dist[0]) * 180 / M_PI); 363 if (yaw < 0) 364 yaw += 360; 365 366 forward = sqrt (dist[0]*dist[0] + dist[1]*dist[1]); 367 pitch = (int) (atan2(dist[2], forward) * 180 / M_PI); 368 if (pitch < 0) 369 pitch += 360; 370 } 371 372 // add new entities for the lightning 373 VectorCopy (b->start, org); 374 d = VectorNormalize(dist); 375 while (d > 0) 376 { 377 ent = CL_NewTempEntity (); 378 if (!ent) 379 return; 380 VectorCopy (org, ent->origin); 381 ent->model = b->model; 382 ent->angles[0] = pitch; 383 ent->angles[1] = yaw; 384 ent->angles[2] = rand()%360; 385 386 for (i=0 ; i<3 ; i++) 387 org[i] += dist[i]*30; 388 d -= 30; 389 } 390 } 391 392} 393 394 395