ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

stark/resources/script.cpp

Go to the documentation of this file.
00001 /* ResidualVM - A 3D game interpreter
00002  *
00003  * ResidualVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the AUTHORS
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 #include "engines/stark/resources/script.h"
00024 
00025 #include "engines/stark/formats/xrc.h"
00026 
00027 #include "engines/stark/resources/anim.h"
00028 #include "engines/stark/resources/command.h"
00029 #include "engines/stark/resources/item.h"
00030 #include "engines/stark/resources/scroll.h"
00031 #include "engines/stark/resources/sound.h"
00032 #include "engines/stark/resources/speech.h"
00033 
00034 #include "engines/stark/services/dialogplayer.h"
00035 #include "engines/stark/services/global.h"
00036 #include "engines/stark/services/services.h"
00037 #include "engines/stark/services/stateprovider.h"
00038 #include "engines/stark/services/userinterface.h"
00039 
00040 #include "engines/stark/tools/decompiler.h"
00041 
00042 namespace Stark {
00043 namespace Resources {
00044 
00045 Script::~Script() {
00046 }
00047 
00048 Script::Script(Object *parent, byte subType, uint16 index, const Common::String &name) :
00049         Object(parent, subType, index, name),
00050         _scriptType(0),
00051         _runEvent(0),
00052         _minChapter(0),
00053         _maxChapter(999),
00054         _shouldResetGameSpeed(false),
00055         _enabled(false),
00056         _nextCommand(nullptr),
00057         _pauseTimeLeft(-1),
00058         _suspendingResource(nullptr),
00059         _resumeStatus(kResumeSuspend) {
00060     _type = TYPE;
00061 }
00062 
00063 void Script::readData(Formats::XRCReadStream *stream) {
00064     uint32 type = stream->readUint32LE();
00065     _runEvent = stream->readUint32LE();
00066     _minChapter = stream->readUint32LE();
00067     _maxChapter = stream->readUint32LE();
00068     _shouldResetGameSpeed = stream->readBool();
00069 
00070     _enabled = type == 0;
00071 
00072     switch (_subType) {
00073     case kSubTypeGameEvent:
00074         _scriptType = type == 2 ? kScriptTypePassiveDialog : kScriptTypeOnGameEvent;
00075         break;
00076     case kSubTypePlayerAction:
00077         _scriptType = kScriptTypeOnPlayerAction;
00078         break;
00079     case kSubTypeDialog:
00080         _scriptType = kScriptType4;
00081         break;
00082     default:
00083         error("Unknown script subtype %d for script %s", _subType, getName().c_str());
00084     }
00085 }
00086 
00087 void Script::onAllLoaded() {
00088     Object::onAllLoaded();
00089     reset();
00090 }
00091 
00092 void Script::onGameLoop() {
00093     Object::onGameLoop();
00094     execute(kCallModeGameLoop);
00095 }
00096 
00097 void Script::reset() {
00098     if (_suspendingResource && _suspendingResource->getType() == Type::kItem) {
00099         Item *item = _suspendingResource->cast<Item>(_suspendingResource);
00100         item->setMovement(nullptr);
00101     }
00102 
00103     _suspendingResource = nullptr;
00104     _resumeStatus = kResumeSuspend;
00105     _pauseTimeLeft = -1;
00106 
00107     _nextCommand = getBeginCommand();
00108 }
00109 
00110 bool Script::isOnBegin() {
00111     return _nextCommand && _nextCommand->getSubType() == Command::kCommandBegin;
00112 }
00113 
00114 bool Script::isOnEnd() {
00115     return _nextCommand && _nextCommand->getSubType() == Command::kCommandEnd;
00116 }
00117 
00118 Command *Script::getBeginCommand() {
00119     return findChildWithSubtype<Command>(Command::kCommandBegin, false);
00120 }
00121 
00122 bool Script::isEnabled() {
00123     switch (_scriptType) {
00124     case kScriptTypeOnGameEvent:
00125     case kScriptTypeOnPlayerAction:
00126         return _enabled;
00127     case kScriptType3:
00128         return false;
00129     case kScriptTypePassiveDialog:
00130     case kScriptType4:
00131         return true;
00132     default:
00133         error("Unknown script type %d for script %s", _scriptType, getName().c_str());
00134     }
00135 }
00136 
00137 void Script::enable(bool value) {
00138     if (_scriptType == kScriptTypeOnGameEvent || _scriptType == kScriptTypeOnPlayerAction) {
00139         _enabled = value;
00140     }
00141 }
00142 
00143 bool Script::shouldExecute(uint32 callMode) {
00144     if ((!isEnabled() && isOnBegin()) || !_nextCommand) {
00145         return false; // Don't execute disabled scripts
00146     }
00147 
00148     if (callMode == kCallModeGameLoop && !isOnBegin()) {
00149         return true; // Continue previously running script
00150     }
00151 
00152     if (_scriptType == kScriptTypeOnGameEvent) {
00153         if (_runEvent == kGameEventOnGameLoop && callMode != kCallModeGameLoop) {
00154             return false; // Wrong call mode for this script
00155         }
00156         if (_runEvent == kGameEventOnEnterLocation && callMode != kCallModeEnterLocation) {
00157             return false; // Wrong call mode for this script
00158         }
00159         if (_runEvent == kGameEventOnExitLocation && callMode != kCallModeExitLocation) {
00160             return false; // Wrong call mode for this script
00161         }
00162 
00163         Item *parentItem = findParent<Item>();
00164         if (parentItem && !parentItem->isEnabled()) {
00165             return false; // Disabled parent
00166         }
00167     } else if (_scriptType == kScriptTypePassiveDialog) {
00168         if (callMode != kCallModeDialogCreateSelections && callMode != kCallModeDialogAnswer) {
00169             return false; // Wrong call mode for this script
00170         }
00171     } else if (_scriptType == kScriptTypeOnPlayerAction) {
00172         if (callMode != kCallModePlayerAction) {
00173             return false; // Wrong call mode for this script
00174         }
00175     } else {
00176         return false; // Wrong script type
00177     }
00178 
00179     uint32 currentChapter = StarkGlobal->getCurrentChapter();
00180     if (currentChapter < _minChapter || currentChapter >= _maxChapter) {
00181         return false; // Wrong chapter
00182     }
00183 
00184     return true;
00185 }
00186 
00187 bool Script::isSuspended() {
00188     return _pauseTimeLeft >= 0 || _suspendingResource;
00189 }
00190 
00191 Object *Script::getSuspendingResource() const {
00192     return _suspendingResource;
00193 }
00194 
00195 void Script::updateSuspended() {
00196     if (_pauseTimeLeft >= 0) {
00197         // Decrease the remaining pause time
00198         _pauseTimeLeft -= StarkGlobal->getMillisecondsPerGameloop();
00199     } else {
00200         _pauseTimeLeft = -1;
00201     }
00202 
00203     if (_nextCommand->getSubType() == Command::kScriptPauseSkippable
00204             && (StarkUserInterface->wasInteractionDenied() || _pauseTimeLeft < 0)) {
00205         StarkUserInterface->setInteractive(true);
00206         _pauseTimeLeft = -1;
00207     }
00208 
00209     bool commandChanged = false;
00210 
00211     if (_suspendingResource) {
00212         // Check if the suspending resource is still active
00213         switch (_suspendingResource->getType().get()) {
00214         case Type::kDialog: {
00215             if (!StarkDialogPlayer->isRunning()) {
00216                 // Resume the script execution if the dialog is complete
00217                 _suspendingResource = nullptr;
00218             }
00219             break;
00220         }
00221         case Type::kFMV: {
00222             // Scripts are not running during an FMV, if we are here, then it has stopped playing
00223             _suspendingResource = nullptr;
00224             break;
00225         }
00226         case Type::kSoundItem: {
00227             Sound *soundItem = Object::cast<Sound>(_suspendingResource);
00228             if (!soundItem->isPlaying()) {
00229                 // Resume the script execution once the sound has stopped playing
00230                 _suspendingResource = nullptr;
00231             }
00232             break;
00233         }
00234         case Type::kSpeech: {
00235             Speech *speech = Object::cast<Speech>(_suspendingResource);
00236             if (!StarkDialogPlayer->isSpeechReady(speech) && !speech->isPlaying()) {
00237                 // Resume the script execution once the speech has stopped playing
00238                 _suspendingResource = nullptr;
00239             }
00240             break;
00241         }
00242         case Type::kScroll: {
00243             Scroll *scroll = Object::cast<Scroll>(_suspendingResource);
00244             if (!scroll->isActive()) {
00245                 // Resume the script execution once the scroll target position is reached
00246                 _suspendingResource = nullptr;
00247             }
00248             break;
00249         }
00250         case Type::kItem: {
00251             if (_nextCommand->getSubType() == Command::kWalkTo) {
00252                 if (_resumeStatus == kResumeComplete) {
00253                     // Resume the script execution once the item has stopped its movement
00254                     _suspendingResource = nullptr;
00255                     _nextCommand = _nextCommand->nextCommandIf(false);
00256                     commandChanged = true;
00257                 } else if (_resumeStatus == kResumeAbort) {
00258                     // Resume the script execution once the item has stopped its movement
00259                     _suspendingResource = nullptr;
00260                     _nextCommand = _nextCommand->nextCommandIf(true);
00261                     commandChanged = true;
00262                 }
00263             } else {
00264                 if (_resumeStatus != kResumeSuspend) {
00265                     // Resume the script execution once the item has stopped its movement
00266                     _suspendingResource = nullptr;
00267                 }
00268             }
00269             break;
00270         }
00271         case Type::kAnim: {
00272             Anim *anim = Object::cast<Anim>(_suspendingResource);
00273             if (anim->isDone()) {
00274                 anim->resetItem();
00275                 // Resume the script execution once the animation is complete
00276                 _suspendingResource = nullptr;
00277             }
00278             break;
00279         }
00280         default:
00281             error("Unhandled suspending resource type %s", _suspendingResource->getType().getName());
00282         }
00283     }
00284 
00285     if (_nextCommand->getSubType() == Command::kItemSetActivity && !_suspendingResource) {
00286         _nextCommand->resumeItemSetActivity();
00287     }
00288 
00289     if (!isSuspended() && _shouldResetGameSpeed) {
00290         StarkGlobal->setNormalSpeed();
00291     }
00292 
00293     if (!isSuspended() && !commandChanged) {
00294         // Resume to the next command
00295         goToNextCommand();
00296     }
00297 }
00298 
00299 void Script::stop() {
00300     reset();
00301     _enabled = false;
00302     _returnObjects.clear();
00303 }
00304 
00305 void Script::pause(int32 msecs) {
00306     _pauseTimeLeft = msecs;
00307 }
00308 
00309 void Script::suspend(Object *cause) {
00310     _suspendingResource = cause;
00311     _resumeStatus = kResumeSuspend;
00312 }
00313 
00314 void Script::setResumeStatus(ResumeStatus status) {
00315     _resumeStatus = status;
00316 }
00317 
00318 void Script::goToNextCommand() {
00319     _nextCommand = _nextCommand->nextCommand();
00320 }
00321 
00322 void Script::execute(uint32 callMode) {
00323     if (!shouldExecute(callMode)) {
00324         return;
00325     }
00326 
00327     if (isSuspended()) {
00328         // If the script is suspended, check if it can be resumed
00329         updateSuspended();
00330     }
00331 
00332     uint32 executedCommands = 0;
00333     while (1) {
00334         if (isSuspended()) {
00335             break;
00336         }
00337 
00338         if (!_nextCommand) {
00339             break; // No next command, stop here
00340         }
00341 
00342         if (isOnEnd()) {
00343             break; // Reached the end of the script
00344         }
00345 
00346         _nextCommand = _nextCommand->execute(callMode, this);
00347 
00348         executedCommands++;
00349 
00350         if (executedCommands > 50) {
00351             break; // Too many consecutive commands
00352         }
00353     }
00354 
00355     if (isOnEnd() || !_nextCommand) {
00356         // Reset ended scripts so they can be started again
00357         reset();
00358 
00359         // Check if we should return to some caller script
00360         if (!_returnObjects.empty()) {
00361             Object *callerObject = _returnObjects.back();
00362             _returnObjects.pop_back();
00363 
00364             // Resume execution of the caller object
00365             resumeCallerExecution(callerObject);
00366         }
00367     }
00368 }
00369 
00370 void Script::resumeCallerExecution(Object *callerObject) {
00371     switch (callerObject->getType().get()) {
00372         case Type::kCommand: {
00373             Command *callerCommand = Object::cast<Command>(callerObject);
00374             _nextCommand = callerCommand->nextCommand();
00375             break;
00376         }
00377         case Type::kDialog: {
00378             Dialog *callerDialog = Object::cast<Dialog>(callerObject);
00379             StarkDialogPlayer->resume(callerDialog);
00380             break;
00381         }
00382         default:
00383             error("Unhandled caller object type %s", callerObject->getType().getName());
00384     }
00385 }
00386 
00387 void Script::addReturnObject(Object *object) {
00388     _returnObjects.push_back(object);
00389 }
00390 
00391 void Script::print(uint depth) {
00392     printDescription(depth);
00393     printData();
00394 
00395     // Print anything that is not a command
00396     for (uint i = 0; i < _children.size(); i++) {
00397         if (_children[i]->getType() != Type::kCommand) {
00398             _children[i]->print(depth + 1);
00399         }
00400     }
00401 
00402     Tools::Decompiler *decompiler = new Tools::Decompiler(this);
00403 
00404     // Print the decompiled output
00405     printWithDepth(depth + 1, "Decompiled output");
00406     if (decompiler->getError() == "") {
00407         decompiler->printDecompiled();
00408     } else {
00409         debug("Decompilation failure: %s", decompiler->getError().c_str());
00410     }
00411 
00412     delete decompiler;
00413 }
00414 
00415 void Script::printData() {
00416     debug("scriptType: %d", _scriptType);
00417     debug("runEvent: %d", _runEvent);
00418     debug("minChapter: %d", _minChapter);
00419     debug("maxChapter: %d", _maxChapter);
00420     debug("shouldResetGameSpeed: %d", _shouldResetGameSpeed);
00421 }
00422 
00423 void Script::saveLoad(ResourceSerializer *serializer) {
00424     serializer->syncAsSint32LE(_enabled);
00425 }
00426 
00427 void Script::saveLoadCurrent(ResourceSerializer *serializer) {
00428     bool isStarted = !isOnBegin();
00429     serializer->syncAsUint32LE(isStarted);
00430 
00431     if (isStarted) {
00432         serializer->syncAsResourceReference(&_nextCommand);
00433 
00434         serializer->syncArraySize(_returnObjects);
00435         for (uint i = 0; i < _returnObjects.size(); i++) {
00436             serializer->syncAsResourceReference(&_returnObjects[i]);
00437         }
00438 
00439         serializer->syncAsSint32LE(_pauseTimeLeft);
00440         serializer->syncAsResourceReference(&_suspendingResource);
00441         serializer->syncAsSint32LE(_resumeStatus);
00442     }
00443 }
00444 
00445 } // End of namespace Resources
00446 } // End of namespace Stark


Generated on Sat Feb 16 2019 05:01:03 for ResidualVM by doxygen 1.7.1
curved edge   curved edge