scummvm/engines/kyra/gui.cpp
2009-10-06 17:22:40 +00:00

679 lines
19 KiB
C++

/* 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.
*
* $URL$
* $Id$
*
*/
#include "kyra/gui.h"
#include "kyra/screen.h"
#include "kyra/text.h"
#include "kyra/wsamovie.h"
#include "common/savefile.h"
namespace Kyra {
GUI::GUI(KyraEngine_v1 *kyra)
: _vm(kyra), _screen(kyra->screen()), _text(kyra->text()) {
_menuButtonList = 0;
_redrawButtonFunctor = BUTTON_FUNCTOR(GUI, this, &GUI::redrawButtonCallback);
_redrawShadedButtonFunctor = BUTTON_FUNCTOR(GUI, this, &GUI::redrawShadedButtonCallback);
}
Button *GUI::addButtonToList(Button *list, Button *newButton) {
if (!newButton)
return list;
newButton->nextButton = 0;
if (list) {
Button *cur = list;
while (cur->nextButton)
cur = cur->nextButton;
cur->nextButton = newButton;
} else {
list = newButton;
}
return list;
}
void GUI::initMenuLayout(Menu &menu) {
if (menu.x == -1)
menu.x = (320 - menu.width) >> 1;
if (menu.y == -1)
menu.y = (200 - menu.height) >> 1;
for (int i = 0; i < menu.numberOfItems; ++i) {
if (menu.item[i].x == -1)
menu.item[i].x = (menu.width - menu.item[i].width) >> 1;
}
}
void GUI::initMenu(Menu &menu) {
_menuButtonList = 0;
_screen->hideMouse();
int textX;
int textY;
int menu_x2 = menu.width + menu.x - 1;
int menu_y2 = menu.height + menu.y - 1;
_screen->fillRect(menu.x + 2, menu.y + 2, menu_x2 - 2, menu_y2 - 2, menu.bkgdColor);
_screen->drawShadedBox(menu.x, menu.y, menu_x2, menu_y2, menu.color1, menu.color2);
if (menu.titleX != -1)
textX = menu.titleX;
else
textX = getMenuCenterStringX(getMenuTitle(menu), menu.x, menu_x2);
textY = menu.y + menu.titleY;
if (_vm->gameFlags().gameID == GI_LOL) {
printMenuText(getMenuTitle(menu), textX, textY, menu.textColor, 0, 9);
} else {
if (_vm->gameFlags().platform != Common::kPlatformAmiga)
printMenuText(getMenuTitle(menu), textX - 1, textY + 1, defaultColor1(), defaultColor2(), 0);
printMenuText(getMenuTitle(menu), textX, textY, menu.textColor, 0, 0);
}
int x1, y1, x2, y2;
for (int i = 0; i < menu.numberOfItems; ++i) {
if (!menu.item[i].enabled)
continue;
x1 = menu.x + menu.item[i].x;
y1 = menu.y + menu.item[i].y;
x2 = x1 + menu.item[i].width - 1;
y2 = y1 + menu.item[i].height - 1;
if (i < 7) {
Button *menuButtonData = getButtonListData() + i;
menuButtonData->nextButton = 0;
menuButtonData->x = x1;
menuButtonData->y = y1;
menuButtonData->width = menu.item[i].width - 1;
menuButtonData->height = menu.item[i].height - 1;
menuButtonData->buttonCallback = menu.item[i].callback;
menuButtonData->keyCode = menu.item[i].keyCode;
menuButtonData->keyCode2 = 0;
menuButtonData->arg = menu.item[i].itemId;
_menuButtonList = addButtonToList(_menuButtonList, menuButtonData);
}
_screen->fillRect(x1, y1, x2, y2, menu.item[i].bkgdColor);
_screen->drawShadedBox(x1, y1, x2, y2, menu.item[i].color1, menu.item[i].color2);
if (getMenuItemTitle(menu.item[i])) {
if (menu.item[i].titleX != -1)
textX = x1 + menu.item[i].titleX + 3;
else
textX = getMenuCenterStringX(getMenuItemTitle(menu.item[i]), x1, x2);
textY = y1 + 2;
if (_vm->gameFlags().gameID == GI_LOL) {
textY++;
if (i == menu.highlightedItem)
printMenuText(getMenuItemTitle(menu.item[i]), textX, textY, menu.item[i].highlightColor, 0, 8);
else
printMenuText(getMenuItemTitle(menu.item[i]), textX, textY, menu.item[i].textColor, 0, 8);
} else {
Screen::FontId of = _screen->_currentFont;
if (_vm->_flags.gameID != GI_LOL && menu.item[i].saveSlot > 0)
_screen->setFont(Screen::FID_8_FNT);
if (_vm->gameFlags().platform != Common::kPlatformAmiga)
printMenuText(getMenuItemTitle(menu.item[i]), textX - 1, textY + 1, defaultColor1(), 0, 0, Screen::FID_8_FNT);
if (i == menu.highlightedItem)
printMenuText(getMenuItemTitle(menu.item[i]), textX, textY, menu.item[i].highlightColor, 0, 0, Screen::FID_8_FNT);
else
printMenuText(getMenuItemTitle(menu.item[i]), textX, textY, menu.item[i].textColor, 0, 0, Screen::FID_8_FNT);
_screen->setFont(of);
}
}
}
for (int i = 0; i < menu.numberOfItems; ++i) {
if (getMenuItemLabel(menu.item[i])) {
if (_vm->gameFlags().gameID == GI_LOL) {
menu.item[i].labelX = menu.item[i].x - 1;
menu.item[i].labelY = menu.item[i].y + 3;
printMenuText(getMenuItemLabel(menu.item[i]), menu.x + menu.item[i].labelX, menu.y + menu.item[i].labelY, menu.item[i].textColor, 0, 10);
} else {
if (_vm->gameFlags().platform != Common::kPlatformAmiga)
printMenuText(getMenuItemLabel(menu.item[i]), menu.x + menu.item[i].labelX - 1, menu.y + menu.item[i].labelY + 1, defaultColor1(), 0, 0);
printMenuText(getMenuItemLabel(menu.item[i]), menu.x + menu.item[i].labelX, menu.y + menu.item[i].labelY, menu.item[i].textColor, 0, 0);
}
}
}
if (menu.scrollUpButtonX != -1) {
Button *scrollUpButton = getScrollUpButton();
scrollUpButton->x = menu.scrollUpButtonX + menu.x;
scrollUpButton->y = menu.scrollUpButtonY + menu.y;
scrollUpButton->buttonCallback = getScrollUpButtonHandler();
scrollUpButton->nextButton = 0;
scrollUpButton->mouseWheel = -1;
_menuButtonList = addButtonToList(_menuButtonList, scrollUpButton);
updateMenuButton(scrollUpButton);
Button *scrollDownButton = getScrollDownButton();
scrollDownButton->x = menu.scrollDownButtonX + menu.x;
scrollDownButton->y = menu.scrollDownButtonY + menu.y;
scrollDownButton->buttonCallback = getScrollDownButtonHandler();
scrollDownButton->nextButton = 0;
scrollDownButton->mouseWheel = 1;
_menuButtonList = addButtonToList(_menuButtonList, scrollDownButton);
updateMenuButton(scrollDownButton);
}
_screen->showMouse();
_screen->updateScreen();
}
void GUI::processHighlights(Menu &menu) {
int x1, y1, x2, y2;
Common::Point p = _vm->getMousePos();
int mouseX = p.x;
int mouseY = p.y;
if (_vm->_flags.gameID == GI_LOL && menu.highlightedItem != 255) {
// LoL doesnt't have default highlighted items.
// We use a highlightedItem value of 255 for this.
// With LoL no highlighting should take place unless the
// mouse cursor moves over a button. The highlighting should end
// when the mouse cursor leaves the button.
if (menu.item[menu.highlightedItem].enabled)
redrawText(menu);
}
for (int i = 0; i < menu.numberOfItems; ++i) {
if (!menu.item[i].enabled)
continue;
x1 = menu.x + menu.item[i].x;
y1 = menu.y + menu.item[i].y;
x2 = x1 + menu.item[i].width;
y2 = y1 + menu.item[i].height;
if (mouseX > x1 && mouseX < x2 &&
mouseY > y1 && mouseY < y2) {
if (menu.highlightedItem != i || _vm->_flags.gameID == GI_LOL) {
if (_vm->_flags.gameID != GI_LOL) {
if (menu.item[menu.highlightedItem].enabled)
redrawText(menu);
}
menu.highlightedItem = i;
redrawHighlight(menu);
}
}
}
_screen->updateScreen();
}
void GUI::redrawText(const Menu &menu) {
int textX;
int i = menu.highlightedItem;
int x1 = menu.x + menu.item[i].x;
int y1 = menu.y + menu.item[i].y;
int x2 = x1 + menu.item[i].width - 1;
if (menu.item[i].titleX >= 0)
textX = x1 + menu.item[i].titleX + 3;
else
textX = getMenuCenterStringX(getMenuItemTitle(menu.item[i]), x1, x2);
int textY = y1 + 2;
if (_vm->gameFlags().gameID == GI_LOL) {
textY++;
printMenuText(getMenuItemTitle(menu.item[i]), textX, textY, menu.item[i].textColor, 0, 8);
} else {
Screen::FontId of = _screen->_currentFont;
if (menu.item[i].saveSlot > 0)
_screen->setFont(Screen::FID_8_FNT);
if (_vm->gameFlags().platform != Common::kPlatformAmiga)
printMenuText(getMenuItemTitle(menu.item[i]), textX - 1, textY + 1, defaultColor1(), 0, 0);
printMenuText(getMenuItemTitle(menu.item[i]), textX, textY, menu.item[i].textColor, 0, 0);
_screen->setFont(of);
}
}
void GUI::redrawHighlight(const Menu &menu) {
int textX;
int i = menu.highlightedItem;
int x1 = menu.x + menu.item[i].x;
int y1 = menu.y + menu.item[i].y;
int x2 = x1 + menu.item[i].width - 1;
if (menu.item[i].titleX != -1)
textX = x1 + menu.item[i].titleX + 3;
else
textX = getMenuCenterStringX(getMenuItemTitle(menu.item[i]), x1, x2);
int textY = y1 + 2;
if (_vm->gameFlags().gameID == GI_LOL) {
textY++;
printMenuText(getMenuItemTitle(menu.item[i]), textX, textY, menu.item[i].highlightColor, 0, 8);
} else {
Screen::FontId of = _screen->_currentFont;
if (menu.item[i].saveSlot > 0)
_screen->setFont(Screen::FID_8_FNT);
if (_vm->gameFlags().platform != Common::kPlatformAmiga)
printMenuText(getMenuItemTitle(menu.item[i]), textX - 1, textY + 1, defaultColor1(), 0, 0);
printMenuText(getMenuItemTitle(menu.item[i]), textX, textY, menu.item[i].highlightColor, 0, 0);
_screen->setFont(of);
}
}
void GUI::updateAllMenuButtons() {
for (Button *cur = _menuButtonList; cur; cur = cur->nextButton)
updateMenuButton(cur);
}
void GUI::updateMenuButton(Button *button) {
if (!_displayMenu)
return;
_screen->hideMouse();
updateButton(button);
_screen->updateScreen();
_screen->showMouse();
}
void GUI::updateButton(Button *button) {
if (!button || (button->flags & 8))
return;
if (button->flags2 & 1)
button->flags2 &= 0xFFF7;
else
button->flags2 |= 8;
button->flags2 &= 0xFFFC;
if (button->flags2 & 4)
button->flags2 |= 0x10;
else
button->flags2 &= 0xEEEF;
button->flags2 &= 0xFFFB;
processButton(button);
}
int GUI::redrawButtonCallback(Button *button) {
if (!_displayMenu)
return 0;
_screen->hideMouse();
if (_vm->gameFlags().platform == Common::kPlatformAmiga)
_screen->drawBox(button->x + 1, button->y + 1, button->x + button->width - 1, button->y + button->height - 1, 17);
else
_screen->drawBox(button->x + 1, button->y + 1, button->x + button->width - 1, button->y + button->height - 1, 0xF8);
_screen->showMouse();
return 0;
}
int GUI::redrawShadedButtonCallback(Button *button) {
if (!_displayMenu)
return 0;
_screen->hideMouse();
if (_vm->gameFlags().platform == Common::kPlatformAmiga)
_screen->drawShadedBox(button->x, button->y, button->x + button->width, button->y + button->height, 31, 18);
else
_screen->drawShadedBox(button->x, button->y, button->x + button->width, button->y + button->height, 0xF9, 0xFA);
_screen->showMouse();
return 0;
}
void GUI::updateSaveList(bool excludeQuickSaves) {
Common::String pattern = _vm->_targetName + ".???";
Common::StringList saveFileList = _vm->_saveFileMan->listSavefiles(pattern);
_saveSlots.clear();
for (Common::StringList::const_iterator i = saveFileList.begin(); i != saveFileList.end(); ++i) {
char s1 = 0, s2 = 0, s3 = 0;
s1 = (*i)[i->size()-3];
s2 = (*i)[i->size()-2];
s3 = (*i)[i->size()-1];
if (!isdigit(s1) || !isdigit(s2) || !isdigit(s3))
continue;
s1 -= '0';
s2 -= '0';
s3 -= '0';
if (excludeQuickSaves && s1 == 9 && s2 == 9)
continue;
_saveSlots.push_back(s1*100+s2*10+s3);
}
if (_saveSlots.begin() == _saveSlots.end())
return;
Common::sort(_saveSlots.begin(), _saveSlots.end(), Common::Less<int>());
if (_saveSlots.size() > 2)
Common::sort(_saveSlots.begin()+1, _saveSlots.end(), Common::Greater<int>());
}
int GUI::getNextSavegameSlot() {
Common::InSaveFile *in;
int start = _vm->gameFlags().gameID == GI_LOL ? 0 : 1;
for (int i = start; i < 990; i++) {
if ((in = _vm->_saveFileMan->openForLoading(_vm->getSavegameFilename(i))))
delete in;
else
return i;
}
warning("Didn't save: Ran out of saveGame filenames");
return 0;
}
void GUI::checkTextfieldInput() {
Common::Event event;
uint32 now = _vm->_system->getMillis();
bool running = true;
int keys = 0;
while (_vm->_eventMan->pollEvent(event) && running) {
switch (event.type) {
case Common::EVENT_KEYDOWN:
if (event.kbd.keycode == 'q' && event.kbd.flags == Common::KBD_CTRL)
_vm->quitGame();
else
_keyPressed = event.kbd;
running = false;
break;
case Common::EVENT_LBUTTONDOWN:
case Common::EVENT_LBUTTONUP: {
Common::Point pos = _vm->getMousePos();
_vm->_mouseX = pos.x;
_vm->_mouseY = pos.y;
keys = event.type == Common::EVENT_LBUTTONDOWN ? 199 : (200 | 0x800);
running = false;
} break;
case Common::EVENT_MOUSEMOVE: {
Common::Point pos = _vm->getMousePos();
_vm->_mouseX = pos.x;
_vm->_mouseY = pos.y;
_vm->_system->updateScreen();
_lastScreenUpdate = now;
} break;
default:
break;
}
}
if (now - _lastScreenUpdate > 50) {
_vm->_system->updateScreen();
_lastScreenUpdate = now;
}
processButtonList(_menuButtonList, keys | 0x8000, 0);
_vm->_system->delayMillis(3);
}
void GUI::printMenuText(const char *str, int x, int y, uint8 c0, uint8 c1, uint8 c2, Screen::FontId font) {
_text->printText(str, x, y, c0, c1, c2/*, font*/);
}
int GUI::getMenuCenterStringX(const char *str, int x1, int x2) {
return _text->getCenterStringX(str, x1, x2);
}
#pragma mark -
MainMenu::MainMenu(KyraEngine_v1 *vm) : _vm(vm), _screen(0) {
_screen = _vm->screen();
_nextUpdate = 0;
_system = g_system;
}
void MainMenu::init(StaticData data, Animation anim) {
_static = data;
_anim = anim;
_animIntern.curFrame = _anim.startFrame;
_animIntern.direction = 1;
}
void MainMenu::updateAnimation() {
if (_anim.anim) {
uint32 now = _system->getMillis();
if (now > _nextUpdate) {
_nextUpdate = now + _anim.delay * _vm->tickLength();
_anim.anim->displayFrame(_animIntern.curFrame, 0, 0, 0, 0, 0, 0);
_animIntern.curFrame += _animIntern.direction ;
if (_animIntern.curFrame < _anim.startFrame) {
_animIntern.curFrame = _anim.startFrame;
_animIntern.direction = 1;
} else if (_animIntern.curFrame > _anim.endFrame) {
_animIntern.curFrame = _anim.endFrame;
_animIntern.direction = -1;
}
}
}
_screen->updateScreen();
}
bool MainMenu::getInput() {
Common::Event event;
Common::EventManager *eventMan = _vm->getEventManager();
bool updateScreen = false;
while (eventMan->pollEvent(event)) {
switch (event.type) {
case Common::EVENT_LBUTTONUP:
return true;
case Common::EVENT_MOUSEMOVE:
updateScreen = true;
break;
default:
break;
}
}
if (updateScreen)
_system->updateScreen();
return false;
}
int MainMenu::handle(int dim) {
int command = -1;
uint8 colorMap[16];
memset(colorMap, 0, sizeof(colorMap));
_screen->setTextColorMap(colorMap);
Screen::FontId oldFont = _screen->setFont(_static.font);
int charWidthBackUp = _screen->_charWidth;
if (_vm->game() != GI_LOL)
_screen->_charWidth = -2;
_screen->setScreenDim(dim);
int backUpX = _screen->_curDim->sx;
int backUpY = _screen->_curDim->sy;
int backUpWidth = _screen->_curDim->w;
int backUpHeight = _screen->_curDim->h;
_screen->copyRegion(backUpX, backUpY, backUpX, backUpY, backUpWidth, backUpHeight, 0, 3);
int x = _screen->_curDim->sx << 3;
int y = _screen->_curDim->sy;
int width = _screen->_curDim->w << 3;
int height = _screen->_curDim->h;
drawBox(x, y, width, height, 1);
drawBox(x + 1, y + 1, width - 2, height - 2, 0);
int selected = 0;
draw(selected);
while (!_screen->isMouseVisible())
_screen->showMouse();
int fh = _screen->getFontHeight();
if (_vm->gameFlags().lang == Common::JA_JPN)
fh++;
int textPos = ((_screen->_curDim->w >> 1) + _screen->_curDim->sx) << 3;
Common::Rect menuRect(x + 16, y + 4, x + width - 16, y + 4 + fh * _static.menuTable[3]);
while (!_vm->shouldQuit()) {
updateAnimation();
bool mousePressed = getInput();
Common::Point mouse = _vm->getMousePos();
if (menuRect.contains(mouse)) {
int item = (mouse.y - menuRect.top) / fh;
if (item != selected) {
printString("%s", textPos, menuRect.top + selected * fh, _static.menuTable[5], 0, 5, _static.strings[selected]);
printString("%s", textPos, menuRect.top + item * fh, _static.menuTable[6], 0, 5, _static.strings[item]);
selected = item;
}
if (mousePressed) {
for (int i = 0; i < 3; i++) {
printString("%s", textPos, menuRect.top + selected * fh, _static.menuTable[5], 0, 5, _static.strings[selected]);
_screen->updateScreen();
_system->delayMillis(50);
printString("%s", textPos, menuRect.top + selected * fh, _static.menuTable[6], 0, 5, _static.strings[selected]);
_screen->updateScreen();
_system->delayMillis(50);
}
command = item;
break;
}
}
_system->delayMillis(10);
}
if (_vm->shouldQuit())
command = -1;
_screen->copyRegion(backUpX, backUpY, backUpX, backUpY, backUpWidth, backUpHeight, 3, 0);
_screen->_charWidth = charWidthBackUp;
_screen->setFont(oldFont);
return command;
}
void MainMenu::draw(int select) {
int top = _screen->_curDim->sy;
top += _static.menuTable[1];
int fh = _screen->getFontHeight();
if (_vm->gameFlags().lang == Common::JA_JPN)
fh++;
for (int i = 0; i < _static.menuTable[3]; ++i) {
int curY = top + i * fh;
int color = (i == select) ? _static.menuTable[6] : _static.menuTable[5];
printString("%s", ((_screen->_curDim->w >> 1) + _screen->_curDim->sx) << 3, curY, color, 0, 5, _static.strings[i]);
}
}
void MainMenu::drawBox(int x, int y, int w, int h, int fill) {
--w; --h;
if (fill)
_screen->fillRect(x, y, x+w, y+h, _static.colorTable[0]);
_screen->drawClippedLine(x, y+h, x+w, y+h, _static.colorTable[1]);
_screen->drawClippedLine(x+w, y, x+w, y+h, _static.colorTable[1]);
_screen->drawClippedLine(x, y, x+w, y, _static.colorTable[2]);
_screen->drawClippedLine(x, y, x, y+h, _static.colorTable[2]);
_screen->setPagePixel(_screen->_curPage, x, y+h, _static.colorTable[3]);
_screen->setPagePixel(_screen->_curPage, x+w, y, _static.colorTable[3]);
}
void MainMenu::printString(const char *format, int x, int y, int col1, int col2, int flags, ...) {
if (!format)
return;
char string[512];
va_list vaList;
va_start(vaList, flags);
vsprintf(string, format, vaList);
va_end(vaList);
if (flags & 1)
x -= _screen->getTextWidth(string) >> 1;
if (flags & 2)
x -= _screen->getTextWidth(string);
if (_vm->gameFlags().use16ColorMode)
flags &= 3;
if (flags & 4) {
_screen->printText(string, x - 1, y, _static.altColor, col2);
_screen->printText(string, x, y + 1, _static.altColor, col2);
}
if (flags & 8) {
_screen->printText(string, x - 1, y, 227, col2);
_screen->printText(string, x, y + 1, 227, col2);
}
_screen->printText(string, x, y, col1, col2);
}
} // End of namespace Kyra