NEVERHOOD: More work on the menu system:

- Add handling of ascii and system key input
- Fix TextSurface::drawChar()
- Pause audio when entering the menu, resume it again afterwards
- Add TextEditWidget
- Add SaveGameMenu (doesn't actually save anything yet and needs more work)
This commit is contained in:
johndoe123 2012-10-27 22:18:54 +00:00 committed by Willem Jan Palenstijn
parent 0022c57858
commit 6be5c11713
8 changed files with 525 additions and 182 deletions

View File

@ -123,13 +123,6 @@ void GameModule::handleMouseUp(int16 x, int16 y) {
} }
} }
void GameModule::handleEscapeKey() {
if (!_prevChildObject /* && _canRequestMainMenu TODO?*/)
_mainMenuRequested = true;
else
sendMessage(_childObject, 0x000C, 0);
}
void GameModule::handleSpaceKey() { void GameModule::handleSpaceKey() {
if (_childObject) { if (_childObject) {
debug(2, "GameModule::handleSpaceKey()"); debug(2, "GameModule::handleSpaceKey()");
@ -137,6 +130,31 @@ void GameModule::handleSpaceKey() {
} }
} }
void GameModule::handleAsciiKey(char key) {
if (_childObject) {
debug(2, "GameModule::handleAsciiKey()");
sendMessage(_childObject, 0x000A, (uint32)key);
}
}
void GameModule::handleKeyDown(Common::KeyCode keyCode) {
if (_childObject) {
if (keyCode == Common::KEYCODE_ESCAPE)
handleEscapeKey();
else if (keyCode == Common::KEYCODE_SPACE)
handleSpaceKey();
debug(2, "GameModule::handleKeyDown()");
sendMessage(_childObject, 0x000B, keyCode);
}
}
void GameModule::handleEscapeKey() {
if (!_prevChildObject /* && _canRequestMainMenu TODO?*/)
_mainMenuRequested = true;
else if (_childObject)
sendMessage(_childObject, 0x000C, 0);
}
void GameModule::initKeySlotsPuzzle() { void GameModule::initKeySlotsPuzzle() {
if (!getSubVar(VA_IS_PUZZLE_INIT, 0x25400B10)) { if (!getSubVar(VA_IS_PUZZLE_INIT, 0x25400B10)) {
NonRepeatingRandomNumbers keySlots(_vm->_rnd, 16); NonRepeatingRandomNumbers keySlots(_vm->_rnd, 16);

View File

@ -37,8 +37,10 @@ public:
void handleMouseMove(int16 x, int16 y); void handleMouseMove(int16 x, int16 y);
void handleMouseDown(int16 x, int16 y); void handleMouseDown(int16 x, int16 y);
void handleMouseUp(int16 x, int16 y); void handleMouseUp(int16 x, int16 y);
void handleEscapeKey();
void handleSpaceKey(); void handleSpaceKey();
void handleAsciiKey(char key);
void handleKeyDown(Common::KeyCode keyCode);
void handleEscapeKey();
void initKeySlotsPuzzle(); void initKeySlotsPuzzle();
void initMemoryPuzzle(); void initMemoryPuzzle();
void initWaterPipesPuzzle(); void initWaterPipesPuzzle();

View File

@ -180,7 +180,7 @@ TextSurface::TextSurface(NeverhoodEngine *vm, uint32 fileHash, uint16 numRows, u
: BaseSurface(vm, 0, charWidth * charCount, charHeight * numRows), : BaseSurface(vm, 0, charWidth * charCount, charHeight * numRows),
_numRows(numRows), _firstChar(firstChar), _charWidth(charWidth), _charHeight(charHeight), _numRows(numRows), _firstChar(firstChar), _charWidth(charWidth), _charHeight(charHeight),
_fileHash(fileHash), _charCount(charCount) { _fileHash(fileHash), _charCount(charCount) {
SpriteResource spriteResource(_vm); SpriteResource spriteResource(_vm);
spriteResource.load2(_fileHash); spriteResource.load2(_fileHash);
drawSpriteResourceEx(spriteResource, false, false, 0, 0); drawSpriteResourceEx(spriteResource, false, false, 0, 0);
@ -189,8 +189,8 @@ TextSurface::TextSurface(NeverhoodEngine *vm, uint32 fileHash, uint16 numRows, u
void TextSurface::drawChar(BaseSurface *destSurface, int16 x, int16 y, byte chr) { void TextSurface::drawChar(BaseSurface *destSurface, int16 x, int16 y, byte chr) {
NDrawRect sourceRect; NDrawRect sourceRect;
chr -= _firstChar; chr -= _firstChar;
sourceRect.x = (chr % 16) * _charWidth; sourceRect.x = (chr % _charCount) * _charWidth;
sourceRect.y = (chr / 16) * _charHeight; sourceRect.y = (chr / _charCount) * _charHeight;
sourceRect.width = _charWidth; sourceRect.width = _charWidth;
sourceRect.height = _charHeight; sourceRect.height = _charHeight;
destSurface->copyFrom(_surface, x, y, sourceRect, true); destSurface->copyFrom(_surface, x, y, sourceRect, true);

View File

@ -27,7 +27,8 @@ namespace Neverhood {
enum { enum {
MAIN_MENU = 0, MAIN_MENU = 0,
CREDITS_SCENE = 1, CREDITS_SCENE = 1,
MAKING_OF = 2 MAKING_OF = 2,
SAVE_GAME_MENU = 3
}; };
static const uint32 kMakingOfSmackerFileHashList[] = { static const uint32 kMakingOfSmackerFileHashList[] = {
@ -48,22 +49,18 @@ static const uint32 kMakingOfSmackerFileHashList[] = {
}; };
MenuModule::MenuModule(NeverhoodEngine *vm, Module *parentModule, int which) MenuModule::MenuModule(NeverhoodEngine *vm, Module *parentModule, int which)
: Module(vm, parentModule) { : Module(vm, parentModule), _savegameList(NULL) {
SetMessageHandler(&MenuModule::handleMessage); SetMessageHandler(&MenuModule::handleMessage);
// TODO Check if the background actually needs to be saved
_savedBackground = new Background(_vm, 0);
_savedBackground->createSurface(0, 640, 480);
// TODO Save current palette
// TODO Stop all sounds and music
_savedPaletteData = _vm->_screen->getPaletteData(); _savedPaletteData = _vm->_screen->getPaletteData();
_vm->_mixer->pauseAll(true);
createScene(MAIN_MENU, -1); createScene(MAIN_MENU, -1);
} }
MenuModule::~MenuModule() { MenuModule::~MenuModule() {
_vm->_mixer->pauseAll(false);
_vm->_screen->setPaletteData(_savedPaletteData); _vm->_screen->setPaletteData(_savedPaletteData);
} }
@ -79,6 +76,9 @@ void MenuModule::createScene(int sceneNum, int which) {
case MAKING_OF: case MAKING_OF:
createSmackerScene(kMakingOfSmackerFileHashList, false, true, true); createSmackerScene(kMakingOfSmackerFileHashList, false, true, true);
break; break;
case SAVE_GAME_MENU:
createSaveGameMenu();
break;
} }
SetUpdateHandler(&MenuModule::updateScene); SetUpdateHandler(&MenuModule::updateScene);
_childObject->handleUpdate(); _childObject->handleUpdate();
@ -100,7 +100,7 @@ void MenuModule::updateScene() {
break; break;
case 2: case 2:
debug("SAVE GAME"); debug("SAVE GAME");
// TODO createSaveGameMenu(); createScene(SAVE_GAME_MENU, -1);
break; break;
case 3: case 3:
debug("RESUME GAME"); debug("RESUME GAME");
@ -137,6 +137,9 @@ void MenuModule::updateScene() {
case MAKING_OF: case MAKING_OF:
createScene(MAIN_MENU, -1); createScene(MAIN_MENU, -1);
break; break;
case SAVE_GAME_MENU:
handleSaveGameMenuAction(_moduleResult);
break;
default: default:
break; break;
} }
@ -148,60 +151,38 @@ uint32 MenuModule::handleMessage(int messageNum, const MessageParam &param, Enti
return Module::handleMessage(messageNum, param, sender);; return Module::handleMessage(messageNum, param, sender);;
} }
static const uint32 kMainMenuButtonFileHashes[] = { void MenuModule::createSaveGameMenu() {
0x36C62120, // TODO Load actual savegames list :)
0x56C62120, _savegameList = new StringArray();
0x96C62120, _savegameList->push_back(Common::String("Annoying scene"));
0x16C62121, _savegameList->push_back(Common::String("Stuff happens"));
0x16C62122, for (uint i = 0; i < 33; ++i)
0x16C62124, _savegameList->push_back(Common::String::format("Game %d", i));
0x16C62128, _childObject = new SaveGameMenu(_vm, this, _savegameList);
0x16C62130,
0x16C62100
};
MainMenuButton::MainMenuButton(NeverhoodEngine *vm, Scene *parentScene, uint buttonIndex)
: StaticSprite(vm, 900), _parentScene(parentScene), _buttonIndex(buttonIndex), _countdown(0) {
loadSprite(kMainMenuButtonFileHashes[_buttonIndex], kSLFDefDrawOffset | kSLFDefPosition, 100);
// TODO Move to const array
switch (_buttonIndex) {
case 0:
_collisionBounds.set(52, 121, 110, 156);
break;
case 1:
_collisionBounds.set(52, 192, 109, 222);
break;
case 2:
_collisionBounds.set(60, 257, 119, 286);
break;
case 3:
_collisionBounds.set(67, 326, 120, 354);
break;
case 4:
_collisionBounds.set(70, 389, 128, 416);
break;
case 5:
_collisionBounds.set(523, 113, 580, 144);
break;
case 6:
_collisionBounds.set(525, 176, 577, 206);
break;
case 7:
_collisionBounds.set(527, 384, 580, 412);
break;
case 8:
_collisionBounds.set(522, 255, 580, 289);
break;
}
setVisible(false);
SetUpdateHandler(&MainMenuButton::update);
SetMessageHandler(&MainMenuButton::handleMessage);
} }
void MainMenuButton::update() { void MenuModule::handleSaveGameMenuAction(int action) {
if (action != 0) {
createScene(MAIN_MENU, -1);
} else {
// TODO Actual saving later 0048A62E
createScene(MAIN_MENU, -1);
}
delete _savegameList;
_savegameList = NULL;
}
MenuButton::MenuButton(NeverhoodEngine *vm, Scene *parentScene, uint buttonIndex, uint32 fileHash, const NRect &collisionBounds)
: StaticSprite(vm, 900), _parentScene(parentScene), _buttonIndex(buttonIndex), _countdown(0) {
loadSprite(fileHash, kSLFDefDrawOffset | kSLFDefPosition, 100);
_collisionBounds = collisionBounds;
setVisible(false);
SetUpdateHandler(&MenuButton::update);
SetMessageHandler(&MenuButton::handleMessage);
}
void MenuButton::update() {
updatePosition(); updatePosition();
if (_countdown != 0 && (--_countdown) == 0) { if (_countdown != 0 && (--_countdown) == 0) {
setVisible(false); setVisible(false);
@ -209,7 +190,7 @@ void MainMenuButton::update() {
} }
} }
uint32 MainMenuButton::handleMessage(int messageNum, const MessageParam &param, Entity *sender) { uint32 MenuButton::handleMessage(int messageNum, const MessageParam &param, Entity *sender) {
uint32 messageResult = Sprite::handleMessage(messageNum, param, sender); uint32 messageResult = Sprite::handleMessage(messageNum, param, sender);
switch (messageNum) { switch (messageNum) {
case 0x1011: case 0x1011:
@ -226,6 +207,30 @@ uint32 MainMenuButton::handleMessage(int messageNum, const MessageParam &param,
MainMenu::MainMenu(NeverhoodEngine *vm, Module *parentModule) MainMenu::MainMenu(NeverhoodEngine *vm, Module *parentModule)
: Scene(vm, parentModule, true) { : Scene(vm, parentModule, true) {
static const uint32 kMenuButtonFileHashes[] = {
0x36C62120,
0x56C62120,
0x96C62120,
0x16C62121,
0x16C62122,
0x16C62124,
0x16C62128,
0x16C62130,
0x16C62100
};
static const NRect kMenuButtonCollisionBounds[] = {
NRect(52, 121, 110, 156),
NRect(52, 192, 109, 222),
NRect(60, 257, 119, 286),
NRect(67, 326, 120, 354),
NRect(70, 389, 128, 416),
NRect(523, 113, 580, 144),
NRect(525, 176, 577, 206),
NRect(527, 384, 580, 412),
NRect(522, 255, 580, 289)
};
setBackground(0x08C0020C); setBackground(0x08C0020C);
setPalette(0x08C0020C); setPalette(0x08C0020C);
insertMouse433(0x00208084); insertMouse433(0x00208084);
@ -233,12 +238,13 @@ MainMenu::MainMenu(NeverhoodEngine *vm, Module *parentModule)
insertStaticSprite(0x41137051, 100); insertStaticSprite(0x41137051, 100);
insertStaticSprite(0xC10B2015, 100); insertStaticSprite(0xC10B2015, 100);
// TODO Only is music is disabled // TODO Only if music is enabled
_musicOnButton = insertStaticSprite(0x0C24C0EE, 100); _musicOnButton = insertStaticSprite(0x0C24C0EE, 100);
for (uint buttonIndex = 0; buttonIndex < 9; ++buttonIndex) { for (uint buttonIndex = 0; buttonIndex < 9; ++buttonIndex) {
Sprite *mainMenuButton = insertSprite<MainMenuButton>(this, buttonIndex); Sprite *menuButton = insertSprite<MenuButton>(this, buttonIndex,
_vm->_collisionMan->addSprite(mainMenuButton); kMenuButtonFileHashes[buttonIndex], kMenuButtonCollisionBounds[buttonIndex]);
_vm->_collisionMan->addSprite(menuButton);
} }
SetUpdateHandler(&Scene::update); SetUpdateHandler(&Scene::update);
@ -341,7 +347,7 @@ uint32 CreditsScene::handleMessage(int messageNum, const MessageParam &param, En
leaveScene(0); leaveScene(0);
break; break;
case 0x000B://TODO Implement this message case 0x000B://TODO Implement this message
if (param.asInteger() == 27 && _canAbort) if (param.asInteger() == Common::KEYCODE_ESCAPE && _canAbort)
leaveScene(0); leaveScene(0);
break; break;
case 0x101D: case 0x101D:
@ -358,9 +364,11 @@ WidgetScene::WidgetScene(NeverhoodEngine *vm, Module *parentModule)
: Scene(vm, parentModule, true), _currWidget(NULL) { : Scene(vm, parentModule, true), _currWidget(NULL) {
} }
void WidgetScene::getMousePos(NPoint &pt) { NPoint WidgetScene::getMousePos() {
NPoint pt;
pt.x = _mouseCursor->getX(); pt.x = _mouseCursor->getX();
pt.y = _mouseCursor->getY(); pt.y = _mouseCursor->getY();
return pt;
} }
void WidgetScene::setCurrWidget(Widget *newWidget) { void WidgetScene::setCurrWidget(Widget *newWidget) {
@ -372,10 +380,13 @@ void WidgetScene::setCurrWidget(Widget *newWidget) {
} }
} }
void WidgetScene::handleEvent(int16 itemID, int eventType) {
}
Widget::Widget(NeverhoodEngine *vm, int16 x, int16 y, int16 itemID, WidgetScene *parentScene, Widget::Widget(NeverhoodEngine *vm, int16 x, int16 y, int16 itemID, WidgetScene *parentScene,
int baseObjectPriority, int baseSurfacePriority, bool visible) int baseObjectPriority, int baseSurfacePriority)
: StaticSprite(vm, baseObjectPriority), _itemID(itemID), _parentScene(parentScene), : StaticSprite(vm, baseObjectPriority), _itemID(itemID), _parentScene(parentScene),
_baseObjectPriority(baseObjectPriority), _baseSurfacePriority(baseSurfacePriority), _visible(visible) { _baseObjectPriority(baseObjectPriority), _baseSurfacePriority(baseSurfacePriority) {
SetUpdateHandler(&Widget::update); SetUpdateHandler(&Widget::update);
SetMessageHandler(&Widget::handleMessage); SetMessageHandler(&Widget::handleMessage);
@ -383,18 +394,6 @@ Widget::Widget(NeverhoodEngine *vm, int16 x, int16 y, int16 itemID, WidgetScene
setPosition(x, y); setPosition(x, y);
} }
void Widget::show() {
if (_surface)
_surface->setVisible(true);
_visible = true;
}
void Widget::hide() {
if (_surface)
_surface->setVisible(false);
_visible = false;
}
void Widget::onClick() { void Widget::onClick() {
_parentScene->setCurrWidget(this); _parentScene->setCurrWidget(this);
// TODO _parentScene->onClick(_itemID, 0); // TODO _parentScene->onClick(_itemID, 0);
@ -451,9 +450,9 @@ uint32 Widget::handleMessage(int messageNum, const MessageParam &param, Entity *
} }
TextLabelWidget::TextLabelWidget(NeverhoodEngine *vm, int16 x, int16 y, int16 itemID, WidgetScene *parentScene, TextLabelWidget::TextLabelWidget(NeverhoodEngine *vm, int16 x, int16 y, int16 itemID, WidgetScene *parentScene,
int baseObjectPriority, int baseSurfacePriority, bool visible, int baseObjectPriority, int baseSurfacePriority,
const byte *string, int stringLen, BaseSurface *drawSurface, int16 tx, int16 ty, TextSurface *textSurface) const byte *string, int stringLen, BaseSurface *drawSurface, int16 tx, int16 ty, TextSurface *textSurface)
: Widget(vm, x, y, itemID, parentScene, baseObjectPriority, baseSurfacePriority, visible), : Widget(vm, x, y, itemID, parentScene, baseObjectPriority, baseSurfacePriority),
_string(string), _stringLen(stringLen), _drawSurface(drawSurface), _tx(tx), _ty(ty), _textSurface(textSurface) { _string(string), _stringLen(stringLen), _drawSurface(drawSurface), _tx(tx), _ty(ty), _textSurface(textSurface) {
} }
@ -473,13 +472,11 @@ int16 TextLabelWidget::getHeight() {
void TextLabelWidget::drawString(int maxStringLength) { void TextLabelWidget::drawString(int maxStringLength) {
_textSurface->drawString(_drawSurface, _x, _y, _string, MIN(_stringLen, maxStringLength)); _textSurface->drawString(_drawSurface, _x, _y, _string, MIN(_stringLen, maxStringLength));
_visible = true;
_collisionBoundsOffset.set(_tx, _ty, getWidth(), getHeight()); _collisionBoundsOffset.set(_tx, _ty, getWidth(), getHeight());
updateBounds(); updateBounds();
} }
void TextLabelWidget::clear() { void TextLabelWidget::clear() {
_visible = false;
_collisionBoundsOffset.set(0, 0, 0, 0); _collisionBoundsOffset.set(0, 0, 0, 0);
updateBounds(); updateBounds();
} }
@ -498,47 +495,215 @@ void TextLabelWidget::setTY(int16 ty) {
_ty = ty; _ty = ty;
} }
SavegameListBox::SavegameListBox(NeverhoodEngine *vm, int16 x, int16 y, int16 itemID, WidgetScene *parentScene, TextEditWidget::TextEditWidget(NeverhoodEngine *vm, int16 x, int16 y, int16 itemID, WidgetScene *parentScene,
int baseObjectPriority, int baseSurfacePriority, bool visible, int baseObjectPriority, int baseSurfacePriority,
StringArray *savegameList, TextSurface *textSurface1, TextSurface *textSurface2, uint32 fileHash1, NRect &rect) const byte *string, int maxStringLength, TextSurface *textSurface, uint32 fileHash, const NRect &rect)
: Widget(vm, x, y, itemID, parentScene, baseObjectPriority, baseSurfacePriority, visible), : Widget(vm, x, y, itemID, parentScene, baseObjectPriority, baseSurfacePriority),
_savegameList(savegameList), _textSurface1(textSurface1), _textSurface2(textSurface2), _fileHash1(fileHash1), _rect(rect), _maxStringLength(maxStringLength), _textSurface(textSurface), _fileHash(fileHash), _rect(rect),
_maxStringLength(0), _topIndex(0), _visibleItemsCount(0), _currIndex(0) { _cursorSurface(NULL), _cursorTicks(0), _cursorPos(0), _cursorFileHash(0), _cursorWidth(0), _cursorHeight(0) {
_maxVisibleItemsCount = (_rect.y2 - _rect.y1) / _textSurface1->getCharHeight(); _entryString = (const char*)string;
_maxStringLength = (_rect.x2 - _rect.x1) / _textSurface1->getCharWidth(); _maxVisibleChars = (_rect.x2 - _rect.x1) / _textSurface->getCharWidth();
_cursorPos = _entryString.size();
SetUpdateHandler(&TextEditWidget::update);
SetMessageHandler(&TextEditWidget::handleMessage);
}
TextEditWidget::~TextEditWidget() {
delete _cursorSurface;
}
void TextEditWidget::onClick() {
NPoint mousePos = _parentScene->getMousePos();
mousePos.x -= _x + _rect.x1;
mousePos.y -= _y + _rect.y1;
if (mousePos.x >= 0 && mousePos.x <= _rect.x2 - _rect.x1 &&
mousePos.y >= 0 && mousePos.y <= _rect.y2 - _rect.y1) {
if (_entryString.size() == 1)
_cursorPos = 0;
else {
int newCursorPos = mousePos.x / _textSurface->getCharWidth();
if (mousePos.x % _textSurface->getCharWidth() > _textSurface->getCharWidth() / 2 && newCursorPos <= (int)_entryString.size())//###
++newCursorPos;
_cursorPos = MIN((int)_entryString.size(), newCursorPos);
}
_cursorSurface->setVisible(true);
refresh();
}
Widget::onClick();
}
void TextEditWidget::addSprite() {
SpriteResource cursorSpriteResource(_vm);
_spriteResource.load2(_fileHash);
createSurface(_baseSurfacePriority, _spriteResource.getDimensions().width, _spriteResource.getDimensions().height);
refreshPosition();
_parentScene->addSprite(this);
_vm->_collisionMan->addSprite(this);
_surface->setVisible(true);
_textLabelWidget = new TextLabelWidget(_vm, _rect.x1, _rect.y1 + (_rect.y2 - _rect.y1 + 1 - _textSurface->getCharHeight()) / 2,
0, _parentScene, _baseObjectPriority + 1, _baseSurfacePriority + 1,
(const byte*)_entryString.c_str(), _entryString.size(), _surface, _x, _y, _textSurface);
_textLabelWidget->addSprite();
cursorSpriteResource.load2(_cursorFileHash);
_cursorSurface = new BaseSurface(_vm, 0, cursorSpriteResource.getDimensions().width, cursorSpriteResource.getDimensions().height);
_cursorSurface->drawSpriteResourceEx(cursorSpriteResource, false, false, cursorSpriteResource.getDimensions().width, cursorSpriteResource.getDimensions().height);
_cursorSurface->setVisible(true);
refresh();
}
void TextEditWidget::enterWidget() {
_cursorSurface->setVisible(true);
refresh();
}
void TextEditWidget::exitWidget() {
_cursorSurface->setVisible(false);
refresh();
}
void TextEditWidget::setCursor(uint32 cursorFileHash, int16 cursorWidth, int16 cursorHeight) {
_cursorFileHash = cursorFileHash;
_cursorWidth = cursorWidth;
_cursorHeight = cursorHeight;
}
void TextEditWidget::drawCursor() {
if (_cursorSurface->getVisible() && _cursorPos >= 0 && _cursorPos <= _maxVisibleChars) {
NDrawRect sourceRect(0, 0, _cursorWidth, _cursorHeight);
_surface->copyFrom(_cursorSurface->getSurface(), _rect.x1 + _cursorPos * _textSurface->getCharWidth(),
_rect.y1 + (_rect.y2 - _cursorHeight - _rect.y1 + 1) / 2, sourceRect, true);
} else
_cursorSurface->setVisible(false);
}
void TextEditWidget::updateString() {
_textLabelWidget->setString((const byte *)_entryString.c_str(), _entryString.size());
_textLabelWidget->drawString(_maxVisibleChars);
}
void TextEditWidget::getString(Common::String &string) {
string = _entryString;
}
void TextEditWidget::setString(const Common::String &string) {
_entryString = string;
_cursorPos = _entryString.size();
refresh();
}
void TextEditWidget::handleAsciiKey(char ch) {
if ((int)_entryString.size() < _maxStringLength &&
((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') || ch == ' ')) {
_entryString.insertChar(ch, _cursorPos);
++_cursorPos;
refresh();
}
}
void TextEditWidget::handleKeyDown(Common::KeyCode keyCode) {
bool doRefresh = true;
switch (keyCode) {
case Common::KEYCODE_DELETE:
if (_entryString.size() > 0 && _cursorPos < (int)_entryString.size())
_entryString.deleteChar(_cursorPos);
break;
case Common::KEYCODE_HOME:
_cursorPos = 0;
break;
case Common::KEYCODE_END:
_cursorPos = _entryString.size();
break;
case Common::KEYCODE_LEFT:
if (_entryString.size() > 0 && _cursorPos > 0)
--_cursorPos;
break;
case Common::KEYCODE_RIGHT:
if (_cursorPos < (int)_entryString.size())
++_cursorPos;
break;
case Common::KEYCODE_BACKSPACE:
if (_entryString.size() > 0 && _cursorPos > 0)
_entryString.deleteChar(--_cursorPos);
break;
default:
break;
}
if (doRefresh) {
_cursorSurface->setVisible(true);
_cursorTicks = 0;
refresh();
}
}
void TextEditWidget::refresh() {
refreshPosition();
updateString();
drawCursor();
}
void TextEditWidget::update() {
Widget::update();
if (_parentScene->getCurrWidget() == this && _cursorTicks++ == 10) {
_cursorSurface->setVisible(!_cursorSurface->getVisible());
refresh();
_cursorTicks = 0;
}
}
uint32 TextEditWidget::handleMessage(int messageNum, const MessageParam &param, Entity *sender) {
uint32 messageResult = Widget::handleMessage(messageNum, param, sender);
switch (messageNum) {
case 0x000A:
handleAsciiKey(param.asInteger());
break;
case 0x000B:
handleKeyDown((Common::KeyCode)param.asInteger());
break;
}
return messageResult;
}
SavegameListBox::SavegameListBox(NeverhoodEngine *vm, int16 x, int16 y, int16 itemID, WidgetScene *parentScene,
int baseObjectPriority, int baseSurfacePriority,
StringArray *savegameList, TextSurface *textSurface, uint32 bgFileHash, const NRect &rect)
: Widget(vm, x, y, itemID, parentScene, baseObjectPriority, baseSurfacePriority),
_savegameList(savegameList), _textSurface(textSurface), _bgFileHash(bgFileHash), _rect(rect),
_maxStringLength(0), _firstVisibleItem(0), _lastVisibleItem(0), _currIndex(0) {
_maxVisibleItemsCount = (_rect.y2 - _rect.y1) / _textSurface->getCharHeight();
_maxStringLength = (_rect.x2 - _rect.x1) / _textSurface->getCharWidth();
} }
void SavegameListBox::onClick() { void SavegameListBox::onClick() {
NPoint mousePos; NPoint mousePos = _parentScene->getMousePos();
int16 w = _rect.x2 - _rect.x1, h = _rect.y2 - _rect.y1;
_parentScene->getMousePos(mousePos);
mousePos.x -= _x + _rect.x1; mousePos.x -= _x + _rect.x1;
mousePos.y -= _y + _rect.y1; mousePos.y -= _y + _rect.y1;
if (mousePos.x >= 0 && mousePos.x <= w && mousePos.y >= 0 && mousePos.y <= h) { if (mousePos.x >= 0 && mousePos.x <= _rect.x2 - _rect.x1 &&
int newIndex = _topIndex + mousePos.y / _textSurface1->getCharHeight(); mousePos.y >= 0 && mousePos.y <= _rect.y2 - _rect.y1) {
if (newIndex <= _visibleItemsCount) { int newIndex = _firstVisibleItem + mousePos.y / _textSurface->getCharHeight();
if (newIndex <= _lastVisibleItem) {
_currIndex = newIndex; _currIndex = newIndex;
refresh(); refresh();
_parentScene->setCurrWidget(this); _parentScene->setCurrWidget(this);
// TODO _parentScene->onClick(_itemID, 5); debug("_currIndex = %d", _currIndex);
_parentScene->handleEvent(_itemID, 5);
} }
} }
} }
void SavegameListBox::addSprite() { void SavegameListBox::addSprite() {
_spriteResource.load2(_fileHash1); _spriteResource.load2(_bgFileHash);
createSurface(_baseSurfacePriority, _spriteResource.getDimensions().width, _spriteResource.getDimensions().height); createSurface(_baseSurfacePriority, _spriteResource.getDimensions().width, _spriteResource.getDimensions().height);
refreshPosition(); refreshPosition();
_parentScene->addSprite(this); _parentScene->addSprite(this);
_vm->_collisionMan->addSprite(this); _vm->_collisionMan->addSprite(this);
if (_visible) _surface->setVisible(true);
show();
else
hide();
buildItems(); buildItems();
_topIndex = 0; _firstVisibleItem = 0;
_visibleItemsCount = MIN(_maxVisibleItemsCount, (int)_textLabelItems.size()); _lastVisibleItem = MIN(_maxVisibleItemsCount, (int)_textLabelItems.size());
refresh(); refresh();
} }
@ -549,7 +714,7 @@ void SavegameListBox::buildItems() {
const byte *string = (const byte*)savegameList[i].c_str(); const byte *string = (const byte*)savegameList[i].c_str();
int stringLen = (int)savegameList[i].size(); int stringLen = (int)savegameList[i].size();
TextLabelWidget *label = new TextLabelWidget(_vm, itemX, itemY, i, _parentScene, _baseObjectPriority + 1, TextLabelWidget *label = new TextLabelWidget(_vm, itemX, itemY, i, _parentScene, _baseObjectPriority + 1,
_baseSurfacePriority + 1, _visible, string, MIN(stringLen, _maxStringLength), _surface, _x, _y, _textSurface1); _baseSurfacePriority + 1, string, MIN(stringLen, _maxStringLength), _surface, _x, _y, _textSurface);
label->addSprite(); label->addSprite();
_textLabelItems.push_back(label); _textLabelItems.push_back(label);
} }
@ -558,13 +723,12 @@ void SavegameListBox::buildItems() {
void SavegameListBox::drawItems() { void SavegameListBox::drawItems() {
for (int i = 0; i < (int)_textLabelItems.size(); ++i) { for (int i = 0; i < (int)_textLabelItems.size(); ++i) {
TextLabelWidget *label = _textLabelItems[i]; TextLabelWidget *label = _textLabelItems[i];
if (i >= _topIndex && i <= _visibleItemsCount) { if (i >= _firstVisibleItem && i < _lastVisibleItem) {
label->setY(_rect.y1 + (i - _topIndex) * _textSurface1->getCharHeight()); label->setY(_rect.y1 + (i - _firstVisibleItem) * _textSurface->getCharHeight());
label->updateBounds(); label->updateBounds();
label->drawString(_maxStringLength); label->drawString(_maxStringLength);
} else { } else
label->clear(); label->clear();
}
} }
} }
@ -574,37 +738,153 @@ void SavegameListBox::refresh() {
} }
void SavegameListBox::scrollUp() { void SavegameListBox::scrollUp() {
if (_topIndex > 0) { if (_firstVisibleItem > 0) {
--_topIndex; --_firstVisibleItem;
--_visibleItemsCount; --_lastVisibleItem;
refresh(); refresh();
} }
} }
void SavegameListBox::scrollDown() { void SavegameListBox::scrollDown() {
if (_visibleItemsCount < (int)_textLabelItems.size()) { if (_lastVisibleItem < (int)_textLabelItems.size()) {
++_topIndex; ++_firstVisibleItem;
++_visibleItemsCount; ++_lastVisibleItem;
refresh(); refresh();
} }
} }
void SavegameListBox::pageUp() { void SavegameListBox::pageUp() {
int distance = MIN(_topIndex, _maxVisibleItemsCount); int amount = MIN(_firstVisibleItem, _maxVisibleItemsCount);
if (distance > 0) { if (amount > 0) {
_topIndex -= distance; _firstVisibleItem -= amount;
_visibleItemsCount = distance; _lastVisibleItem -= amount;
refresh(); refresh();
} }
} }
void SavegameListBox::pageDown() { void SavegameListBox::pageDown() {
int distance = MIN((int)_textLabelItems.size() - _visibleItemsCount - 1, _maxVisibleItemsCount); int amount = MIN((int)_textLabelItems.size() - _lastVisibleItem, _maxVisibleItemsCount);
if (distance > 0) { if (amount > 0) {
_topIndex += distance; _firstVisibleItem += amount;
_visibleItemsCount += distance; _lastVisibleItem += amount;
refresh(); refresh();
} }
} }
SaveGameMenu::SaveGameMenu(NeverhoodEngine *vm, Module *parentModule, StringArray *savegameList)
: WidgetScene(vm, parentModule), _savegameList(savegameList) {
static const uint32 kSaveGameMenuButtonFileHashes[] = {
0x8359A824,
0x0690E260,
0x0352B050,
0x1392A223,
0x13802260,
0x0B32B200
};
static const NRect kSaveGameMenuButtonCollisionBounds[] = {
NRect(518, 106, 602, 160),
NRect(516, 378, 596, 434),
NRect(394, 108, 458, 206),
NRect(400, 204, 458, 276),
NRect(398, 292, 456, 352),
NRect(396, 352, 460, 444)
};
static const NRect kListBoxRect(0, 0, 320, 272);
static const NRect kTextEditRect(0, 0, 377, 17);
static const NRect kMouseRect(50, 47, 427, 64);
_textSurface = new TextSurface(_vm, 0x2328121A, 7, 32, 32, 11, 17);
setBackground(0x30084E25);
setPalette(0x30084E25);
insertMouse433(0x84E21308, &kMouseRect);
insertStaticSprite(0x1340A5C2, 200);
insertStaticSprite(0x1301A7EA, 200);
_listBox = new SavegameListBox(_vm, 60, 142, 69/*ItemID*/, this, 1000, 1000,
_savegameList, _textSurface, 0x1115A223, kListBoxRect);
_listBox->addSprite();
_textEditWidget = new TextEditWidget(_vm, 50, 47, 70/*ItemID*/, this, 1000, 1000,
(const byte*)_savegameName.c_str(), 29, _textSurface, 0x3510A868, kTextEditRect);
_textEditWidget->setCursor(0x8290AC20, 2, 13);
_textEditWidget->addSprite();
_textEditWidget->setString(_savegameName);
setCurrWidget(_textEditWidget);
for (uint buttonIndex = 0; buttonIndex < 6; ++buttonIndex) {
Sprite *menuButton = insertSprite<MenuButton>(this, buttonIndex,
kSaveGameMenuButtonFileHashes[buttonIndex], kSaveGameMenuButtonCollisionBounds[buttonIndex]);
_vm->_collisionMan->addSprite(menuButton);
}
SetUpdateHandler(&Scene::update);
SetMessageHandler(&SaveGameMenu::handleMessage);
}
SaveGameMenu::~SaveGameMenu() {
delete _textSurface;
}
void SaveGameMenu::handleEvent(int16 itemID, int eventType) {
if (itemID == 69 && eventType == 5) {
uint currIndex = _listBox->getCurrIndex();
_textEditWidget->setString((*_savegameList)[currIndex]);
setCurrWidget(_textEditWidget);
}
}
uint32 SaveGameMenu::handleMessage(int messageNum, const MessageParam &param, Entity *sender) {
Scene::handleMessage(messageNum, param, sender);
switch (messageNum) {
case 0x000A:
sendMessage(_textEditWidget, 0x000A, param.asInteger());
setCurrWidget(_textEditWidget);
break;
case 0x000B:
if (param.asInteger() == Common::KEYCODE_RETURN) {
// Return
// TODO 00486B05
// Get string from edit field and inform main module
leaveScene(0);
} else if (param.asInteger() == Common::KEYCODE_ESCAPE) {
// Escape
leaveScene(1);
} else {
sendMessage(_textEditWidget, 0x000B, param.asInteger());
setCurrWidget(_textEditWidget);
}
break;
case 0x2000:
// Handle menu button click
switch (param.asInteger()) {
case 0:
// TODO Same handling as Return
leaveScene(0);
break;
case 1:
leaveScene(1);
break;
case 2:
_listBox->pageUp();
break;
case 3:
_listBox->scrollUp();
break;
case 4:
_listBox->scrollDown();
break;
case 5:
_listBox->pageDown();
break;
}
break;
}
return 0;
}
} // End of namespace Neverhood } // End of namespace Neverhood

View File

@ -30,6 +30,8 @@
namespace Neverhood { namespace Neverhood {
typedef Common::Array<Common::String> StringArray;
class MenuModule : public Module { class MenuModule : public Module {
public: public:
MenuModule(NeverhoodEngine *vm, Module *parentModule, int which); MenuModule(NeverhoodEngine *vm, Module *parentModule, int which);
@ -37,17 +39,18 @@ public:
protected: protected:
int _sceneNum; int _sceneNum;
Common::String _savegameName; Common::String _savegameName;
Background *_savedBackground;
byte *_savedPaletteData; byte *_savedPaletteData;
// TODO _savegameList (list of strings?) StringArray *_savegameList;
void createScene(int sceneNum, int which); void createScene(int sceneNum, int which);
void updateScene(); void updateScene();
uint32 handleMessage(int messageNum, const MessageParam &param, Entity *sender); uint32 handleMessage(int messageNum, const MessageParam &param, Entity *sender);
void createSaveGameMenu();
void handleSaveGameMenuAction(int action);
}; };
class MainMenuButton : public StaticSprite { class MenuButton : public StaticSprite {
public: public:
MainMenuButton(NeverhoodEngine *vm, Scene *parentScene, uint buttonIndex); MenuButton(NeverhoodEngine *vm, Scene *parentScene, uint buttonIndex, uint32 fileHash, const NRect &collisionBounds);
protected: protected:
Scene *_parentScene; Scene *_parentScene;
int _countdown; int _countdown;
@ -79,15 +82,15 @@ protected:
uint32 handleMessage(int messageNum, const MessageParam &param, Entity *sender); uint32 handleMessage(int messageNum, const MessageParam &param, Entity *sender);
}; };
typedef Common::Array<Common::String> StringArray;
class Widget; class Widget;
class WidgetScene : public Scene { class WidgetScene : public Scene {
public: public:
WidgetScene(NeverhoodEngine *vm, Module *parentModule); WidgetScene(NeverhoodEngine *vm, Module *parentModule);
void getMousePos(NPoint &pt); NPoint getMousePos();
virtual void setCurrWidget(Widget *newWidget); virtual void setCurrWidget(Widget *newWidget);
virtual Widget *getCurrWidget() { return _currWidget; }
virtual void handleEvent(int16 itemID, int eventType);
protected: protected:
Widget *_currWidget; Widget *_currWidget;
}; };
@ -95,9 +98,7 @@ protected:
class Widget : public StaticSprite { class Widget : public StaticSprite {
public: public:
Widget(NeverhoodEngine *vm, int16 x, int16 y, int16 itemID, WidgetScene *parentScene, Widget(NeverhoodEngine *vm, int16 x, int16 y, int16 itemID, WidgetScene *parentScene,
int baseObjectPriority, int baseSurfacePriority, bool visible); int baseObjectPriority, int baseSurfacePriority);
virtual void show();
virtual void hide();
virtual void onClick(); virtual void onClick();
virtual void setPosition(int16 x, int16 y); virtual void setPosition(int16 x, int16 y);
virtual void refreshPosition(); virtual void refreshPosition();
@ -111,7 +112,6 @@ protected:
WidgetScene *_parentScene; WidgetScene *_parentScene;
int _baseObjectPriority; int _baseObjectPriority;
int _baseSurfacePriority; int _baseSurfacePriority;
bool _visible;
void update(); void update();
uint32 handleMessage(int messageNum, const MessageParam &param, Entity *sender); uint32 handleMessage(int messageNum, const MessageParam &param, Entity *sender);
}; };
@ -119,7 +119,7 @@ protected:
class TextLabelWidget : public Widget { class TextLabelWidget : public Widget {
public: public:
TextLabelWidget(NeverhoodEngine *vm, int16 x, int16 y, int16 itemID, WidgetScene *parentScene, TextLabelWidget(NeverhoodEngine *vm, int16 x, int16 y, int16 itemID, WidgetScene *parentScene,
int baseObjectPriority, int baseSurfacePriority, bool visible, int baseObjectPriority, int baseSurfacePriority,
const byte *string, int stringLen, BaseSurface *drawSurface, int16 tx, int16 ty, TextSurface *textSurface); const byte *string, int stringLen, BaseSurface *drawSurface, int16 tx, int16 ty, TextSurface *textSurface);
virtual void onClick(); virtual void onClick();
virtual void addSprite(); virtual void addSprite();
@ -138,11 +138,46 @@ protected:
int _stringLen; int _stringLen;
}; };
class TextEditWidget : public Widget {
public:
TextEditWidget(NeverhoodEngine *vm, int16 x, int16 y, int16 itemID, WidgetScene *parentScene,
int baseObjectPriority, int baseSurfacePriority,
const byte *string, int maxStringLength, TextSurface *textSurface, uint32 fileHash, const NRect &rect);
~TextEditWidget();
virtual void onClick();
virtual void addSprite();
virtual void enterWidget();
virtual void exitWidget();
void setCursor(uint32 cursorFileHash, int16 cursorWidth, int16 cursorHeight);
void drawCursor();
void updateString();
void getString(Common::String &string);
void setString(const Common::String &string);
void handleAsciiKey(char ch);
void handleKeyDown(Common::KeyCode keyCode);
void refresh();
protected:
NRect _rect;
uint32 _fileHash;
int _maxVisibleChars;
int _maxStringLength;
int _cursorPos;
int _cursorTicks;
Common::String _entryString;
TextSurface *_textSurface;
TextLabelWidget *_textLabelWidget;
BaseSurface *_cursorSurface;
uint32 _cursorFileHash;
int16 _cursorWidth, _cursorHeight;
void update();
uint32 handleMessage(int messageNum, const MessageParam &param, Entity *sender);
};
class SavegameListBox : public Widget { class SavegameListBox : public Widget {
public: public:
SavegameListBox(NeverhoodEngine *vm, int16 x, int16 y, int16 itemID, WidgetScene *parentScene, SavegameListBox(NeverhoodEngine *vm, int16 x, int16 y, int16 itemID, WidgetScene *parentScene,
int baseObjectPriority, int baseSurfacePriority, bool visible, int baseObjectPriority, int baseSurfacePriority,
StringArray *savegameList, TextSurface *textSurface1, TextSurface *textSurface2, uint32 fileHash1, NRect &rect); StringArray *savegameList, TextSurface *textSurface, uint32 bgFileHash, const NRect &rect);
virtual void onClick(); virtual void onClick();
virtual void addSprite(); virtual void addSprite();
void buildItems(); void buildItems();
@ -152,20 +187,35 @@ public:
void scrollDown(); void scrollDown();
void pageUp(); void pageUp();
void pageDown(); void pageDown();
uint getCurrIndex() const { return _currIndex; }
protected: protected:
NRect _rect; const NRect _rect;
uint32 _fileHash1; uint32 _bgFileHash;
int _maxStringLength; int _maxStringLength;
Common::Array<TextLabelWidget*> _textLabelItems; Common::Array<TextLabelWidget*> _textLabelItems;
int _topIndex; int _firstVisibleItem;
int _visibleItemsCount; int _lastVisibleItem;
StringArray *_savegameList; StringArray *_savegameList;
TextSurface *_textSurface1; TextSurface *_textSurface;
TextSurface *_textSurface2; uint _currIndex;
int _currIndex;
int _maxVisibleItemsCount; int _maxVisibleItemsCount;
}; };
class SaveGameMenu : public WidgetScene {
public:
SaveGameMenu(NeverhoodEngine *vm, Module *parentModule, StringArray *savegameList);
~SaveGameMenu();
virtual void handleEvent(int16 itemID, int eventType);
protected:
StringArray *_savegameList;
TextSurface *_textSurface;
SavegameListBox *_listBox;
TextEditWidget *_textEditWidget;
Common::String _savegameName;
void update();
uint32 handleMessage(int messageNum, const MessageParam &param, Entity *sender);
};
} // End of namespace Neverhood } // End of namespace Neverhood
#endif /* NEVERHOOD_MENUMODULE_H */ #endif /* NEVERHOOD_MENUMODULE_H */

View File

@ -112,16 +112,8 @@ Common::Error NeverhoodEngine::run() {
switch (event.type) { switch (event.type) {
case Common::EVENT_KEYDOWN: case Common::EVENT_KEYDOWN:
_keyState = event.kbd.keycode; _keyState = event.kbd.keycode;
switch (_keyState) { _gameModule->handleKeyDown(event.kbd.keycode);
case Common::KEYCODE_ESCAPE: _gameModule->handleAsciiKey(event.kbd.ascii);
_gameModule->handleEscapeKey();
break;
case Common::KEYCODE_SPACE:
_gameModule->handleSpaceKey();
break;
default:
break;
}
break; break;
case Common::EVENT_KEYUP: case Common::EVENT_KEYUP:
_keyState = Common::KEYCODE_INVALID; _keyState = Common::KEYCODE_INVALID;

View File

@ -100,28 +100,29 @@ void Scene::addEntity(Entity *entity) {
} }
bool Scene::removeEntity(Entity *entity) { bool Scene::removeEntity(Entity *entity) {
for (uint index = 0; index < _entities.size(); index++) { for (uint index = 0; index < _entities.size(); index++)
if (_entities[index] == entity) { if (_entities[index] == entity) {
_entities.remove_at(index); _entities.remove_at(index);
return true; return true;
} }
}
return false; return false;
} }
void Scene::addSurface(BaseSurface *surface) { void Scene::addSurface(BaseSurface *surface) {
int index = 0, insertIndex = -1; if (surface) {
for (Common::Array<BaseSurface*>::iterator iter = _surfaces.begin(); iter != _surfaces.end(); iter++) { int index = 0, insertIndex = -1;
if ((*iter)->getPriority() > surface->getPriority()) { for (Common::Array<BaseSurface*>::iterator iter = _surfaces.begin(); iter != _surfaces.end(); iter++) {
insertIndex = index; if ((*iter)->getPriority() > surface->getPriority()) {
break; insertIndex = index;
break;
}
index++;
} }
index++; if (insertIndex >= 0)
_surfaces.insert_at(insertIndex, surface);
else
_surfaces.push_back(surface);
} }
if (insertIndex >= 0)
_surfaces.insert_at(insertIndex, surface);
else
_surfaces.push_back(surface);
} }
bool Scene::removeSurface(BaseSurface *surface) { bool Scene::removeSurface(BaseSurface *surface) {
@ -191,7 +192,7 @@ Sprite *Scene::insertStaticSprite(uint32 fileHash, int surfacePriority) {
return addSprite(new StaticSprite(_vm, fileHash, surfacePriority)); return addSprite(new StaticSprite(_vm, fileHash, surfacePriority));
} }
void Scene::insertMouse433(uint32 fileHash, NRect *mouseRect) { void Scene::insertMouse433(uint32 fileHash, const NRect *mouseRect) {
NRect rect(-1, -1, -1, -1); NRect rect(-1, -1, -1, -1);
if (mouseRect) if (mouseRect)
rect = *mouseRect; rect = *mouseRect;

View File

@ -59,7 +59,7 @@ public:
void setPalette(uint32 fileHash = 0); void setPalette(uint32 fileHash = 0);
void setHitRects(uint32 id); void setHitRects(uint32 id);
Sprite *insertStaticSprite(uint32 fileHash, int surfacePriority); Sprite *insertStaticSprite(uint32 fileHash, int surfacePriority);
void insertMouse433(uint32 fileHash, NRect *mouseRect = NULL); void insertMouse433(uint32 fileHash, const NRect *mouseRect = NULL);
void insertMouse435(uint32 fileHash, int16 x1, int16 x2); void insertMouse435(uint32 fileHash, int16 x1, int16 x2);
void insertNavigationMouse(uint32 fileHash, int type); void insertNavigationMouse(uint32 fileHash, int type);
void showMouse(bool visible); void showMouse(bool visible);