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