diff options
Diffstat (limited to 'code/q3_ui/ui_players.c')
-rwxr-xr-x | code/q3_ui/ui_players.c | 2496 |
1 files changed, 1248 insertions, 1248 deletions
diff --git a/code/q3_ui/ui_players.c b/code/q3_ui/ui_players.c index e4b98ff..547fa97 100755 --- a/code/q3_ui/ui_players.c +++ b/code/q3_ui/ui_players.c @@ -1,1248 +1,1248 @@ -/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena source code is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-//
-// ui_players.c
-
-#include "ui_local.h"
-
-
-#define UI_TIMER_GESTURE 2300
-#define UI_TIMER_JUMP 1000
-#define UI_TIMER_LAND 130
-#define UI_TIMER_WEAPON_SWITCH 300
-#define UI_TIMER_ATTACK 500
-#define UI_TIMER_MUZZLE_FLASH 20
-#define UI_TIMER_WEAPON_DELAY 250
-
-#define JUMP_HEIGHT 56
-
-#define SWINGSPEED 0.3f
-
-#define SPIN_SPEED 0.9f
-#define COAST_TIME 1000
-
-
-static int dp_realtime;
-static float jumpHeight;
-
-
-/*
-===============
-UI_PlayerInfo_SetWeapon
-===============
-*/
-static void UI_PlayerInfo_SetWeapon( playerInfo_t *pi, weapon_t weaponNum ) {
- gitem_t * item;
- char path[MAX_QPATH];
-
- pi->currentWeapon = weaponNum;
-tryagain:
- pi->realWeapon = weaponNum;
- pi->weaponModel = 0;
- pi->barrelModel = 0;
- pi->flashModel = 0;
-
- if ( weaponNum == WP_NONE ) {
- return;
- }
-
- for ( item = bg_itemlist + 1; item->classname ; item++ ) {
- if ( item->giType != IT_WEAPON ) {
- continue;
- }
- if ( item->giTag == weaponNum ) {
- break;
- }
- }
-
- if ( item->classname ) {
- pi->weaponModel = trap_R_RegisterModel( item->world_model[0] );
- }
-
- if( pi->weaponModel == 0 ) {
- if( weaponNum == WP_MACHINEGUN ) {
- weaponNum = WP_NONE;
- goto tryagain;
- }
- weaponNum = WP_MACHINEGUN;
- goto tryagain;
- }
-
- if ( weaponNum == WP_MACHINEGUN || weaponNum == WP_GAUNTLET || weaponNum == WP_BFG ) {
- strcpy( path, item->world_model[0] );
- COM_StripExtension( path, path );
- strcat( path, "_barrel.md3" );
- pi->barrelModel = trap_R_RegisterModel( path );
- }
-
- strcpy( path, item->world_model[0] );
- COM_StripExtension( path, path );
- strcat( path, "_flash.md3" );
- pi->flashModel = trap_R_RegisterModel( path );
-
- switch( weaponNum ) {
- case WP_GAUNTLET:
- MAKERGB( pi->flashDlightColor, 0.6f, 0.6f, 1 );
- break;
-
- case WP_MACHINEGUN:
- MAKERGB( pi->flashDlightColor, 1, 1, 0 );
- break;
-
- case WP_SHOTGUN:
- MAKERGB( pi->flashDlightColor, 1, 1, 0 );
- break;
-
- case WP_GRENADE_LAUNCHER:
- MAKERGB( pi->flashDlightColor, 1, 0.7f, 0.5f );
- break;
-
- case WP_ROCKET_LAUNCHER:
- MAKERGB( pi->flashDlightColor, 1, 0.75f, 0 );
- break;
-
- case WP_LIGHTNING:
- MAKERGB( pi->flashDlightColor, 0.6f, 0.6f, 1 );
- break;
-
- case WP_RAILGUN:
- MAKERGB( pi->flashDlightColor, 1, 0.5f, 0 );
- break;
-
- case WP_PLASMAGUN:
- MAKERGB( pi->flashDlightColor, 0.6f, 0.6f, 1 );
- break;
-
- case WP_BFG:
- MAKERGB( pi->flashDlightColor, 1, 0.7f, 1 );
- break;
-
- case WP_GRAPPLING_HOOK:
- MAKERGB( pi->flashDlightColor, 0.6f, 0.6f, 1 );
- break;
-
- default:
- MAKERGB( pi->flashDlightColor, 1, 1, 1 );
- break;
- }
-}
-
-
-/*
-===============
-UI_ForceLegsAnim
-===============
-*/
-static void UI_ForceLegsAnim( playerInfo_t *pi, int anim ) {
- pi->legsAnim = ( ( pi->legsAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim;
-
- if ( anim == LEGS_JUMP ) {
- pi->legsAnimationTimer = UI_TIMER_JUMP;
- }
-}
-
-
-/*
-===============
-UI_SetLegsAnim
-===============
-*/
-static void UI_SetLegsAnim( playerInfo_t *pi, int anim ) {
- if ( pi->pendingLegsAnim ) {
- anim = pi->pendingLegsAnim;
- pi->pendingLegsAnim = 0;
- }
- UI_ForceLegsAnim( pi, anim );
-}
-
-
-/*
-===============
-UI_ForceTorsoAnim
-===============
-*/
-static void UI_ForceTorsoAnim( playerInfo_t *pi, int anim ) {
- pi->torsoAnim = ( ( pi->torsoAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim;
-
- if ( anim == TORSO_GESTURE ) {
- pi->torsoAnimationTimer = UI_TIMER_GESTURE;
- }
-
- if ( anim == TORSO_ATTACK || anim == TORSO_ATTACK2 ) {
- pi->torsoAnimationTimer = UI_TIMER_ATTACK;
- }
-}
-
-
-/*
-===============
-UI_SetTorsoAnim
-===============
-*/
-static void UI_SetTorsoAnim( playerInfo_t *pi, int anim ) {
- if ( pi->pendingTorsoAnim ) {
- anim = pi->pendingTorsoAnim;
- pi->pendingTorsoAnim = 0;
- }
-
- UI_ForceTorsoAnim( pi, anim );
-}
-
-
-/*
-===============
-UI_TorsoSequencing
-===============
-*/
-static void UI_TorsoSequencing( playerInfo_t *pi ) {
- int currentAnim;
-
- currentAnim = pi->torsoAnim & ~ANIM_TOGGLEBIT;
-
- if ( pi->weapon != pi->currentWeapon ) {
- if ( currentAnim != TORSO_DROP ) {
- pi->torsoAnimationTimer = UI_TIMER_WEAPON_SWITCH;
- UI_ForceTorsoAnim( pi, TORSO_DROP );
- }
- }
-
- if ( pi->torsoAnimationTimer > 0 ) {
- return;
- }
-
- if( currentAnim == TORSO_GESTURE ) {
- UI_SetTorsoAnim( pi, TORSO_STAND );
- return;
- }
-
- if( currentAnim == TORSO_ATTACK || currentAnim == TORSO_ATTACK2 ) {
- UI_SetTorsoAnim( pi, TORSO_STAND );
- return;
- }
-
- if ( currentAnim == TORSO_DROP ) {
- UI_PlayerInfo_SetWeapon( pi, pi->weapon );
- pi->torsoAnimationTimer = UI_TIMER_WEAPON_SWITCH;
- UI_ForceTorsoAnim( pi, TORSO_RAISE );
- return;
- }
-
- if ( currentAnim == TORSO_RAISE ) {
- UI_SetTorsoAnim( pi, TORSO_STAND );
- return;
- }
-}
-
-
-/*
-===============
-UI_LegsSequencing
-===============
-*/
-static void UI_LegsSequencing( playerInfo_t *pi ) {
- int currentAnim;
-
- currentAnim = pi->legsAnim & ~ANIM_TOGGLEBIT;
-
- if ( pi->legsAnimationTimer > 0 ) {
- if ( currentAnim == LEGS_JUMP ) {
- jumpHeight = JUMP_HEIGHT * sin( M_PI * ( UI_TIMER_JUMP - pi->legsAnimationTimer ) / UI_TIMER_JUMP );
- }
- return;
- }
-
- if ( currentAnim == LEGS_JUMP ) {
- UI_ForceLegsAnim( pi, LEGS_LAND );
- pi->legsAnimationTimer = UI_TIMER_LAND;
- jumpHeight = 0;
- return;
- }
-
- if ( currentAnim == LEGS_LAND ) {
- UI_SetLegsAnim( pi, LEGS_IDLE );
- return;
- }
-}
-
-
-/*
-======================
-UI_PositionEntityOnTag
-======================
-*/
-static void UI_PositionEntityOnTag( refEntity_t *entity, const refEntity_t *parent,
- clipHandle_t parentModel, char *tagName ) {
- int i;
- orientation_t lerped;
-
- // lerp the tag
- trap_CM_LerpTag( &lerped, parentModel, parent->oldframe, parent->frame,
- 1.0 - parent->backlerp, tagName );
-
- // FIXME: allow origin offsets along tag?
- VectorCopy( parent->origin, entity->origin );
- for ( i = 0 ; i < 3 ; i++ ) {
- VectorMA( entity->origin, lerped.origin[i], parent->axis[i], entity->origin );
- }
-
- // cast away const because of compiler problems
- MatrixMultiply( lerped.axis, ((refEntity_t*)parent)->axis, entity->axis );
- entity->backlerp = parent->backlerp;
-}
-
-
-/*
-======================
-UI_PositionRotatedEntityOnTag
-======================
-*/
-static void UI_PositionRotatedEntityOnTag( refEntity_t *entity, const refEntity_t *parent,
- clipHandle_t parentModel, char *tagName ) {
- int i;
- orientation_t lerped;
- vec3_t tempAxis[3];
-
- // lerp the tag
- trap_CM_LerpTag( &lerped, parentModel, parent->oldframe, parent->frame,
- 1.0 - parent->backlerp, tagName );
-
- // FIXME: allow origin offsets along tag?
- VectorCopy( parent->origin, entity->origin );
- for ( i = 0 ; i < 3 ; i++ ) {
- VectorMA( entity->origin, lerped.origin[i], parent->axis[i], entity->origin );
- }
-
- // cast away const because of compiler problems
- MatrixMultiply( entity->axis, ((refEntity_t *)parent)->axis, tempAxis );
- MatrixMultiply( lerped.axis, tempAxis, entity->axis );
-}
-
-
-/*
-===============
-UI_SetLerpFrameAnimation
-===============
-*/
-static void UI_SetLerpFrameAnimation( playerInfo_t *ci, lerpFrame_t *lf, int newAnimation ) {
- animation_t *anim;
-
- lf->animationNumber = newAnimation;
- newAnimation &= ~ANIM_TOGGLEBIT;
-
- if ( newAnimation < 0 || newAnimation >= MAX_ANIMATIONS ) {
- trap_Error( va("Bad animation number: %i", newAnimation) );
- }
-
- anim = &ci->animations[ newAnimation ];
-
- lf->animation = anim;
- lf->animationTime = lf->frameTime + anim->initialLerp;
-}
-
-
-/*
-===============
-UI_RunLerpFrame
-===============
-*/
-static void UI_RunLerpFrame( playerInfo_t *ci, lerpFrame_t *lf, int newAnimation ) {
- int f;
- animation_t *anim;
-
- // see if the animation sequence is switching
- if ( newAnimation != lf->animationNumber || !lf->animation ) {
- UI_SetLerpFrameAnimation( ci, lf, newAnimation );
- }
-
- // if we have passed the current frame, move it to
- // oldFrame and calculate a new frame
- if ( dp_realtime >= lf->frameTime ) {
- lf->oldFrame = lf->frame;
- lf->oldFrameTime = lf->frameTime;
-
- // get the next frame based on the animation
- anim = lf->animation;
- if ( dp_realtime < lf->animationTime ) {
- lf->frameTime = lf->animationTime; // initial lerp
- } else {
- lf->frameTime = lf->oldFrameTime + anim->frameLerp;
- }
- f = ( lf->frameTime - lf->animationTime ) / anim->frameLerp;
- if ( f >= anim->numFrames ) {
- f -= anim->numFrames;
- if ( anim->loopFrames ) {
- f %= anim->loopFrames;
- f += anim->numFrames - anim->loopFrames;
- } else {
- f = anim->numFrames - 1;
- // the animation is stuck at the end, so it
- // can immediately transition to another sequence
- lf->frameTime = dp_realtime;
- }
- }
- lf->frame = anim->firstFrame + f;
- if ( dp_realtime > lf->frameTime ) {
- lf->frameTime = dp_realtime;
- }
- }
-
- if ( lf->frameTime > dp_realtime + 200 ) {
- lf->frameTime = dp_realtime;
- }
-
- if ( lf->oldFrameTime > dp_realtime ) {
- lf->oldFrameTime = dp_realtime;
- }
- // calculate current lerp value
- if ( lf->frameTime == lf->oldFrameTime ) {
- lf->backlerp = 0;
- } else {
- lf->backlerp = 1.0 - (float)( dp_realtime - lf->oldFrameTime ) / ( lf->frameTime - lf->oldFrameTime );
- }
-}
-
-
-/*
-===============
-UI_PlayerAnimation
-===============
-*/
-static void UI_PlayerAnimation( playerInfo_t *pi, int *legsOld, int *legs, float *legsBackLerp,
- int *torsoOld, int *torso, float *torsoBackLerp ) {
-
- // legs animation
- pi->legsAnimationTimer -= uis.frametime;
- if ( pi->legsAnimationTimer < 0 ) {
- pi->legsAnimationTimer = 0;
- }
-
- UI_LegsSequencing( pi );
-
- if ( pi->legs.yawing && ( pi->legsAnim & ~ANIM_TOGGLEBIT ) == LEGS_IDLE ) {
- UI_RunLerpFrame( pi, &pi->legs, LEGS_TURN );
- } else {
- UI_RunLerpFrame( pi, &pi->legs, pi->legsAnim );
- }
- *legsOld = pi->legs.oldFrame;
- *legs = pi->legs.frame;
- *legsBackLerp = pi->legs.backlerp;
-
- // torso animation
- pi->torsoAnimationTimer -= uis.frametime;
- if ( pi->torsoAnimationTimer < 0 ) {
- pi->torsoAnimationTimer = 0;
- }
-
- UI_TorsoSequencing( pi );
-
- UI_RunLerpFrame( pi, &pi->torso, pi->torsoAnim );
- *torsoOld = pi->torso.oldFrame;
- *torso = pi->torso.frame;
- *torsoBackLerp = pi->torso.backlerp;
-}
-
-
-/*
-==================
-UI_SwingAngles
-==================
-*/
-static void UI_SwingAngles( float destination, float swingTolerance, float clampTolerance,
- float speed, float *angle, qboolean *swinging ) {
- float swing;
- float move;
- float scale;
-
- if ( !*swinging ) {
- // see if a swing should be started
- swing = AngleSubtract( *angle, destination );
- if ( swing > swingTolerance || swing < -swingTolerance ) {
- *swinging = qtrue;
- }
- }
-
- if ( !*swinging ) {
- return;
- }
-
- // modify the speed depending on the delta
- // so it doesn't seem so linear
- swing = AngleSubtract( destination, *angle );
- scale = fabs( swing );
- if ( scale < swingTolerance * 0.5 ) {
- scale = 0.5;
- } else if ( scale < swingTolerance ) {
- scale = 1.0;
- } else {
- scale = 2.0;
- }
-
- // swing towards the destination angle
- if ( swing >= 0 ) {
- move = uis.frametime * scale * speed;
- if ( move >= swing ) {
- move = swing;
- *swinging = qfalse;
- }
- *angle = AngleMod( *angle + move );
- } else if ( swing < 0 ) {
- move = uis.frametime * scale * -speed;
- if ( move <= swing ) {
- move = swing;
- *swinging = qfalse;
- }
- *angle = AngleMod( *angle + move );
- }
-
- // clamp to no more than tolerance
- swing = AngleSubtract( destination, *angle );
- if ( swing > clampTolerance ) {
- *angle = AngleMod( destination - (clampTolerance - 1) );
- } else if ( swing < -clampTolerance ) {
- *angle = AngleMod( destination + (clampTolerance - 1) );
- }
-}
-
-
-/*
-======================
-UI_MovedirAdjustment
-======================
-*/
-static float UI_MovedirAdjustment( playerInfo_t *pi ) {
- vec3_t relativeAngles;
- vec3_t moveVector;
-
- VectorSubtract( pi->viewAngles, pi->moveAngles, relativeAngles );
- AngleVectors( relativeAngles, moveVector, NULL, NULL );
- if ( Q_fabs( moveVector[0] ) < 0.01 ) {
- moveVector[0] = 0.0;
- }
- if ( Q_fabs( moveVector[1] ) < 0.01 ) {
- moveVector[1] = 0.0;
- }
-
- if ( moveVector[1] == 0 && moveVector[0] > 0 ) {
- return 0;
- }
- if ( moveVector[1] < 0 && moveVector[0] > 0 ) {
- return 22;
- }
- if ( moveVector[1] < 0 && moveVector[0] == 0 ) {
- return 45;
- }
- if ( moveVector[1] < 0 && moveVector[0] < 0 ) {
- return -22;
- }
- if ( moveVector[1] == 0 && moveVector[0] < 0 ) {
- return 0;
- }
- if ( moveVector[1] > 0 && moveVector[0] < 0 ) {
- return 22;
- }
- if ( moveVector[1] > 0 && moveVector[0] == 0 ) {
- return -45;
- }
-
- return -22;
-}
-
-
-/*
-===============
-UI_PlayerAngles
-===============
-*/
-static void UI_PlayerAngles( playerInfo_t *pi, vec3_t legs[3], vec3_t torso[3], vec3_t head[3] ) {
- vec3_t legsAngles, torsoAngles, headAngles;
- float dest;
- float adjust;
-
- VectorCopy( pi->viewAngles, headAngles );
- headAngles[YAW] = AngleMod( headAngles[YAW] );
- VectorClear( legsAngles );
- VectorClear( torsoAngles );
-
- // --------- yaw -------------
-
- // allow yaw to drift a bit
- if ( ( pi->legsAnim & ~ANIM_TOGGLEBIT ) != LEGS_IDLE
- || ( pi->torsoAnim & ~ANIM_TOGGLEBIT ) != TORSO_STAND ) {
- // if not standing still, always point all in the same direction
- pi->torso.yawing = qtrue; // always center
- pi->torso.pitching = qtrue; // always center
- pi->legs.yawing = qtrue; // always center
- }
-
- // adjust legs for movement dir
- adjust = UI_MovedirAdjustment( pi );
- legsAngles[YAW] = headAngles[YAW] + adjust;
- torsoAngles[YAW] = headAngles[YAW] + 0.25 * adjust;
-
-
- // torso
- UI_SwingAngles( torsoAngles[YAW], 25, 90, SWINGSPEED, &pi->torso.yawAngle, &pi->torso.yawing );
- UI_SwingAngles( legsAngles[YAW], 40, 90, SWINGSPEED, &pi->legs.yawAngle, &pi->legs.yawing );
-
- torsoAngles[YAW] = pi->torso.yawAngle;
- legsAngles[YAW] = pi->legs.yawAngle;
-
- // --------- pitch -------------
-
- // only show a fraction of the pitch angle in the torso
- if ( headAngles[PITCH] > 180 ) {
- dest = (-360 + headAngles[PITCH]) * 0.75;
- } else {
- dest = headAngles[PITCH] * 0.75;
- }
- UI_SwingAngles( dest, 15, 30, 0.1f, &pi->torso.pitchAngle, &pi->torso.pitching );
- torsoAngles[PITCH] = pi->torso.pitchAngle;
-
- // pull the angles back out of the hierarchial chain
- AnglesSubtract( headAngles, torsoAngles, headAngles );
- AnglesSubtract( torsoAngles, legsAngles, torsoAngles );
- AnglesToAxis( legsAngles, legs );
- AnglesToAxis( torsoAngles, torso );
- AnglesToAxis( headAngles, head );
-}
-
-
-/*
-===============
-UI_PlayerFloatSprite
-===============
-*/
-static void UI_PlayerFloatSprite( playerInfo_t *pi, vec3_t origin, qhandle_t shader ) {
- refEntity_t ent;
-
- memset( &ent, 0, sizeof( ent ) );
- VectorCopy( origin, ent.origin );
- ent.origin[2] += 48;
- ent.reType = RT_SPRITE;
- ent.customShader = shader;
- ent.radius = 10;
- ent.renderfx = 0;
- trap_R_AddRefEntityToScene( &ent );
-}
-
-
-/*
-======================
-UI_MachinegunSpinAngle
-======================
-*/
-float UI_MachinegunSpinAngle( playerInfo_t *pi ) {
- int delta;
- float angle;
- float speed;
- int torsoAnim;
-
- delta = dp_realtime - pi->barrelTime;
- if ( pi->barrelSpinning ) {
- angle = pi->barrelAngle + delta * SPIN_SPEED;
- } else {
- if ( delta > COAST_TIME ) {
- delta = COAST_TIME;
- }
-
- speed = 0.5 * ( SPIN_SPEED + (float)( COAST_TIME - delta ) / COAST_TIME );
- angle = pi->barrelAngle + delta * speed;
- }
-
- torsoAnim = pi->torsoAnim & ~ANIM_TOGGLEBIT;
- if( torsoAnim == TORSO_ATTACK2 ) {
- torsoAnim = TORSO_ATTACK;
- }
- if ( pi->barrelSpinning == !(torsoAnim == TORSO_ATTACK) ) {
- pi->barrelTime = dp_realtime;
- pi->barrelAngle = AngleMod( angle );
- pi->barrelSpinning = !!(torsoAnim == TORSO_ATTACK);
- }
-
- return angle;
-}
-
-
-/*
-===============
-UI_DrawPlayer
-===============
-*/
-void UI_DrawPlayer( float x, float y, float w, float h, playerInfo_t *pi, int time ) {
- refdef_t refdef;
- refEntity_t legs;
- refEntity_t torso;
- refEntity_t head;
- refEntity_t gun;
- refEntity_t barrel;
- refEntity_t flash;
- vec3_t origin;
- int renderfx;
- vec3_t mins = {-16, -16, -24};
- vec3_t maxs = {16, 16, 32};
- float len;
- float xx;
-
- if ( !pi->legsModel || !pi->torsoModel || !pi->headModel || !pi->animations[0].numFrames ) {
- return;
- }
-
- dp_realtime = time;
-
- if ( pi->pendingWeapon != -1 && dp_realtime > pi->weaponTimer ) {
- pi->weapon = pi->pendingWeapon;
- pi->lastWeapon = pi->pendingWeapon;
- pi->pendingWeapon = -1;
- pi->weaponTimer = 0;
- if( pi->currentWeapon != pi->weapon ) {
- trap_S_StartLocalSound( weaponChangeSound, CHAN_LOCAL );
- }
- }
-
- UI_AdjustFrom640( &x, &y, &w, &h );
-
- y -= jumpHeight;
-
- memset( &refdef, 0, sizeof( refdef ) );
- memset( &legs, 0, sizeof(legs) );
- memset( &torso, 0, sizeof(torso) );
- memset( &head, 0, sizeof(head) );
-
- refdef.rdflags = RDF_NOWORLDMODEL;
-
- AxisClear( refdef.viewaxis );
-
- refdef.x = x;
- refdef.y = y;
- refdef.width = w;
- refdef.height = h;
-
- refdef.fov_x = (int)((float)refdef.width / 640.0f * 90.0f);
- xx = refdef.width / tan( refdef.fov_x / 360 * M_PI );
- refdef.fov_y = atan2( refdef.height, xx );
- refdef.fov_y *= ( 360 / M_PI );
-
- // calculate distance so the player nearly fills the box
- len = 0.7 * ( maxs[2] - mins[2] );
- origin[0] = len / tan( DEG2RAD(refdef.fov_x) * 0.5 );
- origin[1] = 0.5 * ( mins[1] + maxs[1] );
- origin[2] = -0.5 * ( mins[2] + maxs[2] );
-
- refdef.time = dp_realtime;
-
- trap_R_ClearScene();
-
- // get the rotation information
- UI_PlayerAngles( pi, legs.axis, torso.axis, head.axis );
-
- // get the animation state (after rotation, to allow feet shuffle)
- UI_PlayerAnimation( pi, &legs.oldframe, &legs.frame, &legs.backlerp,
- &torso.oldframe, &torso.frame, &torso.backlerp );
-
- renderfx = RF_LIGHTING_ORIGIN | RF_NOSHADOW;
-
- //
- // add the legs
- //
- legs.hModel = pi->legsModel;
- legs.customSkin = pi->legsSkin;
-
- VectorCopy( origin, legs.origin );
-
- VectorCopy( origin, legs.lightingOrigin );
- legs.renderfx = renderfx;
- VectorCopy (legs.origin, legs.oldorigin);
-
- trap_R_AddRefEntityToScene( &legs );
-
- if (!legs.hModel) {
- return;
- }
-
- //
- // add the torso
- //
- torso.hModel = pi->torsoModel;
- if (!torso.hModel) {
- return;
- }
-
- torso.customSkin = pi->torsoSkin;
-
- VectorCopy( origin, torso.lightingOrigin );
-
- UI_PositionRotatedEntityOnTag( &torso, &legs, pi->legsModel, "tag_torso");
-
- torso.renderfx = renderfx;
-
- trap_R_AddRefEntityToScene( &torso );
-
- //
- // add the head
- //
- head.hModel = pi->headModel;
- if (!head.hModel) {
- return;
- }
- head.customSkin = pi->headSkin;
-
- VectorCopy( origin, head.lightingOrigin );
-
- UI_PositionRotatedEntityOnTag( &head, &torso, pi->torsoModel, "tag_head");
-
- head.renderfx = renderfx;
-
- trap_R_AddRefEntityToScene( &head );
-
- //
- // add the gun
- //
- if ( pi->currentWeapon != WP_NONE ) {
- memset( &gun, 0, sizeof(gun) );
- gun.hModel = pi->weaponModel;
- VectorCopy( origin, gun.lightingOrigin );
- UI_PositionEntityOnTag( &gun, &torso, pi->torsoModel, "tag_weapon");
- gun.renderfx = renderfx;
- trap_R_AddRefEntityToScene( &gun );
- }
-
- //
- // add the spinning barrel
- //
- if ( pi->realWeapon == WP_MACHINEGUN || pi->realWeapon == WP_GAUNTLET || pi->realWeapon == WP_BFG ) {
- vec3_t angles;
-
- memset( &barrel, 0, sizeof(barrel) );
- VectorCopy( origin, barrel.lightingOrigin );
- barrel.renderfx = renderfx;
-
- barrel.hModel = pi->barrelModel;
- angles[YAW] = 0;
- angles[PITCH] = 0;
- angles[ROLL] = UI_MachinegunSpinAngle( pi );
- if( pi->realWeapon == WP_GAUNTLET || pi->realWeapon == WP_BFG ) {
- angles[PITCH] = angles[ROLL];
- angles[ROLL] = 0;
- }
- AnglesToAxis( angles, barrel.axis );
-
- UI_PositionRotatedEntityOnTag( &barrel, &gun, pi->weaponModel, "tag_barrel");
-
- trap_R_AddRefEntityToScene( &barrel );
- }
-
- //
- // add muzzle flash
- //
- if ( dp_realtime <= pi->muzzleFlashTime ) {
- if ( pi->flashModel ) {
- memset( &flash, 0, sizeof(flash) );
- flash.hModel = pi->flashModel;
- VectorCopy( origin, flash.lightingOrigin );
- UI_PositionEntityOnTag( &flash, &gun, pi->weaponModel, "tag_flash");
- flash.renderfx = renderfx;
- trap_R_AddRefEntityToScene( &flash );
- }
-
- // make a dlight for the flash
- if ( pi->flashDlightColor[0] || pi->flashDlightColor[1] || pi->flashDlightColor[2] ) {
- trap_R_AddLightToScene( flash.origin, 200 + (rand()&31), pi->flashDlightColor[0],
- pi->flashDlightColor[1], pi->flashDlightColor[2] );
- }
- }
-
- //
- // add the chat icon
- //
- if ( pi->chat ) {
- UI_PlayerFloatSprite( pi, origin, trap_R_RegisterShaderNoMip( "sprites/balloon3" ) );
- }
-
- //
- // add an accent light
- //
- origin[0] -= 100; // + = behind, - = in front
- origin[1] += 100; // + = left, - = right
- origin[2] += 100; // + = above, - = below
- trap_R_AddLightToScene( origin, 500, 1.0, 1.0, 1.0 );
-
- origin[0] -= 100;
- origin[1] -= 100;
- origin[2] -= 100;
- trap_R_AddLightToScene( origin, 500, 1.0, 0.0, 0.0 );
-
- trap_R_RenderScene( &refdef );
-}
-
-
-/*
-==========================
-UI_RegisterClientSkin
-==========================
-*/
-static qboolean UI_RegisterClientSkin( playerInfo_t *pi, const char *modelName, const char *skinName ) {
- char filename[MAX_QPATH];
-
- Com_sprintf( filename, sizeof( filename ), "models/players/%s/lower_%s.skin", modelName, skinName );
- pi->legsSkin = trap_R_RegisterSkin( filename );
-
- Com_sprintf( filename, sizeof( filename ), "models/players/%s/upper_%s.skin", modelName, skinName );
- pi->torsoSkin = trap_R_RegisterSkin( filename );
-
- Com_sprintf( filename, sizeof( filename ), "models/players/%s/head_%s.skin", modelName, skinName );
- pi->headSkin = trap_R_RegisterSkin( filename );
-
- if ( !pi->legsSkin || !pi->torsoSkin || !pi->headSkin ) {
- return qfalse;
- }
-
- return qtrue;
-}
-
-
-/*
-======================
-UI_ParseAnimationFile
-======================
-*/
-static qboolean UI_ParseAnimationFile( const char *filename, animation_t *animations ) {
- char *text_p, *prev;
- int len;
- int i;
- char *token;
- float fps;
- int skip;
- char text[20000];
- fileHandle_t f;
-
- memset( animations, 0, sizeof( animation_t ) * MAX_ANIMATIONS );
-
- // load the file
- len = trap_FS_FOpenFile( filename, &f, FS_READ );
- if ( len <= 0 ) {
- return qfalse;
- }
- if ( len >= ( sizeof( text ) - 1 ) ) {
- Com_Printf( "File %s too long\n", filename );
- return qfalse;
- }
- trap_FS_Read( text, len, f );
- text[len] = 0;
- trap_FS_FCloseFile( f );
-
- // parse the text
- text_p = text;
- skip = 0; // quite the compiler warning
-
- // read optional parameters
- while ( 1 ) {
- prev = text_p; // so we can unget
- token = COM_Parse( &text_p );
- if ( !token ) {
- break;
- }
- if ( !Q_stricmp( token, "footsteps" ) ) {
- token = COM_Parse( &text_p );
- if ( !token ) {
- break;
- }
- continue;
- } else if ( !Q_stricmp( token, "headoffset" ) ) {
- for ( i = 0 ; i < 3 ; i++ ) {
- token = COM_Parse( &text_p );
- if ( !token ) {
- break;
- }
- }
- continue;
- } else if ( !Q_stricmp( token, "sex" ) ) {
- token = COM_Parse( &text_p );
- if ( !token ) {
- break;
- }
- continue;
- }
-
- // if it is a number, start parsing animations
- if ( token[0] >= '0' && token[0] <= '9' ) {
- text_p = prev; // unget the token
- break;
- }
-
- Com_Printf( "unknown token '%s' is %s\n", token, filename );
- }
-
- // read information for each frame
- for ( i = 0 ; i < MAX_ANIMATIONS ; i++ ) {
-
- token = COM_Parse( &text_p );
- if ( !token ) {
- break;
- }
- animations[i].firstFrame = atoi( token );
- // leg only frames are adjusted to not count the upper body only frames
- if ( i == LEGS_WALKCR ) {
- skip = animations[LEGS_WALKCR].firstFrame - animations[TORSO_GESTURE].firstFrame;
- }
- if ( i >= LEGS_WALKCR ) {
- animations[i].firstFrame -= skip;
- }
-
- token = COM_Parse( &text_p );
- if ( !token ) {
- break;
- }
- animations[i].numFrames = atoi( token );
-
- token = COM_Parse( &text_p );
- if ( !token ) {
- break;
- }
- animations[i].loopFrames = atoi( token );
-
- token = COM_Parse( &text_p );
- if ( !token ) {
- break;
- }
- fps = atof( token );
- if ( fps == 0 ) {
- fps = 1;
- }
- animations[i].frameLerp = 1000 / fps;
- animations[i].initialLerp = 1000 / fps;
- }
-
- if ( i != MAX_ANIMATIONS ) {
- Com_Printf( "Error parsing animation file: %s", filename );
- return qfalse;
- }
-
- return qtrue;
-}
-
-
-/*
-==========================
-UI_RegisterClientModelname
-==========================
-*/
-qboolean UI_RegisterClientModelname( playerInfo_t *pi, const char *modelSkinName ) {
- char modelName[MAX_QPATH];
- char skinName[MAX_QPATH];
- char filename[MAX_QPATH];
- char *slash;
-
- pi->torsoModel = 0;
- pi->headModel = 0;
-
- if ( !modelSkinName[0] ) {
- return qfalse;
- }
-
- Q_strncpyz( modelName, modelSkinName, sizeof( modelName ) );
-
- slash = strchr( modelName, '/' );
- if ( !slash ) {
- // modelName did not include a skin name
- Q_strncpyz( skinName, "default", sizeof( skinName ) );
- } else {
- Q_strncpyz( skinName, slash + 1, sizeof( skinName ) );
- // truncate modelName
- *slash = 0;
- }
-
- // load cmodels before models so filecache works
-
- Com_sprintf( filename, sizeof( filename ), "models/players/%s/lower.md3", modelName );
- pi->legsModel = trap_R_RegisterModel( filename );
- if ( !pi->legsModel ) {
- Com_Printf( "Failed to load model file %s\n", filename );
- return qfalse;
- }
-
- Com_sprintf( filename, sizeof( filename ), "models/players/%s/upper.md3", modelName );
- pi->torsoModel = trap_R_RegisterModel( filename );
- if ( !pi->torsoModel ) {
- Com_Printf( "Failed to load model file %s\n", filename );
- return qfalse;
- }
-
- Com_sprintf( filename, sizeof( filename ), "models/players/%s/head.md3", modelName );
- pi->headModel = trap_R_RegisterModel( filename );
- if ( !pi->headModel ) {
- Com_Printf( "Failed to load model file %s\n", filename );
- return qfalse;
- }
-
- // if any skins failed to load, fall back to default
- if ( !UI_RegisterClientSkin( pi, modelName, skinName ) ) {
- if ( !UI_RegisterClientSkin( pi, modelName, "default" ) ) {
- Com_Printf( "Failed to load skin file: %s : %s\n", modelName, skinName );
- return qfalse;
- }
- }
-
- // load the animations
- Com_sprintf( filename, sizeof( filename ), "models/players/%s/animation.cfg", modelName );
- if ( !UI_ParseAnimationFile( filename, pi->animations ) ) {
- Com_Printf( "Failed to load animation file %s\n", filename );
- return qfalse;
- }
-
- return qtrue;
-}
-
-
-/*
-===============
-UI_PlayerInfo_SetModel
-===============
-*/
-void UI_PlayerInfo_SetModel( playerInfo_t *pi, const char *model ) {
- memset( pi, 0, sizeof(*pi) );
- UI_RegisterClientModelname( pi, model );
- pi->weapon = WP_MACHINEGUN;
- pi->currentWeapon = pi->weapon;
- pi->lastWeapon = pi->weapon;
- pi->pendingWeapon = -1;
- pi->weaponTimer = 0;
- pi->chat = qfalse;
- pi->newModel = qtrue;
- UI_PlayerInfo_SetWeapon( pi, pi->weapon );
-}
-
-
-/*
-===============
-UI_PlayerInfo_SetInfo
-===============
-*/
-void UI_PlayerInfo_SetInfo( playerInfo_t *pi, int legsAnim, int torsoAnim, vec3_t viewAngles, vec3_t moveAngles, weapon_t weaponNumber, qboolean chat ) {
- int currentAnim;
- weapon_t weaponNum;
-
- pi->chat = chat;
-
- // view angles
- VectorCopy( viewAngles, pi->viewAngles );
-
- // move angles
- VectorCopy( moveAngles, pi->moveAngles );
-
- if ( pi->newModel ) {
- pi->newModel = qfalse;
-
- jumpHeight = 0;
- pi->pendingLegsAnim = 0;
- UI_ForceLegsAnim( pi, legsAnim );
- pi->legs.yawAngle = viewAngles[YAW];
- pi->legs.yawing = qfalse;
-
- pi->pendingTorsoAnim = 0;
- UI_ForceTorsoAnim( pi, torsoAnim );
- pi->torso.yawAngle = viewAngles[YAW];
- pi->torso.yawing = qfalse;
-
- if ( weaponNumber != -1 ) {
- pi->weapon = weaponNumber;
- pi->currentWeapon = weaponNumber;
- pi->lastWeapon = weaponNumber;
- pi->pendingWeapon = -1;
- pi->weaponTimer = 0;
- UI_PlayerInfo_SetWeapon( pi, pi->weapon );
- }
-
- return;
- }
-
- // weapon
- if ( weaponNumber == -1 ) {
- pi->pendingWeapon = -1;
- pi->weaponTimer = 0;
- }
- else if ( weaponNumber != WP_NONE ) {
- pi->pendingWeapon = weaponNumber;
- pi->weaponTimer = dp_realtime + UI_TIMER_WEAPON_DELAY;
- }
- weaponNum = pi->lastWeapon;
- pi->weapon = weaponNum;
-
- if ( torsoAnim == BOTH_DEATH1 || legsAnim == BOTH_DEATH1 ) {
- torsoAnim = legsAnim = BOTH_DEATH1;
- pi->weapon = pi->currentWeapon = WP_NONE;
- UI_PlayerInfo_SetWeapon( pi, pi->weapon );
-
- jumpHeight = 0;
- pi->pendingLegsAnim = 0;
- UI_ForceLegsAnim( pi, legsAnim );
-
- pi->pendingTorsoAnim = 0;
- UI_ForceTorsoAnim( pi, torsoAnim );
-
- return;
- }
-
- // leg animation
- currentAnim = pi->legsAnim & ~ANIM_TOGGLEBIT;
- if ( legsAnim != LEGS_JUMP && ( currentAnim == LEGS_JUMP || currentAnim == LEGS_LAND ) ) {
- pi->pendingLegsAnim = legsAnim;
- }
- else if ( legsAnim != currentAnim ) {
- jumpHeight = 0;
- pi->pendingLegsAnim = 0;
- UI_ForceLegsAnim( pi, legsAnim );
- }
-
- // torso animation
- if ( torsoAnim == TORSO_STAND || torsoAnim == TORSO_STAND2 ) {
- if ( weaponNum == WP_NONE || weaponNum == WP_GAUNTLET ) {
- torsoAnim = TORSO_STAND2;
- }
- else {
- torsoAnim = TORSO_STAND;
- }
- }
-
- if ( torsoAnim == TORSO_ATTACK || torsoAnim == TORSO_ATTACK2 ) {
- if ( weaponNum == WP_NONE || weaponNum == WP_GAUNTLET ) {
- torsoAnim = TORSO_ATTACK2;
- }
- else {
- torsoAnim = TORSO_ATTACK;
- }
- pi->muzzleFlashTime = dp_realtime + UI_TIMER_MUZZLE_FLASH;
- //FIXME play firing sound here
- }
-
- currentAnim = pi->torsoAnim & ~ANIM_TOGGLEBIT;
-
- if ( weaponNum != pi->currentWeapon || currentAnim == TORSO_RAISE || currentAnim == TORSO_DROP ) {
- pi->pendingTorsoAnim = torsoAnim;
- }
- else if ( ( currentAnim == TORSO_GESTURE || currentAnim == TORSO_ATTACK ) && ( torsoAnim != currentAnim ) ) {
- pi->pendingTorsoAnim = torsoAnim;
- }
- else if ( torsoAnim != currentAnim ) {
- pi->pendingTorsoAnim = 0;
- UI_ForceTorsoAnim( pi, torsoAnim );
- }
-}
+/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// +// ui_players.c + +#include "ui_local.h" + + +#define UI_TIMER_GESTURE 2300 +#define UI_TIMER_JUMP 1000 +#define UI_TIMER_LAND 130 +#define UI_TIMER_WEAPON_SWITCH 300 +#define UI_TIMER_ATTACK 500 +#define UI_TIMER_MUZZLE_FLASH 20 +#define UI_TIMER_WEAPON_DELAY 250 + +#define JUMP_HEIGHT 56 + +#define SWINGSPEED 0.3f + +#define SPIN_SPEED 0.9f +#define COAST_TIME 1000 + + +static int dp_realtime; +static float jumpHeight; + + +/* +=============== +UI_PlayerInfo_SetWeapon +=============== +*/ +static void UI_PlayerInfo_SetWeapon( playerInfo_t *pi, weapon_t weaponNum ) { + gitem_t * item; + char path[MAX_QPATH]; + + pi->currentWeapon = weaponNum; +tryagain: + pi->realWeapon = weaponNum; + pi->weaponModel = 0; + pi->barrelModel = 0; + pi->flashModel = 0; + + if ( weaponNum == WP_NONE ) { + return; + } + + for ( item = bg_itemlist + 1; item->classname ; item++ ) { + if ( item->giType != IT_WEAPON ) { + continue; + } + if ( item->giTag == weaponNum ) { + break; + } + } + + if ( item->classname ) { + pi->weaponModel = trap_R_RegisterModel( item->world_model[0] ); + } + + if( pi->weaponModel == 0 ) { + if( weaponNum == WP_MACHINEGUN ) { + weaponNum = WP_NONE; + goto tryagain; + } + weaponNum = WP_MACHINEGUN; + goto tryagain; + } + + if ( weaponNum == WP_MACHINEGUN || weaponNum == WP_GAUNTLET || weaponNum == WP_BFG ) { + strcpy( path, item->world_model[0] ); + COM_StripExtension( path, path ); + strcat( path, "_barrel.md3" ); + pi->barrelModel = trap_R_RegisterModel( path ); + } + + strcpy( path, item->world_model[0] ); + COM_StripExtension( path, path ); + strcat( path, "_flash.md3" ); + pi->flashModel = trap_R_RegisterModel( path ); + + switch( weaponNum ) { + case WP_GAUNTLET: + MAKERGB( pi->flashDlightColor, 0.6f, 0.6f, 1 ); + break; + + case WP_MACHINEGUN: + MAKERGB( pi->flashDlightColor, 1, 1, 0 ); + break; + + case WP_SHOTGUN: + MAKERGB( pi->flashDlightColor, 1, 1, 0 ); + break; + + case WP_GRENADE_LAUNCHER: + MAKERGB( pi->flashDlightColor, 1, 0.7f, 0.5f ); + break; + + case WP_ROCKET_LAUNCHER: + MAKERGB( pi->flashDlightColor, 1, 0.75f, 0 ); + break; + + case WP_LIGHTNING: + MAKERGB( pi->flashDlightColor, 0.6f, 0.6f, 1 ); + break; + + case WP_RAILGUN: + MAKERGB( pi->flashDlightColor, 1, 0.5f, 0 ); + break; + + case WP_PLASMAGUN: + MAKERGB( pi->flashDlightColor, 0.6f, 0.6f, 1 ); + break; + + case WP_BFG: + MAKERGB( pi->flashDlightColor, 1, 0.7f, 1 ); + break; + + case WP_GRAPPLING_HOOK: + MAKERGB( pi->flashDlightColor, 0.6f, 0.6f, 1 ); + break; + + default: + MAKERGB( pi->flashDlightColor, 1, 1, 1 ); + break; + } +} + + +/* +=============== +UI_ForceLegsAnim +=============== +*/ +static void UI_ForceLegsAnim( playerInfo_t *pi, int anim ) { + pi->legsAnim = ( ( pi->legsAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim; + + if ( anim == LEGS_JUMP ) { + pi->legsAnimationTimer = UI_TIMER_JUMP; + } +} + + +/* +=============== +UI_SetLegsAnim +=============== +*/ +static void UI_SetLegsAnim( playerInfo_t *pi, int anim ) { + if ( pi->pendingLegsAnim ) { + anim = pi->pendingLegsAnim; + pi->pendingLegsAnim = 0; + } + UI_ForceLegsAnim( pi, anim ); +} + + +/* +=============== +UI_ForceTorsoAnim +=============== +*/ +static void UI_ForceTorsoAnim( playerInfo_t *pi, int anim ) { + pi->torsoAnim = ( ( pi->torsoAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim; + + if ( anim == TORSO_GESTURE ) { + pi->torsoAnimationTimer = UI_TIMER_GESTURE; + } + + if ( anim == TORSO_ATTACK || anim == TORSO_ATTACK2 ) { + pi->torsoAnimationTimer = UI_TIMER_ATTACK; + } +} + + +/* +=============== +UI_SetTorsoAnim +=============== +*/ +static void UI_SetTorsoAnim( playerInfo_t *pi, int anim ) { + if ( pi->pendingTorsoAnim ) { + anim = pi->pendingTorsoAnim; + pi->pendingTorsoAnim = 0; + } + + UI_ForceTorsoAnim( pi, anim ); +} + + +/* +=============== +UI_TorsoSequencing +=============== +*/ +static void UI_TorsoSequencing( playerInfo_t *pi ) { + int currentAnim; + + currentAnim = pi->torsoAnim & ~ANIM_TOGGLEBIT; + + if ( pi->weapon != pi->currentWeapon ) { + if ( currentAnim != TORSO_DROP ) { + pi->torsoAnimationTimer = UI_TIMER_WEAPON_SWITCH; + UI_ForceTorsoAnim( pi, TORSO_DROP ); + } + } + + if ( pi->torsoAnimationTimer > 0 ) { + return; + } + + if( currentAnim == TORSO_GESTURE ) { + UI_SetTorsoAnim( pi, TORSO_STAND ); + return; + } + + if( currentAnim == TORSO_ATTACK || currentAnim == TORSO_ATTACK2 ) { + UI_SetTorsoAnim( pi, TORSO_STAND ); + return; + } + + if ( currentAnim == TORSO_DROP ) { + UI_PlayerInfo_SetWeapon( pi, pi->weapon ); + pi->torsoAnimationTimer = UI_TIMER_WEAPON_SWITCH; + UI_ForceTorsoAnim( pi, TORSO_RAISE ); + return; + } + + if ( currentAnim == TORSO_RAISE ) { + UI_SetTorsoAnim( pi, TORSO_STAND ); + return; + } +} + + +/* +=============== +UI_LegsSequencing +=============== +*/ +static void UI_LegsSequencing( playerInfo_t *pi ) { + int currentAnim; + + currentAnim = pi->legsAnim & ~ANIM_TOGGLEBIT; + + if ( pi->legsAnimationTimer > 0 ) { + if ( currentAnim == LEGS_JUMP ) { + jumpHeight = JUMP_HEIGHT * sin( M_PI * ( UI_TIMER_JUMP - pi->legsAnimationTimer ) / UI_TIMER_JUMP ); + } + return; + } + + if ( currentAnim == LEGS_JUMP ) { + UI_ForceLegsAnim( pi, LEGS_LAND ); + pi->legsAnimationTimer = UI_TIMER_LAND; + jumpHeight = 0; + return; + } + + if ( currentAnim == LEGS_LAND ) { + UI_SetLegsAnim( pi, LEGS_IDLE ); + return; + } +} + + +/* +====================== +UI_PositionEntityOnTag +====================== +*/ +static void UI_PositionEntityOnTag( refEntity_t *entity, const refEntity_t *parent, + clipHandle_t parentModel, char *tagName ) { + int i; + orientation_t lerped; + + // lerp the tag + trap_CM_LerpTag( &lerped, parentModel, parent->oldframe, parent->frame, + 1.0 - parent->backlerp, tagName ); + + // FIXME: allow origin offsets along tag? + VectorCopy( parent->origin, entity->origin ); + for ( i = 0 ; i < 3 ; i++ ) { + VectorMA( entity->origin, lerped.origin[i], parent->axis[i], entity->origin ); + } + + // cast away const because of compiler problems + MatrixMultiply( lerped.axis, ((refEntity_t*)parent)->axis, entity->axis ); + entity->backlerp = parent->backlerp; +} + + +/* +====================== +UI_PositionRotatedEntityOnTag +====================== +*/ +static void UI_PositionRotatedEntityOnTag( refEntity_t *entity, const refEntity_t *parent, + clipHandle_t parentModel, char *tagName ) { + int i; + orientation_t lerped; + vec3_t tempAxis[3]; + + // lerp the tag + trap_CM_LerpTag( &lerped, parentModel, parent->oldframe, parent->frame, + 1.0 - parent->backlerp, tagName ); + + // FIXME: allow origin offsets along tag? + VectorCopy( parent->origin, entity->origin ); + for ( i = 0 ; i < 3 ; i++ ) { + VectorMA( entity->origin, lerped.origin[i], parent->axis[i], entity->origin ); + } + + // cast away const because of compiler problems + MatrixMultiply( entity->axis, ((refEntity_t *)parent)->axis, tempAxis ); + MatrixMultiply( lerped.axis, tempAxis, entity->axis ); +} + + +/* +=============== +UI_SetLerpFrameAnimation +=============== +*/ +static void UI_SetLerpFrameAnimation( playerInfo_t *ci, lerpFrame_t *lf, int newAnimation ) { + animation_t *anim; + + lf->animationNumber = newAnimation; + newAnimation &= ~ANIM_TOGGLEBIT; + + if ( newAnimation < 0 || newAnimation >= MAX_ANIMATIONS ) { + trap_Error( va("Bad animation number: %i", newAnimation) ); + } + + anim = &ci->animations[ newAnimation ]; + + lf->animation = anim; + lf->animationTime = lf->frameTime + anim->initialLerp; +} + + +/* +=============== +UI_RunLerpFrame +=============== +*/ +static void UI_RunLerpFrame( playerInfo_t *ci, lerpFrame_t *lf, int newAnimation ) { + int f; + animation_t *anim; + + // see if the animation sequence is switching + if ( newAnimation != lf->animationNumber || !lf->animation ) { + UI_SetLerpFrameAnimation( ci, lf, newAnimation ); + } + + // if we have passed the current frame, move it to + // oldFrame and calculate a new frame + if ( dp_realtime >= lf->frameTime ) { + lf->oldFrame = lf->frame; + lf->oldFrameTime = lf->frameTime; + + // get the next frame based on the animation + anim = lf->animation; + if ( dp_realtime < lf->animationTime ) { + lf->frameTime = lf->animationTime; // initial lerp + } else { + lf->frameTime = lf->oldFrameTime + anim->frameLerp; + } + f = ( lf->frameTime - lf->animationTime ) / anim->frameLerp; + if ( f >= anim->numFrames ) { + f -= anim->numFrames; + if ( anim->loopFrames ) { + f %= anim->loopFrames; + f += anim->numFrames - anim->loopFrames; + } else { + f = anim->numFrames - 1; + // the animation is stuck at the end, so it + // can immediately transition to another sequence + lf->frameTime = dp_realtime; + } + } + lf->frame = anim->firstFrame + f; + if ( dp_realtime > lf->frameTime ) { + lf->frameTime = dp_realtime; + } + } + + if ( lf->frameTime > dp_realtime + 200 ) { + lf->frameTime = dp_realtime; + } + + if ( lf->oldFrameTime > dp_realtime ) { + lf->oldFrameTime = dp_realtime; + } + // calculate current lerp value + if ( lf->frameTime == lf->oldFrameTime ) { + lf->backlerp = 0; + } else { + lf->backlerp = 1.0 - (float)( dp_realtime - lf->oldFrameTime ) / ( lf->frameTime - lf->oldFrameTime ); + } +} + + +/* +=============== +UI_PlayerAnimation +=============== +*/ +static void UI_PlayerAnimation( playerInfo_t *pi, int *legsOld, int *legs, float *legsBackLerp, + int *torsoOld, int *torso, float *torsoBackLerp ) { + + // legs animation + pi->legsAnimationTimer -= uis.frametime; + if ( pi->legsAnimationTimer < 0 ) { + pi->legsAnimationTimer = 0; + } + + UI_LegsSequencing( pi ); + + if ( pi->legs.yawing && ( pi->legsAnim & ~ANIM_TOGGLEBIT ) == LEGS_IDLE ) { + UI_RunLerpFrame( pi, &pi->legs, LEGS_TURN ); + } else { + UI_RunLerpFrame( pi, &pi->legs, pi->legsAnim ); + } + *legsOld = pi->legs.oldFrame; + *legs = pi->legs.frame; + *legsBackLerp = pi->legs.backlerp; + + // torso animation + pi->torsoAnimationTimer -= uis.frametime; + if ( pi->torsoAnimationTimer < 0 ) { + pi->torsoAnimationTimer = 0; + } + + UI_TorsoSequencing( pi ); + + UI_RunLerpFrame( pi, &pi->torso, pi->torsoAnim ); + *torsoOld = pi->torso.oldFrame; + *torso = pi->torso.frame; + *torsoBackLerp = pi->torso.backlerp; +} + + +/* +================== +UI_SwingAngles +================== +*/ +static void UI_SwingAngles( float destination, float swingTolerance, float clampTolerance, + float speed, float *angle, qboolean *swinging ) { + float swing; + float move; + float scale; + + if ( !*swinging ) { + // see if a swing should be started + swing = AngleSubtract( *angle, destination ); + if ( swing > swingTolerance || swing < -swingTolerance ) { + *swinging = qtrue; + } + } + + if ( !*swinging ) { + return; + } + + // modify the speed depending on the delta + // so it doesn't seem so linear + swing = AngleSubtract( destination, *angle ); + scale = fabs( swing ); + if ( scale < swingTolerance * 0.5 ) { + scale = 0.5; + } else if ( scale < swingTolerance ) { + scale = 1.0; + } else { + scale = 2.0; + } + + // swing towards the destination angle + if ( swing >= 0 ) { + move = uis.frametime * scale * speed; + if ( move >= swing ) { + move = swing; + *swinging = qfalse; + } + *angle = AngleMod( *angle + move ); + } else if ( swing < 0 ) { + move = uis.frametime * scale * -speed; + if ( move <= swing ) { + move = swing; + *swinging = qfalse; + } + *angle = AngleMod( *angle + move ); + } + + // clamp to no more than tolerance + swing = AngleSubtract( destination, *angle ); + if ( swing > clampTolerance ) { + *angle = AngleMod( destination - (clampTolerance - 1) ); + } else if ( swing < -clampTolerance ) { + *angle = AngleMod( destination + (clampTolerance - 1) ); + } +} + + +/* +====================== +UI_MovedirAdjustment +====================== +*/ +static float UI_MovedirAdjustment( playerInfo_t *pi ) { + vec3_t relativeAngles; + vec3_t moveVector; + + VectorSubtract( pi->viewAngles, pi->moveAngles, relativeAngles ); + AngleVectors( relativeAngles, moveVector, NULL, NULL ); + if ( Q_fabs( moveVector[0] ) < 0.01 ) { + moveVector[0] = 0.0; + } + if ( Q_fabs( moveVector[1] ) < 0.01 ) { + moveVector[1] = 0.0; + } + + if ( moveVector[1] == 0 && moveVector[0] > 0 ) { + return 0; + } + if ( moveVector[1] < 0 && moveVector[0] > 0 ) { + return 22; + } + if ( moveVector[1] < 0 && moveVector[0] == 0 ) { + return 45; + } + if ( moveVector[1] < 0 && moveVector[0] < 0 ) { + return -22; + } + if ( moveVector[1] == 0 && moveVector[0] < 0 ) { + return 0; + } + if ( moveVector[1] > 0 && moveVector[0] < 0 ) { + return 22; + } + if ( moveVector[1] > 0 && moveVector[0] == 0 ) { + return -45; + } + + return -22; +} + + +/* +=============== +UI_PlayerAngles +=============== +*/ +static void UI_PlayerAngles( playerInfo_t *pi, vec3_t legs[3], vec3_t torso[3], vec3_t head[3] ) { + vec3_t legsAngles, torsoAngles, headAngles; + float dest; + float adjust; + + VectorCopy( pi->viewAngles, headAngles ); + headAngles[YAW] = AngleMod( headAngles[YAW] ); + VectorClear( legsAngles ); + VectorClear( torsoAngles ); + + // --------- yaw ------------- + + // allow yaw to drift a bit + if ( ( pi->legsAnim & ~ANIM_TOGGLEBIT ) != LEGS_IDLE + || ( pi->torsoAnim & ~ANIM_TOGGLEBIT ) != TORSO_STAND ) { + // if not standing still, always point all in the same direction + pi->torso.yawing = qtrue; // always center + pi->torso.pitching = qtrue; // always center + pi->legs.yawing = qtrue; // always center + } + + // adjust legs for movement dir + adjust = UI_MovedirAdjustment( pi ); + legsAngles[YAW] = headAngles[YAW] + adjust; + torsoAngles[YAW] = headAngles[YAW] + 0.25 * adjust; + + + // torso + UI_SwingAngles( torsoAngles[YAW], 25, 90, SWINGSPEED, &pi->torso.yawAngle, &pi->torso.yawing ); + UI_SwingAngles( legsAngles[YAW], 40, 90, SWINGSPEED, &pi->legs.yawAngle, &pi->legs.yawing ); + + torsoAngles[YAW] = pi->torso.yawAngle; + legsAngles[YAW] = pi->legs.yawAngle; + + // --------- pitch ------------- + + // only show a fraction of the pitch angle in the torso + if ( headAngles[PITCH] > 180 ) { + dest = (-360 + headAngles[PITCH]) * 0.75; + } else { + dest = headAngles[PITCH] * 0.75; + } + UI_SwingAngles( dest, 15, 30, 0.1f, &pi->torso.pitchAngle, &pi->torso.pitching ); + torsoAngles[PITCH] = pi->torso.pitchAngle; + + // pull the angles back out of the hierarchial chain + AnglesSubtract( headAngles, torsoAngles, headAngles ); + AnglesSubtract( torsoAngles, legsAngles, torsoAngles ); + AnglesToAxis( legsAngles, legs ); + AnglesToAxis( torsoAngles, torso ); + AnglesToAxis( headAngles, head ); +} + + +/* +=============== +UI_PlayerFloatSprite +=============== +*/ +static void UI_PlayerFloatSprite( playerInfo_t *pi, vec3_t origin, qhandle_t shader ) { + refEntity_t ent; + + memset( &ent, 0, sizeof( ent ) ); + VectorCopy( origin, ent.origin ); + ent.origin[2] += 48; + ent.reType = RT_SPRITE; + ent.customShader = shader; + ent.radius = 10; + ent.renderfx = 0; + trap_R_AddRefEntityToScene( &ent ); +} + + +/* +====================== +UI_MachinegunSpinAngle +====================== +*/ +float UI_MachinegunSpinAngle( playerInfo_t *pi ) { + int delta; + float angle; + float speed; + int torsoAnim; + + delta = dp_realtime - pi->barrelTime; + if ( pi->barrelSpinning ) { + angle = pi->barrelAngle + delta * SPIN_SPEED; + } else { + if ( delta > COAST_TIME ) { + delta = COAST_TIME; + } + + speed = 0.5 * ( SPIN_SPEED + (float)( COAST_TIME - delta ) / COAST_TIME ); + angle = pi->barrelAngle + delta * speed; + } + + torsoAnim = pi->torsoAnim & ~ANIM_TOGGLEBIT; + if( torsoAnim == TORSO_ATTACK2 ) { + torsoAnim = TORSO_ATTACK; + } + if ( pi->barrelSpinning == !(torsoAnim == TORSO_ATTACK) ) { + pi->barrelTime = dp_realtime; + pi->barrelAngle = AngleMod( angle ); + pi->barrelSpinning = !!(torsoAnim == TORSO_ATTACK); + } + + return angle; +} + + +/* +=============== +UI_DrawPlayer +=============== +*/ +void UI_DrawPlayer( float x, float y, float w, float h, playerInfo_t *pi, int time ) { + refdef_t refdef; + refEntity_t legs; + refEntity_t torso; + refEntity_t head; + refEntity_t gun; + refEntity_t barrel; + refEntity_t flash; + vec3_t origin; + int renderfx; + vec3_t mins = {-16, -16, -24}; + vec3_t maxs = {16, 16, 32}; + float len; + float xx; + + if ( !pi->legsModel || !pi->torsoModel || !pi->headModel || !pi->animations[0].numFrames ) { + return; + } + + dp_realtime = time; + + if ( pi->pendingWeapon != -1 && dp_realtime > pi->weaponTimer ) { + pi->weapon = pi->pendingWeapon; + pi->lastWeapon = pi->pendingWeapon; + pi->pendingWeapon = -1; + pi->weaponTimer = 0; + if( pi->currentWeapon != pi->weapon ) { + trap_S_StartLocalSound( weaponChangeSound, CHAN_LOCAL ); + } + } + + UI_AdjustFrom640( &x, &y, &w, &h ); + + y -= jumpHeight; + + memset( &refdef, 0, sizeof( refdef ) ); + memset( &legs, 0, sizeof(legs) ); + memset( &torso, 0, sizeof(torso) ); + memset( &head, 0, sizeof(head) ); + + refdef.rdflags = RDF_NOWORLDMODEL; + + AxisClear( refdef.viewaxis ); + + refdef.x = x; + refdef.y = y; + refdef.width = w; + refdef.height = h; + + refdef.fov_x = (int)((float)refdef.width / 640.0f * 90.0f); + xx = refdef.width / tan( refdef.fov_x / 360 * M_PI ); + refdef.fov_y = atan2( refdef.height, xx ); + refdef.fov_y *= ( 360 / M_PI ); + + // calculate distance so the player nearly fills the box + len = 0.7 * ( maxs[2] - mins[2] ); + origin[0] = len / tan( DEG2RAD(refdef.fov_x) * 0.5 ); + origin[1] = 0.5 * ( mins[1] + maxs[1] ); + origin[2] = -0.5 * ( mins[2] + maxs[2] ); + + refdef.time = dp_realtime; + + trap_R_ClearScene(); + + // get the rotation information + UI_PlayerAngles( pi, legs.axis, torso.axis, head.axis ); + + // get the animation state (after rotation, to allow feet shuffle) + UI_PlayerAnimation( pi, &legs.oldframe, &legs.frame, &legs.backlerp, + &torso.oldframe, &torso.frame, &torso.backlerp ); + + renderfx = RF_LIGHTING_ORIGIN | RF_NOSHADOW; + + // + // add the legs + // + legs.hModel = pi->legsModel; + legs.customSkin = pi->legsSkin; + + VectorCopy( origin, legs.origin ); + + VectorCopy( origin, legs.lightingOrigin ); + legs.renderfx = renderfx; + VectorCopy (legs.origin, legs.oldorigin); + + trap_R_AddRefEntityToScene( &legs ); + + if (!legs.hModel) { + return; + } + + // + // add the torso + // + torso.hModel = pi->torsoModel; + if (!torso.hModel) { + return; + } + + torso.customSkin = pi->torsoSkin; + + VectorCopy( origin, torso.lightingOrigin ); + + UI_PositionRotatedEntityOnTag( &torso, &legs, pi->legsModel, "tag_torso"); + + torso.renderfx = renderfx; + + trap_R_AddRefEntityToScene( &torso ); + + // + // add the head + // + head.hModel = pi->headModel; + if (!head.hModel) { + return; + } + head.customSkin = pi->headSkin; + + VectorCopy( origin, head.lightingOrigin ); + + UI_PositionRotatedEntityOnTag( &head, &torso, pi->torsoModel, "tag_head"); + + head.renderfx = renderfx; + + trap_R_AddRefEntityToScene( &head ); + + // + // add the gun + // + if ( pi->currentWeapon != WP_NONE ) { + memset( &gun, 0, sizeof(gun) ); + gun.hModel = pi->weaponModel; + VectorCopy( origin, gun.lightingOrigin ); + UI_PositionEntityOnTag( &gun, &torso, pi->torsoModel, "tag_weapon"); + gun.renderfx = renderfx; + trap_R_AddRefEntityToScene( &gun ); + } + + // + // add the spinning barrel + // + if ( pi->realWeapon == WP_MACHINEGUN || pi->realWeapon == WP_GAUNTLET || pi->realWeapon == WP_BFG ) { + vec3_t angles; + + memset( &barrel, 0, sizeof(barrel) ); + VectorCopy( origin, barrel.lightingOrigin ); + barrel.renderfx = renderfx; + + barrel.hModel = pi->barrelModel; + angles[YAW] = 0; + angles[PITCH] = 0; + angles[ROLL] = UI_MachinegunSpinAngle( pi ); + if( pi->realWeapon == WP_GAUNTLET || pi->realWeapon == WP_BFG ) { + angles[PITCH] = angles[ROLL]; + angles[ROLL] = 0; + } + AnglesToAxis( angles, barrel.axis ); + + UI_PositionRotatedEntityOnTag( &barrel, &gun, pi->weaponModel, "tag_barrel"); + + trap_R_AddRefEntityToScene( &barrel ); + } + + // + // add muzzle flash + // + if ( dp_realtime <= pi->muzzleFlashTime ) { + if ( pi->flashModel ) { + memset( &flash, 0, sizeof(flash) ); + flash.hModel = pi->flashModel; + VectorCopy( origin, flash.lightingOrigin ); + UI_PositionEntityOnTag( &flash, &gun, pi->weaponModel, "tag_flash"); + flash.renderfx = renderfx; + trap_R_AddRefEntityToScene( &flash ); + } + + // make a dlight for the flash + if ( pi->flashDlightColor[0] || pi->flashDlightColor[1] || pi->flashDlightColor[2] ) { + trap_R_AddLightToScene( flash.origin, 200 + (rand()&31), pi->flashDlightColor[0], + pi->flashDlightColor[1], pi->flashDlightColor[2] ); + } + } + + // + // add the chat icon + // + if ( pi->chat ) { + UI_PlayerFloatSprite( pi, origin, trap_R_RegisterShaderNoMip( "sprites/balloon3" ) ); + } + + // + // add an accent light + // + origin[0] -= 100; // + = behind, - = in front + origin[1] += 100; // + = left, - = right + origin[2] += 100; // + = above, - = below + trap_R_AddLightToScene( origin, 500, 1.0, 1.0, 1.0 ); + + origin[0] -= 100; + origin[1] -= 100; + origin[2] -= 100; + trap_R_AddLightToScene( origin, 500, 1.0, 0.0, 0.0 ); + + trap_R_RenderScene( &refdef ); +} + + +/* +========================== +UI_RegisterClientSkin +========================== +*/ +static qboolean UI_RegisterClientSkin( playerInfo_t *pi, const char *modelName, const char *skinName ) { + char filename[MAX_QPATH]; + + Com_sprintf( filename, sizeof( filename ), "models/players/%s/lower_%s.skin", modelName, skinName ); + pi->legsSkin = trap_R_RegisterSkin( filename ); + + Com_sprintf( filename, sizeof( filename ), "models/players/%s/upper_%s.skin", modelName, skinName ); + pi->torsoSkin = trap_R_RegisterSkin( filename ); + + Com_sprintf( filename, sizeof( filename ), "models/players/%s/head_%s.skin", modelName, skinName ); + pi->headSkin = trap_R_RegisterSkin( filename ); + + if ( !pi->legsSkin || !pi->torsoSkin || !pi->headSkin ) { + return qfalse; + } + + return qtrue; +} + + +/* +====================== +UI_ParseAnimationFile +====================== +*/ +static qboolean UI_ParseAnimationFile( const char *filename, animation_t *animations ) { + char *text_p, *prev; + int len; + int i; + char *token; + float fps; + int skip; + char text[20000]; + fileHandle_t f; + + memset( animations, 0, sizeof( animation_t ) * MAX_ANIMATIONS ); + + // load the file + len = trap_FS_FOpenFile( filename, &f, FS_READ ); + if ( len <= 0 ) { + return qfalse; + } + if ( len >= ( sizeof( text ) - 1 ) ) { + Com_Printf( "File %s too long\n", filename ); + return qfalse; + } + trap_FS_Read( text, len, f ); + text[len] = 0; + trap_FS_FCloseFile( f ); + + // parse the text + text_p = text; + skip = 0; // quite the compiler warning + + // read optional parameters + while ( 1 ) { + prev = text_p; // so we can unget + token = COM_Parse( &text_p ); + if ( !token ) { + break; + } + if ( !Q_stricmp( token, "footsteps" ) ) { + token = COM_Parse( &text_p ); + if ( !token ) { + break; + } + continue; + } else if ( !Q_stricmp( token, "headoffset" ) ) { + for ( i = 0 ; i < 3 ; i++ ) { + token = COM_Parse( &text_p ); + if ( !token ) { + break; + } + } + continue; + } else if ( !Q_stricmp( token, "sex" ) ) { + token = COM_Parse( &text_p ); + if ( !token ) { + break; + } + continue; + } + + // if it is a number, start parsing animations + if ( token[0] >= '0' && token[0] <= '9' ) { + text_p = prev; // unget the token + break; + } + + Com_Printf( "unknown token '%s' is %s\n", token, filename ); + } + + // read information for each frame + for ( i = 0 ; i < MAX_ANIMATIONS ; i++ ) { + + token = COM_Parse( &text_p ); + if ( !token ) { + break; + } + animations[i].firstFrame = atoi( token ); + // leg only frames are adjusted to not count the upper body only frames + if ( i == LEGS_WALKCR ) { + skip = animations[LEGS_WALKCR].firstFrame - animations[TORSO_GESTURE].firstFrame; + } + if ( i >= LEGS_WALKCR ) { + animations[i].firstFrame -= skip; + } + + token = COM_Parse( &text_p ); + if ( !token ) { + break; + } + animations[i].numFrames = atoi( token ); + + token = COM_Parse( &text_p ); + if ( !token ) { + break; + } + animations[i].loopFrames = atoi( token ); + + token = COM_Parse( &text_p ); + if ( !token ) { + break; + } + fps = atof( token ); + if ( fps == 0 ) { + fps = 1; + } + animations[i].frameLerp = 1000 / fps; + animations[i].initialLerp = 1000 / fps; + } + + if ( i != MAX_ANIMATIONS ) { + Com_Printf( "Error parsing animation file: %s", filename ); + return qfalse; + } + + return qtrue; +} + + +/* +========================== +UI_RegisterClientModelname +========================== +*/ +qboolean UI_RegisterClientModelname( playerInfo_t *pi, const char *modelSkinName ) { + char modelName[MAX_QPATH]; + char skinName[MAX_QPATH]; + char filename[MAX_QPATH]; + char *slash; + + pi->torsoModel = 0; + pi->headModel = 0; + + if ( !modelSkinName[0] ) { + return qfalse; + } + + Q_strncpyz( modelName, modelSkinName, sizeof( modelName ) ); + + slash = strchr( modelName, '/' ); + if ( !slash ) { + // modelName did not include a skin name + Q_strncpyz( skinName, "default", sizeof( skinName ) ); + } else { + Q_strncpyz( skinName, slash + 1, sizeof( skinName ) ); + // truncate modelName + *slash = 0; + } + + // load cmodels before models so filecache works + + Com_sprintf( filename, sizeof( filename ), "models/players/%s/lower.md3", modelName ); + pi->legsModel = trap_R_RegisterModel( filename ); + if ( !pi->legsModel ) { + Com_Printf( "Failed to load model file %s\n", filename ); + return qfalse; + } + + Com_sprintf( filename, sizeof( filename ), "models/players/%s/upper.md3", modelName ); + pi->torsoModel = trap_R_RegisterModel( filename ); + if ( !pi->torsoModel ) { + Com_Printf( "Failed to load model file %s\n", filename ); + return qfalse; + } + + Com_sprintf( filename, sizeof( filename ), "models/players/%s/head.md3", modelName ); + pi->headModel = trap_R_RegisterModel( filename ); + if ( !pi->headModel ) { + Com_Printf( "Failed to load model file %s\n", filename ); + return qfalse; + } + + // if any skins failed to load, fall back to default + if ( !UI_RegisterClientSkin( pi, modelName, skinName ) ) { + if ( !UI_RegisterClientSkin( pi, modelName, "default" ) ) { + Com_Printf( "Failed to load skin file: %s : %s\n", modelName, skinName ); + return qfalse; + } + } + + // load the animations + Com_sprintf( filename, sizeof( filename ), "models/players/%s/animation.cfg", modelName ); + if ( !UI_ParseAnimationFile( filename, pi->animations ) ) { + Com_Printf( "Failed to load animation file %s\n", filename ); + return qfalse; + } + + return qtrue; +} + + +/* +=============== +UI_PlayerInfo_SetModel +=============== +*/ +void UI_PlayerInfo_SetModel( playerInfo_t *pi, const char *model ) { + memset( pi, 0, sizeof(*pi) ); + UI_RegisterClientModelname( pi, model ); + pi->weapon = WP_MACHINEGUN; + pi->currentWeapon = pi->weapon; + pi->lastWeapon = pi->weapon; + pi->pendingWeapon = -1; + pi->weaponTimer = 0; + pi->chat = qfalse; + pi->newModel = qtrue; + UI_PlayerInfo_SetWeapon( pi, pi->weapon ); +} + + +/* +=============== +UI_PlayerInfo_SetInfo +=============== +*/ +void UI_PlayerInfo_SetInfo( playerInfo_t *pi, int legsAnim, int torsoAnim, vec3_t viewAngles, vec3_t moveAngles, weapon_t weaponNumber, qboolean chat ) { + int currentAnim; + weapon_t weaponNum; + + pi->chat = chat; + + // view angles + VectorCopy( viewAngles, pi->viewAngles ); + + // move angles + VectorCopy( moveAngles, pi->moveAngles ); + + if ( pi->newModel ) { + pi->newModel = qfalse; + + jumpHeight = 0; + pi->pendingLegsAnim = 0; + UI_ForceLegsAnim( pi, legsAnim ); + pi->legs.yawAngle = viewAngles[YAW]; + pi->legs.yawing = qfalse; + + pi->pendingTorsoAnim = 0; + UI_ForceTorsoAnim( pi, torsoAnim ); + pi->torso.yawAngle = viewAngles[YAW]; + pi->torso.yawing = qfalse; + + if ( weaponNumber != -1 ) { + pi->weapon = weaponNumber; + pi->currentWeapon = weaponNumber; + pi->lastWeapon = weaponNumber; + pi->pendingWeapon = -1; + pi->weaponTimer = 0; + UI_PlayerInfo_SetWeapon( pi, pi->weapon ); + } + + return; + } + + // weapon + if ( weaponNumber == -1 ) { + pi->pendingWeapon = -1; + pi->weaponTimer = 0; + } + else if ( weaponNumber != WP_NONE ) { + pi->pendingWeapon = weaponNumber; + pi->weaponTimer = dp_realtime + UI_TIMER_WEAPON_DELAY; + } + weaponNum = pi->lastWeapon; + pi->weapon = weaponNum; + + if ( torsoAnim == BOTH_DEATH1 || legsAnim == BOTH_DEATH1 ) { + torsoAnim = legsAnim = BOTH_DEATH1; + pi->weapon = pi->currentWeapon = WP_NONE; + UI_PlayerInfo_SetWeapon( pi, pi->weapon ); + + jumpHeight = 0; + pi->pendingLegsAnim = 0; + UI_ForceLegsAnim( pi, legsAnim ); + + pi->pendingTorsoAnim = 0; + UI_ForceTorsoAnim( pi, torsoAnim ); + + return; + } + + // leg animation + currentAnim = pi->legsAnim & ~ANIM_TOGGLEBIT; + if ( legsAnim != LEGS_JUMP && ( currentAnim == LEGS_JUMP || currentAnim == LEGS_LAND ) ) { + pi->pendingLegsAnim = legsAnim; + } + else if ( legsAnim != currentAnim ) { + jumpHeight = 0; + pi->pendingLegsAnim = 0; + UI_ForceLegsAnim( pi, legsAnim ); + } + + // torso animation + if ( torsoAnim == TORSO_STAND || torsoAnim == TORSO_STAND2 ) { + if ( weaponNum == WP_NONE || weaponNum == WP_GAUNTLET ) { + torsoAnim = TORSO_STAND2; + } + else { + torsoAnim = TORSO_STAND; + } + } + + if ( torsoAnim == TORSO_ATTACK || torsoAnim == TORSO_ATTACK2 ) { + if ( weaponNum == WP_NONE || weaponNum == WP_GAUNTLET ) { + torsoAnim = TORSO_ATTACK2; + } + else { + torsoAnim = TORSO_ATTACK; + } + pi->muzzleFlashTime = dp_realtime + UI_TIMER_MUZZLE_FLASH; + //FIXME play firing sound here + } + + currentAnim = pi->torsoAnim & ~ANIM_TOGGLEBIT; + + if ( weaponNum != pi->currentWeapon || currentAnim == TORSO_RAISE || currentAnim == TORSO_DROP ) { + pi->pendingTorsoAnim = torsoAnim; + } + else if ( ( currentAnim == TORSO_GESTURE || currentAnim == TORSO_ATTACK ) && ( torsoAnim != currentAnim ) ) { + pi->pendingTorsoAnim = torsoAnim; + } + else if ( torsoAnim != currentAnim ) { + pi->pendingTorsoAnim = 0; + UI_ForceTorsoAnim( pi, torsoAnim ); + } +} |