Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

View File

@@ -0,0 +1,284 @@
/* 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 "mutationofjb/tasks/conversationtask.h"
#include "mutationofjb/assets.h"
#include "mutationofjb/game.h"
#include "mutationofjb/gamedata.h"
#include "mutationofjb/gamescreen.h"
#include "mutationofjb/script.h"
#include "mutationofjb/tasks/saytask.h"
#include "mutationofjb/tasks/sequentialtask.h"
#include "mutationofjb/tasks/taskmanager.h"
#include "mutationofjb/util.h"
#include "mutationofjb/widgets/conversationwidget.h"
namespace MutationOfJB {
void ConversationTask::start() {
setState(RUNNING);
Game &game = getTaskManager()->getGame();
game.getGameScreen().showConversationWidget(true);
ConversationWidget &widget = game.getGameScreen().getConversationWidget();
widget.setCallback(this);
_currentGroupIndex = 0;
showChoicesOrPick();
}
void ConversationTask::update() {
if (_sayTask) {
if (_sayTask->getState() == Task::FINISHED) {
_sayTask.reset();
switch (_substate) {
case SAYING_NO_QUESTIONS:
finish();
break;
case SAYING_QUESTION: {
const ConversationLineList &responseList = getTaskManager()->getGame().getAssets().getResponseList();
const ConversationLineList::Line *const line = responseList.getLine(_currentItem->_response);
_substate = SAYING_RESPONSE;
createSayTasks(line);
getTaskManager()->startTask(_sayTask);
break;
}
case SAYING_RESPONSE: {
startExtra();
if (_substate != RUNNING_EXTRA) {
gotoNextGroup();
}
break;
}
default:
break;
}
}
}
if (_innerExecCtx) {
Command::ExecuteResult res = _innerExecCtx->runActiveCommand();
if (res == Command::Finished) {
delete _innerExecCtx;
_innerExecCtx = nullptr;
gotoNextGroup();
}
}
}
void ConversationTask::onChoiceClicked(ConversationWidget *convWidget, int, uint32 data) {
const ConversationInfo::Item &item = getCurrentGroup()[data];
convWidget->clearChoices();
const ConversationLineList &toSayList = getTaskManager()->getGame().getAssets().getToSayList();
const ConversationLineList::Line *line = toSayList.getLine(item._question);
_substate = SAYING_QUESTION;
createSayTasks(line);
getTaskManager()->startTask(_sayTask);
_currentItem = &item;
if (!line->_speeches[0].isRepeating()) {
getTaskManager()->getGame().getGameData().getCurrentScene()->addExhaustedConvItem(_convInfo._context, data + 1, _currentGroupIndex + 1);
}
}
void ConversationTask::showChoicesOrPick() {
Game &game = getTaskManager()->getGame();
GameData &gameData = game.getGameData();
Scene *const scene = gameData.getScene(_sceneId);
if (!scene) {
return;
}
Common::Array<uint32> itemsWithValidQuestions;
Common::Array<uint32> itemsWithValidResponses;
Common::Array<uint32> itemsWithValidNext;
/*
Collect valid questions (not exhausted and not empty).
Collect valid responses (not exhausted and not empty).
If there are at least two visible questions, we show them.
If there is just one visible question, pick it automatically ONLY if this is not the first question in this conversation.
Otherwise we don't start the conversation.
If there are no visible questions, automatically pick the first valid response.
If nothing above applies, don't start the conversation.
*/
const ConversationInfo::ItemGroup &currentGroup = getCurrentGroup();
for (ConversationInfo::ItemGroup::size_type i = 0; i < currentGroup.size(); ++i) {
const ConversationInfo::Item &item = currentGroup[i];
if (scene->isConvItemExhausted(_convInfo._context, static_cast<uint8>(i + 1), static_cast<uint8>(_currentGroupIndex + 1))) {
continue;
}
const uint8 toSay = item._question;
const uint8 response = item._response;
const uint8 next = item._nextGroupIndex;
if (toSay != 0) {
itemsWithValidQuestions.push_back(i);
}
if (response != 0) {
itemsWithValidResponses.push_back(i);
}
if (next != 0) {
itemsWithValidNext.push_back(i);
}
}
if (itemsWithValidQuestions.size() > 1) {
ConversationWidget &widget = game.getGameScreen().getConversationWidget();
const ConversationLineList &toSayList = game.getAssets().getToSayList();
for (Common::Array<uint32>::size_type i = 0; i < itemsWithValidQuestions.size() && i < ConversationWidget::CONVERSATION_MAX_CHOICES; ++i) {
const ConversationInfo::Item &item = currentGroup[itemsWithValidQuestions[i]];
const ConversationLineList::Line *const line = toSayList.getLine(item._question);
const Common::String widgetText = toUpperCP895(line->_speeches[0]._text);
widget.setChoice(static_cast<int>(i), widgetText, itemsWithValidQuestions[i]);
}
_substate = IDLE;
_currentItem = nullptr;
_haveChoices = true;
} else if (itemsWithValidQuestions.size() == 1 && _haveChoices) {
const ConversationLineList &toSayList = game.getAssets().getToSayList();
const ConversationInfo::Item &item = currentGroup[itemsWithValidQuestions.front()];
const ConversationLineList::Line *const line = toSayList.getLine(item._question);
_substate = SAYING_QUESTION;
createSayTasks(line);
getTaskManager()->startTask(_sayTask);
_currentItem = &item;
if (!line->_speeches[0].isRepeating()) {
game.getGameData().getCurrentScene()->addExhaustedConvItem(_convInfo._context, itemsWithValidQuestions.front() + 1, _currentGroupIndex + 1);
}
_haveChoices = true;
} else if (!itemsWithValidResponses.empty() && _haveChoices) {
const ConversationLineList &responseList = game.getAssets().getResponseList();
const ConversationInfo::Item &item = currentGroup[itemsWithValidResponses.front()];
const ConversationLineList::Line *const line = responseList.getLine(item._response);
_substate = SAYING_RESPONSE;
createSayTasks(line);
getTaskManager()->startTask(_sayTask);
_currentItem = &item;
_haveChoices = true;
} else if (!itemsWithValidNext.empty() && _haveChoices) {
_currentGroupIndex = currentGroup[itemsWithValidNext.front()]._nextGroupIndex - 1;
showChoicesOrPick();
} else {
if (_haveChoices) {
finish();
} else {
_sayTask = TaskPtr(new SayTask("Nothing to talk about.", _convInfo._color)); // TODO: This is hardcoded in executable. Load it.
getTaskManager()->startTask(_sayTask);
_substate = SAYING_NO_QUESTIONS;
_currentItem = nullptr;
}
}
}
const ConversationInfo::ItemGroup &ConversationTask::getCurrentGroup() const {
assert(_currentGroupIndex < _convInfo._itemGroups.size());
return _convInfo._itemGroups[_currentGroupIndex];
}
void ConversationTask::finish() {
setState(FINISHED);
Game &game = getTaskManager()->getGame();
game.getGameScreen().showConversationWidget(false);
ConversationWidget &widget = game.getGameScreen().getConversationWidget();
widget.setCallback(nullptr);
}
void ConversationTask::startExtra() {
const ConversationLineList &responseList = getTaskManager()->getGame().getAssets().getResponseList();
const ConversationLineList::Line *const line = responseList.getLine(_currentItem->_response);
if (!line->_extra.empty()) {
_innerExecCtx = new ScriptExecutionContext(getTaskManager()->getGame());
Command *const extraCmd = _innerExecCtx->getExtra(line->_extra);
if (extraCmd) {
Command::ExecuteResult res = _innerExecCtx->startCommand(extraCmd);
if (res == Command::InProgress) {
_substate = RUNNING_EXTRA;
} else {
delete _innerExecCtx;
_innerExecCtx = nullptr;
}
} else {
warning("Extra '%s' not found", line->_extra.c_str());
delete _innerExecCtx;
_innerExecCtx = nullptr;
}
}
}
void ConversationTask::gotoNextGroup() {
if (_currentItem->_nextGroupIndex == 0) {
finish();
} else {
_currentGroupIndex = _currentItem->_nextGroupIndex - 1;
showChoicesOrPick();
}
}
void ConversationTask::createSayTasks(const ConversationLineList::Line *line) {
if (line->_speeches.size() == 1) {
const ConversationLineList::Speech &speech = line->_speeches[0];
_sayTask = TaskPtr(new SayTask(speech._text, getSpeechColor(speech)));
} else {
TaskPtrs tasks;
for (ConversationLineList::Speeches::const_iterator it = line->_speeches.begin(); it != line->_speeches.end(); ++it) {
tasks.push_back(TaskPtr(new SayTask(it->_text, getSpeechColor(*it))));
}
_sayTask = TaskPtr(new SequentialTask(tasks));
}
}
uint8 ConversationTask::getSpeechColor(const ConversationLineList::Speech &speech) {
uint8 color = WHITE;
if (_substate == SAYING_RESPONSE) {
color = _convInfo._color;
if (_mode == TalkCommand::RAY_AND_BUTTLEG_MODE) {
if (speech.isFirstSpeaker()) {
color = GREEN;
} else if (speech.isSecondSpeaker()) {
color = LIGHTBLUE;
}
}
}
return color;
}
}

