diff --git a/engines/nancy/action/actionrecord.h b/engines/nancy/action/actionrecord.h index 7fe2cd05950..fe86e7b8eec 100644 --- a/engines/nancy/action/actionrecord.h +++ b/engines/nancy/action/actionrecord.h @@ -23,8 +23,11 @@ #ifndef NANCY_ACTION_ACTIONRECORD_H #define NANCY_ACTION_ACTIONRECORD_H +#include "engines/nancy/time.h" + #include "common/str.h" #include "common/stream.h" +#include "common/rect.h" namespace Nancy { @@ -34,11 +37,14 @@ enum DependencyType : byte { kNone = 0, kInventory = 1, kEventFlag = 2, - // ... - kPlayTime = 4, + kLogicCondition = 3, + kTotalTime = 4, kSceneTime = 5, + kPlayerTime = 6, + // ... kSceneCount = 9, // ... + kTimeOfDay = 12, kTimerNotDone = 13, kTimerDone = 14, kDifficultyLevel = 15 @@ -63,11 +69,13 @@ public: execType(0), dependencies(nullptr), numDependencies(0), - hasNoDependencies(0), - hasSatisfiedCondition(false), + isActive(0), + satisfiedDependencies(nullptr), + timers(nullptr), + orFlags(nullptr), isDone(false), state(ExecutionState::Start) {} - virtual ~ActionRecord() { delete[] dependencies; delete rawData; } + virtual ~ActionRecord() { delete[] dependencies; delete rawData; delete[] satisfiedDependencies; delete[] timers; delete orFlags; } virtual uint16 readData(Common::SeekableReadStream &stream) =0; virtual void execute(NancyEngine *engine) {}; @@ -88,12 +96,14 @@ public: // 0x32 data DependencyRecord *dependencies; // 0x36 byte numDependencies; // 0x3A - bool hasNoDependencies; // 0x3B, not sure abt this one - bool hasSatisfiedCondition; // 0x3C - int32 timers[12]; // 0x48, not sure how many there can be + bool isActive; // 0x3B + bool *satisfiedDependencies; // 0x3C + Time *timers; // 0x48 + bool *orFlags; // 0x78 bool isDone; // 0x84 + Common::Rect activeZone; // 0x89 ExecutionState state; // 0x91 - }; +}; } // End of namespace Nancy diff --git a/engines/nancy/action/recordtypes.cpp b/engines/nancy/action/recordtypes.cpp index 03fc7de6ab1..5db7d5fb1df 100644 --- a/engines/nancy/action/recordtypes.cpp +++ b/engines/nancy/action/recordtypes.cpp @@ -255,8 +255,8 @@ uint16 ResetAndStartTimer::readData(Common::SeekableReadStream &stream) { } void ResetAndStartTimer::execute(NancyEngine *engine) { - engine->sceneManager->playState.timerIsActive = true; - engine->sceneManager->playState.timerTime = 0; + engine->playState.timerIsActive = true; + engine->playState.timerTime = 0; isDone = true; } @@ -266,8 +266,8 @@ uint16 StopTimer::readData(Common::SeekableReadStream &stream) { } void StopTimer::execute(NancyEngine *engine) { - engine->sceneManager->playState.timerIsActive = false; - engine->sceneManager->playState.timerTime = 0; + engine->playState.timerIsActive = false; + engine->playState.timerTime = 0; isDone = true; } @@ -290,7 +290,7 @@ uint16 EventFlags::readData(Common::SeekableReadStream &stream) { void EventFlags::execute(NancyEngine *engine) { for (uint i = 0; i < 10; ++i) { if (descs[i].label != -1) { - engine->sceneManager->playState.eventFlags[descs[i].label] = descs[i].flag; + engine->playState.eventFlags[descs[i].label] = descs[i].flag; } } isDone = true; @@ -357,9 +357,9 @@ uint16 DifficultyLevel::readData(Common::SeekableReadStream &stream) { } void DifficultyLevel::execute(NancyEngine *engine) { - engine->sceneManager->playState.difficulty = difficulty; + engine->playState.difficulty = difficulty; if (flagLabel != -1) { - engine->sceneManager->playState.eventFlags[flagLabel] = (PlayState::Flag)flagCondition; + engine->playState.eventFlags[flagLabel] = (PlayState::Flag)flagCondition; } isDone = true; } diff --git a/engines/nancy/logic.cpp b/engines/nancy/logic.cpp index 770acfda51f..2f8cc71c36c 100644 --- a/engines/nancy/logic.cpp +++ b/engines/nancy/logic.cpp @@ -21,6 +21,8 @@ */ #include "engines/nancy/logic.h" +#include "engines/nancy/nancy.h" +#include "engines/nancy/playstate.h" #include "common/memstream.h" @@ -52,7 +54,10 @@ bool Logic::addNewActionRecord(Common::SeekableReadStream &inputData) { error("Invalid dependency data size!"); } - newRecord->dependencies = new DependencyRecord[newRecord->numDependencies]; + newRecord->dependencies = new DependencyRecord[newRecord->numDependencies](); + newRecord->satisfiedDependencies = new bool[newRecord->numDependencies](); + newRecord->timers = new Time[newRecord->numDependencies](); + newRecord->orFlags = new bool[newRecord->numDependencies](); // Initialize the dependencies data inputData.seek(/*0x32 + */localChunkSize); @@ -76,15 +81,142 @@ bool Logic::addNewActionRecord(Common::SeekableReadStream &inputData) { for (uint16 i = 0; i < newRecord->numDependencies; ++i) { if (newRecord->dependencies[i].orFlag == 1) { - // newRecord->0x78+i = true + newRecord->orFlags[i] = true; } else { - // 0x78+i = false + newRecord->orFlags[i] = false; } } + } else { + // Set new record to active if it doesn't depend on anything + newRecord->isActive = true; } _records.push_back(newRecord); return true; } +void Logic::processActionRecords() { + for (auto record : _records) { + if (record->isDone) { + continue; + } + + if (!record->isActive) { + if (record->numDependencies > 0) { + for (uint i = 0; i < record->numDependencies; ++i) { + if (record->satisfiedDependencies[i] != 0) { + DependencyRecord &dep = record->dependencies[i]; + switch (record->dependencies[1].type) { + case kNone: + record->satisfiedDependencies[i] = true; + case kInventory: + // TODO + break; + case kEventFlag: + if (_engine->playState.eventFlags[dep.label] == dep.condition) + // nancy1 has code for some timer array that never gets used + // and is discarded from nancy2 onward + record->satisfiedDependencies[i] = true; + break; + case kLogicCondition: + if (_engine->playState.logicConditions[dep.label] == dep.condition) { + // Wait for specified time before satisfying dependency condition + Time elapsed = _engine->playState.totalTime - _engine->playState.logicConditionsTimestamps[dep.label]; + if (elapsed >= record->timers[i]) + record->satisfiedDependencies[i] = true; + } + break; + case kTotalTime: + if (_engine->playState.totalTime >= record->timers[i]) + record->satisfiedDependencies[i] = true; + break; + case kSceneTime: + if (_engine->playState.sceneTime >= record->timers[i]) + record->satisfiedDependencies[i] = true; + break; + case kPlayerTime: + if (_engine->playState.playerTime >= record->timers[i]) + record->satisfiedDependencies[i] = true; + break; + /*case 7: + // TODO + break; + case 8: + // TODO + break;*/ + case kSceneCount: + // This dependency type keeps its data in the time variables + // Also, I'm pretty sure it never gets used + switch (dep.milliseconds) { + case 1: + if (dep.seconds < _engine->playState.sceneHitCount[dep.hours]) + record->satisfiedDependencies[i] = true; + break; + case 2: + if (dep.seconds > _engine->playState.sceneHitCount[dep.hours]) + record->satisfiedDependencies[i] = true; + break; + case 3: + if (dep.seconds == _engine->playState.sceneHitCount[dep.hours]) + record->satisfiedDependencies[i] = true; + break; + } + break; + /*case 10: + // TODO + break; + case 11: + // TODO + break;*/ + case kTimeOfDay: + if (dep.label == (byte)_engine->playState.timeOfDay) + record->satisfiedDependencies[i] = true; + break; + case kTimerNotDone: + if (_engine->playState.timerTime <= record->timers[i]) + record->satisfiedDependencies[i] = true; + break; + case kTimerDone: + if (_engine->playState.timerTime > record->timers[i]) + record->satisfiedDependencies[i] = true; + break; + case kDifficultyLevel: + if (dep.condition == _engine->playState.difficulty) + record->satisfiedDependencies[i] = true; + break; + default: + break; + } + } + } + + // An orFlag marks that its corresponding dependency and the one after it + // mutually satisfy each other; if one is satisfied, so is the other + for (int i = 1; i < record->numDependencies; ++i) { + if (record->orFlags[i-1]) { + if (record->satisfiedDependencies[i-1]) + record->satisfiedDependencies[i] = true; + if (record->satisfiedDependencies[i]) + record->satisfiedDependencies[i-1] = true; + } + } + + // Check if all dependencies have been satisfied, and activate the record if they have + uint satisfied = 0; + for (uint i = 0; i < record->numDependencies; ++i) { + if (record->satisfiedDependencies[i]) + ++satisfied; + } + + if (satisfied == record->numDependencies) + record->isActive = true; + } + } + + if (record->isActive) { + record->execute(_engine); + } + } +} + } // End of namespace Nancy \ No newline at end of file diff --git a/engines/nancy/logic.h b/engines/nancy/logic.h index c7e65ac9239..3a739977aa6 100644 --- a/engines/nancy/logic.h +++ b/engines/nancy/logic.h @@ -43,6 +43,7 @@ public: virtual ~Logic() {} bool addNewActionRecord(Common::SeekableReadStream &inputData); + void processActionRecords(); protected: virtual ActionRecord *createActionRecord(uint16 type); diff --git a/engines/nancy/nancy.h b/engines/nancy/nancy.h index d479d168f06..847758278cb 100644 --- a/engines/nancy/nancy.h +++ b/engines/nancy/nancy.h @@ -25,6 +25,7 @@ #include "nancy/console.h" #include "nancy/detection.h" +#include "nancy/playstate.h" #include "engines/engine.h" #include "common/file.h" @@ -114,6 +115,9 @@ public: SceneManager *sceneManager; GraphicsManager *graphics; InputManager *input; + + // Contains all player data + PlayState playState; protected: // Engine APIs diff --git a/engines/nancy/playstate.h b/engines/nancy/playstate.h index faea2637149..3d007318120 100644 --- a/engines/nancy/playstate.h +++ b/engines/nancy/playstate.h @@ -30,13 +30,18 @@ namespace Nancy { // A catch-all struct for storing all player progress and related variables // TODO split to PlayerState/SceneState struct PlayState { - enum Flag { kFalse = 1, kTrue = 2 }; + enum Flag { kFalse = 1, kTrue = 2 }; // Could be the other way around enum TimeOfDay { kDay, kNight, kDuskDawn }; + // These two are used to keep track of what options the player picked in the + // current conversation. Since they don't get stored they probably shouldn't + // be here + Flag logicConditions[30]; + Time logicConditionsTimestamps[30]; // Stores when the condition got satisfied + Flag inventory[11]; Flag eventFlags[672]; - // Second array with the same size as EventFlags that never gets used? - bool sceneHitCount[1000]; + byte sceneHitCount[1000]; uint16 difficulty; // 0, 1, 2 Time totalTime; Time sceneTime; diff --git a/engines/nancy/scene.cpp b/engines/nancy/scene.cpp index c367faada65..557aca1f571 100644 --- a/engines/nancy/scene.cpp +++ b/engines/nancy/scene.cpp @@ -62,11 +62,11 @@ void SceneManager::process() { void SceneManager::init() { for (uint i = 0; i < 672; ++i) { - playState.eventFlags[i] = PlayState::Flag::kFalse; + _engine->playState.eventFlags[i] = PlayState::Flag::kFalse; } for (uint i = 0; i < 1000; ++i) { - playState.sceneHitCount[i] = 0; + _engine->playState.sceneHitCount[i] = 0; } _sceneID = _engine->_firstSceneID; @@ -268,15 +268,15 @@ void SceneManager::load() { View &viewportDesc = _engine->graphics->viewportDesc; if (!hasLoadedFromSavefile) { - playState.currentMaxVerticalScroll = playState.queuedMaxVerticalScroll; - playState.currentViewFrame = playState.queuedViewFrame; + _engine->playState.currentMaxVerticalScroll = _engine->playState.queuedMaxVerticalScroll; + _engine->playState.currentViewFrame = _engine->playState.queuedViewFrame; if (currentScene.videoFormat == 1) { // TODO not sure this ever gets hit } else if (currentScene.videoFormat == 2) { // always start from the bottom - playState.verticalScroll = playState.currentMaxVerticalScroll; - playState.verticalScrollDelta = currentScene.verticalScrollDelta; + _engine->playState.verticalScroll = _engine->playState.currentMaxVerticalScroll; + _engine->playState.verticalScrollDelta = currentScene.verticalScrollDelta; } else { error("Unrecognized Scene summary chunk video file format"); } @@ -394,10 +394,10 @@ void SceneManager::run() { if (hasLoadedFromSavefile) { if (playTimeThisFrame > _stashedTickCount) { playTimeThisFrame -= _stashedTickCount; - playState.totalTime -= playTimeThisFrame; - playState.sceneTime -= playTimeThisFrame; - if (playState.timerIsActive) - playState.timerTime -= playTimeThisFrame; + _engine->playState.totalTime -= playTimeThisFrame; + _engine->playState.sceneTime -= playTimeThisFrame; + if (_engine->playState.timerIsActive) + _engine->playState.timerTime -= playTimeThisFrame; } } @@ -414,24 +414,24 @@ void SceneManager::run() { diff = playTimeThisFrame - _tickCount; _tickCount = playTimeThisFrame; } - playState.totalTime += diff; - if (playState.timerIsActive) - playState.timerTime += diff; - playState.sceneTime =+ diff; + _engine->playState.totalTime += diff; + if (_engine->playState.timerIsActive) + _engine->playState.timerTime += diff; + _engine->playState.sceneTime =+ diff; // Calculate the in-game time (playerTime) - if (playTimeThisFrame > playState.playerTimeNextMinute) { - playState.playerTime += 60000; // Add a minute - playState.playerTimeNextMinute = playTimeThisFrame + playerTimeMinuteLength; // Set when we're going to add the next minute + if (playTimeThisFrame > _engine->playState.playerTimeNextMinute) { + _engine->playState.playerTime += 60000; // Add a minute + _engine->playState.playerTimeNextMinute = playTimeThisFrame + playerTimeMinuteLength; // Set when we're going to add the next minute } // Set the time of day according to playerTime - if (playState.playerTime.getHours_alt() >= 7 && playState.playerTime.getHours_alt() < 18) { - playState.timeOfDay = playState.kDay; - } else if ((playState.playerTime.getHours_alt() >= 19 || playState.playerTime.getHours_alt() < 6)) { - playState.timeOfDay = playState.kNight; + if (_engine->playState.playerTime.getHours_alt() >= 7 && _engine->playState.playerTime.getHours_alt() < 18) { + _engine->playState.timeOfDay = _engine->playState.kDay; + } else if ((_engine->playState.playerTime.getHours_alt() >= 19 || _engine->playState.playerTime.getHours_alt() < 6)) { + _engine->playState.timeOfDay = _engine->playState.kNight; } else { - playState.timeOfDay = playState.kDuskDawn; + _engine->playState.timeOfDay = _engine->playState.kDuskDawn; } if (_engine->input->isClickValidLMB) { @@ -531,67 +531,67 @@ void SceneManager::run() { ) { switch(movementDirection) { case kLeft: - playState.currentViewFrame += 1; - if (playState.currentViewFrame >= (int16)_engine->graphics->getBackgroundFrameCount()) { - playState.currentViewFrame = 0; + _engine->playState.currentViewFrame += 1; + if (_engine->playState.currentViewFrame >= (int16)_engine->graphics->getBackgroundFrameCount()) { + _engine->playState.currentViewFrame = 0; } break; case kRight: - playState.currentViewFrame -= 1; - if (playState.currentViewFrame < 0) { - playState.currentViewFrame = (int16)_engine->graphics->getBackgroundFrameCount() -1; + _engine->playState.currentViewFrame -= 1; + if (_engine->playState.currentViewFrame < 0) { + _engine->playState.currentViewFrame = (int16)_engine->graphics->getBackgroundFrameCount() -1; } break; case kUp: - if (playState.verticalScroll != 0) { - int16 newScroll = playState.verticalScroll - playState.verticalScrollDelta; - playState.verticalScroll = MAX(newScroll, (int16)0); + if (_engine->playState.verticalScroll != 0) { + int16 newScroll = _engine->playState.verticalScroll - _engine->playState.verticalScrollDelta; + _engine->playState.verticalScroll = MAX(newScroll, (int16)0); } break; case kDown: - if (playState.verticalScroll != playState.currentMaxVerticalScroll) { - uint16 newScroll = playState.verticalScroll + playState.verticalScrollDelta; - playState.verticalScroll = MIN(newScroll, playState.currentMaxVerticalScroll); + if (_engine->playState.verticalScroll != _engine->playState.currentMaxVerticalScroll) { + uint16 newScroll = _engine->playState.verticalScroll + _engine->playState.verticalScrollDelta; + _engine->playState.verticalScroll = MIN(newScroll, _engine->playState.currentMaxVerticalScroll); } break; case kUp | kLeft: - if (playState.verticalScroll != 0) { - int16 newScroll = playState.verticalScroll - playState.verticalScrollDelta; - playState.verticalScroll = MAX(newScroll, (int16)0); + if (_engine->playState.verticalScroll != 0) { + int16 newScroll = _engine->playState.verticalScroll - _engine->playState.verticalScrollDelta; + _engine->playState.verticalScroll = MAX(newScroll, (int16)0); } - playState.currentViewFrame += 1; - if (playState.currentViewFrame >= (int16)_engine->graphics->getBackgroundFrameCount()) { - playState.currentViewFrame = 0; + _engine->playState.currentViewFrame += 1; + if (_engine->playState.currentViewFrame >= (int16)_engine->graphics->getBackgroundFrameCount()) { + _engine->playState.currentViewFrame = 0; } break; case kUp | kRight: - if (playState.verticalScroll != 0) { - int16 newScroll = playState.verticalScroll - playState.verticalScrollDelta; - playState.verticalScroll = MAX(newScroll, (int16)0); + if (_engine->playState.verticalScroll != 0) { + int16 newScroll = _engine->playState.verticalScroll - _engine->playState.verticalScrollDelta; + _engine->playState.verticalScroll = MAX(newScroll, (int16)0); } - playState.currentViewFrame -= 1; - if (playState.currentViewFrame < 0) { - playState.currentViewFrame = _engine->graphics->getBackgroundFrameCount() - 1; + _engine->playState.currentViewFrame -= 1; + if (_engine->playState.currentViewFrame < 0) { + _engine->playState.currentViewFrame = _engine->graphics->getBackgroundFrameCount() - 1; } break; case kDown | kLeft: - if (playState.verticalScroll != playState.currentMaxVerticalScroll) { - uint16 newScroll = playState.verticalScroll + playState.verticalScrollDelta; - playState.verticalScroll = MIN(newScroll, playState.currentMaxVerticalScroll); + if (_engine->playState.verticalScroll != _engine->playState.currentMaxVerticalScroll) { + uint16 newScroll = _engine->playState.verticalScroll + _engine->playState.verticalScrollDelta; + _engine->playState.verticalScroll = MIN(newScroll, _engine->playState.currentMaxVerticalScroll); } - playState.currentViewFrame += 1; - if (playState.currentViewFrame >= (int16)_engine->graphics->getBackgroundFrameCount()) { - playState.currentViewFrame = 0; + _engine->playState.currentViewFrame += 1; + if (_engine->playState.currentViewFrame >= (int16)_engine->graphics->getBackgroundFrameCount()) { + _engine->playState.currentViewFrame = 0; } break; case kDown | kRight: - if (playState.verticalScroll != playState.currentMaxVerticalScroll) { - uint16 newScroll = playState.verticalScroll + playState.verticalScrollDelta; - playState.verticalScroll = MIN(newScroll, playState.currentMaxVerticalScroll); + if (_engine->playState.verticalScroll != _engine->playState.currentMaxVerticalScroll) { + uint16 newScroll = _engine->playState.verticalScroll + _engine->playState.verticalScrollDelta; + _engine->playState.verticalScroll = MIN(newScroll, _engine->playState.currentMaxVerticalScroll); } - playState.currentViewFrame -= 1; - if (playState.currentViewFrame < 0) { - playState.currentViewFrame = _engine->graphics->getBackgroundFrameCount() -1; + _engine->playState.currentViewFrame -= 1; + if (_engine->playState.currentViewFrame < 0) { + _engine->playState.currentViewFrame = _engine->graphics->getBackgroundFrameCount() -1; } break; } @@ -606,22 +606,22 @@ void SceneManager::run() { } // Redraw the Background surface if we've moved - if (playState.currentViewFrame != playState.lastDrawnViewFrame) { + if (_engine->playState.currentViewFrame != _engine->playState.lastDrawnViewFrame) { if (currentScene.videoFormat == 1) { // TODO if it ever gets hit } else if (currentScene.videoFormat == 2) { _engine->graphics->_background.copyRectToSurface( - *_engine->graphics->getBackgroundFrame(playState.currentViewFrame), + *_engine->graphics->getBackgroundFrame(_engine->playState.currentViewFrame), 0, 0, - Common::Rect(0, playState.verticalScroll, _engine->graphics->getBackgroundWidth() - 1, - playState.verticalScroll + _engine->graphics->viewportDesc.srcBottom)); + Common::Rect(0, _engine->playState.verticalScroll, _engine->graphics->getBackgroundWidth() - 1, + _engine->playState.verticalScroll + _engine->graphics->viewportDesc.srcBottom)); } // TODO some if related to PlaySoundPanFrameAnchorAndDie - playState.lastDrawnViewFrame = playState.currentViewFrame; + _engine->playState.lastDrawnViewFrame = _engine->playState.currentViewFrame; // TODO function call that sets a val to 1 and 3 others to 0 } - //_engine->logic->processActionRecords(); + _engine->logic->processActionRecords(); // code that skips rendering for the first 12 frames??? why diff --git a/engines/nancy/scene.h b/engines/nancy/scene.h index ff96c12f60f..e752b30c62b 100644 --- a/engines/nancy/scene.h +++ b/engines/nancy/scene.h @@ -70,7 +70,6 @@ private: }; public: - PlayState playState; int32 playerTimeMinuteLength; byte movementDirection;