diff options
Diffstat (limited to 'engines/mads/sequence.cpp')
-rw-r--r-- | engines/mads/sequence.cpp | 541 |
1 files changed, 541 insertions, 0 deletions
diff --git a/engines/mads/sequence.cpp b/engines/mads/sequence.cpp new file mode 100644 index 0000000000..07b1451718 --- /dev/null +++ b/engines/mads/sequence.cpp @@ -0,0 +1,541 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/scummsys.h" +#include "mads/mads.h" +#include "mads/assets.h" +#include "mads/sequence.h" +#include "mads/scene.h" + +namespace MADS { + +SequenceEntry::SequenceEntry() { + _spritesIndex = 0; + _flipped = 0; + _frameIndex = 0; + _frameStart = 0; + _numSprites = 0; + _animType = ANIMTYPE_NONE; + _frameInc = 0; + _depth = 0; + _scale = 0; + _dynamicHotspotIndex = -1; + _triggerCountdown = 0; + _doneFlag = 0; + _triggerMode = SEQUENCE_TRIGGER_DAEMON; + _numTicks = 0; + _extraTicks = 0; + _timeout = 0; + _active = false; + _nonFixed = false; + _flags = 0; + for (int i = 0; i < 5; ++i) + _entries._mode[i] = SEQUENCE_TRIGGER_EXPIRE; + + _entries._count = 0; + _actionNouns._verbId = VERB_NONE; + _actionNouns._objectNameId = -1; + _actionNouns._indirectObjectId = -1; + + Common::fill(&_entries._frameIndex[0], &_entries._frameIndex[SEQUENCE_ENTRY_SUBSET_MAX], 0); + Common::fill(&_entries._trigger[0], &_entries._trigger[SEQUENCE_ENTRY_SUBSET_MAX], 0); +} + +/*------------------------------------------------------------------------*/ + +#define SEQUENCE_LIST_SIZE 30 + +SequenceList::SequenceList(MADSEngine *vm) : _vm(vm) { + // IMPORTANT: Preallocate timer slots. Note that sprite slots refer to entries + // in this list by index, so we can't just add or delete entries later + for (int i = 0; i < SEQUENCE_LIST_SIZE; ++i) { + SequenceEntry rec; + rec._active = false; + rec._dynamicHotspotIndex = -1; + _entries.push_back(rec); + } +} + +void SequenceList::clear() { + for (uint i = 0; i < _entries.size(); ++i) { + _entries[i]._active = false; + _entries[i]._dynamicHotspotIndex = -1; + } +} + +bool SequenceList::addSubEntry(int index, SequenceTrigger mode, int frameIndex, int trigger) { + if (_entries[index]._entries._count >= SEQUENCE_ENTRY_SUBSET_MAX) + return true; + + int subIndex = _entries[index]._entries._count++; + _entries[index]._entries._mode[subIndex] = mode; + _entries[index]._entries._frameIndex[subIndex] = frameIndex; + _entries[index]._entries._trigger[subIndex] = trigger; + + return false; +} + +int SequenceList::add(int spriteListIndex, bool flipped, int frameIndex, int triggerCountdown, int delayTicks, int extraTicks, int numTicks, + int msgX, int msgY, bool nonFixed, int scale, int depth, int frameInc, SpriteAnimType animType, int numSprites, + int frameStart) { + Scene &scene = _vm->_game->_scene; + + // Find a free slot + uint seqIndex = 0; + while ((seqIndex < _entries.size()) && _entries[seqIndex]._active) + ++seqIndex; + if (seqIndex == _entries.size()) + error("TimerList full"); + + if (frameStart <= 0) + frameStart = 1; + if (numSprites == 0) + numSprites = scene._sprites[spriteListIndex]->getCount(); + if (frameStart == numSprites) + frameInc = 0; + + // Set the list entry fields + _entries[seqIndex]._active = true; + _entries[seqIndex]._spritesIndex = spriteListIndex; + _entries[seqIndex]._flipped = flipped; + _entries[seqIndex]._frameIndex = frameIndex; + _entries[seqIndex]._frameStart = frameStart; + _entries[seqIndex]._numSprites = numSprites; + _entries[seqIndex]._animType = animType; + _entries[seqIndex]._frameInc = frameInc; + _entries[seqIndex]._depth = depth; + _entries[seqIndex]._scale = scale; + _entries[seqIndex]._nonFixed = nonFixed; + _entries[seqIndex]._position.x = msgX; + _entries[seqIndex]._position.y = msgY; + _entries[seqIndex]._numTicks = numTicks; + _entries[seqIndex]._extraTicks = extraTicks; + + _entries[seqIndex]._timeout = scene._frameStartTime + delayTicks; + + _entries[seqIndex]._triggerCountdown = triggerCountdown; + _entries[seqIndex]._doneFlag = false; + _entries[seqIndex]._flags = 0; + _entries[seqIndex]._dynamicHotspotIndex = -1; + _entries[seqIndex]._entries._count = 0; + _entries[seqIndex]._triggerMode = _vm->_game->_triggerSetupMode; + + _entries[seqIndex]._actionNouns = _vm->_game->_scene._action._activeAction; + + return seqIndex; +} + +int SequenceList::addTimer(int timeout, int abortVal) { + Scene &scene = _vm->_game->_scene; + uint seqIndex; + for (seqIndex = 0; seqIndex < _entries.size(); ++seqIndex) { + if (!_entries[seqIndex]._active) + break; + } + assert(seqIndex < _entries.size()); + + SequenceEntry &se = _entries[seqIndex]; + se._active = true; + se._spritesIndex = -1; + se._numTicks = timeout; + se._extraTicks = 0; + se._timeout = scene._frameStartTime + timeout; + se._triggerCountdown = true; + se._doneFlag = false; + se._entries._count = 0; + se._triggerMode = _vm->_game->_triggerSetupMode; + se._actionNouns = _vm->_game->_scene._action._activeAction; + addSubEntry(seqIndex, SEQUENCE_TRIGGER_EXPIRE, 0, abortVal); + + return seqIndex; +} + +void SequenceList::remove(int seqIndex) { + Scene &scene = _vm->_game->_scene; + + if (_entries[seqIndex]._active) { + if (_entries[seqIndex]._dynamicHotspotIndex >= 0) + scene._dynamicHotspots.remove(_entries[seqIndex]._dynamicHotspotIndex); + } + + _entries[seqIndex]._active = false; + scene._spriteSlots.deleteTimer(seqIndex); +} + +void SequenceList::setSpriteSlot(int seqIndex, SpriteSlot &spriteSlot) { + Scene &scene = _vm->_game->_scene; + SequenceEntry &timerEntry = _entries[seqIndex]; + SpriteAsset &spriteSet = *scene._sprites[timerEntry._spritesIndex]; + + spriteSlot._flags = spriteSet.isBackground() ? IMG_DELTA : IMG_UPDATE; + spriteSlot._seqIndex = seqIndex; + spriteSlot._spritesIndex = timerEntry._spritesIndex; + spriteSlot._frameNumber = timerEntry._flipped ? -timerEntry._frameIndex : timerEntry._frameIndex; + spriteSlot._depth = timerEntry._depth; + spriteSlot._scale = timerEntry._scale; + + if (!timerEntry._nonFixed) { + spriteSlot._position = timerEntry._position; + } else { + MSprite *sprite = spriteSet.getFrame(timerEntry._frameIndex - 1); + spriteSlot._position = sprite->_offset; + } +} + +bool SequenceList::loadSprites(int seqIndex) { + Scene &scene = _vm->_game->_scene; + SequenceEntry &seqEntry = _entries[seqIndex]; + int slotIndex; + bool result = false; + int idx = -1; + + scene._spriteSlots.deleteTimer(seqIndex); + if (seqEntry._doneFlag) { + remove(seqIndex); + return false; + } + + if (seqEntry._spritesIndex == -1) { + // Doesn't have an associated sprite anymore, so mark as done + seqEntry._doneFlag = true; + } else if ((slotIndex = scene._spriteSlots.add()) >= 0) { + SpriteSlot &spriteSlot = scene._spriteSlots[slotIndex]; + setSpriteSlot(seqIndex, spriteSlot); + + if ((seqEntry._flags != 0) || (seqEntry._dynamicHotspotIndex >= 0)) { + SpriteAsset &spriteSet = *scene._sprites[seqEntry._spritesIndex]; + MSprite *frame = spriteSet.getFrame(seqEntry._frameIndex - 1); + int width = frame->getWidth() * seqEntry._scale / 200; + int height = frame->getHeight() * seqEntry._scale / 100; + Common::Point pt = spriteSlot._position; + + // Handle sprite movement, if present + if (seqEntry._flags & 1) { + seqEntry._posAccum.x += seqEntry._posDiff.x; + if (seqEntry._posAccum.x >= 100) { + int v = seqEntry._posAccum.x / 100; + seqEntry._position.x += v * seqEntry._posSign.x; + seqEntry._posAccum.x -= v * 100; + } + + seqEntry._posAccum.y += seqEntry._posDiff.y; + if (seqEntry._posAccum.y >= 100) { + int v = seqEntry._posAccum.y / 100; + seqEntry._position.y += v * seqEntry._posSign.y; + seqEntry._posAccum.y -= v * 100; + } + } + + if (seqEntry._flags & 2) { + // Check for object having moved off-scren + if ((pt.x + width) < 0 || (pt.x + width) >= MADS_SCREEN_WIDTH || + pt.y < 0 || (pt.y - height) >= MADS_SCENE_HEIGHT) { + result = true; + seqEntry._doneFlag = true; + } + } + + if (seqEntry._dynamicHotspotIndex >= 0) { + DynamicHotspot &dynHotspot = scene._dynamicHotspots[seqEntry._dynamicHotspotIndex]; + + dynHotspot._bounds.left = MAX(pt.x - width, 0); + dynHotspot._bounds.top = MAX(pt.y - height, 0); + dynHotspot._bounds.right = dynHotspot._bounds.left + width + 1; + dynHotspot._bounds.bottom = dynHotspot._bounds.top + height + 1; + + scene._dynamicHotspots._changed = true; + } + } + + // Frame adjustments + if (seqEntry._frameStart != seqEntry._numSprites) + seqEntry._frameIndex += seqEntry._frameInc; + + if (seqEntry._frameIndex >= seqEntry._frameStart) { + if (seqEntry._frameIndex > seqEntry._numSprites) { + result = true; + if (seqEntry._animType == ANIMTYPE_CYCLED) { + // back to the starting frame (cyclic) + seqEntry._frameIndex = seqEntry._frameStart; + } else { + // Switch into reverse mode + seqEntry._frameIndex = seqEntry._numSprites - 1; + seqEntry._frameInc = -1; + } + } + } else { + // Currently in reverse mode and moved past starting frame + result = true; + + if (seqEntry._animType == ANIMTYPE_CYCLED) + { + // Switch back to forward direction again + seqEntry._frameIndex = seqEntry._frameStart + 1; + seqEntry._frameInc = 1; + } else { + // Otherwise reset back to last sprite for further reverse animating + seqEntry._frameIndex = seqEntry._numSprites; + } + } + + if (result && (seqEntry._triggerCountdown != 0)) { + if (--seqEntry._triggerCountdown == 0) + seqEntry._doneFlag = true; + } + } else { + // Out of sprite display slots, so mark entry as done + seqEntry._doneFlag = true; + } + + for (int i = 0; i < seqEntry._entries._count; ++i) { + switch (seqEntry._entries._mode[i]) { + case SEQUENCE_TRIGGER_EXPIRE: + case SEQUENCE_TRIGGER_LOOP: + if (((seqEntry._entries._mode[i] == SEQUENCE_TRIGGER_EXPIRE) && seqEntry._doneFlag) || + ((seqEntry._entries._mode[i] == SEQUENCE_TRIGGER_LOOP) && result)) + idx = i; + break; + + case SEQUENCE_TRIGGER_SPRITE: { + int v = seqEntry._entries._frameIndex[i]; + if ((v == seqEntry._frameIndex) || (v == 0)) + idx = i; + break; + } + + default: + break; + } + } + + if (idx >= 0) { + _vm->_game->_trigger = seqEntry._entries._trigger[idx]; + _vm->_game->_triggerMode = seqEntry._triggerMode; + + if (seqEntry._triggerMode != SEQUENCE_TRIGGER_DAEMON) + scene._action._activeAction = seqEntry._actionNouns; + } + + return result; +} + +/** +* Handles counting down entries in the timer list for action +*/ +void SequenceList::tick() { + Scene &scene = _vm->_game->_scene; + for (uint idx = 0; idx < _entries.size(); ++idx) { + if ((_vm->_game->_fx == 0) && (_vm->_game->_trigger != 0)) + break; + + SequenceEntry &seqEntry = _entries[idx]; + uint32 currentTimer = scene._frameStartTime; + + if (!seqEntry._active || (currentTimer < seqEntry._timeout)) + continue; + + // Set the next timeout for the timer entry + seqEntry._timeout = currentTimer + seqEntry._numTicks; + + // Action the sprite + if (loadSprites(idx)) { + seqEntry._timeout += seqEntry._extraTicks; + } + } +} + +void SequenceList::delay(uint32 priorFrameTime, uint32 currentTime) { + for (uint idx = 0; idx < _entries.size(); ++idx) { + if (_entries[idx]._active) { + _entries[idx]._timeout += currentTime - priorFrameTime; + } + } +} + +void SequenceList::setAnimRange(int seqIndex, int startVal, int endVal) { + Scene &scene = _vm->_game->_scene; + SequenceEntry &seqEntry = _entries[seqIndex]; + SpriteAsset &spriteSet = *scene._sprites[seqEntry._spritesIndex]; + int numSprites = spriteSet.getCount(); + int tempStart, tempEnd; + + switch (startVal) { + case -2: + tempStart = numSprites; + break; + case -1: + tempStart = 1; + break; + default: + tempStart = startVal; + break; + } + + switch (endVal) { + case -2: + case 0: + tempEnd = numSprites; + break; + case -1: + tempEnd = 1; + break; + default: + tempEnd = endVal; + break; + } + + seqEntry._frameStart = tempStart; + seqEntry._numSprites = tempEnd; + + seqEntry._frameIndex = (seqEntry._frameInc >= 0) ? tempStart : tempEnd; +} + +void SequenceList::scan() { + Scene &scene = _vm->_game->_scene; + + for (uint i = 0; i < _entries.size(); ++i) { + if (_entries[i]._active && (_entries[i]._spritesIndex != -1)) { + int idx = scene._spriteSlots.add(); + setSpriteSlot(i, scene._spriteSlots[idx]); + } + } +} + +/** +* Sets the depth of the specified entry in the sequence list +*/ +void SequenceList::setDepth(int seqIndex, int depth) { + _entries[seqIndex]._depth = depth; +} + +void SequenceList::setPosition(int seqIndex, const Common::Point &pt) { + _entries[seqIndex]._position = pt; + _entries[seqIndex]._nonFixed = false; +} + +int SequenceList::addSpriteCycle(int srcSpriteIdx, bool flipped, int numTicks, int triggerCountdown, int timeoutTicks, int extraTicks) { + Scene &scene = _vm->_game->_scene; + MSprite *spriteFrame = scene._sprites[srcSpriteIdx]->getFrame(0); + int depth = scene._depthSurface.getDepth(Common::Point( + spriteFrame->_offset.x + (spriteFrame->w / 2), + spriteFrame->_offset.y + (spriteFrame->h / 2))); + + return add(srcSpriteIdx, flipped, 1, triggerCountdown, timeoutTicks, extraTicks, numTicks, 0, 0, + true, 100, depth - 1, 1, ANIMTYPE_CYCLED, 0, 0); +} + +int SequenceList::addReverseSpriteCycle(int srcSpriteIdx, bool flipped, int numTicks, + int triggerCountdown, int timeoutTicks, int extraTicks) { + Scene &scene = _vm->_game->_scene; + + SpriteAsset *asset = scene._sprites[srcSpriteIdx]; + MSprite *spriteFrame = asset->getFrame(0); + int depth = scene._depthSurface.getDepth(Common::Point( + spriteFrame->_offset.x + (spriteFrame->w / 2), + spriteFrame->_offset.y + (spriteFrame->h / 2))); + + return add(srcSpriteIdx, flipped, asset->getCount(), triggerCountdown, timeoutTicks, extraTicks, + numTicks, 0, 0, true, 100, depth - 1, -1, ANIMTYPE_CYCLED, 0, 0); +} + + +int SequenceList::startCycle(int srcSpriteIndex, bool flipped, int cycleIndex) { + int result = addSpriteCycle(srcSpriteIndex, flipped, INDEFINITE_TIMEOUT, 0, 0, 0); + if (result >= 0) + setAnimRange(result, cycleIndex, cycleIndex); + + return result; +} + +int SequenceList::startReverseCycle(int srcSpriteIndex, bool flipped, int numTicks, + int triggerCountdown, int timeoutTicks, int extraTicks) { + SpriteAsset *sprites = _vm->_game->_scene._sprites[srcSpriteIndex]; + MSprite *frame = sprites->getFrame(0); + int depth = _vm->_game->_scene._depthSurface.getDepth(Common::Point( + frame->_offset.x + frame->w / 2, frame->_offset.y + frame->h / 2)); + + return add(srcSpriteIndex, flipped, sprites->getCount(), triggerCountdown, timeoutTicks, + extraTicks, numTicks, 0, 0, true, 100, depth - 1, -1, ANIMTYPE_REVERSIBLE, 0, 0); +} + +void SequenceList::updateTimeout(int spriteIdx, int seqIndex) { + Player &player = _vm->_game->_player; + int timeout; + + if (spriteIdx >= 0) + timeout = _entries[spriteIdx]._timeout; + else + timeout = player._priorTimer + player._ticksAmount; + + if (seqIndex >= 0) + _entries[seqIndex]._timeout = timeout; + else + player._priorTimer = timeout - player._ticksAmount; + +} + +void SequenceList::setScale(int spriteIdx, int scale) { + _entries[spriteIdx]._scale = scale; +} + +void SequenceList::setMsgLayout(int seqIndex) { + Player &player = _vm->_game->_player; + int yp = player._playerPos.y + (player._centerOfGravity * player._currentScale) / 100; + setPosition(seqIndex, Common::Point(player._playerPos.x, yp)); + setDepth(seqIndex, player._currentDepth); + setScale(seqIndex, player._currentScale); + updateTimeout(-1, seqIndex); +} + +void SequenceList::setDone(int seqIndex) { + _entries[seqIndex]._doneFlag = true; + _entries[seqIndex]._timeout = _vm->_game->_player._priorTimer; +} + +void SequenceList::setMotion(int seqIndex, int flags, int deltaX, int deltaY) { + SequenceEntry &se = _entries[seqIndex]; + se._flags = flags | 1; + + // Set the direction sign for movement + if (deltaX > 0) { + se._posSign.x = 1; + } else if (deltaX < 0) { + se._posSign.x = -1; + } else { + se._posSign.x = 0; + } + + if (deltaY > 0) { + se._posSign.y = 1; + } + else if (deltaY < 0) { + se._posSign.y = -1; + } else { + se._posSign.y = 0; + } + + se._posDiff.x = ABS(deltaX); + se._posDiff.y = ABS(deltaY); + se._posAccum.x = se._posAccum.y = 0; +} + +} // End of namespace |