mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-30 14:14:43 +00:00
88913c0139
This tries to make our code a bit more compliant with our code formatting conventions. For future use, this is the command I used: git ls-files "*.cpp" "*.h" | xargs sed -i -e 's/[ \t]*$//'
561 lines
13 KiB
C++
561 lines
13 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 2
|
|
* 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
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
*/
|
|
|
|
#include "m4/dialogs.h"
|
|
#include "common/file.h"
|
|
#include "common/textconsole.h"
|
|
|
|
namespace M4 {
|
|
|
|
static void strToUpper(char *s) {
|
|
while (*s) {
|
|
*s = toupper(*s);
|
|
++s;
|
|
}
|
|
}
|
|
|
|
static void strToLower(char *s) {
|
|
while (*s) {
|
|
*s = tolower(*s);
|
|
++s;
|
|
}
|
|
}
|
|
|
|
const RGB8 DIALOG_PALETTE[8] = {
|
|
{0x80, 0x80, 0x80}, {0x90, 0x90, 0x90}, {0x70, 0x70, 0x70}, {0x9c, 0x9c, 0x9c},
|
|
{0x80, 0x80, 0x80}, {0x90, 0x90, 0x90}, {0xDC, 0xDC, 0xDC}, {0x00, 0x00, 0x00}
|
|
};
|
|
|
|
#define ROR16(v,amt) (((uint16)(v) >> amt) | ((uint16)(v) << (16 - amt)))
|
|
|
|
const int DIALOG_SPACING = 1;
|
|
|
|
/**
|
|
* Handles any dialog initialisation
|
|
*/
|
|
void Dialog::initDialog() {
|
|
incLine();
|
|
}
|
|
|
|
/**
|
|
* Adds a new line to the dialog output
|
|
*/
|
|
void Dialog::incLine() {
|
|
_lineX = 0;
|
|
_widthX = 0;
|
|
|
|
_lines.push_back(*new DialogLine());
|
|
assert(_lines.size() <= 20);
|
|
}
|
|
|
|
/**
|
|
* Writes some text to the dialog output, taking care of word wrapping if the text size
|
|
* exceeds the dialog's width
|
|
*/
|
|
void Dialog::writeChars(const char *srcLine) {
|
|
char wordStr[80];
|
|
char line[80];
|
|
int lineLen, lineWidth;
|
|
const char *srcP = srcLine;
|
|
|
|
while (*srcP) {
|
|
bool wordEndedP = false, newlineP = false;
|
|
char *destP = &wordStr[0];
|
|
Common::set_to(&wordStr[0], &wordStr[80], 0);
|
|
|
|
// Try and get the next word
|
|
for (;;) {
|
|
char v = *srcP;
|
|
*destP++ = v;
|
|
|
|
if (v == '\0') break;
|
|
if (v == '\n') {
|
|
newlineP = true;
|
|
++srcP;
|
|
--destP;
|
|
break;
|
|
}
|
|
|
|
if (v == ' ') {
|
|
// Word separator
|
|
++srcP;
|
|
--destP;
|
|
wordEndedP = true;
|
|
} else {
|
|
// Standard character
|
|
if (!wordEndedP)
|
|
// Still in the initial word
|
|
++srcP;
|
|
else {
|
|
// First character of next word, so time to break
|
|
--destP;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (destP < &wordStr[0])
|
|
destP = &wordStr[0];
|
|
*destP = '\0';
|
|
|
|
lineLen = strlen(wordStr);
|
|
|
|
strcpy(line, "");
|
|
if (_lineX > 0)
|
|
strcat(line, " ");
|
|
strcat(line, wordStr);
|
|
|
|
lineLen = strlen(line);
|
|
lineWidth = _vm->_font->current()->getWidth(line, DIALOG_SPACING);
|
|
|
|
if (((_lineX + lineLen) > _widthChars) || ((_widthX + lineWidth) > _dialogWidth)) {
|
|
incLine();
|
|
appendText(wordStr);
|
|
} else {
|
|
appendText(line);
|
|
}
|
|
|
|
if (newlineP)
|
|
incLine();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Appends some text to the current dialog line
|
|
*/
|
|
void Dialog::appendText(const char *line) {
|
|
_lineX += strlen(line);
|
|
_widthX += _vm->_font->current()->getWidth(line, DIALOG_SPACING);
|
|
|
|
strcat(_lines[_lines.size() - 1].data, line);
|
|
}
|
|
|
|
/**
|
|
* Adds a line of text to the dialog lines list
|
|
*/
|
|
void Dialog::addLine(const char *line, bool underlineP) {
|
|
if ((_widthX > 0) || (_lineX > 0))
|
|
incLine();
|
|
|
|
int lineWidth = _vm->_font->current()->getWidth(line, DIALOG_SPACING);
|
|
int lineLen = strlen(line);
|
|
|
|
if ((lineWidth > _dialogWidth) || (lineLen >= _widthChars))
|
|
writeChars(line);
|
|
else {
|
|
_lines[_lines.size() - 1].xp = (_dialogWidth - 10 - lineWidth) / 2;
|
|
strcpy(_lines[_lines.size() - 1].data, line);
|
|
}
|
|
|
|
if (underlineP)
|
|
_lines[_lines.size() - 1].underline = true;
|
|
|
|
incLine();
|
|
}
|
|
|
|
/**
|
|
* Adds a bar separation line to the dialog lines list
|
|
*/
|
|
void Dialog::addBarLine() {
|
|
if ((_widthX > 0) || (_lineX > 0))
|
|
incLine();
|
|
|
|
// Flag the line as being a bar separator
|
|
_lines[_lines.size() - 1].barLine = true;
|
|
incLine();
|
|
}
|
|
|
|
/**
|
|
* Retrieves a specified vocab entry
|
|
*/
|
|
void Dialog::getVocab(int vocabId, char **line) {
|
|
assert(vocabId > 0);
|
|
const char *vocabStr = _madsVm->globals()->getVocab(vocabId);
|
|
strcpy(*line, vocabStr);
|
|
|
|
if (_commandCase)
|
|
strToUpper(*line);
|
|
else
|
|
strToLower(*line);
|
|
|
|
// Move the string pointer to after the added string
|
|
while (!**line)
|
|
++*line;
|
|
}
|
|
|
|
bool Dialog::handleNounSuffix(char *destP, int nounNum, const char *srcP) {
|
|
char srcLine[40];
|
|
|
|
// The next source character must be a colon in front of the first verb
|
|
if (*srcP != ':')
|
|
return false;
|
|
|
|
// Copy the remainder of the line into a temporary buffer to get the seperate verbs
|
|
strcpy(srcLine, ++srcP);
|
|
char *altP = strchr(srcLine, ':');
|
|
if (altP)
|
|
*altP = '\0';
|
|
|
|
if (*srcP != '\0') {
|
|
while (*srcP != ':') {
|
|
++srcP;
|
|
if (!*srcP) break;
|
|
}
|
|
}
|
|
|
|
if (*srcP != '\0')
|
|
++srcP;
|
|
|
|
//
|
|
char var_FC[40];
|
|
char tempLine[40];
|
|
strcpy(var_FC, srcP);
|
|
char *tmpP = &tempLine[0];
|
|
char *tmp2P = tmpP;
|
|
|
|
uint16 _vocabIds[2] = {1, 1}; // FIXME/TODO: Proper vocab ids
|
|
getVocab(_vocabIds[nounNum], &tmpP);
|
|
|
|
if ((*(tmpP - 1) != 'S') && (*(tmpP - 1) != 's')) {
|
|
// Singular object
|
|
tmpP = &var_FC[0];
|
|
} else if (!strcmp(tempLine, "a ")) {
|
|
// Pontially plural
|
|
char ch = tolower(*tmp2P);
|
|
|
|
if (!((ch > 'U') || ((ch != 'A') && (ch != 'E') && (ch != 'I') && (ch != 'O'))))
|
|
strcpy(tempLine, "an ");
|
|
}
|
|
|
|
strcpy(destP, tmpP);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Sets up an area within the dialog for textual input
|
|
*/
|
|
void Dialog::setupInputArea() {
|
|
_askPosition.x = _lineX + 1;
|
|
_askPosition.y = _lines.size();
|
|
|
|
incLine();
|
|
}
|
|
|
|
/**
|
|
* Checks whether the start of an extracted command matches a specified given command constant
|
|
*/
|
|
bool Dialog::matchCommand(const char *s1, const char *s2) {
|
|
bool result = scumm_strnicmp(s1, s2, strlen(s2)) == 0;
|
|
_commandCase = isupper(*s1);
|
|
return result;
|
|
}
|
|
|
|
Dialog::Dialog(MadsM4Engine *vm, const char *msgData, const char *title): View(vm, Common::Rect(0, 0, 0, 0)) {
|
|
assert(msgData);
|
|
_vm->_font->setFont(FONT_INTERFACE_MADS);
|
|
|
|
const char *srcP = msgData;
|
|
bool skipLine = false;
|
|
bool initFlag = false;
|
|
bool cmdFlag = false;
|
|
bool crFlag = false;
|
|
bool underline = false;
|
|
|
|
_screenType = LAYER_DIALOG;
|
|
_widthChars = 0;
|
|
_dialogIndex = 0;
|
|
_askPosition.x = 0;
|
|
_askPosition.y = 0;
|
|
_lineX = 0;
|
|
_widthX = 0;
|
|
_dialogWidth = 0;
|
|
_commandCase = false;
|
|
|
|
char dialogLine[256];
|
|
char cmdText[80];
|
|
char *lineP = &dialogLine[0];
|
|
char *cmdP = NULL;
|
|
|
|
while (srcP && *(srcP - 1) != '\0') {
|
|
if ((*srcP == '\n') || (*srcP == '\0')) {
|
|
// Line completed
|
|
*lineP = '\0';
|
|
++srcP;
|
|
|
|
if (!initFlag) {
|
|
initDialog();
|
|
initFlag = true;
|
|
}
|
|
|
|
if (!skipLine)
|
|
writeChars(dialogLine);
|
|
else {
|
|
addLine(dialogLine, underline);
|
|
|
|
if (crFlag)
|
|
incLine();
|
|
}
|
|
|
|
// Clear the current line contents
|
|
dialogLine[0] = '\0';
|
|
lineP = &dialogLine[0];
|
|
skipLine = crFlag = underline = false;
|
|
continue;
|
|
|
|
} else if (*srcP == '[') {
|
|
// Start of a command sequence
|
|
cmdFlag = true;
|
|
cmdP = &cmdText[0];
|
|
++srcP;
|
|
continue;
|
|
} else if (*srcP == ']') {
|
|
// End of a command sequence
|
|
*cmdP = '\0';
|
|
cmdFlag = false;
|
|
strToUpper(cmdText);
|
|
|
|
if (matchCommand(cmdText, "ASK")) {
|
|
setupInputArea();
|
|
|
|
} else if (matchCommand(cmdText, "BAR")) {
|
|
// Adds a full-width line instead of normal text
|
|
addBarLine();
|
|
|
|
} else if (matchCommand(cmdText, "CENTER")) {
|
|
// Center command
|
|
skipLine = true;
|
|
|
|
} else if (matchCommand(cmdText, "CR")) {
|
|
// CR command
|
|
if (skipLine)
|
|
crFlag = true;
|
|
else if (!initFlag) {
|
|
initDialog();
|
|
initFlag = true;
|
|
}
|
|
|
|
} else if (matchCommand(cmdText, "NOUN1")) {
|
|
// Noun command 1
|
|
handleNounSuffix(lineP, 1, cmdText + 5);
|
|
|
|
} else if (matchCommand(cmdText, "NOUN2")) {
|
|
// Noun command 2
|
|
handleNounSuffix(lineP, 2, cmdText + 5);
|
|
|
|
} else if (matchCommand(cmdText, "SENTENCE")) {
|
|
// Sentence command - loads the title into the line buffer
|
|
strcpy(dialogLine, title);
|
|
strToUpper(dialogLine);
|
|
lineP += strlen(dialogLine) + 1;
|
|
|
|
} else if (matchCommand(cmdText, "TAB")) {
|
|
// Specifies the X offset for the current line
|
|
_lines[_lines.size() - 1].xp = atoi(cmdText + 3);
|
|
|
|
} else if (matchCommand(cmdText, "TITLE")) {
|
|
// Title command - specifies the dialog width in number of characters
|
|
skipLine = true;
|
|
crFlag = true;
|
|
underline = true;
|
|
|
|
int id = atoi(cmdText + 5);
|
|
if (id > 0) {
|
|
// Suffix provided - specifies the dialog width in number of chars
|
|
_widthChars = id * 2;
|
|
_dialogWidth = id * (_vm->_font->current()->getMaxWidth() + DIALOG_SPACING) + 10;
|
|
}
|
|
|
|
} else if (matchCommand(cmdText, "UNDER")) {
|
|
// Underline command
|
|
underline = true;
|
|
|
|
} else if (matchCommand(cmdText, "VERB")) {
|
|
// Verb/vocab retrieval
|
|
int verbId = 1; // TODO: Get correct vocab
|
|
getVocab(verbId, &lineP);
|
|
|
|
|
|
} else if (matchCommand(cmdText, "INDEX")) {
|
|
// Index command
|
|
_dialogIndex = atoi(cmdText + 5);
|
|
} else {
|
|
error("Unknown dialog command '%s' encountered", cmdText);
|
|
}
|
|
}
|
|
|
|
*lineP++ = *srcP;
|
|
if (cmdFlag)
|
|
*cmdP++ = *srcP;
|
|
++srcP;
|
|
}
|
|
|
|
draw();
|
|
}
|
|
|
|
Dialog::Dialog(MadsM4Engine *vm, int widthChars): View(vm, Common::Rect(0, 0, 0, 0)) {
|
|
_vm->_font->setFont(FONT_INTERFACE_MADS);
|
|
_widthChars = widthChars * 2;
|
|
_dialogWidth = widthChars * (_vm->_font->current()->getMaxWidth() + DIALOG_SPACING) + 10;
|
|
_screenType = LAYER_DIALOG;
|
|
_lineX = 0;
|
|
_widthX = 0;
|
|
_askPosition.x = 0;
|
|
_askPosition.y = 0;
|
|
}
|
|
|
|
Dialog::~Dialog() {
|
|
_vm->_palette->deleteRange(_palette);
|
|
delete _palette;
|
|
}
|
|
|
|
void Dialog::draw() {
|
|
assert(_widthChars != 0);
|
|
|
|
// Set up the palette for this view
|
|
_palette = new RGBList(8, NULL);
|
|
_palette->setRange(0, 8, DIALOG_PALETTE);
|
|
_vm->_palette->addRange(_palette);
|
|
|
|
// Calculate bounds
|
|
int dlgWidth = _dialogWidth;
|
|
int dlgHeight = _lines.size() * (_vm->_font->current()->getHeight() + 1) + 10;
|
|
int dialogX = (_vm->_screen->width() - dlgWidth) / 2;
|
|
int dialogY = (_vm->_screen->height() - dlgHeight) / 2;
|
|
|
|
// Create the surface for the dialog
|
|
create(dlgWidth, dlgHeight, Graphics::PixelFormat::createFormatCLUT8());
|
|
_coords.left = dialogX;
|
|
_coords.top = dialogY;
|
|
_coords.right = dialogX + dlgWidth + 1;
|
|
_coords.bottom = dialogY + dlgHeight + 1;
|
|
|
|
// Set up the dialog
|
|
fillRect(Common::Rect(0, 0, width(), height()), 3);
|
|
setColor(2);
|
|
hLine(1, width() - 1, height() - 2); // Bottom edge
|
|
hLine(0, width(), height() - 1);
|
|
vLine(width() - 2, 2, height()); // Right edge
|
|
vLine(width() - 1, 1, height());
|
|
|
|
// Render dialog interior
|
|
uint16 seed = 0xb78e;
|
|
for (int yp = 2; yp < (height() - 2); ++yp) {
|
|
byte *destP = this->getBasePtr(2, yp);
|
|
|
|
for (int xp = 2; xp < (width() - 2); ++xp) {
|
|
// Adjust the random seed
|
|
uint16 v = seed;
|
|
seed += 0x181D;
|
|
v = ROR16(v, 9);
|
|
seed = (seed ^ v) + ROR16(v, 3);
|
|
|
|
*destP++ = ((seed & 0x10) != 0) ? 1 : 0;
|
|
}
|
|
}
|
|
|
|
// If an ask position is set, create the input area frame
|
|
if (_askPosition.y > 0) {
|
|
|
|
}
|
|
|
|
// Handle drawing the text contents
|
|
_vm->_font->current()->setColors(7, 7, 7);
|
|
setColor(7);
|
|
|
|
for (uint lineCtr = 0, yp = 5; lineCtr < _lines.size(); ++lineCtr, yp += _vm->_font->current()->getHeight() + 1) {
|
|
|
|
if (_lines[lineCtr].barLine) {
|
|
// Bar separation line
|
|
hLine(5, width() - 6, ((_vm->_font->current()->getHeight() + 1) >> 1) + yp);
|
|
} else {
|
|
// Standard line
|
|
Common::Point pt(_lines[lineCtr].xp + 5, yp);
|
|
if (_lines[lineCtr].xp & 0x40)
|
|
++pt.y;
|
|
|
|
_vm->_font->current()->writeString(this, _lines[lineCtr].data, pt.x, pt.y, 0, DIALOG_SPACING);
|
|
|
|
if (_lines[lineCtr].underline)
|
|
// Underline needed
|
|
hLine(pt.x, pt.x + _vm->_font->current()->getWidth(_lines[lineCtr].data, DIALOG_SPACING),
|
|
pt.y + _vm->_font->current()->getHeight());
|
|
}
|
|
}
|
|
|
|
// Do final translation of the dialog to game palette
|
|
this->translate(_palette);
|
|
}
|
|
|
|
bool Dialog::onEvent(M4EventType eventType, int32 param1, int x, int y, bool &captureEvents) {
|
|
if (_vm->_mouse->getCursorNum() != CURSOR_ARROW)
|
|
_vm->_mouse->setCursorNum(CURSOR_ARROW);
|
|
|
|
captureEvents = true;
|
|
|
|
if (eventType == MEVENT_LEFT_CLICK) {
|
|
captureEvents = false;
|
|
_vm->_viewManager->deleteView(this);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void Dialog::display(MadsM4Engine *vm, int widthChars, const char **descEntries) {
|
|
Dialog *dlg = new Dialog(vm, widthChars);
|
|
|
|
while (*descEntries != NULL) {
|
|
dlg->incLine();
|
|
dlg->writeChars(*descEntries);
|
|
|
|
int lineWidth = vm->_font->current()->getWidth(*descEntries, DIALOG_SPACING);
|
|
dlg->_lines[dlg->_lines.size() - 1].xp = (dlg->_dialogWidth - 10 - lineWidth) / 2;
|
|
++descEntries;
|
|
}
|
|
|
|
dlg->_lines[0].underline = true;
|
|
|
|
dlg->draw();
|
|
vm->_viewManager->addView(dlg);
|
|
vm->_viewManager->moveToFront(dlg);
|
|
}
|
|
|
|
void Dialog::getValue(MadsM4Engine *vm, const char *title, const char *text, int numChars, int currentValue) {
|
|
int titleLen = strlen(title);
|
|
Dialog *dlg = new Dialog(vm, titleLen + 4);
|
|
|
|
dlg->addLine(title, true);
|
|
dlg->writeChars("\n");
|
|
|
|
dlg->writeChars(text);
|
|
dlg->setupInputArea();
|
|
dlg->writeChars("\n");
|
|
|
|
dlg->draw();
|
|
vm->_viewManager->addView(dlg);
|
|
vm->_viewManager->moveToFront(dlg);
|
|
|
|
// TODO: How to wait until the dialog is closed
|
|
|
|
}
|
|
|
|
} // End of namespace M4
|