[Scummvm-cvs-logs] SF.net SVN: scummvm:[45799] scummvm/trunk/engines/draci

spalek at users.sourceforge.net spalek at users.sourceforge.net
Tue Nov 10 06:16:35 CET 2009


Revision: 45799
          http://scummvm.svn.sourceforge.net/scummvm/?rev=45799&view=rev
Author:   spalek
Date:     2009-11-10 05:16:34 +0000 (Tue, 10 Nov 2009)

Log Message:
-----------
Huge refactoring of data structures.

Replaced IDs of objects by pointers, which saves many lookups, each of which
is horribly ineffective.  Moved a lot of code into methods of structs now
turned into objects.

Tested the new code a lot and seems to work as well as the old code.

Modified Paths:
--------------
    scummvm/trunk/engines/draci/animation.cpp
    scummvm/trunk/engines/draci/animation.h
    scummvm/trunk/engines/draci/draci.cpp
    scummvm/trunk/engines/draci/game.cpp
    scummvm/trunk/engines/draci/game.h
    scummvm/trunk/engines/draci/mouse.cpp
    scummvm/trunk/engines/draci/mouse.h
    scummvm/trunk/engines/draci/script.cpp
    scummvm/trunk/engines/draci/script.h
    scummvm/trunk/engines/draci/walking.cpp

