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 "common/events.h"
24 #include "common/stream.h"
25 #include "graphics/cursorman.h"
26
27 #include "startrek/graphics.h"
28
29 namespace StarTrek {
30
getMenuButtonAt(Sprite * sprites,int numSprites,int x,int y)31 int StarTrekEngine::getMenuButtonAt(Sprite *sprites, int numSprites, int x, int y) {
32 for (int i = 0; i < numSprites; i++) {
33 const Sprite &spr = sprites[i];
34
35 if (spr.drawMode != 2)
36 continue;
37
38 int left = spr.pos.x - spr.bitmap->xoffset;
39 int top = spr.pos.y - spr.bitmap->yoffset;
40
41 // Oddly, this doesn't account for x/yoffset...
42 int right = spr.pos.x + spr.bitmap->width - 1;
43 int bottom = spr.pos.y + spr.bitmap->height - 1;
44
45 if (x >= left && x <= right && y >= top && y <= bottom)
46 return i;
47 }
48
49 return -1;
50 }
51
chooseMousePositionFromSprites(Sprite * sprites,int numSprites,int containMouseSprite,int mode)52 void StarTrekEngine::chooseMousePositionFromSprites(Sprite *sprites, int numSprites, int containMouseSprite, int mode) {
53 uint16 mouseX1 = 0x7fff; // Candidate positions to warp mouse to
54 uint16 mouseY1 = 0x7fff;
55 uint16 mouseX2 = 0x7fff;
56 uint16 mouseY2 = 0x7fff;
57
58 Common::Point mousePos = _gfx->getMousePos();
59
60 // Ensure the cursor is contained within one of the sprites
61 if (containMouseSprite >= 0 && containMouseSprite < numSprites) {
62 Common::Rect rect = sprites[containMouseSprite].getRect();
63
64 if (mousePos.x < rect.left || mousePos.x >= rect.right
65 || mousePos.y < rect.top || mousePos.y >= rect.bottom) {
66 mousePos.x = (rect.left + rect.right) / 2;
67 mousePos.y = (rect.top + rect.bottom) / 2;
68 }
69 }
70
71 // Choose a sprite to warp the cursor to
72 for (int i = 0; i < numSprites; i++) {
73 Sprite *sprite = &sprites[i];
74 if (sprite->drawMode != 2) // Skip hidden buttons
75 continue;
76
77 Common::Rect rect = sprite->getRect();
78
79 int hCenter = (rect.left + rect.right) / 2;
80 int vCenter = (rect.top + rect.bottom) / 2;
81
82 // Choose which sprite is closest based on certain criteria?
83 switch (mode) {
84 case 0: // Choose topmost, leftmost sprite that's below the cursor
85 if (((vCenter == mousePos.y && hCenter > mousePos.x) || vCenter > mousePos.y)
86 && (vCenter < mouseY1 || (vCenter == mouseY1 && hCenter < mouseX1))) {
87 mouseX1 = hCenter;
88 mouseY1 = vCenter;
89 }
90 // fall through
91
92 case 4: // Choose topmost, leftmost sprite
93 if (vCenter < mouseY2 || (vCenter == mouseY2 && hCenter < mouseX2)) {
94 mouseX2 = hCenter;
95 mouseY2 = vCenter;
96 }
97 break;
98
99 case 1: // Choose bottommost, rightmost sprite that's above the cursor
100 if (((vCenter == mousePos.y && hCenter < mousePos.x) || vCenter < mousePos.y)
101 && (mouseY1 == 0x7fff || vCenter > mouseY1
102 || (vCenter == mouseY1 && hCenter > mouseX1))) {
103 mouseX1 = hCenter;
104 mouseY1 = vCenter;
105 }
106 // fall through
107
108 case 5: // Choose bottommost, rightmost sprite
109 if (mouseY2 == 0x7fff || vCenter > mouseY2
110 || (vCenter == mouseY2 && hCenter > mouseX2)) {
111 mouseX2 = hCenter;
112 mouseY2 = vCenter;
113 }
114 break;
115
116 case 2:
117 // This seems broken... OR condition on first line has no affect on the logic...
118 if ((vCenter < mousePos.y || (vCenter == mouseY1 && hCenter == mousePos.x))
119 && (mouseX1 == 0x7fff || vCenter >= mouseY1)) {
120 mouseX1 = hCenter;
121 mouseY1 = vCenter;
122 }
123 if (mouseX2 == 0x7fff || vCenter > mouseY2
124 || (hCenter == mouseX2 && vCenter == mouseY2)) {
125 mouseX2 = hCenter;
126 mouseY2 = vCenter;
127 }
128 break;
129
130 case 3:
131 // Similar to above...
132 if ((vCenter > mousePos.y || (vCenter == mouseY1 && hCenter == mousePos.x))
133 && (mouseX1 == 0x7fff || vCenter <= mouseY1)) {
134 mouseX1 = hCenter;
135 mouseY1 = vCenter;
136 }
137 if (mouseX2 == 0x7fff || vCenter < mouseY2
138 || (hCenter == mouseX2 && vCenter == mouseY2)) {
139 mouseX2 = hCenter;
140 mouseY2 = vCenter;
141 }
142 break;
143 }
144 }
145
146 // Warp mouse to one of the coordinates, if one is valid
147 if (mouseX1 != 0x7fff) {
148 mousePos.x = mouseX1;
149 mousePos.y = mouseY1;
150 } else if (mouseX2 != 0x7fff) {
151 mousePos.x = mouseX2;
152 mousePos.y = mouseY2;
153 }
154
155 _gfx->warpMouse(mousePos.x, mousePos.y);
156
157 }
158
drawMenuButtonOutline(SharedPtr<Bitmap> bitmap,byte color)159 void StarTrekEngine::drawMenuButtonOutline(SharedPtr<Bitmap> bitmap, byte color) {
160 int lineWidth = bitmap->width - 2;
161 int offsetToBottom = (bitmap->height - 3) * bitmap->width;
162
163 byte *dest = bitmap->pixels + bitmap->width + 1;
164
165 while (lineWidth--) {
166 *dest = color;
167 *(dest + offsetToBottom) = color;
168 dest++;
169 }
170
171 int lineHeight = bitmap->height - 2;
172 int offsetToRight = bitmap->width - 3;
173
174 dest = bitmap->pixels + bitmap->width + 1;
175
176 while (lineHeight--) {
177 *dest = color;
178 *(dest + offsetToRight) = color;
179 dest += bitmap->width;
180 }
181 }
182
showOptionsMenu(int x,int y)183 void StarTrekEngine::showOptionsMenu(int x, int y) {
184 bool tmpMouseControllingShip = _mouseControllingShip;
185 _mouseControllingShip = false;
186
187 Common::Point oldMousePos = _gfx->getMousePos();
188 SharedPtr<Bitmap> oldMouseBitmap = _gfx->getMouseBitmap();
189
190 _gfx->setMouseBitmap(_gfx->loadBitmap("options"));
191 loadMenuButtons("options", x, y);
192
193 uint32 disabledButtons = 0;
194 if (_musicWorking) {
195 if (_musicEnabled)
196 disabledButtons |= (1 << OPTIONBUTTON_ENABLEMUSIC);
197 else
198 disabledButtons |= (1 << OPTIONBUTTON_DISABLEMUSIC);
199 } else
200 disabledButtons |= (1 << OPTIONBUTTON_ENABLEMUSIC) | (1 << OPTIONBUTTON_DISABLEMUSIC);
201
202 if (_sfxWorking) {
203 if (_sfxEnabled)
204 disabledButtons |= (1 << OPTIONBUTTON_ENABLESFX);
205 else
206 disabledButtons |= (1 << OPTIONBUTTON_DISABLESFX);
207 } else
208 disabledButtons |= (1 << OPTIONBUTTON_ENABLESFX) | (1 << OPTIONBUTTON_DISABLESFX);
209
210 disableMenuButtons(disabledButtons);
211 chooseMousePositionFromSprites(_activeMenu->sprites, _activeMenu->numButtons, -1, 4);
212 int event = handleMenuEvents(0, false);
213
214 unloadMenuButtons();
215 _mouseControllingShip = tmpMouseControllingShip;
216 _gfx->setMouseBitmap(oldMouseBitmap);
217
218 if (event != MENUEVENT_LCLICK_OFFBUTTON && event != MENUEVENT_RCLICK_OFFBUTTON)
219 _gfx->warpMouse(oldMousePos.x, oldMousePos.y);
220
221
222 // Can't use OPTIONBUTTON constants since the button retvals differ from the button
223 // indices...
224 switch (event) {
225 case 0: // Save
226 showSaveMenu();
227 break;
228 case 1: // Load
229 showLoadMenu();
230 _resetGameMode = true;
231 break;
232 case 2: // Enable music
233 _sound->setMusicEnabled(true);
234 break;
235 case 3: // Disable music
236 _sound->setMusicEnabled(false);
237 break;
238 case 4: // Enable sfx
239 _sound->setSfxEnabled(true);
240 break;
241 case 5: // Disable sfx
242 _sound->setSfxEnabled(false);
243 break;
244 case 6: // Quit
245 showQuitGamePrompt(20, 20);
246 break;
247 case 7: // Text
248 showTextConfigurationMenu(true);
249 break;
250 default:
251 break;
252 }
253 }
254
showActionMenu()255 int StarTrekEngine::showActionMenu() {
256 const int actionMappingUp[] = { // Actions to jump to when up is pressed
257 ACTION_TALK, // <- ACTION_WALK
258 ACTION_TALK, // <- ACTION_USE
259 ACTION_OPTIONS, // <- ACTION_GET
260 ACTION_LOOK, // <- ACTION_LOOK
261 ACTION_LOOK, // <- ACTION_TALK
262 ACTION_OPTIONS // <- ACTION_OPTIONS
263 };
264 const int actionMappingRight[] = { // Actions to jump to when right is pressed
265 ACTION_GET, // <- ACTION_WALK
266 ACTION_WALK, // <- ACTION_USE
267 ACTION_GET, // <- ACTION_GET
268 ACTION_OPTIONS, // <- ACTION_LOOK
269 ACTION_OPTIONS, // <- ACTION_TALK
270 ACTION_OPTIONS // <- ACTION_OPTIONS
271 };
272 const int actionMappingDown[] = { // Actions to jump to when down is pressed
273 ACTION_GET, // <- ACTION_WALK
274 ACTION_WALK, // <- ACTION_USE
275 ACTION_GET, // <- ACTION_GET
276 ACTION_TALK, // <- ACTION_LOOK
277 ACTION_WALK, // <- ACTION_TALK
278 ACTION_GET // <- ACTION_OPTIONS
279 };
280 const int actionMappingLeft[] = { // Actions to jump to when left is pressed
281 ACTION_USE, // <- ACTION_WALK
282 ACTION_USE, // <- ACTION_USE
283 ACTION_WALK, // <- ACTION_GET
284 ACTION_USE, // <- ACTION_LOOK
285 ACTION_USE, // <- ACTION_TALK
286 ACTION_LOOK // <- ACTION_OPTIONS
287 };
288
289 const Common::Point pos(50, 50); // Top-left position to put action menu at
290
291 // Positions to put mouse cursor at to select actions (when using arrow keys)
292 const Common::Point actionPositions[] = {
293 Common::Point(7, 21), // ACTION_USE
294 Common::Point(48, 38), // ACTION_GET
295 Common::Point(28, 5), // ACTION_LOOK
296 Common::Point(28, 14), // ACTION_TALK
297 Common::Point(45, 9) // ACTION_OPTIONS
298 };
299
300 TrekEvent event;
301 Sprite menuSprite;
302
303 bool keyboardControlledMouse = _keyboardControlsMouse;
304 Common::Point oldMousePos = _gfx->getMousePos();
305
306 bool addEventBack = false;
307 int action = ACTION_WALK;
308
309 menuSprite.bitmap = _gfx->loadBitmap("action");
310 int menuWidth = menuSprite.bitmap->width;
311 int menuHeight = menuSprite.bitmap->height;
312
313 _gfx->warpMouse(pos.x + menuWidth / 2, pos.y + menuHeight / 2);
314
315 _gfx->addSprite(&menuSprite);
316 menuSprite.pos = pos;
317 menuSprite.drawPriority = 15;
318
319 chooseMouseBitmapForAction(action, false);
320
321 _gfx->drawAllSprites();
322
323 menuSprite.drawPriority2 = 8;
324 bool displayMenu = true;
325
326 while (displayMenu) {
327 _sound->checkLoopMusic();
328
329 if (!popNextEvent(&event))
330 continue;
331
332 switch (event.type) {
333
334 case TREKEVENT_TICK:
335 _gfx->incPaletteFadeLevel();
336 _gfx->drawAllSprites();
337 break;
338
339 case TREKEVENT_LBUTTONDOWN:
340 displayMenu = false;
341 addEventBack = true;
342 break;
343
344 case TREKEVENT_MOUSEMOVE:
345 mousePosChanged: {
346 Common::Point mouse = _gfx->getMousePos();
347 Common::Point relMouse(mouse.x - pos.x, mouse.y - pos.y);
348
349 Common::String bitmapName;
350 Common::Point lockMousePoint(-1, -1);
351
352 // Check if the mouse is hovering over one of the selectable actions
353 if (relMouse.x >= 39 && relMouse.x <= 50 && relMouse.y >= 2 && relMouse.y <= 17) {
354 action = ACTION_OPTIONS;
355 bitmapName = "options";
356 lockMousePoint = Common::Point(pos.x + 44, pos.y + 2);
357 } else if (relMouse.x >= 18 && relMouse.x <= 38 && relMouse.y >= 2 && relMouse.y <= 9) {
358 action = ACTION_LOOK;
359 bitmapName = "look";
360 lockMousePoint = Common::Point(pos.x + 28, pos.y + 6);
361 } else if (relMouse.x >= 18 && relMouse.x <= 38 && relMouse.y >= 11 && relMouse.y <= 17) {
362 action = ACTION_TALK;
363 bitmapName = "talk";
364 lockMousePoint = Common::Point(pos.x + 27, pos.y + 14);
365 } else if (relMouse.x >= 2 && relMouse.x <= 13 && relMouse.y >= 16 && relMouse.y <= 26) {
366 action = ACTION_USE;
367 bitmapName = "use";
368 lockMousePoint = Common::Point(pos.x + 7, pos.y + 19);
369 } else if (relMouse.x >= 40 && relMouse.x <= 53 && relMouse.y >= 34 && relMouse.y <= 43) {
370 action = ACTION_GET;
371 bitmapName = "get";
372 lockMousePoint = Common::Point(pos.x + 44, pos.y + 38);
373 } else {
374 action = ACTION_WALK;
375 bitmapName = "walk";
376 }
377
378 _gfx->setMouseBitmap(_gfx->loadBitmap(bitmapName));
379
380 if (lockMousePoint.x != -1)
381 _gfx->lockMousePosition(lockMousePoint.x, lockMousePoint.y);
382 else
383 _gfx->unlockMousePosition();
384 }
385 break;
386
387 case TREKEVENT_RBUTTONDOWN:
388 exitMenu:
389 displayMenu = false;
390 action = ACTION_WALK;
391 break;
392
393 case TREKEVENT_KEYDOWN: {
394 int nextAction = action;
395 const int *lookupArray;
396
397 switch (event.kbd.keycode) {
398 case Common::KEYCODE_ESCAPE:
399 case Common::KEYCODE_SPACE:
400 case Common::KEYCODE_F2: // Exit menu without selecting anything
401 goto exitMenu;
402
403 case Common::KEYCODE_RETURN:
404 case Common::KEYCODE_KP_ENTER:
405 case Common::KEYCODE_F1: // Exit menu with whatever is selected
406 displayMenu = false;
407 addEventBack = true;
408 break;
409
410 case Common::KEYCODE_PAGEUP:
411 case Common::KEYCODE_KP9:
412 nextAction = ACTION_OPTIONS;
413 break;
414
415 case Common::KEYCODE_PAGEDOWN:
416 case Common::KEYCODE_KP3:
417 nextAction = ACTION_GET;
418 break;
419
420 // Direction buttons
421 case Common::KEYCODE_UP:
422 case Common::KEYCODE_KP8:
423 lookupArray = actionMappingUp;
424 goto lookupNextAction;
425
426 case Common::KEYCODE_RIGHT:
427 case Common::KEYCODE_KP6:
428 lookupArray = actionMappingRight;
429 goto lookupNextAction;
430
431 case Common::KEYCODE_DOWN:
432 case Common::KEYCODE_KP2:
433 lookupArray = actionMappingDown;
434 goto lookupNextAction;
435
436 case Common::KEYCODE_LEFT:
437 case Common::KEYCODE_KP4:
438 lookupArray = actionMappingLeft;
439 goto lookupNextAction;
440
441 lookupNextAction:
442 // Use a lookup table to decide which action is next after a direction
443 // button is pressed.
444 assert((action >= ACTION_WALK && action <= ACTION_TALK) || action == ACTION_OPTIONS);
445 nextAction = lookupArray[action == ACTION_OPTIONS ? 5 : action - 1];
446 break;
447
448 default:
449 break;
450 }
451
452 if (nextAction == action)
453 break;
454
455 action = nextAction;
456
457 // Warp mouse to the position of the selected action
458 if (nextAction == ACTION_WALK)
459 _gfx->warpMouse(pos.x + menuWidth / 2, pos.y + menuHeight / 2);
460 else {
461 assert((action >= ACTION_WALK && action <= ACTION_TALK) || action == ACTION_OPTIONS);
462 const Common::Point &p = actionPositions[action == ACTION_OPTIONS ? 4 : action - 2];
463 _gfx->warpMouse(pos.x + p.x, pos.y + p.y);
464 }
465
466 goto mousePosChanged;
467 }
468
469 default:
470 break;
471 }
472 }
473
474 playSoundEffectIndex(SND_SELECTION);
475
476 menuSprite.dontDrawNextFrame();
477 _gfx->drawAllSprites();
478 _gfx->delSprite(&menuSprite);
479
480 _gfx->unlockMousePosition();
481
482 if (action == ACTION_OPTIONS) {
483 showOptionsMenu(50, 50);
484 action = ACTION_WALK;
485 }
486
487 Common::Point mouse = _gfx->getMousePos();
488 if (mouse.x < pos.x || mouse.x >= pos.x + menuWidth || mouse.y < pos.y || mouse.y >= pos.y + menuHeight) {
489 if (action == ACTION_WALK && addEventBack)
490 addEventToQueue(event); // Add left-click event back to queue so Kirk can walk there
491 } else
492 _gfx->warpMouse(oldMousePos.x, oldMousePos.y);
493
494 chooseMouseBitmapForAction(action, false);
495 _keyboardControlsMouse = keyboardControlledMouse;
496 return action;
497 }
498
loadMenuButtons(String mnuFilename,int xpos,int ypos)499 void StarTrekEngine::loadMenuButtons(String mnuFilename, int xpos, int ypos) {
500 if (_activeMenu == nullptr)
501 _keyboardControlsMouseOutsideMenu = _keyboardControlsMouse;
502
503 Menu *oldMenu = _activeMenu;
504 _activeMenu = new Menu();
505 _activeMenu->nextMenu = oldMenu;
506
507 Common::MemoryReadStreamEndian *stream = loadFile(mnuFilename + ".MNU");
508
509 _activeMenu->numButtons = stream->size() / 16;
510
511 for (int i = 0; i < _activeMenu->numButtons; i++) {
512 _activeMenu->sprites[i] = Sprite();
513 _gfx->addSprite(&_activeMenu->sprites[i]);
514 _activeMenu->sprites[i].drawMode = 2;
515
516 char bitmapBasename[11];
517 stream->seek(i * 16, SEEK_SET);
518 stream->read(bitmapBasename, 10);
519 for (int j = 0; j < 10; j++) {
520 if (bitmapBasename[j] == ' ')
521 bitmapBasename[j] = '\0';
522 }
523 bitmapBasename[10] = '\0';
524
525 _activeMenu->sprites[i].bitmap = _gfx->loadBitmap(bitmapBasename);
526 _activeMenu->sprites[i].pos.x = stream->readUint16() + xpos;
527 _activeMenu->sprites[i].pos.y = stream->readUint16() + ypos;
528 _activeMenu->retvals[i] = stream->readUint16();
529
530 _activeMenu->sprites[i].drawPriority = 15;
531 _activeMenu->sprites[i].drawPriority2 = 8;
532 }
533
534 delete stream;
535
536 if (_activeMenu->retvals[_activeMenu->numButtons - 1] == 0) {
537 // Set default retvals for buttons
538 for (int i = 0; i < _activeMenu->numButtons; i++)
539 _activeMenu->retvals[i] = i;
540 }
541
542 _activeMenu->selectedButton = -1;
543 _activeMenu->disabledButtons = 0;
544 _keyboardControlsMouse = false;
545 }
546
setVisibleMenuButtons(uint32 bits)547 void StarTrekEngine::setVisibleMenuButtons(uint32 bits) {
548 for (int i = 0; i < _activeMenu->numButtons; i++) {
549 Sprite *sprite = &_activeMenu->sprites[i];
550 uint32 spriteBitmask = (1 << i);
551 if (spriteBitmask == 0)
552 break;
553
554 if ((bits & spriteBitmask) == 0 || sprite->drawMode != 0) {
555 if ((bits & spriteBitmask) == 0 && sprite->drawMode == 2) {
556 if (i == _activeMenu->selectedButton) {
557 drawMenuButtonOutline(sprite->bitmap, 0x00);
558 _activeMenu->selectedButton = -1;
559 }
560
561 sprite->field16 = true;
562 sprite->bitmapChanged = true;
563 }
564 } else {
565 _gfx->addSprite(sprite);
566 sprite->drawMode = 2;
567 sprite->bitmapChanged = true;
568 }
569 }
570
571 _gfx->drawAllSprites();
572
573 for (int i = 0; i < _activeMenu->numButtons; i++) {
574 Sprite *sprite = &_activeMenu->sprites[i];
575 uint32 spriteBitmask = (1 << i);
576 if (spriteBitmask == 0)
577 break;
578
579 if ((bits & spriteBitmask) == 0 && sprite->drawMode == 2) {
580 _gfx->delSprite(sprite);
581
582 // Setting drawMode to 0 is the game's way of saying that the menu button is
583 // hidden (since it would normally be 2).
584 sprite->drawMode = 0;
585 }
586 }
587 }
588
disableMenuButtons(uint32 bits)589 void StarTrekEngine::disableMenuButtons(uint32 bits) {
590 _activeMenu->disabledButtons |= bits;
591 if (_activeMenu->selectedButton != -1
592 && (_activeMenu->disabledButtons & (1 << _activeMenu->selectedButton))) {
593 Sprite *sprite = &_activeMenu->sprites[_activeMenu->selectedButton];
594 drawMenuButtonOutline(sprite->bitmap, 0x00);
595
596 sprite->bitmapChanged = true;
597 _activeMenu->selectedButton = -1;
598 }
599 }
600
enableMenuButtons(uint32 bits)601 void StarTrekEngine::enableMenuButtons(uint32 bits) {
602 _activeMenu->disabledButtons &= ~bits;
603 }
604
handleMenuEvents(uint32 ticksUntilClickingEnabled,bool inTextbox)605 int StarTrekEngine::handleMenuEvents(uint32 ticksUntilClickingEnabled, bool inTextbox) {
606 uint32 tickWhenClickingEnabled = _clockTicks + ticksUntilClickingEnabled;
607
608 while (true) {
609 TrekEvent event;
610 while (popNextEvent(&event)) {
611 switch (event.type) {
612
613 case TREKEVENT_TICK: {
614 Common::Point mousePos = _gfx->getMousePos();
615 int buttonIndex = getMenuButtonAt(_activeMenu->sprites, _activeMenu->numButtons, mousePos.x, mousePos.y);
616 if (buttonIndex != -1) {
617 if (_activeMenu->disabledButtons & (1 << buttonIndex))
618 buttonIndex = -1;
619 }
620
621 if (buttonIndex != _activeMenu->selectedButton) {
622 if (_activeMenu->selectedButton != -1) {
623 Sprite &spr = _activeMenu->sprites[_activeMenu->selectedButton];
624 drawMenuButtonOutline(spr.bitmap, 0x00);
625 spr.bitmapChanged = true;
626 }
627 if (buttonIndex != -1) {
628 Sprite &spr = _activeMenu->sprites[buttonIndex];
629 drawMenuButtonOutline(spr.bitmap, 0xda);
630 spr.bitmapChanged = true;
631 }
632 _activeMenu->selectedButton = buttonIndex;
633 }
634 // Not added: updating mouse position (scummvm handles that)
635
636 updateActorAnimations();
637 renderBanBelowSprites();
638 _gfx->drawAllSprites(false);
639 renderBanAboveSprites();
640 _gfx->updateScreen();
641
642 _sound->checkLoopMusic();
643
644 if (_finishedPlayingSpeech != 0) {
645 _finishedPlayingSpeech = 0;
646 if (_textDisplayMode != TEXTDISPLAY_WAIT) {
647 return TEXTBUTTON_SPEECH_DONE;
648 }
649 }
650 _gfx->incPaletteFadeLevel();
651 _frameIndex++;
652
653 if (ticksUntilClickingEnabled != 0 && _clockTicks >= tickWhenClickingEnabled)
654 return MENUEVENT_ENABLEINPUT;
655 break;
656 }
657
658 case TREKEVENT_LBUTTONDOWN:
659 lclick:
660 if (_activeMenu->selectedButton != -1) {
661 playSoundEffectIndex(SND_SELECTION);
662 return _activeMenu->retvals[_activeMenu->selectedButton];
663 } else {
664 Common::Point mouse = _gfx->getMousePos();
665 if (getMenuButtonAt(_activeMenu->sprites, _activeMenu->numButtons, mouse.x, mouse.y) == -1) {
666 playSoundEffectIndex(SND_SELECTION);
667 return MENUEVENT_LCLICK_OFFBUTTON;
668 }
669 }
670 break;
671
672 case TREKEVENT_RBUTTONDOWN:
673 rclick:
674 playSoundEffectIndex(SND_SELECTION);
675 if (_activeMenu->selectedButton == -1)
676 return MENUEVENT_RCLICK_OFFBUTTON;
677 else
678 return MENUEVENT_RCLICK_ONBUTTON;
679 break;
680
681 case TREKEVENT_KEYDOWN:
682 if (inTextbox) {
683 switch (event.kbd.keycode) {
684 case Common::KEYCODE_ESCAPE:
685 case Common::KEYCODE_F2:
686 goto rclick;
687
688 case Common::KEYCODE_RETURN:
689 case Common::KEYCODE_KP_ENTER:
690 case Common::KEYCODE_F1:
691 playSoundEffectIndex(SND_SELECTION);
692 return TEXTBUTTON_CONFIRM;
693
694 case Common::KEYCODE_SPACE:
695 if (!(_activeMenu->disabledButtons & (1 << TEXTBUTTON_NEXTCHOICE))
696 && _activeMenu->sprites[TEXTBUTTON_NEXTCHOICE].drawMode == 2) {
697 playSoundEffectIndex(SND_SELECTION);
698 return TEXTBUTTON_NEXTCHOICE;
699 }
700 break;
701
702 case Common::KEYCODE_HOME:
703 case Common::KEYCODE_KP7:
704 if (!(_activeMenu->disabledButtons & (1 << TEXTBUTTON_SCROLLUP))
705 && _activeMenu->sprites[TEXTBUTTON_SCROLLUP].drawMode == 2) {
706 playSoundEffectIndex(SND_SELECTION);
707 return TEXTBUTTON_GOTO_TOP;
708 }
709 break;
710
711 case Common::KEYCODE_UP:
712 case Common::KEYCODE_KP8:
713 if (!(_activeMenu->disabledButtons & (1 << TEXTBUTTON_SCROLLUP))
714 && _activeMenu->sprites[TEXTBUTTON_SCROLLUP].drawMode == 2) {
715 playSoundEffectIndex(SND_SELECTION);
716 return TEXTBUTTON_SCROLLUP_ONELINE;
717 }
718 break;
719
720 case Common::KEYCODE_PAGEUP:
721 case Common::KEYCODE_KP9:
722 if (!(_activeMenu->disabledButtons & (1 << TEXTBUTTON_SCROLLUP))
723 && _activeMenu->sprites[TEXTBUTTON_SCROLLUP].drawMode == 2) {
724 playSoundEffectIndex(SND_SELECTION);
725 return TEXTBUTTON_SCROLLUP;
726 }
727 break;
728
729 case Common::KEYCODE_LEFT:
730 case Common::KEYCODE_KP4:
731 if (!(_activeMenu->disabledButtons & (1 << TEXTBUTTON_PREVCHOICE))
732 && _activeMenu->sprites[TEXTBUTTON_PREVCHOICE].drawMode == 2) {
733 playSoundEffectIndex(SND_SELECTION);
734 return TEXTBUTTON_PREVCHOICE;
735 }
736 break;
737
738 case Common::KEYCODE_RIGHT:
739 case Common::KEYCODE_KP6:
740 if (!(_activeMenu->disabledButtons & (1 << TEXTBUTTON_NEXTCHOICE))
741 && _activeMenu->sprites[TEXTBUTTON_NEXTCHOICE].drawMode == 2) {
742 playSoundEffectIndex(SND_SELECTION);
743 return TEXTBUTTON_NEXTCHOICE;
744 }
745 break;
746
747 case Common::KEYCODE_END:
748 case Common::KEYCODE_KP1:
749 if (!(_activeMenu->disabledButtons & (1 << TEXTBUTTON_SCROLLDOWN))
750 && _activeMenu->sprites[TEXTBUTTON_SCROLLDOWN].drawMode == 2) {
751 playSoundEffectIndex(SND_SELECTION);
752 return TEXTBUTTON_GOTO_BOTTOM;
753 }
754 break;
755
756 case Common::KEYCODE_DOWN:
757 case Common::KEYCODE_KP2:
758 if (!(_activeMenu->disabledButtons & (1 << TEXTBUTTON_SCROLLDOWN))
759 && _activeMenu->sprites[TEXTBUTTON_SCROLLDOWN].drawMode == 2) {
760 playSoundEffectIndex(SND_SELECTION);
761 return TEXTBUTTON_SCROLLDOWN_ONELINE;
762 }
763 break;
764
765 case Common::KEYCODE_PAGEDOWN:
766 case Common::KEYCODE_KP3:
767 if (!(_activeMenu->disabledButtons & (1 << TEXTBUTTON_SCROLLDOWN))
768 && _activeMenu->sprites[TEXTBUTTON_SCROLLDOWN].drawMode == 2) {
769 playSoundEffectIndex(SND_SELECTION);
770 return TEXTBUTTON_SCROLLDOWN;
771 }
772 break;
773
774 default:
775 break;
776 }
777 } else { // Not in textbox
778 switch (event.kbd.keycode) {
779 case Common::KEYCODE_ESCAPE:
780 case Common::KEYCODE_F2:
781 goto rclick;
782
783 case Common::KEYCODE_RETURN:
784 case Common::KEYCODE_KP_ENTER:
785 case Common::KEYCODE_F1:
786 goto lclick;
787
788 case Common::KEYCODE_HOME:
789 case Common::KEYCODE_KP7:
790 chooseMousePositionFromSprites(_activeMenu->sprites, _activeMenu->numButtons, _activeMenu->selectedButton, 4);
791 break;
792
793 case Common::KEYCODE_UP:
794 case Common::KEYCODE_KP8:
795 case Common::KEYCODE_PAGEUP:
796 case Common::KEYCODE_KP9:
797 chooseMousePositionFromSprites(_activeMenu->sprites, _activeMenu->numButtons, _activeMenu->selectedButton, 2);
798 break;
799
800 case Common::KEYCODE_LEFT:
801 case Common::KEYCODE_KP4:
802 chooseMousePositionFromSprites(_activeMenu->sprites, _activeMenu->numButtons, _activeMenu->selectedButton, 1);
803 break;
804
805 case Common::KEYCODE_RIGHT:
806 case Common::KEYCODE_KP6:
807 chooseMousePositionFromSprites(_activeMenu->sprites, _activeMenu->numButtons, _activeMenu->selectedButton, 0);
808 break;
809
810 case Common::KEYCODE_END:
811 case Common::KEYCODE_KP1:
812 chooseMousePositionFromSprites(_activeMenu->sprites, _activeMenu->numButtons, _activeMenu->selectedButton, 5);
813 break;
814
815 case Common::KEYCODE_DOWN:
816 case Common::KEYCODE_KP2:
817 case Common::KEYCODE_PAGEDOWN:
818 case Common::KEYCODE_KP3:
819 chooseMousePositionFromSprites(_activeMenu->sprites, _activeMenu->numButtons, _activeMenu->selectedButton, 3);
820 break;
821
822 default:
823 break;
824 }
825 break;
826
827 default:
828 break;
829 }
830 }
831 }
832 }
833 }
834
unloadMenuButtons()835 void StarTrekEngine::unloadMenuButtons() {
836 if (_activeMenu->selectedButton != -1)
837 drawMenuButtonOutline(_activeMenu->sprites[_activeMenu->selectedButton].bitmap, 0x00);
838
839 for (int i = 0; i < _activeMenu->numButtons; i++) {
840 Sprite *sprite = &_activeMenu->sprites[i];
841 if (sprite->drawMode == 2) {
842 sprite->field16 = true;
843 sprite->bitmapChanged = true;
844 }
845 }
846
847 _gfx->drawAllSprites();
848
849 for (int i = 0; i < _activeMenu->numButtons; i++) {
850 Sprite *sprite = &_activeMenu->sprites[i];
851 sprite->bitmap.reset();
852 if (sprite->drawMode == 2)
853 _gfx->delSprite(sprite);
854 }
855
856 Menu *prevMenu = _activeMenu;
857 _activeMenu = _activeMenu->nextMenu;
858 delete prevMenu;
859
860 if (_activeMenu == nullptr)
861 _keyboardControlsMouse = _keyboardControlsMouseOutsideMenu;
862 }
863
chooseMouseBitmapForAction(int action,bool withRedOutline)864 void StarTrekEngine::chooseMouseBitmapForAction(int action, bool withRedOutline) {
865 const char *lookActionBitmaps[] = {
866 "lookh0", // The "look" action randomly animates with these images
867 "lookh0",
868 "lookh0",
869 "lookh0",
870 "lookh0",
871 "lookh1",
872 "lookh2",
873 "lookh3"
874 };
875
876 Common::String bitmapName;
877
878 switch (action) {
879
880 case ACTION_USE:
881 if (withRedOutline)
882 bitmapName = "useh";
883 else
884 bitmapName = "usen";
885 break;
886
887 case ACTION_GET:
888 if (withRedOutline)
889 bitmapName = "geth";
890 else
891 bitmapName = "getn";
892 break;
893
894 case ACTION_LOOK:
895 if (withRedOutline) {
896 if ((getRandomWord() & 7) == 0)
897 _lookActionBitmapIndex = getRandomWord() & 7; // Choose an image randomly
898 bitmapName = lookActionBitmaps[_lookActionBitmapIndex];
899 } else
900 bitmapName = "lookn";
901 break;
902
903 case ACTION_TALK:
904 if (withRedOutline) {
905 if (getRandomWord() & 3)
906 bitmapName = "talkh0";
907 else
908 bitmapName = "talkh1";
909 } else
910 bitmapName = "talkn";
911 break;
912
913 case ACTION_OPTIONS:
914 bitmapName = "options";
915 break;
916
917 case ACTION_WALK:
918 default:
919 bitmapName = "walk";
920 break;
921 }
922
923 _gfx->setMouseBitmap(_gfx->loadBitmap(bitmapName));
924 }
925
showQuitGamePrompt(int x,int y)926 void StarTrekEngine::showQuitGamePrompt(int x, int y) {
927 const char *options[] = {
928 "Quit Game",
929 "#GENE\\GENER028#Yes, quit the game.",
930 "#GENE\\GENER008#No, do not quit the game.",
931 ""
932 };
933
934 if (_inQuitGameMenu)
935 return;
936
937 _inQuitGameMenu = true;
938 int val = showText(&StarTrekEngine::readTextFromArray, (uintptr)options, x, y, TEXTCOLOR_YELLOW, true, 0, true);
939 _inQuitGameMenu = false;
940
941 if (val == 0) {
942 // sub_1e70d();
943 _system->quit();
944 }
945 }
946
showGameOverMenu()947 void StarTrekEngine::showGameOverMenu() {
948 const char *options[] = {
949 "Game Over",
950 "#GENE\\GENER006#Load a previously saved game.",
951 "#GENE\\GENER020#Restart the game.",
952 "#GENE\\GENER018#Quit the game.",
953 ""
954 };
955
956 while (true) {
957 _inQuitGameMenu = true;
958 int selected = showText(&StarTrekEngine::readTextFromArray, (uintptr)options, 20, 20, TEXTCOLOR_YELLOW, true, false, true);
959 _inQuitGameMenu = false;
960
961 switch (selected) {
962 case 0: // Load game
963 _gfx->fadeoutScreen();
964 showLoadMenu(); // TODO: this probably manipulates the stack to jump out of this function...
965 _resetGameMode = true;
966 return;
967 case 1: // Restart
968 _gfx->fadeoutScreen();
969 // TODO
970 _resetGameMode = true;
971 return;
972 case 2: // Quit
973 _gfx->fadeoutScreen();
974 _system->quit();
975 return;
976 default:
977 break;
978 }
979 }
980 }
981
showTextConfigurationMenu(bool fromOptionMenu)982 void StarTrekEngine::showTextConfigurationMenu(bool fromOptionMenu) {
983 const char *options[] = { // TODO: languages...
984 "Text display",
985 "Text subtitles.",
986 "Display text until you press enter.",
987 "No text displayed.",
988 ""
989 };
990
991 int val;
992 if (fromOptionMenu || (val = loadTextDisplayMode()) == -1) {
993 val = showText(&StarTrekEngine::readTextFromArray, (uintptr)options, 20, 30, TEXTCOLOR_YELLOW, true, 0, true);
994 saveTextDisplayMode(val);
995 }
996
997 switch (val) {
998 case 0:
999 _textDisplayMode = TEXTDISPLAY_SUBTITLES;
1000 break;
1001 case 1:
1002 _textDisplayMode = TEXTDISPLAY_WAIT;
1003 break;
1004 case 2:
1005 _textDisplayMode = TEXTDISPLAY_NONE;
1006 break;
1007 }
1008 }
1009
loadTextDisplayMode()1010 int StarTrekEngine::loadTextDisplayMode() {
1011 return -1; // TODO
1012 }
saveTextDisplayMode(int value)1013 void StarTrekEngine::saveTextDisplayMode(int value) {
1014 // TODO;
1015 }
1016
showRepublicMap(int16 arg0,int16 turbolift)1017 void StarTrekEngine::showRepublicMap(int16 arg0, int16 turbolift) {
1018 _gfx->fadeoutScreen();
1019 _sound->stopAllVocSounds();
1020
1021 bool spriteLoaded = false;
1022 int16 clickedArea = 0;
1023
1024 actorFunc1();
1025 _gfx->pushSprites();
1026
1027 if (!_awayMission.veng.showedRepublicMapFirstTime) {
1028 _gfx->setBackgroundImage(_gfx->loadBitmap("veng9b"));
1029 _gfx->copyBackgroundScreen();
1030 _system->updateScreen();
1031 _system->delayMillis(10);
1032 _gfx->setPri(15);
1033 _gfx->fadeinScreen();
1034
1035 // TODO: hide mouse sprite?
1036
1037 bool exitLoop = 0;
1038 int16 var54 = 0x2d;
1039
1040 while (!exitLoop) {
1041 TrekEvent event;
1042 if (!popNextEvent(&event))
1043 continue;
1044
1045 switch (event.type) {
1046 case TREKEVENT_TICK:
1047 if (--var54 == 0)
1048 exitLoop = true;
1049 break;
1050
1051 case TREKEVENT_LBUTTONDOWN:
1052 case TREKEVENT_RBUTTONDOWN:
1053 exitLoop = true;
1054 break;
1055
1056 case TREKEVENT_KEYDOWN:
1057 switch (event.kbd.keycode) {
1058 case Common::KEYCODE_ESCAPE:
1059 case Common::KEYCODE_RETURN:
1060 case Common::KEYCODE_KP_ENTER:
1061 case Common::KEYCODE_SPACE:
1062 exitLoop = true;
1063 break;
1064
1065 default:
1066 break;
1067 }
1068 break;
1069
1070 default:
1071 break;
1072 }
1073 }
1074
1075 // BUGFIX: Original game used variable "scannedComputerBank" (0x32) instead of
1076 // "showedRepublicMapFirstTime" (0x33), which is used elsewhere. Byte 0x33 is
1077 // otherwise unused, so maybe this is a weird off-by-1 error.
1078 // The effective result is that scanning the computer bank would cause the preview
1079 // of the map screen to not appear.
1080 _awayMission.veng.showedRepublicMapFirstTime = true;
1081
1082 _gfx->fadeoutScreen();
1083 }
1084
1085 _gfx->setBackgroundImage(_gfx->loadBitmap("veng9"));
1086 _gfx->copyBackgroundScreen();
1087 _system->updateScreen();
1088 _system->delayMillis(10);
1089 _gfx->setPri(15);
1090
1091 Sprite someSprite;
1092 _gfx->drawAllSprites();
1093 _gfx->warpMouse(_gfx->getMousePos().x, 96);
1094 _gfx->fadeinScreen();
1095
1096 bool exitLoop = false;
1097
1098 while (!exitLoop) {
1099 TrekEvent event;
1100 if (!popNextEvent(&event))
1101 continue;
1102
1103 switch (event.type) {
1104 case TREKEVENT_TICK:
1105 _frameIndex++;
1106 // sub_12fff(); // TODO
1107 _gfx->drawAllSprites();
1108 break;
1109
1110 case TREKEVENT_LBUTTONDOWN: {
1111 lclick:
1112 clickedArea = getRepublicMapAreaOrFailure(turbolift);
1113 if (clickedArea == 0) {
1114 } else if (clickedArea == 6) {
1115 Common::String text = "#GENE\\GENE_F14#Turbolift access is blocked by an extremely high radiation level.";
1116 showTextbox("Mr. Spock", text, 50, 50, TEXTCOLOR_YELLOW, 0); // ENHANCEMENT: Speaker is Spock
1117 } else if (clickedArea == 7) {
1118 Common::String text = "#GENE\\GENE_F15#This turbolift cannot reach that area of the ship.";
1119 showTextbox("Mr. Spock", text, 50, 50, TEXTCOLOR_YELLOW, 0); // ENHANCEMENT: Speaker is Spock
1120 } else
1121 exitLoop = true;
1122 break;
1123 }
1124
1125 case TREKEVENT_MOUSEMOVE: {
1126 if (_gfx->getMousePos().y < 96) // TODO: more elegant solution
1127 _gfx->warpMouse(_gfx->getMousePos().x, 96);
1128
1129 clickedArea = getRepublicMapAreaAtMouse();
1130 if (clickedArea != 0) {
1131 if (!spriteLoaded) {
1132 _gfx->addSprite(&someSprite);
1133 someSprite.setXYAndPriority(3, 168, 15);
1134 someSprite.bitmap = _gfx->loadBitmap(Common::String::format("turbo%d", clickedArea));
1135 spriteLoaded = true;
1136 }
1137 } else {
1138 if (spriteLoaded) {
1139 someSprite.dontDrawNextFrame();
1140 _gfx->drawAllSprites();
1141 _gfx->delSprite(&someSprite);
1142 someSprite.bitmap.reset();
1143 spriteLoaded = false;
1144 }
1145 }
1146 break;
1147 }
1148
1149 case TREKEVENT_KEYDOWN:
1150 switch (event.kbd.keycode) {
1151 case Common::KEYCODE_RETURN:
1152 case Common::KEYCODE_KP_ENTER:
1153 case Common::KEYCODE_F1:
1154 goto lclick;
1155
1156 default:
1157 break;
1158 }
1159 break;
1160
1161 default:
1162 break;
1163 }
1164 }
1165
1166 _gfx->fadeoutScreen();
1167 someSprite.bitmap.reset();
1168 _gfx->popSprites();
1169
1170 _gfx->loadPri(getScreenName());
1171 _gfx->setBackgroundImage(_gfx->loadBitmap(getScreenName()));
1172 _gfx->copyBackgroundScreen();
1173 _system->updateScreen();
1174 _system->delayMillis(10);
1175
1176 _gfx->drawAllSprites();
1177
1178 int16 roomIndex, spawnIndex;
1179 if (clickedArea == 1) {
1180 roomIndex = 0;
1181 spawnIndex = 1;
1182 } else if (clickedArea == 2) {
1183 roomIndex = 1;
1184 spawnIndex = 1;
1185 } else if (clickedArea == 3 && turbolift == 0) {
1186 roomIndex = 3;
1187 spawnIndex = 1;
1188 } else if (clickedArea == 3 && turbolift == 1) {
1189 roomIndex = 3;
1190 spawnIndex = 0;
1191 } else if (clickedArea == 4) {
1192 roomIndex = 5;
1193 spawnIndex = 1;
1194 } else if (clickedArea == 5) {
1195 roomIndex = 7;
1196 spawnIndex = 1;
1197 } else {
1198 warning("Unknown room selected");
1199 roomIndex = 0;
1200 spawnIndex = 1;
1201 }
1202
1203 _roomIndexToLoad = roomIndex;
1204 _spawnIndexToLoad = spawnIndex;
1205 }
1206
getRepublicMapAreaAtMouse()1207 int StarTrekEngine::getRepublicMapAreaAtMouse() {
1208 Common::Point mouse = _gfx->getMousePos();
1209
1210 if (mouse.x >= 0x7f && mouse.x <= 0x91 && mouse.y >= 0x78 && mouse.y <= 0x7b)
1211 return 1;
1212 else if (mouse.x >= 0x6e && mouse.x <= 0x7e && mouse.y >= 0x83 && mouse.y <= 0x87)
1213 return 2;
1214 else if (mouse.x >= 0x95 && mouse.x <= 0xad && mouse.y >= 0x8f && mouse.y <= 0x93)
1215 return 3;
1216 else if (mouse.x >= 0xef && mouse.x <= 0xfd && mouse.y >= 0x98 && mouse.y <= 0xa0)
1217 return 4;
1218 else if (mouse.x >= 0x6b && mouse.x <= 0x80 && mouse.y >= 0xa3 && mouse.y <= 0xa7)
1219 return 5;
1220 else if (mouse.x >= 0x6e && mouse.x <= 0x88 && mouse.y >= 0xab && mouse.y <= 0xaf)
1221 return 6;
1222 else
1223 return 0;
1224 }
1225
getRepublicMapAreaOrFailure(int16 turbolift)1226 int StarTrekEngine::getRepublicMapAreaOrFailure(int16 turbolift) {
1227 Common::Point mouse = _gfx->getMousePos();
1228
1229 if (mouse.x >= 0x7f && mouse.x <= 0x91 && mouse.y >= 0x78 && mouse.y <= 0x7b)
1230 return turbolift == 0 ? 1 : 7;
1231 else if (mouse.x >= 0x6e && mouse.x <= 0x7e && mouse.y >= 0x83 && mouse.y <= 0x87)
1232 return turbolift == 0 ? 2 : 7;
1233 else if (mouse.x >= 0x95 && mouse.x <= 0xad && mouse.y >= 0x8f && mouse.y <= 0x93)
1234 return 3;
1235 else if (mouse.x >= 0xef && mouse.x <= 0xfd && mouse.y >= 0x98 && mouse.y <= 0xa0)
1236 return turbolift == 1 ? 4 : 7;
1237 else if (mouse.x >= 0x6b && mouse.x <= 0x80 && mouse.y >= 0xa3 && mouse.y <= 0xa7)
1238 return turbolift == 1 ? 5 : 7;
1239 else if (mouse.x >= 0x6e && mouse.x <= 0x88 && mouse.y >= 0xab && mouse.y <= 0xaf)
1240 return 6;
1241 return 0;
1242 }
1243
1244 } // End of namespace StarTrek
1245