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