mirror of
https://github.com/UberGames/RPG-X2.git
synced 2024-11-15 17:31:38 +00:00
2033 lines
51 KiB
C
2033 lines
51 KiB
C
// Copyright (C) 1999-2000 Id Software, Inc.
|
|
//
|
|
// 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.2 //TiM - 0.3
|
|
|
|
#define SPIN_SPEED 0.9
|
|
#define COAST_TIME 1000
|
|
|
|
|
|
static int dp_realtime;
|
|
static float jumpHeight;
|
|
|
|
//TiM : Bolton Table
|
|
stringID_table_t BoltonTable[BOLTON_MAX + 1] =
|
|
{
|
|
{ ENUM2STRING(BOLTON_HEAD) },
|
|
{ ENUM2STRING(BOLTON_TORSO) },
|
|
{ ENUM2STRING(BOLTON_LEGS) },
|
|
{ NULL, -1 }
|
|
};
|
|
|
|
/*
|
|
===============
|
|
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 );
|
|
}
|
|
|
|
if( pi->weaponModel == 0 )
|
|
{
|
|
if( weaponNum == WP_PHASER )
|
|
{
|
|
|
|
weaponNum = WP_NONE;
|
|
goto tryagain;
|
|
}
|
|
weaponNum = WP_PHASER;
|
|
goto tryagain;
|
|
}
|
|
|
|
strcpy( path, item->world_model );
|
|
COM_StripExtension( path, path );
|
|
strcat( path, "_flash.md3" );
|
|
pi->flashModel = trap_R_RegisterModel( path );
|
|
|
|
switch( weaponNum ) {
|
|
case WP_GRENADE_LAUNCHER:
|
|
MAKERGB( pi->flashDlightColor, 0.6, 0.6, 1 );
|
|
break;
|
|
|
|
case WP_DISRUPTOR:
|
|
MAKERGB( pi->flashDlightColor, 0.6, 0.6, 1 );
|
|
break;
|
|
|
|
case WP_PHASER:
|
|
MAKERGB( pi->flashDlightColor, 0, 0, 0 );
|
|
break;
|
|
|
|
case WP_DERMAL_REGEN:
|
|
MAKERGB( pi->flashDlightColor, 0.6, 0.6, 1 );
|
|
break;
|
|
|
|
case WP_NULL_HAND:
|
|
//MAKERGB( pi->flashDlightColor, 0.6, 0.6, 1 );
|
|
break;
|
|
|
|
case WP_COMPRESSION_RIFLE:
|
|
MAKERGB( pi->flashDlightColor, 0.16, 0.16, 1 );
|
|
break;
|
|
|
|
case WP_TR116:
|
|
MAKERGB( pi->flashDlightColor, 0.6, 0.6, 1 );
|
|
break;
|
|
|
|
case WP_COFFEE:
|
|
MAKERGB( pi->flashDlightColor, 1, 0.6, 0.6 );
|
|
break;
|
|
|
|
case WP_QUANTUM_BURST:
|
|
MAKERGB( pi->flashDlightColor, 0.6, 0.6, 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 == BOTH_JUMP1 ) {
|
|
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 == UI_GetAnim( ANIM_ATTACK, pi->currentWeapon, qtrue ) ) { //BOTH_ATTACK1 //Hack ROFL. Code can't see the ANIM defines from here. ANIM_ATTACK = 22
|
|
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 && !pi->upperEmoting ) {
|
|
if ( currentAnim != TORSO_DROPWEAP1 ) {
|
|
pi->torsoAnimationTimer = UI_TIMER_WEAPON_SWITCH;
|
|
UI_ForceTorsoAnim( pi, TORSO_DROPWEAP1 );
|
|
}
|
|
}
|
|
|
|
if ( pi->torsoAnimationTimer > 0 ) {
|
|
return;
|
|
}
|
|
|
|
/*if( currentAnim == TORSO_GESTURE ) {
|
|
UI_SetTorsoAnim( pi, TORSO_STAND );
|
|
return;
|
|
}*/
|
|
|
|
if( currentAnim == UI_GetAnim( ANIM_ATTACK, pi->currentWeapon, qtrue ) ) { //BOTH_ATTACK1 22 = ANIM_ATTACK
|
|
//if ( pi->currentWeapon == WP_NONE || pi->currentWeapon == WP_PHASER )
|
|
if ( pi->currentWeapon != WP_COMPRESSION_RIFLE
|
|
&& pi->currentWeapon != WP_TR116
|
|
&& pi->currentWeapon != WP_GRENADE_LAUNCHER
|
|
&& pi->currentWeapon != WP_QUANTUM_BURST )
|
|
{
|
|
UI_SetTorsoAnim( pi, BOTH_STAND1 ); //TORSO_STAND
|
|
}
|
|
else
|
|
{
|
|
UI_SetTorsoAnim( pi, BOTH_STAND4 ); //TORSO_STAND
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if ( currentAnim == TORSO_DROPWEAP1 ) {
|
|
UI_PlayerInfo_SetWeapon( pi, pi->weapon );
|
|
pi->torsoAnimationTimer = UI_TIMER_WEAPON_SWITCH;
|
|
UI_ForceTorsoAnim( pi, TORSO_RAISEWEAP1 ); //TORSO_RAISE
|
|
return;
|
|
}
|
|
|
|
if ( currentAnim == TORSO_RAISEWEAP1 ) {
|
|
UI_SetTorsoAnim( pi, BOTH_STAND1 ); //STAND2
|
|
return;
|
|
}
|
|
|
|
//TiM: Was playing a non-loop emote, so go back to default now
|
|
if ( pi->upperEmoting ) {
|
|
if ( !pi->upperLoopEmote )
|
|
{
|
|
UI_SetTorsoAnim( pi, BOTH_STAND1 );
|
|
pi->upperEmoting = qfalse;
|
|
}
|
|
else
|
|
{
|
|
UI_SetTorsoAnim( pi, pi->upperLoopEmote );
|
|
pi->upperEmoting = qfalse;
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
UI_LegsSequencing
|
|
===============
|
|
*/
|
|
static void UI_LegsSequencing( playerInfo_t *pi ) {
|
|
int currentAnim;
|
|
|
|
currentAnim = pi->legsAnim & ~ANIM_TOGGLEBIT;
|
|
|
|
if ( pi->legsAnimationTimer > 0 ) {
|
|
if ( currentAnim == BOTH_JUMP1) {
|
|
jumpHeight = JUMP_HEIGHT * sin( M_PI * ( UI_TIMER_JUMP - pi->legsAnimationTimer ) / UI_TIMER_JUMP );
|
|
}
|
|
return;
|
|
}
|
|
|
|
if ( currentAnim == BOTH_JUMP1) {
|
|
UI_ForceLegsAnim( pi, BOTH_LAND1 );
|
|
pi->legsAnimationTimer = UI_TIMER_LAND;
|
|
jumpHeight = 0;
|
|
return;
|
|
}
|
|
|
|
if ( currentAnim == BOTH_LAND1 ) {
|
|
UI_SetLegsAnim( pi, BOTH_STAND1 );
|
|
return;
|
|
}
|
|
|
|
//TiM: Was playing a non-loop emote, so go back to default now
|
|
if ( pi->lowerEmoting ) {
|
|
if ( !pi->lowerLoopEmote )
|
|
{
|
|
UI_SetLegsAnim( pi, BOTH_STAND1 );
|
|
pi->lowerEmoting = qfalse;
|
|
}
|
|
else
|
|
{
|
|
UI_SetLegsAnim( pi, pi->lowerLoopEmote );
|
|
pi->lowerEmoting = qfalse;
|
|
}
|
|
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 ( anim->numFrames < 0 ) {
|
|
UI_SetLerpFrameAnimation( ci, lf, BOTH_STAND1 );
|
|
}*/
|
|
|
|
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 ) == BOTH_STAND1
|
|
|| ( pi->legsAnim & ~ANIM_TOGGLEBIT ) == BOTH_STAND2
|
|
|| ( pi->legsAnim & ~ANIM_TOGGLEBIT ) == BOTH_STAND4 ) )
|
|
{
|
|
UI_RunLerpFrame( pi, &pi->legs, LEGS_TURN1 );
|
|
} 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 ) != UI_GetAnim( ANIM_IDLE, pi->currentWeapon, qfalse ) //TORSO_STAND2
|
|
|| ( pi->torsoAnim & ~ANIM_TOGGLEBIT ) != UI_GetAnim( ANIM_IDLE, pi->currentWeapon, qtrue ) ) {
|
|
// 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
|
|
if ( !uis.spinView ) {
|
|
//adjust = UI_MovedirAdjustment( pi ); //TiM: Do we really need this?
|
|
adjust = 0;
|
|
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 );
|
|
}
|
|
else {
|
|
pi->torso.yawAngle = headAngles[YAW];
|
|
pi->legs.yawAngle = headAngles[YAW];
|
|
}
|
|
|
|
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.1, &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.data.sprite.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, vec3_t pOrigin, playerInfo_t *pi, int time ) { //RPG-X : TiM- Origin added
|
|
refdef_t refdef;
|
|
refEntity_t legs;
|
|
refEntity_t torso;
|
|
refEntity_t head;
|
|
refEntity_t gun;
|
|
refEntity_t flash;
|
|
vec3_t origin;
|
|
int renderfx;
|
|
vec3_t mins = {-16, -24, -24};
|
|
vec3_t maxs = {16, 16, 32};
|
|
float len;
|
|
float xx;
|
|
int anim;
|
|
|
|
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( trap_S_RegisterSound( "sound/weapons/change.wav" ), 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 * 10.0f); //RPG-X : TiM- 90.0f //Anyone else noticed how the high FOV value distorted the model horribly in the menus? O_o
|
|
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.7f * ( 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] );
|
|
|
|
len = 0.35f * ( maxs[2] - mins[2] ); //TiM: 0.35f
|
|
origin[0] = (len / tan( DEG2RAD(refdef.fov_x) * 0.5 )) + pOrigin[0]; //0.5 //Z scale - Conventional 3d, not Q3 ;)
|
|
origin[1] = 0.5 * ( mins[1] + maxs[1] ) + pOrigin[1]; //Xscale, adding numbers pushes the model to the left
|
|
origin[2] = -0.5 * ( mins[2] + maxs[2] ) + pOrigin[2]; //yScale, adding numbers pushes up
|
|
|
|
refdef.time = dp_realtime;
|
|
|
|
trap_R_ClearScene();
|
|
|
|
//spinView
|
|
if ( uis.spinView )
|
|
{
|
|
pi->viewAngles[YAW] = AngleNormalize360( ( uis.cursorx - uis.cursorpx) + uis.lastYaw );
|
|
|
|
if ( !trap_Key_IsDown( K_MOUSE1 ) )
|
|
{
|
|
uis.spinView = qfalse;
|
|
uis.lastYaw = pi->viewAngles[YAW];
|
|
}
|
|
}
|
|
|
|
//TiM: random emote functionality :)
|
|
//first init the timer so this will start a minute after loading the menu
|
|
if ( pi->randomEmote && pi->nextEmoteTime == 0 ) {
|
|
pi->nextEmoteTime = uis.realtime + ( irandom( 15, 20 ) * 1000 );
|
|
}
|
|
|
|
//whup, time to play a random emote
|
|
if ( pi->randomEmote && uis.realtime > pi->nextEmoteTime ) {
|
|
//randomly pick an anim
|
|
anim = irandom( BOTH_STAND1_RANDOM2, BOTH_STAND1_RANDOM11 );
|
|
|
|
//make sure we can play this emote
|
|
if ( pi->animations[anim].numFrames > 0 ) {
|
|
|
|
UI_ForceLegsAnim( pi, anim );
|
|
UI_ForceTorsoAnim( pi, anim );
|
|
|
|
//play lower
|
|
pi->legsAnimationTimer = pi->animations[ anim ].numFrames * pi->animations[ anim ].frameLerp * ((float)uis.realtime/(float)dp_realtime);
|
|
pi->lowerEmoting = qtrue;
|
|
|
|
pi->torsoAnimationTimer = pi->animations[ anim ].numFrames * pi->animations[ anim ].frameLerp * ((float)uis.realtime/(float)dp_realtime);
|
|
pi->upperEmoting = qtrue;
|
|
|
|
pi->nextEmoteTime = uis.realtime + ( irandom( 10, 20 ) * 1000 ) + pi->legsAnimationTimer;
|
|
}
|
|
else {
|
|
pi->nextEmoteTime = uis.realtime + ( irandom( 3, 8 ) * 1000 );
|
|
}
|
|
}
|
|
|
|
// 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);
|
|
|
|
VectorScale( legs.axis[0], pi->height, legs.axis[0]);
|
|
VectorScale( legs.axis[1], (pi->height * pi->weight), legs.axis[1]); //weight... i think
|
|
VectorScale( legs.axis[2], pi->height, legs.axis[2]);
|
|
legs.origin[2] = legs.origin[2] - (24.0f * (1.0f - pi->height));
|
|
|
|
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 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/chat" ) );
|
|
}
|
|
|
|
//
|
|
// add an accent light
|
|
// TiM: Holy Hell. This explains why the models are washed out when overBrightBits is active. O_o
|
|
// 500 is WAY too high
|
|
//
|
|
origin[0] -= 100; // + = behind, - = in front
|
|
origin[1] += 100; // + = left, - = right
|
|
origin[2] += 100; // + = above, - = below
|
|
trap_R_AddLightToScene( origin, 100, 1.0, 1.0, 1.0 ); //500
|
|
|
|
origin[0] -= 100;
|
|
origin[1] -= 100;
|
|
origin[2] -= 100;
|
|
trap_R_AddLightToScene( origin, 100, 1.0, 0.0, 0.0 );
|
|
|
|
trap_R_RenderScene( &refdef );
|
|
}
|
|
|
|
|
|
/*
|
|
==========================
|
|
UI_RegisterClientSkin
|
|
==========================
|
|
*/
|
|
/*extern char* BG_RegisterRace( const char *name );
|
|
static qboolean UI_RegisterClientSkin( playerInfo_t *pi, const char *modelName, const char *skinName ) {
|
|
char filename[MAX_QPATH];
|
|
|
|
Com_sprintf( filename, sizeof( filename ), "models/players2/%s/lower_%s.skin", modelName, skinName );
|
|
pi->legsSkin = trap_R_RegisterSkin( filename );
|
|
|
|
Com_sprintf( filename, sizeof( filename ), "models/players2/%s/upper_%s.skin", modelName, skinName );
|
|
pi->torsoSkin = trap_R_RegisterSkin( filename );
|
|
|
|
Com_sprintf( filename, sizeof( filename ), "models/players2/%s/head_%s.skin", modelName, skinName );
|
|
pi->headSkin = trap_R_RegisterSkin( filename );
|
|
|
|
Com_sprintf( filename, sizeof( filename ), "models/players2/%s/groups.cfg", modelName);
|
|
strcpy(pi->race, BG_RegisterRace( 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[0] ) {
|
|
break;
|
|
}
|
|
if ( !Q_stricmp( token, "footsteps" ) ) {
|
|
token = COM_Parse( &text_p );
|
|
if ( !token[0] ) {
|
|
break;
|
|
}
|
|
continue;
|
|
} else if ( !Q_stricmp( token, "headoffset" ) ) {
|
|
for ( i = 0 ; i < 3 ; i++ ) {
|
|
token = COM_Parse( &text_p );
|
|
if ( !token[0] ) {
|
|
break;
|
|
}
|
|
}
|
|
continue;
|
|
} else if ( !Q_stricmp( token, "sex" ) ) {
|
|
token = COM_Parse( &text_p );
|
|
if ( !token[0] ) {
|
|
break;
|
|
}
|
|
continue;
|
|
} else if ( !Q_stricmp( token, "soundpath" ) ) {
|
|
token = COM_Parse( &text_p );
|
|
if ( !token[0] ) {
|
|
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[0] ) {
|
|
break;
|
|
}
|
|
animations[i].firstFrame = atoi( token );
|
|
// leg only frames are adjusted to not count the upper body only frames
|
|
if ( i == LEGS_KNEEL1 ) {
|
|
skip = animations[LEGS_KNEEL1].firstFrame - animations[TORSO_ACTIVATEMEDKIT1].firstFrame;
|
|
}
|
|
if ( i >= LEGS_KNEEL1) {
|
|
animations[i].firstFrame -= skip;
|
|
}
|
|
|
|
token = COM_Parse( &text_p );
|
|
if ( !token[0] ) {
|
|
break;
|
|
}
|
|
animations[i].numFrames = atoi( token );
|
|
|
|
token = COM_Parse( &text_p );
|
|
if ( !token[0] ) {
|
|
break;
|
|
}
|
|
animations[i].loopFrames = atoi( token );
|
|
|
|
token = COM_Parse( &text_p );
|
|
if ( !token[0] ) {
|
|
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_InitModelData
|
|
by TiM
|
|
|
|
Initialize default values
|
|
in case the crazy modder
|
|
left out some of the keys.
|
|
|
|
In most cases, the fields
|
|
will just be left blank.
|
|
No point in using extra
|
|
resources if they weren't
|
|
specified.
|
|
======================
|
|
*/
|
|
|
|
static void UI_InitModelData( playerInfo_t *pi ) {
|
|
pi->hasRanks = qfalse;
|
|
|
|
//initialize all model + skin data as 0, so it can be told if they don't get
|
|
//values assigned in the script parser, in which case we exit.
|
|
pi->headModel = 0;
|
|
pi->torsoModel = 0;
|
|
pi->legsModel = 0;
|
|
|
|
pi->headSkin = 0;
|
|
pi->headSkinBlink = 0; //doesn't matter if left 0; won't end the parser
|
|
pi->torsoSkin = 0;
|
|
pi->legsSkin = 0;
|
|
|
|
//doesn't matter if left 0
|
|
pi->headBlinkTime.minSeconds = 0;
|
|
pi->headBlinkTime.maxSeconds = 0;
|
|
|
|
pi->nextTalkTime = 0;
|
|
pi->currentTalkSkin = 0;
|
|
|
|
pi->headSkinTalk[0] = 0;
|
|
pi->headSkinTalk[1] = 0;
|
|
pi->headSkinTalk[2] = 0;
|
|
pi->headSkinTalk[3] = 0;
|
|
|
|
memset( &pi->boltonTags, 0, sizeof(pi->boltonTags));
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
UI_ParseSkinSetDataFile
|
|
by TiM
|
|
|
|
Parses a separate.skinset
|
|
file to get the skin data
|
|
for this model.
|
|
======================
|
|
*/
|
|
|
|
static qboolean UI_ParseSkinSetDataFile( playerInfo_t *pi, const char *skinSetFrame, const char *charName, const char *skinName )
|
|
{
|
|
char* skinStar;
|
|
char skinSetName[MAX_QPATH];
|
|
char skinSetRoute[MAX_QPATH];
|
|
char* token;
|
|
char* textPtr;
|
|
char buffer[5000];
|
|
int len;
|
|
fileHandle_t f;
|
|
int n, i;
|
|
int noBlinking = trap_Cvar_VariableValue( "cg_noBlinkingHeads" );
|
|
|
|
if ( ( skinStar = strstr( skinSetFrame, "*" ) ) == NULL )
|
|
{
|
|
Com_Printf( S_COLOR_RED "ERROR: No '*' specified in model skin set!\n" );
|
|
return qfalse;
|
|
}
|
|
else
|
|
{
|
|
//star is at front
|
|
if ( skinStar == skinSetFrame )
|
|
{
|
|
skinStar++;
|
|
Com_sprintf( skinSetName, sizeof( skinSetName ), "%s%s", skinName, skinStar );
|
|
}
|
|
//star is at end
|
|
else if ((int)(skinStar - skinSetFrame)+1 == (int)strlen(skinSetFrame) )
|
|
{
|
|
Q_strncpyz( skinSetName, skinSetFrame, strlen( skinSetFrame ) );
|
|
Q_strcat( skinSetName, sizeof( skinSetName ), skinName );
|
|
}
|
|
else
|
|
{
|
|
Com_Printf( "ERROR: The '*' in %s must be on either the start or end, not the middle.\n", skinSetFrame );
|
|
return qfalse;
|
|
}
|
|
}
|
|
|
|
//Com_Printf( S_COLOR_RED "DEBUG: skinSetName = %s \n", skinSetName );
|
|
|
|
Com_sprintf( skinSetRoute, sizeof( skinSetRoute ), "models/players_rpgx/%s/%s.skinset", charName, skinSetName );
|
|
|
|
len = trap_FS_FOpenFile( skinSetRoute, &f, FS_READ );
|
|
|
|
if ( len <= 0 )
|
|
{
|
|
Com_Printf( S_COLOR_RED "ERROR: Could not open file: %s\n", skinSetRoute );
|
|
return qfalse;
|
|
}
|
|
|
|
if ( len > sizeof( buffer) - 1 )
|
|
{
|
|
Com_Printf( S_COLOR_RED "ERROR: Imported file is too big for buffer: %s. Len is %i\n", skinSetRoute, len );
|
|
return qfalse;
|
|
}
|
|
|
|
trap_FS_Read( buffer, len, f );
|
|
|
|
trap_FS_FCloseFile( f );
|
|
|
|
if ( !buffer[0] )
|
|
{
|
|
Com_Printf( S_COLOR_RED "ERROR: Could not import data from %s\n", skinSetRoute );
|
|
return qfalse;
|
|
}
|
|
|
|
buffer[len] = '\0';
|
|
|
|
textPtr = buffer;
|
|
|
|
token = COM_Parse( &textPtr );
|
|
|
|
if ( Q_stricmp( token, "{" ) )
|
|
{
|
|
Com_Printf( S_COLOR_RED "ERROR: Skinset %s did not start with a '{'\n", skinSetRoute );
|
|
return qfalse;
|
|
}
|
|
else
|
|
{
|
|
while ( 1 )
|
|
{ //while we don't hit the closing brace
|
|
|
|
token = COM_Parse( &textPtr ); //parse
|
|
if ( !token[0] ) { //error check
|
|
break;
|
|
}
|
|
|
|
//head skin when blinking
|
|
//must be before headskin, or the damn thing will think the two are the same :P
|
|
if ( !Q_stricmpn( token, "headSkinBlink", 13 ) ) {
|
|
if ( COM_ParseString( &textPtr, &token ) ) {
|
|
continue;
|
|
}
|
|
|
|
if ( !noBlinking ) {
|
|
pi->headSkinBlink = trap_R_RegisterSkin( token );
|
|
}
|
|
|
|
if ( !noBlinking && !pi->headSkinBlink ) {
|
|
//We'll alert them, but not cancel the loop
|
|
Com_Printf( S_COLOR_RED "WARNING: Couldn't load headSkinBlink: %s\n", token);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
//head blink time
|
|
else if ( !Q_stricmpn( token, "headBlinkTime", 13 ) )
|
|
{
|
|
//Done this way so we know we got two valid args b4 proceeding
|
|
if ( COM_ParseInt( &textPtr, &n ) ) { //first arg
|
|
SkipRestOfLine( &textPtr );
|
|
continue;
|
|
}
|
|
|
|
if ( COM_ParseInt( &textPtr, &i ) ) { //2nd arg
|
|
SkipRestOfLine( &textPtr );
|
|
continue;
|
|
}
|
|
|
|
//Bug: if the stupid n00b of a modder made
|
|
//the minimum time larger than the max time >.<
|
|
if ( n > i )
|
|
{
|
|
Com_Printf( S_COLOR_RED "ERROR: Minimum blink time was larger than maximum blink time.\n" );
|
|
continue;
|
|
}
|
|
|
|
if ( !noBlinking ) {
|
|
pi->headBlinkTime.minSeconds = n;
|
|
pi->headBlinkTime.maxSeconds = i;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
else if ( !Q_stricmpn( token, "torsoSkin", 9 ) ) {
|
|
if (COM_ParseString( &textPtr, &token ) ) {
|
|
continue;
|
|
}
|
|
|
|
pi->torsoSkin = trap_R_RegisterSkin( token );
|
|
if (!pi->torsoSkin ) {
|
|
Com_Printf( S_COLOR_RED "ERROR: Couldn't load torsoSkin: %s\n", token);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
else if ( !Q_stricmpn( token, "legsSkin", 8 ) ) {
|
|
if (COM_ParseString( &textPtr, &token ) ) {
|
|
continue;
|
|
}
|
|
|
|
pi->legsSkin = trap_R_RegisterSkin( token );
|
|
if (!pi->legsSkin ) {
|
|
Com_Printf( S_COLOR_RED "ERROR: Couldn't load legsSkin: %s\n", token);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
else if ( !Q_stricmpn( token, "headSkinTalk", 12 ) )
|
|
{
|
|
SkipBracedSection( &textPtr );
|
|
continue;
|
|
}
|
|
|
|
//head skin
|
|
else if ( !Q_stricmp( token, "headSkin" ) ) {
|
|
if ( COM_ParseString( &textPtr, &token ) ) {
|
|
continue;
|
|
}
|
|
|
|
pi->headSkin = trap_R_RegisterSkin( token );
|
|
if ( !pi->headSkin ) {
|
|
Com_Printf( S_COLOR_RED "ERROR: Couldn't load headSkin: %s\n", token );
|
|
return qfalse;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if ( !Q_stricmpn( token, "}", 1) ) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return qtrue;
|
|
}
|
|
|
|
|
|
/*
|
|
======================
|
|
UI_ParseModelDataFile
|
|
by TiM
|
|
|
|
Reads in the .model file
|
|
needed to put together
|
|
a character model.
|
|
======================
|
|
*/
|
|
|
|
qboolean UI_ParseModelDataFile( playerInfo_t *pi, const char *charName,
|
|
const char *modelName, const char *skinName ) {
|
|
fileHandle_t file;
|
|
int file_len;
|
|
char charText[20000];
|
|
char *textPtr, *prevValue;
|
|
char fileName[MAX_QPATH];
|
|
//char animPath[MAX_QPATH];
|
|
int i, n;
|
|
char *token;
|
|
char legsFileRoute[MAX_QPATH];
|
|
qboolean didAnims = qfalse;
|
|
qboolean skinSetFound=qfalse;
|
|
int noBlinking;
|
|
|
|
noBlinking = trap_Cvar_VariableValue( "cg_noBlinkingHeads" );
|
|
//size_t strLen;
|
|
|
|
//create the file route
|
|
Com_sprintf( fileName, sizeof(fileName), "models/players_rpgx/%s/%s.model", charName, modelName);
|
|
|
|
//Okay... gotta get the hang of ANSI C text parsing >.<
|
|
//first... I guess load the file
|
|
file_len = trap_FS_FOpenFile( fileName, &file, FS_READ );
|
|
//Error handle
|
|
//if length was 0, ie file not found or was empty
|
|
if (file_len <= 0 ) {
|
|
return qfalse;
|
|
}
|
|
//Another error... if text is WAY bigger than our available buffer O_O
|
|
if ( file_len >= sizeof( charText ) - 1 ) {
|
|
Com_Printf( S_COLOR_RED "Model Data File %s too long... WAY too long\n", fileName );
|
|
return qfalse;
|
|
}
|
|
|
|
//initialize the buffer
|
|
memset( charText, 0, sizeof( charText ) );
|
|
|
|
//read data into char array
|
|
//i guess we use a char array so we can actually specify size/width.
|
|
trap_FS_Read( charText, file_len, file );
|
|
//I guess this is needed to mark the EOF.
|
|
charText[file_len] = 0;
|
|
//Free memory. Close Files
|
|
trap_FS_FCloseFile( file );
|
|
|
|
//default values if needed
|
|
UI_InitModelData( pi );
|
|
|
|
//Used to just clear any previous parse temp data
|
|
COM_BeginParseSession();
|
|
|
|
//transfer our data from a char array to a char ptr.
|
|
//needed for the parsing func methinks
|
|
textPtr = charText;
|
|
|
|
token = COM_Parse( &textPtr ); //COM_Parse seems to work by splitting up each line of text by the spaces,
|
|
//and then removes that chunk from the original
|
|
//Okay, we should have the beginning variable first... which should be a '{'
|
|
|
|
//from the looks of this, I think we have to do this after
|
|
//every parse call. O_O
|
|
if ( !token[0] ) {
|
|
Com_Printf( S_COLOR_RED "No data found in model data buffer!\n");
|
|
return qfalse;
|
|
}
|
|
|
|
if ( Q_stricmp(token, "{" ) ) {
|
|
Com_Printf(S_COLOR_RED "Missing { in %s\n", fileName);
|
|
return qfalse;
|
|
}
|
|
|
|
while ( 1 ) {
|
|
prevValue = textPtr; //set a backup
|
|
token = COM_Parse( &textPtr );
|
|
|
|
if (!token[0] || !token ) { //we've hit the end of the file. w00t! exit!
|
|
break;
|
|
}
|
|
|
|
//if we randomly find a brace in here (ie a sub-struct that may have no header)
|
|
//just skip it. :P
|
|
if ( !Q_stricmpn( token, "{", 1 ) ) {
|
|
SkipBracedSection ( &textPtr );
|
|
}
|
|
|
|
if ( !Q_stricmpn( token, "animsConfig", 11 ) ) {
|
|
if( COM_ParseString( &textPtr, &token ) ) {
|
|
continue;
|
|
}
|
|
|
|
//no valid anim file found. Don't give up hope though.
|
|
//We have a backup resort at the end if need be. :)
|
|
if ( ( didAnims = UI_ParseAnimationFile( token, pi->animations ) ) == qfalse ) {
|
|
Com_Printf( S_COLOR_RED "WARNING: Was unable to load file %s.\n", token );
|
|
}
|
|
continue;
|
|
}
|
|
|
|
//playermodel gender
|
|
else if ( !Q_stricmpn( token, "sex", 3 ) ) {
|
|
if (COM_ParseString( &textPtr, &token ) ) {
|
|
continue;
|
|
}
|
|
if ( token[0] == 'f' || token[0] == 'F' ) {
|
|
pi->gender = GENDER_FEMALE;
|
|
} else if ( token[0] == 'n' || token[0] == 'N' ) {
|
|
pi->gender = GENDER_NEUTER;
|
|
} else {
|
|
pi->gender = GENDER_MALE;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
//character's legs model
|
|
else if ( !Q_stricmpn( token, "legsModel", 9 ) ) {
|
|
|
|
if( COM_ParseString( &textPtr, &token ) ) {
|
|
continue;
|
|
}
|
|
|
|
pi->legsModel = trap_R_RegisterModel( token );
|
|
if (!pi->legsModel) {
|
|
Com_Printf( S_COLOR_RED "ERROR: Unable to load legs model: %s\n", token);
|
|
return qfalse;
|
|
}
|
|
|
|
//if loaded no anims yet, copy the legs route to this variable,
|
|
//and we'll try again at the end of the function
|
|
//if ( ci->animIndex == -1 ) {
|
|
Q_strncpyz( legsFileRoute, token, sizeof( legsFileRoute ) );
|
|
//} Actually. just copy it regardless. Just in case
|
|
|
|
continue;
|
|
}
|
|
|
|
//character's torso model
|
|
else if ( !Q_stricmpn( token, "torsoModel", 10 ) ) {
|
|
if( COM_ParseString( &textPtr, &token ) ) {
|
|
continue;
|
|
}
|
|
pi->torsoModel = trap_R_RegisterModel( token );
|
|
//Com_Printf("Torsomodel passed as %s, %i\n", token, (int)ci->torsoModel);
|
|
|
|
if (!pi->torsoModel) {
|
|
Com_Printf( S_COLOR_RED "ERROR: Unable to load torso model: %s\n", token);
|
|
return qfalse;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
//character's headmodel
|
|
else if ( !Q_stricmpn( token, "headModel", 9 ) ) {
|
|
|
|
//return true = no extra text found on this line - bad! O_O!
|
|
if( COM_ParseString( &textPtr, &token ) ) {
|
|
continue;
|
|
}
|
|
|
|
pi->headModel = trap_R_RegisterModel( token );
|
|
if (!pi->headModel) {
|
|
Com_Printf( S_COLOR_RED "ERROR: Unable to load head model: %s\n", token);
|
|
return qfalse;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// Custom bolton models... oi O_o
|
|
else if ( !Q_stricmpn( token, "boltonModels", 12 ) ) {
|
|
//needed coz '{' could also be on next line
|
|
token = COM_Parse( &textPtr );
|
|
if ( !token[0] ) { //if that was it
|
|
break;
|
|
} else { //else, if next character is '{'
|
|
if ( !Q_stricmpn( token, "{", 1 ) ) {
|
|
token = COM_Parse( &textPtr );
|
|
if ( !token[0] ) {
|
|
break;
|
|
}
|
|
//loop till we hit the end of the brackets
|
|
i = 0;
|
|
|
|
while ( Q_stricmp( token, "}" ) ) {
|
|
if ( !Q_stricmpn( token, "BOLTON_", 7 ) ) {
|
|
|
|
pi->boltonTags[i].modelBase = GetIDForString( BoltonTable, token );
|
|
|
|
if( COM_ParseString( &textPtr, &token ) ) {
|
|
continue;
|
|
}
|
|
|
|
if (!Q_stricmpn( token, "tag_", 4 ) ) {
|
|
Q_strncpyz(pi->boltonTags[i].tagName, token, sizeof (pi->boltonTags[i].tagName) );
|
|
|
|
if( COM_ParseString( &textPtr, &token ) ) {
|
|
continue;
|
|
}
|
|
pi->boltonTags[i].tagModel = trap_R_RegisterModel( token );
|
|
|
|
if (!pi->boltonTags[i].tagModel) {
|
|
Com_Printf( S_COLOR_RED "WARNING: Unable to load bolton model: %s\n", token);
|
|
}
|
|
|
|
i++;
|
|
|
|
if (i > MAX_BOLTONS -1) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//Com_Printf("Index: %i, Name: %s, Handle: %i\n", ci->boltonTags[ci->numBoltOns].modelBase, ci->boltonTags[ci->numBoltOns].tagName, ci->boltonTags[ci->numBoltOns].tagModel );
|
|
token = COM_Parse( &textPtr );
|
|
if ( !token[0] ) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//whether char is allowed to wear ranks
|
|
else if ( !Q_stricmpn( token, "hasRanks", 8 ) ) {
|
|
if (COM_ParseInt(&textPtr, &n ) ) {
|
|
continue;
|
|
}
|
|
pi->hasRanks = n;
|
|
continue;
|
|
}
|
|
|
|
//TiM - The skinset is defined
|
|
else if ( !Q_stricmpn( token, "skinSet", 7 ) ) {
|
|
if ( COM_ParseString( &textPtr, &token ) ) {
|
|
continue;
|
|
}
|
|
|
|
if ( !UI_ParseSkinSetDataFile( pi, token, charName, skinName ) )
|
|
{
|
|
Com_Printf( S_COLOR_RED "WARNING: Could not load data from specified skin set in char: %s. Attempting to load default.\n", charName );
|
|
}
|
|
else
|
|
{
|
|
skinSetFound = qtrue;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if ( !skinSetFound )
|
|
{
|
|
if ( !UI_ParseSkinSetDataFile( pi, va("%s_*", modelName, skinName ), charName, skinName ) )
|
|
{
|
|
Com_Printf( S_COLOR_RED "ERROR: Tried loading default skin set, however it failed.\n");
|
|
}
|
|
}
|
|
|
|
//if any of the models or skins were left blank, then output false. Coz we need them. :P
|
|
if (!pi->headModel || !pi->torsoModel || !pi->legsModel ) {
|
|
Com_Printf( S_COLOR_RED "One or more necessary model files weren't loaded from %s\n", fileName );
|
|
return qfalse;
|
|
}
|
|
|
|
if (!pi->headSkin || !pi->torsoSkin || !pi->legsSkin ) {
|
|
Com_Printf( S_COLOR_RED "One or more necessary skin files weren't loaded from %s\n", fileName );
|
|
return qfalse;
|
|
}
|
|
|
|
//if modder specified no animations file route, or they did, and it sucked (ie -1 ),
|
|
//Then try looking for one in the same directory as the lower.mdr file
|
|
|
|
//k... the goal of this is to take a string like
|
|
//models/players_rpgx/crewman_male/lower.mdr
|
|
//and turn it into
|
|
//models/players_rpgx/crewman_male/animation.cfg
|
|
|
|
if ( !didAnims && strlen( legsFileRoute ) > 0 ) {
|
|
//get length of file route
|
|
i = strlen(legsFileRoute);
|
|
|
|
while( 1 ) {
|
|
//if we looped all the way to the end.... ie BAD
|
|
if (i <= 0) {
|
|
//we obviously have no animation directory :(
|
|
Com_Printf(S_COLOR_RED "ERROR: Was unable to calculate location of animation.cfg for %s\n", fileName);
|
|
return qfalse;
|
|
}
|
|
|
|
//if this is the first '/' we come across from going from the end to the start
|
|
if (legsFileRoute[i] == '/' ) {
|
|
//copy i bytes of data from token to animpath (effectively giving us the route, with no file)
|
|
Q_strncpyz(legsFileRoute, legsFileRoute, (i = i + 2 )); //+2 for the null char these things auto assign at the end... i think
|
|
break; //won't work without it anyway :P
|
|
}
|
|
i--;
|
|
}
|
|
|
|
//add animation.cfg to the end of the string
|
|
Q_strcat(legsFileRoute, sizeof(legsFileRoute), "animation.cfg");
|
|
|
|
//Com_Printf( S_COLOR_RED "WARNING: Failed to load animation file specified in model config, attempting to load %s\n", legsFileRoute );
|
|
|
|
//Parse it
|
|
if ( !UI_ParseAnimationFile( legsFileRoute, pi->animations) ) {
|
|
Com_Printf( "Tried loading anim data from location %s, however nothing was valid.\n", legsFileRoute );
|
|
return qfalse;
|
|
}
|
|
}
|
|
else {
|
|
if ( !legsFileRoute[0] ) {
|
|
Com_Printf( S_COLOR_RED "Couldn't load/locate any player animation data for player: %s.\n", charName );
|
|
return qfalse;
|
|
}
|
|
}
|
|
|
|
//holy fudgenuggets. after all that checking, we actually made it to the end and have a valid freaking
|
|
//model! OWNED!
|
|
return qtrue;
|
|
}
|
|
|
|
|
|
/*
|
|
==========================
|
|
UI_RegisterClientModelname
|
|
==========================
|
|
*/
|
|
qboolean UI_RegisterClientModelname( playerInfo_t *pi, const char *modelSkinName ) {
|
|
char charName[MAX_QPATH];
|
|
char modelName[MAX_QPATH];
|
|
char skinName[MAX_QPATH];
|
|
//char filename[MAX_QPATH];
|
|
char *model, *skin;
|
|
//char *slash;
|
|
int len;
|
|
|
|
pi->torsoModel = 0;
|
|
pi->headModel = 0;
|
|
|
|
if ( !modelSkinName[0] ) {
|
|
return qfalse;
|
|
}
|
|
|
|
Q_strncpyz( charName, modelSkinName, sizeof( charName ) );
|
|
|
|
/*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;
|
|
}*/
|
|
|
|
//step 1, take the first bit of the string and put it in the charName var.
|
|
if ( ( model = strchr( charName, '/') ) == NULL ) { //if there's no slash
|
|
Q_strncpyz( charName, modelSkinName, sizeof( charName ) ); //just set it
|
|
} else { //otherwise, isolate the first bit, and copy that
|
|
len = strlen( modelSkinName );
|
|
Q_strncpyz( charName, modelSkinName, ((int)len - (int)strlen(model)) + 1 );
|
|
}
|
|
//Com_Printf("%s\n", newInfo.charName);
|
|
|
|
//slash = strchr( newInfo.modelName, '/' );
|
|
if ( !model || !model[1] ) {
|
|
// modelName didn not include a skin name
|
|
//Q_strncpyz( newInfo.skinName, "default", sizeof( newInfo.skinName ) );
|
|
Q_strncpyz( modelName, "main", sizeof( modelName ) );
|
|
Q_strncpyz( skinName, "default", sizeof( skinName ) );
|
|
|
|
if ( model && !model[1] )
|
|
{//if we had a slash, but nothing after, clear it
|
|
*model = 0;
|
|
}
|
|
} else {
|
|
//*model++; //bypass the slash
|
|
model++;
|
|
len = strlen(model);
|
|
skin = strchr( model, '/' );
|
|
|
|
//if there was a model defined, but no skin
|
|
if ( !skin || !skin[1] ) {
|
|
//no skin, but I'm guessing we gotz a model at least
|
|
if ( !skin ) {
|
|
Q_strncpyz( modelName, model, sizeof( modelName ) );
|
|
}
|
|
else {
|
|
if ( !skin[1] ) {
|
|
Q_strncpyz( modelName, model, (int)strlen(model) );
|
|
}
|
|
}
|
|
|
|
Q_strncpyz( skinName, "default", sizeof( skinName ) );
|
|
|
|
if ( skin && !skin[1] ) {
|
|
*skin = 0;
|
|
}
|
|
} else {
|
|
//*skin++;
|
|
skin++;
|
|
Q_strncpyz( modelName, model, ((int)len - (int)strlen(skin)) );
|
|
Q_strncpyz( skinName, skin, sizeof( skinName ) );
|
|
}
|
|
|
|
//Q_strncpyz( newInfo.skinName, slash + 1, sizeof( newInfo.skinName ) );
|
|
// truncate modelName
|
|
*model = 0;
|
|
}
|
|
|
|
// load cmodels before models so filecache works
|
|
//try loading the main model
|
|
if ( !UI_ParseModelDataFile( pi, charName, modelName, skinName ) )
|
|
{
|
|
Com_Printf( S_COLOR_RED "Was unable to parse model file for character: %s/%s/%s\n", charName, modelName, skinName );
|
|
//if that fails, try and load the model's default data at least
|
|
if ( !UI_ParseModelDataFile( pi, charName, DEFAULT_MODEL, DEFAULT_SKIN ) )
|
|
{
|
|
//if THAT fails, try loading our defualt char, with the specfied char's model and skin parms
|
|
if ( !UI_ParseModelDataFile( pi, ui_defaultChar.string, modelName, skinName ) )
|
|
{
|
|
if ( !UI_ParseModelDataFile( pi, DEFAULT_CHAR, DEFAULT_MODEL, DEFAULT_SKIN ) )
|
|
{
|
|
//if all else fails, try and load the normal default model
|
|
return qfalse;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*Com_sprintf( filename, sizeof( filename ), "models/players2/%s/lower.mdr", modelName );
|
|
pi->legsModel = trap_R_RegisterModel( filename );
|
|
if ( !pi->legsModel )
|
|
{
|
|
Com_sprintf( filename, sizeof( filename ), "models/players2/%s/lower.md3", modelName );
|
|
pi->legsModel = trap_R_RegisterModel( filename );
|
|
if ( !pi->legsModel )
|
|
{
|
|
Com_Printf( S_COLOR_RED"Failed to load model file %s\n", filename );
|
|
return qfalse;
|
|
}
|
|
}
|
|
|
|
Com_sprintf( filename, sizeof( filename ), "models/players2/%s/upper.mdr", modelName );
|
|
pi->torsoModel = trap_R_RegisterModel( filename );
|
|
if ( !pi->torsoModel )
|
|
{
|
|
Com_sprintf( filename, sizeof( filename ), "models/players2/%s/upper.md3", modelName );
|
|
pi->torsoModel = trap_R_RegisterModel( filename );
|
|
if ( !pi->torsoModel ) {
|
|
Com_Printf( S_COLOR_RED"Failed to load model file %s\n", filename );
|
|
return qfalse;
|
|
}
|
|
}
|
|
|
|
Com_sprintf( filename, sizeof( filename ), "models/players2/%s/head.md3", modelName );
|
|
pi->headModel = trap_R_RegisterModel( filename );
|
|
if ( !pi->headModel )
|
|
{
|
|
Com_Printf( S_COLOR_RED"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/players2/%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 );
|
|
Q_strncpyz( pi->modelName, model, sizeof( pi->modelName ) );
|
|
pi->weapon = WP_NONE;
|
|
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, float height, float weight, qboolean chat ) {
|
|
int currentAnim;
|
|
weapon_t weaponNum;
|
|
|
|
pi->chat = chat;
|
|
|
|
// view angles
|
|
VectorCopy( viewAngles, pi->viewAngles );
|
|
|
|
// move angles
|
|
VectorCopy( moveAngles, pi->moveAngles );
|
|
|
|
//TiM : Clamp weight and height
|
|
pi->height = height;
|
|
pi->weight = weight;
|
|
|
|
if ( !pi->weight )
|
|
pi->weight = 1.0f;
|
|
if ( !pi->height )
|
|
pi->height = 1.0f;
|
|
|
|
pi->height = Com_Clamp( 0.9f, 1.15f, pi->height );
|
|
pi->weight = Com_Clamp( 0.9f, 1.1f, pi->weight );
|
|
|
|
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 != BOTH_JUMP1 && ( currentAnim == BOTH_JUMP1 || currentAnim == BOTH_LAND1 ) ) {
|
|
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) //TiM: TORSO_STAND2
|
|
if ( torsoAnim == UI_GetAnim( ANIM_IDLE, pi->currentWeapon, qtrue ) )
|
|
{
|
|
/*if ( weaponNum == WP_NONE || weaponNum == WP_PHASER )
|
|
{
|
|
torsoAnim = TORSO_STAND2;
|
|
}
|
|
else
|
|
{
|
|
torsoAnim = TORSO_STAND2;
|
|
}*/
|
|
|
|
/*if ( weaponNum == WP_COMPRESSION_RIFLE || weaponNum == WP_TR116 )
|
|
{
|
|
torsoAnim = TORSO_STAND;
|
|
}
|
|
else
|
|
{
|
|
torsoAnim = TORSO_STAND2;
|
|
}*/
|
|
torsoAnim = UI_GetAnim( ANIM_IDLE, pi->currentWeapon, qtrue );
|
|
|
|
}
|
|
|
|
//if ( torsoAnim == TORSO_ATTACK || torsoAnim == TORSO_ATTACK2 )
|
|
if ( torsoAnim == UI_GetAnim( ANIM_ATTACK, pi->currentWeapon, qtrue ) )
|
|
{
|
|
/*if ( weaponNum == WP_NONE || weaponNum == WP_PHASER )
|
|
{
|
|
torsoAnim = TORSO_ATTACK2;
|
|
}
|
|
else
|
|
{
|
|
torsoAnim = TORSO_ATTACK;
|
|
}*/
|
|
|
|
/*if ( weaponNum == WP_COMPRESSION_RIFLE || weaponNum == WP_TR116 )
|
|
{
|
|
torsoAnim = TORSO_ATTACK;
|
|
}
|
|
else
|
|
{
|
|
torsoAnim = TORSO_ATTACK2;
|
|
}*/
|
|
|
|
torsoAnim = UI_GetAnim( ANIM_ATTACK, pi->currentWeapon, qtrue );
|
|
|
|
pi->muzzleFlashTime = dp_realtime + UI_TIMER_MUZZLE_FLASH;
|
|
//FIXME play firing sound here
|
|
}
|
|
|
|
currentAnim = pi->torsoAnim & ~ANIM_TOGGLEBIT;
|
|
|
|
if ( weaponNum != pi->currentWeapon || currentAnim == TORSO_RAISEWEAP1 || currentAnim == TORSO_DROPWEAP1 ) {
|
|
pi->pendingTorsoAnim = torsoAnim;
|
|
}
|
|
else if ( ( /*currentAnim == TORSO_GESTURE ||*/ currentAnim == UI_GetAnim(ANIM_ATTACK, pi->currentWeapon, qtrue) ) && ( torsoAnim != currentAnim ) ) {
|
|
pi->pendingTorsoAnim = torsoAnim;
|
|
}
|
|
else if ( torsoAnim != currentAnim ) {
|
|
pi->pendingTorsoAnim = 0;
|
|
UI_ForceTorsoAnim( pi, torsoAnim );
|
|
}
|
|
}
|