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