Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

View File

@@ -0,0 +1,130 @@
/* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "mm/xeen/dialogs/credits_screen.h"
#include "mm/xeen/resources.h"
#include "mm/xeen/xeen.h"
namespace MM {
namespace Xeen {
void CreditsScreen::show(XeenEngine *vm) {
CreditsScreen *dlg = new CreditsScreen(vm);
switch (vm->getGameID()) {
case GType_Clouds:
dlg->execute(Res.CLOUDS_CREDITS);
break;
case GType_Swords:
dlg->execute(Res.SWORDS_CREDITS1);
dlg->execute(Res.SWORDS_CREDITS2);
break;
default:
dlg->execute(Res.DARK_SIDE_CREDITS);
break;
}
delete dlg;
}
void CreditsScreen::execute(const char *content) {
Screen &screen = *_vm->_screen;
Windows &windows = *_vm->_windows;
EventsManager &events = *_vm->_events;
// Handle drawing the credits screen
doScroll(true, false);
windows[GAME_WINDOW].close();
screen.loadBackground("marb.raw");
Common::String ttsMessage;
windows[0].writeString(content, false, &ttsMessage);
doScroll(false, false);
#ifdef USE_TTS
speakText(ttsMessage, (_vm->getGameID() != GType_Swords || content == Res.SWORDS_CREDITS1));
#endif
events.setCursor(0);
windows[0].update();
clearButtons();
// Wait for keypress
while (!_vm->shouldExit() && !events.isKeyMousePressed())
events.pollEventsAndWait();
#ifdef USE_TTS
_vm->stopTextToSpeech();
#endif
doScroll(true, false);
}
#ifdef USE_TTS
void CreditsScreen::speakText(const Common::String &text, bool firstCreditsScreen) const {
if (_vm->getGameID() == GType_Swords && firstCreditsScreen) {
uint index = 0;
// Developed/published by
_vm->sayText(getNextTextSection(text, index, 2));
// Next four headers are separate from their corresponding credits. First get the headers, then voice the person
// for the first header, and then voice the second header and second person
for (uint8 i = 0; i < 2; ++i) {
_vm->sayText(getNextTextSection(text, index));
Common::String nextHeader = getNextTextSection(text, index);
_vm->sayText(getNextTextSection(text, index));
_vm->sayText(nextHeader);
_vm->sayText(getNextTextSection(text, index));
}
// Same as first four headers, but with two people at a time instead of one
for (uint8 i = 0; i < 2; ++i) {
// First two headers
_vm->sayText(getNextTextSection(text, index));
Common::String nextHeader = getNextTextSection(text, index);
// First people listed
_vm->sayText(getNextTextSection(text, index));
Common::String nextHeaderPerson = getNextTextSection(text, index);
// Second person listed under first header
_vm->sayText(getNextTextSection(text, index));
// Next header
_vm->sayText(nextHeader);
_vm->sayText(nextHeaderPerson);
// Second person for second header
if (i == 0) {
_vm->sayText(getNextTextSection(text, index));
} else { // Last header has more than two people
_vm->sayText(text.substr(index));
}
}
} else {
_vm->sayText(text);
}
}
#endif
} // End of namespace Xeen
} // End of namespace MM

View File

@@ -0,0 +1,46 @@
/* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef XEEN_DIALOGS_CREDITS_SCREEN_H
#define XEEN_DIALOGS_CREDITS_SCREEN_H
#include "mm/xeen/dialogs/dialogs.h"
namespace MM {
namespace Xeen {
class CreditsScreen: public ButtonContainer {
private:
CreditsScreen(XeenEngine *vm) : ButtonContainer(vm) {}
void execute(const char *content);
#ifdef USE_TTS
void speakText(const Common::String &text, bool firstCreditsScreen) const;
#endif
public:
static void show(XeenEngine *vm);
};
} // End of namespace Xeen
} // End of namespace MM
#endif

View File

@@ -0,0 +1,343 @@
/* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/scummsys.h"
#include "mm/xeen/dialogs/dialogs.h"
#include "mm/xeen/events.h"
#include "mm/xeen/resources.h"
#include "mm/xeen/screen.h"
#include "mm/xeen/xeen.h"
namespace MM {
namespace Xeen {
void ButtonContainer::saveButtons() {
_savedButtons.push(_buttons);
clearButtons();
}
/*
* Clears the current list of defined buttons
*/
void ButtonContainer::clearButtons() {
_buttons.clear();
}
void ButtonContainer::restoreButtons() {
_buttons = _savedButtons.pop();
}
void ButtonContainer::addButton(const Common::Rect &bounds, int val,
SpriteResource *sprites, uint8 ttsIndex) {
_buttons.push_back(UIButton(bounds, val, _buttons.size() * 2, sprites, sprites != nullptr, ttsIndex, ttsIndex < UINT8_MAX));
}
void ButtonContainer::addButton(const Common::Rect &bounds, int val,
int frameNum, SpriteResource *sprites, uint8 ttsIndex) {
_buttons.push_back(UIButton(bounds, val, frameNum, sprites, sprites != nullptr, ttsIndex, ttsIndex < UINT8_MAX));
}
void ButtonContainer::addPartyButtons(XeenEngine *vm) {
for (uint idx = 0; idx < MAX_ACTIVE_PARTY; ++idx) {
addButton(Common::Rect(Res.CHAR_FACES_X[idx], 150, Res.CHAR_FACES_X[idx] + 32, 182),
Common::KEYCODE_F1 + idx);
}
}
bool ButtonContainer::checkEvents(XeenEngine *vm) {
EventsManager &events = *vm->_events;
Party &party = *vm->_party;
Windows &windows = *_vm->_windows;
PendingEvent event;
_buttonValue = 0;
#ifdef USE_TTS
bool buttonKeyboardPressed = false;
checkHoverOverButton();
#endif
if (events.getEvent(event)) {
if (event._leftButton) {
Common::Point pt = events._mousePos;
// Check for party member glyphs being clicked
Common::Rect r(0, 0, 32, 32);
for (uint idx = 0; idx < party._activeParty.size(); ++idx) {
r.moveTo(Res.CHAR_FACES_X[idx], 150);
if (r.contains(pt)) {
_buttonValue = Common::KEYCODE_F1 + idx;
break;
}
}
// Check whether any button is selected
for (uint i = 0; i < _buttons.size(); ++i) {
if (_buttons[i]._bounds.contains(pt) && _buttons[i]._value) {
events.debounceMouse();
_buttonValue = _buttons[i]._value;
break;
}
}
if (!_buttonValue && _waitBounds.contains(pt)) {
_buttonValue = Common::KEYCODE_SPACE;
return true;
}
} else if (event.isKeyboard()) {
const Common::KeyCode &keycode = event._keyState.keycode;
if (keycode == Common::KEYCODE_KP8)
_buttonValue = Common::KEYCODE_UP;
else if (keycode == Common::KEYCODE_KP2)
_buttonValue = Common::KEYCODE_DOWN;
else if (keycode == Common::KEYCODE_KP_ENTER)
_buttonValue = Common::KEYCODE_RETURN;
else if (keycode != Common::KEYCODE_LCTRL && keycode != Common::KEYCODE_RCTRL
&& keycode != Common::KEYCODE_LALT && keycode != Common::KEYCODE_RALT)
_buttonValue = keycode;
if (_buttonValue) {
_buttonValue |= (event._keyState.flags & ~Common::KBD_STICKY) << 16;
#ifdef USE_TTS
buttonKeyboardPressed = true;
#endif
}
}
}
if (_buttonValue) {
// Check for a button matching the selected _buttonValue
Window &win = windows[39];
for (uint btnIndex = 0; btnIndex < _buttons.size(); ++btnIndex) {
UIButton &btn = _buttons[btnIndex];
if (btn._value == _buttonValue) {
#ifdef USE_TTS
// Only voice the button's text if it was activated by keyboard press, to avoid voicing it
// every time the player clicks it
if (buttonKeyboardPressed && btn._canVoice && btn._ttsIndex < _buttonTexts.size()) {
_vm->sayText(_buttonTexts[btn._ttsIndex], Common::TextToSpeechManager::INTERRUPT);
}
#endif
if (btn._draw) {
// Found the correct button
// Draw button depressed
btn._sprites->draw(0, btn._selectedFrame, Common::Point(btn._bounds.left, btn._bounds.top));
win.setBounds(btn._bounds);
win.update();
// Slight delay
events.updateGameCounter();
events.wait(2);
// Redraw button in it's original non-depressed form
btn._sprites->draw(0, btn._frameNum, Common::Point(btn._bounds.left, btn._bounds.top));
win.setBounds(btn._bounds);
win.update();
break;
}
}
}
return true;
}
return false;
}
#ifdef USE_TTS
void ButtonContainer::checkHoverOverButton() {
if (g_vm->_mouseMoved) {
bool hoveringOverButton = false;
Common::Point pt = g_vm->_events->_mousePos;
for (uint i = 0; i < _buttons.size(); ++i) {
if (_buttons[i]._canVoice && _buttons[i]._ttsIndex < _buttonTexts.size() && _buttons[i]._bounds.contains(pt) && _buttons[i]._value) {
hoveringOverButton = true;
if (_previousButton != (int)i) {
_vm->sayText(_buttonTexts[_buttons[i]._ttsIndex], Common::TextToSpeechManager::INTERRUPT);
_previousButton = (int)i;
}
break;
}
}
if (!hoveringOverButton) {
_previousButton = -1;
}
g_vm->_mouseMoved = false;
}
}
#endif
void ButtonContainer::drawButtons(XSurface *surface) {
for (uint btnIndex = 0; btnIndex < _buttons.size(); ++btnIndex) {
UIButton &btn = _buttons[btnIndex];
if (btn._draw) {
assert(btn._sprites);
btn._sprites->draw(*surface, btn._frameNum,
Common::Point(btn._bounds.left, btn._bounds.top));
}
}
}
bool ButtonContainer::doScroll(bool rollUp, bool fadeIn) {
if (_vm->_files->_ccNum) {
return Cutscenes::doScroll(rollUp, fadeIn);
} else {
saveButtons();
clearButtons();
bool result = Cutscenes::doScroll(rollUp, fadeIn);
restoreButtons();
return result;
}
}
void ButtonContainer::loadStrings(const Common::Path &name) {
File f(name);
_textStrings.clear();
while (f.pos() < f.size())
_textStrings.push_back(f.readString());
f.close();
}
void ButtonContainer::loadStrings(const Common::Path &name, int ccMode) {
File f(name, ccMode);
_textStrings.clear();
while (f.pos() < f.size())
_textStrings.push_back(f.readString());
f.close();
}
void ButtonContainer::setWaitBounds() {
_waitBounds = Common::Rect(8, 8, 224, 140);
}
#ifdef USE_TTS
Common::String ButtonContainer::getNextTextSection(const Common::String &text, uint &index, uint count, const char *separator) const {
Common::String result;
for (uint i = 0; i < count; ++i) {
result += getNextTextSection(text, index) + separator;
if (index == Common::String::npos) {
break;
}
}
return result;
}
Common::String ButtonContainer::getNextTextSection(const Common::String &text, uint &index, uint count) const {
return getNextTextSection(text, index, count, "\n");
}
Common::String ButtonContainer::getNextTextSection(const Common::String &text, uint &index) const {
Common::String result;
index = text.findFirstNotOf('\n', index);
if (index == Common::String::npos) {
return result;
}
uint endIndex = text.find('\n', index + 1);
if (endIndex == Common::String::npos) {
result = text.substr(index);
index = endIndex;
return result;
}
result = text.substr(index, endIndex - index);
index = endIndex + 1;
return result;
}
Common::String ButtonContainer::addNextTextToButtons(const Common::String &text, uint &index) {
Common::String buttonText = getNextTextSection(text, index) + '\n';
_buttonTexts.push_back(buttonText);
return buttonText;
}
Common::String ButtonContainer::addNextTextToButtons(const Common::String &text, uint &index, uint count) {
Common::String result;
for (uint i = 0; i < count; ++i) {
result += addNextTextToButtons(text, index);
if (index == Common::String::npos) {
break;
}
}
return result;
}
void ButtonContainer::setButtonTexts(const Common::String &text) {
_buttonTexts.clear();
uint index = 0;
for (uint i = 0; i < _buttons.size(); ++i) {
if (_buttons[i]._value) {
_buttonTexts.push_back(getNextTextSection(text, index));
} else {
_buttonTexts.push_back("");
}
if (index == Common::String::npos) {
break;
}
}
}
void ButtonContainer::disableButtonVoicing(uint startIndex, uint endIndex) {
for (uint i = startIndex; i < endIndex; ++i) {
if (i < _buttons.size()) {
_buttons[i]._canVoice = false;
}
}
}
void ButtonContainer::enableButtonVoicing(uint startIndex, uint endIndex) {
for (uint i = startIndex; i < endIndex; ++i) {
if (i < _buttons.size()) {
_buttons[i]._canVoice = true;
}
}
}
#endif
/*------------------------------------------------------------------------*/
void SettingsBaseDialog::showContents(SpriteResource &title1, bool waitFlag) {
_vm->_events->pollEventsAndWait();
checkEvents(_vm);
}
} // End of namespace Xeen
} // End of namespace MM

View File

@@ -0,0 +1,248 @@
/* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef XEEN_DIALOGS_H
#define XEEN_DIALOGS_H
#include "common/array.h"
#include "common/stack.h"
#include "common/rect.h"
#include "mm/xeen/cutscenes.h"
#include "mm/xeen/sprites.h"
#include "mm/shared/xeen/xsurface.h"
namespace MM {
namespace Xeen {
class XeenEngine;
class UIButton {
public:
Common::Rect _bounds;
SpriteResource *_sprites;
int _value;
uint _frameNum, _selectedFrame;
bool _draw;
#ifdef USE_TTS
uint8 _ttsIndex;
bool _canVoice;
#endif
/**
* Constructor
*/
UIButton(const Common::Rect &bounds, int value, uint frameNum, SpriteResource *sprites, bool draw, uint8 ttsIndex, bool ttsCanVoice) :
_bounds(bounds), _value(value), _frameNum(frameNum), _selectedFrame(frameNum | 1),
_sprites(sprites), _draw(draw) {
#ifdef USE_TTS
_ttsIndex = ttsIndex;
_canVoice = ttsCanVoice;
#endif
}
/**
* Constructor
*/
UIButton() : _value(0), _frameNum(0), _selectedFrame(0), _sprites(nullptr), _draw(false) {
#ifdef USE_TTS
_ttsIndex = 0;
_canVoice = false;
#endif
}
/**
* Set the frame
*/
void setFrame(uint frameNum) {
_frameNum = frameNum;
_selectedFrame = frameNum | 1;
}
/**
* Set the frame
*/
void setFrame(uint frameNum, uint selectedFrame) {
_frameNum = frameNum;
_selectedFrame = selectedFrame;
}
};
class ButtonContainer : public Cutscenes {
private:
Common::Stack< Common::Array<UIButton> > _savedButtons;
protected:
Common::Array<UIButton> _buttons;
Common::StringArray _textStrings;
Common::Rect _waitBounds;
int _buttonValue;
#ifdef USE_TTS
Common::StringArray _buttonTexts;
int _previousButton;
#endif
bool checkEvents(XeenEngine *vm);
/**
* Draws the scroll in the background
* @param rollUp If true, rolls up the scroll. If false, unrolls.
* @param fadeIn If true, does an initial fade in
* @returns True if key or mouse pressed
*/
bool doScroll(bool rollUp, bool fadeIn) override;
/**
* Load a set of text strings from the given resource
* @param name Name of resource containing strings
*/
void loadStrings(const Common::Path &name);
/**
* Load a set of text strings from the given resource
* @param name Name of resource containing strings
* @param ccMode Optional cc file number to explicitly use
*/
void loadStrings(const Common::Path &name, int ccMode);
/**
* Sets the wait bounds, the area where clicking with the mouse generates
* the equivalent of a space bar press, to the main interface area
*/
void setWaitBounds();
#ifdef USE_TTS
/**
* Reads one or more sections of text, then combines them
* @param text Text to take the sections from. Each section should be separated by one or more newlines
* @param index Starting index, which is moved to the start of the following section, or npos if no sections remain
* @param count How many sections to read
* @param separator What to separate sections with
* @returns The text sections combined, each separated by the separator
*/
Common::String getNextTextSection(const Common::String &text, uint &index, uint count, const char *separator) const;
/**
* Reads one or more sections of text, then combines them. Newlines are kept intact
* @param text Text to take the sections from. Each section should be separated by one or more newlines
* @param index Starting index, which is moved to the start of the following section, or npos if no sections remain
* @param count How many sections to read
* @returns The text sections combined
*/
Common::String getNextTextSection(const Common::String &text, uint &index, uint count) const;
/**
* Reads one section of text
* @param text Text to take the section from. Each section should be separated by one or more newlines
* @param index Starting index, which is moved to the start of the following section, or npos if no sections remain
* @returns The text section
*/
Common::String getNextTextSection(const Common::String &text, uint &index) const;
/**
* Reads one section of text and adds it to the button texts
* @param text Text to take the section from. Each section should be separated by one or more newlines
* @param index Starting index, which is moved to the start of the following section, or npos if no sections remain
* @returns The text section
*/
Common::String addNextTextToButtons(const Common::String &text, uint &index);
/**
* Reads one or more sections of text and adds them to the button texts
* @param text Text to take the sections from. Each section should be separated by one or more newlines
* @param index Starting index, which is moved to the start of the following section, or npos if no sections remain
* @param count How many sections to read
* @returns The text sections combined
*/
Common::String addNextTextToButtons(const Common::String &text, uint &index, uint count);
/**
* Sets the text of each button for use by TTS
* @param text Text for buttons. Each button's text should be separated by newlines
*/
void setButtonTexts(const Common::String &text);
/**
* Disables the voicing of buttons from the start index to the end index
* @param startIndex Starting index
* @param endIndex Ending index
*/
void disableButtonVoicing(uint startIndex, uint endIndex);
/**
* Enables the voicing of buttons from the start index to the end index
* @param startIndex Starting index
* @param endIndex Ending index
*/
void enableButtonVoicing(uint startIndex, uint endIndex);
#endif
public:
ButtonContainer(XeenEngine *vm) : Cutscenes(vm), _buttonValue(0) {
#ifdef USE_TTS
_previousButton = -1;
#endif
}
/**
* Saves the current list of buttons
*/
void saveButtons();
void clearButtons();
void restoreButtons();
void addButton(const Common::Rect &bounds, int val,
SpriteResource *sprites = nullptr, uint8 ttsIndex = UINT8_MAX);
void addButton(const Common::Rect &bounds, int val,
int frameNum, SpriteResource *sprites = nullptr, uint8 ttsIndex = UINT8_MAX);
void addPartyButtons(XeenEngine *vm);
/**
* Draws the buttons onto the passed surface
*/
void drawButtons(XSurface *surface);
/**
* Clears any currently set button value
*/
void clearEvents() { _buttonValue = 0; }
#ifdef USE_TTS
/**
* Checks if a button is being hovered over and voices its text with TTS, if there is text
*/
void checkHoverOverButton();
#endif
};
class SettingsBaseDialog : public ButtonContainer {
protected:
virtual void showContents(SpriteResource &title1, bool mode);
public:
SettingsBaseDialog(XeenEngine *vm) : ButtonContainer(vm) {}
~SettingsBaseDialog() override {}
};
} // End of namespace Xeen
} // End of namespace MM
#endif

View File

@@ -0,0 +1,145 @@
/* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "mm/xeen/dialogs/dialogs_awards.h"
#include "mm/xeen/party.h"
#include "mm/xeen/resources.h"
#include "mm/xeen/xeen.h"
namespace MM {
namespace Xeen {
enum AwardsButtonTTSTextIndex {
kAwardsUp = 0,
kAwardsDown = 1,
kAwardsExit = 2
};
void Awards::show(XeenEngine *vm, const Character *ch) {
Awards *dlg = new Awards(vm);
dlg->execute(ch);
delete dlg;
}
void Awards::execute(const Character *ch) {
EventsManager &events = *g_vm->_events;
Windows &windows = *g_vm->_windows;
Common::StringArray awards;
int numAwards;
Mode oldMode = g_vm->_mode;
int topIndex = 0;
loadStrings("award.bin", 1);
addButtons();
// Open the window and draw contents
bool win29Open = windows[29]._enabled;
if (!win29Open) {
windows[29].open();
windows[30].open();
}
Common::String buttonText;
windows[29].writeString(Res.AWARDS_TEXT, false, &buttonText);
drawButtons(&windows[0]);
#ifdef USE_TTS
setButtonTexts(buttonText);
#endif
while (!_vm->shouldExit()) {
// Build up a list of awards the character has
awards.clear();
awards.resize(AWARDS_TOTAL);
numAwards = 0;
for (int awardNum = 0; awardNum < AWARDS_TOTAL; ++awardNum) {
if (ch->hasAward(awardNum)) {
if (awardNum == WARZONE_AWARD) {
// # Warzone Wins
awards[numAwards] = Common::String::format(_textStrings[9].c_str(), ch->getAwardCount(WARZONE_AWARD));
} else if (awardNum == 17) {
// Legendary Race
awards[numAwards] = Common::String::format(_textStrings[17].c_str(),
Res.RACE_NAMES[ch->_race]);
} else {
awards[numAwards] = _textStrings[awardNum];
}
++numAwards;
}
}
// If no awards, add in a message indicating so
if (numAwards == 0) {
awards[1] = Res.NO_AWARDS;
}
Common::String msg = Common::String::format(Res.AWARDS_FOR,
ch->_name.c_str(), Res.CLASS_NAMES[ch->_class],
awards[topIndex].c_str(),
awards[topIndex + 1].c_str(),
awards[topIndex + 2].c_str(),
awards[topIndex + 3].c_str(),
awards[topIndex + 4].c_str(),
awards[topIndex + 5].c_str(),
awards[topIndex + 6].c_str(),
awards[topIndex + 7].c_str(),
awards[topIndex + 8].c_str()
);
windows[30].writeString(msg);
#ifdef USE_TTS
_vm->sayText(buttonText);
#endif
windows[24].update();
// Wait for keypress
do {
events.pollEventsAndWait();
checkEvents(_vm);
} while (!g_vm->shouldExit() && !_buttonValue);
if (_buttonValue == Common::KEYCODE_ESCAPE) {
break;
} else if (_buttonValue == Common::KEYCODE_u) {
topIndex = MAX(topIndex - 1, 0);
} else if (_buttonValue == Common::KEYCODE_d) {
if ((++topIndex + 9) > numAwards)
--topIndex;
}
}
// Close the window
if (!win29Open) {
windows[30].close();
windows[29].close();
}
g_vm->_mode = oldMode;
}
void Awards::addButtons() {
_iconSprites.load("award.icn");
addButton(Common::Rect(216, 109, 240, 129), Common::KEYCODE_u, &_iconSprites, kAwardsUp);
addButton(Common::Rect(250, 109, 274, 129), Common::KEYCODE_d, &_iconSprites, kAwardsDown);
addButton(Common::Rect(284, 109, 308, 129), Common::KEYCODE_ESCAPE, &_iconSprites, kAwardsExit);
}
} // End of namespace Xeen
} // End of namespace MM

View File

@@ -0,0 +1,53 @@
/* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef XEEN_DIALOGS_AWARDS_H
#define XEEN_DIALOGS_AWARDS_H
#include "mm/xeen/dialogs/dialogs.h"
#include "mm/xeen/character.h"
namespace MM {
namespace Xeen {
class Awards : public ButtonContainer {
private:
SpriteResource _iconSprites;
private:
Awards(XeenEngine *vm) : ButtonContainer(vm) {}
/**
* Executes the dialog
*/
void execute(const Character *ch);
/**
* Add buttons for the dialog
*/
void addButtons();
public:
static void show(XeenEngine *vm, const Character *ch);
};
} // End of namespace Xeen
} // End of namespace MM
#endif

View File

