aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--engines/startrek/graphics.cpp4
-rw-r--r--engines/startrek/graphics.h78
-rw-r--r--engines/startrek/menu.cpp569
-rw-r--r--engines/startrek/module.mk3
-rw-r--r--engines/startrek/startrek.cpp4
-rw-r--r--engines/startrek/startrek.h114
-rw-r--r--engines/startrek/text.cpp1003
7 files changed, 903 insertions, 872 deletions
diff --git a/engines/startrek/graphics.cpp b/engines/startrek/graphics.cpp
index 250453466c..ec3c4d0d4d 100644
--- a/engines/startrek/graphics.cpp
+++ b/engines/startrek/graphics.cpp
@@ -46,10 +46,6 @@ Graphics::Graphics(StarTrekEngine *vm) : _vm(vm), _egaMode(false) {
_font = new Font(_vm);
_numSprites = 0;
- _textDisplayMode = TEXTDISPLAY_WAIT;
- _textboxVar2 = 0;
- _textboxVar6 = 0;
- _textboxHasMultipleChoices = false;
_palData = new byte[256 * 3];
_lutData = new byte[256 * 3];
diff --git a/engines/startrek/graphics.h b/engines/startrek/graphics.h
index f7d94020d9..0a737f7d72 100644
--- a/engines/startrek/graphics.h
+++ b/engines/startrek/graphics.h
@@ -44,25 +44,6 @@ const int SCREEN_WIDTH = 320;
const int SCREEN_HEIGHT = 200;
const int MAX_SPRITES = 32;
-const int MAX_MENUBUTTONS = 32;
-
-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];
- uint32 disabledButtons;
- SharedPtr<FileStream> menuFile;
- uint16 numButtons;
- int16 selectedButton;
- SharedPtr<Menu> nextMenu;
-};
-
-class Graphics;
-typedef String (Graphics::*TextGetterFunc)(int, uintptr, String *);
class Graphics {
@@ -119,65 +100,8 @@ private:
Sprite *_sprites[MAX_SPRITES];
int _numSprites;
- SharedPtr<Bitmap> _mouseBitmap;
-
-
- // text.cpp (TODO: separate class)
public:
- int showText(TextGetterFunc textGetter, uintptr var, int xoffset, int yoffset, int textColor, bool loopChoices, int maxTextLines, int arg10);
-
- String readTextFromRdf(int choiceIndex, uintptr data, String *headerTextOutput);
- String readTextFromBuffer(int choiceIndex, uintptr data, String *headerTextOutput);
- String readTextFromArray(int choiceIndex, uintptr data, String *headerTextOutput);
-
-private:
- int handleMenuEvents(uint32 ticksUntilClickingEnabled, bool arg4);
-
- SharedPtr<TextBitmap> initTextSprite(int *xoffsetPtr, int *yoffsetPtr, byte textColor, int numTextLines, bool withHeader, Sprite *sprite);
- void drawMainText(SharedPtr<TextBitmap> bitmap, int numTextLines, int numTextboxLines, const String &text, bool withHeader);
-
- int getNumLines(const String &str);
- void getTextboxHeader(String *headerTextOutput, String speakerText, int choiceIndex);
-
- String readLineFormattedText(TextGetterFunc textGetter, uintptr var, int choiceIndex, SharedPtr<TextBitmap> textBitmap, int numTextboxLines, int *numLines);
- String putTextIntoLines(const String &text);
- const char *getNextTextLine(const char *text, char *line, int lineWidth);
-
- String skipTextAudioPrompt(const String &str);
- String playTextAudio(const String &str);
-
- int getMenuButtonAt(const Menu &menu, int x, int y);
- void drawMenuButtonOutline(SharedPtr<Bitmap> bitmap, byte color);
- void loadMenuButtons(String mnuFilename, int xpos, int ypos);
- void unloadMenuButtons();
- void disableMenuButtons(uint32 bits);
- void enableMenuButtons(uint32 bits);
- void setVisibleMenuButtons(uint32 bits);
-
-public:
- void choseMousePositionFromSprites(Sprite *sprites, int numSprites, int spriteIndex, int mode);
- void showOptionsMenu(int x, int y);
- void showSaveMenu();
- void showLoadMenu();
- void showQuitGamePrompt(int x, int y);
- void showTextConfigurationMenu(bool fromOptionMenu);
-
- int loadTextDisplayMode();
- void saveTextDisplayMode(int value);
-
-private:
- int16 _textDisplayMode;
-
-public:
- uint32 _textboxVar2;
- uint16 _textboxVar6;
- bool _textboxHasMultipleChoices;
-
-private:
- SharedPtr<Menu> _activeMenu;
-
- // Saved value of StarTrekEngine::_keyboardControlsMouse when menus are up
- bool _keyboardControlsMouseOutsideMenu;
+ SharedPtr<Bitmap> _mouseBitmap;
};
}
diff --git a/engines/startrek/menu.cpp b/engines/startrek/menu.cpp
new file mode 100644
index 0000000000..f10e784a35
--- /dev/null
+++ b/engines/startrek/menu.cpp
@@ -0,0 +1,569 @@
+/* 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/events.h"
+#include "common/stream.h"
+#include "graphics/cursorman.h"
+
+#include "startrek/graphics.h"
+
+namespace StarTrek {
+
+/**
+ * Returns the index of the button at the given position, or -1 if none.
+ */
+int StarTrekEngine::getMenuButtonAt(const Menu &menu, int x, int y) {
+ for (int i = 0; i < menu.numButtons; i++) {
+ const Sprite &spr = menu.sprites[i];
+
+ if (spr.drawMode != 2)
+ continue;
+
+ int left = spr.pos.x - spr.bitmap->xoffset;
+ 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;
+}
+
+/**
+ * This chooses a sprite from the list to place the mouse cursor at. The sprite it chooses
+ * may be, for example, the top-leftmost one in the list. Exact behaviour is determined by
+ * the "mode" parameter.
+ *
+ * If "containMouseSprite" is a valid index, it's ensured that the mouse is contained
+ * within it. "mode" should be -1 in this case.
+ */
+void StarTrekEngine::chooseMousePositionFromSprites(Sprite *sprites, int numSprites, int containMouseSprite, int mode) {
+ uint16 mouseX1 = 0x7fff; // Candidate positions to warp mouse to
+ uint16 mouseY1 = 0x7fff;
+ uint16 mouseX2 = 0x7fff;
+ uint16 mouseY2 = 0x7fff;
+
+ Common::Point mousePos = _gfx->getMousePos();
+
+ // Ensure the cursor is contained within one of the sprites
+ if (containMouseSprite >= 0 && containMouseSprite < numSprites) {
+ Common::Rect rect = sprites[containMouseSprite].getRect();
+
+ if (mousePos.x < rect.left || mousePos.x >= rect.right
+ || mousePos.y < rect.top || mousePos.y >= rect.bottom) {
+ mousePos.x = (rect.left + rect.right) / 2;
+ mousePos.y = (rect.top + rect.bottom) / 2;
+ }
+ }
+
+ // Choose a sprite to warp the cursor to
+ for (int i = 0; i < numSprites; i++) {
+ Sprite *sprite = &sprites[i];
+ if (sprite->drawMode != 2) // Skip hidden buttons
+ continue;
+
+ Common::Rect rect = sprite->getRect();
+
+ int hCenter = (rect.left + rect.right) / 2;
+ int vCenter = (rect.top + rect.bottom) / 2;
+
+ // Choose which sprite is closest based on certain criteria?
+ switch(mode) {
+ case 0: // Choose topmost, leftmost sprite that's below the cursor
+ if (((vCenter == mousePos.y && hCenter > mousePos.x) || vCenter > mousePos.y)
+ && (vCenter < mouseY1 || (vCenter == mouseY1 && hCenter < mouseX1))) {
+ mouseX1 = hCenter;
+ mouseY1 = vCenter;
+ }
+ // fall through
+
+ case 4: // Choose topmost, leftmost sprite
+ if (vCenter < mouseY2 || (vCenter == mouseY2 && hCenter < mouseX2)) {
+ mouseX2 = hCenter;
+ mouseY2 = vCenter;
+ }
+ break;
+
+ case 1: // Choose bottommost, rightmost sprite that's above the cursor
+ if (((vCenter == mousePos.y && hCenter < mousePos.x) || vCenter < mousePos.y)
+ && (mouseY1 == 0x7fff || vCenter > mouseY1
+ || (vCenter == mouseY1 && hCenter > mouseX1))) {
+ mouseX1 = hCenter;
+ mouseY1 = vCenter;
+ }
+ // fall through
+
+ case 5: // Choose bottommost, rightmost sprite
+ if (mouseY2 == 0x7fff || vCenter > mouseY2
+ || (vCenter == mouseY2 && hCenter > mouseX2)) {
+ mouseX2 = hCenter;
+ mouseY2 = vCenter;
+ }
+ break;
+
+ case 2:
+ // This seems broken... OR condition on first line has no affect on the logic...
+ if ((vCenter < mousePos.y || (vCenter == mouseY1 && hCenter == mousePos.x))
+ && (mouseX1 == 0x7fff || vCenter >= mouseY1)) {
+ mouseX1 = hCenter;
+ mouseY1 = vCenter;
+ debug("Try %d %d", mouseX1, mouseY1);
+ }
+ if (mouseX2 == 0x7fff || vCenter > mouseY2
+ || (hCenter == mouseX2 && vCenter == mouseY2)) {
+ mouseX2 = hCenter;
+ mouseY2 = vCenter;
+ }
+ break;
+
+ case 3:
+ // Similar to above...
+ if ((vCenter > mousePos.y || (vCenter == mouseY1 && hCenter == mousePos.x))
+ && (mouseX1 == 0x7fff || vCenter <= mouseY1)) {
+ mouseX1 = hCenter;
+ mouseY1 = vCenter;
+ }
+ if (mouseX2 == 0x7fff || vCenter < mouseY2
+ || (hCenter == mouseX2 && vCenter == mouseY2)) {
+ mouseX2 = hCenter;
+ mouseY2 = vCenter;
+ }
+ break;
+ }
+ }
+
+ // Warp mouse to one of the coordinates, if one is valid
+ if (mouseX1 != 0x7fff) {
+ mousePos.x = mouseX1;
+ mousePos.y = mouseY1;
+ }
+ else if (mouseX2 != 0x7fff) {
+ mousePos.x = mouseX2;
+ mousePos.y = mouseY2;
+ }
+
+ _system->warpMouse(mousePos.x, mousePos.y);
+
+}
+
+/**
+ * Draws or removes the outline on menu buttons when the cursor hovers on them, or leaves
+ * them.
+ */
+void StarTrekEngine::drawMenuButtonOutline(SharedPtr<Bitmap> 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 StarTrekEngine::showOptionsMenu(int x, int y) {
+ bool tmpMouseControllingShip = _mouseControllingShip;
+ _mouseControllingShip = false;
+
+ Common::Point oldMousePos = _gfx->getMousePos();
+ SharedPtr<Bitmap> oldMouseBitmap = _gfx->_mouseBitmap;
+
+ _gfx->setMouseCursor(_gfx->loadBitmap("options"));
+ loadMenuButtons("options", x, y);
+
+ uint32 disabledButtons = 0;
+ if (_musicWorking) {
+ if (_musicEnabled)
+ disabledButtons |= (1 << OPTIONBUTTON_ENABLEMUSIC);
+ else
+ disabledButtons |= (1 << OPTIONBUTTON_DISABLEMUSIC);
+ }
+ else
+ disabledButtons |= (1 << OPTIONBUTTON_ENABLEMUSIC) | (1 << OPTIONBUTTON_DISABLEMUSIC);
+
+ if (_sfxWorking) {
+ if (_sfxEnabled)
+ disabledButtons |= (1 << OPTIONBUTTON_ENABLESFX);
+ else
+ disabledButtons |= (1 << OPTIONBUTTON_DISABLESFX);
+ }
+ else
+ disabledButtons |= (1 << OPTIONBUTTON_ENABLESFX) | (1 << OPTIONBUTTON_DISABLESFX);
+
+ disableMenuButtons(disabledButtons);
+ chooseMousePositionFromSprites(_activeMenu->sprites, _activeMenu->numButtons, -1, 4);
+ int event = handleMenuEvents(0, false);
+
+ unloadMenuButtons();
+ _mouseControllingShip = tmpMouseControllingShip;
+ _gfx->setMouseCursor(oldMouseBitmap);
+
+ if (event != MENUEVENT_LCLICK_OFFBUTTON && event != MENUEVENT_RCLICK_OFFBUTTON)
+ _system->warpMouse(oldMousePos.x, oldMousePos.y);
+
+
+ // Can't use OPTIONBUTTON constants since the button retvals differ from the button
+ // indices...
+ switch(event) {
+ case 0: // Save
+ showSaveMenu();
+ break;
+ case 1: // Load
+ showLoadMenu();
+ break;
+ case 2: // Enable music
+ _sound->setMusicEnabled(true);
+ break;
+ case 3: // Disable music
+ _sound->setMusicEnabled(false);
+ break;
+ case 4: // Enable sfx
+ _sound->setSfxEnabled(true);
+ break;
+ case 5: // Disable sfx
+ _sound->setSfxEnabled(false);
+ break;
+ case 6: // Quit
+ showQuitGamePrompt(20, 20);
+ break;
+ case 7: // Text
+ showTextConfigurationMenu(true);
+ break;
+ default:
+ break;
+ }
+}
+
+/**
+ * Loads a .MNU file, which is a list of buttons to display.
+ */
+void StarTrekEngine::loadMenuButtons(String mnuFilename, int xpos, int ypos) {
+ if (_activeMenu == nullptr)
+ _keyboardControlsMouseOutsideMenu = _keyboardControlsMouse;
+
+ SharedPtr<Menu> oldMenu = _activeMenu;
+ _activeMenu = SharedPtr<Menu>(new Menu());
+ _activeMenu->nextMenu = oldMenu;
+
+ SharedPtr<FileStream> stream = loadFile(mnuFilename + ".MNU");
+
+ _activeMenu->menuFile = stream;
+ _activeMenu->numButtons = _activeMenu->menuFile->size() / 16;
+
+ for (int i = 0; i < _activeMenu->numButtons; i++) {
+ memset(&_activeMenu->sprites[i], 0, sizeof(Sprite));
+ _gfx->addSprite(&_activeMenu->sprites[i]);
+ _activeMenu->sprites[i].drawMode = 2;
+
+ char bitmapBasename[11];
+ stream->seek(i * 16, SEEK_SET);
+ stream->read(bitmapBasename, 10);
+ for (int j = 0; j < 10; j++) {
+ if (bitmapBasename[j] == ' ')
+ bitmapBasename[j] = '\0';
+ }
+ bitmapBasename[10] = '\0';
+
+ _activeMenu->sprites[i].bitmap = _gfx->loadBitmap(bitmapBasename);
+ _activeMenu->sprites[i].pos.x = stream->readUint16() + xpos;
+ _activeMenu->sprites[i].pos.y = stream->readUint16() + ypos;
+ _activeMenu->retvals[i] = stream->readUint16();
+
+ _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;
+ _keyboardControlsMouse = false;
+}
+
+/**
+ * Sets which buttons are visible based on the given bitmask.
+ */
+void StarTrekEngine::setVisibleMenuButtons(uint32 bits) {
+ for (int i = 0; i < _activeMenu->numButtons; i++) {
+ Sprite *sprite = &_activeMenu->sprites[i];
+ uint32 spriteBitmask = (1 << i);
+ if (spriteBitmask == 0)
+ break;
+
+ if ((bits & spriteBitmask) == 0 || sprite->drawMode != 0) {
+ if ((bits & spriteBitmask) == 0 && sprite->drawMode == 2) {
+ if (i == _activeMenu->selectedButton) {
+ drawMenuButtonOutline(sprite->bitmap, 0x00);
+ _activeMenu->selectedButton = -1;
+ }
+
+ sprite->field16 = true;
+ sprite->bitmapChanged = true;
+ }
+ }
+ else {
+ _gfx->addSprite(sprite);
+ sprite->drawMode = 2;
+ sprite->bitmapChanged = true;
+ }
+ }
+
+ _gfx->drawAllSprites();
+
+ for (int i = 0; i < _activeMenu->numButtons; i++) {
+ Sprite *sprite = &_activeMenu->sprites[i];
+ uint32 spriteBitmask = (1 << i);
+ if (spriteBitmask == 0)
+ break;
+
+ if ((bits & spriteBitmask) == 0 && sprite->drawMode == 2) {
+ _gfx->delSprite(sprite);
+
+ // Setting drawMode to 0 is the game's way of saying that the menu button is
+ // hidden (since it would normally be 2).
+ sprite->drawMode = 0;
+ }
+ }
+}
+
+/**
+ * Disables the given bitmask of buttons.
+ */
+void StarTrekEngine::disableMenuButtons(uint32 bits) {
+ _activeMenu->disabledButtons |= bits;
+ if (_activeMenu->selectedButton != -1
+ && (_activeMenu->disabledButtons & (1 << _activeMenu->selectedButton))) {
+ Sprite *sprite = &_activeMenu->sprites[_activeMenu->selectedButton];
+ drawMenuButtonOutline(sprite->bitmap, 0x00);
+
+ sprite->bitmapChanged = true;
+ _activeMenu->selectedButton = -1;
+ }
+}
+
+void StarTrekEngine::enableMenuButtons(uint32 bits) {
+ _activeMenu->disabledButtons &= ~bits;
+}
+
+/**
+ * This returns either a special menu event (negative number) or the retval of the button
+ * clicked (usually an index, always positive).
+ */
+int StarTrekEngine::handleMenuEvents(uint32 ticksUntilClickingEnabled, bool arg4) {
+ // TODO: finish
+
+ uint32 tickWhenClickingEnabled = _clockTicks + ticksUntilClickingEnabled;
+
+ while (true) {
+ TrekEvent event;
+ while (popNextEvent(&event)) {
+ switch(event.type) {
+
+ case TREKEVENT_TICK: {
+ case TREKEVENT_MOUSEMOVE: // FIXME: actual game only uses TICK event here
+ Common::Point mousePos = _gfx->getMousePos();
+ int buttonIndex = getMenuButtonAt(*_activeMenu, mousePos.x, mousePos.y);
+ if (buttonIndex != -1) {
+ if (_activeMenu->disabledButtons & (1<<buttonIndex))
+ buttonIndex = -1;
+ }
+
+ if (buttonIndex != _activeMenu->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();
+ _gfx->drawAllSprites();
+ // sub_10BE7();
+ // sub_2A4B1();
+
+ if (_finishedPlayingSpeech != 0) {
+ _finishedPlayingSpeech = 0;
+ if (_textDisplayMode != TEXTDISPLAY_WAIT) {
+ return TEXTBUTTON_SPEECH_DONE;
+ }
+ }
+ // sub_1E88C();
+ _frameIndex++;
+
+ if (ticksUntilClickingEnabled != 0 && _clockTicks >= tickWhenClickingEnabled)
+ return MENUEVENT_ENABLEINPUT;
+ break;
+ }
+
+ case TREKEVENT_LBUTTONDOWN:
+ if (_activeMenu->selectedButton != -1) {
+ playSoundEffectIndex(0x10);
+ return _activeMenu->retvals[_activeMenu->selectedButton];
+ }
+ else {
+ Common::Point mouse = _gfx->getMousePos();
+ if (getMenuButtonAt(*_activeMenu, mouse.x, mouse.y) == -1) {
+ playSoundEffectIndex(0x10);
+ return MENUEVENT_LCLICK_OFFBUTTON;
+ }
+ }
+ break;
+
+ case TREKEVENT_RBUTTONDOWN:
+ // TODO
+ break;
+
+ case TREKEVENT_KEYDOWN:
+ // TODO
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+}
+
+void StarTrekEngine::unloadMenuButtons() {
+ if (_activeMenu->selectedButton != -1)
+ drawMenuButtonOutline(_activeMenu->sprites[_activeMenu->selectedButton].bitmap, 0x00);
+
+ for (int i = 0; i < _activeMenu->numButtons; i++) {
+ Sprite *sprite = &_activeMenu->sprites[i];
+ if (sprite->drawMode == 2) {
+ sprite->field16 = true;
+ sprite->bitmapChanged = true;
+ }
+ }
+
+ _gfx->drawAllSprites();
+
+ for (int i = 0; i < _activeMenu->numButtons; i++) {
+ Sprite *sprite = &_activeMenu->sprites[i];
+ sprite->bitmap.reset();
+ if (sprite->drawMode == 2)
+ _gfx->delSprite(sprite);
+ }
+
+ _activeMenu = _activeMenu->nextMenu;
+
+ if (_activeMenu == nullptr)
+ _keyboardControlsMouse = _keyboardControlsMouseOutsideMenu;
+}
+
+void StarTrekEngine::showSaveMenu() {
+ // TODO
+}
+
+void StarTrekEngine::showLoadMenu() {
+ // TODO
+}
+
+void StarTrekEngine::showQuitGamePrompt(int x, int y) {
+ const char *options[] = {
+ "Quit Game",
+ "#GENE\\GENER028#Yes, quit the game.",
+ "#GENE\\GENER008#No, do not quit the game.",
+ ""
+ };
+
+ if (_inQuitGameMenu)
+ return;
+
+ _inQuitGameMenu = true;
+ int val = showText(&StarTrekEngine::readTextFromArray, (uintptr)options, x, y, 0xb0, true, 0, 1);
+ _inQuitGameMenu = false;
+
+ if (val == 0) {
+ // sub_1e70d();
+ _system->quit();
+ }
+}
+
+/**
+ * This can be called from startup or from the options menu.
+ * On startup, this tries to load the setting without user input.
+ */
+void StarTrekEngine::showTextConfigurationMenu(bool fromOptionMenu) {
+ const char *options[] = { // TODO: languages...
+ "Text display",
+ "Text subtitles.",
+ "Display text until you press enter.",
+ "No text displayed.",
+ ""
+ };
+
+ int val;
+ if (fromOptionMenu || (val = loadTextDisplayMode()) == -1) {
+ val = showText(&StarTrekEngine::readTextFromArray, (uintptr)options, 20, 30, 0xb0, true, 0, 1);
+ saveTextDisplayMode(val);
+ }
+
+ switch(val) {
+ case 0:
+ _textDisplayMode = TEXTDISPLAY_SUBTITLES;
+ break;
+ case 1:
+ _textDisplayMode = TEXTDISPLAY_WAIT;
+ break;
+ case 2:
+ _textDisplayMode = TEXTDISPLAY_NONE;
+ break;
+ }
+}
+
+int StarTrekEngine::loadTextDisplayMode() {
+ return -1; // TODO
+}
+void StarTrekEngine::saveTextDisplayMode(int value) {
+ // TODO;
+}
+
+}
diff --git a/engines/startrek/module.mk b/engines/startrek/module.mk
index 04f2c7c53a..cdaa44449e 100644
--- a/engines/startrek/module.mk
+++ b/engines/startrek/module.mk
@@ -8,9 +8,10 @@ MODULE_OBJS = \
events.o \
filestream.o \
font.o \
+ graphics.o \
iwfile.o \
lzss.o \
- graphics.o \
+ menu.o \
object.o \
room.o \
sound.o \
diff --git a/engines/startrek/startrek.cpp b/engines/startrek/startrek.cpp
index 82fe2e1a77..61c78bd989 100644
--- a/engines/startrek/startrek.cpp
+++ b/engines/startrek/startrek.cpp
@@ -68,6 +68,10 @@ StarTrekEngine::StarTrekEngine(OSystem *syst, const StarTrekGameDescription *gam
_keyboardControlsMouse = true;
_inQuitGameMenu = false;
+ _textDisplayMode = TEXTDISPLAY_WAIT;
+ _textboxVar2 = 0;
+ _textboxVar6 = 0;
+ _textboxHasMultipleChoices = false;
_missionToLoad = "DEMON";
_roomIndexToLoad = 0;
diff --git a/engines/startrek/startrek.h b/engines/startrek/startrek.h
index 7560efa8d7..8031bcb557 100644
--- a/engines/startrek/startrek.h
+++ b/engines/startrek/startrek.h
@@ -44,6 +44,7 @@
using Common::SharedPtr;
+using Common::String;
namespace Common {
class MacResManager;
@@ -51,6 +52,16 @@ namespace Common {
namespace StarTrek {
+class StarTrekEngine;
+
+typedef String (StarTrekEngine::*TextGetterFunc)(int, uintptr, String *);
+
+const int MAX_OBJECTS = 32;
+const int MAX_MENUBUTTONS = 32;
+const int TEXTBOX_WIDTH = 26;
+const int MAX_TEXTBOX_LINES = 12;
+
+
enum StarTrekGameType {
GType_ST25 = 1,
GType_STJR = 2
@@ -65,7 +76,6 @@ enum kDebugLevels {
kDebugGraphics = 1 << 1
};
-
enum GameMode {
GAMEMODE_START = 0,
GAMEMODE_BRIDGE,
@@ -80,6 +90,52 @@ enum TextDisplayMode {
TEXTDISPLAY_NONE // No text displayed
};
+// Keeps track of data for a list of buttons making up a menu
+struct Menu {
+ Sprite sprites[MAX_MENUBUTTONS];
+ uint16 retvals[MAX_MENUBUTTONS];
+ uint32 disabledButtons;
+ SharedPtr<FileStream> menuFile;
+ uint16 numButtons;
+ int16 selectedButton;
+ SharedPtr<Menu> nextMenu;
+};
+
+// Special events that can be returned by handleMenuEvents.
+enum MenuEvent {
+ MENUEVENT_RCLICK_OFFBUTTON = -4,
+ MENUEVENT_ENABLEINPUT, // Makes buttons selectable (occurs after a delay)
+ MENUEVENT_RCLICK_ONBUTTON,
+ MENUEVENT_LCLICK_OFFBUTTON
+};
+
+// Buttons for standard text display
+enum TextButtons {
+ TEXTBUTTON_CONFIRM = 0,
+ TEXTBUTTON_SCROLLUP,
+ TEXTBUTTON_SCROLLDOWN,
+ TEXTBUTTON_PREVCHOICE,
+ TEXTBUTTON_NEXTCHOICE,
+ TEXTBUTTON_SCROLLUP_ONELINE,
+ TEXTBUTTON_SCROLLDOWN_ONELINE,
+ TEXTBUTTON_GOTO_TOP,
+ TEXTBUTTON_GOTO_BOTTOM,
+ TEXTBUTTON_SPEECH_DONE // "Virtual" button?
+};
+
+// Buttons for option menu (corresponding to button indices, not button retvals, which are
+// different for some reason)
+enum OptionMenuButtons {
+ OPTIONBUTTON_TEXT,
+ OPTIONBUTTON_SAVE,
+ OPTIONBUTTON_LOAD,
+ OPTIONBUTTON_ENABLEMUSIC,
+ OPTIONBUTTON_DISABLEMUSIC,
+ OPTIONBUTTON_ENABLESFX,
+ OPTIONBUTTON_DISABLESFX,
+ OPTIONBUTTON_QUIT
+};
+
enum TrekEventType {
TREKEVENT_TICK = 0, // DOS clock changes (see updateClockTicks)
TREKEVENT_LBUTTONDOWN = 1,
@@ -116,7 +172,6 @@ struct Command {
: type(_type), b1(_b1), b2(_b2), b3(_b3) {}
};
-const int MAX_OBJECTS = 0x20;
struct StarTrekGameDescription;
class Graphics;
@@ -207,8 +262,63 @@ private:
bool _tickEventInQueue;
uint32 _frameStartMillis;
+
+ // text.cpp
public:
+ const char *getNextTextLine(const char *text, char *line, int lineWidth);
+
+ void getTextboxHeader(String *headerTextOutput, String speakerText, int choiceIndex);
+ String readTextFromRdf(int choiceIndex, uintptr data, String *headerTextOutput);
+ String readTextFromBuffer(int choiceIndex, uintptr data, String *headerTextOutput);
+
+ String skipTextAudioPrompt(const String &str);
+ String playTextAudio(const String &str);
+
+ int showText(TextGetterFunc textGetter, uintptr var, int xoffset, int yoffset, int textColor, bool loopChoices, int maxTextLines, int arg10);
+
+ int getNumTextboxLines(const String &str);
+ String putTextIntoLines(const String &text);
+
+ SharedPtr<TextBitmap> initTextSprite(int *xoffsetPtr, int *yoffsetPtr, byte textColor, int numTextLines, bool withHeader, Sprite *sprite);
+ void drawMainText(SharedPtr<TextBitmap> bitmap, int numTextLines, int numTextboxLines, const String &text, bool withHeader);
+
+ String readLineFormattedText(TextGetterFunc textGetter, uintptr var, int choiceIndex, SharedPtr<TextBitmap> textBitmap, int numTextboxLines, int *numLines);
+
+ String readTextFromArray(int choiceIndex, uintptr data, String *headerTextOutput);
+
+ // menu.cpp
+public:
+ int getMenuButtonAt(const Menu &menu, int x, int y);
+ void chooseMousePositionFromSprites(Sprite *sprites, int numSprites, int spriteIndex, int mode);
+ void drawMenuButtonOutline(SharedPtr<Bitmap> bitmap, byte color);
+ void showOptionsMenu(int x, int y);
+ void loadMenuButtons(String mnuFilename, int xpos, int ypos);
+ void setVisibleMenuButtons(uint32 bits);
+ void disableMenuButtons(uint32 bits);
+ void enableMenuButtons(uint32 bits);
+ int handleMenuEvents(uint32 ticksUntilClickingEnabled, bool arg4);
+ void unloadMenuButtons();
+
+ void showSaveMenu();
+ void showLoadMenu();
+ void showQuitGamePrompt(int x, int y);
+ void showTextConfigurationMenu(bool fromOptionMenu);
+
+ int loadTextDisplayMode();
+ void saveTextDisplayMode(int value);
+
+
+private:
+ int16 _textDisplayMode;
+ uint32 _textboxVar2;
+ uint16 _textboxVar6;
+ bool _textboxHasMultipleChoices;
+ SharedPtr<Menu> _activeMenu;
+ // Saved value of StarTrekEngine::_keyboardControlsMouse when menus are up
+ bool _keyboardControlsMouseOutsideMenu;
+
// Detection related functions
+public:
const StarTrekGameDescription *_gameDescription;
uint32 getFeatures() const;
uint16 getVersion() const;
diff --git a/engines/startrek/text.cpp b/engines/startrek/text.cpp
index 945ff31468..fed047edcc 100644
--- a/engines/startrek/text.cpp
+++ b/engines/startrek/text.cpp
@@ -27,48 +27,168 @@
#include "startrek/graphics.h"
-// Special events that can be returned by handleMenuEvents.
-enum MenuEvent {
- MENUEVENT_RCLICK_OFFBUTTON = -4,
- MENUEVENT_ENABLEINPUT, // Makes buttons selectable (occurs after a delay)
- MENUEVENT_RCLICK_ONBUTTON,
- MENUEVENT_LCLICK_OFFBUTTON
-};
-
-// Buttons for standard text display
-enum TextButtons {
- TEXTBUTTON_CONFIRM = 0,
- TEXTBUTTON_SCROLLUP,
- TEXTBUTTON_SCROLLDOWN,
- TEXTBUTTON_PREVCHOICE,
- TEXTBUTTON_NEXTCHOICE,
- TEXTBUTTON_SCROLLUP_ONELINE,
- TEXTBUTTON_SCROLLDOWN_ONELINE,
- TEXTBUTTON_GOTO_TOP,
- TEXTBUTTON_GOTO_BOTTOM,
- TEXTBUTTON_SPEECH_DONE // "Virtual" button?
-};
-
-// Buttons for option menu (corresponding to button indices, not button retvals, which are
-// different for some reason)
-enum OptionMenuButtons {
- OPTIONBUTTON_TEXT,
- OPTIONBUTTON_SAVE,
- OPTIONBUTTON_LOAD,
- OPTIONBUTTON_ENABLEMUSIC,
- OPTIONBUTTON_DISABLEMUSIC,
- OPTIONBUTTON_ENABLESFX,
- OPTIONBUTTON_DISABLESFX,
- OPTIONBUTTON_QUIT
-};
-
namespace StarTrek {
-int Graphics::showText(TextGetterFunc textGetter, uintptr var, int xoffset, int yoffset, int textColor, bool loopChoices, int maxTextLines, int arg10) {
+/**
+ * Gets one line of text (does not include words that won't fit).
+ * Returns position of text to continue from, or nullptr if done.
+ */
+const char *StarTrekEngine::getNextTextLine(const char *text, char *lineOutput, int lineWidth) {
+ *lineOutput = '\0';
+ if (*text == '\0')
+ return nullptr;
+
+ const char *lastSpaceInput = nullptr;
+ char *lastSpaceOutput = nullptr;
+ int var4;
+ int charIndex = 0;
+
+ while (charIndex != lineWidth && *text != '\0') {
+ char c = *text;
+
+ if (c == '\n') {
+ *lineOutput = '\0';
+ return text+1;
+ }
+
+ if (c == ' ') {
+ var4 = charIndex;
+ lastSpaceInput = text;
+ lastSpaceOutput = lineOutput;
+ }
+
+ if (c == '\r') {
+ text++;
+ charIndex--;
+ }
+ else {
+ text++;
+ *(lineOutput++) = c;
+ }
+ charIndex++;
+ }
+
+ if (*text == '\0') {
+ *lineOutput = '\0';
+ return text;
+ }
+ if (*text == ' ') {
+ *lineOutput = '\0';
+ return text+1;
+ }
+ if (lastSpaceOutput == nullptr) { // Long word couldn't fit on line
+ *lineOutput = '\0';
+ return text;
+ }
+
+ // In the middle of a word; must go back to the start of it
+ *lastSpaceOutput = '\0';
+ return lastSpaceInput+1;
+}
+
+void StarTrekEngine::getTextboxHeader(String *headerTextOutput, String speakerText, int choiceIndex) {
+ String header = speakerText;
+
+ if (choiceIndex != 0)
+ header += String::format(" choice %d", choiceIndex);
+
+ if (header.size() > TEXTBOX_WIDTH-2)
+ header.erase(TEXTBOX_WIDTH-2);
+ while (header.size() < TEXTBOX_WIDTH-2)
+ header += ' ';
+
+ *headerTextOutput = header;
+}
+
+/**
+ * Text getter for showText which reads from an rdf file.
+ */
+String StarTrekEngine::readTextFromRdf(int choiceIndex, uintptr data, String *headerTextOutput) {
+ SharedPtr<Room> room = getRoom();
+
+ int rdfVar = (size_t)data;
+
+ uint16 textOffset = room->readRdfWord(rdfVar + (choiceIndex+1)*2);
+
+ if (textOffset == 0)
+ return "";
+
+ if (headerTextOutput != nullptr) {
+ uint16 speakerOffset = room->readRdfWord(rdfVar);
+ if (speakerOffset == 0 || room->_rdfData[speakerOffset] == '\0')
+ *headerTextOutput = "";
+ else {
+ char *speakerText = (char*)&room->_rdfData[speakerOffset];
+ if (room->readRdfWord(rdfVar+4) != 0) // Check if there's more than one option
+ getTextboxHeader(headerTextOutput, speakerText, choiceIndex+1);
+ else
+ getTextboxHeader(headerTextOutput, speakerText, 0);
+ }
+ }
+
+ return (char*)&room->_rdfData[textOffset];
+}
+
+/**
+ * Text getter for showText which reads from a given buffer.
+ */
+String StarTrekEngine::readTextFromBuffer(int choiceIndex, uintptr data, String *headerTextOutput) {
+ char buf[TEXTBOX_WIDTH];
+ memcpy(buf, (byte*)data, TEXTBOX_WIDTH-2);
+ buf[TEXTBOX_WIDTH-2] = '\0';
+
+ *headerTextOutput = String(buf);
+
+ char *text = (char*)data+TEXTBOX_WIDTH-2;
+ return String(text);
+}
+
+String StarTrekEngine::skipTextAudioPrompt(const String &str) {
+ const char *text = str.c_str();
+
+ if (*text != '#')
+ return str;
+
+ text++;
+ while (*text != '#') {
+ if (*text == '\0')
+ return str;
+ text++;
+ }
+
+ return String(text+1);
+}
+
+/**
+ * Plays an audio prompt, if it exists, and returns the string starting at the end of the
+ * prompt.
+ */
+String StarTrekEngine::playTextAudio(const String &str) {
+ const char *text = str.c_str();
+ char soundFile[0x100];
+
+ if (*text != '#')
+ return str;
+
+ int len = 0;
+ text++;
+ while (*text != '#') {
+ if (*text == '\0' || len > 0xfa)
+ return str;
+ soundFile[len++] = *text++;
+ }
+
+ soundFile[len] = '\0';
+ playSpeech(soundFile);
+
+ return String(text+1);
+}
+
+int StarTrekEngine::showText(TextGetterFunc textGetter, uintptr var, int xoffset, int yoffset, int textColor, bool loopChoices, int maxTextLines, int arg10) {
int16 tmpTextDisplayMode = _textDisplayMode;
uint32 var7c = 8;
- if (_vm->_frameIndex > _textboxVar2+1) {
+ if (_frameIndex > _textboxVar2+1) {
var7c = 0x10;
}
@@ -82,7 +202,7 @@ int Graphics::showText(TextGetterFunc textGetter, uintptr var, int xoffset, int
if (choiceText.empty())
break;
- int lines = getNumLines(choiceText);
+ int lines = getNumTextboxLines(choiceText);
if (lines > numTextboxLines)
numTextboxLines = lines;
@@ -106,13 +226,13 @@ int Graphics::showText(TextGetterFunc textGetter, uintptr var, int xoffset, int
int choiceIndex = 0;
int scrollOffset = 0;
if (tmpTextDisplayMode != TEXTDISPLAY_WAIT && tmpTextDisplayMode != TEXTDISPLAY_SUBTITLES
- && numChoices == 1 && _vm->_sfxEnabled && !_vm->_sfxWorking)
+ && numChoices == 1 && _sfxEnabled && !_sfxWorking)
_textboxHasMultipleChoices = false;
else
_textboxHasMultipleChoices = true;
if (tmpTextDisplayMode >= TEXTDISPLAY_WAIT && tmpTextDisplayMode <= TEXTDISPLAY_NONE
- && _vm->_sfxEnabled && !_vm->_sfxWorking)
+ && _sfxEnabled && !_sfxWorking)
_textboxVar6 = true;
else
_textboxVar6 = false;
@@ -126,14 +246,14 @@ int Graphics::showText(TextGetterFunc textGetter, uintptr var, int xoffset, int
else {
loadMenuButtons("textbtns", xoffset + 0x96, yoffset - 0x11);
- Common::Point oldMousePos = getMousePos();
- SharedPtr<Bitmap> oldMouseBitmap = _mouseBitmap;
+ Common::Point oldMousePos = _gfx->getMousePos();
+ SharedPtr<Bitmap> oldMouseBitmap = _gfx->_mouseBitmap;
- _vm->_system->warpMouse(xoffset + 0xde, yoffset - 0x08);
- setMouseCursor(loadBitmap("pushbtn"));
+ _system->warpMouse(xoffset + 0xde, yoffset - 0x08);
+ _gfx->setMouseCursor(_gfx->loadBitmap("pushbtn"));
- bool tmpMouseControllingShip = _vm->_mouseControllingShip;
- _vm->_mouseControllingShip = false;
+ bool tmpMouseControllingShip = _mouseControllingShip;
+ _mouseControllingShip = false;
// Decide which buttons to show
uint32 visibleButtons = (1 << TEXTBUTTON_CONFIRM);
@@ -278,177 +398,66 @@ reloadText:
}
}
- setMouseCursor(oldMouseBitmap);
- _vm->_system->warpMouse(oldMousePos.x, oldMousePos.y);
+ _gfx->setMouseCursor(oldMouseBitmap);
+ _system->warpMouse(oldMousePos.x, oldMousePos.y);
- _vm->_mouseControllingShip = tmpMouseControllingShip;
+ _mouseControllingShip = tmpMouseControllingShip;
unloadMenuButtons();
textboxSprite.field16 = true;
textboxSprite.bitmapChanged = true;
- drawAllSprites();
- delSprite(&textboxSprite);
+ _gfx->drawAllSprites();
+ _gfx->delSprite(&textboxSprite);
// sub_272B4
}
- _textboxVar2 = _vm->_frameIndex;
- _vm->stopPlayingSpeech();
+ _textboxVar2 = _frameIndex;
+ stopPlayingSpeech();
return choiceIndex;
}
/**
- * This returns either a special menu event (negative number) or the retval of the button
- * clicked (usually an index, always positive).
+ * Returns the number of lines this string will take up in a textbox.
*/
-int Graphics::handleMenuEvents(uint32 ticksUntilClickingEnabled, bool arg4) {
- // TODO: finish
-
- uint32 tickWhenClickingEnabled = _vm->_clockTicks + ticksUntilClickingEnabled;
-
- while (true) {
- 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<<buttonIndex))
- buttonIndex = -1;
- }
-
- if (buttonIndex != _activeMenu->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 (_vm->_finishedPlayingSpeech != 0) {
- _vm->_finishedPlayingSpeech = 0;
- if (_textDisplayMode != TEXTDISPLAY_WAIT) {
- return TEXTBUTTON_SPEECH_DONE;
- }
- }
- // sub_1E88C();
- _vm->_frameIndex++;
-
- if (ticksUntilClickingEnabled != 0 && _vm->_clockTicks >= tickWhenClickingEnabled)
- return MENUEVENT_ENABLEINPUT;
- break;
- }
-
- case TREKEVENT_LBUTTONDOWN:
- if (_activeMenu->selectedButton != -1) {
- _vm->playSoundEffectIndex(0x10);
- return _activeMenu->retvals[_activeMenu->selectedButton];
- }
- else {
- Common::Point mouse = getMousePos();
- if (getMenuButtonAt(*_activeMenu, mouse.x, mouse.y) == -1) {
- _vm->playSoundEffectIndex(0x10);
- return MENUEVENT_LCLICK_OFFBUTTON;
- }
- }
- break;
-
- case TREKEVENT_RBUTTONDOWN:
- // TODO
- break;
+int StarTrekEngine::getNumTextboxLines(const String &str) {
+ const char *text = str.c_str();
+ char line[TEXTBOX_WIDTH];
- case TREKEVENT_KEYDOWN:
- // TODO
- break;
+ int lines = 0;
- default:
- break;
- }
- }
+ while (text != nullptr) {
+ text = getNextTextLine(text, line, TEXTBOX_WIDTH-2);
+ lines++;
}
+ return lines-1;
}
-/**
- * Text getter for showText which reads from an rdf file.
- */
-String Graphics::readTextFromRdf(int choiceIndex, uintptr data, String *headerTextOutput) {
- SharedPtr<Room> room = _vm->getRoom();
-
- int rdfVar = (size_t)data;
+String StarTrekEngine::putTextIntoLines(const String &_text) {
+ char line[TEXTBOX_WIDTH];
- uint16 textOffset = room->readRdfWord(rdfVar + (choiceIndex+1)*2);
+ const char *text = _text.c_str();
+ String output;
- if (textOffset == 0)
- return "";
+ text = getNextTextLine(text, line, TEXTBOX_WIDTH-2);
- if (headerTextOutput != nullptr) {
- uint16 speakerOffset = room->readRdfWord(rdfVar);
- if (speakerOffset == 0 || room->_rdfData[speakerOffset] == '\0')
- *headerTextOutput = "";
- else {
- char *speakerText = (char*)&room->_rdfData[speakerOffset];
- if (room->readRdfWord(rdfVar+4) != 0) // Check if there's more than one option
- getTextboxHeader(headerTextOutput, speakerText, choiceIndex+1);
- else
- getTextboxHeader(headerTextOutput, speakerText, 0);
+ while (text != nullptr) {
+ int len = strlen(line);
+ while (len != TEXTBOX_WIDTH-2) {
+ line[len++] = ' ';
+ line[len] = '\0';
}
- }
-
- return (char*)&room->_rdfData[textOffset];
-}
-
-/**
- * Text getter for showText which reads from a given buffer.
- */
-String Graphics::readTextFromBuffer(int choiceIndex, uintptr data, String *headerTextOutput) {
- char buf[TEXTBOX_WIDTH];
- memcpy(buf, (byte*)data, TEXTBOX_WIDTH-2);
- buf[TEXTBOX_WIDTH-2] = '\0';
-
- *headerTextOutput = String(buf);
-
- char *text = (char*)data+TEXTBOX_WIDTH-2;
- return String(text);
-}
-
-/**
- * Text getter for showText which reads choices from an array of pointers.
- * Last element in the array must be an empty string.
- */
-String Graphics::readTextFromArray(int choiceIndex, uintptr data, String *headerTextOutput) {
- const char **textArray = (const char**)data;
-
- const char *headerText = textArray[0];
- const char *mainText = textArray[choiceIndex+1];
+ output += line;
- if (*mainText == '\0')
- return Common::String(); // Technically should be nullptr...
+ text = getNextTextLine(text, line, TEXTBOX_WIDTH-2);
+ }
- *headerTextOutput = headerText;
- while (headerTextOutput->size() < TEXTBOX_WIDTH-2)
- *headerTextOutput += ' ';
- return String(mainText);
+ return output;
}
/**
* Creates a blank textbox in a TextBitmap, and initializes a sprite to use it.
*/
-SharedPtr<TextBitmap> Graphics::initTextSprite(int *xoffsetPtr, int *yoffsetPtr, byte textColor, int numTextLines, bool withHeader, Sprite *sprite) {
+SharedPtr<TextBitmap> StarTrekEngine::initTextSprite(int *xoffsetPtr, int *yoffsetPtr, byte textColor, int numTextLines, bool withHeader, Sprite *sprite) {
int linesBeforeTextStart = 2;
if (withHeader)
linesBeforeTextStart = 4;
@@ -510,7 +519,7 @@ SharedPtr<TextBitmap> Graphics::initTextSprite(int *xoffsetPtr, int *yoffsetPtr,
memset(&textAddr[1], 0x16, TEXTBOX_WIDTH-2);
textAddr[TEXTBOX_WIDTH-1] = 0x17;
- addSprite(sprite);
+ _gfx->addSprite(sprite);
sprite->drawMode = 3;
sprite->pos.x = xoffset;
sprite->pos.y = yoffset;
@@ -526,7 +535,7 @@ SharedPtr<TextBitmap> Graphics::initTextSprite(int *xoffsetPtr, int *yoffsetPtr,
* Draws the "main" text (everything but the header which includes the speaker) to
* a TextBitmap.
*/
-void Graphics::drawMainText(SharedPtr<TextBitmap> bitmap, int numTextLines, int numTextboxLines, const String &_text, bool withHeader) {
+void StarTrekEngine::drawMainText(SharedPtr<TextBitmap> bitmap, int numTextLines, int numTextboxLines, const String &_text, bool withHeader) {
byte *dest = bitmap->pixels + TEXTBOX_WIDTH + 1; // Start of 2nd row
const char *text = _text.c_str();
@@ -552,48 +561,18 @@ void Graphics::drawMainText(SharedPtr<TextBitmap> bitmap, int numTextLines, int
}
}
-/**
- * Returns the number of lines this string will take up in a textbox.
- */
-int Graphics::getNumLines(const String &str) {
- const char *text = str.c_str();
- char line[TEXTBOX_WIDTH];
-
- int lines = 0;
-
- while (text != nullptr) {
- text = getNextTextLine(text, line, TEXTBOX_WIDTH-2);
- lines++;
- }
- return lines-1;
-}
-
-void Graphics::getTextboxHeader(String *headerTextOutput, String speakerText, int choiceIndex) {
- String header = speakerText;
-
- if (choiceIndex != 0)
- header += String::format(" choice %d", choiceIndex);
-
- if (header.size() > TEXTBOX_WIDTH-2)
- header.erase(TEXTBOX_WIDTH-2);
- while (header.size() < TEXTBOX_WIDTH-2)
- header += ' ';
-
- *headerTextOutput = header;
-}
-
-String Graphics::readLineFormattedText(TextGetterFunc textGetter, uintptr var, int choiceIndex, SharedPtr<TextBitmap> textBitmap, int numTextboxLines, int *numTextLines) {
+String StarTrekEngine::readLineFormattedText(TextGetterFunc textGetter, uintptr var, int choiceIndex, SharedPtr<TextBitmap> textBitmap, int numTextboxLines, int *numTextLines) {
String headerText;
String text = (this->*textGetter)(choiceIndex, var, &headerText);
- if (_textDisplayMode == TEXTDISPLAY_NONE && _vm->_sfxEnabled && _vm->_sfxWorking) {
+ if (_textDisplayMode == TEXTDISPLAY_NONE && _sfxEnabled && _sfxWorking) {
uint32 oldSize = text.size();
text = playTextAudio(text);
if (oldSize != text.size())
_textboxHasMultipleChoices = true;
}
else if ((_textDisplayMode == TEXTDISPLAY_WAIT || _textDisplayMode == TEXTDISPLAY_SUBTITLES)
- && _vm->_sfxEnabled && _vm->_sfxWorking) {
+ && _sfxEnabled && _sfxWorking) {
text = playTextAudio(text);
}
else {
@@ -601,7 +580,7 @@ String Graphics::readLineFormattedText(TextGetterFunc textGetter, uintptr var, i
}
if (_textboxHasMultipleChoices) {
- *numTextLines = getNumLines(text);
+ *numTextLines = getNumTextboxLines(text);
bool hasHeader = !headerText.empty();
@@ -635,575 +614,23 @@ String Graphics::readLineFormattedText(TextGetterFunc textGetter, uintptr var, i
*/
}
-String Graphics::putTextIntoLines(const String &_text) {
- char line[TEXTBOX_WIDTH];
-
- const char *text = _text.c_str();
- String output;
-
- text = getNextTextLine(text, line, TEXTBOX_WIDTH-2);
-
- while (text != nullptr) {
- int len = strlen(line);
- while (len != TEXTBOX_WIDTH-2) {
- line[len++] = ' ';
- line[len] = '\0';
- }
- output += line;
-
- text = getNextTextLine(text, line, TEXTBOX_WIDTH-2);
- }
-
- return output;
-}
-
-/**
- * Gets one line of text (does not include words that won't fit).
- * Returns position of text to continue from, or nullptr if done.
- */
-const char *Graphics::getNextTextLine(const char *text, char *lineOutput, int lineWidth) {
- *lineOutput = '\0';
- if (*text == '\0')
- return nullptr;
-
- const char *lastSpaceInput = nullptr;
- char *lastSpaceOutput = nullptr;
- int var4;
- int charIndex = 0;
-
- while (charIndex != lineWidth && *text != '\0') {
- char c = *text;
-
- if (c == '\n') {
- *lineOutput = '\0';
- return text+1;
- }
-
- if (c == ' ') {
- var4 = charIndex;
- lastSpaceInput = text;
- lastSpaceOutput = lineOutput;
- }
-
- if (c == '\r') {
- text++;
- charIndex--;
- }
- else {
- text++;
- *(lineOutput++) = c;
- }
- charIndex++;
- }
-
- if (*text == '\0') {
- *lineOutput = '\0';
- return text;
- }
- if (*text == ' ') {
- *lineOutput = '\0';
- return text+1;
- }
- if (lastSpaceOutput == nullptr) { // Long word couldn't fit on line
- *lineOutput = '\0';
- return text;
- }
-
- // In the middle of a word; must go back to the start of it
- *lastSpaceOutput = '\0';
- return lastSpaceInput+1;
-}
-
-String Graphics::skipTextAudioPrompt(const String &str) {
- const char *text = str.c_str();
-
- if (*text != '#')
- return str;
-
- text++;
- while (*text != '#') {
- if (*text == '\0')
- return str;
- text++;
- }
-
- return String(text+1);
-}
-
-/**
- * Plays an audio prompt, if it exists, and returns the string starting at the end of the
- * prompt.
- */
-String Graphics::playTextAudio(const String &str) {
- const char *text = str.c_str();
- char soundFile[0x100];
-
- if (*text != '#')
- return str;
-
- int len = 0;
- text++;
- while (*text != '#') {
- if (*text == '\0' || len > 0xfa)
- return str;
- soundFile[len++] = *text++;
- }
-
- soundFile[len] = '\0';
- _vm->playSpeech(soundFile);
-
- return String(text+1);
-}
-
-/**
- * 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; i < menu.numButtons; i++) {
- const Sprite &spr = menu.sprites[i];
-
- if (spr.drawMode != 2)
- continue;
-
- int left = spr.pos.x - spr.bitmap->xoffset;
- 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> 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;
- }
-}
-
-/**
- * Loads a .MNU file, which is a list of buttons to display.
- */
-void Graphics::loadMenuButtons(String mnuFilename, int xpos, int ypos) {
- if (_activeMenu == nullptr)
- _keyboardControlsMouseOutsideMenu = _vm->_keyboardControlsMouse;
-
- SharedPtr<Menu> oldMenu = _activeMenu;
- _activeMenu = SharedPtr<Menu>(new Menu());
- _activeMenu->nextMenu = oldMenu;
-
- SharedPtr<FileStream> stream = _vm->loadFile(mnuFilename + ".MNU");
-
- _activeMenu->menuFile = stream;
- _activeMenu->numButtons = _activeMenu->menuFile->size() / 16;
-
- for (int i = 0; i < _activeMenu->numButtons; i++) {
- memset(&_activeMenu->sprites[i], 0, sizeof(Sprite));
- addSprite(&_activeMenu->sprites[i]);
- _activeMenu->sprites[i].drawMode = 2;
-
- char bitmapBasename[11];
- stream->seek(i * 16, SEEK_SET);
- stream->read(bitmapBasename, 10);
- for (int j = 0; j < 10; j++) {
- if (bitmapBasename[j] == ' ')
- bitmapBasename[j] = '\0';
- }
- bitmapBasename[10] = '\0';
-
- _activeMenu->sprites[i].bitmap = loadBitmap(bitmapBasename);
- _activeMenu->sprites[i].pos.x = stream->readUint16() + xpos;
- _activeMenu->sprites[i].pos.y = stream->readUint16() + ypos;
- _activeMenu->retvals[i] = stream->readUint16();
-
- _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;
- _vm->_keyboardControlsMouse = false;
-}
-
-void Graphics::unloadMenuButtons() {
- if (_activeMenu->selectedButton != -1)
- drawMenuButtonOutline(_activeMenu->sprites[_activeMenu->selectedButton].bitmap, 0x00);
-
- for (int i = 0; i < _activeMenu->numButtons; i++) {
- Sprite *sprite = &_activeMenu->sprites[i];
- if (sprite->drawMode == 2) {
- sprite->field16 = true;
- sprite->bitmapChanged = true;
- }
- }
-
- drawAllSprites();
-
- for (int i = 0; i < _activeMenu->numButtons; i++) {
- Sprite *sprite = &_activeMenu->sprites[i];
- sprite->bitmap.reset();
- if (sprite->drawMode == 2)
- delSprite(sprite);
- }
-
- _activeMenu = _activeMenu->nextMenu;
-
- if (_activeMenu == nullptr)
- _vm->_keyboardControlsMouse = _keyboardControlsMouseOutsideMenu;
-}
-
/**
- * Disables the given bitmask of buttons.
- */
-void Graphics::disableMenuButtons(uint32 bits) {
- _activeMenu->disabledButtons |= bits;
- if (_activeMenu->selectedButton != -1
- && (_activeMenu->disabledButtons & (1 << _activeMenu->selectedButton))) {
- Sprite *sprite = &_activeMenu->sprites[_activeMenu->selectedButton];
- drawMenuButtonOutline(sprite->bitmap, 0x00);
-
- sprite->bitmapChanged = true;
- _activeMenu->selectedButton = -1;
- }
-}
-
-void Graphics::enableMenuButtons(uint32 bits) {
- _activeMenu->disabledButtons &= ~bits;
-}
-
-/**
- * Sets which buttons are visible based on the given bitmask.
- */
-void Graphics::setVisibleMenuButtons(uint32 bits) {
- for (int i = 0; i < _activeMenu->numButtons; i++) {
- Sprite *sprite = &_activeMenu->sprites[i];
- uint32 spriteBitmask = (1 << i);
- if (spriteBitmask == 0)
- break;
-
- if ((bits & spriteBitmask) == 0 || sprite->drawMode != 0) {
- if ((bits & spriteBitmask) == 0 && sprite->drawMode == 2) {
- if (i == _activeMenu->selectedButton) {
- drawMenuButtonOutline(sprite->bitmap, 0x00);
- _activeMenu->selectedButton = -1;
- }
-
- sprite->field16 = true;
- sprite->bitmapChanged = true;
- }
- }
- else {
- addSprite(sprite);
- sprite->drawMode = 2;
- sprite->bitmapChanged = true;
- }
- }
-
- drawAllSprites();
-
- for (int i = 0; i < _activeMenu->numButtons; i++) {
- Sprite *sprite = &_activeMenu->sprites[i];
- uint32 spriteBitmask = (1 << i);
- if (spriteBitmask == 0)
- break;
-
- if ((bits & spriteBitmask) == 0 && sprite->drawMode == 2) {
- delSprite(sprite);
-
- // Setting drawMode to 0 is the game's way of saying that the menu button is
- // hidden (since it would normally be 2).
- sprite->drawMode = 0;
- }
- }
-}
-
-/**
- * This chooses a sprite from the list to place the mouse cursor at. The sprite it chooses
- * may be, for example, the top-leftmost one in the list. Exact behaviour is determined by
- * the "mode" parameter.
- *
- * If "containMouseSprite" is a valid index, it's ensured that the mouse is contained
- * within it. "mode" should be -1 in this case.
+ * Text getter for showText which reads choices from an array of pointers.
+ * Last element in the array must be an empty string.
*/
-void Graphics::choseMousePositionFromSprites(Sprite *sprites, int numSprites, int containMouseSprite, int mode) {
- uint16 mouseX1 = 0x7fff; // Candidate positions to warp mouse to
- uint16 mouseY1 = 0x7fff;
- uint16 mouseX2 = 0x7fff;
- uint16 mouseY2 = 0x7fff;
-
- Common::Point mousePos = getMousePos();
-
- // Ensure the cursor is contained within one of the sprites
- if (containMouseSprite >= 0 && containMouseSprite < numSprites) {
- Common::Rect rect = sprites[containMouseSprite].getRect();
-
- if (mousePos.x < rect.left || mousePos.x >= rect.right
- || mousePos.y < rect.top || mousePos.y >= rect.bottom) {
- mousePos.x = (rect.left + rect.right) / 2;
- mousePos.y = (rect.top + rect.bottom) / 2;
- }
- }
-
- // Choose a sprite to warp the cursor to
- for (int i = 0; i < numSprites; i++) {
- Sprite *sprite = &sprites[i];
- if (sprite->drawMode != 2) // Skip hidden buttons
- continue;
-
- Common::Rect rect = sprite->getRect();
-
- int hCenter = (rect.left + rect.right) / 2;
- int vCenter = (rect.top + rect.bottom) / 2;
-
- // Choose which sprite is closest based on certain criteria?
- switch(mode) {
- case 0: // Choose topmost, leftmost sprite that's below the cursor
- if (((vCenter == mousePos.y && hCenter > mousePos.x) || vCenter > mousePos.y)
- && (vCenter < mouseY1 || (vCenter == mouseY1 && hCenter < mouseX1))) {
- mouseX1 = hCenter;
- mouseY1 = vCenter;
- }
- // fall through
-
- case 4: // Choose topmost, leftmost sprite
- if (vCenter < mouseY2 || (vCenter == mouseY2 && hCenter < mouseX2)) {
- mouseX2 = hCenter;
- mouseY2 = vCenter;
- }
- break;
-
- case 1: // Choose bottommost, rightmost sprite that's above the cursor
- if (((vCenter == mousePos.y && hCenter < mousePos.x) || vCenter < mousePos.y)
- && (mouseY1 == 0x7fff || vCenter > mouseY1
- || (vCenter == mouseY1 && hCenter > mouseX1))) {
- mouseX1 = hCenter;
- mouseY1 = vCenter;
- }
- // fall through
-
- case 5: // Choose bottommost, rightmost sprite
- if (mouseY2 == 0x7fff || vCenter > mouseY2
- || (vCenter == mouseY2 && hCenter > mouseX2)) {
- mouseX2 = hCenter;
- mouseY2 = vCenter;
- }
- break;
-
- case 2:
- // This seems broken... OR condition on first line has no affect on the logic...
- if ((vCenter < mousePos.y || (vCenter == mouseY1 && hCenter == mousePos.x))
- && (mouseX1 == 0x7fff || vCenter >= mouseY1)) {
- mouseX1 = hCenter;
- mouseY1 = vCenter;
- debug("Try %d %d", mouseX1, mouseY1);
- }
- if (mouseX2 == 0x7fff || vCenter > mouseY2
- || (hCenter == mouseX2 && vCenter == mouseY2)) {
- mouseX2 = hCenter;
- mouseY2 = vCenter;
- }
- break;
-
- case 3:
- // Similar to above...
- if ((vCenter > mousePos.y || (vCenter == mouseY1 && hCenter == mousePos.x))
- && (mouseX1 == 0x7fff || vCenter <= mouseY1)) {
- mouseX1 = hCenter;
- mouseY1 = vCenter;
- }
- if (mouseX2 == 0x7fff || vCenter < mouseY2
- || (hCenter == mouseX2 && vCenter == mouseY2)) {
- mouseX2 = hCenter;
- mouseY2 = vCenter;
- }
- break;
- }
- }
-
- // Warp mouse to one of the coordinates, if one is valid
- if (mouseX1 != 0x7fff) {
- mousePos.x = mouseX1;
- mousePos.y = mouseY1;
- }
- else if (mouseX2 != 0x7fff) {
- mousePos.x = mouseX2;
- mousePos.y = mouseY2;
- }
-
- _vm->_system->warpMouse(mousePos.x, mousePos.y);
-
-}
-
-void Graphics::showOptionsMenu(int x, int y) {
- bool tmpMouseControllingShip = _vm->_mouseControllingShip;
- _vm->_mouseControllingShip = false;
-
- Common::Point oldMousePos = getMousePos();
- SharedPtr<Bitmap> oldMouseBitmap = _mouseBitmap;
-
- setMouseCursor(loadBitmap("options"));
- loadMenuButtons("options", x, y);
-
- uint32 disabledButtons = 0;
- if (_vm->_musicWorking) {
- if (_vm->_musicEnabled)
- disabledButtons |= (1 << OPTIONBUTTON_ENABLEMUSIC);
- else
- disabledButtons |= (1 << OPTIONBUTTON_DISABLEMUSIC);
- }
- else
- disabledButtons |= (1 << OPTIONBUTTON_ENABLEMUSIC) | (1 << OPTIONBUTTON_DISABLEMUSIC);
-
- if (_vm->_sfxWorking) {
- if (_vm->_sfxEnabled)
- disabledButtons |= (1 << OPTIONBUTTON_ENABLESFX);
- else
- disabledButtons |= (1 << OPTIONBUTTON_DISABLESFX);
- }
- else
- disabledButtons |= (1 << OPTIONBUTTON_ENABLESFX) | (1 << OPTIONBUTTON_DISABLESFX);
-
- disableMenuButtons(disabledButtons);
- choseMousePositionFromSprites(_activeMenu->sprites, _activeMenu->numButtons, -1, 4);
- int event = handleMenuEvents(0, false);
-
- unloadMenuButtons();
- _vm->_mouseControllingShip = tmpMouseControllingShip;
- setMouseCursor(oldMouseBitmap);
-
- if (event != MENUEVENT_LCLICK_OFFBUTTON && event != MENUEVENT_RCLICK_OFFBUTTON)
- _vm->_system->warpMouse(oldMousePos.x, oldMousePos.y);
-
-
- // Can't use OPTIONBUTTON constants since the button retvals differ from the button
- // indices...
- switch(event) {
- case 0: // Save
- showSaveMenu();
- break;
- case 1: // Load
- showLoadMenu();
- break;
- case 2: // Enable music
- _vm->_sound->setMusicEnabled(true);
- break;
- case 3: // Disable music
- _vm->_sound->setMusicEnabled(false);
- break;
- case 4: // Enable sfx
- _vm->_sound->setSfxEnabled(true);
- break;
- case 5: // Disable sfx
- _vm->_sound->setSfxEnabled(false);
- break;
- case 6: // Quit
- showQuitGamePrompt(20, 20);
- break;
- case 7: // Text
- showTextConfigurationMenu(true);
- break;
- default:
- break;
- }
-}
-
-void Graphics::showSaveMenu() {
- // TODO
-}
-
-void Graphics::showLoadMenu() {
- // TODO
-}
-
-void Graphics::showQuitGamePrompt(int x, int y) {
- const char *options[] = {
- "Quit Game",
- "#GENE\\GENER028#Yes, quit the game.",
- "#GENE\\GENER008#No, do not quit the game.",
- ""
- };
-
- if (_vm->_inQuitGameMenu)
- return;
-
- _vm->_inQuitGameMenu = true;
- int val = showText(&Graphics::readTextFromArray, (uintptr)options, x, y, 0xb0, true, 0, 1);
- _vm->_inQuitGameMenu = false;
-
- if (val == 0) {
- // sub_1e70d();
- _vm->_system->quit();
- }
-}
+String StarTrekEngine::readTextFromArray(int choiceIndex, uintptr data, String *headerTextOutput) {
+ const char **textArray = (const char**)data;
-/**
- * This can be called from startup or from the options menu.
- * On startup, this tries to load the setting without user input.
- */
-void Graphics::showTextConfigurationMenu(bool fromOptionMenu) {
- const char *options[] = { // TODO: languages...
- "Text display",
- "Text subtitles.",
- "Display text until you press enter.",
- "No text displayed.",
- ""
- };
-
- int val;
- if (fromOptionMenu || (val = loadTextDisplayMode()) == -1) {
- val = showText(&Graphics::readTextFromArray, (uintptr)options, 20, 30, 0xb0, true, 0, 1);
- saveTextDisplayMode(val);
- }
+ const char *headerText = textArray[0];
+ const char *mainText = textArray[choiceIndex+1];
- switch(val) {
- case 0:
- _textDisplayMode = TEXTDISPLAY_SUBTITLES;
- break;
- case 1:
- _textDisplayMode = TEXTDISPLAY_WAIT;
- break;
- case 2:
- _textDisplayMode = TEXTDISPLAY_NONE;
- break;
- }
-}
+ if (*mainText == '\0')
+ return Common::String(); // Technically should be nullptr...
-int Graphics::loadTextDisplayMode() {
- return -1; // TODO
-}
-void Graphics::saveTextDisplayMode(int value) {
- // TODO;
+ *headerTextOutput = headerText;
+ while (headerTextOutput->size() < TEXTBOX_WIDTH-2)
+ *headerTextOutput += ' ';
+ return String(mainText);
}
}