1 /*
2 ===========================================================================
3
4 Return to Castle Wolfenstein multiplayer GPL Source Code
5 Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company.
6
7 This file is part of the Return to Castle Wolfenstein multiplayer GPL Source Code (RTCW MP Source Code).
8
9 RTCW MP Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 RTCW MP Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with RTCW MP Source Code. If not, see <http://www.gnu.org/licenses/>.
21
22 In addition, the RTCW MP Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the RTCW MP Source Code. If not, please request a copy in writing from id Software at the address below.
23
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25
26 ===========================================================================
27 */
28
29 /*
30 * name: cg_ents.c
31 *
32 * desc: present snapshot entities, happens every single frame
33 *
34 */
35
36
37 #include "cg_local.h"
38
39 ///////////////////////
40 extern int propellerModel;
41 ///////////////////////
42
43 /*
44 ======================
45 CG_PositionEntityOnTag
46
47 Modifies the entities position and axis by the given
48 tag location
49 ======================
50 */
CG_PositionEntityOnTag(refEntity_t * entity,const refEntity_t * parent,char * tagName,int startIndex,vec3_t * offset)51 void CG_PositionEntityOnTag( refEntity_t *entity, const refEntity_t *parent,
52 char *tagName, int startIndex, vec3_t *offset ) {
53 int i;
54 orientation_t lerped;
55
56 // lerp the tag
57 trap_R_LerpTag( &lerped, parent, tagName, startIndex );
58
59 // FIXME: allow origin offsets along tag? //----(SA) Yes! Adding.
60
61 VectorCopy( parent->origin, entity->origin );
62
63 if ( offset ) {
64 VectorAdd( lerped.origin, *offset, lerped.origin );
65 }
66 //----(SA) end
67
68 for ( i = 0 ; i < 3 ; i++ ) {
69 VectorMA( entity->origin, lerped.origin[i], parent->axis[i], entity->origin );
70 }
71
72 // had to cast away the const to avoid compiler problems...
73 MatrixMultiply( lerped.axis, ( (refEntity_t *)parent )->axis, entity->axis );
74 // Ridah, not sure why this was here.. causes jittery torso animation, since the torso might have
75 // different frame/oldFrame
76 //entity->backlerp = parent->backlerp;
77 }
78
79
80 /*
81 ======================
82 CG_PositionRotatedEntityOnTag
83
84 Modifies the entities position and axis by the given
85 tag location
86 ======================
87 */
CG_PositionRotatedEntityOnTag(refEntity_t * entity,const refEntity_t * parent,char * tagName)88 void CG_PositionRotatedEntityOnTag( refEntity_t *entity, const refEntity_t *parent,
89 char *tagName ) {
90 int i;
91 orientation_t lerped;
92 vec3_t tempAxis[3];
93
94 //AxisClear( entity->axis );
95 // lerp the tag
96 trap_R_LerpTag( &lerped, parent, tagName, 0 );
97
98 // FIXME: allow origin offsets along tag?
99 VectorCopy( parent->origin, entity->origin );
100 for ( i = 0 ; i < 3 ; i++ ) {
101 VectorMA( entity->origin, lerped.origin[i], parent->axis[i], entity->origin );
102 }
103
104 // had to cast away the const to avoid compiler problems...
105 MatrixMultiply( entity->axis, lerped.axis, tempAxis );
106 MatrixMultiply( tempAxis, ( (refEntity_t *)parent )->axis, entity->axis );
107 }
108
109
110 //----(SA) added
111 /*
112 ==============
113 CG_LoseArmor
114 maybe better in cg_localents.c
115 ==============
116 */
CG_LoseArmor(centity_t * cent,int index)117 void CG_LoseArmor( centity_t *cent, int index ) {
118 }
119
120 /*
121 ==============
122 CG_AttachedPartChange
123 ==============
124 */
CG_AttachedPartChange(centity_t * cent)125 void CG_AttachedPartChange( centity_t *cent ) {
126 }
127
128 //----(SA) end
129
130 /*
131 ==========================================================================
132
133 FUNCTIONS CALLED EACH FRAME
134
135 ==========================================================================
136 */
137
138 /*
139 ======================
140 CG_SetEntitySoundPosition
141
142 Also called by event processing code
143 ======================
144 */
CG_SetEntitySoundPosition(centity_t * cent)145 void CG_SetEntitySoundPosition( centity_t *cent ) {
146 if ( cent->currentState.solid == SOLID_BMODEL ) {
147 vec3_t origin;
148 float *v;
149
150 v = cgs.inlineModelMidpoints[ cent->currentState.modelindex ];
151 VectorAdd( cent->lerpOrigin, v, origin );
152 trap_S_UpdateEntityPosition( cent->currentState.number, origin );
153 } else {
154 trap_S_UpdateEntityPosition( cent->currentState.number, cent->lerpOrigin );
155 }
156 }
157
158
159
160
161 #define LS_FRAMETIME 100 // (ms) cycle through lightstyle characters at 10fps
162
163
164 /*
165 ==============
166 CG_SetDlightIntensity
167
168 ==============
169 */
CG_AddLightstyle(centity_t * cent)170 void CG_AddLightstyle( centity_t *cent ) {
171 float lightval;
172 int cl;
173 int r, g, b;
174 int stringlength;
175 float offset;
176 int otime;
177 int lastch, nextch;
178
179 if ( cent->dl_stylestring[0] == '\0' ) {
180 return;
181 }
182
183 otime = cg.time - cent->dl_time;
184 stringlength = strlen( cent->dl_stylestring );
185
186 // it's been a long time since you were updated, lets assume a reset
187 if ( otime > 2 * LS_FRAMETIME ) {
188 otime = 0;
189 cent->dl_frame = cent->dl_oldframe = 0;
190 cent->dl_backlerp = 0;
191 }
192
193 cent->dl_time = cg.time;
194
195 offset = ( (float)otime ) / LS_FRAMETIME;
196
197 cent->dl_backlerp += offset;
198
199
200 if ( cent->dl_backlerp > 1 ) { // we're moving on to the next frame
201 cent->dl_oldframe = cent->dl_oldframe + (int)cent->dl_backlerp;
202 cent->dl_frame = cent->dl_oldframe + 1;
203 if ( cent->dl_oldframe >= stringlength ) {
204 cent->dl_oldframe = ( cent->dl_oldframe ) % stringlength;
205 if ( cent->dl_oldframe < 3 && cent->dl_sound ) { // < 3 so if an alarm comes back into the pvs it will only start a sound if it's going to be closely synced with the light, otherwise wait till the next cycle
206 trap_S_StartSound( NULL, cent->currentState.number, CHAN_AUTO, cgs.gameSounds[cent->dl_sound] );
207 }
208 }
209
210 if ( cent->dl_frame >= stringlength ) {
211 cent->dl_frame = ( cent->dl_frame ) % stringlength;
212 }
213
214 cent->dl_backlerp = cent->dl_backlerp - (int)cent->dl_backlerp;
215 }
216
217
218 lastch = cent->dl_stylestring[cent->dl_oldframe] - 'a';
219 nextch = cent->dl_stylestring[cent->dl_frame] - 'a';
220
221 lightval = ( lastch * ( 1.0 - cent->dl_backlerp ) ) + ( nextch * cent->dl_backlerp );
222
223 lightval = ( lightval * ( 1000.0f / 24.0f ) ) - 200.0f; // they want 'm' as the "middle" value as 300
224
225 lightval = max( 0.0f, lightval );
226 lightval = min( 1000.0f, lightval );
227
228 cl = cent->currentState.constantLight;
229 r = cl & 255;
230 g = ( cl >> 8 ) & 255;
231 b = ( cl >> 16 ) & 255;
232
233 trap_R_AddLightToScene( cent->lerpOrigin, lightval, (float)r / 255.0f, (float)g / 255.0f, (float)b / 255.0f, 0 ); // overdraw forced to 0 for now
234 }
235
236
237 void CG_GetWindVector( vec3_t dir ); // JPW NERVE
238
239 /*
240 ==================
241 CG_EntityEffects
242
243 Add continuous entity effects, like local entity emission and lighting
244 ==================
245 */
CG_EntityEffects(centity_t * cent)246 static void CG_EntityEffects( centity_t *cent ) {
247 static vec3_t dir;
248 // update sound origins
249 CG_SetEntitySoundPosition( cent );
250
251 // add loop sound
252 if ( cent->currentState.loopSound ) {
253 //----(SA) hmm, the above (CG_SetEntitySoundPosition()) sets s_entityPosition[entityNum] with a valid
254 // location, but the looping sound for a bmodel will never get it since that sound is
255 // started with the lerpOriging right here. \/ \/ How do looping sounds ever work for bmodels?
256 // Or have they always been broken and we just never used them?
257
258 if ( cent->currentState.eType == ET_SPEAKER ) {
259 /*if(cent->currentState.density == 1) { // NO_PVS
260 CG_S_AddRealLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, cgs.gameSounds[ cent->currentState.loopSound ] );
261 }
262 else*/if ( cent->currentState.dmgFlags ) { // range is set
263 CG_S_AddRangedLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, cgs.gameSounds[ cent->currentState.loopSound ], cent->currentState.dmgFlags );
264 } else {
265 CG_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, cgs.gameSounds[ cent->currentState.loopSound ], 255 );
266 }
267 } else if ( cent->currentState.solid == SOLID_BMODEL ) {
268 vec3_t origin;
269 float *v;
270
271 v = cgs.inlineModelMidpoints[ cent->currentState.modelindex ];
272 VectorAdd( cent->lerpOrigin, v, origin );
273 CG_S_AddLoopingSound( cent->currentState.number, origin, vec3_origin, cgs.gameSounds[ cent->currentState.loopSound ], 255 );
274 } else {
275 CG_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, cgs.gameSounds[ cent->currentState.loopSound ], 255 );
276 }
277 } /*else {
278 // stop NO_PVS speakers if they've been turned off
279 if(cent->currentState.eType == ET_SPEAKER) {
280 if(cent->currentState.density == 1) {
281 trap_S_StopLoopingSound(cent->currentState.number);
282 }
283 }
284 }*/
285
286
287 // constant light glow
288 if ( cent->currentState.constantLight ) {
289 int cl;
290 float i, r, g, b;
291
292
293 if ( cent->dl_stylestring[0] != 0 ) { // it's probably a dlight
294 CG_AddLightstyle( cent );
295 } else
296 {
297 cl = cent->currentState.constantLight;
298 r = (float) (cl & 0xFF) / 255.0;
299 g = (float) ((cl >> 8) & 0xFF) / 255.0;
300 b = (float) ((cl >> 16) & 0xFF) / 255.0;
301 i = (float) ((cl >> 24) & 0xFF) * 4.0;
302 trap_R_AddLightToScene(cent->lerpOrigin, i, r, g, b, 0);
303 }
304 }
305
306 // Ridah, flaming sounds
307 if ( CG_EntOnFire( cent ) ) {
308 // play a flame blow sound when moving
309 CG_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, cgs.media.flameBlowSound, (int)( 255.0 * ( 1.0 - fabs( cent->fireRiseDir[2] ) ) ) );
310 // play a burning sound when not moving
311 CG_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, cgs.media.flameSound, (int)( 0.3 * 255.0 * ( pow( cent->fireRiseDir[2],2 ) ) ) );
312 }
313
314
315 // DHM - Nerve :: If EF_SMOKING is set, emit smoke
316 if ( cgs.gametype >= GT_WOLF && cent->currentState.eFlags & EF_SMOKING ) {
317 float rnd = random();
318
319 if ( cent->lastTrailTime < cg.time ) {
320 cent->lastTrailTime = cg.time + 100;
321
322 // JPW NERVE -- use wind vector for smoke
323 CG_GetWindVector( dir );
324 VectorScale( dir,20,dir ); // was 75, before that 55
325 if ( dir[2] < 10 ) {
326 dir[2] += 10;
327 }
328 // dir[0] = crandom() * 10;
329 // dir[1] = crandom() * 10;
330 // dir[2] = 10 + rnd * 30;
331 // jpw
332 CG_SmokePuff( cent->lerpOrigin, dir, 15 + ( random() * 10 ),
333 0.3 + rnd, 0.3 + rnd, 0.3 + rnd, 0.4, 1500 + ( rand() % 500 ),
334 cg.time, cg.time + 500, 0, cgs.media.smokePuffShader );
335 }
336 }
337 // dhm - end
338 // JPW NERVE same thing but for smoking barrels instead of nasty server-side effect from single player
339 if ( cgs.gametype >= GT_WOLF && cent->currentState.eFlags & EF_SMOKINGBLACK ) {
340 float rnd = random();
341
342 if ( cent->lastTrailTime < cg.time ) {
343 cent->lastTrailTime = cg.time + 75;
344
345 CG_GetWindVector( dir );
346 VectorScale( dir,50,dir ); // was 75, before that 55
347 if ( dir[2] < 50 ) {
348 dir[2] += 50;
349 }
350
351 CG_SmokePuff( cent->lerpOrigin, dir, 40 + random() * 70, //40+(rnd*40),
352 rnd * 0.1, rnd * 0.1, rnd * 0.1, 1, 2800 + ( rand() % 4000 ), //2500+(random()*1500),
353 cg.time, 0, 0, cgs.media.smokePuffShader );
354 }
355 }
356 // jpw
357
358
359
360 }
361
362
363 /*
364 ==================
365 CG_General
366 ==================
367 */
CG_General(centity_t * cent)368 static void CG_General( centity_t *cent ) {
369 refEntity_t ent;
370 entityState_t *s1;
371
372 s1 = ¢->currentState;
373
374 // if set to invisible, skip
375 if ( !s1->modelindex ) {
376 return;
377 }
378
379 memset( &ent, 0, sizeof( ent ) );
380
381 // set frame
382
383 ent.frame = s1->frame;
384 ent.oldframe = ent.frame;
385 ent.backlerp = 0;
386
387 if ( ent.frame ) {
388
389 ent.oldframe -= 1;
390 ent.backlerp = 1 - cg.frameInterpolation;
391
392 if ( cent->currentState.time ) {
393 ent.fadeStartTime = cent->currentState.time;
394 ent.fadeEndTime = cent->currentState.time2;
395 }
396
397 }
398
399 VectorCopy( cent->lerpOrigin, ent.origin );
400 VectorCopy( cent->lerpOrigin, ent.oldorigin );
401
402 ent.hModel = cgs.gameModels[s1->modelindex];
403
404 // player model
405 if ( s1->number == cg.snap->ps.clientNum ) {
406 ent.renderfx |= RF_THIRD_PERSON; // only draw from mirrors
407 }
408
409 if ( cent->currentState.eType == ET_MG42_BARREL ) {
410 // grab angles from first person user or self if not
411 // ATVI Wolfenstein Misc #469 - don't track until viewlocked
412 if ( cent->currentState.otherEntityNum == cg.snap->ps.clientNum && cg.snap->ps.viewlocked ) {
413 AnglesToAxis( cg.predictedPlayerState.viewangles, ent.axis );
414 } else {
415 AnglesToAxis( cent->lerpAngles, ent.axis );
416 }
417 } else {
418 // convert angles to axis
419 AnglesToAxis( cent->lerpAngles, ent.axis );
420 }
421
422 // scale gamemodels
423 if ( cent->currentState.eType == ET_GAMEMODEL ) {
424 VectorScale( ent.axis[0], cent->currentState.angles2[0], ent.axis[0] );
425 VectorScale( ent.axis[1], cent->currentState.angles2[1], ent.axis[1] );
426 VectorScale( ent.axis[2], cent->currentState.angles2[2], ent.axis[2] );
427 ent.nonNormalizedAxes = qtrue;
428
429 //----(SA) testing
430 if ( cent->currentState.apos.trType ) {
431 ent.reFlags |= REFLAG_ORIENT_LOD;
432 }
433 //----(SA) end
434 }
435
436 // add to refresh list
437 trap_R_AddRefEntityToScene( &ent );
438
439 memcpy( ¢->refEnt, &ent, sizeof( refEntity_t ) );
440 }
441
442 /*
443 ==================
444 CG_Speaker
445
446 Speaker entities can automatically play sounds
447 ==================
448 */
CG_Speaker(centity_t * cent)449 static void CG_Speaker( centity_t *cent ) {
450 if ( !cent->currentState.clientNum ) { // FIXME: use something other than clientNum...
451 return; // not auto triggering
452 }
453
454 if ( cg.time < cent->miscTime ) {
455 return;
456 }
457
458 trap_S_StartSound( NULL, cent->currentState.number, CHAN_ITEM, cgs.gameSounds[cent->currentState.eventParm] );
459
460 // ent->s.frame = ent->wait * 10;
461 // ent->s.clientNum = ent->random * 10;
462 cent->miscTime = cg.time + cent->currentState.frame * 100 + cent->currentState.clientNum * 100 * crandom();
463 }
464
465
466
467 /*
468 ==============
469 CG_DrawHoldableSelect
470
471 This, of course, will all change when we've got a hud, but for now it makes the holdable items usable and not bad looking
472 ==============
473 */
CG_DrawHoldableSelect(void)474 void CG_DrawHoldableSelect( void ) {
475 /*
476 int bits;
477 int count;
478 int amount;
479 int i, x, y, w;
480 float *color;
481 char *name;
482 gitem_t *item;
483
484 // don't display if dead
485 if ( cg.predictedPlayerState.stats[STAT_HEALTH] <= 0 ) {
486 return;
487 }
488
489 color = CG_FadeColor( cg.holdableSelectTime, HOLDABLE_SELECT_TIME );
490 if ( !color ) {
491 return;
492 }
493 trap_R_SetColor( color );
494
495 // showing select clears pickup item display, but not the blend blob
496 cg.itemPickupTime = 0;
497
498 //----(SA) removed
499 // count the number of holdables owned
500 bits = cg.snap->ps.stats[ STAT_HOLDABLE_ITEM ];
501 count = 0;
502
503 for ( i = 1 ; i <= HI_BOOK3; i++ ) {
504 if ( bits & ( 1 << i ) ) {
505 if ( cg.predictedPlayerState.holdable[i] ) { // don't show ones we're out of
506 count++;
507 }
508 }
509 }
510
511 if ( !count ) {
512 return;
513 }
514
515 x = 320 - count * 20;
516 y = 380;
517
518
519 for ( i = 1 ; i <= HI_BOOK3 ; i++ ) {
520 if ( !( bits & ( 1 << i ) ) ) {
521 continue;
522 }
523
524 amount = cg.predictedPlayerState.holdable[i];
525
526 if ( !amount ) {
527 continue;
528 }
529
530 item = BG_FindItemForHoldable( i );
531 if ( !item ) {
532 continue;
533 }
534
535 CG_RegisterItemVisuals( item - bg_itemlist );
536
537 // draw icon
538 if ( i == HI_WINE ) {
539 // wine icons have three stages since each bottle has three uses (as opposed to others so far where there's only 1 use)
540 int wine = amount;
541 if ( wine > 3 ) {
542 wine = 3;
543 }
544 CG_DrawPic( x, y, 32, 32, cg_items[item - bg_itemlist].icons[ 2 - ( wine - 1 ) ] ) ;
545 } else {
546 CG_DrawPic( x, y, 32, 32, cg_items[item - bg_itemlist].icons[0] );
547 }
548
549 // draw remaining uses if there's more than one
550 if ( amount > 1 ) {
551 CG_DrawBigStringColor( x, y + 34, va( "%d", amount ), color );
552 }
553
554 // draw selection marker
555 if ( i == cg.holdableSelect ) {
556 CG_DrawPic( x - 4, y - 4, 40, 40, cgs.media.selectShader );
557 }
558
559 x += 40;
560 }
561
562 // draw the selected name
563 if ( cg.holdableSelect ) {
564 item = BG_FindItemForHoldable( cg.holdableSelect );
565 if ( item ) {
566 name = item->pickup_name;
567 if ( name ) {
568 //----(SA) trying smaller text
569 // w = CG_DrawStrlen( name ) * BIGCHAR_WIDTH;
570 w = CG_DrawStrlen( name ) * 10;
571 x = ( SCREEN_WIDTH - w ) / 2;
572 // CG_DrawBigStringColor(x, y - 22, name, color);
573 CG_DrawStringExt2( x, y + 60, name, color, qfalse, qtrue, 10, 10, 0 );
574 }
575 }
576 }
577
578 trap_R_SetColor( NULL );
579 */
580 }
581
582
583 /*
584 ==============
585 CG_NextItem_f
586 ==============
587 */
588
CG_NextItem_f(void)589 void CG_NextItem_f( void ) {
590 /*
591 int i;
592 int original, next;
593
594 if ( !cg.snap ) {
595 return;
596 }
597
598 if ( cg.snap->ps.pm_flags & PMF_FOLLOW ) {
599 return;
600 }
601
602 cg.holdableSelectTime = cg.time;
603 cg.weaponSelectTime = 0; // (SA) clear weapon selection drawing
604
605 next = original = cg.holdableSelect;
606
607 for ( i = 0 ; i < HI_NUM_HOLDABLE ; i++ ) {
608 next++;
609
610 if ( next == HI_NUM_HOLDABLE ) {
611 next = 0;
612 }
613
614 if ( cg.predictedPlayerState.holdable[next] ) { //----(SA)
615 break;
616 }
617 }
618
619 if ( i == HI_NUM_HOLDABLE ) {
620 next = original;
621 }
622
623 cg.holdableSelect = next;
624 */
625 }
626
627 /*
628 ==============
629 CG_PrevItem_f
630 ==============
631 */
CG_PrevItem_f(void)632 void CG_PrevItem_f( void ) {
633 /*
634 int i;
635 int original, next;
636
637 if ( !cg.snap ) {
638 return;
639 }
640
641 if ( cg.snap->ps.pm_flags & PMF_FOLLOW ) {
642 return;
643 }
644
645 cg.weaponSelectTime = 0; // (SA) clear weapon selection drawing
646 cg.holdableSelectTime = cg.time;
647
648 next = original = cg.holdableSelect;
649
650 for ( i = 0 ; i < HI_NUM_HOLDABLE ; i++ ) {
651 next--;
652
653 if ( next == -1 ) {
654 next = HI_NUM_HOLDABLE - 1;
655 }
656
657 if ( cg.predictedPlayerState.holdable[next] ) { //----(SA)
658 break;
659 }
660 }
661
662 if ( i == HI_NUM_HOLDABLE ) {
663 next = original;
664 }
665
666 cg.holdableSelect = next;
667 */
668 }
669
670 /*
671 ==============
672 CG_Item_f
673 ==============
674 */
CG_Item_f(void)675 void CG_Item_f( void ) {
676 //int num;
677 //num = atoi( CG_Argv( 1 ) );
678
679 //cg.holdableSelectTime = cg.time;
680
681 //CG_Printf( "Item set to: %d\n", num );
682 }
683
684
685
686 //----(SA) added
687 /*
688 ==============
689 CG_HoldableUsedupChange
690 ==============
691 */
CG_HoldableUsedupChange(void)692 void CG_HoldableUsedupChange( void ) {
693 int holding;
694
695 holding = cg.holdableSelect;
696
697 CG_NextItem_f();
698
699 if ( cg.holdableSelect == holding ) { // nothing else to go to
700 cg.holdableSelect = 0;
701 cg.weaponSelectTime = 0;
702 return;
703 }
704 }
705 //----(SA) end
706
707
708
CG_PlayerSeesItem(playerState_t * ps,entityState_t * item,int atTime,int itemType)709 qboolean CG_PlayerSeesItem( playerState_t *ps, entityState_t *item, int atTime, int itemType ) {
710 vec3_t vorigin, eorigin, viewa, dir;
711 float dot, dist, foo;
712 trace_t tr;
713
714 BG_EvaluateTrajectory( &item->pos, atTime, eorigin );
715
716 VectorCopy( ps->origin, vorigin );
717 vorigin[2] += ps->viewheight; // get the view loc up to the viewheight
718 // eorigin[2] += 8; // and subtract the item's offset (that is used to place it on the ground)
719
720
721 VectorSubtract( vorigin, eorigin, dir );
722
723 dist = VectorNormalize( dir ); // dir is now the direction from the item to the player
724
725 if ( dist > 255 ) {
726 return qfalse; // only run the remaining stuff on items that are close enough
727
728 }
729 // (SA) FIXME: do this without AngleVectors.
730 // It'd be nice if the angle vectors for the player
731 // have already been figured at this point and I can
732 // just pick them up. (if anybody is storing this somewhere,
733 // for the current frame please let me know so I don't
734 // have to do redundant calcs)
735 AngleVectors( ps->viewangles, viewa, 0, 0 );
736 dot = DotProduct( viewa, dir );
737
738 // give more range based on distance (the hit area is wider when closer)
739
740 // foo = -0.94f - (dist/255.0f) * 0.057f; // (ranging from -0.94 to -0.997) (it happened to be a pretty good range)
741 foo = -0.94f - ( dist * ( 1.0f / 255.0f ) ) * 0.057f; // (ranging from -0.94 to -0.997) (it happened to be a pretty good range)
742
743 /// Com_Printf("test: if(%f > %f) return qfalse (dot > foo)\n", dot, foo);
744 if ( dot > foo ) {
745 return qfalse;
746 }
747
748 // (SA) okay, everything else is okay, so do a bloody trace. (so coronas on treasure doesn't show through walls) <sigh>
749 if ( itemType == IT_TREASURE ) {
750 CG_Trace( &tr, vorigin, NULL, NULL, eorigin, -1, MASK_SOLID );
751
752 if ( tr.fraction != 1 ) {
753 return qfalse;
754 }
755 }
756
757 return qtrue;
758
759 }
760
761
762 /*
763 ==================
764 CG_Item
765 ==================
766 */
CG_Item(centity_t * cent)767 static void CG_Item( centity_t *cent ) {
768 refEntity_t ent;
769 entityState_t *es;
770 gitem_t *item;
771 qboolean hasStand, highlight;
772 float highlightFadeScale = 1.0f;
773
774 es = ¢->currentState;
775
776 hasStand = qfalse;
777 highlight = qfalse;
778
779 // (item index is stored in es->modelindex for item)
780
781 if ( es->modelindex >= bg_numItems ) {
782 CG_Error( "Bad item index %i on entity", es->modelindex );
783 }
784
785 // if set to invisible, skip
786 if ( !es->modelindex || ( es->eFlags & EF_NODRAW ) ) {
787 return;
788 }
789
790 item = &bg_itemlist[ es->modelindex ];
791
792 if ( cg_simpleItems.integer && item->giType != IT_TEAM ) {
793 memset( &ent, 0, sizeof( ent ) );
794 ent.reType = RT_SPRITE;
795 VectorCopy( cent->lerpOrigin, ent.origin );
796 ent.radius = 14;
797 ent.customShader = cg_items[es->modelindex].icons[0];
798 ent.shaderRGBA[0] = 255;
799 ent.shaderRGBA[1] = 255;
800 ent.shaderRGBA[2] = 255;
801 ent.shaderRGBA[3] = 255;
802 trap_R_AddRefEntityToScene( &ent );
803 return;
804 }
805
806 memset( &ent, 0, sizeof( ent ) );
807
808 ent.nonNormalizedAxes = qfalse;
809
810 if ( item->giType == IT_WEAPON ) {
811 weaponInfo_t *weaponInfo = &cg_weapons[item->giTag];
812
813 if ( weaponInfo->standModel ) {
814 hasStand = qtrue;
815 }
816
817 if ( hasStand ) { // first try to put the weapon on it's 'stand'
818 refEntity_t stand;
819
820 memset( &stand, 0, sizeof( stand ) );
821 stand.hModel = weaponInfo->standModel;
822
823 if ( es->eFlags & EF_SPINNING ) {
824 if ( es->groundEntityNum == -1 || !es->groundEntityNum ) { // (SA) spinning with a stand will spin the stand and the attached weap (only when in the air)
825 VectorCopy( cg.autoAnglesSlow, cent->lerpAngles );
826 VectorCopy( cg.autoAnglesSlow, cent->lastLerpAngles );
827 } else {
828 VectorCopy( cent->lastLerpAngles, cent->lerpAngles ); // make a tossed weapon sit on the ground in a position that matches how it was yawed
829 }
830 }
831
832 AnglesToAxis( cent->lerpAngles, stand.axis );
833 VectorCopy( cent->lerpOrigin, stand.origin );
834
835 // scale the stand to match the weapon scale ( the weapon will also be scaled inside CG_PositionEntityOnTag() )
836 VectorScale( stand.axis[0], 1.5, stand.axis[0] );
837 VectorScale( stand.axis[1], 1.5, stand.axis[1] );
838 VectorScale( stand.axis[2], 1.5, stand.axis[2] );
839
840 //----(SA) modified
841 if ( cent->currentState.frame ) {
842 CG_PositionEntityOnTag( &ent, &stand, va( "tag_stand%d", cent->currentState.frame ), 0, NULL );
843 } else {
844 CG_PositionEntityOnTag( &ent, &stand, "tag_stand", 0, NULL );
845 }
846 //----(SA) end
847
848 VectorCopy( ent.origin, ent.oldorigin );
849 ent.nonNormalizedAxes = qtrue;
850
851 } else { // then default to laying it on it's side
852 if ( !cg_items[es->modelindex].models[2] ) {
853 cent->lerpAngles[2] += 90;
854 }
855
856 AnglesToAxis( cent->lerpAngles, ent.axis );
857
858 // increase the size of the weapons when they are presented as items
859 VectorScale( ent.axis[0], 1.5, ent.axis[0] );
860 VectorScale( ent.axis[1], 1.5, ent.axis[1] );
861 VectorScale( ent.axis[2], 1.5, ent.axis[2] );
862 ent.nonNormalizedAxes = qtrue;
863
864 VectorCopy( cent->lerpOrigin, ent.origin );
865 VectorCopy( cent->lerpOrigin, ent.oldorigin );
866
867 if ( es->eFlags & EF_SPINNING ) { // spinning will override the angles set by a stand
868 if ( es->groundEntityNum == -1 || !es->groundEntityNum ) { // (SA) spinning with a stand will spin the stand and the attached weap (only when in the air)
869 VectorCopy( cg.autoAnglesSlow, cent->lerpAngles );
870 VectorCopy( cg.autoAnglesSlow, cent->lastLerpAngles );
871 } else {
872 VectorCopy( cent->lastLerpAngles, cent->lerpAngles ); // make a tossed weapon sit on the ground in a position that matches how it was yawed
873 }
874 }
875 }
876
877 } else {
878 AnglesToAxis( cent->lerpAngles, ent.axis );
879 VectorCopy( cent->lerpOrigin, ent.origin );
880 VectorCopy( cent->lerpOrigin, ent.oldorigin );
881
882 if ( es->eFlags & EF_SPINNING ) { // spinning will override the angles set by a stand
883 VectorCopy( cg.autoAnglesSlow, cent->lerpAngles );
884 AxisCopy( cg.autoAxisSlow, ent.axis );
885 }
886 }
887
888
889 if ( es->modelindex2 ) { // modelindex2 was specified for the ent, meaning it probably has an alternate model (as opposed to the one in the itemlist)
890 // try to load it first, and if it fails, default to the itemlist model
891 ent.hModel = cgs.gameModels[ es->modelindex2 ];
892 } else {
893 if ( item->giType == IT_WEAPON && cg_items[es->modelindex].models[2] ) { // check if there's a specific model for weapon pickup placement
894 ent.hModel = cg_items[es->modelindex].models[2];
895 } else if ( item->giType == IT_HEALTH || item->giType == IT_AMMO || item->giType == IT_POWERUP ) {
896 if ( es->density < ( 1 << 9 ) ) { // (10 bits of data transmission for density)
897 ent.hModel = cg_items[es->modelindex].models[es->density]; // multi-state powerups store their state in 'density'
898 } else {
899 ent.hModel = cg_items[es->modelindex].models[0];
900 }
901 } else {
902 ent.hModel = cg_items[es->modelindex].models[0];
903 }
904 }
905
906 //----(SA) find midpoint for highlight corona.
907 // Can't do it when item is registered since it wouldn't know about replacement model
908 if ( !( cent->usehighlightOrigin ) ) {
909 vec3_t mins, maxs, offset;
910 int i;
911
912 trap_R_ModelBounds( ent.hModel, mins, maxs ); // get bounds
913
914 for ( i = 0 ; i < 3 ; i++ ) {
915 offset[i] = mins[i] + 0.5 * ( maxs[i] - mins[i] ); // find object-space center
916 }
917
918 VectorCopy( cent->lerpOrigin, cent->highlightOrigin ); // set 'midpoint' to origin
919
920 for ( i = 0 ; i < 3 ; i++ ) { // adjust midpoint by offset and orientation
921 cent->highlightOrigin[i] += offset[0] * ent.axis[0][i] +
922 offset[1] * ent.axis[1][i] +
923 offset[2] * ent.axis[2][i];
924 }
925
926 cent->usehighlightOrigin = qtrue;
927 }
928
929 // items without glow textures need to keep a minimum light value so they are always visible
930 // if ( ( item->giType == IT_WEAPON ) || ( item->giType == IT_ARMOR ) ) {
931 ent.renderfx |= RF_MINLIGHT;
932 // }
933
934 // highlighting items the player looks at
935 if ( cg_drawCrosshairPickups.integer ) {
936
937
938 if ( cg_drawCrosshairPickups.integer == 2 ) { // '2' is 'force highlights'
939 highlight = qtrue;
940 }
941
942 if ( CG_PlayerSeesItem( &cg.predictedPlayerState, es, cg.time, item->giType ) ) {
943 highlight = qtrue;
944
945 if ( item->giType == IT_TREASURE ) {
946 trap_R_AddCoronaToScene( cent->highlightOrigin, 1, 0.85, 0.5, 2, cent->currentState.number, qtrue ); //----(SA) add corona to treasure
947 }
948 } else {
949 if ( item->giType == IT_TREASURE ) {
950 trap_R_AddCoronaToScene( cent->highlightOrigin, 1, 0.85, 0.5, 2, cent->currentState.number, qfalse ); //----(SA) "empty corona" for proper fades
951 }
952 }
953
954 //----(SA) added fixed item highlight fading
955
956 if ( highlight ) {
957 if ( !cent->highlighted ) {
958 cent->highlighted = qtrue;
959 cent->highlightTime = cg.time;
960 }
961 ent.hilightIntensity = ( ( cg.time - cent->highlightTime ) / 250.0f ) * highlightFadeScale; // .25 sec to brighten up
962 } else {
963 if ( cent->highlighted ) {
964 cent->highlighted = qfalse;
965 cent->highlightTime = cg.time;
966 }
967 ent.hilightIntensity = 1.0f - ( ( cg.time - cent->highlightTime ) / 1000.0f ) * highlightFadeScale; // 1 sec to dim down (diff in time causes problems if you quickly flip to/away from looking at the item)
968 }
969
970 if ( ent.hilightIntensity < 0.25f ) { // leave a minlight
971 ent.hilightIntensity = 0.25f;
972 }
973 if ( ent.hilightIntensity > 1 ) {
974 ent.hilightIntensity = 1.0;
975 }
976 }
977 //----(SA) end
978
979
980 // add to refresh list
981 trap_R_AddRefEntityToScene( &ent );
982 }
983
984 //============================================================================
985
986 /*
987 ===============
988 CG_Smoker
989 ===============
990 */
CG_Smoker(centity_t * cent)991 static void CG_Smoker( centity_t *cent ) {
992 // this ent has some special setting up
993 // time = speed
994 // time2 = duration
995 // angles2[0] = start_size
996 // angles2[1] = end_size
997 // angles2[2] = wait
998 // dl_intensity = health
999 // constantLight = delay
1000 // origin2 = normal to emit particles along
1001
1002 if ( cg.time - cent->highlightTime > cent->currentState.constantLight ) {
1003 // FIXME: make this framerate independant?
1004 cent->highlightTime = cg.time; // fire a particle this frame
1005
1006 if ( cent->currentState.density == 3 ) { // cannon
1007 CG_ParticleSmoke( cgs.media.smokePuffShaderdirty, cent );
1008 } else if ( !( cent->currentState.density ) ) {
1009 CG_ParticleSmoke( cgs.media.smokePuffShader, cent );
1010 } else {
1011 CG_ParticleSmoke( cgs.media.smokePuffShader, cent );
1012 }
1013 }
1014
1015 cent->lastTrailTime = cg.time; // time we were last received at the client
1016 }
1017
1018 /*
1019 ===============
1020 CG_Missile
1021 ===============
1022 */
1023
1024 extern void CG_RocketTrail( centity_t *ent, const weaponInfo_t *wi );
1025
CG_Missile(centity_t * cent)1026 static void CG_Missile( centity_t *cent ) {
1027 refEntity_t ent;
1028 entityState_t *s1;
1029 const weaponInfo_t *weapon;
1030
1031 s1 = ¢->currentState;
1032 if ( s1->weapon >= WP_NUM_WEAPONS ) {
1033 s1->weapon = 0;
1034 }
1035 weapon = &cg_weapons[s1->weapon];
1036
1037 // calculate the axis
1038 VectorCopy( s1->angles, cent->lerpAngles );
1039
1040 // add trails
1041 if ( cent->currentState.eType == ET_FP_PARTS
1042 || cent->currentState.eType == ET_FIRE_COLUMN
1043 || cent->currentState.eType == ET_FIRE_COLUMN_SMOKE
1044 || cent->currentState.eType == ET_RAMJET ) {
1045 CG_RocketTrail( cent, NULL );
1046 } else if ( weapon->missileTrailFunc ) {
1047 weapon->missileTrailFunc( cent, weapon );
1048 }
1049
1050 // add dynamic light
1051 if ( weapon->missileDlight ) {
1052 trap_R_AddLightToScene( cent->lerpOrigin, weapon->missileDlight,
1053 weapon->missileDlightColor[0], weapon->missileDlightColor[1], weapon->missileDlightColor[2], 0 );
1054 }
1055
1056 //----(SA) whoops, didn't mean to check it in with the missile flare
1057
1058 // add missile sound
1059 if ( weapon->missileSound ) {
1060 vec3_t velocity;
1061
1062 BG_EvaluateTrajectoryDelta( ¢->currentState.pos, cg.time, velocity );
1063 CG_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, velocity, weapon->missileSound, 255 );
1064 }
1065
1066 // DHM - Nerve :: Don't tick until armed
1067 if ( cgs.gametype >= GT_WOLF && cent->currentState.weapon == WP_DYNAMITE ) {
1068 if ( cent->currentState.teamNum < 4 ) {
1069 vec3_t velocity;
1070
1071 BG_EvaluateTrajectoryDelta( ¢->currentState.pos, cg.time, velocity );
1072 CG_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, velocity, weapon->spindownSound, 255 );
1073 }
1074 }
1075
1076 // create the render entity
1077 memset( &ent, 0, sizeof( ent ) );
1078 VectorCopy( cent->lerpOrigin, ent.origin );
1079 VectorCopy( cent->lerpOrigin, ent.oldorigin );
1080
1081 //----(SA) removed plasma gun code as sp5 is taking that spot
1082
1083 // flicker between two skins
1084 ent.skinNum = cg.clientFrame & 1;
1085
1086 if ( cent->currentState.eType == ET_FP_PARTS ) {
1087 ent.hModel = cgs.gameModels[cent->currentState.modelindex];
1088 } else if ( cent->currentState.eType == ET_EXPLO_PART ) {
1089 ent.hModel = cgs.gameModels[cent->currentState.modelindex];
1090 } else if ( cent->currentState.eType == ET_FLAMEBARREL ) {
1091 ent.hModel = cgs.media.flamebarrel;
1092 } else if ( cent->currentState.eType == ET_FIRE_COLUMN || cent->currentState.eType == ET_FIRE_COLUMN_SMOKE ) {
1093 // it may have a model sometime in the future
1094 ent.hModel = 0;
1095 } else if ( cent->currentState.eType == ET_RAMJET ) {
1096 ent.hModel = 0;
1097 }
1098 // ent.hModel = cgs.gameModels[cent->currentState.modelindex];
1099 else {
1100 ent.hModel = weapon->missileModel;
1101 }
1102 ent.renderfx = weapon->missileRenderfx | RF_NOSHADOW;
1103
1104 // convert direction of travel into axis
1105 if ( VectorNormalize2( s1->pos.trDelta, ent.axis[0] ) == 0 ) {
1106 ent.axis[0][2] = 1;
1107 }
1108
1109 // spin as it moves
1110 if ( s1->pos.trType != TR_STATIONARY ) {
1111 RotateAroundDirection( ent.axis, cg.time / 4 );
1112 } else {
1113 RotateAroundDirection( ent.axis, s1->time );
1114 }
1115
1116 // Rafael
1117 // Added this since it may be a propExlosion
1118 if ( ent.hModel ) {
1119 // add to refresh list, possibly with quad glow
1120 CG_AddRefEntityWithPowerups( &ent, s1->powerups, TEAM_FREE, s1, vec3_origin );
1121 }
1122
1123 }
1124
1125 /*
1126 ===============
1127 CG_Bat
1128
1129 RF, a bat now is actually a spirit
1130 ===============
1131 */
CG_Bat(centity_t * cent)1132 static void CG_Bat( centity_t *cent ) {
1133 CG_ParticleBat( cent );
1134 CG_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, cgs.media.batsFlyingLoopSound, 5 );
1135 }
1136
1137 static animation_t grabberAnims[6];
1138 static animation_t footlockerAnims[3];
1139 static animation_t multi_flagpoleAnims[9];
1140
1141 typedef struct {
1142 char *name; // for QVM this cannot be char name[MAX_QPATH];
1143 int firstFrame;
1144 int numFrames;
1145 int loopFrames; // 0 to numFrames
1146 int frameLerp; // msec between frames
1147 int initialLerp; // msec to get to first frame
1148 int moveSpeed;
1149 int animBlend; // take this long to blend to next anim
1150 //
1151 // derived
1152 //
1153 int duration;
1154 int nameHash;
1155 int flags;
1156 int movetype;
1157 } animationInline_t;
1158
CG_InlineAnimToFullAnim(const animationInline_t * inl,animation_t * anim)1159 static void CG_InlineAnimToFullAnim( const animationInline_t *inl, animation_t *anim ) {
1160 Com_Memset( anim, 0, sizeof ( animation_t ) );
1161 anim->firstFrame = inl->firstFrame;
1162 anim->numFrames = inl->numFrames;
1163 anim->loopFrames = inl->loopFrames;
1164 anim->frameLerp = inl->frameLerp;
1165 anim->initialLerp = inl->initialLerp;
1166 }
1167
1168 //----(SA) animation_t struct changed, so changes are to keep this working
1169 static animationInline_t grabberAnimsInline[6] = {
1170 {"", 0, 6, 6, 1000 / 5, 1000 / 5 }, // (main idle)
1171 {"", 5, 21, 21, 1000 / 7, 1000 / 7 }, // (random idle)
1172 {"", 25, 11, 0, 1000 / 15, 1000 / 15 }, // (attack big swipe)
1173 {"", 35, 16, 0, 1000 / 15, 1000 / 15 }, // (attack small swipe)
1174 {"", 50, 16, 0, 1000 / 15, 1000 / 15 }, // (attack grab)
1175 {"", 66, 1, 0, 1000 / 15, 1000 / 15 } // (starting position)
1176 };
1177
1178 //----(SA) added
1179 static animationInline_t footlockerAnimsInline[3] = {
1180 {"", 0, 1, 1, 1000 / 5, 1000 / 5 }, // (main idle)
1181 {"", 0, 5, 5, 1000 / 5, 1000 / 5 }, // (lock rattle)
1182 {"", 5, 6, 0, 1000 / 5, 1000 / 5 } // (break open)
1183 };
1184
1185 //----(SA) end
1186
1187 // DHM - Nerve :: capture and hold flag
1188
1189 static animationInline_t multi_flagpoleAnimsInline[9] = {
1190 {"", 0, 1, 0, 1000 / 15, 1000 / 15 }, // (no flags, idle)
1191 {"", 0, 15, 0, 1000 / 15, 1000 / 15 }, // (axis flag rising)
1192 {"", 490, 15, 0, 1000 / 15, 1000 / 15 }, // (american flag rising)
1193 {"", 20, 211, 211, 1000 / 15, 1000 / 15 }, // (axis flag raised)
1194 {"", 255, 211, 211, 1000 / 15, 1000 / 15 }, // (american flag raised)
1195 {"", 235, 15, 0, 1000 / 15, 1000 / 15 }, // (axis switching to american)
1196 {"", 470, 15, 0, 1000 / 15, 1000 / 15 }, // (american switching to axis)
1197 {"", 510, 15, 0, 1000 / 15, 1000 / 15 }, // (axis flag falling)
1198 {"", 530, 15, 0, 1000 / 15, 1000 / 15 } // (american flag falling)
1199 };
1200
1201 // dhm - end
1202
1203 /*
1204 ===============
1205 CG_InitTrapAnimations
1206 ===============
1207 */
CG_InitTrapAnimations(void)1208 void CG_InitTrapAnimations( void ) {
1209 int i;
1210
1211 for ( i = 0; i < ARRAY_LEN(grabberAnims); i++ ) {
1212 CG_InlineAnimToFullAnim( &grabberAnimsInline[i], &grabberAnims[i] );
1213 }
1214 for ( i = 0; i < ARRAY_LEN(footlockerAnims); i++ ) {
1215 CG_InlineAnimToFullAnim( &footlockerAnimsInline[i], &footlockerAnims[i] );
1216 }
1217 for ( i = 0; i < ARRAY_LEN(multi_flagpoleAnims); i++ ) {
1218 CG_InlineAnimToFullAnim( &multi_flagpoleAnimsInline[i], &multi_flagpoleAnims[i] );
1219 }
1220 }
1221
1222 extern void CG_RunLerpFrame( clientInfo_t *ci, lerpFrame_t *lf, int newAnimation, float speedScale );
1223
1224
1225 /*
1226 ==============
1227 CG_TrapSetAnim
1228 ==============
1229 */
CG_TrapSetAnim(centity_t * cent,lerpFrame_t * lf,int newAnim)1230 static void CG_TrapSetAnim( centity_t *cent, lerpFrame_t *lf, int newAnim ) {
1231 // transition animation
1232 lf->animationNumber = cent->currentState.frame;
1233
1234 if ( 0 ) {
1235 lf->animation = &grabberAnims[cent->currentState.frame];
1236 } else {
1237 lf->animation = &footlockerAnims[cent->currentState.frame];
1238 }
1239
1240 // DHM - Nerve :: teamNum specifies which set of animations to use (only 1 exists right now)
1241 if ( cgs.gametype >= GT_WOLF ) {
1242 switch ( cent->currentState.teamNum ) {
1243
1244 case 1:
1245 lf->animation = &multi_flagpoleAnims[ cent->currentState.frame ];
1246 break;
1247 default:
1248 // Keep what was set above
1249 break;
1250 }
1251 }
1252 // dhm - end
1253
1254 lf->animationTime = lf->frameTime + lf->animation->initialLerp;
1255 }
1256
1257 /*
1258 ==============
1259 CG_Trap
1260 // TODO: change from 'trap' to something else. 'trap' is a misnomer. it's actually used for other stuff too
1261 ==============
1262 */
CG_Trap(centity_t * cent)1263 static void CG_Trap( centity_t *cent ) {
1264 refEntity_t ent;
1265 entityState_t *cs;
1266 lerpFrame_t *traplf;
1267
1268 memset( &ent, 0, sizeof( ent ) );
1269
1270 cs = ¢->currentState;
1271
1272 traplf = ¢->lerpFrame;
1273
1274 // initial setup
1275 if ( !traplf->oldFrameTime ) {
1276 traplf->frameTime =
1277 traplf->oldFrameTime = cg.time;
1278
1279 CG_TrapSetAnim( cent, traplf, cs->frame );
1280
1281 traplf->frame =
1282 traplf->oldFrame = traplf->animation->firstFrame;
1283 }
1284
1285 // transition to new anim if requested
1286 if ( ( traplf->animationNumber != cs->frame ) || !traplf->animation ) {
1287 CG_TrapSetAnim( cent, traplf, cs->frame );
1288 }
1289
1290 CG_RunLerpFrame( NULL, traplf, 0, 1 ); // use existing lerp code rather than re-writing
1291
1292 ent.frame = traplf->frame;
1293 ent.oldframe = traplf->oldFrame;
1294 ent.backlerp = traplf->backlerp;
1295
1296 VectorCopy( cent->lerpOrigin, ent.origin );
1297 VectorCopy( cent->lerpOrigin, ent.oldorigin );
1298
1299 ent.hModel = cgs.gameModels[cs->modelindex];
1300
1301 AnglesToAxis( cent->lerpAngles, ent.axis );
1302
1303 trap_R_AddRefEntityToScene( &ent );
1304
1305 memcpy( ¢->refEnt, &ent, sizeof( refEntity_t ) );
1306 }
1307 //----(SA) end
1308
1309
1310 /*
1311 ==============
1312 CG_Corona
1313 ==============
1314 */
CG_Corona(centity_t * cent)1315 static void CG_Corona( centity_t *cent ) {
1316 trace_t tr;
1317 int r, g, b;
1318 int dli;
1319 qboolean visible = qfalse,
1320 behind = qfalse,
1321 toofar = qfalse;
1322
1323 float dot, dist;
1324 vec3_t dir;
1325
1326 if ( cg_coronas.integer == 0 ) { // if set to '0' no coronas
1327 return;
1328 }
1329
1330 dli = cent->currentState.dl_intensity;
1331 r = dli & 255;
1332 g = ( dli >> 8 ) & 255;
1333 b = ( dli >> 16 ) & 255;
1334
1335 // only coronas that are in your PVS are being added
1336
1337 VectorSubtract( cg.refdef.vieworg, cent->lerpOrigin, dir );
1338
1339 dist = VectorNormalize2( dir, dir );
1340 if ( dist > cg_coronafardist.integer ) { // performance variable cg_coronafardist will keep down super long traces
1341 toofar = qtrue;
1342 }
1343
1344 dot = DotProduct( dir, cg.refdef.viewaxis[0] );
1345 if ( dot >= -0.6 ) { // assumes ~90 deg fov (SA) changed value to 0.6 (screen corner at 90 fov)
1346 behind = qtrue; // use the dot to at least do trivial removal of those behind you.
1347 }
1348 // yeah, I could calc side planes to clip against, but would that be worth it? (much better than dumb dot>= thing?)
1349
1350 // CG_Printf("dot: %f\n", dot);
1351
1352 if ( cg_coronas.integer == 2 ) { // if set to '2' trace everything
1353 behind = qfalse;
1354 toofar = qfalse;
1355 }
1356
1357
1358 if ( !behind && !toofar ) {
1359 CG_Trace( &tr, cg.refdef.vieworg, NULL, NULL, cent->lerpOrigin, -1, MASK_SOLID | CONTENTS_BODY ); // added blockage by players. not sure how this is going to be since this is their bb, not their model (too much blockage)
1360
1361 if ( tr.fraction == 1 ) {
1362 visible = qtrue;
1363 }
1364
1365 trap_R_AddCoronaToScene( cent->lerpOrigin, (float)r / 255.0f, (float)g / 255.0f, (float)b / 255.0f, (float)cent->currentState.density / 255.0f, cent->currentState.number, visible );
1366 }
1367 }
1368
1369
1370 /*
1371 ==============
1372 CG_Efx
1373 ==============
1374 */
CG_Efx(centity_t * cent)1375 static void CG_Efx( centity_t *cent ) {
1376 int i;
1377 float rnd;
1378 trace_t trace;
1379 vec3_t perpvec;
1380 vec3_t stickPoint;
1381 float movePerUpdate;
1382
1383 if ( cent->currentState.eType == ET_EF_TESLA ) {
1384 rnd = cent->currentState.angles2[0];
1385
1386 for ( i = 0; i < MAX_TESLA_BOLTS; i++ ) {
1387 if ( cent->boltTimes[i] < cg.time ) {
1388 VectorSet( cent->boltLocs[i], crandom(), crandom(), crandom() );
1389 VectorNormalize2( cent->boltLocs[i], cent->boltLocs[i] );
1390 VectorMA( cent->currentState.origin2, rnd, cent->boltLocs[i], cent->boltLocs[i] );
1391
1392 cent->boltTimes[i] = cg.time + rand() % cent->currentState.time2; // hold this position for ~1 second ('stickytime' value is stored in .time2)
1393
1394 // cut the bolt short if it collides w/ something
1395 CG_Trace( &trace, cent->currentState.origin, NULL, NULL, cent->boltLocs[i], -1, MASK_SOLID | CONTENTS_BODY );
1396
1397 if ( trace.fraction < 1 ) {
1398 // take damage
1399 if ( trace.entityNum != ENTITYNUM_WORLD ) {
1400 // CG_ClientDamage(trace.entityNum, cent->currentState.number, CLDMG_TESLA);
1401 // cg_entities[trace.entityNum].pe.teslaDamagedTime = cg.time;
1402 }
1403
1404 VectorCopy( trace.endpos, cent->boltLocs[i] );
1405
1406 // store perpendicular vector so end can 'crawl'
1407 PerpendicularVector( perpvec, trace.plane.normal );
1408
1409 RotatePointAroundVector( stickPoint, trace.plane.normal, perpvec, crandom() * 360 );
1410
1411 // scale it so it won't move too far with bolts that have long boltTimer's
1412 movePerUpdate = 1.0f / (float)( cent->boltTimes[i] - cg.time );
1413
1414 // move a max of 64 away from the 'original' target location
1415 VectorScale( stickPoint, movePerUpdate * trace.fraction * 64.0f, cent->boltCrawlDirs[i] );
1416
1417 } else {
1418 VectorSet( cent->boltCrawlDirs[i], 0, 0, 0 );
1419 }
1420
1421 }
1422 }
1423
1424 for ( i = 0; i < MAX_TESLA_BOLTS; i++ ) {
1425
1426 if ( cent->boltCrawlDirs[0] ) {
1427 VectorMA( cent->boltLocs[i], cent->boltTimes[i] - cg.time, cent->boltCrawlDirs[i], perpvec );
1428 } else {
1429 VectorCopy( cent->boltLocs[i], perpvec );
1430 }
1431
1432 CG_DynamicLightningBolt( cgs.media.lightningBoltShader, // shader
1433 cent->currentState.origin, // start
1434 perpvec, // end
1435 cent->currentState.density, // numBolts
1436 cent->currentState.frame, // maxWidth
1437 qtrue, // fade
1438 1.0, // startAlpha
1439 0, // recursion
1440 i * i * 2 ); // randseed
1441 }
1442
1443 // add dlight
1444 if ( cent->currentState.dl_intensity ) {
1445 int r, g, b;
1446 int dli;
1447 int randomness = cent->currentState.time2;
1448
1449 if ( ( cg.time / 50 ) % ( randomness + ( cg.time % randomness ) ) == 0 ) {
1450 dli = cent->currentState.dl_intensity;
1451 r = dli & 255;
1452 g = ( dli >> 8 ) & 255;
1453 b = ( dli >> 16 ) & 255;
1454 trap_R_AddLightToScene( cent->currentState.origin, cent->currentState.time, (float)r / 255.0f, (float)g / 255.0f, (float)b / 255.0f, 0 );
1455 }
1456 }
1457 } else if ( cent->currentState.eType == ET_EF_SPOTLIGHT ) {
1458
1459 vec3_t targetpos, normalized_direction, direction;
1460 float dist, fov = 90;
1461 vec4_t color = {1, 1, 1, .1};
1462 int splinetarget = 0;
1463 char *cs;
1464
1465 VectorCopy( cent->currentState.origin2, targetpos );
1466
1467 splinetarget = cent->overheatTime;
1468
1469 if ( !splinetarget ) {
1470 cs = (char *)CG_ConfigString( CS_SPLINES + cent->currentState.density );
1471 cent->overheatTime = splinetarget = CG_LoadCamera( va( "cameras/%s.camera", cs ) );
1472 if ( splinetarget != -1 ) {
1473 trap_startCamera( splinetarget, cg.time );
1474 }
1475 } else {
1476 vec3_t angles;
1477 if ( splinetarget != -1 ) {
1478 if ( trap_getCameraInfo( splinetarget, cg.time, &targetpos, &angles, &fov ) ) {
1479
1480 } else { // loop
1481 trap_startCamera( splinetarget, cg.time );
1482 trap_getCameraInfo( splinetarget, cg.time, &targetpos, &angles, &fov );
1483 }
1484 }
1485 }
1486
1487
1488 normalized_direction[0] = direction[0] = targetpos[0] - cent->currentState.origin[0];
1489 normalized_direction[1] = direction[1] = targetpos[1] - cent->currentState.origin[1];
1490 normalized_direction[2] = direction[2] = targetpos[2] - cent->currentState.origin[2];
1491
1492 dist = VectorNormalize( normalized_direction );
1493
1494 if ( dist == 0 ) {
1495 return;
1496 }
1497
1498 CG_Spotlight( cent, color, cent->currentState.origin, normalized_direction, 999, 2048, 10, fov, 0 );
1499 }
1500 }
1501
1502
1503 //----(SA) adding func_explosive
1504
1505 /*
1506 ===============
1507 CG_Explosive
1508 This is currently almost exactly the same as CG_Mover
1509 It's split out so that any changes or experiments are
1510 unattached to anything else.
1511 ===============
1512 */
CG_Explosive(centity_t * cent)1513 static void CG_Explosive( centity_t *cent ) {
1514 refEntity_t ent;
1515 entityState_t *s1;
1516
1517 s1 = ¢->currentState;
1518
1519
1520 // create the render entity
1521 memset( &ent, 0, sizeof( ent ) );
1522 // VectorCopy( cent->lerpOrigin, ent.origin);
1523 VectorCopy( cent->lerpOrigin, ent.oldorigin );
1524 // VectorCopy( ent.origin, cent->lerpOrigin);
1525 AnglesToAxis( cent->lerpAngles, ent.axis );
1526
1527 ent.renderfx = RF_NOSHADOW;
1528
1529 // get the model, either as a bmodel or a modelindex
1530 if ( s1->solid == SOLID_BMODEL ) {
1531 ent.hModel = cgs.inlineDrawModel[s1->modelindex];
1532 } else {
1533 ent.hModel = cgs.gameModels[s1->modelindex];
1534 }
1535
1536 // add to refresh list
1537 // trap_R_AddRefEntityToScene(&ent);
1538
1539 // add the secondary model
1540 if ( s1->modelindex2 ) {
1541 ent.skinNum = 0;
1542 ent.hModel = cgs.gameModels[s1->modelindex2];
1543 trap_R_AddRefEntityToScene( &ent );
1544 } else {
1545 trap_R_AddRefEntityToScene( &ent );
1546 }
1547
1548 }
1549
1550 //----(SA) done
1551
1552 // declaration for add bullet particles (might as well stick this one in a .h file I think)
1553 extern void CG_AddBulletParticles( vec3_t origin, vec3_t dir, int speed, int duration, int count, float randScale );
1554
1555 /*
1556 ===============
1557 CG_Mover
1558 ===============
1559 */
CG_Mover(centity_t * cent)1560 static void CG_Mover( centity_t *cent ) {
1561 refEntity_t ent;
1562 entityState_t *s1;
1563
1564 s1 = ¢->currentState;
1565
1566 // create the render entity
1567 memset( &ent, 0, sizeof( ent ) );
1568
1569 VectorCopy( cent->lerpOrigin, ent.origin );
1570 VectorCopy( cent->lerpOrigin, ent.oldorigin );
1571
1572 AnglesToAxis( cent->lerpAngles, ent.axis );
1573
1574 ent.renderfx = RF_NOSHADOW;
1575
1576 // flicker between two skins (FIXME?)
1577 ent.skinNum = 0;
1578
1579 // get the model, either as a bmodel or a modelindex
1580 if ( s1->solid == SOLID_BMODEL ) {
1581 ent.hModel = cgs.inlineDrawModel[s1->modelindex];
1582 } else {
1583 ent.hModel = cgs.gameModels[s1->modelindex];
1584 }
1585
1586 // Rafael
1587 // testing for mike to get movers to scale
1588 if ( cent->currentState.density == ET_MOVERSCALED ) {
1589 VectorScale( ent.axis[0], cent->currentState.angles2[0], ent.axis[0] );
1590 VectorScale( ent.axis[1], cent->currentState.angles2[1], ent.axis[1] );
1591 VectorScale( ent.axis[2], cent->currentState.angles2[2], ent.axis[2] );
1592 ent.nonNormalizedAxes = qtrue;
1593 }
1594
1595
1596 //----(SA) added
1597 if ( cent->currentState.eType == ET_ALARMBOX ) {
1598 ent.renderfx |= RF_MINLIGHT;
1599 }
1600 //----(SA) end
1601
1602
1603 // add the secondary model
1604 if ( s1->modelindex2 ) {
1605 ent.skinNum = 0;
1606 ent.hModel = cgs.gameModels[s1->modelindex2];
1607 ent.frame = s1->frame;
1608 trap_R_AddRefEntityToScene( &ent );
1609 memcpy( ¢->refEnt, &ent, sizeof( refEntity_t ) );
1610 } else {
1611 trap_R_AddRefEntityToScene( &ent );
1612 }
1613
1614 // add propeller and sfx to me109
1615 if ( cent->currentState.density == 7 || cent->currentState.density == 8 ) {
1616 refEntity_t propeller;
1617 vec3_t angles;
1618
1619 memset( &propeller, 0, sizeof( propeller ) );
1620 VectorCopy( ent.lightingOrigin, propeller.lightingOrigin );
1621 propeller.shadowPlane = ent.shadowPlane;
1622 propeller.renderfx = ent.renderfx;
1623
1624 propeller.hModel = propellerModel;
1625
1626 angles[PITCH] = cg.time % 16;
1627
1628 AnglesToAxis( angles, propeller.axis );
1629
1630 CG_PositionRotatedEntityOnTag( &propeller, &ent, "tag_prop" );
1631
1632 trap_R_AddRefEntityToScene( &propeller );
1633
1634 if ( cent->currentState.density == 8 ) {
1635 refEntity_t flash;
1636 vec3_t angles;
1637
1638 angles[YAW] = 90;
1639 angles[ROLL] = random() * 90;
1640
1641 memset( &flash, 0, sizeof( flash ) );
1642 flash.renderfx = ent.shadowPlane;
1643 //flash.hModel = cgs.media.mg42muzzleflash;
1644 flash.hModel = cgs.media.planemuzzleflash;
1645
1646 AnglesToAxis( angles, flash.axis );
1647 CG_PositionRotatedEntityOnTag( &flash, &ent, "tag_gun1" );
1648
1649 trap_R_AddRefEntityToScene( &flash );
1650 trap_R_AddLightToScene( flash.origin, 200 + ( rand() & 31 ),1.0, 0.6, 0.23, 0 );
1651
1652 memset( &flash, 0, sizeof( flash ) );
1653 flash.renderfx = ent.shadowPlane;
1654 //flash.hModel = cgs.media.mg42muzzleflash;
1655 flash.hModel = cgs.media.planemuzzleflash;
1656
1657 AnglesToAxis( angles, flash.axis );
1658 CG_PositionRotatedEntityOnTag( &flash, &ent, "tag_gun02" );
1659
1660 trap_R_AddRefEntityToScene( &flash );
1661 trap_R_AddLightToScene( flash.origin, 200 + ( rand() & 31 ),1.0, 0.6, 0.23, 0 );
1662 }
1663 }
1664
1665 // alarm box spark effects
1666
1667 if ( cent->currentState.eType == ET_ALARMBOX ) {
1668 if ( cent->currentState.frame == 2 ) { // i'm dead
1669 if ( rand() % 50 == 1 ) {
1670 vec3_t angNorm; // normalized angles
1671 VectorNormalize2( cent->lerpAngles, angNorm );
1672 // (origin, dir, speed, duration, count, 'randscale')
1673 CG_AddBulletParticles( cent->lerpOrigin, angNorm, 2, 800, 4, 16.0f );
1674 trap_S_StartSound( NULL, cent->currentState.number, CHAN_AUTO, cgs.media.sparkSounds[0] );
1675 }
1676 }
1677 }
1678 }
1679
1680 /*
1681 ===============
1682 CG_Beam
1683
1684 Also called as an event
1685 ===============
1686 */
CG_Beam(centity_t * cent)1687 void CG_Beam( centity_t *cent ) {
1688 refEntity_t ent;
1689 entityState_t *s1;
1690
1691 s1 = ¢->currentState;
1692
1693 // create the render entity
1694 memset( &ent, 0, sizeof( ent ) );
1695 VectorCopy( s1->pos.trBase, ent.origin );
1696 VectorCopy( s1->origin2, ent.oldorigin );
1697
1698 AxisClear( ent.axis );
1699 ent.reType = RT_BEAM;
1700
1701 ent.renderfx = RF_NOSHADOW;
1702
1703 // add to refresh list
1704 trap_R_AddRefEntityToScene( &ent );
1705 }
1706
1707
1708 /*
1709 ===============
1710 CG_Portal
1711 ===============
1712 */
CG_Portal(centity_t * cent)1713 static void CG_Portal( centity_t *cent ) {
1714 refEntity_t ent;
1715 entityState_t *s1;
1716
1717 s1 = ¢->currentState;
1718
1719 // create the render entity
1720 memset( &ent, 0, sizeof( ent ) );
1721 VectorCopy( cent->lerpOrigin, ent.origin );
1722 VectorCopy( s1->origin2, ent.oldorigin );
1723 ByteToDir( s1->eventParm, ent.axis[0] );
1724 PerpendicularVector( ent.axis[1], ent.axis[0] );
1725
1726 // negating this tends to get the directions like they want
1727 // we really should have a camera roll value
1728 VectorSubtract( vec3_origin, ent.axis[1], ent.axis[1] );
1729
1730 CrossProduct( ent.axis[0], ent.axis[1], ent.axis[2] );
1731 ent.reType = RT_PORTALSURFACE;
1732 ent.frame = s1->frame; // rotation speed
1733 ent.skinNum = s1->clientNum / 256.0 * 360; // roll offset
1734
1735 // add to refresh list
1736 trap_R_AddRefEntityToScene( &ent );
1737 }
1738
1739 /*
1740 ===============
1741 CG_Prop
1742 ===============
1743 */
CG_Prop(centity_t * cent)1744 static void CG_Prop( centity_t *cent ) {
1745 refEntity_t ent;
1746 entityState_t *s1;
1747 vec3_t angles;
1748 float scale;
1749
1750 s1 = ¢->currentState;
1751
1752 // create the render entity
1753 memset( &ent, 0, sizeof( ent ) );
1754
1755 if ( cg.renderingThirdPerson ) {
1756 VectorCopy( cent->lerpOrigin, ent.origin );
1757 VectorCopy( cent->lerpOrigin, ent.oldorigin );
1758
1759 ent.frame = s1->frame;
1760 ent.oldframe = ent.frame;
1761 ent.backlerp = 0;
1762 } else
1763 {
1764 VectorCopy( cg.refdef.vieworg, ent.origin );
1765 VectorCopy( cg.refdefViewAngles, angles );
1766
1767 if ( cg.bobcycle & 1 ) {
1768 scale = -cg.xyspeed;
1769 } else {
1770 scale = cg.xyspeed;
1771 }
1772
1773 // modify angles from bobbing
1774 angles[ROLL] += scale * cg.bobfracsin * 0.005;
1775 angles[YAW] += scale * cg.bobfracsin * 0.01;
1776 angles[PITCH] += cg.xyspeed * cg.bobfracsin * 0.005;
1777
1778 VectorCopy( angles, cent->lerpAngles );
1779
1780 ent.frame = s1->frame;
1781 ent.oldframe = ent.frame;
1782 ent.backlerp = 0;
1783
1784 if ( cent->currentState.density ) {
1785 ent.frame = s1->frame + cent->currentState.density;
1786 ent.oldframe = ent.frame - 1;
1787 ent.backlerp = 1 - cg.frameInterpolation;
1788 ent.renderfx = RF_DEPTHHACK | RF_FIRST_PERSON;
1789
1790 //CG_Printf ("frame %d oldframe %d\n", ent.frame, ent.oldframe);
1791 } else if ( ent.frame ) {
1792 ent.oldframe -= 1;
1793 ent.backlerp = 1 - cg.frameInterpolation;
1794 } else
1795 {
1796 ent.renderfx = RF_DEPTHHACK | RF_FIRST_PERSON;
1797 }
1798 }
1799
1800 AnglesToAxis( cent->lerpAngles, ent.axis );
1801
1802 ent.renderfx |= RF_NOSHADOW;
1803
1804 // flicker between two skins (FIXME?)
1805 ent.skinNum = ( cg.time >> 6 ) & 1;
1806
1807 // get the model, either as a bmodel or a modelindex
1808 if ( s1->solid == SOLID_BMODEL ) {
1809 ent.hModel = cgs.inlineDrawModel[s1->modelindex];
1810 } else {
1811 ent.hModel = cgs.gameModels[s1->modelindex];
1812 }
1813
1814 // add the secondary model
1815 if ( s1->modelindex2 ) {
1816 ent.skinNum = 0;
1817 ent.hModel = cgs.gameModels[s1->modelindex2];
1818 ent.frame = s1->frame;
1819 trap_R_AddRefEntityToScene( &ent );
1820 memcpy( ¢->refEnt, &ent, sizeof( refEntity_t ) );
1821 } else {
1822 trap_R_AddRefEntityToScene( &ent );
1823 }
1824
1825 }
1826
1827 /*
1828 ================
1829 CG_CreateRotationMatrix
1830 ================
1831 */
CG_CreateRotationMatrix(vec3_t angles,vec3_t matrix[3])1832 void CG_CreateRotationMatrix(vec3_t angles, vec3_t matrix[3]) {
1833 AngleVectors(angles, matrix[0], matrix[1], matrix[2]);
1834 VectorInverse(matrix[1]);
1835 }
1836
1837 /*
1838 ================
1839 CG_TransposeMatrix
1840 ================
1841 */
CG_TransposeMatrix(vec3_t matrix[3],vec3_t transpose[3])1842 void CG_TransposeMatrix(vec3_t matrix[3], vec3_t transpose[3]) {
1843 int i, j;
1844 for (i = 0; i < 3; i++) {
1845 for (j = 0; j < 3; j++) {
1846 transpose[i][j] = matrix[j][i];
1847 }
1848 }
1849 }
1850
1851 /*
1852 ================
1853 CG_RotatePoint
1854 ================
1855 */
CG_RotatePoint(vec3_t point,vec3_t matrix[3])1856 void CG_RotatePoint(vec3_t point, vec3_t matrix[3]) {
1857 vec3_t tvec;
1858 VectorCopy(point, tvec);
1859 point[0] = DotProduct(matrix[0], tvec);
1860 point[1] = DotProduct(matrix[1], tvec);
1861 point[2] = DotProduct(matrix[2], tvec);
1862 }
1863
1864 /*
1865 =========================
1866 CG_AdjustPositionForMover
1867
1868 Also called by client movement prediction code
1869 =========================
1870 */
CG_AdjustPositionForMover(const vec3_t in,int moverNum,int fromTime,int toTime,vec3_t out,vec3_t angles_in,vec3_t angles_out)1871 void CG_AdjustPositionForMover(const vec3_t in, int moverNum, int fromTime, int toTime, vec3_t out, vec3_t angles_in, vec3_t angles_out) {
1872 centity_t *cent;
1873 vec3_t oldOrigin, origin, deltaOrigin;
1874 vec3_t oldAngles, angles, deltaAngles;
1875 vec3_t matrix[3], transpose[3];
1876 vec3_t org, org2, move2;
1877
1878 // if ( outDeltaAngles ) {
1879 // VectorClear( outDeltaAngles );
1880 // }
1881
1882 if ( moverNum <= 0 || moverNum >= ENTITYNUM_MAX_NORMAL ) {
1883 VectorCopy( in, out );
1884 VectorCopy(angles_in, angles_out);
1885 return;
1886 }
1887
1888 cent = &cg_entities[ moverNum ];
1889
1890 if ( cent->currentState.eType != ET_MOVER ) {
1891 VectorCopy( in, out );
1892 VectorCopy(angles_in, angles_out);
1893 return;
1894 }
1895
1896 BG_EvaluateTrajectory( ¢->currentState.pos, fromTime, oldOrigin );
1897 BG_EvaluateTrajectory( ¢->currentState.apos, fromTime, oldAngles );
1898
1899 BG_EvaluateTrajectory( ¢->currentState.pos, toTime, origin );
1900 BG_EvaluateTrajectory( ¢->currentState.apos, toTime, angles );
1901
1902 VectorSubtract( origin, oldOrigin, deltaOrigin );
1903 VectorSubtract( angles, oldAngles, deltaAngles );
1904
1905 // origin change when on a rotating object
1906 CG_CreateRotationMatrix( deltaAngles, transpose );
1907 CG_TransposeMatrix( transpose, matrix );
1908 VectorSubtract( in, oldOrigin, org );
1909 VectorCopy( org, org2 );
1910 CG_RotatePoint( org2, matrix );
1911 VectorSubtract( org2, org, move2 );
1912 VectorAdd( deltaOrigin, move2, deltaOrigin );
1913
1914 VectorAdd( in, deltaOrigin, out );
1915 // if ( outDeltaAngles ) {
1916 // VectorCopy( deltaAngles, outDeltaAngles );
1917 // }
1918
1919 VectorAdd( angles_in, deltaAngles, angles_out );
1920 }
1921
1922
1923 /*
1924 =============================
1925 CG_InterpolateEntityPosition
1926 =============================
1927 */
CG_InterpolateEntityPosition(centity_t * cent)1928 static void CG_InterpolateEntityPosition( centity_t *cent ) {
1929 vec3_t current, next;
1930 float f;
1931
1932 // it would be an internal error to find an entity that interpolates without
1933 // a snapshot ahead of the current one
1934 if ( cg.nextSnap == NULL ) {
1935 // DHM - Nerve :: FIXME? There are some cases when in Limbo mode during a map restart
1936 // that were tripping this error.
1937 //CG_Error( "CG_InterpolateEntityPosition: cg.nextSnap == NULL" );
1938 //CG_Printf("CG_InterpolateEntityPosition: cg.nextSnap == NULL");
1939 return;
1940 }
1941
1942 f = cg.frameInterpolation;
1943
1944 // this will linearize a sine or parabolic curve, but it is important
1945 // to not extrapolate player positions if more recent data is available
1946 BG_EvaluateTrajectory( ¢->currentState.pos, cg.snap->serverTime, current );
1947 BG_EvaluateTrajectory( ¢->nextState.pos, cg.nextSnap->serverTime, next );
1948
1949 cent->lerpOrigin[0] = current[0] + f * ( next[0] - current[0] );
1950 cent->lerpOrigin[1] = current[1] + f * ( next[1] - current[1] );
1951 cent->lerpOrigin[2] = current[2] + f * ( next[2] - current[2] );
1952
1953 BG_EvaluateTrajectory( ¢->currentState.apos, cg.snap->serverTime, current );
1954 BG_EvaluateTrajectory( ¢->nextState.apos, cg.nextSnap->serverTime, next );
1955
1956 cent->lerpAngles[0] = LerpAngle( current[0], next[0], f );
1957 cent->lerpAngles[1] = LerpAngle( current[1], next[1], f );
1958 cent->lerpAngles[2] = LerpAngle( current[2], next[2], f );
1959
1960 }
1961
1962 /*
1963 ===============
1964 CG_CalcEntityLerpPositions
1965
1966 ===============
1967 */
CG_CalcEntityLerpPositions(centity_t * cent)1968 static void CG_CalcEntityLerpPositions( centity_t *cent ) {
1969 if ( cent->interpolate && cent->currentState.pos.trType == TR_INTERPOLATE ) {
1970 CG_InterpolateEntityPosition( cent );
1971 return;
1972 }
1973
1974 // NERVE - SMF - fix for jittery clients in multiplayer
1975 if ( cgs.gametype != GT_SINGLE_PLAYER ) {
1976 // first see if we can interpolate between two snaps for
1977 // linear extrapolated clients
1978 if ( cent->interpolate && cent->currentState.pos.trType == TR_LINEAR_STOP &&
1979 cent->currentState.number < MAX_CLIENTS ) {
1980 CG_InterpolateEntityPosition( cent );
1981 return;
1982 }
1983 }
1984 // -NERVE - SMF
1985
1986 // just use the current frame and evaluate as best we can
1987 BG_EvaluateTrajectory( ¢->currentState.pos, cg.time, cent->lerpOrigin );
1988 BG_EvaluateTrajectory( ¢->currentState.apos, cg.time, cent->lerpAngles );
1989
1990 // adjust for riding a mover if it wasn't rolled into the predicted
1991 // player state
1992 if ( cent != &cg.predictedPlayerEntity ) {
1993 CG_AdjustPositionForMover( cent->lerpOrigin, cent->currentState.groundEntityNum,
1994 cg.snap->serverTime, cg.time, cent->lerpOrigin, cent->lerpAngles, cent->lerpAngles);
1995 }
1996 }
1997
1998 /*
1999 ===============
2000 CG_ProcessEntity
2001 ===============
2002 */
CG_ProcessEntity(centity_t * cent)2003 static void CG_ProcessEntity( centity_t *cent ) {
2004 switch ( cent->currentState.eType ) {
2005 default:
2006 CG_Error( "Bad entity type: %i", cent->currentState.eType );
2007 break;
2008 case ET_CONCUSSIVE_TRIGGER:
2009 case ET_CAMERA:
2010 case ET_INVISIBLE:
2011 case ET_PUSH_TRIGGER:
2012 case ET_TELEPORT_TRIGGER:
2013 case ET_OID_TRIGGER:
2014 case ET_AI_EFFECT:
2015 case ET_EXPLOSIVE_INDICATOR: // NERVE - SMF
2016 break;
2017 case ET_SPEAKER:
2018 CG_Speaker( cent );
2019 break;
2020 case ET_GAMEMODEL:
2021 if ( !cg_drawGamemodels.integer ) {
2022 break;
2023 }
2024 case ET_MG42_BARREL:
2025 case ET_FOOTLOCKER:
2026 case ET_GENERAL:
2027 CG_General( cent );
2028 break;
2029 case ET_CORPSE:
2030 case ET_PLAYER:
2031 CG_Player( cent );
2032 break;
2033 case ET_ITEM:
2034 CG_Item( cent );
2035 break;
2036 case ET_MISSILE:
2037 case ET_FLAMEBARREL:
2038 case ET_FP_PARTS:
2039 case ET_FIRE_COLUMN:
2040 case ET_FIRE_COLUMN_SMOKE:
2041 case ET_EXPLO_PART:
2042 case ET_RAMJET:
2043 CG_Missile( cent );
2044 break;
2045 case ET_EF_TESLA:
2046 case ET_EF_SPOTLIGHT:
2047 case ET_EFFECT3:
2048 CG_Efx( cent );
2049 break;
2050 case ET_EXPLOSIVE:
2051 CG_Explosive( cent );
2052 break;
2053 case ET_TRAP:
2054 CG_Trap( cent );
2055 break;
2056 case ET_ALARMBOX:
2057 case ET_MOVER:
2058 CG_Mover( cent );
2059 break;
2060 case ET_PROP:
2061 CG_Prop( cent );
2062 break;
2063 case ET_BEAM:
2064 CG_Beam( cent );
2065 break;
2066 case ET_PORTAL:
2067 CG_Portal( cent );
2068 break;
2069 case ET_CORONA:
2070 CG_Corona( cent );
2071 break;
2072 case ET_BAT:
2073 CG_Bat( cent );
2074 break;
2075 case ET_SMOKER:
2076 CG_Smoker( cent );
2077 break;
2078 }
2079 }
2080
2081 /*
2082 ===============
2083 CG_AddCEntity
2084
2085 ===============
2086 */
CG_AddCEntity(centity_t * cent)2087 static void CG_AddCEntity( centity_t *cent ) {
2088 // event-only entities will have been dealt with already
2089 if ( cent->currentState.eType >= ET_EVENTS ) {
2090 return;
2091 }
2092
2093 cent->processedFrame = cg.clientFrame;
2094
2095 // calculate the current origin
2096 CG_CalcEntityLerpPositions( cent );
2097
2098 // add automatic effects
2099 CG_EntityEffects( cent );
2100
2101 // call the appropriate function which will add this entity to the view accordingly
2102 CG_ProcessEntity( cent );
2103 }
2104
2105 /*
2106 ==================
2107 CG_AddEntityToTag
2108 ==================
2109 */
CG_AddEntityToTag(centity_t * cent)2110 static void CG_AddEntityToTag( centity_t *cent ) {
2111 entityState_t *s1;
2112 centity_t *centParent;
2113 entityState_t *sParent;
2114 refEntity_t ent;
2115 char *cs, *token = NULL;
2116 int i, pi;
2117 vec3_t ang;
2118
2119 memset( &ent, 0, sizeof( ent ) );
2120
2121 // event-only entities will have been dealt with already
2122 if ( cent->currentState.eType >= ET_EVENTS ) {
2123 return;
2124 }
2125
2126 if ( cent->processedFrame == cg.clientFrame ) {
2127 // already processed this frame
2128 return;
2129 }
2130
2131 // calculate the current origin
2132 CG_CalcEntityLerpPositions( cent );
2133
2134 s1 = ¢->currentState;
2135
2136 // find us in the list of tagged entities
2137 sParent = NULL;
2138 centParent = NULL;
2139 for ( i = CS_TAGCONNECTS + 1; i < CS_TAGCONNECTS + MAX_TAGCONNECTS; i++ ) { // NOTE: +1 since G_FindConfigStringIndex() starts at index 1 rather than 0 (not sure why)
2140 cs = (char *)CG_ConfigString( i );
2141 token = COM_Parse( &cs );
2142 if ( !token[0] ) {
2143 break;
2144 }
2145 if ( atoi( token ) == s1->number ) {
2146 token = COM_Parse( &cs );
2147 if ( !token[0] ) {
2148 CG_Error( "CG_EntityTagConnected: missing parameter in configstring" );
2149 }
2150 pi = atoi( token );
2151 if ( pi < 0 || pi >= MAX_GENTITIES ) {
2152 CG_Error( "CG_EntityTagConnected: parent out of range" );
2153 }
2154 centParent = &cg_entities[pi];
2155 sParent = &( cg_entities[pi].currentState );
2156 token = COM_Parse( &cs );
2157 if ( !token[0] ) {
2158 CG_Error( "CG_EntityTagConnected: missing parameter in configstring" );
2159 }
2160
2161 // NOTE: token is now the tag name to attach to
2162
2163 break;
2164 }
2165 }
2166
2167 if ( !sParent ) {
2168 CG_Error( "CG_EntityTagConnected: unable to find configstring to perform connection" );
2169 }
2170
2171 // if parent isn't visible, then don't draw us
2172 if ( !centParent->currentValid ) {
2173 return;
2174 }
2175
2176 // make sure all parents are added first
2177 if ( centParent->processedFrame != cg.clientFrame ) {
2178 if ( sParent->eFlags & EF_TAGCONNECT ) {
2179 CG_AddEntityToTag( centParent );
2180 }
2181 }
2182
2183 // if there was a higher ranking parent not added to the scene, then don't add us
2184 if ( centParent->processedFrame != cg.clientFrame ) {
2185 return;
2186 }
2187
2188 cent->processedFrame = cg.clientFrame;
2189
2190 // start with default axis
2191 AnglesToAxis( vec3_origin, ent.axis );
2192
2193 // get the tag position from parent
2194 CG_PositionEntityOnTag( &ent, ¢Parent->refEnt, token, 0, NULL );
2195
2196 VectorCopy( ent.origin, cent->lerpOrigin );
2197 // we need to add the child's angles to the tag angles
2198 if ( !cent->currentState.density ) { // this entity should rotate with it's parent, but can turn around using it's own angles
2199 AxisToAngles( ent.axis, ang );
2200 VectorAdd( cent->lerpAngles, ang, cent->lerpAngles );
2201 } else { // face our angles exactly
2202 BG_EvaluateTrajectory( ¢->currentState.apos, cg.time, cent->lerpAngles );
2203 }
2204
2205 // add automatic effects
2206 CG_EntityEffects( cent );
2207
2208 // call the appropriate function which will add this entity to the view accordingly
2209 CG_ProcessEntity( cent );
2210 }
2211
2212 /*
2213 ===============
2214 CG_AddPacketEntities
2215
2216 ===============
2217 */
CG_AddPacketEntities(void)2218 void CG_AddPacketEntities( void ) {
2219 int num;
2220 centity_t *cent;
2221 playerState_t *ps;
2222 //int clcount;
2223
2224 // set cg.frameInterpolation
2225 if ( cg.nextSnap ) {
2226 int delta;
2227
2228 delta = ( cg.nextSnap->serverTime - cg.snap->serverTime );
2229 if ( delta == 0 ) {
2230 cg.frameInterpolation = 0;
2231 } else {
2232 cg.frameInterpolation = (float)( cg.time - cg.snap->serverTime ) / delta;
2233 }
2234 } else {
2235 cg.frameInterpolation = 0; // actually, it should never be used, because
2236 // no entities should be marked as interpolating
2237 }
2238
2239 // the auto-rotating items will all have the same axis
2240 cg.autoAnglesSlow[0] = 0;
2241 cg.autoAnglesSlow[1] = ( cg.time & 4095 ) * 360 / 4095.0;
2242 cg.autoAnglesSlow[2] = 0;
2243
2244 cg.autoAngles[0] = 0;
2245 cg.autoAngles[1] = ( cg.time & 2047 ) * 360 / 2048.0;
2246 cg.autoAngles[2] = 0;
2247
2248 cg.autoAnglesFast[0] = 0;
2249 cg.autoAnglesFast[1] = ( cg.time & 1023 ) * 360 / 1024.0f;
2250 cg.autoAnglesFast[2] = 0;
2251
2252 AnglesToAxis( cg.autoAnglesSlow, cg.autoAxisSlow );
2253 AnglesToAxis( cg.autoAngles, cg.autoAxis );
2254 AnglesToAxis( cg.autoAnglesFast, cg.autoAxisFast );
2255
2256 // generate and add the entity from the playerstate
2257 ps = &cg.predictedPlayerState;
2258 BG_PlayerStateToEntityState( ps, &cg.predictedPlayerEntity.currentState, qfalse );
2259 CG_AddCEntity( &cg.predictedPlayerEntity );
2260
2261 // lerp the non-predicted value for lightning gun origins
2262 CG_CalcEntityLerpPositions( &cg_entities[ cg.snap->ps.clientNum ] );
2263
2264 // add each entity sent over by the server
2265
2266 // NON TAG-CONNECTED ENTITIES
2267 for ( num = 0 ; num < cg.snap->numEntities ; num++ ) {
2268 cent = &cg_entities[ cg.snap->entities[ num ].number ];
2269 if ( !( cent->currentState.eFlags & EF_TAGCONNECT ) ) {
2270 CG_AddCEntity( cent );
2271 }
2272 }
2273
2274 // TAG-CONNECTED ENTITIES (connected to NON TAG-CONNECTED ENTITIES)
2275 for ( num = 0 ; num < cg.snap->numEntities ; num++ ) {
2276 cent = &cg_entities[ cg.snap->entities[ num ].number ];
2277 if ( cent->currentState.eFlags & EF_TAGCONNECT ) {
2278 CG_AddEntityToTag( cent );
2279 }
2280 }
2281
2282 // Ridah, add the flamethrower sounds
2283 CG_UpdateFlamethrowerSounds();
2284 }
2285