~ubuntu-branches/ubuntu/precise/openarena/precise

« back to all changes in this revision

Viewing changes to code/cgame/cg_players.c

  • Committer: Bazaar Package Importer
  • Author(s): Bruno "Fuddl" Kleinert
  • Date: 2007-01-20 12:28:09 UTC
  • Revision ID: james.westby@ubuntu.com-20070120122809-2yza5ojt7nqiyiam
Tags: upstream-0.6.0
ImportĀ upstreamĀ versionĀ 0.6.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
===========================================================================
 
3
Copyright (C) 1999-2005 Id Software, Inc.
 
4
 
 
5
This file is part of Quake III Arena source code.
 
6
 
 
7
Quake III Arena source code is free software; you can redistribute it
 
8
and/or modify it under the terms of the GNU General Public License as
 
9
published by the Free Software Foundation; either version 2 of the License,
 
10
or (at your option) any later version.
 
11
 
 
12
Quake III Arena source code is distributed in the hope that it will be
 
13
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 
14
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
15
GNU General Public License for more details.
 
16
 
 
17
You should have received a copy of the GNU General Public License
 
18
along with Quake III Arena source code; if not, write to the Free Software
 
19
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
20
===========================================================================
 
21
*/
 
22
//
 
23
// cg_players.c -- handle the media and animation for player entities
 
24
#include "cg_local.h"
 
25
 
 
26
char    *cg_customSoundNames[MAX_CUSTOM_SOUNDS] = {
 
27
        "*death1.wav",
 
28
        "*death2.wav",
 
29
        "*death3.wav",
 
30
        "*jump1.wav",
 
31
        "*pain25_1.wav",
 
32
        "*pain50_1.wav",
 
33
        "*pain75_1.wav",
 
34
        "*pain100_1.wav",
 
35
        "*falling1.wav",
 
36
        "*gasp.wav",
 
37
        "*drown.wav",
 
38
        "*fall1.wav",
 
39
        "*taunt.wav"
 
40
};
 
41
 
 
42
 
 
43
/*
 
44
================
 
45
CG_CustomSound
 
46
 
 
47
================
 
48
*/
 
49
sfxHandle_t     CG_CustomSound( int clientNum, const char *soundName ) {
 
50
        clientInfo_t *ci;
 
51
        int                     i;
 
52
 
 
53
        if ( soundName[0] != '*' ) {
 
54
                return trap_S_RegisterSound( soundName, qfalse );
 
55
        }
 
56
 
 
57
        if ( clientNum < 0 || clientNum >= MAX_CLIENTS ) {
 
58
                clientNum = 0;
 
59
        }
 
60
        ci = &cgs.clientinfo[ clientNum ];
 
61
 
 
62
        for ( i = 0 ; i < MAX_CUSTOM_SOUNDS && cg_customSoundNames[i] ; i++ ) {
 
63
                if ( !strcmp( soundName, cg_customSoundNames[i] ) ) {
 
64
                        return ci->sounds[i];
 
65
                }
 
66
        }
 
67
 
 
68
        CG_Error( "Unknown custom sound: %s", soundName );
 
69
        return 0;
 
70
}
 
71
 
 
72
 
 
73
 
 
74
/*
 
75
=============================================================================
 
76
 
 
77
CLIENT INFO
 
78
 
 
79
=============================================================================
 
80
*/
 
81
 
 
82
/*
 
83
======================
 
84
CG_ParseAnimationFile
 
85
 
 
86
Read a configuration file containing animation coutns and rates
 
87
models/players/visor/animation.cfg, etc
 
88
======================
 
89
*/
 
90
static qboolean CG_ParseAnimationFile( const char *filename, clientInfo_t *ci ) {
 
91
        char            *text_p, *prev;
 
92
        int                     len;
 
93
        int                     i;
 
94
        char            *token;
 
95
        float           fps;
 
96
        int                     skip;
 
97
        char            text[20000];
 
98
        fileHandle_t    f;
 
99
        animation_t *animations;
 
100
 
 
101
        animations = ci->animations;
 
102
 
 
103
        // load the file
 
104
        len = trap_FS_FOpenFile( filename, &f, FS_READ );
 
105
        if ( len <= 0 ) {
 
106
                return qfalse;
 
107
        }
 
108
        if ( len >= sizeof( text ) - 1 ) {
 
109
                CG_Printf( "File %s too long\n", filename );
 
110
                trap_FS_FCloseFile( f );
 
111
                return qfalse;
 
112
        }
 
113
        trap_FS_Read( text, len, f );
 
114
        text[len] = 0;
 
115
        trap_FS_FCloseFile( f );
 
116
 
 
117
        // parse the text
 
118
        text_p = text;
 
119
        skip = 0;       // quite the compiler warning
 
120
 
 
121
        ci->footsteps = FOOTSTEP_NORMAL;
 
122
        VectorClear( ci->headOffset );
 
123
        ci->gender = GENDER_MALE;
 
124
        ci->fixedlegs = qfalse;
 
125
        ci->fixedtorso = qfalse;
 
126
 
 
127
        // read optional parameters
 
128
        while ( 1 ) {
 
129
                prev = text_p;  // so we can unget
 
130
                token = COM_Parse( &text_p );
 
131
                if ( !token ) {
 
132
                        break;
 
133
                }
 
134
                if ( !Q_stricmp( token, "footsteps" ) ) {
 
135
                        token = COM_Parse( &text_p );
 
136
                        if ( !token ) {
 
137
                                break;
 
138
                        }
 
139
                        if ( !Q_stricmp( token, "default" ) || !Q_stricmp( token, "normal" ) ) {
 
140
                                ci->footsteps = FOOTSTEP_NORMAL;
 
141
                        } else if ( !Q_stricmp( token, "boot" ) ) {
 
142
                                ci->footsteps = FOOTSTEP_BOOT;
 
143
                        } else if ( !Q_stricmp( token, "flesh" ) ) {
 
144
                                ci->footsteps = FOOTSTEP_FLESH;
 
145
                        } else if ( !Q_stricmp( token, "mech" ) ) {
 
146
                                ci->footsteps = FOOTSTEP_MECH;
 
147
                        } else if ( !Q_stricmp( token, "energy" ) ) {
 
148
                                ci->footsteps = FOOTSTEP_ENERGY;
 
149
                        } else {
 
150
                                CG_Printf( "Bad footsteps parm in %s: %s\n", filename, token );
 
151
                        }
 
152
                        continue;
 
153
                } else if ( !Q_stricmp( token, "headoffset" ) ) {
 
154
                        for ( i = 0 ; i < 3 ; i++ ) {
 
155
                                token = COM_Parse( &text_p );
 
156
                                if ( !token ) {
 
157
                                        break;
 
158
                                }
 
159
                                ci->headOffset[i] = atof( token );
 
160
                        }
 
161
                        continue;
 
162
                } else if ( !Q_stricmp( token, "sex" ) ) {
 
163
                        token = COM_Parse( &text_p );
 
164
                        if ( !token ) {
 
165
                                break;
 
166
                        }
 
167
                        if ( token[0] == 'f' || token[0] == 'F' ) {
 
168
                                ci->gender = GENDER_FEMALE;
 
169
                        } else if ( token[0] == 'n' || token[0] == 'N' ) {
 
170
                                ci->gender = GENDER_NEUTER;
 
171
                        } else {
 
172
                                ci->gender = GENDER_MALE;
 
173
                        }
 
174
                        continue;
 
175
                } else if ( !Q_stricmp( token, "fixedlegs" ) ) {
 
176
                        ci->fixedlegs = qtrue;
 
177
                        continue;
 
178
                } else if ( !Q_stricmp( token, "fixedtorso" ) ) {
 
179
                        ci->fixedtorso = qtrue;
 
180
                        continue;
 
181
                }
 
182
 
 
183
                // if it is a number, start parsing animations
 
184
                if ( token[0] >= '0' && token[0] <= '9' ) {
 
185
                        text_p = prev;  // unget the token
 
186
                        break;
 
187
                }
 
188
                Com_Printf( "unknown token '%s' is %s\n", token, filename );
 
189
        }
 
190
 
 
191
        // read information for each frame
 
192
        for ( i = 0 ; i < MAX_ANIMATIONS ; i++ ) {
 
193
 
 
194
                token = COM_Parse( &text_p );
 
195
                if ( !*token ) {
 
196
                        if( i >= TORSO_GETFLAG && i <= TORSO_NEGATIVE ) {
 
197
                                animations[i].firstFrame = animations[TORSO_GESTURE].firstFrame;
 
198
                                animations[i].frameLerp = animations[TORSO_GESTURE].frameLerp;
 
199
                                animations[i].initialLerp = animations[TORSO_GESTURE].initialLerp;
 
200
                                animations[i].loopFrames = animations[TORSO_GESTURE].loopFrames;
 
201
                                animations[i].numFrames = animations[TORSO_GESTURE].numFrames;
 
202
                                animations[i].reversed = qfalse;
 
203
                                animations[i].flipflop = qfalse;
 
204
                                continue;
 
205
                        }
 
206
                        break;
 
207
                }
 
208
                animations[i].firstFrame = atoi( token );
 
209
                // leg only frames are adjusted to not count the upper body only frames
 
210
                if ( i == LEGS_WALKCR ) {
 
211
                        skip = animations[LEGS_WALKCR].firstFrame - animations[TORSO_GESTURE].firstFrame;
 
212
                }
 
213
                if ( i >= LEGS_WALKCR && i<TORSO_GETFLAG) {
 
214
                        animations[i].firstFrame -= skip;
 
215
                }
 
216
 
 
217
                token = COM_Parse( &text_p );
 
218
                if ( !*token ) {
 
219
                        break;
 
220
                }
 
221
                animations[i].numFrames = atoi( token );
 
222
 
 
223
                animations[i].reversed = qfalse;
 
224
                animations[i].flipflop = qfalse;
 
225
                // if numFrames is negative the animation is reversed
 
226
                if (animations[i].numFrames < 0) {
 
227
                        animations[i].numFrames = -animations[i].numFrames;
 
228
                        animations[i].reversed = qtrue;
 
229
                }
 
230
 
 
231
                token = COM_Parse( &text_p );
 
232
                if ( !*token ) {
 
233
                        break;
 
234
                }
 
235
                animations[i].loopFrames = atoi( token );
 
236
 
 
237
                token = COM_Parse( &text_p );
 
238
                if ( !*token ) {
 
239
                        break;
 
240
                }
 
241
                fps = atof( token );
 
242
                if ( fps == 0 ) {
 
243
                        fps = 1;
 
244
                }
 
245
                animations[i].frameLerp = 1000 / fps;
 
246
                animations[i].initialLerp = 1000 / fps;
 
247
        }
 
248
 
 
249
        if ( i != MAX_ANIMATIONS ) {
 
250
                CG_Printf( "Error parsing animation file: %s", filename );
 
251
                return qfalse;
 
252
        }
 
253
 
 
254
        // crouch backward animation
 
255
        memcpy(&animations[LEGS_BACKCR], &animations[LEGS_WALKCR], sizeof(animation_t));
 
256
        animations[LEGS_BACKCR].reversed = qtrue;
 
257
        // walk backward animation
 
258
        memcpy(&animations[LEGS_BACKWALK], &animations[LEGS_WALK], sizeof(animation_t));
 
259
        animations[LEGS_BACKWALK].reversed = qtrue;
 
260
        // flag moving fast
 
261
        animations[FLAG_RUN].firstFrame = 0;
 
262
        animations[FLAG_RUN].numFrames = 16;
 
263
        animations[FLAG_RUN].loopFrames = 16;
 
264
        animations[FLAG_RUN].frameLerp = 1000 / 15;
 
265
        animations[FLAG_RUN].initialLerp = 1000 / 15;
 
266
        animations[FLAG_RUN].reversed = qfalse;
 
267
        // flag not moving or moving slowly
 
268
        animations[FLAG_STAND].firstFrame = 16;
 
269
        animations[FLAG_STAND].numFrames = 5;
 
270
        animations[FLAG_STAND].loopFrames = 0;
 
271
        animations[FLAG_STAND].frameLerp = 1000 / 20;
 
272
        animations[FLAG_STAND].initialLerp = 1000 / 20;
 
273
        animations[FLAG_STAND].reversed = qfalse;
 
274
        // flag speeding up
 
275
        animations[FLAG_STAND2RUN].firstFrame = 16;
 
276
        animations[FLAG_STAND2RUN].numFrames = 5;
 
277
        animations[FLAG_STAND2RUN].loopFrames = 1;
 
278
        animations[FLAG_STAND2RUN].frameLerp = 1000 / 15;
 
279
        animations[FLAG_STAND2RUN].initialLerp = 1000 / 15;
 
280
        animations[FLAG_STAND2RUN].reversed = qtrue;
 
281
        //
 
282
        // new anims changes
 
283
        //
 
284
//      animations[TORSO_GETFLAG].flipflop = qtrue;
 
285
//      animations[TORSO_GUARDBASE].flipflop = qtrue;
 
286
//      animations[TORSO_PATROL].flipflop = qtrue;
 
287
//      animations[TORSO_AFFIRMATIVE].flipflop = qtrue;
 
288
//      animations[TORSO_NEGATIVE].flipflop = qtrue;
 
289
        //
 
290
        return qtrue;
 
291
}
 
292
 
 
293
/*
 
294
==========================
 
295
CG_FileExists
 
296
==========================
 
297
*/
 
298
static qboolean CG_FileExists(const char *filename) {
 
299
        int len;
 
300
 
 
301
        len = trap_FS_FOpenFile( filename, NULL, FS_READ );
 
302
        if (len>0) {
 
303
                return qtrue;
 
304
        }
 
305
        return qfalse;
 
306
}
 
307
 
 
308
/*
 
309
==========================
 
310
CG_FindClientModelFile
 
311
==========================
 
312
*/
 
