File: client\cl_tent.c
1 /*
2 Copyright (C) 1997-2001 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 "client.h"
23
24 typedef enum
25 {
26 ex_free, ex_explosion, ex_misc, ex_flash, ex_mflash, ex_poly, ex_poly2
27 } exptype_t;
28
29 typedef struct
30 {
31 exptype_t type;
32 entity_t ent;
33
34 int frames;
35 float light;
36 vec3_t lightcolor;
37 float start;
38 int baseframe;
39 } explosion_t;
40
41
42
43 #define MAX_EXPLOSIONS 32
44 explosion_t cl_explosions[MAX_EXPLOSIONS];
45
46
47 #define MAX_BEAMS 32
48 typedef struct
49 {
50 int entity;
51 int dest_entity;
52 struct model_s *model;
53 int endtime;
54 vec3_t offset;
55 vec3_t start, end;
56 } beam_t;
57 beam_t cl_beams[MAX_BEAMS];
58 //PMM - added this for player-linked beams. Currently only used by the plasma beam
59 beam_t cl_playerbeams[MAX_BEAMS];
60
61
62 #define MAX_LASERS 32
63 typedef struct
64 {
65 entity_t ent;
66 int endtime;
67 } laser_t;
68 laser_t cl_lasers[MAX_LASERS];
69
70 //ROGUE
71 cl_sustain_t cl_sustains[MAX_SUSTAINS];
72 //ROGUE
73
74 //PGM
75 extern void CL_TeleportParticles (vec3_t org);
76 //PGM
77
78 void CL_BlasterParticles (vec3_t org, vec3_t dir);
79 void CL_ExplosionParticles (vec3_t org);
80 void CL_BFGExplosionParticles (vec3_t org);
81 // RAFAEL
82 void CL_BlueBlasterParticles (vec3_t org, vec3_t dir);
83
84 struct sfx_s *cl_sfx_ric1;
85 struct sfx_s *cl_sfx_ric2;
86 struct sfx_s *cl_sfx_ric3;
87 struct sfx_s *cl_sfx_lashit;
88 struct sfx_s *cl_sfx_spark5;
89 struct sfx_s *cl_sfx_spark6;
90 struct sfx_s *cl_sfx_spark7;
91 struct sfx_s *cl_sfx_railg;
92 struct sfx_s *cl_sfx_rockexp;
93 struct sfx_s *cl_sfx_grenexp;
94 struct sfx_s *cl_sfx_watrexp;
95 // RAFAEL
96 struct sfx_s *cl_sfx_plasexp;
97 struct sfx_s *cl_sfx_footsteps[4];
98
99 struct model_s *cl_mod_explode;
100 struct model_s *cl_mod_smoke;
101 struct model_s *cl_mod_flash;
102 struct model_s *cl_mod_parasite_segment;
103 struct model_s *cl_mod_grapple_cable;
104 struct model_s *cl_mod_parasite_tip;
105 struct model_s *cl_mod_explo4;
106 struct model_s *cl_mod_bfg_explo;
107 struct model_s *cl_mod_powerscreen;
108 // RAFAEL
109 struct model_s *cl_mod_plasmaexplo;
110
111 //ROGUE
112 struct sfx_s *cl_sfx_lightning;
113 struct sfx_s *cl_sfx_disrexp;
114 struct model_s *cl_mod_lightning;
115 struct model_s *cl_mod_heatbeam;
116 struct model_s *cl_mod_monster_heatbeam;
117 struct model_s *cl_mod_explo4_big;
118
119 //ROGUE
120 /*
121 =================
122 CL_RegisterTEntSounds
123 =================
124 */
125 void CL_RegisterTEntSounds (void)
126 {
127 int i;
128 char name[MAX_QPATH];
129
130 // PMM - version stuff
131 // Com_Printf ("%s\n", ROGUE_VERSION_STRING);
132 // PMM
133 cl_sfx_ric1 = S_RegisterSound ("world/ric1.wav");
134 cl_sfx_ric2 = S_RegisterSound ("world/ric2.wav");
135 cl_sfx_ric3 = S_RegisterSound ("world/ric3.wav");
136 cl_sfx_lashit = S_RegisterSound("weapons/lashit.wav");
137 cl_sfx_spark5 = S_RegisterSound ("world/spark5.wav");
138 cl_sfx_spark6 = S_RegisterSound ("world/spark6.wav");
139 cl_sfx_spark7 = S_RegisterSound ("world/spark7.wav");
140 cl_sfx_railg = S_RegisterSound ("weapons/railgf1a.wav");
141 cl_sfx_rockexp = S_RegisterSound ("weapons/rocklx1a.wav");
142 cl_sfx_grenexp = S_RegisterSound ("weapons/grenlx1a.wav");
143 cl_sfx_watrexp = S_RegisterSound ("weapons/xpld_wat.wav");
144 // RAFAEL
145 // cl_sfx_plasexp = S_RegisterSound ("weapons/plasexpl.wav");
146 S_RegisterSound ("player/land1.wav");
147
148 S_RegisterSound ("player/fall2.wav");
149 S_RegisterSound ("player/fall1.wav");
150
151 for (i=0 ; i<4 ; i++)
152 {
153 Com_sprintf (name, sizeof(name), "player/step%i.wav", i+1);
154 cl_sfx_footsteps[i] = S_RegisterSound (name);
155 }
156
157 //PGM
158 cl_sfx_lightning = S_RegisterSound ("weapons/tesla.wav");
159 cl_sfx_disrexp = S_RegisterSound ("weapons/disrupthit.wav");
160 // version stuff
161 // sprintf (name, "weapons/sound%d.wav", ROGUE_VERSION_ID);
162 // if (name[0] == 'w')
163 // name[0] = 'W';
164 //PGM
165 }
166
167 /*
168 =================
169 CL_RegisterTEntModels
170 =================
171 */
172 void CL_RegisterTEntModels (void)
173 {
174 cl_mod_explode = re.RegisterModel ("models/objects/explode/tris.md2");
175 cl_mod_smoke = re.RegisterModel ("models/objects/smoke/tris.md2");
176 cl_mod_flash = re.RegisterModel ("models/objects/flash/tris.md2");
177 cl_mod_parasite_segment = re.RegisterModel ("models/monsters/parasite/segment/tris.md2");
178 cl_mod_grapple_cable = re.RegisterModel ("models/ctf/segment/tris.md2");
179 cl_mod_parasite_tip = re.RegisterModel ("models/monsters/parasite/tip/tris.md2");
180 cl_mod_explo4 = re.RegisterModel ("models/objects/r_explode/tris.md2");
181 cl_mod_bfg_explo = re.RegisterModel ("sprites/s_bfg2.sp2");
182 cl_mod_powerscreen = re.RegisterModel ("models/items/armor/effect/tris.md2");
183
184 re.RegisterModel ("models/objects/laser/tris.md2");
185 re.RegisterModel ("models/objects/grenade2/tris.md2");
186 re.RegisterModel ("models/weapons/v_machn/tris.md2");
187 re.RegisterModel ("models/weapons/v_handgr/tris.md2");
188 re.RegisterModel ("models/weapons/v_shotg2/tris.md2");
189 re.RegisterModel ("models/objects/gibs/bone/tris.md2");
190 re.RegisterModel ("models/objects/gibs/sm_meat/tris.md2");
191 re.RegisterModel ("models/objects/gibs/bone2/tris.md2");
192 // RAFAEL
193 // re.RegisterModel ("models/objects/blaser/tris.md2");
194
195 re.RegisterPic ("w_machinegun");
196 re.RegisterPic ("a_bullets");
197 re.RegisterPic ("i_health");
198 re.RegisterPic ("a_grenades");
199
200 //ROGUE
201 cl_mod_explo4_big = re.RegisterModel ("models/objects/r_explode2/tris.md2");
202 cl_mod_lightning = re.RegisterModel ("models/proj/lightning/tris.md2");
203 cl_mod_heatbeam = re.RegisterModel ("models/proj/beam/tris.md2");
204 cl_mod_monster_heatbeam = re.RegisterModel ("models/proj/widowbeam/tris.md2");
205 //ROGUE
206 }
207
208 /*
209 =================
210 CL_ClearTEnts
211 =================
212 */
213 void CL_ClearTEnts (void)
214 {
215 memset (cl_beams, 0, sizeof(cl_beams));
216 memset (cl_explosions, 0, sizeof(cl_explosions));
217 memset (cl_lasers, 0, sizeof(cl_lasers));
218
219 //ROGUE
220 memset (cl_playerbeams, 0, sizeof(cl_playerbeams));
221 memset (cl_sustains, 0, sizeof(cl_sustains));
222 //ROGUE
223 }
224
225 /*
226 =================
227 CL_AllocExplosion
228 =================
229 */
230 explosion_t *CL_AllocExplosion (void)
231 {
232 int i;
233 int time;
234 int index;
235
236 for (i=0 ; i<MAX_EXPLOSIONS ; i++)
237 {
238 if (cl_explosions[i].type == ex_free)
239 {
240 memset (&cl_explosions[i], 0, sizeof (cl_explosions[i]));
241 return &cl_explosions[i];
242 }
243 }
244 // find the oldest explosion
245 time = cl.time;
246 index = 0;
247
248 for (i=0 ; i<MAX_EXPLOSIONS ; i++)
249 if (cl_explosions[i].start < time)
250 {
251 time = cl_explosions[i].start;
252 index = i;
253 }
254 memset (&cl_explosions[index], 0, sizeof (cl_explosions[index]));
255 return &cl_explosions[index];
256 }
257
258 /*
259 =================
260 CL_SmokeAndFlash
261 =================
262 */
263 void CL_SmokeAndFlash(vec3_t origin)
264 {
265 explosion_t *ex;
266
267 ex = CL_AllocExplosion ();
268 VectorCopy (origin, ex->ent.origin);
269 ex->type = ex_misc;
270 ex->frames = 4;
271 ex->ent.flags = RF_TRANSLUCENT;
272 ex->start = cl.frame.servertime - 100;
273 ex->ent.model = cl_mod_smoke;
274
275 ex = CL_AllocExplosion ();
276 VectorCopy (origin, ex->ent.origin);
277 ex->type = ex_flash;
278 ex->ent.flags = RF_FULLBRIGHT;
279 ex->frames = 2;
280 ex->start = cl.frame.servertime - 100;
281 ex->ent.model = cl_mod_flash;
282 }
283
284 /*
285 =================
286 CL_ParseParticles
287 =================
288 */
289 void CL_ParseParticles (void)
290 {
291 int color, count;
292 vec3_t pos, dir;
293
294 MSG_ReadPos (&net_message, pos);
295 MSG_ReadDir (&net_message, dir);
296
297 color = MSG_ReadByte (&net_message);
298
299 count = MSG_ReadByte (&net_message);
300
301 CL_ParticleEffect (pos, dir, color, count);
302 }
303
304 /*
305 =================
306 CL_ParseBeam
307 =================
308 */
309 int CL_ParseBeam (struct model_s *model)
310 {
311 int ent;
312 vec3_t start, end;
313 beam_t *b;
314 int i;
315
316 ent = MSG_ReadShort (&net_message);
317
318 MSG_ReadPos (&net_message, start);
319 MSG_ReadPos (&net_message, end);
320
321 // override any beam with the same entity
322 for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++)
323 if (b->entity == ent)
324 {
325 b->entity = ent;
326 b->model = model;
327 b->endtime = cl.time + 200;
328 VectorCopy (start, b->start);
329 VectorCopy (end, b->end);
330 VectorClear (b->offset);
331 return ent;
332 }
333
334 // find a free beam
335 for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++)
336 {
337 if (!b->model || b->endtime < cl.time)
338 {
339 b->entity = ent;
340 b->model = model;
341 b->endtime = cl.time + 200;
342 VectorCopy (start, b->start);
343 VectorCopy (end, b->end);
344 VectorClear (b->offset);
345 return ent;
346 }
347 }
348 Com_Printf ("beam list overflow!\n");
349 return ent;
350 }
351
352 /*
353 =================
354 CL_ParseBeam2
355 =================
356 */
357 int CL_ParseBeam2 (struct model_s *model)
358 {
359 int ent;
360 vec3_t start, end, offset;
361 beam_t *b;
362 int i;
363
364 ent = MSG_ReadShort (&net_message);
365
366 MSG_ReadPos (&net_message, start);
367 MSG_ReadPos (&net_message, end);
368 MSG_ReadPos (&net_message, offset);
369
370 // Com_Printf ("end- %f %f %f\n", end[0], end[1], end[2]);
371
372 // override any beam with the same entity
373
374 for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++)
375 if (b->entity == ent)
376 {
377 b->entity = ent;
378 b->model = model;
379 b->endtime = cl.time + 200;
380 VectorCopy (start, b->start);
381 VectorCopy (end, b->end);
382 VectorCopy (offset, b->offset);
383 return ent;
384 }
385
386 // find a free beam
387 for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++)
388 {
389 if (!b->model || b->endtime < cl.time)
390 {
391 b->entity = ent;
392 b->model = model;
393 b->endtime = cl.time + 200;
394 VectorCopy (start, b->start);
395 VectorCopy (end, b->end);
396 VectorCopy (offset, b->offset);
397 return ent;
398 }
399 }
400 Com_Printf ("beam list overflow!\n");
401 return ent;
402 }
403
404 // ROGUE
405 /*
406 =================
407 CL_ParsePlayerBeam
408 - adds to the cl_playerbeam array instead of the cl_beams array
409 =================
410 */
411 int CL_ParsePlayerBeam (struct model_s *model)
412 {
413 int ent;
414 vec3_t start, end, offset;
415 beam_t *b;
416 int i;
417
418 ent = MSG_ReadShort (&net_message);
419
420 MSG_ReadPos (&net_message, start);
421 MSG_ReadPos (&net_message, end);
422 // PMM - network optimization
423 if (model == cl_mod_heatbeam)
424 VectorSet(offset, 2, 7, -3);
425 else if (model == cl_mod_monster_heatbeam)
426 {
427 model = cl_mod_heatbeam;
428 VectorSet(offset, 0, 0, 0);
429 }
430 else
431 MSG_ReadPos (&net_message, offset);
432
433 // Com_Printf ("end- %f %f %f\n", end[0], end[1], end[2]);
434
435 // override any beam with the same entity
436 // PMM - For player beams, we only want one per player (entity) so..
437 for (i=0, b=cl_playerbeams ; i< MAX_BEAMS ; i++, b++)
438 {
439 if (b->entity == ent)
440 {
441 b->entity = ent;
442 b->model = model;
443 b->endtime = cl.time + 200;
444 VectorCopy (start, b->start);
445 VectorCopy (end, b->end);
446 VectorCopy (offset, b->offset);
447 return ent;
448 }
449 }
450
451 // find a free beam
452 for (i=0, b=cl_playerbeams ; i< MAX_BEAMS ; i++, b++)
453 {
454 if (!b->model || b->endtime < cl.time)
455 {
456 b->entity = ent;
457 b->model = model;
458 b->endtime = cl.time + 100; // PMM - this needs to be 100 to prevent multiple heatbeams
459 VectorCopy (start, b->start);
460 VectorCopy (end, b->end);
461 VectorCopy (offset, b->offset);
462 return ent;
463 }
464 }
465 Com_Printf ("beam list overflow!\n");
466 return ent;
467 }
468 //rogue
469
470 /*
471 =================
472 CL_ParseLightning
473 =================
474 */
475 int CL_ParseLightning (struct model_s *model)
476 {
477 int srcEnt, destEnt;
478 vec3_t start, end;
479 beam_t *b;
480 int i;
481
482 srcEnt = MSG_ReadShort (&net_message);
483 destEnt = MSG_ReadShort (&net_message);
484
485 MSG_ReadPos (&net_message, start);
486 MSG_ReadPos (&net_message, end);
487
488 // override any beam with the same source AND destination entities
489 for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++)
490 if (b->entity == srcEnt && b->dest_entity == destEnt)
491 {
492 // Com_Printf("%d: OVERRIDE %d -> %d\n", cl.time, srcEnt, destEnt);
493 b->entity = srcEnt;
494 b->dest_entity = destEnt;
495 b->model = model;
496 b->endtime = cl.time + 200;
497 VectorCopy (start, b->start);
498 VectorCopy (end, b->end);
499 VectorClear (b->offset);
500 return srcEnt;
501 }
502
503 // find a free beam
504 for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++)
505 {
506 if (!b->model || b->endtime < cl.time)
507 {
508 // Com_Printf("%d: NORMAL %d -> %d\n", cl.time, srcEnt, destEnt);
509 b->entity = srcEnt;
510 b->dest_entity = destEnt;
511 b->model = model;
512 b->endtime = cl.time + 200;
513 VectorCopy (start, b->start);
514 VectorCopy (end, b->end);
515 VectorClear (b->offset);
516 return srcEnt;
517 }
518 }
519 Com_Printf ("beam list overflow!\n");
520 return srcEnt;
521 }
522
523 /*
524 =================
525 CL_ParseLaser
526 =================
527 */
528 void CL_ParseLaser (int colors)
529 {
530 vec3_t start;
531 vec3_t end;
532 laser_t *l;
533 int i;
534
535 MSG_ReadPos (&net_message, start);
536 MSG_ReadPos (&net_message, end);
537
538 for (i=0, l=cl_lasers ; i< MAX_LASERS ; i++, l++)
539 {
540 if (l->endtime < cl.time)
541 {
542 l->ent.flags = RF_TRANSLUCENT | RF_BEAM;
543 VectorCopy (start, l->ent.origin);
544 VectorCopy (end, l->ent.oldorigin);
545 l->ent.alpha = 0.30;
546 l->ent.skinnum = (colors >> ((rand() % 4)*8)) & 0xff;
547 l->ent.model = NULL;
548 l->ent.frame = 4;
549 l->endtime = cl.time + 100;
550 return;
551 }
552 }
553 }
554
555 //=============
556 //ROGUE
557 void CL_ParseSteam (void)
558 {
559 vec3_t pos, dir;
560 int id, i;
561 int r;
562 int cnt;
563 int color;
564 int magnitude;
565 cl_sustain_t *s, *free_sustain;
566
567 id = MSG_ReadShort (&net_message); // an id of -1 is an instant effect
568 if (id != -1) // sustains
569 {
570 // Com_Printf ("Sustain effect id %d\n", id);
571 free_sustain = NULL;
572 for (i=0, s=cl_sustains; i<MAX_SUSTAINS; i++, s++)
573 {
574 if (s->id == 0)
575 {
576 free_sustain = s;
577 break;
578 }
579 }
580 if (free_sustain)
581 {
582 s->id = id;
583 s->count = MSG_ReadByte (&net_message);
584 MSG_ReadPos (&net_message, s->org);
585 MSG_ReadDir (&net_message, s->dir);
586 r = MSG_ReadByte (&net_message);
587 s->color = r & 0xff;
588 s->magnitude = MSG_ReadShort (&net_message);
589 s->endtime = cl.time + MSG_ReadLong (&net_message);
590 s->think = CL_ParticleSteamEffect2;
591 s->thinkinterval = 100;
592 s->nextthink = cl.time;
593 }
594 else
595 {
596 // Com_Printf ("No free sustains!\n");
597 // FIXME - read the stuff anyway
598 cnt = MSG_ReadByte (&net_message);
599 MSG_ReadPos (&net_message, pos);
600 MSG_ReadDir (&net_message, dir);
601 r = MSG_ReadByte (&net_message);
602 magnitude = MSG_ReadShort (&net_message);
603 magnitude = MSG_ReadLong (&net_message); // really interval
604 }
605 }
606 else // instant
607 {
608 cnt = MSG_ReadByte (&net_message);
609 MSG_ReadPos (&net_message, pos);
610 MSG_ReadDir (&net_message, dir);
611 r = MSG_ReadByte (&net_message);
612 magnitude = MSG_ReadShort (&net_message);
613 color = r & 0xff;
614 CL_ParticleSteamEffect (pos, dir, color, cnt, magnitude);
615 // S_StartSound (pos, 0, 0, cl_sfx_lashit, 1, ATTN_NORM, 0);
616 }
617 }
618
619 void CL_ParseWidow (void)
620 {
621 vec3_t pos;
622 int id, i;
623 cl_sustain_t *s, *free_sustain;
624
625 id = MSG_ReadShort (&net_message);
626
627 free_sustain = NULL;
628 for (i=0, s=cl_sustains; i<MAX_SUSTAINS; i++, s++)
629 {
630 if (s->id == 0)
631 {
632 free_sustain = s;
633 break;
634 }
635 }
636 if (free_sustain)
637 {
638 s->id = id;
639 MSG_ReadPos (&net_message, s->org);
640 s->endtime = cl.time + 2100;
641 s->think = CL_Widowbeamout;
642 s->thinkinterval = 1;
643 s->nextthink = cl.time;
644 }
645 else // no free sustains
646 {
647 // FIXME - read the stuff anyway
648 MSG_ReadPos (&net_message, pos);
649 }
650 }
651
652 void CL_ParseNuke (void)
653 {
654 vec3_t pos;
655 int i;
656 cl_sustain_t *s, *free_sustain;
657
658 free_sustain = NULL;
659 for (i=0, s=cl_sustains; i<MAX_SUSTAINS; i++, s++)
660 {
661 if (s->id == 0)
662 {
663 free_sustain = s;
664 break;
665 }
666 }
667 if (free_sustain)
668 {
669 s->id = 21000;
670 MSG_ReadPos (&net_message, s->org);
671 s->endtime = cl.time + 1000;
672 s->think = CL_Nukeblast;
673 s->thinkinterval = 1;
674 s->nextthink = cl.time;
675 }
676 else // no free sustains
677 {
678 // FIXME - read the stuff anyway
679 MSG_ReadPos (&net_message, pos);
680 }
681 }
682
683 //ROGUE
684 //=============
685
686
687 /*
688 =================
689 CL_ParseTEnt
690 =================
691 */
692 static byte splash_color[] = {0x00, 0xe0, 0xb0, 0x50, 0xd0, 0xe0, 0xe8};
693
694 void CL_ParseTEnt (void)
695 {
696 int type;
697 vec3_t pos, pos2, dir;
698 explosion_t *ex;
699 int cnt;
700 int color;
701 int r;
702 int ent;
703 int magnitude;
704
705 type = MSG_ReadByte (&net_message);
706
707 switch (type)
708 {
709 case TE_BLOOD: // bullet hitting flesh
710 MSG_ReadPos (&net_message, pos);
711 MSG_ReadDir (&net_message, dir);
712 CL_ParticleEffect (pos, dir, 0xe8, 60);
713 break;
714
715 case TE_GUNSHOT: // bullet hitting wall
716 case TE_SPARKS:
717 case TE_BULLET_SPARKS:
718 MSG_ReadPos (&net_message, pos);
719 MSG_ReadDir (&net_message, dir);
720 if (type == TE_GUNSHOT)
721 CL_ParticleEffect (pos, dir, 0, 40);
722 else
723 CL_ParticleEffect (pos, dir, 0xe0, 6);
724
725 if (type != TE_SPARKS)
726 {
727 CL_SmokeAndFlash(pos);
728
729 // impact sound
730 cnt = rand()&15;
731 if (cnt == 1)
732 S_StartSound (pos, 0, 0, cl_sfx_ric1, 1, ATTN_NORM, 0);
733 else if (cnt == 2)
734 S_StartSound (pos, 0, 0, cl_sfx_ric2, 1, ATTN_NORM, 0);
735 else if (cnt == 3)
736 S_StartSound (pos, 0, 0, cl_sfx_ric3, 1, ATTN_NORM, 0);
737 }
738
739 break;
740
741 case TE_SCREEN_SPARKS:
742 case TE_SHIELD_SPARKS:
743 MSG_ReadPos (&net_message, pos);
744 MSG_ReadDir (&net_message, dir);
745 if (type == TE_SCREEN_SPARKS)
746 CL_ParticleEffect (pos, dir, 0xd0, 40);
747 else
748 CL_ParticleEffect (pos, dir, 0xb0, 40);
749 //FIXME : replace or remove this sound
750 S_StartSound (pos, 0, 0, cl_sfx_lashit, 1, ATTN_NORM, 0);
751 break;
752
753 case TE_SHOTGUN: // bullet hitting wall
754 MSG_ReadPos (&net_message, pos);
755 MSG_ReadDir (&net_message, dir);
756 CL_ParticleEffect (pos, dir, 0, 20);
757 CL_SmokeAndFlash(pos);
758 break;
759
760 case TE_SPLASH: // bullet hitting water
761 cnt = MSG_ReadByte (&net_message);
762 MSG_ReadPos (&net_message, pos);
763 MSG_ReadDir (&net_message, dir);
764 r = MSG_ReadByte (&net_message);
765 if (r > 6)
766 color = 0x00;
767 else
768 color = splash_color[r];
769 CL_ParticleEffect (pos, dir, color, cnt);
770
771 if (r == SPLASH_SPARKS)
772 {
773 r = rand() & 3;
774 if (r == 0)
775 S_StartSound (pos, 0, 0, cl_sfx_spark5, 1, ATTN_STATIC, 0);
776 else if (r == 1)
777 S_StartSound (pos, 0, 0, cl_sfx_spark6, 1, ATTN_STATIC, 0);
778 else
779 S_StartSound (pos, 0, 0, cl_sfx_spark7, 1, ATTN_STATIC, 0);
780 }
781 break;
782
783 case TE_LASER_SPARKS:
784 cnt = MSG_ReadByte (&net_message);
785 MSG_ReadPos (&net_message, pos);
786 MSG_ReadDir (&net_message, dir);
787 color = MSG_ReadByte (&net_message);
788 CL_ParticleEffect2 (pos, dir, color, cnt);
789 break;
790
791 // RAFAEL
792 case TE_BLUEHYPERBLASTER:
793 MSG_ReadPos (&net_message, pos);
794 MSG_ReadPos (&net_message, dir);
795 CL_BlasterParticles (pos, dir);
796 break;
797
798 case TE_BLASTER: // blaster hitting wall
799 MSG_ReadPos (&net_message, pos);
800 MSG_ReadDir (&net_message, dir);
801 CL_BlasterParticles (pos, dir);
802
803 ex = CL_AllocExplosion ();
804 VectorCopy (pos, ex->ent.origin);
805 ex->ent.angles[0] = acos(dir[2])/M_PI*180;
806 // PMM - fixed to correct for pitch of 0
807 if (dir[0])
808 ex->ent.angles[1] = atan2(dir[1], dir[0])/M_PI*180;
809 else if (dir[1] > 0)
810 ex->ent.angles[1] = 90;
811 else if (dir[1] < 0)
812 ex->ent.angles[1] = 270;
813 else
814 ex->ent.angles[1] = 0;
815
816 ex->type = ex_misc;
817 ex->ent.flags = RF_FULLBRIGHT|RF_TRANSLUCENT;
818 ex->start = cl.frame.servertime - 100;
819 ex->light = 150;
820 ex->lightcolor[0] = 1;
821 ex->lightcolor[1] = 1;
822 ex->ent.model = cl_mod_explode;
823 ex->frames = 4;
824 S_StartSound (pos, 0, 0, cl_sfx_lashit, 1, ATTN_NORM, 0);
825 break;
826
827 case TE_RAILTRAIL: // railgun effect
828 MSG_ReadPos (&net_message, pos);
829 MSG_ReadPos (&net_message, pos2);
830 CL_RailTrail (pos, pos2);
831 S_StartSound (pos2, 0, 0, cl_sfx_railg, 1, ATTN_NORM, 0);
832 break;
833
834 case TE_EXPLOSION2:
835 case TE_GRENADE_EXPLOSION:
836 case TE_GRENADE_EXPLOSION_WATER:
837 MSG_ReadPos (&net_message, pos);
838
839 ex = CL_AllocExplosion ();
840 VectorCopy (pos, ex->ent.origin);
841 ex->type = ex_poly;
842 ex->ent.flags = RF_FULLBRIGHT;
843 ex->start = cl.frame.servertime - 100;
844 ex->light = 350;
845 ex->lightcolor[0] = 1.0;
846 ex->lightcolor[1] = 0.5;
847 ex->lightcolor[2] = 0.5;
848 ex->ent.model = cl_mod_explo4;
849 ex->frames = 19;
850 ex->baseframe = 30;
851 ex->ent.angles[1] = rand() % 360;
852 CL_ExplosionParticles (pos);
853 if (type == TE_GRENADE_EXPLOSION_WATER)
854 S_StartSound (pos, 0, 0, cl_sfx_watrexp, 1, ATTN_NORM, 0);
855 else
856 S_StartSound (pos, 0, 0, cl_sfx_grenexp, 1, ATTN_NORM, 0);
857 break;
858
859 // RAFAEL
860 case TE_PLASMA_EXPLOSION:
861 MSG_ReadPos (&net_message, pos);
862 ex = CL_AllocExplosion ();
863 VectorCopy (pos, ex->ent.origin);
864 ex->type = ex_poly;
865 ex->ent.flags = RF_FULLBRIGHT;
866 ex->start = cl.frame.servertime - 100;
867 ex->light = 350;
868 ex->lightcolor[0] = 1.0;
869 ex->lightcolor[1] = 0.5;
870 ex->lightcolor[2] = 0.5;
871 ex->ent.angles[1] = rand() % 360;
872 ex->ent.model = cl_mod_explo4;
873 if (frand() < 0.5)
874 ex->baseframe = 15;
875 ex->frames = 15;
876 CL_ExplosionParticles (pos);
877 S_StartSound (pos, 0, 0, cl_sfx_rockexp, 1, ATTN_NORM, 0);
878 break;
879
880 case TE_EXPLOSION1:
881 case TE_EXPLOSION1_BIG: // PMM
882 case TE_ROCKET_EXPLOSION:
883 case TE_ROCKET_EXPLOSION_WATER:
884 case TE_EXPLOSION1_NP: // PMM
885 MSG_ReadPos (&net_message, pos);
886
887 ex = CL_AllocExplosion ();
888 VectorCopy (pos, ex->ent.origin);
889 ex->type = ex_poly;
890 ex->ent.flags = RF_FULLBRIGHT;
891 ex->start = cl.frame.servertime - 100;
892 ex->light = 350;
893 ex->lightcolor[0] = 1.0;
894 ex->lightcolor[1] = 0.5;
895 ex->lightcolor[2] = 0.5;
896 ex->ent.angles[1] = rand() % 360;
897 if (type != TE_EXPLOSION1_BIG) // PMM
898 ex->ent.model = cl_mod_explo4; // PMM
899 else
900 ex->ent.model = cl_mod_explo4_big;
901 if (frand() < 0.5)
902 ex->baseframe = 15;
903 ex->frames = 15;
904 if ((type != TE_EXPLOSION1_BIG) && (type != TE_EXPLOSION1_NP)) // PMM
905 CL_ExplosionParticles (pos); // PMM
906 if (type == TE_ROCKET_EXPLOSION_WATER)
907 S_StartSound (pos, 0, 0, cl_sfx_watrexp, 1, ATTN_NORM, 0);
908 else
909 S_StartSound (pos, 0, 0, cl_sfx_rockexp, 1, ATTN_NORM, 0);
910 break;
911
912 case TE_BFG_EXPLOSION:
913 MSG_ReadPos (&net_message, pos);
914 ex = CL_AllocExplosion ();
915 VectorCopy (pos, ex->ent.origin);
916 ex->type = ex_poly;
917 ex->ent.flags = RF_FULLBRIGHT;
918 ex->start = cl.frame.servertime - 100;
919 ex->light = 350;
920 ex->lightcolor[0] = 0.0;
921 ex->lightcolor[1] = 1.0;
922 ex->lightcolor[2] = 0.0;
923 ex->ent.model = cl_mod_bfg_explo;
924 ex->ent.flags |= RF_TRANSLUCENT;
925 ex->ent.alpha = 0.30;
926 ex->frames = 4;
927 break;
928
929 case TE_BFG_BIGEXPLOSION:
930 MSG_ReadPos (&net_message, pos);
931 CL_BFGExplosionParticles (pos);
932 break;
933
934 case TE_BFG_LASER:
935 CL_ParseLaser (0xd0d1d2d3);
936 break;
937
938 case TE_BUBBLETRAIL:
939 MSG_ReadPos (&net_message, pos);
940 MSG_ReadPos (&net_message, pos2);
941 CL_BubbleTrail (pos, pos2);
942 break;
943
944 case TE_PARASITE_ATTACK:
945 case TE_MEDIC_CABLE_ATTACK:
946 ent = CL_ParseBeam (cl_mod_parasite_segment);
947 break;
948
949 case TE_BOSSTPORT: // boss teleporting to station
950 MSG_ReadPos (&net_message, pos);
951 CL_BigTeleportParticles (pos);
952 S_StartSound (pos, 0, 0, S_RegisterSound ("misc/bigtele.wav"), 1, ATTN_NONE, 0);
953 break;
954
955 case TE_GRAPPLE_CABLE:
956 ent = CL_ParseBeam2 (cl_mod_grapple_cable);
957 break;
958
959 // RAFAEL
960 case TE_WELDING_SPARKS:
961 cnt = MSG_ReadByte (&net_message);
962 MSG_ReadPos (&net_message, pos);
963 MSG_ReadDir (&net_message, dir);
964 color = MSG_ReadByte (&net_message);
965 CL_ParticleEffect2 (pos, dir, color, cnt);
966
967 ex = CL_AllocExplosion ();
968 VectorCopy (pos, ex->ent.origin);
969 ex->type = ex_flash;
970 // note to self
971 // we need a better no draw flag
972 ex->ent.flags = RF_BEAM;
973 ex->start = cl.frame.servertime - 0.1;
974 ex->light = 100 + (rand()%75);
975 ex->lightcolor[0] = 1.0;
976 ex->lightcolor[1] = 1.0;
977 ex->lightcolor[2] = 0.3;
978 ex->ent.model = cl_mod_flash;
979 ex->frames = 2;
980 break;
981
982 case TE_GREENBLOOD:
983 MSG_ReadPos (&net_message, pos);
984 MSG_ReadDir (&net_message, dir);
985 CL_ParticleEffect2 (pos, dir, 0xdf, 30);
986 break;
987
988 // RAFAEL
989 case TE_TUNNEL_SPARKS:
990 cnt = MSG_ReadByte (&net_message);
991 MSG_ReadPos (&net_message, pos);
992 MSG_ReadDir (&net_message, dir);
993 color = MSG_ReadByte (&net_message);
994 CL_ParticleEffect3 (pos, dir, color, cnt);
995 break;
996
997 //=============
998 //PGM
999 // PMM -following code integrated for flechette (different color)
1000 case TE_BLASTER2: // green blaster hitting wall
1001 case TE_FLECHETTE: // flechette
1002 MSG_ReadPos (&net_message, pos);
1003 MSG_ReadDir (&net_message, dir);
1004
1005 // PMM
1006 if (type == TE_BLASTER2)
1007 CL_BlasterParticles2 (pos, dir, 0xd0);
1008 else
1009 CL_BlasterParticles2 (pos, dir, 0x6f); // 75
1010
1011 ex = CL_AllocExplosion ();
1012 VectorCopy (pos, ex->ent.origin);
1013 ex->ent.angles[0] = acos(dir[2])/M_PI*180;
1014 // PMM - fixed to correct for pitch of 0
1015 if (dir[0])
1016 ex->ent.angles[1] = atan2(dir[1], dir[0])/M_PI*180;
1017 else if (dir[1] > 0)
1018 ex->ent.angles[1] = 90;
1019 else if (dir[1] < 0)
1020 ex->ent.angles[1] = 270;
1021 else
1022 ex->ent.angles[1] = 0;
1023
1024 ex->type = ex_misc;
1025 ex->ent.flags = RF_FULLBRIGHT|RF_TRANSLUCENT;
1026
1027 // PMM
1028 if (type == TE_BLASTER2)
1029 ex->ent.skinnum = 1;
1030 else // flechette
1031 ex->ent.skinnum = 2;
1032
1033 ex->start = cl.frame.servertime - 100;
1034 ex->light = 150;
1035 // PMM
1036 if (type == TE_BLASTER2)
1037 ex->lightcolor[1] = 1;
1038 else // flechette
1039 {
1040 ex->lightcolor[0] = 0.19;
1041 ex->lightcolor[1] = 0.41;
1042 ex->lightcolor[2] = 0.75;
1043 }
1044 ex->ent.model = cl_mod_explode;
1045 ex->frames = 4;
1046 S_StartSound (pos, 0, 0, cl_sfx_lashit, 1, ATTN_NORM, 0);
1047 break;
1048
1049
1050 case TE_LIGHTNING:
1051 ent = CL_ParseLightning (cl_mod_lightning);
1052 S_StartSound (NULL, ent, CHAN_WEAPON, cl_sfx_lightning, 1, ATTN_NORM, 0);
1053 break;
1054
1055 case TE_DEBUGTRAIL:
1056 MSG_ReadPos (&net_message, pos);
1057 MSG_ReadPos (&net_message, pos2);
1058 CL_DebugTrail (pos, pos2);
1059 break;
1060
1061 case TE_PLAIN_EXPLOSION:
1062 MSG_ReadPos (&net_message, pos);
1063
1064 ex = CL_AllocExplosion ();
1065 VectorCopy (pos, ex->ent.origin);
1066 ex->type = ex_poly;
1067 ex->ent.flags = RF_FULLBRIGHT;
1068 ex->start = cl.frame.servertime - 100;
1069 ex->light = 350;
1070 ex->lightcolor[0] = 1.0;
1071 ex->lightcolor[1] = 0.5;
1072 ex->lightcolor[2] = 0.5;
1073 ex->ent.angles[1] = rand() % 360;
1074 ex->ent.model = cl_mod_explo4;
1075 if (frand() < 0.5)
1076 ex->baseframe = 15;
1077 ex->frames = 15;
1078 if (type == TE_ROCKET_EXPLOSION_WATER)
1079 S_StartSound (pos, 0, 0, cl_sfx_watrexp, 1, ATTN_NORM, 0);
1080 else
1081 S_StartSound (pos, 0, 0, cl_sfx_rockexp, 1, ATTN_NORM, 0);
1082 break;
1083
1084 case TE_FLASHLIGHT:
1085 MSG_ReadPos(&net_message, pos);
1086 ent = MSG_ReadShort(&net_message);
1087 CL_Flashlight(ent, pos);
1088 break;
1089
1090 case TE_FORCEWALL:
1091 MSG_ReadPos(&net_message, pos);
1092 MSG_ReadPos(&net_message, pos2);
1093 color = MSG_ReadByte (&net_message);
1094 CL_ForceWall(pos, pos2, color);
1095 break;
1096
1097 case TE_HEATBEAM:
1098 ent = CL_ParsePlayerBeam (cl_mod_heatbeam);
1099 break;
1100
1101 case TE_MONSTER_HEATBEAM:
1102 ent = CL_ParsePlayerBeam (cl_mod_monster_heatbeam);
1103 break;
1104
1105 case TE_HEATBEAM_SPARKS:
1106 // cnt = MSG_ReadByte (&net_message);
1107 cnt = 50;
1108 MSG_ReadPos (&net_message, pos);
1109 MSG_ReadDir (&net_message, dir);
1110 // r = MSG_ReadByte (&net_message);
1111 // magnitude = MSG_ReadShort (&net_message);
1112 r = 8;
1113 magnitude = 60;
1114 color = r & 0xff;
1115 CL_ParticleSteamEffect (pos, dir, color, cnt, magnitude);
1116 S_StartSound (pos, 0, 0, cl_sfx_lashit, 1, ATTN_NORM, 0);
1117 break;
1118
1119 case TE_HEATBEAM_STEAM:
1120 // cnt = MSG_ReadByte (&net_message);
1121 cnt = 20;
1122 MSG_ReadPos (&net_message, pos);
1123 MSG_ReadDir (&net_message, dir);
1124 // r = MSG_ReadByte (&net_message);
1125 // magnitude = MSG_ReadShort (&net_message);
1126 // color = r & 0xff;
1127 color = 0xe0;
1128 magnitude = 60;
1129 CL_ParticleSteamEffect (pos, dir, color, cnt, magnitude);
1130 S_StartSound (pos, 0, 0, cl_sfx_lashit, 1, ATTN_NORM, 0);
1131 break;
1132
1133 case TE_STEAM:
1134 CL_ParseSteam();
1135 break;
1136
1137 case TE_BUBBLETRAIL2:
1138 // cnt = MSG_ReadByte (&net_message);
1139 cnt = 8;
1140 MSG_ReadPos (&net_message, pos);
1141 MSG_ReadPos (&net_message, pos2);
1142 CL_BubbleTrail2 (pos, pos2, cnt);
1143 S_StartSound (pos, 0, 0, cl_sfx_lashit, 1, ATTN_NORM, 0);
1144 break;
1145
1146 case TE_MOREBLOOD:
1147 MSG_ReadPos (&net_message, pos);
1148 MSG_ReadDir (&net_message, dir);
1149 CL_ParticleEffect (pos, dir, 0xe8, 250);
1150 break;
1151
1152 case TE_CHAINFIST_SMOKE:
1153 dir[0]=0; dir[1]=0; dir[2]=1;
1154 MSG_ReadPos(&net_message, pos);
1155 CL_ParticleSmokeEffect (pos, dir, 0, 20, 20);
1156 break;
1157
1158 case TE_ELECTRIC_SPARKS:
1159 MSG_ReadPos (&net_message, pos);
1160 MSG_ReadDir (&net_message, dir);
1161 // CL_ParticleEffect (pos, dir, 109, 40);
1162 CL_ParticleEffect (pos, dir, 0x75, 40);
1163 //FIXME : replace or remove this sound
1164 S_StartSound (pos, 0, 0, cl_sfx_lashit, 1, ATTN_NORM, 0);
1165 break;
1166
1167 case TE_TRACKER_EXPLOSION:
1168 MSG_ReadPos (&net_message, pos);
1169 CL_ColorFlash (pos, 0, 150, -1, -1, -1);
1170 CL_ColorExplosionParticles (pos, 0, 1);
1171 // CL_Tracker_Explode (pos);
1172 S_StartSound (pos, 0, 0, cl_sfx_disrexp, 1, ATTN_NORM, 0);
1173 break;
1174
1175 case TE_TELEPORT_EFFECT:
1176 case TE_DBALL_GOAL:
1177 MSG_ReadPos (&net_message, pos);
1178 CL_TeleportParticles (pos);
1179 break;
1180
1181 case TE_WIDOWBEAMOUT:
1182 CL_ParseWidow ();
1183 break;
1184
1185 case TE_NUKEBLAST:
1186 CL_ParseNuke ();
1187 break;
1188
1189 case TE_WIDOWSPLASH:
1190 MSG_ReadPos (&net_message, pos);
1191 CL_WidowSplash (pos);
1192 break;
1193 //PGM
1194 //==============
1195
1196 default:
1197 Com_Error (ERR_DROP, "CL_ParseTEnt: bad type");
1198 }
1199 }
1200
1201 /*
1202 =================
1203 CL_AddBeams
1204 =================
1205 */
1206 void CL_AddBeams (void)
1207 {
1208 int i,j;
1209 beam_t *b;
1210 vec3_t dist, org;
1211 float d;
1212 entity_t ent;
1213 float yaw, pitch;
1214 float forward;
1215 float len, steps;
1216 float model_length;
1217
1218 // update beams
1219 for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++)
1220 {
1221 if (!b->model || b->endtime < cl.time)
1222 continue;
1223
1224 // if coming from the player, update the start position
1225 if (b->entity == cl.playernum+1) // entity 0 is the world
1226 {
1227 VectorCopy (cl.refdef.vieworg, b->start);
1228 b->start[2] -= 22; // adjust for view height
1229 }
1230 VectorAdd (b->start, b->offset, org);
1231
1232 // calculate pitch and yaw
1233 VectorSubtract (b->end, org, dist);
1234
1235 if (dist[1] == 0 && dist[0] == 0)
1236 {
1237 yaw = 0;
1238 if (dist[2] > 0)
1239 pitch = 90;
1240 else
1241 pitch = 270;
1242 }
1243 else
1244 {
1245 // PMM - fixed to correct for pitch of 0
1246 if (dist[0])
1247 yaw = (atan2(dist[1], dist[0]) * 180 / M_PI);
1248 else if (dist[1] > 0)
1249 yaw = 90;
1250 else
1251 yaw = 270;
1252 if (yaw < 0)
1253 yaw += 360;
1254
1255 forward = sqrt (dist[0]*dist[0] + dist[1]*dist[1]);
1256 pitch = (atan2(dist[2], forward) * -180.0 / M_PI);
1257 if (pitch < 0)
1258 pitch += 360.0;
1259 }
1260
1261 // add new entities for the beams
1262 d = VectorNormalize(dist);
1263
1264 memset (&ent, 0, sizeof(ent));
1265 if (b->model == cl_mod_lightning)
1266 {
1267 model_length = 35.0;
1268 d-= 20.0; // correction so it doesn't end in middle of tesla
1269 }
1270 else
1271 {
1272 model_length = 30.0;
1273 }
1274 steps = ceil(d/model_length);
1275 len = (d-model_length)/(steps-1);
1276
1277 // PMM - special case for lightning model .. if the real length is shorter than the model,
1278 // flip it around & draw it from the end to the start. This prevents the model from going
1279 // through the tesla mine (instead it goes through the target)
1280 if ((b->model == cl_mod_lightning) && (d <= model_length))
1281 {
1282 // Com_Printf ("special case\n");
1283 VectorCopy (b->end, ent.origin);
1284 // offset to push beam outside of tesla model (negative because dist is from end to start
1285 // for this beam)
1286 // for (j=0 ; j<3 ; j++)
1287 // ent.origin[j] -= dist[j]*10.0;
1288 ent.model = b->model;
1289 ent.flags = RF_FULLBRIGHT;
1290 ent.angles[0] = pitch;
1291 ent.angles[1] = yaw;
1292 ent.angles[2] = rand()%360;
1293 V_AddEntity (&ent);
1294 return;
1295 }
1296 while (d > 0)
1297 {
1298 VectorCopy (org, ent.origin);
1299 ent.model = b->model;
1300 if (b->model == cl_mod_lightning)
1301 {
1302 ent.flags = RF_FULLBRIGHT;
1303 ent.angles[0] = -pitch;
1304 ent.angles[1] = yaw + 180.0;
1305 ent.angles[2] = rand()%360;
1306 }
1307 else
1308 {
1309 ent.angles[0] = pitch;
1310 ent.angles[1] = yaw;
1311 ent.angles[2] = rand()%360;
1312 }
1313
1314 // Com_Printf("B: %d -> %d\n", b->entity, b->dest_entity);
1315 V_AddEntity (&ent);
1316
1317 for (j=0 ; j<3 ; j++)
1318 org[j] += dist[j]*len;
1319 d -= model_length;
1320 }
1321 }
1322 }
1323
1324
1325 /*
1326 // Com_Printf ("Endpoint: %f %f %f\n", b->end[0], b->end[1], b->end[2]);
1327 // Com_Printf ("Pred View Angles: %f %f %f\n", cl.predicted_angles[0], cl.predicted_angles[1], cl.predicted_angles[2]);
1328 // Com_Printf ("Act View Angles: %f %f %f\n", cl.refdef.viewangles[0], cl.refdef.viewangles[1], cl.refdef.viewangles[2]);
1329 // VectorCopy (cl.predicted_origin, b->start);
1330 // b->start[2] += 22; // adjust for view height
1331 // if (fabs(cl.refdef.vieworg[2] - b->start[2]) >= 10) {
1332 // b->start[2] = cl.refdef.vieworg[2];
1333 // }
1334
1335 // Com_Printf ("Time: %d %d %f\n", cl.time, cls.realtime, cls.frametime);
1336 */
1337
1338 extern cvar_t *hand;
1339
1340 /*
1341 =================
1342 ROGUE - draw player locked beams
1343 CL_AddPlayerBeams
1344 =================
1345 */
1346 void CL_AddPlayerBeams (void)
1347 {
1348 int i,j;
1349 beam_t *b;
1350 vec3_t dist, org;
1351 float d;
1352 entity_t ent;
1353 float yaw, pitch;
1354 float forward;
1355 float len, steps;
1356 int framenum;
1357 float model_length;
1358
1359 float hand_multiplier;
1360 frame_t *oldframe;
1361 player_state_t *ps, *ops;
1362
1363 //PMM
1364 if (hand)
1365 {
1366 if (hand->value == 2)
1367 hand_multiplier = 0;
1368 else if (hand->value == 1)
1369 hand_multiplier = -1;
1370 else
1371 hand_multiplier = 1;
1372 }
1373 else
1374 {
1375 hand_multiplier = 1;
1376 }
1377 //PMM
1378
1379 // update beams
1380 for (i=0, b=cl_playerbeams ; i< MAX_BEAMS ; i++, b++)
1381 {
1382 vec3_t f,r,u;
1383 if (!b->model || b->endtime < cl.time)
1384 continue;
1385
1386 if(cl_mod_heatbeam && (b->model == cl_mod_heatbeam))
1387 {
1388
1389 // if coming from the player, update the start position
1390 if (b->entity == cl.playernum+1) // entity 0 is the world
1391 {
1392 // set up gun position
1393 // code straight out of CL_AddViewWeapon
1394 ps = &cl.frame.playerstate;
1395 j = (cl.frame.serverframe - 1) & UPDATE_MASK;
1396 oldframe = &cl.frames[j];
1397 if (oldframe->serverframe != cl.frame.serverframe-1 || !oldframe->valid)
1398 oldframe = &cl.frame; // previous frame was dropped or involid
1399 ops = &oldframe->playerstate;
1400 for (j=0 ; j<3 ; j++)
1401 {
1402 b->start[j] = cl.refdef.vieworg[j] + ops->gunoffset[j]
1403 + cl.lerpfrac * (ps->gunoffset[j] - ops->gunoffset[j]);
1404 }
1405 VectorMA (b->start, (hand_multiplier * b->offset[0]), cl.v_right, org);
1406 VectorMA ( org, b->offset[1], cl.v_forward, org);
1407 VectorMA ( org, b->offset[2], cl.v_up, org);
1408 if ((hand) && (hand->value == 2)) {
1409 VectorMA (org, -1, cl.v_up, org);
1410 }
1411 // FIXME - take these out when final
1412 VectorCopy (cl.v_right, r);
1413 VectorCopy (cl.v_forward, f);
1414 VectorCopy (cl.v_up, u);
1415
1416 }
1417 else
1418 VectorCopy (b->start, org);
1419 }
1420 else
1421 {
1422 // if coming from the player, update the start position
1423 if (b->entity == cl.playernum+1) // entity 0 is the world
1424 {
1425 VectorCopy (cl.refdef.vieworg, b->start);
1426 b->start[2] -= 22; // adjust for view height
1427 }
1428 VectorAdd (b->start, b->offset, org);
1429 }
1430
1431 // calculate pitch and yaw
1432 VectorSubtract (b->end, org, dist);
1433
1434 //PMM
1435 if(cl_mod_heatbeam && (b->model == cl_mod_heatbeam) && (b->entity == cl.playernum+1))
1436 {
1437 vec_t len;
1438
1439 len = VectorLength (dist);
1440 VectorScale (f, len, dist);
1441 VectorMA (dist, (hand_multiplier * b->offset[0]), r, dist);
1442 VectorMA (dist, b->offset[1], f, dist);
1443 VectorMA (dist, b->offset[2], u, dist);
1444 if ((hand) && (hand->value == 2)) {
1445 VectorMA (org, -1, cl.v_up, org);
1446 }
1447 }
1448 //PMM
1449
1450 if (dist[1] == 0 && dist[0] == 0)
1451 {
1452 yaw = 0;
1453 if (dist[2] > 0)
1454 pitch = 90;
1455 else
1456 pitch = 270;
1457 }
1458 else
1459 {
1460 // PMM - fixed to correct for pitch of 0
1461 if (dist[0])
1462 yaw = (atan2(dist[1], dist[0]) * 180 / M_PI);
1463 else if (dist[1] > 0)
1464 yaw = 90;
1465 else
1466 yaw = 270;
1467 if (yaw < 0)
1468 yaw += 360;
1469
1470 forward = sqrt (dist[0]*dist[0] + dist[1]*dist[1]);
1471 pitch = (atan2(dist[2], forward) * -180.0 / M_PI);
1472 if (pitch < 0)
1473 pitch += 360.0;
1474 }
1475
1476 if (cl_mod_heatbeam && (b->model == cl_mod_heatbeam))
1477 {
1478 if (b->entity != cl.playernum+1)
1479 {
1480 framenum = 2;
1481 // Com_Printf ("Third person\n");
1482 ent.angles[0] = -pitch;
1483 ent.angles[1] = yaw + 180.0;
1484 ent.angles[2] = 0;
1485 // Com_Printf ("%f %f - %f %f %f\n", -pitch, yaw+180.0, b->offset[0], b->offset[1], b->offset[2]);
1486 AngleVectors(ent.angles, f, r, u);
1487
1488 // if it's a non-origin offset, it's a player, so use the hardcoded player offset
1489 if (!VectorCompare (b->offset, vec3_origin))
1490 {
1491 VectorMA (org, -(b->offset[0])+1, r, org);
1492 VectorMA (org, -(b->offset[1]), f, org);
1493 VectorMA (org, -(b->offset[2])-10, u, org);
1494 }
1495 else
1496 {
1497 // if it's a monster, do the particle effect
1498 CL_MonsterPlasma_Shell(b->start);
1499 }
1500 }
1501 else
1502 {
1503 framenum = 1;
1504 }
1505 }
1506
1507 // if it's the heatbeam, draw the particle effect
1508 if ((cl_mod_heatbeam && (b->model == cl_mod_heatbeam) && (b->entity == cl.playernum+1)))
1509 {
1510 CL_Heatbeam (org, dist);
1511 }
1512
1513 // add new entities for the beams
1514 d = VectorNormalize(dist);
1515
1516 memset (&ent, 0, sizeof(ent));
1517 if (b->model == cl_mod_heatbeam)
1518 {
1519 model_length = 32.0;
1520 }
1521 else if (b->model == cl_mod_lightning)
1522 {
1523 model_length = 35.0;
1524 d-= 20.0; // correction so it doesn't end in middle of tesla
1525 }
1526 else
1527 {
1528 model_length = 30.0;
1529 }
1530 steps = ceil(d/model_length);
1531 len = (d-model_length)/(steps-1);
1532
1533 // PMM - special case for lightning model .. if the real length is shorter than the model,
1534 // flip it around & draw it from the end to the start. This prevents the model from going
1535 // through the tesla mine (instead it goes through the target)
1536 if ((b->model == cl_mod_lightning) && (d <= model_length))
1537 {
1538 // Com_Printf ("special case\n");
1539 VectorCopy (b->end, ent.origin);
1540 // offset to push beam outside of tesla model (negative because dist is from end to start
1541 // for this beam)
1542 // for (j=0 ; j<3 ; j++)
1543 // ent.origin[j] -= dist[j]*10.0;
1544 ent.model = b->model;
1545 ent.flags = RF_FULLBRIGHT;
1546 ent.angles[0] = pitch;
1547 ent.angles[1] = yaw;
1548 ent.angles[2] = rand()%360;
1549 V_AddEntity (&ent);
1550 return;
1551 }
1552 while (d > 0)
1553 {
1554 VectorCopy (org, ent.origin);
1555 ent.model = b->model;
1556 if(cl_mod_heatbeam && (b->model == cl_mod_heatbeam))
1557 {
1558 // ent.flags = RF_FULLBRIGHT|RF_TRANSLUCENT;
1559 // ent.alpha = 0.3;
1560 ent.flags = RF_FULLBRIGHT;
1561 ent.angles[0] = -pitch;
1562 ent.angles[1] = yaw + 180.0;
1563 ent.angles[2] = (cl.time) % 360;
1564 // ent.angles[2] = rand()%360;
1565 ent.frame = framenum;
1566 }
1567 else if (b->model == cl_mod_lightning)
1568 {
1569 ent.flags = RF_FULLBRIGHT;
1570 ent.angles[0] = -pitch;
1571 ent.angles[1] = yaw + 180.0;
1572 ent.angles[2] = rand()%360;
1573 }
1574 else
1575 {
1576 ent.angles[0] = pitch;
1577 ent.angles[1] = yaw;
1578 ent.angles[2] = rand()%360;
1579 }
1580
1581 // Com_Printf("B: %d -> %d\n", b->entity, b->dest_entity);
1582 V_AddEntity (&ent);
1583
1584 for (j=0 ; j<3 ; j++)
1585 org[j] += dist[j]*len;
1586 d -= model_length;
1587 }
1588 }
1589 }
1590
1591 /*
1592 =================
1593 CL_AddExplosions
1594 =================
1595 */
1596 void CL_AddExplosions (void)
1597 {
1598 entity_t *ent;
1599 int i;
1600 explosion_t *ex;
1601 float frac;
1602 int f;
1603
1604 memset (&ent, 0, sizeof(ent));
1605
1606 for (i=0, ex=cl_explosions ; i< MAX_EXPLOSIONS ; i++, ex++)
1607 {
1608 if (ex->type == ex_free)
1609 continue;
1610 frac = (cl.time - ex->start)/100.0;
1611 f = floor(frac);
1612
1613 ent = &ex->ent;
1614
1615 switch (ex->type)
1616 {
1617 case ex_mflash:
1618 if (f >= ex->frames-1)
1619 ex->type = ex_free;
1620 break;
1621 case ex_misc:
1622 if (f >= ex->frames-1)
1623 {
1624 ex->type = ex_free;
1625 break;
1626 }
1627 ent->alpha = 1.0 - frac/(ex->frames-1);
1628 break;
1629 case ex_flash:
1630 if (f >= 1)
1631 {
1632 ex->type = ex_free;
1633 break;
1634 }
1635 ent->alpha = 1.0;
1636 break;
1637 case ex_poly:
1638 if (f >= ex->frames-1)
1639 {
1640 ex->type = ex_free;
1641 break;
1642 }
1643
1644 ent->alpha = (16.0 - (float)f)/16.0;
1645
1646 if (f < 10)
1647 {
1648 ent->skinnum = (f>>1);
1649 if (ent->skinnum < 0)
1650 ent->skinnum = 0;
1651 }
1652 else
1653 {
1654 ent->flags |= RF_TRANSLUCENT;
1655 if (f < 13)
1656 ent->skinnum = 5;
1657 else
1658 ent->skinnum = 6;
1659 }
1660 break;
1661 case ex_poly2:
1662 if (f >= ex->frames-1)
1663 {
1664 ex->type = ex_free;
1665 break;
1666 }
1667
1668 ent->alpha = (5.0 - (float)f)/5.0;
1669 ent->skinnum = 0;
1670 ent->flags |= RF_TRANSLUCENT;
1671 break;
1672 }
1673
1674 if (ex->type == ex_free)
1675 continue;
1676 if (ex->light)
1677 {
1678 V_AddLight (ent->origin, ex->light*ent->alpha,
1679 ex->lightcolor[0], ex->lightcolor[1], ex->lightcolor[2]);
1680 }
1681
1682 VectorCopy (ent->origin, ent->oldorigin);
1683
1684 if (f < 0)
1685 f = 0;
1686 ent->frame = ex->baseframe + f + 1;
1687 ent->oldframe = ex->baseframe + f;
1688 ent->backlerp = 1.0 - cl.lerpfrac;
1689
1690 V_AddEntity (ent);
1691 }
1692 }
1693
1694
1695 /*
1696 =================
1697 CL_AddLasers
1698 =================
1699 */
1700 void CL_AddLasers (void)
1701 {
1702 laser_t *l;
1703 int i;
1704
1705 for (i=0, l=cl_lasers ; i< MAX_LASERS ; i++, l++)
1706 {
1707 if (l->endtime >= cl.time)
1708 V_AddEntity (&l->ent);
1709 }
1710 }
1711
1712 /* PMM - CL_Sustains */
1713 void CL_ProcessSustain ()
1714 {
1715 cl_sustain_t *s;
1716 int i;
1717
1718 for (i=0, s=cl_sustains; i< MAX_SUSTAINS; i++, s++)
1719 {
1720 if (s->id)
1721 if ((s->endtime >= cl.time) && (cl.time >= s->nextthink))
1722 {
1723 // Com_Printf ("think %d %d %d\n", cl.time, s->nextthink, s->thinkinterval);
1724 s->think (s);
1725 }
1726 else if (s->endtime < cl.time)
1727 s->id = 0;
1728 }
1729 }
1730
1731 /*
1732 =================
1733 CL_AddTEnts
1734 =================
1735 */
1736 void CL_AddTEnts (void)
1737 {
1738 CL_AddBeams ();
1739 // PMM - draw plasma beams
1740 CL_AddPlayerBeams ();
1741 CL_AddExplosions ();
1742 CL_AddLasers ();
1743 // PMM - set up sustain
1744 CL_ProcessSustain();
1745 }
1746