@@ -0,0 +1,735 @@
/* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "mm/xeen/dialogs/dialogs_awards.h"
#include "mm/xeen/dialogs/dialogs_char_info.h"
#include "mm/xeen/dialogs/dialogs_exchange.h"
#include "mm/xeen/dialogs/dialogs_items.h"
#include "mm/xeen/dialogs/dialogs_quick_ref.h"
#include "mm/xeen/resources.h"
#include "mm/xeen/xeen.h"
namespace MM {
namespace Xeen {
#ifdef USE_TTS
static const uint8 kCharacterInfoInformationCount = 20;
static const uint8 kCharacterInfoSideButtonCount = 4;
static const uint8 kCharacterInfoPartyGoldIndex = 7;
static const uint8 kCharacterInfoRowCount = 5;
static const uint8 kCharacterInfoColumnCount = 4;
#endif
enum CharacterInfoButtonTTSTextIndex {
kCharacterInfoItem = 0,
kCharacterInfoQuickRef = 1,
kCharacterInfoExchange = 2,
kCharacterInfoExit = 3,
kCharacterInfoMight = 4,
kCharacterInfoAccuracy = 5,
kCharacterInfoHP = 6,
kCharacterInfoExperience = 7,
kCharacterInfoIntellect = 8,
kCharacterInfoLuck = 9,
kCharacterInfoSP = 10,
kCharacterInfoPartyGold = 11,
kCharacterInfoPersonality = 12,
kCharacterInfoAge = 13,
kCharacterInfoResistances = 14,
kCharacterInfoPartyGems = 15,
kCharacterInfoEndurance = 16,
kCharacterInfoLevel = 17,
kCharacterInfoSkills = 18,
kCharacterInfoPartyFood = 19,
kCharacterInfoSpeed = 20,
kCharacterInfoAC = 21,
kCharacterInfoAwards = 22,
kCharacterInfoCondition = 23
};
void CharacterInfo::show(XeenEngine *vm, int charIndex) {
CharacterInfo *dlg = new CharacterInfo(vm);
dlg->execute(charIndex);
delete dlg;
}
void CharacterInfo::execute(int charIndex) {
Combat &combat = *_vm->_combat;
EventsManager &events = *_vm->_events;
Interface &intf = *_vm->_interface;
Party &party = *_vm->_party;
Windows &windows = *_vm->_windows;
bool redrawFlag = true;
Mode oldMode = _vm->_mode;
_vm->_mode = MODE_CHARACTER_INFO;
loadDrawStructs();
addButtons();
Character *c = (oldMode != MODE_COMBAT) ? &party._activeParty[charIndex] : combat._combatParty[charIndex];
intf.highlightChar(charIndex);
Window &w = windows[24];
w.open();
do {
if (redrawFlag) {
Common::String charDetails = loadCharacterDetails(*c);
Common::String ttsMessage;
w.writeString(Common::String::format(Res.CHARACTER_TEMPLATE, charDetails.c_str()), false, &ttsMessage);
#ifdef USE_TTS
speakText(ttsMessage);
#endif
w.drawList(_drawList, 24);
w.update();
redrawFlag = false;
}
// Wait for keypress, showing a blinking cursor
events.updateGameCounter();
bool cursorFlag = false;
_buttonValue = 0;
while (!_vm->shouldExit() && !_buttonValue) {
events.pollEventsAndWait();
if (events.timeElapsed() > 4) {
cursorFlag = !cursorFlag;
events.updateGameCounter();
}
showCursor(cursorFlag);
w.update();
checkEvents(_vm);
}
events.clearEvents();
if (Common::KEYCODE_F1 == _buttonValue ||
Common::KEYCODE_F2 == _buttonValue ||
Common::KEYCODE_F3 == _buttonValue ||
Common::KEYCODE_F4 == _buttonValue ||
Common::KEYCODE_F5 == _buttonValue ||
Common::KEYCODE_F6 == _buttonValue) {
_buttonValue -= Common::KEYCODE_F1;
if (_buttonValue < (int)(oldMode == MODE_COMBAT ? combat._combatParty.size() : party._activeParty.size())) {
charIndex = _buttonValue;
c = (oldMode != MODE_COMBAT) ? &party._activeParty[charIndex] : combat._combatParty[charIndex];
intf.highlightChar(_buttonValue);
redrawFlag = true;
} else {
_vm->_mode = MODE_CHARACTER_INFO;
}
} else if (Common::KEYCODE_UP == _buttonValue ||
Common::KEYCODE_KP8 == _buttonValue) {
if (_cursorCell > 0) {
showCursor(false);
--_cursorCell;
showCursor(true);
#ifdef USE_TTS
_vm->sayText(_buttonTexts[_buttons[_cursorCell]._ttsIndex], Common::TextToSpeechManager::INTERRUPT);
#endif
}
w.update();
} else if (Common::KEYCODE_DOWN == _buttonValue ||
Common::KEYCODE_KP2 == _buttonValue) {
if (_cursorCell < 20) {
showCursor(false);
++_cursorCell;
showCursor(true);
#ifdef USE_TTS
_vm->sayText(_buttonTexts[_buttons[_cursorCell]._ttsIndex], Common::TextToSpeechManager::INTERRUPT);
#endif
}
w.update();
} else if (Common::KEYCODE_LEFT == _buttonValue ||
Common::KEYCODE_KP4 == _buttonValue) {
if (_cursorCell >= 5) {
showCursor(false);
_cursorCell -= 5;
showCursor(true);
#ifdef USE_TTS
_vm->sayText(_buttonTexts[_buttons[_cursorCell]._ttsIndex], Common::TextToSpeechManager::INTERRUPT);
#endif
}
w.update();
} else if (Common::KEYCODE_RIGHT == _buttonValue ||
Common::KEYCODE_KP6 == _buttonValue) {
if (_cursorCell <= 15) {
showCursor(false);
_cursorCell += 5;
showCursor(true);
#ifdef USE_TTS
_vm->sayText(_buttonTexts[_buttons[_cursorCell]._ttsIndex], Common::TextToSpeechManager::INTERRUPT);
#endif
}
w.update();
} else if (1001 == _buttonValue ||
1002 == _buttonValue ||
1003 == _buttonValue ||
1004 == _buttonValue ||
1005 == _buttonValue ||
1006 == _buttonValue ||
1007 == _buttonValue ||
1008 == _buttonValue ||
1009 == _buttonValue ||
1010 == _buttonValue ||
1011 == _buttonValue ||
1012 == _buttonValue ||
1013 == _buttonValue ||
1014 == _buttonValue ||
1015 == _buttonValue ||
1016 == _buttonValue ||
1017 == _buttonValue ||
1018 == _buttonValue ||
1019 == _buttonValue ||
1020 == _buttonValue) {
showCursor(false);
_cursorCell = _buttonValue - 1001;
showCursor(true);
w.update();
bool result = expandStat(_cursorCell, *c);
_vm->_mode = MODE_COMBAT;
if (result)
redrawFlag = true;
} else if (Common::KEYCODE_RETURN == _buttonValue ||
Common::KEYCODE_KP_ENTER == _buttonValue) {
bool result = expandStat(_cursorCell, *c);
_vm->_mode = MODE_COMBAT;
if (result)
redrawFlag = true;
} else if (Res.KeyConstants.DialogsCharInfo.KEY_EXCHANGE == _buttonValue) {
if (oldMode == MODE_COMBAT) {
ErrorScroll::show(_vm, Res.EXCHANGING_IN_COMBAT, WT_FREEZE_WAIT);
} else {
_vm->_mode = oldMode;
ExchangeDialog::show(_vm, c, charIndex);
_vm->_mode = MODE_CHARACTER_INFO;
redrawFlag = true;
}
} else if (Res.KeyConstants.DialogsCharInfo.KEY_ITEM == _buttonValue) {
_vm->_mode = oldMode;
_vm->_combat->_itemFlag = _vm->_mode == MODE_COMBAT;
c = ItemsDialog::show(_vm, c, ITEMMODE_CHAR_INFO);
if (!c) {
party._stepped = true;
goto exit;
}
_vm->_mode = MODE_CHARACTER_INFO;
redrawFlag = true;
} else if (Res.KeyConstants.DialogsCharInfo.KEY_QUICK == _buttonValue) {
QuickReferenceDialog::show(_vm);
redrawFlag = true;
} else if (Common::KEYCODE_ESCAPE == _buttonValue) {
goto exit;
}
} while (!_vm->shouldExit());
exit:
w.close();
intf.unhighlightChar();
_vm->_mode = oldMode;
_vm->_combat->_itemFlag = false;
}
void CharacterInfo::loadDrawStructs() {
_drawList[0] = DrawStruct(0, 2, 16);
_drawList[1] = DrawStruct(2, 2, 39);
_drawList[2] = DrawStruct(4, 2, 62);
_drawList[3] = DrawStruct(6, 2, 85);
_drawList[4] = DrawStruct(8, 2, 108);
_drawList[5] = DrawStruct(10, 53, 16);
_drawList[6] = DrawStruct(12, 53, 39);
_drawList[7] = DrawStruct(14, 53, 62);
_drawList[8] = DrawStruct(16, 53, 85);
_drawList[9] = DrawStruct(18, 53, 108);
_drawList[10] = DrawStruct(20, 104, 16);
_drawList[11] = DrawStruct(22, 104, 39);
_drawList[12] = DrawStruct(24, 104, 62);
_drawList[13] = DrawStruct(26, 104, 85);
_drawList[14] = DrawStruct(28, 104, 108);
_drawList[15] = DrawStruct(30, 169, 16);
_drawList[16] = DrawStruct(32, 169, 39);
_drawList[17] = DrawStruct(34, 169, 62);
_drawList[18] = DrawStruct(36, 169, 85);
_drawList[19] = DrawStruct(38, 169, 108);
_drawList[20] = DrawStruct(40, 277, 3);
_drawList[21] = DrawStruct(42, 277, 35);
_drawList[22] = DrawStruct(44, 277, 67);
_drawList[23] = DrawStruct(46, 277, 99);
_iconSprites.load("view.icn");
for (int idx = 0; idx < 24; ++idx)
_drawList[idx]._sprites = &_iconSprites;
}
void CharacterInfo::addButtons() {
addButton(Common::Rect(10, 24, 34, 44), 1001, &_iconSprites, kCharacterInfoMight);
addButton(Common::Rect(10, 47, 34, 67), 1002, &_iconSprites, kCharacterInfoIntellect);
addButton(Common::Rect(10, 70, 34, 90), 1003, &_iconSprites, kCharacterInfoPersonality);
addButton(Common::Rect(10, 93, 34, 113), 1004, &_iconSprites, kCharacterInfoEndurance);
addButton(Common::Rect(10, 116, 34, 136), 1005, &_iconSprites, kCharacterInfoSpeed);
addButton(Common::Rect(61, 24, 85, 44), 1006, &_iconSprites, kCharacterInfoAccuracy);
addButton(Common::Rect(61, 47, 85, 67), 1007, &_iconSprites, kCharacterInfoLuck);
addButton(Common::Rect(61, 70, 85, 90), 1008, &_iconSprites, kCharacterInfoAge);
addButton(Common::Rect(61, 93, 85, 113), 1009, &_iconSprites, kCharacterInfoLevel);
addButton(Common::Rect(61, 116, 85, 136), 1010, &_iconSprites, kCharacterInfoAC);
addButton(Common::Rect(112, 24, 136, 44), 1011, &_iconSprites, kCharacterInfoHP);
addButton(Common::Rect(112, 47, 136, 67), 1012, &_iconSprites, kCharacterInfoSP);
addButton(Common::Rect(112, 70, 136, 90), 1013, &_iconSprites, kCharacterInfoResistances);
addButton(Common::Rect(112, 93, 136, 113), 1014, &_iconSprites, kCharacterInfoSkills);
addButton(Common::Rect(112, 116, 136, 136), 1015, &_iconSprites, kCharacterInfoAwards);
addButton(Common::Rect(177, 24, 201, 44), 1016, &_iconSprites, kCharacterInfoExperience);
addButton(Common::Rect(177, 47, 201, 67), 1017, &_iconSprites, kCharacterInfoPartyGold);
addButton(Common::Rect(177, 70, 201, 90), 1018, &_iconSprites, kCharacterInfoPartyGems);
addButton(Common::Rect(177, 93, 201, 113), 1019, &_iconSprites, kCharacterInfoPartyFood);
addButton(Common::Rect(177, 116, 201, 136), 1020, &_iconSprites, kCharacterInfoCondition);
addButton(Common::Rect(285, 11, 309, 31), Res.KeyConstants.DialogsCharInfo.KEY_ITEM, &_iconSprites, kCharacterInfoItem);
addButton(Common::Rect(285, 43, 309, 63), Res.KeyConstants.DialogsCharInfo.KEY_QUICK, &_iconSprites, kCharacterInfoQuickRef);
addButton(Common::Rect(285, 75, 309, 95), Res.KeyConstants.DialogsCharInfo.KEY_EXCHANGE, &_iconSprites, kCharacterInfoExchange);
addButton(Common::Rect(285, 107, 309, 127), Common::KEYCODE_ESCAPE, &_iconSprites, kCharacterInfoExit);
addPartyButtons(_vm);
}
const char *CharacterInfo::getDaysPlurals(int val) {
if (Common::RU_RUS == g_vm->getLanguage()) {
int i = val % 100;
if (i < 5 || i > 20)
switch (val % 10) {
case 1:
return Res.DAYS[0];
case 2:
case 3:
case 4:
return Res.DAYS[1];
}
return Res.DAYS[2];
} else {
return Res.DAYS[val == 1 ? 0 : 1];
}
}
Common::String CharacterInfo::loadCharacterDetails(const Character &c) {
Condition condition = c.worstCondition();
Party &party = *_vm->_party;
int foodVal = party._food / party._activeParty.size() / 3;
int totalResist =
c._fireResistance._permanent + c.itemScan(11) + c._fireResistance._temporary +
c._coldResistance._permanent + c.itemScan(13) + c._coldResistance._temporary +
c._electricityResistance._permanent + c.itemScan(12) + c._electricityResistance._temporary +
c._poisonResistance._permanent + c.itemScan(14) + c._poisonResistance._temporary +
c._energyResistance._permanent + c.itemScan(15) + c._energyResistance._temporary +
c._magicResistance._permanent + c.itemScan(16) + c._magicResistance._temporary;
const char **_tmpConditions = c._sex == FEMALE ? (const char **)Res.CONDITION_NAMES_F : (const char **)Res.CONDITION_NAMES_M;
return Common::String::format(Res.CHARACTER_DETAILS,
Res.PARTY_GOLD, c._name.c_str(), Res.SEX_NAMES[c._sex],
Res.RACE_NAMES[c._race], Res.CLASS_NAMES[c._class],
c.statColor(c.getStat(MIGHT), c.getStat(MIGHT, true)), c.getStat(MIGHT),
c.statColor(c.getStat(ACCURACY), c.getStat(ACCURACY, true)), c.getStat(ACCURACY),
c.statColor(c._currentHp, c.getMaxHP()), c._currentHp,
c.getCurrentExperience(),
c.statColor(c.getStat(INTELLECT), c.getStat(INTELLECT, true)), c.getStat(INTELLECT),
c.statColor(c.getStat(LUCK), c.getStat(LUCK, true)), c.getStat(LUCK),
c.statColor(c._currentSp, c.getMaxSP()), c._currentSp,
party._gold,
c.statColor(c.getStat(PERSONALITY), c.getStat(PERSONALITY, true)), c.getStat(PERSONALITY),
c.statColor(c.getAge(), c.getAge(true)), c.getAge(),
totalResist,
party._gems,
c.statColor(c.getStat(ENDURANCE), c.getStat(ENDURANCE, true)), c.getStat(ENDURANCE),
c.statColor(c.getCurrentLevel(), c._level._permanent), c.getCurrentLevel(),
c.getNumSkills(),
foodVal,
getDaysPlurals(foodVal),
c.statColor(c.getStat(SPEED), c.getStat(SPEED, true)), c.getStat(SPEED),
c.statColor(c.getArmorClass(), c.getArmorClass(true)), c.getArmorClass(),
c.getNumAwards(),
Res.CONDITION_COLORS[condition], _tmpConditions[condition],
condition == NO_CONDITION && party._blessed ? Res.PLUS_14 : "",
condition == NO_CONDITION && party._powerShield ? Res.PLUS_14 : "",
condition == NO_CONDITION && party._holyBonus ? Res.PLUS_14 : "",
condition == NO_CONDITION && party._heroism ? Res.PLUS_14 : "");
}
void CharacterInfo::showCursor(bool flag) {
const int CURSOR_X[5] = { 9, 60, 111, 176, 0 };
const int CURSOR_Y[5] = { 23, 46, 69, 92, 115 };
if (_cursorCell < 20) {
_iconSprites.draw(0, flag ? 49 : 48,
Common::Point(CURSOR_X[_cursorCell / 5], CURSOR_Y[_cursorCell % 5]));
}
}
const char *CharacterInfo::getBornForm(const Character &c) {
if (Common::RU_RUS == g_vm->getLanguage()) {
switch (c._sex) {
case MALE:
return Res.BORN[0];
case FEMALE:
return Res.BORN[1];
case YES_PLEASE:
break;
}
}
return Res.BORN[0];
}
const char *CharacterInfo::getFoodOnHandPlurals(int food) {
if (Common::RU_RUS == g_vm->getLanguage()) {
int i = food % 100;
if (i < 5 || i > 20)
switch (food % 10) {
case 1:
return Res.FOOD_ON_HAND[0];
case 2:
case 3:
case 4:
return Res.FOOD_ON_HAND[1];
}
return Res.FOOD_ON_HAND[2];
}
return Res.FOOD_ON_HAND[0];
}
#ifdef USE_TTS
void CharacterInfo::speakText(const Common::String &text) {
uint index = 0;
uint statNameIndex = 0;
// Get the header for each piece of information
Common::String informationHeaders[kCharacterInfoInformationCount];
for (uint i = 0; i < kCharacterInfoInformationCount; ++i) {
if (i != kCharacterInfoPartyGoldIndex) {
// Items in the rightmost column already have their full names, not abbreviations
if ((i + 1) % kCharacterInfoColumnCount != 0) {
// Replace abbreviations with their full versions
informationHeaders[i] = Res.STAT_NAMES[statNameIndex];
getNextTextSection(text, index);
} else {
informationHeaders[i] = getNextTextSection(text, index);
}
}
// The text is displayed in order from left to right, while the stat names in the STAT_NAMES array are ordered
// from top to bottom. Therefore, we need to set the stat name index to correspond to the STAT_NAMES array
statNameIndex += kCharacterInfoRowCount;
if (statNameIndex >= kCharacterInfoInformationCount) {
statNameIndex -= kCharacterInfoInformationCount - 1;
}
}
// Get the text for the side buttons
Common::String sideButtonsText = addNextTextToButtons(text, index, kCharacterInfoSideButtonCount);
// Party gold label (but not the value) sorts out of order and not with the rest of the information,
// so we need to move it to its correct place
informationHeaders[kCharacterInfoPartyGoldIndex] = getNextTextSection(text, index);
// Character name
_vm->sayText(getNextTextSection(text, index), Common::TextToSpeechManager::INTERRUPT);
// Each attribute
for (uint i = 0; i < kCharacterInfoInformationCount; ++i) {
Common::String buttonText = informationHeaders[i] + ": " + getNextTextSection(text, index);
_vm->sayText(buttonText);
_buttonTexts.push_back(buttonText);
}
_vm->sayText(sideButtonsText);
}
#endif
bool CharacterInfo::expandStat(int attrib, const Character &c) {
const int STAT_POS[2][20] = {
{
61, 61, 61, 61, 61, 112, 112, 112, 112, 112,
177, 177, 177, 177, 177, 34, 34, 34, 34, 34
}, {
24, 47, 70, 93, 116, 24, 47, 70, 93, 116,
24, 47, 70, 93, 116, 24, 47, 70, 93, 116
}
};
assert(attrib < 20);
Common::Rect bounds(STAT_POS[0][attrib], STAT_POS[1][attrib],
STAT_POS[0][attrib] + 143, STAT_POS[1][attrib] + 52);
Party &party = *_vm->_party;
Windows &windows = *_vm->_windows;
uint stat1, stat2;
uint idx;
Common::String msg;
switch (attrib) {
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
// Basic attributes
stat1 = c.getStat((Attribute)attrib, false);
stat2 = c.getStat((Attribute)attrib, true);
for (idx = 0; idx < ARRAYSIZE(Res.STAT_VALUES) - 1; ++idx)
if (Res.STAT_VALUES[idx] > (int)stat1)
break;
msg = Common::String::format(Res.CURRENT_MAXIMUM_RATING_TEXT, Res.STAT_NAMES[attrib],
stat1, stat2, Res.RATING_TEXT[idx]);
break;
case 7:
// Age
stat1 = c.getAge(false);
stat2 = c.getAge(true);
msg = Common::String::format(Res.AGE_TEXT, Res.STAT_NAMES[attrib],
stat1, stat2, getBornForm(c), c._birthDay, c._birthYear);
break;
case 8: {
// Level
const int CLASS_ATTACK_GAINS[10] = { 5, 6, 6, 7, 8, 6, 5, 4, 7, 6 };
idx = c.getCurrentLevel() / CLASS_ATTACK_GAINS[c._class] + 1;
msg = Common::String::format(Res.LEVEL_TEXT, Res.STAT_NAMES[attrib],
c.getCurrentLevel(), c._level._permanent,
idx, idx > 1 ? "s" : "",
c._level._permanent);
break;
}
case 9:
// Armor Class
stat1 = c.getArmorClass(false);
stat2 = c.getArmorClass(true);
msg = Common::String::format(Res.CURRENT_MAXIMUM_TEXT, Res.STAT_NAMES[attrib],
stat1, stat2);
bounds.setHeight(42);
break;
case 10: {
// Hit Points
Common::String fmt(Res.CURRENT_MAXIMUM_TEXT);
const char *p;
while ((p = strstr(fmt.c_str(), "%u")) != nullptr)
fmt.setChar('d', p - fmt.c_str() + 1);
msg = Common::String::format(fmt.c_str(), Res.STAT_NAMES[attrib],
c._currentHp, c.getMaxHP());
bounds.setHeight(42);
break;
}
case 11:
// Spell Points
stat1 = c._currentSp;
stat2 = c.getMaxSP();
msg = Common::String::format(Res.CURRENT_MAXIMUM_TEXT, Res.STAT_NAMES[attrib],
stat1, stat2);
bounds.setHeight(42);
break;
case 12:
// Resistances
msg = Common::String::format(Res.RESISTANCES_TEXT, Res.STAT_NAMES[attrib],
c._fireResistance._permanent + c.itemScan(11) + c._fireResistance._temporary,
c._coldResistance._permanent + c.itemScan(13) + c._coldResistance._temporary,
c._electricityResistance._permanent + c.itemScan(12) + c._electricityResistance._temporary,
c._poisonResistance._permanent + c.itemScan(14) + c._poisonResistance._temporary,
c._energyResistance._permanent + c.itemScan(15) + c._energyResistance._temporary,
c._magicResistance._permanent + c.itemScan(16) + c._magicResistance._temporary);
bounds.setHeight(80);
break;
case 13: {
// Skills
Common::String lines[20];
int numLines = c.getNumSkills();
if (numLines > 0) {
for (int skill = THIEVERY; skill <= DANGER_SENSE; ++skill) {
if (c._skills[skill]) {
if (skill == THIEVERY) {
lines[0] = Common::String::format("\n\t020%s%u",
Res.SKILL_NAMES[THIEVERY], c.getThievery());
} else {
lines[skill] = Common::String::format("\n\t020%s", Res.SKILL_NAMES[skill]);
}
}
}
} else {
lines[0] = Res.NONE;
numLines = 1;
}
msg = Common::String::format("\x2\x3""c%s\x3l%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
Res.STAT_NAMES[attrib], lines[0].c_str(), lines[1].c_str(),
lines[2].c_str(), lines[3].c_str(), lines[4].c_str(), lines[5].c_str(),
lines[17].c_str(), lines[6].c_str(), lines[7].c_str(), lines[8].c_str(),
lines[9].c_str(), lines[10].c_str(), lines[11].c_str(), lines[12].c_str(),
lines[13].c_str(), lines[16].c_str(), lines[14].c_str(), lines[15].c_str());
bounds.top -= (numLines / 2) * 8;
bounds.setHeight(numLines * 9 + 26);
if (bounds.bottom >= SCREEN_HEIGHT)
bounds.moveTo(bounds.left, SCREEN_HEIGHT - bounds.height() - 1);
break;
}
case 14:
// Awards
Awards::show(_vm, &c);
return false;
case 15:
// Experience
stat1 = c.getCurrentExperience();
stat2 = c.experienceToNextLevel();
msg = Common::String::format(Res.EXPERIENCE_TEXT,
Res.STAT_NAMES[attrib], stat1,
stat2 == 0 ? Res.ELIGIBLE : Common::String::format("%d", stat2).c_str()
);
bounds.setHeight(43);
break;
case 16:
// Gold
msg = Common::String::format(Res.IN_PARTY_IN_BANK, Res.CONSUMABLE_NAMES[0],
party._gold, party._bankGold);
bounds.setHeight(43);
break;
case 17:
// Gems
msg = Common::String::format(Res.IN_PARTY_IN_BANK, Res.CONSUMABLE_NAMES[1],
party._gems, party._bankGems);
bounds.setHeight(43);
break;
case 18: {
// Food
int food = (party._food / party._activeParty.size()) / 3;
msg = Common::String::format(Res.FOOD_TEXT, Res.CONSUMABLE_NAMES[2],
party._food, getFoodOnHandPlurals(food), food, getDaysPlurals(food));
break;
}
case 19: {
// Conditions
Common::String lines[20];
const char **_tmpConditions = c._sex == FEMALE ? (const char **)Res.CONDITION_NAMES_F : (const char **)Res.CONDITION_NAMES_M;
int total = 0;
for (int condition = CURSED; condition <= ERADICATED; ++condition) {
if (c._conditions[condition]) {
if (condition >= UNCONSCIOUS) {
lines[condition] = Common::String::format("\n\t020%s",
_tmpConditions[condition]);
} else {
lines[condition] = Common::String::format("\n\t020%s\t095-%d",
_tmpConditions[condition], c._conditions[condition]);
}
++total;
}
}
Condition condition = c.worstCondition();
if (condition == NO_CONDITION) {
lines[0] = Common::String::format("\n\t020%s", Res.GOOD);
++total;
}
if (party._blessed) {
lines[16] = Common::String::format(Res.BLESSED, party._blessed);
++total;
}
if (party._powerShield) {
lines[17] = Common::String::format(Res.POWER_SHIELD, party._powerShield);
++total;
}
if (party._holyBonus) {
lines[18] = Common::String::format(Res.HOLY_BONUS, party._holyBonus);
++total;
}
if (party._heroism) {
lines[19] = Common::String::format(Res.HEROISM, party._heroism);
++total;
}
msg = Common::String::format("\x2\x3""c%s\x3l%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\x1",
Res.CONSUMABLE_NAMES[3], lines[0].c_str(), lines[1].c_str(),
lines[2].c_str(), lines[3].c_str(), lines[4].c_str(),
lines[5].c_str(), lines[6].c_str(), lines[7].c_str(),
lines[8].c_str(), lines[9].c_str(), lines[10].c_str(),
lines[11].c_str(), lines[12].c_str(), lines[13].c_str(),
lines[14].c_str(), lines[15].c_str(), lines[16].c_str(),
lines[17].c_str(), lines[18].c_str(), lines[19].c_str()
);
bounds.top -= ((total - 1) / 2) * 8;
bounds.setHeight(total * 9 + 26);
if (bounds.bottom >= SCREEN_HEIGHT)
bounds.moveTo(bounds.left, SCREEN_HEIGHT - bounds.height() - 1);
break;
}
default:
break;
}
// Write the data for the stat display
Window &w = windows[28];
w.setBounds(bounds);
w.open();
w.writeString(msg);
w.update();
// Wait for a user key/click
EventsManager &events = *_vm->_events;
while (!_vm->shouldExit() && !events.isKeyMousePressed())
events.pollEventsAndWait();
events.clearEvents();
#ifdef USE_TTS
_vm->stopTextToSpeech();
#endif
w.close();
return false;
}
} // End of namespace Xeen
} // End of namespace MM

View File

@@ -0,0 +1,89 @@
/* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef XEEN_DIALOGS_CHAR_INFO_H
#define XEEN_DIALOGS_CHAR_INFO_H
#include "mm/xeen/dialogs/dialogs.h"
#include "mm/xeen/party.h"
#include "mm/xeen/window.h"
namespace MM {
namespace Xeen {
class CharacterInfo : public ButtonContainer {
private:
SpriteResource _iconSprites;
DrawStruct _drawList[24];
int _cursorCell;
CharacterInfo(XeenEngine *vm) : ButtonContainer(vm), _cursorCell(0) {}
void execute(int charIndex);
/**
* Load the draw structure list with frame numbers and positions
*/
void loadDrawStructs();
/**
* Set up the button list for the dialog
*/
void addButtons();
/**
* Get plural day/days form
*/
const char *getDaysPlurals(int val);
/**
* Return a string containing the details of the character
*/
Common::String loadCharacterDetails(const Character &c);
/**
* Cursor display handling
*/
void showCursor(bool flag);
/**
* Get gender form 'born'
*/
const char *getBornForm(const Character &c);
/**
* Get plural form 'on hand' for food
*/
const char *getFoodOnHandPlurals(int food);
#ifdef USE_TTS
void speakText(const Common::String &text);
#endif
bool expandStat(int attrib, const Character &c);
public:
static void show(XeenEngine *vm, int charIndex);
};
} // End of namespace Xeen
} // End of namespace MM
#endif

