diff options
Diffstat (limited to 'engines/parallaction')
33 files changed, 3305 insertions, 1506 deletions
diff --git a/engines/parallaction/balloons.cpp b/engines/parallaction/balloons.cpp index fab92dada9..81b32adb15 100644 --- a/engines/parallaction/balloons.cpp +++ b/engines/parallaction/balloons.cpp @@ -23,6 +23,8 @@ * */ +#include "common/util.h" + #include "parallaction/graphics.h" #include "parallaction/parallaction.h" @@ -76,6 +78,7 @@ class BalloonManager_ns : public BalloonManager { uint _numBalloons; + void getStringExtent(Font *font, char *text, uint16 maxwidth, int16* width, int16* height); void drawWrappedText(Font *font, Graphics::Surface* surf, char *text, byte color, int16 wrapwidth); int createBalloon(int16 w, int16 h, int16 winding, uint16 borderThickness); Balloon *getBalloon(uint id); @@ -149,12 +152,12 @@ int BalloonManager_ns::setSingleBalloon(char *text, uint16 x, uint16 y, uint16 w int16 w, h; - _gfx->getStringExtent(_vm->_dialogueFont, text, MAX_BALLOON_WIDTH, &w, &h); + getStringExtent(_vm->_dialogueFont, text, MAX_BALLOON_WIDTH, &w, &h); int id = createBalloon(w+5, h, winding, 1); Balloon *balloon = &_intBalloons[id]; - _gfx->drawWrappedText(_vm->_dialogueFont, balloon->surface, text, textColor, MAX_BALLOON_WIDTH); + drawWrappedText(_vm->_dialogueFont, balloon->surface, text, textColor, MAX_BALLOON_WIDTH); // TODO: extract some text to make a name for obj balloon->obj = _gfx->registerBalloon(new SurfaceToFrames(balloon->surface), 0); @@ -169,12 +172,12 @@ int BalloonManager_ns::setDialogueBalloon(char *text, uint16 winding, byte textC int16 w, h; - _gfx->getStringExtent(_vm->_dialogueFont, text, MAX_BALLOON_WIDTH, &w, &h); + getStringExtent(_vm->_dialogueFont, text, MAX_BALLOON_WIDTH, &w, &h); int id = createBalloon(w+5, h, winding, 1); Balloon *balloon = &_intBalloons[id]; - _gfx->drawWrappedText(_vm->_dialogueFont, balloon->surface, text, textColor, MAX_BALLOON_WIDTH); + drawWrappedText(_vm->_dialogueFont, balloon->surface, text, textColor, MAX_BALLOON_WIDTH); // TODO: extract some text to make a name for obj balloon->obj = _gfx->registerBalloon(new SurfaceToFrames(balloon->surface), 0); @@ -193,7 +196,7 @@ int BalloonManager_ns::setDialogueBalloon(char *text, uint16 winding, byte textC void BalloonManager_ns::setBalloonText(uint id, char *text, byte textColor) { Balloon *balloon = getBalloon(id); balloon->surface->fillRect(balloon->innerBox, 1); - _gfx->drawWrappedText(_vm->_dialogueFont, balloon->surface, text, textColor, MAX_BALLOON_WIDTH); + drawWrappedText(_vm->_dialogueFont, balloon->surface, text, textColor, MAX_BALLOON_WIDTH); } @@ -201,11 +204,11 @@ int BalloonManager_ns::setLocationBalloon(char *text, bool endGame) { int16 w, h; - _gfx->getStringExtent(_vm->_dialogueFont, text, MAX_BALLOON_WIDTH, &w, &h); + getStringExtent(_vm->_dialogueFont, text, MAX_BALLOON_WIDTH, &w, &h); int id = createBalloon(w+(endGame ? 5 : 10), h+5, -1, BALLOON_TRANSPARENT_COLOR_NS); Balloon *balloon = &_intBalloons[id]; - _gfx->drawWrappedText(_vm->_dialogueFont, balloon->surface, text, 0, MAX_BALLOON_WIDTH); + drawWrappedText(_vm->_dialogueFont, balloon->surface, text, 0, MAX_BALLOON_WIDTH); // TODO: extract some text to make a name for obj balloon->obj = _gfx->registerBalloon(new SurfaceToFrames(balloon->surface), 0); @@ -242,8 +245,105 @@ void BalloonManager_ns::freeBalloons() { _numBalloons = 0; } +void BalloonManager_ns::drawWrappedText(Font *font, Graphics::Surface* surf, char *text, byte color, int16 wrapwidth) { + + uint16 lines = 0; + uint16 linewidth = 0; + + uint16 rx = 10; + uint16 ry = 4; + + uint16 blankWidth = font->getStringWidth(" "); + uint16 tokenWidth = 0; + + char token[MAX_TOKEN_LEN]; + + if (wrapwidth == -1) + wrapwidth = _vm->_screenWidth; + + while (strlen(text) > 0) { + + text = parseNextToken(text, token, MAX_TOKEN_LEN, " ", true); + + if (!scumm_stricmp(token, "%p")) { + lines++; + rx = 10; + ry = 4 + lines*10; // y + + strcpy(token, "> ......."); + strncpy(token+2, _password, strlen(_password)); + tokenWidth = font->getStringWidth(token); + } else { + tokenWidth = font->getStringWidth(token); + + linewidth += tokenWidth; + + if (linewidth > wrapwidth) { + // wrap line + lines++; + rx = 10; // x + ry = 4 + lines*10; // y + linewidth = tokenWidth; + } + + if (!scumm_stricmp(token, "%s")) { + sprintf(token, "%d", _score); + } + + } + + _gfx->drawText(font, surf, rx, ry, token, color); + + rx += tokenWidth + blankWidth; + linewidth += blankWidth; + + text = Common::ltrim(text); + } + +} +void BalloonManager_ns::getStringExtent(Font *font, char *text, uint16 maxwidth, int16* width, int16* height) { + uint16 lines = 0; + uint16 w = 0; + *width = 0; + + uint16 blankWidth = font->getStringWidth(" "); + uint16 tokenWidth = 0; + + char token[MAX_TOKEN_LEN]; + + while (strlen(text) != 0) { + + text = parseNextToken(text, token, MAX_TOKEN_LEN, " ", true); + tokenWidth = font->getStringWidth(token); + + w += tokenWidth; + + if (!scumm_stricmp(token, "%p")) { + lines++; + } else { + if (w > maxwidth) { + w -= tokenWidth; + lines++; + if (w > *width) + *width = w; + + w = tokenWidth; + } + } + + w += blankWidth; + text = Common::ltrim(text); + } + + if (*width < w) *width = w; + *width += 10; + + *height = lines * 10 + 20; + + return; +} @@ -259,17 +359,36 @@ class BalloonManager_br : public BalloonManager { uint _numBalloons; - Frames *_leftBalloon; - Frames *_rightBalloon; Disk *_disk; Gfx *_gfx; + Frames *_leftBalloon; + Frames *_rightBalloon; + void cacheAnims(); + void getStringExtent(Font *font, const char *text, uint16 maxwidth, int16* width, int16* height); void drawWrappedText(Font *font, Graphics::Surface* surf, char *text, byte color, int16 wrapwidth); int createBalloon(int16 w, int16 h, int16 winding, uint16 borderThickness); Balloon *getBalloon(uint id); Graphics::Surface *expandBalloon(Frames *data, int frameNum); + void textSetupRendering(const Common::String &text, Graphics::Surface *dest, Font *font, byte color); + void textEmitCenteredLine(); + void textAccum(const Common::String &token, uint16 width); + void textNewLine(); + + Common::String _textLine; + Graphics::Surface *_textSurf; + Font *_textFont; + uint16 _textX, _textY; + byte _textColor; + uint16 _textLines, _textWidth; + + void extentSetup(Font *font, int16 *width, int16 *height); + void extentAction(); + + int16 *_extentWidth, *_extentHeight; + public: BalloonManager_br(Disk *disk, Gfx *gfx); @@ -315,11 +434,11 @@ int BalloonManager_br::setSingleBalloon(char *text, uint16 x, uint16 y, uint16 w Balloon *balloon = &_intBalloons[id]; if (winding == 0) { - src = _leftBalloon; + src = _rightBalloon; srcFrame = 0; } else if (winding == 1) { - src = _rightBalloon; + src = _leftBalloon; srcFrame = 0; } @@ -328,14 +447,16 @@ int BalloonManager_br::setSingleBalloon(char *text, uint16 x, uint16 y, uint16 w balloon->surface = expandBalloon(src, srcFrame); src->getRect(srcFrame, balloon->box); -// drawWrappedText(_vm->_dialogueFont, balloon->surface, text, textColor, MAX_BALLOON_WIDTH); + drawWrappedText(_vm->_dialogueFont, balloon->surface, text, textColor, MAX_BALLOON_WIDTH); // TODO: extract some text to make a name for obj balloon->obj = _gfx->registerBalloon(new SurfaceToFrames(balloon->surface), 0); - balloon->obj->x = x; - balloon->obj->y = y; + balloon->obj->x = x + balloon->box.left; + balloon->obj->y = y + balloon->box.top; balloon->obj->transparentKey = BALLOON_TRANSPARENT_COLOR_BR; + printf("balloon (%i, %i)\n", balloon->obj->x, balloon->obj->y); + _numBalloons++; return id; @@ -351,11 +472,11 @@ int BalloonManager_br::setDialogueBalloon(char *text, uint16 winding, byte textC Balloon *balloon = &_intBalloons[id]; if (winding == 0) { - src = _leftBalloon; + src = _rightBalloon; srcFrame = id; } else if (winding == 1) { - src = _rightBalloon; + src = _leftBalloon; srcFrame = 0; } @@ -364,12 +485,12 @@ int BalloonManager_br::setDialogueBalloon(char *text, uint16 winding, byte textC balloon->surface = expandBalloon(src, srcFrame); src->getRect(srcFrame, balloon->box); -// drawWrappedText(_vm->_dialogueFont, balloon->surface, text, textColor, MAX_BALLOON_WIDTH); + drawWrappedText(_vm->_dialogueFont, balloon->surface, text, textColor, MAX_BALLOON_WIDTH); // TODO: extract some text to make a name for obj balloon->obj = _gfx->registerBalloon(new SurfaceToFrames(balloon->surface), 0); - balloon->obj->x = 0; - balloon->obj->y = 10; + balloon->obj->x = balloon->box.left; + balloon->obj->y = balloon->box.top; balloon->obj->transparentKey = BALLOON_TRANSPARENT_COLOR_BR; if (id > 0) { @@ -434,6 +555,155 @@ void BalloonManager_br::cacheAnims() { } } + +void BalloonManager_br::extentSetup(Font *font, int16 *width, int16 *height) { + _extentWidth = width; + _extentHeight = height; + + _textLine.clear(); + _textLines = 0; + _textWidth = 0; + _textFont = font; +} + +void BalloonManager_br::extentAction() { + if (_textWidth > *_extentWidth) { + *_extentWidth = _textWidth; + } + *_extentHeight = _textLines * _textFont->height(); +} + +void BalloonManager_br::textSetupRendering(const Common::String &text, Graphics::Surface *dest, Font *font, byte color) { + uint16 maxWidth = 216; + + int16 w, h; + getStringExtent(font, text.c_str(), maxWidth, &w, &h); + + w += 10; + h += 12; + + _textLine.clear(); + _textSurf = dest; + _textFont = font; + _textX = 0; + _textY = (_textSurf->h - h) / 2; + _textColor = color; + _textLines = 0; + _textWidth = 0; +} + +void BalloonManager_br::textEmitCenteredLine() { + if (_textLine.empty()) { + return; + } + uint16 rx = _textX + (_textSurf->w - _textWidth) / 2; + uint16 ry = _textY + _textLines * _textFont->height(); // y + _gfx->drawText(_textFont, _textSurf, rx, ry, _textLine.c_str(), _textColor); +} + +void BalloonManager_br::textAccum(const Common::String &token, uint16 width) { + if (token.empty()) { + return; + } + + _textWidth += width; + _textLine += token; +} + +void BalloonManager_br::textNewLine() { + _textLines++; + _textWidth = 0; + _textLine.clear(); +} + + +// TODO: really, base this and getStringExtent on some kind of LineTokenizer, instead of +// repeating the algorithm and changing a couple of lines. +void BalloonManager_br::drawWrappedText(Font *font, Graphics::Surface* surf, char *text, byte color, int16 wrapWidth) { + textSetupRendering(text, surf, font, color); + + wrapWidth = 216; + + Common::StringTokenizer tokenizer(text, " "); + Common::String token; + Common::String blank(" "); + + uint16 blankWidth = font->getStringWidth(" "); + uint16 tokenWidth = 0; + + while (!tokenizer.empty()) { + token = tokenizer.nextToken(); + + if (token == '/') { + tokenWidth = 0; + textEmitCenteredLine(); + textNewLine(); + } else { + // todo: expand '%' + tokenWidth = font->getStringWidth(token.c_str()); + + if (_textWidth == 0) { + textAccum(token, tokenWidth); + } else { + if (_textWidth + blankWidth + tokenWidth <= wrapWidth) { + textAccum(blank, blankWidth); + textAccum(token, tokenWidth); + } else { + textEmitCenteredLine(); + textNewLine(); + textAccum(token, tokenWidth); + } + } + } + } + + textEmitCenteredLine(); +} + + + +void BalloonManager_br::getStringExtent(Font *font, const char *text, uint16 maxwidth, int16* width, int16* height) { + extentSetup(font, width, height); + + Common::StringTokenizer tokenizer(text, " "); + Common::String token; + Common::String blank(" "); + + uint16 blankWidth = font->getStringWidth(" "); + uint16 tokenWidth = 0; + + while (!tokenizer.empty()) { + token = tokenizer.nextToken(); + + if (token == '/') { + tokenWidth = 0; + extentAction(); + textNewLine(); + } else { + // todo: expand '%' + tokenWidth = font->getStringWidth(token.c_str()); + + if (_textWidth == 0) { + textAccum(token, tokenWidth); + } else { + if (_textWidth + blankWidth + tokenWidth <= maxwidth) { + textAccum(blank, blankWidth); + textAccum(token, tokenWidth); + } else { + extentAction(); + textNewLine(); + textAccum(token, tokenWidth); + } + } + } + } + + extentAction(); +} + + + + BalloonManager_br::BalloonManager_br(Disk *disk, Gfx *gfx) : _numBalloons(0), _disk(disk), _gfx(gfx), _leftBalloon(0), _rightBalloon(0) { } @@ -453,4 +723,6 @@ void Parallaction::setupBalloonManager() { } } + + } // namespace Parallaction diff --git a/engines/parallaction/callables_ns.cpp b/engines/parallaction/callables_ns.cpp index ed60a193ce..9cd6b610ff 100644 --- a/engines/parallaction/callables_ns.cpp +++ b/engines/parallaction/callables_ns.cpp @@ -37,18 +37,6 @@ namespace Parallaction { -// part completion messages -static const char *endMsg0[] = {"COMPLIMENTI!", "BRAVO!", "CONGRATULATIONS!", "PRIMA!"}; -static const char *endMsg1[] = {"HAI FINITO QUESTA PARTE", "TU AS COMPLETE' CETTE AVENTURE", "YOU HAVE COMPLETED THIS PART", "DU HAST EIN ABENTEUER ERFOLGREICH"}; -static const char *endMsg2[] = {"ORA COMPLETA IL RESTO ", "AVEC SUCCES.", "NOW GO ON WITH THE REST OF", "ZU ENDE GEFUHRT"}; -static const char *endMsg3[] = {"DELL' AVVENTURA", "CONTINUE AVEC LES AUTRES", "THIS ADVENTURE", "MACH' MIT DEN ANDEREN WEITER"}; -// game completion messages -static const char *endMsg4[] = {"COMPLIMENTI!", "BRAVO!", "CONGRATULATIONS!", "PRIMA!"}; -static const char *endMsg5[] = {"HAI FINITO LE TRE PARTI", "TU AS COMPLETE' LES TROIS PARTIES", "YOU HAVE COMPLETED THE THREE PARTS", "DU HAST DREI ABENTEURE ERFOLGREICH"}; -static const char *endMsg6[] = {"DELL' AVVENTURA", "DE L'AVENTURE", "OF THIS ADVENTURE", "ZU ENDE GEFUHRT"}; -static const char *endMsg7[] = {"ED ORA IL GRAN FINALE ", "ET MAINTENANT LE GRAND FINAL", "NOW THE GREAT FINAL", "UND YETZT DER GROSSE SCHLUSS!"}; - - /* intro callables data members */ @@ -143,18 +131,6 @@ static uint16 _rightHandPositions[684] = { 0x00e0, 0x007b, 0x00e0, 0x0077 }; -struct Credit { - const char *_role; - const char *_name; -} _credits[] = { - {"Music and Sound Effects", "MARCO CAPRELLI"}, - {"PC Version", "RICCARDO BALLARINO"}, - {"Project Manager", "LOVRANO CANEPA"}, - {"Production", "BRUNO BOZ"}, - {"Special Thanks to", "LUIGI BENEDICENTI - GILDA and DANILO"}, - {"Copyright 1992 Euclidea s.r.l ITALY", "All rights reserved"} -}; - /* game callables */ @@ -304,15 +280,11 @@ void Parallaction_ns::_c_trasformata(void *parm) { } void Parallaction_ns::_c_offMouse(void *parm) { - _input->showCursor(false); - _engineFlags |= kEngineBlockInput; - return; + _input->setMouseState(MOUSE_DISABLED); } void Parallaction_ns::_c_onMouse(void *parm) { - _engineFlags &= ~kEngineBlockInput; - _input->showCursor(true); - return; + _input->setMouseState(MOUSE_ENABLED_SHOW); } @@ -340,7 +312,7 @@ void Parallaction_ns::_c_endComment(void *param) { g_system->delayMillis(20); } - _input->waitUntilLeftClick(); + _input->waitForButtonEvent(kMouseLeftUp); _balloonMan->freeBalloons(); return; @@ -376,37 +348,12 @@ void Parallaction_ns::_c_finito(void *parm) { setPartComplete(_char); cleanInventory(); - _gfx->setPalette(_gfx->_palette); - - uint id[4]; - - if (allPartsComplete()) { - id[0] = _gfx->createLabel(_menuFont, endMsg4[_language], 1); - id[1] = _gfx->createLabel(_menuFont, endMsg5[_language], 1); - id[2] = _gfx->createLabel(_menuFont, endMsg6[_language], 1); - id[3] = _gfx->createLabel(_menuFont, endMsg7[_language], 1); - } else { - id[0] = _gfx->createLabel(_menuFont, endMsg0[_language], 1); - id[1] = _gfx->createLabel(_menuFont, endMsg1[_language], 1); - id[2] = _gfx->createLabel(_menuFont, endMsg2[_language], 1); - id[3] = _gfx->createLabel(_menuFont, endMsg3[_language], 1); - } - - _gfx->showLabel(id[0], CENTER_LABEL_HORIZONTAL, 70); - _gfx->showLabel(id[1], CENTER_LABEL_HORIZONTAL, 100); - _gfx->showLabel(id[2], CENTER_LABEL_HORIZONTAL, 130); - _gfx->showLabel(id[3], CENTER_LABEL_HORIZONTAL, 160); - _input->waitUntilLeftClick(); + cleanupGame(); - _gfx->freeLabels(); + _gfx->setPalette(_gfx->_palette); - if (allPartsComplete()) { - scheduleLocationSwitch("estgrotta.drki"); - } else { - selectStartLocation(); - } + startEndPartSequence(); - cleanupGame(); return; } @@ -467,52 +414,11 @@ void Parallaction_ns::_c_startIntro(void *parm) { _soundMan->playMusic(); } - _engineFlags |= kEngineBlockInput; - - return; + _input->setMouseState(MOUSE_DISABLED); } void Parallaction_ns::_c_endIntro(void *parm) { - - debugC(1, kDebugExec, "endIntro()"); - - uint id[2]; - for (uint16 _si = 0; _si < 6; _si++) { - id[0] = _gfx->createLabel(_menuFont, _credits[_si]._role, 1); - id[1] = _gfx->createLabel(_menuFont, _credits[_si]._name, 1); - - _gfx->showLabel(id[0], CENTER_LABEL_HORIZONTAL, 80); - _gfx->showLabel(id[1], CENTER_LABEL_HORIZONTAL, 100); - - _gfx->updateScreen(); - - _input->waitForButtonEvent(kMouseLeftUp, 5500); - - _gfx->freeLabels(); - } - debugC(1, kDebugExec, "endIntro(): done showing credits"); - - _soundMan->stopMusic(); - - if ((getFeatures() & GF_DEMO) == 0) { - - id[0] = _gfx->createLabel(_menuFont, "CLICK MOUSE BUTTON TO START", 1); - _gfx->showLabel(id[0], CENTER_LABEL_HORIZONTAL, 80); - - _input->waitUntilLeftClick(); - - _gfx->freeLabels(); - - _engineFlags &= ~kEngineBlockInput; - selectStartLocation(); - - cleanupGame(); - - } else { - _input->waitUntilLeftClick(); - } - - return; + startCreditSequence(); } void Parallaction_ns::_c_moveSheet(void *parm) { diff --git a/engines/parallaction/detection.cpp b/engines/parallaction/detection.cpp index 8841b9ca40..0476b01454 100644 --- a/engines/parallaction/detection.cpp +++ b/engines/parallaction/detection.cpp @@ -154,7 +154,23 @@ static const PARALLACTIONGameDescription gameDescriptions[] = { Common::ADGF_NO_FLAGS }, GType_BRA, - GF_LANG_EN | GF_LANG_FR | GF_LANG_DE | GF_LANG_IT | GF_LANG_MULT + GF_LANG_EN | GF_LANG_FR | GF_LANG_DE | GF_LANG_IT | GF_LANG_MULT, + }, + + { + { + "bra", + "Demo", + { + { "russia.fnt", 0, "0dd55251d2886d6783718df2b184bf97", 10649 }, + { NULL, 0, NULL, 0} + }, + Common::UNK_LANG, + Common::kPlatformPC, + Common::ADGF_DEMO + }, + GType_BRA, + GF_LANG_EN | GF_DEMO, }, // TODO: Base the detection of Amiga BRA on actual data file, not executable file. @@ -171,9 +187,25 @@ static const PARALLACTIONGameDescription gameDescriptions[] = { Common::ADGF_NO_FLAGS }, GType_BRA, - GF_LANG_EN | GF_LANG_FR | GF_LANG_DE | GF_LANG_IT | GF_LANG_MULT + GF_LANG_EN | GF_LANG_FR | GF_LANG_DE | GF_LANG_IT | GF_LANG_MULT, }, + // TODO: Base the detection of Amiga BRA demo on actual data file, not executable file. + { + { + "bra", + "Demo", + { + { "bigred", 0, "b62a7b589fb5e9071f021227640893bf", 97004 }, + { NULL, 0, NULL, 0} + }, + Common::UNK_LANG, + Common::kPlatformAmiga, + Common::ADGF_DEMO + }, + GType_BRA, + GF_LANG_EN | GF_DEMO, + }, { AD_TABLE_END_MARKER, 0, 0 } }; diff --git a/engines/parallaction/dialogue.cpp b/engines/parallaction/dialogue.cpp index 290f8cfd4f..21584a0525 100644 --- a/engines/parallaction/dialogue.cpp +++ b/engines/parallaction/dialogue.cpp @@ -33,7 +33,7 @@ namespace Parallaction { #define MAX_PASSWORD_LENGTH 7 - +/* #define QUESTION_BALLOON_X 140 #define QUESTION_BALLOON_Y 10 #define QUESTION_CHARACTER_X 190 @@ -41,6 +41,25 @@ namespace Parallaction { #define ANSWER_CHARACTER_X 10 #define ANSWER_CHARACTER_Y 80 +*/ +struct BalloonPositions { + Common::Point _questionBalloon; + Common::Point _questionChar; + + Common::Point _answerChar; +}; + +BalloonPositions _balloonPositions_NS = { + Common::Point(140, 10), + Common::Point(190, 80), + Common::Point(10, 80) +}; + +BalloonPositions _balloonPositions_BR = { + Common::Point(0, 0), + Common::Point(380, 80), + Common::Point(10, 80) +}; class DialogueManager { @@ -78,6 +97,7 @@ class DialogueManager { bool _isKeyDown; uint16 _downKey; + BalloonPositions _ballonPos; public: DialogueManager(Parallaction *vm, ZonePtr z); @@ -112,6 +132,15 @@ protected: }; DialogueManager::DialogueManager(Parallaction *vm, ZonePtr z) : _vm(vm), _z(z) { + int gtype = vm->getGameType(); + if (gtype == GType_Nippon) { + _ballonPos = _balloonPositions_NS; + } else + if (gtype == GType_BRA) { + _ballonPos = _balloonPositions_BR; + } else + error("unsupported game in DialogueManager"); + _dialogue = _z->u.speak->_dialogue; isNpc = scumm_stricmp(_z->u.speak->_name, "yourself") && _z->u.speak->_name[0] != '\0'; _questioner = isNpc ? _vm->_disk->loadTalk(_z->u.speak->_name) : _vm->_char._talk; @@ -168,16 +197,16 @@ bool DialogueManager::displayAnswers() { if (_askPassword) { resetPassword(); // _vm->_balloonMan->setDialogueBalloon(_q->_answers[0]->_text, 1, 3); - int id = _vm->_gfx->setItem(_answerer, ANSWER_CHARACTER_X, ANSWER_CHARACTER_Y); + int id = _vm->_gfx->setItem(_answerer, _ballonPos._answerChar.x, _ballonPos._answerChar.y); _vm->_gfx->setItemFrame(id, 0); } else if (_numVisAnswers == 1) { - int id = _vm->_gfx->setItem(_answerer, ANSWER_CHARACTER_X, ANSWER_CHARACTER_Y); + int id = _vm->_gfx->setItem(_answerer, _ballonPos._answerChar.x, _ballonPos._answerChar.y); _vm->_gfx->setItemFrame(id, _q->_answers[0]->_mood & 0xF); _vm->_balloonMan->setBalloonText(0, _q->_answers[_visAnswers[0]]->_text, 0); } else if (_numVisAnswers > 1) { - int id = _vm->_gfx->setItem(_answerer, ANSWER_CHARACTER_X, ANSWER_CHARACTER_Y); + int id = _vm->_gfx->setItem(_answerer, _ballonPos._answerChar.x, _ballonPos._answerChar.y); _vm->_gfx->setItemFrame(id, _q->_answers[_visAnswers[0]]->_mood & 0xF); _oldSelection = -1; _selection = 0; @@ -189,8 +218,8 @@ bool DialogueManager::displayAnswers() { bool DialogueManager::displayQuestion() { if (!scumm_stricmp(_q->_text, "NULL")) return false; - _vm->_balloonMan->setSingleBalloon(_q->_text, QUESTION_BALLOON_X, QUESTION_BALLOON_Y, _q->_mood & 0x10, 0); - int id = _vm->_gfx->setItem(_questioner, QUESTION_CHARACTER_X, QUESTION_CHARACTER_Y); + _vm->_balloonMan->setSingleBalloon(_q->_text, _ballonPos._questionBalloon.x, _ballonPos._questionBalloon.y, _q->_mood & 0x10, 0); + int id = _vm->_gfx->setItem(_questioner, _ballonPos._questionChar.x, _ballonPos._questionChar.y); _vm->_gfx->setItemFrame(id, _q->_mood & 0xF); return true; @@ -359,9 +388,6 @@ void DialogueManager::run() { break; case DIALOGUE_OVER: - if (_cmdList) { - _vm->_cmdExec->run(*_cmdList); - } break; default: @@ -381,6 +407,10 @@ void Parallaction::exitDialogueMode() { debugC(1, kDebugDialogue, "Parallaction::exitDialogueMode()"); _input->_inputMode = Input::kInputModeGame; + if (_dialogueMan->_cmdList) { + _vm->_cmdExec->run(*_dialogueMan->_cmdList); + } + // The current instance of _dialogueMan must be destroyed before the zone commands // are executed, because they may create another instance of _dialogueMan that // overwrite the current one. This would cause headaches (and it did, actually). diff --git a/engines/parallaction/disk.h b/engines/parallaction/disk.h index 694d4efa6d..ee393afd6a 100644 --- a/engines/parallaction/disk.h +++ b/engines/parallaction/disk.h @@ -28,6 +28,8 @@ #define PATH_LEN 200 +#include "common/fs.h" + #include "common/file.h" #include "graphics/surface.h" @@ -202,15 +204,29 @@ public: class DosDisk_br : public Disk { protected: + uint16 _language; + Parallaction *_vm; - char _partPath[PATH_LEN]; - char _languageDir[2]; + + FilesystemNode _baseDir; + FilesystemNode _partDir; + + FilesystemNode _aniDir; + FilesystemNode _bkgDir; + FilesystemNode _mscDir; + FilesystemNode _mskDir; + FilesystemNode _pthDir; + FilesystemNode _rasDir; + FilesystemNode _scrDir; + FilesystemNode _sfxDir; + FilesystemNode _talDir; protected: - void errorFileNotFound(const char *s); + void errorFileNotFound(const FilesystemNode &dir, const Common::String &filename); Font *createFont(const char *name, Common::ReadStream &stream); Sprites* createSprites(Common::ReadStream &stream); void loadBitmap(Common::SeekableReadStream &stream, Graphics::Surface &surf, byte *palette); + GfxObj* createInventoryObjects(Common::SeekableReadStream &stream); public: DosDisk_br(Parallaction *vm); @@ -234,15 +250,34 @@ public: Common::ReadStream* loadSound(const char* name); }; +class DosDemo_br : public DosDisk_br { + +public: + DosDemo_br(Parallaction *vm); + virtual ~DosDemo_br(); + + Common::String selectArchive(const Common::String& name); + +}; + class AmigaDisk_br : public DosDisk_br { protected: BackgroundInfo _backgroundTemp; - Sprites* createSprites(const char *name); + Sprites* createSprites(Common::ReadStream &stream); Font *createFont(const char *name, Common::SeekableReadStream &stream); - void loadMask(BackgroundInfo& info, const char *name); - void loadBackground(BackgroundInfo& info, const char *name); + void loadMask(BackgroundInfo& info, Common::SeekableReadStream &stream); + void loadBackground(BackgroundInfo& info, Common::SeekableReadStream &stream); + + FilesystemNode _baseBkgDir; + FilesystemNode _fntDir; + FilesystemNode _commonAniDir; + FilesystemNode _commonBkgDir; + FilesystemNode _commonMscDir; + FilesystemNode _commonMskDir; + FilesystemNode _commonPthDir; + FilesystemNode _commonTalDir; public: AmigaDisk_br(Parallaction *vm); @@ -254,6 +289,11 @@ public: Frames* loadFrames(const char* name); void loadSlide(BackgroundInfo& info, const char *filename); void loadScenery(BackgroundInfo& info, const char* name, const char* mask, const char* path); + GfxObj* loadObjects(const char *name); + Common::SeekableReadStream* loadMusic(const char* name); + Common::ReadStream* loadSound(const char* name); + Common::String selectArchive(const Common::String& name); + }; } // namespace Parallaction diff --git a/engines/parallaction/disk_br.cpp b/engines/parallaction/disk_br.cpp index ee1e111139..2285c5608e 100644 --- a/engines/parallaction/disk_br.cpp +++ b/engines/parallaction/disk_br.cpp @@ -25,6 +25,7 @@ #include "graphics/iff.h" +#include "common/config-manager.h" #include "parallaction/parallaction.h" @@ -90,49 +91,40 @@ struct Sprites : public Frames { -void DosDisk_br::errorFileNotFound(const char *s) { - error("File '%s' not found", s); +void DosDisk_br::errorFileNotFound(const FilesystemNode &dir, const Common::String &filename) { + error("File '%s' not found in directory '%s'", filename.c_str(), dir.getDisplayName().c_str()); } Common::String DosDisk_br::selectArchive(const Common::String& name) { debugC(5, kDebugDisk, "DosDisk_br::selectArchive"); - Common::String oldPath(_partPath); - strcpy(_partPath, name.c_str()); + Common::String oldPath; + if (_partDir.exists()) { + oldPath = _partDir.getDisplayName(); + } + + _partDir = _baseDir.getChild(name); + + _aniDir = _partDir.getChild("ani"); + _bkgDir = _partDir.getChild("bkg"); + _mscDir = _partDir.getChild("msc"); + _mskDir = _partDir.getChild("msk"); + _pthDir = _partDir.getChild("pth"); + _rasDir = _partDir.getChild("ras"); + _scrDir = _partDir.getChild("scripts"); + _sfxDir = _partDir.getChild("sfx"); + _talDir = _partDir.getChild("tal"); return oldPath; } void DosDisk_br::setLanguage(uint16 language) { debugC(5, kDebugDisk, "DosDisk_br::setLanguage"); - - switch (language) { - case 0: - strcpy(_languageDir, "it"); - break; - - case 1: - strcpy(_languageDir, "fr"); - break; - - case 2: - strcpy(_languageDir, "en"); - break; - - case 3: - strcpy(_languageDir, "ge"); - break; - - default: - error("unknown language"); - - } - - return; + assert(language < 4); + _language = language; } -DosDisk_br::DosDisk_br(Parallaction* vm) : _vm(vm) { - +DosDisk_br::DosDisk_br(Parallaction* vm) : _vm(vm), _baseDir(ConfMan.get("path")) { } DosDisk_br::~DosDisk_br() { @@ -141,45 +133,63 @@ DosDisk_br::~DosDisk_br() { GfxObj* DosDisk_br::loadTalk(const char *name) { debugC(5, kDebugDisk, "DosDisk_br::loadTalk(%s)", name); - Common::File stream; - - char path[PATH_LEN]; - sprintf(path, "%s/tal/%s", _partPath, name); - if (!stream.open(path)) { - sprintf(path, "%s/tal/%s.tal", _partPath, name); - if (!stream.open(path)) - errorFileNotFound(path); + Common::String path(name); + FilesystemNode node = _talDir.getChild(path); + if (!node.exists()) { + path += ".tal"; + node = _talDir.getChild(path); + if (!node.exists()) + errorFileNotFound(_talDir, path); } - return new GfxObj(0, createSprites(stream), name); + Common::File stream; + stream.open(node); + + // talk position is set to (0,0), because talks are always displayed at + // absolute coordinates, set in the dialogue manager. The original used + // to null out coordinates every time they were needed. We do it better! + Sprites *spr = createSprites(stream); + for (int i = 0; i < spr->getNum(); i++) { + spr->_sprites[i].x = 0; + spr->_sprites[i].y = 0; + } + return new GfxObj(0, spr, name); } Script* DosDisk_br::loadLocation(const char *name) { debugC(5, kDebugDisk, "DosDisk_br::loadLocation"); - Common::File *stream = new Common::File; - - char path[PATH_LEN]; - sprintf(path, "%s/%s/%s.slf", _partPath, _languageDir, name); - if (!stream->open(path)) { - sprintf(path, "%s/%s/%s.loc", _partPath, _languageDir, name); - if (!stream->open(path)) - errorFileNotFound(path); + Common::String langs[4] = { "it", "fr", "en", "ge" }; + FilesystemNode locDir = _partDir.getChild(langs[_language]); + + Common::String path(name); + path += ".slf"; + FilesystemNode node = locDir.getChild(path); + if (!node.exists()) { + path = Common::String(name) + ".loc"; + node = locDir.getChild(path); + if (!node.exists()) { + errorFileNotFound(locDir, path); + } } + Common::File *stream = new Common::File; + stream->open(node); return new Script(stream, true); } Script* DosDisk_br::loadScript(const char* name) { debugC(5, kDebugDisk, "DosDisk_br::loadScript"); - Common::File *stream = new Common::File; - - char path[PATH_LEN]; - sprintf(path, "%s/scripts/%s.scr", _partPath, name); - if (!stream->open(path)) - errorFileNotFound(path); + Common::String path(name); + path += ".scr"; + FilesystemNode node = _scrDir.getChild(path); + if (!node.exists()) { + errorFileNotFound(_scrDir, path); + } + Common::File *stream = new Common::File; + stream->open(node); return new Script(stream, true); } @@ -192,6 +202,7 @@ GfxObj* DosDisk_br::loadHead(const char* name) { void DosDisk_br::loadBitmap(Common::SeekableReadStream &stream, Graphics::Surface &surf, byte *palette) { stream.skip(4); uint width = stream.readUint32BE(); + if (width & 1) width++; uint height = stream.readUint32BE(); stream.skip(20); @@ -208,12 +219,15 @@ void DosDisk_br::loadBitmap(Common::SeekableReadStream &stream, Graphics::Surfac Frames* DosDisk_br::loadPointer(const char *name) { debugC(5, kDebugDisk, "DosDisk_br::loadPointer"); - char path[PATH_LEN]; - sprintf(path, "%s.ras", name); + Common::String path(name); + path += ".ras"; + FilesystemNode node = _baseDir.getChild(path); + if (!node.exists()) { + errorFileNotFound(_baseDir, path); + } Common::File stream; - if (!stream.open(path)) - errorFileNotFound(path); + stream.open(node); Graphics::Surface *surf = new Graphics::Surface; loadBitmap(stream, *surf, 0); @@ -224,20 +238,32 @@ Frames* DosDisk_br::loadPointer(const char *name) { Font* DosDisk_br::loadFont(const char* name) { debugC(5, kDebugDisk, "DosDisk_br::loadFont"); - char path[PATH_LEN]; - sprintf(path, "%s.fnt", name); + Common::String path(name); + path += ".fnt"; + FilesystemNode node = _baseDir.getChild(path); + if (!node.exists()) { + errorFileNotFound(_baseDir, path); + } Common::File stream; - if (!stream.open(path)) - errorFileNotFound(path); - + stream.open(node); return createFont(name, stream); } GfxObj* DosDisk_br::loadObjects(const char *name) { debugC(5, kDebugDisk, "DosDisk_br::loadObjects"); - return 0; + + Common::String path(name); + FilesystemNode node = _partDir.getChild(path); + if (!node.exists()) { + errorFileNotFound(_partDir, path); + } + + Common::File stream; + stream.open(node); + + return createInventoryObjects(stream); } void genSlidePath(char *path, const char* name) { @@ -247,13 +273,15 @@ void genSlidePath(char *path, const char* name) { GfxObj* DosDisk_br::loadStatic(const char* name) { debugC(5, kDebugDisk, "DosDisk_br::loadStatic"); - char path[PATH_LEN]; - sprintf(path, "%s/ras/%s", _partPath, name); - Common::File stream; - if (!stream.open(path)) { - errorFileNotFound(path); + Common::String path(name); + FilesystemNode node = _rasDir.getChild(path); + if (!node.exists()) { + errorFileNotFound(_rasDir, path); } + Common::File stream; + stream.open(node); + Graphics::Surface *surf = new Graphics::Surface; loadBitmap(stream, *surf, 0); return new GfxObj(0, new SurfaceToFrames(surf), name); @@ -283,17 +311,18 @@ Sprites* DosDisk_br::createSprites(Common::ReadStream &stream) { Frames* DosDisk_br::loadFrames(const char* name) { debugC(5, kDebugDisk, "DosDisk_br::loadFrames"); - char path[PATH_LEN]; - sprintf(path, "%s/ani/%s", _partPath, name); - - Common::File stream; - if (!stream.open(path)) { - sprintf(path, "%s/ani/%s.ani", _partPath, name); - if (!stream.open(path)) { - errorFileNotFound(path); + Common::String path(name); + FilesystemNode node = _aniDir.getChild(path); + if (!node.exists()) { + path += ".ani"; + node = _aniDir.getChild(path); + if (!node.exists()) { + errorFileNotFound(_aniDir, path); } } + Common::File stream; + stream.open(node); return createSprites(stream); } @@ -305,12 +334,15 @@ Frames* DosDisk_br::loadFrames(const char* name) { void DosDisk_br::loadSlide(BackgroundInfo& info, const char *name) { debugC(5, kDebugDisk, "DosDisk_br::loadSlide"); - char path[PATH_LEN]; - genSlidePath(path, name); + Common::String path(name); + path += ".bmp"; + FilesystemNode node = _baseDir.getChild(path); + if (!node.exists()) { + errorFileNotFound(_baseDir, path); + } Common::File stream; - if (!stream.open(path)) - errorFileNotFound(path); + stream.open(node); byte rgb[768]; @@ -328,13 +360,17 @@ void DosDisk_br::loadSlide(BackgroundInfo& info, const char *name) { void DosDisk_br::loadScenery(BackgroundInfo& info, const char *name, const char *mask, const char* path) { debugC(5, kDebugDisk, "DosDisk_br::loadScenery"); - char filename[PATH_LEN]; + Common::String filepath; + FilesystemNode node; Common::File stream; if (name) { - sprintf(filename, "%s/bkg/%s.bkg", _partPath, name); - if (!stream.open(filename)) - errorFileNotFound(filename); + filepath = Common::String(name) + ".bkg"; + node = _bkgDir.getChild(filepath); + if (!node.exists()) { + errorFileNotFound(_bkgDir, filepath); + } + stream.open(node); byte rgb[768]; @@ -350,9 +386,12 @@ void DosDisk_br::loadScenery(BackgroundInfo& info, const char *name, const char } if (mask) { - sprintf(filename, "%s/msk/%s.msk", _partPath, mask); - if (!stream.open(filename)) - errorFileNotFound(filename); + filepath = Common::String(mask) + ".msk"; + node = _mskDir.getChild(filepath); + if (!node.exists()) { + errorFileNotFound(_mskDir, filepath); + } + stream.open(node); // NOTE: info.width and info.height are only valid if the background graphics // have already been loaded @@ -363,9 +402,12 @@ void DosDisk_br::loadScenery(BackgroundInfo& info, const char *name, const char } if (path) { - sprintf(filename, "%s/pth/%s.pth", _partPath, path); - if (!stream.open(filename)) - errorFileNotFound(filename); + filepath = Common::String(path) + ".pth"; + node = _pthDir.getChild(filepath); + if (!node.exists()) { + errorFileNotFound(_pthDir, filepath); + } + stream.open(node); // NOTE: info.width and info.height are only valid if the background graphics // have already been loaded @@ -380,15 +422,16 @@ void DosDisk_br::loadScenery(BackgroundInfo& info, const char *name, const char Table* DosDisk_br::loadTable(const char* name) { debugC(5, kDebugDisk, "DosDisk_br::loadTable"); - char path[PATH_LEN]; - sprintf(path, "%s/%s.tab", _partPath, name); - - Common::File stream; - if (!stream.open(path)) - errorFileNotFound(path); + Common::String path(name); + path += ".tab"; + FilesystemNode node = _partDir.getChild(path); + if (!node.exists()) { + errorFileNotFound(_partDir, path); + } + Common::File stream; + stream.open(node); Table *t = createTableFromStream(100, stream); - stream.close(); return t; @@ -410,8 +453,55 @@ Common::ReadStream* DosDisk_br::loadSound(const char* name) { +DosDemo_br::DosDemo_br(Parallaction *vm) : DosDisk_br(vm) { + +} + + +DosDemo_br::~DosDemo_br() { + +} + +Common::String DosDemo_br::selectArchive(const Common::String& name) { + debugC(5, kDebugDisk, "DosDemo_br::selectArchive"); + + Common::String oldPath; + if (_partDir.exists()) { + oldPath = _partDir.getDisplayName(); + } + + _partDir = _baseDir; + + _aniDir = _partDir.getChild("ani"); + _bkgDir = _partDir.getChild("bkg"); + _mscDir = _partDir.getChild("msc"); + _mskDir = _partDir.getChild("msk"); + _pthDir = _partDir.getChild("pth"); + _rasDir = _partDir.getChild("ras"); + _scrDir = _partDir.getChild("scripts"); + _sfxDir = _partDir.getChild("sfx"); + _talDir = _partDir.getChild("tal"); + + return oldPath; +} + + + + + + AmigaDisk_br::AmigaDisk_br(Parallaction *vm) : DosDisk_br(vm) { + _fntDir = _baseDir.getChild("fonts"); + _baseBkgDir = _baseDir.getChild("backs"); + + FilesystemNode commonDir = _baseDir.getChild("common"); + _commonAniDir = commonDir.getChild("anims"); + _commonBkgDir = commonDir.getChild("backs"); + _commonMscDir = commonDir.getChild("msc"); + _commonMskDir = commonDir.getChild("msk"); + _commonPthDir = commonDir.getChild("pth"); + _commonTalDir = commonDir.getChild("talks"); } @@ -447,19 +537,11 @@ void buildMask2(byte* buf) { } } -void AmigaDisk_br::loadBackground(BackgroundInfo& info, const char *name) { - - char path[PATH_LEN]; - sprintf(path, "%s", name); - - Common::File s; - - if (!s.open(path)) - errorFileNotFound(path); +void AmigaDisk_br::loadBackground(BackgroundInfo& info, Common::SeekableReadStream &stream) { byte *pal; - Graphics::ILBMDecoder decoder(s, info.bg, pal); + Graphics::ILBMDecoder decoder(stream, info.bg, pal); decoder.decode(); uint i; @@ -483,30 +565,23 @@ void AmigaDisk_br::loadBackground(BackgroundInfo& info, const char *name) { return; } -void AmigaDisk_br::loadMask(BackgroundInfo& info, const char *name) { - debugC(5, kDebugDisk, "AmigaDisk_br::loadMask(%s)", name); - - Common::File s; - - if (!s.open(name)) - return; - - s.seek(0x30, SEEK_SET); +void AmigaDisk_br::loadMask(BackgroundInfo& info, Common::SeekableReadStream &stream) { + stream.seek(0x30, SEEK_SET); byte r, g, b; for (uint i = 0; i < 4; i++) { - r = s.readByte(); - g = s.readByte(); - b = s.readByte(); + r = stream.readByte(); + g = stream.readByte(); + b = stream.readByte(); info.layers[i] = (((r << 4) & 0xF00) | (g & 0xF0) | (b >> 4)) & 0xFF; } - s.seek(0x126, SEEK_SET); // HACK: skipping IFF/ILBM header should be done by analysis, not magic - Graphics::PackBitsReadStream stream(s); + stream.seek(0x126, SEEK_SET); // HACK: skipping IFF/ILBM header should be done by analysis, not magic + Graphics::PackBitsReadStream unpackedStream(stream); info.mask.create(info.width, info.height); - stream.read(info.mask.data, info.mask.size); + unpackedStream.read(info.mask.data, info.mask.size); buildMask2(info.mask.data); return; @@ -515,26 +590,51 @@ void AmigaDisk_br::loadMask(BackgroundInfo& info, const char *name) { void AmigaDisk_br::loadScenery(BackgroundInfo& info, const char* name, const char* mask, const char* path) { debugC(1, kDebugDisk, "AmigaDisk_br::loadScenery '%s', '%s' '%s'", name, mask, path); - char filename[PATH_LEN]; + Common::String filepath; + FilesystemNode node; Common::File stream; if (name) { - sprintf(filename, "%s/backs/%s.bkg", _partPath, name); - - loadBackground(info, filename); + filepath = Common::String(name) + ".bkg"; + node = _bkgDir.getChild(filepath); + if (!node.exists()) { + filepath = Common::String(name) + ".bkg"; + node = _commonBkgDir.getChild(filepath); + if (!node.exists()) { + errorFileNotFound(_bkgDir, filepath); + } + } + stream.open(node); + loadBackground(info, stream); + stream.close(); } - if (mask) { - sprintf(filename, "%s/msk/%s.msk", _partPath, name); + if (mask && _mskDir.exists()) { + filepath = Common::String(mask) + ".msk"; + node = _mskDir.getChild(filepath); + if (!node.exists()) { + filepath = Common::String(mask) + ".msk"; + node = _commonMskDir.getChild(filepath); + } - loadMask(info, filename); + if (node.exists()) { + stream.open(node); + loadMask(info, stream); + stream.close(); + } } - if (path) { - sprintf(filename, "%s/pth/%s.pth", _partPath, path); - if (!stream.open(filename)) - errorFileNotFound(filename); - + if (path && _pthDir.exists()) { + filepath = Common::String(path) + ".pth"; + node = _pthDir.getChild(filepath); + if (!node.exists()) { + filepath = Common::String(path) + ".pth"; + node = _commonPthDir.getChild(filepath); + if (!node.exists()) { + errorFileNotFound(_pthDir, filepath); + } + } + stream.open(node); // NOTE: info.width and info.height are only valid if the background graphics // have already been loaded info.path.create(info.width, info.height); @@ -548,22 +648,28 @@ void AmigaDisk_br::loadScenery(BackgroundInfo& info, const char* name, const cha void AmigaDisk_br::loadSlide(BackgroundInfo& info, const char *name) { debugC(1, kDebugDisk, "AmigaDisk_br::loadSlide '%s'", name); - char path[PATH_LEN]; - sprintf(path, "backs/%s.bkg", name); - - loadBackground(info, path); + Common::String path(name); + path += ".bkg"; + FilesystemNode node = _baseBkgDir.getChild(path); + if (!node.exists()) { + errorFileNotFound(_baseBkgDir, path); + } + Common::File stream; + stream.open(node); + loadBackground(info, stream); return; } GfxObj* AmigaDisk_br::loadStatic(const char* name) { debugC(1, kDebugDisk, "AmigaDisk_br::loadStatic '%s'", name); - char path[PATH_LEN]; - sprintf(path, "%s/ras/%s", _partPath, name); - Common::File stream; - if (!stream.open(path)) { - errorFileNotFound(path); + Common::String path(name); + FilesystemNode node = _rasDir.getChild(path); + if (!node.exists()) { + errorFileNotFound(_rasDir, path); } + Common::File stream; + stream.open(node); byte *pal = 0; Graphics::Surface* surf = new Graphics::Surface; @@ -576,13 +682,7 @@ GfxObj* AmigaDisk_br::loadStatic(const char* name) { return new GfxObj(0, new SurfaceToFrames(surf)); } -Sprites* AmigaDisk_br::createSprites(const char *path) { - - Common::File stream; - if (!stream.open(path)) { - errorFileNotFound(path); - } - +Sprites* AmigaDisk_br::createSprites(Common::ReadStream &stream) { uint16 num = stream.readUint16BE(); Sprites *sprites = new Sprites(num); @@ -606,32 +706,165 @@ Sprites* AmigaDisk_br::createSprites(const char *path) { Frames* AmigaDisk_br::loadFrames(const char* name) { debugC(1, kDebugDisk, "AmigaDisk_br::loadFrames '%s'", name); - char path[PATH_LEN]; - sprintf(path, "%s/anims/%s", _partPath, name); + Common::String path(name); + FilesystemNode node = _aniDir.getChild(path); + if (!node.exists()) { + path += ".ani"; + node = _aniDir.getChild(path); + if (!node.exists()) { + path = Common::String(name); + node = _commonAniDir.getChild(path); + if (!node.exists()) { + path += ".ani"; + node = _commonAniDir.getChild(path); + if (!node.exists()) { + errorFileNotFound(_aniDir, path); + } + } + } + } - return createSprites(path); + Common::File stream; + stream.open(node); + return createSprites(stream); } GfxObj* AmigaDisk_br::loadTalk(const char *name) { debugC(1, kDebugDisk, "AmigaDisk_br::loadTalk '%s'", name); - char path[PATH_LEN]; - sprintf(path, "%s/talks/%s.tal", _partPath, name); + Common::String path(name); + FilesystemNode node = _talDir.getChild(path); + if (!node.exists()) { + path += ".tal"; + node = _talDir.getChild(path); + if (!node.exists()) { + path = Common::String(name); + node = _commonTalDir.getChild(path); + if (!node.exists()) { + path += ".tal"; + node = _commonTalDir.getChild(path); + if (!node.exists()) { + errorFileNotFound(_talDir, path); + } + } + } + } - return new GfxObj(0, createSprites(path)); + Common::File stream; + stream.open(node); + return new GfxObj(0, createSprites(stream)); } Font* AmigaDisk_br::loadFont(const char* name) { debugC(1, kDebugDisk, "AmigaFullDisk::loadFont '%s'", name); - char path[PATH_LEN]; - sprintf(path, "%s", name); + Common::String path(name); + path += ".font"; + FilesystemNode node = _fntDir.getChild(path); + if (!node.exists()) { + errorFileNotFound(_fntDir, path); + } + + Common::String fontDir; + Common::String fontFile; + byte ch; Common::File stream; - if (!stream.open(path)) - errorFileNotFound(path); + stream.open(node); + stream.seek(4, SEEK_SET); + while ((ch = stream.readByte()) != 0x2F) fontDir += ch; + while ((ch = stream.readByte()) != 0) fontFile += ch; + stream.close(); + printf("fontDir = %s, fontFile = %s\n", fontDir.c_str(), fontFile.c_str()); + + node = _fntDir.getChild(fontDir); + if (!node.exists()) { + errorFileNotFound(_fntDir, fontDir); + } + node = node.getChild(fontFile); + if (!node.exists()) { + errorFileNotFound(node, fontFile); + } + + stream.open(node); return createFont(name, stream); } +Common::SeekableReadStream* AmigaDisk_br::loadMusic(const char* name) { + debugC(5, kDebugDisk, "AmigaDisk_br::loadMusic"); + + Common::String path(name); + FilesystemNode node = _mscDir.getChild(path); + if (!node.exists()) { + // TODO (Kirben): error out when music file is not found? + return 0; + } + + Common::File *stream = new Common::File; + stream->open(node); + return stream; +} + + +Common::ReadStream* AmigaDisk_br::loadSound(const char* name) { + debugC(5, kDebugDisk, "AmigaDisk_br::loadSound"); + + Common::String path(name); + FilesystemNode node = _sfxDir.getChild(path); + if (!node.exists()) { + errorFileNotFound(_sfxDir, path); + } + + Common::File *stream = new Common::File; + stream->open(node); + return stream; +} + +GfxObj* AmigaDisk_br::loadObjects(const char *name) { + debugC(5, kDebugDisk, "AmigaDisk_br::loadObjects"); + + Common::String path(name); + FilesystemNode node = _partDir.getChild(path); + if (!node.exists()) { + errorFileNotFound(_partDir, path); + } + + Common::File stream; + stream.open(node); + + byte *pal = 0; + Graphics::Surface* surf = new Graphics::Surface; + + Graphics::ILBMDecoder decoder(stream, *surf, pal); + decoder.decode(); + + free(pal); + + return new GfxObj(0, new SurfaceToFrames(surf)); +} + +Common::String AmigaDisk_br::selectArchive(const Common::String& name) { + debugC(5, kDebugDisk, "AmigaDisk_br::selectArchive"); + + Common::String oldPath; + if (_partDir.exists()) { + oldPath = _partDir.getDisplayName(); + } + + _partDir = _baseDir.getChild(name); + + _aniDir = _partDir.getChild("anims"); + _bkgDir = _partDir.getChild("backs"); + _mscDir = _partDir.getChild("msc"); + _mskDir = _partDir.getChild("msk"); + _pthDir = _partDir.getChild("pth"); + _rasDir = _partDir.getChild("ras"); + _scrDir = _partDir.getChild("scripts"); + _sfxDir = _partDir.getChild("sfx"); + _talDir = _partDir.getChild("talks"); + + return oldPath; +} + } // namespace Parallaction diff --git a/engines/parallaction/exec.h b/engines/parallaction/exec.h index 887d6be526..22e75744f1 100644 --- a/engines/parallaction/exec.h +++ b/engines/parallaction/exec.h @@ -47,14 +47,30 @@ protected: struct ParallactionStruct1 { CommandPtr cmd; ZonePtr z; + bool suspend; } _ctxt; OpcodeSet _opcodes; + struct SuspendedContext { + bool valid; + CommandList::iterator first; + CommandList::iterator last; + ZonePtr zone; + } _suspendedCtxt; + + ZonePtr _execZone; + void runList(CommandList::iterator first, CommandList::iterator last); + void createSuspendList(CommandList::iterator first, CommandList::iterator last); + void cleanSuspendedList(); + public: virtual void init() = 0; virtual void run(CommandList &list, ZonePtr z = nullZonePtr); + void runSuspended(); + CommandExec() { + _suspendedCtxt.valid = false; } virtual ~CommandExec() { for (Common::Array<const Opcode*>::iterator i = _opcodes.begin(); i != _opcodes.end(); ++i) @@ -143,23 +159,23 @@ public: ~CommandExec_br(); }; - - - - class ProgramExec { protected: struct ParallactionStruct2 { AnimationPtr anim; ProgramPtr program; InstructionList::iterator inst; + InstructionList::iterator ip; uint16 modCounter; bool suspend; } _ctxt; + const char **_instructionNames; + OpcodeSet _opcodes; uint16 _modCounter; + void runScript(ProgramPtr script, AnimationPtr a); public: virtual void init() = 0; @@ -183,7 +199,7 @@ protected: DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(off); DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(loop); DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(endloop); - DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(null); + DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(show); DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(call); DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(inc); DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(set); @@ -208,7 +224,6 @@ class ProgramExec_br : public ProgramExec_ns { protected: DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(on); DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(off); - DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(loop); DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(inc); DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(dec); DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(set); @@ -228,7 +243,6 @@ protected: DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(ifgt); DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(endif); DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(stop); - DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(endscript); public: void init(); diff --git a/engines/parallaction/exec_br.cpp b/engines/parallaction/exec_br.cpp index edb832cffc..0b7400f0f7 100644 --- a/engines/parallaction/exec_br.cpp +++ b/engines/parallaction/exec_br.cpp @@ -61,8 +61,6 @@ namespace Parallaction { #define INST_STOP 30 #define INST_ENDSCRIPT 31 - - #define SetOpcodeTable(x) table = &x; typedef Common::Functor0Mem<void, CommandExec_br> OpcodeV1; @@ -73,6 +71,8 @@ typedef Common::Functor0Mem<void, ProgramExec_br> OpcodeV2; #define INSTRUCTION_OPCODE(op) table->push_back(new OpcodeV2(this, &ProgramExec_br::instOp_##op)) #define DECLARE_INSTRUCTION_OPCODE(op) void ProgramExec_br::instOp_##op() +extern const char *_instructionNamesRes_br[]; + void Parallaction_br::setupSubtitles(char *s, char *s2, int y) { debugC(5, kDebugExec, "setupSubtitles(%s, %s, %i)", s, s2, y); @@ -122,11 +122,19 @@ DECLARE_COMMAND_OPCODE(location) { DECLARE_COMMAND_OPCODE(open) { warning("Parallaction_br::cmdOp_open command not yet implemented"); + _ctxt.cmd->u._zone->_flags &= ~kFlagsClosed; + if (_ctxt.cmd->u._zone->u.door->gfxobj) { + _vm->updateDoor(_ctxt.cmd->u._zone); + } } DECLARE_COMMAND_OPCODE(close) { warning("Parallaction_br::cmdOp_close not yet implemented"); + _ctxt.cmd->u._zone->_flags |= kFlagsClosed; + if (_ctxt.cmd->u._zone->u.door->gfxobj) { + _vm->updateDoor(_ctxt.cmd->u._zone); + } } @@ -165,12 +173,13 @@ DECLARE_COMMAND_OPCODE(call) { DECLARE_COMMAND_OPCODE(drop) { - warning("Parallaction_br::cmdOp_drop not yet implemented"); + _vm->dropItem(_ctxt.cmd->u._object); } DECLARE_COMMAND_OPCODE(move) { - warning("Parallaction_br::cmdOp_move not yet implemented"); + _vm->_char.scheduleWalk(_ctxt.cmd->u._move.x, _ctxt.cmd->u._move.y); + _ctxt.suspend = true; } DECLARE_COMMAND_OPCODE(start) { @@ -194,17 +203,17 @@ DECLARE_COMMAND_OPCODE(followme) { DECLARE_COMMAND_OPCODE(onmouse) { - _vm->_input->showCursor(true); + _vm->_input->setMouseState(MOUSE_ENABLED_SHOW); } DECLARE_COMMAND_OPCODE(offmouse) { - _vm->_input->showCursor(false); + _vm->_input->setMouseState(MOUSE_DISABLED); } DECLARE_COMMAND_OPCODE(add) { - warning("Parallaction_br::cmdOp_add not yet implemented"); + _vm->addInventoryItem(_ctxt.cmd->u._object); } @@ -365,13 +374,6 @@ DECLARE_INSTRUCTION_OPCODE(set) { } -DECLARE_INSTRUCTION_OPCODE(loop) { - InstructionPtr inst = *_ctxt.inst; - - _ctxt.program->_loopCounter = inst->_opB.getRValue(); - _ctxt.program->_loopStart = _ctxt.inst; -} - DECLARE_INSTRUCTION_OPCODE(inc) { InstructionPtr inst = *_ctxt.inst; @@ -495,16 +497,6 @@ DECLARE_INSTRUCTION_OPCODE(stop) { warning("Parallaction_br::instOp_stop not yet implemented"); } -DECLARE_INSTRUCTION_OPCODE(endscript) { - if ((_ctxt.anim->_flags & kFlagsLooping) == 0) { - _ctxt.anim->_flags &= ~kFlagsActing; - _vm->_cmdExec->run(_ctxt.anim->_commands, _ctxt.anim); - _ctxt.program->_status = kProgramDone; - } - _ctxt.program->_ip = _ctxt.program->_instructions.begin(); - - _ctxt.suspend = true; -} void CommandExec_br::init() { Common::Array<const Opcode*> *table = 0; @@ -576,7 +568,7 @@ void ProgramExec_br::init() { INSTRUCTION_OPCODE(set); // f INSTRUCTION_OPCODE(loop); INSTRUCTION_OPCODE(endloop); - INSTRUCTION_OPCODE(null); // show + INSTRUCTION_OPCODE(show); // show INSTRUCTION_OPCODE(inc); INSTRUCTION_OPCODE(inc); // dec INSTRUCTION_OPCODE(set); @@ -602,6 +594,7 @@ void ProgramExec_br::init() { } ProgramExec_br::ProgramExec_br(Parallaction_br *vm) : ProgramExec_ns(vm), _vm(vm) { + _instructionNames = _instructionNamesRes_br; } ProgramExec_br::~ProgramExec_br() { diff --git a/engines/parallaction/exec_ns.cpp b/engines/parallaction/exec_ns.cpp index 9cde27a853..3e3ee19a03 100644 --- a/engines/parallaction/exec_ns.cpp +++ b/engines/parallaction/exec_ns.cpp @@ -61,7 +61,7 @@ typedef Common::Functor0Mem<void, ProgramExec_ns> OpcodeV2; #define INSTRUCTION_OPCODE(op) table->push_back(new OpcodeV2(this, &ProgramExec_ns::instOp_##op)) #define DECLARE_INSTRUCTION_OPCODE(op) void ProgramExec_ns::instOp_##op() - +extern const char *_instructionNamesRes_ns[]; DECLARE_INSTRUCTION_OPCODE(on) { @@ -81,13 +81,13 @@ DECLARE_INSTRUCTION_OPCODE(loop) { InstructionPtr inst = *_ctxt.inst; _ctxt.program->_loopCounter = inst->_opB.getRValue(); - _ctxt.program->_loopStart = _ctxt.inst; + _ctxt.program->_loopStart = _ctxt.ip; } DECLARE_INSTRUCTION_OPCODE(endloop) { if (--_ctxt.program->_loopCounter > 0) { - _ctxt.inst = _ctxt.program->_loopStart; + _ctxt.ip = _ctxt.program->_loopStart; } } @@ -97,7 +97,7 @@ DECLARE_INSTRUCTION_OPCODE(inc) { if (inst->_flags & kInstMod) { // mod int16 _bx = (_si > 0 ? _si : -_si); - if (_modCounter % _bx != 0) return; + if (_ctxt.modCounter % _bx != 0) return; _si = (_si > 0 ? 1 : -1); } @@ -142,8 +142,8 @@ DECLARE_INSTRUCTION_OPCODE(put) { _vm->_gfx->patchBackground(v18, x, y, mask); } -DECLARE_INSTRUCTION_OPCODE(null) { - +DECLARE_INSTRUCTION_OPCODE(show) { + _ctxt.suspend = true; } DECLARE_INSTRUCTION_OPCODE(invalid) { @@ -156,8 +156,10 @@ DECLARE_INSTRUCTION_OPCODE(call) { DECLARE_INSTRUCTION_OPCODE(wait) { - if (_engineFlags & kEngineWalking) + if (_engineFlags & kEngineWalking) { + _ctxt.ip--; _ctxt.suspend = true; + } } @@ -186,8 +188,8 @@ DECLARE_INSTRUCTION_OPCODE(endscript) { _vm->_cmdExec->run(_ctxt.anim->_commands, _ctxt.anim); _ctxt.program->_status = kProgramDone; } - _ctxt.program->_ip = _ctxt.program->_instructions.begin(); + _ctxt.ip = _ctxt.program->_instructions.begin(); _ctxt.suspend = true; } @@ -331,35 +333,40 @@ void Parallaction_ns::drawAnimations() { for (AnimationList::iterator it = _location._animations.begin(); it != _location._animations.end(); it++) { - AnimationPtr v18 = *it; - GfxObj *obj = v18->gfxobj; + AnimationPtr anim = *it; + GfxObj *obj = anim->gfxobj; + + // Validation is performed here, so that every animation is affected, instead that only the ones + // who *own* a script. In fact, some scripts can change values in other animations. + // The right way to do this would be to enforce validation when any variable is modified from + // a script. + anim->validateScriptVars(); - if ((v18->_flags & kFlagsActive) && ((v18->_flags & kFlagsRemove) == 0)) { + if ((anim->_flags & kFlagsActive) && ((anim->_flags & kFlagsRemove) == 0)) { - int16 frame = CLIP((int)v18->_frame, 0, v18->getFrameNum()-1); - if (v18->_flags & kFlagsNoMasked) + if (anim->_flags & kFlagsNoMasked) layer = 3; else - layer = _gfx->_backgroundInfo.getLayer(v18->_top + v18->height()); + layer = _gfx->_backgroundInfo.getLayer(anim->_top + anim->height()); if (obj) { _gfx->showGfxObj(obj, true); - obj->frame = frame; - obj->x = v18->_left; - obj->y = v18->_top; - obj->z = v18->_z; + obj->frame = anim->_frame; + obj->x = anim->_left; + obj->y = anim->_top; + obj->z = anim->_z; obj->layer = layer; } } - if (((v18->_flags & kFlagsActive) == 0) && (v18->_flags & kFlagsRemove)) { - v18->_flags &= ~kFlagsRemove; - v18->_oldPos.x = -1000; + if (((anim->_flags & kFlagsActive) == 0) && (anim->_flags & kFlagsRemove)) { + anim->_flags &= ~kFlagsRemove; + anim->_oldPos.x = -1000; } - if ((v18->_flags & kFlagsActive) && (v18->_flags & kFlagsRemove)) { - v18->_flags &= ~kFlagsActive; - v18->_flags |= kFlagsRemove; + if ((anim->_flags & kFlagsActive) && (anim->_flags & kFlagsRemove)) { + anim->_flags &= ~kFlagsActive; + anim->_flags |= kFlagsRemove; if (obj) { _gfx->showGfxObj(obj, false); } @@ -371,14 +378,41 @@ void Parallaction_ns::drawAnimations() { return; } +void ProgramExec::runScript(ProgramPtr script, AnimationPtr a) { + debugC(9, kDebugExec, "runScript(Animation = %s)", a->_name); + + _ctxt.ip = script->_ip; + _ctxt.anim = a; + _ctxt.program = script; + _ctxt.suspend = false; + _ctxt.modCounter = _modCounter; + + InstructionList::iterator inst; + for ( ; (a->_flags & kFlagsActing) ; ) { + + inst = _ctxt.ip; + _ctxt.inst = inst; + _ctxt.ip++; + + debugC(9, kDebugExec, "inst [%02i] %s\n", (*inst)->_index, _instructionNames[(*inst)->_index - 1]); + + script->_status = kProgramRunning; + + (*_opcodes[(*inst)->_index])(); + + if (_ctxt.suspend) + break; + + } + script->_ip = _ctxt.ip; + +} void ProgramExec::runScripts(ProgramList::iterator first, ProgramList::iterator last) { if (_engineFlags & kEnginePauseJobs) { return; } - debugC(9, kDebugExec, "runScripts"); - for (ProgramList::iterator it = first; it != last; it++) { AnimationPtr a = (*it)->_anim; @@ -389,31 +423,8 @@ void ProgramExec::runScripts(ProgramList::iterator first, ProgramList::iterator if ((a->_flags & kFlagsActing) == 0) continue; - InstructionList::iterator inst = (*it)->_ip; - while (((*inst)->_index != INST_SHOW) && (a->_flags & kFlagsActing)) { - - (*it)->_status = kProgramRunning; - - debugC(9, kDebugExec, "Animation: %s, instruction: %i", a->_name, (*inst)->_index); //_instructionNamesRes[(*inst)->_index - 1]); - - _ctxt.inst = inst; - _ctxt.anim = AnimationPtr(a); - _ctxt.program = *it; - _ctxt.suspend = false; - - (*_opcodes[(*inst)->_index])(); + runScript(*it, a); - inst = _ctxt.inst; // handles endloop correctly - - if (_ctxt.suspend) - goto label1; - - inst++; - } - - (*it)->_ip = ++inst; - -label1: if (a->_flags & kFlagsCharacter) a->_z = a->_top + a->height(); } @@ -423,23 +434,18 @@ label1: return; } -void CommandExec::run(CommandList& list, ZonePtr z) { - if (list.size() == 0) { - debugC(3, kDebugExec, "runCommands: nothing to do"); - return; - } - - debugC(3, kDebugExec, "runCommands starting"); +void CommandExec::runList(CommandList::iterator first, CommandList::iterator last) { uint32 useFlags = 0; bool useLocalFlags; - CommandList::iterator it = list.begin(); - for ( ; it != list.end(); it++) { + _ctxt.suspend = false; + + for ( ; first != last; first++) { if (_engineFlags & kEngineQuit) break; - CommandPtr cmd = *it; + CommandPtr cmd = *first; if (cmd->_flagsOn & kFlagsGlobal) { useFlags = _commandFlags | kFlagsGlobal; @@ -457,16 +463,65 @@ void CommandExec::run(CommandList& list, ZonePtr z) { if (!onMatch || !offMatch) continue; - _ctxt.z = z; + _ctxt.z = _execZone; _ctxt.cmd = cmd; (*_opcodes[cmd->_id])(); + + if (_ctxt.suspend) { + createSuspendList(++first, last); + return; + } + } + +} + +void CommandExec::run(CommandList& list, ZonePtr z) { + if (list.size() == 0) { + debugC(3, kDebugExec, "runCommands: nothing to do"); + return; } + _execZone = z; + + debugC(3, kDebugExec, "runCommands starting"); + runList(list.begin(), list.end()); debugC(3, kDebugExec, "runCommands completed"); +} - return; +void CommandExec::createSuspendList(CommandList::iterator first, CommandList::iterator last) { + if (first == last) { + return; + } + + debugC(3, kDebugExec, "CommandExec::createSuspendList()"); + + _suspendedCtxt.valid = true; + _suspendedCtxt.first = first; + _suspendedCtxt.last = last; + _suspendedCtxt.zone = _execZone; +} +void CommandExec::cleanSuspendedList() { + debugC(3, kDebugExec, "CommandExec::cleanSuspended()"); + + _suspendedCtxt.valid = false; + _suspendedCtxt.first = _suspendedCtxt.last; + _suspendedCtxt.zone = nullZonePtr; +} + +void CommandExec::runSuspended() { + if (_engineFlags & kEngineWalking) { + return; + } + + if (_suspendedCtxt.valid) { + debugC(3, kDebugExec, "CommandExec::runSuspended()"); + + _execZone = _suspendedCtxt.zone; + runList(_suspendedCtxt.first, _suspendedCtxt.last); + cleanSuspendedList(); + } } CommandExec_ns::CommandExec_ns(Parallaction_ns* vm) : _vm(vm) { @@ -481,35 +536,69 @@ CommandExec_ns::~CommandExec_ns() { // ZONE TYPE: EXAMINE // -void Parallaction::displayComment(ExamineData *data) { +void Parallaction::enterCommentMode(ZonePtr z) { + if (!z) { + return; + } + + _commentZone = z; + + ExamineData *data = _commentZone->u.examine; + if (!data->_description) { return; } - int id; + // TODO: move this balloons stuff into DialogueManager and BalloonManager + if (getGameType() == GType_Nippon) { + int id; + if (data->_filename) { + if (data->_cnv == 0) { + data->_cnv = _disk->loadStatic(data->_filename); + } - if (data->_filename) { - if (data->_cnv == 0) { - data->_cnv = _disk->loadStatic(data->_filename); + _gfx->setHalfbriteMode(true); + _balloonMan->setSingleBalloon(data->_description, 0, 90, 0, 0); + Common::Rect r; + data->_cnv->getRect(0, r); + id = _gfx->setItem(data->_cnv, 140, (_screenHeight - r.height())/2); + _gfx->setItemFrame(id, 0); + id = _gfx->setItem(_char._head, 100, 152); + _gfx->setItemFrame(id, 0); + } else { + _balloonMan->setSingleBalloon(data->_description, 140, 10, 0, 0); + id = _gfx->setItem(_char._talk, 190, 80); + _gfx->setItemFrame(id, 0); } - - _gfx->setHalfbriteMode(true); - _balloonMan->setSingleBalloon(data->_description, 0, 90, 0, 0); - Common::Rect r; - data->_cnv->getRect(0, r); - id = _gfx->setItem(data->_cnv, 140, (_screenHeight - r.height())/2); - _gfx->setItemFrame(id, 0); - id = _gfx->setItem(_char._head, 100, 152); - _gfx->setItemFrame(id, 0); - } else { - _balloonMan->setSingleBalloon(data->_description, 140, 10, 0, 0); - id = _gfx->setItem(_char._talk, 190, 80); + } else + if (getGameType() == GType_BRA) { + _balloonMan->setSingleBalloon(data->_description, 0, 0, 1, 0); + int id = _gfx->setItem(_char._talk, 10, 80); _gfx->setItemFrame(id, 0); } _input->_inputMode = Input::kInputModeComment; } +void Parallaction::exitCommentMode() { + _input->_inputMode = Input::kInputModeGame; + + hideDialogueStuff(); + _gfx->setHalfbriteMode(false); + + _cmdExec->run(_commentZone->_commands, _commentZone); + _commentZone = nullZonePtr; +} + +void Parallaction::runCommentFrame() { + if (_input->_inputMode != Input::kInputModeComment) { + return; + } + + if (_input->getLastButtonEvent() == kMouseLeftUp) { + exitCommentMode(); + } +} uint16 Parallaction::runZone(ZonePtr z) { @@ -521,8 +610,8 @@ uint16 Parallaction::runZone(ZonePtr z) { switch(subtype) { case kZoneExamine: - displayComment(z->u.examine); - break; + enterCommentMode(z); + return 0; case kZoneGet: if (z->_flags & kFlagsFixed) break; @@ -713,7 +802,7 @@ void ProgramExec_ns::init() { INSTRUCTION_OPCODE(set); // f INSTRUCTION_OPCODE(loop); INSTRUCTION_OPCODE(endloop); - INSTRUCTION_OPCODE(null); + INSTRUCTION_OPCODE(show); INSTRUCTION_OPCODE(inc); INSTRUCTION_OPCODE(inc); // dec INSTRUCTION_OPCODE(set); @@ -728,6 +817,7 @@ void ProgramExec_ns::init() { } ProgramExec_ns::ProgramExec_ns(Parallaction_ns *vm) : _vm(vm) { + _instructionNames = _instructionNamesRes_ns; } ProgramExec_ns::~ProgramExec_ns() { diff --git a/engines/parallaction/font.cpp b/engines/parallaction/font.cpp index 91848b30a4..2a379828ae 100644 --- a/engines/parallaction/font.cpp +++ b/engines/parallaction/font.cpp @@ -35,6 +35,7 @@ extern byte _amigaTopazFont[]; class BraFont : public Font { +protected: byte *_cp; uint _bufPitch; @@ -45,15 +46,15 @@ class BraFont : public Font { uint *_offsets; byte *_data; - - static byte _charMap[]; + const byte *_charMap; byte mapChar(byte c) { - return _charMap[c]; + return (_charMap == 0) ? c : _charMap[c]; } public: - BraFont(Common::ReadStream &stream) { + BraFont(Common::ReadStream &stream, const byte *charMap = 0) { + _charMap = charMap; _numGlyphs = stream.readByte(); _height = stream.readUint32BE(); @@ -137,7 +138,7 @@ public: }; -byte BraFont::_charMap[] = { +const byte _braDosFullCharMap[256] = { // 0 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, // 1 @@ -172,6 +173,111 @@ byte BraFont::_charMap[] = { 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34 }; +const byte _braDosDemoComicCharMap[] = { +// 0 + 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, +// 1 + 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, +// 2 + 0x34, 0x49, 0x48, 0x34, 0x34, 0x34, 0x34, 0x47, 0x34, 0x34, 0x34, 0x34, 0x40, 0x34, 0x3F, 0x34, +// 3 + 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x46, 0x45, 0x34, 0x34, 0x34, 0x42, +// 4 + 0x34, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, +// 5 + 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x34, 0x34, 0x34, 0x34, 0x34, +// 6 + 0x34, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, +// 7 + 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x34, 0x34, 0x34, 0x34, +// 8 + 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, +// 9 + 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, +// A + 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, +// B + 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, +// C + 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, +// D + 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, +// E + 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, +// F + 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34 +}; + +const byte _braDosDemoRussiaCharMap[] = { +// 0 + 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, +// 1 + 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, +// 2 + 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, +// 3 + 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, +// 4 + 0x34, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, +// 5 + 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x34, 0x34, 0x34, 0x34, 0x34, +// 6 + 0x34, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, +// 7 + 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x34, 0x34, 0x34, 0x34, +// 8 + 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, +// 9 + 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, +// A + 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, +// B + 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, +// C + 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, +// D + 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, +// E + 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, +// F + 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34 +}; + +class BraInventoryObjects : public BraFont, public Frames { + +public: + BraInventoryObjects(Common::ReadStream &stream) : BraFont(stream) { + } + + // Frames implementation + uint16 getNum() { + return _numGlyphs; + } + + byte* getData(uint16 index) { + assert(index < _numGlyphs); + return _data + (_height * _widths[index]) * index;; + } + + void getRect(uint16 index, Common::Rect &r) { + assert(index < _numGlyphs); + r.left = 0; + r.top = 0; + r.setWidth(_widths[index]); + r.setHeight(_height); + } + + uint getRawSize(uint16 index) { + assert(index < _numGlyphs); + return _widths[index] * _height; + } + + uint getSize(uint16 index) { + assert(index < _numGlyphs); + return _widths[index] * _height; + } + +}; class DosFont : public Font { @@ -537,7 +643,19 @@ Font *AmigaDisk_ns::createFont(const char *name, Common::SeekableReadStream &str Font *DosDisk_br::createFont(const char *name, Common::ReadStream &stream) { // printf("DosDisk_br::createFont(%s)\n", name); - return new BraFont(stream); + Font *font; + + if (_vm->getFeatures() & GF_DEMO) { + if (!scumm_stricmp(name, "russia")) { + font = new BraFont(stream, _braDosDemoRussiaCharMap); + } else { + font = new BraFont(stream, _braDosDemoComicCharMap); + } + } else { + font = new BraFont(stream, _braDosFullCharMap); + } + + return font; } Font *AmigaDisk_br::createFont(const char *name, Common::SeekableReadStream &stream) { @@ -545,6 +663,12 @@ Font *AmigaDisk_br::createFont(const char *name, Common::SeekableReadStream &str return new AmigaFont(stream); } +GfxObj* DosDisk_br::createInventoryObjects(Common::SeekableReadStream &stream) { + Frames *frames = new BraInventoryObjects(stream); + return new GfxObj(0, frames, "inventoryobjects"); +} + + void Parallaction_ns::initFonts() { if (getPlatform() == Common::kPlatformPC) { @@ -573,8 +697,8 @@ void Parallaction_br::initFonts() { // fonts/sonya/18 // fonts/vanya/16 - _menuFont = _disk->loadFont("fonts/natasha/16"); - _dialogueFont = _disk->loadFont("fonts/sonya/18"); + _menuFont = _disk->loadFont("natasha"); + _dialogueFont = _disk->loadFont("sonya"); Common::MemoryReadStream stream(_amigaTopazFont, 2600, false); _labelFont = new AmigaFont(stream); } diff --git a/engines/parallaction/gfxbase.cpp b/engines/parallaction/gfxbase.cpp index e8250ac8fd..aa02253cb1 100644 --- a/engines/parallaction/gfxbase.cpp +++ b/engines/parallaction/gfxbase.cpp @@ -32,7 +32,7 @@ namespace Parallaction { -GfxObj::GfxObj(uint objType, Frames *frames, const char* name) : type(objType), _frames(frames), x(0), y(0), z(0), frame(0), layer(3), _flags(kGfxObjNormal), _keep(true) { +GfxObj::GfxObj(uint objType, Frames *frames, const char* name) : _frames(frames), _keep(true), x(0), y(0), z(0), _flags(kGfxObjNormal), type(objType), frame(0), layer(3) { if (name) { _name = strdup(name); } else { @@ -209,63 +209,6 @@ void Gfx::drawText(Font *font, Graphics::Surface* surf, uint16 x, uint16 y, cons font->drawString(dst, surf->w, text); } -void Gfx::drawWrappedText(Font *font, Graphics::Surface* surf, char *text, byte color, int16 wrapwidth) { - - uint16 lines = 0; - uint16 linewidth = 0; - - uint16 rx = 10; - uint16 ry = 4; - - uint16 blankWidth = font->getStringWidth(" "); - uint16 tokenWidth = 0; - - char token[MAX_TOKEN_LEN]; - - if (wrapwidth == -1) - wrapwidth = _vm->_screenWidth; - - while (strlen(text) > 0) { - - text = parseNextToken(text, token, MAX_TOKEN_LEN, " ", true); - - if (!scumm_stricmp(token, "%p")) { - lines++; - rx = 10; - ry = 4 + lines*10; // y - - strcpy(token, "> ......."); - strncpy(token+2, _password, strlen(_password)); - tokenWidth = font->getStringWidth(token); - } else { - tokenWidth = font->getStringWidth(token); - - linewidth += tokenWidth; - - if (linewidth > wrapwidth) { - // wrap line - lines++; - rx = 10; // x - ry = 4 + lines*10; // y - linewidth = tokenWidth; - } - - if (!scumm_stricmp(token, "%s")) { - sprintf(token, "%d", _score); - } - - } - - drawText(font, surf, rx, ry, token, color); - - rx += tokenWidth + blankWidth; - linewidth += blankWidth; - - text = Common::ltrim(text); - } - -} - #if 0 void Gfx::unpackBlt(const Common::Rect& r, byte *data, uint size, Graphics::Surface *surf, uint16 z, byte transparentColor) { diff --git a/engines/parallaction/graphics.cpp b/engines/parallaction/graphics.cpp index 9b9ea8605a..a31b7f4994 100644 --- a/engines/parallaction/graphics.cpp +++ b/engines/parallaction/graphics.cpp @@ -153,6 +153,13 @@ void Palette::setEntry(uint index, int red, int green, int blue) { _data[index*3+2] = blue & 0xFF; } +void Palette::getEntry(uint index, int &red, int &green, int &blue) { + assert(index < _colors); + red = _data[index*3]; + green = _data[index*3+1]; + blue = _data[index*3+2]; +} + void Palette::makeGrayscale() { byte v; for (uint16 i = 0; i < _colors; i++) { @@ -307,10 +314,14 @@ void Gfx::setProjectorProgram(int16 *data) { } void Gfx::drawInventory() { - +/* if ((_engineFlags & kEngineInventory) == 0) { return; } +*/ + if (_vm->_input->_inputMode != Input::kInputModeInventory) { + return; + } Common::Rect r; _vm->_inventoryRenderer->getRect(r); @@ -348,29 +359,37 @@ void Gfx::clearScreen() { } void Gfx::beginFrame() { - - int32 oldBackgroundMode = _varBackgroundMode; - _varBackgroundMode = getVar("background_mode"); - - if (oldBackgroundMode != _varBackgroundMode) { - switch (_varBackgroundMode) { - case 1: - _bitmapMask.free(); - break; - case 2: - _bitmapMask.create(_backgroundInfo.width, _backgroundInfo.height, 1); - byte *data = (byte*)_bitmapMask.pixels; - for (uint y = 0; y < _bitmapMask.h; y++) { - for (uint x = 0; x < _bitmapMask.w; x++) { - *data++ = _backgroundInfo.mask.getValue(x, y); + _skipBackground = (_backgroundInfo.bg.pixels == 0); // don't render frame if background is missing + + if (!_skipBackground) { + int32 oldBackgroundMode = _varBackgroundMode; + _varBackgroundMode = getVar("background_mode"); + if (oldBackgroundMode != _varBackgroundMode) { + switch (_varBackgroundMode) { + case 1: + _bitmapMask.free(); + break; + case 2: + _bitmapMask.create(_backgroundInfo.width, _backgroundInfo.height, 1); + byte *data = (byte*)_bitmapMask.pixels; + for (uint y = 0; y < _bitmapMask.h; y++) { + for (uint x = 0; x < _bitmapMask.w; x++) { + *data++ = _backgroundInfo.mask.getValue(x, y); + } } + break; } - break; } } + _varDrawPathZones = getVar("draw_path_zones"); + if (_varDrawPathZones == 1 && _vm->getGameType() != GType_BRA) { + setVar("draw_path_zones", 0); + _varDrawPathZones = 0; + warning("Path zones are supported only in Big Red Adventure"); + } - if (_vm->_screenWidth >= _backgroundInfo.width) { + if (_skipBackground || (_vm->_screenWidth >= _backgroundInfo.width)) { _varScrollX = 0; } else { _varScrollX = getVar("scroll_x"); @@ -395,24 +414,38 @@ int32 Gfx::getRenderMode(const char *type) { void Gfx::updateScreen() { - // background may not cover the whole screen, so adjust bulk update size - uint w = MIN(_vm->_screenWidth, (int32)_backgroundInfo.width); - uint h = MIN(_vm->_screenHeight, (int32)_backgroundInfo.height); + if (!_skipBackground) { + // background may not cover the whole screen, so adjust bulk update size + uint w = MIN(_vm->_screenWidth, (int32)_backgroundInfo.width); + uint h = MIN(_vm->_screenHeight, (int32)_backgroundInfo.height); - byte *backgroundData = 0; - uint16 backgroundPitch = 0; - switch (_varBackgroundMode) { - case 1: - backgroundData = (byte*)_backgroundInfo.bg.getBasePtr(_varScrollX, 0); - backgroundPitch = _backgroundInfo.bg.pitch; - break; - case 2: - backgroundData = (byte*)_bitmapMask.getBasePtr(_varScrollX, 0); - backgroundPitch = _bitmapMask.pitch; - break; + byte *backgroundData = 0; + uint16 backgroundPitch = 0; + switch (_varBackgroundMode) { + case 1: + backgroundData = (byte*)_backgroundInfo.bg.getBasePtr(_varScrollX, 0); + backgroundPitch = _backgroundInfo.bg.pitch; + break; + case 2: + backgroundData = (byte*)_bitmapMask.getBasePtr(_varScrollX, 0); + backgroundPitch = _bitmapMask.pitch; + break; + } + g_system->copyRectToScreen(backgroundData, backgroundPitch, _backgroundInfo.x, _backgroundInfo.y, w, h); } - g_system->copyRectToScreen(backgroundData, backgroundPitch, _backgroundInfo.x, _backgroundInfo.y, w, h); + if (_varDrawPathZones == 1) { + Graphics::Surface *surf = g_system->lockScreen(); + ZoneList::iterator b = _vm->_location._zones.begin(); + ZoneList::iterator e = _vm->_location._zones.end(); + for (; b != e; b++) { + ZonePtr z = *b; + if (z->_type & kZonePath) { + surf->frameRect(Common::Rect(z->_left, z->_top, z->_right, z->_bottom), 2); + } + } + g_system->unlockScreen(); + } _varRenderMode = _varAnimRenderMode; @@ -674,49 +707,6 @@ void Gfx::drawLabels() { } -void Gfx::getStringExtent(Font *font, char *text, uint16 maxwidth, int16* width, int16* height) { - - uint16 lines = 0; - uint16 w = 0; - *width = 0; - - uint16 blankWidth = font->getStringWidth(" "); - uint16 tokenWidth = 0; - - char token[MAX_TOKEN_LEN]; - - while (strlen(text) != 0) { - - text = parseNextToken(text, token, MAX_TOKEN_LEN, " ", true); - tokenWidth = font->getStringWidth(token); - - w += tokenWidth; - - if (!scumm_stricmp(token, "%p")) { - lines++; - } else { - if (w > maxwidth) { - w -= tokenWidth; - lines++; - if (w > *width) - *width = w; - - w = tokenWidth; - } - } - - w += blankWidth; - text = Common::ltrim(text); - } - - if (*width < w) *width = w; - *width += 10; - - *height = lines * 10 + 20; - - return; -} - void Gfx::copyRect(const Common::Rect &r, Graphics::Surface &src, Graphics::Surface &dst) { @@ -769,6 +759,16 @@ Gfx::Gfx(Parallaction* vm) : registerVar("anim_render_mode", 1); registerVar("misc_render_mode", 1); + registerVar("draw_path_zones", 0); + + if ((_vm->getGameType() == GType_BRA) && (_vm->getPlatform() == Common::kPlatformPC)) { + // this loads the backup palette needed by the PC version of BRA (see setBackground()). + BackgroundInfo paletteInfo; + _disk->loadSlide(paletteInfo, "pointer"); + _backupPal.clone(paletteInfo.palette); + paletteInfo.free(); + } + return; } @@ -839,10 +839,24 @@ void Gfx::setBackground(uint type, const char* name, const char* mask, const cha if (type == kBackgroundLocation) { _disk->loadScenery(_backgroundInfo, name, mask, path); + + // The PC version of BRA needs the entries 20-31 of the palette to be constant, but + // the background resource files are screwed up. The right colors come from an unused + // bitmap (pointer.bmp). Nothing is known about the Amiga version so far. + if ((_vm->getGameType() == GType_BRA) && (_vm->getPlatform() == Common::kPlatformPC)) { + int r, g, b; + for (uint i = 16; i < 32; i++) { + _backupPal.getEntry(i, r, g, b); + _backgroundInfo.palette.setEntry(i, r, g, b); + } + } + setPalette(_backgroundInfo.palette); _palette.clone(_backgroundInfo.palette); } else { _disk->loadSlide(_backgroundInfo, name); + for (uint i = 0; i < 6; i++) + _backgroundInfo.ranges[i]._flags = 0; // disable palette cycling for slides setPalette(_backgroundInfo.palette); } diff --git a/engines/parallaction/graphics.h b/engines/parallaction/graphics.h index a7242ba6f4..52b7a4b870 100644 --- a/engines/parallaction/graphics.h +++ b/engines/parallaction/graphics.h @@ -261,6 +261,7 @@ public: void makeBlack(); void setEntries(byte* data, uint first, uint num); + void getEntry(uint index, int &red, int &green, int &blue); void setEntry(uint index, int red, int green, int blue); void makeGrayscale(); void fadeTo(const Palette& target, uint step); @@ -471,6 +472,9 @@ typedef Common::HashMap<Common::String, int32, Common::IgnoreCase_Hash, Common:: class Gfx { +protected: + Parallaction* _vm; + public: Disk *_disk; VarMap _vars; @@ -496,7 +500,6 @@ public: void freeLabels(); // dialogue balloons - void getStringExtent(Font *font, char *text, uint16 maxwidth, int16* width, int16* height); GfxObj* registerBalloon(Frames *frames, const char *text); void destroyBalloons(); @@ -550,18 +553,23 @@ public: byte *_unpackedBitmap; protected: - Parallaction* _vm; bool _halfbrite; + bool _skipBackground; + Common::Point _hbCirclePos; int _hbCircleRadius; + // BRA specific + Palette _backupPal; + // frame data stored in programmable variables int32 _varBackgroundMode; // 1 = normal, 2 = only mask int32 _varScrollX; int32 _varAnimRenderMode; // 1 = normal, 2 = flat int32 _varMiscRenderMode; // 1 = normal, 2 = flat int32 _varRenderMode; + int32 _varDrawPathZones; // 0 = don't draw, 1 = draw Graphics::Surface _bitmapMask; int32 getRenderMode(const char *type); @@ -592,7 +600,6 @@ public: // low level text and patches void drawText(Font *font, Graphics::Surface* surf, uint16 x, uint16 y, const char *text, byte color); - void drawWrappedText(Font *font, Graphics::Surface* surf, char *text, byte color, int16 wrapwidth); void drawGfxObject(GfxObj *obj, Graphics::Surface &surf, bool scene); void blt(const Common::Rect& r, byte *data, Graphics::Surface *surf, uint16 z, byte transparentColor); diff --git a/engines/parallaction/gui.cpp b/engines/parallaction/gui.cpp new file mode 100644 index 0000000000..2dbe64fcf6 --- /dev/null +++ b/engines/parallaction/gui.cpp @@ -0,0 +1,92 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ + +#include "parallaction/gui.h" + +namespace Parallaction { + +bool MenuInputHelper::run() { + if (_newState == 0) { + debugC(3, kDebugExec, "MenuInputHelper has set NULL state"); + return false; + } + + if (_newState != _state) { + debugC(3, kDebugExec, "MenuInputHelper changing state to '%s'", _newState->_name.c_str()); + + _newState->enter(); + _state = _newState; + } + + _newState = _state->run(); + + return true; +} + +MenuInputHelper::~MenuInputHelper() { + StateMap::iterator b = _map.begin(); + for ( ; b != _map.end(); b++) { + delete b->_value; + } + _map.clear(); +} + + +void Parallaction::runGuiFrame() { + if (_input->_inputMode != Input::kInputModeMenu) { + return; + } + + if (!_menuHelper) { + error("No menu helper defined!"); + } + + bool res = _menuHelper->run(); + + if (!res) { + cleanupGui(); + _input->_inputMode = Input::kInputModeGame; + } + +} + +void Parallaction::cleanupGui() { + delete _menuHelper; + _menuHelper = 0; +} + +void Parallaction::setInternLanguage(uint id) { + //TODO: assert id! + + _language = id; + _disk->setLanguage(id); +} + +uint Parallaction::getInternLanguage() { + return _language; +} + + +} // namespace Parallaction diff --git a/engines/parallaction/gui.h b/engines/parallaction/gui.h new file mode 100644 index 0000000000..dc6d1bc71b --- /dev/null +++ b/engines/parallaction/gui.h @@ -0,0 +1,93 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ + +#ifndef PARALLACTION_GUI_H +#define PARALLACTION_GUI_H + +#include "common/system.h" +#include "common/hashmap.h" + +#include "parallaction/input.h" +#include "parallaction/parallaction.h" +#include "parallaction/sound.h" + + +namespace Parallaction { + +class MenuInputState; + +class MenuInputHelper { + typedef Common::HashMap<Common::String, MenuInputState*> StateMap; + + StateMap _map; + MenuInputState *_state; + MenuInputState *_newState; + +public: + MenuInputHelper() : _state(0) { + } + + ~MenuInputHelper(); + + void setState(const Common::String &name) { + // bootstrap routine + _newState = getState(name); + assert(_newState); + } + + void addState(const Common::String &name, MenuInputState *state) { + _map.setVal(name, state); + } + + MenuInputState *getState(const Common::String &name) { + return _map[name]; + } + + bool run(); +}; + +class MenuInputState { + +protected: + MenuInputHelper *_helper; + +public: + MenuInputState(const Common::String &name, MenuInputHelper *helper) : _helper(helper), _name(name) { + debugC(3, kDebugExec, "MenuInputState(%s)", name.c_str()); + _helper->addState(name, this); + } + + Common::String _name; + + virtual ~MenuInputState() { } + + virtual MenuInputState* run() = 0; + virtual void enter() = 0; +}; + + +} // namespace Parallaction + +#endif diff --git a/engines/parallaction/gui_br.cpp b/engines/parallaction/gui_br.cpp index 0be070e345..b10237046e 100644 --- a/engines/parallaction/gui_br.cpp +++ b/engines/parallaction/gui_br.cpp @@ -25,184 +25,269 @@ #include "common/system.h" - +#include "parallaction/gui.h" #include "parallaction/input.h" #include "parallaction/parallaction.h" namespace Parallaction { -enum MenuOptions { - kMenuPart0 = 0, - kMenuPart1 = 1, - kMenuPart2 = 2, - kMenuPart3 = 3, - kMenuPart4 = 4, - kMenuLoadGame = 5, - kMenuQuit = 6 -}; - +class SplashInputState_BR : public MenuInputState { +protected: + Common::String _slideName; + uint32 _timeOut; + Common::String _nextState; + uint32 _startTime; + Palette blackPal; + Palette pal; -void Parallaction_br::guiStart() { + Parallaction_br *_vm; + int _fadeSteps; - // TODO: load progress value from special save game - _progress = 3; +public: + SplashInputState_BR(Parallaction_br *vm, const Common::String &name, MenuInputHelper *helper) : MenuInputState(name, helper), _vm(vm) { + } - int option = guiShowMenu(); - switch (option) { - case kMenuQuit: - _engineFlags |= kEngineQuit; - break; + virtual MenuInputState* run() { + if (_fadeSteps > 0) { + pal.fadeTo(blackPal, 1); + _vm->_gfx->setPalette(pal); + _fadeSteps--; + // TODO: properly implement timers to avoid delay calls + _vm->_system->delayMillis(20); + return this; + } - case kMenuLoadGame: - warning("loadgame not yet implemented"); - break; + if (_fadeSteps == 0) { + _vm->freeBackground(); + return _helper->getState(_nextState); + } - default: - _part = option; - _disk->selectArchive(_partNames[_part]); - startPart(); + uint32 curTime = _vm->_system->getMillis(); + if (curTime - _startTime > _timeOut) { + _fadeSteps = 64; + pal.clone(_vm->_gfx->_backgroundInfo.palette); + } + return this; } -} -void Parallaction_br::guiSplash(const char *name) { + virtual void enter() { + _vm->_gfx->clearScreen(); + _vm->_gfx->setBackground(kBackgroundSlide, _slideName.c_str(), 0, 0); + _vm->_gfx->_backgroundInfo.x = (_vm->_screenWidth - _vm->_gfx->_backgroundInfo.width) >> 1; + _vm->_gfx->_backgroundInfo.y = (_vm->_screenHeight - _vm->_gfx->_backgroundInfo.height) >> 1; + _vm->_input->setMouseState(MOUSE_DISABLED); - _gfx->clearScreen(); - _gfx->setBackground(kBackgroundSlide, name, 0, 0); - _gfx->_backgroundInfo.x = (_screenWidth - _gfx->_backgroundInfo.width) >> 1; - _gfx->_backgroundInfo.y = (_screenHeight - _gfx->_backgroundInfo.height) >> 1; - _gfx->updateScreen(); - _system->delayMillis(600); + _startTime = g_system->getMillis(); + _fadeSteps = -1; + } +}; - Palette blackPal; - Palette pal(_gfx->_backgroundInfo.palette); - for (uint i = 0; i < 64; i++) { - pal.fadeTo(blackPal, 1); - _gfx->setPalette(pal); - _gfx->updateScreen(); - _system->delayMillis(20); +class SplashInputState0_BR : public SplashInputState_BR { + +public: + SplashInputState0_BR(Parallaction_br *vm, MenuInputHelper *helper) : SplashInputState_BR(vm, "intro0", helper) { + _slideName = "dyna"; + _timeOut = 600; + _nextState = "intro1"; } +}; -} +class SplashInputState1_BR : public SplashInputState_BR { -#define MENUITEMS_X 250 -#define MENUITEMS_Y 200 +public: + SplashInputState1_BR(Parallaction_br *vm, MenuInputHelper *helper) : SplashInputState_BR(vm, "intro1", helper) { + _slideName = "core"; + _timeOut = 600; + _nextState = "mainmenu"; + } +}; -#define MENUITEM_WIDTH 190 -#define MENUITEM_HEIGHT 18 +class MainMenuInputState_BR : public MenuInputState { + Parallaction_br *_vm; -Frames* Parallaction_br::guiRenderMenuItem(const char *text) { - // this builds a surface containing two copies of the text. - // one is in normal color, the other is inverted. - // the two 'frames' are used to display selected/unselected menu items + #define MENUITEMS_X 250 + #define MENUITEMS_Y 200 - Graphics::Surface *surf = new Graphics::Surface; - surf->create(MENUITEM_WIDTH, MENUITEM_HEIGHT*2, 1); + #define MENUITEM_WIDTH 190 + #define MENUITEM_HEIGHT 18 - // build first frame to be displayed when item is not selected - if (getPlatform() == Common::kPlatformPC) { - _menuFont->setColor(0); - } else { - _menuFont->setColor(7); - } - _menuFont->drawString((byte*)surf->getBasePtr(5, 2), MENUITEM_WIDTH, text); + Frames* renderMenuItem(const char *text) { + // this builds a surface containing two copies of the text. + // one is in normal color, the other is inverted. + // the two 'frames' are used to display selected/unselected menu items - // build second frame to be displayed when item is selected - _menuFont->drawString((byte*)surf->getBasePtr(5, 2 + MENUITEM_HEIGHT), MENUITEM_WIDTH, text); - byte *s = (byte*)surf->getBasePtr(0, MENUITEM_HEIGHT); - for (int i = 0; i < surf->w * MENUITEM_HEIGHT; i++) { - *s++ ^= 0xD; - } + Graphics::Surface *surf = new Graphics::Surface; + surf->create(MENUITEM_WIDTH, MENUITEM_HEIGHT*2, 1); - // wrap the surface into the suitable Frames adapter - return new SurfaceToMultiFrames(2, MENUITEM_WIDTH, MENUITEM_HEIGHT, surf); -} + // build first frame to be displayed when item is not selected + if (_vm->getPlatform() == Common::kPlatformPC) { + _vm->_menuFont->setColor(0); + } else { + _vm->_menuFont->setColor(7); + } + _vm->_menuFont->drawString((byte*)surf->getBasePtr(5, 2), MENUITEM_WIDTH, text); + + // build second frame to be displayed when item is selected + _vm->_menuFont->drawString((byte*)surf->getBasePtr(5, 2 + MENUITEM_HEIGHT), MENUITEM_WIDTH, text); + byte *s = (byte*)surf->getBasePtr(0, MENUITEM_HEIGHT); + for (int i = 0; i < surf->w * MENUITEM_HEIGHT; i++) { + *s++ ^= 0xD; + } + // wrap the surface into the suitable Frames adapter + return new SurfaceToMultiFrames(2, MENUITEM_WIDTH, MENUITEM_HEIGHT, surf); + } -int Parallaction_br::guiShowMenu() { - // TODO: filter menu entries according to progress in game + enum MenuOptions { + kMenuPart0 = 0, + kMenuPart1 = 1, + kMenuPart2 = 2, + kMenuPart3 = 3, + kMenuPart4 = 4, + kMenuLoadGame = 5, + kMenuQuit = 6 + }; #define NUM_MENULINES 7 GfxObj *_lines[NUM_MENULINES]; - const char *menuStrings[NUM_MENULINES] = { - "SEE INTRO", - "NEW GAME", - "SAVED GAME", - "EXIT TO DOS", - "PART 2", - "PART 3", - "PART 4" - }; + static const char *_menuStrings[NUM_MENULINES]; + static const MenuOptions _options[NUM_MENULINES]; - MenuOptions options[NUM_MENULINES] = { - kMenuPart0, - kMenuPart1, - kMenuLoadGame, - kMenuQuit, - kMenuPart2, - kMenuPart3, - kMenuPart4 - }; + int _availItems; + int _selection; - _gfx->clearScreen(); - _gfx->setBackground(kBackgroundSlide, "tbra", 0, 0); - if (getPlatform() == Common::kPlatformPC) { - _gfx->_backgroundInfo.x = 20; - _gfx->_backgroundInfo.y = 50; + void cleanup() { + _vm->_system->showMouse(false); + _vm->hideDialogueStuff(); + + for (int i = 0; i < _availItems; i++) { + delete _lines[i]; + } } - int availItems = 4 + _progress; + void performChoice(int selectedItem) { + switch (selectedItem) { + case kMenuQuit: + _engineFlags |= kEngineQuit; + break; - // TODO: keep track of and destroy menu item frames/surfaces + case kMenuLoadGame: + warning("loadgame not yet implemented"); + break; - int i; - for (i = 0; i < availItems; i++) { - _lines[i] = new GfxObj(0, guiRenderMenuItem(menuStrings[i]), "MenuItem"); - uint id = _gfx->setItem(_lines[i], MENUITEMS_X, MENUITEMS_Y + MENUITEM_HEIGHT * i, 0xFF); - _gfx->setItemFrame(id, 0); + default: + _vm->startPart(selectedItem); + } } - int selectedItem = -1; - - setMousePointer(0); - - uint32 event; - Common::Point p; - while (true) { +public: + MainMenuInputState_BR(Parallaction_br *vm, MenuInputHelper *helper) : MenuInputState("mainmenu", helper), _vm(vm) { + } - _input->readInput(); + virtual MenuInputState* run() { - event = _input->getLastButtonEvent(); - if ((event == kMouseLeftUp) && selectedItem >= 0) - break; + int event = _vm->_input->getLastButtonEvent(); + if ((event == kMouseLeftUp) && _selection >= 0) { + cleanup(); + performChoice(_options[_selection]); + return 0; + } - _input->getCursorPos(p); + Common::Point p; + _vm->_input->getCursorPos(p); if ((p.x > MENUITEMS_X) && (p.x < (MENUITEMS_X+MENUITEM_WIDTH)) && (p.y > MENUITEMS_Y)) { - selectedItem = (p.y - MENUITEMS_Y) / MENUITEM_HEIGHT; + _selection = (p.y - MENUITEMS_Y) / MENUITEM_HEIGHT; - if (!(selectedItem < availItems)) - selectedItem = -1; + if (!(_selection < _availItems)) + _selection = -1; } else - selectedItem = -1; + _selection = -1; - for (i = 0; i < availItems; i++) { - _gfx->setItemFrame(i, selectedItem == i ? 1 : 0); + for (int i = 0; i < _availItems; i++) { + _vm->_gfx->setItemFrame(i, _selection == i ? 1 : 0); } - _gfx->updateScreen(); - _system->delayMillis(20); + + return this; } - _system->showMouse(false); - hideDialogueStuff(); + virtual void enter() { + _vm->_gfx->clearScreen(); + _vm->_gfx->setBackground(kBackgroundSlide, "tbra", 0, 0); + if (_vm->getPlatform() == Common::kPlatformPC) { + _vm->_gfx->_backgroundInfo.x = 20; + _vm->_gfx->_backgroundInfo.y = 50; + } + + // TODO: load progress from savefile + int progress = 3; + _availItems = 4 + progress; - for (i = 0; i < availItems; i++) { - delete _lines[i]; + // TODO: keep track of and destroy menu item frames/surfaces + int i; + for (i = 0; i < _availItems; i++) { + _lines[i] = new GfxObj(0, renderMenuItem(_menuStrings[i]), "MenuItem"); + uint id = _vm->_gfx->setItem(_lines[i], MENUITEMS_X, MENUITEMS_Y + MENUITEM_HEIGHT * i, 0xFF); + _vm->_gfx->setItemFrame(id, 0); + } + _selection = -1; + _vm->setArrowCursor(); + _vm->_input->setMouseState(MOUSE_ENABLED_SHOW); } - return options[selectedItem]; +}; + +const char *MainMenuInputState_BR::_menuStrings[NUM_MENULINES] = { + "SEE INTRO", + "NEW GAME", + "SAVED GAME", + "EXIT TO DOS", + "PART 2", + "PART 3", + "PART 4" +}; + +const MainMenuInputState_BR::MenuOptions MainMenuInputState_BR::_options[NUM_MENULINES] = { + kMenuPart0, + kMenuPart1, + kMenuLoadGame, + kMenuQuit, + kMenuPart2, + kMenuPart3, + kMenuPart4 +}; + + + + + + + +void Parallaction_br::startGui() { + _menuHelper = new MenuInputHelper; + new SplashInputState0_BR(this, _menuHelper); + new SplashInputState1_BR(this, _menuHelper); + new MainMenuInputState_BR(this, _menuHelper); + + _menuHelper->setState("intro0"); + _input->_inputMode = Input::kInputModeMenu; + + do { + _input->readInput(); + if (!_menuHelper->run()) break; + _gfx->beginFrame(); + _gfx->updateScreen(); + } while (true); + + delete _menuHelper; + _menuHelper = 0; + + _input->_inputMode = Input::kInputModeGame; } + + } // namespace Parallaction diff --git a/engines/parallaction/gui_ns.cpp b/engines/parallaction/gui_ns.cpp index 9c48586dbc..815c27bd1c 100644 --- a/engines/parallaction/gui_ns.cpp +++ b/engines/parallaction/gui_ns.cpp @@ -24,7 +24,9 @@ */ #include "common/system.h" +#include "common/hashmap.h" +#include "parallaction/gui.h" #include "parallaction/input.h" #include "parallaction/parallaction.h" #include "parallaction/sound.h" @@ -32,313 +34,567 @@ namespace Parallaction { -const char *introMsg1[] = { - "INSERISCI IL CODICE", - "ENTREZ CODE", - "ENTER CODE", - "GIB DEN KODE EIN" -}; +class SplashInputState_NS : public MenuInputState { +protected: + Common::String _slideName; + uint32 _timeOut; + Common::String _nextState; + uint32 _startTime; -const char *introMsg2[] = { - "CODICE ERRATO", - "CODE ERRONE", - "WRONG CODE", - "GIB DEN KODE EIN" -}; + Parallaction_ns *_vm; -const char *introMsg3[] = { - "PRESS LEFT MOUSE BUTTON", - "TO SEE INTRO", - "PRESS RIGHT MOUSE BUTTON", - "TO START" +public: + SplashInputState_NS(Parallaction_ns *vm, const Common::String &name, MenuInputHelper *helper) : MenuInputState(name, helper), _vm(vm) { + } + + virtual MenuInputState* run() { + uint32 curTime = g_system->getMillis(); + if (curTime - _startTime > _timeOut) { + _vm->freeBackground(); + return _helper->getState(_nextState); + } + return this; + } + + virtual void enter() { + _vm->_input->setMouseState(MOUSE_DISABLED); + _vm->showSlide(_slideName.c_str()); + _startTime = g_system->getMillis(); + } }; -const char *newGameMsg[] = { - "NUOVO GIOCO", - "NEUF JEU", - "NEW GAME", - "NEUES SPIEL" +class SplashInputState0_NS : public SplashInputState_NS { + +public: + SplashInputState0_NS(Parallaction_ns *vm, MenuInputHelper *helper) : SplashInputState_NS(vm, "intro0", helper) { + _slideName = "intro"; + _timeOut = 2000; + _nextState = "intro1"; + } }; -const char *loadGameMsg[] = { - "GIOCO SALVATO", - "JEU SAUVE'", - "SAVED GAME", - "SPIEL GESPEICHERT" +class SplashInputState1_NS : public SplashInputState_NS { + +public: + SplashInputState1_NS(Parallaction_ns *vm, MenuInputHelper *helper) : SplashInputState_NS(vm, "intro1", helper) { + _slideName = "minintro"; + _timeOut = 2000; + _nextState = "chooselanguage"; + } }; -#define BLOCK_WIDTH 16 -#define BLOCK_HEIGHT 24 +class ChooseLanguageInputState_NS : public MenuInputState { + #define BLOCK_WIDTH 16 + #define BLOCK_HEIGHT 24 -#define BLOCK_X 112 -#define BLOCK_Y 130 + #define BLOCK_X 112 + #define BLOCK_Y 130 -#define BLOCK_SELECTION_X (BLOCK_X-1) -#define BLOCK_SELECTION_Y (BLOCK_Y-1) + #define BLOCK_SELECTION_X (BLOCK_X-1) + #define BLOCK_SELECTION_Y (BLOCK_Y-1) -#define BLOCK_X_OFFSET (BLOCK_WIDTH+1) -#define BLOCK_Y_OFFSET 9 + #define BLOCK_X_OFFSET (BLOCK_WIDTH+1) + #define BLOCK_Y_OFFSET 9 -// destination slots for code blocks -// -#define SLOT_X 61 -#define SLOT_Y 64 -#define SLOT_WIDTH (BLOCK_WIDTH+2) + // destination slots for code blocks + // + #define SLOT_X 61 + #define SLOT_Y 64 + #define SLOT_WIDTH (BLOCK_WIDTH+2) -#define PASSWORD_LEN 6 + int _language; + bool _allowChoice; + Common::String _nextState; -#define CHAR_DINO 0 -#define CHAR_DONNA 1 -#define CHAR_DOUGH 2 + static const Common::Rect _dosLanguageSelectBlocks[4]; + static const Common::Rect _amigaLanguageSelectBlocks[4]; + const Common::Rect *_blocks; -static const uint16 _amigaKeys[][PASSWORD_LEN] = { - { 5, 3, 6, 2, 2, 7 }, // dino - { 0, 3, 6, 2, 2, 6 }, // donna - { 1, 3 ,7, 2, 4, 6 } // dough -}; + Parallaction_ns *_vm; -static const uint16 _pcKeys[][PASSWORD_LEN] = { - { 5, 3, 6, 1, 4, 7 }, // dino - { 0, 2, 8, 5, 5, 1 }, // donna - { 1, 7 ,7, 2, 2, 6 } // dough -}; +public: + ChooseLanguageInputState_NS(Parallaction_ns *vm, MenuInputHelper *helper) : MenuInputState("chooselanguage", helper), _vm(vm) { + _allowChoice = false; + _nextState = "selectgame"; -static const char *_charStartLocation[] = { - "test.dino", - "test.donna", - "test.dough" + if (_vm->getPlatform() == Common::kPlatformAmiga) { + if (!(_vm->getFeatures() & GF_LANG_MULT)) { + if (_vm->getFeatures() & GF_DEMO) { + _language = 1; // Amiga Demo supports English + _nextState = "startdemo"; + return; + } else { + _language = 0; // The only other non multi-lingual version just supports Italian + return; + } + } + + _blocks = _amigaLanguageSelectBlocks; + } else { + _blocks = _dosLanguageSelectBlocks; + } + + _language = -1; + _allowChoice = true; + } + + virtual MenuInputState* run() { + if (!_allowChoice) { + _vm->setInternLanguage(_language); + return _helper->getState(_nextState); + } + + int event = _vm->_input->getLastButtonEvent(); + if (event != kMouseLeftUp) { + return this; + } + + Common::Point p; + _vm->_input->getCursorPos(p); + + for (uint16 i = 0; i < 4; i++) { + if (_blocks[i].contains(p)) { + _vm->setInternLanguage(i); + _vm->beep(); + _vm->_gfx->freeLabels(); + return _helper->getState(_nextState); + } + } + + return this; + } + + virtual void enter() { + if (!_allowChoice) { + return; + } + + _vm->_input->setMouseState(MOUSE_ENABLED_SHOW); + + // user can choose language in this version + _vm->showSlide("lingua"); + + uint id = _vm->_gfx->createLabel(_vm->_introFont, "SELECT LANGUAGE", 1); + _vm->_gfx->showLabel(id, 60, 30); + + _vm->setArrowCursor(); + } }; -enum { - NEW_GAME, - LOAD_GAME +const Common::Rect ChooseLanguageInputState_NS::_dosLanguageSelectBlocks[4] = { + Common::Rect( 80, 110, 128, 180 ), // Italian + Common::Rect( 129, 85, 177, 155 ), // French + Common::Rect( 178, 60, 226, 130 ), // English + Common::Rect( 227, 35, 275, 105 ) // German }; -enum { - START_DEMO, - START_INTRO, - GAME_LOADED, - SELECT_CHARACTER +const Common::Rect ChooseLanguageInputState_NS::_amigaLanguageSelectBlocks[4] = { + Common::Rect( -1, -1, -1, -1 ), // Italian: not supported by Amiga multi-lingual version + Common::Rect( 129, 85, 177, 155 ), // French + Common::Rect( 178, 60, 226, 130 ), // English + Common::Rect( 227, 35, 275, 105 ) // German }; -void Parallaction_ns::guiStart() { +class SelectGameInputState_NS : public MenuInputState { - _disk->selectArchive((getFeatures() & GF_DEMO) ? "disk0" : "disk1"); + int _choice, _oldChoice; + Common::String _nextState[2]; - guiSplash(); + uint _labels[2]; - _language = guiChooseLanguage(); - _disk->setLanguage(_language); + Parallaction_ns *_vm; - int event; + static const char *newGameMsg[4]; + static const char *loadGameMsg[4]; - if (getFeatures() & GF_DEMO) { - event = START_DEMO; - } else { - if (guiSelectGame() == NEW_GAME) { - event = guiNewGame(); - } else { - event = loadGame() ? GAME_LOADED : START_INTRO; - } +public: + SelectGameInputState_NS(Parallaction_ns *vm, MenuInputHelper *helper) : MenuInputState("selectgame", helper), _vm(vm) { + _choice = 0; + _oldChoice = -1; + + _nextState[0] = "newgame"; + _nextState[1] = "loadgame"; } - switch (event) { - case START_DEMO: - strcpy(_location._name, "fognedemo.dough"); - break; - case START_INTRO: - strcpy(_location._name, "fogne.dough"); - break; + virtual MenuInputState *run() { + int event = _vm->_input->getLastButtonEvent(); + + if (event == kMouseLeftUp) { + _vm->_gfx->freeLabels(); + return _helper->getState(_nextState[_choice]); + } - case GAME_LOADED: - // nothing to do here - return; + Common::Point p; + _vm->_input->getCursorPos(p); + _choice = (p.x > 160) ? 1 : 0; - case SELECT_CHARACTER: - selectStartLocation(); - break; + if (_choice != _oldChoice) { + if (_oldChoice != -1) + _vm->_gfx->hideLabel(_labels[_oldChoice]); + if (_choice != -1) + _vm->_gfx->showLabel(_labels[_choice], 60, 30); + + _oldChoice = _choice; + } + + return this; } - return; -} + virtual void enter() { + _vm->showSlide("restore"); + _vm->_input->setMouseState(MOUSE_ENABLED_SHOW); -void Parallaction_ns::selectStartLocation() { - _inTestResult = false; + _labels[0] = _vm->_gfx->createLabel(_vm->_introFont, newGameMsg[_vm->getInternLanguage()], 1); + _labels[1] = _vm->_gfx->createLabel(_vm->_introFont, loadGameMsg[_vm->getInternLanguage()], 1); + } - int character = guiSelectCharacter(); - if (character == -1) - error("invalid character selected from menu screen"); +}; - scheduleLocationSwitch(_charStartLocation[character]); -} +const char *SelectGameInputState_NS::newGameMsg[4] = { + "NUOVO GIOCO", + "NEUF JEU", + "NEW GAME", + "NEUES SPIEL" +}; +const char *SelectGameInputState_NS::loadGameMsg[4] = { + "GIOCO SALVATO", + "JEU SAUVE'", + "SAVED GAME", + "SPIEL GESPEICHERT" +}; -void Parallaction_ns::guiSplash() { - showSlide("intro"); - _gfx->updateScreen(); - g_system->delayMillis(2000); - freeBackground(); - showSlide("minintro"); - _gfx->updateScreen(); - g_system->delayMillis(2000); - freeBackground(); -} +class LoadGameInputState_NS : public MenuInputState { + bool _result; + Parallaction_ns *_vm; -int Parallaction_ns::guiNewGame() { +public: + LoadGameInputState_NS(Parallaction_ns *vm, MenuInputHelper *helper) : MenuInputState("loadgame", helper), _vm(vm) { } - const char **v14 = introMsg3; + virtual MenuInputState* run() { + if (!_result) { + _vm->scheduleLocationSwitch("fogne.dough"); + } + return 0; + } - _disk->selectArchive("disk1"); + virtual void enter() { + _result = _vm->loadGame(); + } +}; - setBackground("test", NULL, NULL); - _gfx->updateScreen(); - uint id[4]; - id[0] = _gfx->createLabel(_menuFont, v14[0], 1); - id[1] = _gfx->createLabel(_menuFont, v14[1], 1); - id[2] = _gfx->createLabel(_menuFont, v14[2], 1); - id[3] = _gfx->createLabel(_menuFont, v14[3], 1); - _gfx->showLabel(id[0], CENTER_LABEL_HORIZONTAL, 50); - _gfx->showLabel(id[1], CENTER_LABEL_HORIZONTAL, 70); - _gfx->showLabel(id[2], CENTER_LABEL_HORIZONTAL, 100); - _gfx->showLabel(id[3], CENTER_LABEL_HORIZONTAL, 120); +class NewGameInputState_NS : public MenuInputState { + Parallaction_ns *_vm; - _input->showCursor(false); + static const char *introMsg3[4]; - _gfx->updateScreen(); +public: + NewGameInputState_NS(Parallaction_ns *vm, MenuInputHelper *helper) : MenuInputState("newgame", helper), _vm(vm) { + } - _input->waitForButtonEvent(kMouseLeftUp | kMouseRightUp); - uint32 event = _input->getLastButtonEvent(); + virtual MenuInputState* run() { + int event = _vm->_input->getLastButtonEvent(); - _input->showCursor(true); + if (event == kMouseLeftUp || event == kMouseRightUp) { + _vm->_input->setMouseState(MOUSE_ENABLED_SHOW); + _vm->_gfx->freeLabels(); - _gfx->freeLabels(); + if (event == kMouseLeftUp) { + _vm->scheduleLocationSwitch("fogne.dough"); + return 0; + } - if (event != kMouseRightUp) { - return START_INTRO; - } + return _helper->getState("selectcharacter"); + } - return SELECT_CHARACTER; -} + return this; + } -static const Common::Rect _dosLanguageSelectBlocks[4] = { - Common::Rect( 80, 110, 128, 180 ), // Italian - Common::Rect( 129, 85, 177, 155 ), // French - Common::Rect( 178, 60, 226, 130 ), // English - Common::Rect( 227, 35, 275, 105 ) // German + virtual void enter() { + _vm->_disk->selectArchive("disk1"); + _vm->setBackground("test", NULL, NULL); + _vm->_input->setMouseState(MOUSE_ENABLED_HIDE); + + uint id[4]; + id[0] = _vm->_gfx->createLabel(_vm->_menuFont, introMsg3[0], 1); + id[1] = _vm->_gfx->createLabel(_vm->_menuFont, introMsg3[1], 1); + id[2] = _vm->_gfx->createLabel(_vm->_menuFont, introMsg3[2], 1); + id[3] = _vm->_gfx->createLabel(_vm->_menuFont, introMsg3[3], 1); + _vm->_gfx->showLabel(id[0], CENTER_LABEL_HORIZONTAL, 50); + _vm->_gfx->showLabel(id[1], CENTER_LABEL_HORIZONTAL, 70); + _vm->_gfx->showLabel(id[2], CENTER_LABEL_HORIZONTAL, 100); + _vm->_gfx->showLabel(id[3], CENTER_LABEL_HORIZONTAL, 120); + } }; -static const Common::Rect _amigaLanguageSelectBlocks[4] = { - Common::Rect( -1, -1, -1, -1 ), // Italian: not supported by Amiga multi-lingual version - Common::Rect( 129, 85, 177, 155 ), // French - Common::Rect( 178, 60, 226, 130 ), // English - Common::Rect( 227, 35, 275, 105 ) // German +const char *NewGameInputState_NS::introMsg3[4] = { + "PRESS LEFT MOUSE BUTTON", + "TO SEE INTRO", + "PRESS RIGHT MOUSE BUTTON", + "TO START" }; -uint16 Parallaction_ns::guiChooseLanguage() { - const Common::Rect *blocks; +class StartDemoInputState_NS : public MenuInputState { + Parallaction_ns *_vm; - if (getPlatform() == Common::kPlatformAmiga) { - if (!(getFeatures() & GF_LANG_MULT)) { - if (getFeatures() & GF_DEMO) { - return 1; // Amiga Demo supports English - } else { - return 0; // The only other non multi-lingual version just supports Italian - } - } +public: + StartDemoInputState_NS(Parallaction_ns *vm, MenuInputHelper *helper) : MenuInputState("startdemo", helper), _vm(vm) { + } - blocks = _amigaLanguageSelectBlocks; - } else { - blocks = _dosLanguageSelectBlocks; + virtual MenuInputState* run() { + _vm->scheduleLocationSwitch("fognedemo.dough"); + return 0; } - // user can choose language in dos version - showSlide("lingua"); + virtual void enter() { + _vm->_input->setMouseState(MOUSE_DISABLED); + } +}; - uint id = _gfx->createLabel(_introFont, "SELECT LANGUAGE", 1); - _gfx->showLabel(id, 60, 30); +class SelectCharacterInputState_NS : public MenuInputState { - setArrowCursor(); + #define PASSWORD_LEN 6 - Common::Point p; + #define CHAR_DINO 0 + #define CHAR_DONNA 1 + #define CHAR_DOUGH 2 - int selection = -1; - while (selection == -1) { - _input->waitUntilLeftClick(); - _input->getCursorPos(p); - for (uint16 i = 0; i < 4; i++) { - if (blocks[i].contains(p)) { + static const Common::Rect codeSelectBlocks[9]; + static const Common::Rect codeTrueBlocks[9]; + + Parallaction_ns *_vm; + + int guiGetSelectedBlock(const Common::Point &p) { + + int selection = -1; + + for (uint16 i = 0; i < 9; i++) { + if (codeSelectBlocks[i].contains(p)) { selection = i; break; } } + + if ((selection != -1) && (_vm->getPlatform() == Common::kPlatformAmiga)) { + _vm->_gfx->invertBackground(codeTrueBlocks[selection]); + _vm->_gfx->updateScreen(); + _vm->beep(); + g_system->delayMillis(100); + _vm->_gfx->invertBackground(codeTrueBlocks[selection]); + _vm->_gfx->updateScreen(); + } + + return selection; } - beep(); + byte _points[3]; + bool _fail; + const uint16 (*_keys)[PASSWORD_LEN]; + Graphics::Surface _block; + Graphics::Surface _emptySlots; - _gfx->freeLabels(); + uint _labels[2]; + uint _len; + uint32 _startTime; - return selection; -} + enum { + CHOICE, + FAIL, + SUCCESS, + DELAY + }; + uint _state; + static const char *introMsg1[4]; + static const char *introMsg2[4]; -uint16 Parallaction_ns::guiSelectGame() { -// printf("selectGame()\n"); + static const uint16 _amigaKeys[3][PASSWORD_LEN]; + static const uint16 _pcKeys[3][PASSWORD_LEN]; + static const char *_charStartLocation[3]; - showSlide("restore"); - uint16 _si = 0; - uint16 _di = 3; +public: + SelectCharacterInputState_NS(Parallaction_ns *vm, MenuInputHelper *helper) : MenuInputState("selectcharacter", helper), _vm(vm) { + _keys = (_vm->getPlatform() == Common::kPlatformAmiga && (_vm->getFeatures() & GF_LANG_MULT)) ? _amigaKeys : _pcKeys; + _block.create(BLOCK_WIDTH, BLOCK_HEIGHT, 1); + } - uint id0, id1; - id0 = _gfx->createLabel(_introFont, loadGameMsg[_language], 1); - id1 = _gfx->createLabel(_introFont, newGameMsg[_language], 1); + ~SelectCharacterInputState_NS() { + _block.free(); + _emptySlots.free(); + } - Common::Point p; + void cleanup() { + _points[0] = _points[1] = _points[2] = 0; + _vm->_gfx->hideLabel(_labels[1]); + _vm->_gfx->showLabel(_labels[0], 60, 30); + _fail = false; + _len = 0; + } - _input->readInput(); - uint32 event = _input->getLastButtonEvent(); + void delay() { + if (g_system->getMillis() - _startTime < 2000) { + return; + } + cleanup(); + _state = CHOICE; + } - while (event != kMouseLeftUp) { + void choice() { + int event = _vm->_input->getLastButtonEvent(); + if (event != kMouseLeftUp) { + return; + } - _input->readInput(); - _input->getCursorPos(p); - event = _input->getLastButtonEvent(); + Common::Point p; + _vm->_input->getCursorPos(p); + int _si = guiGetSelectedBlock(p); - _si = (p.x > 160) ? 1 : 0; + if (_si != -1) { + _vm->_gfx->grabBackground(codeTrueBlocks[_si], _block); + _vm->_gfx->patchBackground(_block, _len * SLOT_WIDTH + SLOT_X, SLOT_Y, false); - if (_si != _di) { - if (_si != 0) { - // load a game - _gfx->hideLabel(id1); - _gfx->showLabel(id0, 60, 30); - } else { - // new game - _gfx->hideLabel(id0); - _gfx->showLabel(id1, 60, 30); + if (_keys[0][_len] != _si && _keys[1][_len] != _si && _keys[2][_len] != _si) { + _fail = true; } - _di = _si; + + // build user preference + _points[0] += (_keys[0][_len] == _si); + _points[1] += (_keys[1][_len] == _si); + _points[2] += (_keys[2][_len] == _si); + + _len++; } - _gfx->updateScreen(); - g_system->delayMillis(30); + if (_len == PASSWORD_LEN) { + _state = _fail ? FAIL : SUCCESS; + } } - _gfx->freeLabels(); + void fail() { + _vm->_gfx->patchBackground(_emptySlots, SLOT_X, SLOT_Y, false); + _vm->_gfx->hideLabel(_labels[0]); + _vm->_gfx->showLabel(_labels[1], 60, 30); + _startTime = g_system->getMillis(); + _state = DELAY; + } + + void success() { + _vm->_gfx->freeLabels(); + _vm->_gfx->setBlackPalette(); + _emptySlots.free(); + + // actually select character + int character = -1; + if (_points[0] >= _points[1] && _points[0] >= _points[2]) { + character = CHAR_DINO; + } else + if (_points[1] >= _points[0] && _points[1] >= _points[2]) { + character = CHAR_DONNA; + } else + if (_points[2] >= _points[0] && _points[2] >= _points[1]) { + character = CHAR_DOUGH; + } else { + error("If you read this, either your CPU or transivity is broken (we believe the former)."); + } + + _vm->_inTestResult = false; + _vm->cleanupGame(); + _vm->scheduleLocationSwitch(_charStartLocation[character]); + } + + virtual MenuInputState* run() { + MenuInputState* nextState = this; + + switch (_state) { + case DELAY: + delay(); + break; + + case CHOICE: + choice(); + break; + + case FAIL: + fail(); + break; + + case SUCCESS: + success(); + nextState = 0; + break; + + default: + error("unknown state in SelectCharacterInputState"); + } + + return nextState; + } + + virtual void enter() { + _vm->_soundMan->stopMusic(); + _vm->_disk->selectArchive((_vm->getFeatures() & GF_DEMO) ? "disk0" : "disk1"); + _vm->showSlide("password"); + + _emptySlots.create(BLOCK_WIDTH * 8, BLOCK_HEIGHT, 1); + Common::Rect rect(SLOT_X, SLOT_Y, SLOT_X + BLOCK_WIDTH * 8, SLOT_Y + BLOCK_HEIGHT); + _vm->_gfx->grabBackground(rect, _emptySlots); + + _labels[0] = _vm->_gfx->createLabel(_vm->_introFont, introMsg1[_vm->getInternLanguage()], 1); + _labels[1] = _vm->_gfx->createLabel(_vm->_introFont, introMsg2[_vm->getInternLanguage()], 1); + + cleanup(); + + _vm->setArrowCursor(); + _vm->_input->setMouseState(MOUSE_ENABLED_SHOW); + _state = CHOICE; + } +}; + +const char *SelectCharacterInputState_NS::introMsg1[4] = { + "INSERISCI IL CODICE", + "ENTREZ CODE", + "ENTER CODE", + "GIB DEN KODE EIN" +}; + +const char *SelectCharacterInputState_NS::introMsg2[4] = { + "CODICE ERRATO", + "CODE ERRONE", + "WRONG CODE", + "GIB DEN KODE EIN" +}; + +const uint16 SelectCharacterInputState_NS::_amigaKeys[][PASSWORD_LEN] = { + { 5, 3, 6, 2, 2, 7 }, // dino + { 0, 3, 6, 2, 2, 6 }, // donna + { 1, 3 ,7, 2, 4, 6 } // dough +}; + +const uint16 SelectCharacterInputState_NS::_pcKeys[][PASSWORD_LEN] = { + { 5, 3, 6, 1, 4, 7 }, // dino + { 0, 2, 8, 5, 5, 1 }, // donna + { 1, 7 ,7, 2, 2, 6 } // dough +}; + +const char *SelectCharacterInputState_NS::_charStartLocation[] = { + "test.dino", + "test.donna", + "test.dough" +}; - return _si ? LOAD_GAME : NEW_GAME; -} -static const Common::Rect codeSelectBlocks[9] = { +const Common::Rect SelectCharacterInputState_NS::codeSelectBlocks[9] = { Common::Rect( 111, 129, 127, 153 ), // na Common::Rect( 128, 120, 144, 144 ), // wa Common::Rect( 145, 111, 161, 135 ), // ra @@ -350,7 +606,7 @@ static const Common::Rect codeSelectBlocks[9] = { Common::Rect( 247, 57, 263, 81 ) // ka }; -static const Common::Rect codeTrueBlocks[9] = { +const Common::Rect SelectCharacterInputState_NS::codeTrueBlocks[9] = { Common::Rect( 112, 130, 128, 154 ), Common::Rect( 129, 121, 145, 145 ), Common::Rect( 146, 112, 162, 136 ), @@ -363,146 +619,219 @@ static const Common::Rect codeTrueBlocks[9] = { }; -int Parallaction_ns::guiGetSelectedBlock(const Common::Point &p) { +class ShowCreditsInputState_NS : public MenuInputState { + Parallaction_ns *_vm; + int _current; + uint32 _startTime; - int selection = -1; + struct Credit { + const char *_role; + const char *_name; + }; - for (uint16 i = 0; i < 9; i++) { - if (codeSelectBlocks[i].contains(p)) { - selection = i; - break; - } + static const Credit _credits[6]; + +public: + ShowCreditsInputState_NS(Parallaction_ns *vm, MenuInputHelper *helper) : MenuInputState("showcredits", helper), _vm(vm) { } - if ((selection != -1) && (getPlatform() == Common::kPlatformAmiga)) { - _gfx->invertBackground(codeTrueBlocks[selection]); - _gfx->updateScreen(); - beep(); - g_system->delayMillis(100); - _gfx->invertBackground(codeTrueBlocks[selection]); - _gfx->updateScreen(); + void drawCurrentLabel() { + uint id[2]; + id[0] = _vm->_gfx->createLabel(_vm->_menuFont, _credits[_current]._role, 1); + id[1] = _vm->_gfx->createLabel(_vm->_menuFont, _credits[_current]._name, 1); + _vm->_gfx->showLabel(id[0], CENTER_LABEL_HORIZONTAL, 80); + _vm->_gfx->showLabel(id[1], CENTER_LABEL_HORIZONTAL, 100); } - return selection; -} + virtual MenuInputState* run() { + if (_current == -1) { + _startTime = g_system->getMillis(); + _current = 0; + drawCurrentLabel(); + return this; + } -// -// character selection and protection -// -int Parallaction_ns::guiSelectCharacter() { - debugC(1, kDebugMenu, "Parallaction_ns::guiselectCharacter()"); + int event = _vm->_input->getLastButtonEvent(); + uint32 curTime = g_system->getMillis(); + if ((event == kMouseLeftUp) || (curTime - _startTime > 5500)) { + _current++; + _startTime = curTime; + _vm->_gfx->freeLabels(); - setArrowCursor(); - _soundMan->stopMusic(); + if (_current == 6) { + return _helper->getState("endintro"); + } - _disk->selectArchive((getFeatures() & GF_DEMO) ? "disk0" : "disk1"); + drawCurrentLabel(); + } - showSlide("password"); + return this; + } + virtual void enter() { + _current = -1; + _vm->_input->setMouseState(MOUSE_DISABLED); + } +}; - const uint16 (*keys)[PASSWORD_LEN] = (getPlatform() == Common::kPlatformAmiga && (getFeatures() & GF_LANG_MULT)) ? _amigaKeys : _pcKeys; - uint16 _di = 0; - byte points[3] = { 0, 0, 0 }; +const ShowCreditsInputState_NS::Credit ShowCreditsInputState_NS::_credits[6] = { + {"Music and Sound Effects", "MARCO CAPRELLI"}, + {"PC Version", "RICCARDO BALLARINO"}, + {"Project Manager", "LOVRANO CANEPA"}, + {"Production", "BRUNO BOZ"}, + {"Special Thanks to", "LUIGI BENEDICENTI - GILDA and DANILO"}, + {"Copyright 1992 Euclidea s.r.l ITALY", "All rights reserved"} +}; - bool fail; +class EndIntroInputState_NS : public MenuInputState { + Parallaction_ns *_vm; + bool _isDemo; - uint id[2]; - id[0] = _gfx->createLabel(_introFont, introMsg1[_language], 1); - id[1] = _gfx->createLabel(_introFont, introMsg2[_language], 1); +public: + EndIntroInputState_NS(Parallaction_ns *vm, MenuInputHelper *helper) : MenuInputState("endintro", helper), _vm(vm) { + _isDemo = (_vm->getFeatures() & GF_DEMO) != 0; + } - Graphics::Surface v14; - v14.create(BLOCK_WIDTH * 8, BLOCK_HEIGHT, 1); - Common::Rect rect(SLOT_X, SLOT_Y, SLOT_X + BLOCK_WIDTH * 8, SLOT_Y + BLOCK_HEIGHT); - _gfx->grabBackground(rect, v14); + virtual MenuInputState* run() { - Graphics::Surface block; - block.create(BLOCK_WIDTH, BLOCK_HEIGHT, 1); + int event = _vm->_input->getLastButtonEvent(); + if (event != kMouseLeftUp) { + return this; + } - Common::Point p; + if (_isDemo) { + _engineFlags |= kEngineQuit; + return 0; + } - while (true) { + _vm->_gfx->freeLabels(); + return _helper->getState("selectcharacter"); + } - points[0] = 0; - points[1] = 0; - points[2] = 0; - fail = false; + virtual void enter() { + _vm->_soundMan->stopMusic(); + _vm->_input->setMouseState(MOUSE_DISABLED); - _gfx->hideLabel(id[1]); - _gfx->showLabel(id[0], 60, 30); + if (!_isDemo) { + int label = _vm->_gfx->createLabel(_vm->_menuFont, "CLICK MOUSE BUTTON TO START", 1); + _vm->_gfx->showLabel(label, CENTER_LABEL_HORIZONTAL, 80); + } + } +}; - _di = 0; - while (_di < PASSWORD_LEN) { - _input->waitUntilLeftClick(); - _input->getCursorPos(p); +class EndPartInputState_NS : public MenuInputState { + Parallaction_ns *_vm; + bool _allPartsComplete; - int _si = guiGetSelectedBlock(p); + // part completion messages + static const char *endMsg0[4]; + static const char *endMsg1[4]; + static const char *endMsg2[4]; + static const char *endMsg3[4]; + // game completion messages + static const char *endMsg4[4]; + static const char *endMsg5[4]; + static const char *endMsg6[4]; + static const char *endMsg7[4]; - if (_si != -1) { - _gfx->grabBackground(codeTrueBlocks[_si], block); - _gfx->patchBackground(block, _di * SLOT_WIDTH + SLOT_X, SLOT_Y, false); - if (keys[0][_di] == _si) { - points[0]++; - } else - if (keys[1][_di] == _si) { - points[1]++; - } else - if (keys[2][_di] == _si) { - points[2]++; - } else { - fail = true; - } +public: + EndPartInputState_NS(Parallaction_ns *vm, MenuInputHelper *helper) : MenuInputState("endpart", helper), _vm(vm) { + } - // build user preference - points[0] += (keys[0][_di] == _si); - points[1] += (keys[1][_di] == _si); - points[2] += (keys[2][_di] == _si); + virtual MenuInputState* run() { + int event = _vm->_input->getLastButtonEvent(); + if (event != kMouseLeftUp) { + return this; + } - _di++; - } + _vm->_gfx->freeLabels(); + if (_allPartsComplete) { + _vm->scheduleLocationSwitch("estgrotta.drki"); + return 0; } - if (!fail) { - break; + return _helper->getState("selectcharacter"); + } + + virtual void enter() { + _allPartsComplete = _vm->allPartsComplete(); + _vm->_input->setMouseState(MOUSE_DISABLED); + + uint id[4]; + if (_allPartsComplete) { + id[0] = _vm->_gfx->createLabel(_vm->_menuFont, endMsg4[_language], 1); + id[1] = _vm->_gfx->createLabel(_vm->_menuFont, endMsg5[_language], 1); + id[2] = _vm->_gfx->createLabel(_vm->_menuFont, endMsg6[_language], 1); + id[3] = _vm->_gfx->createLabel(_vm->_menuFont, endMsg7[_language], 1); + } else { + id[0] = _vm->_gfx->createLabel(_vm->_menuFont, endMsg0[_language], 1); + id[1] = _vm->_gfx->createLabel(_vm->_menuFont, endMsg1[_language], 1); + id[2] = _vm->_gfx->createLabel(_vm->_menuFont, endMsg2[_language], 1); + id[3] = _vm->_gfx->createLabel(_vm->_menuFont, endMsg3[_language], 1); } - _gfx->patchBackground(v14, SLOT_X, SLOT_Y, false); + _vm->_gfx->showLabel(id[0], CENTER_LABEL_HORIZONTAL, 70); + _vm->_gfx->showLabel(id[1], CENTER_LABEL_HORIZONTAL, 100); + _vm->_gfx->showLabel(id[2], CENTER_LABEL_HORIZONTAL, 130); + _vm->_gfx->showLabel(id[3], CENTER_LABEL_HORIZONTAL, 160); + } +}; - _gfx->hideLabel(id[0]); - _gfx->showLabel(id[1], 60, 30); +// part completion messages +const char *EndPartInputState_NS::endMsg0[] = {"COMPLIMENTI!", "BRAVO!", "CONGRATULATIONS!", "PRIMA!"}; +const char *EndPartInputState_NS::endMsg1[] = {"HAI FINITO QUESTA PARTE", "TU AS COMPLETE' CETTE AVENTURE", "YOU HAVE COMPLETED THIS PART", "DU HAST EIN ABENTEUER ERFOLGREICH"}; +const char *EndPartInputState_NS::endMsg2[] = {"ORA COMPLETA IL RESTO ", "AVEC SUCCES.", "NOW GO ON WITH THE REST OF", "ZU ENDE GEFUHRT"}; +const char *EndPartInputState_NS::endMsg3[] = {"DELL' AVVENTURA", "CONTINUE AVEC LES AUTRES", "THIS ADVENTURE", "MACH' MIT DEN ANDEREN WEITER"}; +// game completion messages +const char *EndPartInputState_NS::endMsg4[] = {"COMPLIMENTI!", "BRAVO!", "CONGRATULATIONS!", "PRIMA!"}; +const char *EndPartInputState_NS::endMsg5[] = {"HAI FINITO LE TRE PARTI", "TU AS COMPLETE' LES TROIS PARTIES", "YOU HAVE COMPLETED THE THREE PARTS", "DU HAST DREI ABENTEURE ERFOLGREICH"}; +const char *EndPartInputState_NS::endMsg6[] = {"DELL' AVVENTURA", "DE L'AVENTURE", "OF THIS ADVENTURE", "ZU ENDE GEFUHRT"}; +const char *EndPartInputState_NS::endMsg7[] = {"ED ORA IL GRAN FINALE ", "ET MAINTENANT LE GRAND FINAL", "NOW THE GREAT FINAL", "UND YETZT DER GROSSE SCHLUSS!"}; + +void Parallaction_ns::startGui() { + _disk->selectArchive((getFeatures() & GF_DEMO) ? "disk0" : "disk1"); - _gfx->updateScreen(); + _menuHelper = new MenuInputHelper; + assert(_menuHelper); - g_system->delayMillis(2000); - } + new SelectGameInputState_NS(this, _menuHelper); + new LoadGameInputState_NS(this, _menuHelper); + new NewGameInputState_NS(this, _menuHelper); + new StartDemoInputState_NS(this, _menuHelper); + new SelectCharacterInputState_NS(this, _menuHelper); + new ChooseLanguageInputState_NS(this, _menuHelper); + new SplashInputState1_NS(this, _menuHelper); + new SplashInputState0_NS(this, _menuHelper); + _menuHelper->setState("intro0"); - _gfx->freeLabels(); + _input->_inputMode = Input::kInputModeMenu; +} - _gfx->setBlackPalette(); - _gfx->updateScreen(); +void Parallaction_ns::startCreditSequence() { + _menuHelper = new MenuInputHelper; + assert(_menuHelper); - v14.free(); + new ShowCreditsInputState_NS(this, _menuHelper); + new EndIntroInputState_NS(this, _menuHelper); + new SelectCharacterInputState_NS(this, _menuHelper); + _menuHelper->setState("showcredits"); + _input->_inputMode = Input::kInputModeMenu; +} - // actually select character +void Parallaction_ns::startEndPartSequence() { + _menuHelper = new MenuInputHelper; + assert(_menuHelper); - int character = -1; - if (points[0] >= points[1] && points[0] >= points[2]) { - character = CHAR_DINO; - } else - if (points[1] >= points[0] && points[1] >= points[2]) { - character = CHAR_DONNA; - } else - if (points[2] >= points[0] && points[2] >= points[1]) { - character = CHAR_DOUGH; - } else { - error("If you read this, either your CPU or transivity is broken (we believe the former)."); - } + new EndPartInputState_NS(this, _menuHelper); + new SelectCharacterInputState_NS(this, _menuHelper); + _menuHelper->setState("endpart"); - return character; + _input->_inputMode = Input::kInputModeMenu; } diff --git a/engines/parallaction/input.cpp b/engines/parallaction/input.cpp index 9601ae3b36..287618e803 100644 --- a/engines/parallaction/input.cpp +++ b/engines/parallaction/input.cpp @@ -36,25 +36,23 @@ namespace Parallaction { // loops which could possibly be merged into this one with some effort in changing // caller code, i.e. adding condition checks. // -uint16 Input::readInput() { +void Input::readInput() { Common::Event e; - uint16 KeyDown = 0; _mouseButtons = kMouseNone; - _lastKeyDownAscii = -1; + _hasKeyPressEvent = false; Common::EventManager *eventMan = _vm->_system->getEventManager(); while (eventMan->pollEvent(e)) { switch (e.type) { case Common::EVENT_KEYDOWN: - _lastKeyDownAscii = e.kbd.ascii; + _hasKeyPressEvent = true; + _keyPressed = e.kbd; + if (e.kbd.flags == Common::KBD_CTRL && e.kbd.keycode == 'd') _vm->_debugger->attach(); - if (_vm->getFeatures() & GF_DEMO) break; - if (e.kbd.keycode == Common::KEYCODE_l) KeyDown = kEvLoadGame; - if (e.kbd.keycode == Common::KEYCODE_s) KeyDown = kEvSaveGame; break; case Common::EVENT_LBUTTONDOWN: @@ -83,7 +81,7 @@ uint16 Input::readInput() { case Common::EVENT_QUIT: _engineFlags |= kEngineQuit; - return KeyDown; + return; default: break; @@ -95,13 +93,13 @@ uint16 Input::readInput() { if (_vm->_debugger->isAttached()) _vm->_debugger->onFrame(); - return KeyDown; + return; } bool Input::getLastKeyDown(uint16 &ascii) { - ascii = _lastKeyDownAscii; - return (_lastKeyDownAscii != -1); + ascii = _keyPressed.ascii; + return (_hasKeyPressEvent); } // FIXME: see comment for readInput() @@ -128,64 +126,36 @@ void Input::waitForButtonEvent(uint32 buttonEventMask, int32 timeout) { } -// FIXME: see comment for readInput() -void Input::waitUntilLeftClick() { - - do { - readInput(); - _vm->_gfx->updateScreen(); - _vm->_system->delayMillis(30); - } while (_mouseButtons != kMouseLeftUp); - - return; -} - void Input::updateGameInput() { - int16 keyDown = readInput(); + readInput(); - debugC(3, kDebugInput, "translateInput: input flags (%i, %i, %i, %i)", - !_mouseHidden, - (_engineFlags & kEngineBlockInput) == 0, - (_engineFlags & kEngineWalking) == 0, - (_engineFlags & kEngineChangeLocation) == 0 - ); - - if ((_mouseHidden) || - (_engineFlags & kEngineBlockInput) || + if (!isMouseEnabled() || (_engineFlags & kEngineWalking) || (_engineFlags & kEngineChangeLocation)) { + debugC(3, kDebugInput, "updateGameInput: input flags (mouse: %i, walking: %i, changeloc: %i)", + isMouseEnabled(), + (_engineFlags & kEngineWalking) == 0, + (_engineFlags & kEngineChangeLocation) == 0 + ); + return; } - if (keyDown == kEvQuitGame) { - _inputData._event = kEvQuitGame; - } else - if (keyDown == kEvSaveGame) { - _inputData._event = kEvSaveGame; - } else - if (keyDown == kEvLoadGame) { - _inputData._event = kEvLoadGame; - } else { + if (_hasKeyPressEvent && (_vm->getFeatures() & GF_DEMO) == 0) { + if (_keyPressed.keycode == Common::KEYCODE_l) _inputData._event = kEvLoadGame; + if (_keyPressed.keycode == Common::KEYCODE_s) _inputData._event = kEvSaveGame; + } + + if (_inputData._event == kEvNone) { _inputData._mousePos = _mousePos; - _inputData._event = kEvNone; - if (!translateGameInput()) { - translateInventoryInput(); - } + translateGameInput(); } } -void Input::updateCommentInput() { - waitUntilLeftClick(); - - _vm->hideDialogueStuff(); - _vm->_gfx->setHalfbriteMode(false); - - _inputMode = kInputModeGame; -} InputData* Input::updateInput() { @@ -193,15 +163,18 @@ InputData* Input::updateInput() { switch (_inputMode) { case kInputModeComment: - updateCommentInput(); + case kInputModeDialogue: + case kInputModeMenu: + readInput(); break; case kInputModeGame: updateGameInput(); break; - case kInputModeDialogue: + case kInputModeInventory: readInput(); + updateInventoryInput(); break; } @@ -230,38 +203,45 @@ void Input::stopHovering() { _vm->_gfx->hideFloatingLabel(); } +void Input::takeAction(ZonePtr z) { + stopHovering(); + _vm->pauseJobs(); + _vm->runZone(z); + _vm->resumeJobs(); +} + +void Input::walkTo(const Common::Point &dest) { + stopHovering(); + _vm->setArrowCursor(); + _vm->_char.scheduleWalk(dest.x, dest.y); +} bool Input::translateGameInput() { - if ((_engineFlags & kEnginePauseJobs) || (_engineFlags & kEngineInventory)) { + if (_engineFlags & kEnginePauseJobs) { return false; } - if (_actionAfterWalk) { + if (_hasDelayedAction) { // if walking is over, then take programmed action - _inputData._event = kEvAction; - _actionAfterWalk = false; + takeAction(_delayedActionZone); + _hasDelayedAction = false; + _delayedActionZone = nullZonePtr; return true; } if (_mouseButtons == kMouseRightDown) { // right button down shows inventory - - if (_vm->hitZone(kZoneYou, _mousePos.x, _mousePos.y) && (_activeItem._id != 0)) { - _activeItem._index = (_activeItem._id >> 16) & 0xFFFF; - _engineFlags |= kEngineDragging; - } - - _inputData._event = kEvOpenInventory; - _transCurrentHoverItem = -1; + enterInventoryMode(); return true; } // test if mouse is hovering on an interactive zone for the currently selected inventory item ZonePtr z = _vm->hitZone(_activeItem._id, _mousePos.x, _mousePos.y); + Common::Point dest(_mousePos); if (((_mouseButtons == kMouseLeftUp) && (_activeItem._id == 0) && ((_engineFlags & kEngineWalking) == 0)) && ((!z) || ((z->_type & 0xFFFF) != kZoneCommand))) { - _inputData._event = kEvWalk; + walkTo(dest); return true; } @@ -275,16 +255,17 @@ bool Input::translateGameInput() { _inputData._zone = z; if (z->_flags & kFlagsNoWalk) { // character doesn't need to walk to take specified action - _inputData._event = kEvAction; - + takeAction(z); } else { // action delayed: if Zone defined a moveto position the character is programmed to move there, // else it will move to the mouse position - _inputData._event = kEvWalk; - _actionAfterWalk = true; + _delayedActionZone = z; + _hasDelayedAction = true; if (z->_moveTo.y != 0) { - _inputData._mousePos = z->_moveTo; + dest = z->_moveTo; } + + walkTo(dest); } _vm->beep(); @@ -293,31 +274,40 @@ bool Input::translateGameInput() { } return true; - } -bool Input::translateInventoryInput() { - if ((_engineFlags & kEngineInventory) == 0) { - return false; +void Input::enterInventoryMode() { + bool hitCharacter = _vm->hitZone(kZoneYou, _mousePos.x, _mousePos.y); + + if (hitCharacter) { + if (_activeItem._id != 0) { + _activeItem._index = (_activeItem._id >> 16) & 0xFFFF; + _engineFlags |= kEngineDragging; + } else { + _vm->setArrowCursor(); + } } - // in inventory - int16 _si = _vm->getHoverInventoryItem(_mousePos.x, _mousePos.y); + stopHovering(); + _vm->pauseJobs(); + _vm->openInventory(); - if (_mouseButtons == kMouseRightUp) { - // right up hides inventory + _transCurrentHoverItem = -1; - _inputData._event = kEvCloseInventory; - _inputData._inventoryIndex = _vm->getHoverInventoryItem(_mousePos.x, _mousePos.y); - _vm->highlightInventoryItem(-1); // disable + _inputMode = kInputModeInventory; +} - if ((_engineFlags & kEngineDragging) == 0) { - return true; - } +void Input::exitInventoryMode() { + // right up hides inventory + + int pos = _vm->getHoverInventoryItem(_mousePos.x, _mousePos.y); + _vm->highlightInventoryItem(-1); // disable + + if ((_engineFlags & kEngineDragging)) { _engineFlags &= ~kEngineDragging; - ZonePtr z = _vm->hitZone(kZoneMerge, _activeItem._index, _vm->getInventoryItemIndex(_inputData._inventoryIndex)); + ZonePtr z = _vm->hitZone(kZoneMerge, _activeItem._index, _vm->getInventoryItemIndex(pos)); if (z) { _vm->dropItem(z->u.merge->_obj1); @@ -326,25 +316,61 @@ bool Input::translateInventoryInput() { _vm->_cmdExec->run(z->_commands); } - return true; } - if (_si == _transCurrentHoverItem) { - _inputData._event = kEvNone; + _vm->closeInventory(); + if (pos == -1) { + _vm->setArrowCursor(); + } else { + const InventoryItem *item = _vm->getInventoryItem(pos); + if (item->_index != 0) { + _activeItem._id = item->_id; + _vm->setInventoryCursor(item->_index); + } + } + _vm->resumeJobs(); + + _inputMode = kInputModeGame; +} + +bool Input::updateInventoryInput() { + if (_mouseButtons == kMouseRightUp) { + exitInventoryMode(); return true; } - _transCurrentHoverItem = _si; - _inputData._event = kEvHoverInventory; - _inputData._inventoryIndex = _si; + int16 _si = _vm->getHoverInventoryItem(_mousePos.x, _mousePos.y); + if (_si != _transCurrentHoverItem) { + _transCurrentHoverItem = _si; + _vm->highlightInventoryItem(_si); // enable + } + return true; } -void Input::showCursor(bool visible) { - _mouseHidden = !visible; - _vm->_system->showMouse(visible); +void Input::setMouseState(MouseTriState state) { + assert(state == MOUSE_ENABLED_SHOW || state == MOUSE_ENABLED_HIDE || state == MOUSE_DISABLED); + _mouseState = state; + + switch (_mouseState) { + case MOUSE_ENABLED_HIDE: + case MOUSE_DISABLED: + _vm->_system->showMouse(false); + break; + + case MOUSE_ENABLED_SHOW: + _vm->_system->showMouse(true); + break; + } +} + +MouseTriState Input::getMouseState() { + return _mouseState; } +bool Input::isMouseEnabled() { + return (_mouseState == MOUSE_ENABLED_SHOW) || (_mouseState == MOUSE_ENABLED_HIDE); +} } // namespace Parallaction diff --git a/engines/parallaction/input.h b/engines/parallaction/input.h index f260352dba..c1e912db74 100644 --- a/engines/parallaction/input.h +++ b/engines/parallaction/input.h @@ -26,6 +26,8 @@ #ifndef PARALLACTION_INPUT_H #define PARALLACTION_INPUT_H +#include "common/keyboard.h" + #include "parallaction/objects.h" #include "parallaction/inventory.h" @@ -47,51 +49,65 @@ struct InputData { uint _label; }; +enum MouseTriState { + MOUSE_ENABLED_SHOW, + MOUSE_ENABLED_HIDE, + MOUSE_DISABLED +}; + class Input { void updateGameInput(); - void updateCommentInput(); // input-only InputData _inputData; - bool _actionAfterWalk; // actived when the character needs to move before taking an action - // these two could/should be merged as they carry on the same duty in two member functions, - // respectively processInput and translateInput + + bool _hasKeyPressEvent; + Common::KeyState _keyPressed; + + bool _hasDelayedAction; // actived when the character needs to move before taking an action + ZonePtr _delayedActionZone; + int16 _transCurrentHoverItem; InputData *translateInput(); bool translateGameInput(); - bool translateInventoryInput(); + bool updateInventoryInput(); + void takeAction(ZonePtr z); + void walkTo(const Common::Point &dest); Parallaction *_vm; Common::Point _mousePos; uint16 _mouseButtons; - int32 _lastKeyDownAscii; - bool _mouseHidden; ZonePtr _hoverZone; + void enterInventoryMode(); + void exitInventoryMode(); + public: enum { kInputModeGame = 0, kInputModeComment = 1, - kInputModeDialogue = 2 + kInputModeDialogue = 2, + kInputModeInventory = 3, + kInputModeMenu = 4 }; Input(Parallaction *vm) : _vm(vm) { _transCurrentHoverItem = 0; - _actionAfterWalk = false; // actived when the character needs to move before taking an action - _mouseHidden = false; + _hasDelayedAction = false; // actived when the character needs to move before taking an action + _mouseState = MOUSE_DISABLED; _activeItem._index = 0; _activeItem._id = 0; _mouseButtons = 0; + _delayedActionZone = nullZonePtr; } virtual ~Input() { } - void showCursor(bool visible); void getCursorPos(Common::Point& p) { p = _mousePos; } @@ -99,15 +115,20 @@ public: int _inputMode; InventoryItem _activeItem; - uint16 readInput(); + void readInput(); InputData* updateInput(); void trackMouse(ZonePtr z); - void waitUntilLeftClick(); void waitForButtonEvent(uint32 buttonEventMask, int32 timeout = -1); uint32 getLastButtonEvent() { return _mouseButtons; } bool getLastKeyDown(uint16 &ascii); void stopHovering(); + + MouseTriState _mouseState; + + void setMouseState(MouseTriState state); + MouseTriState getMouseState(); + bool isMouseEnabled(); }; } // namespace Parallaction diff --git a/engines/parallaction/inventory.cpp b/engines/parallaction/inventory.cpp index 58848196d7..7b92974205 100644 --- a/engines/parallaction/inventory.cpp +++ b/engines/parallaction/inventory.cpp @@ -30,23 +30,58 @@ namespace Parallaction { -// -// inventory is a grid made of (at most) 30 cells, 24x24 pixels each, -// arranged in 6 lines -// -// inventory items are stored in cnv files in a 32x24 grid -// but only 24x24 pixels are actually copied to graphic memory -// + +/* +#define INVENTORYITEM_PITCH 32 +#define INVENTORYITEM_WIDTH 24 +#define INVENTORYITEM_HEIGHT 24 #define INVENTORY_MAX_ITEMS 30 -#define INVENTORY_FIRST_ITEM 4 // first four entries are used up by verbs #define INVENTORY_ITEMS_PER_LINE 5 #define INVENTORY_LINES 6 #define INVENTORY_WIDTH (INVENTORY_ITEMS_PER_LINE*INVENTORYITEM_WIDTH) #define INVENTORY_HEIGHT (INVENTORY_LINES*INVENTORYITEM_HEIGHT) - +*/ + +InventoryItem _verbs_NS[] = { + { 1, kZoneDoor }, + { 3, kZoneExamine }, + { 2, kZoneGet }, + { 4, kZoneSpeak }, + { 0, 0 } +}; + +InventoryItem _verbs_BR[] = { + { 1, kZoneBox }, + { 2, kZoneGet }, + { 3, kZoneExamine }, + { 4, kZoneSpeak }, + { 0, 0 } +}; + +InventoryProperties _invProps_NS = { + 32, // INVENTORYITEM_PITCH + 24, // INVENTORYITEM_WIDTH + 24, // INVENTORYITEM_HEIGHT + 30, // INVENTORY_MAX_ITEMS + 5, // INVENTORY_ITEMS_PER_LINE + 6, // INVENTORY_LINES + 5 * 24, // INVENTORY_WIDTH =(INVENTORY_ITEMS_PER_LINE*INVENTORYITEM_WIDTH) + 6 * 24 // INVENTORY_HEIGHT = (INVENTORY_LINES*INVENTORYITEM_HEIGHT) +}; + +InventoryProperties _invProps_BR = { + 51, // INVENTORYITEM_PITCH + 51, // INVENTORYITEM_WIDTH + 51, // INVENTORYITEM_HEIGHT + 48, // INVENTORY_MAX_ITEMS + 6, // INVENTORY_ITEMS_PER_LINE + 8, // INVENTORY_LINES + 6 * 51, // INVENTORY_WIDTH =(INVENTORY_ITEMS_PER_LINE*INVENTORYITEM_WIDTH) + 8 * 51 // INVENTORY_HEIGHT = (INVENTORY_LINES*INVENTORYITEM_HEIGHT) +}; int16 Parallaction::getHoverInventoryItem(int16 x, int16 y) { return _inventoryRenderer->hitTest(Common::Point(x,y)); @@ -91,8 +126,19 @@ int16 Parallaction::getInventoryItemIndex(int16 pos) { } void Parallaction::initInventory() { - _inventory = new Inventory(INVENTORY_MAX_ITEMS); - _inventoryRenderer = new InventoryRenderer(this); + InventoryProperties *props; + InventoryItem *verbs; + + if (getGameType() == GType_Nippon) { + props = &_invProps_NS; + verbs = _verbs_NS; + } else { + props = &_invProps_BR; + verbs = _verbs_BR; + } + + _inventory = new Inventory(props, verbs); + _inventoryRenderer = new InventoryRenderer(this, props); _inventoryRenderer->bindInventory(_inventory); } @@ -119,8 +165,8 @@ void Parallaction::closeInventory() { -InventoryRenderer::InventoryRenderer(Parallaction *vm) : _vm(vm) { - _surf.create(INVENTORY_WIDTH, INVENTORY_HEIGHT, 1); +InventoryRenderer::InventoryRenderer(Parallaction *vm, InventoryProperties *props) : _vm(vm), _props(props) { + _surf.create(_props->_width, _props->_height, 1); } InventoryRenderer::~InventoryRenderer() { @@ -131,15 +177,13 @@ void InventoryRenderer::showInventory() { if (!_inv) error("InventoryRenderer not bound to inventory"); - _engineFlags |= kEngineInventory; - uint16 lines = getNumLines(); Common::Point p; _vm->_input->getCursorPos(p); - _pos.x = CLIP(p.x - (INVENTORY_WIDTH / 2), 0, (int)(_vm->_screenWidth - INVENTORY_WIDTH)); - _pos.y = CLIP(p.y - 2 - (lines * INVENTORYITEM_HEIGHT), 0, (int)(_vm->_screenHeight - lines * INVENTORYITEM_HEIGHT)); + _pos.x = CLIP((int)(p.x - (_props->_width / 2)), 0, (int)(_vm->_screenWidth - _props->_width)); + _pos.y = CLIP((int)(p.y - 2 - (lines * _props->_itemHeight)), 0, (int)(_vm->_screenHeight - lines * _props->_itemHeight)); refresh(); } @@ -147,13 +191,11 @@ void InventoryRenderer::showInventory() { void InventoryRenderer::hideInventory() { if (!_inv) error("InventoryRenderer not bound to inventory"); - - _engineFlags &= ~kEngineInventory; } void InventoryRenderer::getRect(Common::Rect& r) const { - r.setWidth(INVENTORY_WIDTH); - r.setHeight(INVENTORYITEM_HEIGHT * getNumLines()); + r.setWidth(_props->_width); + r.setHeight(_props->_itemHeight * getNumLines()); r.moveTo(_pos); } @@ -163,35 +205,36 @@ ItemPosition InventoryRenderer::hitTest(const Common::Point &p) const { if (!r.contains(p)) return -1; - return ((p.x - _pos.x) / INVENTORYITEM_WIDTH) + (INVENTORY_ITEMS_PER_LINE * ((p.y - _pos.y) / INVENTORYITEM_HEIGHT)); + return ((p.x - _pos.x) / _props->_itemWidth) + (_props->_itemsPerLine * ((p.y - _pos.y) / _props->_itemHeight)); } - void InventoryRenderer::drawItem(ItemPosition pos, ItemName name) { - Common::Rect r; getItemRect(pos, r); + byte* d = (byte*)_surf.getBasePtr(r.left, r.top); + drawItem(name, d, _surf.pitch); +} - // FIXME: this will end up in a general blit function - +void InventoryRenderer::drawItem(ItemName name, byte *buffer, uint pitch) { byte* s = _vm->_char._objs->getData(name); - byte* d = (byte*)_surf.getBasePtr(r.left, r.top); - for (uint32 i = 0; i < INVENTORYITEM_HEIGHT; i++) { - memcpy(d, s, INVENTORYITEM_WIDTH); + byte* d = buffer; + for (uint i = 0; i < _props->_itemHeight; i++) { + memcpy(d, s, _props->_itemWidth); - d += INVENTORY_WIDTH; - s += INVENTORYITEM_PITCH; + s += _props->_itemPitch; + d += pitch; } } + int16 InventoryRenderer::getNumLines() const { int16 num = _inv->getNumItems(); - return (num / INVENTORY_ITEMS_PER_LINE) + ((num % INVENTORY_ITEMS_PER_LINE) > 0 ? 1 : 0); + return (num / _props->_itemsPerLine) + ((num % _props->_itemsPerLine) > 0 ? 1 : 0); } void InventoryRenderer::refresh() { - for (uint16 i = 0; i < INVENTORY_MAX_ITEMS; i++) { + for (uint16 i = 0; i < _props->_maxItems; i++) { ItemName name = _inv->getItemName(i); drawItem(i, name); } @@ -212,25 +255,24 @@ void InventoryRenderer::highlightItem(ItemPosition pos, byte color) { void InventoryRenderer::getItemRect(ItemPosition pos, Common::Rect &r) { - r.setHeight(INVENTORYITEM_HEIGHT); - r.setWidth(INVENTORYITEM_WIDTH); + r.setHeight(_props->_itemHeight); + r.setWidth(_props->_itemWidth); - uint16 line = pos / INVENTORY_ITEMS_PER_LINE; - uint16 col = pos % INVENTORY_ITEMS_PER_LINE; + uint16 line = pos / _props->_itemsPerLine; + uint16 col = pos % _props->_itemsPerLine; - r.moveTo(col * INVENTORYITEM_WIDTH, line * INVENTORYITEM_HEIGHT); + r.moveTo(col * _props->_itemWidth, line * _props->_itemHeight); } +Inventory::Inventory(InventoryProperties *props, InventoryItem *verbs) : _numItems(0), _props(props) { + _items = (InventoryItem*)calloc(_props->_maxItems, sizeof(InventoryItem)); - -Inventory::Inventory(uint16 maxItems) : _maxItems(maxItems), _numItems(0) { - _items = (InventoryItem*)calloc(_maxItems, sizeof(InventoryItem)); - - addItem(1, kZoneDoor); - addItem(3, kZoneExamine); - addItem(2, kZoneGet); - addItem(4, kZoneSpeak); + int i = 0; + for ( ; verbs[i]._id; i++) { + addItem(verbs[i]._id, verbs[i]._index); + } + _numVerbs = i; } @@ -241,7 +283,7 @@ Inventory::~Inventory() { ItemPosition Inventory::addItem(ItemName name, uint32 value) { debugC(1, kDebugInventory, "addItem(%i, %i)", name, value); - if (_numItems == INVENTORY_MAX_ITEMS) { + if (_numItems == _props->_maxItems) { debugC(3, kDebugInventory, "addItem: inventory is full"); return -1; } @@ -300,9 +342,9 @@ void Inventory::removeItem(ItemName name) { void Inventory::clear(bool keepVerbs) { debugC(1, kDebugInventory, "clearInventory()"); - uint first = (keepVerbs ? INVENTORY_FIRST_ITEM : 0); + uint first = (keepVerbs ? _numVerbs : 0); - for (uint16 slot = first; slot < _maxItems; slot++) { + for (uint16 slot = first; slot < _numVerbs; slot++) { _items[slot]._id = 0; _items[slot]._index = 0; } @@ -312,7 +354,7 @@ void Inventory::clear(bool keepVerbs) { ItemName Inventory::getItemName(ItemPosition pos) const { - return (pos >= 0 && pos < INVENTORY_MAX_ITEMS) ? _items[pos]._index : 0; + return (pos >= 0 && pos < _props->_maxItems) ? _items[pos]._index : 0; } const InventoryItem* Inventory::getItem(ItemPosition pos) const { diff --git a/engines/parallaction/inventory.h b/engines/parallaction/inventory.h index 8c32c09219..f041627810 100644 --- a/engines/parallaction/inventory.h +++ b/engines/parallaction/inventory.h @@ -38,9 +38,19 @@ struct InventoryItem { uint16 _index; // index to frame in objs file }; -#define INVENTORYITEM_PITCH 32 -#define INVENTORYITEM_WIDTH 24 -#define INVENTORYITEM_HEIGHT 24 +struct InventoryProperties { + uint _itemPitch; + uint _itemWidth; + uint _itemHeight; + + int _maxItems; + + int _itemsPerLine; + int _maxLines; + + int _width; + int _height; +}; #define MAKE_INVENTORY_ID(x) (((x) & 0xFFFF) << 16) @@ -50,12 +60,14 @@ typedef uint16 ItemName; class Inventory { protected: + uint16 _numVerbs; + InventoryItem *_items; - uint16 _maxItems; uint16 _numItems; + InventoryProperties *_props; public: - Inventory(uint16 maxItems); + Inventory(InventoryProperties *props, InventoryItem *verbs); virtual ~Inventory(); ItemPosition addItem(ItemName name, uint32 value); @@ -75,6 +87,8 @@ public: class InventoryRenderer { Parallaction *_vm; + InventoryProperties *_props; + Inventory *_inv; Common::Point _pos; @@ -87,7 +101,7 @@ protected: void refresh(); public: - InventoryRenderer(Parallaction *vm); + InventoryRenderer(Parallaction *vm, InventoryProperties *props); virtual ~InventoryRenderer(); void bindInventory(Inventory *inv) { _inv = inv; } @@ -97,6 +111,7 @@ public: ItemPosition hitTest(const Common::Point &p) const; void highlightItem(ItemPosition pos, byte color); + void drawItem(ItemName name, byte *buffer, uint pitch); byte* getData() const { return (byte*)_surf.pixels; } diff --git a/engines/parallaction/module.mk b/engines/parallaction/module.mk index fb867f5285..9d44422541 100644 --- a/engines/parallaction/module.mk +++ b/engines/parallaction/module.mk @@ -14,6 +14,7 @@ MODULE_OBJS := \ font.o \ gfxbase.o \ graphics.o \ + gui.o \ gui_br.o \ gui_ns.o \ input.o \ diff --git a/engines/parallaction/objects.cpp b/engines/parallaction/objects.cpp index 66025cf0f7..c387484de7 100644 --- a/engines/parallaction/objects.cpp +++ b/engines/parallaction/objects.cpp @@ -60,14 +60,14 @@ Animation::~Animation() { uint16 Animation::width() const { if (!gfxobj) return 0; Common::Rect r; - gfxobj->getRect(0, r); + gfxobj->getRect(_frame, r); return r.width(); } uint16 Animation::height() const { if (!gfxobj) return 0; Common::Rect r; - gfxobj->getRect(0, r); + gfxobj->getRect(_frame, r); return r.height(); } @@ -81,6 +81,12 @@ byte* Animation::getFrameData(uint32 index) const { return gfxobj->getData(index); } +void Animation::validateScriptVars() { + // this is used to clip values of _frame, _left and _top + // which can be screwed up by buggy scripts. + + _frame = CLIP(_frame, (int16)0, (int16)(getFrameNum() - 1)); +} #define NUM_LOCALS 10 char _localNames[NUM_LOCALS][10]; diff --git a/engines/parallaction/objects.h b/engines/parallaction/objects.h index 19835da9d0..7e7a811ba6 100644 --- a/engines/parallaction/objects.h +++ b/engines/parallaction/objects.h @@ -54,6 +54,7 @@ typedef Common::SharedPtr<Instruction> InstructionPtr; typedef Common::List<InstructionPtr> InstructionList; extern InstructionPtr nullInstructionPtr; +typedef Common::List<Common::Point> PointList; enum ZoneTypes { kZoneExamine = 1, // zone displays comment if activated @@ -67,7 +68,11 @@ enum ZoneTypes { kZoneNone = 0x100, // used to prevent parsing on peculiar Animations kZoneTrap = 0x200, // zone activated when character enters kZoneYou = 0x400, // marks the character - kZoneCommand = 0x800 + kZoneCommand = 0x800, + + // BRA specific + kZonePath = 0x1000, // defines nodes for assisting walk calculation routines + kZoneBox = 0x2000 }; @@ -89,6 +94,7 @@ enum ZoneFlags { kFlagsYourself = 0x1000, kFlagsScaled = 0x2000, kFlagsSelfuse = 0x4000, + kFlagsIsAnimation = 0x1000000, // BRA: used in walk code (trap check), to tell is a Zone is an Animation kFlagsAnimLinked = 0x2000000 }; @@ -256,6 +262,15 @@ struct MergeData { // size = 12 _obj1 = _obj2 = _obj3 = 0; } }; +#define MAX_WALKPOINT_LISTS 20 +struct PathData { + int _numLists; + PointList _lists[MAX_WALKPOINT_LISTS]; + + PathData() { + _numLists = 0; + } +}; struct TypeData { GetData *get; @@ -264,6 +279,8 @@ struct TypeData { DoorData *door; HearData *hear; MergeData *merge; + // BRA specific field + PathData *path; TypeData() { get = NULL; @@ -272,6 +289,7 @@ struct TypeData { door = NULL; hear = NULL; merge = NULL; + path = NULL; } }; @@ -432,6 +450,8 @@ struct Animation : public Zone { virtual uint16 height() const; uint16 getFrameNum() const; byte* getFrameData(uint32 index) const; + + void validateScriptVars(); }; class Table { diff --git a/engines/parallaction/parallaction.cpp b/engines/parallaction/parallaction.cpp index cd852f7c84..54cb175e46 100644 --- a/engines/parallaction/parallaction.cpp +++ b/engines/parallaction/parallaction.cpp @@ -98,6 +98,10 @@ Parallaction::~Parallaction() { freeCharacter(); destroyInventory(); + cleanupGui(); + + delete _comboArrow; + delete _localFlagNames; delete _gfx; delete _soundMan; @@ -136,6 +140,8 @@ int Parallaction::init() { _debugger = new Debugger(this); + _menuHelper = 0; + setupBalloonManager(); return 0; @@ -151,7 +157,7 @@ void Parallaction::clearSet(OpcodeSet &opcodes) { void Parallaction::updateView() { - if ((_engineFlags & kEnginePauseJobs) && (_engineFlags & kEngineInventory) == 0) { + if ((_engineFlags & kEnginePauseJobs) && (_input->_inputMode != Input::kInputModeInventory)) { return; } @@ -255,7 +261,7 @@ void Parallaction::freeLocation() { _localFlagNames->clear(); - _location._walkNodes.clear(); + _location._walkPoints.clear(); _gfx->clearGfxObjects(kGfxObjNormal); freeBackground(); @@ -297,46 +303,11 @@ void Parallaction::showLocationComment(const char *text, bool end) { void Parallaction::processInput(InputData *data) { + if (!data) { + return; + } switch (data->_event) { - case kEvAction: - debugC(2, kDebugInput, "processInput: kEvAction"); - _input->stopHovering(); - pauseJobs(); - runZone(data->_zone); - resumeJobs(); - break; - - case kEvOpenInventory: - _input->stopHovering(); - if (hitZone(kZoneYou, data->_mousePos.x, data->_mousePos.y) == 0) { - setArrowCursor(); - } - pauseJobs(); - openInventory(); - break; - - case kEvCloseInventory: // closes inventory and possibly select item - closeInventory(); - setInventoryCursor(data->_inventoryIndex); - resumeJobs(); - break; - - case kEvHoverInventory: - highlightInventoryItem(data->_inventoryIndex); // enable - break; - - case kEvWalk: - debugC(2, kDebugInput, "processInput: kEvWalk"); - _input->stopHovering(); - setArrowCursor(); - _char.scheduleWalk(data->_mousePos.x, data->_mousePos.y); - break; - - case kEvQuitGame: - _engineFlags |= kEngineQuit; - break; - case kEvSaveGame: _input->stopHovering(); saveGame(); @@ -360,16 +331,12 @@ void Parallaction::runGame() { if (_engineFlags & kEngineQuit) return; - if (_input->_inputMode == Input::kInputModeDialogue) { - runDialogueFrame(); - } else { - if (data->_event != kEvNone) { - processInput(data); - } - - if (_engineFlags & kEngineQuit) - return; + runGuiFrame(); + runDialogueFrame(); + runCommentFrame(); + if (_input->_inputMode == Input::kInputModeGame) { + processInput(data); runPendingZones(); if (_engineFlags & kEngineQuit) @@ -388,7 +355,7 @@ void Parallaction::runGame() { if (_char._ani->gfxobj) { _char._ani->gfxobj->z = _char._ani->_z; } - walk(_char); + _char._walker->walk(); drawAnimations(); } @@ -424,11 +391,10 @@ void Parallaction::doLocationEnterTransition() { _programExec->runScripts(_location._programs.begin(), _location._programs.end()); drawAnimations(); - + showLocationComment(_location._comment, false); _gfx->updateScreen(); - showLocationComment(_location._comment, false); - _input->waitUntilLeftClick(); + _input->waitForButtonEvent(kMouseLeftUp); _balloonMan->freeBalloons(); // fades maximum intensity palette towards approximation of main palette @@ -533,7 +499,7 @@ const char Character::_suffixTras[] = "tras"; const char Character::_empty[] = "\0"; -Character::Character(Parallaction *vm) : _vm(vm), _ani(new Animation), _builder(_ani) { +Character::Character(Parallaction *vm) : _vm(vm), _ani(new Animation) { _talk = NULL; _head = NULL; _objs = NULL; @@ -552,24 +518,61 @@ Character::Character(Parallaction *vm) : _vm(vm), _ani(new Animation), _builder( _ani->_flags = kFlagsActive | kFlagsNoName; _ani->_type = kZoneYou; strncpy(_ani->_name, "yourself", ZONENAME_LENGTH); + + // TODO: move creation into Parallaction. Needs to make Character a pointer first. + if (_vm->getGameType() == GType_Nippon) { + _builder = new PathBuilder_NS(this); + _walker = new PathWalker_NS(this); + } else { + _builder = new PathBuilder_BR(this); + _walker = new PathWalker_BR(this); + } +} + +Character::~Character() { + delete _builder; + _builder = 0; + + delete _walker; + _walker = 0; + + free(); } void Character::getFoot(Common::Point &foot) { - foot.x = _ani->_left + _ani->width() / 2; - foot.y = _ani->_top + _ani->height(); + Common::Rect rect; + _ani->gfxobj->getRect(_ani->_frame, rect); + + foot.x = _ani->_left + (rect.left + rect.width() / 2); + foot.y = _ani->_top + (rect.top + rect.height()); } void Character::setFoot(const Common::Point &foot) { - _ani->_left = foot.x - _ani->width() / 2; - _ani->_top = foot.y - _ani->height(); + Common::Rect rect; + _ani->gfxobj->getRect(_ani->_frame, rect); + + _ani->_left = foot.x - (rect.left + rect.width() / 2); + _ani->_top = foot.y - (rect.top + rect.height()); +} + +#if 0 +void dumpPath(const PointList &list, const char* text) { + for (PointList::iterator it = list.begin(); it != list.end(); it++) + printf("node (%i, %i)\n", it->x, it->y); + + return; } +#endif void Character::scheduleWalk(int16 x, int16 y) { if ((_ani->_flags & kFlagsRemove) || (_ani->_flags & kFlagsActive) == 0) { return; } - _walkPath = _builder.buildPath(x, y); + _builder->buildPath(x, y); +#if 0 + dumpPath(_walkPath, _name); +#endif _engineFlags |= kEngineWalking; } diff --git a/engines/parallaction/parallaction.h b/engines/parallaction/parallaction.h index c45d32b013..d8e4da5baf 100644 --- a/engines/parallaction/parallaction.h +++ b/engines/parallaction/parallaction.h @@ -101,10 +101,8 @@ enum { enum EngineFlags { kEngineQuit = (1 << 0), kEnginePauseJobs = (1 << 1), - kEngineInventory = (1 << 2), kEngineWalking = (1 << 3), kEngineChangeLocation = (1 << 4), - kEngineBlockInput = (1 << 5), kEngineDragging = (1 << 6), kEngineTransformedDonna = (1 << 7), @@ -114,12 +112,6 @@ enum EngineFlags { enum { kEvNone = 0, - kEvAction = 3, - kEvOpenInventory = 4, - kEvCloseInventory = 5, - kEvHoverInventory = 6, - kEvWalk = 7, - kEvQuitGame = 1000, kEvSaveGame = 2000, kEvLoadGame = 4000 }; @@ -164,6 +156,7 @@ class Gfx; class SoundMan; class Input; class DialogueManager; +class MenuInputHelper; struct Location { @@ -184,7 +177,7 @@ struct Location { char _soundFile[50]; // NS specific - WalkNodeList _walkNodes; + PointList _walkPoints; char _slideText[2][MAX_TOKEN_LEN]; // BRA specific @@ -205,10 +198,13 @@ struct Character { GfxObj *_head; GfxObj *_talk; GfxObj *_objs; - PathBuilder _builder; - WalkNodeList *_walkPath; + PathBuilder *_builder; + PathWalker *_walker; + PointList _walkPath; Character(Parallaction *vm); + ~Character(); + void getFoot(Common::Point &foot); void setFoot(const Common::Point &foot); void scheduleWalk(int16 x, int16 y); @@ -265,10 +261,6 @@ public: void pauseJobs(); void resumeJobs(); - void finalizeWalk(Character &character); - int16 selectWalkFrame(Character &character, const Common::Point& pos, const WalkNodePtr to); - void clipMove(Common::Point& pos, const Common::Point& to); - ZonePtr findZone(const char *name); ZonePtr hitZone(uint32 type, uint16 x, uint16 y); uint16 runZone(ZonePtr z); @@ -333,6 +325,7 @@ public: Common::RandomSource _rnd; Debugger *_debugger; + Frames *_comboArrow; protected: // data @@ -359,8 +352,6 @@ protected: // members void displayComment(ExamineData *data); - void checkDoor(const Common::Point &foot); - void freeCharacter(); int16 pickupItem(ZonePtr z); @@ -375,18 +366,18 @@ public: virtual void callFunction(uint index, void* parm) { } virtual void setArrowCursor() = 0; - virtual void setInventoryCursor(int pos) = 0; + virtual void setInventoryCursor(ItemName name) = 0; virtual void parseLocation(const char* name) = 0; void updateDoor(ZonePtr z); - virtual void walk(Character &character) = 0; virtual void drawAnimations() = 0; void beep(); ZonePtr _zoneTrap; + PathBuilder* getPathBuilder(Character *ch); public: // const char **_zoneFlagNamesRes; @@ -422,6 +413,17 @@ public: void exitDialogueMode(); void runDialogueFrame(); + MenuInputHelper *_menuHelper; + void runGuiFrame(); + void cleanupGui(); + + ZonePtr _commentZone; + void enterCommentMode(ZonePtr z); + void exitCommentMode(); + void runCommentFrame(); + + void setInternLanguage(uint id); + uint getInternLanguage(); }; @@ -480,12 +482,18 @@ public: typedef void (Parallaction_ns::*Callable)(void*); virtual void callFunction(uint index, void* parm); - void setMousePointer(uint32 value); bool loadGame(); bool saveGame(); void switchBackground(const char* background, const char* mask); + void showSlide(const char *name); + void setArrowCursor(); + + // TODO: this should be private!!!!!!! + bool _inTestResult; + void cleanupGame(); + bool allPartsComplete(); private: LocationParser_ns *_locationParser; @@ -497,17 +505,14 @@ private: Common::String genSaveFileName(uint slot, bool oldStyle = false); Common::InSaveFile *getInSaveFile(uint slot); Common::OutSaveFile *getOutSaveFile(uint slot); - bool allPartsComplete(); void setPartComplete(const Character& character); private: void changeLocation(char *location); void changeCharacter(const char *name); void runPendingZones(); - void cleanupGame(); - void setArrowCursor(); - void setInventoryCursor(int pos); + void setInventoryCursor(ItemName name); void doLoadGame(uint16 slot); @@ -520,7 +525,6 @@ private: static byte _resMouseArrow[256]; byte *_mouseArrow; - Frames *_mouseComposedArrow; static const Callable _dosCallables[25]; static const Callable _amigaCallables[25]; @@ -540,9 +544,6 @@ private: ZonePtr _moveSarcExaZones[5]; AnimationPtr _rightHandAnim; - bool _inTestResult; - - // common callables void _c_play_boogie(void*); void _c_startIntro(void*); @@ -579,7 +580,6 @@ private: const Callable *_callables; protected: - void walk(Character &character); void drawAnimations(); void parseLocation(const char *filename); @@ -587,15 +587,9 @@ protected: void selectStartLocation(); - void guiStart(); - int guiSelectCharacter(); - void guiSplash(); - int guiNewGame(); - uint16 guiChooseLanguage(); - uint16 guiSelectGame(); - int guiGetSelectedBlock(const Common::Point &p); - - void showSlide(const char *name); + void startGui(); + void startCreditSequence(); + void startEndPartSequence(); }; @@ -639,7 +633,8 @@ public: int32 _counters[32]; uint32 _zoneFlags[NUM_LOCATIONS][NUM_ZONES]; - + void startPart(uint part); + void setArrowCursor(); private: LocationParser_br *_locationParser; ProgramParser_br *_programParser; @@ -648,17 +643,14 @@ private: void initFonts(); void freeFonts(); - void setArrowCursor(); - void setInventoryCursor(int pos); + void setInventoryCursor(ItemName name); void changeLocation(char *location); void runPendingZones(); void initPart(); void freePart(); - void startPart(); - void setMousePointer(int16 index); void initCursors(); Frames *_dinoCursor; @@ -669,10 +661,7 @@ private: static const char *_partNames[]; - void guiStart(); - int guiShowMenu(); - void guiSplash(const char *name); - Frames* guiRenderMenuItem(const char *text); + void startGui(); static const Callable _dosCallables[6]; diff --git a/engines/parallaction/parallaction_br.cpp b/engines/parallaction/parallaction_br.cpp index 9e2a0f10f1..761c8d1b74 100644 --- a/engines/parallaction/parallaction_br.cpp +++ b/engines/parallaction/parallaction_br.cpp @@ -32,6 +32,27 @@ namespace Parallaction { +struct MouseComboProperties { + int _xOffset; + int _yOffset; + int _width; + int _height; +}; +/* +// TODO: improve NS's handling of normal cursor before merging cursor code. +MouseComboProperties _mouseComboProps_NS = { + 7, // combo x offset (the icon from the inventory will be rendered from here) + 7, // combo y offset (ditto) + 32, // combo (arrow + icon) width + 32 // combo (arrow + icon) height +}; +*/ +MouseComboProperties _mouseComboProps_BR = { + 8, // combo x offset (the icon from the inventory will be rendered from here) + 8, // combo y offset (ditto) + 68, // combo (arrow + icon) width + 68 // combo (arrow + icon) height +}; const char *Parallaction_br::_partNames[] = { "PART0", @@ -56,7 +77,11 @@ int Parallaction_br::init() { if (getGameType() == GType_BRA) { if (getPlatform() == Common::kPlatformPC) { - _disk = new DosDisk_br(this); + if (getFeatures() & GF_DEMO) { + _disk = new DosDemo_br(this); + } else { + _disk = new DosDisk_br(this); + } _disk->setLanguage(2); // NOTE: language is now hardcoded to English. Original used command-line parameters. _soundMan = new DummySoundMan(this); } else { @@ -99,6 +124,7 @@ Parallaction_br::~Parallaction_br() { delete _dougCursor; delete _donnaCursor; + delete _mouseArrow; } void Parallaction_br::callFunction(uint index, void* parm) { @@ -109,16 +135,14 @@ void Parallaction_br::callFunction(uint index, void* parm) { int Parallaction_br::go() { - guiSplash("dyna"); - guiSplash("core"); + if (getFeatures() & GF_DEMO) { + startPart(1); + } else { + startGui(); + } while ((_engineFlags & kEngineQuit) == 0) { - guiStart(); - - if (_engineFlags & kEngineQuit) - return 0; - // initCharacter(); _input->_inputMode = Input::kInputModeGame; @@ -152,6 +176,12 @@ void Parallaction_br::initCursors() { _dougCursor = _disk->loadPointer("pointer2"); _donnaCursor = _disk->loadPointer("pointer3"); + Graphics::Surface *surf = new Graphics::Surface; + surf->create(_mouseComboProps_BR._width, _mouseComboProps_BR._height, 1); + _comboArrow = new SurfaceToFrames(surf); + + // TODO: choose the pointer depending on the active character + // For now, we pick Donna's _mouseArrow = _donnaCursor; } else { // TODO: Where are the Amiga cursors? @@ -159,19 +189,6 @@ void Parallaction_br::initCursors() { } -void Parallaction_br::setMousePointer(int16 index) { - // FIXME: Where are the Amiga cursors? - if (getPlatform() == Common::kPlatformAmiga) - return; - - Common::Rect r; - _mouseArrow->getRect(0, r); - - _system->setMouseCursor(_mouseArrow->getData(0), r.width(), r.height(), 0, 0, 0); - _system->showMouse(true); - -} - void Parallaction_br::initPart() { memset(_counters, 0, ARRAYSIZE(_counters)); @@ -180,7 +197,12 @@ void Parallaction_br::initPart() { _objectsNames = _disk->loadTable("objects"); _countersNames = _disk->loadTable("counters"); -// _disk->loadObjects("icone.ico"); + // TODO: maybe handle this into Disk + if (getPlatform() == Common::kPlatformPC) { + _char._objs = _disk->loadObjects("icone.ico"); + } else { + _char._objs = _disk->loadObjects("icons.ico"); + } } @@ -195,11 +217,17 @@ void Parallaction_br::freePart() { _countersNames = 0; } -void Parallaction_br::startPart() { +void Parallaction_br::startPart(uint part) { + _part = part; + _disk->selectArchive(_partNames[_part]); initPart(); - strcpy(_location._name, partFirstLocation[_part]); + if (getFeatures() & GF_DEMO) { + strcpy(_location._name, "camalb"); + } else { + strcpy(_location._name, partFirstLocation[_part]); + } parseLocation("common"); changeLocation(_location._name); @@ -209,6 +237,8 @@ void Parallaction_br::startPart() { void Parallaction_br::runPendingZones() { ZonePtr z; + _cmdExec->runSuspended(); + if (_activeZone) { z = _activeZone; // speak Zone or sound _activeZone = nullZonePtr; @@ -236,7 +266,10 @@ void Parallaction_br::changeLocation(char *location) { // free open location stuff clearSubtitles(); freeBackground(); - _gfx->clearGfxObjects(kGfxObjNormal | kGfxObjCharacter); + _gfx->clearGfxObjects(kGfxObjNormal); + _gfx->freeLabels(); + _subtitle[0] = _subtitle[1] = -1; + _location._programs.clear(); _location._animations.remove(_char._ani); @@ -248,12 +281,17 @@ void Parallaction_br::changeLocation(char *location) { // free(_location._comment); // _location._comment = 0; -// _location._commands.clear(); -// _location._aCommands.clear(); - + _location._commands.clear(); + _location._aCommands.clear(); // load new location parseLocation(location); + + // kFlagsRemove is cleared because the character defaults to visible on new locations + // script command can hide the character, anyway, so that's why the flag is cleared + // before _location._commands are executed + _char._ani->_flags &= ~kFlagsRemove; + _cmdExec->run(_location._commands); // doLocationEnterTransition(); _cmdExec->run(_location._aCommands); @@ -305,30 +343,46 @@ void Parallaction_br::loadProgram(AnimationPtr a, const char *filename) { void Parallaction_br::changeCharacter(const char *name) { - printf("changeCharacter(%s)\n", name); - const char *charName = _char.getName(); - if (!scumm_stricmp(charName, name)) { - return; + + if (scumm_stricmp(charName, name)) { + debugC(1, kDebugExec, "changeCharacter(%s)", name); + + _char.setName(name); + _char._ani->gfxobj = _gfx->loadAnim(name); + _char._ani->gfxobj->setFlags(kGfxObjCharacter); + _char._ani->gfxobj->clearFlags(kGfxObjNormal); + _char._talk = _disk->loadTalk(name); } - _char.setName(name); - _char._ani->gfxobj = _gfx->loadAnim(name); - _char._ani->gfxobj->setFlags(kGfxObjCharacter); - _char._talk = _disk->loadTalk(name); + _char._ani->_flags |= kFlagsActive; } void Parallaction_br::setArrowCursor() { + // FIXME: Where are the Amiga cursors? + if (getPlatform() == Common::kPlatformAmiga) + return; + Common::Rect r; + _mouseArrow->getRect(0, r); + _system->setMouseCursor(_mouseArrow->getData(0), r.width(), r.height(), 0, 0, 0); + _system->showMouse(true); + _input->_activeItem._id = 0; } -void Parallaction_br::setInventoryCursor(int pos) { - +void Parallaction_br::setInventoryCursor(ItemName name) { + assert(name > 0); + byte *src = _mouseArrow->getData(0); + byte *dst = _comboArrow->getData(0); + memcpy(dst, src, _comboArrow->getSize(0)); + // FIXME: destination offseting is not clear + _inventoryRenderer->drawItem(name, dst + _mouseComboProps_BR._yOffset * _mouseComboProps_BR._width + _mouseComboProps_BR._xOffset, _mouseComboProps_BR._width); + _system->setMouseCursor(dst, _mouseComboProps_BR._width, _mouseComboProps_BR._height, 0, 0, 0); } } // namespace Parallaction diff --git a/engines/parallaction/parallaction_ns.cpp b/engines/parallaction/parallaction_ns.cpp index 9e925d1e1d..851fe38138 100644 --- a/engines/parallaction/parallaction_ns.cpp +++ b/engines/parallaction/parallaction_ns.cpp @@ -34,6 +34,7 @@ namespace Parallaction { + #define MOUSEARROW_WIDTH 16 #define MOUSEARROW_HEIGHT 16 @@ -165,7 +166,6 @@ Parallaction_ns::~Parallaction_ns() { delete _locationParser; delete _programParser; - delete _mouseComposedArrow; _location._animations.remove(_char._ani); @@ -182,7 +182,7 @@ void Parallaction_ns::freeFonts() { } void Parallaction_ns::initCursors() { - _mouseComposedArrow = _disk->loadPointer("pointer"); + _comboArrow = _disk->loadPointer("pointer"); _mouseArrow = _resMouseArrow; } @@ -195,36 +195,16 @@ void Parallaction_ns::setArrowCursor() { _input->_activeItem._id = 0; _system->setMouseCursor(_mouseArrow, MOUSEARROW_WIDTH, MOUSEARROW_HEIGHT, 0, 0, 0); - _system->showMouse(true); - } -void Parallaction_ns::setInventoryCursor(int pos) { - - if (pos == -1) - return; - - const InventoryItem *item = getInventoryItem(pos); - if (item->_index == 0) - return; - - _input->_activeItem._id = item->_id; +void Parallaction_ns::setInventoryCursor(ItemName name) { + assert(name > 0); - byte *v8 = _mouseComposedArrow->getData(0); + byte *v8 = _comboArrow->getData(0); // FIXME: destination offseting is not clear - byte* s = _char._objs->getData(item->_index); - byte* d = v8 + 7 + MOUSECOMBO_WIDTH * 7; - - for (uint i = 0; i < INVENTORYITEM_HEIGHT; i++) { - memcpy(d, s, INVENTORYITEM_WIDTH); - - s += INVENTORYITEM_PITCH; - d += MOUSECOMBO_WIDTH; - } - + _inventoryRenderer->drawItem(name, v8 + 7 * MOUSECOMBO_WIDTH + 7, MOUSECOMBO_WIDTH); _system->setMouseCursor(v8, MOUSECOMBO_WIDTH, MOUSECOMBO_HEIGHT, 0, 0, 0); - } @@ -240,17 +220,8 @@ int Parallaction_ns::go() { _globalTable = _disk->loadTable("global"); - guiStart(); + startGui(); - if (_engineFlags & kEngineQuit) - return 0; - - changeLocation(_location._name); - - if (_engineFlags & kEngineQuit) - return 0; - - _input->_inputMode = Input::kInputModeGame; while ((_engineFlags & kEngineQuit) == 0) { runGame(); } @@ -300,6 +271,9 @@ void Parallaction_ns::runPendingZones() { void Parallaction_ns::changeLocation(char *location) { debugC(1, kDebugExec, "changeLocation(%s)", location); + MouseTriState oldMouseState = _input->getMouseState(); + _input->setMouseState(MOUSE_DISABLED); + _soundMan->playLocationMusic(location); _input->stopHovering(); @@ -307,9 +281,7 @@ void Parallaction_ns::changeLocation(char *location) { _zoneTrap = nullZonePtr; - if (_engineFlags & kEngineBlockInput) { - setArrowCursor(); - } + setArrowCursor(); _gfx->showGfxObj(_char._ani->gfxobj, false); _location._animations.remove(_char._ani); @@ -323,7 +295,9 @@ void Parallaction_ns::changeLocation(char *location) { showSlide(locname.slide()); uint id = _gfx->createLabel(_menuFont, _location._slideText[0], 1); _gfx->showLabel(id, CENTER_LABEL_HORIZONTAL, 14); - _input->waitUntilLeftClick(); + _gfx->updateScreen(); + + _input->waitForButtonEvent(kMouseLeftUp); _gfx->freeLabels(); freeBackground(); } @@ -367,10 +341,9 @@ void Parallaction_ns::changeLocation(char *location) { if (_location._hasSound) _soundMan->playSfx(_location._soundFile, 0, true); - debugC(1, kDebugExec, "changeLocation() done"); - - return; + _input->setMouseState(oldMouseState); + debugC(1, kDebugExec, "changeLocation() done"); } diff --git a/engines/parallaction/parser.h b/engines/parallaction/parser.h index 1541fb89b2..488d437deb 100644 --- a/engines/parallaction/parser.h +++ b/engines/parallaction/parser.h @@ -171,7 +171,7 @@ protected: DECLARE_UNQUALIFIED_COMMAND_PARSER(animation); DECLARE_UNQUALIFIED_COMMAND_PARSER(zone); DECLARE_UNQUALIFIED_COMMAND_PARSER(location); - DECLARE_UNQUALIFIED_COMMAND_PARSER(drop); + DECLARE_UNQUALIFIED_COMMAND_PARSER(invObject); DECLARE_UNQUALIFIED_COMMAND_PARSER(call); DECLARE_UNQUALIFIED_COMMAND_PARSER(simple); DECLARE_UNQUALIFIED_COMMAND_PARSER(move); @@ -192,8 +192,8 @@ protected: Question *parseQuestion(); void parseZone(ZoneList &list, char *name); - void parseZoneTypeBlock(ZonePtr z); - void parseWalkNodes(WalkNodeList &list); + virtual void parseZoneTypeBlock(ZonePtr z); + void parsePointList(PointList &list); void parseAnimation(AnimationList &list, char *name); void parseCommands(CommandList&); void parseCommandFlags(); @@ -284,6 +284,9 @@ protected: DECLARE_UNQUALIFIED_ANIM_PARSER(moveto); DECLARE_UNQUALIFIED_ANIM_PARSER(endanimation); + virtual void parseZoneTypeBlock(ZonePtr z); + void parsePathData(ZonePtr z); + public: LocationParser_br(Parallaction_br *vm) : LocationParser_ns((Parallaction_ns*)vm), _vm(vm) { } @@ -307,6 +310,7 @@ protected: Parser *_parser; Parallaction_ns *_vm; + Script *_script; ProgramPtr _program; diff --git a/engines/parallaction/parser_br.cpp b/engines/parallaction/parser_br.cpp index defc917a72..3ba2beb288 100644 --- a/engines/parallaction/parser_br.cpp +++ b/engines/parallaction/parser_br.cpp @@ -442,7 +442,7 @@ DECLARE_LOCATION_PARSER(redundant) { DECLARE_LOCATION_PARSER(character) { debugC(7, kDebugParser, "LOCATION_PARSER(character) "); - ctxt.characterName = strdup(_tokens[0]); + ctxt.characterName = strdup(_tokens[1]); } @@ -750,6 +750,67 @@ DECLARE_ZONE_PARSER(type) { _parser->popTables(); } +void LocationParser_br::parsePathData(ZonePtr z) { + + PathData *data = new PathData; + + do { + + if (!scumm_stricmp("zone", _tokens[0])) { + int id = atoi(_tokens[1]); + parsePointList(data->_lists[id]); + data->_numLists++; + } + + _script->readLineToken(true); + } while (scumm_stricmp("endzone", _tokens[0])); + + z->u.path = data; +} + +void LocationParser_br::parseZoneTypeBlock(ZonePtr z) { + debugC(7, kDebugParser, "parseZoneTypeBlock(name: %s, type: %x)", z->_name, z->_type); + + switch (z->_type & 0xFFFF) { + case kZoneExamine: // examine Zone alloc + parseExamineData(z); + break; + + case kZoneDoor: // door Zone alloc + parseDoorData(z); + break; + + case kZoneGet: // get Zone alloc + parseGetData(z); + break; + + case kZoneMerge: // merge Zone alloc + parseMergeData(z); + break; + + case kZoneHear: // hear Zone alloc + parseHearData(z); + break; + + case kZoneSpeak: // speak Zone alloc + parseSpeakData(z); + break; + + // BRA specific zone + case kZonePath: + parsePathData(z); + break; + + default: + // eats up 'ENDZONE' line for unprocessed zone types + _script->readLineToken(true); + break; + } + + debugC(7, kDebugParser, "parseZoneTypeBlock() done"); + + return; +} DECLARE_ANIM_PARSER(file) { debugC(7, kDebugParser, "ANIM_PARSER(file) "); @@ -983,7 +1044,7 @@ void LocationParser_br::init() { COMMAND_PARSER(zone); // off COMMAND_PARSER(call); COMMAND_PARSER(flags); // toggle - COMMAND_PARSER(drop); + COMMAND_PARSER(invObject); // drop COMMAND_PARSER(simple); // quit COMMAND_PARSER(move); COMMAND_PARSER(zone); // stop @@ -991,7 +1052,7 @@ void LocationParser_br::init() { COMMAND_PARSER(string); // followme COMMAND_PARSER(simple); // onmouse COMMAND_PARSER(simple); // offmouse - COMMAND_PARSER(drop); // add + COMMAND_PARSER(invObject); // add COMMAND_PARSER(zone); // leave COMMAND_PARSER(math); // inc COMMAND_PARSER(math); // dec diff --git a/engines/parallaction/parser_ns.cpp b/engines/parallaction/parser_ns.cpp index 2f4d2df776..ad0f714fdc 100644 --- a/engines/parallaction/parser_ns.cpp +++ b/engines/parallaction/parser_ns.cpp @@ -299,6 +299,7 @@ void LocationParser_ns::parseAnimation(AnimationList &list, char *name) { AnimationPtr a(new Animation); strncpy(a->_name, name, ZONENAME_LENGTH); + a->_flags |= kFlagsIsAnimation; list.push_front(AnimationPtr(a)); @@ -658,7 +659,7 @@ DECLARE_COMMAND_PARSER(location) { } -DECLARE_COMMAND_PARSER(drop) { +DECLARE_COMMAND_PARSER(invObject) { debugC(7, kDebugParser, "COMMAND_PARSER(drop) "); createCommand(_parser->_lookup); @@ -1011,7 +1012,7 @@ DECLARE_LOCATION_PARSER(disk) { DECLARE_LOCATION_PARSER(nodes) { debugC(7, kDebugParser, "LOCATION_PARSER(nodes) "); - parseWalkNodes(_vm->_location._walkNodes); + parsePointList(_vm->_location._walkPoints); } @@ -1124,26 +1125,20 @@ void LocationParser_ns::parse(Script *script) { resolveCommandForwards(); } -void LocationParser_ns::parseWalkNodes(WalkNodeList &list) { - debugC(5, kDebugParser, "parseWalkNodes()"); +void LocationParser_ns::parsePointList(PointList &list) { + debugC(5, kDebugParser, "parsePointList()"); _script->readLineToken(true); while (scumm_stricmp(_tokens[0], "ENDNODES")) { if (!scumm_stricmp(_tokens[0], "COORD")) { - - WalkNodePtr v4(new WalkNode( - atoi(_tokens[1]), - atoi(_tokens[2]) - )); - - list.push_front(v4); + list.push_front(Common::Point(atoi(_tokens[1]), atoi(_tokens[2]))); } _script->readLineToken(true); } - debugC(5, kDebugParser, "parseWalkNodes() done"); + debugC(5, kDebugParser, "parsePointList() done"); return; } @@ -1203,7 +1198,7 @@ void LocationParser_ns::init() { COMMAND_PARSER(zone); // off COMMAND_PARSER(call); // call COMMAND_PARSER(flags); // toggle - COMMAND_PARSER(drop); // drop + COMMAND_PARSER(invObject); // drop COMMAND_PARSER(simple); // quit COMMAND_PARSER(move); // move COMMAND_PARSER(zone); // stop @@ -1397,7 +1392,7 @@ void LocationParser_ns::parseZone(ZoneList &list, char *name) { list.push_front(z); _parser->pushTables(&_locationZoneParsers, _locationZoneStmt); - + return; } diff --git a/engines/parallaction/walk.cpp b/engines/parallaction/walk.cpp index a717b615e6..bf8f423fd5 100644 --- a/engines/parallaction/walk.cpp +++ b/engines/parallaction/walk.cpp @@ -28,26 +28,42 @@ namespace Parallaction { + +#define IS_PATH_CLEAR(x,y) _vm->_pathBuffer->getValue((x), (y)) + inline byte PathBuffer::getValue(uint16 x, uint16 y) { byte m = data[(x >> 3) + y * internalWidth]; - uint n = (_vm->getPlatform() == Common::kPlatformPC) ? (x & 7) : (7 - (x & 7)); - return ((1 << n) & m) >> n; + uint bit = 0; + switch (_vm->getGameType()) { + case GType_Nippon: + bit = (_vm->getPlatform() == Common::kPlatformPC) ? (x & 7) : (7 - (x & 7)); + break; + + case GType_BRA: + // Amiga and PC versions pack the path bits the same way in BRA + bit = 7 - (x & 7); + break; + + default: + error("path mask not yet implemented for this game type"); + } + return ((1 << bit) & m) >> bit; } // adjusts position towards nearest walkable point // -void PathBuilder::correctPathPoint(Common::Point &to) { +void PathBuilder_NS::correctPathPoint(Common::Point &to) { - if (_vm->_pathBuffer->getValue(to.x, to.y)) return; + if (IS_PATH_CLEAR(to.x, to.y)) return; int16 right = to.x; int16 left = to.x; do { right++; - } while ((_vm->_pathBuffer->getValue(right, to.y) == 0) && (right < _vm->_pathBuffer->w)); + } while (!IS_PATH_CLEAR(right, to.y) && (right < _vm->_pathBuffer->w)); do { left--; - } while ((_vm->_pathBuffer->getValue(left, to.y) == 0) && (left > 0)); + } while (!IS_PATH_CLEAR(left, to.y) && (left > 0)); right = (right == _vm->_pathBuffer->w) ? 1000 : right - to.x; left = (left == 0) ? 1000 : to.x - left; @@ -56,10 +72,10 @@ void PathBuilder::correctPathPoint(Common::Point &to) { int16 bottom = to.y; do { top--; - } while ((_vm->_pathBuffer->getValue(to.x, top) == 0) && (top > 0)); + } while (!IS_PATH_CLEAR(to.x, top) && (top > 0)); do { bottom++; - } while ((_vm->_pathBuffer->getValue(to.x, bottom) == 0) && (bottom < _vm->_pathBuffer->h)); + } while (!IS_PATH_CLEAR(to.x, bottom) && (bottom < _vm->_pathBuffer->h)); top = (top == 0) ? 1000 : to.y - top; bottom = (bottom == _vm->_pathBuffer->h) ? 1000 : bottom - to.y; @@ -84,7 +100,7 @@ void PathBuilder::correctPathPoint(Common::Point &to) { } -uint32 PathBuilder::buildSubPath(const Common::Point& pos, const Common::Point& stop) { +uint32 PathBuilder_NS::buildSubPath(const Common::Point& pos, const Common::Point& stop) { uint32 v28 = 0; uint32 v2C = 0; @@ -97,16 +113,15 @@ uint32 PathBuilder::buildSubPath(const Common::Point& pos, const Common::Point& while (true) { - WalkNodeList::iterator nearest = _vm->_location._walkNodes.end(); - WalkNodeList::iterator locNode = _vm->_location._walkNodes.begin(); + PointList::iterator nearest = _vm->_location._walkPoints.end(); + PointList::iterator locNode = _vm->_location._walkPoints.begin(); // scans location path nodes searching for the nearest Node // which can't be farther than the target position // otherwise no _closest_node is selected - while (locNode != _vm->_location._walkNodes.end()) { + while (locNode != _vm->_location._walkPoints.end()) { - Common::Point v8; - (*locNode)->getPoint(v8); + Common::Point v8 = *locNode; v2C = v8.sqrDist(stop); v28 = v8.sqrDist(v20); @@ -118,80 +133,59 @@ uint32 PathBuilder::buildSubPath(const Common::Point& pos, const Common::Point& locNode++; } - if (nearest == _vm->_location._walkNodes.end()) break; + if (nearest == _vm->_location._walkPoints.end()) break; - (*nearest)->getPoint(v20); + v20 = *nearest; v34 = v30 = v20.sqrDist(stop); - _subPath.push_back(WalkNodePtr(new WalkNode(**nearest))); + _subPath.push_back(*nearest); } return v34; } -#if 0 -void printNodes(WalkNodeList *list, const char* text) { - printf("%s\n-------------------\n", text); - for (WalkNodeList::iterator it = list->begin(); it != list->end(); it++) - printf("node [%p] (%i, %i)\n", *it, (*it)->_x, (*it)->_y); - return; -} -#endif // // x, y: mouse click (foot) coordinates // -WalkNodeList *PathBuilder::buildPath(uint16 x, uint16 y) { +void PathBuilder_NS::buildPath(uint16 x, uint16 y) { debugC(1, kDebugWalk, "PathBuilder::buildPath to (%i, %i)", x, y); + _ch->_walkPath.clear(); + Common::Point to(x, y); correctPathPoint(to); debugC(1, kDebugWalk, "found closest path point at (%i, %i)", to.x, to.y); - WalkNodePtr v48(new WalkNode(to.x, to.y)); - WalkNodePtr v44 = v48; + Common::Point v48(to); + Common::Point v44(to); - uint16 v38 = walkFunc1(to.x, to.y, v44); + uint16 v38 = walkFunc1(to, v44); if (v38 == 1) { // destination directly reachable debugC(1, kDebugWalk, "direct move to (%i, %i)", to.x, to.y); - - _list = new WalkNodeList; - _list->push_back(v48); - return _list; + _ch->_walkPath.push_back(v48); + return; } // path is obstructed: look for alternative - _list = new WalkNodeList; - _list->push_back(v48); -#if 0 - printNodes(_list, "start"); -#endif - - Common::Point stop(v48->_x, v48->_y); + _ch->_walkPath.push_back(v48); Common::Point pos; - _vm->_char.getFoot(pos); + _ch->getFoot(pos); - uint32 v34 = buildSubPath(pos, stop); + uint32 v34 = buildSubPath(pos, v48); if (v38 != 0 && v34 > v38) { // no alternative path (gap?) - _list->clear(); - _list->push_back(v44); - return _list; + _ch->_walkPath.clear(); + _ch->_walkPath.push_back(v44); + return; } - _list->insert(_list->begin(), _subPath.begin(), _subPath.end()); -#if 0 - printNodes(_list, "first segment"); -#endif + _ch->_walkPath.insert(_ch->_walkPath.begin(), _subPath.begin(), _subPath.end()); - (*_list->begin())->getPoint(stop); - buildSubPath(pos, stop); - _list->insert(_list->begin(), _subPath.begin(), _subPath.end()); -#if 0 - printNodes(_list, "complete"); -#endif + buildSubPath(pos, *_ch->_walkPath.begin()); + _ch->_walkPath.insert(_ch->_walkPath.begin(), _subPath.begin(), _subPath.end()); - return _list; + return; } @@ -202,23 +196,23 @@ WalkNodeList *PathBuilder::buildPath(uint16 x, uint16 y) { // 1 : Point reachable in a straight line // other values: square distance to target (point not reachable in a straight line) // -uint16 PathBuilder::walkFunc1(int16 x, int16 y, WalkNodePtr Node) { +uint16 PathBuilder_NS::walkFunc1(const Common::Point &to, Common::Point& node) { - Common::Point arg(x, y); + Common::Point arg(to); - Common::Point v4(0, 0); + Common::Point v4; Common::Point foot; - _vm->_char.getFoot(foot); + _ch->getFoot(foot); Common::Point v8(foot); while (foot != arg) { - if (foot.x < x && _vm->_pathBuffer->getValue(foot.x + 1, foot.y) != 0) foot.x++; - if (foot.x > x && _vm->_pathBuffer->getValue(foot.x - 1, foot.y) != 0) foot.x--; - if (foot.y < y && _vm->_pathBuffer->getValue(foot.x, foot.y + 1) != 0) foot.y++; - if (foot.y > y && _vm->_pathBuffer->getValue(foot.x, foot.y - 1) != 0) foot.y--; + if (foot.x < to.x && IS_PATH_CLEAR(foot.x + 1, foot.y)) foot.x++; + if (foot.x > to.x && IS_PATH_CLEAR(foot.x - 1, foot.y)) foot.x--; + if (foot.y < to.y && IS_PATH_CLEAR(foot.x, foot.y + 1)) foot.y++; + if (foot.y > to.y && IS_PATH_CLEAR(foot.x, foot.y - 1)) foot.y--; if (foot == v8 && foot != arg) { @@ -228,10 +222,10 @@ uint16 PathBuilder::walkFunc1(int16 x, int16 y, WalkNodePtr Node) { while (foot != arg) { - if (foot.x < x && _vm->_pathBuffer->getValue(foot.x + 1, foot.y) == 0) foot.x++; - if (foot.x > x && _vm->_pathBuffer->getValue(foot.x - 1, foot.y) == 0) foot.x--; - if (foot.y < y && _vm->_pathBuffer->getValue(foot.x, foot.y + 1) == 0) foot.y++; - if (foot.y > y && _vm->_pathBuffer->getValue(foot.x, foot.y - 1) == 0) foot.y--; + if (foot.x < to.x && !IS_PATH_CLEAR(foot.x + 1, foot.y)) foot.x++; + if (foot.x > to.x && !IS_PATH_CLEAR(foot.x - 1, foot.y)) foot.x--; + if (foot.y < to.y && !IS_PATH_CLEAR(foot.x, foot.y + 1)) foot.y++; + if (foot.y > to.y && !IS_PATH_CLEAR(foot.x, foot.y - 1)) foot.y--; if (foot == v8 && foot != arg) return 0; @@ -239,10 +233,8 @@ uint16 PathBuilder::walkFunc1(int16 x, int16 y, WalkNodePtr Node) { v8 = foot; } - Node->_x = v4.x; - Node->_y = v4.y; - - return (x - v4.x) * (x - v4.x) + (y - v4.y) * (y - v4.y); + node = v4; + return v4.sqrDist(to); } v8 = foot; @@ -253,21 +245,21 @@ uint16 PathBuilder::walkFunc1(int16 x, int16 y, WalkNodePtr Node) { return 1; } -void Parallaction::clipMove(Common::Point& pos, const Common::Point& to) { +void PathWalker_NS::clipMove(Common::Point& pos, const Common::Point& to) { - if ((pos.x < to.x) && (pos.x < _pathBuffer->w) && (_pathBuffer->getValue(pos.x + 2, pos.y) != 0)) { + if ((pos.x < to.x) && (pos.x < _vm->_pathBuffer->w) && IS_PATH_CLEAR(pos.x + 2, pos.y)) { pos.x = (pos.x + 2 < to.x) ? pos.x + 2 : to.x; } - if ((pos.x > to.x) && (pos.x > 0) && (_pathBuffer->getValue(pos.x - 2, pos.y) != 0)) { + if ((pos.x > to.x) && (pos.x > 0) && IS_PATH_CLEAR(pos.x - 2, pos.y)) { pos.x = (pos.x - 2 > to.x) ? pos.x - 2 : to.x; } - if ((pos.y < to.y) && (pos.y < _pathBuffer->h) && (_pathBuffer->getValue(pos.x, pos.y + 2) != 0)) { + if ((pos.y < to.y) && (pos.y < _vm->_pathBuffer->h) && IS_PATH_CLEAR(pos.x, pos.y + 2)) { pos.y = (pos.y + 2 <= to.y) ? pos.y + 2 : to.y; } - if ((pos.y > to.y) && (pos.y > 0) && (_pathBuffer->getValue(pos.x, pos.y - 2) != 0)) { + if ((pos.y > to.y) && (pos.y > 0) && IS_PATH_CLEAR(pos.x, pos.y - 2)) { pos.y = (pos.y - 2 >= to.y) ? pos.y - 2 : to.y; } @@ -275,85 +267,81 @@ void Parallaction::clipMove(Common::Point& pos, const Common::Point& to) { } -void Parallaction::checkDoor(const Common::Point &foot) { +void PathWalker_NS::checkDoor(const Common::Point &foot) { - ZonePtr z = hitZone(kZoneDoor, foot.x, foot.y); + ZonePtr z = _vm->hitZone(kZoneDoor, foot.x, foot.y); if (z) { if ((z->_flags & kFlagsClosed) == 0) { - _location._startPosition = z->u.door->_startPos; - _location._startFrame = z->u.door->_startFrame; - scheduleLocationSwitch(z->u.door->_location); - _zoneTrap = nullZonePtr; + _vm->_location._startPosition = z->u.door->_startPos; + _vm->_location._startFrame = z->u.door->_startFrame; + _vm->scheduleLocationSwitch(z->u.door->_location); + _vm->_zoneTrap = nullZonePtr; } else { - _cmdExec->run(z->_commands, z); + _vm->_cmdExec->run(z->_commands, z); } } - z = hitZone(kZoneTrap, foot.x, foot.y); + z = _vm->hitZone(kZoneTrap, foot.x, foot.y); if (z) { - setLocationFlags(kFlagsEnter); - _cmdExec->run(z->_commands, z); - clearLocationFlags(kFlagsEnter); - _zoneTrap = z; + _vm->setLocationFlags(kFlagsEnter); + _vm->_cmdExec->run(z->_commands, z); + _vm->clearLocationFlags(kFlagsEnter); + _vm->_zoneTrap = z; } else - if (_zoneTrap) { - setLocationFlags(kFlagsExit); - _cmdExec->run(_zoneTrap->_commands, _zoneTrap); - clearLocationFlags(kFlagsExit); - _zoneTrap = nullZonePtr; + if (_vm->_zoneTrap) { + _vm->setLocationFlags(kFlagsExit); + _vm->_cmdExec->run(_vm->_zoneTrap->_commands, _vm->_zoneTrap); + _vm->clearLocationFlags(kFlagsExit); + _vm->_zoneTrap = nullZonePtr; } } -void Parallaction::finalizeWalk(Character &character) { +void PathWalker_NS::finalizeWalk() { _engineFlags &= ~kEngineWalking; Common::Point foot; - character.getFoot(foot); + _ch->getFoot(foot); checkDoor(foot); - delete character._walkPath; - character._walkPath = 0; + _ch->_walkPath.clear(); } -void Parallaction_ns::walk(Character &character) { +void PathWalker_NS::walk() { if ((_engineFlags & kEngineWalking) == 0) { return; } Common::Point curPos; - character.getFoot(curPos); + _ch->getFoot(curPos); // update target, if previous was reached - WalkNodeList::iterator it = character._walkPath->begin(); - if (it != character._walkPath->end()) { - if ((*it)->_x == curPos.x && (*it)->_y == curPos.y) { - debugC(1, kDebugWalk, "walk reached node (%i, %i)", (*it)->_x, (*it)->_y); - it = character._walkPath->erase(it); + PointList::iterator it = _ch->_walkPath.begin(); + if (it != _ch->_walkPath.end()) { + if (*it == curPos) { + debugC(1, kDebugWalk, "walk reached node (%i, %i)", (*it).x, (*it).y); + it = _ch->_walkPath.erase(it); } } // advance character towards the target Common::Point targetPos; - if (it == character._walkPath->end()) { + if (it == _ch->_walkPath.end()) { debugC(1, kDebugWalk, "walk reached last node"); - finalizeWalk(character); + finalizeWalk(); targetPos = curPos; } else { - if (*it) { - // targetPos is saved to help setting character direction - targetPos.x = (*it)->_x; - targetPos.y = (*it)->_y; - } + // targetPos is saved to help setting character direction + targetPos = *it; Common::Point newPos(curPos); clipMove(newPos, targetPos); - character.setFoot(newPos); + _ch->setFoot(newPos); if (newPos == curPos) { debugC(1, kDebugWalk, "walk was blocked by an unforeseen obstacle"); - finalizeWalk(character); + finalizeWalk(); targetPos = newPos; // when walking is interrupted, targetPos must be hacked so that a still frame can be selected } } @@ -364,25 +352,283 @@ void Parallaction_ns::walk(Character &character) { // from curPos to newPos is prone to abrutply change in direction, thus making the // code select 'too different' frames when walking diagonally against obstacles, // and yielding an annoying shaking effect in the character. - character.updateDirection(curPos, targetPos); + _ch->updateDirection(curPos, targetPos); } -WalkNode::WalkNode() : _x(0), _y(0) { + +PathBuilder_NS::PathBuilder_NS(Character *ch) : PathBuilder(ch), _list(0) { } -WalkNode::WalkNode(int16 x, int16 y) : _x(x), _y(y) { + +bool PathBuilder_BR::directPathExists(const Common::Point &from, const Common::Point &to) { + + Common::Point copy(from); + Common::Point p(copy); + + while (p != to) { + + if (p.x < to.x && IS_PATH_CLEAR(p.x + 1, p.y)) p.x++; + if (p.x > to.x && IS_PATH_CLEAR(p.x - 1, p.y)) p.x--; + if (p.y < to.y && IS_PATH_CLEAR(p.x, p.y + 1)) p.y++; + if (p.y > to.y && IS_PATH_CLEAR(p.x, p.y - 1)) p.y--; + + if (p == copy && p != to) { + return false; + } + + copy = p; + } + + return true; } -WalkNode::WalkNode(const WalkNode& w) : _x(w._x), _y(w._y) { +void PathBuilder_BR::buildPath(uint16 x, uint16 y) { + Common::Point foot; + _ch->getFoot(foot); + + debugC(1, kDebugWalk, "buildPath: from (%i, %i) to (%i, %i)", foot.x, foot.y, x, y); + _ch->_walkPath.clear(); + + // look for easy path first + Common::Point dest(x, y); + if (directPathExists(foot, dest)) { + _ch->_walkPath.push_back(dest); + debugC(3, kDebugWalk, "buildPath: direct path found"); + return; + } + + // look for short circuit cases + ZonePtr z0 = _vm->hitZone(kZonePath, x, y); + if (!z0) { + _ch->_walkPath.push_back(dest); + debugC(3, kDebugWalk, "buildPath: corner case 0"); + return; + } + ZonePtr z1 = _vm->hitZone(kZonePath, foot.x, foot.y); + if (!z1 || z1 == z0) { + _ch->_walkPath.push_back(dest); + debugC(3, kDebugWalk, "buildPath: corner case 1"); + return; + } + + // build complex path + int id = atoi(z0->_name); + + if (z1->u.path->_lists[id].empty()) { + _ch->_walkPath.clear(); + debugC(3, kDebugWalk, "buildPath: no path"); + return; + } + + PointList::iterator b = z1->u.path->_lists[id].begin(); + PointList::iterator e = z1->u.path->_lists[id].end(); + for ( ; b != e; b++) { + _ch->_walkPath.push_front(*b); + } + _ch->_walkPath.push_back(dest); + debugC(3, kDebugWalk, "buildPath: complex path"); + + return; +} + +PathBuilder_BR::PathBuilder_BR(Character *ch) : PathBuilder(ch) { +} + +void PathWalker_BR::finalizeWalk() { + _engineFlags &= ~kEngineWalking; + _first = true; + _fieldC = 1; + + Common::Point foot; + _ch->getFoot(foot); + + ZonePtr z = _vm->hitZone(kZoneDoor, foot.x, foot.y); + if (z && ((z->_flags & kFlagsClosed) == 0)) { + _vm->_location._startPosition = z->u.door->_startPos; // foot pos + _vm->_location._startFrame = z->u.door->_startFrame; + +#if 0 + // TODO: implement working follower. Must find out a location in which the code is + // used and which is stable enough. + _followerFootInit.x = -1; + if (_follower && z->u.door->startPos2.x != -1) { + _followerFootInit.x = z->u.door->startPos2.x; // foot pos + _followerFootInit.y = z->u.door->startPos2.y; // foot pos + } + _followerFootInit.z = -1; + if (_follower && z->u.door->startPos2.z != -1) { + _followerFootInit.z = z->u.door->startPos2.z; // foot pos + } +#endif + + _vm->scheduleLocationSwitch(z->u.door->_location); + _vm->_cmdExec->run(z->_commands, z); + } + +#if 0 + // TODO: Input::walkTo must be extended to support destination frame in addition to coordinates + // TODO: the frame argument must be passed to PathWalker through PathBuilder, so probably + // a merge between the two Path managers is the right solution + if (_engineFlags & FINAL_WALK_FRAME) { // this flag is set in readInput() + _engineFlags &= ~FINAL_WALK_FRAME; + _char.ani->_frame = _moveToF; // from readInput()... + } else { + _char.ani->_frame = _dirFrame; // from walk() + } + _char.setFoot(foot); +#endif + + _ch->_ani->_frame = _dirFrame; // temporary solution + +#if 0 + // TODO: support scrolling ;) + if (foot.x > _gfx->hscroll + 600) _gfx->scrollRight(78); + if (foot.x < _gfx->hscroll + 40) _gfx->scrollLeft(78); + if (foot.y > 350) _gfx->scrollDown(100); + if (foot.y < 80) _gfx->scrollUp(100); +#endif + + return; } -void WalkNode::getPoint(Common::Point &p) const { - p.x = _x; - p.y = _y; + +void PathWalker_BR::walk() { + if ((_engineFlags & kEngineWalking) == 0) { + return; + } + +#if 0 + // TODO: support delays in walking. This requires extending Input::walkIo(). + if (ch._walkDelay > 0) { + ch._walkDelay--; + if (ch._walkDelay == 0 && _ch._ani->_scriptName) { + // stop script and reset + _ch._ani->_flags &= ~kFlagsActing; + Script *script = findScript(_ch._ani->_scriptName); + script->_nextCommand = script->firstCommand; + } + return; + } +#endif + + GfxObj *obj = _ch->_ani->gfxobj; + + Common::Rect rect; + obj->getRect(_ch->_ani->_frame, rect); + + uint scale; + if (rect.bottom > _vm->_location._zeta0) { + scale = 100; + } else + if (rect.bottom < _vm->_location._zeta1) { + scale = _vm->_location._zeta2; + } else { + scale = _vm->_location._zeta2 + ((rect.bottom - _vm->_location._zeta1) * (100 - _vm->_location._zeta2)) / (_vm->_location._zeta0 - _vm->_location._zeta1); + } + int xStep = (scale * 16) / 100 + 1; + int yStep = (scale * 10) / 100 + 1; + + debugC(9, kDebugWalk, "calculated step: (%i, %i)\n", xStep, yStep); + + if (_fieldC == 0) { + _ch->_walkPath.erase(_ch->_walkPath.begin()); + + if (_ch->_walkPath.empty()) { + finalizeWalk(); + debugC(3, kDebugWalk, "PathWalker_BR::walk, case 0\n"); + return; + } else { + debugC(3, kDebugWalk, "PathWalker_BR::walk, moving to next node\n"); + } + } + + _ch->getFoot(_startFoot); + + _fieldC = 0; + _step++; + _step %= 8; + + int walkFrame = _step; + _dirFrame = 0; + Common::Point newpos(_startFoot), delta; + + Common::Point p(*_ch->_walkPath.begin()); + + if (_startFoot.y < p.y && _startFoot.y < 400 && IS_PATH_CLEAR(_startFoot.x, yStep + _startFoot.y)) { + if (yStep + _startFoot.y <= p.y) { + _fieldC = 1; + delta.y = yStep; + newpos.y = yStep + _startFoot.y; + } else { + delta.y = p.y - _startFoot.y; + newpos.y = p.y; + } + _dirFrame = 9; + } else + if (_startFoot.y > p.y && _startFoot.y > 0 && IS_PATH_CLEAR(_startFoot.x, _startFoot.y - yStep)) { + if (_startFoot.y - yStep >= p.y) { + _fieldC = 1; + delta.y = yStep; + newpos.y = _startFoot.y - yStep; + } else { + delta.y = _startFoot.y - p.y; + newpos.y = p.y; + } + _dirFrame = 0; + } + + if (_startFoot.x < p.x && _startFoot.x < 640 && IS_PATH_CLEAR(_startFoot.x + xStep, _startFoot.y)) { + if (_startFoot.x + xStep <= p.x) { + _fieldC = 1; + delta.x = xStep; + newpos.x = xStep + _startFoot.x; + } else { + delta.x = p.x - _startFoot.x; + newpos.x = p.x; + } + if (delta.y < delta.x) { + _dirFrame = 18; // right + } + } else + if (_startFoot.x > p.x && _startFoot.x > 0 && IS_PATH_CLEAR(_startFoot.x - xStep, _startFoot.y)) { + if (_startFoot.x - xStep >= p.x) { + _fieldC = 1; + delta.x = xStep; + newpos.x = _startFoot.x - xStep; + } else { + delta.x = _startFoot.x - p.x; + newpos.x = p.x; + } + if (delta.y < delta.x) { + _dirFrame = 27; // left + } + } + + debugC(9, kDebugWalk, "foot (%i, %i) dest (%i, %i) deltas = %i/%i \n", _startFoot.x, _startFoot.y, p.x, p.y, delta.x, delta.y); + + if (_fieldC) { + debugC(9, kDebugWalk, "PathWalker_BR::walk, foot moved from (%i, %i) to (%i, %i)\n", _startFoot.x, _startFoot.y, newpos.x, newpos.y); + _ch->_ani->_frame = walkFrame + _dirFrame + 1; + _startFoot.x = newpos.x; + _startFoot.y = newpos.y; + _ch->setFoot(_startFoot); + _ch->_ani->_z = newpos.y; + } + + if (_fieldC || !_ch->_walkPath.empty()) { +// checkTrap(); + debugC(3, kDebugWalk, "PathWalker_BR::walk, case 1\n"); + return; + } + + debugC(3, kDebugWalk, "PathWalker_BR::walk, case 2\n"); + finalizeWalk(); + return; } -PathBuilder::PathBuilder(AnimationPtr anim) : _anim(anim), _list(0) { +PathWalker_BR::PathWalker_BR(Character *ch) : PathWalker(ch), _fieldC(1), _first(true) { + } diff --git a/engines/parallaction/walk.h b/engines/parallaction/walk.h index 788a6e1375..8d21e5ebbd 100644 --- a/engines/parallaction/walk.h +++ b/engines/parallaction/walk.h @@ -29,43 +29,89 @@ #include "common/ptr.h" #include "common/list.h" +#include "parallaction/objects.h" + + namespace Parallaction { -struct Animation; +struct Character; + +class PathBuilder { -struct WalkNode { - int16 _x; - int16 _y; +protected: + Character *_ch; public: - WalkNode(); - WalkNode(int16 x, int16 y); - WalkNode(const WalkNode& w); + PathBuilder(Character *ch) : _ch(ch) { } + virtual ~PathBuilder() { } - void getPoint(Common::Point &p) const; + virtual void buildPath(uint16 x, uint16 y) = 0; }; -typedef Common::SharedPtr<WalkNode> WalkNodePtr; -typedef Common::List<WalkNodePtr> WalkNodeList; +class PathBuilder_NS : public PathBuilder { -class PathBuilder { - - AnimationPtr _anim; - - WalkNodeList *_list; - WalkNodeList _subPath; + PointList *_list; + PointList _subPath; void correctPathPoint(Common::Point &to); uint32 buildSubPath(const Common::Point& pos, const Common::Point& stop); - uint16 walkFunc1(int16 x, int16 y, WalkNodePtr Node); + uint16 walkFunc1(const Common::Point &to, Common::Point& node); public: - PathBuilder(AnimationPtr anim); - WalkNodeList* buildPath(uint16 x, uint16 y); + PathBuilder_NS(Character *ch); + void buildPath(uint16 x, uint16 y); +}; + +class PathBuilder_BR : public PathBuilder { + + bool directPathExists(const Common::Point &from, const Common::Point &to); + +public: + PathBuilder_BR(Character *ch); + void buildPath(uint16 x, uint16 y); +}; + +class PathWalker { +protected: + Character *_ch; +public: + PathWalker(Character *ch) : _ch(ch) { } + virtual ~PathWalker() { } + virtual void walk() = 0; }; +class PathWalker_NS : public PathWalker { + + + void finalizeWalk(); + void clipMove(Common::Point& pos, const Common::Point& to); + void checkDoor(const Common::Point &foot); + +public: + PathWalker_NS(Character *ch) : PathWalker(ch) { } + void walk(); +}; + + +class PathWalker_BR : public PathWalker { + + + int _walkDelay; + int _fieldC; + Common::Point _startFoot; + bool _first; + int _step; + + int _dirFrame; + + void finalizeWalk(); + +public: + PathWalker_BR(Character *ch); + void walk(); +}; } |