313
static qboolean CG_FindClientModelFile( char *filename, int length, clientInfo_t *ci, const char *teamName, const char *modelName, const char *skinName, const char *base, const char *ext ) {
 
314
        char *team, *charactersFolder;
 
315
        int i;
 
316
 
 
317
        if ( cgs.gametype >= GT_TEAM ) {
 
318
                switch ( ci->team ) {
 
319
                        case TEAM_BLUE: {
 
320
                                team = "blue";
 
321
                                break;
 
322
                        }
 
323
                        default: {
 
324
                                team = "red";
 
325
                                break;
 
326
                        }
 
327
                }
 
328
        }
 
329
        else {
 
330
                team = "default";
 
331
        }
 
332
        charactersFolder = "";
 
333
        while(1) {
 
334
                for ( i = 0; i < 2; i++ ) {
 
335
                        if ( i == 0 && teamName && *teamName ) {
 
336
                                //                                                              "models/players/characters/james/stroggs/lower_lily_red.skin"
 
337
                                Com_sprintf( filename, length, "models/players/%s%s/%s%s_%s_%s.%s", charactersFolder, modelName, teamName, base, skinName, team, ext );
 
338
                        }
 
339
                        else {
 
340
                                //                                                              "models/players/characters/james/lower_lily_red.skin"
 
341
                                Com_sprintf( filename, length, "models/players/%s%s/%s_%s_%s.%s", charactersFolder, modelName, base, skinName, team, ext );
 
342
                        }
 
343
                        if ( CG_FileExists( filename ) ) {
 
344
                                return qtrue;
 
345
                        }
 
346
                        if ( cgs.gametype >= GT_TEAM ) {
 
347
                                if ( i == 0 && teamName && *teamName ) {
 
348
                                        //                                                              "models/players/characters/james/stroggs/lower_red.skin"
 
349
                                        Com_sprintf( filename, length, "models/players/%s%s/%s%s_%s.%s", charactersFolder, modelName, teamName, base, team, ext );
 
350
                                }
 
351
                                else {
 
352
                                        //                                                              "models/players/characters/james/lower_red.skin"
 
353
                                        Com_sprintf( filename, length, "models/players/%s%s/%s_%s.%s", charactersFolder, modelName, base, team, ext );
 
354
                                }
 
355
                        }
 
356
                        else {
 
357
                                if ( i == 0 && teamName && *teamName ) {
 
358
                                        //                                                              "models/players/characters/james/stroggs/lower_lily.skin"
 
359
                                        Com_sprintf( filename, length, "models/players/%s%s/%s%s_%s.%s", charactersFolder, modelName, teamName, base, skinName, ext );
 
360
                                }
 
361
                                else {
 
362
                                        //                                                              "models/players/characters/james/lower_lily.skin"
 
363
                                        Com_sprintf( filename, length, "models/players/%s%s/%s_%s.%s", charactersFolder, modelName, base, skinName, ext );
 
364
                                }
 
365
                        }
 
366
                        if ( CG_FileExists( filename ) ) {
 
367
                                return qtrue;
 
368
                        }
 
369
                        if ( !teamName || !*teamName ) {
 
370
                                break;
 
371
                        }
 
372
                }
 
373
                // if tried the heads folder first
 
374
                if ( charactersFolder[0] ) {
 
375
                        break;
 
376
                }
 
377
                charactersFolder = "characters/";
 
378
        }
 
379
 
 
380
        return qfalse;
 
381
}
 
382
 
 
383
/*
 
384
==========================
 
385
CG_FindClientHeadFile
 
386
==========================
 
387
*/
 
388
static qboolean CG_FindClientHeadFile( char *filename, int length, clientInfo_t *ci, const char *teamName, const char *headModelName, const char *headSkinName, const char *base, const char *ext ) {
 
389
        char *team, *headsFolder;
 
390
        int i;
 
391
 
 
392
        if ( cgs.gametype >= GT_TEAM ) {
 
393
                switch ( ci->team ) {
 
394
                        case TEAM_BLUE: {
 
395
                                team = "blue";
 
396
                                break;
 
397
                        }
 
398
                        default: {
 
399
                                team = "red";
 
400
                                break;
 
401
                        }
 
402
                }
 
403
        }
 
404
        else {
 
405
                team = "default";
 
406
        }
 
407
 
 
408
        if ( headModelName[0] == '*' ) {
 
409
                headsFolder = "heads/";
 
410
                headModelName++;
 
411
        }
 
412
        else {
 
413
                headsFolder = "";
 
414
        }
 
415
        while(1) {
 
416
                for ( i = 0; i < 2; i++ ) {
 
417
                        if ( i == 0 && teamName && *teamName ) {
 
418
                                Com_sprintf( filename, length, "models/players/%s%s/%s/%s%s_%s.%s", headsFolder, headModelName, headSkinName, teamName, base, team, ext );
 
419
                        }
 
420
                        else {
 
421
                                Com_sprintf( filename, length, "models/players/%s%s/%s/%s_%s.%s", headsFolder, headModelName, headSkinName, base, team, ext );
 
422
                        }
 
423
                        if ( CG_FileExists( filename ) ) {
 
424
                                return qtrue;
 
425
                        }
 
426
                        if ( cgs.gametype >= GT_TEAM ) {
 
427
                                if ( i == 0 &&  teamName && *teamName ) {
 
428
                                        Com_sprintf( filename, length, "models/players/%s%s/%s%s_%s.%s", headsFolder, headModelName, teamName, base, team, ext );
 
429
                                }
 
430
                                else {
 
431
                                        Com_sprintf( filename, length, "models/players/%s%s/%s_%s.%s", headsFolder, headModelName, base, team, ext );
 
432
                                }
 
433
                        }
 
434
                        else {
 
435
                                if ( i == 0 && teamName && *teamName ) {
 
436
                                        Com_sprintf( filename, length, "models/players/%s%s/%s%s_%s.%s", headsFolder, headModelName, teamName, base, headSkinName, ext );
 
437
                                }
 
438
                                else {
 
439
                                        Com_sprintf( filename, length, "models/players/%s%s/%s_%s.%s", headsFolder, headModelName, base, headSkinName, ext );
 
440
                                }
 
441
                        }
 
442
                        if ( CG_FileExists( filename ) ) {
 
443
                                return qtrue;
 
444
                        }
 
445
                        if ( !teamName || !*teamName ) {
 
446
                                break;
 
447
                        }
 
448
                }
 
449
                // if tried the heads folder first
 
450
                if ( headsFolder[0] ) {
 
451
                        break;
 
452
                }
 
453
                headsFolder = "heads/";
 
454
        }
 
455
 
 
456
        return qfalse;
 
457
}
 
458
 
 
459
/*
 
460
==========================
 
461
CG_RegisterClientSkin
 
462
==========================
 
463
*/
 
464
static qboolean CG_RegisterClientSkin( clientInfo_t *ci, const char *teamName, const char *modelName, const char *skinName, const char *headModelName, const char *headSkinName ) {
 
465
        char filename[MAX_QPATH];
 
466
 
 
467
        /*
 
468
        Com_sprintf( filename, sizeof( filename ), "models/players/%s/%slower_%s.skin", modelName, teamName, skinName );
 
469
        ci->legsSkin = trap_R_RegisterSkin( filename );
 
470
        if (!ci->legsSkin) {
 
471
                Com_sprintf( filename, sizeof( filename ), "models/players/characters/%s/%slower_%s.skin", modelName, teamName, skinName );
 
472
                ci->legsSkin = trap_R_RegisterSkin( filename );
 
473
                if (!ci->legsSkin) {
 
474
                        Com_Printf( "Leg skin load failure: %s\n", filename );
 
475
                }
 
476
        }
 
477
 
 
478
 
 
479
        Com_sprintf( filename, sizeof( filename ), "models/players/%s/%supper_%s.skin", modelName, teamName, skinName );
 
480
        ci->torsoSkin = trap_R_RegisterSkin( filename );
 
481
        if (!ci->torsoSkin) {
 
482
                Com_sprintf( filename, sizeof( filename ), "models/players/characters/%s/%supper_%s.skin", modelName, teamName, skinName );
 
483
                ci->torsoSkin = trap_R_RegisterSkin( filename );
 
484
                if (!ci->torsoSkin) {
 
485
                        Com_Printf( "Torso skin load failure: %s\n", filename );
 
486
                }
 
487
        }
 
488
        */
 
489
        if ( CG_FindClientModelFile( filename, sizeof(filename), ci, teamName, modelName, skinName, "lower", "skin" ) ) {
 
490
                ci->legsSkin = trap_R_RegisterSkin( filename );
 
491
        }
 
492
        if (!ci->legsSkin) {
 
493
                Com_Printf( "Leg skin load failure: %s\n", filename );
 
494
        }
 
495
 
 
496
        if ( CG_FindClientModelFile( filename, sizeof(filename), ci, teamName, modelName, skinName, "upper", "skin" ) ) {
 
497
                ci->torsoSkin = trap_R_RegisterSkin( filename );
 
498
        }
 
499
        if (!ci->torsoSkin) {
 
500
                Com_Printf( "Torso skin load failure: %s\n", filename );
 
501
        }
 
502
 
 
503
        if ( CG_FindClientHeadFile( filename, sizeof(filename), ci, teamName, headModelName, headSkinName, "head", "skin" ) ) {
 
504
                ci->headSkin = trap_R_RegisterSkin( filename );
 
505
        }
 
506
        if (!ci->headSkin) {
 
507
                Com_Printf( "Head skin load failure: %s\n", filename );
 
508
        }
 
509
 
 
510
        // if any skins failed to load
 
511
        if ( !ci->legsSkin || !ci->torsoSkin || !ci->headSkin ) {
 
512
                return qfalse;
 
513
        }
 
514
        return qtrue;
 
515
}
 
516
 
 
517
/*
 
518
==========================
 
519
CG_RegisterClientModelname
 
520
==========================
 
521
*/
 
522
static qboolean CG_RegisterClientModelname( clientInfo_t *ci, const char *modelName, const char *skinName, const char *headModelName, const char *headSkinName, const char *teamName ) {
 
523
        char    filename[MAX_QPATH*2];
 
524
        const char              *headName;
 
525
        char newTeamName[MAX_QPATH*2];
 
526
 
 
527
        if ( headModelName[0] == '\0' ) {
 
528
                headName = modelName;
 
529
        }
 
530
        else {
 
531
                headName = headModelName;
 
532
        }
 
533
        Com_sprintf( filename, sizeof( filename ), "models/players/%s/lower.md3", modelName );
 
534
        ci->legsModel = trap_R_RegisterModel( filename );
 
535
        if ( !ci->legsModel ) {
 
536
                Com_sprintf( filename, sizeof( filename ), "models/players/characters/%s/lower.md3", modelName );
 
537
                ci->legsModel = trap_R_RegisterModel( filename );
 
538
                if ( !ci->legsModel ) {
 
539
                        Com_Printf( "Failed to load model file %s\n", filename );
 
540
                        return qfalse;
 
541
                }
 
542
        }
 
543
 
 
544
        Com_sprintf( filename, sizeof( filename ), "models/players/%s/upper.md3", modelName );
 
545
        ci->torsoModel = trap_R_RegisterModel( filename );
 
546
        if ( !ci->torsoModel ) {
 
547
                Com_sprintf( filename, sizeof( filename ), "models/players/characters/%s/upper.md3", modelName );
 
548
                ci->torsoModel = trap_R_RegisterModel( filename );
 
549
                if ( !ci->torsoModel ) {
 
550
                        Com_Printf( "Failed to load model file %s\n", filename );
 
551
                        return qfalse;
 
552
                }
 
553
        }
 
554
 
 
555
        if( headName[0] == '*' ) {
 
556
                Com_sprintf( filename, sizeof( filename ), "models/players/heads/%s/%s.md3", &headModelName[1], &headModelName[1] );
 
557
        }
 
558
        else {
 
559
                Com_sprintf( filename, sizeof( filename ), "models/players/%s/head.md3", headName );
 
560
        }
 
561
        ci->headModel = trap_R_RegisterModel( filename );
 
562
        // if the head model could not be found and we didn't load from the heads folder try to load from there
 
563
        if ( !ci->headModel && headName[0] != '*' ) {
 
564
                Com_sprintf( filename, sizeof( filename ), "models/players/heads/%s/%s.md3", headModelName, headModelName );
 
565
                ci->headModel = trap_R_RegisterModel( filename );
 
566
        }
 
567
        if ( !ci->headModel ) {
 
568
                Com_Printf( "Failed to load model file %s\n", filename );
 
569
                return qfalse;
 
570
        }
 
571
 
 
572
        // if any skins failed to load, return failure
 
573
        if ( !CG_RegisterClientSkin( ci, teamName, modelName, skinName, headName, headSkinName ) ) {
 
574
                if ( teamName && *teamName) {
 
575
                        Com_Printf( "Failed to load skin file: %s : %s : %s, %s : %s\n", teamName, modelName, skinName, headName, headSkinName );
 
576
                        if( ci->team == TEAM_BLUE ) {
 
577
                                Com_sprintf(newTeamName, sizeof(newTeamName), "%s/", DEFAULT_BLUETEAM_NAME);
 
578
                        }
 
579
                        else {
 
580
                                Com_sprintf(newTeamName, sizeof(newTeamName), "%s/", DEFAULT_REDTEAM_NAME);
 
581
                        }
 
582
                        if ( !CG_RegisterClientSkin( ci, newTeamName, modelName, skinName, headName, headSkinName ) ) {
 
583
                                Com_Printf( "Failed to load skin file: %s : %s : %s, %s : %s\n", newTeamName, modelName, skinName, headName, headSkinName );
 
584
                                return qfalse;
 
585
                        }
 
586
                } else {
 
587
                        Com_Printf( "Failed to load skin file: %s : %s, %s : %s\n", modelName, skinName, headName, headSkinName );
 
588
                        return qfalse;
 
589
                }
 
590
        }
 
591
 
 
592
        // load the animations
 
593
        Com_sprintf( filename, sizeof( filename ), "models/players/%s/animation.cfg", modelName );
 
594
        if ( !CG_ParseAnimationFile( filename, ci ) ) {
 
595
                Com_sprintf( filename, sizeof( filename ), "models/players/characters/%s/animation.cfg", modelName );
 
596
                if ( !CG_ParseAnimationFile( filename, ci ) ) {
 
597
                        Com_Printf( "Failed to load animation file %s\n", filename );
 
598
                        return qfalse;
 
599
                }
 
600
        }
 
601
 
 
602
        if ( CG_FindClientHeadFile( filename, sizeof(filename), ci, teamName, headName, headSkinName, "icon", "skin" ) ) {
 
603
                ci->modelIcon = trap_R_RegisterShaderNoMip( filename );
 
604
        }
 
605
        else if ( CG_FindClientHeadFile( filename, sizeof(filename), ci, teamName, headName, headSkinName, "icon", "tga" ) ) {
 
606
                ci->modelIcon = trap_R_RegisterShaderNoMip( filename );
 
607
        }
 
608
 
 
609
        if ( !ci->modelIcon ) {
 
610
                return qfalse;
 
611
        }
 
612
 
 
613
        return qtrue;
 
614
}
 
615
 
 
616
/*
 
617
====================
 
618
CG_ColorFromString
 
619
====================
 
620
*/
 
621
static void CG_ColorFromString( const char *v, vec3_t color ) {
 
622
        int val;
 
623
 
 
624
        VectorClear( color );
 
625
 
 
626
        val = atoi( v );
 
627
 
 
628
        if ( val < 1 || val > 7 ) {
 
629
                VectorSet( color, 1, 1, 1 );
 
630
                return;
 
631
        }
 
632
 
 
633
        if ( val & 1 ) {
 
634
                color[2] = 1.0f;
 
635
        }
 
636
        if ( val & 2 ) {
 
637
                color[1] = 1.0f;
 
638
        }
 
639
        if ( val & 4 ) {
 
640
                color[0] = 1.0f;
 
641
        }
 
642
}
 
643
 
 
644
/*
 
645
===================
 
646
CG_LoadClientInfo
 
647
 
 
648
Load it now, taking the disk hits.
 
649
This will usually be deferred to a safe time
 
650
===================
 
651
*/
 
