From febff83a4edc89e1dbc6f4c56f5531f0eb8f3287 Mon Sep 17 00:00:00 2001 From: Ľubomír Remák Date: Sun, 15 Jul 2018 00:18:54 +0200 Subject: MUTATIONOFJB: Draw objects (first frame only) and improve conversation support. --- .../mutationofjb/commands/definestructcommand.cpp | 2 +- engines/mutationofjb/commands/talkcommand.cpp | 9 +- engines/mutationofjb/game.cpp | 8 + engines/mutationofjb/game.h | 2 + engines/mutationofjb/gamedata.cpp | 22 ++- engines/mutationofjb/gamedata.h | 26 +++- engines/mutationofjb/module.mk | 2 + engines/mutationofjb/room.cpp | 31 +++- engines/mutationofjb/room.h | 3 + engines/mutationofjb/tasks/conversationtask.cpp | 173 ++++++++++++++++++--- engines/mutationofjb/tasks/conversationtask.h | 25 ++- engines/mutationofjb/tasks/saytask.cpp | 55 +++++++ engines/mutationofjb/tasks/saytask.h | 44 ++++++ engines/mutationofjb/tasks/task.h | 5 + engines/mutationofjb/tasks/taskmanager.cpp | 4 +- engines/mutationofjb/timer.cpp | 56 +++++++ engines/mutationofjb/timer.h | 50 ++++++ .../mutationofjb/widgets/conversationwidget.cpp | 29 ++-- engines/mutationofjb/widgets/conversationwidget.h | 12 +- 19 files changed, 505 insertions(+), 53 deletions(-) create mode 100644 engines/mutationofjb/tasks/saytask.cpp create mode 100644 engines/mutationofjb/tasks/saytask.h create mode 100644 engines/mutationofjb/timer.cpp create mode 100644 engines/mutationofjb/timer.h (limited to 'engines/mutationofjb') diff --git a/engines/mutationofjb/commands/definestructcommand.cpp b/engines/mutationofjb/commands/definestructcommand.cpp index ee15274a2d..93dbfc80bc 100644 --- a/engines/mutationofjb/commands/definestructcommand.cpp +++ b/engines/mutationofjb/commands/definestructcommand.cpp @@ -56,7 +56,7 @@ bool DefineStructCommandParser::parse(const Common::String &line, ScriptParseCon for (int j = 0; j < 5; ++j) { ConversationInfo::Item convItem; - convItem._question = atoi(linePtr); + convItem._choice = atoi(linePtr); linePtr += 6; convItem._response = atoi(linePtr); linePtr += 6; diff --git a/engines/mutationofjb/commands/talkcommand.cpp b/engines/mutationofjb/commands/talkcommand.cpp index d0775e481f..18ce956696 100644 --- a/engines/mutationofjb/commands/talkcommand.cpp +++ b/engines/mutationofjb/commands/talkcommand.cpp @@ -21,9 +21,12 @@ */ #include "mutationofjb/commands/talkcommand.h" -#include "mutationofjb/tasks/conversationtask.h" -#include "mutationofjb/script.h" + #include "mutationofjb/game.h" +#include "mutationofjb/script.h" +#include "mutationofjb/tasks/conversationtask.h" +#include "mutationofjb/tasks/taskmanager.h" + #include "common/str.h" namespace MutationOfJB { @@ -53,7 +56,7 @@ bool TalkCommandParser::parse(const Common::String &line, ScriptParseContext &, Command::ExecuteResult TalkCommand::execute(ScriptExecutionContext &scriptExeCtx) { if (!_task) { - _task = new ConversationTask(scriptExeCtx.getGame().getGameData()._conversationInfo); + _task = new ConversationTask(scriptExeCtx.getGameData()._currentScene, scriptExeCtx.getGame().getGameData()._conversationInfo); scriptExeCtx.getGame().getTaskManager().addTask(_task); } diff --git a/engines/mutationofjb/game.cpp b/engines/mutationofjb/game.cpp index 24159254df..69647f2ef3 100644 --- a/engines/mutationofjb/game.cpp +++ b/engines/mutationofjb/game.cpp @@ -100,7 +100,9 @@ Script *Game::changeSceneLoadScript(uint8 sceneId, bool partB) { _gameData->_lastScene = _gameData->_currentScene; _gameData->_currentScene = sceneId; _gameData->_partB = partB; + _room->load(_gameData->_currentScene, partB); + _room->redraw(); EncryptedFile scriptFile; Common::String fileName = Common::String::format("scrn%d%s.atn", sceneId, partB ? "b" : ""); @@ -180,6 +182,7 @@ void Game::update() { } _gui.update(); + _taskManager.update(); } Gui &Game::getGui() { @@ -227,4 +230,9 @@ Assets& Game::getAssets() { return _assets; } +Graphics::Screen &Game::getScreen() +{ + return *_vm->getScreen(); +} + } diff --git a/engines/mutationofjb/game.h b/engines/mutationofjb/game.h index 33527ae441..056cffb757 100644 --- a/engines/mutationofjb/game.h +++ b/engines/mutationofjb/game.h @@ -71,6 +71,8 @@ public: TaskManager& getTaskManager(); Assets &getAssets(); + Graphics::Screen &getScreen(); + private: bool loadGameData(bool partB); void runActiveCommand(); diff --git a/engines/mutationofjb/gamedata.cpp b/engines/mutationofjb/gamedata.cpp index 1ece44c740..6a9c166106 100644 --- a/engines/mutationofjb/gamedata.cpp +++ b/engines/mutationofjb/gamedata.cpp @@ -136,7 +136,11 @@ bool Scene::loadFromStream(Common::ReadStream &stream) { _palRotStart = stream.readByte(); _palRotEnd = stream.readByte(); _palRotPeriod = stream.readByte(); - stream.read(_unknown38A, 80); + _exhaustedChoiceNext = stream.readByte(); + + for (i = 0; i < 79; ++i) { + _exhaustedChoices[i]._encodedData = stream.readByte(); + } return true; } @@ -222,6 +226,22 @@ Bitmap *Scene::findBitmap(int16 x, int16 y, int *index) { return nullptr; } +void Scene::addExhaustedChoice(uint8 context, uint8 choiceIndex, uint8 choiceIndexList) { + _exhaustedChoices[_exhaustedChoiceNext - 1] = ExhaustedChoice(context, choiceIndex, choiceIndexList); + _exhaustedChoiceNext++; +} + +bool Scene::isChoiceExhausted(uint8 context, uint8 choiceIndex, uint8 choiceListIndex) const { + for (uint i = 0; i < _exhaustedChoiceNext - 1; ++i) { + const ExhaustedChoice &choice = _exhaustedChoices[i]; + if (choice.getContext() == context && choice.getChoiceIndex() == choiceIndex && choice.getChoiceListIndex() == choiceListIndex) { + return true; + } + } + + return false; +} + GameData::GameData() : _currentScene(0), diff --git a/engines/mutationofjb/gamedata.h b/engines/mutationofjb/gamedata.h index d1bbc546b3..62c66a1363 100644 --- a/engines/mutationofjb/gamedata.h +++ b/engines/mutationofjb/gamedata.h @@ -121,6 +121,22 @@ struct Bitmap { bool loadFromStream(Common::ReadStream &stream); }; +struct ExhaustedChoice { + /* + 1 bit - context + 3 bits - choice index + 4 bits - choice list index + */ + uint8 _encodedData; + + uint8 getContext() const { return (_encodedData >> 7) & 0x1; } + uint8 getChoiceIndex() const { return (_encodedData >> 4) & 0x7; } + uint8 getChoiceListIndex() const { return _encodedData & 0xF; } + + ExhaustedChoice() : _encodedData(0) {} + ExhaustedChoice(uint8 context, uint8 choiceIndex, uint8 choiceListIndex) : + _encodedData(((context & 0x1) << 7) | ((choiceIndex & 0x7) << 4) | (choiceListIndex & 0xF)) {} +}; struct Scene { Door *getDoor(uint8 objectId); @@ -135,6 +151,9 @@ struct Scene { Static *findStatic(int16 x, int16 y, int *index = nullptr); Bitmap *findBitmap(int16 x, int16 y, int *index = nullptr); + void addExhaustedChoice(uint8 context, uint8 choiceIndex, uint8 choiceIndexList); + bool isChoiceExhausted(uint8 context, uint8 choiceIndex, uint8 choiceIndexList) const; + uint8 _startup; uint8 _unknown001; uint8 _unknown002; @@ -156,14 +175,17 @@ struct Scene { uint8 _palRotStart; uint8 _palRotEnd; uint8 _palRotPeriod; - uint8 _unknown38A[80]; + + /* Points to the first free item in exhausted choices list. */ + uint8 _exhaustedChoiceNext; + ExhaustedChoice _exhaustedChoices[79]; bool loadFromStream(Common::ReadStream &stream); }; struct ConversationInfo { struct Item { - uint8 _question; + uint8 _choice; uint8 _response; uint8 _nextLineIndex; }; diff --git a/engines/mutationofjb/module.mk b/engines/mutationofjb/module.mk index 89da245dd4..f3a1d78784 100644 --- a/engines/mutationofjb/module.mk +++ b/engines/mutationofjb/module.mk @@ -22,6 +22,7 @@ MODULE_OBJS := \ commands/seqcommand.o \ commands/talkcommand.o \ tasks/conversationtask.o \ + tasks/saytask.o \ tasks/taskmanager.o \ widgets/buttonwidget.o \ widgets/conversationwidget.o \ @@ -42,6 +43,7 @@ MODULE_OBJS := \ mutationofjb.o \ room.o \ script.o \ + timer.o \ util.o # This module can be built as a plugin diff --git a/engines/mutationofjb/room.cpp b/engines/mutationofjb/room.cpp index 62af98327e..5577077480 100644 --- a/engines/mutationofjb/room.cpp +++ b/engines/mutationofjb/room.cpp @@ -21,17 +21,25 @@ */ #include "mutationofjb/room.h" + #include "mutationofjb/animationdecoder.h" #include "mutationofjb/encryptedfile.h" #include "mutationofjb/game.h" #include "mutationofjb/gamedata.h" #include "mutationofjb/util.h" + #include "common/str.h" #include "common/translation.h" + #include "graphics/screen.h" namespace MutationOfJB { +enum { + GAME_AREA_WIDTH = 320, + GAME_AREA_HEIGHT = 139 +}; + class RoomAnimationDecoderCallback : public AnimationDecoderCallback { public: RoomAnimationDecoderCallback(Room &room) : _room(room) {} @@ -47,9 +55,11 @@ void RoomAnimationDecoderCallback::onPaletteUpdated(byte palette[PALETTE_SIZE]) void RoomAnimationDecoderCallback::onFrame(int frameNo, Graphics::Surface &surface) { if (frameNo == 0) { - Common::Rect rect(0, 0, 320, 139); + Common::Rect rect(0, 0, GAME_AREA_WIDTH, GAME_AREA_HEIGHT); if (_room._game->isCurrentSceneMap()) { rect = Common::Rect(0, 0, 320, 200); + } else { + _room._background.blitFrom(surface, rect, Common::Point(0, 0)); } _room._screen->blitFrom(surface, rect, Common::Point(0, 0)); } @@ -78,10 +88,11 @@ void RoomAnimationDecoderCallback::onFrame(int frameNo, Graphics::Surface &surfa } } -Room::Room(Game *game, Graphics::Screen *screen) : _game(game), _screen(screen) {} +Room::Room(Game *game, Graphics::Screen *screen) : _game(game), _screen(screen), _background(GAME_AREA_WIDTH, GAME_AREA_HEIGHT) {} bool Room::load(uint8 roomNumber, bool roomB) { _objectsStart.clear(); + _surfaces.clear(); // TODO: Fix memory leak. Scene *const scene = _game->getGameData().getCurrentScene(); if (scene) { @@ -118,7 +129,23 @@ void Room::drawObjectAnimation(uint8 objectId, int animOffset) { const int startFrame = _objectsStart[objectId - 1]; const int animFrame = startFrame + animOffset; + // TODO: Threshold. _screen->blitFrom(_surfaces[animFrame], Common::Point(object->_x, object->_y)); } +void Room::redraw() { + if (!_game->isCurrentSceneMap()) { + Common::Rect rect(0, 0, GAME_AREA_WIDTH, GAME_AREA_HEIGHT); + _screen->blitFrom(_background.rawSurface(), rect, Common::Point(0, 0)); + + Scene *const currentScene = _game->getGameData().getCurrentScene(); + for (int i = 0; i < currentScene->getNoObjects(); ++i) { + Object *const obj = currentScene->getObject(i + 1); + if (obj->_AC) { + drawObjectAnimation(i + 1, 0); + } + } + } +} + } diff --git a/engines/mutationofjb/room.h b/engines/mutationofjb/room.h index e57d2ebb61..083fba6335 100644 --- a/engines/mutationofjb/room.h +++ b/engines/mutationofjb/room.h @@ -26,6 +26,7 @@ #include "common/scummsys.h" #include "common/array.h" #include "graphics/surface.h" +#include "graphics/managed_surface.h" namespace Graphics { class Screen; @@ -44,9 +45,11 @@ public: Room(Game *game, Graphics::Screen *screen); bool load(uint8 roomNumber, bool roomB); void drawObjectAnimation(uint8 objectId, int animOffset); + void redraw(); private: Game *_game; Graphics::Screen *_screen; + Graphics::ManagedSurface _background; Common::Array _surfaces; Common::Array _objectsStart; }; diff --git a/engines/mutationofjb/tasks/conversationtask.cpp b/engines/mutationofjb/tasks/conversationtask.cpp index 75d850465b..512b38b710 100644 --- a/engines/mutationofjb/tasks/conversationtask.cpp +++ b/engines/mutationofjb/tasks/conversationtask.cpp @@ -21,63 +21,186 @@ */ #include "mutationofjb/tasks/conversationtask.h" -#include "mutationofjb/tasks/taskmanager.h" + #include "mutationofjb/assets.h" +#include "mutationofjb/conversationlinelist.h" #include "mutationofjb/game.h" #include "mutationofjb/gamedata.h" #include "mutationofjb/gui.h" +#include "mutationofjb/tasks/saytask.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(); ConversationWidget &widget = game.getGui().getConversationWidget(); widget.setCallback(this); widget.setVisible(true); - updateWidget(); + _currentLineIndex = 0; + + showChoicesOrPick(); } void ConversationTask::update() { -} + if (_sayTask) { + if (_sayTask->getState() == Task::FINISHED) { + getTaskManager()->removeTask(_sayTask); + delete _sayTask; + _sayTask = nullptr; -void ConversationTask::onResponseClicked(ConversationWidget *, int response) { + switch (_substate) { + case SAYING_NO_CHOICES: + finish(); + break; + case SAYING_CHOICE: { + const ConversationLineList& responseList = getTaskManager()->getGame().getAssets().getResponseList(); + const ConversationLineList::Line *const line = responseList.getLine(_currentItem->_response); - uint8 nextLineIndex = _convInfo._lines[_currentLine]._items[response]._nextLineIndex; - if (nextLineIndex == 0) { - setState(FINISHED); - Game &game = getTaskManager()->getGame(); - ConversationWidget &widget = game.getGui().getConversationWidget(); - widget.setVisible(false); - game.getGui().markDirty(); // TODO: Handle automatically when changing visibility. - return; + _sayTask = new SayTask(line->_speeches[0]._text, _convInfo._color); + getTaskManager()->addTask(_sayTask); + _substate = SAYING_RESPONSE; + break; + } + case SAYING_RESPONSE: { + if (_currentItem->_nextLineIndex == 0) { + finish(); + } else { + _currentLineIndex = _currentItem->_nextLineIndex - 1; + showChoicesOrPick(); + } + break; + } + default: + break; + } + } } +} - _currentLine = nextLineIndex - 1; - updateWidget(); +void ConversationTask::onChoiceClicked(ConversationWidget *convWidget, int, uint32 data) { + const ConversationInfo::Item &item = getCurrentLine()->_items[data]; + convWidget->clearChoices(); + + const ConversationLineList& toSayList = getTaskManager()->getGame().getAssets().getToSayList(); + _sayTask = new SayTask(toSayList.getLine(item._choice)->_speeches[0]._text, _convInfo._color); + getTaskManager()->addTask(_sayTask); + _substate = SAYING_CHOICE; + _currentItem = &item; + getTaskManager()->getGame().getGameData().getCurrentScene()->addExhaustedChoice(_convInfo._context, data + 1, _currentLineIndex + 1); } -void ConversationTask::updateWidget() { +void ConversationTask::showChoicesOrPick() { Game &game = getTaskManager()->getGame(); - ConversationWidget &widget = game.getGui().getConversationWidget(); + GameData &gameData = game.getGameData(); + Scene *const scene = gameData.getScene(_sceneId); + + Common::Array itemsWithValidChoices; + Common::Array itemsWithValidResponses; + Common::Array itemsWithValidNext; + + /* + Collect valid "to say" choices (not exhausted and not empty). + Collect valid responses (not exhausted and not empty). + If there are at least two visible choices, we show them. + If there is just one visible choice, pick it automatically. + If there are no visible choices, automatically pick first valid response. + */ + + const ConversationInfo::Line *const currentLine = getCurrentLine(); + for (ConversationInfo::Items::size_type i = 0; i < currentLine->_items.size(); ++i) { + const ConversationInfo::Item &item = currentLine->_items[i]; + + if (scene->isChoiceExhausted(_convInfo._context, (uint8) i + 1, (uint8) _currentLineIndex + 1)) { + continue; + } + const uint8 choice = item._choice; + const uint8 response = item._response; + const uint8 next = item._nextLineIndex; + + if (choice != 0) { + itemsWithValidChoices.push_back(i); + } - const ConversationLineList& toSayList = game.getAssets().getToSayList(); + if (response != 0) { + itemsWithValidResponses.push_back(i); + } - const ConversationInfo::Line &convLine = _convInfo._lines[_currentLine]; + if (next != 0) { + itemsWithValidNext.push_back(i); + } + } + + if (itemsWithValidChoices.size() > 1) { + ConversationWidget &widget = game.getGui().getConversationWidget(); + const ConversationLineList& toSayList = game.getAssets().getToSayList(); - for (ConversationInfo::Items::size_type i = 0; i < convLine._items.size(); ++i) { - Common::String widgetText; - const uint8 question = convLine._items[i]._question; - if (question != 0) { - const ConversationLineList::Line *line = toSayList.getLine(convLine._items[i]._question); - widgetText = toUpperCP895(line->_speeches[0]._text); + for (Common::Array::size_type i = 0; i < itemsWithValidChoices.size() && i < ConversationWidget::CONVERSATION_MAX_CHOICES; ++i) { + const ConversationInfo::Item &item = currentLine->_items[itemsWithValidChoices[i]]; + const ConversationLineList::Line *const line = toSayList.getLine(item._choice); + const Common::String widgetText = toUpperCP895(line->_speeches[0]._text); + widget.setChoice((int) i, widgetText, itemsWithValidChoices[i]); } + _substate = IDLE; + _currentItem = nullptr; + + _haveChoices = true; + } else if (itemsWithValidChoices.size() == 1) { + const ConversationLineList& toSayList = game.getAssets().getToSayList(); + const ConversationInfo::Item &item = currentLine->_items[itemsWithValidChoices.front()]; + const ConversationLineList::Line *const line = toSayList.getLine(item._choice); + + _sayTask = new SayTask(line->_speeches[0]._text, _convInfo._color); + getTaskManager()->addTask(_sayTask); + _substate = SAYING_CHOICE; + _currentItem = &item; + + game.getGameData().getCurrentScene()->addExhaustedChoice(_convInfo._context, itemsWithValidChoices.front() + 1, _currentLineIndex + 1); + + _haveChoices = true; + } else if (!itemsWithValidResponses.empty()) { + const ConversationLineList& responseList = game.getAssets().getResponseList(); + const ConversationInfo::Item &item = currentLine->_items[itemsWithValidResponses.front()]; + const ConversationLineList::Line *const line = responseList.getLine(item._response); - widget.setLine(i, widgetText); + _sayTask = new SayTask(line->_speeches[0]._text, _convInfo._color); + getTaskManager()->addTask(_sayTask); + _substate = SAYING_RESPONSE; + _currentItem = &item; + + _haveChoices = true; + } else if (!itemsWithValidNext.empty()) { + _currentLineIndex = currentLine->_items[itemsWithValidNext.front()]._nextLineIndex - 1; + showChoicesOrPick(); + } else { + if (_haveChoices) { + finish(); + } else { + _sayTask = new SayTask("Nothing to talk about.", _convInfo._color); // TODO: This is hardcoded in executable. Load it. + getTaskManager()->addTask(_sayTask); + _substate = SAYING_NO_CHOICES; + _currentItem = nullptr; + } } } +const ConversationInfo::Line *ConversationTask::getCurrentLine() const { + return &_convInfo._lines[_currentLineIndex]; +} + +void ConversationTask::finish() { + setState(FINISHED); + + Game &game = getTaskManager()->getGame(); + ConversationWidget &widget = game.getGui().getConversationWidget(); + widget.setVisible(false); + game.getGui().markDirty(); // TODO: Handle automatically when changing visibility. +} + } diff --git a/engines/mutationofjb/tasks/conversationtask.h b/engines/mutationofjb/tasks/conversationtask.h index 5cc0e5f49f..3bcbb6f998 100644 --- a/engines/mutationofjb/tasks/conversationtask.h +++ b/engines/mutationofjb/tasks/conversationtask.h @@ -26,20 +26,37 @@ namespace MutationOfJB { +class SayTask; + class ConversationTask : public Task, public ConversationWidgetCallback { public: - ConversationTask(const ConversationInfo& convInfo) : _convInfo(convInfo), _currentLine(0) {} + ConversationTask(uint8 sceneId, const ConversationInfo& convInfo) : _sceneId(sceneId), _convInfo(convInfo), _currentLineIndex(0), _currentItem(nullptr), _sayTask(nullptr), _substate(IDLE), _haveChoices(false) {} virtual ~ConversationTask() {} virtual void start() override; virtual void update() override; - virtual void onResponseClicked(ConversationWidget *, int response) override; + virtual void onChoiceClicked(ConversationWidget *, int response, uint32 data) override; private: - void updateWidget(); + void showChoicesOrPick(); + const ConversationInfo::Line *getCurrentLine() const; + void finish(); + uint8 _sceneId; const ConversationInfo &_convInfo; - uint _currentLine; + uint _currentLineIndex; + const ConversationInfo::Item *_currentItem; + SayTask* _sayTask; + + enum Substate { + IDLE, + SAYING_CHOICE, + SAYING_RESPONSE, + SAYING_NO_CHOICES + }; + + Substate _substate; + bool _haveChoices; }; } diff --git a/engines/mutationofjb/tasks/saytask.cpp b/engines/mutationofjb/tasks/saytask.cpp new file mode 100644 index 0000000000..2ef4cdf5df --- /dev/null +++ b/engines/mutationofjb/tasks/saytask.cpp @@ -0,0 +1,55 @@ +/* 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 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#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 "graphics/managed_surface.h" +#include "graphics/screen.h" + +namespace MutationOfJB { + +SayTask::SayTask(const Common::String &toSay, uint8 color) : _toSay(toSay), _color(color), _timer(1000) {} + +void SayTask::start() { + + getTaskManager()->getGame().getAssets().getSpeechFont().drawString(_toSay, _color, 0, 0, getTaskManager()->getGame().getScreen()); + _timer.start(); + setState(RUNNING); +} + +void SayTask::update() { + _timer.update(); + + if (_timer.isFnished()) { + getTaskManager()->getGame().getRoom().redraw(); // TODO: Only redraw the area occupied by the text. + setState(FINISHED); + return; + } +} + +} diff --git a/engines/mutationofjb/tasks/saytask.h b/engines/mutationofjb/tasks/saytask.h new file mode 100644 index 0000000000..a7ac96d875 --- /dev/null +++ b/engines/mutationofjb/tasks/saytask.h @@ -0,0 +1,44 @@ +/* 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 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "mutationofjb/tasks/task.h" + +#include "mutationofjb/timer.h" + +#include "common/str.h" + +namespace MutationOfJB { + +class SayTask : public Task { +public: + SayTask(const Common::String &toSay, uint8 color); + + virtual void start() override; + virtual void update() override; + +private: + Common::String _toSay; + uint8 _color; + Timer _timer; +}; + +} diff --git a/engines/mutationofjb/tasks/task.h b/engines/mutationofjb/tasks/task.h index 7b43868269..1b98097b9c 100644 --- a/engines/mutationofjb/tasks/task.h +++ b/engines/mutationofjb/tasks/task.h @@ -20,6 +20,9 @@ * */ +#ifndef MUTATIONOFJB_TASK_H +#define MUTATIONOFJB_TASK_H + #include "common/scummsys.h" namespace MutationOfJB { @@ -54,3 +57,5 @@ private: }; } + +#endif diff --git a/engines/mutationofjb/tasks/taskmanager.cpp b/engines/mutationofjb/tasks/taskmanager.cpp index 981654981f..7fbf64dc9b 100644 --- a/engines/mutationofjb/tasks/taskmanager.cpp +++ b/engines/mutationofjb/tasks/taskmanager.cpp @@ -40,7 +40,9 @@ void TaskManager::removeTask(Task *task) { void TaskManager::update() { for (Tasks::const_iterator it = _tasks.begin(); it != _tasks.end(); ++it) { - (*it)->update(); + if ((*it)->getState() == Task::RUNNING) { + (*it)->update(); + } } } diff --git a/engines/mutationofjb/timer.cpp b/engines/mutationofjb/timer.cpp new file mode 100644 index 0000000000..8375445c08 --- /dev/null +++ b/engines/mutationofjb/timer.cpp @@ -0,0 +1,56 @@ +/* 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 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "mutationofjb/timer.h" + +#include "common/system.h" + +namespace MutationOfJB { + +Timer::Timer(uint32 millis) : _millis(millis), _startTime(0), _state(IDLE) { +} + +void Timer::start() { + _startTime = g_system->getMillis(); + _state = RUNNING; +} + +bool Timer::isFnished() const { + return _state == FINISHED; +} + +bool Timer::isRunning() const { + return _state == RUNNING; +} + +void Timer::update() { + if (_state != RUNNING) { + return; + } + + uint32 currentTime = g_system->getMillis(); + if (currentTime - _startTime >= _millis) { + _state = FINISHED; + } +} + +} diff --git a/engines/mutationofjb/timer.h b/engines/mutationofjb/timer.h new file mode 100644 index 0000000000..b12fe2278c --- /dev/null +++ b/engines/mutationofjb/timer.h @@ -0,0 +1,50 @@ +/* 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 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/scummsys.h" + +namespace MutationOfJB { + +class Timer { +public: + Timer(uint32 millis); + + void start(); + + bool isFnished() const; + bool isRunning() const; + + void update(); + +private: + enum State { + IDLE, + RUNNING, + FINISHED + }; + + uint32 _millis; + uint32 _startTime; + State _state; +}; + +} diff --git a/engines/mutationofjb/widgets/conversationwidget.cpp b/engines/mutationofjb/widgets/conversationwidget.cpp index 71a3483b75..a46b9c50f8 100644 --- a/engines/mutationofjb/widgets/conversationwidget.cpp +++ b/engines/mutationofjb/widgets/conversationwidget.cpp @@ -41,26 +41,35 @@ ConversationWidget::ConversationWidget(Gui &gui, const Common::Rect &area, const _callback(nullptr) {} -void ConversationWidget::setLine(int lineNo, const Common::String &str) { - if (lineNo >= CONVERSATION_LINES) { +void ConversationWidget::setChoice(int choiceNo, const Common::String &str, uint32 data) { + if (choiceNo >= CONVERSATION_MAX_CHOICES) { return; } - _lines[lineNo] = str; + _choices[choiceNo]._str = str; + _choices[choiceNo]._data = data; + markDirty(); +} + +void ConversationWidget::clearChoices() { + for (int i = 0; i < CONVERSATION_MAX_CHOICES; ++i) { + _choices[i]._str.clear(); + _choices[i]._data = 0; + } markDirty(); } void ConversationWidget::_draw(Graphics::ManagedSurface &surface) { surface.blitFrom(_surface, Common::Point(_area.left, _area.top)); - for (int i = 0; i < CONVERSATION_LINES; ++i) { - Common::String &line = _lines[i]; - if (line.empty()) { + for (int i = 0; i < CONVERSATION_MAX_CHOICES; ++i) { + Common::String &str = _choices[i]._str; + if (str.empty()) { continue; } // TODO: Active line should be WHITE. - _gui.getGame().getAssets().getSystemFont().drawString(line, LIGHTGRAY, CONVERSATION_LINES_X, CONVERSATION_LINES_Y + i * CONVERSATION_LINE_HEIGHT, surface); + _gui.getGame().getAssets().getSystemFont().drawString(str, LIGHTGRAY, CONVERSATION_LINES_X, CONVERSATION_LINES_Y + i * CONVERSATION_LINE_HEIGHT, surface); } } @@ -72,9 +81,9 @@ void ConversationWidget::handleEvent(const Common::Event &event) { const int16 y = event.mouse.y; if (_area.contains(x, y)) { if (_callback) { - int lineNum = (y - CONVERSATION_LINES_Y) / CONVERSATION_LINE_HEIGHT; - if (!_lines[lineNum].empty()) { - _callback->onResponseClicked(this, lineNum); + int choiceNo = (y - CONVERSATION_LINES_Y) / CONVERSATION_LINE_HEIGHT; + if (!_choices[choiceNo]._str.empty()) { + _callback->onChoiceClicked(this, choiceNo, _choices[choiceNo]._data); } } } diff --git a/engines/mutationofjb/widgets/conversationwidget.h b/engines/mutationofjb/widgets/conversationwidget.h index b404abc198..51ea86ed02 100644 --- a/engines/mutationofjb/widgets/conversationwidget.h +++ b/engines/mutationofjb/widgets/conversationwidget.h @@ -33,17 +33,18 @@ class ConversationWidget; class ConversationWidgetCallback { public: virtual ~ConversationWidgetCallback() {} - virtual void onResponseClicked(ConversationWidget *, int response) = 0; + virtual void onChoiceClicked(ConversationWidget *, int choiceNo, uint32 data) = 0; }; class ConversationWidget : public Widget { public: - enum { CONVERSATION_LINES = 4 }; + enum { CONVERSATION_MAX_CHOICES = 4 }; ConversationWidget(Gui &gui, const Common::Rect &area, const Graphics::Surface &surface); void setCallback(ConversationWidgetCallback *callback) { _callback = callback; } - void setLine(int lineNo, const Common::String &str); + void setChoice(int choiceNo, const Common::String &str, uint32 data = 0); + void clearChoices(); virtual void handleEvent(const Common::Event &event) override; @@ -52,7 +53,10 @@ protected: private: Graphics::Surface _surface; - Common::String _lines[CONVERSATION_LINES]; + struct ChoiceInfo { + Common::String _str; + uint32 _data; + } _choices[CONVERSATION_MAX_CHOICES]; ConversationWidgetCallback *_callback; }; -- cgit v1.2.3