/*] 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 "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(Mf::Script& script, Mf::Script::Slot table) : mIndex(0), mDuration(1.0) { table.pushField("index"); script[-1].get(mIndex); script.pop(); table.pushField("duration"); script[-1].get(mDuration); script.pop(); } }; /** * 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(Mf::Script& script, Mf::Script::Slot table) : mDelay(0.0), mLoop(true) { table.pushField("delay"); script[-1].get(mDelay); script.pop(); table.pushField("loop"); script[-1].get(mLoop); script.pop(); table.pushField("next"); script[-1].get(mNext); script.pop(); // TODO - sequence class/type not yet implemented table.pushField("frames"); Mf::Script::Slot frameTable = script.getTop(); if (frameTable.isTable()) { Mf::Script::Slot top = script[-1]; int index = 1; for (;;) { script.push(index); frameTable.pushField(); if (top.isTable()) { mFrames.push_back(Frame(script, top)); } else break; ++index; } } script.pop(); } }; /** * 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 filePath = Animation::getPath(name); script.importBaseLibrary(); importLogFunctions(script); importAnimationBindings(script); if (script.doFile(filePath) != 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(script, table))); return 0; } void importAnimationBindings(Mf::Script& script) { script.importFunction("DefineSequence", boost::bind(&Data::defineSequence, this, _1)); script.push(1); script.set("ATTACK"); script.push(2); script.set("CHARGE"); script.push(3); script.set("FLY"); script.push(4); script.set("HIT"); script.push(5); script.set("JUMP"); script.push(6); script.set("RUN"); script.push(7); script.set("STAND"); } 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. */ std::string Animation::getPath(const std::string& name) { return Mf::Resource::getPath("animations/" + name + ".lua"); }