diff --git a/engines/startrek/events.cpp b/engines/startrek/events.cpp new file mode 100644 index 00000000000..a37ae8c7a45 --- /dev/null +++ b/engines/startrek/events.cpp @@ -0,0 +1,94 @@ +/* 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 "startrek/startrek.h" + +namespace StarTrek { + +void StarTrekEngine::initializeEventsAndMouse() { + _mouseMoveEventInQueue = false; + _tickEventInQueue = false; + + // TODO: mouse +} + +/** + * Returns false if there is no event waiting. + */ +bool StarTrekEngine::getNextEvent(TrekEvent *e) { + pollSystemEvents(); // TODO: put this somewhere else? + + if (_eventQueue.empty()) + return false; + *e = _eventQueue.front(); + return true; +} + +void StarTrekEngine::removeNextEvent() { + if (_eventQueue.empty()) + return; + + const TrekEvent &e = _eventQueue.front(); + + if (e.type == TREKEVENT_MOUSEMOVE) + _mouseMoveEventInQueue = false; + if (e.type == TREKEVENT_TICK) + _tickEventInQueue = false; + + _eventQueue.pop_front(); +} + +bool StarTrekEngine::popNextEvent(TrekEvent *e) { + if (!getNextEvent(e)) + return false; + + removeNextEvent(); + return true; +} + +void StarTrekEngine::addEventToQueue(const TrekEvent &e) { + if (e.type == TREKEVENT_MOUSEMOVE && _mouseMoveEventInQueue) { + // Only allow one mouse move event at once + for (Common::List::iterator i = _eventQueue.begin(); i!=_eventQueue.end(); i++) { + if (i->type == TREKEVENT_MOUSEMOVE) { + *i = e; + return; + } + } + + error("Couldn't find mouse move event in eventQueue"); + } + + if (e.type == TREKEVENT_TICK) { + // Only allow one tick event at once + if (_tickEventInQueue) + return; + _tickEventInQueue = true; + } + + if (e.type == TREKEVENT_MOUSEMOVE) + _mouseMoveEventInQueue = true; + + assert(_eventQueue.size() < 0x40); + _eventQueue.push_back(e); +} + +} diff --git a/engines/startrek/graphics.h b/engines/startrek/graphics.h index 62c49f2d01f..578abdf705f 100755 --- a/engines/startrek/graphics.h +++ b/engines/startrek/graphics.h @@ -47,23 +47,24 @@ const int MAX_SPRITES = 32; const int MAX_MENUBUTTONS = 16; // This is arbitrary, the original game has no such limit const int TEXTBOX_WIDTH = 26; +const int MAX_TEXTBOX_LINES = 12; // Keeps track of data for a list of buttons making up a menu struct Menu { Sprite sprites[MAX_MENUBUTTONS]; uint16 retvals[MAX_MENUBUTTONS]; - uint16 buttonVar2; + uint32 disabledButtons; SharedPtr menuFile; uint16 numButtons; - uint16 buttonVar1; + int16 selectedButton; SharedPtr nextMenu; Menu() : nextMenu(SharedPtr()) {} }; class Graphics; -typedef String (Graphics::*TextGetterFunc)(int, int, String *); +typedef String (Graphics::*TextGetterFunc)(int, void *, String *); class Graphics { @@ -115,11 +116,12 @@ private: // text.cpp (TODO: separate class) public: int showText(TextGetterFunc textGetter, int var, int xoffset, int yoffset, int textColor, bool loopChoices, int maxTextLines, int arg10); - String tmpFunction(int choiceIndex, int var, String *headerTextOutput); - String readTextFromRdf(int choiceIndex, int rdfVar, String *headerTextOutput); + + String readTextFromRdf(int choiceIndex, void *data, String *headerTextOutput); + String readTextFromBuffer(int choiceIndex, void *data, String *headerTextOutput); private: - int handleTextboxEvents(uint32 arg0, bool arg4); + int handleTextboxEvents(uint32 ticksUntilClickingEnabled, bool arg4); SharedPtr initTextSprite(int *xoffsetPtr, int *yoffsetPtr, byte textColor, int numTextLines, bool withHeader, Sprite *sprite); void drawMainText(SharedPtr bitmap, int numTextLines, int numTextboxLines, const String &text, bool withHeader); @@ -134,20 +136,24 @@ private: String skipTextAudioPrompt(const String &str); String playTextAudio(const String &str); + int getMenuButtonAt(const Menu &menu, int x, int y); + void drawMenuButtonOutline(SharedPtr bitmap, byte color); void loadMenuButtons(String mnuFilename, int xpos, int ypos); void setMenuButtonVar2Bits(uint32 bits); void clearMenuButtonVar2Bits(uint32 bits); + uint16 _textboxVar1; uint32 _textboxVar2; uint32 _textboxVar3; - uint16 _textboxVar4; - uint16 _textboxVar5; uint16 _textboxVar6; uint16 _textboxVar7; bool _textboxHasMultipleChoices; SharedPtr _activeMenu; + + uint16 _textboxButtonVar4; + uint16 _word_4B422; }; } diff --git a/engines/startrek/module.mk b/engines/startrek/module.mk index ea8d4d385a1..9114de46321 100755 --- a/engines/startrek/module.mk +++ b/engines/startrek/module.mk @@ -4,6 +4,7 @@ MODULE_OBJS = \ bitmap.o \ common.o \ detection.o \ + events.o \ filestream.o \ font.o \ lzss.o \ diff --git a/engines/startrek/startrek.cpp b/engines/startrek/startrek.cpp index f83a18218b6..1b333b925a2 100755 --- a/engines/startrek/startrek.cpp +++ b/engines/startrek/startrek.cpp @@ -55,6 +55,9 @@ StarTrekEngine::~StarTrekEngine() { } Common::Error StarTrekEngine::run() { + _cdAudioEnabled = true; + _midiAudioEnabled = true; + _gfx = new Graphics(this); _sound = new Sound(this); @@ -66,6 +69,8 @@ Common::Error StarTrekEngine::run() { } initGraphics(320, 200); + + initializeEventsAndMouse(); // Hexdump data #if 0 @@ -151,21 +156,38 @@ Common::Error StarTrekEngine::run() { _gfx->showText(&Graphics::readTextFromRdf, 0x2220, 150, 160, 0xb3, 0, 10, 0); while (!shouldQuit()) { - pollEvents(); + pollSystemEvents(); } #endif return Common::kNoError; } -void StarTrekEngine::pollEvents() { +Room *StarTrekEngine::getRoom() { + return _room; +} + +void StarTrekEngine::pollSystemEvents() { Common::Event event; + TrekEvent trekEvent; while (_eventMan->pollEvent(event)) { + trekEvent.mouse = event.mouse; + trekEvent.kbd = event.kbd; + switch (event.type) { case Common::EVENT_QUIT: _system->quit(); break; + + case Common::EVENT_MOUSEMOVE: + trekEvent.type = TREKEVENT_MOUSEMOVE; + addEventToQueue(trekEvent); + break; + case Common::EVENT_LBUTTONDOWN: + trekEvent.type = TREKEVENT_LBUTTONDOWN; + addEventToQueue(trekEvent); + break; default: break; } @@ -175,8 +197,14 @@ void StarTrekEngine::pollEvents() { _system->delayMillis(1000/60); } -Room *StarTrekEngine::getRoom() { - return _room; +void StarTrekEngine::playSound(int id) { + // TODO +} + +void StarTrekEngine::updateClockTicks() { + // TODO (based on DOS interrupt 1A, AH=0; read system clock counter) + + _clockTicks = 0; } SharedPtr StarTrekEngine::openFile(Common::String filename, int fileIndex) { diff --git a/engines/startrek/startrek.h b/engines/startrek/startrek.h index e19fd114369..a0b97b97608 100755 --- a/engines/startrek/startrek.h +++ b/engines/startrek/startrek.h @@ -17,15 +17,13 @@ * 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. - * - * $URL: https://scummvm-startrek.googlecode.com/svn/trunk/startrek.h $ - * $Id: startrek.h 14 2010-05-26 15:44:12Z clone2727 $ - * */ #ifndef STARTREK_H #define STARTREK_H +#include "common/events.h" +#include "common/list.h" #include "common/ptr.h" #include "common/rect.h" #include "common/scummsys.h" @@ -59,6 +57,24 @@ enum StarTrekGameFeatures { GF_DEMO = (1 << 0) }; + +enum TrekEventType { + TREKEVENT_TICK = 0, // DOS clock changes (see updateClockTicks) + TREKEVENT_LBUTTONDOWN = 1, + TREKEVENT_MOUSEMOVE = 2, + TREKEVENT_LBUTTONUP = 3, + TREKEVENT_RBUTTONDOWN = 4, + TREKEVENT_RBUTTONUP = 5, + TREKEVENT_KEYDOWN = 6 +}; + +struct TrekEvent { + TrekEventType type; + Common::KeyState kbd; + Common::Point mouse; + uint32 tick; +}; + struct StarTrekGameDescription; class Graphics; class Sound; @@ -72,9 +88,35 @@ public: virtual ~StarTrekEngine(); // Running the game - void pollEvents(); Room *getRoom(); + void pollSystemEvents(); + void playSound(int id); // TODO: rename, figure out what it is + + // Events +public: + void initializeEventsAndMouse(); + bool getNextEvent(TrekEvent *e); + void removeNextEvent(); + bool popNextEvent(TrekEvent *e); + void addEventToQueue(const TrekEvent &e); + void clearEventBuffer(); + uint32 getClockTicks(); + void updateEvents(); + void updateTimerEvent(); + void updateMouseEvents(); + void updateKeyboardEvents(); + void updateClockTicks(); + bool checkKeyPressed(); + + Common::EventManager *getEventMan() { return _eventMan; } + +private: + Common::List _eventQueue; + bool _mouseMoveEventInQueue; + bool _tickEventInQueue; + +public: // Detection related functions const StarTrekGameDescription *_gameDescription; uint32 getFeatures() const; @@ -89,6 +131,14 @@ public: // Movie related functions void playMovie(Common::String filename); void playMovieMac(Common::String filename); + + + uint32 _clockTicks; + + bool _midiAudioEnabled; + bool _cdAudioEnabled; + bool _textboxVar4; + private: Graphics *_gfx; diff --git a/engines/startrek/text.cpp b/engines/startrek/text.cpp index 9e54e8375ce..6ac31b02a47 100644 --- a/engines/startrek/text.cpp +++ b/engines/startrek/text.cpp @@ -19,19 +19,20 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include "common/events.h" #include "common/stream.h" #include "graphics/cursorman.h" #include "startrek/graphics.h" -// List of events that can be returned by handleTextEvents. +// List of events that can be returned by handleTextboxEvents. enum TextEvent { - TEXTEVENT_RCLICK_OFFBUTTON = 0, + TEXTEVENT_RCLICK_OFFBUTTON = -4, TEXTEVENT_ENABLEINPUT, // Makes buttons selectable (occurs after a delay) TEXTEVENT_RCLICK_ONBUTTON, TEXTEVENT_LCLICK_OFFBUTTON, - TEXTEVENT_CONFIRM, + TEXTEVENT_CONFIRM = 0, TEXTEVENT_SCROLLUP, TEXTEVENT_SCROLLDOWN, TEXTEVENT_PREVCHOICE, @@ -59,7 +60,7 @@ int Graphics::showText(TextGetterFunc textGetter, int var, int xoffset, int yoff String speakerText; while(true) { - String choiceText = (this->*textGetter)(numChoices, var, &speakerText); + String choiceText = (this->*textGetter)(numChoices, &var, &speakerText); if (choiceText.empty()) break; @@ -73,8 +74,8 @@ int Graphics::showText(TextGetterFunc textGetter, int var, int xoffset, int yoff numChoices++; } - if (maxTextLines == 0 || maxTextLines > 12) - maxTextLines = 12; + if (maxTextLines == 0 || maxTextLines > MAX_TEXTBOX_LINES) + maxTextLines = MAX_TEXTBOX_LINES; if (numTextboxLines > maxTextLines) numTextboxLines = maxTextLines; @@ -87,12 +88,12 @@ int Graphics::showText(TextGetterFunc textGetter, int var, int xoffset, int yoff int choiceIndex = 0; int scrollOffset = 0; if (tmpTextboxVar1 != 0 && tmpTextboxVar1 != 1 && numChoices == 1 - && _textboxVar5 != 0 && _textboxVar4 == 0) + && _vm->_cdAudioEnabled && !_vm->_textboxVar4) _textboxHasMultipleChoices = false; else _textboxHasMultipleChoices = true; - if (tmpTextboxVar1 >= 0 && tmpTextboxVar1 <= 2 && _textboxVar5 == 1 && _textboxVar4 == 0) + if (tmpTextboxVar1 >= 0 && tmpTextboxVar1 <= 2 && _vm->_cdAudioEnabled && !_vm->_textboxVar4) _textboxVar6 = true; else _textboxVar6 = false; @@ -196,7 +197,12 @@ readjustScrollDown: readjustScroll: textboxSprite.bitmapChanged = true; - // sub_225d3(textBitmap, numTextLines-scrollOffset, numTextboxLines, lineFormattedText+scrollOffset*0x18); + drawMainText( + textBitmap, + numTextLines-scrollOffset, + numTextboxLines, + lineFormattedText.c_str() + scrollOffset*(TEXTBOX_WIDTH-2), + numChoicesWithNames != 0); break; case TEXTEVENT_PREVCHOICE: @@ -266,27 +272,97 @@ reloadText: return choiceIndex; } -int Graphics::handleTextboxEvents(uint32 arg0, bool arg4) { - // TODO +int Graphics::handleTextboxEvents(uint32 ticksUntilClickingEnabled, bool arg4) { + // TODO: finish + + uint32 tickWhenClickingEnabled = _vm->_clockTicks + ticksUntilClickingEnabled; + while (true) { - _vm->pollEvents(); + TrekEvent event; + while (_vm->popNextEvent(&event)) { + switch(event.type) { + + case TREKEVENT_TICK: { + case TREKEVENT_MOUSEMOVE: // FIXME: actual game only uses TICK event here + Common::Point mousePos = getMousePos(); + int buttonIndex = getMenuButtonAt(*_activeMenu, mousePos.x, mousePos.y); + if (buttonIndex != -1) { + if (_activeMenu->disabledButtons & (1<selectedButton) { + if (_activeMenu->selectedButton != -1) { + Sprite &spr = _activeMenu->sprites[_activeMenu->selectedButton]; + drawMenuButtonOutline(spr.bitmap, 0x00); + spr.bitmapChanged = true; + } + if (buttonIndex != -1) { + Sprite &spr = _activeMenu->sprites[buttonIndex]; + drawMenuButtonOutline(spr.bitmap, 0xda); + spr.bitmapChanged = true; + } + _activeMenu->selectedButton = buttonIndex; + } + // Not added: updating mouse position (scummvm handles that) + + // sub_10492(); + // sub_10A91(); + drawAllSprites(); + // sub_10BE7(); + // sub_2A4B1(); + + if (_word_4B422 != 0) { + _word_4B422 = 0; + if (_textboxVar1 != 0) { + return TEXTEVENT_SPEECH_DONE; + } + } + // sub_1E88C(); + _textboxVar3++; + + if (ticksUntilClickingEnabled != 0 && _vm->_clockTicks >= tickWhenClickingEnabled) + return TEXTEVENT_ENABLEINPUT; + break; + } + + case TREKEVENT_LBUTTONDOWN: + if (_activeMenu->selectedButton != -1) { + _vm->playSound(0x10); + return _activeMenu->retvals[_activeMenu->selectedButton]; + } + else { + Common::Point mouse = getMousePos(); + if (getMenuButtonAt(*_activeMenu, mouse.x, mouse.y) == -1) { + _vm->playSound(0x10); + return TEXTEVENT_LCLICK_OFFBUTTON; + } + } + break; + + case TREKEVENT_RBUTTONDOWN: + // TODO + break; + + case TREKEVENT_KEYDOWN: + // TODO + break; + + default: + break; + } + } } } -String Graphics::tmpFunction(int choiceIndex, int var, String *headerTextOutput) { - if (headerTextOutput != nullptr) - *headerTextOutput = "Speaker "; - - if (choiceIndex >= 1) - return NULL; - return "Text test"; -} - -String Graphics::readTextFromRdf(int choiceIndex, int rdfVar, String *headerTextOutput) { +/** + * Text getter for showText which reads from an rdf file. + */ +String Graphics::readTextFromRdf(int choiceIndex, void *data, String *headerTextOutput) { Room *room = _vm->getRoom(); - // FIXME: in original game "rdfVar" is a pointer to the variable holding the offset. - // Currently treating it as the offset itself. + int rdfVar = *(int*)data; + uint16 textOffset = room->readRdfWord(rdfVar + (choiceIndex+1)*2); if (textOffset == 0) @@ -308,6 +384,20 @@ String Graphics::readTextFromRdf(int choiceIndex, int rdfVar, String *headerText return (char*)&room->_rdfData[textOffset]; } +/** + * Text getter for showText which reads from a given buffer. + */ +String Graphics::readTextFromBuffer(int choiceIndex, void *data, String *headerTextOutput) { + char buf[TEXTBOX_WIDTH]; + memcpy(buf, data, TEXTBOX_WIDTH-2); + buf[TEXTBOX_WIDTH-2] = '\0'; + + *headerTextOutput = String(buf); + + char *text = (char*)data+TEXTBOX_WIDTH-2; + return String(text); +} + /** * Creates a blank textbox in a TextBitmap, and initializes a sprite to use it. */ @@ -407,7 +497,6 @@ void Graphics::drawMainText(SharedPtr bitmap, int numTextLines, int lineIndex++; } - debug("%d, %d\n", numTextLines, numTextboxLines); // Fill all remaining blank lines while (lineIndex != numTextboxLines) { memset(dest, ' ', TEXTBOX_WIDTH-2); @@ -448,15 +537,15 @@ void Graphics::getTextboxHeader(String *headerTextOutput, String speakerText, in String Graphics::readLineFormattedText(TextGetterFunc textGetter, int var, int choiceIndex, SharedPtr textBitmap, int numTextboxLines, int *numTextLines) { String headerText; - String text = (this->*textGetter)(choiceIndex, var, &headerText); + String text = (this->*textGetter)(choiceIndex, &var, &headerText); - if (_textboxVar1 == 2 && _textboxVar5 == 1 && _textboxVar4 == 1) { + if (_textboxVar1 == 2 && _vm->_cdAudioEnabled && _vm->_textboxVar4) { uint32 oldSize = text.size(); text = playTextAudio(text); if (oldSize != text.size()) _textboxHasMultipleChoices = true; } - else if ((_textboxVar1 == 0 || _textboxVar1 == 1) && _textboxVar5 == 1 && _textboxVar4 == 1) { + else if ((_textboxVar1 == 0 || _textboxVar1 == 1) && _vm->_cdAudioEnabled && _vm->_textboxVar4) { text = playTextAudio(text); } else { @@ -598,6 +687,58 @@ String Graphics::playTextAudio(const String &str) { return skipTextAudioPrompt(str); } +/** + * Returns the index of the button at the given position, or -1 if none. + */ +int Graphics::getMenuButtonAt(const Menu &menu, int x, int y) { + for (int i=0; ixoffset; + int top = spr.pos.y - spr.bitmap->yoffset; + + // Oddly, this doesn't account for x/yoffset... + int right = spr.pos.x + spr.bitmap->width - 1; + int bottom = spr.pos.y + spr.bitmap->height - 1; + + if (x >= left && x <= right && y >= top && y <= bottom) + return i; + } + + return -1; +} + +/** + * Draws or removes the outline on menu buttons when the cursor hovers on them, or leaves + * them. + */ +void Graphics::drawMenuButtonOutline(SharedPtr bitmap, byte color) { + int lineWidth = bitmap->width-2; + int offsetToBottom = (bitmap->height-3)*bitmap->width; + + byte *dest = bitmap->pixels + bitmap->width + 1; + + while (lineWidth--) { + *dest = color; + *(dest+offsetToBottom) = color; + dest++; + } + + int lineHeight = bitmap->height - 2; + int offsetToRight = bitmap->width - 3; + + dest = bitmap->pixels + bitmap->width + 1; + + while (lineHeight--) { + *dest = color; + *(dest+offsetToRight) = color; + dest += bitmap->width; + } +} + void Graphics::loadMenuButtons(String mnuFilename, int xpos, int ypos) { SharedPtr oldMenu = _activeMenu; _activeMenu = SharedPtr(new Menu()); @@ -629,6 +770,16 @@ void Graphics::loadMenuButtons(String mnuFilename, int xpos, int ypos) { _activeMenu->sprites[i].field6 = 8; } + + if (_activeMenu->retvals[_activeMenu->numButtons-1] == 0) { + // Set default retvals for buttons + for (int i=0; i<_activeMenu->numButtons; i++) + _activeMenu->retvals[i] = i; + } + + _activeMenu->selectedButton = -1; + _activeMenu->disabledButtons = 0; + _textboxButtonVar4 = 0; } // 0x0002: Disable scroll up