2
===========================================================================
3
Copyright (C) 1999-2005 Id Software, Inc.
5
This file is part of Quake III Arena source code.
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.
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.
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
===========================================================================
28
#define UI_TIMER_GESTURE 2300
29
#define UI_TIMER_JUMP 1000
30
#define UI_TIMER_LAND 130
31
#define UI_TIMER_WEAPON_SWITCH 300
32
#define UI_TIMER_ATTACK 500
33
#define UI_TIMER_MUZZLE_FLASH 20
34
#define UI_TIMER_WEAPON_DELAY 250
36
#define JUMP_HEIGHT 56
38
#define SWINGSPEED 0.3f
40
#define SPIN_SPEED 0.9f
41
#define COAST_TIME 1000
44
static int dp_realtime;
45
static float jumpHeight;
50
UI_PlayerInfo_SetWeapon
53
static void UI_PlayerInfo_SetWeapon( playerInfo_t *pi, weapon_t weaponNum ) {
57
pi->currentWeapon = weaponNum;
59
pi->realWeapon = weaponNum;
64
if ( weaponNum == WP_NONE ) {
68
for ( item = bg_itemlist + 1; item->classname ; item++ ) {
69
if ( item->giType != IT_WEAPON ) {
72
if ( item->giTag == weaponNum ) {
77
if ( item->classname ) {
78
pi->weaponModel = trap_R_RegisterModel( item->world_model[0] );
81
if( pi->weaponModel == 0 ) {
82
if( weaponNum == WP_MACHINEGUN ) {
86
weaponNum = WP_MACHINEGUN;
90
if ( weaponNum == WP_MACHINEGUN || weaponNum == WP_GAUNTLET || weaponNum == WP_BFG ) {
91
strcpy( path, item->world_model[0] );
92
COM_StripExtension( path, path, sizeof(path) );
93
strcat( path, "_barrel.md3" );
94
pi->barrelModel = trap_R_RegisterModel( path );
97
strcpy( path, item->world_model[0] );
98
COM_StripExtension( path, path, sizeof(path) );
99
strcat( path, "_flash.md3" );
100
pi->flashModel = trap_R_RegisterModel( path );
102
switch( weaponNum ) {
104
MAKERGB( pi->flashDlightColor, 0.6f, 0.6f, 1 );
108
MAKERGB( pi->flashDlightColor, 1, 1, 0 );
112
MAKERGB( pi->flashDlightColor, 1, 1, 0 );
115
case WP_GRENADE_LAUNCHER:
116
MAKERGB( pi->flashDlightColor, 1, 0.7f, 0.5f );
119
case WP_ROCKET_LAUNCHER:
120
MAKERGB( pi->flashDlightColor, 1, 0.75f, 0 );
124
MAKERGB( pi->flashDlightColor, 0.6f, 0.6f, 1 );
128
MAKERGB( pi->flashDlightColor, 1, 0.5f, 0 );
132
MAKERGB( pi->flashDlightColor, 0.6f, 0.6f, 1 );
136
MAKERGB( pi->flashDlightColor, 1, 0.7f, 1 );
139
case WP_GRAPPLING_HOOK:
140
MAKERGB( pi->flashDlightColor, 0.6f, 0.6f, 1 );
144
MAKERGB( pi->flashDlightColor, 1, 1, 1 );
155
static void UI_ForceLegsAnim( playerInfo_t *pi, int anim ) {
156
pi->legsAnim = ( ( pi->legsAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim;
158
if ( anim == LEGS_JUMP ) {
159
pi->legsAnimationTimer = UI_TIMER_JUMP;
169
static void UI_SetLegsAnim( playerInfo_t *pi, int anim ) {
170
if ( pi->pendingLegsAnim ) {
171
anim = pi->pendingLegsAnim;
172
pi->pendingLegsAnim = 0;
174
UI_ForceLegsAnim( pi, anim );
183
static void UI_ForceTorsoAnim( playerInfo_t *pi, int anim ) {
184
pi->torsoAnim = ( ( pi->torsoAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim;
186
if ( anim == TORSO_GESTURE ) {
187
pi->torsoAnimationTimer = UI_TIMER_GESTURE;
190
if ( anim == TORSO_ATTACK || anim == TORSO_ATTACK2 ) {
191
pi->torsoAnimationTimer = UI_TIMER_ATTACK;
201
static void UI_SetTorsoAnim( playerInfo_t *pi, int anim ) {
202
if ( pi->pendingTorsoAnim ) {
203
anim = pi->pendingTorsoAnim;
204
pi->pendingTorsoAnim = 0;
207
UI_ForceTorsoAnim( pi, anim );
216
static void UI_TorsoSequencing( playerInfo_t *pi ) {
219
currentAnim = pi->torsoAnim & ~ANIM_TOGGLEBIT;
221
if ( pi->weapon != pi->currentWeapon ) {
222
if ( currentAnim != TORSO_DROP ) {
223
pi->torsoAnimationTimer = UI_TIMER_WEAPON_SWITCH;
224
UI_ForceTorsoAnim( pi, TORSO_DROP );
228
if ( pi->torsoAnimationTimer > 0 ) {
232
if( currentAnim == TORSO_GESTURE ) {
233
UI_SetTorsoAnim( pi, TORSO_STAND );
237
if( currentAnim == TORSO_ATTACK || currentAnim == TORSO_ATTACK2 ) {
238
UI_SetTorsoAnim( pi, TORSO_STAND );
242
if ( currentAnim == TORSO_DROP ) {
243
UI_PlayerInfo_SetWeapon( pi, pi->weapon );
244
pi->torsoAnimationTimer = UI_TIMER_WEAPON_SWITCH;
245
UI_ForceTorsoAnim( pi, TORSO_RAISE );
249
if ( currentAnim == TORSO_RAISE ) {
250
UI_SetTorsoAnim( pi, TORSO_STAND );
261
static void UI_LegsSequencing( playerInfo_t *pi ) {
264
currentAnim = pi->legsAnim & ~ANIM_TOGGLEBIT;
266
if ( pi->legsAnimationTimer > 0 ) {
267
if ( currentAnim == LEGS_JUMP ) {
268
jumpHeight = JUMP_HEIGHT * sin( M_PI * ( UI_TIMER_JUMP - pi->legsAnimationTimer ) / UI_TIMER_JUMP );
273
if ( currentAnim == LEGS_JUMP ) {
274
UI_ForceLegsAnim( pi, LEGS_LAND );
275
pi->legsAnimationTimer = UI_TIMER_LAND;
280
if ( currentAnim == LEGS_LAND ) {
281
UI_SetLegsAnim( pi, LEGS_IDLE );
288
======================
289
UI_PositionEntityOnTag
290
======================
292
static void UI_PositionEntityOnTag( refEntity_t *entity, const refEntity_t *parent,
293
clipHandle_t parentModel, char *tagName ) {
295
orientation_t lerped;
298
trap_CM_LerpTag( &lerped, parentModel, parent->oldframe, parent->frame,
299
1.0 - parent->backlerp, tagName );
301
// FIXME: allow origin offsets along tag?
302
VectorCopy( parent->origin, entity->origin );
303
for ( i = 0 ; i < 3 ; i++ ) {
304
VectorMA( entity->origin, lerped.origin[i], parent->axis[i], entity->origin );
307
// cast away const because of compiler problems
308
MatrixMultiply( lerped.axis, ((refEntity_t*)parent)->axis, entity->axis );
309
entity->backlerp = parent->backlerp;
314
======================
315
UI_PositionRotatedEntityOnTag
316
======================
318
static void UI_PositionRotatedEntityOnTag( refEntity_t *entity, const refEntity_t *parent,
319
clipHandle_t parentModel, char *tagName ) {
321
orientation_t lerped;
325
trap_CM_LerpTag( &lerped, parentModel, parent->oldframe, parent->frame,
326
1.0 - parent->backlerp, tagName );
328
// FIXME: allow origin offsets along tag?
329
VectorCopy( parent->origin, entity->origin );
330
for ( i = 0 ; i < 3 ; i++ ) {
331
VectorMA( entity->origin, lerped.origin[i], parent->axis[i], entity->origin );
334
// cast away const because of compiler problems
335
MatrixMultiply( entity->axis, ((refEntity_t *)parent)->axis, tempAxis );
336
MatrixMultiply( lerped.axis, tempAxis, entity->axis );
342
UI_SetLerpFrameAnimation
345
static void UI_SetLerpFrameAnimation( playerInfo_t *ci, lerpFrame_t *lf, int newAnimation ) {
348
lf->animationNumber = newAnimation;
349
newAnimation &= ~ANIM_TOGGLEBIT;
351
if ( newAnimation < 0 || newAnimation >= MAX_ANIMATIONS ) {
352
trap_Error( va("Bad animation number: %i", newAnimation) );
355
anim = &ci->animations[ newAnimation ];
357
lf->animation = anim;
358
lf->animationTime = lf->frameTime + anim->initialLerp;
367
static void UI_RunLerpFrame( playerInfo_t *ci, lerpFrame_t *lf, int newAnimation ) {
371
// see if the animation sequence is switching
372
if ( newAnimation != lf->animationNumber || !lf->animation ) {
373
UI_SetLerpFrameAnimation( ci, lf, newAnimation );
376
// if we have passed the current frame, move it to
377
// oldFrame and calculate a new frame
378
if ( dp_realtime >= lf->frameTime ) {
379
lf->oldFrame = lf->frame;
380
lf->oldFrameTime = lf->frameTime;
382
// get the next frame based on the animation
383
anim = lf->animation;
384
if ( dp_realtime < lf->animationTime ) {
385
lf->frameTime = lf->animationTime; // initial lerp
387
lf->frameTime = lf->oldFrameTime + anim->frameLerp;
389
f = ( lf->frameTime - lf->animationTime ) / anim->frameLerp;
390
if ( f >= anim->numFrames ) {
391
f -= anim->numFrames;
392
if ( anim->loopFrames ) {
393
f %= anim->loopFrames;
394
f += anim->numFrames - anim->loopFrames;
396
f = anim->numFrames - 1;
397
// the animation is stuck at the end, so it
398
// can immediately transition to another sequence
399
lf->frameTime = dp_realtime;
402
lf->frame = anim->firstFrame + f;
403
if ( dp_realtime > lf->frameTime ) {
404
lf->frameTime = dp_realtime;
408
if ( lf->frameTime > dp_realtime + 200 ) {
409
lf->frameTime = dp_realtime;
412
if ( lf->oldFrameTime > dp_realtime ) {
413
lf->oldFrameTime = dp_realtime;
415
// calculate current lerp value
416
if ( lf->frameTime == lf->oldFrameTime ) {
419
lf->backlerp = 1.0 - (float)( dp_realtime - lf->oldFrameTime ) / ( lf->frameTime - lf->oldFrameTime );
429
static void UI_PlayerAnimation( playerInfo_t *pi, int *legsOld, int *legs, float *legsBackLerp,
430
int *torsoOld, int *torso, float *torsoBackLerp ) {
433
pi->legsAnimationTimer -= uis.frametime;
434
if ( pi->legsAnimationTimer < 0 ) {
435
pi->legsAnimationTimer = 0;
438
UI_LegsSequencing( pi );
440
if ( pi->legs.yawing && ( pi->legsAnim & ~ANIM_TOGGLEBIT ) == LEGS_IDLE ) {
441
UI_RunLerpFrame( pi, &pi->legs, LEGS_TURN );
443
UI_RunLerpFrame( pi, &pi->legs, pi->legsAnim );
445
*legsOld = pi->legs.oldFrame;
446
*legs = pi->legs.frame;
447
*legsBackLerp = pi->legs.backlerp;
450
pi->torsoAnimationTimer -= uis.frametime;
451
if ( pi->torsoAnimationTimer < 0 ) {
452
pi->torsoAnimationTimer = 0;
455
UI_TorsoSequencing( pi );
457
UI_RunLerpFrame( pi, &pi->torso, pi->torsoAnim );
458
*torsoOld = pi->torso.oldFrame;
459
*torso = pi->torso.frame;
460
*torsoBackLerp = pi->torso.backlerp;
469
static void UI_SwingAngles( float destination, float swingTolerance, float clampTolerance,
470
float speed, float *angle, qboolean *swinging ) {
476
// see if a swing should be started
477
swing = AngleSubtract( *angle, destination );
478
if ( swing > swingTolerance || swing < -swingTolerance ) {
487
// modify the speed depending on the delta
488
// so it doesn't seem so linear
489
swing = AngleSubtract( destination, *angle );
490
scale = fabs( swing );
491
if ( scale < swingTolerance * 0.5 ) {
493
} else if ( scale < swingTolerance ) {
499
// swing towards the destination angle
501
move = uis.frametime * scale * speed;
502
if ( move >= swing ) {
506
*angle = AngleMod( *angle + move );
507
} else if ( swing < 0 ) {
508
move = uis.frametime * scale * -speed;
509
if ( move <= swing ) {
513
*angle = AngleMod( *angle + move );
516
// clamp to no more than tolerance
517
swing = AngleSubtract( destination, *angle );
518
if ( swing > clampTolerance ) {
519
*angle = AngleMod( destination - (clampTolerance - 1) );
520
} else if ( swing < -clampTolerance ) {
521
*angle = AngleMod( destination + (clampTolerance - 1) );
527
======================
529
======================
531
static float UI_MovedirAdjustment( playerInfo_t *pi ) {
532
vec3_t relativeAngles;
535
VectorSubtract( pi->viewAngles, pi->moveAngles, relativeAngles );
536
AngleVectors( relativeAngles, moveVector, NULL, NULL );
537
if ( Q_fabs( moveVector[0] ) < 0.01 ) {
540
if ( Q_fabs( moveVector[1] ) < 0.01 ) {
544
if ( moveVector[1] == 0 && moveVector[0] > 0 ) {
547
if ( moveVector[1] < 0 && moveVector[0] > 0 ) {
550
if ( moveVector[1] < 0 && moveVector[0] == 0 ) {
553
if ( moveVector[1] < 0 && moveVector[0] < 0 ) {
556
if ( moveVector[1] == 0 && moveVector[0] < 0 ) {
559
if ( moveVector[1] > 0 && moveVector[0] < 0 ) {
562
if ( moveVector[1] > 0 && moveVector[0] == 0 ) {
575
static void UI_PlayerAngles( playerInfo_t *pi, vec3_t legs[3], vec3_t torso[3], vec3_t head[3] ) {
576
vec3_t legsAngles, torsoAngles, headAngles;
580
VectorCopy( pi->viewAngles, headAngles );
581
headAngles[YAW] = AngleMod( headAngles[YAW] );
582
VectorClear( legsAngles );
583
VectorClear( torsoAngles );
585
// --------- yaw -------------
587
// allow yaw to drift a bit
588
if ( ( pi->legsAnim & ~ANIM_TOGGLEBIT ) != LEGS_IDLE
589
|| ( pi->torsoAnim & ~ANIM_TOGGLEBIT ) != TORSO_STAND ) {
590
// if not standing still, always point all in the same direction
591
pi->torso.yawing = qtrue; // always center
592
pi->torso.pitching = qtrue; // always center
593
pi->legs.yawing = qtrue; // always center
596
// adjust legs for movement dir
597
adjust = UI_MovedirAdjustment( pi );
598
legsAngles[YAW] = headAngles[YAW] + adjust;
599
torsoAngles[YAW] = headAngles[YAW] + 0.25 * adjust;
603
UI_SwingAngles( torsoAngles[YAW], 25, 90, SWINGSPEED, &pi->torso.yawAngle, &pi->torso.yawing );
604
UI_SwingAngles( legsAngles[YAW], 40, 90, SWINGSPEED, &pi->legs.yawAngle, &pi->legs.yawing );
606
torsoAngles[YAW] = pi->torso.yawAngle;
607
legsAngles[YAW] = pi->legs.yawAngle;
609
// --------- pitch -------------
611
// only show a fraction of the pitch angle in the torso
612
if ( headAngles[PITCH] > 180 ) {
613
dest = (-360 + headAngles[PITCH]) * 0.75;
615
dest = headAngles[PITCH] * 0.75;
617
UI_SwingAngles( dest, 15, 30, 0.1f, &pi->torso.pitchAngle, &pi->torso.pitching );
618
torsoAngles[PITCH] = pi->torso.pitchAngle;
620
// pull the angles back out of the hierarchial chain
621
AnglesSubtract( headAngles, torsoAngles, headAngles );
622
AnglesSubtract( torsoAngles, legsAngles, torsoAngles );
623
AnglesToAxis( legsAngles, legs );
624
AnglesToAxis( torsoAngles, torso );
625
AnglesToAxis( headAngles, head );
634
static void UI_PlayerFloatSprite( playerInfo_t *pi, vec3_t origin, qhandle_t shader ) {
637
memset( &ent, 0, sizeof( ent ) );
638
VectorCopy( origin, ent.origin );
640
ent.reType = RT_SPRITE;
641
ent.customShader = shader;
644
trap_R_AddRefEntityToScene( &ent );
649
======================
650
UI_MachinegunSpinAngle
651
======================
653
float UI_MachinegunSpinAngle( playerInfo_t *pi ) {
659
delta = dp_realtime - pi->barrelTime;
660
if ( pi->barrelSpinning ) {
661
angle = pi->barrelAngle + delta * SPIN_SPEED;
663
if ( delta > COAST_TIME ) {
667
speed = 0.5 * ( SPIN_SPEED + (float)( COAST_TIME - delta ) / COAST_TIME );
668
angle = pi->barrelAngle + delta * speed;
671
torsoAnim = pi->torsoAnim & ~ANIM_TOGGLEBIT;
672
if( torsoAnim == TORSO_ATTACK2 ) {
673
torsoAnim = TORSO_ATTACK;
675
if ( pi->barrelSpinning == !(torsoAnim == TORSO_ATTACK) ) {
676
pi->barrelTime = dp_realtime;
677
pi->barrelAngle = AngleMod( angle );
678
pi->barrelSpinning = !!(torsoAnim == TORSO_ATTACK);
690
void UI_DrawPlayer( float x, float y, float w, float h, playerInfo_t *pi, int time ) {
700
vec3_t mins = {-16, -16, -24};
701
vec3_t maxs = {16, 16, 32};
705
if ( !pi->legsModel || !pi->torsoModel || !pi->headModel || !pi->animations[0].numFrames ) {
711
if ( pi->pendingWeapon != -1 && dp_realtime > pi->weaponTimer ) {
712
pi->weapon = pi->pendingWeapon;
713
pi->lastWeapon = pi->pendingWeapon;
714
pi->pendingWeapon = -1;
716
if( pi->currentWeapon != pi->weapon ) {
717
trap_S_StartLocalSound( weaponChangeSound, CHAN_LOCAL );
721
UI_AdjustFrom640( &x, &y, &w, &h );
725
memset( &refdef, 0, sizeof( refdef ) );
726
memset( &legs, 0, sizeof(legs) );
727
memset( &torso, 0, sizeof(torso) );
728
memset( &head, 0, sizeof(head) );
730
refdef.rdflags = RDF_NOWORLDMODEL;
732
AxisClear( refdef.viewaxis );
739
refdef.fov_x = (int)((float)refdef.width / 640.0f * 90.0f);
740
xx = refdef.width / tan( refdef.fov_x / 360 * M_PI );
741
refdef.fov_y = atan2( refdef.height, xx );
742
refdef.fov_y *= ( 360 / M_PI );
744
// calculate distance so the player nearly fills the box
745
len = 0.7 * ( maxs[2] - mins[2] );
746
origin[0] = len / tan( DEG2RAD(refdef.fov_x) * 0.5 );
747
origin[1] = 0.5 * ( mins[1] + maxs[1] );
748
origin[2] = -0.5 * ( mins[2] + maxs[2] );
750
refdef.time = dp_realtime;
754
// get the rotation information
755
UI_PlayerAngles( pi, legs.axis, torso.axis, head.axis );
757
// get the animation state (after rotation, to allow feet shuffle)
758
UI_PlayerAnimation( pi, &legs.oldframe, &legs.frame, &legs.backlerp,
759
&torso.oldframe, &torso.frame, &torso.backlerp );
761
renderfx = RF_LIGHTING_ORIGIN | RF_NOSHADOW;
766
legs.hModel = pi->legsModel;
767
legs.customSkin = pi->legsSkin;
769
VectorCopy( origin, legs.origin );
771
VectorCopy( origin, legs.lightingOrigin );
772
legs.renderfx = renderfx;
773
VectorCopy (legs.origin, legs.oldorigin);
775
trap_R_AddRefEntityToScene( &legs );
784
torso.hModel = pi->torsoModel;
789
torso.customSkin = pi->torsoSkin;
791
VectorCopy( origin, torso.lightingOrigin );
793
UI_PositionRotatedEntityOnTag( &torso, &legs, pi->legsModel, "tag_torso");
795
torso.renderfx = renderfx;
797
trap_R_AddRefEntityToScene( &torso );
802
head.hModel = pi->headModel;
806
head.customSkin = pi->headSkin;
808
VectorCopy( origin, head.lightingOrigin );
810
UI_PositionRotatedEntityOnTag( &head, &torso, pi->torsoModel, "tag_head");
812
head.renderfx = renderfx;
814
trap_R_AddRefEntityToScene( &head );
819
if ( pi->currentWeapon != WP_NONE ) {
820
memset( &gun, 0, sizeof(gun) );
821
gun.hModel = pi->weaponModel;
822
VectorCopy( origin, gun.lightingOrigin );
823
UI_PositionEntityOnTag( &gun, &torso, pi->torsoModel, "tag_weapon");
824
gun.renderfx = renderfx;
825
trap_R_AddRefEntityToScene( &gun );
829
// add the spinning barrel
831
if ( pi->realWeapon == WP_MACHINEGUN || pi->realWeapon == WP_GAUNTLET || pi->realWeapon == WP_BFG ) {
834
memset( &barrel, 0, sizeof(barrel) );
835
VectorCopy( origin, barrel.lightingOrigin );
836
barrel.renderfx = renderfx;
838
barrel.hModel = pi->barrelModel;
841
angles[ROLL] = UI_MachinegunSpinAngle( pi );
842
if( pi->realWeapon == WP_GAUNTLET || pi->realWeapon == WP_BFG ) {
843
angles[PITCH] = angles[ROLL];
846
AnglesToAxis( angles, barrel.axis );
848
UI_PositionRotatedEntityOnTag( &barrel, &gun, pi->weaponModel, "tag_barrel");
850
trap_R_AddRefEntityToScene( &barrel );
856
if ( dp_realtime <= pi->muzzleFlashTime ) {
857
if ( pi->flashModel ) {
858
memset( &flash, 0, sizeof(flash) );
859
flash.hModel = pi->flashModel;
860
VectorCopy( origin, flash.lightingOrigin );
861
UI_PositionEntityOnTag( &flash, &gun, pi->weaponModel, "tag_flash");
862
flash.renderfx = renderfx;
863
trap_R_AddRefEntityToScene( &flash );
866
// make a dlight for the flash
867
if ( pi->flashDlightColor[0] || pi->flashDlightColor[1] || pi->flashDlightColor[2] ) {
868
trap_R_AddLightToScene( flash.origin, 200 + (rand()&31), pi->flashDlightColor[0],
869
pi->flashDlightColor[1], pi->flashDlightColor[2] );
877
UI_PlayerFloatSprite( pi, origin, trap_R_RegisterShaderNoMip( "sprites/balloon3" ) );
881
// add an accent light
883
origin[0] -= 100; // + = behind, - = in front
884
origin[1] += 100; // + = left, - = right
885
origin[2] += 100; // + = above, - = below
886
trap_R_AddLightToScene( origin, 500, 1.0, 1.0, 1.0 );
891
trap_R_AddLightToScene( origin, 500, 1.0, 0.0, 0.0 );
893
trap_R_RenderScene( &refdef );
898
==========================
899
UI_RegisterClientSkin
900
==========================
902
static qboolean UI_RegisterClientSkin( playerInfo_t *pi, const char *modelName, const char *skinName ) {
903
char filename[MAX_QPATH];
905
Com_sprintf( filename, sizeof( filename ), "models/players/%s/lower_%s.skin", modelName, skinName );
906
pi->legsSkin = trap_R_RegisterSkin( filename );
908
Com_sprintf( filename, sizeof( filename ), "models/players/%s/upper_%s.skin", modelName, skinName );
909
pi->torsoSkin = trap_R_RegisterSkin( filename );
911
Com_sprintf( filename, sizeof( filename ), "models/players/%s/head_%s.skin", modelName, skinName );
912
pi->headSkin = trap_R_RegisterSkin( filename );
914
if ( !pi->legsSkin || !pi->torsoSkin || !pi->headSkin ) {
923
======================
924
UI_ParseAnimationFile
925
======================
927
static qboolean UI_ParseAnimationFile( const char *filename, animation_t *animations ) {
937
memset( animations, 0, sizeof( animation_t ) * MAX_ANIMATIONS );
940
len = trap_FS_FOpenFile( filename, &f, FS_READ );
944
if ( len >= ( sizeof( text ) - 1 ) ) {
945
Com_Printf( "File %s too long\n", filename );
946
trap_FS_FCloseFile( f );
949
trap_FS_Read( text, len, f );
951
trap_FS_FCloseFile( f );
955
skip = 0; // quite the compiler warning
957
// read optional parameters
959
prev = text_p; // so we can unget
960
token = COM_Parse( &text_p );
964
if ( !Q_stricmp( token, "footsteps" ) ) {
965
token = COM_Parse( &text_p );
970
} else if ( !Q_stricmp( token, "headoffset" ) ) {
971
for ( i = 0 ; i < 3 ; i++ ) {
972
token = COM_Parse( &text_p );
978
} else if ( !Q_stricmp( token, "sex" ) ) {
979
token = COM_Parse( &text_p );
986
// if it is a number, start parsing animations
987
if ( token[0] >= '0' && token[0] <= '9' ) {
988
text_p = prev; // unget the token
992
Com_Printf( "unknown token '%s' is %s\n", token, filename );
995
// read information for each frame
996
for ( i = 0 ; i < MAX_ANIMATIONS ; i++ ) {
998
token = COM_Parse( &text_p );
1002
animations[i].firstFrame = atoi( token );
1003
// leg only frames are adjusted to not count the upper body only frames
1004
if ( i == LEGS_WALKCR ) {
1005
skip = animations[LEGS_WALKCR].firstFrame - animations[TORSO_GESTURE].firstFrame;
1007
if ( i >= LEGS_WALKCR ) {
1008
animations[i].firstFrame -= skip;
1011
token = COM_Parse( &text_p );
1015
animations[i].numFrames = atoi( token );
1017
token = COM_Parse( &text_p );
1021
animations[i].loopFrames = atoi( token );
1023
token = COM_Parse( &text_p );
1027
fps = atof( token );
1031
animations[i].frameLerp = 1000 / fps;
1032
animations[i].initialLerp = 1000 / fps;
1035
if ( i != MAX_ANIMATIONS ) {
1036
Com_Printf( "Error parsing animation file: %s", filename );
1045
==========================
1046
UI_RegisterClientModelname
1047
==========================
1049
qboolean UI_RegisterClientModelname( playerInfo_t *pi, const char *modelSkinName ) {
1050
char modelName[MAX_QPATH];
1051
char skinName[MAX_QPATH];
1052
char filename[MAX_QPATH];
1058
if ( !modelSkinName[0] ) {
1062
Q_strncpyz( modelName, modelSkinName, sizeof( modelName ) );
1064
slash = strchr( modelName, '/' );
1066
// modelName did not include a skin name
1067
Q_strncpyz( skinName, "default", sizeof( skinName ) );
1069
Q_strncpyz( skinName, slash + 1, sizeof( skinName ) );
1070
// truncate modelName
1074
// load cmodels before models so filecache works
1076
Com_sprintf( filename, sizeof( filename ), "models/players/%s/lower.md3", modelName );
1077
pi->legsModel = trap_R_RegisterModel( filename );
1078
if ( !pi->legsModel ) {
1079
Com_Printf( "Failed to load model file %s\n", filename );
1083
Com_sprintf( filename, sizeof( filename ), "models/players/%s/upper.md3", modelName );
1084
pi->torsoModel = trap_R_RegisterModel( filename );
1085
if ( !pi->torsoModel ) {
1086
Com_Printf( "Failed to load model file %s\n", filename );
1090
Com_sprintf( filename, sizeof( filename ), "models/players/%s/head.md3", modelName );
1091
pi->headModel = trap_R_RegisterModel( filename );
1092
if ( !pi->headModel ) {
1093
Com_Printf( "Failed to load model file %s\n", filename );
1097
// if any skins failed to load, fall back to default
1098
if ( !UI_RegisterClientSkin( pi, modelName, skinName ) ) {
1099
if ( !UI_RegisterClientSkin( pi, modelName, "default" ) ) {
1100
Com_Printf( "Failed to load skin file: %s : %s\n", modelName, skinName );
1105
// load the animations
1106
Com_sprintf( filename, sizeof( filename ), "models/players/%s/animation.cfg", modelName );
1107
if ( !UI_ParseAnimationFile( filename, pi->animations ) ) {
1108
Com_Printf( "Failed to load animation file %s\n", filename );
1118
UI_PlayerInfo_SetModel
1121
void UI_PlayerInfo_SetModel( playerInfo_t *pi, const char *model ) {
1122
memset( pi, 0, sizeof(*pi) );
1123
UI_RegisterClientModelname( pi, model );
1124
pi->weapon = WP_MACHINEGUN;
1125
pi->currentWeapon = pi->weapon;
1126
pi->lastWeapon = pi->weapon;
1127
pi->pendingWeapon = -1;
1128
pi->weaponTimer = 0;
1130
pi->newModel = qtrue;
1131
UI_PlayerInfo_SetWeapon( pi, pi->weapon );
1137
UI_PlayerInfo_SetInfo
1140
void UI_PlayerInfo_SetInfo( playerInfo_t *pi, int legsAnim, int torsoAnim, vec3_t viewAngles, vec3_t moveAngles, weapon_t weaponNumber, qboolean chat ) {
1147
VectorCopy( viewAngles, pi->viewAngles );
1150
VectorCopy( moveAngles, pi->moveAngles );
1152
if ( pi->newModel ) {
1153
pi->newModel = qfalse;
1156
pi->pendingLegsAnim = 0;
1157
UI_ForceLegsAnim( pi, legsAnim );
1158
pi->legs.yawAngle = viewAngles[YAW];
1159
pi->legs.yawing = qfalse;
1161
pi->pendingTorsoAnim = 0;
1162
UI_ForceTorsoAnim( pi, torsoAnim );
1163
pi->torso.yawAngle = viewAngles[YAW];
1164
pi->torso.yawing = qfalse;
1166
if ( weaponNumber != -1 ) {
1167
pi->weapon = weaponNumber;
1168
pi->currentWeapon = weaponNumber;
1169
pi->lastWeapon = weaponNumber;
1170
pi->pendingWeapon = -1;
1171
pi->weaponTimer = 0;
1172
UI_PlayerInfo_SetWeapon( pi, pi->weapon );
1179
if ( weaponNumber == -1 ) {
1180
pi->pendingWeapon = -1;
1181
pi->weaponTimer = 0;
1183
else if ( weaponNumber != WP_NONE ) {
1184
pi->pendingWeapon = weaponNumber;
1185
pi->weaponTimer = dp_realtime + UI_TIMER_WEAPON_DELAY;
1187
weaponNum = pi->lastWeapon;
1188
pi->weapon = weaponNum;
1190
if ( torsoAnim == BOTH_DEATH1 || legsAnim == BOTH_DEATH1 ) {
1191
torsoAnim = legsAnim = BOTH_DEATH1;
1192
pi->weapon = pi->currentWeapon = WP_NONE;
1193
UI_PlayerInfo_SetWeapon( pi, pi->weapon );
1196
pi->pendingLegsAnim = 0;
1197
UI_ForceLegsAnim( pi, legsAnim );
1199
pi->pendingTorsoAnim = 0;
1200
UI_ForceTorsoAnim( pi, torsoAnim );
1206
currentAnim = pi->legsAnim & ~ANIM_TOGGLEBIT;
1207
if ( legsAnim != LEGS_JUMP && ( currentAnim == LEGS_JUMP || currentAnim == LEGS_LAND ) ) {
1208
pi->pendingLegsAnim = legsAnim;
1210
else if ( legsAnim != currentAnim ) {
1212
pi->pendingLegsAnim = 0;
1213
UI_ForceLegsAnim( pi, legsAnim );
1217
if ( torsoAnim == TORSO_STAND || torsoAnim == TORSO_STAND2 ) {
1218
if ( weaponNum == WP_NONE || weaponNum == WP_GAUNTLET ) {
1219
torsoAnim = TORSO_STAND2;
1222
torsoAnim = TORSO_STAND;
1226
if ( torsoAnim == TORSO_ATTACK || torsoAnim == TORSO_ATTACK2 ) {
1227
if ( weaponNum == WP_NONE || weaponNum == WP_GAUNTLET ) {
1228
torsoAnim = TORSO_ATTACK2;
1231
torsoAnim = TORSO_ATTACK;
1233
pi->muzzleFlashTime = dp_realtime + UI_TIMER_MUZZLE_FLASH;
1234
//FIXME play firing sound here
1237
currentAnim = pi->torsoAnim & ~ANIM_TOGGLEBIT;
1239
if ( weaponNum != pi->currentWeapon || currentAnim == TORSO_RAISE || currentAnim == TORSO_DROP ) {
1240
pi->pendingTorsoAnim = torsoAnim;
1242
else if ( ( currentAnim == TORSO_GESTURE || currentAnim == TORSO_ATTACK ) && ( torsoAnim != currentAnim ) ) {
1243
pi->pendingTorsoAnim = torsoAnim;
1245
else if ( torsoAnim != currentAnim ) {
1246
pi->pendingTorsoAnim = 0;
1247
UI_ForceTorsoAnim( pi, torsoAnim );