Files
scummvm-cursorfix/engines/stark/services/resourceprovider.cpp
2026-02-02 04:50:13 +01:00

417 lines
14 KiB
C++

/* 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 <http://www.gnu.org/licenses/>.
*
*/
#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<Resources::Root>("x.xarc");
_global->setRoot(root);
// Resources lifecycle update
root->onAllLoaded();
// Find the global level node
Resources::Level *global = root->findChildWithSubtype<Resources::Level>(Resources::Level::kGlobal);
// Load the global archive
Common::Path globalArchiveName = _archiveLoader->buildArchiveName(global);
_archiveLoader->load(globalArchiveName);
// Set the global tree
global = _archiveLoader->useRoot<Resources::Level>(globalArchiveName);
_global->setLevel(global);
// Resources lifecycle update
global->onAllLoaded();
// Load the state
_stateProvider->restoreLevelState(global);
_global->setInventory(global->findChildWithSubtype<Resources::KnowledgeSet>(Resources::KnowledgeSet::kInventory));
_global->setApril(global->findChildWithSubtype<Resources::GlobalItemTemplate>(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<Resources::Level>(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<Resources::Level>(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<Resources::Location>(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<Resources::Location>(locationArchive));
if (currentLocation->getLocation()->has3DLayer()) {
Resources::Layer3D *layer = currentLocation->getLocation()->findChildWithSubtype<Resources::Layer3D>(Resources::Layer::kLayer3D);
currentLocation->setFloor(layer->findChild<Resources::Floor>());
currentLocation->setCamera(layer->findChild<Resources::Camera>());
} 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<Resources::ModelItem>(_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<Resources::Script *> scripts = resource->listChildrenRecursive<Resources::Script>();
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<Resources::Sound *> sounds = resource->listChildrenRecursive<Resources::Sound>();
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<Resources::Bookmark>();
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