1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include "mads/animation.h"
24 #include "mads/compression.h"
25 
26 #define FILENAME_SIZE 13
27 
28 namespace MADS {
29 
load(Common::SeekableReadStream * f)30 void AAHeader::load(Common::SeekableReadStream *f) {
31 	_spriteSetsCount = f->readUint16LE();
32 	_miscEntriesCount = f->readUint16LE();
33 	_frameEntriesCount = f->readUint16LE();
34 	_messagesCount = f->readUint16LE();
35 	_loadFlags = f->readUint16LE();
36 	_charSpacing = f->readSint16LE();
37 	_bgType = (AnimBgType)f->readUint16LE();
38 	_roomNumber = f->readUint16LE();
39 	f->skip(2);
40 	_manualFlag = f->readUint16LE() != 0;
41 	_spritesIndex = f->readUint16LE();
42 	_scrollPosition.x = f->readSint16LE();
43 	_scrollPosition.y = f->readSint16LE();
44 	_scrollTicks = f->readUint32LE() & 0xffff;
45 	f->skip(6);
46 
47 	char buffer[FILENAME_SIZE];
48 	f->read(buffer, FILENAME_SIZE);
49 	buffer[FILENAME_SIZE - 1] = '\0';
50 	_backgroundFile = Common::String(buffer);
51 
52 	for (int i = 0; i < 50; ++i) {
53 		f->read(buffer, FILENAME_SIZE);
54 		buffer[FILENAME_SIZE - 1] = '\0';
55 		if (i < _spriteSetsCount)
56 			_spriteSetNames.push_back(Common::String(buffer));
57 	}
58 
59 	f->read(buffer, FILENAME_SIZE);
60 	buffer[FILENAME_SIZE - 1] = '\0';
61 	_soundName = Common::String(buffer);
62 
63 	f->skip(13);
64 	f->read(buffer, FILENAME_SIZE);
65 	buffer[FILENAME_SIZE - 1] = '\0';
66 	_dsrName = Common::String(buffer);
67 
68 	f->read(buffer, FILENAME_SIZE);
69 	buffer[FILENAME_SIZE - 1] = '\0';
70 	_fontResource = Common::String(buffer);
71 }
72 
73 /*------------------------------------------------------------------------*/
74 
load(Common::SeekableReadStream * f)75 void AnimMessage::load(Common::SeekableReadStream *f) {
76 	_soundId = f->readSint16LE();
77 
78 	char buffer[64];
79 	f->read(&buffer[0], 64);
80 	_msg = Common::String(buffer);
81 	f->skip(4);
82 	_pos.x = f->readSint16LE();
83 	_pos.y = f->readSint16LE();
84 	_flags = f->readUint16LE();
85 	_rgb1[0] = f->readByte() << 2;
86 	_rgb1[1] = f->readByte() << 2;
87 	_rgb1[2] = f->readByte() << 2;
88 	_rgb2[0] = f->readByte() << 2;
89 	_rgb2[1] = f->readByte() << 2;
90 	_rgb2[2] = f->readByte() << 2;
91 	f->skip(2);	// Space for kernelMsgIndex
92 	_kernelMsgIndex = -1;
93 	f->skip(6);
94 	_startFrame = f->readUint16LE();
95 	_endFrame = f->readUint16LE();
96 	f->skip(2);
97 }
98 
load(Common::SeekableReadStream * f,bool uiFlag)99 void AnimFrameEntry::load(Common::SeekableReadStream *f, bool uiFlag) {
100 	if (uiFlag) {
101 		f->skip(2);
102 		_frameNumber = -1;		// Unused
103 		_seqIndex = f->readByte();
104 		_spriteSlot._spritesIndex = f->readByte();
105 		_spriteSlot._frameNumber = (int8)f->readByte();
106 		f->skip(1);
107 		_spriteSlot._position.x = f->readSint16LE();
108 		_spriteSlot._position.y = f->readSint16LE();
109 	} else {
110 		_frameNumber = f->readUint16LE();
111 		if (_frameNumber & 0x8000)
112 			_frameNumber = -(_frameNumber & 0x7fff);
113 
114 		_seqIndex = f->readByte();
115 		_spriteSlot._spritesIndex = f->readByte();
116 		_spriteSlot._frameNumber = f->readUint16LE();
117 		if (_spriteSlot._frameNumber & 0x8000)
118 			_spriteSlot._frameNumber = -(_spriteSlot._frameNumber & 0x7fff);
119 
120 		_spriteSlot._position.x = f->readSint16LE();
121 		_spriteSlot._position.y = f->readSint16LE();
122 		_spriteSlot._depth = f->readSByte();
123 		_spriteSlot._scale = (int8)f->readByte();
124 	}
125 }
126 
127 /*------------------------------------------------------------------------*/
128 
load(Common::SeekableReadStream * f)129 void AnimMiscEntry::load(Common::SeekableReadStream *f) {
130 	_soundId = f->readByte();
131 	_msgIndex = f->readSByte();
132 	_numTicks = f->readUint16LE();
133 	_posAdjust.x = f->readSint16LE();
134 	_posAdjust.y = f->readSint16LE();
135 	_scroll.x = f->readSByte();
136 	_scroll.y = f->readSByte();
137 }
138 
139 /*------------------------------------------------------------------------*/
140 
load(Common::SeekableReadStream * f)141 void AnimUIEntry::load(Common::SeekableReadStream *f) {
142 	_probability = f->readUint16LE();
143 	_imageCount = f->readUint16LE();
144 	_firstImage = f->readUint16LE();
145 	_lastImage = f->readUint16LE();
146 	_counter = f->readSint16LE();
147 	for (int i = 0; i < ANIM_SPAWN_COUNT; ++i)
148 		_spawn[i] = f->readByte();
149 	for (int i = 0; i < ANIM_SPAWN_COUNT; ++i)
150 		_spawnFrame[i] = f->readUint16LE();
151 	_sound = f->readUint16LE() & 0xFF;
152 	_soundFrame = f->readUint16LE();
153 }
154 
155 /*------------------------------------------------------------------------*/
156 
init(MADSEngine * vm,Scene * scene)157 Animation *Animation::init(MADSEngine *vm, Scene *scene) {
158 	return new Animation(vm, scene);
159 }
160 
Animation(MADSEngine * vm,Scene * scene)161 Animation::Animation(MADSEngine *vm, Scene *scene) : _vm(vm), _scene(scene) {
162 	_flags = 0;
163 	_font = nullptr;
164 	_resetFlag = false;
165 	_canChangeView = false;
166 	_messageCtr = 0;
167 	_skipLoad = false;
168 	_freeFlag = false;
169 	_unkIndex = -1;
170 	_nextFrameTimer = 0;
171 	_nextScrollTimer = 0;
172 	_trigger = 0;
173 	_triggerMode = SEQUENCE_TRIGGER_PREPARE;
174 	_actionDetails._verbId = VERB_NONE;
175 	_actionDetails._objectNameId = -1;
176 	_actionDetails._indirectObjectId = -1;
177 	_currentFrame = 0;
178 	_oldFrameEntry = 0;
179 	_rgbResult = -1;
180 	_palIndex1 = _palIndex2 = -1;
181 	_dynamicHotspotIndex = -1;
182 }
183 
~Animation()184 Animation::~Animation() {
185 	Scene &scene = _vm->_game->_scene;
186 
187 	if (_header._manualFlag)
188 		scene._sprites.remove(_spriteListIndexes[_header._spritesIndex]);
189 
190 	for (int idx = 0; idx < _header._spriteSetsCount; ++idx) {
191 		if (!_header._manualFlag || _header._spritesIndex != idx)
192 			scene._sprites.remove(_spriteListIndexes[idx]);
193 	}
194 }
195 
load(MSurface & backSurface,DepthSurface & depthSurface,const Common::String & resName,int flags,Common::Array<PaletteCycle> * palCycles,SceneInfo * sceneInfo)196 void Animation::load(MSurface &backSurface, DepthSurface &depthSurface,
197 		const Common::String &resName, int flags, Common::Array<PaletteCycle> *palCycles,
198 		SceneInfo *sceneInfo) {
199 	Common::String resourceName = resName;
200 	if (!resourceName.contains("."))
201 		resourceName += ".AA";
202 
203 	File f(resourceName);
204 	MadsPack madsPack(&f);
205 
206 	Common::SeekableReadStream *stream = madsPack.getItemStream(0);
207 	_header.load(stream);
208 	delete stream;
209 
210 	if (_header._bgType == ANIMBG_INTERFACE)
211 		flags |= PALFLAG_RESERVED;
212 	_flags = flags;
213 
214 	if (flags & ANIMFLAG_LOAD_BACKGROUND) {
215 		loadBackground(backSurface, depthSurface, _header, flags, palCycles, sceneInfo);
216 	}
217 	if (flags & ANIMFLAG_LOAD_BACKGROUND_ONLY) {
218 		// No data
219 		_header._messagesCount = 0;
220 		_header._frameEntriesCount = 0;
221 		_header._miscEntriesCount = 0;
222 	}
223 
224 	// Initialize the reference list
225 	_spriteListIndexes.clear();
226 	for (int i = 0; i < _header._spriteSetsCount; ++i)
227 		_spriteListIndexes.push_back(-1);
228 
229 	int streamIndex = 1;
230 	_messages.clear();
231 	if (_header._messagesCount > 0) {
232 		// Chunk 2: Following is a list of any messages for the animation
233 		Common::SeekableReadStream *msgStream = madsPack.getItemStream(streamIndex++);
234 
235 		for (int i = 0; i < _header._messagesCount; ++i) {
236 			AnimMessage rec;
237 			rec.load(msgStream);
238 			_messages.push_back(rec);
239 		}
240 
241 		delete msgStream;
242 	}
243 
244 	_frameEntries.clear();
245 	if (_header._frameEntriesCount > 0) {
246 		// Chunk 3: animation frame info
247 		Common::SeekableReadStream *frameStream = madsPack.getItemStream(streamIndex++);
248 
249 		for (int i = 0; i < _header._frameEntriesCount; i++) {
250 			AnimFrameEntry rec;
251 			rec.load(frameStream, _header._bgType == ANIMBG_INTERFACE);
252 			_frameEntries.push_back(rec);
253 		}
254 
255 		delete frameStream;
256 	}
257 
258 	_miscEntries.clear();
259 	_uiEntries.clear();
260 	if (_header._miscEntriesCount > 0) {
261 		// Chunk 4: Misc Data
262 		Common::SeekableReadStream *miscStream = madsPack.getItemStream(streamIndex++);
263 
264 		if (_header._bgType == ANIMBG_INTERFACE) {
265 			for (int i = 0; i < _header._miscEntriesCount; ++i) {
266 				AnimUIEntry rec;
267 				rec.load(miscStream);
268 				_uiEntries.push_back(rec);
269 			}
270 		} else {
271 			for (int i = 0; i < _header._miscEntriesCount; ++i) {
272 				AnimMiscEntry rec;
273 				rec.load(miscStream);
274 				_miscEntries.push_back(rec);
275 			}
276 		}
277 
278 		delete miscStream;
279 	}
280 
281 	// If the animation specifies a font, then load it for access
282 	delete _font;
283 	if (_header._loadFlags & ANIMFLAG_CUSTOM_FONT) {
284 		Common::String fontName = "*" + _header._fontResource;
285 		_font = _vm->_font->getFont(fontName.c_str());
286 	} else {
287 		_font = nullptr;
288 	}
289 
290 	// Load all the sprite sets for the animation
291 	for (uint i = 0; i < _spriteSets.size(); ++i)
292 		delete _spriteSets[i];
293 	_spriteSets.clear();
294 	_spriteSets.resize(_header._spriteSetsCount);
295 
296 	for (int i = 0; i < _header._spriteSetsCount; ++i) {
297 		if (_header._manualFlag && (i == _header._spritesIndex)) {
298 			// Skip over field, since it's manually loaded
299 			_spriteSets[i] = nullptr;
300 		} else {
301 			_spriteSets[i] = new SpriteAsset(_vm, _header._spriteSetNames[i], flags);
302 			_spriteListIndexes[i] = _vm->_game->_scene._sprites.add(_spriteSets[i]);
303 		}
304 	}
305 
306 	if (_header._manualFlag) {
307 		Common::String assetResName = "*" + _header._spriteSetNames[_header._spritesIndex];
308 		SpriteAsset *sprites = new SpriteAsset(_vm, assetResName, flags);
309 		_spriteSets[_header._spritesIndex] = sprites;
310 
311 		_spriteListIndexes[_header._spritesIndex] = _scene->_sprites.add(sprites);
312 	}
313 
314 	Common::Array<int> usageList;
315 	for (int idx = 0; idx < _header._spriteSetsCount; ++idx)
316 		usageList.push_back(_spriteSets[idx]->_usageIndex);
317 
318 	if (usageList.size() > 0) {
319 		int spritesUsageIndex = _spriteSets[0]->_usageIndex;
320 		_vm->_palette->_paletteUsage.updateUsage(usageList, spritesUsageIndex);
321 	}
322 
323 	// Remaps the sprite list indexes for frames to the loaded sprite list indexes
324 	for (uint i = 0; i < _frameEntries.size(); ++i) {
325 		int spriteListIndex = _frameEntries[i]._spriteSlot._spritesIndex;
326 		_frameEntries[i]._spriteSlot._spritesIndex = _spriteListIndexes[spriteListIndex];
327 	}
328 
329 	f.close();
330 }
331 
preLoad(const Common::String & resName,int level)332 void Animation::preLoad(const Common::String &resName, int level) {
333 	// No implementation in ScummVM, since access is fast enough that data
334 	// doesn't need to be preloaded
335 }
336 
startAnimation(int endTrigger)337 void Animation::startAnimation(int endTrigger) {
338 	_messageCtr = 0;
339 	_skipLoad = true;
340 
341 	if (_header._manualFlag) {
342 		_unkIndex = -1;
343 		//SpriteAsset *asset = _scene->_sprites[_spriteListIndexes[_header._spritesIndex]];
344 
345 		loadFrame(1);
346 	}
347 
348 	if (_vm->_game->_kernelMode == KERNEL_ACTIVE_CODE)
349 		_vm->_palette->refreshSceneColors();
350 
351 	_currentFrame = 0;
352 	_oldFrameEntry = 0;
353 	_nextFrameTimer = _vm->_game->_scene._frameStartTime;
354 	_trigger = endTrigger;
355 	_triggerMode = _vm->_game->_triggerSetupMode;
356 	_actionDetails = _vm->_game->_scene._action._activeAction;
357 
358 	for (int idx = 0; idx < _header._messagesCount; ++idx) {
359 		_messages[idx]._kernelMsgIndex = -1;
360 	}
361 }
362 
loadFrame(int frameNumber)363 void Animation::loadFrame(int frameNumber) {
364 	Scene &scene = _vm->_game->_scene;
365 	if (_skipLoad)
366 		return;
367 
368 	Common::Point pt;
369 	int spriteListIndex = _spriteListIndexes[_header._spritesIndex];
370 	SpriteAsset &spriteSet = *scene._sprites[spriteListIndex];
371 
372 	if (_unkIndex < 0) {
373 		MSurface *frame = spriteSet.getFrame(0);
374 		pt.x = frame->getBounds().left;
375 		pt.y = frame->getBounds().top;
376 	} else {
377 		pt.x = _unkList[_unkIndex].x;
378 		pt.y = _unkList[_unkIndex].y;
379 		_unkIndex = 1 - _unkIndex;
380 		warning("LoadFrame - Using unknown array");
381 	}
382 
383 	if (drawFrame(spriteSet, pt, frameNumber))
384 		error("drawFrame failure");
385 }
386 
drawFrame(SpriteAsset & spriteSet,const Common::Point & pt,int frameNumber)387 bool Animation::drawFrame(SpriteAsset &spriteSet, const Common::Point &pt, int frameNumber) {
388 	return 0;
389 }
390 
loadBackground(MSurface & backSurface,DepthSurface & depthSurface,AAHeader & header,int flags,Common::Array<PaletteCycle> * palCycles,SceneInfo * sceneInfo)391 void Animation::loadBackground(MSurface &backSurface, DepthSurface &depthSurface,
392 		AAHeader &header, int flags, Common::Array<PaletteCycle> *palCycles, SceneInfo *sceneInfo) {
393 	_scene->_depthStyle = 0;
394 	if (header._bgType <= ANIMBG_FULL_SIZE) {
395 		_vm->_palette->_paletteUsage.setEmpty();
396 		sceneInfo->load(header._roomNumber, 0, header._backgroundFile, flags, depthSurface, backSurface);
397 		_scene->_depthStyle = sceneInfo->_depthStyle == 2 ? 1 : 0;
398 		if (palCycles) {
399 			palCycles->clear();
400 			for (uint i = 0; i < sceneInfo->_paletteCycles.size(); ++i)
401 				palCycles->push_back(sceneInfo->_paletteCycles[i]);
402 		}
403 	} else if (header._bgType == ANIMBG_INTERFACE) {
404 		// Load a scene interface
405 		Common::String resourceName = "*" + header._backgroundFile;
406 		backSurface.load(resourceName);
407 
408 		if (palCycles)
409 			palCycles->clear();
410 	} else {
411 		// Original has useless code here
412 	}
413 }
414 
hasScroll() const415 bool Animation::hasScroll() const {
416 	return (_header._scrollPosition.x != 0) || (_header._scrollPosition.y != 0);
417 }
418 
update()419 void Animation::update() {
420 	Scene &scene = _vm->_game->_scene;
421 	Palette &palette = *_vm->_palette;
422 
423 	if (_header._manualFlag) {
424 		int spriteListIndex = _spriteListIndexes[_header._spritesIndex];
425 		int newIndex = -1;
426 
427 		for (uint idx = _oldFrameEntry; idx < _frameEntries.size(); ++idx) {
428 			if (_frameEntries[idx]._frameNumber > _currentFrame)
429 				break;
430 			if (_frameEntries[idx]._spriteSlot._spritesIndex == spriteListIndex)
431 				newIndex = _frameEntries[idx]._spriteSlot._frameNumber;
432 		}
433 
434 		if (newIndex >= 0)
435 			loadFrame(newIndex);
436 	}
437 
438 	// If it's not time for the next frame, then exit
439 	if (_vm->_game->_scene._frameStartTime < _nextFrameTimer)
440 		return;
441 
442 	// Erase any active sprites
443 	eraseSprites();
444 
445 	// Validate the current frame
446 	if (_currentFrame >= (int)_miscEntries.size()) {
447 		// Is the animation allowed to be repeated?
448 		if (_resetFlag) {
449 			_currentFrame = 0;
450 			_oldFrameEntry = 0;
451 		} else {
452 			_freeFlag = true;
453 			return;
454 		}
455 	}
456 
457 	// Handle executing any sound command for this frame
458 	AnimMiscEntry &misc = _miscEntries[_currentFrame];
459 	if (misc._soundId)
460 		_vm->_sound->command(misc._soundId);
461 
462 	// Handle any screen scrolling
463 	if (hasScroll()) {
464 		scene._backgroundSurface.scrollX(_header._scrollPosition.x);
465 		scene._backgroundSurface.scrollY(_header._scrollPosition.y);
466 		scene._spriteSlots.fullRefresh();
467 	}
468 
469 	bool isV2 = (_vm->getGameID() != GType_RexNebular);
470 	if (isV2 && _canChangeView) {
471 		// Handle any offset adjustment for sprites as of this frame
472 		bool paChanged = false;
473 		if (getFramePosAdjust(_currentFrame).x != scene._posAdjust.x) {
474 			scene._posAdjust.x = getFramePosAdjust(_currentFrame).x;
475 			paChanged = true;
476 		}
477 
478 		if (getFramePosAdjust(_currentFrame).y != scene._posAdjust.y) {
479 			scene._posAdjust.y = getFramePosAdjust(_currentFrame).y;
480 			paChanged = true;
481 		}
482 
483 		if (paChanged) {
484 			int newIndex = scene._spriteSlots.add();
485 			scene._spriteSlots[newIndex]._seqIndex = -1;
486 			scene._spriteSlots[newIndex]._flags = IMG_REFRESH;
487 		}
488 	}
489 
490 	// Main frame animation loop - frames get animated by being placed, as necessary, into the
491 	// main sprite slot array
492 	while ((uint)_oldFrameEntry < _frameEntries.size()) {
493 		if (_frameEntries[_oldFrameEntry]._frameNumber > _currentFrame)
494 			break;
495 		else if (_frameEntries[_oldFrameEntry]._frameNumber == _currentFrame) {
496 			// Found the correct frame
497 			int spriteSlotIndex = 0;
498 			int index = 0;
499 
500 			for (;;) {
501 				if ((spriteSlotIndex == 0) && (index < (int)scene._spriteSlots.size())) {
502 					int seqIndex = _frameEntries[_oldFrameEntry]._seqIndex - scene._spriteSlots[index]._seqIndex;
503 					if (seqIndex == 0x80) {
504 						if (scene._spriteSlots[index] == _frameEntries[_oldFrameEntry]._spriteSlot) {
505 							scene._spriteSlots[index]._flags = IMG_STATIC;
506 							spriteSlotIndex = -1;
507 						}
508 					}
509 					++index;
510 					continue;
511 				}
512 
513 				if (spriteSlotIndex == 0) {
514 					int slotIndex = scene._spriteSlots.add();
515 					SpriteSlot &slot = scene._spriteSlots[slotIndex];
516 					slot.copy(_frameEntries[_oldFrameEntry]._spriteSlot);
517 					slot._seqIndex = _frameEntries[_oldFrameEntry]._seqIndex + 0x80;
518 
519 					SpriteAsset &spriteSet = *scene._sprites[
520 						scene._spriteSlots[slotIndex]._spritesIndex];
521 					slot._flags = spriteSet.isBackground() ? IMG_DELTA : IMG_UPDATE;
522 				}
523 				break;
524 			}
525 		}
526 
527 		++_oldFrameEntry;
528 	}
529 
530 	// Handle the display of any messages
531 	for (uint idx = 0; idx < _messages.size(); ++idx) {
532 		if (_messages[idx]._kernelMsgIndex >= 0) {
533 			// Handle currently active message
534 			if ((_currentFrame < _messages[idx]._startFrame) || (_currentFrame > _messages[idx]._endFrame)) {
535 				scene._kernelMessages.remove(_messages[idx]._kernelMsgIndex);
536 				_messages[idx]._kernelMsgIndex = -1;
537 				--_messageCtr;
538 			}
539 		} else if ((_currentFrame >= _messages[idx]._startFrame) && (_currentFrame <= _messages[idx]._endFrame)) {
540 			// Start displaying the message
541 			AnimMessage &me = _messages[idx];
542 
543 			if (_flags & ANIMFLAG_ANIMVIEW) {
544 				_rgbResult = palette._paletteUsage.checkRGB(me._rgb1, -1, true, &_palIndex1);
545 				_rgbResult = palette._paletteUsage.checkRGB(me._rgb2, _rgbResult, true, &_palIndex2);
546 
547 				// Update the palette with the two needed colors
548 				int palStart = MIN(_palIndex1, _palIndex2);
549 				int palCount = ABS(_palIndex2 - _palIndex1) + 1;
550 				palette.setPalette(&palette._mainPalette[palStart * 3], palStart, palCount);
551 			} else {
552 				// The color index to use is dependant on how many messages are currently on-screen
553 				switch (_messageCtr) {
554 				case 1:
555 					_palIndex1 = 252;
556 					break;
557 				case 2:
558 					_palIndex1 = 16;
559 					break;
560 				default:
561 					_palIndex1 = 250;
562 					break;
563 				}
564 				_palIndex2 = _palIndex1 + 1;
565 
566 				_vm->_palette->setEntry(_palIndex1, me._rgb1[0], me._rgb1[1], me._rgb1[2]);
567 				_vm->_palette->setEntry(_palIndex2, me._rgb2[0], me._rgb2[1], me._rgb2[2]);
568 			}
569 
570 			// Add a kernel message to display the given text
571 			me._kernelMsgIndex = scene._kernelMessages.add(me._pos,
572 				_palIndex1 | (_palIndex2 << 8),
573 				0, 0, INDEFINITE_TIMEOUT, me._msg);
574 			assert(me._kernelMsgIndex >= 0);
575 			++_messageCtr;
576 
577 			// If there's an accompanying sound, also play it
578 			if (me._soundId > 0)
579 				_vm->_audio->playSound(me._soundId - 1);
580 		}
581 	}
582 
583 	// Move to the next frame
584 	_currentFrame++;
585 	if (_currentFrame >= (int)_miscEntries.size()) {
586 		// Animation is complete
587 		if (_trigger != 0) {
588 			_vm->_game->_trigger = _trigger;
589 			_vm->_game->_triggerMode = _triggerMode;
590 
591 			if (_triggerMode != SEQUENCE_TRIGGER_DAEMON) {
592 				// Copy the noun list
593 				scene._action._activeAction = _actionDetails;
594 			}
595 		}
596 	}
597 
598 	int frameNum = MIN(_currentFrame, (int)_miscEntries.size() - 1);
599 	_nextFrameTimer = _vm->_game->_scene._frameStartTime + _miscEntries[frameNum]._numTicks;
600 }
601 
setCurrentFrame(int frameNumber)602 void Animation::setCurrentFrame(int frameNumber) {
603 	_currentFrame = frameNumber;
604 	_oldFrameEntry = 0;
605 	_freeFlag = false;
606 }
607 
setNextFrameTimer(uint32 newTimer)608 void Animation::setNextFrameTimer(uint32 newTimer) {
609 	_nextFrameTimer = newTimer;
610 }
611 
eraseSprites()612 void Animation::eraseSprites() {
613 	Scene &scene = _vm->_game->_scene;
614 
615 	for (uint idx = 0; idx < scene._spriteSlots.size(); ++idx) {
616 		if (scene._spriteSlots[idx]._seqIndex >= 0x80)
617 			scene._spriteSlots[idx]._flags = IMG_ERASE;
618 	}
619 }
620 
getFramePosAdjust(int idx)621 Common::Point Animation::getFramePosAdjust(int idx) {
622 	warning("TODO: Implement getFramePosAdjust");
623 
624 	return Common::Point(0, 0);
625 }
626 } // End of namespace MADS
627