View File

@@ -0,0 +1,71 @@
/* 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 "mutationofjb/commands/talkcommand.h"
#include "mutationofjb/conversationlinelist.h"
#include "mutationofjb/gamedata.h"
#include "mutationofjb/tasks/task.h"
#include "mutationofjb/widgets/conversationwidget.h"
namespace MutationOfJB {
class SayTask;
class ScriptExecutionContext;
class ConversationTask : public Task, public ConversationWidgetCallback {
public:
ConversationTask(uint8 sceneId, const ConversationInfo &convInfo, TalkCommand::Mode mode) : _sceneId(sceneId), _convInfo(convInfo), _mode(mode), _currentGroupIndex(0), _currentItem(nullptr), _substate(IDLE), _haveChoices(false), _innerExecCtx(nullptr) {}
~ConversationTask() override {}
void start() override;
void update() override;
void onChoiceClicked(ConversationWidget *, int response, uint32 data) override;
private:
void showChoicesOrPick();
const ConversationInfo::ItemGroup &getCurrentGroup() const;
void finish();
void startExtra();
void gotoNextGroup();
void createSayTasks(const ConversationLineList::Line *line);
uint8 getSpeechColor(const ConversationLineList::Speech &speech);
uint8 _sceneId;
const ConversationInfo &_convInfo;
TalkCommand::Mode _mode;
uint _currentGroupIndex;
const ConversationInfo::Item *_currentItem;
TaskPtr _sayTask;
enum Substate {
IDLE,
SAYING_QUESTION,
SAYING_RESPONSE,
SAYING_NO_QUESTIONS,
RUNNING_EXTRA
};
Substate _substate;
bool _haveChoices;
ScriptExecutionContext *_innerExecCtx;
};
}

View File

@@ -0,0 +1,153 @@
/* 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 "mutationofjb/tasks/objectanimationtask.h"
#include "mutationofjb/tasks/taskmanager.h"
#include "mutationofjb/game.h"
#include "mutationofjb/gamedata.h"
#include "mutationofjb/room.h"
namespace MutationOfJB {
static const int TICK_MILLIS = 100;
// TODO: Respect currentScene._delay.
ObjectAnimationTask::ObjectAnimationTask() : _timer(TICK_MILLIS) {
}
void ObjectAnimationTask::start() {
setState(RUNNING);
_timer.start();
}
void ObjectAnimationTask::update() {
_timer.update();
if (_timer.isFinished()) {
_timer.start();
updateObjects();
}
}
void ObjectAnimationTask::updateObjects() {
Scene *const scene = getTaskManager()->getGame().getGameData().getCurrentScene();
if (!scene) {
return;
}
for (uint8 i = 1; i <= scene->getNoObjects(); ++i) {
Object *const object = scene->getObject(i);
// Skip if object animation not active.
if (!object->_active)
continue;
// Number of frames must be higher than 1.
if (object->_numFrames <= 1)
continue;
const uint8 currentAnimOffset = object->_currentFrame - object->_firstFrame;
const bool randomized = object->_randomFrame != 0;
const bool belowRandomFrame = currentAnimOffset < (object->_randomFrame - 1);
uint8 maxAnimOffset = object->_numFrames - 1;
if (randomized && belowRandomFrame) {
maxAnimOffset = object->_randomFrame - 2;
}
uint8 nextAnimationOffset = currentAnimOffset + 1;
if (currentAnimOffset == maxAnimOffset) {
if (randomized && object->_jumpChance != 0 && getTaskManager()->getGame().getRandomSource().getRandomNumber(object->_jumpChance) == 0)
nextAnimationOffset = object->_randomFrame - 1;
else
nextAnimationOffset = 0;
}
object->_currentFrame = nextAnimationOffset + object->_firstFrame;
const bool drawObject = handleHardcodedAnimation(object);
if (drawObject) {
getTaskManager()->getGame().getRoom().drawObject(i);
}
}
}
bool ObjectAnimationTask::handleHardcodedAnimation(Object *const object) {
GameData &gameData = getTaskManager()->getGame().getGameData();
Scene *const scene = gameData.getCurrentScene();
const bool carnivalScene = gameData._currentScene == 30 && !gameData._partB;
const bool tavernScene = gameData._currentScene == 8 && gameData._partB;
if (carnivalScene) {
// This alternates between the two burglars' talking animations.
// Each burglar gets to talk for a varying amount of time since
// the switch occurs when his random frame is reached.
if (object->_WX == 1 && object->_currentFrame == 79) {
object->_currentFrame = 68;
object->_active = 0;
scene->getObject(6)->_active = 1;
scene->getObject(7)->_active = 0;
scene->getObject(8)->_active = 1;
return false;
} else if (object->_WX == 2 && object->_currentFrame == 91) {
object->_currentFrame = 80;
object->_active = 0;
scene->getObject(5)->_active = 1;
scene->getObject(7)->_active = 1;
scene->getObject(8)->_active = 0;
return false;
}
// The following makes sure you can't interact with the glass
// while the scientist is drinking from it.
if (scene->getObject(4)->_currentFrame > 52 && scene->getObject(4)->_active) {
scene->getStatic(9)->_active = 0; // disable scientist's glass
} else {
scene->getStatic(9)->_active = 1; // enable scientist's glass
}
if (!scene->getObject(4)->_active) {
scene->getStatic(9)->_active = 0; // disable scientist's glass
}
} else if (tavernScene) {
// Similarly to the carnival burglars, this alternates between
// the talking animations of the two soldiers in the tavern.
//
// At some point the script disables their conversation
// by nulling their _WX registers.
if (object->_WX == 3 && object->_currentFrame == 46) {
object->_currentFrame = 30;
object->_active = 0;
scene->getObject(3)->_active = 1;
return false;
} else if (object->_WX == 4 && object->_currentFrame == 63) {
object->_currentFrame = 47;
object->_active = 0;
scene->getObject(2)->_active = 1;
return false;
}
}
return true;
}
}

View File

@@ -0,0 +1,69 @@
/* 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/>.
*
*/
#ifndef MUTATIONOFJB_OBJECTANIMATIONTASK_H
#define MUTATIONOFJB_OBJECTANIMATIONTASK_H
#include "mutationofjb/tasks/task.h"
#include "mutationofjb/timer.h"
namespace MutationOfJB {
struct Object;
class ObjectAnimationTask : public Task {
public:
ObjectAnimationTask();
void start() override;
void update() override;
/**
* Advances every object animation in the current scene to the next frame.
*
* Normally the animation restarts after the last object frame. However, some animations have random
* elements to them. If _randomFrame is set, the animation restarts when _randomFrame is reached.
* Additionally, there is a chance with each frame until _randomFrame that the animation may jump
* straight to _randomFrame and continue until the last frame, then wrap around to the first frame.
*
* Randomness is used to introduce variety - e.g. in the starting scene a perched bird occasionally
* spreads its wings.
*/
void updateObjects();
/**
* Nasty, hacky stuff the original game does to make some complex animations
* in the Carnival and Tavern Earthquake scenes possible.
*
* @param object Object to process.
* @return Whether to draw the object. It's important to respect this, otherwise
* some of the hardcoded animations would suffer from graphical glitches.
*/
bool handleHardcodedAnimation(Object *const object);
private:
Timer _timer;
};
}
#endif