View File

@@ -0,0 +1,265 @@
/* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "mm/xeen/dialogs/dialogs_control_panel.h"
#include "mm/xeen/dialogs/dialogs_query.h"
#include "mm/xeen/party.h"
#include "mm/xeen/resources.h"
#include "mm/xeen/xeen.h"
namespace MM {
namespace Xeen {
#ifdef USE_TTS
static const uint8 kControlPanelTextCount = 7;
static const uint8 kControlPanelEfxIndex = 1;
static const uint8 kControlPanelMusicIndex = 3;
#endif
enum ControlPanelButtonTTSTextIndex {
kControlPanelLoad = 0,
kControlPanelEfx = 1,
kControlPanelSave = 2,
kControlPanelMusic = 3,
kControlPanelQuit = 4,
kControlPanelWizard = 5
};
int ControlPanel::show(XeenEngine *vm) {
ControlPanel *dlg = new ControlPanel(vm);
int result = dlg->execute();
delete dlg;
return result;
}
int ControlPanel::execute() {
EventsManager &events = *_vm->_events;
Interface &intf = *_vm->_interface;
Map &map = *_vm->_map;
Party &party = *_vm->_party;
SavesManager &saves = *_vm->_saves;
Sound &sound = *_vm->_sound;
Windows &windows = *_vm->_windows;
Window &w = windows[23];
Window &w3 = windows[3];
loadButtons();
int result = 0, debugCtr = 0;
w.open();
#ifdef USE_TTS
bool voiceText = true;
_vm->stopTextToSpeech();
#endif
do {
Common::String btnText = getButtonText();
Common::String text = Common::String::format(Res.CONTROL_PANEL_TEXT, btnText.c_str());
drawButtons(&w);
Common::String ttsMessage;
w.writeString(text, false, &ttsMessage);
w.writeString("\xB""000\t000\x1");
w.update();
#ifdef USE_TTS
if (voiceText) {
speakText(ttsMessage);
voiceText = false;
}
#endif
events.updateGameCounter();
intf.draw3d(false, false);
do {
w.writeString("\r", false);
drawButtons(&w);
w.writeString(text, false);
w.writeString("\v000\t000", false);
w.frame();
if (_debugFlag)
w.writeString(getTimeText());
w3.update();
w.update();
events.pollEventsAndWait();
checkEvents(_vm);
if (_vm->shouldExit())
return 0;
} while (!_buttonValue && events.timeElapsed() < 2);
if (Res.KeyConstants.DialogsControlPanel.KEY_QUIT == _buttonValue) {
if (Confirm::show(g_vm, Res.CONFIRM_QUIT)) {
g_vm->_gameMode = GMODE_QUIT;
result = 1;
}
} else if (Res.KeyConstants.DialogsControlPanel.KEY_MRWIZARD == _buttonValue) {
if (Confirm::show(g_vm, Res.MR_WIZARD)) {
w.close();
if (!windows[2]._enabled) {
sound.playFX(51);
if (g_vm->getGameID() == GType_WorldOfXeen) {
map._loadCcNum = 0;
map.load(28);
party._mazeDirection = DIR_EAST;
} else {
map._loadCcNum = 1;
map.load(29);
party._mazeDirection = DIR_SOUTH;
}
party.moveToRunLocation();
}
party._gems = 0;
result = 2;
}
} else if (Res.KeyConstants.DialogsControlPanel.KEY_LOAD == _buttonValue) {
if (_vm->_mode == MODE_COMBAT) {
ErrorScroll::show(_vm, Res.NO_LOADING_IN_COMBAT);
} else {
// Close dialog and show loading dialog
result = 3;
}
} else if (Res.KeyConstants.DialogsControlPanel.KEY_SAVE == _buttonValue) {
if (_vm->_mode == MODE_COMBAT) {
ErrorScroll::show(_vm, Res.NO_SAVING_IN_COMBAT);
} else {
// Close dialog and show saving dialog
result = 4;
}
} else if (Res.KeyConstants.DialogsControlPanel.KEY_FXON == _buttonValue) {
sound.setFxOn(!sound._fxOn);
if (sound._fxOn)
sound.playFX(20);
#ifdef USE_TTS
voiceOnOffText(sound._fxOn, kControlPanelEfxIndex);
#endif
} else if (Res.KeyConstants.DialogsControlPanel.KEY_MUSICON == _buttonValue) {
sound.setMusicOn(!sound._musicOn);
#ifdef USE_TTS
voiceOnOffText(sound._musicOn, kControlPanelMusicIndex);
#endif
} else if (Common::KEYCODE_ESCAPE == _buttonValue) {
result = 1;
} else if (Common::KEYCODE_g == _buttonValue) { // Goober cheat sequence
debugCtr = 1;
} else if (Common::KEYCODE_o == _buttonValue) {
debugCtr = (debugCtr == 1 || debugCtr == 2) ? 2 : 0;
} else if (Common::KEYCODE_b == _buttonValue) {
debugCtr = (debugCtr == 2) ? 3 : 0;
} else if (Common::KEYCODE_r == _buttonValue) {
if (debugCtr == 3)
_debugFlag = true;
else
debugCtr = 0;
}
} while (!result);
w.close();
intf.drawParty(true);
if (result == 3) {
if (g_vm->canLoadGameStateCurrently())
saves.loadGame();
} else if (result == 4) {
saves.saveGame();
}
return result;
}
void ControlPanel::loadButtons() {
_iconSprites.load("cpanel.icn");
addButton(Common::Rect(214, 56, 244, 69), Res.KeyConstants.DialogsControlPanel.KEY_FXON, 0, &_iconSprites, kControlPanelEfx);
addButton(Common::Rect(214, 75, 244, 88), Res.KeyConstants.DialogsControlPanel.KEY_MUSICON, 0, &_iconSprites, kControlPanelMusic);
addButton(Common::Rect(135, 56, 165, 69), Res.KeyConstants.DialogsControlPanel.KEY_LOAD, 0, &_iconSprites, kControlPanelLoad);
addButton(Common::Rect(135, 75, 165, 88), Res.KeyConstants.DialogsControlPanel.KEY_SAVE, 0, &_iconSprites, kControlPanelSave);
// For ScummVM we've merged both Save and Save As into a single
// save item, so we don't need this one
addButton(Common::Rect(), 0);
addButton(Common::Rect(135, 94, 165, 107), Res.KeyConstants.DialogsControlPanel.KEY_QUIT, 0, &_iconSprites, kControlPanelQuit);
addButton(Common::Rect(175, 113, 205, 126), Res.KeyConstants.DialogsControlPanel.KEY_MRWIZARD, 0, &_iconSprites, kControlPanelWizard);
}
Common::String ControlPanel::getButtonText() {
Sound &sound = *g_vm->_sound;
_btnSoundText = sound._fxOn ? Res.ON : Res.OFF;
_btnMusicText = sound._musicOn ? Res.ON : Res.OFF;
return Common::String::format(Res.CONTROL_PANEL_BUTTONS,
_btnSoundText.c_str(), _btnMusicText.c_str());
}
Common::String ControlPanel::getTimeText() const {
TimeDate td;
g_system->getTimeAndDate(td);
Common::String timeStr = Common::String::format("%d:%.2d:%.2d%c",
td.tm_hour == 0 || td.tm_hour == 12 ? 12 : (td.tm_hour % 12),
td.tm_min, td.tm_sec, (td.tm_hour >= 12) ? 'p' : 'c');
uint32 playtime = g_vm->_events->playTime() / GAME_FRAME_RATE;
Common::String playtimeStr = Common::String::format("%d:%.2d:%.2d",
playtime / 3600, (playtime / 60) % 60, playtime % 60);
return Common::String::format(
"\x2\x3l\xB""000\t000\x4""160%s\x3r\xB""000\t000%s\x1",
timeStr.c_str(), playtimeStr.c_str());
}
#ifdef USE_TTS
void ControlPanel::speakText(const Common::String &text) {
uint index = 0;
_vm->sayText(getNextTextSection(text, index, kControlPanelTextCount));
addNextTextToButtons(text, index, kControlPanelTextCount - 1);
}
void ControlPanel::voiceOnOffText(bool on, uint buttonTextIndex) {
const char *baseMessage = on ? Res.ON : Res.OFF;
Common::String cleanedMessage;
for (uint i = 0; i < strlen(baseMessage); ++i) {
if (Common::isAlpha(baseMessage[i])) {
cleanedMessage += baseMessage[i];
}
}
_vm->sayText(cleanedMessage, Common::TextToSpeechManager::INTERRUPT);
_buttonTexts[buttonTextIndex] = cleanedMessage;
}
#endif
} // End of namespace Xeen
} // End of namespace MM

View File

@@ -0,0 +1,82 @@
/* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef XEEN_DIALOGS_CONTROL_PANEL_H
#define XEEN_DIALOGS_CONTROL_PANEL_H
#include "mm/xeen/dialogs/dialogs.h"
namespace MM {
namespace Xeen {
class ControlPanel : public ButtonContainer {
private:
SpriteResource _iconSprites;
Common::String _btnSoundText, _btnMusicText;
bool _debugFlag;
private:
ControlPanel(XeenEngine *vm) : ButtonContainer(vm), _debugFlag(false) {}
/**
* Inner handler for showing the dialog
*/
int execute();
/**
* Loads the buttons for the dialog
*/
void loadButtons();
/**
* Gets the text for the dialog buttons
*/
Common::String getButtonText();
/**
* Gets the current time
*/
Common::String getTimeText() const;
#ifdef USE_TTS
/**
* Voices text as text-to-speech and sets the buttons
* @param text Text of the control panel, each piece separated by a newline
*/
void speakText(const Common::String &text);
/**
* Voices "On" or "Off" text and changes the TTS text of an on/off button to this text
* @param on Whether to voice "on" or "off"
* @param buttonTextIndex The index of the button text to change
*/
void voiceOnOffText(bool on, uint buttonTextIndex);
#endif
public:
/**
* Show the control panel
*/
static int show(XeenEngine *vm);
};
} // End of namespace Xeen
} // End of namespace MM
#endif

View File

@@ -0,0 +1,124 @@
/* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "mm/xeen/dialogs/dialogs_copy_protection.h"
#include "mm/xeen/dialogs/dialogs_input.h"
#include "mm/xeen/resources.h"
#include "mm/xeen/xeen.h"
namespace MM {
namespace Xeen {
#ifdef USE_TTS
static const uint8 kCopyProtectionDirectionCount = 2;
#endif
bool CopyProtection::show(XeenEngine *vm) {
CopyProtection *dlg = new CopyProtection(vm);
int result = dlg->execute();
delete dlg;
return result;
}
CopyProtection::CopyProtection(XeenEngine *vm) : Input(vm, &(*vm->_windows)[11]) {
loadEntries();
}
bool CopyProtection::execute() {
EventsManager &events = *_vm->_events;
Sound &sound = *_vm->_sound;
Window &w = *_window;
bool result = false;
Common::String line;
// Choose a random entry
ProtectionEntry &protEntry = _entries[_vm->getRandomNumber(_entries.size() - 1)];
Common::String msg = Common::String::format(Res.WHATS_THE_PASSWORD,
protEntry._pageNum, protEntry._lineNum, protEntry._wordNum);
w.open();
Common::String ttsMessage;
w.writeString(msg, false, &ttsMessage);
w.update();
#ifdef USE_TTS
speakText(ttsMessage);
#endif
for (int tryNum = 0; tryNum < 3 && !_vm->shouldExit(); ++tryNum) {
line.clear();
if (getString(line, 20, 200, false) && !line.compareToIgnoreCase(protEntry._text)) {
sound.playFX(20);
result = true;
break;
}
sound.playFX(21);
w.writeString("\x3l\v040\n\x4""200");
w.writeString(Res.PASSWORD_INCORRECT);
w.update();
events.updateGameCounter();
events.wait(50, false);
}
w.close();
return result;
}
void CopyProtection::loadEntries() {
FileManager &files = *g_vm->_files;
File f(files._ccNum ? "timer.drv" : "cpstruct");
ProtectionEntry pe;
byte seed = 0;
char text[13];
while (f.pos() < f.size()) {
pe._pageNum = f.readByte() ^ seed++;
pe._lineNum = f.readByte() ^ seed++;
pe._wordNum = f.readByte() ^ seed++;
for (int idx = 0; idx < 13; ++idx)
text[idx] = f.readByte() ^ seed++;
text[12] = '\0';
pe._text = Common::String(text);
_entries.push_back(pe);
}
}
#ifdef USE_TTS
void CopyProtection::speakText(const Common::String &text) const {
uint index = 0;
_vm->sayText(getNextTextSection(text, index), Common::TextToSpeechManager::INTERRUPT);
// Combine the directions so they're spoken cleanly as one sentence
_vm->sayText(getNextTextSection(text, index, kCopyProtectionDirectionCount, " "));
_vm->sayText(text.substr(index));
}
#endif
} // End of namespace Xeen
} // End of namespace MM

View File

@@ -0,0 +1,73 @@
/* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef XEEN_DIALOGS_COPY_PROTECTION_H
#define XEEN_DIALOGS_COPY_PROTECTION_H
#include "mm/xeen/dialogs/dialogs_input.h"
#include "common/array.h"
namespace MM {
namespace Xeen {
class CopyProtection : public Input {
struct ProtectionEntry {
uint8 _pageNum;
uint8 _lineNum;
uint8 _wordNum;
Common::String _text;
};
private:
Common::Array<ProtectionEntry> _entries;
private:
/**
* Constructor
*/
CopyProtection(XeenEngine *vm);
/**
* Execute the dialog
*/
bool execute();
/**
* Load the copy protection entries
*/
void loadEntries();
#ifdef USE_TTS
/**
* Voices text with TTS
* @param text Text to voice
*/
void speakText(const Common::String &text) const;
#endif
public:
/**
* Show the dialog
*/
static bool show(XeenEngine *vm);
};
} // End of namespace Xeen
} // End of namespace MM
#endif

View File

