scummvm/engines/saga2/uidialog.cpp
2022-10-29 23:46:00 +02:00

1844 lines
41 KiB
C++

/* 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