/* 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$ * */ // Scene management module #include "saga/saga.h" #include "saga/gfx.h" #include "saga/animation.h" #include "saga/console.h" #include "saga/interface.h" #include "saga/events.h" #include "saga/isomap.h" #include "saga/objectmap.h" #include "saga/palanim.h" #include "saga/puzzle.h" #include "saga/render.h" #include "saga/script.h" #include "saga/sound.h" #include "saga/music.h" #include "saga/scene.h" #include "saga/stream.h" #include "saga/actor.h" #include "saga/rscfile.h" #include "saga/sagaresnames.h" #include "graphics/iff.h" #include "common/util.h" namespace Saga { static int initSceneDoors[SCENE_DOORS_MAX] = { 0, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; static SAGAResourceTypes ITESceneResourceTypes[26] = { SAGA_ACTOR, SAGA_OBJECT, SAGA_BG_IMAGE, SAGA_BG_MASK, SAGA_UNKNOWN, SAGA_STRINGS, SAGA_OBJECT_MAP, SAGA_ACTION_MAP, SAGA_ISO_IMAGES, SAGA_ISO_MAP, SAGA_ISO_PLATFORMS, SAGA_ISO_METATILES, SAGA_ENTRY, SAGA_UNKNOWN, SAGA_ANIM, SAGA_ANIM, SAGA_ANIM, SAGA_ANIM, SAGA_ANIM, SAGA_ANIM, SAGA_ANIM, SAGA_ANIM, SAGA_ISO_MULTI, SAGA_PAL_ANIM, SAGA_FACES, SAGA_PALETTE }; static SAGAResourceTypes IHNMSceneResourceTypes[28] = { SAGA_ACTOR, SAGA_UNKNOWN, SAGA_BG_IMAGE, SAGA_BG_MASK, SAGA_UNKNOWN, SAGA_STRINGS, SAGA_OBJECT_MAP, SAGA_ACTION_MAP, SAGA_ISO_IMAGES, SAGA_ISO_MAP, SAGA_ISO_PLATFORMS, SAGA_ISO_METATILES, SAGA_ENTRY, SAGA_UNKNOWN, SAGA_ANIM, SAGA_ANIM, SAGA_ANIM, SAGA_ANIM, SAGA_ANIM, SAGA_ANIM, SAGA_ANIM, SAGA_ANIM, SAGA_ANIM, SAGA_ANIM, SAGA_ISO_MULTI, SAGA_PAL_ANIM, SAGA_FACES, SAGA_PALETTE }; const char *SAGAResourceTypesString[] = { "SAGA_UNKNOWN", "SAGA_ACTOR", "SAGA_OBJECT", "SAGA_BG_IMAGE", "SAGA_BG_MASK", "SAGA_STRINGS", "SAGA_OBJECT_MAP", "SAGA_ACTION_MAP", "SAGA_ISO_IMAGES", "SAGA_ISO_MAP", "SAGA_ISO_PLATFORMS", "SAGA_ISO_METATILES", "SAGA_ENTRY", "SAGA_ANIM", "SAGA_ISO_MULTI", "SAGA_PAL_ANIM", "SAGA_FACES", "SAGA_PALETTE" }; Scene::Scene(SagaEngine *vm) : _vm(vm) { byte *sceneLUTPointer; size_t sceneLUTLength; uint32 resourceId; int i; // Load scene module resource context _sceneContext = _vm->_resource->getContext(GAME_RESOURCEFILE); if (_sceneContext == NULL) { error("Scene::Scene() scene context not found"); } // Load scene lookup table resourceId = _vm->_resource->convertResourceId(_vm->getResourceDescription()->sceneLUTResourceId); debug(3, "Loading scene LUT from resource %i", resourceId); _vm->_resource->loadResource(_sceneContext, resourceId, sceneLUTPointer, sceneLUTLength); if (sceneLUTLength == 0) { error("Scene::Scene() sceneLUTLength == 0"); } _sceneCount = sceneLUTLength / 2; _sceneLUT = (int *)malloc(_sceneCount * sizeof(*_sceneLUT)); if (_sceneLUT == NULL) { memoryError("Scene::Scene()"); } MemoryReadStreamEndian readS(sceneLUTPointer, sceneLUTLength, _sceneContext->isBigEndian); for (i = 0; i < _sceneCount; i++) { _sceneLUT[i] = readS.readUint16(); debug(8, "sceneNumber %i has resourceId %i", i, _sceneLUT[i]); } free(sceneLUTPointer); #define DUMP_SCENES_LEVEL 10 if (DUMP_SCENES_LEVEL <= gDebugLevel) { uint j; int backUpDebugLevel = gDebugLevel; SAGAResourceTypes *types; int typesCount; SAGAResourceTypes resType; getResourceTypes(types, typesCount); for (i = 0; i < _sceneCount; i++) { gDebugLevel = -1; loadSceneDescriptor(_sceneLUT[i]); loadSceneResourceList(_sceneDescription.resourceListResourceId); gDebugLevel = backUpDebugLevel; debug(DUMP_SCENES_LEVEL, "Dump Scene: number %i, descriptor resourceId %i, resourceList resourceId %i", i, _sceneLUT[i], _sceneDescription.resourceListResourceId); debug(DUMP_SCENES_LEVEL, "\tresourceListCount %i", (int)_resourceListCount); for (j = 0; j < _resourceListCount; j++) { if (_resourceList[j].resourceType >= typesCount) { error("wrong resource type %i", _resourceList[j].resourceType); } resType = types[_resourceList[j].resourceType]; debug(DUMP_SCENES_LEVEL, "\t%s resourceId %i", SAGAResourceTypesString[resType], _resourceList[j].resourceId); } free(_resourceList); } } debug(3, "LUT has %d entries.", _sceneCount); _sceneLoaded = false; _sceneNumber = 0; _sceneResourceId = 0; _inGame = false; _loadDescription = false; memset(&_sceneDescription, 0, sizeof(_sceneDescription)); _resourceListCount = 0; _resourceList = NULL; _sceneProc = NULL; _objectMap = new ObjectMap(_vm); _actionMap = new ObjectMap(_vm); memset(&_bg, 0, sizeof(_bg)); memset(&_bgMask, 0, sizeof(_bgMask)); } Scene::~Scene() { delete _actionMap; delete _objectMap; free(_sceneLUT); } void Scene::getResourceTypes(SAGAResourceTypes *&types, int &typesCount) { if (_vm->getGameType() == GType_IHNM) { typesCount = ARRAYSIZE(IHNMSceneResourceTypes); types = IHNMSceneResourceTypes; } else { typesCount = ARRAYSIZE(ITESceneResourceTypes); types = ITESceneResourceTypes; } } void Scene::drawTextList(Surface *ds) { TextListEntry *entry; for (TextList::iterator textIterator = _textList.begin(); textIterator != _textList.end(); ++textIterator) { entry = (TextListEntry *)textIterator.operator->(); if (entry->display) { if (entry->useRect) { _vm->_font->textDrawRect(entry->font, ds, entry->text, entry->rect, _vm->KnownColor2ColorId(entry->knownColor), _vm->KnownColor2ColorId(entry->effectKnownColor), entry->flags); } else { _vm->_font->textDraw(entry->font, ds, entry->text, entry->point, _vm->KnownColor2ColorId(entry->knownColor), _vm->KnownColor2ColorId(entry->effectKnownColor), entry->flags); } } } } void Scene::startScene() { SceneQueueList::iterator queueIterator; LoadSceneParams *sceneQueue; Event event; if (_sceneLoaded) { error("Scene::start(): Error: Can't start game...scene already loaded"); } if (_inGame) { error("Scene::start(): Error: Can't start game...game already started"); } // Hide cursor during intro event.type = kEvTOneshot; event.code = kCursorEvent; event.op = kEventHide; _vm->_events->queue(&event); switch (_vm->getGameType()) { case GType_ITE: ITEStartProc(); break; case GType_IHNM: IHNMStartProc(); break; default: error("Scene::start(): Error: Can't start game... gametype not supported"); break; } // Load the head in scene queue queueIterator = _sceneQueue.begin(); if (queueIterator == _sceneQueue.end()) { return; } sceneQueue = queueIterator.operator->(); loadScene(sceneQueue); } void Scene::nextScene() { SceneQueueList::iterator queueIterator; LoadSceneParams *sceneQueue; if (!_sceneLoaded) { error("Scene::next(): Error: Can't advance scene...no scene loaded"); } if (_inGame) { error("Scene::next(): Error: Can't advance scene...game already started"); } endScene(); // Delete the current head in scene queue queueIterator = _sceneQueue.begin(); if (queueIterator == _sceneQueue.end()) { return; } queueIterator = _sceneQueue.erase(queueIterator); if (queueIterator == _sceneQueue.end()) { return; } // Load the head in scene queue sceneQueue = queueIterator.operator->(); loadScene(sceneQueue); } void Scene::skipScene() { SceneQueueList::iterator queueIterator; LoadSceneParams *sceneQueue = NULL; LoadSceneParams *skipQueue = NULL; if (!_sceneLoaded) { error("Scene::skip(): Error: Can't skip scene...no scene loaded"); } if (_inGame) { error("Scene::skip(): Error: Can't skip scene...game already started"); } // Walk down scene queue and try to find a skip target queueIterator = _sceneQueue.begin(); if (queueIterator == _sceneQueue.end()) { error("Scene::skip(): Error: Can't skip scene...no scenes in queue"); } ++queueIterator; while (queueIterator != _sceneQueue.end()) { sceneQueue = queueIterator.operator->(); assert(sceneQueue != NULL); if (sceneQueue->sceneSkipTarget) { skipQueue = sceneQueue; break; } ++queueIterator; } // If skip target found, remove preceding scenes and load if (skipQueue != NULL) { _sceneQueue.erase(_sceneQueue.begin(), queueIterator); endScene(); loadScene(skipQueue); } } static struct SceneSubstitutes { int sceneId; const char *message; const char *title; const char *image; } sceneSubstitutes[] = { { 7, "Tycho says he knows much about the northern lands. Can Rif convince " "the Dog to share this knowledge?", "The Home of Tycho Northpaw", "tycho.bbm" }, { 27, "The scene of the crime may hold many clues, but will the servants of " "the Sanctuary trust Rif?", "The Sanctuary of the Orb", "sanctuar.bbm" }, { 5, "The Rats hold many secrets that could guide Rif on his quest -- assuming " "he can get past the doorkeeper.", "The Rat Complex", "ratdoor.bbm" }, { 2, "The Ferrets enjoy making things and have the materials to do so. How can " "that help Rif?", "The Ferret Village", "ferrets.bbm" }, { 67, "What aid can the noble King of the Elks provide to Rif and his companions?", "The Realm of the Forest King", "elkenter.bbm" }, { 3, "The King holds Rif's sweetheart hostage. Will the Boar provide any " "assistance to Rif?", "The Great Hall of the Boar King", "boarhall.bbm" } }; void Scene::changeScene(int16 sceneNumber, int actorsEntrance, SceneTransitionType transitionType, int chapter) { debug(5, "Scene::changeScene(%d, %d, %d, %d)", sceneNumber, actorsEntrance, transitionType, chapter); // This is used for latter ITE demos where all places on world map except // Tent Faire are substituted with LBM picture and short description if (_vm->getFeatures() & GF_SCENE_SUBSTITUTES) { for (int i = 0; i < ARRAYSIZE(sceneSubstitutes); i++) { if (sceneSubstitutes[i].sceneId == sceneNumber) { Surface *backBuffer = _vm->_gfx->getBackBuffer(); Surface bbmBuffer; byte *pal, *colors; Common::File file; Rect rect; PalEntry cPal[PAL_ENTRIES]; _vm->_interface->setMode(kPanelSceneSubstitute); if (file.open(sceneSubstitutes[i].image)) { Graphics::decodePBM(file, bbmBuffer, pal); colors = pal; rect.setWidth(bbmBuffer.w); rect.setHeight(bbmBuffer.h); backBuffer->blit(rect, (const byte*)bbmBuffer.pixels); for (int j = 0; j < PAL_ENTRIES; j++) { cPal[j].red = *pal++; cPal[j].green = *pal++; cPal[j].blue = *pal++; } free(colors); _vm->_gfx->setPalette(cPal); } _vm->_interface->setStatusText("Click or Press Return to continue. Press Q to quit.", 96); _vm->_font->textDrawRect(kKnownFontMedium, backBuffer, sceneSubstitutes[i].title, Common::Rect(0, 7, _vm->getDisplayWidth(), 27), _vm->KnownColor2ColorId(kKnownColorBrightWhite), _vm->KnownColor2ColorId(kKnownColorBlack), kFontOutline); _vm->_font->textDrawRect(kKnownFontMedium, backBuffer, sceneSubstitutes[i].message, Common::Rect(24, getHeight() - 33, _vm->getDisplayWidth() - 11, getHeight()), _vm->KnownColor2ColorId(kKnownColorBrightWhite), _vm->KnownColor2ColorId(kKnownColorBlack), kFontOutline); return; } } } LoadSceneParams sceneParams; sceneParams.actorsEntrance = actorsEntrance; sceneParams.loadFlag = kLoadBySceneNumber; sceneParams.sceneDescriptor = sceneNumber; sceneParams.transitionType = transitionType; sceneParams.sceneProc = NULL; sceneParams.sceneSkipTarget = false; sceneParams.chapter = chapter; if (sceneNumber != -2) { endScene(); } loadScene(&sceneParams); } void Scene::getSlopes(int &beginSlope, int &endSlope) { beginSlope = getHeight() - _sceneDescription.beginSlope; endSlope = getHeight() - _sceneDescription.endSlope; } void Scene::getBGInfo(BGInfo &bgInfo) { bgInfo.buffer = _bg.buf; bgInfo.bufferLength = _bg.buf_len; bgInfo.bounds.left = 0; bgInfo.bounds.top = 0; if (_bg.w < _vm->getDisplayWidth()) { bgInfo.bounds.left = (_vm->getDisplayWidth() - _bg.w) / 2; } if (_bg.h < getHeight()) { bgInfo.bounds.top = (getHeight() - _bg.h) / 2; } bgInfo.bounds.setWidth(_bg.w); bgInfo.bounds.setHeight(_bg.h); } bool Scene::canWalk(const Point &testPoint) { int maskType; if (!_bgMask.loaded) { return true; } if (!validBGMaskPoint(testPoint)) { return true; } maskType = getBGMaskType(testPoint); return getDoorState(maskType) == 0; } bool Scene::offscreenPath(Point &testPoint) { Point point; if (!_bgMask.loaded) { return false; } point.x = clamp( 0, testPoint.x, _bgMask.w - 1 ); point.y = clamp( 0, testPoint.y, _bgMask.h - 1 ); if (point == testPoint) { return false; } if (point.y >= _bgMask.h - 1) { point.y = _bgMask.h - 2; } testPoint = point; return true; } void Scene::getBGMaskInfo(int &width, int &height, byte *&buffer, size_t &bufferLength) { if (!_bgMask.loaded) { error("Scene::getBGMaskInfo _bgMask not loaded"); } width = _bgMask.w; height = _bgMask.h; buffer = _bgMask.buf; bufferLength = _bgMask.buf_len; } void Scene::initDoorsState() { memcpy(_sceneDoors, initSceneDoors, sizeof (_sceneDoors) ); } void Scene::loadScene(LoadSceneParams *loadSceneParams) { size_t i; Event event; Event *q_event; static PalEntry current_pal[PAL_ENTRIES]; if (loadSceneParams->transitionType == kTransitionFade) _vm->_interface->setFadeMode(kFadeOut); // Change the cursor to an hourglass in IHNM event.type = kEvTOneshot; event.code = kCursorEvent; event.op = kEventSetBusyCursor; event.time = 0; _vm->_events->queue(&event); _chapterPointsChanged = false; if ((_vm->getGameType() == GType_IHNM) && (loadSceneParams->chapter != NO_CHAPTER_CHANGE)) { if (loadSceneParams->loadFlag != kLoadBySceneNumber) { error("loadScene wrong usage"); } if (loadSceneParams->chapter == 6) _vm->_interface->setLeftPortrait(0); _vm->_anim->freeCutawayList(); // FIXME: Freed script modules are not reloaded correctly when changing chapters. // This is apparent when returning back to the character selection screen, // where the scene script module is loaded incorrectly // Don't free them for now, but free them on game exit, like ITE. // This has no impact on the game itself (other than increased memory usage), // as each chapter uses a different module slot // TODO: Find out why the script modules are not loaded correctly when // changing chapters and uncomment this again //_vm->_script->freeModules(); // deleteAllScenes(); // installSomeAlarm() _vm->_interface->clearInventory(); _vm->_resource->loadGlobalResources(loadSceneParams->chapter, loadSceneParams->actorsEntrance); _vm->_interface->addToInventory(IHNM_OBJ_PROFILE); _vm->_interface->activate(); if (loadSceneParams->chapter == 8 || loadSceneParams->chapter == -1) { if (_vm->getGameId() != GID_IHNM_DEMO) _vm->_interface->setMode(kPanelChapterSelection); else _vm->_interface->setMode(kPanelNull); } else { _vm->_interface->setMode(kPanelMain); } _inGame = true; _vm->_script->setVerb(_vm->_script->getVerbType(kVerbWalkTo)); if (loadSceneParams->sceneDescriptor == -2) { _vm->_interface->setFadeMode(kNoFade); return; } } if (_sceneLoaded) { error("Scene::loadScene(): Error, a scene is already loaded"); } _loadDescription = true; if (_vm->getGameType() == GType_IHNM) { if (loadSceneParams->loadFlag == kLoadBySceneNumber) // When will we get rid of it? if (loadSceneParams->sceneDescriptor <= 0) loadSceneParams->sceneDescriptor = _vm->_resource->_metaResource.sceneIndex; } switch (loadSceneParams->loadFlag) { case kLoadByResourceId: _sceneNumber = 0; // original assign zero for loaded by resource id _sceneResourceId = loadSceneParams->sceneDescriptor; break; case kLoadBySceneNumber: _sceneNumber = loadSceneParams->sceneDescriptor; _sceneResourceId = getSceneResourceId(_sceneNumber); break; case kLoadByDescription: _sceneNumber = -1; _sceneResourceId = -1; assert(loadSceneParams->sceneDescription != NULL); assert(loadSceneParams->sceneDescription->resourceList != NULL); _loadDescription = false; _sceneDescription = *loadSceneParams->sceneDescription; _resourceList = loadSceneParams->sceneDescription->resourceList; _resourceListCount = loadSceneParams->sceneDescription->resourceListCount; break; } debug(3, "Loading scene number %d:", _sceneNumber); if (_vm->getGameId() == GID_IHNM_DEMO && (_sceneNumber >= 144 && _sceneNumber <= 149)) { // WORKAROUND for the non-interactive part of the IHNM demo: When restarting the // non-interactive demo, opcode sfMainMode is incorrectly called. Therefore, if any // of the scenes of the non-interactive demo are loaded (scenes 144-149), set panel // to null and lock the user interface _vm->_interface->deactivate(); _vm->_interface->setMode(kPanelNull); } // Load scene descriptor and resource list resources if (_loadDescription) { debug(3, "Loading scene resource %i", _sceneResourceId); loadSceneDescriptor(_sceneResourceId); loadSceneResourceList(_sceneDescription.resourceListResourceId); } else { debug(3, "Loading memory scene resource"); } // Load resources from scene resource list for (i = 0; i < _resourceListCount; i++) { if (!_resourceList[i].invalid) { _vm->_resource->loadResource(_sceneContext, _resourceList[i].resourceId, _resourceList[i].buffer, _resourceList[i].size); if (_resourceList[i].size >= 6) { if (!memcmp(_resourceList[i].buffer, "DUMMY!", 6)) { _resourceList[i].invalid = true; warning("DUMMY resource %i", _resourceList[i].resourceId); } } } } // Process resources from scene resource list processSceneResources(); if (_sceneDescription.flags & kSceneFlagISO) { _outsetSceneNumber = _sceneNumber; _sceneClip.left = 0; _sceneClip.top = 0; _sceneClip.right = _vm->getDisplayWidth(); _sceneClip.bottom = getHeight(); } else { BGInfo backGroundInfo; getBGInfo(backGroundInfo); _sceneClip = backGroundInfo.bounds; if (!(_bg.w < _vm->getDisplayWidth() || _bg.h < getHeight())) _outsetSceneNumber = _sceneNumber; } _sceneLoaded = true; q_event = NULL; //fix placard bug //i guess we should remove RF_PLACARD flag - and use _interface->getMode() event.type = kEvTOneshot; event.code = kGraphicsEvent; event.op = kEventClearFlag; event.param = RF_PLACARD; q_event = _vm->_events->chain(q_event, &event); if (loadSceneParams->transitionType == kTransitionFade) { _vm->_interface->setFadeMode(kFadeOut); // Fade to black out _vm->_gfx->getCurrentPal(current_pal); event.type = kEvTImmediate; event.code = kPalEvent; event.op = kEventPalToBlack; event.time = 0; event.duration = kNormalFadeDuration; event.data = current_pal; q_event = _vm->_events->queue(&event); // set fade mode event.type = kEvTImmediate; event.code = kInterfaceEvent; event.op = kEventSetFadeMode; event.param = kNoFade; event.time = 0; event.duration = 0; q_event = _vm->_events->chain(q_event, &event); // Display scene background, but stay with black palette event.type = kEvTImmediate; event.code = kBgEvent; event.op = kEventDisplay; event.param = kEvPNoSetPalette; event.time = 0; event.duration = 0; q_event = _vm->_events->chain(q_event, &event); } // Start the scene pre script, but stay with black palette if (_sceneDescription.startScriptEntrypointNumber > 0) { event.type = kEvTOneshot; event.code = kScriptEvent; event.op = kEventExecBlocking; event.time = 0; event.param = _sceneDescription.scriptModuleNumber; event.param2 = _sceneDescription.startScriptEntrypointNumber; event.param3 = 0; // Action event.param4 = _sceneNumber; // Object event.param5 = loadSceneParams->actorsEntrance; // With Object event.param6 = 0; // Actor q_event = _vm->_events->chain(q_event, &event); } if (loadSceneParams->transitionType == kTransitionFade) { // set fade mode event.type = kEvTImmediate; event.code = kInterfaceEvent; event.op = kEventSetFadeMode; event.param = kFadeIn; event.time = 0; event.duration = 0; q_event = _vm->_events->chain(q_event, &event); // Fade in from black to the scene background palette event.type = kEvTImmediate; event.code = kPalEvent; event.op = kEventBlackToPal; event.time = 0; event.duration = kNormalFadeDuration; event.data = _bg.pal; q_event = _vm->_events->chain(q_event, &event); // set fade mode event.type = kEvTImmediate; event.code = kInterfaceEvent; event.op = kEventSetFadeMode; event.param = kNoFade; event.time = 0; event.duration = 0; q_event = _vm->_events->chain(q_event, &event); } if (loadSceneParams->sceneProc == NULL) { if (!_inGame && _vm->getGameType() == GType_ITE) { _inGame = true; _vm->_interface->setMode(kPanelMain); } _vm->_sound->stopAll(); if (_vm->getGameType() == GType_ITE) { if (_sceneDescription.musicResourceId >= 0) { event.type = kEvTOneshot; event.code = kMusicEvent; event.param = _sceneDescription.musicResourceId; event.param2 = MUSIC_DEFAULT; event.op = kEventPlay; event.time = 0; _vm->_events->queue(&event); } else { event.type = kEvTOneshot; event.code = kMusicEvent; event.op = kEventStop; event.time = 0; _vm->_events->queue(&event); } } // Set scene background event.type = kEvTOneshot; event.code = kBgEvent; event.op = kEventDisplay; event.param = kEvPSetPalette; event.time = 0; _vm->_events->queue(&event); // Begin palette cycle animation if present event.type = kEvTOneshot; event.code = kPalAnimEvent; event.op = kEventCycleStart; event.time = 0; q_event = _vm->_events->queue(&event); // Start the scene main script if (_sceneDescription.sceneScriptEntrypointNumber > 0) { event.type = kEvTOneshot; event.code = kScriptEvent; event.op = kEventExecNonBlocking; event.time = 0; event.param = _sceneDescription.scriptModuleNumber; event.param2 = _sceneDescription.sceneScriptEntrypointNumber; event.param3 = _vm->_script->getVerbType(kVerbEnter); // Action event.param4 = _sceneNumber; // Object event.param5 = loadSceneParams->actorsEntrance; // With Object event.param6 = 0; // Actor _vm->_events->queue(&event); } debug(3, "Scene started"); } else { loadSceneParams->sceneProc(SCENE_BEGIN, this); } // We probably don't want "followers" to go into scene -1 , 0. At the very // least we don't want garbage to be drawn that early in the ITE intro. if (_sceneNumber > 0 && _sceneNumber != ITE_SCENE_PUZZLE) _vm->_actor->updateActorsScene(loadSceneParams->actorsEntrance); if (_sceneNumber == ITE_SCENE_PUZZLE) _vm->_puzzle->execute(); if (getFlags() & kSceneFlagShowCursor) { // Activate user interface event.type = kEvTOneshot; event.code = kInterfaceEvent; event.op = kEventActivate; event.time = 0; _vm->_events->queue(&event); } // Change the cursor back to a crosshair in IHNM event.type = kEvTOneshot; event.code = kCursorEvent; event.op = kEventSetNormalCursor; event.time = 0; _vm->_events->queue(&event); } void Scene::loadSceneDescriptor(uint32 resourceId) { byte *sceneDescriptorData; size_t sceneDescriptorDataLength; memset(&_sceneDescription, 0, sizeof(_sceneDescription)); if (resourceId == 0) { return; } _vm->_resource->loadResource(_sceneContext, resourceId, sceneDescriptorData, sceneDescriptorDataLength); if (sceneDescriptorDataLength == 16) { MemoryReadStreamEndian readS(sceneDescriptorData, sceneDescriptorDataLength, _sceneContext->isBigEndian); _sceneDescription.flags = readS.readSint16(); _sceneDescription.resourceListResourceId = readS.readSint16(); _sceneDescription.endSlope = readS.readSint16(); _sceneDescription.beginSlope = readS.readSint16(); _sceneDescription.scriptModuleNumber = readS.readUint16(); _sceneDescription.sceneScriptEntrypointNumber = readS.readUint16(); _sceneDescription.startScriptEntrypointNumber = readS.readUint16(); _sceneDescription.musicResourceId = readS.readSint16(); } free(sceneDescriptorData); } void Scene::loadSceneResourceList(uint32 resourceId) { byte *resourceListData; size_t resourceListDataLength; size_t i; _resourceListCount = 0; _resourceList = NULL; if (resourceId == 0) { return; } // Load the scene resource table _vm->_resource->loadResource(_sceneContext, resourceId, resourceListData, resourceListDataLength); if ((resourceListDataLength % SAGA_RESLIST_ENTRY_LEN) == 0) { MemoryReadStreamEndian readS(resourceListData, resourceListDataLength, _sceneContext->isBigEndian); // Allocate memory for scene resource list _resourceListCount = resourceListDataLength / SAGA_RESLIST_ENTRY_LEN; debug(3, "Scene resource list contains %i entries", (int)_resourceListCount); _resourceList = (SceneResourceData *)calloc(_resourceListCount, sizeof(*_resourceList)); // Load scene resource list from raw scene // resource table debug(3, "Loading scene resource list"); for (i = 0; i < _resourceListCount; i++) { _resourceList[i].resourceId = readS.readUint16(); _resourceList[i].resourceType = readS.readUint16(); // demo version may contain invalid resourceId _resourceList[i].invalid = !_vm->_resource->validResourceId(_sceneContext, _resourceList[i].resourceId); } } free(resourceListData); } void Scene::processSceneResources() { byte *resourceData; size_t resourceDataLength; const byte *palPointer; size_t i; SAGAResourceTypes *types; int typesCount; SAGAResourceTypes resType; getResourceTypes(types, typesCount); // Process the scene resource list for (i = 0; i < _resourceListCount; i++) { if (_resourceList[i].invalid) { continue; } resourceData = _resourceList[i].buffer; resourceDataLength = _resourceList[i].size; if (_resourceList[i].resourceType >= typesCount) { error("Scene::processSceneResources() wrong resource type %i", _resourceList[i].resourceType); } resType = types[_resourceList[i].resourceType]; switch (resType) { case SAGA_UNKNOWN: warning("UNKNOWN resourceType %i", _resourceList[i].resourceType); break; case SAGA_ACTOR: //for (a = actorsInScene; a; a = a->nextInScene) // if (a->obj.figID == glist->file_id) // if (_vm->getGameType() == GType_ITE || // ((a->obj.flags & ACTORF_FINAL_FACE) & 0xff)) // a->sprites = (xSpriteSet *)glist->offset; warning("STUB: unimplemeted handler of SAGA_ACTOR resource"); break; case SAGA_OBJECT: break; case SAGA_BG_IMAGE: // Scene background resource if (_bg.loaded) { error("Scene::processSceneResources() Multiple background resources encountered"); } debug(3, "Loading background resource."); _bg.res_buf = resourceData; _bg.res_len = resourceDataLength; _bg.loaded = 1; if (_vm->decodeBGImage(_bg.res_buf, _bg.res_len, &_bg.buf, &_bg.buf_len, &_bg.w, &_bg.h) != SUCCESS) { error("Scene::processSceneResources() Error loading background resource %i", _resourceList[i].resourceId); } palPointer = _vm->getImagePal(_bg.res_buf, _bg.res_len); memcpy(_bg.pal, palPointer, sizeof(_bg.pal)); break; case SAGA_BG_MASK: // Scene background mask resource if (_bgMask.loaded) { error("Scene::ProcessSceneResources(): Duplicate background mask resource encountered"); } debug(3, "Loading BACKGROUND MASK resource."); _bgMask.res_buf = resourceData; _bgMask.res_len = resourceDataLength; _bgMask.loaded = 1; _vm->decodeBGImage(_bgMask.res_buf, _bgMask.res_len, &_bgMask.buf, &_bgMask.buf_len, &_bgMask.w, &_bgMask.h, true); // At least in ITE the mask needs to be clipped. _bgMask.w = MIN(_bgMask.w, _vm->getDisplayWidth()); _bgMask.h = MIN(_bgMask.h, getHeight()); debug(4, "BACKGROUND MASK width=%d height=%d length=%d", _bgMask.w, _bgMask.h, (int)_bgMask.buf_len); break; case SAGA_STRINGS: debug(3, "Loading scene strings resource..."); _vm->loadStrings(_sceneStrings, resourceData, resourceDataLength); break; case SAGA_OBJECT_MAP: debug(3, "Loading object map resource..."); _objectMap->load(resourceData, resourceDataLength); break; case SAGA_ACTION_MAP: debug(3, "Loading action map resource..."); _actionMap->load(resourceData, resourceDataLength); break; case SAGA_ISO_IMAGES: if (!(_sceneDescription.flags & kSceneFlagISO)) { error("Scene::ProcessSceneResources(): not Iso mode"); } debug(3, "Loading isometric images resource."); _vm->_isoMap->loadImages(resourceData, resourceDataLength); break; case SAGA_ISO_MAP: if (!(_sceneDescription.flags & kSceneFlagISO)) { error("Scene::ProcessSceneResources(): not Iso mode"); } debug(3, "Loading isometric map resource."); _vm->_isoMap->loadMap(resourceData, resourceDataLength); break; case SAGA_ISO_PLATFORMS: if (!(_sceneDescription.flags & kSceneFlagISO)) { error("Scene::ProcessSceneResources(): not Iso mode"); } debug(3, "Loading isometric platforms resource."); _vm->_isoMap->loadPlatforms(resourceData, resourceDataLength); break; case SAGA_ISO_METATILES: if (!(_sceneDescription.flags & kSceneFlagISO)) { error("Scene::ProcessSceneResources(): not Iso mode"); } debug(3, "Loading isometric metatiles resource."); _vm->_isoMap->loadMetaTiles(resourceData, resourceDataLength); break; case SAGA_ANIM: { uint16 animId = _resourceList[i].resourceType - 14; debug(3, "Loading animation resource animId=%i", animId); _vm->_anim->load(animId, resourceData, resourceDataLength); } break; case SAGA_ENTRY: debug(3, "Loading entry list resource..."); loadSceneEntryList(resourceData, resourceDataLength); break; case SAGA_ISO_MULTI: if (!(_sceneDescription.flags & kSceneFlagISO)) { error("Scene::ProcessSceneResources(): not Iso mode"); } debug(3, "Loading isometric multi resource."); _vm->_isoMap->loadMulti(resourceData, resourceDataLength); break; case SAGA_PAL_ANIM: debug(3, "Loading palette animation resource."); _vm->_palanim->loadPalAnim(resourceData, resourceDataLength); break; case SAGA_FACES: if (_vm->getGameType() == GType_ITE) _vm->_interface->loadScenePortraits(_resourceList[i].resourceId); break; case SAGA_PALETTE: { PalEntry pal[PAL_ENTRIES]; byte *palPtr = resourceData; if (resourceDataLength < 3 * PAL_ENTRIES) error("Too small scene palette %i", (int)resourceDataLength); for (uint16 c = 0; c < PAL_ENTRIES; c++) { pal[c].red = *palPtr++; pal[c].green = *palPtr++; pal[c].blue = *palPtr++; } _vm->_gfx->setPalette(pal); } break; default: error("Scene::ProcessSceneResources() Encountered unknown resource type %i", _resourceList[i].resourceType); break; } } } void Scene::draw() { Surface *backBuffer; Surface *backGroundSurface; Rect rect; backBuffer = _vm->_gfx->getBackBuffer(); backGroundSurface = _vm->_render->getBackGroundSurface(); if (_sceneDescription.flags & kSceneFlagISO) { _vm->_isoMap->adjustScroll(false); _vm->_isoMap->draw(backBuffer); } else { backGroundSurface->getRect(rect); if (_sceneClip.bottom < rect.bottom) { rect.bottom = getHeight(); } backBuffer->blit(rect, (const byte *)backGroundSurface->pixels); } } void Scene::endScene() { Surface *backBuffer; Surface *backGroundSurface; Rect rect; size_t i; if (!_sceneLoaded) return; debug(3, "Ending scene..."); if (_sceneProc != NULL) { _sceneProc(SCENE_END, this); } // _vm->_script->abortAllThreads(); _vm->_script->_skipSpeeches = false; // Copy current screen to render buffer so inset rooms will get proper background backGroundSurface = _vm->_render->getBackGroundSurface(); if (!(_sceneDescription.flags & kSceneFlagISO) && !_vm->_scene->isInIntro()) { BGInfo bgInfo; _vm->_scene->getBGInfo(bgInfo); backGroundSurface->blit(bgInfo.bounds, bgInfo.buffer); } else { backBuffer = _vm->_gfx->getBackBuffer(); backBuffer->getRect(rect); backGroundSurface->blit(rect, (const byte *)backBuffer->pixels); } // Free scene background if (_bg.loaded) { free(_bg.buf); _bg.loaded = 0; } // Free scene background mask if (_bgMask.loaded) { free(_bgMask.buf); _bgMask.loaded = 0; } // Free scene resource list for (i = 0; i < _resourceListCount; i++) { free(_resourceList[i].buffer); } if (_loadDescription) { free(_resourceList); } // Free animation info list _vm->_anim->reset(); _vm->_palanim->freePalAnim(); _objectMap->freeMem(); _actionMap->freeMem(); _entryList.freeMem(); _sceneStrings.freeMem(); _vm->_isoMap->freeMem(); _vm->_events->clearList(); _textList.clear(); _sceneLoaded = false; } void Scene::restoreScene() { // There is no implementation for tiled scenes, since this function is only used // in IHNM, which has no tiled scenes Event event; _vm->_gfx->showCursor(false); _vm->_gfx->restorePalette(); event.type = kEvTImmediate; event.code = kBgEvent; event.op = kEventDisplay; event.param = kEvPNoSetPalette; event.time = 0; event.duration = 0; _vm->_events->queue(&event); _vm->_gfx->showCursor(true); } void Scene::cmdSceneChange(int argc, const char **argv) { int scene_num = 0; scene_num = atoi(argv[1]); if ((scene_num < 1) || (scene_num >= _sceneCount)) { _vm->_console->DebugPrintf("Invalid scene number.\n"); return; } clearSceneQueue(); changeScene(scene_num, 0, kTransitionNoFade); } void Scene::cmdActionMapInfo() { _actionMap->cmdInfo(); } void Scene::cmdObjectMapInfo() { _objectMap->cmdInfo(); } void Scene::loadSceneEntryList(const byte* resourcePointer, size_t resourceLength) { int i; _entryList.entryListCount = resourceLength / 8; MemoryReadStreamEndian readS(resourcePointer, resourceLength, _sceneContext->isBigEndian); if (_entryList.entryList) error("Scene::loadSceneEntryList entryList != NULL"); _entryList.entryList = (SceneEntry *) malloc(_entryList.entryListCount * sizeof(*_entryList.entryList)); if (_entryList.entryList == NULL) { memoryError("Scene::loadSceneEntryList"); } for (i = 0; i < _entryList.entryListCount; i++) { _entryList.entryList[i].location.x = readS.readSint16(); _entryList.entryList[i].location.y = readS.readSint16(); _entryList.entryList[i].location.z = readS.readSint16(); _entryList.entryList[i].facing = readS.readUint16(); } } void Scene::clearPlacard() { static PalEntry cur_pal[PAL_ENTRIES]; PalEntry *pal; Event event; Event *q_event; _vm->_interface->restoreMode(); _vm->_gfx->getCurrentPal(cur_pal); event.type = kEvTImmediate; event.code = kPalEvent; event.op = kEventPalToBlack; event.time = 0; event.duration = kNormalFadeDuration; event.data = cur_pal; q_event = _vm->_events->queue(&event); event.type = kEvTOneshot; event.code = kGraphicsEvent; event.op = kEventClearFlag; event.param = RF_PLACARD; q_event = _vm->_events->chain(q_event, &event); event.type = kEvTOneshot; event.code = kTextEvent; event.op = kEventRemove; event.data = _vm->_script->getPlacardTextEntry(); q_event = _vm->_events->chain(q_event, &event); _vm->_scene->getBGPal(pal); event.type = kEvTImmediate; event.code = kPalEvent; event.op = kEventBlackToPal; event.time = 0; event.duration = kNormalFadeDuration; event.data = pal; q_event = _vm->_events->chain(q_event, &event); event.type = kEvTOneshot; event.code = kCursorEvent; event.op = kEventShow; q_event = _vm->_events->chain(q_event, &event); event.type = kEvTOneshot; event.code = kScriptEvent; event.op = kEventThreadWake; event.param = kWaitTypePlacard; q_event = _vm->_events->chain(q_event, &event); } void Scene::showPsychicProfile(const char *text) { int textHeight; static PalEntry cur_pal[PAL_ENTRIES]; PalEntry *pal; TextListEntry textEntry; Event event; Event *q_event; if (_vm->_interface->getMode() == kPanelPlacard) return; _vm->_interface->rememberMode(); _vm->_interface->setMode(kPanelPlacard); _vm->_gfx->savePalette(); event.type = kEvTOneshot; event.code = kCursorEvent; event.op = kEventHide; q_event = _vm->_events->queue(&event); _vm->_gfx->getCurrentPal(cur_pal); event.type = kEvTImmediate; event.code = kPalEvent; event.op = kEventPalToBlack; event.time = 0; event.duration = kNormalFadeDuration; event.data = cur_pal; q_event = _vm->_events->chain(q_event, &event); event.type = kEvTOneshot; event.code = kInterfaceEvent; event.op = kEventClearStatus; q_event = _vm->_events->chain(q_event, &event); event.type = kEvTOneshot; event.code = kGraphicsEvent; event.op = kEventSetFlag; event.param = RF_PLACARD; q_event = _vm->_events->chain(q_event, &event); // Set the background and palette for the psychic profile event.type = kEvTOneshot; event.code = kPsychicProfileBgEvent; q_event = _vm->_events->chain(q_event, &event); _vm->_scene->_textList.clear(); if (text != NULL) { textHeight = _vm->_font->getHeight(kKnownFontVerb, text, 226, kFontCentered); textEntry.knownColor = kKnownColorBlack; textEntry.useRect = true; textEntry.rect.left = 245; textEntry.rect.setHeight(210 + 76); textEntry.rect.setWidth(226); textEntry.rect.top = 210 - textHeight; textEntry.font = kKnownFontVerb; textEntry.flags = (FontEffectFlags)(kFontCentered); textEntry.text = text; TextListEntry *_psychicProfileTextEntry = _vm->_scene->_textList.addEntry(textEntry); event.type = kEvTOneshot; event.code = kTextEvent; event.op = kEventDisplay; event.data = _psychicProfileTextEntry; q_event = _vm->_events->chain(q_event, &event); } _vm->_scene->getBGPal(pal); event.type = kEvTImmediate; event.code = kPalEvent; event.op = kEventBlackToPal; event.time = 0; event.duration = kNormalFadeDuration; event.data = pal; q_event = _vm->_events->chain(q_event, &event); event.type = kEvTOneshot; event.code = kScriptEvent; event.op = kEventThreadWake; event.param = kWaitTypePlacard; q_event = _vm->_events->chain(q_event, &event); } void Scene::clearPsychicProfile() { if (_vm->_interface->getMode() == kPanelPlacard || _vm->getGameId() == GID_IHNM_DEMO) { _vm->_scene->clearPlacard(); _vm->_scene->_textList.clear(); _vm->_actor->showActors(false); _vm->_gfx->restorePalette(); _vm->_scene->restoreScene(); _vm->_interface->activate(); } } void Scene::showIHNMDemoSpecialScreen() { _vm->_gfx->showCursor(true); _vm->_interface->clearInventory(); _vm->_scene->changeScene(150, 0, kTransitionFade); } } // End of namespace Saga