]> Dogcows Code - chaz/yoink/blobdiff - src/Animation.cc
converted tilemap scripts to lua
[chaz/yoink] / src / Animation.cc
diff --git a/src/Animation.cc b/src/Animation.cc
new file mode 100644 (file)
index 0000000..b449a0f
--- /dev/null
@@ -0,0 +1,352 @@
+
+/*******************************************************************************
+
+ 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: *************************************************/
+
This page took 0.026381 seconds and 4 git commands to generate.