/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include "common/system.h" #include "common/events.h" #include "graphics/cursorman.h" #include "graphics/palette.h" #include "graphics/surface.h" #include "chewy/cursor.h" #include "chewy/graphics.h" #include "chewy/scene.h" #include "chewy/resource.h" #include "chewy/sound.h" #include "chewy/text.h" #include "chewy/video/cfo_decoder.h" namespace Chewy { #define MAX_DETAILS 32 #define MAX_HOTSPOTS 50 #define MAX_AUTOMOVE 20 #define MAX_SOUNDS 3 struct SoundInfo { uint16 enable[MAX_SOUNDS]; // flag, 0 = disable, 1 = enable int16 index[MAX_SOUNDS]; uint16 start[MAX_SOUNDS]; uint16 channel[MAX_SOUNDS]; uint16 volume[MAX_SOUNDS]; uint16 repeatCount[MAX_SOUNDS]; uint16 stereo[MAX_SOUNDS]; // stereo position for the SFX }; // Animated details - scene animations struct AnimatedDetails { int16 x; int16 y; byte startFlag; // 0: no animation byte repeat; int16 startSprite; int16 endSprite; int16 spriteCount; uint16 delay; uint16 delayCount; uint16 reverse; // 0: play normally, 1: play in reverse uint16 timerStart; // seconds until detail is started (0: no timer) uint16 zIndex; byte loadFlag; // 0: load animation in memory immediately, 1: load animation in memory when it is played byte zoom; SoundInfo soundInfo; byte showOneFrame; // show a sprite, 0: none, 1: before animation, 2: after animation byte currentFrame; }; // Static details - scene sprites and props struct StaticDetails { int16 x; int16 y; int16 spriteNum; uint16 zIndex; byte hide; // 1 byte dummy }; struct Hotspot { Common::Rect rect; uint16 resource; Common::String desc; int16 speechId; }; struct RoomInfo { byte roomNum; byte picNum; byte autoMoveCount; byte loadTaf; Common::String tafName; // 14 bytes byte zoomFactor; // 1 byte dummy }; struct AutoMove { int16 x; int16 y; byte spriteNum; // sprite number to draw when the end point is reached // 1 byte dummy }; /*struct HotspotSpeech { int16 look; int16 use; int16 talk; };*/ struct SceneInfo { uint16 staticDetailsCount; uint16 animatedDetailsCount; uint32 spritePtr; AnimatedDetails animatedDetails[MAX_DETAILS]; StaticDetails staticDetails[MAX_DETAILS]; Hotspot hotspot[MAX_HOTSPOTS]; RoomInfo roomInfo; AutoMove autoMove[MAX_AUTOMOVE]; int16 hotspotSpeech[MAX_DETAILS * MAX_SOUNDS]; //uint32 hotspotSoundPtr[MAX_DETAILS][MAX_SOUNDS]; // unused }; Scene::Scene(ChewyEngine *vm) : _vm(vm) { _sceneInfo = new SceneInfo(); _vm->_graphics->setDescSurface(Common::Point(-1, -1)); } Scene::~Scene() { delete _sceneInfo; } void Scene::change(uint scene) { _curScene = scene; _vm->_cursor->setCursor(0); _vm->_cursor->showCursor(); loadSceneInfo(); draw(); } void Scene::draw() { // Background _vm->_graphics->drawImage("episode1.tgp", _curScene); for (uint16 i = 0; i < MAX_DETAILS; i++) { // Static details StaticDetails s = _sceneInfo->staticDetails[i]; if (s.spriteNum >= 0 && s.x >= 0 && s.y >= 0 && !s.hide) _vm->_graphics->drawSprite(Common::String::format("det%d.taf", _curScene), s.spriteNum, s.x, s.y); } // TODO: These are all hardcoded for now _vm->_graphics->drawSprite("det1.taf", 0, 200, 100); _vm->_graphics->loadFont("6x8.tff"); //_vm->_graphics->drawText("This is a test", 200, 80); _vm->_graphics->setDescSurface(Common::Point(-1, -1)); } void Scene::updateMouse(Common::Point coords) { _vm->_graphics->restoreDescSurface(); // Static details for (uint16 i = 0; i < MAX_HOTSPOTS; i++) { //_vm->_graphics->drawRect(_sceneInfo->hotspot[i].rect, 0); // debug if (_sceneInfo->hotspot[i].rect.contains(coords) && _sceneInfo->hotspot[i].resource < kATSTextMax) { if (coords.y >= 8) { _vm->_graphics->setDescSurface(Common::Point(coords.x, coords.y - 8)); _vm->_graphics->drawText(_sceneInfo->hotspot[i].desc, coords.x, coords.y - 8); //_vm->_graphics->drawText(_sceneInfo->hotspot[i].desc + Common::String::format(" (%d)", i), coords.x, coords.y - 8); // debug } break; } } } void Scene::mouseClick(Common::Point coords) { // Static details for (uint16 i = 0; i < MAX_HOTSPOTS; i++) { //_vm->_graphics->drawRect(_sceneInfo->hotspot[i].rect, 0); // debug if (_sceneInfo->hotspot[i].rect.contains(coords)) { int16 sample = -1; // TODO: This is still not right int16 speechId = _sceneInfo->hotspotSpeech[_sceneInfo->hotspot[i].resource - 4]; SoundInfo *s = &_sceneInfo->animatedDetails[speechId].soundInfo; switch (_vm->_cursor->getCurrentCursor()) { case kUse: sample = s->index[0]; break; case kLook: sample = s->index[1]; break; case kTalk: sample = s->index[2]; break; default: break; } if (sample >= 0) _vm->_sound->playSpeech(sample); } } } static void readSSoundInfo(Common::File &indexFile, int16 *data) { for (int i = 0; i < MAX_SOUNDS; i++) data[i] = indexFile.readSint16LE(); } static void readUSoundInfo(Common::File &indexFile, uint16 *data) { for (int i = 0; i < MAX_SOUNDS; i++) data[i] = indexFile.readUint16LE(); } /** * Loads scene information from test.rdi * Note that the original loads everything with a single read into a structure, * which is why there are some pointers saved in the resource file - however, * these are set to zero */ void Scene::loadSceneInfo() { const uint32 sceneInfoSize = 3784; const uint32 headerRDI = MKTAG('R', 'D', 'I', '\0'); const char *sceneIndexFileName = "test.rdi"; Common::File indexFile; if (!Common::File::exists(sceneIndexFileName)) error("File %s not found", sceneIndexFileName); Text *text = new Text(); indexFile.open(sceneIndexFileName); uint32 header = indexFile.readUint32BE(); if (header != headerRDI) error("Invalid resource - %s", sceneIndexFileName); indexFile.skip(2); // room count, unused (set to 100) indexFile.seek(sceneInfoSize * _curScene, SEEK_CUR); _sceneInfo->staticDetailsCount = indexFile.readUint16LE(); _sceneInfo->animatedDetailsCount = indexFile.readUint16LE(); indexFile.skip(4); // pointer to sprites // Animated details for (int i = 0; i < MAX_DETAILS; i++) { _sceneInfo->animatedDetails[i].x = indexFile.readSint16LE(); _sceneInfo->animatedDetails[i].y = indexFile.readSint16LE(); _sceneInfo->animatedDetails[i].startFlag = indexFile.readByte(); _sceneInfo->animatedDetails[i].repeat = indexFile.readByte(); _sceneInfo->animatedDetails[i].startSprite = indexFile.readSint16LE(); _sceneInfo->animatedDetails[i].endSprite = indexFile.readSint16LE(); _sceneInfo->animatedDetails[i].spriteCount = indexFile.readSint16LE(); _sceneInfo->animatedDetails[i].delay = indexFile.readUint16LE(); _sceneInfo->animatedDetails[i].delayCount = indexFile.readUint16LE(); _sceneInfo->animatedDetails[i].reverse = indexFile.readUint16LE(); _sceneInfo->animatedDetails[i].timerStart = indexFile.readUint16LE(); _sceneInfo->animatedDetails[i].zIndex = indexFile.readUint16LE(); _sceneInfo->animatedDetails[i].loadFlag = indexFile.readByte(); _sceneInfo->animatedDetails[i].zoom = indexFile.readByte(); SoundInfo *s = &_sceneInfo->animatedDetails[i].soundInfo; readUSoundInfo(indexFile, s->enable); readSSoundInfo(indexFile, s->index); readUSoundInfo(indexFile, s->start); readUSoundInfo(indexFile, s->channel); readUSoundInfo(indexFile, s->volume); readUSoundInfo(indexFile, s->repeatCount); readUSoundInfo(indexFile, s->stereo); //debug("Sound %i: %i, %i, %i", i, s->index[0], s->index[1], s->index[2]); _sceneInfo->animatedDetails[i].showOneFrame = indexFile.readUint16LE(); _sceneInfo->animatedDetails[i].currentFrame = indexFile.readUint16LE(); } // Static details for (int i = 0; i < MAX_DETAILS; i++) { _sceneInfo->staticDetails[i].x = indexFile.readSint16LE(); _sceneInfo->staticDetails[i].y = indexFile.readSint16LE(); _sceneInfo->staticDetails[i].spriteNum = indexFile.readSint16LE(); _sceneInfo->staticDetails[i].zIndex = indexFile.readUint16LE(); _sceneInfo->staticDetails[i].hide = indexFile.readByte(); indexFile.readByte(); // padding } // Hotspots for (int i = 0; i < MAX_HOTSPOTS; i++) { _sceneInfo->hotspot[i].rect.left = indexFile.readUint16LE(); _sceneInfo->hotspot[i].rect.top = indexFile.readUint16LE(); _sceneInfo->hotspot[i].rect.right = indexFile.readUint16LE(); _sceneInfo->hotspot[i].rect.bottom = indexFile.readUint16LE(); if (!_sceneInfo->hotspot[i].rect.isValidRect()) warning("Hotspot %d has an invalid rect", i); } // Hotspot descriptions for (int i = 0; i < MAX_HOTSPOTS; i++) { _sceneInfo->hotspot[i].resource = indexFile.readUint16LE() + 4; _sceneInfo->hotspot[i].desc = ""; _sceneInfo->hotspot[i].speechId = -1; if (_sceneInfo->hotspot[i].resource < kATSTextMax) { TextEntry *entry = text->getText(_curScene + kADSTextMax, _sceneInfo->hotspot[i].resource); if (entry) { _sceneInfo->hotspot[i].desc = entry->text; //_sceneInfo->hotspot[i].speechId = entry->speechId; // TODO //debug("Hotspot %i: '%s', resource %d", i, entry->text.c_str(), _sceneInfo->hotspot[i].resource); } } } // Room info _sceneInfo->roomInfo.roomNum = indexFile.readByte(); _sceneInfo->roomInfo.picNum = indexFile.readByte(); _sceneInfo->roomInfo.autoMoveCount = indexFile.readByte(); _sceneInfo->roomInfo.loadTaf = indexFile.readByte(); _sceneInfo->roomInfo.tafName = ""; for (int i = 0; i < 14; i++) _sceneInfo->roomInfo.tafName += indexFile.readByte(); _sceneInfo->roomInfo.zoomFactor = indexFile.readByte(); indexFile.readByte(); // padding for (int i = 0; i < MAX_AUTOMOVE; i++) { _sceneInfo->autoMove[i].x = indexFile.readSint16LE(); _sceneInfo->autoMove[i].y = indexFile.readSint16LE(); _sceneInfo->autoMove[i].spriteNum = indexFile.readByte(); indexFile.readByte(); // padding if (i > _sceneInfo->roomInfo.autoMoveCount && !(_sceneInfo->autoMove[i].x <= 0 || _sceneInfo->autoMove[i].y <= 0)) warning("Auto move %d should be unused, but it isn't (max auto move items are %d)", i, _sceneInfo->roomInfo.autoMoveCount); } for (int i = 0; i < MAX_DETAILS * MAX_SOUNDS; i++) { _sceneInfo->hotspotSpeech[i] = indexFile.readSint16LE(); /*HotspotSpeech *hs = &_sceneInfo->hotspotSpeech[i]; hs->look = indexFile.readSint16LE(); hs->use = indexFile.readSint16LE(); hs->talk = indexFile.readSint16LE(); debug("Hotspot %d: (%d, %d, %d)", i, hs->look, hs->use, hs->talk);*/ } // The rest of the scene data is MAX_DETAILS * 3 * 4 bytes full of null or // invalid pointers to sound buffers, where sounds for each hotspot are // preloaded indexFile.skip(MAX_DETAILS * 3 * 4); delete text; indexFile.close(); } } // End of namespace Chewy