@@ -0,0 +1,791 @@
/* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "mm/xeen/dialogs/dialogs_create_char.h"
#include "mm/xeen/dialogs/dialogs_input.h"
#include "mm/xeen/xeen.h"
namespace MM {
namespace Xeen {
#ifdef USE_TTS
static const uint8 kCreateCharacterBasicInfoCount = 6;
static const uint8 kCreateCharacterSideButtonCount = 3;
#endif
enum CreateCharacterButtonTTSTextIndex {
kCreateCharacterRoll = 0,
kCreateCharacterCreate = 1,
kCreateCharacterExit = 2,
kCreateCharacterMight = 3,
kCreateCharacterIntellect = 4,
kCreateCharacterPersonality = 5,
kCreateCharacterEndurance = 6,
kCreateCharacterSpeed = 7,
kCreateCharacterAccuracy = 8,
kCreateCharacterLuck = 9,
kCreateCharacterKnight = 10,
kCreateCharacterPaladin = 11,
kCreateCharacterArcher = 12,
kCreateCharacterCleric = 13,
kCreateCharacterSorcerer = 14,
kCreateCharacterRobber = 15,
kCreateCharacterNinja = 16,
kCreateCharacterBarbarian = 17,
kCreateCharacterDruid = 18,
kCreateCharacterRanger = 19,
kCreateCharacterSwapMight = 20,
kCreateCharacterSwapIntellect = 21,
kCreateCharacterSwapPersonality = 22,
kCreateCharacterSwapEndurance = 23,
kCreateCharacterSwapSpeed = 24,
kCreateCharacterSwapAccuracy = 25,
kCreateCharacterSwapLuck = 26
};
void CreateCharacterDialog::show(XeenEngine *vm) {
CreateCharacterDialog *dlg = new CreateCharacterDialog(vm);
dlg->execute();
delete dlg;
}
CreateCharacterDialog::CreateCharacterDialog(XeenEngine *vm) : ButtonContainer(vm) {
Common::fill(&_attribs[0], &_attribs[TOTAL_ATTRIBUTES], 0);
Common::fill(&_allowedClasses[0], &_allowedClasses[TOTAL_CLASSES], false);
_dicePos[0] = Common::Point(20, 17);
_dicePos[1] = Common::Point(112, 35);
_dicePos[2] = Common::Point(61, 50);
_diceFrame[0] = 0;
_diceFrame[1] = 2;
_diceFrame[2] = 4;
_diceInc[0] = Common::Point(10, -10);
_diceInc[1] = Common::Point(-10, -10);
_diceInc[2] = Common::Point(-10, 10);
_dice.load("dice.vga");
_diceSize = _dice.getFrameSize(0);
loadButtons();
}
void CreateCharacterDialog::execute() {
EventsManager &events = *_vm->_events;
Party &party = *_vm->_party;
Screen &screen = *_vm->_screen;
Windows &windows = *_vm->_windows;
Window &w = windows[0];
Common::Array<int> freeCharList;
int classId = -1;
int selectedClass = 0;
bool hasFadedIn = false;
bool restartFlag = true;
Race race = HUMAN;
Sex sex = MALE;
Common::String msg, details;
int charIndex = 0;
Mode oldMode = _vm->_mode;
_vm->_mode = MODE_4;
// Load the background
screen.loadBackground("create.raw");
events.setCursor(0);
do {
if (restartFlag) {
// Build up list of roster slot indexes that are free
freeCharList.clear();
for (uint idx = 0; idx < XEEN_TOTAL_CHARACTERS; ++idx) {
if (party._roster[idx]._name.empty())
freeCharList.push_back(idx);
}
charIndex = 0;
if (freeCharList.size() == 0)
break;
// Get and race and sex for the given character
race = (Race)((freeCharList[charIndex] / 4) % 5);
sex = (Sex)(freeCharList[charIndex] & 1);
// Randomly determine attributes, and which classes they allow
rollAttributes();
// Get the display of the rolled character details
selectedClass = newCharDetails(race, sex, classId, selectedClass, details);
msg = Common::String::format(Res.CREATE_CHAR_DETAILS,
details.c_str());
// Draw the icons and the currently selected headshot
drawIcons();
party._roster[freeCharList[charIndex]]._faceSprites->draw(
w, 0, Common::Point(27, 102));
Common::String ttsMessage;
// Render all on-screen text
w.writeString(msg, false, &ttsMessage);
w.update();
#ifdef USE_TTS
speakText(ttsMessage, false, classId != -1, selectedClass);
#endif
// Draw the arrow for the selected class, if applicable
if (selectedClass != -1)
printSelectionArrow(selectedClass);
// Draw the dice
drawDice();
if (!hasFadedIn) {
screen.fadeIn();
hasFadedIn = true;
}
restartFlag = false;
}
// Animate the dice until a user action occurs
_buttonValue = 0;
while (!_vm->shouldExit() && !_buttonValue)
drawDice();
// Handling for different actions
if (_buttonValue == Common::KEYCODE_ESCAPE)
break;
if (Common::KEYCODE_UP == _buttonValue) {
if (charIndex == 0)
continue;
--charIndex;
race = (Race)((freeCharList[charIndex] / 4) % 5);
sex = (Sex)(freeCharList[charIndex] & 1);
} else if (Common::KEYCODE_DOWN == _buttonValue) {
if (++charIndex == (int)freeCharList.size()) {
--charIndex;
continue;
} else {
race = (Race)((freeCharList[charIndex] / 4) % 5);
sex = (Sex)(freeCharList[charIndex] & 1);
}
} else if (Common::KEYCODE_PAGEUP == _buttonValue) {
for (int tempClass = selectedClass - 1; tempClass >= 0; --tempClass) {
if (_allowedClasses[tempClass]) {
selectedClass = tempClass;
break;
}
}
printSelectionArrow(selectedClass);
continue;
} else if (Common::KEYCODE_PAGEDOWN == _buttonValue) {
} else if (Res.KeyConstants.DialogsCreateChar.KEY_MGT == _buttonValue ||
Res.KeyConstants.DialogsCreateChar.KEY_INT == _buttonValue ||
Res.KeyConstants.DialogsCreateChar.KEY_PER == _buttonValue ||
Res.KeyConstants.DialogsCreateChar.KEY_END == _buttonValue ||
Res.KeyConstants.DialogsCreateChar.KEY_SPD == _buttonValue ||
Res.KeyConstants.DialogsCreateChar.KEY_ACY == _buttonValue ||
Res.KeyConstants.DialogsCreateChar.KEY_LCK == _buttonValue) {
if (swapAttributes(_buttonValue)) {
checkClass();
classId = -1;
selectedClass = newCharDetails(race, sex, classId, selectedClass, msg);
}
} else if (1000 == _buttonValue ||
1001 == _buttonValue ||
1002 == _buttonValue ||
1003 == _buttonValue ||
1004 == _buttonValue ||
1005 == _buttonValue ||
1006 == _buttonValue ||
1007 == _buttonValue ||
1008 == _buttonValue ||
1009 == _buttonValue) {
if (_allowedClasses[_buttonValue - 1000]) {
selectedClass = classId = _buttonValue - 1000;
}
} else if (Res.KeyConstants.DialogsCreateChar.KEY_CREATE == _buttonValue) {
_vm->_mode = MODE_FF;
bool result = saveCharacter(party._roster[freeCharList[charIndex]],
classId, race, sex);
_vm->_mode = MODE_4;
if (result)
restartFlag = true;
continue;
} else if (Common::KEYCODE_RETURN == _buttonValue) {
classId = selectedClass;
} else if (Common::KEYCODE_SPACE == _buttonValue ||
Res.KeyConstants.DialogsCreateChar.KEY_ROLL == _buttonValue) {
// Re-roll the attributes
rollAttributes();
classId = -1;
} else {
// For all other keypresses, skip the code below the switch
// statement, and go to wait for the next key
continue;
}
if (_buttonValue != Common::KEYCODE_PAGEDOWN) {
selectedClass = newCharDetails(race, sex, classId, selectedClass, msg);
drawIcons2();
party._roster[freeCharList[charIndex]]._faceSprites->draw(w, 0,
Common::Point(27, 102));
Common::String ttsMessage;
w.writeString(msg, false, &ttsMessage);
w.update();
#ifdef USE_TTS
speakText(ttsMessage, true, classId != -1, selectedClass);
#endif
if (selectedClass != -1) {
printSelectionArrow(selectedClass);
continue;
}
}
// Move to next available class, or if the code block above resulted in
// selectedClass being -1, move to select the first available class
for (int tempClass = selectedClass + 1; tempClass <= CLASS_RANGER; ++tempClass) {
if (_allowedClasses[tempClass]) {
selectedClass = tempClass;
break;
}
}
printSelectionArrow(selectedClass);
} while (!_vm->shouldExit() && _buttonValue != Common::KEYCODE_ESCAPE);
_vm->_mode = oldMode;
}
void CreateCharacterDialog::loadButtons() {
_icons.load("create.icn");
// Add buttons
addButton(Common::Rect(132, 98, 156, 118), Res.KeyConstants.DialogsCreateChar.KEY_ROLL, &_icons, kCreateCharacterRoll);
addButton(Common::Rect(132, 128, 156, 148), Res.KeyConstants.DialogsCreateChar.KEY_CREATE, &_icons, kCreateCharacterCreate);
addButton(Common::Rect(132, 158, 156, 178), Common::KEYCODE_ESCAPE, &_icons, kCreateCharacterExit);
addButton(Common::Rect(86, 98, 110, 118), Common::KEYCODE_UP, &_icons);
addButton(Common::Rect(86, 120, 110, 140), Common::KEYCODE_DOWN, &_icons);
addButton(Common::Rect(168, 19, 192, 39), Res.KeyConstants.DialogsCreateChar.KEY_MGT, nullptr, kCreateCharacterMight);
addButton(Common::Rect(168, 43, 192, 63), Res.KeyConstants.DialogsCreateChar.KEY_INT, nullptr, kCreateCharacterIntellect);
addButton(Common::Rect(168, 67, 192, 87), Res.KeyConstants.DialogsCreateChar.KEY_PER, nullptr, kCreateCharacterPersonality);
addButton(Common::Rect(168, 91, 192, 111), Res.KeyConstants.DialogsCreateChar.KEY_END, nullptr, kCreateCharacterEndurance);
addButton(Common::Rect(168, 115, 192, 135), Res.KeyConstants.DialogsCreateChar.KEY_SPD, nullptr, kCreateCharacterSpeed);
addButton(Common::Rect(168, 139, 192, 159), Res.KeyConstants.DialogsCreateChar.KEY_ACY, nullptr, kCreateCharacterAccuracy);
addButton(Common::Rect(168, 163, 192, 183), Res.KeyConstants.DialogsCreateChar.KEY_LCK, nullptr, kCreateCharacterLuck);
addButton(Common::Rect(227, 19, 239, 29), 1000, nullptr, kCreateCharacterKnight);
addButton(Common::Rect(227, 30, 239, 40), 1001, nullptr, kCreateCharacterPaladin);
addButton(Common::Rect(227, 41, 239, 51), 1002, nullptr, kCreateCharacterArcher);
addButton(Common::Rect(227, 52, 239, 62), 1003, nullptr, kCreateCharacterCleric);
addButton(Common::Rect(227, 63, 239, 73), 1004, nullptr, kCreateCharacterSorcerer);
addButton(Common::Rect(227, 74, 239, 84), 1005, nullptr, kCreateCharacterRobber);
addButton(Common::Rect(227, 85, 239, 95), 1006, nullptr, kCreateCharacterNinja);
addButton(Common::Rect(227, 96, 239, 106), 1007, nullptr, kCreateCharacterBarbarian);
addButton(Common::Rect(227, 107, 239, 117), 1008, nullptr, kCreateCharacterDruid);
addButton(Common::Rect(227, 118, 239, 128), 1009, nullptr, kCreateCharacterRanger);
}
void CreateCharacterDialog::drawIcons() {
// Draw the screen
_icons.draw(0, 10, Common::Point(168, 19));
_icons.draw(0, 12, Common::Point(168, 43));
_icons.draw(0, 14, Common::Point(168, 67));
_icons.draw(0, 16, Common::Point(168, 91));
_icons.draw(0, 18, Common::Point(168, 115));
_icons.draw(0, 20, Common::Point(168, 139));
_icons.draw(0, 22, Common::Point(168, 163));
for (int idx = 0; idx < 9; ++idx)
_icons.draw(0, 24 + idx * 2, Common::Point(227, 19 + 11 * idx));
for (int idx = 0; idx < 7; ++idx)
_icons.draw(0, 50 + idx, Common::Point(195, 31 + 24 * idx));
_icons.draw(0, 57, Common::Point(62, 148));
_icons.draw(0, 58, Common::Point(62, 158));
_icons.draw(0, 59, Common::Point(62, 168));
_icons.draw(0, 61, Common::Point(220, 19));
_icons.draw(0, 64, Common::Point(220, 155));
_icons.draw(0, 65, Common::Point(220, 170));
_icons.draw(0, 0, Common::Point(132, 98));
_icons.draw(0, 2, Common::Point(132, 128));
_icons.draw(0, 4, Common::Point(132, 158));
_icons.draw(0, 6, Common::Point(86, 98));
_icons.draw(0, 8, Common::Point(86, 120));
}
void CreateCharacterDialog::drawIcons2() {
for (int idx = 0; idx < 7; ++idx)
_icons.draw(0, 10 + idx * 2, Common::Point(168, 19 + idx * 24));
for (int idx = 0; idx < 10; ++idx)
_icons.draw(0, 24 + idx * 2, Common::Point(227, 19 + idx * 11));
for (int idx = 0; idx < 8; ++idx)
_icons.draw(0, 50 + idx, Common::Point(195, 31 + idx * 24));
_icons.draw(0, 57, Common::Point(62, 148));
_icons.draw(0, 58, Common::Point(62, 158));
_icons.draw(0, 59, Common::Point(62, 168));
_icons.draw(0, 61, Common::Point(220, 19));
_icons.draw(0, 64, Common::Point(220, 155));
_icons.draw(0, 65, Common::Point(220, 170));
_icons.draw(0, 0, Common::Point(132, 98));
_icons.draw(0, 2, Common::Point(132, 128));
_icons.draw(0, 4, Common::Point(132, 158));
_icons.draw(0, 6, Common::Point(86, 98));
_icons.draw(0, 8, Common::Point(86, 120));
}
void CreateCharacterDialog::rollAttributes() {
bool repeat = true;
do {
// Default all the attributes to zero
Common::fill(&_attribs[0], &_attribs[TOTAL_ATTRIBUTES], 0);
// Assign random amounts to each attribute
for (int idx1 = 0; idx1 < 3; ++idx1) {
for (int idx2 = 0; idx2 < TOTAL_ATTRIBUTES; ++idx2) {
_attribs[idx2] += _vm->getRandomNumber(10, 79) / 10;
}
}
// Check which classes are allowed based on the rolled attributes
checkClass();
// Only exit if the attributes allow for at least one class
for (int idx = 0; idx < TOTAL_CLASSES; ++idx) {
if (_allowedClasses[idx])
repeat = false;
}
} while (repeat);
}
void CreateCharacterDialog::checkClass() {
_allowedClasses[CLASS_KNIGHT] = _attribs[MIGHT] >= 15;
_allowedClasses[CLASS_PALADIN] = _attribs[MIGHT] >= 13
&& _attribs[PERSONALITY] >= 13 && _attribs[ENDURANCE] >= 13;
_allowedClasses[CLASS_ARCHER] = _attribs[INTELLECT] >= 13 && _attribs[ACCURACY] >= 13;
_allowedClasses[CLASS_CLERIC] = _attribs[PERSONALITY] >= 13;
_allowedClasses[CLASS_SORCERER] = _attribs[INTELLECT] >= 13;
_allowedClasses[CLASS_ROBBER] = _attribs[LUCK] >= 13;
_allowedClasses[CLASS_NINJA] = _attribs[SPEED] >= 13 && _attribs[ACCURACY] >= 13;
_allowedClasses[CLASS_BARBARIAN] = _attribs[ENDURANCE] >= 15;
_allowedClasses[CLASS_DRUID] = _attribs[INTELLECT] >= 15 && _attribs[PERSONALITY] >= 15;
_allowedClasses[CLASS_RANGER] = _attribs[INTELLECT] >= 12 && _attribs[PERSONALITY] >= 12
&& _attribs[ENDURANCE] >= 12 && _attribs[SPEED] >= 12;
}
int CreateCharacterDialog::newCharDetails(Race race, Sex sex, int classId,
int selectedClass, Common::String &msg) {
int foundClass = -1;
Common::String skillStr, classStr, raceSkillStr;
// If a selected class is provided, set the default skill for that class
if (classId != -1 && Res.NEW_CHAR_SKILLS[classId] != -1) {
const char *skillP = Res.SKILL_NAMES[Res.NEW_CHAR_SKILLS[classId]];
skillStr = Common::String(skillP, skillP + Res.NEW_CHAR_SKILLS_LEN[classId]);
}
// If a class is provided, set the class name
if (classId != -1) {
classStr = Common::String::format("\t062\v168%s", Res.CLASS_NAMES[classId]);
}
// Set up default skill for the race, if any
if (Res.NEW_CHAR_RACE_SKILLS[race] != -1) {
const char *skillP = Res.SKILL_NAMES[Res.NEW_CHAR_RACE_SKILLS[race]];
raceSkillStr = Common::String(skillP + Res.NEW_CHAR_SKILLS_OFFSET[race]);
}
// Set up color to use for each skill string to be displayed, based
// on whether each class is allowed or not for the given attributes
int classColors[TOTAL_CLASSES];
Common::fill(&classColors[0], &classColors[TOTAL_CLASSES], 0);
for (int classNum = CLASS_KNIGHT; classNum <= CLASS_RANGER; ++classNum) {
if (_allowedClasses[classNum]) {
if (classId == -1 && (foundClass == -1 || foundClass < classNum))
foundClass = classNum;
classColors[classNum] = 4;
}
}
if (classId != -1)
classColors[selectedClass] = 12;
// Return stats details and character class
msg = Common::String::format(Res.NEW_CHAR_STATS, Res.RACE_NAMES[race], Res.SEX_NAMES[sex],
_attribs[MIGHT], _attribs[INTELLECT], _attribs[PERSONALITY],
_attribs[ENDURANCE], _attribs[SPEED], _attribs[ACCURACY], _attribs[LUCK],
classColors[CLASS_KNIGHT], classColors[CLASS_PALADIN],
classColors[CLASS_ARCHER], classColors[CLASS_CLERIC],
classColors[CLASS_SORCERER], classColors[CLASS_ROBBER],
classColors[CLASS_NINJA], classColors[CLASS_BARBARIAN],
classColors[CLASS_DRUID], classColors[CLASS_RANGER],
skillStr.c_str(), raceSkillStr.c_str(), classStr.c_str()
);
return classId == -1 ? foundClass : selectedClass;
}
void CreateCharacterDialog::printSelectionArrow(int selectedClass) {
Windows &windows = *_vm->_windows;
Window &w = windows[0];
_icons.draw(0, 61, Common::Point(220, 19));
_icons.draw(0, 63, Common::Point(220, selectedClass * 11 + 21));
w.update();
}
void CreateCharacterDialog::drawDice() {
EventsManager &events = *_vm->_events;
Windows &windows = *_vm->_windows;
Window &w = windows[32];
// Draw the dice area background
events.updateGameCounter();
_dice.draw(w, 7, Common::Point(12, 11));
// Iterate through each of the three dice
for (int diceNum = 0; diceNum < 3; ++diceNum) {
_diceFrame[diceNum] = (_diceFrame[diceNum] + 1) % 7;
_dicePos[diceNum] += _diceInc[diceNum];
if (_dicePos[diceNum].x < 13) {
_dicePos[diceNum].x = 13;
_diceInc[diceNum].x *= -1;
} else if (_dicePos[diceNum].x >= (163 - _diceSize.x)) {
_dicePos[diceNum].x = 163 - _diceSize.x;
_diceInc[diceNum].x *= -1;
}
if (_dicePos[diceNum].y < 12) {
_dicePos[diceNum].y = 12;
_diceInc[diceNum].y *= -1;
} else if (_dicePos[diceNum].y >= (93 - _diceSize.y)) {
_dicePos[diceNum].y = 93 - _diceSize.y;
_diceInc[diceNum].y *= -1;
}
_dice.draw(w, _diceFrame[diceNum], _dicePos[diceNum]);
}
// Wait for a single frame, checking for any events
w.update();
events.wait(1);
checkEvents(_vm);
}
int CreateCharacterDialog::getAttribFromKeycode(int keycode) const {
if (Res.KeyConstants.DialogsCreateChar.KEY_MGT == keycode)
return MIGHT;
else if (Res.KeyConstants.DialogsCreateChar.KEY_INT == keycode)
return INTELLECT;
else if (Res.KeyConstants.DialogsCreateChar.KEY_PER == keycode)
return PERSONALITY;
else if (Res.KeyConstants.DialogsCreateChar.KEY_END == keycode)
return ENDURANCE;
else if (Res.KeyConstants.DialogsCreateChar.KEY_SPD == keycode)
return SPEED;
else if (Res.KeyConstants.DialogsCreateChar.KEY_ACY == keycode)
return ACCURACY;
else if (Res.KeyConstants.DialogsCreateChar.KEY_LCK == keycode)
return LUCK;
else
return -1;
}
bool CreateCharacterDialog::swapAttributes(int keycode) {
Windows &windows = *_vm->_windows;
Window &w = windows[0];
int srcAttrib = getAttribFromKeycode(keycode);
assert(srcAttrib >= 0);
_vm->_mode = MODE_86;
_icons.draw(w, srcAttrib * 2 + 11, Common::Point(
_buttons[srcAttrib + 5]._bounds.left, _buttons[srcAttrib + 5]._bounds.top));
w.update();
int destAttrib = exchangeAttribute(srcAttrib);
if (destAttrib != -1) {
_icons.draw(w, destAttrib * 2 + 11, Common::Point(
_buttons[destAttrib + 5]._bounds.left,
_buttons[destAttrib + 5]._bounds.top));
SWAP(_attribs[srcAttrib], _attribs[destAttrib]);
return true;
} else {
_icons.draw(w, srcAttrib * 2 + 10, Common::Point(
_buttons[srcAttrib + 5]._bounds.left,
_buttons[srcAttrib + 5]._bounds.top));
w.update();
_vm->_mode = MODE_SLEEPING;
return false;
}
}
int CreateCharacterDialog::exchangeAttribute(int srcAttr) {
EventsManager &events = *_vm->_events;
Windows &windows = *_vm->_windows;
SpriteResource icons;
icons.load("create2.icn");
saveButtons();
addButton(Common::Rect(118, 58, 142, 78), Common::KEYCODE_ESCAPE, &_icons);
addButton(Common::Rect(168, 19, 192, 39), Res.KeyConstants.DialogsCreateChar.KEY_MGT, nullptr, kCreateCharacterSwapMight);
addButton(Common::Rect(168, 43, 192, 63), Res.KeyConstants.DialogsCreateChar.KEY_INT, nullptr, kCreateCharacterSwapIntellect);
addButton(Common::Rect(168, 67, 192, 87), Res.KeyConstants.DialogsCreateChar.KEY_PER, nullptr, kCreateCharacterSwapPersonality);
addButton(Common::Rect(168, 91, 192, 111), Res.KeyConstants.DialogsCreateChar.KEY_END, nullptr, kCreateCharacterSwapEndurance);
addButton(Common::Rect(168, 115, 192, 135), Res.KeyConstants.DialogsCreateChar.KEY_SPD, nullptr, kCreateCharacterSwapSpeed);
addButton(Common::Rect(168, 139, 192, 159), Res.KeyConstants.DialogsCreateChar.KEY_ACY, nullptr, kCreateCharacterSwapAccuracy);
addButton(Common::Rect(168, 163, 192, 183), Res.KeyConstants.DialogsCreateChar.KEY_LCK, nullptr, kCreateCharacterSwapLuck);
#ifdef USE_TTS
for (uint i = 0; i < TOTAL_ATTRIBUTES; ++i) {
_buttonTexts.push_back(Res.STAT_NAMES[i]);
}
_vm->stopTextToSpeech();
#endif
Window &w = windows[26];
w.open();
w.writeString(Common::String::format(Res.EXCHANGE_ATTR_WITH, Res.STAT_NAMES[srcAttr]));
icons.draw(w, 0, Common::Point(118, 58));
w.update();
int result = -1;
bool breakFlag = false;
while (!_vm->shouldExit() && !breakFlag) {
// Wait for an action
do {
events.pollEventsAndWait();
checkEvents(_vm);
} while (!_vm->shouldExit() && !_buttonValue);
if (_buttonValue == Common::KEYCODE_ESCAPE)
break;
int destAttr = getAttribFromKeycode(_buttonValue);
if (destAttr != -1 && srcAttr != destAttr) {
result = destAttr;
break;
}
}
w.close();
restoreButtons();
#ifdef USE_TTS
for (uint i = 0; i < TOTAL_ATTRIBUTES; ++i) {
_buttonTexts.pop_back();
}
#endif
_buttonValue = 0;
return result;
}
bool CreateCharacterDialog::saveCharacter(Character &c, int classId, Race race, Sex sex) {
if (classId == -1) {
ErrorScroll::show(_vm, Res.SELECT_CLASS_BEFORE_SAVING);
return false;
}
Map &map = *_vm->_map;
Party &party = *_vm->_party;
Windows &windows = *_vm->_windows;
Window &w = windows[6];
Common::String name;
int result;
int ccNum = _vm->_files->_ccNum;
// Prompt for a character name
w.open();
w.writeString(Res.NAME_FOR_NEW_CHARACTER);
saveButtons();
result = Input::show(_vm, &w, name, 10, 200);
restoreButtons();
w.close();
if (!result)
// Name aborted, so exit
return false;
#ifdef USE_TTS
_vm->sayText(name, Common::TextToSpeechManager::INTERRUPT);
#endif
// Save new character details
c.clear();
c._name = name;
c._savedMazeId = party._priorMazeId;
c._xeenSide = map._loadCcNum;
c._sex = sex;
c._race = race;
c._class = (CharacterClass)classId;
c._level._permanent = ccNum ? 5 : 1;
c._might._permanent = _attribs[MIGHT];
c._intellect._permanent = _attribs[INTELLECT];
c._personality._permanent = _attribs[PERSONALITY];
c._endurance._permanent = _attribs[ENDURANCE];
c._speed._permanent = _attribs[SPEED];
c._accuracy._permanent = _attribs[ACCURACY];
c._luck._permanent = _attribs[LUCK];
c._magicResistance._permanent = Res.RACE_MAGIC_RESISTANCES[race];
c._fireResistance._permanent = Res.RACE_FIRE_RESISTANCES[race];
c._electricityResistance._permanent = Res.RACE_ELECTRIC_RESISTANCES[race];
c._coldResistance._permanent = Res.RACE_COLD_RESISTANCES[race];
c._energyResistance._permanent = Res.RACE_ENERGY_RESISTANCES[race];
c._poisonResistance._permanent = Res.RACE_POISON_RESISTANCES[race];
c._birthYear = party._year - 18;
c._birthDay = party._day;
c._hasSpells = false;
c._currentSpell = -1;
// Set up any default spells for the character's class
for (int idx = 0; idx < 4; ++idx) {
if (Res.NEW_CHARACTER_SPELLS[c._class][idx] != -1) {
c._hasSpells = true;
c._currentSpell = Res.NEW_CHARACTER_SPELLS[c._class][idx];
c._spells[c._currentSpell] = true;
}
}
int classSkill = Res.NEW_CHAR_SKILLS[c._class];
if (classSkill != -1)
c._skills[classSkill] = 1;
int raceSkill = Res.NEW_CHAR_RACE_SKILLS[c._race];
if (raceSkill != -1)
c._skills[raceSkill] = 1;
c._currentHp = c.getMaxHP();
c._currentSp = c.getMaxSP();
return true;
}
#ifdef USE_TTS
void CreateCharacterDialog::speakText(const Common::String &text, bool hasAttributeLabels, bool classSelected, int selectedClass) {
_vm->stopTextToSpeech();
uint index = 0;
bool addNewButtons = _buttonTexts.empty();
Common::String buttonTexts;
if (!hasAttributeLabels) {
// Roll/create/ESC buttons
if (addNewButtons) {
buttonTexts = addNextTextToButtons(text, index, kCreateCharacterSideButtonCount);
} else {
buttonTexts = getNextTextSection(text, index, kCreateCharacterSideButtonCount);
}
for (uint i = 0; i < TOTAL_ATTRIBUTES; ++i) {
getNextTextSection(text, index);
}
}
// Race/sex/class info
_vm->sayText(getNextTextSection(text, index, kCreateCharacterBasicInfoCount));
uint classIndex = 0;
// The selected class is at the very end of the string, but it should be voiced with the rest of the race/sex/class
// info, so find it here early and voice it
if (classSelected) {
uint endClassIndex = text.findLastNotOf('\n');
if (endClassIndex != Common::String::npos) {
for (uint i = endClassIndex; i >= index; --i) {
if (text[i] == '\n') {
classIndex = i;
_vm->sayText(text.substr(classIndex));
break;
}
}
}
}
if (!hasAttributeLabels) {
_vm->sayText(buttonTexts);
}
// Get attribute values
for (uint i = 0; i < TOTAL_ATTRIBUTES; ++i) {
Common::String attribute = Common::String(Res.STAT_NAMES[i]) + ": " + getNextTextSection(text, index);
if (addNewButtons) {
_buttonTexts.push_back(attribute);
} else {
_buttonTexts[i + kCreateCharacterSideButtonCount] = attribute;
}
_vm->sayText(attribute);
}
// Classes
for (int i = 0; i < TOTAL_CLASSES; ++i) {
Common::String buttonText;
if (_allowedClasses[i]) {
buttonText = getNextTextSection(text, index);
} else {
buttonText = "";
getNextTextSection(text, index);
}
if (addNewButtons) {
_buttonTexts.push_back(buttonText);
} else {
_buttonTexts[i + TOTAL_CLASSES] = buttonText;
}
if (i == selectedClass) {
_vm->sayText(buttonText);
}
}
// Skills
_vm->sayText(text.substr(index, classIndex - index));
}
#endif
} // End of namespace Xeen
} // End of namespace MM

View File

@@ -0,0 +1,136 @@
/* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef XEEN_DIALOGS_CREATE_CHAR_H
#define XEEN_DIALOGS_CREATE_CHAR_H
#include "mm/xeen/dialogs/dialogs.h"
#include "mm/xeen/character.h"
namespace MM {
namespace Xeen {
class CreateCharacterDialog : public ButtonContainer {
private:
SpriteResource _icons;
SpriteResource _dice;
Common::Point _diceSize;
int _diceFrame[3];
Common::Point _dicePos[3];
Common::Point _diceInc[3];
uint _attribs[TOTAL_ATTRIBUTES];
bool _allowedClasses[TOTAL_CLASSES];
private:
/**
* Constructor
*/
CreateCharacterDialog(XeenEngine *vm);
/**
* Loads the buttons for the dialog
*/
void loadButtons();
/**
* Draws on-screen icons
*/
void drawIcons();
/**
* Draws on-screen icons
*/
void drawIcons2();
/**
* Animate the dice rolling around
*/
void drawDice();
/**
* Executes the dialog
*/
void execute();
/**
* Returns the attribute that a given keycode represents
*/
int getAttribFromKeycode(int keycode) const;
/**
* Handles the logic for swapping attributes
* @param keycode Key pressed representing one of the attributes
* @returns True if swap occurred
*/
bool swapAttributes(int keycode);
/**
* Exchanging two attributes for the character being rolled
*/
int exchangeAttribute(int srcAttr);
/**
* Set a list of flags for which classes the passed attribute set meet the
* minimum requirements of
*/
void checkClass();
/**
* Return details of the generated character
*/
int newCharDetails(Race race, Sex sex, int classId, int selectedClass, Common::String &msg);
/**
* Print the selection arrow to indicate the selected class
*/
void printSelectionArrow(int selectedClass);
/**
* Saves the rolled character into the roster
*/
bool saveCharacter(Character &c, int classId, Race race, Sex sex);
/**
* Roll up some random values for the attributes, and return both them as
* well as a list of classes that the attributes meet the requirements for
*/
void rollAttributes();
#ifdef USE_TTS
/**
* Voices text with TTS and sets up buttons
* @param text Text for voicing and buttons. Each section should be separated by a newline
* @param hasAttributeLabels Whether this text has attribute labels in it
* @param classSelected Whether a class is selected
* @param selectedClass Class selected by the player
*/
void speakText(const Common::String &text, bool hasAttributeLabels, bool classSelected, int selectedClass);
#endif
public:
/**
* Shows the Create Character dialog
*/
static void show(XeenEngine *vm);
};
} // End of namespace Xeen
} // End of namespace MM
#endif

View File

@@ -0,0 +1,101 @@
/* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "mm/xeen/dialogs/dialogs_difficulty.h"
#include "mm/xeen/resources.h"
#include "mm/xeen/xeen.h"
namespace MM {
namespace Xeen {
#ifdef USE_TTS
static const char *difficultyButtonsText[] = {
"Adventurer\nWarrior", // English
"Abenteurer\nK\204mpfer", // German
"Aventurier\nGuerrier", // French
"Aventurero\nGuerrero", // Spanish
"\x80\xa2\xa0\xad\xe2\xee\xe0\xa8\xe1\n\x82\xae\xa8\xad", // Russian (Авантюрис, Воин)
"\xab\x5f\xc0\x49\xbc\xd2\xa6\xa1\n\xbe\xd4\xa4\x68\xbc\xd2\xa6\xa1" // Chinese (冒險模式, 戰士模式)
};
#endif
enum DifficultyButtonTTSTextIndex {
kDifficultyAdventurer = 0,
kDifficultyWarrior = 1
};
int DifficultyDialog::show(XeenEngine *vm) {
DifficultyDialog *dlg = new DifficultyDialog(vm);
int result = dlg->execute();
delete dlg;
return result;
}
DifficultyDialog::DifficultyDialog(XeenEngine *vm) : ButtonContainer(vm) {
loadButtons();
#ifdef USE_TTS
setButtonTexts(difficultyButtonsText[_vm->_ttsLanguage]);
#endif
}
int DifficultyDialog::execute() {
EventsManager &events = *_vm->_events;
Windows &windows = *_vm->_windows;
Window &w = windows[6];
w.open();
#ifdef USE_TTS
_vm->stopTextToSpeech();
#endif
w.writeString(Res.DIFFICULTY_TEXT);
drawButtons(&w);
int result = -1;
while (!_vm->shouldExit()) {
events.pollEventsAndWait();
checkEvents(_vm);
if (Res.KeyConstants.DialogsDifficulty.KEY_ADVENTURER == _buttonValue)
result = ADVENTURER;
else if (Res.KeyConstants.DialogsDifficulty.KEY_WARRIOR == _buttonValue)
result = WARRIOR;
else if (Common::KEYCODE_ESCAPE != _buttonValue)
continue;
break;
}
w.close();
return result;
}
void DifficultyDialog::loadButtons() {
_sprites.load("choice.icn");
addButton(Common::Rect(68, 167, 158, 187), Res.KeyConstants.DialogsDifficulty.KEY_ADVENTURER, &_sprites, kDifficultyAdventurer);
addButton(Common::Rect(166, 167, 256, 187), Res.KeyConstants.DialogsDifficulty.KEY_WARRIOR, &_sprites, kDifficultyWarrior);
}
} // End of namespace Xeen
} // End of namespace MM

View File

@@ -0,0 +1,61 @@
/* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef XEEN_DIALOGS_DIFFICULTY_H
#define XEEN_DIALOGS_DIFFICULTY_H
#include "mm/xeen/dialogs/dialogs.h"
#include "mm/xeen/party.h"
namespace MM {
namespace Xeen {
class DifficultyDialog : public ButtonContainer {
private:
SpriteResource _sprites;
/**
* Constructor
*/
DifficultyDialog(XeenEngine *vm);
/**
* Shows the dialog
*/
int execute();
/**
* Loads buttons for the dialog
*/
void loadButtons();
public:
/**
* Shows the difficulty selection dialog
* @param vm Engine reference
* @returns 0=Adventurer, 1=Warrior, -1 exit
*/
static int show(XeenEngine *vm);
};
} // End of namespace Xeen
} // End of namespace MM
#endif

View File

