/* 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 3 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, see . * */ #include "engines/stark/services/resourceprovider.h" #include "engines/stark/resources/bookmark.h" #include "engines/stark/resources/camera.h" #include "engines/stark/resources/floor.h" #include "engines/stark/resources/item.h" #include "engines/stark/resources/knowledgeset.h" #include "engines/stark/resources/layer.h" #include "engines/stark/resources/level.h" #include "engines/stark/resources/location.h" #include "engines/stark/resources/root.h" #include "engines/stark/resources/script.h" #include "engines/stark/resources/sound.h" #include "engines/stark/services/services.h" #include "engines/stark/services/archiveloader.h" #include "engines/stark/services/global.h" #include "engines/stark/services/stateprovider.h" #include "engines/stark/services/userinterface.h" namespace Stark { ResourceProvider::ResourceProvider(ArchiveLoader *archiveLoader, StateProvider *stateProvider, Global *global) : _archiveLoader(archiveLoader), _stateProvider(stateProvider), _global(global), _locationChangeRequest(false), _restoreCurrentState(false), _nextDirection(0) { } void ResourceProvider::initGlobal() { // Load the root archive _archiveLoader->load("x.xarc"); // Set the root tree Resources::Root *root = _archiveLoader->useRoot("x.xarc"); _global->setRoot(root); // Resources lifecycle update root->onAllLoaded(); // Find the global level node Resources::Level *global = root->findChildWithSubtype(Resources::Level::kGlobal); // Load the global archive Common::Path globalArchiveName = _archiveLoader->buildArchiveName(global); _archiveLoader->load(globalArchiveName); // Set the global tree global = _archiveLoader->useRoot(globalArchiveName); _global->setLevel(global); // Resources lifecycle update global->onAllLoaded(); // Load the state _stateProvider->restoreLevelState(global); _global->setInventory(global->findChildWithSubtype(Resources::KnowledgeSet::kInventory)); _global->setApril(global->findChildWithSubtype(Resources::Item::kItemGlobalTemplate)); } Current *ResourceProvider::findLevel(uint16 level) const { for (CurrentList::const_iterator it = _locations.begin(); it != _locations.end(); it++) { if ((*it)->getLevel()->getIndex() == level) { return *it; } } return nullptr; } Current *ResourceProvider::findLocation(uint16 level, uint16 location) const { for (CurrentList::const_iterator it = _locations.begin(); it != _locations.end(); it++) { if ((*it)->getLevel()->getIndex() == level && (*it)->getLocation()->getIndex() == location) { return *it; } } return nullptr; } Resources::Level *ResourceProvider::getLevel(uint16 level) const { Current *current = findLevel(level); if (current) { return current->getLevel(); } return nullptr; } Resources::Location *ResourceProvider::getLocation(uint16 level, uint16 location) const { Current *current = findLocation(level, location); if (current) { return current->getLocation(); } return nullptr; } void ResourceProvider::pushAndChangeLocation(int16 level, int16 location) { pushCurrentLocation(); requestLocationChange(level, location); } void ResourceProvider::returnToPushedLocation() { popCurrentLocation(); } void ResourceProvider::pushCurrentLocation() { PreviousLocation current; current.level = _global->getCurrent()->getLevel()->getIndex(); current.location = _global->getCurrent()->getLocation()->getIndex(); current.inventoryOpen = StarkUserInterface->isInventoryOpen(); _locationStack.push_back(current); StarkUserInterface->inventoryOpen(false); } void ResourceProvider::popCurrentLocation() { if (_locationStack.empty()) { error("Empty location stack"); } else { PreviousLocation previous = _locationStack.back(); _locationStack.pop_back(); requestLocationChange(previous.level, previous.location); StarkUserInterface->inventoryOpen(previous.inventoryOpen); } } void ResourceProvider::requestLocationChange(uint16 level, uint16 location) { Current *currentLocation = new Current(); _locations.push_back(currentLocation); // Retrieve the level archive name Resources::Root *root = _global->getRoot(); Resources::Level *rootLevelResource = root->findChildWithIndex(level); Common::Path levelArchive = _archiveLoader->buildArchiveName(rootLevelResource); // Load the archive, and get the resource sub-tree root bool newlyLoaded = _archiveLoader->load(levelArchive); currentLocation->setLevel(_archiveLoader->useRoot(levelArchive)); // If we just loaded a resource tree, restore its state if (newlyLoaded) { currentLocation->getLevel()->onAllLoaded(); _stateProvider->restoreLevelState(currentLocation->getLevel()); } // Retrieve the location archive name Resources::Level *levelResource = currentLocation->getLevel(); Resources::Location *levelLocationResource = levelResource->findChildWithIndex(location); Common::Path locationArchive = _archiveLoader->buildArchiveName(levelResource, levelLocationResource); // Load the archive, and get the resource sub-tree root newlyLoaded = _archiveLoader->load(locationArchive); currentLocation->setLocation(_archiveLoader->useRoot(locationArchive)); if (currentLocation->getLocation()->has3DLayer()) { Resources::Layer3D *layer = currentLocation->getLocation()->findChildWithSubtype(Resources::Layer::kLayer3D); currentLocation->setFloor(layer->findChild()); currentLocation->setCamera(layer->findChild()); } else { currentLocation->setFloor(nullptr); currentLocation->setCamera(nullptr); } // If we just loaded a resource tree, restore its state if (newlyLoaded) { currentLocation->getLocation()->onAllLoaded(); _stateProvider->restoreLocationState(currentLocation->getLevel(), currentLocation->getLocation()); } _locationChangeRequest = true; } void ResourceProvider::performLocationChange() { Current *current = _locations.back(); Current *previous = _global->getCurrent(); bool levelChanged = !previous || previous->getLevel() != current->getLevel(); // Exit the previous location if (previous) { // Trigger location change scripts if (levelChanged) { runLocationChangeScripts(previous->getLevel(), Resources::Script::kCallModeExitLocation); } runLocationChangeScripts(previous->getLocation(), Resources::Script::kCallModeExitLocation); // Resources lifecycle update previous->getLocation()->onExitLocation(); previous->getLevel()->onExitLocation(); _global->getLevel()->onExitLocation(); } // Clear all pointers to location objects in the UI instances StarkUserInterface->clearLocationDependentState(); // Set the new current location _global->setCurrent(current); // Resources lifecycle update _global->getLevel()->onEnterLocation(); current->getLevel()->onEnterLocation(); current->getLocation()->onEnterLocation(); if (current->getLocation()->has3DLayer()) { // Fetch the scene item for April current->setInteractive(Resources::Object::cast(_global->getApril()->getSceneInstance())); } if (_restoreCurrentState) { _stateProvider->restoreGlobalState(_global->getLevel()); _stateProvider->restoreCurrentLevelState(current->getLevel()); _stateProvider->restoreCurrentLocationState(current->getLevel(), current->getLocation()); _restoreCurrentState = false; } else { setAprilInitialPosition(); setScrollInitialPosition(); // Trigger location change scripts if (levelChanged) { runLocationChangeScripts(current->getLevel(), Resources::Script::kCallModeEnterLocation); } runLocationChangeScripts(current->getLocation(), Resources::Script::kCallModeEnterLocation); } current->getLocation()->resetAnimationBlending(); purgeOldLocations(); _locationChangeRequest = false; } void ResourceProvider::runLocationChangeScripts(Resources::Object *resource, uint32 scriptCallMode) { Common::Array scripts = resource->listChildrenRecursive(); if (scriptCallMode == Resources::Script::kCallModeEnterLocation) { for (uint i = 0; i < scripts.size(); i++) { scripts[i]->reset(); } } for (uint i = 0; i < scripts.size(); i++) { scripts[i]->execute(scriptCallMode); } if (scriptCallMode == Resources::Script::kCallModeExitLocation) { Common::Array sounds = resource->listChildrenRecursive(); for (uint i = 0; i < sounds.size(); i++) { sounds[i]->stop(); } } } void ResourceProvider::setNextLocationPosition(const ResourceReference &bookmark, int32 direction) { _nextPositionBookmarkReference = bookmark; _nextDirection = direction; } void ResourceProvider::setAprilInitialPosition() { Current *current = _global->getCurrent(); Resources::ModelItem *april = current->getInteractive(); if (!april) { return; // No character } // Set the initial position for April if (!_nextPositionBookmarkReference.empty()) { Resources::Bookmark *position = _nextPositionBookmarkReference.resolve(); april->placeOnBookmark(position); Resources::Camera *camera = current->getCamera(); Math::Angle cameraAngle = camera->getHorizontalAngle(); april->setDirection(_nextDirection + cameraAngle); } else if (april->getFloorFaceIndex() <= 0) { // No target location provided, place April on the first floor face april->placeDefaultPosition(); } _nextPositionBookmarkReference = ResourceReference(); _nextDirection = 0; } void ResourceProvider::setScrollInitialPosition() { Current *current = _global->getCurrent(); Resources::Location *location = current->getLocation(); location->scrollToCharacterImmediate(); } void ResourceProvider::purgeOldLocations() { while (_locations.size() > 2) { Current *location = _locations.front(); _stateProvider->saveLocationState(location->getLevel(), location->getLocation()); _stateProvider->saveLevelState(location->getLevel()); _archiveLoader->returnRoot(_archiveLoader->buildArchiveName(location->getLevel(), location->getLocation())); _archiveLoader->returnRoot(_archiveLoader->buildArchiveName(location->getLevel())); delete location; _locations.pop_front(); } _archiveLoader->unloadUnused(); } void ResourceProvider::commitActiveLocationsState() { // Save active location states for (CurrentList::const_iterator it = _locations.begin(); it != _locations.end(); it++) { _stateProvider->saveLocationState((*it)->getLevel(), (*it)->getLocation()); _stateProvider->saveLevelState((*it)->getLevel()); } _stateProvider->saveLevelState(_global->getLevel()); // Save the current location "extended" state, to be able to restore them to the exact same state. Current *location = _global->getCurrent(); _stateProvider->saveCurrentLocationState(location->getLevel(), location->getLocation()); _stateProvider->saveCurrentLevelState(location->getLevel()); _stateProvider->saveGlobalState(_global->getLevel()); } void ResourceProvider::shutdown() { _stateProvider->clear(); _locationStack.clear(); // Flush the locations list for (CurrentList::const_iterator it = _locations.begin(); it != _locations.end(); it++) { Current *location = *it; _archiveLoader->returnRoot(_archiveLoader->buildArchiveName(location->getLevel(), location->getLocation())); _archiveLoader->returnRoot(_archiveLoader->buildArchiveName(location->getLevel())); delete location; } _locations.clear(); // Return the global resources if (_global->getLevel()) { _archiveLoader->returnRoot(_archiveLoader->buildArchiveName(_global->getLevel())); _global->setLevel(nullptr); } if (_global->getRoot()) { _archiveLoader->returnRoot("x.xarc"); _global->setRoot(nullptr); } _global->setCurrent(nullptr); _global->setInventory(nullptr); _global->setApril(nullptr); _archiveLoader->unloadUnused(); } Resources::Level *ResourceProvider::getLevelFromLocation(Resources::Location *location) const { for (CurrentList::const_iterator it = _locations.begin(); it != _locations.end(); it++) { if ((*it)->getLocation() == location) { return (*it)->getLevel(); } } return nullptr; } void ResourceProvider::readLocationStack(Common::SeekableReadStream *stream, uint32 version) { ResourceSerializer serializer(stream, nullptr, version); saveLoadLocationStack(serializer); } void ResourceProvider::writeLocationStack(Common::WriteStream *stream) { ResourceSerializer serializer(nullptr, stream, StateProvider::kSaveVersion); saveLoadLocationStack(serializer); } void ResourceProvider::saveLoadLocationStack(ResourceSerializer &serializer) { serializer.syncArraySize(_locationStack, 12); for (uint i = 0; i < _locationStack.size(); i++) { serializer.syncAsUint16LE(_locationStack[i].level); serializer.syncAsUint16LE(_locationStack[i].location); serializer.syncAsUint32LE(_locationStack[i].inventoryOpen); } } } // End of namespace Stark