/* ScummVM - Scumm Interpreter * Copyright (C) 2006 The ScummVM project * * 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 "common/stdafx.h" #include "common/events.h" #include "parallaction/parallaction.h" namespace Parallaction { #define SKIPPED_ANSWER 1000 #define MAX_BALLOON_WIDTH 130 #define MAX_PASSWORD_LENGTH 7 #define QUESTION_BALLOON_X 140 #define QUESTION_BALLOON_Y 10 #define QUESTION_CHARACTER_X 190 #define QUESTION_CHARACTER_Y 80 #define ANSWER_CHARACTER_X 10 #define ANSWER_CHARACTER_Y 80 void enterDialogue(); void exitDialogue(); int16 selectAnswer(Question *q, StaticCnv*); int16 getHoverAnswer(int16 x, int16 y, Question *q); int16 _answerBalloonX[10] = { 80, 120, 150, 150, 150, 0, 0, 0, 0, 0 }; int16 _answerBalloonY[10] = { 10, 70, 130, 0, 0, 0, 0, 0, 0, 0 }; int16 _answerBalloonW[10] = { 0 }; int16 _answerBalloonH[10] = { 0 }; Dialogue *Parallaction::parseDialogue(Script &script) { // printf("parseDialogue()\n"); uint16 num_questions = 0; uint16 v50[20]; Table _questions_names(20); Question *_questions[20]; for (uint16 _si = 0; _si < 20; _si++) { v50[_si] = 0; } fillBuffers(script, true); while (scumm_stricmp(_tokens[0], "enddialogue")) { if (scumm_stricmp(_tokens[0], "Question")) continue; _questions[num_questions] = new Dialogue; Dialogue *vB4 = _questions[num_questions]; _questions_names.addData(_tokens[1]); vB4->_text = parseDialogueString(script); // printf("Question: '%s'\n", vB4->_text); fillBuffers(script, true); vB4->_mood = atoi(_tokens[0]); uint16 _di = 0; fillBuffers(script, true); while (scumm_stricmp(_tokens[0], "endquestion")) { // parse answers vB4->_answers[_di] = new Answer; if (_tokens[1][0]) { Table* v60 = _localFlagNames; uint16 v56 = 1; if (!scumm_stricmp(_tokens[1], "global")) { v56 = 2; v60 = _globalTable; vB4->_answers[_di]->_yesFlags |= kFlagsGlobal; } do { if (!scumm_strnicmp(_tokens[v56], "no", 2)) { byte _al = v60->lookup(_tokens[v56]+2); vB4->_answers[_di]->_noFlags |= 1 << (_al - 1); } else { byte _al = v60->lookup(_tokens[v56]); vB4->_answers[_di]->_yesFlags |= 1 << (_al - 1); } v56++; } while (!scumm_stricmp(_tokens[v56++], "|")); } vB4->_answers[_di]->_text = parseDialogueString(script); // printf("answer[%i]: '%s'\n", _di, vB4->_answers[_di]); fillBuffers(script, true); vB4->_answers[_di]->_mood = atoi(_tokens[0]); vB4->_answers[_di]->_following._name = parseDialogueString(script); fillBuffers(script, true); if (!scumm_stricmp(_tokens[0], "commands")) { parseCommands(script, vB4->_answers[_di]->_commands); fillBuffers(script, true); } _di++; } fillBuffers(script, true); num_questions++; } for (uint16 _si = 0; _si _answers[v5A] == 0) continue; int16 v58 = _questions_names.lookup(_questions[_si]->_answers[v5A]->_following._name); free(_questions[_si]->_answers[v5A]->_following._name); if (v58 == -1) { _questions[_si]->_answers[v5A]->_following._question = 0; } else { _questions[_si]->_answers[v5A]->_following._question = _questions[v58-1]; if (v50[v58]) { _questions[_si]->_answers[v5A]->_mood |= 0x10; } v50[v58] = 1; } } } return _questions[0]; } char *Parallaction::parseDialogueString(Script &script) { char vC8[200]; char *vD0 = NULL; do { vD0 = script.readLine(vC8, 200); if (vD0 == 0) return NULL; vD0 = Common::ltrim(vD0); } while (strlen(vD0) == 0); vD0[strlen(vD0)-1] = '\0'; // deletes the trailing '0xA' // this is critical for Gfx::displayBalloonString to work properly char *vCC = (char*)malloc(strlen(vD0)+1); strcpy(vCC, vD0); return vCC; } uint16 Parallaction::askDialoguePassword(Dialogue *q, StaticCnv *face) { debugC(1, kDebugDialogue, "checkDialoguePassword()"); char password[100]; uint16 passwordLen = 0; while (true) { strcpy(password, "......."); _gfx->copyScreen(Gfx::kBitBack, Gfx::kBitFront); Common::Rect r(_answerBalloonW[0], _answerBalloonH[0]); r.moveTo(_answerBalloonX[0], _answerBalloonY[0]); _gfx->drawBalloon(r, 1); _gfx->displayWrappedString(q->_answers[0]->_text, _answerBalloonX[0], _answerBalloonY[0], MAX_BALLOON_WIDTH, 3); _gfx->flatBlitCnv(face, ANSWER_CHARACTER_X, ANSWER_CHARACTER_Y, Gfx::kBitFront); _gfx->displayBalloonString(_answerBalloonX[0] + 5, _answerBalloonY[0] + _answerBalloonH[0] - 15, "> ", 0); Common::Event e; while (e.kbd.ascii != 0xD && passwordLen < MAX_PASSWORD_LENGTH) { // FIXME: see comment for updateInput() if (!g_system->getEventManager()->pollEvent(e)) continue; if (e.type != Common::EVENT_KEYDOWN) continue; if (e.type == Common::EVENT_QUIT) g_system->quit(); if (!isdigit(e.kbd.ascii)) continue; password[passwordLen] = e.kbd.ascii; passwordLen++; password[passwordLen] = '\0'; _gfx->displayBalloonString(_answerBalloonX[0] + 5, _answerBalloonY[0] + _answerBalloonH[0] - 15, password, 0); _gfx->updateScreen(); g_system->delayMillis(20); } if ((!scumm_stricmp(_characterName, _doughName) && !scumm_strnicmp(password, "1732461", 7)) || (!scumm_stricmp(_characterName, _donnaName) && !scumm_strnicmp(password, "1622", 4)) || (!scumm_stricmp(_characterName, _dinoName) && !scumm_strnicmp(password, "179", 3))) { break; } } return 0; } bool _askPassword; bool Parallaction::displayAnswer(Dialogue *q, uint16 i) { uint32 v28 = _localFlags[_currentLocationIndex]; if (q->_answers[i]->_yesFlags & kFlagsGlobal) v28 = _commandFlags | kFlagsGlobal; // display suitable answers if (((q->_answers[i]->_yesFlags & v28) == q->_answers[i]->_yesFlags) && ((q->_answers[i]->_noFlags & ~v28) == q->_answers[i]->_noFlags)) { _gfx->getStringExtent(q->_answers[i]->_text, MAX_BALLOON_WIDTH, &_answerBalloonW[i], &_answerBalloonH[i]); Common::Rect r(_answerBalloonW[i], _answerBalloonH[i]); r.moveTo(_answerBalloonX[i], _answerBalloonY[i]); _gfx->drawBalloon(r, 1); _answerBalloonY[i+1] = 10 + _answerBalloonY[i] + _answerBalloonH[i]; _askPassword = _gfx->displayWrappedString(q->_answers[i]->_text, _answerBalloonX[i], _answerBalloonY[i], MAX_BALLOON_WIDTH, 3); return true; } return false; } bool Parallaction::displayAnswers(Dialogue *q) { bool displayed = false; uint16 i = 0; while (i < NUM_ANSWERS && q->_answers[i]) { if (displayAnswer(q, i)) { displayed = true; } else { _answerBalloonY[i+1] = _answerBalloonY[i]; _answerBalloonY[i] = SKIPPED_ANSWER; } i++; } _gfx->updateScreen(); return displayed; } void Parallaction::displayQuestion(Dialogue *q, Cnv *cnv) { int16 w = 0, h = 0; if (!scumm_stricmp(q->_text, "NULL")) return; StaticCnv face; face._width = cnv->_width; face._height = cnv->_height; face._data0 = cnv->getFramePtr(q->_mood & 0xF); face._data1 = NULL; // cnv->field_8[v60->_mood & 0xF]; _gfx->flatBlitCnv(&face, QUESTION_CHARACTER_X, QUESTION_CHARACTER_Y, Gfx::kBitFront); _gfx->getStringExtent(q->_text, MAX_BALLOON_WIDTH, &w, &h); Common::Rect r(w, h); r.moveTo(QUESTION_BALLOON_X, QUESTION_BALLOON_Y); _gfx->drawBalloon(r, q->_mood & 0x10); _gfx->displayWrappedString(q->_text, QUESTION_BALLOON_X, QUESTION_BALLOON_Y, MAX_BALLOON_WIDTH, 0); _gfx->updateScreen(); waitUntilLeftClick(); _gfx->copyScreen(Gfx::kBitBack, Gfx::kBitFront); return; } uint16 Parallaction::getDialogueAnswer(Dialogue *q, Cnv *cnv) { uint16 answer = 0; StaticCnv face; face._width = cnv->_width; face._height = cnv->_height; face._data0 = cnv->getFramePtr(0); face._data1 = NULL; // cnv->field_8[0]; _gfx->flatBlitCnv(&face, ANSWER_CHARACTER_X, ANSWER_CHARACTER_Y, Gfx::kBitFront); if (_askPassword == false) { answer = selectAnswer(q, &face); } else { answer = askDialoguePassword(q, &face); } _gfx->copyScreen(Gfx::kBitBack, Gfx::kBitFront); // erase answer screen debugC(1, kDebugDialogue, "runDialogue: user selected answer #%i", answer); return answer; } void Parallaction::runDialogue(SpeakData *data) { debugC(1, kDebugDialogue, "runDialogue: starting dialogue '%s'", data->_name); enterDialogue(); _gfx->setFont(kFontDialogue); bool isNpc = scumm_stricmp(data->_name, "yourself") && data->_name[0] != '\0'; Cnv *face = isNpc ? _disk->loadTalk(data->_name) : _char._talk; _askPassword = false; CommandList *cmdlist = NULL; uint16 answer; Dialogue *q = data->_dialogue; while (q) { answer = 0; displayQuestion(q, face); if (q->_answers[0] == NULL) break; _answerBalloonY[0] = 10; if (scumm_stricmp(q->_answers[0]->_text, "NULL")) { if (!displayAnswers(q)) break; answer = getDialogueAnswer(q, _char._talk); cmdlist = &q->_answers[answer]->_commands; } q = q->_answers[answer]->_following._question; } debugC(1, kDebugDialogue, "runDialogue: out of dialogue loop"); _gfx->copyScreen(Gfx::kBitBack, Gfx::kBitFront); if (isNpc) { delete face; } exitDialogue(); if (cmdlist) runCommands(*cmdlist); return; } int16 Parallaction::selectAnswer(Question *q, StaticCnv *cnv) { int16 numAvailableAnswers = 0; int16 _si = 0; int16 _di = 0; int16 i = 0; for (; q->_answers[i]; i++) { if (_answerBalloonY[i] == SKIPPED_ANSWER) continue; _di = i; numAvailableAnswers++; } _answerBalloonY[i] = 2000; if (numAvailableAnswers == 1) { _gfx->displayWrappedString(q->_answers[_di]->_text, _answerBalloonX[_di], _answerBalloonY[_di], MAX_BALLOON_WIDTH, 0); cnv->_data0 = _char._talk->getFramePtr(q->_answers[_di]->_mood & 0xF); // cnv->_data1 = _char._talk->field_8[q->_answers[_di]->_mood & 0xF]; _gfx->flatBlitCnv(cnv, ANSWER_CHARACTER_X, ANSWER_CHARACTER_Y, Gfx::kBitFront); _gfx->updateScreen(); waitUntilLeftClick(); return _di; } int16 v2 = -1; _mouseButtons = kMouseNone; while (_mouseButtons != kMouseLeftUp) { updateInput(); _si = getHoverAnswer(_mousePos.x, _mousePos.y, q); if (_si != v2) { if (v2 != -1) _gfx->displayWrappedString(q->_answers[v2]->_text, _answerBalloonX[v2], _answerBalloonY[v2], MAX_BALLOON_WIDTH, 3); _gfx->displayWrappedString(q->_answers[_si]->_text, _answerBalloonX[_si], _answerBalloonY[_si], MAX_BALLOON_WIDTH, 0); cnv->_data0 = _char._talk->getFramePtr(q->_answers[_si]->_mood & 0xF); // cnv->_data1 = _char._talk->field_8[q->_answers[_si]->_mood & 0xF]; _gfx->flatBlitCnv(cnv, ANSWER_CHARACTER_X, ANSWER_CHARACTER_Y, Gfx::kBitFront); } _gfx->updateScreen(); g_system->delayMillis(30); v2 = _si; } return _si; } // // finds out which answer is currently selected // int16 getHoverAnswer(int16 x, int16 y, Question *q) { int16 top = 1000; int16 bottom = 1000; for (int16 _si = 0; _si < NUM_ANSWERS; _si++) { if (q->_answers[_si] == NULL) break; if (_answerBalloonY[_si] != SKIPPED_ANSWER) { top = _answerBalloonY[_si]; } int16 _di = _si + 1; for (; _answerBalloonY[_di] == SKIPPED_ANSWER; _di++) ; bottom = _answerBalloonY[_di]; // mouse position is compared only with y coordinates if (y > top && y < bottom) return _si; } return 0; } void Parallaction::enterDialogue() { return; } // rebuilds inventory // void Parallaction::exitDialogue() { refreshInventory(_characterName); return; } Answer::Answer() { _text = NULL; _mood = 0; _following._question = NULL; _noFlags = 0; _yesFlags = 0; } Answer::~Answer() { if (_mood & 0x10) delete _following._question; if (_text) free(_text); } Question::Question() { _text = NULL; _mood = 0; for (uint32 i = 0; i < NUM_ANSWERS; i++) _answers[i] = NULL; } Question::~Question() { for (uint32 i = 0; i < NUM_ANSWERS; i++) if (_answers[i]) delete _answers[i]; free(_text); } } // namespace Parallaction