@@ -0,0 +1,104 @@
/* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "mm/xeen/dialogs/dialogs_dismiss.h"
#include "mm/xeen/party.h"
#include "mm/xeen/resources.h"
#include "mm/xeen/xeen.h"
namespace MM {
namespace Xeen {
void Dismiss::show(XeenEngine *vm) {
Dismiss *dlg = new Dismiss(vm);
dlg->execute();
delete dlg;
}
void Dismiss::execute() {
EventsManager &events = *_vm->_events;
Interface &intf = *_vm->_interface;
Party &party = *_vm->_party;
Windows &windows = *_vm->_windows;
loadButtons();
Window &w = windows[31];
w.open();
bool ttsVoiceText = true;
bool breakFlag = false;
while (!_vm->shouldExit() && !breakFlag) {
do {
events.updateGameCounter();
intf.draw3d(false, false);
w.frame();
w.fill();
w.writeString(Res.DISMISS_WHOM, ttsVoiceText);
_iconSprites.draw(w, 0, Common::Point(225, 120));
w.update();
ttsVoiceText = false;
do {
events.pollEventsAndWait();
checkEvents(_vm);
} while (!_vm->shouldExit() && !_buttonValue && events.timeElapsed() < 2);
} while (!_vm->shouldExit() && !_buttonValue);
if (_buttonValue >= Common::KEYCODE_F1 && _buttonValue <= Common::KEYCODE_F6) {
_buttonValue -= Common::KEYCODE_F1;
if (_buttonValue < (int)party._activeParty.size()) {
if (party._activeParty.size() == 1) {
w.close();
ErrorScroll::show(_vm, Res.CANT_DISMISS_LAST_CHAR, WT_NONFREEZED_WAIT);
w.open();
} else if (party._activeParty[_buttonValue]._weapons.hasElderWeapon()) {
w.close();
ErrorScroll::show(_vm, Res.DELETE_CHAR_WITH_ELDER_WEAPON, WT_NONFREEZED_WAIT);
w.open();
} else {
// Remove the character from the party
party._activeParty.remove_at(_buttonValue);
breakFlag = true;
}
break;
}
} else if (_buttonValue == Common::KEYCODE_ESCAPE) {
breakFlag = true;
}
}
w.close();
intf.drawParty(true);
}
void Dismiss::loadButtons() {
_iconSprites.load("esc.icn");
addButton(Common::Rect(225, 120, 249, 140), Common::KEYCODE_ESCAPE, &_iconSprites);
addButton(Common::Rect(16, 16, 48, 48), Common::KEYCODE_1);
addButton(Common::Rect(117, 16, 149, 48), Common::KEYCODE_2);
addButton(Common::Rect(16, 59, 48, 91), Common::KEYCODE_3);
addButton(Common::Rect(117, 59, 149, 91), Common::KEYCODE_4);
}
} // End of namespace Xeen
} // End of namespace MM

View File

@@ -0,0 +1,47 @@
/* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef XEEN_DIALOGS_DISMISS_H
#define XEEN_DIALOGS_DISMISS_H
#include "mm/xeen/dialogs/dialogs.h"
#include "mm/xeen/party.h"
namespace MM {
namespace Xeen {
class Dismiss : public ButtonContainer {
private:
SpriteResource _iconSprites;
Dismiss(XeenEngine *vm) : ButtonContainer(vm) {}
void execute();
void loadButtons();
public:
static void show(XeenEngine *vm);
};
} // End of namespace Xeen
} // End of namespace MM
#endif

View File

@@ -0,0 +1,81 @@
/* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "mm/xeen/dialogs/dialogs_exchange.h"
#include "mm/xeen/resources.h"
#include "mm/xeen/xeen.h"
namespace MM {
namespace Xeen {
void ExchangeDialog::show(XeenEngine *vm, Character *&c, int &charIndex) {
ExchangeDialog *dlg = new ExchangeDialog(vm);
dlg->execute(c, charIndex);
delete dlg;
}
void ExchangeDialog::execute(Character *&c, int &charIndex) {
EventsManager &events = *_vm->_events;
Interface &intf = *_vm->_interface;
Party &party = *_vm->_party;
Windows &windows = *_vm->_windows;
loadButtons();
Window &w = windows[31];
w.open();
w.writeString(Res.EXCHANGE_WITH_WHOM);
_iconSprites.draw(w, 0, Common::Point(225, 120));
w.update();
while (!_vm->shouldExit()) {
events.pollEventsAndWait();
checkEvents(_vm);
if (_buttonValue >= Common::KEYCODE_F1 && _buttonValue <= Common::KEYCODE_F6) {
_buttonValue -= Common::KEYCODE_F1;
if (_buttonValue < (int)party._activeParty.size() && _buttonValue != charIndex) {
SWAP(party._activeParty[charIndex], party._activeParty[_buttonValue]);
charIndex = _buttonValue;
c = &party._activeParty[charIndex];
break;
}
} else if (_buttonValue == Common::KEYCODE_ESCAPE) {
break;
}
}
w.close();
intf.drawParty(true);
intf.highlightChar(charIndex);
}
void ExchangeDialog::loadButtons() {
_iconSprites.load("esc.icn");
addButton(Common::Rect(225, 120, 249, 245), Common::KEYCODE_ESCAPE, &_iconSprites);
addButton(Common::Rect(16, 16, 48, 48), Common::KEYCODE_1);
addButton(Common::Rect(117, 16, 149, 48), Common::KEYCODE_2);
addButton(Common::Rect(16, 59, 48, 91), Common::KEYCODE_3);
addButton(Common::Rect(117, 59, 149, 91), Common::KEYCODE_4);
}
} // End of namespace Xeen
} // End of namespace MM

View File

@@ -0,0 +1,47 @@
/* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef XEEN_DIALOGS_EXCHANGE_H
#define XEEN_DIALOGS_EXCHANGE_H
#include "mm/xeen/dialogs/dialogs.h"
#include "mm/xeen/party.h"
namespace MM {
namespace Xeen {
class ExchangeDialog : public ButtonContainer {
private:
SpriteResource _iconSprites;
ExchangeDialog(XeenEngine *vm) : ButtonContainer(vm) {}
void execute(Character *&c, int &charIndex);
void loadButtons();
public:
static void show(XeenEngine *vm, Character *&c, int &charIndex);
};
} // End of namespace Xeen
} // End of namespace MM
#endif

View File

@@ -0,0 +1,169 @@
/* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "mm/xeen/dialogs/dialogs_info.h"
#include "mm/xeen/resources.h"
#include "mm/xeen/xeen.h"
namespace MM {
namespace Xeen {
#ifdef USE_TTS
static const uint8 kInfoTimeCount = 3;
#endif
void InfoDialog::show(XeenEngine *vm) {
InfoDialog *dlg = new InfoDialog(vm);
dlg->execute();
delete dlg;
}
void InfoDialog::execute() {
EventsManager &events = *_vm->_events;
Interface &intf = *_vm->_interface;
Party &party = *_vm->_party;
Windows &windows = *_vm->_windows;
protectionText();
Common::String statusText = "";
for (uint idx = 0; idx < _lines.size(); ++idx)
statusText += _lines[idx];
Common::String gameName;
if (_vm->getGameID() == GType_Swords)
gameName = Res.SWORDS_GAME_TEXT;
else if (_vm->getGameID() == GType_Clouds)
gameName = Res.CLOUDS_GAME_TEXT;
else if (_vm->getGameID() == GType_DarkSide)
gameName = Res.DARKSIDE_GAME_TEXT;
else
gameName = Res.WORLD_GAME_TEXT;
// Form the display message
int hour = party._minutes / 60;
Common::String details = Common::String::format(Res.GAME_INFORMATION,
gameName.c_str(), Res.WEEK_DAY_STRINGS[party._day % 10],
(hour > 12) ? hour - 12 : (!hour ? 12 : hour),
party._minutes % 60, (hour > 11) ? 'p' : 'a',
party._day, party._year, statusText.c_str());
Window &w = windows[28];
w.setBounds(Common::Rect(88, 20, 248, 112 + (_lines.empty() ? 0 : _lines.size() * 9 + 13)));
w.open();
Common::String ttsMessage;
w.writeString(details, false, &ttsMessage);
#ifdef USE_TTS
_vm->stopTextToSpeech();
speakText(ttsMessage);
#endif
do {
events.updateGameCounter();
intf.draw3d(false, false);
w.frame();
w.writeString(details, false);
w.update();
events.wait(1);
} while (!_vm->shouldExit() && !events.isKeyMousePressed());
#ifdef USE_TTS
_vm->stopTextToSpeech();
#endif
events.clearEvents();
w.close();
}
#ifdef USE_TTS
void InfoDialog::speakText(const Common::String &text) const {
uint index = 0;
_vm->sayText(getNextTextSection(text, index, kInfoTimeCount));
Common::String timeInfo[kInfoTimeCount];
// Time, day, and year headers
for (uint i = 0; i < kInfoTimeCount; ++i) {
timeInfo[i] = getNextTextSection(text, index);
}
// Time, day, and year numbers
for (uint i = 0; i < kInfoTimeCount; ++i) {
_vm->sayText(timeInfo[i] + ": " + getNextTextSection(text, index));
}
// Any remaining lines
_vm->sayText(text.substr(index));
}
#endif
void InfoDialog::protectionText() {
Party &party = *_vm->_party;
// Common::StringArray _lines;
const char *const AA_L024 = "\x3l\n\x9""024";
const char *const AA_R124 = "\x3r\x9""124";
if (party._lightCount) {
_lines.push_back(Common::String::format(Res.LIGHT_COUNT_TEXT, party._lightCount));
}
if (party._fireResistance) {
_lines.push_back(Common::String::format(Res.FIRE_RESISTANCE_TEXT,
_lines.size() == 0 ? 10 : 1, AA_L024, AA_R124, party._fireResistance));
}
if (party._electricityResistance) {
_lines.push_back(Common::String::format(Res.ELECRICITY_RESISTANCE_TEXT,
_lines.size() == 0 ? 10 : 1, AA_L024, AA_R124, party._electricityResistance));
}
if (party._coldResistance) {
_lines.push_back(Common::String::format(Res.COLD_RESISTANCE_TEXT,
_lines.size() == 0 ? 10 : 1, AA_L024, AA_R124, party._coldResistance));
}
if (party._poisonResistance) {
_lines.push_back(Common::String::format(Res.POISON_RESISTANCE_TEXT,
_lines.size() == 0 ? 10 : 1, AA_L024, AA_R124, party._poisonResistance));
}
if (party._clairvoyanceActive) {
_lines.push_back(Common::String::format(Res.CLAIRVOYANCE_TEXT,
_lines.size() == 0 ? 10 : 1, AA_L024, AA_R124));
}
if (party._levitateCount) {
_lines.push_back(Common::String::format(Res.LEVITATE_TEXT,
_lines.size() == 0 ? 10 : 1, AA_L024, AA_R124));
}
if (party._walkOnWaterActive) {
_lines.push_back(Common::String::format(Res.WALK_ON_WATER_TEXT,
_lines.size() == 0 ? 10 : 1, AA_L024, AA_R124));
}
}
} // End of namespace Xeen
} // End of namespace MM

View File

@@ -0,0 +1,51 @@
/* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef XEEN_DIALOGS_INFO_H
#define XEEN_DIALOGS_INFO_H
#include "common/str-array.h"
#include "mm/xeen/dialogs/dialogs.h"
namespace MM {
namespace Xeen {
class InfoDialog : public ButtonContainer {
private:
Common::StringArray _lines;
InfoDialog(XeenEngine *vm) : ButtonContainer(vm) {}
void execute();
void protectionText();
#ifdef USE_TTS
void speakText(const Common::String &text) const;
#endif
public:
static void show(XeenEngine *vm);
};
} // End of namespace Xeen
} // End of namespace MM
#endif

View File

@@ -0,0 +1,409 @@
/* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "mm/xeen/dialogs/dialogs_input.h"
#include "mm/xeen/scripts.h"
#include "mm/xeen/xeen.h"
namespace MM {
namespace Xeen {
int Input::show(XeenEngine *vm, Window *window, Common::String &line,
uint maxLen, int maxWidth, bool isNumeric) {
Input *dlg = new Input(vm, window);
int result = dlg->getString(line, maxLen, maxWidth, isNumeric);
delete dlg;
return result;
}
int Input::nonEnToLower(uint16 ascii) {
if (Common::RU_RUS == g_vm->getLanguage()) {
switch (ascii) {
case Common::KEYCODE_f: return 0xA0; // А
case Common::KEYCODE_COMMA: return 0xA1; // Б
case Common::KEYCODE_d: return 0xA2; // В
case Common::KEYCODE_u: return 0xA3; // Г
case Common::KEYCODE_l: return 0xA4; // Д
case Common::KEYCODE_t: return 0xA5; // Е
case Common::KEYCODE_BACKQUOTE: return 0xF1; // Ё
case Common::KEYCODE_SEMICOLON: return 0xA6; // Ж
case Common::KEYCODE_p: return 0xA7; // З
case Common::KEYCODE_b: return 0xA8; // И
case Common::KEYCODE_q: return 0xA9; // Й
case Common::KEYCODE_r: return 0xAA; // К
case Common::KEYCODE_k: return 0xAB; // Л
case Common::KEYCODE_v: return 0xAC; // М
case Common::KEYCODE_y: return 0xAD; // Н
case Common::KEYCODE_j: return 0xAE; // О
case Common::KEYCODE_g: return 0xAF; // П
case Common::KEYCODE_h: return 0xE0; // Р
case Common::KEYCODE_c: return 0xE1; // С
case Common::KEYCODE_n: return 0xE2; // Т
case Common::KEYCODE_e: return 0xE3; // У
case Common::KEYCODE_a: return 0xE4; // Ф
case Common::KEYCODE_LEFTBRACKET: return 0xE5; // Х
case Common::KEYCODE_w: return 0xE6; // Ц
case Common::KEYCODE_x: return 0xE7; // Ч
case Common::KEYCODE_i: return 0xE8; // Ш
case Common::KEYCODE_o: return 0xE9; // Щ
case Common::KEYCODE_RIGHTBRACKET: return 0xEA; // Ъ
case Common::KEYCODE_s: return 0xEB; // Ы
case Common::KEYCODE_m: return 0xEC; // Ь
case Common::KEYCODE_QUOTE: return 0xED; // Э
case Common::KEYCODE_PERIOD: return 0xEE; // Ю
case Common::KEYCODE_z: return 0xEF; // Я
default:
return tolower(ascii);
}
}
return ascii;
}
int Input::getString(Common::String &line, uint maxLen, int maxWidth, bool isNumeric) {
bool nonEnCharset = false;
_vm->_noDirectionSense = true;
Common::String msg = Common::String::format("\x3""l\t000\x4%03d\x3""c", maxWidth);
_window->writeString(msg, false);
_window->update();
while (!_vm->shouldExit()) {
Common::KeyState keyState = waitForKey(msg);
const Common::KeyCode keyCode = keyState.keycode;
bool refresh = false;
if ((keyCode == Common::KEYCODE_BACKSPACE || keyCode == Common::KEYCODE_DELETE)
&& line.size() > 0) {
line.deleteLastChar();
refresh = true;
} else if (line.size() < maxLen && (line.size() > 0 || keyCode != Common::KEYCODE_SPACE)
&& ((isNumeric && keyState.ascii >= '0' && keyState.ascii <= '9') ||
(!isNumeric && keyState.ascii >= ' ' && keyState.ascii <= (char)127))) {
if (!nonEnCharset) {
if (!isNumeric && Common::isAlpha(keyState.ascii)) {
// The original game doesn't care about Shift or Caps Locks. The
// capitalization is done for the user automatically at the beginning of
// words.
if (line.empty() || line.hasSuffix(" ")) {
line += toupper(keyState.ascii);
} else {
line += tolower(keyState.ascii);
}
} else {
line += keyState.ascii;
}
} else {
if (!isNumeric) {
line += nonEnToLower(keyState.ascii);
} else {
line += keyState.ascii;
}
}
refresh = true;
} else if (keyCode == Common::KEYCODE_RETURN || keyCode == Common::KEYCODE_KP_ENTER) {
break;
} else if (keyCode == Common::KEYCODE_ESCAPE) {
line = "";
break;
} else if (Common::RU_RUS == g_vm->getLanguage()) {
if (Common::KEYCODE_F11 == keyCode) {
nonEnCharset = true;
} else if (Common::KEYCODE_F12 == keyCode) {
nonEnCharset = false;
}
}
if (refresh) {
msg = Common::String::format("\x3""l\t000\x4%03d\x3""c%s", maxWidth, line.c_str());
_window->writeString(msg, false);
_window->update();
}
}
#ifdef USE_TTS
_vm->sayText(line, Common::TextToSpeechManager::INTERRUPT);
#endif
_vm->_noDirectionSense = false;
return line.size();
}
Common::KeyState Input::waitForKey(const Common::String &msg) {
EventsManager &events = *_vm->_events;
Interface &intf = *_vm->_interface;
Windows &windows = *_vm->_windows;
bool oldUpDoorText = intf._upDoorText;
byte oldTillMove = intf._tillMove;
intf._upDoorText = false;
intf._tillMove = 0;
bool flag = !_vm->_startupWindowActive && !windows[25]._enabled
&& _vm->_mode != MODE_FF && _vm->_mode != MODE_INTERACTIVE7;
PendingEvent pe;
while (!_vm->shouldExit()) {
events.updateGameCounter();
if (flag)
intf.draw3d(false);
_window->writeString(msg, false);
animateCursor();
_window->update();
if (flag)
windows[3].update();
events.wait(1);
if (events.getEvent(pe) && pe.isKeyboard())
break;
}
_window->writeString("", false);
_window->update();
intf._tillMove = oldTillMove;
intf._upDoorText = oldUpDoorText;
return pe._keyState;
}
void Input::animateCursor() {
// Iterate through each frame
_cursorAnimIndex = _cursorAnimIndex ? _cursorAnimIndex - 1 : 5;
static const char CURSOR_ANIMATION_IDS[] = { 32, 124, 126, 127, 126, 124 };
// Form a string for the cursor and write it out
Common::Point writePos = _window->_writePos;
_window->writeCharacter(CURSOR_ANIMATION_IDS[_cursorAnimIndex]);
_window->_writePos = writePos;
}
/*------------------------------------------------------------------------*/
StringInput::StringInput(XeenEngine *vm): Input(vm, &(*vm->_windows)[6]) {
}
int StringInput::show(XeenEngine *vm, bool type, const Common::String &msg1,
const Common::String &msg2, int opcode) {
StringInput *dlg = new StringInput(vm);
int result = dlg->execute(type, msg1, msg2, opcode);
delete dlg;
return result;
}
int StringInput::execute(bool type, const Common::String &expected,
const Common::String &title, int opcode) {
FileManager &files = *_vm->_files;
Scripts &scripts = *_vm->_scripts;
Windows &windows = *_vm->_windows;
Window &w = windows[6];
Sound &sound = *_vm->_sound;
int result = 0;
w.open();
w.writeString(Common::String::format("\r\x03""c%s\v024\t000", title.c_str()));
w.update();
Common::String line;
if (getString(line, 30, 200, false)) {
if (type) {
// WORKAROUND: Fix for incorrect answer for one of the Vowelless Knight riddles
line.toLowercase();
if (line == "iieeeoeeeouie")
line = "iieeeoeeaouie";
Common::String scriptsMsg = scripts._message;
scriptsMsg.toLowercase();
if (scriptsMsg == "iieeeoeeeouie")
scriptsMsg = "iieeeoeeaouie";
if (line == scriptsMsg) {
result = true;
} else if (!line.compareToIgnoreCase(expected)) {
result = (opcode == 55) ? -1 : 1;
}
} else {
// Load in the mirror list
MirrorEntry me;
scripts._mirror.clear();
File f(Common::Path(Common::String::format("%smirr.txt", files._ccNum ? "dark" : "xeen")), 1);
while (me.synchronize(f))
scripts._mirror.push_back(me);
f.close();
// Load in any extended mirror entries
Common::File f2;
if (f2.open(Common::Path(Common::String::format("%smirr.ext", files._ccNum ? "dark" : "xeen")))) {
while (me.synchronize(f2))
scripts._mirror.push_back(me);
f2.close();
}
if (Common::RU_RUS == g_vm->getLanguage() && GType_Clouds == g_vm->getGameID()) {
for (uint idx = 0; idx < 59; ++idx) {
if (!line.compareToIgnoreCase(Res.CLOUDS_MIRROR_LOCATIONS[idx])) {
result = idx + 1;
sound.playFX(_vm->_files->_ccNum ? 35 : 61);
break;
}
}
} else {
for (uint idx = 0; idx < scripts._mirror.size(); ++idx) {
if (!line.compareToIgnoreCase(scripts._mirror[idx]._name)) {
result = idx + 1;
sound.playFX(_vm->_files->_ccNum ? 35 : 61);
break;
}
}
}
}
}
w.close();
return result;
}
/*------------------------------------------------------------------------*/
NumericInput::NumericInput(XeenEngine *vm, int window) : Input(vm, &(*vm->_windows)[window]) {
}
int NumericInput::show(XeenEngine *vm, int window, int maxLength, int maxWidth) {
NumericInput *dlg = new NumericInput(vm, window);
int result = dlg->execute(maxLength, maxWidth);
delete dlg;
return result;
}
int NumericInput::execute(int maxLength, int maxWidth) {
Common::String line;
if (getString(line, maxLength, maxWidth, true))
return atoi(line.c_str());
else
return 0;
}
/*------------------------------------------------------------------------*/
int Choose123::show(XeenEngine *vm, uint numOptions) {
assert(numOptions <= 9);
Choose123 *dlg = new Choose123(vm);
int result = dlg->execute(numOptions);
delete dlg;
return result;
}
int Choose123::execute(uint numOptions) {
EventsManager &events = *_vm->_events;
Interface &intf = *_vm->_interface;
LocationManager &loc = *_vm->_locations;
Windows &windows = *_vm->_windows;
Resources &res = *_vm->_resources;
Mode oldMode = _vm->_mode;
_vm->_mode = MODE_DIALOG_123;
loadButtons(numOptions);
res._globalSprites.draw(0, 7, Common::Point(232, 74));
drawButtons(&windows[0]);
windows[34].update();
int result = -1;
while (result == -1) {
do {
events.updateGameCounter();
int delay;
if (loc.isActive()) {
loc.drawAnim(true);
delay = 3;
} else {
intf.draw3d(true);
delay = 1;
}
events.wait(delay);
checkEvents(_vm);
if (_vm->shouldExit())
return 0;
} while (!_buttonValue);
if (_buttonValue == Common::KEYCODE_ESCAPE) {
result = 0;
} else if (_buttonValue >= Common::KEYCODE_1 && _buttonValue < (Common::KEYCODE_1 + (int)numOptions)) {
_buttonValue -= Common::KEYCODE_0;
result = (_buttonValue == (int)numOptions) ? 0 : _buttonValue;
}
}
_vm->_mode = oldMode;
intf.mainIconsPrint();
return result;
}
void Choose123::loadButtons(uint numOptions) {
assert(numOptions > 0 && numOptions <= 9);
_iconSprites.load("choose.icn");
const int XPOS[3] = { 235, 260, 286 };
const int YPOS[3] = { 75, 96, 117 };
for (uint idx = 0; idx < numOptions; ++idx) {
Common::Rect r(24, 20);
r.moveTo(XPOS[idx % 3], YPOS[idx / 3]);
addButton(r, Common::KEYCODE_1 + idx, &_iconSprites);
}
}
/*------------------------------------------------------------------------*/
int HowMuch::show(XeenEngine *vm) {
HowMuch *dlg = new HowMuch(vm);
int result = dlg->execute();
delete dlg;
return result;
}
int HowMuch::execute() {
Windows &windows = *_vm->_windows;
Window &w = windows[6];
Common::String num;
w.open();
w.writeString(Res.HOW_MUCH);
w.update();
int lineSize = Input::show(_vm, &w, num, 8, 70, true);
w.close();
if (!lineSize)
return -1;
return atoi(num.c_str());
}
} // End of namespace Xeen
} // End of namespace MM

View File

