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 //
21 // cg_entities.c
22 //
23
24 #include "cg_local.h"
25
26 cgEntity_t cg_entityList[MAX_CS_EDICTS];
27 entityState_t cg_parseEntities[MAX_PARSE_ENTITIES];
28
29 static qBool cg_inFrameSequence = qFalse;
30
31 /*
32 ==========================================================================
33
34 ENTITY STATE
35
36 ==========================================================================
37 */
38
39 /*
40 ==================
41 CG_BeginFrameSequence
42 ==================
43 */
CG_BeginFrameSequence(frame_t frame)44 void CG_BeginFrameSequence (frame_t frame)
45 {
46 if (cg_inFrameSequence) {
47 Com_Error (ERR_DROP, "CG_BeginFrameSequence: already in sequence");
48 return;
49 }
50
51 cg.oldFrame = cg.frame;
52 cg.frame = frame;
53
54 cg_inFrameSequence = qTrue;
55 }
56
57
58 /*
59 ==================
60 CG_NewPacketEntityState
61 ==================
62 */
CG_NewPacketEntityState(int entNum,entityState_t state)63 void CG_NewPacketEntityState (int entNum, entityState_t state)
64 {
65 cgEntity_t *ent;
66
67 if (!cg_inFrameSequence)
68 Com_Error (ERR_DROP, "CG_NewPacketEntityState: no sequence");
69
70 ent = &cg_entityList[entNum];
71 cg_parseEntities[(cg.frame.parseEntities+cg.frame.numEntities) & (MAX_PARSEENTITIES_MASK)] = state;
72 cg.frame.numEntities++;
73
74 // Some data changes will force no lerping
75 if (state.modelIndex != ent->current.modelIndex
76 || state.modelIndex2 != ent->current.modelIndex2
77 || state.modelIndex3 != ent->current.modelIndex3
78 || state.modelIndex4 != ent->current.modelIndex4
79 || abs ((int)state.origin[0] - (int)ent->current.origin[0]) > 512
80 || abs ((int)state.origin[1] - (int)ent->current.origin[1]) > 512
81 || abs ((int)state.origin[2] - (int)ent->current.origin[2]) > 512
82 || state.event == EV_PLAYER_TELEPORT
83 || state.event == EV_OTHER_TELEPORT)
84 ent->serverFrame = -99;
85
86 if (ent->serverFrame != cg.frame.serverFrame - 1) {
87 // Wasn't in last update, so initialize some things duplicate
88 // the current state so lerping doesn't hurt anything
89 ent->prev = state;
90 if (state.event == EV_OTHER_TELEPORT) {
91 Vec3Copy (state.origin, ent->prev.origin);
92 Vec3Copy (state.origin, ent->lerpOrigin);
93 }
94 else {
95 Vec3Copy (state.oldOrigin, ent->prev.origin);
96 Vec3Copy (state.oldOrigin, ent->lerpOrigin);
97 }
98 }
99 else {
100 // Shuffle the last state to previous
101 ent->prev = ent->current;
102 }
103
104 ent->serverFrame = cg.frame.serverFrame;
105 ent->current = state;
106 }
107
108
109 /*
110 ==============
111 CG_EntityEvent
112
113 An entity has just been parsed that has an event value
114 the female events are there for backwards compatability
115 ==============
116 */
117 // this here is ugly and hacky, will be scripted soon
118 enum {
119 SURF_NORMAL = 1 << 10, // 0x400
120
121 SURF_CONCRETE = 1 << 11, // 0x800
122 SURF_DIRT = 1 << 12, // 0x1000
123 SURF_DUCT = 1 << 13, // 0x2000
124 SURF_GRASS = 1 << 14, // 0x4000
125 SURF_GRAVEL = 1 << 15, // 0x8000
126 SURF_METAL = 1 << 16, // 0x10000
127 SURF_METALGRATE = 1 << 17, // 0x20000
128 SURF_METALLADDER = 1 << 18, // 0x40000
129 SURF_MUD = 1 << 19, // 0x80000
130 SURF_SAND = 1 << 20, // 0x100000
131 SURF_SLOSH = 1 << 21, // 0x200000
132 SURF_SNOW = 1 << 22, // 0x400000
133 SURF_TILE = 1 << 23, // 0x800000
134 SURF_WADE = 1 << 24, // 0x1000000
135 SURF_WOOD = 1 << 25, // 0x2000000
136 SURF_WOODPANEL = 1 << 26 // 0x4000000
137 // 0x8000000
138 // 0x10000000
139 // 0x20000000
140 // 0x40000000
141 // 0x80000000
142 };
143 #define SURF_MAXFLAGS (SURF_NORMAL-1)
144
145 enum {
146 STEP_NORMAL,
147
148 STEP_CONCRETE,
149 STEP_DIRT,
150 STEP_DUCT,
151 STEP_GRASS,
152 STEP_GRAVEL,
153 STEP_METAL,
154 STEP_METALGRATE,
155 STEP_METALLADDER,
156 STEP_MUD,
157 STEP_SAND,
158 STEP_SLOSH,
159 STEP_SNOW,
160 STEP_TILE,
161 STEP_WADE,
162 STEP_WOOD,
163 STEP_WOODPANEL
164 };
165
CG_StepTypeForTexture(cBspSurface_t * surf)166 int CG_StepTypeForTexture (cBspSurface_t *surf)
167 {
168 // int surfflags;
169 char newName[16];
170 int type;
171
172 // some maps have UPPERCASE TEXTURE NAMES
173 strcpy (newName, surf->name);
174 Q_strlwr (newName);
175
176 // this will be done after map load
177 // surfflags = surf->flags;
178 // if (surfflags > SURF_MAXFLAGS)
179 // surfflags = SURF_MAXFLAGS;
180
181 if (strstr (newName, "/dirt")) {
182 type = STEP_DIRT;
183 }
184 else if (strstr (newName, "/mud")) {
185 type = STEP_MUD;
186 }
187 else if (strstr (newName, "/cindr5_2")) {
188 type = STEP_CONCRETE;
189 }
190 else if (strstr (newName, "/grass")) {
191 type = STEP_GRASS;
192 }
193 else if (strstr (newName, "/c_met")
194 || strstr (newName, "/florr")
195 || strstr (newName, "/stairs")
196 || strstr (newName, "/rmetal")
197 || strstr (newName, "/blum")
198 || strstr (newName, "/metal")
199 || strstr (newName, "/floor3_1")
200 || strstr (newName, "/floor3_2")
201 || strstr (newName, "/floor3_3")
202 || strstr (newName, "/bflor3_1")
203 || strstr (newName, "/bflor3_2")
204 || strstr (newName, "/grate")
205 || strstr (newName, "/grnx")
206 || strstr (newName, "/grill")) {
207 type = STEP_METAL;
208 }
209 else if (strstr (newName, "/rock")
210 || strstr (newName, "/rrock")) {
211 type = STEP_GRAVEL;
212 }
213 else if (strstr (newName, "/airduc")) {
214 type = STEP_DUCT;
215 }
216 else {
217 // Com_Printf (0, "normal: ");
218 type = STEP_NORMAL;
219 }
220 //Com_Printf (0, "%s\n", newName);
221
222 // surfflags |= type;
223
224 // strip the type out of the flags
225 // type = surfflags &~ SURF_MAXFLAGS;
226
227 return type;
228 }
229
CG_FootStep(entityState_t * ent)230 static void CG_FootStep (entityState_t *ent)
231 {
232 trace_t tr;
233 vec3_t end;
234 int stepType;
235 struct sfx_s *sound;
236
237 Vec3Set (end, ent->origin[0], ent->origin[1], ent->origin[2]-64);
238 CG_PMTrace (&tr, ent->origin, NULL, NULL, end, qFalse);
239
240 if (!tr.surface || !tr.surface->name || !tr.surface->name[0]) {
241 sound = cgMedia.sfx.steps.standard[rand () & 3];
242 }
243 else {
244 stepType = CG_StepTypeForTexture (tr.surface);
245
246 switch (stepType) {
247 case STEP_CONCRETE: sound = cgMedia.sfx.steps.concrete[rand () & 3]; break;
248 case STEP_DIRT: sound = cgMedia.sfx.steps.dirt[rand () & 3]; break;
249 case STEP_DUCT: sound = cgMedia.sfx.steps.duct[rand () & 3]; break;
250 case STEP_GRASS: sound = cgMedia.sfx.steps.grass[rand () & 3]; break;
251 case STEP_GRAVEL: sound = cgMedia.sfx.steps.gravel[rand () & 3]; break;
252 case STEP_METAL: sound = cgMedia.sfx.steps.metal[rand () & 3]; break;
253 case STEP_METALGRATE: sound = cgMedia.sfx.steps.metalGrate[rand () & 3]; break;
254 case STEP_METALLADDER: sound = cgMedia.sfx.steps.metalLadder[rand () & 3]; break;
255 case STEP_MUD: sound = cgMedia.sfx.steps.mud[rand () & 3]; break;
256 case STEP_SAND: sound = cgMedia.sfx.steps.sand[rand () & 3]; break;
257 case STEP_SLOSH: sound = cgMedia.sfx.steps.slosh[rand () & 3]; break;
258 case STEP_SNOW: sound = cgMedia.sfx.steps.snow[rand () % 6]; break;
259 case STEP_TILE: sound = cgMedia.sfx.steps.tile[rand () & 3]; break;
260 case STEP_WADE: sound = cgMedia.sfx.steps.wade[rand () & 3]; break;
261 case STEP_WOOD: sound = cgMedia.sfx.steps.wood[rand () & 3]; break;
262 case STEP_WOODPANEL: sound = cgMedia.sfx.steps.woodPanel[rand () & 3]; break;
263
264 default:
265 case STEP_NORMAL:
266 sound = cgMedia.sfx.steps.standard[rand () & 3];
267 break;
268 }
269 }
270
271 cgi.Snd_StartSound (NULL, ent->number, CHAN_BODY, sound, 1.0f, ATTN_NORM, 0);
272 }
CG_EntityEvent(entityState_t * ent)273 static void CG_EntityEvent (entityState_t *ent)
274 {
275 switch (ent->event) {
276 case EV_ITEM_RESPAWN:
277 cgi.Snd_StartSound (NULL, ent->number, CHAN_WEAPON, cgMedia.sfx.itemRespawn, 1, ATTN_IDLE, 0);
278 CG_ItemRespawnEffect (ent->origin);
279 break;
280
281 case EV_FOOTSTEP:
282 if (cl_footsteps->intVal)
283 CG_FootStep (ent);
284 break;
285
286 case EV_FALL:
287 cgi.Snd_StartSound (NULL, ent->number, CHAN_AUTO, cgMedia.sfx.playerFall, 1, ATTN_NORM, 0);
288 break;
289
290 case EV_FALLSHORT:
291 cgi.Snd_StartSound (NULL, ent->number, CHAN_AUTO, cgMedia.sfx.playerFallShort, 1, ATTN_NORM, 0);
292 break;
293
294 case EV_FALLFAR:
295 cgi.Snd_StartSound (NULL, ent->number, CHAN_AUTO, cgMedia.sfx.playerFallFar, 1, ATTN_NORM, 0);
296 break;
297
298 case EV_PLAYER_TELEPORT:
299 cgi.Snd_StartSound (NULL, ent->number, CHAN_WEAPON, cgMedia.sfx.playerTeleport, 1, ATTN_IDLE, 0);
300 CG_TeleportParticles (ent->origin);
301 break;
302
303 case EV_NONE:
304 default:
305 break;
306 }
307 }
308
309
310 /*
311 ==================
312 CG_FireEntityEvents
313 ==================
314 */
CG_FireEntityEvents(void)315 static void CG_FireEntityEvents (void)
316 {
317 entityState_t *state;
318 int pNum;
319
320 for (pNum=0 ; pNum<cg.frame.numEntities ; pNum++) {
321 state = &cg_parseEntities[(cg.frame.parseEntities+pNum)&(MAX_PARSEENTITIES_MASK)];
322 if (state->event)
323 CG_EntityEvent (state);
324 }
325 }
326
327
328 /*
329 ==================
330 CG_EndFrameSequence
331 ==================
332 */
CG_EndFrameSequence(int numEntities)333 void CG_EndFrameSequence (int numEntities)
334 {
335 if (!cg_inFrameSequence) {
336 Com_Error (ERR_DROP, "CG_EndFrameSequence: no sequence started");
337 return;
338 }
339
340 cg_inFrameSequence = qFalse;
341
342 // Clamp time
343 cg.netTime = clamp (cg.netTime, cg.frame.serverTime - 100, cg.frame.serverTime);
344 cg.refreshTime = clamp (cg.refreshTime, cg.frame.serverTime - 100, cg.frame.serverTime);
345
346 if (!cg.frame.valid)
347 return;
348
349 // Verify our data is valid
350 if (cg.frame.numEntities != numEntities) {
351 Com_Error (ERR_DROP, "CG_EndFrameSequence: bad sequence");
352 return;
353 }
354
355 // Build a list of collision solids
356 CG_BuildSolidList ();
357
358 // Fire entity events
359 CG_FireEntityEvents ();
360
361 // Check for a prediction error
362 if (!cl_predict->intVal || !(cg.frame.playerState.pMove.pmFlags & PMF_NO_PREDICTION))
363 CG_CheckPredictionError ();
364 }
365
366 /*
367 ==========================================================================
368
369 INTERPOLATE BETWEEN FRAMES TO GET RENDERING PARMS
370
371 ==========================================================================
372 */
373
374 /*
375 ===============
376 CG_AddPacketEntities
377 ===============
378 */
379 static float cg_gloomFLightMins[3] = { -4, -4, -4 };
380 static float cg_gloomFLightMaxs[3] = { 4, 4, 4 };
CG_AddEntityShells(refEntity_t * ent)381 static void CG_AddEntityShells (refEntity_t *ent)
382 {
383 // Double
384 if (ent->flags & RF_SHELL_DOUBLE) {
385 ent->skin = cgMedia.modelShellDouble;
386 cgi.R_AddEntity (ent);
387 }
388 // Half-dam
389 if (ent->flags & RF_SHELL_HALF_DAM) {
390 ent->skin = cgMedia.modelShellHalfDam;
391 cgi.R_AddEntity (ent);
392 }
393
394 // God mode
395 if (ent->flags & RF_SHELL_RED && ent->flags & RF_SHELL_GREEN && ent->flags & RF_SHELL_BLUE) {
396 ent->skin = cgMedia.modelShellGod;
397 cgi.R_AddEntity (ent);
398 }
399 else {
400 // Red
401 if (ent->flags & RF_SHELL_RED) {
402 ent->skin = cgMedia.modelShellRed;
403 cgi.R_AddEntity (ent);
404 }
405 // Green
406 if (ent->flags & RF_SHELL_GREEN) {
407 ent->skin = cgMedia.modelShellGreen;
408 cgi.R_AddEntity (ent);
409 }
410 // Blue
411 if (ent->flags & RF_SHELL_BLUE) {
412 ent->skin = cgMedia.modelShellBlue;
413 cgi.R_AddEntity (ent);
414 }
415 }
416 }
CG_AddPacketEntities(void)417 void CG_AddPacketEntities (void)
418 {
419 refEntity_t ent;
420 entityState_t *state;
421 clientInfo_t *clInfo;
422 cgEntity_t *cent;
423 vec3_t autoRotate, angles;
424 mat3x3_t autoRotateAxis;
425 int i, pNum, autoAnim;
426 uint32 effects;
427 qBool isSelf, isPred, isDrawn;
428 uint32 delta;
429
430 // bonus items rotate at a fixed rate
431 Vec3Set (autoRotate, 0, AngleModf (cg.realTime * 0.1f), 0);
432 Angles_Matrix3 (autoRotate, autoRotateAxis);
433
434 autoAnim = cg.realTime / 1000; // brush models can auto animate their frames
435
436 memset (&ent, 0, sizeof (ent));
437
438 cg.thirdPerson = qFalse;
439 for (pNum=0 ; pNum<cg.frame.numEntities ; pNum++) {
440 state = &cg_parseEntities[(cg.frame.parseEntities+pNum)&(MAX_PARSEENTITIES_MASK)];
441 cent = &cg_entityList[state->number];
442
443 effects = state->effects;
444 ent.flags = state->renderFx;
445
446 isSelf = isPred = qFalse;
447 isDrawn = qTrue;
448
449 // Set frame
450 if (effects & EF_ANIM01)
451 ent.frame = autoAnim & 1;
452 else if (effects & EF_ANIM23)
453 ent.frame = 2 + (autoAnim & 1);
454 else if (effects & EF_ANIM_ALL)
455 ent.frame = autoAnim;
456 else if (effects & EF_ANIM_ALLFAST)
457 ent.frame = cg.realTime / 100;
458 else
459 ent.frame = state->frame;
460 ent.oldFrame = cent->prev.frame;
461
462 // Check effects
463 if (effects & EF_PENT) {
464 effects &= ~EF_PENT;
465 effects |= EF_COLOR_SHELL;
466 ent.flags |= RF_SHELL_RED;
467 }
468
469 if (effects & EF_POWERSCREEN)
470 ent.flags |= RF_SHELL_GREEN;
471
472 if (effects & EF_QUAD) {
473 effects &= ~EF_QUAD;
474 effects |= EF_COLOR_SHELL;
475 ent.flags |= RF_SHELL_BLUE;
476 }
477
478 if (effects & EF_DOUBLE) {
479 effects &= ~EF_DOUBLE;
480 effects |= EF_COLOR_SHELL;
481 ent.flags |= RF_SHELL_DOUBLE;
482 }
483
484 if (effects & EF_HALF_DAMAGE) {
485 effects &= ~EF_HALF_DAMAGE;
486 effects |= EF_COLOR_SHELL;
487 ent.flags |= RF_SHELL_HALF_DAM;
488 }
489
490 ent.backLerp = 1.0f - cg.lerpFrac;
491 ent.scale = 1;
492 Vec4Set (ent.color, 255, 255, 255, 255);
493
494 // Is it me?
495 if (state->number == cg.playerNum+1) {
496 isSelf = qTrue;
497
498 if (cl_predict->intVal
499 && !(cg.frame.playerState.pMove.pmFlags & PMF_NO_PREDICTION)
500 && cg.frame.playerState.pMove.pmType == PMT_NORMAL) {
501 // Use prediction origins, add predicted.error since it seems to solve stutteryness on platforms
502 ent.origin[0] = cg.predicted.origin[0] - ((1.0f-cg.lerpFrac) * cg.predicted.error[0]);
503 ent.origin[1] = cg.predicted.origin[1] - ((1.0f-cg.lerpFrac) * cg.predicted.error[1]);
504 ent.origin[2] = cg.predicted.origin[2] - ((1.0f-cg.lerpFrac) * cg.predicted.error[2]);
505
506 // Smooth out stair climbing
507 delta = cg.realTime - cg.predicted.stepTime;
508 if (delta < 150)
509 ent.origin[2] -= cg.predicted.step * (150 - delta) / 150;
510
511 Vec3Copy (ent.origin, ent.oldOrigin);
512 isPred = qTrue;
513 }
514 }
515
516 if (!isPred) {
517 if (ent.flags & (RF_FRAMELERP|RF_BEAM)) {
518 // Step origin discretely, because the frames do the animation properly
519 Vec3Copy (cent->current.origin, ent.origin);
520 Vec3Copy (cent->current.oldOrigin, ent.oldOrigin);
521 }
522 else {
523 // Interpolate origin
524 ent.origin[0] = ent.oldOrigin[0] = cent->prev.origin[0] + cg.lerpFrac * (cent->current.origin[0] - cent->prev.origin[0]);
525 ent.origin[1] = ent.oldOrigin[1] = cent->prev.origin[1] + cg.lerpFrac * (cent->current.origin[1] - cent->prev.origin[1]);
526 ent.origin[2] = ent.oldOrigin[2] = cent->prev.origin[2] + cg.lerpFrac * (cent->current.origin[2] - cent->prev.origin[2]);
527 }
528 }
529
530 // Tweak the color of beams
531 if (ent.flags & RF_BEAM) {
532 // The four beam colors are encoded in 32 bits of skinNum (hack)
533 int clr;
534 vec3_t length;
535
536 clr = ((state->skinNum >> ((rand () % 4)*8)) & 0xff);
537
538 if (rand () % 2)
539 CG_BeamTrail (ent.origin, ent.oldOrigin,
540 clr, (float)ent.frame,
541 0.33f + (frand () * 0.2f), -1.0f / (5 + (frand () * 0.3f)));
542
543 Vec3Subtract (ent.oldOrigin, ent.origin, length);
544
545 CG_SpawnParticle (
546 ent.origin[0], ent.origin[1], ent.origin[2],
547 length[0], length[1], length[2],
548 0, 0, 0,
549 0, 0, 0,
550 palRed (clr), palGreen (clr), palBlue (clr),
551 palRed (clr), palGreen (clr), palBlue (clr),
552 0.30f, PART_INSTANT,
553 ent.frame + ((ent.frame * 0.1f) * (rand () & 1)),
554 ent.frame + ((ent.frame * 0.1f) * (rand () & 1)),
555 PT_BEAM, 0,
556 0, qFalse,
557 PART_STYLE_BEAM,
558 0);
559
560 goto done;
561 }
562 else {
563 // Set skin
564 if (state->modelIndex == 255) {
565 // Use custom player skin
566 ent.skinNum = 0;
567 clInfo = &cg.clientInfo[state->skinNum & 0xff];
568 ent.skin = clInfo->skin;
569 ent.model = clInfo->model;
570 if (!ent.skin || !ent.model) {
571 ent.skin = cg.baseClientInfo.skin;
572 ent.model = cg.baseClientInfo.model;
573 }
574
575 //PGM
576 if (ent.flags & RF_USE_DISGUISE) {
577 if (!Q_strnicmp ((char *)ent.skin, "players/male", 12)) {
578 ent.skin = cgMedia.maleDisguiseSkin;
579 ent.model = cgMedia.maleDisguiseModel;
580 }
581 else if (!Q_strnicmp ((char *)ent.skin, "players/female", 14)) {
582 ent.skin = cgMedia.femaleDisguiseSkin;
583 ent.model = cgMedia.femaleDisguiseModel;
584 }
585 else if (!Q_strnicmp ((char *)ent.skin, "players/cyborg", 14)) {
586 ent.skin = cgMedia.cyborgDisguiseSkin;
587 ent.model = cgMedia.cyborgDisguiseModel;
588 }
589 }
590 //PGM
591 }
592 else {
593 ent.skinNum = state->skinNum;
594 ent.skin = NULL;
595 ent.model = cg.modelCfgDraw[state->modelIndex];
596 }
597 }
598
599 if (ent.model) {
600 // Gloom-specific effects
601 if (cg.currGameMod == GAME_MOD_GLOOM) {
602 // Stinger fire/C4 debris
603 if (!Q_stricmp ((char *)ent.model, "sprites/s_firea.sp2")
604 || !Q_stricmp ((char *)ent.model, "sprites/s_fireb.sp2")
605 || !Q_stricmp ((char *)ent.model, "sprites/s_flame.sp2")) {
606 if (effects & EF_ROCKET) {
607 // C4 debris
608 CG_GloomEmberTrail (cent->lerpOrigin, ent.origin);
609 }
610 else if (glm_advstingfire->intVal) {
611 // Stinger fire
612 CG_GloomStingerFire (cent->lerpOrigin, ent.origin, 25 + (frand () * 15), qTrue);
613 }
614
615 // Skip the original lighting/trail effects
616 if (effects & EF_ROCKET || glm_advstingfire->intVal)
617 goto done;
618 }
619
620 // Bio flare
621 else if ((effects & EF_ROCKET || !(effects & EF_BLASTER)) && !Q_stricmp ((char *)ent.model, "models/objects/laser/tris.md2")) {
622 CG_GloomFlareTrail (cent->lerpOrigin, ent.origin);
623
624 if (effects & EF_ROCKET) {
625 effects &= ~EF_ROCKET;
626 cgi.R_AddLight (ent.origin, 200, 0, 1, 0);
627 }
628 }
629
630 // Blob model
631 else if (!Q_stricmp ((char *)ent.model, "models/objects/tlaser/tris.md2")) {
632 CG_GloomBlobTip (cent->lerpOrigin, ent.origin);
633 isDrawn = qFalse;
634 }
635
636 // ST/Stinger gas
637 else if (!Q_stricmp ((char *)ent.model, "models/objects/smokexp/tris.md2")) {
638 if (glm_advgas->intVal) {
639 CG_GloomGasEffect (ent.origin);
640 goto done;
641 }
642 }
643
644 // C4 explosion sprite
645 else if (!Q_stricmp ((char *)ent.model, "models/objects/r_explode/tris.md2")) {
646 cgi.R_AddLight (ent.origin, 200.0f + (150*(ent.frame - 29)/36), 1.0f, 0.8f, 0.6f);
647 goto done;
648 }
649 else if (!Q_stricmp ((char *)ent.model, "models/objects/r_explode/tris2.md2") ||
650 !Q_stricmp ((char *)ent.model, "models/objects/explode/tris.md2")) {
651 // Just don't draw this crappy looking crap
652 goto done;
653 }
654 }
655
656 // Xatrix-specific effects
657 else if (cg.currGameMod == GAME_MOD_XATRIX) {
658 // Ugly phalanx tip
659 if (!Q_stricmp ((char *)ent.model, "sprites/s_photon.sp2")) {
660 CG_PhalanxTip (cent->lerpOrigin, ent.origin);
661 isDrawn = qFalse;
662 }
663 }
664
665 // Ugly model-based blaster tip
666 if (!Q_stricmp ((char *)ent.model, "models/objects/laser/tris.md2")) {
667 CG_BlasterTip (cent->lerpOrigin, ent.origin);
668 isDrawn = qFalse;
669 }
670
671 // Don't draw the BFG sprite
672 if (effects & EF_BFG && Q_WildcardMatch ("sprites/s_bfg*.sp2", (char *)ent.model, 1))
673 isDrawn = qFalse;
674 }
675
676 // Generically translucent
677 if (ent.flags & RF_TRANSLUCENT)
678 ent.color[3] = 255 * 0.70f;
679
680 // Calculate angles
681 if (effects & EF_ROTATE) {
682 // Some bonus items auto-rotate
683 Matrix3_Copy (autoRotateAxis, ent.axis);
684 }
685 else if (effects & EF_SPINNINGLIGHTS) {
686 vec3_t forward;
687 vec3_t start;
688
689 angles[0] = 0;
690 angles[1] = AngleModf (cg.realTime/2.0f) + state->angles[1];
691 angles[2] = 180;
692
693 Angles_Matrix3 (angles, ent.axis);
694
695 Angles_Vectors (angles, forward, NULL, NULL);
696 Vec3MA (ent.origin, 64, forward, start);
697 cgi.R_AddLight (start, 100, 1, 0, 0);
698 }
699 else {
700 if (isPred) {
701 if (cg.predicted.angles[PITCH] > 180)
702 angles[PITCH] = (-360 + cg.predicted.angles[PITCH]) * 0.333f;
703 else
704 angles[PITCH] = cg.predicted.angles[PITCH] * 0.333f;
705
706 angles[YAW] = cg.predicted.angles[YAW];
707 angles[ROLL] = cg.predicted.angles[ROLL];
708 }
709 else {
710 angles[0] = LerpAngle (cent->prev.angles[0], cent->current.angles[0], cg.lerpFrac);
711 angles[1] = LerpAngle (cent->prev.angles[1], cent->current.angles[1], cg.lerpFrac);
712 angles[2] = LerpAngle (cent->prev.angles[2], cent->current.angles[2], cg.lerpFrac);
713 }
714
715 if (angles[0] || angles[1] || angles[2])
716 Angles_Matrix3 (angles, ent.axis);
717 else
718 Matrix3_Identity (ent.axis);
719 }
720
721 // Flip your shadow around for lefty
722 if (isSelf && hand->intVal == 1) {
723 ent.flags |= RF_CULLHACK;
724 Vec3Negate (ent.axis[1], ent.axis[1]);
725 }
726
727 // If set to invisible, skip
728 if (!state->modelIndex)
729 goto done;
730
731 if (effects & EF_BFG) {
732 ent.flags |= RF_TRANSLUCENT;
733 ent.color[3] = 255 * 0.30f;
734 }
735
736 // RAFAEL
737 if (effects & EF_PLASMA) {
738 ent.flags |= RF_TRANSLUCENT;
739 ent.color[3] = 255 * 0.6f;
740 }
741
742 if (effects & EF_SPHERETRANS) {
743 ent.flags |= RF_TRANSLUCENT;
744
745 // PMM - *sigh* yet more EF overloading
746 if (effects & EF_TRACKERTRAIL)
747 ent.color[3] = 255 * 0.6f;
748 else
749 ent.color[3] = 255 * 0.3f;
750 }
751
752 // Some items dont deserve a shadow
753 if (effects & (EF_GIB|EF_GREENGIB|EF_GRENADE|EF_ROCKET|EF_BLASTER|EF_HYPERBLASTER|EF_BLUEHYPERBLASTER))
754 ent.flags |= RF_NOSHADOW;
755
756 // Hackish mod handling for shells
757 if (effects & EF_COLOR_SHELL) {
758 if (ent.flags & RF_SHELL_HALF_DAM) {
759 if (cg.currGameMod == GAME_MOD_ROGUE) {
760 if (ent.flags & (RF_SHELL_RED|RF_SHELL_BLUE|RF_SHELL_DOUBLE))
761 ent.flags &= ~RF_SHELL_HALF_DAM;
762 }
763 }
764
765 if (ent.flags & RF_SHELL_DOUBLE) {
766 if (cg.currGameMod == GAME_MOD_ROGUE) {
767 if (ent.flags & (RF_SHELL_RED|RF_SHELL_BLUE|RF_SHELL_GREEN))
768 ent.flags &= ~RF_SHELL_DOUBLE;
769 if (ent.flags & RF_SHELL_RED)
770 ent.flags |= RF_SHELL_BLUE;
771 else if (ent.flags & RF_SHELL_BLUE)
772 if (ent.flags & RF_SHELL_GREEN)
773 ent.flags &= ~RF_SHELL_BLUE;
774 else
775 ent.flags |= RF_SHELL_GREEN;
776 }
777 }
778 }
779
780 // Check for third person
781 if (isSelf) {
782 if (cg_thirdPerson->intVal && cl_predict->intVal && !(cg.frame.playerState.pMove.pmFlags & PMF_NO_PREDICTION) && !cg.attractLoop) {
783 cg.thirdPerson = (state->modelIndex != 0 && cg.frame.playerState.pMove.pmType != PMT_SPECTATOR);
784 }
785 else {
786 cg.thirdPerson = qFalse;
787 }
788
789 if (cg.thirdPerson && cg.cameraTrans > 0) {
790 ent.color[3] = cg.cameraTrans;
791 if (cg.cameraTrans < 255)
792 ent.flags |= RF_TRANSLUCENT;
793 }
794 else {
795 ent.flags |= RF_VIEWERMODEL;
796 }
797 }
798
799 // Force entity colors for these flags
800 if (cg.refDef.rdFlags & RDF_IRGOGGLES && ent.flags & RF_IR_VISIBLE) {
801 ent.flags |= RF_FULLBRIGHT;
802 Vec3Set (ent.color, 255, 0, 0);
803 }
804 else if (cg.refDef.rdFlags & RDF_UVGOGGLES) {
805 ent.flags |= RF_FULLBRIGHT;
806 Vec3Set (ent.color, 0, 255, 0);
807 }
808
809 // Add lights to shells
810 if (effects & EF_COLOR_SHELL) {
811 if (ent.flags & RF_SHELL_RED) {
812 ent.flags |= RF_MINLIGHT;
813 cgi.R_AddLight (ent.origin, 50, 1, 0, 0);
814 }
815 if (ent.flags & RF_SHELL_GREEN) {
816 ent.flags |= RF_MINLIGHT;
817 cgi.R_AddLight (ent.origin, 50, 0, 1, 0);
818 }
819 if (ent.flags & RF_SHELL_BLUE) {
820 ent.flags |= RF_MINLIGHT;
821 cgi.R_AddLight (ent.origin, 50, 0, 0, 1);
822 }
823 if (ent.flags & RF_SHELL_DOUBLE) {
824 ent.flags |= RF_MINLIGHT;
825 cgi.R_AddLight (ent.origin, 50, 0.9f, 0.7f, 0);
826 }
827 if (ent.flags & RF_SHELL_HALF_DAM) {
828 ent.flags |= RF_MINLIGHT;
829 cgi.R_AddLight (ent.origin, 50, 0.56f, 0.59f, 0.45f);
830 }
831 }
832
833 // Add to refresh list
834 if (isDrawn) {
835 cgi.R_AddEntity (&ent);
836
837 // Add entity shell(s)
838 if (effects & EF_COLOR_SHELL) {
839 ent.skinNum = 0;
840 CG_AddEntityShells (&ent);
841 }
842 }
843
844 // Linked models
845 if (state->modelIndex2) {
846 ent.skin = NULL; // Never use a custom skin on others
847 ent.skinNum = 0;
848
849 if (state->modelIndex2 == 255) {
850 // Custom weapon
851 clInfo = &cg.clientInfo[state->skinNum & 0xff];
852 i = (state->skinNum >> 8); // 0 is default weapon model
853 if (!cl_vwep->intVal || i > MAX_CLIENTWEAPONMODELS-1)
854 i = 0;
855
856 ent.model = clInfo->weaponModels[i];
857 if (!ent.model) {
858 if (i != 0)
859 ent.model = clInfo->weaponModels[0];
860 if (!ent.model)
861 ent.model = cg.baseClientInfo.weaponModels[0];
862 }
863 }
864 else {
865 ent.model = cg.modelCfgDraw[state->modelIndex2];
866 }
867
868 // PMM - check for the defender sphere shell .. make it translucent
869 // replaces the previous version which used the high bit on modelIndex2 to determine transparency
870 if (!Q_stricmp (cg.configStrings[CS_MODELS+(state->modelIndex2)], "models/items/shell/tris.md2")) {
871 ent.flags |= RF_TRANSLUCENT;
872 ent.color[3] = 255 * 0.32f;
873 }
874 // pmm
875
876 if (isDrawn) {
877 cgi.R_AddEntity (&ent);
878
879 // Add entity shell(s)
880 if (effects & EF_COLOR_SHELL) {
881 ent.skinNum = 0;
882 CG_AddEntityShells (&ent);
883 }
884 }
885 }
886
887 if (state->modelIndex3) {
888 ent.skin = NULL; // Never use a custom skin on others
889 ent.skinNum = 0;
890 ent.model = cg.modelCfgDraw[state->modelIndex3];
891
892 if (isDrawn) {
893 cgi.R_AddEntity (&ent);
894
895 // Add entity shell(s)
896 if (effects & EF_COLOR_SHELL) {
897 ent.skinNum = 0;
898 CG_AddEntityShells (&ent);
899 }
900 }
901 }
902
903 if (state->modelIndex4) {
904 ent.skin = NULL; // Never use a custom skin on others
905 ent.skinNum = 0;
906 ent.model = cg.modelCfgDraw[state->modelIndex4];
907
908 if (isDrawn) {
909 cgi.R_AddEntity (&ent);
910
911 // Add entity shell(s)
912 if (effects & EF_COLOR_SHELL) {
913 ent.skinNum = 0;
914 CG_AddEntityShells (&ent);
915 }
916 }
917 }
918
919 // EF_POWERSCREEN shield
920 if (effects & EF_POWERSCREEN) {
921 ent.skin = NULL; // Never use a custom skin on others
922 ent.skinNum = 0;
923 ent.model = cgMedia.powerScreenModel;
924 ent.oldFrame = 0;
925 ent.frame = 0;
926 ent.flags |= RF_TRANSLUCENT|RF_FULLBRIGHT;
927 ent.color[0] = ent.color[2] = 0;
928 ent.color[1] = 255;
929 ent.color[3] = 0.3f * 255;
930 cgi.R_AddEntity (&ent);
931 }
932
933 // EF_TELEPORTER acts like an event, but is not cleared each frame
934 // moved so it doesn't stutter on packet loss
935 if (effects & EF_TELEPORTER)
936 CG_TeleporterParticles (state);
937
938 // add automatic particle trails
939 if (effects &~ EF_ROTATE) {
940 if (effects & EF_ROCKET) {
941 CG_RocketTrail (cent->lerpOrigin, ent.origin);
942 cgi.R_AddLight (ent.origin, 200, 1, 1, 0.6f);
943 }
944 // PGM - Do not reorder EF_BLASTER and EF_HYPERBLASTER
945 else if (effects & EF_BLASTER) {
946 //PGM
947 // EF_BLASTER | EF_TRACKER is a special case for EF_BLASTER2... Cheese!
948 if (effects & EF_TRACKER) {
949 CG_BlasterGreenTrail (cent->lerpOrigin, ent.origin);
950 cgi.R_AddLight (ent.origin, 200, 0, 1, 0);
951 }
952 else {
953 CG_BlasterGoldTrail (cent->lerpOrigin, ent.origin);
954 cgi.R_AddLight (ent.origin, 200, 1, 1, 0);
955 }
956 //PGM
957 }
958 else if (effects & EF_HYPERBLASTER) {
959 if (effects & EF_TRACKER) // PGM overloaded for blaster2.
960 cgi.R_AddLight (ent.origin, 200, 0, 1, 0); // PGM
961 else {
962 // See if it is a Gloom flashlight
963 if (ent.model && cg.currGameMod == GAME_MOD_GLOOM && !Q_stricmp ((char *)ent.model, "sprites/s_shine.sp2")) {
964 static int flPredLastTime = -1;
965 qBool flPred;
966 vec3_t flOrg;
967
968 // Flashlight prediction
969 switch (cg.gloomClassType) {
970 default:
971 case GLM_DEFAULT:
972
973 case GLM_OBSERVER:
974 case GLM_BREEDER:
975 case GLM_HATCHLING:
976 case GLM_DRONE:
977 case GLM_WRAITH:
978 case GLM_KAMIKAZE:
979 case GLM_STINGER:
980 case GLM_GUARDIAN:
981 case GLM_STALKER:
982 flPred = qFalse;
983 break;
984
985 case GLM_ENGINEER:
986 case GLM_GRUNT:
987 case GLM_ST:
988 case GLM_BIOTECH:
989 case GLM_HT:
990 case GLM_COMMANDO:
991 case GLM_EXTERM:
992 case GLM_MECH:
993 flPred = qTrue;
994 break;
995 }
996 if (glm_flashpred->intVal && flPred && flPredLastTime != cg.realTime) {
997 playerStateNew_t *ps;
998 vec3_t org, forward;
999 trace_t tr;
1000
1001 ps = &cg.frame.playerState; // Calc server side player origin
1002 org[0] = ps->pMove.origin[0] * (1.0f/8.0f);
1003 org[1] = ps->pMove.origin[1] * (1.0f/8.0f);
1004 org[2] = ps->pMove.origin[2] * (1.0f/8.0f);
1005
1006 // Project our own flashlight forward
1007 Angles_Vectors (ps->viewAngles, forward, NULL, NULL);
1008 Vec3Scale (forward, 2048, forward);
1009 Vec3Add (forward, org, forward);
1010 tr = cgi.CM_BoxTrace (org, forward, cg_gloomFLightMins, cg_gloomFLightMaxs, 0,
1011 CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_DEADMONSTER);
1012
1013 // Check if it's close
1014 Vec3Subtract (tr.endPos, ent.origin, forward);
1015 if (Vec3LengthFast (forward) > 256) {
1016 Vec3Copy (ent.origin, flOrg);
1017 }
1018 else {
1019 flPredLastTime = cg.realTime;
1020
1021 // Project a flashlight
1022 Vec3Scale (cg.refDef.viewAxis[0], 2048, forward);
1023 Vec3Add (forward, cg.refDef.viewOrigin, forward);
1024 tr = cgi.CM_BoxTrace (cg.refDef.viewOrigin, forward, cg_gloomFLightMins, cg_gloomFLightMaxs, 0,
1025 CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_DEADMONSTER);
1026
1027 Vec3Copy (tr.endPos, flOrg);
1028 }
1029 }
1030 else
1031 Vec3Copy (ent.origin, flOrg);
1032
1033 // White flashlight?
1034 if (glm_flwhite->intVal)
1035 cgi.R_AddLight (flOrg, 200, 1, 1, 1);
1036 else
1037 cgi.R_AddLight (flOrg, 200, 1, 1, 0);
1038 }
1039 else
1040 cgi.R_AddLight (ent.origin, 200, 1, 1, 0);
1041 }
1042 }
1043 else if (effects & EF_GIB)
1044 CG_GibTrail (cent->lerpOrigin, ent.origin, EF_GIB);
1045 else if (effects & EF_GRENADE)
1046 CG_GrenadeTrail (cent->lerpOrigin, ent.origin);
1047 else if (effects & EF_FLIES)
1048 CG_FlyEffect (cent, ent.origin);
1049 else if (effects & EF_BFG) {
1050 if (effects & EF_ANIM_ALLFAST) {
1051 // flying
1052 CG_BfgTrail (&ent);
1053 i = 200;
1054 }
1055 else {
1056 // explosion
1057 static const int BFG_BrightRamp[6] = { 300, 400, 600, 300, 150, 75 };
1058 i = BFG_BrightRamp[state->frame%6];
1059 }
1060
1061 cgi.R_AddLight (ent.origin, (float)i, 0, 1, 0);
1062 // RAFAEL
1063 }
1064 else if (effects & EF_TRAP) {
1065 ent.origin[2] += 32;
1066 CG_TrapParticles (&ent);
1067 i = ((int)frand () * 100) + 100;
1068 cgi.R_AddLight (ent.origin, (float)i, 1, 0.8f, 0.1f);
1069 }
1070 else if (effects & EF_FLAG1) {
1071 CG_FlagTrail (cent->lerpOrigin, ent.origin, EF_FLAG1);
1072 cgi.R_AddLight (ent.origin, 225, 1, 0.1f, 0.1f);
1073 }
1074 else if (effects & EF_FLAG2) {
1075 CG_FlagTrail (cent->lerpOrigin, ent.origin, EF_FLAG2);
1076 cgi.R_AddLight (ent.origin, 225, 0.1f, 0.1f, 1);
1077
1078 //ROGUE
1079 }
1080 else if (effects & EF_TAGTRAIL) {
1081 CG_TagTrail (cent->lerpOrigin, ent.origin);
1082 cgi.R_AddLight (ent.origin, 225, 1.0, 1.0, 0.0);
1083 }
1084 else if (effects & EF_TRACKERTRAIL) {
1085 if (effects & EF_TRACKER) {
1086 float intensity;
1087
1088 intensity = 50 + (500 * ((float)sin (cg.realTime/500.0f) + 1.0f));
1089 cgi.R_AddLight (ent.origin, intensity, -1.0, -1.0, -1.0);
1090 }
1091 else {
1092 CG_TrackerShell (cent->lerpOrigin);
1093 cgi.R_AddLight (ent.origin, 155, -1.0, -1.0, -1.0);
1094 }
1095 }
1096 else if (effects & EF_TRACKER) {
1097 CG_TrackerTrail (cent->lerpOrigin, ent.origin);
1098 cgi.R_AddLight (ent.origin, 200, -1, -1, -1);
1099 //ROGUE
1100
1101 // RAFAEL
1102 }
1103 else if (effects & EF_GREENGIB)
1104 CG_GibTrail (cent->lerpOrigin, ent.origin, EF_GREENGIB);
1105 // RAFAEL
1106
1107 else if (effects & EF_IONRIPPER) {
1108 CG_IonripperTrail (cent->lerpOrigin, ent.origin);
1109 if (cg.currGameMod == GAME_MOD_GLOOM)
1110 cgi.R_AddLight (ent.origin, 100, 0.3f, 1, 0.3f);
1111 else
1112 cgi.R_AddLight (ent.origin, 100, 1, 0.5f, 0.5f);
1113 }
1114
1115 // RAFAEL
1116 else if (effects & EF_BLUEHYPERBLASTER)
1117 cgi.R_AddLight (ent.origin, 200, 0, 0, 1);
1118 // RAFAEL
1119
1120 else if (effects & EF_PLASMA) {
1121 if (effects & EF_ANIM_ALLFAST)
1122 CG_BlasterGoldTrail (cent->lerpOrigin, ent.origin);
1123
1124 cgi.R_AddLight (ent.origin, 130, 1, 0.5, 0.5);
1125 }
1126 }
1127 done:
1128 if (cent->muzzleOn) {
1129 cent->muzzleOn = qFalse;
1130 cent->muzzType = -1;
1131 cent->muzzSilenced = qFalse;
1132 cent->muzzVWeap = qFalse;
1133 }
1134 Vec3Copy (ent.origin, cent->lerpOrigin);
1135 }
1136 }
1137
1138
1139 /*
1140 ================
1141 CG_AddEntities
1142 ================
1143 */
CG_AddEntities(void)1144 void CG_AddEntities (void)
1145 {
1146 CG_AddViewWeapon ();
1147 CG_AddPacketEntities ();
1148 CG_AddTempEnts ();
1149 CG_AddLocalEnts ();
1150 CG_AddDLights ();
1151 CG_AddLightStyles ();
1152 CG_AddDecals ();
1153 CG_AddParticles ();
1154 }
1155
1156
1157 /*
1158 ==============
1159 CG_ClearEntities
1160 ==============
1161 */
CG_ClearEntities(void)1162 void CG_ClearEntities (void)
1163 {
1164 memset (cg_entityList, 0, sizeof (cg_entityList));
1165 memset (cg_parseEntities, 0, sizeof (cg_parseEntities));
1166
1167 CG_ClearTempEnts ();
1168 CG_ClearLocalEnts ();
1169 CG_ClearDLights ();
1170 CG_ClearLightStyles ();
1171 CG_ClearDecals ();
1172 CG_ClearParticles ();
1173 }
1174
1175
1176 /*
1177 ===============
1178 CG_GetEntitySoundOrigin
1179
1180 Called to get the sound spatialization origin, so that interpolated origins are used.
1181 ===============
1182 */
CG_GetEntitySoundOrigin(int entNum,vec3_t origin,vec3_t velocity)1183 void CG_GetEntitySoundOrigin (int entNum, vec3_t origin, vec3_t velocity)
1184 {
1185 cgEntity_t *ent;
1186
1187 if (entNum < 0 || entNum >= MAX_CS_EDICTS)
1188 Com_Error (ERR_DROP, "CG_GetEntitySoundOrigin: bad entNum");
1189
1190 ent = &cg_entityList[entNum];
1191
1192 if (ent->current.renderFx & (RF_FRAMELERP|RF_BEAM)) {
1193 origin[0] = ent->current.oldOrigin[0] + (ent->current.origin[0] - ent->current.oldOrigin[0]) * cg.lerpFrac;
1194 origin[1] = ent->current.oldOrigin[1] + (ent->current.origin[1] - ent->current.oldOrigin[1]) * cg.lerpFrac;
1195 origin[2] = ent->current.oldOrigin[2] + (ent->current.origin[2] - ent->current.oldOrigin[2]) * cg.lerpFrac;
1196
1197 Vec3Subtract (ent->current.origin, ent->current.oldOrigin, velocity);
1198 Vec3Scale (velocity, 10, velocity);
1199 }
1200 else {
1201 origin[0] = ent->prev.origin[0] + (ent->current.origin[0] - ent->prev.origin[0]) * cg.lerpFrac;
1202 origin[1] = ent->prev.origin[1] + (ent->current.origin[1] - ent->prev.origin[1]) * cg.lerpFrac;
1203 origin[2] = ent->prev.origin[2] + (ent->current.origin[2] - ent->prev.origin[2]) * cg.lerpFrac;
1204
1205 Vec3Subtract (ent->current.origin, ent->prev.origin, velocity);
1206 Vec3Scale (velocity, 10, velocity);
1207 }
1208
1209 if (ent->current.solid == 31) {
1210 struct cBspModel_s *cModel;
1211 vec3_t mins, maxs;
1212
1213 cModel = cgi.CM_InlineModel (cg.configStrings[CS_MODELS+ent->current.modelIndex]);
1214 if (!cModel)
1215 return;
1216
1217 cgi.CM_InlineModelBounds (cModel, mins, maxs);
1218
1219 origin[0] += 0.5f * (mins[0] + maxs[0]);
1220 origin[1] += 0.5f * (mins[1] + maxs[1]);
1221 origin[2] += 0.5f * (mins[2] + maxs[2]);
1222 }
1223 }
1224