scummvm/engines/avalanche/ghostroom.cpp

396 lines
10 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.
*/
#include "avalanche/avalanche.h"
#include "avalanche/ghostroom.h"
#include "common/random.h"
namespace Avalanche {
const int8 GhostRoom::kAdjustment[5] = { 7, 0, 7, 7, 7 };
const byte GhostRoom::kWaveOrder[5] = { 4, 0, 1, 2, 3 };
const byte GhostRoom::kGlerkFade[26] = { 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1, 0 };
const byte GhostRoom::kGreldetFade[18] = { 0, 1, 2, 3, 4, 5, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0 };
GhostRoom::GhostRoom(AvalancheEngine *vm) {
_vm = vm;
_glerkStage = 0;
_batX = 0;
_batY = 0;
_batCount = 0;
_aarghCount = 0;
_greldetX = _greldetY = 0;
_greldetCount = 0;
_redGreldet = false;
_wasLoaded = false;
}
GhostRoom::~GhostRoom() {
for (int i = 0; i < 2; i++)
_eyes[i].free();
_exclamation.free();
for (int i = 0; i < 3; i++)
_bat[i].free();
for (int i = 0; i < 6; i++)
_aargh[i].free();
for (int i = 0; i < 5; i++)
_greenEyes[i].free();
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 6; j++)
_greldet[j][i].free();
}
if (_wasLoaded) {
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 2; j++) {
for (int y = 0; y < 66; y++) {
delete[] _ghost[i][j][y];
}
delete[] _ghost[i][j];
}
delete[] _ghost[i];
}
delete[] _ghost;
for (int i = 0; i < 6; i++) {
for (int j = 0; j < 4; j++) {
for (int y = 0; y < 35; y++) {
delete[] _glerk[i][j][y];
}
delete[] _glerk[i][j];
}
delete[] _glerk[i];
}
delete[] _glerk;
}
}
void GhostRoom::wait(uint16 howLong) {
for (int i = 0; i < howLong; i++) {
Common::Event event;
_vm->getEvent(event);
if (event.type == Common::EVENT_KEYDOWN)
_vm->_sound->playNote(6177, 1);
_vm->_system->delayMillis(1);
}
}
void GhostRoom::doBat() {
_batCount++;
int8 dx = 0;
int8 iy = 0;
byte batImage = 0;
if ((_batCount % 2) == 1) {
if ((1 <= _batCount) && (_batCount <= 90)) {
dx = 2;
iy = 1;
batImage = 0;
} else if ((91 <= _batCount) && (_batCount <= 240)) {
dx = 1;
iy = 1;
batImage = 1;
} else if((241 <= _batCount) && (_batCount <= 260)) {
dx = 1;
iy = 4;
batImage = 2;
}
if ((_batCount == 91) || (_batCount == 241)) // When the bat changes, blank out the old one.
_vm->_graphics->drawFilledRectangle(Common::Rect(_batX + _bat[batImage].w, _batY, _batX + _bat[batImage - 1].w, _batY + _bat[batImage - 1].h), kColorBlack);
_vm->_graphics->drawFilledRectangle(Common::Rect(_batX, _batY, _batX + _bat[batImage].w, _batY + iy), kColorBlack);
_vm->_graphics->drawFilledRectangle(Common::Rect(_batX + _bat[batImage].w - dx, _batY, _batX + _bat[batImage].w, _batY + _bat[batImage].h), kColorBlack);
_batX -= dx;
_batY++;
_vm->_graphics->ghostDrawPicture(_bat[batImage], _batX, _batY);
}
}
void GhostRoom::bigGreenEyes(byte how) {
_vm->_graphics->ghostDrawPicture(_greenEyes[how], 330, 103);
_vm->_graphics->ghostDrawPicture(_greenEyes[how], 376, 103);
_vm->_graphics->refreshScreen();
}
ChunkBlock GhostRoom::readChunkBlock(Common::File &file) {
ChunkBlock cb;
cb._flavour = (Flavour)file.readByte();
cb._x = file.readSint16LE();
cb._y = file.readSint16LE();
cb._width = file.readSint16LE();
cb._height = file.readSint16LE();
cb._size = file.readSint32LE();
return cb;
}
void GhostRoom::loadPictures() {
Common::File file;
if (!file.open("spooky.avd"))
error("AVALANCHE: GhostRoom: File not found: spooky.avd");
file.seek(44);
// Initializing ghost's array.
_ghost = new byte***[5];
for (int i = 0; i < 5; i++) {
_ghost[i] = new byte**[2];
for (int j = 0; j < 2; j++) {
_ghost[i][j] = new byte*[66];
for (int y = 0; y < 66; y++) {
_ghost[i][j][y] = new byte[26];
for (int x = 0; x < 26; x++)
_ghost[i][j][y][x] = 0;
}
}
}
// Read in the pictures of the ghost.
for (int i = 0; i < 5; i++) {
ChunkBlock cb = readChunkBlock(file);
for (int j = 0; j < 2; j++) {
for (int y = 0; y <= cb._height; y++)
file.read(_ghost[i][j][y], cb._width / 8);
}
}
for (int i = 0; i < 2; i++)
_eyes[i] = _vm->_graphics->ghostLoadPicture(file, dummyCoord);
_exclamation = _vm->_graphics->ghostLoadPicture(file, dummyCoord);
// Actually this function not just loads, but also draws the images, but they are part of the background
// and they are need to be drawn only once.
_vm->_graphics->ghostDrawBackgroundItems(file);
for (int i = 0; i < 3; i++)
_bat[i] = _vm->_graphics->ghostLoadPicture(file, dummyCoord);
// Initializing glerk's array.
_glerk = new byte***[6];
for (int i = 0; i < 6; i++) {
_glerk[i] = new byte**[4];
for (int j = 0; j < 4; j++) {
_glerk[i][j] = new byte*[35];
for (int y = 0; y < 35; y++) {
_glerk[i][j][y] = new byte[9];
for (int x = 0; x < 9; x++)
_glerk[i][j][y][x] = 0;
}
}
}
// Read in the pictures of the "glerk".
for (int i = 0; i < 6; i++) {
ChunkBlock cb = readChunkBlock(file);
for (int j = 0; j < 4; j++) {
for (int y = 0; y <= cb._height; y++)
file.read(_glerk[i][j][y], cb._width / 8);
}
}
for (int i = 0; i < 6; i++)
_aargh[i] = _vm->_graphics->ghostLoadPicture(file, _aarghWhere[i]);
for (int i = 0; i < 5; i++)
_greenEyes[i] = _vm->_graphics->ghostLoadPicture(file, dummyCoord);
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 6; j++)
_greldet[j][i] = _vm->_graphics->ghostLoadPicture(file, dummyCoord);
}
file.close();
}
void GhostRoom::run() {
CursorMan.showMouse(false);
_vm->_graphics->saveScreen();
_vm->fadeOut();
_vm->_graphics->blackOutScreen();
_vm->fadeIn();
// Only load the pictures if it's our first time walking into the room.
// After that we simply use the already loaded images.
if (!_wasLoaded) {
loadPictures();
_wasLoaded = true;
}
// Avvy walks over:
_glerkStage = 0;
_batX = 277;
_batY = 40;
_batCount = 0;
for (int x = 500; x >= 217; x--) {
// The floating eyeballs:
int xBound = x % 30;
if ((22 <= xBound) && (xBound <= 27)) {
if (xBound == 27)
_vm->_graphics->drawFilledRectangle(Common::Rect(x, 135, x + 17, 137), kColorBlack);
_vm->_graphics->ghostDrawPicture(_eyes[0], x, 136);
_vm->_graphics->drawDot(x + 16, 137, kColorBlack);
} else {
if (xBound == 21)
_vm->_graphics->drawFilledRectangle(Common::Rect(x, 137, x + 18, 139), kColorBlack);
_vm->_graphics->ghostDrawPicture(_eyes[0], x, 135);
_vm->_graphics->drawDot(x + 16, 136, kColorBlack); // Eyes would leave a trail 1 pixel high behind them.
}
// Plot the Glerk:
if ((x % 10) == 0) {
if (_glerkStage > 25)
break;
_vm->_graphics->ghostDrawMonster(_glerk[kGlerkFade[_glerkStage]], 456, 14, kMonsterTypeGlerk);
_glerkStage++;
}
doBat();
_vm->_graphics->refreshScreen();
wait(15);
}
// Blank out the Glerk's space.
_vm->_graphics->drawFilledRectangle(Common::Rect(456, 14, 531, 51), kColorBlack);
_vm->_graphics->refreshScreen();
// Here comes the descending ghost:
for (int y = -64; y <= 103; y++) {
_vm->_graphics->ghostDrawMonster(_ghost[1 + (abs(y / 7) % 2) * 3], 0, y, kMonsterTypeGhost);
if (y > 0)
_vm->_graphics->drawFilledRectangle(Common::Rect(0, y - 1, 26 * 8 + 1, y + 1), kColorBlack);
_vm->_graphics->refreshScreen();
wait(27);
}
// Then it waves:
_aarghCount = -15;
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 5; j++) {
_vm->_graphics->drawFilledRectangle(Common::Rect(0, 96, 26 * 8, 170), kColorBlack);
_vm->_graphics->ghostDrawMonster(_ghost[kWaveOrder[j]], 0, 96 + kAdjustment[j], kMonsterTypeGhost);
_aarghCount++;
if (_aarghCount >= 0) {
for (int k = 0; k <= _aarghCount; k++)
_vm->_graphics->ghostDrawPicture(_aargh[k], _aarghWhere[k].x, _aarghWhere[k].y);
}
_vm->_graphics->refreshScreen();
wait(177);
}
}
// The exclamation mark appears:
_vm->_graphics->ghostDrawPicture(_exclamation, 246, 127);
_vm->_graphics->refreshScreen();
wait(777);
// Erase "aargh":
_vm->_graphics->drawFilledRectangle(Common::Rect(172, 78, 348, 112), kColorBlack);
_vm->_graphics->refreshScreen();
for (int i = 4; i >= 0; i--) {
wait(377);
bigGreenEyes(i);
}
// Erase the exclamation mark:
_vm->_graphics->drawFilledRectangle(Common::Rect(246, 127, 252, 134), kColorBlack);
_vm->_graphics->refreshScreen();
// Avvy hurries back:
_glerkStage = 0;
_greldetCount = 18;
_redGreldet = false;
for (int x = 217; x <= 479; x++) {
// The floating eyeballs again:
int xBound = x % 30;
if ((22 <= xBound) && (xBound <= 27)) {
if (xBound == 22)
_vm->_graphics->drawFilledRectangle(Common::Rect(x + 22, 134, x + 39, 138), kColorBlack);
_vm->_graphics->ghostDrawPicture(_eyes[1], x + 23, 136);
_vm->_graphics->drawDot(x + 22, 137, kColorBlack);
} else {
if (xBound == 28)
_vm->_graphics->drawFilledRectangle(Common::Rect(x + 22, 135, x + 39, 139), kColorBlack);
_vm->_graphics->ghostDrawPicture(_eyes[1], x + 23, 135);
_vm->_graphics->drawDot(x + 22, 136, kColorBlack); // Eyes would leave a trail 1 pixel high behind them.
}
// Plot the Green Eyes:
if ((x % 53) == 5) {
bigGreenEyes(_glerkStage);
_glerkStage++;
}
// Plot the Greldet:
if (_greldetCount == 18) {
_greldetX = _vm->_rnd->getRandomNumber(599);
_greldetY = _vm->_rnd->getRandomNumber(79);
_greldetCount = 0;
_redGreldet = !_redGreldet;
}
_vm->_graphics->ghostDrawPicture(_greldet[kGreldetFade[_greldetCount]][_redGreldet], _greldetX, _greldetY);
_greldetCount++;
_vm->_graphics->refreshScreen();
wait(10);
}
CursorMan.showMouse(true);
_vm->fadeOut();
_vm->_graphics->restoreScreen();
_vm->_graphics->removeBackup();
_vm->_animation->animLink();
_vm->fadeIn();
}
} // End of namespace Avalanche