652
static void CG_LoadClientInfo( int clientNum, clientInfo_t *ci ) {
 
653
        const char      *dir, *fallback;
 
654
        int                     i, modelloaded;
 
655
        const char      *s;
 
656
        char            teamname[MAX_QPATH];
 
657
 
 
658
        teamname[0] = 0;
 
659
#ifdef MISSIONPACK
 
660
        if( cgs.gametype >= GT_TEAM) {
 
661
                if( ci->team == TEAM_BLUE ) {
 
662
                        Q_strncpyz(teamname, cg_blueTeamName.string, sizeof(teamname) );
 
663
                } else {
 
664
                        Q_strncpyz(teamname, cg_redTeamName.string, sizeof(teamname) );
 
665
                }
 
666
        }
 
667
        if( teamname[0] ) {
 
668
                strcat( teamname, "/" );
 
669
        }
 
670
#endif
 
671
        modelloaded = qtrue;
 
672
        if ( !CG_RegisterClientModelname( ci, ci->modelName, ci->skinName, ci->headModelName, ci->headSkinName, teamname ) ) {
 
673
                if ( cg_buildScript.integer ) {
 
674
                        CG_Error( "CG_RegisterClientModelname( %s, %s, %s, %s %s ) failed", ci->modelName, ci->skinName, ci->headModelName, ci->headSkinName, teamname );
 
675
                }
 
676
 
 
677
                // fall back to default team name
 
678
                if( cgs.gametype >= GT_TEAM) {
 
679
                        // keep skin name
 
680
                        if( ci->team == TEAM_BLUE ) {
 
681
                                Q_strncpyz(teamname, DEFAULT_BLUETEAM_NAME, sizeof(teamname) );
 
682
                        } else {
 
683
                                Q_strncpyz(teamname, DEFAULT_REDTEAM_NAME, sizeof(teamname) );
 
684
                        }
 
685
                        if ( !CG_RegisterClientModelname( ci, DEFAULT_TEAM_MODEL, ci->skinName, DEFAULT_TEAM_HEAD, ci->skinName, teamname ) ) {
 
686
                                CG_Error( "DEFAULT_TEAM_MODEL / skin (%s/%s) failed to register", DEFAULT_TEAM_MODEL, ci->skinName );
 
687
                        }
 
688
                } else {
 
689
                        if ( !CG_RegisterClientModelname( ci, DEFAULT_MODEL, "default", DEFAULT_MODEL, "default", teamname ) ) {
 
690
                                CG_Error( "DEFAULT_MODEL (%s) failed to register", DEFAULT_MODEL );
 
691
                        }
 
692
                }
 
693
                modelloaded = qfalse;
 
694
        }
 
695
 
 
696
        ci->newAnims = qfalse;
 
697
        if ( ci->torsoModel ) {
 
698
                orientation_t tag;
 
699
                // if the torso model has the "tag_flag"
 
700
                if ( trap_R_LerpTag( &tag, ci->torsoModel, 0, 0, 1, "tag_flag" ) ) {
 
701
                        ci->newAnims = qtrue;
 
702
                }
 
703
        }
 
704
 
 
705
        // sounds
 
706
        dir = ci->modelName;
 
707
        fallback = (cgs.gametype >= GT_TEAM) ? DEFAULT_TEAM_MODEL : DEFAULT_MODEL;
 
708
 
 
709
        for ( i = 0 ; i < MAX_CUSTOM_SOUNDS ; i++ ) {
 
710
                s = cg_customSoundNames[i];
 
711
                if ( !s ) {
 
712
                        break;
 
713
                }
 
714
                ci->sounds[i] = 0;
 
715
                // if the model didn't load use the sounds of the default model
 
716
                if (modelloaded) {
 
717
                        ci->sounds[i] = trap_S_RegisterSound( va("sound/player/%s/%s", dir, s + 1), qfalse );
 
718
                }
 
719
                if ( !ci->sounds[i] ) {
 
720
                        ci->sounds[i] = trap_S_RegisterSound( va("sound/player/%s/%s", fallback, s + 1), qfalse );
 
721
                }
 
722
        }
 
723
 
 
724
        ci->deferred = qfalse;
 
725
 
 
726
        // reset any existing players and bodies, because they might be in bad
 
727
        // frames for this new model
 
728
        for ( i = 0 ; i < MAX_GENTITIES ; i++ ) {
 
729
                if ( cg_entities[i].currentState.clientNum == clientNum
 
730
                        && cg_entities[i].currentState.eType == ET_PLAYER ) {
 
731
                        CG_ResetPlayerEntity( &cg_entities[i] );
 
732
                }
 
733
        }
 
734
}
 
735
 
 
736
/*
 
737
======================
 
738
CG_CopyClientInfoModel
 
739
======================
 
740
*/
 
741
static void CG_CopyClientInfoModel( clientInfo_t *from, clientInfo_t *to ) {
 
742
        VectorCopy( from->headOffset, to->headOffset );
 
743
        to->footsteps = from->footsteps;
 
744
        to->gender = from->gender;
 
745
 
 
746
        to->legsModel = from->legsModel;
 
747
        to->legsSkin = from->legsSkin;
 
748
        to->torsoModel = from->torsoModel;
 
749
        to->torsoSkin = from->torsoSkin;
 
750
        to->headModel = from->headModel;
 
751
        to->headSkin = from->headSkin;
 
752
        to->modelIcon = from->modelIcon;
 
753
 
 
754
        to->newAnims = from->newAnims;
 
755
 
 
756
        memcpy( to->animations, from->animations, sizeof( to->animations ) );
 
757
        memcpy( to->sounds, from->sounds, sizeof( to->sounds ) );
 
758
}
 
759
 
 
760
/*
 
761
======================
 
762
CG_ScanForExistingClientInfo
 
763
======================
 
764
*/
 
765
static qboolean CG_ScanForExistingClientInfo( clientInfo_t *ci ) {
 
766
        int             i;
 
767
        clientInfo_t    *match;
 
768
 
 
769
        for ( i = 0 ; i < cgs.maxclients ; i++ ) {
 
770
                match = &cgs.clientinfo[ i ];
 
771
                if ( !match->infoValid ) {
 
772
                        continue;
 
773
                }
 
774
                if ( match->deferred ) {
 
775
                        continue;
 
776
                }
 
777
                if ( !Q_stricmp( ci->modelName, match->modelName )
 
778
                        && !Q_stricmp( ci->skinName, match->skinName )
 
779
                        && !Q_stricmp( ci->headModelName, match->headModelName )
 
780
                        && !Q_stricmp( ci->headSkinName, match->headSkinName ) 
 
781
                        && !Q_stricmp( ci->blueTeam, match->blueTeam ) 
 
782
                        && !Q_stricmp( ci->redTeam, match->redTeam )
 
783
                        && (cgs.gametype < GT_TEAM || ci->team == match->team) ) {
 
784
                        // this clientinfo is identical, so use it's handles
 
785
 
 
786
                        ci->deferred = qfalse;
 
787
 
 
788
                        CG_CopyClientInfoModel( match, ci );
 
789
 
 
790
                        return qtrue;
 
791
                }
 
792
        }
 
793
 
 
794
        // nothing matches, so defer the load
 
795
        return qfalse;
 
796
}
 
797
 
 
798
/*
 
799
======================
 
800
CG_SetDeferredClientInfo
 
801
 
 
802
We aren't going to load it now, so grab some other
 
803
client's info to use until we have some spare time.
 
804
======================
 
805
*/
 
806
static void CG_SetDeferredClientInfo( int clientNum, clientInfo_t *ci ) {
 
807
        int             i;
 
808
        clientInfo_t    *match;
 
809
 
 
810
        // if someone else is already the same models and skins we
 
811
        // can just load the client info
 
812
        for ( i = 0 ; i < cgs.maxclients ; i++ ) {
 
813
                match = &cgs.clientinfo[ i ];
 
814
                if ( !match->infoValid || match->deferred ) {
 
815
                        continue;
 
816
                }
 
817
                if ( Q_stricmp( ci->skinName, match->skinName ) ||
 
818
                         Q_stricmp( ci->modelName, match->modelName ) ||
 
819
//                       Q_stricmp( ci->headModelName, match->headModelName ) ||
 
820
//                       Q_stricmp( ci->headSkinName, match->headSkinName ) ||
 
821
                         (cgs.gametype >= GT_TEAM && ci->team != match->team) ) {
 
822
                        continue;
 
823
                }
 
824
                // just load the real info cause it uses the same models and skins
 
825
                CG_LoadClientInfo( clientNum, ci );
 
826
                return;
 
827
        }
 
828
 
 
829
        // if we are in teamplay, only grab a model if the skin is correct
 
830
        if ( cgs.gametype >= GT_TEAM ) {
 
831
                for ( i = 0 ; i < cgs.maxclients ; i++ ) {
 
832
                        match = &cgs.clientinfo[ i ];
 
833
                        if ( !match->infoValid || match->deferred ) {
 
834
                                continue;
 
835
                        }
 
836
                        if ( Q_stricmp( ci->skinName, match->skinName ) ||
 
837
                                (cgs.gametype >= GT_TEAM && ci->team != match->team) ) {
 
838
                                continue;
 
839
                        }
 
840
                        ci->deferred = qtrue;
 
841
                        CG_CopyClientInfoModel( match, ci );
 
842
                        return;
 
843
                }
 
844
                // load the full model, because we don't ever want to show
 
845
                // an improper team skin.  This will cause a hitch for the first
 
846
                // player, when the second enters.  Combat shouldn't be going on
 
847
                // yet, so it shouldn't matter
 
848
                CG_LoadClientInfo( clientNum, ci );
 
849
                return;
 
850
        }
 
851
 
 
852
        // find the first valid clientinfo and grab its stuff
 
853
        for ( i = 0 ; i < cgs.maxclients ; i++ ) {
 
854
                match = &cgs.clientinfo[ i ];
 
855
                if ( !match->infoValid ) {
 
856
                        continue;
 
857
                }
 
858
 
 
859
                ci->deferred = qtrue;
 
860
                CG_CopyClientInfoModel( match, ci );
 
861
                return;
 
862
        }
 
863
 
 
864
        // we should never get here...
 
865
        CG_Printf( "CG_SetDeferredClientInfo: no valid clients!\n" );
 
866
 
 
867
        CG_LoadClientInfo( clientNum, ci );
 
868
}
 
869
 
 
870
 
 
871
/*
 
872
======================
 
873
CG_NewClientInfo
 
874
======================
 
875
*/
 
876
void CG_NewClientInfo( int clientNum ) {
 
877
        clientInfo_t *ci;
 
878
        clientInfo_t newInfo;
 
879
        const char      *configstring;
 
880
        const char      *v;
 
881
        char            *slash;
 
882
 
 
883
        ci = &cgs.clientinfo[clientNum];
 
884
 
 
885
        configstring = CG_ConfigString( clientNum + CS_PLAYERS );
 
886
        if ( !configstring[0] ) {
 
887
                memset( ci, 0, sizeof( *ci ) );
 
888
                return;         // player just left
 
889
        }
 
890
 
 
891
        // build into a temp buffer so the defer checks can use
 
892
        // the old value
 
893
        memset( &newInfo, 0, sizeof( newInfo ) );
 
894
 
 
895
        // isolate the player's name
 
896
        v = Info_ValueForKey(configstring, "n");
 
897
        Q_strncpyz( newInfo.name, v, sizeof( newInfo.name ) );
 
898
 
 
899
        // colors
 
900
        v = Info_ValueForKey( configstring, "c1" );
 
901
        CG_ColorFromString( v, newInfo.color1 );
 
902
 
 
903
        v = Info_ValueForKey( configstring, "c2" );
 
904
        CG_ColorFromString( v, newInfo.color2 );
 
905
 
 
906
        // bot skill
 
907
        v = Info_ValueForKey( configstring, "skill" );
 
908
        newInfo.botSkill = atoi( v );
 
909
 
 
910
        // handicap
 
911
        v = Info_ValueForKey( configstring, "hc" );
 
912
        newInfo.handicap = atoi( v );
 
913
 
 
914
        // wins
 
915
        v = Info_ValueForKey( configstring, "w" );
 
916
        newInfo.wins = atoi( v );
 
917
 
 
918
        // losses
 
919
        v = Info_ValueForKey( configstring, "l" );
 
920
        newInfo.losses = atoi( v );
 
921
 
 
922
        // team
 
923
        v = Info_ValueForKey( configstring, "t" );
 
924
        newInfo.team = atoi( v );
 
925
 
 
926
        // team task
 
927
        v = Info_ValueForKey( configstring, "tt" );
 
928
        newInfo.teamTask = atoi(v);
 
929
 
 
930
        // team leader
 
931
        v = Info_ValueForKey( configstring, "tl" );
 
932
        newInfo.teamLeader = atoi(v);
 
933
 
 
934
        v = Info_ValueForKey( configstring, "g_redteam" );
 
935
        Q_strncpyz(newInfo.redTeam, v, MAX_TEAMNAME);
 
936
 
 
937
        v = Info_ValueForKey( configstring, "g_blueteam" );
 
938
        Q_strncpyz(newInfo.blueTeam, v, MAX_TEAMNAME);
 
939
 
 
940
        // model
 
941
        v = Info_ValueForKey( configstring, "model" );
 
942
        if ( cg_forceModel.integer ) {
 
943
                // forcemodel makes everyone use a single model
 
944
                // to prevent load hitches
 
945
                char modelStr[MAX_QPATH];
 
946
                char *skin;
 
947
 
 
948
                if( cgs.gametype >= GT_TEAM ) {
 
949
                        Q_strncpyz( newInfo.modelName, DEFAULT_TEAM_MODEL, sizeof( newInfo.modelName ) );
 
950
                        Q_strncpyz( newInfo.skinName, "default", sizeof( newInfo.skinName ) );
 
951
                } else {
 
952
                        trap_Cvar_VariableStringBuffer( "model", modelStr, sizeof( modelStr ) );
 
953
                        if ( ( skin = strchr( modelStr, '/' ) ) == NULL) {
 
954
                                skin = "default";
 
955
                        } else {
 
956
                                *skin++ = 0;
 
957
                        }
 
958
 
 
959
                        Q_strncpyz( newInfo.skinName, skin, sizeof( newInfo.skinName ) );
 
960
                        Q_strncpyz( newInfo.modelName, modelStr, sizeof( newInfo.modelName ) );
 
961
                }
 
962
 
 
963
                if ( cgs.gametype >= GT_TEAM ) {
 
964
                        // keep skin name
 
965
                        slash = strchr( v, '/' );
 
966
                        if ( slash ) {
 
967
                                Q_strncpyz( newInfo.skinName, slash + 1, sizeof( newInfo.skinName ) );
 
968
                        }
 
969
                }
 
970
        } else {
 
971
                Q_strncpyz( newInfo.modelName, v, sizeof( newInfo.modelName ) );
 
972
 
 
973
                slash = strchr( newInfo.modelName, '/' );
 
974
                if ( !slash ) {
 
975
                        // modelName didn not include a skin name
 
976
                        Q_strncpyz( newInfo.skinName, "default", sizeof( newInfo.skinName ) );
 
977
                } else {
 
978
                        Q_strncpyz( newInfo.skinName, slash + 1, sizeof( newInfo.skinName ) );
 
979
                        // truncate modelName
 
980
                        *slash = 0;
 
981
                }
 
982
        }
 
983
 
 
984
        // head model
 
985
        v = Info_ValueForKey( configstring, "hmodel" );
 
986
        if ( cg_forceModel.integer ) {
 
987
                // forcemodel makes everyone use a single model
 
988
                // to prevent load hitches
 
989
                char modelStr[MAX_QPATH];
 
990
                char *skin;
 
991
 
 
992
                if( cgs.gametype >= GT_TEAM ) {
 
993
                        Q_strncpyz( newInfo.headModelName, DEFAULT_TEAM_MODEL, sizeof( newInfo.headModelName ) );
 
994
                        Q_strncpyz( newInfo.headSkinName, "default", sizeof( newInfo.headSkinName ) );
 
995
                } else {
 
996
                        trap_Cvar_VariableStringBuffer( "headmodel", modelStr, sizeof( modelStr ) );
 
997
                        if ( ( skin = strchr( modelStr, '/' ) ) == NULL) {
 
998
                                skin = "default";
 
999
                        } else {
 
1000
                                *skin++ = 0;
 
1001
                        }
 
1002
 
 
1003
                        Q_strncpyz( newInfo.headSkinName, skin, sizeof( newInfo.headSkinName ) );
 
1004
                        Q_strncpyz( newInfo.headModelName, modelStr, sizeof( newInfo.headModelName ) );
 
1005
                }
 
1006
 
 
1007
                if ( cgs.gametype >= GT_TEAM ) {
 
1008
                        // keep skin name
 
1009
                        slash = strchr( v, '/' );
 
1010
                        if ( slash ) {
 
1011
                                Q_strncpyz( newInfo.headSkinName, slash + 1, sizeof( newInfo.headSkinName ) );
 
1012
                        }
 
1013
                }
 
1014
        } else {
 
1015
                Q_strncpyz( newInfo.headModelName, v, sizeof( newInfo.headModelName ) );
 
1016
 
 
1017
                slash = strchr( newInfo.headModelName, '/' );
 
1018
                if ( !slash ) {
 
1019
                        // modelName didn not include a skin name
 
1020
                        Q_strncpyz( newInfo.headSkinName, "default", sizeof( newInfo.headSkinName ) );
 
1021
                } else {
 
1022
                        Q_strncpyz( newInfo.headSkinName, slash + 1, sizeof( newInfo.headSkinName ) );
 
1023
                        // truncate modelName
 
1024
                        *slash = 0;
 
1025
                }
 
1026
        }
 
1027
 
 
1028
        // scan for an existing clientinfo that matches this modelname
 
1029
        // so we can avoid loading checks if possible
 
1030
        if ( !CG_ScanForExistingClientInfo( &newInfo ) ) {
 
1031
                qboolean        forceDefer;
 
1032
 
 
1033
                forceDefer = trap_MemoryRemaining() < 4000000;
 
1034
 
 
1035
                // if we are defering loads, just have it pick the first valid
 
1036
                if ( forceDefer || (cg_deferPlayers.integer && !cg_buildScript.integer && !cg.loading ) ) {
 
1037
                        // keep whatever they had if it won't violate team skins
 
1038
                        CG_SetDeferredClientInfo( clientNum, &newInfo );
 
1039
                        // if we are low on memory, leave them with this model
 
1040
                        if ( forceDefer ) {
 
1041
                                CG_Printf( "Memory is low.  Using deferred model.\n" );
 
1042
                                newInfo.deferred = qfalse;
 
1043
                        }
 
1044
                } else {
 
1045
                        CG_LoadClientInfo( clientNum, &newInfo );
 
1046
                }
 
1047
        }
 
1048
 
 
1049
        // replace whatever was there with the new one
 
1050
        newInfo.infoValid = qtrue;
 
1051
        *ci = newInfo;
 
1052
}
 
