diff options
43 files changed, 13015 insertions, 0 deletions
diff --git a/engines/avalanche/acci2.cpp b/engines/avalanche/acci2.cpp new file mode 100644 index 0000000000..1c10a36fe3 --- /dev/null +++ b/engines/avalanche/acci2.cpp @@ -0,0 +1,2060 @@ + /* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on the original source code of Lord Avalot d'Argent version 1.3. + * Copyright (c) 1994-1995 Mike, Mark and Thomas Thurman. + */ + +/* ACCIDENCE II The parser. */ + +#include "avalanche/avalanche.h" + +#include "avalanche/acci2.h" +#include "avalanche/gyro2.h" +#include "avalanche/lucerna2.h" +#include "avalanche/scrolls2.h" +#include "avalanche/visa2.h" +#include "avalanche/timer.h" +#include "avalanche/animation.h" +#include "avalanche/enid2.h" +#include "avalanche/celer2.h" +#include "avalanche/pingo2.h" +#include "avalanche/sequence2.h" + +#include "common/textconsole.h" + +#include <cstring> +#include <cmath> + +namespace Avalanche { + +const Acci::VocabEntry Acci::kVocabulary[kParserWordsNum] = { + // Verbs: 1-49 + {1, "EXAMINE"}, {1, "READ"}, {1, "XAM"}, // short + {2, "OPEN"}, {2, "LEAVE"}, {2, "UNLOCK"}, + {3, "PAUSE"}, {47, "TA"}, // Early to avoid Take and Talk. + {4, "TAKE"}, {4, "GET"}, {4, "PICK"}, + {5, "DROP"}, {6, "INVENTORY"}, {7, "TALK"}, + {7, "SAY"}, {7, "ASK"}, + {8, "GIVE"}, {9, "DRINK"}, {9, "IMBIBE"}, + {9, "DRAIN"}, {10, "LOAD"}, {10, "RESTORE"}, + {11, "SAVE"}, {12, "BRIBE"}, {12, "PAY"}, + {13, "LOOK"}, {14, "BREAK"}, {15, "QUIT"}, + {15, "EXIT"}, {16, "SIT"}, {16, "SLEEP"}, + {17, "STAND"}, + {18, "GO"}, {19, "INFO"}, {20, "UNDRESS"}, + {20, "DOFF"}, + {21, "DRESS"}, {21, "WEAR"}, {21, "DON"}, + {22, "PLAY"}, + {22, "STRUM"}, {23, "RING"}, {24, "HELP"}, + {25, "KENDAL"}, {26, "CAPYBARA"}, {27, "BOSS"}, + {255, "NINET"}, // block for NINETY + {28, "URINATE"}, {28, "MINGITE"}, {29, "NINETY"}, + {30, "ABRACADABRA"}, {30, "PLUGH"}, {30, "XYZZY"}, + {30, "HOCUS"}, {30, "POCUS"}, {30, "IZZY"}, + {30, "WIZZY"}, {30, "PLOVER"}, + {30, "MELENKURION"}, {30, "ZORTON"}, {30, "BLERBI"}, + {30, "THURB"}, {30, "SNOEZE"}, {30, "SAMOHT"}, + {30, "NOSIDE"}, {30, "PHUGGG"}, {30, "KNERL"}, + {30, "MAGIC"}, {30, "KLAETU"}, {30, "VODEL"}, + {30, "BONESCROLLS"}, {30, "RADOF"}, + {31, "RESTART"}, + {32, "SWALLOW"}, {32, "EAT"}, {33, "LISTEN"}, + {33, "HEAR"}, {34, "BUY"}, {34, "PURCHASE"}, + {34, "ORDER"}, {34, "DEMAND"}, + {35, "ATTACK"}, {35, "HIT"}, {35, "KILL"}, + {35, "PUNCH"}, {35, "KICK"}, {35, "SHOOT"}, + {35, "FIRE"}, + + // Passwords: 36 + {36, "TIROS"}, {36, "WORDY"}, {36, "STACK"}, + {36, "SHADOW"}, {36, "OWL"}, {36, "ACORN"}, + {36, "DOMESDAY"}, {36, "FLOPPY"}, {36, "DIODE"}, + {36, "FIELD"}, {36, "COWSLIP"}, {36, "OSBYTE"}, + {36, "OSCLI"}, {36, "TIMBER"}, {36, "ADVAL"}, + {36, "NEUTRON"}, {36, "POSITRON"}, {36, "ELECTRON"}, + {36, "CIRCUIT"}, {36, "AURUM"}, {36, "PETRIFY"}, + {36, "EBBY"}, {36, "CATAPULT"}, {36, "GAMERS"}, + {36, "FUDGE"}, {36, "CANDLE"}, {36, "BEEB"}, + {36, "MICRO"}, {36, "SESAME"}, {36, "LORDSHIP"}, + {37, "DIR"}, {37, "LS"}, {38, "DIE"}, + {39, "SCORE"}, + {40, "PUT"}, {40, "INSERT"}, {41, "KISS"}, + {41, "SNOG"}, {41, "CUDDLE"}, {42, "CLIMB"}, + {42, "CLAMBER"}, {43, "JUMP"}, {44, "HIGHSCORES"}, + {44, "HISCORES"}, {45, "WAKEN"}, {45, "AWAKEN"}, + {46, "HELLO"}, {46, "HI"}, {46, "YO"}, + {47, "THANKS"}, // = 47, "ta", which was defined earlier. + + // Nouns - Objects: 50-100 + {50, "WINE"}, {50, "BOOZE"}, {50, "NASTY"}, + {50, "VINEGAR"}, {51, "MONEYBAG"}, + {51, "BAG"}, {51, "CASH"}, {51, "DOSH"}, + {51, "WALLET"}, + {52, "BODKIN"}, {52, "DAGGER"}, {53, "POTION"}, + {54, "CHASTITY"}, {54, "BELT"}, {55, "BOLT"}, + {55, "ARROW"}, {55, "DART"}, + {56, "CROSSBOW"}, {56, "BOW"}, {57, "LUTE"}, + {58, "PILGRIM"}, {58, "BADGE"}, {59, "MUSHROOMS"}, + {59, "TOADSTOOLS"}, {60, "KEY"}, {61, "BELL"}, + {62, "PRESCRIPT"}, {62, "SCROLL"}, {62, "MESSAGE"}, + {63, "PEN"}, {63, "QUILL"}, {64, "INK"}, + {64, "INKPOT"}, {65, "CLOTHES"}, {66, "HABIT"}, + {66, "DISGUISE"}, {67, "ONION"}, + {99, "PASSWORD"}, + + // Objects from Also are placed between 101 and 131. + + // Nouns - People - Male: 150-174 + {150, "AVVY"}, {150, "AVALOT"}, {150, "YOURSELF"}, + {150, "ME"}, {150, "MYSELF"}, {151, "SPLUDWICK"}, + {151, "THOMAS"}, {151, "ALCHEMIST"}, {151, "CHEMIST"}, + {152, "CRAPULUS"}, {152, "SERF"}, {152, "SLAVE"}, + {158, "DU"}, // <<< Put in early for Baron DU Lustie to save confusion with Duck & Duke. + {152, "CRAPPY"}, {153, "DUCK"}, {153, "DOCTOR"}, + {154, "MALAGAUCHE"}, + {155, "FRIAR"}, {155, "TUCK"}, {156, "ROBIN"}, + {156, "HOOD"}, {157, "CWYTALOT"}, {157, "GUARD"}, + {157, "BRIDGEKEEP"}, {158, "BARON"}, {158, "LUSTIE"}, + {159, "DUKE"}, {159, "GRACE"}, {160, "DOGFOOD"}, + {160, "MINSTREL"}, {161, "TRADER"}, {161, "SHOPKEEPER"}, + {161, "STALLHOLDER"}, + {162, "PILGRIM"}, {162, "IBYTHNETH"}, {163, "ABBOT"}, + {163, "AYLES"}, {164, "PORT"}, {165, "SPURGE"}, + {166, "JACQUES"}, {166, "SLEEPER"}, {166, "RINGER"}, + + // Nouns - People - Female: 175-199 + {175, "WIFE"}, {175, "ARKATA"}, {176, "GEDALODAVA"}, + {176, "GEIDA"}, {176, "PRINCESS"}, {178, "WISE"}, + {178, "WITCH"}, + + // Pronouns: 200-224 + {200, "HIM"}, {200, "MAN"}, {200, "GUY"}, + {200, "DUDE"}, {200, "CHAP"}, {200, "FELLOW"}, + {201, "HER"}, {201, "GIRL"}, {201, "WOMAN"}, + {202, "IT"}, {202, "THING"}, + {203, "MONK"}, {204, "BARMAN"}, {204, "BARTENDER"}, + + // Prepositions: 225-249 + {225, "TO"}, {226, "AT"}, {227, "UP"}, + {228, "INTO"}, {228, "INSIDE"}, {229, "OFF"}, + {230, "UP"}, {231, "DOWN"}, {232, "ON"}, + + // Please: 251 + {251, "PLEASE"}, + + // About: 252 + {252, "ABOUT"}, {252, "CONCERNING"}, + + // Swear words: 253 + /* I M P O R T A N T M E S S A G E + + DO *NOT* READ THE LINES BELOW IF YOU ARE OF A SENSITIVE + DISPOSITION. THOMAS IS *NOT* RESPONSIBLE FOR THEM. + GOODNESS KNOWS WHO WROTE THEM. + READ THEM AT YOUR OWN RISK. BETTER STILL, DON'T READ THEM. + WHY ARE YOU SNOOPING AROUND IN MY PROGRAM, ANYWAY? */ + + {253, "SHIT"}, {28 , "PISS"}, {28 , "PEE"}, + {253, "FART"}, {253, "FUCK"}, {253, "BALLS"}, + {253, "BLAST"}, {253, "BUGGER"}, {253, "KNICKERS"}, + {253, "BLOODY"}, {253, "HELL"}, {253, "DAMN"}, + {253, "SMEG"}, + // ...and other even ruder words. You didn't read them, did you? Good. */ + + // Answer-back smart-alec words: 249 + {249, "YES"}, {249, "NO"}, {249, "BECAUSE"}, + + // Noise words: 255 + {255, "THE"}, {255, "A"}, {255, "NOW"}, + {255, "SOME"}, {255, "AND"}, {255, "THAT"}, + {255, "POCUS"}, {255, "HIS"}, + {255, "THIS"}, {255, "SENTINEL"} // for "Ken SENT Me" +}; + +Acci::Acci(AvalancheEngine *vm) { + _vm = vm; +} + +void Acci::init() { + _vm->_gyro->_weirdWord = false; +} + +void Acci::clearWords() { + for (byte i = 0; i < 11; i++) { + if (!_realWords[i].empty()) + _realWords[i].clear(); + } +} + +byte Acci::wordNum(Common::String word) { + if (word.empty()) + return 0; + + for (int32 i = kParserWordsNum - 1; i >= 0; i--) { + if (kVocabulary[i]._word == word) + return kVocabulary[i]._number; + } + + // If not found as a whole, we look for it as a substring. + for (int32 i = kParserWordsNum - 1; i >= 0; i--) { + if (Common::String(kVocabulary[i]._word.c_str(), word.size()) == word) + return kVocabulary[i]._number; + } + + return kPardon; +} + +void Acci::replace(Common::String oldChars, byte newChar) { + int16 pos = _vm->_parser->pos(oldChars, _thats); + while (pos != -1) { + if (newChar == 0) + _thats.deleteChar(pos); + else { + for (byte i = pos; i < pos + oldChars.size(); i++) + _thats.deleteChar(pos); + _thats.insertChar(newChar, pos); + } + pos = _vm->_parser->pos(oldChars, _thats); + } +} + +Common::String Acci::rank() { + static const RankType kRanks[9] = { + {0, "Beginner"}, {10, "Novice"}, + {20, "Improving"}, {35, "Not bad"}, + {50, "Passable"}, {65, "Good"}, + {80, "Experienced"}, {108, "The BEST!"}, + {32767, "copyright'93"} + }; + + for (byte i = 0; i < 8; i++) { + if ((_vm->_gyro->_dna._score >= kRanks[i]._score) && (_vm->_gyro->_dna._score < kRanks[i + 1]._score)) { + return kRanks[i]._title; + } + } + return ""; +} + +Common::String Acci::totalTime() { + // There are 65535 clock ticks in a second, 1092.25 in a minute, and 65535 in an hour. + const double ticksInOneSec = (double)(65535) / 3600; + uint16 h, m, s; + + h = _vm->_gyro->_dna._totalTime / ticksInOneSec; // No. of seconds. + h = floor((float)h); + m = h % 3600; + h = h / 3600; + s = m % 60; + m = m / 60; + + Common::String result = "You've been playing for "; + if (h > 0) + result = result + _vm->_gyro->intToStr(h) + " hours, "; + if ((m > 0) || (h != 0)) + result = result + _vm->_gyro->intToStr(m) + " minutes and "; + return result + _vm->_gyro->intToStr(s) + " seconds."; +} + +void Acci::cheatParse(Common::String codes) { +// uint16 num; +// int16 e; +// char cmd; +// int16 se, sx, sy; + + warning("STUB: Acci::cheatParse()"); +} + + + +void Acci::stripPunctuation(Common::String &word) { + const char punct[] = "~`!@#$%^&*()_+-={}[]:\"|;'\\,./<>?"; + + for (byte i = 0; i < 32; i++) { + for (;;) { + int16 pos = _vm->_parser->pos(Common::String(punct[i]), word); + if (pos == -1) + break; + word.deleteChar(pos); + } + } +} + +void Acci::displayWhat(byte target, bool animate, bool &ambiguous) { + if (target == kPardon) { + ambiguous = true; + if (animate) + _vm->_scrolls->displayText("Whom?"); + else + _vm->_scrolls->displayText("What?"); + } else { + if (animate) + _vm->_scrolls->displayText(Common::String("{ ") + _vm->_gyro->getName(target) + " }"); + else { + Common::String z = _vm->_gyro->getItem(target); + if (z != "") + _vm->_scrolls->displayText(Common::String("{ ") + z + " }"); + } + } +} + +bool Acci::doPronouns() { + bool ambiguous = false; + + for (byte i = 0; i < _thats.size(); i++) { + byte wordCode = _thats[i]; + switch (wordCode) { + case 200: { + displayWhat(_vm->_gyro->_him, true, ambiguous); + _thats.setChar(_vm->_gyro->_him, i); + } + break; + case 201: { + displayWhat(_vm->_gyro->_her, true, ambiguous); + _thats.setChar(_vm->_gyro->_her, i); + } + break; + case 202: { + displayWhat(_vm->_gyro->_it, false, ambiguous); + _thats.setChar(_vm->_gyro->_it, i); + } + break; + } + } + + return ambiguous; +} + +void Acci::properNouns() { + _vm->_parser->_inputText.toLowercase(); + + // We set every word's first character to uppercase. + for (byte i = 1; i < (_vm->_parser->_inputText.size() - 1); i++) { + if (_vm->_parser->_inputText[i] == ' ') + _vm->_parser->_inputText.setChar(toupper(_vm->_parser->_inputText[i + 1]), i + 1); + } + + // And the first character as well. + _vm->_parser->_inputText.setChar(toupper(_vm->_parser->_inputText[0]), 0); +} + +void Acci::sayIt() { + Common::String x = _vm->_parser->_inputText; + x.setChar(toupper(x[0]), 0); + _vm->_scrolls->displayText(Common::String(_vm->_scrolls->kControlRegister) + '1' + x + + '.' + _vm->_scrolls->kControlSpeechBubble + _vm->_scrolls->kControlRegister + '2'); +} + +void Acci::storeInterrogation(byte interrogation) { + if (_vm->_parser->_inputText.empty()) + return; + + // Strip _vm->_parser->_inputText: + while ((_vm->_parser->_inputText[0] == ' ') && (!_vm->_parser->_inputText.empty())) + _vm->_parser->_inputText.deleteChar(0); + while ((_vm->_parser->_inputText.lastChar() == ' ') && (!_vm->_parser->_inputText.empty())) + _vm->_parser->_inputText.deleteLastChar(); + + _vm->_timer->loseTimer(_vm->_timer->kReasonCardiffsurvey); // If you want to use any other timer, put this into the case statement. + + switch (interrogation) { + case 1: + _vm->_parser->_inputText.toLowercase(); + sayIt(); + _vm->_gyro->_dna._favouriteDrink = _vm->_parser->_inputText; + _vm->_gyro->_dna._cardiffQuestionNum = 2; + break; + case 2: + properNouns(); + sayIt(); + _vm->_gyro->_dna._favouriteSong = _vm->_parser->_inputText; + _vm->_gyro->_dna._cardiffQuestionNum = 3; + break; + case 3: + properNouns(); + sayIt(); + _vm->_gyro->_dna._worstPlaceOnEarth = _vm->_parser->_inputText; + _vm->_gyro->_dna._cardiffQuestionNum = 4; + break; + case 4: + _vm->_parser->_inputText.toLowercase(); + sayIt(); + if (!_vm->_gyro->_dna._spareEvening.empty()) + _vm->_gyro->_dna._spareEvening.clear(); + _vm->_gyro->_dna._spareEvening = _vm->_parser->_inputText; + _vm->_visa->displayScrollChain('z', 5); // His closing statement... + _vm->_animation->_sprites[1].walkTo(4); // The end of the drawbridge + _vm->_animation->_sprites[1]._vanishIfStill = true; // Then go away! + _vm->_gyro->_magics[1]._operation = _vm->_gyro->kMagicNothing; + _vm->_gyro->_dna._cardiffQuestionNum = 5; + break; + case 99: + //store_high(_vm->_parser->_inputText); + warning("STUB: Acci::store_interrogation()"); + break; + } + + if (interrogation < 4) + _vm->_timer->cardiffSurvey(); +} + + + +void Acci::parse() { + // First parsing - word identification + if (!_thats.empty()) + _thats.clear(); + + byte n = 0; + _polite = false; + _verb = kPardon; + _thing = kPardon; + _thing2 = kPardon; + _person = kPardon; + clearWords(); + + + // A cheat mode attempt. + if (_vm->_parser->_inputText[0] == '.') { + cheatParse(_vm->_parser->_inputText); + _thats = kNothing; + return; + } + + // Are we being interrogated right now? + if (_vm->_gyro->_interrogation > 0) { + storeInterrogation(_vm->_gyro->_interrogation); + _vm->_gyro->_weirdWord = true; + return; + } + + // Actually process the command. + Common::String inputText = _vm->_parser->_inputText + ' '; + Common::String inputTextUpper = inputText; + inputTextUpper.toUppercase(); + while (!inputTextUpper.empty()) { + while ((!inputTextUpper.empty()) && (inputTextUpper[0] == ' ')) { + inputTextUpper.deleteChar(0); + inputText.deleteChar(0); + } + if (inputTextUpper.empty()) + break; + + // Get the following word of the strings. + byte size = _vm->_parser->pos(Common::String(' '), inputTextUpper) + 1; + char *subStr = new char[size]; + Common::strlcpy(subStr, inputTextUpper.c_str(), size); + Common::String thisword = subStr; + Common::strlcpy(subStr, inputText.c_str(), size); + _realWords[n] = subStr; + delete[] subStr; + + stripPunctuation(inputTextUpper); + + bool notfound = true; + + // Check also[] first, which conatins words about the actual room. + if (!thisword.empty()) { + for (byte i = 0; i < 31; i++) { + if ((_vm->_gyro->_also[i][0] != 0) && (_vm->_parser->pos(',' + thisword, *_vm->_gyro->_also[i][0]) > -1)) { + _thats = _thats + Common::String(99 + i); + notfound = false; + } + } + } + + // Check Accis's own table (words[]) for "global" commands. + if (notfound) { + byte answer = wordNum(thisword); + if (answer == kPardon) { + notfound = true; + _thats = _thats + kPardon; + } else + _thats = _thats + answer; + n++; + } + + // Delete words we already processed. + int16 spacePos = _vm->_parser->pos(Common::String(' '), inputTextUpper); + if (spacePos > -1) { + for (byte i = 0; i <= spacePos; i++) + inputTextUpper.deleteChar(0); + } + + spacePos = _vm->_parser->pos(Common::String(' '), inputText); + if (spacePos > -1) { + for (byte i = 0; i <= spacePos; i++) + inputText.deleteChar(0); + } + } + + Common::String unkString; + if (_vm->_parser->pos(Common::String('\xFE'), _thats) > -1) + unkString = _realWords[_vm->_parser->pos(Common::String('\xFE'), _thats)]; + else + unkString.clear(); + + // Replace words' codes that mean the same. + replace(Common::String('\xFF'), 0); // zap noise words + replace(Common::String('\xD') + '\xE2', 1); // "look at" = "examine" + replace(Common::String('\xD') + '\xE4', 1); // "look in" = "examine" + replace(Common::String('\x4') + '\xE6', 17); // "get up" = "stand" + replace(Common::String('\x4') + '\xE7', 17); // "get down" = "stand"... well, why not? + replace(Common::String('\x12') + '\xE4', 2); // "go in" = "open [door]" + replace(Common::String('\x1C') + '\xE5', 253); // "P' off" is a swear word + replace(Common::String('\x4') + '\x6' , 6); // "Take inventory" (remember Colossal Adventure?) + replace(Common::String('\x28') + '\xE8', 21); // "put on" = "don" + replace(Common::String('\x4') + '\xE5', 20); // "take off" = "doff" + + // Words that could mean more than one _person + if (_vm->_gyro->_dna._room == r__nottspub) + replace(Common::String('\xCC'), 164); // Barman = Port + else + replace(Common::String('\xCC'), 154); // Barman = Malagauche + + switch (_vm->_gyro->_dna._room) { + case r__aylesoffice: + replace(Common::String('\xCB'), 163); // Monk = Ayles + break; + case r__musicroom: + replace(Common::String('\xCB'), 166); // Monk = Jacques + break; + default: + replace(Common::String('\xCB'), 162); // Monk = Ibythneth + } + + if (doPronouns()) { + _vm->_gyro->_weirdWord = true; + _thats = kNothing; + return; + } + + // Second parsing. + if (!_vm->_gyro->_subject.empty()) + _vm->_gyro->_subject.clear(); + _vm->_gyro->_subjectNum = 0; // Find subject of conversation. + + for (int i = 0; (i < 11) && !_realWords[i].empty(); i++) { + if ((_realWords[i][0] == '\'') || (_realWords[i][0] == '\"')) { + _vm->_gyro->_subjectNum = (byte)_thats[i]; + _thats.setChar(kMoved, i); + break; + } + } + + if ((_vm->_gyro->_subjectNum == 0) && !_thats.empty()) { // Still not found. + for (uint16 i = 0; i < _thats.size() - 1; i++) { + if ((byte)_thats[i] == 252) { // The word is "about", or something similar. + _vm->_gyro->_subjectNum = (byte)_thats[i + 1]; + _thats.setChar(0, i + 1); + break; + } + } + } + + if ((_vm->_gyro->_subjectNum == 0) && !_thats.empty()) { // STILL not found! Must be the word after "say". + for (uint16 i = 0; i < _thats.size() - 1; i++) { + if (((byte)_thats[i] == 7) && ((byte)_thats[i + 1] != 0) && !((225 <= (byte)_thats[i + 1]) && ((byte)_thats[i + 1] <= 229))) { + // SAY not followed by a preposition + _vm->_gyro->_subjectNum = (byte)_thats[i + 1]; + _thats.setChar(0, i + 1); + break; + } + } + } + + for (int16 i = _thats.size() - 1; i >= 0; i--) { // Reverse order, so first will be used. + if (((byte)_thats[i] == 253) || ((byte)_thats[i] == 249) || ((1 <= (byte)_thats[i]) && ((byte)_thats[i] <= 49))) + _verb = (byte)_thats[i]; + else if ((50 <= (byte)_thats[i]) && ((byte)_thats[i] <= 149)) { + _thing2 = _thing; + _thing = (byte)_thats[i]; + } else if ((150 <= (byte)_thats[i]) && ((byte)_thats[i] <= 199)) + _person = (byte)_thats[i]; + else if ((byte)_thats[i] == 251) + _polite = true; + } + + if ((!unkString.empty()) && (_verb != kVerbCodeExam) && (_verb != kVerbCodeTalk) && (_verb != kVerbCodeSave) && (_verb != kVerbCodeLoad) && (_verb != kVerbCodeDir)) { + _vm->_scrolls->displayText(Common::String("Sorry, but I have no idea what \"") + unkString + "\" means. Can you rephrase it?"); + _vm->_gyro->_weirdWord = true; + } else + _vm->_gyro->_weirdWord = false; + + if (_thats.empty()) + _thats = kNothing; + + if (_thing != kPardon) + _vm->_gyro->_it = _thing; + + if (_person != kPardon) { + if (_person < 175) + _vm->_gyro->_him = _person; + else + _vm->_gyro->_her = _person; + } +} + +void Acci::examineObject() { + if (_thing != _vm->_gyro->_thinks) + _vm->_lucerna->thinkAbout(_thing, Gyro::kThing); + switch (_thing) { + case Gyro::kObjectWine : + switch (_vm->_gyro->_dna._wineState) {// 4 is perfect wine. 0 is not holding the wine. + case 1: + _vm->_visa->displayScrollChain('t', 1); // Normal examine wine scroll + break; + case 2: + _vm->_visa->displayScrollChain('d', 6); // Bad wine + break; + case 3: + _vm->_visa->displayScrollChain('d', 7); // Vinegar + break; + } + break; + case Gyro::kObjectOnion: + if (_vm->_gyro->_dna._rottenOnion) + _vm->_visa->displayScrollChain('q', 21); // Yucky onion. + else + _vm->_visa->displayScrollChain('t', 18); // Normal onion scroll + break; + default: + _vm->_visa->displayScrollChain('t', _thing); // <<< Ordinarily + } +} + +bool Acci::isPersonHere() { // Person equivalent of "holding". + if ((_person == kPardon) || (_person == 0) || (_vm->_gyro->_whereIs[_person - 150] == _vm->_gyro->_dna._room)) + return true; + else { + if (_person < 175) + _vm->_scrolls->displayText(Common::String('H') + _vm->_scrolls->kControlToBuffer); + else + _vm->_scrolls->displayText(Common::String("Sh") + _vm->_scrolls->kControlToBuffer); + _vm->_scrolls->displayText("e isn't around at the moment."); + return false; + } +} + +void Acci::exampers() { + if (isPersonHere()) { + if (_thing != _vm->_gyro->_thinks) + _vm->_lucerna->thinkAbout(_person, Gyro::kPerson); + _person -= 149; + switch (_person) { // Special cases + case 11: + if (_vm->_gyro->_dna._wonNim) { + _vm->_visa->displayScrollChain('Q', 8); // "I'm Not Playing!" + return; + } + break; + case 99: + if (_vm->_gyro->_dna._lustieIsAsleep) { + _vm->_visa->displayScrollChain('Q', 65); // He's asleep. (65! Wow!) + return; + } + break; + } + // Otherwise... + _vm->_visa->displayScrollChain('p', _person); + } + + // And afterwards... + if ((_person == 14) && (!_vm->_gyro->_dna._aylesIsAwake)) + _vm->_visa->displayScrollChain('Q', 13); +} + +/** + * Return whether Avvy is holding an object or not + * @remarks Originally called 'holding' + */ +bool Acci::isHolding() { + if ((51 <= _thing) && (_thing <= 99)) // Also. + return true; + + bool holdingResult = false; + + if (_thing > 100) + _vm->_scrolls->displayText("Be reasonable!"); + else if (!_vm->_gyro->_dna._objects[_thing - 1]) // Verbs that need "_thing" to be in the inventory. + _vm->_scrolls->displayText("You're not holding it, Avvy."); + else + holdingResult = true; + + return holdingResult; +} + +void Acci::openBox(bool isOpening) { + if ((_vm->_gyro->_dna._room == r__yours) && (_thing == 54)) { + _vm->_celer->drawBackgroundSprite(-1, -1, 5); + + _vm->_celer->updateBackgroundSprites(); + _vm->_animation->animLink(); + _vm->_graphics->refreshScreen(); + + _vm->_system->delayMillis(55); + + if (!isOpening) { + _vm->_celer->drawBackgroundSprite(-1, -1, 6); + _vm->_celer->updateBackgroundSprites(); + _vm->_animation->animLink(); + _vm->_graphics->refreshScreen(); + } + } +} + +void Acci::examine() { + // EITHER it's an object OR it's an Also OR it's a _person OR it's something else. + if ((_person == kPardon) && (_thing != kPardon)) { + if (isHolding()) { + // Remember: it's been Slipped! Ie subtract 49. + if ((1 <= _thing) && (_thing <= 49)) // Standard object + examineObject(); + else if ((50 <= _thing) && (_thing <= 100)) { // Also _thing + openBox(true); + _vm->_scrolls->displayText(*_vm->_gyro->_also[_thing - 50][1]); + openBox(false); + } + } + } else if (_person != kPardon) + exampers(); + else + _vm->_scrolls->displayText("It's just as it looks on the picture."); // Don't know: guess. +} + +void Acci::inventory() { + byte itemNum = 0; + _vm->_scrolls->displayText(Common::String("You're carrying ") + _vm->_scrolls->kControlToBuffer); + + for (byte i = 0; i < kObjectNum; i++) { + if (_vm->_gyro->_dna._objects[i]) { + itemNum++; + if (itemNum == _vm->_gyro->_dna._carryNum) + _vm->_scrolls->displayText(Common::String("and ") + _vm->_scrolls->kControlToBuffer); + _vm->_scrolls->displayText(_vm->_gyro->getItem(i + 1) + _vm->_scrolls->kControlToBuffer); + if ((i + 1) == _vm->_gyro->_dna._wearing) + _vm->_scrolls->displayText(Common::String(", which you're wearing") + _vm->_scrolls->kControlToBuffer); + if (itemNum < _vm->_gyro->_dna._carryNum) + _vm->_scrolls->displayText(Common::String(", ") + _vm->_scrolls->kControlToBuffer); + } + } + + if (_vm->_gyro->_dna._wearing == kNothing) + _vm->_scrolls->displayText(Common::String("...") + _vm->_scrolls->kControlNewLine + _vm->_scrolls->kControlNewLine + "...and you're stark naked!"); + else + _vm->_scrolls->displayText("."); +} + +void Acci::swallow() { // Eat something. + switch (_thing) { + case Gyro::kObjectWine: + switch (_vm->_gyro->_dna._wineState) { // 4 is perfect + case 1: + if (_vm->_gyro->_dna._teetotal) { + _vm->_visa->displayScrollChain('D', 6); + return; + } + _vm->_visa->displayScrollChain('U', 1); + _vm->_pingo->wobble(); + _vm->_visa->displayScrollChain('U', 2); + _vm->_gyro->_dna._objects[_vm->_gyro->kObjectWine - 1] = false; + _vm->_lucerna->refreshObjectList(); + drink(); + break; + case 2: + case 3: + _vm->_visa->displayScrollChain('d', 8); + break; // You can't drink it! + } + break; + case Gyro::kObjectPotion: + _vm->_gyro->setBackgroundColor(4); + _vm->_visa->displayScrollChain('U', 3); + _vm->_lucerna->gameOver(); + _vm->_gyro->setBackgroundColor(0); + break; + case Gyro::kObjectInk: + _vm->_visa->displayScrollChain('U', 4); + break; + case Gyro::kObjectChastity: + _vm->_visa->displayScrollChain('U', 5); + break; + case Gyro::kObjectMushroom: + _vm->_visa->displayScrollChain('U', 6); + _vm->_lucerna->gameOver(); + break; + case Gyro::kObjectOnion: + if (_vm->_gyro->_dna._rottenOnion) + _vm->_visa->displayScrollChain('U', 11); + else { + _vm->_visa->displayScrollChain('U', 8); + _vm->_gyro->_dna._objects[_vm->_gyro->kObjectOnion - 1] = false; + _vm->_lucerna->refreshObjectList(); + } + break; + default: + if ((_vm->_gyro->_dna._room == r__argentpub) || (_vm->_gyro->_dna._room == r__nottspub)) + _vm->_scrolls->displayText("Try BUYing things before you drink them!"); + else + _vm->_scrolls->displayText("The taste of it makes you retch!"); + } +} + +void Acci::peopleInRoom() { + byte numPeople = 0; // Number of people in the room. + + for (byte i = 1; i < 29; i++) { // Start at 1 so we don't list Avvy himself! + if (_vm->_gyro->_whereIs[i] == _vm->_gyro->_dna._room) + numPeople++; + } + + if (numPeople == 0) // If nobody's here, we can cut out straight away. + return; + + byte actPerson = 0; // Actually listed people. + for (byte i = 1; i < 29; i++) { + if (_vm->_gyro->_whereIs[i] == _vm->_gyro->_dna._room) { + actPerson++; + if (actPerson == 1) // First on the list. + _vm->_scrolls->displayText(_vm->_gyro->getName(i + 150) + _vm->_scrolls->kControlToBuffer); + else if (actPerson < numPeople) // The middle... + _vm->_scrolls->displayText(Common::String(", ") + _vm->_gyro->getName(i + 150) + _vm->_scrolls->kControlToBuffer); + else // The end. + _vm->_scrolls->displayText(Common::String(" and ") + _vm->_gyro->getName(i + 150) + _vm->_scrolls->kControlToBuffer); + } + } + + if (numPeople == 1) + _vm->_scrolls->displayText(Common::String(" is") + _vm->_scrolls->kControlToBuffer); + else + _vm->_scrolls->displayText(Common::String(" are") + _vm->_scrolls->kControlToBuffer); + + _vm->_scrolls->displayText(" here."); // End and display it. +} + +void Acci::lookAround() { + _vm->_scrolls->displayText(*_vm->_gyro->_also[0][1]); + switch (_vm->_gyro->_dna._room) { + case r__spludwicks: + if (_vm->_gyro->_dna._avariciusTalk > 0) + _vm->_visa->displayScrollChain('q', 23); + else + peopleInRoom(); + break; + case r__robins: + if (_vm->_gyro->_dna._tiedUp) + _vm->_visa->displayScrollChain('q', 38); + if (_vm->_gyro->_dna._mushroomGrowing) + _vm->_visa->displayScrollChain('q', 55); + break; + case r__insidecardiffcastle: + if (!_vm->_gyro->_dna._takenPen) + _vm->_visa->displayScrollChain('q', 49); + break; + case r__lustiesroom: + if (_vm->_gyro->_dna._lustieIsAsleep) + _vm->_visa->displayScrollChain('q', 65); + break; + case r__catacombs: + switch (_vm->_gyro->_dna._catacombY * 256 + _vm->_gyro->_dna._catacombX) { + case 258 : + _vm->_visa->displayScrollChain('q', 80); // Inside art gallery. + break; + case 514 : + _vm->_visa->displayScrollChain('q', 81); // Outside ditto. + break; + case 260 : + _vm->_visa->displayScrollChain('q', 82); // Outside Geida's room. + break; + } + break; + default: + peopleInRoom(); + } +} + +void Acci::openDoor() { + // Special cases. + switch (_vm->_gyro->_dna._room) { + case r__yours: + if (_vm->_animation->inField(2)) { + // Opening the box. + _thing = 54; // The box. + _person = kPardon; + examine(); + return; + } + break; + case r__spludwicks: + if (_thing == 61) { + _vm->_visa->displayScrollChain('q', 85); + return; + } + break; + } + + if ((!_vm->_gyro->_dna._userMovesAvvy) && (_vm->_gyro->_dna._room != r__lusties)) + return; // No doors can open if you can't move Avvy. + + for (byte fv = 8; fv < 15; fv++) { + if (_vm->_animation->inField(fv + 1)) { + fv -= 8; + + switch (_vm->_gyro->_portals[fv]._operation) { + case Gyro::kMagicExclaim: + _vm->_animation->_sprites[0].bounce(); + _vm->_visa->displayScrollChain('x', _vm->_gyro->_portals[fv]._data); + break; + case Gyro::kMagicTransport: + _vm->_animation->flipRoom((_vm->_gyro->_portals[fv]._data) >> 8, // High byte + (_vm->_gyro->_portals[fv]._data) & 0x0F // Low byte + ); + break; + case Gyro::kMagicUnfinished: + _vm->_animation->_sprites[0].bounce(); + _vm->_scrolls->displayText("Sorry. This place is not available yet!"); + break; + case Gyro::kMagicSpecial: + _vm->_animation->callSpecial(_vm->_gyro->_portals[fv]._data); + break; + case Gyro::kMagicOpenDoor: + _vm->_animation->openDoor((_vm->_gyro->_portals[fv]._data) >> 8, (_vm->_gyro->_portals[fv]._data) & 0x0F, fv + 9); + break; + } + + return; + } + } + + if (_vm->_gyro->_dna._room == r__map) + _vm->_scrolls->displayText(Common::String("Avvy, you can complete the whole game without ever going " + "to anywhere other than Argent, Birmingham, Cardiff, Nottingham and Norwich.")); + else + _vm->_scrolls->displayText("Door? What door?"); +} + + + +void Acci::silly() { + _vm->_scrolls->displayText("Don't be silly!"); +} + +void Acci::putProc() { + if (!isHolding()) + return; + + _thing2 -= 49; // Slip the second object. + char temp = _thing; + _thing = _thing2; + if (!isHolding()) + return; + _thing = temp; + + // Thing is the _thing which you're putting in. _thing2 is where you're putting it. + switch (_thing2) { + case Gyro::kObjectWine: + if (_thing == _vm->_gyro->kObjectOnion) { + if (_vm->_gyro->_dna._rottenOnion) + _vm->_scrolls->displayText("That's a bit like shutting the stable door after the horse has bolted!"); + else { // Put onion into wine? + if (_vm->_gyro->_dna._wineState != 3) + _vm->_scrolls->displayText(Common::String(_vm->_scrolls->kControlItalic) + "Oignon au vin" + + _vm->_scrolls->kControlRoman + " is a bit too strong for your tastes!"); + else { // Put onion into vinegar! Yes! + _vm->_gyro->_dna._onionInVinegar = true; + _vm->_lucerna->incScore(7); + _vm->_visa->displayScrollChain('u', 9); + } + } + } else + silly(); + break; + + case 54: + if (_vm->_gyro->_dna._room == r__yours) { // Put something into the box. + if (_vm->_gyro->_dna._boxContent != kNothing) + _vm->_scrolls->displayText("There's something in the box already, Avvy. Try taking that out first."); + else { + switch (_thing) { + case Gyro::kObjectMoney: + _vm->_scrolls->displayText("You'd better keep some ready cash on you!"); + break; + case Gyro::kObjectBell: + _vm->_scrolls->displayText("That's a silly place to keep a bell."); + break; + case Gyro::kObjectBodkin: + _vm->_scrolls->displayText("But you might need it!"); + break; + case Gyro::kObjectOnion: + _vm->_scrolls->displayText("Just give it to Spludwick, Avvy!"); + break; + default: // Put the object into the box... + if (_vm->_gyro->_dna._wearing == _thing) + _vm->_scrolls->displayText(Common::String("You'd better take ") + _vm->_gyro->getItem(_thing) + " off first!"); + else { + openBox(true); // Open box. + + _vm->_gyro->_dna._boxContent = _thing; + _vm->_gyro->_dna._objects[_thing - 1] = false; + _vm->_lucerna->refreshObjectList(); + _vm->_scrolls->displayText("OK, it's in the box."); + + openBox(false); // Shut box. + } + } + } + } else + silly(); + break; + + default: + silly(); + } +} + +/** + * Display text when ingredients are not in the right order + * @remarks Originally called 'not_in_order' + */ +void Acci::notInOrder() { + _vm->_scrolls->displayText(Common::String("Sorry, I need the ingredients in the right order for this potion. What I need next is ") + + _vm->_gyro->getItem(_vm->_gyro->kSpludwicksOrder[_vm->_gyro->_dna._givenToSpludwick]) + + _vm->_scrolls->kControlRegister + 2 + _vm->_scrolls->kControlSpeechBubble); +} + +/** + * Move Spludwick to cauldron + * @remarks Originally called 'go_to_cauldron' + */ +void Acci::goToCauldron() { + _vm->_animation->_sprites[1]._callEachStepFl = false; // Stops Geida_Procs. + _vm->_timer->addTimer(1, _vm->_timer->kProcSpludwickGoesToCauldron, _vm->_timer->kReasonSpludwickWalk); + _vm->_animation->_sprites[1].walkTo(2); +} + +/** + * Check is it's possible to give something to Spludwick + * @remarks Originally called 'give2spludwick' + */ +bool Acci::giveToSpludwick() { + if (_vm->_gyro->kSpludwicksOrder[_vm->_gyro->_dna._givenToSpludwick] != _thing) { + notInOrder(); + return false; + } + + switch (_thing) { + case Gyro::kObjectOnion: + _vm->_gyro->_dna._objects[_vm->_gyro->kObjectOnion - 1] = false; + if (_vm->_gyro->_dna._rottenOnion) + _vm->_visa->displayScrollChain('q', 22); + else { + _vm->_gyro->_dna._givenToSpludwick++; + _vm->_visa->displayScrollChain('q', 20); + goToCauldron(); + _vm->_lucerna->incScore(3); + } + _vm->_lucerna->refreshObjectList(); + break; + case Gyro::kObjectInk: + _vm->_gyro->_dna._objects[_vm->_gyro->kObjectInk - 1] = false; + _vm->_lucerna->refreshObjectList(); + _vm->_gyro->_dna._givenToSpludwick++; + _vm->_visa->displayScrollChain('q', 24); + goToCauldron(); + _vm->_lucerna->incScore(3); + break; + case Gyro::kObjectMushroom: + _vm->_gyro->_dna._objects[_vm->_gyro->kObjectMushroom - 1] = false; + _vm->_visa->displayScrollChain('q', 25); + _vm->_lucerna->incScore(5); + _vm->_gyro->_dna._givenToSpludwick++; + goToCauldron(); + _vm->_gyro->_dna._objects[_vm->_gyro->kObjectPotion - 1] = true; + _vm->_lucerna->refreshObjectList(); + break; + default: + return true; + } + + return false; +} + +void Acci::drink() { + _vm->_gyro->_dna._alcoholLevel += 1; + if (_vm->_gyro->_dna._alcoholLevel == 5) { + _vm->_gyro->_dna._objects[_vm->_gyro->kObjectKey - 1] = true; // Get the key. + _vm->_gyro->_dna._teetotal = true; + _vm->_gyro->_dna._avvyIsAwake = false; + _vm->_gyro->_dna._avvyInBed = true; + _vm->_lucerna->refreshObjectList(); + _vm->_lucerna->dusk(); + _vm->_gyro->hangAroundForAWhile(); + _vm->_animation->flipRoom(1, 1); + _vm->_gyro->setBackgroundColor(14); + _vm->_animation->_sprites[0]._visible = false; + } +} + +void Acci::cardiffClimbing() { + if (_vm->_gyro->_dna._standingOnDais) { // Clamber up. + _vm->_scrolls->displayText("You climb down, back onto the floor."); + _vm->_gyro->_dna._standingOnDais = false; + _vm->_animation->appearPed(1, 3); + } else { // Clamber down. + if (_vm->_animation->inField(1)) { + _vm->_scrolls->displayText("You clamber up onto the dais."); + _vm->_gyro->_dna._standingOnDais = true; + _vm->_animation->appearPed(1, 2); + } else + _vm->_scrolls->displayText("Get a bit closer, Avvy."); + } +} + +void Acci::already() { + _vm->_scrolls->displayText("You're already standing!"); +} + +void Acci::standUp() { + switch (_vm->_gyro->_dna._room) { + case r__yours: // Avvy isn't asleep. + if (_vm->_gyro->_dna._avvyIsAwake && _vm->_gyro->_dna._avvyInBed) { // But he's in bed. + if (_vm->_gyro->_dna._teetotal) { + _vm->_visa->displayScrollChain('d', 12); + _vm->_gyro->setBackgroundColor(0); + _vm->_visa->displayScrollChain('d', 14); + } + _vm->_animation->_sprites[0]._visible = true; + _vm->_gyro->_dna._userMovesAvvy = true; + _vm->_animation->appearPed(1, 2); + _vm->_gyro->_dna._direction = _vm->_gyro->kDirectionLeft; + _vm->_celer->drawBackgroundSprite(-1, -1, 4); // Picture of empty pillow. + _vm->_lucerna->incScore(1); + _vm->_gyro->_dna._avvyInBed = false; + _vm->_timer->loseTimer(_vm->_timer->kReasonArkataShouts); + } else + already(); + break; + + case r__insidecardiffcastle: + cardiffClimbing(); + break; + + case r__nottspub: + if (_vm->_gyro->_dna._sittingInPub) { + _vm->_celer->drawBackgroundSprite(-1, -1, 4); // Not sitting down. + _vm->_animation->_sprites[0]._visible = true; // But standing up. + _vm->_animation->appearPed(1, 4); // And walking away. + _vm->_gyro->_dna._sittingInPub = false; // Really not sitting down. + _vm->_gyro->_dna._userMovesAvvy = true; // And ambulant. + } else + already(); + break; + default: + already(); + } +} + + + +void Acci::getProc(char thing) { + switch (_vm->_gyro->_dna._room) { + case r__yours: + if (_vm->_animation->inField(2)) { + if (_vm->_gyro->_dna._boxContent == thing) { + _vm->_celer->drawBackgroundSprite(-1, -1, 5); + _vm->_scrolls->displayText("OK, I've got it."); + _vm->_gyro->_dna._objects[thing - 1] = true; + _vm->_lucerna->refreshObjectList(); + _vm->_gyro->_dna._boxContent = kNothing; + _vm->_celer->drawBackgroundSprite(-1, -1, 6); + } else + _vm->_scrolls->displayText(Common::String("I can't see ") + _vm->_gyro->getItem(thing) + " in the box."); + } else + _vm->_visa->displayScrollChain('q', 57); + break; + case r__insidecardiffcastle: + switch (thing) { + case Gyro::kObjectPen: + if (_vm->_animation->inField(2)) { // Standing on the dais. + if (_vm->_gyro->_dna._takenPen) + _vm->_scrolls->displayText("It's not there, Avvy."); + else { + // OK: we're taking the pen, and it's there. + _vm->_celer->drawBackgroundSprite(-1, -1, 4); // No pen there now. + _vm->_animation->callSpecial(3); // Zap! + _vm->_gyro->_dna._takenPen = true; + _vm->_gyro->_dna._objects[_vm->_gyro->kObjectPen - 1] = true; + _vm->_lucerna->refreshObjectList(); + _vm->_scrolls->displayText("Taken."); + } + } else if (_vm->_gyro->_dna._standingOnDais) + _vm->_visa->displayScrollChain('q', 53); + else + _vm->_visa->displayScrollChain('q', 51); + break; + case Gyro::kObjectBolt: + _vm->_visa->displayScrollChain('q', 52); + break; + default: + _vm->_visa->displayScrollChain('q', 57); + } + break; + case r__robins: + if ((thing == _vm->_gyro->kObjectMushroom) & (_vm->_animation->inField(1)) & (_vm->_gyro->_dna._mushroomGrowing)) { + _vm->_celer->drawBackgroundSprite(-1, -1, 3); + _vm->_scrolls->displayText("Got it!"); + _vm->_gyro->_dna._mushroomGrowing = false; + _vm->_gyro->_dna._takenMushroom = true; + _vm->_gyro->_dna._objects[_vm->_gyro->kObjectMushroom - 1] = true; + _vm->_lucerna->refreshObjectList(); + _vm->_lucerna->incScore(3); + } else + _vm->_visa->displayScrollChain('q', 57); + break; + default: + _vm->_visa->displayScrollChain('q', 57); + } +} + +/** + * Give the lute to Geida + * @remarks Originally called 'give_Geida_the_lute' + */ +void Acci::giveGeidaTheLute() { + if (_vm->_gyro->_dna._room != r__lustiesroom) { + _vm->_scrolls->displayText(Common::String("Not yet. Try later!") + _vm->_scrolls->kControlRegister + '2' + _vm->_scrolls->kControlSpeechBubble); + return; + } + _vm->_gyro->_dna._objects[_vm->_gyro->kObjectLute - 1] = false; + _vm->_lucerna->refreshObjectList(); + _vm->_visa->displayScrollChain('q', 64); // She plays it. + + _vm->_timer->addTimer(1, _vm->_timer->kProcGiveLuteToGeida, _vm->_timer->kReasonGeidaSings); + _vm->_enid->backToBootstrap(4); +} + +void Acci::playHarp() { + if (_vm->_animation->inField(7)) + _vm->_scrolls->musicalScroll(); + else + _vm->_scrolls->displayText("Get a bit closer to it, Avvy!"); +} + +void Acci::winSequence() { + _vm->_visa->displayScrollChain('q', 78); + _vm->_sequence->firstShow(7); + _vm->_sequence->thenShow(8); + _vm->_sequence->thenShow(9); + _vm->_sequence->startToClose(); + _vm->_timer->addTimer(30, _vm->_timer->kProcWinning, _vm->_timer->kReasonWinning); +} + +void Acci::personSpeaks() { + if ((_person == kPardon) || (_person == 0)) { + if ((_vm->_gyro->_him == kPardon) || (_vm->_gyro->_whereIs[_vm->_gyro->_him - 150] != _vm->_gyro->_dna._room)) + _person = _vm->_gyro->_her; + else + _person = _vm->_gyro->_him; + } + + if (_vm->_gyro->_whereIs[_person - 150] != _vm->_gyro->_dna._room) { + _vm->_scrolls->displayText(Common::String(_vm->_scrolls->kControlRegister) + '1' + _vm->_scrolls->kControlToBuffer); // Avvy himself! + return; + } + + bool found = false; // The _person we're looking for's code is in _person. + + for (byte i = 0; i < _vm->_animation->kSpriteNumbMax; i++) { + if (_vm->_animation->_sprites[i]._quick && ((_vm->_animation->_sprites[i]._stat._acciNum + 149) == _person)) { + _vm->_scrolls->displayText(Common::String(_vm->_scrolls->kControlRegister) + byte(i + 49) + _vm->_scrolls->kControlToBuffer); + found = true; + } + } + + if (!found) { + for (byte i = 0; i < 16; i++) { + if ((_vm->_gyro->kQuasipeds[i]._who == _person) && (_vm->_gyro->kQuasipeds[i]._room == _vm->_gyro->_dna._room)) + _vm->_scrolls->displayText(Common::String(_vm->_scrolls->kControlRegister) + byte(i + 65) + _vm->_scrolls->kControlToBuffer); + } + } +} +void Acci::heyThanks() { + personSpeaks(); + _vm->_scrolls->displayText(Common::String("Hey, thanks!") + _vm->_scrolls->kControlSpeechBubble + "(But now, you've lost it!)"); + _vm->_gyro->_dna._objects[_thing - 1] = false; +} + +/** + * @remarks Originally called 'do_that' + */ +void Acci::doThat() { + static const Common::String booze[] = {"Bitter", "GIED", "Whisky", "Cider", "", "", "", "Mead"}; + static const char kWhat[] = "That's not possible!"; + + if (_thats == Common::String(kNothing)) { + if (!_thats.empty()) + _thats.clear(); + return; + } + + if (_vm->_gyro->_weirdWord) + return; + + if (_thing < 200) + _thing -= 49; // "Slip" + + + if ((_verb != kVerbCodeLoad) && (_verb != kVerbCodeSave) && (_verb != kVerbCodeQuit) && (_verb != kVerbCodeInfo) && (_verb != kVerbCodeHelp) + && (_verb != kVerbCodeLarrypass) && (_verb != kVerbCodePhaon) && (_verb != kVerbCodeBoss) && (_verb != kVerbCodeCheat) && (_verb != kVerbCodeRestart) + && (_verb != kVerbCodeDir) && (_verb != kVerbCodeScore) && (_verb != kVerbCodeHiscores) && (_verb != kVerbCodeSmartAlec)) { + if (!_vm->_gyro->_alive) { + _vm->_scrolls->displayText(Common::String("You're dead, so don't talk. What are you, a ghost or something? Try restarting, or restoring a saved game!")); + return; + } + if (!_vm->_gyro->_dna._avvyIsAwake && (_verb != kVerbCodeDie) && (_verb != kVerbCodeExpletive) && (_verb != kVerbCodeWake)) { + _vm->_scrolls->displayText("Talking in your sleep? Try waking up!"); + return; + } + } + + switch (_verb) { + case kVerbCodeExam: + examine(); + break; + case kVerbCodeOpen: + openDoor(); + break; + case kVerbCodePause: // Note that the original game doesn't care about the "O.K." box neither, it accepts clicks from everywhere on the screen to continue. Just like my code. + _vm->_scrolls->displayText(Common::String("Game paused.") + _vm->_scrolls->kControlCenter + _vm->_scrolls->kControlNewLine + _vm->_scrolls->kControlNewLine + + "Press Enter, Esc, or click the mouse on the \"O.K.\" box to continue."); + break; + case kVerbCodeGet: + if (_thing != kPardon) { // Legitimate try to pick something up. + if (_vm->_gyro->_dna._carryNum >= kCarryLimit) + _vm->_scrolls->displayText("You can't carry any more!"); + else + getProc(_thing); + } else { // Not... ditto. + if (_person != kPardon) + _vm->_scrolls->displayText("You can't sweep folk off their feet!"); + else + _vm->_scrolls->displayText("I assure you, you don't need it."); + } + break; + case kVerbCodeDrop: + _vm->_scrolls->displayText(Common::String("Two years ago you dropped a florin in the street. Three days ") + + "later it was gone! So now you never leave ANYTHING lying around. OK?"); + break; + case kVerbCodeInv: + inventory(); + break; + case kVerbCodeTalk: + if (_person == kPardon) { + if (_vm->_gyro->_subjectNum == 99) // They typed "say password". + _vm->_scrolls->displayText(Common::String("Yes, but what ") + _vm->_scrolls->kControlItalic + "is" + _vm->_scrolls->kControlRoman + " the password?"); + else if (((1 <= _vm->_gyro->_subjectNum) && (_vm->_gyro->_subjectNum <= 49)) || (_vm->_gyro->_subjectNum == 253) || (_vm->_gyro->_subjectNum == 249)) { + _thats.deleteChar(0); + + for (byte i = 0; i < 10; i++) + _realWords[i] = _realWords[i + 1]; + + _verb = _vm->_gyro->_subjectNum; + doThat(); + return; + } else { + _person = _vm->_gyro->_subjectNum; + _vm->_gyro->_subjectNum = 0; + if ((_person == 0) || (_person == kPardon)) + _vm->_scrolls->displayText("Talk to whom?"); + else if (isPersonHere()) + _vm->_visa->talkTo(_person); + } + } else if (isPersonHere()) + _vm->_visa->talkTo(_person); + break; + case kVerbCodeGive: + if (isHolding()) { + if (_person == kPardon) + _vm->_scrolls->displayText("Give to whom?"); + else if (isPersonHere()) { + switch (_thing) { + case Gyro::kObjectMoney : + _vm->_scrolls->displayText("You can't bring yourself to give away your moneybag."); + break; + case Gyro::kObjectBodkin: + case Gyro::kObjectBell: + case Gyro::kObjectClothes: + case Gyro::kObjectHabit : + _vm->_scrolls->displayText("Don't give it away, it might be useful!"); + break; + default: + switch (_person) { + case Gyro::kPeopleCrapulus: + if (_thing == _vm->_gyro->kObjectWine) { + _vm->_scrolls->displayText("Crapulus grabs the wine and gulps it down."); + _vm->_gyro->_dna._objects[_vm->_gyro->kObjectWine - 1] = false; + } else + heyThanks(); + break; + case Gyro::kPeopleCwytalot: + if ((_thing == _vm->_gyro->kObjectCrossbow) || (_thing == _vm->_gyro->kObjectBolt)) + _vm->_scrolls->displayText(Common::String("You might be able to influence Cwytalot more if you used it!")); + else + heyThanks(); + break; + case Gyro::kPeopleSpludwick: + if (giveToSpludwick()) + heyThanks(); + break; + case Gyro::kPeopleIbythneth: + if (_thing == _vm->_gyro->kObjectBadge) { + _vm->_visa->displayScrollChain('q', 32); // Thanks! Wow! + _vm->_lucerna->incScore(3); + _vm->_gyro->_dna._objects[_vm->_gyro->kObjectBadge - 1] = false; + _vm->_gyro->_dna._objects[_vm->_gyro->kObjectHabit - 1] = true; + _vm->_gyro->_dna._givenBadgeToIby = true; + _vm->_celer->drawBackgroundSprite(-1, -1, 8); + _vm->_celer->drawBackgroundSprite(-1, -1, 9); + } else + heyThanks(); + break; + case Gyro::kPeopleAyles: + if (_vm->_gyro->_dna._aylesIsAwake) { + if (_thing == _vm->_gyro->kObjectPen) { + _vm->_gyro->_dna._objects[_vm->_gyro->kObjectPen - 1] = false; + _vm->_visa->displayScrollChain('q', 54); + _vm->_gyro->_dna._objects[_vm->_gyro->kObjectInk - 1] = true; + _vm->_gyro->_dna._givenPenToAyles = true; + _vm->_lucerna->refreshObjectList(); + _vm->_lucerna->incScore(2); + } else + heyThanks(); + } else + _vm->_scrolls->displayText("But he's asleep!"); + break; + case Gyro::kPeopleGeida: + switch (_thing) { + case Gyro::kObjectPotion: + _vm->_gyro->_dna._objects[_vm->_gyro->kObjectPotion - 1] = false; + _vm->_visa->displayScrollChain('u', 16); // She drinks it. + _vm->_lucerna->incScore(2); + _vm->_gyro->_dna._givenPotionToGeida = true; + _vm->_lucerna->refreshObjectList(); + break; + case Gyro::kObjectLute: + giveGeidaTheLute(); + break; + default: + heyThanks(); + } + break; + case Gyro::kPeopleArkata: + switch (_thing) { + case Gyro::kObjectPotion: + if (_vm->_gyro->_dna._givenPotionToGeida) + winSequence(); + else + _vm->_visa->displayScrollChain('q', 77); // That Geida woman! + break; + default: + heyThanks(); + } + break; + default: + heyThanks(); + } + } + } + _vm->_lucerna->refreshObjectList(); // Just in case... + } + break; + + case kVerbCodeEat: + case kVerbCodeDrink: + if (isHolding()) + swallow(); + break; + + case kVerbCodeLoad: + break; + case kVerbCodeSave: + break; + // We don't handle these two because we use ScummVM's save/load system. + + case kVerbCodePay: + _vm->_scrolls->displayText("No money need change hands."); + break; + case kVerbCodeLook: + lookAround(); + break; + case kVerbCodeBreak: + _vm->_scrolls->displayText("Vandalism is prohibited within this game!"); + break; + case kVerbCodeQuit: // quit + if (_vm->_gyro->kDemo) { + warning("STUB: Acci::doThat() - case kVerbCodequit"); + // _vm->_visa->displayScrollChain('pos', 31); + // close(demofile); + // exit(0); // Change this later!!! + } + if (!_polite) + _vm->_scrolls->displayText("How about a `please\", Avvy?"); + else if (_vm->_scrolls->displayQuestion(Common::String(_vm->_scrolls->kControlRegister) + 'C' + _vm->_scrolls->kControlIcon + "Do you really want to quit?")) + _vm->_gyro->_letMeOut = true; + break; + case kVerbCodeGo: + _vm->_scrolls->displayText("Just use the arrow keys to walk there."); + break; + case kVerbCodeInfo: { + _vm->_scrolls->_aboutScroll = true; + + Common::String toDisplay; + for (byte i = 0; i < 7; i++) + toDisplay += _vm->_scrolls->kControlNewLine; + toDisplay = toDisplay + "LORD AVALOT D'ARGENT" + _vm->_scrolls->kControlCenter + _vm->_scrolls->kControlNewLine + + "The medi\x91val descendant of" + _vm->_scrolls->kControlNewLine + + "Denarius Avaricius Sextus" + _vm->_scrolls->kControlNewLine + _vm->_scrolls->kControlNewLine + + "version " + _vm->_gyro->kVersionNum + _vm->_scrolls->kControlNewLine + _vm->_scrolls->kControlNewLine + "Copyright \xEF " + + _vm->_gyro->kCopyright + ", Mark, Mike and Thomas Thurman." + _vm->_scrolls->kControlRegister + 'Y' + _vm->_scrolls->kControlIcon; + _vm->_scrolls->displayText(toDisplay); + _vm->_scrolls->_aboutScroll = false; + } + break; + case kVerbCodeUndress: + if (_vm->_gyro->_dna._wearing == kNothing) + _vm->_scrolls->displayText("You're already stark naked!"); + else if (_vm->_gyro->_dna._avvysInTheCupboard) { + _vm->_scrolls->displayText(Common::String("You take off ") + _vm->_gyro->getItem(_vm->_gyro->_dna._wearing) + '.'); + _vm->_gyro->_dna._wearing = kNothing; + _vm->_lucerna->refreshObjectList(); + } else + _vm->_scrolls->displayText("Hadn't you better find somewhere more private, Avvy?"); + break; + case kVerbCodeWear: + if (isHolding()) { // Wear something. + switch (_thing) { + case Gyro::kObjectChastity: + // \? are used to avoid that ??! is parsed as a trigraph + _vm->_scrolls->displayText("Hey, what kind of a weirdo are you\?\?!"); + break; + case Gyro::kObjectClothes: + case Gyro::kObjectHabit: { // Change this! + if (_vm->_gyro->_dna._wearing != kNothing) { + if (_vm->_gyro->_dna._wearing == _thing) + _vm->_scrolls->displayText("You're already wearing that."); + else + _vm->_scrolls->displayText("You'll be rather warm wearing two sets of clothes!"); + return; + } else + _vm->_gyro->_dna._wearing = _thing; + + _vm->_lucerna->refreshObjectList(); + + byte i; + if (_thing == _vm->_gyro->kObjectHabit) + i = 3; + else + i = 0; + if (_vm->_animation->_sprites[0]._id != i) { + int16 x = _vm->_animation->_sprites[0]._x; + int16 y = _vm->_animation->_sprites[0]._y; + _vm->_animation->_sprites[0].remove(); + _vm->_animation->_sprites[0].init(i, true, _vm->_animation); + _vm->_animation->_sprites[0].appear(x, y, Animation::kDirLeft); + _vm->_animation->_sprites[0]._visible = false; + } + } + break; + default: + _vm->_scrolls->displayText(kWhat); + } + } + break; + case kVerbCodePlay: + if (_thing == kPardon) { + switch (_vm->_gyro->_dna._room) { // They just typed "play"... + case r__argentpub: { // ...in the pub, => play Nim. + warning("STUB: Acci::doThat() - case kVerbCodeplay"); + // play_nim(); + // The following parts are copied from play_nim(). + // The player automatically wins the game everytime he wins, until I implement the mini-game. + + if (_vm->_gyro->_dna._wonNim) { // Already won the game. + _vm->_visa->displayScrollChain('Q', 6); + return; + } + + if (!_vm->_gyro->_dna._askedDogfoodAboutNim) { + _vm->_visa->displayScrollChain('q', 84); + return; + } + + _vm->_visa->displayScrollChain('Q', 3); + _vm->_gyro->_dna._playedNim++; + + // You won - strange! + _vm->_visa->displayScrollChain('Q', 7); // You won! Give us a lute! + _vm->_gyro->_dna._objects[_vm->_gyro->kObjectLute - 1] = true; + _vm->_lucerna->refreshObjectList(); + _vm->_gyro->_dna._wonNim = true; + _vm->_celer->drawBackgroundSprite(-1, -1, 1); // Show the settle with no lute on it. + _vm->_lucerna->incScore(7); // 7 points for winning! + + if (_vm->_gyro->_dna._playedNim == 1) + _vm->_lucerna->incScore(3); // 3 points for playing your 1st game. + + // A warning to the player that there should have been a mini-game. TODO: Remove it later!!! + _vm->_scrolls->displayText(Common::String("P.S.: There should have been the mini-game called \"Nim\", but I haven't implemented it yet: you win and get the lute automatically.") + + _vm->_scrolls->kControlNewLine + _vm->_scrolls->kControlNewLine + "Peter (uruk)"); + } + break; + case r__musicroom: + playHarp(); + break; + } + } else if (isHolding()) { + switch (_thing) { + case Gyro::kObjectLute : + _vm->_visa->displayScrollChain('U', 7); + + if (_vm->_gyro->_whereIs[_vm->_gyro->kPeopleCwytalot - 150] == _vm->_gyro->_dna._room) + _vm->_visa->displayScrollChain('U', 10); + + if (_vm->_gyro->_whereIs[_vm->_gyro->kPeopleDuLustie - 150] == _vm->_gyro->_dna._room) + _vm->_visa->displayScrollChain('U', 15); + break; + case 52: + if (_vm->_gyro->_dna._room == r__musicroom) + playHarp(); + else + _vm->_scrolls->displayText(kWhat); + break; + case 55: + if (_vm->_gyro->_dna._room == r__argentpub) + // play_nim(); + warning("STUB: Acci::doThat() - case kVerbCodeplay"); + else + _vm->_scrolls->displayText(kWhat); + break; + default: + _vm->_scrolls->displayText(kWhat); + } + } + break; + case kVerbCodeRing: + if (isHolding()) { + if (_thing == _vm->_gyro->kObjectBell) { + _vm->_scrolls->displayText("Ding, dong, ding, dong, ding, dong, ding, dong..."); + if ((_vm->_gyro->_dna._bellsAreRinging) & (_vm->_gyro->setFlag('B'))) + // \? are used to avoid that ??! is parsed as a trigraph + _vm->_scrolls->displayText("(Are you trying to join in, Avvy\?\?!)"); + } else + _vm->_scrolls->displayText(kWhat); + } + break; + case kVerbCodeHelp: + // boot_help(); + warning("STUB: Acci::doThat() - case kVerbCodehelp"); + break; + case kVerbCodeLarrypass: + _vm->_scrolls->displayText("Wrong game!"); + break; + case kVerbCodePhaon: + _vm->_scrolls->displayText("Hello, Phaon!"); + break; + case kVerbCodeBoss: + // bosskey(); + warning("STUB: Acci::doThat() - case kVerbCodeboss"); + break; + case kVerbCodePee: + if (_vm->_gyro->setFlag('P')) { + _vm->_scrolls->displayText("Hmm, I don't think anyone will notice..."); + _vm->_timer->addTimer(4, _vm->_timer->kProcUrinate, _vm->_timer->kReasonGoToToilet); + } else + _vm->_scrolls->displayText(Common::String("It would be ") + _vm->_scrolls->kControlItalic + "VERY" + + _vm->_scrolls->kControlRoman + " unwise to do that here, Avvy!"); + break; + case kVerbCodeCheat: + _vm->_scrolls->displayText(Common::String(_vm->_scrolls->kControlItalic) + "Cheat mode now enabled."); + _vm->_gyro->_cheat = true; + break; + case kVerbCodeMagic: + if (_vm->_gyro->_dna._avariciusTalk > 0) + _vm->_visa->displayScrollChain('q', 19); + else { + if ((_vm->_gyro->_dna._room == 12) & (_vm->_animation->inField(2))) { // Avaricius appears! + _vm->_visa->displayScrollChain('q', 17); + if (_vm->_gyro->_whereIs[1] == 12) + _vm->_visa->displayScrollChain('q', 18); + else { + _vm->_animation->_sprites[1].init(1, false, _vm->_animation); // Avaricius + _vm->_animation->appearPed(2, 4); + _vm->_animation->_sprites[1].walkTo(5); + _vm->_animation->_sprites[1]._callEachStepFl = true; + _vm->_animation->_sprites[1]._eachStepProc = _vm->_animation->kProcBackAndForth; + _vm->_gyro->_dna._avariciusTalk = 14; + _vm->_timer->addTimer(177, _vm->_timer->kProcAvariciusTalks, _vm->_timer->kReasonAvariciusTalks); + } + } else + _vm->_scrolls->displayText("Nothing appears to happen..."); + } + break; + case kVerbCodeSmartAlec: + _vm->_scrolls->displayText("Listen, smart alec, that was just rhetoric."); + break; + case kVerbCodeExpletive: + switch (_vm->_gyro->_dna._sworeNum) { + case 0: + _vm->_scrolls->displayText(Common::String("Avvy! Do you mind? There might be kids playing!") + + _vm->_scrolls->kControlNewLine + _vm->_scrolls->kControlNewLine + "(I shouldn't say it again, if I were you!)"); + break; + case 1: + _vm->_scrolls->displayText(Common::String("You hear a distant rumble of thunder. Must you always do things I tell you not to?") + + _vm->_scrolls->kControlNewLine + _vm->_scrolls->kControlNewLine + "Don't do it again!"); + break; + default: + _vm->_pingo->zonk(); + _vm->_scrolls->displayText(Common::String("A crack of lightning shoots from the sky, and fries you.") + + _vm->_scrolls->kControlNewLine + _vm->_scrolls->kControlNewLine + "(`Such is the anger of the gods, Avvy!\")"); + _vm->_lucerna->gameOver(); + } + _vm->_gyro->_dna._sworeNum++; + break; + case kVerbCodeListen: + if ((_vm->_gyro->_dna._bellsAreRinging) & (_vm->_gyro->setFlag('B'))) + _vm->_scrolls->displayText("All other noise is drowned out by the ringing of the bells."); + else if (_vm->_gyro->_listen.empty()) + _vm->_scrolls->displayText("You can't hear anything much at the moment, Avvy."); + else + _vm->_scrolls->displayText(_vm->_gyro->_listen); + break; + case kVerbCodeBuy: // What are they trying to buy? + switch (_vm->_gyro->_dna._room) { + case r__argentpub: + if (_vm->_animation->inField(6)) { // We're in a pub, and near the bar. + switch (_thing) { + case 51: + case 53: + case 54: + case 58: // Beer, whisky, cider or mead. + if (_vm->_gyro->_dna._malagauche == 177) { // Already getting us one. + _vm->_visa->displayScrollChain('D', 15); + return; + } + + if (_vm->_gyro->_dna._teetotal) { + _vm->_visa->displayScrollChain('D', 6); + return; + } + + if (_vm->_gyro->_dna._alcoholLevel == 0) + _vm->_lucerna->incScore(3); + + _vm->_celer->drawBackgroundSprite(-1, -1, 12); + _vm->_scrolls->displayText(booze[_thing - 51] + ", please." + _vm->_scrolls->kControlRegister + '1' + _vm->_scrolls->kControlSpeechBubble); + _vm->_gyro->_dna._drinking = _thing; + + _vm->_celer->drawBackgroundSprite(-1, -1, 10); + _vm->_gyro->_dna._malagauche = 177; + _vm->_timer->addTimer(27, _vm->_timer->kProcBuyDrinks, _vm->_timer->kReasonDrinks); + break; + case 52: + examine(); + break; // We have a right one here - buy Pepsi??! + case Gyro::kObjectWine: + if (_vm->_gyro->_dna._objects[_vm->_gyro->kObjectWine - 1]) // We've already got the wine! + _vm->_visa->displayScrollChain('D', 2); // 1 bottle's shufishent! + else { + if (_vm->_gyro->_dna._malagauche == 177) { // Already getting us one. + _vm->_visa->displayScrollChain('D', 15); + return; + } + + if (_vm->_gyro->_dna._carryNum >= kCarryLimit) { + _vm->_scrolls->displayText("Your hands are full."); + return; + } + + _vm->_celer->drawBackgroundSprite(-1, -1, 12); + _vm->_scrolls->displayText(Common::String("Wine, please.") + _vm->_scrolls->kControlRegister + '1' + _vm->_scrolls->kControlSpeechBubble); + if (_vm->_gyro->_dna._alcoholLevel == 0) + _vm->_lucerna->incScore(3); + _vm->_celer->drawBackgroundSprite(-1, -1, 10); + _vm->_gyro->_dna._malagauche = 177; + + _vm->_timer->addTimer(27, _vm->_timer->kProcBuyWine, _vm->_timer->kReasonDrinks); + } + break; + } + } else + _vm->_visa->displayScrollChain('D', 5); // Go to the bar! + break; + + case r__outsideducks: + if (_vm->_animation->inField(6)) { + if (_thing == _vm->_gyro->kObjectOnion) { + if (_vm->_gyro->_dna._objects[_vm->_gyro->kObjectOnion - 1]) + _vm->_visa->displayScrollChain('D', 10); // Not planning to juggle with the things! + else if (_vm->_gyro->_dna._carryNum >= kCarryLimit) + _vm->_scrolls->displayText("Before you ask, you remember that your hands are full."); + else { + if (_vm->_gyro->_dna._boughtOnion) + _vm->_visa->displayScrollChain('D', 11); + else { + _vm->_visa->displayScrollChain('D', 9); + _vm->_lucerna->incScore(3); + } + _vm->_gyro->decreaseMoney(3); // It costs thruppence. + _vm->_gyro->_dna._objects[_vm->_gyro->kObjectOnion - 1] = true; + _vm->_lucerna->refreshObjectList(); + _vm->_gyro->_dna._boughtOnion = true; + _vm->_gyro->_dna._rottenOnion = false; // It's OK when it leaves the stall! + _vm->_gyro->_dna._onionInVinegar = false; + } + } else + _vm->_visa->displayScrollChain('D', 0); + } else + _vm->_visa->displayScrollChain('D', 0); + break; + + case r__nottspub: + _vm->_visa->displayScrollChain('n', 15); // Can't sell to southerners. + break; + default: + _vm->_visa->displayScrollChain('D', 0); // Can't buy that. + } + break; + case kVerbCodeAttack: + if ((_vm->_gyro->_dna._room == r__brummieroad) && + ((_person == 157) || (_thing == _vm->_gyro->kObjectCrossbow) || (_thing == _vm->_gyro->kObjectBolt)) + && (_vm->_gyro->_whereIs[7] == _vm->_gyro->_dna._room)) { + switch (_vm->_gyro->_dna._objects[_vm->_gyro->kObjectBolt - 1] + _vm->_gyro->_dna._objects[_vm->_gyro->kObjectCrossbow - 1] * 2) { + // 0 = neither, 1 = only bolt, 2 = only crossbow, 3 = both. + case 0: + _vm->_visa->displayScrollChain('Q', 10); + _vm->_scrolls->displayText("(At the very least, don't use your bare hands!)"); + break; + case 1: + _vm->_scrolls->displayText("Attack _vm->_gyro->him with only a crossbow bolt? Are you planning on playing darts?!"); + break; + case 2: + _vm->_scrolls->displayText("Come on, Avvy! You're not going to get very far with only a crossbow!"); + break; + case 3: + _vm->_visa->displayScrollChain('Q', 11); + _vm->_gyro->_dna._cwytalotGone = true; + _vm->_gyro->_dna._objects[_vm->_gyro->kObjectBolt - 1] = false; + _vm->_gyro->_dna._objects[_vm->_gyro->kObjectCrossbow - 1] = false; + _vm->_lucerna->refreshObjectList(); + _vm->_gyro->_magics[11]._operation = _vm->_gyro->kMagicNothing; + _vm->_lucerna->incScore(7); + _vm->_animation->_sprites[1].walkTo(2); + _vm->_animation->_sprites[1]._vanishIfStill = true; + _vm->_animation->_sprites[1]._callEachStepFl = false; + _vm->_gyro->_whereIs[7] = 177; + break; + default: + _vm->_visa->displayScrollChain('Q', 10); // Please try not to be so violent! + } + } else + _vm->_visa->displayScrollChain('Q', 10); + break; + case kVerbCodePasswd: + if (_vm->_gyro->_dna._room != r__bridge) + _vm->_visa->displayScrollChain('Q', 12); + else { + bool ok = true; + for (byte i = 0; i < _thats.size(); i++) { + Common::String temp = _realWords[i]; + temp.toUppercase(); + for (byte j = 0; j < kVocabulary[_vm->_gyro->_dna._passwordNum + kFirstPassword]._word.size(); j++) { + if (kVocabulary[_vm->_gyro->_dna._passwordNum + kFirstPassword]._word[j] != temp[j]) + ok = false; + } + } + + if (ok) { + if (_vm->_gyro->_dna._drawbridgeOpen != 0) + _vm->_scrolls->displayText("Contrary to your expectations, the drawbridge fails to close again."); + else { + _vm->_lucerna->incScore(4); + _vm->_scrolls->displayText("The drawbridge opens!"); + _vm->_timer->addTimer(7, _vm->_timer->kProcOpenDrawbridge, _vm->_timer->kReasonDrawbridgeFalls); + _vm->_gyro->_dna._drawbridgeOpen = 1; + } + } else + _vm->_visa->displayScrollChain('Q', 12); + } + break; + case kVerbCodeDir: + _vm->_enid->dir(_realWords[1]); + break; + case kVerbCodeDie: + _vm->_lucerna->gameOver(); + break; + case kVerbCodeScore: + _vm->_scrolls->displayText(Common::String("Your score is ") + _vm->_gyro->intToStr(_vm->_gyro->_dna._score) + ',' + _vm->_scrolls->kControlCenter + + _vm->_scrolls->kControlNewLine + "out of a possible 128." + _vm->_scrolls->kControlNewLine + _vm->_scrolls->kControlNewLine + + "This gives you a rank of " + rank() + '.' + _vm->_scrolls->kControlNewLine + _vm->_scrolls->kControlNewLine + totalTime()); + break; + case kVerbCodePut: + putProc(); + break; + case kVerbCodeStand: + standUp(); + break; + case kVerbCodeKiss: + if (_person == kPardon) + _vm->_scrolls->displayText("Kiss whom?"); + else if (isPersonHere()) { + switch (_person) { + case Gyro::kPeopleArkata: + _vm->_visa->displayScrollChain('U', 12); + break; + case Gyro::kPeopleGeida: + _vm->_visa->displayScrollChain('U', 13); + break; + case Gyro::kPeopleWisewoman: + _vm->_visa->displayScrollChain('U', 14); + break; + default: + _vm->_visa->displayScrollChain('U', 5); // You WHAT? + } + } else if ((150 <= _person) && (_person <= 174)) + _vm->_scrolls->displayText("Hey, what kind of a weirdo are you??"); + + break; + case kVerbCodeClimb: + if (_vm->_gyro->_dna._room == r__insidecardiffcastle) + cardiffClimbing(); + else // In the wrong room! + _vm->_scrolls->displayText("Not with your head for heights, Avvy!"); + break; + case kVerbCodeJump: + _vm->_timer->addTimer(1, _vm->_timer->kProcJump, _vm->_timer->kReasonJumping); + _vm->_gyro->_dna._userMovesAvvy = false; + break; + case kVerbCodeHiscores: + // show_highs(); + warning("STUB: Acci::doThat() - case kVerbCodehighscores"); + break; + case kVerbCodeWake: + if (isPersonHere()) + switch (_person) { + case kPardon: + case Gyro::kPeopleAvalot: + case 0: + if (!_vm->_gyro->_dna._avvyIsAwake) { + _vm->_gyro->_dna._avvyIsAwake = true; + _vm->_lucerna->incScore(1); + _vm->_gyro->_dna._avvyInBed = true; + _vm->_celer->drawBackgroundSprite(-1, -1, 3); // Picture of Avvy, awake in bed. + if (_vm->_gyro->_dna._teetotal) + _vm->_visa->displayScrollChain('d', 13); + } else + _vm->_scrolls->displayText("You're already awake, Avvy!"); + break; + case Gyro::kPeopleAyles: + if (!_vm->_gyro->_dna._aylesIsAwake) + _vm->_scrolls->displayText("You can't seem to wake him by yourself."); + break; + case Gyro::kPeopleJacques: + _vm->_scrolls->displayText(Common::String("Brother Jacques, Brother Jacques, are you asleep?") + _vm->_scrolls->kControlRegister + '1' + + _vm->_scrolls->kControlSpeechBubble + "Hmmm... that doesn't seem to do any good..."); + break; + default: + _vm->_scrolls->displayText("It's difficult to awaken people who aren't asleep...!"); + } + break; + case kVerbCodeSit: + if (_vm->_gyro->_dna._room == r__nottspub) { + if (_vm->_gyro->_dna._sittingInPub) + _vm->_scrolls->displayText("You're already sitting!"); + else { + _vm->_animation->_sprites[0].walkTo(4); // Move Avvy to the place, and sit him down. + _vm->_timer->addTimer(1, _vm->_timer->kProcAvvySitDown, _vm->_timer->kReasonSittingDown); + } + } else { // Default doodah. + _vm->_lucerna->dusk(); + _vm->_gyro->hangAroundForAWhile(); + _vm->_lucerna->dawn(); + _vm->_scrolls->displayText(Common::String("A few hours later...") + _vm->_scrolls->kControlParagraph + "nothing much has happened..."); + } + break; + case kVerbCodeRestart: + if (_vm->_scrolls->displayQuestion("Restart game and lose changes?")) { + _vm->_lucerna->dusk(); + _vm->_gyro->newGame(); + _vm->_lucerna->dawn(); + } + break; + case kPardon: + _vm->_scrolls->displayText("Hey, a verb would be helpful!"); + break; + case kVerbCodeHello: + personSpeaks(); + _vm->_scrolls->displayText(Common::String("Hello.") + _vm->_scrolls->kControlSpeechBubble); + break; + case kVerbCodeThanks: + personSpeaks(); + _vm->_scrolls->displayText(Common::String("That's OK.") + _vm->_scrolls->kControlSpeechBubble); + break; + default: + _vm->_scrolls->displayText(Common::String(_vm->_scrolls->kControlBell) + "Parser bug!"); + } +} + +void Acci::verbOpt(byte verb, Common::String &answer, char &ansKey) { + switch (verb) { + case kVerbCodeExam: + answer = "Examine"; + ansKey = 'x'; + break; // The ubiquitous one. + // kVerbCodegive isn't dealt with by this procedure, but by ddm__with. + case kVerbCodeDrink: + answer = "Drink"; + ansKey = 'D'; + break; + case kVerbCodeWear: + answer = "Wear"; + ansKey = 'W'; + break; + case kVerbCodeRing: + answer = "Ring"; + ansKey = 'R'; + break; // Only the bell! + case kVerbCodePlay: + answer = "Play"; + ansKey = 'P'; + break; + case kVerbCodeEat: + answer = "Eat"; + ansKey = 'E'; + break; + default: + answer = "? Unknown!"; // Bug! + ansKey = '?'; + } +} + +} // End of namespace Avalanche. diff --git a/engines/avalanche/acci2.h b/engines/avalanche/acci2.h new file mode 100644 index 0000000000..55ad64e65f --- /dev/null +++ b/engines/avalanche/acci2.h @@ -0,0 +1,133 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on the original source code of Lord Avalot d'Argent version 1.3. + * Copyright (c) 1994-1995 Mike, Mark and Thomas Thurman. + */ + +/* ACCIDENCE II The parser. */ + +#ifndef AVALANCHE_ACCI2_H +#define AVALANCHE_ACCI2_H + +#include "common/scummsys.h" +#include "common/str.h" + +namespace Avalanche { +class AvalancheEngine; + +class Acci { +public: + enum VerbCode { + kVerbCodeExam = 1, kVerbCodeOpen = 2, kVerbCodePause = 3, kVerbCodeGet = 4, kVerbCodeDrop = 5, + kVerbCodeInv = 6, kVerbCodeTalk = 7, kVerbCodeGive = 8, kVerbCodeDrink = 9, kVerbCodeLoad = 10, + kVerbCodeSave = 11, kVerbCodePay = 12, kVerbCodeLook = 13, kVerbCodeBreak = 14, kVerbCodeQuit = 15, + kVerbCodeSit = 16, kVerbCodeStand = 17, kVerbCodeGo = 18, kVerbCodeInfo = 19, kVerbCodeUndress = 20, + kVerbCodeWear = 21, kVerbCodePlay = 22, kVerbCodeRing = 23, kVerbCodeHelp = 24, kVerbCodeLarrypass = 25, + kVerbCodePhaon = 26, kVerbCodeBoss = 27, kVerbCodePee = 28, kVerbCodeCheat = 29, kVerbCodeMagic = 30, + kVerbCodeRestart = 31, kVerbCodeEat = 32, kVerbCodeListen = 33, kVerbCodeBuy = 34, kVerbCodeAttack = 35, + kVerbCodePasswd = 36, kVerbCodeDir = 37, kVerbCodeDie = 38, kVerbCodeScore = 39, kVerbCodePut = 40, + kVerbCodeKiss = 41, kVerbCodeClimb = 42, kVerbCodeJump = 43, kVerbCodeHiscores = 44, kVerbCodeWake = 45, + kVerbCodeHello = 46, kVerbCodeThanks = 47, kVerbCodeSmartAlec = 249, kVerbCodeExpletive = 253 + }; + + static const byte kPardon = 254; // Didn't understand / wasn't given. + static const int16 kParserWordsNum = 277; // How many words does the parser know? + static const byte kNothing = 250; + static const byte kMoved = 0; // This word was moved. (Usually because it was the subject of conversation.) + static const int16 kFirstPassword = 88; // words[kFirstPassword] should equal "TIROS". + + struct VocabEntry { + byte _number; + Common::String _word; + }; + + static const VocabEntry kVocabulary[kParserWordsNum]; + + Common::String _realWords[11]; + byte _verb, _person, _thing; + bool _polite; + + Acci(AvalancheEngine *vm); + + void init(); + void parse(); + void doThat(); + void verbOpt(byte verb, Common::String &answer, char &ansKey); + void drink(); + +private: + struct RankType { + uint16 _score; + Common::String _title; + }; + + Common::String _thats; + byte _thing2; + + AvalancheEngine *_vm; + + byte wordNum(Common::String word); + void replace(Common::String oldChars, byte newChar); + + Common::String rank(); + Common::String totalTime(); + + void clearWords(); + void cheatParse(Common::String codes); + void stripPunctuation(Common::String &word); // Strips punctuation from word. + void displayWhat(byte target, bool animate, bool &ambiguous); // << It's an adjective! + bool doPronouns(); + void properNouns(); + void lookAround(); // This is called when you say "look". + void sayIt(); // This makes Avalot say the response. + void openDoor(); + void storeInterrogation(byte interrogation); + void examineObject(); // Examine a standard object-thing + bool isPersonHere(); + void exampers(); + bool isHolding(); + void openBox(bool isOpening); + void examine(); + void inventory(); + void swallow(); + void peopleInRoom(); // This lists the other people in the room. + void silly(); + void putProc(); // Called when you call kVerbCodeput. + void notInOrder(); + void goToCauldron(); + bool giveToSpludwick(); // The result of this fn is whether or not he says "Hey, thanks!". + void cardiffClimbing(); + void already(); + void standUp(); // Called when you ask Avvy to stand. + void getProc(char thing); + void giveGeidaTheLute(); + void playHarp(); + void winSequence(); + void personSpeaks(); + void heyThanks(); +}; + +} // End of namespace Avalanche. + +#endif // AVALANCHE_ACCI2_H diff --git a/engines/avalanche/animation.cpp b/engines/avalanche/animation.cpp new file mode 100644 index 0000000000..006ed5a3d3 --- /dev/null +++ b/engines/avalanche/animation.cpp @@ -0,0 +1,1460 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on the original source code of Lord Avalot d'Argent version 1.3. + * Copyright (c) 1994-1995 Mike, Mark and Thomas Thurman. + */ + +/* TRIP5 Trippancy V - the sprite animation subsystem */ + +#include "avalanche/avalanche.h" + +#include "avalanche/animation.h" +#include "avalanche/scrolls2.h" +#include "avalanche/lucerna2.h" +#include "avalanche/visa2.h" +#include "avalanche/gyro2.h" +#include "avalanche/celer2.h" +#include "avalanche/sequence2.h" +#include "avalanche/timer.h" +#include "avalanche/enid2.h" + +#include "common/scummsys.h" +#include "common/textconsole.h" +#include "common/file.h" + +namespace Avalanche { + +void AnimationType::init(byte spritenum, bool doCheck, Animation *anim) { + _anim = anim; + + const int32 idshould = -1317732048; + + if (spritenum == 177) + return; // Already running! + + Common::String filename; + Common::File inf; + filename = filename.format("sprite%d.avd", spritenum); + if (!inf.open(filename)) { + warning("AVALANCHE: Trip: File not found: %s", filename.c_str()); + return; + } + + inf.seek(177); + + int32 id = inf.readSint32LE(); + if (id != idshould) { + //output << '\7'; + inf.close(); + return; + } + + inf.skip(2); // Replace variable named 'soa' in the original code. + + if (!_stat._name.empty()) + _stat._name.clear(); + byte nameSize = inf.readByte(); + for (byte i = 0; i < nameSize; i++) + _stat._name += inf.readByte(); + inf.skip(12 - nameSize); + + //inf.skip(1); // Same as above. + byte commentSize = inf.readByte(); + for (byte i = 0; i < commentSize; i++) + _stat._comment += inf.readByte(); + inf.skip(16 - commentSize); + + _stat._frameNum = inf.readByte(); + _info._xLength = inf.readByte(); + _info._yLength = inf.readByte(); + _stat._seq = inf.readByte(); + _info._size = inf.readUint16LE(); + _stat._fgBubbleCol = inf.readByte(); + _stat._bgBubbleCol = inf.readByte(); + _stat._acciNum = inf.readByte(); + + _animCount = 0; // = 1; + _info._xWidth = _info._xLength / 8; + if ((_info._xLength % 8) > 0) + _info._xWidth++; + for (byte i = 0; i < _stat._frameNum; i++) { + _info._sil[_animCount] = new SilType[11 * (_info._yLength + 1)]; + //getmem(sil[totalnum-1], 11 * (a.yl + 1)); + _info._mani[_animCount] = new ManiType[_info._size - 6]; + //getmem(mani[totalnum-1], a.size - 6); + for (byte j = 0; j <= _info._yLength; j++) + inf.read((*_info._sil[_animCount])[j], _info._xWidth); + //blockread(inf, (*sil[totalnum-1])[fv], xw); + inf.read(*_info._mani[_animCount], _info._size - 6); + //blockread(inf, *mani[totalnum-1], a.size - 6); + + _animCount++; + } + _animCount++; + + // on; + _x = 0; + _y = 0; + _quick = true; + _visible = false; + _speedX = 3; + _speedY = 1; + if (spritenum == 1) + _anim->updateSpeed(); // Just for the lights. + + _homing = false; + _moveX = 0; + _moveY = 0; + _stepNum = 0; + _doCheck = doCheck; + _count = 0; + _id = spritenum; + _vanishIfStill = false; + _callEachStepFl = false; + + inf.close(); +} + +void AnimationType::original() { + _quick = false; + _id = 177; +} + +void AnimationType::draw() { + if ((_vanishIfStill) && (_moveX == 0) && (_moveY == 0)) + return; + byte picnum = _facingDir * _stat._seq + _stepNum; // There'll maybe problem because of the different array indexes in Pascal (starting from 1). + + _anim->_vm->_graphics->drawSprite(_info, picnum, _x, _y); +} + +void AnimationType::turn(byte whichway) { + if (whichway == 8) + _facingDir = Animation::kDirUp; + else + _facingDir = whichway; +} + +void AnimationType::appear(int16 wx, int16 wy, byte wf) { + _x = (wx / 8) * 8; + _y = wy; + _oldX[_anim->_vm->_gyro->_cp] = wx; + _oldY[_anim->_vm->_gyro->_cp] = wy; + turn(wf); + _visible = true; + _moveX = 0; + _moveY = 0; +} + +/** + * Check collision + * @remarks Originally called 'collision_check' + */ +bool AnimationType::checkCollision() { + for (byte i = 0; i < _anim->kSpriteNumbMax; i++) { + if (_anim->_sprites[i]._quick && (_anim->_sprites[i]._id != _id) && + ((_x + _info._xLength) > _anim->_sprites[i]._x) && + (_x < (_anim->_sprites[i]._x + _anim->_sprites[i]._info._xLength)) && + (_anim->_sprites[i]._y == _y)) + return true; + } + + return false; +} + +void AnimationType::walk() { + if (_visible) { + ByteField r; + r._x1 = (_x / 8) - 1; + if (r._x1 == 255) + r._x1 = 0; + r._y1 = _y - 2; + r._x2 = ((_x + _info._xLength) / 8) + 1; + r._y2 = _y + _info._yLength + 2; + } + + if (!_anim->_vm->_gyro->_doingSpriteRun) { + _oldX[_anim->_vm->_gyro->_cp] = _x; + _oldY[_anim->_vm->_gyro->_cp] = _y; + if (_homing) + homeStep(); + _x += _moveX; + _y += _moveY; + } + + if (_doCheck) { + if (checkCollision()) { + bounce(); + return; + } + + byte tc = _anim->checkFeet(_x, _x + _info._xLength, _oldY[_anim->_vm->_gyro->_cp], _y, _info._yLength) - 1; + // -1 is because the modified array indexes of magics[] compared to Pascal . + + if ((tc != 255) & (!_anim->_vm->_gyro->_doingSpriteRun)) { + switch (_anim->_vm->_gyro->_magics[tc]._operation) { + case Gyro::kMagicExclaim: { + bounce(); + _anim->_mustExclaim = true; + _anim->_sayWhat = _anim->_vm->_gyro->_magics[tc]._data; + } + break; + case Gyro::kMagicBounce: + bounce(); + break; + case Gyro::kMagicTransport: + _anim->flipRoom(_anim->_vm->_gyro->_magics[tc]._data >> 8, _anim->_vm->_gyro->_magics[tc]._data & 0xff); + break; + case Gyro::kMagicUnfinished: { + bounce(); + _anim->_vm->_scrolls->displayText("\7Sorry.\3\rThis place is not available yet!"); + } + break; + case Gyro::kMagicSpecial: + _anim->callSpecial(_anim->_vm->_gyro->_magics[tc]._data); + break; + case Gyro::kMagicOpenDoor: + _anim->openDoor(_anim->_vm->_gyro->_magics[tc]._data >> 8, _anim->_vm->_gyro->_magics[tc]._data & 0xff, tc); + break; + } + } + } + + if (!_anim->_vm->_gyro->_doingSpriteRun) { + _count++; + if (((_moveX != 0) || (_moveY != 0)) && (_count > 1)) { + _stepNum++; + if (_stepNum == _stat._seq) + _stepNum = 0; + _count = 0; + } + } +} + +void AnimationType::bounce() { + _x = _oldX[_anim->_vm->_gyro->_cp]; + _y = _oldY[_anim->_vm->_gyro->_cp]; + if (_doCheck) + _anim->stopWalking(); + else + stopWalk(); + _anim->_vm->_gyro->_onCanDoPageSwap = false; + _anim->_vm->_lucerna->drawDirection(); + _anim->_vm->_gyro->_onCanDoPageSwap = true; +} + +int8 AnimationType::getSign(int16 val) { + if (val > 0) + return 1; + else if (val < 0) + return -1; + else + return 0; +} + +void AnimationType::walkTo(byte pednum) { + pednum--; // Pascal -> C conversion: different array indexes. + setSpeed(getSign(_anim->_vm->_gyro->_peds[pednum]._x - _x) * 4, getSign(_anim->_vm->_gyro->_peds[pednum]._y - _y)); + _homingX = _anim->_vm->_gyro->_peds[pednum]._x - _info._xLength / 2; + _homingY = _anim->_vm->_gyro->_peds[pednum]._y - _info._yLength; + _homing = true; +} + +void AnimationType::stopHoming() { + _homing = false; +} + +void AnimationType::homeStep() { + int16 temp; + + if ((_homingX == _x) && (_homingY == _y)) { + // touching the target + stopWalk(); + return; + } + _moveX = 0; + _moveY = 0; + if (_homingY != _y) { + temp = _homingY - _y; + if (temp > 4) + _moveY = 4; + else if (temp < -4) + _moveY = -4; + else + _moveY = temp; + } + if (_homingX != _x) { + temp = _homingX - _x; + if (temp > 4) + _moveX = 4; + else if (temp < -4) + _moveX = -4; + else + _moveX = temp; + } +} + +void AnimationType::setSpeed(int8 xx, int8 yy) { + _moveX = xx; + _moveY = yy; + if ((_moveX == 0) && (_moveY == 0)) + return; // no movement + if (_moveX == 0) { + // No horz movement + if (_moveY < 0) + turn(_anim->kDirUp); + else + turn(_anim->kDirDown); + } else { + if (_moveX < 0) + turn(_anim->kDirLeft); + else + turn(_anim->kDirRight); + } +} + +void AnimationType::stopWalk() { + _moveX = 0; + _moveY = 0; + _homing = false; +} + +void AnimationType::chatter() { + _anim->_vm->_gyro->_talkX = _x + _info._xLength / 2; + _anim->_vm->_gyro->_talkY = _y; + _anim->_vm->_gyro->_talkFontColor = _stat._fgBubbleCol; + _anim->_vm->_gyro->_talkBackgroundColor = _stat._bgBubbleCol; +} + +void AnimationType::remove() { + _animCount--; + _info._xWidth = _info._xLength / 8; + if ((_info._xLength % 8) > 0) + _info._xWidth++; + for (byte i = 0; i < _stat._frameNum; i++) { + _animCount--; + assert(_animCount >= 0); + delete[] _info._mani[_animCount]; + delete[] _info._sil[_animCount]; + } + + _quick = false; + _id = 177; +} + +Animation::Animation(AvalancheEngine *vm) { + _vm = vm; + + _mustExclaim = false; +} + +Animation::~Animation() { + for (int16 i = 0; i < kSpriteNumbMax; i++) { + if (_sprites[i]._quick) + _sprites[i].remove(); + } +} + +void Animation::loadAnims() { + for (int16 i = 0; i < kSpriteNumbMax; i++) + _sprites[i].original(); +} + +byte Animation::checkFeet(int16 x1, int16 x2, int16 oy, int16 y, byte yl) { + // if not alive then begin checkfeet:=0; exit; end; + byte a = 0; + + //setactivepage(2); + if (x1 < 0) + x1 = 0; + if (x2 > 639) + x2 = 639; + if (oy < y) { + for (int16 i = x1; i <= x2; i++) { + for (int16 j = oy + yl; j <= y + yl; j++) { + byte c = *(byte *)_vm->_graphics->_magics.getBasePtr(i, j); + if (c > a) + a = c; + } + } + } else { + for (int16 i = x1; i <= x2; i++) { + for (int16 j = y + yl; j <= oy + yl; j++) { + byte c = *(byte *)_vm->_graphics->_magics.getBasePtr(i, j); + if (c > a) + a = c; + } + } + } + + //setactivepage(1 - cp); + return a; +} + +byte Animation::geidaPed(byte which) { + switch (which) { + case 1: + return 7; + case 2: + case 6: + return 8; + case 3: + case 5: + return 9; + case 4: + return 10; + default: + return 0; + } +} + +void Animation::catacombMove(byte ped) { + int32 here; + uint16 xy_uint16; + byte fv; + + // XY_uint16 is cat_x+cat_y*256. Thus, every room in the + // catacombs has a different number for it. + + + + xy_uint16 = _vm->_gyro->_dna._catacombX + _vm->_gyro->_dna._catacombY * 256; + _vm->_gyro->_dna._geidaSpin = 0; + + switch (xy_uint16) { + case 1801: // Exit catacombs + flipRoom(r__lustiesroom, 4); + _vm->_scrolls->displayText("Phew! Nice to be out of there!"); + return; + case 1033: // Oubliette + flipRoom(r__oubliette, 1); + _vm->_scrolls->displayText(Common::String("Oh, NO!") + _vm->_scrolls->kControlRegister + '1' + _vm->_scrolls->kControlSpeechBubble); + return; + case 4: + flipRoom(r__geidas, 1); + return; + case 2307: + flipRoom(r__lusties, 5); + _vm->_scrolls->displayText("Oh no... here we go again..."); + _vm->_gyro->_dna._userMovesAvvy = false; + _sprites[0]._moveY = 1; + _sprites[0]._moveX = 0; + return; + } + + if (!_vm->_gyro->_dna._enterCatacombsFromLustiesRoom) + _vm->_lucerna->loadRoom(29); + here = _vm->_gyro->kCatacombMap[_vm->_gyro->_dna._catacombY - 1][_vm->_gyro->_dna._catacombX - 1]; + + switch (here & 0xf) { // West. + case 0: // no connection (wall) + _vm->_gyro->_magics[1]._operation = _vm->_gyro->kMagicBounce; // Sloping wall. + _vm->_gyro->_magics[2]._operation = _vm->_gyro->kMagicNothing; // Straight wall. + _vm->_gyro->_portals[4]._operation = _vm->_gyro->kMagicNothing; // Door. + _vm->_celer->drawBackgroundSprite(-1, -1, 28); + break; + case 0x1: // no connection (wall + shield), + _vm->_gyro->_magics[1]._operation = _vm->_gyro->kMagicBounce; // Sloping wall. + _vm->_gyro->_magics[2]._operation = _vm->_gyro->kMagicNothing; // Straight wall. + _vm->_gyro->_portals[4]._operation = _vm->_gyro->kMagicNothing; // Door. + _vm->_celer->drawBackgroundSprite(-1, -1, 28); // Wall, plus... + _vm->_celer->drawBackgroundSprite(-1, -1, 29); // ...shield. + break; + case 0x2: // wall with door + _vm->_gyro->_magics[1]._operation = _vm->_gyro->kMagicBounce; // Sloping wall. + _vm->_gyro->_magics[2]._operation = _vm->_gyro->kMagicNothing; // Straight wall. + _vm->_gyro->_portals[4]._operation = _vm->_gyro->kMagicSpecial; // Door. + _vm->_celer->drawBackgroundSprite(-1, -1, 28); // Wall, plus... + _vm->_celer->drawBackgroundSprite(-1, -1, 30); // ...door. + break; + case 0x3: // wall with door and shield + _vm->_gyro->_magics[1]._operation = _vm->_gyro->kMagicBounce; // Sloping wall. + _vm->_gyro->_magics[2]._operation = _vm->_gyro->kMagicNothing; // Straight wall. + _vm->_gyro->_portals[4]._operation = _vm->_gyro->kMagicSpecial; // Door. + _vm->_celer->drawBackgroundSprite(-1, -1, 28); // Wall, plus... + _vm->_celer->drawBackgroundSprite(-1, -1, 30); // ...door, and... + _vm->_celer->drawBackgroundSprite(-1, -1, 29); // ...shield. + break; + case 0x4: // no connection (wall + window), + _vm->_gyro->_magics[1]._operation = _vm->_gyro->kMagicBounce; // Sloping wall. + _vm->_gyro->_magics[2]._operation = _vm->_gyro->kMagicNothing; // Straight wall. + _vm->_gyro->_portals[4]._operation = _vm->_gyro->kMagicNothing; // Door. + _vm->_celer->drawBackgroundSprite(-1, -1, 28); // Wall, plus... + _vm->_celer->drawBackgroundSprite(-1, -1, 5); // ...window. + break; + case 0x5: // wall with door and window + _vm->_gyro->_magics[1]._operation = _vm->_gyro->kMagicBounce; // Sloping wall. + _vm->_gyro->_magics[2]._operation = _vm->_gyro->kMagicNothing; // Straight wall. + _vm->_gyro->_portals[4]._operation = _vm->_gyro->kMagicSpecial; // Door. + _vm->_celer->drawBackgroundSprite(-1, -1, 28); // Wall, plus... + _vm->_celer->drawBackgroundSprite(-1, -1, 30); // ...door, and... + _vm->_celer->drawBackgroundSprite(-1, -1, 5); // ...window. + break; + case 0x6: // no connection (wall + torches), + _vm->_gyro->_magics[1]._operation = _vm->_gyro->kMagicBounce; // Sloping wall. + _vm->_gyro->_magics[2]._operation = _vm->_gyro->kMagicNothing; // Straight wall. + _vm->_gyro->_portals[4]._operation = _vm->_gyro->kMagicNothing; // No door. + _vm->_celer->drawBackgroundSprite(-1, -1, 28); // Wall, plus... + _vm->_celer->drawBackgroundSprite(-1, -1, 7); // ...torches. + break; + case 0x7: // wall with door and torches + _vm->_gyro->_magics[1]._operation = _vm->_gyro->kMagicBounce; // Sloping wall. + _vm->_gyro->_magics[2]._operation = _vm->_gyro->kMagicNothing; // Straight wall. + _vm->_gyro->_portals[4]._operation = _vm->_gyro->kMagicSpecial; // Door. + _vm->_celer->drawBackgroundSprite(-1, -1, 28); // Wall, plus... + _vm->_celer->drawBackgroundSprite(-1, -1, 30); // ...door, and... + _vm->_celer->drawBackgroundSprite(-1, -1, 7); // ...torches. + break; + case 0xf: // straight-through corridor. + _vm->_gyro->_magics[1]._operation = _vm->_gyro->kMagicNothing; // Sloping wall. + _vm->_gyro->_magics[2]._operation = _vm->_gyro->kMagicSpecial; // Straight wall. + break; + } + + /* ---- */ + + switch ((here & 0xf0) >> 4) { // East + case 0: // no connection (wall) + _vm->_gyro->_magics[4]._operation = _vm->_gyro->kMagicBounce; // Sloping wall. + _vm->_gyro->_magics[5]._operation = _vm->_gyro->kMagicNothing; // Straight wall. + _vm->_gyro->_portals[6]._operation = _vm->_gyro->kMagicNothing; // Door. + _vm->_celer->drawBackgroundSprite(-1, -1, 19); + break; + case 0x1: // no connection (wall + window), + _vm->_gyro->_magics[4]._operation = _vm->_gyro->kMagicBounce; // Sloping wall. + _vm->_gyro->_magics[5]._operation = _vm->_gyro->kMagicNothing; // Straight wall. + _vm->_gyro->_portals[6]._operation = _vm->_gyro->kMagicNothing; // Door. + _vm->_celer->drawBackgroundSprite(-1, -1, 19); // Wall, plus... + _vm->_celer->drawBackgroundSprite(-1, -1, 20); // ...window. + break; + case 0x2: // wall with door + _vm->_gyro->_magics[4]._operation = _vm->_gyro->kMagicBounce; // Sloping wall. + _vm->_gyro->_magics[5]._operation = _vm->_gyro->kMagicNothing; // Straight wall. + _vm->_gyro->_portals[6]._operation = _vm->_gyro->kMagicSpecial; // Door. + _vm->_celer->drawBackgroundSprite(-1, -1, 19); // Wall, plus... + _vm->_celer->drawBackgroundSprite(-1, -1, 21); // ...door. + break; + case 0x3: // wall with door and window + _vm->_gyro->_magics[4]._operation = _vm->_gyro->kMagicBounce; // Sloping wall. + _vm->_gyro->_magics[5]._operation = _vm->_gyro->kMagicNothing; // Straight wall. + _vm->_gyro->_portals[6]._operation = _vm->_gyro->kMagicSpecial; // Door. + _vm->_celer->drawBackgroundSprite(-1, -1, 19); // Wall, plus... + _vm->_celer->drawBackgroundSprite(-1, -1, 20); // ...door, and... + _vm->_celer->drawBackgroundSprite(-1, -1, 21); // ...window. + break; + case 0x6: // no connection (wall + torches), + _vm->_gyro->_magics[4]._operation = _vm->_gyro->kMagicBounce; // Sloping wall. + _vm->_gyro->_magics[5]._operation = _vm->_gyro->kMagicNothing; // Straight wall. + _vm->_gyro->_portals[6]._operation = _vm->_gyro->kMagicNothing; // No door. + _vm->_celer->drawBackgroundSprite(-1, -1, 19); // Wall, plus... + _vm->_celer->drawBackgroundSprite(-1, -1, 18); // ...torches. + break; + case 0x7: // wall with door and torches + _vm->_gyro->_magics[4]._operation = _vm->_gyro->kMagicBounce; // Sloping wall. + _vm->_gyro->_magics[5]._operation = _vm->_gyro->kMagicNothing; // Straight wall. + _vm->_gyro->_portals[6]._operation = _vm->_gyro->kMagicSpecial; // Door. + _vm->_celer->drawBackgroundSprite(-1, -1, 19); // Wall, plus... + _vm->_celer->drawBackgroundSprite(-1, -1, 21); // ...door, and... + _vm->_celer->drawBackgroundSprite(-1, -1, 18); // ...torches. + break; + case 0xf: // straight-through corridor. + _vm->_gyro->_magics[4]._operation = _vm->_gyro->kMagicNothing; // Sloping wall. + _vm->_gyro->_magics[5]._operation = _vm->_gyro->kMagicSpecial; // Straight wall. + _vm->_gyro->_portals[6]._operation = _vm->_gyro->kMagicNothing; // Door. + break; + } + + /* ---- */ + + switch ((here & 0xf00) >> 8) { // South + case 0: // No connection. + _vm->_gyro->_magics[6]._operation = _vm->_gyro->kMagicBounce; + _vm->_gyro->_magics[11]._operation = _vm->_gyro->kMagicBounce; + _vm->_gyro->_magics[12]._operation = _vm->_gyro->kMagicBounce; + break; + case 0x1: + _vm->_celer->drawBackgroundSprite(-1, -1, 22); + + if ((xy_uint16 == 2051) && (_vm->_gyro->_dna._geidaFollows)) + _vm->_gyro->_magics[12]._operation = _vm->_gyro->kMagicExclaim; + else + _vm->_gyro->_magics[12]._operation = _vm->_gyro->kMagicSpecial; // Right exit south. + + _vm->_gyro->_magics[6]._operation = _vm->_gyro->kMagicBounce; + _vm->_gyro->_magics[11]._operation = _vm->_gyro->kMagicBounce; + break; + case 0x2: + _vm->_celer->drawBackgroundSprite(-1, -1, 23); + _vm->_gyro->_magics[6]._operation = _vm->_gyro->kMagicSpecial; // Middle exit south. + _vm->_gyro->_magics[11]._operation = _vm->_gyro->kMagicBounce; + _vm->_gyro->_magics[12]._operation = _vm->_gyro->kMagicBounce; + break; + case 0x3: + _vm->_celer->drawBackgroundSprite(-1, -1, 24); + _vm->_gyro->_magics[11]._operation = _vm->_gyro->kMagicSpecial; // Left exit south. + _vm->_gyro->_magics[6]._operation = _vm->_gyro->kMagicBounce; + _vm->_gyro->_magics[12]._operation = _vm->_gyro->kMagicBounce; + break; + } + + switch ((here & 0xf000) >> 12) { // North + case 0: // No connection + _vm->_gyro->_magics[0]._operation = _vm->_gyro->kMagicBounce; + _vm->_gyro->_portals[3]._operation = _vm->_gyro->kMagicNothing; // Door. + break; + // LEFT handles: +#if 0 + case 0x1: + _vm->_celer->show_one(-1, -1, 4); + _vm->_gyro->magics[1].op = _vm->_gyro->bounces; // { Left exit north. } { Change magic number! } + _vm->_gyro->portals[12].op = _vm->_gyro->special; // { Door. } + break; +#endif + case 0x2: + _vm->_celer->drawBackgroundSprite(-1, -1, 4); + _vm->_gyro->_magics[0]._operation = _vm->_gyro->kMagicBounce; // Middle exit north. + _vm->_gyro->_portals[3]._operation = _vm->_gyro->kMagicSpecial; // Door. + break; +#if 0 + case 0x3: + _vm->_celer->show_one(-1, -1, 4); + _vm->_gyro->magics[1].op = _vm->_gyro->bounces; // { Right exit north. } { Change magic number! } + _vm->_gyro->portals[12].op = _vm->_gyro->special; // { Door. } + break; + // RIGHT handles: + case 0x4: + _vm->_celer->show_one(-1, -1, 3); + _vm->_gyro->magics[1].op = _vm->_gyro->bounces; // { Left exit north. } { Change magic number! } + _vm->_gyro->portals[12].op = _vm->_gyro->special; // { Door. } + break; +#endif + case 0x5: + _vm->_celer->drawBackgroundSprite(-1, -1, 3); + _vm->_gyro->_magics[0]._operation = _vm->_gyro->kMagicBounce; // Middle exit north. + _vm->_gyro->_portals[3]._operation = _vm->_gyro->kMagicSpecial; // Door. + break; +#if 0 + case 0x6: + _vm->_celer->show_one(-1, -1, 3); + _vm->_gyro->magics[1].op = _vm->_gyro->bounces; // { Right exit north. } + _vm->_gyro->portals[12].op = _vm->_gyro->special; // { Door. } + break; +#endif + // ARCHWAYS: + case 0x7: + case 0x8: + case 0x9: { + _vm->_celer->drawBackgroundSprite(-1, -1, 6); + + if (((here & 0xf000) >> 12) > 0x7) + _vm->_celer->drawBackgroundSprite(-1, -1, 31); + if (((here & 0xf000) >> 12) == 0x9) + _vm->_celer->drawBackgroundSprite(-1, -1, 32); + + _vm->_gyro->_magics[0]._operation = _vm->_gyro->kMagicSpecial; // Middle arch north. + _vm->_gyro->_portals[3]._operation = _vm->_gyro->kMagicNothing; // Door. + } + break; + // DECORATIONS: + case 0xd: // No connection + WINDOW + _vm->_gyro->_magics[0]._operation = _vm->_gyro->kMagicBounce; + _vm->_gyro->_portals[3]._operation = _vm->_gyro->kMagicNothing; // Door. + _vm->_celer->drawBackgroundSprite(-1, -1, 14); + break; + case 0xe: // No connection + TORCH + _vm->_gyro->_magics[0]._operation = _vm->_gyro->kMagicBounce; + _vm->_gyro->_portals[3]._operation = _vm->_gyro->kMagicNothing; // Door. + _vm->_celer->drawBackgroundSprite(-1, -1, 8); + break; + // Recessed door: + case 0xf: + _vm->_gyro->_magics[0]._operation = _vm->_gyro->kMagicNothing; // Door to Geida's room. + _vm->_celer->drawBackgroundSprite(-1, -1, 1); + _vm->_gyro->_portals[3]._operation = _vm->_gyro->kMagicSpecial; // Door. + break; + } + + switch (xy_uint16) { + case 514: + _vm->_celer->drawBackgroundSprite(-1, -1, 17); + break; // [2,2] : "Art Gallery" sign over door. + case 264: + _vm->_celer->drawBackgroundSprite(-1, -1, 9); + break; // [8,1] : "The Wrong Way!" sign. + case 1797: + _vm->_celer->drawBackgroundSprite(-1, -1, 2); + break; // [5,7] : "Ite Mingite" sign. + case 258: + for (fv = 0; fv <= 2; fv++) { // [2,1] : Art gallery - pictures + _vm->_celer->drawBackgroundSprite(130 + fv * 120, 70, 15); + _vm->_celer->drawBackgroundSprite(184 + fv * 120, 78, 16); + } + break; + case 1287: + for (fv = 10; fv <= 13; fv++) + _vm->_celer->drawBackgroundSprite(-1, -1, fv); + break; // [7,5] : 4 candles. + case 776: + _vm->_celer->drawBackgroundSprite(-1, -1, 10); + break; // [8,3] : 1 candle. + case 2049: + _vm->_celer->drawBackgroundSprite(-1, -1, 11); + break; // [1,8] : another candle. + case 257: + _vm->_celer->drawBackgroundSprite(-1, -1, 12); + _vm->_celer->drawBackgroundSprite(-1, -1, 13); + break; // [1,1] : the other two. + } + + if ((_vm->_gyro->_dna._geidaFollows) && (ped > 0)) { + if (!_sprites[1]._quick) // If we don't already have her... + _sprites[1].init(5, true, this); // ...Load Geida. + appearPed(2, geidaPed(ped)); + _sprites[1]._callEachStepFl = true; + _sprites[1]._eachStepProc = kProcGeida; + } +} + + + +// This proc gets called whenever you touch a line defined as _vm->_gyro->special. +void Animation::dawnDelay() { + _vm->_timer->addTimer(2, _vm->_timer->kProcDawnDelay, _vm->_timer->kReasonDawndelay); +} + +void Animation::callSpecial(uint16 which) { + switch (which) { + case 1: // _vm->_gyro->special 1: Room 22: top of stairs. + _vm->_celer->drawBackgroundSprite(-1, -1, 1); + _vm->_gyro->_dna._brummieStairs = 1; + _vm->_gyro->_magics[9]._operation = _vm->_gyro->kMagicNothing; + _vm->_timer->addTimer(10, _vm->_timer->kProcStairs, _vm->_timer->kReasonBrummieStairs); + stopWalking(); + _vm->_gyro->_dna._userMovesAvvy = false; + break; + case 2: // _vm->_gyro->special 2: Room 22: bottom of stairs. + _vm->_gyro->_dna._brummieStairs = 3; + _vm->_gyro->_magics[10]._operation = _vm->_gyro->kMagicNothing; + _vm->_gyro->_magics[11]._operation = _vm->_gyro->kMagicExclaim; + _vm->_gyro->_magics[11]._data = 5; + _vm->_gyro->_magics[3]._operation = _vm->_gyro->kMagicBounce; // Now works as planned! + stopWalking(); + _vm->_visa->displayScrollChain('q', 26); + _vm->_gyro->_dna._userMovesAvvy = true; + break; + case 3: // _vm->_gyro->special 3: Room 71: triggers dart. + _sprites[0].bounce(); // Must include that. + + if (!_vm->_gyro->_dna._arrowTriggered) { + _vm->_gyro->_dna._arrowTriggered = true; + appearPed(2, 4); // The dart starts at ped 4, and... + _sprites[1].walkTo(5); // flies to ped 5. + _sprites[1]._facingDir = kDirUp; // Only face. + // Should call some kind of Eachstep procedure which will deallocate + // the sprite when it hits the wall, and replace it with the chunk + // graphic of the arrow buried in the plaster. */ + + // OK! + _sprites[1]._callEachStepFl = true; + _sprites[1]._eachStepProc = kProcArrow; + } + break; + case 4: // This is the ghost room link. + _vm->_lucerna->dusk(); + _sprites[0].turn(kDirRight); // you'll see this after we get back from bootstrap + _vm->_timer->addTimer(1, _vm->_timer->kProcGhostRoomPhew, _vm->_timer->kReasonGhostRoomPhew); + _vm->_enid->backToBootstrap(3); + break; + case 5: + if (_vm->_gyro->_dna._friarWillTieYouUp) { + // _vm->_gyro->special 5: Room 42: touched tree, and get tied up. + _vm->_gyro->_magics[4]._operation = _vm->_gyro->kMagicBounce; // Boundary effect is now working again. + _vm->_visa->displayScrollChain('q', 35); + _sprites[0].remove(); + //tr[1].vanishifstill:=true; + _vm->_celer->drawBackgroundSprite(-1, -1, 2); + _vm->_visa->displayScrollChain('q', 36); + _vm->_gyro->_dna._tiedUp = true; + _vm->_gyro->_dna._friarWillTieYouUp = false; + _sprites[1].walkTo(3); + _sprites[1]._vanishIfStill = true; + _sprites[1]._doCheck = true; // One of them must have Check_Me switched on. + _vm->_gyro->_whereIs[_vm->_gyro->kPeopleFriarTuck - 150] = 177; // Not here, then. + _vm->_timer->addTimer(364, _vm->_timer->kProcHangAround, _vm->_timer->kReasonHangingAround); + } + break; + case 6: // _vm->_gyro->special 6: fall down oubliette. + _vm->_gyro->_dna._userMovesAvvy = false; + _sprites[0]._moveX = 3; + _sprites[0]._moveY = 0; + _sprites[0]._facingDir = kDirRight; + _vm->_timer->addTimer(1, _vm->_timer->kProcFallDownOubliette, _vm->_timer->kReasonFallingDownOubliette); + break; + case 7: // _vm->_gyro->special 7: stop falling down oubliette. + _sprites[0]._visible = false; + _vm->_gyro->_magics[9]._operation = _vm->_gyro->kMagicNothing; + stopWalking(); + _vm->_timer->loseTimer(_vm->_timer->kReasonFallingDownOubliette); + //_vm->_lucerna->mblit(12, 80, 38, 160, 3, 0); + //_vm->_lucerna->mblit(12, 80, 38, 160, 3, 1); + _vm->_scrolls->displayText("Oh dear, you seem to be down the bottom of an oubliette."); + _vm->_timer->addTimer(200, _vm->_timer->kProcMeetAvaroid, _vm->_timer->kReasonMeetingAvaroid); + break; + case 8: // _vm->_gyro->special 8: leave du Lustie's room. + if ((_vm->_gyro->_dna._geidaFollows) && (!_vm->_gyro->_dna._lustieIsAsleep)) { + _vm->_visa->displayScrollChain('q', 63); + _sprites[1].turn(kDirDown); + _sprites[1].stopWalk(); + _sprites[1]._callEachStepFl = false; // Geida + _vm->_lucerna->gameOver(); + } + break; + case 9: // _vm->_gyro->special 9: lose Geida to Robin Hood... + if (!_vm->_gyro->_dna._geidaFollows) + return; // DOESN'T COUNT: no Geida. + _sprites[1]._callEachStepFl = false; // She no longer follows Avvy around. + _sprites[1].walkTo(4); // She walks to somewhere... + _sprites[0].remove(); // Lose Avvy. + _vm->_gyro->_dna._userMovesAvvy = false; + _vm->_timer->addTimer(40, _vm->_timer->kProcRobinHoodAndGeida, _vm->_timer->kReasonRobinHoodAndGeida); + break; + case 10: // _vm->_gyro->special 10: transfer north in catacombs. + if ((_vm->_gyro->_dna._catacombX == 4) && (_vm->_gyro->_dna._catacombY == 1)) { + // Into Geida's room. + if (_vm->_gyro->_dna._objects[_vm->_gyro->kObjectKey - 1]) + _vm->_visa->displayScrollChain('q', 62); + else { + _vm->_visa->displayScrollChain('q', 61); + return; + } + } + _vm->_lucerna->dusk(); + _vm->_gyro->_dna._catacombY--; + catacombMove(4); + if (_vm->_gyro->_dna._room != r__catacombs) + return; + switch ((_vm->_gyro->kCatacombMap[_vm->_gyro->_dna._catacombY - 1][_vm->_gyro->_dna._catacombX - 1] & 0xf00) >> 8) { + case 0x1: + appearPed(1, 12); + break; + case 0x3: + appearPed(1, 11); + break; + default: + appearPed(1, 4); + } + dawnDelay(); + break; + case 11: // _vm->_gyro->special 11: transfer east in catacombs. + _vm->_lucerna->dusk(); + _vm->_gyro->_dna._catacombX++; + catacombMove(1); + if (_vm->_gyro->_dna._room != r__catacombs) + return; + appearPed(1, 1); + dawnDelay(); + break; + case 12: // _vm->_gyro->special 12: transfer south in catacombs. + _vm->_lucerna->dusk(); + _vm->_gyro->_dna._catacombY += 1; + catacombMove(2); + if (_vm->_gyro->_dna._room != r__catacombs) + return; + appearPed(1, 2); + dawnDelay(); + break; + case 13: // _vm->_gyro->special 13: transfer west in catacombs. + _vm->_lucerna->dusk(); + _vm->_gyro->_dna._catacombX--; + catacombMove(3); + if (_vm->_gyro->_dna._room != r__catacombs) + return; + appearPed(1, 3); + dawnDelay(); + break; + } +} + +/** + * Open the Door. + * This slides the door open. The data really ought to be saved in + * the Also file, and will be next time. However, for now, they're + * here. + * @remarks Originally called 'open_the_door' + */ +void Animation::openDoor(byte whither, byte ped, byte magicnum) { + switch (_vm->_gyro->_dna._room) { + case r__outsideyours: + case r__outsidenottspub: + case r__outsideducks: + _vm->_sequence->firstShow(1); + _vm->_sequence->thenShow(2); + _vm->_sequence->thenShow(3); + break; + case r__insidecardiffcastle: + _vm->_sequence->firstShow(1); + _vm->_sequence->thenShow(5); + break; + case r__avvysgarden: + case r__entrancehall: + case r__insideabbey: + case r__yourhall: + _vm->_sequence->firstShow(1); + _vm->_sequence->thenShow(2); + break; + case r__musicroom: + case r__outsideargentpub: + _vm->_sequence->firstShow(5); + _vm->_sequence->thenShow(6); + break; + case r__lusties: + switch (magicnum) { + case 14: + if (_vm->_gyro->_dna._avvysInTheCupboard) { + hideInCupboard(); + _vm->_sequence->firstShow(8); + _vm->_sequence->thenShow(7); + _vm->_sequence->startToClose(); + return; + } else { + appearPed(1, 6); + _sprites[0]._facingDir = kDirRight; // added by TT 12/3/1995 + _vm->_sequence->firstShow(8); + _vm->_sequence->thenShow(9); + } + break; + case 12: + _vm->_sequence->firstShow(4); + _vm->_sequence->thenShow(5); + _vm->_sequence->thenShow(6); + break; + } + break; + } + + _vm->_sequence->thenFlip(whither, ped); + _vm->_sequence->startToOpen(); +} + +void Animation::updateSpeed() { + // Given that you've just changed the speed in triptype._speedX, this adjusts _moveX. + + _sprites[0]._moveX = (_sprites[0]._moveX / 3) * _sprites[0]._speedX; + + //setactivepage(3); + + if (_sprites[0]._speedX == _vm->_gyro->kRun) + _vm->_graphics->_surface.drawLine(371, 199, 373, 199, kColorYellow); + else + _vm->_graphics->_surface.drawLine(336, 199, 338, 199, kColorYellow); + + if (_sprites[0]._speedX == _vm->_gyro->kRun) + _vm->_graphics->_surface.drawLine(336, 199, 338, 199, kColorLightblue); + else + _vm->_graphics->_surface.drawLine(371, 199, 373, 199, kColorLightblue); + + //setactivepage(1 - cp); +} + +void Animation::changeDirection(byte t, byte dir) { + switch (dir) { + case kDirUp: + _sprites[t].setSpeed(0, -_sprites[t]._speedY); + break; + case kDirDown: + _sprites[t].setSpeed(0, _sprites[t]._speedY); + break; + case kDirLeft: + _sprites[t].setSpeed(-_sprites[t]._speedX, 0); + break; + case kDirRight: + _sprites[t].setSpeed(_sprites[t]._speedX, 0); + break; + case kDirUpLeft: + _sprites[t].setSpeed(-_sprites[t]._speedX, -_sprites[t]._speedY); + break; + case kDirUpRight: + _sprites[t].setSpeed(_sprites[t]._speedX, -_sprites[t]._speedY); + break; + case kDirDownLeft: + _sprites[t].setSpeed(-_sprites[t]._speedX, _sprites[t]._speedY); + break; + case kDirDownRight: + _sprites[t].setSpeed(_sprites[t]._speedX, _sprites[t]._speedY); + break; + } +} + +void Animation::appearPed(byte trn, byte np) { + trn--; + np--; + _sprites[trn].appear(_vm->_gyro->_peds[np]._x - _sprites[trn]._info._xLength / 2, _vm->_gyro->_peds[np]._y - _sprites[trn]._info._yLength, _vm->_gyro->_peds[np]._direction); + changeDirection(trn, _vm->_gyro->_peds[np]._direction); +} + +// Eachstep procedures: +void Animation::followAvalotY(byte tripnum) { + if (_sprites[0]._facingDir == kDirLeft) + return; + if (_sprites[tripnum]._homing) + _sprites[tripnum]._homingY = _sprites[1]._y; + else { + if (_sprites[tripnum]._y < _sprites[1]._y) + _sprites[tripnum]._y += 1; + else if (_sprites[tripnum]._y > _sprites[1]._y) + _sprites[tripnum]._y -= 1; + else + return; + if (_sprites[tripnum]._moveX == 0) { + _sprites[tripnum]._stepNum += 1; + if (_sprites[tripnum]._stepNum == _sprites[tripnum]._stat._seq) + _sprites[tripnum]._stepNum = 0; + _sprites[tripnum]._count = 0; + } + } +} + +void Animation::backAndForth(byte tripnum) { + if (!_sprites[tripnum]._homing) { + if (_sprites[tripnum]._facingDir == kDirRight) + _sprites[tripnum].walkTo(4); + else + _sprites[tripnum].walkTo(5); + } +} + +void Animation::faceAvvy(byte tripnum) { + if (!_sprites[tripnum]._homing) { + if (_sprites[0]._x >= _sprites[tripnum]._x) + _sprites[tripnum]._facingDir = kDirRight; + else + _sprites[tripnum]._facingDir = kDirLeft; + } +} + +void Animation::arrowProcs(byte tripnum) { + if (_sprites[tripnum]._homing) { + // Arrow is still in flight. + // We must check whether or not the arrow has collided tr[tripnum] Avvy's head. + // This is so if: a) the bottom of the arrow is below Avvy's head, + // b) the left of the arrow is left of the right of Avvy's head, and + // c) the right of the arrow is right of the left of Avvy's head. + if (((_sprites[tripnum]._y + _sprites[tripnum]._info._yLength) >= _sprites[0]._y) // A + && (_sprites[tripnum]._x <= (_sprites[0]._x + _sprites[0]._info._xLength)) // B + && ((_sprites[tripnum]._x + _sprites[tripnum]._info._xLength) >= _sprites[0]._x)) { // C + // OK, it's hit him... what now? + + _sprites[1]._callEachStepFl = false; // prevent recursion. + _vm->_visa->displayScrollChain('Q', 47); // Complaint! + _sprites[tripnum].remove(); // Deallocate the arrow. +#if 0 + tr[1].done; { Deallocate normal pic of Avvy. } + + off; + for byte fv:=0 to 1 do + begin + cp:=1-cp; + getback; + end; + on; +#endif + _vm->_lucerna->gameOver(); + + _vm->_gyro->_dna._userMovesAvvy = false; // Stop the user from moving him. + _vm->_timer->addTimer(55, _vm->_timer->kProcNaughtyDuke, _vm->_timer->kReasonNaughtyDuke); + } + } else { // Arrow has hit the wall! + _sprites[tripnum].remove(); // Deallocate the arrow. + _vm->_celer->drawBackgroundSprite(-1, -1, 3); // Show pic of arrow stuck into the door. + _vm->_gyro->_dna._arrowInTheDoor = true; // So that we can pick it up. + } + +} + +#if 0 +procedure Spludwick_procs(tripnum:byte); +var fv:byte; +begin + with tr[tripnum] do + if not homing then { We only need to do anything if Spludwick *stops* + walking. } + with _vm->_gyro->dna do + begin + inc(DogfoodPos); + if DogfoodPos=8 then DogfoodPos:=1; + walkto(DogfoodPos); + end; +end; +#endif + +void Animation::grabAvvy(byte tripnum) { // For Friar Tuck, in Nottingham. + int16 tox = _sprites[0]._x + 17; + int16 toy = _sprites[0]._y - 1; + if ((_sprites[tripnum]._x == tox) && (_sprites[tripnum]._y == toy)) { + _sprites[tripnum]._callEachStepFl = false; + _sprites[tripnum]._facingDir = kDirLeft; + _sprites[tripnum].stopWalk(); + // ... whatever ... + } else { + // Still some way to go. + if (_sprites[tripnum]._x < tox) { + _sprites[tripnum]._x += 5; + if (_sprites[tripnum]._x > tox) + _sprites[tripnum]._x = tox; + } + if (_sprites[tripnum]._y < toy) + _sprites[tripnum]._y++; + _sprites[tripnum]._stepNum++; + if (_sprites[tripnum]._stepNum == _sprites[tripnum]._stat._seq) + _sprites[tripnum]._stepNum = 0; + } +} + +void Animation::takeAStep(byte &tripnum) { + if (_sprites[tripnum]._moveX == 0) { + _sprites[tripnum]._stepNum++; + if (_sprites[tripnum]._stepNum == _sprites[tripnum]._stat._seq) + _sprites[tripnum]._stepNum = 0; + _sprites[tripnum]._count = 0; + } +} + +void Animation::spin(byte whichway, byte &tripnum) { + if (_sprites[tripnum]._facingDir != whichway) { + _sprites[tripnum]._facingDir = whichway; + if (_sprites[tripnum]._id == 2) + return; // Not for Spludwick + + _vm->_gyro->_dna._geidaSpin += 1; + _vm->_gyro->_dna._geidaTime = 20; + if (_vm->_gyro->_dna._geidaSpin == 5) { + _vm->_scrolls->displayText("Steady on, Avvy, you'll make the poor girl dizzy!"); + _vm->_gyro->_dna._geidaSpin = 0; + _vm->_gyro->_dna._geidaTime = 0; // knock out records + } + } +} + +void Animation::geidaProcs(byte tripnum) { + if (_vm->_gyro->_dna._geidaTime > 0) { + _vm->_gyro->_dna._geidaTime--; + if (_vm->_gyro->_dna._geidaTime == 0) + _vm->_gyro->_dna._geidaSpin = 0; + } + + if (_sprites[tripnum]._y < (_sprites[0]._y - 2)) { + // Geida is further from the screen than Avvy. + spin(kDirDown, tripnum); + _sprites[tripnum]._moveY = 1; + _sprites[tripnum]._moveX = 0; + takeAStep(tripnum); + return; + } else if (_sprites[tripnum]._y > (_sprites[0]._y + 2)) { + // Avvy is further from the screen than Geida. + spin(kDirUp, tripnum); + _sprites[tripnum]._moveY = -1; + _sprites[tripnum]._moveX = 0; + takeAStep(tripnum); + return; + } + + _sprites[tripnum]._moveY = 0; + // These 12-s are not in the original, I added them to make the following method more "smooth". + // Now the NPC which is following Avvy won't block his way and will walk next to him properly. + if (_sprites[tripnum]._x < _sprites[0]._x - _sprites[0]._speedX * 8 - 12) { + _sprites[tripnum]._moveX = _sprites[0]._speedX; + spin(kDirRight, tripnum); + takeAStep(tripnum); + } else if (_sprites[tripnum]._x > _sprites[0]._x + _sprites[0]._speedX * 8 + 12) { + _sprites[tripnum]._moveX = -_sprites[0]._speedX; + spin(kDirLeft, tripnum); + takeAStep(tripnum); + } else + _sprites[tripnum]._moveX = 0; +} + +// That's all... + +void Animation::drawSprites() { + int8 order[5]; + byte temp; + bool ok; + + for (int i = 0; i < 5; i++) + order[i] = -1; + + for (int16 i = 0; i < kSpriteNumbMax; i++) { + if (_sprites[i]._quick && _sprites[i]._visible) + order[i] = i; + } + + do { + ok = true; + for (byte i = 0; i < 4; i++) { + if (((order[i] != -1) && (order[i + 1] != -1)) + && (_sprites[order[i]]._y > _sprites[order[i + 1]]._y)) { + // Swap them! + temp = order[i]; + order[i] = order[i + 1]; + order[i + 1] = temp; + ok = false; + } + } + } while (!ok); + + + _vm->_graphics->refreshBackground(); + + for (byte i = 0; i < 5; i++) { + if (order[i] > -1) + _sprites[order[i]].draw(); + } +} + +/** + * Animation links + * @remarks Originally called 'trippancy_link' + */ +void Animation::animLink() { + if (_vm->_gyro->_dropdownActive | _vm->_gyro->_onToolbar | _vm->_gyro->_seeScroll) + return; + for (int16 i = 0; i < kSpriteNumbMax; i++) { + if (_sprites[i]._quick && _sprites[i]._visible) + _sprites[i].walk(); + } + + drawSprites(); + + for (int16 i = 0; i < kSpriteNumbMax; i++) { + if (_sprites[i]._quick && _sprites[i]._callEachStepFl) { + switch (_sprites[i]._eachStepProc) { + case kProcFollowAvvyY : + followAvalotY(i); + break; + case kProcBackAndForth : + backAndForth(i); + break; + case kProcFaceAvvy : + faceAvvy(i); + break; + case kProcArrow : + arrowProcs(i); + break; + // PROCSpludwick_procs : spludwick_procs(fv); + case kProcGrabAvvy : + grabAvvy(i); + break; + case kProcGeida : + geidaProcs(i); + break; + } + } + } + + if (_mustExclaim) { + _mustExclaim = false; + _vm->_visa->displayScrollChain('x', _sayWhat); + } +} + +void Animation::stopWalking() { + _sprites[0].stopWalk(); + _vm->_gyro->_dna._direction = kDirStopped; + if (_vm->_gyro->_alive) + _sprites[0]._stepNum = 1; +} + +/** + * Hide in the cupboard + * @remarks Originally called 'hide_in_the_cupboard' + */ +void Animation::hideInCupboard() { + if (_vm->_gyro->_dna._avvysInTheCupboard) { + if (_vm->_gyro->_dna._wearing == Acci::kNothing) + _vm->_scrolls->displayText(Common::String(_vm->_scrolls->kControlItalic) + "AVVY!" + _vm->_scrolls->kControlRoman + "Get dressed first!"); + else { + _sprites[0]._visible = true; + _vm->_gyro->_dna._userMovesAvvy = true; + appearPed(1, 3); // Walk out of the cupboard. + _vm->_scrolls->displayText("You leave the cupboard. Nice to be out of there!"); + _vm->_gyro->_dna._avvysInTheCupboard = false; + _vm->_sequence->firstShow(8); + _vm->_sequence->thenShow(7); + _vm->_sequence->startToClose(); + } + } else { + // Not hiding in the cupboard + _sprites[0]._visible = false; + _vm->_gyro->_dna._userMovesAvvy = false; + _vm->_scrolls->displayText(Common::String("You walk into the room...") + _vm->_scrolls->kControlParagraph + + "It seems to be an empty, but dusty, cupboard. Hmmmm... you leave the door slightly open to avoid suffocation."); + _vm->_gyro->_dna._avvysInTheCupboard = true; + _vm->_celer->drawBackgroundSprite(-1, -1, 8); + } +} + +void Animation::flipRoom(byte room, byte ped) { + if (!_vm->_gyro->_alive) { + // You can't leave the room if you're dead. + _sprites[0]._moveX = 0; + _sprites[0]._moveY = 0; // Stop him from moving. + return; + } + + if ((room == 177) && (_vm->_gyro->_dna._room == r__lusties)) { + hideInCupboard(); + return; + } + + if ((_vm->_gyro->_dna._jumpStatus > 0) && (_vm->_gyro->_dna._room == r__insidecardiffcastle)) { + // You can't *jump* out of Cardiff Castle! + _sprites[0]._moveX = 0; + return; + } + + _vm->_lucerna->exitRoom(_vm->_gyro->_dna._room); + _vm->_lucerna->dusk(); + + for (int16 i = 1; i < kSpriteNumbMax; i++) { + if (_sprites[i]._quick) + _sprites[i].remove(); + } // Deallocate sprite + + if (_vm->_gyro->_dna._room == r__lustiesroom) + _vm->_gyro->_dna._enterCatacombsFromLustiesRoom = true; + + _vm->_lucerna->enterRoom(room, ped); + appearPed(1, ped); + _vm->_gyro->_dna._enterCatacombsFromLustiesRoom = false; + _vm->_gyro->_oldDirection = _vm->_gyro->_dna._direction; + _vm->_gyro->_dna._direction = _sprites[0]._facingDir; + _vm->_lucerna->drawDirection(); + + _vm->_lucerna->dawn(); + + // Tidy up after mouse. I know it's a kludge... + // tidy_after_mouse; +} + +bool Animation::inField(byte which) { + which--; // Pascal -> C: different array indexes. + + int16 yy = _sprites[0]._y + _sprites[0]._info._yLength; + + return (_sprites[0]._x >= _vm->_gyro->_fields[which]._x1) && (_sprites[0]._x <= _vm->_gyro->_fields[which]._x2) + && (yy >= _vm->_gyro->_fields[which]._y1) && (yy <= _vm->_gyro->_fields[which]._y2); + +} + +bool Animation::nearDoor() { + if (_vm->_gyro->_fieldNum < 8) { + // there ARE no doors here! + return false; + } + + int16 ux = _sprites[0]._x; + int16 uy = _sprites[0]._y + _sprites[0]._info._yLength; + bool nd = false; + for (byte fv = 8; fv < _vm->_gyro->_fieldNum; fv++) + if ((ux >= _vm->_gyro->_fields[fv]._x1) && (ux <= _vm->_gyro->_fields[fv]._x2) + && (uy >= _vm->_gyro->_fields[fv]._y1) && (uy <= _vm->_gyro->_fields[fv]._y2)) + nd = true; + return nd; +} + +void Animation::handleMoveKey(const Common::Event &event) { + if (!_vm->_gyro->_dna._userMovesAvvy) + return; + + if (_vm->_dropdown->_activeMenuItem._activeNow) + _vm->_parser->tryDropdown(); + else { + switch (event.kbd.keycode) { + case Common::KEYCODE_UP: + if (_vm->_gyro->_dna._direction != kDirUp) { + _vm->_gyro->_dna._direction = kDirUp; + changeDirection(0, _vm->_gyro->_dna._direction); + } else + stopWalking(); + break; + case Common::KEYCODE_DOWN: + if (_vm->_gyro->_dna._direction != kDirDown) { + _vm->_gyro->_dna._direction = kDirDown; + changeDirection(0, _vm->_gyro->_dna._direction); + } else + stopWalking(); + break; + case Common::KEYCODE_LEFT: + if (_vm->_gyro->_dna._direction != kDirLeft) { + _vm->_gyro->_dna._direction = kDirLeft; + changeDirection(0, _vm->_gyro->_dna._direction); + } else + stopWalking(); + break; + case Common::KEYCODE_RIGHT: + if (_vm->_gyro->_dna._direction != kDirRight) { + _vm->_gyro->_dna._direction = kDirRight; + changeDirection(0, _vm->_gyro->_dna._direction); + } else + stopWalking(); + break; + case Common::KEYCODE_PAGEUP: + if (_vm->_gyro->_dna._direction != kDirUpRight) { + _vm->_gyro->_dna._direction = kDirUpRight; + changeDirection(0, _vm->_gyro->_dna._direction); + } else + stopWalking(); + break; + case Common::KEYCODE_PAGEDOWN: + if (_vm->_gyro->_dna._direction != kDirDownRight) { + _vm->_gyro->_dna._direction = kDirDownRight; + changeDirection(0, _vm->_gyro->_dna._direction); + } else + stopWalking(); + break; + case Common::KEYCODE_END: + if (_vm->_gyro->_dna._direction != kDirDownLeft) { + _vm->_gyro->_dna._direction = kDirDownLeft; + changeDirection(0, _vm->_gyro->_dna._direction); + } else + stopWalking(); + break; + case Common::KEYCODE_HOME: + if (_vm->_gyro->_dna._direction != kDirUpLeft) { + _vm->_gyro->_dna._direction = kDirUpLeft; + changeDirection(0, _vm->_gyro->_dna._direction); + } else + stopWalking(); + break; + case Common::KEYCODE_KP5: + stopWalking(); + break; + default: + break; + } + } +} + + +} // End of namespace Avalanche. diff --git a/engines/avalanche/animation.h b/engines/avalanche/animation.h new file mode 100644 index 0000000000..73abadac2e --- /dev/null +++ b/engines/avalanche/animation.h @@ -0,0 +1,160 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on the original source code of Lord Avalot d'Argent version 1.3. + * Copyright (c) 1994-1995 Mike, Mark and Thomas Thurman. + */ + +/* Original name: TRIP5 / Trippancy V - the sprite animation subsystem */ + +#ifndef AVALANCHE_ANIMATION_H +#define AVALANCHE_ANIMATION_H + +#include "avalanche/graphics.h" + +#include "common/scummsys.h" +#include "common/str.h" + +namespace Avalanche { +class AvalancheEngine; +class Animation; + +struct StatType { + Common::String _name; // Name of character. + Common::String _comment; // Comment. + byte _frameNum; // Number of pictures. + byte _seq; // How many in one stride. + byte _fgBubbleCol, _bgBubbleCol; // Foreground & background bubble colors. + byte _acciNum; // The number according to Acci. (1=Avvy, etc.) +}; + +class AnimationType { +public: + SpriteInfo _info; + StatType _stat; // Vital statistics. + byte _facingDir, _stepNum; + int16 _x, _y; // Current xy coords. + int16 _oldX[2], _oldY[2]; // Last xy coords. + int8 _moveX, _moveY; // Amount to move sprite by, each step. + byte _id; + bool _quick, _visible, _homing, _doCheck; + int16 _homingX, _homingY; // Homing x & y coords. + byte _count; // Counts before changing step. + byte _speedX, _speedY; // x & y speed. + byte _animCount; // Total number of sprites. + bool _vanishIfStill; // Do we show this sprite if it's still? + bool _callEachStepFl; // Do we call the eachstep procedure? + byte _eachStepProc; + + void init(byte spritenum, bool doCheck, Animation *anim); // Loads & sets up the sprite. + void original(); // Just sets 'quick' to false. + void draw(); // Drops sprite onto screen. Original: andexor(). + void turn(byte whichway); // Turns character round. + void appear(int16 wx, int16 wy, byte wf); // Switches it on. + void bounce(); // Bounces off walls. + void walk(); // Prepares for andexor, etc. + void walkTo(byte pednum); // Home in on a point. + void stopHoming(); // Self-explanatory. + void homeStep(); // Calculates ix & iy for one homing step. + void setSpeed(int8 xx, int8 yy); // Sets ix & iy, non-homing, etc. + void stopWalk(); // Stops the sprite from moving. + void chatter(); // Sets up talk vars. + void remove(); + +private: + Animation *_anim; + + bool checkCollision(); + int8 getSign(int16 val); +}; + +class Animation { +public: + friend class AnimationType; + + enum Direction { + kDirUp, kDirRight, kDirDown, kDirLeft, + kDirUpRight, kDirDownRight, kDirDownLeft, kDirUpLeft, + kDirStopped + }; + + static const byte kSpriteNumbMax = 5; // current max no. of sprites + + enum Proc { + kProcFollowAvvyY = 1, + kProcBackAndForth, + kProcFaceAvvy, + kProcArrow, + kProcSpludwick, // Unused + kProcGrabAvvy, + kProcGeida // Spludwick uses it as well for homing! TODO: Unify it with kProcSpludwick. + }; + + AnimationType _sprites[kSpriteNumbMax]; + bool _mustExclaim; + uint16 _sayWhat; + + Animation(AvalancheEngine *vm); + ~Animation(); + + void animLink(); + void loadAnims(); // Original: loadtrip(). + void callSpecial(uint16 which); + void openDoor(byte whither, byte ped, byte magicnum); // Handles slidey-open doors. + void catacombMove(byte ped); // When you enter a new position in the catacombs, this procedure should be called. It changes the 'also' codes so that they may match the picture on the screen. + void stopWalking(); + void changeDirection(byte t, byte dir); + void appearPed(byte trn, byte np); + void flipRoom(byte room, byte ped); + bool inField(byte which); // Returns true if you're within field "which". + bool nearDoor(); // Returns True if you're near a door. + void updateSpeed(); + void handleMoveKey(const Common::Event &event); // To replace tripkey(). + +private: + AvalancheEngine *_vm; + + byte checkFeet(int16 x1, int16 x2, int16 oy, int16 y, byte yl); + byte geidaPed(byte which); + void dawnDelay(); + + void grabAvvy(byte tripnum); + void arrowProcs(byte tripnum); + void hideInCupboard(); + + // Different movements for NPCs: + void followAvalotY(byte tripnum); // Original: follow_avvy_y(). + void backAndForth(byte tripnum); + void faceAvvy(byte tripnum); + + // Movements for Homing NPCs: Spludwick and Geida. + void spin(byte whichway, byte &tripnum); + void takeAStep(byte &tripnum); + void geidaProcs(byte tripnum); + + void drawSprites(); // Original: call_andexors(). +}; + +} // End of namespace Avalanche. + +#endif // AVALANCHE_ANIMATION_H diff --git a/engines/avalanche/avalanche.cpp b/engines/avalanche/avalanche.cpp new file mode 100644 index 0000000000..d2e9a8de9b --- /dev/null +++ b/engines/avalanche/avalanche.cpp @@ -0,0 +1,732 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on the original source code of Lord Avalot d'Argent version 1.3. + * Copyright (c) 1994-1995 Mike, Mark and Thomas Thurman. + */ + +#include "avalanche/avalanche.h" + +#include "common/system.h" +#include "common/random.h" +#include "common/error.h" +#include "common/events.h" +#include "common/debug-channels.h" +#include "common/config-manager.h" +#include "common/textconsole.h" +#include "common/savefile.h" + +#include "engines/util.h" + +#include "gui/saveload.h" +#include "graphics/thumbnail.h" + +namespace Avalanche { + +AvalancheEngine *AvalancheEngine::s_Engine = 0; + +AvalancheEngine::AvalancheEngine(OSystem *syst, const AvalancheGameDescription *gd) : Engine(syst), _gameDescription(gd) { + _system = syst; + s_Engine = this; + _console = new AvalancheConsole(this); + + _rnd = new Common::RandomSource("avalanche"); + _rnd->setSeed(42); +} + +AvalancheEngine::~AvalancheEngine() { + delete _console; + delete _rnd; + + delete _graphics; + delete _parser; + + delete _avalot; + delete _pingo; + delete _scrolls; + delete _visa; + delete _lucerna; + delete _enid; + delete _celer; + delete _sequence; + delete _timer; + delete _animation; + delete _acci; + delete _dropdown; + delete _closing; + delete _gyro; +} + +Common::ErrorCode AvalancheEngine::initialize() { + _graphics = new Graphics(this); + _parser = new Parser(this); + + _avalot = new Avalot(this); + _gyro = new Gyro(this); + _pingo = new Pingo(this); + _scrolls = new Scrolls(this); + _visa = new Visa(this); + _lucerna = new Lucerna(this); + _enid = new Enid(this); + _celer = new Celer(this); + _sequence = new Sequence(this); + _timer = new Timer(this); + _animation = new Animation(this); + _acci = new Acci(this); + _dropdown = new Dropdown(this); + _closing = new Closing(this); + + _graphics->init(); + + _scrolls->init(); + _lucerna->init(); + _acci->init(); + _parser->init(); + + return Common::kNoError; +} + + +GUI::Debugger *AvalancheEngine::getDebugger() { + return _console; +} + +Common::Platform AvalancheEngine::getPlatform() const { + return _platform; +} + + + +bool AvalancheEngine::hasFeature(EngineFeature f) const { + return (f == kSupportsSavingDuringRuntime) || (f == kSupportsLoadingDuringRuntime); +} + +const char *AvalancheEngine::getCopyrightString() const { + return "Copyright (c) 1994-1995 Mike, Mark and Thomas Thurman."; +} + + + +void AvalancheEngine::synchronize(Common::Serializer &sz) { + //blockwrite(f, dna, sizeof(dna)); + sz.syncAsByte(_gyro->_dna._direction); + sz.syncAsByte(_gyro->_dna._carryNum); + for (byte i = 0; i < kObjectNum; i++) + sz.syncAsByte(_gyro->_dna._objects[i]); + sz.syncAsSint16LE(_gyro->_dna._score); + sz.syncAsSint32LE(_gyro->_dna._money); + sz.syncAsByte(_gyro->_dna._room); + sz.syncAsByte(_gyro->_dna._wearing); + sz.syncAsByte(_gyro->_dna._sworeNum); + sz.syncAsByte(_gyro->_dna._saveNum); + sz.syncBytes(_gyro->_dna._roomCount, 100); + sz.syncAsByte(_gyro->_dna._alcoholLevel); + sz.syncAsByte(_gyro->_dna._playedNim); + sz.syncAsByte(_gyro->_dna._wonNim); + sz.syncAsByte(_gyro->_dna._wineState); + sz.syncAsByte(_gyro->_dna._cwytalotGone); + sz.syncAsByte(_gyro->_dna._passwordNum); + sz.syncAsByte(_gyro->_dna._aylesIsAwake); + sz.syncAsByte(_gyro->_dna._drawbridgeOpen); + sz.syncAsByte(_gyro->_dna._avariciusTalk); + sz.syncAsByte(_gyro->_dna._boughtOnion); + sz.syncAsByte(_gyro->_dna._rottenOnion); + sz.syncAsByte(_gyro->_dna._onionInVinegar); + sz.syncAsByte(_gyro->_dna._givenToSpludwick); + sz.syncAsByte(_gyro->_dna._brummieStairs); + sz.syncAsByte(_gyro->_dna._cardiffQuestionNum); + sz.syncAsByte(_gyro->_dna._passedCwytalotInHerts); + sz.syncAsByte(_gyro->_dna._avvyIsAwake); + sz.syncAsByte(_gyro->_dna._avvyInBed); + sz.syncAsByte(_gyro->_dna._userMovesAvvy); + sz.syncAsByte(_gyro->_dna._dogFoodPos); + sz.syncAsByte(_gyro->_dna._givenBadgeToIby); + sz.syncAsByte(_gyro->_dna._friarWillTieYouUp); + sz.syncAsByte(_gyro->_dna._tiedUp); + sz.syncAsByte(_gyro->_dna._boxContent); + sz.syncAsByte(_gyro->_dna._talkedToCrapulus); + sz.syncAsByte(_gyro->_dna._jacquesState); + sz.syncAsByte(_gyro->_dna._bellsAreRinging); + sz.syncAsByte(_gyro->_dna._standingOnDais); + sz.syncAsByte(_gyro->_dna._takenPen); + sz.syncAsByte(_gyro->_dna._arrowTriggered); + sz.syncAsByte(_gyro->_dna._arrowInTheDoor); + + if (sz.isSaving()) { + uint16 like2drinkSize = _gyro->_dna._favouriteDrink.size(); + sz.syncAsUint16LE(like2drinkSize); + for (uint16 i = 0; i < like2drinkSize; i++) { + char actChr = _gyro->_dna._favouriteDrink[i]; + sz.syncAsByte(actChr); + } + + uint16 favourite_songSize = _gyro->_dna._favouriteSong.size(); + sz.syncAsUint16LE(favourite_songSize); + for (uint16 i = 0; i < favourite_songSize; i++) { + char actChr = _gyro->_dna._favouriteSong[i]; + sz.syncAsByte(actChr); + } + + uint16 worst_place_on_earthSize = _gyro->_dna._worstPlaceOnEarth.size(); + sz.syncAsUint16LE(worst_place_on_earthSize); + for (uint16 i = 0; i < worst_place_on_earthSize; i++) { + char actChr = _gyro->_dna._worstPlaceOnEarth[i]; + sz.syncAsByte(actChr); + } + + uint16 spare_eveningSize = _gyro->_dna._spareEvening.size(); + sz.syncAsUint16LE(spare_eveningSize); + for (uint16 i = 0; i < spare_eveningSize; i++) { + char actChr = _gyro->_dna._spareEvening[i]; + sz.syncAsByte(actChr); + } + } else { + if (!_gyro->_dna._favouriteDrink.empty()) + _gyro->_dna._favouriteDrink.clear(); + uint16 like2drinkSize = 0; + char actChr = ' '; + sz.syncAsUint16LE(like2drinkSize); + for (uint16 i = 0; i < like2drinkSize; i++) { + sz.syncAsByte(actChr); + _gyro->_dna._favouriteDrink += actChr; + } + + if (!_gyro->_dna._favouriteSong.empty()) + _gyro->_dna._favouriteSong.clear(); + uint16 favourite_songSize = 0; + sz.syncAsUint16LE(favourite_songSize); + for (uint16 i = 0; i < favourite_songSize; i++) { + sz.syncAsByte(actChr); + _gyro->_dna._favouriteSong += actChr; + } + + if (!_gyro->_dna._worstPlaceOnEarth.empty()) + _gyro->_dna._worstPlaceOnEarth.clear(); + uint16 worst_place_on_earthSize = 0; + sz.syncAsUint16LE(worst_place_on_earthSize); + for (uint16 i = 0; i < worst_place_on_earthSize; i++) { + sz.syncAsByte(actChr); + _gyro->_dna._worstPlaceOnEarth += actChr; + } + + if (!_gyro->_dna._spareEvening.empty()) + _gyro->_dna._spareEvening.clear(); + uint16 spare_eveningSize = 0; + sz.syncAsUint16LE(spare_eveningSize); + for (uint16 i = 0; i < spare_eveningSize; i++) { + sz.syncAsByte(actChr); + _gyro->_dna._spareEvening += actChr; + } + } + + sz.syncAsSint32LE(_gyro->_dna._totalTime); + sz.syncAsByte(_gyro->_dna._jumpStatus); + sz.syncAsByte(_gyro->_dna._mushroomGrowing); + sz.syncAsByte(_gyro->_dna._spludwickAtHome); + sz.syncAsByte(_gyro->_dna._lastRoom); + sz.syncAsByte(_gyro->_dna._lastRoomNotMap); + sz.syncAsByte(_gyro->_dna._crapulusWillTell); + sz.syncAsByte(_gyro->_dna._enterCatacombsFromLustiesRoom); + sz.syncAsByte(_gyro->_dna._teetotal); + sz.syncAsByte(_gyro->_dna._malagauche); + sz.syncAsByte(_gyro->_dna._drinking); + sz.syncAsByte(_gyro->_dna._enteredLustiesRoomAsMonk); + sz.syncAsByte(_gyro->_dna._catacombX); + sz.syncAsByte(_gyro->_dna._catacombY); + sz.syncAsByte(_gyro->_dna._avvysInTheCupboard); + sz.syncAsByte(_gyro->_dna._geidaFollows); + sz.syncAsByte(_gyro->_dna._geidaSpin); + sz.syncAsByte(_gyro->_dna._geidaTime); + sz.syncAsByte(_gyro->_dna._nextBell); + sz.syncAsByte(_gyro->_dna._givenPotionToGeida); + sz.syncAsByte(_gyro->_dna._lustieIsAsleep); + sz.syncAsByte(_gyro->_dna._flipToWhere); + sz.syncAsByte(_gyro->_dna._flipToPed); + sz.syncAsByte(_gyro->_dna._beenTiedUp); + sz.syncAsByte(_gyro->_dna._sittingInPub); + sz.syncAsByte(_gyro->_dna._spurgeTalkCount); + sz.syncAsByte(_gyro->_dna._metAvaroid); + sz.syncAsByte(_gyro->_dna._takenMushroom); + sz.syncAsByte(_gyro->_dna._givenPenToAyles); + sz.syncAsByte(_gyro->_dna._askedDogfoodAboutNim); + + +#if 0 + for (byte groi = 0; groi < numtr; groi++) { + if (tr[groi].quick) { + blockwrite(f, groi, 1); + tr[groi].savedata(f); + } + } +#endif + + byte spriteNum = 0; + if (sz.isSaving()) { + for (byte i = 0; i < _animation->kSpriteNumbMax; i++) { + if (_animation->_sprites[i]._quick) + spriteNum++; + } + } + sz.syncAsByte(spriteNum); + + if (sz.isLoading()) { + for (byte i = 0; i < _animation->kSpriteNumbMax; i++) { // Deallocate sprites. + if (_animation->_sprites[i]._quick) + _animation->_sprites[i].remove(); + } + } + + for (byte i = 0; i < spriteNum; i++) { + sz.syncAsByte(_animation->_sprites[i]._id); + sz.syncAsByte(_animation->_sprites[i]._doCheck); + + + if (sz.isLoading()) { + _animation->_sprites[i]._quick = true; + _animation->_sprites[i].init(_animation->_sprites[i]._id, _animation->_sprites[i]._doCheck, _animation); + } + + sz.syncAsByte(_animation->_sprites[i]._moveX); + sz.syncAsByte(_animation->_sprites[i]._moveY); + sz.syncAsByte(_animation->_sprites[i]._facingDir); + sz.syncAsByte(_animation->_sprites[i]._stepNum); + sz.syncAsByte(_animation->_sprites[i]._visible); + sz.syncAsByte(_animation->_sprites[i]._homing); + sz.syncAsByte(_animation->_sprites[i]._count); + sz.syncAsByte(_animation->_sprites[i]._info._xWidth); + sz.syncAsByte(_animation->_sprites[i]._speedX); + sz.syncAsByte(_animation->_sprites[i]._speedY); + sz.syncAsByte(_animation->_sprites[i]._animCount); + sz.syncAsSint16LE(_animation->_sprites[i]._homingX); + sz.syncAsSint16LE(_animation->_sprites[i]._homingY); + sz.syncAsByte(_animation->_sprites[i]._callEachStepFl); + sz.syncAsByte(_animation->_sprites[i]._eachStepProc); + sz.syncAsByte(_animation->_sprites[i]._vanishIfStill); + + sz.syncAsSint16LE(_animation->_sprites[i]._x); + sz.syncAsSint16LE(_animation->_sprites[i]._y); + + if (sz.isLoading() && _animation->_sprites[i]._visible) + _animation->_sprites[i].appear(_animation->_sprites[i]._x, _animation->_sprites[i]._y, _animation->_sprites[i]._facingDir); + } + + //groi = 177; + //blockwrite(f, groi, 1); + + //blockwrite(f, times, sizeof(times)); // Timeout.times: Timers. + for (byte i = 0; i < 7; i++) { + sz.syncAsSint32LE(_timer->_times[i]._timeLeft); + sz.syncAsByte(_timer->_times[i]._action); + sz.syncAsByte(_timer->_times[i]._reason); + } + + //blockwrite(f, seq, sizeof(seq)); // Sequencer information. + sz.syncBytes(_sequence->_seq, _sequence->kSeqLength); +} + +bool AvalancheEngine::canSaveGameStateCurrently() { // TODO: Refine these!!! + return (!_gyro->_seeScroll && _gyro->_alive); +} + +Common::Error AvalancheEngine::saveGameState(int slot, const Common::String &desc) { + return (saveGame(slot, desc) ? Common::kNoError : Common::kWritingFailed); +} + +bool AvalancheEngine::saveGame(const int16 slot, const Common::String &desc) { + Common::String fileName = getSaveFileName(slot); + Common::OutSaveFile *f = g_system->getSavefileManager()->openForSaving(fileName); + if (!f) { + warning("Can't create file '%s', game not saved.", fileName.c_str()); + return false; + } + + const char *signature = "AVAL"; + f->write(signature, 4); + + // Write version. We can't restore from obsolete versions. + f->writeByte(kSavegameVersion); + + f->writeUint32LE(desc.size()); + f->write(desc.c_str(), desc.size()); + ::Graphics::saveThumbnail(*f); + + TimeDate t; + _system->getTimeAndDate(t); + f->writeSint16LE(t.tm_mday); + f->writeSint16LE(t.tm_mon); + f->writeSint16LE(t.tm_year); + + Common::Serializer sz(NULL, f); + + synchronize(sz); + + f->finalize(); + + delete f; + + return true; +} + + + +Common::String AvalancheEngine::getSaveFileName(const int slot) { + Common::String upperName = _targetName; + upperName.toUppercase(); + return upperName+ Common::String::format("-%02d.SAV", slot); +} + + + +bool AvalancheEngine::canLoadGameStateCurrently() { // TODO: Refine these!!! + return (!_gyro->_seeScroll); +} + +Common::Error AvalancheEngine::loadGameState(int slot) { + return (loadGame(slot) ? Common::kNoError : Common::kReadingFailed); +} + +bool AvalancheEngine::loadGame(const int16 slot) { + Common::String fileName = getSaveFileName(slot); + Common::InSaveFile *f = g_system->getSavefileManager()->openForLoading(fileName); + if (!f) + return false; + + // Check for our signature. + Common::String signature; + for (byte i = 0; i < 4; i++) + signature += f->readByte(); + if (signature != "AVAL") + return false; + + // Check version. We can't restore from obsolete versions. + byte saveVersion = f->readByte(); + if (saveVersion != kSavegameVersion) { + warning("Savegame of incompatible version!"); + delete f; + return false; + } + + // Read the description. + uint32 descSize = f->readUint32LE(); + Common::String description; + for (uint32 i = 0; i < descSize; i++) { + char actChar = f->readByte(); + description += actChar; + } + description.toUppercase(); + + ::Graphics::skipThumbnail(*f); + + // Read the time the game was saved. + TimeDate t; + t.tm_mday = f->readSint16LE(); + t.tm_mon = f->readSint16LE(); + t.tm_year = f->readSint16LE(); + + Common::Serializer sz(f, NULL); + + synchronize(sz); + + delete f; + + _gyro->isLoaded = true; + + _gyro->_seeScroll = true; // This prevents display of the new sprites before the new picture is loaded. + + if (_gyro->_holdTheDawn) { + _gyro->_holdTheDawn = false; + _lucerna->dawn(); + } + + _celer->forgetBackgroundSprites(); + + _lucerna->minorRedraw(); + + _dropdown->setupMenu(); + + _gyro->_whereIs[0] = _gyro->_dna._room; + + _gyro->_alive = true; + + _lucerna->refreshObjectList(); + + _animation->updateSpeed(); + + _lucerna->drawDirection(); + + _gyro->_onToolbar = false; + _animation->animLink(); + + _celer->updateBackgroundSprites(); + + _scrolls->displayText(Common::String(_scrolls->kControlItalic) + "Loaded: " + _scrolls->kControlRoman + description + ".ASG" + + _scrolls->kControlCenter + _scrolls->kControlNewLine + _scrolls->kControlNewLine + + _gyro->_roomnName + _scrolls->kControlNewLine + _scrolls->kControlNewLine + + "saved on " + expandDate(t.tm_mday, t.tm_mon, t.tm_year) + '.'); + + if (_animation->_sprites[0]._quick && _animation->_sprites[0]._visible) + _animation->changeDirection(0, _gyro->_dna._direction); // We push Avvy in the right direction is he was moving. + + return true; +} + +Common::String AvalancheEngine::expandDate(int d, int m, int y) { + static const Common::String months[12] = { + "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" + }; + + Common::String month = months[m]; + + Common::String day = _gyro->intToStr(d); + + if (((1 <= d) && (d <= 9)) || ((21 <= d) && (d <= 31))) + switch (d % 10) { + case 1: + day = day + "st"; + break; + case 2: + day = day + "nd"; + break; + case 3: + day = day + "rd"; + break; + default: + day = day + "th"; + } + + return day + ' ' + month + ' ' + _gyro->intToStr(y + 1900); +} + + + +void AvalancheEngine::updateEvents() { + Common::Event event; + + while (_eventMan->pollEvent(event)) { + switch (event.type) { + case Common::EVENT_LBUTTONDOWN: + _lucerna->_holdLeftMouse = true; // Used in Lucerna::checkclick() and Dropdown::menu_link(). + break; + case Common::EVENT_LBUTTONUP: + _lucerna->_holdLeftMouse = false; // Same as above. + break; + case Common::EVENT_KEYDOWN: + _avalot->handleKeyDown(event); + break; + default: + break; + } + } +} + +bool AvalancheEngine::getEvent(Common::Event &event) { + return _eventMan->pollEvent(event); +} + +Common::Point AvalancheEngine::getMousePos() { + return _eventMan->getMousePos(); +} + + + + + +// From Bootstrp: + +const char AvalancheEngine::kRuncodes[2][3] = {"et", "Go"}; + + + +// The original ones were all commented out, so porbably there's no need +// of these two cursor functions at all. TODO: Remove later. +void AvalancheEngine::cursorOff() { + warning("STUB: cursorOff()"); +} + +void AvalancheEngine::cursorOn() { + warning("STUB: cursorOn()"); +} + +// Needed later. +void AvalancheEngine::quit() { + cursorOn(); +} + +// Needed in dos_shell(). TODO: Remove later. +Common::String AvalancheEngine::commandCom() { + warning("STUB: commandCom()"); + return ("STUB: commandCom()"); +} + +// Needed for run_avalot()'s errors. TODO: Remove later. +void AvalancheEngine::explain(byte error) { + warning("STUB: explain()"); +} + + + +//TODO: Remove these (b_flight) functions later ( https://github.com/tthurman/avalot/wiki/B-Flight ) + +void AvalancheEngine::bFlight() { //interrupt; + _storage._skellern++; +} + +void AvalancheEngine::bFlightOn() { + _storage._skellern = kReset; + // setintvec(0x1c, &b_flight); +} + +void AvalancheEngine::bFlightOff() { + // setintvec(0x1c, old_1c); +} + + + +Common::String AvalancheEngine::elmToStr(Elm how) { + switch (how) { + case kNormal: + case kMusical: + return Common::String("jsb"); + case kRegi: + return Common::String("REGI"); + case kElmpoyten: + return Common::String("ELMPOYTEN"); + // Useless, but silent a warning + default: + return Common::String(""); + } +} + +void AvalancheEngine::run(Common::String what, bool withJsb, bool withBflight, Elm how) { + warning("STUB: run(%s)", what.c_str()); + // Probably there'll be no need of this function, as all *.AVX-es will become classes. +} + +void AvalancheEngine::getArguments() { + // This function should mess around with command line arguments, + // but I am not sure if there'll be use of these arguments at all... + warning("STUB: getArguments()"); +} + +void AvalancheEngine::getSlope() { + // Same as get_arguments() + warning("STUB: getSlope()"); +} + +void AvalancheEngine::callMenu() { + warning("STUB: callMenu()"); +} + +void AvalancheEngine::runDemo() { + warning("STUB: runDemo()"); +} + +void AvalancheEngine::dosShell() { + warning("STUB: dosShell()"); +} + +// Getting used only in demo() / call_menu(). Going to be implemented at the same time with these. +bool AvalancheEngine::keyPressed() { + warning("STUB: keyPressed()"); + return false; +} + +// Same as keypressed1(). +void AvalancheEngine::flushBuffer() { + warning("STUB: flushBuffer()"); +} + +// Same as keypressed1(). +void AvalancheEngine::demo() { + warning("STUB: demo()"); +} + + + + +void AvalancheEngine::runAvalot() { + bFlightOn(); + + _avalot->run(Common::String(kRuncodes[_firstTime]) + _arguments); + // TODO: Check if parameteres are ever used (probably not) and eventually remove them. + // If there's an error initalizing avalot, i'll handle it in there, not here + + _firstTime = false; +} + + + +Common::Error AvalancheEngine::run() { + Common::ErrorCode err = initialize(); + if (err != Common::kNoError) + return err; + + + + // From bootstrp: + + _firstTime = true; + + getArguments(); + getSlope(); + + _zoomy = true; + // Don't call the menu by default. Might be modified later, if get_slope() gets implemented, + // because zoomy's value is given there. Not sure yet what "zoomy" stands for. + if (!_zoomy) + callMenu(); // Not run when zoomy. + + + + do { + runAvalot(); + + // Needed for later implementation!!! Don't remove these comments!!! + + //if (dosexitcode != 77) quit(); // Didn't stop for us. + + //switch (_storage._operation) { + //case kRunShootemup: + // run("seu.avx", kJsb, kBflight, kNormal); + // break; + //case kRunDosshell: + // dosShell(); + // break; + //case kRunGhostroom: + // run("g-room.avx", kJsb, kNoBflight, kNormal); + // break; + //case kRunGolden: + // run("golden.avx", kJsb, kBflight, kMusical); + // break; + //} + + } while (!shouldQuit()); + + + + return Common::kNoError; +} + + + +} // End of namespace Avalanche diff --git a/engines/avalanche/avalanche.h b/engines/avalanche/avalanche.h new file mode 100644 index 0000000000..998363a242 --- /dev/null +++ b/engines/avalanche/avalanche.h @@ -0,0 +1,175 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on the original source code of Lord Avalot d'Argent version 1.3. + * Copyright (c) 1994-1995 Mike, Mark and Thomas Thurman. + */ + +#ifndef AVALANCHE_AVALANCHE_H +#define AVALANCHE_AVALANCHE_H + +#include "avalanche/console.h" + +#include "avalanche/graphics.h" +#include "avalanche/parser.h" + +#include "avalanche/avalot.h" +#include "avalanche/gyro2.h" +#include "avalanche/pingo2.h" +#include "avalanche/scrolls2.h" +#include "avalanche/visa2.h" +#include "avalanche/lucerna2.h" +#include "avalanche/enid2.h" +#include "avalanche/celer2.h" +#include "avalanche/sequence2.h" +#include "avalanche/timer.h" +#include "avalanche/animation.h" +#include "avalanche/acci2.h" +#include "avalanche/dropdown2.h" +#include "avalanche/closing2.h" + +#include "common/serializer.h" + +#include "engines/engine.h" +#include "engines/advancedDetector.h" + +#include "graphics/cursorman.h" + +namespace Common { +class RandomSource; +} + +namespace Avalanche { + +struct AvalancheGameDescription; + +static const int kSavegameVersion = 1; + +class AvalancheEngine : public Engine { +public: + Graphics *_graphics; + Parser *_parser; + + Avalot *_avalot; + Gyro *_gyro; + Pingo *_pingo; + Scrolls *_scrolls; + Visa *_visa; + Lucerna *_lucerna; + Enid *_enid; + Celer *_celer; + Sequence *_sequence; + Timer *_timer; + Animation *_animation; + Acci *_acci; + Dropdown *_dropdown; + Closing *_closing; + + OSystem *_system; + + AvalancheEngine(OSystem *syst, const AvalancheGameDescription *gd); + ~AvalancheEngine(); + + Common::ErrorCode initialize(); + GUI::Debugger *getDebugger(); + + Common::RandomSource *_rnd; + + const AvalancheGameDescription *_gameDescription; + uint32 getFeatures() const; + const char *getGameId() const; + Common::Platform getPlatform() const; + bool hasFeature(EngineFeature f) const; + const char *getCopyrightString() const; + + void synchronize(Common::Serializer &sz); + virtual bool canSaveGameStateCurrently(); + Common::Error saveGameState(int slot, const Common::String &desc); + bool saveGame(const int16 slot, const Common::String &desc); + Common::String getSaveFileName(const int slot); + virtual bool canLoadGameStateCurrently(); + Common::Error loadGameState(int slot); + bool loadGame(const int16 slot); + Common::String expandDate(int d, int m, int y); + + void updateEvents(); + bool getEvent(Common::Event &event); // A wrapper around _eventMan->pollEvent(), so we can use it in Scrolls::normscroll() for example. + Common::Point getMousePos(); + +protected: + // Engine APIs + Common::Error run(); + +private: + static AvalancheEngine *s_Engine; + + AvalancheConsole *_console; + Common::Platform _platform; + + // From bootstrp: + + enum Elm {kNormal, kMusical, kElmpoyten, kRegi}; + + static const int16 kRunShootemup = 1, kRunDosshell = 2, kRunGhostroom = 3, kRunGolden = 4; + static const char kRuncodes[2][3]; + static const int16 kReset = 0; + static const bool kJsb = true, kNoJsb = false, kBflight = true, kNoBflight = false; + + struct { + byte _operation; + uint16 _skellern; + byte _contents[1000]; + } _storage; + + Common::String _arguments, _demoArgs, _argsWithNoFilename; + bool _firstTime; + byte _originalMode; + byte *_old1c; + Common::String _segofs; + bool _zoomy; + int32 _soundcard, _speed, _baseaddr, _irq, _dma; + + void cursorOff(); + void cursorOn(); + void quit(); + Common::String commandCom(); + void explain(byte error); + void bFlight(); + void bFlightOn(); + void bFlightOff(); + Common::String elmToStr(Elm how); + void run(Common::String what, bool withJsb, bool withBflight, Elm how); + void getArguments(); + void getSlope(); + void callMenu(); + void runAvalot(); + void runDemo(); + void dosShell(); + bool keyPressed(); + void flushBuffer(); + void demo(); +}; + +} // End of namespace Avalanche + +#endif // AVALANCHE_AVALANCHE_H diff --git a/engines/avalanche/avalot.cpp b/engines/avalanche/avalot.cpp new file mode 100644 index 0000000000..99c3950ef1 --- /dev/null +++ b/engines/avalanche/avalot.cpp @@ -0,0 +1,237 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on the original source code of Lord Avalot d'Argent version 1.3. + * Copyright (c) 1994-1995 Mike: Mark and Thomas Thurman. + */ + +/* AVALOT The kernel of the program. */ + +#include "avalanche/avalanche.h" + +#include "avalanche/graphics.h" +#include "avalanche/avalot.h" +#include "avalanche/gyro2.h" +#include "avalanche/animation.h" +#include "avalanche/gyro2.h" +#include "avalanche/lucerna2.h" +#include "avalanche/scrolls2.h" +#include "avalanche/dropdown2.h" +#include "avalanche/pingo2.h" +#include "avalanche/timer.h" +#include "avalanche/celer2.h" +#include "avalanche/enid2.h" +#include "avalanche/visa2.h" +#include "avalanche/closing2.h" + +#include "common/str.h" +#include "common/textconsole.h" +#include "common/config-manager.h" + + + +namespace Avalanche { + + + +Avalot::Avalot(AvalancheEngine *vm) { + _vm = vm; +} + +void Avalot::handleKeyDown(Common::Event &event) { + //if (keyboardclick) + // click(); + // + // To be implemented later with the sounds, I assume. + + + if ((Common::KEYCODE_F1 <= event.kbd.keycode) && (event.kbd.keycode <= Common::KEYCODE_F15)) + _vm->_parser->handleFunctionKey(event); + else if ((32 <= event.kbd.ascii) && (event.kbd.ascii <= 128) && (event.kbd.ascii != 47)) + _vm->_parser->handleInputText(event); + else + switch (event.kbd.keycode) { // We can control Avvy with the numpad as well. + case Common::KEYCODE_KP8: + event.kbd.keycode = Common::KEYCODE_UP; + break; + case Common::KEYCODE_KP2: + event.kbd.keycode = Common::KEYCODE_DOWN; + break; + case Common::KEYCODE_KP6: + event.kbd.keycode = Common::KEYCODE_RIGHT; + break; + case Common::KEYCODE_KP4: + event.kbd.keycode = Common::KEYCODE_LEFT; + break; + case Common::KEYCODE_KP9: + event.kbd.keycode = Common::KEYCODE_PAGEUP; + break; + case Common::KEYCODE_KP3: + event.kbd.keycode = Common::KEYCODE_PAGEDOWN; + break; + case Common::KEYCODE_KP7: + event.kbd.keycode = Common::KEYCODE_HOME; + break; + case Common::KEYCODE_KP1: + event.kbd.keycode = Common::KEYCODE_END; + break; + default: + break; + } + + switch (event.kbd.keycode) { + case Common::KEYCODE_UP: + case Common::KEYCODE_DOWN: + case Common::KEYCODE_RIGHT: + case Common::KEYCODE_LEFT: + case Common::KEYCODE_PAGEUP: + case Common::KEYCODE_PAGEDOWN: + case Common::KEYCODE_HOME: + case Common::KEYCODE_END: + case Common::KEYCODE_KP5: + if (_vm->_gyro->_alive && _vm->_gyro->_dna._avvyIsAwake) { + _vm->_animation->handleMoveKey(event); // Fallthroughs are intended. + _vm->_lucerna->drawDirection(); + return; + } + case Common::KEYCODE_BACKSPACE: + _vm->_parser->handleBackspace(); + break; + case Common::KEYCODE_RETURN: + _vm->_parser->handleReturn(); + break; + default: + break; + } + + _vm->_lucerna->drawDirection(); +} + + + +void Avalot::setup() { + _vm->_gyro->_mouse = _vm->_gyro->kMouseStateNo; + _vm->_gyro->_letMeOut = false; + _vm->_scrolls->resetScroll(); + CursorMan.showMouse(true); + _vm->_gyro->_holdTheDawn = true; + _vm->_lucerna->dusk(); + _vm->_gyro->_currentMouse = 177; + _vm->_gyro->setMousePointerWait(); + _vm->_gyro->_dropsOk = true; + _vm->_gyro->_mouseText = ""; + _vm->_gyro->_dropdownActive = false; + _vm->_lucerna->loadDigits(); + _vm->_gyro->_cheat = false; + _vm->_gyro->_cp = 0; + _vm->_parser->_inputTextPos = 0; + _vm->_parser->_quote = true; + _vm->_gyro->_ledStatus = 177; + _vm->_gyro->_defaultLed = 2; + // TSkellern = 0; Replace with a more local variable sometime + _vm->_gyro->_dna._direction = _vm->_gyro->kDirectionStopped; + _vm->_gyro->_enidFilename = ""; // Undefined. + _vm->_lucerna->drawToolbar(); + _vm->_scrolls->setReadyLight(2); + for (byte i = 0; i < 3; i++) + _vm->_gyro->_scoreToDisplay[i] = -1; // Impossible digits. + + _vm->_animation->loadAnims(); + + _vm->_gyro->_holdTheDawn = false; + _vm->_lucerna->dawn(); + _vm->_parser->_cursorState = false; + _vm->_parser->cursorOn(); + _vm->_animation->_sprites[0]._speedX = _vm->_gyro->kWalk; + _vm->_animation->updateSpeed(); + + + + int16 loadSlot = Common::ConfigManager::instance().getInt("save_slot"); + if (loadSlot >= 0) { + _vm->_gyro->_thinks = 2; // You always have money. + _vm->_lucerna->thinkAbout(_vm->_gyro->kObjectMoney, Gyro::kThing); + + _vm->loadGame(loadSlot); + } else { + _vm->_gyro->isLoaded = false; // Set to true in _vm->loadGame(). + _vm->_gyro->newGame(); // No game was requested- load the default. + + _vm->_gyro->_soundFx = ! _vm->_gyro->_soundFx; + _vm->_lucerna->fxToggle(); + _vm->_lucerna->thinkAbout(_vm->_gyro->kObjectMoney, Gyro::kThing); + + _vm->_visa->displayScrollChain('q', 83); // Info on the game, etc. + } +} + + + +void Avalot::run(Common::String arg) { + setup(); + + do { + uint32 beginLoop = _vm->_system->getMillis(); + + _vm->updateEvents(); // The event handler. + + + + _vm->_lucerna->_clock.update(); + _vm->_dropdown->updateMenu(); + _vm->_gyro->forceNumlock(); + _vm->_celer->updateBackgroundSprites(); + _vm->_animation->animLink(); + _vm->_lucerna->checkClick(); + _vm->_timer->updateTimer(); + + + +#ifdef DEBUG + // ONLY FOR TESTING!!! + for (byte i = 0; i < _vm->_gyro->_lineNum; i++) + _vm->_graphics->_surface.drawLine(_vm->_gyro->_lines[i]._x1, _vm->_gyro->_lines[i]._y1, _vm->_gyro->_lines[i]._x2, _vm->_gyro->_lines[i]._y2, _vm->_gyro->_lines[i].col); + + for (byte i = 0; i < _vm->_gyro->_fieldNum; i++) { + if (_vm->_gyro->_fields[i]._x1 < 640) + _vm->_graphics->_surface.frameRect(Common::Rect(_vm->_gyro->_fields[i]._x1, _vm->_gyro->_fields[i]._y1, _vm->_gyro->_fields[i]._x2, _vm->_gyro->_fields[i]._y2), kColorLightmagenta); + } + // ONLY FOR TESTING!!! +#endif + + + _vm->_graphics->refreshScreen(); // TODO: Maybe it'll have a better place later. Move it there when it's needed. + + uint32 delay = _vm->_system->getMillis() - beginLoop; + if (delay <= 55) + _vm->_system->delayMillis(55 - delay); // Replaces _vm->_gyro->slowdown(); 55 comes from 18.2 Hz (B Flight). + } while (!_vm->_gyro->_letMeOut && !_vm->shouldQuit()); + + //if (logging) + // close(logfile); + warning("STUB: Avalot::run()"); + + _vm->_closing->exitGame(); +} + +} // End of namespace Avalanche diff --git a/engines/avalanche/avalot.h b/engines/avalanche/avalot.h new file mode 100644 index 0000000000..34a9d9f7ba --- /dev/null +++ b/engines/avalanche/avalot.h @@ -0,0 +1,53 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on the original source code of Lord Avalot d'Argent version 1.3. + * Copyright (c) 1994-1995 Mike, Mark and Thomas Thurman. + */ + +/* AVALOT The kernel of the program. */ + +#ifndef AVALANCHE_AVALOT_H +#define AVALANCHE_AVALOT_H + +#include "common/events.h" +#include "common/system.h" + +namespace Avalanche { +class AvalancheEngine; + +class Avalot { +public: + Avalot(AvalancheEngine *vm); + + void handleKeyDown(Common::Event &event); // To replace Basher::keyboard_link() and Basher::typein(). + void setup(); + void run(Common::String arg); + +private: + AvalancheEngine *_vm; +}; + +} // End of namespace Avalanche + +#endif // AVALANCHE_AVALOT_H diff --git a/engines/avalanche/celer2.cpp b/engines/avalanche/celer2.cpp new file mode 100644 index 0000000000..5069a92e0a --- /dev/null +++ b/engines/avalanche/celer2.cpp @@ -0,0 +1,391 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on the original source code of Lord Avalot d'Argent version 1.3. + * Copyright (c) 1994-1995 Mike, Mark and Thomas Thurman. + */ + +/* CELER The unit for updating the screen pics. */ + +#include "avalanche/avalanche.h" + +#include "avalanche/celer2.h" +#include "avalanche/animation.h" +#include "avalanche/lucerna2.h" +#include "avalanche/gyro2.h" +#include "avalanche/roomnums.h" + +#include "common/textconsole.h" + +namespace Avalanche { + +const int16 Celer::kOnDisk = -1; + +Celer::Celer(AvalancheEngine *vm) { + _vm = vm; + _spriteNum = 0; +} + +Celer::~Celer() { + forgetBackgroundSprites(); +} + +/** + * @remarks Originally called 'pics_link' + */ +void Celer::updateBackgroundSprites() { + if (_vm->_gyro->_dropdownActive) + return; // No animation when the menus are up. + + switch (_vm->_gyro->_dna._room) { + case r__outsideargentpub: + if ((_vm->_gyro->_roomTime % 12) == 0) + drawBackgroundSprite(-1, -1, 1 + (_vm->_gyro->_roomTime / 12) % 4); + break; + case r__brummieroad: + if ((_vm->_gyro->_roomTime % 2) == 0) + drawBackgroundSprite(-1, -1, 1 + (_vm->_gyro->_roomTime / 2) % 4); + break; + case r__bridge: + if ((_vm->_gyro->_roomTime % 2) == 0) + drawBackgroundSprite(-1, -1, 4 + (_vm->_gyro->_roomTime / 2) % 4); + break; + case r__yours: + if ((!_vm->_gyro->_dna._avvyIsAwake) && ((_vm->_gyro->_roomTime % 4) == 0)) + drawBackgroundSprite(-1, -1, 1 + (_vm->_gyro->_roomTime / 12) % 2); + break; + case r__argentpub: + if (((_vm->_gyro->_roomTime % 7) == 1) && (_vm->_gyro->_dna._malagauche != 177)) { + // Malagauche cycle. + _vm->_gyro->_dna._malagauche += 1; + switch (_vm->_gyro->_dna._malagauche) { + case 1: + case 11: + case 21: + drawBackgroundSprite(-1, -1, 12); // Looks forwards. + break; + case 8: + case 18: + case 28: + case 32: + drawBackgroundSprite(-1, -1, 11); // Looks at you. + break; + case 30: + drawBackgroundSprite(-1, -1, 13); // Winks. + break; + case 33: + _vm->_gyro->_dna._malagauche = 0; + break; + } + } + + switch (_vm->_gyro->_roomTime % 200) { + case 179: + case 197: + drawBackgroundSprite(-1, -1, 5); // Dogfood's drinking cycle. + break; + case 182: + case 194: + drawBackgroundSprite(-1, -1, 6); + break; + case 185: + drawBackgroundSprite(-1, -1, 7); + break; + case 199: + _vm->_gyro->_dna._dogFoodPos = 177; // Impossible value for this. + break; + } + + if ((_vm->_gyro->_roomTime % 200 >= 0) && (_vm->_gyro->_roomTime % 200 <= 178)) { // Normally. + byte direction = 0; + if (((_vm->_lucerna->bearing(2) >= 1) && (_vm->_lucerna->bearing(2) <= 90)) || ((_vm->_lucerna->bearing(2) >= 358) && (_vm->_lucerna->bearing(2) <= 360))) + direction = 3; + else if ((_vm->_lucerna->bearing(2) >= 293) && (_vm->_lucerna->bearing(2) <= 357)) + direction = 2; + else if ((_vm->_lucerna->bearing(2) >= 271) && (_vm->_lucerna->bearing(2) <= 292)) + direction = 4; + + if (direction != _vm->_gyro->_dna._dogFoodPos) { // Only if it's changed. + drawBackgroundSprite(-1, -1, direction); + _vm->_gyro->_dna._dogFoodPos = direction; + } + } + break; + case r__westhall: + if ((_vm->_gyro->_roomTime % 3) == 0) { + switch ((_vm->_gyro->_roomTime / int32(3)) % int32(6)) { + case 4: + drawBackgroundSprite(-1, -1, 1); + break; + case 1: + case 3: + case 5: + drawBackgroundSprite(-1, -1, 2); + break; + case 0: + case 2: + drawBackgroundSprite(-1, -1, 3); + break; + } + } + break; + case r__lustiesroom: + if (!(_vm->_gyro->_dna._lustieIsAsleep)) { + byte direction = 0; + uint16 angle = _vm->_lucerna->bearing(2); + if ((_vm->_gyro->_roomTime % 45) > 42) + direction = 4; // du Lustie blinks. + // Bearing of Avvy from du Lustie. + else if ((angle <= 45) || ((angle >= 315) && (angle <= 360))) + direction = 1; // Middle. + else if ((angle >= 45) && (angle <= 180)) + direction = 2; // Left. + else if ((angle >= 181) && (angle <= 314)) + direction = 3; // Right. + + if (direction != _vm->_gyro->_dna._dogFoodPos) { // Only if it's changed. + drawBackgroundSprite(-1, -1, direction); + _vm->_gyro->_dna._dogFoodPos = direction; // We use DogfoodPos here too - why not? + } + } + break; + case r__aylesoffice: + if ((!_vm->_gyro->_dna._aylesIsAwake) && (_vm->_gyro->_roomTime % 14 == 0)) { + switch ((_vm->_gyro->_roomTime / 14) % 2) { + case 0: + drawBackgroundSprite(-1, -1, 1); // Frame 2: EGA. + break; + case 1: + drawBackgroundSprite(-1, -1, 3); // Frame 1: Natural. + break; + } + } + break; + case r__robins: + if (_vm->_gyro->_dna._tiedUp) { + switch (_vm->_gyro->_roomTime % 54) { + case 20: + drawBackgroundSprite(-1, -1, 4); // Frame 4: Avalot blinks. + break; + case 23: + drawBackgroundSprite(-1, -1, 2); // Frame 1: Back to normal. + break; + } + } + break; + case r__nottspub: { + // Bearing of Avvy from Port. + byte direction = 0; + uint16 angle = _vm->_lucerna->bearing(5); + if ((angle <= 45) || ((angle >= 315) && (angle <= 360))) + direction = 2; // Middle. + else if ((angle >= 45) && (angle <= 180)) + direction = 6; // Left. + else if ((angle >= 181) && (angle <= 314)) + direction = 8; // Right. + + if ((_vm->_gyro->_roomTime % 60) > 57) + direction--; // Blinks. + + if (direction != _vm->_gyro->_dna._dogFoodPos) { // Only if it's changed. + drawBackgroundSprite(-1, -1, direction); + _vm->_gyro->_dna._dogFoodPos = direction; // We use DogfoodPos here too - why not? + } + + switch (_vm->_gyro->_roomTime % 50) { + case 45 : + drawBackgroundSprite(-1, -1, 9); // Spurge blinks. + break; + case 49 : + drawBackgroundSprite(-1, -1, 10); + break; + } + break; + } + case r__ducks: { + if ((_vm->_gyro->_roomTime % 3) == 0) // The fire flickers. + drawBackgroundSprite(-1, -1, 1 + (_vm->_gyro->_roomTime / 3) % 3); + + // Bearing of Avvy from Duck. + byte direction = 0; + uint16 angle = _vm->_lucerna->bearing(2); + if ((angle <= 45) || ((angle >= 315) && (angle <= 360))) + direction = 4; // Middle. + else if ((angle >= 45) && (angle <= 180)) + direction = 6; // Left. + else if ((angle >= 181) && (angle <= 314)) + direction = 8; // Right. + + if ((_vm->_gyro->_roomTime % 45) > 42) + direction++; // Duck blinks. + + if (direction != _vm->_gyro->_dna._dogFoodPos) { // Only if it's changed. + drawBackgroundSprite(-1, -1, direction); + _vm->_gyro->_dna._dogFoodPos = direction; // We use DogfoodPos here too - why not? + } + break; + } + } + + if ((_vm->_gyro->_dna._bellsAreRinging) && (_vm->_gyro->setFlag('B'))) { + // They're ringing the bells. + switch (_vm->_gyro->_roomTime % 4) { + case 1: + if (_vm->_gyro->_dna._nextBell < 5) + _vm->_gyro->_dna._nextBell = 12; + _vm->_gyro->_dna._nextBell--; + _vm->_gyro->note(_vm->_gyro->kNotes[_vm->_gyro->_dna._nextBell]); + break; + case 2: + //nosound(); + updateBackgroundSprites(); + break; + } + } +} + +void Celer::loadBackgroundSprites(byte number) { + Common::File f; + _filename = _filename.format("chunk%d.avd", number); + if (!f.open(_filename)) { + warning("AVALANCHE: Celer: File not found: %s", _filename.c_str()); + return; + } + + f.seek(44); + _spriteNum = f.readByte(); + for (byte i = 0; i < _spriteNum; i++) + _offsets[i] = f.readSint32LE(); + + for (byte i = 0; i < _spriteNum; i++) { + f.seek(_offsets[i]); + + SpriteType sprite; + sprite._type = PictureType(f.readByte()); + sprite._x = f.readSint16LE(); + sprite._y = f.readSint16LE(); + sprite._xl = f.readSint16LE(); + sprite._yl = f.readSint16LE(); + sprite._size = f.readSint32LE(); + bool natural = f.readByte(); + bool memorize = f.readByte(); + + if (memorize) { + _sprites[i]._x = sprite._x; + _sprites[i]._xl = sprite._xl; + _sprites[i]._y = sprite._y; + _sprites[i]._yl = sprite._yl; + _sprites[i]._type = sprite._type; + + if (natural) { + _sprites[i]._type = kNaturalImage; // We simply read from the screen and later, in drawSprite() we draw it right back. + _sprites[i]._size = _sprites[i]._xl * 8 * _sprites[i]._yl + 1; + _sprites[i]._picture.create(_sprites[i]._xl * 8, _sprites[i]._yl + 1, ::Graphics::PixelFormat::createFormatCLUT8()); + + for (uint16 y = 0; y < _sprites[i]._yl + 1; y++) { + for (uint16 x = 0; x < _sprites[i]._xl * 8; x++) + *(byte *)_sprites[i]._picture.getBasePtr(x, y) = *(byte *)_vm->_graphics->_surface.getBasePtr(_sprites[i]._x * 8 + x, _sprites[i]._y + y); + } + } else { + _sprites[i]._size = sprite._size; + _sprites[i]._picture = _vm->_graphics->loadPictureRow(f, _sprites[i]._xl * 8, _sprites[i]._yl + 1); + } + } else + _sprites[i]._x = kOnDisk; + } + f.close(); +} + +void Celer::forgetBackgroundSprites() { + for (byte i = 0; i < _spriteNum; i++) { + if (_sprites[i]._x > kOnDisk) + _sprites[i]._picture.free(); + } +} + +void Celer::drawBackgroundSprite(int16 destX, int16 destY, byte which) { + which--; // For the difference between the Pascal and C array indexes. + //setactivepage(3); + warning("STUB: Celer::show_one()"); + + if (_sprites[which]._x > kOnDisk) { + if (destX < 0) { + destX = _sprites[which]._x * 8; + destY = _sprites[which]._y; + } + drawSprite(destX, destY, _sprites[which]); + } else { + Common::File f; + if (!f.open(_filename)) { // Filename was set in loadBackgroundSprites(). + warning("AVALANCHE: Celer: File not found: %s", _filename.c_str()); + return; + } + + f.seek(_offsets[which]); + + SpriteType sprite; + sprite._type = PictureType(f.readByte()); + sprite._x = f.readSint16LE(); + sprite._y = f.readSint16LE(); + sprite._xl = f.readSint16LE(); + sprite._yl = f.readSint16LE(); + sprite._size = f.readSint32LE(); + f.skip(2); // For the now not existing natural and memorize data members of the SpriteType (called chunkblocktype in the original). + sprite._picture = _vm->_graphics->loadPictureRow(f, sprite._xl * 8, sprite._yl + 1); + + if (destX < 0) { + destX = sprite._x * 8; + destY = sprite._y; + } + drawSprite(destX, destY, sprite); + + sprite._picture.free(); + f.close(); + } + + //setactivepage(1 - cp); + warning("STUB: Celer::show_one()"); +} + + + +void Celer::drawSprite(int16 x, int16 y, SpriteType &sprite) { + _r._x1 = x; + _r._y1 = y; + _r._y2 = y + sprite._yl; + + switch (sprite._type) { + case kNaturalImage: // Allow fallthrough on purpose. + case kBgi: + _r._x2 = x + sprite._xl + 1; + break; + case kEga: + _r._x2 = x + sprite._xl; + break; + } + + // These pictures are practically parts of the background. -10 is for the drop-down menu. + _vm->_graphics->drawPicture(_vm->_graphics->_background, sprite._picture, x, y - 10); +} + +} // End of namespace Avalanche. diff --git a/engines/avalanche/celer2.h b/engines/avalanche/celer2.h new file mode 100644 index 0000000000..47a11bfad4 --- /dev/null +++ b/engines/avalanche/celer2.h @@ -0,0 +1,81 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on the original source code of Lord Avalot d'Argent version 1.3. + * Copyright (c) 1994-1995 Mike, Mark and Thomas Thurman. + */ + +/* CELER The unit for updating the screen pics. */ + +#ifndef AVALANCHE_CELER2_H +#define AVALANCHE_CELER2_H + +#include "common/scummsys.h" +#include "common/file.h" +#include "common/str.h" + +#include "graphics/surface.h" + +namespace Avalanche { +class AvalancheEngine; + +class Celer { +public: + Celer(AvalancheEngine *vm); + ~Celer(); + + void updateBackgroundSprites(); + void loadBackgroundSprites(byte number); + void forgetBackgroundSprites(); + + // Setting the destination to negative coordinates means the picture should be drawn to it's original position. + // If you give it positive values, the picture will be plotted to the desired coordinates on the screen. + // By that we get rid of show_one_at(), which would be almost identical and cause a lot of code duplication. + void drawBackgroundSprite(int16 destX, int16 destY, byte which); + +private: + enum PictureType {kEga, kBgi, kNaturalImage}; + + struct SpriteType { + PictureType _type; + int16 _x, _y; + int16 _xl, _yl; + int32 _size; + ::Graphics::Surface _picture; + }; + + AvalancheEngine *_vm; + + int32 _offsets[40]; + byte _spriteNum; + SpriteType _sprites[40]; + ByteField _r; + Common::String _filename; + static const int16 kOnDisk; // Value of memos[fv].x when it's not in memory. + + void drawSprite(int16 x, int16 y, SpriteType &sprite); +}; + +} // End of namespace Avalanche. + +#endif // AVALANCHE_CELER2_H diff --git a/engines/avalanche/closing2.cpp b/engines/avalanche/closing2.cpp new file mode 100644 index 0000000000..c9bfcd0cd4 --- /dev/null +++ b/engines/avalanche/closing2.cpp @@ -0,0 +1,86 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on the original source code of Lord Avalot d'Argent version 1.3. + * Copyright (c) 1994-1995 Mike, Mark and Thomas Thurman. + */ + +/* CLOSING The closing screen and error handler. */ + +#include "avalanche/avalanche.h" + +#include "avalanche/closing2.h" +#include "avalanche/gyro2.h" +#include "avalanche/lucerna2.h" + +#include "common/textconsole.h" +#include "common/random.h" + + + +namespace Avalanche { + +Closing::Closing(AvalancheEngine *vm) { + _vm = vm; + warning("STUB: Closing::Closing()"); +} + +void Closing::getScreen(ScreenType which) { + warning("STUB: Closing::getScreen()"); +} + +void Closing::showScreen() { + warning("STUB: Closing::showScreen()"); +} + +void Closing::putIn(Common::String str, uint16 where) { + warning("STUB: Closing::putIn()"); +} + +void Closing::exitGame() { + const Common::String nouns[12] = { + "sackbut", "harpsichord", "camel", "conscience", "ice-cream", "serf", + "abacus", "castle", "carrots", "megaphone", "manticore", "drawbridge" + }; + + const Common::String verbs[12] = { + "haunt", "daunt", "tickle", "gobble", "erase", "provoke", "surprise", + "ignore", "stare at", "shriek at", "frighten", "quieten" + }; + + Common::String result; + + //nosound(); + warning("STUB: Closing::exitGame()"); + + getScreen(kScreenNagScreen); + result = nouns[_vm->_rnd->getRandomNumber(12)] + " will " + verbs[_vm->_rnd->getRandomNumber(12)] + " you"; + putIn(result, 1628); + showScreen(); // No halt- it's already set up. +} + +void Closing::handleBug() { + warning("STUB: Closing::handleBug()"); +} + +} // End of namespace Avalanche. diff --git a/engines/avalanche/closing2.h b/engines/avalanche/closing2.h new file mode 100644 index 0000000000..e1bf8b2cab --- /dev/null +++ b/engines/avalanche/closing2.h @@ -0,0 +1,71 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on the original source code of Lord Avalot d'Argent version 1.3. + * Copyright (c) 1994-1995 Mike, Mark and Thomas Thurman. + */ + +/* CLOSING The closing screen and error handler. */ + +#ifndef AVALANCHE_CLOSING2_H +#define AVALANCHE_CLOSING2_H + +#include "common/scummsys.h" +#include "common/str.h" + + + +namespace Avalanche { +class AvalancheEngine; + +class Closing { +public: + Closing(AvalancheEngine *vm); + + void exitGame(); + +private: + enum ScreenType { + kScreenBugAlert = 1, + kScreenRamCram = 2, + kScreenNagScreen = 3, + kScreenTwoCopies = 5 + }; + + AvalancheEngine *_vm; + + Common::String q; //absolute $B8FA:0* ; Nobody's using the graphics memory now. + Common::File f; + + void getScreen(ScreenType which); + + void showScreen(); + + void putIn(Common::String str, uint16 where); + + void handleBug(); +}; + +} // End of namespace Avalanche. + +#endif // AVALANCHE_CLOSING2_H diff --git a/engines/avalanche/color.h b/engines/avalanche/color.h new file mode 100644 index 0000000000..e9f9c460bc --- /dev/null +++ b/engines/avalanche/color.h @@ -0,0 +1,58 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on the original source code of Lord Avalot d'Argent version 1.3. + * Copyright (c) 1994-1995 Mike, Mark and Thomas Thurman. + */ + +// Color constants replacing the colors from the CRT unit of Pascal + +#ifndef AVALANCHE_COLOR_H +#define AVALANCHE_COLOR_H + +#include "common/system.h" + +namespace Avalanche { + +enum Color { + kColorBlack, + kColorBlue, + kColorGreen, + kColorCyan, + kColorRed, + kColorMagenta, + kColorBrown, + kColorLightgray, + kColorDarkgray, + kColorLightblue, + kColorLightgreen, + kColorLightcyan, + kColorLightred, + kColorLightmagenta, + kColorYellow, + kColorWhite +}; + +} // End of namespace Avalanche + +#endif // AVALANCHE_COLOR_H diff --git a/engines/avalanche/console.cpp b/engines/avalanche/console.cpp new file mode 100644 index 0000000000..03f198fbf5 --- /dev/null +++ b/engines/avalanche/console.cpp @@ -0,0 +1,39 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on the original source code of Lord Avalot d'Argent version 1.3. + * Copyright (c) 1994-1995 Mike, Mark and Thomas Thurman. + */ + +#include "avalanche/console.h" +#include "avalanche/avalanche.h" + +namespace Avalanche { + + AvalancheConsole::AvalancheConsole(AvalancheEngine *vm) : GUI::Debugger(), _vm(vm) { + } + + AvalancheConsole::~AvalancheConsole() { + } + +} // End of namespace Avalanche diff --git a/engines/avalanche/console.h b/engines/avalanche/console.h new file mode 100644 index 0000000000..138c3db05c --- /dev/null +++ b/engines/avalanche/console.h @@ -0,0 +1,48 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on the original source code of Lord Avalot d'Argent version 1.3. + * Copyright (c) 1994-1995 Mike, Mark and Thomas Thurman. + */ + +#ifndef AVALANCHE_CONSOLE_H +#define AVALANCHE_CONSOLE_H + +#include "gui/debugger.h" + +namespace Avalanche { + + class AvalancheEngine; + + class AvalancheConsole : public GUI::Debugger { + public: + AvalancheConsole(AvalancheEngine *vm); + virtual ~AvalancheConsole(void); + + private: + AvalancheEngine *_vm; + }; + +} // End of namespace Avalanche + +#endif // AVALANCHE_CONSOLE_H diff --git a/engines/avalanche/detection.cpp b/engines/avalanche/detection.cpp new file mode 100644 index 0000000000..97b823eb1d --- /dev/null +++ b/engines/avalanche/detection.cpp @@ -0,0 +1,220 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on the original source code of Lord Avalot d'Argent version 1.3. + * Copyright (c) 1994-1995 Mike, Mark and Thomas Thurman. + */ + +#include "engines/advancedDetector.h" +#include "common/system.h" +#include "common/savefile.h" +#include "graphics/thumbnail.h" + +#include "avalanche/avalanche.h" + +namespace Avalanche { + +struct AvalancheGameDescription { + ADGameDescription desc; +}; + +uint32 AvalancheEngine::getFeatures() const { + return _gameDescription->desc.flags; +} + +const char *AvalancheEngine::getGameId() const { + return _gameDescription->desc.gameid; +} + +static const PlainGameDescriptor avalancheGames[] = { + {"avalanche", "Lord Avalot d'Argent"}, + {0, 0} +}; + +static const ADGameDescription gameDescriptions[] = { + { + "avalanche", 0, + { + {"avalot.sez", 0, "de10eb353228013da3d3297784f81ff9", 48763}, + {"mainmenu.avd", 0, "89f31211af579a872045b175cc264298", 18880}, + AD_LISTEND + }, + Common::EN_ANY, + Common::kPlatformDOS, + ADGF_NO_FLAGS, + GUIO0() + }, + + AD_TABLE_END_MARKER +}; + +class AvalancheMetaEngine : public AdvancedMetaEngine { +public: + AvalancheMetaEngine() : AdvancedMetaEngine(gameDescriptions, sizeof(AvalancheGameDescription), avalancheGames) { + } + + const char *getName() const { + return "Avalanche"; + } + + const char *getOriginalCopyright() const { + return "Avalanche Engine Copyright (c) 1994-1995 Mike, Mark and Thomas Thurman."; + } + + bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *gd) const; + bool hasFeature(MetaEngineFeature f) const; + + int getMaximumSaveSlot() const { return 99; } + SaveStateList listSaves(const char *target) const; + void removeSaveState(const char *target, int slot) const; + SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const; +}; + +bool AvalancheMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *gd) const { + if (gd) + *engine = new AvalancheEngine(syst, (const AvalancheGameDescription *)gd); + return gd != 0; +} + +bool AvalancheMetaEngine::hasFeature(MetaEngineFeature f) const { + return + (f == kSupportsListSaves) || + (f == kSupportsDeleteSave) || + (f == kSupportsLoadingDuringStartup) || + (f == kSavesSupportMetaInfo) || + (f == kSavesSupportThumbnail); +} + +SaveStateList AvalancheMetaEngine::listSaves(const char *target) const { + Common::SaveFileManager *saveFileMan = g_system->getSavefileManager(); + Common::StringArray filenames; + Common::String pattern = target; + pattern.toUppercase(); + pattern += "-??.SAV"; + + filenames = saveFileMan->listSavefiles(pattern); + sort(filenames.begin(), filenames.end()); // Sort (hopefully ensuring we are sorted numerically..) + + SaveStateList saveList; + char slot[3]; + for (Common::StringArray::const_iterator filename = filenames.begin(); filename != filenames.end(); ++filename) { + slot[0] = filename->c_str()[filename->size() - 6]; + slot[1] = filename->c_str()[filename->size() - 5]; + slot[2] = '\0'; + // Obtain the last 2 digits of the filename (without extension), since they correspond to the save slot + int slotNum = atoi(slot); + if (slotNum >= 0 && slotNum <= getMaximumSaveSlot()) { + Common::InSaveFile *file = saveFileMan->openForLoading(*filename); + if (file) { + // Check for our signature. + Common::String signature; + for (byte i = 0; i < 4; i++) + signature += file->readByte(); + if (signature != "AVAL") { + warning("Savegame of incompatible type!"); + delete file; + continue; + } + + // Check version. + byte saveVersion = file->readByte(); + if (saveVersion != kSavegameVersion) { + warning("Savegame of incompatible version!"); + delete file; + continue; + } + + // Read name. + uint32 nameSize = file->readUint32LE(); + if (nameSize >= 255) { + delete file; + continue; + } + char *name = new char[nameSize + 1]; + file->read(name, nameSize); + name[nameSize] = 0; + + saveList.push_back(SaveStateDescriptor(slotNum, name)); + delete[] name; + delete file; + } + } + } + + return saveList; +} + +void AvalancheMetaEngine::removeSaveState(const char *target, int slot) const { + Common::String fileName = Common::String::format("%s-%02d.SAV", target, slot); + g_system->getSavefileManager()->removeSavefile(fileName); +} + +SaveStateDescriptor AvalancheMetaEngine::querySaveMetaInfos(const char *target, int slot) const { + Common::String fileName = Common::String::format("%s-%02d.SAV", target, slot); + Common::InSaveFile *f = g_system->getSavefileManager()->openForLoading(fileName); + + if (f) { + // Check for our signature. + Common::String signature; + for (byte i = 0; i < 4; i++) + signature += f->readByte(); + if (signature != "AVAL") { + warning("Savegame of incompatible type!"); + delete f; + return SaveStateDescriptor(); + } + + // Check version. + byte saveVersion = f->readByte(); + if (saveVersion != kSavegameVersion) { + warning("Savegame of incompatible version!"); + delete f; + return SaveStateDescriptor(); + } + + // Read the description. + uint32 descSize = f->readUint32LE(); + Common::String description; + for (uint32 i = 0; i < descSize; i++) { + char actChar = f->readByte(); + description += actChar; + } + + SaveStateDescriptor desc(slot, description); + + ::Graphics::Surface *const thumbnail = ::Graphics::loadThumbnail(*f); + desc.setThumbnail(thumbnail); + + delete f; + return desc; + } + return SaveStateDescriptor(); +} + +} // End of namespace Avalanche + +#if PLUGIN_ENABLED_DYNAMIC(AVALANCHE) +REGISTER_PLUGIN_DYNAMIC(AVALANCHE, PLUGIN_TYPE_ENGINE, Avalanche::AvalancheMetaEngine); +#else +REGISTER_PLUGIN_STATIC(AVALANCHE, PLUGIN_TYPE_ENGINE, Avalanche::AvalancheMetaEngine); +#endif diff --git a/engines/avalanche/dropdown2.cpp b/engines/avalanche/dropdown2.cpp new file mode 100644 index 0000000000..12af639ab7 --- /dev/null +++ b/engines/avalanche/dropdown2.cpp @@ -0,0 +1,815 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on the original source code of Lord Avalot d'Argent version 1.3. + * Copyright (c) 1994-1995 Mike, Mark and Thomas Thurman. + */ + + /* DROPDOWN A customised version of Oopmenu (qv). */ + +#include "avalanche/avalanche.h" + +#include "avalanche/dropdown2.h" +#include "avalanche/lucerna2.h" +#include "avalanche/gyro2.h" +#include "avalanche/acci2.h" +#include "avalanche/animation.h" +#include "avalanche/enid2.h" + +#include "common/textconsole.h" + +namespace Avalanche { + +void HeadType::init(char trig, char altTrig, Common::String title, byte pos, DropdownFunc setupFunc, DropdownFunc chooseFunc, Dropdown *dr) { + _trigger = trig; + _altTrigger = altTrig; + _title = title; + _position = pos; + _xpos = _position * _dr->kSpacing + _dr->kIndent; + _xright = (_position + 1) * _dr->kSpacing + _dr->kIndent; + _setupFunc = setupFunc; + _chooseFunc = chooseFunc; + + _dr = dr; +} + +void HeadType::draw() { + CursorMan.showMouse(false); + _dr->drawMenuText(_xpos, 1, _trigger, _title, true, false); + CursorMan.showMouse(true); +} + +void HeadType::highlight() { + CursorMan.showMouse(false); + + //nosound(); + //setactivepage(cp); + warning("STUB: Dropdown::headytpe::highlight()"); + + _dr->drawMenuText(_xpos, 1, _trigger, _title, true, true); + + _dr->_activeMenuItem._left = _xpos; + _dr->_activeMenuItem._activeNow = true; + _dr->_vm->_gyro->_dropdownActive = true; + _dr->_activeMenuItem._activeNum = _position; + + _dr->_vm->_gyro->_currentMouse = 177; // Force redraw of cursor. +} + +bool HeadType::parseAltTrigger(char key) { + if (key != _altTrigger) + return true; + return false; +} + + + +void MenuItem::init(Dropdown *dr) { + _dr = dr; + _activeNow = false; + _dr->_vm->_gyro->_dropdownActive = false; + _activeNum = 1; +} + +void MenuItem::reset() { + _optionNum = 0; + _width = 0; + _firstlix = false; + _oldY = 0; + _highlightNum = 0; +} + +void MenuItem::setupOption(Common::String title, char trigger, Common::String shortcut, bool valid) { + uint16 width = (title + shortcut).size() + 3; + if (_width < width) + _width = width; + + _options[_optionNum]._title = title; + _options[_optionNum]._trigger = trigger; + _options[_optionNum]._shortcut = shortcut; + _options[_optionNum]._valid = valid; + _optionNum++; +} + +void MenuItem::displayOption(byte y, bool highlit) { + byte backgroundColor; + if (highlit) + backgroundColor = 0; + else + backgroundColor = 7; + _dr->_vm->_graphics->_surface.fillRect(Common::Rect((_flx1 + 1) * 8, 3 + (y + 1) * 10, (_flx2 + 1) * 8, 13 + (y + 1) * 10), backgroundColor); + + Common::String text = _options[y]._title; + while (text.size() + _options[y]._shortcut.size() < _width) + text += ' '; // Pad _options[y] with spaces. + text += _options[y]._shortcut; + + _dr->drawMenuText(_left, 4 + (y + 1) * 10, _options[y]._trigger, text, _options[y]._valid, highlit); +} + +void MenuItem::display() { + CursorMan.showMouse(false); + //setactivepage(cp); + //setvisualpage(cp); + //setfillstyle(1, menu_b); + //setcolor(menu_border); + _firstlix = true; + _flx1 = _left - 2; + _flx2 = _left + _width; + fly = 15 + _optionNum * 10; + _activeNow = true; + _dr->_vm->_gyro->_dropdownActive = true; + + _dr->_vm->_graphics->_surface.fillRect(Common::Rect((_flx1 + 1) * 8, 12, (_flx2 + 1) * 8, fly), _dr->kMenuBackgroundColor); + _dr->_vm->_graphics->_surface.frameRect(Common::Rect((_flx1 + 1) * 8 - 1, 11, (_flx2 + 1) * 8 + 1, fly + 1), _dr->kMenuBorderColor); + + displayOption(0, true); + for (byte y = 1; y < _optionNum; y++) + displayOption(y, false); + + _dr->_vm->_gyro->_defaultLed = 1; + _dr->_vm->_gyro->_currentMouse = 177; + //mousepage(cp); + CursorMan.showMouse(true); // 4 = fletch +} + +void MenuItem::wipe() { + //setactivepage(cp); + CursorMan.showMouse(false); + + _dr->drawMenuText(_dr->_menuBar._menuItems[_dr->_activeMenuItem._activeNum]._xpos, 1, _dr->_menuBar._menuItems[_dr->_activeMenuItem._activeNum]._trigger, _dr->_menuBar._menuItems[_dr->_activeMenuItem._activeNum]._title, true, false); + + _activeNow = false; + _dr->_vm->_gyro->_dropdownActive = false; + _firstlix = false; + _dr->_vm->_gyro->_defaultLed = 2; + + CursorMan.showMouse(true); +} + +void MenuItem::moveHighlight(int8 inc) { + if (inc != 0) { + int8 highlightNum = _highlightNum + inc; + if ((highlightNum < 0) || (highlightNum >= _optionNum)) + return; + _highlightNum = highlightNum; + } + //setactivepage(cp); + CursorMan.showMouse(false); + displayOption(_oldY, false); + displayOption(_highlightNum, true); + //setactivepage(1 - cp); + _oldY = _highlightNum; + CursorMan.showMouse(true); +} + +void MenuItem::lightUp(Common::Point cursorPos) { + if ((cursorPos.x < _flx1 * 8) || (cursorPos.x > _flx2 * 8) || (cursorPos.y <= 25) || (cursorPos.y > ((fly - 3) * 2 + 1))) + return; + _highlightNum = (cursorPos.y - 26) / 20; + if (_highlightNum == _oldY) + return; + moveHighlight(0); +} + +void MenuItem::select(byte which) { + if (!_options[which]._valid) + return; + + _choiceNum = which; + wipe(); + + if (_choiceNum == _optionNum) + _choiceNum--; // Off the bottom. + if (_choiceNum > _optionNum) + _choiceNum = 0; // Off the top, I suppose. + + (_dr->*_dr->_menuBar._menuItems[_activeNum]._chooseFunc)(); +} + +void MenuItem::parseKey(char c) { + c = toupper(c); + bool found = false; + for (byte i = 0; i < _optionNum; i++) { + if ((toupper(_options[i]._trigger) == c) && _options[i]._valid) { + select(i); + found = true; + } + } + if (!found) + _dr->_vm->_gyro->blip(); +} + +void MenuBar::init(Dropdown *dr) { + _dr = dr; + _menuNum = 0; +} + +void MenuBar::createMenuItem(char trig, Common::String title, char altTrig, DropdownFunc setupFunc, DropdownFunc chooseFunc) { + _menuItems[_menuNum].init(trig, altTrig, title, _menuNum, setupFunc, chooseFunc, _dr); + _menuNum++; +} + +void MenuBar::draw() { + //setactivepage(3); + + _dr->_vm->_graphics->_surface.fillRect(Common::Rect(0, 0, 640, 10), _dr->kMenuBackgroundColor); + + byte savecp = _dr->_vm->_gyro->_cp; + _dr->_vm->_gyro->_cp = 3; + + for (byte i = 0; i < _menuNum; i++) + _menuItems[i].draw(); + + _dr->_vm->_gyro->_cp = savecp; +} + +void MenuBar::parseAltTrigger(char c) { + byte i = 0; + while ((i < _menuNum) && (_menuItems[i].parseAltTrigger(c))) + i++; + if (i == _menuNum) + return; + setupMenuItem(i); +} + +void MenuBar::setupMenuItem(byte which) { + if (_dr->_activeMenuItem._activeNow) { + _dr->_activeMenuItem.wipe(); // Get rid of menu. + if (_dr->_activeMenuItem._activeNum == _menuItems[which]._position) + return; // Clicked on own highlight. + } + _menuItems[which].highlight(); + (_dr->*_menuItems[which]._setupFunc)(); +} + +void MenuBar::chooseMenuItem(int16 x) { + byte i = 0; + do { + if ((x > _menuItems[i]._xpos * 8) && (x < _menuItems[i]._xright * 8)) { + setupMenuItem(i); + return; + } + i++; + } while (i < _menuNum); +} + +Dropdown::Dropdown(AvalancheEngine *vm) { + _vm = vm; + _activeMenuItem.init(this); + _menuBar.init(this); +} + +void Dropdown::findWhatYouCanDoWithIt() { + switch (_vm->_gyro->_thinks) { + case Gyro::kObjectWine: + case Gyro::kObjectPotion: + case Gyro::kObjectInk: + _vm->_gyro->_verbStr = Common::String(_vm->_acci->kVerbCodeExam) + _vm->_acci->kVerbCodeDrink; + break; + case Gyro::kObjectBell: + _vm->_gyro->_verbStr = Common::String(_vm->_acci->kVerbCodeExam) + _vm->_acci->kVerbCodeRing; + break; + case Gyro::kObjectChastity: + _vm->_gyro->_verbStr = Common::String(_vm->_acci->kVerbCodeExam) + _vm->_acci->kVerbCodeWear; + break; + case Gyro::kObjectLute: + _vm->_gyro->_verbStr = Common::String(_vm->_acci->kVerbCodeExam) + _vm->_acci->kVerbCodePlay; + break; + case Gyro::kObjectMushroom: + case Gyro::kObjectOnion: + _vm->_gyro->_verbStr = Common::String(_vm->_acci->kVerbCodeExam) + _vm->_acci->kVerbCodeEat; + break; + case Gyro::kObjectClothes: + _vm->_gyro->_verbStr = Common::String(_vm->_acci->kVerbCodeExam) + _vm->_acci->kVerbCodeWear; + break; + default: + _vm->_gyro->_verbStr = _vm->_acci->kVerbCodeExam; // Anything else. + } +} + +void Dropdown::drawMenuText(int16 x, int16 y, char trigger, Common::String text, bool valid, bool highlighted) { + byte fontColor; + byte backgroundColor; + if (highlighted) { + fontColor = kColorWhite; + backgroundColor = kColorBlack; + } else { + fontColor = kColorBlack; + backgroundColor = kColorLightgray; + } + + byte ander; + if (valid) + ander = 255; + else + ander = 170; + + FontType font; + for (byte i = 0; i < text.size(); i++) { + for (byte j = 0; j < 8; j++) { + byte idx = text[i]; + font[idx][j] = _vm->_gyro->_font[idx][j] & ander; // Set the font. + // And set the background of the text to the desired color. + for (byte k = 0; k < 8; k++) + *(byte *)_vm->_graphics->_surface.getBasePtr(x * 8 + i * 8 + k, y + j) = backgroundColor; + } + } + + _vm->_graphics->drawText(_vm->_graphics->_surface, text, font, 8, x * 8, y, fontColor); + + // Underline the selected character. + if (!text.contains(trigger)) + return; + else { + byte i; + for (i = 0; text[i] != trigger; i++) + ; // Search for the character in the string. + + byte pixel = ander; + for (byte bit = 0; bit < 8; bit++) { + byte pixelBit = (pixel >> bit) & 1; + if (pixelBit) + *(byte *)_vm->_graphics->_surface.getBasePtr(x * 8 + i * 8 + 7 - bit, y + 8) = fontColor; + } + } + + _vm->_graphics->refreshScreen(); +} + +void Dropdown::bleep() { + warning("STUB: Dropdown::bleep()"); +} + +void Dropdown::parseKey(char r, char re) { + //switch (r) { + //case 0: + //case 224: { + // switch (re) { + // case 'K': + // if (_activeMenuItem._activeNum > 1) { + // _activeMenuItem.wipe(); + // _menuBar.setupMenuItem(_activeMenuItem._activeNum - 1); + // } else { + // // Get menu on the left-hand side. + // _activeMenuItem.wipe(); + // _menuBar.chooseMenuItem((_menuBar._menuNum - 1) * kSpacing + kIndent); + // } + // break; + // case 'M': + // if (_activeMenuItem._activeNum < _menuBar._menuNum) { + // _activeMenuItem.wipe(); + // _menuBar.setupMenuItem(_activeMenuItem._activeNum + 1); + // } else { + // // Get menu on the far right-hand side. + // _activeMenuItem.wipe(); + // _menuBar.chooseMenuItem(kIndent); + // } + // break; + // case 'H': + // _activeMenuItem.moveHighlight(-1); + // break; + // case 'P': + // _activeMenuItem.moveHighlight(1); + // break; + // default: + // _menuBar.parseAltTrigger(re); + // } + //} + //break; + //case 13: + // _activeMenuItem.select(_activeMenuItem._highlightNum); + // break; + //default: { + // if (_activeMenuItem._activeNow) + // _activeMenuItem.parseKey(r); + // } + //} + warning("STUB: Dropdown::parseKey()"); // To be implemented properly later! Don't remove the comment above! +} + +Common::String Dropdown::selectGender(byte x) { + if (x < 175) + return "im"; + else + return "er"; +} + +void Dropdown::setupMenuGame() { + _activeMenuItem.reset(); + _activeMenuItem.setupOption("Help...", 'H', "f1", true); + _activeMenuItem.setupOption("Boss Key", 'B', "alt-B", true); + _activeMenuItem.setupOption("Untrash screen", 'U', "ctrl-f7", true); + _activeMenuItem.setupOption("Score and rank", 'S', "f9", true); + _activeMenuItem.setupOption("About Avvy...", 'A', "shift-f10", true); + _activeMenuItem.display(); +} + +void Dropdown::setupMenuFile() { + _activeMenuItem.reset(); + _activeMenuItem.setupOption("New game", 'N', "f4", true); + _activeMenuItem.setupOption("Load...", 'L', "^f3", true); + _activeMenuItem.setupOption("Save", 'S', "^f2", _vm->_gyro->_alive); + _activeMenuItem.setupOption("Save As...", 'v', "", _vm->_gyro->_alive); + _activeMenuItem.setupOption("DOS Shell", 'D', _vm->_gyro->_atKey + '1', true); + _activeMenuItem.setupOption("Quit", 'Q', "alt-X", true); + _activeMenuItem.display(); +} + +void Dropdown::setupMenuAction() { + _activeMenuItem.reset(); + + Common::String f5Does = _vm->_gyro->f5Does(); + for (byte i = 0; i < 2; i++) + if (!f5Does.empty()) + f5Does.deleteChar(0); + if (f5Does.empty()) + _activeMenuItem.setupOption("Do something", 'D', "f5", false); + else + _activeMenuItem.setupOption(f5Does, f5Does[0], "f5", true); + _activeMenuItem.setupOption("Pause game", 'P', "f6", true); + if (_vm->_gyro->_dna._room == 99) + _activeMenuItem.setupOption("Journey thither", 'J', "f7", _vm->_animation->nearDoor()); + else + _activeMenuItem.setupOption("Open the door", 'O', "f7", _vm->_animation->nearDoor()); + _activeMenuItem.setupOption("Look around", 'L', "f8", true); + _activeMenuItem.setupOption("Inventory", 'I', "Tab", true); + if (_vm->_animation->_sprites[0]._speedX == _vm->_gyro->kWalk) + _activeMenuItem.setupOption("Run fast", 'R', "^R", true); + else + _activeMenuItem.setupOption("Walk slowly", 'W', "^W", true); + + _activeMenuItem.display(); +} + +void Dropdown::setupMenuPeople() { + if (!people.empty()) + people.clear(); + + _activeMenuItem.reset(); + + for (byte i = 150; i <= 178; i++) + if (_vm->_gyro->_whereIs[i - 150] == _vm->_gyro->_dna._room) { + _activeMenuItem.setupOption(_vm->_gyro->getName(i), _vm->_gyro->getNameChar(i), "", true); + people = people + i; + } + + _activeMenuItem.display(); +} + +void Dropdown::setupMenuObjects() { + _activeMenuItem.reset(); + for (byte i = 0; i < kObjectNum; i++) { + if (_vm->_gyro->_dna._objects[i]) + _activeMenuItem.setupOption(_vm->_gyro->getThing(i + 1), _vm->_gyro->getThingChar(i + 1), "", true); + } + _activeMenuItem.display(); +} + +void Dropdown::setupMenuWith() { + _activeMenuItem.reset(); + + if (_vm->_gyro->_thinkThing) { + findWhatYouCanDoWithIt(); + + for (byte i = 0; i < _vm->_gyro->_verbStr.size(); i++) { + char vbchar; + Common::String verb; + + _vm->_acci->verbOpt(_vm->_gyro->_verbStr[i], verb, vbchar); + _activeMenuItem.setupOption(verb, vbchar, "", true); + } + + // We disable the "give" option if: (a), you haven't selected anybody, (b), the _person you've selected isn't in the room, + // or (c), the _person you've selected is YOU! + + if ((_vm->_gyro->_lastPerson == _vm->_gyro->kPeopleAvalot) || (_vm->_gyro->_lastPerson == _vm->_acci->kNothing) + || (_vm->_gyro->_whereIs[_vm->_gyro->_lastPerson - 150] != _vm->_gyro->_dna._room)) + _activeMenuItem.setupOption("Give to...", 'G', "", false); // Not here. + else { + _activeMenuItem.setupOption(Common::String("Give to ") + _vm->_gyro->getName(_vm->_gyro->_lastPerson), 'G', "", true); + _vm->_gyro->_verbStr = _vm->_gyro->_verbStr + _vm->_acci->kVerbCodeGive; + } + } else { + _activeMenuItem.setupOption("Examine", 'x', "", true); + _activeMenuItem.setupOption(Common::String("Talk to h") + selectGender(_vm->_gyro->_thinks), 'T', "", true); + _vm->_gyro->_verbStr = Common::String(_vm->_acci->kVerbCodeExam) + _vm->_acci->kVerbCodeTalk; + switch (_vm->_gyro->_thinks) { + case Gyro::kPeopleGeida: + case Gyro::kPeopleArkata: { + _activeMenuItem.setupOption("Kiss her", 'K', "", true); + _vm->_gyro->_verbStr = _vm->_gyro->_verbStr + _vm->_acci->kVerbCodeKiss; + } + break; + case Gyro::kPeopleDogfood: { + _activeMenuItem.setupOption("Play his game", 'P', "", !_vm->_gyro->_dna._wonNim); // True if you HAVEN'T won. + _vm->_gyro->_verbStr = _vm->_gyro->_verbStr + _vm->_acci->kVerbCodePlay; + } + break; + case Gyro::kPeopleMalagauche: { + bool isSober = !_vm->_gyro->_dna._teetotal; + _activeMenuItem.setupOption("Buy some wine", 'w', "", !_vm->_gyro->_dna._objects[_vm->_gyro->kObjectWine - 1]); + _activeMenuItem.setupOption("Buy some beer", 'b', "", isSober); + _activeMenuItem.setupOption("Buy some whisky", 'h', "", isSober); + _activeMenuItem.setupOption("Buy some cider", 'c', "", isSober); + _activeMenuItem.setupOption("Buy some mead", 'm', "", isSober); + _vm->_gyro->_verbStr = _vm->_gyro->_verbStr + 101 + 100 + 102 + 103 + 104; + } + break; + case Gyro::kPeopleTrader: { + _activeMenuItem.setupOption("Buy an onion", 'o', "", !_vm->_gyro->_dna._objects[_vm->_gyro->kObjectOnion - 1]); + _vm->_gyro->_verbStr = _vm->_gyro->_verbStr + 105; + } + break; + } + } + _activeMenuItem.display(); +} + +void Dropdown::runMenuGame() { + // Help, boss, untrash screen. + switch (_activeMenuItem._choiceNum) { + case 0: + _vm->_lucerna->callVerb(_vm->_acci->kVerbCodeHelp); + break; + case 1: + _vm->_lucerna->callVerb(_vm->_acci->kVerbCodeBoss); + break; + case 2: + _vm->_lucerna->majorRedraw(); + break; + case 3: + _vm->_lucerna->callVerb(_vm->_acci->kVerbCodeScore); + break; + case 4: + _vm->_lucerna->callVerb(_vm->_acci->kVerbCodeInfo); + break; + } +} + +void Dropdown::runMenuFile() { + // New game, load, save, save as, DOS shell, about, quit. + switch (_activeMenuItem._choiceNum) { + case 0: + _vm->_lucerna->callVerb(_vm->_acci->kVerbCodeRestart); + break; + case 1: { + if (!_vm->_acci->_realWords[1].empty()) + _vm->_acci->_realWords[1].clear(); + _vm->_lucerna->callVerb(_vm->_acci->kVerbCodeLoad); + } + break; + case 2: { + if (!_vm->_acci->_realWords[1].empty()) + _vm->_acci->_realWords[1].clear(); + _vm->_lucerna->callVerb(_vm->_acci->kVerbCodeSave); + } + break; + case 3: + //_vm->_basher->filename_edit(); + warning("STUB: Dropdown::runMenuFile()"); + break; + case 4: + _vm->_enid->backToBootstrap(2); + break; + case 5: + _vm->_lucerna->callVerb(_vm->_acci->kVerbCodeQuit); + break; + } +} + +void Dropdown::runMenuAction() { + Common::String f5Does; + // Get up, pause game, open door, look, inventory, walk/run. + switch (_activeMenuItem._choiceNum) { + case 0: { + _vm->_acci->_person = _vm->_acci->kPardon; + _vm->_acci->_thing = _vm->_acci->kPardon; + f5Does = _vm->_gyro->f5Does(); + _vm->_lucerna->callVerb(f5Does[0]); + } + break; + case 1: + _vm->_acci->_thing = _vm->_acci->kPardon; + _vm->_lucerna->callVerb(_vm->_acci->kVerbCodePause); + break; + case 2: + _vm->_lucerna->callVerb(_vm->_acci->kVerbCodeOpen); + break; + case 3: + _vm->_acci->_thing = _vm->_acci->kPardon; + _vm->_lucerna->callVerb(_vm->_acci->kVerbCodeLook); + break; + case 4: + _vm->_lucerna->callVerb(_vm->_acci->kVerbCodeInv); + break; + case 5: { + if (_vm->_animation->_sprites[0]._speedX == _vm->_gyro->kWalk) + _vm->_animation->_sprites[0]._speedX = _vm->_gyro->kRun; + else + _vm->_animation->_sprites[0]._speedX = _vm->_gyro->kWalk; + _vm->_animation->updateSpeed(); + } + break; + } +} + +void Dropdown::runMenuObjects() { + _vm->_lucerna->thinkAbout(_vm->_gyro->_objectList[_activeMenuItem._choiceNum + 1], Gyro::kThing); +} + +void Dropdown::runMenuPeople() { + _vm->_lucerna->thinkAbout(people[_activeMenuItem._choiceNum], Gyro::kPerson); + _vm->_gyro->_lastPerson = people[_activeMenuItem._choiceNum]; +} + +void Dropdown::runMenuWith() { + _vm->_acci->_thing = _vm->_gyro->_thinks; + + if (_vm->_gyro->_thinkThing) { + + _vm->_acci->_thing += 49; + + if (_vm->_gyro->_verbStr[_activeMenuItem._choiceNum] == _vm->_acci->kVerbCodeGive) + _vm->_acci->_person = _vm->_gyro->_lastPerson; + else + _vm->_acci->_person = 254; + + } else { + switch (_vm->_gyro->_verbStr[_activeMenuItem._choiceNum]) { + case 100: { // Beer + _vm->_acci->_thing = 100; + _vm->_lucerna->callVerb(_vm->_acci->kVerbCodeBuy); + return; + } + break; + case 101: { // Wine + _vm->_acci->_thing = 50; + _vm->_lucerna->callVerb(_vm->_acci->kVerbCodeBuy); + return; + } + break; + case 102: { // Whisky + _vm->_acci->_thing = 102; + _vm->_lucerna->callVerb(_vm->_acci->kVerbCodeBuy); + return; + } + break; + case 103: { // Cider + _vm->_acci->_thing = 103; + _vm->_lucerna->callVerb(_vm->_acci->kVerbCodeBuy); + return; + } + break; + case 104: { // Mead + _vm->_acci->_thing = 107; + _vm->_lucerna->callVerb(_vm->_acci->kVerbCodeBuy); + return; + } + break; + case 105: { // Onion (trader) + _vm->_acci->_thing = 67; + _vm->_lucerna->callVerb(_vm->_acci->kVerbCodeBuy); + return; + } + break; + default: { + _vm->_acci->_person = _vm->_acci->_thing; + _vm->_acci->_thing = 254; + _vm->_gyro->_subjectNum = 0; + } + } + } + _vm->_lucerna->callVerb(_vm->_gyro->_verbStr[_activeMenuItem._choiceNum]); +} + +void Dropdown::setupMenu() { + _menuBar.init(this); + _activeMenuItem.init(this); + + _menuBar.createMenuItem('F', "File", '!', &Avalanche::Dropdown::setupMenuFile, &Avalanche::Dropdown::runMenuFile); + _menuBar.createMenuItem('G', "Game", 34, &Avalanche::Dropdown::setupMenuGame, &Avalanche::Dropdown::runMenuGame); + _menuBar.createMenuItem('A', "Action", 30, &Avalanche::Dropdown::setupMenuAction, &Avalanche::Dropdown::runMenuAction); + _menuBar.createMenuItem('O', "Objects", 24, &Avalanche::Dropdown::setupMenuObjects, &Avalanche::Dropdown::runMenuObjects); + _menuBar.createMenuItem('P', "People", 25, &Avalanche::Dropdown::setupMenuPeople, &Avalanche::Dropdown::runMenuPeople); + _menuBar.createMenuItem('W', "With", 17, &Avalanche::Dropdown::setupMenuWith, &Avalanche::Dropdown::runMenuWith); + + _menuBar.draw(); +} + +void Dropdown::updateMenu() { // TODO: Optimize it ASAP!!! It really needs it... + Common::Point cursorPos = _vm->getMousePos(); + ::Graphics::Surface backup; + backup.copyFrom(_vm->_graphics->_surface); + + while (!_activeMenuItem._activeNow && (cursorPos.y <= 21) && _vm->_lucerna->_holdLeftMouse) { + _menuBar.chooseMenuItem(cursorPos.x); + do + _vm->updateEvents(); + while (_vm->_lucerna->_holdLeftMouse); + + + while (!_vm->shouldQuit()) { + do { + _vm->updateEvents(); + + // We updadte the cursor's picture. + cursorPos = _vm->getMousePos(); + // Change arrow... + if ((0 <= cursorPos.y) && (cursorPos.y <= 21)) + _vm->_gyro->newMouse(1); // Up arrow + else if ((22 <= cursorPos.y) && (cursorPos.y <= 339)) { + if ((cursorPos.x >= _activeMenuItem._flx1 * 8) && (cursorPos.x <= _activeMenuItem._flx2 * 8) && (cursorPos.y > 21) && (cursorPos.y <= _activeMenuItem.fly * 2 + 1)) + _vm->_gyro->newMouse(3); // Right-arrow + else + _vm->_gyro->newMouse(4); // Fletch + } else if ((340 <= cursorPos.y) && (cursorPos.y <= 399)) + _vm->_gyro->newMouse(2); // Screwdriver + + _activeMenuItem.lightUp(cursorPos); + + _vm->_graphics->refreshScreen(); + } while (!_vm->_lucerna->_holdLeftMouse); + + if (_vm->_lucerna->_holdLeftMouse) { + if (cursorPos.y > 21) { + if (!((_activeMenuItem._firstlix) && ((cursorPos.x >= _activeMenuItem._flx1 * 8) && (cursorPos.x <= _activeMenuItem._flx2 * 8) + && (cursorPos.y >= 24) && (cursorPos.y <= (_activeMenuItem.fly * 2 + 1))))) { + // Clicked OUTSIDE the menu. + if (_activeMenuItem._activeNow) { + _activeMenuItem.wipe(); + _vm->_lucerna->_holdLeftMouse = false; + backup.free(); + return; + } // No "else"- clicking on menu has no effect (only releasing). + } + } else { + // Clicked on menu bar. + if (_activeMenuItem._activeNow) { + _activeMenuItem.wipe(); + _vm->_graphics->_surface.copyFrom(backup); + _vm->_graphics->refreshScreen(); + + if (((_activeMenuItem._left * 8) <= cursorPos.x) && (cursorPos.x <= (_activeMenuItem._left * 8 + 80))) { // 80: the width of one menu item on the bar in pixels. + // If we clicked on the same menu item (the one that is already active) on the bar... + _vm->_lucerna->_holdLeftMouse = false; + backup.free(); + return; + } else { + _vm->_lucerna->_holdLeftMouse = true; + break; + } + } + } + + // NOT clicked button... + if ((_activeMenuItem._firstlix) && ((cursorPos.x >= _activeMenuItem._flx1 * 8) && (cursorPos.x <= _activeMenuItem._flx2 * 8) + && (cursorPos.y >= 12) && (cursorPos.y <= (_activeMenuItem.fly * 2 + 1)))) { + + // We act only if the button is released over a menu item. + while (!_vm->shouldQuit()) { + cursorPos = _vm->getMousePos(); + _activeMenuItem.lightUp(cursorPos); + _vm->_graphics->refreshScreen(); + + _vm->updateEvents(); + if (!_vm->_lucerna->_holdLeftMouse) + break; + } + + uint16 which = (cursorPos.y - 26) / 20; + _activeMenuItem.select(which); + if (_activeMenuItem._options[which]._valid) { // If the menu item wasn't active, we do nothing. + backup.free(); + return; + } + } + } + + } + } + + backup.free(); +} + +} // End of namespace Avalanche. diff --git a/engines/avalanche/dropdown2.h b/engines/avalanche/dropdown2.h new file mode 100644 index 0000000000..95e258effb --- /dev/null +++ b/engines/avalanche/dropdown2.h @@ -0,0 +1,167 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on the original source code of Lord Avalot d'Argent version 1.3. + * Copyright (c) 1994-1995 Mike, Mark and Thomas Thurman. + */ + +/* DROPDOWN A customised version of Oopmenu (qv). */ + +#ifndef AVALANCHE_DROPDOWN2_H +#define AVALANCHE_DROPDOWN2_H + +#include "avalanche/color.h" + +#include "common/scummsys.h" +#include "common/str.h" + + + +namespace Avalanche { +class AvalancheEngine; + +class Dropdown; + +typedef void (Dropdown::*DropdownFunc)(); + +class HeadType { +public: + Common::String _title; + char _trigger, _altTrigger; + byte _position; + int16 _xpos, _xright; + DropdownFunc _setupFunc, _chooseFunc; + + void init(char trig, char alTtrig, Common::String title, byte pos, DropdownFunc setupFunc, DropdownFunc chooseFunc, Dropdown *dr); + void draw(); + void highlight(); + bool parseAltTrigger(char key); + +private: + Dropdown *_dr; +}; + +struct OptionType { + Common::String _title; + byte _trigger; + Common::String _shortcut; + bool _valid; +}; + +class MenuItem { +public: + OptionType _options[12]; + byte _optionNum; + uint16 _width, _left; + bool _firstlix; + int16 _flx1, _flx2, fly; + byte _oldY; // used by lightUp */ + bool _activeNow; // Is there an active option now? + byte _activeNum; // And if so, which is it? + byte _choiceNum; // Your choice? + byte _highlightNum; + + void init(Dropdown *dr); + void reset(); + void setupOption(Common::String title, char trigger, Common::String shortcut, bool valid); + void display(); + void wipe(); + void lightUp(Common::Point cursorPos); // This makes the menu highlight follow the mouse. + void displayOption(byte y, bool highlit); + void moveHighlight(int8 inc); + void select(byte which); // Choose which one you want. + void parseKey(char c); + +private: + Dropdown *_dr; +}; + +class MenuBar { +public: + HeadType _menuItems[8]; + byte _menuNum; + + void init(Dropdown *dr); + void createMenuItem(char trig, Common::String title, char altTrig, DropdownFunc setupFunc, DropdownFunc chooseFunc); + void draw(); + void parseAltTrigger(char c); + void setupMenuItem(byte which); + void chooseMenuItem(int16 x); + +private: + Dropdown *_dr; +}; + +class Dropdown { +public: + friend class HeadType; + friend class MenuItem; + friend class MenuBar; + + MenuItem _activeMenuItem; + MenuBar _menuBar; + + Common::String people; + + Dropdown(AvalancheEngine *vm); + + void parseKey(char r, char re); + void updateMenu(); + void setupMenu(); // Standard menu bar. + +private: + static const byte kIndent = 5; + static const byte kSpacing = 10; + + static const byte kMenuBackgroundColor = kColorLightgray; + static const byte kMenuFontColor = kColorBlack; + static const byte kMenuBorderColor = kColorBlack; + static const byte kHighlightBackgroundColor = kColorBlack; + static const byte kHighlightFontColor = kColorWhite; + static const byte kDisabledColor = kColorDarkgray; + + AvalancheEngine *_vm; + + Common::String selectGender(byte x); // Returns "im" for boys, and "er" for girls. + void findWhatYouCanDoWithIt(); + void drawMenuText(int16 x, int16 y, char trigger, Common::String text, bool valid, bool highlighted); + void bleep(); + + void setupMenuGame(); + void setupMenuFile(); + void setupMenuAction(); + void setupMenuPeople(); + void setupMenuObjects(); + void setupMenuWith(); + + void runMenuGame(); + void runMenuFile(); + void runMenuAction(); + void runMenuObjects(); + void runMenuPeople(); + void runMenuWith(); +}; + +} // End of namespace Avalanche. + +#endif // AVALANCHE_DROPDOWN2_H diff --git a/engines/avalanche/enid2.cpp b/engines/avalanche/enid2.cpp new file mode 100644 index 0000000000..f6e22dfc59 --- /dev/null +++ b/engines/avalanche/enid2.cpp @@ -0,0 +1,302 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on the original source code of Lord Avalot d'Argent version 1.3. + * Copyright (c) 1994-1995 Mike, Mark and Thomas Thurman. + */ + +/* ENID Edna's manager. Loads/saves files. */ + +#include "avalanche/avalanche.h" + +#include "avalanche/enid2.h" +#include "avalanche/scrolls2.h" +#include "avalanche/lucerna2.h" +#include "avalanche/animation.h" +#include "avalanche/timer.h" +#include "avalanche/celer2.h" +#include "avalanche/sequence2.h" + +#include "common/textconsole.h" + +namespace Avalanche { + +Enid::Enid(AvalancheEngine *vm) { + _vm = vm; +} + +// CHECKME: useless? +#if 0 +const Common::String Enid::kCrlf = Common::String(char(15)) + Common::String(char(12)); +const char Enid::kTab = '\t'; +const char Enid::kEof = '\n'; + +const Common::String Enid::kEdnaFirst = + Common::String("This is an EDNA-based file, saved by a Thorsoft game. Good luck!") + // 64 + kCrlf + kEof + kCrlf + kCrlf + // 7 + kTab + "Glory to God in the highest," + kCrlf + // 31 + kTab + "and on earth peace, goodwill toward men." + // 42 + kCrlf + kTab + kTab + kTab + kTab + // 6 + "Luke 2:14." + // 10 + kCrlf + kCrlf + kCrlf + // 6 + "1234567890" +kCrlf; // 11 + +const Common::String Enid::kEdnaId = Common::String("TT\xB1\x30\x01\x75\xB1\x99\xB1"); +const int16 Enid::kAge = 18; +const Common::String Enid::kWasHere = "Thomas was here "; +const Enid::FourType Enid::kAvariciusFile = "Avvy"; + +/** + * Expand month name + * @remarks Originally called 'addon' + */ +void Enid::expandMonthName(Common::String x) { + _month += x; +} + +Common::String Enid::expandDate(byte d, byte m, uint16 y) { + const Common::String months[12] = { + "Jan#", "Febr#", "March", "April", "May", "June", "July", "August", + "Septem*", "Octo*", "Novem*", "Decem*" + }; + + Common::String expanddate_result; + _month = months[m]; + switch (_month[_month.size()]) { + case '#': + expandMonthName("uary"); + break; + case '*': + expandMonthName("ber"); + break; + } + + _day = _vm->_gyro->strf(d); + + // d is always positive + if ((d <= 9) || ((d >= 21) && (d <= 31))) { + switch (d % 10) { + case 1: + _day = _day + "st"; + break; + case 2: + _day = _day + "nd"; + break; + case 3: + _day = _day + "rd"; + break; + default: + _day = _day + "th"; + } + } + + expanddate_result = _day + ' ' + _month + ' ' + _vm->_gyro->strf(y); + return expanddate_result; +} + +void Enid::showBug(char icon, Common::String strn) { + _vm->_scrolls->display(Common::String("\7\6\23") + icon + "\26\r" + strn + '\15'); +} + +bool Enid::testBug(byte what) { + bool test_bug_result; + if (what == 0) { + test_bug_result = false; + return test_bug_result; + } + switch (what) { + case 2: + showBug('7', "Error in filename!"); + break; + case 101: + showBug('6', "Disk full!"); + break; + case 150: + showBug('4', "Disk is write-protected!"); + break; + default: + showBug('B', "Saving error!"); + } + test_bug_result = true; + return test_bug_result; +} + +void Enid::ednaSave(Common::String name) { + warning("STUB: Enid::ednaSave()"); +} + +void Enid::loadError(Common::String x, char icon) { + if (_vm->_gyro->holdthedawn) { + _vm->_gyro->holdthedawn = false; + _vm->_lucerna->dawn(); + } + _vm->_scrolls->display(Common::String('\7') + '\6' + '\23' + icon + '\26' + "Loading error: " + "\r\r\22" + x); + _bug = true; +} + +void Enid::ednaLoad(Common::String name) { + warning("STUB: Enid::ednaLoad()"); +} + +void Enid::showHeader() { + _vm->_scrolls->display(Common::String("Dir: ") + _path + "\r\r\4"); +} + +void Enid::avvyBackground() { + // Not really a filing procedure, + // but it's only called just before edna_load, so I thought I'd put it + // in Enid instead of, say, Lucerna. + +#if 0 + port[$3c4]:=2; port[$3ce]:=4; port[$3C5]:=1; port[$3CF]:=1; { Blue. } + + asm + mov dx,$3c4; mov al,2; out dx,al; { Set up the VGA to use the "blue" } + mov dx,$3ce; mov al,4; out dx,al; { register. } + mov dx,$3c5; mov al,1; out dx,al; + mov dx,$3cf; out dx,al; + + mov bx,$A000; call far ptr @drawup; + mov bx,$A400; call far ptr @drawup; + + jmp @the_end; + + @drawup: + + mov es,bx; { The segment to copy it to... } + mov di,$370; { The offset (10 pixels down, plus 1 offset.) } + + mov cx,10; + mov ax,$AA4A; call far ptr @sameline; { Print "AVVY" } + mov ax,$AEAA; call far ptr @sameline; + mov ax,$A4EA; call far ptr @sameline; + mov ax,$44A4; call far ptr @sameline; + + mov cx,9; + mov ax,$AAA4; call far ptr @sameline; { Print "YAVV" } + mov ax,$AAEA; call far ptr @sameline; + mov ax,$AA4E; call far ptr @sameline; + mov ax,$444A; call far ptr @sameline; + + mov ax,$4AAA; call far ptr @sameline; { Print "VYAV" } + mov ax,$AAAE; call far ptr @sameline; + mov ax,$EAA4; call far ptr @sameline; + mov ax,$A444; call far ptr @sameline; + + mov ax,$A4AA; call far ptr @sameline; { Print "VVYA" } + mov ax,$EAAA; call far ptr @sameline; + mov ax,$4EAA; call far ptr @sameline; + mov ax,$4A44; call far ptr @sameline; + + ret; + + + { Replicate the same line many times. } + + @sameline: + { Requires: + what to copy in AX, + how many lines in CX, and + original offset in DI. } + push cx; + push di; + + @samelineloop: + + push cx; + mov cx,40; { No. of times to repeat it on one line. } + + repz stosw; { Fast uint16-copying } + + pop cx; + + add di,1200; { The next one will be 16 lines down. } + + loop @samelineloop; + pop di; + add di,80; + pop cx; + + ret; + + @the_end: + end; +#endif +} + +void Enid::toSundry(Sundry &sund) { + sund._qEnidFilename = _vm->_gyro->_enidFilename; + sund._qSoundFx = _vm->_gyro->_soundFx; + sund._qThinks = _vm->_gyro->_thinks; + sund._qThinkThing = _vm->_gyro->_thinkThing; +} + +void Enid::fromSundry(Sundry sund) { + _vm->_gyro->_enidFilename = sund._qEnidFilename; + _vm->_gyro->_soundFx = sund._qSoundFx; + _vm->_gyro->_thinks = sund._qThinks; + _vm->_gyro->_thinkThing = sund._qThinkThing; +} + +void Enid::restoreDna() { +// uint16 here, fv; +// sundry sund; + + warning("STUB: Enid::restoreDna()"); +} + +void Enid::ednaReload() { + + restoreDna(); + + _vm->_gyro->_seeScroll = true; // This prevents display of the new sprites before the + // new picture is loaded. + + _vm->_lucerna->majorRedraw(); + + _vm->_gyro->_whereIs[_vm->_gyro->kPeopleAvalot - 150] = _vm->_gyro->_dna._room; + + _vm->_gyro->_alive = true; + + _vm->_lucerna->refreshObjectList(); + + if (_vm->_gyro->_holdTheDawn) { + _vm->_gyro->_holdTheDawn = false; + _vm->_lucerna->dawn(); + } +} + +bool Enid::thereWasAProblem() { + return _bug; +} + +#endif + +void Enid::dir(Common::String where) { // OK, it worked in Avaricius, let's do it in Avalot! + warning("STUB: Enid::dir()"); +} +void Enid::backToBootstrap(byte what) { + warning("STUB: Enid::back_to_bootstrap()"); +} + +} // End of namespace Avalanche. diff --git a/engines/avalanche/enid2.h b/engines/avalanche/enid2.h new file mode 100644 index 0000000000..97e249e187 --- /dev/null +++ b/engines/avalanche/enid2.h @@ -0,0 +1,78 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on the original source code of Lord Avalot d'Argent version 1.3. + * Copyright (c) 1994-1995 Mike, Mark and Thomas Thurman. + */ + +/* ENID Edna's manager. Loads/saves files. */ + +#ifndef AVALANCHE_ENID2_H +#define AVALANCHE_ENID2_H + +#include "common/scummsys.h" + +namespace Avalanche { +class AvalancheEngine; + +class Enid { +public: + Enid(AvalancheEngine *vm); + void dir(Common::String where); + void backToBootstrap(byte what); + +private: + AvalancheEngine *_vm; + +// CHECKME: useless? +// static const Common::String kCrlf; +// static const char kTab, kEof; +// static const Common::String kEdnaFirst, kEdnaId, kWasHere; +// static const int16 kAge; +// void expandMonthName(Common::String x); +// Common::String expandDate(byte d, byte m, uint16 y); +// void ednaSave(Common::String name); +// void showBug(char icon, Common::String strn); +// bool testBug(byte what); +// void loadError(Common::String x, char icon); +// void ednaLoad(Common::String name); +// void showHeader(); +// void toSundry(sundry &sund); +// void fromSundry(sundry sund); +// void avvyBackground(); +// void ednaReload(); // From Bootstrap's storage. +// void restoreDna(); +// bool thereWasAProblem(); + +// typedef char FourType[5]; +// static const FourType kAvariciusFile; + +// bool _bug; +// Common::String _path, _groi; +// Common::String _month, _day; + +}; + +} // End of namespace Avalanche. + +#endif // AVALANCHE_ENID2_H diff --git a/engines/avalanche/graphics.cpp b/engines/avalanche/graphics.cpp new file mode 100644 index 0000000000..6058b006bf --- /dev/null +++ b/engines/avalanche/graphics.cpp @@ -0,0 +1,320 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on the original source code of Lord Avalot d'Argent version 1.3. + * Copyright (c) 1994-1995 Mike, Mark and Thomas Thurman. + */ + +#include "math.h" + +#include "avalanche/avalanche.h" +#include "avalanche/graphics.h" + +#include "common/system.h" +#include "common/rect.h" + +#include "engines/util.h" + +#include "graphics/palette.h" + +namespace Avalanche { + +const byte Graphics::kEgaPaletteIndex[16] = {0, 1, 2, 3, 4, 5, 20, 7, 56, 57, 58, 59, 60, 61, 62, 63}; + +Graphics::Graphics(AvalancheEngine *vm) { + _vm = vm; +} + +Graphics::~Graphics() { + _surface.free(); + _magics.free(); + _background.free(); + _screen.free(); + _scrolls.free(); +} + +void Graphics::init() { + initGraphics(kScreenWidth, kScreenHeight * 2, true); // Doubling the height. + + for (int i = 0; i < 64; ++i) { + _egaPalette[i][0] = (i >> 2 & 1) * 0xaa + (i >> 5 & 1) * 0x55; + _egaPalette[i][1] = (i >> 1 & 1) * 0xaa + (i >> 4 & 1) * 0x55; + _egaPalette[i][2] = (i & 1) * 0xaa + (i >> 3 & 1) * 0x55; + } + + for (byte i = 0; i < 16; i++) + g_system->getPaletteManager()->setPalette(_egaPalette[kEgaPaletteIndex[i]], i, 1); + + _surface.create(kScreenWidth, kScreenHeight, ::Graphics::PixelFormat::createFormatCLUT8()); + _magics.create(kScreenWidth, kScreenHeight, ::Graphics::PixelFormat::createFormatCLUT8()); + _screen.create(kScreenWidth, kScreenHeight * 2, ::Graphics::PixelFormat::createFormatCLUT8()); + _scrolls.create(kScreenWidth, kScreenHeight, ::Graphics::PixelFormat::createFormatCLUT8()); +} + +void Graphics::fleshColors() +{ + g_system->getPaletteManager()->setPalette(_egaPalette[39], 13, 1); + g_system->getPaletteManager()->setPalette(_egaPalette[28], 5, 1); +} + +Common::Point Graphics::drawArc(::Graphics::Surface &surface, int16 x, int16 y, int16 stAngle, int16 endAngle, uint16 radius, byte color) { + Common::Point endPoint; + const double pi = 3.14; + const double convfac = pi / 180.0; + + int32 xRadius = radius; + int32 yRadius = radius * kScreenWidth / (8 * kScreenHeight); // Just don't ask why... + + if (xRadius == 0) + xRadius++; + if (yRadius == 0) + yRadius++; + + // Check for an ellipse with negligable x and y radius. + if ((xRadius <= 1) && (yRadius <= 1)) { + *(byte *)_scrolls.getBasePtr(x, y) = color; + endPoint.x = x; + endPoint.y = y; + return endPoint; + } + + // Check if valid angles. + stAngle = stAngle % 361; + endAngle = endAngle % 361; + + // If impossible angles, then swap them! + if (endAngle < stAngle) { + uint16 tmpAngle=endAngle; + endAngle=stAngle; + stAngle=tmpAngle; + } + + // Approximate the number of pixels required by using the circumference equation of an ellipse. + uint16 numOfPixels = (uint16)floor(sqrt(3.0) * sqrt(pow(double(xRadius), 2) + pow(double(yRadius), 2)) + 0.5); + + // Calculate the angle precision required. + double delta = 90.0 / numOfPixels; + + // Always just go over the first 90 degrees. Could be optimized a + // bit if startAngle and endAngle lie in the same quadrant, left as an + // exercise for the reader. :) + double j = 0; + + // Calculate stop position, go 1 further than 90 because otherwise 1 pixel is sometimes not drawn. + uint16 deltaEnd = 91; + + // Set the end point. + double tempTerm = endAngle * convfac; + endPoint.x = (int16)floor(xRadius * cos(tempTerm) + 0.5) + x; + endPoint.y = (int16)floor(yRadius * sin(tempTerm + pi) + 0.5) + y; + + // Calculate points. + int16 xNext = xRadius; + int16 yNext = 0; + do { + int16 xTemp = xNext; + int16 yTemp = yNext; + // This is used by both sin and cos. + tempTerm = (j + delta) * convfac; + + xNext = (int16)floor(xRadius * cos(tempTerm) + 0.5); + yNext = (int16)floor(yRadius * sin(tempTerm + pi) + 0.5); + + int16 xp = x + xTemp; + int16 xm = x - xTemp; + int16 yp = y + yTemp; + int16 ym = y - yTemp; + + if ((j >= stAngle) && (j <= endAngle)) + *(byte *)_scrolls.getBasePtr(xp,yp) = color; + + if (((180-j) >= stAngle) && ((180-j) <= endAngle)) + *(byte *)_scrolls.getBasePtr(xm,yp) = color; + + if (((j+180) >= stAngle) && ((j+180) <= endAngle)) + *(byte *)_scrolls.getBasePtr(xm,ym) = color; + + if (((360-j) >= stAngle) && ((360-j) <= endAngle)) + *(byte *)_scrolls.getBasePtr(xp,ym) = color; + + j += delta; + } while (j <= deltaEnd); + + return endPoint; +} + +void Graphics::drawPieSlice(::Graphics::Surface &surface, int16 x, int16 y, int16 stAngle, int16 endAngle, uint16 radius, byte color) { + while (radius > 0) + drawArc(surface, x, y, stAngle, endAngle, radius--, color); +} + +void Graphics::drawTriangle(::Graphics::Surface &surface, Common::Point *p, byte color) { + // Draw the borders with a marking color. + _scrolls.drawLine(p[0].x, p[0].y, p[1].x, p[1].y, 255); + _scrolls.drawLine(p[1].x, p[1].y, p[2].x, p[2].y, 255); + _scrolls.drawLine(p[2].x, p[2].y, p[0].x, p[0].y, 255); + + // Get the top and the bottom of the triangle. + uint16 maxY = p[0].y, minY = p[0].y; + for (byte i = 1; i < 3; i++) { + if (p[i].y < minY) + minY = p[i].y; + if (p[i].y > maxY) + maxY = p[i].y; + } + + // Fill the triangle. + for (uint16 y = minY; y <= maxY; y++) { + uint16 x = 0; + while (*(byte *)_scrolls.getBasePtr(x, y) != 255) + x++; + uint16 minX = x; + uint16 maxX = x; + x++; + while ((*(byte *)_scrolls.getBasePtr(x, y) != 255) && (x != 639)) + x++; + if (x != 639) + maxX = x; + if (minX != maxX) + _scrolls.drawLine(minX, y, maxX, y, color); + } + + // Redraw the borders with the actual color. + _scrolls.drawLine(p[0].x, p[0].y, p[1].x, p[1].y, color); + _scrolls.drawLine(p[1].x, p[1].y, p[2].x, p[2].y, color); + _scrolls.drawLine(p[2].x, p[2].y, p[0].x, p[0].y, color); +} + +void Graphics::drawText(::Graphics::Surface &surface, const Common::String &text, FontType font, byte fontHeight, int16 x, int16 y, byte color) { + for (byte i = 0; i < text.size(); i++) { + for (byte j = 0; j < fontHeight; j++) { + byte pixel = font[(byte)text[i]][j]; + for (byte bit = 0; bit < 8; bit++) { + byte pixelBit = (pixel >> bit) & 1; + if (pixelBit) + *(byte *)surface.getBasePtr(x + i * 8 + 7 - bit, y + j) = color; + } + } + } +} + +::Graphics::Surface Graphics::loadPictureGraphic(Common::File &file) { + // This function mimics Pascal's getimage(). + // The height and the width are stored in 2-2 bytes. We have to add 1 to each because Pascal stores the value of them -1. + uint16 width = file.readUint16LE() + 1; + uint16 height = file.readUint16LE() + 1; + + ::Graphics::Surface picture; // We make a Surface object for the picture itself. + picture.create(width, height, ::Graphics::PixelFormat::createFormatCLUT8()); + + // Produce the picture. We read it in row-by-row, and every row has 4 planes. + for (byte y = 0; y < height; y++) { + for (int8 plane = 3; plane >= 0; plane--) { // The planes are in the opposite way. + for (uint16 x = 0; x < width; x += 8) { + byte pixel = file.readByte(); + for (byte bit = 0; bit < 8; bit++) { + byte pixelBit = (pixel >> bit) & 1; + if (pixelBit != 0) + *(byte *)picture.getBasePtr(x + 7 - bit, y) += (pixelBit << plane); + } + } + } + } + return picture; +} + +::Graphics::Surface Graphics::loadPictureRow(Common::File &file, uint16 width, uint16 height) { + // This function is our own creation, very much like the one above. The main differences are that + // we don't read the width and the height from the file, the planes are in a different order + // and we read the picture plane-by-plane. + + ::Graphics::Surface picture; + picture.create(width, height, ::Graphics::PixelFormat::createFormatCLUT8()); + + for (byte plane = 0; plane < 4; plane++) { + for (uint16 y = 0; y < height; y++) { + for (uint16 x = 0; x < width; x += 8) { + byte pixel = file.readByte(); + for (byte i = 0; i < 8; i++) { + byte pixelBit = (pixel >> i) & 1; + *(byte *)picture.getBasePtr(x + 7 - i, y) += (pixelBit << plane); + } + } + } + } + + return picture; +} + +void Graphics::drawSprite(const SpriteInfo &sprite, byte picnum, int16 x, int16 y) { + // First we make the pixels of the spirte blank. + for (byte j = 0; j < sprite._yLength; j++) { + for (byte i = 0; i < sprite._xLength; i++) { + if (((*sprite._sil[picnum])[j][i / 8] >> ((7 - i % 8)) & 1) == 0) + *(byte *)_surface.getBasePtr(x + i, y + j) = 0; + } + } + + // Then we draw the picture to the blank places. + uint16 maniPos = 0; // Because the original manitype starts at 5!!! See Graphics.h for definition. + + for (byte j = 0; j < sprite._yLength; j++) { + for (int8 plane = 3; plane >= 0; plane--) { // The planes are in the opposite way. + for (uint16 i = 0; i < sprite._xLength; i += 8) { + byte pixel = (*sprite._mani[picnum])[maniPos++]; + for (byte bit = 0; bit < 8; bit++) { + byte pixelBit = (pixel >> bit) & 1; + *(byte *)_surface.getBasePtr(x + i + 7 - bit, y + j) += (pixelBit << plane); + } + } + } + } +} + +void Graphics::drawPicture(::Graphics::Surface &target, ::Graphics::Surface &picture, uint16 destX, uint16 destY) { + // Copy the picture to the given place on the screen. + for (uint16 y = 0; y < picture.h; y++) { + for (uint16 x = 0; x < picture.w; x++) { + *(byte *)target.getBasePtr(x + destX, y + destY) = *(byte *)picture.getBasePtr(x, y); + } + } +} + +void Graphics::refreshScreen() { + // These cycles are for doubling the screen height. + for (uint16 y = 0; y < _screen.h / 2; y++) { + for (uint16 x = 0; x < _screen.w; x++) { + for (byte j = 0; j < 2; j++) + *(byte *)_screen.getBasePtr(x, y * 2 + j) = *(byte *)_surface.getBasePtr(x, y); + } + } + // Now we copy the stretched picture to the screen. + g_system->copyRectToScreen(_screen.pixels, _screen.pitch, 0, 0, kScreenWidth, kScreenHeight * 2); + g_system->updateScreen(); +} + +void Graphics::refreshBackground() { + _vm->_graphics->drawPicture(_vm->_graphics->_surface, _vm->_graphics->_background, 0, 10); +} + +} // End of namespace Avalanche diff --git a/engines/avalanche/graphics.h b/engines/avalanche/graphics.h new file mode 100644 index 0000000000..a94ed77112 --- /dev/null +++ b/engines/avalanche/graphics.h @@ -0,0 +1,103 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on the original source code of Lord Avalot d'Argent version 1.3. + * Copyright (c) 1994-1995 Mike, Mark and Thomas Thurman. + */ + +#ifndef AVALANCHE_GRAPHICS_H +#define AVALANCHE_GRAPHICS_H + +#include "common/file.h" + +#include "graphics/surface.h" +#include "common/rect.h" + +namespace Avalanche { +class AvalancheEngine; + +typedef byte FontType[256][16]; // raw font type + +typedef byte ManiType[2049]; // manitype = array[5..2053] of byte; +// Be aware!!! + +typedef byte SilType[51][11]; // 35, 4 + +class SpriteInfo { +public: + byte _xWidth; + byte _xLength, _yLength; + ManiType *_mani[24]; + SilType *_sil[24]; + uint16 _size; // The size of one picture. +}; + +class Graphics { +public: + static const int16 kScreenWidth = 640; + static const int16 kScreenHeight = 200; + static const uint16 kBackgroundWidth = kScreenWidth; + static const byte kBackgroundHeight = 8 * 12080 / kScreenWidth; // With 640 width it's 151. + // The 8 = number of bits in a byte, and 12080 comes from Lucerna::load(). + + ::Graphics::Surface _surface; + ::Graphics::Surface _background; + ::Graphics::Surface _magics; // Lucerna::draw_also_lines() draws the "magical" lines here. Further information: https://github.com/urukgit/avalot/wiki/Also + ::Graphics::Surface _scrolls; + + Graphics(AvalancheEngine *vm); + ~Graphics(); + void init(); + void fleshColors(); + + // Taken from Free Pascal's Procedure InternalEllipseDefault. Used to replace Pascal's procedure arc. + // Returns the end point of the arc. (Needed in Lucerna::lucerna_clock().) + // TODO: Make it more accurate later. + Common::Point drawArc(::Graphics::Surface &surface, int16 x, int16 y, int16 stAngle, int16 endAngle, uint16 radius, byte color); + + void drawPieSlice(::Graphics::Surface &surface, int16 x, int16 y, int16 stAngle, int16 endAngle, uint16 radius, byte color); + void drawTriangle(::Graphics::Surface &surface, Common::Point *p, byte color); + void drawText(::Graphics::Surface &surface, const Common::String &text, FontType font, byte fontHeight, int16 x, int16 y, byte color); + + // The caller has to .free() the returned Surfaces!!! + // Further information about these two: http://www.shikadi.net/moddingwiki/Raw_EGA_data + ::Graphics::Surface loadPictureGraphic(Common::File &file); // Reads Graphic-planar EGA data. + ::Graphics::Surface loadPictureRow(Common::File &file, uint16 width, uint16 height); // Reads Row-planar EGA data. + + void drawSprite(const SpriteInfo &sprite, byte picnum, int16 x, int16 y); + void drawPicture(::Graphics::Surface &target, ::Graphics::Surface &picture, uint16 destX, uint16 destY); // Can't call .free() here. See Lucerna::showscore() for example. + void refreshScreen(); + void refreshBackground(); + +private: + AvalancheEngine *_vm; + + static const byte kEgaPaletteIndex[16]; + + byte _egaPalette[64][3]; + ::Graphics::Surface _screen; // Only used in refreshScreen() to make it more optimized. (No recreation of it at every call of the function.) +}; + +} // End of namespace Avalanche + +#endif // AVALANCHE_GRAPHICS_H diff --git a/engines/avalanche/gyro2.cpp b/engines/avalanche/gyro2.cpp new file mode 100644 index 0000000000..513052fd19 --- /dev/null +++ b/engines/avalanche/gyro2.cpp @@ -0,0 +1,515 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on the original source code of Lord Avalot d'Argent version 1.3. + * Copyright (c) 1994-1995 Mike, Mark and Thomas Thurman. + */ + +/* GYRO It all revolves around this bit! */ + +#include "avalanche/avalanche.h" + +#include "avalanche/gyro2.h" +#include "avalanche/pingo2.h" +#include "avalanche/scrolls2.h" +#include "avalanche/lucerna2.h" +#include "avalanche/visa2.h" +#include "avalanche/acci2.h" +#include "avalanche/animation.h" + +#include "common/file.h" +#include "common/random.h" +#include "common/textconsole.h" + +namespace Avalanche { + +const char *Gyro::kVersionNum = "1.30"; +const char *Gyro::kCopyright = "1995"; + +const MouseHotspotType Gyro::kMouseHotSpots[9] = { + {8,0}, // 1 - up-arrow + {0,0}, // 2 - screwdriver + {15,6}, // 3 - right-arrow + {0,0}, // 4 - fletch + {8,7}, // 5 - hourglass + {4,0}, // 6 - TTHand + {8,5}, // 7- Mark's crosshairs + {8,7}, // 8- I-beam + {0,0} // 9 - question mark +}; + +// Art gallery at 2,1; notice about this at 2,2. +const int32 Gyro::kCatacombMap[8][8] = { + // Geida's room + // 1 2 3 | 4 5 6 7 8 + {0x204, 0x200, 0xd0f0, 0xf0ff, 0xff, 0xd20f, 0xd200, 0x200}, + {0x50f1, 0x20ff, 0x2ff, 0xff, 0xe0ff, 0x20ff, 0x200f, 0x7210}, + {0xe3f0, 0xe10f, 0x72f0, 0xff, 0xe0ff, 0xff, 0xff, 0x800f}, + {0x2201, 0x2030, 0x800f, 0x220, 0x20f, 0x30, 0xff, 0x23f}, // >> Oubliette + {0x5024, 0xf3, 0xff, 0x200f, 0x22f0, 0x20f, 0x200, 0x7260}, + {0xf0, 0x2ff, 0xe2ff, 0xff, 0x200f, 0x50f0, 0x72ff, 0x201f}, + {0xf6, 0x220f, 0x22f0, 0x30f, 0xf0, 0x20f, 0x8200, 0x2f0}, // <<< In here + {0x34, 0x200f, 0x51f0, 0x201f, 0xf1, 0x50ff, 0x902f, 0x2062} +}; +// vv Stairs trap. + +/* Explanation: $NSEW. + Nibble N: North. + 0 = no connection, + 2 = (left,) middle(, right) door with left-hand handle, + 5 = (left,) middle(, right) door with right-hand handle, + 7 = arch, + 8 = arch and 1 north of it, + 9 = arch and 2 north of it, + D = no connection + WINDOW, + E = no connection + TORCH, + F = recessed door (to Geida's room.) + + Nibble S: South. + 0 = no connection, + 1,2,3 = left, middle, right door. + + Nibble E: East. + 0 = no connection (wall), + 1 = no connection (wall + window), + 2 = wall with door, + 3 = wall with door and window, + 6 = wall with candles, + 7 = wall with door and candles, + F = straight-through corridor. + + Nibble W: West. + 0 = no connection (wall), + 1 = no connection (wall + shield), + 2 = wall with door, + 3 = wall with door and shield, + 4 = no connection (window), + 5 = wall with door and window, + 6 = wall with candles, + 7 = wall with door and candles, + F = straight-through corridor. */ + +const char Gyro::kSpludwicksOrder[3] = {kObjectOnion, kObjectInk, kObjectMushroom}; + +// A quasiped defines how people who aren't sprites talk. For example, quasiped +// "A" is Dogfood. The rooms aren't stored because I'm leaving that to context. +const QuasipedType Gyro::kQuasipeds[16] = { + {2, kColorLightgray, 19, kColorBrown, kPeopleDogfood}, // A: Dogfood (screen 19). + {3, kColorGreen, 19, kColorWhite, kPeopleIbythneth}, // B: Ibythneth (screen 19). + {3, kColorWhite, 1, kColorMagenta, kPeopleArkata}, // C: Arkata (screen 1). + {3, kColorBlack, 23, kColorRed, 177}, // D: Hawk (screen 23). + {3, kColorLightgreen, 50, kColorBrown, kPeopleTrader}, // E: Trader (screen 50). + {6, kColorYellow, 42, kColorRed, kPeopleAvalot}, // F: Avvy, tied up (scr.42) + {2, kColorBlue, 16, kColorWhite, kPeopleAyles}, // G: Ayles (screen 16). + {2, kColorBrown, 7, kColorWhite, kPeopleJacques}, // H: Jacques (screen 7). + {2, kColorLightgreen, 47, kColorGreen, kPeopleSpurge}, // I: Spurge (screen 47). + {3, kColorYellow, 47, kColorRed, kPeopleAvalot}, // J: Avalot (screen 47). + {2, kColorLightgray, 23, kColorBlack, kPeopleDuLustie}, // K: du Lustie (screen 23). + {2, kColorYellow, 27, kColorRed, kPeopleAvalot}, // L: Avalot (screen 27). + {3, kColorWhite, 27, kColorRed, 177}, // M: Avaroid (screen 27). + {4, kColorLightgray, 19, kColorDarkgray, kPeopleMalagauche}, // N: Malagauche (screen 19). + {5, kColorLightmagenta, 47, kColorRed, kPeoplePort}, // O: Port (screen 47). + {2, kColorLightgreen, 51, kColorDarkgray, kPeopleDrDuck} // P: Duck (screen 51). +}; + +const char Gyro::kMusicKeys[] = "QWERTYUIOP[]"; + +const uint16 Gyro::kNotes[12] = {196, 220, 247, 262, 294, 330, 350, 392, 440, 494, 523, 587}; + +const TuneType Gyro::kTune = { + kPitchHigher, kPitchHigher, kPitchLower, kPitchSame, kPitchHigher, kPitchHigher, kPitchLower, kPitchHigher, kPitchHigher, kPitchHigher, + kPitchLower, kPitchHigher, kPitchHigher, kPitchSame, kPitchHigher, kPitchLower, kPitchLower, kPitchLower, kPitchLower, kPitchHigher, + kPitchHigher, kPitchLower, kPitchLower, kPitchLower, kPitchLower, kPitchSame, kPitchLower, kPitchHigher, kPitchSame, kPitchLower, kPitchHigher +}; + +byte Gyro::_whereIs[29] = { + // The Lads + r__yours, // Avvy + r__spludwicks, // Spludwick + r__outsideyours, // Crapulus + r__ducks, // Duck - r__DucksRoom's not defined yet. + r__argentpub, // Malagauche + r__robins, // Friar Tuck. + 177, // Robin Hood - can't meet him at the start. + r__brummieroad, // Cwytalot + r__lustiesroom, // Baron du Lustie. + r__outsidecardiffcastle, // The Duke of Cardiff. + r__argentpub, // Dogfood + r__outsideducks, // Trader + r__argentpub, // Ibythneth + r__aylesoffice, // Ayles + r__nottspub, // Port + r__nottspub, // Spurge + r__musicroom, // Jacques + 0, 0, 0, 0, 0, 0, 0, 0, + // The Lasses + r__yours, // Arkata + r__geidas, // Geida + 177, // nobody allocated here! + r__wisewomans // The Wise Woman. +}; + +Gyro::Gyro(AvalancheEngine *vm) : _interrogation(0), _onCanDoPageSwap(true) { + _vm = vm; + + // Needed because of Lucerna::load_also() + for (int fv = 0; fv < 31; fv++) { + for (int ff = 0; ff < 2; ff++) + _also[fv][ff] = 0; + } + + _dna._totalTime = 0; +} + +Gyro::~Gyro() { + for (byte i = 0; i < 9; i++) { + _digits[i].free(); + _directions[i].free(); + } + _digits[9].free(); +} + +Common::String Gyro::intToStr(int32 num) { + Common::String result = Common::String::format("%d", num); + return result; +} + +void Gyro::newMouse(byte id) { + if (id == _currentMouse) + return; + _currentMouse = id; + + loadMouse(id); +} + +/** + * Set the mouse pointer to 'HourGlass" + * @remarks Originally called 'wait' + */ +void Gyro::setMousePointerWait() { + newMouse(5); +} + +void Gyro::note(uint16 hertz) { + warning("STUB: Gyro::note()"); +} + +void Gyro::blip() { + warning("STUB: Gyro::blip()"); +} + +void Gyro::drawShadow(int16 x1, int16 y1, int16 x2, int16 y2, byte hc, byte sc) { + warning("STUB: Gyro::shadow()"); +} + +void Gyro::drawShadowBox(int16 x1, int16 y1, int16 x2, int16 y2, Common::String t) { + warning("STUB: Gyro::shbox()"); +} + +void Gyro::newGame() { + for (byte i = 0; i < kMaxSprites; i++) { + if (_vm->_animation->_sprites[i]._quick) + _vm->_animation->_sprites[i].remove(); + } + // Deallocate sprite. Sorry, beta testers! + + _vm->_animation->_sprites[0].init(0, true, _vm->_animation); + + _alive = true; + _score = 0; + memset(&_vm->_gyro->_dna, 0, sizeof(DnaType)); + + _vm->_scrolls->setBubbleStateNatural(); + + _dna._spareEvening = "answer a questionnaire"; + _dna._favouriteDrink = "beer"; + _dna._money = 30; // 2/6 + _dna._direction = kDirectionStopped; + _dna._wearing = kObjectClothes; + _dna._objects[kObjectMoney - 1] = true; + _dna._objects[kObjectBodkin - 1] = true; + _dna._objects[kObjectBell - 1] = true; + _dna._objects[kObjectClothes - 1] = true; + + _thinkThing = true; + _thinks = 2; + _vm->_lucerna->refreshObjectList(); + _onToolbar = false; + _seeScroll = false; + + _vm->_animation->_sprites[0].appear(300,117,kDirectionRight); // Needed to initialize Avalot. + //for (gd = 0; gd <= 30; gd++) for (gm = 0; gm <= 1; gm++) also[gd][gm] = nil; + // fillchar(previous^,sizeof(previous^),#0); { blank out array } + _him = 254; + _her = 254; + _it = 254; + _lastPerson = 254; // = Pardon? + _dna._passwordNum = _vm->_rnd->getRandomNumber(30) + 1; //Random(30) + 1; + _dna._userMovesAvvy = false; + _doingSpriteRun = false; + _dna._avvyInBed = true; + _enidFilename = ""; + + _vm->_lucerna->enterRoom(1, 1); + _vm->_animation->_sprites[0]._visible = false; + _vm->_lucerna->drawScore(); + _vm->_dropdown->setupMenu(); + _vm->_lucerna->_clock.update(); + _vm->_lucerna->spriteRun(); +} + +void Gyro::click() { + warning("STUB: Gyro::click()"); +} + +void Gyro::slowDown() { + warning("STUB: Gyro::slowdown()"); +} + +bool Gyro::setFlag(char x) { + for (uint16 i = 0; i < _flags.size(); i++) { + if (_flags[i] == x) + return true; + } + + return false; +} + +void Gyro::forceNumlock() { + warning("STUB: Gyro::force_numlock()"); +} + +bool Gyro::decreaseMoney(uint16 howmuchby) { + _dna._money -= howmuchby; + if (_dna._money < 0) { + _vm->_visa->displayScrollChain('Q', 2); // "You are now denariusless!" + _vm->_lucerna->gameOver(); + return false; + } else + return true; +} + +Common::String Gyro::getName(byte whose) { + static const Common::String kLads[17] = { + "Avalot", "Spludwick", "Crapulus", "Dr. Duck", "Malagauche", "Friar Tuck", + "Robin Hood", "Cwytalot", "du Lustie", "the Duke of Cardiff", "Dogfood", + "A trader", "Ibythneth", "Ayles", "Port", "Spurge", "Jacques" + }; + + static const Common::String kLasses[4] = {"Arkata", "Geida", "\0xB1", "the Wise Woman"}; + + if (whose < 175) + return kLads[whose - 150]; + else + return kLasses[whose - 175]; +} + +byte Gyro::getNameChar(byte whose) { + static const char kLadChar[] = "ASCDMTRwLfgeIyPu"; + static const char kLassChar[] = "kG\0xB1o"; + + if (whose < 175) + return kLadChar[whose - 150]; + else + return kLassChar[whose - 175]; +} + +Common::String Gyro::getThing(byte which) { + static const Common::String kThings[kObjectNum] = { + "Wine", "Money-bag", "Bodkin", "Potion", "Chastity belt", + "Crossbow bolt", "Crossbow", "Lute", "Pilgrim's badge", "Mushroom", "Key", + "Bell", "Scroll", "Pen", "Ink", "Clothes", "Habit", "Onion" + }; + + Common::String get_thing_result; + switch (which) { + case kObjectWine: + switch (_dna._wineState) { + case 1: + case 4: + get_thing_result = kThings[which - 1]; + break; + case 3: + get_thing_result = "Vinegar"; + break; + } + break; + case kObjectOnion: + if (_dna._rottenOnion) + get_thing_result = "rotten onion"; + else + get_thing_result = kThings[which - 1]; + break; + default: + get_thing_result = kThings[which - 1]; + } + return get_thing_result; +} + +char Gyro::getThingChar(byte which) { + static const char kThingsChar[] = "WMBParCLguKeSnIohn"; // V=Vinegar + + char get_thingchar_result; + switch (which) { + case kObjectWine: + if (_dna._wineState == 3) + get_thingchar_result = 'V'; // Vinegar + else + get_thingchar_result = kThingsChar[which - 1]; + break; + default: + get_thingchar_result = kThingsChar[which - 1]; + } + return get_thingchar_result; +} + +Common::String Gyro::getItem(byte which) { + static const Common::String kItems[kObjectNum] = { + "some wine", "your money-bag", "your bodkin", "a potion", "a chastity belt", + "a crossbow bolt", "a crossbow", "a lute", "a pilgrim's badge", "a mushroom", + "a key", "a bell", "a scroll", "a pen", "some ink", "your clothes", "a habit", + "an onion" + }; + + Common::String get_better_result; + if (which > 150) + which -= 149; + + switch (which) { + case kObjectWine: + switch (_dna._wineState) { + case 0: + case 1: + case 4: + get_better_result = kItems[which - 1]; + break; + case 3: + get_better_result = "some vinegar"; + break; + } + break; + case kObjectOnion: + if (_dna._rottenOnion) + get_better_result = "a rotten onion"; + else if (_dna._onionInVinegar) + get_better_result = "a pickled onion (in the vinegar)"; + else + get_better_result = kItems[which - 1]; + break; + default: + if ((which < kObjectNum) && (which > 0)) + get_better_result = kItems[which - 1]; + else + get_better_result = ""; + } + return get_better_result; +} + + +Common::String Gyro::f5Does() { + Common::String f5_does_result; + switch (_dna._room) { + case r__yours: { + if (!_dna._avvyIsAwake) + return Common::String(_vm->_acci->kVerbCodeWake) + "WWake up"; + else if (_dna._avvyInBed) + return Common::String(_vm->_acci->kVerbCodeStand) + "GGet up"; + } + break; + case r__insidecardiffcastle: + if (_dna._standingOnDais) + return Common::String(_vm->_acci->kVerbCodeClimb) + "CClimb down"; + else + return Common::String(_vm->_acci->kVerbCodeClimb) + "CClimb up"; + break; + case r__nottspub: + if (_dna._sittingInPub) + return Common::String(_vm->_acci->kVerbCodeStand) + "SStand up"; + else + return Common::String(_vm->_acci->kVerbCodeSit) + "SSit down"; + break; + case r__musicroom: + if (_vm->_animation->inField(7)) + return Common::String(_vm->_acci->kVerbCodePlay) + "PPlay the harp"; + break; + } + + return Common::String(_vm->_acci->kPardon); // If all else fails... +} + +void Gyro::loadMouse(byte which) { + Common::File f; + + if (!f.open("mice.avd")) { + warning("AVALANCHE: Gyro: File not found: mice.avd"); + return; + } + + ::Graphics::Surface cursor; + cursor.create(16, 32, ::Graphics::PixelFormat::createFormatCLUT8()); + cursor.fillRect(Common::Rect(0, 0, 16, 32), 255); + + + // The AND mask. + f.seek(kMouseSize * 2 * (which - 1) + 134); + + ::Graphics::Surface mask = _vm->_graphics->loadPictureGraphic(f); + + for (byte j = 0; j < mask.h; j++) + for (byte i = 0; i < mask.w; i++) + for (byte k = 0; k < 2; k++) + if (*(byte *)mask.getBasePtr(i, j) == 0) + *(byte *)cursor.getBasePtr(i, j * 2 + k) = 0; + + mask.free(); + + // The OR mask. + f.seek(kMouseSize * 2 * (which - 1) + 134 * 2); + + mask = _vm->_graphics->loadPictureGraphic(f); + + for (byte j = 0; j < mask.h; j++) + for (byte i = 0; i < mask.w; i++) + for (byte k = 0; k < 2; k++) { + byte pixel = *(byte *)mask.getBasePtr(i, j); + if (pixel != 0) + *(byte *)cursor.getBasePtr(i, j * 2 + k) = pixel; + } + + mask.free(); + f.close(); + + CursorMan.replaceCursor(cursor.pixels, 16, 32, kMouseHotSpots[which - 1]._horizontal, kMouseHotSpots[which - 1]._vertical * 2, 255, false); + cursor.free(); +} + +void Gyro::setBackgroundColor(byte x) { + warning("STUB: Gyro::background()"); +} + +void Gyro::hangAroundForAWhile() { + for (byte i = 0; i < 28; i++) + slowDown(); +} + +} // End of namespace Avalanche diff --git a/engines/avalanche/gyro2.h b/engines/avalanche/gyro2.h new file mode 100644 index 0000000000..faab64c070 --- /dev/null +++ b/engines/avalanche/gyro2.h @@ -0,0 +1,424 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on the original source code of Lord Avalot d'Argent version 1.3. + * Copyright (c) 1994-1995 Mike, Mark and Thomas Thurman. + */ + +/* GYRO It all revolves around this bit! */ + +#ifndef AVALANCHE_GYRO2_H +#define AVALANCHE_GYRO2_H + +#include "common/str.h" +#include "common/scummsys.h" +#include "common/file.h" + +#include "graphics/surface.h" + +#include "avalanche/roomnums.h" +#include "avalanche/color.h" + +namespace Avalanche { +class AvalancheEngine; + + +static const byte kObjectNum = 18; // always preface with a # +static const int16 kCarryLimit = 12; // carry limit + +static const int16 kNumlockCode = 32; // Code for Num Lock +static const int16 kMouseSize = 134; + +struct MouseHotspotType { // mouse-void + int16 _horizontal, _vertical; +}; + +struct DnaType { // Ux, uy, & ww now all belong to Trip5 + byte _direction; // The direction Avvy is currently facing. + byte _carryNum; // How many objects you're carrying... + bool _objects[kObjectNum]; // ...and which ones they are. + int16 _score; // your score, of course + int32 _money; // your current amount of dosh + byte _room; // your current room + byte _wearing; // what you're wearing + byte _sworeNum; // number of times you've sworn + byte _saveNum; // number of times this game has been saved + byte _roomCount[100]; // Add one to each every time you enter a room + byte _alcoholLevel; // Your blood alcohol level. + byte _playedNim; // How many times you've played Nim. + bool _wonNim; // Have you *won* Nim? (That's harder.) + byte _wineState; // 0=good (Notts), 1=passable(Argent) ... 3=vinegar. + bool _cwytalotGone; // Has Cwytalot rushed off to Jerusalem yet? + byte _passwordNum; // Number of the passw for this game. + bool _aylesIsAwake; // pretty obvious! + byte _drawbridgeOpen; // Between 0 (shut) and 4 (open). + byte _avariciusTalk; // How much Avaricius has said to you. + bool _boughtOnion; // Have you bought an onion yet? + bool _rottenOnion; // And has it rotted? + bool _onionInVinegar; // Is the onion in the vinegar? + byte _givenToSpludwick; // 0 = nothing given, 1 = onion... + byte _brummieStairs; // Progression through the stairs trick. + byte _cardiffQuestionNum; // Things you get asked in Cardiff. + bool _passedCwytalotInHerts; // Have you passed Cwytalot in Herts? + bool _avvyIsAwake; // Well? Is Avvy awake? (Screen 1 only.) + bool _avvyInBed; // True if Avvy's in bed, but awake. + bool _userMovesAvvy; // If this is false, the user has no control over Avvy's movements. + byte _dogFoodPos; // Which way Dogfood's looking in the pub. + bool _givenBadgeToIby; // Have you given the badge to Iby yet? + bool _friarWillTieYouUp; // If you're going to get tied up. + bool _tiedUp; // You ARE tied up! + byte _boxContent; // 0 = money (sixpence), 254 = empty, any other number implies the contents of the box. + bool _talkedToCrapulus; // Pretty self-explanatory. + byte _jacquesState; // 0=asleep, 1=awake, 2=gets up, 3=gone. + bool _bellsAreRinging; // Is Jacques ringing the bells? + bool _standingOnDais; // In room 71, inside Cardiff Castle. + bool _takenPen; // Have you taken the pen (in Cardiff?) + bool _arrowTriggered; // And has the arrow been triggered? + bool _arrowInTheDoor; // Did the arrow hit the wall? + Common::String _favouriteDrink, _favouriteSong, _worstPlaceOnEarth, _spareEvening; // Personalisation str's + uint32 _totalTime; // Your total time playing this game, in ticks. + byte _jumpStatus; // Fixes how high you're jumping. + bool _mushroomGrowing; // Is the mushroom growing in 42? + bool _spludwickAtHome; // Is Spludwick at home? + byte _lastRoom; + byte _lastRoomNotMap; + bool _crapulusWillTell; // Will Crapulus tell you about Spludwick being away? + bool _enterCatacombsFromLustiesRoom; + bool _teetotal; // Are we touching any more drinks? + byte _malagauche; // Position of Malagauche. See Celer for more info. + char _drinking; // What's he getting you? + bool _enteredLustiesRoomAsMonk; + byte _catacombX, _catacombY; // XY coords in the catacombs. + bool _avvysInTheCupboard; // On screen 22. + bool _geidaFollows; // Is Geida following you? + byte _geidaSpin, _geidaTime; // For the making "Geida dizzy" joke. + byte _nextBell; // For the ringing. + bool _givenPotionToGeida; // Does Geida have the potion? + bool _lustieIsAsleep; // Is BDL asleep? + byte _flipToWhere, _flipToPed; // For the sequencer. + bool _beenTiedUp; // In r__Robins. + bool _sittingInPub; // Are you sitting down in the pub? + byte _spurgeTalkCount; // Count for talking to Spurge. + bool _metAvaroid; + bool _takenMushroom, _givenPenToAyles, _askedDogfoodAboutNim; +}; + +struct PedType { + int16 _x, _y; + byte _direction; +}; + +struct MagicType { + byte _operation; // one of the operations + uint16 _data; // data for them +}; + +class FieldType { +public: + int16 _x1, _y1, _x2, _y2; +}; + +struct ByteField { + byte _x1, _y1, _x2, _y2; +}; + +class LineType : public FieldType { +public: + byte _color; +}; + +struct DemoType { + uint16 _delay; + char _key, _extd; +}; + +typedef byte TuneType[31]; + +struct QuasipedType { + byte _whichPed, _foregroundColor, _room, _backgroundColor; + uint16 _who; +}; + +#if 0 +struct Sundry { // Things which must be saved over a backtobootstrap, outside DNA. + Common::String _qEnidFilename; + bool _qSoundFx; + byte _qThinks; + bool _qThinkThing; +}; + +struct ednahead { // Edna header + // This header starts at byte offset 177 in the .ASG file. + char id[9]; // signature + uint16 revision; // EDNA revision, here 2 (1=dna256) + Common::String game; // Long name, eg Lord Avalot D'Argent + Common::String shortname; // Short name, eg Avalot + uint16 number; // Game's code number, here 2 + uint16 ver; // Version number as int16 (eg 1.00 = 100) + Common::String verstr; // Vernum as Common::String (eg 1.00 = "1.00") + Common::String filename; // Filename, eg AVALOT.EXE + byte osbyte; // Saving OS (here 1=DOS. See below for others. + Common::String os; // Saving OS in text format. + + // Info on this particular game + + Common::String fn; // Filename (not extension ('cos that's .ASG)) + byte d, m; // D, M, Y are the Day, Month & Year this game was... + uint16 y; // ...saved on. + Common::String desc; // Description of game (same as in Avaricius!) + uint16 len; // Length of DNA (it's not going to be above 65535!) + + // Quick reference & miscellaneous + + uint16 saves; // no. of times this game has been saved + int16 cash; // contents of your wallet in numerical form + Common::String money; // ditto in Common::String form (eg 5/-, or 1 denarius) + uint16 points; // your score + + // DNA values follow, then footer (which is ignored) +}; +#endif + +class Gyro { +public: + static const char *kVersionNum; + static const char *kCopyright; + static const int16 kVersionCode = 130; // Same as kVersionCode, but numerically & without the ".". + static const int16 kGameCode = 2; // Avalot's code number + + // Objects you can hold: + enum Object { + kObjectWine = 1, + kObjectMoney, + kObjectBodkin, + kObjectPotion, + kObjectChastity, + kObjectBolt, + kObjectCrossbow, + kObjectLute, + kObjectBadge, + kObjectMushroom, + kObjectKey, + kObjectBell, + kObjectPrescription, + kObjectPen, + kObjectInk, + kObjectClothes, + kObjectHabit, + kObjectOnion + }; + + // People who hang around this game. + enum People { + // Boys: + kPeopleAvalot = 150, + kPeopleSpludwick = 151, + kPeopleCrapulus = 152, + kPeopleDrDuck = 153, + kPeopleMalagauche = 154, + kPeopleFriarTuck = 155, + kPeopleRobinHood = 156, + kPeopleCwytalot = 157, + kPeopleDuLustie = 158, + kPeopleDuke = 159, + kPeopleDogfood = 160, + kPeopleTrader = 161, + kPeopleIbythneth = 162, + kPeopleAyles = 163, + kPeoplePort = 164, + kPeopleSpurge = 165, + kPeopleJacques = 166, + // Girls: + kPeopleArkata = 175, + kPeopleGeida = 176, + kPeopleWisewoman = 178 + }; + + static const int16 kXW = 30; + static const int16 kYW = 36; // x width & y whatsit + + static const int16 kMargin = 5; + + static const MouseHotspotType kMouseHotSpots[9]; + + static const int16 kMaxSprites = 2; // Current max no. of sprites. + + // For Thinkabout: + static const bool kThing = true; + static const bool kPerson = false; + + // Magic/portal constants: + enum Magics { + kMagicNothing, // Ignore it if this line is touched. + kMagicBounce, // Bounce off this line. Not valid for portals. + kMagicExclaim, // Put up a chain of scrolls. + kMagicTransport, // Enter new room. + kMagicUnfinished, // Unfinished connection. + kMagicSpecial, // Special function. + kMagicOpenDoor // Opening door. + }; + + // These following static constants should be included in CFG when it's written. + + static const bool kSlowComputer = false; // Stops walking when mouse touches toolbar. + + static const int16 kBorder = 1; // size of border on shadowboxes + + enum Direction { + kDirectionUp, kDirectionRight, kDirectionDown, kDirectionLeft, + kDirectionUpRight, kDirectionDownRight, kDirectionDownLeft, kDirectionUpLeft, + kDirectionStopped + }; + + static const int16 kWalk = 3; + static const int16 kRun = 5; + + static const int32 kCatacombMap[8][8]; + + static const bool kDemo = false; // If this is true, we're in a demo of the game. + + static const char kSpludwicksOrder[3]; + + static const QuasipedType kQuasipeds[16]; + + enum Pitch { + kPitchLower, + kPitchSame, + kPitchHigher + }; + + static const char kMusicKeys[]; + static const uint16 kNotes[12]; + + static const TuneType kTune; + + + + // If this is greater than zero, the next line you type is stored in the DNA in a position dictated by the value. + // If a scroll comes up, or you leave the room, it's automatically set to zero. + byte _interrogation; + + static byte _whereIs[29]; + + // Variable static constant for overriding the ability of On to switch pages. + // You may know better than On which page to switch to. + bool _onCanDoPageSwap; + + DnaType _dna; + byte _lineNum; // Number of lines. + LineType _lines[50]; // For Also. + enum MouseState { kMouseStateNo, kMouseStateYes, kMouseStateVirtual } _mouse; + bool _dropsOk, _scReturn, _soundFx, _cheat; + Common::String _mouseText; + bool _weirdWord; + bool _letMeOut; + Common::String _scroll[15]; + byte _scrollNum, _score, _whichwas; + byte _thinks; + bool _thinkThing; + int16 _talkX, _talkY; + byte _talkBackgroundColor, _talkFontColor; + byte _scrollBells; // no. of times to ring the bell + bool _onToolbar, _seeScroll; // TODO: maybe this means we're interacting with the toolbar / a scroll? + char _objectList[10]; + ::Graphics::Surface _digits[10]; // digitsize and rwlitesize are defined in Lucerna::load_digits() !!! + ::Graphics::Surface _directions[9]; // Maybe it will be needed to move them to the class itself instead. + // Called .free() for them in ~Gyro(). + byte _oldDirection; + int8 _scoreToDisplay[3]; + byte _currentMouse; // current mouse-void + Common::String _verbStr; // what you can do with your object. :-) + Common::String *_also[31][2]; + PedType _peds[15]; + MagicType _magics[15]; + MagicType _portals[7]; + FieldType _fields[30]; + byte _fieldNum; + Common::String _flags; + Common::String _listen; + Common::String _atKey; // For XTs, set to "alt-". For ATs, set to "f1". + byte _cp, _ledStatus, _defaultLed; + FontType _font; + bool _alive; + byte _buffer[2000]; + uint16 _bufSize; + int16 _underScroll; // Y-coord of just under the scroll text. + bool _dropdownActive; // Kludge so we don't have to keep referring to Dropdown + Common::String _roomnName; // Name of actual room + Common::String _subject; // What you're talking to them about. + byte _subjectNum; // The same thing. + bool _keyboardClick; // Is a keyboard click noise wanted? + byte _him, _her, _it; + int32 _roomTime; // Set to 0 when you enter a room, added to in every loop. + + // For the demo: + DemoType _demoRec; + Common::File _demoFile; // of demo_type + Common::DumpFile _demoFileSave; // uruk added it - first use located in constructor of Basher + + byte _lastPerson; // Last person to have been selected using the People menu. + bool _doingSpriteRun; // Only set to True if we're doing a sprite_run at this moment. This stops the trippancy system from moving any of the sprites. + bool _holdTheDawn; // If this is true, calling Dawn will do nothing. It's used, for example, at the start, to stop Load from dawning. + bool isLoaded; // Is it a loaded gamestate? + Common::String _enidFilename; + + + + Gyro(AvalancheEngine *vm); + ~Gyro(); + + Common::String intToStr(int32 num); + void newMouse(byte id); + void setMousePointerWait(); // Makes hourglass. + void loadMouse(byte which); + + void note(uint16 hertz); + void blip(); + void click(); // "Audio keyboard feedback" + + void setBackgroundColor(byte x); + void drawShadowBox(int16 x1, int16 y1, int16 x2, int16 y2, Common::String t); + + void newGame(); // This sets up the DNA for a completely new game. + void slowDown(); + bool setFlag(char x); + void forceNumlock(); + bool decreaseMoney(uint16 howmuchby); // Called pennycheck in the original. + void hangAroundForAWhile(); + + Common::String getName(byte whose); + byte getNameChar(byte whose); + Common::String getThing(byte which); + char getThingChar(byte which); + Common::String getItem(byte which); // Called get_better in the original. + Common::String f5Does(); // This procedure determines what f5 does. + +private: + AvalancheEngine *_vm; + + void drawShadow(int16 x1, int16 y1, int16 x2, int16 y2, byte hc, byte sc); +}; + +} // End of namespace Avalanche + +#endif // AVALANCHE_GYRO2_H diff --git a/engines/avalanche/lucerna2.cpp b/engines/avalanche/lucerna2.cpp new file mode 100644 index 0000000000..390c13bfa9 --- /dev/null +++ b/engines/avalanche/lucerna2.cpp @@ -0,0 +1,1262 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on the original source code of Lord Avalot d'Argent version 1.3. + * Copyright (c) 1994-1995 Mike, Mark and Thomas Thurman. + */ + +/* LUCERNA The screen, [keyboard] and mouse handler.*/ + +#include "avalanche/avalanche.h" + +#include "avalanche/lucerna2.h" +#include "avalanche/gyro2.h" +#include "avalanche/scrolls2.h" +#include "avalanche/visa2.h" +#include "avalanche/timer.h" +#include "avalanche/animation.h" +#include "avalanche/enid2.h" +#include "avalanche/celer2.h" +#include "avalanche/pingo2.h" +#include "avalanche/sequence2.h" +#include "avalanche/acci2.h" +#include "avalanche/roomnums.h" + +#include "common/rect.h" +#include "common/system.h" + +#include "graphics/palette.h" + +namespace Avalanche { + +Clock::Clock(AvalancheEngine *vm) { + _vm = vm; + _oldHour = _oldHourAngle = _oldMinute = 17717; +} + +void Clock::update() { // TODO: Move variables from Gyro to here (or at least somewhere nearby), rename them. + TimeDate t; + _vm->_system->getTimeAndDate(t); + _hour = t.tm_hour; + _minute = t.tm_min; + _second = t.tm_sec; + + _hourAngle = (_hour % 12) * 30 + _minute / 2; + + if (_oldHour != _hour) { + plotHands(); + chime(); + } + + if (_oldMinute != _minute) + plotHands(); + + if ((_hour == 0) && (_oldHour != 0) && (_oldHour != 17717)) + _vm->_scrolls->displayText(Common::String("Good morning!") + _vm->_scrolls->kControlNewLine + _vm->_scrolls->kControlNewLine + + "Yes, it's just past midnight. Are you having an all-night Avvy session? Glad you like the game that much!"); + + _oldHour = _hour; + _oldHourAngle = _hourAngle; + _oldMinute = _minute; +} + +void Clock::calcHand(uint16 angle, uint16 length, Common::Point &endPoint, byte color) { + if (angle > 900) { + endPoint.x = 177; + return; + } + + endPoint = _vm->_graphics->drawArc(_vm->_graphics->_surface, kCenterX, kCenterY, 449 - angle, 450 - angle, length, color); +} + +void Clock::drawHand(const Common::Point &endPoint, byte color) { + if (endPoint.x == 177) + return; + + _vm->_graphics->_surface.drawLine(kCenterX, kCenterY, endPoint.x, endPoint.y, color); +} + +void Clock::plotHands() { + calcHand(_oldHourAngle, 14, _clockHandHour, kColorYellow); + calcHand(_oldMinute * 6, 17, _clockHandMinute, kColorYellow); + drawHand(_clockHandHour, kColorBrown); + drawHand(_clockHandMinute, kColorBrown); + + calcHand(_hourAngle, 14, _clockHandHour, kColorBrown); + calcHand(_minute * 6, 17, _clockHandMinute, kColorBrown); + drawHand(_clockHandHour, kColorYellow); + drawHand(_clockHandMinute, kColorYellow); +} + +void Clock::chime() { + warning("STUB: Clock::chime()"); +} + +Lucerna::Lucerna(AvalancheEngine *vm) : _fxHidden(false), _clock(vm) { + _vm = vm; +} + +Lucerna::~Lucerna() { + for (byte i = 0; i < 31; i++) { + for (byte j = 0; j < 2; j++) { + if (_vm->_gyro->_also[i][j] != 0) { + delete _vm->_gyro->_also[i][j]; + _vm->_gyro->_also[i][j] = 0; + } + } + } +} + +void Lucerna::init() { + for (byte i = 0; i < 31; i++) { + for (byte j = 0; j < 2; j++) + _vm->_gyro->_also[i][j] = 0; + } + +#if 0 + if (_vm->_enhanced->atbios) + _vm->_gyro->atkey = "f1"; + else + _vm->_gyro->atkey = "alt-"; +#endif +} + +/** + * Call a given Verb + * @remarks Originally called 'callverb' + */ +void Lucerna::callVerb(byte id) { + if (id == _vm->_acci->kPardon) { + _vm->_scrolls->displayText(Common::String("The f5 key lets you do a particular action in certain ") + + "situations. However, at the moment there is nothing " + + "assigned to it. You may press alt-A to see what the " + + "current setting of this key is."); + } else { + _vm->_gyro->_weirdWord = false; + _vm->_acci->_polite = true; + _vm->_acci->_verb = id; + _vm->_acci->doThat(); + } +} + +void Lucerna::drawAlsoLines() { + CursorMan.showMouse(false); + + _vm->_graphics->_magics.fillRect(Common::Rect(0, 0, 640, 200), 0); + _vm->_graphics->_magics.frameRect(Common::Rect(0, 45, 640, 161), 15); + + for (byte i = 0; i < _vm->_gyro->_lineNum; i++) { + // We had to check if the lines are within the borders of the screen. + if ((_vm->_gyro->_lines[i]._x1 >= 0) && (_vm->_gyro->_lines[i]._x1 < _vm->_graphics->kScreenWidth) && (_vm->_gyro->_lines[i]._y1 >= 0) && (_vm->_gyro->_lines[i]._y1 < _vm->_graphics->kScreenHeight) + && (_vm->_gyro->_lines[i]._x2 >= 0) && (_vm->_gyro->_lines[i]._x2 < _vm->_graphics->kScreenWidth) && (_vm->_gyro->_lines[i]._y2 >= 0) && (_vm->_gyro->_lines[i]._y2 < _vm->_graphics->kScreenHeight)) + _vm->_graphics->_magics.drawLine(_vm->_gyro->_lines[i]._x1, _vm->_gyro->_lines[i]._y1, _vm->_gyro->_lines[i]._x2, _vm->_gyro->_lines[i]._y2, _vm->_gyro->_lines[i]._color); + } + + CursorMan.showMouse(true); +} + +// readAlsoStringFromFile, scram and unScramble are only used in loadAlso + +/** + * Check is it's possible to give something to Spludwick + * @remarks Originally called 'nextstring' + */ +Common::String Lucerna::readAlsoStringFromFile() { + Common::String str; + byte length = file.readByte(); + for (int i = 0; i < length; i++) + str += file.readByte(); + return str; +} + +void Lucerna::scram(Common::String &str) { + for (uint i = 0; i < str.size(); i++) + str.setChar(str[i] ^ 177, i); +} + +void Lucerna::unScramble() { + for (byte i = 0; i < 31; i++) { + for (byte j = 0; j < 2; j++) { + if (_vm->_gyro->_also[i][j] != 0) + scram(*_vm->_gyro->_also[i][j]); + } + } + scram(_vm->_gyro->_listen); + scram(_vm->_gyro->_flags); +} + +void Lucerna::loadAlso(byte num) { + for (byte i = 0; i < 31; i++) { + for (byte j = 0; j < 2; j++) { + if (_vm->_gyro->_also[i][j] != 0) { + delete _vm->_gyro->_also[i][j]; + _vm->_gyro->_also[i][j] = 0; + } + } + } + Common::String filename; + filename = filename.format("also%d.avd", num); + if (!file.open(filename)) { + warning("AVALANCHE: Lucerna: File not found: %s", filename.c_str()); + return; + } + + file.seek(128); + + byte alsoNum = file.readByte(); + for (byte i = 0; i <= alsoNum; i++) { + for (byte j = 0; j < 2; j++) { + _vm->_gyro->_also[i][j] = new Common::String; + *_vm->_gyro->_also[i][j] = readAlsoStringFromFile(); + } + *_vm->_gyro->_also[i][0] = Common::String('\x9D') + *_vm->_gyro->_also[i][0] + Common::String('\x9D'); + } + + memset(_vm->_gyro->_lines, 0xFF, sizeof(_vm->_gyro->_lines)); + + _vm->_gyro->_lineNum = file.readByte(); + for (byte i = 0; i < _vm->_gyro->_lineNum; i++) { + _vm->_gyro->_lines[i]._x1 = file.readSint16LE(); + _vm->_gyro->_lines[i]._y1 = file.readSint16LE(); + _vm->_gyro->_lines[i]._x2 = file.readSint16LE(); + _vm->_gyro->_lines[i]._y2 = file.readSint16LE(); + _vm->_gyro->_lines[i]._color = file.readByte(); + } + + memset(_vm->_gyro->_peds, 177, sizeof(_vm->_gyro->_peds)); + byte pedNum = file.readByte(); + for (byte i = 0; i < pedNum; i++) { + _vm->_gyro->_peds[i]._x = file.readSint16LE(); + _vm->_gyro->_peds[i]._y = file.readSint16LE(); + _vm->_gyro->_peds[i]._direction = file.readByte(); + } + + _vm->_gyro->_fieldNum = file.readByte(); + for (byte i = 0; i < _vm->_gyro->_fieldNum; i++) { + _vm->_gyro->_fields[i]._x1 = file.readSint16LE(); + _vm->_gyro->_fields[i]._y1 = file.readSint16LE(); + _vm->_gyro->_fields[i]._x2 = file.readSint16LE(); + _vm->_gyro->_fields[i]._y2 = file.readSint16LE(); + } + + for (byte i = 0; i < 15; i++) { + _vm->_gyro->_magics[i]._operation = file.readByte(); + _vm->_gyro->_magics[i]._data = file.readUint16LE(); + } + + for (byte i = 0; i < 7; i++) { + _vm->_gyro->_portals[i]._operation = file.readByte(); + _vm->_gyro->_portals[i]._data = file.readUint16LE(); + } + + _vm->_gyro->_flags.clear(); + for (byte i = 0; i < 26; i++) + _vm->_gyro->_flags += file.readByte(); + + int16 listen_length = file.readByte(); + _vm->_gyro->_listen.clear(); + for (byte i = 0; i < listen_length; i++) + _vm->_gyro->_listen += file.readByte(); + + drawAlsoLines(); + + file.close(); + unScramble(); + for (byte i = 0; i <= alsoNum; i++) + *_vm->_gyro->_also[i][0] = Common::String(',') + *_vm->_gyro->_also[i][0] + ','; +} + +void Lucerna::loadRoom(byte num) { + CursorMan.showMouse(false); + + _vm->_graphics->fleshColors(); + + Common::String filename; + filename = filename.format("place%d.avd", num); + if (!file.open(filename)) { + warning("AVALANCHE: Lucerna: File not found: %s", filename.c_str()); + return; + } + + file.seek(146); + if (!_vm->_gyro->_roomnName.empty()) + _vm->_gyro->_roomnName.clear(); + for (byte i = 0; i < 30; i++) { + char actChar = file.readByte(); + if ((32 <= actChar) && (actChar <= 126)) + _vm->_gyro->_roomnName += actChar; + } + // Compression method byte follows this... + + file.seek(177); + + _vm->_graphics->_background = _vm->_graphics->loadPictureRow(file, _vm->_graphics->kBackgroundWidth, _vm->_graphics->kBackgroundHeight); + _vm->_graphics->refreshBackground(); + + file.close(); + + loadAlso(num); + _vm->_celer->loadBackgroundSprites(num); + CursorMan.showMouse(true); +} + + + +void Lucerna::zoomOut(int16 x, int16 y) { + warning("STUB: Lucerna::zoomout()"); +} + +void Lucerna::findPeople(byte room) { + for (byte i = 1; i < 29; i++) { + if (_vm->_gyro->_whereIs[i] == room) { + if (i < 25) + _vm->_gyro->_him = i + 150; + else + _vm->_gyro->_her = i + 150; + } + } +} + +void Lucerna::exitRoom(byte x) { + //nosound(); + _vm->_celer->forgetBackgroundSprites(); + _vm->_gyro->_seeScroll = true; // This stops the trippancy system working over the length of this procedure. + + switch (x) { + case r__spludwicks: + _vm->_timer->loseTimer(_vm->_timer->kReasonAvariciusTalks); + _vm->_gyro->_dna._avariciusTalk = 0; + // He doesn't HAVE to be talking for this to work. It just deletes it IF it exists. + break; + case r__bridge: + if (_vm->_gyro->_dna._drawbridgeOpen > 0) { + _vm->_gyro->_dna._drawbridgeOpen = 4; // Fully open. + _vm->_timer->loseTimer(_vm->_timer->kReasonDrawbridgeFalls); + } + break; + case r__outsidecardiffcastle: + _vm->_timer->loseTimer(_vm->_timer->kReasonCardiffsurvey); + break; + case r__robins: + _vm->_timer->loseTimer(_vm->_timer->kReasonGettingTiedUp); + break; + } + + _vm->_gyro->_interrogation = 0; // Leaving the room cancels all the questions automatically. + + _vm->_gyro->_seeScroll = false; // Now it can work again! + + _vm->_gyro->_dna._lastRoom = _vm->_gyro->_dna._room; + if (_vm->_gyro->_dna._room != r__map) + _vm->_gyro->_dna._lastRoomNotMap = _vm->_gyro->_dna._room; +} + + +/** + * Only when entering a NEW town! Not returning to the last one, + * but choosing another from the map. + * @remarks Originally called 'new_town' + */ +void Lucerna::enterNewTown() { + _vm->_dropdown->setupMenu(); + + switch (_vm->_gyro->_dna._room) { + case r__outsidenottspub: // Entry into Nottingham. + if ((_vm->_gyro->_dna._roomCount[r__robins] > 0) && (_vm->_gyro->_dna._beenTiedUp) && (!_vm->_gyro->_dna._takenMushroom)) + _vm->_gyro->_dna._mushroomGrowing = true; + break; + case r__wisewomans: { // Entry into Argent. + if (_vm->_gyro->_dna._talkedToCrapulus && (! _vm->_gyro->_dna._lustieIsAsleep)) { + _vm->_gyro->_dna._spludwickAtHome = !((_vm->_gyro->_dna._roomCount[r__wisewomans] % 3) == 1); + _vm->_gyro->_dna._crapulusWillTell = ! _vm->_gyro->_dna._spludwickAtHome; + } else { + _vm->_gyro->_dna._spludwickAtHome = true; + _vm->_gyro->_dna._crapulusWillTell = false; + } + if (_vm->_gyro->_dna._boxContent == _vm->_gyro->kObjectWine) + _vm->_gyro->_dna._wineState = 3; // Vinegar + } + break; + } + + if (_vm->_gyro->_dna._room != r__outsideducks) { + if ((_vm->_gyro->_dna._objects[_vm->_gyro->kObjectOnion - 1]) && !(_vm->_gyro->_dna._onionInVinegar)) + _vm->_gyro->_dna._rottenOnion = true; // You're holding the onion + } +} + + + +void Lucerna::putGeidaAt(byte whichPed, byte &ped) { + if (ped == 0) + return; + _vm->_animation->_sprites[1].init(5, false, _vm->_animation); // load Geida + _vm->_animation->appearPed(2, whichPed); + _vm->_animation->_sprites[1]._callEachStepFl = true; + _vm->_animation->_sprites[1]._eachStepProc = _vm->_animation->kProcGeida; +} + +void Lucerna::enterRoom(byte room, byte ped) { + _vm->_gyro->_seeScroll = true; // This stops the trippancy system working over the length of this procedure. + + findPeople(room); + _vm->_gyro->_dna._room = room; + if (ped != 0) + _vm->_gyro->_dna._roomCount[room]++; + + loadRoom(room); + + if ((_vm->_gyro->_dna._roomCount[room] == 0) && (! _vm->_gyro->setFlag('S'))) + incScore(1); + + _vm->_gyro->_whereIs[_vm->_gyro->kPeopleAvalot - 150] = _vm->_gyro->_dna._room; + + if (_vm->_gyro->_dna._geidaFollows) + _vm->_gyro->_whereIs[_vm->_gyro->kPeopleGeida - 150] = room; + + _vm->_gyro->_roomTime = 0; + + + if ((_vm->_gyro->_dna._lastRoom == r__map) && (_vm->_gyro->_dna._lastRoomNotMap != _vm->_gyro->_dna._room)) + enterNewTown(); + + + switch (room) { + case r__yours: + if (_vm->_gyro->_dna._avvyInBed) { + _vm->_celer->drawBackgroundSprite(-1, -1, 3); + _vm->_graphics->refreshBackground(); + _vm->_timer->addTimer(100, _vm->_timer->kProcArkataShouts, _vm->_timer->kReasonArkataShouts); + } + break; + + case r__outsideyours: + if (ped > 0) { + if (! _vm->_gyro->_dna._talkedToCrapulus) { + + _vm->_gyro->_whereIs[_vm->_gyro->kPeopleCrapulus - 150] = r__outsideyours; + _vm->_animation->_sprites[1].init(8, false, _vm->_animation); // load Crapulus + + if (_vm->_gyro->_dna._roomCount[r__outsideyours] == 1) { + _vm->_animation->appearPed(2, 4); // Start on the right-hand side of the screen. + _vm->_animation->_sprites[1].walkTo(5); // Walks up to greet you. + } else { + _vm->_animation->appearPed(2, 5); // Starts where he was before. + _vm->_animation->_sprites[1]._facingDir = Animation::kDirLeft; + } + + _vm->_animation->_sprites[1]._callEachStepFl = true; + _vm->_animation->_sprites[1]._eachStepProc = _vm->_animation->kProcFaceAvvy; // He always faces Avvy. + + } else _vm->_gyro->_whereIs[_vm->_gyro->kPeopleCrapulus - 150] = r__nowhere; + + if (_vm->_gyro->_dna._crapulusWillTell) { + _vm->_animation->_sprites[1].init(8, false, _vm->_animation); + _vm->_animation->appearPed(2, 2); + _vm->_animation->_sprites[1].walkTo(4); + _vm->_timer->addTimer(20, _vm->_timer->kProcCrapulusSpludOut, _vm->_timer->kReasonCrapulusSaysSpludwickOut); + _vm->_gyro->_dna._crapulusWillTell = false; + } + } + break; + + case r__outsidespludwicks: + if ((_vm->_gyro->_dna._roomCount[r__outsidespludwicks] == 1) && (ped == 1)) { + _vm->_timer->addTimer(20, _vm->_timer->kProcBang, _vm->_timer->kReasonExplosion); + _vm->_gyro->_dna._spludwickAtHome = true; + } + break; + + case r__spludwicks: + if (_vm->_gyro->_dna._spludwickAtHome) { + if (ped > 0) { + _vm->_animation->_sprites[1].init(2, false, _vm->_animation); // load Spludwick + _vm->_animation->appearPed(2, 2); + _vm->_gyro->_whereIs[1] = r__spludwicks; + } + + _vm->_gyro->_dna._dogFoodPos = 0; // _vm->_gyro->also Spludwick pos. + + _vm->_animation->_sprites[1]._callEachStepFl = true; + _vm->_animation->_sprites[1]._eachStepProc = _vm->_animation->kProcGeida; + } else + _vm->_gyro->_whereIs[1] = r__nowhere; + break; + + case r__brummieroad: + if (_vm->_gyro->_dna._geidaFollows) + putGeidaAt(5, ped); + if (_vm->_gyro->_dna._cwytalotGone) { + _vm->_gyro->_magics[kColorLightred - 1]._operation = _vm->_gyro->kMagicNothing; + _vm->_gyro->_whereIs[_vm->_gyro->kPeopleCwytalot - 150] = r__nowhere; + } else { + if (ped > 0) { + _vm->_animation->_sprites[1].init(4, false, _vm->_animation); // 4 = Cwytalot + _vm->_animation->_sprites[1]._callEachStepFl = true; + _vm->_animation->_sprites[1]._eachStepProc = _vm->_animation->kProcFollowAvvyY; + _vm->_gyro->_whereIs[_vm->_gyro->kPeopleCwytalot - 150] = r__brummieroad; + + if (_vm->_gyro->_dna._roomCount[r__brummieroad] == 1) { // First time here... + _vm->_animation->appearPed(2, 2); // He appears on the right of the screen... + _vm->_animation->_sprites[1].walkTo(4); // ...and he walks up... + } else { + // You've been here before. + _vm->_animation->appearPed(2, 4); // He's standing in your way straight away... + _vm->_animation->_sprites[1]._facingDir = Animation::kDirLeft; + } + } + } + break; + + case r__argentroad: + if ((_vm->_gyro->_dna._cwytalotGone) && (! _vm->_gyro->_dna._passedCwytalotInHerts) && (ped == 2) && + (_vm->_gyro->_dna._roomCount[r__argentroad] > 3)) { + _vm->_animation->_sprites[1].init(4, false, _vm->_animation); // 4 = Cwytalot again + _vm->_animation->appearPed(2, 1); + _vm->_animation->_sprites[1].walkTo(2); + _vm->_animation->_sprites[1]._vanishIfStill = true; + _vm->_gyro->_dna._passedCwytalotInHerts = true; + // _vm->_gyro->whereis[#157] = r__Nowhere; // can we fit this in? + _vm->_timer->addTimer(20, _vm->_timer->kProcCwytalotInHerts, _vm->_timer->kReasonCwytalotInHerts); + } + break; + + case r__bridge: + if (_vm->_gyro->_dna._drawbridgeOpen == 4) { // open + _vm->_celer->drawBackgroundSprite(-1, -1, 3); // Position of drawbridge + _vm->_graphics->refreshBackground(); + _vm->_gyro->_magics[kColorGreen - 1]._operation = _vm->_gyro->kMagicNothing; // You may enter the drawbridge. + } + if (_vm->_gyro->_dna._geidaFollows) + putGeidaAt(ped + 3, ped); // load Geida + break; + + case r__robins: + if (ped > 0) { + if (! _vm->_gyro->_dna._beenTiedUp) { + // A welcome party... or maybe not... + _vm->_animation->_sprites[1].init(6, false, _vm->_animation); + _vm->_animation->appearPed(2, 2); + _vm->_animation->_sprites[1].walkTo(3); + _vm->_timer->addTimer(36, _vm->_timer->kProcGetTiedUp, _vm->_timer->kReasonGettingTiedUp); + } + } + + if (_vm->_gyro->_dna._beenTiedUp) { + _vm->_gyro->_whereIs[_vm->_gyro->kPeopleRobinHood - 150] = 0; + _vm->_gyro->_whereIs[_vm->_gyro->kPeopleFriarTuck - 150] = 0; + } + + if (_vm->_gyro->_dna._tiedUp) + _vm->_celer->drawBackgroundSprite(-1, -1, 2); + + if (!_vm->_gyro->_dna._mushroomGrowing) + _vm->_celer->drawBackgroundSprite(-1, -1, 3); + _vm->_graphics->refreshBackground(); + break; + + case r__outsidecardiffcastle: + if (ped > 0) + switch (_vm->_gyro->_dna._cardiffQuestionNum) { + case 0 : { // You've answered NONE of his questions. + _vm->_animation->_sprites[1].init(9, false, _vm->_animation); + _vm->_animation->appearPed(2, 2); + _vm->_animation->_sprites[1].walkTo(3); + _vm->_timer->addTimer(47, _vm->_timer->kProcCardiffSurvey, _vm->_timer->kReasonCardiffsurvey); + } + break; + case 5 : + _vm->_gyro->_magics[1]._operation = _vm->_gyro->kMagicNothing; + break; // You've answered ALL his questions. => nothing happens. + default: { // You've answered SOME of his questions. + _vm->_animation->_sprites[1].init(9, false, _vm->_animation); + _vm->_animation->appearPed(2, 3); + _vm->_animation->_sprites[1]._facingDir = Animation::kDirRight; + _vm->_timer->addTimer(3, _vm->_timer->kProcCardiffReturn, _vm->_timer->kReasonCardiffsurvey); + } + } + if (_vm->_gyro->_dna._cardiffQuestionNum < 5) + _vm->_gyro->_interrogation = _vm->_gyro->_dna._cardiffQuestionNum; + else _vm->_gyro->_interrogation = 0; + break; + + case r__map: + // You're entering the map. + dawn(); + //setactivepage(cp); + if (ped > 0) + zoomOut(_vm->_gyro->_peds[ped - 1]._x, _vm->_gyro->_peds[ped - 1]._y); + //setactivepage(1 - cp); + + { + if ((_vm->_gyro->_dna._objects[_vm->_gyro->kObjectWine - 1]) && (_vm->_gyro->_dna._wineState != 3)) { + _vm->_visa->displayScrollChain('q', 9); // Don't want to waste the wine! + _vm->_gyro->_dna._objects[_vm->_gyro->kObjectWine - 1] = false; + refreshObjectList(); + } + } + + _vm->_visa->displayScrollChain('q', 69); + break; + + case r__catacombs: + if ((ped == 0) || (ped == 3) || (ped == 5) || (ped == 6)) { + + switch (ped) { + case 3: { // Enter from oubliette + _vm->_gyro->_dna._catacombX = 8; + _vm->_gyro->_dna._catacombY = 4; + } + break; + case 5: { // Enter from du Lustie's + _vm->_gyro->_dna._catacombX = 8; + _vm->_gyro->_dna._catacombY = 7; + } + break; + case 6: { // Enter from Geida's + _vm->_gyro->_dna._catacombX = 4; + _vm->_gyro->_dna._catacombY = 1; + } + break; + } + + _vm->_gyro->_dna._enterCatacombsFromLustiesRoom = true; + _vm->_animation->catacombMove(ped); + _vm->_gyro->_dna._enterCatacombsFromLustiesRoom = false; + } + break; + + case r__argentpub: + if (_vm->_gyro->_dna._wonNim) + _vm->_celer->drawBackgroundSprite(-1, -1, 1); // No lute by the settle. + _vm->_gyro->_dna._malagauche = 0; // Ready to boot Malagauche + if (_vm->_gyro->_dna._givenBadgeToIby) { + _vm->_celer->drawBackgroundSprite(-1, -1, 8); + _vm->_celer->drawBackgroundSprite(-1, -1, 9); + } + _vm->_graphics->refreshBackground(); + break; + + case r__lustiesroom: + _vm->_gyro->_dna._dogFoodPos = 1; // Actually, du Lustie pos. + if (_vm->_animation->_sprites[0]._id == 0) // Avvy in his normal clothes + _vm->_timer->addTimer(3, _vm->_timer->kProcCallsGuards, _vm->_timer->kReasonDuLustieTalks); + else if (! _vm->_gyro->_dna._enteredLustiesRoomAsMonk) // already + // Presumably, Avvy dressed as a monk. + _vm->_timer->addTimer(3, _vm->_timer->kProcGreetsMonk, _vm->_timer->kReasonDuLustieTalks); + + if (_vm->_gyro->_dna._geidaFollows) { + putGeidaAt(5, ped); + if (_vm->_gyro->_dna._lustieIsAsleep) { + _vm->_celer->drawBackgroundSprite(-1, -1, 5); + _vm->_graphics->refreshBackground(); + } + } + break; + + case r__musicroom: + if (_vm->_gyro->_dna._jacquesState > 0) { + _vm->_gyro->_dna._jacquesState = 5; + _vm->_celer->drawBackgroundSprite(-1, -1, 2); + _vm->_graphics->refreshBackground(); + _vm->_celer->drawBackgroundSprite(-1, -1, 4); + _vm->_gyro->_magics[kColorBrown - 1]._operation = _vm->_gyro->kMagicNothing; + _vm->_gyro->_whereIs[_vm->_gyro->kPeopleJacques - 150] = 0; + } + if (ped != 0) { + _vm->_celer->drawBackgroundSprite(-1, -1, 6); + _vm->_graphics->refreshBackground(); + _vm->_sequence->firstShow(6); + _vm->_sequence->thenShow(5); + _vm->_sequence->thenShow(7); + _vm->_sequence->startToClose(); + } + break; + + case r__outsidenottspub: + if (ped == 2) { + _vm->_celer->drawBackgroundSprite(-1, -1, 3); + _vm->_graphics->refreshBackground(); + _vm->_sequence->firstShow(3); + _vm->_sequence->thenShow(2); + _vm->_sequence->thenShow(1); + _vm->_sequence->thenShow(4); + _vm->_sequence->startToClose(); + } + break; + + case r__outsideargentpub: + if (ped == 2) { + _vm->_celer->drawBackgroundSprite(-1, -1, 6); + _vm->_graphics->refreshBackground(); + _vm->_sequence->firstShow(6); + _vm->_sequence->thenShow(5); + _vm->_sequence->thenShow(7); + _vm->_sequence->startToClose(); + } + break; + + case r__wisewomans: + _vm->_animation->_sprites[1].init(11, false, _vm->_animation); + if ((_vm->_gyro->_dna._roomCount[r__wisewomans] == 1) && (ped > 0)) { + _vm->_animation->appearPed(2, 2); // Start on the right-hand side of the screen. + _vm->_animation->_sprites[1].walkTo(4); // Walks up to greet you. + } else { + _vm->_animation->appearPed(2, 4); // Starts where she was before. + _vm->_animation->_sprites[1]._facingDir = Animation::kDirLeft; + } + + _vm->_animation->_sprites[1]._callEachStepFl = true; + _vm->_animation->_sprites[1]._eachStepProc = _vm->_animation->kProcFaceAvvy; // She always faces Avvy. + break; + + case r__insidecardiffcastle: + if (ped > 0) { + _vm->_animation->_sprites[1].init(10, false, _vm->_animation); // Define the dart. + _vm->_celer->drawBackgroundSprite(-1, -1, 1); + _vm->_graphics->refreshBackground(); + _vm->_sequence->firstShow(1); + if (_vm->_gyro->_dna._arrowInTheDoor) + _vm->_sequence->thenShow(3); + else + _vm->_sequence->thenShow(2); + + if (_vm->_gyro->_dna._takenPen) + _vm->_celer->drawBackgroundSprite(-1, -1, 4); + + _vm->_sequence->startToClose(); + } else { + _vm->_celer->drawBackgroundSprite(-1, -1, 1); + if (_vm->_gyro->_dna._arrowInTheDoor) + _vm->_celer->drawBackgroundSprite(-1, -1, 3); + else + _vm->_celer->drawBackgroundSprite(-1, -1, 2); + _vm->_graphics->refreshBackground(); + } + break; + + case r__avvysgarden: + if (ped == 1) { + _vm->_celer->drawBackgroundSprite(-1, -1, 2); + _vm->_graphics->refreshBackground(); + _vm->_sequence->firstShow(2); + _vm->_sequence->thenShow(1); + _vm->_sequence->thenShow(3); + _vm->_sequence->startToClose(); + } + break; + + case r__entrancehall: + case r__insideabbey: + case r__yourhall: + if (ped == 2) { +#if 0 + // It was the original: + _vm->_celer->show_one(-1, -1, 2); + _vm->_sequence->first_show(1); + _vm->_sequence->then_show(3); + _vm->_sequence->start_to_close(); +#endif + + _vm->_celer->drawBackgroundSprite(-1, -1, 2); + _vm->_graphics->refreshBackground(); + _vm->_sequence->firstShow(2); + _vm->_sequence->thenShow(1); + _vm->_sequence->thenShow(3); + _vm->_sequence->startToClose(); + } + break; + + case r__aylesoffice: + if (_vm->_gyro->_dna._aylesIsAwake) + _vm->_celer->drawBackgroundSprite(-1, -1, 2); + _vm->_graphics->refreshBackground(); + break; // Ayles awake. + + case r__geidas: + putGeidaAt(2, ped); + break; // load Geida + + case r__easthall: + case r__westhall: + if (_vm->_gyro->_dna._geidaFollows) + putGeidaAt(ped + 2, ped); + break; + + case r__lusties: + if (_vm->_gyro->_dna._geidaFollows) + putGeidaAt(ped + 6, ped); + break; + + case r__nottspub: + if (_vm->_gyro->_dna._sittingInPub) _vm->_celer->drawBackgroundSprite(-1, -1, 3); + _vm->_gyro->_dna._dogFoodPos = 1; // Actually, du Lustie pos. + break; + + case r__outsideducks: + if (ped == 2) { + // Shut the door + _vm->_celer->drawBackgroundSprite(-1, -1, 3); + _vm->_graphics->refreshBackground(); + _vm->_sequence->firstShow(3); + _vm->_sequence->firstShow(2); + _vm->_sequence->thenShow(1); + _vm->_sequence->thenShow(4); + _vm->_sequence->startToClose(); + } + break; + + case r__ducks: + _vm->_gyro->_dna._dogFoodPos = 1; + break; // Actually, Duck pos. + } + + _vm->_gyro->_seeScroll = false; // Now it can work again! + + if (_vm->_gyro->isLoaded) + _vm->_gyro->isLoaded = false; +} + +void Lucerna::thinkAbout(byte object, bool type) { + const int16 picSize = 966; + + _vm->_gyro->_thinks = object; + object--; + + _vm->_gyro->setMousePointerWait(); + + if (type == Gyro::kThing) { + if (!file.open("thinks.avd")) { + warning("AVALANCHE: Lucerna: File not found: thinks.avd"); + return; + } + } else { // Gyro::kPerson + if (!file.open("folk.avd")) { + warning("AVALANCHE: Lucerna: File not found: folk.avd"); + return; + } + + object -= 149; + if (object >= 25) + object -= 8; + if (object == 20) + object--; // Last time... + } + + file.seek(object * picSize + 65); + + ::Graphics::Surface picture = _vm->_graphics->loadPictureGraphic(file); + + _vm->_graphics->drawPicture(_vm->_graphics->_surface, picture, 205, 170); + + picture.free(); + + file.close(); + + CursorMan.showMouse(false); + +#if 0 + setactivepage(3); + putimage(x, y, p, 0); + setactivepage(1 - cp); +#endif + + CursorMan.showMouse(true); + _vm->_gyro->_thinkThing = type; +} + +void Lucerna::loadDigits() { // Load the scoring digits & rwlites + const byte digitsize = 134; + const byte rwlitesize = 126; + + if (!file.open("digit.avd")) { + warning("AVALANCHE: Lucerna: File not found: digit.avd"); + return; + } + + for (byte i = 0; i < 10; i++) { + file.seek(i * digitsize); + _vm->_gyro->_digits[i] = _vm->_graphics->loadPictureGraphic(file); + } + + for (byte i = 0; i < 9; i++) { + file.seek(10 * digitsize + i * rwlitesize); + _vm->_gyro->_directions[i] = _vm->_graphics->loadPictureGraphic(file); + } + + file.close(); +} + +void Lucerna::drawToolbar() { + if (!file.open("useful.avd")) { + warning("AVALANCHE: Lucerna: File not found: useful.avd"); + return; + } + + file.seek(40); + + CursorMan.showMouse(false); + + ::Graphics::Surface picture = _vm->_graphics->loadPictureGraphic(file); + + _vm->_graphics->drawPicture(_vm->_graphics->_surface, picture, 5, 169); + + picture.free(); + + file.close(); + + CursorMan.showMouse(true); + + _vm->_gyro->_oldDirection = 177; + drawDirection(); +} + +void Lucerna::drawScore() { + if (_vm->_gyro->kDemo) + return; + + uint16 score = _vm->_gyro->_dna._score; + int8 numbers[3] = {0, 0, 0}; + for (byte i = 0; i < 2; i++) { + byte divisor = 1; + for (byte j = 0; j < (2 - i); j++) + divisor *= 10; + numbers[i] = score / divisor; + score -= numbers[i] * divisor; + } + numbers[2] = score; + + CursorMan.showMouse(false); + + for (byte fv = 0; fv < 3; fv++) { + if (_vm->_gyro->_scoreToDisplay[fv] != numbers[fv]) + _vm->_graphics->drawPicture(_vm->_graphics->_surface, _vm->_gyro->_digits[numbers[fv]], 250 + (fv + 1) * 15, 177); + } + + CursorMan.showMouse(true); + + for (byte i = 0; i < 3; i++) + _vm->_gyro->_scoreToDisplay[i] = numbers[i]; +} + +void Lucerna::incScore(byte num) { // Add on no. of points + for (byte q = 1; q <= num; q++) { + _vm->_gyro->_dna._score++; + +#if 0 + if (soundfx) { + for (byte fv = 1; fv <= 97; fv++) + sound(177 + dna.score * 3); + } + nosound; +#endif + } + warning("STUB: Lucerna::points()"); + + drawScore(); +} + +void Lucerna::useCompass(const Common::Point &cursorPos) { + byte color = *(byte *)_vm->_graphics->_surface.getBasePtr(cursorPos.x, cursorPos.y / 2); + + switch (color) { + case kColorGreen: + _vm->_gyro->_dna._direction = Animation::kDirUp; + _vm->_animation->changeDirection(0, Animation::kDirUp); + drawDirection(); + break; + case kColorBrown: + _vm->_gyro->_dna._direction = Animation::kDirDown; + _vm->_animation->changeDirection(0, Animation::kDirDown); + drawDirection(); + break; + case kColorCyan: + _vm->_gyro->_dna._direction = Animation::kDirLeft; + _vm->_animation->changeDirection(0, Animation::kDirLeft); + drawDirection(); + break; + case kColorLightmagenta: + _vm->_gyro->_dna._direction = Animation::kDirRight; + _vm->_animation->changeDirection(0, Animation::kDirRight); + drawDirection(); + break; + case kColorRed: + case kColorWhite: + case kColorLightcyan: + case kColorYellow: // Fall-throughs are intended. + _vm->_animation->stopWalking(); + drawDirection(); + break; + } +} + +void Lucerna::fxToggle() { + warning("STUB: Lucerna::fxtoggle()"); +} + +void Lucerna::refreshObjectList() { + _vm->_gyro->_dna._carryNum = 0; + if (_vm->_gyro->_thinkThing && !_vm->_gyro->_dna._objects[_vm->_gyro->_thinks - 1]) + thinkAbout(_vm->_gyro->kObjectMoney, Gyro::kThing); // you always have money + + for (byte i = 0; i < kObjectNum; i++) { + if (_vm->_gyro->_dna._objects[i]) { + _vm->_gyro->_dna._carryNum++; + _vm->_gyro->_objectList[_vm->_gyro->_dna._carryNum] = i + 1; + } + } +} + +/** + * @remarks Originally called 'verte' + */ +void Lucerna::guideAvvy(Common::Point cursorPos) { + if (!_vm->_gyro->_dna._userMovesAvvy) + return; + + cursorPos.y /= 2; + byte what; + + // _vm->_animation->tr[0] is Avalot.) + if (cursorPos.x < _vm->_animation->_sprites[0]._x) + what = 1; + else if (cursorPos.x > (_vm->_animation->_sprites[0]._x + _vm->_animation->_sprites[0]._info._xLength)) + what = 2; + else + what = 0; // On top + + if (cursorPos.y < _vm->_animation->_sprites[0]._y) + what += 3; + else if (cursorPos.y > (_vm->_animation->_sprites[0]._y + _vm->_animation->_sprites[0]._info._yLength)) + what += 6; + + switch (what) { + case 0: + _vm->_animation->stopWalking(); + break; // Clicked on Avvy: no movement. + case 1: + _vm->_animation->changeDirection(0, Animation::kDirLeft); + break; + case 2: + _vm->_animation->changeDirection(0, Animation::kDirRight); + break; + case 3: + _vm->_animation->changeDirection(0, Animation::kDirUp); + break; + case 4: + _vm->_animation->changeDirection(0, Animation::kDirUpLeft); + break; + case 5: + _vm->_animation->changeDirection(0, Animation::kDirUpRight); + break; + case 6: + _vm->_animation->changeDirection(0, Animation::kDirDown); + break; + case 7: + _vm->_animation->changeDirection(0, Animation::kDirDownLeft); + break; + case 8: + _vm->_animation->changeDirection(0, Animation::kDirDownRight); + break; + } // No other values are possible. + + drawDirection(); +} + +void Lucerna::checkClick() { + Common::Point cursorPos = _vm->getMousePos(); + _vm->_gyro->_onToolbar = _vm->_gyro->kSlowComputer && ((cursorPos.y >= 169) || (cursorPos.y <= 10)); + + /*if (_vm->_gyro->mrelease > 0) + _vm->_gyro->after_the_scroll = false;*/ + + if ((0 <= cursorPos.y) && (cursorPos.y <= 21)) + _vm->_gyro->newMouse(1); // up arrow + else if ((317 <= cursorPos.y) && (cursorPos.y <= 339)) + _vm->_gyro->newMouse(8); //I-beam + else if ((340 <= cursorPos.y) && (cursorPos.y <= 399)) + _vm->_gyro->newMouse(2); // screwdriver + else if (!_vm->_gyro->_dropdownActive) { // Dropdown can handle its own pointers. + if (_holdLeftMouse) { + _vm->_gyro->newMouse(7); // Mark's crosshairs + guideAvvy(cursorPos); // Normally, if you click on the picture, you're guiding Avvy around. + } else + _vm->_gyro->newMouse(4); // fletch + } + + if (_holdLeftMouse) { + if ((0 <= cursorPos.y) && (cursorPos.y <= 21)) { // Click on the dropdown menu. + if (_vm->_gyro->_dropsOk) + _vm->_dropdown->updateMenu(); + } else if ((317 <= cursorPos.y) && (cursorPos.y <= 339)) { // Click on the command line. + _vm->_parser->_inputTextPos = (cursorPos.x - 23) / 8; + if (_vm->_parser->_inputTextPos > _vm->_parser->_inputText.size() + 1) + _vm->_parser->_inputTextPos = _vm->_parser->_inputText.size() + 1; + if (_vm->_parser->_inputTextPos < 1) + _vm->_parser->_inputTextPos = 1; + _vm->_parser->_inputTextPos--; + _vm->_parser->plotText(); + } else if ((340 <= cursorPos.y) && (cursorPos.y <= 399)) { // Check the toolbar. + if ((137 <= cursorPos.x) && (cursorPos.x <= 207)) { // Control Avvy with the compass. + if (_vm->_gyro->_alive && _vm->_gyro->_dna._avvyIsAwake) + useCompass(cursorPos); + } else if ((208 <= cursorPos.x) && (cursorPos.x <= 260)) { // Examine the _thing. + do { + _vm->updateEvents(); + } while (_holdLeftMouse); + + if (_vm->_gyro->_thinkThing) { + _vm->_acci->_thing = _vm->_gyro->_thinks; + _vm->_acci->_thing += 49; + _vm->_acci->_person = _vm->_acci->kPardon; + } else { + _vm->_acci->_person = _vm->_gyro->_thinks; + _vm->_acci->_thing = _vm->_acci->kPardon; + } + callVerb(_vm->_acci->kVerbCodeExam); + } else if ((261 <= cursorPos.x) && (cursorPos.x <= 319)) { // Display the score. + do { + _vm->updateEvents(); + } while (_holdLeftMouse); + + callVerb(_vm->_acci->kVerbCodeScore); + } else if ((320 <= cursorPos.x) && (cursorPos.x <= 357)) { // Change speed. + _vm->_animation->_sprites[0]._speedX = _vm->_gyro->kWalk; + _vm->_animation->updateSpeed(); + } else if ((358 <= cursorPos.x) && (cursorPos.x <= 395)) { // Change speed. + _vm->_animation->_sprites[0]._speedX = _vm->_gyro->kRun; + _vm->_animation->updateSpeed(); + } else if ((396 <= cursorPos.x) && (cursorPos.x <= 483)) + fxToggle(); + else if ((535 <= cursorPos.x) && (cursorPos.x <= 640)) + _vm->_gyro->_mouseText = Common::String(13) + _vm->_gyro->_mouseText; + } else if (!_vm->_gyro->_dropsOk) + _vm->_gyro->_mouseText = Common::String(13) + _vm->_gyro->_mouseText; + } +} + +void Lucerna::errorLed() { + warning("STUB: Lucerna::errorled()"); +} + +int8 Lucerna::fades(int8 x) { + warning("STUB: Lucerna::fades()"); + return 0; +} + +void Lucerna::fadeOut(byte n) { + warning("STUB: Lucerna::fadeOut()"); +} + +void Lucerna::dusk() { + warning("STUB: Lucerna::dusk()"); +} + +void Lucerna::fadeIn(byte n) { + warning("STUB: Lucerna::fadeIn()"); +} + +void Lucerna::dawn() { + warning("STUB: Lucerna::dawn()"); +} + +void Lucerna::drawDirection() { // It's data is loaded in load_digits(). + if (_vm->_gyro->_oldDirection == _vm->_gyro->_dna._direction) + return; + + _vm->_gyro->_oldDirection = _vm->_gyro->_dna._direction; + + CursorMan.showMouse(false); + _vm->_graphics->drawPicture(_vm->_graphics->_surface, _vm->_gyro->_directions[_vm->_gyro->_dna._direction], 0, 161); + CursorMan.showMouse(true); +} + + +void Lucerna::gameOver() { + _vm->_gyro->_dna._userMovesAvvy = false; + + int16 sx = _vm->_animation->_sprites[0]._x; + int16 sy = _vm->_animation->_sprites[0]._y; + + _vm->_animation->_sprites[0].remove(); + _vm->_animation->_sprites[0].init(12, true, _vm->_animation); // 12 = Avalot falls + _vm->_animation->_sprites[0]._stepNum = 0; + _vm->_animation->_sprites[0].appear(sx, sy, 0); + + _vm->_timer->addTimer(3, _vm->_timer->kProcAvalotFalls, _vm->_timer->kReasonFallingOver); + _vm->_gyro->_alive = false; +} + +void Lucerna::minorRedraw() { + dusk(); + + enterRoom(_vm->_gyro->_dna._room, 0); // Ped unknown or non-existant. + + for (byte i = 0; i < 3; i++) + _vm->_gyro->_scoreToDisplay[i] = -1; // impossible digits + drawScore(); + + dawn(); +} + +void Lucerna::majorRedraw() { + warning("STUB: Lucerna::major_redraw()"); +} + +uint16 Lucerna::bearing(byte whichPed) { + byte pedId = whichPed - 1; // Different array indexes in Pascal and C. + + const double rad2deg = 180 / 3.14; // Pi + + if (_vm->_animation->_sprites[0]._x == _vm->_gyro->_peds[pedId]._x) + return 0; + else if (_vm->_animation->_sprites[0]._x < _vm->_gyro->_peds[pedId]._x) { + return (uint16)((atan(double((_vm->_animation->_sprites[0]._y - _vm->_gyro->_peds[pedId]._y)) + / (_vm->_animation->_sprites[0]._x - _vm->_gyro->_peds[pedId]._x)) * rad2deg) + 90); + } else { + return (uint16)((atan(double((_vm->_animation->_sprites[0]._y - _vm->_gyro->_peds[pedId]._y)) + / (_vm->_animation->_sprites[0]._x - _vm->_gyro->_peds[pedId]._x)) * rad2deg) + 270); + } +} + +/** + * A sprite run is performed before displaying a scroll, if not all the + * sprites are still. It performs two fast cycles, only using a few of + * the links usually used, and without any extra animation. This should + * make the sprites the same on both pages. + * @remarks Originally called 'sprite_run' + */ +void Lucerna::spriteRun() { + _vm->_gyro->_doingSpriteRun = true; + _vm->_animation->animLink(); + _vm->_gyro->_doingSpriteRun = false; +} + +void Lucerna::fixFlashers() { + _vm->_gyro->_ledStatus = 177; + _vm->_gyro->_oldDirection = 177; + _vm->_scrolls->setReadyLight(2); + drawDirection(); +} + +} // End of namespace Avalanche diff --git a/engines/avalanche/lucerna2.h b/engines/avalanche/lucerna2.h new file mode 100644 index 0000000000..9c1cc6ce44 --- /dev/null +++ b/engines/avalanche/lucerna2.h @@ -0,0 +1,124 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on the original source code of Lord Avalot d'Argent version 1.3. + * Copyright (c) 1994-1995 Mike, Mark and Thomas Thurman. + */ + +/* LUCERNA The screen, [keyboard] and mouse handler.*/ + +#ifndef AVALANCHE_LUCERNA2_H +#define AVALANCHE_LUCERNA2_H + +#include "common/scummsys.h" +#include "common/file.h" + +namespace Avalanche { +class AvalancheEngine; + +class Clock { +public: + Clock(AvalancheEngine *vm); + + void update(); + +private: + static const int kCenterX = 510; + static const int kCenterY = 183; + + AvalancheEngine *_vm; + + uint16 _hour, _minute, _second, _hourAngle, _oldHour, _oldMinute, _oldHourAngle; + Common::Point _clockHandHour, _clockHandMinute; + + void calcHand(uint16 angle, uint16 length, Common::Point &endPoint, byte color); + void drawHand(const Common::Point &endPoint, byte color); + void plotHands(); + void chime(); +}; + +class Lucerna { +public: + bool _holdLeftMouse; + Clock _clock; + + Lucerna(AvalancheEngine *vm); + ~Lucerna(); + + void init(); + void callVerb(byte id); + void drawAlsoLines(); + void loadRoom(byte num); + void exitRoom(byte x); + void enterRoom(byte room, byte ped); + void thinkAbout(byte object, bool type); // Hey!!! Get it and put it!!! + void loadDigits(); // Load the scoring digits & rwlites + void drawToolbar(); + void drawScore(); + void incScore(byte num); // Add on no. of points + void useCompass(const Common::Point &cursorPos); // Click on the compass on the toolbar to control Avvy's movement. + void fxToggle(); + void refreshObjectList(); + void checkClick(); + void errorLed(); + void dusk(); + void dawn(); + void drawDirection(); // Draws the little icon at the left end of the text input field. + void gameOver(); + uint16 bearing(byte whichPed); // Returns the bearing from ped 'whichped' to Avvy, in degrees. + void fixFlashers(); + void loadAlso(byte num); + + // There are two kinds of redraw: Major and Minor. Minor is what happens when you load a game, etc. Major redraws EVERYTHING. + void minorRedraw(); + void majorRedraw(); + + void spriteRun(); + +private: + AvalancheEngine *_vm; + + Common::File file; + + Common::String readAlsoStringFromFile(); + void scram(Common::String &str); + void unScramble(); + + void zoomOut(int16 x, int16 y); // Only used when entering the map. + void enterNewTown(); + void findPeople(byte room); + void putGeidaAt(byte whichPed, byte &ped); + void guideAvvy(Common::Point cursorPos); + + // Will be used in dusk() and dawn(). + bool _fxHidden; + + int8 fades(int8 x); + void fadeOut(byte n); + void fadeIn(byte n); + +}; + +} // End of namespace Avalanche + +#endif // AVALANCHE_LUCERNA2_H diff --git a/engines/avalanche/module.mk b/engines/avalanche/module.mk new file mode 100644 index 0000000000..0f1722a482 --- /dev/null +++ b/engines/avalanche/module.mk @@ -0,0 +1,30 @@ +MODULE := engines/avalanche + +MODULE_OBJS = \ + avalanche.o \ + graphics.o \ + parser.o \ + avalot.o \ + console.o \ + detection.o \ + gyro2.o \ + pingo2.o \ + scrolls2.o \ + visa2.o \ + lucerna2.o \ + enid2.o \ + celer2.o \ + sequence2.o \ + timer.o \ + animation.o \ + acci2.o \ + dropdown2.o \ + closing2.o + +# This module can be built as a plugin +ifeq ($(ENABLE_AVALANCHE), DYNAMIC_PLUGIN) +PLUGIN := 1 +endif + +# Include common rules +include $(srcdir)/rules.mk diff --git a/engines/avalanche/parser.cpp b/engines/avalanche/parser.cpp new file mode 100644 index 0000000000..a378b0d54b --- /dev/null +++ b/engines/avalanche/parser.cpp @@ -0,0 +1,173 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on the original source code of Lord Avalot d'Argent version 1.3. + * Copyright (c) 1994-1995 Mike, Mark and Thomas Thurman. + */ + +#include "avalanche/avalanche.h" +#include "avalanche/parser.h" + +namespace Avalanche { + +Parser::Parser(AvalancheEngine *vm) { + _vm = vm; +} + +void Parser::init() { + _leftMargin = 0; + if (!_inputText.empty()) + _inputText.clear(); + _inputTextPos = 0; +} + +void Parser::handleInputText(const Common::Event &event) { + byte inChar = event.kbd.ascii; + warning("STUB: Parser::handleInputText()"); +// if (_vm->_dropdown->_activeMenuItem._activeNow) { +// _vm->_dropdown->parseKey(inChar, _vm->_enhanced->extd); +// } else { + if (_inputText.size() < 76) { + if ((inChar == '"') || (inChar == '`')) { + if (_quote) + inChar = '`'; + else + inChar = '"'; + _quote = !_quote; // quote - unquote + } + + _inputText.insertChar(inChar, _inputTextPos); + _inputTextPos++; + plotText(); + } else + _vm->_gyro->blip(); +// } +} + +void Parser::handleBackspace() { + if (!_vm->_dropdown->_activeMenuItem._activeNow) { + if (_inputTextPos > _leftMargin) { + _inputTextPos--; + if ((_inputText[_inputTextPos] == '"') || (_inputText[_inputTextPos] == '`')) + _quote = !_quote; + _inputText.deleteChar(_inputTextPos); + plotText(); + } else + _vm->_gyro->blip(); + } +} + +void Parser::handleReturn() { + if (_vm->_dropdown->_activeMenuItem._activeNow) + _vm->_parser->tryDropdown(); + else { + if (!_inputText.empty()) { + _inputTextBackup = _inputText; + _vm->_acci->parse(); + _vm->_acci->doThat(); + _inputText.clear(); + wipeText(); + } + } +} + +void Parser::handleFunctionKey(const Common::Event &event) { + switch (event.kbd.keycode) { + case Common::KEYCODE_F5: { + _vm->_acci->_person = _vm->_acci->kPardon; + _vm->_acci->_thing = _vm->_acci->kPardon; + _vm->_lucerna->callVerb(_vm->_gyro->f5Does()[0]); + } + break; + case Common::KEYCODE_F7: + _vm->_lucerna->callVerb(_vm->_acci->kVerbCodeOpen); + break; + default: + break; + } +} + +void Parser::plotText() { + CursorMan.showMouse(false); + + cursorOff(); + + _vm->_graphics->_surface.fillRect(Common::Rect(24, 161, 640, 169), kColorBlack); // Black out the line of the text. + + _vm->_graphics->drawText(_vm->_graphics->_surface, _vm->_parser->_inputText, _vm->_gyro->_font, 8, 24, 161, kColorWhite); + + cursorOn(); + CursorMan.showMouse(true); +} + +void Parser::cursorOn() { + if (_cursorState == true) + return; + drawCursor(); + _cursorState = true; +} + +void Parser::cursorOff() { + if (_cursorState == false) + return; + drawCursor(); + _cursorState = false; +} + +void Parser::tryDropdown() { + warning("STUB: Parser::tryDropdown()"); // TODO: Implement at the same time with Dropdown's keyboard handling. +} + +int16 Parser::pos(const Common::String &crit, const Common::String &src) { + if (src.contains(crit)) + return strstr(src.c_str(),crit.c_str()) - src.c_str(); + else + return -1; +} + +void Parser::drawCursor() { + // Draw the '_' character. + for (byte bit = 0; bit < 8; bit++) + *(byte *)_vm->_graphics->_surface.getBasePtr(24 + _inputTextPos * 8 + 7 - bit, 168) = kColorWhite; + + ByteField bf; + bf._x1 = _inputTextPos + 1; + bf._x2 = _inputTextPos + 2; + bf._y1 = 168; + bf._y2 = 168; +} + +void Parser::wipeText() { + CursorMan.showMouse(false); + cursorOff(); + + _vm->_graphics->_surface.fillRect(Common::Rect(24, 161, 640, 169), kColorBlack); // Black out the line of the text. + + _quote = true; + _inputTextPos = 0; + + cursorOn(); + CursorMan.showMouse(true); +} + +} // End of namespace Avalanche diff --git a/engines/avalanche/parser.h b/engines/avalanche/parser.h new file mode 100644 index 0000000000..b6ee0466ae --- /dev/null +++ b/engines/avalanche/parser.h @@ -0,0 +1,68 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on the original source code of Lord Avalot d'Argent version 1.3. + * Copyright (c) 1994-1995 Mike, Mark and Thomas Thurman. + */ + +#ifndef AVALANCHE_PARSER_H +#define AVALANCHE_PARSER_H + +#include "common/events.h" + +namespace Avalanche { +class AvalancheEngine; + +class Parser { +public: + Common::String _inputText; // Original name: current + Common::String _inputTextBackup; + byte _inputTextPos; // Original name: curpos + bool _quote; // 66 or 99 next? + byte _leftMargin; + bool _cursorState; + + Parser(AvalancheEngine *vm); + + void init(); + void handleInputText(const Common::Event &event); + void handleBackspace(); + void handleReturn(); + void handleFunctionKey(const Common::Event &event); + void plotText(); + void cursorOn(); + void cursorOff(); + void tryDropdown(); // This asks the parsekey proc in Dropdown if it knows it. + int16 pos(const Common::String &crit, const Common::String &src); // Returns the index of the first appearance of crit in src. + +private: + AvalancheEngine *_vm; + + void drawCursor(); + void wipeText(); + +}; + +} // End of namespace Avalanche + +#endif // AVALANCHE_PARSER_H diff --git a/engines/avalanche/pingo2.cpp b/engines/avalanche/pingo2.cpp new file mode 100644 index 0000000000..3ebb47ed98 --- /dev/null +++ b/engines/avalanche/pingo2.cpp @@ -0,0 +1,152 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on the original source code of Lord Avalot d'Argent version 1.3. + * Copyright (c) 1994-1995 Mike, Mark and Thomas Thurman. + */ + +/* PINGO Full-screen sub-parts of the game. */ + +#include "avalanche/avalanche.h" + +#include "avalanche/pingo2.h" +#include "avalanche/gyro2.h" +#include "avalanche/lucerna2.h" +#include "avalanche/animation.h" +#include "avalanche/scrolls2.h" + +#include "common/textconsole.h" +#include "common/file.h" + +namespace Avalanche { + +Pingo::Pingo(AvalancheEngine *vm) { + _vm = vm; +} + +void Pingo::dPlot(int16 x, int16 y, Common::String z) { + warning("STUB: Pingo::dPlot()"); +} + +void Pingo::bossKey() { +#if 0 +const + months : array[0..11] of char = 'JFMAMJJASOND'; + title = 'Net Profits'; + fish = 224; // 'à' +var fv:byte; gd,gm:int16; r:char; +begin; + dusk; delavvy; + setactivepage(3); mousepage(3); setvisualpage(3); off; + cleardevice; setfillstyle(xhatchfill,11); + settextstyle(1,0,4); settextjustify(1,1); + dplot(320,10,title); + settextstyle(1,0,0); setusercharsize(4,3,7,12); + for fv:=0 to 11 do + begin; + dplot(26+fv*52,187,months[fv]); + bar(fv*52,177-fv*14,51+fv*52,180); + rectangle(fv*52,177-fv*14,51+fv*52,180); + end; + settextstyle(0,0,1); + for fv:=1 to 177 do + begin; + gd:=random(630); gm:=random(160)+30; + setcolor(lightred); outtextxy(gd ,gm ,fish); + setcolor(yellow); outtextxy(gd+1,gm-1,fish); + end; + newpointer(6); { TTHand } + dawn; on; setbkcolor(1); repeat check until (mpress>0) or keypressed; + while keypressed do r:=readkey; setbkcolor(0); settextjustify(0,0); + dusk; setvisualpage(0); setactivepage(0); mousepage(0); dawn; + copy02; +#endif + + warning("STUB: Pingo::bossKey()"); +} + +void Pingo::copy02() { // taken from Wobble (below) + warning("STUB: Pingo::copy02()"); +} + +void Pingo::copy03() { // taken from Wobble (below) + warning("STUB: Pingo::copy03()"); +} + +void Pingo::copyPage(byte frp, byte top) { // taken from Copy02 (above) + warning("STUB: Pingo::copyPage()"); +} + +void Pingo::wobble() { + warning("STUB: Pingo::wobble()"); +} + +void Pingo::zl(int16 x1, int16 y1, int16 x2, int16 y2) { + warning("STUB: Pingo::zl()"); +} + +void Pingo::zonk() { + warning("STUB: Pingo::zonk()"); +} + +void Pingo::winningPic() { + Common::File f; + _vm->_lucerna->dusk(); + + if (!f.open("finale.avd")) { + warning("AVALANCHE: Lucerna: File not found: finale.avd"); + return; + } + +#if 0 + for (byte bit = 0; bit <= 3; bit++) { + port[0x3c4] = 2; + port[0x3ce] = 4; + port[0x3c5] = 1 << bit; + port[0x3cf] = bit; + blockread(f, mem[0xa000 * 0], 16000); + } +#endif + + warning("STUB: Pingo::winningPic()"); + + f.close(); + + //setvisualpage(0); + warning("STUB: Pingo::winningPic()"); + + _vm->_lucerna->dawn(); + +#if 0 + do { + _vm->_gyro->check(); + } while (!(keypressed() || (mrelease > 0))); + while (keypressed()) + char r = readkey(); + major_redraw(); +#endif + + warning("STUB: Pingo::winningPic()"); +} + +} // End of namespace Avalanche. diff --git a/engines/avalanche/pingo2.h b/engines/avalanche/pingo2.h new file mode 100644 index 0000000000..ee4fb587e7 --- /dev/null +++ b/engines/avalanche/pingo2.h @@ -0,0 +1,60 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on the original source code of Lord Avalot d'Argent version 1.3. + * Copyright (c) 1994-1995 Mike, Mark and Thomas Thurman. + */ + +/* PINGO Full-screen sub-parts of the game. */ + +#ifndef AVALANCHE_PINGO2_H +#define AVALANCHE_PINGO2_H + +#include "common/scummsys.h" +#include "common/str.h" + +namespace Avalanche { +class AvalancheEngine; + +class Pingo { +public: + Pingo(AvalancheEngine *vm); + + void bossKey(); + void copy02(); + void copy03(); + void copyPage(byte frp, byte top); + void wobble(); + void zonk(); + void winningPic(); + +private: + AvalancheEngine *_vm; + + void dPlot(int16 x, int16 y, Common::String z); + void zl(int16 x1, int16 y1, int16 x2, int16 y2); +}; + +} // End of namespace Avalanche. + +#endif // AVALANCHE_PINGO2_H diff --git a/engines/avalanche/roomnums.h b/engines/avalanche/roomnums.h new file mode 100644 index 0000000000..e623c27a20 --- /dev/null +++ b/engines/avalanche/roomnums.h @@ -0,0 +1,88 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on the original source code of Lord Avalot d'Argent version 1.3. + * Copyright (c) 1994-1995 Mike, Mark and Thomas Thurman. + */ + +#ifndef AVALANCHE_ROOMNUMS_H +#define AVALANCHE_ROOMNUMS_H + +#include "common/system.h" + +namespace Avalanche { + +const byte r__nowhere = 0; +const byte r__yours = 1; +const byte r__outsideyours = 2; +const byte r__outsidespludwicks = 3; +const byte r__yourhall = 5; +const byte r__musicroom = 7; +const byte r__outsideargentpub = 9; +const byte r__argentroad = 10; +const byte r__wisewomans = 11; +const byte r__spludwicks = 12; +const byte r__insideabbey = 13; +const byte r__outsideabbey = 14; // assumed +const byte r__avvysgarden = 15; +const byte r__aylesoffice = 16; +const byte r__argentpub = 19; +const byte r__brummieroad = 20; +const byte r__bridge = 21; // ? not sure +const byte r__lusties = 22; +const byte r__lustiesroom = 23; +const byte r__westhall = 25; +const byte r__easthall = 26; +const byte r__oubliette = 27; +const byte r__geidas = 28; +const byte r__catacombs = 29; + +//{ -------------- } + +const byte r__entrancehall = 40; +const byte r__robins = 42; +const byte r__outsidenottspub = 46; +const byte r__nottspub = 47; + +//{ -------------- } + +const byte r__outsideducks = 50; +const byte r__ducks = 51; + +//{ -------------- } + +const byte r__outsidecardiffcastle = 70; +const byte r__insidecardiffcastle = 71; + +//{ -------------- } + +// place80 appears to be bogus + +//{ -------------- } + +const byte r__bosskey = 98; // assumed +const byte r__map = 99; + +} // End of namespace Avalanche + +#endif // AVALANCHE_ROOMNUMS_H diff --git a/engines/avalanche/scrolls2.cpp b/engines/avalanche/scrolls2.cpp new file mode 100644 index 0000000000..30e5bc4d26 --- /dev/null +++ b/engines/avalanche/scrolls2.cpp @@ -0,0 +1,827 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on the original source code of Lord Avalot d'Argent version 1.3. + * Copyright (c) 1994-1995 Mike, Mark and Thomas Thurman. + */ + + /* SCROLLS The scroll driver. */ + +#include "avalanche/avalanche.h" + +#include "avalanche/scrolls2.h" +#include "avalanche/gyro2.h" +#include "avalanche/lucerna2.h" +#include "avalanche/animation.h" +#include "avalanche/acci2.h" +#include "avalanche/visa2.h" +#include "avalanche/timer.h" + +#include "common/textconsole.h" +#include "common/file.h" + +namespace Avalanche { + +Scrolls::Scrolls(AvalancheEngine *vm) { + _vm = vm; +} + +void Scrolls::init() { + loadFont(); + resetScrollDriver(); +} + +void Scrolls::setReadyLight(byte state) { // Sets "Ready" light to whatever + if (_vm->_gyro->_ledStatus == state) + return; // Already like that! + + byte color = kColorBlack; + switch (state) { + case 0: + color = kColorBlack; + break; // Off + case 1: + case 2: + case 3: + color = kColorGreen; + break; // Hit a key + } + warning("STUB: Scrolls::state()"); + + CursorMan.showMouse(false); + + _vm->_graphics->_surface.fillRect(Common::Rect(419, 195, 438, 197), color); + + CursorMan.showMouse(true); + _vm->_gyro->_ledStatus = state; +} + +void Scrolls::easterEgg() { + warning("STUB: Scrolls::easterEgg()"); +} + +void Scrolls::say(int16 x, int16 y, Common::String z) { + FontType itw; + byte lz = z.size(); + + bool offset = x % 8 == 4; + x = x / 8; + y++; + int16 i = 0; + for (byte xx = 0; xx < lz; xx++) { + switch (z[xx]) { + case kControlRoman: { + _currentFont = kFontStyleRoman; + } + break; + case kControlItalic: { + _currentFont = kFontStyleItalic; + } + break; + default: { + for (byte yy = 0; yy < 12; yy++) + itw[(byte)z[xx]][yy] = _scrollFonts[_currentFont][(byte)z[xx]][yy + 2]; + + // We have to draw the characters one-by-one because of the accidental font changes. + i++; + Common::String chr(z[xx]); + _vm->_graphics->drawText(_vm->_graphics->_scrolls, chr, itw, 12, (x - 1) * 8 + offset * 4 + i * 8, y, kColorBlack); + } + } + } +} + +void Scrolls::scrollModeNormal() { + Common::String egg = Common::String(kControlParagraph) + kControlLeftJustified + kControlNegative + kControlBell + kControlBackspace + "***"; + Common::String e = "(c) 1994"; + + setReadyLight(3); + _vm->_gyro->_seeScroll = true; + CursorMan.showMouse(true); + _vm->_gyro->newMouse(4); + + ::Graphics::Surface temp; + temp.copyFrom(_vm->_graphics->_surface); + _vm->_graphics->_surface.copyFrom(_vm->_graphics->_scrolls); // TODO: Rework it using getSubArea !!!!!!! + + Common::Event event; + while (!_vm->shouldQuit()) { + _vm->_graphics->refreshScreen(); + + _vm->getEvent(event); + if ((event.type == Common::EVENT_LBUTTONUP) || + ((event.type == Common::EVENT_KEYDOWN) && ((event.kbd.keycode == Common::KEYCODE_ESCAPE) || (event.kbd.keycode == Common::KEYCODE_RETURN) || (event.kbd.keycode == Common::KEYCODE_HASH) || (event.kbd.keycode == Common::KEYCODE_PLUS)))) + break; + } + + _vm->_graphics->_surface.copyFrom(temp); + temp.free(); + +#if 0 + char r; + bool oktoexit; + do { + do { + _vm->_gyro->check(); // was "checkclick;" + +//#ifdef RECORD slowdown(); basher::count += 1; #endif + + if (_vm->_gyro->demo) { + if (_vm->_basher->demo_ready()) + break; + if (_vm->_enhanced->keypressede()) + return; + } else if (_vm->_enhanced->keypressede()) + break; + } while (!((mrelease > 0) || (buttona1()) || (buttonb1()))); + + + if (mrelease == 0) { + inkey(); + if (aboutscroll) { + move(e[2 - 1], e[1 - 1], 7); + e[8 - 1] = inchar; + if (egg == e) easteregg(); + } + oktoexit = set::of('\15', '\33', '+', '#', eos).has(inchar); + if (! oktoexit) errorled(); + } + + } while (!((oktoexit) || (mrelease > 0))); + +//#ifdef RECORD record_one(); #endif + + _vm->_gyro->screturn = r == '#'; // "back door" +#endif + + setReadyLight(0); + _vm->_gyro->_seeScroll = false; + CursorMan.showMouse(false); + _vm->_lucerna->_holdLeftMouse = false; // Used in Lucerna::checkclick(). + + warning("STUB: Scrolls::scrollModeNormal()"); +} + +void Scrolls::scrollModeDialogue() { + warning("STUB: Scrolls::scrollModeDialogue()"); +} + +void Scrolls::store(byte what, TuneType &played) { + memcpy(played + 1, played + 2, sizeof(played) - 1); + played[30] = what; +} + +bool Scrolls::theyMatch(TuneType &played) { + byte mistakes = 0; + + for (byte i = 0; i < sizeof(played); i++) { + if (played[i] != _vm->_gyro->kTune[i]) { + mistakes += 1; + } + } + + return mistakes < 5; +} + +void Scrolls::scrollModeMusic() { + setReadyLight(3); + _vm->_gyro->_seeScroll = true; + CursorMan.showMouse(true); + _vm->_gyro->newMouse(4); + + // Since there are no sounds in the game yet, it's pretty pointless to implement this function further. + // For now we act like the player just played the right tone. +#if 0 + if (they_match(played)) { +#endif + _vm->_gyro->_scReturn = true; + CursorMan.showMouse(false); + setReadyLight(0); + _vm->_gyro->_seeScroll = false; + + _vm->_timer->addTimer(8, _vm->_timer->kProcJacquesWakesUp, _vm->_timer->kReasonJacquesWakingUp); + warning("STUB: Scrolls::music_scroll()"); + return; +#if 0 + } + + _vm->_gyro->screturn = false; + CursorMan.showMouse(false); + state(0); + _vm->_gyro->seescroll = false; +#endif +} + +void Scrolls::resetScrollDriver() { + _vm->_gyro->_scrollBells = 0; + _currentFont = kFontStyleRoman; + _useIcon = 0; + _vm->_gyro->_interrogation = 0; // Always reset after a scroll comes up. +} + +void Scrolls::ringBell() { // Pussy's in the well. Who put her in? Little... + for (byte i = 0; i < _vm->_gyro->_scrollBells; i++) + _vm->_lucerna->errorLed(); // Ring the bell "x" times. +} + +void Scrolls::dodgem() { + _dodgeCoord = _vm->getMousePos(); + g_system->warpMouse(_dodgeCoord.x, _vm->_gyro->_underScroll); // Move the pointer off the scroll. +} + +void Scrolls::unDodgem() { + Common::Point actCoord = _vm->getMousePos(); + if ((actCoord.x == _dodgeCoord.x) && (actCoord.y == _vm->_gyro->_underScroll)) + g_system->warpMouse(_dodgeCoord.x, _dodgeCoord.y); // No change, so restore the pointer's original position. +} + +void Scrolls::getIcon(int16 x, int16 y, byte which) { + Common::File file; + + if (!file.open("icons.avd")) { + warning("AVALANCHE: Scrolls: File not found: icons.avd"); + return; + } + + which--; + file.seek(which * 426); + + byte *p = new byte[426]; + file.read(p, 426); + + //putimage(x, y, p, 0); + warning("STUB: Scrolls::getIcon()"); + + delete[] p; + file.close(); +} + +void Scrolls::drawSign(Common::String fn, int16 xl, int16 yl, int16 y) { + Common::File file; + + Common::String filename; + filename = filename.format("%s.avd", fn.c_str()); + if (!file.open(filename)) { + warning("AVALANCHE: Scrolls: File not found: %s", filename.c_str()); + return; + } + +#if 0 + uint16 st = (y - 1) * 80 + (40 - xl / 2) + ((1 - _vm->_gyro->cp) * _vm->_gyro->pagetop); + byte bit; + for (uint16 fv = 1; fv <= yl; fv++) + for (bit = 0; bit <= 3; bit++) { + port[0x3c4] = 2; + port[0x3ce] = 4; + port[0x3c5] = 1 << bit; + port[0x3cf] = bit; + blockread(f, mem[0xa000 * st + (fv * 80)], xl); + } + bit = getpixel(0, 0); +#endif + + warning("STUB: Scrolls::drawSign()"); + + file.close(); +} + +void Scrolls::drawScroll(ScrollsFunctionType modeFunc) { + int16 ex; + + //setvisualpage(cp); + //setactivepage(1 - cp); + _vm->_gyro->_onCanDoPageSwap = false; // On can now no longer swap pages. So we can do what we want without its interference! + + int16 lx = 0; + int16 ly = (_vm->_gyro->_scrollNum) * 6; + for (byte i = 0; i < _vm->_gyro->_scrollNum; i++) { + ex = _vm->_gyro->_scroll[i].size() * 8; + if (lx < ex) + lx = ex; + } + int16 mx = 320; + int16 my = 100; // Getmaxx & getmaxy div 2, both. + lx = lx / 2; + ly -= 2; + + if ((1 <= _useIcon) && (_useIcon <= 34)) + lx += kHalfIconWidth; + + _vm->_graphics->_scrolls.copyFrom(_vm->_graphics->_surface); + + CursorMan.showMouse(false); + + // The right corners of the scroll. + _vm->_graphics->drawPieSlice(_vm->_graphics->_scrolls, mx + lx, my - ly, 0, 90, 15, kColorLightgray); + _vm->_graphics->drawPieSlice(_vm->_graphics->_scrolls, mx + lx, my + ly, 270, 360, 15, kColorLightgray); + _vm->_graphics->drawArc(_vm->_graphics->_scrolls, mx + lx, my - ly, 0, 90, 15, kColorRed); + _vm->_graphics->drawArc(_vm->_graphics->_scrolls, mx + lx, my + ly, 270, 360, 15, kColorRed); + + // The body of the scroll. + _vm->_graphics->_scrolls.fillRect(Common::Rect(mx - lx - 30, my + ly, mx + lx, my + ly + 6), kColorLightgray); + _vm->_graphics->_scrolls.fillRect(Common::Rect(mx - lx - 30, my - ly - 6, mx + lx, my - ly + 1), kColorLightgray); + _vm->_graphics->_scrolls.fillRect(Common::Rect(mx - lx - 15, my - ly, mx + lx + 15, my + ly + 1), kColorLightgray); + + // The left corners of the scroll. + _vm->_graphics->drawPieSlice(_vm->_graphics->_scrolls, mx - lx - 31, my - ly, 0, 180, 15, kColorDarkgray); + _vm->_graphics->drawArc(_vm->_graphics->_scrolls, mx - lx - 31, my - ly, 0, 180, 15, kColorRed); + _vm->_graphics->_scrolls.drawLine(mx - lx - 31 - 15, my - ly, mx - lx - 31 + 15, my - ly, kColorRed); + _vm->_graphics->drawPieSlice(_vm->_graphics->_scrolls, mx - lx - 31, my + ly, 180, 360, 15, kColorDarkgray); + _vm->_graphics->drawArc(_vm->_graphics->_scrolls, mx - lx - 31, my + ly, 180, 360, 15, kColorRed); + _vm->_graphics->_scrolls.drawLine(mx - lx - 31 - 15, my + ly, mx - lx - 31 + 15, my + ly, kColorRed); + + // The rear borders of the scroll. + _vm->_graphics->_scrolls.fillRect(Common::Rect(mx - lx - 30, my - ly - 6, mx + lx, my - ly - 5), kColorRed); + _vm->_graphics->_scrolls.fillRect(Common::Rect(mx - lx - 30, my + ly + 6, mx + lx, my + ly + 7), kColorRed); + _vm->_graphics->_scrolls.fillRect(Common::Rect(mx - lx - 15, my - ly, mx - lx - 14, my + ly), kColorRed); + _vm->_graphics->_scrolls.fillRect(Common::Rect(mx + lx + 15, my - ly, mx + lx + 16, my + ly), kColorRed); + +// CHECKME: unused? +// ex = mx - lx; +// int16 ey = my - ly; + mx -= lx; + my -= ly + 2; + + bool centre = false; + + byte iconIndent = 0; + switch (_useIcon) { + case 0: + iconIndent = 0; + break; // No icon. + case 34: { + drawSign("about", 28, 76, 15); + iconIndent = 0; + } + break; + case 35: { + drawSign("gameover", 52, 59, 71); + iconIndent = 0; + } + break; + } + + if ((1 <= _useIcon) && (_useIcon <= 33)) { // Standard icon. + getIcon(mx, my + ly / 2, _useIcon); + iconIndent = 53; + } + + + for (byte i = 0; i < _vm->_gyro->_scrollNum; i++) { + if (!_vm->_gyro->_scroll[i].empty()) + switch (_vm->_gyro->_scroll[i][_vm->_gyro->_scroll[i].size() - 1]) { + case kControlCenter: { + centre = true; + _vm->_gyro->_scroll[i].deleteLastChar(); + } + break; + case kControlLeftJustified: { + centre = false; + _vm->_gyro->_scroll[i].deleteLastChar(); + } + break; + case kControlQuestion: { + //settextjustify(1, 1); + _shadowBoxX = mx + lx; + _shadowBoxY = my + ly; + _vm->_gyro->_scroll[i].setChar(' ', 0); + // byte groi = *_vm->_graphics->getPixel(0, 0); + // inc(diy,14); + _vm->_gyro->drawShadowBox(_shadowBoxX - 65, _shadowBoxY - 24, _shadowBoxX - 5, _shadowBoxY - 10, "Yes."); + _vm->_gyro->drawShadowBox(_shadowBoxX + 5, _shadowBoxY - 24, _shadowBoxX + 65, _shadowBoxY - 10, "No."); + } + break; + } + + if (centre) + say(320 - _vm->_gyro->_scroll[i].size() * 4 + iconIndent, my, _vm->_gyro->_scroll[i]); + else + say(mx + iconIndent, my, _vm->_gyro->_scroll[i]); + + my += 12; + } + + _vm->_gyro->_underScroll = my * 2 + 6; // Multiplying because of the doubled screen height. + //setvisualpage(1 - cp); + ringBell(); + //my = getpixel(0, 0); + _vm->_gyro->_dropsOk = false; + dodgem(); + + (this->*modeFunc)(); + + unDodgem(); + _vm->_gyro->_dropsOk = true; + //setvisualpage(cp); + //mousepage(cp); + CursorMan.showMouse(false); + // mblit(ex-46,ey-6,ex+lx*2+15,ey+ly*2+6,3,0); + //mblit((ex - 46) / 8, ey - 6, 1 + (ex + lx * 2 + 15) / 8, ey + ly * 2 + 6, cp, 1 - cp); + //blitfix(); + _vm->_gyro->_onCanDoPageSwap = true; // Normality again. + CursorMan.showMouse(true); + //settextjustify(0, 0); // sink + resetScrollDriver(); + /*if (_vm->_gyro->mpress > 0) + _vm->_gyro->after_the_scroll = true;*/ + + warning("STUB: Scrolls::drawScroll()"); +} + +void Scrolls::drawBubble(ScrollsFunctionType modeFunc) { + Common::Point points[3]; +// byte *rp1, *rp2; // replace: 1=bubble, 2=pointer + int16 xc; // x correction + + //setvisualpage(cp); + //setactivepage(1 - cp); + _vm->_gyro->_onCanDoPageSwap = false; // On can now no longer swap pages. So we can do what we want without its interference! + //mousepage(1 - cp); // Mousepage + + CursorMan.showMouse(false); + + int16 xl = 0; + int16 yl = _vm->_gyro->_scrollNum * 5; + for (byte i = 0; i < _vm->_gyro->_scrollNum; i++) { + uint16 textWidth = _vm->_gyro->_scroll[i].size() * 8; + if (textWidth > xl) + xl = textWidth; + } + xl /= 2; + + int16 xw = xl + 18; + int16 yw = yl + 7; + int16 my = yw * 2 - 2; + xc = 0; + + if ((_vm->_gyro->_talkX - xw) < 0) + xc = -(_vm->_gyro->_talkX - xw); + if ((_vm->_gyro->_talkX + xw) > 639) + xc = 639 - (_vm->_gyro->_talkX + xw); + + points[0].x = _vm->_gyro->_talkX - 10; + points[0].y = yw; + points[1].x = _vm->_gyro->_talkX + 10; + points[1].y = yw; + points[2].x = _vm->_gyro->_talkX; + points[2].y = _vm->_gyro->_talkY; + + // Backup the screen before drawing the bubble. + _vm->_graphics->_scrolls.copyFrom(_vm->_graphics->_surface); + + // The body of the bubble. + _vm->_graphics->_scrolls.fillRect(Common::Rect(xc + _vm->_gyro->_talkX - xw + 9, 7, _vm->_gyro->_talkX + xw - 8 + xc, my + 1), _vm->_gyro->_talkBackgroundColor); + _vm->_graphics->_scrolls.fillRect(Common::Rect(xc + _vm->_gyro->_talkX - xw - 1, 12, _vm->_gyro->_talkX + xw + xc + 2, my - 4), _vm->_gyro->_talkBackgroundColor); + + // Top right corner of the bubble. + _vm->_graphics->drawPieSlice(_vm->_graphics->_scrolls, xc + _vm->_gyro->_talkX + xw - 10, 11, 0, 90, 9, _vm->_gyro->_talkBackgroundColor); + // Bottom right corner of the bubble. + _vm->_graphics->drawPieSlice(_vm->_graphics->_scrolls, xc + _vm->_gyro->_talkX + xw - 10, my - 4, 270, 360, 9, _vm->_gyro->_talkBackgroundColor); + // Top left corner of the bubble. + _vm->_graphics->drawPieSlice(_vm->_graphics->_scrolls, xc + _vm->_gyro->_talkX - xw + 10, 11, 90, 180, 9, _vm->_gyro->_talkBackgroundColor); + // Bottom left corner of the bubble. + _vm->_graphics->drawPieSlice(_vm->_graphics->_scrolls, xc + _vm->_gyro->_talkX - xw + 10, my - 4, 180, 270, 9, _vm->_gyro->_talkBackgroundColor); + + // "Tail" of the speech bubble. + _vm->_graphics->drawTriangle(_vm->_graphics->_scrolls, points, _vm->_gyro->_talkBackgroundColor); + + + // CHECKME: Unused? + // yl -= 3; + + // Draw the text of the bubble. The centering of the text was improved here compared to Pascal's settextjustify(). + // The font is not the same that outtextxy() uses in Pascal. I don't have that, so I used Gyro::characters instead. + // It's almost the same, only notable differences are '?', '!', etc. + for (byte i = 0; i < _vm->_gyro->_scrollNum; i++) { + int16 x = xc + _vm->_gyro->_talkX - _vm->_gyro->_scroll[i].size() / 2 * 8; + bool offset = _vm->_gyro->_scroll[i].size() % 2; + _vm->_graphics->drawText(_vm->_graphics->_scrolls, _vm->_gyro->_scroll[i], _vm->_gyro->_font, 8, x - offset * 4, (i * 10) + 12, _vm->_gyro->_talkFontColor); + } + + //setvisualpage(1 - cp); + ringBell(); + _vm->_gyro->_onCanDoPageSwap = false; + CursorMan.showMouse(true); + _vm->_gyro->_dropsOk = false; + + // This does the actual drawing to the screen. + (this->*modeFunc)(); + + CursorMan.showMouse(false); + _vm->_gyro->_dropsOk = true; + + //setvisualpage(cp); + CursorMan.showMouse(true); // sink; + _vm->_gyro->_onCanDoPageSwap = true; + resetScrollDriver(); + /*if (_vm->_gyro->mpress > 0) + _vm->_gyro->after_the_scroll = true;*/ +} + +bool Scrolls::displayQuestion(Common::String question) { + warning("STUB: Scrolls::displayQuestion()"); + return true; +} + +void Scrolls::resetScroll() { + _vm->_gyro->_scrollNum = 1; + for (int i = 0; i < 15; i++) + if (!_vm->_gyro->_scroll[i].empty()) + _vm->_gyro->_scroll[i].clear(); +} + +void Scrolls::setBubbleStateNatural() { + _vm->_gyro->_talkX = 320; + _vm->_gyro->_talkY = 200; + _vm->_gyro->_talkBackgroundColor = 8; + _vm->_gyro->_talkFontColor = 15; +} + +Common::String Scrolls::displayMoney() { + Common::String result; + + if (_vm->_gyro->_dna._money < 12) { // just pence + result = _vm->_gyro->intToStr(_vm->_gyro->_dna._money) + 'd'; + } else if (_vm->_gyro->_dna._money < 240) { // shillings & pence + result = _vm->_gyro->intToStr(_vm->_gyro->_dna._money / 12) + '/'; + if ((_vm->_gyro->_dna._money % 12) == 0) + result = result + '-'; + else + result = result + _vm->_gyro->intToStr(_vm->_gyro->_dna._money % 12); + } else // L, s & d + result = Common::String('\x9C') + _vm->_gyro->intToStr(_vm->_gyro->_dna._money / 240) + '.' + _vm->_gyro->intToStr((_vm->_gyro->_dna._money / 12) % 20) + + '.' + _vm->_gyro->intToStr(_vm->_gyro->_dna._money % 12); + if (_vm->_gyro->_dna._money > 12) + result = result + " (that's " + _vm->_gyro->intToStr(_vm->_gyro->_dna._money) + "d)"; + + return result; +} + +void Scrolls::stripTrailingSpaces(Common::String &str) { + while (str[str.size() - 1] == ' ') { + str.deleteLastChar(); + } +} + +void Scrolls::solidify(byte n) { + if (!_vm->_gyro->_scroll[n].contains(' ')) + return; // No spaces. + + // So there MUST be a space there, somewhere... + do { + _vm->_gyro->_scroll[n + 1] = _vm->_gyro->_scroll[n][_vm->_gyro->_scroll[n].size() - 1] + _vm->_gyro->_scroll[n + 1]; + _vm->_gyro->_scroll[n].deleteLastChar(); + } while (_vm->_gyro->_scroll[n][_vm->_gyro->_scroll[n].size() - 1] != ' '); + + stripTrailingSpaces(_vm->_gyro->_scroll[n]); +} + +void Scrolls::callScrollDriver() { +// bool was_virtual; // Was the mouse cursor virtual on entry to this proc? + + + //nosound(); + warning("STUB: Scrolls::calldrivers()"); + + setReadyLight(0); + _vm->_gyro->_scReturn = false; + bool mouthnext = false; + bool call_spriterun = true; // Only call sprite_run the FIRST time. + + switch (_vm->_gyro->_buffer[_vm->_gyro->_bufSize - 1]) { + case kControlToBuffer: + _vm->_gyro->_bufSize--; + break; // ^D = (D)on't include pagebreak + case kControlSpeechBubble: + case kControlQuestion: + break; // ^B = speech (B)ubble, ^Q = (Q)uestion in dialogue box + default: { + _vm->_gyro->_bufSize++; + _vm->_gyro->_buffer[_vm->_gyro->_bufSize - 1] = kControlParagraph; + } + } + + uint16 size = _vm->_gyro->_bufSize; + + for (uint16 i = 0; i < size; i++) { + if (mouthnext) { + if (_vm->_gyro->_buffer[i] == kControlRegister) + _param = 0; + else if (('0' <= _vm->_gyro->_buffer[i]) && (_vm->_gyro->_buffer[i] <= '9')) + _param = _vm->_gyro->_buffer[i] - 48; + else if (('A' <= _vm->_gyro->_buffer[i]) && (_vm->_gyro->_buffer[i] <= 'Z')) + _param = _vm->_gyro->_buffer[i] - 55; + + mouthnext = false; + } else { + switch (_vm->_gyro->_buffer[i]) { + case kControlParagraph: { + if ((_vm->_gyro->_scrollNum == 1) && (_vm->_gyro->_scroll[0].empty())) + break; + + if (call_spriterun) + _vm->_lucerna->spriteRun(); + call_spriterun = false; + + drawScroll(&Avalanche::Scrolls::scrollModeNormal); + + resetScroll(); + + if (_vm->_gyro->_scReturn) + return; + } + break; + case kControlBell: + _vm->_gyro->_scrollBells++; + break; // #7 = "Bel" + case kControlSpeechBubble: { + if ((_vm->_gyro->_scrollNum == 1) && (_vm->_gyro->_scroll[0].empty())) + break; + + if (call_spriterun) + _vm->_lucerna->spriteRun(); + call_spriterun = false; + + if (_param == 0) + setBubbleStateNatural(); + else if ((1 <= _param) && (_param <= 9)) { + if ((_param > _vm->_animation->kSpriteNumbMax) || (!_vm->_animation->_sprites[_param - 1]._quick)) { // Not valid. + _vm->_lucerna->errorLed(); + setBubbleStateNatural(); + } else + _vm->_animation->_sprites[_param - 1].chatter(); // Normal sprite talking routine. + } else if ((10 <= _param) && (_param <= 36)) { + // Quasi-peds. (This routine performs the same + // thing with QPs as triptype.chatter does with the + // sprites.) + _vm->_gyro->_talkX = _vm->_gyro->_peds[_vm->_gyro->kQuasipeds[_param - 10]._whichPed - 1]._x; + _vm->_gyro->_talkY = _vm->_gyro->_peds[_vm->_gyro->kQuasipeds[_param - 10]._whichPed - 1]._y; // Position. + + _vm->_gyro->_talkFontColor = _vm->_gyro->kQuasipeds[_param - 10]._foregroundColor; + _vm->_gyro->_talkBackgroundColor = _vm->_gyro->kQuasipeds[_param - 10]._backgroundColor; // Colors. + } else { + _vm->_lucerna->errorLed(); // Not valid. + setBubbleStateNatural(); + } + + drawBubble(&Avalanche::Scrolls::scrollModeNormal); + + resetScroll(); + + if (_vm->_gyro->_scReturn) + return; + } + break; + case kControlNegative: + switch (_param) { + case 1: + displayText(displayMoney() + kControlToBuffer); // Insert cash balance. (Recursion) + break; + case 2: + displayText(_vm->_acci->kVocabulary[_vm->_acci->kFirstPassword + _vm->_gyro->_dna._passwordNum]._word + kControlToBuffer); + break; + case 3: + displayText(_vm->_gyro->_dna._favouriteDrink + kControlToBuffer); + break; + case 4: + displayText(_vm->_gyro->_dna._favouriteSong + kControlToBuffer); + break; + case 5: + displayText(_vm->_gyro->_dna._worstPlaceOnEarth + kControlToBuffer); + break; + case 6: + displayText(_vm->_gyro->_dna._spareEvening + kControlToBuffer); + break; + case 9: + displayText(_vm->_gyro->intToStr(_vm->_gyro->_dna._catacombX) + ',' + _vm->_gyro->intToStr(_vm->_gyro->_dna._catacombY) + kControlToBuffer); + break; + case 10: + switch (_vm->_gyro->_dna._boxContent) { + case 0: { // Sixpence. + _vm->_visa->displayScrollChain('q', 37); // You find the sixpence. + _vm->_gyro->_dna._money += 6; + _vm->_gyro->_dna._boxContent = _vm->_acci->kNothing; + _vm->_lucerna->incScore(2); + return; + } + break; + case Acci::kNothing: + displayText("nothing at all. It's completely empty."); + break; + default: + displayText(_vm->_gyro->getItem(_vm->_gyro->_dna._boxContent) + '.'); + } + break; + case 11: + for (byte j = 0; j < kObjectNum; j++) { + if (_vm->_gyro->_dna._objects[j]) + displayText(_vm->_gyro->getItem(j) + ", " + kControlToBuffer); + } + break; + } + break; + case kControlIcon: + _useIcon = _param; + break; + case kControlNewLine: + _vm->_gyro->_scrollNum++; + break; + case kControlQuestion: { + if (call_spriterun) + _vm->_lucerna->spriteRun(); + call_spriterun = false; + + _vm->_gyro->_scrollNum++; + _vm->_gyro->_scroll[_vm->_gyro->_scrollNum - 1] = kControlQuestion; + + drawScroll(&Avalanche::Scrolls::scrollModeDialogue); + + resetScroll(); + } + break; + case kControlRegister: + mouthnext = true; + break; + case kControlInsertSpaces: + for (byte j = 0; j < 9; j++) + _vm->_gyro->_scroll[_vm->_gyro->_scrollNum - 1] += ' '; + break; + default: { // Add new char. + if (_vm->_gyro->_scroll[_vm->_gyro->_scrollNum - 1].size() == 50) { + solidify(_vm->_gyro->_scrollNum - 1); + _vm->_gyro->_scrollNum++; + } + _vm->_gyro->_scroll[_vm->_gyro->_scrollNum - 1] += _vm->_gyro->_buffer[i]; + } + } + } + } +} + +void Scrolls::displayText(Common::String text) { // TODO: REPLACE BUFFER WITH A STRING!!!!!!!!!! + _vm->_gyro->_bufSize = text.size(); + memcpy(_vm->_gyro->_buffer, text.c_str(), _vm->_gyro->_bufSize); + callScrollDriver(); +} + +void Scrolls::loadFont() { + Common::File file; + + if (!file.open("avalot.fnt")) { + warning("AVALANCHE: Scrolls: File not found: avalot.fnt"); + return; + } + for (int16 i = 0; i < 256; i++) + file.read(_scrollFonts[0][i], 16); + file.close(); + + if (!file.open("avitalic.fnt")) { + warning("AVALANCHE: Scrolls: File not found: avitalic.fnt"); + return; + } + for (int16 i = 0; i < 256; i++) + file.read(_scrollFonts[1][i], 16); + file.close(); + + if (!file.open("ttsmall.fnt")) { + warning("AVALANCHE: Scrolls: File not found: ttsmall.fnt"); + return; + } + for (int16 i = 0; i < 256; i++) + file.read(_vm->_gyro->_font[i],16); + file.close(); +} + +void Scrolls::musicalScroll() { + displayText(Common::String("To play the harp...") + kControlNewLine + kControlNewLine + "Use these keys:" + + kControlNewLine + + kControlInsertSpaces + "Q W E R T Y U I O P [ ]" + kControlNewLine + kControlNewLine + "Or press Enter to stop playing." + + kControlToBuffer); + + _vm->_lucerna->spriteRun(); + + drawScroll(&Avalanche::Scrolls::scrollModeMusic); + + resetScroll(); +} + +} // End of namespace Avalanche diff --git a/engines/avalanche/scrolls2.h b/engines/avalanche/scrolls2.h new file mode 100644 index 0000000000..026c5888e1 --- /dev/null +++ b/engines/avalanche/scrolls2.h @@ -0,0 +1,123 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on the original source code of Lord Avalot d'Argent version 1.3. + * Copyright (c) 1994-1995 Mike, Mark and Thomas Thurman. + */ + + /* SCROLLS The scroll driver. */ + +#ifndef AVALANCHE_SCROLLS2_H +#define AVALANCHE_SCROLLS2_H + +#include "common/system.h" + +namespace Avalanche { +class AvalancheEngine; + +class Scrolls; + +typedef void (Scrolls::*ScrollsFunctionType)(); + +class Scrolls { +public: + // Constants to replace the command characters from Pascal. + // For more information, see: https://github.com/urukgit/avalot/wiki/Scrolldrivers + enum ControlCharacter { + kControlSpeechBubble = 2, // ^B + kControlCenter = 3, // ^C + kControlToBuffer = 4, // ^D + kControlItalic = 6, // ^F + kControlBell = 7, // ^G + kControlBackspace = 8, // ^H + kControlInsertSpaces = 9, // ^I + kControlLeftJustified = 12, // ^L + kControlNewLine = 13, // ^M + kControlParagraph = 16, // ^P + kControlQuestion = 17, // ^Q + kControlRoman = 18, // ^R + kControlRegister = 19, // ^S + kControlNegative = 21, // ^U + kControlIcon = 22 // ^V + }; + + bool _aboutScroll; // Is this the about box? + FontType _scrollFonts[2]; + + Scrolls(AvalancheEngine *vm); + + void init(); + void setReadyLight(byte state); // Sets "Ready" light to whatever. + void drawScroll(ScrollsFunctionType modeFunc); + void drawBubble(ScrollsFunctionType modeFunc); + void resetScroll(); + void callScrollDriver(); + void displayText(Common::String text); + bool displayQuestion(Common::String question); + void setBubbleStateNatural(); // Natural state of bubbles + Common::String displayMoney(); + void musicalScroll(); // Practically this one is a mini-game which called when you play the harp in the monastery. + +private: + AvalancheEngine *_vm; + + enum FontStyle { + kFontStyleRoman, + kFontStyleItalic + }; + + static const int16 kHalfIconWidth = 19; // Half the width of an icon. + + int16 _shadowBoxX, _shadowBoxY; + byte _currentFont; // Current font + Common::Point _dodgeCoord; + byte _param; // For using arguments code + byte _useIcon; + + // These 3 functions are always passed as ScrollsFunctionType parameters. + void scrollModeNormal(); + void scrollModeDialogue(); + void scrollModeMusic(); + + // These 2 are used only in musicalScroll(). + void store(byte what, TuneType &played); + bool theyMatch(TuneType &played); + + void stripTrailingSpaces(Common::String &str); // Original: strip. + void solidify(byte n); // Does the word wrapping. + + void dodgem(); // This moves the mouse pointer off the scroll so that you can read it. + void unDodgem(); // This is the opposite of Dodgem. It moves the mouse pointer back, IF you haven't moved it in the meantime. + + void easterEgg(); + void say(int16 x, int16 y, Common::String text); + void resetScrollDriver(); + void ringBell(); // Original: dingdongbell + void getIcon(int16 x, int16 y, byte which); + void drawSign(Common::String name, int16 xl, int16 yl, int16 y); // This is for drawing a big "about" or "gameover" picture loaded from a file into an empty scroll. + void loadFont(); +}; + +} // End of namespace Avalanche + +#endif // AVALANCHE_SCROLLS2_H diff --git a/engines/avalanche/sequence2.cpp b/engines/avalanche/sequence2.cpp new file mode 100644 index 0000000000..600a3cae4d --- /dev/null +++ b/engines/avalanche/sequence2.cpp @@ -0,0 +1,108 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on the original source code of Lord Avalot d'Argent version 1.3. + * Copyright (c) 1994-1995 Mike, Mark and Thomas Thurman. + */ + +/* SEQUENCE The sequencer. */ + +#include "avalanche/avalanche.h" + +#include "avalanche/sequence2.h" +#include "avalanche/gyro2.h" +#include "avalanche/timer.h" +#include "avalanche/celer2.h" +#include "avalanche/animation.h" + +#include "common/scummsys.h" + +namespace Avalanche { + +Sequence::Sequence(AvalancheEngine *vm) { + _vm = vm; +} + +void Sequence::firstShow(byte what) { + // First, we need to blank out the entire array. + for (uint i = 0; i < sizeof(_seq); i++) + _seq[i] = 0; + + // Then it's just the same as thenShow. + thenShow(what); +} + +void Sequence::thenShow(byte what) { + for (int16 i = 0; i < kSeqLength; i++) { + if (_seq[i] == 0) { + _seq[i] = what; + return; + } + } +} + +void Sequence::thenFlip(byte where, byte ped) { + thenShow(kNowFlip); + + _vm->_gyro->_dna._flipToWhere = where; + _vm->_gyro->_dna._flipToPed = ped; +} + +void Sequence::startToClose() { + _vm->_timer->loseTimer(_vm->_timer->kReasonSequencer); + _vm->_timer->addTimer(7, _vm->_timer->kProcSequence, _vm->_timer->kReasonSequencer); +} + +void Sequence::startToOpen() { + _vm->_gyro->_dna._userMovesAvvy = false; // They can't move. + _vm->_animation->stopWalking(); // And they're not moving now. + startToClose(); // Apart from that, it's the same thing. +} + +void Sequence::shoveLeft() { + memcpy(_seq, _seq+1, kSeqLength - 1); // Shift everything to the left. +} + +void Sequence::callSequencer() { + switch (_seq[0]) { + case 0: + return; // No more routines. + break; + case 177: // Flip room. + _vm->_gyro->_dna._userMovesAvvy = true; + _vm->_animation->flipRoom(_vm->_gyro->_dna._flipToWhere, _vm->_gyro->_dna._flipToPed); + if (_seq[0] == 177) + shoveLeft(); + break; + } + + if ((_seq[0] >= 1) && (_seq[0] <= 176)) { + // Show a frame. + _vm->_celer->drawBackgroundSprite(-1, -1, _seq[0]); + shoveLeft(); + } + + startToClose(); // Make sure this PROC gets called again. +} + +} // End of namespace Avalanche. diff --git a/engines/avalanche/sequence2.h b/engines/avalanche/sequence2.h new file mode 100644 index 0000000000..67650d519d --- /dev/null +++ b/engines/avalanche/sequence2.h @@ -0,0 +1,62 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on the original source code of Lord Avalot d'Argent version 1.3. + * Copyright (c) 1994-1995 Mike, Mark and Thomas Thurman. + */ + +/* SEQUENCE The sequencer. */ + +#ifndef AVALANCHE_SEQUENCE2_H +#define AVALANCHE_SEQUENCE2_H + +#include "common/scummsys.h" + +namespace Avalanche { +class AvalancheEngine; + +class Sequence { +public: + static const int16 kNowFlip = 177; + static const int16 kSeqLength = 10; + + byte _seq[kSeqLength]; + + Sequence(AvalancheEngine *vm); + + void firstShow(byte what); + void thenShow(byte what); + void thenFlip(byte where, byte ped); + void startToClose(); + void startToOpen(); + void callSequencer(); + +private: + AvalancheEngine *_vm; + + void shoveLeft(); // This PROC is called by Timer when it's time to do another frame. +}; + +} // End of namespace Avalanche. + +#endif // AVALANCHE_SEQUENCE2_H diff --git a/engines/avalanche/timer.cpp b/engines/avalanche/timer.cpp new file mode 100644 index 0000000000..883e1a25a4 --- /dev/null +++ b/engines/avalanche/timer.cpp @@ -0,0 +1,684 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on the original source code of Lord Avalot d'Argent version 1.3. + * Copyright (c) 1994-1995 Mike, Mark and Thomas Thurman. + */ + +/* Original name: TIMEOUT The scheduling unit. */ + +#include "avalanche/avalanche.h" + +#include "avalanche/timer.h" +#include "avalanche/visa2.h" +#include "avalanche/lucerna2.h" +#include "avalanche/animation.h" +#include "avalanche/scrolls2.h" +#include "avalanche/acci2.h" +#include "avalanche/sequence2.h" +#include "avalanche/enid2.h" +#include "avalanche/pingo2.h" + +#include "common/textconsole.h" + +namespace Avalanche { + +Timer::Timer(AvalancheEngine *vm) { + _vm = vm; + + for (byte i = 0; i < 7; i++) { + _times[i]._timeLeft = 0; + _times[i]._action = 0; + _times[i]._reason = 0; + } + _timerLost = false; +} + +/** + * Add a nex timer + * @remarks Originally called 'set_up_timer' + */ +void Timer::addTimer(int32 duration, byte action, byte reason) { + if ((_vm->_gyro->isLoaded == false) || (_timerLost == true)) { + byte i = 0; + while ((i < 7) && (_times[i]._timeLeft != 0)) + i++; + + if (i == 7) + return; // Oh dear... No timer left + + // Everything's OK here! + _times[i]._timeLeft = duration; + _times[i]._action = action; + _times[i]._reason = reason; + } else { + _vm->_gyro->isLoaded = false; + return; + } +} + +/** + * Update the timers + * @remarks Originally called 'one_tick' + */ +void Timer::updateTimer() { + if (_vm->_gyro->_dropdownActive) + return; + + for (byte i = 0; i < 7; i++) { + if (_times[i]._timeLeft <= 0) + continue; + + _times[i]._timeLeft--; + + if (_times[i]._timeLeft == 0) { + switch (_times[i]._action) { + case kProcOpenDrawbridge : + openDrawbridge(); + break; + case kProcAvariciusTalks : + avariciusTalks(); + break; + case kProcUrinate : + urinate(); + break; + case kProcToilet : + toilet(); + break; + case kProcBang: + bang(); + break; + case kProcBang2: + bang2(); + break; + case kProcStairs: + stairs(); + break; + case kProcCardiffSurvey: + cardiffSurvey(); + break; + case kProcCardiffReturn: + cardiffReturn(); + break; + case kProcCwytalotInHerts: + cwytalotInHerts(); + break; + case kProcGetTiedUp: + getTiedUp(); + break; + case kProcGetTiedUp2: + getTiedUp2(); + break; + case kProcHangAround: + hangAround(); + break; + case kProcHangAround2: + hangAround2(); + break; + case kProcAfterTheShootemup: + afterTheShootemup(); + break; + case kProcJacquesWakesUp: + jacquesWakesUp(); + break; + case kProcNaughtyDuke: + naughtyDuke(); + break; + case kProcNaughtyDuke2: + naughtyDuke2(); + break; + case kProcNaughtyDuke3: + naughtyDuke3(); + break; + case kProcJump: + jump(); + break; + case kProcSequence: + _vm->_sequence->callSequencer(); + break; + case kProcCrapulusSpludOut: + crapulusSaysSpludOut(); + break; + case kProcDawnDelay: + _vm->_lucerna->dawn(); + break; + case kProcBuyDrinks: + buyDrinks(); + break; + case kProcBuyWine: + buyWine(); + break; + case kProcCallsGuards: + callsGuards(); + break; + case kProcGreetsMonk: + greetsMonk(); + break; + case kProcFallDownOubliette: + fallDownOubliette(); + break; + case kProcMeetAvaroid: + meetAvaroid(); + break; + case kProcRiseUpOubliette: + riseUpOubliette(); + break; + case kProcRobinHoodAndGeida: + robinHoodAndGeida(); + break; + case kProcRobinHoodAndGeidaTalk: + robinHoodAndGeidaTalk(); + break; + case kProcAvalotReturns: + avalotReturns(); + break; + case kProcAvvySitDown: + avvySitDown(); + break; + case kProcGhostRoomPhew: + ghostRoomPhew(); + break; + case kProcArkataShouts: + arkataShouts(); + break; + case kProcWinning: + winning(); + break; + case kProcAvalotFalls: + avalotFalls(); + break; + case kProcSpludwickGoesToCauldron: + spludwickGoesToCauldron(); + break; + case kProcSpludwickLeavesCauldron: + spludwickLeavesCauldron(); + break; + case kProcGiveLuteToGeida: + giveLuteToGeida(); + break; + } + } + } + _vm->_gyro->_roomTime++; // Cycles since you've been in this room. + _vm->_gyro->_dna._totalTime++; // Total amount of time for this game. +} + +void Timer::loseTimer(byte which) { + for (byte i = 0; i < 7; i++) { + if (_times[i]._reason == which) + _times[i]._timeLeft = 0; // Cancel this one! + } + + _timerLost = true; +} + +void Timer::openDrawbridge() { + _vm->_gyro->_dna._drawbridgeOpen++; + _vm->_celer->drawBackgroundSprite(-1, -1, _vm->_gyro->_dna._drawbridgeOpen - 1); + + if (_vm->_gyro->_dna._drawbridgeOpen == 4) + _vm->_gyro->_magics[1]._operation = _vm->_gyro->kMagicNothing; // You may enter the drawbridge. + else + addTimer(7, kProcOpenDrawbridge, kReasonDrawbridgeFalls); +} + +void Timer::avariciusTalks() { + _vm->_visa->displayScrollChain('q', _vm->_gyro->_dna._avariciusTalk); + _vm->_gyro->_dna._avariciusTalk++; + + if (_vm->_gyro->_dna._avariciusTalk < 17) + addTimer(177, kProcAvariciusTalks, kReasonAvariciusTalks); + else + _vm->_lucerna->incScore(3); +} + +void Timer::urinate() { + _vm->_animation->_sprites[0].turn(Animation::kDirUp); + _vm->_animation->stopWalking(); + _vm->_lucerna->drawDirection(); + addTimer(14, kProcToilet, kReasonGoToToilet); +} + +void Timer::toilet() { + _vm->_scrolls->displayText("That's better!"); +} + +void Timer::bang() { + _vm->_scrolls->displayText(Common::String(_vm->_scrolls->kControlItalic) + "< BANG! >"); + addTimer(30, kProcBang2, kReasonExplosion); +} + +void Timer::bang2() { + _vm->_scrolls->displayText("Hmm... sounds like Spludwick's up to something..."); +} + +void Timer::stairs() { + _vm->_gyro->blip(); + _vm->_animation->_sprites[0].walkTo(4); + _vm->_celer->drawBackgroundSprite(-1, -1, 2); + _vm->_gyro->_dna._brummieStairs = 2; + _vm->_gyro->_magics[10]._operation = _vm->_gyro->kMagicSpecial; + _vm->_gyro->_magics[10]._data = 2; // Reached the bottom of the stairs. + _vm->_gyro->_magics[3]._operation = _vm->_gyro->kMagicNothing; // Stop them hitting the sides (or the game will hang.) +} + +void Timer::cardiffSurvey() { + if (_vm->_gyro->_dna._cardiffQuestionNum == 0) { + _vm->_gyro->_dna._cardiffQuestionNum++; + _vm->_visa->displayScrollChain('q', 27); + } + + _vm->_visa->displayScrollChain('z', _vm->_gyro->_dna._cardiffQuestionNum); + _vm->_gyro->_interrogation = _vm->_gyro->_dna._cardiffQuestionNum; + addTimer(182, kProcCardiffSurvey, kReasonCardiffsurvey); +} + +void Timer::cardiffReturn() { + _vm->_visa->displayScrollChain('q', 28); + cardiffSurvey(); // Add end of question. +} + +void Timer::cwytalotInHerts() { + _vm->_visa->displayScrollChain('q', 29); +} + +void Timer::getTiedUp() { + _vm->_visa->displayScrollChain('q', 34); // ...Trouble! + _vm->_gyro->_dna._userMovesAvvy = false; + _vm->_gyro->_dna._beenTiedUp = true; + _vm->_animation->stopWalking(); + _vm->_animation->_sprites[1].stopWalk(); + _vm->_animation->_sprites[1].stopHoming(); + _vm->_animation->_sprites[1]._callEachStepFl = true; + _vm->_animation->_sprites[1]._eachStepProc = _vm->_animation->kProcGrabAvvy; + addTimer(70, kProcGetTiedUp2, kReasonGettingTiedUp); +} + +void Timer::getTiedUp2() { + _vm->_animation->_sprites[0].walkTo(4); + _vm->_animation->_sprites[1].walkTo(5); + _vm->_gyro->_magics[3]._operation = _vm->_gyro->kMagicNothing; // No effect when you touch the boundaries. + _vm->_gyro->_dna._friarWillTieYouUp = true; +} + +void Timer::hangAround() { + _vm->_animation->_sprites[1]._doCheck = false; + _vm->_animation->_sprites[0].init(7, true, _vm->_animation); // Robin Hood + _vm->_gyro->_whereIs[_vm->_gyro->kPeopleRobinHood - 150] = r__robins; + _vm->_animation->appearPed(1, 2); + _vm->_visa->displayScrollChain('q', 39); + _vm->_animation->_sprites[0].walkTo(7); + addTimer(55, kProcHangAround2, kReasonHangingAround); +} + +void Timer::hangAround2() { + _vm->_visa->displayScrollChain('q', 40); + _vm->_animation->_sprites[1]._vanishIfStill = false; + _vm->_animation->_sprites[1].walkTo(4); + _vm->_gyro->_whereIs[_vm->_gyro->kPeopleFriarTuck - 150] = r__robins; + _vm->_visa->displayScrollChain('q', 41); + _vm->_animation->_sprites[0].remove(); + _vm->_animation->_sprites[1].remove(); // Get rid of Robin Hood and Friar Tuck. + + addTimer(1, kProcAfterTheShootemup, kReasonHangingAround); + // Immediately call the following proc (when you have a chance). + + _vm->_gyro->_dna._tiedUp = false; + + _vm->_enid->backToBootstrap(1); // Call the shoot-'em-up. +} + +void Timer::afterTheShootemup() { + + _vm->_animation->flipRoom(_vm->_gyro->_dna._room, 0); + // Only placed this here to replace the minigame. TODO: Remove it when the shoot em' up is implemented! + + _vm->_animation->_sprites[0].init(0, true, _vm->_animation); // Avalot. + _vm->_animation->appearPed(1, 2); + _vm->_gyro->_dna._userMovesAvvy = true; + _vm->_gyro->_dna._objects[_vm->_gyro->kObjectCrossbow - 1] = true; + _vm->_lucerna->refreshObjectList(); + + // Same as the added line above: TODO: Remove it later!!! + _vm->_scrolls->displayText(Common::String("P.S.: There should have been the mini-game called \"shoot em' up\", but I haven't implemented it yet: you get the crossbow automatically.") + + _vm->_scrolls->kControlNewLine + _vm->_scrolls->kControlNewLine + "Peter (uruk)"); + +#if 0 + byte shootscore, gain; + + shootscore = mem[storage_seg * storage_ofs]; + gain = (shootscore + 5) / 10; // Rounding up. + + display(string("\6Your score was ") + strf(shootscore) + '.' + "\r\rYou gain (" + + strf(shootscore) + " 0xF6 10) = " + strf(gain) + " points."); + + if (gain > 20) { + display("But we won't let you have more than 20 points!"); + points(20); + } else + points(gain); +#endif + + warning("STUB: Timer::after_the_shootemup()"); + + _vm->_visa->displayScrollChain('q', 70); +} + +void Timer::jacquesWakesUp() { + _vm->_gyro->_dna._jacquesState++; + + switch (_vm->_gyro->_dna._jacquesState) { // Additional pictures. + case 1 : + _vm->_celer->drawBackgroundSprite(-1, -1, 1); // Eyes open. + _vm->_visa->displayScrollChain('Q', 45); + break; + case 2 : // Going through the door. + _vm->_celer->drawBackgroundSprite(-1, -1, 2); // Not on the floor. + _vm->_celer->drawBackgroundSprite(-1, -1, 3); // But going through the door. + _vm->_gyro->_magics[5]._operation = _vm->_gyro->kMagicNothing; // You can't wake him up now. + break; + case 3 : // Gone through the door. + _vm->_celer->drawBackgroundSprite(-1, -1, 2); // Not on the floor, either. + _vm->_celer->drawBackgroundSprite(-1, -1, 4); // He's gone... so the door's open. + _vm->_gyro->_whereIs[_vm->_gyro->kPeopleJacques - 150] = 0; // Gone! + break; + } + + if (_vm->_gyro->_dna._jacquesState == 5) { + _vm->_gyro->_dna._bellsAreRinging = true; + _vm->_gyro->_dna._aylesIsAwake = true; + _vm->_lucerna->incScore(2); + } + + switch (_vm->_gyro->_dna._jacquesState) { + case 1: + case 2: + case 3: + addTimer(12, kProcJacquesWakesUp, kReasonJacquesWakingUp); + break; + case 4: + addTimer(24, kProcJacquesWakesUp, kReasonJacquesWakingUp); + break; + } +} + +void Timer::naughtyDuke() { // This is when the Duke comes in and takes your money. + _vm->_animation->_sprites[1].init(9, false, _vm->_animation); // Here comes the Duke. + _vm->_animation->appearPed(2, 1); // He starts at the door... + _vm->_animation->_sprites[1].walkTo(3); // He walks over to you. + + // Let's get the door opening. + _vm->_celer->drawBackgroundSprite(-1, -1, 1); + _vm->_sequence->firstShow(2); + _vm->_sequence->startToClose(); + + addTimer(50, kProcNaughtyDuke2, kReasonNaughtyDuke); +} + +void Timer::naughtyDuke2() { + _vm->_visa->displayScrollChain('q', 48); // "Ha ha, it worked again!" + _vm->_animation->_sprites[1].walkTo(1); // Walk to the door. + _vm->_animation->_sprites[1]._vanishIfStill = true; // Then go away! + addTimer(32, kProcNaughtyDuke3, kReasonNaughtyDuke); +} + +void Timer::naughtyDuke3() { + _vm->_celer->drawBackgroundSprite(-1, -1, 1); + _vm->_sequence->firstShow(2); + _vm->_sequence->startToClose(); +} + +void Timer::jump() { + _vm->_gyro->_dna._jumpStatus++; + + switch (_vm->_gyro->_dna._jumpStatus) { + case 1: + case 2: + case 3: + case 5: + case 7: + case 9: + _vm->_animation->_sprites[0]._y--; + break; + case 12: + case 13: + case 14: + case 16: + case 18: + case 19: + _vm->_animation->_sprites[0]._y++; + break; + } + + if (_vm->_gyro->_dna._jumpStatus == 20) { // End of jump. + _vm->_gyro->_dna._userMovesAvvy = true; + _vm->_gyro->_dna._jumpStatus = 0; + } else { // Still jumping. + addTimer(1, kProcJump, kReasonJumping); + } + + if ((_vm->_gyro->_dna._jumpStatus == 10) // You're at the highest point of your jump. + && (_vm->_gyro->_dna._room == r__insidecardiffcastle) + && (_vm->_gyro->_dna._arrowInTheDoor == true) + && (_vm->_animation->inField(3))) { // Beside the wall + // Grab the arrow! + if (_vm->_gyro->_dna._carryNum >= kCarryLimit) + _vm->_scrolls->displayText("You fail to grab it, because your hands are full."); + else { + _vm->_celer->drawBackgroundSprite(-1, -1, 2); + _vm->_gyro->_dna._arrowInTheDoor = false; // You've got it. + _vm->_gyro->_dna._objects[_vm->_gyro->kObjectBolt - 1] = true; + _vm->_lucerna->refreshObjectList(); + _vm->_visa->displayScrollChain('q', 50); + _vm->_lucerna->incScore(3); + } + } +} + +void Timer::crapulusSaysSpludOut() { + _vm->_visa->displayScrollChain('q', 56); + _vm->_gyro->_dna._crapulusWillTell = false; +} + +void Timer::buyDrinks() { + _vm->_celer->drawBackgroundSprite(-1, -1, 11); // Malagauche gets up again. + _vm->_gyro->_dna._malagauche = 0; + + _vm->_visa->displayScrollChain('D', _vm->_gyro->_dna._drinking); // Display message about it. + _vm->_pingo->wobble(); // Do the special effects. + _vm->_visa->displayScrollChain('D', 1); // That'll be thruppence. + if (_vm->_gyro->decreaseMoney(3)) // Pay 3d. + _vm->_visa->displayScrollChain('D', 3); // Tell 'em you paid up. + _vm->_acci->drink(); +} + +void Timer::buyWine() { + _vm->_celer->drawBackgroundSprite(-1, -1, 11); // Malagauche gets up again. + _vm->_gyro->_dna._malagauche = 0; + + _vm->_visa->displayScrollChain('D', 50); // You buy the wine. + _vm->_visa->displayScrollChain('D', 1); // It'll be thruppence. + if (_vm->_gyro->decreaseMoney(3)) { + _vm->_visa->displayScrollChain('D', 4); // You paid up. + _vm->_gyro->_dna._objects[_vm->_gyro->kObjectWine - 1] = true; + _vm->_lucerna->refreshObjectList(); + _vm->_gyro->_dna._wineState = 1; // OK Wine. + } +} + +void Timer::callsGuards() { + _vm->_visa->displayScrollChain('Q', 58); // "GUARDS!!!" + _vm->_lucerna->gameOver(); +} + +void Timer::greetsMonk() { + _vm->_visa->displayScrollChain('Q', 59); + _vm->_gyro->_dna._enteredLustiesRoomAsMonk = true; +} + +void Timer::fallDownOubliette() { + _vm->_gyro->_magics[8]._operation = _vm->_gyro->kMagicNothing; + _vm->_animation->_sprites[0]._moveY++; // Increments dx/dy! + _vm->_animation->_sprites[0]._y += _vm->_animation->_sprites[0]._moveY; // Dowwwn we go... + addTimer(3, kProcFallDownOubliette, kReasonFallingDownOubliette); +} + +void Timer::meetAvaroid() { + if (_vm->_gyro->_dna._metAvaroid) { + _vm->_scrolls->displayText(Common::String("You can't expect to be ") + _vm->_scrolls->kControlItalic + "that" + + _vm->_scrolls->kControlRoman + " lucky twice in a row!"); + _vm->_lucerna->gameOver(); + } else { + _vm->_visa->displayScrollChain('Q', 60); + _vm->_gyro->_dna._metAvaroid = true; + addTimer(1, kProcRiseUpOubliette, kReasonRisingUpOubliette); + + _vm->_animation->_sprites[0]._facingDir = Animation::kDirLeft; + _vm->_animation->_sprites[0]._x = 151; + _vm->_animation->_sprites[0]._moveX = -3; + _vm->_animation->_sprites[0]._moveY = -5; + + _vm->_gyro->setBackgroundColor(2); + } +} + +void Timer::riseUpOubliette() { + _vm->_animation->_sprites[0]._visible = true; + _vm->_animation->_sprites[0]._moveY++; // Decrements dx/dy! + _vm->_animation->_sprites[0]._y -= _vm->_animation->_sprites[0]._moveY; // Uuuupppp we go... + if (_vm->_animation->_sprites[0]._moveY > 0) + addTimer(3, kProcRiseUpOubliette, kReasonRisingUpOubliette); + else + _vm->_gyro->_dna._userMovesAvvy = true; +} + +void Timer::robinHoodAndGeida() { + _vm->_animation->_sprites[0].init(7, true, _vm->_animation); + _vm->_animation->appearPed(1, 7); + _vm->_animation->_sprites[0].walkTo(6); + _vm->_animation->_sprites[1].stopWalk(); + _vm->_animation->_sprites[1]._facingDir = Animation::kDirLeft; + addTimer(20, kProcRobinHoodAndGeidaTalk, kReasonRobinHoodAndGeida); + _vm->_gyro->_dna._geidaFollows = false; +} + +void Timer::robinHoodAndGeidaTalk() { + _vm->_visa->displayScrollChain('q', 66); + _vm->_animation->_sprites[0].walkTo(2); + _vm->_animation->_sprites[1].walkTo(2); + _vm->_animation->_sprites[0]._vanishIfStill = true; + _vm->_animation->_sprites[1]._vanishIfStill = true; + addTimer(162, kProcAvalotReturns, kReasonRobinHoodAndGeida); +} + +void Timer::avalotReturns() { + _vm->_animation->_sprites[0].remove(); + _vm->_animation->_sprites[1].remove(); + _vm->_animation->_sprites[0].init(0, true, _vm->_animation); + _vm->_animation->appearPed(1, 1); + _vm->_visa->displayScrollChain('q', 67); + _vm->_gyro->_dna._userMovesAvvy = true; +} + +/** + * This is used when you sit down in the pub in Notts. It loops around + * so that it will happen when Avvy stops walking. + * @remarks Originally called 'avvy_sit_down' + */ +void Timer::avvySitDown() { + if (_vm->_animation->_sprites[0]._homing) // Still walking. + addTimer(1, kProcAvvySitDown, kReasonSittingDown); + else { + _vm->_celer->drawBackgroundSprite(-1, -1, 3); + _vm->_gyro->_dna._sittingInPub = true; + _vm->_gyro->_dna._userMovesAvvy = false; + _vm->_animation->_sprites[0]._visible = false; + } +} + +void Timer::ghostRoomPhew() { + _vm->_scrolls->displayText(Common::String(_vm->_scrolls->kControlItalic) + "PHEW!" + _vm->_scrolls->kControlRoman + + " You're glad to get out of " + _vm->_scrolls->kControlItalic + "there!"); +} + +void Timer::arkataShouts() { + if (_vm->_gyro->_dna._teetotal) + return; + + _vm->_visa->displayScrollChain('q', 76); + addTimer(160, kProcArkataShouts, kReasonArkataShouts); +} + +void Timer::winning() { + _vm->_visa->displayScrollChain('q', 79); + _vm->_pingo->winningPic(); + + warning("STUB: Timer::winning()"); +#if 0 + do { + _vm->_lucerna->checkclick(); + } while (!(_vm->_gyro->mrelease == 0)); +#endif + // TODO: To be implemented with Pingo::winningPic(). + + _vm->_lucerna->callVerb(_vm->_acci->kVerbCodeScore); + _vm->_scrolls->displayText(" T H E E N D "); + _vm->_gyro->_letMeOut = true; +} + +void Timer::avalotFalls() { + if (_vm->_animation->_sprites[0]._stepNum < 5) { + _vm->_animation->_sprites[0]._stepNum++; + addTimer(3, kProcAvalotFalls, kReasonFallingOver); + } else { + Common::String toDisplay; + for (byte i = 0; i < 6; i++) + toDisplay += _vm->_scrolls->kControlNewLine; + for (byte i = 0; i < 6; i++) + toDisplay += _vm->_scrolls->kControlInsertSpaces; + toDisplay = toDisplay + _vm->_scrolls->kControlRegister + 'Z' + _vm->_scrolls->kControlIcon; + _vm->_scrolls->displayText(toDisplay); + } +} + +void Timer::spludwickGoesToCauldron() { + if (_vm->_animation->_sprites[1]._homing) + addTimer(1, kProcSpludwickGoesToCauldron, kReasonSpludwickWalk); + else + addTimer(17, kProcSpludwickLeavesCauldron, kReasonSpludwickWalk); +} + +void Timer::spludwickLeavesCauldron() { + _vm->_animation->_sprites[1]._callEachStepFl = true; // So that normal procs will continue. +} + +void Timer::giveLuteToGeida() { // Moved here from Acci. + _vm->_visa->displayScrollChain('Q', 86); + _vm->_lucerna->incScore(4); + _vm->_gyro->_dna._lustieIsAsleep = true; + _vm->_sequence->firstShow(5); + _vm->_sequence->thenShow(6); // He falls asleep... + _vm->_sequence->startToClose(); // Not really closing, but we're using the same procedure. +} + +} // End of namespace Avalanche. diff --git a/engines/avalanche/timer.h b/engines/avalanche/timer.h new file mode 100644 index 0000000000..134a4dc94e --- /dev/null +++ b/engines/avalanche/timer.h @@ -0,0 +1,180 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on the original source code of Lord Avalot d'Argent version 1.3. + * Copyright (c) 1994-1995 Mike, Mark and Thomas Thurman. + */ + +/* Original name: TIMEOUT The scheduling unit. */ + +#ifndef AVALANCHE_TIMER_H +#define AVALANCHE_TIMER_H + +#include "common/scummsys.h" + +namespace Avalanche { +class AvalancheEngine; + +class Timer { +public: + // Reason runs between 1 and 28. + enum Reason { + kReasonDrawbridgeFalls = 2, + kReasonAvariciusTalks = 3, + kReasonGoToToilet = 4, + kReasonExplosion = 5, + kReasonBrummieStairs = 6, + kReasonCardiffsurvey = 7, + kReasonCwytalotInHerts = 8, + kReasonGettingTiedUp = 9, + kReasonHangingAround = 10, // Tied to the tree in Nottingham. + kReasonJacquesWakingUp = 11, + kReasonNaughtyDuke = 12, + kReasonJumping = 13, + kReasonSequencer = 14, + kReasonCrapulusSaysSpludwickOut = 15, + kReasonDawndelay = 16, + kReasonDrinks = 17, + kReasonDuLustieTalks = 18, + kReasonFallingDownOubliette = 19, + kReasonMeetingAvaroid = 20, + kReasonRisingUpOubliette = 21, + kReasonRobinHoodAndGeida = 22, + kReasonSittingDown = 23, + kReasonGhostRoomPhew = 1, + kReasonArkataShouts = 24, + kReasonWinning = 25, + kReasonFallingOver = 26, + kReasonSpludwickWalk = 27, + kReasonGeidaSings = 28 + }; + + // Proc runs between 1 and 41. + enum Proc { + kProcOpenDrawbridge = 3, + kProcAvariciusTalks = 4, + kProcUrinate = 5, + kProcToilet = 6, + kProcBang = 7, + kProcBang2 = 8, + kProcStairs = 9, + kProcCardiffSurvey = 10, + kProcCardiffReturn = 11, + kProcCwytalotInHerts = 12, + kProcGetTiedUp = 13, + kProcGetTiedUp2 = 1, + kProcHangAround = 14, + kProcHangAround2 = 15, + kProcAfterTheShootemup = 32, + kProcJacquesWakesUp = 16, + kProcNaughtyDuke = 17, + kProcNaughtyDuke2 = 18, + kProcNaughtyDuke3 = 38, + kProcJump = 19, + kProcSequence = 20, + kProcCrapulusSpludOut = 21, + kProcDawnDelay = 22, + kProcBuyDrinks = 23, + kProcBuyWine = 24, + kProcCallsGuards = 25, + kProcGreetsMonk = 26, + kProcFallDownOubliette = 27, + kProcMeetAvaroid = 28, + kProcRiseUpOubliette = 29, + kProcRobinHoodAndGeida = 2, + kProcRobinHoodAndGeidaTalk = 30, + kProcAvalotReturns = 31, + kProcAvvySitDown = 33, // In Nottingham. + kProcGhostRoomPhew = 34, + kProcArkataShouts = 35, + kProcWinning = 36, + kProcAvalotFalls = 37, + kProcSpludwickGoesToCauldron = 39, + kProcSpludwickLeavesCauldron = 40, + kProcGiveLuteToGeida = 41 + }; + + struct TimerType { + int32 _timeLeft; + byte _action; + byte _reason; + }; + + TimerType _times[7]; + bool _timerLost; // Is the timer "lost"? (Because of using loseTimer()) + + Timer(AvalancheEngine *vm); + + void addTimer(int32 duration, byte action, byte reason); + void updateTimer(); + void loseTimer(byte which); + + // Procedures to do things at the end of amounts of time: + void openDrawbridge(); + void avariciusTalks(); + void urinate(); + void toilet(); + void bang(); + void bang2(); + void stairs(); + void cardiffSurvey(); + void cardiffReturn(); + void cwytalotInHerts(); + void getTiedUp(); + void getTiedUp2(); + void hangAround(); + void hangAround2(); + void afterTheShootemup(); + void jacquesWakesUp(); + void naughtyDuke(); + void naughtyDuke2(); + void naughtyDuke3(); + void jump(); + void crapulusSaysSpludOut(); + void buyDrinks(); + void buyWine(); + void callsGuards(); + void greetsMonk(); + void fallDownOubliette(); + void meetAvaroid(); + void riseUpOubliette(); + void robinHoodAndGeida(); + void robinHoodAndGeidaTalk(); + void avalotReturns(); + void avvySitDown(); + void ghostRoomPhew(); + void arkataShouts(); + void winning(); + void avalotFalls(); + void spludwickGoesToCauldron(); + void spludwickLeavesCauldron(); + void giveLuteToGeida(); + +private: + AvalancheEngine *_vm; + +}; + +} // End of namespace Avalanche. + +#endif // AVALANCHE_TIMER_H diff --git a/engines/avalanche/visa2.cpp b/engines/avalanche/visa2.cpp new file mode 100644 index 0000000000..81e46712b4 --- /dev/null +++ b/engines/avalanche/visa2.cpp @@ -0,0 +1,281 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on the original source code of Lord Avalot d'Argent version 1.3. + * Copyright (c) 1994-1995 Mike, Mark and Thomas Thurman. + */ + +/* VISA The new Sez handler. (Replaces Access.) */ + +#include "avalanche/avalanche.h" + +#include "avalanche/visa2.h" +#include "avalanche/gyro2.h" +#include "avalanche/scrolls2.h" +#include "avalanche/acci2.h" +#include "avalanche/lucerna2.h" +#include "avalanche/animation.h" + +#include "common/textconsole.h" + +namespace Avalanche { + +Visa::Visa(AvalancheEngine *vm) { + _vm = vm; + _noError = true; +} + +void Visa::unSkrimble() { + for (uint16 i = 0; i < _vm->_gyro->_bufSize; i++) + _vm->_gyro->_buffer[i] = (~(_vm->_gyro->_buffer[i] - (i + 1))) % 256; +} + +void Visa::doTheBubble() { + _vm->_gyro->_bufSize++; + _vm->_gyro->_buffer[_vm->_gyro->_bufSize - 1] = 2; +} + +/** + * Display a string in a scroll + * @remarks Originally called 'dixi' + */ +void Visa::displayScrollChain(char block, byte point, bool report, bool bubbling) { + Common::File indexfile; + if (!indexfile.open("avalot.idx")) { + warning("AVALANCHE: Visa: File not found: avalot.idx"); + return; + } + + bool error = false; + + indexfile.seek((toupper(block) - 65) * 2); + uint16 idx_offset = indexfile.readUint16LE(); + if (idx_offset == 0) + error = true; + + indexfile.seek(idx_offset + point * 2); + uint16 sez_offset = indexfile.readUint16LE(); + if (sez_offset == 0) + error = true; + + indexfile.close(); + + _noError = !error; + + if (error) { + if (report) { + Common::String todisplay; + todisplay.format("%cError accessing scroll %c%s", 7, block, _vm->_gyro->intToStr(point).c_str()); + _vm->_scrolls->displayText(todisplay); + } + return; + } + + Common::File sezfile; + if (!sezfile.open("avalot.sez")) { + warning("AVALANCHE: Visa: File not found: avalot.sez"); + return; + } + sezfile.seek(sez_offset); + _vm->_gyro->_bufSize = sezfile.readUint16LE(); + sezfile.read(_vm->_gyro->_buffer, _vm->_gyro->_bufSize); + sezfile.close(); + unSkrimble(); + + if (bubbling) + doTheBubble(); + + _vm->_scrolls->callScrollDriver(); +} + +/** + * Start speech + * @remarks Originally called 'speech' + */ +void Visa::speak(byte who, byte subject) { + if (subject == 0) { // No subject. + displayScrollChain('s', who, false, true); + return; + } + + // Subject given. + _noError = false; // Assume that until we know otherwise. + + Common::File indexfile; + if (!indexfile.open("converse.avd")) { + warning("AVALANCHE: Visa: File not found: converse.avd"); + return; + } + + indexfile.seek(who * 2 - 2); + uint16 idx_offset = indexfile.readUint16LE(); + uint16 next_idx_offset = indexfile.readUint16LE(); + + if ((idx_offset == 0) || ((((next_idx_offset - idx_offset) / 2) - 1) < subject)) + return; + + indexfile.seek(idx_offset + subject * 2); + uint16 sez_offset = indexfile.readUint16LE(); + if ((sez_offset == 0) || (indexfile.err())) + return; + indexfile.close(); + + Common::File sezfile; + if (!sezfile.open("avalot.sez")) { + warning("AVALANCHE: Visa: File not found: avalot.sez"); + return; + } + sezfile.seek(sez_offset); + _vm->_gyro->_bufSize = sezfile.readUint16LE(); + sezfile.read(_vm->_gyro->_buffer, _vm->_gyro->_bufSize); + sezfile.close(); + + unSkrimble(); + doTheBubble(); + + _vm->_scrolls->callScrollDriver(); + _noError = true; +} + +void Visa::talkTo(byte whom) { + if (_vm->_acci->_person == _vm->_acci->kPardon) { + _vm->_acci->_person = _vm->_gyro->_subjectNum; + _vm->_gyro->_subjectNum = 0; + } + + if (_vm->_gyro->_subjectNum == 0) { + switch (whom) { + case Gyro::kPeopleSpludwick: + if ((_vm->_gyro->_dna._lustieIsAsleep) & (!_vm->_gyro->_dna._objects[_vm->_gyro->kObjectPotion - 1])) { + displayScrollChain('q', 68); + _vm->_gyro->_dna._objects[_vm->_gyro->kObjectPotion - 1] = true; + _vm->_lucerna->refreshObjectList(); + _vm->_lucerna->incScore(3); + return; + } else if (_vm->_gyro->_dna._talkedToCrapulus) { + // Spludwick - what does he need? + // 0 - let it through to use normal routine. + switch (_vm->_gyro->_dna._givenToSpludwick) { + case 1: // Fallthrough is intended. + case 2: + _vm->_scrolls->displayText(Common::String("Can you get me ") + _vm->_gyro->getItem(_vm->_gyro->kSpludwicksOrder[_vm->_gyro->_dna._givenToSpludwick]) + ", please?" + _vm->_scrolls->kControlRegister + '2' + _vm->_scrolls->kControlSpeechBubble); + return; + case 3: + displayScrollChain('q', 30); // Need any help with the game? + return; + } + } else { + displayScrollChain('q', 42); // Haven't talked to Crapulus. Go and talk to him. + return; + } + break; + case Gyro::kPeopleIbythneth: + if (_vm->_gyro->_dna._givenBadgeToIby) { + displayScrollChain('q', 33); // Thanks a lot! + return; // And leave the proc. + } + break; // Or... just continue, 'cos he hasn't got it. + case Gyro::kPeopleDogfood: + if (_vm->_gyro->_dna._wonNim) { // We've won the game. + displayScrollChain('q', 6); // "I'm Not Playing!" + return; // Zap back. + } else + _vm->_gyro->_dna._askedDogfoodAboutNim = true; + break; + case Gyro::kPeopleAyles: + if (!_vm->_gyro->_dna._aylesIsAwake) { + displayScrollChain('q', 43); // He's fast asleep! + return; + } else if (!_vm->_gyro->_dna._givenPenToAyles) { + displayScrollChain('q', 44); // Can you get me a pen, Avvy? + return; + } + break; + + case Gyro::kPeopleJacques: + displayScrollChain('q', 43); + return; + + case Gyro::kPeopleGeida: + if (_vm->_gyro->_dna._givenPotionToGeida) + _vm->_gyro->_dna._geidaFollows = true; + else { + displayScrollChain('u', 17); + return; + } + break; + case Gyro::kPeopleSpurge: + if (!_vm->_gyro->_dna._sittingInPub) { + displayScrollChain('q', 71); // Try going over and sitting down. + return; + } else { + if (_vm->_gyro->_dna._spurgeTalkCount < 5) + _vm->_gyro->_dna._spurgeTalkCount++; + if (_vm->_gyro->_dna._spurgeTalkCount > 1) { // no. 1 falls through + displayScrollChain('q', 70 + _vm->_gyro->_dna._spurgeTalkCount); + return; + } + } + break; + } + // On a subject. Is there any reason to block it? + } else if ((whom == _vm->_gyro->kPeopleAyles) && (!_vm->_gyro->_dna._aylesIsAwake)) { + displayScrollChain('q', 43); // He's fast asleep! + return; + } + + if (whom > 149) + whom -= 149; + + bool noMatches = true; + for (byte i = 0; i <= _vm->_animation->kSpriteNumbMax; i++) { + if (_vm->_animation->_sprites[i]._stat._acciNum == whom) { + _vm->_scrolls->displayText(Common::String(_vm->_scrolls->kControlRegister) + (i + 49) + _vm->_scrolls->kControlToBuffer); + noMatches = false; + break; + } + } + + if (noMatches) + _vm->_scrolls->displayText(Common::String(_vm->_scrolls->kControlRegister) + _vm->_scrolls->kControlRegister + _vm->_scrolls->kControlToBuffer); + + speak(whom, _vm->_gyro->_subjectNum); + + if (!_noError) + displayScrollChain('n', whom); // File not found! + + if ((_vm->_gyro->_subjectNum == 0) && ((whom + 149) == _vm->_gyro->kPeopleCrapulus)) { // Crapulus: get the badge - first time only + _vm->_gyro->_dna._objects[_vm->_gyro->kObjectBadge - 1] = true; + _vm->_lucerna->refreshObjectList(); + displayScrollChain('q', 1); // Circular from Cardiff. + _vm->_gyro->_dna._talkedToCrapulus = true; + _vm->_gyro->_whereIs[_vm->_gyro->kPeopleCrapulus - 150] = 177; // Crapulus walks off. + + _vm->_animation->_sprites[1]._vanishIfStill = true; + _vm->_animation->_sprites[1].walkTo(3); // Walks away. + + _vm->_lucerna->incScore(2); + } +} + +} // End of namespace Avalanche. diff --git a/engines/avalanche/visa2.h b/engines/avalanche/visa2.h new file mode 100644 index 0000000000..c811053fab --- /dev/null +++ b/engines/avalanche/visa2.h @@ -0,0 +1,56 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on the original source code of Lord Avalot d'Argent version 1.3. + * Copyright (c) 1994-1995 Mike, Mark and Thomas Thurman. + */ + +/* VISA The new Sez handler. (Replaces Access.) */ + +#ifndef AVALANCHE_VISA2_H +#define AVALANCHE_VISA2_H + +#include "common/scummsys.h" + +namespace Avalanche { +class AvalancheEngine; + +class Visa { +public: + Visa(AvalancheEngine *vm); + + void displayScrollChain(char block, byte point, bool report = true, bool bubbling = false); + void talkTo(byte whom); + +private: + AvalancheEngine *_vm; + + bool _noError; + void unSkrimble(); + void doTheBubble(); + void speak(byte who, byte subject); +}; + +} // End of namespace Avalanche. + +#endif // AVALANCHE_VISA2_H diff --git a/engines/configure.engines b/engines/configure.engines index 963b9f774f..7004f4111f 100644 --- a/engines/configure.engines +++ b/engines/configure.engines @@ -6,6 +6,7 @@ add_engine he "HE71+ games" yes add_engine agi "AGI" yes add_engine agos "AGOS" yes "agos2" "AGOS 1 games" add_engine agos2 "AGOS 2 games" yes +add_engine avalanche "Lord Avalot d'Argent" no add_engine cge "CGE" yes add_engine cine "Cinematique evo 1" yes add_engine composer "Magic Composer" yes diff --git a/engines/engines.mk b/engines/engines.mk index f58dba0d6d..3401df37e5 100644 --- a/engines/engines.mk +++ b/engines/engines.mk @@ -26,6 +26,11 @@ DEFINES += -DENABLE_AGOS2 endif endif +ifdef ENABLE_AVALANCHE +DEFINES += -DENABLE_AVALANCHE=$(ENABLE_AVALANCHE) +MODULES += engines/avalanche +endif + ifdef ENABLE_CGE DEFINES += -DENABLE_CGE=$(ENABLE_CGE) MODULES += engines/cge diff --git a/engines/plugins_table.h b/engines/plugins_table.h index edc94eb0d3..d1519c367f 100644 --- a/engines/plugins_table.h +++ b/engines/plugins_table.h @@ -8,6 +8,9 @@ LINK_PLUGIN(AGI) #if PLUGIN_ENABLED_STATIC(AGOS) LINK_PLUGIN(AGOS) #endif +#if PLUGIN_ENABLED_STATIC(AVALANCHE) +LINK_PLUGIN(AVALANCHE) +#endif #if PLUGIN_ENABLED_STATIC(CGE) LINK_PLUGIN(CGE) #endif |