--- /dev/null
+
+/*******************************************************************************
+
+ Copyright (c) 2009, Charles McGarvey
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*******************************************************************************/
+
+#include <map>
+#include <vector>
+
+#include <Moof/Log.hh>
+#include <Moof/Mippleton.hh>
+#include <Moof/Script.hh>
+
+#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
+{
+ friend class Animation;
+
+ /**
+ * 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::Mippleton<Data>
+ {
+ friend class Impl;
+ friend class Mf::Mippleton<Data>;
+
+ /**
+ * 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.
+ */
+
+ struct Frame
+ {
+ unsigned index; ///< Frame index.
+ Mf::Scalar duration; ///< 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::Value table) :
+ index(0),
+ duration(1.0)
+ {
+ table.pushField("index");
+ script[-1].get(index);
+ script.pop();
+
+ table.pushField("duration");
+ script[-1].get(duration);
+ script.pop();
+ }
+ };
+
+
+ /**
+ * A sequence is just a few attributes and a list of frames in the order
+ * that they should be played.
+ */
+
+ struct Sequence
+ {
+ std::vector<Frame> frames; ///< List of frames.
+ Mf::Scalar delay; ///< Scale frame durations.
+ bool loop; ///< Does the sequence repeat?
+ std::string next; ///< 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::Value table) :
+ delay(0.0),
+ loop(true)
+ {
+ table.pushField("delay");
+ script[-1].get(delay);
+ script.pop();
+
+ table.pushField("loop");
+ script[-1].get(loop);
+ script.pop();
+
+ table.pushField("next");
+ script[-1].get(next);
+ script.pop();
+
+ // TODO - sequence class/type not yet implemented
+
+ table.pushField("frames");
+ Mf::Script::Value frameTable = script.getTop();
+ if (frameTable.isTable())
+ {
+ Mf::Script::Value top = script[-1];
+ int index = 1;
+
+ for (;;)
+ {
+ script.push(index);
+ frameTable.pushField();
+
+ if (top.isTable()) frames.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 loadFromFile()
+ {
+ Mf::Script script;
+ std::string filePath = Animation::getPath(getName());
+
+ script.importStandardLibraries();
+ importLogScript(script);
+ importAnimationBindings(script);
+
+ if (script.doFile(filePath) != Mf::Script::SUCCESS)
+ {
+ std::string str;
+ script[-1].get(str);
+ Mf::logScript("%s", str.c_str());
+ }
+ }
+
+ int defineSequence(Mf::Script& script)
+ {
+ Mf::Script::Value name = script[1].requireString();
+ Mf::Script::Value table = script[2].requireTable();
+
+ std::string nameStr;
+ name.get(nameStr);
+
+ sequences.insert(std::pair<std::string,Sequence>(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");
+ }
+
+
+ /**
+ * Construction is initialization. The animation class data container
+ * registers itself as a mippleton and then loads the animation data.
+ */
+
+ explicit Data(const std::string& name) :
+ Mf::Mippleton<Data>(name)
+ {
+ loadFromFile();
+ }
+
+ std::map<std::string,Sequence> sequences; ///< List of sequences.
+ };
+
+
+ /**
+ * Construction is intialization.
+ */
+
+ Impl(const std::string& name) :
+ data(Data::getInstance(name)),
+ currentSequence(0),
+ frameCounter(0),
+ frameIndex(0),
+ timeAccum(0),
+ frameDuration(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<std::string,Data::Sequence>::iterator it;
+
+ it = data->sequences.find(name);
+
+ if (it != data->sequences.end())
+ {
+ currentSequence = &(*it).second;
+ frameCounter = 0;
+ frameIndex = currentSequence->frames[0].index;
+ timeAccum = 0.0;
+ frameDuration = currentSequence->delay *
+ currentSequence->frames[0].duration;
+ }
+ }
+
+ /**
+ * 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 (currentSequence)
+ {
+ timeAccum += dt;
+
+ if (timeAccum >= frameDuration)
+ {
+ if (++frameCounter >= currentSequence->frames.size())
+ {
+ if (!currentSequence->next.empty())
+ {
+ startSequence(currentSequence->next);
+ }
+ else if (currentSequence->loop)
+ {
+ frameCounter = 0;
+ }
+ else
+ {
+ frameCounter--;
+ currentSequence = 0;
+ }
+ }
+
+ frameIndex = currentSequence->frames[frameCounter].index;
+ timeAccum = frameDuration - timeAccum;
+ frameDuration = currentSequence->delay *
+ currentSequence->frames[frameCounter].duration;
+ }
+ }
+ }
+
+ boost::shared_ptr<Data> data; ///< Internal data.
+
+ Data::Sequence* currentSequence; ///< Active sequence.
+ unsigned frameCounter; ///< Current frame.
+ unsigned frameIndex; ///< Index of current frame.
+ Mf::Scalar timeAccum; ///< Time accumulation.
+ Mf::Scalar frameDuration; ///< Scaled frame duration.
+};
+
+
+Animation::Animation(const std::string& name) :
+ // pass through
+ impl_(new Animation::Impl(name)) {}
+
+
+void Animation::startSequence(const std::string& name)
+{
+ // pass through
+ impl_->startSequence(name);
+}
+
+void Animation::update(Mf::Scalar t, Mf::Scalar dt)
+{
+ // pass through
+ impl_->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 impl_->frameIndex;
+}
+
+
+/**
+ * Specialized search location for animation files. They can be found in the
+ * "animations" subdirectory of any of the searched directories.
+ */
+
+std::string Animation::getPath(const std::string& name)
+{
+ return Mf::Resource::getPath("animations/" + name + ".lua");
+}
+
+
+/** vim: set ts=4 sw=4 tw=80: *************************************************/
+