1053
 
 
1054
 
 
1055
 
 
1056
/*
 
1057
======================
 
1058
CG_LoadDeferredPlayers
 
1059
 
 
1060
Called each frame when a player is dead
 
1061
and the scoreboard is up
 
1062
so deferred players can be loaded
 
1063
======================
 
1064
*/
 
1065
void CG_LoadDeferredPlayers( void ) {
 
1066
        int             i;
 
1067
        clientInfo_t    *ci;
 
1068
 
 
1069
        // scan for a deferred player to load
 
1070
        for ( i = 0, ci = cgs.clientinfo ; i < cgs.maxclients ; i++, ci++ ) {
 
1071
                if ( ci->infoValid && ci->deferred ) {
 
1072
                        // if we are low on memory, leave it deferred
 
1073
                        if ( trap_MemoryRemaining() < 4000000 ) {
 
1074
                                CG_Printf( "Memory is low.  Using deferred model.\n" );
 
1075
                                ci->deferred = qfalse;
 
1076
                                continue;
 
1077
                        }
 
1078
                        CG_LoadClientInfo( i, ci );
 
1079
//                      break;
 
1080
                }
 
1081
        }
 
1082
}
 
1083
 
 
1084
/*
 
1085
=============================================================================
 
1086
 
 
1087
PLAYER ANIMATION
 
1088
 
 
1089
=============================================================================
 
1090
*/
 
1091
 
 
1092
 
 
1093
/*
 
1094
===============
 
1095
CG_SetLerpFrameAnimation
 
1096
 
 
1097
may include ANIM_TOGGLEBIT
 
1098
===============
 
1099
*/
 
1100
static void CG_SetLerpFrameAnimation( clientInfo_t *ci, lerpFrame_t *lf, int newAnimation ) {
 
1101
        animation_t     *anim;
 
1102
 
 
1103
        lf->animationNumber = newAnimation;
 
1104
        newAnimation &= ~ANIM_TOGGLEBIT;
 
1105
 
 
1106
        if ( newAnimation < 0 || newAnimation >= MAX_TOTALANIMATIONS ) {
 
1107
                CG_Error( "Bad animation number: %i", newAnimation );
 
1108
        }
 
1109
 
 
1110
        anim = &ci->animations[ newAnimation ];
 
1111
 
 
1112
        lf->animation = anim;
 
1113
        lf->animationTime = lf->frameTime + anim->initialLerp;
 
1114
 
 
1115
        if ( cg_debugAnim.integer ) {
 
1116
                CG_Printf( "Anim: %i\n", newAnimation );
 
1117
        }
 
1118
}
 
1119
 
 
1120
/*
 
1121
===============
 
1122
CG_RunLerpFrame
 
1123
 
 
1124
Sets cg.snap, cg.oldFrame, and cg.backlerp
 
1125
cg.time should be between oldFrameTime and frameTime after exit
 
1126
===============
 
1127
*/
 
1128
static void CG_RunLerpFrame( clientInfo_t *ci, lerpFrame_t *lf, int newAnimation, float speedScale ) {
 
1129
        int                     f, numFrames;
 
1130
        animation_t     *anim;
 
1131
 
 
1132
        // debugging tool to get no animations
 
1133
        if ( cg_animSpeed.integer == 0 ) {
 
1134
                lf->oldFrame = lf->frame = lf->backlerp = 0;
 
1135
                return;
 
1136
        }
 
1137
 
 
1138
        // see if the animation sequence is switching
 
1139
        if ( newAnimation != lf->animationNumber || !lf->animation ) {
 
1140
                CG_SetLerpFrameAnimation( ci, lf, newAnimation );
 
1141
        }
 
1142
 
 
1143
        // if we have passed the current frame, move it to
 
1144
        // oldFrame and calculate a new frame
 
1145
        if ( cg.time >= lf->frameTime ) {
 
1146
                lf->oldFrame = lf->frame;
 
1147
                lf->oldFrameTime = lf->frameTime;
 
1148
 
 
1149
                // get the next frame based on the animation
 
1150
                anim = lf->animation;
 
1151
                if ( !anim->frameLerp ) {
 
1152
                        return;         // shouldn't happen
 
1153
                }
 
1154
                if ( cg.time < lf->animationTime ) {
 
1155
                        lf->frameTime = lf->animationTime;              // initial lerp
 
1156
                } else {
 
1157
                        lf->frameTime = lf->oldFrameTime + anim->frameLerp;
 
1158
                }
 
1159
                f = ( lf->frameTime - lf->animationTime ) / anim->frameLerp;
 
1160
                f *= speedScale;                // adjust for haste, etc
 
1161
 
 
1162
                numFrames = anim->numFrames;
 
1163
                if (anim->flipflop) {
 
1164
                        numFrames *= 2;
 
1165
                }
 
1166
                if ( f >= numFrames ) {
 
1167
                        f -= numFrames;
 
1168
                        if ( anim->loopFrames ) {
 
1169
                                f %= anim->loopFrames;
 
1170
                                f += anim->numFrames - anim->loopFrames;
 
1171
                        } else {
 
1172
                                f = numFrames - 1;
 
1173
                                // the animation is stuck at the end, so it
 
1174
                                // can immediately transition to another sequence
 
1175
                                lf->frameTime = cg.time;
 
1176
                        }
 
1177
                }
 
1178
                if ( anim->reversed ) {
 
1179
                        lf->frame = anim->firstFrame + anim->numFrames - 1 - f;
 
1180
                }
 
1181
                else if (anim->flipflop && f>=anim->numFrames) {
 
1182
                        lf->frame = anim->firstFrame + anim->numFrames - 1 - (f%anim->numFrames);
 
1183
                }
 
1184
                else {
 
1185
                        lf->frame = anim->firstFrame + f;
 
1186
                }
 
1187
                if ( cg.time > lf->frameTime ) {
 
1188
                        lf->frameTime = cg.time;
 
1189
                        if ( cg_debugAnim.integer ) {
 
1190
                                CG_Printf( "Clamp lf->frameTime\n");
 
1191
                        }
 
1192
                }
 
1193
        }
 
1194
 
 
1195
        if ( lf->frameTime > cg.time + 200 ) {
 
1196
                lf->frameTime = cg.time;
 
1197
        }
 
1198
 
 
1199
        if ( lf->oldFrameTime > cg.time ) {
 
1200
                lf->oldFrameTime = cg.time;
 
1201
        }
 
1202
        // calculate current lerp value
 
1203
        if ( lf->frameTime == lf->oldFrameTime ) {
 
1204
                lf->backlerp = 0;
 
1205
        } else {
 
1206
                lf->backlerp = 1.0 - (float)( cg.time - lf->oldFrameTime ) / ( lf->frameTime - lf->oldFrameTime );
 
1207
        }
 
1208
}
 
1209
 
 
1210
 
 
1211
/*
 
1212
===============
 
1213
CG_ClearLerpFrame
 
1214
===============
 
1215
*/
 
1216
static void CG_ClearLerpFrame( clientInfo_t *ci, lerpFrame_t *lf, int animationNumber ) {
 
1217
        lf->frameTime = lf->oldFrameTime = cg.time;
 
1218
        CG_SetLerpFrameAnimation( ci, lf, animationNumber );
 
1219
        lf->oldFrame = lf->frame = lf->animation->firstFrame;
 
1220
}
 
1221
 
 
1222
 
 
1223
/*
 
1224
===============
 
1225
CG_PlayerAnimation
 
1226
===============
 
1227
*/
 
1228
static void CG_PlayerAnimation( centity_t *cent, int *legsOld, int *legs, float *legsBackLerp,
 
1229
                                                int *torsoOld, int *torso, float *torsoBackLerp ) {
 
1230
        clientInfo_t    *ci;
 
1231
        int                             clientNum;
 
1232
        float                   speedScale;
 
1233
 
 
1234
        clientNum = cent->currentState.clientNum;
 
1235
 
 
1236
        if ( cg_noPlayerAnims.integer ) {
 
1237
                *legsOld = *legs = *torsoOld = *torso = 0;
 
1238
                return;
 
1239
        }
 
1240
 
 
1241
        if ( cent->currentState.powerups & ( 1 << PW_HASTE ) ) {
 
1242
                speedScale = 1.5;
 
1243
        } else {
 
1244
                speedScale = 1;
 
1245
        }
 
1246
 
 
1247
        ci = &cgs.clientinfo[ clientNum ];
 
1248
 
 
1249
        // do the shuffle turn frames locally
 
1250
        if ( cent->pe.legs.yawing && ( cent->currentState.legsAnim & ~ANIM_TOGGLEBIT ) == LEGS_IDLE ) {
 
1251
                CG_RunLerpFrame( ci, &cent->pe.legs, LEGS_TURN, speedScale );
 
1252
        } else {
 
1253
                CG_RunLerpFrame( ci, &cent->pe.legs, cent->currentState.legsAnim, speedScale );
 
1254
        }
 
1255
 
 
1256
        *legsOld = cent->pe.legs.oldFrame;
 
1257
        *legs = cent->pe.legs.frame;
 
1258
        *legsBackLerp = cent->pe.legs.backlerp;
 
1259
 
 
1260
        CG_RunLerpFrame( ci, &cent->pe.torso, cent->currentState.torsoAnim, speedScale );
 
1261
 
 
1262
        *torsoOld = cent->pe.torso.oldFrame;
 
1263
        *torso = cent->pe.torso.frame;
 
1264
        *torsoBackLerp = cent->pe.torso.backlerp;
 
1265
}
 
1266
 
 
1267
/*
 
1268
=============================================================================
 
1269
 
 
1270
PLAYER ANGLES
 
1271
 
 
1272
=============================================================================
 
1273
*/
 
1274
 
 
1275
/*
 
1276
==================
 
1277
CG_SwingAngles
 
1278
==================
 
1279
*/
 
1280
static void CG_SwingAngles( float destination, float swingTolerance, float clampTolerance,
 
1281
                                        float speed, float *angle, qboolean *swinging ) {
 
1282
        float   swing;
 
1283
        float   move;
 
1284
        float   scale;
 
1285
 
 
1286
        if ( !*swinging ) {
 
1287
                // see if a swing should be started
 
1288
                swing = AngleSubtract( *angle, destination );
 
1289
                if ( swing > swingTolerance || swing < -swingTolerance ) {
 
1290
                        *swinging = qtrue;
 
1291
                }
 
1292
        }
 
1293
 
 
1294
        if ( !*swinging ) {
 
1295
                return;
 
1296
        }
 
1297
        
 
1298
        // modify the speed depending on the delta
 
1299
        // so it doesn't seem so linear
 
1300
        swing = AngleSubtract( destination, *angle );
 
1301
        scale = fabs( swing );
 
1302
        if ( scale < swingTolerance * 0.5 ) {
 
1303
                scale = 0.5;
 
1304
        } else if ( scale < swingTolerance ) {
 
1305
                scale = 1.0;
 
1306
        } else {
 
1307
                scale = 2.0;
 
1308
        }
 
1309
 
 
1310
        // swing towards the destination angle
 
1311
        if ( swing >= 0 ) {
 
1312
                move = cg.frametime * scale * speed;
 
1313
                if ( move >= swing ) {
 
1314
                        move = swing;
 
1315
                        *swinging = qfalse;
 
1316
                }
 
1317
                *angle = AngleMod( *angle + move );
 
1318
        } else if ( swing < 0 ) {
 
1319
                move = cg.frametime * scale * -speed;
 
1320
                if ( move <= swing ) {
 
1321
                        move = swing;
 
1322
                        *swinging = qfalse;
 
1323
                }
 
1324
                *angle = AngleMod( *angle + move );
 
1325
        }
 
1326
 
 
1327
        // clamp to no more than tolerance
 
1328
        swing = AngleSubtract( destination, *angle );
 
1329
        if ( swing > clampTolerance ) {
 
1330
                *angle = AngleMod( destination - (clampTolerance - 1) );
 
1331
        } else if ( swing < -clampTolerance ) {
 
1332
                *angle = AngleMod( destination + (clampTolerance - 1) );
 
1333
        }
 
1334
}
 
