diff options
-rw-r--r-- | engines/illusions/actor.cpp | 17 | ||||
-rw-r--r-- | engines/illusions/actor.h | 4 | ||||
-rw-r--r-- | engines/illusions/duckman/illusions_duckman.cpp | 14 | ||||
-rw-r--r-- | engines/illusions/duckman/illusions_duckman.h | 2 | ||||
-rw-r--r-- | engines/illusions/duckman/menusystem_duckman.cpp | 179 | ||||
-rw-r--r-- | engines/illusions/duckman/menusystem_duckman.h | 70 | ||||
-rw-r--r-- | engines/illusions/duckman/scriptopcodes_duckman.cpp | 60 | ||||
-rw-r--r-- | engines/illusions/duckman/scriptopcodes_duckman.h | 6 | ||||
-rw-r--r-- | engines/illusions/gamarchive.cpp | 4 | ||||
-rw-r--r-- | engines/illusions/input.cpp | 10 | ||||
-rw-r--r-- | engines/illusions/input.h | 1 | ||||
-rw-r--r-- | engines/illusions/menusystem.cpp | 591 | ||||
-rw-r--r-- | engines/illusions/menusystem.h | 235 | ||||
-rw-r--r-- | engines/illusions/module.mk | 2 | ||||
-rw-r--r-- | engines/illusions/resources/fontresource.h | 2 | ||||
-rw-r--r-- | engines/illusions/screentext.cpp | 6 | ||||
-rw-r--r-- | engines/illusions/screentext.h | 1 | ||||
-rw-r--r-- | engines/illusions/scriptopcodes.cpp | 2 | ||||
-rw-r--r-- | engines/illusions/sound.cpp | 8 | ||||
-rw-r--r-- | engines/illusions/textdrawer.cpp | 2 |
20 files changed, 1186 insertions, 30 deletions
diff --git a/engines/illusions/actor.cpp b/engines/illusions/actor.cpp index bae77c71fc..91929466be 100644 --- a/engines/illusions/actor.cpp +++ b/engines/illusions/actor.cpp @@ -929,6 +929,23 @@ void Control::refreshSequenceCode() { _actor->_seqCodeIp = sequence->_sequenceCode; } +void Control::getActorFrameDimensions(WidthHeight &dimensions) { + dimensions._width = _actor->_surface->w; + dimensions._height = _actor->_surface->h; +} + +void Control::drawActorRect(const Common::Rect r, byte color) { + _actor->_surface->fillRect(r, color); + _actor->_flags |= 0x4000; +} + +void Control::fillActor(byte color) { + debug("FILL %d, %d", _actor->_surface->w, _actor->_surface->h); + Common::Rect r = Common::Rect(_actor->_surface->w, _actor->_surface->h); + _actor->_surface->fillRect(r, color); + _actor->_flags |= 0x4000; +} + void Control::startSequenceActorIntern(uint32 sequenceId, int value, byte *entryTblPtr, uint32 notifyThreadId) { stopActor(); diff --git a/engines/illusions/actor.h b/engines/illusions/actor.h index 295a0b5c9a..add7519156 100644 --- a/engines/illusions/actor.h +++ b/engines/illusions/actor.h @@ -34,6 +34,7 @@ namespace Illusions { +class Control; class IllusionsEngine; class SequenceOpcodes; struct OpCall; @@ -200,6 +201,9 @@ public: PointArray *createPath(Common::Point destPt); void updateActorMovement(uint32 deltaTime); void refreshSequenceCode(); + void getActorFrameDimensions(WidthHeight &dimensions); + void drawActorRect(const Common::Rect r, byte color); + void fillActor(byte color); public: IllusionsEngine *_vm; uint _flags; diff --git a/engines/illusions/duckman/illusions_duckman.cpp b/engines/illusions/duckman/illusions_duckman.cpp index 7255327eaa..db8f83c295 100644 --- a/engines/illusions/duckman/illusions_duckman.cpp +++ b/engines/illusions/duckman/illusions_duckman.cpp @@ -23,6 +23,7 @@ #include "illusions/duckman/illusions_duckman.h" #include "illusions/duckman/duckman_dialog.h" #include "illusions/duckman/duckman_specialcode.h" +#include "illusions/duckman/menusystem_duckman.h" #include "illusions/duckman/scriptopcodes_duckman.h" #include "illusions/actor.h" #include "illusions/camera.h" @@ -110,6 +111,7 @@ Common::Error IllusionsEngine_Duckman::run() { _threads = new ThreadList(this); _updateFunctions = new UpdateFunctions(); _soundMan = new SoundMan(this); + _menuSystem = new DuckmanMenuSystem(this); _fader = new Fader(); @@ -180,6 +182,7 @@ Common::Error IllusionsEngine_Duckman::run() { delete _fader; + delete _menuSystem; delete _soundMan; delete _updateFunctions; delete _threads; @@ -231,8 +234,10 @@ void IllusionsEngine_Duckman::initInput() { _input->setInputEvent(kEventDown, 0x80) .addMouseButton(MOUSE_RIGHT_BUTTON) .addKey(Common::KEYCODE_DOWN); + /* Not implemented, used for original debugging purposes _input->setInputEvent(kEventF1, 0x100) .addKey(Common::KEYCODE_F1); + */ } #define UPDATEFUNCTION(priority, sceneId, callback) \ @@ -256,8 +261,13 @@ int IllusionsEngine_Duckman::updateScript(uint flags) { if (_screen->isDisplayOn() && !_screen->isFaderActive() && _pauseCtr == 0) { if (_input->pollEvent(kEventAbort)) { startScriptThread(0x00020342, 0); + //testMenu(this);//TODO DEBUG + + //BaseMenu *me = _menuSystem->getMenuById(kDuckmanPauseMenu); + //_menuSystem->openMenu(me); + //_menuSystem->runMenu(0x180002); + } else if (_input->pollEvent(kEventF1)) { - debug("F1"); startScriptThread(0x0002033F, 0); } } @@ -632,7 +642,7 @@ void IllusionsEngine_Duckman::cursorControlRoutine(Control *control, uint32 delt _dialogSys->updateDialogState(); break; case 4: - // TODO ShellMgr_update(_cursor._control); + _menuSystem->update(_cursor._control); break; } } diff --git a/engines/illusions/duckman/illusions_duckman.h b/engines/illusions/duckman/illusions_duckman.h index 39c421dae9..e2b3223efc 100644 --- a/engines/illusions/duckman/illusions_duckman.h +++ b/engines/illusions/duckman/illusions_duckman.h @@ -32,6 +32,7 @@ namespace Illusions { class Dictionary; class ScriptStack; class DuckmanDialogSystem; +class DuckmanMenuSystem; struct Cursor_Duckman { int _gameState; @@ -99,6 +100,7 @@ public: int _savedInventoryActorIndex; ScreenShaker *_screenShaker; + DuckmanMenuSystem *_menuSystem; void initInput(); diff --git a/engines/illusions/duckman/menusystem_duckman.cpp b/engines/illusions/duckman/menusystem_duckman.cpp new file mode 100644 index 0000000000..96d350ab4b --- /dev/null +++ b/engines/illusions/duckman/menusystem_duckman.cpp @@ -0,0 +1,179 @@ +/* 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 "illusions/illusions.h" +#include "illusions/actor.h" +#include "illusions/duckman/illusions_duckman.h" +#include "illusions/duckman/menusystem_duckman.h" + +namespace Illusions { + +// DuckmanMenuSystem + +DuckmanMenuSystem::DuckmanMenuSystem(IllusionsEngine_Duckman *vm) + : BaseMenuSystem(vm), _vm(vm) { + clearMenus(); +} + +DuckmanMenuSystem::~DuckmanMenuSystem() { + freeMenus(); +} + +void DuckmanMenuSystem::runMenu(MenuChoiceOffsets menuChoiceOffsets, int16 *menuChoiceOffset, + uint32 menuId, uint32 duration, uint timeOutMenuChoiceIndex, uint32 menuCallerThreadId) { + + setTimeOutDuration(duration, timeOutMenuChoiceIndex); + setMenuCallerThreadId(menuCallerThreadId); + setMenuChoiceOffsets(menuChoiceOffsets, menuChoiceOffset); + + int rootMenuId = convertRootMenuId(menuId | 0x180000); + BaseMenu *rootMenu = getMenuById(rootMenuId); + openMenu(rootMenu); + +} + +void DuckmanMenuSystem::clearMenus() { + for (int i = 0; i < kDuckmanLastMenuIndex; ++i) + _menus[i] = 0; +} + +void DuckmanMenuSystem::freeMenus() { + for (int i = 0; i < kDuckmanLastMenuIndex; ++i) + delete _menus[i]; +} + +BaseMenu *DuckmanMenuSystem::getMenuById(int menuId) { + if (!_menus[menuId]) + _menus[menuId] = createMenuById(menuId); + return _menus[menuId]; +} + +BaseMenu *DuckmanMenuSystem::createMenuById(int menuId) { + switch (menuId) { + case kDuckmanMainMenu: + return createMainMenu(); + case kDuckmanPauseMenu: + return createPauseMenu(); + case kDuckmanQueryRestartMenu: + return createQueryRestartMenu(); + case kDuckmanQueryQuitMenu: + return createQueryQuitMenu(); + default: + error("DuckmanMenuSystem::createMenuById() Invalid menu id %d", menuId); + } +} + +BaseMenu *DuckmanMenuSystem::createMainMenu() { + BaseMenu *menu = new BaseMenu(this, 0x00120003, 12, 17, 11, 27, 0); + menu->addMenuItem(new MenuItem("Start New Game", new MenuActionReturnChoice(this, 11))); + + menu->addMenuItem(new MenuItem("Load Saved Game", new MenuActionReturnChoice(this, 0))); + menu->addMenuItem(new MenuItem("Options", new MenuActionReturnChoice(this, 0))); + + // TODO menu->addMenuItem(new MenuItem("Load Saved Game", new MenuActionEnterMenu(this, kDuckmanLoadGameMenu))); + // TODO menu->addMenuItem(new MenuItem("Options", new MenuActionEnterMenu(this, kDuckmanOptionsMenu))); + menu->addMenuItem(new MenuItem("Quit Game", new MenuActionEnterQueryMenu(this, kDuckmanQueryQuitMenu, 12))); + return menu; +} + +BaseMenu *DuckmanMenuSystem::createLoadGameMenu() { + return 0; // TODO +} + +BaseMenu *DuckmanMenuSystem::createOptionsMenu() { + return 0; // TODO +} + +BaseMenu *DuckmanMenuSystem::createPauseMenu() { + BaseMenu *menu = new BaseMenu(this, 0x00120003, 12, 17, 11, 27, 1); + menu->addText(" Game Paused"); + menu->addText("-------------------"); + menu->addMenuItem(new MenuItem("Resume", new MenuActionReturnChoice(this, 1))); + //menu->addMenuItem(new MenuItem("Restart Game", new MenuActionEnterQueryMenu(this, kDuckmanQueryRestartMenu, 2))); + // TODO menu->addMenuItem(new MenuItem("Options", new MenuActionEnterMenu(this, kDuckmanOptionsMenu))); + menu->addMenuItem(new MenuItem("Quit Game", new MenuActionEnterQueryMenu(this, kDuckmanQueryQuitMenu, 3))); + return menu; +} + +BaseMenu *DuckmanMenuSystem::createQueryRestartMenu() { + return 0; // TODO +} + +BaseMenu *DuckmanMenuSystem::createQueryQuitMenu() { + BaseMenu *menu = new BaseMenu(this, 0x00120003, 12, 17, 11, 27, 2); + menu->addText("Do you really want to quit?"); + menu->addText("-------------------------------"); + menu->addMenuItem(new MenuItem("Yes, I'm outta here", new MenuActionReturnChoice(this, getQueryConfirmationChoiceIndex()))); + menu->addMenuItem(new MenuItem("No, just kidding", new MenuActionLeaveMenu(this))); + return menu; +} + +int DuckmanMenuSystem::convertRootMenuId(uint32 menuId) { + switch (menuId) { + case 0x180001: + return kDuckmanMainMenu; + case 0x180002: + return kDuckmanPauseMenu; + /* Debug menus, not implemented + case 0x180005: + case 0x180006: + case 0x180007: + */ + /* TODO CHECKME Another pause menu? + case 0x180008: + menuData = &g_menuDataPause; + */ + default: + error("DuckmanMenuSystem() Menu ID %08X not found", menuId); + } +} + +bool DuckmanMenuSystem::initMenuCursor() { + bool cursorInitialVisibleFlag = false; + Control *cursorControl = _vm->getObjectControl(0x40004); + if (cursorControl) { + if (cursorControl->_flags & 1) + cursorInitialVisibleFlag = false; + cursorControl->appearActor(); + } else { + Common::Point pos = _vm->getNamedPointPosition(0x70001); + _vm->_controls->placeActor(0x50001, pos, 0x60001, 0x40004, 0); + cursorControl = _vm->getObjectControl(0x40004); + } + return cursorInitialVisibleFlag; +} + +int DuckmanMenuSystem::getGameState() { + return _vm->_cursor._gameState; +} + +void DuckmanMenuSystem::setMenuCursorNum(int cursorNum) { + Control *mouseCursor = _vm->getObjectControl(0x40004); + _vm->setCursorActorIndex(5, cursorNum, 0); + mouseCursor->startSequenceActor(0x60001, 2, 0); +} + +void DuckmanMenuSystem::setGameState(int gameState) { + _vm->_cursor._gameState = gameState; +} + +} // End of namespace Illusions diff --git a/engines/illusions/duckman/menusystem_duckman.h b/engines/illusions/duckman/menusystem_duckman.h new file mode 100644 index 0000000000..bb43619acd --- /dev/null +++ b/engines/illusions/duckman/menusystem_duckman.h @@ -0,0 +1,70 @@ +/* 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. + * + */ + +#ifndef ILLUSIONS_DUCKMAN_MENUSYSTEM_DUCKMAN_H +#define ILLUSIONS_DUCKMAN_MENUSYSTEM_DUCKMAN_H + +#include "illusions/menusystem.h" + +namespace Illusions { + +enum { + kDuckmanMainMenu, + kDuckmanLoadGameMenu, + kDuckmanOptionsMenu, + kDuckmanPauseMenu, + kDuckmanQueryQuitMenu, + kDuckmanQueryRestartMenu, + kDuckmanLastMenuIndex +}; + +class IllusionsEngine_Duckman; + +class DuckmanMenuSystem : public BaseMenuSystem { +public: + DuckmanMenuSystem(IllusionsEngine_Duckman *vm); + ~DuckmanMenuSystem(); + void runMenu(MenuChoiceOffsets menuChoiceOffsets, int16 *menuChoiceOffset, + uint32 menuId, uint32 duration, uint timeOutMenuChoiceIndex, uint32 menuCallerThreadId); +public://protected: + IllusionsEngine_Duckman *_vm; + BaseMenu *_menus[kDuckmanLastMenuIndex]; + void clearMenus(); + void freeMenus(); + BaseMenu *getMenuById(int menuId); + BaseMenu *createMenuById(int menuId); + BaseMenu *createMainMenu(); + BaseMenu *createLoadGameMenu(); + BaseMenu *createOptionsMenu(); + BaseMenu *createPauseMenu(); + BaseMenu *createQueryRestartMenu(); + BaseMenu *createQueryQuitMenu(); + int convertRootMenuId(uint32 menuId); + virtual bool initMenuCursor(); + virtual int getGameState(); + virtual void setGameState(int gameState); + virtual void setMenuCursorNum(int cursorNum); +}; + +} // End of namespace Illusions + +#endif // ILLUSIONS_DUCKMAN_MENUSYSTEM_DUCKMAN_H diff --git a/engines/illusions/duckman/scriptopcodes_duckman.cpp b/engines/illusions/duckman/scriptopcodes_duckman.cpp index 401f2e3eed..0fa7361574 100644 --- a/engines/illusions/duckman/scriptopcodes_duckman.cpp +++ b/engines/illusions/duckman/scriptopcodes_duckman.cpp @@ -23,10 +23,12 @@ #include "illusions/duckman/illusions_duckman.h" #include "illusions/duckman/scriptopcodes_duckman.h" #include "illusions/duckman/duckman_dialog.h" +#include "illusions/duckman/menusystem_duckman.h" #include "illusions/actor.h" #include "illusions/camera.h" #include "illusions/dictionary.h" #include "illusions/input.h" +#include "illusions/menusystem.h" #include "illusions/resources/scriptresource.h" #include "illusions/resources/talkresource.h" #include "illusions/screen.h" @@ -57,6 +59,7 @@ void ScriptOpcodes_Duckman::initOpcodes() { // First clear everything for (uint i = 0; i < 256; ++i) _opcodes[i] = 0; + // Register opcodes OPCODE(1, opNop); OPCODE(2, opSuspend); OPCODE(3, opYield); @@ -69,13 +72,15 @@ void ScriptOpcodes_Duckman::initOpcodes() { OPCODE(16, opLoadResource); OPCODE(17, opUnloadResource); OPCODE(18, opEnterScene18); + OPCODE(19, opUnloadResourcesBySceneId); OPCODE(20, opChangeScene); OPCODE(22, opStartModalScene); OPCODE(23, opExitModalScene); OPCODE(24, opEnterScene24); OPCODE(25, opLeaveScene24); - OPCODE(26, opEnterScene26); - OPCODE(27, opLeaveScene26); + OPCODE(26, opEnterDebugger); + OPCODE(27, opLeaveDebugger); + OPCODE(28, opDumpCurrentSceneFiles); OPCODE(32, opPanCenterObject); OPCODE(33, opPanTrackObject); OPCODE(34, opPanToObject); @@ -135,7 +140,6 @@ void ScriptOpcodes_Duckman::initOpcodes() { OPCODE(126, opDebug126); OPCODE(127, opDebug127); #if 0 - // Register opcodes OPCODE(8, opStartTempScriptThread); OPCODE(14, opSetThreadSceneId); OPCODE(15, opEndTalkThreads); @@ -252,6 +256,12 @@ void ScriptOpcodes_Duckman::opEnterScene18(ScriptThread *scriptThread, OpCall &o _vm->enterScene(sceneId, 0); } +void ScriptOpcodes_Duckman::opUnloadResourcesBySceneId(ScriptThread *scriptThread, OpCall &opCall) { + ARG_SKIP(2); + ARG_UINT32(sceneId); + _vm->_resSys->unloadResourcesBySceneId(sceneId); +} + //static uint dsceneId = 0, dthreadId = 0; //static uint dsceneId = 0x00010008, dthreadId = 0x00020029;//Beginning in Jac static uint dsceneId = 0x0001000A, dthreadId = 0x00020043;//Home front @@ -279,11 +289,13 @@ void ScriptOpcodes_Duckman::opChangeScene(ScriptThread *scriptThread, OpCall &op debug(1, "changeScene(%08X, %08X)", sceneId, threadId); //DEBUG + /* if (dsceneId) { sceneId = dsceneId; threadId = dthreadId; dsceneId = 0; } + */ if (_vm->_scriptResource->_properties.get(31)) { _vm->changeScene(0x10002, 0x20001, opCall._callerThreadId); @@ -331,12 +343,19 @@ void ScriptOpcodes_Duckman::opLeaveScene24(ScriptThread *scriptThread, OpCall &o _vm->leavePause(_vm->getCurrentScene(), opCall._callerThreadId); } -void ScriptOpcodes_Duckman::opEnterScene26(ScriptThread *scriptThread, OpCall &opCall) { - // TODO +void ScriptOpcodes_Duckman::opEnterDebugger(ScriptThread *scriptThread, OpCall &opCall) { + // Used for debugging purposes in the original engine + // This is not supported and only reachable by code not implemented here! + error("ScriptOpcodes_Duckman::opEnterDebugger() Debugger function called"); +} + +void ScriptOpcodes_Duckman::opLeaveDebugger(ScriptThread *scriptThread, OpCall &opCall) { + // See opEnterDebugger + error("ScriptOpcodes_Duckman::opLeaveDebugger() Debugger function called"); } -void ScriptOpcodes_Duckman::opLeaveScene26(ScriptThread *scriptThread, OpCall &opCall) { - // TODO +void ScriptOpcodes_Duckman::opDumpCurrentSceneFiles(ScriptThread *scriptThread, OpCall &opCall) { + _vm->dumpCurrSceneFiles(_vm->getCurrentScene(), opCall._callerThreadId); } void ScriptOpcodes_Duckman::opPanCenterObject(ScriptThread *scriptThread, OpCall &opCall) { @@ -615,23 +634,32 @@ void ScriptOpcodes_Duckman::opAddMenuChoice(ScriptThread *scriptThread, OpCall & } void ScriptOpcodes_Duckman::opDisplayMenu(ScriptThread *scriptThread, OpCall &opCall) { - ARG_INT16(unk1); + ARG_INT16(timeOutDuration); ARG_UINT32(menuId); - ARG_UINT32(unk2); - // TODO _vm->_shellMgr->displayMenu(_vm->_stack->topPtr(), &_vm->_menuChoiceOfs, menuId, unk1, unk2, opCall._callerThreadId); - // Remove menu choices from the stack + ARG_UINT32(timeOutMenuChoiceIndex); + + debug("timeOutMenuChoiceIndex: %d", timeOutMenuChoiceIndex); + + MenuChoiceOffsets menuChoiceOffsets; + + // Load menu choices from the stack do { - _vm->_stack->pop(); + int16 choiceOffs = _vm->_stack->pop(); + debug("choiceOffs: %04X", choiceOffs); + menuChoiceOffsets.push_back(choiceOffs); } while (_vm->_stack->pop() == 0); - + + _vm->_menuSystem->runMenu(menuChoiceOffsets, &_vm->_menuChoiceOfs, + menuId, timeOutDuration, timeOutMenuChoiceIndex, + opCall._threadId); + //DEBUG Resume calling thread, later done by the video player - _vm->notifyThreadId(opCall._callerThreadId); + //_vm->notifyThreadId(opCall._callerThreadId); } void ScriptOpcodes_Duckman::opSwitchMenuChoice(ScriptThread *scriptThread, OpCall &opCall) { -_vm->_menuChoiceOfs = 156; // DEBUG Chose "Start game" - + //_vm->_menuChoiceOfs = 156; // DEBUG Chose "Start game" opCall._deltaOfs += _vm->_menuChoiceOfs; } diff --git a/engines/illusions/duckman/scriptopcodes_duckman.h b/engines/illusions/duckman/scriptopcodes_duckman.h index 5b2f089460..d50f967a43 100644 --- a/engines/illusions/duckman/scriptopcodes_duckman.h +++ b/engines/illusions/duckman/scriptopcodes_duckman.h @@ -54,13 +54,15 @@ protected: void opUnloadResource(ScriptThread *scriptThread, OpCall &opCall); void opStartTimerThread(ScriptThread *scriptThread, OpCall &opCall); void opEnterScene18(ScriptThread *scriptThread, OpCall &opCall); + void opUnloadResourcesBySceneId(ScriptThread *scriptThread, OpCall &opCall); void opChangeScene(ScriptThread *scriptThread, OpCall &opCall); void opStartModalScene(ScriptThread *scriptThread, OpCall &opCall); void opExitModalScene(ScriptThread *scriptThread, OpCall &opCall); void opEnterScene24(ScriptThread *scriptThread, OpCall &opCall); void opLeaveScene24(ScriptThread *scriptThread, OpCall &opCall); - void opEnterScene26(ScriptThread *scriptThread, OpCall &opCall); - void opLeaveScene26(ScriptThread *scriptThread, OpCall &opCall); + void opEnterDebugger(ScriptThread *scriptThread, OpCall &opCall); + void opLeaveDebugger(ScriptThread *scriptThread, OpCall &opCall); + void opDumpCurrentSceneFiles(ScriptThread *scriptThread, OpCall &opCall); void opPanCenterObject(ScriptThread *scriptThread, OpCall &opCall); void opPanTrackObject(ScriptThread *scriptThread, OpCall &opCall); void opPanToObject(ScriptThread *scriptThread, OpCall &opCall); diff --git a/engines/illusions/gamarchive.cpp b/engines/illusions/gamarchive.cpp index 517882c501..098ad35154 100644 --- a/engines/illusions/gamarchive.cpp +++ b/engines/illusions/gamarchive.cpp @@ -61,13 +61,13 @@ void GamArchive::loadDictionary() { _groups[i]._fileCount = fileCount; _groups[i]._files = new GamFileEntry[fileCount]; - debug("Group %08X, fileCount: %d", _groups[i]._id, _groups[i]._fileCount); + debug(8, "Group %08X, fileCount: %d", _groups[i]._id, _groups[i]._fileCount); for (uint j = 0; j < fileCount; ++j) { _groups[i]._files[j]._id = _fd->readUint32LE(); _groups[i]._files[j]._fileOffset = _fd->readUint32LE(); _groups[i]._files[j]._fileSize = _fd->readUint32LE(); - debug(" %08X, %08X, %d", _groups[i]._files[j]._id, _groups[i]._files[j]._fileOffset, _groups[i]._files[j]._fileSize); + debug(8, " %08X, %08X, %d", _groups[i]._files[j]._id, _groups[i]._files[j]._fileOffset, _groups[i]._files[j]._fileSize); } } diff --git a/engines/illusions/input.cpp b/engines/illusions/input.cpp index 3956691ef6..37da087543 100644 --- a/engines/illusions/input.cpp +++ b/engines/illusions/input.cpp @@ -77,12 +77,14 @@ uint InputEvent::handle(Common::KeyCode key, int mouseButton, bool down) { // Input +const uint kAllButtons = 0xFFFF; + Input::Input() { _buttonStates = 0; _newButtons = 0; _buttonsDown = 0; _newKeys = 0; - _enabledButtons = 0xFFFF; + _enabledButtons = kAllButtons; _cursorPos.x = 0; _cursorPos.y = 0; _prevCursorPos.x = 0; @@ -123,12 +125,16 @@ bool Input::pollEvent(uint evt) { return pollButton(_inputEvents[evt].getBitMask()); } +bool Input::hasNewEvents() { + return lookNewButtons(kAllButtons); +} + void Input::discardEvent(uint evt) { discardButtons(_inputEvents[evt].getBitMask()); } void Input::discardAllEvents() { - discardButtons(0xFFFF); + discardButtons(kAllButtons); } void Input::activateButton(uint bitMask) { diff --git a/engines/illusions/input.h b/engines/illusions/input.h index 093adad222..7d01ea6eec 100644 --- a/engines/illusions/input.h +++ b/engines/illusions/input.h @@ -79,6 +79,7 @@ public: Input(); void processEvent(Common::Event event); bool pollEvent(uint evt); + bool hasNewEvents(); void discardEvent(uint evt); void discardAllEvents(); void activateButton(uint bitMask); diff --git a/engines/illusions/menusystem.cpp b/engines/illusions/menusystem.cpp new file mode 100644 index 0000000000..f26a461223 --- /dev/null +++ b/engines/illusions/menusystem.cpp @@ -0,0 +1,591 @@ +/* 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 "illusions/menusystem.h" +#include "illusions/illusions.h" +#include "illusions/dictionary.h" +#include "illusions/input.h" +#include "illusions/screen.h" +#include "illusions/screentext.h" +#include "illusions/thread.h" +#include "illusions/time.h" + +namespace Illusions { + +// MenuItem + +MenuItem::MenuItem(const Common::String text, BaseMenuAction *action) + : _text(text), _action(action) { +} + +MenuItem::~MenuItem() { + delete _action; +} + +void MenuItem::executeAction() { + _action->execute(); +} + +// BaseMenu + +BaseMenu::BaseMenu(BaseMenuSystem *menuSystem, uint32 fontId, byte field8, byte fieldA, byte fieldC, byte fieldE, + uint defaultMenuItemIndex) + : _menuSystem(menuSystem), _fontId(fontId), _field8(field8), _fieldA(fieldA), _fieldC(fieldC), _fieldE(fieldE), + _defaultMenuItemIndex(defaultMenuItemIndex) +{ +} + +BaseMenu::~BaseMenu() { + for (MenuItems::iterator it = _menuItems.begin(); it != _menuItems.end(); ++it) + delete *it; +} + +void BaseMenu::addText(const Common::String text) { + _text.push_back(text); +} + +void BaseMenu::addMenuItem(MenuItem *menuItem) { + _menuItems.push_back(menuItem); +} + +uint BaseMenu::getHeaderLinesCount() { + return _text.size(); +} + +const Common::String& BaseMenu::getHeaderLine(uint index) { + return _text[index]; +} + +uint BaseMenu::getMenuItemsCount() { + return _menuItems.size(); +} + +MenuItem *BaseMenu::getMenuItem(uint index) { + return _menuItems[index]; +} + +void BaseMenu::enterMenu() { + // Empty, implemented if neccessary by the inherited class when the menu is entered +} + +// BaseMenuSystem + +BaseMenuSystem::BaseMenuSystem(IllusionsEngine *vm) + : _vm(vm), _isTimeOutEnabled(false), _menuChoiceOffset(0) { +} + +BaseMenuSystem::~BaseMenuSystem() { +} + +void BaseMenuSystem::playSoundEffect13() { + // TODO +} + +void BaseMenuSystem::playSoundEffect14() { + // TODO +} + +void BaseMenuSystem::selectMenuChoiceIndex(uint choiceIndex) { + if (choiceIndex > 0 && _menuChoiceOffset) { + *_menuChoiceOffset = _menuChoiceOffsets[choiceIndex - 1]; + debug(0, "*_menuChoiceOffset: %04X", *_menuChoiceOffset); + } + _vm->_threads->notifyId(_menuCallerThreadId); + _menuCallerThreadId = 0; + closeMenu(); +} + +void BaseMenuSystem::leaveMenu() { + playSoundEffect13(); + if (!_menuStack.empty()) + leaveSubMenu(); + else + closeMenu(); +} + +void BaseMenuSystem::enterSubMenu(BaseMenu *menu) { + _menuStack.push(_activeMenu); + activateMenu(menu); + _hoveredMenuItemIndex = _hoveredMenuItemIndex3; + _hoveredMenuItemIndex2 = _hoveredMenuItemIndex3; + setMouseCursorToMenuItem(_hoveredMenuItemIndex); + placeActor318(); + placeActor323(); +} + +void BaseMenuSystem::leaveSubMenu() { + _activeMenu = _menuStack.pop(); + _field54 = _activeMenu->_field2C18; + _menuLinesCount = _activeMenu->getHeaderLinesCount(); + _hoveredMenuItemIndex = 1; + _vm->_screenText->removeText(); + _vm->_screenText->removeText(); + activateMenu(_activeMenu); + _hoveredMenuItemIndex = _hoveredMenuItemIndex3; + _hoveredMenuItemIndex2 = _hoveredMenuItemIndex3; + setMouseCursorToMenuItem(_hoveredMenuItemIndex); + initActor318(); + placeActor323(); +} + +void BaseMenuSystem::enterSubMenuById(int menuId) { + BaseMenu *menu = getMenuById(menuId); + enterSubMenu(menu); +} + +uint BaseMenuSystem::getQueryConfirmationChoiceIndex() const { + return _queryConfirmationChoiceIndex; +} + +void BaseMenuSystem::setQueryConfirmationChoiceIndex(uint queryConfirmationChoiceIndex) { + _queryConfirmationChoiceIndex = queryConfirmationChoiceIndex; +} + +void BaseMenuSystem::setMouseCursorToMenuItem(int menuItemIndex) { + Common::Point mousePos; + if (calcMenuItemMousePos(menuItemIndex, mousePos)) + setMousePos(mousePos); +} + +void BaseMenuSystem::calcMenuItemRect(uint menuItemIndex, WRect &rect) { + FontResource *font = _vm->_dict->findFont(_activeMenu->_fontId); + int charHeight = font->getCharHeight() + font->getLineIncr(); + + _vm->_screenText->getTextInfoPosition(rect._topLeft); + /* TODO + if (_activeMenu->_field8) { + rect._topLeft.y += 4; + rect._topLeft.x += 4; + } + */ + rect._topLeft.y += charHeight * (menuItemIndex + _menuLinesCount - 1); + + WidthHeight textInfoDimensions; + _vm->_screenText->getTextInfoDimensions(textInfoDimensions); + rect._bottomRight.x = rect._topLeft.x + textInfoDimensions._width; + rect._bottomRight.y = rect._topLeft.y + charHeight; +} + +bool BaseMenuSystem::calcMenuItemMousePos(uint menuItemIndex, Common::Point &pt) { + if (menuItemIndex < _hoveredMenuItemIndex3 || menuItemIndex >= _hoveredMenuItemIndex3 + _menuItemCount) + return false; + + WRect rect; + calcMenuItemRect(menuItemIndex - _hoveredMenuItemIndex3 + 1, rect); + pt.x = rect._topLeft.x; + pt.y = rect._topLeft.y + (rect._bottomRight.y - rect._topLeft.y) / 2; + return true; +} + +bool BaseMenuSystem::calcMenuItemIndexAtPoint(Common::Point pt, uint &menuItemIndex) { + WRect rect; + calcMenuItemRect(1, rect); + + uint index = _hoveredMenuItemIndex3 + (pt.y - rect._topLeft.y) / (rect._bottomRight.y - rect._topLeft.y); + + if (pt.y < rect._topLeft.y || pt.x < rect._topLeft.x || pt.x > rect._bottomRight.x || + index > _field54 || index > _hoveredMenuItemIndex3 + _menuItemCount - 1) + return false; + + menuItemIndex = index; + return true; +} + +void BaseMenuSystem::setMousePos(Common::Point &mousePos) { + _vm->_input->setCursorPosition(mousePos); + Control *mouseCursor = _vm->getObjectControl(0x40004); + mouseCursor->_actor->_position = mousePos; +} + +void BaseMenuSystem::activateMenu(BaseMenu *menu) { + _activeMenu = menu; + // TODO Run menu enter callback if neccessary + _menuLinesCount = menu->getHeaderLinesCount(); + menu->_field2C18 = menu->getMenuItemsCount(); + _hoveredMenuItemIndex3 = 1; + _field54 = menu->_field2C18; + + uint v2 = drawMenuText(menu); + if (menu->_field2C18 <= v2) + _menuItemCount = menu->_field2C18; + else + _menuItemCount = v2; + +} + +void BaseMenuSystem::initActor318() { + Control *v0 = _vm->getObjectControl(0x4013E); + if (!v0) { + WidthHeight dimensions; + dimensions._width = 300; + dimensions._height = 15; + _vm->_controls->placeSequenceLessActor(0x4013E, Common::Point(0, 0), dimensions, 18); + v0 = _vm->getObjectControl(0x4013E); + v0->_flags |= 8; + } + placeActor318(); + v0->appearActor(); +} + +void BaseMenuSystem::placeActor318() { + Control *v0 = _vm->getObjectControl(0x4013E); + v0->fillActor(0); + + WidthHeight textInfoDimensions; + _vm->_screenText->getTextInfoDimensions(textInfoDimensions); + + /* TODO + if ( _activeMenu->_field8 && _activeMenu->_fieldA != _activeMenu->_field8) + textInfoDimensions._width -= 6; + */ + + WidthHeight frameDimensions; + v0->getActorFrameDimensions(frameDimensions); + + FontResource *font = _vm->_dict->findFont(_activeMenu->_fontId); + int charHeight = font->getCharHeight() + font->getLineIncr(); + if (frameDimensions._height < charHeight) + charHeight = frameDimensions._height; + + v0->drawActorRect(Common::Rect(textInfoDimensions._width - 1, charHeight - 1), _activeMenu->_fieldE); + + updateActor318(); +} + +void BaseMenuSystem::updateActor318() { + Control *v0 = _vm->getObjectControl(0x4013E); + WRect rect; + calcMenuItemRect(_hoveredMenuItemIndex2 - _hoveredMenuItemIndex3 + 1, rect); + v0->setActorPosition(rect._topLeft); +} + +void BaseMenuSystem::hideActor318() { + Control *v0 = _vm->getObjectControl(0x4013E); + if (v0) + v0->disappearActor(); +} + +void BaseMenuSystem::initActor323() { + Control *v0 = _vm->getObjectControl(0x40143); + if (!v0) { + WidthHeight dimensions; + dimensions._width = 300; + dimensions._height = 180; + _vm->_controls->placeSequenceLessActor(0x40143, Common::Point(0, 0), dimensions, 17); + v0 = _vm->getObjectControl(0x40143); + v0->_flags |= 8; + } + placeActor323(); + v0->appearActor(); +} + +void BaseMenuSystem::placeActor323() { + Control *v0 = _vm->getObjectControl(0x40143); + v0->fillActor(0); + + Common::Point textInfoPosition; + WidthHeight textInfoDimensions; + _vm->_screenText->getTextInfoPosition(textInfoPosition); + _vm->_screenText->getTextInfoDimensions(textInfoDimensions); + + /* TODO + if (_activeMenu->_field8 && _activeMenu->_fieldA != _activeMenu->_field8) { + textInfoDimensions._width -= 2; + textInfoDimensions._height -= 6; + } + */ + + v0->setActorPosition(textInfoPosition); + v0->drawActorRect(Common::Rect(textInfoDimensions._width - 1, textInfoDimensions._height - 1), _activeMenu->_fieldC); + +} + +void BaseMenuSystem::hideActor323() { + Control *v0 = _vm->getObjectControl(0x40143); + if (v0) + v0->disappearActor(); +} + +void BaseMenuSystem::openMenu(BaseMenu *menu) { + + _isActive = true; + _menuStack.clear(); + + _cursorInitialVisibleFlag = initMenuCursor(); + _savedCursorPos = _vm->_input->getCursorPosition(); + _savedGameState = getGameState(); + Control *cursorControl = _vm->getObjectControl(0x40004); + _savedCursorActorIndex = cursorControl->_actor->_actorIndex; + _savedCursorSequenceId = cursorControl->_actor->_sequenceId; + + setMenuCursorNum(1); + + setGameState(4); + + activateMenu(menu); + + _hoveredMenuItemIndex = _hoveredMenuItemIndex3; + _hoveredMenuItemIndex2 = _hoveredMenuItemIndex3; + setMouseCursorToMenuItem(_hoveredMenuItemIndex); + initActor318(); + initActor323(); + _vm->_input->discardAllEvents(); +} + +void BaseMenuSystem::closeMenu() { + while (!_menuStack.empty()) { + _vm->_screenText->removeText(); + _menuStack.pop(); + } + _vm->_screenText->removeText(); + hideActor318(); + hideActor323(); + Control *mouseCursor = _vm->getObjectControl(0x40004); + setGameState(_savedGameState); + mouseCursor->_actor->_actorIndex = _savedCursorActorIndex; + mouseCursor->_actor->_position = _savedCursorPos; + setMousePos(_savedCursorPos); + mouseCursor->startSequenceActor(_savedCursorSequenceId, 2, 0); + if (_cursorInitialVisibleFlag) + mouseCursor->disappearActor(); + _vm->_input->discardAllEvents(); + _isActive = false; +} + +void BaseMenuSystem::handleClick(uint menuItemIndex, const Common::Point &mousePos) { + + if (menuItemIndex == 0) { + playSoundEffect14(); + return; + } + + MenuItem *menuItem = _activeMenu->getMenuItem(menuItemIndex - 1); + menuItem->executeAction(); + +} + +uint BaseMenuSystem::drawMenuText(BaseMenu *menu) { + MenuTextBuilder *menuTextBuilder = new MenuTextBuilder(); + uint lineCount = 0; + + for (uint i = 0; i < menu->getHeaderLinesCount(); ++i) { + menuTextBuilder->appendString(menu->getHeaderLine(i)); + menuTextBuilder->appendNewLine(); + } + + for (uint i = _hoveredMenuItemIndex3; i <= _field54; ++i) { + menuTextBuilder->appendString(menu->getMenuItem(i - 1)->getText()); + if (i + 1 <= menu->getMenuItemsCount()) + menuTextBuilder->appendNewLine(); + ++lineCount; + } + + menuTextBuilder->finalize(); + + uint16 *text = menuTextBuilder->getText(); + + Common::Point textPt; + int16 v9 = 0; + if (menu->_field8) + v9 = 4; + textPt.x = v9; + textPt.y = v9; + + uint flags = 1; + if (menu->_field8 != menu->_fieldA) + flags = 25; + + WidthHeight dimensions; + dimensions._width = 300; + dimensions._height = 180; + + uint16 *outTextPtr; + if (!_vm->_screenText->insertText(text, menu->_fontId, dimensions, textPt, flags, menu->_field8, menu->_fieldA, 0xFF, 0xFF, 0xFF, outTextPtr)) { + --lineCount; + for ( ; *outTextPtr; ++outTextPtr) { + if (*outTextPtr == 13) + --lineCount; + } + } + + delete menuTextBuilder; + + return lineCount; +} + +void BaseMenuSystem::update(Control *cursorControl) { + Common::Point mousePos = _vm->_input->getCursorPosition(); + setMousePos(mousePos); + + uint newHoveredMenuItemIndex; + bool resetTimeOut = false; + + if (calcMenuItemIndexAtPoint(mousePos, newHoveredMenuItemIndex) && newHoveredMenuItemIndex != _hoveredMenuItemIndex) { + if (_hoveredMenuItemIndex == 0) + initActor318(); + _hoveredMenuItemIndex = newHoveredMenuItemIndex; + _hoveredMenuItemIndex2 = newHoveredMenuItemIndex; + setMenuCursorNum(2); + updateActor318(); + resetTimeOut = true; + } else if (_hoveredMenuItemIndex == 0) { + setMenuCursorNum(1); + hideActor318(); + _hoveredMenuItemIndex = 0; + resetTimeOut = true; + } + + if (_vm->_input->hasNewEvents()) + resetTimeOut = true; + + if (_vm->_input->pollEvent(kEventLeftClick)) { + handleClick(_hoveredMenuItemIndex, mousePos); + } else if (_vm->_input->pollEvent(kEventAbort) && _activeMenu->_defaultMenuItemIndex) { + handleClick(_activeMenu->_defaultMenuItemIndex, mousePos); + } else if (_vm->_input->pollEvent(kEventUp)) { + // TODO handleUpKey(); + } else if (_vm->_input->pollEvent(kEventDown)) { + // TODO handleDownKey(); + } + + updateTimeOut(resetTimeOut); +} + +void BaseMenuSystem::setTimeOutDuration(uint32 duration, uint timeOutMenuChoiceIndex) { + if (duration > 0) { + _isTimeOutEnabled = true; + _isTimeOutReached = false; + _timeOutDuration = duration; + _timeOutMenuChoiceIndex = timeOutMenuChoiceIndex; + _timeOutStartTime = getCurrentTime(); + _timeOutEndTime = duration + _timeOutStartTime; + } else { + _isTimeOutEnabled = false; + } +} + +void BaseMenuSystem::setMenuCallerThreadId(uint32 menuCallerThreadId) { + _menuCallerThreadId = menuCallerThreadId; +} + +void BaseMenuSystem::setMenuChoiceOffsets(MenuChoiceOffsets menuChoiceOffsets, int16 *menuChoiceOffset) { + _menuChoiceOffsets = menuChoiceOffsets; + _menuChoiceOffset = menuChoiceOffset; +} + +void BaseMenuSystem::updateTimeOut(bool resetTimeOut) { + + if (!_isTimeOutEnabled) + return; + + if (_menuStack.empty()) { + if (_isTimeOutReached) { + resetTimeOut = true; + _isTimeOutReached = false; + } + } else if (!_isTimeOutReached) { + _isTimeOutReached = true; + } + + if (!_isTimeOutReached) { + if (resetTimeOut) { + _timeOutStartTime = getCurrentTime(); + _timeOutEndTime = _timeOutDuration + _timeOutStartTime; + } else if (isTimerExpired(_timeOutStartTime, _timeOutEndTime)) { + _isTimeOutEnabled = false; + selectMenuChoiceIndex(_timeOutMenuChoiceIndex); + // TODO *_menuChoiceOffset = *((_WORD *)&_menuCallerThreadId + _timeOutMenuChoiceIndex + 1); + //_vm->_threads->notifyId(_menuCallerThreadId); + //_menuCallerThreadId = 0; + //closeMenu(); + } + } + +} + +// MenuTextBuilder + +MenuTextBuilder::MenuTextBuilder() : _pos(0) { +} + +void MenuTextBuilder::appendString(const Common::String &value) { + for (uint i = 0; i < value.size(); ++i) + _text[_pos++] = value[i]; +} + +void MenuTextBuilder::appendNewLine() { + _text[_pos++] = '\r'; +} + +void MenuTextBuilder::finalize() { + _text[_pos] = '\0'; +} + +// BaseMenuAction + +BaseMenuAction::BaseMenuAction(BaseMenuSystem *menuSystem) + : _menuSystem(menuSystem) { +} + +// MenuActionEnterMenu + +MenuActionEnterMenu::MenuActionEnterMenu(BaseMenuSystem *menuSystem, int menuId) + : BaseMenuAction(menuSystem), _menuId(menuId) { +} + +void MenuActionEnterMenu::execute() { + _menuSystem->enterSubMenuById(_menuId); +} + +// MenuActionLeaveMenu + +MenuActionLeaveMenu::MenuActionLeaveMenu(BaseMenuSystem *menuSystem) + : BaseMenuAction(menuSystem) { +} + +void MenuActionLeaveMenu::execute() { + _menuSystem->leaveMenu(); +} + +// MenuActionReturnChoice + +MenuActionReturnChoice::MenuActionReturnChoice(BaseMenuSystem *menuSystem, uint choiceIndex) + : BaseMenuAction(menuSystem), _choiceIndex(choiceIndex) { +} + +void MenuActionReturnChoice::execute() { + _menuSystem->playSoundEffect13(); + _menuSystem->selectMenuChoiceIndex(_choiceIndex); +} + +// MenuActionEnterQueryMenu + +MenuActionEnterQueryMenu::MenuActionEnterQueryMenu(BaseMenuSystem *menuSystem, int menuId, uint confirmationChoiceIndex) + : BaseMenuAction(menuSystem), _menuId(menuId), _confirmationChoiceIndex(confirmationChoiceIndex) { +} + +void MenuActionEnterQueryMenu::execute() { + _menuSystem->setQueryConfirmationChoiceIndex(_confirmationChoiceIndex); + _menuSystem->enterSubMenuById(_menuId); +} + +} // End of namespace Illusions diff --git a/engines/illusions/menusystem.h b/engines/illusions/menusystem.h new file mode 100644 index 0000000000..98ef924300 --- /dev/null +++ b/engines/illusions/menusystem.h @@ -0,0 +1,235 @@ +/* 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. + * + */ + +#ifndef ILLUSIONS_MENUSYSTEM_H +#define ILLUSIONS_MENUSYSTEM_H + +#include "illusions/actor.h" +#include "illusions/graphics.h" +#include "illusions/resources/fontresource.h" +#include "common/array.h" +#include "common/rect.h" +#include "common/stack.h" +#include "common/str.h" +#include "graphics/surface.h" + +namespace Illusions { + +class IllusionsEngine; + +class BaseMenuSystem; +class BaseMenuAction; + +const uint kMenuTextSize = 4096; + +class MenuItem { +public: + MenuItem(const Common::String text, BaseMenuAction *action); + ~MenuItem(); + void executeAction(); + const Common::String& getText() const { return _text; } +protected: + Common::String _text; + BaseMenuAction *_action; +}; + +class BaseMenu { +public: + BaseMenu(BaseMenuSystem *menuSystem, uint32 fontId, byte field8, byte fieldA, byte fieldC, byte fieldE, + uint defaultMenuItemIndex); + virtual ~BaseMenu(); + void addText(const Common::String text); + void addMenuItem(MenuItem *menuItem); + uint getHeaderLinesCount(); + const Common::String& getHeaderLine(uint index); + uint getMenuItemsCount(); + MenuItem *getMenuItem(uint index); + virtual void enterMenu(); +public://protected://TODO + typedef Common::Array<MenuItem*> MenuItems; + BaseMenuSystem *_menuSystem; + uint32 _fontId; + byte _field8, _fieldA, _fieldC, _fieldE; + uint _field2C18; + uint _defaultMenuItemIndex; + Common::Array<Common::String> _text; + MenuItems _menuItems; +}; + +class MenuStack : public Common::Stack<BaseMenu*> { +}; + +typedef Common::Array<int16> MenuChoiceOffsets; + +class BaseMenuSystem { +public: + BaseMenuSystem(IllusionsEngine *vm); + virtual ~BaseMenuSystem(); + void playSoundEffect13(); + void playSoundEffect14(); + void selectMenuChoiceIndex(uint choiceIndex); + void leaveMenu(); + void enterSubMenu(BaseMenu *menu); + void leaveSubMenu(); + void enterSubMenuById(int menuId); + uint getQueryConfirmationChoiceIndex() const; + void setQueryConfirmationChoiceIndex(uint queryConfirmationChoiceIndex); + bool isActive() const { return _isActive; } + void openMenu(BaseMenu *menu); + void closeMenu(); + void handleClick(uint menuItemIndex, const Common::Point &mousePos); + uint drawMenuText(BaseMenu *menu); + void update(Control *cursorControl); + void setTimeOutDuration(uint32 duration, uint timeOutMenuChoiceIndex); + void setMenuCallerThreadId(uint32 menuCallerThreadId); + void setMenuChoiceOffsets(MenuChoiceOffsets menuChoiceOffsets, int16 *menuChoiceOffset); + virtual bool initMenuCursor() = 0; + virtual int getGameState() = 0; + virtual void setGameState(int gameState) = 0; + virtual void setMenuCursorNum(int cursorNum) = 0; +protected: + IllusionsEngine *_vm; + MenuStack _menuStack; + + uint32 _menuCallerThreadId; + bool _isTimeOutEnabled; + bool _isTimeOutReached; + uint32 _timeOutDuration; + uint _timeOutMenuChoiceIndex; + uint32 _timeOutStartTime; + uint32 _timeOutEndTime; + + Common::Point _savedCursorPos; + bool _cursorInitialVisibleFlag; + int _savedGameState; + int _savedCursorActorIndex; + int _savedCursorSequenceId; + + bool _isActive; + + MenuChoiceOffsets _menuChoiceOffsets; + int16 *_menuChoiceOffset; + + uint _queryConfirmationChoiceIndex; + + uint _field54; + uint _menuLinesCount; + uint _menuItemCount; + + uint _hoveredMenuItemIndex; + uint _hoveredMenuItemIndex2; + uint _hoveredMenuItemIndex3; + + BaseMenu *_activeMenu; + void setMouseCursorToMenuItem(int menuItemIndex); + + void calcMenuItemRect(uint menuItemIndex, WRect &rect); + bool calcMenuItemMousePos(uint menuItemIndex, Common::Point &pt); + bool calcMenuItemIndexAtPoint(Common::Point pt, uint &menuItemIndex); + void setMousePos(Common::Point &mousePos); + + void activateMenu(BaseMenu *menu); + + void updateTimeOut(bool resetTimeOut); + + void initActor318(); + void placeActor318(); + void updateActor318(); + void hideActor318(); + + void initActor323(); + void placeActor323(); + void hideActor323(); + + virtual BaseMenu *getMenuById(int menuId) = 0; +}; + +/* + + +*/ + +class MenuTextBuilder { +public: + MenuTextBuilder(); + void appendString(const Common::String &value); + void appendNewLine(); + void finalize(); + uint16 *getText() { return _text; } +protected: + uint16 _text[kMenuTextSize]; + uint _pos; +}; + +// Menu actions + +class BaseMenuAction { +public: + BaseMenuAction(BaseMenuSystem *menuSystem); + virtual ~BaseMenuAction() {} + virtual void execute() = 0; +protected: + BaseMenuSystem *_menuSystem; +}; + +// Type 1: Enter a submenu + +class MenuActionEnterMenu : public BaseMenuAction { +public: + MenuActionEnterMenu(BaseMenuSystem *menuSystem, int menuId); + virtual void execute(); +protected: + int _menuId; +}; + +// Type 4: Leave a submenu or the whole menu if on the main menu level + +class MenuActionLeaveMenu : public BaseMenuAction { +public: + MenuActionLeaveMenu(BaseMenuSystem *menuSystem); + virtual void execute(); +}; + +// Type 5: Return a menu choice index and exit the menu + +class MenuActionReturnChoice : public BaseMenuAction { +public: + MenuActionReturnChoice(BaseMenuSystem *menuSystem, uint choiceIndex); + virtual void execute(); +protected: + int _choiceIndex; +}; + +// Type 8: Return a menu choice index and exit the menu after displaying a query message + +class MenuActionEnterQueryMenu : public BaseMenuAction { +public: + MenuActionEnterQueryMenu(BaseMenuSystem *menuSystem, int menuId, uint confirmationChoiceIndex); + virtual void execute(); +protected: + int _menuId; + uint _confirmationChoiceIndex; +}; + +} // End of namespace Illusions + +#endif // ILLUSIONS_MENUSYSTEM_H diff --git a/engines/illusions/module.mk b/engines/illusions/module.mk index ee40fb37bc..d4bc3930d8 100644 --- a/engines/illusions/module.mk +++ b/engines/illusions/module.mk @@ -18,6 +18,7 @@ MODULE_OBJS := \ duckman/duckman_screenshakereffects.o \ duckman/duckman_specialcode.o \ duckman/illusions_duckman.o \ + duckman/menusystem_duckman.o \ duckman/propertytimers.o \ duckman/scriptopcodes_duckman.o \ fileresourcereader.o \ @@ -27,6 +28,7 @@ MODULE_OBJS := \ graphics.o \ illusions.o \ input.o \ + menusystem.o \ pathfinder.o \ resources/actorresource.o \ resources/backgroundresource.o \ diff --git a/engines/illusions/resources/fontresource.h b/engines/illusions/resources/fontresource.h index e0291452df..fa758bc4f1 100644 --- a/engines/illusions/resources/fontresource.h +++ b/engines/illusions/resources/fontresource.h @@ -65,6 +65,8 @@ public: void load(Resource *resource); CharInfo *getCharInfo(uint16 c); int16 getColorIndex() const { return _colorIndex; } + int16 getCharHeight() const { return _charHeight; } + int16 getLineIncr() const { return _lineIncr; } public: uint32 _totalSize; int16 _charHeight; diff --git a/engines/illusions/screentext.cpp b/engines/illusions/screentext.cpp index 8c31705300..7c39823dc5 100644 --- a/engines/illusions/screentext.cpp +++ b/engines/illusions/screentext.cpp @@ -42,6 +42,10 @@ void ScreenText::getTextInfoDimensions(WidthHeight &textInfoDimensions) { textInfoDimensions = _dimensions; } +void ScreenText::getTextInfoPosition(Common::Point &position) { + position = _position; +} + void ScreenText::setTextInfoPosition(Common::Point position) { _position = position; clipTextInfoPosition(_position); @@ -86,6 +90,7 @@ bool ScreenText::refreshScreenText(FontResource *font, WidthHeight dimensions, C bool done = textDrawer.wrapText(font, text, &dimensions, offsPt, textFlags, outTextPtr); _surface = _vm->_screen->allocSurface(dimensions._width, dimensions._height); _surface->fillRect(Common::Rect(0, 0, _surface->w, _surface->h), _vm->_screen->getColorKey1()); + debug("ScreenText dimensions (%d, %d)", dimensions._width, dimensions._height); _dimensions = dimensions; textDrawer.drawText(_vm->_screen, _surface, color2, color1); return done; @@ -121,6 +126,7 @@ bool ScreenText::insertText(uint16 *text, uint32 fontId, WidthHeight dimensions, _screenTexts.push_back(screenText); FontResource *font = _vm->_dict->findFont(screenText->_info._fontId); + debug("font: %p", font); bool done = refreshScreenText(font, screenText->_info._dimensions, screenText->_info._offsPt, text, screenText->_info._flags, screenText->_info._color2, screenText->_info._color1, outTextPtr); diff --git a/engines/illusions/screentext.h b/engines/illusions/screentext.h index 4340224bdb..6b365e5f6e 100644 --- a/engines/illusions/screentext.h +++ b/engines/illusions/screentext.h @@ -54,6 +54,7 @@ public: ScreenText(IllusionsEngine *vm); ~ScreenText(); void getTextInfoDimensions(WidthHeight &textInfoDimensions); + void getTextInfoPosition(Common::Point &position); void setTextInfoPosition(Common::Point position); void updateTextInfoPosition(Common::Point position); void clipTextInfoPosition(Common::Point &position); diff --git a/engines/illusions/scriptopcodes.cpp b/engines/illusions/scriptopcodes.cpp index 7d515a8109..38fdbec3ce 100644 --- a/engines/illusions/scriptopcodes.cpp +++ b/engines/illusions/scriptopcodes.cpp @@ -62,7 +62,7 @@ ScriptOpcodes::~ScriptOpcodes() { void ScriptOpcodes::execOpcode(ScriptThread *scriptThread, OpCall &opCall) { if (!_opcodes[opCall._op]) error("ScriptOpcodes::execOpcode() Unimplemented opcode %d", opCall._op); - debug(0, "\nexecOpcode([%08X] %d) %s", opCall._callerThreadId, opCall._op, _opcodeNames[opCall._op].c_str()); + debug("\nexecOpcode([%08X] %d) %s", opCall._callerThreadId, opCall._op, _opcodeNames[opCall._op].c_str()); (*_opcodes[opCall._op])(scriptThread, opCall); } diff --git a/engines/illusions/sound.cpp b/engines/illusions/sound.cpp index 5eebf00d21..5b1eea85f8 100644 --- a/engines/illusions/sound.cpp +++ b/engines/illusions/sound.cpp @@ -37,7 +37,7 @@ MusicPlayer::~MusicPlayer() { } void MusicPlayer::play(uint32 musicId, bool looping, int16 volume, int16 pan) { - debug("MusicPlayer::play(%08X)", musicId); + debug(1, "MusicPlayer::play(%08X)", musicId); if (_flags & 1) { stop(); _musicId = musicId; @@ -57,7 +57,7 @@ void MusicPlayer::play(uint32 musicId, bool looping, int16 volume, int16 pan) { } void MusicPlayer::stop() { - debug("MusicPlayer::stop()"); + debug(1, "MusicPlayer::stop()"); if ((_flags & 1) && (_flags & 2)) { if (g_system->getMixer()->isSoundHandleActive(_soundHandle)) g_system->getMixer()->stopHandle(_soundHandle); @@ -82,7 +82,7 @@ VoicePlayer::~VoicePlayer() { } bool VoicePlayer::cue(const char *voiceName) { - debug("VoicePlayer::cue(%s)", voiceName); + debug(1, "VoicePlayer::cue(%s)", voiceName); _voiceName = voiceName; _voiceStatus = 2; if (!isEnabled()) { @@ -147,7 +147,7 @@ void Sound::load() { } void Sound::unload() { - debug("Sound::unload() %08X", _soundEffectId); + debug(1, "Sound::unload() %08X", _soundEffectId); stop(); delete _stream; _stream = 0; diff --git a/engines/illusions/textdrawer.cpp b/engines/illusions/textdrawer.cpp index 61c4455150..f1c93c7c80 100644 --- a/engines/illusions/textdrawer.cpp +++ b/engines/illusions/textdrawer.cpp @@ -161,7 +161,7 @@ bool TextDrawer::wrapTextIntern(int16 x, int16 y, int16 maxWidth, int16 maxHeigh } } - + _dimensions->_width = maxLineWidth; _dimensions->_height = textPosY - _font->_lineIncr; |