aboutsummaryrefslogtreecommitdiff
path: root/engines/avalanche
diff options
context:
space:
mode:
Diffstat (limited to 'engines/avalanche')
-rw-r--r--engines/avalanche/acci2.cpp2060
-rw-r--r--engines/avalanche/acci2.h133
-rw-r--r--engines/avalanche/animation.cpp1460
-rw-r--r--engines/avalanche/animation.h160
-rw-r--r--engines/avalanche/avalanche.cpp732
-rw-r--r--engines/avalanche/avalanche.h175
-rw-r--r--engines/avalanche/avalot.cpp237
-rw-r--r--engines/avalanche/avalot.h53
-rw-r--r--engines/avalanche/celer2.cpp391
-rw-r--r--engines/avalanche/celer2.h81
-rw-r--r--engines/avalanche/closing2.cpp86
-rw-r--r--engines/avalanche/closing2.h71
-rw-r--r--engines/avalanche/color.h58
-rw-r--r--engines/avalanche/console.cpp39
-rw-r--r--engines/avalanche/console.h48
-rw-r--r--engines/avalanche/detection.cpp220
-rw-r--r--engines/avalanche/dropdown2.cpp815
-rw-r--r--engines/avalanche/dropdown2.h167
-rw-r--r--engines/avalanche/enid2.cpp302
-rw-r--r--engines/avalanche/enid2.h78
-rw-r--r--engines/avalanche/graphics.cpp320
-rw-r--r--engines/avalanche/graphics.h103
-rw-r--r--engines/avalanche/gyro2.cpp515
-rw-r--r--engines/avalanche/gyro2.h424
-rw-r--r--engines/avalanche/lucerna2.cpp1262
-rw-r--r--engines/avalanche/lucerna2.h124
-rw-r--r--engines/avalanche/module.mk30
-rw-r--r--engines/avalanche/parser.cpp173
-rw-r--r--engines/avalanche/parser.h68
-rw-r--r--engines/avalanche/pingo2.cpp152
-rw-r--r--engines/avalanche/pingo2.h60
-rw-r--r--engines/avalanche/roomnums.h88
-rw-r--r--engines/avalanche/scrolls2.cpp827
-rw-r--r--engines/avalanche/scrolls2.h123
-rw-r--r--engines/avalanche/sequence2.cpp108
-rw-r--r--engines/avalanche/sequence2.h62
-rw-r--r--engines/avalanche/timer.cpp684
-rw-r--r--engines/avalanche/timer.h180
-rw-r--r--engines/avalanche/visa2.cpp281
-rw-r--r--engines/avalanche/visa2.h56
40 files changed, 13006 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