1335
 
 
1336
/*
 
1337
=================
 
1338
CG_AddPainTwitch
 
1339
=================
 
1340
*/
 
1341
static void CG_AddPainTwitch( centity_t *cent, vec3_t torsoAngles ) {
 
1342
        int             t;
 
1343
        float   f;
 
1344
 
 
1345
        t = cg.time - cent->pe.painTime;
 
1346
        if ( t >= PAIN_TWITCH_TIME ) {
 
1347
                return;
 
1348
        }
 
1349
 
 
1350
        f = 1.0 - (float)t / PAIN_TWITCH_TIME;
 
1351
 
 
1352
        if ( cent->pe.painDirection ) {
 
1353
                torsoAngles[ROLL] += 20 * f;
 
1354
        } else {
 
1355
                torsoAngles[ROLL] -= 20 * f;
 
1356
        }
 
1357
}
 
1358
 
 
1359
 
 
1360
/*
 
1361
===============
 
1362
CG_PlayerAngles
 
1363
 
 
1364
Handles seperate torso motion
 
1365
 
 
1366
  legs pivot based on direction of movement
 
1367
 
 
1368
  head always looks exactly at cent->lerpAngles
 
1369
 
 
1370
  if motion < 20 degrees, show in head only
 
1371
  if < 45 degrees, also show in torso
 
1372
===============
 
1373
*/
 
1374
static void CG_PlayerAngles( centity_t *cent, vec3_t legs[3], vec3_t torso[3], vec3_t head[3] ) {
 
1375
        vec3_t          legsAngles, torsoAngles, headAngles;
 
1376
        float           dest;
 
1377
        static  int     movementOffsets[8] = { 0, 22, 45, -22, 0, 22, -45, -22 };
 
1378
        vec3_t          velocity;
 
1379
        float           speed;
 
1380
        int                     dir, clientNum;
 
1381
        clientInfo_t    *ci;
 
1382
 
 
1383
        VectorCopy( cent->lerpAngles, headAngles );
 
1384
        headAngles[YAW] = AngleMod( headAngles[YAW] );
 
1385
        VectorClear( legsAngles );
 
1386
        VectorClear( torsoAngles );
 
1387
 
 
1388
        // --------- yaw -------------
 
1389
 
 
1390
        // allow yaw to drift a bit
 
1391
        if ( ( cent->currentState.legsAnim & ~ANIM_TOGGLEBIT ) != LEGS_IDLE 
 
1392
                || ( cent->currentState.torsoAnim & ~ANIM_TOGGLEBIT ) != TORSO_STAND  ) {
 
1393
                // if not standing still, always point all in the same direction
 
1394
                cent->pe.torso.yawing = qtrue;  // always center
 
1395
                cent->pe.torso.pitching = qtrue;        // always center
 
1396
                cent->pe.legs.yawing = qtrue;   // always center
 
1397
        }
 
1398
 
 
1399
        // adjust legs for movement dir
 
1400
        if ( cent->currentState.eFlags & EF_DEAD ) {
 
1401
                // don't let dead bodies twitch
 
1402
                dir = 0;
 
1403
        } else {
 
1404
                dir = cent->currentState.angles2[YAW];
 
1405
                if ( dir < 0 || dir > 7 ) {
 
1406
                        CG_Error( "Bad player movement angle" );
 
1407
                }
 
1408
        }
 
1409
        legsAngles[YAW] = headAngles[YAW] + movementOffsets[ dir ];
 
1410
        torsoAngles[YAW] = headAngles[YAW] + 0.25 * movementOffsets[ dir ];
 
1411
 
 
1412
        // torso
 
1413
        CG_SwingAngles( torsoAngles[YAW], 25, 90, cg_swingSpeed.value, &cent->pe.torso.yawAngle, &cent->pe.torso.yawing );
 
1414
        CG_SwingAngles( legsAngles[YAW], 40, 90, cg_swingSpeed.value, &cent->pe.legs.yawAngle, &cent->pe.legs.yawing );
 
1415
 
 
1416
        torsoAngles[YAW] = cent->pe.torso.yawAngle;
 
1417
        legsAngles[YAW] = cent->pe.legs.yawAngle;
 
1418
 
 
1419
 
 
1420
        // --------- pitch -------------
 
1421
 
 
1422
        // only show a fraction of the pitch angle in the torso
 
1423
        if ( headAngles[PITCH] > 180 ) {
 
1424
                dest = (-360 + headAngles[PITCH]) * 0.75f;
 
1425
        } else {
 
1426
                dest = headAngles[PITCH] * 0.75f;
 
1427
        }
 
1428
        CG_SwingAngles( dest, 15, 30, 0.1f, &cent->pe.torso.pitchAngle, &cent->pe.torso.pitching );
 
1429
        torsoAngles[PITCH] = cent->pe.torso.pitchAngle;
 
1430
 
 
1431
        //
 
1432
        clientNum = cent->currentState.clientNum;
 
1433
        if ( clientNum >= 0 && clientNum < MAX_CLIENTS ) {
 
1434
                ci = &cgs.clientinfo[ clientNum ];
 
1435
                if ( ci->fixedtorso ) {
 
1436
                        torsoAngles[PITCH] = 0.0f;
 
1437
                }
 
1438
        }
 
1439
 
 
1440
        // --------- roll -------------
 
1441
 
 
1442
 
 
1443
        // lean towards the direction of travel
 
1444
        VectorCopy( cent->currentState.pos.trDelta, velocity );
 
1445
        speed = VectorNormalize( velocity );
 
1446
        if ( speed ) {
 
1447
                vec3_t  axis[3];
 
1448
                float   side;
 
1449
 
 
1450
                speed *= 0.05f;
 
1451
 
 
1452
                AnglesToAxis( legsAngles, axis );
 
1453
                side = speed * DotProduct( velocity, axis[1] );
 
1454
                legsAngles[ROLL] -= side;
 
1455
 
 
1456
                side = speed * DotProduct( velocity, axis[0] );
 
1457
                legsAngles[PITCH] += side;
 
1458
        }
 
1459
 
 
1460
        //
 
1461
        clientNum = cent->currentState.clientNum;
 
1462
        if ( clientNum >= 0 && clientNum < MAX_CLIENTS ) {
 
1463
                ci = &cgs.clientinfo[ clientNum ];
 
1464
                if ( ci->fixedlegs ) {
 
1465
                        legsAngles[YAW] = torsoAngles[YAW];
 
1466
                        legsAngles[PITCH] = 0.0f;
 
1467
                        legsAngles[ROLL] = 0.0f;
 
1468
                }
 
1469
        }
 
1470
 
 
1471
        // pain twitch
 
1472
        CG_AddPainTwitch( cent, torsoAngles );
 
1473
 
 
1474
        // pull the angles back out of the hierarchial chain
 
1475
        AnglesSubtract( headAngles, torsoAngles, headAngles );
 
1476
        AnglesSubtract( torsoAngles, legsAngles, torsoAngles );
 
1477
        AnglesToAxis( legsAngles, legs );
 
1478
        AnglesToAxis( torsoAngles, torso );
 
1479
        AnglesToAxis( headAngles, head );
 
1480
}
 
1481
 
 
1482
 
 
1483
//==========================================================================
 
1484
 
 
1485
/*
 
1486
===============
 
1487
CG_HasteTrail
 
1488
===============
 
1489
*/
 
1490
static void CG_HasteTrail( centity_t *cent ) {
 
1491
        localEntity_t   *smoke;
 
1492
        vec3_t                  origin;
 
1493
        int                             anim;
 
1494
 
 
1495
        if ( cent->trailTime > cg.time ) {
 
1496
                return;
 
1497
        }
 
1498
        anim = cent->pe.legs.animationNumber & ~ANIM_TOGGLEBIT;
 
1499
        if ( anim != LEGS_RUN && anim != LEGS_BACK ) {
 
1500
                return;
 
1501
        }
 
1502
 
 
1503
        cent->trailTime += 100;
 
1504
        if ( cent->trailTime < cg.time ) {
 
1505
                cent->trailTime = cg.time;
 
1506
        }
 
1507
 
 
1508
        VectorCopy( cent->lerpOrigin, origin );
 
1509
        origin[2] -= 16;
 
1510
 
 
1511
        smoke = CG_SmokePuff( origin, vec3_origin, 
 
1512
                                  8, 
 
1513
                                  1, 1, 1, 1,
 
1514
                                  500, 
 
1515
                                  cg.time,
 
1516
                                  0,
 
1517
                                  0,
 
1518
                                  cgs.media.hastePuffShader );
 
1519
 
 
1520
        // use the optimized local entity add
 
1521
        smoke->leType = LE_SCALE_FADE;
 
1522
}
 
1523
 
 
1524
#ifdef MISSIONPACK
 
1525
/*
 
1526
===============
 
1527
CG_BreathPuffs
 
1528
===============
 
1529
*/
 
1530
static void CG_BreathPuffs( centity_t *cent, refEntity_t *head) {
 
1531
        clientInfo_t *ci;
 
1532
        vec3_t up, origin;
 
1533
        int contents;
 
1534
 
 
1535
        ci = &cgs.clientinfo[ cent->currentState.number ];
 
1536
 
 
1537
        if (!cg_enableBreath.integer) {
 
1538
                return;
 
1539
        }
 
1540
        if ( cent->currentState.number == cg.snap->ps.clientNum && !cg.renderingThirdPerson) {
 
1541
                return;
 
1542
        }
 
1543
        if ( cent->currentState.eFlags & EF_DEAD ) {
 
1544
                return;
 
1545
        }
 
1546
        contents = trap_CM_PointContents( head->origin, 0 );
 
1547
        if ( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) {
 
1548
                return;
 
1549
        }
 
1550
        if ( ci->breathPuffTime > cg.time ) {
 
1551
                return;
 
1552
        }
 
1553
 
 
1554
        VectorSet( up, 0, 0, 8 );
 
1555
        VectorMA(head->origin, 8, head->axis[0], origin);
 
1556
        VectorMA(origin, -4, head->axis[2], origin);
 
1557
        CG_SmokePuff( origin, up, 16, 1, 1, 1, 0.66f, 1500, cg.time, cg.time + 400, LEF_PUFF_DONT_SCALE, cgs.media.shotgunSmokePuffShader );
 
1558
        ci->breathPuffTime = cg.time + 2000;
 
1559
}
 
1560
 
 
1561
/*
 
1562
===============
 
1563
CG_DustTrail
 
1564
===============
 
1565
*/
 
1566
static void CG_DustTrail( centity_t *cent ) {
 
1567
        int                             anim;
 
1568
        localEntity_t   *dust;
 
1569
        vec3_t end, vel;
 
1570
        trace_t tr;
 
1571
 
 
1572
        if (!cg_enableDust.integer)
 
1573
                return;
 
1574
 
 
1575
        if ( cent->dustTrailTime > cg.time ) {
 
1576
                return;
 
1577
        }
 
1578
 
 
1579
        anim = cent->pe.legs.animationNumber & ~ANIM_TOGGLEBIT;
 
1580
        if ( anim != LEGS_LANDB && anim != LEGS_LAND ) {
 
1581
                return;
 
1582
        }
 
1583
 
 
1584
        cent->dustTrailTime += 40;
 
1585
        if ( cent->dustTrailTime < cg.time ) {
 
1586
                cent->dustTrailTime = cg.time;
 
1587
        }
 
1588
 
 
1589
        VectorCopy(cent->currentState.pos.trBase, end);
 
1590
        end[2] -= 64;
 
1591
        CG_Trace( &tr, cent->currentState.pos.trBase, NULL, NULL, end, cent->currentState.number, MASK_PLAYERSOLID );
 
1592
 
 
1593
        if ( !(tr.surfaceFlags & SURF_DUST) )
 
1594
                return;
 
1595
 
 
1596
        VectorCopy( cent->currentState.pos.trBase, end );
 
1597
        end[2] -= 16;
 
1598
 
 
1599
        VectorSet(vel, 0, 0, -30);
 
1600
        dust = CG_SmokePuff( end, vel,
 
1601
                                  24,
 
1602
                                  .8f, .8f, 0.7f, 0.33f,
 
1603
                                  500,
 
1604
                                  cg.time,
 
1605
                                  0,
 
1606
                                  0,
 
1607
                                  cgs.media.dustPuffShader );
 
1608
}
 
1609
 
 
1610
#endif
 
1611
 
 
1612
/*
 
1613
===============
 
1614
CG_TrailItem
 
1615
===============
 
1616
*/
 
1617
static void CG_TrailItem( centity_t *cent, qhandle_t hModel ) {
 
1618
        refEntity_t             ent;
 
1619
        vec3_t                  angles;
 
1620
        vec3_t                  axis[3];
 
1621
 
 
1622
        VectorCopy( cent->lerpAngles, angles );
 
1623
        angles[PITCH] = 0;
 
1624
        angles[ROLL] = 0;
 
1625
        AnglesToAxis( angles, axis );
 
1626
 
 
1627
        memset( &ent, 0, sizeof( ent ) );
 
1628
        VectorMA( cent->lerpOrigin, -16, axis[0], ent.origin );
 
1629
        ent.origin[2] += 16;
 
1630
        angles[YAW] += 90;
 
1631
        AnglesToAxis( angles, ent.axis );
 
1632
 
 
1633
        ent.hModel = hModel;
 
1634
        trap_R_AddRefEntityToScene( &ent );
 
1635
}
 
1636
 
 
1637
 
 
1638
/*
 
1639
===============
 
1640
CG_PlayerFlag
 
1641
===============
 
1642
*/
 
