]> Dogcows Code - chaz/yoink/blob - src/Animation.cc
port to NetBSD
[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/Library.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 friend class Animation;
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::Library<Data>
59 {
60 friend class Impl;
61 friend class Mf::Library<Data>;
62
63 /**
64 * A frame of an animation sequence. A frame is merely an index which
65 * presumably represents a "slide" or tile which should be displayed,
66 * and the duration that is how long the slide will be shown.
67 */
68
69 class Frame
70 {
71 friend class Impl;
72
73 unsigned mIndex; ///< Frame index.
74 Mf::Scalar mDuration; ///< Frame duration.
75
76 /**
77 * Construction is initialization. The frame data is loaded from a
78 * frame map which is probably loaded within an animation file.
79 */
80
81 Frame(Mf::Script& script, Mf::Script::Value table) :
82 mIndex(0),
83 mDuration(1.0)
84 {
85 table.pushField("index");
86 script[-1].get(mIndex);
87 script.pop();
88
89 table.pushField("duration");
90 script[-1].get(mDuration);
91 script.pop();
92 }
93 };
94
95
96 /**
97 * A sequence is just a few attributes and a list of frames in the order
98 * that they should be played.
99 */
100
101 struct Sequence
102 {
103 friend class Impl;
104
105 std::vector<Frame> mFrames; ///< List of frames.
106 Mf::Scalar mDelay; ///< Scale frame durations.
107 bool mLoop; ///< Does the sequence repeat?
108 std::string mNext; ///< Next sequence name.
109
110 /**
111 * Construction is initialization. The constructor loads sequence
112 * data from the sequence map, presumably loaded from an animation
113 * file. The rest of the loading takes place in the frame's
114 * constructor which loads each individual frame.
115 */
116
117 Sequence(Mf::Script& script, Mf::Script::Value table) :
118 mDelay(0.0),
119 mLoop(true)
120 {
121 table.pushField("delay");
122 script[-1].get(mDelay);
123 script.pop();
124
125 table.pushField("loop");
126 script[-1].get(mLoop);
127 script.pop();
128
129 table.pushField("next");
130 script[-1].get(mNext);
131 script.pop();
132
133 // TODO - sequence class/type not yet implemented
134
135 table.pushField("frames");
136 Mf::Script::Value frameTable = script.getTop();
137 if (frameTable.isTable())
138 {
139 Mf::Script::Value top = script[-1];
140 int index = 1;
141
142 for (;;)
143 {
144 script.push(index);
145 frameTable.pushField();
146
147 if (top.isTable()) mFrames.push_back(Frame(script, top));
148 else break;
149
150 ++index;
151 }
152 }
153 script.pop();
154 }
155 };
156
157
158 /**
159 * Starts loading a file with animation data. Such a file is formatted
160 * as a map of named sequences. The sequence constructor loads each
161 * individual sequence.
162 */
163
164 void loadFromFile()
165 {
166 Mf::Script script;
167 std::string filePath = Animation::getPath(getName());
168
169 script.importStandardLibraries();
170 importLogScript(script);
171 importAnimationBindings(script);
172
173 if (script.doFile(filePath) != Mf::Script::SUCCESS)
174 {
175 std::string str;
176 script[-1].get(str);
177 Mf::logScript("%s", str.c_str());
178 }
179 }
180
181 int defineSequence(Mf::Script& script)
182 {
183 Mf::Script::Value name = script[1].requireString();
184 Mf::Script::Value table = script[2].requireTable();
185
186 std::string nameStr;
187 name.get(nameStr);
188
189 sequences.insert(std::pair<std::string,Sequence>(nameStr,
190 Sequence(script, table)));
191
192 return 0;
193 }
194
195
196 void importAnimationBindings(Mf::Script& script)
197 {
198 script.importFunction("DefineSequence",
199 boost::bind(&Data::defineSequence, this, _1));
200
201 script.push(1); script.set("ATTACK");
202 script.push(2); script.set("CHARGE");
203 script.push(3); script.set("FLY");
204 script.push(4); script.set("HIT");
205 script.push(5); script.set("JUMP");
206 script.push(6); script.set("RUN");
207 script.push(7); script.set("STAND");
208 }
209
210
211 /**
212 * Construction is initialization. The animation class data container
213 * registers itself as a mippleton and then loads the animation data.
214 */
215
216 explicit Data(const std::string& name) :
217 Mf::Library<Data>(name)
218 {
219 loadFromFile();
220 }
221
222 std::map<std::string,Sequence> sequences; ///< List of sequences.
223 };
224
225
226 /**
227 * Construction is intialization.
228 */
229
230 Impl(const std::string& name) :
231 mData(Data::getInstance(name)),
232 mCurrentSequence(0),
233 mFrameCounter(0),
234 mFrameIndex(0),
235 mTimeAccum(0),
236 mFrameDuration(0) {}
237
238
239 /**
240 * Sets up the animation classes to "play" a named sequence. If another
241 * sequence was active, it will be replaced. Future updates will progress
242 * the new sequence.
243 */
244
245 void startSequence(const std::string& name)
246 {
247 std::map<std::string,Data::Sequence>::iterator it;
248
249 it = mData->sequences.find(name);
250
251 if (it != mData->sequences.end())
252 {
253 mCurrentSequence = &(*it).second;
254 mFrameCounter = 0;
255 mFrameIndex = mCurrentSequence->mFrames[0].mIndex;
256 mTimeAccum = 0.0;
257 mFrameDuration = mCurrentSequence->mDelay *
258 mCurrentSequence->mFrames[0].mDuration;
259 }
260 }
261
262 /**
263 * Updates or progresses the animation sequence. If the time interval
264 * surpasses the duration of the current frame, a new frame becomes the
265 * current frame. If the last frame of a sequence expires, the active
266 * sequence will switch automatically to the designated "next" sequence, or
267 * if none is specified but the sequence is set to loop, the first frame of
268 * the sequence will become the current frame, and the animation essentially
269 * starts over again.
270 */
271
272 void update(Mf::Scalar t, Mf::Scalar dt)
273 {
274 if (mCurrentSequence)
275 {
276 mTimeAccum += dt;
277
278 if (mTimeAccum >= mFrameDuration)
279 {
280 if (++mFrameCounter >= mCurrentSequence->mFrames.size())
281 {
282 if (!mCurrentSequence->mNext.empty())
283 {
284 startSequence(mCurrentSequence->mNext);
285 }
286 else if (mCurrentSequence->mLoop)
287 {
288 mFrameCounter = 0;
289 }
290 else
291 {
292 mFrameCounter--;
293 mCurrentSequence = 0;
294 }
295 }
296
297 mFrameIndex = mCurrentSequence->mFrames[mFrameCounter].mIndex;
298 mTimeAccum = mFrameDuration - mTimeAccum;
299 mFrameDuration = mCurrentSequence->mDelay *
300 mCurrentSequence->mFrames[mFrameCounter].mDuration;
301 }
302 }
303 }
304
305 boost::shared_ptr<Data> mData; ///< Internal data.
306
307 Data::Sequence* mCurrentSequence; ///< Active sequence.
308 unsigned mFrameCounter; ///< Current frame.
309 unsigned mFrameIndex; ///< Index of current frame.
310 Mf::Scalar mTimeAccum; ///< Time accumulation.
311 Mf::Scalar mFrameDuration; ///< Scaled frame duration.
312 };
313
314
315 Animation::Animation(const std::string& name) :
316 // pass through
317 mImpl(new Animation::Impl(name)) {}
318
319
320 void Animation::startSequence(const std::string& name)
321 {
322 // pass through
323 mImpl->startSequence(name);
324 }
325
326 void Animation::update(Mf::Scalar t, Mf::Scalar dt)
327 {
328 // pass through
329 mImpl->update(t, dt);
330 }
331
332
333 /**
334 * Gets the index for the current frame. This is presumably called by some
335 * drawing code which will draw the correct current frame.
336 */
337
338 unsigned Animation::getFrame() const
339 {
340 return mImpl->mFrameIndex;
341 }
342
343
344 /**
345 * Specialized search location for animation files. They can be found in the
346 * "animations" subdirectory of any of the searched directories.
347 */
348
349 std::string Animation::getPath(const std::string& name)
350 {
351 return Mf::Resource::getPath("animations/" + name + ".lua");
352 }
353
354
355 /** vim: set ts=4 sw=4 tw=80: *************************************************/
356
This page took 0.049055 seconds and 4 git commands to generate.