View File

@@ -0,0 +1,103 @@
/* 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 "mutationofjb/tasks/saytask.h"
#include "mutationofjb/tasks/taskmanager.h"
#include "mutationofjb/assets.h"
#include "mutationofjb/game.h"
#include "mutationofjb/gamedata.h"
#include "mutationofjb/room.h"
#include "mutationofjb/util.h"
#include "graphics/managed_surface.h"
#include "graphics/screen.h"
namespace MutationOfJB {
SayTask::SayTask(const Common::String &toSay, uint8 color) : _toSay(toSay), _color(color), _timer(50 * toSay.size()) {}
void SayTask::start() {
Game &game = getTaskManager()->getGame();
if (game.getActiveSayTask()) {
getTaskManager()->stopTask(game.getActiveSayTask());
}
game.setActiveSayTask(getTaskManager()->getTask(this));
setState(RUNNING);
drawSubtitle(_toSay, 160, 0, _color); // TODO: Respect PTALK and LTALK commands.
_timer.start();
}
void SayTask::update() {
_timer.update();
if (_timer.isFinished()) {
finish();
}
}
void SayTask::stop() {
if (getState() == RUNNING) {
finish();
}
}
void SayTask::drawSubtitle(const Common::String &text, int16 talkX, int16 talkY, uint8 color) {
const int MAX_LINE_WIDTH = 250;
const Font &font = getTaskManager()->getGame().getAssets().getSpeechFont();
Common::Array<Common::String> lines;
const int16 actualMaxWidth = font.wordWrapText(text, MAX_LINE_WIDTH, lines);
// Get the x, y coordinates of the top center point of the text's bounding box
// from the (rather strange) talk coordinates coming from scripts.
int16 x = talkX;
int16 y = talkY - (lines.size() - 1) * font.getFontHeight() - 15;
// Clamp to screen edges.
x = CLIP<int16>(x, 3 + actualMaxWidth / 2, 317 - actualMaxWidth / 2);
y = MAX<int16>(y, 3);
// Remember the area occupied by the text.
_boundingBox.left = x - actualMaxWidth / 2;
_boundingBox.top = y;
_boundingBox.setWidth(actualMaxWidth);
_boundingBox.setHeight(lines.size() * font.getFontHeight());
// Draw lines.
for (uint i = 0; i < lines.size(); i++) {
font.drawString(&getTaskManager()->getGame().getScreen(), lines[i], _boundingBox.left, _boundingBox.top + i * font.getFontHeight(), _boundingBox.width(), color, Graphics::kTextAlignCenter);
}
}
void SayTask::finish() {
getTaskManager()->getGame().getRoom().redraw(); // TODO: Only redraw the area occupied by the text.
setState(FINISHED);
Game &game = getTaskManager()->getGame();
if (game.getActiveSayTask().get() == this) {
game.setActiveSayTask(Common::SharedPtr<SayTask>());
}
}
}

View File

@@ -0,0 +1,54 @@
/* 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/>.
*
*/
#ifndef MUTATIONOFJB_SAYTASK_H
#define MUTATIONOFJB_SAYTASK_H
#include "mutationofjb/tasks/task.h"
#include "mutationofjb/timer.h"
#include "common/rect.h"
#include "common/str.h"
namespace MutationOfJB {
class SayTask : public Task {
public:
SayTask(const Common::String &toSay, uint8 color);
void start() override;
void update() override;
void stop() override;
private:
void drawSubtitle(const Common::String &text, int16 talkX, int16 talkY, uint8 color);
void finish();
Common::String _toSay;
uint8 _color;
Timer _timer;
Common::Rect _boundingBox;
};
}
#endif

