diff options
Diffstat (limited to 'engines/toon')
38 files changed, 13659 insertions, 0 deletions
diff --git a/engines/toon/anim.cpp b/engines/toon/anim.cpp new file mode 100644 index 0000000000..53980e3dc1 --- /dev/null +++ b/engines/toon/anim.cpp @@ -0,0 +1,738 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ + +#include "toon/anim.h" +#include "toon/toon.h" +#include "toon/tools.h" + +namespace Toon { + +bool Animation::loadAnimation(Common::String file) { + debugC(1, kDebugAnim, "loadAnimation(%s)", file.c_str()); + + uint32 fileSize = 0; + uint8 *fileData = _vm->resources()->getFileData(file, &fileSize); + if (!fileData) + return false; + + strcpy(_name, "not_loaded"); + if (strncmp((char *)fileData, "KevinAguilar", 12)) + return false; + + strcpy(_name, file.c_str()); + + uint32 headerSize = READ_LE_UINT32(fileData + 16); + uint32 uncompressedBytes = READ_LE_UINT32(fileData + 20); + uint32 compressedBytes = READ_LE_UINT32(fileData + 24); + _numFrames = READ_LE_UINT32(fileData + 28); + _x1 = READ_LE_UINT32(fileData + 32); + _y1 = READ_LE_UINT32(fileData + 36); + _x2 = READ_LE_UINT32(fileData + 40); + _y2 = READ_LE_UINT32(fileData + 44); + _paletteEntries = READ_LE_UINT32(fileData + 56); + _fps = READ_LE_UINT32(fileData + 60); + uint32 paletteSize = READ_LE_UINT32(fileData + 64); + + uint8 *currentData = fileData + 68; + if (_paletteEntries) { + if (paletteSize) { + _palette = new uint8[paletteSize]; + memcpy(_palette, currentData, paletteSize); + currentData += paletteSize; + } else { + _palette = 0; + } + } + + byte *finalBuffer = new byte[uncompressedBytes]; + if (uncompressedBytes > compressedBytes) + decompressLZSS(currentData, finalBuffer, uncompressedBytes); + else + memcpy(finalBuffer, currentData, uncompressedBytes); + + if (READ_LE_UINT32(finalBuffer) == 0x12345678) { + uint8 *data = finalBuffer; + _frames = new AnimationFrame[_numFrames]; + for (int32 e = 0; e < _numFrames; e++) { + if (READ_LE_UINT32(data) != 0x12345678) + return false; + + int32 oldRef = READ_LE_UINT32(data + 4); + uint32 compressedSize = READ_LE_UINT32(data + 8); + uint32 decompressedSize = READ_LE_UINT32(data + 12); + + _frames[e]._x1 = READ_LE_UINT32(data + 16); + _frames[e]._y1 = READ_LE_UINT32(data + 20); + _frames[e]._x2 = READ_LE_UINT32(data + 24); + _frames[e]._y2 = READ_LE_UINT32(data + 28); + + uint8 *imageData = data + headerSize; + if (oldRef != -1 || decompressedSize == 0) { + _frames[e]._ref = oldRef; + _frames[e]._data = 0; + } else { + _frames[e]._ref = -1; + _frames[e]._data = new uint8[decompressedSize]; + if (compressedSize < decompressedSize) { + decompressLZSS(imageData, _frames[e]._data, decompressedSize); + } else { + memcpy(_frames[e]._data, imageData, compressedSize); + } + } + + data += headerSize + compressedSize; + } + } + + delete[] finalBuffer; + return true; +} + +Animation::Animation(ToonEngine *vm) : _vm(vm) { + _palette = 0; + _frames = 0; +} + +Animation::~Animation() { + delete[] _palette; + for (int32 i = 0; i < _numFrames; i++) { + delete[] _frames[i]._data; + } + delete[] _frames; +} + +Common::Rect Animation::getRect() { + debugC(5, kDebugAnim, "getRect"); + return Common::Rect(_x1, _y1, _x2, _y2); +} + +void Animation::drawFrame(Graphics::Surface &surface, int32 frame, int32 xx, int32 yy) { + debugC(3, kDebugAnim, "drawFrame(surface, %d, %d, %d)", frame, xx, yy); + if (frame < 0) + frame = 0; + + if (frame >= _numFrames) + frame = _numFrames - 1; + + if (_numFrames == 0) + return; + + if (_frames[frame]._ref != -1) + frame = _frames[frame]._ref; + + int32 rectX = _frames[frame]._x2 - _frames[frame]._x1; + int32 rectY = _frames[frame]._y2 - _frames[frame]._y1; + int32 offsX = 0; + int32 offsY = 0; + + if (xx + _x1 + _frames[frame]._x1 < 0) { + offsX = -(xx + _x1 + _frames[frame]._x1); + } + + if (offsX >= rectX) + return; + else + rectX -= offsX; + + if (yy + _y1 + _frames[frame]._y1 < 0) { + offsY = -(yy + _y1 + _frames[frame]._y1); + } + + if (offsY >= rectY) + return; + else + rectY -= offsY; + + if (rectX + xx + _x1 + _frames[frame]._x1 >= surface.w) + rectX = surface.w - xx - _x1 - _frames[frame]._x1; + + if (rectX < 0) + return; + + if (rectY + yy + _y1 + _frames[frame]._y1 >= surface.h) + rectY = surface.h - yy - _y1 - _frames[frame]._y1; + + if (rectY < 0) + return; + + int32 destPitch = surface.pitch; + uint8 *srcRow = _frames[frame]._data + offsX + (_frames[frame]._x2 - _frames[frame]._x1) * offsY; + uint8 *curRow = (uint8 *)surface.pixels + (yy + _frames[frame]._y1 + _y1 + offsY) * destPitch + (xx + _x1 + _frames[frame]._x1 + offsX); + for (int32 y = 0; y < rectY; y++) { + uint8 *cur = curRow; + uint8 *c = srcRow + y * (_frames[frame]._x2 - _frames[frame]._x1); + for (int32 x = 0; x < rectX; x++) { + if (*c) + *cur = *c; + c++; + cur++; + } + curRow += destPitch; + } +} + +void Animation::drawFrameWithMask(Graphics::Surface &surface, int32 frame, int32 xx, int32 yy, int32 zz, Picture *mask) { + debugC(1, kDebugAnim, "drawFrameWithMask(surface, %d, %d, %d, %d, mask)", frame, xx, yy, zz); + warning("STUB: drawFrameWithMask()"); +} + +void Animation::drawFrameWithMaskAndScale(Graphics::Surface &surface, int32 frame, int32 xx, int32 yy, int32 zz, Picture *mask, int32 scale) { + debugC(5, kDebugAnim, "drawFrameWithMaskAndScale(surface, %d, %d, %d, %d, mask, %d)", frame, xx, yy, zz, scale); + if (_frames[frame]._ref != -1) + frame = _frames[frame]._ref; + int32 rectX = _frames[frame]._x2 - _frames[frame]._x1; + int32 rectY = _frames[frame]._y2 - _frames[frame]._y1; + + int32 finalWidth = rectX * scale / 1024; + int32 finalHeight = rectY * scale / 1024; + + // compute final x1,y1,x2,y2 + int32 xx1 = xx + _x1 + _frames[frame]._x1 * scale / 1024; + int32 yy1 = yy + _y1 + _frames[frame]._y1 * scale / 1024; + int32 xx2 = xx1 + finalWidth; + int32 yy2 = yy1 + finalHeight; + int32 w = _frames[frame]._x2 - _frames[frame]._x1; +// Strangerke - Commented (not used) +// int32 h = _frames[frame]._y2 - _frames[frame]._y1; + + int32 destPitch = surface.pitch; + int32 destPitchMask = mask->getWidth(); + uint8 *c = _frames[frame]._data; + uint8 *curRow = (uint8 *)surface.pixels; + uint8 *curRowMask = mask->getDataPtr(); + + if (strstr(_name, "shadow")) { + for (int y = yy1; y < yy2; y++) { + for (int x = xx1; x < xx2; x++) { + if (x < 0 || x >= 1280 || y < 0 || y >= 400) + continue; + + uint8 *cur = curRow + x + y * destPitch; + uint8 *curMask = curRowMask + x + y * destPitchMask; + + // find the good c + int32 xs = (x - xx1) * 1024 / scale; + int32 ys = (y - yy1) * 1024 / scale; + uint8 *cc = &c[ys * w + xs]; + if (*cc && ((*curMask) >= zz)) + *cur = _vm->getShadowLUT()[*cur]; + } + } + } else { + for (int y = yy1; y < yy2; y++) { + for (int x = xx1; x < xx2; x++) { + if (x < 0 || x >= 1280 || y < 0 || y >= 400) + continue; + + uint8 *cur = curRow + x + y * destPitch; + uint8 *curMask = curRowMask + x + y * destPitchMask; + + // find the good c + int32 xs = (x - xx1) * 1024 / scale; + int32 ys = (y - yy1) * 1024 / scale; + uint8 *cc = &c[ys * w + xs]; + if (*cc && ((*curMask) >= zz)) + *cur = *cc; + } + } + } +} + +void Animation::applyPalette(int32 offset, int32 srcOffset, int32 numEntries) { + debugC(1, kDebugAnim, "applyPalette(%d, %d, %d)", offset, srcOffset, numEntries); + _vm->setPaletteEntries(_palette + srcOffset, offset, numEntries); +} + +int32 Animation::getFrameWidth(int32 frame) { + debugC(4, kDebugAnim, "getFrameWidth(%d)", frame); + if ((frame < 0) || (frame >= _numFrames)) + return 0; + + if (_frames[frame]._ref != -1) + frame = _frames[frame]._ref; + + return _frames[frame]._x2 - _frames[frame]._x1; +} + +int32 Animation::getFrameHeight(int32 frame) { + debugC(4, kDebugAnim, "getFrameHeight(%d)", frame); + if (frame < 0 || frame >= _numFrames) + return 0; + + if (_frames[frame]._ref != -1) + frame = _frames[frame]._ref; + + return _frames[frame]._y2 - _frames[frame]._y1; +} + +int32 Animation::getWidth() const { + return _x2 - _x1; +} + +int32 Animation::getHeight() const { + return _y2 - _y1; +} + +void Animation::drawFontFrame(Graphics::Surface &surface, int32 frame, int32 xx, int32 yy, byte *colorMap) { + debugC(4, kDebugAnim, "drawFontFrame(surface, %d, %d, %d, colorMap)", frame, xx, yy); + if (frame < 0) + frame = 0; + + if (frame >= _numFrames) + frame = _numFrames - 1; + + if (_numFrames == 0) + return; + + if (_frames[frame]._ref != -1) + frame = _frames[frame]._ref; + + int32 rectX = _frames[frame]._x2 - _frames[frame]._x1; + int32 rectY = _frames[frame]._y2 - _frames[frame]._y1; + + if ((xx + _x1 + _frames[frame]._x1 < 0) || (yy + _y1 + _frames[frame]._y1 < 0)) + return; + + if (rectX + xx + _x1 + _frames[frame]._x1 >= surface.w) + rectX = surface.w - xx - _x1 - _frames[frame]._x1; + + if (rectX < 0) + return; + + if (rectY + yy + _y1 + _frames[frame]._y1 >= surface.h) + rectY = surface.h - yy - _y1 - _frames[frame]._y1; + + if (rectY < 0) + return; + + int32 destPitch = surface.pitch; + uint8 *c = _frames[frame]._data; + uint8 *curRow = (uint8 *)surface.pixels + (yy + _frames[frame]._y1 + _y1) * destPitch + (xx + _x1 + _frames[frame]._x1); + for (int32 y = 0; y < rectY; y++) { + unsigned char *cur = curRow; + for (int32 x = 0; x < rectX; x++) { + if (*c && *c < 4) + *cur = colorMap[*c]; + c++; + cur++; + } + curRow += destPitch; + } +} + +void Animation::drawFrameOnPicture(int32 frame, int32 xx, int32 yy) { + debugC(1, kDebugAnim, "drawFrameOnPicture(%d, %d, %d)", frame, xx, yy); + if (frame < 0) + frame = 0; + + if (frame >= _numFrames) + frame = _numFrames - 1; + + if (_numFrames == 0) + return; + + if (_frames[frame]._ref != -1) + frame = _frames[frame]._ref; + + int32 rectX = _frames[frame]._x2 - _frames[frame]._x1; + int32 rectY = _frames[frame]._y2 - _frames[frame]._y1; + + Picture *pic = _vm->getPicture(); + + if ((xx + _x1 + _frames[frame]._x1 < 0) || (yy + _y1 + _frames[frame]._y1 < 0)) + return; + + if (rectX + xx + _x1 + _frames[frame]._x1 >= pic->getWidth()) + rectX = pic->getWidth() - xx - _x1 - _frames[frame]._x1; + + if (rectX < 0) + return; + + if (rectY + yy + _y1 + _frames[frame]._y1 >= pic->getHeight()) + rectY = pic->getHeight() - yy - _y1 - _frames[frame]._y1; + + if (rectY < 0) + return; + + int32 destPitch = pic->getWidth(); + uint8 *c = _frames[frame]._data; + uint8 *curRow = (uint8 *)pic->getDataPtr() + (yy + _frames[frame]._y1 + _y1) * destPitch + (xx + _x1 + _frames[frame]._x1); + for (int32 y = 0; y < rectY; y++) { + unsigned char *cur = curRow; + for (int32 x = 0; x < rectX; x++) { + if (*c) + *cur = *c; + c++; + cur++; + } + curRow += destPitch; + } +} + +void AnimationInstance::update(int32 timeIncrement) { + debugC(5, kDebugAnim, "update(%d)", timeIncrement); + if (_currentFrame == -1) + return; + + if (_rangeStart == _rangeEnd) { + _currentFrame = _rangeStart; + return; + } + + if (_playing) { + _currentTime += timeIncrement; + _currentFrame = _currentTime / (1000 / _fps); + } + + if (_looping) { + _currentFrame = (_currentFrame % (_rangeEnd - _rangeStart + 1)) + _rangeStart; + } else { + if (_currentFrame >= _rangeEnd - _rangeStart) { + _playing = false; + _currentFrame = _rangeEnd; + } else { + _currentFrame = _rangeStart + _currentFrame; + } + } +} + +AnimationInstance::AnimationInstance(ToonEngine *vm, AnimationInstanceType type) : _vm(vm) { + _id = 0; + _type = type; + _animation = 0; + _currentFrame = 0; + _currentTime = 0; + _fps = 15; + _looping = true; + _playing = false; + _rangeEnd = 0; + _useMask = false; + _alignBottom = false; + _rangeStart = 0; + _scale = 1024; + _x = 0; + _y = 0; + _z = 0; + _layerZ = 0; +} + + +void AnimationInstance::render() { + debugC(5, kDebugAnim, "render()"); + if (_visible && _animation) { + int32 frame = _currentFrame; + if (frame < 0) + frame = 0; + + if (frame >= _animation->_numFrames) + frame = _animation->_numFrames - 1; + + int32 x = _x; + int32 y = _y; + + if (_alignBottom) { + int32 offsetX = (_animation->_x2 - _animation->_x1) / 2 * (_scale - 1024); + int32 offsetY = (_animation->_y2 - _animation->_y1) * (_scale - 1024); + + x -= offsetX >> 10; + y -= offsetY >> 10; + } + + if (_useMask) { + //if (_scale == 100) { // 100% scale + // _animation->drawFrameWithMask(_vm->getMainSurface(), _currentFrame, _x, _y, _z, _vm->getMask()); + //} else { + _animation->drawFrameWithMaskAndScale(_vm->getMainSurface(), frame, x, y, _z, _vm->getMask(), _scale); + //} + } else { + _animation->drawFrame(_vm->getMainSurface(), frame, _x, _y); + } + } +} + +void AnimationInstance::renderOnPicture() { + debugC(5, kDebugAnim, "renderOnPicture()"); + if (_visible && _animation) + _animation->drawFrameOnPicture(_currentFrame, _x, _y); +} + +void AnimationInstance::playAnimation() { + debugC(6, kDebugAnim, "playAnimation()"); + _playing = true; +} + +void AnimationInstance::setAnimation(Animation *animation, bool setRange) { + debugC(5, kDebugAnim, "setAnimation(animation)"); + _animation = animation; + if (animation && setRange) { + _rangeStart = 0; + _rangeEnd = animation->_numFrames - 1; + } +} + +void AnimationInstance::setAnimationRange(int32 rangeStart, int rangeEnd) { + debugC(5, kDebugAnim, "setAnimationRange(%d, %d)", rangeStart, rangeEnd); + _rangeStart = rangeStart; + _rangeEnd = rangeEnd; + + if (_currentFrame < _rangeStart) + _currentFrame = _rangeStart; + + if (_currentFrame > _rangeEnd) + _currentFrame = _rangeEnd; +} + +void AnimationInstance::setPosition(int32 x, int32 y, int32 z, bool relative) { + debugC(5, kDebugAnim, "setPosition(%d, %d, %d, %d)", x, y, z, (relative) ? 1 : 0); + if (relative || !_animation) { + _x = x; + _y = y; + _z = z; + } else { + _x = x - _animation->_x1; + _y = y - _animation->_y1; + _z = z; + } +} + +void AnimationInstance::moveRelative(int32 dx, int32 dy, int32 dz) { + debugC(1, kDebugAnim, "moveRelative(%d, %d, %d)", dx, dy, dz); + _x += dx; + _y += dy; + _z += dz; +} + +void AnimationInstance::forceFrame(int32 position) { + debugC(5, kDebugAnim, "forceFrame(%d)", position); + _currentFrame = position; + _rangeStart = position; + _rangeEnd = position; +} + +void AnimationInstance::setFrame(int32 position) { + debugC(5, kDebugAnim, "setFrame(%d)", position); + _currentFrame = position; +} + +void AnimationInstance::setFps(int32 fps) { + debugC(4, kDebugAnim, "setFps(%d)", fps); + _fps = fps; +} + +void AnimationInstance::stopAnimation() { + debugC(5, kDebugAnim, "stopAnimation()"); + _playing = false; +} + +void AnimationInstance::setVisible(bool visible) { + debugC(1, kDebugAnim, "setVisible(%d)", (visible) ? 1 : 0); + _visible = visible; +} + +void AnimationInstance::setScale(int32 scale, bool align) { + debugC(4, kDebugAnim, "setScale(%d)", scale); + _scale = scale; + _alignBottom = align; +} + +void AnimationInstance::setUseMask(bool useMask) { + debugC(1, kDebugAnim, "setUseMask(%d)", (useMask) ? 1 : 0); + _useMask = useMask; +} + +void AnimationInstance::getRect(int32 *x1, int32 *y1, int32 *x2, int32 *y2) const { + debugC(5, kDebugAnim, "getRect(%d, %d, %d, %d)", *x1, *y1, *x2, *y2); + int32 rectX = _animation->_frames[_currentFrame]._x2 - _animation->_frames[_currentFrame]._x1; + int32 rectY = _animation->_frames[_currentFrame]._y2 - _animation->_frames[_currentFrame]._y1; + + int32 finalWidth = rectX * _scale / 1024; + int32 finalHeight = rectY * _scale / 1024; + + // compute final x1,y1,x2,y2 + *x1 = _x + _animation->_x1 + _animation->_frames[_currentFrame]._x1 * _scale / 1024; + *y1 = _y + _animation->_y1 + _animation->_frames[_currentFrame]._y1 * _scale / 1024; + *x2 = *x1 + finalWidth; + *y2 = *y1 + finalHeight; +} + +void AnimationInstance::setX(int32 x, bool relative) { + debugC(1, kDebugAnim, "setX(%d, %d)", x, (relative) ? 1 : 0); + if (relative || !_animation) + _x = x; + else + _x = x - _animation->_x1; +} + +void AnimationInstance::setY(int32 y, bool relative) { + debugC(1, kDebugAnim, "setY(%d, %d)", y, (relative) ? 1 : 0); + if (relative || !_animation) + _y = y; + else + _y = y - _animation->_y1; +} + +void AnimationInstance::setZ(int32 z, bool relative) { + debugC(1, kDebugAnim, "setZ(%d, %d)", z, (relative) ? 1 : 0); + _z = z; +} + +void AnimationInstance::setLayerZ(int32 z) { + _layerZ = z; +} + +int32 AnimationInstance::getLayerZ() const { + return _layerZ; +} + +int32 AnimationInstance::getX2() const { + return _x + _animation->_x1; +} + +int32 AnimationInstance::getY2() const { + return _y + _animation->_y1; +} + +int32 AnimationInstance::getZ2() const { + return _z; +} + +void AnimationInstance::save(Common::WriteStream *stream) { + // we don't load the animation here + // it must be loaded externally to avoid leaks. + stream->writeSint32LE(_currentFrame); + stream->writeSint32LE(_currentTime); + stream->writeSint32LE(_layerZ); + stream->writeSint32LE(_x); + stream->writeSint32LE(_y); + stream->writeSint32LE(_z); + stream->writeSint32LE(_scale); + stream->writeSint32LE(_playing); + stream->writeSint32LE(_looping); + stream->writeSint32LE(_rangeStart); + stream->writeSint32LE(_rangeEnd); + stream->writeSint32LE(_rangeStart); + stream->writeSint32LE(_fps); + stream->writeSint32LE(_id); + stream->writeSint32LE(_type); + stream->writeSint32LE(_visible); + stream->writeSint32LE(_useMask); +} +void AnimationInstance::load(Common::ReadStream *stream) { + _currentFrame = stream->readSint32LE(); + _currentTime = stream->readSint32LE(); + _layerZ = stream->readSint32LE(); + _x = stream->readSint32LE(); + _y = stream->readSint32LE(); + _z = stream->readSint32LE(); + _scale = stream->readSint32LE(); + _playing = stream->readSint32LE(); + _looping = stream->readSint32LE(); + _rangeStart = stream->readSint32LE(); + _rangeEnd = stream->readSint32LE(); + _rangeStart = stream->readSint32LE(); + _fps = stream->readSint32LE(); + _id = stream->readSint32LE(); + _type = (AnimationInstanceType)stream->readSint32LE(); + _visible = stream->readSint32LE(); + _useMask = stream->readSint32LE(); +} + + + +void AnimationInstance::setLooping(bool enable) { + debugC(6, kDebugAnim, "setLooping(%d)", (enable) ? 1 : 0); + _looping = enable; +} + +void AnimationInstance::reset() { + _currentFrame = 0; + _currentTime = 0; +} + +AnimationManager::AnimationManager(ToonEngine *vm) : _vm(vm) { +} + +void AnimationManager::addInstance(AnimationInstance *instance) { + _instances.push_back(instance); +} + +void AnimationManager::removeInstance(AnimationInstance *instance) { + debugC(1, kDebugAnim, "removeInstance(instance)"); + int32 found = -1; + for (uint32 i = 0; i < _instances.size(); i++) { + if (_instances[i] == instance) { + found = i; + break; + } + } + + if (found > -1) + _instances.remove_at(found); +} + +void AnimationManager::removeAllInstances(AnimationInstanceType type) { + debugC(1, kDebugAnim, "removeInstance(type)"); + for (int32 i = (int32)_instances.size(); i >= 0; i--) { + if (_instances[i]->getType() & type) + _instances.remove_at(i); + } +} + +void AnimationManager::update(int32 timeIncrement) { + debugC(5, kDebugAnim, "update(%d)", timeIncrement); + for (uint32 i = 0; i < _instances.size(); i++) + _instances[i]->update(timeIncrement); +} + +void AnimationManager::render() { + debugC(5, kDebugAnim, "render()"); + // sort the instance by layer z + // bubble sort (replace by faster afterwards) + bool changed = true; + while (changed) { + changed = false; + for (uint32 i = 0; i < _instances.size() - 1; i++) { + if ((_instances[i]->getLayerZ() > _instances[i + 1]->getLayerZ()) || + ((_instances[i]->getLayerZ() == _instances[i + 1]->getLayerZ()) && + (_instances[i]->getId() < _instances[i+1]->getId()))) { + AnimationInstance *instance = _instances[i]; + _instances[i] = _instances[i + 1]; + _instances[i + 1] = instance; + changed = true; + } + } + } + + for (uint32 i = 0; i < _instances.size(); i++) { + if (_instances[i]->getVisible()) + _instances[i]->render(); + } +} + +AnimationInstance *AnimationManager::createNewInstance(AnimationInstanceType type) { + return new AnimationInstance(_vm, type); +} + +} // End of namespace Toon diff --git a/engines/toon/anim.h b/engines/toon/anim.h new file mode 100644 index 0000000000..01475276c5 --- /dev/null +++ b/engines/toon/anim.h @@ -0,0 +1,196 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ + +#ifndef TOON_ANIM_H +#define TOON_ANIM_H + +#include "common/stream.h" +#include "common/array.h" +#include "common/func.h" +#include "graphics/surface.h" + +#include "toon/script.h" + +namespace Toon { + +class Picture; +class ToonEngine; + +struct AnimationFrame { + int32 _x1; + int32 _y1; + int32 _x2; + int32 _y2; + int32 _ref; + uint8 *_data; +}; + +class Animation { +public: + Animation(ToonEngine *vm); + ~Animation(); + + int32 _x1; + int32 _y1; + int32 _x2; + int32 _y2; + int32 _numFrames; + int32 _fps; + AnimationFrame *_frames; + uint8 *_palette; + int32 _paletteEntries; + char _name[32]; + + bool loadAnimation(Common::String file); + void drawFrame(Graphics::Surface &surface, int32 frame, int32 x, int32 y); + void drawFontFrame(Graphics::Surface &surface, int32 frame, int32 x, int32 y, byte *colorMap); + void drawFrameOnPicture(int32 frame, int32 x, int32 y); + void drawFrameWithMask(Graphics::Surface &surface, int32 frame, int32 xx, int32 yy, int32 zz, Picture *mask); + void drawFrameWithMaskAndScale(Graphics::Surface &surface, int32 frame, int32 xx, int32 yy, int32 zz, Picture *mask, int32 scale); + void drawStrip(int32 offset = 0); + void applyPalette(int32 offset, int32 srcOffset, int32 numEntries); + int32 getFrameWidth(int32 frame); + int32 getFrameHeight(int32 frame); + int32 getWidth() const; + int32 getHeight() const; + Common::Rect getRect(); +protected: + ToonEngine *_vm; +}; + +enum AnimationInstanceType { + kAnimationCharacter = 1, + kAnimationScene = 2, + kAnimationCursor = 4 +}; + +class AnimationInstance { +public: + AnimationInstance(ToonEngine *vm, AnimationInstanceType type); + void update(int32 timeIncrement); + void render(); + void renderOnPicture(); + void setAnimation(Animation *animation, bool setRange = true); + void playAnimation(); + void setAnimationRange(int32 rangeStart, int rangeEnd); + void setFps(int32 fps); + void setLooping(bool enable); + void stopAnimation(); + void setFrame(int32 position); + void forceFrame(int32 position); + void setPosition(int32 x, int32 y, int32 z, bool relative = false); + Animation *getAnimation() const { return _animation; } + void setScale(int32 scale, bool align = false); + void setVisible(bool visible); + bool getVisible() const { return _visible; } + void setUseMask(bool useMask); + void moveRelative(int32 dx, int32 dy, int32 dz); + void getRect(int32 *x1, int32 *y1, int32 *x2, int32 *y2) const; + int32 getX() const { return _x; } + int32 getY() const { return _y; } + int32 getZ() const { return _z; } + int32 getX2() const; + int32 getY2() const; + int32 getZ2() const; + int32 getFrame() const { return _currentFrame; } + void reset(); + void save(Common::WriteStream *stream); + void load(Common::ReadStream *stream); + + void setId(int32 id) { _id = id; } + int32 getId() const { return _id; } + + void setX(int32 x, bool relative = false); + void setY(int32 y, bool relative = false); + void setZ(int32 z, bool relative = false); + void setLayerZ(int32 layer); + int32 getLayerZ() const; + + AnimationInstanceType getType() const { return _type; } + +protected: + int32 _currentFrame; + int32 _currentTime; + int32 _fps; + Animation *_animation; + int32 _x; + int32 _y; + int32 _z; + int32 _layerZ; + int32 _rangeStart; + int32 _rangeEnd; + int32 _scale; + int32 _id; + + AnimationInstanceType _type; + + bool _useMask; + bool _playing; + bool _looping; + bool _visible; + bool _alignBottom; + + ToonEngine *_vm; +}; + +class AnimationManager { +public: + AnimationManager(ToonEngine *vm); + AnimationInstance *createNewInstance(AnimationInstanceType type); + void addInstance(AnimationInstance *instance); + void removeInstance(AnimationInstance *instance); + void removeAllInstances(AnimationInstanceType type); + void render(); + void update(int32 timeIncrement); + +protected: + ToonEngine *_vm; + Common::Array<AnimationInstance *> _instances; +}; + +class SceneAnimation { +public: + AnimationInstance *_animInstance; + Animation *_animation; + int32 _id; + bool _active; + + void load(ToonEngine *vm, Common::ReadStream *stream); + void save(ToonEngine *vm, Common::WriteStream *stream); +}; + +class SceneAnimationScript { +public: + EMCData *_data; + EMCState _state; + uint32 _lastTimer; + bool _frozen; + bool _frozenForConversation; + bool _active; +}; + +} // End of namespace Toon + +#endif diff --git a/engines/toon/audio.cpp b/engines/toon/audio.cpp new file mode 100644 index 0000000000..4381dad6f7 --- /dev/null +++ b/engines/toon/audio.cpp @@ -0,0 +1,629 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ + +#include "toon/audio.h" + +namespace Toon { + +static int ADPCM_index[8] = { + -1, -1, -1, -1, 2 , 4 , 6 , 8 +}; +static int ADPCM_table[89] = { + 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, + 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, + 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, + 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, + 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, + 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, + 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, + 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, + 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 +}; + +AudioManager::AudioManager(ToonEngine *vm, Audio::Mixer *mixer) : _vm(vm), _mixer(mixer) { + for (int32 i = 0; i < 16; i++) + _channels[i] = 0; + + for (int32 i = 0; i < 4; i++) + _audioPacks[i] = 0; + + for (int32 i = 0; i < 4; i++) { + _ambientSFXs[i]._delay = 0; + _ambientSFXs[i]._enabled = false; + _ambientSFXs[i]._id = -1; + _ambientSFXs[i]._channel = -1; + _ambientSFXs[i]._lastTimer = 0; + _ambientSFXs[i]._volume = 255; + } + + _voiceMuted = false; + _musicMuted = false; + _sfxMuted = false; +} + +AudioManager::~AudioManager(void) { + for (int32 i = 0; i < 4; i++) { + closeAudioPack(i); + } +} + +void AudioManager::muteMusic(bool muted) { + setMusicVolume(muted ? 0 : 255); + _musicMuted = muted; +} + +void AudioManager::muteVoice(bool muted) { + if(voiceStillPlaying() && _channels[2]) { + _channels[2]->setVolume(muted ? 0 : 255); + } + _voiceMuted = muted; +} + +void AudioManager::muteSfx(bool muted) { + _sfxMuted = muted; +} + +void AudioManager::removeInstance(AudioStreamInstance *inst) { + debugC(1, kDebugAudio, "removeInstance(inst)"); + + for (int32 i = 0; i < 16; i++) { + if (inst == _channels[i]) + _channels[i] = 0; + } +} + +void AudioManager::playMusic(Common::String dir, Common::String music) { + debugC(1, kDebugAudio, "playMusic(%s, %s)", dir.c_str(), music.c_str()); + + // two musics can be played at same time + Common::String path = Common::String::printf("act%d/%s/%s.mus", _vm->state()->_currentChapter, dir.c_str(), music.c_str()); + + if (_currentMusicName == music) + return; + + _currentMusicName = music; + + Common::SeekableReadStream *srs = _vm->resources()->openFile(path); + if (!srs) + return; + + // see what channel to take + if (_channels[0] && _channels[0]->isPlaying() && _channels[1] && _channels[1]->isPlaying()) { + // take the one that is fading + if (_channels[0]->isFading()) { + _channels[0]->stop(false); + _channels[1]->stop(true); + _currentMusicChannel = 0; + } else { + _channels[1]->stop(false); + _channels[0]->stop(true); + _currentMusicChannel = 1; + } + } else if (_channels[0] && _channels[0]->isPlaying()) { + _channels[0]->stop(true); + _currentMusicChannel = 1; + } else { + if (_channels[1] && _channels[1]->isPlaying()) + _channels[1]->stop(true); + _currentMusicChannel = 0; + } + + + //if (!_channels[_currentMusicChannel]) + // delete _channels[_currentMusicChannel]; + _channels[_currentMusicChannel] = new AudioStreamInstance(this, _mixer, srs, true); + _channels[_currentMusicChannel]->setVolume(_musicMuted ? 0 : 255); + _channels[_currentMusicChannel]->play(true, Audio::Mixer::kMusicSoundType); +} + +bool AudioManager::voiceStillPlaying() { + if (!_channels[2]) + return false; + + return _channels[2]->isPlaying(); +} + +void AudioManager::playVoice(int32 id, bool genericVoice) { + debugC(1, kDebugAudio, "playVoice(%d, %d)", id, (genericVoice) ? 1 : 0); + + if (voiceStillPlaying()) { + // stop current voice + _channels[2]->stop(false); + } + + Common::SeekableReadStream *stream; + if (genericVoice) + stream = _audioPacks[0]->getStream(id); + else + stream = _audioPacks[1]->getStream(id); + + _channels[2] = new AudioStreamInstance(this, _mixer, stream); + _channels[2]->play(false, Audio::Mixer::kSpeechSoundType); + _channels[2]->setVolume(_voiceMuted ? 0 : 255); + +} + +int32 AudioManager::playSFX(int32 id, int volume , bool genericSFX) { + debugC(4, kDebugAudio, "playSFX(%d, %d)", id, (genericSFX) ? 1 : 0); + + // find a free SFX channel + Common::SeekableReadStream *stream; + + if (genericSFX) + stream = _audioPacks[2]->getStream(id, true); + else + stream = _audioPacks[3]->getStream(id, true); + + if (stream->size() == 0) + return -1; + + for (int32 i = 3; i < 16; i++) { + if (!_channels[i]) { + _channels[i] = new AudioStreamInstance(this, _mixer, stream); + _channels[i]->play(false, Audio::Mixer::kSFXSoundType); + _channels[i]->setVolume(_sfxMuted ? 0 : volume); + return i; + } + } + + return -1; +} + +void AudioManager::stopAllSfxs() { + for (int32 i = 3; i < 16; i++) { + if (_channels[i] && _channels[i]->isPlaying()) { + _channels[i]->stop(false); + } + } +} + +void AudioManager::stopCurrentVoice() { + debugC(1, kDebugAudio, "stopCurrentVoice()"); + + if (_channels[2] && _channels[2]->isPlaying()) + _channels[2]->stop(false); +} + + +void AudioManager::closeAudioPack(int32 id) { + if(_audioPacks[id]) { + delete _audioPacks[id]; + _audioPacks[id] = 0; + } +} + +bool AudioManager::loadAudioPack(int32 id, Common::String indexFile, Common::String packFile) { + debugC(4, kDebugAudio, "loadAudioPack(%d, %s, %s)", id, indexFile.c_str(), packFile.c_str()); + + closeAudioPack(id); + _audioPacks[id] = new AudioStreamPackage(_vm); + return _audioPacks[id]->loadAudioPackage(indexFile, packFile); +} + +void AudioManager::setMusicVolume(int32 volume) { + debugC(1, kDebugAudio, "setMusicVolume(%d)", volume); + if (_channels[0]) + _channels[0]->setVolume(volume); + + if (_channels[1]) + _channels[1]->setVolume(volume); +} + +void AudioManager::stopMusic() { + debugC(1, kDebugAudio, "stopMusic()"); + + if (_channels[0]) + _channels[0]->stop(true); + if (_channels[1]) + _channels[1]->stop(true); +} +AudioStreamInstance::~AudioStreamInstance() { + if (_man) + _man->removeInstance(this); +} + +AudioStreamInstance::AudioStreamInstance(AudioManager *man, Audio::Mixer *mixer, Common::SeekableReadStream *stream , bool looping) { + _compBufferSize = 0; + _buffer = 0; + _bufferMaxSize = 0; + _mixer = mixer; + _compBuffer = 0; + _bufferOffset = 0; + _lastADPCMval1 = 0; + _lastADPCMval2 = 0; + _file = stream; + _fadingIn = false; + _fadingOut = false; + _fadeTime = 0; + _stopped = false; + _volume = 255; + _totalSize = stream->size(); + _currentReadSize = 8; + _man = man; + _looping = looping; + _musicAttenuation = 1000; + + // preload one packet + if (_totalSize > 0) { + _file->skip(8); + readPacket(); + } else { + stopNow(); + } +} + +int AudioStreamInstance::readBuffer(int16 *buffer, const int numSamples) { + debugC(5, kDebugAudio, "readBuffer(buffer, %d)", numSamples); + + handleFade(numSamples); + int32 leftSamples = numSamples; + int32 destOffset = 0; + if ((_bufferOffset + leftSamples) * 2 >= _bufferSize) { + if (_bufferSize - _bufferOffset * 2 > 0) { + memcpy(buffer, &_buffer[_bufferOffset], _bufferSize - _bufferOffset * 2); + leftSamples -= (_bufferSize - _bufferOffset * 2) / 2; + destOffset += (_bufferSize - _bufferOffset * 2) / 2; + } + if (!readPacket()) + return 0; + + _bufferOffset = 0; + } + + if (leftSamples >= 0) { + memcpy(buffer + destOffset, &_buffer[_bufferOffset], MIN(leftSamples * 2, _bufferSize)); + _bufferOffset += leftSamples; + } + + return numSamples; +} + +bool AudioStreamInstance::readPacket() { + debugC(5, kDebugAudio, "readPacket()"); + + if (_file->eos() || (_currentReadSize >= _totalSize)) { + if (_looping) { + _file->seek(8); + _currentReadSize = 8; + _lastADPCMval1 = 0; + _lastADPCMval2 = 0; + } else { + _bufferSize = 0; + stopNow(); + return false; + } + } + int16 numCompressedBytes = _file->readSint16LE(); + int16 numDecompressedBytes = _file->readSint16LE(); + _file->readSint32LE(); + + if (numCompressedBytes > _compBufferSize) { + if (_compBuffer) + delete[] _compBuffer; + _compBufferSize = numCompressedBytes; + _compBuffer = new uint8[_compBufferSize]; + } + + if (numDecompressedBytes > _bufferMaxSize) { + if (_buffer) + delete [] _buffer; + _bufferMaxSize = numDecompressedBytes; + _buffer = new int16[numDecompressedBytes]; + } + + _bufferSize = numDecompressedBytes; + _file->read(_compBuffer, numCompressedBytes); + _currentReadSize += 8 + numCompressedBytes; + + decodeADPCM(_compBuffer, _buffer, numCompressedBytes); + return true; +} + +void AudioStreamInstance::decodeADPCM(uint8 *comp, int16 *dest, int32 packetSize) { + debugC(5, kDebugAudio, "decodeADPCM(comp, dest, %d)", packetSize); + + int32 numSamples = 2 * packetSize; + int32 v18 = _lastADPCMval1; + int32 v19 = _lastADPCMval2; + for (int32 i = 0; i < numSamples; i++) { + uint8 comm = *comp; + + int32 v29 = i & 1; + int32 v30; + if (v29 == 0) + v30 = comm & 0xf; + else + v30 = (comm & 0xf0) >> 4; + + int32 v31 = v30 & 0x8; + int32 v32 = v30 & 0x7; + int32 v33 = ADPCM_table[v19]; + int32 v34 = v33 >> 3; + if (v32 & 4) + v34 += v33; + + if (v32 & 2) + v34 += v33 >> 1; + + if (v32 & 1) + v34 += v33 >> 2; + + v19 += ADPCM_index[v32]; + if (v19 < 0) + v19 = 0; + + if (v19 > 88) + v19 = 88; + + if (v31) + v18 -= v34; + else + v18 += v34; + + if (v18 > 32767) + v18 = 32767; + else if (v18 < -32768) + v18 = -32768; + + *dest = v18; + comp += v29; + dest++; + } + + _lastADPCMval1 = v18; + _lastADPCMval2 = v19; +} + +void AudioStreamInstance::play(bool fade, Audio::Mixer::SoundType soundType) { + debugC(1, kDebugAudio, "play(%d)", (fade) ? 1 : 0); + + Audio::SoundHandle soundHandle; + _stopped = false; + _fadingIn = fade; + _fadeTime = 0; + _soundType = soundType; + _musicAttenuation = 1000; // max volume + _mixer->playStream(soundType, &_handle, this, -1); + handleFade(0); +} + +void AudioStreamInstance::handleFade(int32 numSamples) { + debugC(5, kDebugAudio, "handleFade(%d)", numSamples); + + // Fading enabled only for music + if (_soundType != Audio::Mixer::kMusicSoundType) + return; + + int32 finalVolume = _volume; + + if (_fadingOut) { + _fadeTime += numSamples; + + if (_fadeTime > 40960) { + _fadeTime = 40960; + stopNow(); + _fadingOut = false; + } + finalVolume = _volume - _fadeTime * _volume / 40960; + } else { + if (_fadingIn) { + _fadeTime += numSamples; + if (_fadeTime > 40960) { + _fadeTime = 40960; + _fadingIn = false; + } + + finalVolume = _volume * _fadeTime / 40960; + } + } + + // the music is too loud when someone is talking + // smoothing to avoid big volume changes + if (_man->voiceStillPlaying()) { + _musicAttenuation -= numSamples >> 4; + if (_musicAttenuation < 250) + _musicAttenuation = 250; + } else { + _musicAttenuation += numSamples >> 5; + if (_musicAttenuation > 1000) + _musicAttenuation = 1000; + } + + + _mixer->setChannelVolume(_handle, finalVolume * _musicAttenuation / 1000); + +} + +void AudioStreamInstance::stop(bool fade /*= false*/) { + debugC(1, kDebugAudio, "stop(%d)", (fade) ? 1 : 0); + + if (fade) { + _fadingIn = false; + _fadingOut = true; + _fadeTime = 0; + } else { + stopNow(); + } +} + +void AudioStreamInstance::stopNow() { + debugC(1, kDebugAudio, "stopNow()"); + + _stopped = true; +} + +void AudioStreamInstance::setVolume(int32 volume) { + debugC(1, kDebugAudio, "setVolume(%d)", volume); + + _volume = volume; + _mixer->setChannelVolume(_handle, volume); +} + +AudioStreamPackage::AudioStreamPackage(ToonEngine *vm) : _vm(vm) { + _indexBuffer = 0; +} + +AudioStreamPackage::~AudioStreamPackage() { + delete[] _indexBuffer; + if (_file) { + delete _file; + _file = 0; + } +} + +bool AudioStreamPackage::loadAudioPackage(Common::String indexFile, Common::String streamFile) { + debugC(4, kDebugAudio, "loadAudioPackage(%s, %s)", indexFile.c_str(), streamFile.c_str()); + + uint32 size = 0; + uint8 *fileData = _vm->resources()->getFileData(indexFile, &size); + if (!fileData) + return false; + + delete[] _indexBuffer; + + _indexBuffer = new uint32[size / 4]; + memcpy(_indexBuffer, fileData, size); + + _file = _vm->resources()->openFile(streamFile); + if (!_file) + return false; + + return true; +} + +void AudioStreamPackage::getInfo(int32 id, int32 *offset, int32 *size) { + debugC(1, kDebugAudio, "getInfo(%d, offset, size)", id); + + *offset = READ_LE_UINT32(_indexBuffer + id); + *size = READ_LE_UINT32(_indexBuffer + id + 1) - READ_LE_UINT32(_indexBuffer + id); +} + +Common::SeekableReadStream *AudioStreamPackage::getStream(int32 id, bool ownMemory) { + debugC(1, kDebugAudio, "getStream(%d, %d)", id, (ownMemory) ? 1 : 0); + + int32 offset = 0; + int32 size = 0; + getInfo(id, &offset, &size); + if (ownMemory) { + byte *memory = new byte[size]; + _file->seek(offset); + _file->read(memory, size); + return new Common::MemoryReadStream(memory, size, DisposeAfterUse::YES); + } else { + return new Common::SeekableSubReadStream(_file, offset, size + offset); + } +} + +void AudioManager::startAmbientSFX(int32 id, int32 delay, int32 mode, int32 volume) +{ + int32 found = -1; + for (int32 i = 0; i < 4; i++) { + if (!_ambientSFXs[i]._enabled) { + found = i; + break; + } + } + + if (found < 0) + return; + + _ambientSFXs[found]._lastTimer = _vm->getOldMilli() - 1; + _ambientSFXs[found]._delay = delay; + _ambientSFXs[found]._enabled = true; + _ambientSFXs[found]._mode = mode; + _ambientSFXs[found]._volume = volume; + _ambientSFXs[found]._id = id; + updateAmbientSFX(); + +} + +void AudioManager::setAmbientSFXVolume(int32 id, int volume) { + for (int32 i = 0; i < 4; i++) { + AudioAmbientSFX* ambient = &_ambientSFXs[i]; + if (ambient->_id == id && ambient->_enabled) { + ambient->_volume = volume; + if (ambient->_channel >= 0 && _channels[ambient->_channel] && _channels[ambient->_channel]->isPlaying()) { + _channels[ambient->_channel]->setVolume(volume); + } + break; + } + } +} + +void AudioManager::killAmbientSFX(int32 id) +{ + for (int32 i = 0; i < 4; i++) { + AudioAmbientSFX* ambient = &_ambientSFXs[i]; + if (ambient->_id == id && ambient->_enabled) { + ambient->_enabled = false; + ambient->_id = -1; + + if (_channels[ambient->_channel]) { + _channels[ambient->_channel]->stop(false); + } + } + + } +} + +void AudioManager::killAllAmbientSFX() +{ + for (int32 i = 0; i < 4; i++) { + AudioAmbientSFX* ambient = &_ambientSFXs[i]; + if (ambient->_enabled) { + ambient->_enabled = false; + ambient->_id = -1; + + if (_channels[ambient->_channel] && _channels[ambient->_channel]->isPlaying()) { + _channels[ambient->_channel]->stop(false); + } + } + } +} + +void AudioManager::updateAmbientSFX() +{ + if (_vm->getMoviePlayer()->isPlaying()) return; + + for (int32 i = 0; i < 4; i++) { + AudioAmbientSFX* ambient = &_ambientSFXs[i]; + if (ambient->_enabled && (ambient->_channel < 0 || !(_channels[ambient->_channel] && _channels[ambient->_channel]->isPlaying()))) { + if(ambient->_mode == 1) { + if (_vm->randRange(0, 32767) < ambient->_delay) { + ambient->_channel = playSFX(ambient->_id, ambient->_volume, false); + } + } else { + if (_vm->getOldMilli() > ambient->_lastTimer) { + ambient->_channel = playSFX(ambient->_id, ambient->_volume, false); + ambient->_lastTimer = _vm->getOldMilli(); // + 60 * _vm->getTickLength() * ambient->_delay; + } + } + } + } +} + + +} // End of namespace Toon + diff --git a/engines/toon/audio.h b/engines/toon/audio.h new file mode 100644 index 0000000000..e0676c2992 --- /dev/null +++ b/engines/toon/audio.h @@ -0,0 +1,177 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ + +#ifndef TOON_AUDIO_H +#define TOON_AUDIO_H + +#include "sound/audiostream.h" +#include "sound/mixer.h" +#include "toon/toon.h" + +namespace Toon { + +// used for music/voice/everything +class AudioManager; +class AudioStreamInstance : public Audio::AudioStream { + +public: + AudioStreamInstance(AudioManager *man, Audio::Mixer *mixer, Common::SeekableReadStream *stream, bool looping = false); + ~AudioStreamInstance(); + void play(bool fade = false, Audio::Mixer::SoundType soundType = Audio::Mixer::kMusicSoundType); + void stop(bool fade = false); + + bool isPlaying() { + return !_stopped; + } + bool isLooping() { + return _looping; + } + bool isFading() { + return _fadingIn || _fadingOut; + } + + void setVolume(int32 volume); +protected: + int readBuffer(int16 *buffer, const int numSamples); + bool isStereo() const { + return false; + } + int getRate() const { + return 22100; + } + bool endOfData() const { + return _stopped; + } + void handleFade(int32 numSamples); + void stopNow(); + + bool readPacket(); + void decodeADPCM(uint8 *comp, int16 *dest, int32 packetSize); + + Common::SeekableReadStream *_file; + bool _fadingIn; + bool _fadingOut; + int32 _fadeTime; + uint8 *_compBuffer; + int16 *_buffer; + int32 _bufferSize; + int32 _bufferMaxSize; + int32 _bufferOffset; + int32 _compBufferSize; + Audio::SoundHandle _handle; + Audio::Mixer::SoundType _soundType; + Audio::Mixer *_mixer; + int32 _lastADPCMval1; + int32 _lastADPCMval2; + bool _stopped; + AudioManager *_man; + int32 _totalSize; + int32 _currentReadSize; + bool _looping; + int32 _volume; + int32 _musicAttenuation; +}; + +class AudioStreamPackage { + +public: + AudioStreamPackage(ToonEngine *vm); + ~AudioStreamPackage(); + + bool loadAudioPackage(Common::String indexFile, Common::String streamFile); + void getInfo(int32 id, int32 *offset, int32 *size); + Common::SeekableReadStream *getStream(int32 id, bool ownMemory = false); +protected: + Common::SeekableReadStream *_file; + uint32 *_indexBuffer; + ToonEngine *_vm; +}; + +struct AudioAmbientSFX { + int32 _id; + int32 _volume; + int32 _lastTimer; + int32 _delay; + int32 _mode; + int32 _channel; + bool _enabled; +}; + +class AudioManager { +public: + void removeInstance(AudioStreamInstance *inst); // called by destructor + + AudioManager(ToonEngine *vm, Audio::Mixer *mixer); + ~AudioManager(void); + + bool voiceStillPlaying(); + + void playMusic(Common::String dir, Common::String music); + void playVoice(int32 id, bool genericVoice); + int32 playSFX(int32 id, int volume, bool genericSFX); + void stopCurrentVoice(); + void stopAllSfxs(); + void setMusicVolume(int32 volume); + void stopMusic(); + void muteVoice(bool mute); + void muteMusic(bool mute); + void muteSfx(bool mute); + bool isVoiceMuted() { return _voiceMuted; } + bool isMusicMuted() { return _musicMuted; } + bool isSfxMuted() { return _sfxMuted; } + + void startAmbientSFX(int32 id, int32 delay, int32 mode, int32 volume); + void killAmbientSFX(int32 id); + void killAllAmbientSFX(); + void updateAmbientSFX(); + void setAmbientSFXVolume(int32 id, int volume); + + void closeAudioPack(int32 id); + bool loadAudioPack(int32 id, Common::String indexFile, Common::String packFile); + + AudioStreamInstance *_channels[16]; // 0-1 : music + // 2 : voice + // 3-16 : SFX + + AudioStreamPackage *_audioPacks[4]; // 0 : generic streams + // 1 : local streams + // 2 : generic SFX + // 3 : local SFX + uint32 _currentMusicChannel; + Common::String _currentMusicName; + ToonEngine *_vm; + Audio::Mixer *_mixer; + +protected: + bool _voiceMuted; + bool _musicMuted; + bool _sfxMuted; + + AudioAmbientSFX _ambientSFXs[4]; +}; + +} // End of namespace Toon + +#endif diff --git a/engines/toon/character.cpp b/engines/toon/character.cpp new file mode 100644 index 0000000000..f6e244a064 --- /dev/null +++ b/engines/toon/character.cpp @@ -0,0 +1,1043 @@ +/* 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. +* +* $URL$ +* $Id$ +* +*/ + +#include "toon/character.h" +#include "toon/drew.h" +#include "toon/flux.h" +#include "toon/path.h" + +namespace Toon { + +Character::Character(ToonEngine *vm) : _vm(vm) { + _animationInstance = 0; + _shadowAnimationInstance = 0; + _shadowAnim = 0; + _x = 0; + _y = 0; + _z = 0; + _finalX = 0; + _finalY = 0; + _specialAnim = 0; + _sceneAnimationId = -1; + _idleAnim = 0; + _walkAnim = 0; + _talkAnim = 0; + _facing = 0; + _flags = 0; + _animFlags = 0; + _isTalking = false; + _id = 0; + _scale = 1024; + _blockingWalk = false; + _animScriptId = -1; + _animSpecialId = -1; + _animSpecialDefaultId = 0; + _currentPathNodeCount = 0; + _currentPathNode = 0; + _visible = true; + _speed = 150; // 150 = nominal drew speed + _lastWalkTime = 0; + _numPixelToWalk = 0; + _nextIdleTime = _vm->getSystem()->getMillis() + (_vm->randRange(0, 600) + 300) * _vm->getTickLength(); +} + +Character::~Character(void) { +} + +void Character::init() { + +} + +void Character::setFacing(int32 facing) { + debugC(4, kDebugCharacter, "setFacing(%d)", facing); + _facing = facing; +} + +void Character::setPosition(int32 x, int32 y) { + debugC(5, kDebugCharacter, "setPosition(%d, %d)", x, y); + + _x = x; + _y = y; + if (_animationInstance) + _animationInstance->setPosition(_x, _y, _z); + return; +} + +bool Character::walkTo(int32 newPosX, int32 newPosY) { + debugC(1, kDebugCharacter, "walkTo(%d, %d)", newPosX, newPosY); + + if (!_visible) + return true; + + if (_x == newPosX && _y == newPosY) + return true; + + _vm->getPathFinding()->resetBlockingRects(); + + if (_id == 1) { + int32 sizeX = MAX<int32>(5, 40 * _vm->getDrew()->getScale() / 1024); + int32 sizeY = MAX<int32>(2, 20 * _vm->getDrew()->getScale() / 1024); + _vm->getPathFinding()->addBlockingEllipse(_vm->getDrew()->getFinalX(), _vm->getDrew()->getFinalY(), sizeX, sizeY); + } + + _vm->getPathFinding()->findClosestWalkingPoint(newPosX, newPosY, &_finalX, &_finalY, _x, _y); + if (_x == _finalX && _y == _finalY) + return true; + + + if (_vm->getPathFinding()->findPath(_x, _y, _finalX, _finalY)) { + + int32 localFinalX = _finalX; + int32 localFinalY = _finalY; + + for (int32 a = 0; a < _vm->getPathFinding()->getPathNodeCount(); a++) { + _currentPathX[a] = _vm->getPathFinding()->getPathNodeX(a); + _currentPathY[a] = _vm->getPathFinding()->getPathNodeY(a); + } + _currentPathNodeCount = _vm->getPathFinding()->getPathNodeCount(); + _currentPathNode = 0; + stopSpecialAnim(); + _flags |= 0x1; + _lastWalkTime = _vm->getSystem()->getMillis(); + + _numPixelToWalk = 0; + + if (_blockingWalk) { + while ((_x != newPosX || _y != newPosY) && _currentPathNode < _currentPathNodeCount && !_vm->shouldQuitGame()) { + if (_currentPathNode < _currentPathNodeCount - 10) { + int32 delta = MIN<int32>(10, _currentPathNodeCount - _currentPathNode); + int32 dx = _currentPathX[_currentPathNode+delta] - _x; + int32 dy = _currentPathY[_currentPathNode+delta] - _y; + setFacing(getFacingFromDirection(dx, dy)); + playWalkAnim(0, 0); + } + + // in 1/1000 pixels + _numPixelToWalk += _speed * (_vm->getSystem()->getMillis() - _lastWalkTime) * _scale / 1024; + _lastWalkTime = _vm->getSystem()->getMillis(); + + while (_numPixelToWalk >= 1000 && _currentPathNode < _currentPathNodeCount) { + _x = _currentPathX[_currentPathNode]; + _y = _currentPathY[_currentPathNode]; + _currentPathNode += 1; + _numPixelToWalk -= 1000; + } + setPosition(_x, _y); + + _vm->doFrame(); + } + playStandingAnim(); + _flags &= ~0x1; + _currentPathNode = 0; + _currentPathNodeCount = 0; + + if (_x != localFinalX || _y != localFinalY) { + return false; + } + } + } + + //_vm->getPathFinding()->findClosestWalkingPoint(newPosX, newPosY, &_x, &_y, _x, _y); + //setPosition(_x,_y); + return true; +} + +void Character::setFlag(int flag) { + _flags = flag; +} + +int32 Character::getFlag() { + return _flags; +} + +int32 Character::getX() { + return _x; +} +int32 Character::getY() { + return _y; +} + +bool Character::getVisible() { + return _visible; +} + +void Character::setVisible(bool visible) { + debugC(1, kDebugCharacter, "setVisible(%d)", (visible) ? 1 : 0); + + _visible = visible; + if (_animationInstance) + _animationInstance->setVisible(visible); + + if (_shadowAnimationInstance) + _shadowAnimationInstance->setVisible(visible); + + return; +} + +int32 Character::getFacing() { + return _facing; +} + +bool Character::loadWalkAnimation(Common::String animName) { + debugC(1, kDebugCharacter, "loadWalkAnimation(%s)", animName.c_str()); + if (_walkAnim) + delete _walkAnim; + + _walkAnim = new Animation(_vm); + return _walkAnim->loadAnimation(animName); +} + +bool Character::loadIdleAnimation(Common::String animName) { + debugC(1, kDebugCharacter, "loadIdleAnimation(%s)", animName.c_str()); + if (_idleAnim) + delete _idleAnim; + + _idleAnim = new Animation(_vm); + return _idleAnim->loadAnimation(animName); +} + +bool Character::loadTalkAnimation(Common::String animName) { + debugC(1, kDebugCharacter, "loadTalkAnimation(%s)", animName.c_str()); + if (_talkAnim) + delete _talkAnim; + + _talkAnim = new Animation(_vm); + return _talkAnim->loadAnimation(animName); +} + +bool Character::setupPalette() { + return false; // only for Drew +} + +void Character::playStandingAnim() { + +} + +void Character::updateTimers(int32 relativeAdd) { + _nextIdleTime += relativeAdd; + _lastWalkTime += relativeAdd; +} + +void Character::stopSpecialAnim() { + debugC(4, kDebugCharacter, "stopSpecialAnim()"); +// Strangerke - Commented (not used) +#if 0 + if (_animSpecialId != _animSpecialDefaultId) + delete anim +#endif + if (_animScriptId != -1) + _vm->getSceneAnimationScript(_animScriptId)->_frozenForConversation = false; + + //if (_sceneAnimationId != -1) + // _animationInstance->setAnimation(_vm->getSceneAnimation(_sceneAnimationId)->_animation); + + bool needStandingAnim = (_animFlags & 0x40) != 0; + + _animSpecialId = -1; + _time = 0; + _animFlags = 0; + _flags &= ~1; + _flags &= ~4; + + if (needStandingAnim) { + playStandingAnim(); + } +} + +void Character::update(int32 timeIncrement) { + debugC(5, kDebugCharacter, "update(%d)", timeIncrement); + if ((_flags & 0x1) && _currentPathNodeCount > 0) { + if (_currentPathNode < _currentPathNodeCount) { + if (_currentPathNode < _currentPathNodeCount - 10) { + int32 delta = MIN<int32>(10, _currentPathNodeCount - _currentPathNode); + int32 dx = _currentPathX[_currentPathNode+delta] - _x; + int32 dy = _currentPathY[_currentPathNode+delta] - _y; + setFacing(getFacingFromDirection(dx, dy)); + playWalkAnim(0, 0); + } + + // in 1/1000 pixels + _numPixelToWalk += _speed * (_vm->getSystem()->getMillis() - _lastWalkTime) * _scale / 1024; + _lastWalkTime = _vm->getSystem()->getMillis(); + + while (_numPixelToWalk > 1000 && _currentPathNode < _currentPathNodeCount) { + _x = _currentPathX[_currentPathNode]; + _y = _currentPathY[_currentPathNode]; + _currentPathNode += 1; + _numPixelToWalk -= 1000; + } + setPosition(_x, _y); + } else { + playStandingAnim(); + _flags &= ~0x1; + _currentPathNodeCount = 0; + } + } + + updateIdle(); + +#if 0 + // handle special anims + if ((_flags & 4) == 0) + return; + + + if (_animScriptId != -1) { + _animationInstance = _vm->getSceneAnimation(this->) +#endif + + int32 animId = _animSpecialId; + if (animId >= 1000) + animId = 0; + + if (_animSpecialId < 0) + return; + + int32 currentFrame = _animationInstance->getFrame(); + + const SpecialCharacterAnimation *anim = getSpecialAnimation(_id, animId); + + if ((_animFlags & 0x10) == 0) { + if (_animScriptId != -1 && currentFrame > 0 && !_vm->getSceneAnimationScript(_animScriptId)->_frozen) { + if (_vm->getCurrentLineToSay() != _lineToSayId && (_animFlags & 8)) + stopSpecialAnim(); + return; + } + + if (_id == 1 && (_animFlags & 4)) { + if (_animFlags & 0x10) + return; + } else { + if (_id == 1 && (_animFlags & 0x10) && _vm->getCurrentLineToSay() != -1) { + return; + } + if ((_animFlags & 0x40) == 0 && _vm->getCurrentLineToSay() == -1) { + stopSpecialAnim(); + return; + } + +// Strangerke - Commented (not used) +#if 0 + if (_animFlags & 8) { + if (anim->_flags7 == 0xff && anim->_flags9 == 0xff) { + // start voice + } + } +#endif + + if (_animScriptId != -1) + _vm->getSceneAnimationScript(_animScriptId)->_frozenForConversation = true; + + + // TODO setup backup // + + _animFlags |= 0x10; + _animationInstance->setAnimation(_specialAnim); + _animationInstance->setFrame(0); + _time = _vm->getOldMilli() + 8 * _vm->getTickLength(); + } + + } + + if ((_animFlags & 3) == 2) { + if ((((_animFlags & 8) == 8) && _vm->getCurrentLineToSay() != _lineToSayId) || !_vm->getAudioManager()->voiceStillPlaying()) // || (_flags & 8)) && _vm->getAudioManager()->voiceStillPlaying()) + _animFlags |= 1; + + } + + if (_time > _vm->getOldMilli()) + return; + + int32 animFlag = anim->_unused; + int32 nextFrame = currentFrame + 1; + int32 nextTime = _time; + int32 animDir = 1; + if (!animFlag) { + if (_animFlags & 1) { + if (anim->_flags7 == 0xff) { + if (currentFrame > anim->_flag1 / 2) + animDir = 1; + else + animDir = -1; + } else { + if (currentFrame >= anim->_flags6) { + if (currentFrame < anim->_flags7) + currentFrame = anim->_flags7; + } + if (currentFrame > anim->_flags6) + animDir = 1; + else + animDir = -1; + } + + nextFrame = currentFrame + animDir; + nextTime = _vm->getOldMilli() + 6 * _vm->getTickLength(); + } else { + if (_animFlags & 0x20) { + nextFrame = currentFrame - 1; + if (nextFrame == anim->_flags6 - 1) { + if (anim->_flags8 != 1 && (_vm->randRange(0, 1) == 1 || anim->_flags8 == 2)) { + _animFlags &= ~0x20; + nextFrame += 2; + if (nextFrame > anim->_flags7) + nextFrame = anim->_flags7; + } else { + nextFrame = anim->_flags7; + } + } + } else { + nextFrame = currentFrame + 1; +// Strangerke - Commented (not used) +#if 0 + if (!_vm->getAudioManager()->voiceStillPlaying()) { + if (_animFlags & 8) { + if ((anim->_flags9 == 0xff && nextFrame == anim->_flags6) || + (anim->_flags9 != 0xff && nextFrame >= anim->_flags9)) { + // start really talking + } + } + } +#endif + if (nextFrame == anim->_flags7 + 1 && (_animFlags & 0x40) == 0) { + if (anim->_flags8 != 1 && (_vm->randRange(0, 1) || anim->_flags8 == 2)) { + _animFlags |= 0x20; + nextFrame -= 2; + if (nextFrame < anim->_flags6) + nextFrame = anim->_flags6; + } else { + nextFrame = anim->_flags6; + } + } + } + + nextTime = _vm->getOldMilli() + 8 * _vm->getTickLength(); + } + // goto label78 + } + // skipped all this part. + + //label78 + + +#if 0 + if (_id == 0) + printf(" drew animation name %s / flag %d / frame %d \n", _specialAnim->_name, _animFlags, nextFrame); + if (_id == 1) + debugC(0, 0xfff, " flux animation flag %d / frame %d", _animFlags, nextFrame); + if (_id == 7) + debugC(0, 0xfff, " footman animation flag %d / frame %d", _animFlags, nextFrame); +#endif + + _time = nextTime; + if (nextFrame < 0 || nextFrame >= anim->_flag1) { + if ((_animFlags & 2) == 0 || _vm->getCurrentLineToSay() != _lineToSayId) { + stopSpecialAnim(); + return; + } + + // lots skipped here + + _animFlags &= ~0x10; + _animationInstance->forceFrame(0); + return; + + } + + //if ((_flags & 8) == 0 || !_vm->getAudioManager()->voiceStillPlaying( ) || ) + _animationInstance->forceFrame(nextFrame); +} + +// adapted from Kyra +int32 Character::getFacingFromDirection(int32 dx, int32 dy) { + debugC(4, kDebugCharacter, "getFacingFromDirection(%d, %d)", dx, dy); + + static const int facingTable[] = { + //1, 0, 1, 2, 3, 4, 3, 2, 7, 0, 7, 6, 5, 4, 5, 6 + 5, 6, 5, 4, 3, 2, 3, 4, 7, 6, 7, 0, 1, 2, 1, 0 + }; + dx = -dx; + + int32 facingEntry = 0; + int32 ydiff = dy; + if (ydiff < 0) { + ++facingEntry; + ydiff = -ydiff; + } + facingEntry <<= 1; + + int32 xdiff = dx; + if (xdiff < 0) { + ++facingEntry; + xdiff = -xdiff; + } + + facingEntry <<= 1; + + if (xdiff >= ydiff) { + int32 temp = ydiff; + ydiff = xdiff; + xdiff = temp; + } else { + facingEntry += 1; + } + + facingEntry <<= 1; + + int32 temp = (ydiff + 1) >> 1; + + if (xdiff < temp) + facingEntry += 1; + + return facingTable[facingEntry]; +} + +AnimationInstance *Character::getAnimationInstance() { + return _animationInstance; +} + +void Character::setAnimationInstance(AnimationInstance *instance) { + _animationInstance = instance; +} + +int32 Character::getScale() { + return _scale; +} + +void Character::playWalkAnim(int32 startFrame, int32 endFrame) { + +} + +void Character::setId(int32 id) { + _id = id; +} + +int32 Character::getId() { + return _id; +} + +void Character::save(Common::WriteStream *stream) { + debugC(1, kDebugCharacter, "save(stream)"); + + stream->writeSint32LE(_flags); + stream->writeSint32LE(_x); + stream->writeSint32LE(_y); + stream->writeSint32LE(_z); + stream->writeSint32LE(_finalX); + stream->writeSint32LE(_finalY); + stream->writeSint32LE(_scale); + stream->writeSint32LE(_id); + + stream->writeSint32LE(_animScriptId); + stream->writeSint32LE(_animFlags); + stream->writeSint32LE(_animSpecialDefaultId); + stream->writeSint32LE(_sceneAnimationId); +} + +void Character::load(Common::ReadStream *stream) { + debugC(1, kDebugCharacter, "read(stream)"); + + _flags = stream->readSint32LE(); + _flags &= ~1; // characters are not walking when restoring. + + _x = stream->readSint32LE(); + _y = stream->readSint32LE(); + _z = stream->readSint32LE(); + _finalX = stream->readSint32LE(); + _finalY = stream->readSint32LE(); + _scale = stream->readSint32LE(); + _id = stream->readSint32LE(); + + _animScriptId = stream->readSint32LE(); + _animFlags = stream->readSint32LE(); + _animSpecialDefaultId = stream->readSint32LE(); + _sceneAnimationId = stream->readSint32LE(); + + if (_sceneAnimationId > -1) { + setAnimationInstance(_vm->getSceneAnimation(_sceneAnimationId)->_animInstance); + } +} + +void Character::setAnimScript(int32 animScriptId) { + _animScriptId = animScriptId; +} + +void Character::setSceneAnimationId(int32 sceneAnimationId) { + _sceneAnimationId = sceneAnimationId; +} + +int32 Character::getAnimScript() { + return _animScriptId; +} + +void Character::playTalkAnim() { + +} + +void Character::stopWalk() { + debugC(1, kDebugCharacter, "stopWalk()"); + + _finalX = _x; + _finalY = _y; + _flags &= ~0x1; + _currentPathNode = 0; + _currentPathNodeCount = 0; +} + +const SpecialCharacterAnimation *Character::getSpecialAnimation(int32 characterId, int32 animationId) { + debugC(6, kDebugCharacter, "getSpecialAnimation(%d, %d)", characterId, animationId); + + // very nice animation list hardcoded in the executable... + static const SpecialCharacterAnimation anims[] = { + { "TLK547_?", 9, 0, 0, 0, 0, 0, 1, 5, 8, 1, 8, 0, 255 }, + { "TLK555_?", 16, 0, 0, 0, 0, 6, 8, 10, 255, 6, 11, 2, 255 }, + { "LST657_?", 14, 0, 0, 0, 0, 255, 255, 255, 255, 5, 11, 0, 255 }, + { "TLK587_?", 18, 0, 0, 0, 0, 5, 7, 9, 11, 4, 13, 1, 255 }, + { "LST659_?", 14, 0, 0, 0, 0, 255, 255, 255, 255, 6, 8, 0, 255 }, + { "TLK595_?", 11, 0, 0, 0, 0, 3, 6, 255, 255, 1, 7, 0, 255 }, + { "IDL165_?", 13, 0, 0, 0, 0, 255, 255, 255, 255, 6, 8, 0, 255 }, + { "LST699_?", 10, 0, 0, 0, 0, 255, 255, 255, 255, 1, 9, 1, 255 }, + { "LST713_?", 10, 0, 0, 0, 0, 255, 255, 255, 255, 4, 6, 0, 255 }, + { "IDL169_?", 16, 0, 0, 0, 0, 255, 255, 255, 255, 5, 9, 2, 255 }, + { "IDL173_?", 19, 0, 0, 0, 0, 255, 255, 255, 255, 4, 17, 1, 255 }, + { "IDL187_?", 14, 0, 0, 0, 0, 255, 255, 255, 255, 4, 8, 0, 255 }, + { "IDL185_?", 15, 0, 0, 0, 0, 255, 255, 255, 255, 6, 9, 1, 255 }, + { "TLK635_?", 16, 0, 0, 0, 0, 5, 8, 10, 12, 4, 12, 0, 255 }, + { "TLK637_?", 18, 0, 0, 0, 0, 5, 7, 9, 12, 4, 13, 0, 255 }, + { "TLK551_?", 20, 0, 0, 0, 0, 5, 9, 11, 15, 4, 16, 0, 255 }, + { "TLK553_?", 20, 0, 0, 0, 0, 7, 9, 11, 13, 6, 15, 0, 255 }, + { "TLK619_?", 18, 0, 0, 0, 0, 5, 8, 11, 13, 5, 15, 0, 255 }, + { "TLK601_?", 12, 0, 0, 0, 0, 2, 5, 6, 10, 2, 10, 1, 255 }, + { "TLK559_?", 18, 0, 0, 0, 0, 4, 6, 10, 12, 4, 13, 0, 255 }, + { "TLK557_?", 16, 0, 0, 0, 0, 6, 8, 10, 255, 6, 11, 0, 255 }, + { "TLK561_?", 17, 0, 0, 0, 0, 6, 8, 10, 12, 5, 12, 0, 255 }, + { "TLK623_?", 19, 0, 0, 0, 0, 6, 8, 10, 13, 6, 14, 0, 255 }, + { "TLK591_?", 20, 0, 0, 0, 0, 10, 14, 255, 255, 7, 15, 0, 255 }, + { "TLK567_?", 19, 0, 0, 0, 0, 6, 9, 11, 14, 5, 15, 0, 255 }, + { "TLK629_?", 18, 0, 0, 0, 0, 6, 8, 10, 11, 6, 12, 0, 255 }, + { "TLK627_?", 19, 0, 0, 0, 0, 7, 10, 12, 14, 4, 14, 0, 255 }, + { "TLK631_?", 19, 0, 0, 0, 0, 8, 10, 255, 255, 8, 12, 0, 255 }, + { "TLK565_?", 17, 0, 0, 0, 0, 4, 7, 9, 11, 3, 12, 0, 255 }, + { "TLK603_?", 16, 0, 0, 0, 0, 5, 255, 255, 255, 3, 9, 0, 255 }, + { "TLK573_?", 20, 0, 0, 0, 0, 6, 7, 10, 255, 6, 16, 2, 255 }, + { "TLK615_?", 17, 0, 0, 0, 0, 6, 8, 10, 12, 5, 12, 0, 255 }, + { "TLK609_?", 18, 0, 0, 0, 0, 6, 8, 10, 12, 5, 13, 0, 255 }, + { "TLK611_?", 18, 0, 0, 0, 0, 8, 10, 12, 255, 7, 13, 0, 255 }, + { "TLK607_?", 16, 0, 0, 0, 0, 4, 7, 9, 11, 4, 12, 0, 255 }, + { "TLK581_?", 15, 0, 0, 0, 0, 7, 9, 11, 255, 6, 11, 0, 255 }, + { "SHD107_?", 46, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 }, + { "IHL106_?", 23, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 7 }, + { "GLV106_?", 23, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 7 }, + { "FXTKA_?", 11, 0, 0, 0, 0, 7, 255, 255, 255, 2, 9, 0, 255 }, + { "FXTKF_?", 12, 0, 0, 0, 0, 6, 8, 255, 255, 5, 9, 0, 255 }, + { "FXTKG_?", 9, 0, 0, 0, 0, 5, 255, 255, 255, 4, 7, 0, 255 }, + { "FXTKI_?", 12, 0, 0, 0, 0, 6, 255, 255, 255, 5, 9, 0, 255 }, + { "FXTKL_?", 14, 0, 0, 0, 0, 4, 6, 255, 255, 3, 10, 0, 255 }, + { "FXTKO_?", 10, 0, 0, 0, 0, 4, 255, 255, 255, 4, 7, 0, 255 }, + { "FXTKP_?", 9, 0, 0, 0, 0, 4, 6, 255, 255, 3, 7, 0, 255 }, + { "FXTKQ_?", 10, 0, 0, 0, 0, 4, 6, 255, 255, 3, 7, 0, 255 }, + { "FXLSA_?", 11, 0, 0, 0, 0, 255, 255, 255, 255, 4, 6, 0, 255 }, + { "FXLSB_?", 9, 0, 0, 0, 0, 255, 255, 255, 255, 4, 5, 0, 255 }, + { "FXLSK_?", 8, 0, 0, 0, 0, 255, 255, 255, 255, 5, 6, 0, 255 }, + { "FXLSM_?", 7, 0, 0, 0, 0, 255, 255, 255, 255, 4, 4, 0, 255 }, + { "FXLSP_?", 7, 0, 0, 0, 0, 255, 255, 255, 255, 3, 3, 0, 255 }, + { "FXLSQ_?", 6, 0, 0, 0, 0, 255, 255, 255, 255, 3, 3, 0, 255 }, + { "FXIDE_?", 10, 0, 0, 0, 0, 255, 255, 255, 255, 5, 7, 0, 255 }, + { "FXIDI_?", 7, 0, 0, 0, 0, 255, 255, 255, 255, 1, 6, 1, 255 }, + { "FXRCT1_?", 12, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 }, + { "FXTKB_?", 11, 0, 0, 0, 0, 7, 255, 255, 255, 5, 9, 0, 255 }, + { "FXTKC_?", 14, 0, 0, 0, 0, 2, 5, 8, 10, 1, 12, 2, 255 }, + { "FXTKD_?", 14, 0, 0, 0, 0, 5, 7, 9, 255, 4, 11, 0, 255 }, + { "FXTKE_?", 14, 0, 0, 0, 0, 2, 255, 255, 255, 1, 12, 1, 255 }, + { "FXTKH_?", 11, 0, 0, 0, 0, 6, 8, 255, 255, 4, 9, 0, 255 }, + { "FXTKJ_?", 8, 0, 0, 0, 0, 7, 255, 255, 255, 4, 7, 0, 255 }, + { "FXTKK_?", 13, 0, 0, 0, 0, 6, 8, 255, 255, 5, 9, 0, 255 }, + { "FXTKM_?", 11, 0, 0, 0, 0, 6, 255, 255, 255, 4, 7, 0, 255 }, + { "FXTKN_?", 9, 0, 0, 0, 0, 5, 7, 255, 255, 4, 7, 0, 255 }, + { "FXLSC_?", 9, 0, 0, 0, 0, 255, 255, 255, 255, 3, 6, 1, 255 }, + { "FXLSD_?", 7, 0, 0, 0, 0, 255, 255, 255, 255, 4, 5, 0, 255 }, + { "FXLSE_?", 9, 0, 0, 0, 0, 255, 255, 255, 255, 8, 8, 0, 255 }, + { "FXLSG_?", 11, 0, 0, 0, 0, 255, 255, 255, 255, 6, 8, 2, 255 }, + { "FXLSI_?", 8, 0, 0, 0, 0, 255, 255, 255, 255, 5, 6, 0, 255 }, + { "FXLSJ_?", 5, 0, 0, 0, 0, 255, 255, 255, 255, 3, 4, 0, 255 }, + { "FXLSO_?", 8, 0, 0, 0, 0, 255, 255, 255, 255, 4, 5, 0, 255 }, + { "FXIDA_?", 15, 0, 0, 0, 0, 255, 255, 255, 255, 1, 12, 1, 255 }, + { "FXIDB_?", 12, 0, 0, 0, 0, 255, 255, 255, 255, 4, 11, 1, 255 }, + { "FXIDC_?", 11, 0, 0, 0, 0, 255, 255, 255, 255, 7, 7, 0, 255 }, + { "FXIDD_?", 15, 0, 0, 0, 0, 255, 255, 255, 255, 6, 6, 0, 255 }, + { "FXIDG_?", 6, 0, 0, 0, 0, 255, 255, 255, 255, 3, 4, 0, 255 }, + { "FXVRA_?", 7, 0, 0, 0, 0, 255, 255, 255, 255, 2, 6, 2, 255 }, + { "FXIDF_?", 15, 0, 0, 0, 0, 255, 255, 255, 255, 9, 11, 0, 255 }, + { "FXEXA_?", 9, 0, 0, 0, 0, 255, 255, 255, 255, 5, 5, 0, 255 }, + { "FXEXA_?", 9, 0, 0, 0, 0, 255, 255, 255, 255, 5, 5, 0, 255 }, + { "FFNTK1", 8, 0, 0, 0, 0, 255, 255, 255, 255, 1, 7, 0, 255 }, + { "FFTLK1", 10, 0, 0, 0, 0, 255, 255, 255, 255, 1, 9, 0, 1 }, + { "FFBLS1", 9, 0, 0, 0, 0, 255, 255, 255, 255, 3, 8, 0, 2 }, + { "FFLOV2", 6, 0, 0, 0, 0, 255, 255, 255, 255, 3, 5, 0, 2 }, + { "FFWOE1", 11, 0, 0, 0, 0, 255, 255, 255, 255, 3, 9, 0, 2 }, + { "FFSNF1", 9, 0, 0, 0, 0, 255, 255, 255, 255, 4, 6, 0, 4 }, + { "FFLAF1", 9, 0, 0, 0, 0, 255, 255, 255, 255, 2, 8, 0, 1 }, + { "FFSKE1", 11, 0, 0, 0, 0, 255, 255, 255, 255, 3, 10, 0, 2 }, + { "RGTLK2", 10, 0, 0, 0, 0, 4, 6, 255, 255, 2, 6, 0, 1 }, + { "RGTLK1", 10, 0, 0, 0, 0, 4, 6, 255, 255, 2, 6, 0, 1 }, + { "BRTLK1", 26, 0, 0, 0, 0, 255, 255, 255, 255, 2, 23, 0, 255 }, + { "BREXT1", 14, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 }, + { "BRLRT1", 19, 0, 0, 0, 0, 255, 255, 255, 255, 1, 15, 0, 255 }, + { "BRBWV1", 12, 0, 0, 0, 0, 255, 255, 255, 255, 3, 8, 0, 255 }, + { "BRPAT1", 11, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 }, + { "BRBSP1", 7, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 }, + { "BRBEX1", 10, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 }, + { "BRBLK1", 10, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 }, + { "BRBET1", 17, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 }, + { "BRWEX1", 10, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 }, + { "BBTLK2", 26, 0, 0, 0, 0, 255, 255, 255, 255, 2, 23, 1, 255 }, + { "BBEXT2", 14, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 1, 255 }, + { "BRLST1", 9, 0, 0, 0, 0, 255, 255, 255, 255, 2, 7, 0, 255 }, + { "BRLSN1", 13, 0, 0, 0, 0, 255, 255, 255, 255, 1, 13, 2, 255 }, + { "BRBNO1", 13, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 }, + { "BRBND1", 8, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 }, + { "BBLSN2", 13, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 }, + { "CCTALK", 6, 0, 0, 0, 0, 2, 5, 255, 255, 1, 5, 0, 255 }, + { "CCBIT1", 13, 0, 0, 0, 0, 3, 5, 9, 11, 2, 11, 2, 255 }, + { "CCCMP1", 13, 0, 0, 0, 0, 6, 9, 255, 255, 5, 10, 1, 2 }, + { "CCCOY1", 14, 0, 0, 0, 0, 6, 8, 255, 255, 4, 8, 0, 3 }, + { "CCFNG1", 5, 0, 0, 0, 0, 255, 255, 255, 255, 4, 4, 0, 255 }, + { "CCGRB1", 13, 0, 0, 0, 0, 6, 255, 255, 255, 6, 9, 0, 3 }, + { "CCGST1", 9, 0, 0, 0, 0, 4, 255, 255, 255, 4, 7, 0, 2 }, + { "CCHCN1", 10, 0, 0, 0, 0, 6, 9, 255, 255, 4, 9, 0, 0 }, + { "CCHND1", 7, 0, 0, 0, 0, 6, 255, 255, 255, 2, 6, 0, 1 }, + { "FTTLK2", 11, 0, 0, 0, 0, 1, 4, 6, 9, 1, 10, 0, 2 }, + { "FTGNO2", 11, 0, 0, 0, 0, 4, 6, 8, 255, 4, 8, 1, 2 }, + { "FTGST2", 6, 0, 0, 0, 0, 1, 2, 4, 5, 2, 5, 0, 1 }, + { "FTHND2", 7, 0, 0, 0, 0, 2, 5, 255, 255, 1, 6, 1, 255 }, + { "FTRNT2", 11, 0, 0, 0, 0, 3, 5, 7, 9, 2, 9, 1, 1 }, + { "FTSRG2", 10, 0, 0, 0, 0, 4, 6, 8, 255, 3, 8, 1, 1 }, + { "FTQOT2", 8, 0, 0, 0, 0, 1, 4, 8, 255, 1, 6, 1, 255 }, + { "FMSTK1", 9, 0, 0, 0, 0, 255, 255, 255, 255, 1, 7, 0, 255 }, + { "FMCRH1", 13, 0, 0, 0, 0, 255, 255, 255, 255, 3, 10, 0, 255 }, + { "FMFGR1", 12, 0, 0, 0, 0, 255, 255, 255, 255, 1, 10, 0, 255 }, + { "FMPRS1", 17, 0, 0, 0, 0, 255, 255, 255, 255, 1, 14, 0, 255 }, + { "FMAGR1", 12, 0, 0, 0, 0, 255, 255, 255, 255, 2, 9, 0, 255 }, + { "FMWOE1", 11, 0, 0, 0, 0, 255, 255, 255, 255, 1, 9, 0, 255 }, + { "FMTOE1", 17, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 }, + { "FM1TK1", 12, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 }, + { "FM2TK1", 6, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 }, + { "FM3TK1", 8, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 }, + { "FMTNB1", 4, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 }, + { "FMLOK1", 6, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 }, + { "FMCST1", 11, 0, 0, 0, 0, 255, 255, 255, 255, 3, 8, 0, 255 }, + { "FMLUP3", 8, 0, 0, 0, 0, 255, 255, 255, 255, 2, 5, 0, 255 }, + { "BDTLK1", 8, 0, 0, 0, 0, 255, 255, 255, 255, 1, 7, 0, 255 }, + { "BDGLE1", 15, 0, 0, 0, 0, 255, 255, 255, 255, 6, 10, 0, 255 }, + { "BDSHK1", 16, 0, 0, 0, 0, 255, 255, 255, 255, 5, 11, 0, 1 }, + { "BDWOE1", 22, 0, 0, 0, 0, 255, 255, 255, 255, 9, 16, 0, 2 }, + { "BDHIP1", 22, 0, 0, 0, 0, 255, 255, 255, 255, 8, 16, 0, 1 }, + { "BDFLG1", 13, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 }, + { "BDKLT1", 12, 0, 0, 0, 0, 255, 255, 255, 255, 5, 10, 0, 255 }, + { "BDSWY1", 8, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 }, + { "WPSNK1", 5, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 }, + { "WPLAF1", 10, 0, 0, 0, 0, 255, 255, 255, 255, 5, 9, 1, 1 }, + { "DOTLK1", 10, 0, 0, 0, 0, 255, 255, 255, 255, 1, 8, 0, 255 }, + { "DOGST1", 15, 0, 0, 0, 0, 255, 255, 255, 255, 4, 11, 1, 255 }, + { "DO2DF1", 14, 0, 0, 0, 0, 255, 255, 255, 255, 3, 11, 1, 255 }, + { "DOSNG1", 11, 0, 0, 0, 0, 255, 255, 255, 255, 8, 9, 1, 255 }, + { "DOWOE1", 12, 0, 0, 0, 0, 255, 255, 255, 255, 5, 10, 1, 255 }, + { "DO2ME1", 18, 0, 0, 0, 0, 255, 255, 255, 255, 5, 13, 1, 255 }, + { "DOGLP1", 9, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 1, 255 }, + { "DOCRY1", 9, 0, 0, 0, 0, 255, 255, 255, 255, 3, 6, 1, 255 }, + { "METLK1", 5, 0, 0, 0, 0, 255, 255, 255, 255, 1, 4, 0, 255 }, + { "MECHT1", 10, 0, 0, 0, 0, 255, 255, 255, 255, 2, 9, 1, 255 }, + { "ME2DF1", 10, 0, 0, 0, 0, 255, 255, 255, 255, 2, 9, 0, 255 }, + { "MESNG1", 12, 0, 0, 0, 0, 255, 255, 255, 255, 5, 10, 2, 255 }, + { "MEWOE1", 13, 0, 0, 0, 0, 255, 255, 255, 255, 3, 10, 1, 255 }, + { "ME2DO1", 11, 0, 0, 0, 0, 255, 255, 255, 255, 2, 9, 1, 255 }, + { "MEGLP1", 9, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 1, 255 }, + { "MECRY1", 12, 0, 0, 0, 0, 255, 255, 255, 255, 6, 9, 1, 255 }, + { "CSTLK1", 8, 0, 0, 0, 0, 255, 255, 255, 255, 1, 7, 0, 0 }, + { "CSNUD1", 14, 0, 0, 0, 0, 255, 255, 255, 255, 5, 11, 0, 2 }, + { "CSSPR1", 11, 0, 0, 0, 0, 255, 255, 255, 255, 4, 8, 0, 2 }, + { "CSWVE1", 13, 0, 0, 0, 0, 255, 255, 255, 255, 4, 9, 0, 1 }, + { "CSYEL1", 9, 0, 0, 0, 0, 255, 255, 255, 255, 2, 6, 0, 1 }, + { "JMTLK1", 7, 0, 0, 0, 0, 1, 4, 255, 255, 1, 6, 0, 0 }, + { "JMEGO1", 11, 0, 0, 0, 0, 6, 255, 255, 255, 3, 8, 0, 1 }, + { "JMARS1", 7, 0, 0, 0, 0, 4, 6, 255, 255, 3, 6, 0, 2 }, + { "JMHIP1", 8, 0, 0, 0, 0, 3, 5, 7, 255, 2, 7, 0, 1 }, + { "JMBNK1", 2, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 }, + { "MRTLK1", 9, 0, 0, 0, 0, 4, 7, 255, 255, 2, 7, 0, 1 }, + { "MRHOF1", 8, 0, 0, 0, 0, 3, 5, 255, 255, 2, 5, 0, 255 }, + { "MRMRN1", 11, 0, 0, 0, 0, 3, 7, 255, 255, 1, 8, 0, 0 }, + { "MRDPR1", 11, 0, 0, 0, 0, 1, 5, 9, 255, 1, 8, 0, 255 }, + { "MRGLE1", 13, 0, 0, 0, 0, 5, 9, 255, 255, 3, 10, 0, 2 }, + { "MRTDF1", 11, 0, 0, 0, 0, 3, 7, 9, 255, 3, 9, 0, 1 }, + { "MREDF1", 11, 0, 0, 0, 0, 4, 255, 255, 255, 1, 10, 1, 255 }, + { "MREPL1", 12, 0, 0, 0, 0, 5, 6, 7, 9, 2, 9, 1, 1 }, + { "MRAPL1", 12, 0, 0, 0, 0, 4, 8, 9, 255, 2, 9, 0, 1 }, + { "MREVL1", 8, 0, 0, 0, 0, 5, 255, 255, 255, 1, 5, 1, 255 }, + { "BWDMR1", 16, 0, 0, 0, 0, 4, 7, 9, 11, 3, 14, 0, 1 }, + { "BWBUF1", 12, 0, 0, 0, 0, 5, 8, 255, 255, 3, 11, 0, 1 }, + { "BWHIP1", 12, 0, 0, 0, 0, 3, 6, 255, 255, 1, 9, 2, 0 }, + { "BWHWL1", 14, 0, 0, 0, 0, 255, 255, 255, 255, 1, 4, 2, 255 }, + { "BWLEN1", 10, 0, 0, 0, 0, 3, 6, 255, 255, 2, 7, 0, 1 }, + { "BWSRL1", 6, 0, 0, 0, 0, 255, 255, 255, 255, 2, 5, 0, 1 }, + { "BWWAG1", 6, 0, 0, 0, 0, 4, 10, 14, 18, 1, 4, 0, 0 }, + { "BWYEL1", 8, 0, 0, 0, 0, 4, 255, 255, 255, 2, 7, 0, 1 }, + { "BWTLK1", 15, 0, 0, 0, 0, 5, 8, 255, 255, 5, 9, 0, 1 }, + { "SLTLK1", 19, 0, 0, 0, 0, 255, 255, 255, 255, 1, 18, 0, 255 }, + { "SLPND1", 12, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 }, + { "SLPNT1", 8, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 }, + { "SLPTR1", 14, 0, 0, 0, 0, 255, 255, 255, 255, 6, 13, 1, 255 }, + { "SDTLK1", 7, 0, 0, 0, 0, 255, 255, 255, 255, 1, 5, 0, 255 }, + { "SDPDF1", 10, 0, 0, 0, 0, 255, 255, 255, 255, 3, 6, 0, 255 }, + { "SDPNT1", 10, 0, 0, 0, 0, 255, 255, 255, 255, 2, 7, 0, 255 }, + { "SDSLF1", 10, 0, 0, 0, 0, 255, 255, 255, 255, 2, 7, 0, 255 }, + { "SDSTG1", 10, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 }, + { "SDWVE1", 10, 0, 0, 0, 0, 255, 255, 255, 255, 1, 8, 0, 255 }, + { "SDSTK1", 9, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 }, + { "SDSMK1", 22, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 }, + { "SDGLN1", 5, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 }, + { "SDLAF1", 8, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 }, + { "RMHIP1", 12, 0, 0, 0, 0, 7, 255, 255, 255, 1, 10, 2, 255 }, + { "RMGES1", 19, 0, 0, 0, 0, 11, 255, 255, 255, 8, 13, 2, 2 }, + { "RMPCH1", 18, 0, 0, 0, 0, 12, 255, 255, 255, 6, 13, 0, 2 }, + { "RMSTH1", 12, 0, 0, 0, 0, 5, 255, 255, 255, 3, 6, 0, 2 }, + { "RMHND1", 7, 0, 0, 0, 0, 5, 255, 255, 255, 5, 5, 1, 255 }, + { "RMSTH1", 12, 0, 0, 0, 0, 5, 255, 255, 255, 5, 6, 1, 2 }, + { "SGHND1", 10, 0, 0, 0, 0, 255, 255, 255, 255, 1, 9, 0, 0 }, + { "SGSTF1", 13, 0, 0, 0, 0, 255, 255, 255, 255, 1, 12, 0, 255 }, + { "SGSLP1", 16, 0, 0, 0, 0, 255, 255, 255, 255, 1, 15, 0, 255 }, + { "SGPHC1", 12, 0, 0, 0, 0, 255, 255, 255, 255, 4, 9, 0, 255 }, + { "SGHALT", 22, 0, 0, 0, 0, 255, 255, 255, 255, 7, 15, 0, 255 }, + { "STTLK1", 13, 0, 0, 0, 0, 5, 9, 255, 255, 3, 10, 0, 2 }, + { "STTNM1", 13, 0, 0, 0, 0, 5, 9, 255, 255, 3, 10, 0, 2 }, + { "STFST1", 11, 0, 0, 0, 0, 3, 8, 255, 255, 1, 9, 0, 255 }, + { "STLAF1", 20, 0, 0, 0, 0, 255, 255, 255, 255, 11, 15, 1, 2 }, + { "STGES1", 13, 0, 0, 0, 0, 5, 7, 255, 255, 3, 7, 0, 2 }, + { "STFNT1", 10, 0, 0, 0, 0, 4, 6, 255, 255, 255, 255, 0, 2 }, + { "STSRK1", 10, 0, 0, 0, 0, 255, 255, 255, 255, 1, 3, 2, 0 }, + { "STRED1", 11, 0, 0, 0, 0, 255, 255, 255, 255, 2, 10, 0, 255 }, + { "STLKU1", 6, 0, 0, 0, 0, 3, 255, 255, 255, 2, 5, 0, 0 }, + { "STKEY1", 15, 0, 0, 0, 0, 9, 11, 255, 255, 9, 14, 0, 255 }, + { "STMKTD1", 7, 0, 0, 0, 0, 3, 6, 255, 255, 1, 6, 0, 255 }, + { "STTKM1", 21, 0, 0, 0, 0, 12, 13, 15, 16, 12, 17, 0, 1 }, + { "STMSZ1", 10, 0, 0, 0, 0, 255, 255, 255, 255, 1, 3, 2, 255 }, + { "STPNV1", 14, 0, 0, 0, 0, 6, 11, 255, 255, 4, 11, 0, 1 }, + { "STSOM1", 10, 0, 0, 0, 0, 255, 255, 255, 255, 1, 3, 2, 255 }, + { "MYTLK1", 9, 0, 0, 0, 0, 2, 4, 255, 255, 1, 4, 0, 0 }, + { "MYSQUAWK", 5, 0, 0, 0, 0, 255, 255, 255, 255, 3, 3, 1, 255 }, + { "SPTLK", 12, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 }, + { "SPARM", 16, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 }, + { "SPHOP", 18, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 }, + { "SPLNT", 16, 0, 0, 0, 0, 255, 255, 255, 255, 3, 13, 0, 255 }, + { "SPLAF", 11, 0, 0, 0, 0, 255, 255, 255, 255, 5, 10, 2, 255 }, + { "SPTFN", 10, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 }, + { "SPPIN", 14, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 }, + { "SPINH1", 21, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 }, + { "SPSFTCOM", 10, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 }, + { "MFTMZ1", 9, 0, 0, 0, 0, 255, 255, 255, 255, 1, 8, 1, 255 }, + { "MFTLK1", 13, 0, 0, 0, 0, 2, 7, 255, 255, 1, 12, 1, 255 }, + { "VGCIR1", 15, 0, 0, 0, 0, 5, 9, 255, 255, 2, 13, 1, 255 }, + { "VGBIT1", 12, 0, 0, 0, 0, 6, 9, 255, 255, 2, 9, 1, 255 }, + { "VGANG1", 10, 0, 0, 0, 0, 9, 255, 255, 255, 1, 9, 0, 255 }, + { "VGCOM1", 13, 0, 0, 0, 0, 5, 11, 255, 255, 2, 11, 0, 255 }, + { "VGCUR1", 8, 0, 0, 0, 0, 4, 8, 255, 255, 2, 7, 0, 255 }, + { "VGTLK1", 11, 0, 0, 0, 0, 3, 6, 255, 255, 3, 10, 0, 255 }, + { "VGEXP1", 10, 0, 0, 0, 0, 5, 9, 255, 255, 3, 9, 0, 255 }, + { "WFTLK1", 8, 0, 0, 0, 0, 255, 255, 255, 255, 1, 7, 0, 1 }, + { "WFPNT1", 20, 0, 0, 0, 0, 255, 255, 255, 255, 6, 16, 0, 1 }, + { "WFFST1", 13, 0, 0, 0, 0, 255, 255, 255, 255, 2, 8, 0, 2 }, + { "WFTNO1", 8, 0, 0, 0, 0, 255, 255, 255, 255, 2, 5, 0, 2 }, + { "WFSRG1", 11, 0, 0, 0, 0, 255, 255, 255, 255, 3, 8, 0, 1 }, + { "WFGTK1", 16, 0, 0, 0, 0, 255, 255, 255, 255, 1, 15, 0, 255 }, + { "WFPAW1", 24, 0, 0, 0, 0, 255, 255, 255, 255, 4, 22, 0, 1 }, + { "LGTLK", 20, 0, 0, 0, 0, 4, 8, 11, 15, 1, 17, 0, 255 }, + { "LGSHOUT", 16, 0, 0, 0, 0, 12, 255, 255, 255, 6, 12, 0, 255 }, + { "POMRN1", 12, 0, 0, 0, 0, 3, 5, 7, 255, 3, 9, 0, 2 }, + { "POGLE1", 14, 0, 0, 0, 0, 7, 10, 255, 255, 5, 10, 0, 2 }, + { "PLMRG1", 16, 0, 0, 0, 0, 9, 255, 255, 255, 8, 12, 0, 1 }, + { "PLCMR1", 16, 0, 0, 0, 0, 8, 10, 255, 255, 8, 12, 0, 3 }, + { "PLEVL1", 17, 0, 0, 0, 0, 9, 255, 255, 255, 7, 9, 0, 1 }, + { "PLEDF1", 9, 0, 0, 0, 0, 4, 6, 255, 255, 5, 7, 0, 2 }, + { "PLTLK1", 11, 0, 0, 0, 0, 5, 8, 255, 255, 5, 8, 0, 1 }, + { "ELTLK1", 8, 0, 0, 0, 0, 3, 5, 7, 255, 2, 7, 0, 255 }, + { "ELSNR1", 7, 0, 0, 0, 0, 3, 255, 255, 255, 1, 5, 0, 255 }, + { "RG2TK1", 10, 0, 0, 0, 0, 4, 6, 255, 255, 2, 6, 0, 1 }, + { "RG2TK1", 10, 0, 0, 0, 0, 4, 6, 255, 255, 2, 6, 0, 1 }, + { "C2TALK", 6, 0, 0, 0, 0, 2, 5, 255, 255, 1, 5, 0, 255 }, + { "C2BIT1", 13, 0, 0, 0, 0, 3, 5, 9, 11, 2, 11, 2, 255 }, + { "C2CMP1", 13, 0, 0, 0, 0, 6, 9, 255, 255, 5, 10, 1, 2 }, + { "C2COY1", 14, 0, 0, 0, 0, 6, 8, 255, 255, 4, 8, 0, 3 }, + { "C2FNG1", 5, 0, 0, 0, 0, 255, 255, 255, 255, 4, 4, 0, 255 }, + { "C2GRB1", 13, 0, 0, 0, 0, 6, 255, 255, 255, 6, 9, 0, 3 }, + { "C2GST1", 9, 0, 0, 0, 0, 4, 255, 255, 255, 4, 7, 0, 2 }, + { "C2HCN1", 10, 0, 0, 0, 0, 6, 9, 255, 255, 4, 9, 0, 0 }, + { "C2HND1", 7, 0, 0, 0, 0, 6, 255, 255, 255, 2, 6, 0, 1 }, + { "666TKBB3", 21, 0, 0, 0, 0, 9, 14, 255, 255, 6, 16, 0, 255 }, + { "665TFLX3", 27, 0, 0, 0, 0, 10, 14, 17, 255, 10, 18, 0, 255 }, + { "664FXTK3", 18, 0, 0, 0, 0, 5, 7, 11, 13, 3, 15, 0, 255 }, + { "FDTALK", 15, 0, 0, 0, 0, 9, 255, 255, 255, 7, 9, 0, 255 }, + { "FDYELL", 16, 0, 0, 0, 0, 10, 255, 255, 255, 8, 10, 0, 255 }, + { "GLTLK", 20, 0, 0, 0, 0, 6, 12, 18, 255, 1, 19, 0, 255 }, + { "GLTRN", 4, 0, 0, 0, 0, 3, 255, 255, 255, 1, 2, 0, 255 }, + { "RAYTALK1", 10, 0, 0, 0, 0, 3, 5, 8, 255, 1, 9, 0, 255 }, + { "BRTKB1", 17, 0, 0, 0, 0, 255, 255, 255, 255, 2, 14, 0, 255 } + }; + + static const int32 characterAnims[] = { + 0, 39, 81, 89, 91, 108, 117, 124, 138, 146, + 148, 156, 164, 169, 174, 179, 184, 193, 197, 207, + 213, 218, 233, 235, 244, 245, 246, 246, 246, 246, + 253, 253, 253, 253, 260, 262, 264, 269, 271, 273, + 282, 284, 285, 287, 289, 290, 291, 291, 291, 291, + 289, 289, 289, 289, 289, 289, 289, 289, 289, 289 + }; + + return &anims[characterAnims[characterId] + animationId]; +} + +bool Character::loadShadowAnimation(Common::String animName) { + debugC(1, kDebugCharacter, "loadShadowAnimation(%s)", animName.c_str()); + + _shadowAnim = new Animation(_vm); + if (!_shadowAnim->loadAnimation(animName)) + return false; + + _shadowAnimationInstance = _vm->getAnimationManager()->createNewInstance(kAnimationCharacter); + _vm->getAnimationManager()->addInstance(_shadowAnimationInstance); + _shadowAnimationInstance->setAnimation(_shadowAnim); + _shadowAnimationInstance->setVisible(true); + _shadowAnimationInstance->setUseMask(true); + + return true; +} + +void Character::playAnim(int32 animId, int32 unused, int32 flags) { + debugC(3, kDebugCharacter, "playAnim(%d, unused, %d)", animId, flags); + + if (animId == 0) + animId = _animSpecialDefaultId; + + // get the anim to load + const SpecialCharacterAnimation *anim = getSpecialAnimation(_id, animId); + + char animName[20]; + strcpy(animName, anim->_filename); + + int32 facing = _facing; + if (_id == 1) { + // flux special case... some animations are not for every facing + facing = CharacterFlux::fixFacingForAnimation(facing, animId); + } + + if (strchr(animName, '?')) + *strchr(animName, '?') = '0' + facing; + strcat(animName, ".CAF"); + + + if (_animScriptId != -1 && (flags & 8) == 0) + _vm->getSceneAnimationScript(_animScriptId)->_frozenForConversation = true; + + stopSpecialAnim(); + + if (flags & 8) { + // talker + _lineToSayId = _vm->getCurrentLineToSay(); + + // make the talker busy + _flags |= 1; + + // wait for the character to be ready + while (_animScriptId != -1 && _animationInstance->getFrame() > 0 && (_specialAnim && _animationInstance->getAnimation() != _specialAnim)) { + _vm->simpleUpdate(false); + } + } + + + if (_sceneAnimationId > -1) + setAnimationInstance(_vm->getSceneAnimation(_sceneAnimationId)->_animInstance); + + + _animFlags |= flags; + + if (_specialAnim) + delete _specialAnim; + _specialAnim = new Animation(_vm); + _specialAnim->loadAnimation(animName); + + _animSpecialId = animId; + + _animationInstance->setAnimation(_specialAnim); + _animationInstance->setAnimationRange(0, _specialAnim->_numFrames - 1); + _animationInstance->reset(); + _animationInstance->stopAnimation(); + _animationInstance->setLooping(false); +} + +int32 Character::getAnimFlag() { + return _animFlags; +} + +void Character::setAnimFlag(int32 flag) { + _animFlags = flag; +} + +int32 Character::getSceneAnimationId() { + return _sceneAnimationId; +} + +void Character::setDefaultSpecialAnimationId(int32 defaultAnimationId) { + _animSpecialDefaultId = defaultAnimationId; +} + +int32 Character::getFinalX() { + return _finalX; +} + +int32 Character::getFinalY() { + return _finalY; +} + +void Character::updateIdle() { + debugC(5, kDebugCharacter, "updateIdle()"); + + // only flux and drew + if (_id > 1) + return; + + if (_vm->state()->_mouseHidden) + _nextIdleTime = _vm->getOldMilli() + (300 + _vm->randRange(0, 600)) * _vm->getTickLength(); + + if (_vm->getOldMilli() > _nextIdleTime) { + if (((_flags & 1) == 0) || ((_flags & 2) != 0)) { + if (!_vm->state()->_inCloseUp && !_vm->state()->_inCutaway && _animSpecialId == -1) { + if (!_vm->state()->_mouseHidden) { + _nextIdleTime = _vm->getOldMilli() + (300 + _vm->randRange(0, 600)) * _vm->getTickLength(); + playAnim(getRandomIdleAnim(), 0, 0x40); + _flags |= 0x4; + } + } + } + } +} +} // End of namespace Toon + diff --git a/engines/toon/character.h b/engines/toon/character.h new file mode 100644 index 0000000000..43636b8eb5 --- /dev/null +++ b/engines/toon/character.h @@ -0,0 +1,148 @@ +/* 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. +* +* $URL$ +* $Id$ +* +*/ + +#ifndef TOON_CHARACTER_H +#define TOON_CHARACTER_H + +#include "toon/toon.h" + +namespace Toon { + +class ToonEngine; + +struct SpecialCharacterAnimation { + char _filename[9]; // 0 + byte _flag1; // 9 + short _offsetX; // 10 + short _offsetY; // 12 + short _unused; // 14 + short _unused2; // 16 + byte _flags2; // 18 + byte _flags3; // 19 + byte _flags4; // 20 + byte _flags5; // 21 + byte _flags6; // 22 + byte _flags7; // 23 + byte _flags8; // 24 + byte _flags9; // 25 +}; + + +class Character { +public: + Character(ToonEngine *vm); + virtual ~Character(void); + virtual void init(); + virtual int32 getId(); + virtual void setId(int32 id); + virtual void setFacing(int32 facing); + virtual int32 getFacing(); + virtual void setAnimScript(int32 animScriptId); + virtual void setSceneAnimationId(int32 sceneAnimationId); + virtual void setDefaultSpecialAnimationId(int32 defaultAnimationId); + virtual int32 getAnimScript(); + virtual int32 getSceneAnimationId(); + virtual void setFlag(int flag); + virtual int32 getFlag(); + virtual int32 getAnimFlag(); + virtual void setAnimFlag(int32 flag); + virtual void setPosition(int32 x, int32 y); + virtual int32 getX(); + virtual int32 getY(); + virtual int32 getFinalX(); + virtual int32 getFinalY(); + virtual bool walkTo(int32 newPosX, int32 newPosY); + virtual bool getVisible(); + virtual void setVisible(bool visible); + virtual bool loadWalkAnimation(Common::String animName); + virtual bool loadIdleAnimation(Common::String animName); + virtual bool loadTalkAnimation(Common::String animName); + virtual bool loadShadowAnimation(Common::String animName); + virtual bool setupPalette(); + virtual void playStandingAnim(); + virtual void playWalkAnim(int32 start, int32 end); + virtual void playTalkAnim(); + virtual void playAnim(int32 animId, int32 unused, int32 flags); + virtual void update(int32 timeIncrement); + virtual int32 getScale(); + virtual AnimationInstance *getAnimationInstance(); + virtual void setAnimationInstance(AnimationInstance *instance); + virtual void save(Common::WriteStream *stream); + virtual void load(Common::ReadStream *stream); + virtual void stopWalk(); + virtual void stopSpecialAnim(); + virtual void updateIdle(); + virtual int32 getRandomIdleAnim() { return 0; } + virtual void updateTimers(int32 relativeAdd); + virtual void setTalking(bool talking) { _isTalking = talking; } + virtual bool isTalking() { return _isTalking; } + + int32 getFacingFromDirection(int32 dx, int32 dy); + static const SpecialCharacterAnimation *getSpecialAnimation(int32 characterId, int32 animationId); + + +protected: + ToonEngine *_vm; + + int32 _id; + int32 _animScriptId; + int32 _animSpecialId; + int32 _animSpecialDefaultId; + int32 _sceneAnimationId; + int32 _lineToSayId; + int32 _time; + int32 _x; + int32 _y; + int32 _z; + int32 _finalX; + int32 _finalY; + int32 _facing; + int32 _flags; + int32 _animFlags; + int32 _scale; + int32 _nextIdleTime; + bool _visible; + bool _blockingWalk; + int32 _speed; + int32 _lastWalkTime; + int32 _numPixelToWalk; + bool _isTalking; + + AnimationInstance *_animationInstance; + AnimationInstance *_shadowAnimationInstance; + Animation *_walkAnim; + Animation *_idleAnim; + Animation *_talkAnim; + Animation *_shadowAnim; + Animation *_specialAnim; + + int32 _currentPathX[4096]; + int32 _currentPathY[4096]; + int32 _currentPathNodeCount; + int32 _currentPathNode; +}; + +} // End of namespace Toon +#endif diff --git a/engines/toon/conversation.cpp b/engines/toon/conversation.cpp new file mode 100644 index 0000000000..4678ccc1c8 --- /dev/null +++ b/engines/toon/conversation.cpp @@ -0,0 +1,49 @@ +/* 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. +* +* $URL$ +* $Id$ +* +*/ + +#include "toon/conversation.h" + +namespace Toon { + +void Conversation::save(Common::WriteStream *stream, int16 *conversationDataBase) { + stream->writeSint32BE(_enable); + for (int32 i = 0; i < 10; i++) { + stream->writeSint32BE(state[i]._data2); + stream->writeSint16BE(state[i]._data3); + stream->writeSint32BE((int16 *)state[i]._data4 - conversationDataBase); + } +} + +void Conversation::load(Common::ReadStream *stream, int16 *conversationDataBase) { + _enable = stream->readSint32BE(); + for (int32 i = 0; i < 10; i++) { + state[i]._data2 = stream->readSint32BE(); + state[i]._data3 = stream->readSint16BE(); + state[i]._data4 = conversationDataBase + stream->readSint32BE(); + } +} + + +} diff --git a/engines/toon/conversation.h b/engines/toon/conversation.h new file mode 100644 index 0000000000..784c681055 --- /dev/null +++ b/engines/toon/conversation.h @@ -0,0 +1,50 @@ +/* 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. +* +* $URL$ +* $Id$ +* +*/ + +#ifndef TOON_CONVERSATION_H +#define TOON_CONVERSATION_H + +#include "engines/engine.h" +#include "common/stream.h" + +namespace Toon { + +class Conversation { +public: + int32 _enable; // 00 + + struct ConvState { + int32 _data2; // 04 + int16 _data3; // 08 + void *_data4; // 10 + } state[10]; + + void save(Common::WriteStream *stream, int16 *conversationDataBase); + void load(Common::ReadStream *stream, int16 *conversationDataBase); +}; + +} // End of namespace Toon + +#endif diff --git a/engines/toon/detection.cpp b/engines/toon/detection.cpp new file mode 100644 index 0000000000..e9649b1560 --- /dev/null +++ b/engines/toon/detection.cpp @@ -0,0 +1,273 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ + +#include "common/config-manager.h" +#include "engines/advancedDetector.h" +#include "common/savefile.h" +#include "common/system.h" +#include "base/plugins.h" +#include "graphics/thumbnail.h" +#include "toon/toon.h" + +static const PlainGameDescriptor ToonGames[] = { + { "toon", "Toonstruck" }, + { 0, 0 } +}; + +namespace Toon { + +using Common::GUIO_NONE; + +static const ADGameDescription gameDescriptions[] = { + { + "toon", "", + { + {"local.pak", 0, "3290209ef9bc92692108dd2f45df0736", 3237611}, + {"arcaddbl.svl", 0, "c418478cd2833c7c983799f948af41ac", 7844688}, + {"study.svl", 0, "281efa3f33f6712c0f641a605f4d40fd", 2511090}, + AD_LISTEND + }, + Common::EN_ANY, Common::kPlatformPC, ADGF_NO_FLAGS, GUIO_NONE + }, + { + "toon", "", + { + {"local.pak", 0, "517132c3575b38806d1e7b6f59848072", 3224044}, + {"arcaddbl.svl", 0, "ff74008827b62fbef1f46f104c438e44", 9699256}, + {"study.svl", 0, "df056b94ea83f1ed92a539cf636053ab", 2542668}, + AD_LISTEND + }, + Common::FR_FRA, Common::kPlatformPC, ADGF_NO_FLAGS, GUIO_NONE + }, + { + "toon", "", + { + {"local.pak", 0, "bf5da4c03f78ffbd643f12122319366e", 3250841}, + {"arcaddbl.svl", 0, "7a0d74f4d66d1c722b946abbeb0834ef", 9122249}, + {"study.svl", 0, "72fe96a9e10967d3138e918295babc42", 2910283}, + AD_LISTEND + }, + Common::DE_DEU, Common::kPlatformPC, ADGF_NO_FLAGS, GUIO_NONE + }, + { + "toon", "", + { + {"local.pak", 0, "e8645168a247e2abdbfc2f9fa9d1c0fa", 3232222}, + {"arcaddbl.svl", 0, "7893ac4cc78d51356baa058bbee7aa28", 8275016}, + {"study.svl", 0, "b6b1ee2d9d94d53d305856039ab7bde7", 2634620}, + AD_LISTEND + }, + Common::ES_ESP, Common::kPlatformPC, ADGF_NO_FLAGS, GUIO_NONE + }, + { + "toon", "", + { + {"local.pak", 0, "bf5da4c03f78ffbd643f12122319366e", 3250841}, + {"wacexdbl.emc", 0, "cfbc2156a31b294b038204888407ebc8", 6974}, + {"generic.svl", 0, "5eb99850ada22f0b8cf6392262d4dd07", 9404599}, + AD_LISTEND + }, + Common::DE_DEU, Common::kPlatformPC, ADGF_DEMO, GUIO_NONE + }, + + AD_TABLE_END_MARKER +}; + +static const ADFileBasedFallback fileBasedFallback[] = { + { &gameDescriptions[0], { "local.pak", "arcaddbl.svl", "study.svl", 0 } }, // default to english version + { 0, { 0 } } +}; + +} // End of namespace Toon + +static const char * const directoryGlobs[] = { + "misc", + "act1", + "arcaddbl", + "act2", + "study", + 0 +}; + +static const ADParams detectionParams = { + (const byte *)Toon::gameDescriptions, + sizeof(ADGameDescription), + 5000, // number of md5 bytes + ToonGames, + 0, // no obsolete targets data + "toon", + Toon::fileBasedFallback, + 0, + // Additional GUI options (for every game} + Common::GUIO_NONE, + // Maximum directory depth + 3, + // List of directory globs + directoryGlobs +}; + +class ToonMetaEngine : public AdvancedMetaEngine { +public: + ToonMetaEngine() : AdvancedMetaEngine(detectionParams) {} + + virtual const char *getName() const { + return "Toon Engine"; + } + + virtual const char *getOriginalCopyright() const { + return "Toonstruck (C) 1996 Virgin Interactive"; + } + + virtual bool hasFeature(MetaEngineFeature f) const; + virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const; + virtual int getMaximumSaveSlot() const; + virtual SaveStateList listSaves(const char *target) const; + SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const; + virtual void removeSaveState(const char *target, int slot) const; +}; + +bool ToonMetaEngine::hasFeature(MetaEngineFeature f) const { + return + (f == kSupportsListSaves) || + (f == kSupportsLoadingDuringStartup) || + (f == kSupportsDeleteSave) || + (f == kSavesSupportMetaInfo) || + (f == kSavesSupportThumbnail) || + (f == kSavesSupportCreationDate); +} + +void ToonMetaEngine::removeSaveState(const char *target, int slot) const { + Common::String fileName = Common::String::printf("%s.%03d", target, slot); + g_system->getSavefileManager()->removeSavefile(fileName); +} + +int ToonMetaEngine::getMaximumSaveSlot() const { return 99; } + +SaveStateList ToonMetaEngine::listSaves(const char *target) const { + Common::SaveFileManager *saveFileMan = g_system->getSavefileManager(); + Common::StringArray filenames; + Common::String pattern = target; + pattern += ".???"; + + filenames = saveFileMan->listSavefiles(pattern); + sort(filenames.begin(), filenames.end()); // Sort (hopefully ensuring we are sorted numerically..) + + SaveStateList saveList; + int slotNum = 0; + for (Common::StringArray::const_iterator filename = filenames.begin(); filename != filenames.end(); ++filename) { + // Obtain the last 3 digits of the filename, since they correspond to the save slot + slotNum = atoi(filename->c_str() + filename->size() - 3); + + if (slotNum >= 0 && slotNum <= 99) { + Common::InSaveFile *file = saveFileMan->openForLoading(*filename); + if (file) { + int32 version = file->readSint32BE(); + if (version != TOON_SAVEGAME_VERSION) { + delete file; + continue; + } + + // read name + uint16 nameSize = file->readUint16BE(); + if (nameSize >= 255) { + delete file; + continue; + } + char name[256]; + file->read(name, nameSize); + name[nameSize] = 0; + + saveList.push_back(SaveStateDescriptor(slotNum, name)); + delete file; + } + } + } + + return saveList; +} + +SaveStateDescriptor ToonMetaEngine::querySaveMetaInfos(const char *target, int slot) const { + Common::String fileName = Common::String::printf("%s.%03d", target, slot); + Common::InSaveFile *file = g_system->getSavefileManager()->openForLoading(fileName); + + if (file) { + + int32 version = file->readSint32BE(); + if (version != TOON_SAVEGAME_VERSION) { + delete file; + return SaveStateDescriptor(); + } + + uint32 saveNameLength = file->readUint16BE(); + char saveName[256]; + file->read(saveName, saveNameLength); + saveName[saveNameLength] = 0; + + SaveStateDescriptor desc(slot, saveName); + + Graphics::Surface *thumbnail = new Graphics::Surface(); + assert(thumbnail); + if (!Graphics::loadThumbnail(*file, *thumbnail)) { + delete thumbnail; + thumbnail = 0; + } + desc.setThumbnail(thumbnail); + + desc.setDeletableFlag(true); + desc.setWriteProtectedFlag(false); + + uint32 saveDate = file->readUint32BE(); + uint16 saveTime = file->readUint16BE(); + + int day = (saveDate >> 24) & 0xFF; + int month = (saveDate >> 16) & 0xFF; + int year = saveDate & 0xFFFF; + + desc.setSaveDate(year, month, day); + + int hour = (saveTime >> 8) & 0xFF; + int minutes = saveTime & 0xFF; + + desc.setSaveTime(hour, minutes); + + delete file; + return desc; + } + + return SaveStateDescriptor(); +} + +bool ToonMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const { + if (desc) { + *engine = new Toon::ToonEngine(syst, desc); + } + return desc != 0; +} + +#if PLUGIN_ENABLED_DYNAMIC(TOON) + REGISTER_PLUGIN_DYNAMIC(TOON, PLUGIN_TYPE_ENGINE, ToonMetaEngine); +#else + REGISTER_PLUGIN_STATIC(TOON, PLUGIN_TYPE_ENGINE, ToonMetaEngine); +#endif diff --git a/engines/toon/drew.cpp b/engines/toon/drew.cpp new file mode 100644 index 0000000000..b50a8db3dc --- /dev/null +++ b/engines/toon/drew.cpp @@ -0,0 +1,122 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ + +#include "toon/drew.h" + +namespace Toon { + +CharacterDrew::CharacterDrew(ToonEngine *vm) : Character(vm) { + _id = 0; + _blockingWalk = true; + _animationInstance = vm->getAnimationManager()->createNewInstance(kAnimationCharacter); + _animationInstance->setUseMask(true); + vm->getAnimationManager()->addInstance(_animationInstance); +} + +CharacterDrew::~CharacterDrew(void) { +} + +bool CharacterDrew::setupPalette() { + debugC(1, kDebugCharacter, "setupPalette()"); + + if (_walkAnim) { + _walkAnim->applyPalette(129, 129 * 3, 63); + return true; + } + return false; +} + +void CharacterDrew::setFacing(int32 facing) { + debugC(4, kDebugCharacter, "setFacing(%d)", facing); + _facing = facing; +} + +void CharacterDrew::setPosition(int32 x, int32 y) { + debugC(5, kDebugCharacter, "setPosition(%d, %d)", x, y); + + _z = _vm->getLayerAtPoint(x, y); + _scale = _vm->getScaleAtPoint(x, y); + + // work out position and scale of the character sprite + int32 width = _walkAnim->getWidth() * _scale / 1024; + int32 height = 210 * _scale / 1024; + _animationInstance->setPosition(x - width / 2, y - height, _z , false); + _animationInstance->setScale(_scale); + + // work out position and scale of the shadow below character + int32 shadowWidth = _shadowAnim->getWidth() * _scale / 1024; + int32 shadowHeight = _shadowAnim->getHeight() * _scale / 1024; + + _shadowAnimationInstance->setPosition(x - shadowWidth / 2, y - shadowHeight / 2 - 4 , _z , false); + _shadowAnimationInstance->setScale(_scale); + + _x = x; + _y = y; + _animationInstance->setLayerZ(_y); +} + +void CharacterDrew::playStandingAnim() { + debugC(4, kDebugCharacter, "playStandingAnim()"); + + stopSpecialAnim(); + _animationInstance->setAnimation(_walkAnim); + _animationInstance->setFrame(_facing * 2); + _shadowAnimationInstance->setFrame(_facing); + _animationInstance->setAnimationRange(_facing * 2, _facing * 2); + _animationInstance->stopAnimation(); + _animationInstance->setLooping(true); + //setVisible(true); + +} + +void CharacterDrew::playWalkAnim(int32 start, int32 end) { + debugC(4, kDebugCharacter, "playWalkAnim(%d, %d)", start, end); + + stopSpecialAnim(); + _animationInstance->setAnimation(_walkAnim); + _shadowAnimationInstance->setFrame(_facing); + _animationInstance->setAnimationRange(16 + _facing * 14, 16 + _facing * 14 + 13); + _animationInstance->playAnimation(); + _animationInstance->setFps(16); + _animationInstance->setLooping(true); + + //setVisible(true); +} + +void CharacterDrew::update(int32 timeIncrement) { + debugC(5, kDebugCharacter, "update(%d)", timeIncrement); + Character::update(timeIncrement); + setPosition(_x, _y); + +} + +int32 CharacterDrew::getRandomIdleAnim() { + debugC(3, kDebugCharacter, "getRandomIdleAnim()"); + + static const int32 idle[] = { 6, 9, 10, 11, 12 }; + return idle[_vm->randRange(0, 4)]; +} +} // End of namespace Toon + diff --git a/engines/toon/drew.h b/engines/toon/drew.h new file mode 100644 index 0000000000..a5be4c76c3 --- /dev/null +++ b/engines/toon/drew.h @@ -0,0 +1,51 @@ +/* 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. +* +* $URL$ +* $Id$ +* +*/ + +#ifndef TOON_DREW_H +#define TOON_DREW_H + +#include "toon/character.h" + + +namespace Toon { + +class ToonEngine; + +class CharacterDrew : public Character { +public: + CharacterDrew(ToonEngine *vm); + virtual ~CharacterDrew(void); + bool setupPalette(); + void setFacing(int32 facing); + void playStandingAnim(); + void setPosition(int32 x, int32 y); + void update(int32 timeIncrement); + void playWalkAnim(int32 start, int32 end); + int32 getRandomIdleAnim(); +}; + +} // End of namespace Toon + +#endif diff --git a/engines/toon/flux.cpp b/engines/toon/flux.cpp new file mode 100644 index 0000000000..9dd38cd37a --- /dev/null +++ b/engines/toon/flux.cpp @@ -0,0 +1,140 @@ +/* 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. +* +* $URL$ +* $Id$ +* +*/ + +#include "toon/flux.h" + +namespace Toon { + +CharacterFlux::CharacterFlux(ToonEngine *vm) : Character(vm) { + _id = 1; + _animationInstance = vm->getAnimationManager()->createNewInstance(kAnimationCharacter); + _animationInstance->setUseMask(true); + vm->getAnimationManager()->addInstance(_animationInstance); +} + +CharacterFlux::~CharacterFlux(void) { +} + +void CharacterFlux::playStandingAnim() { + debugC(4, kDebugCharacter, "playStandingAnim()"); + + _animationInstance->setAnimation(_walkAnim); + _animationInstance->setFrame(_facing * 3); + _animationInstance->setAnimationRange(_facing * 3, _facing * 3); + _animationInstance->stopAnimation(); + _animationInstance->setLooping(true); + + //s/etVisible(true); +} + +void CharacterFlux::setVisible(bool visible) { + if (_vm->state()->_currentChapter == 2) { + Character::setVisible(false); + } else { + Character::setVisible(visible); + } +} + +void CharacterFlux::playWalkAnim(int32 start, int32 end) { + debugC(4, kDebugCharacter, "playWalkAnim(%d, %d)", start, end); + + _animationInstance->setAnimation(_walkAnim); + _animationInstance->setAnimationRange(24 + _facing * 10, 24 + _facing * 10 + 9); + _animationInstance->playAnimation(); + _animationInstance->setFps(16); + _animationInstance->setLooping(true); +} + +int32 CharacterFlux::fixFacingForAnimation(int32 originalFacing, int32 animationId) { + + static const byte fixFluxAnimationFacing[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xee, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x55, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + + byte animFacingFlag = fixFluxAnimationFacing[animationId]; + int32 v5 = 1 << originalFacing; + int32 v6 = 1 << originalFacing; + int32 facingMask = 0; + do { + if (v6 & animFacingFlag) { + facingMask = v6; + } else if (v5 & animFacingFlag) { + facingMask = v5; + } + v5 >>= 1; + v6 <<= 1; + } + while (!facingMask); + + int32 finalFacing = 0; + for (finalFacing = 0; ; ++finalFacing) { + facingMask >>= 1; + if (!facingMask) + break; + } + + return finalFacing; +} + +void CharacterFlux::setPosition(int32 x, int32 y) { + debugC(5, kDebugCharacter, "setPosition(%d, %d)", x, y); + + _z = _vm->getLayerAtPoint(x, y); + _scale = _vm->getScaleAtPoint(x, y); + int32 width = _walkAnim->getWidth() * _scale / 1024; + int32 height = 165 * _scale / 1024; + _animationInstance->setPosition(x - width / 2, y - height, _z , false); + _animationInstance->setScale(_scale); + + // in original code, flux shadow scale is 3/4 of real scale + int32 shadowScale = _scale * 3 / 4; + + // work out position and scale of the shadow below character + int32 shadowWidth = _shadowAnim->getWidth() * shadowScale / 1024; + int32 shadowHeight = _shadowAnim->getHeight() * shadowScale / 1024; + _shadowAnimationInstance->setPosition(x - shadowWidth / 2, y - shadowHeight / 2 , _z , false); + _shadowAnimationInstance->setScale(shadowScale); + _x = x; + _y = y; + _finalX = x; + _finalY = y; + _animationInstance->setLayerZ(_y); +} + +void CharacterFlux::update(int32 timeIncrement) { + debugC(5, kDebugCharacter, "update(%d)", timeIncrement); + Character::update(timeIncrement); + setPosition(_x, _y); +} + +int32 CharacterFlux::getRandomIdleAnim() { + debugC(3, kDebugCharacter, "getRandomIdleAnim()"); + static const int32 idle[] = { 0xe, 0xf, 0x21, 0x22, 0x24, 0x25, 0x27 }; + return idle[_vm->randRange(0, 6)]; +} + +} // End of namespace Toon diff --git a/engines/toon/flux.h b/engines/toon/flux.h new file mode 100644 index 0000000000..7981799cba --- /dev/null +++ b/engines/toon/flux.h @@ -0,0 +1,52 @@ +/* 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. +* +* $URL$ +* $Id$ +* +*/ + +#ifndef TOON_FLUX_H +#define TOON_FLUX_H + + +#include "toon/character.h" + +class ToonEngine; + +namespace Toon { + +class CharacterFlux : public Character { +public: + CharacterFlux(ToonEngine *vm); + virtual ~CharacterFlux(void); + + void setPosition(int32 x, int32 y); + void playStandingAnim(); + void playWalkAnim(int32 start, int32 end); + void update(int32 timeIncrement); + int32 getRandomIdleAnim(); + void setVisible(bool visible); + static int32 fixFacingForAnimation(int32 originalFacing, int32 animationId); +}; + +} // End of namespace Toon + +#endif diff --git a/engines/toon/font.cpp b/engines/toon/font.cpp new file mode 100644 index 0000000000..8b042f499d --- /dev/null +++ b/engines/toon/font.cpp @@ -0,0 +1,285 @@ +/* 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. +* +* $URL$ +* $Id$ +* +*/ + +#include "toon/font.h" + +namespace Toon { + +FontRenderer::FontRenderer(ToonEngine *vm) : _vm(vm) { + _currentFontColor[0] = 0; + _currentFontColor[1] = 0xc8; + _currentFontColor[2] = 0xcb; + _currentFontColor[3] = 0xce; + +} + +// mapping extended characters required for foreign versions to font (animation) +static const byte map_textToFont[0x80] = { + '?', '?', '?', '?', 0x03, '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', // 0x8x + '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', // 0x9x + '?', 0x09, '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', // 0xAx + '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', 0x0a, // 0xBx + '?', '?', '?', '?', 0x1d, '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', // 0xCx + '?', 0x0b, '?', '?', '?', '?', 0x1e, '?', '?', '?', '?', '?', 0x1f, '?', '?', 0x19, // 0xDx + 0x0d, 0x04, 0x0e, '?', 0x1a, '?', '?', 0x18, 0x10, 0x0f, 0x12, 0x11, 0x09, 0x05, 0x14, 0x13, // 0xEx + 0x23, 0x08, 0x23, 0x06, 0x15, 0x23, 0x1b, 0x23, 0x23, 0x16, 0x07, 0x17, 0x1c, 0x23, 0x23, 0x23 // 0xFx +}; + +byte FontRenderer::textToFont(byte c) { + // No need to remap simple characters. + if (c < 0x80) + return c; + + // The Spanish version shows grave accent over the 'e' when it should + // be acute. This happens both in the original interpreter and when + // using the common map which works for other languages, so we add a + // special case for it. + if (_vm->_language == Common::ES_ESP && c == 0xe9) + return 0x10; + + // Use the common map to convert the extended characters. + return map_textToFont[c - 0x80]; +} + +void FontRenderer::renderText(int32 x, int32 y, Common::String origText, int32 mode) { + debugC(5, kDebugFont, "renderText(%d, %d, %s, %d)", x, y, origText.c_str(), mode); + + int32 xx, yy; + computeSize(origText, &xx, &yy); + + if (mode & 2) { + y -= yy / 2; + } else if (mode & 4) { + y -= yy; + } + + if (mode & 1) { + x -= xx / 2; + } + + int32 curX = x; + int32 curY = y; + int32 height = 0; + + const byte *text = (const byte *)origText.c_str(); + while (*text) { + byte curChar = *text; + if (curChar == 13) { + curY = curY + height; + height = 0; + curX = x; + } else { + curChar = textToFont(curChar); + _currentFont->drawFontFrame(_vm->getMainSurface(), curChar, curX, curY, _currentFontColor); + curX = curX + _currentFont->getFrameWidth(curChar) - 1; + height = MAX(height, _currentFont->getFrameHeight(curChar)); + } + text++; + } +} + +void FontRenderer::computeSize(Common::String origText, int32 *retX, int32 *retY) { + debugC(4, kDebugFont, "computeSize(%s, retX, retY)", origText.c_str()); + + int32 lineWidth = 0; + int32 lineHeight = 0; + int32 totalHeight = 0; + int32 totalWidth = 0; + + const byte *text = (const byte *)origText.c_str(); + while (*text) { + byte curChar = *text; + if (curChar < 32) { + text++; + continue; + } else if (curChar == 13) { + totalWidth = MAX(totalWidth, lineWidth); + totalHeight += lineHeight; + lineHeight = 0; + lineWidth = 0; + } else { + curChar = textToFont(curChar); + int32 charWidth = _currentFont->getFrameWidth(curChar) - 1; + int32 charHeight = _currentFont->getFrameHeight(curChar); + lineWidth += charWidth; + lineHeight = MAX(lineHeight, charHeight); + } + text++; + } + + totalHeight += lineHeight; + totalWidth = MAX(totalWidth, lineWidth); + + *retX = totalWidth; + *retY = totalHeight; +} + +void FontRenderer::setFont(Animation *font) { + debugC(5, kDebugFont, "setFont(font)"); + + _currentFont = font; +} + +void FontRenderer::setFontColorByCharacter(int32 characterId) { + debugC(5, kDebugFont, "setFontColorByCharacter(%d)", characterId); + + // unfortunately this table was hardcoded in the original executable + static const byte colorsByCharacters[] = { + 0xe0, 0xdc, 0xc8, 0xd6, 0xc1, 0xc8, 0xe9, 0xde, 0xc8, 0xeb, 0xe8, 0xc8, + 0xd1, 0xcf, 0xc8, 0xd8, 0xd5, 0xc8, 0xfb, 0xfa, 0xc8, 0xd9, 0xd7, 0xc8, + 0xe8, 0xe4, 0xc8, 0xe9, 0xfa, 0xc8, 0xeb, 0xe4, 0xc8, 0xeb, 0xe4, 0xc8, + 0xd2, 0xea, 0xc8, 0xd3, 0xd0, 0xc8, 0xe1, 0xdd, 0xc8, 0xd9, 0xd7, 0xc8, + 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, + 0xd2, 0xcf, 0xc8, 0xd1, 0xcf, 0xc8, 0xd9, 0xd7, 0xc8, 0xe3, 0xdd, 0xc8, + 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, + 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, + 0xd9, 0xd7, 0xc8, 0xe6, 0xe4, 0xc8, 0xd9, 0xd7, 0xc8, 0xcd, 0xca, 0xc8, + 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xe8, 0xe8, 0xc8, 0xdb, 0xd5, 0xc8, + 0xe0, 0xdc, 0xc8, 0xd6, 0xc1, 0xc8, 0xd3, 0xd0, 0xc8, 0xd1, 0xcf, 0xc8, + 0xe6, 0xe4, 0xc8, 0xd1, 0xcf, 0xc8, 0xd2, 0xcf, 0xc8, 0xcc, 0xcb, 0xc8, + 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, + 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, + 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8 + }; + + setFontColor(colorsByCharacters[characterId * 3 + 2], colorsByCharacters[characterId * 3 + 1], colorsByCharacters[characterId * 3 + 0]); +} + +void FontRenderer::setFontColor(int32 fontColor1, int32 fontColor2, int32 fontColor3) { + debugC(5, kDebugFont, "setFontColor(%d, %d, %d)", fontColor1, fontColor2, fontColor3); + + _currentFontColor[0] = 0; + _currentFontColor[1] = fontColor1; + _currentFontColor[2] = fontColor2; + _currentFontColor[3] = fontColor3; +} + +void FontRenderer::renderMultiLineText(int32 x, int32 y, Common::String origText, int32 mode) { + debugC(5, kDebugFont, "renderMultiLineText(%d, %d, %s, %d)", x, y, origText.c_str(), mode); + + // divide the text in several lines + // based on number of characters or size of lines. + byte text[1024]; + strncpy((char *)text, origText.c_str(), 1023); + text[1023] = 0; + + byte *lines[16]; + int32 lineSize[16]; + int32 numLines = 0; + + byte *it = text; + + int32 maxWidth = 0; + int32 curWidth = 0; + + while (1) { + byte *lastLine = it; + byte *lastSpace = it; + int32 lastSpaceX = 0; + int32 curLetterNr = 0; + curWidth = 0; + + while (*it && curLetterNr < 50 && curWidth < 580) { + byte curChar = *it; + if (curChar == 32) { + lastSpace = it; + lastSpaceX = curWidth; + } else + curChar = textToFont(curChar); + + int width = _currentFont->getFrameWidth(curChar); + curWidth += width - 2; + it++; + curLetterNr++; + } + + if (*lastLine == 0) + break; + + lines[numLines] = lastLine; + + if (*it == 0) + lineSize[numLines] = curWidth; + else + lineSize[numLines] = lastSpaceX; + + if (lineSize[numLines] > maxWidth) + maxWidth = lineSize[numLines]; + + lastLine = lastSpace + 1; + numLines++; + + if (*it == 0) + break; + + it = lastLine; + *lastSpace = 0; + + if (numLines >= 16) + break; + } + + if (curWidth > maxWidth) { + maxWidth = curWidth; + } + //numLines++; + + // get font height (assumed to be constant) + int32 height = _currentFont->getHeight(); + int textSize = (height - 2) * numLines; + y = y - textSize; + if (y < 30) + y = 30; + if (y + textSize > 370) + y = 370 - textSize; + + x -= _vm->state()->_currentScrollValue; + + // adapt x + if (x - 30 - maxWidth / 2 < 0) + x = maxWidth / 2 + 30; + + if (x + 30 + (maxWidth / 2) > 640) + x = 640 - (maxWidth / 2) - 30; + + // we have good coordinates now, we can render the multi line + int32 curX = x; + int32 curY = y; + + for (int32 i = 0; i < numLines; i++) { + const byte *line = lines[i]; + curX = x - lineSize[i] / 2; + while (*line) { + byte curChar = textToFont(*line); + if (curChar != 32) _currentFont->drawFontFrame(_vm->getMainSurface(), curChar, curX + _vm->state()->_currentScrollValue, curY, _currentFontColor); + curX = curX + _currentFont->getFrameWidth(curChar) - 2; + //height = MAX(height, _currentFont->getFrameHeight(curChar)); + line++; + } + curY += height; + } +} + +} // End of namespace Toon diff --git a/engines/toon/font.h b/engines/toon/font.h new file mode 100644 index 0000000000..e1b00fbf54 --- /dev/null +++ b/engines/toon/font.h @@ -0,0 +1,53 @@ +/* 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. +* +* $URL$ +* $Id$ +* +*/ + +#ifndef TOON_FONT_H +#define TOON_FONT_H + +#include "toon/toon.h" + +namespace Toon { + +class FontRenderer { +public: + FontRenderer(ToonEngine *vm); + ~FontRenderer(void); + + void setFont(Animation *font); + void computeSize(Common::String origText, int32 *retX, int32 *retY); + void renderText(int32 x, int32 y, Common::String origText, int32 mode); + void renderMultiLineText(int32 x, int32 y, Common::String origText, int32 mode); + void setFontColorByCharacter(int32 characterId); + void setFontColor(int32 fontColor1, int32 fontColor2, int32 fontColor3); +protected: + Animation *_currentFont; + ToonEngine *_vm; + byte _currentFontColor[4]; + byte textToFont(byte c); +}; + +} // End of namespace Toon + +#endif diff --git a/engines/toon/hotspot.cpp b/engines/toon/hotspot.cpp new file mode 100644 index 0000000000..5af61197d7 --- /dev/null +++ b/engines/toon/hotspot.cpp @@ -0,0 +1,157 @@ +/* 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. +* +* $URL$ +* $Id$ +* +*/ + +#include "toon/hotspot.h" +#include "toon/tools.h" + +namespace Toon { + +Hotspots::Hotspots(ToonEngine *vm) : _vm(vm) { + _items = 0; + _numItems = 0; +} + + +void Hotspots::load(Common::ReadStream *Stream) { + delete[] _items; + + _numItems = Stream->readSint16BE(); + _items = new HotspotData[_numItems]; + + for (int32 i = 0; i < _numItems; i++) { + for (int32 a = 0; a < 256; a++) + _items[i].setData(a, Stream->readSint16BE()); + } +} + +void Hotspots::save(Common::WriteStream *Stream) { + + Stream->writeSint16BE(_numItems); + + for (int32 i = 0; i < _numItems; i++) { + for (int32 a = 0; a < 256; a++) + Stream->writeSint16BE(_items[i].getData(a)); + } +} + +int32 Hotspots::FindBasedOnCorner(int32 x, int32 y) { + debugC(1, kDebugHotspot, "FindBasedOnCorner(%d, %d)", x, y); + + for (int32 i = 0; i < _numItems; i++) { + if (x == _items[i].getX1()) { + if (y == _items[i].getY1()) { + if (_items[i].getMode() == -1) + return _items[i].getRef(); + + return i; + } + } + } + return -1; +} + +int32 Hotspots::Find(int32 x, int32 y) { + debugC(6, kDebugHotspot, "Find(%d, %d)", x, y); + + int32 priority = -1; +// Strangerke - Commented (not used) +// bool found = false; + int32 foundId = -1; + int32 testId = -1; + + for (int i = 0; i < _numItems; i++) { + if (x >= _items[i].getX1() && x <= _items[i].getX2() && y >= _items[i].getY1() && y <= _items[i].getY2()) { + if (_items[i].getMode() == -1) + testId = _items[i].getRef(); + else + testId = i; + + if (_items[testId].getPriority() > priority) { +// Strangerke - Commented (not used) +// found = true; + foundId = testId; + priority = _items[testId].getPriority(); + } + } + } + return foundId; +} + +bool Hotspots::LoadRif(Common::String rifName, Common::String additionalRifName) { + debugC(1, kDebugHotspot, "LoadRif(%s, %s)", rifName.c_str(), additionalRifName.c_str()); + + uint32 size = 0; + uint8 *rifData = _vm->resources()->getFileData(rifName, &size); + if (!rifData) + return false; + + uint32 size2 = 0; + uint8 *rifData2 = 0; + if (additionalRifName.size()) + rifData2 = _vm->resources()->getFileData(additionalRifName, &size2); + + // figure out the number of hotspots based on file size + int32 rifsize = READ_BE_UINT32(&rifData[4]); + int32 rifsize2 = 0; + + if (size2) + rifsize2 = READ_BE_UINT32(&rifData2[4]); + + _numItems = (rifsize + rifsize2) / 512; + + if (_items) + delete[] _items; + + _items = new HotspotData[_numItems]; + + // RIFs are compressed in RNC1 + RncDecoder decoder; + decoder.unpackM1(rifData, _items); + if (rifsize2) { + RncDecoder decoder2; + decoder2.unpackM1(rifData2 , _items + (rifsize >> 9)); + for (int32 i = 0; i < (rifsize2 >> 9); i++) { + HotspotData *hot = _items + (rifsize >> 9) + i; + hot->setData(0, hot->getX1() + 1280); + hot->setData(2, hot->getX2() + 1280); + if (hot->getMode() == -1) + hot->setData(5, hot->getRef() + (rifsize >> 9)); + } + } + + return true; +} + +HotspotData *Hotspots::Get(int32 id) { + debugC(5, kDebugHotspot, "Get(%d)", id); + + if (id < 0 || id >= _numItems) + return 0; + else + return &_items[id]; +} + +} // End of namespace Toon + diff --git a/engines/toon/hotspot.h b/engines/toon/hotspot.h new file mode 100644 index 0000000000..233bcebcb7 --- /dev/null +++ b/engines/toon/hotspot.h @@ -0,0 +1,73 @@ +/* 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. +* +* $URL$ +* $Id$ +* +*/ + +#ifndef TOON_HOTSPOT_H +#define TOON_HOTSPOT_H + +#include "toon/toon.h" +#include "toon/tools.h" + +namespace Toon { + +class HotspotData { +public: + int16 getX1() const { return READ_LE_INT16(_data + 0); } + int16 getY1() const { return READ_LE_INT16(_data + 1); } + int16 getX2() const { return READ_LE_INT16(_data + 2); } + int16 getY2() const { return READ_LE_INT16(_data + 3); } + int16 getMode() const { return READ_LE_INT16(_data + 4); } + int16 getRef() const { return READ_LE_INT16(_data + 5); } + int16 getPriority() const { return READ_LE_INT16(_data + 7); } + int16 getType() const { return READ_LE_INT16(_data + 8); } + int16 getData(int32 id) const { return READ_LE_INT16(_data + id); } + void setData(int32 id, int16 val) { WRITE_LE_UINT16(&_data[id], val); } + +private: + int16 _data[256]; +}; + +class Hotspots { +public: + Hotspots(ToonEngine *vm); + ~Hotspots(void); + + bool LoadRif(Common::String rifName, Common::String additionalRifName); + int32 Find(int32 x, int32 y); + int32 FindBasedOnCorner(int32 x, int32 y); + HotspotData *Get(int32 id); + int32 getCount() const { return _numItems; } + + void load(Common::ReadStream *Stream); + void save(Common::WriteStream *Stream); + +protected: + HotspotData *_items; + int32 _numItems; + ToonEngine *_vm; +}; + +} // End of namespace Toon + +#endif diff --git a/engines/toon/module.mk b/engines/toon/module.mk new file mode 100644 index 0000000000..403408e497 --- /dev/null +++ b/engines/toon/module.mk @@ -0,0 +1,30 @@ +MODULE := engines/toon + +MODULE_OBJS := \ + anim.o \ + audio.o \ + character.o \ + conversation.o \ + detection.o \ + drew.o \ + flux.o \ + font.o \ + hotspot.o \ + movie.o \ + path.o \ + picture.o \ + resource.o \ + script.o \ + script_func.o \ + state.o \ + text.o \ + tools.o \ + toon.o + +# This module can be built as a plugin +ifeq ($(ENABLE_TOON), DYNAMIC_PLUGIN) +PLUGIN := 1 +endif + +# Include common rules +include $(srcdir)/rules.mk diff --git a/engines/toon/movie.cpp b/engines/toon/movie.cpp new file mode 100644 index 0000000000..91ea98a91f --- /dev/null +++ b/engines/toon/movie.cpp @@ -0,0 +1,114 @@ +/* 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. +* +* $URL$ +* $Id$ +* +*/ + +#include "toon/movie.h" + +namespace Toon { + +void ToonstruckSmackerDecoder::handleAudioTrack(byte track, uint32 chunkSize, uint32 unpackedSize) { + debugC(6, kDebugMovie, "handleAudioTrack(%d, %d, %d)", track, chunkSize, unpackedSize); + + if (track == 1 && chunkSize == 4) { + /* uint16 width = */ _fileStream->readUint16LE(); + uint16 height = _fileStream->readUint16LE(); + + _header.flags = (height == getHeight() / 2) ? 4 : 0; + } else + Graphics::SmackerDecoder::handleAudioTrack(track, chunkSize, unpackedSize); +} + +bool ToonstruckSmackerDecoder::loadFile(const Common::String &filename, int forcedflags) { + debugC(1, kDebugMovie, "loadFile(%s, %d)", filename.c_str(), forcedflags); + + if (Graphics::SmackerDecoder::loadFile(filename)) { + if (forcedflags & 0x10 || _surface->h == 200) { + + _header.flags = 4; + delete this->_surface; + _surface = new Graphics::Surface(); + _surface->create(640, 400, 1); + } + return true; + } + return false; +} + +ToonstruckSmackerDecoder::ToonstruckSmackerDecoder(Audio::Mixer *mixer, Audio::Mixer::SoundType soundType) : Graphics::SmackerDecoder(mixer, soundType) { + +} + +Movie::Movie(ToonEngine *vm , ToonstruckSmackerDecoder *decoder) { + _vm = vm; + _playing = false; + _decoder = decoder; +} + +Movie::~Movie() { +} + +void Movie::init() const { +} + +void Movie::play(Common::String video, int32 flags) { + debugC(1, kDebugMovie, "play(%s, %d)", video.c_str(), flags); + + _playing = true; + if (flags & 1) + _vm->getAudioManager()->setMusicVolume(0); + _decoder->loadFile(video.c_str(), flags); + playVideo(); + _vm->flushPalette(); + if (flags & 1) + _vm->getAudioManager()->setMusicVolume(_vm->getAudioManager()->isMusicMuted() ? 0 : 255); + _decoder->close(); + _playing = false; +} + +bool Movie::playVideo() { + debugC(1, kDebugMovie, "playVideo()"); + + int32 x = 0; + int32 y = 0; + while (!_vm->shouldQuit() && !_decoder->endOfVideo()) { + if (_decoder->needsUpdate()) { + Graphics::Surface *frame = _decoder->decodeNextFrame(); + if (frame) + _vm->getSystem()->copyRectToScreen((byte *)frame->pixels, frame->pitch, x, y, frame->w, frame->h); + _decoder->setSystemPalette(); + _vm->getSystem()->updateScreen(); + } + + Common::Event event; + while (_vm->getSystem()->getEventManager()->pollEvent(event)) + if ((event.type == Common::EVENT_KEYDOWN && event.kbd.keycode == Common::KEYCODE_ESCAPE) || event.type == Common::EVENT_LBUTTONUP) { + return false; + } + + _vm->getSystem()->delayMillis(10); + } + return !_vm->shouldQuit(); +} + +} // End of namespace Toon diff --git a/engines/toon/movie.h b/engines/toon/movie.h new file mode 100644 index 0000000000..4d5efb3343 --- /dev/null +++ b/engines/toon/movie.h @@ -0,0 +1,61 @@ +/* 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. +* +* $URL$ +* $Id$ +* +*/ + +#ifndef TOON_MOVIE_H +#define TOON_MOVIE_H + +#include "toon/toon.h" +#include "graphics/video/smk_decoder.h" + +namespace Toon { + +class ToonstruckSmackerDecoder : public Graphics::SmackerDecoder { +public: + ToonstruckSmackerDecoder(Audio::Mixer *mixer, Audio::Mixer::SoundType soundType = Audio::Mixer::kSFXSoundType); + void handleAudioTrack(byte track, uint32 chunkSize, uint32 unpackedSize); + bool loadFile(const Common::String &filename, int forcedflags) ; +}; + +class Movie { +public: + Movie(ToonEngine *vm, ToonstruckSmackerDecoder *decoder); + ~Movie(void); + + void init() const; + void play(Common::String video, int32 flags = 0); + bool isPlaying() { return _playing; } + +protected: + bool playVideo(); + ToonEngine *_vm; + Audio::Mixer *_mixer; + ToonstruckSmackerDecoder *_decoder; + bool _playing; + +}; + +} // End of namespace Toon + +#endif diff --git a/engines/toon/path.cpp b/engines/toon/path.cpp new file mode 100644 index 0000000000..cec9c7dbf0 --- /dev/null +++ b/engines/toon/path.cpp @@ -0,0 +1,370 @@ +/* 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. +* +* $URL$ +* $Id$ +* +*/ + +#include "toon/path.h" + +namespace Toon { + +int32 PathFindingHeap::init(int32 size) { + debugC(1, kDebugPath, "init(%d)", size); + + _data = new HeapDataGrid[size * 2]; + memset(_data, 0, sizeof(HeapDataGrid) * size * 2); + _count = 0; + _alloc = size; + return size; +} + +int32 PathFindingHeap::unload() { + if (_data) + delete[] _data; + return 0; +} + +int32 PathFindingHeap::clear() { + //debugC(1, kDebugPath, "clear()"); + + _count = 0; + memset(_data, 0, sizeof(HeapDataGrid) * _alloc * 2); + return 1; +} + +int32 PathFindingHeap::push(int32 x, int32 y, int32 weight) { + //debugC(6, kDebugPath, "push(%d, %d, %d)", x, y, weight); + + _count++; + _data[_count]._x = x; + _data[_count]._y = y; + _data[_count]._weight = weight; + + int32 lMax = _count; + int32 lT = 0; + + while (1) { + lT = lMax / 2; + if (lT < 1) + break; + + if (_data[lT]._weight > _data[lMax]._weight) { + HeapDataGrid temp; + temp = _data[lT]; + _data[lT] = _data[lMax]; + _data[lMax] = temp; + lMax = lT; + } else { + break; + } + } + return 1; +} + +int32 PathFindingHeap::pop(int32 *x, int32 *y, int32 *weight) { + //debugC(6, kDebugPath, "pop(x, y, weight)"); + + if (!_count) + return 0; + + *x = _data[1]._x; + *y = _data[1]._y; + *weight = _data[1]._weight; + + _data[1] = _data[_count]; + _count--; + if (!_count) + return 0; + + int32 lMin = 1; + int32 lT = 1; + + while (1) { + lT = lMin << 1; + if (lT <= _count) { + if (lT < _count) { + if (_data[lT + 1]._weight < _data[lT]._weight) + lT++; + } + if (_data[lT]._weight <= _data[lMin]._weight) { + HeapDataGrid temp; + temp = _data[lMin]; + _data[lMin] = _data[lT]; + _data[lT] = temp; + + lMin = lT; + } else { + break; + } + } else { + break; + } + } + return 0; +} + +PathFinding::PathFinding(ToonEngine *vm) : _vm(vm) { + _width = 0; + _height = 0; + _heap = new PathFindingHeap(); + _gridTemp = 0; + _numBlockingRects = 0; +} + +PathFinding::~PathFinding(void) { + if (_heap) { + _heap->unload(); + delete _heap; + } +} + +bool PathFinding::isWalkable(int32 x, int32 y) { + //debugC(6, kDebugPath, "isWalkable(%d, %d)", x, y); + + bool maskWalk = (_currentMask->getData(x, y) & 0x1f) > 0; + for (int32 i = 0; i < _numBlockingRects; i++) { + if (_blockingRects[i][4] == 0) { + if (x >= _blockingRects[i][0] && x <= _blockingRects[i][2] && y >= _blockingRects[i][1] && y < _blockingRects[i][3]) + return false; + } else { + int32 dx = abs(_blockingRects[i][0] - x); + int32 dy = abs(_blockingRects[i][1] - y); + if ((dx << 8) / _blockingRects[i][2] < (1 << 8) && (dy << 8) / _blockingRects[i][3] < (1 << 8)) { + return false; + } + } + } + return maskWalk; +} + +int32 PathFinding::findClosestWalkingPoint(int32 xx, int32 yy, int32 *fxx, int32 *fyy, int origX, int origY) { + debugC(1, kDebugPath, "findClosestWalkingPoint(%d, %d, fxx, fyy, %d, %d)", xx, yy, origX, origY); + + int32 currentFound = -1; + int32 dist = -1; + int32 dist2 = -1; + + if (origX == -1) + origX = xx; + if (origY == -1) + origY = yy; + + for (int y = 0; y < _height; y++) { + for (int x = 0; x < _width; x++) { + if (isWalkable(x, y)) { + int32 ndist = (x - xx) * (x - xx) + (y - yy) * (y - yy); + int32 ndist2 = (x - origX) * (x - origX) + (y - origY) * (y - origY); + if (currentFound < 0 || ndist < dist || (ndist == dist && ndist2 < dist2)) { + dist = ndist; + dist2 = ndist2; + currentFound = y * _width + x; + } + } + } + } + + if (currentFound != -1) { + *fxx = currentFound % _width; + *fyy = currentFound / _width; + return 1; + } else { + *fxx = 0; + *fyy = 0; + return 0; + } +} + +int32 PathFinding::findPath(int32 x, int32 y, int32 destx, int32 desty) { + debugC(1, kDebugPath, "findPath(%d, %d, %d, %d)", x, y, destx, desty); + + if (x == destx && y == desty) { + _gridPathCount = 0; + return true; + } + + memset(_gridTemp , 0, _width * _height * sizeof(int32)); + _heap->clear(); + int32 curX = x; + int32 curY = y; + int32 curWeight = 0; + int32 *sq = _gridTemp; + + sq[curX + curY *_width] = 1; + _heap->push(curX, curY, abs(destx - x) + abs(desty - y)); + int wei = 0; + +// Strangerke - Commented (not used) +// byte *mask = _currentMask->getDataPtr(); + + while (_heap->_count) { + wei = 0; + _heap->pop(&curX, &curY, &curWeight); + int curNode = curX + curY * _width; + + int32 endX = MIN<int32>(curX + 1, _width - 1); + int32 endY = MIN<int32>(curY + 1, _height - 1); + int32 startX = MAX<int32>(curX - 1, 0); + int32 startY = MAX<int32>(curY - 1, 0); + + for (int32 px = startX; px <= endX; px++) { + for (int py = startY; py <= endY; py++) { + if (px != curX || py != curY) { + wei = abs(px - curX) + abs(py - curY); + + int32 curPNode = px + py * _width; + if (isWalkable(px, py)) { // walkable ? + int sum = sq[curNode] + wei; + if (sq[curPNode] > sum || !sq[curPNode]) { + int newWeight = abs(destx - px) + abs(desty - py); + sq[curPNode] = sum; + _heap->push(px, py, sq[curPNode] + newWeight); + if (!newWeight) + goto next; // we found it ! + } + } + } + } + } + } + +next: + + // let's see if we found a result ! + if (!_gridTemp[destx + desty * _width]) { + // didn't find anything + _gridPathCount = 0; + return false; + } + + curX = destx; + curY = desty; + + int32 retPathX[4096]; + int32 retPathY[4096]; + int32 numpath = 0; + + retPathX[numpath] = curX; + retPathY[numpath] = curY; + numpath++; + int32 bestscore = sq[destx + desty * _width]; + + while (1) { + int32 bestX = -1; + int32 bestY = -1; + + int32 endX = MIN<int32>(curX + 1, _width - 1); + int32 endY = MIN<int32>(curY + 1, _height - 1); + int32 startX = MAX<int32>(curX - 1, 0); + int32 startY = MAX<int32>(curY - 1, 0); + + for (int32 px = startX; px <= endX; px++) { + for (int32 py = startY; py <= endY; py++) { + if (px != curX || py != curY) { + wei = abs(px - curX) + abs(py - curY); + + int PNode = px + py * _width; + if (sq[PNode] && (isWalkable(px, py))) { + if (sq[PNode] < bestscore) { + bestscore = sq[PNode]; + bestX = px; + bestY = py; + } + } + } + } + } + + if (bestX < 0 || bestY < 0) + return 0; + + retPathX[numpath] = bestX; + retPathY[numpath] = bestY; + numpath++; + + if ((bestX == x && bestY == y)) { + _gridPathCount = numpath; + + memcpy(_tempPathX, retPathX, sizeof(int32) * numpath); + memcpy(_tempPathY, retPathY, sizeof(int32) * numpath); + return true; + } + + curX = bestX; + curY = bestY; + } + + return false; +} + +void PathFinding::init(Picture *mask) { + debugC(1, kDebugPath, "init(mask)"); + + _width = mask->getWidth(); + _height = mask->getHeight(); + _currentMask = mask; + _heap->unload(); + _heap->init(_width * _height); + if (_gridTemp) + delete[] _gridTemp; + _gridTemp = new int32[_width*_height]; +} + +void PathFinding::resetBlockingRects() { + _numBlockingRects = 0; +} + +void PathFinding::addBlockingRect(int32 x1, int32 y1, int32 x2, int32 y2) { + debugC(1, kDebugPath, "addBlockingRect(%d, %d, %d, %d)", x1, y1, x2, y2); + + _blockingRects[_numBlockingRects][0] = x1; + _blockingRects[_numBlockingRects][1] = y1; + _blockingRects[_numBlockingRects][2] = x2; + _blockingRects[_numBlockingRects][3] = y2; + _blockingRects[_numBlockingRects][4] = 0; + _numBlockingRects++; +} + +void PathFinding::addBlockingEllipse(int32 x1, int32 y1, int32 w, int32 h) { + debugC(1, kDebugPath, "addBlockingRect(%d, %d, %d, %d)", x1, y1, w, h); + + _blockingRects[_numBlockingRects][0] = x1; + _blockingRects[_numBlockingRects][1] = y1; + _blockingRects[_numBlockingRects][2] = w; + _blockingRects[_numBlockingRects][3] = h; + _blockingRects[_numBlockingRects][4] = 1; + _numBlockingRects++; +} + + +int32 PathFinding::getPathNodeCount() const { + return _gridPathCount; +} + +int32 PathFinding::getPathNodeX(int32 nodeId) const { + return _tempPathX[ _gridPathCount - nodeId - 1]; +} + +int32 PathFinding::getPathNodeY(int32 nodeId) const { + return _tempPathY[ _gridPathCount - nodeId - 1]; +} + +} // End of namespace Toon diff --git a/engines/toon/path.h b/engines/toon/path.h new file mode 100644 index 0000000000..d8ef2eac02 --- /dev/null +++ b/engines/toon/path.h @@ -0,0 +1,93 @@ +/* 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. +* +* $URL$ +* $Id$ +* +*/ + +#ifndef TOON_PATH_H +#define TOON_PATH_H + +#include "toon/toon.h" + +namespace Toon { + +// binary heap system for fast A* +struct HeapDataGrid { + int16 _x, _y; + int16 _weight; +}; + +class PathFindingHeap { + +private: + HeapDataGrid *_data; +public: + int32 _alloc; + int32 _count; + int32 push(int32 x, int32 y, int32 weight); + int32 pop(int32 *x, int32 *y, int32 *weight); + int32 init(int32 size); + int32 clear(); + int32 unload(); +}; + + +class PathFinding { +public: + PathFinding(ToonEngine *vm); + ~PathFinding(void); + + int32 findPath(int32 x, int32 y, int32 destX, int32 destY); + int32 findClosestWalkingPoint(int32 xx, int32 yy, int32 *fxx, int32 *fyy, int origX = -1, int origY = -1); + bool isWalkable(int32 x, int32 y); + void init(Picture *mask); + + void resetBlockingRects(); + void addBlockingRect(int32 x1, int32 y1, int32 x2, int32 y2); + void addBlockingEllipse(int32 x1, int32 y1, int32 w, int32 h); + + int32 getPathNodeCount() const; + int32 getPathNodeX(int32 nodeId) const; + int32 getPathNodeY(int32 nodeId) const; +protected: + Picture *_currentMask; + + PathFindingHeap *_heap; + + int32 *_gridTemp; + int32 _width; + int32 _height; + + int32 _tempPathX[4096]; + int32 _tempPathY[4096]; + int32 _blockingRects[16][5]; + int32 _numBlockingRects; + int32 _allocatedGridPathCount; + int32 _gridPathCount; + + ToonEngine *_vm; + +}; + +} // End of namespace Toon + +#endif diff --git a/engines/toon/picture.cpp b/engines/toon/picture.cpp new file mode 100644 index 0000000000..11a5572066 --- /dev/null +++ b/engines/toon/picture.cpp @@ -0,0 +1,296 @@ +/* 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. +* +* $URL$ +* $Id$ +* +*/ + +#include "toon/picture.h" +#include "toon/tools.h" +#include "common/stack.h" + +namespace Toon { + +bool Picture::loadPicture(Common::String file, bool totalPalette /*= false*/) { + debugC(1, kDebugPicture, "loadPicture(%s, %d)", file.c_str(), (totalPalette) ? 1 : 0); + + uint32 size = 0; + uint8 *fileData = _vm->resources()->getFileData(file, &size); + if (!fileData) + return false; + + _useFullPalette = totalPalette; + + uint32 compId = READ_BE_UINT32(fileData); + + switch (compId) { + case kCompLZSS: { + uint32 dstsize = READ_LE_UINT32(fileData + 4); + _data = new uint8[dstsize]; + decompressLZSS(fileData + 8, _data, dstsize); + + // size can only be 640x400 or 1280x400 + if (dstsize > 640 * 400 + 768) + _width = 1280; + else + _width = 640; + + _height = 400; + + // do we have a palette ? + _paletteEntries = (dstsize & 0x7ff) / 3; + if (_paletteEntries) { + _palette = new uint8[_paletteEntries * 3]; + memcpy(_palette, _data + dstsize - (dstsize & 0x7ff), _paletteEntries * 3); + _vm->fixPaletteEntries(_palette, _paletteEntries); + } else { + _palette = 0; + } + return true; + } + case kCompSPCN: { + uint32 decSize = READ_LE_UINT32(fileData + 10); + _data = new uint8[decSize+100]; + _paletteEntries = READ_LE_UINT16(fileData + 14) / 3; + + if (_paletteEntries) { + _palette = new uint8[_paletteEntries * 3]; + memcpy(_palette, fileData + 16, _paletteEntries * 3); + _vm->fixPaletteEntries(_palette, _paletteEntries); + } + + // size can only be 640x400 or 1280x400 + if (decSize > 640 * 400 + 768) + _width = 1280; + else + _width = 640; + + _height = 400; + + // decompress the picture into our buffer + decompressSPCN(fileData + 16 + _paletteEntries * 3, _data, decSize); + return true; + } + case kCompRNC1: { + Toon::RncDecoder rnc; + + // allocate enough place + uint32 decSize = READ_BE_UINT32(fileData + 4); + + _data = new uint8[decSize]; + rnc.unpackM1(fileData, _data); + + // size can only be 640x400 or 1280x400 + if (decSize > 640 * 400 + 768) + _width = 1280; + else + _width = 640; + + _height = 400; + return true; + } + case kCompRNC2: { + Toon::RncDecoder rnc; + + // allocate enough place + uint32 decSize = READ_BE_UINT32(fileData + 4); + + _data = new uint8[decSize]; + + decSize = rnc.unpackM2(fileData, _data); + + if (decSize > 640 * 400 + 768) + _width = 1280; + else + _width = 640; + + _height = 400; + return true; + } + } + return false; +} + +Picture::Picture(ToonEngine *vm) : _vm(vm) { + +} + +void Picture::setupPalette() { + debugC(1, kDebugPicture, "setupPalette()"); + + if (_useFullPalette) + _vm->setPaletteEntries(_palette, 0, 256); + else + _vm->setPaletteEntries(_palette, 1, 128); +} + +void Picture::drawMask(Graphics::Surface &surface, int32 x, int32 y, int32 dx, int32 dy) { + debugC(1, kDebugPicture, "drawMask(surface, %d, %d, %d, %d)", x, y, dx, dy); + + for (int32 i = 0; i < 128; i++) { + byte color[3]; + color[0] = i * 2; + color[1] = i * 2; + color[2] = 255 - i * 2; + _vm->setPaletteEntries(color, i, 1); + } + + int32 rx = MIN(_width, surface.w - x); + int32 ry = MIN(_height, surface.h - y); + + if (rx < 0 || ry < 0) + return; + + int32 destPitch = surface.pitch; + int32 srcPitch = _width; + uint8 *c = _data + _width * dy + dx; + uint8 *curRow = (uint8 *)surface.pixels + y * destPitch + x; + + for (int32 yy = 0; yy < ry; yy++) { + uint8 *curSrc = c; + uint8 *cur = curRow; + for (int32 xx = 0; xx < rx; xx++) { + //*cur = (*curSrc >> 5) * 8; // & 0x1f; + *cur = (*curSrc & 0x1f) ? 127 : 0; + + curSrc++; + cur++; + } + curRow += destPitch; + c += srcPitch; + } +} + +void Picture::draw(Graphics::Surface &surface, int32 x, int32 y, int32 dx, int32 dy) { + debugC(6, kDebugPicture, "draw(surface, %d, %d, %d, %d)", x, y, dx, dy); + + int32 rx = MIN(_width, surface.w - x); + int32 ry = MIN(_height, surface.h - y); + + if (rx < 0 || ry < 0) + return; + + int32 destPitch = surface.pitch; + int32 srcPitch = _width; + uint8 *c = _data + _width * dy + dx; + uint8 *curRow = (uint8 *)surface.pixels + y * destPitch + x; + + for (int32 yy = 0; yy < ry; yy++) { + uint8 *curSrc = c; + uint8 *cur = curRow; + for (int32 xx = 0; xx < rx; xx++) { + *cur = *curSrc; + curSrc++; + cur++; + } + curRow += destPitch; + c += srcPitch; + } +} + +uint8 Picture::getData(int32 x, int32 y) { + debugC(6, kDebugPicture, "getData(%d, %d)", x, y); + + if (!_data) + return 0; + + return _data[y * _width + x]; +} + +// use original work from johndoe +void Picture::floodFillNotWalkableOnMask(int32 x, int32 y) { + debugC(1, kDebugPicture, "floodFillNotWalkableOnMask(%d, %d)", x, y); + + // Stack-based floodFill algorithm based on + // http://student.kuleuven.be/~m0216922/CG/files/floodfill.cpp + Common::Stack<Common::Point> stack; + bool spanLeft, spanRight; + stack.push(Common::Point(x, y)); + while (!stack.empty()) { + Common::Point pt = stack.pop(); + while (_data[pt.x + pt.y * _width] & 0x1F && pt.y >= 0) + pt.y--; + pt.y++; + spanLeft = false; + spanRight = false; + while (_data[pt.x + pt.y * _width] & 0x1F && pt.y < _height) { + _data[pt.x + pt.y * _width] &= 0xE0; + if (!spanLeft && pt.x > 0 && _data[pt.x - 1 + pt.y * _width] & 0x1F) { + stack.push(Common::Point(pt.x - 1, pt.y)); + spanLeft = 1; + } else if (spanLeft && pt.x > 0 && !(_data[pt.x - 1 + pt.y * _width] & 0x1F)) { + spanLeft = 0; + } + if (!spanRight && pt.x < _width - 1 && _data[pt.x + 1 + pt.y * _width] & 0x1F) { + stack.push(Common::Point(pt.x + 1, pt.y)); + spanRight = 1; + } else if (spanRight && pt.x < _width - 1 && !(_data[pt.x + 1 + pt.y * _width] & 0x1F)) { + spanRight = 0; + } + pt.y++; + } + } +} + +void Picture::drawLineOnMask(int32 x, int32 y, int32 x2, int32 y2, bool walkable) { + debugC(1, kDebugPicture, "drawLineOnMask(%d, %d, %d, %d, %d)", x, y, x2, y2, (walkable) ? 1 : 0); + + static int32 lastX = 0; + static int32 lastY = 0; + + if (x == -1) { + x = lastX; + y = lastY; + } + + uint32 bx = x << 16; + int32 dx = x2 - x; + uint32 by = y << 16; + int32 dy = y2 - y; + uint32 adx = abs(dx); + uint32 ady = abs(dy); + int32 t = 0; + if (adx <= ady) + t = ady; + else + t = adx; + + + int32 cdx = (dx << 16) / t; + int32 cdy = (dy << 16) / t; + + int32 i = t; + while (i) { + if (!walkable) { + _data[_width * (by >> 16) + (bx >> 16)] &= 0xe0; + _data[_width * (by >> 16) + (bx >> 16)+1] &= 0xe0; + } else { + int32 v = _data[_width * (by >> 16) + (bx >> 16) - 1]; + _data[_width * (by >> 16) + (bx >> 16)] = v; + _data[_width * (by >> 16) + (bx >> 16)+1] = v; + } + + bx += cdx; + by += cdy; + i--; + } +} +} // End of namespace Toon diff --git a/engines/toon/picture.h b/engines/toon/picture.h new file mode 100644 index 0000000000..5065843b3c --- /dev/null +++ b/engines/toon/picture.h @@ -0,0 +1,66 @@ +/* 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. +* +* $URL$ +* $Id$ +* +*/ + +#ifndef TOON_PICTURE_H +#define TOON_PICTURE_H + +#include "common/stream.h" +#include "common/array.h" +#include "common/func.h" +#include "common/str.h" + +#include "toon/toon.h" + +namespace Toon { + +class ToonEngine; +class Picture { + +public: + Picture(ToonEngine *vm); + bool loadPicture(Common::String file, bool totalPalette = false); + void setupPalette(); + void draw(Graphics::Surface &surface, int32 x, int32 y, int32 dx, int32 dy); + void drawMask(Graphics::Surface &surface, int32 x, int32 y, int32 dx, int32 dy); + void drawLineOnMask(int32 x, int32 y, int32 x2, int32 y2, bool walkable); + void floodFillNotWalkableOnMask(int32 x, int32 y); + uint8 getData(int32 x, int32 y); + uint8 *getDataPtr() { return _data; } + int32 getWidth() const { return _width; } + int32 getHeight() const { return _height; } +protected: + int32 _width; + int32 _height; + uint8 *_data; + uint8 *_palette; // need to be copied at 3-387 + int32 _paletteEntries; + bool _useFullPalette; + + ToonEngine *_vm; +}; + +} // End of namespace Toon + +#endif diff --git a/engines/toon/resource.cpp b/engines/toon/resource.cpp new file mode 100644 index 0000000000..348aa45ae9 --- /dev/null +++ b/engines/toon/resource.cpp @@ -0,0 +1,215 @@ +/* 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. +* +* $URL$ +* $Id$ +* +*/ + +#include "toon/resource.h" +#include "common/file.h" +#include "toon/toon.h" + + +namespace Toon { + +void Resources::openPackage(Common::String fileName, bool preloadEntirePackage) { + debugC(1, kDebugResource, "openPackage(%s, %d)", fileName.c_str(), (preloadEntirePackage) ? 1 : 0); + + Common::File file; + bool opened = file.open(fileName); + + if (!opened) + return; + + + PakFile *pakFile = new PakFile(); + pakFile->open(&file, fileName, preloadEntirePackage); + + if (preloadEntirePackage) + file.close(); + + _pakFiles.push_back(pakFile); +} + +void Resources::closePackage(Common::String fileName) { + for (uint32 i = 0; i < _pakFiles.size(); i++) { + if (_pakFiles[i]->getPackName() == fileName) { + delete _pakFiles[i]; + _pakFiles.remove_at(i); + return; + } + } +} + +Resources::Resources(ToonEngine *vm) : _vm(vm) { + +} + +uint8 *Resources::getFileData(Common::String fileName, uint32 *fileSize) { + debugC(4, kDebugResource, "getFileData(%s, fileSize)", fileName.c_str()); + + // first try to find files outside of .pak + // some patched files have not been included in package. + if (Common::File::exists(fileName)) { + Common::File file; + bool opened = file.open(fileName); + if (!opened) + return 0; + + *fileSize = file.size(); + uint8 *memory = (uint8 *)new uint8[*fileSize]; + file.read(memory, *fileSize); + file.close(); + return memory; + } else { + for (uint32 i = 0; i < _pakFiles.size(); i++) { + uint32 locFileSize = 0; + uint8 *locFileData = 0; + + locFileData = _pakFiles[i]->getFileData(fileName, &locFileSize); + if (locFileData) { + *fileSize = locFileSize; + return locFileData; + } + } + return 0; + } +} + +Common::SeekableReadStream *Resources::openFile(Common::String fileName) { + debugC(1, kDebugResource, "openFile(%s)", fileName.c_str()); + + // first try to find files outside of .pak + // some patched files have not been included in package. + if (Common::File::exists(fileName)) { + Common::File *file = new Common::File(); + bool opened = file->open(fileName); + if (!opened) { + delete file; + return 0; + } + return file; + } else { + for (uint32 i = 0; i < _pakFiles.size(); i++) { + Common::SeekableReadStream *stream = 0; + stream = _pakFiles[i]->createReadStream(fileName); + if (stream) + return stream; + } + + return 0; + } +} +Common::SeekableReadStream *PakFile::createReadStream(Common::String fileName) { + debugC(1, kDebugResource, "createReadStream(%s)", fileName.c_str()); + + int32 offset = 0; + int32 size = 0; + for (uint32 i = 0; i < _numFiles; i++) { + if (fileName.compareToIgnoreCase(_files[i]._name) == 0) { + size = _files[i]._size; + offset = _files[i]._offset; + break; + } + } + if (!size) + return 0; + + if (_fileHandle) + return new Common::SeekableSubReadStream(_fileHandle, offset, offset + size); + else + return new Common::MemoryReadStream(_buffer + offset, size); +} + +uint8 *PakFile::getFileData(Common::String fileName, uint32 *fileSize) { + debugC(4, kDebugResource, "getFileData(%s, fileSize)", fileName.c_str()); + + for (uint32 i = 0; i < _numFiles; i++) { + if (fileName.compareToIgnoreCase(_files[i]._name) == 0) { + *fileSize = _files[i]._size; + return _buffer + _files[i]._offset; + } + } + + return 0; +} + +void PakFile::open(Common::SeekableReadStream *rs, Common::String packName, bool preloadEntirePackage) { + debugC(1, kDebugResource, "open(rs, %d)", (preloadEntirePackage) ? 1 : 0); + + char buffer[64]; + int32 currentPos = 0; + _numFiles = 0; + _packName = packName; + + while (1) { + rs->seek(currentPos); + rs->read(buffer, 64); + + int32 offset = READ_LE_UINT32(buffer); + char *name = buffer + 4; + + if (!*name) + break; + + int32 nameSize = strlen(name) + 1; + int32 nextOffset = READ_LE_UINT32(buffer + 4 + nameSize); + currentPos += 4 + nameSize; + + PakFile::File newFile; + strcpy(newFile._name, name); + newFile._offset = offset; + newFile._size = nextOffset - offset; + _numFiles++; + _files.push_back(newFile); + } + + if (preloadEntirePackage) { + _bufferSize = rs->size(); + _buffer = new uint8[_bufferSize]; + rs->seek(0); + rs->read(_buffer, _bufferSize); + } +} + +void PakFile::close() { + if (_buffer) { + delete[] _buffer; + } + + if (_fileHandle) { + _fileHandle->close(); + delete _fileHandle; + } +} + +PakFile::~PakFile() { + close(); +} + + +PakFile::PakFile() { + _fileHandle = 0; + _buffer = 0; + _bufferSize = 0; +} + +} // End of namespace Toon diff --git a/engines/toon/resource.h b/engines/toon/resource.h new file mode 100644 index 0000000000..3a080fe894 --- /dev/null +++ b/engines/toon/resource.h @@ -0,0 +1,82 @@ +/* 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. +* +* $URL$ +* $Id$ +* +*/ + +#ifndef TOON_RESOURCE_H +#define TOON_RESOURCE_H + +#include "common/array.h" +#include "common/str.h" +#include "common/file.h" +#include "common/stream.h" + +namespace Toon { + +class PakFile { +public: + PakFile(); + ~PakFile(); + + void open(Common::SeekableReadStream *rs, Common::String packName, bool preloadEntirePackage); + uint8 *getFileData(Common::String fileName, uint32 *fileSize); + Common::String getPackName() { return _packName; } + Common::SeekableReadStream *createReadStream(Common::String fileName); + void close(); + +protected: + struct File { + char _name[13]; + int32 _offset; + int32 _size; + }; + Common::String _packName; + + uint8 *_buffer; + int32 _bufferSize; + + uint32 _numFiles; + Common::Array<File> _files; + Common::File *_fileHandle; + + +}; + +class ToonEngine; + +class Resources { +public: + Resources(ToonEngine *vm); + void openPackage(Common::String file, bool preloadEntirePackage); + void closePackage(Common::String fileName); + Common::SeekableReadStream *openFile(Common::String file); + uint8 *getFileData(Common::String fileName, uint32 *fileSize); + +protected: + ToonEngine *_vm; + Common::Array<PakFile *> _pakFiles; + +}; + +} // End of namespace Toon +#endif diff --git a/engines/toon/script.cpp b/engines/toon/script.cpp new file mode 100644 index 0000000000..31d9f94f36 --- /dev/null +++ b/engines/toon/script.cpp @@ -0,0 +1,504 @@ +/* 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. +* +* $URL$ +* $Id$ +* +*/ + + +#include "common/endian.h" +#include "common/stream.h" +#include "common/util.h" +#include "common/system.h" + +#include "toon/toon.h" +#include "toon/script.h" + +namespace Toon { +EMCInterpreter::EMCInterpreter(ToonEngine *vm) : _vm(vm), _scriptData(0), _filename(0) { + +#define OPCODE(x) { &EMCInterpreter::x, #x } + static const OpcodeEntry opcodes[] = { + // 0x00 + OPCODE(op_jmp), + OPCODE(op_setRetValue), + OPCODE(op_pushRetOrPos), + OPCODE(op_push), + // 0x04 + OPCODE(op_push), + OPCODE(op_pushReg), + OPCODE(op_pushBPNeg), + OPCODE(op_pushBPAdd), + // 0x08 + OPCODE(op_popRetOrPos), + OPCODE(op_popReg), + OPCODE(op_popBPNeg), + OPCODE(op_popBPAdd), + // 0x0C + OPCODE(op_addSP), + OPCODE(op_subSP), + OPCODE(op_sysCall), + OPCODE(op_ifNotJmp), + // 0x10 + OPCODE(op_negate), + OPCODE(op_eval), + OPCODE(op_setRetAndJmp) + }; + _opcodes = opcodes; +#undef OPCODE +} + +bool EMCInterpreter::callback(Common::IFFChunk &chunk) { + switch (chunk._type) { + case MKID_BE('TEXT'): + _scriptData->text = new byte[chunk._size]; + assert(_scriptData->text); + if (chunk._stream->read(_scriptData->text, chunk._size) != chunk._size) + error("Couldn't read TEXT chunk from file '%s'", _filename); + break; + + case MKID_BE('ORDR'): + _scriptData->ordr = new uint16[chunk._size >> 1]; + assert(_scriptData->ordr); + if (chunk._stream->read(_scriptData->ordr, chunk._size) != chunk._size) + error("Couldn't read ORDR chunk from file '%s'", _filename); + + for (int i = (chunk._size >> 1) - 1; i >= 0; --i) + _scriptData->ordr[i] = READ_BE_UINT16(&_scriptData->ordr[i]); + break; + + case MKID_BE('DATA'): + _scriptData->data = new uint16[chunk._size >> 1]; + assert(_scriptData->data); + if (chunk._stream->read(_scriptData->data, chunk._size) != chunk._size) + error("Couldn't read DATA chunk from file '%s'", _filename); + + for (int i = (chunk._size >> 1) - 1; i >= 0; --i) + _scriptData->data[i] = READ_BE_UINT16(&_scriptData->data[i]); + break; + + default: + warning("Unexpected chunk '%s' of size %d found in file '%s'", Common::tag2string(chunk._type).c_str(), chunk._size, _filename); + } + + return false; +} + +bool EMCInterpreter::load(const char *filename, EMCData *scriptData, const Common::Array<const OpcodeV2 *> *opcodes) { + Common::SeekableReadStream *stream = _vm->resources()->openFile(filename); + if (!stream) { + error("Couldn't open script file '%s'", filename); + return false; // for compilers that don't support NORETURN + } + + memset(scriptData, 0, sizeof(EMCData)); + + _scriptData = scriptData; + _filename = filename; + + IFFParser iff(*stream); + Common::Functor1Mem< Common::IFFChunk &, bool, EMCInterpreter > c(this, &EMCInterpreter::callback); + iff.parse(c); + + if (!_scriptData->ordr) + error("No ORDR chunk found in file: '%s'", filename); + + if (!_scriptData->data) + error("No DATA chunk found in file: '%s'", filename); + + if (stream->err()) + error("Read error while parsing file '%s'", filename); + + delete stream; + + _scriptData->sysFuncs = opcodes; + + strncpy(_scriptData->filename, filename, 13); + _scriptData->filename[12] = 0; + + _scriptData = 0; + _filename = 0; + + return true; +} + +void EMCInterpreter::unload(EMCData *data) { + if (!data) + return; + + delete[] data->text; + delete[] data->ordr; + delete[] data->data; + + data->text = 0; + data->ordr = data->data = 0; +} + +void EMCInterpreter::init(EMCState *scriptStat, const EMCData *data) { + scriptStat->dataPtr = data; + scriptStat->ip = 0; + scriptStat->stack[EMCState::kStackLastEntry] = 0; + scriptStat->bp = EMCState::kStackSize + 1; + scriptStat->sp = EMCState::kStackLastEntry; + scriptStat->running = false; +} + +bool EMCInterpreter::start(EMCState *script, int function) { + if (!script->dataPtr) + return false; + + uint16 functionOffset = script->dataPtr->ordr[function]; + if (functionOffset == 0xFFFF) + return false; + + script->ip = &script->dataPtr->data[functionOffset+1]; + + return true; +} + +bool EMCInterpreter::isValid(EMCState *script) { + if (!script->ip || !script->dataPtr || _vm->shouldQuitGame()) + return false; + return true; +} + +bool EMCInterpreter::run(EMCState *script) { + + if (script->running) + return false; + + _parameter = 0; + + if (!script->ip) + return false; + + script->running = true; + + + // Should be no Problem at all to cast to uint32 here, since that's the biggest ptrdiff the original + // would allow, of course that's not realistic to happen to be somewhere near the limit of uint32 anyway. + const uint32 instOffset = (uint32)((const byte *)script->ip - (const byte *)script->dataPtr->data); + int16 code = *script->ip++; + int16 opcode = (code >> 8) & 0x1F; + + if (code & 0x8000) { + opcode = 0; + _parameter = code & 0x7FFF; + } else if (code & 0x4000) { + _parameter = (int8)(code); + } else if (code & 0x2000) { + _parameter = *script->ip++; + } else { + _parameter = 0; + } + + if (opcode > 18) { + error("Unknown script opcode: %d in file '%s' at offset 0x%.08X", opcode, script->dataPtr->filename, instOffset); + } else { + static bool EMCDebug = false; + if (EMCDebug) + debugC(5, 0, "[0x%.08X] EMCInterpreter::%s([%d/%u])", instOffset * 2, _opcodes[opcode].desc, _parameter, (uint)_parameter); + //debug(0, "[0x%.08X] EMCInterpreter::%s([%d/%u])", instOffset, _opcodes[opcode].desc, _parameter, (uint)_parameter); + + (this->*(_opcodes[opcode].proc))(script); + } + + script->running = false; + return (script->ip != 0); +} + +#pragma mark - +#pragma mark - Command implementations +#pragma mark - + +void EMCInterpreter::op_jmp(EMCState *script) { + script->ip = script->dataPtr->data + _parameter; +} + +void EMCInterpreter::op_setRetValue(EMCState *script) { + script->retValue = _parameter; +} + +void EMCInterpreter::op_pushRetOrPos(EMCState *script) { + switch (_parameter) { + case 0: + script->stack[--script->sp] = script->retValue; + break; + + case 1: + script->stack[--script->sp] = script->ip - script->dataPtr->data + 1; + script->stack[--script->sp] = script->bp; + script->bp = script->sp + 2; + break; + + default: + script->ip = 0; + } +} + +void EMCInterpreter::op_push(EMCState *script) { + script->stack[--script->sp] = _parameter; +} + +void EMCInterpreter::op_pushReg(EMCState *script) { + script->stack[--script->sp] = script->regs[_parameter]; +} + +void EMCInterpreter::op_pushBPNeg(EMCState *script) { + script->stack[--script->sp] = script->stack[(-(int32)(_parameter + 2)) + script->bp]; +} + +void EMCInterpreter::op_pushBPAdd(EMCState *script) { + script->stack[--script->sp] = script->stack[(_parameter - 1) + script->bp]; +} + +void EMCInterpreter::op_popRetOrPos(EMCState *script) { + switch (_parameter) { + case 0: + script->retValue = script->stack[script->sp++]; + break; + + case 1: + if (script->sp >= EMCState::kStackLastEntry) { + script->ip = 0; + } else { + script->bp = script->stack[script->sp++]; + script->ip = script->dataPtr->data + script->stack[script->sp++]; + } + break; + + default: + script->ip = 0; + } +} + +void EMCInterpreter::op_popReg(EMCState *script) { + script->regs[_parameter] = script->stack[script->sp++]; +} + +void EMCInterpreter::op_popBPNeg(EMCState *script) { + script->stack[(-(int32)(_parameter + 2)) + script->bp] = script->stack[script->sp++]; +} + +void EMCInterpreter::op_popBPAdd(EMCState *script) { + script->stack[(_parameter - 1) + script->bp] = script->stack[script->sp++]; +} + +void EMCInterpreter::op_addSP(EMCState *script) { + script->sp += _parameter; +} + +void EMCInterpreter::op_subSP(EMCState *script) { + script->sp -= _parameter; +} + +void EMCInterpreter::op_sysCall(EMCState *script) { + const uint8 id = _parameter; + + assert(script->dataPtr->sysFuncs); + assert(id < script->dataPtr->sysFuncs->size()); + + if ((*script->dataPtr->sysFuncs)[id] && ((*script->dataPtr->sysFuncs)[id])->isValid()) { + script->retValue = (*(*script->dataPtr->sysFuncs)[id])(script); + } else { + script->retValue = 0; + warning("Unimplemented system call 0x%.02X/%d used in file '%s'", id, id, script->dataPtr->filename); + } +} + +void EMCInterpreter::op_ifNotJmp(EMCState *script) { + if (!script->stack[script->sp++]) { + _parameter &= 0x7FFF; + script->ip = script->dataPtr->data + _parameter; + } +} + +void EMCInterpreter::op_negate(EMCState *script) { + int16 value = script->stack[script->sp]; + switch (_parameter) { + case 0: + if (!value) + script->stack[script->sp] = 1; + else + script->stack[script->sp] = 0; + break; + + case 1: + script->stack[script->sp] = -value; + break; + + case 2: + script->stack[script->sp] = ~value; + break; + + default: + warning("Unknown negation func: %d", _parameter); + script->ip = 0; + } +} + +void EMCInterpreter::op_eval(EMCState *script) { + int16 ret = 0; + bool error = false; + + int16 val1 = script->stack[script->sp++]; + int16 val2 = script->stack[script->sp++]; + + switch (_parameter) { + case 0: + ret = (val2 && val1) ? 1 : 0; + break; + + case 1: + ret = (val2 || val1) ? 1 : 0; + break; + + case 2: + ret = (val1 == val2) ? 1 : 0; + break; + + case 3: + ret = (val1 != val2) ? 1 : 0; + break; + + case 4: + ret = (val1 > val2) ? 1 : 0; + break; + + case 5: + ret = (val1 >= val2) ? 1 : 0; + break; + + case 6: + ret = (val1 < val2) ? 1 : 0; + break; + + case 7: + ret = (val1 <= val2) ? 1 : 0; + break; + + case 8: + ret = val1 + val2; + break; + + case 9: + ret = val2 - val1; + break; + + case 10: + ret = val1 * val2; + break; + + case 11: + ret = val2 / val1; + break; + + case 12: + ret = val2 >> val1; + break; + + case 13: + ret = val2 << val1; + break; + + case 14: + ret = val1 & val2; + break; + + case 15: + ret = val1 | val2; + break; + + case 16: + ret = val2 % val1; + break; + + case 17: + ret = val1 ^ val2; + break; + + default: + warning("Unknown evaluate func: %d", _parameter); + error = true; + } + + if (error) + script->ip = 0; + else + script->stack[--script->sp] = ret; +} + +void EMCInterpreter::op_setRetAndJmp(EMCState *script) { + if (script->sp >= EMCState::kStackLastEntry) { + script->ip = 0; + } else { + script->retValue = script->stack[script->sp++]; + uint16 temp = script->stack[script->sp++]; + script->stack[EMCState::kStackLastEntry] = 0; + script->ip = &script->dataPtr->data[temp]; + } +} + +void EMCInterpreter::saveState(EMCState *script, Common::WriteStream *stream) { + stream->writeSint16LE(script->bp); + stream->writeSint16LE(script->sp); + if (!script->ip) { + stream->writeSint16LE(-1); + } else { + stream->writeSint16LE(script->ip - script->dataPtr->data); + } + + for (int32 i = 0; i < EMCState::kStackSize; i++) { + stream->writeSint16LE(script->stack[i]); + } + + for (int32 i = 0; i < 30; i++) { + stream->writeSint16LE(script->regs[i]); + } + + stream->writeSint16LE(script->retValue); + stream->writeByte(script->running); +} +void EMCInterpreter::loadState(EMCState *script, Common::ReadStream *stream) { + script->bp = stream->readSint16LE(); + script->sp = stream->readSint16LE(); + + int16 scriptIp = stream->readSint16LE(); + if (scriptIp == -1) { + script->ip = 0; + } else { + script->ip = scriptIp + script->dataPtr->data; + } + + for (int32 i = 0; i < EMCState::kStackSize; i++) { + script->stack[i] = stream->readSint16LE(); + } + + for (int32 i = 0; i < 30; i++) { + script->regs[i] = stream->readSint16LE(); + } + + script->retValue = stream->readSint16LE(); + script->running = stream->readByte(); +} + +} // End of namespace Toon + diff --git a/engines/toon/script.h b/engines/toon/script.h new file mode 100644 index 0000000000..d7f45096c2 --- /dev/null +++ b/engines/toon/script.h @@ -0,0 +1,153 @@ +/* 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. +* +* $URL$ +* $Id$ +* +*/ + +#ifndef TOON_SCRIPT_H +#define TOON_SCRIPT_H + +#include "common/stream.h" +#include "common/array.h" +#include "common/func.h" +#include "common/iff_container.h" + + +// Based on Kyra script interpretor +namespace Toon { + +struct EMCState; +class ScriptFunc; +typedef Common::Functor1Mem<EMCState *, int32, ScriptFunc> OpcodeV2; + +struct EMCData { + char filename[13]; + + byte *text; + uint16 *data; + uint16 *ordr; + uint16 dataSize; + + const Common::Array<const OpcodeV2 *> *sysFuncs; +}; + +struct EMCState { + enum { + kStackSize = 100, + kStackLastEntry = kStackSize - 1 + }; + + const uint16 *ip; + const EMCData *dataPtr; + int16 retValue; + uint16 bp; + uint16 sp; + int16 regs[30]; // VM registers + int16 stack[kStackSize]; // VM stack + bool running; +}; + +#define stackPos(x) (state->stack[state->sp+x]) +#define stackPosString(x) ((const char *)&state->dataPtr->text[READ_BE_UINT16(&state->dataPtr->text[stackPos(x)<<1])]) + +class Resource; +class ToonEngine; + +class IFFParser : public Common::IFFParser { +public: + IFFParser(Common::ReadStream &input) : Common::IFFParser(&input) { + // It seems Westwood missunderstood the 'size' field of the FORM chunk. + // + // For EMC scripts (type EMC2) it's filesize instead of filesize - 8, + // means accidently including the 8 bytes used by the chunk header for the FORM + // chunk. + // + // For TIM scripts (type AVFS) it's filesize - 12 instead of filesize - 8, + // means it will not include the size of the 'type' field in the FORM chunk, + // instead of only not including the chunk header size. + // + // Both lead to some problems in our IFF parser, either reading after the end + // of file or producing a "Chunk overread" error message. To work around this + // we need to adjust the size field properly. + if (_formType == MKID_BE('EMC2')) + _formChunk.size -= 8; + else if (_formType == MKID_BE('AVFS')) + _formChunk.size += 4; + } +}; + +class EMCInterpreter { +public: + EMCInterpreter(ToonEngine *vm); + + bool load(const char *filename, EMCData *data, const Common::Array<const OpcodeV2 *> *opcodes); + void unload(EMCData *data); + + void init(EMCState *scriptState, const EMCData *data); + bool start(EMCState *script, int function); + + void saveState(EMCState *script, Common::WriteStream *stream); + void loadState(EMCState *script, Common::ReadStream *stream); + + bool isValid(EMCState *script); + + bool run(EMCState *script); +protected: + ToonEngine *_vm; + int16 _parameter; + + const char *_filename; + EMCData *_scriptData; + + bool callback(Common::IFFChunk &chunk); + + typedef void (EMCInterpreter::*OpcodeProc)(EMCState *); + struct OpcodeEntry { + OpcodeProc proc; + const char *desc; + }; + + const OpcodeEntry *_opcodes; +private: + void op_jmp(EMCState *); + void op_setRetValue(EMCState *); + void op_pushRetOrPos(EMCState *); + void op_push(EMCState *); + void op_pushReg(EMCState *); + void op_pushBPNeg(EMCState *); + void op_pushBPAdd(EMCState *); + void op_popRetOrPos(EMCState *); + void op_popReg(EMCState *); + void op_popBPNeg(EMCState *); + void op_popBPAdd(EMCState *); + void op_addSP(EMCState *); + void op_subSP(EMCState *); + void op_sysCall(EMCState *); + void op_ifNotJmp(EMCState *); + void op_negate(EMCState *); + void op_eval(EMCState *); + void op_setRetAndJmp(EMCState *); +}; +} // End of namespace Toon + +#endif + diff --git a/engines/toon/script_func.cpp b/engines/toon/script_func.cpp new file mode 100644 index 0000000000..f571f324ef --- /dev/null +++ b/engines/toon/script_func.cpp @@ -0,0 +1,1186 @@ +/* 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. +* +* $URL$ +* $Id$ +* +*/ + +#include "toon/script_func.h" +#include "toon/script.h" +#include "toon/state.h" +#include "toon/toon.h" +#include "toon/anim.h" +#include "toon/hotspot.h" +#include "toon/drew.h" +#include "toon/flux.h" + +namespace Toon { + +#define SetOpcodeTable(x) table = &x; +#define Opcode(x) table->push_back(new OpcodeV2(this, &ScriptFunc::x)) +#define OpcodeUnImpl() table->push_back(new OpcodeV2(this, 0)) + +ScriptFunc::ScriptFunc(ToonEngine *vm) { + Common::Array<const OpcodeV2 *> *table = 0; + + _vm = vm; + _opcodes.reserve(176); + SetOpcodeTable(_opcodes); + + Opcode(sys_Cmd_Dummy); // dd offset sub_2B160 + Opcode(sys_Cmd_Change_Actor_X_And_Y); // dd offset sub_2A710 + Opcode(sys_Cmd_Init_Talking_Character); // dd offset sub_2A4E0 + Opcode(sys_Cmd_Draw_Actor_Standing), // dd offset sub_2A650 + Opcode(sys_Cmd_Get_Actor_X), // dd offset sub_2ADC0 + Opcode(sys_Cmd_Get_Actor_Y), // dd offset sub_2ADD0 + Opcode(sys_Cmd_Get_Actor_Facing), // dd offset sub_2A790 + Opcode(sys_Cmd_Get_Last_Scene), // dd offset sub_29F80 + Opcode(sys_Cmd_Debug_Print), // dd offset sub_2A510 + Opcode(sys_Cmd_Flip_Screens), // dd offset sub_2A180 + Opcode(sys_Cmd_Play_Flic), // dd offset sub_2A080 + Opcode(sys_Cmd_Force_Facing), // dd offset sub_29F90 + Opcode(sys_Cmd_Restart_Thread), // dd offset sub_29F30 + Opcode(sys_Cmd_Walk_Actor_To_Point), // dd offset sub_2A440 + Opcode(sys_Cmd_Set_Sack_Visible), // dd offset sub_29920 + Opcode(sys_Cmd_Set_Actor_Facing), // dd offset sub_2AD60 + Opcode(sys_Cmd_Confiscate_Inventory), // dd offset sub_29EB0 + Opcode(sys_Cmd_Character_Talks), // dd offset sub_29F00 + Opcode(sys_Cmd_Visited_Scene), // dd offset sub_29E80 + Opcode(sys_Cmd_Query_Rif_Flag), // dd offset sub_29D20 + Opcode(sys_Cmd_Query_Scroll), // dd offset sub_29D60 + Opcode(sys_Cmd_Set_Initial_Location), // dd offset sub_2AD80 + Opcode(sys_Cmd_Make_Line_Non_Walkable), // dd offset sub_29FC0 + Opcode(sys_Cmd_Make_Line_Walkable), // dd offset sub_2A050 + Opcode(sys_Cmd_Walk_Actor_On_Condition), // dd offset sub_29D70 + Opcode(sys_Cmd_Set_Actor_Facing_Point), // dd offset sub_29E60 + Opcode(sys_Cmd_Set_Inventory_Slot), // dd offset sub_2B0D0 + Opcode(sys_Cmd_Get_Inventory_Slot), // dd offset sub_2B0F0 + Opcode(sys_Cmd_Add_Item_To_Inventory), // dd offset sub_2AE50 + Opcode(sys_Cmd_Set_Actor_RGB_Modifiers), // dd offset sub_29CA0 + Opcode(sys_Cmd_Init_Conversation_AP), // dd offset sub_2B130 + Opcode(sys_Cmd_Actor_Talks), // dd offset sub_2ADA0 + Opcode(sys_Cmd_Say_Lines), // dd offset sub_29B20 + Opcode(sys_Cmd_Set_Rif_Flag), // dd offset sub_2A320 + Opcode(sys_Cmd_Empty_Inventory), // dd offset sub_2AE10 + Opcode(sys_Cmd_Set_Anim_Scale_Size), // dd offset sub_29BD0 + Opcode(sys_Cmd_Delete_Item_From_Inventory), // dd offset sub_2AE70 + Opcode(sys_Cmd_Specific_Item_In_Inventory), // dd offset sub_2A740 + Opcode(sys_Cmd_Run_Script), // dd offset sub_29AF0 + Opcode(sys_Cmd_Query_Game_Flag), // dd offset sub_2A3E0 + Opcode(sys_Cmd_Reset_Game_Flag), // dd offset sub_2A420 + Opcode(sys_Cmd_Set_Game_Flag), // dd offset sub_2A400 + Opcode(sys_Cmd_Create_Mouse_Item), // dd offset sub_2A4B0 + Opcode(sys_Cmd_Destroy_Mouse_Item), // dd offset sub_2A4D0 + Opcode(sys_Cmd_Get_Mouse_State), // dd offset sub_2A860 + Opcode(sys_Cmd_Hide_Mouse), // dd offset sub_2A5D0 + Opcode(sys_Cmd_Exit_Conversation), // dd offset sub_29AE0 + Opcode(sys_Cmd_Set_Mouse_Pos), // dd offset sub_2A810 + Opcode(sys_Cmd_Show_Mouse), // dd offset sub_2A5F0 + Opcode(sys_Cmd_In_Close_Up), // dd offset sub_29FB0 + Opcode(sys_Cmd_Set_Scroll_Lock), // dd offset sub_298B0 + Opcode(sys_Cmd_Fill_Area_Non_Walkable), // dd offset sub_29FF0 + Opcode(sys_Cmd_Set_Scroll_Coords), // dd offset sub_298D0 + Opcode(sys_Cmd_Hide_Cutaway), // dd offset sub_2A0F0 + Opcode(sys_Cmd_Show_Cutaway), // dd offset sub_2A100 + Opcode(sys_Cmd_Pause_Ticks), // dd offset sub_2A360 + Opcode(sys_Cmd_In_Conversation), // dd offset sub_29C60 + Opcode(sys_Cmd_Character_Talking), // dd offset sub_29C70 + Opcode(sys_Cmd_Set_Flux_Facing_Point), // dd offset sub_29980 + Opcode(sys_Cmd_Set_Flux_Facing), // dd offset sub_299A0 + Opcode(sys_Cmd_Set_Flux_Coords), // dd offset sub_299C0 + Opcode(sys_Cmd_Set_Flux_Visible), // dd offset sub_299F0 + Opcode(sys_Cmd_Get_Flux_X), // dd offset sub_29A40 + Opcode(sys_Cmd_Get_Flux_Y), // dd offset sub_29A50 + Opcode(sys_Cmd_Get_Flux_Facing), // dd offset sub_29A60 + Opcode(sys_Cmd_Get_Flux_Flags), // dd offset sub_29A70 + Opcode(sys_Cmd_Query_Flux_Coords), // dd offset sub_29A90 + Opcode(sys_Cmd_Have_A_Conversation), // dd offset sub_2B110 + Opcode(sys_Cmd_Walk_Flux_To_Point), // dd offset sub_29AC0 + Opcode(sys_Cmd_Get_Actor_Final_X), // dd offset sub_29940 + Opcode(sys_Cmd_Get_Actor_Final_Y), // dd offset sub_29960 + Opcode(sys_Cmd_Query_Scene_Anim_Loaded), // dd offset sub_29870 + Opcode(sys_Cmd_Play_Flux_Anim), // dd offset sub_29820 + Opcode(sys_Cmd_Set_Anim_Priority), // dd offset sub_29790 + Opcode(sys_Cmd_Place_Scene_Anim), // dd offset sub_2A7A0 + Opcode(sys_Cmd_Update_Scene_Animations), // dd offset sub_2AE30 + Opcode(sys_Cmd_Get_Drew_Scale), // dd offset sub_297E0 + Opcode(sys_Cmd_Query_Drew_Flags), // dd offset sub_29800 + Opcode(sys_Cmd_Set_Music), // dd offset sub_29720 + Opcode(sys_Cmd_Query_Speech), // dd offset sub_296D0 + Opcode(sys_Cmd_Enter_New_Scene), // dd offset sub_2A550 + Opcode(sys_Cmd_Enter_Same_Scene), // dd offset sub_2ADE0 + Opcode(sys_Cmd_Is_Pixel_Walkable), // dd offset sub_2A4F0 + Opcode(sys_Cmd_Show_Screen), // dd offset sub_2A0C0 + Opcode(sys_Cmd_Hide_Screen), // dd offset sub_2A0F0 + Opcode(sys_Cmd_Dummy), // dd offset sub_295D0 + Opcode(sys_Cmd_Set_Special_Enter_X_And_Y), // dd offset sub_2A590 + Opcode(sys_Cmd_Get_Mouse_X), // dd offset sub_296B0 + Opcode(sys_Cmd_Get_Mouse_Y), // dd offset sub_296C0 + Opcode(sys_Cmd_Fade_Palette), // dd offset sub_29650 + Opcode(sys_Cmd_Music_Enabled), // dd offset sub_29620 + Opcode(sys_Cmd_Dummy), // dd offset sub_295F0 + Opcode(sys_Cmd_Dummy), // dd offset sub_29610 + Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 + Opcode(sys_Cmd_Random), // dd offset sub_2A600 + Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 + Opcode(sys_Cmd_Wait_Key), // dd offset sub_2AE20 + Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 + Opcode(sys_Cmd_Draw_Scene_Anim_WSA_Frame_To_Back), // dd offset sub_2A940 + Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 + Opcode(sys_Cmd_Set_Scene_Anim_Wait), // dd offset sub_2A870 + Opcode(sys_Cmd_Init_Scene_Anim), // dd offset sub_2AC60 + Opcode(sys_Cmd_Set_Scene_Animation_Active_Flag), // dd offset sub_2AB10 + Opcode(sys_Cmd_Draw_Scene_Anim_WSA_Frame), // dd offset sub_2A8D0 + Opcode(sys_Cmd_Move_Scene_Anim), // dd offset sub_2AA90 + Opcode(sys_Cmd_Run_Actor_Default_Script), // dd offset sub_2A4E0 + Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 + Opcode(sys_Cmd_Set_Location_Data), // dd offset sub_2AE90 + Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 + Opcode(sys_Cmd_Set_CountDown_Timer), // dd offset sub_2AFC0 + Opcode(sys_Cmd_Query_CountDown_Timer), // dd offset sub_2AFE0 + Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 + Opcode(sys_Cmd_Proceed_To_Next_Chapter), // dd offset sub_2AFF0 + Opcode(sys_Cmd_Play_Sfx_Plus), // dd offset sub_2A1D0 + Opcode(sys_Cmd_Play_Sfx), // dd offset sub_2A1A0 + Opcode(sys_Cmd_Set_Ambient_Sfx), // dd offset sub_2A260 + Opcode(sys_Cmd_Kill_Ambient_Sfx), // dd offset sub_2A300 + Opcode(sys_Cmd_Set_Ambient_Sfx_Plus), // dd offset sub_2A290 + Opcode(sys_Cmd_Set_Ambient_Volume), // dd offset sub_2A240 + Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 + Opcode(sys_Cmd_Freeze_Scene_Animation), // dd offset sub_2AB90 + Opcode(sys_Cmd_Unfreeze_Scene_Animation), // dd offset sub_2ABB0 + Opcode(sys_Cmd_Scene_Animation_Frozen), // dd offset sub_2ABD0 + Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 + Opcode(sys_Cmd_Set_Script_Game_Data_Global), // dd offset sub_2ABF0 + Opcode(sys_Cmd_Get_Script_Game_Data_Global), // dd offset sub_2AC30 + Opcode(sys_Cmd_Say_Line), // dd offset loc_2A190 + Opcode(sys_Cmd_Knight_Puzzle_Get_Coord), // dd offset sub_2A110 + Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 + Opcode(sys_Cmd_Add_Scene_Anim), // dd offset sub_2AC60 + Opcode(sys_Cmd_Remove_Scene_Anim), // dd offset sub_2ACE0 + Opcode(sys_Cmd_Disable_Timer), // dd offset sub_2AD00 + Opcode(sys_Cmd_Enable_Timer), // dd offset sub_2AD20 + Opcode(sys_Cmd_Set_Timer), // dd offset sub_2AD40 + Opcode(sys_Cmd_Set_Palette_Color), // dd offset sub_2B020 + Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 + Opcode(sys_Cmd_Number_Of_NPCs), // dd offset loc_2A190 + Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 + Opcode(sys_Cmd_Dummy), // dd offset sub_2B160 + Opcode(sys_Cmd_Get_Config_Language), // dd offset sub_2B0C0 + Opcode(sys_Cmd_Dummy); // dd offset sub_2B160 +} + +ScriptFunc::~ScriptFunc(void) { + +} + +char *GetText(int32 i, EMCState *state) { + short stack = stackPos(i); + unsigned short textoffset = READ_BE_UINT16(&((unsigned short *)(state->dataPtr->text))[stack]); + char *text = (char *)&state->dataPtr->text[textoffset]; + return text; +} + +int32 ScriptFunc::sys_Cmd_Dummy(EMCState *state) { + return 0; +} + +int32 ScriptFunc::sys_Cmd_Change_Actor_X_And_Y(EMCState *state) { + _vm->getDrew()->setPosition(stackPos(0), stackPos(1)); + return 0; +} + +int32 ScriptFunc::sys_Cmd_Init_Talking_Character(EMCState *state) { + // really does nothing in original + return 0; +} + +int32 ScriptFunc::sys_Cmd_Draw_Actor_Standing(EMCState *state) { + + int32 arg1 = stackPos(0); + int32 arg2 = stackPos(1); + + if (arg2 > -1) + _vm->getDrew()->setFacing(arg2); + + if (arg1 < 0) { + _vm->getDrew()->setVisible(false); + } else { + _vm->getDrew()->setVisible(true); + _vm->getDrew()->playStandingAnim(); + } + + return 0; +} + +int32 ScriptFunc::sys_Cmd_Get_Actor_X(EMCState *state) { + return _vm->getDrew()->getX(); +} + +int32 ScriptFunc::sys_Cmd_Get_Actor_Y(EMCState *state) { + return _vm->getDrew()->getY(); +} + +int32 ScriptFunc::sys_Cmd_Get_Actor_Final_X(EMCState *state) { + return _vm->getDrew()->getFinalX(); +} + +int32 ScriptFunc::sys_Cmd_Get_Actor_Final_Y(EMCState *state) { + return _vm->getDrew()->getFinalY(); +} + +int32 ScriptFunc::sys_Cmd_Get_Actor_Facing(EMCState *state) { + return _vm->getDrew()->getFacing(); +} + +int32 ScriptFunc::sys_Cmd_Get_Last_Scene(EMCState *state) { + return _vm->state()->_lastVisitedScene; +} + +int32 ScriptFunc::sys_Cmd_Debug_Print(EMCState *state) { + return 0; +} + +int32 ScriptFunc::sys_Cmd_Flip_Screens(EMCState *state) { + _vm->flipScreens(); + return 0; +} + +int32 ScriptFunc::sys_Cmd_Play_Flic(EMCState *state) { + + char name[256]; + + // workaround for the video of the beginning + if (strstr(GetText(0, state), "209")) + sprintf(name, "misc/%s", GetText(0, state)); + else + strcpy(name, _vm->createRoomFilename(GetText(0, state)).c_str()); + +// Strangerke - Commented (not used) +// int32 Flags = stackPos(1); + int32 stopMusic = stackPos(2); + _vm->getMoviePlayer()->play(name, stopMusic); + return 0; +} + +int32 ScriptFunc::sys_Cmd_Force_Facing(EMCState *state) { + _vm->getDrew()->setFacing(stackPos(0)); + _vm->getDrew()->playStandingAnim(); + return 0; +} + +int32 ScriptFunc::sys_Cmd_Restart_Thread(EMCState *state) { + + int32 sceneId = stackPos(0); + _vm->getScript()->init(&_vm->getSceneAnimationScript(sceneId)->_state, _vm->getSceneAnimationScript(sceneId)->_data); + _vm->getScript()->start(&_vm->getSceneAnimationScript(sceneId)->_state, 9 + sceneId); + + if (!stackPos(1)) + _vm->setSceneAnimationScriptUpdate(false); + + return 0; +} + +int32 ScriptFunc::sys_Cmd_Walk_Actor_To_Point(EMCState *state) { + return _vm->getDrew()->walkTo(stackPos(0), stackPos(1)); +} + +int32 ScriptFunc::sys_Cmd_Set_Sack_Visible(EMCState *state) { + _vm->state()->_sackVisible = stackPos(0) > 0; + return 0; +} + +int32 ScriptFunc::sys_Cmd_Set_Actor_Facing(EMCState *state) { + _vm->getDrew()->setFacing(stackPos(0)); + _vm->getDrew()->playStandingAnim(); + return 0; +} + +int32 ScriptFunc::sys_Cmd_Confiscate_Inventory(EMCState *state) { + for (int32 i = 0; i < _vm->state()->_numInventoryItems; i++) { + _vm->state()->_confiscatedInventory[_vm->state()->_numConfiscatedInventoryItems] = _vm->state()->_inventory[i]; + _vm->state()->_numConfiscatedInventoryItems++; + } + _vm->state()->_numInventoryItems = 0; + return 0; +} + +int32 ScriptFunc::sys_Cmd_Character_Talks(EMCState *state) { + _vm->characterTalk(stackPos(0), false); + //_vm->characterTalk(stackPos(0)); + return 0; +} + +int32 ScriptFunc::sys_Cmd_Visited_Scene(EMCState *state) { + return _vm->state()->_locations[stackPos(0)]._visited ? 1 : 0; +} + +int32 ScriptFunc::sys_Cmd_Query_Rif_Flag(EMCState *state) { + + int32 hs = _vm->getHotspots()->FindBasedOnCorner(stackPos(0), stackPos(1)); + if (hs >= 0) + return _vm->getHotspots()->Get(hs)->getData(stackPos(2)); + + return 0; +} + +int32 ScriptFunc::sys_Cmd_Query_Scroll(EMCState *state) { + return _vm->state()->_currentScrollValue; +} + +int32 ScriptFunc::sys_Cmd_Set_Initial_Location(EMCState *state) { + int32 initialLocation = stackPos(0); + _vm->state()->_currentScene = initialLocation; + return 0; +} + +int32 ScriptFunc::sys_Cmd_Make_Line_Non_Walkable(EMCState *state) { + _vm->makeLineNonWalkable(stackPos(0), stackPos(1), stackPos(2), stackPos(3)); + + // we have to store some info for savegame + _vm->getSaveBufferStream()->writeSint16BE(2); // 2 = sys_Cmd_Make_Line_Non_Walkable + _vm->getSaveBufferStream()->writeSint16BE(stackPos(0)); + _vm->getSaveBufferStream()->writeSint16BE(stackPos(1)); + _vm->getSaveBufferStream()->writeSint16BE(stackPos(2)); + _vm->getSaveBufferStream()->writeSint16BE(stackPos(3)); + return 0; +} + +int32 ScriptFunc::sys_Cmd_Make_Line_Walkable(EMCState *state) { + _vm->makeLineWalkable(stackPos(0), stackPos(1), stackPos(2), stackPos(3)); + + // we have to store some info for savegame + _vm->getSaveBufferStream()->writeSint16BE(3); // 3 = sys_Cmd_Make_Line_Walkable + _vm->getSaveBufferStream()->writeSint16BE(stackPos(0)); + _vm->getSaveBufferStream()->writeSint16BE(stackPos(1)); + _vm->getSaveBufferStream()->writeSint16BE(stackPos(2)); + _vm->getSaveBufferStream()->writeSint16BE(stackPos(3)); + return 0; +} + +int32 ScriptFunc::sys_Cmd_Walk_Actor_On_Condition(EMCState *state) { + return 0; +} + +int32 ScriptFunc::sys_Cmd_Set_Actor_Facing_Point(EMCState *state) { + int32 fx = stackPos(0); + int32 fy = stackPos(1); + _vm->getDrew()->setFacing(_vm->getDrew()->getFacingFromDirection(fx - _vm->getDrew()->getX(), fy - _vm->getDrew()->getY())); + return 1; +} + +int32 ScriptFunc::sys_Cmd_Set_Inventory_Slot(EMCState *state) { + _vm->state()->_inventory[stackPos(1)] = stackPos(0); + return 0; +} + +int32 ScriptFunc::sys_Cmd_Get_Inventory_Slot(EMCState *state) { + return _vm->state()->_inventory[stackPos(0)]; +} + +int32 ScriptFunc::sys_Cmd_Add_Item_To_Inventory(EMCState *state) { + _vm->addItemToInventory(stackPos(0)); + return 0; +} + +int32 ScriptFunc::sys_Cmd_Set_Actor_RGB_Modifiers(EMCState *state) { + return 0; +} + +int32 ScriptFunc::sys_Cmd_Init_Conversation_AP(EMCState *state) { + debugC(0, 0xfff, "init_conversation_ap %d %d %d %d", stackPos(0), stackPos(1), stackPos(2), stackPos(3)); + _vm->initCharacter(stackPos(0), stackPos(1), stackPos(2), stackPos(3)); + return 0; +} + +int32 ScriptFunc::sys_Cmd_Actor_Talks(EMCState *state) { + _vm->characterTalk(stackPos(0), false); + return 0; +} + +int32 ScriptFunc::sys_Cmd_Say_Lines(EMCState *state) { + //_vm->sayLines(2, 1440); + _vm->sayLines(stackPos(0), stackPos(1)); + return 0; +} + +int32 ScriptFunc::sys_Cmd_Set_Rif_Flag(EMCState *state) { + int32 hs = _vm->getHotspots()->FindBasedOnCorner(stackPos(0), stackPos(1)); + if (hs >= 0) + _vm->getHotspots()->Get(hs)->setData(stackPos(2), stackPos(3)); + return 0; +} + +int32 ScriptFunc::sys_Cmd_Empty_Inventory(EMCState *state) { + + for (int32 i = 0; i < _vm->state()->_numInventoryItems; i++) + _vm->state()->_inventory[i] = 0; + + _vm->state()->_numInventoryItems = 0; + return 0; +} + +int32 ScriptFunc::sys_Cmd_Set_Anim_Scale_Size(EMCState *state) { + int32 animID = stackPos(0); + int32 scale = stackPos(1); + + SceneAnimation *sceneAnim = _vm->getSceneAnimation(animID); + if (sceneAnim) { + sceneAnim->_animInstance->setUseMask(true); + sceneAnim->_animInstance->setScale(scale,true); + } + return 0; +} +int32 ScriptFunc::sys_Cmd_Delete_Item_From_Inventory(EMCState *state) { + for (int32 i = 0; i < _vm->state()->_numInventoryItems; i++) { + if (stackPos(0) == _vm->state()->_inventory[i]) + _vm->state()->_inventory[i] = 0; + } + _vm->rearrangeInventory(); + return 0; +} +int32 ScriptFunc::sys_Cmd_Specific_Item_In_Inventory(EMCState *state) { + for (int32 i = 0; i < _vm->state()->_numInventoryItems; i++) { + if (_vm->state()->_inventory[i] == stackPos(0)) + return 1; + } + if (_vm->state()->_mouseState == stackPos(0)) + return 1; + return 0; +} + +int32 ScriptFunc::sys_Cmd_Run_Script(EMCState *state) { + return _vm->runEventScript(_vm->getMouseX(), _vm->getMouseY(), 2, stackPos(0), 0); +} + +int32 ScriptFunc::sys_Cmd_Query_Game_Flag(EMCState *state) { + int32 arg = stackPos(0); + return (_vm->state()->_gameFlag[arg >> 3] & (1 << (arg & 7))) != 0; +} + +int32 ScriptFunc::sys_Cmd_Reset_Game_Flag(EMCState *state) { + int32 arg = stackPos(0); + _vm->state()->_gameFlag[arg >> 3] &= ~(1 << (arg & 7)); + return 0; +} + +int32 ScriptFunc::sys_Cmd_Set_Game_Flag(EMCState *state) { + int32 arg = stackPos(0); + _vm->state()->_gameFlag[arg >> 3] |= (1 << (arg & 7)); + return 0; +} + +int32 ScriptFunc::sys_Cmd_Create_Mouse_Item(EMCState *state) { + _vm->createMouseItem(stackPos(0)); + return 0; +} + +int32 ScriptFunc::sys_Cmd_Destroy_Mouse_Item(EMCState *state) { + _vm->deleteMouseItem(); + return 0; +} + +int32 ScriptFunc::sys_Cmd_Get_Mouse_State(EMCState *state) { + return _vm->state()->_mouseState; +} + +int32 ScriptFunc::sys_Cmd_Hide_Mouse(EMCState *state) { + _vm->state()->_mouseHidden = true; + //if (Game.MouseHiddenCount > 0) Game.MouseHiddenCount = 1; + return 0; +} + +int32 ScriptFunc::sys_Cmd_Exit_Conversation(EMCState *state) { + _vm->state()->_exitConversation = true; + return 0; +} + +int32 ScriptFunc::sys_Cmd_Set_Mouse_Pos(EMCState *state) { + if (_vm->state()->_inCloseUp) { + _vm->getSystem()->warpMouse(stackPos(0), stackPos(1)); + } else { + _vm->getSystem()->warpMouse(stackPos(0) - _vm->state()->_currentScrollValue, stackPos(1)); + } + return 0; +} + +int32 ScriptFunc::sys_Cmd_Show_Mouse(EMCState *state) { + _vm->state()->_mouseHidden = false; + //if (Game.MouseHiddenCount < 0) Game.MouseHiddenCount = 0; + return 0; +} + +int32 ScriptFunc::sys_Cmd_In_Close_Up(EMCState *state) { + return _vm->state()->_inCloseUp; +} + +int32 ScriptFunc::sys_Cmd_Set_Scroll_Lock(EMCState *state) { + _vm->state()->_currentScrollLock = stackPos(0) > 0; + return 0; +} + +int32 ScriptFunc::sys_Cmd_Fill_Area_Non_Walkable(EMCState *state) { + _vm->getMask()->floodFillNotWalkableOnMask(stackPos(0), stackPos(1)); + + // we have to store some info for savegame + _vm->getSaveBufferStream()->writeSint16BE(4); // 4 = sys_Cmd_Make_Line_Walkable + _vm->getSaveBufferStream()->writeSint16BE(stackPos(0)); + _vm->getSaveBufferStream()->writeSint16BE(stackPos(1)); + + return 0; +} + +int32 ScriptFunc::sys_Cmd_Set_Scroll_Coords(EMCState *state) { + _vm->state()->_currentScrollValue = stackPos(0); + return 0; +} + +int32 ScriptFunc::sys_Cmd_Hide_Cutaway(EMCState *state) { + _vm->hideCutaway(); + return 0; +} + +int32 ScriptFunc::sys_Cmd_Show_Cutaway(EMCState *state) { + _vm->showCutaway(""); + return 0; +} + +int32 ScriptFunc::sys_Cmd_Pause_Ticks(EMCState *state) { + + if (!_vm->isUpdatingSceneAnimation() || _vm->getScriptRegionNested() > 0) { + if (stackPos(1)) + _vm->waitTicks(stackPos(0), true); + else + _vm->waitTicks(stackPos(0), false); + } else { + uint32 sceneId = _vm->getCurrentUpdatingSceneAnimation(); + uint32 waitTicks = stackPos(0); + if (waitTicks < 1) waitTicks = 1; + + waitTicks *= _vm->getTickLength(); + + if (sceneId < 40) { + int32 nextTicks = waitTicks + _vm->getSceneAnimationScript(sceneId)->_lastTimer; + if (nextTicks < _vm->getOldMilli()) + _vm->getSceneAnimationScript(sceneId)->_lastTimer = _vm->getOldMilli() + waitTicks; + else + _vm->getSceneAnimationScript(sceneId)->_lastTimer = nextTicks; + } + return 0; + } + + return 0; +} + +int32 ScriptFunc::sys_Cmd_In_Conversation(EMCState *state) { + return _vm->state()->_inConversation; +} + +int32 ScriptFunc::sys_Cmd_Character_Talking(EMCState *state) { + int32 characterId = stackPos(0); + Character *character = _vm->getCharacterById(characterId); + if (character) + return character->isTalking(); + return 0; +} + +int32 ScriptFunc::sys_Cmd_Set_Flux_Facing_Point(EMCState *state) { + int32 fx = stackPos(0); + int32 fy = stackPos(1); + _vm->getFlux()->setFacing(_vm->getFlux()->getFacingFromDirection(fx - _vm->getFlux()->getX(), fy - _vm->getFlux()->getY())); + _vm->getFlux()->playStandingAnim(); + return 1; +} + +int32 ScriptFunc::sys_Cmd_Set_Flux_Facing(EMCState *state) { + _vm->getFlux()->setFacing(stackPos(0)); + _vm->getFlux()->playStandingAnim(); + return 0; +} + +int32 ScriptFunc::sys_Cmd_Set_Flux_Coords(EMCState *state) { + _vm->getFlux()->stopWalk(); + _vm->getFlux()->setPosition(stackPos(0), stackPos(1)); + return 0; +} + +int32 ScriptFunc::sys_Cmd_Set_Flux_Visible(EMCState *state) { + _vm->getFlux()->setVisible(stackPos(0) > 0); + return 0; +} + +int32 ScriptFunc::sys_Cmd_Get_Flux_X(EMCState *state) { + return _vm->getFlux()->getX(); +} + +int32 ScriptFunc::sys_Cmd_Get_Flux_Y(EMCState *state) { + return _vm->getFlux()->getY(); +} + +int32 ScriptFunc::sys_Cmd_Get_Flux_Facing(EMCState *state) { + return _vm->getFlux()->getFacing(); +} + +int32 ScriptFunc::sys_Cmd_Get_Flux_Flags(EMCState *state) { + return (_vm->getFlux()->getFlag() & stackPos(0)) != 0; +} + +int32 ScriptFunc::sys_Cmd_Query_Flux_Coords(EMCState *state) { + return (stackPos(0) == _vm->getFlux()->getX()) && (stackPos(1) == _vm->getFlux()->getY()); +} + +int32 ScriptFunc::sys_Cmd_Have_A_Conversation(EMCState *state) { + _vm->haveAConversation(stackPos(0)); + return 0; +} + +int32 ScriptFunc::sys_Cmd_Walk_Flux_To_Point(EMCState *state) { + _vm->getFlux()->walkTo(stackPos(0), stackPos(1)); + return 1; +} + +int32 ScriptFunc::sys_Cmd_Query_Scene_Anim_Loaded(EMCState *state) { + return _vm->getSceneAnimation(stackPos(0))->_active; +} + +int32 ScriptFunc::sys_Cmd_Play_Flux_Anim(EMCState *state) { + return 0; +} + +int32 ScriptFunc::sys_Cmd_Set_Anim_Priority(EMCState *state) { + + SceneAnimation *sceneAnim = _vm->getSceneAnimation(stackPos(0)); + sceneAnim->_animInstance->setLayerZ(stackPos(1)); + return 0; +} + +int32 ScriptFunc::sys_Cmd_Place_Scene_Anim(EMCState *state) { + int32 sceneId = stackPos(0); + int32 x = stackPos(1); + int32 y = stackPos(2); + int32 frame = stackPos(5); + + SceneAnimation *sceneAnim = _vm->getSceneAnimation(sceneId); + sceneAnim->_animInstance->setPosition(x, y, sceneAnim->_animInstance->getZ(), false); + sceneAnim->_animInstance->forceFrame(frame); + _vm->setSceneAnimationScriptUpdate(false); + return 0; +} + +int32 ScriptFunc::sys_Cmd_Update_Scene_Animations(EMCState *state) { + //debugC(0, 0xfff, "UpdateAnimations"); + return 0; +} + +int32 ScriptFunc::sys_Cmd_Get_Drew_Scale(EMCState *state) { + int32 scale = _vm->getDrew()->getScale(); + if (!scale) + return 1024; + return scale; +} + +int32 ScriptFunc::sys_Cmd_Query_Drew_Flags(EMCState *state) { + return (_vm->getDrew()->getFlag() & stackPos(0)) != 0; +} + +int32 ScriptFunc::sys_Cmd_Set_Music(EMCState *state) { + + char *newMus = GetText(0, state); + _vm->getAudioManager()->playMusic(_vm->state()->_locations[_vm->state()->_currentScene]._name, newMus); + return 0; +} + +int32 ScriptFunc::sys_Cmd_Query_Speech(EMCState *state) { + if (_vm->getAudioManager()->voiceStillPlaying()) + return 1; + else + return 0; +} + +int32 ScriptFunc::sys_Cmd_Enter_New_Scene(EMCState *state) { + _vm->exitScene(); + _vm->getDrew()->setFacing(stackPos(1)); + _vm->loadScene(stackPos(0)); + _vm->setSceneAnimationScriptUpdate(false); + + return 0; +} + +int32 ScriptFunc::sys_Cmd_Enter_Same_Scene(EMCState *state) { + _vm->exitScene(); + _vm->loadScene(_vm->state()->_currentScene); + _vm->setSceneAnimationScriptUpdate(false); + + return 0; +} + +int32 ScriptFunc::sys_Cmd_Is_Pixel_Walkable(EMCState *state) { + return (_vm->getMask()->getData(stackPos(0), stackPos(1)) & 0x1f) > 0; +} + +int32 ScriptFunc::sys_Cmd_Show_Screen(EMCState *state) { + _vm->showCutaway(GetText(0, state)); + return 0; +} + +int32 ScriptFunc::sys_Cmd_Hide_Screen(EMCState *state) { + _vm->hideCutaway(); + return 0; +} + +int32 ScriptFunc::sys_Cmd_Set_Special_Enter_X_And_Y(EMCState *state) { + _vm->state()->_nextSpecialEnterX = stackPos(0); + _vm->state()->_nextSpecialEnterY = stackPos(1); + return 0; +} + +int32 ScriptFunc::sys_Cmd_Get_Mouse_X(EMCState *state) { + return _vm->getMouseX(); +} + +int32 ScriptFunc::sys_Cmd_Get_Mouse_Y(EMCState *state) { + return _vm->getMouseY(); +} + +int32 ScriptFunc::sys_Cmd_Fade_Palette(EMCState *state) { + debugC(0, 0xfff, "fadePalette %d %d", stackPos(0), stackPos(1)); + return 0; +} + +int32 ScriptFunc::sys_Cmd_Music_Enabled(EMCState *state) { + return 0; +} + +int32 ScriptFunc::sys_Cmd_Random(EMCState *state) { + int32 randMin = stackPos(0); + int32 randMax = stackPos(1); + int32 t = 0; + + if (randMin > randMax) { + t = randMin; + randMin = randMax; + randMax = t; + } + return _vm->randRange(randMin, randMax); +} + +int32 ScriptFunc::sys_Cmd_Wait_Key(EMCState *state) { + return 0; +} + +int32 ScriptFunc::sys_Cmd_Draw_Scene_Anim_WSA_Frame_To_Back(EMCState *state) { + // draw the frame in the backbuffer (picture then) + + int32 animId = stackPos(0); + int32 frame = stackPos(1); + + if (frame < 0) + return 0; + + SceneAnimation *sceneAnim = _vm->getSceneAnimation(animId); + + if (sceneAnim->_active) { + sceneAnim->_animInstance->setFrame(frame); + sceneAnim->_animInstance->setAnimationRange(frame, frame); + sceneAnim->_animInstance->stopAnimation(); + sceneAnim->_animInstance->renderOnPicture(); + + // we have to store some info for savegame + _vm->getSaveBufferStream()->writeSint16BE(1); // 1 = Draw_Scene_Anim_WSA_Frame_To_Back + _vm->getSaveBufferStream()->writeSint16BE(frame); + _vm->getSaveBufferStream()->writeSint16BE(strlen(sceneAnim->_animInstance->getAnimation()->_name) + 1); + _vm->getSaveBufferStream()->write(sceneAnim->_animInstance->getAnimation()->_name, strlen(sceneAnim->_animInstance->getAnimation()->_name) + 1); + _vm->getSaveBufferStream()->writeSint16BE(sceneAnim->_animInstance->getX()); + _vm->getSaveBufferStream()->writeSint16BE(sceneAnim->_animInstance->getY()); + _vm->getSaveBufferStream()->writeSint16BE(sceneAnim->_animInstance->getZ()); + _vm->getSaveBufferStream()->writeSint16BE(sceneAnim->_animInstance->getLayerZ()); + + } + return 1; +} + +int32 ScriptFunc::sys_Cmd_Set_Scene_Anim_Wait(EMCState *state) { + int32 sceneId = stackPos(0); + int32 waitTicks = stackPos(1); + if (waitTicks < 1) waitTicks = 1; + + // WORKAROUND : To fix the timing problem in the Gift-O-Matic + // The animation was too fast and the player was unable to click fast enough to get objects + // Here we slow down the animation + if (_vm->state()->_currentScene == 24) { + if (_vm->getCurrentUpdatingSceneAnimation() == 6) { + if (waitTicks == 1) { + waitTicks = 10; + _vm->setSceneAnimationScriptUpdate(false); + } + } + } + + // WORKAROUND : In Wolf place, the animation to move the pot was too fast and the player was unable to + // progress into the game. + if (_vm->state()->_currentScene == 29) { + if (_vm->getCurrentUpdatingSceneAnimation() == 8 || _vm->getCurrentUpdatingSceneAnimation() == 7) { + if (waitTicks == 1) { + waitTicks = 5; + _vm->setSceneAnimationScriptUpdate(false); + } + } + } + + // WORKAROUND : In transformed hangar, everything is too fast.. + if (_vm->state()->_currentScene == 19) { + waitTicks = 10; + _vm->setSceneAnimationScriptUpdate(false); + } + + // WORKAROUND : Slow down just a little the guards dance animation so that the voices don't cut + if (_vm->state()->_currentScene == 2 && (sceneId == 2 || sceneId == 3)) { + waitTicks = 7; + _vm->setSceneAnimationScriptUpdate(false); + } + + waitTicks *= _vm->getTickLength(); + + if (sceneId >= 0 && sceneId < 40) { + int32 nextTicks = waitTicks + _vm->getSceneAnimationScript(sceneId)->_lastTimer; + //debugC(0,0xff, "sw : assigining %d to lasttimer of %d (current tick %d old milli %d) ",nextTicks, sceneId , _vm->getSystem()->getMillis(), _vm->getOldMilli()); + if (nextTicks < _vm->getOldMilli()) + _vm->getSceneAnimationScript(sceneId)->_lastTimer = _vm->getOldMilli() + waitTicks; + else + _vm->getSceneAnimationScript(sceneId)->_lastTimer = nextTicks; + } + return 0; +} + +int32 ScriptFunc::sys_Cmd_Init_Scene_Anim(EMCState *state) { + int32 animId = stackPos(0); + int32 flags = stackPos(1); + + SceneAnimation *sceneAnim = _vm->getSceneAnimation(animId); + if (sceneAnim->_active) + return 0; + + sceneAnim->_animation = new Animation(_vm); + sceneAnim->_animation->loadAnimation(GetText(12, state)); + sceneAnim->_animInstance = _vm->getAnimationManager()->createNewInstance(kAnimationScene); + sceneAnim->_animInstance->setAnimation(sceneAnim->_animation); + sceneAnim->_animInstance->setVisible((flags & 1) != 0); + sceneAnim->_animInstance->setAnimationRange(stackPos(11), stackPos(11)); + sceneAnim->_animInstance->setFrame(stackPos(11)); + + _vm->getAnimationManager()->addInstance(sceneAnim->_animInstance); + + debugC(0, 0xfff, "Init Anim %s %d %d %d %d %d %d %d %d %d %d %d %d %d\n", GetText(12, state), stackPos(0), stackPos(1), stackPos(2), stackPos(3), + stackPos(4), stackPos(5), stackPos(6), stackPos(7), stackPos(8), stackPos(9), stackPos(10), stackPos(11), stackPos(12)); + + int32 dx = stackPos(4); + int32 dy = stackPos(5); + int32 x = stackPos(2); + int32 layerZ = stackPos(3); + + if (dx == -2) + sceneAnim->_animInstance->moveRelative(640, 0, 0); + else if (dx < 0) { + dx = sceneAnim->_animation->_x1; + } + else if (dx >= 0) + sceneAnim->_animInstance->setX(dx); + + if (dy >= 0) + sceneAnim->_animInstance->setY(dy); + else + dy = sceneAnim->_animation->_y1; + + if (flags & 0x20) { + sceneAnim->_animInstance->setZ(_vm->getLayerAtPoint(x, layerZ)); + sceneAnim->_animInstance->setUseMask(true); + } + + if (layerZ >= 0) { + sceneAnim->_animInstance->setLayerZ(layerZ); + } else { + dy = dy + sceneAnim->_animation->_y2 - sceneAnim->_animation->_y1 - 1; + sceneAnim->_animInstance->setLayerZ(dy); + } + + sceneAnim->_animInstance->setId(stackPos(0)); + + + sceneAnim->_active = true; + return 0; +} + +int32 ScriptFunc::sys_Cmd_Set_Scene_Animation_Active_Flag(EMCState *state) { + int32 animId = stackPos(0); + int32 activeFlag = stackPos(1); + + SceneAnimation *sceneAnim = _vm->getSceneAnimation(animId); + + if (sceneAnim->_active) + sceneAnim->_animInstance->setVisible(activeFlag > 0); + + return 0; +} + +int32 ScriptFunc::sys_Cmd_Draw_Scene_Anim_WSA_Frame(EMCState *state) { + int32 animId = stackPos(0); + int32 frame = stackPos(1); + + if (frame < 0) + return 0; + + SceneAnimation *sceneAnim = _vm->getSceneAnimation(animId); + + if (sceneAnim->_active) { + sceneAnim->_animInstance->setAnimation(sceneAnim->_animation); + sceneAnim->_animInstance->setFrame(frame); + sceneAnim->_animInstance->setAnimationRange(frame, frame); + sceneAnim->_animInstance->stopAnimation(); + } + _vm->setSceneAnimationScriptUpdate(false); + + // WORKAROUND : Too fast animations... + if (_vm->state()->_currentScene == 26 && animId == 22) + _vm->pauseSceneAnimationScript(_vm->getCurrentUpdatingSceneAnimation(), 3); + + if (_vm->state()->_currentScene == 14) { + if (animId == 3 || animId == 2 || animId == 4) + _vm->pauseSceneAnimationScript(_vm->getCurrentUpdatingSceneAnimation(), 2); + else if (animId == 20 || animId == 15 || animId == 21 || animId == 16 || animId == 17 || animId == 18) + _vm->pauseSceneAnimationScript(_vm->getCurrentUpdatingSceneAnimation(), 1); + else if (animId == 9) { + _vm->pauseSceneAnimationScript(_vm->getCurrentUpdatingSceneAnimation(), 6); + } + } + + if (_vm->state()->_currentScene == 29) { + if (animId == 16 || animId == 26 || animId == 36) + _vm->pauseSceneAnimationScript(_vm->getCurrentUpdatingSceneAnimation(), 2); + } + + return 0; +} + +int32 ScriptFunc::sys_Cmd_Move_Scene_Anim(EMCState *state) { + int32 animId = stackPos(0); + + SceneAnimation *sceneAnim = _vm->getSceneAnimation(animId); + sceneAnim->_animInstance->moveRelative(stackPos(1), stackPos(2), 0); + _vm->setSceneAnimationScriptUpdate(false); + return 0; +} + +int32 ScriptFunc::sys_Cmd_Run_Actor_Default_Script(EMCState *state) { + return 0; +} + +int32 ScriptFunc::sys_Cmd_Set_Location_Data(EMCState *state) { + // initial setup of locations + int32 locationId = stackPos(0); + debugC(0, 0, "setlocationdata(%d) %s %x %s %s %d %d", locationId, GetText(1, state), stackPos(2), GetText(3, state), GetText(4, state), stackPos(5), stackPos(6)); + strcpy(_vm->state()->_locations[locationId]._name, GetText(1, state)); + strcpy(_vm->state()->_locations[locationId]._music, GetText(3, state)); + strcpy(_vm->state()->_locations[locationId]._cutaway, GetText(4, state)); + _vm->state()->_locations[locationId]._flags = stackPos(2); + _vm->state()->_locations[locationId]._visited = false; + _vm->state()->_locations[locationId]._numSceneAnimations = stackPos(5); + + return 0; + +} + +int32 ScriptFunc::sys_Cmd_Set_CountDown_Timer(EMCState *state) { + // game timer is in ticks + _vm->state()->_gameTimer = stackPos(0) * _vm->getTickLength(); + return 0; +} + +int32 ScriptFunc::sys_Cmd_Query_CountDown_Timer(EMCState *state) { + return _vm->state()->_gameTimer; +} + +int32 ScriptFunc::sys_Cmd_Proceed_To_Next_Chapter(EMCState *state) { + + _vm->state()->_currentChapter = stackPos(0); + _vm->exitScene(); + _vm->loadScene(stackPos(1)); + return 0; +} + +int32 ScriptFunc::sys_Cmd_Play_Sfx_Plus(EMCState *state) { + //debugC(0,0xfff, "playSfx ( %d , %d, %d, %d, %d )", stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4)); + _vm->playSFX(stackPos(0), stackPos(1)); + return 0; +} + +int32 ScriptFunc::sys_Cmd_Play_Sfx(EMCState *state) { + //debugC(0,0xfff, "playSfx ( %d , %d)", stackPos(0), stackPos(1)); + _vm->playSFX(stackPos(0), stackPos(1)); + return 0; +} + +int32 ScriptFunc::sys_Cmd_Set_Ambient_Sfx(EMCState *state) { + //printf("Ambient Sfx : %d %d %d %d\n", stackPos(0), stackPos(1), stackPos(2), stackPos(3)); + _vm->getAudioManager()->startAmbientSFX(stackPos(0), stackPos(1), stackPos(2), stackPos(3)); + return 0; +} + +int32 ScriptFunc::sys_Cmd_Kill_Ambient_Sfx(EMCState *state) { + //printf("Kill Sfx : %d \n", stackPos(0)); + _vm->getAudioManager()->killAmbientSFX(stackPos(0)); + return 0; +} + +int32 ScriptFunc::sys_Cmd_Set_Ambient_Sfx_Plus(EMCState *state) { + //printf("Ambient Sfx Plus: %d %d %d %d %d %d %d %d\n", stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5), stackPos(6), stackPos(7)); + _vm->getAudioManager()->startAmbientSFX(stackPos(0), stackPos(1), stackPos(2), stackPos(3)); + return 0; +} + +int32 ScriptFunc::sys_Cmd_Set_Ambient_Volume(EMCState *state) { + //printf("Ambient Volume : %d %d \n", stackPos(0), stackPos(1)); + _vm->getAudioManager()->setAmbientSFXVolume(stackPos(0), stackPos(1)); + return 0; +} + +int32 ScriptFunc::sys_Cmd_Freeze_Scene_Animation(EMCState *state) { + + _vm->getSceneAnimationScript(stackPos(0))->_frozen = true; + return 0; +} + +int32 ScriptFunc::sys_Cmd_Unfreeze_Scene_Animation(EMCState *state) { + _vm->getSceneAnimationScript(stackPos(0))->_frozen = false; + return 0; +} + +int32 ScriptFunc::sys_Cmd_Scene_Animation_Frozen(EMCState *state) { + return _vm->getSceneAnimationScript(stackPos(0))->_frozen ? 1 : 0; +} + +int32 ScriptFunc::sys_Cmd_Set_Script_Game_Data_Global(EMCState *state) { + _vm->state()->_gameGlobalData[stackPos(0)] = stackPos(1); + return stackPos(1); +} + +int32 ScriptFunc::sys_Cmd_Get_Script_Game_Data_Global(EMCState *state) { + return _vm->state()->_gameGlobalData[stackPos(0)]; +} + +int32 ScriptFunc::sys_Cmd_Say_Line(EMCState *state) { + _vm->sayLines(1 , stackPos(0)); + return 0; +} + +int32 ScriptFunc::sys_Cmd_Knight_Puzzle_Get_Coord(EMCState *state) { + static const uint16 knightCoords[] = { + 0x327, 0x0fa, 0x354, 0x0f8, 0x383, 0x0f4, 0x3a4, 0x0f0, 0x3d3, 0x0ed, + 0x3fd, 0x0ef, 0x2fe, 0x12d, 0x2fe, 0x12d, 0x310, 0x109, 0x349, 0x103, + 0x378, 0x103, 0x3a4, 0x102, 0x3d5, 0x102, 0x403, 0x103, 0x2fe, 0x12d, + 0x2fe, 0x12d, 0x2fe, 0x12d, 0x2fe, 0x12d, 0x369, 0x123, 0x3a4, 0x123, + 0x3d8, 0x121, 0x410, 0x124, 0x2fe, 0x12d, 0x2fe, 0x12d, 0x2fe, 0x12d, + 0x2fe, 0x12d, 0x35d, 0x14f, 0x3a2, 0x149, 0x3db, 0x149, 0x415, 0x14a, + 0x2fe, 0x12d, 0x2fe, 0x12d + }; + + return knightCoords[stackPos(2) + 2 * (8 * stackPos(1) + stackPos(0))]; +} + +int32 ScriptFunc::sys_Cmd_Add_Scene_Anim(EMCState *state) { + return sys_Cmd_Init_Scene_Anim(state); +} + +int32 ScriptFunc::sys_Cmd_Remove_Scene_Anim(EMCState *state) { + int32 sceneId = stackPos(0); + + if (!_vm->getSceneAnimation(sceneId)->_active) + return 0; + + SceneAnimation *sceneAnim = _vm->getSceneAnimation(sceneId); + sceneAnim->_active = false; + _vm->getAnimationManager()->removeInstance(sceneAnim->_animInstance); + sceneAnim->_animation = 0; + sceneAnim->_animInstance = 0; + return 0; +} + +int32 ScriptFunc::sys_Cmd_Disable_Timer(EMCState *state) { + _vm->disableTimer(stackPos(0)); + return 0; +} + +int32 ScriptFunc::sys_Cmd_Enable_Timer(EMCState *state) { + _vm->enableTimer(stackPos(0)); + return 0; +} + +int32 ScriptFunc::sys_Cmd_Set_Timer(EMCState *state) { + _vm->setTimer(stackPos(0), stackPos(1)); + return 0; +} + +int32 ScriptFunc::sys_Cmd_Set_Palette_Color(EMCState *state) { + return 0; +} + +int32 ScriptFunc::sys_Cmd_Number_Of_NPCs(EMCState *state) { + return 0; +} + +int32 ScriptFunc::sys_Cmd_Get_Config_Language(EMCState *state) { + return 0; +} + +} // End of namespace Toon diff --git a/engines/toon/script_func.h b/engines/toon/script_func.h new file mode 100644 index 0000000000..76b7b0ada1 --- /dev/null +++ b/engines/toon/script_func.h @@ -0,0 +1,174 @@ +/* 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. +* +* $URL$ +* $Id$ +* +*/ + +#ifndef SCRIPT_FUNC_H +#define SCRIPT_FUNC_H + +#include "common/array.h" +#include "toon/script.h" + +namespace Toon { + +class ScriptFunc; + +typedef Common::Functor1Mem<EMCState *, int32, ScriptFunc> OpcodeV2; + +class ScriptFunc { +public: + ScriptFunc(ToonEngine *vm); + ~ScriptFunc(void); + Common::Array<const OpcodeV2 *> _opcodes; + ToonEngine *_vm; + +#define SYSFUNC(x) int32 x(EMCState*) + SYSFUNC(sys_Cmd_Dummy); + SYSFUNC(sys_Cmd_Change_Actor_X_And_Y); + SYSFUNC(sys_Cmd_Init_Talking_Character); + SYSFUNC(sys_Cmd_Draw_Actor_Standing); + SYSFUNC(sys_Cmd_Get_Actor_X); + SYSFUNC(sys_Cmd_Get_Actor_Y); + SYSFUNC(sys_Cmd_Get_Actor_Facing); + SYSFUNC(sys_Cmd_Get_Last_Scene); + SYSFUNC(sys_Cmd_Debug_Print); + SYSFUNC(sys_Cmd_Flip_Screens); + SYSFUNC(sys_Cmd_Play_Flic); + SYSFUNC(sys_Cmd_Force_Facing); + SYSFUNC(sys_Cmd_Restart_Thread); + SYSFUNC(sys_Cmd_Walk_Actor_To_Point); + SYSFUNC(sys_Cmd_Set_Sack_Visible); + SYSFUNC(sys_Cmd_Set_Actor_Facing); + SYSFUNC(sys_Cmd_Confiscate_Inventory); + SYSFUNC(sys_Cmd_Character_Talks); + SYSFUNC(sys_Cmd_Visited_Scene); + SYSFUNC(sys_Cmd_Query_Rif_Flag); + SYSFUNC(sys_Cmd_Query_Scroll); + SYSFUNC(sys_Cmd_Set_Initial_Location); + SYSFUNC(sys_Cmd_Make_Line_Non_Walkable); + SYSFUNC(sys_Cmd_Make_Line_Walkable); + SYSFUNC(sys_Cmd_Walk_Actor_On_Condition); + SYSFUNC(sys_Cmd_Set_Actor_Facing_Point); + SYSFUNC(sys_Cmd_Set_Inventory_Slot); + SYSFUNC(sys_Cmd_Get_Inventory_Slot); + SYSFUNC(sys_Cmd_Add_Item_To_Inventory); + SYSFUNC(sys_Cmd_Set_Actor_RGB_Modifiers); + SYSFUNC(sys_Cmd_Init_Conversation_AP); + SYSFUNC(sys_Cmd_Actor_Talks); + SYSFUNC(sys_Cmd_Say_Lines); + SYSFUNC(sys_Cmd_Set_Rif_Flag); + SYSFUNC(sys_Cmd_Empty_Inventory); + SYSFUNC(sys_Cmd_Set_Anim_Scale_Size); + SYSFUNC(sys_Cmd_Delete_Item_From_Inventory); + SYSFUNC(sys_Cmd_Specific_Item_In_Inventory); + SYSFUNC(sys_Cmd_Run_Script); + SYSFUNC(sys_Cmd_Query_Game_Flag); + SYSFUNC(sys_Cmd_Reset_Game_Flag); + SYSFUNC(sys_Cmd_Set_Game_Flag); + SYSFUNC(sys_Cmd_Create_Mouse_Item); + SYSFUNC(sys_Cmd_Destroy_Mouse_Item); + SYSFUNC(sys_Cmd_Get_Mouse_State); + SYSFUNC(sys_Cmd_Hide_Mouse); + SYSFUNC(sys_Cmd_Exit_Conversation); + SYSFUNC(sys_Cmd_Set_Mouse_Pos); + SYSFUNC(sys_Cmd_Show_Mouse); + SYSFUNC(sys_Cmd_In_Close_Up); + SYSFUNC(sys_Cmd_Set_Scroll_Lock); + SYSFUNC(sys_Cmd_Fill_Area_Non_Walkable); + SYSFUNC(sys_Cmd_Set_Scroll_Coords); + SYSFUNC(sys_Cmd_Hide_Cutaway); + SYSFUNC(sys_Cmd_Show_Cutaway); + SYSFUNC(sys_Cmd_Pause_Ticks); + SYSFUNC(sys_Cmd_In_Conversation); + SYSFUNC(sys_Cmd_Character_Talking); + SYSFUNC(sys_Cmd_Set_Flux_Facing_Point); + SYSFUNC(sys_Cmd_Set_Flux_Facing); + SYSFUNC(sys_Cmd_Set_Flux_Coords); + SYSFUNC(sys_Cmd_Set_Flux_Visible); + SYSFUNC(sys_Cmd_Get_Flux_X); + SYSFUNC(sys_Cmd_Get_Flux_Y); + SYSFUNC(sys_Cmd_Get_Flux_Facing); + SYSFUNC(sys_Cmd_Get_Flux_Flags); + SYSFUNC(sys_Cmd_Query_Flux_Coords); + SYSFUNC(sys_Cmd_Have_A_Conversation); + SYSFUNC(sys_Cmd_Walk_Flux_To_Point); + SYSFUNC(sys_Cmd_Query_Scene_Anim_Loaded); + SYSFUNC(sys_Cmd_Play_Flux_Anim); + SYSFUNC(sys_Cmd_Set_Anim_Priority); + SYSFUNC(sys_Cmd_Place_Scene_Anim); + SYSFUNC(sys_Cmd_Update_Scene_Animations); + SYSFUNC(sys_Cmd_Get_Drew_Scale); + SYSFUNC(sys_Cmd_Query_Drew_Flags); + SYSFUNC(sys_Cmd_Set_Music); + SYSFUNC(sys_Cmd_Query_Speech); + SYSFUNC(sys_Cmd_Enter_New_Scene); + SYSFUNC(sys_Cmd_Enter_Same_Scene); + SYSFUNC(sys_Cmd_Is_Pixel_Walkable); + SYSFUNC(sys_Cmd_Show_Screen); + SYSFUNC(sys_Cmd_Hide_Screen); + SYSFUNC(sys_Cmd_Set_Special_Enter_X_And_Y); + SYSFUNC(sys_Cmd_Get_Mouse_X); + SYSFUNC(sys_Cmd_Get_Mouse_Y); + SYSFUNC(sys_Cmd_Fade_Palette); + SYSFUNC(sys_Cmd_Music_Enabled); + SYSFUNC(sys_Cmd_Random); + SYSFUNC(sys_Cmd_Wait_Key); + SYSFUNC(sys_Cmd_Draw_Scene_Anim_WSA_Frame_To_Back); + SYSFUNC(sys_Cmd_Set_Scene_Anim_Wait); + SYSFUNC(sys_Cmd_Init_Scene_Anim); + SYSFUNC(sys_Cmd_Set_Scene_Animation_Active_Flag); + SYSFUNC(sys_Cmd_Draw_Scene_Anim_WSA_Frame); + SYSFUNC(sys_Cmd_Move_Scene_Anim); + SYSFUNC(sys_Cmd_Run_Actor_Default_Script); + SYSFUNC(sys_Cmd_Set_Location_Data); + SYSFUNC(sys_Cmd_Set_CountDown_Timer); + SYSFUNC(sys_Cmd_Query_CountDown_Timer); + SYSFUNC(sys_Cmd_Proceed_To_Next_Chapter); + SYSFUNC(sys_Cmd_Play_Sfx_Plus); + SYSFUNC(sys_Cmd_Play_Sfx); + SYSFUNC(sys_Cmd_Set_Ambient_Sfx); + SYSFUNC(sys_Cmd_Kill_Ambient_Sfx); + SYSFUNC(sys_Cmd_Set_Ambient_Sfx_Plus); + SYSFUNC(sys_Cmd_Set_Ambient_Volume); + SYSFUNC(sys_Cmd_Freeze_Scene_Animation); + SYSFUNC(sys_Cmd_Unfreeze_Scene_Animation); + SYSFUNC(sys_Cmd_Scene_Animation_Frozen); + SYSFUNC(sys_Cmd_Set_Script_Game_Data_Global); + SYSFUNC(sys_Cmd_Get_Script_Game_Data_Global); + SYSFUNC(sys_Cmd_Say_Line); + SYSFUNC(sys_Cmd_Knight_Puzzle_Get_Coord); + SYSFUNC(sys_Cmd_Add_Scene_Anim); + SYSFUNC(sys_Cmd_Remove_Scene_Anim); + SYSFUNC(sys_Cmd_Disable_Timer); + SYSFUNC(sys_Cmd_Enable_Timer); + SYSFUNC(sys_Cmd_Set_Timer); + SYSFUNC(sys_Cmd_Set_Palette_Color); + SYSFUNC(sys_Cmd_Number_Of_NPCs); + SYSFUNC(sys_Cmd_Get_Config_Language); + SYSFUNC(sys_Cmd_Get_Actor_Final_X); + SYSFUNC(sys_Cmd_Get_Actor_Final_Y); +}; + +} // End of namespace Toon + +#endif diff --git a/engines/toon/state.cpp b/engines/toon/state.cpp new file mode 100644 index 0000000000..71674688d5 --- /dev/null +++ b/engines/toon/state.cpp @@ -0,0 +1,261 @@ +/* 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. +* +* $URL$ +* $Id$ +* +*/ + +#include "toon/state.h" +#include "toon/toon.h" + +namespace Toon { + +void Location::save(Common::WriteStream *stream) { + stream->write(_cutaway, 64); + stream->write(_music, 64); + stream->write(_name, 64); + stream->writeSint16BE(_numRifBoxes); + stream->writeSint16BE(_numSceneAnimations); + stream->writeSByte(_visited); + + for (int32 i = 0; i < _numRifBoxes * 2; i++) { + stream->writeSint16BE(_rifBoxesFlags[i]); + } +} +void Location::load(Common::ReadStream *stream) { + stream->read(_cutaway, 64); + stream->read(_music, 64); + stream->read(_name, 64); + _numRifBoxes = stream->readSint16BE(); + _numSceneAnimations = stream->readSint16BE(); + _visited = stream->readSByte(); + + for (int32 i = 0; i < _numRifBoxes * 2; i++) { + _rifBoxesFlags[i] = stream->readSint16BE(); + } +} + +State::State(void) { + for (int32 i = 0; i < 64; i++) { + _locations[i]._visited = false; + _locations[i]._numSceneAnimations = 0; + _locations[i]._numRifBoxes = 0; + } + + memset(_gameFlag, 0, sizeof(_gameFlag)); + memset(_gameGlobalData, -1, sizeof(_gameGlobalData)); + + for (int32 i = 0; i < 2; i++) { + _timerEnabled[i] = false; + _timerTimeout[i] = 0; + _timerDelay[i] = -1; + } + + _lastVisitedScene = -1; + _currentScene = -1; + + _currentScrollLock = false; + _currentScrollValue = 0; + + _gameTimer = 0; + _currentChapter = 1; + + _showConversationIcons = false; + + _inCloseUp = false; + _inConversation = false; + + _mouseState = -1; + _mouseHidden = false; + + _firstConverstationLine = false; + + _sackVisible = false; // to change + _inCutaway = false; + + _inInventory = false; + _numInventoryItems = 0; //To chhange + _numConfiscatedInventoryItems = 0; + + _nextSpecialEnterX = -1; + _nextSpecialEnterY = -1; + +#if 0 + for (int i = 0; i < 30; i++) { + _inventory[i] = 90 + i; + if (_inventory[i] == 41) + _inventory[i] = 42; + } + + _inventory[0] = 53; + _inventory[1] = 22; + _inventory[2] = 93; + _inventory[3] = 49; + _inventory[4] = 47; + _inventory[5] = 14; + _numInventoryItems = 6; //To change +#endif + + memset(_conversationState, 0, sizeof(Conversation) * 60); +} + +State::~State(void) { + +} + +int32 State::getGameFlag(int32 flagId) { + return (_gameFlag[flagId >> 3] & (1 << (flagId & 7))) != 0; +} + +bool State::hasItemInInventory(int32 item) { + debugC(1, kDebugState, "hasItemInInventory(%d)", item); + + for (int32 i = 0; i < _numInventoryItems; i++) { + if (_inventory[i] == item) + return true; + } + return false; +} + +void State::save(Common::WriteStream *stream) { + + for (int32 i = 0; i < 256; i++) { + _locations[i].save(stream); + } + + for (int32 i = 0; i < 256; i++) { + stream->writeSint16BE(_gameGlobalData[i]); + } + + for (int32 i = 0; i < 256; i++) { + stream->writeSint16BE(_gameFlag[i]); + } + + stream->writeSint16BE(_lastVisitedScene); + stream->writeSint16BE(_currentScene); + stream->writeSint16BE(_currentScrollValue); + stream->writeSByte(_currentScrollLock); + + for (int32 i = 0; i < 35; i++) { + stream->writeSint16BE(_inventory[i]); + } + + for (int32 i = 0; i < 35; i++) { + stream->writeSint16BE(_confiscatedInventory[i]); + } + + stream->writeSint32BE(_numInventoryItems); + stream->writeSint32BE(_numConfiscatedInventoryItems); + + stream->writeSByte(_inCloseUp); + stream->writeSByte(_inCutaway); + stream->writeSByte(_inConversation); + stream->writeSByte(_inInventory); + stream->writeSByte(_showConversationIcons); + + stream->writeSint16BE(_mouseState); + + stream->writeSint16BE(_currentConversationId); + stream->writeSByte(_firstConverstationLine); + stream->writeSByte(_exitConversation); + stream->writeSByte(_mouseHidden); + stream->writeSByte(_sackVisible); + stream->writeSint32BE(_gameTimer); + stream->writeSByte(_currentChapter); + + stream->writeByte(_timerEnabled[0]); + stream->writeByte(_timerEnabled[1]); + + stream->writeSint32BE(_timerTimeout[0]); + stream->writeSint32BE(_timerTimeout[1]); + + stream->writeSint32BE(_timerDelay[0]); + stream->writeSint32BE(_timerDelay[1]); +} + +void State::load(Common::ReadStream *stream) { + for (int32 i = 0; i < 256; i++) { + _locations[i].load(stream); + } + + for (int32 i = 0; i < 256; i++) { + _gameGlobalData[i] = stream->readSint16BE(); + } + + for (int32 i = 0; i < 256; i++) { + _gameFlag[i] = stream->readSint16BE(); + } + + _lastVisitedScene = stream->readSint16BE(); + _currentScene = stream->readSint16BE(); + _currentScrollValue = stream->readSint16BE(); + _currentScrollLock = stream->readSByte(); + + for (int32 i = 0; i < 35; i++) { + _inventory[i] = stream->readSint16BE(); + } + + for (int32 i = 0; i < 35; i++) { + _confiscatedInventory[i] = stream->readSint16BE(); + } + + _numInventoryItems = stream->readSint32BE(); + _numConfiscatedInventoryItems = stream->readSint32BE(); + + _inCloseUp = stream->readSByte(); + _inCutaway = stream->readSByte(); + _inConversation = stream->readSByte(); + _inInventory = stream->readSByte(); + _showConversationIcons = stream->readSByte(); + + _mouseState = stream->readSint16BE(); + + _currentConversationId = stream->readSint16BE(); + _firstConverstationLine = stream->readSByte(); + _exitConversation = stream->readSByte(); + _mouseHidden = stream->readSByte(); + _sackVisible = stream->readSByte(); + _gameTimer = stream->readSint32BE(); + _currentChapter = stream->readSByte(); + + _timerEnabled[0] = stream->readByte(); + _timerEnabled[1] = stream->readByte(); + + _timerTimeout[0] = stream->readSint32BE(); + _timerTimeout[1] = stream->readSint32BE(); + + _timerDelay[0] = stream->readSint32BE(); + _timerDelay[1] = stream->readSint32BE(); +} + +void State::loadConversations(Common::ReadStream *stream) { + for (int32 i = 0; i < 60; i++) { + _conversationState[i].load(stream, _conversationData); + } +} + +void State::saveConversations(Common::WriteStream *stream) { + for (int32 i = 0; i < 60; i++) { + _conversationState[i].save(stream, _conversationData); + } +} + +} // End of namespace Toon diff --git a/engines/toon/state.h b/engines/toon/state.h new file mode 100644 index 0000000000..283e378443 --- /dev/null +++ b/engines/toon/state.h @@ -0,0 +1,101 @@ +/* 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. +* +* $URL$ +* $Id$ +* +*/ + +#ifndef TOON_STATE_H +#define TOON_STATE_H + +#include "common/file.h" +#include "common/str.h" +#include "toon/conversation.h" + +namespace Toon { + +struct Location { + char _name[64]; + char _music[64]; + char _cutaway[64]; + bool _visited; + int32 _numSceneAnimations; + int32 _flags; + int32 _numRifBoxes; + int16 _rifBoxesFlags[256]; + + void save(Common::WriteStream *stream); + void load(Common::ReadStream *stream); +}; + +class State { +public: + State(void); + ~State(void); + + Location _locations[256]; + int16 _gameGlobalData[256]; + uint8 _gameFlag[256]; + int16 _lastVisitedScene; + int16 _currentScene; + int16 _currentScrollValue; + bool _currentScrollLock; + int16 _inventory[35]; + int16 _confiscatedInventory[35]; + int32 _numInventoryItems; + int32 _numConfiscatedInventoryItems; + bool _inCloseUp; + bool _inCutaway; + bool _inConversation; + bool _inInventory; + bool _showConversationIcons; + int16 _mouseState; + int16 *_conversationData; + Conversation _conversationState[60]; + int16 _currentConversationId; + bool _firstConverstationLine; + bool _exitConversation; + bool _mouseHidden; + bool _sackVisible; + int32 _gameTimer; + int8 _currentChapter; + int32 _nextSpecialEnterX; + int32 _nextSpecialEnterY; + + + bool _timerEnabled[2]; + int32 _timerTimeout[2]; + int32 _timerDelay[2]; + + int32 getGameFlag(int32 flagId); + bool hasItemInInventory(int32 item); + + void load(Common::ReadStream *stream); + void save(Common::WriteStream *stream); + + void loadConversations(Common::ReadStream *stream); + void saveConversations(Common::WriteStream *stream); + +}; + +} // End of namespace Toon + +#endif diff --git a/engines/toon/text.cpp b/engines/toon/text.cpp new file mode 100644 index 0000000000..c18e0cbdc8 --- /dev/null +++ b/engines/toon/text.cpp @@ -0,0 +1,95 @@ +/* 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. +* +* $URL$ +* $Id$ +* +*/ + +#include "toon/text.h" + +namespace Toon { + + +TextResource::TextResource(ToonEngine *vm) : _vm(vm) { + _numTexts = 0; + _textData = 0; +} + +TextResource::~TextResource(void) { + +} + +bool TextResource::loadTextResource(Common::String fileName) { + debugC(1, kDebugText, "loadTextResource(%s)", fileName.c_str()); + + uint32 fileSize = 0; + uint8 *data = _vm->resources()->getFileData(fileName, &fileSize); + if (!data) + return false; + + _textData = new uint8[fileSize]; + memcpy(_textData, data, fileSize); + _numTexts = READ_LE_UINT16(data); + + return true; +} + +int32 TextResource::getNext(int32 offset) { + debugC(1, kDebugText, "getNext(%d)", offset); + + uint16 *table = (uint16 *)_textData + 1; + int a = getId(offset); + return table[a+1]; +} + +int32 TextResource::getId(int32 offset) { + debugC(1, kDebugText, "getId(%d)", offset); + + uint16 *table = (uint16 *)_textData + 1; + int32 found = -1; + for (int32 i = 0; i < _numTexts; i++) { + if (offset == table[i]) { + found = i; + break; + } + } + return found; +} + +char *TextResource::getText(int32 offset) { + debugC(6, kDebugText, "getText(%d)", offset); + + uint16 *table = (uint16 *)_textData + 1; + int32 found = -1; + for (int32 i = 0; i < _numTexts; i++) { + if (offset == table[i]) { + found = i; + break; + } + } + if (found < 0) + return NULL; + + int32 realOffset = ((uint16 *)_textData + 1 + _numTexts)[found]; + return (char *)_textData + realOffset; +} + +} // End of namespace Toon diff --git a/engines/toon/text.h b/engines/toon/text.h new file mode 100644 index 0000000000..9a35471e4f --- /dev/null +++ b/engines/toon/text.h @@ -0,0 +1,51 @@ +/* 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. +* +* $URL$ +* $Id$ +* +*/ + +#ifndef TOON_TEXT_H +#define TOON_TEXT_H + +#include "toon/toon.h" + +namespace Toon { + +class TextResource { +public: + TextResource(ToonEngine *vm); + ~TextResource(void); + + bool loadTextResource(Common::String fileName); + char *getText(int32 id); + int32 getId(int32 offset); + int32 getNext(int32 offset); + +protected: + int32 _numTexts; + uint8 *_textData; + ToonEngine *_vm; +}; + +} // End of namespace Toon + +#endif diff --git a/engines/toon/tools.cpp b/engines/toon/tools.cpp new file mode 100644 index 0000000000..a03a2d57ce --- /dev/null +++ b/engines/toon/tools.cpp @@ -0,0 +1,516 @@ +/* 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. +* +* $URL$ +* $Id$ +* +*/ + +#include "toon/tools.h" +#include "toon/toon.h" + +namespace Toon { + +uint32 decompressLZSS(byte *src, byte *dst, int dstsize) { + debugC(5, kDebugTools, "decompressLZSS(src, dst, %d)", dstsize); + + byte *srcp = src; + byte *dstp = dst; + uint16 bitbuf; + int32 len, ofs; + len = 0; + while (dstsize > 0) { + bitbuf = 0x100 | *(srcp++); + while (bitbuf != 1 && dstsize > 0) { + if (bitbuf & 1) { + ofs = READ_LE_UINT16(srcp); + srcp += 2; + len = ((ofs & 0xF000) >> 12) + 3; + ofs = ofs | 0xF000; + dstsize -= len; + if (dstsize < 0) + break; + while (len--) { + *dstp = *(byte *)(dstp + (signed short)ofs); + dstp++; + } + } else { + len = 0; + while ((bitbuf & 2) == 0) { + len++; + bitbuf >>= 1; + } + len++; + dstsize -= len; + if (dstsize < 0) + break; + while (len--) + *(dstp++) = *(srcp++); + } + bitbuf >>= 1; + } + } + len += dstsize; + if (len < 0) + return 0; + + while (len--) + *(dstp++) = *(srcp++); + + return (dstp - dst); +} + +uint32 decompressSPCN(byte *src, byte *dst, uint32 dstsize) { + debugC(1, kDebugTools, "decompressSPCN(src, dst, %d)", dstsize); + + byte *srcp = src; + byte *dstp = dst, *dste = dst + dstsize; + byte val; + uint16 len, ofs; + if (!(*srcp & 0x80)) srcp++; + while (dstp < dste) { + val = *(srcp++); + if (val & 0x80) { + if (val & 0x40) { + if (val == 0xFE) { + len = READ_LE_UINT16(srcp); + while (len--) + *(dstp++) = srcp[2]; + srcp += 3; + } else { + if (val == 0xFF) { + len = READ_LE_UINT16(srcp); + srcp += 2; + } else { + len = (val & 0x3F) + 3; + } + ofs = READ_LE_UINT16(srcp); + srcp += 2; + while (len--) { + *dstp = *(byte *)(dstp - ofs); + dstp++; + } + } + } else { + len = val & 0x3F; + while (len--) + *(dstp++) = *(srcp++); + } + } else { + len = (val >> 4) + 3; + ofs = ((val & 0x0F) << 8) | *(srcp++); + while (len--) { + *dstp = *(byte *)(dstp - ofs); + dstp++; + } + } + } + return (dstp - dst); +} + + +//return codes +#define NOT_PACKED 0 +#define PACKED_CRC -1 +#define UNPACKED_CRC -2 + +//other defines +#define TABLE_SIZE (16 * 8) +#define MIN_LENGTH 2 +#define HEADER_LEN 18 + +RncDecoder::RncDecoder() { + initCrc(); +} + +RncDecoder::~RncDecoder() { } + +void RncDecoder::initCrc() { + debugC(1, kDebugTools, "initCrc()"); + + uint16 cnt = 0; + uint16 tmp1 = 0; + uint16 tmp2 = 0; + + for (tmp2 = 0; tmp2 < 0x100; tmp2++) { + tmp1 = tmp2; + for (cnt = 8; cnt > 0; cnt--) { + if (tmp1 % 2) { + tmp1 >>= 1; + tmp1 ^= 0x0a001; + } else + tmp1 >>= 1; + } + _crcTable[tmp2] = tmp1; + } +} + +//calculate 16 bit crc of a block of memory +uint16 RncDecoder::crcBlock(const uint8 *block, uint32 size) { + debugC(1, kDebugTools, "crcBlock(block, %d)", size); + + uint16 crc = 0; + uint8 *crcTable8 = (uint8 *)_crcTable; //make a uint8* to crc_table + uint8 tmp; + uint32 i; + + for (i = 0; i < size; i++) { + tmp = *block++; + crc ^= tmp; + tmp = (uint8)((crc >> 8) & 0x00FF); + crc &= 0x00FF; + crc = *(uint16 *)&crcTable8[crc << 1]; + crc ^= tmp; + } + + return crc; +} + +uint16 RncDecoder::inputBits(uint8 amount) { + debugC(5, kDebugTools, "inputBits(%d)", amount); + + uint16 newBitBuffh = _bitBuffh; + uint16 newBitBuffl = _bitBuffl; + int16 newBitCount = _bitCount; + uint16 remBits, returnVal; + + returnVal = ((1 << amount) - 1) & newBitBuffl; + newBitCount -= amount; + + if (newBitCount < 0) { + newBitCount += amount; + remBits = (newBitBuffh << (16 - newBitCount)); + newBitBuffh >>= newBitCount; + newBitBuffl >>= newBitCount; + newBitBuffl |= remBits; + _srcPtr += 2; + newBitBuffh = READ_LE_UINT16(_srcPtr); + amount -= newBitCount; + newBitCount = 16 - amount; + } + remBits = (newBitBuffh << (16 - amount)); + _bitBuffh = newBitBuffh >> amount; + _bitBuffl = (newBitBuffl >> amount) | remBits; + _bitCount = (uint8)newBitCount; + + return returnVal; +} + +void RncDecoder::makeHufftable(uint16 *table) { + debugC(1, kDebugTools, "makeHufftable(table)"); + + uint16 bitLength, i, j; + uint16 numCodes = inputBits(5); + + if (!numCodes) + return; + + uint8 huffLength[16]; + for (i = 0; i < numCodes; i++) + huffLength[i] = (uint8)(inputBits(4) & 0x00FF); + + uint16 huffCode = 0; + + for (bitLength = 1; bitLength < 17; bitLength++) { + for (i = 0; i < numCodes; i++) { + if (huffLength[i] == bitLength) { + *table++ = (1 << bitLength) - 1; + + uint16 b = huffCode >> (16 - bitLength); + uint16 a = 0; + + for (j = 0; j < bitLength; j++) + a |= ((b >> j) & 1) << (bitLength - j - 1); + *table++ = a; + + *(table + 0x1e) = (huffLength[i] << 8) | (i & 0x00FF); + huffCode += 1 << (16 - bitLength); + } + } + } +} + +uint16 RncDecoder::inputValue(uint16 *table) { + debugC(5, kDebugTools, "inputValue(table)"); + + uint16 valOne, valTwo, value = _bitBuffl; + + do { + valTwo = (*table++) & value; + valOne = *table++; + } while (valOne != valTwo); + + value = *(table + 0x1e); + inputBits((uint8)((value >> 8) & 0x00FF)); + value &= 0x00FF; + + if (value >= 2) { + value--; + valOne = inputBits((uint8)value & 0x00FF); + valOne |= (1 << value); + value = valOne; + } + + return value; +} + +int RncDecoder::getbit() { + debugC(6, kDebugTools, "getbits()"); + + if (_bitCount == 0) { + _bitBuffl = *_srcPtr++; + _bitCount = 8; + } + byte temp = (_bitBuffl & 0x80) >> 7; + _bitBuffl <<= 1; + _bitCount--; + return temp; +} + +int32 RncDecoder::unpackM1(const void *input, void *output) { + debugC(1, kDebugTools, "unpackM1(input, output)"); + + uint8 *outputLow, *outputHigh; + const uint8 *inputHigh, *inputptr = (const uint8 *)input; + + uint32 unpackLen = 0; + uint32 packLen = 0; + uint16 counts = 0; + uint16 crcUnpacked = 0; + uint16 crcPacked = 0; + + + _bitBuffl = 0; + _bitBuffh = 0; + _bitCount = 0; + + //Check for "RNC " + if (READ_BE_UINT32(inputptr) != RNC1_SIGNATURE) + return NOT_PACKED; + + inputptr += 4; + + // read unpacked/packed file length + unpackLen = READ_BE_UINT32(inputptr); + inputptr += 4; + packLen = READ_BE_UINT32(inputptr); + inputptr += 4; + + uint8 blocks = *(inputptr + 5); + + //read CRC's + crcUnpacked = READ_BE_UINT16(inputptr); + inputptr += 2; + crcPacked = READ_BE_UINT16(inputptr); + inputptr += 2; + inputptr = (inputptr + HEADER_LEN - 16); + + if (crcBlock(inputptr, packLen) != crcPacked) + return PACKED_CRC; + + inputptr = (((const uint8 *)input) + HEADER_LEN); + _srcPtr = inputptr; + + inputHigh = ((const uint8 *)input) + packLen + HEADER_LEN; + outputLow = (uint8 *)output; + outputHigh = *(((const uint8 *)input) + 16) + unpackLen + outputLow; + + if (!((inputHigh <= outputLow) || (outputHigh <= inputHigh))) { + _srcPtr = inputHigh; + _dstPtr = outputHigh; + memcpy((_dstPtr - packLen), (_srcPtr - packLen), packLen); + _srcPtr = (_dstPtr - packLen); + } + + _dstPtr = (uint8 *)output; + _bitCount = 0; + + _bitBuffl = READ_LE_UINT16(_srcPtr); + inputBits(2); + + do { + makeHufftable(_rawTable); + makeHufftable(_posTable); + makeHufftable(_lenTable); + + counts = inputBits(16); + + do { + uint32 inputLength = inputValue(_rawTable); + uint32 inputOffset; + + if (inputLength) { + memcpy(_dstPtr, _srcPtr, inputLength); //memcpy is allowed here + _dstPtr += inputLength; + _srcPtr += inputLength; + uint16 a = READ_LE_UINT16(_srcPtr); + uint16 b = READ_LE_UINT16(_srcPtr + 2); + + _bitBuffl &= ((1 << _bitCount) - 1); + _bitBuffl |= (a << _bitCount); + _bitBuffh = (a >> (16 - _bitCount)) | (b << _bitCount); + } + + if (counts > 1) { + inputOffset = inputValue(_posTable) + 1; + inputLength = inputValue(_lenTable) + MIN_LENGTH; + + // Don't use memcpy here! because input and output overlap. + uint8 *tmpPtr = (_dstPtr - inputOffset); + while (inputLength--) + *_dstPtr++ = *tmpPtr++; + } + } while (--counts); + } while (--blocks); + + if (crcBlock((uint8 *)output, unpackLen) != crcUnpacked) + return UNPACKED_CRC; + + // all is done..return the amount of unpacked bytes + return unpackLen; +} + +int32 RncDecoder::unpackM2(const void *input, void *output) { + debugC(1, kDebugTools, "unpackM2(input, output)"); + + const uint8 *inputptr = (const uint8 *)input; + + uint32 unpackLen = 0; + uint32 packLen = 0; + uint16 crcUnpacked = 0; + uint16 crcPacked = 0; + +// Strangerke - Commented (not used) +// uint16 counts = 0; + + _bitBuffl = 0; + _bitCount = 0; + + //Check for "RNC " + if (READ_BE_UINT32(inputptr) != RNC2_SIGNATURE) + return NOT_PACKED; + + inputptr += 4; + + // read unpacked/packed file length + unpackLen = READ_BE_UINT32(inputptr); + inputptr += 4; + packLen = READ_BE_UINT32(inputptr); + inputptr += 4; + + //read CRC's + crcUnpacked = READ_BE_UINT16(inputptr); + inputptr += 2; + crcPacked = READ_BE_UINT16(inputptr); + inputptr += 2; + inputptr = (inputptr + HEADER_LEN - 16); + + if (crcBlock(inputptr, packLen) != crcPacked) + return PACKED_CRC; + + inputptr = (((const uint8 *)input) + HEADER_LEN); + _srcPtr = inputptr; + _dstPtr = (uint8 *)output; + + + uint16 ofs, len; + byte ofs_hi, ofs_lo; + + len = 0; + ofs_hi = 0; + ofs_lo = 0; + + getbit(); + getbit(); + + while (1) { + + bool loadVal = false; + + while (getbit() == 0) + *_dstPtr++ = *_srcPtr++; + + len = 2; + ofs_hi = 0; + if (getbit() == 0) { + len = (len << 1) | getbit(); + if (getbit() == 1) { + len--; + len = (len << 1) | getbit(); + if (len == 9) { + len = 4; + while (len--) + ofs_hi = (ofs_hi << 1) | getbit(); + len = (ofs_hi + 3) * 4; + while (len--) + *_dstPtr++ = *_srcPtr++; + continue; + } + } + loadVal = true; + } else { + if (getbit() == 1) { + len++; + if (getbit() == 1) { + len = *_srcPtr++; + if (len == 0) { + if (getbit() == 1) + continue; + else + break; + } + len += 8; + } + loadVal = true; + } else { + loadVal = false; + } + } + + if (loadVal) { + if (getbit() == 1) { + ofs_hi = (ofs_hi << 1) | getbit(); + if (getbit() == 1) { + ofs_hi = ((ofs_hi << 1) | getbit()) | 4; + if (getbit() == 0) + ofs_hi = (ofs_hi << 1) | getbit(); + } else if (ofs_hi == 0) { + ofs_hi = 2 | getbit(); + } + } + } + + ofs_lo = *_srcPtr++; + ofs = (ofs_hi << 8) | ofs_lo; + while (len--) { + *_dstPtr = *(byte *)(_dstPtr - ofs - 1); + _dstPtr++; + } + + } + + if (crcBlock((uint8 *)output, unpackLen) != crcUnpacked) + return UNPACKED_CRC; + + // all is done..return the amount of unpacked bytes + return unpackLen; +} + +} // End of namespace Toon diff --git a/engines/toon/tools.h b/engines/toon/tools.h new file mode 100644 index 0000000000..05fc5c9cda --- /dev/null +++ b/engines/toon/tools.h @@ -0,0 +1,83 @@ +/* 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. +* +* $URL$ +* $Id$ +* +*/ + +#ifndef TOON_TOOLS_H +#define TOON_TOOLS_H + +#include "common/scummsys.h" +#include "common/endian.h" + +#define RNC1_SIGNATURE 0x524E4301 // "RNC\001" +#define RNC2_SIGNATURE 0x524E4302 // "RNC\002" + +namespace Toon { + +const uint32 kCompLZSS = 0x4C5A5353; +const uint32 kCompSPCN = 0x5350434E; +const uint32 kCompRNC1 = 0x524E4301; +const uint32 kCompRNC2 = 0x524E4302; + +#define READ_LE_INT16(x) (int16) READ_LE_UINT16(x) +#define READ_LE_INT32(x) (int32) READ_LE_UINT32(x) + +#define WRITE_LE_INT16(x,y) WRITE_LE_UINT16(x,(int16)y) +#define WRITE_LE_INT32(x,y) WRITE_LE_UINT32(x,(int32)y) + +uint32 decompressSPCN(byte *src, byte *dst, uint32 dstsize); +uint32 decompressLZSS(byte *src, byte *dst, int dstsize); + +class RncDecoder { + +protected: + uint16 _rawTable[64]; + uint16 _posTable[64]; + uint16 _lenTable[64]; + uint16 _crcTable[256]; + + uint16 _bitBuffl; + uint16 _bitBuffh; + uint8 _bitCount; + + const uint8 *_srcPtr; + uint8 *_dstPtr; + +public: + RncDecoder(); + ~RncDecoder(); + int32 unpackM1(const void *input, void *output); + int32 unpackM2(const void *input, void *output); + +protected: + void initCrc(); + uint16 crcBlock(const uint8 *block, uint32 size); + uint16 inputBits(uint8 amount); + void makeHufftable(uint16 *table); + uint16 inputValue(uint16 *table); + int getbit(); +}; + +} // End of namespace Toon + +#endif diff --git a/engines/toon/toon.cpp b/engines/toon/toon.cpp new file mode 100644 index 0000000000..7a6a2fc87a --- /dev/null +++ b/engines/toon/toon.cpp @@ -0,0 +1,4548 @@ +/* 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. +* +* $URL$ +* $Id$ +* +*/ + +#include "common/system.h" +#include "common/events.h" +#include "common/debug-channels.h" +#include "common/archive.h" +#include "common/config-manager.h" +#include "common/EventRecorder.h" +#include "engines/util.h" +#include "graphics/surface.h" +#include "graphics/thumbnail.h" +#include "gui/saveload.h" +#include "gui/about.h" +#include "gui/message.h" +#include "toon/resource.h" +#include "toon/toon.h" +#include "toon/anim.h" +#include "toon/picture.h" +#include "toon/hotspot.h" +#include "toon/flux.h" +#include "toon/drew.h" +#include "toon/path.h" + +namespace Toon { + + +void ToonEngine::init() { + _currentScriptRegion = 0; + _resources = new Resources(this); + _animationManager = new AnimationManager(this); + _moviePlayer = new Movie(this, new ToonstruckSmackerDecoder(_mixer)); + _hotspots = new Hotspots(this); + + _mainSurface = new Graphics::Surface(); + _mainSurface->create(1280, 400, 1); + + _finalPalette = new uint8[768]; + _backupPalette = new uint8[768]; + _additionalPalette1 = new uint8[69]; + _additionalPalette2 = new uint8[69]; + _cutawayPalette = new uint8[768]; + _universalPalette = new uint8[96]; + _fluxPalette = new uint8[24]; + + memset(_finalPalette, 0, 768); + memset(_backupPalette, 0, 768); + memset(_additionalPalette1, 0, 69); + memset(_additionalPalette2, 0, 69); + memset(_cutawayPalette, 0, 768); + memset(_universalPalette, 0, 96); + memset(_fluxPalette, 0, 24); + + _conversationData = new int16[4096]; + memset(_conversationData, 0, 4096 * sizeof(int16)); + + _shouldQuit = false; + _scriptStep = 0; + + _cursorOffsetX = 0; + _cursorOffsetY = 0; + _currentHotspotItem = 0; + + _currentTextLine = 0; + _currentTextLineId = -1; + _currentTextLineX = 0; + _currentTextLineY = 0; + _currentTextLineCharacterId = 0; + + _saveBufferStream = new Common::MemoryWriteStreamDynamic(); + + _firstFrame = false; + + const Common::FSNode gameDataDir(ConfMan.get("path")); + SearchMan.addSubDirectoryMatching(gameDataDir, "misc"); + + syncSoundSettings(); + + _pathFinding = new PathFinding(this); + + resources()->openPackage("misc/local.pak", true); + resources()->openPackage("misc/onetime.pak", true); + resources()->openPackage("misc/drew.pak", true); + + for (int32 i = 0; i < 32; i++) + _characters[i] = 0; + + _characters[0] = new CharacterDrew(this); + _characters[1] = new CharacterFlux(this); + _drew = _characters[0]; + _flux = _characters[1]; + + // preload walk anim for flux and drew + _drew->loadWalkAnimation("stndwalk.caf"); + _drew->setupPalette(); + _drew->loadShadowAnimation("shadow.caf"); + + _flux->loadWalkAnimation("fxstwalk.caf"); + _flux->loadShadowAnimation("shadow.caf"); + + loadAdditionalPalette("universe.pal", 3); + loadAdditionalPalette("flux.pal", 4); + setupGeneralPalette(); + + _script_func = new ScriptFunc(this); + _gameState = new State(); + _gameState->_conversationData = _conversationData; + + memset(_sceneAnimations, 0, sizeof(_sceneAnimations)); + memset(_sceneAnimationScripts, 0, sizeof(_sceneAnimationScripts)); + + + _gameState->_currentChapter = 1; + initChapter(); + loadCursor(); + initFonts(); + + _dialogIcons = new Animation(this); + _dialogIcons->loadAnimation("dialogue.caf"); + + _inventoryIcons = new Animation(this); + _inventoryIcons->loadAnimation("inventry.caf"); + + _inventoryIconSlots = new Animation(this); + _inventoryIconSlots->loadAnimation("iconslot.caf"); + + _genericTexts = new TextResource(this); + _genericTexts->loadTextResource("generic.tre"); + + _audioManager = new AudioManager(this, _mixer); + _audioManager->loadAudioPack(0, "generic.svi", "misc/generic.svl"); + _audioManager->loadAudioPack(2, "generic.sei", "generic.sel"); + + _lastMouseButton = 0; + _mouseButton = 0; +} + +void ToonEngine::waitForScriptStep() { + // Wait after a specified number of script steps when executing a script + // to lower CPU usage + if (++_scriptStep >= 40) { + g_system->delayMillis(1); + _scriptStep = 0; + } +} + +void ToonEngine::parseInput() { + + Common::EventManager *_event = _system->getEventManager(); + + _mouseX = _event->getMousePos().x; + _mouseY = _event->getMousePos().y; + _mouseButton = _event->getButtonState(); + + Common::Event event; + while (_event->pollEvent(event)) { + switch (event.type) { + case Common::EVENT_KEYUP: + if (event.kbd.ascii == 27 || event.kbd.ascii == 32) { + _audioManager->stopCurrentVoice(); + } + if (event.kbd.keycode == Common::KEYCODE_F5) { + if(canSaveGameStateCurrently()) + saveGame(-1, Common::String()); + } + if (event.kbd.keycode == Common::KEYCODE_F6) { + if(canLoadGameStateCurrently()) + loadGame(-1); + } + if (event.kbd.ascii == 't') { + _showConversationText = !_showConversationText; + } + if (event.kbd.ascii == 'm') { + _audioManager->muteMusic(!_audioManager->isMusicMuted()); + } + if (event.kbd.ascii == 'd') { + _audioManager->muteVoice(!_audioManager->isVoiceMuted()); + } + if (event.kbd.ascii == 's') { + _audioManager->muteSfx(!_audioManager->isSfxMuted()); + } + + if (event.kbd.flags & Common::KBD_ALT) { + int32 slotNum = event.kbd.ascii - '0'; + if (slotNum >= 0 && slotNum <= 9 && canSaveGameStateCurrently()) { + if (saveGame(slotNum, Common::String())) { + // ok + char buf[256]; + snprintf(buf, 256, "Saved game in slot #%d ", slotNum); + GUI::TimedMessageDialog dialog(buf, 1000); + dialog.runModal(); + } else { + char buf[256]; + snprintf(buf, 256, "Could not quick save into slot #%d", slotNum); + GUI::MessageDialog dialog2(buf, "OK", 0); + //warning("%s", buf); + dialog2.runModal(); + + } + } + } + + if (event.kbd.flags & Common::KBD_CTRL) { + int32 slotNum = event.kbd.ascii - '0'; + if (slotNum >= 0 && slotNum <= 9 && canLoadGameStateCurrently()) { + if (loadGame(slotNum)) { + // ok + char buf[256]; + snprintf(buf, 256, "Savegame #%d quick loaded", slotNum); + GUI::TimedMessageDialog dialog(buf, 1000); + dialog.runModal(); + } else { + char buf[256]; + snprintf(buf, 256, "Could not quick load the savegame #%d", slotNum); + GUI::MessageDialog dialog(buf, "OK", 0); + warning("%s", buf); + dialog.runModal(); + } + } + } + break; +// Strangerke - Commented (not used) +// case Common::EVENT_LBUTTONDOWN: +// break; +// case Common::EVENT_RBUTTONDOWN: +// break; +// case Common::EVENT_LBUTTONUP: +// break; +// case Common::EVENT_RBUTTONUP: +// break; +// case Common::EVENT_WHEELUP: +// break; +// case Common::EVENT_WHEELDOWN: +// break; + default: + break; + } + } + + if (!_gameState->_inConversation && !_gameState->_mouseHidden && !_gameState->_inInventory) { + selectHotspot(); + clickEvent(); + } + +} + +void ToonEngine::enableTimer(int32 timerId) { + _gameState->_timerEnabled[timerId] = true; +} +void ToonEngine::setTimer(int32 timerId, int32 timerWait) { + _gameState->_timerTimeout[timerId] = getOldMilli() + timerWait * getTickLength(); + _gameState->_timerDelay[timerId] = timerWait; +} +void ToonEngine::disableTimer(int32 timerId) { + _gameState->_timerEnabled[timerId] = false; +} +void ToonEngine::updateTimers() { + for (int32 i = 0; i < 2; i++) { + if (_gameState->_timerEnabled[i]) { + if (_gameState->_timerDelay[i] > -1 && getOldMilli() > _gameState->_timerTimeout[i]) { + if (i == 0) { + + EMCState *status = &_scriptState[_currentScriptRegion]; + _script->init(status, &_scriptData); + + // setup registers + status->regs[0] = _mouseX; + status->regs[1] = _mouseY; + status->regs[2] = 0; + + _currentScriptRegion++; + + _script->start(status, 7); + while (_script->run(status)) + waitForScriptStep(); + + _currentScriptRegion--; + + _gameState->_timerTimeout[i] = getOldMilli() + _gameState->_timerDelay[i] * getTickLength(); + + return; + + } + } + } + } +} + +void ToonEngine::updateScrolling(bool force, int32 timeIncrement) { + static int32 lastScrollOffset = 320; + if (!_audioManager->voiceStillPlaying() && !_gameState->_currentScrollLock && (_drew->getFlag() & 1) == 0) { + if (_drew->getFacing() & 3) { + if (_drew->getFacing() <= 4) + lastScrollOffset = 200; + else + lastScrollOffset = 440; + } + + if (_gameState->_inCutaway || _gameState->_inInventory || _gameState->_inCloseUp) + return; + + int32 desiredScrollValue = _drew->getX() - lastScrollOffset; + + if ((_gameState->_locations[_gameState->_currentScene]._flags & 0x80) == 0) { + if (desiredScrollValue < 0) + desiredScrollValue = 0; + if (desiredScrollValue >= _currentPicture->getWidth() - 640) + desiredScrollValue = _currentPicture->getWidth() - 640; + + if (force) { + _gameState->_currentScrollValue = desiredScrollValue; + return; + } else { + if (_gameState->_currentScrollValue < desiredScrollValue) { + _gameState->_currentScrollValue += timeIncrement / 2; + + if (_gameState->_currentScrollValue > desiredScrollValue) + _gameState->_currentScrollValue = desiredScrollValue; + } else if (_gameState->_currentScrollValue > desiredScrollValue) { + _gameState->_currentScrollValue -= timeIncrement / 2; + + if (_gameState->_currentScrollValue < desiredScrollValue) + _gameState->_currentScrollValue = desiredScrollValue; + } + } + } + } +} + +void ToonEngine::update(int32 timeIncrement) { + // to make sure we're updating the game at 5fps at least + if (timeIncrement > 200) + timeIncrement = 200; + + updateAnimationSceneScripts(timeIncrement); + updateCharacters(timeIncrement); + updateTimer(timeIncrement); + updateTimers(); + updateScrolling(false, timeIncrement); + _audioManager->updateAmbientSFX(); + _animationManager->update(timeIncrement); + _cursorAnimationInstance->update(timeIncrement); + + if (!_audioManager->voiceStillPlaying()) { + _currentTextLine = 0; + _currentTextLineId = -1; + } +} + +void ToonEngine::updateTimer(int32 timeIncrement) { + if (_gameState->_gameTimer > 0) { + debugC(0, 0xfff, "updateTimer(%d)", timeIncrement); + _gameState->_gameTimer -= timeIncrement; + if (_gameState->_gameTimer < 0) + _gameState->_gameTimer = 0; + } +} + +void ToonEngine::render() { + if (_gameState->_inCutaway) + _currentCutaway->draw(*_mainSurface, 0, 0, 0, 0); + else + _currentPicture->draw(*_mainSurface, 0, 0, 0, 0); + + //_currentMask->drawMask(*_mainSurface,0,0,0,0); + _animationManager->render(); + + drawInfoLine(); + drawConversationLine(); + drawConversationIcons(); + drawSack(); + //drawPalette(); + +#if 0 + char test[256]; + if (_mouseX > 0 && _mouseX < 640 && _mouseY > 0 && _mouseY < 400) { + sprintf(test, "%d %d / mask %d layer %d z %d", _mouseX, _mouseY, getMask()->getData(_mouseX, _mouseY), getLayerAtPoint(_mouseX, _mouseY), getZAtPoint(_mouseX, _mouseY)); + + int32 c = *(uint8 *)_mainSurface->getBasePtr(_mouseX, _mouseY); + sprintf(test, "%d %d / color id %d %d,%d,%d", _mouseX, _mouseY, c, _finalPalette[c*3+0], _finalPalette[c*3+1], _finalPalette[c*3+2]); + + _fontRenderer->setFont(_fontToon); + _fontRenderer->renderText(40, 150, Common::String(test), 0); + } +#endif + + if (_firstFrame) { + copyToVirtualScreen(false); + fadeIn(5); + _firstFrame = false; + } else { + copyToVirtualScreen(true); + } +} + +void ToonEngine::doMagnifierEffect() { + int32 posX = _mouseX + state()->_currentScrollValue - _cursorOffsetX; + int32 posY = _mouseY - _cursorOffsetY - 2; + + Graphics::Surface &surface = *_mainSurface; + + // fast sqrt table lookup (values up to 144 only) + static const byte intSqrt[] = { + 0, 1, 1, 1, 2, 2, 2, 2, 2, 3, + 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 12 + }; + + byte tempBuffer[25*25]; + for (int32 y = -12; y <= 12; y++) { + for (int32 x = -12; x <= 12; x++) { + int32 destPitch = surface.pitch; + uint8 *curRow = (uint8 *)surface.pixels + (posY + y) * destPitch + (posX + x); + tempBuffer[(y+12) * 25 + x + 12] = *curRow; + } + } + + for (int32 y = -12; y <= 12; y++) { + for (int32 x = -12; x <= 12; x++) { + int32 dist = y * y + x * x; + if (dist > 144) + continue; + int32 destPitch = surface.pitch; + uint8 *curRow = (uint8 *)surface.pixels + (posY + y) * destPitch + (posX + x); + int32 lerp = (512 + intSqrt[dist] * 256 / 12); + *curRow = tempBuffer[(y*lerp/1024+12) * 25 + x*lerp/1024 + 12]; + } + } +} + +void ToonEngine::copyToVirtualScreen(bool updateScreen) { + // render cursor last + if (!_gameState->_mouseHidden) { + if (_cursorAnimationInstance->getFrame() == 7) // magnifier icon needs a special effect + doMagnifierEffect(); + _cursorAnimationInstance->setPosition(_mouseX - 40 + state()->_currentScrollValue - _cursorOffsetX, _mouseY - 40 - _cursorOffsetY, 0, false); + _cursorAnimationInstance->render(); + } + _system->copyRectToScreen((byte *)_mainSurface->pixels + state()->_currentScrollValue, 1280, 0, 0, 640, 400); + if (updateScreen) { + _system->updateScreen(); + _shouldQuit = shouldQuit(); // update game quit flag - this shouldn't be called all the time, as it's a virtual function + } +} + +void ToonEngine::doFrame() { + + if (_gameState->_inInventory) { + renderInventory(); + } else { + render(); + + int32 currentTimer = _system->getMillis(); +// Strangerke - Commented (not used) +// int32 elapsedTime = currentTimer - _oldTimer; + + update(currentTimer - _oldTimer); + _oldTimer = currentTimer; + _oldTimer2 = currentTimer; + } + parseInput(); +} + +enum MainMenuSelections { + MAINMENUHOTSPOT_NONE = 0, + MAINMENUHOTSPOT_START = 1, + MAINMENUHOTSPOT_INTRO = 2, + MAINMENUHOTSPOT_LOADGAME = 3, + MAINMENUHOTSPOT_HOTKEYS = 4, + MAINMENUHOTSPOT_CREDITS = 5, + MAINMENUHOTSPOT_QUIT = 6, + MAINMENUHOTSPOT_HOTKEYSCLOSE = 7 +}; + +enum MainMenuMasks { + MAINMENUMASK_BASE = 1, + MAINMENUMASK_HOTKEYS = 2, + MAINMENUMASK_EVERYWHERE = 3 +}; + +struct MainMenuFile { + int menuMask; + int id; + const char *animationFile; + int animateOnFrame; +}; + +#define MAINMENU_ENTRYCOUNT 12 +static const MainMenuFile mainMenuFiles[] = { + { MAINMENUMASK_BASE, MAINMENUHOTSPOT_START, "STARTBUT.CAF", 0 }, // "Start" button + { MAINMENUMASK_BASE, MAINMENUHOTSPOT_INTRO, "INTROBUT.CAF", 0 }, // "Intro" button + { MAINMENUMASK_BASE, MAINMENUHOTSPOT_LOADGAME, "LOADBUT.CAF", 0 }, // "Load Game" button + { MAINMENUMASK_BASE, MAINMENUHOTSPOT_HOTKEYS, "HOTBUT.CAF", 0 }, // "Hot Keys" button + { MAINMENUMASK_BASE, MAINMENUHOTSPOT_CREDITS, "CREDBUT.CAF", 0 }, // "Credits" button + { MAINMENUMASK_BASE, MAINMENUHOTSPOT_QUIT, "QUITBUT.CAF", 0 }, // "Quit" button + { MAINMENUMASK_BASE, MAINMENUHOTSPOT_NONE, "LEGALTXT.CAF", 0 }, // Legal Text + + { MAINMENUMASK_EVERYWHERE, MAINMENUHOTSPOT_NONE, "TOONGLOW.CAF", 6 }, // Clown glow + { MAINMENUMASK_EVERYWHERE, MAINMENUHOTSPOT_NONE, "TOONSTRK.CAF", 6 }, // Toonstruck title + { MAINMENUMASK_EVERYWHERE, MAINMENUHOTSPOT_NONE, "EYEGLOW.CAF", 4 }, // Clown eye glow + { MAINMENUMASK_EVERYWHERE, MAINMENUHOTSPOT_NONE, "PROPHEAD.CAF", 4 }, // Clown propellor head + { MAINMENUMASK_HOTKEYS, MAINMENUHOTSPOT_HOTKEYSCLOSE, "HOTKEYS.CAF", 0 } // Hotkeys display - clicking on it will close hotkeys +}; + +struct MainMenuEntry { + int menuMask; + int id; + Animation *animation; + Common::Rect rect; + int animateOnFrame; + int animateCurFrame; + int activeFrame; +}; + +bool ToonEngine::showMainmenu(bool &loadedGame) { + Picture *mainmenuPicture = new Picture(this); + mainmenuPicture->loadPicture("TITLESCR.CPS", true); + mainmenuPicture->setupPalette(); + + MainMenuEntry entries[MAINMENU_ENTRYCOUNT]; + + for (int entryNr = 0; entryNr < MAINMENU_ENTRYCOUNT; entryNr++) { + entries[entryNr].menuMask = mainMenuFiles[entryNr].menuMask; + entries[entryNr].id = mainMenuFiles[entryNr].id; + entries[entryNr].animation = new Animation(this); + entries[entryNr].animation->loadAnimation(mainMenuFiles[entryNr].animationFile); + if (entries[entryNr].id != MAINMENUHOTSPOT_NONE) + entries[entryNr].rect = entries[entryNr].animation->getRect(); + entries[entryNr].animateOnFrame = mainMenuFiles[entryNr].animateOnFrame; + entries[entryNr].animateCurFrame = 0; + entries[entryNr].activeFrame = 0; + } + + setCursor(1); + + bool doExit = false; + bool exitGame = false; + int clickingOn, clickRelease; + int menuMask = MAINMENUMASK_BASE; + AudioStreamInstance *mainmenuMusic = NULL; + bool musicPlaying = false; + + while (!doExit) { + clickingOn = MAINMENUHOTSPOT_NONE; + clickRelease = false; + + if (!musicPlaying) { + Common::SeekableReadStream *mainmenuMusicFile = resources()->openFile("misc/BR091013.MUS"); + mainmenuMusic = new AudioStreamInstance(_audioManager, _mixer, mainmenuMusicFile, true); + mainmenuMusic->play(false); + musicPlaying = true; + } + + while (!clickRelease) { + mainmenuPicture->draw(*_mainSurface, 0, 0, 0, 0); + + for (int entryNr = 0; entryNr < MAINMENU_ENTRYCOUNT; entryNr++) { + if (entries[entryNr].menuMask & menuMask) { + if (entries[entryNr].animateOnFrame) { + entries[entryNr].animateCurFrame++; + if (entries[entryNr].animateOnFrame <= entries[entryNr].animateCurFrame) { + entries[entryNr].activeFrame++; + if (entries[entryNr].activeFrame >= entries[entryNr].animation->_numFrames) + entries[entryNr].activeFrame = 0; + entries[entryNr].animateCurFrame = 0; + } + } + int32 frameNr = entries[entryNr].activeFrame; + if ((entries[entryNr].id == clickingOn) && (clickingOn != MAINMENUHOTSPOT_NONE)) + frameNr = 1; + entries[entryNr].animation->drawFrame(*_mainSurface, frameNr, 0, 0); + } + } + + parseInput(); + copyToVirtualScreen(true); + _system->delayMillis(17); + + if (_mouseButton & 1) { + // left mouse button pushed down + clickingOn = MAINMENUHOTSPOT_NONE; + for (int entryNr = 0; entryNr < MAINMENU_ENTRYCOUNT; entryNr++) { + if (entries[entryNr].menuMask & menuMask) { + if (entries[entryNr].id != MAINMENUHOTSPOT_NONE) { + if (entries[entryNr].rect.contains(_mouseX, _mouseY)) + clickingOn = entries[entryNr].id; + } + } + } + } else { + // left mouse button released/not pushed down + if (clickingOn != MAINMENUHOTSPOT_NONE) + clickRelease = true; + } + if (_shouldQuit) { + clickingOn = MAINMENUHOTSPOT_NONE; + clickRelease = true; + doExit = true; + } + } + + if (clickingOn != MAINMENUHOTSPOT_NONE) { + _audioManager->playSFX(10, 128, true); + } + + switch (clickingOn) { + case MAINMENUHOTSPOT_HOTKEYS: + menuMask = MAINMENUMASK_HOTKEYS; + continue; + case MAINMENUHOTSPOT_HOTKEYSCLOSE: + menuMask = MAINMENUMASK_BASE; + continue; + } + + if (musicPlaying) { + //stop music + mainmenuMusic->stop(false); + musicPlaying = false; + } + + + + switch (clickingOn) { + case MAINMENUHOTSPOT_START: + // Start game (actually exit main menu) + loadedGame = false; + doExit = true; + break; + case MAINMENUHOTSPOT_INTRO: + // Play intro movies + getMoviePlayer()->play("MISC/209_1M.SMK", 0x10); + getMoviePlayer()->play("MISC/209_2M.SMK", 0x10); + getMoviePlayer()->play("MISC/209_3M.SMK", 0x10); + break; + case MAINMENUHOTSPOT_LOADGAME: + doExit = loadGame(-1); + loadedGame = doExit; + exitGame = false; + break; + case MAINMENUHOTSPOT_CREDITS: + // Play credits movie + getMoviePlayer()->play("MISC/CREDITS.SMK", 0x0); + break; + case MAINMENUHOTSPOT_QUIT: + exitGame = true; + doExit = true; + break; + } + } + + return !exitGame; +} + +Common::Error ToonEngine::run() { + + if (!loadToonDat()) + return Common::kUnknownError; + + g_eventRec.registerRandomSource(_rnd, "toon"); + + initGraphics(640, 400, true); + init(); + + // do we need to load directly a game? + bool loadedGame = false; + int32 slot = ConfMan.getInt("save_slot"); + if (slot > -1) { + loadedGame = loadGame(slot); + } + + if (!loadedGame) { + + // play producer intro + getMoviePlayer()->play("MISC/VIELOGOM.SMK", 0x10); + + // show mainmenu + if (!showMainmenu(loadedGame)) { + return Common::kNoError; + } + } + + //loadScene(17); + //loadScene(37); + if (!loadedGame) { + newGame(); + } + +// Strangerke - Commented (not used) +// int32 oldTimer = _system->getMillis(); + while (!_shouldQuit && _gameState->_currentScene != -1) + doFrame(); + return Common::kNoError; +} + +ToonEngine::ToonEngine(OSystem *syst, const ADGameDescription *gameDescription) + : Engine(syst), _gameDescription(gameDescription), _language(gameDescription->language) { + _system = syst; + _tickLength = 16; + _currentMask = 0; + _currentPicture = 0; + _roomScaleData = 0; + _shadowLUT = 0; + _showConversationText = true; + _isDemo = _gameDescription->flags & ADGF_DEMO; + + DebugMan.addDebugChannel(kDebugAnim, "Anim", "Animation debug level"); + DebugMan.addDebugChannel(kDebugCharacter, "Character", "Character debug level"); + DebugMan.addDebugChannel(kDebugAudio, "Audio", "Audio debug level"); + DebugMan.addDebugChannel(kDebugHotspot, "Hotspot", "Hotspot debug level"); + DebugMan.addDebugChannel(kDebugFont, "Font", "Font debug level"); + DebugMan.addDebugChannel(kDebugPath, "Path", "Path debug level"); + DebugMan.addDebugChannel(kDebugMovie, "Movie", "Movie debug level"); + DebugMan.addDebugChannel(kDebugPicture, "Picture", "Picture debug level"); + DebugMan.addDebugChannel(kDebugResource, "Resource", "Resource debug level"); + DebugMan.addDebugChannel(kDebugState, "State", "State debug level"); + DebugMan.addDebugChannel(kDebugTools, "Tools", "Tools debug level"); + DebugMan.addDebugChannel(kDebugText, "Text", "Text debug level"); + + switch (_language) { + case Common::EN_GRB: + case Common::EN_USA: + case Common::EN_ANY: + _gameVariant = 0; + break; + case Common::FR_FRA: + _gameVariant = 1; + break; + case Common::DE_DEU: + _gameVariant = 2; + break; + case Common::RU_RUS: + _gameVariant = 3; + break; + case Common::ES_ESP: + _gameVariant = 4; + break; + default: + // 0 - english + _gameVariant = 0; + break; + } +} + +ToonEngine::~ToonEngine() { + +} + +void ToonEngine::flushPalette() { + + uint8 vmpalette[1024]; + for (int32 i = 0; i < 256; i++) { + vmpalette[i*4+0] = _finalPalette[i*3+0]; + vmpalette[i*4+1] = _finalPalette[i*3+1]; + vmpalette[i*4+2] = _finalPalette[i*3+2]; + vmpalette[i*4+3] = 0; + } + _system->setPalette(vmpalette, 0, 256); +} +void ToonEngine::setPaletteEntries(uint8 *palette, int32 offset, int32 num) { + memcpy(_finalPalette + offset * 3, palette, num * 3); + uint8 vmpalette[1024]; + for (int32 i = 0; i < num; i++) { + vmpalette[i*4+0] = palette[i*3+0]; + vmpalette[i*4+1] = palette[i*3+1]; + vmpalette[i*4+2] = palette[i*3+2]; + vmpalette[i*4+3] = 0; + } + _system->setPalette(vmpalette, offset, num); +} + +void ToonEngine::simpleUpdate(bool waitCharacterToTalk) { + int32 elapsedTime = _system->getMillis() - _oldTimer2; + _oldTimer2 = _system->getMillis(); + _oldTimer = _oldTimer2; + + if (!_audioManager->voiceStillPlaying() && !waitCharacterToTalk) { + _currentTextLine = 0; + _currentTextLineId = -1; + } + + updateCharacters(elapsedTime); + updateAnimationSceneScripts(elapsedTime); + updateTimer(elapsedTime); + _animationManager->update(elapsedTime); + _audioManager->updateAmbientSFX(); + render(); + + +} + +void ToonEngine::fixPaletteEntries(uint8 *palette, int num) { + // some color values are coded on 6bits ( for old 6bits DAC ) + for (int32 i = 0; i < num * 3; i++) { + int32 a = palette[i]; + a = a * 4; + if (a > 255) + a = 255; + palette[i] = a; + } +} + +// adapted from KyraEngine +void ToonEngine::updateAnimationSceneScripts(int32 timeElapsed) { + + + static int32 numReentrant = 0; + numReentrant++; + +// Strangerke - Commented (not used) +// uint32 nextTime = _system->getMillis() + _tickLength; + const int startScript = _lastProcessedSceneScript; + + _updatingSceneScriptRunFlag = true; + + do { + if (_sceneAnimationScripts[_lastProcessedSceneScript]._lastTimer <= _system->getMillis() && + !_sceneAnimationScripts[_lastProcessedSceneScript]._frozen && !_sceneAnimationScripts[_lastProcessedSceneScript]._frozenForConversation) { + _animationSceneScriptRunFlag = true; + + while (_animationSceneScriptRunFlag && _sceneAnimationScripts[_lastProcessedSceneScript]._lastTimer <= _system->getMillis() && !_shouldQuit) { + if (!_script->run(&_sceneAnimationScripts[_lastProcessedSceneScript]._state)) + _animationSceneScriptRunFlag = false; + + //waitForScriptStep(); + + if (_sceneAnimationScripts[_lastProcessedSceneScript]._frozen || _sceneAnimationScripts[_lastProcessedSceneScript]._frozenForConversation) + break; + } + + } + + if (!_script->isValid(&_sceneAnimationScripts[_lastProcessedSceneScript]._state)) { + _script->start(&_sceneAnimationScripts[_lastProcessedSceneScript]._state, 9 + _lastProcessedSceneScript); + _animationSceneScriptRunFlag = false; + } + + ++_lastProcessedSceneScript; + if (_lastProcessedSceneScript >= state()->_locations[state()->_currentScene]._numSceneAnimations) + _lastProcessedSceneScript = 0; + + } while (_lastProcessedSceneScript != startScript && !_shouldQuit); + + + _updatingSceneScriptRunFlag = false; + numReentrant--; + +} + +void ToonEngine::loadScene(int32 SceneId, bool forGameLoad) { + char temp[256]; + char temp2[256]; + + + _firstFrame = true; + + _gameState->_lastVisitedScene = _gameState->_currentScene; + _gameState->_currentScene = SceneId; + + _saveBufferStream->seek(0); + + if (SceneId == -1) { + // this scene -1 is loaded at the very end of the game + getAudioManager()->stopMusic(); + getMoviePlayer()->play("CREDITS.SMK"); + return; + } + + // find out in what chapter we are (the script function ProcessToNextChapter is actually not called ) + // the location flag has the chapter info in it + int32 flag = _gameState->_locations[SceneId]._flags; + if (flag) { + _gameState->_currentChapter = 0; + do { + _gameState->_currentChapter++; + flag >>= 1; + } while ((flag & 1) == 0); + } + + for (int32 i = 0; i < 8; i++) { + if (_characters[i]) _characters[i]->setFlag(0); + } + _drew->playStandingAnim(); + _drew->setVisible(true); + + // hide flux in chapter 2 + if (_gameState->_currentChapter == 1) { + _flux->playStandingAnim(); + _flux->setVisible(true); + } else { + _flux->setVisible(false); + } + + _lastMouseButton = 0; + _mouseButton = 0; + _currentHotspotItem = 0; + + if (!forGameLoad) { + _gameState->_sackVisible = true; + _gameState->_inCloseUp = false; + _gameState->_inConversation = false; + _gameState->_inInventory = false; + _gameState->_inCutaway = false; + _gameState->_currentScrollValue = 0; + _gameState->_currentScrollLock = false; + _gameState->_inCloseUp = false; + } + + if (_gameState->_mouseState >= 0) + addItemToInventory(_gameState->_mouseState); + + _gameState->_mouseState = -1; + _mouseButton = 0; + _lastMouseButton = 0x3; + + + // load package + strcpy(temp, createRoomFilename(Common::String::printf("%s.pak", _gameState->_locations[_gameState->_currentScene]._name).c_str()).c_str()); + resources()->openPackage(temp, true); + + strcpy(temp, state()->_locations[SceneId]._name); + strcat(temp, ".npp"); + loadAdditionalPalette(temp, 0); + + strcpy(temp, state()->_locations[SceneId]._name); + strcat(temp, ".np2"); + loadAdditionalPalette(temp, 1); + + strcpy(temp, state()->_locations[SceneId]._name); + strcat(temp, ".cup"); + loadAdditionalPalette(temp, 2); + + // load artwork + strcpy(temp, state()->_locations[SceneId]._name); + strcat(temp, ".cps"); + _currentPicture = new Picture(this); + _currentPicture->loadPicture(temp); + _currentPicture->setupPalette(); + + strcpy(temp, state()->_locations[SceneId]._name); + strcat(temp, ".msc"); + _currentMask = new Picture(this); + if (_currentMask->loadPicture(temp)) + _pathFinding->init(_currentMask); + + strcpy(temp, state()->_locations[SceneId]._name); + strcat(temp, ".tre"); + _roomTexts = new TextResource(this); + _roomTexts->loadTextResource(temp); + + + strcpy(temp, state()->_locations[SceneId]._name); + strcat(temp, ".dat"); + uint32 fileSize; + uint8 *sceneData = resources()->getFileData(temp, &fileSize); + if (sceneData) { + _roomScaleData = new uint8[fileSize]; + memcpy(_roomScaleData, sceneData, fileSize); + } + + strcpy(temp, state()->_locations[SceneId]._name); + strcat(temp, ".svi"); + strcpy(temp2, createRoomFilename(Common::String::printf("%s.svl", _gameState->_locations[_gameState->_currentScene]._name).c_str()).c_str()); + _audioManager->loadAudioPack(1, temp, temp2); + strcpy(temp, state()->_locations[SceneId]._name); + strcpy(temp2, state()->_locations[SceneId]._name); + strcat(temp, ".sei"); + strcat(temp2, ".sel"); + _audioManager->loadAudioPack(3, temp, temp2); + + strcpy(temp, state()->_locations[SceneId]._name); + strcat(temp, ".ric"); + if (state()->_locations[SceneId]._flags & 0x40) { + strcpy(temp2, state()->_locations[SceneId]._cutaway); + strcat(temp2, ".ric"); + } else { + strcpy(temp2, ""); + } + _hotspots->LoadRif(temp, temp2); + restoreRifFlags(_gameState->_currentScene); + + + strcpy(temp, state()->_locations[SceneId]._name); + strcat(temp, ".cnv"); + uint32 convfileSize; + uint8 *convData = resources()->getFileData(temp, &convfileSize); + if (convData) { + assert(convfileSize < 4096 * sizeof(int16)); + memcpy(_conversationData , convData, convfileSize); + prepareConversations(); + } + + // load script + strcpy(temp, state()->_locations[SceneId]._name); + strcat(temp, ".emc"); + + _oldTimer = _system->getMillis(); + _oldTimer2 = _oldTimer; + + // fix the weird scaling issue during one frame when entering new scene + _drew->update(0); + _flux->update(0); + + + _script->load(temp, &_scriptData, &_script_func->_opcodes); + _script->init(&_scriptState[0], &_scriptData); + _script->init(&_scriptState[1], &_scriptData); + _script->init(&_scriptState[2], &_scriptData); + _script->init(&_scriptState[3], &_scriptData); + + //_script->RoomScript->Decompile("decomp.txt"); + //RoomScript->Decompile2("decomp2.txt"); + + for (int i = 0; i < state()->_locations[SceneId]._numSceneAnimations; i++) { + _sceneAnimationScripts[i]._data = &_scriptData; + _script->init(&_sceneAnimationScripts[i]._state, _sceneAnimationScripts[i]._data); + if (!forGameLoad) { + _script->start(&_sceneAnimationScripts[i]._state, 9 + i); + _sceneAnimationScripts[i]._lastTimer = getSystem()->getMillis(); + _sceneAnimationScripts[i]._frozen = false; + _sceneAnimationScripts[i]._frozenForConversation = false; + } + } + + // launch mus +#if 0 + SoundManager.StopMusic(); + SoundManager.UnloadMusic(); + SoundManager.LoadMusic(GameLocations[Scene].name, GameLocations[Scene].mus); + SoundManager.PlayMusic(); +#endif + + playRoomMusic(); + + _lastProcessedSceneScript = 0; + _gameState->_locations[SceneId]._visited = true; + + + setupGeneralPalette(); + createShadowLUT(); + + state()->_mouseHidden = false; + + if (!forGameLoad) { + + _script->start(&_scriptState[0], 0); + + while (_script->run(&_scriptState[0])) + waitForScriptStep(); + + _script->start(&_scriptState[0], 8); + + while (_script->run(&_scriptState[0])) + waitForScriptStep(); + + if (_gameState->_nextSpecialEnterX != -1 && _gameState->_nextSpecialEnterY != -1) { + _drew->setPosition(_gameState->_nextSpecialEnterX, _gameState->_nextSpecialEnterY); + _gameState->_nextSpecialEnterX = -1; + _gameState->_nextSpecialEnterY = -1; + } + + _script->start(&_scriptState[0], 3); + + while (_script->run(&_scriptState[0])) + waitForScriptStep(); + + _script->start(&_scriptState[0], 4); + + while (_script->run(&_scriptState[0])) + waitForScriptStep(); + + } +} + +void ToonEngine::setupGeneralPalette() { + setPaletteEntries(_additionalPalette1, 232, 23); + setPaletteEntries(_universalPalette, 200, 32); + setPaletteEntries(_fluxPalette, 192, 8); + + if (_drew) + _drew->setupPalette(); +} + +void ToonEngine::loadAdditionalPalette(Common::String fileName, int32 mode) { + + uint32 size = 0; + uint8 *palette = resources()->getFileData(fileName, &size); + if (!palette) + return; + + switch (mode) { + case 0: + memcpy(_additionalPalette1, palette, 69); + fixPaletteEntries(_additionalPalette1, 23); + break; + case 1: + memcpy(_additionalPalette2, palette, 69); + fixPaletteEntries(_additionalPalette2, 23); + break; + case 2: + memcpy(_cutawayPalette, palette, 768); + fixPaletteEntries(_cutawayPalette, 256); + break; + case 3: + memcpy(_universalPalette, palette, 96); + fixPaletteEntries(_universalPalette, 32); + break; + case 4: + memcpy(_fluxPalette, palette, 24); + fixPaletteEntries(_fluxPalette , 8); + break; + default: + warning("loadAdditionalPalette() - Unknown mode"); + } +} + +void ToonEngine::initChapter() { + + EMCData data; + EMCState status; + memset(&data, 0, sizeof(data)); + memset(&status, 0, sizeof(status)); + + _script = new EMCInterpreter(this); + + _script->load("_START01.EMC", &data, &_script_func->_opcodes); + _script->init(&status, &data); + _script->start(&status, 0); + while (_script->run(&status)) + waitForScriptStep(); + + setupGeneralPalette(); + +} + +void ToonEngine::loadCursor() { + _cursorAnimation = new Animation(this); + _cursorAnimation->loadAnimation("MOUSE.CAF"); + _cursorAnimationInstance = _animationManager->createNewInstance(kAnimationCursor); + _cursorAnimationInstance->setAnimation(_cursorAnimation); + _cursorAnimationInstance->setVisible(true); + _cursorAnimationInstance->setFrame(0); + _cursorAnimationInstance->setAnimationRange(0, 0); + _cursorAnimationInstance->setFps(8); + + setCursor(5); +} + +void ToonEngine::setCursor(int32 type, bool inventory, int32 offsetX, int offsetY) { + + static const int32 offsets[] = { + 0, 1, 1, 6, 7, 1, 8, 10, 18, 10, + 28, 8, 36, 10, 46, 10, 56, 10, 66, 10, + 76, 10, 86, 10, 96, 10, 106, 10, 116, 10, + 126, 10 + }; + + if (!inventory) { + _cursorAnimationInstance->setAnimation(_cursorAnimation); + _cursorAnimationInstance->setAnimationRange(offsets[type * 2 + 0], offsets[type * 2 + 0] + offsets[type * 2 + 1] - 1); + _cursorAnimationInstance->playAnimation(); + } else { + _cursorAnimationInstance->setAnimation(_inventoryIcons); + _cursorAnimationInstance->setAnimationRange(type, type); + _cursorAnimationInstance->playAnimation(); + } + + _cursorOffsetX = offsetX; + _cursorOffsetY = offsetY; +} + +void ToonEngine::setSceneAnimationScriptUpdate(bool enable) { + _animationSceneScriptRunFlag = enable; +} + +bool ToonEngine::isUpdatingSceneAnimation() { + return _updatingSceneScriptRunFlag; +} + +int32 ToonEngine::getCurrentUpdatingSceneAnimation() { + return _lastProcessedSceneScript; +} + +int32 ToonEngine::randRange(int32 minStart, int32 maxStart) { + return _rnd.getRandomNumberRng(minStart, maxStart); +} + +int32 ToonEngine::runEventScript(int32 x, int32 y, int32 mode, int32 id, int32 scriptId) { + + if (_currentScriptRegion >= 4) + return 0; + + EMCState *status = &_scriptState[_currentScriptRegion]; + _script->init(status, &_scriptData); + + // setup registers + status->regs[0] = x; + status->regs[1] = y; + status->regs[2] = 0; + status->regs[3] = 0; + status->regs[4] = _gameState->_mouseState; // + status->regs[5] = 0; + status->regs[6] = scriptId; + status->regs[7] = mode; + status->regs[8] = id; + + _currentScriptRegion++; + + _script->start(status, 1); + while (_script->run(status)) + waitForScriptStep(); + + _currentScriptRegion--; + + return status->regs[2]; +} + +void ToonEngine::clickEvent() { + bool leftButton = false; + bool rightButton = false; + + if ((_lastMouseButton & 0x1) == 0 && (_mouseButton & 0x1) == 1) + leftButton = true; + if ((_lastMouseButton & 0x2) == 0 && (_mouseButton & 0x2) == 2) + rightButton = true; + + _lastMouseButton = _mouseButton; + if (!leftButton && !rightButton) + return; + + if (_gameState->_sackVisible) { + if (_mouseX > 0 && _mouseX < 40 && _mouseY > 356 && _mouseY < 396) { + if (_gameState->_mouseState >= 0 && !rightButton) { + addItemToInventory(_gameState->_mouseState); + setCursor(0, false, 0, 0); + _currentHotspotItem = 0; + return; + } else { + showInventory(); + } + return; + } + } + + // with inventory + if (rightButton && _gameState->_mouseState >= 0) { + addItemToInventory(_gameState->_mouseState); + setCursor(0, false, 0, 0); + _currentHotspotItem = 0; + return; + } + + + int32 mouseX = _mouseX; + if (_gameState->_inCutaway) { + mouseX += 1280; + } + + // find hotspot + int32 hot = _hotspots->Find(mouseX + state()->_currentScrollValue , _mouseY); + HotspotData *currentHot = 0; + if (hot > -1) { + currentHot = _hotspots->Get(hot); + } + + if (_currentHotspotItem == -3) { + if (_gameState->_mouseState <= 0) { + if (leftButton) + createMouseItem(104); + else + characterTalk(518); + } + } + if (_currentHotspotItem == -4) { + if (_gameState->_mouseState >= 0) { + if (leftButton) + if (!handleInventoryOnInventory(0, _gameState->_mouseState)) { + playSoundWrong(); + } + return; + } + } + + + if (!currentHot) { + int32 xx, yy; + + if (_gameState->_inCutaway || _gameState->_inInventory || _gameState->_inCloseUp) + return; + + if (_pathFinding->findClosestWalkingPoint(_mouseX + _gameState->_currentScrollValue , _mouseY, &xx, &yy)) + _drew->walkTo(xx, yy); + return; + } + + int commandId = 0; + if (_gameState->_mouseState < 0) { + // left or right click + if (rightButton) + commandId = 2 + 8; + else + commandId = 0 + 8; + } else { + commandId = 2 * (_gameState->_mouseState - 1) + 16; + } + + _drew->stopWalk(); + + int16 command = currentHot->getData(commandId); + int16 argument = currentHot->getData(commandId + 1); + int16 priority = currentHot->getPriority(); +// Strangerke - Commented (not used) +// int16 ref = currentHot->getRef(); +// int16 pad1 = currentHot->getData(6); + + if (!_gameState->_inCutaway && !_gameState->_inCloseUp) { + if (leftButton && (currentHot->getData(4) != 2 || _gameState->_mouseState >= 0) && currentHot->getData(5) != -1) { + if (currentHot->getData(5)) { + if (!_drew->walkTo(currentHot->getData(5), currentHot->getData(6))) { + // walk was canceled ? + return; + } + } else { + if (!_drew->walkTo(_mouseX, _mouseY)) { + // walk was canceled ? + return; + } + } + } + } + + int32 result = 0; + + switch (command) { + case 1: + sayLines(1, argument); + break; + case 2: + result = runEventScript(_mouseX, _mouseY, command, argument, priority); + break; + case 3: + runEventScript(_mouseX, _mouseY, command, argument, priority); + result = 0; + break; + case 4: + playSFX(argument, 128); + break; + case 5: + break; + case 6: + createMouseItem(argument); + currentHot->setData(7, -1); + break; + case 7: + // switch to CloseUp +// Strangerke - Commented (not used) +// int closeup = 1; + break; + case 8: + // face flux + sayLines(1, argument); + break; + case 9: + case 10: +// Strangerke - Commented (not used) +// if (rand() % 1 == 1) { +// } else { +// } + // setFluxFacingPoint(x,y) + sayLines(2, argument); + break; + case 11: + sayLines(3, argument); + break; + default: + playSoundWrong(); + return; + } + + if (result == 3) { + int32 val = _scriptState[_currentScriptRegion].regs[4]; + currentHot->setData(4, currentHot->getData(4) & val); + } + if (result == 2 || result == 3) { + int32 val = _scriptState[_currentScriptRegion].regs[6]; + currentHot->setData(7, val); + } + + if (result == 1) { + int32 val = _scriptState[_currentScriptRegion].regs[4]; + currentHot->setData(4, currentHot->getData(4) & val); + } + +} + +void ToonEngine::selectHotspot() { + int32 x1 = 0; + int32 x2 = 0; + int32 y1 = 0; + int32 y2 = 0; + + int32 mouseX = _mouseX; + + if (_gameState->_inCutaway) + mouseX += 1280; + + if (_gameState->_sackVisible) { + if (_mouseX > 0 && _mouseX < 40 && _mouseY > 356 && _mouseY < 396) { + _currentHotspotItem = -2; + + if (_gameState->_mouseState < 0) { + int mode = 3; + setCursor(mode); + } else { + setCursor(_gameState->_mouseState, true, -18, -14); + } + + return; + } + } + + if (_gameState->_mouseState > 0) { + // picked drew? + getDrew()->getAnimationInstance()->getRect(&x1, &y1, &x2, &y2); + if (_mouseX + _gameState->_currentScrollValue >= x1 && _mouseX + _gameState->_currentScrollValue <= x2 && _mouseY >= y1 && _mouseY <= y2) { + _currentHotspotItem = -4; + return; + } + } + + if (getFlux()->getVisible()) { + getFlux()->getAnimationInstance()->getRect(&x1, &y1, &x2, &y2); + if (_mouseX + _gameState->_currentScrollValue >= x1 && _mouseX + _gameState->_currentScrollValue <= x2 && _mouseY >= y1 && _mouseY <= y2) { + _currentHotspotItem = -3; + + if (_gameState->_mouseState < 0) { + int mode = 3; + setCursor(mode); + } else { + setCursor(_gameState->_mouseState, true, -18, -14); + } + + return; + } + } + + int32 hot = _hotspots->Find(mouseX + state()->_currentScrollValue, _mouseY); + if (hot != -1) { + HotspotData *hotspot = _hotspots->Get(hot); + int32 item = hotspot->getData(14); + if (hotspot->getType() == 3) + item += 2000; + + // update palette based on ticks if we're in "use from inventory mode" + if (_gameState->_mouseState >= 0) { + + int32 tick = _system->getMillis() / _tickLength; + int32 animReverse = tick & 0x10; + int32 animStep = tick & 0xf; + + byte color[3]; + if (animReverse == 0) { + color[0] = 16 * animStep; + color[1] = 0; + color[2] = 0; + } else { + color[0] = 16 * (15 - animStep); + color[1] = 0; + color[2] = 0; + } + setPaletteEntries(color, 255, 1); + } + +#if 0 + if (item == _currentHotspotItem) + return; +#endif + _currentHotspotItem = item; + if (_gameState->_mouseState < 0) { + int mode = hotspot->getMode(); + setCursor(mode); + } else { + setCursor(_gameState->_mouseState, true, -18, -14); + } + } else { + _currentHotspotItem = 0; + + if (_gameState->_mouseState < 0) { + setCursor(0); + } else { + byte color[3]; + color[0] = 0; + color[1] = 0; + color[2] = 0; + setCursor(_gameState->_mouseState, true, -18, -14); + setPaletteEntries(color, 255, 1); + } + } +} + +void ToonEngine::exitScene() { + + fadeOut(5); + + // disable all scene animation + for (int32 i = 0; i < 64; i++) { + if (_sceneAnimations[i]._active) { + delete _sceneAnimations[i]._animation; + _sceneAnimations[i]._active = false; + _animationManager->removeInstance(_sceneAnimations[i]._animInstance); + _sceneAnimations[i]._animInstance = 0; + _sceneAnimations[i]._animation = 0; + } + } + for (int32 i = 0; i < 64; i++) { + _sceneAnimationScripts[i]._frozen = true; + _sceneAnimationScripts[i]._active = false; + } + + // remove all characters except drew and flux + for (int32 i = 0; i < 8; i++) { + if (_characters[i] != _drew && _characters[i] != _flux) { + if (_characters[i]) { + delete _characters[i]; + _characters[i] = 0; + } + } else { + _characters[i]->stopSpecialAnim(); + } + } + + for (int32 i = 0; i < 2; i++) { + _gameState->_timerEnabled[i] = false; + } + + // put back our item if inventory if needed + if (_gameState->_mouseState >= 0) { + addItemToInventory(_gameState->_mouseState); + _gameState->_mouseState = -1; + } + + _audioManager->killAllAmbientSFX(); + _audioManager->stopAllSfxs(); + _audioManager->stopCurrentVoice(); + _currentTextLine = 0; + _currentTextLineId = -1; + _currentTextLineCharacterId = 0; + + char temp[256]; + strcpy(temp, createRoomFilename(Common::String::printf("%s.pak", _gameState->_locations[_gameState->_currentScene]._name).c_str()).c_str()); + resources()->closePackage(temp); + + + _drew->stopWalk(); + _flux->stopWalk(); + + storeRifFlags(_gameState->_currentScene); +} + +// flip between the cutaway scene and the normal scene +void ToonEngine::flipScreens() { + _gameState->_inCloseUp = !_gameState->_inCloseUp; + + if (_gameState->_inCloseUp) { + _gameState->_currentScrollValue = 640; + setPaletteEntries(_cutawayPalette, 1, 128); + setPaletteEntries(_additionalPalette2, 232, 23); + } else { + _gameState->_currentScrollValue = 0; + _currentPicture->setupPalette(); + setupGeneralPalette(); + } + flushPalette(); +} + +void ToonEngine::fadeIn(int32 numFrames) { + for (int32 f = 0; f < numFrames; f++) { + + uint8 vmpalette[1024]; + for (int32 i = 0; i < 256; i++) { + vmpalette[i*4+0] = f * _finalPalette[i*3+0] / (numFrames - 1); + vmpalette[i*4+1] = f * _finalPalette[i*3+1] / (numFrames - 1); + vmpalette[i*4+2] = f * _finalPalette[i*3+2] / (numFrames - 1); + vmpalette[i*4+3] = 0; + } + _system->setPalette(vmpalette, 0, 256); + _system->updateScreen(); + _system->delayMillis(_tickLength); + } +} + +void ToonEngine::fadeOut(int32 numFrames) { + for (int32 f = 0; f < numFrames; f++) { + + uint8 vmpalette[1024]; + for (int32 i = 0; i < 256; i++) { + vmpalette[i*4+0] = (numFrames - f - 1) * _finalPalette[i*3+0] / (numFrames - 1); + vmpalette[i*4+1] = (numFrames - f - 1) * _finalPalette[i*3+1] / (numFrames - 1); + vmpalette[i*4+2] = (numFrames - f - 1) * _finalPalette[i*3+2] / (numFrames - 1); + vmpalette[i*4+3] = (numFrames - f - 1); + } + _system->setPalette(vmpalette, 0, 256); + _system->updateScreen(); + _system->delayMillis(_tickLength); + } +} + +void ToonEngine::initFonts() { + _fontRenderer = new FontRenderer(this); + _fontToon = new Animation(this); + _fontToon->loadAnimation("misc/toonfont.caf"); + + _fontEZ = new Animation(this); + _fontEZ->loadAnimation("misc/ezfont.caf"); +} + +void ToonEngine::drawInfoLine() { + if (_currentHotspotItem != 0 && !_gameState->_mouseHidden && !_gameState->_inConversation) { + const char *infoTool = NULL; + if (_currentHotspotItem >= 0 && _currentHotspotItem < 2000) { + infoTool = _roomTexts->getText(_currentHotspotItem); + } else if (_currentHotspotItem <= -1) { +// static const char * const specialInfoLine[] = { "Exit non defined", "Bottomless Bag", "Flux", "Drew Blanc" }; + infoTool = _specialInfoLine[-1 - _currentHotspotItem]; + } else { + int32 loc = _currentHotspotItem - 2000; + // location names are hardcoded ... + infoTool = getLocationString(loc, _gameState->_locations[loc]._visited); + } + if (infoTool) { + _fontRenderer->setFontColor(0xc8, 0xdd, 0xe3); + _fontRenderer->setFont(_fontToon); + _fontRenderer->renderText(320 + _gameState->_currentScrollValue, 398, infoTool, 5); + } + } +} + +const char *ToonEngine::getLocationString(int32 locationId, bool alreadyVisited) { + if (alreadyVisited) + return _locationDirVisited[locationId]; + else + return _locationDirNotVisited[locationId]; +} + +int32 ToonEngine::getScaleAtPoint(int32 x, int32 y) { + if (!_currentMask) + return 1024; + + int32 maskData = _currentMask->getData(x, y) & 0x1f; + return _roomScaleData[maskData+2] * 1024 / 100; +} + +int32 ToonEngine::getLayerAtPoint(int32 x, int32 y) { + if (!_currentMask) + return 0; + + int32 maskData = _currentMask->getData(x, y) & 0x1f; + return _roomScaleData[maskData+130] << 5; +} + +int32 ToonEngine::getZAtPoint(int32 x, int32 y) { + if (!_currentMask) + return 0; + return _currentMask->getData(x, y) & 0x1f; +} + +void ToonEngine::storeRifFlags(int32 location) { + + if (_gameState->_locations[location]._numRifBoxes != _hotspots->getCount()) { + _gameState->_locations[location]._numRifBoxes = _hotspots->getCount(); + } + + for (int32 i = 0; i < _hotspots->getCount(); i++) { + _gameState->_locations[location]._rifBoxesFlags[i * 2 + 0] = _hotspots->Get(i)->getData(4); + _gameState->_locations[location]._rifBoxesFlags[i * 2 + 1] = _hotspots->Get(i)->getData(7); + } +} + +void ToonEngine::restoreRifFlags(int32 location) { + if (_hotspots) { + if (!_gameState->_locations[location]._visited) { + for (int32 i = 0; i < _hotspots->getCount(); i++) { + _gameState->_locations[location]._rifBoxesFlags[i * 2 + 0] = _hotspots->Get(i)->getData(4); + _gameState->_locations[location]._rifBoxesFlags[i * 2 + 1] = _hotspots->Get(i)->getData(7); + } + _gameState->_locations[location]._numRifBoxes = _hotspots->getCount(); + } else { + if (_gameState->_locations[location]._numRifBoxes != _hotspots->getCount()) + return; + + for (int32 i = 0; i < _hotspots->getCount(); i++) { + _hotspots->Get(i)->setData(4, _gameState->_locations[location]._rifBoxesFlags[i * 2 + 0]); + _hotspots->Get(i)->setData(7, _gameState->_locations[location]._rifBoxesFlags[i * 2 + 1]); + } + } + } +} + +void ToonEngine::sayLines(int numLines, int dialogId) { + // exit conversation state + + // if (inInventory) + // character_talks(dialogid, -1, 0, 0); + // else + +#if 0 + int oldShowMouse = 0; + + if (Game.MouseHiddenCount <= 0) { + Game.MouseHiddenCount = 1; + oldShowMouse = 1; + } +#endif + + int32 currentLine = dialogId; + + for (int32 i = 0; i < numLines; i++) { + if (!characterTalk(currentLine)) + break; + + while (_audioManager->voiceStillPlaying() && !_shouldQuit) + doFrame(); + + // find next line + if (currentLine < 1000) + currentLine = _roomTexts->getNext(currentLine); + else + currentLine = _genericTexts->getNext(currentLine - 1000) + 1000; + } + +#if 0 + if (oldShowMouse) + Game.MouseHiddenCount = 0; +#endif + +} + +int32 ToonEngine::simpleCharacterTalk(int32 dialogid) { + int32 myId = 0; + +// Strangerke - Commented (not used) +#if 0 + char *myLine; + if (dialogid < 1000) { + myLine = _roomTexts->getText(dialogid); + myId = dialogid; + } else { + myLine = _genericTexts->getText(dialogid - 1000); + myId = dialogid - 1000; + } + debugC(0, 0xfff, "Talker = %d will say '%s' \n", READ_LE_UINT16(c - 2), myLine); +#endif + + if (_audioManager->voiceStillPlaying()) + _audioManager->stopCurrentVoice(); + + if (dialogid < 1000) { + myId = _roomTexts->getId(dialogid); + _audioManager->playVoice(myId, false); + } else { + myId = _genericTexts->getId(dialogid - 1000); + _audioManager->playVoice(myId, true); + } + + return 1; +} + +void ToonEngine::playTalkAnimOnCharacter(int32 animID, int32 characterId, bool talker) { + if (animID || talker) { +// Strangerke - Commented (not used) +#if 0 + if (_gameState->_inCutaway || _gameState->_inInventory) { + if (talker) { + // character talks + } + } else +#endif + if (characterId == 0) { + _drew->playAnim(animID, 0, (talker ? 8 : 0) + 2); + } else if (characterId == 1) { + // stop flux if he is walking + if (_flux->getFlag() & 1) { + _flux->stopWalk(); + } + _flux->playAnim(animID, 0, (talker ? 8 : 0) + 2); + _flux->setFlag(_flux->getFlag() | 1); + } else { + Character *character = getCharacterById(characterId); + if (character) { + character->playAnim(animID, 0, (talker ? 8 : 0) + 2); + } + } + } else { + Character *character = getCharacterById(characterId); + if (character) + character->setAnimFlag(character->getAnimFlag() | 1); + } +} + +int32 ToonEngine::characterTalk(int32 dialogid, bool blocking) { + if (blocking == false && _audioManager->voiceStillPlaying()) { + if (_currentTextLineCharacterId == 0 || _currentTextLineCharacterId == 1) { + // Drew or Flux is already talking, and this voice is not important + // skip it + return 0; + } + } + + int32 myId = 0; + char *myLine; + if (dialogid < 1000) { + myLine = _roomTexts->getText(dialogid); + myId = dialogid; + } else { + myLine = _genericTexts->getText(dialogid - 1000); + myId = dialogid - 1000; + } + + if (!myLine) + return 0; + + bool oldMouseHidden = _gameState->_mouseHidden; + if (blocking) { + _gameState->_mouseHidden = true; + } + + + // get what is before the string + int a = READ_LE_UINT16(myLine - 2); + char *b = myLine - 2 - 4 * a; + + char *c = b - 2; // v6 + int numParticipants = READ_LE_UINT16(c); // num dialogue participants + + char *e = c - 2 - 4 * numParticipants; + READ_LE_UINT16(e); + +// Strangerke - Commented (not used) +// char *g = e - 2 * f; + + // flag as talking +// Strangerke - Commented (not used) +// char *h = c; + + + // if one voice is still playing, wait ! + if (blocking) { + while (_audioManager->voiceStillPlaying() && !_shouldQuit) + doFrame(); + + char *cc = c; + Character *waitChar; + for (int32 i = 0; i < numParticipants - 1; i++) { + // listener + int32 listenerId = READ_LE_UINT16(cc - 2); + cc -= 4; + waitChar = getCharacterById(listenerId); + if (waitChar) { + while ((waitChar->getAnimFlag() & 0x10) == 0x10 && !_shouldQuit) + doFrame(); + } + + } + int32 talkerId = READ_LE_UINT16(cc - 2); + + waitChar = getCharacterById(talkerId); + if (waitChar && !_gameState->_inInventory) { + while ((waitChar->getAnimFlag() & 0x10) == 0x10 && !_shouldQuit) + doFrame(); + } + } else { + if (_audioManager->voiceStillPlaying()) + _audioManager->stopCurrentVoice(); + } + + for (int32 i = 0; i < numParticipants - 1; i++) { + // listener + int32 listenerId = READ_LE_UINT16(c - 2); + int32 listenerAnimId = READ_LE_UINT16(c - 4); + if (blocking) playTalkAnimOnCharacter(listenerAnimId, listenerId, false); + c -= 4; + } + + int32 talkerId = READ_LE_UINT16(c - 2); + int32 talkerAnimId = READ_LE_UINT16(c - 4); + + _currentTextLine = myLine; + _currentTextLineCharacterId = talkerId; + _currentTextLineId = dialogid; + + if (blocking) { + Character *character = getCharacterById(talkerId); + if (character) + character->setTalking(true); + + playTalkAnimOnCharacter(talkerAnimId, talkerId, true); + + // set once more the values, they may have been overwritten when the engine + // waits for the character to be ready. + _currentTextLine = myLine; + _currentTextLineCharacterId = talkerId; + _currentTextLineId = dialogid; + + + } else { + Character *character = getCharacterById(talkerId); + if (character) + character->stopSpecialAnim(); + } + + debugC(0, 0xfff, "Talker = %d (num participants : %d) will say '%s'", talkerId , numParticipants, myLine); + + + getTextPosition(talkerId, &_currentTextLineX, &_currentTextLineY); + + if (dialogid < 1000) { + myId = _roomTexts->getId(dialogid); + _audioManager->playVoice(myId, false); + } else { + myId = _genericTexts->getId(dialogid - 1000); + _audioManager->playVoice(myId, true); + } + + if (blocking) { + while (_audioManager->voiceStillPlaying() && !_shouldQuit) + doFrame(); + _gameState->_mouseHidden = oldMouseHidden && _gameState->_mouseHidden; + + Character *character = getCharacterById(talkerId); + if (character) + character->setTalking(false); + } + + + return 1; +} + +void ToonEngine::haveAConversation(int32 convId) { + setCursor(0); + + _gameState->_inConversation = true; + _gameState->_showConversationIcons = false; + _gameState->_exitConversation = false; + _gameState->_sackVisible = false; + Conversation *conv = &state()->_conversationState[convId]; + _gameState->_currentConversationId = convId; + + // change the music to the "conversation" music if needed. + playRoomMusic(); + + if (conv->_enable) { + // fix dialog script based on new flags + for (int32 i = 0; i < 10; i++) { + if (conv->state[i]._data2 == 1 || conv->state[i]._data2 == 3) { + if (getConversationFlag(_gameState->_currentScene, conv->state[i]._data3)) + conv->state[i]._data2 = 1; + else + conv->state[i]._data2 = 3; + } + } + + // if current voice stream sub 15130 + processConversationClick(conv , 2); + doFrame(); + } + + + _mouseButton = 0; + _gameState->_firstConverstationLine = true; + + while (!_gameState->_exitConversation && !_shouldQuit) { + _gameState->_mouseHidden = false; + _gameState->_showConversationIcons = true; + int32 oldMouseButton = _mouseButton; + while (!_shouldQuit) { + doFrame(); + + if (_mouseButton != 0) { + if (!oldMouseButton) + break; + } else { + oldMouseButton = 0; + } + } + int selected = -1; + int a = 0; + for (int i = 0; i < 10; i++) { + if (conv->state[i]._data2 == 1) { + if (_mouseX > 50 + a * 60 && _mouseX < 100 + a * 60 && _mouseY >= 336 && _mouseY <= 386) { + selected = i; + break; + } + a++; + } + } + if (_shouldQuit) return; + _gameState->_showConversationIcons = false; + _gameState->_mouseHidden = 1; + + + if (selected < 0 || selected == 1 || selected == 3) { + if (_gameState->_firstConverstationLine) + processConversationClick(conv, 3); + else + processConversationClick(conv, 1); + break; + } else { + processConversationClick(conv, selected); + } + } + +// Strangerke - Commented (not used) +// int cur = 0; + + for (int i = 0; i < 10; i++) { + if (conv->state[i]._data2 == 2) { + if (i != 3) + conv->state[i]._data2 = 1; + } + } + + _gameState->_exitConversation = false; + _gameState->_inConversation = false; + _gameState->_currentConversationId = -1; + _gameState->_mouseHidden = false; + _gameState->_sackVisible = true; + + // switch back to original music + playRoomMusic(); + +} + +void ToonEngine::drawConversationIcons() { + if (!_gameState->_inConversation || !_gameState->_showConversationIcons) + return; + int32 aa = 50 + _gameState->_currentScrollValue; + for (int32 i = 0; i < 10; i++) { + if (_gameState->_conversationState[_gameState->_currentConversationId].state[i]._data2 == 1) { + _dialogIcons->drawFrame(*_mainSurface, (i + _gameState->_currentScene) & 7, aa, 336); + _dialogIcons->drawFrame(*_mainSurface, 7 + _gameState->_conversationState[_gameState->_currentConversationId].state[i]._data3, aa, 339); + aa += 60; + } + } +} + +void ToonEngine::prepareConversations() { + Conversation *allConvs = _gameState->_conversationState; + for (int32 i = 0; i < 60; i++) { + + allConvs[i].state[0]._data2 = 1; + if (!allConvs[i].state[0]._data3) { + allConvs[i].state[0]._data3 = 1; + } + allConvs[i].state[1]._data2 = 1; + allConvs[i].state[1]._data3 = 6; + allConvs[i].state[3]._data2 = 2; + + } + int numConversations = READ_LE_UINT16(_conversationData + 1); + int16 *curConversation = _conversationData + 3; + for (int i = 0; i < numConversations; i++) { + Conversation *conv = &allConvs[ READ_LE_UINT16(curConversation)]; + if (!conv->_enable) { + + conv->_enable = 1; + + int16 offset1 = READ_LE_UINT16(curConversation + 1); + void *convData1 = (char *)_conversationData + offset1; + conv->state[0]._data4 = convData1; + + int16 offset2 = READ_LE_UINT16(curConversation + 2); + void *convData2 = (char *)_conversationData + offset2; + conv->state[1]._data4 = convData2; + + int16 offset3 = READ_LE_UINT16(curConversation + 3); + void *convData3 = (char *)_conversationData + offset3; + conv->state[2]._data4 = convData3; + + int16 offset4 = READ_LE_UINT16(curConversation + 4); + void *convData4 = (char *)_conversationData + offset4; + conv->state[3]._data4 = convData4; + } + curConversation += 5; + } +} + +void ToonEngine::processConversationClick(Conversation *conv, int32 status) { + Conversation::ConvState *v2 = (Conversation::ConvState *)&conv->state[status]; + + int16 *i = (int16 *)((char *)v2->_data4 + 2); + + _gameState->_firstConverstationLine = false; + while (READ_LE_INT16(i) >= 0) { + if (READ_LE_INT16(i) < 100) { + if (_gameState->_exitConversation == false) { + characterTalk(READ_LE_INT16(i + 1)); + } + } else { + runConversationCommand(&i); + } + i += 2; + } + + int16 command = READ_LE_INT16(i); + int16 value = READ_LE_INT16(i + 1); + + if (command == -1) { + v2->_data2 = 0; + } else if (command == -2) { + v2->_data4 = (char *)_conversationData + value; + v2->_data3 = READ_LE_INT16(v2->_data4); + } else if (command == -3) { + v2->_data2 = 2; + v2->_data4 = (char *)_conversationData + value; + v2->_data3 = READ_LE_INT16(v2->_data4); + } + + int16 *v7 = i + 2; + int16 v8 = READ_LE_INT16(v7); + if (v8 == -1) { + _gameState->_mouseHidden = false; + } else { +retry: + while (1) { + v7 += 1; + int16 *v14 = (int16 *)((char *)_conversationData + v8); + + // find free dialogue slot + for (int j = 0; j < 10; j++) { + if (!conv->state[j]._data2) { + conv->state[j]._data3 = READ_LE_INT16(v14); + conv->state[j]._data4 = v14; + if (getConversationFlag(_gameState->_currentScene, conv->state[j]._data3)) + conv->state[j]._data2 = 1; + else + conv->state[j]._data2 = 3; + + v8 = READ_LE_INT16(v7); + if (v8 == -1) + return; + + goto retry; + } + } + + if (v8 != -1) + continue; + + break; + } + } + +} + +// hardcoded conversation flag to know if one dialogue icon must be displayed or not +// based on game events... +int32 ToonEngine::getConversationFlag(int32 locationId, int32 param) { + if (locationId == 1) { + if (param == 0x34) + return _gameState->getGameFlag(93); + + if (param != 55) + return 1; + + if (!_gameState->getGameFlag(262)) + return 1; + + return 0; + } else if (locationId == 2) { + if (param == 36 && _gameState->getGameFlag(149)) + return 0; + return 1; + } else if (locationId == 7) { + if (param == 30) + return _gameState->getGameFlag(132); + else + return 1; + } else if (locationId == 8) { + if (param == 0x20) { + if (!_gameState->getGameFlag(73) && !_gameState->getGameFlag(151) && !_gameState->getGameFlag(152) && !_gameState->getGameFlag(153)) + return 1; + return 0; + } + if (param == 33) { + if (!_gameState->getGameFlag(73) && !_gameState->getGameFlag(151) && !_gameState->getGameFlag(152) && !_gameState->getGameFlag(153)) + return 0; + return 1; + } + } else if (locationId == 0xb) { + if (param == 0x12) { + if (!_gameState->hasItemInInventory(71)) + return 1; + else + return 0; + } + if (param == 74) { + if (_gameState->hasItemInInventory(71)) + return 1; + else + return 0; + } + return 1; + } else if (locationId == 0xc) { + if (param == 0x3d && _gameState->getGameFlag(154)) { + return 0; + } + if (param == 76 && !_gameState->getGameFlag(79)) { + return 0; + } + if (param == 0x4e && !_gameState->hasItemInInventory(32)) { + return 0; + } + if (param == 0x4f && !_gameState->hasItemInInventory(92)) { + return 0; + } + if (param == 80 && !_gameState->hasItemInInventory(91)) { + return 0; + } + if (param == 0x4d && _gameState->getGameFlag(79)) { + return 0; + } + } else if (locationId == 0xd) { + if (param == 0x2f && _gameState->getGameFlag(81)) { + return 0; + } + if (param == 48 && _gameState->getGameFlag(81)) { + return 0; + } + } else if (locationId == 0x10) { + switch (param) { + case 0x3e8: + if (!(_gameState->_gameGlobalData[30] & 1)) + return 0; + break; + case 0x3e9: + if (!(_gameState->_gameGlobalData[30] & 2)) + return 0; + break; + case 0x3ea: + if (!(_gameState->_gameGlobalData[30] & 4)) + return 0; + break; + case 0x3eb: + if (!(_gameState->_gameGlobalData[30] & 8)) + return 0; + break; + case 0x3ec: + if (!(_gameState->_gameGlobalData[30] & 16)) + return 0; + break; + case 0x3ed: + if (!(_gameState->_gameGlobalData[30] & 32)) + return 0; + break; + case 0x3ee: + if (!(_gameState->_gameGlobalData[30] & 64)) + return 0; + break; + default: + break; + }; + return 1; + } else if (locationId == 0x12) { + if (param == 0x28 && _gameState->getGameFlag(91)) { + return 0; + } + if (param == 41 && (!_gameState->getGameFlag(96) || _gameState->getGameFlag(91))) { + return 0; + } + } else if (locationId == 0x13) { + if (param == 0x32 && _gameState->getGameFlag(107)) { + return 0; + } + if (param == 68 && !_gameState->getGameFlag(107)) { + return 0; + } + } else if (locationId == 0x14) { + if (param == 1000 && !_gameState->getGameFlag(82)) { + return 0; + } + } else if (locationId == 0x25) { + if (param == 7 && _gameState->_gameGlobalData[28] != 1) { + return 0; + } + if (param == 8 && _gameState->_gameGlobalData[28] != 1) { + return 0; + } + if (param == 9 && _gameState->_gameGlobalData[28] != 1) { + return 0; + } + if (param == 75 && _gameState->_gameGlobalData[28] != 2) { + return 0; + } + } else if (locationId == 72) { + if (param == 63 && _gameState->getGameFlag(105)) { + return 0; + } + if (param == 67 && !_gameState->getGameFlag(105)) { + return 0; + } + if (param == 0x40 && !_gameState->getGameFlag(105)) { + return 0; + } + } + return 1; +} + +int32 ToonEngine::runConversationCommand(int16 **command) { + + int16 *v5 = *command; + + int v2 = READ_LE_INT16(v5); + int v4 = READ_LE_INT16(v5+1); + int result = v2 - 100; + switch (v2) { + case 100: + result = runEventScript(_mouseX, _mouseY, 2, v4, 0); + break; + case 101: + _gameState->_exitConversation = true; + break; + case 102: + playSoundWrong(); + break; + case 104: + *command = (int16 *)((char *)_conversationData + v4); + *command = (int16 *)((char *)_conversationData + v4 - 4); + break; + // + case 105: + if (getConversationFlag(_gameState->_currentScene, v4)) { + result = READ_LE_INT16(*command + 4); + *command = (int16 *)((char *)_conversationData + result); + *command = (int16 *)((char *)_conversationData + result - 4); + } else { + int16 *newPtr = *command + 1; + *command = newPtr; + } + break; + case 103: + return result; + break; + } + return result; +} + +int32 ToonEngine::waitTicks(int32 numTicks, bool breakOnMouseClick) { +// Strangerke - Commented (not used) +// Common::EventManager *_event = _system->getEventManager(); + + uint32 nextTime = _system->getMillis() + numTicks * _tickLength; + while (_system->getMillis() < nextTime || numTicks == -1) { + //if (!_animationSceneScriptRunFlag) + // break; + updateAnimationSceneScripts(0); + getMouseEvent(); + simpleUpdate(); + + if (breakOnMouseClick && (_mouseButton & 0x2)) + break; + } + return 0; +} + +void ToonEngine::renderInventory() { + if (!_gameState->_inInventory) + return; + + _inventoryPicture->draw(*_mainSurface, 0, 0, 0, 0); + + // draw items on screen + for (int32 i = 0; i < _gameState->_numInventoryItems; i++) { + int32 x = 57 * (i % 7) + 114; + int32 y = ((9 * (i % 7)) & 0xf) + 56 * (i / 7) + 80; + _inventoryIconSlots->drawFrame(*_mainSurface, i % 12, x + _gameState->_currentScrollValue, y); + if (_gameState->_inventory[i]) + _inventoryIcons->drawFrame(*_mainSurface, _gameState->_inventory[i], x + _gameState->_currentScrollValue + 2, y + 2); + } + + drawConversationLine(); + if (!_audioManager->voiceStillPlaying()) { + _currentTextLineCharacterId = -1; + _currentTextLine = 0; + _currentTextLineId = -1; + } + + if (_firstFrame) { + copyToVirtualScreen(false); + _firstFrame = false; + fadeIn(5); + } + copyToVirtualScreen(); +} + +int32 ToonEngine::showInventory() { + int32 oldScrollValue = _gameState->_currentScrollValue; +// Strangerke - Commented (not used) +// Common::EventManager *_event = _system->getEventManager(); + _inventoryPicture = new Picture(this); + fadeOut(5); + _inventoryPicture->loadPicture("SACK128.CPS", true); + _inventoryPicture->setupPalette(); + + if (_gameState->_mouseState >= 0) { + setCursor(_gameState->_mouseState, true, -18, -14); + + // make sure we have a free spot + if (!_gameState->hasItemInInventory(0)) { + _gameState->_inventory[_gameState->_numInventoryItems] = 0; + _gameState->_numInventoryItems++; + } + } else { + setCursor(0); + } + + _gameState->_inInventory = true; + _gameState->_currentScrollValue = 0; + + int32 oldMouseButton = 0x3; + int32 justPressedButton = 0; + _firstFrame = true; + + while (!_shouldQuit) { + getMouseEvent(); + + justPressedButton = _mouseButton & ~oldMouseButton; + oldMouseButton = _mouseButton; + + if (justPressedButton & 0x3) { + // find out what object we're on + int32 foundObj = -1; + for (int32 i = 0; i < _gameState->_numInventoryItems; i++) { + int32 x = 57 * (i % 7) + 114; + int32 y = ((9 * (i % 7)) & 0xf) + 56 * (i / 7) + 80; + if (_mouseX >= (_gameState->_currentScrollValue + x - 6) && + _mouseX <= (_gameState->_currentScrollValue + x + 44 + 7) && + _mouseY >= y - 6 && _mouseY <= y + 50) { + foundObj = i; + break; + } + } + + if (justPressedButton & 0x1) { + if (_gameState->_mouseState < 0) { + if (foundObj >= 0) { + // take an object + int32 item = _gameState->_inventory[foundObj]; + + int32 modItem = getSpecialInventoryItem(item); + if (modItem) { + + if (modItem == -1) { + _gameState->_mouseState = item; + _gameState->_inventory[foundObj] = 0; + } else { + _gameState->_mouseState = modItem; + if (!_gameState->hasItemInInventory(0)) { + _gameState->_inventory[_gameState->_numInventoryItems] = 0; + _gameState->_numInventoryItems++; + } + } + + setCursor(_gameState->_mouseState, true, -18, -14); + } + + } else { + break; + } + } else { + if (foundObj >= 0 && _gameState->_inventory[foundObj] == 0) { // empty place + _gameState->_inventory[foundObj] = _gameState->_mouseState; + setCursor(0, false); + _gameState->_mouseState = -1; + } else if (foundObj >= 0 && _gameState->_inventory[foundObj] > 0) { + if (!handleInventoryOnInventory(_gameState->_mouseState, _gameState->_inventory[foundObj])) + playSoundWrong(); + } else { + // quit the inventory mode with the icon + break; + } + } + + } else if (justPressedButton & 0x2) { // right button + if (foundObj >= 0) { + // talk about the object + if (!handleInventoryOnInventory(_gameState->_inventory[foundObj], -1)) + characterTalk(1000 + _gameState->_inventory[foundObj]); + } else { + // go out + break; + } + } + } + + renderInventory(); + + } + + _gameState->_currentScrollValue = oldScrollValue; + _gameState->_inInventory = false; + _mouseButton = 0; + _lastMouseButton = 0x3; + + fadeOut(5); + if (_gameState->_inCloseUp) { + _gameState->_inCloseUp = false; + flipScreens(); + } else if (_gameState->_inCutaway) { + _currentCutaway->setupPalette(); + setupGeneralPalette(); + } else { + _currentPicture->setupPalette(); + setupGeneralPalette(); + } + flushPalette(); + _firstFrame = true; + + return 0; +} + +void ToonEngine::getMouseEvent() { + Common::EventManager *_event = _system->getEventManager(); + + Common::Event event; + while (_event->pollEvent(event) && !_shouldQuit) + ; + + _mouseX = _event->getMousePos().x; + _mouseY = _event->getMousePos().y; + _mouseButton = _event->getButtonState(); +} + +void ToonEngine::drawSack() { + if (_gameState->_sackVisible) { + _inventoryIcons->drawFrame(*_mainSurface, 0, _gameState->_currentScrollValue, 356); + } +} + +void ToonEngine::addItemToInventory(int32 item) { + + if (item == 103 || item == 104 || item == 89 || item == 82) { + // can't add that to inventory + _gameState->_mouseState = -1; + return; + } + + if (item == 41) { + // confiscated inventory + for (int32 i = 0; i < _gameState->_numConfiscatedInventoryItems; i++) + addItemToInventory(_gameState->_confiscatedInventory[i]); + + _gameState->_numConfiscatedInventoryItems = 0; + _gameState->_mouseState = -1; + return; + } + + for (int32 i = 0; i < _gameState->_numInventoryItems; i++) { + if (_gameState->_inventory[i] == 0) { + _gameState->_inventory[i] = item; + _gameState->_mouseState = -1; + return; + } + } + _gameState->_inventory[_gameState->_numInventoryItems] = item; + _gameState->_numInventoryItems++; + _gameState->_mouseState = -1; +} + +void ToonEngine::createMouseItem(int32 item) { + _gameState->_mouseState = item; + setCursor(_gameState->_mouseState, true, -18, -14); +} + +void ToonEngine::deleteMouseItem() { + _gameState->_mouseState = -1; + rearrangeInventory(); + setCursor(0); +} + +void ToonEngine::showCutaway(Common::String cutawayPicture) { + _gameState->_inCutaway = true; + _currentCutaway = new Picture(this); + if (cutawayPicture == "") { + cutawayPicture = Common::String(_gameState->_locations[_gameState->_currentScene]._cutaway) + ".CPS"; + } + _currentCutaway->loadPicture(cutawayPicture, false); + _currentCutaway->setupPalette(); + _oldScrollValue = _gameState->_currentScrollValue; + _gameState->_currentScrollValue = 0; + flushPalette(); +} + +void ToonEngine::hideCutaway() { + _gameState->_inCutaway = false; + _gameState->_sackVisible = true; + delete _currentCutaway; + _gameState->_currentScrollValue = _oldScrollValue; + _currentCutaway = 0; + _currentPicture->setupPalette(); + flushPalette(); +} + +void ToonEngine::updateCharacters(int32 timeElapsed) { + for (int32 i = 0; i < 8; i++) { + if (_characters[i]) { + _characters[i]->update(timeElapsed); + } + } +} + +void ToonEngine::drawPalette() { + for (int32 i = 0; i < 256; i++) { + int32 x = i % 32; + int32 y = i / 32; + _mainSurface->fillRect(Common::Rect(x * 16, y * 16, x * 16 + 16, y * 16 + 16), i); + } +} + +void ToonEngine::rearrangeInventory() { + for (int32 i = 0; i < _gameState->_numInventoryItems; i++) { + if (_gameState->_inventory[i] == 0) { + // move all the following items from one + for (int32 j = i + 1; j < _gameState->_numInventoryItems; j++) { + _gameState->_inventory[j-1] = _gameState->_inventory[j]; + } + _gameState->_numInventoryItems--; + } + } +} + +void ToonEngine::newGame() { + + if (_isDemo) { + addItemToInventory(59); + addItemToInventory(67); + addItemToInventory(11); + addItemToInventory(19); + loadScene(_gameState->_currentScene); + } else { + //loadScene(4); + loadScene(_gameState->_currentScene); + } +} + + +void ToonEngine::playSFX(int32 id, int32 volume) { + if (id < 0) + _audioManager->playSFX(-id + 1, volume, true); + else + _audioManager->playSFX(id , volume, false); +} + +void ToonEngine::playSoundWrong() { + _audioManager->playSFX(rand() & 7, 128, true); +} + +void ToonEngine::getTextPosition(int32 characterId, int32 *retX, int32 *retY) { + + if (characterId < 0) + characterId = 0; + + // default position is the center of current screen + *retX = _gameState->_currentScrollValue + 320; + *retY = 70; + + // hardcoded special cases... + if (characterId == 0) { + // drew + int32 x = _drew->getX(); + int32 y = _drew->getY(); + if (x >= _gameState->_currentScrollValue && x <= _gameState->_currentScrollValue + 640) { + if (!_gameState->_inCutaway && !_gameState->_inInventory) { + *retX = x; + *retY = y - ((_drew->getScale() * 256 / 1024) >> 1) - 45; + } + } + } else if (characterId == 1) { + // flux + int32 x = _flux->getX(); + int32 y = _flux->getY(); + if (x >= _gameState->_currentScrollValue && x <= _gameState->_currentScrollValue + 640) { + if (!_gameState->_inCutaway) { + *retX = x; + *retY = y - ((_drew->getScale() * 100 / 1024) >> 1) - 30; + } + } + } else if (characterId == 5 || characterId == 39) { + *retX = 80; + *retY = 120; + } else if (characterId == 14) { + *retX = 257; + *retY = 132; + } else if (characterId == 18) { + *retX = 80; + *retY = 180; + } else if (characterId == 21) { + *retX = 363; + *retY = 193; + } else if (characterId == 23) { + *retX = 532; + *retY = 178; + } else if (characterId == 33) { + *retX = 167; + *retY = 172; + } else { + + // more "standard" code by character + Character *character = getCharacterById(characterId); + if (character && !_gameState->_inCutaway) { + if (character->getAnimationInstance()) { + if (character->getX() >= _gameState->_currentScrollValue && character->getX() <= _gameState->_currentScrollValue + 640) { + int32 x1, y1, x2, y2; + character->getAnimationInstance()->getRect(&x1, &y1, &x2, &y2); + *retX = (x1 + x2) / 2; + *retY = y1; + } + } + } + } +} + +Character *ToonEngine::getCharacterById(int32 charId) { + + for (int32 i = 0; i < 8; i++) { + if (_characters[i] && _characters[i]->getId() == charId) + return _characters[i]; + } + return 0; +} + +void ToonEngine::drawConversationLine() { + if (_currentTextLine && _showConversationText) { + _fontRenderer->setFontColorByCharacter(_currentTextLineCharacterId); + _fontRenderer->setFont(_fontToon); + _fontRenderer->renderMultiLineText(_currentTextLineX, _currentTextLineY, Common::String(_currentTextLine), 0); + } +} + +void ToonEngine::pauseEngineIntern(bool pause) { + + Engine::pauseEngineIntern(pause); + + static int32 pauseStart = 0; + if (pause) { + pauseStart = _system->getMillis(); + + } else { + _oldTimer = _system->getMillis(); + _oldTimer2 = _oldTimer; + + int32 diff = _oldTimer - pauseStart; + + // we have to add the difference between the start and the current time + // to all "timer based" values. + for (int32 i = 0; i < _gameState->_locations[_gameState->_currentScene]._numSceneAnimations; i++) { + _sceneAnimationScripts[i]._lastTimer += diff; + } + for (int32 i = 0; i < 8; i++) { + if (_characters[i]) { + _characters[i]->updateTimers(diff); + } + } + + _gameState->_timerTimeout[0] += diff; + _gameState->_timerTimeout[1] += diff; + } +} + +bool ToonEngine::canSaveGameStateCurrently() { + return !_gameState->_inInventory && !_gameState->_inConversation && !_gameState->_inCutaway && !_gameState->_mouseHidden && !_moviePlayer->isPlaying(); +} + +bool ToonEngine::canLoadGameStateCurrently() { + return !_gameState->_inInventory && !_gameState->_inConversation && !_gameState->_inCutaway && !_gameState->_mouseHidden && !_moviePlayer->isPlaying(); +} + +Common::String ToonEngine::getSavegameName(int nr) { + return _targetName + Common::String::printf(".%03d", nr); +} + +bool ToonEngine::saveGame(int32 slot, Common::String saveGameDesc) { + const EnginePlugin *plugin = NULL; + int16 savegameId; + Common::String savegameDescription; + EngineMan.findGame(_gameDescription->gameid, &plugin); + + if (slot == -1) { + GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser("Save game:", "Save"); + dialog->setSaveMode(true); + savegameId = dialog->runModal(plugin, ConfMan.getActiveDomainName()); + savegameDescription = dialog->getResultString(); + delete dialog; + } else { + savegameId = slot; + if (!saveGameDesc.empty()) { + savegameDescription = saveGameDesc; + } else { + savegameDescription = Common::String::printf("Quick save #%d", slot); + } + } + + if (savegameId < 0) + return false; // dialog aborted + + Common::String savegameFile = getSavegameName(savegameId); + Common::SaveFileManager *saveMan = g_system->getSavefileManager(); + Common::OutSaveFile *saveFile = saveMan->openForSaving(savegameFile); + if (!saveFile) + return false; + + // save savegame header + saveFile->writeSint32BE(TOON_SAVEGAME_VERSION); + + if (savegameDescription == "") { + savegameDescription = "Untitled savegame"; + } + + saveFile->writeSint16BE(savegameDescription.size() + 1); + saveFile->write(savegameDescription.c_str(), savegameDescription.size() + 1); + + Graphics::saveThumbnail(*saveFile); + + TimeDate curTime; + _system->getTimeAndDate(curTime); + + uint32 saveDate = (curTime.tm_mday & 0xFF) << 24 | ((curTime.tm_mon + 1) & 0xFF) << 16 | ((curTime.tm_year + 1900) & 0xFFFF); + uint16 saveTime = (curTime.tm_hour & 0xFF) << 8 | ((curTime.tm_min) & 0xFF); + + saveFile->writeUint32BE(saveDate); + saveFile->writeUint16BE(saveTime); + + + // save global state + _gameState->save(saveFile); + _gameState->saveConversations(saveFile); + _hotspots->save(saveFile); + + // save current time to be able to patch the time when loading + saveFile->writeSint32BE(getOldMilli()); + + // save script states + for (int32 i = 0; i < 4; i++) { + _script->saveState(&_scriptState[i], saveFile); + } + + // save animation script states + for (int32 i = 0; i < state()->_locations[_gameState->_currentScene]._numSceneAnimations; i++) { + saveFile->writeByte(_sceneAnimationScripts[i]._active); + saveFile->writeByte(_sceneAnimationScripts[i]._frozen); + saveFile->writeSint32BE(_sceneAnimationScripts[i]._lastTimer); + _script->saveState(&_sceneAnimationScripts[i]._state, saveFile); + } + + // save scene animations + for (int32 i = 0; i < 64; i++) { + _sceneAnimations[i].save(this, saveFile); + } + + + for (int32 i = 0; i < 8; i++) { + if (_characters[i]) { + saveFile->writeSByte(i); + _characters[i]->save(saveFile); + } + } + saveFile->writeSByte(-1); + + // save "command buffer" + saveFile->writeSint16BE(_saveBufferStream->pos()); + if (_saveBufferStream->pos() > 0) { + saveFile->write(_saveBufferStream->getData(), _saveBufferStream->pos()); + saveFile->writeSint16BE(0); + } + + delete saveFile; + + return true; +} + +bool ToonEngine::loadGame(int32 slot) { + const EnginePlugin *plugin = NULL; + int16 savegameId; + EngineMan.findGame(_gameDescription->gameid, &plugin); + + if (slot == -1) { + GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser("Restore game:", "Restore"); + dialog->setSaveMode(false); + savegameId = dialog->runModal(plugin, ConfMan.getActiveDomainName()); + delete dialog; + } else { + savegameId = slot; + } + if (savegameId < 0) + return false; // dialog aborted + + Common::String savegameFile = getSavegameName(savegameId); + Common::SaveFileManager *saveMan = g_system->getSavefileManager(); + Common::InSaveFile *loadFile = saveMan->openForLoading(savegameFile); + if (!loadFile) + return false; + + int32 saveGameVersion = loadFile->readSint32BE(); + if (saveGameVersion != TOON_SAVEGAME_VERSION) { + delete loadFile; + return false; + } + int32 saveGameNameSize = loadFile->readSint16BE(); + loadFile->skip(saveGameNameSize); + + // We don't need the thumbnail here, so just read it and discard it + Graphics::skipThumbnail(*loadFile); + + loadFile->skip(6); // date & time skip + + if (_gameState->_currentScene != -1) { + exitScene(); + } + + + _gameState->load(loadFile); + loadScene(_gameState->_currentScene, true); + _gameState->loadConversations(loadFile); + _hotspots->load(loadFile); + + // read the old time + int32 savedTime = loadFile->readSint32BE(); + int32 timerDiff = _system->getMillis() - savedTime; + + // load script states + for (int32 i = 0; i < 4; i++) { + _script->loadState(&_scriptState[i], loadFile); + } + + // load animation script states + for (int32 i = 0; i < state()->_locations[_gameState->_currentScene]._numSceneAnimations; i++) { + _sceneAnimationScripts[i]._active = loadFile->readByte(); + _sceneAnimationScripts[i]._frozen = loadFile->readByte(); + _sceneAnimationScripts[i]._frozenForConversation = false; + int32 oldTimer = loadFile->readSint32BE(); + _sceneAnimationScripts[i]._lastTimer = MAX<int32>(0,oldTimer + timerDiff); + _script->loadState(&_sceneAnimationScripts[i]._state, loadFile); + } + + // load scene animations + for (int32 i = 0; i < 64; i++) { + _sceneAnimations[i].load(this, loadFile); + } + + _gameState->_timerTimeout[0] += timerDiff; + _gameState->_timerTimeout[1] += timerDiff; + + /* + int32 diff = _conversationData - _gameState->_conversationData; + + for (int32 i = 0; i < 60; i++) { + if (_gameState->_conversationState[i]._enable) { + // we have to fix up our pointers... + for (int32 a = 0; a < 10; a++) { + if (_gameState->_conversationState[i].state[a]._data4) + _gameState->_conversationState[i].state[a]._data4 = (int16 *)_gameState->_conversationState[i].state[a]._data4 + diff; + } + } + } + */ + + _gameState->_conversationData = _conversationData; + _firstFrame = true; + + // read characters info + while (1) { + int8 c = loadFile->readSByte(); + if (c < 0) + break; + + if (!_characters[c]) { + _characters[c] = new Character(this); + } + _characters[c]->load(loadFile); + //_characters[c]->setVisible(true); + _characters[c]->update(0); + } + + // load "command buffer" + int32 size = loadFile->readSint16BE(); + if (size) { + uint8 *buf = new uint8[size+2]; + loadFile->read(buf, size + 2); + + Common::MemoryReadStream rStr(buf, size + 2); + while (1) { + int16 command = rStr.readSint16BE(); + if (!command) break; + switch (command) { + case 1: { + int16 frame = rStr.readSint16BE(); + int16 animLen = rStr.readSint16BE(); + char animName[32]; + rStr.read(animName, animLen); + int16 x = rStr.readSint16BE(); + int16 y = rStr.readSint16BE(); +// int16 z = rStr.readSint16BE(); +// int16 layerZ = rStr.readSint16BE(); + rStr.readSint16BE(); + rStr.readSint16BE(); + + Animation *anim = new Animation(this); + anim->loadAnimation(animName); + anim->drawFrameOnPicture(frame, x, y); + delete anim; + break; + } + case 2: { + int16 x = rStr.readSint16BE(); + int16 y = rStr.readSint16BE(); + int16 x1 = rStr.readSint16BE(); + int16 y1 = rStr.readSint16BE(); + makeLineNonWalkable(x, y, x1, y1); + break; + } + case 3: { + int16 x = rStr.readSint16BE(); + int16 y = rStr.readSint16BE(); + int16 x1 = rStr.readSint16BE(); + int16 y1 = rStr.readSint16BE(); + makeLineWalkable(x, y, x1, y1); + break; + } + case 4: { + int16 x = rStr.readSint16BE(); + int16 y = rStr.readSint16BE(); + getMask()->floodFillNotWalkableOnMask(x, y); + break; + } + default: + break; + } + } + + _saveBufferStream->write(buf, size); + delete loadFile; + } + return true; +} + +// another special case for inventory +int32 ToonEngine::getSpecialInventoryItem(int32 item) { + + // butter + if (item == 12) { + for (int32 i = 0; i < _gameState->_numInventoryItems; i++) { + if (_gameState->_inventory[i] == 12) + _gameState->_inventory[i] = 11; + } + return 11; + + } else if (item == 84) { + if (_gameState->getGameFlag(26)) { + characterTalk(1726); + return 0; + } else { + if (!_gameState->hasItemInInventory(102) && !_gameState->hasItemInInventory(90) && !_gameState->hasItemInInventory(89)) { + characterTalk(1416); + return 102; + } else { + return 0; + } + } + } + + return -1; +} + +void ToonEngine::initCharacter(int32 characterId, int32 animScriptId, int32 sceneAnimationId, int32 animToPlayId) { + // find a new index + int32 characterIndex = -1; + for (int32 i = 0; i < 8; i++) { + if (_characters[i] && _characters[i]->getId() == characterId) { + characterIndex = i; + break; + } + + if (!_characters[i]) { + characterIndex = i; + break; + } + } + + if (characterIndex == -1) { + return; + } + +// Strangerke - Commented (not used) +// if (_characters[characterIndex]) +// delete current char + + _characters[characterIndex] = new Character(this); + _characters[characterIndex]->setId(characterId); + _characters[characterIndex]->setAnimScript(animScriptId); + _characters[characterIndex]->setDefaultSpecialAnimationId(animToPlayId); + _characters[characterIndex]->setSceneAnimationId(sceneAnimationId); + _characters[characterIndex]->setFlag(0); + _characters[characterIndex]->setVisible(true); + if (sceneAnimationId != -1) + _characters[characterIndex]->setAnimationInstance(_sceneAnimations[sceneAnimationId]._animInstance); +} + +int32 ToonEngine::handleInventoryOnFlux(int32 itemId) { + + switch (itemId) { + case 8: + sayLines(1, 1332); + break; + case 0x14: + case 0x15: + case 0x45: + sayLines(1, 1304); + break; + case 0x68: + _gameState->_mouseState = 0; + setCursor(0, false, 0, 0); + break; + case 116: + sayLines(1, 1306); + break; + default: + return false; + } + return true; +} + +void ToonEngine::storePalette() { + memcpy(_backupPalette, _finalPalette, 768); +} + +void ToonEngine::restorePalette() { + memcpy(_finalPalette, _backupPalette, 768); + flushPalette(); +} + +const char *ToonEngine::getSpecialConversationMusic(int32 conversationId) { + static const char * const specialMusic[] = { + 0, 0, + "BR091013", "BR091013", + "NET1214", "NET1214", + 0, 0, + "CAR1365B", "CAR1365B", + 0, 0, + 0, 0, + "CAR14431", "CAR14431", + 0, 0, + 0, 0, + "SCD16520", "SCD16520", + "SCD16520", "SCD16520", + "SCD16522", "SCD16522", + 0, 0, + "KPM8719", "KPM8719", + 0, 0, + "CAR1368B", "CAR1368B", + 0, 0, + 0, 0, + "KPM6337", "KPM6337", + "CAR20471", "CAR20471", + "CAR136_1", "KPM87_57", + 0, 0, + "CAR13648", "CAR13648", + 0, 0, + 0, 0, + 0, 0, + 0, 0, + "SCD16526", "SCD16526", + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0 + }; + + return specialMusic[randRange(0, 1) + conversationId * 2]; +} + +void ToonEngine::viewInventoryItem(Common::String str, int32 lineId, int32 itemDest) { + storePalette(); + fadeOut(5); + + Picture *pic = new Picture(this); + pic->loadPicture(str, false); + pic->setupPalette(); + flushPalette(); + + if (lineId) { + characterTalk(lineId, false); + } + + uint32 oldMouseButton = _mouseButton; + uint32 justPressedButton = 0; + _firstFrame = true; + + int32 oldScrollValue = _gameState->_currentScrollValue; + _gameState->_currentScrollValue = 0; + + while (!_shouldQuit) { + getMouseEvent(); + + justPressedButton = _mouseButton & ~oldMouseButton; + oldMouseButton = _mouseButton; + + if (justPressedButton) { + break; + } + + pic->draw(*_mainSurface, 0, 0, 0, 0); + + drawConversationLine(); + if (!_audioManager->voiceStillPlaying()) { + _currentTextLineCharacterId = -1; + _currentTextLine = 0; + _currentTextLineId = -1; + } + + if (_firstFrame) { + copyToVirtualScreen(false); + _firstFrame = false; + fadeIn(5); + } + + copyToVirtualScreen(); + } + + fadeOut(5); + restorePalette(); + _firstFrame = true; + _gameState->_currentScrollValue = oldScrollValue; + delete pic; + +} + +int32 ToonEngine::handleInventoryOnInventory(int32 itemDest, int32 itemSrc) { + switch (itemDest) { + case 0: + return handleInventoryOnDrew(itemSrc); + case 1: + if (itemSrc == 71) { + sayLines(2, 1212); + return 1; + } + break; + case 5: + if (itemSrc == 15) { + characterTalk(1492); + } else if (itemSrc == 0x2f) { + characterTalk(1488); + } else if (itemSrc == 88) { + sayLines(2, 1478); + } else { + return 0; + } + break; + case 6: + if (itemSrc == -1) { + viewInventoryItem("BLUEPRNT.CPS", 1006, itemDest); + return 1; + } else + return 0; + break; + case 8: + if (itemSrc == -1) { + viewInventoryItem("BOOK.CPS", 0, itemDest); + return 1; + } else { + return 0; + } + break; + case 11: + if (itemSrc == 0xb) { + _gameState->_mouseState = -1; + replaceItemFromInventory(11,12); + setCursor(0, false, 0, 0); + rearrangeInventory(); + return 1; + // + } else if (itemSrc == 24) { + characterTalk(1244); + return 1; + } else if (itemSrc == 0x1a || itemSrc == 0x40 || itemSrc == 71) { + sayLines(2, 1212); + return 1; + } + break; + case 12: + if (itemSrc == 24) { + characterTalk(1244); + return 1; + } else if (itemSrc == 0x1a || itemSrc == 0x40 || itemSrc == 71) { + sayLines(2, 1212); + return 1; + } + break; + case 13: + if (itemSrc == 0x35 || itemSrc == 0x36) { + characterTalk(1204); + return 1; + } else if (itemSrc >= 0x6b && itemSrc <= 0x72) { + characterTalk(1312); + return 1; + } + break; + case 14: + if (itemSrc == -1) { + deleteItemFromInventory(14); + addItemToInventory(15); + addItemToInventory(42); + rearrangeInventory(); + return 1; + } else if (itemSrc == 43) { + characterTalk(1410); + return 1; + } else if (itemSrc == 49) { + characterTalk(1409); + return 1; + } + break; + case 16: + if (itemSrc == 55) { + characterTalk(1400); + replaceItemFromInventory(55, 98); + return 1; + } + break; + case 19: + if (itemSrc == 0x34) { + characterTalk(1322); + return 1; + } else if (itemSrc == 107) { + sayLines(2 , 1300); + replaceItemFromInventory(107, 111); + _gameState->_mouseState = -1; + setCursor(0, false, 0, 0); + rearrangeInventory(); + return 1; + } else if (itemSrc == 0x6c) { + sayLines(2, 1300); + replaceItemFromInventory(108, 112); + _gameState->_mouseState = -1; + setCursor(0, false, 0, 0); + rearrangeInventory(); + return 1; + } else if (itemSrc == 0x6d) { + sayLines(2, 1300); + replaceItemFromInventory(109, 113); + _gameState->_mouseState = -1; + setCursor(0, false, 0, 0); + rearrangeInventory(); + return 1; + } else if (itemSrc == 110) { + sayLines(2, 1300); + replaceItemFromInventory(110, 114); + _gameState->_mouseState = -1; + setCursor(0, false, 0, 0); + rearrangeInventory(); + return 1; + } + break; + case 20: + if (itemSrc == 35) { + createMouseItem(21); + replaceItemFromInventory(35, 36); + return 1; + } else if (itemSrc == 0x24) { + createMouseItem(21); + replaceItemFromInventory(36, 37); + return 1; + } else if (itemSrc == 37) { + deleteItemFromInventory(37); + createMouseItem(21); + rearrangeInventory(); + return 1; + } else if (itemSrc == 0x6b || itemSrc == 0x6c || itemSrc == 0x6f || itemSrc == 108 || itemSrc == 112) { + sayLines(2, 1292); + return 1; + } + break; + case 21: + switch (itemSrc) { + + case 107: + characterTalk(1296); + replaceItemFromInventory(107, 109); + _gameState->_mouseState = -1; + setCursor(0, false, 0, 0); + rearrangeInventory(); + return 1; + case 108: + characterTalk(1298); + replaceItemFromInventory(108, 110); + _gameState->_mouseState = -1; + setCursor(0, false, 0, 0); + rearrangeInventory(); + return 1; + case 111: + characterTalk(1296); + replaceItemFromInventory(111, 113); + _gameState->_mouseState = -1; + setCursor(0, false, 0, 0); + rearrangeInventory(); + return 1; + case 112: + characterTalk(1298); + replaceItemFromInventory(112, 114); + _gameState->_mouseState = -1; + setCursor(0, false, 0, 0); + rearrangeInventory(); + return 1; + } + break; + case 22: + if (itemSrc == 32) { + characterTalk(1252); + return 1; + } + break; + case 24: + if (itemSrc == 0xc) { + characterTalk(1244); + return 1; + } else if (itemSrc == 79) { + characterTalk(1280); + return 1; + } + break; + case 26: + if (itemSrc == 0x5e) { + characterTalk(1316); + return 1; + } else if (itemSrc == 95) { + characterTalk(1320); + return 1; + } + break; + case 31: + if (itemSrc == 61) { + characterTalk(1412); + deleteItemFromInventory(61); + createMouseItem(62); + rearrangeInventory(); + return 1; + } + break; + case 32: + if (itemSrc == 22) { + characterTalk(1252); + return 1; + } + break; + case 33: + if (itemSrc == 117) { + characterTalk(1490); + return 1; + } + break; + case 34: + if (itemSrc == 61) { + characterTalk(1414); + return 1; + } + break; + case 35: + if (itemSrc == -1) { + characterTalk(1035); + return 1; + } else if (itemSrc == 20) { + replaceItemFromInventory(20, 21); + createMouseItem(36); + return 1; + } else if (itemSrc == 68) { + replaceItemFromInventory(68, 69); + createMouseItem(36); + return 1; + } else if (itemSrc >= 107 && itemSrc <= 114) { + characterTalk(1314); + return 1; + } else { + characterTalk(1208); + return 1; + } + break; + case 36: + if (itemSrc == -1) { + characterTalk(1035); + return 1; + } else if (itemSrc == 20) { + replaceItemFromInventory(20, 21); + createMouseItem(37); + return 1; + } else if (itemSrc == 68) { + replaceItemFromInventory(68, 69); + createMouseItem(37); + return 1; + } else if (itemSrc >= 107 && itemSrc <= 114) { + characterTalk(1314); + return 1; + } else { + characterTalk(1208); + return 1; + } + break; + case 37: + if (itemSrc == -1) { + characterTalk(1035); + return 1; + } else if (itemSrc == 20) { + replaceItemFromInventory(20, 21); + _gameState->_mouseState = -1; + setCursor(0, false, 0, 0); + rearrangeInventory(); + return 1; + } else if (itemSrc == 68) { + replaceItemFromInventory(68, 69); + _gameState->_mouseState = -1; + setCursor(0, false, 0, 0); + rearrangeInventory(); + return 1; + } else if (itemSrc >= 107 && itemSrc <= 114) { + characterTalk(1314); + return 1; + } else { + characterTalk(1208); + return 1; + } + break; + case 38: + if (itemSrc == 15) { + characterTalk(1492); + return 1; + } else if (itemSrc == 0x2f) { + characterTalk(1488); + return 1; + } else if (itemSrc == 88) { + sayLines(2, 1478); + return 1; + } + break; + case 40: + if (itemSrc == 53) { + replaceItemFromInventory(53, 54); + characterTalk(1222); + return 1; + } else if (itemSrc == 0x36) { + characterTalk(1228); + return 1; + } else if (itemSrc == 0x5b) { + characterTalk(1230); + return 1; + } else if (itemSrc == 92) { + characterTalk(1220); + return 1; + } + break; + case 43: + if (itemSrc == 14) { + characterTalk(1410); + return 1; + } + break; + case 47: + if (itemSrc == -1) + characterTalk(1047); + else + characterTalk(1488); + + return 1; + case 49: + if (itemSrc == 0xe) { + characterTalk(1409); + return 1; + } else if (itemSrc == 38 || itemSrc == 5 || itemSrc == 0x42) { + characterTalk(1476); + return 1; + } else if (itemSrc == 0x34) { + characterTalk(1260); + return 1; + } else if (itemSrc == 0x47) { + characterTalk(1246); + return 1; + } else if (itemSrc == 0x36) { + sayLines(2, 1324); + return 1; + } + break; + case 52: + if (itemSrc == 0x13) { + characterTalk(1322); + return 1; + } else if (itemSrc == 94) { + characterTalk(1282); + return 1; + } + break; + case 53: + if (itemSrc == 40) { + createMouseItem(54); + characterTalk(1222); + return 1; + } else if (itemSrc == 0x31) { + sayLines(2, 1324); + return 1; + } else if (itemSrc == 0x34) { + characterTalk(1310); + return 1; + } else if (itemSrc == 91) { + characterTalk(1218); + return 1; + } + + break; + case 54: + if (itemSrc == 40) { + characterTalk(1228); + return 1; + } else if (itemSrc == 0x34) { + characterTalk(1310); + return 1; + } else if (itemSrc == 0x5b) { + characterTalk(1226); + replaceItemFromInventory(91, 92); + return 1; + } else if (itemSrc == 92) { + characterTalk(1220); + return 1; + } + + break; + case 55: + if (itemSrc == 16) { + createMouseItem(98); + characterTalk(1400); + return 1; + } + break; + case 61: + if (itemSrc == 0x1f) { + characterTalk(1412); + deleteItemFromInventory(31); + createMouseItem(62); + rearrangeInventory(); + return 1; + } else if (itemSrc == 0x21 || itemSrc == 0x22) { + characterTalk(1414); + return 1; + } + break; + case 64: + if (itemSrc == 0xb) { + sayLines(2, 1212); + return 1; + } else if (itemSrc == 0x5e || itemSrc == 0x5f) { + characterTalk(1318); + return 1; + } + break; + case 66: + if (itemSrc == 15) { + characterTalk(1492); + return 1; + } else if (itemSrc == 0x2f) { + characterTalk(1488); + return 1; + } else if (itemSrc == 88) { + sayLines(2, 1478); + characterTalk(1478); + return 1; + } + break; + case 67: + if (itemSrc == 79) { + sayLines(2, 1212); + return 1; + } + break; + case 68: + if (itemSrc == 35) { + createMouseItem(69); + replaceItemFromInventory(35, 36); + return 1; + } else if (itemSrc == 0x24) { + createMouseItem(69); + replaceItemFromInventory(36, 37); + return 1; + } else if (itemSrc == 37) { + deleteItemFromInventory(37); + createMouseItem(69); + rearrangeInventory(); + return 1; + } else if (itemSrc == 0x6b || itemSrc == 113 || itemSrc == 0x6f || itemSrc == 109) { + sayLines(2, 1288); + return 1; + } + break; + case 69: + switch (itemSrc) { + case 107: + characterTalk(1296); + replaceItemFromInventory(107, 108); + _gameState->_mouseState = -1; + setCursor(0, false, 0, 0); + rearrangeInventory(); + return 1; + case 109: + characterTalk(1298); + replaceItemFromInventory(109, 110); + _gameState->_mouseState = -1; + setCursor(0, false, 0, 0); + rearrangeInventory(); + return 1; + case 111: + characterTalk(1296); + replaceItemFromInventory(111, 112); + _gameState->_mouseState = -1; + setCursor(0, false, 0, 0); + rearrangeInventory(); + return 1; + case 113: + characterTalk(1298); + replaceItemFromInventory(113, 114); + _gameState->_mouseState = -1; + setCursor(0, false, 0, 0); + rearrangeInventory(); + return 1; + } + break; + case 71: + if (itemSrc == 0xc || itemSrc == 1 || itemSrc == 0x41 || itemSrc == 67 || itemSrc == 0x4c || itemSrc == 57) { + sayLines(2, 1212); + return 1; + } else if (itemSrc == 79) { + characterTalk(1238); + return 1; + } + break; + case 79: + if (itemSrc == 1 || itemSrc == 67 || itemSrc == 76 || itemSrc == 57 || itemSrc == 0x41) { + sayLines(2, 1212); + return 1; + } else if (itemSrc == 0x18) { + characterTalk(1280); + return 1; + } else if (itemSrc == 0x47) { + characterTalk(1238); + return 1; + } + break; + case 82: + if (itemSrc == 84) { + sayLines(2, 1424); + return 1; + } else if (itemSrc == 0x58) { + deleteItemFromInventory(88); + createMouseItem(89); + rearrangeInventory(); + characterTalk(1428); + return 1; + } else if (itemSrc == 117) { + sayLines(2, 1496); + return 1; + } + break; + case 84: + if (itemSrc == 0x58) { + replaceItemFromInventory(88, 90); + characterTalk(1090); + return 1; + } else if (itemSrc == 117) { + characterTalk(1494); + return 1; + } + break; + case 88: + if (itemSrc == 82) { + deleteItemFromInventory(82); + createMouseItem(89); + rearrangeInventory(); + characterTalk(1428); + return 1; + } else if (itemSrc == 0x54) { + createMouseItem(90); + characterTalk(1090); + return 1; + } else if (itemSrc == 102) { + deleteItemFromInventory(102); + createMouseItem(90); + rearrangeInventory(); + characterTalk(1090); + return 1; + } + break; + case 89: + if (itemSrc == 117) { + sayLines(2, 1496); + return 1; + } + break; + case 90: + if (itemSrc == 117) { + sayLines(2, 1494); + return 1; + } + break; + case 91: + if (itemSrc == 0x28) { + characterTalk(1230); + return 1; + } else if (itemSrc == 54) { + createMouseItem(92); + return 1; + } + break; + case 92: + if (itemSrc == 0x28 || itemSrc == 54) { + characterTalk(1220); + return 1; + } + break; + case 94: + if (itemSrc == 26) { + characterTalk(1316); + return 1; + } else if (itemSrc == 0x34) { + characterTalk(1282); + return 1; + } else if (itemSrc == 64) { + characterTalk(1318); + return 1; + } + break; + case 95: + if (itemSrc == 26) { + characterTalk(1320); + return 1; + } else if (itemSrc == 0x40) { + characterTalk(1318); + return 1; + } else if (itemSrc == 115) { + characterTalk(1284); + replaceItemFromInventory(115, 116); + createMouseItem(93); + return 1; + } + break; + case 96: + if (itemSrc == 0x34) { + characterTalk(1234); + return 1; + } else if (itemSrc == 71) { + sayLines(2, 1212); + return 1; + } + break; + case 97: + if (itemSrc == 15) { + characterTalk(1492); + return 1; + } else if (itemSrc == 0x2f) { + characterTalk(1488); + return 1; + } else if (itemSrc == 88) { + sayLines(2, 1478); + return 1; + } + break; + case 100: + if (itemSrc == 117) { + characterTalk(1490); + return 1; + } + break; + case 102: + if (itemSrc == -1) { + characterTalk(1102); + return 1; + } else if (itemSrc == 84) { + _gameState->_mouseState = -1; + setCursor(0, false, 0, 0); + rearrangeInventory(); + characterTalk(1418); + return 1; + } else if (itemSrc == 88) { + deleteItemFromInventory(88); + createMouseItem(90); + rearrangeInventory(); + characterTalk(1090); + return 1; + } else if (itemSrc == 117) { + characterTalk(1494); + return 1; + } else { + characterTalk(1426); + return 1; + } + break; + case 106: + if (itemSrc == 13) { + characterTalk(1308); + return 1; + } + break; + case 107: + if (itemSrc == 19) { + sayLines(2, 1300); + deleteItemFromInventory(19); + createMouseItem(111); + rearrangeInventory(); + return 1; + } else if (itemSrc == 0x15) { + characterTalk(1296); + deleteItemFromInventory(21); + createMouseItem(109); + rearrangeInventory(); + return 1; + } else if (itemSrc == 0x23) { + characterTalk(1314); + return 1; + } else if (itemSrc == 69) { + characterTalk(1296); + deleteItemFromInventory(69); + createMouseItem(108); + rearrangeInventory(); + return 1; + } + break; + case 108: + if (itemSrc == 19) { + sayLines(2, 1300); + deleteItemFromInventory(19); + createMouseItem(112); + rearrangeInventory(); + return 1; + } else if (itemSrc == 0x15) { + characterTalk(1298); + deleteItemFromInventory(21); + createMouseItem(110); + rearrangeInventory(); + return 1; + } else if (itemSrc == 35) { + characterTalk(1314); + return 1; + } + break; + case 109: + if (itemSrc == 19) { + sayLines(2, 1300); + deleteItemFromInventory(19); + createMouseItem(113); + rearrangeInventory(); + return 1; + } else if (itemSrc == 0x23) { + characterTalk(1314); + return 1; + } else if (itemSrc == 69) { + characterTalk(1298); + deleteItemFromInventory(69); + createMouseItem(110); + rearrangeInventory(); + return 1; + } + break; + case 110: + if (itemSrc == 0x13) { + sayLines(2, 1300); + deleteItemFromInventory(19); + createMouseItem(114); + rearrangeInventory(); + return 1; + } else if (itemSrc == 35) { + characterTalk(1314); + return 1; + } + break; + case 111: + if (itemSrc == 21) { + characterTalk(1296); + deleteItemFromInventory(21); + createMouseItem(113); + rearrangeInventory(); + return 1; + } else if (itemSrc == 0x23) { + characterTalk(1314); + return 1; + } else if (itemSrc == 69) { + characterTalk(1296); + deleteItemFromInventory(69); + createMouseItem(112); + rearrangeInventory(); + return 1; + } + break; + case 112: + if (itemSrc == 0x15) { + characterTalk(1298); + deleteItemFromInventory(21); + createMouseItem(114); + rearrangeInventory(); + return 1; + } else if (itemSrc == 35) { + characterTalk(1314); + return 1; + } + break; + case 113: + if (itemSrc == 0x23) { + characterTalk(1314); + return 1; + } else if (itemSrc == 69) { + characterTalk(1298); + deleteItemFromInventory(69); + createMouseItem(114); + rearrangeInventory(); + return 1; + } + break; + case 114: + if (itemSrc == 35) { + characterTalk(1314); + return 1; + } + break; + case 115: + if (itemSrc == 95) { + replaceItemFromInventory(95, 93); + createMouseItem(116); + return 1; + } + break; + case 117: + if (itemSrc == 90 || itemSrc == 33) { + characterTalk(1490); + } else if (itemSrc == 102 || itemSrc == 84) { + characterTalk(1494); + } else if (itemSrc == 0x59 || itemSrc == 0x52) { + characterTalk(1496); + } + } + return 0; +} +int32 ToonEngine::handleInventoryOnDrew(int32 itemId) { + switch (itemId) { + case 1: + sayLines(1, 1232); + return 1; + case 2: + sayLines(2, 1202); + return 1; + case 7: + if (_gameState->_currentScene == 32) { + runEventScript(_mouseX, _mouseY, 2, 107, 0); + } else if (_gameState->_currentScene < 37) { + sayLines(2, 1258); + } else { + sayLines(2, 1462); + } + return 1; + case 8: + sayLines(2, 1328); + return 1; + case 0xc: + sayLines(1, 1266); + return 1; + case 0xd: + sayLines(1, 1206); + return 1; + case 16: + sayLines(1, 1438); + return 1; + case 0x12: + if (_gameState->_currentScene == 30) { + runEventScript(_mouseX, _mouseY, 2, 106, 0); + _gameState->_mouseState = -1; + } else { + sayLines(2, 1200); + } + return 1; + case 0x14: + sayLines(1, 1216); + return 1; + case 22: + if (_gameState->_currentScene != 39 && _gameState->_currentScene != 50 && _gameState->_currentScene != 49) { + if (_gameState->_currentScene < 37) { + sayLines(1, 1256); + } else { + sayLines(1, 1456); + } + } else { + runEventScript(_mouseX, _mouseY, 2, 100 , 0); + } + return 1; + case 0x18: + sayLines(1, 1216); + return 1; + case 0x23: + sayLines(1, 1210); + return 1; + case 0x31: + sayLines(1, 1262); + return 1; + case 50: + if (_gameState->_currentScene == 37) { + runEventScript(_mouseX, _mouseY, 2, 103, 0); + return 1; + }; + break; + case 0x36: + if (_gameState->_currentScene == 46) { + runEventScript(_mouseX, _mouseY, 2, 102, 0); + } else { + sayLines(1, 1224); + } + return 1; + case 0x37: + sayLines(1, 1408); + return 1; + case 0x20: + sayLines(1, 1254); + return 1; + case 0x21: + sayLines(1, 1268); + return 1; + case 0x22: + if (_gameState->_currentScene == 52) { + runEventScript(_mouseX, _mouseY, 2, 104, 0); + return 1; + } else { + _gameState->_mouseHidden = true; + _drew->setFacing(4); + sayLines(1, 1465); + sayLines(1, randRange(0, 1) + 1468); + createMouseItem(33); + _gameState->_mouseHidden = false; + return 1; + } + break; + case 31: + sayLines(1, 1436); + return 1; + case 0x1a: + sayLines(1, 1216); + return 1; + case 0x39: + sayLines(1, 1270); + return 1; + case 0x3a: + sayLines(1, 1444); + return 1; + case 0x3b: + sayLines(1, 1272); + return 1; + case 0x3f: + if (_gameState->_currentScene != 10 && _gameState->_currentScene != 30 && _gameState->_currentScene != 22) { + sayLines(1, 1274); + } else { + runEventScript(_mouseX, _mouseY, 2, 109, 0); + } + return 1; + case 0x41: + sayLines(1, 1232); + return 1; + + case 0x4b: + if (_gameState->_currentScene != 53) { + _gameState->_mouseHidden = true; + _drew->setFacing(4); + sayLines(1, 1437); + sayLines(2, 1440); + _gameState->_mouseHidden = false; + } else { + runEventScript(_mouseX, _mouseY, 2 , 101, 0); + } + return 1; + case 79: + sayLines(1, 1242); + return 1; + case 0x4c: + sayLines(1, 1232); + return 1; + case 71: + sayLines(1, 1250); + return 1; + case 0x43: + sayLines(1, 1216); + return 1; + case 0x60: + sayLines(2, 1236); + return 1; + case 99: + if (_gameState->_currentScene == 43) { + runEventScript(_mouseX, _mouseY, 2, 105, 0); + } + _gameState->_mouseState = -1; + setCursor(0, false, 0, 0); + sayLines(1, 1555); + return 1; + case 0x5a: + sayLines(1, 1432); + return 1; + case 0x58: + sayLines(1, 1432); + return 1; + case 0x65: + if (_gameState->_currentScene == 52) { + runEventScript(_mouseX, _mouseY, 2, 104, 0); + } else { + _gameState->_mouseHidden = true; + _drew->setFacing(4); + sayLines(1, 1464); + sayLines(1, 1468 + randRange(0, 1)); + createMouseItem(100); + _gameState->_mouseHidden = false; + } + return 1; + case 0x74: + sayLines(1, 1286); + return 1; + case 0x75: + sayLines(1, 1482); + return 1; + case 118: + sayLines(2, 1500); + return 1; + case 115: + sayLines(1, 1216); + return 1; + case 0x67: + if (_gameState->_currentScene == 52 || _gameState->_currentScene == 53) { + runEventScript(_mouseX, _mouseY, 2, 108, 0); + } + return 1; + } + return 0; +} + +void ToonEngine::deleteItemFromInventory(int32 item) { + for (int32 i = 0; i < _gameState->_numInventoryItems; i++) { + if (_gameState->_inventory[i] == item) { + _gameState->_inventory[i] = 0; + rearrangeInventory(); + return; + } + } +} + +void ToonEngine::replaceItemFromInventory(int32 item, int32 newitem) { + for (int32 i = 0; i < _gameState->_numInventoryItems; i++) { + if (_gameState->_inventory[i] == item) { + _gameState->_inventory[i] = newitem; + return; + } + } +} + +int32 ToonEngine::pauseSceneAnimationScript(int32 animScriptId, int32 tickToWait) { + int32 nextTicks = getTickLength() * tickToWait + getSceneAnimationScript(animScriptId)->_lastTimer; + if (nextTicks < getOldMilli()) { + getSceneAnimationScript(animScriptId)->_lastTimer = getOldMilli() + getTickLength() * tickToWait; + } else { + getSceneAnimationScript(animScriptId)->_lastTimer = nextTicks; + } + return nextTicks; +} + +Common::String ToonEngine::createRoomFilename(Common::String name) { + Common::String file = Common::String::printf("ACT%d/%s/%s", _gameState->_currentChapter, _gameState->_locations[_gameState->_currentScene]._name, name.c_str()); + return file; +} + +void ToonEngine::createShadowLUT() { + // here we create the redirection table that will be used to draw shadows + // for each color of the palette we find the closest color in the palette that could be used for shadowed color. + + // In the original program, the scale factor is 0.77f + // we will use 77 / 100 here. + + if (!_shadowLUT) { + _shadowLUT = new uint8[256]; + } + + uint32 scaleNum = 77; + uint32 scaleDenom = 100; + + for (int32 i = 0; i < 255; i++) { + + // goal color + uint32 destR = _finalPalette[i*3+0] * scaleNum / scaleDenom; + uint32 destG = _finalPalette[i*3+1] * scaleNum / scaleDenom; + uint32 destB = _finalPalette[i*3+2] * scaleNum / scaleDenom; + + // search only in the "picture palette" which is in colors 1-128 and 200-255 + int32 colorDist = 0xffffff; + int32 foundColor = 0; + + for (int32 c = 1; c < 129; c++) { + + int32 diffR = _finalPalette[c*3+0] - destR; + int32 diffG = _finalPalette[c*3+1] - destG; + int32 diffB = _finalPalette[c*3+2] - destB; + + if (colorDist > diffR * diffR + diffG * diffG + diffB * diffB) { + colorDist = diffR * diffR + diffG * diffG + diffB * diffB; + foundColor = c; + } + } + + for (int32 c = 200; c < 256; c++) { + + int32 diffR = _finalPalette[c*3+0] - destR; + int32 diffG = _finalPalette[c*3+1] - destG; + int32 diffB = _finalPalette[c*3+2] - destB; + + if (colorDist > diffR * diffR + diffG * diffG + diffB * diffB) { + colorDist = diffR * diffR + diffG * diffG + diffB * diffB; + foundColor = c; + } + } + + _shadowLUT[i] = foundColor; + + } +} + +bool ToonEngine::loadToonDat() { + Common::File in; + char buf[256]; + int majVer, minVer; + + in.open("toon.dat"); + + if (!in.isOpen()) { + Common::String errorMessage = "You're missing the 'toon.dat' file. Get it from the ScummVM website"; + GUIErrorMessage(errorMessage); + warning("%s", errorMessage.c_str()); + return false; + } + + // Read header + in.read(buf, 4); + buf[4] = '\0'; + + if (strcmp(buf, "TOON")) { + Common::String errorMessage = "File 'toon.dat' is corrupt. Get it from the ScummVM website"; + GUIErrorMessage(errorMessage); + warning("%s", errorMessage.c_str()); + return false; + } + + majVer = in.readByte(); + minVer = in.readByte(); + + if ((majVer != TOON_DAT_VER_MAJ) || (minVer != TOON_DAT_VER_MIN)) { + snprintf(buf, 256, "File 'toon.dat' is wrong version. Expected %d.%d but got %d.%d. Get it from the ScummVM website", TOON_DAT_VER_MAJ, TOON_DAT_VER_MIN, majVer, minVer); + GUIErrorMessage(buf); + warning("%s", buf); + + return false; + } + + _numVariant = in.readUint16BE(); + + _locationDirNotVisited = loadTextsVariante(in); + _locationDirVisited = loadTextsVariante(in); + _specialInfoLine = loadTextsVariante(in); + + return true; +} + +char **ToonEngine::loadTextsVariante(Common::File &in) { + int numTexts; + int entryLen; + int len; + char **res = 0; + char *pos = 0; + + for (int varnt = 0; varnt < _numVariant; varnt++) { + numTexts = in.readUint16BE(); + entryLen = in.readUint16BE(); + pos = (char *)malloc(entryLen); + if (varnt == _gameVariant) { + res = (char **)malloc(sizeof(char *) * numTexts); + res[0] = pos; + in.read(res[0], entryLen); + res[0] += DATAALIGNMENT; + } else { + in.read(pos, entryLen); + } + + pos += DATAALIGNMENT; + + for (int i = 1; i < numTexts; i++) { + pos -= 2; + + len = READ_BE_UINT16(pos); + pos += 2 + len; + + if (varnt == _gameVariant) + res[i] = pos; + } + } + + return res; +} + +void ToonEngine::makeLineNonWalkable(int32 x, int32 y, int32 x2, int32 y2) { + _currentMask->drawLineOnMask(x, y, x2, y2, false); +} + +void ToonEngine::makeLineWalkable(int32 x, int32 y, int32 x2, int32 y2) { + _currentMask->drawLineOnMask(x, y, x2, y2, true); +} + +void ToonEngine::playRoomMusic() { + if(_gameState->_inConversation) { + const char* music = getSpecialConversationMusic(_gameState->_currentConversationId); + if (music) { + _audioManager->playMusic(_gameState->_locations[_gameState->_currentScene]._name, music); + return; + } + } + + _audioManager->playMusic(_gameState->_locations[_gameState->_currentScene]._name, _gameState->_locations[_gameState->_currentScene]._music); +} + +void SceneAnimation::save(ToonEngine *vm, Common::WriteStream *stream) { + stream->writeByte(_active); + stream->writeSint32BE(_id); + + if (!_active) + return; + + if (_animInstance) { + stream->writeByte(1); + _animInstance->save(stream); + } else { + stream->writeByte(0); + } + + if (!_animation) { + stream->writeByte(0); + } else { + stream->writeByte(strlen(_animation->_name) + 1); + stream->write(_animation->_name, strlen(_animation->_name) + 1); + } +} +void SceneAnimation::load(ToonEngine *vm, Common::ReadStream *stream) { + + _active = stream->readByte(); + _id = stream->readSint32BE(); + + + if (!_active) + return; + + if (stream->readByte() == 1) { + _animInstance = vm->getAnimationManager()->createNewInstance(kAnimationScene); + _animInstance->load(stream); + vm->getAnimationManager()->addInstance(_animInstance); + } + + // load animation if any + char animationName[256]; + *animationName = 0; + int8 strSize = stream->readByte(); + if (!strSize) { + _animation = 0; + if (_animInstance) + _animInstance->setAnimation(0); + } else { + stream->read(animationName, strSize); + animationName[strSize] = 0; + + _animation = new Animation(vm); + _animation->loadAnimation(animationName); + + if (_animInstance) { + _animInstance->setAnimation(_animation, false); + } + } +} + +} // End of namespace Toon diff --git a/engines/toon/toon.h b/engines/toon/toon.h new file mode 100644 index 0000000000..299cb403b1 --- /dev/null +++ b/engines/toon/toon.h @@ -0,0 +1,424 @@ +/* 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. +* +* $URL$ +* $Id$ +* +*/ + +#ifndef TOON_TOON_H +#define TOON_TOON_H + +#include "engines/advancedDetector.h" +#include "engines/engine.h" +#include "graphics/surface.h" +#include "common/random.h" +#include "common/error.h" +#include "toon/resource.h" +#include "toon/script.h" +#include "toon/script_func.h" +#include "toon/state.h" +#include "toon/picture.h" +#include "toon/anim.h" +#include "toon/movie.h" +#include "toon/font.h" +#include "toon/text.h" +#include "toon/audio.h" + +#define TOON_DAT_VER_MAJ 0 // 1 byte +#define TOON_DAT_VER_MIN 3 // 1 byte +#define TOON_SAVEGAME_VERSION 4 +#define DATAALIGNMENT 4 + +/** + * This is the namespace of the Toon engine. + * + * Status of this engine: ??? + * + * Games using this engine: + * - Toonstruck + */ +namespace Toon { + +enum ToonGameType { + GType_TOON = 1 +}; + +enum ToonDebugChannels { + kDebugAnim = 1 << 0, + kDebugCharacter = 1 << 1, + kDebugAudio = 1 << 2, + kDebugHotspot = 1 << 3, + kDebugFont = 1 << 4, + kDebugPath = 1 << 5, + kDebugMovie = 1 << 6, + kDebugPicture = 1 << 7, + kDebugResource = 1 << 8, + kDebugState = 1 << 9, + kDebugTools = 1 << 10, + kDebugText = 1 << 11 +}; + +class Picture; +class Movie; +class Hotspots; +class Character; +class CharacterDrew; +class CharacterFlux; +class FontRenderer; +class TextResource; +class AudioManager; +class PathFinding; + +class ToonEngine : public Engine { +public: + ToonEngine(OSystem *syst, const ADGameDescription *gameDescription); + ~ToonEngine(); + + const ADGameDescription *_gameDescription; + Common::Language _language; + byte _numVariant; + byte _gameVariant; + char **_locationDirNotVisited; + char **_locationDirVisited; + char **_specialInfoLine; + + Common::Error run(); + bool showMainmenu(bool &loadedGame); + void init(); + bool loadToonDat(); + char **loadTextsVariante(Common::File &in); + void setPaletteEntries(uint8 *palette, int32 offset, int32 num); + void fixPaletteEntries(uint8 *palette, int num); + void flushPalette(); + void parseInput(); + void initChapter(); + void initFonts(); + void loadScene(int32 SceneId, bool forGameLoad = false); + void exitScene(); + void loadCursor(); + void setCursor(int32 type, bool inventory = false, int32 offsetX = 0, int offsetY = 0); + void loadAdditionalPalette(Common::String fileName, int32 mode); + void setupGeneralPalette(); + void render(); + void update(int32 timeIncrement); + void doFrame(); + void updateAnimationSceneScripts(int32 timeElapsed); + void updateCharacters(int32 timeElapsed); + void setSceneAnimationScriptUpdate(bool enable); + bool isUpdatingSceneAnimation(); + int32 getCurrentUpdatingSceneAnimation(); + int32 randRange(int32 minStart, int32 maxStart); + void selectHotspot(); + void clickEvent(); + int32 runEventScript(int32 x, int32 y, int32 mode, int32 id, int32 scriptId); + void flipScreens(); + void drawInfoLine(); + void drawConversationLine(); + const char *getLocationString(int32 locationId, bool alreadyVisited); + int32 getScaleAtPoint(int32 x, int32 y); + int32 getZAtPoint(int32 x, int32 y); + int32 getLayerAtPoint(int32 x, int32 y); + int32 characterTalk(int32 dialogid, bool blocking = true); + int32 simpleCharacterTalk(int32 dialogid); + void sayLines(int numLines, int dialogId); + void haveAConversation(int32 convId); + void processConversationClick(Conversation *conv, int32 status); + int32 runConversationCommand(int16 **command); + void prepareConversations(); + void drawConversationIcons(); + void simpleUpdate(bool waitCharacterToTalk = false); + int32 waitTicks(int32 numTicks, bool breakOnMouseClick); + void copyToVirtualScreen(bool updateScreen = true); + void getMouseEvent(); + int32 showInventory(); + void drawSack(); + void addItemToInventory(int32 item); + void deleteItemFromInventory(int32 item); + void replaceItemFromInventory(int32 item, int32 destItem); + void rearrangeInventory(); + void createMouseItem(int32 item); + void deleteMouseItem(); + void showCutaway(Common::String cutawayPicture); + void hideCutaway(); + void drawPalette(); + void newGame(); + void playSoundWrong(); + void playSFX(int32 id, int32 volume); + void storeRifFlags(int32 location); + void restoreRifFlags(int32 location); + void getTextPosition(int32 characterId, int32 *retX, int32 *retY); + int32 getConversationFlag(int32 locationId, int32 param); + int32 getSpecialInventoryItem(int32 item); + Character *getCharacterById(int32 charId); + Common::String getSavegameName(int nr); + bool loadGame(int32 slot); + bool saveGame(int32 slot, Common::String saveGameDesc); + void fadeIn(int32 numFrames) ; + void fadeOut(int32 numFrames) ; + void initCharacter(int32 characterId, int32 animScriptId, int32 animToPlayId, int32 sceneAnimationId); + int32 handleInventoryOnFlux(int32 itemId); + int32 handleInventoryOnInventory(int32 itemDest, int32 itemSrc); + int32 handleInventoryOnDrew(int32 itemId); + int32 pauseSceneAnimationScript(int32 animScriptId, int32 tickToWait); + void updateTimer(int32 timeIncrement); + Common::String createRoomFilename(Common::String name); + void createShadowLUT(); + void playTalkAnimOnCharacter(int32 animID, int32 characterId, bool talker); + void updateScrolling(bool force, int32 timeIncrement); + void enableTimer(int32 timerId); + void setTimer(int32 timerId, int32 timerWait); + void disableTimer(int32 timerId); + void updateTimers(); + void makeLineNonWalkable(int32 x, int32 y, int32 x2, int32 y2); + void makeLineWalkable(int32 x, int32 y, int32 x2, int32 y2); + void renderInventory(); + void viewInventoryItem(Common::String str, int32 lineId, int32 itemDest); + void storePalette(); + void restorePalette(); + const char *getSpecialConversationMusic(int32 locationId); + void playRoomMusic(); + void waitForScriptStep(); + void doMagnifierEffect(); + + bool canSaveGameStateCurrently(); + bool canLoadGameStateCurrently(); + void pauseEngineIntern(bool pause); + + Resources *resources() { + return _resources; + } + + State *state() { + return _gameState; + } + + Graphics::Surface &getMainSurface() { + return *_mainSurface; + } + + Picture *getMask() { + return _currentMask; + } + + Picture *getPicture() { + return _currentPicture; + } + + AnimationManager *getAnimationManager() { + return _animationManager; + } + + Movie *getMoviePlayer() { + return _moviePlayer; + } + + SceneAnimation *getSceneAnimation(int32 id) { + return &_sceneAnimations[id]; + } + + SceneAnimationScript *getSceneAnimationScript(int32 id) { + return &_sceneAnimationScripts[id]; + } + + EMCInterpreter *getScript() { + return _script; + } + + Hotspots *getHotspots() { + return _hotspots; + } + + Character *getCharacter(int32 charId) { + return _characters[charId]; + } + + uint8 *getShadowLUT() { + return _shadowLUT; + } + + int32 getCurrentLineToSay() { + return _currentTextLineId; + } + + int32 getCurrentCharacterTalking() { + return _currentTextLineCharacterId; + } + + CharacterDrew *getDrew() { + return (CharacterDrew *)_drew; + } + + CharacterFlux *getFlux() { + return (CharacterFlux *)_flux; + } + + int32 getTickLength() { + return _tickLength; + } + + int32 getOldMilli() { + return _oldTimer2; + } + + OSystem *getSystem() { + return _system; + } + + AudioManager *getAudioManager() { + return _audioManager; + } + + int32 getScriptRegionNested() { + return _currentScriptRegion; + } + + int32 getMouseX() { + return _mouseX; + } + + int32 getMouseY() { + return _mouseY; + } + + PathFinding *getPathFinding() { + return _pathFinding; + } + + Common::WriteStream *getSaveBufferStream() { + return _saveBufferStream; + } + + bool shouldQuitGame() const { + return _shouldQuit; + } + + Common::Error saveGameState(int slot, const char *desc) { + + return (saveGame(slot, desc) ? Common::kWritingFailed : Common::kNoError); + } + + Common::Error loadGameState(int slot) { + return (loadGame(slot) ? Common::kReadingFailed : Common::kNoError); + } + + bool hasFeature(EngineFeature f) const { + return + (f == kSupportsRTL) || + (f == kSupportsLoadingDuringRuntime) || + (f == kSupportsSavingDuringRuntime); + } + +protected: + OSystem *_system; + int32 _tickLength; + Resources *_resources; + TextResource *_genericTexts; + TextResource *_roomTexts; + State *_gameState; + uint8 *_finalPalette; + uint8 *_backupPalette; + uint8 *_additionalPalette1; + uint8 *_additionalPalette2; + uint8 *_cutawayPalette; + uint8 *_universalPalette; + uint8 *_fluxPalette; + uint8 *_roomScaleData; + uint8 *_shadowLUT; + + Picture *_currentPicture; + Picture *_currentMask; + Picture *_currentCutaway; + Picture *_inventoryPicture; + PathFinding *_pathFinding; + + EMCInterpreter *_script; + EMCData _scriptData; + EMCState _scriptState[4]; + int32 _currentScriptRegion; // script region ( nested script run ) + + ScriptFunc *_script_func; + + SceneAnimation _sceneAnimations[64]; + SceneAnimationScript _sceneAnimationScripts[64]; + int32 _lastProcessedSceneScript; + bool _animationSceneScriptRunFlag; + bool _updatingSceneScriptRunFlag; + + Graphics::Surface *_mainSurface; + + AnimationInstance *_cursorAnimationInstance; + Animation *_cursorAnimation; + Animation *_dialogIcons; + Animation *_inventoryIcons; + Animation *_inventoryIconSlots; + int32 _cursorOffsetX; + int32 _cursorOffsetY; + + char *_currentTextLine; + int32 _currentTextLineId; + int32 _currentTextLineX; + int32 _currentTextLineY; + int32 _currentTextLineCharacterId; + + int32 _oldScrollValue; + + AnimationManager *_animationManager; + + Character *_characters[32]; + Character *_drew; + Character *_flux; + + Hotspots *_hotspots; + int32 _currentHotspotItem; + + bool _shouldQuit; + int32 _scriptStep; + + int32 _mouseX; + int32 _mouseY; + int32 _mouseButton; + int32 _lastMouseButton; + + int32 _oldTimer; + int32 _oldTimer2; + + Movie *_moviePlayer; + + Common::RandomSource _rnd; + + FontRenderer *_fontRenderer; + Animation *_fontToon; + Animation *_fontEZ; + + AudioManager *_audioManager; + + Common::MemoryWriteStreamDynamic *_saveBufferStream; + + int16 *_conversationData; + + bool _firstFrame; + bool _isDemo; + bool _showConversationText; +}; + +} // End of namespace Toon + +#endif |