]> Dogcows Code - chaz/yoink/blob - src/Animation.cc
477997037230b125635f28cf4980d8ffa3d3cc3a
[chaz/yoink] / src / Animation.cc
1
2 /*******************************************************************************
3
4 Copyright (c) 2009, Charles McGarvey
5 All rights reserved.
6
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
9
10 * Redistributions of source code must retain the above copyright notice,
11 this list of conditions and the following disclaimer.
12 * Redistributions in binary form must reproduce the above copyright notice,
13 this list of conditions and the following disclaimer in the documentation
14 and/or other materials provided with the distribution.
15
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
20 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
27 *******************************************************************************/
28
29 #include <map>
30 #include <vector>
31
32 #include <Moof/Manager.hh>
33 #include <Moof/Log.hh>
34 #include <Moof/Script.hh>
35
36 #include "Animation.hh"
37
38
39 /**
40 * The collection of nested animation classes. The animation implementation
41 * consists of an Impl class which is allocated and initialized with the
42 * interface object. This class contains the specific fields which are required
43 * to run a single instance of an animation. The sequence data is loaded in a
44 * different class which can be shared amongst multiple animation implementation
45 * instances.
46 */
47
48 class Animation::Impl
49 {
50 public:
51
52 /**
53 * Contains "global" animation data for the various animations which get
54 * loaded. This is a mippleton, so it will be shared amongst any animation
55 * which wants to use these loaded sequences.
56 */
57
58 class Data : public Mf::Manager<Data>
59 {
60 public:
61
62 /**
63 * A frame of an animation sequence. A frame is merely an index which
64 * presumably represents a "slide" or tile which should be displayed,
65 * and the duration that is how long the slide will be shown.
66 */
67
68 class Frame
69 {
70 public:
71
72 unsigned mIndex; ///< Frame index.
73 Mf::Scalar mDuration; ///< Frame duration.
74
75 /**
76 * Construction is initialization. The frame data is loaded from a
77 * frame map which is probably loaded within an animation file.
78 */
79
80 Frame(Mf::Script& script, Mf::Script::Slot table) :
81 mIndex(0),
82 mDuration(1.0)
83 {
84 table.pushField("index");
85 script[-1].get(mIndex);
86 script.pop();
87
88 table.pushField("duration");
89 script[-1].get(mDuration);
90 script.pop();
91 }
92 };
93
94
95 /**
96 * A sequence is just a few attributes and a list of frames in the order
97 * that they should be played.
98 */
99
100 class Sequence
101 {
102 public:
103
104 std::vector<Frame> mFrames; ///< List of frames.
105 Mf::Scalar mDelay; ///< Scale frame durations.
106 bool mLoop; ///< Does the sequence repeat?
107 std::string mNext; ///< Next sequence name.
108
109 /**
110 * Construction is initialization. The constructor loads sequence
111 * data from the sequence map, presumably loaded from an animation
112 * file. The rest of the loading takes place in the frame's
113 * constructor which loads each individual frame.
114 */
115
116 Sequence(Mf::Script& script, Mf::Script::Slot table) :
117 mDelay(0.0),
118 mLoop(true)
119 {
120 table.pushField("delay");
121 script[-1].get(mDelay);
122 script.pop();
123
124 table.pushField("loop");
125 script[-1].get(mLoop);
126 script.pop();
127
128 table.pushField("next");
129 script[-1].get(mNext);
130 script.pop();
131
132 // TODO - sequence class/type not yet implemented
133
134 table.pushField("frames");
135 Mf::Script::Slot frameTable = script.getTop();
136 if (frameTable.isTable())
137 {
138 Mf::Script::Slot top = script[-1];
139 int index = 1;
140
141 for (;;)
142 {
143 script.push(index);
144 frameTable.pushField();
145
146 if (top.isTable()) mFrames.push_back(Frame(script, top));
147 else break;
148
149 ++index;
150 }
151 }
152 script.pop();
153 }
154 };
155
156
157 /**
158 * Starts loading a file with animation data. Such a file is formatted
159 * as a map of named sequences. The sequence constructor loads each
160 * individual sequence.
161 */
162
163 void init(const std::string& name)
164 {
165 Mf::Script script;
166 std::string filePath = Animation::getPath(name);
167
168 script.importBaseLibrary();
169 importLogFunctions(script);
170 importAnimationBindings(script);
171
172 if (script.doFile(filePath) != Mf::Script::SUCCESS)
173 {
174 std::string str;
175 script[-1].get(str);
176 Mf::logWarning(str);
177 }
178 }
179
180 int defineSequence(Mf::Script& script)
181 {
182 Mf::Script::Slot name = script[1].requireString();
183 Mf::Script::Slot table = script[2].requireTable();
184
185 std::string nameStr;
186 name.get(nameStr);
187
188 mSequences.insert(std::pair<std::string,Sequence>(nameStr,
189 Sequence(script, table)));
190
191 return 0;
192 }
193
194
195 void importAnimationBindings(Mf::Script& script)
196 {
197 script.importFunction("DefineSequence",
198 boost::bind(&Data::defineSequence, this, _1));
199
200 script.push(1); script.set("ATTACK");
201 script.push(2); script.set("CHARGE");
202 script.push(3); script.set("FLY");
203 script.push(4); script.set("HIT");
204 script.push(5); script.set("JUMP");
205 script.push(6); script.set("RUN");
206 script.push(7); script.set("STAND");
207 }
208
209
210 std::map<std::string,Sequence> mSequences; ///< List of sequences.
211 };
212
213
214 /**
215 * Construction is intialization.
216 */
217
218 Impl(const std::string& name) :
219 mData(Data::getInstance(name)),
220 mCurrentSequence(0),
221 mFrameCounter(0),
222 mFrameIndex(0),
223 mTimeAccum(0),
224 mFrameDuration(0) {}
225
226
227 /**
228 * Sets up the animation classes to "play" a named sequence. If another
229 * sequence was active, it will be replaced. Future updates will progress
230 * the new sequence.
231 */
232
233 void startSequence(const std::string& name)
234 {
235 std::map<std::string,Data::Sequence>::iterator it;
236
237 it = mData->mSequences.find(name);
238
239 if (it != mData->mSequences.end())
240 {
241 mCurrentSequence = &(*it).second;
242 mFrameCounter = 0;
243 mFrameIndex = mCurrentSequence->mFrames[0].mIndex;
244 mTimeAccum = 0.0;
245 mFrameDuration = mCurrentSequence->mDelay *
246 mCurrentSequence->mFrames[0].mDuration;
247 }
248 }
249
250 /**
251 * Updates or progresses the animation sequence. If the time interval
252 * surpasses the duration of the current frame, a new frame becomes the
253 * current frame. If the last frame of a sequence expires, the active
254 * sequence will switch automatically to the designated "next" sequence, or
255 * if none is specified but the sequence is set to loop, the first frame of
256 * the sequence will become the current frame, and the animation essentially
257 * starts over again.
258 */
259
260 void update(Mf::Scalar t, Mf::Scalar dt)
261 {
262 if (mCurrentSequence)
263 {
264 mTimeAccum += dt;
265
266 if (mTimeAccum >= mFrameDuration)
267 {
268 if (++mFrameCounter >= mCurrentSequence->mFrames.size())
269 {
270 if (!mCurrentSequence->mNext.empty())
271 {
272 startSequence(mCurrentSequence->mNext);
273 }
274 else if (mCurrentSequence->mLoop)
275 {
276 mFrameCounter = 0;
277 }
278 else
279 {
280 mFrameCounter--;
281 mCurrentSequence = 0;
282 }
283 }
284
285 mFrameIndex = mCurrentSequence->mFrames[mFrameCounter].mIndex;
286 mTimeAccum = mFrameDuration - mTimeAccum;
287 mFrameDuration = mCurrentSequence->mDelay *
288 mCurrentSequence->mFrames[mFrameCounter].mDuration;
289 }
290 }
291 }
292
293 boost::shared_ptr<Data> mData; ///< Internal data.
294
295 Data::Sequence* mCurrentSequence; ///< Active sequence.
296 unsigned mFrameCounter; ///< Current frame.
297 unsigned mFrameIndex; ///< Index of current frame.
298 Mf::Scalar mTimeAccum; ///< Time accumulation.
299 Mf::Scalar mFrameDuration; ///< Scaled frame duration.
300 };
301
302
303 Animation::Animation(const std::string& name) :
304 // pass through
305 mImpl(new Animation::Impl(name)) {}
306
307
308 void Animation::startSequence(const std::string& name)
309 {
310 // pass through
311 mImpl->startSequence(name);
312 }
313
314 void Animation::update(Mf::Scalar t, Mf::Scalar dt)
315 {
316 // pass through
317 mImpl->update(t, dt);
318 }
319
320
321 /**
322 * Gets the index for the current frame. This is presumably called by some
323 * drawing code which will draw the correct current frame.
324 */
325
326 unsigned Animation::getFrame() const
327 {
328 return mImpl->mFrameIndex;
329 }
330
331
332 /**
333 * Specialized search location for animation files. They can be found in the
334 * "animations" subdirectory of any of the searched directories.
335 */
336
337 std::string Animation::getPath(const std::string& name)
338 {
339 return Mf::Resource::getPath("animations/" + name + ".lua");
340 }
341
342
343 /** vim: set ts=4 sw=4 tw=80: *************************************************/
344
This page took 0.045658 seconds and 3 git commands to generate.