aboutsummaryrefslogtreecommitdiff
path: root/engines
diff options
context:
space:
mode:
Diffstat (limited to 'engines')
-rw-r--r--engines/draci/animation.cpp225
-rw-r--r--engines/draci/animation.h20
-rw-r--r--engines/draci/draci.cpp12
-rw-r--r--engines/draci/game.cpp821
-rw-r--r--engines/draci/game.h88
-rw-r--r--engines/draci/mouse.cpp6
-rw-r--r--engines/draci/mouse.h3
-rw-r--r--engines/draci/script.cpp109
-rw-r--r--engines/draci/script.h5
-rw-r--r--engines/draci/walking.cpp17
10 files changed, 630 insertions, 676 deletions
diff --git a/engines/draci/animation.cpp b/engines/draci/animation.cpp
index 6e1a4e4ad8..1b6cafed4e 100644
--- a/engines/draci/animation.cpp
+++ b/engines/draci/animation.cpp
@@ -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 @@ void Animation::deleteFrames() {
_samples.clear();
}
-void Animation::stopAnimation() {
- _vm->_anims->stop(_id);
-}
-
void Animation::exitGameLoop() {
_vm->_game->setExitLoop(true);
}
@@ -251,73 +247,38 @@ void Animation::tellWalkingState() {
_vm->_game->heroAnimationFinished();
}
-Animation *AnimationManager::addAnimation(int id, uint z, bool playing) {
- // Increment animation index
- ++_lastIndex;
-
- Animation *anim = new Animation(_vm, _lastIndex);
+void Animation::play() {
+ if (isPlaying()) {
+ return;
+ }
- anim->setID(id);
- anim->setZ(z);
- anim->setPlaying(playing);
+ // Mark the first frame dirty so it gets displayed
+ markDirtyRect(_vm->_screen->getSurface());
- insertAnimation(anim);
+ setPlaying(true);
- return anim;
+ debugC(3, kDraciAnimationDebugLevel, "Playing animation %d...", getID());
}
-Animation *AnimationManager::addItem(int id, bool playing) {
- Animation *anim = new Animation(_vm, kIgnoreIndex);
-
- anim->setID(id);
- anim->setZ(256);
- anim->setPlaying(playing);
-
- insertAnimation(anim);
-
- return anim;
-}
+void Animation::stop() {
+ if (!isPlaying()) {
+ return;
+ }
-Animation *AnimationManager::addText(int id, bool playing) {
- Animation *anim = new Animation(_vm, kIgnoreIndex);
+ // Clean up the last frame that was drawn before stopping
+ markDirtyRect(_vm->_screen->getSurface());
- anim->setID(id);
- anim->setZ(257);
- anim->setPlaying(playing);
+ setPlaying(false);
- insertAnimation(anim);
+ // Reset the animation to the beginning
+ setCurrentFrame(0);
+ clearShift();
- 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 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 Animation::del() {
+ _vm->_anims->deleteAnimation(this);
}
void AnimationManager::pauseAnimations() {
@@ -358,7 +319,10 @@ Animation *AnimationManager::getAnimation(int id) {
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 @@ void AnimationManager::insertAnimation(Animation *anim) {
_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 @@ void AnimationManager::sortAnimations() {
Animation *anim = *next;
next = _animations.reverse_erase(next);
- insertAnimation(anim);
+ insert(anim, false);
hasChanged = true;
}
@@ -441,21 +391,24 @@ void AnimationManager::sortAnimations() {
} 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 @@ void AnimationManager::deleteAfterIndex(int index) {
_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 @@ int AnimationManager::getTopAnimationID(int x, int y) const {
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;
+}
+
}
diff --git a/engines/draci/animation.h b/engines/draci/animation.h
index aff2680c37..b669d53272 100644
--- a/engines/draci/animation.h
+++ b/engines/draci/animation.h
@@ -62,7 +62,7 @@ class Animation {
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 @@ public:
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 @@ public:
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 @@ public:
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;
diff --git a/engines/draci/draci.cpp b/engines/draci/draci.cpp
index d9720b77fd..f759cad4a3 100644
--- a/engines/draci/draci.cpp
+++ b/engines/draci/draci.cpp
@@ -242,7 +242,7 @@ void DraciEngine::handleEvents() {
? _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 @@ void DraciEngine::handleEvents() {
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());
diff --git a/engines/draci/game.cpp b/engines/draci/game.cpp
index f9eb55fb0e..1f3245a31b 100644
--- a/engines/draci/game.cpp
+++ b/engines/draci/game.cpp
@@ -135,6 +135,8 @@ Game::Game(DraciEngine *vm) : _vm(vm), _walkingState(vm) {
// Set object location
_objects[i]._location = (~(1 << 7) & tmp) - 1;
+
+ _objects[i]._playingAnim = -1;
}
assert(numDialogues == _info._numDialogues);
@@ -154,6 +156,7 @@ void Game::start() {
loop(kOuterLoop, false);
}
}
+
}
void Game::init() {
@@ -168,61 +171,63 @@ void Game::init() {
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 @@ void Game::handleOrdinaryLoop(int x, int y) {
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 @@ void Game::handleOrdinaryLoop(int x, int y) {
if (_vm->_mouse->rButtonPressed()) {
_vm->_mouse->rButtonSet(false);
- if (_objUnderCursor != kObjectNotFound) {
- const GameObject *obj = &_objects[_objUnderCursor];
-
- if (_vm->_script->testExpression(obj->_program, obj->_canUse)) {
- _walkingState.setCallback(&obj->_program, obj->_use);
+ if (_objUnderCursor) {
+ if (_vm->_script->testExpression(_objUnderCursor->_program, _objUnderCursor->_canUse)) {
+ _walkingState.setCallback(&_objUnderCursor->_program, _objUnderCursor->_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 @@ void Game::handleInventoryLoop() {
// 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 @@ void Game::handleInventoryLoop() {
// 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 @@ void Game::handleInventoryLoop() {
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 @@ void Game::handleInventoryLoop() {
// 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 @@ void Game::handleDialogueLoop() {
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 @@ void Game::loop(LoopSubstatus substatus, bool shouldExit) {
// 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 @@ void Game::updateOrdinaryCursor() {
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 @@ void Game::updateOrdinaryCursor() {
}
// 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 @@ void Game::updateOrdinaryCursor() {
// 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 @@ void Game::updateInventoryCursor() {
// 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 @@ void Game::updateInventoryCursor() {
}
}
if (!mouseChanged) {
- if (_currentItem == kNoItem) {
+ if (!_currentItem) {
_vm->_mouse->setCursorType(kNormalCursor);
} else {
_vm->_mouse->loadItemCursor(_currentItem, false);
@@ -621,101 +615,85 @@ void Game::updateTitle(int x, int y) {
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 @@ void Game::putItem(int itemID, int position) {
(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 @@ void Game::putItem(int itemID, int position) {
// 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 @@ void Game::inventoryDone() {
_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 @@ void Game::dialogueMenu(int dialogueID) {
} while (!_dialogueExit);
dialogueDone();
- _currentDialogue = kNoDialogue;
+ _currentDialogue = -1;
}
int Game::dialogueDraw() {
@@ -878,7 +856,7 @@ int Game::dialogueDraw() {
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 @@ int Game::dialogueDraw() {
if (notDialogueAnim) {
ret = -1;
} else {
- ret = _dialogueAnims[0]->getID() - _animUnderCursor;
+ ret = kDialogueLinesID - _animUnderCursor->getID();
}
} else {
ret = _dialogueLinesNum - 1;
@@ -932,7 +910,7 @@ void Game::dialogueInit(int dialogID) {
}
for (uint i = 0; i < kDialogueLines; ++i) {
- _vm->_anims->play(_dialogueAnims[i]->getID());
+ _dialogueAnims[i]->play();
}
setLoopStatus(kStatusDialogue);
@@ -942,7 +920,7 @@ void Game::dialogueInit(int dialogID) {
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 @@ void Game::runDialogueProg(GPL2Program prog, int offset) {
}
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 @@ void Game::walkHero(int x, int y, SightDirection dir) {
_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 @@ void Game::walkHero(int x, int y, SightDirection dir) {
_walkingMap.getDelta(), obliquePath);
}
-void Game::loadItem(int itemID) {
- const BAFile *f = _vm->_itemsArchive->getFile(itemID * 3);
- Common::MemoryReadStream itemReader(f->_data, f->_length);
-
- 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;
-}
-
-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();
- }
+void Game::initWalkingOverlays() {
+ _walkingMapOverlay = new Animation(_vm, kWalkingMapOverlay, 256, _vm->_showWalkingMap);
+ _walkingMapOverlay->addFrame(NULL, NULL); // rewritten below by loadWalkingMap()
+ _vm->_anims->insert(_walkingMapOverlay, true);
- _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);
+ _walkingShortestPathOverlay = new Animation(_vm, kWalkingShortestPathOverlay, 257, _vm->_showWalkingMap);
+ _walkingObliquePathOverlay = new Animation(_vm, 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());
+ _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::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 @@ void Game::loadRoom(int roomNum) {
// 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,138 +1045,9 @@ void Game::loadRoom(int roomNum) {
}
}
- // 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) {
@@ -1301,11 +1055,22 @@ void Game::loadWalkingMap(int mapID) {
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() {
@@ -1328,7 +1093,11 @@ void Game::loadOverlays() {
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::loadOverlays() {
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 @@ bool Game::enterNewRoom() {
_vm->_anims->deleteOverlays();
- // Delete walking map testing overlay
- _vm->_anims->deleteAnimation(kWalkingMapOverlay);
- _vm->_anims->deleteAnimation(kWalkingShortestPathOverlay);
- _vm->_anims->deleteAnimation(kWalkingObliquePathOverlay);
-
- // TODO: Make objects capable of stopping their own animations
- const GameObject *dragon = getObject(kDragonObject);
- stopObjectAnimations(dragon);
+ GameObject *dragon = getObject(kDragonObject);
+ dragon->stopAnim();
// 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 @@ bool Game::enterNewRoom() {
// 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 @@ void Game::deleteAnimationsAfterIndex(int lastAnimIndex) {
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 @@ void Game::DoSync(Common::Serializer &s) {
}
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 @@ static double real_to_double(byte real[6]) {
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;
+}
+
}
diff --git a/engines/draci/game.h b/engines/draci/game.h
index 08c4307c41..8d219850d2 100644
--- a/engines/draci/game.h
+++ b/engines/draci/game.h
@@ -42,26 +42,7 @@ enum {
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 @@ enum InventoryConstants {
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 @@ struct GameInfo {
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 @@ struct Dialogue {
GPL2Program _program;
};
-struct Room {
+class Room {
+public:
int _roomNum;
byte _music;
int _mapID;
@@ -156,6 +155,8 @@ struct Room {
byte _numGates;
Common::Array<int> _gates;
GPL2Program _program;
+
+ void load(int roomNum, BArchive *archive);
};
enum LoopStatus {
@@ -223,19 +224,15 @@ public:
// 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 @@ public:
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 @@ private:
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 @@ private:
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 @@ private:
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 @@ private:
WalkingMap _walkingMap;
WalkingState _walkingState;
+
+ Animation *_titleAnim;
+ Animation *_inventoryAnim;
+ Animation *_walkingMapOverlay;
+ Animation *_walkingShortestPathOverlay;
+ Animation *_walkingObliquePathOverlay;
};
} // End of namespace Draci
diff --git a/engines/draci/mouse.cpp b/engines/draci/mouse.cpp
index ae80775898..4d15d2e3f6 100644
--- a/engines/draci/mouse.cpp
+++ b/engines/draci/mouse.cpp
@@ -24,6 +24,7 @@
*/
#include "draci/draci.h"
+#include "draci/game.h"
#include "draci/mouse.h"
#include "draci/barchive.h"
@@ -104,8 +105,9 @@ void Mouse::setCursorType(CursorType cur) {
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;
diff --git a/engines/draci/mouse.h b/engines/draci/mouse.h
index 90c5b919ad..87af2a3993 100644
--- a/engines/draci/mouse.h
+++ b/engines/draci/mouse.h
@@ -45,6 +45,7 @@ enum CursorType {
};
class DraciEngine;
+class GameItem;
class Mouse {
public:
@@ -58,7 +59,7 @@ public:
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; }
diff --git a/engines/draci/script.cpp b/engines/draci/script.cpp
index 079e1c5a5a..f7cf015c24 100644
--- a/engines/draci/script.cpp
+++ b/engines/draci/script.cpp
@@ -270,7 +270,9 @@ int Script::funcIcoStat(int itemID) const {
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 @@ int Script::funcActIco(int itemID) const {
// 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 @@ int Script::funcActPhase(int objID) const {
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 @@ void Script::play(const Common::Array<int> &params) {
_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 @@ void Script::load(const Common::Array<int> &params) {
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 @@ void Script::start(const Common::Array<int> &params) {
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 @@ void Script::start(const Common::Array<int> &params) {
// 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 @@ void Script::startPlay(const Common::Array<int> &params) {
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 @@ void Script::startPlay(const Common::Array<int> &params) {
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::release(const Common::Array<int> &params) {
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 @@ void Script::icoStat(const Common::Array<int> &params) {
}
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 @@ void Script::objStat(const Common::Array<int> &params) {
obj->_location = -1;
}
- _vm->_game->stopObjectAnimations(obj);
+ obj->stopAnim();
}
void Script::execInit(const Common::Array<int> &params) {
@@ -674,7 +651,7 @@ void Script::stayOn(const Common::Array<int> &params) {
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);
diff --git a/engines/draci/script.h b/engines/draci/script.h
index a7be243387..5fd63517c1 100644
--- a/engines/draci/script.h
+++ b/engines/draci/script.h
@@ -90,7 +90,7 @@ struct GPL2Program {
};
class Animation;
-struct GameObject;
+class GameObject;
class Script {
@@ -193,9 +193,6 @@ private:
int handleMathExpression(Common::MemoryReadStream *reader) const;
DraciEngine *_vm;
-
- // Auxilliary functions
- Animation *loadObjectAnimation(int objID, GameObject *obj, int animID);
};
} // End of namespace Draci
diff --git a/engines/draci/walking.cpp b/engines/draci/walking.cpp
index 932259d213..7998126699 100644
--- a/engines/draci/walking.cpp
+++ b/engines/draci/walking.cpp
@@ -458,7 +458,7 @@ void WalkingState::startWalking(const Common::Point &p1, const Common::Point &p2
// 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::continueWalkingOrClearPath() {
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 @@ bool WalkingState::continueWalking() {
// 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::continueWalking() {
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::alignHeroToEdge(const Common::Point &p1, const Common::Point
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 @@ bool WalkingState::turnForTheNextSegment() {
// 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);