View File

@@ -0,0 +1,62 @@
/* 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 "mutationofjb/tasks/sequentialtask.h"
#include "mutationofjb/tasks/taskmanager.h"
namespace MutationOfJB {
SequentialTask::SequentialTask(const TaskPtrs &tasks) : _tasks(tasks) {
}
void SequentialTask::start() {
setState(RUNNING);
runTasks();
}
void SequentialTask::update() {
runTasks();
}
void SequentialTask::runTasks() {
while (true) {
if (_tasks.empty()) {
setState(FINISHED);
return;
}
const TaskPtr &task = _tasks.front();
switch (task->getState()) {
case IDLE:
getTaskManager()->startTask(task);
break;
case RUNNING:
default:
return;
case FINISHED:
_tasks.remove_at(0);
break;
}
}
}
}

View File

@@ -0,0 +1,47 @@
/* 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/>.
*
*/
#ifndef MUTATIONOFJB_SEQUENTIALTASK_H
#define MUTATIONOFJB_SEQUENTIALTASK_H
#include "mutationofjb/tasks/task.h"
namespace MutationOfJB {
/**
* Queues multiple tasks.
*/
class SequentialTask : public Task {
public:
SequentialTask(const TaskPtrs &tasks);
void start() override;
void update() override;
private:
void runTasks();
TaskPtrs _tasks;
};
}
#endif