Modified: scummvm/trunk/engines/draci/animation.cpp
===================================================================
--- scummvm/trunk/engines/draci/animation.cpp	2009-11-10 00:10:30 UTC (rev 45798)
+++ scummvm/trunk/engines/draci/animation.cpp	2009-11-10 05:16:34 UTC (rev 45799)
@@ -29,13 +29,13 @@
 
 namespace Draci {
 
-Animation::Animation(DraciEngine *vm, int index) : _vm(vm) {
-	_id = kUnused;
-	_index = index;
-	_z = 0;
+Animation::Animation(DraciEngine *vm, int id, uint z, bool playing) : _vm(vm) {
+	_id = id;
+	_index = kIgnoreIndex;
+	_z = z;
 	clearShift();
 	_displacement = kNoDisplacement;
-	_playing = false;
+	_playing = playing;
 	_looping = false;
 	_paused = false;
 	_canBeQuick = false;
@@ -239,10 +239,6 @@
 	_samples.clear();
 }
 
-void Animation::stopAnimation() {
-	_vm->_anims->stop(_id);
-}
-
 void Animation::exitGameLoop() {
 	_vm->_game->setExitLoop(true);
 }
@@ -251,75 +247,40 @@
 	_vm->_game->heroAnimationFinished();
 }
 
-Animation *AnimationManager::addAnimation(int id, uint z, bool playing) {
-	// Increment animation index
-	++_lastIndex;
+void Animation::play() {
+	if (isPlaying()) {
+		return;
+	}
 
-	Animation *anim = new Animation(_vm, _lastIndex);
+	// Mark the first frame dirty so it gets displayed
+	markDirtyRect(_vm->_screen->getSurface());
 
-	anim->setID(id);
-	anim->setZ(z);
-	anim->setPlaying(playing);
+	setPlaying(true);
 
-	insertAnimation(anim);
-
-	return anim;
+	debugC(3, kDraciAnimationDebugLevel, "Playing animation %d...", getID());
 }
 
-Animation *AnimationManager::addItem(int id, bool playing) {
-	Animation *anim = new Animation(_vm, kIgnoreIndex);
+void Animation::stop() {
+	if (!isPlaying()) {
+		return;
+	}
 
-	anim->setID(id);
-	anim->setZ(256);
-	anim->setPlaying(playing);
+	// Clean up the last frame that was drawn before stopping
+	markDirtyRect(_vm->_screen->getSurface());
 
-	insertAnimation(anim);
+	setPlaying(false);
 
-	return anim;
-}
+	// Reset the animation to the beginning
+	setCurrentFrame(0);
+	clearShift();
 
-Animation *AnimationManager::addText(int id, bool playing) {
-	Animation *anim = new Animation(_vm, kIgnoreIndex);
-
-	anim->setID(id);
-	anim->setZ(257);
-	anim->setPlaying(playing);
-
-	insertAnimation(anim);
-
-	return anim;
+	debugC(3, kDraciAnimationDebugLevel, "Stopping animation %d...", getID());
 }
 
-void AnimationManager::play(int id) {
-	Animation *anim = getAnimation(id);
-
-	if (anim && !anim->isPlaying()) {
-		// Mark the first frame dirty so it gets displayed
-		anim->markDirtyRect(_vm->_screen->getSurface());
-
-		anim->setPlaying(true);
-
-		debugC(3, kDraciAnimationDebugLevel, "Playing animation %d...", id);
-	}
+void Animation::del() {
+	_vm->_anims->deleteAnimation(this);
 }
 
-void AnimationManager::stop(int id) {
-	Animation *anim = getAnimation(id);
-
-	if (anim && anim->isPlaying()) {
-		// Clean up the last frame that was drawn before stopping
-		anim->markDirtyRect(_vm->_screen->getSurface());
-
-		anim->setPlaying(false);
-
-		// Reset the animation to the beginning
-		anim->setCurrentFrame(0);
-		anim->clearShift();
-
-		debugC(3, kDraciAnimationDebugLevel, "Stopping animation %d...", id);
-	}
-}
-
 void AnimationManager::pauseAnimations() {
 	Common::List<Animation *>::iterator it;
 
@@ -358,7 +319,10 @@
 	return NULL;
 }
 
-void AnimationManager::insertAnimation(Animation *anim) {
+void AnimationManager::insert(Animation *anim, bool allocateIndex) {
+	if (allocateIndex)
+		anim->setIndex(++_lastIndex);
+
 	Common::List<Animation *>::iterator it;
 
 	for (it = _animations.begin(); it != _animations.end(); ++it) {
@@ -369,20 +333,6 @@
 	_animations.insert(it, anim);
 }
 
-void AnimationManager::addOverlay(Drawable *overlay, uint z) {
-	// Since this is an overlay, we don't need it to be deleted
-	// when the GPL Release command is invoked so we pass the index
-	// as kIgnoreIndex
-	Animation *anim = new Animation(_vm, kIgnoreIndex);
-
-	anim->setID(kOverlayImage);
-	anim->setZ(z);
-	anim->setPlaying(true);
-	anim->addFrame(overlay, NULL);
-
-	insertAnimation(anim);
-}
-
 void AnimationManager::drawScene(Surface *surf) {
 	// Fill the screen with colour zero since some rooms may rely on the screen being black
 	_vm->_screen->getSurface()->fill(0);
@@ -431,7 +381,7 @@
 				Animation *anim = *next;
 				next = _animations.reverse_erase(next);
 
-				insertAnimation(anim);
+				insert(anim, false);
 				hasChanged = true;
 			}
 
@@ -441,21 +391,24 @@
 	} while (hasChanged);
 }
 
-void AnimationManager::deleteAnimation(int id) {
+void AnimationManager::deleteAnimation(Animation *anim) {
+	if (!anim) {
+		return;
+	}
 	Common::List<Animation *>::iterator it;
 
 	int index = -1;
 
 	// Iterate for the first time to delete the animation
 	for (it = _animations.begin(); it != _animations.end(); ++it) {
-		if ((*it)->getID() == id) {
+		if (*it == anim) {
 			// Remember index of the deleted animation
 			index = (*it)->getIndex();
 
 			delete *it;
 			_animations.erase(it);
 
-			debugC(3, kDraciAnimationDebugLevel, "Deleting animation %d...", id);
+			debugC(3, kDraciAnimationDebugLevel, "Deleting animation %d...", anim->getID());
 
 			break;
 		}
@@ -515,12 +468,10 @@
 	_lastIndex = index;
 }
 
-int AnimationManager::getTopAnimationID(int x, int y) const {
+const Animation *AnimationManager::getTopAnimation(int x, int y) const {
 	Common::List<Animation *>::const_iterator it;
 
-	// The default return value if no animations were found on these coordinates (not even overlays)
-	// i.e. the black background shows through so treat it as an overlay
-	int retval = kOverlayImage;
+	Animation *retval = NULL;
 
 	// Get transparent colour for the current screen
 	const int transparent = _vm->_screen->getSurface()->getTransparentColour();
@@ -540,24 +491,112 @@
 			continue;
 		}
 
+		bool matches = false;
 		if (frame->getRect(anim->getCurrentFrameDisplacement()).contains(x, y)) {
 			if (frame->getType() == kDrawableText) {
 
-				retval = anim->getID();
+				matches = true;
 
 			} else if (frame->getType() == kDrawableSprite &&
 					   reinterpret_cast<const Sprite *>(frame)->getPixel(x, y, anim->getCurrentFrameDisplacement()) != transparent) {
 
-				retval = anim->getID();
+				matches = true;
 			}
 		}
 
-		// Found an animation
-		if (retval != kOverlayImage)
-			break;
+		// Return the top-most animation object, unless it is an
+		// overlay sprite and there is an actual object underneath it.
+		if (matches) {
+			if (anim->getID() != kOverlayImage) {
+				return anim;
+			} else if (retval == NULL) {
+				retval = anim;
+			}
+		}
 	}
 
+	// The default return value if no animations were found on these coordinates (not even overlays)
 	return retval;
 }
 
+Animation *AnimationManager::load(uint animNum) {
+	// Make double-sure that an animation isn't loaded more than twice,
+	// otherwise horrible things happen in the AnimationManager, because
+	// they use a simple link-list without duplicate checking.  This should
+	// never happen unless there is a bug in the game, because all GPL2
+	// commands are guarded.
+	assert(!getAnimation(animNum));
+
+	const BAFile *animFile = _vm->_animationsArchive->getFile(animNum);
+	Common::MemoryReadStream animationReader(animFile->_data, animFile->_length);
+
+	uint numFrames = animationReader.readByte();
+
+	// The following two flags are ignored by the played.  Memory logic was
+	// a hint to the old player whether it should cache the sprites or load
+	// them on demand.  We have 1 memory manager and ignore these hints.
+	animationReader.readByte();
+	// The disable erasing field is just a (poor) optimization flag that
+	// turns of drawing the background underneath the sprite.  By reading
+	// the source code of the old player, I'm not sure if that would ever
+	// have worked.  There are only 6 animations in the game with this flag
+	// true.  All of them have just 1 animation phase and they are used to
+	// patch a part of the original background by a new sprite.  This
+	// should work with the default logic as well---just play this
+	// animation on top of the background.  Since the only meaning of the
+	// flag was optimization, ignoring should be OK even without dipping
+	// into details.
+	animationReader.readByte();
+	const bool cyclic = animationReader.readByte();
+	const bool relative = animationReader.readByte();
+
+	Animation *anim = new Animation(_vm, animNum, 0, false);
+	insert(anim, true);
+
+	anim->setLooping(cyclic);
+
+	for (uint i = 0; i < numFrames; ++i) {
+		uint spriteNum = animationReader.readUint16LE() - 1;
+		int x = animationReader.readSint16LE();
+		int y = animationReader.readSint16LE();
+		uint scaledWidth = animationReader.readUint16LE();
+		uint scaledHeight = animationReader.readUint16LE();
+		byte mirror = animationReader.readByte();
+		int sample = animationReader.readUint16LE() - 1;
+		uint freq = animationReader.readUint16LE();
+		uint delay = animationReader.readUint16LE();
+
+		// _spritesArchive is flushed when entering a room.  All
+		// scripts in a room are responsible for loading their animations.
+		const BAFile *spriteFile = _vm->_spritesArchive->getFile(spriteNum);
+		Sprite *sp = new Sprite(spriteFile->_data, spriteFile->_length,
+			relative ? 0 : x, relative ? 0 : y, true);
+
+		// Some frames set the scaled dimensions to 0 even though other frames
+		// from the same animations have them set to normal values
+		// We work around this by assuming it means no scaling is necessary
+		if (scaledWidth == 0) {
+			scaledWidth = sp->getWidth();
+		}
+
+		if (scaledHeight == 0) {
+			scaledHeight = sp->getHeight();
+		}
+
+		sp->setScaled(scaledWidth, scaledHeight);
+
+		if (mirror)
+			sp->setMirrorOn();
+
+		sp->setDelay(delay * 10);
+
+		anim->addFrame(sp, _vm->_soundsArchive->getSample(sample, freq));
+		if (relative) {
+			anim->makeLastFrameRelative(x, y);
+		}
+	}
+
+	return anim;
 }
+
+}

Modified: scummvm/trunk/engines/draci/animation.h
===================================================================
--- scummvm/trunk/engines/draci/animation.h	2009-11-10 00:10:30 UTC (rev 45798)
+++ scummvm/trunk/engines/draci/animation.h	2009-11-10 05:16:34 UTC (rev 45799)
@@ -62,7 +62,7 @@
 typedef void (Animation::* AnimationCallback)();
 
 public:
-	Animation(DraciEngine *v, int index);
+	Animation(DraciEngine *v, int id, uint z, bool playing);
 	~Animation();
 
 	uint getZ() const { return _z; }
@@ -120,10 +120,13 @@
 	void registerCallback(AnimationCallback callback) { _callback = callback; }
 
 	void doNothing() {}
-	void stopAnimation();
 	void exitGameLoop();
 	void tellWalkingState();
 
+	void play();
+	void stop();
+	void del();
+
 private:
 	uint nextFrameNum() const;
 	void deleteFrames();
@@ -174,17 +177,13 @@
 	AnimationManager(DraciEngine *vm) : _vm(vm), _lastIndex(-1) {}
 	~AnimationManager() { deleteAll(); }
 
-	Animation *addAnimation(int id, uint z, bool playing);
-	Animation *addText(int id, bool playing);
-	Animation *addItem(int id, bool playing);
-	void addOverlay(Drawable *overlay, uint z);
+	void insert(Animation *anim, bool allocateIndex);
+	Animation *load(uint animNum);
 
-	void play(int id);
-	void stop(int id);
 	void pauseAnimations();
 	void unpauseAnimations();
 
-	void deleteAnimation(int id);
+	void deleteAnimation(Animation *anim);
 	void deleteOverlays();
 	void deleteAll();
 
@@ -195,11 +194,10 @@
 	int getLastIndex() const { return _lastIndex; }
 	void deleteAfterIndex(int index);
 
-	int getTopAnimationID(int x, int y) const;
+	const Animation *getTopAnimation(int x, int y) const;
 
 private:
 	void sortAnimations();
-	void insertAnimation(Animation *anim);
 
 	DraciEngine *_vm;
 

Modified: scummvm/trunk/engines/draci/draci.cpp
===================================================================
--- scummvm/trunk/engines/draci/draci.cpp	2009-11-10 00:10:30 UTC (rev 45798)
+++ scummvm/trunk/engines/draci/draci.cpp	2009-11-10 05:16:34 UTC (rev 45799)
@@ -242,7 +242,7 @@
 					? _game->getEscRoom() : _game->getPreviousRoomNum();
 
 				// Check if there is an escape room defined for the current room
-				if (escRoom != kNoEscRoom) {
+				if (escRoom >= 0) {
 
 					// Schedule room change
 					// TODO: gate 0 is not always the best one for returning from the map
@@ -266,15 +266,7 @@
 			case Common::KEYCODE_w:
 				// Show walking map toggle
 				_showWalkingMap = !_showWalkingMap;
-				if (_showWalkingMap) {
-					_anims->play(kWalkingMapOverlay);
-					_anims->play(kWalkingShortestPathOverlay);
-					_anims->play(kWalkingObliquePathOverlay);
-				} else {
-					_anims->stop(kWalkingMapOverlay);
-					_anims->stop(kWalkingShortestPathOverlay);
-					_anims->stop(kWalkingObliquePathOverlay);
-				}
+				_game->switchWalkingAnimations(_showWalkingMap);
 				break;
 			case Common::KEYCODE_q:
 				_game->setWantQuickHero(!_game->getWantQuickHero());

Modified: scummvm/trunk/engines/draci/game.cpp
===================================================================
--- scummvm/trunk/engines/draci/game.cpp	2009-11-10 00:10:30 UTC (rev 45798)
+++ scummvm/trunk/engines/draci/game.cpp	2009-11-10 05:16:34 UTC (rev 45799)
@@ -135,6 +135,8 @@
 
 		// Set object location
 		_objects[i]._location = (~(1 << 7) & tmp) - 1;
+
+		_objects[i]._playingAnim = -1;
 	}
 
 	assert(numDialogues == _info._numDialogues);
@@ -154,6 +156,7 @@
 			loop(kOuterLoop, false);
 		}
 	}
+
 }
 
 void Game::init() {
@@ -168,61 +171,63 @@
 	setLoopStatus(kStatusGate);
 	setLoopSubstatus(kOuterLoop);
 
-	_animUnderCursor = kOverlayImage;
+	_animUnderCursor = NULL;
 
-	_currentItem = kNoItem;
-	_itemUnderCursor = kNoItem;
+	_currentItem = _itemUnderCursor = NULL;
 
 	_vm->_mouse->setCursorType(kHighlightedCursor);	// anything different from kNormalCursor
 
-	_objUnderCursor = kOverlayImage;
+	_objUnderCursor = NULL;
         
 	// Set the inventory to empty initially
-	memset(_inventory, kNoItem, kInventorySlots * sizeof(int));
+	memset(_inventory, 0, kInventorySlots * sizeof(GameItem *));
 
 	// Initialize animation for object / room titles
-	Animation *titleAnim = _vm->_anims->addText(kTitleText, true);
-	Text *title = new Text("", _vm->_smallFont, kTitleColour, 0, 0, 0);
-	titleAnim->addFrame(title, NULL);
+	_titleAnim = new Animation(_vm, kTitleText, 257, true);
+	_titleAnim->addFrame(new Text("", _vm->_smallFont, kTitleColour, 0, 0, 0), NULL);
+	_vm->_anims->insert(_titleAnim, false);
 
 	// Initialize animation for speech text
-	Animation *speechAnim = _vm->_anims->addText(kSpeechText, true);
-	Text *speech = new Text("", _vm->_bigFont, kFontColour1, 0, 0, 0);
-	speechAnim->addFrame(speech, NULL);
+	Animation *speechAnim = new Animation(_vm, kSpeechText, 257, true);
+	speechAnim->addFrame(new Text("", _vm->_bigFont, kFontColour1, 0, 0, 0), NULL);
+	_vm->_anims->insert(speechAnim, false);
 
 	// Initialize inventory animation.  _iconsArchive is never flushed.
 	const BAFile *f = _vm->_iconsArchive->getFile(13);
-	Animation *inventoryAnim = _vm->_anims->addAnimation(kInventorySprite, 255, false);
+	_inventoryAnim = new Animation(_vm, kInventorySprite, 255, false);
 	Sprite *inventorySprite = new Sprite(f->_data, f->_length, 0, 0, true);
-	inventoryAnim->addFrame(inventorySprite, NULL);
-	inventoryAnim->setRelative((kScreenWidth - inventorySprite->getWidth()) / 2,
+	_inventoryAnim->addFrame(inventorySprite, NULL);
+	_inventoryAnim->setRelative((kScreenWidth - inventorySprite->getWidth()) / 2,
 	                           (kScreenHeight - inventorySprite->getHeight()) / 2);
+	_vm->_anims->insert(_inventoryAnim, true);
 
 	for (uint i = 0; i < kDialogueLines; ++i) {
-		_dialogueAnims[i] = _vm->_anims->addText(kDialogueLinesID - i, true);
-		Text *dialogueLine = new Text("", _vm->_smallFont, kLineInactiveColour, 0, 0, 0);
-		_dialogueAnims[i]->addFrame(dialogueLine, NULL);
+		_dialogueAnims[i] = new Animation(_vm, kDialogueLinesID - i, 254, true);
+		_dialogueAnims[i]->addFrame(new Text("", _vm->_smallFont, kLineInactiveColour, 0, 0, 0), NULL);
 
-		_dialogueAnims[i]->setZ(254);
 		_dialogueAnims[i]->setRelative(1,
 		                      kScreenHeight - (i + 1) * _vm->_smallFont->getFontHeight());
+		_vm->_anims->insert(_dialogueAnims[i], false);
 
 		Text *text = reinterpret_cast<Text *>(_dialogueAnims[i]->getCurrentFrame());
 		text->setText("");
 	}
 
 	for (uint i = 0; i < _info._numItems; ++i) {
-		loadItem(i);
+		_items[i].load(i, _vm->_itemsArchive);
 	}
 
-	loadObject(kDragonObject);
+	_objects[kDragonObject].load(kDragonObject, _vm->_objectsArchive);
 
 	const GameObject *dragon = getObject(kDragonObject);
 	debugC(4, kDraciLogicDebugLevel, "Running init program for the dragon object...");
 	_vm->_script->run(dragon->_program, dragon->_init);
 
+	// Add overlays for the walking map and shortest/obliqued paths.
+	initWalkingOverlays();
+
 	// Make sure we enter the right room in start().
-	setRoomNum(kNoEscRoom);
+	setRoomNum(-1);
 	rememberRoomNumAsPrevious();
 	scheduleEnteringRoomUsingGate(_info._startRoom, 0);
 	_pushedNewRoom = _pushedNewGate = -1;
@@ -238,23 +243,21 @@
 	if (_vm->_mouse->lButtonPressed()) {
 		_vm->_mouse->lButtonSet(false);
 
-		if (_currentItem != kNoItem) {
+		if (_currentItem) {
 			putItem(_currentItem, 0);
-			_currentItem = kNoItem;
+			_currentItem = NULL;
 			updateOrdinaryCursor();
 		} else {
-			if (_objUnderCursor != kObjectNotFound) {
-				const GameObject *obj = &_objects[_objUnderCursor];
+			if (_objUnderCursor) {
+				_walkingState.setCallback(&_objUnderCursor->_program, _objUnderCursor->_look);
 
-				_walkingState.setCallback(&obj->_program, obj->_look);
-
-				if (obj->_imLook || !_currentRoom._heroOn) {
+				if (_objUnderCursor->_imLook || !_currentRoom._heroOn) {
 					_walkingState.callback();
 				} else {
-					if (obj->_lookDir == kDirectionLast) {
-						walkHero(x, y, obj->_lookDir);
+					if (_objUnderCursor->_lookDir == kDirectionLast) {
+						walkHero(x, y, _objUnderCursor->_lookDir);
 					} else {
-						walkHero(obj->_lookX, obj->_lookY, obj->_lookDir);
+						walkHero(_objUnderCursor->_lookX, _objUnderCursor->_lookY, _objUnderCursor->_lookDir);
 					}
 				}
 			} else {
@@ -267,19 +270,17 @@
 	if (_vm->_mouse->rButtonPressed()) {
 		_vm->_mouse->rButtonSet(false);
 
-		if (_objUnderCursor != kObjectNotFound) {
-			const GameObject *obj = &_objects[_objUnderCursor];
+		if (_objUnderCursor) {
+			if (_vm->_script->testExpression(_objUnderCursor->_program, _objUnderCursor->_canUse)) {
+				_walkingState.setCallback(&_objUnderCursor->_program, _objUnderCursor->_use);
 
-			if (_vm->_script->testExpression(obj->_program, obj->_canUse)) {
-				_walkingState.setCallback(&obj->_program, obj->_use);
-
-				if (obj->_imUse || !_currentRoom._heroOn) {
+				if (_objUnderCursor->_imUse || !_currentRoom._heroOn) {
 					_walkingState.callback();
 				} else {
-					if (obj->_useDir == kDirectionLast) {
-						walkHero(x, y, obj->_useDir);
+					if (_objUnderCursor->_useDir == kDirectionLast) {
+						walkHero(x, y, _objUnderCursor->_useDir);
 					} else {
-						walkHero(obj->_useX, obj->_useY, obj->_useDir);
+						walkHero(_objUnderCursor->_useX, _objUnderCursor->_useY, _objUnderCursor->_useDir);
 					}
 				}
 			} else {
@@ -309,14 +310,15 @@
 	// If we are in inventory mode, all the animations except game items'
 	// images will necessarily be paused so we can safely assume that any
 	// animation under the cursor (a value returned by
-	// AnimationManager::getTopAnimationID()) will be an item animation or
+	// AnimationManager::getTopAnimation()) will be an item animation or
 	// an overlay, for which we check. Item animations have their IDs
 	// calculated by offseting their itemID from the ID of the last "special"
 	// animation ID. In this way, we obtain its itemID.
-	if (_animUnderCursor != kOverlayImage && _animUnderCursor != kInventorySprite) {
-		_itemUnderCursor = kInventoryItemsID - _animUnderCursor;
+	if (_animUnderCursor != NULL && _animUnderCursor != _inventoryAnim && _animUnderCursor->getID() != kOverlayImage) {
+		_itemUnderCursor = getItem(kInventoryItemsID - _animUnderCursor->getID());
+		assert(_itemUnderCursor->_anim == _animUnderCursor);
 	} else {
-		_itemUnderCursor = kNoItem;
+		_itemUnderCursor = NULL;
 	}
 
 	// If the user pressed the left mouse button
@@ -325,13 +327,11 @@
 
 		// If there is an inventory item under the cursor and we aren't
 		// holding any item, run its look GPL program
-		if (_itemUnderCursor != kNoItem && _currentItem == kNoItem) {
-			const GameItem *item = &_items[_itemUnderCursor];
-
-			_vm->_script->run(item->_program, item->_look);
+		if (_itemUnderCursor && !_currentItem) {
+			_vm->_script->run(_itemUnderCursor->_program, _itemUnderCursor->_look);
 		// Otherwise, if we are holding an item, try to place it inside the
 		// inventory
-		} else if (_currentItem != kNoItem) {
+		} else if (_currentItem) {
 			const int column = scummvm_lround(
 				(_vm->_mouse->getPosX() - kInventoryX + kInventoryItemWidth / 2.) /
 				kInventoryItemWidth) - 1;
@@ -342,22 +342,22 @@
 			putItem(_currentItem, index);
 
 			// Remove it from our hands
-			_currentItem = kNoItem;
+			_currentItem = NULL;
 		}
 	} else if (_vm->_mouse->rButtonPressed()) {
 		_vm->_mouse->rButtonSet(false);
 
 		// If we right-clicked outside the inventory, close it
-		if (_animUnderCursor != kInventorySprite && _itemUnderCursor == kNoItem) {
+		if (_animUnderCursor != _inventoryAnim && !_itemUnderCursor) {
 			inventoryDone();
 
 		// If there is an inventory item under our cursor
-		} else if (_itemUnderCursor != kNoItem) {
+		} else if (_itemUnderCursor) {
 			// Again, we have two possibilities:
 
 			// The first is that there is no item in our hands.
 			// In that case, just take the inventory item from the inventory.
-			if (_currentItem == kNoItem) {
+			if (!_currentItem) {
 				_currentItem = _itemUnderCursor;
 				removeItem(_itemUnderCursor);
 
@@ -366,10 +366,8 @@
 			// which will check if the two items are combinable and, finally,
 			// run the use script for the item.
 			} else {
-				const GameItem *item = &_items[_itemUnderCursor];
-
-				if (_vm->_script->testExpression(item->_program, item->_canUse)) {
-					_vm->_script->run(item->_program, item->_use);
+				if (_vm->_script->testExpression(_itemUnderCursor->_program, _itemUnderCursor->_canUse)) {
+					_vm->_script->run(_itemUnderCursor->_program, _itemUnderCursor->_use);
 				}
 			}
 			updateInventoryCursor();
@@ -386,7 +384,7 @@
 	for (int i = 0; i < kDialogueLines; ++i) {
 		text = reinterpret_cast<Text *>(_dialogueAnims[i]->getCurrentFrame());
 
-		if (_animUnderCursor == _dialogueAnims[i]->getID()) {
+		if (_animUnderCursor == _dialogueAnims[i]) {
 			text->setColour(kLineActiveColour);
 		} else {
 			text->setColour(kLineInactiveColour);
@@ -509,9 +507,9 @@
 			// corresponding to it
 			int x = _vm->_mouse->getPosX();
 			int y = _vm->_mouse->getPosY();
-			_animUnderCursor = _vm->_anims->getTopAnimationID(x, y);
+			_animUnderCursor = _vm->_anims->getTopAnimation(x, y);
 			_objUnderCursor = getObjectWithAnimation(_animUnderCursor);
-			debugC(5, kDraciLogicDebugLevel, "Anim under cursor: %d", _animUnderCursor);
+			debugC(5, kDraciLogicDebugLevel, "Anim under cursor: %d", _animUnderCursor ? _animUnderCursor->getID() : -1);
 
 			switch (_loopStatus) {
 			case kStatusOrdinary:
@@ -547,9 +545,9 @@
 	bool mouseChanged = false;
 
 	// If there is no game object under the cursor, try using the room itself
-	if (_objUnderCursor == kObjectNotFound) {
+	if (!_objUnderCursor) {
 		if (_vm->_script->testExpression(_currentRoom._program, _currentRoom._canUse)) {
-			if (_currentItem == kNoItem) {
+			if (!_currentItem) {
 				_vm->_mouse->setCursorType(kHighlightedCursor);
 			} else {
 				_vm->_mouse->loadItemCursor(_currentItem, true);
@@ -558,14 +556,12 @@
 		}
 	// If there *is* a game object under the cursor, update the cursor image
 	} else {
-		const GameObject *obj = &_objects[_objUnderCursor];
-
 		// If there is no walking direction set on the object (i.e. the object
 		// is not a gate / exit), test whether it can be used and, if so,
 		// update the cursor image (highlight it).
-		if (obj->_walkDir == 0) {
-			if (_vm->_script->testExpression(obj->_program, obj->_canUse)) {
-				if (_currentItem == kNoItem) {
+		if (_objUnderCursor->_walkDir == 0) {
+			if (_vm->_script->testExpression(_objUnderCursor->_program, _objUnderCursor->_canUse)) {
+				if (!_currentItem) {
 					_vm->_mouse->setCursorType(kHighlightedCursor);
 				} else {
 					_vm->_mouse->loadItemCursor(_currentItem, true);
@@ -575,14 +571,14 @@
 		// If the walking direction *is* set, the game object is a gate, so update
 		// the cursor image to the appropriate arrow.
 		} else {
-			_vm->_mouse->setCursorType((CursorType)obj->_walkDir);
+			_vm->_mouse->setCursorType((CursorType)_objUnderCursor->_walkDir);
 			mouseChanged = true;
 		}
 	}
 	// Load the appropriate cursor (item image if an item is held or ordinary cursor
 	// if not)
 	if (!mouseChanged) {
-		if (_currentItem == kNoItem) {
+		if (!_currentItem) {
 			_vm->_mouse->setCursorType(kNormalCursor);
 		} else {
 			_vm->_mouse->loadItemCursor(_currentItem, false);
@@ -594,11 +590,9 @@
 	// Fetch mouse coordinates
 	bool mouseChanged = false;
 
-	if (_itemUnderCursor != kNoItem) {
-		const GameItem *item = &_items[_itemUnderCursor];
-
-		if (_vm->_script->testExpression(item->_program, item->_canUse)) {
-			if (_currentItem == kNoItem) {
+	if (_itemUnderCursor) {
+		if (_vm->_script->testExpression(_itemUnderCursor->_program, _itemUnderCursor->_canUse)) {
+			if (!_currentItem) {
 				_vm->_mouse->setCursorType(kHighlightedCursor);
 			} else {
 				_vm->_mouse->loadItemCursor(_currentItem, true);
@@ -607,7 +601,7 @@
 		}
 	}
 	if (!mouseChanged) {
-		if (_currentItem == kNoItem) {
+		if (!_currentItem) {
 			_vm->_mouse->setCursorType(kNormalCursor);
 		} else {
 			_vm->_mouse->loadItemCursor(_currentItem, false);
@@ -621,101 +615,85 @@
 	const int smallFontHeight = _vm->_smallFont->getFontHeight();
 
 	// Fetch the dedicated objects' title animation / current frame
-	Animation *titleAnim = _vm->_anims->getAnimation(kTitleText);
-	Text *title = reinterpret_cast<Text *>(titleAnim->getCurrentFrame());
+	Text *title = reinterpret_cast<Text *>(_titleAnim->getCurrentFrame());
 
 	// Mark dirty rectangle to delete the previous text
-	titleAnim->markDirtyRect(surface);
+	_titleAnim->markDirtyRect(surface);
 
 	if (_loopStatus == kStatusInventory) {
 		// If there is no item under the cursor, delete the title.
 		// Otherwise, show the item's title.
-		if (_itemUnderCursor == kNoItem) {
-			title->setText("");
-		} else {
-			const GameItem *item = &_items[_itemUnderCursor];
-			title->setText(item->_title);
-		}
+		title->setText(_itemUnderCursor ? _itemUnderCursor->_title : "");
 	} else {
 		// If there is no object under the cursor, delete the title.
 		// Otherwise, show the object's title.
-		if (_objUnderCursor == kObjectNotFound) {
-			title->setText("");
-		} else {
-			const GameObject *obj = &_objects[_objUnderCursor];
-			title->setText(obj->_title);
-		}
+		title->setText(_objUnderCursor ? _objUnderCursor->_title : "");
 	}
 
 	// Move the title to the correct place (just above the cursor)
 	int newX = surface->centerOnX(x, title->getWidth());
 	int newY = surface->putAboveY(y - smallFontHeight / 2, title->getHeight());
-	titleAnim->setRelative(newX, newY);
+	_titleAnim->setRelative(newX, newY);
 
 	// If we are currently playing the title, mark it dirty so it gets updated.
 	// Otherwise, start playing the title animation.
-	if (titleAnim->isPlaying()) {
-		titleAnim->markDirtyRect(surface);
+	if (_titleAnim->isPlaying()) {
+		_titleAnim->markDirtyRect(surface);
 	} else {
-		_vm->_anims->play(titleAnim->getID());
+		_titleAnim->play();
 	}
 }
 
-int Game::getObjectWithAnimation(int animID) const {
+const GameObject *Game::getObjectWithAnimation(const Animation *anim) const {
 	for (uint i = 0; i < _info._numObjects; ++i) {
 		GameObject *obj = &_objects[i];
-
-		for (uint j = 0; j < obj->_anim.size(); ++j) {
-			if (obj->_anim[j] == animID) {
-				return i;
-			}
+		if (obj->_playingAnim >= 0 && obj->_anim[obj->_playingAnim] == anim) {
+			return obj;
 		}
 	}
 
-	return kObjectNotFound;
+	return NULL;
 }
 
-void Game::removeItem(int itemID) {
+void Game::removeItem(GameItem *item) {
 	for (uint i = 0; i < kInventorySlots; ++i) {
-		if (_inventory[i] == itemID) {
-			_inventory[i] = kNoItem;
-			_vm->_anims->stop(kInventoryItemsID - itemID);
+		if (_inventory[i] == item) {
+			_inventory[i] = NULL;
+			item->_anim->stop();
 			break;
 		}
 	}
 }
 
-void Game::putItem(int itemID, int position) {
-	if (itemID == kNoItem)
+void Game::loadItemAnimation(GameItem *item) {
+	if (item->_anim)
 		return;
+	item->_anim = new Animation(_vm, kInventoryItemsID - item->_absNum, 256, false);
+	_vm->_anims->insert(item->_anim, false);
+	// _itemImagesArchive is never flushed.
+	const BAFile *img = _vm->_itemImagesArchive->getFile(2 * item->_absNum);
+	item->_anim->addFrame(new Sprite(img->_data, img->_length, 0, 0, true), NULL);
+}
 
-	if (position >= 0 &&
-		position < kInventorySlots &&
-		(_inventory[position] == kNoItem || _inventory[position] == itemID)) {
-		_inventory[position] = itemID;
-	} else {
-		for (int i = 0; i < kInventorySlots; ++i) {
-			int pos = (position + i) % kInventorySlots;
-			if (_inventory[pos] == kNoItem) {
-				_inventory[pos] = itemID;
-				position = pos;
-				break;
-			}
+void Game::putItem(GameItem *item, int position) {
+	if (!item)
+		return;
+	assert(position >= 0);
+
+	for (int i = 0; i < kInventorySlots; ++i) {
+		int pos = (position + i) % kInventorySlots;
+		if (!_inventory[pos] || _inventory[pos] == item) {
+			_inventory[pos] = item;
+			position = pos;
+			break;
 		}
 	}
 
 	const int line = position / kInventoryColumns + 1;
 	const int column = position % kInventoryColumns + 1;
 
-	const int anim_id = kInventoryItemsID - itemID;
-	Animation *anim = _vm->_anims->getAnimation(anim_id);
-	if (!anim) {
-		anim = _vm->_anims->addItem(anim_id, false);
-		// _itemImagesArchive is never flushed.
-		const BAFile *img = _vm->_itemImagesArchive->getFile(2 * itemID);
-		Sprite *sp = new Sprite(img->_data, img->_length, 0, 0, true);
-		anim->addFrame(sp, NULL);
-	}
+	loadItemAnimation(item);
+	Animation *anim = item->_anim;
 	Drawable *frame = anim->getCurrentFrame();
 
 	const int x = kInventoryX +
@@ -728,7 +706,7 @@
 	              (kInventoryItemHeight / 2) -
 	              (frame->getHeight() / 2);
 
-	debug(2, "itemID: %d position: %d line: %d column: %d x: %d y: %d", itemID, position, line, column, x, y);
+	debug(2, "itemID: %d position: %d line: %d column: %d x: %d y: %d", item->_absNum, position, line, column, x, y);
 
 	anim->setRelative(x, y);
 
@@ -736,7 +714,7 @@
 	// upon returning it to its slot but *not* in other modes because it should be
 	// invisible then (along with the inventory)
 	if (_loopStatus == kStatusInventory && _loopSubstatus == kOuterLoop) {
-		_vm->_anims->play(anim_id);
+		anim->play();
 	}
 }
 
@@ -764,24 +742,24 @@
 
 	_vm->_anims->unpauseAnimations();
 
-	_vm->_anims->stop(kInventorySprite);
+	_inventoryAnim->stop();
 
 	for (uint i = 0; i < kInventorySlots; ++i) {
-		if (_inventory[i] != kNoItem) {
-			_vm->_anims->stop(kInventoryItemsID - _inventory[i]);
+		if (_inventory[i]) {
+			_inventory[i]->_anim->stop();
 		}
 	}
 
 	// Reset item under cursor
-	_itemUnderCursor = kNoItem;
+	_itemUnderCursor = NULL;
 }
 
 void Game::inventoryDraw() {
-	_vm->_anims->play(kInventorySprite);
+	_inventoryAnim->play();
 
 	for (uint i = 0; i < kInventorySlots; ++i) {
-		if (_inventory[i] != kNoItem) {
-			_vm->_anims->play(kInventoryItemsID - _inventory[i]);
+		if (_inventory[i]) {
+			_inventory[i]->_anim->play();
 		}
 	}
 }
@@ -834,7 +812,7 @@
 	} while (!_dialogueExit);
 
 	dialogueDone();
-	_currentDialogue = kNoDialogue;
+	_currentDialogue = -1;
 }
 
 int Game::dialogueDraw() {
@@ -878,7 +856,7 @@
 
 		bool notDialogueAnim = true;
 		for (uint j = 0; j < kDialogueLines; ++j) {
-			if (_dialogueAnims[j]->getID() == _animUnderCursor) {
+			if (_dialogueAnims[j] == _animUnderCursor) {
 				notDialogueAnim = false;
 				break;
 			}
@@ -887,7 +865,7 @@
 		if (notDialogueAnim) {
 			ret = -1;
 		} else {
-			ret = _dialogueAnims[0]->getID() - _animUnderCursor;
+			ret = kDialogueLinesID - _animUnderCursor->getID();
 		}
 	} else {
 		ret = _dialogueLinesNum - 1;
@@ -932,7 +910,7 @@
 	}
 
 	for (uint i = 0; i < kDialogueLines; ++i) {
-		_vm->_anims->play(_dialogueAnims[i]->getID());
+		_dialogueAnims[i]->play();
 	}
 
 	setLoopStatus(kStatusDialogue);
@@ -942,7 +920,7 @@
 
 void Game::dialogueDone() {
 	for (uint i = 0; i < kDialogueLines; ++i) {
-		_vm->_anims->stop(_dialogueAnims[i]->getID());
+		_dialogueAnims[i]->stop();
 	}
 
 	delete _dialogueArchive;
@@ -963,28 +941,26 @@
 }
 
 int Game::playHeroAnimation(int anim_index) {
-	const GameObject *dragon = getObject(kDragonObject);
-	const int current_anim_index = playingObjectAnimation(dragon);
-	const int animID = dragon->_anim[anim_index];
-	Animation *anim = _vm->_anims->getAnimation(animID);
+	GameObject *dragon = getObject(kDragonObject);
+	const int current_anim_index = dragon->_playingAnim;
+	Animation *anim = dragon->_anim[anim_index];
 
 	if (anim_index == current_anim_index) {
 		anim->markDirtyRect(_vm->_screen->getSurface());
 	} else {
-		stopObjectAnimations(dragon);
+		dragon->stopAnim();
 	}
 	positionAnimAsHero(anim);
 	if (anim_index == current_anim_index) {
 		anim->markDirtyRect(_vm->_screen->getSurface());
 	} else {
-		_vm->_anims->play(animID);
+		dragon->playAnim(anim_index);
 	}
 
 	return anim->currentFrameNum();
 }
 
-void Game::redrawWalkingPath(int id, byte colour, const WalkingPath &path) {
-	Animation *anim = _vm->_anims->getAnimation(id);
+void Game::redrawWalkingPath(Animation *anim, byte colour, const WalkingPath &path) {
 	Sprite *ov = _walkingMap.newOverlayFromPath(path, colour);
 	delete anim->getFrame(0);
 	anim->replaceFrame(0, ov, NULL);
@@ -1016,8 +992,8 @@
 	_walkingMap.obliquePath(shortestPath, &obliquePath);
 	debugC(2, kDraciWalkingDebugLevel, "Walking path lengths: shortest=%d oblique=%d", shortestPath.size(), obliquePath.size());
 	if (_vm->_showWalkingMap) {
-		redrawWalkingPath(kWalkingShortestPathOverlay, kWalkingShortestPathOverlayColour, shortestPath);
-		redrawWalkingPath(kWalkingObliquePathOverlay, kWalkingObliquePathOverlayColour, obliquePath);
+		redrawWalkingPath(_walkingShortestPathOverlay, kWalkingShortestPathOverlayColour, shortestPath);
+		redrawWalkingPath(_walkingObliquePathOverlay, kWalkingObliquePathOverlayColour, obliquePath);
 	}
 
 	// Start walking.  Walking will be gradually advanced by
@@ -1030,123 +1006,30 @@
 		_walkingMap.getDelta(), obliquePath);
 }
 
-void Game::loadItem(int itemID) {
-	const BAFile *f = _vm->_itemsArchive->getFile(itemID * 3);
-	Common::MemoryReadStream itemReader(f->_data, f->_length);
+void Game::initWalkingOverlays() {
+	_walkingMapOverlay = new Animation(_vm, kWalkingMapOverlay, 256, _vm->_showWalkingMap);
+	_walkingMapOverlay->addFrame(NULL, NULL);	// rewritten below by loadWalkingMap()
+	_vm->_anims->insert(_walkingMapOverlay, true);
 
-	GameItem *item = _items + itemID;
-
-	item->_init = itemReader.readSint16LE();
-	item->_look = itemReader.readSint16LE();
-	item->_use = itemReader.readSint16LE();
-	item->_canUse = itemReader.readSint16LE();
-	item->_imInit = itemReader.readByte();
-	item->_imLook = itemReader.readByte();
-	item->_imUse = itemReader.readByte();
-
-	f = _vm->_itemsArchive->getFile(itemID * 3 + 1);
-
-	// The first byte is the length of the string
-	item->_title = Common::String((const char *)f->_data + 1, f->_length - 1);
-	assert(f->_data[0] == item->_title.size());
-
-	f = _vm->_itemsArchive->getFile(itemID * 3 + 2);
-
-	item->_program._bytecode = f->_data;
-	item->_program._length = f->_length;
+	_walkingShortestPathOverlay = new Animation(_vm, kWalkingShortestPathOverlay, 257, _vm->_showWalkingMap);
+	_walkingObliquePathOverlay = new Animation(_vm, kWalkingObliquePathOverlay, 258, _vm->_showWalkingMap);
+	WalkingPath emptyPath;
+	_walkingShortestPathOverlay->addFrame(_walkingMap.newOverlayFromPath(emptyPath, 0), NULL);
+	_walkingObliquePathOverlay->addFrame(_walkingMap.newOverlayFromPath(emptyPath, 0), NULL);
+	_vm->_anims->insert(_walkingShortestPathOverlay, true);
+	_vm->_anims->insert(_walkingObliquePathOverlay, true);
 }
 
-void Game::loadRoom(int roomNum) {
-	const BAFile *f;
-	f = _vm->_roomsArchive->getFile(roomNum * 4);
-	Common::MemoryReadStream roomReader(f->_data, f->_length);
-
-	roomReader.readUint32LE(); // Pointer to room program, not used
-	roomReader.readUint16LE(); // Program length, not used
-	roomReader.readUint32LE(); // Pointer to room title, not used
-
-	// Music will be played by the GPL2 command startMusic when needed.
-	setMusicTrack(roomReader.readByte());
-
-	_currentRoom._mapID = roomReader.readByte() - 1;
-	_currentRoom._palette = roomReader.readByte() - 1;
-	_currentRoom._numOverlays = roomReader.readSint16LE();
-	_currentRoom._init = roomReader.readSint16LE();
-	_currentRoom._look = roomReader.readSint16LE();
-	_currentRoom._use = roomReader.readSint16LE();
-	_currentRoom._canUse = roomReader.readSint16LE();
-	_currentRoom._imInit = roomReader.readByte();
-	_currentRoom._imLook = roomReader.readByte();
-	_currentRoom._imUse = roomReader.readByte();
-	_currentRoom._mouseOn = roomReader.readByte();
-	_currentRoom._heroOn = roomReader.readByte();
-
-	// Read in pers0 and persStep (stored as 6-byte Pascal reals)
-	byte real[6];
-
-	for (int i = 5; i >= 0; --i) {
-		real[i] = roomReader.readByte();
-	}
-
-	_currentRoom._pers0 = real_to_double(real);
-
-	for (int i = 5; i >= 0; --i) {
-		real[i] = roomReader.readByte();
-	}
-
-	_currentRoom._persStep = real_to_double(real);
-
-	_currentRoom._escRoom = roomReader.readByte() - 1;
-	_currentRoom._numGates = roomReader.readByte();
-
-	debugC(4, kDraciLogicDebugLevel, "Music: %d", getMusicTrack());
-	debugC(4, kDraciLogicDebugLevel, "Map: %d", getMapID());
-	debugC(4, kDraciLogicDebugLevel, "Palette: %d", _currentRoom._palette);
-	debugC(4, kDraciLogicDebugLevel, "Overlays: %d", _currentRoom._numOverlays);
-	debugC(4, kDraciLogicDebugLevel, "Init: %d", _currentRoom._init);
-	debugC(4, kDraciLogicDebugLevel, "Look: %d", _currentRoom._look);
-	debugC(4, kDraciLogicDebugLevel, "Use: %d", _currentRoom._use);
-	debugC(4, kDraciLogicDebugLevel, "CanUse: %d", _currentRoom._canUse);
-	debugC(4, kDraciLogicDebugLevel, "ImInit: %d", _currentRoom._imInit);
-	debugC(4, kDraciLogicDebugLevel, "ImLook: %d", _currentRoom._imLook);
-	debugC(4, kDraciLogicDebugLevel, "ImUse: %d", _currentRoom._imUse);
-	debugC(4, kDraciLogicDebugLevel, "MouseOn: %d", _currentRoom._mouseOn);
-	debugC(4, kDraciLogicDebugLevel, "HeroOn: %d", _currentRoom._heroOn);
-	debugC(4, kDraciLogicDebugLevel, "Pers0: %f", _currentRoom._pers0);
-	debugC(4, kDraciLogicDebugLevel, "PersStep: %f", _currentRoom._persStep);
-	debugC(4, kDraciLogicDebugLevel, "EscRoom: %d", _currentRoom._escRoom);
-	debugC(4, kDraciLogicDebugLevel, "Gates: %d", _currentRoom._numGates);
-
-	// Read in the gates' numbers
-	_currentRoom._gates.clear();
-	for (uint i = 0; i < _currentRoom._numGates; ++i) {
-		_currentRoom._gates.push_back(roomReader.readSint16LE());
-	}
-
-	// Add overlays for the walking map and shortest/obliqued paths.
-	Animation *map = _vm->_anims->addAnimation(kWalkingMapOverlay, 256, _vm->_showWalkingMap);
-	map->addFrame(NULL, NULL);	// rewritten below by loadWalkingMap()
-
-	Animation *sPath = _vm->_anims->addAnimation(kWalkingShortestPathOverlay, 257, _vm->_showWalkingMap);
-	Animation *oPath = _vm->_anims->addAnimation(kWalkingObliquePathOverlay, 258, _vm->_showWalkingMap);
-	WalkingPath emptyPath;
-	Sprite *ov = _walkingMap.newOverlayFromPath(emptyPath, 0);
-	sPath->addFrame(ov, NULL);
-	ov = _walkingMap.newOverlayFromPath(emptyPath, 0);
-	oPath->addFrame(ov, NULL);
-
-	// Load the walking map
-	loadWalkingMap(getMapID());
-
+void Game::loadRoomObjects() {
 	// Load the room's objects
 	for (uint i = 0; i < _info._numObjects; ++i) {
 		debugC(7, kDraciLogicDebugLevel,
 			"Checking if object %d (%d) is at the current location (%d)", i,
-			_objects[i]._location, roomNum);
+			_objects[i]._location, getRoomNum());
 
-		if (_objects[i]._location == roomNum) {
-			debugC(6, kDraciLogicDebugLevel, "Loading object %d from room %d", i, roomNum);
-			loadObject(i);
+		if (_objects[i]._location == getRoomNum()) {
+			debugC(6, kDraciLogicDebugLevel, "Loading object %d from room %d", i, getRoomNum());
+			_objects[i].load(i, _vm->_objectsArchive);
 		}
 	}
 
@@ -1154,7 +1037,7 @@
 	// We can't do this in the above loop because some objects' scripts reference
 	// other objects that may not yet be loaded
 	for (uint i = 0; i < _info._numObjects; ++i) {
-		if (_objects[i]._location == roomNum) {
+		if (_objects[i]._location == getRoomNum()) {
 			const GameObject *obj = getObject(i);
 			debugC(6, kDraciLogicDebugLevel,
 				"Running init program for object %d (offset %d)", i, obj->_init);
@@ -1162,152 +1045,34 @@
 		}
 	}
 
-	// Load the room's GPL program and run the init part
-	f = _vm->_roomsArchive->getFile(roomNum * 4 + 3);
-	_currentRoom._program._bytecode = f->_data;
-	_currentRoom._program._length = f->_length;
-
+	// Run the init part of the GPL program
 	debugC(4, kDraciLogicDebugLevel, "Running room init program...");
 	_vm->_script->run(_currentRoom._program, _currentRoom._init);
-
-	// Set room palette
-	f = _vm->_paletteArchive->getFile(_currentRoom._palette);
-	_vm->_screen->setPalette(f->_data, 0, kNumColours);
 }
 
-int Game::loadAnimation(uint animNum, uint z) {
-	// Make double-sure that an animation isn't loaded more than twice,
-	// otherwise horrible things happen in the AnimationManager, because
-	// they use a simple link-list without duplicate checking.  This should
-	// never happen unless there is a bug in the game, because all GPL2
-	// commands are guarded.
-	assert(!_vm->_anims->getAnimation(animNum));
-
-	const BAFile *animFile = _vm->_animationsArchive->getFile(animNum);
-	Common::MemoryReadStream animationReader(animFile->_data, animFile->_length);
-
-	uint numFrames = animationReader.readByte();
-
-	// The following two flags are ignored by the played.  Memory logic was
-	// a hint to the old player whether it should cache the sprites or load
-	// them on demand.  We have 1 memory manager and ignore these hints.
-	animationReader.readByte();
-	// The disable erasing field is just a (poor) optimization flag that
-	// turns of drawing the background underneath the sprite.  By reading
-	// the source code of the old player, I'm not sure if that would ever
-	// have worked.  There are only 6 animations in the game with this flag
-	// true.  All of them have just 1 animation phase and they are used to
-	// patch a part of the original background by a new sprite.  This
-	// should work with the default logic as well---just play this
-	// animation on top of the background.  Since the only meaning of the
-	// flag was optimization, ignoring should be OK even without dipping
-	// into details.
-	animationReader.readByte();
-	const bool cyclic = animationReader.readByte();
-	const bool relative = animationReader.readByte();
-
-	Animation *anim = _vm->_anims->addAnimation(animNum, z, false);
-
-	anim->setLooping(cyclic);
-
-	for (uint i = 0; i < numFrames; ++i) {
-		uint spriteNum = animationReader.readUint16LE() - 1;
-		int x = animationReader.readSint16LE();
-		int y = animationReader.readSint16LE();
-		uint scaledWidth = animationReader.readUint16LE();
-		uint scaledHeight = animationReader.readUint16LE();
-		byte mirror = animationReader.readByte();
-		int sample = animationReader.readUint16LE() - 1;
-		uint freq = animationReader.readUint16LE();
-		uint delay = animationReader.readUint16LE();
-
-		// _spritesArchive is flushed when entering a room.  All
-		// scripts in a room are responsible for loading their animations.
-		const BAFile *spriteFile = _vm->_spritesArchive->getFile(spriteNum);
-		Sprite *sp = new Sprite(spriteFile->_data, spriteFile->_length,
-			relative ? 0 : x, relative ? 0 : y, true);
-
-		// Some frames set the scaled dimensions to 0 even though other frames
-		// from the same animations have them set to normal values
-		// We work around this by assuming it means no scaling is necessary
-		if (scaledWidth == 0) {
-			scaledWidth = sp->getWidth();
-		}
-
-		if (scaledHeight == 0) {
-			scaledHeight = sp->getHeight();
-		}
-
-		sp->setScaled(scaledWidth, scaledHeight);
-
-		if (mirror)
-			sp->setMirrorOn();
-
-		sp->setDelay(delay * 10);
-
-		const SoundSample *sam = _vm->_soundsArchive->getSample(sample, freq);
-
-		anim->addFrame(sp, sam);
-		if (relative) {
-			anim->makeLastFrameRelative(x, y);
-		}
-	}
-
-	return animNum;
-}
-
-void Game::loadObject(uint objNum) {
-	const BAFile *file;
-
-	file = _vm->_objectsArchive->getFile(objNum * 3);
-	Common::MemoryReadStream objReader(file->_data, file->_length);
-
-	GameObject *obj = _objects + objNum;
-
-	obj->_init = objReader.readUint16LE();
-	obj->_look = objReader.readUint16LE();
-	obj->_use = objReader.readUint16LE();
-	obj->_canUse = objReader.readUint16LE();
-	obj->_imInit = objReader.readByte();
-	obj->_imLook = objReader.readByte();
-	obj->_imUse = objReader.readByte();
-	obj->_walkDir = objReader.readByte() - 1;
-	obj->_z = objReader.readByte();
-	objReader.readUint16LE(); // idxSeq field, not used
-	objReader.readUint16LE(); // numSeq field, not used
-	obj->_lookX = objReader.readUint16LE();
-	obj->_lookY = objReader.readUint16LE();
-	obj->_useX = objReader.readUint16LE();
-	obj->_useY = objReader.readUint16LE();
-	obj->_lookDir = static_cast<SightDirection> (objReader.readByte());
-	obj->_useDir = static_cast<SightDirection> (objReader.readByte());
-
-	obj->_absNum = objNum;
-
-	file = _vm->_objectsArchive->getFile(objNum * 3 + 1);
-
-	// The first byte of the file is the length of the string (without the length)
-	assert(file->_length - 1 == file->_data[0]);
-
-	obj->_title = Common::String((char *)(file->_data+1), file->_length-1);
-
-	file = _vm->_objectsArchive->getFile(objNum * 3 + 2);
-	obj->_program._bytecode = file->_data;
-	obj->_program._length = file->_length;
-}
-
 void Game::loadWalkingMap(int mapID) {
 	const BAFile *f;
 	f = _vm->_walkingMapsArchive->getFile(mapID);
 	_walkingMap.load(f->_data, f->_length);
 
-	Animation *anim = _vm->_anims->getAnimation(kWalkingMapOverlay);
 	Sprite *ov = _walkingMap.newOverlayFromMap(kWalkingMapOverlayColour);
-	delete anim->getFrame(0);
-	anim->replaceFrame(0, ov, NULL);
-	anim->markDirtyRect(_vm->_screen->getSurface());
+	delete _walkingMapOverlay->getFrame(0);
+	_walkingMapOverlay->replaceFrame(0, ov, NULL);
+	_walkingMapOverlay->markDirtyRect(_vm->_screen->getSurface());
 }
 
+void Game::switchWalkingAnimations(bool enabled) {
+	if (enabled) {
+		_walkingMapOverlay->play();
+		_walkingShortestPathOverlay->play();
+		_walkingObliquePathOverlay->play();
+	} else {
+		_walkingMapOverlay->stop();
+		_walkingShortestPathOverlay->stop();
+		_walkingObliquePathOverlay->stop();
+	}
+}
+
 void Game::loadOverlays() {
 	uint x, y, z, num;
 
@@ -1328,7 +1093,11 @@
 		overlayFile = _vm->_overlaysArchive->getFile(num);
 		Sprite *sp = new Sprite(overlayFile->_data, overlayFile->_length, x, y, true);
 
-		_vm->_anims->addOverlay(sp, z);
+		Animation *anim = new Animation(_vm, kOverlayImage, z, true);
+		anim->addFrame(sp, NULL);
+		// Since this is an overlay, we don't need it to be deleted
+		// when the GPL Release command is invoked
+		_vm->_anims->insert(anim, false);
 	}
 
 	_vm->_screen->getSurface()->markDirty();
@@ -1337,27 +1106,12 @@
 void Game::deleteObjectAnimations() {
 	for (uint i = 0; i < _info._numObjects; ++i) {
 		GameObject *obj = &_objects[i];
-
 		if (i != 0 && (obj->_location == getPreviousRoomNum())) {
-			for (uint j = 0; j < obj->_anim.size(); ++j) {
-					_vm->_anims->deleteAnimation(obj->_anim[j]);
-			}
-			obj->_anim.clear();
+			obj->deleteAnims();
 		}
 	}
 }
 
-int Game::playingObjectAnimation(const GameObject *obj) const {
-	for (uint i = 0; i < obj->_anim.size(); ++i) {
-		const int animID = obj->_anim[i];
-		const Animation *anim = _vm->_anims->getAnimation(animID);
-		if (anim && anim->isPlaying()) {
-			return i;
-		}
-	}
-	return -1;
-}
-
 bool Game::enterNewRoom() {
 	if (_newRoom == getRoomNum() && !isReloaded()) {
 		// If the game has been reloaded, force reloading all animations.
@@ -1386,22 +1140,13 @@
 
 	_vm->_anims->deleteOverlays();
 
-	// Delete walking map testing overlay
-	_vm->_anims->deleteAnimation(kWalkingMapOverlay);
-	_vm->_anims->deleteAnimation(kWalkingShortestPathOverlay);
-	_vm->_anims->deleteAnimation(kWalkingObliquePathOverlay);
+	GameObject *dragon = getObject(kDragonObject);
+	dragon->stopAnim();
 
-	// TODO: Make objects capable of stopping their own animations
-	const GameObject *dragon = getObject(kDragonObject);
-	stopObjectAnimations(dragon);
-
 	// Remember the previous room for returning back from the map.
 	rememberRoomNumAsPrevious();
 	deleteObjectAnimations();
 
-	// Set the current room to the new value
-	_currentRoom._roomNum = _newRoom;
-
 	// Before setting these variables we have to convert the values to 1-based indexing
 	// because this is how everything is stored in the data files
 	_variables[0] = _newGate + 1;
@@ -1425,16 +1170,22 @@
 	// on by pressing Escape in the intro or in the map room.
 	_vm->_script->endCurrentProgram(false);
 
-	loadRoom(_newRoom);
+	_currentRoom.load(_newRoom, _vm->_roomsArchive);
+	loadWalkingMap(getMapID());
+	loadRoomObjects();
 	loadOverlays();
 
+	// Set room palette
+	const BAFile *f;
+	f = _vm->_paletteArchive->getFile(_currentRoom._palette);
+	_vm->_screen->setPalette(f->_data, 0, kNumColours);
+
 	// Clean the mouse and animation title.  It gets first updated in
 	// loop(), hence if the hero walks during the initialization scripts,
 	// the old values would remain otherwise.
 	_vm->_mouse->setCursorType(kNormalCursor);
-	Animation *titleAnim = _vm->_anims->getAnimation(kTitleText);
-	titleAnim->markDirtyRect(_vm->_screen->getSurface());
-	Text *title = reinterpret_cast<Text *>(titleAnim->getCurrentFrame());
+	_titleAnim->markDirtyRect(_vm->_screen->getSurface());
+	Text *title = reinterpret_cast<Text *>(_titleAnim->getCurrentFrame());
 	title->setText("");
 
 	// Run the program for the gate the dragon came through
@@ -1557,24 +1308,20 @@
 	for (uint i = 0; i < getNumObjects(); ++i) {
 		GameObject *obj = &_objects[i];
 
-		for (uint j = 0; j < obj->_anim.size(); ++j) {
-			Animation *anim;
-
-			anim = _vm->_anims->getAnimation(obj->_anim[j]);
-			if (anim != NULL && anim->getIndex() > lastAnimIndex)
-				obj->_anim.remove_at(j--);
+		for (int j = obj->_anim.size() - 1; j >= 0; --j) {
+			Animation *anim = obj->_anim[j];
+			if (anim->getIndex() > lastAnimIndex) {
+				obj->_anim.remove_at(j);
+				if (j == obj->_playingAnim) {
+					obj->_playingAnim = -1;
+				}
+			}
 		}
 	}
 
 	_vm->_anims->deleteAfterIndex(lastAnimIndex);
 }
 
-void Game::stopObjectAnimations(const GameObject *obj) {
-	for (uint i = 0; i < obj->_anim.size(); ++i) {
-		_vm->_anims->stop(obj->_anim[i]);
-	}
-}
-
 Game::~Game() {
 	delete[] _persons;
 	delete[] _variables;
@@ -1599,7 +1346,14 @@
 	}
 
 	for (int i = 0; i < kInventorySlots; ++i) {
-		s.syncAsSint16LE(_inventory[i]);
+		if (s.isSaving()) {
+			int itemID = _inventory[i] ? _inventory[i]->_absNum : -1;
+			s.syncAsSint16LE(itemID);
+		} else {
+			int itemID;
+			s.syncAsSint16LE(itemID);
+			_inventory[i] = getItem(itemID);
+		}
 	}
 
 	for (int i = 0; i < _info._numVariables; ++i) {
@@ -1648,4 +1402,189 @@
 	return ldexp(mantissa, exp);
 }
 
+int GameObject::getAnim(int animID) const {
+	for (uint i = 0; i < _anim.size(); ++i) {
+		if (_anim[i]->getID() == animID) {
+			return i;
+		}
+	}
+	return -1;
 }
+
+int GameObject::addAnim(Animation *anim) {
+	anim->setZ(_z);
+	_anim.push_back(anim);
+	int index = _anim.size() - 1;
+	if (_absNum == kDragonObject && index <= kLastTurning) {
+		// Index to _anim is the Movement type.  All walking and
+		// turning movements can be accelerated.
+		anim->supportsQuickAnimation(true);
+	}
+	return index;
+}
+
+void GameObject::playAnim(int i) {
+      _anim[i]->play();
+      _playingAnim = i;
+}
+
+void GameObject::stopAnim() {
+      if (_playingAnim >= 0) {
+	      _anim[_playingAnim]->stop();
+	      _playingAnim = -1;
+      }
+}
+
+void GameObject::deleteAnims() {
+	for (uint j = 0; j < _anim.size(); ++j) {
+		_anim[j]->del();
+	}
+	_anim.clear();
+	_playingAnim = -1;
+}
+
+void GameObject::load(uint objNum, BArchive *archive) {
+	const BAFile *file;
+
+	file = archive->getFile(objNum * 3);
+	Common::MemoryReadStream objReader(file->_data, file->_length);
+
+	_init = objReader.readUint16LE();
+	_look = objReader.readUint16LE();
+	_use = objReader.readUint16LE();
+	_canUse = objReader.readUint16LE();
+	_imInit = objReader.readByte();
+	_imLook = objReader.readByte();
+	_imUse = objReader.readByte();
+	_walkDir = objReader.readByte() - 1;
+	_z = objReader.readByte();
+	objReader.readUint16LE(); // idxSeq field, not used
+	objReader.readUint16LE(); // numSeq field, not used
+	_lookX = objReader.readUint16LE();
+	_lookY = objReader.readUint16LE();
+	_useX = objReader.readUint16LE();
+	_useY = objReader.readUint16LE();
+	_lookDir = static_cast<SightDirection> (objReader.readByte());
+	_useDir = static_cast<SightDirection> (objReader.readByte());
+
+	_absNum = objNum;
+
+	file = archive->getFile(objNum * 3 + 1);
+
+	// The first byte of the file is the length of the string (without the length)
+	assert(file->_length - 1 == file->_data[0]);
+
+	_title = Common::String((char *)(file->_data+1), file->_length-1);
+
+	file = archive->getFile(objNum * 3 + 2);
+	_program._bytecode = file->_data;
+	_program._length = file->_length;
+
+	_playingAnim = -1;
+	_anim.clear();
+}
+
+void GameItem::load(int itemID, BArchive *archive) {
+	const BAFile *f = archive->getFile(itemID * 3);
+	Common::MemoryReadStream itemReader(f->_data, f->_length);
+
+	_init = itemReader.readSint16LE();
+	_look = itemReader.readSint16LE();
+	_use = itemReader.readSint16LE();
+	_canUse = itemReader.readSint16LE();
+	_imInit = itemReader.readByte();
+	_imLook = itemReader.readByte();
+	_imUse = itemReader.readByte();
+
+	_absNum = itemID;
+
+	f = archive->getFile(itemID * 3 + 1);
+
+	// The first byte is the length of the string
+	_title = Common::String((const char *)f->_data + 1, f->_length - 1);
+	assert(f->_data[0] == _title.size());
+
+	f = archive->getFile(itemID * 3 + 2);
+
+	_program._bytecode = f->_data;
+	_program._length = f->_length;
+
+	_anim = NULL;
+}
+
+void Room::load(int roomNum, BArchive *archive) {
+	const BAFile *f;
+	f = archive->getFile(roomNum * 4);
+	Common::MemoryReadStream roomReader(f->_data, f->_length);
+
+	roomReader.readUint32LE(); // Pointer to room program, not used
+	roomReader.readUint16LE(); // Program length, not used
+	roomReader.readUint32LE(); // Pointer to room title, not used
+
+	// Set the current room to the new value
+	_roomNum = roomNum;
+
+	// Music will be played by the GPL2 command startMusic when needed.
+	_music = roomReader.readByte();
+	_mapID = roomReader.readByte() - 1;
+	_palette = roomReader.readByte() - 1;
+	_numOverlays = roomReader.readSint16LE();
+	_init = roomReader.readSint16LE();
+	_look = roomReader.readSint16LE();
+	_use = roomReader.readSint16LE();
+	_canUse = roomReader.readSint16LE();
+	_imInit = roomReader.readByte();
+	_imLook = roomReader.readByte();
+	_imUse = roomReader.readByte();
+	_mouseOn = roomReader.readByte();
+	_heroOn = roomReader.readByte();
+
+	// Read in pers0 and persStep (stored as 6-byte Pascal reals)
+	byte real[6];
+
+	for (int i = 5; i >= 0; --i) {
+		real[i] = roomReader.readByte();
+	}
+
+	_pers0 = real_to_double(real);
+
+	for (int i = 5; i >= 0; --i) {
+		real[i] = roomReader.readByte();
+	}
+
+	_persStep = real_to_double(real);
+
+	_escRoom = roomReader.readByte() - 1;
+	_numGates = roomReader.readByte();
+
+	debugC(4, kDraciLogicDebugLevel, "Music: %d", _music);
+	debugC(4, kDraciLogicDebugLevel, "Map: %d", _mapID);
+	debugC(4, kDraciLogicDebugLevel, "Palette: %d", _palette);
+	debugC(4, kDraciLogicDebugLevel, "Overlays: %d", _numOverlays);
+	debugC(4, kDraciLogicDebugLevel, "Init: %d", _init);
+	debugC(4, kDraciLogicDebugLevel, "Look: %d", _look);
+	debugC(4, kDraciLogicDebugLevel, "Use: %d", _use);
+	debugC(4, kDraciLogicDebugLevel, "CanUse: %d", _canUse);
+	debugC(4, kDraciLogicDebugLevel, "ImInit: %d", _imInit);
+	debugC(4, kDraciLogicDebugLevel, "ImLook: %d", _imLook);
+	debugC(4, kDraciLogicDebugLevel, "ImUse: %d", _imUse);
+	debugC(4, kDraciLogicDebugLevel, "MouseOn: %d", _mouseOn);
+	debugC(4, kDraciLogicDebugLevel, "HeroOn: %d", _heroOn);
+	debugC(4, kDraciLogicDebugLevel, "Pers0: %f", _pers0);
+	debugC(4, kDraciLogicDebugLevel, "PersStep: %f", _persStep);
+	debugC(4, kDraciLogicDebugLevel, "EscRoom: %d", _escRoom);
+	debugC(4, kDraciLogicDebugLevel, "Gates: %d", _numGates);
+
+	// Read in the gates' numbers
+	_gates.clear();
+	for (uint i = 0; i < _numGates; ++i) {
+		_gates.push_back(roomReader.readSint16LE());
+	}
+
+	// Load the room's GPL program
+	f = archive->getFile(roomNum * 4 + 3);
+	_program._bytecode = f->_data;
+	_program._length = f->_length;
+}
+
+}

Modified: scummvm/trunk/engines/draci/game.h
===================================================================
--- scummvm/trunk/engines/draci/game.h	2009-11-10 00:10:30 UTC (rev 45798)
+++ scummvm/trunk/engines/draci/game.h	2009-11-10 05:16:34 UTC (rev 45799)
@@ -42,26 +42,7 @@
 	kDragonObject = 0
 };
 
-// Used as a return value for Game::getObjectWithAnimation() if no object
-// owns the animation in question
 enum {
-	kObjectNotFound = -1
-};
-
-// Used as the value of the _escRoom field of the current room if there is
-// no escape room defined
-enum {
-	kNoEscRoom = -1
-};
-
-// Used as a value to Game::_currentIcon and means there is no item selected
-// and a "real" cursor image is used
-enum {
-	kNoItem = -1
-};
-
-enum {
-	kNoDialogue = -1,
 	kDialogueLines = 4
 };
 
@@ -94,19 +75,30 @@
   kInventorySlots = kInventoryLines * kInventoryColumns
 };
 
-struct GameObject {
+class GameObject {
+public:
+	int _absNum;
 	uint _init, _look, _use, _canUse;
 	bool _imInit, _imLook, _imUse;
 	int _walkDir;
 	byte _z;
 	uint _lookX, _lookY, _useX, _useY;
 	SightDirection _lookDir, _useDir;
-	uint _absNum;
-	Common::Array<int> _anim;
 	GPL2Program _program;
 	Common::String _title;
 	int _location;
 	bool _visible;
+
+	Common::Array<Animation *> _anim;
+	int _playingAnim;
+
+	int getAnim(int animID) const;
+	int addAnim(Animation *anim);
+	int playingAnim() const { return _playingAnim; }
+	void playAnim(int i);
+	void stopAnim();
+	void deleteAnims();
+	void load(uint objNum, BArchive *archive);
 };
 
 struct GameInfo {
@@ -123,11 +115,17 @@
 	uint _numDialogueBlocks;
 };
 
-struct GameItem {
+class GameItem {
+public:
+	int _absNum;
 	uint _init, _look, _use, _canUse;
 	bool _imInit, _imLook, _imUse;
 	GPL2Program _program;
 	Common::String _title;
+
+	Animation *_anim;
+
+	void load(int itemID, BArchive *archive);
 };
 
 struct Person {
@@ -142,7 +140,8 @@
 	GPL2Program _program;
 };
 
-struct Room {
+class Room {
+public:
 	int _roomNum;
 	byte _music;
 	int _mapID;
@@ -156,6 +155,8 @@
 	byte _numGates;
 	Common::Array<int> _gates;
 	GPL2Program _program;
+
+	void load(int roomNum, BArchive *archive);
 };
 
 enum LoopStatus {
@@ -223,19 +224,15 @@
 	// unless the animation hasn't changed).
 	int playHeroAnimation(int anim_index);
 
-	int loadAnimation(uint animNum, uint z);
 	void loadOverlays();
-	void loadObject(uint numObj);
 	void loadWalkingMap(int mapID);		// but leaves _currentRoom._mapID untouched
-	void loadItem(int itemID);
+	void switchWalkingAnimations(bool enabled);
 
 	uint getNumObjects() const { return _info._numObjects; }
 	GameObject *getObject(uint objNum) { return _objects + objNum; }
-	int getObjectWithAnimation(int animID) const;
+	const GameObject *getObjectWithAnimation(const Animation *anim) const;
 	void deleteObjectAnimations();
 	void deleteAnimationsAfterIndex(int lastAnimIndex);
-	void stopObjectAnimations(const GameObject *obj);
-	int playingObjectAnimation(const GameObject *obj) const;
 
 	int getVariable(int varNum) const { return _variables[varNum]; }
 	void setVariable(int varNum, int value) { _variables[varNum] = value; }
@@ -257,10 +254,12 @@
 
 	int getItemStatus(int itemID) const { return _itemStatus[itemID]; }
 	void setItemStatus(int itemID, int status) { _itemStatus[itemID] = status; }
-	int getCurrentItem() const { return _currentItem; }
-	void setCurrentItem(int itemID) { _currentItem = itemID; }
-	void removeItem(int itemID);
-	void putItem(int itemID, int position);
+	GameItem *getItem(int id) { return id >= 0 ? &_items[id] : NULL; }
+	GameItem *getCurrentItem() const { return _currentItem; }
+	void setCurrentItem(GameItem *item) { _currentItem = item; }
+	void removeItem(GameItem *item);
+	void loadItemAnimation(GameItem *item);
+	void putItem(GameItem *item, int position);
 	void addItem(int itemID);
 
 	int getEscRoom() const { return _currentRoom._escRoom; }
@@ -337,9 +336,10 @@
 	void advanceAnimationsAndTestLoopExit();
 
 	bool enterNewRoom();	// Returns false if another room change has been triggered and therefore loop() shouldn't be called yet.
-	void loadRoom(int roomNum);
+	void initWalkingOverlays();
+	void loadRoomObjects();
 	void runGateProgram(int gate);
-	void redrawWalkingPath(int id, byte colour, const WalkingPath &path);
+	void redrawWalkingPath(Animation *anim, byte colour, const WalkingPath &path);
 
 	DraciEngine *_vm;
 
@@ -353,10 +353,10 @@
 
 	byte *_itemStatus;
 	GameItem *_items;
-	int _currentItem;
-	int _itemUnderCursor;
+	GameItem *_currentItem;
+	GameItem *_itemUnderCursor;
 
-	int _inventory[kInventorySlots];
+	GameItem *_inventory[kInventorySlots];
 	bool _inventoryExit;
 
 	Room _currentRoom;
@@ -390,8 +390,8 @@
 	uint _speechTick;
 	uint _speechDuration;
 
-	int _objUnderCursor;
-	int _animUnderCursor;
+	const GameObject *_objUnderCursor;
+	const Animation *_animUnderCursor;
 
 	int _markedAnimationIndex; ///< Used by the Mark GPL command
 
@@ -406,6 +406,12 @@
 
 	WalkingMap _walkingMap;
 	WalkingState _walkingState;
+
+	Animation *_titleAnim;
+	Animation *_inventoryAnim;
+	Animation *_walkingMapOverlay;
+	Animation *_walkingShortestPathOverlay;
+	Animation *_walkingObliquePathOverlay;
 };
 
 } // End of namespace Draci

Modified: scummvm/trunk/engines/draci/mouse.cpp
===================================================================
--- scummvm/trunk/engines/draci/mouse.cpp	2009-11-10 00:10:30 UTC (rev 45798)
+++ scummvm/trunk/engines/draci/mouse.cpp	2009-11-10 05:16:34 UTC (rev 45799)
@@ -24,6 +24,7 @@
  */
 
 #include "draci/draci.h"
+#include "draci/game.h"
 #include "draci/mouse.h"
 #include "draci/barchive.h"
 
@@ -104,8 +105,9 @@
 	        sp.getWidth() / 2, sp.getHeight() / 2);
 }
 
-void Mouse::loadItemCursor(int itemID, bool highlighted) {
-	int archiveIndex = 2 * itemID + highlighted;
+void Mouse::loadItemCursor(const GameItem *item, bool highlighted) {
+	const int itemID = item->_absNum;
+	const int archiveIndex = 2 * itemID + highlighted;
 	CursorType newCursor = static_cast<CursorType> (kItemCursor + archiveIndex);
 	if (newCursor == getCursorType()) {
 		return;

Modified: scummvm/trunk/engines/draci/mouse.h
===================================================================
--- scummvm/trunk/engines/draci/mouse.h	2009-11-10 00:10:30 UTC (rev 45798)
+++ scummvm/trunk/engines/draci/mouse.h	2009-11-10 05:16:34 UTC (rev 45799)
@@ -45,6 +45,7 @@
 };
 
 class DraciEngine;
+class GameItem;
 
 class Mouse {
 public:
@@ -58,7 +59,7 @@
 	void setPosition(uint16 x, uint16 y);
 	CursorType getCursorType() const { return _cursorType; }
 	void setCursorType(CursorType cur);
-	void loadItemCursor(int itemID, bool highlighted);
+	void loadItemCursor(const GameItem *item, bool highlighted);
 	bool lButtonPressed() const { return _lButton; }
 	bool rButtonPressed() const { return _rButton; }
 	void lButtonSet(bool state) { _lButton = state; }

Modified: scummvm/trunk/engines/draci/script.cpp
===================================================================
--- scummvm/trunk/engines/draci/script.cpp	2009-11-10 00:10:30 UTC (rev 45798)
+++ scummvm/trunk/engines/draci/script.cpp	2009-11-10 05:16:34 UTC (rev 45799)
@@ -270,7 +270,9 @@
 int Script::funcIsIcoAct(int itemID) const {
 	itemID -= 1;
 
-	return _vm->_game->getCurrentItem() == itemID;
+	const GameItem *item = _vm->_game->getCurrentItem();
+	const int currentID = item ? item->_absNum : -1;
+	return currentID == itemID;
 }
 
 int Script::funcActIco(int itemID) const {
@@ -279,7 +281,8 @@
 	// implemented in such a way that they had to have a single parameter so this is only
 	// passed as a dummy.
 
-	return _vm->_game->getCurrentItem();
+	const GameItem *item = _vm->_game->getCurrentItem();
+	return item ? item->_absNum + 1 : 0;
 }
 
 int Script::funcIsObjOn(int objID) const {
@@ -340,10 +343,9 @@
 	bool visible = (obj->_location == _vm->_game->getRoomNum() && obj->_visible);
 
 	if (objID == kDragonObject || visible) {
-		const int i = _vm->_game->playingObjectAnimation(obj);
+		const int i = obj->playingAnim();
 		if (i >= 0) {
-			int animID = obj->_anim[i];
-			Animation *anim = _vm->_anims->getAnimation(animID);
+			Animation *anim = obj->_anim[i];
 			ret = anim->currentFrameNum();
 		}
 	}
@@ -363,18 +365,6 @@
 	_vm->_game->loop(kInnerUntilExit, true);
 }
 
-Animation *Script::loadObjectAnimation(int objID, GameObject *obj, int animID) {
-	_vm->_game->loadAnimation(animID, obj->_z);
-	obj->_anim.push_back(animID);
-	Animation *anim = _vm->_anims->getAnimation(animID);
-	if (objID == kDragonObject && obj->_anim.size() - 1 <= kLastTurning) {
-		// obj->_anim.size() is the Movement type.  All walking and
-		// turning movements can be accelerated.
-		anim->supportsQuickAnimation(true);
-	}
-	return anim;
-}
-
 void Script::load(const Common::Array<int> &params) {
 	if (_vm->_game->getLoopStatus() == kStatusInventory) {
 		return;
@@ -383,21 +373,17 @@
 	int objID = params[0] - 1;
 	int animID = params[1] - 1;
 
-	uint i;
-	GameObject *obj = _vm->_game->getObject(objID);
-
 	// If the animation is already loaded, return
-	for (i = 0; i < obj->_anim.size(); ++i) {
-		if (obj->_anim[i] == animID) {
-			return;
-		}
+	GameObject *obj = _vm->_game->getObject(objID);
+	if (obj->getAnim(animID) >= 0) {
+		return;
 	}
 
 	// We don't test here whether an animation is loaded in the
 	// AnimationManager while not being registered in the object's array of
 	// animations.  This cannot legally happen and an assertion will be
-	// thrown by loadAnimation().
-	loadObjectAnimation(objID, obj, animID);
+	// thrown by AnimationManager::load().
+	obj->addAnim(_vm->_anims->load(animID));
 }
 
 void Script::start(const Common::Array<int> &params) {
@@ -409,10 +395,10 @@
 	int animID = params[1] - 1;
 
 	GameObject *obj = _vm->_game->getObject(objID);
-	_vm->_game->stopObjectAnimations(obj);
+	obj->stopAnim();
 
-	Animation *anim = _vm->_anims->getAnimation(animID);
-	if (!anim) {
+	int index = obj->getAnim(animID);
+	if (index < 0) {
 		// WORKAROUND:
 		//
 		// The original game files seem to contain errors, which I have
@@ -432,20 +418,21 @@
 		// to apply the hedgehog, but there is no way that the game
 		// player would load the requested animation by itself.
 		// See objekty:5077 and parezy.txt:27.
-		anim = loadObjectAnimation(objID, obj, animID);
+		index = obj->addAnim(_vm->_anims->load(animID));
 		debugC(1, kDraciBytecodeDebugLevel, "start(%d=%s) cannot find animation %d.  Loading.",
 			objID, obj->_title.c_str(), animID);
 	}
+	Animation *anim = obj->_anim[index];
 
 	if (objID == kDragonObject)
 		_vm->_game->positionAnimAsHero(anim);
 
-	anim->registerCallback(&Animation::stopAnimation);
+	anim->registerCallback(&Animation::stop);
 
 	bool visible = (obj->_location == _vm->_game->getRoomNum() && obj->_visible);
 
 	if (objID == kDragonObject || visible) {
-		_vm->_anims->play(animID);
+		obj->playAnim(index);
 	}
 }
 
@@ -458,14 +445,15 @@
 	int animID = params[1] - 1;
 
 	GameObject *obj = _vm->_game->getObject(objID);
-	_vm->_game->stopObjectAnimations(obj);
+	obj->stopAnim();
 
-	Animation *anim = _vm->_anims->getAnimation(animID);
-	if (!anim) {
-		anim = loadObjectAnimation(objID, obj, animID);
+	int index = obj->getAnim(animID);
+	if (index < 0) {
+		index = obj->addAnim(_vm->_anims->load(animID));
 		debugC(1, kDraciBytecodeDebugLevel, "startPlay(%d=%s) cannot find animation %d.  Loading.",
 			objID, obj->_title.c_str(), animID);
 	}
+	Animation *anim = obj->_anim[index];
 
 	if (objID == kDragonObject)
 		_vm->_game->positionAnimAsHero(anim);
@@ -474,37 +462,27 @@
 
 	bool visible = (obj->_location == _vm->_game->getRoomNum() && obj->_visible);
 	if (objID == kDragonObject || visible) {
-		_vm->_anims->play(animID);
+		obj->playAnim(index);
 	}
 
 	// Runs an inner loop until the animation ends.
 	_vm->_game->loop(kInnerUntilExit, false);
-	_vm->_anims->stop(animID);
+	obj->stopAnim();
 
 	anim->registerCallback(&Animation::doNothing);
 }
 
 void Script::justTalk(const Common::Array<int> &params) {
 	const GameObject *dragon = _vm->_game->getObject(kDragonObject);
-	const int last_anim = static_cast<Movement> (_vm->_game->playingObjectAnimation(dragon));
-	int new_anim;
-	if (last_anim == kSpeakRight || last_anim == kStopRight) {
-		new_anim = kSpeakRight;
-	} else {
-		new_anim = kSpeakLeft;
-	}
+	const int last_anim = static_cast<Movement> (dragon->playingAnim());
+	const int new_anim = (last_anim == kSpeakRight || last_anim == kStopRight) ? kSpeakRight : kSpeakLeft;
 	_vm->_game->playHeroAnimation(new_anim);
 }
 
 void Script::justStay(const Common::Array<int> &params) {
 	const GameObject *dragon = _vm->_game->getObject(kDragonObject);
-	const int last_anim = static_cast<Movement> (_vm->_game->playingObjectAnimation(dragon));
-	int new_anim;
-	if (last_anim == kSpeakRight || last_anim == kStopRight) {
-		new_anim = kStopRight;
-	} else {
-		new_anim = kStopLeft;
-	}
+	const int last_anim = static_cast<Movement> (dragon->playingAnim());
+	const int new_anim = (last_anim == kSpeakRight || last_anim == kStopRight) ? kStopRight : kStopLeft;
 	_vm->_game->playHeroAnimation(new_anim);
 }
 
@@ -557,18 +535,20 @@
 void Script::icoStat(const Common::Array<int> &params) {
 	int status = params[0];
 	int itemID = params[1] - 1;
+	GameItem *item = _vm->_game->getItem(itemID);
 
 	_vm->_game->setItemStatus(itemID, status == 1);
 
 	if (_vm->_game->getItemStatus(itemID) == 0) {
-		if (itemID != kNoItem) {
-			_vm->_anims->deleteAnimation(kInventoryItemsID - itemID);
+		if (item) {
+			item->_anim->del();
+			item->_anim = NULL;
 		}
 
-		_vm->_game->removeItem(itemID);
+		_vm->_game->removeItem(item);
 
-		if (_vm->_game->getCurrentItem() == itemID) {
-			_vm->_game->setCurrentItem(kNoItem);
+		if (_vm->_game->getCurrentItem() == item) {
+			_vm->_game->setCurrentItem(NULL);
 		}
 
 		if (_vm->_mouse->getCursorType() == kNormalCursor) {
@@ -580,16 +560,13 @@
 	}
 
 	if (_vm->_game->getItemStatus(itemID) == 1) {
-		if (itemID != kNoItem) {
-			Animation *itemAnim = _vm->_anims->addItem(kInventoryItemsID - itemID, false);
-			const BAFile *f = _vm->_itemImagesArchive->getFile(2 * itemID);
-			Sprite *sp = new Sprite(f->_data, f->_length, 0, 0, true);
-			itemAnim->addFrame(sp, NULL);
+		if (item) {
+			_vm->_game->loadItemAnimation(item);
 		}
 
-		_vm->_game->setCurrentItem(itemID);
+		_vm->_game->setCurrentItem(item);
 
-		_vm->_mouse->loadItemCursor(itemID, false);
+		_vm->_mouse->loadItemCursor(item, false);
 
 		// TODO: This is probably not needed but I'm leaving it to be sure for now
 		// The original engine needed to turn off the mouse temporarily when changing
@@ -625,7 +602,7 @@
 		obj->_location = -1;
 	}
 
-	_vm->_game->stopObjectAnimations(obj);
+	obj->stopAnim();
 }
 
 void Script::execInit(const Common::Array<int> &params) {
@@ -674,7 +651,7 @@
 	Common::Point heroPos(_vm->_game->findNearestWalkable(x, y));
 	Common::Point mousePos(_vm->_mouse->getPosX(), _vm->_mouse->getPosY());
 	const GameObject *dragon = _vm->_game->getObject(kDragonObject);
-	Movement startingDirection = static_cast<Movement> (_vm->_game->playingObjectAnimation(dragon));
+	Movement startingDirection = static_cast<Movement> (dragon->playingAnim());
 
 	_vm->_game->stopWalking();
 	_vm->_game->setHeroPosition(heroPos);

Modified: scummvm/trunk/engines/draci/script.h
===================================================================
--- scummvm/trunk/engines/draci/script.h	2009-11-10 00:10:30 UTC (rev 45798)
+++ scummvm/trunk/engines/draci/script.h	2009-11-10 05:16:34 UTC (rev 45799)
@@ -90,7 +90,7 @@
 };
 
 class Animation;
-struct GameObject;
+class GameObject;
 
 class Script {
 
@@ -193,9 +193,6 @@
 	int handleMathExpression(Common::MemoryReadStream *reader) const;
 
 	DraciEngine *_vm;
-
-	// Auxilliary functions
-	Animation *loadObjectAnimation(int objID, GameObject *obj, int animID);
 };
 
 } // End of namespace Draci

Modified: scummvm/trunk/engines/draci/walking.cpp
===================================================================
--- scummvm/trunk/engines/draci/walking.cpp	2009-11-10 00:10:30 UTC (rev 45798)
+++ scummvm/trunk/engines/draci/walking.cpp	2009-11-10 05:16:34 UTC (rev 45799)
@@ -458,7 +458,7 @@
 
 	// Remember the initial dragon's direction.
 	const GameObject *dragon = _vm->_game->getObject(kDragonObject);
-	_startingDirection = static_cast<Movement> (_vm->_game->playingObjectAnimation(dragon));
+	_startingDirection = static_cast<Movement> (dragon->playingAnim());
 
 	// Going to start with the first segment.
 	_segment = 0;
@@ -503,7 +503,7 @@
 
 bool WalkingState::continueWalking() {
 	const GameObject *dragon = _vm->_game->getObject(kDragonObject);
-	const Movement movement = static_cast<Movement> (_vm->_game->playingObjectAnimation(dragon));
+	const Movement movement = static_cast<Movement> (dragon->playingAnim());
 
 	if (_turningFinished) {
 		// When a turning animation has finished, heroAnimationFinished() callback
@@ -526,8 +526,7 @@
 
 	// Read the dragon's animation's current phase.  Determine if it has
 	// changed from the last time.  If not, wait until it has.
-	const int animID = dragon->_anim[movement];
-	Animation *anim = _vm->_anims->getAnimation(animID);
+	Animation *anim = dragon->_anim[movement];
 	const int animPhase = anim->currentFrameNum();
 	const bool wasUpdated = animPhase != _lastAnimPhase;
 	if (!wasUpdated) {
@@ -589,6 +588,11 @@
 bool WalkingState::alignHeroToEdge(const Common::Point &p1, const Common::Point &p2, const Common::Point &prevHero, Common::Point *hero) {
 	const Movement movement = animationForDirection(p1, p2);
 	const Common::Point p2Diff(p2.x - p1.x, p2.y - p1.y);
+	if (p2Diff.x == 0 && p2Diff.y == 0) {
+		debugC(2, kDraciWalkingDebugLevel, "Adjusted walking edge has zero length");
+		// Due to changing the path vertices on the fly, this can happen.
+		return true;
+	}
 	bool reachedEnd;
 	if (movement == kMoveLeft || movement == kMoveRight) {
 		reachedEnd = movement == kMoveLeft ? hero->x <= p2.x : hero->x >= p2.x;
@@ -602,7 +606,7 @@
 
 bool WalkingState::turnForTheNextSegment() {
 	const GameObject *dragon = _vm->_game->getObject(kDragonObject);
-	const Movement currentAnim = static_cast<Movement> (_vm->_game->playingObjectAnimation(dragon));
+	const Movement currentAnim = static_cast<Movement> (dragon->playingAnim());
 	const Movement wantAnim = directionForNextPhase();
 	Movement transition = transitionBetweenAnimations(currentAnim, wantAnim);
 
@@ -617,8 +621,7 @@
 		// to calling walkOnNextEdge() in the next phase.
 		assert(isTurningMovement(transition));
 		_lastAnimPhase = _vm->_game->playHeroAnimation(transition);
-		const int animID = dragon->_anim[transition];
-		Animation *anim = _vm->_anims->getAnimation(animID);
+		Animation *anim = dragon->_anim[transition];
 		anim->registerCallback(&Animation::tellWalkingState);
 
 		debugC(2, kDraciWalkingDebugLevel, "Starting turning animation %d with phase %d", transition, _lastAnimPhase);


This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.




More information about the Scummvm-git-logs mailing list