12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379 |
- /*
- ===========================================================================
- 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;
- sfxHandle_t weaponChangeSound;
- /*
- ===============
- 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 -= uiInfo.uiDC.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 -= uiInfo.uiDC.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 = uiInfo.uiDC.frameTime * scale * speed;
- if ( move >= swing ) {
- move = swing;
- *swinging = qfalse;
- }
- *angle = AngleMod( *angle + move );
- } else if ( swing < 0 ) {
- move = uiInfo.uiDC.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;
- }
- // this allows the ui to cache the player model on the main menu
- if (w == 0 || h == 0) {
- 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 / (float)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_FileExists
- ==========================
- */
- static qboolean UI_FileExists(const char *filename) {
- int len;
- len = trap_FS_FOpenFile( filename, 0, FS_READ );
- if (len>0) {
- return qtrue;
- }
- return qfalse;
- }
- /*
- ==========================
- UI_FindClientHeadFile
- ==========================
- */
- static qboolean UI_FindClientHeadFile( char *filename, int length, const char *teamName, const char *headModelName, const char *headSkinName, const char *base, const char *ext ) {
- char *team, *headsFolder;
- int i;
- team = "default";
- if ( headModelName[0] == '*' ) {
- headsFolder = "heads/";
- headModelName++;
- }
- else {
- headsFolder = "";
- }
- while(1) {
- for ( i = 0; i < 2; i++ ) {
- if ( i == 0 && teamName && *teamName ) {
- Com_sprintf( filename, length, "models/players/%s%s/%s/%s%s_%s.%s", headsFolder, headModelName, headSkinName, teamName, base, team, ext );
- }
- else {
- Com_sprintf( filename, length, "models/players/%s%s/%s/%s_%s.%s", headsFolder, headModelName, headSkinName, base, team, ext );
- }
- if ( UI_FileExists( filename ) ) {
- return qtrue;
- }
- if ( i == 0 && teamName && *teamName ) {
- Com_sprintf( filename, length, "models/players/%s%s/%s%s_%s.%s", headsFolder, headModelName, teamName, base, headSkinName, ext );
- }
- else {
- Com_sprintf( filename, length, "models/players/%s%s/%s_%s.%s", headsFolder, headModelName, base, headSkinName, ext );
- }
- if ( UI_FileExists( filename ) ) {
- return qtrue;
- }
- if ( !teamName || !*teamName ) {
- break;
- }
- }
- // if tried the heads folder first
- if ( headsFolder[0] ) {
- break;
- }
- headsFolder = "heads/";
- }
- return qfalse;
- }
- /*
- ==========================
- UI_RegisterClientSkin
- ==========================
- */
- static qboolean UI_RegisterClientSkin( playerInfo_t *pi, const char *modelName, const char *skinName, const char *headModelName, const char *headSkinName , const char *teamName) {
- char filename[MAX_QPATH*2];
- if (teamName && *teamName) {
- Com_sprintf( filename, sizeof( filename ), "models/players/%s/%s/lower_%s.skin", modelName, teamName, skinName );
- } else {
- Com_sprintf( filename, sizeof( filename ), "models/players/%s/lower_%s.skin", modelName, skinName );
- }
- pi->legsSkin = trap_R_RegisterSkin( filename );
- if (!pi->legsSkin) {
- if (teamName && *teamName) {
- Com_sprintf( filename, sizeof( filename ), "models/players/characters/%s/%s/lower_%s.skin", modelName, teamName, skinName );
- } else {
- Com_sprintf( filename, sizeof( filename ), "models/players/characters/%s/lower_%s.skin", modelName, skinName );
- }
- pi->legsSkin = trap_R_RegisterSkin( filename );
- }
- if (teamName && *teamName) {
- Com_sprintf( filename, sizeof( filename ), "models/players/%s/%s/upper_%s.skin", modelName, teamName, skinName );
- } else {
- Com_sprintf( filename, sizeof( filename ), "models/players/%s/upper_%s.skin", modelName, skinName );
- }
- pi->torsoSkin = trap_R_RegisterSkin( filename );
- if (!pi->torsoSkin) {
- if (teamName && *teamName) {
- Com_sprintf( filename, sizeof( filename ), "models/players/characters/%s/%s/upper_%s.skin", modelName, teamName, skinName );
- } else {
- Com_sprintf( filename, sizeof( filename ), "models/players/characters/%s/upper_%s.skin", modelName, skinName );
- }
- pi->torsoSkin = trap_R_RegisterSkin( filename );
- }
- if ( UI_FindClientHeadFile( filename, sizeof(filename), teamName, headModelName, headSkinName, "head", "skin" ) ) {
- 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 );
- COM_Compress(text);
- // 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, const char *headModelSkinName, const char *teamName ) {
- char modelName[MAX_QPATH];
- char skinName[MAX_QPATH];
- char headModelName[MAX_QPATH];
- char headSkinName[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 ) );
- *slash = '\0';
- }
- Q_strncpyz( headModelName, headModelSkinName, sizeof( headModelName ) );
- slash = strchr( headModelName, '/' );
- if ( !slash ) {
- // modelName did not include a skin name
- Q_strncpyz( headSkinName, "default", sizeof( skinName ) );
- } else {
- Q_strncpyz( headSkinName, slash + 1, sizeof( skinName ) );
- *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_sprintf( filename, sizeof( filename ), "models/players/characters/%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_sprintf( filename, sizeof( filename ), "models/players/characters/%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;
- }
- }
- if (headModelName && headModelName[0] == '*' ) {
- Com_sprintf( filename, sizeof( filename ), "models/players/heads/%s/%s.md3", &headModelName[1], &headModelName[1] );
- }
- else {
- Com_sprintf( filename, sizeof( filename ), "models/players/%s/head.md3", headModelName );
- }
- pi->headModel = trap_R_RegisterModel( filename );
- if ( !pi->headModel && headModelName[0] != '*') {
- Com_sprintf( filename, sizeof( filename ), "models/players/heads/%s/%s.md3", headModelName, headModelName );
- 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, headModelName, headSkinName, teamName) ) {
- if ( !UI_RegisterClientSkin( pi, modelName, "default", headModelName, "default", teamName ) ) {
- 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_sprintf( filename, sizeof( filename ), "models/players/characters/%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, const char *headmodel, char *teamName ) {
- memset( pi, 0, sizeof(*pi) );
- UI_RegisterClientModelname( pi, model, headmodel, teamName );
- 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 );
- }
- }
|