// Copyright (C) 2001-2002 Raven Software // // ui_players.c #include "ui_local.h" #include "..\ghoul2\g2.h" #define UI_TIMER_GESTURE 2300 #define UI_TIMER_JUMP 1000 #define UI_TIMER_LAND 130 #define UI_TIMER_WEAPON_SWITCH 300 #define UI_TIMER_ATTACK 500 #define UI_TIMER_MUZZLE_FLASH 20 #define UI_TIMER_WEAPON_DELAY 250 #define JUMP_HEIGHT 56 #define SWINGSPEED 0.3f #define SPIN_SPEED 0.9f #define COAST_TIME 1000 static int dp_realtime; static float jumpHeight; /* =============== UI_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; } /* =============== 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->torsoAnimationTimer > 0 ) { 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; } } /* =============== 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 ); } f = 0; // 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 (lf->animation->numFrames) { 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 != -1 ) //Before 0 meant no loop { if(anim->numFrames - anim->loopFrames == 0) { f %= anim->numFrames; } else { f %= (anim->numFrames - anim->loopFrames); } f += 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 ) == TORSO_IDLE_PISTOL ) { UI_RunLerpFrame( pi, &pi->legs, TORSO_IDLE_PISTOL ); } 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_WALK ) { // 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; 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; vec3_t origin; int renderfx; vec3_t mins = {-16, -16, -24}; vec3_t maxs = {16, 16, 32}; float len; float xx; animation_t *anim; float animSpeed; int flags=BONE_ANIM_OVERRIDE_FREEZE; if ( !pi->playerG2Model ) // || !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; } 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.8f * ( 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 | RF_MINLIGHT; // // add the legs // // legs.hModel = pi->playerModel; // legs.customSkin = pi->legsSkin; legs.ghoul2 = pi->playerG2Model; // lf->animation = anim; // lf->animationTime = lf->frameTime + anim->initialLerp; anim = pi->legs.animation; animSpeed = 50.0f / anim->frameLerp; if (anim->loopFrames != -1) { flags = BONE_ANIM_OVERRIDE_LOOP; } // Temp // flags |= BONE_ANIM_BLEND; /* if (torsoOnly) { trap_G2API_SetBoneAnim(cent->ghoul2, 0, "upper_lumbar", anim->firstFrame, anim->firstFrame + anim->numFrames, flags, animSpeed, lf->frameTime, -1, 150); }*/ // else if ( pi->legsOldAnim != pi->legs.animationNumber ) { trap_G2API_SetBoneAnim( legs.ghoul2, 0, "model_root", anim->firstFrame, anim->firstFrame + anim->numFrames, flags, animSpeed, pi->legs.frameTime / 10, -1, 150); pi->legsOldAnim = pi->legs.animationNumber; } 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 gun // if ( pi->currentWeapon != WP_NONE ) { memset( &gun, 0, sizeof(gun) ); // gun.hModel = pi->weaponModel; // VectorCopy( origin, gun.lightingOrigin ); gun.ghoul2 = pi->weaponG2Model; // NOTENOTE Need to change this to a bolt position on player model //rjr UI_PositionEntityOnTag( &gun, &torso, pi->torsoModel, "tag_weapon"); gun.renderfx = renderfx; trap_R_AddRefEntityToScene( &gun ); } // // 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, 0.5, 0.5, 0.5 ); origin[0] -= 100; origin[1] -= 100; origin[2] -= 100; trap_R_AddLightToScene( origin, 500, 0.5, 0.5, 0.5 ); trap_R_RenderScene( &refdef ); } /* ========================== UI_FileExists ========================== */ static qboolean UI_FileExists(const char *filename) { int len; fileHandle_t f; len = trap_FS_FOpenFile( filename, &f, FS_READ ); if (len>0) { trap_FS_FCloseFile(f); return qtrue; } return qfalse; } /* ========================== UI_RegisterClientIdentity ========================== */ qboolean UI_RegisterClientIdentity ( playerInfo_t *pi, const char* identityName ) { char filename[MAX_QPATH]; if (pi->playerG2Model) { trap_G2API_CleanGhoul2Models(&pi->playerG2Model); pi->playerG2Model = NULL; } pi->playerG2Model = UI_RegisterIdentity ( identityName, filename ); if ( !pi->playerG2Model ) { Com_Printf( "Failed to read Ghoul2 Skin%s\n", filename ); return qfalse; } // load the animations if ( !BG_ParseAnimationFile( filename, pi->animations ) ) { Com_Printf( "Failed to load animation file %s\n", filename ); return qfalse; } pi->legsOldAnim = 0; return qtrue; } /* =============== UI_PlayerInfo_SetIdentity =============== */ void UI_PlayerInfo_SetIdentity ( playerInfo_t *pi, const char *identity ) { memset( pi, 0, sizeof(*pi) ); UI_RegisterClientIdentity ( pi, identity ); pi->weapon = WP_FIRST_RANGED_WEAPON; pi->currentWeapon = pi->weapon; pi->lastWeapon = pi->weapon; pi->pendingWeapon = -1; pi->weaponTimer = 0; pi->chat = qfalse; pi->newModel = qtrue; } /* =============== 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; } 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_DEATH_CHEST_1 || legsAnim == BOTH_DEATH_CHEST_1 ) { torsoAnim = legsAnim = BOTH_DEATH_CHEST_1; pi->weapon = pi->currentWeapon = WP_NONE; 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 ) ) { pi->pendingLegsAnim = legsAnim; } else if ( legsAnim != currentAnim ) { jumpHeight = 0; pi->pendingLegsAnim = 0; UI_ForceLegsAnim( pi, legsAnim ); } // torso animation if ( torsoAnim == TORSO_IDLE_PISTOL ) { if ( weaponNum == WP_NONE || weaponNum == WP_KNIFE ) { torsoAnim = TORSO_IDLE_PISTOL; } else { torsoAnim = TORSO_IDLE_PISTOL; } } currentAnim = pi->torsoAnim & ~ANIM_TOGGLEBIT; if ( torsoAnim != currentAnim ) { pi->pendingTorsoAnim = 0; UI_ForceTorsoAnim( pi, torsoAnim ); } } /* =============== UI_LoadIdentityIcons =============== */ void UI_LoadIdentityIcons ( void ) { int i; char fileName[MAX_OSPATH]; #ifdef SPECIAL_PRE_CACHE return; #endif for ( i = 0; i < bg_identityCount; i ++ ) { Com_sprintf(fileName, sizeof(fileName), "gfx/playericons/%s ( %s ).JPG", bg_identities[i].mCharacter->mName, bg_identities[i].mSkin->mSkin); bg_identities[i].mIcon = trap_R_RegisterShaderNoMip(fileName); } } /* ================= UI_ProcessIdentityItems Attaches all the items for a character skin ================= */ static void UI_ProcessIdentityItems ( TGhoul2 ghoul2, TInventoryTemplate *items ) { int modelIndex; int boltIndex; TSurfaceList *surf; while ( items ) { if ( items->mItem ) { if ( items->mItem->mModel && items->mBolt ) { modelIndex = trap_G2API_InitGhoul2Model(&ghoul2, items->mItem->mModel, 0, 0, 0, 0, 0); if (modelIndex != -1) { boltIndex = trap_G2API_AddBolt ( ghoul2, 0, items->mBolt); if (boltIndex != -1) { trap_G2API_AttachG2Model( ghoul2, modelIndex, ghoul2, boltIndex, 0); } } } surf = items->mItem->mOffList; while(surf) { trap_G2API_SetSurfaceOnOff( ghoul2, 0, surf->mName, G2SURFACEFLAG_OFF); surf = surf->mNext; } surf = items->mItem->mOnList; while(surf) { trap_G2API_SetSurfaceOnOff( ghoul2, 0, surf->mName, 0); surf = surf->mNext; } } items = items->mNext; } } /* ================= UI_RegisterIdentity Registers an identity ================= */ TGhoul2 UI_RegisterIdentity ( const char *identityName, char *animationFile ) { char name[MAX_QPATH]; TGenericParser2 skinFile; TGPGroup *basegroup, *group, *sub; char temp[20480], *end; int numPairs; TIdentity *identity; TGhoul2 ghoul2Ptr; numPairs = 0; end = temp; *end = 0; ghoul2Ptr = NULL; *animationFile = 0; // Find the identity in question identity = BG_FindIdentity( identityName ); if (!identity ) { return NULL; } if (trap_G2API_InitGhoul2Model( &ghoul2Ptr, identity->mCharacter->mModel, 0, 0, 0, (1<<4), 0) == -1) { return NULL; } #ifdef SPECIAL_PRE_CACHE Com_sprintf(temp, sizeof(temp), "gfx/playericons/%s ( %s ).JPG", identity->mCharacter->mName, identity->mSkin->mSkin); identity->mIcon = trap_R_RegisterShaderNoMip(temp); #endif trap_G2API_GetAnimFileNameIndex( ghoul2Ptr, 0, name ); Com_sprintf(animationFile, MAX_QPATH, "%s_mp.cfg", name ); if ( identity->mCharacter->mParent) { UI_ProcessIdentityItems( ghoul2Ptr, identity->mCharacter->mParent->mInventory ); } UI_ProcessIdentityItems( ghoul2Ptr, identity->mCharacter->mInventory); UI_ProcessIdentityItems( ghoul2Ptr, identity->mSkin->mInventory); // don't need the mouth trap_G2API_SetSurfaceOnOff( ghoul2Ptr, 0, "mouth_r", G2SURFACEFLAG_OFF|G2SURFACEFLAG_NODESCENDANTS); trap_G2API_SetSurfaceOnOff( ghoul2Ptr, 0, "mouth_l", G2SURFACEFLAG_OFF|G2SURFACEFLAG_NODESCENDANTS); // Parse the g2skin file Com_sprintf( name, sizeof(name), "models/characters/skins/%s.g2skin", identity->mSkin->mSkin ); skinFile = trap_GP_ParseFile( name, qtrue, qfalse ); if ( !skinFile ) { trap_G2API_CleanGhoul2Models( &ghoul2Ptr); return NULL; } basegroup = trap_GP_GetBaseParseGroup ( skinFile ); group = trap_GPG_GetSubGroups ( basegroup ); while(group) { trap_GPG_GetName ( group, name ); // Parse the material if ( Q_stricmp ( name, "material") == 0) { char matName[MAX_QPATH]; char shaderName[MAX_QPATH]; trap_GPG_FindPairValue ( group, "name", "", matName ); sub = trap_GPG_FindSubGroup ( group, "group"); if (sub) { trap_GPG_FindPairValue ( sub, "shader1", "", shaderName ); if (!shaderName[0]) { trap_GPG_FindPairValue ( sub, "texture1", "", shaderName ); } } if (matName[0] && shaderName[0]) { end += Com_sprintf(end, sizeof(temp) - (end-temp+1), "%s %s ", matName, shaderName); numPairs++; } } group = trap_GPG_GetNext ( group ); } trap_GP_Delete(&skinFile); if (numPairs) { qhandle_t handle; handle = trap_G2API_RegisterSkin( identity->mName, numPairs, temp); trap_G2API_SetSkin( ghoul2Ptr, 0, handle); } return ghoul2Ptr; }