scummvm/engines/avalanche/dialogs.cpp
2013-09-28 15:08:41 +02:00

1117 lines
31 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
/*
* This code is based on the original source code of Lord Avalot d'Argent version 1.3.
* Copyright (c) 1994-1995 Mike, Mark and Thomas Thurman.
*/
/* SCROLLS The scroll driver. */
#include "avalanche/avalanche.h"
#include "avalanche/dialogs.h"
#include "avalanche/animation.h"
#include "avalanche/timer.h"
#include "common/textconsole.h"
#include "common/file.h"
namespace Avalanche {
Dialogs::Dialogs(AvalancheEngine *vm) {
_vm = vm;
_noError = true;
}
void Dialogs::init() {
loadFont();
resetScrollDriver();
}
void Dialogs::setReadyLight(byte state) { // Sets "Ready" light to whatever
if (_vm->_ledStatus == state)
return; // Already like that!
Color color = kColorBlack;
switch (state) {
case 0:
color = kColorBlack;
break; // Off
case 1:
case 2:
case 3:
color = kColorGreen;
break; // Hit a key
}
warning("STUB: Scrolls::state()");
CursorMan.showMouse(false);
_vm->_graphics->_surface.fillRect(Common::Rect(419, 195, 438, 197), color);
CursorMan.showMouse(true);
_vm->_ledStatus = state;
}
void Dialogs::easterEgg() {
warning("STUB: Scrolls::easterEgg()");
}
void Dialogs::say(int16 x, int16 y, Common::String z) {
FontType itw;
byte lz = z.size();
bool offset = x % 8 == 4;
x /= 8;
y++;
int16 i = 0;
for (int xx = 0; xx < lz; xx++) {
switch (z[xx]) {
case kControlRoman:
_currentFont = kFontStyleRoman;
break;
case kControlItalic:
_currentFont = kFontStyleItalic;
break;
default: {
for (int yy = 0; yy < 12; yy++)
itw[(byte)z[xx]][yy] = _fonts[_currentFont][(byte)z[xx]][yy + 2];
// We have to draw the characters one-by-one because of the accidental font changes.
i++;
Common::String chr(z[xx]);
_vm->_graphics->drawScrollText(chr, itw, 12, (x - 1) * 8 + offset * 4 + i * 8, y, kColorBlack);
}
}
}
}
void Dialogs::scrollModeNormal() {
// Original code is:
// egg : array[1..8] of char = ^P^L^U^G^H+'***';
// this is not using kControl characters: it's the secret code to be entered to trigger the easter egg
// TODO: To be fixed when the Easter egg code is implemented
Common::String egg = Common::String::format("%c%c%c%c%c***", kControlParagraph, kControlLeftJustified, kControlNegative, kControlBell, kControlBackspace);
Common::String e = "(c) 1994";
setReadyLight(3);
_vm->_seeScroll = true;
_vm->_graphics->loadMouse(kCurFletch);
::Graphics::Surface temp;
temp.copyFrom(_vm->_graphics->_surface);
_vm->_graphics->_surface.copyFrom(_vm->_graphics->_scrolls); // TODO: Rework it using getSubArea !!!!!!!
Common::Event event;
while (!_vm->shouldQuit()) {
_vm->_graphics->refreshScreen();
_vm->getEvent(event);
if (_vm->shouldQuit() ||
(event.type == Common::EVENT_LBUTTONUP) ||
((event.type == Common::EVENT_KEYDOWN) && ((event.kbd.keycode == Common::KEYCODE_ESCAPE)
|| (event.kbd.keycode == Common::KEYCODE_RETURN) || (event.kbd.keycode == Common::KEYCODE_HASH)
|| (event.kbd.keycode == Common::KEYCODE_PLUS))))
break;
}
_vm->_graphics->_surface.copyFrom(temp);
temp.free();
warning("STUB: scrollModeNormal() - Check Easter Egg trigger");
#if 0
char r;
bool oktoexit;
do {
do {
_vm->check(); // was "checkclick;"
//#ifdef RECORD slowdown(); basher::count++; #endif
if (_vm->_enhanced->keypressede())
break;
} while (!((mrelease > 0) || (buttona1()) || (buttonb1())));
if (mrelease == 0) {
inkey();
if (aboutscroll) {
move(e[2 - 1], e[1 - 1], 7);
e[8 - 1] = inchar;
if (egg == e)
easteregg();
}
oktoexit = set::of('\15', '\33', '+', '#', eos).has(inchar);
if (!oktoexit) errorled();
}
} while (!((oktoexit) || (mrelease > 0)));
//#ifdef RECORD record_one(); #endif
_vm->screturn = r == '#'; // "back door"
#endif
setReadyLight(0);
_vm->_seeScroll = false;
_vm->_holdLeftMouse = false; // Used in Lucerna::checkclick().
warning("STUB: Scrolls::scrollModeNormal()");
}
void Dialogs::scrollModeDialogue() {
warning("STUB: Scrolls::scrollModeDialogue()");
// It should work with keypresses too! TODO: Implement it!
_vm->_graphics->loadMouse(kCurHand);
::Graphics::Surface temp;
temp.copyFrom(_vm->_graphics->_surface);
_vm->_graphics->_surface.copyFrom(_vm->_graphics->_scrolls); // TODO: Rework it using getSubArea !!!!!!!
Common::Event event;
while (!_vm->shouldQuit()) {
_vm->_graphics->refreshScreen();
_vm->getEvent(event);
Common::Point cursorPos = _vm->getMousePos();
cursorPos.y /= 2;
if (_vm->shouldQuit() || (event.type == Common::EVENT_LBUTTONUP)) {
if ((cursorPos.x >= _shadowBoxX - 65) && (cursorPos.y >= _shadowBoxY - 24) && (cursorPos.x <= _shadowBoxX - 5) && (cursorPos.y <= _shadowBoxY - 10)) {
_scReturn = true;
break;
} else if ((cursorPos.x >= _shadowBoxX + 5) && (cursorPos.y >= _shadowBoxY - 24) && (cursorPos.x <= _shadowBoxX + 65) && (cursorPos.y <= _shadowBoxY - 10)) {
_scReturn = false;
break;
}
}
}
_vm->_graphics->_surface.copyFrom(temp);
temp.free();
}
void Dialogs::store(byte what, TuneType &played) {
memcpy(played, played + 1, sizeof(played) - 1);
played[30] = what;
}
bool Dialogs::theyMatch(TuneType &played) {
byte mistakes = 0;
for (unsigned int i = 0; i < sizeof(played); i++) {
if (played[i] != _vm->kTune[i])
mistakes++;
}
return mistakes < 5;
}
void Dialogs::scrollModeMusic() {
setReadyLight(3);
_vm->_seeScroll = true;
CursorMan.showMouse(false);
_vm->_graphics->loadMouse(kCurFletch);
TuneType played;
for (unsigned int i = 0; i < sizeof(played); i++)
played[i] = kPitchInvalid;
int8 lastOne = -1, thisOne = -1; // Invalid values.
_vm->_seeScroll = true;
::Graphics::Surface temp;
temp.copyFrom(_vm->_graphics->_surface);
_vm->_graphics->_surface.copyFrom(_vm->_graphics->_scrolls); // TODO: Rework it using getSubArea !!!!!!!
Common::Event event;
while (!_vm->shouldQuit()) {
_vm->_graphics->refreshScreen();
_vm->getEvent(event);
// When we stop playing?
if ((event.type == Common::EVENT_LBUTTONDOWN) ||
((event.type == Common::EVENT_KEYDOWN) && ((event.kbd.keycode == Common::KEYCODE_RETURN) || (event.kbd.keycode == Common::KEYCODE_ESCAPE)))) {
break;
}
// When we DO play:
if ((event.type == Common::EVENT_KEYDOWN)
&& ((event.kbd.keycode == Common::KEYCODE_q) || (event.kbd.keycode == Common::KEYCODE_w)
|| (event.kbd.keycode == Common::KEYCODE_e) || (event.kbd.keycode == Common::KEYCODE_r)
|| (event.kbd.keycode == Common::KEYCODE_t) || (event.kbd.keycode == Common::KEYCODE_y)
|| (event.kbd.keycode == Common::KEYCODE_u) || (event.kbd.keycode == Common::KEYCODE_i)
|| (event.kbd.keycode == Common::KEYCODE_o) || (event.kbd.keycode == Common::KEYCODE_p)
|| (event.kbd.keycode == Common::KEYCODE_LEFTBRACKET) || (event.kbd.keycode == Common::KEYCODE_RIGHTBRACKET))) {
byte value;
switch (event.kbd.keycode) {
case Common::KEYCODE_q:
value = 0;
break;
case Common::KEYCODE_w:
value = 1;
break;
case Common::KEYCODE_e:
value = 2;
break;
case Common::KEYCODE_r:
value = 3;
break;
case Common::KEYCODE_t:
value = 4;
break;
case Common::KEYCODE_y:
value = 5;
break;
case Common::KEYCODE_u:
value = 6;
break;
case Common::KEYCODE_i:
value = 7;
break;
case Common::KEYCODE_o:
value = 8;
break;
case Common::KEYCODE_p:
value = 9;
break;
case Common::KEYCODE_LEFTBRACKET:
value = 10;
break;
case Common::KEYCODE_RIGHTBRACKET:
value = 11;
break;
default:
break;
}
lastOne = thisOne;
thisOne = value;
_vm->_sound->playNote(_vm->kNotes[thisOne], 100);
_vm->_system->delayMillis(200);
if (!_vm->_bellsAreRinging) { // These handle playing the right tune.
if (thisOne < lastOne)
store(kPitchLower, played);
else if (thisOne == lastOne)
store(kPitchSame, played);
else
store(kPitchHigher, played);
}
if (theyMatch(played)) {
setReadyLight(0);
_vm->_timer->addTimer(8, Timer::kProcJacquesWakesUp, Timer::kReasonJacquesWakingUp);
break;
}
}
}
_vm->_graphics->_surface.copyFrom(temp);
temp.free();
_vm->_seeScroll = false;
CursorMan.showMouse(true);
}
void Dialogs::resetScrollDriver() {
_vm->_scrollBells = 0;
_currentFont = kFontStyleRoman;
_useIcon = 0;
_vm->_interrogation = 0; // Always reset after a scroll comes up.
}
void Dialogs::ringBell() { // Pussy's in the well. Who put her in? Little...
for (int i = 0; i < _vm->_scrollBells; i++)
_vm->errorLed(); // Ring the bell "x" times.
}
void Dialogs::dodgem() {
_dodgeCoord = _vm->getMousePos();
g_system->warpMouse(_dodgeCoord.x, _vm->_underScroll); // Move the pointer off the scroll.
}
void Dialogs::unDodgem() {
Common::Point actCoord = _vm->getMousePos();
if ((actCoord.x == _dodgeCoord.x) && (actCoord.y == _vm->_underScroll))
g_system->warpMouse(_dodgeCoord.x, _dodgeCoord.y); // No change, so restore the pointer's original position.
}
void Dialogs::getIcon(int16 x, int16 y, byte which) {
Common::File file;
if (!file.open("icons.avd"))
error("AVALANCHE: Scrolls: File not found: icons.avd");
which--;
file.seek(which * 426);
byte *p = new byte[426];
file.read(p, 426);
//putimage(x, y, p, 0);
warning("STUB: Scrolls::getIcon()");
delete[] p;
file.close();
}
void Dialogs::drawSign(Common::String fn, int16 xl, int16 yl, int16 y) {
Common::File file;
Common::String filename = Common::String::format("%s.avd", fn.c_str());
if (!file.open(filename))
error("AVALANCHE: Scrolls: File not found: %s", filename.c_str());
#if 0
uint16 st = (y - 1) * 80 + (40 - xl / 2) + ((1 - _vm->cp) * _vm->pagetop);
byte bit;
for (uint16 i = 1; i <= yl; i++)
for (bit = 0; bit <= 3; bit++) {
port[0x3c4] = 2;
port[0x3ce] = 4;
port[0x3c5] = 1 << bit;
port[0x3cf] = bit;
blockread(f, mem[0xa000 * st + (i * 80)], xl);
}
bit = getpixel(0, 0);
#endif
warning("STUB: Scrolls::drawSign()");
file.close();
}
void Dialogs::drawScroll(DialogFunctionType modeFunc) {
int16 lx = 0;
int16 ly = (_vm->_scrollNum) * 6;
int16 ex;
for (int i = 0; i < _vm->_scrollNum; i++) {
ex = _vm->_scroll[i].size() * 8;
if (lx < ex)
lx = ex;
}
int16 mx = 320;
int16 my = 100; // Getmaxx & getmaxy div 2, both.
lx /= 2;
ly -= 2;
if ((1 <= _useIcon) && (_useIcon <= 34))
lx += kHalfIconWidth;
_vm->_graphics->_scrolls.copyFrom(_vm->_graphics->_surface);
CursorMan.showMouse(false);
// The right corners of the scroll.
_vm->_graphics->drawPieSlice(mx + lx, my - ly, 0, 90, 15, kColorLightgray);
_vm->_graphics->drawPieSlice(mx + lx, my + ly, 270, 360, 15, kColorLightgray);
_vm->_graphics->drawArc(_vm->_graphics->_scrolls, mx + lx, my - ly, 0, 90, 15, kColorRed);
_vm->_graphics->drawArc(_vm->_graphics->_scrolls, mx + lx, my + ly, 270, 360, 15, kColorRed);
// The body of the scroll.
_vm->_graphics->_scrolls.fillRect(Common::Rect(mx - lx - 30, my + ly, mx + lx, my + ly + 6), kColorLightgray);
_vm->_graphics->_scrolls.fillRect(Common::Rect(mx - lx - 30, my - ly - 6, mx + lx, my - ly + 1), kColorLightgray);
_vm->_graphics->_scrolls.fillRect(Common::Rect(mx - lx - 15, my - ly, mx + lx + 15, my + ly + 1), kColorLightgray);
// The left corners of the scroll.
_vm->_graphics->drawPieSlice(mx - lx - 31, my - ly, 0, 180, 15, kColorDarkgray);
_vm->_graphics->drawArc(_vm->_graphics->_scrolls, mx - lx - 31, my - ly, 0, 180, 15, kColorRed);
_vm->_graphics->_scrolls.drawLine(mx - lx - 31 - 15, my - ly, mx - lx - 31 + 15, my - ly, kColorRed);
_vm->_graphics->drawPieSlice(mx - lx - 31, my + ly, 180, 360, 15, kColorDarkgray);
_vm->_graphics->drawArc(_vm->_graphics->_scrolls, mx - lx - 31, my + ly, 180, 360, 15, kColorRed);
_vm->_graphics->_scrolls.drawLine(mx - lx - 31 - 15, my + ly, mx - lx - 31 + 15, my + ly, kColorRed);
// The rear borders of the scroll.
_vm->_graphics->_scrolls.fillRect(Common::Rect(mx - lx - 30, my - ly - 6, mx + lx, my - ly - 5), kColorRed);
_vm->_graphics->_scrolls.fillRect(Common::Rect(mx - lx - 30, my + ly + 6, mx + lx, my + ly + 7), kColorRed);
_vm->_graphics->_scrolls.fillRect(Common::Rect(mx - lx - 15, my - ly, mx - lx - 14, my + ly), kColorRed);
_vm->_graphics->_scrolls.fillRect(Common::Rect(mx + lx + 15, my - ly, mx + lx + 16, my + ly), kColorRed);
mx -= lx;
my -= ly + 2;
bool centre = false;
byte iconIndent = 0;
switch (_useIcon) {
case 0:
iconIndent = 0;
break; // No icon.
case 34:
drawSign("about", 28, 76, 15);
iconIndent = 0;
break;
case 35:
drawSign("gameover", 52, 59, 71);
iconIndent = 0;
break;
}
if ((1 <= _useIcon) && (_useIcon <= 33)) { // Standard icon.
getIcon(mx, my + ly / 2, _useIcon);
iconIndent = 53;
}
for (int i = 0; i < _vm->_scrollNum; i++) {
if (!_vm->_scroll[i].empty())
switch (_vm->_scroll[i][_vm->_scroll[i].size() - 1]) {
case kControlCenter:
centre = true;
_vm->_scroll[i].deleteLastChar();
break;
case kControlLeftJustified:
centre = false;
_vm->_scroll[i].deleteLastChar();
break;
case kControlQuestion:
_shadowBoxX = mx + lx;
_shadowBoxY = my + ly;
_vm->_scroll[i].setChar(' ', 0);
_vm->_graphics->drawShadowBox(_shadowBoxX - 65, _shadowBoxY - 24, _shadowBoxX - 5, _shadowBoxY - 10, "Yes.");
_vm->_graphics->drawShadowBox(_shadowBoxX + 5, _shadowBoxY - 24, _shadowBoxX + 65, _shadowBoxY - 10, "No.");
break;
}
if (centre)
say(320 - _vm->_scroll[i].size() * 4 + iconIndent, my, _vm->_scroll[i]);
else
say(mx + iconIndent, my, _vm->_scroll[i]);
my += 12;
}
_vm->_underScroll = my * 2 + 6; // Multiplying because of the doubled screen height.
ringBell();
_vm->_dropsOk = false;
dodgem();
(this->*modeFunc)();
unDodgem();
_vm->_dropsOk = true;
resetScrollDriver();
}
void Dialogs::drawBubble(DialogFunctionType modeFunc) {
Common::Point points[3];
CursorMan.showMouse(false);
int16 xl = 0;
int16 yl = _vm->_scrollNum * 5;
for (int i = 0; i < _vm->_scrollNum; i++) {
uint16 textWidth = _vm->_scroll[i].size() * 8;
if (textWidth > xl)
xl = textWidth;
}
xl /= 2;
int16 xw = xl + 18;
int16 yw = yl + 7;
int16 my = yw * 2 - 2;
int16 xc = 0;
if ((_vm->_talkX - xw) < 0)
xc = -(_vm->_talkX - xw);
if ((_vm->_talkX + xw) > 639)
xc = 639 - (_vm->_talkX + xw);
points[0].x = _vm->_talkX - 10;
points[0].y = yw;
points[1].x = _vm->_talkX + 10;
points[1].y = yw;
points[2].x = _vm->_talkX;
points[2].y = _vm->_talkY;
// Backup the screen before drawing the bubble.
_vm->_graphics->_scrolls.copyFrom(_vm->_graphics->_surface);
// The body of the bubble.
_vm->_graphics->_scrolls.fillRect(Common::Rect(xc + _vm->_talkX - xw + 9, 7, _vm->_talkX + xw - 8 + xc, my + 1), _vm->_talkBackgroundColor);
_vm->_graphics->_scrolls.fillRect(Common::Rect(xc + _vm->_talkX - xw - 1, 12, _vm->_talkX + xw + xc + 2, my - 4), _vm->_talkBackgroundColor);
// Top right corner of the bubble.
_vm->_graphics->drawPieSlice(xc + _vm->_talkX + xw - 10, 11, 0, 90, 9, _vm->_talkBackgroundColor);
// Bottom right corner of the bubble.
_vm->_graphics->drawPieSlice(xc + _vm->_talkX + xw - 10, my - 4, 270, 360, 9, _vm->_talkBackgroundColor);
// Top left corner of the bubble.
_vm->_graphics->drawPieSlice(xc + _vm->_talkX - xw + 10, 11, 90, 180, 9, _vm->_talkBackgroundColor);
// Bottom left corner of the bubble.
_vm->_graphics->drawPieSlice(xc + _vm->_talkX - xw + 10, my - 4, 180, 270, 9, _vm->_talkBackgroundColor);
// "Tail" of the speech bubble.
_vm->_graphics->drawTriangle(points, _vm->_talkBackgroundColor);
// Draw the text of the bubble. The centering of the text was improved here compared to Pascal's settextjustify().
// The font is not the same that outtextxy() uses in Pascal. I don't have that, so I used characters instead.
// It's almost the same, only notable differences are '?', '!', etc.
for (int i = 0; i < _vm->_scrollNum; i++) {
int16 x = xc + _vm->_talkX - _vm->_scroll[i].size() / 2 * 8;
bool offset = _vm->_scroll[i].size() % 2;
_vm->_graphics->drawScrollText(_vm->_scroll[i], _vm->_font, 8, x - offset * 4, (i * 10) + 12, _vm->_talkFontColor);
}
ringBell();
CursorMan.showMouse(false);
_vm->_dropsOk = false;
// This does the actual drawing to the screen.
(this->*modeFunc)();
_vm->_dropsOk = true;
CursorMan.showMouse(true); // sink;
resetScrollDriver();
}
void Dialogs::reset() {
_vm->_scrollNum = 1;
for (int i = 0; i < 15; i++) {
if (!_vm->_scroll[i].empty())
_vm->_scroll[i].clear();
}
}
void Dialogs::setBubbleStateNatural() {
_vm->_talkX = 320;
_vm->_talkY = 200;
_vm->_talkBackgroundColor = kColorDarkgray;
_vm->_talkFontColor = kColorWhite;
}
Common::String Dialogs::displayMoney() {
Common::String result;
if (_vm->_money < 12) { // just pence
result = Common::String::format("%dd", _vm->_money);
} else if (_vm->_money < 240) { // shillings & pence
if ((_vm->_money % 12) == 0)
result = Common::String::format("%d/-", _vm->_money / 12);
else
result = Common::String::format("%d/%d", _vm->_money / 12, _vm->_money % 12);
} else { // L, s & d
result = Common::String::format("\x9C%d.%d.%d", _vm->_money / 240, (_vm->_money / 12) % 20,
_vm->_money % 12);
}
if (_vm->_money > 12) {
Common::String extraStr = Common::String::format(" (that's %dd)", _vm->_money);
result += extraStr;
}
return result;
}
void Dialogs::stripTrailingSpaces(Common::String &str) {
while (str[str.size() - 1] == ' ')
str.deleteLastChar();
}
void Dialogs::solidify(byte n) {
if (!_vm->_scroll[n].contains(' '))
return; // No spaces.
// So there MUST be a space there, somewhere...
do {
_vm->_scroll[n + 1] = _vm->_scroll[n][_vm->_scroll[n].size() - 1] + _vm->_scroll[n + 1];
_vm->_scroll[n].deleteLastChar();
} while (_vm->_scroll[n][_vm->_scroll[n].size() - 1] != ' ');
stripTrailingSpaces(_vm->_scroll[n]);
}
void Dialogs::callDialogDriver() {
// bool was_virtual; // Was the mouse cursor virtual on entry to this proc?
warning("STUB: Scrolls::calldrivers()");
_vm->_sound->stopSound();
setReadyLight(0);
_scReturn = false;
bool mouthnext = false;
bool call_spriterun = true; // Only call sprite_run the FIRST time.
switch (_vm->_buffer[_vm->_bufSize - 1]) {
case kControlToBuffer:
_vm->_bufSize--;
break; // ^D = (D)on't include pagebreak
case kControlSpeechBubble:
case kControlQuestion:
break; // ^B = speech (B)ubble, ^Q = (Q)uestion in dialogue box
default:
_vm->_buffer[_vm->_bufSize] = kControlParagraph;
_vm->_bufSize++;
}
uint16 size = _vm->_bufSize;
for (uint16 i = 0; i < size; i++) {
if (mouthnext) {
if (_vm->_buffer[i] == kControlRegister)
_param = 0;
else if (('0' <= _vm->_buffer[i]) && (_vm->_buffer[i] <= '9'))
_param = _vm->_buffer[i] - 48;
else if (('A' <= _vm->_buffer[i]) && (_vm->_buffer[i] <= 'Z'))
_param = _vm->_buffer[i] - 55;
mouthnext = false;
} else {
switch (_vm->_buffer[i]) {
case kControlParagraph:
if ((_vm->_scrollNum == 1) && (_vm->_scroll[0].empty()))
break;
if (call_spriterun)
_vm->spriteRun();
call_spriterun = false;
drawScroll(&Avalanche::Dialogs::scrollModeNormal);
reset();
if (_scReturn)
return;
break;
case kControlBell:
_vm->_scrollBells++;
break; // #7 = "Bel"
case kControlSpeechBubble:
if ((_vm->_scrollNum == 1) && (_vm->_scroll[0].empty()))
break;
if (call_spriterun)
_vm->spriteRun();
call_spriterun = false;
if (_param == 0)
setBubbleStateNatural();
else if ((1 <= _param) && (_param <= 9)) {
AnimationType *spr = &_vm->_animation->_sprites[_param - 1];
if ((_param > _vm->_animation->kSpriteNumbMax) || (!spr->_quick)) { // Not valid.
_vm->errorLed();
setBubbleStateNatural();
} else
spr->chatter(); // Normal sprite talking routine.
} else if ((10 <= _param) && (_param <= 36)) {
// Quasi-peds. (This routine performs the same
// thing with QPs as triptype.chatter does with the
// sprites.)
PedType *quasiPed = &_vm->_peds[_vm->kQuasipeds[_param - 10]._whichPed];
_vm->_talkX = quasiPed->_x;
_vm->_talkY = quasiPed->_y; // Position.
_vm->_talkFontColor = _vm->kQuasipeds[_param - 10]._foregroundColor;
_vm->_talkBackgroundColor = _vm->kQuasipeds[_param - 10]._backgroundColor; // Colors.
} else {
_vm->errorLed(); // Not valid.
setBubbleStateNatural();
}
drawBubble(&Avalanche::Dialogs::scrollModeNormal);
reset();
if (_scReturn)
return;
break;
// CHECME: The whole kControlNegative block seems completely unused, as the only use (the easter egg check) is a false positive
case kControlNegative:
switch (_param) {
case 1:
displayText(displayMoney() + kControlToBuffer); // Insert cash balance. (Recursion)
break;
case 2: {
int pwdId = _vm->_parser->kFirstPassword + _vm->_passwordNum;
displayText(_vm->_parser->_vocabulary[pwdId]._word + kControlToBuffer);
}
break;
case 3:
displayText(_vm->_favouriteDrink + kControlToBuffer);
break;
case 4:
displayText(_vm->_favouriteSong + kControlToBuffer);
break;
case 5:
displayText(_vm->_worstPlaceOnEarth + kControlToBuffer);
break;
case 6:
displayText(_vm->_spareEvening + kControlToBuffer);
break;
case 9: {
Common::String tmpStr = Common::String::format("%d,%d%c",_vm->_catacombX, _vm->_catacombY, kControlToBuffer);
displayText(tmpStr);
}
break;
case 10:
switch (_vm->_boxContent) {
case 0: // Sixpence.
displayScrollChain('q', 37); // You find the sixpence.
_vm->_money += 6;
_vm->_boxContent = _vm->_parser->kNothing;
_vm->incScore(2);
return;
case Parser::kNothing:
displayText("nothing at all. It's completely empty.");
break;
default:
displayText(_vm->getItem(_vm->_boxContent) + '.');
}
break;
case 11:
for (int j = 0; j < kObjectNum; j++) {
if (_vm->_objects[j])
displayText(_vm->getItem(j) + ", " + kControlToBuffer);
}
break;
}
break;
case kControlIcon:
_useIcon = _param;
break;
case kControlNewLine:
_vm->_scrollNum++;
break;
case kControlQuestion:
if (call_spriterun)
_vm->spriteRun();
call_spriterun = false;
_vm->_scroll[_vm->_scrollNum] = kControlQuestion;
_vm->_scrollNum++;
drawScroll(&Avalanche::Dialogs::scrollModeDialogue);
reset();
break;
case kControlRegister:
mouthnext = true;
break;
case kControlInsertSpaces:
for (int j = 0; j < 9; j++)
_vm->_scroll[_vm->_scrollNum - 1] += ' ';
break;
default: // Add new char.
if (_vm->_scroll[_vm->_scrollNum - 1].size() == 50) {
solidify(_vm->_scrollNum - 1);
_vm->_scrollNum++;
}
_vm->_scroll[_vm->_scrollNum - 1] += _vm->_buffer[i];
break;
}
}
}
}
void Dialogs::displayText(Common::String text) { // TODO: REPLACE BUFFER WITH A STRING!!!!!!!!!!
_vm->_bufSize = text.size();
memcpy(_vm->_buffer, text.c_str(), _vm->_bufSize);
callDialogDriver();
}
bool Dialogs::displayQuestion(Common::String question) {
displayText(question + kControlNewLine + kControlQuestion);
warning("STUB: Dialogs::displayQuestion()");
return _scReturn;
}
void Dialogs::loadFont() {
Common::File file;
if (!file.open("avalot.fnt"))
error("AVALANCHE: Scrolls: File not found: avalot.fnt");
for (int16 i = 0; i < 256; i++)
file.read(_fonts[0][i], 16);
file.close();
if (!file.open("avitalic.fnt"))
error("AVALANCHE: Scrolls: File not found: avitalic.fnt");
for (int16 i = 0; i < 256; i++)
file.read(_fonts[1][i], 16);
file.close();
if (!file.open("ttsmall.fnt"))
error("AVALANCHE: Scrolls: File not found: ttsmall.fnt");
for (int16 i = 0; i < 256; i++)
file.read(_vm->_font[i],16);
file.close();
}
void Dialogs::displayMusicalScroll() {
Common::String tmpStr = Common::String::format("To play the harp...%c%cUse these keys:%c%cQ W E R T Y U I O P [ ]%c%cOr press Enter to stop playing.%c",
kControlNewLine, kControlNewLine, kControlNewLine, kControlInsertSpaces, kControlNewLine, kControlNewLine, kControlToBuffer);
displayText(tmpStr);
_vm->spriteRun();
CursorMan.showMouse(false);
drawScroll(&Avalanche::Dialogs::scrollModeMusic);
CursorMan.showMouse(true);
reset();
}
// From Visa:
void Dialogs::unSkrimble() {
for (uint16 i = 0; i < _vm->_bufSize; i++)
_vm->_buffer[i] = (~(_vm->_buffer[i] - (i + 1))) % 256;
}
void Dialogs::doTheBubble() {
_vm->_buffer[_vm->_bufSize] = 2;
_vm->_bufSize++;
}
/**
* Display a string in a scroll
* @remarks Originally called 'dixi'
*/
void Dialogs::displayScrollChain(char block, byte point, bool report, bool bubbling) {
Common::File indexfile;
if (!indexfile.open("avalot.idx"))
error("AVALANCHE: Visa: File not found: avalot.idx");
bool error = false;
indexfile.seek((toupper(block) - 65) * 2);
uint16 idx_offset = indexfile.readUint16LE();
if (idx_offset == 0)
error = true;
indexfile.seek(idx_offset + point * 2);
uint16 sez_offset = indexfile.readUint16LE();
if (sez_offset == 0)
error = true;
indexfile.close();
_noError = !error;
if (error) {
if (report) {
Common::String todisplay = Common::String::format("%cError accessing scroll %c%d", Dialogs::kControlBell, block, point);
displayText(todisplay);
}
return;
}
Common::File sezfile;
if (!sezfile.open("avalot.sez"))
::error("AVALANCHE: Visa: File not found: avalot.sez");
sezfile.seek(sez_offset);
_vm->_bufSize = sezfile.readUint16LE();
sezfile.read(_vm->_buffer, _vm->_bufSize);
sezfile.close();
unSkrimble();
if (bubbling)
doTheBubble();
callDialogDriver();
}
/**
* Start speech
* @remarks Originally called 'speech'
*/
void Dialogs::speak(byte who, byte subject) {
if (subject == 0) { // No subject.
displayScrollChain('s', who, false, true);
return;
}
// Subject given.
_noError = false; // Assume that until we know otherwise.
Common::File indexfile;
if (!indexfile.open("converse.avd"))
error("AVALANCHE: Visa: File not found: converse.avd");
indexfile.seek(who * 2 - 2);
uint16 idx_offset = indexfile.readUint16LE();
uint16 next_idx_offset = indexfile.readUint16LE();
if ((idx_offset == 0) || ((((next_idx_offset - idx_offset) / 2) - 1) < subject))
return;
indexfile.seek(idx_offset + subject * 2);
uint16 sez_offset = indexfile.readUint16LE();
if ((sez_offset == 0) || (indexfile.err()))
return;
indexfile.close();
Common::File sezfile;
if (!sezfile.open("avalot.sez"))
error("AVALANCHE: Visa: File not found: avalot.sez");
sezfile.seek(sez_offset);
_vm->_bufSize = sezfile.readUint16LE();
sezfile.read(_vm->_buffer, _vm->_bufSize);
sezfile.close();
unSkrimble();
doTheBubble();
callDialogDriver();
_noError = true;
}
void Dialogs::talkTo(byte whom) {
if (_vm->_parser->_person == kPeoplePardon) {
_vm->_parser->_person = (People)_vm->_subjectNum;
_vm->_subjectNum = 0;
}
if (_vm->_subjectNum == 0) {
switch (whom) {
case kPeopleSpludwick:
if ((_vm->_lustieIsAsleep) & (!_vm->_objects[kObjectPotion - 1])) {
displayScrollChain('q', 68);
_vm->_objects[kObjectPotion - 1] = true;
_vm->refreshObjectList();
_vm->incScore(3);
return;
} else if (_vm->_talkedToCrapulus) {
// Spludwick - what does he need?
// 0 - let it through to use normal routine.
switch (_vm->_givenToSpludwick) {
case 1: // Fallthrough is intended.
case 2: {
Common::String objStr = _vm->getItem(AvalancheEngine::kSpludwicksOrder[_vm->_givenToSpludwick]);
Common::String tmpStr = Common::String::format("Can you get me %s, please?%c2%c", objStr.c_str(), Dialogs::kControlRegister, Dialogs::kControlSpeechBubble);
displayText(tmpStr);
}
return;
case 3:
displayScrollChain('q', 30); // Need any help with the game?
return;
}
} else {
displayScrollChain('q', 42); // Haven't talked to Crapulus. Go and talk to him.
return;
}
break;
case kPeopleIbythneth:
if (_vm->_givenBadgeToIby) {
displayScrollChain('q', 33); // Thanks a lot!
return; // And leave the proc.
}
break; // Or... just continue, 'cos he hasn't got it.
case kPeopleDogfood:
if (_vm->_wonNim) { // We've won the game.
displayScrollChain('q', 6); // "I'm Not Playing!"
return; // Zap back.
} else
_vm->_askedDogfoodAboutNim = true;
break;
case kPeopleAyles:
if (!_vm->_aylesIsAwake) {
displayScrollChain('q', 43); // He's fast asleep!
return;
} else if (!_vm->_givenPenToAyles) {
displayScrollChain('q', 44); // Can you get me a pen, Avvy?
return;
}
break;
case kPeopleJacques:
displayScrollChain('q', 43);
return;
case kPeopleGeida:
if (_vm->_givenPotionToGeida)
_vm->_geidaFollows = true;
else {
displayScrollChain('u', 17);
return;
}
break;
case kPeopleSpurge:
if (!_vm->_sittingInPub) {
displayScrollChain('q', 71); // Try going over and sitting down.
return;
} else {
if (_vm->_spurgeTalkCount < 5)
_vm->_spurgeTalkCount++;
if (_vm->_spurgeTalkCount > 1) { // no. 1 falls through
displayScrollChain('q', 70 + _vm->_spurgeTalkCount);
return;
}
}
break;
}
// On a subject. Is there any reason to block it?
} else if ((whom == kPeopleAyles) && (!_vm->_aylesIsAwake)) {
displayScrollChain('q', 43); // He's fast asleep!
return;
}
if (whom > 149)
whom -= 149;
bool noMatches = true;
for (int i = 0; i <= _vm->_animation->kSpriteNumbMax; i++) {
if (_vm->_animation->_sprites[i]._stat._acciNum == whom) {
Common::String tmpStr = Common::String::format("%c%c%c", Dialogs::kControlRegister, i + 49, Dialogs::kControlToBuffer);
displayText(tmpStr);
noMatches = false;
break;
}
}
if (noMatches) {
Common::String tmpStr = Common::String::format("%c%c%c", Dialogs::kControlRegister, Dialogs::kControlRegister, Dialogs::kControlToBuffer);
displayText(tmpStr);
}
speak(whom, _vm->_subjectNum);
if (!_noError)
displayScrollChain('n', whom); // File not found!
if ((_vm->_subjectNum == 0) && ((whom + 149) == kPeopleCrapulus)) { // Crapulus: get the badge - first time only
_vm->_objects[kObjectBadge - 1] = true;
_vm->refreshObjectList();
displayScrollChain('q', 1); // Circular from Cardiff.
_vm->_talkedToCrapulus = true;
_vm->setRoom(kPeopleCrapulus, kRoomDummy); // Crapulus walks off.
AnimationType *spr = &_vm->_animation->_sprites[1];
spr->_vanishIfStill = true;
spr->walkTo(2); // Walks away.
_vm->incScore(2);
}
}
void Dialogs::sayIt(Common::String str) {
Common::String x = str;
x.setChar(toupper(x[0]), 0);
Common::String tmpStr = Common::String::format("%c1%s.%c%c2", kControlRegister, x.c_str(), kControlSpeechBubble, kControlRegister);
displayText(tmpStr);
}
} // End of namespace Avalanche