@@ -0,0 +1,109 @@
/* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef XEEN_DIALOGS_STRING_INPUT_H
#define XEEN_DIALOGS_STRING_INPUT_H
#include "common/keyboard.h"
#include "mm/xeen/dialogs/dialogs.h"
#include "mm/xeen/screen.h"
namespace MM {
namespace Xeen {
class Input : public ButtonContainer {
private:
/**
* Draws the text input and cursor and waits until the user presses a key
*/
Common::KeyState waitForKey(const Common::String &msg);
/**
* Animates the box text cursor
*/
void animateCursor();
protected:
Window *_window;
int _cursorAnimIndex;
int nonEnToUpper(uint16 ascii);
int nonEnToLower(uint16 ascii);
/**
* Allows the user to enter a string
*/
int getString(Common::String &line, uint maxLen, int maxWidth, bool isNumeric);
Input(XeenEngine *vm, Window *window) : ButtonContainer(vm),
_window(window), _cursorAnimIndex(0) {}
public:
static int show(XeenEngine *vm, Window *window, Common::String &line,
uint maxLen, int maxWidth, bool isNumeric = false);
};
class StringInput : public Input {
protected:
StringInput(XeenEngine *vm);
int execute(bool type, const Common::String &expected,
const Common::String &title, int opcode);
public:
static int show(XeenEngine *vm, bool type, const Common::String &msg1,
const Common::String &msg2, int opcode);
};
class NumericInput : public Input {
private:
NumericInput(XeenEngine *vm, int window);
int execute(int maxLength, int maxWidth);
public:
static int show(XeenEngine *vm, int window, int maxLength, int maxWidth);
};
class Choose123 : public ButtonContainer {
private:
SpriteResource _iconSprites;
Choose123(XeenEngine *vm) : ButtonContainer(vm) {}
int execute(uint numOptions);
void loadButtons(uint numOptions);
public:
static int show(XeenEngine *vm, uint numOptions);
};
class HowMuch : public ButtonContainer {
private:
SpriteResource _iconSprites;
HowMuch(XeenEngine *vm) : ButtonContainer(vm) {}
int execute();
public:
static int show(XeenEngine *vm);
};
} // End of namespace Xeen
} // End of namespace MM
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,123 @@
/* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef XEEN_DIALOGS_ITEMS_H
#define XEEN_DIALOGS_ITEMS_H
#include "mm/xeen/dialogs/dialogs.h"
#include "mm/xeen/party.h"
#include "mm/xeen/window.h"
namespace MM {
namespace Xeen {
enum ItemsMode {
ITEMMODE_CHAR_INFO = 0, ITEMMODE_BUY = 1, ITEMMODE_SELL = 2, ITEMMODE_3 = 3,
ITEMMODE_RECHARGE = 4, ITEMMODE_5 = 5, ITEMMODE_ENCHANT = 6, ITEMMODE_COMBAT = 7, ITEMMODE_8 = 8,
ITEMMODE_REPAIR = 9, ITEMMODE_IDENTIFY = 10, ITEMMODE_TO_GOLD = 11,
ITEMMODE_INVALID = -1
};
class ItemsDialog : public ButtonContainer {
private:
SpriteResource _iconSprites;
SpriteResource _equipSprites;
Character _itemsCharacter;
Character *_oldCharacter;
DrawStruct _itemsDrawList[INV_ITEMS_TOTAL];
ItemsDialog(XeenEngine *vm) : ButtonContainer(vm), _oldCharacter(nullptr) {}
Character *execute(Character *c, ItemsMode mode);
/**
* Load the buttons for the dialog
*/
void loadButtons(ItemsMode mode, Character *&c, ItemCategory category);
/**
* Sets the equipment icon to use for each item for display
*/
void setEquipmentIcons();
/**
* Calculate the cost of an item, or charges renaming for Misc items as appropriate
*/
int calcItemCost(Character *c, int itemIndex, ItemsMode mode, int skillLevel,
ItemCategory category);
/**
* Get plural gold form
*/
const char *getGoldPlurals(int val);
int doItemOptions(Character &c, int actionIndex, int itemIndex,
ItemCategory category, ItemsMode mode);
void itemToGold(Character &c, int itemIndex, ItemCategory category, ItemsMode mode);
#ifdef USE_TTS
void speakText(const Common::String &text, uint8 headerCount, uint8 lineCount);
#endif
public:
static Character *show(XeenEngine *vm, Character *c, ItemsMode mode);
};
class ItemSelectionDialog : public ButtonContainer {
private:
SpriteResource _icons;
int _actionIndex;
InventoryItems &_items;
ItemSelectionDialog(XeenEngine *vm, int actionIndex, InventoryItems &items, const Common::StringArray &ttsItemButtonTexts) : ButtonContainer(vm),
_actionIndex(actionIndex), _items(items) {
loadButtons();
#ifdef USE_TTS
_buttonTexts = ttsItemButtonTexts;
#endif
}
/**
* Executes the dialog
* @returns Selected item index
*/
int execute();
/**
* Loads buttons
*/
void loadButtons();
public:
/**
* Shows the dialog
* @param actionIndex Current action type
* @param items Currently active items category
* @param ttsItemButtonTexts Text for each item button
* @returns Selected item index
*/
static int show(int actionIndex, InventoryItems &items, const Common::StringArray &ttsItemButtonTexts);
};
} // End of namespace Xeen
} // End of namespace MM
#endif

View File

@@ -0,0 +1,471 @@
/* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "mm/xeen/dialogs/dialogs_map.h"
#include "mm/xeen/resources.h"
#include "mm/xeen/xeen.h"
namespace MM {
namespace Xeen {
#define MAP_SIZE 16
#define MAP_DIFF (MAP_SIZE / 2)
#define MAP_XSTART 80
#define MAP_YSTART 38
#define TILE_WIDTH 10
#define TILE_HEIGHT 8
void MapDialog::show(XeenEngine *vm) {
MapDialog *dlg = new MapDialog(vm);
dlg->execute();
delete dlg;
}
void MapDialog::execute() {
EventsManager &events = *_vm->_events;
Interface &intf = *_vm->_interface;
Map &map = *_vm->_map;
Party &party = *_vm->_party;
Windows &windows = *_vm->_windows;
_pt = party._mazePosition;
_globalSprites.load("global.icn");
if (_pt.x < 8 && map.mazeData()._surroundingMazes._west == 0) {
_arrowPt.x = _pt.x * 10 + 4;
_pt.x = 7;
} else if (_pt.x > 23) {
_arrowPt.x = (byte)(_pt.x * 10 + 100);
_pt.x = 23;
} else if (_pt.x > 8 && map.mazeData()._surroundingMazes._east == 0) {
_arrowPt.x = (byte)(_pt.x * 10 + 4);
_pt.x = 7;
} else {
_arrowPt.x = 74;
}
if (_pt.y < 8 && map.mazeData()._surroundingMazes._south == 0) {
_arrowPt.y = ((15 - _pt.y) << 3) + 13;
_pt.y = 8;
} else if (_pt.y > 24) {
_arrowPt.y = ((15 - (_pt.y - 16)) << 3) + 13;
_pt.y = 24;
} else if (_pt.y >= 8 && map.mazeData()._surroundingMazes._north == 0) {
_arrowPt.y = ((15 - _pt.y) << 3) + 13;
_pt.y = 8;
} else {
_arrowPt.y = 69;
}
windows[5].open();
bool drawFlag = true;
#ifdef USE_TTS
_vm->stopTextToSpeech();
#endif
bool ttsVoiceText = true;
events.updateGameCounter();
do {
if (drawFlag)
intf.draw3d(false, false);
windows[5].writeString("\r");
if (map._isOutdoors)
drawOutdoors();
else
drawIndoors();
windows[5].frame();
if (!map._isOutdoors) {
map._tileSprites.draw(0, 52, Common::Point(76, 30));
} else if (_frameEndFlag) {
_globalSprites.draw(0, party._mazeDirection + 1,
Common::Point(_arrowPt.x + 76, _arrowPt.y + 25));
}
if (events.timeElapsed() > 5) {
// Set the flag to make the basic arrow blinking effect
_frameEndFlag = !_frameEndFlag;
events.updateGameCounter();
}
windows[5].writeString(Common::String::format(Res.MAP_TEXT,
map._mazeName.c_str(), party._mazePosition.x,
party._mazePosition.y, Res.DIRECTION_TEXT[party._mazeDirection]), ttsVoiceText);
ttsVoiceText = false;
windows[5].update();
windows[3].update();
events.ipause5(2);
drawFlag = false;
} while (!_vm->shouldExit() && !events.isKeyMousePressed());
#ifdef USE_TTS
_vm->stopTextToSpeech();
#endif
events.clearEvents();
windows[5].close();
}
void MapDialog::drawOutdoors() {
Map &map = *g_vm->_map;
int v, frame;
// Draw outdoors map
for (int yp = MAP_YSTART, mazeY = _pt.y + MAP_DIFF - 1; mazeY >= (_pt.y - MAP_DIFF);
--mazeY, yp += TILE_HEIGHT) {
for (int xp = MAP_XSTART, mazeX = _pt.x - (MAP_DIFF - 1); mazeX <= (_pt.x + MAP_DIFF);
xp += TILE_WIDTH, ++mazeX) {
v = map.mazeLookup(Common::Point(mazeX, mazeY), 0);
assert(v != INVALID_CELL);
frame = map.mazeDataCurrent()._surfaceTypes[v];
if (map._currentSteppedOn) {
map._tileSprites.draw(0, frame, Common::Point(xp, yp));
}
}
}
for (int yp = MAP_YSTART, mazeY = _pt.y + MAP_DIFF - 1; mazeY >= (_pt.y - MAP_DIFF);
--mazeY, yp += TILE_HEIGHT) {
for (int xp = MAP_XSTART, mazeX = _pt.x - (MAP_DIFF - 1); mazeX <= (_pt.x + MAP_DIFF);
xp += TILE_WIDTH, ++mazeX) {
v = map.mazeLookup(Common::Point(mazeX, mazeY), 4);
assert(v != INVALID_CELL);
frame = map.mazeDataCurrent()._wallTypes[v];
if (frame && map._currentSteppedOn)
map._tileSprites.draw(0, frame + 16, Common::Point(xp, yp));
}
}
for (int yp = MAP_YSTART, mazeY = _pt.y + MAP_DIFF - 1; mazeY >= (_pt.y - MAP_DIFF);
--mazeY, yp += TILE_HEIGHT) {
for (int xp = MAP_XSTART, mazeX = _pt.x - (MAP_DIFF - 1); mazeX <= (_pt.x + MAP_DIFF);
xp += TILE_WIDTH, ++mazeX) {
frame = map.mazeLookup(Common::Point(mazeX, mazeY), 8, 0xff);
if (frame && map._currentSteppedOn)
map._tileSprites.draw(0, frame + 32, Common::Point(xp, yp));
}
}
}
void MapDialog::drawIndoors() {
Map &map = *g_vm->_map;
Party &party = *g_vm->_party;
int v, frame;
int frame2 = _animFrame;
_animFrame = (_animFrame + 2) % 8;
// Draw indoors map
frame2 = (frame2 + 2) % 8;
// Draw default ground for all the valid explored areas
for (int yp = MAP_YSTART, mazeY = _pt.y + MAP_DIFF - 1; mazeY >= (_pt.y - MAP_DIFF);
yp += TILE_HEIGHT, --mazeY) {
for (int xp = MAP_XSTART, mazeX = _pt.x - (MAP_DIFF - 1); mazeX <= (_pt.x + MAP_DIFF);
xp += TILE_WIDTH, ++mazeX) {
v = map.mazeLookup(Common::Point(mazeX, mazeY), 0, 0xffff);
if (v != INVALID_CELL && map._currentSteppedOn)
map._tileSprites.draw(0, 0, Common::Point(xp, yp));
}
}
// Draw thinner ground tiles on the left edge of the map
for (int yp = MAP_YSTART + 5, mazeY = _pt.y + MAP_DIFF - 1; mazeY >= (_pt.y - MAP_DIFF);
yp += TILE_HEIGHT, --mazeY) {
v = map.mazeLookup(Common::Point(_pt.x - 8, mazeY), 0, 0xffff);
if (v != INVALID_CELL && map._currentSurfaceId != 0 && map._currentSteppedOn)
map._tileSprites.draw(0, 36 + map.mazeData()._surfaceTypes[
map._currentSurfaceId], Common::Point(75, yp));
}
// Draw thin tile portion on top-left corner of map
v = map.mazeLookup(Common::Point(_pt.x - 8, _pt.y + 8), 0, 0xffff);
if (v != INVALID_CELL && map._currentSurfaceId != 0 && map._currentSteppedOn)
map._tileSprites.draw(0, 36 + map.mazeData()._surfaceTypes[
map._currentSurfaceId], Common::Point(75, 35));
// Draw any thin tiles at the very top of the map
for (int xp = MAP_XSTART + 5, mazeX = _pt.x - (MAP_DIFF - 1); mazeX <= (_pt.x + MAP_DIFF);
xp += TILE_WIDTH, ++mazeX) {
v = map.mazeLookup(Common::Point(mazeX, _pt.y + 8), 0, 0xffff);
if (v != INVALID_CELL && map._currentSurfaceId != 0 && map._currentSteppedOn)
map._tileSprites.draw(0, 36 + map.mazeData()._surfaceTypes[
map._currentSurfaceId], Common::Point(xp, 35));
}
// Draw the default ground tiles
for (int yp = MAP_YSTART + 5, mazeY = _pt.y + MAP_DIFF - 1; mazeY >= (_pt.y - MAP_DIFF);
yp += TILE_HEIGHT, --mazeY) {
for (int xp = MAP_XSTART + 5, mazeX = _pt.x - (MAP_DIFF - 1); mazeX <= (_pt.x + MAP_DIFF);
xp += TILE_WIDTH, ++mazeX) {
v = map.mazeLookup(Common::Point(mazeX, mazeY), 0, 0xffff);
if (v != INVALID_CELL && map._currentSurfaceId && map._currentSteppedOn)
map._tileSprites.draw(0, 36 + map.mazeData()._surfaceTypes[
map._currentSurfaceId], Common::Point(xp, yp));
}
}
// Draw walls on left and top edges of map
for (int xp = MAP_XSTART, yp = MAP_YSTART + (MAP_SIZE - 1) * TILE_HEIGHT,
mazeX = _pt.x - (MAP_DIFF - 1), mazeY = _pt.y - MAP_DIFF;
mazeX < (_pt.x + MAP_DIFF); xp += TILE_WIDTH, yp -= TILE_HEIGHT, ++mazeX, ++mazeY) {
// Draw walls on left edge of map
v = map.mazeLookup(Common::Point(_pt.x - 8, mazeY), 12);
switch (v) {
case SURFTYPE_DIRT:
frame = 18;
break;
case SURFTYPE_SNOW:
frame = 22;
break;
case SURFTYPE_SWAMP:
case SURFTYPE_CLOUD:
frame = 16;
break;
case SURFTYPE_LAVA:
case SURFTYPE_DWATER:
frame = 2;
break;
case SURFTYPE_DESERT:
frame = 30;
break;
case SURFTYPE_ROAD:
frame = 32;
break;
case SURFTYPE_TFLR:
frame = 20;
break;
case SURFTYPE_SKY:
frame = 28;
break;
case SURFTYPE_CROAD:
frame = 14;
break;
case SURFTYPE_SEWER:
frame = frame2 + 4;
break;
case SURFTYPE_SCORCH:
frame = 24;
break;
case SURFTYPE_SPACE:
frame = 26;
break;
default:
frame = -1;
break;
}
if (frame != -1 && map._currentSteppedOn)
map._tileSprites.draw(0, frame, Common::Point(70, yp));
// Draw walls on top edge of map
v = map.mazeLookup(Common::Point(mazeX, _pt.y + 8), 0);
switch (v) {
case SURFTYPE_DIRT:
frame = 19;
break;
case SURFTYPE_GRASS:
frame = 35;
break;
case SURFTYPE_SNOW:
frame = 23;
break;
case SURFTYPE_SWAMP:
case SURFTYPE_CLOUD:
frame = 17;
break;
case SURFTYPE_LAVA:
case SURFTYPE_DWATER:
frame = 3;
break;
case SURFTYPE_DESERT:
frame = 31;
break;
case SURFTYPE_ROAD:
frame = 33;
break;
case SURFTYPE_TFLR:
frame = 21;
break;
case SURFTYPE_SKY:
frame = 29;
break;
case SURFTYPE_CROAD:
frame = 15;
break;
case SURFTYPE_SEWER:
frame = frame2 + 5;
break;
case SURFTYPE_SCORCH:
frame = 25;
break;
case SURFTYPE_SPACE:
frame = 27;
break;
default:
frame = -1;
break;
}
if (frame != -1 && map._currentSteppedOn)
map._tileSprites.draw(0, frame, Common::Point(xp, 30));
}
// Draw the walls for the remaining cells of the minimap
for (int yp = MAP_YSTART, mazeY = _pt.y + MAP_DIFF - 1, yCtr = 0; yCtr < MAP_SIZE;
yp += TILE_HEIGHT, --mazeY, ++yCtr) {
for (int xp = MAP_XSTART, mazeX = _pt.x - (MAP_DIFF - 1), xCtr = 0; xCtr < MAP_SIZE;
xp += TILE_WIDTH, ++mazeX, ++xCtr) {
// Draw the arrow if at the correct position
if ((_arrowPt.x / 10) == xCtr && (14 - (_arrowPt.y / 10)) == yCtr && _frameEndFlag) {
_globalSprites.draw(0, party._mazeDirection + 1,
Common::Point(_arrowPt.x + 81, _arrowPt.y + 29));
}
v = map.mazeLookup(Common::Point(mazeX, mazeY), 12);
switch (v) {
case 1:
frame = 18;
break;
case 2:
frame = 34;
break;
case 3:
frame = 22;
break;
case 4:
case 13:
frame = 16;
break;
case 5:
case 8:
frame = 2;
break;
case 6:
frame = 30;
break;
case 7:
frame = 32;
break;
case 9:
frame = 20;
break;
case 10:
frame = 28;
break;
case 11:
frame = 14;
break;
case 12:
frame = frame2 + 4;
break;
case 14:
frame = 24;
break;
case 15:
frame = 26;
break;
default:
frame = -1;
break;
}
if (frame != -1 && map._currentSteppedOn)
map._tileSprites.draw(0, frame, Common::Point(xp, yp));
v = map.mazeLookup(Common::Point(mazeX, mazeY), 0);
switch (v) {
case 1:
frame = 19;
break;
case 2:
frame = 35;
break;
case 3:
frame = 23;
break;
case 4:
case 13:
frame = 17;
break;
case 5:
case 8:
frame = 3;
break;
case 6:
frame = 31;
break;
case 7:
frame = 33;
break;
case 9:
frame = 21;
break;
case 10:
frame = 29;
break;
case 11:
frame = 15;
break;
case 12:
frame = frame2 + 5;
break;
case 14:
frame = 25;
break;
case 15:
frame = 27;
break;
default:
frame = -1;
break;
}
if (frame != -1 && map._currentSteppedOn)
map._tileSprites.draw(0, frame, Common::Point(xp, yp));
}
}
// Draw overlay on cells that haven't been stepped on yet
for (int yp = MAP_YSTART, mazeY = _pt.y + MAP_DIFF - 1; mazeY >= (_pt.y - MAP_DIFF);
yp += TILE_HEIGHT, --mazeY) {
for (int xp = MAP_XSTART, mazeX = _pt.x - (MAP_DIFF - 1); mazeX <= (_pt.x + MAP_DIFF);
xp += TILE_WIDTH, ++mazeX) {
v = map.mazeLookup(Common::Point(mazeX, mazeY), 0, 0xffff);
if (v == INVALID_CELL || !map._currentSteppedOn)
map._tileSprites.draw(0, 1, Common::Point(xp, yp));
}
}
}
} // End of namespace Xeen
} // End of namespace MM

View File

@@ -0,0 +1,63 @@
/* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef XEEN_DIALOGS_MAP_H
#define XEEN_DIALOGS_MAP_H
#include "mm/xeen/dialogs/dialogs.h"
namespace MM {
namespace Xeen {
class XeenEngine;
class MapDialog: public ButtonContainer {
private:
int _animFrame;
SpriteResource _globalSprites;
Common::Point _pt, _arrowPt;
bool _frameEndFlag;
private:
MapDialog(XeenEngine *vm) : ButtonContainer(vm),
_animFrame(0), _frameEndFlag(false) {}
/**
* Draws the map contents when outdoors
*/
void drawOutdoors();
/**
* Draws the map contents when indoors
*/
void drawIndoors();
/**
* Handles the display of the dialog
*/
void execute();
public:
static void show(XeenEngine *vm);
};
} // End of namespace Xeen
} // End of namespace MM
#endif

View File

@@ -0,0 +1,136 @@
/* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/scummsys.h"
#include "mm/xeen/dialogs/dialogs_message.h"
#include "mm/xeen/events.h"
#include "mm/xeen/xeen.h"
namespace MM {
namespace Xeen {
void MessageDialog::show(XeenEngine *vm, const Common::String &msg, MessageWaitType waitType) {
MessageDialog *dlg = new MessageDialog(vm);
dlg->execute(msg, waitType);
delete dlg;
}
void MessageDialog::execute(const Common::String &msg, MessageWaitType waitType) {
EventsManager &events = *_vm->_events;
Windows &windows = *_vm->_windows;
Window &w = windows[6];
#ifdef USE_TTS
_vm->stopTextToSpeech();
#endif
w.open();
w.writeString(msg);
w.update();
switch (waitType) {
case WT_FREEZE_WAIT:
while (!_vm->shouldExit() && !events.isKeyMousePressed())
events.pollEventsAndWait();
events.clearEvents();
#ifdef USE_TTS
_vm->stopTextToSpeech();
#endif
break;
case WT_ANIMATED_WAIT:
if (g_vm->_locations->isActive() && (windows[11]._enabled || _vm->_mode == MODE_INTERACTIVE7)) {
g_vm->_locations->wait();
break;
}
// fall through
case WT_NONFREEZED_WAIT:
do {
events.updateGameCounter();
_vm->_interface->draw3d(true);
events.wait(1);
if (checkEvents(_vm))
break;
} while (!_vm->shouldExit() && !_buttonValue);
#ifdef USE_TTS
_vm->stopTextToSpeech();
#endif
break;
case WT_LOC_WAIT:
g_vm->_locations->wait();
break;
default:
break;
}
w.close();
}
/*------------------------------------------------------------------------*/
void ErrorScroll::show(XeenEngine *vm, const Common::String &msg, MessageWaitType waitType) {
Common::String s = Common::String::format("\x3""c\v010\t000%s", msg.c_str());
MessageDialog::show(vm, s, waitType);
}
/*------------------------------------------------------------------------*/
void CantCast::show(XeenEngine *vm, int spellId, int componentNum) {
CantCast *dlg = new CantCast(vm);
dlg->execute(spellId, componentNum);
delete dlg;
}
void CantCast::execute(int spellId, int componentNum) {
EventsManager &events = *_vm->_events;
Sound &sound = *_vm->_sound;
Spells &spells = *_vm->_spells;
Windows &windows = *_vm->_windows;
Window &w = windows[6];
Mode oldMode = _vm->_mode;
_vm->_mode = MODE_FF;
sound.playFX(21);
w.open();
w.writeString(Common::String::format(Res.NOT_ENOUGH_TO_CAST,
Res.SPELL_CAST_COMPONENTS[componentNum - 1],
spells._spellNames[spellId].c_str()
));
w.update();
do {
events.pollEventsAndWait();
} while (!_vm->shouldExit() && !events.isKeyMousePressed());
events.clearEvents();
w.close();
#ifdef USE_TTS
_vm->stopTextToSpeech();
#endif
_vm->_mode = oldMode;
}
} // End of namespace Xeen
} // End of namespace MM

View File

