1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "common/config-manager.h"
24 #include "common/translation.h"
25
26 #include "audio/mixer.h"
27
28 #include "gui/saveload.h"
29
30 #include "neverhood/menumodule.h"
31 #include "neverhood/gamemodule.h"
32
33 #include "engines/savestate.h"
34
35 namespace Neverhood {
36
37 enum {
38 MAIN_MENU = 0,
39 CREDITS_SCENE = 1,
40 MAKING_OF = 2,
41 LOAD_GAME_MENU = 3,
42 SAVE_GAME_MENU = 4,
43 DELETE_GAME_MENU = 5,
44 QUERY_OVR_MENU = 6
45 };
46
47 enum {
48 kMainMenuRestartGame = 0,
49 kMainMenuLoadGame = 1,
50 kMainMenuSaveGame = 2,
51 kMainMenuResumeGame = 3,
52 kMainMenuQuitGame = 4,
53 kMainMenuCredits = 5,
54 kMainMenuMakingOf = 6,
55 kMainMenuToggleMusic = 7,
56 kMainMenuDeleteGame = 8
57 };
58
59 static const uint32 kMakingOfSmackerFileHashList[] = {
60 0x21082409,
61 0x21082809,
62 0x21083009,
63 0x21080009,
64 0x21086009,
65 0x2108A009,
66 0x21092009,
67 0x210A2009,
68 0x210C2009,
69 0x21082411,
70 0x21082811,
71 0x21083011,
72 0x21080011,
73 0
74 };
75
MenuModule(NeverhoodEngine * vm,Module * parentModule,int which)76 MenuModule::MenuModule(NeverhoodEngine *vm, Module *parentModule, int which)
77 : Module(vm, parentModule), _savegameList(NULL) {
78
79 SetMessageHandler(&MenuModule::handleMessage);
80
81 _savedPaletteData = _vm->_screen->getPaletteData();
82 _vm->_mixer->pauseAll(true);
83 _vm->toggleSoundUpdate(false);
84
85 createScene(MAIN_MENU, -1);
86 }
87
~MenuModule()88 MenuModule::~MenuModule() {
89 _vm->_mixer->pauseAll(false);
90 _vm->toggleSoundUpdate(true);
91 _vm->_screen->setPaletteData(_savedPaletteData);
92 }
93
setLoadgameInfo(uint index)94 void MenuModule::setLoadgameInfo(uint index) {
95 _savegameSlot = (*_savegameList)[index].slotNum;
96 }
97
setLoadgameSlot(int slot)98 void MenuModule::setLoadgameSlot(int slot) {
99 _savegameSlot = slot;
100 }
101
setSavegameInfo(const Common::String & description,uint index,bool newSavegame)102 void MenuModule::setSavegameInfo(const Common::String &description, uint index, bool newSavegame) {
103 _savegameDescription = description;
104 _savegameSlot = newSavegame ? -1 : (*_savegameList)[index].slotNum;
105 }
106
setDeletegameInfo(uint index)107 void MenuModule::setDeletegameInfo(uint index) {
108 _savegameSlot = (*_savegameList)[index].slotNum;
109 }
110
createScene(int sceneNum,int which)111 void MenuModule::createScene(int sceneNum, int which) {
112 _sceneNum = sceneNum;
113 switch (_sceneNum) {
114 case MAIN_MENU:
115 _childObject = new MainMenu(_vm, this);
116 break;
117 case CREDITS_SCENE:
118 _childObject = new CreditsScene(_vm, this, true);
119 break;
120 case MAKING_OF:
121 createSmackerScene(kMakingOfSmackerFileHashList, ConfMan.getBool("scalemakingofvideos"), true, true);
122 break;
123 case LOAD_GAME_MENU:
124 createLoadGameMenu();
125 break;
126 case SAVE_GAME_MENU:
127 createSaveGameMenu();
128 break;
129 case DELETE_GAME_MENU:
130 createDeleteGameMenu();
131 break;
132 case QUERY_OVR_MENU:
133 _childObject = new QueryOverwriteMenu(_vm, this, _savegameDescription);
134 break;
135 default:
136 break;
137 }
138 SetUpdateHandler(&MenuModule::updateScene);
139 _childObject->handleUpdate();
140 }
141
updateScene()142 void MenuModule::updateScene() {
143 if (!updateChild()) {
144 switch (_sceneNum) {
145 case MAIN_MENU:
146 switch (_moduleResult) {
147 case kMainMenuRestartGame:
148 _vm->_gameModule->requestRestartGame(false);
149 leaveModule(0);
150 break;
151 case kMainMenuLoadGame:
152 createScene(LOAD_GAME_MENU, -1);
153 break;
154 case kMainMenuSaveGame:
155 createScene(SAVE_GAME_MENU, -1);
156 break;
157 case kMainMenuResumeGame:
158 leaveModule(0);
159 break;
160 case kMainMenuQuitGame:
161 _vm->quitGame();
162 break;
163 case kMainMenuCredits:
164 createScene(CREDITS_SCENE, -1);
165 break;
166 case kMainMenuMakingOf:
167 createScene(MAKING_OF, -1);
168 break;
169 case kMainMenuToggleMusic:
170 _vm->toggleMusic(!_vm->musicIsEnabled());
171 _vm->_mixer->muteSoundType(Audio::Mixer::kMusicSoundType, !_vm->musicIsEnabled());
172 createScene(MAIN_MENU, -1);
173 break;
174 case kMainMenuDeleteGame:
175 createScene(DELETE_GAME_MENU, -1);
176 break;
177 default:
178 createScene(MAIN_MENU, -1);
179 break;
180 }
181 break;
182 case CREDITS_SCENE:
183 case MAKING_OF:
184 createScene(MAIN_MENU, -1);
185 break;
186 case LOAD_GAME_MENU:
187 handleLoadGameMenuAction(_moduleResult != 1);
188 break;
189 case SAVE_GAME_MENU:
190 handleSaveGameMenuAction(_moduleResult != 1, true);
191 break;
192 case DELETE_GAME_MENU:
193 handleDeleteGameMenuAction(_moduleResult != 1);
194 break;
195 case QUERY_OVR_MENU:
196 handleSaveGameMenuAction(_moduleResult != 1, false);
197 break;
198 default:
199 break;
200 }
201 }
202 }
203
handleMessage(int messageNum,const MessageParam & param,Entity * sender)204 uint32 MenuModule::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) {
205 switch(messageNum) {
206 case NM_KEYPRESS_ESC:
207 leaveModule(0);
208 break;
209 default:
210 break;
211 }
212
213 return Module::handleMessage(messageNum, param, sender);
214 }
215
createLoadGameMenu()216 void MenuModule::createLoadGameMenu() {
217 refreshSaveGameList();
218 _childObject = new LoadGameMenu(_vm, this, _savegameList);
219 }
220
createSaveGameMenu()221 void MenuModule::createSaveGameMenu() {
222 refreshSaveGameList();
223 _childObject = new SaveGameMenu(_vm, this, _savegameList);
224 }
225
createDeleteGameMenu()226 void MenuModule::createDeleteGameMenu() {
227 refreshSaveGameList();
228 _childObject = new DeleteGameMenu(_vm, this, _savegameList);
229 }
230
refreshSaveGameList()231 void MenuModule::refreshSaveGameList() {
232 _savegameSlot = -1;
233 delete _savegameList;
234 _savegameList = NULL;
235 _savegameList = new SavegameList();
236 loadSavegameList();
237 }
238
handleLoadGameMenuAction(bool doLoad)239 void MenuModule::handleLoadGameMenuAction(bool doLoad) {
240 createScene(MAIN_MENU, -1);
241 if (doLoad && _savegameSlot >= 0) {
242 _vm->loadGameState(_savegameSlot);
243 leaveModule(0);
244 }
245 delete _savegameList;
246 _savegameList = NULL;
247 }
248
handleSaveGameMenuAction(bool doSave,bool doQuery)249 void MenuModule::handleSaveGameMenuAction(bool doSave, bool doQuery) {
250 if (doSave && doQuery && _savegameSlot >= 0) {
251 createScene(QUERY_OVR_MENU, -1);
252 } else if (doSave) {
253 // Get a new slot number if it's a new savegame
254 if (_savegameSlot < 0)
255 _savegameSlot = _savegameList->size() > 0 ? _savegameList->back().slotNum + 1 : 0;
256 // Restore the scene palette and background so that the correct thumbnail is saved
257 byte *menuPaletteData = _vm->_screen->getPaletteData();
258 _vm->_screen->setPaletteData(_savedPaletteData);
259 _vm->_gameModule->redrawPrevChildObject();
260 _vm->saveGameState(_savegameSlot, _savegameDescription);
261 _vm->_screen->setPaletteData(menuPaletteData);
262 createScene(MAIN_MENU, -1);
263 } else {
264 createScene(MAIN_MENU, -1);
265 }
266 delete _savegameList;
267 _savegameList = NULL;
268 }
269
handleDeleteGameMenuAction(bool doDelete)270 void MenuModule::handleDeleteGameMenuAction(bool doDelete) {
271 createScene(MAIN_MENU, -1);
272 if (doDelete && _savegameSlot >= 0) {
273 _vm->removeGameState(_savegameSlot);
274 }
275 delete _savegameList;
276 _savegameList = NULL;
277 }
278
loadSavegameList()279 void MenuModule::loadSavegameList() {
280
281 Common::SaveFileManager *saveFileMan = _vm->_system->getSavefileManager();
282 Neverhood::NeverhoodEngine::SaveHeader header;
283 Common::String pattern = _vm->getTargetName();
284 pattern += ".???";
285
286 Common::StringArray filenames;
287 filenames = saveFileMan->listSavefiles(pattern.c_str());
288 Common::sort(filenames.begin(), filenames.end());
289
290 SaveStateList saveList;
291 for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); file++) {
292 int slotNum = atoi(file->c_str() + file->size() - 3);
293 if (slotNum >= 0 && slotNum <= 999) {
294 Common::InSaveFile *in = saveFileMan->openForLoading(file->c_str());
295 if (in) {
296 if (Neverhood::NeverhoodEngine::readSaveHeader(in, header) == Neverhood::NeverhoodEngine::kRSHENoError) {
297 SavegameItem savegameItem;
298 savegameItem.slotNum = slotNum;
299 savegameItem.description = header.description;
300 _savegameList->push_back(savegameItem);
301 }
302 delete in;
303 }
304 }
305 }
306
307 }
308
MenuButton(NeverhoodEngine * vm,Scene * parentScene,uint buttonIndex,uint32 fileHash,const NRect & collisionBounds)309 MenuButton::MenuButton(NeverhoodEngine *vm, Scene *parentScene, uint buttonIndex, uint32 fileHash, const NRect &collisionBounds)
310 : StaticSprite(vm, 900), _parentScene(parentScene), _buttonIndex(buttonIndex), _countdown(0) {
311
312 loadSprite(fileHash, kSLFDefDrawOffset | kSLFDefPosition, 100);
313 _collisionBounds = collisionBounds;
314 setVisible(false);
315 SetUpdateHandler(&MenuButton::update);
316 SetMessageHandler(&MenuButton::handleMessage);
317 }
318
update()319 void MenuButton::update() {
320 updatePosition();
321 if (_countdown != 0 && (--_countdown) == 0) {
322 setVisible(false);
323 sendMessage(_parentScene, 0x2000, _buttonIndex);
324 }
325 }
326
handleMessage(int messageNum,const MessageParam & param,Entity * sender)327 uint32 MenuButton::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) {
328 uint32 messageResult = Sprite::handleMessage(messageNum, param, sender);
329 switch (messageNum) {
330 case 0x1011:
331 if (_countdown == 0) {
332 setVisible(true);
333 _countdown = 4;
334 }
335 messageResult = 1;
336 break;
337 default:
338 break;
339 }
340 return messageResult;
341 }
342
MainMenu(NeverhoodEngine * vm,Module * parentModule)343 MainMenu::MainMenu(NeverhoodEngine *vm, Module *parentModule)
344 : Scene(vm, parentModule) {
345
346 static const uint32 kMenuButtonFileHashes[] = {
347 0x36C62120,
348 0x56C62120,
349 0x96C62120,
350 0x16C62121,
351 0x16C62122,
352 0x16C62124,
353 0x16C62128,
354 0x16C62130,
355 0x16C62100
356 };
357
358 static const NRect kMenuButtonCollisionBounds[] = {
359 { 52, 121, 110, 156 },
360 { 52, 192, 109, 222 },
361 { 60, 257, 119, 286 },
362 { 67, 326, 120, 354 },
363 { 70, 389, 128, 416 },
364 { 523, 113, 580, 144 },
365 { 525, 176, 577, 206 },
366 { 527, 384, 580, 412 },
367 { 522, 255, 580, 289 }
368 };
369
370 setBackground(0x08C0020C);
371 setPalette(0x08C0020C);
372 insertScreenMouse(0x00208084);
373
374 insertStaticSprite(0x41137051, 100); // "Options" header text
375 insertStaticSprite(0xC10B2015, 100); // Button texts
376
377 if (!_vm->musicIsEnabled())
378 insertStaticSprite(0x0C24C0EE, 100); // "Music is off" button
379
380 for (uint buttonIndex = 0; buttonIndex < 9; ++buttonIndex) {
381 Sprite *menuButton = insertSprite<MenuButton>(this, buttonIndex,
382 kMenuButtonFileHashes[buttonIndex], kMenuButtonCollisionBounds[buttonIndex]);
383 addCollisionSprite(menuButton);
384 }
385
386 SetUpdateHandler(&Scene::update);
387 SetMessageHandler(&MainMenu::handleMessage);
388
389 }
390
handleMessage(int messageNum,const MessageParam & param,Entity * sender)391 uint32 MainMenu::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) {
392 Scene::handleMessage(messageNum, param, sender);
393 switch (messageNum) {
394 case NM_ANIMATION_UPDATE:
395 leaveScene(param.asInteger());
396 break;
397 default:
398 break;
399 }
400 return 0;
401 }
402
403 static const uint32 kCreditsSceneFileHashes[] = {
404 0x6081128C, 0x608112BC, 0x608112DC,
405 0x6081121C, 0x6081139C, 0x6081109C,
406 0x6081169C, 0x60811A9C, 0x6081029C,
407 0x0081128C, 0x008112BC, 0x008012BC,
408 0x008112DC, 0x0081121C, 0x0081139C,
409 0x0081109C, 0x0081169C, 0x00811A9C,
410 0x0081029C, 0x0081329C, 0xC08112BC,
411 0xC08112DC, 0xC081121C, 0xC081139C,
412 0
413 };
414
CreditsScene(NeverhoodEngine * vm,Module * parentModule,bool canAbort)415 CreditsScene::CreditsScene(NeverhoodEngine *vm, Module *parentModule, bool canAbort)
416 : Scene(vm, parentModule), _canAbort(canAbort), _screenIndex(0), _ticksDuration(0),
417 _countdown(216) {
418
419 SetUpdateHandler(&CreditsScene::update);
420 SetMessageHandler(&CreditsScene::handleMessage);
421
422 setBackground(0x6081128C);
423 setPalette(0x6081128C);
424
425 _ticksTime = _vm->_system->getMillis() + 202100;
426
427 _musicResource = new MusicResource(_vm);
428 _musicResource->load(0x30812225);
429 _musicResource->play(0);
430
431 }
432
~CreditsScene()433 CreditsScene::~CreditsScene() {
434 _musicResource->unload();
435 delete _musicResource;
436 }
437
update()438 void CreditsScene::update() {
439 Scene::update();
440 if (_countdown != 0) {
441 if (_screenIndex == 23 && _vm->_system->getMillis() > _ticksTime)
442 leaveScene(0);
443 else if ((--_countdown) == 0) {
444 ++_screenIndex;
445 if (kCreditsSceneFileHashes[_screenIndex] == 0)
446 leaveScene(0);
447 else {
448 _background->load(kCreditsSceneFileHashes[_screenIndex]);
449 _palette->addPalette(kCreditsSceneFileHashes[_screenIndex], 0, 256, 0);
450 if (_screenIndex < 5)
451 _countdown = 192;
452 else if (_screenIndex < 15)
453 _countdown = 144;
454 else if (_screenIndex < 16)
455 _countdown = 216;
456 else if (_screenIndex < 23)
457 _countdown = 144;
458 else
459 _countdown = 1224;
460 }
461 }
462 }
463 }
464
handleMessage(int messageNum,const MessageParam & param,Entity * sender)465 uint32 CreditsScene::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) {
466 Scene::handleMessage(messageNum, param, sender);
467 switch (messageNum) {
468 case NM_KEYPRESS_SPACE:
469 leaveScene(0);
470 break;
471 case 0x000B:
472 if (param.asInteger() == Common::KEYCODE_ESCAPE && _canAbort)
473 leaveScene(0);
474 break;
475 case NM_MOUSE_HIDE:
476 _ticksDuration = _ticksTime - _vm->_system->getMillis();
477 break;
478 case NM_MOUSE_SHOW:
479 _ticksTime = _ticksDuration + _vm->_system->getMillis();
480 break;
481 default:
482 break;
483 }
484 return 0;
485 }
486
Widget(NeverhoodEngine * vm,int16 x,int16 y,GameStateMenu * parentScene,int baseObjectPriority,int baseSurfacePriority)487 Widget::Widget(NeverhoodEngine *vm, int16 x, int16 y, GameStateMenu *parentScene,
488 int baseObjectPriority, int baseSurfacePriority)
489 : StaticSprite(vm, baseObjectPriority), _parentScene(parentScene),
490 _baseObjectPriority(baseObjectPriority), _baseSurfacePriority(baseSurfacePriority) {
491
492 SetUpdateHandler(&Widget::update);
493 SetMessageHandler(&Widget::handleMessage);
494
495 setPosition(x, y);
496 }
497
onClick()498 void Widget::onClick() {
499 _parentScene->setCurrWidget(this);
500 }
501
setPosition(int16 x,int16 y)502 void Widget::setPosition(int16 x, int16 y) {
503 _x = x;
504 _y = y;
505 updateBounds();
506 }
507
refreshPosition()508 void Widget::refreshPosition() {
509 _needRefresh = true;
510 StaticSprite::updatePosition();
511 _collisionBoundsOffset.set(0, 0,
512 _spriteResource.getDimensions().width, _spriteResource.getDimensions().height);
513 updateBounds();
514 }
515
initialize()516 void Widget::initialize() {
517 // Empty
518 }
519
getWidth()520 int16 Widget::getWidth() {
521 return _spriteResource.getDimensions().width;
522 }
523
getHeight()524 int16 Widget::getHeight() {
525 return _spriteResource.getDimensions().height;
526 }
527
enterWidget()528 void Widget::enterWidget() {
529 // Empty
530 }
531
exitWidget()532 void Widget::exitWidget() {
533 // Empty
534 }
535
update()536 void Widget::update() {
537 handleSpriteUpdate();
538 StaticSprite::updatePosition();
539 }
540
handleMessage(int messageNum,const MessageParam & param,Entity * sender)541 uint32 Widget::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) {
542 uint32 messageResult = Sprite::handleMessage(messageNum, param, sender);
543 switch (messageNum) {
544 case 0x1011:
545 onClick();
546 messageResult = 1;
547 break;
548 default:
549 break;
550 }
551 return messageResult;
552 }
553
TextLabelWidget(NeverhoodEngine * vm,int16 x,int16 y,GameStateMenu * parentScene,int baseObjectPriority,int baseSurfacePriority,const byte * string,int stringLen,BaseSurface * drawSurface,int16 tx,int16 ty,FontSurface * fontSurface)554 TextLabelWidget::TextLabelWidget(NeverhoodEngine *vm, int16 x, int16 y, GameStateMenu *parentScene,
555 int baseObjectPriority, int baseSurfacePriority,
556 const byte *string, int stringLen, BaseSurface *drawSurface, int16 tx, int16 ty, FontSurface *fontSurface)
557 : Widget(vm, x, y, parentScene, baseObjectPriority, baseSurfacePriority),
558 _string(string), _stringLen(stringLen), _drawSurface(drawSurface), _tx(tx), _ty(ty), _fontSurface(fontSurface) {
559
560 }
561
initialize()562 void TextLabelWidget::initialize() {
563 _parentScene->addSprite(this);
564 _parentScene->addCollisionSprite(this);
565 }
566
getWidth()567 int16 TextLabelWidget::getWidth() {
568 return _fontSurface->getStringWidth(_string, _stringLen);
569 }
570
getHeight()571 int16 TextLabelWidget::getHeight() {
572 return _fontSurface->getCharHeight();
573 }
574
drawString(int maxStringLength)575 void TextLabelWidget::drawString(int maxStringLength) {
576 _fontSurface->drawString(_drawSurface, _x, _y, _string, MIN(_stringLen, maxStringLength));
577 _collisionBoundsOffset.set(_tx, _ty, getWidth(), getHeight());
578 updateBounds();
579 }
580
clear()581 void TextLabelWidget::clear() {
582 _collisionBoundsOffset.set(0, 0, 0, 0);
583 updateBounds();
584 }
585
setString(const byte * string,int stringLen)586 void TextLabelWidget::setString(const byte *string, int stringLen) {
587 _string = string;
588 _stringLen = stringLen;
589 }
590
TextEditWidget(NeverhoodEngine * vm,int16 x,int16 y,GameStateMenu * parentScene,int maxStringLength,FontSurface * fontSurface,uint32 fileHash,const NRect & rect)591 TextEditWidget::TextEditWidget(NeverhoodEngine *vm, int16 x, int16 y, GameStateMenu *parentScene,
592 int maxStringLength, FontSurface *fontSurface, uint32 fileHash, const NRect &rect)
593 : Widget(vm, x, y, parentScene, 1000, 1000),
594 _maxStringLength(maxStringLength), _fontSurface(fontSurface), _fileHash(fileHash), _rect(rect),
595 _cursorSurface(NULL), _cursorTicks(0), _cursorPos(0), _cursorFileHash(0), _cursorWidth(0), _cursorHeight(0),
596 _modified(false), _readOnly(false) {
597
598 _maxVisibleChars = (_rect.x2 - _rect.x1) / _fontSurface->getCharWidth();
599 _cursorPos = 0;
600 _textLabelWidget = NULL;
601
602 SetUpdateHandler(&TextEditWidget::update);
603 SetMessageHandler(&TextEditWidget::handleMessage);
604 }
605
~TextEditWidget()606 TextEditWidget::~TextEditWidget() {
607 delete _cursorSurface;
608 }
609
onClick()610 void TextEditWidget::onClick() {
611 NPoint mousePos = _parentScene->getMousePos();
612 mousePos.x -= _x + _rect.x1;
613 mousePos.y -= _y + _rect.y1;
614 if (mousePos.x >= 0 && mousePos.x <= _rect.x2 - _rect.x1 &&
615 mousePos.y >= 0 && mousePos.y <= _rect.y2 - _rect.y1) {
616 if (_entryString.size() == 1)
617 _cursorPos = 0;
618 else {
619 int newCursorPos = mousePos.x / _fontSurface->getCharWidth();
620 if (mousePos.x % _fontSurface->getCharWidth() > _fontSurface->getCharWidth() / 2 && newCursorPos <= (int)_entryString.size())
621 ++newCursorPos;
622 _cursorPos = MIN((int)_entryString.size(), newCursorPos);
623 }
624 if (!_readOnly)
625 _cursorSurface->setVisible(true);
626 refresh();
627 }
628 Widget::onClick();
629 }
630
initialize()631 void TextEditWidget::initialize() {
632 SpriteResource cursorSpriteResource(_vm);
633
634 _spriteResource.load(_fileHash, true);
635 createSurface(_baseSurfacePriority, _spriteResource.getDimensions().width, _spriteResource.getDimensions().height);
636 refreshPosition();
637 _parentScene->addSprite(this);
638 _parentScene->addCollisionSprite(this);
639 _surface->setVisible(true);
640 _textLabelWidget = new TextLabelWidget(_vm, _rect.x1, _rect.y1 + (_rect.y2 - _rect.y1 + 1 - _fontSurface->getCharHeight()) / 2,
641 _parentScene, _baseObjectPriority + 1, _baseSurfacePriority + 1,
642 (const byte*)_entryString.c_str(), _entryString.size(), _surface, _x, _y, _fontSurface);
643 _textLabelWidget->initialize();
644 if (_cursorFileHash != 0) {
645 cursorSpriteResource.load(_cursorFileHash, true);
646 _cursorSurface = new BaseSurface(_vm, 0, cursorSpriteResource.getDimensions().width, cursorSpriteResource.getDimensions().height, "cursor");
647 _cursorSurface->drawSpriteResourceEx(cursorSpriteResource, false, false, cursorSpriteResource.getDimensions().width, cursorSpriteResource.getDimensions().height);
648 _cursorSurface->setVisible(!_readOnly);
649 }
650 refresh();
651 }
652
enterWidget()653 void TextEditWidget::enterWidget() {
654 if (!_readOnly) {
655 _cursorSurface->setVisible(true);
656 _vm->_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, true);
657 }
658 refresh();
659 }
660
exitWidget()661 void TextEditWidget::exitWidget() {
662 if (!_readOnly) {
663 _cursorSurface->setVisible(false);
664 _vm->_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false);
665 }
666 refresh();
667 }
668
setCursor(uint32 cursorFileHash,int16 cursorWidth,int16 cursorHeight)669 void TextEditWidget::setCursor(uint32 cursorFileHash, int16 cursorWidth, int16 cursorHeight) {
670 _cursorFileHash = cursorFileHash;
671 _cursorWidth = cursorWidth;
672 _cursorHeight = cursorHeight;
673 }
674
drawCursor()675 void TextEditWidget::drawCursor() {
676 if (_cursorSurface->getVisible() && _cursorPos >= 0 && _cursorPos <= _maxVisibleChars) {
677 NDrawRect sourceRect(0, 0, _cursorWidth, _cursorHeight);
678 _surface->copyFrom(_cursorSurface->getSurface(), _rect.x1 + _cursorPos * _fontSurface->getCharWidth(),
679 _rect.y1 + (_rect.y2 - _cursorHeight - _rect.y1 + 1) / 2, sourceRect);
680 } else if (!_readOnly)
681 _cursorSurface->setVisible(false);
682 }
683
updateString()684 void TextEditWidget::updateString() {
685 _textLabelWidget->setString((const byte *)_entryString.c_str(), _entryString.size());
686 _textLabelWidget->drawString(_maxVisibleChars);
687 }
688
getString()689 Common::String& TextEditWidget::getString() {
690 return _entryString;
691 }
692
setString(const Common::String & string)693 void TextEditWidget::setString(const Common::String &string) {
694 _entryString = string;
695 _cursorPos = _entryString.size();
696 _modified = false;
697 refresh();
698 }
699
handleAsciiKey(char ch)700 void TextEditWidget::handleAsciiKey(char ch) {
701 if ((int)_entryString.size() < _maxStringLength &&
702 ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') || ch == ' ')) {
703 _entryString.insertChar(ch, _cursorPos);
704 ++_cursorPos;
705 _modified = true;
706 refresh();
707 }
708 }
709
handleKeyDown(Common::KeyCode keyCode)710 void TextEditWidget::handleKeyDown(Common::KeyCode keyCode) {
711 bool doRefresh = true;
712 switch (keyCode) {
713 case Common::KEYCODE_HOME:
714 _cursorPos = 0;
715 break;
716 case Common::KEYCODE_END:
717 _cursorPos = _entryString.size();
718 break;
719 case Common::KEYCODE_LEFT:
720 if (_entryString.size() > 0 && _cursorPos > 0)
721 --_cursorPos;
722 break;
723 case Common::KEYCODE_RIGHT:
724 if (_cursorPos < (int)_entryString.size())
725 ++_cursorPos;
726 break;
727 case Common::KEYCODE_DELETE:
728 if (_entryString.size() > 0 && _cursorPos < (int)_entryString.size()) {
729 _entryString.deleteChar(_cursorPos);
730 _modified = true;
731 }
732 break;
733 case Common::KEYCODE_BACKSPACE:
734 if (_entryString.size() > 0 && _cursorPos > 0) {
735 _entryString.deleteChar(--_cursorPos);
736 _modified = true;
737 }
738 break;
739 default:
740 doRefresh = false;
741 break;
742 }
743 if (doRefresh) {
744 _cursorSurface->setVisible(!_readOnly);
745 _cursorTicks = 0;
746 refresh();
747 }
748 }
749
refresh()750 void TextEditWidget::refresh() {
751 refreshPosition();
752 updateString();
753 if (_cursorFileHash != 0)
754 drawCursor();
755 }
756
update()757 void TextEditWidget::update() {
758 Widget::update();
759 if (!_readOnly && _parentScene->getCurrWidget() == this && _cursorTicks++ == 10) {
760 _cursorSurface->setVisible(!_cursorSurface->getVisible());
761 refresh();
762 _cursorTicks = 0;
763 }
764 }
765
handleMessage(int messageNum,const MessageParam & param,Entity * sender)766 uint32 TextEditWidget::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) {
767 uint32 messageResult = Widget::handleMessage(messageNum, param, sender);
768 switch (messageNum) {
769 case 0x000A:
770 handleAsciiKey(param.asInteger());
771 break;
772 case 0x000B:
773 handleKeyDown((Common::KeyCode)param.asInteger());
774 break;
775 default:
776 break;
777 }
778 return messageResult;
779 }
780
SavegameListBox(NeverhoodEngine * vm,int16 x,int16 y,GameStateMenu * parentScene,SavegameList * savegameList,FontSurface * fontSurface,uint32 bgFileHash,const NRect & rect)781 SavegameListBox::SavegameListBox(NeverhoodEngine *vm, int16 x, int16 y, GameStateMenu *parentScene,
782 SavegameList *savegameList, FontSurface *fontSurface, uint32 bgFileHash, const NRect &rect)
783 : Widget(vm, x, y, parentScene, 1000, 1000),
784 _savegameList(savegameList), _fontSurface(fontSurface), _bgFileHash(bgFileHash), _rect(rect),
785 _maxStringLength(0), _firstVisibleItem(0), _lastVisibleItem(0), _currIndex(0) {
786
787 _maxVisibleItemsCount = (_rect.y2 - _rect.y1) / _fontSurface->getCharHeight();
788 _maxStringLength = (_rect.x2 - _rect.x1) / _fontSurface->getCharWidth();
789 }
790
onClick()791 void SavegameListBox::onClick() {
792 NPoint mousePos = _parentScene->getMousePos();
793 mousePos.x -= _x + _rect.x1;
794 mousePos.y -= _y + _rect.y1;
795 if (mousePos.x >= 0 && mousePos.x <= _rect.x2 - _rect.x1 &&
796 mousePos.y >= 0 && mousePos.y <= _rect.y2 - _rect.y1) {
797 int newIndex = _firstVisibleItem + mousePos.y / _fontSurface->getCharHeight();
798 if (newIndex <= _lastVisibleItem) {
799 _currIndex = newIndex;
800 refresh();
801 _parentScene->setCurrWidget(this);
802 _parentScene->refreshDescriptionEdit();
803 }
804 }
805 }
806
initialize()807 void SavegameListBox::initialize() {
808 _spriteResource.load(_bgFileHash, true);
809 createSurface(_baseSurfacePriority, _spriteResource.getDimensions().width, _spriteResource.getDimensions().height);
810 refreshPosition();
811 _parentScene->addSprite(this);
812 _parentScene->addCollisionSprite(this);
813 _surface->setVisible(true);
814 buildItems();
815 _firstVisibleItem = 0;
816 _lastVisibleItem = MIN(_maxVisibleItemsCount, (int)_textLabelItems.size()) - 1;
817 refresh();
818 }
819
buildItems()820 void SavegameListBox::buildItems() {
821 SavegameList &savegameList = *_savegameList;
822 int16 itemX = _rect.x1, itemY = 0;
823 for (uint i = 0; i < savegameList.size(); ++i) {
824 const byte *string = (const byte*)savegameList[i].description.c_str();
825 int stringLen = (int)savegameList[i].description.size();
826 TextLabelWidget *label = new TextLabelWidget(_vm, itemX, itemY, _parentScene, _baseObjectPriority + 1,
827 _baseSurfacePriority + 1, string, MIN(stringLen, _maxStringLength), _surface, _x, _y, _fontSurface);
828 label->initialize();
829 _textLabelItems.push_back(label);
830 }
831 }
832
drawItems()833 void SavegameListBox::drawItems() {
834 for (int i = 0; i < (int)_textLabelItems.size(); ++i) {
835 TextLabelWidget *label = _textLabelItems[i];
836 if (i >= _firstVisibleItem && i <= _lastVisibleItem) {
837 label->setY(_rect.y1 + (i - _firstVisibleItem) * _fontSurface->getCharHeight());
838 label->updateBounds();
839 label->drawString(_maxStringLength);
840 } else
841 label->clear();
842 }
843 }
844
refresh()845 void SavegameListBox::refresh() {
846 refreshPosition();
847 drawItems();
848 }
849
scrollUp()850 void SavegameListBox::scrollUp() {
851 if (_firstVisibleItem > 0) {
852 --_firstVisibleItem;
853 --_lastVisibleItem;
854 refresh();
855 }
856 }
857
scrollDown()858 void SavegameListBox::scrollDown() {
859 if (_lastVisibleItem < (int)_textLabelItems.size() - 1) {
860 ++_firstVisibleItem;
861 ++_lastVisibleItem;
862 refresh();
863 }
864 }
865
pageUp()866 void SavegameListBox::pageUp() {
867 int amount = MIN(_firstVisibleItem, _maxVisibleItemsCount);
868 if (amount > 0) {
869 _firstVisibleItem -= amount;
870 _lastVisibleItem -= amount;
871 refresh();
872 }
873 }
874
pageDown()875 void SavegameListBox::pageDown() {
876 int amount = MIN((int)_textLabelItems.size() - _lastVisibleItem - 1, _maxVisibleItemsCount);
877 if (amount > 0) {
878 _firstVisibleItem += amount;
879 _lastVisibleItem += amount;
880 refresh();
881 }
882 }
883
scummVMSaveLoadDialog(bool isSave,Common::String & saveDesc)884 int GameStateMenu::scummVMSaveLoadDialog(bool isSave, Common::String &saveDesc) {
885 GUI::SaveLoadChooser *dialog;
886 Common::String desc;
887 int slot;
888
889 if (isSave) {
890 dialog = new GUI::SaveLoadChooser(_("Save game:"), _("Save"), true);
891
892 slot = dialog->runModalWithCurrentTarget();
893 desc = dialog->getResultString();
894
895 if (desc.empty())
896 desc = dialog->createDefaultSaveDescription(slot);
897
898 if (desc.size() > 29)
899 desc = Common::String(desc.c_str(), 29);
900
901 saveDesc = desc;
902 } else {
903 dialog = new GUI::SaveLoadChooser(_("Restore game:"), _("Restore"), false);
904 slot = dialog->runModalWithCurrentTarget();
905 }
906
907 delete dialog;
908
909 return slot;
910 }
911
GameStateMenu(NeverhoodEngine * vm,Module * parentModule,SavegameList * savegameList,const uint32 * buttonFileHashes,const NRect * buttonCollisionBounds,uint32 backgroundFileHash,uint32 fontFileHash,uint32 mouseFileHash,const NRect * mouseRect,uint32 listBoxBackgroundFileHash,int16 listBoxX,int16 listBoxY,const NRect & listBoxRect,uint32 textEditBackgroundFileHash,uint32 textEditCursorFileHash,int16 textEditX,int16 textEditY,const NRect & textEditRect,uint32 textFileHash1,uint32 textFileHash2)912 GameStateMenu::GameStateMenu(NeverhoodEngine *vm, Module *parentModule, SavegameList *savegameList,
913 const uint32 *buttonFileHashes, const NRect *buttonCollisionBounds,
914 uint32 backgroundFileHash, uint32 fontFileHash,
915 uint32 mouseFileHash, const NRect *mouseRect,
916 uint32 listBoxBackgroundFileHash, int16 listBoxX, int16 listBoxY, const NRect &listBoxRect,
917 uint32 textEditBackgroundFileHash, uint32 textEditCursorFileHash, int16 textEditX, int16 textEditY, const NRect &textEditRect,
918 uint32 textFileHash1, uint32 textFileHash2)
919 : Scene(vm, parentModule), _currWidget(NULL), _savegameList(savegameList) {
920
921 bool isSave = (textEditCursorFileHash != 0);
922
923 _fontSurface = new FontSurface(_vm, fontFileHash, 32, 7, 32, 11, 17);
924
925 if (!ConfMan.getBool("originalsaveload")) {
926 Common::String saveDesc;
927 int saveCount = savegameList->size();
928 int slot = scummVMSaveLoadDialog(isSave, saveDesc);
929
930 if (slot >= 0) {
931 if (!isSave) {
932 ((MenuModule*)_parentModule)->setLoadgameSlot(slot);
933 } else {
934 ((MenuModule*)_parentModule)->setSavegameInfo(saveDesc,
935 slot, slot >= saveCount);
936 }
937 leaveScene(0);
938 } else {
939 leaveScene(1);
940 }
941 return;
942 }
943
944 setBackground(backgroundFileHash);
945 setPalette(backgroundFileHash);
946 insertScreenMouse(mouseFileHash, mouseRect);
947 insertStaticSprite(textFileHash1, 200);
948 insertStaticSprite(textFileHash2, 200);
949
950 _listBox = new SavegameListBox(_vm, listBoxX, listBoxY, this,
951 _savegameList, _fontSurface, listBoxBackgroundFileHash, listBoxRect);
952 _listBox->initialize();
953
954 _textEditWidget = new TextEditWidget(_vm, textEditX, textEditY, this, 29,
955 _fontSurface, textEditBackgroundFileHash, textEditRect);
956 if (isSave)
957 _textEditWidget->setCursor(textEditCursorFileHash, 2, 13);
958 else
959 _textEditWidget->setReadOnly(true);
960 _textEditWidget->initialize();
961 setCurrWidget(_textEditWidget);
962
963 for (uint buttonIndex = 0; buttonIndex < 6; ++buttonIndex) {
964 Sprite *menuButton = insertSprite<MenuButton>(this, buttonIndex,
965 buttonFileHashes[buttonIndex], buttonCollisionBounds[buttonIndex]);
966 addCollisionSprite(menuButton);
967 }
968
969 SetUpdateHandler(&Scene::update);
970 SetMessageHandler(&GameStateMenu::handleMessage);
971 }
972
~GameStateMenu()973 GameStateMenu::~GameStateMenu() {
974 delete _fontSurface;
975 }
976
getMousePos()977 NPoint GameStateMenu::getMousePos() {
978 NPoint pt;
979 pt.x = _mouseCursor->getX();
980 pt.y = _mouseCursor->getY();
981 return pt;
982 }
983
setCurrWidget(Widget * newWidget)984 void GameStateMenu::setCurrWidget(Widget *newWidget) {
985 if (newWidget && newWidget != _currWidget) {
986 if (_currWidget)
987 _currWidget->exitWidget();
988 newWidget->enterWidget();
989 _currWidget = newWidget;
990 }
991 }
992
refreshDescriptionEdit()993 void GameStateMenu::refreshDescriptionEdit() {
994 uint currIndex = _listBox->getCurrIndex();
995 _textEditWidget->setString((*_savegameList)[currIndex].description);
996 setCurrWidget(_textEditWidget);
997 }
998
performAction()999 void GameStateMenu::performAction() {
1000 // Empty
1001 }
1002
handleMessage(int messageNum,const MessageParam & param,Entity * sender)1003 uint32 GameStateMenu::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) {
1004 Scene::handleMessage(messageNum, param, sender);
1005 switch (messageNum) {
1006 case 0x000A:
1007 if (!_textEditWidget->isReadOnly()) {
1008 sendMessage(_textEditWidget, 0x000A, param.asInteger());
1009 setCurrWidget(_textEditWidget);
1010 }
1011 break;
1012 case 0x000B:
1013 if (param.asInteger() == Common::KEYCODE_RETURN)
1014 performAction();
1015 else if (param.asInteger() == Common::KEYCODE_ESCAPE)
1016 leaveScene(1);
1017 else if (!_textEditWidget->isReadOnly()) {
1018 sendMessage(_textEditWidget, 0x000B, param.asInteger());
1019 setCurrWidget(_textEditWidget);
1020 }
1021 break;
1022 case NM_ANIMATION_UPDATE:
1023 // Handle menu button click
1024 switch (param.asInteger()) {
1025 case 0:
1026 performAction();
1027 break;
1028 case 1:
1029 leaveScene(1);
1030 break;
1031 case 2:
1032 _listBox->pageUp();
1033 break;
1034 case 3:
1035 _listBox->scrollUp();
1036 break;
1037 case 4:
1038 _listBox->scrollDown();
1039 break;
1040 case 5:
1041 _listBox->pageDown();
1042 break;
1043 default:
1044 break;
1045 }
1046 break;
1047 case NM_MOUSE_WHEELUP:
1048 _listBox->scrollUp();
1049 break;
1050 case NM_MOUSE_WHEELDOWN:
1051 _listBox->scrollDown();
1052 break;
1053 default:
1054 break;
1055 }
1056 return 0;
1057 }
1058
1059 static const uint32 kSaveGameMenuButtonFileHashes[] = {
1060 0x8359A824, 0x0690E260, 0x0352B050,
1061 0x1392A223, 0x13802260, 0x0B32B200
1062 };
1063
1064 static const NRect kSaveGameMenuButtonCollisionBounds[] = {
1065 { 518, 106, 602, 160 },
1066 { 516, 378, 596, 434 },
1067 { 394, 108, 458, 206 },
1068 { 400, 204, 458, 276 },
1069 { 398, 292, 456, 352 },
1070 { 396, 352, 460, 444 }
1071 };
1072
1073 static const NRect kSaveGameMenuListBoxRect = { 0, 0, 320, 272 };
1074 static const NRect kSaveGameMenuTextEditRect = { 0, 0, 377, 17 };
1075 static const NRect kSaveGameMenuMouseRect = { 50, 47, 427, 64 };
1076
SaveGameMenu(NeverhoodEngine * vm,Module * parentModule,SavegameList * savegameList)1077 SaveGameMenu::SaveGameMenu(NeverhoodEngine *vm, Module *parentModule, SavegameList *savegameList)
1078 : GameStateMenu(vm, parentModule, savegameList, kSaveGameMenuButtonFileHashes, kSaveGameMenuButtonCollisionBounds,
1079 0x30084E25, 0x2328121A,
1080 0x84E21308, &kSaveGameMenuMouseRect,
1081 0x1115A223, 60, 142, kSaveGameMenuListBoxRect,
1082 0x3510A868, 0x8290AC20, 50, 47, kSaveGameMenuTextEditRect,
1083 0x1340A5C2, 0x1301A7EA) {
1084
1085 }
1086
performAction()1087 void SaveGameMenu::performAction() {
1088 if (!_textEditWidget->getString().empty()) {
1089 ((MenuModule*)_parentModule)->setSavegameInfo(_textEditWidget->getString(),
1090 _listBox->getCurrIndex(), _textEditWidget->isModified());
1091 leaveScene(0);
1092 }
1093 }
1094
1095 static const uint32 kLoadGameMenuButtonFileHashes[] = {
1096 0x100B2091, 0x84822B03, 0x20E22087,
1097 0x04040107, 0x04820122, 0x24423047
1098 };
1099
1100 static const NRect kLoadGameMenuButtonCollisionBounds[] = {
1101 { 44, 115, 108, 147 },
1102 { 52, 396, 112, 426 },
1103 { 188, 116, 245, 196 },
1104 { 189, 209, 239, 269 },
1105 { 187, 301, 233, 349 },
1106 { 182, 358, 241, 433 }
1107 };
1108
1109 static const NRect kLoadGameMenuListBoxRect = { 0, 0, 320, 272 };
1110 static const NRect kLoadGameMenuTextEditRect = { 0, 0, 320, 17 };
1111
1112 #if 0
1113 // Unlike the original game, the text widget in our load dialog is read-only so
1114 // don't change the mouse cursor to indicate that you can type the name of the
1115 // game to load.
1116 //
1117 // Since we allow multiple saved games to have the same name, it's probably
1118 // better this way.
1119 static const NRect kLoadGameMenuMouseRect = { 263, 48, 583, 65 };
1120 #endif
1121
LoadGameMenu(NeverhoodEngine * vm,Module * parentModule,SavegameList * savegameList)1122 LoadGameMenu::LoadGameMenu(NeverhoodEngine *vm, Module *parentModule, SavegameList *savegameList)
1123 : GameStateMenu(vm, parentModule, savegameList, kLoadGameMenuButtonFileHashes, kLoadGameMenuButtonCollisionBounds,
1124 0x98620234, 0x201C2474,
1125 0x2023098E, NULL /* &kLoadGameMenuMouseRect */,
1126 0x04040409, 263, 142, kLoadGameMenuListBoxRect,
1127 0x10924C03, 0, 263, 48, kLoadGameMenuTextEditRect,
1128 0x0BC600A3, 0x0F960021) {
1129
1130 }
1131
performAction()1132 void LoadGameMenu::performAction() {
1133 // TODO: The original would display a message here if nothing was selected.
1134 if (!_textEditWidget->getString().empty()) {
1135 ((MenuModule*)_parentModule)->setLoadgameInfo(_listBox->getCurrIndex());
1136 leaveScene(0);
1137 }
1138 }
1139
1140 static const uint32 kDeleteGameMenuButtonFileHashes[] = {
1141 0x8198E268, 0xDD0C4620, 0x81296520,
1142 0x8D284211, 0x8C004621, 0x07294020
1143 };
1144
1145 static const NRect kDeleteGameMenuButtonCollisionBounds[] = {
1146 { 518, 46, 595, 91 },
1147 { 524, 322, 599, 369 },
1148 { 395, 40, 462, 127 },
1149 { 405, 126, 460, 185 },
1150 { 397, 205, 456, 273 },
1151 { 395, 278, 452, 372 }
1152 };
1153
1154 static const NRect kDeleteGameMenuListBoxRect = { 0, 0, 320, 272 };
1155 static const NRect kDeleteGameMenuTextEditRect = { 0, 0, 320, 17 };
1156
DeleteGameMenu(NeverhoodEngine * vm,Module * parentModule,SavegameList * savegameList)1157 DeleteGameMenu::DeleteGameMenu(NeverhoodEngine *vm, Module *parentModule, SavegameList *savegameList)
1158 : GameStateMenu(vm, parentModule, savegameList, kDeleteGameMenuButtonFileHashes, kDeleteGameMenuButtonCollisionBounds,
1159 0x4080E01C, 0x728523ED,
1160 0x0E018400, NULL,
1161 0xA5584211, 61, 64, kDeleteGameMenuListBoxRect,
1162 0x250A3060, 0, 49, 414, kDeleteGameMenuTextEditRect,
1163 0x80083C01, 0x84181E81) {
1164
1165 }
1166
performAction()1167 void DeleteGameMenu::performAction() {
1168 // TODO: The original would display a message here if no game was selected.
1169 if (!_textEditWidget->getString().empty()) {
1170 ((MenuModule*)_parentModule)->setDeletegameInfo(_listBox->getCurrIndex());
1171 leaveScene(0);
1172 }
1173 }
1174
QueryOverwriteMenu(NeverhoodEngine * vm,Module * parentModule,const Common::String & description)1175 QueryOverwriteMenu::QueryOverwriteMenu(NeverhoodEngine *vm, Module *parentModule, const Common::String &description)
1176 : Scene(vm, parentModule) {
1177
1178 static const uint32 kQueryOverwriteMenuButtonFileHashes[] = {
1179 0x90312400,
1180 0x94C22A22
1181 };
1182
1183 static const NRect kQueryOverwriteMenuCollisionBounds[] = {
1184 { 145, 334, 260, 385 },
1185 { 365, 340, 477, 388 }
1186 };
1187
1188 setBackground(0x043692C4);
1189 setPalette(0x043692C4);
1190 insertScreenMouse(0x692C004B);
1191 insertStaticSprite(0x08C0AC24, 200);
1192
1193 for (uint buttonIndex = 0; buttonIndex < 2; ++buttonIndex) {
1194 Sprite *menuButton = insertSprite<MenuButton>(this, buttonIndex,
1195 kQueryOverwriteMenuButtonFileHashes[buttonIndex], kQueryOverwriteMenuCollisionBounds[buttonIndex]);
1196 addCollisionSprite(menuButton);
1197 }
1198
1199 // Draw the query text to the background, each text line is centered
1200 // NOTE The original had this text in its own class
1201 FontSurface *fontSurface = new FontSurface(_vm, 0x94188D4D, 32, 7, 32, 11, 17);
1202 Common::StringArray textLines;
1203 textLines.push_back(description);
1204 textLines.push_back("Game exists.");
1205 textLines.push_back("Overwrite it?");
1206 for (uint i = 0; i < textLines.size(); ++i)
1207 fontSurface->drawString(_background->getSurface(), 106 + (423 - textLines[i].size() * 11) / 2,
1208 127 + 31 + i * 17, (const byte*)textLines[i].c_str());
1209 delete fontSurface;
1210
1211 SetUpdateHandler(&Scene::update);
1212 SetMessageHandler(&QueryOverwriteMenu::handleMessage);
1213 }
1214
handleMessage(int messageNum,const MessageParam & param,Entity * sender)1215 uint32 QueryOverwriteMenu::handleMessage(int messageNum, const MessageParam ¶m, Entity *sender) {
1216 Scene::handleMessage(messageNum, param, sender);
1217 switch (messageNum) {
1218 case NM_ANIMATION_UPDATE:
1219 // Handle menu button click
1220 leaveScene(param.asInteger());
1221 break;
1222 default:
1223 break;
1224 }
1225 return 0;
1226 }
1227
1228 } // End of namespace Neverhood
1229