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 // Game interface module
24 #include "saga/saga.h"
25
26 #include "saga/gfx.h"
27 #include "saga/actor.h"
28 #include "saga/console.h"
29 #include "saga/displayinfo.h"
30 #include "saga/events.h"
31 #include "saga/font.h"
32 #include "saga/objectmap.h"
33 #include "saga/isomap.h"
34 #include "saga/itedata.h"
35 #include "saga/music.h"
36 #include "saga/puzzle.h"
37 #include "saga/render.h"
38 #include "saga/scene.h"
39 #include "saga/script.h"
40 #include "saga/sound.h"
41 #include "saga/sprite.h"
42 #include "saga/resource.h"
43
44 #include "saga/interface.h"
45
46 #include "common/config-manager.h"
47 #include "common/system.h"
48 #include "common/timer.h"
49
50 namespace Saga {
51
52 static const int verbToTextIdITE[] = {
53 kTextWalkTo, kTextLookAt, kTextPickUp, kTextTalkTo,
54 kTextOpen, kTextClose, kTextUse, kTextGive
55 };
56
57 // This maps the internally used string ITE IDs to the LUT strings loaded in IHNM
58 // i.e. id 12 (quit game button) maps to string 14 (Quit game)
59 // The comments are what the actual IHNM string is
60 // For the text string IDs, refer to saga.h, enum TextStringIds
61 static const int IHNMTextStringIdsLUT[56] = {
62 -1, // (Empty)
63 -1, // (Empty)
64 4, // Take
65 6, // Talk to
66 -1,
67 -1,
68 5, // Use
69 8, // Give
70 10, // Options
71 11, // Test
72 12, // Demo
73 13, // Help
74 14, // Quit Game
75 16, // Fast
76 18, // Slow
77 20, // On
78 21, // Off
79 15, // Continue Playing
80 22, // Load
81 23, // Save
82 24, // Game Options
83 25, // Reading Speed
84 26, // Music
85 27, // Sound
86 32, // Cancel
87 33, // Quit
88 34, // OK
89 17, // Mid
90 19, // Click
91 36, // 10%
92 37, // 20%
93 38, // 30%
94 39, // 40%
95 40, // 50%
96 41, // 60%
97 42, // 70%
98 43, // 80%
99 44, // 90%
100 45, // Max
101 -1,
102 -1,
103 -1,
104 -1,
105 -1,
106 -1,
107 -1,
108 -1,
109 -1,
110 -1,
111 -1,
112 -1,
113 -1,
114 28, // Voices
115 29, // Text
116 30, // Audio
117 31 // Both
118 };
119
120 #define buttonRes0 0x42544E00
121 #define buttonRes1 0x42544E01
122
Interface(SagaEngine * vm)123 Interface::Interface(SagaEngine *vm) : _vm(vm) {
124 ByteArray resourceData;
125 int i;
126
127 // Load interface module resource file context
128 _interfaceContext = _vm->_resource->getContext(GAME_RESOURCEFILE);
129 if (_interfaceContext == NULL) {
130 error("Interface::Interface() resource context not found");
131 }
132
133 // Main panel
134 _mainPanel.buttons = _vm->getDisplayInfo().mainPanelButtons;
135 _mainPanel.buttonsCount = _vm->getDisplayInfo().mainPanelButtonsCount;
136
137 for (i = 0; i < kVerbTypeIdsMax; i++) {
138 _verbTypeToPanelButton[i] = NULL;
139 }
140
141 for (i = 0; i < _mainPanel.buttonsCount; i++) {
142 if (_mainPanel.buttons[i].type == kPanelButtonVerb) {
143 _verbTypeToPanelButton[_mainPanel.buttons[i].id] = &_mainPanel.buttons[i];
144 }
145 }
146
147 _vm->_resource->loadResource(_interfaceContext, _vm->getResourceDescription()->mainPanelResourceId, resourceData);
148 _vm->decodeBGImage(resourceData, _mainPanel.image, &_mainPanel.imageWidth, &_mainPanel.imageHeight);
149
150 // Converse panel
151 _conversePanel.buttons = _vm->getDisplayInfo().conversePanelButtons;
152 _conversePanel.buttonsCount = _vm->getDisplayInfo().conversePanelButtonsCount;
153
154 _vm->_resource->loadResource(_interfaceContext, _vm->getResourceDescription()->conversePanelResourceId, resourceData);
155 _vm->decodeBGImage(resourceData, _conversePanel.image, &_conversePanel.imageWidth, &_conversePanel.imageHeight);
156
157 // Option panel
158 if (!_vm->_script->isNonInteractiveDemo()) {
159 _optionPanel.buttons = _vm->getDisplayInfo().optionPanelButtons;
160 _optionPanel.buttonsCount = _vm->getDisplayInfo().optionPanelButtonsCount;
161
162 _vm->_resource->loadResource(_interfaceContext, _vm->getResourceDescription()->optionPanelResourceId, resourceData);
163 _vm->decodeBGImage(resourceData, _optionPanel.image, &_optionPanel.imageWidth, &_optionPanel.imageHeight);
164 } else {
165 _optionPanel.buttons = NULL;
166 _optionPanel.buttonsCount = 0;
167 _optionPanel.sprites.clear();
168 }
169
170 #ifdef ENABLE_IHNM
171 // Quit panel
172 if (_vm->getGameId() == GID_IHNM) {
173 _quitPanel.buttons = _vm->getDisplayInfo().quitPanelButtons;
174 _quitPanel.buttonsCount = _vm->getDisplayInfo().quitPanelButtonsCount;
175
176 _vm->_resource->loadResource(_interfaceContext, _vm->getResourceDescription()->warningPanelResourceId, resourceData);
177 _vm->decodeBGImage(resourceData, _quitPanel.image, &_quitPanel.imageWidth, &_quitPanel.imageHeight);
178 }
179
180 // Save panel
181 if (_vm->getGameId() == GID_IHNM) {
182 _savePanel.buttons = _vm->getDisplayInfo().savePanelButtons;
183 _savePanel.buttonsCount = _vm->getDisplayInfo().savePanelButtonsCount;
184
185 _vm->_resource->loadResource(_interfaceContext, _vm->getResourceDescription()->warningPanelResourceId, resourceData);
186 _vm->decodeBGImage(resourceData, _savePanel.image, &_savePanel.imageWidth, &_savePanel.imageHeight);
187 }
188
189 // Load panel
190 if (_vm->getGameId() == GID_IHNM) {
191 _loadPanel.buttons = _vm->getDisplayInfo().loadPanelButtons;
192 _loadPanel.buttonsCount = _vm->getDisplayInfo().loadPanelButtonsCount;
193
194 _vm->_resource->loadResource(_interfaceContext, _vm->getResourceDescription()->warningPanelResourceId, resourceData);
195 _vm->decodeBGImage(resourceData, _loadPanel.image, &_loadPanel.imageWidth, &_loadPanel.imageHeight);
196 }
197 #endif
198
199 // Main panel sprites
200 _vm->_sprite->loadList(_vm->getResourceDescription()->mainPanelSpritesResourceId, _mainPanel.sprites);
201 if (!_vm->_script->isNonInteractiveDemo()) {
202 // Option panel sprites
203 _vm->_sprite->loadList(_vm->getResourceDescription()->optionPanelSpritesResourceId, _optionPanel.sprites);
204 // Save panel sprites
205 _vm->_sprite->loadList(_vm->getResourceDescription()->warningPanelSpritesResourceId, _savePanel.sprites);
206 // Load panel sprites
207 _vm->_sprite->loadList(_vm->getResourceDescription()->warningPanelSpritesResourceId, _loadPanel.sprites);
208 // Quit panel sprites
209 _vm->_sprite->loadList(_vm->getResourceDescription()->warningPanelSpritesResourceId, _quitPanel.sprites);
210 }
211
212 if (_vm->getGameId() == GID_ITE) {
213 _vm->_sprite->loadList(_vm->getResourceDescription()->defaultPortraitsResourceId, _defPortraits);
214 }
215
216 setPortraitBgColor(0, 0, 0);
217
218 _mainPanel.x = _vm->getDisplayInfo().mainPanelXOffset;
219 _mainPanel.y = _vm->getDisplayInfo().mainPanelYOffset;
220 _mainPanel.currentButton = NULL;
221 _inventoryUpButton = _mainPanel.getButton(_vm->getDisplayInfo().inventoryUpButtonIndex);
222 _inventoryDownButton = _mainPanel.getButton(_vm->getDisplayInfo().inventoryDownButtonIndex);
223
224 _conversePanel.x = _vm->getDisplayInfo().conversePanelXOffset;
225 _conversePanel.y = _vm->getDisplayInfo().conversePanelYOffset;
226 _conversePanel.currentButton = NULL;
227 _converseUpButton = _conversePanel.getButton(_vm->getDisplayInfo().converseUpButtonIndex);
228 _converseDownButton = _conversePanel.getButton(_vm->getDisplayInfo().converseDownButtonIndex);
229
230 _leftPortrait = 0;
231 _rightPortrait = 0;
232
233 _optionPanel.x = _vm->getDisplayInfo().optionPanelXOffset;
234 _optionPanel.y = _vm->getDisplayInfo().optionPanelYOffset;
235 _optionPanel.currentButton = NULL;
236 _optionSaveFileSlider = _optionPanel.getButton(_vm->getDisplayInfo().optionSaveFileSliderIndex);
237 _optionSaveFilePanel = _optionPanel.getButton(_vm->getDisplayInfo().optionSaveFilePanelIndex);
238
239 _quitPanel.x = _vm->getDisplayInfo().quitPanelXOffset;
240 _quitPanel.y = _vm->getDisplayInfo().quitPanelYOffset;
241 _quitPanel.imageWidth = _vm->getDisplayInfo().quitPanelWidth;
242 _quitPanel.imageHeight = _vm->getDisplayInfo().quitPanelHeight;
243 _quitPanel.buttons = _vm->getDisplayInfo().quitPanelButtons;
244 _quitPanel.buttonsCount = _vm->getDisplayInfo().quitPanelButtonsCount;
245 _quitPanel.currentButton = NULL;
246
247 _loadPanel.x = _vm->getDisplayInfo().loadPanelXOffset;
248 _loadPanel.y = _vm->getDisplayInfo().loadPanelYOffset;
249 _loadPanel.imageWidth = _vm->getDisplayInfo().loadPanelWidth;
250 _loadPanel.imageHeight = _vm->getDisplayInfo().loadPanelHeight;
251 _loadPanel.buttons = _vm->getDisplayInfo().loadPanelButtons;
252 _loadPanel.buttonsCount = _vm->getDisplayInfo().loadPanelButtonsCount;
253 _loadPanel.currentButton = NULL;
254
255 _savePanel.x = _vm->getDisplayInfo().savePanelXOffset;
256 _savePanel.y = _vm->getDisplayInfo().savePanelYOffset;
257 _savePanel.imageWidth = _vm->getDisplayInfo().savePanelWidth;
258 _savePanel.imageHeight = _vm->getDisplayInfo().savePanelHeight;
259 _savePanel.buttons = _vm->getDisplayInfo().savePanelButtons;
260 _savePanel.buttonsCount = _vm->getDisplayInfo().savePanelButtonsCount;
261 _saveEdit = _savePanel.getButton(_vm->getDisplayInfo().saveEditIndex);
262 _savePanel.currentButton = NULL;
263
264 _protectPanel.x = _vm->getDisplayInfo().protectPanelXOffset;
265 _protectPanel.y = _vm->getDisplayInfo().protectPanelYOffset;
266 _protectPanel.imageWidth = _vm->getDisplayInfo().protectPanelWidth;
267 _protectPanel.imageHeight = _vm->getDisplayInfo().protectPanelHeight;
268 _protectPanel.buttons = _vm->getDisplayInfo().protectPanelButtons;
269 _protectPanel.buttonsCount = _vm->getDisplayInfo().protectPanelButtonsCount;
270 _protectEdit = _protectPanel.getButton(_vm->getDisplayInfo().protectEditIndex);
271 _protectPanel.currentButton = NULL;
272
273 _active = true;
274 _panelMode = _lockedMode = kPanelNull;
275 _savedMode = -1;
276 _bossMode = -1;
277 _fadeMode = kNoFade;
278 _inMainMode = false;
279 *_statusText = 0;
280 _statusOnceColor = -1;
281
282 _inventoryCount = 0;
283 _inventoryPos = 0;
284 _inventoryStart = 0;
285 _inventoryEnd = 0;
286 _inventoryBox = 0;
287 _saveReminderState = 0;
288
289 _optionSaveFileTop = 0;
290 _optionSaveFileTitleNumber = 0;
291
292 _inventory.resize(ITE_INVENTORY_SIZE);
293
294 _textInput = false;
295 _statusTextInput = false;
296 _statusTextInputState = kStatusTextInputFirstRun;
297
298 _disableAbortSpeeches = false;
299
300 // set save game reminder alarm
301 _vm->getTimerManager()->installTimerProc(&saveReminderCallback, TIMETOSAVE, this, "sagaSaveReminder");
302 }
303
~Interface()304 Interface::~Interface() {
305 _vm->getTimerManager()->removeTimerProc(&saveReminderCallback);
306 }
307
saveReminderCallback(void * refCon)308 void Interface::saveReminderCallback(void *refCon) {
309 ((Interface *)refCon)->updateSaveReminder();
310 }
311
updateSaveReminder()312 void Interface::updateSaveReminder() {
313 // CHECKME: This is potentially called from a different thread because it is
314 // called from a timer callback. However, it does not seem to take any
315 // precautions to avoid race conditions.
316 if (_active && _panelMode == kPanelMain) {
317 _saveReminderState = _saveReminderState % _vm->getDisplayInfo().saveReminderNumSprites + 1;
318 drawStatusBar();
319 _vm->getTimerManager()->removeTimerProc(&saveReminderCallback);
320 _vm->getTimerManager()->installTimerProc(&saveReminderCallback, ((_vm->getGameId() == GID_ITE) ? TIMETOBLINK_ITE : TIMETOBLINK_IHNM), this, "sagaSaveReminder");
321 }
322 }
323
activate()324 int Interface::activate() {
325 if (!_active) {
326 _active = true;
327 _vm->_script->_skipSpeeches = false;
328 _vm->_actor->_protagonist->_targetObject = ID_NOTHING;
329 unlockMode();
330 if (_panelMode == kPanelMain || _panelMode == kPanelChapterSelection) {
331 _saveReminderState = 1;
332 } else if (_panelMode == kPanelNull && _vm->isIHNMDemo()) {
333 _saveReminderState = 1;
334 }
335 _vm->_gfx->showCursor(true);
336 draw();
337 _vm->_render->setFullRefresh(true);
338 }
339
340 return SUCCESS;
341 }
342
deactivate()343 int Interface::deactivate() {
344 if (_active) {
345 _active = false;
346 lockMode();
347 setMode(kPanelNull);
348 }
349 _vm->_gfx->showCursor(false);
350
351 return SUCCESS;
352 }
353
rememberMode()354 void Interface::rememberMode() {
355 debug(1, "rememberMode(%d)", _panelMode);
356
357 _savedMode = _panelMode;
358 }
359
restoreMode(bool draw_)360 void Interface::restoreMode(bool draw_) {
361 debug(1, "restoreMode(%d)", _savedMode);
362
363 // If _savedMode is -1 by a race condition, set it to kPanelMain
364 if (_savedMode == -1)
365 _savedMode = kPanelMain;
366
367 _panelMode = _savedMode;
368 _savedMode = -1;
369
370 if (draw_)
371 draw();
372 }
373
setMode(int mode)374 void Interface::setMode(int mode) {
375 debug(1, "Interface::setMode %i", mode);
376
377 if (mode == kPanelMain) {
378 _inMainMode = true;
379 _saveReminderState = 1;
380 } else if (mode == kPanelChapterSelection) {
381 _saveReminderState = 1;
382 } else if (mode == kPanelNull) {
383 if (_vm->isIHNMDemo()) {
384 _inMainMode = true;
385 _saveReminderState = 1;
386 }
387 } else if (mode == kPanelOption) {
388 // Show the cursor if it's hidden
389 _vm->_gfx->showCursor(true);
390 } else {
391 if (mode == kPanelConverse) {
392 _inMainMode = false;
393 }
394
395 _saveReminderState = 0;
396 }
397
398 _panelMode = mode;
399
400 switch (_panelMode) {
401 case kPanelMain:
402 // FIXME: Implement IHNM differences from ExecuteInventoryPanel for IHNM (though I believe they're already
403 // implemented)
404
405 _mainPanel.currentButton = NULL;
406 break;
407 case kPanelConverse:
408 _conversePanel.currentButton = NULL;
409 converseDisplayText();
410 break;
411 case kPanelOption:
412 _optionPanel.currentButton = NULL;
413 _vm->fillSaveList();
414 calcOptionSaveSlider();
415 if (_optionSaveFileTitleNumber >= _vm->getDisplayInfo().optionSaveFileVisible) {
416 _optionSaveFileTitleNumber = _vm->getDisplayInfo().optionSaveFileVisible - 1;
417 }
418 break;
419 case kPanelLoad:
420 _loadPanel.currentButton = NULL;
421 break;
422 case kPanelQuit:
423 _quitPanel.currentButton = NULL;
424 break;
425 case kPanelSave:
426 _savePanel.currentButton = NULL;
427 _textInputMaxWidth = _saveEdit->width - 10;
428 _textInput = true;
429 _textInputStringLength = strlen(_textInputString);
430 _textInputPos = _textInputStringLength + 1;
431 break;
432 case kPanelMap:
433 mapPanelShow();
434 break;
435 case kPanelSceneSubstitute:
436 _vm->_render->setFlag(RF_DEMO_SUBST);
437 _vm->_gfx->getCurrentPal(_mapSavedPal);
438 break;
439 case kPanelChapterSelection:
440 break;
441 case kPanelBoss:
442 _vm->_render->setFlag(RF_DEMO_SUBST);
443 break;
444 case kPanelProtect:
445 if (_vm->getGameId() == GID_ITE) {
446 // This is used as the copy protection panel in ITE
447 _protectPanel.currentButton = NULL;
448 _textInputMaxWidth = _protectEdit->width - 10;
449 _textInput = true;
450 _textInputString[0] = 0;
451 _textInputStringLength = 0;
452 _textInputPos = _textInputStringLength + 1;
453 } else {
454 // In the IHNM demo, this panel mode is set by the scripts
455 // to flip through the pages of the help system
456 }
457 break;
458 default:
459 break;
460 }
461
462 draw();
463 _vm->_render->setFullRefresh(true);
464 }
465
processAscii(Common::KeyState keystate)466 bool Interface::processAscii(Common::KeyState keystate) {
467 // TODO: Checking for Esc and Enter below is a bit hackish, and
468 // probably only works with the English version. Maybe we should
469 // add a flag to the button so it can indicate if it's the default
470 // or cancel button?
471 uint16 ascii = keystate.ascii;
472 int i;
473 PanelButton *panelButton;
474 if (_statusTextInput) {
475 processStatusTextInput(keystate);
476 return true;
477 }
478
479 switch (_panelMode) {
480 case kPanelNull:
481 if (keystate.keycode == Common::KEYCODE_ESCAPE) {
482 if (_vm->_scene->isInIntro()) {
483 _vm->_scene->skipScene();
484 } else {
485 if (!_disableAbortSpeeches)
486 _vm->_actor->abortAllSpeeches();
487 }
488 return true;
489 }
490
491 #ifdef ENABLE_IHNM
492 if (_vm->_scene->isNonInteractiveIHNMDemoPart())
493 _vm->_scene->showIHNMDemoSpecialScreen();
494 #endif
495 break;
496 case kPanelCutaway:
497 if (keystate.keycode == Common::KEYCODE_ESCAPE) {
498 if (!_disableAbortSpeeches)
499 _vm->_actor->abortAllSpeeches();
500 _vm->_scene->cutawaySkip();
501 return true;
502 }
503
504 #ifdef ENABLE_INHM
505 if (_vm->_scene->isNonInteractiveIHNMDemoPart())
506 _vm->_scene->showIHNMDemoSpecialScreen();
507 #endif
508 break;
509 case kPanelVideo:
510 if (keystate.keycode == Common::KEYCODE_ESCAPE) {
511 if (_vm->_scene->isInIntro()) {
512 _vm->_scene->skipScene();
513 } else {
514 if (!_disableAbortSpeeches)
515 _vm->_actor->abortAllSpeeches();
516 }
517 _vm->_scene->cutawaySkip();
518 return true;
519 }
520
521 #ifdef ENABLE_IHNM
522 if (_vm->_scene->isNonInteractiveIHNMDemoPart())
523 _vm->_scene->showIHNMDemoSpecialScreen();
524 #endif
525 break;
526 case kPanelOption:
527 // TODO: check input dialog keys
528 if (keystate.keycode == Common::KEYCODE_ESCAPE || keystate.keycode == Common::KEYCODE_RETURN) { // Esc or Enter
529 ascii = 'c'; //continue
530 }
531
532 for (i = 0; i < _optionPanel.buttonsCount; i++) {
533 panelButton = &_optionPanel.buttons[i];
534 if (panelButton->type == kPanelButtonOption) {
535 if (panelButton->ascii == ascii) {
536 setOption(panelButton);
537 return true;
538 }
539 }
540 }
541 break;
542 case kPanelSave:
543 if (_textInput && processTextInput(keystate)) {
544 return true;
545 }
546
547 if (keystate.keycode == Common::KEYCODE_ESCAPE) {
548 ascii = 'c'; // cancel
549 } else if (keystate.keycode == Common::KEYCODE_RETURN) { // Enter
550 ascii = 's'; // save
551 }
552
553 for (i = 0; i < _savePanel.buttonsCount; i++) {
554 panelButton = &_savePanel.buttons[i];
555 if (panelButton->type == kPanelButtonSave) {
556 if (panelButton->ascii == ascii) {
557 setSave(panelButton);
558 return true;
559 }
560 }
561 }
562 break;
563 case kPanelQuit:
564 if (keystate.keycode == Common::KEYCODE_ESCAPE) {
565 ascii = 'c'; // cancel
566 } else if (keystate.keycode == Common::KEYCODE_RETURN) { // Enter
567 ascii = 'q'; // quit
568 }
569
570 for (i = 0; i < _quitPanel.buttonsCount; i++) {
571 panelButton = &_quitPanel.buttons[i];
572 if (panelButton->type == kPanelButtonQuit) {
573 if (panelButton->ascii == ascii) {
574 setQuit(panelButton);
575 return true;
576 }
577 }
578 }
579 break;
580 case kPanelLoad:
581 for (i = 0; i < _loadPanel.buttonsCount; i++) {
582 panelButton = &_loadPanel.buttons[i];
583 if (panelButton->type == kPanelButtonLoad) {
584 if (panelButton->ascii == ascii) {
585 setLoad(panelButton);
586 return true;
587 }
588 }
589 }
590 break;
591 case kPanelMain:
592 for (i = 0; i < _mainPanel.buttonsCount; i++) {
593 panelButton = &_mainPanel.buttons[i];
594 if (panelButton->ascii == ascii) {
595 if (panelButton->type == kPanelButtonVerb) {
596 _vm->_script->setVerb(panelButton->id);
597 }
598 if (panelButton->type == kPanelButtonArrow) {
599 inventoryChangePos(panelButton->id);
600 }
601 return true;
602 }
603 }
604 if (keystate.keycode == Common::KEYCODE_o && keystate.hasFlags(Common::KBD_CTRL)) { // ctrl-o
605 if (_saveReminderState > 0) {
606 setMode(kPanelOption);
607 return true;
608 }
609 }
610 break;
611 case kPanelConverse:
612 switch (ascii) {
613 case 'x':
614 setMode(kPanelMain);
615 if (_vm->_scene->isITEPuzzleScene())
616 _vm->_puzzle->exitPuzzle();
617 break;
618
619 case 'u':
620 converseChangePos(-1);
621 break;
622
623 case 'd':
624 converseChangePos(1);
625 break;
626
627 case '1':
628 case '2':
629 case '3':
630 case '4':
631 case '5':
632 case '6':
633 case '7':
634 case '8':
635 case '9':
636 converseSetPos(ascii);
637 break;
638
639 default:
640 break;
641 }
642 break;
643 case kPanelMap:
644 mapPanelClean();
645 break;
646 case kPanelSceneSubstitute:
647 if (keystate.keycode == Common::KEYCODE_RETURN) {
648 _vm->_render->clearFlag(RF_DEMO_SUBST);
649 _vm->_gfx->setPalette(_mapSavedPal);
650 setMode(kPanelMain);
651 _vm->_script->setNoPendingVerb();
652 } else if (ascii == 'q' || ascii == 'Q') {
653 _vm->quitGame();
654 }
655 break;
656 case kPanelBoss:
657 _vm->_render->clearFlag(RF_DEMO_SUBST);
658 keyBossExit();
659 break;
660 case kPanelProtect:
661 if (_vm->getGameId() == GID_ITE) {
662 if (_textInput && processTextInput(keystate)) {
663 return true;
664 }
665
666 if (keystate.keycode == Common::KEYCODE_ESCAPE || keystate.keycode == Common::KEYCODE_RETURN) {
667 _vm->_script->wakeUpThreads(kWaitTypeRequest);
668 _vm->_interface->setMode(kPanelMain);
669
670 _protectHash = 0;
671
672 for (char *p = _textInputString; *p; p++)
673 _protectHash = (_protectHash << 1) + toupper(*p);
674 }
675 } else {
676 // In the IHNM demo, this panel mode is set by the scripts
677 // to flip through the pages of the help system
678 }
679 break;
680 case kPanelPlacard:
681 #ifdef ENABLE_IHNM
682 if (_vm->getGameId() == GID_IHNM) {
683 // Any keypress here returns the user back to the game
684 if (!_vm->isIHNMDemo()) {
685 _vm->_scene->clearPsychicProfile();
686 } else {
687 setMode(kPanelConverse);
688 _vm->_scene->_textList.clear();
689 _vm->_script->wakeUpThreads(kWaitTypeDelay);
690 }
691 }
692 #endif
693 break;
694 default:
695 break;
696 }
697 return false;
698 }
699
setStatusText(const char * text,int statusColor)700 void Interface::setStatusText(const char *text, int statusColor) {
701 if (_vm->getGameId() == GID_IHNM) {
702 // Don't show the status text for the IHNM chapter selection screens (chapter 8), or
703 // scene 0 (IHNM demo introduction)
704 if (_vm->_scene->currentChapterNumber() == 8 || _vm->_scene->currentSceneNumber() == 0)
705 return;
706 }
707
708 assert(text != NULL);
709 assert(strlen(text) < STATUS_TEXT_LEN);
710
711 if (_vm->_render->getFlags() & RF_MAP || _vm->_interface->getMode() == kPanelPlacard)
712 return;
713
714 Common::strlcpy(_statusText, text, STATUS_TEXT_LEN);
715 _statusOnceColor = statusColor;
716 drawStatusBar();
717 }
718
loadScenePortraits(int resourceId)719 void Interface::loadScenePortraits(int resourceId) {
720 _scenePortraits.clear();
721
722 _vm->_sprite->loadList(resourceId, _scenePortraits);
723 }
724
drawVerbPanel(PanelButton * panelButton)725 void Interface::drawVerbPanel(PanelButton* panelButton) {
726 PanelButton * rightButtonVerbPanelButton;
727 PanelButton * currentVerbPanelButton;
728 KnownColor textColor;
729 int spriteNumber;
730 Point point;
731
732 rightButtonVerbPanelButton = getPanelButtonByVerbType(_vm->_script->getRightButtonVerb());
733 currentVerbPanelButton = getPanelButtonByVerbType(_vm->_script->getCurrentVerb());
734
735 if (panelButton->state) {
736 textColor = kKnownColorVerbTextActive;
737 } else if (panelButton == rightButtonVerbPanelButton) {
738 textColor = kKnownColorVerbTextActive;
739 } else {
740 textColor = kKnownColorVerbText;
741 }
742
743 if (panelButton == currentVerbPanelButton) {
744 spriteNumber = panelButton->downSpriteNumber;
745 } else {
746 spriteNumber = panelButton->upSpriteNumber;
747 }
748 point.x = _mainPanel.x + panelButton->xOffset;
749 point.y = _mainPanel.y + panelButton->yOffset;
750
751 _vm->_sprite->draw(_mainPanel.sprites, spriteNumber, point, 256);
752
753 drawVerbPanelText(panelButton, textColor, kKnownColorVerbTextShadow);
754 }
755
draw()756 void Interface::draw() {
757 Point leftPortraitPoint;
758 Point rightPortraitPoint;
759 Rect rect;
760
761 if (_vm->_scene->isInIntro() || _fadeMode == kFadeOut)
762 return;
763
764 drawStatusBar();
765
766 if (_panelMode == kPanelMain || _panelMode == kPanelMap ||
767 (_panelMode == kPanelNull && _vm->isIHNMDemo())) {
768 _mainPanel.getRect(rect);
769 _vm->_gfx->drawRegion(rect, _mainPanel.image.getBuffer());
770
771 for (int i = 0; i < kVerbTypeIdsMax; i++) {
772 if (_verbTypeToPanelButton[i] != NULL) {
773 drawVerbPanel(_verbTypeToPanelButton[i]);
774 }
775 }
776 } else if (_panelMode == kPanelConverse) {
777 _conversePanel.getRect(rect);
778 _vm->_gfx->drawRegion(rect, _conversePanel.image.getBuffer());
779 converseDisplayTextLines();
780 }
781
782 if (_panelMode == kPanelMain || _panelMode == kPanelConverse ||
783 _lockedMode == kPanelMain || _lockedMode == kPanelConverse ||
784 (_panelMode == kPanelNull && _vm->isIHNMDemo())) {
785 leftPortraitPoint.x = _mainPanel.x + _vm->getDisplayInfo().leftPortraitXOffset;
786 leftPortraitPoint.y = _mainPanel.y + _vm->getDisplayInfo().leftPortraitYOffset;
787 _vm->_sprite->draw(_defPortraits, _leftPortrait, leftPortraitPoint, 256);
788 }
789
790 if (!_inMainMode && _vm->getDisplayInfo().rightPortraitXOffset >= 0) { //FIXME: should we change !_inMainMode to _panelMode == kPanelConverse ?
791 rightPortraitPoint.x = _mainPanel.x + _vm->getDisplayInfo().rightPortraitXOffset;
792 rightPortraitPoint.y = _mainPanel.y + _vm->getDisplayInfo().rightPortraitYOffset;
793
794 // This looks like hack - particularly since it's only done for
795 // the right-side portrait - and perhaps it is! But as far as I
796 // can tell this is what the original engine does. And it keeps
797 // ITE from crashing when entering the Elk King's court.
798
799 if (_rightPortrait >= (int)_scenePortraits.size())
800 _rightPortrait = 0;
801
802 _vm->_sprite->draw(_scenePortraits, _rightPortrait, rightPortraitPoint, 256);
803 }
804
805 drawInventory();
806 }
807
calcOptionSaveSlider()808 void Interface::calcOptionSaveSlider() {
809 int totalFiles = _vm->getSaveFilesCount();
810 int visibleFiles = _vm->getDisplayInfo().optionSaveFileVisible;
811 int height = _optionSaveFileSlider->height;
812 int sliderHeight = 13; // IHNM's save file list slider has a fixed height
813 int pos;
814
815 if (totalFiles < visibleFiles) {
816 totalFiles = visibleFiles;
817 }
818
819 if (_vm->getGameId() == GID_ITE) {
820 // ITE's save file list slider has a dynamically computed height, depending on
821 // the number of save games
822 sliderHeight = visibleFiles * height / totalFiles;
823 }
824
825 if (sliderHeight < 7) {
826 sliderHeight = 7;
827 }
828
829 if (totalFiles - visibleFiles <= 0) {
830 pos = 0;
831 } else {
832 pos = _optionSaveFileTop * (height - sliderHeight) / (totalFiles - visibleFiles);
833 }
834 _optionPanel.calcPanelButtonRect(_optionSaveFileSlider, _optionSaveRectTop);
835 _optionSaveRectBottom = _optionSaveRectSlider = _optionSaveRectTop;
836
837 _optionSaveRectTop.bottom = _optionSaveRectTop.top + pos;
838 _optionSaveRectTop.top++;
839 _optionSaveRectTop.right--;
840
841 _optionSaveRectSlider.top = _optionSaveRectTop.bottom;
842 _optionSaveRectSlider.bottom = _optionSaveRectSlider.top + sliderHeight;
843
844 _optionSaveRectBottom.top = _optionSaveRectSlider.bottom;
845 _optionSaveRectBottom.right--;
846 }
847
drawPanelText(InterfacePanel * panel,PanelButton * panelButton)848 void Interface::drawPanelText(InterfacePanel *panel, PanelButton *panelButton) {
849 const char *text;
850 int textWidth, textHeight;
851 Rect rect;
852 Point textPoint;
853 KnownColor textShadowKnownColor = kKnownColorVerbTextShadow;
854 KnownFont textFont = kKnownFontMedium;
855
856 // Button differs for CD version
857 if (panelButton->id == kTextReadingSpeed && _vm->getGameId() == GID_ITE && !(_vm->getFeatures() & GF_ITE_FLOPPY))
858 return;
859 if (panelButton->id == kTextShowDialog && _vm->getFeatures() & GF_ITE_FLOPPY)
860 return;
861
862 if (_vm->getGameId() == GID_ITE) {
863 text = _vm->getTextString(panelButton->id);
864 textFont = kKnownFontMedium;
865 textShadowKnownColor = kKnownColorVerbTextShadow;
866 } else {
867 if ((panelButton->id < 39 || panelButton->id > 50) && panelButton->id != kTextLoadSavedGame) {
868 // Read non-hardcoded strings from the LUT string table, loaded from the game
869 // data files
870 text = _vm->_script->_mainStrings.getString(IHNMTextStringIdsLUT[panelButton->id]);
871 } else if (panelButton->id == kTextLoadSavedGame) {
872 // a bit of a kludge, but it will do
873 text = _vm->getTextString(52);
874 } else {
875 // Hardcoded strings in IHNM are read from the ITE hardcoded strings
876 text = _vm->getTextString(panelButton->id);
877 }
878 textFont = kKnownFontVerb;
879 textShadowKnownColor = kKnownColorTransparent;
880 }
881
882 panel->calcPanelButtonRect(panelButton, rect);
883 if (_vm->getGameId() == GID_ITE) {
884 textWidth = _vm->_font->getStringWidth(kKnownFontMedium, text, 0, kFontNormal);
885 textHeight = _vm->_font->getHeight(kKnownFontMedium);
886 } else {
887 textWidth = _vm->_font->getStringWidth(kKnownFontVerb, text, 0, kFontNormal);
888 textHeight = _vm->_font->getHeight(kKnownFontVerb);
889 }
890 if (panelButton->xOffset < 0) {
891 // Special case: Centered to dialog. This is used for things like the
892 // title of a dialog.
893 rect.left += 2 + (panel->imageWidth - 1 - textWidth) / 2;
894 } else {
895 // The standard case is used for the things that look a bit like buttons
896 // but are not clickable, e.g. texts like "Music", "Sound", etc.
897 if (_vm->getGameId() == GID_ITE && _vm->getPlatform() != Common::kPlatformPC98) {
898 rect.left = rect.right - textWidth - 3;
899 } else {
900 rect.left = (rect.right + rect.left - textWidth) / 2;
901 if (_vm->getGameId() == GID_ITE)
902 rect.left += 4;
903 }
904 rect.top = (rect.top + rect.bottom - textHeight) / 2;
905 }
906
907 textPoint.x = rect.left;
908 textPoint.y = rect.top + (_vm->getPlatform() == Common::kPlatformPC98 ? 0 : 1);
909
910 _vm->_font->textDraw(textFont, text, textPoint,
911 _vm->KnownColor2ColorId(kKnownColorVerbText), _vm->KnownColor2ColorId(textShadowKnownColor), _vm->getPlatform() == Common::kPlatformPC98 ? kFontOutline : kFontShadow);
912 }
913
drawOption()914 void Interface::drawOption() {
915 const char *text;
916 int fontHeight;
917 uint idx;
918 int fgColor;
919 int bgColor;
920 Rect rect;
921 Rect rect2;
922 PanelButton *panelButton;
923 Point textPoint;
924 Point sliderPoint;
925 int spritenum = 0;
926
927 _optionPanel.getRect(rect);
928 _vm->_gfx->drawRegion(rect, _optionPanel.image.getBuffer());
929
930 for (int i = 0; i < _optionPanel.buttonsCount; i++) {
931 panelButton = &_optionPanel.buttons[i];
932
933 if (panelButton->type == kPanelButtonOption) {
934 if (_vm->getGameId() == GID_ITE) {
935 drawPanelButtonText(&_optionPanel, panelButton);
936 } else {
937 drawPanelButtonText(&_optionPanel, panelButton, spritenum);
938 spritenum += 2; // 2 sprites per button (lit and unlit)
939 }
940 }
941 if (panelButton->type == kPanelButtonOptionText) {
942 drawPanelText(&_optionPanel, panelButton);
943 }
944 }
945
946 if (_optionSaveRectTop.height() > 0) {
947 if (_vm->getGameId() == GID_ITE)
948 _vm->_gfx->drawRect(_optionSaveRectTop, kITEColorDarkGrey);
949 }
950
951 if (_vm->getGameId() == GID_ITE) {
952 drawButtonBox(_optionSaveRectSlider, kSlider, _optionSaveFileSlider->state > 0);
953 } else {
954 panelButton = &_optionPanel.buttons[0];
955 sliderPoint.x = _optionPanel.x + panelButton->xOffset;
956 sliderPoint.y = _optionSaveRectSlider.top;
957 _vm->_sprite->draw(_optionPanel.sprites, 0 + _optionSaveFileSlider->state, sliderPoint, 256);
958
959 }
960
961 if (_optionSaveRectBottom.height() > 0) {
962 _vm->_gfx->drawRect(_optionSaveRectBottom, kITEColorDarkGrey);
963 }
964
965 _optionPanel.calcPanelButtonRect(_optionSaveFilePanel, rect);
966 rect.top++;
967 rect2 = rect;
968 fontHeight = _vm->_font->getHeight(kKnownFontSmall);
969 for (uint j = 0; j < _vm->getDisplayInfo().optionSaveFileVisible; j++) {
970 if (_vm->getGameId() == GID_ITE)
971 bgColor = kITEColorDarkGrey0C;
972 else
973 bgColor = _vm->KnownColor2ColorId(kKnownColorBlack);
974 fgColor = kITEColorBrightWhite;
975
976 idx = j + _optionSaveFileTop;
977 if (idx == _optionSaveFileTitleNumber) {
978 SWAP(bgColor, fgColor);
979 }
980 if (idx < _vm->getSaveFilesCount()) {
981 rect2.top = rect.top + j * (fontHeight + 1);
982 rect2.bottom = rect2.top + fontHeight;
983 _vm->_gfx->fillRect(rect2, bgColor);
984 text = _vm->getSaveFile(idx)->name;
985 textPoint.x = rect.left + 1;
986 textPoint.y = rect2.top;
987 if (_vm->getGameId() == GID_ITE)
988 _vm->_font->textDraw(kKnownFontSmall, text, textPoint, fgColor, 0, kFontNormal);
989 else
990 _vm->_font->textDraw(kKnownFontVerb, text, textPoint, fgColor, 0, kFontNormal);
991 }
992 }
993
994 }
995
drawQuit()996 void Interface::drawQuit() {
997 Rect rect;
998 int i;
999 PanelButton *panelButton;
1000
1001 _quitPanel.getRect(rect);
1002 if (_vm->getGameId() == GID_ITE)
1003 drawButtonBox(rect, kButton, false);
1004 else
1005 _vm->_gfx->drawRegion(rect, _quitPanel.image.getBuffer());
1006
1007 for (i = 0; i < _quitPanel.buttonsCount; i++) {
1008 panelButton = &_quitPanel.buttons[i];
1009 if (panelButton->type == kPanelButtonQuit) {
1010 drawPanelButtonText(&_quitPanel, panelButton);
1011 }
1012 if (panelButton->type == kPanelButtonQuitText) {
1013 drawPanelText(&_quitPanel, panelButton);
1014 }
1015 }
1016 }
1017
handleQuitUpdate(const Point & mousePoint)1018 void Interface::handleQuitUpdate(const Point& mousePoint) {
1019 bool releasedButton;
1020
1021 _quitPanel.currentButton = quitHitTest(mousePoint);
1022 releasedButton = (_quitPanel.currentButton != NULL) && (_quitPanel.currentButton->state > 0) && (!_vm->mouseButtonPressed());
1023
1024 if (!_vm->mouseButtonPressed()) {
1025 _quitPanel.zeroAllButtonState();
1026 }
1027
1028 if (releasedButton) {
1029 setQuit(_quitPanel.currentButton);
1030 }
1031 }
1032
handleQuitClick(const Point & mousePoint)1033 void Interface::handleQuitClick(const Point& mousePoint) {
1034 _quitPanel.currentButton = quitHitTest(mousePoint);
1035
1036 _quitPanel.zeroAllButtonState();
1037
1038 if (_quitPanel.currentButton == NULL) {
1039 return;
1040 }
1041
1042 _quitPanel.currentButton->state = 1;
1043 }
1044
setQuit(PanelButton * panelButton)1045 void Interface::setQuit(PanelButton *panelButton) {
1046 _quitPanel.currentButton = NULL;
1047 switch (panelButton->id) {
1048 case kTextCancel:
1049 setMode(kPanelOption);
1050 break;
1051 case kTextQuit:
1052 #ifdef ENABLE_IHNM
1053 if (_vm->isIHNMDemo())
1054 _vm->_scene->creditsScene(); // display sales info for IHNM demo
1055 else
1056 #endif
1057 _vm->quitGame();
1058 break;
1059 default:
1060 break;
1061 }
1062 }
1063
drawLoad()1064 void Interface::drawLoad() {
1065 Rect rect;
1066 int i;
1067 PanelButton *panelButton;
1068
1069 _loadPanel.getRect(rect);
1070 if (_vm->getGameId() == GID_ITE)
1071 drawButtonBox(rect, kButton, false);
1072 else
1073 _vm->_gfx->drawRegion(rect, _loadPanel.image.getBuffer());
1074
1075 for (i = 0; i < _loadPanel.buttonsCount; i++) {
1076 panelButton = &_loadPanel.buttons[i];
1077 if (panelButton->type == kPanelButtonLoad) {
1078 drawPanelButtonText(&_loadPanel, panelButton);
1079 }
1080 if (panelButton->type == kPanelButtonLoadText) {
1081 drawPanelText(&_loadPanel, panelButton);
1082 }
1083 }
1084 }
1085
handleLoadUpdate(const Point & mousePoint)1086 void Interface::handleLoadUpdate(const Point& mousePoint) {
1087 bool releasedButton;
1088
1089 _loadPanel.currentButton = loadHitTest(mousePoint);
1090 releasedButton = (_loadPanel.currentButton != NULL) && (_loadPanel.currentButton->state > 0) && (!_vm->mouseButtonPressed());
1091
1092 if (!_vm->mouseButtonPressed()) {
1093 _loadPanel.zeroAllButtonState();
1094 }
1095
1096 if (releasedButton) {
1097 setLoad(_loadPanel.currentButton);
1098 }
1099 }
1100
handleLoadClick(const Point & mousePoint)1101 void Interface::handleLoadClick(const Point& mousePoint) {
1102 _loadPanel.currentButton = loadHitTest(mousePoint);
1103
1104 _loadPanel.zeroAllButtonState();
1105
1106 if (_loadPanel.currentButton == NULL) {
1107 return;
1108 }
1109
1110 _loadPanel.currentButton->state = 1;
1111 }
1112
setLoad(PanelButton * panelButton)1113 void Interface::setLoad(PanelButton *panelButton) {
1114 _loadPanel.currentButton = NULL;
1115 switch (panelButton->id) {
1116 case kTextOK:
1117 if (_vm->getGameId() == GID_ITE) {
1118 setMode(kPanelMain);
1119 } else {
1120 if (_vm->getSaveFilesCount() > 0) {
1121 if (_vm->isSaveListFull() || (_optionSaveFileTitleNumber > 0)) {
1122 debug(1, "Loading save game %d", _vm->getSaveFile(_optionSaveFileTitleNumber)->slotNumber);
1123 setMode(kPanelMain);
1124 _vm->load(_vm->calcSaveFileName(_vm->getSaveFile(_optionSaveFileTitleNumber)->slotNumber));
1125 _vm->syncSoundSettings();
1126 }
1127 }
1128 }
1129 break;
1130 case kTextCancel:
1131 // IHNM only
1132 setMode(kPanelOption);
1133 break;
1134 default:
1135 break;
1136 }
1137 }
1138
processStatusTextInput(Common::KeyState keystate)1139 void Interface::processStatusTextInput(Common::KeyState keystate) {
1140
1141 switch (keystate.keycode) {
1142 case Common::KEYCODE_ESCAPE:
1143 _statusTextInputState = kStatusTextInputAborted;
1144 _statusTextInput = false;
1145 _vm->_script->wakeUpThreads(kWaitTypeStatusTextInput);
1146 break;
1147 case Common::KEYCODE_RETURN:
1148 _statusTextInputState = kStatusTextInputEntered;
1149 _statusTextInput = false;
1150 _vm->_script->wakeUpThreads(kWaitTypeStatusTextInput);
1151 break;
1152 case Common::KEYCODE_BACKSPACE:
1153 if (_statusTextInputPos == 0) {
1154 break;
1155 }
1156 _statusTextInputPos--;
1157 _statusTextInputString[_statusTextInputPos] = 0;
1158 break;
1159 default:
1160 if (_statusTextInputPos >= STATUS_TEXT_INPUT_MAX - 1) { // -1 because of the null termination
1161 break;
1162 }
1163 if (Common::isAlnum(keystate.ascii) || (keystate.ascii == ' ')) {
1164 _statusTextInputString[_statusTextInputPos++] = keystate.ascii;
1165 _statusTextInputString[_statusTextInputPos] = 0;
1166 }
1167 }
1168 setStatusText(_statusTextInputString);
1169 }
1170
processTextInput(Common::KeyState keystate)1171 bool Interface::processTextInput(Common::KeyState keystate) {
1172 char ch[2];
1173 char tempString[SAVE_TITLE_SIZE];
1174 uint tempWidth;
1175 memset(tempString, 0, SAVE_TITLE_SIZE);
1176 ch[1] = 0;
1177 // IHNM has a smaller save title size than ITE. We only limit the save title size during text input
1178 // in IHNM, to preserve backwards compatibility with older save games
1179 uint save_title_size = _vm->getGameId() == GID_ITE ? SAVE_TITLE_SIZE : IHNM_SAVE_TITLE_SIZE;
1180
1181 switch (keystate.keycode) {
1182 case Common::KEYCODE_RETURN:
1183 return false;
1184 case Common::KEYCODE_ESCAPE:
1185 _textInput = false;
1186 break;
1187 case Common::KEYCODE_BACKSPACE:
1188 if (_textInputPos <= 1) {
1189 break;
1190 }
1191 _textInputPos--;
1192 // fall through
1193 case Common::KEYCODE_DELETE:
1194 if (_textInputPos <= _textInputStringLength) {
1195 if (_textInputPos != 1) {
1196 strncpy(tempString, _textInputString, _textInputPos - 1);
1197 }
1198 if (_textInputPos != _textInputStringLength) {
1199 strncat(tempString, &_textInputString[_textInputPos], _textInputStringLength - _textInputPos);
1200 }
1201 strcpy(_textInputString, tempString);
1202 _textInputStringLength = strlen(_textInputString);
1203 }
1204 break;
1205 case Common::KEYCODE_LEFT:
1206 if (_textInputPos > 1) {
1207 _textInputPos--;
1208 }
1209 break;
1210 case Common::KEYCODE_RIGHT:
1211 if (_textInputPos <= _textInputStringLength) {
1212 _textInputPos++;
1213 }
1214 break;
1215 case Common::KEYCODE_HOME:
1216 _textInputPos = 1;
1217 break;
1218 case Common::KEYCODE_END:
1219 _textInputPos = _textInputStringLength + 1;
1220 break;
1221 default:
1222 if (((keystate.ascii <= 255) && (Common::isAlnum(keystate.ascii))) || (keystate.ascii == ' ') ||
1223 (keystate.ascii == '-') || (keystate.ascii == '_')) {
1224 // This conversion actually works if the isAlnum() check is removed (which locks out all characters > 127).
1225 // However, some glyphs seem to missing from the font, so it might be best to keep the limitation...
1226 keystate.ascii = Common::U32String(Common::String::format("%c", keystate.ascii), Common::kISO8859_1).encode(_vm->getLanguage() == Common::JA_JPN ? Common::kWindows932 : Common::kDos850).firstChar();
1227
1228 if (_textInputStringLength < save_title_size - 1) {
1229 ch[0] = keystate.ascii;
1230 tempWidth = _vm->_font->getStringWidth(kKnownFontSmall, ch, 0, kFontNormal);
1231 tempWidth += _vm->_font->getStringWidth(kKnownFontSmall, _textInputString, 0, kFontNormal);
1232 if (tempWidth > _textInputMaxWidth) {
1233 break;
1234 }
1235 if (_textInputPos != 1) {
1236 strncpy(tempString, _textInputString, _textInputPos - 1);
1237 strcat(tempString, ch);
1238 }
1239 if ((_textInputStringLength == 0) || (_textInputPos == 1)) {
1240 strcpy(tempString, ch);
1241 }
1242 if ((_textInputStringLength != 0) && (_textInputPos != _textInputStringLength)) {
1243 strncat(tempString, &_textInputString[_textInputPos - 1], _textInputStringLength - _textInputPos + 1);
1244 }
1245
1246 strcpy(_textInputString, tempString);
1247 _textInputStringLength = strlen(_textInputString);
1248 _textInputPos++;
1249 }
1250 }
1251 break;
1252 }
1253 return true;
1254 }
1255
drawTextInput(InterfacePanel * panel,PanelButton * panelButton)1256 void Interface::drawTextInput(InterfacePanel *panel, PanelButton *panelButton) {
1257 Point textPoint;
1258 Rect rect;
1259 char ch[2];
1260 int fgColor;
1261 uint i;
1262
1263 ch[1] = 0;
1264 panel->calcPanelButtonRect(panelButton, rect);
1265 drawButtonBox(rect, kEdit, _textInput);
1266 rect.left += 4;
1267 rect.top += 4;
1268 rect.setHeight(_vm->_font->getHeight(kKnownFontSmall));
1269
1270 i = 0;
1271 while ((ch[0] = _textInputString[i++]) != 0) {
1272 rect.setWidth(_vm->_font->getStringWidth(kKnownFontSmall, ch, 0, kFontNormal));
1273 if ((i == _textInputPos) && _textInput) {
1274 fgColor = _vm->KnownColor2ColorId(kKnownColorBlack);
1275 _vm->_gfx->fillRect(rect, _vm->KnownColor2ColorId(kKnownColorWhite));
1276 } else {
1277 fgColor = _vm->KnownColor2ColorId(kKnownColorWhite);
1278 }
1279 textPoint.x = rect.left;
1280 textPoint.y = rect.top + 1;
1281
1282 _vm->_font->textDraw(kKnownFontSmall, ch, textPoint, fgColor, 0, kFontNormal);
1283 rect.left += rect.width();
1284 }
1285 if (_textInput && (_textInputPos >= i)) {
1286 ch[0] = ' ';
1287 rect.setWidth(_vm->_font->getStringWidth(kKnownFontSmall, ch, 0, kFontNormal));
1288 _vm->_gfx->fillRect(rect, _vm->KnownColor2ColorId(kKnownColorWhite));
1289 }
1290 }
1291
drawSave()1292 void Interface::drawSave() {
1293 Rect rect;
1294 int i;
1295 PanelButton *panelButton;
1296
1297 _savePanel.getRect(rect);
1298 if (_vm->getGameId() == GID_ITE)
1299 drawButtonBox(rect, kButton, false);
1300 else
1301 _vm->_gfx->drawRegion(rect, _savePanel.image.getBuffer());
1302
1303 for (i = 0; i < _savePanel.buttonsCount; i++) {
1304 panelButton = &_savePanel.buttons[i];
1305 if (panelButton->type == kPanelButtonSave) {
1306 drawPanelButtonText(&_savePanel, panelButton);
1307 }
1308 if (panelButton->type == kPanelButtonSaveText) {
1309 drawPanelText(&_savePanel, panelButton);
1310 }
1311 }
1312
1313 drawTextInput(&_savePanel, _saveEdit);
1314 }
1315
drawProtect()1316 void Interface::drawProtect() {
1317 Rect rect;
1318 int i;
1319 PanelButton *panelButton;
1320
1321 _protectPanel.getRect(rect);
1322 drawButtonBox(rect, kButton, false);
1323
1324 for (i = 0; i < _protectPanel.buttonsCount; i++) {
1325 panelButton = &_protectPanel.buttons[i];
1326 if (panelButton->type == kPanelButtonProtectText) {
1327 drawPanelText(&_protectPanel, panelButton);
1328 }
1329 }
1330 drawTextInput(&_protectPanel, _protectEdit);
1331 }
1332
handleSaveUpdate(const Point & mousePoint)1333 void Interface::handleSaveUpdate(const Point& mousePoint) {
1334 bool releasedButton;
1335
1336 _savePanel.currentButton = saveHitTest(mousePoint);
1337
1338 validateSaveButtons();
1339
1340 releasedButton = (_savePanel.currentButton != NULL) &&
1341 (_savePanel.currentButton->state > 0) && (!_vm->mouseButtonPressed());
1342
1343 if (!_vm->mouseButtonPressed()) {
1344 _savePanel.zeroAllButtonState();
1345 }
1346
1347 if (releasedButton) {
1348 setSave(_savePanel.currentButton);
1349 }
1350 }
1351
handleSaveClick(const Point & mousePoint)1352 void Interface::handleSaveClick(const Point& mousePoint) {
1353 _savePanel.currentButton = saveHitTest(mousePoint);
1354
1355 validateSaveButtons();
1356
1357 _savePanel.zeroAllButtonState();
1358
1359 if (_savePanel.currentButton == NULL) {
1360 _textInput = false;
1361 return;
1362 }
1363
1364 _savePanel.currentButton->state = 1;
1365 if (_savePanel.currentButton == _saveEdit) {
1366 _textInput = true;
1367 }
1368 }
1369
setSave(PanelButton * panelButton)1370 void Interface::setSave(PanelButton *panelButton) {
1371 _savePanel.currentButton = NULL;
1372 uint titleNumber;
1373 char *fileName;
1374
1375 char desc[28];
1376 Common::strlcpy(desc, Common::U32String(_textInputString, Common::kDos850).encode(Common::kUtf8).c_str(), sizeof(desc));
1377
1378 switch (panelButton->id) {
1379 case kTextSave:
1380 if (_textInputStringLength == 0) {
1381 break;
1382 }
1383
1384 if (!_vm->isSaveListFull() && (_optionSaveFileTitleNumber == 0)) {
1385 if (_vm->locateSaveFile(desc, titleNumber)) {
1386 fileName = _vm->calcSaveFileName(_vm->getSaveFile(titleNumber)->slotNumber);
1387 _vm->save(fileName, desc);
1388 _optionSaveFileTitleNumber = titleNumber;
1389 } else {
1390 fileName = _vm->calcSaveFileName(_vm->getNewSaveSlotNumber());
1391 _vm->save(fileName, desc);
1392 _vm->fillSaveList();
1393 calcOptionSaveSlider();
1394 }
1395 } else {
1396 fileName = _vm->calcSaveFileName(_vm->getSaveFile(_optionSaveFileTitleNumber)->slotNumber);
1397 _vm->save(fileName, desc);
1398 }
1399 resetSaveReminder();
1400
1401 _textInput = false;
1402 setMode(kPanelOption);
1403 break;
1404 case kTextCancel:
1405 _textInput = false;
1406 setMode(kPanelOption);
1407 break;
1408 default:
1409 break;
1410 }
1411 }
1412
resetSaveReminder()1413 void Interface::resetSaveReminder() {
1414 _vm->getTimerManager()->removeTimerProc(&saveReminderCallback);
1415 _vm->getTimerManager()->installTimerProc(&saveReminderCallback, TIMETOSAVE, this, "sagaSaveReminder");
1416 setSaveReminderState(1);
1417 }
1418
handleOptionUpdate(const Point & mousePoint)1419 void Interface::handleOptionUpdate(const Point& mousePoint) {
1420 int16 mouseY;
1421 Rect rect;
1422 int totalFiles = _vm->getSaveFilesCount();
1423 int visibleFiles = _vm->getDisplayInfo().optionSaveFileVisible;
1424 bool releasedButton;
1425
1426 if (_vm->mouseButtonPressed()) {
1427 if (_optionSaveFileSlider->state > 0) {
1428 _optionPanel.calcPanelButtonRect(_optionSaveFileSlider, rect);
1429
1430 mouseY = mousePoint.y - rect.top -_optionSaveFileMouseOff;
1431 if (mouseY < 0)
1432 mouseY = 0;
1433
1434 if (totalFiles - visibleFiles <= 0) {
1435 _optionSaveFileTop = 0;
1436 } else {
1437 _optionSaveFileTop = mouseY * (totalFiles - visibleFiles) /
1438 (_optionSaveFileSlider->height - _optionSaveRectSlider.height());
1439 }
1440
1441 _optionSaveFileTop = CLIP<uint>(_optionSaveFileTop, 0, totalFiles - visibleFiles);
1442 calcOptionSaveSlider();
1443 }
1444 }
1445
1446 _optionPanel.currentButton = optionHitTest(mousePoint);
1447
1448 validateOptionButtons();
1449
1450 releasedButton = (_optionPanel.currentButton != NULL) && (_optionPanel.currentButton->state > 0) && (!_vm->mouseButtonPressed());
1451
1452 if (!_vm->mouseButtonPressed()) {
1453 _optionPanel.zeroAllButtonState();
1454 }
1455
1456 if (releasedButton) {
1457 setOption(_optionPanel.currentButton);
1458 }
1459 }
1460
1461
handleOptionClick(const Point & mousePoint)1462 void Interface::handleOptionClick(const Point& mousePoint) {
1463 Rect rect;
1464 _optionPanel.currentButton = optionHitTest(mousePoint);
1465
1466 validateOptionButtons();
1467
1468 _optionPanel.zeroAllButtonState();
1469
1470 if (_optionPanel.currentButton == NULL) {
1471 return;
1472 }
1473
1474 if (_optionPanel.currentButton == _optionSaveFileSlider) {
1475 if ((_optionSaveRectTop.height() > 0) && (mousePoint.y < _optionSaveRectTop.bottom)) {
1476 _optionSaveFileTop -= _vm->getDisplayInfo().optionSaveFileVisible;
1477 } else {
1478 if ((_optionSaveRectBottom.height() > 0) && (mousePoint.y >= _optionSaveRectBottom.top)) {
1479 _optionSaveFileTop += _vm->getDisplayInfo().optionSaveFileVisible;
1480 } else {
1481 if (_vm->getDisplayInfo().optionSaveFileVisible < _vm->getSaveFilesCount()) {
1482 _optionSaveFileMouseOff = mousePoint.y - _optionSaveRectSlider.top;
1483 _optionPanel.currentButton->state = 1;
1484 }
1485 }
1486 }
1487
1488 _optionSaveFileTop = CLIP<uint>(_optionSaveFileTop, 0, _vm->getSaveFilesCount() - _vm->getDisplayInfo().optionSaveFileVisible);
1489 calcOptionSaveSlider();
1490 } else {
1491 if (_optionPanel.currentButton == _optionSaveFilePanel) {
1492 _optionPanel.calcPanelButtonRect(_optionSaveFilePanel, rect);
1493 _optionSaveFileTitleNumber = (mousePoint.y - rect.top) / (_vm->_font->getHeight(kKnownFontSmall) + 1);
1494
1495 if (_optionSaveFileTitleNumber >= _vm->getDisplayInfo().optionSaveFileVisible) {
1496 _optionSaveFileTitleNumber = _vm->getDisplayInfo().optionSaveFileVisible - 1;
1497 }
1498 _optionSaveFileTitleNumber += _optionSaveFileTop;
1499 if (_optionSaveFileTitleNumber >= _vm->getSaveFilesCount()) {
1500 _optionSaveFileTitleNumber = _vm->getSaveFilesCount() - 1;
1501 }
1502 } else {
1503 _optionPanel.currentButton->state = 1;
1504 }
1505 }
1506 }
1507
handleChapterSelectionUpdate(const Point & mousePoint)1508 void Interface::handleChapterSelectionUpdate(const Point& mousePoint) {
1509 uint16 objectId;
1510 int hitZoneIndex;
1511 const HitZone * hitZone;
1512
1513 // FIXME: Original handled more object types here.
1514
1515 objectId = _vm->_actor->hitTest(mousePoint, true);
1516
1517 if (objectId == ID_NOTHING) {
1518 hitZoneIndex = _vm->_scene->_objectMap->hitTest(mousePoint);
1519
1520 if ((hitZoneIndex != -1)) {
1521 hitZone = _vm->_scene->_objectMap->getHitZone(hitZoneIndex);
1522 objectId = hitZone->getHitZoneId();
1523 }
1524 }
1525
1526 if (objectId != _vm->_script->_pointerObject) {
1527 _vm->_script->_pointerObject = objectId;
1528 }
1529 }
1530
handleChapterSelectionClick(const Point & mousePoint)1531 void Interface::handleChapterSelectionClick(const Point& mousePoint) {
1532 int obj = _vm->_script->_pointerObject;
1533
1534 _vm->_actor->abortSpeech();
1535
1536 if (obj) {
1537 int script = 0;
1538 HitZone *hitZone;
1539 ActorData *a;
1540 ObjectData *o;
1541 Event event;
1542
1543 switch (objectTypeId(obj)) {
1544 case kGameObjectHitZone:
1545 hitZone = _vm->_scene->_objectMap->getHitZone(objectIdToIndex(obj));
1546
1547 if (hitZone == NULL)
1548 return;
1549
1550 if (hitZone->getFlags() & kHitZoneEnabled)
1551 script = hitZone->getScriptNumber();
1552 break;
1553
1554 case kGameObjectActor:
1555 a = _vm->_actor->getActor(obj);
1556 script = a->_scriptEntrypointNumber;
1557 break;
1558
1559 case kGameObjectObject:
1560 o = _vm->_actor->getObj(obj);
1561 script = o->_scriptEntrypointNumber;
1562 break;
1563
1564 default:
1565 break;
1566 }
1567
1568 if (script > 0) {
1569 event.type = kEvTOneshot;
1570 event.code = kScriptEvent;
1571 event.op = kEventExecNonBlocking;
1572 event.time = 0;
1573 event.param = _vm->_scene->getScriptModuleNumber();
1574 event.param2 = script;
1575 event.param3 = _vm->_script->getVerbType(kVerbUse); // Action
1576 event.param4 = obj; // Object
1577 event.param5 = 0; // With Object
1578 event.param6 = obj; // Actor
1579 _vm->_events->queue(event);
1580 }
1581 }
1582 }
1583
setOption(PanelButton * panelButton)1584 void Interface::setOption(PanelButton *panelButton) {
1585 _optionPanel.currentButton = NULL;
1586 switch (panelButton->id) {
1587 case kTextContinuePlaying:
1588 ConfMan.flushToDisk();
1589 if (_vm->getGameId() == GID_ITE) {
1590 setMode(kPanelMain);
1591 } else {
1592 if (_vm->_scene->currentChapterNumber() == 8) {
1593 setMode(kPanelChapterSelection);
1594 } else if (_vm->_scene->isNonInteractiveIHNMDemoPart()) {
1595 setMode(kPanelNull);
1596 } else {
1597 setMode(kPanelMain);
1598 }
1599 }
1600 break;
1601 case kTextQuitGame:
1602 setMode(kPanelQuit);
1603 break;
1604 case kTextLoad:
1605 if (_vm->getGameId() == GID_ITE) {
1606 if (_vm->getSaveFilesCount() > 0) {
1607 if (_vm->isSaveListFull() || (_optionSaveFileTitleNumber > 0)) {
1608 debug(1, "Loading save game %d", _vm->getSaveFile(_optionSaveFileTitleNumber)->slotNumber);
1609 setMode(kPanelMain);
1610 _vm->load(_vm->calcSaveFileName(_vm->getSaveFile(_optionSaveFileTitleNumber)->slotNumber));
1611 _vm->syncSoundSettings();
1612 }
1613 }
1614 } else {
1615 setMode(kPanelLoad);
1616 }
1617 break;
1618 case kTextSave:
1619 // Disallow saving in the non-interactive part of the IHNM demo (original demo didn't support saving at all)
1620 if (_vm->_scene->isNonInteractiveIHNMDemoPart())
1621 return;
1622
1623 if (!_vm->isSaveListFull() && (_optionSaveFileTitleNumber == 0)) {
1624 _textInputString[0] = 0;
1625 } else {
1626 strcpy(_textInputString, _vm->getSaveFile(_optionSaveFileTitleNumber)->name);
1627 }
1628 setMode(kPanelSave);
1629 break;
1630 case kTextReadingSpeed:
1631 if (_vm->getGameId() == GID_ITE && !(_vm->getFeatures() & GF_ITE_FLOPPY)) {
1632 _vm->_subtitlesEnabled = !_vm->_subtitlesEnabled;
1633 ConfMan.setBool("subtitles", _vm->_subtitlesEnabled);
1634 } else {
1635 _vm->_readingSpeed = (_vm->_readingSpeed + 1) % 4;
1636 _vm->setTalkspeed(_vm->_readingSpeed);
1637 }
1638 break;
1639 case kTextMusic:
1640 int userVolume;
1641 userVolume = ConfMan.getInt("music_volume");
1642 userVolume = userVolume + 25;
1643 if (userVolume > 255)
1644 userVolume = 0;
1645 ConfMan.setInt("music_volume", userVolume);
1646 _vm->_music->syncSoundSettings();
1647 break;
1648 case kTextSound:
1649 _vm->_soundVolume = _vm->_soundVolume + 25;
1650 if (_vm->_soundVolume > 255) _vm->_soundVolume = 0;
1651 ConfMan.setInt("sfx_volume", _vm->_soundVolume);
1652 _vm->_sound->setVolume();
1653 break;
1654 case kTextVoices:
1655 if (_vm->_voiceFilesExist) {
1656 if (_vm->_subtitlesEnabled && _vm->_voicesEnabled) { // Both
1657 _vm->_subtitlesEnabled = false; // Set it to "Audio"
1658 _vm->_voicesEnabled = true; // Not necessary, just for completeness
1659 } else if (!_vm->_subtitlesEnabled && _vm->_voicesEnabled) {
1660 _vm->_subtitlesEnabled = true; // Set it to "Text"
1661 _vm->_voicesEnabled = false;
1662 } else if (_vm->_subtitlesEnabled && !_vm->_voicesEnabled) {
1663 _vm->_subtitlesEnabled = true; // Set it to "Both"
1664 _vm->_voicesEnabled = true;
1665 }
1666 } else {
1667 _vm->_subtitlesEnabled = true; // Set it to "Text"
1668 _vm->_voicesEnabled = false;
1669 }
1670
1671 _vm->_speechVolume = _vm->_speechVolume + 25;
1672 if (_vm->_speechVolume > 255) _vm->_speechVolume = 0;
1673 ConfMan.setInt("speech_volume", _vm->_speechVolume);
1674 _vm->_sound->setVolume();
1675
1676 ConfMan.setBool("subtitles", _vm->_subtitlesEnabled);
1677 ConfMan.setBool("voices", _vm->_voicesEnabled);
1678 break;
1679 default:
1680 break;
1681 }
1682 }
1683
update(const Point & mousePoint,int updateFlag)1684 void Interface::update(const Point& mousePoint, int updateFlag) {
1685
1686 if (!_active && _panelMode == kPanelNull && (updateFlag & UPDATE_MOUSECLICK))
1687 _vm->_actor->abortSpeech();
1688
1689 if (_vm->_scene->isInIntro() || _fadeMode == kFadeOut || !_active) {
1690 // When opening the psychic profile, or the options screen in the non-interactive part of the IHNM demo,
1691 // the interface is locked (_active is false)
1692 // Don't return in those cases, so that mouse actions can be processed
1693 if (_vm->getGameId() == GID_ITE) {
1694 return;
1695 } else {
1696 if (_panelMode == kPanelPlacard && (updateFlag & UPDATE_MOUSECLICK)) {
1697 // the psychic profile or the special screen in IHNM demo is open, don't return
1698 } else if (_panelMode == kPanelOption || _panelMode == kPanelQuit) {
1699 // options/quit panel is open, set interface to active and don't return
1700 _vm->_actor->abortSpeech(); // abort any speech being played
1701 _active = true;
1702 } else {
1703 return;
1704 }
1705 }
1706 }
1707
1708 if (_statusTextInput) {
1709 return;
1710 }
1711
1712 switch (_panelMode) {
1713 case kPanelMain:
1714 if (updateFlag & UPDATE_MOUSEMOVE) {
1715 bool lastWasPlayfield = _lastMousePoint.y < _vm->_scene->getHeight();
1716 if (mousePoint.y < _vm->_scene->getHeight()) {
1717 if (!lastWasPlayfield) {
1718 handleMainUpdate(mousePoint);
1719 }
1720 _vm->_script->whichObject(mousePoint);
1721 } else {
1722 if (lastWasPlayfield) {
1723 _vm->_script->setNonPlayfieldVerb();
1724 }
1725 handleMainUpdate(mousePoint);
1726 }
1727
1728 } else {
1729
1730 if (updateFlag & UPDATE_MOUSECLICK) {
1731 if (mousePoint.y < _vm->_scene->getHeight()) {
1732 _vm->_script->playfieldClick(mousePoint, (updateFlag & UPDATE_LEFTBUTTONCLICK) != 0);
1733 } else {
1734 handleMainClick(mousePoint);
1735 }
1736 }
1737 }
1738 break;
1739
1740 case kPanelConverse:
1741 if (updateFlag & UPDATE_MOUSEMOVE) {
1742 handleConverseUpdate(mousePoint);
1743 } else {
1744 if (updateFlag & UPDATE_MOUSECLICK) {
1745 handleConverseClick(mousePoint);
1746 }
1747 if (updateFlag & UPDATE_WHEELUP) {
1748 converseChangePos(-1);
1749 }
1750 if (updateFlag & UPDATE_WHEELDOWN) {
1751 converseChangePos(1);
1752 }
1753
1754 if (_vm->_scene->isITEPuzzleScene()) {
1755 _vm->_puzzle->handleClick(mousePoint);
1756 }
1757 }
1758 break;
1759
1760 case kPanelOption:
1761 if (updateFlag & UPDATE_MOUSEMOVE) {
1762 handleOptionUpdate(mousePoint);
1763 } else {
1764 if (updateFlag & UPDATE_MOUSECLICK) {
1765 handleOptionClick(mousePoint);
1766 }
1767 if (updateFlag & UPDATE_WHEELUP) {
1768 if (_optionSaveFileTop)
1769 _optionSaveFileTop--;
1770 calcOptionSaveSlider();
1771 }
1772 if (updateFlag & UPDATE_WHEELDOWN) {
1773 if (_optionSaveFileTop < _vm->getSaveFilesCount() - _vm->getDisplayInfo().optionSaveFileVisible)
1774 _optionSaveFileTop++;
1775 calcOptionSaveSlider();
1776 }
1777 }
1778 break;
1779
1780 case kPanelQuit:
1781 if (updateFlag & UPDATE_MOUSEMOVE) {
1782 handleQuitUpdate(mousePoint);
1783 } else {
1784 if (updateFlag & UPDATE_MOUSECLICK) {
1785 handleQuitClick(mousePoint);
1786 }
1787 }
1788 break;
1789
1790 case kPanelLoad:
1791 if (updateFlag & UPDATE_MOUSEMOVE) {
1792
1793 handleLoadUpdate(mousePoint);
1794
1795 } else {
1796 if (updateFlag & UPDATE_MOUSECLICK) {
1797 handleLoadClick(mousePoint);
1798 }
1799 }
1800 break;
1801
1802 case kPanelSave:
1803 if (updateFlag & UPDATE_MOUSEMOVE) {
1804
1805 handleSaveUpdate(mousePoint);
1806
1807 } else {
1808 if (updateFlag & UPDATE_MOUSECLICK) {
1809 handleSaveClick(mousePoint);
1810 }
1811 }
1812 break;
1813
1814 case kPanelMap:
1815 if (updateFlag & UPDATE_MOUSECLICK)
1816 mapPanelClean();
1817 break;
1818
1819 case kPanelSceneSubstitute:
1820 if (updateFlag & UPDATE_MOUSECLICK) {
1821 _vm->_render->clearFlag(RF_DEMO_SUBST);
1822 _vm->_gfx->setPalette(_mapSavedPal);
1823 setMode(kPanelMain);
1824 _vm->_script->setNoPendingVerb();
1825 }
1826 break;
1827
1828 case kPanelChapterSelection:
1829 if (updateFlag & UPDATE_MOUSEMOVE) {
1830 handleChapterSelectionUpdate(mousePoint);
1831 } else {
1832 if (updateFlag & UPDATE_MOUSECLICK) {
1833 Rect rect;
1834 rect.left = _vm->getDisplayInfo().saveReminderXOffset;
1835 rect.top = _vm->getDisplayInfo().saveReminderYOffset;
1836
1837 rect.right = rect.left + _vm->getDisplayInfo().saveReminderWidth;
1838 rect.bottom = rect.top + _vm->getDisplayInfo().saveReminderHeight;
1839 if (rect.contains(mousePoint))
1840 setMode(kPanelOption);
1841 else
1842 handleChapterSelectionClick(mousePoint);
1843 }
1844 }
1845 break;
1846
1847 case kPanelProtect:
1848 // No mouse interaction
1849 break;
1850
1851 case kPanelPlacard:
1852 #ifdef ENABLE_IHNM
1853 if (_vm->getGameId() == GID_IHNM) {
1854 // Any mouse click here returns the user back to the game
1855 if (updateFlag & UPDATE_MOUSECLICK) {
1856 if (!_vm->isIHNMDemo()) {
1857 _vm->_scene->clearPsychicProfile();
1858 _vm->_script->wakeUpThreads(kWaitTypeDelay);
1859 } else {
1860 setMode(kPanelConverse);
1861 _vm->_scene->_textList.clear();
1862 _vm->_script->wakeUpThreads(kWaitTypeDelay);
1863 }
1864 }
1865 }
1866 #endif
1867 break;
1868
1869 case kPanelNull:
1870 #ifdef ENABLE_IHNM
1871 if (_vm->_scene->isNonInteractiveIHNMDemoPart() && (updateFlag & UPDATE_MOUSECLICK))
1872 _vm->_scene->showIHNMDemoSpecialScreen();
1873 #endif
1874 break;
1875
1876 default:
1877 break;
1878 }
1879
1880 _lastMousePoint = mousePoint;
1881 }
1882
drawStatusBar()1883 void Interface::drawStatusBar() {
1884 Rect rect;
1885 Point textPoint;
1886 int stringWidth;
1887 int color;
1888 // The default colors in the Spanish version of IHNM are shifted by one
1889 // Fixes bug #3498 - "IHNM: Wrong Subtitles Color (Spanish)". This
1890 // also applies to the German and French versions (bug #7064 - "IHNM:
1891 // text mistake in german version").
1892 int offset = (_vm->getFeatures() & GF_IHNM_COLOR_FIX) ? 1 : 0;
1893
1894 // Disable the status text in IHNM when the chapter is 8
1895 if (_vm->getGameId() == GID_IHNM && _vm->_scene->currentChapterNumber() == 8)
1896 return;
1897
1898 // Don't draw the status bar while fading out
1899 if (_fadeMode == kFadeOut)
1900 return;
1901
1902 // Erase background of status bar
1903 rect.left = _vm->getDisplayInfo().statusXOffset;
1904 rect.top = _vm->getDisplayInfo().statusYOffset;
1905 rect.right = rect.left + _vm->getDisplayInfo().width;
1906 rect.bottom = rect.top + _vm->getDisplayInfo().statusHeight;
1907
1908 _vm->_gfx->drawRect(rect, _vm->getDisplayInfo().statusBGColor - offset);
1909
1910 stringWidth = _vm->_font->getStringWidth(kKnownFontSmall, _statusText, 0, kFontNormal);
1911
1912 if (_statusOnceColor == -1)
1913 color = _vm->getDisplayInfo().statusTextColor - offset;
1914 else
1915 color = _statusOnceColor;
1916
1917 textPoint.x = _vm->getDisplayInfo().statusXOffset + (_vm->getDisplayInfo().statusWidth - stringWidth) / 2;
1918 textPoint.y = _vm->getDisplayInfo().statusYOffset + _vm->getDisplayInfo().statusTextY;
1919 if (_vm->getGameId() == GID_ITE)
1920 _vm->_font->textDraw(kKnownFontSmall, _statusText, textPoint, color, 0, kFontNormal);
1921 else
1922 _vm->_font->textDraw(kKnownFontVerb, _statusText, textPoint, color, 0, kFontNormal);
1923
1924 if (_saveReminderState > 0) {
1925 rect.left = _vm->getDisplayInfo().saveReminderXOffset;
1926 rect.top = _vm->getDisplayInfo().saveReminderYOffset;
1927
1928 rect.right = rect.left + _vm->getDisplayInfo().saveReminderWidth;
1929 rect.bottom = rect.top + _vm->getDisplayInfo().saveReminderHeight;
1930 _vm->_sprite->draw(_vm->_sprite->_saveReminderSprites,
1931 _vm->getDisplayInfo().saveReminderFirstSpriteNumber + _saveReminderState - 1,
1932 rect, 256);
1933
1934 }
1935 }
1936
handleMainClick(const Point & mousePoint)1937 void Interface::handleMainClick(const Point& mousePoint) {
1938
1939 PanelButton *panelButton;
1940
1941 panelButton = verbHitTest(mousePoint);
1942 if (panelButton) {
1943 _vm->_script->setVerb(panelButton->id);
1944 return;
1945 }
1946
1947 panelButton = _mainPanel.hitTest(mousePoint, kPanelAllButtons);
1948
1949 if (panelButton != NULL) {
1950 if (panelButton->type == kPanelButtonArrow) {
1951 panelButton->state = 1;
1952 converseChangePos(panelButton->id);
1953 }
1954
1955 if (panelButton->type == kPanelButtonInventory) {
1956 if (_vm->_script->_pointerObject != ID_NOTHING) {
1957 _vm->_script->hitObject(_vm->leftMouseButtonPressed());
1958 }
1959 if (_vm->_script->_pendingVerb) {
1960 _vm->_actor->_protagonist->_currentAction = kActionWait;
1961 _vm->_script->doVerb();
1962 }
1963 }
1964 } else {
1965 if (_saveReminderState > 0) {
1966 Rect rect;
1967 rect.left = _vm->getDisplayInfo().saveReminderXOffset;
1968 rect.top = _vm->getDisplayInfo().saveReminderYOffset;
1969
1970 rect.right = rect.left + _vm->getDisplayInfo().saveReminderWidth;
1971 rect.bottom = rect.top + _vm->getDisplayInfo().saveReminderHeight;
1972 if (rect.contains(mousePoint)) {
1973 setMode(kPanelOption);
1974 }
1975 }
1976 }
1977 }
1978
handleMainUpdate(const Point & mousePoint)1979 void Interface::handleMainUpdate(const Point& mousePoint) {
1980 PanelButton *panelButton;
1981
1982 panelButton = verbHitTest(mousePoint);
1983 if (_mainPanel.currentButton != panelButton) {
1984 if (_mainPanel.currentButton) {
1985 if (_mainPanel.currentButton->type == kPanelButtonVerb) {
1986 setVerbState(_mainPanel.currentButton->id, 0);
1987 }
1988 }
1989 if (panelButton) {
1990 setVerbState(panelButton->id, 1);
1991 }
1992 }
1993
1994 if (panelButton) {
1995 _mainPanel.currentButton = panelButton;
1996 return;
1997 }
1998
1999
2000 if (!_vm->mouseButtonPressed()) { // remove pressed flag
2001 if (_inventoryUpButton) {
2002 _inventoryUpButton->state = 0;
2003 _inventoryDownButton->state = 0;
2004 }
2005 }
2006
2007 panelButton = _mainPanel.hitTest(mousePoint, kPanelAllButtons);
2008
2009 bool changed = false;
2010
2011 if ((panelButton != NULL) && (panelButton->type == kPanelButtonArrow)) {
2012 if (panelButton->state == 1) {
2013 inventoryChangePos(panelButton->id);
2014 }
2015 changed = true;
2016 } else {
2017 _vm->_script->whichObject(mousePoint);
2018 }
2019
2020 changed = changed || (panelButton != _mainPanel.currentButton);
2021 _mainPanel.currentButton = panelButton;
2022 if (changed) {
2023 draw();
2024 }
2025 }
2026
2027 //inventory stuff
inventoryChangePos(int chg)2028 void Interface::inventoryChangePos(int chg) {
2029 // Arrows will scroll the inventory up or down up to 4 items
2030 for (int i = 1; i <= 4; i++) {
2031 if ((chg < 0 && _inventoryStart + chg >= 0) ||
2032 (chg > 0 && _inventoryStart < _inventoryEnd)) {
2033 _inventoryStart += chg;
2034 }
2035 }
2036 draw();
2037 }
2038
inventorySetPos(int key)2039 void Interface::inventorySetPos(int key) {
2040 _inventoryBox = key - '1';
2041 _inventoryPos = _inventoryStart + _inventoryBox;
2042 if (_inventoryPos >= _inventoryCount)
2043 _inventoryPos = -1;
2044 }
2045
updateInventory(int pos)2046 void Interface::updateInventory(int pos) {
2047 int cols = _vm->getDisplayInfo().inventoryColumns;
2048 if (pos >= _inventoryCount) {
2049 pos = _inventoryCount - 1;
2050 }
2051 if (pos < 0) {
2052 pos = 0;
2053 }
2054 _inventoryStart = (pos - cols) / cols * cols;
2055 if (_inventoryStart < 0) {
2056 _inventoryStart = 0;
2057 }
2058
2059 _inventoryEnd = (_inventoryCount - 1 - cols) / cols * cols;
2060 if (_inventoryEnd < 0) {
2061 _inventoryEnd = 0;
2062 }
2063 }
2064
addToInventory(int objectId)2065 void Interface::addToInventory(int objectId) {
2066 if (uint(_inventoryCount) >= _inventory.size()) {
2067 return;
2068 }
2069
2070 for (int i = _inventoryCount; i > 0; i--) {
2071 _inventory[i] = _inventory[i - 1];
2072 }
2073
2074 _inventory[0] = objectId;
2075 _inventoryCount++;
2076
2077 _inventoryPos = 0;
2078 updateInventory(0);
2079 draw();
2080 }
2081
removeFromInventory(int objectId)2082 void Interface::removeFromInventory(int objectId) {
2083 int j = inventoryItemPosition(objectId);
2084 if (j == -1) {
2085 return;
2086 }
2087
2088 int i;
2089
2090 for (i = j; i < _inventoryCount - 1; i++) {
2091 _inventory[i] = _inventory[i + 1];
2092 }
2093
2094 --_inventoryCount;
2095 _inventory[_inventoryCount] = 0;
2096 updateInventory(j);
2097 draw();
2098 }
2099
clearInventory()2100 void Interface::clearInventory() {
2101 for (int i = 0; i < _inventoryCount; i++)
2102 _inventory[i] = 0;
2103
2104 _inventoryCount = 0;
2105 updateInventory(0);
2106 }
2107
inventoryItemPosition(int objectId)2108 int Interface::inventoryItemPosition(int objectId) {
2109 for (int i = 0; i < _inventoryCount; i++)
2110 if (_inventory[i] == objectId)
2111 return i;
2112
2113 return -1;
2114 }
2115
drawInventory()2116 void Interface::drawInventory() {
2117 if (!isInMainMode())
2118 return;
2119
2120 Rect rect;
2121 int ci = _inventoryStart;
2122 ObjectData *obj;
2123
2124 if (_inventoryStart != 0) {
2125 drawPanelButtonArrow(&_mainPanel, _inventoryUpButton);
2126 }
2127 if (_inventoryStart != _inventoryEnd) {
2128 drawPanelButtonArrow(&_mainPanel, _inventoryDownButton);
2129 }
2130
2131 for (int i = 0; i < _mainPanel.buttonsCount; i++) {
2132 if (_mainPanel.buttons[i].type != kPanelButtonInventory) {
2133 continue;
2134 }
2135 _mainPanel.calcPanelButtonRect(&_mainPanel.buttons[i], rect);
2136
2137 if (_vm->getGameId() == GID_ITE)
2138 _vm->_gfx->drawRect(rect, kITEColorDarkGrey);
2139 else
2140 _vm->_gfx->drawRect(rect, _vm->KnownColor2ColorId(kKnownColorBlack));
2141
2142 if (ci < _inventoryCount) {
2143 obj = _vm->_actor->getObj(_inventory[ci]);
2144 _vm->_sprite->draw(_vm->_sprite->_inventorySprites, obj->_spriteListResourceId, rect, 256);
2145 }
2146
2147 ci++;
2148 }
2149 }
2150
setVerbState(int verb,int state)2151 void Interface::setVerbState(int verb, int state) {
2152 PanelButton * panelButton = getPanelButtonByVerbType(verb);
2153 if (panelButton == NULL) return;
2154 if (state == 2) {
2155 state = (_mainPanel.currentButton == panelButton) ? 1 : 0;
2156 }
2157 panelButton->state = state;
2158 draw();
2159 }
2160
drawButtonBox(const Rect & rect,ButtonKind kind,bool down)2161 void Interface::drawButtonBox(const Rect& rect, ButtonKind kind, bool down) {
2162 byte cornerColor;
2163 byte frameColor;
2164 byte fillColor;
2165 byte solidColor;
2166 byte odl, our, idl, iur;
2167
2168 switch (kind) {
2169 case kSlider:
2170 cornerColor = 0x8b;
2171 frameColor = _vm->KnownColor2ColorId(kKnownColorBlack);
2172 fillColor = kITEColorLightBlue96;
2173 odl = kITEColorDarkBlue8a;
2174 our = kITEColorLightBlue92;
2175 idl = 0x89;
2176 iur = 0x94;
2177 solidColor = down ? kITEColorLightBlue94 : kITEColorLightBlue96;
2178 break;
2179 case kEdit:
2180 if (_vm->getGameId() == GID_ITE) {
2181 cornerColor = frameColor = fillColor = kITEColorLightBlue96;
2182 our = kITEColorDarkBlue8a;
2183 odl = kITEColorLightBlue94;
2184 solidColor = down ? kITEColorBlue : kITEColorDarkGrey0C;
2185 } else {
2186 cornerColor = frameColor = fillColor = _vm->KnownColor2ColorId(kKnownColorBlack);
2187 our = odl = solidColor = _vm->KnownColor2ColorId(kKnownColorBlack);
2188 }
2189 iur = 0x97;
2190 idl = 0x95;
2191 break;
2192 default:
2193 cornerColor = 0x8b;
2194 frameColor = _vm->KnownColor2ColorId(kKnownColorBlack);
2195 solidColor = fillColor = kITEColorLightBlue96;
2196 odl = kITEColorDarkBlue8a;
2197 our = kITEColorLightBlue94;
2198 idl = 0x97;
2199 iur = 0x95;
2200 if (down) {
2201 SWAP(odl, our);
2202 SWAP(idl, iur);
2203 }
2204 break;
2205 }
2206
2207 int x = rect.left;
2208 int y = rect.top;
2209 int w = rect.width();
2210 int h = rect.height();
2211 int xe = rect.right - 1;
2212 int ye = rect.bottom - 1;
2213
2214 _vm->_gfx->setPixelColor(x, y, cornerColor);
2215 _vm->_gfx->setPixelColor(x, ye, cornerColor);
2216 _vm->_gfx->setPixelColor(xe, y, cornerColor);
2217 _vm->_gfx->setPixelColor(xe, ye, cornerColor);
2218 _vm->_gfx->hLine(x + 1, y, x + w - 2, frameColor);
2219 _vm->_gfx->hLine(x + 1, ye, x + w - 2, frameColor);
2220 _vm->_gfx->vLine(x, y + 1, y + h - 2, frameColor);
2221 _vm->_gfx->vLine(xe, y + 1, y + h - 2, frameColor);
2222
2223 x++;
2224 y++;
2225 xe--;
2226 ye--;
2227 w -= 2;
2228 h -= 2;
2229 _vm->_gfx->vLine(x, y, y + h - 1, odl);
2230 _vm->_gfx->hLine(x, ye, x + w - 1, odl);
2231 _vm->_gfx->vLine(xe, y, y + h - 2, our);
2232 _vm->_gfx->hLine(x + 1, y, x + 1 + w - 2, our);
2233
2234 x++;
2235 y++;
2236 xe--;
2237 ye--;
2238 w -= 2;
2239 h -= 2;
2240 _vm->_gfx->setPixelColor(x, y, fillColor);
2241 _vm->_gfx->setPixelColor(xe, ye, fillColor);
2242 _vm->_gfx->vLine(x, y + 1, y + 1 + h - 2, idl);
2243 _vm->_gfx->hLine(x + 1, ye, x + 1 + w - 2, idl);
2244 _vm->_gfx->vLine(xe, y, y + h - 2, iur);
2245 _vm->_gfx->hLine(x + 1, y, x + 1 + w - 2, iur);
2246
2247 x++; y++;
2248 w -= 2; h -= 2;
2249
2250 Common::Rect fill(x, y, x + w, y + h);
2251 _vm->_gfx->fillRect(fill, solidColor);
2252 _vm->_render->addDirtyRect(rect);
2253 }
2254
2255 static const int readingSpeeds[] = { kTextClick, kTextSlow, kTextMid, kTextFast };
2256
drawPanelButtonText(InterfacePanel * panel,PanelButton * panelButton,int spritenum)2257 void Interface::drawPanelButtonText(InterfacePanel *panel, PanelButton *panelButton, int spritenum) {
2258 const char *text;
2259 int textId;
2260 int textWidth;
2261 int textHeight;
2262 Point point;
2263 Point texturePoint;
2264 KnownColor textColor;
2265 Rect rect;
2266 int litButton = 0;
2267 KnownColor textShadowKnownColor = kKnownColorVerbTextShadow;
2268 KnownFont textFont = kKnownFontMedium;
2269
2270 textId = panelButton->id;
2271 switch (panelButton->id) {
2272 case kTextReadingSpeed:
2273 if (_vm->getGameId() == GID_ITE && !(_vm->getFeatures() & GF_ITE_FLOPPY)) {
2274 if (_vm->_subtitlesEnabled)
2275 textId = kTextOn;
2276 else
2277 textId = kTextOff;
2278 } else {
2279 textId = readingSpeeds[_vm->_readingSpeed];
2280 }
2281 break;
2282 case kTextMusic:
2283 int userVolume;
2284 userVolume = ConfMan.getInt("music_volume");
2285 if (userVolume) {
2286 textId = kText10Percent + userVolume / 25 - 1;
2287 if (textId > kTextMax) {
2288 textId = kTextMax;
2289 }
2290 }
2291 else
2292 textId = kTextOff;
2293 break;
2294 case kTextSound:
2295 if (_vm->_soundVolume) {
2296 textId = kText10Percent + _vm->_soundVolume / 25 - 1;
2297 if (textId > kTextMax) {
2298 textId = kTextMax;
2299 }
2300 }
2301 else
2302 textId = kTextOff;
2303 break;
2304 case kTextVoices:
2305 if (_vm->_subtitlesEnabled && _vm->_voicesEnabled)
2306 textId = kTextBoth;
2307 else if (_vm->_subtitlesEnabled && !_vm->_voicesEnabled)
2308 textId = kTextText;
2309 else if (!_vm->_subtitlesEnabled && _vm->_voicesEnabled)
2310 textId = kTextAudio;
2311 break;
2312 default:
2313 break;
2314 }
2315 if (_vm->getGameId() == GID_ITE) {
2316 if (textId > kTextEnterProtectAnswer)
2317 error("This should not happen. Please report to ScummVM Team how you achieved this error.");
2318
2319 text = _vm->getTextString(textId);
2320 textFont = kKnownFontMedium;
2321 textShadowKnownColor = kKnownColorVerbTextShadow;
2322 textWidth = _vm->_font->getStringWidth(kKnownFontMedium, text, 0, kFontNormal);
2323 textHeight = _vm->_font->getHeight(kKnownFontMedium);
2324 } else {
2325 if (textId < 39 || textId > 50) {
2326 // Read non-hardcoded strings from the LUT string table, loaded from the game
2327 // data files
2328 text = _vm->_script->_mainStrings.getString(IHNMTextStringIdsLUT[textId]);
2329 } else {
2330 // Hardcoded strings in IHNM are read from the ITE hardcoded strings
2331 text = _vm->getTextString(textId);
2332 }
2333
2334 textFont = kKnownFontVerb;
2335 textShadowKnownColor = kKnownColorTransparent;
2336 textWidth = _vm->_font->getStringWidth(kKnownFontVerb, text, 0, kFontNormal);
2337 textHeight = _vm->_font->getHeight(kKnownFontVerb);
2338 }
2339
2340 point.x = panel->x + panelButton->xOffset + (panelButton->width / 2) - (textWidth / 2);
2341 point.y = panel->y + panelButton->yOffset + (panelButton->height / 2) - (textHeight / 2);
2342
2343 if (panelButton == panel->currentButton) {
2344 textColor = kKnownColorVerbTextActive;
2345 } else {
2346 textColor = kKnownColorVerbText;
2347 }
2348
2349 panel->calcPanelButtonRect(panelButton, rect);
2350 if (_vm->getGameId() == GID_ITE) {
2351 drawButtonBox(rect, kButton, panelButton->state > 0);
2352 } else {
2353 litButton = panelButton->state > 0;
2354
2355 if (panel == &_optionPanel) {
2356 texturePoint.x = _optionPanel.x + panelButton->xOffset - 1;
2357 texturePoint.y = _optionPanel.y + panelButton->yOffset - 1;
2358 _vm->_sprite->draw(_optionPanel.sprites, spritenum + 2 + litButton, texturePoint, 256);
2359 } else if (panel == &_quitPanel) {
2360 texturePoint.x = _quitPanel.x + panelButton->xOffset - 3;
2361 texturePoint.y = _quitPanel.y + panelButton->yOffset - 3;
2362 _vm->_sprite->draw(_quitPanel.sprites, litButton, texturePoint, 256);
2363 } else if (panel == &_savePanel) {
2364 texturePoint.x = _savePanel.x + panelButton->xOffset - 3;
2365 texturePoint.y = _savePanel.y + panelButton->yOffset - 3;
2366 _vm->_sprite->draw(_savePanel.sprites, litButton, texturePoint, 256);
2367 // Input text box sprite
2368 texturePoint.x = _savePanel.x + _saveEdit->xOffset - 2;
2369 texturePoint.y = _savePanel.y + _saveEdit->yOffset - 2;
2370 _vm->_sprite->draw(_savePanel.sprites, 2, texturePoint, 256);
2371 } else if (panel == &_loadPanel) {
2372 texturePoint.x = _loadPanel.x + panelButton->xOffset - 3;
2373 texturePoint.y = _loadPanel.y + panelButton->yOffset - 3;
2374 _vm->_sprite->draw(_loadPanel.sprites, litButton, texturePoint, 256);
2375 } else {
2376 // revert to default behavior
2377 drawButtonBox(rect, kButton, panelButton->state > 0);
2378 }
2379 }
2380
2381 _vm->_font->textDraw(textFont, text, point,
2382 _vm->KnownColor2ColorId(textColor), _vm->KnownColor2ColorId(textShadowKnownColor), _vm->getPlatform() == Common::kPlatformPC98 ? kFontOutline : kFontShadow);
2383 }
2384
drawPanelButtonArrow(InterfacePanel * panel,PanelButton * panelButton)2385 void Interface::drawPanelButtonArrow(InterfacePanel *panel, PanelButton *panelButton) {
2386 Point point;
2387 int spriteNumber;
2388
2389 if (panel->currentButton == panelButton) {
2390 if (panelButton->state != 0) {
2391 spriteNumber = panelButton->downSpriteNumber;
2392 } else {
2393 spriteNumber = panelButton->overSpriteNumber;
2394 }
2395 } else {
2396 spriteNumber = panelButton->upSpriteNumber;
2397 }
2398
2399 point.x = panel->x + panelButton->xOffset;
2400 point.y = panel->y + panelButton->yOffset;
2401
2402 if (_vm->getGameId() == GID_ITE)
2403 _vm->_sprite->draw(_vm->_sprite->_mainSprites, spriteNumber, point, 256);
2404 else
2405 _vm->_sprite->draw(_vm->_sprite->_arrowSprites, spriteNumber, point, 256);
2406 }
2407
drawVerbPanelText(PanelButton * panelButton,KnownColor textKnownColor,KnownColor textShadowKnownColor)2408 void Interface::drawVerbPanelText(PanelButton *panelButton, KnownColor textKnownColor, KnownColor textShadowKnownColor) {
2409 const char *text;
2410 int textWidth;
2411 Point point;
2412 int textId;
2413
2414 if (_vm->getGameId() == GID_ITE) {
2415 textId = verbToTextIdITE[panelButton->id - 1];
2416 text = _vm->getTextString(textId);
2417 } else {
2418 textId = panelButton->id;
2419 text = _vm->_script->_mainStrings.getString(textId + 1);
2420 textShadowKnownColor = kKnownColorTransparent;
2421 }
2422
2423 textWidth = _vm->_font->getStringWidth(kKnownFontVerb, text, 0, kFontNormal);
2424
2425 if (_vm->getGameId() == GID_ITE) {
2426 point.x = _mainPanel.x + panelButton->xOffset + 1 + (panelButton->width - 1 - textWidth) / 2;
2427 point.y = _mainPanel.y + panelButton->yOffset + 1;
2428 } else {
2429 point.x = _mainPanel.x + panelButton->xOffset + 1 + (panelButton->width - textWidth) / 2;
2430 point.y = _mainPanel.y + panelButton->yOffset + 12;
2431 }
2432
2433 _vm->_font->textDraw(kKnownFontVerb, text, point,
2434 _vm->KnownColor2ColorId(textKnownColor), _vm->KnownColor2ColorId(textShadowKnownColor),
2435 (textShadowKnownColor != kKnownColorTransparent) ? (_vm->getPlatform() == Common::kPlatformPC98 ? kFontOutline : kFontShadow) : kFontNormal);
2436 }
2437
2438
2439 // Converse stuff
converseClear()2440 void Interface::converseClear() {
2441 for (int i = 0; i < CONVERSE_MAX_TEXTS; i++) {
2442 _converseText[i].text.clear();
2443 _converseText[i].stringNum = -1;
2444 _converseText[i].replyId = 0;
2445 _converseText[i].replyFlags = 0;
2446 _converseText[i].replyBit = 0;
2447 }
2448
2449 _converseTextCount = 0;
2450 _converseStrCount = 0;
2451 _converseStartPos = 0;
2452 _converseEndPos = 0;
2453 _conversePos = -1;
2454 }
2455
converseAddText(const char * text,int strId,int replyId,byte replyFlags,int replyBit)2456 bool Interface::converseAddText(const char *text, int strId, int replyId, byte replyFlags, int replyBit) {
2457 int count = 0; // count how many pieces of text per string
2458 int i;
2459 int len;
2460 byte c;
2461
2462 assert(strlen(text) < CONVERSE_MAX_WORK_STRING);
2463
2464 Common::strlcpy(_converseWorkString, text, CONVERSE_MAX_WORK_STRING);
2465
2466 while (1) {
2467 len = strlen(_converseWorkString);
2468
2469 for (i = len; i >= 0; i--) {
2470 c = _converseWorkString[i];
2471
2472 if (_vm->getGameId() == GID_ITE) {
2473 if ((c == ' ' || c == '\0') && (_vm->_font->getStringWidth(kKnownFontSmall, _converseWorkString, i, kFontNormal) <= _vm->getDisplayInfo().converseMaxTextWidth))
2474 break;
2475 } else {
2476 if ((c == ' ' || c == '\0') && (_vm->_font->getStringWidth(kKnownFontVerb, _converseWorkString, i, kFontNormal) <= _vm->getDisplayInfo().converseMaxTextWidth))
2477 break;
2478 }
2479 }
2480 if (i < 0) {
2481 return true;
2482 }
2483
2484 if (_converseTextCount == CONVERSE_MAX_TEXTS) {
2485 return true;
2486 }
2487
2488 _converseText[_converseTextCount].text.resize(i + 1);
2489 strncpy(&_converseText[_converseTextCount].text.front(), _converseWorkString, i);
2490
2491 _converseText[_converseTextCount].strId = strId;
2492 _converseText[_converseTextCount].text[i] = 0;
2493 _converseText[_converseTextCount].textNum = count;
2494 _converseText[_converseTextCount].stringNum = _converseStrCount;
2495 _converseText[_converseTextCount].replyId = replyId;
2496 _converseText[_converseTextCount].replyFlags = replyFlags;
2497 _converseText[_converseTextCount].replyBit = replyBit;
2498
2499 _converseTextCount++;
2500 count++;
2501
2502 if (len == i)
2503 break;
2504
2505 strncpy(_converseWorkString, &_converseWorkString[i + 1], len - i);
2506 }
2507
2508 _converseStrCount++;
2509
2510 return false;
2511 }
2512
converseDisplayText()2513 void Interface::converseDisplayText() {
2514 int end;
2515
2516 _converseStartPos = 0;
2517
2518 end = _converseTextCount - _vm->getDisplayInfo().converseTextLines;
2519
2520 if (end < 0)
2521 end = 0;
2522
2523 _converseEndPos = end;
2524 draw();
2525 }
2526
2527
converseSetTextLines(int row)2528 void Interface::converseSetTextLines(int row) {
2529 int pos = row + _converseStartPos;
2530 if (pos >= _converseTextCount)
2531 pos = -1;
2532 if (pos != _conversePos) {
2533 _conversePos = pos;
2534 draw();
2535 }
2536 }
2537
converseDisplayTextLines()2538 void Interface::converseDisplayTextLines() {
2539 int relPos;
2540 byte foregnd;
2541 byte backgnd;
2542 byte bulletForegnd;
2543 byte bulletBackgnd;
2544 const char *str;
2545 static const char bulletStr[3][3] = {
2546 "\xb7", "\x81\x45", ">"
2547 };
2548 const char *bullet = (_vm->getGameId() == GID_ITE) ? (_vm->getPlatform() == Common::kPlatformPC98 ? bulletStr[1] : bulletStr[0]) : bulletStr[2];
2549
2550 assert(_conversePanel.buttonsCount >= 6);
2551 Rect rect(8, _vm->getDisplayInfo().converseTextLines * _vm->getDisplayInfo().converseTextHeight);
2552 rect.moveTo(_conversePanel.x + _conversePanel.buttons[0].xOffset, _conversePanel.y + _conversePanel.buttons[0].yOffset);
2553
2554 Point textPoint;
2555
2556 if (_vm->getGameId() == GID_ITE) {
2557 bulletForegnd = kITEColorGreen;
2558 bulletBackgnd = kITEColorBlack;
2559 } else {
2560 bulletForegnd = _vm->KnownColor2ColorId(kKnownColorBrightWhite);
2561 bulletBackgnd = _vm->KnownColor2ColorId(kKnownColorBlack);
2562 }
2563
2564 if (_vm->getGameId() == GID_ITE)
2565 _vm->_gfx->drawRect(rect, kITEColorDarkGrey); // fill bullet place
2566 else if (_vm->getGameId() == GID_IHNM)
2567 // TODO: Add these to IHNM_DisplayInfo?
2568 _vm->_gfx->drawRect(Common::Rect(118, 345, 603, 463), _vm->KnownColor2ColorId(kKnownColorBlack)); // fill converse rect
2569
2570 for (int i = 0; i < _vm->getDisplayInfo().converseTextLines; i++) {
2571 relPos = _converseStartPos + i;
2572
2573 if (_converseTextCount <= relPos) {
2574 break;
2575 }
2576
2577 if (_conversePos >= 0 && _converseText[_conversePos].stringNum == _converseText[relPos].stringNum) {
2578 if (_vm->getGameId() == GID_ITE) {
2579 foregnd = kITEColorBrightWhite;
2580 backgnd = (!_vm->leftMouseButtonPressed()) ? kITEColorDarkGrey : kITEColorGrey;
2581 } else {
2582 foregnd = _vm->KnownColor2ColorId(kKnownColorVerbTextActive);
2583 backgnd = _vm->KnownColor2ColorId(kKnownColorVerbTextActive);
2584 }
2585 } else {
2586 if (_vm->getGameId() == GID_ITE) {
2587 foregnd = kITEColorBlue;
2588 backgnd = kITEColorDarkGrey;
2589 } else {
2590 foregnd = _vm->KnownColor2ColorId(kKnownColorBrightWhite);
2591 backgnd = _vm->KnownColor2ColorId(kKnownColorBlack);
2592 }
2593 }
2594
2595 _conversePanel.calcPanelButtonRect(&_conversePanel.buttons[i], rect);
2596 rect.left += 8;
2597 _vm->_gfx->drawRect(rect, backgnd);
2598
2599 str = &_converseText[relPos].text.front();
2600
2601 if (_converseText[relPos].textNum == 0) { // first entry
2602 textPoint.x = rect.left - 6;
2603 textPoint.y = rect.top;
2604
2605 if (_vm->getGameId() == GID_ITE)
2606 _vm->_font->textDraw(kKnownFontSmall, bullet, textPoint, bulletForegnd, bulletBackgnd, _vm->getPlatform() == Common::kPlatformPC98 ? kFontNormal : (FontEffectFlags)(kFontShadow | kFontDontmap));
2607 else
2608 _vm->_font->textDraw(kKnownFontVerb, bullet, textPoint, bulletForegnd, bulletBackgnd, (FontEffectFlags)(kFontShadow | kFontDontmap));
2609 }
2610 textPoint.x = rect.left + 1;
2611 textPoint.y = rect.top;
2612 if (_vm->getGameId() == GID_ITE)
2613 _vm->_font->textDraw(kKnownFontSmall, str, textPoint, foregnd, kITEColorBlack, _vm->getPlatform() == Common::kPlatformPC98 ? kFontNormal : kFontShadow);
2614 else
2615 _vm->_font->textDraw(kKnownFontVerb, str, textPoint, foregnd, _vm->KnownColor2ColorId(kKnownColorBlack), kFontShadow);
2616 }
2617
2618 if (_converseStartPos != 0) {
2619 drawPanelButtonArrow(&_conversePanel, _converseUpButton);
2620 }
2621
2622 if (_converseStartPos != _converseEndPos) {
2623 drawPanelButtonArrow(&_conversePanel, _converseDownButton);
2624 }
2625 }
2626
converseChangePos(int chg)2627 void Interface::converseChangePos(int chg) {
2628 // Arrows will scroll the converse panel or down up to 4 conversation options
2629 for (int i = 1; i <= 4; i++) {
2630 if ((chg < 0 && _converseStartPos + chg >= 0) ||
2631 (chg > 0 && _converseStartPos < _converseEndPos)) {
2632 _converseStartPos += chg;
2633 }
2634 }
2635 draw();
2636 }
2637
converseSetPos(int key)2638 void Interface::converseSetPos(int key) {
2639 Converse *ct;
2640 int selection = key - '1';
2641
2642 if (selection >= _converseTextCount)
2643 return;
2644
2645 converseSetTextLines(selection);
2646
2647 ct = &_converseText[_conversePos];
2648
2649 _vm->_script->finishDialog(ct->strId, ct->replyId, ct->replyFlags, ct->replyBit);
2650
2651 if (_vm->_scene->isITEPuzzleScene())
2652 _vm->_puzzle->handleReply(ct->replyId);
2653
2654 _conversePos = -1;
2655 }
2656
2657
handleConverseUpdate(const Point & mousePoint)2658 void Interface::handleConverseUpdate(const Point& mousePoint) {
2659 bool changed;
2660
2661 PanelButton *last = _conversePanel.currentButton;
2662
2663 if (!_vm->mouseButtonPressed()) { // remove pressed flag
2664 if (_converseUpButton) {
2665 _converseUpButton->state = 0;
2666 _converseDownButton->state = 0;
2667 }
2668 }
2669
2670 _conversePanel.currentButton = converseHitTest(mousePoint);
2671 changed = last != _conversePanel.currentButton;
2672
2673
2674 if (_conversePanel.currentButton == NULL) {
2675 _conversePos = -1;
2676 if (changed) {
2677 draw();
2678 }
2679 return;
2680 }
2681
2682 if (_conversePanel.currentButton->type == kPanelButtonConverseText) {
2683 converseSetTextLines(_conversePanel.currentButton->id);
2684 }
2685
2686 if (_conversePanel.currentButton->type == kPanelButtonArrow) {
2687 if (_conversePanel.currentButton->state == 1) {
2688 converseChangePos(_conversePanel.currentButton->id);
2689 }
2690 draw();
2691 }
2692 }
2693
2694
handleConverseClick(const Point & mousePoint)2695 void Interface::handleConverseClick(const Point& mousePoint) {
2696 _conversePanel.currentButton = converseHitTest(mousePoint);
2697
2698 if (_conversePanel.currentButton == NULL) {
2699 return;
2700 }
2701
2702 if (_conversePanel.currentButton->type == kPanelButtonConverseText) {
2703 converseSetPos(_conversePanel.currentButton->ascii);
2704 }
2705
2706 if (_conversePanel.currentButton->type == kPanelButtonArrow) {
2707 _conversePanel.currentButton->state = 1;
2708 converseChangePos(_conversePanel.currentButton->id);
2709 }
2710
2711 }
2712
saveState(Common::OutSaveFile * out)2713 void Interface::saveState(Common::OutSaveFile *out) {
2714 out->writeUint16LE(_inventoryCount);
2715
2716 for (int i = 0; i < _inventoryCount; i++) {
2717 out->writeUint16LE(_inventory[i]);
2718 }
2719 }
2720
loadState(Common::InSaveFile * in)2721 void Interface::loadState(Common::InSaveFile *in) {
2722 _inventoryCount = in->readUint16LE();
2723
2724 for (int i = 0; i < _inventoryCount; i++) {
2725 _inventory[i] = in->readUint16LE();
2726 }
2727
2728 updateInventory(0);
2729 }
2730
mapPanelShow()2731 void Interface::mapPanelShow() {
2732 int i;
2733 ByteArray resourceData;
2734 Rect rect;
2735 ByteArray image;
2736 int imageWidth, imageHeight;
2737 const byte *pal;
2738 PalEntry cPal[PAL_ENTRIES];
2739
2740 _vm->_gfx->showCursor(false);
2741
2742 rect.left = rect.top = 0;
2743
2744 _vm->_resource->loadResource(_interfaceContext, _vm->_resource->convertResourceId(RID_ITE_TYCHO_MAP), resourceData);
2745 if (resourceData.empty()) {
2746 error("Interface::mapPanelShow() unable to load Tycho map resource");
2747 }
2748
2749 _vm->_gfx->getCurrentPal(_mapSavedPal);
2750
2751 for (i = 0; i < 6; i++) {
2752 _vm->_gfx->palToBlack(_mapSavedPal, 0.2 * i);
2753 _vm->_render->drawScene();
2754 _vm->_system->delayMillis(5);
2755 }
2756
2757 _vm->_render->setFlag(RF_MAP);
2758
2759 _vm->decodeBGImage(resourceData, image, &imageWidth, &imageHeight);
2760 pal = _vm->getImagePal(resourceData);
2761
2762 for (i = 0; i < PAL_ENTRIES; i++) {
2763 cPal[i].red = *pal++;
2764 cPal[i].green = *pal++;
2765 cPal[i].blue = *pal++;
2766 }
2767
2768 rect.setWidth(imageWidth);
2769 rect.setHeight(imageHeight);
2770
2771 _vm->_gfx->drawRegion(rect, image.getBuffer());
2772
2773 // Evil Evil
2774 for (i = 0; i < 6; i++) {
2775 _vm->_gfx->blackToPal(cPal, 0.2 * i);
2776 _vm->_render->drawScene();
2777 _vm->_system->delayMillis(5);
2778 }
2779
2780
2781 setSaveReminderState(false);
2782
2783 _mapPanelCrossHairState = true;
2784 }
2785
mapPanelClean()2786 void Interface::mapPanelClean() {
2787 PalEntry pal[PAL_ENTRIES];
2788 int i;
2789
2790 _vm->_gfx->getCurrentPal(pal);
2791
2792 for (i = 0; i < 6; i++) {
2793 _vm->_gfx->palToBlack(pal, 0.2 * i);
2794 _vm->_render->drawScene();
2795 _vm->_system->delayMillis(5);
2796 }
2797
2798 _vm->_render->clearFlag(RF_MAP);
2799 setMode(kPanelMain);
2800
2801 _vm->_gfx->showCursor(true);
2802 _vm->_render->drawScene();
2803
2804 for (i = 0; i < 6; i++) {
2805 _vm->_gfx->blackToPal(_mapSavedPal, 0.2 * i);
2806 _vm->_render->drawScene();
2807 _vm->_system->delayMillis(5);
2808 }
2809 }
2810
mapPanelDrawCrossHair()2811 void Interface::mapPanelDrawCrossHair() {
2812 _mapPanelCrossHairState = !_mapPanelCrossHairState;
2813
2814 Point mapPosition = _vm->_isoMap->getMapPosition();
2815 Rect screen(_vm->getDisplayInfo().width, _vm->_scene->getHeight());
2816
2817 if (screen.contains(mapPosition)) {
2818 _vm->_sprite->draw(_vm->_sprite->_mainSprites,
2819 _mapPanelCrossHairState ? RID_ITE_SPR_CROSSHAIR : RID_ITE_SPR_CROSSHAIR + 1,
2820 mapPosition, 256);
2821 }
2822 }
2823
keyBoss()2824 void Interface::keyBoss() {
2825 if (_vm->getGameId() == GID_ITE)
2826 return;
2827
2828 if (_bossMode != -1 || _fadeMode != kNoFade)
2829 return;
2830
2831 _vm->_sound->pauseVoice();
2832 _vm->_sound->pauseSound();
2833 _vm->_music->pause();
2834
2835 int i;
2836 ByteArray resourceData;
2837 Rect rect;
2838 ByteArray image;
2839 int imageWidth, imageHeight;
2840 //const byte *pal;
2841 PalEntry cPal[PAL_ENTRIES];
2842
2843 _vm->_gfx->showCursor(false);
2844
2845 rect.left = rect.top = 0;
2846
2847 _vm->_resource->loadResource(_interfaceContext, RID_IHNM_BOSS_SCREEN, resourceData);
2848 if (resourceData.empty()) {
2849 error("Interface::bossKey() unable to load Boss image resource");
2850 }
2851
2852 _bossMode = _panelMode;
2853 setMode(kPanelBoss);
2854
2855 _vm->decodeBGImage(resourceData, image, &imageWidth, &imageHeight);
2856 rect.setWidth(imageWidth);
2857 rect.setHeight(imageHeight);
2858
2859 _vm->_gfx->getCurrentPal(_mapSavedPal);
2860 //pal = _vm->getImagePal(resourceData);
2861
2862 cPal[0].red = 0;
2863 cPal[0].green = 0;
2864 cPal[0].blue = 0;
2865
2866 for (i = 1; i < PAL_ENTRIES; i++) {
2867 cPal[i].red = 128;
2868 cPal[i].green = 128;
2869 cPal[i].blue = 128;
2870 }
2871
2872 _vm->_gfx->drawRegion(rect, image.getBuffer());
2873
2874 _vm->_gfx->setPalette(cPal);
2875 }
2876
2877
keyBossExit()2878 void Interface::keyBossExit() {
2879 PalEntry pal[PAL_ENTRIES];
2880
2881 _vm->_sound->resumeVoice();
2882 _vm->_sound->resumeSound();
2883 _vm->_music->resume();
2884
2885 _vm->_gfx->getCurrentPal(pal);
2886
2887 _vm->_gfx->palToBlack(pal, 1);
2888 setMode(_bossMode);
2889
2890 _vm->_render->drawScene();
2891
2892 _vm->_gfx->blackToPal(_mapSavedPal, 1);
2893
2894 _vm->_gfx->showCursor(true);
2895
2896 _bossMode = -1;
2897 }
2898
2899
2900 } // End of namespace Saga
2901