1643
static void CG_PlayerFlag( centity_t *cent, qhandle_t hSkin, refEntity_t *torso ) {
 
1644
        clientInfo_t    *ci;
 
1645
        refEntity_t     pole;
 
1646
        refEntity_t     flag;
 
1647
        vec3_t          angles, dir;
 
1648
        int                     legsAnim, flagAnim, updateangles;
 
1649
        float           angle, d;
 
1650
 
 
1651
        // show the flag pole model
 
1652
        memset( &pole, 0, sizeof(pole) );
 
1653
        pole.hModel = cgs.media.flagPoleModel;
 
1654
        VectorCopy( torso->lightingOrigin, pole.lightingOrigin );
 
1655
        pole.shadowPlane = torso->shadowPlane;
 
1656
        pole.renderfx = torso->renderfx;
 
1657
        CG_PositionEntityOnTag( &pole, torso, torso->hModel, "tag_flag" );
 
1658
        trap_R_AddRefEntityToScene( &pole );
 
1659
 
 
1660
        // show the flag model
 
1661
        memset( &flag, 0, sizeof(flag) );
 
1662
        flag.hModel = cgs.media.flagFlapModel;
 
1663
        flag.customSkin = hSkin;
 
1664
        VectorCopy( torso->lightingOrigin, flag.lightingOrigin );
 
1665
        flag.shadowPlane = torso->shadowPlane;
 
1666
        flag.renderfx = torso->renderfx;
 
1667
 
 
1668
        VectorClear(angles);
 
1669
 
 
1670
        updateangles = qfalse;
 
1671
        legsAnim = cent->currentState.legsAnim & ~ANIM_TOGGLEBIT;
 
1672
        if( legsAnim == LEGS_IDLE || legsAnim == LEGS_IDLECR ) {
 
1673
                flagAnim = FLAG_STAND;
 
1674
        } else if ( legsAnim == LEGS_WALK || legsAnim == LEGS_WALKCR ) {
 
1675
                flagAnim = FLAG_STAND;
 
1676
                updateangles = qtrue;
 
1677
        } else {
 
1678
                flagAnim = FLAG_RUN;
 
1679
                updateangles = qtrue;
 
1680
        }
 
1681
 
 
1682
        if ( updateangles ) {
 
1683
 
 
1684
                VectorCopy( cent->currentState.pos.trDelta, dir );
 
1685
                // add gravity
 
1686
                dir[2] += 100;
 
1687
                VectorNormalize( dir );
 
1688
                d = DotProduct(pole.axis[2], dir);
 
1689
                // if there is anough movement orthogonal to the flag pole
 
1690
                if (fabs(d) < 0.9) {
 
1691
                        //
 
1692
                        d = DotProduct(pole.axis[0], dir);
 
1693
                        if (d > 1.0f) {
 
1694
                                d = 1.0f;
 
1695
                        }
 
1696
                        else if (d < -1.0f) {
 
1697
                                d = -1.0f;
 
1698
                        }
 
1699
                        angle = acos(d);
 
1700
 
 
1701
                        d = DotProduct(pole.axis[1], dir);
 
1702
                        if (d < 0) {
 
1703
                                angles[YAW] = 360 - angle * 180 / M_PI;
 
1704
                        }
 
1705
                        else {
 
1706
                                angles[YAW] = angle * 180 / M_PI;
 
1707
                        }
 
1708
                        if (angles[YAW] < 0)
 
1709
                                angles[YAW] += 360;
 
1710
                        if (angles[YAW] > 360)
 
1711
                                angles[YAW] -= 360;
 
1712
 
 
1713
                        //vectoangles( cent->currentState.pos.trDelta, tmpangles );
 
1714
                        //angles[YAW] = tmpangles[YAW] + 45 - cent->pe.torso.yawAngle;
 
1715
                        // change the yaw angle
 
1716
                        CG_SwingAngles( angles[YAW], 25, 90, 0.15f, &cent->pe.flag.yawAngle, &cent->pe.flag.yawing );
 
1717
                }
 
1718
 
 
1719
                /*
 
1720
                d = DotProduct(pole.axis[2], dir);
 
1721
                angle = Q_acos(d);
 
1722
 
 
1723
                d = DotProduct(pole.axis[1], dir);
 
1724
                if (d < 0) {
 
1725
                        angle = 360 - angle * 180 / M_PI;
 
1726
                }
 
1727
                else {
 
1728
                        angle = angle * 180 / M_PI;
 
1729
                }
 
1730
                if (angle > 340 && angle < 20) {
 
1731
                        flagAnim = FLAG_RUNUP;
 
1732
                }
 
1733
                if (angle > 160 && angle < 200) {
 
1734
                        flagAnim = FLAG_RUNDOWN;
 
1735
                }
 
1736
                */
 
1737
        }
 
1738
 
 
1739
        // set the yaw angle
 
1740
        angles[YAW] = cent->pe.flag.yawAngle;
 
1741
        // lerp the flag animation frames
 
1742
        ci = &cgs.clientinfo[ cent->currentState.clientNum ];
 
1743
        CG_RunLerpFrame( ci, &cent->pe.flag, flagAnim, 1 );
 
1744
        flag.oldframe = cent->pe.flag.oldFrame;
 
1745
        flag.frame = cent->pe.flag.frame;
 
1746
        flag.backlerp = cent->pe.flag.backlerp;
 
1747
 
 
1748
        AnglesToAxis( angles, flag.axis );
 
1749
        CG_PositionRotatedEntityOnTag( &flag, &pole, pole.hModel, "tag_flag" );
 
1750
 
 
1751
        trap_R_AddRefEntityToScene( &flag );
 
1752
}
 
1753
 
 
1754
 
 
1755
#ifdef MISSIONPACK // bk001204
 
1756
/*
 
1757
===============
 
1758
CG_PlayerTokens
 
1759
===============
 
1760
*/
 
1761
static void CG_PlayerTokens( centity_t *cent, int renderfx ) {
 
1762
        int                     tokens, i, j;
 
1763
        float           angle;
 
1764
        refEntity_t     ent;
 
1765
        vec3_t          dir, origin;
 
1766
        skulltrail_t *trail;
 
1767
        trail = &cg.skulltrails[cent->currentState.number];
 
1768
        tokens = cent->currentState.generic1;
 
1769
        if ( !tokens ) {
 
1770
                trail->numpositions = 0;
 
1771
                return;
 
1772
        }
 
1773
 
 
1774
        if ( tokens > MAX_SKULLTRAIL ) {
 
1775
                tokens = MAX_SKULLTRAIL;
 
1776
        }
 
1777
 
 
1778
        // add skulls if there are more than last time
 
1779
        for (i = 0; i < tokens - trail->numpositions; i++) {
 
1780
                for (j = trail->numpositions; j > 0; j--) {
 
1781
                        VectorCopy(trail->positions[j-1], trail->positions[j]);
 
1782
                }
 
1783
                VectorCopy(cent->lerpOrigin, trail->positions[0]);
 
1784
        }
 
1785
        trail->numpositions = tokens;
 
1786
 
 
1787
        // move all the skulls along the trail
 
1788
        VectorCopy(cent->lerpOrigin, origin);
 
1789
        for (i = 0; i < trail->numpositions; i++) {
 
1790
                VectorSubtract(trail->positions[i], origin, dir);
 
1791
                if (VectorNormalize(dir) > 30) {
 
1792
                        VectorMA(origin, 30, dir, trail->positions[i]);
 
1793
                }
 
1794
                VectorCopy(trail->positions[i], origin);
 
1795
        }
 
1796
 
 
1797
        memset( &ent, 0, sizeof( ent ) );
 
1798
        if( cgs.clientinfo[ cent->currentState.clientNum ].team == TEAM_BLUE ) {
 
1799
                ent.hModel = cgs.media.redCubeModel;
 
1800
        } else {
 
1801
                ent.hModel = cgs.media.blueCubeModel;
 
1802
        }
 
1803
        ent.renderfx = renderfx;
 
1804
 
 
1805
        VectorCopy(cent->lerpOrigin, origin);
 
1806
        for (i = 0; i < trail->numpositions; i++) {
 
1807
                VectorSubtract(origin, trail->positions[i], ent.axis[0]);
 
1808
                ent.axis[0][2] = 0;
 
1809
                VectorNormalize(ent.axis[0]);
 
1810
                VectorSet(ent.axis[2], 0, 0, 1);
 
1811
                CrossProduct(ent.axis[0], ent.axis[2], ent.axis[1]);
 
1812
 
 
1813
                VectorCopy(trail->positions[i], ent.origin);
 
1814
                angle = (((cg.time + 500 * MAX_SKULLTRAIL - 500 * i) / 16) & 255) * (M_PI * 2) / 255;
 
1815
                ent.origin[2] += sin(angle) * 10;
 
1816
                trap_R_AddRefEntityToScene( &ent );
 
1817
                VectorCopy(trail->positions[i], origin);
 
1818
        }
 
1819
}
 
1820
#endif
 
1821
 
 
1822
 
 
1823
/*
 
1824
===============
 
1825
CG_PlayerPowerups
 
1826
===============
 
1827
*/
 
1828
static void CG_PlayerPowerups( centity_t *cent, refEntity_t *torso ) {
 
1829
        int             powerups;
 
1830
        clientInfo_t    *ci;
 
1831
 
 
1832
        powerups = cent->currentState.powerups;
 
1833
        if ( !powerups ) {
 
1834
                return;
 
1835
        }
 
1836
 
 
1837
        // quad gives a dlight
 
1838
        if ( powerups & ( 1 << PW_QUAD ) ) {
 
1839
                trap_R_AddLightToScene( cent->lerpOrigin, 200 + (rand()&31), 0.2f, 0.2f, 1 );
 
1840
        }
 
1841
 
 
1842
        // flight plays a looped sound
 
1843
        if ( powerups & ( 1 << PW_FLIGHT ) ) {
 
1844
                trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, cgs.media.flightSound );
 
1845
        }
 
1846
 
 
1847
        ci = &cgs.clientinfo[ cent->currentState.clientNum ];
 
1848
        // redflag
 
1849
        if ( powerups & ( 1 << PW_REDFLAG ) ) {
 
1850
                if (ci->newAnims) {
 
1851
                        CG_PlayerFlag( cent, cgs.media.redFlagFlapSkin, torso );
 
1852
                }
 
1853
                else {
 
1854
                        CG_TrailItem( cent, cgs.media.redFlagModel );
 
1855
                }
 
1856
                trap_R_AddLightToScene( cent->lerpOrigin, 200 + (rand()&31), 1.0, 0.2f, 0.2f );
 
1857
        }
 
1858
 
 
1859
        // blueflag
 
1860
        if ( powerups & ( 1 << PW_BLUEFLAG ) ) {
 
1861
                if (ci->newAnims){
 
1862
                        CG_PlayerFlag( cent, cgs.media.blueFlagFlapSkin, torso );
 
1863
                }
 
1864
                else {
 
1865
                        CG_TrailItem( cent, cgs.media.blueFlagModel );
 
1866
                }
 
1867
                trap_R_AddLightToScene( cent->lerpOrigin, 200 + (rand()&31), 0.2f, 0.2f, 1.0 );
 
1868
        }
 
1869
 
 
1870
        // neutralflag
 
1871
        if ( powerups & ( 1 << PW_NEUTRALFLAG ) ) {
 
1872
                if (ci->newAnims) {
 
1873
                        CG_PlayerFlag( cent, cgs.media.neutralFlagFlapSkin, torso );
 
1874
                }
 
1875
                else {
 
1876
                        CG_TrailItem( cent, cgs.media.neutralFlagModel );
 
1877
                }
 
1878
                trap_R_AddLightToScene( cent->lerpOrigin, 200 + (rand()&31), 1.0, 1.0, 1.0 );
 
1879
        }
 
1880
 
 
1881
        // haste leaves smoke trails
 
1882
        if ( powerups & ( 1 << PW_HASTE ) ) {
 
1883
                CG_HasteTrail( cent );
 
1884
        }
 
1885
}
 
1886
 
 
1887
 
 
1888
/*
 
1889
===============
 
1890
CG_PlayerFloatSprite
 
1891
 
 
1892
Float a sprite over the player's head
 
1893
===============
 
1894
*/
 
1895
static void CG_PlayerFloatSprite( centity_t *cent, qhandle_t shader ) {
 
1896
        int                             rf;
 
1897
        refEntity_t             ent;
 
1898
 
 
1899
        if ( cent->currentState.number == cg.snap->ps.clientNum && !cg.renderingThirdPerson ) {
 
1900
                rf = RF_THIRD_PERSON;           // only show in mirrors
 
1901
        } else {
 
1902
                rf = 0;
 
1903
        }
 
1904
 
 
1905
        memset( &ent, 0, sizeof( ent ) );
 
1906
        VectorCopy( cent->lerpOrigin, ent.origin );
 
1907
        ent.origin[2] += 48;
 
1908
        ent.reType = RT_SPRITE;
 
1909
        ent.customShader = shader;
 
1910
        ent.radius = 10;
 
1911
        ent.renderfx = rf;
 
1912
        ent.shaderRGBA[0] = 255;
 
1913
        ent.shaderRGBA[1] = 255;
 
1914
        ent.shaderRGBA[2] = 255;
 
1915
        ent.shaderRGBA[3] = 255;
 
1916
        trap_R_AddRefEntityToScene( &ent );
 
1917
}
 
1918
 
 
1919
 
 
1920
 
 
1921
/*
 
1922
===============
 
1923
CG_PlayerSprites
 
1924
 
 
1925
Float sprites over the player's head
 
1926
===============
 
1927
*/
 
1928
static void CG_PlayerSprites( centity_t *cent ) {
 
1929
        int             team;
 
1930
 
 
1931
        if ( cent->currentState.eFlags & EF_CONNECTION ) {
 
1932
                CG_PlayerFloatSprite( cent, cgs.media.connectionShader );
 
1933
                return;
 
1934
        }
 
1935
 
 
1936
        if ( cent->currentState.eFlags & EF_TALK ) {
 
1937
                CG_PlayerFloatSprite( cent, cgs.media.balloonShader );
 
1938
                return;
 
1939
        }
 
1940
 
 
1941
        if ( cent->currentState.eFlags & EF_AWARD_IMPRESSIVE ) {
 
1942
                CG_PlayerFloatSprite( cent, cgs.media.medalImpressive );
 
1943
                return;
 
1944
        }
 
1945
 
 
1946
        if ( cent->currentState.eFlags & EF_AWARD_EXCELLENT ) {
 
1947
                CG_PlayerFloatSprite( cent, cgs.media.medalExcellent );
 
1948
                return;
 
1949
        }
 
1950
 
 
1951
        if ( cent->currentState.eFlags & EF_AWARD_GAUNTLET ) {
 
1952
                CG_PlayerFloatSprite( cent, cgs.media.medalGauntlet );
 
1953
                return;
 
1954
        }
 
1955
 
 
1956
        if ( cent->currentState.eFlags & EF_AWARD_DEFEND ) {
 
1957
                CG_PlayerFloatSprite( cent, cgs.media.medalDefend );
 
1958
                return;
 
1959
        }
 
1960
 
 
1961
        if ( cent->currentState.eFlags & EF_AWARD_ASSIST ) {
 
1962
                CG_PlayerFloatSprite( cent, cgs.media.medalAssist );
 
1963
                return;
 
1964
        }
 
1965
 
 
1966
        if ( cent->currentState.eFlags & EF_AWARD_CAP ) {
 
1967
                CG_PlayerFloatSprite( cent, cgs.media.medalCapture );
 
1968
                return;
 
1969
        }
 
1970
 
 
1971
        team = cgs.clientinfo[ cent->currentState.clientNum ].team;
 
1972
        if ( !(cent->currentState.eFlags & EF_DEAD) && 
 
1973
                cg.snap->ps.persistant[PERS_TEAM] == team &&
 
1974
                cgs.gametype >= GT_TEAM) {
 
1975
                if (cg_drawFriend.integer) {
 
1976
                        CG_PlayerFloatSprite( cent, cgs.media.friendShader );
 
1977
                }
 
1978
                return;
 
1979
        }
 
1980
}
 
1981
 
 
1982
/*
 
1983
===============
 
1984
CG_PlayerShadow
 
1985
 
 
1986
Returns the Z component of the surface being shadowed
 
1987
 
 
1988
  should it return a full plane instead of a Z?
 
1989
===============
 
1990
*/
 
1991
#define SHADOW_DISTANCE         128
 
