1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "kyra/gui/gui_v1.h"
24
25 #include "kyra/text/text.h"
26 #include "kyra/graphics/wsamovie.h"
27
28 #include "common/savefile.h"
29 #include "common/system.h"
30
31 namespace Kyra {
32
GUI_v1(KyraEngine_v1 * kyra)33 GUI_v1::GUI_v1(KyraEngine_v1 *kyra) : GUI(kyra), _text(kyra->text()) {
34 _menuButtonList = 0;
35
36 _redrawButtonFunctor = BUTTON_FUNCTOR(GUI_v1, this, &GUI_v1::redrawButtonCallback);
37 _redrawShadedButtonFunctor = BUTTON_FUNCTOR(GUI_v1, this, &GUI_v1::redrawShadedButtonCallback);
38 }
39
addButtonToList(Button * list,Button * newButton)40 Button *GUI_v1::addButtonToList(Button *list, Button *newButton) {
41 if (!newButton)
42 return list;
43
44 newButton->nextButton = 0;
45
46 if (list) {
47 Button *cur = list;
48 while (cur->nextButton)
49 cur = cur->nextButton;
50 cur->nextButton = newButton;
51 } else {
52 list = newButton;
53 }
54
55 return list;
56 }
57
initMenuLayout(Menu & menu)58 void GUI_v1::initMenuLayout(Menu &menu) {
59 if (menu.x == -1)
60 menu.x = (320 - menu.width) >> 1;
61 if (menu.y == -1)
62 menu.y = (200 - menu.height) >> 1;
63
64 for (int i = 0; i < menu.numberOfItems; ++i) {
65 if (menu.item[i].x == -1)
66 menu.item[i].x = (menu.width - menu.item[i].width) >> 1;
67 }
68 }
69
initMenu(Menu & menu)70 void GUI_v1::initMenu(Menu &menu) {
71 _menuButtonList = 0;
72
73 int textX;
74 int textY;
75
76 int menu_x2 = menu.width + menu.x - 1;
77 int menu_y2 = menu.height + menu.y - 1;
78
79 _screen->fillRect(menu.x + 2, menu.y + 2, menu_x2 - 2, menu_y2 - 2, menu.bkgdColor);
80 _screen->drawShadedBox(menu.x, menu.y, menu_x2, menu_y2, menu.color1, menu.color2);
81
82 if (menu.titleX != -1)
83 textX = menu.titleX;
84 else
85 textX = getMenuCenterStringX(getMenuTitle(menu), menu.x, menu_x2);
86
87 textY = menu.y + menu.titleY;
88
89 if (_vm->game() == GI_LOL) {
90 printMenuText(getMenuTitle(menu), textX, textY, menu.textColor, 0, 9);
91 } else {
92 if (_vm->gameFlags().platform != Common::kPlatformAmiga)
93 printMenuText(getMenuTitle(menu), textX - 1, textY + 1, defaultColor1(), defaultColor2(), 0);
94 printMenuText(getMenuTitle(menu), textX, textY, menu.textColor, 0, 0);
95 }
96
97 int x1, y1, x2, y2;
98 for (int i = 0; i < menu.numberOfItems; ++i) {
99 if (!menu.item[i].enabled)
100 continue;
101
102 x1 = menu.x + menu.item[i].x;
103 y1 = menu.y + menu.item[i].y;
104
105 x2 = x1 + menu.item[i].width - 1;
106 y2 = y1 + menu.item[i].height - 1;
107
108 if (i < 7) {
109 Button *menuButtonData = getButtonListData() + i;
110 menuButtonData->nextButton = 0;
111 menuButtonData->x = x1;
112 menuButtonData->y = y1;
113 menuButtonData->width = menu.item[i].width - 1;
114 menuButtonData->height = menu.item[i].height - 1;
115 menuButtonData->buttonCallback = menu.item[i].callback;
116 menuButtonData->keyCode = menu.item[i].keyCode;
117 menuButtonData->keyCode2 = 0;
118 menuButtonData->arg = menu.item[i].itemId;
119
120 _menuButtonList = addButtonToList(_menuButtonList, menuButtonData);
121 }
122
123 _screen->fillRect(x1, y1, x2, y2, menu.item[i].bkgdColor);
124 _screen->drawShadedBox(x1, y1, x2, y2, menu.item[i].color1, menu.item[i].color2);
125
126 if (getMenuItemTitle(menu.item[i])) {
127 if (menu.item[i].titleX != -1)
128 textX = x1 + menu.item[i].titleX + 3;
129 else
130 textX = getMenuCenterStringX(getMenuItemTitle(menu.item[i]), x1, x2);
131
132 textY = y1 + 2;
133 if (_vm->game() == GI_LOL) {
134 textY++;
135 if (i == menu.highlightedItem)
136 printMenuText(getMenuItemTitle(menu.item[i]), textX, textY, menu.item[i].highlightColor, 0, 8);
137 else
138 printMenuText(getMenuItemTitle(menu.item[i]), textX, textY, menu.item[i].textColor, 0, 8);
139 } else {
140 Screen::FontId of = _screen->_currentFont;
141 if (menu.item[i].saveSlot > 0)
142 _screen->setFont(Screen::FID_8_FNT);
143
144 if (_vm->gameFlags().platform != Common::kPlatformAmiga)
145 printMenuText(getMenuItemTitle(menu.item[i]), textX - 1, textY + 1, defaultColor1(), 0, 0);
146
147 if (i == menu.highlightedItem)
148 printMenuText(getMenuItemTitle(menu.item[i]), textX, textY, menu.item[i].highlightColor, 0, 0);
149 else
150 printMenuText(getMenuItemTitle(menu.item[i]), textX, textY, menu.item[i].textColor, 0, 0);
151
152 _screen->setFont(of);
153 }
154 }
155 }
156
157 for (int i = 0; i < menu.numberOfItems; ++i) {
158 if (getMenuItemLabel(menu.item[i])) {
159 if (_vm->game() == GI_LOL) {
160 menu.item[i].labelX = menu.item[i].x - 1;
161 menu.item[i].labelY = menu.item[i].y + 3;
162 printMenuText(getMenuItemLabel(menu.item[i]), menu.x + menu.item[i].labelX, menu.y + menu.item[i].labelY, menu.item[i].textColor, 0, 10);
163 } else {
164 if (_vm->gameFlags().platform != Common::kPlatformAmiga)
165 printMenuText(getMenuItemLabel(menu.item[i]), menu.x + menu.item[i].labelX - 1, menu.y + menu.item[i].labelY + 1, defaultColor1(), 0, 0);
166 printMenuText(getMenuItemLabel(menu.item[i]), menu.x + menu.item[i].labelX, menu.y + menu.item[i].labelY, menu.item[i].textColor, 0, 0);
167 }
168 }
169 }
170
171 if (menu.scrollUpButtonX != -1) {
172 Button *scrollUpButton = getScrollUpButton();
173 scrollUpButton->x = menu.scrollUpButtonX + menu.x;
174 scrollUpButton->y = menu.scrollUpButtonY + menu.y;
175 scrollUpButton->buttonCallback = getScrollUpButtonHandler();
176 scrollUpButton->nextButton = 0;
177 scrollUpButton->mouseWheel = -1;
178
179 _menuButtonList = addButtonToList(_menuButtonList, scrollUpButton);
180 updateMenuButton(scrollUpButton);
181
182 Button *scrollDownButton = getScrollDownButton();
183 scrollDownButton->x = menu.scrollDownButtonX + menu.x;
184 scrollDownButton->y = menu.scrollDownButtonY + menu.y;
185 scrollDownButton->buttonCallback = getScrollDownButtonHandler();
186 scrollDownButton->nextButton = 0;
187 scrollDownButton->mouseWheel = 1;
188
189 _menuButtonList = addButtonToList(_menuButtonList, scrollDownButton);
190 updateMenuButton(scrollDownButton);
191 }
192
193 _screen->updateScreen();
194 }
195
processHighlights(Menu & menu)196 void GUI_v1::processHighlights(Menu &menu) {
197 int x1, y1, x2, y2;
198 Common::Point p = _vm->getMousePos();
199 int mouseX = p.x;
200 int mouseY = p.y;
201
202 if (_vm->game() == GI_LOL && menu.highlightedItem != 255) {
203 // LoL doesn't have default highlighted items.
204 // We use a highlightedItem value of 255 for this.
205
206 // With LoL no highlighting should take place unless the
207 // mouse cursor moves over a button. The highlighting should end
208 // when the mouse cursor leaves the button.
209 if (menu.item[menu.highlightedItem].enabled)
210 redrawText(menu);
211 }
212
213 for (int i = 0; i < menu.numberOfItems; ++i) {
214 if (!menu.item[i].enabled)
215 continue;
216
217 x1 = menu.x + menu.item[i].x;
218 y1 = menu.y + menu.item[i].y;
219
220 x2 = x1 + menu.item[i].width;
221 y2 = y1 + menu.item[i].height;
222
223 if (mouseX > x1 && mouseX < x2 &&
224 mouseY > y1 && mouseY < y2) {
225
226 if (menu.highlightedItem != i || _vm->game() == GI_LOL) {
227 if (_vm->game() != GI_LOL) {
228 if (menu.item[menu.highlightedItem].enabled)
229 redrawText(menu);
230 }
231
232 menu.highlightedItem = i;
233 redrawHighlight(menu);
234 }
235 }
236 }
237
238 _screen->updateScreen();
239 }
240
redrawText(const Menu & menu)241 void GUI_v1::redrawText(const Menu &menu) {
242 int textX;
243 int i = menu.highlightedItem;
244
245 int x1 = menu.x + menu.item[i].x;
246 int y1 = menu.y + menu.item[i].y;
247
248 int x2 = x1 + menu.item[i].width - 1;
249
250 if (menu.item[i].titleX >= 0)
251 textX = x1 + menu.item[i].titleX + 3;
252 else
253 textX = getMenuCenterStringX(getMenuItemTitle(menu.item[i]), x1, x2);
254
255 int textY = y1 + 2;
256 if (_vm->game() == GI_LOL) {
257 textY++;
258 printMenuText(getMenuItemTitle(menu.item[i]), textX, textY, menu.item[i].textColor, 0, 8);
259 } else {
260 Screen::FontId of = _screen->_currentFont;
261 if (menu.item[i].saveSlot > 0)
262 _screen->setFont(Screen::FID_8_FNT);
263 if (_vm->gameFlags().platform != Common::kPlatformAmiga)
264 printMenuText(getMenuItemTitle(menu.item[i]), textX - 1, textY + 1, defaultColor1(), 0, 0);
265 printMenuText(getMenuItemTitle(menu.item[i]), textX, textY, menu.item[i].textColor, 0, 0);
266 _screen->setFont(of);
267 }
268 }
269
redrawHighlight(const Menu & menu)270 void GUI_v1::redrawHighlight(const Menu &menu) {
271 int textX;
272 int i = menu.highlightedItem;
273
274 int x1 = menu.x + menu.item[i].x;
275 int y1 = menu.y + menu.item[i].y;
276
277 int x2 = x1 + menu.item[i].width - 1;
278
279 if (menu.item[i].titleX != -1)
280 textX = x1 + menu.item[i].titleX + 3;
281 else
282 textX = getMenuCenterStringX(getMenuItemTitle(menu.item[i]), x1, x2);
283
284 int textY = y1 + 2;
285
286 if (_vm->game() == GI_LOL) {
287 textY++;
288 printMenuText(getMenuItemTitle(menu.item[i]), textX, textY, menu.item[i].highlightColor, 0, 8);
289 } else {
290 Screen::FontId of = _screen->_currentFont;
291 if (menu.item[i].saveSlot > 0)
292 _screen->setFont(Screen::FID_8_FNT);
293 if (_vm->gameFlags().platform != Common::kPlatformAmiga)
294 printMenuText(getMenuItemTitle(menu.item[i]), textX - 1, textY + 1, defaultColor1(), 0, 0);
295 printMenuText(getMenuItemTitle(menu.item[i]), textX, textY, menu.item[i].highlightColor, 0, 0);
296 _screen->setFont(of);
297 }
298 }
299
updateAllMenuButtons()300 void GUI_v1::updateAllMenuButtons() {
301 for (Button *cur = _menuButtonList; cur; cur = cur->nextButton)
302 updateMenuButton(cur);
303 }
304
updateMenuButton(Button * button)305 void GUI_v1::updateMenuButton(Button *button) {
306 if (!_displayMenu)
307 return;
308
309 updateButton(button);
310 _screen->updateScreen();
311 }
312
updateButton(Button * button)313 void GUI_v1::updateButton(Button *button) {
314 if (!button || (button->flags & 8))
315 return;
316
317 if (button->flags2 & 1)
318 button->flags2 &= 0xFFF7;
319 else
320 button->flags2 |= 8;
321
322 button->flags2 &= 0xFFFC;
323
324 if (button->flags2 & 4)
325 button->flags2 |= 0x10;
326 else
327 button->flags2 &= 0xEEEF;
328
329 button->flags2 &= 0xFFFB;
330
331 processButton(button);
332 }
333
redrawButtonCallback(Button * button)334 int GUI_v1::redrawButtonCallback(Button *button) {
335 if (!_displayMenu)
336 return 0;
337
338 if (_vm->gameFlags().platform == Common::kPlatformAmiga)
339 _screen->drawBox(button->x + 1, button->y + 1, button->x + button->width - 1, button->y + button->height - 1, 17);
340 else
341 _screen->drawBox(button->x + 1, button->y + 1, button->x + button->width - 1, button->y + button->height - 1, 0xF8);
342
343 return 0;
344 }
345
redrawShadedButtonCallback(Button * button)346 int GUI_v1::redrawShadedButtonCallback(Button *button) {
347 if (!_displayMenu)
348 return 0;
349
350 if (_vm->gameFlags().platform == Common::kPlatformAmiga)
351 _screen->drawShadedBox(button->x, button->y, button->x + button->width, button->y + button->height, 31, 18);
352 else
353 _screen->drawShadedBox(button->x, button->y, button->x + button->width, button->y + button->height, 0xF9, 0xFA);
354
355 return 0;
356 }
357
checkTextfieldInput()358 void GUI_v1::checkTextfieldInput() {
359 Common::Event event;
360
361 uint32 now = _vm->_system->getMillis();
362
363 bool running = true;
364 int keys = 0;
365 while (_vm->_eventMan->pollEvent(event) && running) {
366 switch (event.type) {
367 case Common::EVENT_KEYDOWN:
368 if (event.kbd.keycode == Common::KEYCODE_q && event.kbd.hasFlags(Common::KBD_CTRL))
369 _vm->quitGame();
370 else
371 _keyPressed = event.kbd;
372 running = false;
373 break;
374
375 case Common::EVENT_LBUTTONDOWN:
376 case Common::EVENT_LBUTTONUP: {
377 Common::Point pos = _vm->getMousePos();
378 _vm->_mouseX = pos.x;
379 _vm->_mouseY = pos.y;
380 keys = event.type == Common::EVENT_LBUTTONDOWN ? 199 : (200 | 0x800);
381 running = false;
382 } break;
383
384 case Common::EVENT_MOUSEMOVE: {
385 Common::Point pos = _vm->getMousePos();
386 _vm->_mouseX = pos.x;
387 _vm->_mouseY = pos.y;
388
389 _vm->_system->updateScreen();
390 _lastScreenUpdate = now;
391 } break;
392
393 default:
394 break;
395 }
396 }
397
398 if (now - _lastScreenUpdate > 50) {
399 _vm->_system->updateScreen();
400 _lastScreenUpdate = now;
401 }
402
403 processButtonList(_menuButtonList, keys | 0x8000, 0);
404 _vm->_system->delayMillis(3);
405 }
406
printMenuText(const char * str,int x,int y,uint8 c0,uint8 c1,uint8 c2)407 void GUI_v1::printMenuText(const char *str, int x, int y, uint8 c0, uint8 c1, uint8 c2) {
408 _text->printText(str, x, y, c0, c1, c2);
409 }
410
getMenuCenterStringX(const char * str,int x1,int x2)411 int GUI_v1::getMenuCenterStringX(const char *str, int x1, int x2) {
412 return _text->getCenterStringX(str, x1, x2);
413 }
414
415 #pragma mark -
416
MainMenu(KyraEngine_v1 * vm)417 MainMenu::MainMenu(KyraEngine_v1 *vm) : _vm(vm), _screen(0) {
418 _screen = _vm->screen();
419 _nextUpdate = 0;
420 _system = g_system;
421 }
422
init(StaticData data,Animation anim)423 void MainMenu::init(StaticData data, Animation anim) {
424 _static = data;
425 _anim = anim;
426 _animIntern.curFrame = _anim.startFrame;
427 _animIntern.direction = 1;
428 }
429
updateAnimation()430 void MainMenu::updateAnimation() {
431 if (_anim.anim) {
432 uint32 now = _system->getMillis();
433 if (now > _nextUpdate) {
434 _nextUpdate = now + _anim.delay * _vm->tickLength();
435
436 _anim.anim->displayFrame(_animIntern.curFrame, 0, 0, 0, 0, 0, 0);
437 _animIntern.curFrame += _animIntern.direction;
438 if (_animIntern.curFrame < _anim.startFrame) {
439 _animIntern.curFrame = _anim.startFrame;
440 _animIntern.direction = 1;
441 } else if (_animIntern.curFrame > _anim.endFrame) {
442 _animIntern.curFrame = _anim.endFrame;
443 _animIntern.direction = -1;
444 }
445 }
446 }
447
448 _screen->updateScreen();
449 }
450
getInput()451 bool MainMenu::getInput() {
452 Common::Event event;
453 Common::EventManager *eventMan = _vm->getEventManager();
454
455 bool updateScreen = false;
456
457 while (eventMan->pollEvent(event)) {
458 switch (event.type) {
459 case Common::EVENT_LBUTTONUP:
460 return true;
461
462 case Common::EVENT_MOUSEMOVE:
463 updateScreen = true;
464 break;
465
466 default:
467 break;
468 }
469 }
470
471 if (updateScreen)
472 _system->updateScreen();
473 return false;
474 }
475
handle(int dim)476 int MainMenu::handle(int dim) {
477 int command = -1;
478
479 uint8 colorMap[16];
480 memset(colorMap, 0, sizeof(colorMap));
481 _screen->setTextColorMap(colorMap);
482
483 Screen::FontId oldFont = _screen->setFont(_static.font);
484 int charWidthBackUp = _screen->_charWidth;
485
486 if (_vm->game() != GI_LOL)
487 _screen->_charWidth = -2;
488 _screen->setScreenDim(dim);
489
490 int backUpX = _screen->_curDim->sx;
491 int backUpY = _screen->_curDim->sy;
492 int backUpWidth = _screen->_curDim->w;
493 int backUpHeight = _screen->_curDim->h;
494 _screen->copyRegion(backUpX, backUpY, backUpX, backUpY, backUpWidth, backUpHeight, 0, 3);
495
496 int x = _screen->_curDim->sx << 3;
497 int y = _screen->_curDim->sy;
498 int width = _screen->_curDim->w << 3;
499 int height = _screen->_curDim->h;
500
501 drawBox(x, y, width, height, 1);
502 drawBox(x + 1, y + 1, width - 2, height - 2, 0);
503
504 int selected = 0;
505
506 draw(selected);
507
508 while (!_screen->isMouseVisible())
509 _screen->showMouse();
510
511 int fh = _screen->getFontHeight();
512 if (_vm->gameFlags().lang == Common::JA_JPN)
513 fh++;
514
515 int textPos = ((_screen->_curDim->w >> 1) + _screen->_curDim->sx) << 3;
516
517 Common::Rect menuRect(x + 16, y + 4, x + width - 16, y + 4 + fh * _static.menuTable[3]);
518
519 while (!_vm->shouldQuit()) {
520 updateAnimation();
521 bool mousePressed = getInput();
522
523 Common::Point mouse = _vm->getMousePos();
524 if (menuRect.contains(mouse)) {
525 int item = (mouse.y - menuRect.top) / fh;
526
527 if (item != selected) {
528 printString("%s", textPos, menuRect.top + selected * fh, _static.menuTable[5], 0, 5, _static.strings[selected]);
529 printString("%s", textPos, menuRect.top + item * fh, _static.menuTable[6], 0, 5, _static.strings[item]);
530
531 selected = item;
532 }
533
534 if (mousePressed) {
535 for (int i = 0; i < 3; i++) {
536 printString("%s", textPos, menuRect.top + selected * fh, _static.menuTable[5], 0, 5, _static.strings[selected]);
537 _screen->updateScreen();
538 _system->delayMillis(50);
539 printString("%s", textPos, menuRect.top + selected * fh, _static.menuTable[6], 0, 5, _static.strings[selected]);
540 _screen->updateScreen();
541 _system->delayMillis(50);
542 }
543 command = item;
544 break;
545 }
546 }
547 _system->delayMillis(10);
548 }
549
550 if (_vm->shouldQuit())
551 command = -1;
552
553 _screen->copyRegion(backUpX, backUpY, backUpX, backUpY, backUpWidth, backUpHeight, 3, 0);
554 _screen->_charWidth = charWidthBackUp;
555 _screen->setFont(oldFont);
556
557 return command;
558 }
559
draw(int select)560 void MainMenu::draw(int select) {
561 int top = _screen->_curDim->sy;
562 top += _static.menuTable[1];
563 int fh = _screen->getFontHeight();
564 if (_vm->gameFlags().lang == Common::JA_JPN)
565 fh++;
566
567 for (int i = 0; i < _static.menuTable[3]; ++i) {
568 int curY = top + i * fh;
569 int color = (i == select) ? _static.menuTable[6] : _static.menuTable[5];
570 printString("%s", ((_screen->_curDim->w >> 1) + _screen->_curDim->sx) << 3, curY, color, 0, 5, _static.strings[i]);
571 }
572 }
573
drawBox(int x,int y,int w,int h,int fill)574 void MainMenu::drawBox(int x, int y, int w, int h, int fill) {
575 --w; --h;
576
577 if (fill)
578 _screen->fillRect(x, y, x + w, y + h, _static.colorTable[0]);
579
580 _screen->drawClippedLine(x, y + h, x + w, y + h, _static.colorTable[1]);
581 _screen->drawClippedLine(x + w, y, x + w, y + h, _static.colorTable[1]);
582 _screen->drawClippedLine(x, y, x + w, y, _static.colorTable[2]);
583 _screen->drawClippedLine(x, y, x, y + h, _static.colorTable[2]);
584
585 _screen->setPagePixel(_screen->_curPage, x, y + h, _static.colorTable[3]);
586 _screen->setPagePixel(_screen->_curPage, x + w, y, _static.colorTable[3]);
587 }
588
printString(const char * format,int x,int y,int col1,int col2,int flags,...)589 void MainMenu::printString(const char *format, int x, int y, int col1, int col2, int flags, ...) {
590 if (!format)
591 return;
592
593 va_list vaList;
594 va_start(vaList, flags);
595 Common::String string = Common::String::vformat(format, vaList);
596 va_end(vaList);
597
598 if (flags & 1)
599 x -= _screen->getTextWidth(string.c_str()) >> 1;
600
601 if (flags & 2)
602 x -= _screen->getTextWidth(string.c_str());
603
604 if (_vm->gameFlags().use16ColorMode)
605 flags &= 3;
606
607 if (flags & 4) {
608 _screen->printText(string.c_str(), x - 1, y, _static.altColor, col2);
609 _screen->printText(string.c_str(), x, y + 1, _static.altColor, col2);
610 }
611
612 if (flags & 8) {
613 _screen->printText(string.c_str(), x - 1, y, 227, col2);
614 _screen->printText(string.c_str(), x, y + 1, 227, col2);
615 }
616
617 _screen->printText(string.c_str(), x, y, col1, col2);
618 }
619
620 } // End of namespace Kyra
621