@@ -0,0 +1,62 @@
/* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef XEEN_dialogs_message_H
#define XEEN_dialogs_message_H
#include "mm/xeen/dialogs/dialogs.h"
#include "mm/xeen/character.h"
namespace MM {
namespace Xeen {
enum MessageWaitType { WT_FREEZE_WAIT = 0, WT_NONFREEZED_WAIT = 1,
WT_LOC_WAIT = 2, WT_ANIMATED_WAIT = 3 };
class MessageDialog : public ButtonContainer {
private:
MessageDialog(XeenEngine *vm) : ButtonContainer(vm) { setWaitBounds(); }
void execute(const Common::String &msg, MessageWaitType waitType);
public:
static void show(XeenEngine *vm, const Common::String &msg,
MessageWaitType waitType = WT_FREEZE_WAIT);
};
class ErrorScroll {
public:
static void show(XeenEngine *vm, const Common::String &msg,
MessageWaitType waitType = WT_FREEZE_WAIT);
};
class CantCast: public ButtonContainer {
private:
CantCast(XeenEngine *vm) : ButtonContainer(vm) { setWaitBounds(); }
void execute(int spellId, int componentNum);
public:
static void show(XeenEngine *vm, int spellId, int componentNum);
};
} // End of namespace Xeen
} // End of namespace MM
#endif

View File

@@ -0,0 +1,531 @@
/* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "mm/xeen/dialogs/dialogs_char_info.h"
#include "mm/xeen/dialogs/dialogs_create_char.h"
#include "mm/xeen/dialogs/dialogs_party.h"
#include "mm/xeen/dialogs/dialogs_input.h"
#include "mm/xeen/dialogs/dialogs_query.h"
#include "mm/xeen/character.h"
#include "mm/xeen/events.h"
#include "mm/xeen/party.h"
#include "mm/xeen/xeen.h"
namespace MM {
namespace Xeen {
#ifdef USE_TTS
static const uint8 kPartyDialogFaceCount = 4;
static const uint8 kPartyDialogButtonCount = 6;
static const uint8 kPartyDialogInfoPerFaceCount = 4;
#endif
enum PartyDialogButtonTTSTextIndex {
kPartyDialogUp = 0,
kPartyDialogDown = 1,
kPartyDialogDelete = 2,
kPartyDialogRemove = 3,
kPartyDialogCreate = 4,
kPartyDialogExit = 5,
kPartyDialogFace1 = 6,
kPartyDialogFace2 = 7,
kPartyDialogFace3 = 8,
kPartyDialogFace4 = 9
};
PartyDialog::PartyDialog(XeenEngine *vm) : ButtonContainer(vm),
PartyDrawer(vm), _vm(vm) {
#ifdef USE_TTS
_faceCount = 0;
#endif
initDrawStructs();
}
void PartyDialog::show(XeenEngine *vm) {
PartyDialog *dlg = new PartyDialog(vm);
dlg->execute();
delete dlg;
}
void PartyDialog::execute() {
EventsManager &events = *_vm->_events;
FileManager &files = *_vm->_files;
Interface &intf = *_vm->_interface;
Party &party = *_vm->_party;
Screen &screen = *_vm->_screen;
Sound &sound = *_vm->_sound;
Windows &windows = *_vm->_windows;
bool modeFlag = false;
int startingChar = 0;
sound.playSong(files._ccNum ? "newbrigh.m" : "inn.m");
loadButtons();
setupBackground();
Common::String ttsMessage;
#ifdef USE_TTS
bool returnFromCreate = false;
#endif
while (!_vm->shouldExit()) {
_vm->_mode = MODE_INTERACTIVE;
// Build up a list of available characters in the Roster that are on the
// same side of Xeen as the player is currently on
loadCharacters();
Window &w = windows[11];
w.open();
setupFaces(startingChar, false);
w.writeString(Common::String::format(Res.PARTY_DIALOG_TEXT, _partyDetails.c_str()), false, &ttsMessage);
#ifdef USE_TTS
speakText(ttsMessage, returnFromCreate);
returnFromCreate = false;
#endif
w.drawList(&_faceDrawStructs[0], 4);
_uiSprites.draw(w, 0, Common::Point(16, 100));
_uiSprites.draw(w, 2, Common::Point(52, 100));
_uiSprites.draw(w, 4, Common::Point(87, 100));
_uiSprites.draw(w, 6, Common::Point(122, 100));
_uiSprites.draw(w, 8, Common::Point(157, 100));
_uiSprites.draw(w, 10, Common::Point(192, 100));
if (g_vm->getGameID() == GType_Swords)
Res._logoSprites.draw(1, 0, Common::Point(232, 9));
screen.loadPalette("mm4.pal");
if (modeFlag) {
windows[0].update();
events.setCursor(0);
screen.fadeIn();
} else {
if (_vm->getGameID() == GType_DarkSide) {
screen.fadeOut();
windows[0].update();
}
doScroll(false, false);
events.setCursor(0);
if (_vm->getGameID() == GType_DarkSide) {
screen.fadeIn();
}
}
bool breakFlag = false;
while (!_vm->shouldExit() && !breakFlag) {
do {
events.pollEventsAndWait();
checkEvents(_vm);
} while (!_vm->shouldExit() && !_buttonValue);
if (Common::KEYCODE_F1 == _buttonValue ||
Common::KEYCODE_F2 == _buttonValue ||
Common::KEYCODE_F3 == _buttonValue ||
Common::KEYCODE_F4 == _buttonValue ||
Common::KEYCODE_F5 == _buttonValue ||
Common::KEYCODE_F6 == _buttonValue) {
// Show character info
_buttonValue -= Common::KEYCODE_F1;
if (_buttonValue < (int)party._activeParty.size())
CharacterInfo::show(_vm, _buttonValue);
} else if (Common::KEYCODE_1 == _buttonValue ||
Common::KEYCODE_2 == _buttonValue ||
Common::KEYCODE_3 == _buttonValue ||
Common::KEYCODE_4 == _buttonValue) {
_buttonValue -= Common::KEYCODE_1 - 7;
if ((_buttonValue - 7 + startingChar) < (int)_charList.size()) {
// Check if the selected character is already in the party
uint idx = 0;
for (; idx < party._activeParty.size(); ++idx) {
if (_charList[_buttonValue - 7 + startingChar] ==
party._activeParty[idx]._rosterId)
break;
}
// Only add the character if they're not already in the party
if (idx == party._activeParty.size()) {
if (party._activeParty.size() == MAX_ACTIVE_PARTY) {
sound.playFX(21);
ErrorScroll::show(_vm, Res.YOUR_PARTY_IS_FULL);
} else {
// Add the character to the active party
party._activeParty.push_back(party._roster[_charList[_buttonValue - 7 + startingChar]]);
startingCharChanged(startingChar);
}
}
}
} else if (Common::KEYCODE_UP == _buttonValue ||
Common::KEYCODE_KP8 == _buttonValue) {
// Up arrow
if (startingChar > 0) {
startingChar -= 4;
startingCharChanged(startingChar);
}
} else if (Common::KEYCODE_DOWN == _buttonValue ||
Common::KEYCODE_KP2 == _buttonValue) {
// Down arrow
if (startingChar < ((int)_charList.size() - 4)) {
startingChar += 4;
startingCharChanged(startingChar);
}
} else if (Res.KeyConstants.DialogsParty.KEY_EXIT == _buttonValue ||
Common::KEYCODE_ESCAPE == _buttonValue ||
Common::KEYCODE_SPACE == _buttonValue) {
if (party._activeParty.size() == 0) {
ErrorScroll::show(_vm, Res.NO_ONE_TO_ADVENTURE_WITH);
} else {
if (_vm->_mode != MODE_STARTUP) {
for (int idx = OBSCURITY_NONE; idx >= OBSCURITY_BLACK; --idx) {
events.updateGameCounter();
intf.obscureScene((Obscurity)idx);
w.update();
while (events.timeElapsed() < 1)
events.pollEventsAndWait();
}
}
w.close();
party._mazeId = party._priorMazeId;
party.copyPartyToRoster();
//_vm->_saves->writeCharFile();
return;
}
} else if (Res.KeyConstants.DialogsParty.KEY_CREATE == _buttonValue) {
// Create
if (_charList.size() == XEEN_TOTAL_CHARACTERS) {
ErrorScroll::show(_vm, Res.YOUR_ROSTER_IS_FULL);
} else {
// Save Background
Graphics::ManagedSurface savedBg;
savedBg.copyFrom(screen);
screen.fadeOut();
w.close();
// Show the create character dialog
CreateCharacterDialog::show(_vm);
party.copyPartyToRoster();
//_vm->_saves->writeCharFile();
// Restore Background
screen.fadeOut();
screen.blitFrom(savedBg);
windows[0].update();
#ifdef USE_TTS
returnFromCreate = true;
#endif
modeFlag = true;
breakFlag = true;
}
} else if (Res.KeyConstants.DialogsParty.KEY_DELETE == _buttonValue) {
// Delete character
if (_charList.size() > 0) {
int charButtonValue = selectCharacter(true, startingChar);
if (charButtonValue != 0) {
int charIndex = charButtonValue - Common::KEYCODE_1 + startingChar;
Character &c = party._roster[_charList[charIndex]];
if (c.hasSlayerSword()) {
ErrorScroll::show(_vm, Res.HAS_SLAYER_SWORD);
} else {
Common::String msg = Common::String::format(Res.SURE_TO_DELETE_CHAR,
c._name.c_str(), Res.CLASS_NAMES[c._class]);
if (Confirm::show(_vm, msg)) {
// If the character is in the party, remove it
for (uint idx = 0; idx < party._activeParty.size(); ++idx) {
if (party._activeParty[idx]._rosterId == c._rosterId) {
party._activeParty.remove_at(idx);
break;
}
}
// Empty the character in the roster
c.clear();
loadCharacters();
startingChar = 0;
startingCharChanged(startingChar);
}
}
}
}
} else if (Res.KeyConstants.DialogsParty.KEY_REMOVE == _buttonValue) {
// Remove character
if (party._activeParty.size() > 0) {
int charButtonValue = selectCharacter(false, startingChar);
if (charButtonValue != 0) {
party.copyPartyToRoster();
party._activeParty.remove_at(charButtonValue - Common::KEYCODE_F1);
}
startingCharChanged(startingChar);
}
}
}
}
}
void PartyDialog::loadCharacters() {
Map &map = *_vm->_map;
Party &party = *_vm->_party;
_charList.clear();
for (int i = 0; i < XEEN_TOTAL_CHARACTERS; ++i) {
Character &player = party._roster[i];
if (player._name.empty() || player._xeenSide != map._loadCcNum)
continue;
_charList.push_back(i);
}
}
void PartyDialog::loadButtons() {
_uiSprites.load("inn.icn");
addButton(Common::Rect(16, 100, 40, 120), Common::KEYCODE_UP, &_uiSprites, kPartyDialogUp);
addButton(Common::Rect(52, 100, 76, 120), Common::KEYCODE_DOWN, &_uiSprites, kPartyDialogDown);
addButton(Common::Rect(87, 100, 111, 120), Res.KeyConstants.DialogsParty.KEY_DELETE, &_uiSprites, kPartyDialogDelete);
addButton(Common::Rect(122, 100, 146, 120), Res.KeyConstants.DialogsParty.KEY_REMOVE, &_uiSprites, kPartyDialogRemove);
addButton(Common::Rect(157, 100, 181, 120), Res.KeyConstants.DialogsParty.KEY_CREATE, &_uiSprites, kPartyDialogCreate);
addButton(Common::Rect(192, 100, 216, 120), Res.KeyConstants.DialogsParty.KEY_EXIT, &_uiSprites, kPartyDialogExit);
addButton(Common::Rect(0, 0, 0, 0), Common::KEYCODE_ESCAPE);
}
void PartyDialog::initDrawStructs() {
_faceDrawStructs[0] = DrawStruct(0, 0, 0);
_faceDrawStructs[1] = DrawStruct(0, 101, 0);
_faceDrawStructs[2] = DrawStruct(0, 0, 43);
_faceDrawStructs[3] = DrawStruct(0, 101, 43);
}
void PartyDialog::setupBackground() {
_vm->_screen->loadBackground("back.raw");
_vm->_interface->assembleBorder();
}
void PartyDialog::setupFaces(int firstDisplayChar, bool updateFlag) {
Party &party = *_vm->_party;
Common::String charNames[4];
Common::String charRaces[4];
Common::String charSex[4];
Common::String charClasses[4];
int posIndex;
int charId;
// Reset the button areas for the display character images
while (_buttons.size() > 7)
_buttons.remove_at(7);
addButton(Common::Rect(16, 16, 48, 48), Common::KEYCODE_1, nullptr, kPartyDialogFace1);
addButton(Common::Rect(117, 16, 149, 48), Common::KEYCODE_2, nullptr, kPartyDialogFace2);
addButton(Common::Rect(59, 59, 91, 91), Common::KEYCODE_3, nullptr, kPartyDialogFace3);
addButton(Common::Rect(117, 59, 151, 91), Common::KEYCODE_4, nullptr, kPartyDialogFace4);
#ifdef USE_TTS
_faceCount = 0;
#endif
for (posIndex = 0; posIndex < 4; ++posIndex) {
charId = (firstDisplayChar + posIndex) >= (int)_charList.size() ? -1 :
_charList[firstDisplayChar + posIndex];
bool isInParty = party.isInParty(charId);
if (charId == -1) {
while ((int)_buttons.size() >(7 + posIndex))
_buttons.remove_at(_buttons.size() - 1);
break;
}
#ifdef USE_TTS
_faceCount++;
#endif
Common::Rect &b = _buttons[7 + posIndex]._bounds;
b.moveTo((posIndex & 1) ? 117 : 16, b.top);
Character &ps = party._roster[_charList[firstDisplayChar + posIndex]];
charNames[posIndex] = isInParty ? Res.IN_PARTY : ps._name;
charRaces[posIndex] = Res.RACE_NAMES[ps._race];
charSex[posIndex] = Res.SEX_NAMES[ps._sex];
charClasses[posIndex] = Res.CLASS_NAMES[ps._class];
}
drawParty(updateFlag);
// Set up the sprite set to use for each face
for (posIndex = 0; posIndex < 4; ++posIndex) {
if ((firstDisplayChar + posIndex) >= (int)_charList.size())
_faceDrawStructs[posIndex]._sprites = nullptr;
else
_faceDrawStructs[posIndex]._sprites = party._roster[
_charList[firstDisplayChar + posIndex]]._faceSprites;
}
_partyDetails = Common::String::format(Res.PARTY_DETAILS,
charNames[0].c_str(), charRaces[0].c_str(), charSex[0].c_str(), charClasses[0].c_str(),
charNames[1].c_str(), charRaces[1].c_str(), charSex[1].c_str(), charClasses[1].c_str(),
charNames[2].c_str(), charRaces[2].c_str(), charSex[2].c_str(), charClasses[2].c_str(),
charNames[3].c_str(), charRaces[3].c_str(), charSex[3].c_str(), charClasses[3].c_str()
);
}
void PartyDialog::startingCharChanged(int firstDisplayChar) {
Windows &windows = *_vm->_windows;
Window &w = windows[11];
setupFaces(firstDisplayChar, true);
Common::String ttsMessage;
w.writeString(Common::String::format(Res.PARTY_DIALOG_TEXT, _partyDetails.c_str()), false, &ttsMessage);
#ifdef USE_TTS
speakText(ttsMessage, false);
#endif
w.drawList(_faceDrawStructs, 4);
_uiSprites.draw(w, 0, Common::Point(16, 100));
_uiSprites.draw(w, 2, Common::Point(52, 100));
_uiSprites.draw(w, 4, Common::Point(87, 100));
_uiSprites.draw(w, 6, Common::Point(122, 100));
_uiSprites.draw(w, 8, Common::Point(157, 100));
_uiSprites.draw(w, 10, Common::Point(192, 100));
w.update();
}
int PartyDialog::selectCharacter(bool isDelete, int firstDisplayChar) {
EventsManager &events = *_vm->_events;
Party &party = *_vm->_party;
Windows &windows = *_vm->_windows;
Window &w = windows[28];
SpriteResource iconSprites;
iconSprites.load("esc.icn");
w.setBounds(Common::Rect(50, isDelete ? 112 : 76, 266, isDelete ? 148 : 112));
w.open();
w.writeString(Common::String::format(Res.REMOVE_OR_DELETE_WHICH,
Res.REMOVE_DELETE[isDelete ? 1 : 0]));
iconSprites.draw(w, 0, Common::Point(225, isDelete ? 120 : 84));
w.update();
saveButtons();
addButton(Common::Rect(225, isDelete ? 120 : 84, 249, isDelete ? 140 : 104),
Common::KEYCODE_ESCAPE, &iconSprites);
addButton(Common::Rect(16, 16, 48, 48), Common::KEYCODE_1, nullptr, kPartyDialogFace1);
addButton(Common::Rect(117, 16, 149, 48), Common::KEYCODE_2, nullptr, kPartyDialogFace2);
addButton(Common::Rect(16, 59, 48, 91), Common::KEYCODE_3, nullptr, kPartyDialogFace3);
addButton(Common::Rect(117, 59, 149, 91), Common::KEYCODE_4, nullptr, kPartyDialogFace4);
addPartyButtons(_vm);
int result = -1, v;
while (!_vm->shouldExit() && result == -1) {
_buttonValue = 0;
while (!_vm->shouldExit() && !_buttonValue) {
events.pollEventsAndWait();
checkEvents(_vm);
}
switch (_buttonValue) {
case Common::KEYCODE_ESCAPE:
result = 0;
break;
case Common::KEYCODE_F1:
case Common::KEYCODE_F2:
case Common::KEYCODE_F3:
case Common::KEYCODE_F4:
case Common::KEYCODE_F5:
case Common::KEYCODE_F6:
if (!isDelete) {
v = _buttonValue - Common::KEYCODE_F1;
if (v < (int)party._activeParty.size())
result = _buttonValue;
}
break;
case Common::KEYCODE_1:
case Common::KEYCODE_2:
case Common::KEYCODE_3:
case Common::KEYCODE_4:
if (isDelete) {
v = _buttonValue - Common::KEYCODE_1;
if ((firstDisplayChar + v) < (int)_charList.size())
result = _buttonValue;
}
break;
default:
break;
}
}
w.close();
restoreButtons();
return result == -1 ? 0 : result;
}
#ifdef USE_TTS
void PartyDialog::speakText(const Common::String &text, bool isCharacterCreation) {
uint index = 0;
// After returning from the character creation screen, the text is duplicated, with the first iteration being
// outdated
if (isCharacterCreation) {
for (uint i = 0; i < _faceCount; ++i) {
getNextTextSection(text, index, kPartyDialogInfoPerFaceCount);
}
getNextTextSection(text, index, kPartyDialogButtonCount);
}
_buttonTexts.clear();
Common::String faceTexts[kPartyDialogFaceCount];
for (uint8 i = 0; i < _faceCount; ++i) {
faceTexts[i] = getNextTextSection(text, index, kPartyDialogInfoPerFaceCount);
_vm->sayText(faceTexts[i]);
}
Common::String buttonTexts = addNextTextToButtons(text, index, kPartyDialogButtonCount);
_vm->sayText(buttonTexts);
// Since the number of faces can vary, sort them after the bottom buttons
for (uint8 i = 0; i < _faceCount; ++i) {
_buttonTexts.push_back(faceTexts[i]);
}
}
#endif
} // End of namespace Xeen
} // End of namespace MM

View File

@@ -0,0 +1,100 @@
/* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef XEEN_DIALOGS_PARTY_H
#define XEEN_DIALOGS_PARTY_H
#include "common/array.h"
#include "mm/xeen/dialogs/dialogs.h"
#include "mm/xeen/interface.h"
#include "mm/xeen/screen.h"
#include "mm/xeen/sprites.h"
namespace MM {
namespace Xeen {
/**
* Shows the Party dialog that's shown when signing into an inn
*/
class PartyDialog : public ButtonContainer, public PartyDrawer {
private:
XeenEngine *_vm;
SpriteResource _uiSprites;
DrawStruct _faceDrawStructs[4];
Common::String _partyDetails;
Common::Array<int> _charList;
#ifdef USE_TTS
uint8 _faceCount;
#endif
/**
* Constructor
*/
PartyDialog(XeenEngine *vm);
/**
* Executes the dialog
*/
void execute();
/**
* Loads the list of character
*/
void loadCharacters();
/**
* Loads buttons for the dialog
*/
void loadButtons();
/**
* Initialises a list of elements to draw
*/
void initDrawStructs();
/**
* Sets up the background
*/
void setupBackground();
/**
* Sets up the faces from the avaialble roster for display in the party dialog
*/
void setupFaces(int firstDisplayChar, bool updateFlag);
void startingCharChanged(int firstDisplayChar);
int selectCharacter(bool isDelete, int firstDisplayChar);
#ifdef USE_TTS
void speakText(const Common::String &text, bool isCharacterCreation);
#endif
public:
/**
* Show the Party dialog
*/
static void show(XeenEngine *vm);
};
} // End of namespace Xeen
} // End of namespace MM
#endif

View File

@@ -0,0 +1,172 @@
/* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "mm/xeen/dialogs/dialogs_query.h"
#include "mm/xeen/xeen.h"
namespace MM {
namespace Xeen {
bool Confirm::show(XeenEngine *vm, const Common::String &msg, int mode) {
Confirm *dlg = new Confirm(vm);
bool result = dlg->execute(msg, mode);
delete dlg;
return result;
}
bool Confirm::execute(const Common::String &msg, int mode) {
EventsManager &events = *_vm->_events;
Windows &windows = *_vm->_windows;
SpriteResource confirmSprites;
confirmSprites.load("confirm.icn");
addButton(Common::Rect(129, 112, 153, 132), Common::KEYCODE_y, &confirmSprites);
addButton(Common::Rect(185, 112, 209, 132), Common::KEYCODE_n, &confirmSprites);
Window &w = windows[mode ? 22 : 21];
w.open();
if (!mode) {
confirmSprites.draw(w, 0, Common::Point(129, 112));
confirmSprites.draw(w, 2, Common::Point(185, 112));
_buttons[0]._bounds.moveTo(129, 112);
_buttons[1]._bounds.moveTo(185, 112);
} else {
if (mode & 0x80) {
clearButtons();
} else {
confirmSprites.draw(w, 0, Common::Point(120, 133));
confirmSprites.draw(w, 2, Common::Point(176, 133));
_buttons[0]._bounds.moveTo(120, 133);
_buttons[1]._bounds.moveTo(176, 133);
}
}
#ifdef USE_TTS
_vm->stopTextToSpeech();
#endif
w.writeString(msg);
w.update();
events.clearEvents();
bool result = false;
while (!_vm->shouldExit()) {
_buttonValue = 0;
while (!_vm->shouldExit() && !_buttonValue) {
events.pollEvents();
checkEvents(_vm);
}
if ((mode & 0x80) || _buttonValue == Common::KEYCODE_ESCAPE
|| _buttonValue == Common::KEYCODE_n)
break;
if (_buttonValue == Common::KEYCODE_y) {
result = true;
break;
}
}
#ifdef USE_TTS
_vm->stopTextToSpeech();
#endif
events.clearEvents();
w.close();
return result;
}
/*------------------------------------------------------------------------*/
bool YesNo::show(XeenEngine *vm, bool type, bool townFlag) {
YesNo *dlg = new YesNo(vm);
bool result = dlg->execute(type, townFlag);
delete dlg;
return result;
}
bool YesNo::execute(bool type, bool townFlag) {
EventsManager &events = *_vm->_events;
Interface &intf = *_vm->_interface;
LocationManager &loc = *_vm->_locations;
SpriteResource confirmSprites;
bool result = false;
Mode oldMode = _vm->_mode;
_vm->_mode = oldMode == MODE_7 ? MODE_8 : MODE_7;
events.clearEvents();
setWaitBounds();
if (!type) {
Map &map = *_vm->_map;
Party &party = *_vm->_party;
Resources &res = *_vm->_resources;
Windows &windows = *_vm->_windows;
confirmSprites.load("confirm.icn");
res._globalSprites.draw(0, 7, Common::Point(232, 74));
confirmSprites.draw(0, 0, Common::Point(235, 75));
confirmSprites.draw(0, 2, Common::Point(260, 75));
windows[34].update();
addButton(Common::Rect(235, 75, 259, 95), Common::KEYCODE_y, &confirmSprites);
addButton(Common::Rect(260, 75, 284, 95), Common::KEYCODE_n, &confirmSprites);
intf._face1State = map._headData[party._mazePosition.y][party._mazePosition.x]._left;
intf._face2State = map._headData[party._mazePosition.y][party._mazePosition.x]._right;
}
while (!_vm->shouldExit()) {
events.updateGameCounter();
if (loc.isActive()) {
loc.drawAnim(townFlag);
//numFrames = 3;
} else {
intf.draw3d(true);
//numFrames = 1;
}
events.wait(3);
checkEvents(_vm);
if (!_buttonValue)
continue;
if (type || _buttonValue == Common::KEYCODE_y) {
result = true;
break;
} else if (_buttonValue == Common::KEYCODE_n || _buttonValue == Common::KEYCODE_ESCAPE)
break;
}
intf._face1State = intf._face2State = 2;
_vm->_mode = oldMode;
if (!type)
intf.mainIconsPrint();
return result;
}
} // End of namespace Xeen
} // End of namespace MM

View File

