/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * aint32 with this program; if not, write to the Free Software * * * Based on the original sources * Faery Tale II -- The Halls of the Dead * (c) 1993-1996 The Wyrmkeep Entertainment Co. */ #include "common/config-manager.h" #include "audio/mixer.h" #include "saga2/saga2.h" #include "saga2/intrface.h" #include "saga2/grequest.h" #include "saga2/gtextbox.h" #include "saga2/saveload.h" #include "saga2/script.h" #include "saga2/uidialog.h" #include "saga2/document.h" #include "saga2/tilemode.h" #include "saga2/display.h" #include "saga2/uitext.h" #include "saga2/vpal.h" #include "saga2/palette.h" #include "saga2/fontlib.h" namespace Saga2 { // dialog functions APPFUNC(cmdDialogQuit); APPFUNCV(cmdFileSave); APPFUNCV(cmdFileLoad); APPFUNC(cmdSaveDialogUp); APPFUNC(cmdSaveDialogDown); APPFUNC(cmdTextResponse); APPFUNC(cmdOptionsSaveGame); APPFUNC(cmdOptionsLoadGame); APPFUNC(cmdOptionsNewGame); APPFUNC(cmdQuitGame); APPFUNC(cmdCredits); APPFUNC(cmdAutoAggression); APPFUNC(cmdAutoWeapon); APPFUNC(cmdNight); APPFUNC(cmdSpeechText); // volume control functions APPFUNC(cmdSetMIDIVolume); APPFUNC(cmdSetDIGVolume); APPFUNC(cmdSetSpeechVolume); APPFUNC(cmdSetSoundVolume); APPFUNCV(cmdSaveVolumeSettings); #define SmallFont Helv11Font /* ===================================================================== * External declarations * ===================================================================== */ extern BackWindow *mainWindow; extern bool fullInitialized; /* ===================================================================== * Dialog Controls * ===================================================================== */ // control pointers gTextBox *textBox; CTextWindow *editWin; /* ===================================================================== * User interface dialog metrics * ===================================================================== */ enum fileProcessTypes { typeSave = 0, typeLoad }; enum saveLoadImageResIDs { SLTopPanelResID = 0, SLMidPanelResID, SLBotPanelResID }; enum optionsImageResIDs { optTopPanelResID = 0, optMidPanelResID, optBotPanelResID }; enum messageImageResIDs { mesPanelResID = 0 }; // panels static const StaticRect SLTopPanel = { kSLDBoxX, kSLDBoxY, kSLTPWidth, kSLTPHeight }; static const StaticRect SLMidPanel = { kSLDBoxX, kSLDBoxY + kSLTPHeight, kSLMDWidth, kSLMDHeight }; static const StaticRect SLBotPanel = { kSLDBoxX, kSLDBoxY + kSLTPHeight + kSLMDHeight, kSLBTWidth, kSLBTHeight }; // buttons static const StaticRect SLQuitBtnRect = { 211, kSLTPHeight + kSLMDHeight + 11, 122, 30 }; static const StaticRect SLBtnRect = { 31, kSLTPHeight + kSLMDHeight + 11, 122, 30 }; static const StaticRect SLUpArrowBtnRect = { 327, 46, 32, 36 }; static const StaticRect SLDnArrowBtnRect = { 327, 121, 32, 36 }; // texts static const StaticRect SLTitleRect = { 0, 0, kSLDBoxXSzNS, 47 }; // save load window rect static const StaticRect saveLoadWindowRect = { kSLDBoxX, kSLDBoxY, kSLDBoxXSize, kSLDBoxYSize }; // indirections static const StaticRect *saveLoadButtonRects[kNumSaveLoadBtns] = { &SLQuitBtnRect, &SLBtnRect, &SLUpArrowBtnRect, &SLDnArrowBtnRect }; static const StaticRect *saveLoadTextRects[kNumSaveLoadTexts] = { &SLTitleRect }; // save/load dialog window decorations static StaticWindow saveWindowDecorations[kNumSaveLoadPanels] = { {SLTopPanel, nullptr, SLTopPanelResID}, {SLMidPanel, nullptr, SLMidPanelResID}, {SLBotPanel, nullptr, SLBotPanelResID} }; // panels static const StaticRect optTopPanel = { kOptBoxX, kOptBoxY, kOptTPWidth, kOptTPHeight }; static const StaticRect optMidPanel = { kOptBoxX, kOptBoxY + kOptTPHeight, kOptMDWidth, kOptMDHeight }; static const StaticRect optBotPanel = { kOptBoxX, kOptBoxY + kOptTPHeight + kOptMDHeight, kOptBTWidth, kOptBTHeight }; static const StaticRect optResumeRect = { kOptBoxXSzNS - (kPushButtonWidth + 14), kButtonYOffset + kButtonSpace, kPushButtonWidth, kPushButtonHeight }; static const StaticRect optSaveRect = { kOptBoxXSzNS - (kPushButtonWidth + 14), kButtonYOffset + ((kPushButtonHeight * 1) + kButtonSpace * 2), kPushButtonWidth, kPushButtonHeight }; static const StaticRect optRestoreRect = { kOptBoxXSzNS - (kPushButtonWidth + 14), kButtonYOffset + ((kPushButtonHeight * 2) + kButtonSpace * 3), kPushButtonWidth, kPushButtonHeight }; static const StaticRect optQuitRect = { kOptBoxXSzNS - (kPushButtonWidth + 14), kButtonYOffset + ((kPushButtonHeight * 3) + kButtonSpace * 4), kPushButtonWidth, kPushButtonHeight }; static const StaticRect optCreditsRect = { kOptBoxXSzNS - (kPushButtonWidth + 14), kButtonYOffset + ((kPushButtonHeight * 4) + kButtonSpace * 5), kPushButtonWidth, kPushButtonHeight }; static const StaticRect optAggressRect = { 14, 98 + kOptTPHeight, 18, 17 }; static const StaticRect optWeaponRect = { 14, 121 + kOptTPHeight, 18, 17 }; static const StaticRect optSpeechRect = { 14, 121 + 23 + kOptTPHeight, 18, 17 }; static const StaticRect optNightRect = { 14 + 200, 98 + kOptTPHeight, 18, 17 }; static const StaticRect optTopSliderRect = { 15, 15 + kOptTPHeight - 2, kSliderWidth, kImageHeight }; static const StaticRect optMidSliderRect = { 15, (int16)(optTopSliderRect.y + 32 - 2), kSliderWidth, kImageHeight }; static const StaticRect optBotSliderRect = { 15, (int16)(optMidSliderRect.y + 32 - 2), kSliderWidth, kImageHeight }; static const StaticRect optTopFaceRect = { optTopSliderRect.x, optTopSliderRect.y, 28, kImageHeight }; static const StaticRect optMidFaceRect = { optMidSliderRect.x, optMidSliderRect.y, 28, kImageHeight }; static const StaticRect optBotFaceRect = { optBotSliderRect.x, optBotSliderRect.y, 28, kImageHeight }; // texts static const StaticRect optTitleText = { 0, 0, kOptBoxXSzNS, kOptTPHeight }; static const StaticRect optTopSlideText = { 16 + kSliderWidth, (int16)(optTopSliderRect.y + 1), kTextPixelLen, 20 }; static const StaticRect optMidSlideText = { 16 + kSliderWidth, (int16)(optMidSliderRect.y + 1), kTextPixelLen, 17 }; static const StaticRect optBotSlideText = { 16 + kSliderWidth, (int16)(optBotSliderRect.y + 1), kTextPixelLen, 17 }; static const StaticRect optTopCheckText = { (int16)(optAggressRect.x + optAggressRect.width + 3), optAggressRect.y, kTextPixelLen - kSmallTextOffset, 17 }; static const StaticRect optMidCheckText = { (int16)(optWeaponRect.x + optWeaponRect.width + 3), optWeaponRect.y, kTextPixelLen - kSmallTextOffset, 17 }; static const StaticRect optBotCheckText = { (int16)(optSpeechRect.x + optSpeechRect.width + 3), optSpeechRect.y, kTextPixelLen - kSmallTextOffset, 17 }; static const StaticRect optTop2CheckText = { (int16)(optNightRect.x + optNightRect.width + 3), optNightRect.y, kTextPixelLen - kSmallTextOffset, 17 }; // options window rect static const StaticRect optionsWindowRect = { kOptBoxX, kOptBoxY, kOptBoxXSize, kOptBoxYSize }; // indirections static const StaticRect *optionsButtonRects[] = { &optResumeRect, &optSaveRect, &optRestoreRect, &optQuitRect, &optCreditsRect, &optAggressRect, &optWeaponRect, &optSpeechRect, &optNightRect }; static const StaticRect *optionsTextRects[] = { &optTitleText, &optTopSlideText, &optMidSlideText, &optBotSlideText, &optTopCheckText, &optMidCheckText, &optBotCheckText, &optTop2CheckText }; // options dialog window decorations static StaticWindow optionsDecorations[kNumOptionsPanels] = { {optTopPanel, nullptr, optTopPanelResID}, {optMidPanel, nullptr, optMidPanelResID}, {optBotPanel, nullptr, optBotPanelResID} }; // panels static const StaticRect messagePanel = { kMesBoxX, kMesBoxY, kMesBoxXSize, kMesBoxYSize }; // buttons static const StaticRect mesCancelBtnRect = { kMesBoxXSzNS - (kPushButtonWidth + kMesBtnOffset), kMesBoxY - kMesBtnOffset, kPushButtonWidth, kPushButtonHeight }; static const StaticRect mesOkBtnRect = { kMesBtnOffset, kMesBoxY - kMesBtnOffset, kPushButtonWidth, kPushButtonHeight }; static const StaticRect mesBtn3Rect = { kMesBoxXSzNS / 2 - kPushButtonWidth / 2, kMesBoxY - kMesBtnOffset, kPushButtonWidth, kPushButtonHeight }; // texts static const StaticRect mesTitleRect = { 0, 0, kMesBoxXSzNS, 47 }; static const StaticRect messageRect = { 0, 0, kMesBoxXSzNS, kMesBoxYSize }; static const StaticRect *messageTextRects[kNumMessageTexts] = { &mesTitleRect, &messageRect }; static const StaticRect *messageButtonRects[kNumMessageBtns] = { &mesOkBtnRect, &mesCancelBtnRect, &mesBtn3Rect }; // options window rect static const StaticRect messageWindowRect = { kMesBoxX, kMesBoxY, kMesBoxXSize, kMesBoxYSize }; // message dialog window decorations static StaticWindow messageDecorations[kNumMessagePanels] = { {messagePanel, nullptr, mesPanelResID} }; // pointer to the auto aggression button GfxOwnerSelCompButton *autoAggressBtn, *autoWeaponBtn, *nightBtn, *speechTextBtn; static int deferredLoadID = 0; static bool deferredLoadFlag = false; static bool deferredSaveFlag = false; static char deferredSaveName[64]; inline bool isUserAction(gEvent ev) { return (ev.eventType == kEventNewValue) || (ev.eventType == kEventKeyDown); } /* ===================================================================== * Save game file name handling funtions * ===================================================================== */ char **initFileFields() { uint16 i; SaveFileHeader header; // The save file header. char **strings = new (char *[kNumEditLines]); for (i = 0; i < kNumEditLines; i++) { strings[i] = new char[kEditLen + 1]; if (getSaveName(i, header)) { Common::strlcpy(strings[i], header.saveName.c_str(), kEditLen); } else { Common::strlcpy(strings[i], FILE_DIALOG_NONAME, kEditLen); strings[i][0] |= 0x80; } // make sure this thing is caped strings[i][kEditLen] = '\0'; } return strings; } int numValid(char **names) { int v = 0; for (int i = 0; i < kNumEditLines; i++) { if ((names[i][0] & 0x80) == 0) v++; } return v; } void destroyFileFields(char **strings) { uint16 i; for (i = 0; i < kNumEditLines; i++) { if (strings[i]) delete[] strings[i]; strings[i] = nullptr; } delete[] strings; } bool getSaveName(int8 saveNo, SaveFileHeader &header) { Common::String fname = g_vm->getSavegameFile(saveNo); Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(fname); if (!in) { debugC(1, kDebugSaveload, "Unable to load save %d (%s)", saveNo, fname.c_str()); return false; } header.read(in); delete in; return true; } /* ===================================================================== * Dialog boxes * ===================================================================== */ int16 FileDialog(int16 fileProcess) { //const int strLen = kEditLen; char **fieldStrings; uint16 stringIndex; bool displayOnly; void **arrowUpIm = nullptr, **arrowDnIm = nullptr, **pushBtnIm = nullptr; AppFunc *fileCommands[2] = { cmdFileSave, cmdFileLoad }; // text for dialog const char *saveTextStrings[kNumSaveLoadTexts] = { SAVE_DIALOG_NAME }; const char *saveBtnStrings[kNumSaveLoadBtns] = { SAVE_DIALOG_BUTTON1, SAVE_DIALOG_BUTTON2 }; const char *loadTextStrings[kNumSaveLoadTexts] = { LOAD_DIALOG_NAME }; const char *loadBtnStrings[kNumSaveLoadBtns] = { LOAD_DIALOG_BUTTON1, LOAD_DIALOG_BUTTON2 }; const char **textStrings[] = { saveTextStrings, loadTextStrings }; const char **btnStrings[] = { saveBtnStrings, loadBtnStrings }; // make the text coloring object textPallete pal(33 + 9, 36 + 9, 41 + 9, 34 + 9, 40 + 9, 43 + 9); if (fileProcess == typeSave) { stringIndex = 0; displayOnly = false; } else { stringIndex = 1; displayOnly = true; } // resource info const int16 dialogPushResNum = 4; const int16 upArrowResNum = 0; const int16 dnArrowResNum = 2; // requester info struct requestInfo rInfo; rInfo.result = -1; rInfo.running = true; // point to the modal window ModalWindow *win; // resource handle hResContext *decRes; // get the file fields names fieldStrings = initFileFields(); #ifndef ALLOW_BAD_LOADS if (displayOnly && numValid(fieldStrings) == 0) { destroyFileFields(fieldStrings); if (userDialog("Error", "No saved games to load!\n Would you like to start over?", "_Yes", "_No", nullptr) != 1) { deferredLoadFlag = true; deferredLoadID = 999; return typeLoad; } return 0; } #endif // init the resource context handle decRes = resFile->newContext(kDialogGroupID, "dialog resources"); // get the graphics associated with the buttons pushBtnIm = loadButtonRes(decRes, dialogPushResNum, kNumBtnImages); arrowUpIm = loadButtonRes(decRes, upArrowResNum, kNumBtnImages); arrowDnIm = loadButtonRes(decRes, dnArrowResNum, kNumBtnImages); // create the window win = new ModalWindow(saveLoadWindowRect, 0, nullptr); // make the quit button new GfxCompButton(*win, *saveLoadButtonRects[0], pushBtnIm, kNumBtnImages, btnStrings[stringIndex][0], pal, 0, cmdDialogQuit); //t->_accelKey=0x1B; // make the Save/Load button new GfxCompButton(*win, *saveLoadButtonRects[1], pushBtnIm, kNumBtnImages, btnStrings[stringIndex][1], pal, fileProcess, fileCommands[fileProcess]); //t->_accelKey=0x0D; // make the up arrow new GfxCompButton(*win, *saveLoadButtonRects[2], arrowUpIm, kNumBtnImages, 0, cmdSaveDialogUp); //t->_accelKey=33+0x80; // make the down arrow new GfxCompButton(*win, *saveLoadButtonRects[3], arrowDnIm, kNumBtnImages, 0, cmdSaveDialogDown); //t->_accelKey=34+0x80; // attach the title new CPlaqText(*win, *saveLoadTextRects[0], textStrings[stringIndex][0], &Plate18Font, 0, pal, 0, nullptr); // attach the text box editing field object textBox = new gTextBox(*win, editBaseRect, &Onyx10Font, kTextHeight, kTextPen, kTextBackground, kTextHilite, kTextBackHilite, kCursorColor, nullptr, "Error out", fieldStrings, kEditLen, 0, (uint16) - 1, displayOnly, nullptr, fileCommands[fileProcess], cmdDialogQuit); win->setDecorations(saveWindowDecorations, ARRAYSIZE(saveWindowDecorations), decRes, 'S', 'L', 'D'); win->_userData = &rInfo; win->open(); if (GameMode::_newmodeFlag) GameMode::update(); win->invalidate(); textBox->choose(); EventLoop(rInfo.running, true); // remove the window all attatched controls delete win; win = nullptr; // unload all image arrays unloadImageRes(arrowUpIm, kNumBtnImages); unloadImageRes(arrowDnIm, kNumBtnImages); unloadImageRes(pushBtnIm, kNumBtnImages); // remove the resource handle if (decRes) resFile->disposeContext(decRes); decRes = nullptr; // destroy the file fields destroyFileFields(fieldStrings); // replace the damaged area mainWindow->invalidate(&saveLoadWindowRect); // return the result code return rInfo.result; } /* ===================================================================== * Options dialog box * ===================================================================== */ void updateMainDisplay(); void drawMainDisplay(); void fadeUp(); void fadeDown(); void clearTileAreaPort(); void displayUpdate(); int16 OptionsDialog(bool disableSaveResume) { // Save back buffer before opening the dialog g_vm->_renderer->saveBackBuffer(kBeforeOpeningMenu); // text for dialog const char *btnStrings[kNumOptionsBtns] = { OPTN_DIALOG_BUTTON1, OPTN_DIALOG_BUTTON2, OPTN_DIALOG_BUTTON3, OPTN_DIALOG_BUTTON4, OPTN_DIALOG_BUTTON5 }; const char *textStrings[kNumOptionsTexts] = { OPTN_DIALOG_NAME, OPTN_DIALOG_SLIDE1, OPTN_DIALOG_SLIDE2, OPTN_DIALOG_SLIDE3, OPTN_DIALOG_CHECK1, OPTN_DIALOG_CHECK2, OPTN_DIALOG_CHECK3, OPTN_DIALOG_CHECK4 }; // make the text coloring object textPallete pal(33 + 9, 36 + 9, 41 + 9, 34 + 9, 40 + 9, 43 + 9); // requester info struct requestInfo rInfo; rInfo.result = -1; rInfo.running = true; deferredLoadID = 0; deferredLoadFlag = false; deferredSaveFlag = false; deferredSaveName[0] = '\0'; // point to the modal window ModalWindow *win; // resource handle hResContext *decRes; // resource info const int16 dialogPushResNum = 4; const int16 checkResNum = 6; const int16 slideFaceResNum = 8; const int16 numSlideFace = 4; // compressed image arrays void **dialogPushImag; void **checkImag; void **slideFaceImag; if (!fullInitialized) return -1; // init the resource context handle decRes = resFile->newContext(kDialogGroupID, "dialog resources"); // get the graphics associated with the buttons dialogPushImag = loadButtonRes(decRes, dialogPushResNum, kNumBtnImages); checkImag = loadButtonRes(decRes, checkResNum, kNumBtnImages); slideFaceImag = loadButtonRes(decRes, slideFaceResNum, numSlideFace); // create the window win = new ModalWindow(optionsWindowRect, 0, nullptr); GfxCompButton *t; // buttons if (!disableSaveResume) { t = new GfxCompButton(*win, *optionsButtonRects[0], dialogPushImag, kNumBtnImages, btnStrings[0], pal, 0, cmdDialogQuit); t->_accelKey = 0x1B; t = new GfxCompButton(*win, *optionsButtonRects[1], dialogPushImag, kNumBtnImages, btnStrings[1], pal, 0, cmdOptionsSaveGame); // make the quit button t->_accelKey = 'S'; } else { t = new GfxCompButton(*win, *optionsButtonRects[1], dialogPushImag, kNumBtnImages, OPTN_DIALOG_BUTTON6, pal, 0, cmdOptionsNewGame); t->_accelKey = 'N'; } t = new GfxCompButton(*win, *optionsButtonRects[2], dialogPushImag, kNumBtnImages, btnStrings[2], pal, 0, cmdOptionsLoadGame); // make the quit button t->_accelKey = 'L'; t = new GfxCompButton(*win, *optionsButtonRects[3], dialogPushImag, kNumBtnImages, btnStrings[3], pal, 0, cmdQuitGame); t->_accelKey = 'Q'; t = new GfxCompButton(*win, *optionsButtonRects[4], dialogPushImag, kNumBtnImages, btnStrings[4], pal, 0, cmdCredits); t->_accelKey = 'C'; autoAggressBtn = new GfxOwnerSelCompButton(*win, *optionsButtonRects[5], checkImag, kNumBtnImages, 0, cmdAutoAggression); autoAggressBtn->select(isAutoAggressionSet()); autoWeaponBtn = new GfxOwnerSelCompButton(*win, *optionsButtonRects[6], checkImag, kNumBtnImages, 0, cmdAutoWeapon); autoWeaponBtn->select(isAutoWeaponSet()); speechTextBtn = new GfxOwnerSelCompButton(*win, *optionsButtonRects[7], checkImag, kNumBtnImages, 0, cmdSpeechText); speechTextBtn->select(g_vm->_speechText); nightBtn = new GfxOwnerSelCompButton(*win, *optionsButtonRects[8], checkImag, kNumBtnImages, 0, cmdNight); nightBtn->select(g_vm->_showNight); new GfxSlider(*win, optTopSliderRect, optTopFaceRect, 0, Audio::Mixer::kMaxMixerVolume, slideFaceImag, numSlideFace, ConfMan.getInt("sfx_volume"), 0, cmdSetSoundVolume); new GfxSlider(*win, optMidSliderRect, optMidFaceRect, 0, Audio::Mixer::kMaxMixerVolume, slideFaceImag, numSlideFace, ConfMan.getInt("speech_volume"), 0, cmdSetSpeechVolume); new GfxSlider(*win, optBotSliderRect, optBotFaceRect, 0, Audio::Mixer::kMaxMixerVolume, slideFaceImag, numSlideFace, ConfMan.getInt("music_volume"), 0, cmdSetMIDIVolume); new CPlaqText(*win, *optionsTextRects[0], textStrings[0], &Plate18Font, 0, pal, 0, nullptr); for (int i = 1; i < kNumOptionsTexts; i++) { new CPlaqText(*win, *optionsTextRects[i], textStrings[i], &SmallFont, kTextPosLeft, pal, 0, nullptr); } win->setDecorations(optionsDecorations, ARRAYSIZE(optionsDecorations), decRes, 'O', 'P', 'T'); win->_userData = &rInfo; win->open(); EventLoop(rInfo.running, true); g_vm->saveConfig(); // remove the window all attatched controls delete win; win = nullptr; // unload all image arrays unloadImageRes(slideFaceImag, numSlideFace); unloadImageRes(checkImag, kNumBtnImages); unloadImageRes(dialogPushImag, kNumBtnImages); // remove the resource handle if (decRes) resFile->disposeContext(decRes); decRes = nullptr; // replace the damaged area if (deferredLoadFlag) { reDrawScreen(); disableUserControls(); cleanupGameState(); fadeDown(); if (deferredLoadID == 999) loadRestartGame(); else { loadSavedGameState(deferredLoadID); } if (GameMode::_newmodeFlag) GameMode::update(); updateActiveRegions(); //displayUpdate(); enableUserControls(); updateMainDisplay(); drawMainDisplay(); enablePaletteChanges(); updateAllUserControls(); fadeUp(); reDrawScreen(); } else { if (deferredSaveFlag) { #ifdef IMMEDIATE_SAVE g_vm->saveGameState(deferredLoadID, deferredSaveName, false); #endif } mainWindow->invalidate(&optionsWindowRect); } // Pop back buffer g_vm->_renderer->popSavedBackBuffer(kBeforeOpeningMenu); // return the result code return rInfo.result; } /* ===================================================================== * message dialog box * ===================================================================== */ static char stripAccel(char (&t)[32], const char *s) { char accel = '\0'; char *underscore; if (s == nullptr) return accel; Common::strcpy_s(t, s); if ((underscore = strchr(t, '_')) != nullptr) { accel = toupper(underscore[1]); Common::strcpy_s(underscore, sizeof(t) - (underscore - t), s + (underscore - t) + 1); } return accel; } #ifdef DYNALOAD_USERDIALOG // requester info struct requestInfo udrInfo; // point to the modal window ModalWindow *udWin; // resource handle hResContext *udDecRes; // compressed image array void **udDialogPushImag; bool udInit = false; bool initUserDialog() { const int16 dialogPushResNum = 4; // init the resource context handle udDecRes = resFile->newContext(kDialogGroupID, "dialog resources"); // get the graphics associated with the buttons udDialogPushImag = loadButtonRes(udDecRes, dialogPushResNum, kNumBtnImages); // create the window udWin = new ModalWindow(messageWindowRect, 0 nullptr); udWin->setDecorations(messageDecorations, ARRAYSIZE(messageDecorations), udDecRes, 'M', 'E', 'S'); udWin->_userData = &udrInfo; if (udDecRes) resFile->disposeContext(udDecRes); udDecRes = nullptr; udInit = true; return true; } bool userDialogAvailable() { return udInit; } void cleanupUserDialog() { udInit = false; // remove the window all attatched controls if (udWin) delete udWin; udWin = nullptr; // unload all image arrays unloadImageRes(udDialogPushImag, kNumBtnImages); } int16 userDialog(const char *title, const char *msg, const char *bMsg1, const char *bMsg2, const char *bMsg3) { const maxBtns = 3; uint8 numBtns = 0; char k1, k2, k3; char btnMsg1[32]; char btnMsg2[32]; char btnMsg3[32]; if (bMsg1 && strlen(bMsg1)) numBtns++; if (bMsg2 && strlen(bMsg2)) numBtns++; if (bMsg3 && strlen(bMsg3)) numBtns++; k1 = stripAccel(btnMsg1, bMsg1); k2 = stripAccel(btnMsg2, bMsg2); k3 = stripAccel(btnMsg3, bMsg3); // make the text coloring object textPallete pal(33 + 9, 36 + 9, 41 + 9, 34 + 9, 40 + 9, 43 + 9); if (udWin == nullptr) return -1; udrInfo.result = -1; udrInfo.running = true; GfxCompButton *t; // button one if (numBtns >= 1) { t = new GfxCompButton(*udWin, messageButtonRects[0], udDialogPushImag, kNumBtnImages, btnMsg1, pal, 10, cmdDialogQuit); t->accel = k1; } // button two if (numBtns >= 2) { t = new GfxCompButton(*udWin, messageButtonRects[1], udDialogPushImag, kNumBtnImages, btnMsg2, pal, 11, cmdDialogQuit); t->accel = k2; } // button three if (numBtns >= 3) { t = new GfxCompButton(*udWin, messageButtonRects[2], udDialogPushImag, kNumBtnImages, btnMsg3, pal, 12, cmdDialogQuit); t->accel = k3; } // title for the box new CPlaqText(*udWin, messageTextRects[0], title, &Plate18Font, nullptr, pal, 0, nullptr); // message for box new CPlacardPanel(*udWin, messageTextRects[1], msg, &Onyx10Font, nullptr, pal, 0, nullptr); udWin->open(); EventLoop(udrInfo.running, true); udWin->close(); udWin->removeControls(); // replace the damaged area mainWindow->invalidate(messageWindowRect); // return the result code return (udrInfo.result % 10); } #else /* ===================================================================== * message dialog box * ===================================================================== */ bool initUserDialog() { return true; } bool userDialogAvailable() { return true; } void cleanupUserDialog() {} int16 userDialog(const char *title, const char *msg, const char *bMsg1, const char *bMsg2, const char *bMsg3) { //const int maxBtns = 3; uint8 numBtns = 0; char k1, k2, k3; char btnMsg1[32]; char btnMsg2[32]; char btnMsg3[32]; if (bMsg1 && strlen(bMsg1)) numBtns++; if (bMsg2 && strlen(bMsg2)) numBtns++; if (bMsg3 && strlen(bMsg3)) numBtns++; k1 = stripAccel(btnMsg1, bMsg1); k2 = stripAccel(btnMsg2, bMsg2); k3 = stripAccel(btnMsg3, bMsg3); // make the text coloring object textPallete pal(33 + 9, 36 + 9, 41 + 9, 34 + 9, 40 + 9, 43 + 9); // resource info const int16 dialogPushResNum = 4; // requester info struct requestInfo rInfo; // point to the modal window ModalWindow *win; // resource handle hResContext *decRes; // compressed image array void **dialogPushImag; rInfo.result = -1; rInfo.running = true; if (!fullInitialized) return -1; // init the resource context handle decRes = resFile->newContext(kDialogGroupID, "dialog resources"); // get the graphics associated with the buttons dialogPushImag = loadButtonRes(decRes, dialogPushResNum, kNumBtnImages); // create the window win = new ModalWindow(messageWindowRect, 0, nullptr); GfxCompButton *t; // button one if (numBtns >= 1) { t = new GfxCompButton(*win, *messageButtonRects[0], dialogPushImag, kNumBtnImages, btnMsg1, pal, 10, cmdDialogQuit); t->_accelKey = k1; } // button two if (numBtns >= 2) { t = new GfxCompButton(*win, *messageButtonRects[1], dialogPushImag, kNumBtnImages, btnMsg2, pal, 11, cmdDialogQuit); t->_accelKey = k2; } // button three if (numBtns >= 3) { t = new GfxCompButton(*win, *messageButtonRects[2], dialogPushImag, kNumBtnImages, btnMsg3, pal, 12, cmdDialogQuit); t->_accelKey = k3; } // title for the box new CPlaqText(*win, *messageTextRects[0], title, &Plate18Font, 0, pal, 0, nullptr); // message for box new CPlacardPanel(*win, *messageTextRects[1], msg, &Onyx10Font, 0, pal, 0, nullptr); win->setDecorations(messageDecorations, ARRAYSIZE(messageDecorations), decRes, 'M', 'E', 'S'); win->_userData = &rInfo; win->open(); EventLoop(rInfo.running, true); // remove the window all attatched controls delete win; // unload all image arrays unloadImageRes(dialogPushImag, kNumBtnImages); // remove the resource handle if (decRes) resFile->disposeContext(decRes); decRes = nullptr; // replace the damaged area mainWindow->invalidate(&messageWindowRect); // return the result code return rInfo.result % 10; } #endif /* ===================================================================== * Placard thingus * ===================================================================== */ CPlacardWindow::CPlacardWindow( const Rect16 &r, uint16 ident, AppFunc *cmd, char *windowText, textPallete &pal, gFont *font) : ModalWindow(r, ident, cmd) { _textPal = pal; _textFont = font; positionText(windowText, Rect16(0, 0, r.width, r.height)); } void CPlacardWindow::positionText( char *windowText, const Rect16 &textArea) { if (windowText) { int16 i, yPos, maxY; int16 fontHeight = _textFont->height; // make a copy of the window text string Common::sprintf_s(_titleBuf, "%s", windowText); // break up the title text string _titleCount = SplitString(_titleBuf, _titleStrings, kMaxLines, '\n'); yPos = textArea.y + ((textArea.height - _titleCount * fontHeight) >> 1); yPos = MAX(yPos, textArea.y); maxY = textArea.y + textArea.height - fontHeight; for (i = 0; i < _titleCount; i++, yPos += fontHeight) { if (yPos < maxY) { _titlePos[i].y = yPos; _titlePos[i].x = textArea.x + ((textArea.width - TextWidth(_textFont, _titleStrings[i], -1, 0)) >> 1); } else _titleCount = i; } } else _titleCount = 0; } int16 CPlacardWindow:: SplitString( char *text, char *textStart[], int16 maxStrings, char delimiter) { int16 count; for (count = 0; count < maxStrings;) { textStart[count++] = text; if ((text = strchr(text, delimiter)) == nullptr) break; *text++ = '\0'; } return count; } // just exit if the user hit the screen. bool CPlacardWindow::pointerHit(gPanelMessage &) { gWindow *win; requestInfo *ri; win = getWindow(); // get the window pointer ri = win ? (requestInfo *)win->_userData : nullptr; if (ri) { ri->running = 0; ri->result = _id; } //activate( kEventMouseDown ); return true; } void CPlacardWindow::drawClipped( gPort &port, const Point16 &offset, const Rect16 &r) { if (!_extent.overlap(r)) return; // do background drawing first... ModalWindow::drawClipped(port, offset, r); int16 i; Point16 origin; Rect16 rect; SAVE_GPORT_STATE(port); origin.x = _extent.x - offset.x; origin.y = _extent.y - offset.y; rect.x = origin.x; rect.y = origin.y; rect.width = _extent.width; rect.height = _extent.height; for (i = 0; i < _titleCount; i++) { Point16 textPos = origin + _titlePos[i]; writePlaqTextPos(port, textPos, _textFont, 0, _textPal, false, _titleStrings[i]); } } CPlacardPanel::CPlacardPanel(gPanelList &gpl, const Rect16 &r, const char *t, gFont *f, int16 i, textPallete &p, int16 i2, AppFunc *cmd) : CPlaqText(gpl, r, t, f, i, p, i2, cmd) { positionText(t, Rect16(0, 0, r.width, r.height)); } void CPlacardPanel::positionText(const char *windowText, const Rect16 &textArea) { if (windowText) { int16 i, yPos, maxY; int16 fontHeight = _buttonFont->height; // make a copy of the window text string Common::sprintf_s(_titleBuf, "%s", windowText); // break up the title text string _titleCount = SplitString(_titleBuf, _titleStrings, kMaxLines, '\n'); yPos = textArea.y + ((textArea.height - _titleCount * fontHeight) >> 1); yPos = MAX(yPos, textArea.y); maxY = textArea.y + textArea.height - fontHeight; for (i = 0; i < _titleCount; i++, yPos += fontHeight) { if (yPos < maxY) { _titlePos[i].y = yPos; _titlePos[i].x = textArea.x + ((textArea.width - TextWidth(_buttonFont, _titleStrings[i], -1, 0)) >> 1); } else _titleCount = i; } } else _titleCount = 0; } int16 CPlacardPanel:: SplitString( char *text, char *textStart[], int16 maxStrings, char delimiter) { int16 count; for (count = 0; count < maxStrings;) { textStart[count++] = text; if ((text = strchr(text, delimiter)) == nullptr) break; *text++ = '\0'; } return count; } void CPlacardPanel::drawClipped( gPort &port, const Point16 &offset, const Rect16 &r) { if (!_extent.overlap(r)) return; // do background drawing first... int16 i; Point16 origin; Rect16 rect; SAVE_GPORT_STATE(port); origin.x = _extent.x - offset.x; origin.y = _extent.y - offset.y; rect.x = origin.x; rect.y = origin.y; rect.width = _extent.width; rect.height = _extent.height; for (i = 0; i < _titleCount; i++) { Point16 textPos = origin + _titlePos[i]; writePlaqTextPos(port, textPos, _buttonFont, 0, _textFacePal, false, _titleStrings[i]); } } void placardWindow(int8 type, char *text) { Rect16 plaqRectWood = Rect16((640 - 238) / 2, (480 - 145) / 3, 238, 145); Rect16 plaqRectStone = Rect16((640 - 236) / 2, (480 - 143) / 3, 236, 143); Rect16 plaqRectBrass = Rect16((640 - 274) / 2, (480 - 145) / 3, 274, 145); // decoration information WindowDecoration plaqDecWood[1] = { WindowDecoration(plaqRectWood, 0) }; WindowDecoration plaqDecStone[1] = { WindowDecoration(plaqRectStone, 1) }; WindowDecoration plaqDecBrass[2] = { WindowDecoration(plaqRectBrass, 2), WindowDecoration(plaqRectBrass, 2) }; // used to hold the coloration of the text for a give type textPallete pal; // requester info struct requestInfo rInfo; rInfo.result = -1; rInfo.running = true; // point to the modal window CPlacardWindow *win; // resource handle hResContext *resContext; // init the resource context handle resContext = resFile->newContext(MKTAG('I', 'M', 'A', 'G'), "Placard resources"); // do type related assignments switch (type) { case WOOD_TYPE: // set wood text inlay color pal.set(62, 64, 67, 11, 23, 17); // create the window win = new CPlacardWindow(plaqRectWood, 0, nullptr, text, pal, &Plate18Font); // setup the background imagery win->setDecorations(plaqDecWood, ARRAYSIZE(plaqDecWood), resContext, 'P', 'L', 'Q'); break; case STONE_TYPE: // set stone text inlay color pal.set(16, 12, 18, 11, 23, 0x78); // create the window win = new CPlacardWindow(plaqRectStone, 0, nullptr, text, pal, &Plate18Font); // setup the background imagery win->setDecorations(plaqDecStone, ARRAYSIZE(plaqDecStone), resContext, 'P', 'L', 'Q'); break; case BRASS_TYPE: // set brass text inlay color pal.set(89, 93, 95, 11, 23, 0x76); // create the window win = new CPlacardWindow(plaqRectBrass, 0, nullptr, text, pal, &Plate18Font); // setup the background imagery win->setDecorations(plaqDecBrass, ARRAYSIZE(plaqDecBrass), resContext, 'P', 'L', 'Q'); break; default: error("Unhandled placard type %d", type); break; } win->_userData = &rInfo; win->open(); EventLoop(rInfo.running, true); // remove the window all attatched controls delete win; // remove the resource handle if (resContext) resFile->disposeContext(resContext); // replace the damaged area mainWindow->invalidate(&plaqRectBrass); // brass just happens to be the largest rect.... // return the result code //return rInfo.result; } void updateAutoAggressionButton(bool setting) { if (autoAggressBtn != nullptr) autoAggressBtn->select(setting); } void updateAutoWeaponButton(bool setting) { if (autoWeaponBtn != nullptr) autoWeaponBtn->select(setting); } // dialog appfuncs APPFUNC(cmdDialogQuit) { gWindow *win; requestInfo *ri; if (ev.panel && isUserAction(ev) && ev.value) { win = ev.panel->getWindow(); // get the window pointer ri = win ? (requestInfo *)win->_userData : nullptr; if (ri) { ri->running = 0; ri->result = ev.panel->_id; } } } APPFUNC(cmdFileSave) { gWindow *win; requestInfo *ri; if (ev.panel && isUserAction(ev) && ev.value) { // now close the window win = ev.panel->getWindow(); // get the window pointer ri = win ? (requestInfo *)win->_userData : nullptr; if (ri) { ri->running = 0; ri->result = typeSave; //ev.panel->id; } textBox->keepChanges(); // get index of the game int8 saveIndex = textBox->getIndex(); #ifndef IMMEDIATE_SAVE // save game g_vm->saveGameState(saveIndex, textBox->getLine(saveIndex), false); #else deferredLoadID = saveIndex; deferredSaveFlag = true; Common::strcpy_s(deferredSaveName, textBox->getLine(saveIndex)); #endif } } APPFUNC(cmdFileLoad) { gWindow *win; requestInfo *ri; SaveFileHeader header; int saveNo; if (ev.panel && isUserAction(ev) && ev.value) { // get the file index saveNo = textBox->getIndex(); if (getSaveName(saveNo, header)) { // close window win = ev.panel->getWindow(); // get the window pointer ri = win ? (requestInfo *)win->_userData : nullptr; if (ri) { ri->running = 0; ri->result = typeLoad; //ev.panel->id; } deferredLoadID = saveNo; deferredLoadFlag = true; } } } APPFUNC(cmdSaveDialogUp) { if (ev.panel && isUserAction(ev) && ev.value) { if (textBox) { textBox->scrollUp(); } } } APPFUNC(cmdSaveDialogDown) { if (ev.panel && isUserAction(ev) && ev.value) { if (textBox) { textBox->scrollDown(); } } } APPFUNCV(cmdTextResponse) { } APPFUNC(cmdOptionsSaveGame) { if (ev.panel && isUserAction(ev) && ev.value) { FileDialog(typeSave); } } APPFUNC(cmdOptionsNewGame) { if (ev.panel && isUserAction(ev) && ev.value) { gWindow *win; requestInfo *ri; win = ev.panel->getWindow(); // get the window pointer ri = win ? (requestInfo *)win->_userData : nullptr; if (ri) { ri->running = 0; ri->result = ev.panel->_id; deferredLoadID = 999; deferredLoadFlag = true; } } } APPFUNC(cmdOptionsLoadGame) { gWindow *win; requestInfo *ri; if (ev.panel && isUserAction(ev) && ev.value) { // if the fileDialog actually did loading if (FileDialog(typeLoad) == typeLoad) { win = ev.panel->getWindow(); // get the window pointer ri = win ? (requestInfo *)win->_userData : nullptr; if (ri) { ri->running = 0; ri->result = ev.panel->_id; } } } } APPFUNC(cmdQuitGame) { gWindow *win; requestInfo *ri; if (ev.panel && isUserAction(ev) && ev.value) { win = ev.panel->getWindow(); // get the window pointer ri = win ? (requestInfo *)win->_userData : nullptr; if (ri && userDialog( VFYX_DIALOG_NAME, VFYX_DIALOG_CAPTION, VFYX_DIALOG_BUTTON1, VFYX_DIALOG_BUTTON2, nullptr) == 0) { endGame(); ri->running = false; ri->result = ev.panel->_id; } } } inline int16 quantizedVolume(uint16 trueVolume) { int16 quantized = trueVolume & 0xFFF8; quantized += (quantized / 16); quantized = CLIP(quantized, (int16)0, (int16)255); return quantized; } APPFUNC(cmdCredits) { if (ev.panel && isUserAction(ev) && ev.value) { //reDrawScreen(); openBook(resImports->reserved[0]); } } APPFUNC(cmdAutoAggression) { if (isUserAction(ev)) { toggleAutoAggression(); } } APPFUNC(cmdAutoWeapon) { if (isUserAction(ev)) { toggleAutoWeapon(); } } APPFUNC(cmdNight) { if (isUserAction(ev)) { g_vm->_showNight = !g_vm->_showNight; nightBtn->select(g_vm->_showNight); } } APPFUNC(cmdSpeechText) { if (isUserAction(ev)) { g_vm->_speechText = !g_vm->_speechText; speechTextBtn->select(g_vm->_speechText); ConfMan.setBool("subtitles", g_vm->_speechText); } } void volumeChanged(); // Set music volume APPFUNC(cmdSetMIDIVolume) { int16 v = quantizedVolume(ev.value); ConfMan.setInt("music_volume", v); g_vm->syncSoundSettings(); volumeChanged(); } // Set DIG volume for DINO APPFUNC(cmdSetDIGVolume) { int16 v = quantizedVolume(ev.value); ConfMan.setInt("music_volume", v); g_vm->syncSoundSettings(); volumeChanged(); } // Set DIG speech volume for FTA APPFUNC(cmdSetSpeechVolume) { int16 v = quantizedVolume(ev.value); ConfMan.setInt("speech_volume", v); g_vm->syncSoundSettings(); volumeChanged(); } // Set DIG sound volume for FTA APPFUNC(cmdSetSoundVolume) { int16 v = quantizedVolume(ev.value); ConfMan.setInt("sfx_volume", v); g_vm->syncSoundSettings(); volumeChanged(); } // Save volume settings // This should be called when exiting the dialog to save the changes APPFUNCV(cmdSaveVolumeSettings) { g_vm->saveConfig(); } } // end of namespace Saga2