1870 lines
49 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
/*
* This code is based on the original source code of Lord Avalot d'Argent version 1.3.
* Copyright (c) 1994-1995 Mike: Mark and Thomas Thurman.
*/
/* AVALOT The kernel of the program. */
#include "avalanche/avalanche.h"
#include "common/file.h"
#include "common/random.h"
#include "common/str.h"
#include "common/textconsole.h"
#include "common/config-manager.h"
namespace Avalanche {
const char *AvalancheEngine::kVersionNum = "1.30";
const char *AvalancheEngine::kCopyright = "1995";
const MouseHotspotType AvalancheEngine::kMouseHotSpots[9] = {
{8,0}, // 0 - up-arrow
{0,0}, // 1 - screwdriver
{15,6}, // 2 - right-arrow
{0,0}, // 3 - fletch
{8,7}, // 4 - hourglass
{4,0}, // 5 - TTHand
{8,5}, // 6 - Mark's crosshairs
{8,7}, // 7 - I-beam
{0,0} // 8 - question mark
};
// Art gallery at 2,1; notice about this at 2,2.
const int32 AvalancheEngine::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 AvalancheEngine::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 AvalancheEngine::kQuasipeds[16] = {
//_whichPed, _foregroundColor, _room, _backgroundColor, _who
{1, kColorLightgray, kRoomArgentPub, kColorBrown, kPeopleDogfood}, // A: Dogfood (screen 19).
{2, kColorGreen, kRoomArgentPub, kColorWhite, kPeopleIbythneth}, // B: Ibythneth (screen 19).
{2, kColorWhite, kRoomYours, kColorMagenta, kPeopleArkata}, // C: Arkata (screen 1).
{2, kColorBlack, kRoomLustiesRoom, kColorRed, kPeopleInvisible}, // D: Hawk (screen 23).
{2, kColorLightgreen, kRoomOutsideDucks, kColorBrown, kPeopleTrader}, // E: Trader (screen 50).
{5, kColorYellow, kRoomRobins, kColorRed, kPeopleAvalot}, // F: Avvy, tied up (scr.42)
{1, kColorBlue, kRoomAylesOffice, kColorWhite, kPeopleAyles}, // G: Ayles (screen 16).
{1, kColorBrown, kRoomMusicRoom, kColorWhite, kPeopleJacques}, // H: Jacques (screen 7).
{1, kColorLightgreen, kRoomNottsPub, kColorGreen, kPeopleSpurge}, // I: Spurge (screen 47).
{2, kColorYellow, kRoomNottsPub, kColorRed, kPeopleAvalot}, // J: Avalot (screen 47).
{1, kColorLightgray, kRoomLustiesRoom, kColorBlack, kPeopleDuLustie}, // K: du Lustie (screen 23).
{1, kColorYellow, kRoomOubliette, kColorRed, kPeopleAvalot}, // L: Avalot (screen 27).
{2, kColorWhite, kRoomOubliette, kColorRed, kPeopleInvisible}, // M: Avaroid (screen 27).
{3, kColorLightgray, kRoomArgentPub, kColorDarkgray, kPeopleMalagauche},// N: Malagauche (screen 19).
{4, kColorLightmagenta, kRoomNottsPub, kColorRed, kPeoplePort}, // O: Port (screen 47).
{1, kColorLightgreen, kRoomDucks, kColorDarkgray, kPeopleDrDuck} // P: Duck (screen 51).
};
const uint16 AvalancheEngine::kNotes[12] = {196, 220, 247, 262, 294, 330, 350, 392, 440, 494, 523, 587};
const TuneType AvalancheEngine::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
};
Room AvalancheEngine::_whereIs[29] = {
// The Lads
kRoomYours, // Avvy
kRoomSpludwicks, // Spludwick
kRoomOutsideYours, // Crapulus
kRoomDucks, // Duck - r__DucksRoom's not defined yet.
kRoomArgentPub, // Malagauche
kRoomRobins, // Friar Tuck.
kRoomDummy, // Robin Hood - can't meet him at the start.
kRoomBrummieRoad, // Cwytalot
kRoomLustiesRoom, // Baron du Lustie.
kRoomOutsideCardiffCastle, // The Duke of Cardiff.
kRoomArgentPub, // Dogfood
kRoomOutsideDucks, // Trader
kRoomArgentPub, // Ibythneth
kRoomAylesOffice, // Ayles
kRoomNottsPub, // Port
kRoomNottsPub, // Spurge
kRoomMusicRoom, // Jacques
kRoomNowhere,
kRoomNowhere,
kRoomNowhere,
kRoomNowhere,
kRoomNowhere,
kRoomNowhere,
kRoomNowhere,
kRoomNowhere,
// The Lasses
kRoomYours, // Arkata
kRoomGeidas, // Geida
kRoomDummy, // nobody allocated here!
kRoomWiseWomans // The Wise Woman.
};
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)) {
Common::String tmpStr = Common::String::format("Good morning!%c%cYes, it's just past " \
"midnight. Are you having an all-night Avvy session? Glad you like the game that much!",
Dialogs::kControlNewLine, Dialogs::kControlNewLine);
_vm->_dialogs->displayText(tmpStr);
}
_oldHour = _hour;
_oldHourAngle = _hourAngle;
_oldMinute = _minute;
}
void Clock::calcHand(uint16 angle, uint16 length, Common::Point &endPoint, Color 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, Color 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() {
if ((_oldHour == 17717) || (!_vm->_soundFx)) // Too high - must be first time around
return;
byte hour = _hour % 12;
if (hour == 0)
hour = 12;
_vm->setMousePointerWait();
for (int i = 1; i <= hour; i++) {
for (int j = 1; j <= 3; j++)
_vm->_sound->playNote((i % 3) * 64 + 140 - j * 30, 50 - j * 12);
if (i != hour)
_vm->_system->delayMillis(100);
}
}
void AvalancheEngine::handleKeyDown(Common::Event &event) {
_sound->click();
if ((Common::KEYCODE_F1 <= event.kbd.keycode) && (event.kbd.keycode <= Common::KEYCODE_F15))
_parser->handleFunctionKey(event);
else if ((32 <= event.kbd.ascii) && (event.kbd.ascii <= 128) && (event.kbd.ascii != 47))
_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 (_alive && _avvyIsAwake) {
_animation->handleMoveKey(event); // Fallthroughs are intended.
drawDirection();
return;
}
case Common::KEYCODE_BACKSPACE:
_parser->handleBackspace();
break;
case Common::KEYCODE_RETURN:
_parser->handleReturn();
break;
default:
break;
}
drawDirection();
}
void AvalancheEngine::setup() {
init();
_dialogs->reset();
dusk();
_graphics->loadDigits(file);
_parser->_inputTextPos = 0;
_parser->_quote = true;
_animation->resetAnims();
drawToolbar();
_dialogs->setReadyLight(2);
dawn();
_parser->_cursorState = false;
_parser->cursorOn();
_animation->_sprites[0]._speedX = kWalk;
_animation->updateSpeed();
_menu->init();
int16 loadSlot = Common::ConfigManager::instance().getInt("save_slot");
if (loadSlot >= 0) {
_thinks = 2; // You always have money.
thinkAbout(kObjectMoney, kThing);
loadGame(loadSlot);
} else {
_isLoaded = false; // Set to true in _vm->loadGame().
newGame();
_soundFx = !_soundFx;
fxToggle();
thinkAbout(kObjectMoney, kThing);
_dialogs->displayScrollChain('q', 83); // Info on the game, etc.
}
}
void AvalancheEngine::runAvalot() {
setup();
do {
uint32 beginLoop = _system->getMillis();
updateEvents(); // The event handler.
_clock->update();
_menu->update();
_background->update();
_animation->animLink();
checkClick();
_timer->updateTimer();
#ifdef DEBUG
for (int i = 0; i < _lineNum; i++) {
LineType *curLine = &_lines[i];
_graphics->_surface.drawLine(curLine->_x1, curLine->_y1, curLine->_x2, curLine->_y2, curLine->col);
}
for (int i = 0; i < _fieldNum; i++) {
FieldType *curField = &_fields[i];
if (curField->_x1 < 640)
_graphics->_surface.frameRect(Common::Rect(curField->_x1, curField->_y1, curField->_x2, curField->_y2), kColorLightmagenta);
}
#endif
_graphics->refreshScreen();
uint32 delay = _system->getMillis() - beginLoop;
if (delay <= 55)
_system->delayMillis(55 - delay); // Replaces slowdown(); 55 comes from 18.2 Hz (B Flight).
} while (!_letMeOut && !shouldQuit());
warning("STUB: run()");
_closing->exitGame();
}
void AvalancheEngine::init() {
for (int i = 0; i < 31; i++) {
for (int j = 0; j < 2; j++)
_also[i][j] = nullptr;
}
#if 0
if (_vm->_enhanced->atbios)
atkey = "f1";
else
atkey = "alt-";
#endif
_letMeOut = false;
_holdTheDawn = true;
_currentMouse = 177;
_dropsOk = true;
_mouseText = "";
_cheat = false;
_cp = 0;
_ledStatus = 177;
_defaultLed = 2;
for (int i = 0; i < 3; i++)
_scoreToDisplay[i] = -1; // Impossible digits.
_holdTheDawn = false;
setMousePointerWait();
CursorMan.showMouse(true);
}
/**
* Call a given Verb
* @remarks Originally called 'callverb'
*/
void AvalancheEngine::callVerb(VerbCode id) {
if (id == _parser->kPardon) {
Common::String tmpStr = Common::String::format("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.");
_dialogs->displayText(tmpStr);
} else {
_weirdWord = false;
_parser->_polite = true;
_parser->_verb = id;
_parser->doThat();
}
}
void AvalancheEngine::drawAlsoLines() {
CursorMan.showMouse(false);
_graphics->_magics.fillRect(Common::Rect(0, 0, 640, 200), 0);
_graphics->_magics.frameRect(Common::Rect(0, 45, 640, 161), 15);
for (int i = 0; i < _lineNum; i++) {
// We had to check if the lines are within the borders of the screen.
if ((_lines[i]._x1 >= 0) && (_lines[i]._x1 < _graphics->kScreenWidth) && (_lines[i]._y1 >= 0) && (_lines[i]._y1 < _graphics->kScreenHeight)
&& (_lines[i]._x2 >= 0) && (_lines[i]._x2 < _graphics->kScreenWidth) && (_lines[i]._y2 >= 0) && (_lines[i]._y2 < _graphics->kScreenHeight))
_graphics->_magics.drawLine(_lines[i]._x1, _lines[i]._y1, _lines[i]._x2, _lines[i]._y2, _lines[i]._color);
}
CursorMan.showMouse(true);
}
/**
* Check is it's possible to give something to Spludwick
* @remarks Originally called 'nextstring'
*/
Common::String AvalancheEngine::readAlsoStringFromFile() {
Common::String str;
byte length = file.readByte();
for (int i = 0; i < length; i++)
str += file.readByte();
return str;
}
void AvalancheEngine::scram(Common::String &str) {
for (uint i = 0; i < str.size(); i++)
str.setChar(str[i] ^ 177, i);
}
void AvalancheEngine::unScramble() {
for (int i = 0; i < 31; i++) {
for (int j = 0; j < 2; j++) {
if (_also[i][j] != nullptr)
scram(*_also[i][j]);
}
}
scram(_listen);
scram(_flags);
}
void AvalancheEngine::loadAlso(byte num) {
for (int i = 0; i < 31; i++) {
for (int j = 0; j < 2; j++) {
if (_also[i][j] != nullptr) {
delete _also[i][j];
_also[i][j] = nullptr;
}
}
}
Common::String filename;
filename = Common::String::format("also%d.avd", num);
if (!file.open(filename))
error("AVALANCHE: File not found: %s", filename.c_str());
file.seek(128);
byte alsoNum = file.readByte();
Common::String tmpStr;
for (int i = 0; i <= alsoNum; i++) {
for (int j = 0; j < 2; j++) {
_also[i][j] = new Common::String;
*_also[i][j] = readAlsoStringFromFile();
}
tmpStr = Common::String::format("\x9D%s\x9D", _also[i][0]->c_str());
*_also[i][0] = tmpStr;
}
memset(_lines, 0xFF, sizeof(_lines));
_lineNum = file.readByte();
for (int i = 0; i < _lineNum; i++) {
LineType *curLine = &_lines[i];
curLine->_x1 = file.readSint16LE();
curLine->_y1 = file.readSint16LE();
curLine->_x2 = file.readSint16LE();
curLine->_y2 = file.readSint16LE();
curLine->_color = (Color)file.readByte();
}
memset(_peds, 177, sizeof(_peds));
byte pedNum = file.readByte();
for (int i = 0; i < pedNum; i++) {
PedType *curPed = &_peds[i];
curPed->_x = file.readSint16LE();
curPed->_y = file.readSint16LE();
curPed->_direction = (Direction)file.readByte();
}
_fieldNum = file.readByte();
for (int i = 0; i < _fieldNum; i++) {
FieldType *curField = &_fields[i];
curField->_x1 = file.readSint16LE();
curField->_y1 = file.readSint16LE();
curField->_x2 = file.readSint16LE();
curField->_y2 = file.readSint16LE();
}
for (int i = 0; i < 15; i++) {
MagicType *magic = &_magics[i];
magic->_operation = file.readByte();
magic->_data = file.readUint16LE();
}
for (int i = 0; i < 7; i++) {
MagicType *portal = &_portals[i];
portal->_operation = file.readByte();
portal->_data = file.readUint16LE();
}
_flags.clear();
for (int i = 0; i < 26; i++)
_flags += file.readByte();
int16 listen_length = file.readByte();
_listen.clear();
for (int i = 0; i < listen_length; i++)
_listen += file.readByte();
drawAlsoLines();
file.close();
unScramble();
for (int i = 0; i <= alsoNum; i++) {
tmpStr = Common::String::format(",%s,", _also[i][0]->c_str());
*_also[i][0] = tmpStr;
}
}
void AvalancheEngine::loadRoom(byte num) {
CursorMan.showMouse(false);
_graphics->fleshColors();
Common::String filename = Common::String::format("place%d.avd", num);
if (!file.open(filename))
error("AVALANCHE: File not found: %s", filename.c_str());
file.seek(146);
if (!_roomnName.empty())
_roomnName.clear();
for (int i = 0; i < 30; i++) {
char actChar = file.readByte();
if ((32 <= actChar) && (actChar <= 126))
_roomnName += actChar;
}
// Compression method byte follows this...
file.seek(177);
_graphics->loadBackground(file);
_graphics->refreshBackground();
file.close();
loadAlso(num);
_background->load(num);
CursorMan.showMouse(true);
}
void AvalancheEngine::findPeople(byte room) {
for (int i = 1; i < 29; i++) {
if (_whereIs[i] == room) {
if (i < 25)
_him = (People)(150 + i);
else
_her = (People)(150 + i);
}
}
}
void AvalancheEngine::exitRoom(byte x) {
_sound->stopSound();
_background->release();
_seeScroll = true; // This stops the trippancy system working over the length of this procedure.
switch (x) {
case kRoomSpludwicks:
_timer->loseTimer(Timer::kReasonAvariciusTalks);
_avariciusTalk = 0;
// He doesn't HAVE to be talking for this to work. It just deletes it IF it exists.
break;
case kRoomBridge:
if (_drawbridgeOpen > 0) {
_drawbridgeOpen = 4; // Fully open.
_timer->loseTimer(Timer::kReasonDrawbridgeFalls);
}
break;
case kRoomOutsideCardiffCastle:
_timer->loseTimer(Timer::kReasonCardiffsurvey);
break;
case kRoomRobins:
_timer->loseTimer(Timer::kReasonGettingTiedUp);
break;
}
_interrogation = 0; // Leaving the room cancels all the questions automatically.
_seeScroll = false; // Now it can work again!
_lastRoom = _room;
if (_room != kRoomMap)
_lastRoomNotMap = _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 AvalancheEngine::enterNewTown() {
_menu->setup();
switch (_room) {
case kRoomOutsideNottsPub: // Entry into Nottingham.
if ((_roomCount[kRoomRobins] > 0) && (_beenTiedUp) && (!_takenMushroom))
_mushroomGrowing = true;
break;
case kRoomWiseWomans: // Entry into Argent.
if (_talkedToCrapulus && (!_lustieIsAsleep)) {
_spludwickAtHome = !((_roomCount[kRoomWiseWomans] % 3) == 1);
_crapulusWillTell = !_spludwickAtHome;
} else {
_spludwickAtHome = true;
_crapulusWillTell = false;
}
if (_boxContent == kObjectWine)
_wineState = 3; // Vinegar
break;
}
if ((_room != kRoomOutsideDucks) && (_objects[kObjectOnion - 1]) && !(_onionInVinegar))
_rottenOnion = true; // You're holding the onion
}
void AvalancheEngine::putGeidaAt(byte whichPed, byte ped) {
if (ped == 0)
return;
AnimationType *spr1 = &_animation->_sprites[1];
spr1->init(5, false, _animation); // load Geida
_animation->appearPed(1, whichPed);
spr1->_callEachStepFl = true;
spr1->_eachStepProc = Animation::kProcGeida;
}
void AvalancheEngine::enterRoom(Room roomId, byte ped) {
_seeScroll = true; // This stops the trippancy system working over the length of this procedure.
findPeople(roomId);
_room = roomId;
if (ped != 0)
_roomCount[roomId]++;
loadRoom(roomId);
if ((_roomCount[roomId] == 0) && (!setFlag('S')))
incScore(1);
_whereIs[kPeopleAvalot - 150] = _room;
if (_geidaFollows)
_whereIs[kPeopleGeida - 150] = roomId;
_roomTime = 0;
if ((_lastRoom == kRoomMap) && (_lastRoomNotMap != _room))
enterNewTown();
switch (roomId) {
case kRoomYours:
if (_avvyInBed) {
_background->draw(-1, -1, 2);
_graphics->refreshBackground();
_timer->addTimer(100, Timer::kProcArkataShouts, Timer::kReasonArkataShouts);
}
break;
case kRoomOutsideYours:
if (ped > 0) {
AnimationType *spr1 = &_animation->_sprites[1];
if (!_talkedToCrapulus) {
_whereIs[kPeopleCrapulus - 150] = kRoomOutsideYours;
spr1->init(8, false, _animation); // load Crapulus
if (_roomCount[kRoomOutsideYours] == 1) {
_animation->appearPed(1, 3); // Start on the right-hand side of the screen.
spr1->walkTo(4); // Walks up to greet you.
} else {
_animation->appearPed(1, 4); // Starts where he was before.
spr1->_facingDir = kDirLeft;
}
spr1->_callEachStepFl = true;
spr1->_eachStepProc = Animation::kProcFaceAvvy; // He always faces Avvy.
} else
_whereIs[kPeopleCrapulus - 150] = kRoomNowhere;
if (_crapulusWillTell) {
spr1->init(8, false, _animation);
_animation->appearPed(1, 1);
spr1->walkTo(3);
_timer->addTimer(20, Timer::kProcCrapulusSpludOut, Timer::kReasonCrapulusSaysSpludwickOut);
_crapulusWillTell = false;
}
}
break;
case kRoomOutsideSpludwicks:
if ((_roomCount[kRoomOutsideSpludwicks] == 1) && (ped == 1)) {
_timer->addTimer(20, Timer::kProcBang, Timer::kReasonExplosion);
_spludwickAtHome = true;
}
break;
case kRoomSpludwicks:
if (_spludwickAtHome) {
AnimationType *spr1 = &_animation->_sprites[1];
if (ped > 0) {
spr1->init(2, false, _animation); // load Spludwick
_animation->appearPed(1, 1);
_whereIs[kPeopleSpludwick - 150] = kRoomSpludwicks;
}
spr1->_callEachStepFl = true;
spr1->_eachStepProc = Animation::kProcGeida;
} else
_whereIs[kPeopleSpludwick - 150] = kRoomNowhere;
break;
case kRoomBrummieRoad:
if (_geidaFollows)
putGeidaAt(4, ped);
if (_cwytalotGone) {
_magics[kColorLightred - 1]._operation = kMagicNothing;
_whereIs[kPeopleCwytalot - 150] = kRoomNowhere;
} else if (ped > 0) {
AnimationType *spr1 = &_animation->_sprites[1];
spr1->init(4, false, _animation); // 4 = Cwytalot
spr1->_callEachStepFl = true;
spr1->_eachStepProc = Animation::kProcFollowAvvyY;
_whereIs[kPeopleCwytalot - 150] = kRoomBrummieRoad;
if (_roomCount[kRoomBrummieRoad] == 1) { // First time here...
_animation->appearPed(1, 1); // He appears on the right of the screen...
spr1->walkTo(3); // ...and he walks up...
} else {
// You've been here before.
_animation->appearPed(1, 3); // He's standing in your way straight away...
spr1->_facingDir = kDirLeft;
}
}
break;
case kRoomArgentRoad:
if ((_cwytalotGone) && (!_passedCwytalotInHerts) && (ped == 2) && (_roomCount[kRoomArgentRoad] > 3)) {
AnimationType *spr1 = &_animation->_sprites[1];
spr1->init(4, false, _animation); // 4 = Cwytalot again
_animation->appearPed(1, 0);
spr1->walkTo(1);
spr1->_vanishIfStill = true;
_passedCwytalotInHerts = true;
// whereis[#157] = r__Nowhere; // can we fit this in?
_timer->addTimer(20, Timer::kProcCwytalotInHerts, Timer::kReasonCwytalotInHerts);
}
break;
case kRoomBridge:
if (_drawbridgeOpen == 4) { // open
_background->draw(-1, -1, 2); // Position of drawbridge
_graphics->refreshBackground();
_magics[kColorGreen - 1]._operation = kMagicNothing; // You may enter the drawbridge.
}
if (_geidaFollows)
putGeidaAt(ped + 2, ped); // load Geida
break;
case kRoomRobins:
if ((ped > 0) && (!_beenTiedUp)) {
// A welcome party... or maybe not...
AnimationType *spr1 = &_animation->_sprites[1];
spr1->init(6, false, _animation);
_animation->appearPed(1, 1);
spr1->walkTo(2);
_timer->addTimer(36, Timer::kProcGetTiedUp, Timer::kReasonGettingTiedUp);
}
if (_beenTiedUp) {
_whereIs[kPeopleRobinHood - 150] = kRoomNowhere;
_whereIs[kPeopleFriarTuck - 150] = kRoomNowhere;
}
if (_tiedUp)
_background->draw(-1, -1, 1);
if (!_mushroomGrowing)
_background->draw(-1, -1, 2);
_graphics->refreshBackground();
break;
case kRoomOutsideCardiffCastle:
if (ped > 0) {
AnimationType *spr1 = &_animation->_sprites[1];
switch (_cardiffQuestionNum) {
case 0 : // You've answered NONE of his questions.
spr1->init(9, false, _animation);
_animation->appearPed(1, 1);
spr1->walkTo(2);
_timer->addTimer(47, Timer::kProcCardiffSurvey, Timer::kReasonCardiffsurvey);
break;
case 5 :
_magics[1]._operation = kMagicNothing;
break; // You've answered ALL his questions. => nothing happens.
default: // You've answered SOME of his questions.
spr1->init(9, false, _animation);
_animation->appearPed(1, 2);
spr1->_facingDir = kDirRight;
_timer->addTimer(3, Timer::kProcCardiffReturn, Timer::kReasonCardiffsurvey);
}
}
if (_cardiffQuestionNum < 5)
_interrogation = _cardiffQuestionNum;
else
_interrogation = 0;
break;
case kRoomMap:
// You're entering the map.
dawn();
if (ped > 0)
_graphics->zoomOut(_peds[ped]._x, _peds[ped]._y);
if ((_objects[kObjectWine - 1]) && (_wineState != 3)) {
_dialogs->displayScrollChain('q', 9); // Don't want to waste the wine!
_objects[kObjectWine - 1] = false;
refreshObjectList();
}
_dialogs->displayScrollChain('q', 69);
break;
case kRoomCatacombs:
if ((ped == 0) || (ped == 3) || (ped == 5) || (ped == 6)) {
switch (ped) {
case 3: // Enter from oubliette
_catacombX = 8;
_catacombY = 4;
break;
case 5: // Enter from du Lustie's
_catacombX = 8;
_catacombY = 7;
break;
case 6: // Enter from Geida's
_catacombX = 4;
_catacombY = 1;
break;
}
_enterCatacombsFromLustiesRoom = true;
_animation->catacombMove(ped);
_enterCatacombsFromLustiesRoom = false;
}
break;
case kRoomArgentPub:
if (_wonNim)
_background->draw(-1, -1, 0); // No lute by the settle.
_malagauche = 0; // Ready to boot Malagauche
if (_givenBadgeToIby) {
_background->draw(-1, -1, 7);
_background->draw(-1, -1, 8);
}
_graphics->refreshBackground();
break;
case kRoomLustiesRoom:
_npcFacing = 1; // du Lustie.
if (_animation->_sprites[0]._id == 0) // Avvy in his normal clothes
_timer->addTimer(3, Timer::kProcCallsGuards, Timer::kReasonDuLustieTalks);
else if (!_enteredLustiesRoomAsMonk) // already
// Presumably, Avvy dressed as a monk.
_timer->addTimer(3, Timer::kProcGreetsMonk, Timer::kReasonDuLustieTalks);
if (_geidaFollows) {
putGeidaAt(4, ped);
if (_lustieIsAsleep) {
_background->draw(-1, -1, 4);
_graphics->refreshBackground();
}
}
break;
case kRoomMusicRoom:
if (_jacquesState > 0) {
_jacquesState = 5;
_background->draw(-1, -1, 1);
_graphics->refreshBackground();
_background->draw(-1, -1, 3);
_magics[kColorBrown - 1]._operation = kMagicNothing;
_whereIs[kPeopleJacques - 150] = kRoomNowhere;
}
if (ped != 0) {
_background->draw(-1, -1, 5);
_graphics->refreshBackground();
_sequence->startMusicRoomSeq();
}
break;
case kRoomOutsideNottsPub:
if (ped == 2) {
_background->draw(-1, -1, 2);
_graphics->refreshBackground();
_sequence->startDuckSeq();
}
break;
case kRoomOutsideArgentPub:
if (ped == 2) {
_background->draw(-1, -1, 5);
_graphics->refreshBackground();
_sequence->startMusicRoomSeq();
}
break;
case kRoomWiseWomans: {
AnimationType *spr1 = &_animation->_sprites[1];
spr1->init(11, false, _animation);
if ((_roomCount[kRoomWiseWomans] == 1) && (ped > 0)) {
_animation->appearPed(1, 1); // Start on the right-hand side of the screen.
spr1->walkTo(3); // Walks up to greet you.
} else {
_animation->appearPed(1, 3); // Starts where she was before.
spr1->_facingDir = kDirLeft;
}
spr1->_callEachStepFl = true;
spr1->_eachStepProc = Animation::kProcFaceAvvy; // She always faces Avvy.
}
break;
case kRoomInsideCardiffCastle:
if (ped > 0) {
_animation->_sprites[1].init(10, false, _animation); // Define the dart.
_background->draw(-1, -1, 0);
_graphics->refreshBackground();
_sequence->startCardiffSeq2();
} else {
_background->draw(-1, -1, 0);
if (_arrowInTheDoor)
_background->draw(-1, -1, 2);
else
_background->draw(-1, -1, 1);
_graphics->refreshBackground();
}
break;
case kRoomAvvysGarden:
if (ped == 1) {
_background->draw(-1, -1, 1);
_graphics->refreshBackground();
_sequence->startGardenSeq();
}
break;
case kRoomEntranceHall:
case kRoomInsideAbbey:
case kRoomYourHall:
if (ped == 2) {
#if 0
// It was the original:
_celer->show_one(-1, -1, 2);
_sequence->first_show(1);
_sequence->then_show(3);
_sequence->start_to_close();
#endif
_background->draw(-1, -1, 1);
_graphics->refreshBackground();
_sequence->startGardenSeq();
}
break;
case kRoomAylesOffice:
if (_aylesIsAwake)
_background->draw(-1, -1, 1);
_graphics->refreshBackground();
break; // Ayles awake.
case kRoomGeidas:
putGeidaAt(1, ped);
break; // load Geida
case kRoomEastHall:
case kRoomWestHall:
if (_geidaFollows)
putGeidaAt(ped + 1, ped);
break;
case kRoomLusties:
if (_geidaFollows)
putGeidaAt(ped + 5, ped);
break;
case kRoomNottsPub:
if (_sittingInPub)
_background->draw(-1, -1, 2);
_npcFacing = 1; // Port.
break;
case kRoomOutsideDucks:
if (ped == 2) {
// Shut the door
_background->draw(-1, -1, 2);
_graphics->refreshBackground();
_sequence->startDuckSeq();
}
break;
case kRoomDucks:
_npcFacing = 1; // Duck.
break;
}
_seeScroll = false; // Now it can work again!
_isLoaded = false;
}
void AvalancheEngine::thinkAbout(byte object, bool type) {
const int16 picSize = 966;
_thinks = object;
object--;
setMousePointerWait();
if (type == kThing) {
if (!file.open("thinks.avd"))
error("AVALANCHE: File not found: thinks.avd");
} else { // kPerson
if (!file.open("folk.avd"))
error("AVALANCHE: File not found: folk.avd");
object -= 149;
if (object >= 25)
object -= 8;
if (object == 20)
object--; // Last time...
}
CursorMan.showMouse(false);
file.seek(object * picSize + 65);
::Graphics::Surface picture = _graphics->loadPictureGraphic(file);
_graphics->drawPicture(_graphics->_surface, picture, 205, 170);
picture.free();
file.close();
CursorMan.showMouse(true);
_thinkThing = type;
}
void AvalancheEngine::drawToolbar() {
if (!file.open("useful.avd"))
error("AVALANCHE: File not found: useful.avd");
file.seek(40);
CursorMan.showMouse(false);
::Graphics::Surface picture = _graphics->loadPictureGraphic(file);
_graphics->drawPicture(_graphics->_surface, picture, 5, 169);
picture.free();
file.close();
CursorMan.showMouse(true);
_animation->setOldDirection(kDirNone);
drawDirection();
}
void AvalancheEngine::drawScore() {
uint16 score = _dnascore;
int8 numbers[3] = {0, 0, 0};
for (int i = 0; i < 2; i++) {
byte divisor = 1;
for (int j = 0; j < (2 - i); j++)
divisor *= 10;
numbers[i] = score / divisor;
score -= numbers[i] * divisor;
}
numbers[2] = score;
CursorMan.showMouse(false);
for (int i = 0; i < 3; i++) {
if (_scoreToDisplay[i] != numbers[i])
_graphics->drawDigit(numbers[i], 250 + (i + 1) * 15, 177);
}
CursorMan.showMouse(true);
for (int i = 0; i < 3; i++)
_scoreToDisplay[i] = numbers[i];
}
void AvalancheEngine::incScore(byte num) {
for (int i = 1; i <= num; i++) {
_dnascore++;
if (_soundFx) {
for (int j = 1; j <= 97; j++)
// Length os 2 is a guess, the original doesn't have a delay specified
_sound->playNote(177 + _dnascore * 3, 2);
}
}
warning("STUB: points()");
drawScore();
}
void AvalancheEngine::useCompass(const Common::Point &cursorPos) {
byte color = *(byte *)_graphics->_surface.getBasePtr(cursorPos.x, cursorPos.y / 2);
switch (color) {
case kColorGreen:
_animation->setDirection(kDirUp);
_animation->setMoveSpeed(0, kDirUp);
drawDirection();
break;
case kColorBrown:
_animation->setDirection(kDirDown);
_animation->setMoveSpeed(0, kDirDown);
drawDirection();
break;
case kColorCyan:
_animation->setDirection(kDirLeft);
_animation->setMoveSpeed(0, kDirLeft);
drawDirection();
break;
case kColorLightmagenta:
_animation->setDirection(kDirRight);
_animation->setMoveSpeed(0, kDirRight);
drawDirection();
break;
case kColorRed:
case kColorWhite:
case kColorLightcyan:
case kColorYellow: // Fall-throughs are intended.
_animation->stopWalking();
drawDirection();
break;
}
}
void AvalancheEngine::fxToggle() {
warning("STUB: fxtoggle()");
}
void AvalancheEngine::refreshObjectList() {
_carryNum = 0;
if (_thinkThing && !_objects[_thinks - 1])
thinkAbout(kObjectMoney, kThing); // you always have money
for (int i = 0; i < kObjectNum; i++) {
if (_objects[i]) {
_objectList[_carryNum] = i + 1;
_carryNum++;
}
}
}
/**
* @remarks Originally called 'verte'
*/
void AvalancheEngine::guideAvvy(Common::Point cursorPos) {
if (!_userMovesAvvy)
return;
cursorPos.y /= 2;
byte what;
// _animation->tr[0] is Avalot.)
AnimationType *avvy = &_animation->_sprites[0];
if (cursorPos.x < avvy->_x)
what = 1;
else if (cursorPos.x > (avvy->_x + avvy->_info._xLength))
what = 2;
else
what = 0; // On top
if (cursorPos.y < avvy->_y)
what += 3;
else if (cursorPos.y > (avvy->_y + avvy->_info._yLength))
what += 6;
switch (what) {
case 0:
_animation->stopWalking();
break; // Clicked on Avvy: no movement.
case 1:
_animation->setMoveSpeed(0, kDirLeft);
break;
case 2:
_animation->setMoveSpeed(0, kDirRight);
break;
case 3:
_animation->setMoveSpeed(0, kDirUp);
break;
case 4:
_animation->setMoveSpeed(0, kDirUpLeft);
break;
case 5:
_animation->setMoveSpeed(0, kDirUpRight);
break;
case 6:
_animation->setMoveSpeed(0, kDirDown);
break;
case 7:
_animation->setMoveSpeed(0, kDirDownLeft);
break;
case 8:
_animation->setMoveSpeed(0, kDirDownRight);
break;
} // No other values are possible.
drawDirection();
}
void AvalancheEngine::checkClick() {
Common::Point cursorPos = getMousePos();
_onToolbar = kSlowComputer && ((cursorPos.y >= 169) || (cursorPos.y <= 10));
/*if (mrelease > 0)
after_the_scroll = false;*/
if ((0 <= cursorPos.y) && (cursorPos.y <= 21))
_graphics->loadMouse(0); // up arrow
else if ((317 <= cursorPos.y) && (cursorPos.y <= 339))
_graphics->loadMouse(7); //I-beam
else if ((340 <= cursorPos.y) && (cursorPos.y <= 399))
_graphics->loadMouse(1); // screwdriver
else if (!_menu->isActive()) { // Dropdown can handle its own pointers.
if (_holdLeftMouse) {
_graphics->loadMouse(6); // Mark's crosshairs
guideAvvy(cursorPos); // Normally, if you click on the picture, you're guiding Avvy around.
} else
_graphics->loadMouse(3); // fletch
}
if (_holdLeftMouse) {
if ((0 <= cursorPos.y) && (cursorPos.y <= 21)) { // Click on the dropdown menu.
if (_dropsOk)
_menu->update();
} else if ((317 <= cursorPos.y) && (cursorPos.y <= 339)) { // Click on the command line.
_parser->_inputTextPos = (cursorPos.x - 23) / 8;
if (_parser->_inputTextPos > _parser->_inputText.size() + 1)
_parser->_inputTextPos = _parser->_inputText.size() + 1;
if (_parser->_inputTextPos < 1)
_parser->_inputTextPos = 1;
_parser->_inputTextPos--;
_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 (_alive && _avvyIsAwake)
useCompass(cursorPos);
} else if ((208 <= cursorPos.x) && (cursorPos.x <= 260)) { // Examine the _thing.
do {
updateEvents();
} while (_holdLeftMouse);
if (_thinkThing) {
_parser->_thing = _thinks;
_parser->_thing += 49;
_parser->_person = kPeoplePardon;
} else {
_parser->_person = (People) _thinks;
_parser->_thing = _parser->kPardon;
}
callVerb(kVerbCodeExam);
} else if ((261 <= cursorPos.x) && (cursorPos.x <= 319)) { // Display the score.
do {
updateEvents();
} while (_holdLeftMouse);
callVerb(kVerbCodeScore);
} else if ((320 <= cursorPos.x) && (cursorPos.x <= 357)) { // Change speed.
_animation->_sprites[0]._speedX = kWalk;
_animation->updateSpeed();
} else if ((358 <= cursorPos.x) && (cursorPos.x <= 395)) { // Change speed.
_animation->_sprites[0]._speedX = kRun;
_animation->updateSpeed();
} else if ((396 <= cursorPos.x) && (cursorPos.x <= 483))
fxToggle();
else if ((535 <= cursorPos.x) && (cursorPos.x <= 640))
_mouseText.insertChar(Dialogs::kControlNewLine, 0);
} else if (!_dropsOk)
_mouseText = Common::String(13) + _mouseText;
}
}
void AvalancheEngine::errorLed() {
warning("STUB: errorled()");
}
int8 AvalancheEngine::fades(int8 x) {
warning("STUB: fades()");
return 0;
}
void AvalancheEngine::fadeOut(byte n) {
warning("STUB: fadeOut()");
}
void AvalancheEngine::dusk() {
warning("STUB: dusk()");
}
void AvalancheEngine::fadeIn(byte n) {
warning("STUB: fadeIn()");
}
void AvalancheEngine::dawn() {
warning("STUB: dawn()");
}
void AvalancheEngine::drawDirection() { // It's data is loaded in load_digits().
if (_animation->getOldDirection() == _animation->getDirection())
return;
_animation->setOldDirection(_animation->getDirection());
CursorMan.showMouse(false);
_graphics->drawDirection(_animation->getDirection(), 0, 161);
CursorMan.showMouse(true);
}
void AvalancheEngine::gameOver() {
_userMovesAvvy = false;
AnimationType *avvy = &_animation->_sprites[0];
int16 sx = avvy->_x;
int16 sy = avvy->_y;
avvy->remove();
avvy->init(12, true, _animation); // 12 = Avalot falls
avvy->_stepNum = 0;
avvy->appear(sx, sy, kDirUp);
_timer->addTimer(3, Timer::kProcAvalotFalls, Timer::kReasonFallingOver);
_alive = false;
}
void AvalancheEngine::minorRedraw() {
dusk();
enterRoom(_room, 0); // Ped unknown or non-existant.
for (int i = 0; i < 3; i++)
_scoreToDisplay[i] = -1; // impossible digits
drawScore();
dawn();
}
void AvalancheEngine::majorRedraw() {
warning("STUB: major_redraw()");
}
uint16 AvalancheEngine::bearing(byte whichPed) {
static const double rad2deg = 180 / 3.14; // Pi
AnimationType *avvy = &_animation->_sprites[0];
PedType *curPed = &_peds[whichPed];
if (avvy->_x == curPed->_x)
return 0;
else if (avvy->_x < curPed->_x) {
return (uint16)((atan(double((avvy->_y - curPed->_y)) / (avvy->_x - curPed->_x)) * rad2deg) + 90);
} else {
return (uint16)((atan(double((avvy->_y - curPed->_y)) / (avvy->_x - curPed->_x)) * rad2deg) + 270);
}
}
/**
* @remarks Originally called 'sprite_run'
*/
void AvalancheEngine::spriteRun() {
_doingSpriteRun = true;
_animation->animLink();
_doingSpriteRun = false;
}
// CHECKME: Unused function
void AvalancheEngine::fixFlashers() {
_ledStatus = 177;
_animation->setOldDirection(kDirNone);
_dialogs->setReadyLight(2);
drawDirection();
}
Common::String AvalancheEngine::intToStr(int32 num) {
return Common::String::format("%d", num);
}
/**
* Set the mouse pointer to 'HourGlass"
* @remarks Originally called 'wait'
*/
void AvalancheEngine::setMousePointerWait() {
_graphics->loadMouse(4);
}
void AvalancheEngine::resetVariables() {
_animation->setDirection(kDirUp);
_carryNum = 0;
for (int i = 0; i < kObjectNum; i++)
_objects[i] = false;
_dnascore = 0;
_money = 0;
_room = kRoomNowhere;
_saveNum = 0;
for (int i = 0; i < 100; i++)
_roomCount[i] = 0;
_wonNim = false;
_wineState = 0;
_cwytalotGone = false;
_passwordNum = 0;
_aylesIsAwake = false;
_drawbridgeOpen = 0;
_avariciusTalk = 0;
_rottenOnion = false;
_onionInVinegar = false;
_givenToSpludwick = 0;
_brummieStairs = 0;
_cardiffQuestionNum = 0;
_passedCwytalotInHerts = false;
_avvyIsAwake = false;
_avvyInBed = false;
_userMovesAvvy = false;
_npcFacing = 0;
_givenBadgeToIby = false;
_friarWillTieYouUp = false;
_tiedUp = false;
_boxContent = 0;
_talkedToCrapulus = false;
_jacquesState = 0;
_bellsAreRinging = false;
_standingOnDais = false;
_takenPen = false;
_arrowTriggered = false;
_arrowInTheDoor = false;
_favouriteDrink = "";
_favouriteSong = "";
_worstPlaceOnEarth = "";
_spareEvening = "";
_totalTime = 0;
_jumpStatus = 0;
_mushroomGrowing = false;
_spludwickAtHome = false;
_lastRoom = 0;
_lastRoomNotMap = 0;
_crapulusWillTell = false;
_enterCatacombsFromLustiesRoom = false;
_teetotal = false;
_malagauche = 0;
_drinking = 0;
_enteredLustiesRoomAsMonk = false;
_catacombX = 0;
_catacombY = 0;
_avvysInTheCupboard = false;
_geidaFollows = false;
_nextBell = 0;
_givenPotionToGeida = false;
_lustieIsAsleep = false;
_beenTiedUp = false;
_sittingInPub = false;
_spurgeTalkCount = 0;
_metAvaroid = false;
_takenMushroom = false;
_givenPenToAyles = false;
_askedDogfoodAboutNim = false;
_parser->resetVariables();
_animation->resetVariables();
_sequence->resetVariables();
}
void AvalancheEngine::newGame() {
for (int i = 0; i < kMaxSprites; i++) {
AnimationType *spr = &_animation->_sprites[i];
if (spr->_quick)
spr->remove();
}
// Deallocate sprite. Sorry, beta testers!
AnimationType *avvy = &_animation->_sprites[0];
avvy->init(0, true, _animation);
_alive = true;
resetVariables();
_dialogs->setBubbleStateNatural();
_spareEvening = "answer a questionnaire";
_favouriteDrink = "beer";
_money = 30; // 2/6
_animation->setDirection(kDirStopped);
_parser->_wearing = kObjectClothes;
_objects[kObjectMoney - 1] = true;
_objects[kObjectBodkin - 1] = true;
_objects[kObjectBell - 1] = true;
_objects[kObjectClothes - 1] = true;
_thinkThing = true;
_thinks = 2;
refreshObjectList();
_onToolbar = false;
_seeScroll = false;
avvy->appear(300, 117, kDirRight); // 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 = kPeoplePardon;
_her = kPeoplePardon;
_it = Parser::kPardon;
_lastPerson = kPeoplePardon; // = Pardon?
_passwordNum = _rnd->getRandomNumber(30) + 1; //Random(30) + 1;
_userMovesAvvy = false;
_doingSpriteRun = false;
_avvyInBed = true;
enterRoom(kRoomYours, 1);
avvy->_visible = false;
drawScore();
_menu->setup();
_clock->update();
spriteRun();
}
void AvalancheEngine::slowDown() {
warning("STUB: slowdown()");
}
bool AvalancheEngine::setFlag(char x) {
for (uint16 i = 0; i < _flags.size(); i++) {
if (_flags[i] == x)
return true;
}
return false;
}
bool AvalancheEngine::decreaseMoney(uint16 amount) {
_money -= amount;
if (_money < 0) {
_dialogs->displayScrollChain('Q', 2); // "You are now denariusless!"
gameOver();
return false;
} else
return true;
}
Common::String AvalancheEngine::getName(People 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 < kPeopleArkata)
return kLads[whose - kPeopleAvalot];
else
return kLasses[whose - kPeopleArkata];
}
byte AvalancheEngine::getNameChar(People whose) {
static const char kLadChar[] = "ASCDMTRwLfgeIyPu";
static const char kLassChar[] = "kG\0xB1o";
if (whose < kPeopleArkata)
return kLadChar[whose - kPeopleAvalot];
else
return kLassChar[whose - kPeopleArkata];
}
Common::String AvalancheEngine::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 (_wineState) {
case 1:
case 4:
get_thing_result = kThings[which - 1];
break;
case 3:
get_thing_result = "Vinegar";
break;
}
break;
case kObjectOnion:
if (_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 AvalancheEngine::getThingChar(byte which) {
static const char kThingsChar[] = "WMBParCLguKeSnIohn"; // V=Vinegar
char get_thingchar_result;
switch (which) {
case kObjectWine:
if (_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 AvalancheEngine::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 (_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 (_rottenOnion)
get_better_result = "a rotten onion";
else if (_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 AvalancheEngine::f5Does() {
switch (_room) {
case kRoomYours:
if (!_avvyIsAwake)
return Common::String::format("%cWWake up", kVerbCodeWake);
else if (_avvyInBed)
return Common::String::format("%cGGet up", kVerbCodeStand);
break;
case kRoomInsideCardiffCastle:
if (_standingOnDais)
return Common::String::format("%cCClimb down", kVerbCodeClimb);
else
return Common::String::format("%cCClimb up", kVerbCodeClimb);
break;
case kRoomNottsPub:
if (_sittingInPub)
return Common::String::format("%cSStand up", kVerbCodeStand);
else
return Common::String::format("%cSSit down", kVerbCodeSit);
break;
case kRoomMusicRoom:
if (_animation->inField(5))
return Common::String::format("%cPPlay the harp", kVerbCodePlay);
break;
}
return Common::String::format("%c", kVerbCodePardon); // If all else fails...
}
void AvalancheEngine::setBackgroundColor(byte x) {
warning("STUB: background()");
}
void AvalancheEngine::hangAroundForAWhile() {
for (int i = 0; i < 28; i++)
slowDown();
}
void AvalancheEngine::flipRoom(Room room, byte ped) {
assert((ped > 0) && (ped < 15));
if (!_alive) {
// You can't leave the room if you're dead.
_animation->_sprites[0]._moveX = 0;
_animation->_sprites[0]._moveY = 0; // Stop him from moving.
return;
}
if ((room == kRoomDummy) && (_room == kRoomLusties)) {
_animation->hideInCupboard();
return;
}
if ((_jumpStatus > 0) && (_room == kRoomInsideCardiffCastle)) {
// You can't *jump* out of Cardiff Castle!
_animation->_sprites[0]._moveX = 0;
return;
}
exitRoom(_room);
dusk();
for (int16 i = 1; i < _animation->kSpriteNumbMax; i++) {
if (_animation->_sprites[i]._quick)
_animation->_sprites[i].remove();
} // Deallocate sprite
if (_room == kRoomLustiesRoom)
_enterCatacombsFromLustiesRoom = true;
enterRoom(room, ped);
_animation->appearPed(0, ped - 1);
_enterCatacombsFromLustiesRoom = false;
_animation->setOldDirection(_animation->getDirection());
_animation->setDirection(_animation->_sprites[0]._facingDir);
drawDirection();
dawn();
}
/**
* 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 AvalancheEngine::openDoor(Room whither, byte ped, byte magicnum) {
switch (_room) {
case kRoomOutsideYours:
case kRoomOutsideNottsPub:
case kRoomOutsideDucks:
_sequence->startOutsideSeq(whither, ped);
break;
case kRoomInsideCardiffCastle:
_sequence->startCardiffSeq(whither, ped);
break;
case kRoomAvvysGarden:
case kRoomEntranceHall:
case kRoomInsideAbbey:
case kRoomYourHall:
_sequence->startHallSeq(whither, ped);
break;
case kRoomMusicRoom:
case kRoomOutsideArgentPub:
_sequence->startMusicRoomSeq2(whither, ped);
break;
case kRoomLusties:
switch (magicnum) {
case 14:
if (_avvysInTheCupboard) {
_animation->hideInCupboard();
_sequence->startCupboardSeq();
return;
} else {
_animation->appearPed(0, 5);
_animation->_sprites[0]._facingDir = kDirRight;
_sequence->startLustiesSeq2(whither, ped);
}
break;
case 12:
_sequence->startLustiesSeq3(whither, ped);
break;
}
break;
default:
_sequence->startDummySeq(whither, ped);
}
}
void AvalancheEngine::setRoom(People persId, Room roomId) {
_whereIs[persId - kPeopleAvalot] = roomId;
}
Room AvalancheEngine::getRoom(People persId) {
return _whereIs[persId - kPeopleAvalot];
}
} // End of namespace Avalanche