X-Git-Url: https://git.dogcows.com/gitweb?p=chaz%2Fyoink;a=blobdiff_plain;f=src%2FAnimation.cc;fp=src%2FAnimation.cc;h=b449a0fbd97a500e36cd10e987831117fee4bf08;hp=0000000000000000000000000000000000000000;hb=23d8f7a5fbd1eca7f46f2342c20ac5e28ae0128a;hpb=fa9438c66ae0154e6d9ad196e0fb39649d359da4 diff --git a/src/Animation.cc b/src/Animation.cc new file mode 100644 index 0000000..b449a0f --- /dev/null +++ b/src/Animation.cc @@ -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 +#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 +{ + 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 + { + friend class Impl; + friend class Mf::Mippleton; + + /** + * 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 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(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(name) + { + loadFromFile(); + } + + std::map 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::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; ///< 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: *************************************************/ +