1992
static qboolean CG_PlayerShadow( centity_t *cent, float *shadowPlane ) {
 
1993
        vec3_t          end, mins = {-15, -15, 0}, maxs = {15, 15, 2};
 
1994
        trace_t         trace;
 
1995
        float           alpha;
 
1996
 
 
1997
        *shadowPlane = 0;
 
1998
 
 
1999
        if ( cg_shadows.integer == 0 ) {
 
2000
                return qfalse;
 
2001
        }
 
2002
 
 
2003
        // no shadows when invisible
 
2004
        if ( cent->currentState.powerups & ( 1 << PW_INVIS ) ) {
 
2005
                return qfalse;
 
2006
        }
 
2007
 
 
2008
        // send a trace down from the player to the ground
 
2009
        VectorCopy( cent->lerpOrigin, end );
 
2010
        end[2] -= SHADOW_DISTANCE;
 
2011
 
 
2012
        trap_CM_BoxTrace( &trace, cent->lerpOrigin, end, mins, maxs, 0, MASK_PLAYERSOLID );
 
2013
 
 
2014
        // no shadow if too high
 
2015
        if ( trace.fraction == 1.0 || trace.startsolid || trace.allsolid ) {
 
2016
                return qfalse;
 
2017
        }
 
2018
 
 
2019
        *shadowPlane = trace.endpos[2] + 1;
 
2020
 
 
2021
        if ( cg_shadows.integer != 1 ) {        // no mark for stencil or projection shadows
 
2022
                return qtrue;
 
2023
        }
 
2024
 
 
2025
        // fade the shadow out with height
 
2026
        alpha = 1.0 - trace.fraction;
 
2027
 
 
2028
        // bk0101022 - hack / FPE - bogus planes?
 
2029
        //assert( DotProduct( trace.plane.normal, trace.plane.normal ) != 0.0f ) 
 
2030
 
 
2031
        // add the mark as a temporary, so it goes directly to the renderer
 
2032
        // without taking a spot in the cg_marks array
 
2033
        CG_ImpactMark( cgs.media.shadowMarkShader, trace.endpos, trace.plane.normal, 
 
2034
                cent->pe.legs.yawAngle, alpha,alpha,alpha,1, qfalse, 24, qtrue );
 
2035
 
 
2036
        return qtrue;
 
2037
}
 
2038
 
 
2039
 
 
2040
/*
 
2041
===============
 
2042
CG_PlayerSplash
 
2043
 
 
2044
Draw a mark at the water surface
 
2045
===============
 
2046
*/
 
2047
static void CG_PlayerSplash( centity_t *cent ) {
 
2048
        vec3_t          start, end;
 
2049
        trace_t         trace;
 
2050
        int                     contents;
 
2051
        polyVert_t      verts[4];
 
2052
 
 
2053
        if ( !cg_shadows.integer ) {
 
2054
                return;
 
2055
        }
 
2056
 
 
2057
        VectorCopy( cent->lerpOrigin, end );
 
2058
        end[2] -= 24;
 
2059
 
 
2060
        // if the feet aren't in liquid, don't make a mark
 
2061
        // this won't handle moving water brushes, but they wouldn't draw right anyway...
 
2062
        contents = trap_CM_PointContents( end, 0 );
 
2063
        if ( !( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) ) {
 
2064
                return;
 
2065
        }
 
2066
 
 
2067
        VectorCopy( cent->lerpOrigin, start );
 
2068
        start[2] += 32;
 
2069
 
 
2070
        // if the head isn't out of liquid, don't make a mark
 
2071
        contents = trap_CM_PointContents( start, 0 );
 
2072
        if ( contents & ( CONTENTS_SOLID | CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) {
 
2073
                return;
 
2074
        }
 
2075
 
 
2076
        // trace down to find the surface
 
2077
        trap_CM_BoxTrace( &trace, start, end, NULL, NULL, 0, ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) );
 
2078
 
 
2079
        if ( trace.fraction == 1.0 ) {
 
2080
                return;
 
2081
        }
 
2082
 
 
2083
        // create a mark polygon
 
2084
        VectorCopy( trace.endpos, verts[0].xyz );
 
2085
        verts[0].xyz[0] -= 32;
 
2086
        verts[0].xyz[1] -= 32;
 
2087
        verts[0].st[0] = 0;
 
2088
        verts[0].st[1] = 0;
 
2089
        verts[0].modulate[0] = 255;
 
2090
        verts[0].modulate[1] = 255;
 
2091
        verts[0].modulate[2] = 255;
 
2092
        verts[0].modulate[3] = 255;
 
2093
 
 
2094
        VectorCopy( trace.endpos, verts[1].xyz );
 
2095
        verts[1].xyz[0] -= 32;
 
2096
        verts[1].xyz[1] += 32;
 
2097
        verts[1].st[0] = 0;
 
2098
        verts[1].st[1] = 1;
 
2099
        verts[1].modulate[0] = 255;
 
2100
        verts[1].modulate[1] = 255;
 
2101
        verts[1].modulate[2] = 255;
 
2102
        verts[1].modulate[3] = 255;
 
2103
 
 
2104
        VectorCopy( trace.endpos, verts[2].xyz );
 
2105
        verts[2].xyz[0] += 32;
 
2106
        verts[2].xyz[1] += 32;
 
2107
        verts[2].st[0] = 1;
 
2108
        verts[2].st[1] = 1;
 
2109
        verts[2].modulate[0] = 255;
 
2110
        verts[2].modulate[1] = 255;
 
2111
        verts[2].modulate[2] = 255;
 
2112
        verts[2].modulate[3] = 255;
 
2113
 
 
2114
        VectorCopy( trace.endpos, verts[3].xyz );
 
2115
        verts[3].xyz[0] += 32;
 
2116
        verts[3].xyz[1] -= 32;
 
2117
        verts[3].st[0] = 1;
 
2118
        verts[3].st[1] = 0;
 
2119
        verts[3].modulate[0] = 255;
 
2120
        verts[3].modulate[1] = 255;
 
2121
        verts[3].modulate[2] = 255;
 
2122
        verts[3].modulate[3] = 255;
 
2123
 
 
2124
        trap_R_AddPolyToScene( cgs.media.wakeMarkShader, 4, verts );
 
2125
}
 
2126
 
 
2127
 
 
2128
 
 
2129
/*
 
2130
===============
 
2131
CG_AddRefEntityWithPowerups
 
2132
 
 
2133
Adds a piece with modifications or duplications for powerups
 
2134
Also called by CG_Missile for quad rockets, but nobody can tell...
 
2135
===============
 
2136
*/
 
2137
void CG_AddRefEntityWithPowerups( refEntity_t *ent, entityState_t *state, int team ) {
 
2138
 
 
2139
        if ( state->powerups & ( 1 << PW_INVIS ) ) {
 
2140
                ent->customShader = cgs.media.invisShader;
 
2141
                trap_R_AddRefEntityToScene( ent );
 
2142
        } else {
 
2143
                /*
 
2144
                if ( state->eFlags & EF_KAMIKAZE ) {
 
2145
                        if (team == TEAM_BLUE)
 
2146
                                ent->customShader = cgs.media.blueKamikazeShader;
 
2147
                        else
 
2148
                                ent->customShader = cgs.media.redKamikazeShader;
 
2149
                        trap_R_AddRefEntityToScene( ent );
 
2150
                }
 
2151
                else {*/
 
2152
                        trap_R_AddRefEntityToScene( ent );
 
2153
                //}
 
2154
 
 
2155
                if ( state->powerups & ( 1 << PW_QUAD ) )
 
2156
                {
 
2157
                        if (team == TEAM_RED)
 
2158
                                ent->customShader = cgs.media.redQuadShader;
 
2159
                        else
 
2160
                                ent->customShader = cgs.media.quadShader;
 
2161
                        trap_R_AddRefEntityToScene( ent );
 
2162
                }
 
2163
                if ( state->powerups & ( 1 << PW_REGEN ) ) {
 
2164
                        if ( ( ( cg.time / 100 ) % 10 ) == 1 ) {
 
2165
                                ent->customShader = cgs.media.regenShader;
 
2166
                                trap_R_AddRefEntityToScene( ent );
 
2167
                        }
 
2168
                }
 
2169
                if ( state->powerups & ( 1 << PW_BATTLESUIT ) ) {
 
2170
                        ent->customShader = cgs.media.battleSuitShader;
 
2171
                        trap_R_AddRefEntityToScene( ent );
 
2172
                }
 
2173
        }
 
2174
}
 
2175
 
 
2176
/*
 
2177
=================
 
2178
CG_LightVerts
 
2179
=================
 
2180
*/
 
2181
int CG_LightVerts( vec3_t normal, int numVerts, polyVert_t *verts )
 
2182
{
 
2183
        int                             i, j;
 
2184
        float                   incoming;
 
2185
        vec3_t                  ambientLight;
 
2186
        vec3_t                  lightDir;
 
2187
        vec3_t                  directedLight;
 
2188
 
 
2189
        trap_R_LightForPoint( verts[0].xyz, ambientLight, directedLight, lightDir );
 
2190
 
 
2191
        for (i = 0; i < numVerts; i++) {
 
2192
                incoming = DotProduct (normal, lightDir);
 
2193
                if ( incoming <= 0 ) {
 
2194
                        verts[i].modulate[0] = ambientLight[0];
 
2195
                        verts[i].modulate[1] = ambientLight[1];
 
2196
                        verts[i].modulate[2] = ambientLight[2];
 
2197
                        verts[i].modulate[3] = 255;
 
2198
                        continue;
 
2199
                } 
 
2200
                j = ( ambientLight[0] + incoming * directedLight[0] );
 
2201
                if ( j > 255 ) {
 
2202
                        j = 255;
 
2203
                }
 
2204
                verts[i].modulate[0] = j;
 
2205
 
 
2206
                j = ( ambientLight[1] + incoming * directedLight[1] );
 
2207
                if ( j > 255 ) {
 
2208
                        j = 255;
 
2209
                }
 
2210
                verts[i].modulate[1] = j;
 
2211
 
 
2212
                j = ( ambientLight[2] + incoming * directedLight[2] );
 
2213
                if ( j > 255 ) {
 
2214
                        j = 255;
 
2215
                }
 
2216
                verts[i].modulate[2] = j;
 
2217
 
 
2218
                verts[i].modulate[3] = 255;
 
2219
        }
 
2220
        return qtrue;
 
2221
}
 
2222
 
 
2223
/*
 
2224
===============
 
2225
CG_Player
 
2226
===============
 
2227
*/
 