View File

@@ -0,0 +1,80 @@
/* 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/>.
*
*/
#ifndef MUTATIONOFJB_TASK_H
#define MUTATIONOFJB_TASK_H
#include "common/scummsys.h"
#include "common/ptr.h"
#include "common/array.h"
namespace MutationOfJB {
class TaskManager;
/**
* Base class for tasks.
*/
class Task {
public:
enum State {
IDLE,
RUNNING,
FINISHED
};
Task() : _taskManager(nullptr), _state(IDLE) {}
virtual ~Task() {}
virtual void start() = 0;
virtual void update() = 0;
virtual void stop() {
assert(false); // Assert by default - stopping might not be safe for all tasks.
}
void setTaskManager(TaskManager *taskMan) {
_taskManager = taskMan;
}
TaskManager *getTaskManager() {
return _taskManager;
}
State getState() const {
return _state;
}
protected:
void setState(State state) {
_state = state;
}
private:
TaskManager *_taskManager;
State _state;
};
typedef Common::SharedPtr<Task> TaskPtr;
typedef Common::Array<Common::SharedPtr<Task> > TaskPtrs;
}
#endif

View File

@@ -0,0 +1,71 @@
/* 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 "mutationofjb/tasks/taskmanager.h"
#include "mutationofjb/tasks/task.h"
namespace MutationOfJB {
void TaskManager::startTask(const TaskPtr &task) {
_tasks.push_back(task);
task->setTaskManager(this);
task->start();
}
void TaskManager::stopTask(const TaskPtr &task) {
TaskPtrs::iterator it = Common::find(_tasks.begin(), _tasks.end(), task);
if (it == _tasks.end()) {
warning("Task is not registered in TaskManager");
return;
}
task->stop();
assert(task->getState() != Task::RUNNING);
_tasks.erase(it);
}
TaskPtr TaskManager::getTask(Task *const task) {
for (TaskPtrs::iterator it = _tasks.begin(); it != _tasks.end(); ++it) {
if (it->get() == task) {
return *it;
}
}
return TaskPtr();
}
void TaskManager::update() {
for (TaskPtrs::iterator it = _tasks.begin(); it != _tasks.end();) {
const Task::State state = (*it)->getState();
if (state == Task::RUNNING) {
(*it)->update();
}
if (state == Task::FINISHED) {
it = _tasks.erase(it);
} else {
++it;
}
}
}
}

View File

@@ -0,0 +1,78 @@
/* 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/>.
*
*/
#ifndef MUTATIONOFJB_TASKMANAGER_H
#define MUTATIONOFJB_TASKMANAGER_H
#include "common/array.h"
#include "mutationofjb/tasks/task.h"
namespace MutationOfJB {
class Game;
/**
* Handles task management.
*
* Tasks are a way run game logic asynchronously.
*/
class TaskManager {
public:
TaskManager(Game &game) : _game(game) {}
/**
* Adds the task to the internal list and starts it.
*
* When the task is finished, it is automatically removed from the list.
* stopTask does not need to be called for that.
*/
void startTask(const TaskPtr &task);
/**
* Stops the task and removes it from the internal list.
*
* Call this only if you need to explicitly stop the task (usually before it's finished).
*/
void stopTask(const TaskPtr &task);
/**
* Gets task shared pointer from raw pointer.
*
* Since task lifetime is under control of SharedPtr, raw pointers shouldn't be used.
* However, if only a raw pointer is available (e.g. this),
* the method can be used to obtain a SharedPtr.
*/
TaskPtr getTask(Task *task);
void update();
Game &getGame() {
return _game;
}
private:
TaskPtrs _tasks;
Game &_game;
};
}
#endif