/* Copyright (c) 2002-2012 Croteam Ltd. This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation This program 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 this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include #include #include #include #include #include #include /* * One animation of an animateable object */ class COneAnim { public: COneAnim(); ~COneAnim(); // copy constructor COneAnim &operator=(const COneAnim &oaAnim); NAME oa_Name; TIME oa_SecsPerFrame; // speed of this animation INDEX oa_NumberOfFrames; INDEX *oa_FrameIndices; // array of frame indices }; /* * Node used for linking ptrs to COneAnim objects while loading * script file before turning them into an array * Class is used only for loading script files */ class COneAnimNode { public: ~COneAnimNode(); COneAnimNode(COneAnim *AnimToInsert, CListHead *LH); CListNode coan_Node; COneAnim *coan_OneAnim; }; COneAnimNode::~COneAnimNode() { ASSERT( coan_OneAnim != NULL); delete coan_OneAnim; } /* * This temporary list head class is used for automatic deleting of temporary list on exit */ class CTmpListHead : public CListHead { public: ~CTmpListHead(); }; CTmpListHead::~CTmpListHead() { FORDELETELIST(COneAnimNode, coan_Node, *this, it) delete &it.Current(); } // Remember ptr to animation and add this node at the end of given animation list COneAnimNode::COneAnimNode(COneAnim *AnimToInsert, CListHead *LH) { coan_OneAnim = AnimToInsert; LH->AddTail( coan_Node); } // Constructor sets invalid data COneAnim::COneAnim() { oa_FrameIndices = NULL; } // Free allocated frame indices array for this animation COneAnim::~COneAnim() { ASSERT(oa_FrameIndices != NULL); FreeMemory( oa_FrameIndices); oa_FrameIndices = NULL; } /* * Copy constructor. */ COneAnim &COneAnim::operator=(const COneAnim &oaAnim) { ASSERT( oaAnim.oa_NumberOfFrames > 0); strcpy(oa_Name, oaAnim.oa_Name); oa_SecsPerFrame = oaAnim.oa_SecsPerFrame; oa_NumberOfFrames = oaAnim.oa_NumberOfFrames; if( oa_FrameIndices != NULL) { FreeMemory(oa_FrameIndices); } oa_FrameIndices = (INDEX *) AllocMemory( sizeof(INDEX) * oa_NumberOfFrames); for( INDEX iFrame = 0; iFrame0); strcpy( cfnn_FileName, NewFileName); LH->AddTail( cfnn_Node); } CAnimData::CAnimData() { ad_Anims = NULL; ad_NumberOfAnims = 0; } CAnimData::~CAnimData() { Clear(); } void CAnimData::Clear() { if(ad_Anims != NULL) delete[] ad_Anims; ad_Anims = NULL; ad_NumberOfAnims = 0; // clear serial object CSerial::Clear(); } // get amount of memory used by this object SLONG CAnimData::GetUsedMemory(void) { SLONG slUsed = sizeof(*this)+sizeof(COneAnim)*ad_NumberOfAnims; slUsed += strlen(GetName())+1; for(INDEX iAnim=0; iAnimRelease(this); } // creates given number of default animations (1 frame, given name and speed) void CAnimData::CreateAnimations( INDEX ctAnimations, CTString strName/*="None"*/, INDEX iDefaultFrame/*=0*/, TIME tmSpeed/*=0.02f*/) { ASSERT(strlen(strName)oa_Name,"OnlyAnim"); ad_Anims->oa_SecsPerFrame = (TIME) 0.02; ad_Anims->oa_NumberOfFrames = 1; ad_Anims->oa_FrameIndices = (INDEX *) AllocMemory( sizeof(INDEX)); ad_Anims->oa_FrameIndices[0] = 0; } // Returns index of given frame name in global frame names list. If it is not found // new CFileNameObject is added into frames list INDEX FindFrameIndex( CListHead *pFrameFileList, const char *pFileName) { UWORD i=0; FOREACHINLIST(CFileNameNode, cfnn_Node, *pFrameFileList, it) { if( strcmpi(it->cfnn_FileName, pFileName) == 0) return( i); i++; } new CFileNameNode( pFileName, pFrameFileList); return( i); } CTString GetFrameFileName( CListHead *pFrameFileList, INDEX iMemberInList) { ASSERT( iMemberInListCount()); UWORD iMember=0; FOREACHINLIST(CFileNameNode, cfnn_Node, *pFrameFileList, it) { if( iMember == iMemberInList) return CTString( it->cfnn_FileName); iMember++; } ASSERTALWAYS( "Frame with given index is not found in list of frames"); return ""; } // If found given word at the beginning of curently loaded line #define EQUAL_SUB_STR( str) (strnicmp( ld_line, str, strlen(str)) == 0) // Loads part of given script file until word AnimEnd is reached // Fills ACanimData (its instance) with apropriate data (animations and their frame indices) // and fills given list head with string nodes containing file names representing frames // needed to be loaded by a parent object void CAnimData::LoadFromScript_t( CTStream *File, CListHead *pFrameFileList) // throw char * { UWORD i; char error_str[ 256]; char key_word[ 256]; char base_path[ PATH_MAX] = ""; char file_name[ PATH_MAX]; char anim_name[ 256]; char full_path[ PATH_MAX]; char ld_line[ 128]; CTmpListHead TempAnimationList; SLONG lc; BOOL ret_val; //ASSERT( ad_Anims == NULL); // clears possible animations CAnimData::Clear(); ret_val = TRUE; FOREVER { // Repeat reading of one line of script file until it is not empty or comment do { File->GetLine_t(ld_line, 128); } while( (strlen( ld_line)== 0) || (ld_line[0]==';')); // If key-word is "/*", search end of comment block if( EQUAL_SUB_STR( "/*")) { do { File->GetLine_t(ld_line, 128); } while( !EQUAL_SUB_STR( "*/")); } // If key-word is "DIRECTORY", remember it and add "\" character at the end of new path // if it is not yet there else if( EQUAL_SUB_STR( "DIRECTORY")) { // !!! FIXME : rcg10092001 You can't uppercase filenames! //_strupr( ld_line); sscanf( ld_line, "DIRECTORY %s", base_path); // !!! FIXME : rcg10092001 Use CFileSystem::GetDirSeparator(). if( base_path[ strlen( base_path) - 1] != '\\') strcat( base_path,"\\"); } // Key-word animation must follow its name (in same line), // its speed and its number of frames (new lines) else if( EQUAL_SUB_STR( "ANIMATION")) { if( strlen( ld_line) <= (strlen( "ANIMATION") + 1)) { throw("You have to give descriptive name to every animation."); } // Create new animation COneAnim *poaOneAnim = new COneAnim; _strupr( ld_line); sscanf( ld_line, "ANIMATION %s", poaOneAnim->oa_Name); File->GetLine_t(ld_line, 128); if( !EQUAL_SUB_STR( "SPEED")) { throw("Expecting key word \"SPEED\" after key word \"ANIMATION\"."); } _strupr( ld_line); sscanf( ld_line, "SPEED %f", &poaOneAnim->oa_SecsPerFrame); CDynamicArray astrFrames; SLONG slLastPos; FOREVER { slLastPos = File->GetPos_t(); File->GetLine_t(ld_line, 128); _strupr( ld_line); // jump over old key word "FRAMES" and comments if( EQUAL_SUB_STR( "FRAMES") || (ld_line[0]==';') ) continue; // key words that start or end animations or empty line breaks frame reading if( (EQUAL_SUB_STR( "ANIMATION")) || (strlen( ld_line)== 0) || (EQUAL_SUB_STR( "ANIM_END")) ) break; sscanf( ld_line, "%s", key_word); if( key_word == CTString( "ANIM")) { // read file name from line and add it at the end of last path string loaded sscanf( ld_line, "%s %s", error_str, anim_name); // search trough all allready readed animations for macro one FOREACHINLIST(COneAnimNode, coan_Node, TempAnimationList, itOAN) { if( itOAN->coan_OneAnim->oa_Name == CTString( anim_name)) { CTString *pstrMacroFrames = astrFrames.New( itOAN->coan_OneAnim->oa_NumberOfFrames); for( INDEX iMacroFrame = 0; iMacroFramecoan_OneAnim->oa_NumberOfFrames; iMacroFrame++) { *pstrMacroFrames = GetFrameFileName( pFrameFileList, itOAN->coan_OneAnim->oa_FrameIndices[iMacroFrame]); pstrMacroFrames++; } } } } else { // read file name from line and add it at the end of last path string loaded sscanf( ld_line, "%s", file_name); sprintf( full_path, "%s%s", base_path, file_name); CTString *pstrNewFile = astrFrames.New(1); *pstrNewFile = CTString( full_path); } } if( astrFrames.Count() == 0) { ThrowF_t( "Can't find any frames for animation %s.\nThere must be at least 1 frame " "per animation.\nList of frames must start at line after line containing key" "word SPEED.", poaOneAnim->oa_Name); } // set position before last line readed File->SetPos_t( slLastPos); // Allocate array of indices poaOneAnim->oa_NumberOfFrames = astrFrames.Count(); poaOneAnim->oa_FrameIndices = (INDEX *) AllocMemory( poaOneAnim->oa_NumberOfFrames * sizeof( INDEX)); INDEX iFrame = 0; FOREACHINDYNAMICARRAY( astrFrames, CTString, itStrFrame) { // find existing index (of insert new one) for this file name into FileNameList poaOneAnim->oa_FrameIndices[ iFrame] = FindFrameIndex( pFrameFileList, *itStrFrame); iFrame++; } // clear used array astrFrames.Clear(); // Add this new animation instance to temporary animation list new COneAnimNode( poaOneAnim, &TempAnimationList); ad_NumberOfAnims ++; } else if( EQUAL_SUB_STR( "ANIM_END")) break; else { sprintf(error_str, "Incorrect word readed from script file.\n"); strcat(error_str, "Probable cause: missing \"ANIM_END\" key-word at end of animation list."); throw(error_str); } } lc = TempAnimationList.Count(); ASSERT(lc!=0); // create array of OneAnim object containing members as many as temporary list ad_Anims = new COneAnim[ lc]; // copy list to array lc=0; FOREACHINLIST(COneAnimNode, coan_Node, TempAnimationList, it2) { strcpy( ad_Anims[ lc].oa_Name, it2->coan_OneAnim->oa_Name); ad_Anims[ lc].oa_SecsPerFrame = it2->coan_OneAnim->oa_SecsPerFrame; ad_Anims[ lc].oa_NumberOfFrames = it2->coan_OneAnim->oa_NumberOfFrames; ad_Anims[ lc].oa_FrameIndices = (INDEX *) AllocMemory( ad_Anims[ lc].oa_NumberOfFrames * sizeof(INDEX)); for( i=0; icoan_OneAnim->oa_NumberOfFrames; i++) ad_Anims[ lc].oa_FrameIndices[ i] = it2->coan_OneAnim->oa_FrameIndices[ i]; lc++; } FORDELETELIST( COneAnimNode, coan_Node, TempAnimationList, litDel) delete &litDel.Current(); } void CAnimData::Write_t( CTStream *ostrFile) // throw char * { SLONG i; // First we save main ID ostrFile->WriteID_t( CChunkID( "ADAT")); // Then we save number of how many animations do we have and then save them all ostrFile->Write_t( &ad_NumberOfAnims, sizeof( INDEX)); for( i=0; iWrite_t( &ad_Anims[i].oa_Name, sizeof( NAME)); ostrFile->Write_t( &ad_Anims[i].oa_SecsPerFrame, sizeof( TIME)); ostrFile->Write_t( &ad_Anims[i].oa_NumberOfFrames, sizeof( INDEX)); ostrFile->Write_t( ad_Anims[i].oa_FrameIndices, ad_Anims[i].oa_NumberOfFrames * sizeof( INDEX)); } } // print #define lines for all animations into given file void CAnimData::ExportAnimationNames_t( CTStream *ostrFile, CTString strAnimationPrefix) // throw char * { char chrLine[ 256]; // for each animation for( INDEX iAnimation=0; iAnimationPutLine_t( chrLine); } } // Get info about some animation void CAnimData::GetAnimInfo(INDEX iAnimNo, CAnimInfo &aiInfo) const { if(iAnimNo>=ad_NumberOfAnims) { iAnimNo = 0; } strcpy( aiInfo.ai_AnimName, ad_Anims[ iAnimNo].oa_Name); aiInfo.ai_SecsPerFrame = ad_Anims[ iAnimNo].oa_SecsPerFrame; aiInfo.ai_NumberOfFrames = ad_Anims[ iAnimNo].oa_NumberOfFrames; } // Add animation void CAnimData::AddAnimation(void) { COneAnim *pNewAnims = new COneAnim[ ad_NumberOfAnims+1]; for( INDEX iOldAnim=0; iOldAnimExpectID_t( CChunkID( "ADAT")); // Then we load and create number of animations (*istrFile)>>ad_NumberOfAnims; ad_Anims = new COneAnim[ ad_NumberOfAnims]; for( i=0; iRead_t(ad_Anims[i].oa_Name, sizeof (ad_Anims[i].oa_Name)); // char[32] (*istrFile)>>ad_Anims[i].oa_SecsPerFrame; (*istrFile)>>ad_Anims[i].oa_NumberOfFrames; ad_Anims[i].oa_FrameIndices = (INDEX *) AllocMemory( ad_Anims[i].oa_NumberOfFrames * sizeof( INDEX)); for (SLONG j = 0; j < ad_Anims[i].oa_NumberOfFrames; j++) (*istrFile)>>ad_Anims[i].oa_FrameIndices[j]; } } /* * Default constructor. */ CAnimObject::CAnimObject(void) { // set invalid data for validation check ao_AnimData = NULL; ao_tmAnimStart = 0.0f; ao_iCurrentAnim = -1; ao_iLastAnim = -1; ao_ulFlags = AOF_PAUSED; } /* Destructor. */ CAnimObject::~CAnimObject(void) { ao_AnimData->RemReference(); } // copy from another object of same class ENGINE_API void CAnimObject::Copy(CAnimObject &aoOther) { SetData(aoOther.GetData()); ao_tmAnimStart = aoOther.ao_tmAnimStart; ao_iCurrentAnim = aoOther.ao_iCurrentAnim; ao_iLastAnim = aoOther.ao_iLastAnim; ao_ulFlags = aoOther.ao_ulFlags; } // synchronize with another animation object (set same anim and frames) ENGINE_API void CAnimObject::Synchronize(CAnimObject &aoOther) { // copy animations, time and flags INDEX ctAnims = GetAnimsCt(); ao_tmAnimStart = aoOther.ao_tmAnimStart; ao_iCurrentAnim = ClampUp(aoOther.ao_iCurrentAnim, ctAnims-1L); ao_iLastAnim = ClampUp(aoOther.ao_iLastAnim, ctAnims-1L); ao_ulFlags = aoOther.ao_ulFlags; } /* * Get animation's length. */ FLOAT CAnimObject::GetAnimLength(INDEX iAnim) const { if(ao_AnimData == NULL) return 1.0f; ASSERT( ao_AnimData != NULL); if(iAnim>=ao_AnimData->ad_NumberOfAnims) { iAnim = 0; } ASSERT( (iAnim >= 0) && (iAnim < ao_AnimData->ad_NumberOfAnims) ); COneAnim *pCOA = &ao_AnimData->ad_Anims[iAnim]; return pCOA->oa_NumberOfFrames*pCOA->oa_SecsPerFrame; } FLOAT CAnimObject::GetCurrentAnimLength(void) const { return GetAnimLength(ao_iCurrentAnim); } /* * Calculate frame that coresponds to given time. */ INDEX CAnimObject::FrameInTime(TIME time) const { ASSERT( ao_AnimData != NULL); ASSERT( (ao_iCurrentAnim >= 0) && (ao_iCurrentAnim < ao_AnimData->ad_NumberOfAnims) ); INDEX iFrameInAnim; COneAnim *pCOA = &ao_AnimData->ad_Anims[ao_iCurrentAnim]; if( ao_ulFlags&AOF_PAUSED) { // return index of paused frame inside global frame array iFrameInAnim = ClipFrame(pCOA->oa_NumberOfFrames + ClipFrame( FloatToInt(ao_tmAnimStart/pCOA->oa_SecsPerFrame))); } else { // return index of frame inside global frame array of frames in given moment iFrameInAnim = ClipFrame( FloatToInt((time - ao_tmAnimStart)/pCOA->oa_SecsPerFrame)); } return pCOA->oa_FrameIndices[iFrameInAnim]; } /* * Pauses animation */ void CAnimObject::PauseAnim(void) { if( ao_ulFlags&AOF_PAUSED) return; // dont pause twice ao_ulFlags |= AOF_PAUSED; ao_tmAnimStart = _pTimer->CurrentTick() - ao_tmAnimStart; // set difference from current time as start time, MarkChanged(); // so get frame will get correct current frame } /* * Continues paused animation */ void CAnimObject::ContinueAnim(void) { if( !(ao_ulFlags&AOF_PAUSED)) return; // calculate freezed frame index inside current animation (not in global list of frames!) COneAnim *pCOA = &ao_AnimData->ad_Anims[ao_iCurrentAnim]; if (pCOA->oa_NumberOfFrames<=0) { return; } INDEX iStoppedFrame = (pCOA->oa_NumberOfFrames + (SLONG)(ao_tmAnimStart/pCOA->oa_SecsPerFrame) % pCOA->oa_NumberOfFrames) % pCOA->oa_NumberOfFrames; // using current frame index calculate time so animation continues from same frame ao_tmAnimStart = _pTimer->CurrentTick() - pCOA->oa_SecsPerFrame * iStoppedFrame; ao_ulFlags &= ~AOF_PAUSED; MarkChanged(); } /* * Offsets the animation phase */ void CAnimObject::OffsetPhase(TIME tm) { ao_tmAnimStart += tm; } /* * Loop anims forward */ void CAnimObject::NextAnim() { ASSERT( ao_iCurrentAnim != -1); ASSERT( ao_AnimData != NULL); ao_iCurrentAnim = (ao_iCurrentAnim + 1) % ao_AnimData->ad_NumberOfAnims; ao_iLastAnim = ao_iCurrentAnim; ao_tmAnimStart = _pTimer->CurrentTick(); MarkChanged(); } /* * Loop anims backward */ void CAnimObject::PrevAnim() { ASSERT( ao_iCurrentAnim != -1); ASSERT( ao_AnimData != NULL); ao_iCurrentAnim = (ao_AnimData->ad_NumberOfAnims + ao_iCurrentAnim - 1) % ao_AnimData->ad_NumberOfAnims; ao_iLastAnim = ao_iCurrentAnim; ao_tmAnimStart = _pTimer->CurrentTick(); MarkChanged(); } /* * Selects frame for given time offset from animation start (0) */ void CAnimObject::SelectFrameInTime(TIME tmOffset) { ao_tmAnimStart = tmOffset; // set fixed start time MarkChanged(); } void CAnimObject::FirstFrame(void) { SelectFrameInTime(0.0f); } void CAnimObject::LastFrame(void) { class COneAnim *pCOA = &ao_AnimData->ad_Anims[ao_iCurrentAnim]; SelectFrameInTime(GetAnimLength(ao_iCurrentAnim)-pCOA->oa_SecsPerFrame); } /* * Loop frames forward */ void CAnimObject::NextFrame() { ASSERT( ao_iCurrentAnim != -1); ASSERT( ao_AnimData != NULL); ASSERT( ao_ulFlags&AOF_PAUSED); ao_tmAnimStart += ao_AnimData->ad_Anims[ ao_iCurrentAnim].oa_SecsPerFrame; MarkChanged(); } /* * Loop frames backward */ void CAnimObject::PrevFrame() { ASSERT( ao_iCurrentAnim != -1); ASSERT( ao_AnimData != NULL); ASSERT( ao_ulFlags&AOF_PAUSED); ao_tmAnimStart -= ao_AnimData->ad_Anims[ ao_iCurrentAnim].oa_SecsPerFrame; MarkChanged(); } /* * Retrieves paused flag */ BOOL CAnimObject::IsPaused() { return ao_ulFlags&AOF_PAUSED; } /* * Test if some updateable object is up to date with this anim object. */ BOOL CAnimObject::IsUpToDate(const CUpdateable &ud) const { // if the object itself has changed, or its data has changed if (!CChangeable::IsUpToDate(ud) || !ao_AnimData->IsUpToDate(ud)) { // something has changed return FALSE; } // otherwise, nothing has changed return TRUE; } /* * Attach data to this object. */ void CAnimObject::SetData(CAnimData *pAD) { // mark new data as referenced once more pAD->AddReference(); // mark old data as referenced once less ao_AnimData->RemReference(); // remember new data ao_AnimData = pAD; if( pAD != NULL) StartAnim( 0); // mark that something has changed MarkChanged(); } // obtain anim and set it for this object void CAnimObject::SetData_t(const CTFileName &fnmAnim) // throw char * { // if the filename is empty if (fnmAnim=="") { // release current anim SetData(NULL); // if the filename is not empty } else { // obtain it (adds one reference) CAnimData *pad = _pAnimStock->Obtain_t(fnmAnim); // set it as data (adds one more reference, and remove old reference) SetData(pad); // release it (removes one reference) _pAnimStock->Release(pad); // total reference count +1+1-1 = +1 for new data -1 for old data } } /* * Sets new animation (but doesn't starts it). */ void CAnimObject::SetAnim(INDEX iNew) { if(ao_AnimData == NULL) return; // clamp animation if( iNew >= GetAnimsCt() ) { iNew = 0; } // if new animation if (ao_iCurrentAnim!=iNew) { // remember starting time ao_tmAnimStart = _pTimer->CurrentTick(); } // set new animation number ao_iCurrentAnim=iNew; ao_iLastAnim=iNew; // mark that something has changed MarkChanged(); } /* * Start new animation. */ void CAnimObject::StartAnim(INDEX iNew) { if(ao_AnimData == NULL) return; // set new animation SetAnim( iNew); // set pause off, looping on ao_ulFlags = AOF_LOOPING; } /* Start playing an animation. */ void CAnimObject::PlayAnim(INDEX iNew, ULONG ulFlags) { if(ao_AnimData == NULL) return; // clamp animation if( iNew >= GetAnimsCt() ) { iNew = 0; } // if anim needs to be reset at start if (!(ulFlags&AOF_NORESTART) || ao_iCurrentAnim!=iNew) { // if smooth transition if (ulFlags&AOF_SMOOTHCHANGE) { // calculate time to end of the current anim class COneAnim *pCOA = &ao_AnimData->ad_Anims[ao_iCurrentAnim]; TIME tmNow = _pTimer->CurrentTick(); TIME tmLength = GetCurrentAnimLength(); FLOAT fFrame = ((_pTimer->CurrentTick() - ao_tmAnimStart)/pCOA->oa_SecsPerFrame); INDEX iFrame = INDEX(fFrame); FLOAT fFract = fFrame-iFrame; iFrame = ClipFrame(iFrame); TIME tmPassed = (iFrame+fFract)*pCOA->oa_SecsPerFrame; TIME tmLeft = tmLength-tmPassed; // set time ahead to end of the current animation ao_tmAnimStart = _pTimer->CurrentTick()+tmLeft; // remember last animation ao_iLastAnim = ao_iCurrentAnim; // set new animation number ao_iCurrentAnim = iNew; // if normal transition } else { ao_iLastAnim = iNew; ao_iCurrentAnim = iNew; // remember starting time ao_tmAnimStart = _pTimer->CurrentTick(); } // if anim doesn't need be reset at start } else { // do nothing NOTHING; } // set pause off, looping flag from flags ao_ulFlags = ulFlags&(AOF_LOOPING|AOF_PAUSED); // mark that something has changed MarkChanged(); } /* Seamlessly continue playing another animation from same point. */ void CAnimObject::SwitchToAnim(INDEX iNew) { if(ao_AnimData == NULL) return; // clamp animation if( iNew >= GetAnimsCt() ) { iNew = 0; } // set new animation number ao_iCurrentAnim=iNew; ao_iLastAnim = ao_iCurrentAnim; } /* * Reset anim (restart) */ void CAnimObject::ResetAnim() { if(ao_AnimData == NULL) return; // remember starting time ao_tmAnimStart = _pTimer->CurrentTick(); // mark that something has changed MarkChanged(); } // Get info about some animation void CAnimObject::GetAnimInfo(INDEX iAnimNo, CAnimInfo &aiInfo) const { if (iAnimNo >= ao_AnimData->ad_NumberOfAnims) { iAnimNo = 0; } ASSERT( iAnimNo < ao_AnimData->ad_NumberOfAnims); strcpy( aiInfo.ai_AnimName, ao_AnimData->ad_Anims[ iAnimNo].oa_Name); aiInfo.ai_SecsPerFrame = ao_AnimData->ad_Anims[ iAnimNo].oa_SecsPerFrame; aiInfo.ai_NumberOfFrames = ao_AnimData->ad_Anims[ iAnimNo].oa_NumberOfFrames; } // clip frame index to be inside valid range (wrap around for looping anims) INDEX CAnimObject::ClipFrame(INDEX iFrame) const { if (ao_AnimData->ad_NumberOfAnims==0) { return 0; } class COneAnim *pCOA = &ao_AnimData->ad_Anims[ao_iCurrentAnim]; // if looping if (ao_ulFlags&AOF_LOOPING) { // wrap-around if (pCOA->oa_NumberOfFrames<=0) { return 0; } return ULONG(iFrame)%pCOA->oa_NumberOfFrames; // if not looping } else { // clamp if (iFrame<0) { return 0; } else if (iFrame>=pCOA->oa_NumberOfFrames) { return pCOA->oa_NumberOfFrames-1; } else { return iFrame; } } } // Get info about time passed until now in current animation TIME CAnimObject::GetPassedTime(void) const { if(ao_AnimData == NULL) return 0.0f; INDEX iStoppedFrame; class COneAnim *pCOA = &ao_AnimData->ad_Anims[ao_iCurrentAnim]; if( !(ao_ulFlags&AOF_PAUSED)) iStoppedFrame = ClipFrame((INDEX)((_pTimer->CurrentTick() - ao_tmAnimStart)/pCOA->oa_SecsPerFrame)); else iStoppedFrame = ClipFrame((INDEX)(ao_tmAnimStart/pCOA->oa_SecsPerFrame)); return( iStoppedFrame * pCOA->oa_SecsPerFrame); } /* * If animation is finished */ BOOL CAnimObject::IsAnimFinished(void) const { if(ao_AnimData == NULL) return FALSE; if(ao_ulFlags&AOF_LOOPING) return FALSE; INDEX iStoppedFrame; class COneAnim *pCOA = &ao_AnimData->ad_Anims[ao_iCurrentAnim]; if( !(ao_ulFlags&AOF_PAUSED)) iStoppedFrame = ClipFrame((INDEX)((_pTimer->CurrentTick() - ao_tmAnimStart)/pCOA->oa_SecsPerFrame)); else iStoppedFrame = ClipFrame((INDEX)(ao_tmAnimStart/pCOA->oa_SecsPerFrame)); return( iStoppedFrame == pCOA->oa_NumberOfFrames-1); } // get number of animations in curent anim data INDEX CAnimObject::GetAnimsCt(void) const { if(ao_AnimData == NULL) return 1; ASSERT( ao_AnimData != NULL); return( ao_AnimData->ad_NumberOfAnims); } // get index of current animation INDEX CAnimObject::GetAnim(void) const { return( ao_iCurrentAnim); } /* * Gets the number of current frame. */ INDEX CAnimObject::GetFrame(void) const { return FrameInTime(_pTimer->CurrentTick()); // return frame index that coresponds to current moment } /* Gets number of frames in current anim. */ INDEX CAnimObject::GetFramesInCurrentAnim(void) const { ASSERT( ao_AnimData != NULL); return ao_AnimData->ad_Anims[ao_iCurrentAnim].oa_NumberOfFrames; } /* * Get information for linear interpolation beetween frames. */ void CAnimObject::GetFrame( INDEX &iFrame0, INDEX &iFrame1, FLOAT &fRatio) const { if(ao_AnimData == NULL || ao_AnimData->ad_NumberOfAnims<=0 || ao_AnimData->ad_Anims[ao_iCurrentAnim].oa_NumberOfFrames<=0) { iFrame0 = 0; iFrame1 = 0; fRatio =0.0f; return; } ASSERT( ao_AnimData != NULL); ASSERT( (ao_iCurrentAnim >= 0) && (ao_iCurrentAnim < ao_AnimData->ad_NumberOfAnims) ); TIME tmNow = _pTimer->CurrentTick() + _pTimer->GetLerpFactor()*_pTimer->TickQuantum; if( ao_ulFlags&AOF_PAUSED) { // return index of paused frame inside global frame array class COneAnim *pCOA = &ao_AnimData->ad_Anims[ao_iCurrentAnim]; INDEX iStoppedFrame = ClipFrame((SLONG)(ao_tmAnimStart/pCOA->oa_SecsPerFrame)); iFrame0 = iFrame1 = pCOA->oa_FrameIndices[ iStoppedFrame]; fRatio = 0.0f; } else { // return index of frame inside global frame array of frames in given moment TIME tmCurrentRelative = tmNow - ao_tmAnimStart; if (tmCurrentRelative>=0) { class COneAnim *pOA0 = &ao_AnimData->ad_Anims[ao_iCurrentAnim]; float fFrameNow = (tmCurrentRelative)/pOA0->oa_SecsPerFrame; iFrame0 = pOA0->oa_FrameIndices[ ClipFrame(ULONG(fFrameNow))]; iFrame1 = pOA0->oa_FrameIndices[ ClipFrame(ULONG(fFrameNow+1))]; fRatio = fFrameNow - (float)floor(fFrameNow); } else { class COneAnim *pOA0 = &ao_AnimData->ad_Anims[ao_iLastAnim]; class COneAnim *pOA1 = &ao_AnimData->ad_Anims[ao_iCurrentAnim]; INDEX iAnim = ao_iCurrentAnim; ((CAnimObject*)this)->ao_iCurrentAnim = ao_iLastAnim; float fFrameNow = tmCurrentRelative/pOA0->oa_SecsPerFrame+pOA0->oa_NumberOfFrames; iFrame0 = pOA0->oa_FrameIndices[ Clamp(SLONG(fFrameNow), 0L, pOA0->oa_NumberOfFrames-1L)]; INDEX iFrameNext = SLONG(fFrameNow+1); if (iFrameNext>=pOA0->oa_NumberOfFrames) { iFrame1 = pOA1->oa_FrameIndices[0]; } else { iFrame1 = pOA0->oa_FrameIndices[ Clamp(iFrameNext, 0L, pOA0->oa_NumberOfFrames-1L)]; } ((CAnimObject*)this)->ao_iCurrentAnim = iAnim; fRatio = fFrameNow - (float)floor(fFrameNow); } } } void CAnimObject::Write_t( CTStream *pstr) // throw char * { (*pstr).WriteID_t("ANOB"); (*pstr)<>ao_tmAnimStart; (*pstr)>>ao_iCurrentAnim; (*pstr)>>ao_iLastAnim; (*pstr)>>ao_ulFlags; } else { (*pstr)>>ao_tmAnimStart; (*pstr)>>ao_iCurrentAnim; ao_iLastAnim = ao_iCurrentAnim; ao_ulFlags = 0; } // clamp animation if (ao_AnimData==NULL || ao_iCurrentAnim >= GetAnimsCt() ) { ao_iCurrentAnim = 0; } // clamp animation if (ao_AnimData==NULL || ao_iLastAnim >= GetAnimsCt() ) { ao_iLastAnim = 0; } }