/*] Copyright (c) 2009-2010, Charles McGarvey [************************** **] All rights reserved. * * vi:ts=4 sw=4 tw=75 * * Distributable under the terms and conditions of the 2-clause BSD license; * see the file COPYING for a complete text of the license. * **************************************************************************/ #include #include #include #include #include #include #include "Animation.hh" /** * The collection of nested animation classes. The animation * implementation consists of an Impl class which is allocated and * initialized with the interface object. This class contains the specific * fields which are required to run a single instance of an animation. The * sequence data is loaded in a different class which can be shared amongst * multiple animation implementation instances. */ class Animation::Impl { public: /** * Contains "global" animation data for the various animations which * get loaded. This is a mippleton, so it will be shared amongst any * animation which wants to use these loaded sequences. */ class Data : public Mf::Manager { public: /** * A frame of an animation sequence. A frame is merely an index * which presumably represents a "slide" or tile which should be * displayed, and the duration that is how long the slide will be * shown. */ class Frame { public: unsigned mIndex; ///< Frame index. Mf::Scalar mDuration; ///< Frame duration. /** * Construction is initialization. The frame data is loaded * from a frame map which is probably loaded within an * animation file. */ Frame(const Mf::Script::Slot& table) : mIndex(0), mDuration(1.0) { table.get(mIndex, "index"); table.get(mDuration, "duration"); } }; /** * A sequence is just a few attributes and a list of frames in the * order that they should be played. */ class Sequence { public: std::vector mFrames; ///< List of frames. Mf::Scalar mDelay; ///< Scale frame durations. bool mLoop; ///< Does the sequence repeat? std::string mNext; ///< Next sequence name. /** * Construction is initialization. The constructor loads * sequence data from the sequence map, presumably loaded from * an animation file. The rest of the loading takes place in * the frame's constructor which loads each individual frame. */ Sequence(const Mf::Script::Slot& table) : mDelay(0.0), mLoop(true) { table.get(mDelay, "delay"); table.get(mLoop, "loop"); table.get(mNext, "next"); // TODO - sequence class/type not yet implemented Mf::Script::Slot frameTable = table.pushField("frames"); if (frameTable.isTable()) { int max = frameTable.length(); for (int index = 1; index <= max; ++index) { Mf::Script::Slot top = frameTable.pushField(index); if (top.isTable()) { mFrames.push_back(Frame(top)); } else { Mf::logWarning << "invalid frame at index " << index << std::endl; } } } frameTable.remove(); } }; /** * Starts loading a file with animation data. Such a file is * formatted as a map of named sequences. The sequence * constructor loads each individual sequence. */ void init(const std::string& name) { Mf::Script script; std::string path(name); if (!Animation::getPath(path)) { Mf::Error(Mf::Error::RESOURCE_NOT_FOUND).raise(); } script.importBaseLibrary(); importLogFunctions(script); importAnimationBindings(script); if (script.doFile(path) != Mf::Script::SUCCESS) { std::string str; script[-1].get(str); Mf::logWarning(str); } } int defineSequence(Mf::Script& script) { Mf::Script::Slot name = script[1].requireString(); Mf::Script::Slot table = script[2].requireTable(); std::string nameStr; name.get(nameStr); mSequences.insert(std::make_pair(nameStr, Sequence(table))); return 0; } void importAnimationBindings(Mf::Script& script) { script.importFunction("DefineSequence", boost::bind(&Data::defineSequence, this, _1)); script.globals().setField("ATTACK", 1); script.globals().setField("CHARGE", 2); script.globals().setField("FLY", 3); script.globals().setField("HIT", 4); script.globals().setField("JUMP", 5); script.globals().setField("RUN", 6); script.globals().setField("STAND", 7); } std::map mSequences; ///< List of sequences. }; /** * Construction is intialization. */ Impl(const std::string& name) : mData(Data::getInstance(name)), mCurrentSequence(0), mFrameCounter(0), mFrameIndex(0), mTimeAccum(0), mFrameDuration(0) {} /** * Sets up the animation classes to "play" a named sequence. If * another sequence was active, it will be replaced. Future updates * will progress the new sequence. */ void startSequence(const std::string& name) { std::map::iterator it; it = mData->mSequences.find(name); if (it != mData->mSequences.end()) { mCurrentSequence = &(*it).second; mFrameCounter = 0; mFrameIndex = mCurrentSequence->mFrames[0].mIndex; mTimeAccum = 0.0; mFrameDuration = mCurrentSequence->mDelay * mCurrentSequence->mFrames[0].mDuration; } } /** * Updates or progresses the animation sequence. If the time interval * surpasses the duration of the current frame, a new frame becomes the * current frame. If the last frame of a sequence expires, the active * sequence will switch automatically to the designated "next" * sequence, or if none is specified but the sequence is set to loop, * the first frame of the sequence will become the current frame, and * the animation essentially starts over again. */ void update(Mf::Scalar t, Mf::Scalar dt) { if (!mCurrentSequence) return; mTimeAccum += dt; if (mTimeAccum >= mFrameDuration) { if (++mFrameCounter >= mCurrentSequence->mFrames.size()) { if (!mCurrentSequence->mNext.empty()) { startSequence(mCurrentSequence->mNext); } else if (mCurrentSequence->mLoop) { mFrameCounter = 0; } else { mFrameCounter--; mCurrentSequence = 0; } } mFrameIndex = mCurrentSequence->mFrames[mFrameCounter].mIndex; mTimeAccum = mFrameDuration - mTimeAccum; mFrameDuration = mCurrentSequence->mDelay * mCurrentSequence->mFrames[mFrameCounter].mDuration; } } boost::shared_ptr mData; ///< Internal data. Data::Sequence* mCurrentSequence; ///< Active sequence. unsigned mFrameCounter; ///< Current frame. unsigned mFrameIndex; ///< Index of current frame. Mf::Scalar mTimeAccum; ///< Time accumulation. Mf::Scalar mFrameDuration; ///< Scaled frame duration. }; Animation::Animation(const std::string& name) : // pass through mImpl(new Animation::Impl(name)) {} void Animation::startSequence(const std::string& name) { // pass through mImpl->startSequence(name); } void Animation::update(Mf::Scalar t, Mf::Scalar dt) { // pass through mImpl->update(t, dt); } /** * Gets the index for the current frame. This is presumably called by some * drawing code which will draw the correct current frame. */ unsigned Animation::getFrame() const { return mImpl->mFrameIndex; } /** * Specialized search location for animation files. They can be found in * the "animations" subdirectory of any of the search directories. */ bool Animation::getPath(std::string& name) { return Mf::Resource::getPath(name, "animations/", "lua"); }