2228
void CG_Player( centity_t *cent ) {
 
2229
        clientInfo_t    *ci;
 
2230
        refEntity_t             legs;
 
2231
        refEntity_t             torso;
 
2232
        refEntity_t             head;
 
2233
        int                             clientNum;
 
2234
        int                             renderfx;
 
2235
        qboolean                shadow;
 
2236
        float                   shadowPlane;
 
2237
#ifdef MISSIONPACK
 
2238
        refEntity_t             skull;
 
2239
        refEntity_t             powerup;
 
2240
        int                             t;
 
2241
        float                   c;
 
2242
        float                   angle;
 
2243
        vec3_t                  dir, angles;
 
2244
#endif
 
2245
 
 
2246
        // the client number is stored in clientNum.  It can't be derived
 
2247
        // from the entity number, because a single client may have
 
2248
        // multiple corpses on the level using the same clientinfo
 
2249
        clientNum = cent->currentState.clientNum;
 
2250
        if ( clientNum < 0 || clientNum >= MAX_CLIENTS ) {
 
2251
                CG_Error( "Bad clientNum on player entity");
 
2252
        }
 
2253
        ci = &cgs.clientinfo[ clientNum ];
 
2254
 
 
2255
        // it is possible to see corpses from disconnected players that may
 
2256
        // not have valid clientinfo
 
2257
        if ( !ci->infoValid ) {
 
2258
                return;
 
2259
        }
 
2260
 
 
2261
        // get the player model information
 
2262
        renderfx = 0;
 
2263
        if ( cent->currentState.number == cg.snap->ps.clientNum) {
 
2264
                if (!cg.renderingThirdPerson) {
 
2265
                        renderfx = RF_THIRD_PERSON;                     // only draw in mirrors
 
2266
                } else {
 
2267
                        if (cg_cameraMode.integer) {
 
2268
                                return;
 
2269
                        }
 
2270
                }
 
2271
        }
 
2272
 
 
2273
 
 
2274
        memset( &legs, 0, sizeof(legs) );
 
2275
        memset( &torso, 0, sizeof(torso) );
 
2276
        memset( &head, 0, sizeof(head) );
 
2277
 
 
2278
        // get the rotation information
 
2279
        CG_PlayerAngles( cent, legs.axis, torso.axis, head.axis );
 
2280
        
 
2281
        // get the animation state (after rotation, to allow feet shuffle)
 
2282
        CG_PlayerAnimation( cent, &legs.oldframe, &legs.frame, &legs.backlerp,
 
2283
                 &torso.oldframe, &torso.frame, &torso.backlerp );
 
2284
 
 
2285
        // add the talk baloon or disconnect icon
 
2286
        CG_PlayerSprites( cent );
 
2287
 
 
2288
        // add the shadow
 
2289
        shadow = CG_PlayerShadow( cent, &shadowPlane );
 
2290
 
 
2291
        // add a water splash if partially in and out of water
 
2292
        CG_PlayerSplash( cent );
 
2293
 
 
2294
        if ( cg_shadows.integer == 3 && shadow ) {
 
2295
                renderfx |= RF_SHADOW_PLANE;
 
2296
        }
 
2297
        renderfx |= RF_LIGHTING_ORIGIN;                 // use the same origin for all
 
2298
#ifdef MISSIONPACK
 
2299
        if( cgs.gametype == GT_HARVESTER ) {
 
2300
                CG_PlayerTokens( cent, renderfx );
 
2301
        }
 
2302
#endif
 
2303
        //
 
2304
        // add the legs
 
2305
        //
 
2306
        legs.hModel = ci->legsModel;
 
2307
        legs.customSkin = ci->legsSkin;
 
2308
 
 
2309
        VectorCopy( cent->lerpOrigin, legs.origin );
 
2310
 
 
2311
        VectorCopy( cent->lerpOrigin, legs.lightingOrigin );
 
2312
        legs.shadowPlane = shadowPlane;
 
2313
        legs.renderfx = renderfx;
 
2314
        VectorCopy (legs.origin, legs.oldorigin);       // don't positionally lerp at all
 
2315
 
 
2316
        CG_AddRefEntityWithPowerups( &legs, &cent->currentState, ci->team );
 
2317
 
 
2318
        // if the model failed, allow the default nullmodel to be displayed
 
2319
        if (!legs.hModel) {
 
2320
                return;
 
2321
        }
 
2322
 
 
2323
        //
 
2324
        // add the torso
 
2325
        //
 
2326
        torso.hModel = ci->torsoModel;
 
2327
        if (!torso.hModel) {
 
2328
                return;
 
2329
        }
 
2330
 
 
2331
        torso.customSkin = ci->torsoSkin;
 
2332
 
 
2333
        VectorCopy( cent->lerpOrigin, torso.lightingOrigin );
 
2334
 
 
2335
        CG_PositionRotatedEntityOnTag( &torso, &legs, ci->legsModel, "tag_torso");
 
2336
 
 
2337
        torso.shadowPlane = shadowPlane;
 
2338
        torso.renderfx = renderfx;
 
2339
 
 
2340
        CG_AddRefEntityWithPowerups( &torso, &cent->currentState, ci->team );
 
2341
 
 
2342
#ifdef MISSIONPACK
 
2343
        if ( cent->currentState.eFlags & EF_KAMIKAZE ) {
 
2344
 
 
2345
                memset( &skull, 0, sizeof(skull) );
 
2346
 
 
2347
                VectorCopy( cent->lerpOrigin, skull.lightingOrigin );
 
2348
                skull.shadowPlane = shadowPlane;
 
2349
                skull.renderfx = renderfx;
 
2350
 
 
2351
                if ( cent->currentState.eFlags & EF_DEAD ) {
 
2352
                        // one skull bobbing above the dead body
 
2353
                        angle = ((cg.time / 7) & 255) * (M_PI * 2) / 255;
 
2354
                        if (angle > M_PI * 2)
 
2355
                                angle -= (float)M_PI * 2;
 
2356
                        dir[0] = sin(angle) * 20;
 
2357
                        dir[1] = cos(angle) * 20;
 
2358
                        angle = ((cg.time / 4) & 255) * (M_PI * 2) / 255;
 
2359
                        dir[2] = 15 + sin(angle) * 8;
 
2360
                        VectorAdd(torso.origin, dir, skull.origin);
 
2361
                        
 
2362
                        dir[2] = 0;
 
2363
                        VectorCopy(dir, skull.axis[1]);
 
2364
                        VectorNormalize(skull.axis[1]);
 
2365
                        VectorSet(skull.axis[2], 0, 0, 1);
 
2366
                        CrossProduct(skull.axis[1], skull.axis[2], skull.axis[0]);
 
2367
 
 
2368
                        skull.hModel = cgs.media.kamikazeHeadModel;
 
2369
                        trap_R_AddRefEntityToScene( &skull );
 
2370
                        skull.hModel = cgs.media.kamikazeHeadTrail;
 
2371
                        trap_R_AddRefEntityToScene( &skull );
 
2372
                }
 
2373
                else {
 
2374
                        // three skulls spinning around the player
 
2375
                        angle = ((cg.time / 4) & 255) * (M_PI * 2) / 255;
 
2376
                        dir[0] = cos(angle) * 20;
 
2377
                        dir[1] = sin(angle) * 20;
 
2378
                        dir[2] = cos(angle) * 20;
 
2379
                        VectorAdd(torso.origin, dir, skull.origin);
 
2380
 
 
2381
                        angles[0] = sin(angle) * 30;
 
2382
                        angles[1] = (angle * 180 / M_PI) + 90;
 
2383
                        if (angles[1] > 360)
 
2384
                                angles[1] -= 360;
 
2385
                        angles[2] = 0;
 
2386
                        AnglesToAxis( angles, skull.axis );
 
2387
 
 
2388
                        /*
 
2389
                        dir[2] = 0;
 
2390
                        VectorInverse(dir);
 
2391
                        VectorCopy(dir, skull.axis[1]);
 
2392
                        VectorNormalize(skull.axis[1]);
 
2393
                        VectorSet(skull.axis[2], 0, 0, 1);
 
2394
                        CrossProduct(skull.axis[1], skull.axis[2], skull.axis[0]);
 
2395
                        */
 
2396
 
 
2397
                        skull.hModel = cgs.media.kamikazeHeadModel;
 
2398
                        trap_R_AddRefEntityToScene( &skull );
 
2399
                        // flip the trail because this skull is spinning in the other direction
 
2400
                        VectorInverse(skull.axis[1]);
 
2401
                        skull.hModel = cgs.media.kamikazeHeadTrail;
 
2402
                        trap_R_AddRefEntityToScene( &skull );
 
2403
 
 
2404
                        angle = ((cg.time / 4) & 255) * (M_PI * 2) / 255 + M_PI;
 
2405
                        if (angle > M_PI * 2)
 
2406
                                angle -= (float)M_PI * 2;
 
2407
                        dir[0] = sin(angle) * 20;
 
2408
                        dir[1] = cos(angle) * 20;
 
2409
                        dir[2] = cos(angle) * 20;
 
2410
                        VectorAdd(torso.origin, dir, skull.origin);
 
2411
 
 
2412
                        angles[0] = cos(angle - 0.5 * M_PI) * 30;
 
2413
                        angles[1] = 360 - (angle * 180 / M_PI);
 
2414
                        if (angles[1] > 360)
 
2415
                                angles[1] -= 360;
 
2416
                        angles[2] = 0;
 
2417
                        AnglesToAxis( angles, skull.axis );
 
2418
 
 
2419
                        /*
 
2420
                        dir[2] = 0;
 
2421
                        VectorCopy(dir, skull.axis[1]);
 
2422
                        VectorNormalize(skull.axis[1]);
 
2423
                        VectorSet(skull.axis[2], 0, 0, 1);
 
2424
                        CrossProduct(skull.axis[1], skull.axis[2], skull.axis[0]);
 
2425
                        */
 
2426
 
 
2427
                        skull.hModel = cgs.media.kamikazeHeadModel;
 
2428
                        trap_R_AddRefEntityToScene( &skull );
 
2429
                        skull.hModel = cgs.media.kamikazeHeadTrail;
 
2430
                        trap_R_AddRefEntityToScene( &skull );
 
2431
 
 
2432
                        angle = ((cg.time / 3) & 255) * (M_PI * 2) / 255 + 0.5 * M_PI;
 
2433
                        if (angle > M_PI * 2)
 
2434
                                angle -= (float)M_PI * 2;
 
2435
                        dir[0] = sin(angle) * 20;
 
2436
                        dir[1] = cos(angle) * 20;
 
2437
                        dir[2] = 0;
 
2438
                        VectorAdd(torso.origin, dir, skull.origin);
 
2439
                        
 
2440
                        VectorCopy(dir, skull.axis[1]);
 
2441
                        VectorNormalize(skull.axis[1]);
 
2442
                        VectorSet(skull.axis[2], 0, 0, 1);
 
2443
                        CrossProduct(skull.axis[1], skull.axis[2], skull.axis[0]);
 
2444
 
 
2445
                        skull.hModel = cgs.media.kamikazeHeadModel;
 
2446
                        trap_R_AddRefEntityToScene( &skull );
 
2447
                        skull.hModel = cgs.media.kamikazeHeadTrail;
 
2448
                        trap_R_AddRefEntityToScene( &skull );
 
2449
                }
 
2450
        }
 
2451
 
 
2452
        if ( cent->currentState.powerups & ( 1 << PW_GUARD ) ) {
 
2453
                memcpy(&powerup, &torso, sizeof(torso));
 
2454
                powerup.hModel = cgs.media.guardPowerupModel;
 
2455
                powerup.frame = 0;
 
2456
                powerup.oldframe = 0;
 
2457
                powerup.customSkin = 0;
 
2458
                trap_R_AddRefEntityToScene( &powerup );
 
2459
        }
 
2460
        if ( cent->currentState.powerups & ( 1 << PW_SCOUT ) ) {
 
2461
                memcpy(&powerup, &torso, sizeof(torso));
 
2462
                powerup.hModel = cgs.media.scoutPowerupModel;
 
2463
                powerup.frame = 0;
 
2464
                powerup.oldframe = 0;
 
2465
                powerup.customSkin = 0;
 
2466
                trap_R_AddRefEntityToScene( &powerup );
 
2467
        }
 
2468
        if ( cent->currentState.powerups & ( 1 << PW_DOUBLER ) ) {
 
2469
                memcpy(&powerup, &torso, sizeof(torso));
 
2470
                powerup.hModel = cgs.media.doublerPowerupModel;
 
2471
                powerup.frame = 0;
 
2472
                powerup.oldframe = 0;
 
2473
                powerup.customSkin = 0;
 
2474
                trap_R_AddRefEntityToScene( &powerup );
 
2475
        }
 
2476
        if ( cent->currentState.powerups & ( 1 << PW_AMMOREGEN ) ) {
 
2477
                memcpy(&powerup, &torso, sizeof(torso));
 
2478
                powerup.hModel = cgs.media.ammoRegenPowerupModel;
 
2479
                powerup.frame = 0;
 
2480
                powerup.oldframe = 0;
 
2481
                powerup.customSkin = 0;
 
2482
                trap_R_AddRefEntityToScene( &powerup );
 
2483
        }
 
2484
        if ( cent->currentState.powerups & ( 1 << PW_INVULNERABILITY ) ) {
 
2485
                if ( !ci->invulnerabilityStartTime ) {
 
2486
                        ci->invulnerabilityStartTime = cg.time;
 
2487
                }
 
2488
                ci->invulnerabilityStopTime = cg.time;
 
2489
        }
 
2490
        else {
 
2491
                ci->invulnerabilityStartTime = 0;
 
2492
        }
 
2493
        if ( (cent->currentState.powerups & ( 1 << PW_INVULNERABILITY ) ) ||
 
2494
                cg.time - ci->invulnerabilityStopTime < 250 ) {
 
2495
 
 
2496
                memcpy(&powerup, &torso, sizeof(torso));
 
2497
                powerup.hModel = cgs.media.invulnerabilityPowerupModel;
 
2498
                powerup.customSkin = 0;
 
2499
                // always draw
 
2500
                powerup.renderfx &= ~RF_THIRD_PERSON;
 
2501
                VectorCopy(cent->lerpOrigin, powerup.origin);
 
2502
 
 
2503
                if ( cg.time - ci->invulnerabilityStartTime < 250 ) {
 
2504
                        c = (float) (cg.time - ci->invulnerabilityStartTime) / 250;
 
2505
                }
 
2506
                else if (cg.time - ci->invulnerabilityStopTime < 250 ) {
 
2507
                        c = (float) (250 - (cg.time - ci->invulnerabilityStopTime)) / 250;
 
2508
                }
 
2509
                else {
 
2510
                        c = 1;
 
2511
                }
 
2512
                VectorSet( powerup.axis[0], c, 0, 0 );
 
2513
                VectorSet( powerup.axis[1], 0, c, 0 );
 
2514
                VectorSet( powerup.axis[2], 0, 0, c );
 
2515
                trap_R_AddRefEntityToScene( &powerup );
 
2516
        }
 
2517
 
 
2518
        t = cg.time - ci->medkitUsageTime;
 
2519
        if ( ci->medkitUsageTime && t < 500 ) {
 
2520
                memcpy(&powerup, &torso, sizeof(torso));
 
2521
                powerup.hModel = cgs.media.medkitUsageModel;
 
2522
                powerup.customSkin = 0;
 
2523
                // always draw
 
2524
                powerup.renderfx &= ~RF_THIRD_PERSON;
 
2525
                VectorClear(angles);
 
2526
                AnglesToAxis(angles, powerup.axis);
 
2527
                VectorCopy(cent->lerpOrigin, powerup.origin);
 
2528
                powerup.origin[2] += -24 + (float) t * 80 / 500;
 
2529
                if ( t > 400 ) {
 
2530
                        c = (float) (t - 1000) * 0xff / 100;
 
2531
                        powerup.shaderRGBA[0] = 0xff - c;
 
2532
                        powerup.shaderRGBA[1] = 0xff - c;
 
2533
                        powerup.shaderRGBA[2] = 0xff - c;
 
2534
                        powerup.shaderRGBA[3] = 0xff - c;
 
2535
                }
 
2536
                else {
 
2537
                        powerup.shaderRGBA[0] = 0xff;
 
2538
                        powerup.shaderRGBA[1] = 0xff;
 
2539
                        powerup.shaderRGBA[2] = 0xff;
 
2540
                        powerup.shaderRGBA[3] = 0xff;
 
2541
                }
 
2542
                trap_R_AddRefEntityToScene( &powerup );
 
2543
        }
 
2544
#endif // MISSIONPACK
 
2545
 
 
2546
        //
 
2547
        // add the head
 
2548
        //
 
2549
        head.hModel = ci->headModel;
 
2550
        if (!head.hModel) {
 
2551
                return;
 
2552
        }
 
2553
        head.customSkin = ci->headSkin;
 
2554
 
 
2555
        VectorCopy( cent->lerpOrigin, head.lightingOrigin );
 
2556
 
 
2557
        CG_PositionRotatedEntityOnTag( &head, &torso, ci->torsoModel, "tag_head");
 
2558
 
 
2559
        head.shadowPlane = shadowPlane;
 
2560
        head.renderfx = renderfx;
 
2561
 
 
2562
        CG_AddRefEntityWithPowerups( &head, &cent->currentState, ci->team );
 
2563
 
 
2564
#ifdef MISSIONPACK
 
2565
        CG_BreathPuffs(cent, &head);
 
2566
 
 
2567
        CG_DustTrail(cent);
 
2568
#endif
 
2569
 
 
2570
        //
 
2571
        // add the gun / barrel / flash
 
2572
        //
 
2573
        CG_AddPlayerWeapon( &torso, NULL, cent, ci->team );
 
2574
 
 
2575
        // add powerups floating behind the player
 
2576
        CG_PlayerPowerups( cent, &torso );
 
2577
}
 
2578
 
 
2579
 
 
2580
//=====================================================================
 
2581
 
 
2582
/*
 
2583
===============
 
2584
CG_ResetPlayerEntity
 
2585
 
 
2586
A player just came into view or teleported, so reset all animation info
 
2587
===============
 
2588
*/
 
2589
void CG_ResetPlayerEntity( centity_t *cent ) {
 
2590
        cent->errorTime = -99999;               // guarantee no error decay added
 
2591
        cent->extrapolated = qfalse;    
 
2592
 
 
2593
        CG_ClearLerpFrame( &cgs.clientinfo[ cent->currentState.clientNum ], &cent->pe.legs, cent->currentState.legsAnim );
 
2594
        CG_ClearLerpFrame( &cgs.clientinfo[ cent->currentState.clientNum ], &cent->pe.torso, cent->currentState.torsoAnim );
 
2595
 
 
2596
        BG_EvaluateTrajectory( &cent->currentState.pos, cg.time, cent->lerpOrigin );
 
2597
        BG_EvaluateTrajectory( &cent->currentState.apos, cg.time, cent->lerpAngles );
 
2598
 
 
2599
        VectorCopy( cent->lerpOrigin, cent->rawOrigin );
 
2600
        VectorCopy( cent->lerpAngles, cent->rawAngles );
 
2601
 
 
2602
        memset( &cent->pe.legs, 0, sizeof( cent->pe.legs ) );
 
2603
        cent->pe.legs.yawAngle = cent->rawAngles[YAW];
 
2604
        cent->pe.legs.yawing = qfalse;
 
2605
        cent->pe.legs.pitchAngle = 0;
 
2606
        cent->pe.legs.pitching = qfalse;
 
2607
 
 
2608
        memset( &cent->pe.torso, 0, sizeof( cent->pe.legs ) );
 
2609
        cent->pe.torso.yawAngle = cent->rawAngles[YAW];
 
2610
        cent->pe.torso.yawing = qfalse;
 
2611
        cent->pe.torso.pitchAngle = cent->rawAngles[PITCH];
 
2612
        cent->pe.torso.pitching = qfalse;
 
2613
 
 
2614
        if ( cg_debugPosition.integer ) {
 
2615
                CG_Printf("%i ResetPlayerEntity yaw=%i\n", cent->currentState.number, cent->pe.torso.yawAngle );
 
2616
        }
 
2617
}
 
2618