NANCY: Add action event processing

Implemented (almost) all of processActionRecords(), which is the primary logic processing function of the engine. Moved the playState structure from Scene to NancyEngine.
This commit is contained in:
fracturehill
2021-03-24 17:20:46 +01:00
committed by Eugene Sandulenko
parent f60ccc38b9
commit 0a4ed23f51
8 changed files with 238 additions and 87 deletions
+19 -9
View File
@@ -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
+7 -7
View File
@@ -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;
}
+135 -3
View File
@@ -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
+1
View File
@@ -43,6 +43,7 @@ public:
virtual ~Logic() {}
bool addNewActionRecord(Common::SeekableReadStream &inputData);
void processActionRecords();
protected:
virtual ActionRecord *createActionRecord(uint16 type);
+4
View File
@@ -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
+8 -3
View File
@@ -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;
+64 -64
View File
@@ -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
-1
View File
@@ -70,7 +70,6 @@ private:
};
public:
PlayState playState;
int32 playerTimeMinuteLength;
byte movementDirection;