@@ -0,0 +1,51 @@
/* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef XEEN_DIALOGS_QUERY_H
#define XEEN_DIALOGS_QUERY_H
#include "mm/xeen/dialogs/dialogs.h"
namespace MM {
namespace Xeen {
class Confirm : public ButtonContainer {
private:
Confirm(XeenEngine *vm) : ButtonContainer(vm) {}
bool execute(const Common::String &msg, int mode);
public:
static bool show(XeenEngine *vm, const Common::String &msg, int mode = 0);
};
class YesNo : public ButtonContainer {
private:
YesNo(XeenEngine *vm) : ButtonContainer(vm) {}
bool execute(bool type, bool townFlag);
public:
static bool show(XeenEngine *vm, bool type, bool townFlag = false);
};
} // End of namespace Xeen
} // End of namespace MM
#endif

View File

@@ -0,0 +1,308 @@
/* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/scummsys.h"
#include "mm/xeen/dialogs/dialogs_quests.h"
#include "mm/xeen/events.h"
#include "mm/xeen/party.h"
#include "mm/xeen/xeen.h"
namespace MM {
namespace Xeen {
enum QuestButtonTTSTextIndex {
kQuestItems = 0,
kQuestCurrentQuests = 1,
kQuestAutoNotes = 2,
kQuestUp = 3,
kQuestDown = 4,
kQuestExit = 5
};
#define MAX_DIALOG_LINES 128
void Quests::show(XeenEngine *vm) {
Quests *dlg = new Quests(vm);
dlg->execute();
delete dlg;
}
void Quests::execute() {
EventsManager &events = *_vm->_events;
Party &party = *_vm->_party;
Windows &windows = *_vm->_windows;
Mode oldMode = _vm->_mode;
int count = 0;
bool headerShown = false;
int topRow = 0;
const char **questItemNames = (g_vm->getGameID() == GType_Swords) ? Res.QUEST_ITEM_NAMES_SWORDS : Res.QUEST_ITEM_NAMES;
int itemsCount = (g_vm->getGameID() == GType_Swords) ? TOTAL_QUEST_ITEMS_SWORDS : TOTAL_QUEST_ITEMS;
const char *title1 = (g_vm->getGameID() == GType_Swords) ? Res.SWORDS_OF_XEEN_LINE : Res.CLOUDS_OF_XEEN_LINE;
addButtons();
loadQuestNotes();
enum { QUEST_ITEMS, CURRENT_QUESTS, AUTO_NOTES } mode = QUEST_ITEMS;
bool windowFlag;
if (windows[29]._enabled) {
windowFlag = false;
} else {
windows[29].open();
windows[30].open();
windowFlag = true;
}
Common::String ttsButtons;
windows[29].writeString(Res.QUESTS_DIALOG_TEXT, false, &ttsButtons);
#ifdef USE_TTS
_vm->stopTextToSpeech();
setButtonTexts(ttsButtons);
#endif
drawButtons(&windows[0]);
Common::String ttsMessage;
while (!_vm->shouldExit()) {
Common::String lines[MAX_DIALOG_LINES];
switch (mode) {
case QUEST_ITEMS:
for (int idx = 0; idx < itemsCount; ++idx)
lines[idx] = "\b \b*";
count = 0;
headerShown = false;
for (int idx = 0; idx < itemsCount; ++idx) {
if (party._questItems[idx]) {
if (!count ) {
if (_vm->getGameID() == GType_Swords)
lines[count++] = Res.SWORDS_OF_XEEN_LINE;
else if (idx < 35)
lines[count++] = Res.CLOUDS_OF_XEEN_LINE;
} else if (_vm->getGameID() != GType_Swords && idx >= 35 && !headerShown) {
lines[count++] = Res.DARKSIDE_OF_XEEN_LINE;
headerShown = true;
}
bool multiFlag = false;
if (_vm->getGameID() == GType_Swords) {
multiFlag = (idx == 20) || (idx == 27) || (idx == 41);
} else {
switch (idx) {
case 17:
case 26:
case 79:
case 80:
case 81:
case 82:
case 83:
case 84:
multiFlag = true;
break;
default:
break;
}
}
if (multiFlag) {
lines[count++] = Common::String::format("%d %s%c",
party._questItems[idx], questItemNames[idx],
party._questItems[idx] == 1 ? ' ' : 's');
} else {
lines[count++] = questItemNames[idx];
}
}
}
ttsMessage.clear();
if (count == 0) {
windows[30].writeString(Res.NO_QUEST_ITEMS);
} else {
windows[30].writeString(Common::String::format(Res.QUEST_ITEMS_DATA,
lines[topRow].c_str(), lines[topRow + 1].c_str(),
lines[topRow + 2].c_str(), lines[topRow + 3].c_str(),
lines[topRow + 4].c_str(), lines[topRow + 5].c_str(),
lines[topRow + 6].c_str(), lines[topRow + 7].c_str(),
lines[topRow + 8].c_str()
), false, &ttsMessage);
ttsMessage.replace('*', ' ');
#ifdef USE_TTS
_vm->sayText(ttsMessage);
#endif
}
break;
case CURRENT_QUESTS:
for (int idx = 0; idx < itemsCount; ++idx)
lines[idx] = "";
count = 0;
headerShown = false;
for (int idx = 0; idx < TOTAL_QUEST_FLAGS; ++idx) {
if (party._questFlags[idx + 1]) {
if (!count && !headerShown && (_vm->getGameID() == GType_Swords || idx < 29)) {
lines[count++] = title1;
}
if (_vm->getGameID() != GType_Swords && idx > 28 && !headerShown) {
lines[count++] = Res.DARKSIDE_OF_XEEN_LINE;
headerShown = true;
}
lines[count++] = _questNotes[idx];
}
}
if (count == 0)
lines[1] = Res.NO_CURRENT_QUESTS;
ttsMessage.clear();
windows[30].writeString(Common::String::format(Res.CURRENT_QUESTS_DATA,
lines[topRow].c_str(), lines[topRow + 1].c_str(), lines[topRow + 2].c_str()), false, &ttsMessage);
ttsMessage.replace('*', ' ');
#ifdef USE_TTS
_vm->sayText(ttsMessage);
#endif
break;
case AUTO_NOTES: {
int max, offset;
switch (_vm->getGameID()) {
case GType_Swords:
max = 49;
offset = 51;
break;
case GType_Clouds:
max = MAX_DIALOG_LINES;
offset = 31;
break;
default:
max = MAX_DIALOG_LINES;
offset = 56;
break;
}
for (int idx = 0; idx < max; ++idx)
lines[idx] = "";
count = 0;
headerShown = false;
for (int idx = 0; idx < max; ++idx) {
if (party._worldFlags[idx + (_vm->getGameID() != GType_Swords ? 1 : 0)]) {
if (!count && !headerShown && (_vm->getGameID() == GType_Swords || idx < 72)) {
lines[count++] = title1;
}
if (idx >= 72 && !headerShown) {
lines[count++] = Res.DARKSIDE_OF_XEEN_LINE;
headerShown = true;
}
lines[count++] = _questNotes[idx + offset];
}
}
if (count == 0)
lines[1] = Res.NO_AUTO_NOTES;
windows[30].writeString(Common::String::format(Res.AUTO_NOTES_DATA,
lines[topRow].c_str(), lines[topRow + 1].c_str(),
lines[topRow + 2].c_str(), lines[topRow + 3].c_str(),
lines[topRow + 4].c_str(), lines[topRow + 5].c_str(),
lines[topRow + 6].c_str(), lines[topRow + 7].c_str(),
lines[topRow + 8].c_str()
));
break;
}
default:
break;
}
windows[30].writeString("\v000\t000");
windows[24].update();
#ifdef USE_TTS
g_vm->sayText(ttsButtons);
#endif
// Key handling
_buttonValue = 0;
while (!_vm->shouldExit() && !_buttonValue) {
events.pollEventsAndWait();
checkEvents(_vm);
}
if (_buttonValue == Common::KEYCODE_ESCAPE)
break;
if (Res.KeyConstants.DialogsQuests.KEY_AUTO_NOTES == _buttonValue) {
mode = AUTO_NOTES;
topRow = 0;
} else if (Res.KeyConstants.DialogsQuests.KEY_QUEST_ITEMS == _buttonValue) {
mode = QUEST_ITEMS;
topRow = 0;
} else if (Res.KeyConstants.DialogsQuests.KEY_CURRENT_QUESTS == _buttonValue) {
mode = CURRENT_QUESTS;
topRow = 0;
} else if (Common::KEYCODE_HOME == _buttonValue) {
topRow = 0;
} else if (Common::KEYCODE_END == _buttonValue) {
topRow = MAX(count - 1, 0);
} else if (Common::KEYCODE_PAGEUP == _buttonValue) {
topRow = MAX(topRow - 3, 0);
} else if (Common::KEYCODE_PAGEDOWN == _buttonValue) {
topRow = CLIP(topRow + 3, 0, MAX(count - 1, 0));
} else if (Common::KEYCODE_UP == _buttonValue ||
Common::KEYCODE_KP8 == _buttonValue ||
Common::KEYCODE_u == _buttonValue) {
topRow = MAX(topRow - 1, 0);
} else if (Common::KEYCODE_DOWN == _buttonValue ||
Common::KEYCODE_KP2 == _buttonValue ||
Common::KEYCODE_d == _buttonValue) {
topRow = CLIP(topRow + 1, 0, MAX(count - 1, 0));
}
}
if (windowFlag) {
windows[30].close();
windows[29].close();
}
_vm->_mode = oldMode;
}
void Quests::addButtons() {
_iconSprites.load("quest.icn");
addButton(Common::Rect(12, 109, 36, 129), Res.KeyConstants.DialogsQuests.KEY_QUEST_ITEMS, &_iconSprites, kQuestItems);
addButton(Common::Rect(80, 109, 104, 129), Res.KeyConstants.DialogsQuests.KEY_CURRENT_QUESTS, &_iconSprites, kQuestCurrentQuests);
addButton(Common::Rect(148, 109, 172, 129), Res.KeyConstants.DialogsQuests.KEY_AUTO_NOTES, &_iconSprites, kQuestAutoNotes);
addButton(Common::Rect(216, 109, 240, 129), Common::KEYCODE_UP, &_iconSprites, kQuestUp);
addButton(Common::Rect(250, 109, 274, 129), Common::KEYCODE_DOWN, &_iconSprites, kQuestDown);
addButton(Common::Rect(284, 109, 308, 129), Common::KEYCODE_ESCAPE, &_iconSprites, kQuestExit);
}
void Quests::loadQuestNotes() {
File f("qnotes.bin", 1);
while (f.pos() < f.size())
_questNotes.push_back(f.readString());
f.close();
}
} // End of namespace Xeen
} // End of namespace MM

View File

@@ -0,0 +1,50 @@
/* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef XEEN_DIALOGS_QUESTS_H
#define XEEN_DIALOGS_QUESTS_H
#include "common/str-array.h"
#include "mm/xeen/dialogs/dialogs.h"
namespace MM {
namespace Xeen {
class Quests : public ButtonContainer {
private:
SpriteResource _iconSprites;
Common::StringArray _questNotes;
Quests(XeenEngine *vm) : ButtonContainer(vm) {}
void execute();
void addButtons();
void loadQuestNotes();
public:
static void show(XeenEngine *vm);
};
} // End of namespace Xeen
} // End of namespace MM
#endif

View File

@@ -0,0 +1,138 @@
/* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "mm/xeen/dialogs/dialogs_quick_fight.h"
#include "mm/xeen/resources.h"
#include "mm/xeen/xeen.h"
namespace MM {
namespace Xeen {
#ifdef USE_TTS
static const uint8 kQuickFightInfoCount = 4;
static const uint8 kQuickFightButtonCount = 2;
#endif
enum QuickFightButtonTTSTextIndex {
kQuickFightNext = 0,
kQuickFightExit = 1
};
void QuickFight::show(XeenEngine *vm, Character *currentChar) {
QuickFight *dlg = new QuickFight(vm, currentChar);
dlg->execute();
delete dlg;
}
QuickFight::QuickFight(XeenEngine *vm, Character *currentChar) : ButtonContainer(vm),
_currentChar(currentChar) {
loadButtons();
}
void QuickFight::execute() {
Combat &combat = *_vm->_combat;
EventsManager &events = *_vm->_events;
Interface &intf = *_vm->_interface;
Party &party = *_vm->_party;
Windows &windows = *_vm->_windows;
Window &w = windows[10];
w.open();
bool ttsVoiceText = true;
do {
// Draw the dialog text and buttons
Common::String msg = Common::String::format(Res.QUICK_FIGHT_TEXT,
_currentChar->_name.c_str(),
Res.QUICK_FIGHT_OPTIONS[_currentChar->_quickOption]);
Common::String ttsMessage;
w.writeString(msg, ttsVoiceText, &ttsMessage);
drawButtons(&w);
#ifdef USE_TTS
if (ttsVoiceText) {
setUpButtons(ttsMessage);
ttsVoiceText = false;
}
#endif
// Wait for selection
_buttonValue = 0;
events.updateGameCounter();
do {
intf.draw3d(false, false);
events.pollEventsAndWait();
checkEvents(_vm);
if (_vm->shouldExit())
return;
} while (!_buttonValue && !events.timeElapsed());
if (Common::KEYCODE_F1 == _buttonValue ||
Common::KEYCODE_F2 == _buttonValue ||
Common::KEYCODE_F3 == _buttonValue ||
Common::KEYCODE_F4 == _buttonValue ||
Common::KEYCODE_F5 == _buttonValue ||
Common::KEYCODE_F6 == _buttonValue) {
int charIdx = _buttonValue - Common::KEYCODE_F1;
if (charIdx < (int)combat._combatParty.size()) {
// Highlight new character
_currentChar = &party._activeParty[charIdx];
#ifdef USE_TTS
_vm->sayText(_currentChar->_name + ": " + Res.QUICK_FIGHT_OPTIONS[_currentChar->_quickOption], Common::TextToSpeechManager::INTERRUPT);
#endif
intf.highlightChar(charIdx);
}
} else if (Common::KEYCODE_n == _buttonValue ||
Res.KeyConstants.DialogsQuickFight.KEY_NEXT == _buttonValue) {
_currentChar->_quickOption = (QuickAction)(((int)_currentChar->_quickOption + 1) % 4);
#ifdef USE_TTS
_vm->sayText(Res.QUICK_FIGHT_OPTIONS[_currentChar->_quickOption], Common::TextToSpeechManager::INTERRUPT);
#endif
}
} while (_buttonValue != Common::KEYCODE_RETURN && _buttonValue != Common::KEYCODE_ESCAPE);
w.close();
events.clearEvents();
}
void QuickFight::loadButtons() {
_icons.load("train.icn");
addButton(Common::Rect(281, 108, 305, 128), Common::KEYCODE_ESCAPE, &_icons, kQuickFightExit);
addButton(Common::Rect(242, 108, 266, 128), Res.KeyConstants.DialogsQuickFight.KEY_NEXT, &_icons, kQuickFightNext);
}
#ifdef USE_TTS
void QuickFight::setUpButtons(const Common::String &text) {
if (_buttonTexts.empty()) {
uint index = 0;
getNextTextSection(text, index, kQuickFightInfoCount);
addNextTextToButtons(text, index, kQuickFightButtonCount);
}
}
#endif
} // End of namespace Xeen
} // End of namespace MM

View File

@@ -0,0 +1,69 @@
/* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef XEEN_DIALOGS_QUICK_FIGHT_H
#define XEEN_DIALOGS_QUICK_FIGHT_H
#include "mm/xeen/character.h"
#include "mm/xeen/dialogs/dialogs.h"
#include "mm/xeen/sprites.h"
namespace MM {
namespace Xeen {
class QuickFight : public ButtonContainer {
private:
SpriteResource _icons;
Character *_currentChar;
private:
/**
* Constructor
*/
QuickFight(XeenEngine *vm, Character *currentChar);
/**
* Executes the display of the dialog
*/
void execute();
/**
* Load butons for the dialog
*/
void loadButtons();
#ifdef USE_TTS
/**
* Sets up button texts
* @param text Text for buttons, with each button text separated by newlines
*/
void setUpButtons(const Common::String &text);
#endif
public:
/**
* Show the dialog
*/
static void show(XeenEngine *vm, Character *currentChar);
};
} // End of namespace Xeen
} // End of namespace MM
#endif

View File

@@ -0,0 +1,165 @@
/* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/config-manager.h"
#include "mm/xeen/dialogs/dialogs_quick_ref.h"
#include "mm/xeen/resources.h"
#include "mm/xeen/xeen.h"
namespace MM {
namespace Xeen {
#ifdef USE_TTS
static const uint8 kQuickRefUpperHeaderCount = 8;
static const uint8 kQuickRefLowerHeaderCount = 3;
#endif
void QuickReferenceDialog::show(XeenEngine *vm) {
QuickReferenceDialog *dlg = new QuickReferenceDialog(vm);
dlg->execute();
delete dlg;
}
const char *QuickReferenceDialog::getDaysPlurals(int val) {
if (Common::RU_RUS == Common::parseLanguage(ConfMan.get("language"))) {
int i = val % 100;
if (i < 5 || i > 20)
switch (val % 10) {
case 1:
return Res.DAYS[0];
case 2:
case 3:
case 4:
return Res.DAYS[1];
}
return Res.DAYS[2];
} else {
return Res.DAYS[val == 1 ? 0 : 1];
}
}
void QuickReferenceDialog::execute() {
Combat &combat = *_vm->_combat;
EventsManager &events = *_vm->_events;
Party &party = *_vm->_party;
Windows &windows = *_vm->_windows;
Common::String lines[8];
events.setCursor(0);
uint partySize = combat._globalCombat == 2 ? combat._combatParty.size() : party._activeParty.size();
for (uint idx = 0; idx < partySize; ++idx) {
Character &c = combat._globalCombat == 2 ? *combat._combatParty[idx] :
party._activeParty[idx];
const char **tmpConditions = c._sex == FEMALE ? (const char **)Res.CONDITION_NAMES_F : (const char **)Res.CONDITION_NAMES_M;
Condition condition = c.worstCondition();
lines[idx] = Common::String::format(Res.QUICK_REF_LINE,
idx * 10 + 24, idx + 1, c._name.c_str(),
Res.CLASS_NAMES[c._class][0], Res.CLASS_NAMES[c._class][1], Res.CLASS_NAMES[c._class][2],
c.statColor(c.getCurrentLevel(), c._level._permanent), c._level._permanent,
c.statColor(c._currentHp, c.getMaxHP()), c._currentHp,
c.statColor(c._currentSp, c.getMaxSP()), c._currentSp,
c.statColor(c.getArmorClass(), c.getArmorClass(true)), c.getArmorClass(),
Res.CONDITION_COLORS[condition],
tmpConditions[condition][0], tmpConditions[condition][1],
tmpConditions[condition][2], tmpConditions[condition][3]
);
}
int food = (party._food / party._activeParty.size()) / 3;
Common::String msg = Common::String::format(Res.QUICK_REFERENCE,
lines[0].c_str(), lines[1].c_str(), lines[2].c_str(),
lines[3].c_str(), lines[4].c_str(), lines[5].c_str(),
lines[6].c_str(), lines[7].c_str(),
party._gold, party._gems,
food, getDaysPlurals(food)
);
Window &w = windows[24];
bool windowOpen = w._enabled;
if (!windowOpen)
w.open();
Common::String ttsMessage;
// Turn off reduced font mode and then print everything
w.writeString("\1");
w.writeString(msg, false, &ttsMessage);
w.update();
#ifdef USE_TTS
speakText(ttsMessage, partySize);
#endif
// Wait for a key/mouse press
events.clearEvents();
while (!_vm->shouldExit() && !events.isKeyMousePressed())
events.pollEventsAndWait();
#ifdef USE_TTS
_vm->stopTextToSpeech();
#endif
events.clearEvents();
if (!windowOpen)
w.close();
}
#ifdef USE_TTS
void QuickReferenceDialog::speakText(const Common::String &text, uint partySize) const {
uint index = 0;
// "Quick reference chart"
_vm->sayText(getNextTextSection(text, index), Common::TextToSpeechManager::INTERRUPT);
// Split the header descriptors ("Name", "Cond", etc.) into an array
// If we voice everything as originally ordered, the context isn't clear (i.e. "Name, Class, Level, ..."
// will be voiced as one piece, which may make it difficult to match each field with the corresponding info of
// each party member)
Common::String headers[kQuickRefUpperHeaderCount];
for (uint8 i = 0; i < kQuickRefUpperHeaderCount; ++i) {
headers[i] = getNextTextSection(text, index);
}
// Voice each party member's description
for (uint i = 0; i < partySize; ++i) {
for (uint8 j = 0; j < kQuickRefUpperHeaderCount; ++j) {
_vm->sayText(headers[j] + ": " + getNextTextSection(text, index));
}
}
// Split the gold/gems/food descriptors into an array
for (uint8 i = 0; i < kQuickRefLowerHeaderCount; ++i) {
headers[i] = getNextTextSection(text, index);
}
// Voice the gold/gems/food amounts
for (uint8 i = 0; i < kQuickRefLowerHeaderCount; ++i) {
_vm->sayText(headers[i] + ": " + getNextTextSection(text, index));
}
}
#endif
} // End of namespace Xeen
} // End of namespace MM

View File

@@ -0,0 +1,51 @@
/* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef XEEN_DIALOGS_QUICK_REF_H
#define XEEN_DIALOGS_QUICK_REF_H
#include "mm/xeen/dialogs/dialogs.h"
namespace MM {
namespace Xeen {
class QuickReferenceDialog : public ButtonContainer {
private:
QuickReferenceDialog(XeenEngine *vm) : ButtonContainer(vm) {}
/**
* Get plural day/days form
*/
const char *getDaysPlurals(int val);
void execute();
#ifdef USE_TTS
void speakText(const Common::String &text, uint partySize) const;
#endif
public:
static void show(XeenEngine *vm);
};
} // End of namespace Xeen
} // End of namespace MM
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,216 @@
/* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef XEEN_DIALOGS_SPELLS_H
#define XEEN_DIALOGS_SPELLS_H
#include "common/array.h"
#include "mm/xeen/dialogs/dialogs.h"
#include "mm/xeen/party.h"
namespace MM {
namespace Xeen {
enum SpellDialogMode {
SPELLS_DIALOG_BUY = 0, SPELLS_DIALOG_SELECT = 1, SPELLS_DIALOG_INFO = 0x80
};
struct SpellEntry {
Common::String _name;
int _spellIndex;
int _spellId;
int _color;
SpellEntry(const Common::String &name, int spellIndex, int spellId) :
_name(name), _spellIndex(spellIndex), _spellId(spellId), _color(9) {}
};
/**
* Spells list dialog. Used for both selecting spells to cast, as well as the
* spells listing when visiting Guild locations
*/
class SpellsDialog : public ButtonContainer {
private:
SpriteResource _iconSprites;
SpriteResource _scrollSprites;
Common::Array<SpellEntry> _spells;
/**
* Constructor
*/
SpellsDialog(XeenEngine *vm) : ButtonContainer(vm) {}
/**
* Executes the dialog
*/
Character *execute(ButtonContainer *priorDialog, Character *c, int mode);
/**
* Loads buttons for the dialog
*/
void loadButtons();
/**
* Sets the spell text
*/
const char *setSpellText(Character *c, int isCasting);
#ifdef USE_TTS
/**
* Voices text with TTS and sets up buttons
* @param text Text for voicing and buttons. Each section should be separated by a newline
* @param mode Mode of the spells menu
*/
void speakText(const Common::String &text, int mode);
/**
* Resets all button texts to what's currently on screen
* @param text Text, both for buttons and for voicing. Each section should be separated by a newline
* @param mode Mode of the spells menu
* @returns All text combined
*/
Common::String resetButtonTexts(const Common::String &text, int mode);
#endif
public:
/**
* Show the spells list dialog
*/
static Character *show(XeenEngine *vm, ButtonContainer *priorDialog,
Character *c, SpellDialogMode mode);
};
class CastSpell : public ButtonContainer {
private:
SpriteResource _iconSprites;
int _oldMode;
private:
CastSpell(XeenEngine *vm);
~CastSpell() override;
int execute(Character *&c);
void loadButtons();
#ifdef USE_TTS
void speakText(const Common::String &text);
#endif
public:
static int show(XeenEngine *vm);
};
class SpellOnWho : public ButtonContainer {
private:
SpellOnWho(XeenEngine *vm) : ButtonContainer(vm) {}
int execute(int spellId);
public:
static Character *show(XeenEngine *vm, int spellId);
};
class SelectElement : public ButtonContainer {
private:
SpriteResource _iconSprites;
SelectElement(XeenEngine *vm) : ButtonContainer(vm) {}
int execute(int spellId);
void loadButtons();
#ifdef USE_TTS
void speakText(const Common::String &text);
#endif
public:
static int show(XeenEngine *vm, int spellId);
};
class NotWhileEngaged : public ButtonContainer {
private:
NotWhileEngaged(XeenEngine *vm) : ButtonContainer(vm) {}
void execute(int spellId);
public:
static void show(XeenEngine *vm, int spellId);
};
class LloydsBeacon : public ButtonContainer {
private:
SpriteResource _iconSprites;
LloydsBeacon(XeenEngine *vm) : ButtonContainer(vm) {}
bool execute();
void loadButtons();
#ifdef USE_TTS
void speakText(const Common::String &text);
#endif
public:
static bool show(XeenEngine *vm);
};
class Teleport : public ButtonContainer {
private:
SpriteResource _iconSprites;
Teleport(XeenEngine *vm) : ButtonContainer(vm) {}
int execute();
public:
static int show(XeenEngine *vm);
};
class TownPortal : public ButtonContainer {
private:
TownPortal(XeenEngine *vm) : ButtonContainer(vm) {}
int execute();
public:
static int show(XeenEngine *vm);
};
class IdentifyMonster : public ButtonContainer {
private:
IdentifyMonster(XeenEngine *vm) : ButtonContainer(vm) {}
void execute();
#ifdef USE_TTS
void speakText(const Common::String &text) const;
#endif
public:
static void show(XeenEngine *vm);
};
class DetectMonsters : public ButtonContainer {
private:
DetectMonsters(XeenEngine *vm) : ButtonContainer(vm) {}
void execute();
public:
static void show(XeenEngine *vm);
};
} // End of namespace Xeen
} // End of namespace MM
#endif

View File

@@ -0,0 +1,130 @@
/* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "mm/xeen/dialogs/dialogs_whowill.h"
#include "mm/xeen/resources.h"
#include "mm/xeen/xeen.h"
namespace MM {
namespace Xeen {
int WhoWill::show(XeenEngine *vm, int message, int action, bool type) {
WhoWill *dlg = new WhoWill(vm);
int result = dlg->execute(message, action, type);
delete dlg;
return result;
}
int WhoWill::execute(int message, int action, bool type) {
EventsManager &events = *_vm->_events;
Interface &intf = *_vm->_interface;
Map &map = *_vm->_map;
Party &party = *_vm->_party;
Scripts &scripts = *_vm->_scripts;
LocationManager &loc = *_vm->_locations;
Windows &windows = *_vm->_windows;
int numFrames;
if (party._activeParty.size() <= 1)
// Unless there's at least two characters, just return the first one
return 1;
windows[38].close();
windows[12].close();
Common::String actionStr = type ? map._events._text[action] : Res.WHO_WILL_ACTIONS[action];
Common::String msg = Common::String::format(Res.WHO_WILL, actionStr.c_str(),
Res.WHO_ACTIONS[message], party._activeParty.size());
windows[36].open();
Common::String ttsMessage;
windows[36].writeString(msg, false, &ttsMessage);
windows[36].update();
#ifdef USE_TTS
speakText(ttsMessage);
#endif
intf._face1State = map._headData[party._mazePosition.y][party._mazePosition.x]._left;
intf._face2State = map._headData[party._mazePosition.y][party._mazePosition.x]._right;
while (!_vm->shouldExit()) {
events.updateGameCounter();
if (windows[11]._enabled) {
loc.drawAnim(false);
windows[36].frame();
numFrames = 3;
} else {
intf.draw3d(false);
windows[36].frame();
windows[3].update();
numFrames = 1;
}
events.wait(numFrames);
checkEvents(_vm);
if (!_buttonValue)
continue;
if (_buttonValue == 27) {
_buttonValue = 0;
break;
} else if (_buttonValue >= Common::KEYCODE_F1 && _buttonValue <= Common::KEYCODE_F6) {
_buttonValue -= Common::KEYCODE_F1 - 1;
if (_buttonValue > (int)party._activeParty.size())
continue;
if (party._activeParty[_buttonValue - 1].noActions())
continue;
scripts._whoWill = _buttonValue;
break;
}
}
#ifdef USE_TTS
_vm->stopTextToSpeech();
#endif
intf._face1State = intf._face2State = 2;
windows[36].close();
return _buttonValue;
}
#ifdef USE_TTS
void WhoWill::speakText(const Common::String &text) const {
uint index = 0;
// Title
_vm->sayText(getNextTextSection(text, index));
// "Who will" and the action verb (i.e. try)
_vm->sayText(getNextTextSection(text, index, 2, " "));
// F1 - F6
_vm->sayText(getNextTextSection(text, index));
}
#endif
} // End of namespace Xeen
} // End of namespace MM

View File

@@ -0,0 +1,46 @@
/* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef XEEN_DIALOGS_WHOWHILL_H
#define XEEN_DIALOGS_WHOWHILL_H
#include "mm/xeen/dialogs/dialogs.h"
namespace MM {
namespace Xeen {
class WhoWill : public ButtonContainer {
private:
WhoWill(XeenEngine *vm) : ButtonContainer(vm) {}
int execute(int message, int action, bool type);
#ifdef USE_TTS
void speakText(const Common::String &text) const;
#endif
public:
static int show(XeenEngine *vm, int message, int action, bool type);
};
} // End of namespace Xeen
} // End of namespace MM
#endif

View File

@@ -0,0 +1,52 @@
/* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "mm/xeen/dialogs/please_wait.h"
#include "mm/xeen/resources.h"
#include "mm/xeen/window.h"
#include "mm/xeen/xeen.h"
namespace MM {
namespace Xeen {
PleaseWait::PleaseWait(bool isOops) {
_msg = isOops ? Res.OOPS : Res.PLEASE_WAIT;
}
PleaseWait::~PleaseWait() {
Windows &windows = *g_vm->_windows;
windows[9].close();
}
void PleaseWait::show() {
if (g_vm->_mode == MODE_STARTUP) {
return;
}
Windows &windows = *g_vm->_windows;
Window &w = windows[9];
w.open();
w.writeString(_msg);
w.update();
}
} // End of namespace Xeen
} // End of namespace MM

View File

@@ -0,0 +1,46 @@
/* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef XEEN_DIALOGS_PLEASE_WAIT_H
#define XEEN_DIALOGS_PLEASE_WAIT_H
#include "mm/xeen/dialogs/dialogs.h"
namespace MM {
namespace Xeen {
class PleaseWait {
private:
Common::String _msg;
public:
PleaseWait(bool isOops = false);
~PleaseWait();
/**
* Show the dialog
*/
void show();
};
} // End of namespace Xeen
} // End of namespace MM
#endif