Initial commit
This commit is contained in:
3
engines/drascula/POTFILES
Normal file
3
engines/drascula/POTFILES
Normal file
@@ -0,0 +1,3 @@
|
||||
engines/drascula/drascula.cpp
|
||||
engines/drascula/metaengine.cpp
|
||||
engines/drascula/saveload.cpp
|
||||
469
engines/drascula/actors.cpp
Normal file
469
engines/drascula/actors.cpp
Normal file
@@ -0,0 +1,469 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "drascula/drascula.h"
|
||||
|
||||
namespace Drascula {
|
||||
|
||||
void DrasculaEngine::placeIgor() {
|
||||
int igY = 0;
|
||||
|
||||
if (currentChapter == 4) {
|
||||
igY = 138;
|
||||
} else {
|
||||
if (trackIgor == 3)
|
||||
igY = 138;
|
||||
else if (trackIgor == 1)
|
||||
igY = 76;
|
||||
}
|
||||
|
||||
copyRect(1, igY, igorX, igorY, 54, 61, frontSurface, screenSurface);
|
||||
}
|
||||
|
||||
void DrasculaEngine::placeDrascula() {
|
||||
int drX = 0;
|
||||
|
||||
if (trackDrascula == 1)
|
||||
drX = 47;
|
||||
else if (trackDrascula == 0)
|
||||
drX = 1;
|
||||
else if (trackDrascula == 3 && currentChapter == 1)
|
||||
drX = 93;
|
||||
|
||||
if (currentChapter == 6)
|
||||
copyRect(drX, 122, drasculaX, drasculaY, 45, 77, drawSurface2, screenSurface);
|
||||
else
|
||||
copyRect(drX, 122, drasculaX, drasculaY, 45, 77, backSurface, screenSurface);
|
||||
}
|
||||
|
||||
void DrasculaEngine::hiccup(int counter) {
|
||||
int y = 0, trackCharacter = 0;
|
||||
if (currentChapter == 3)
|
||||
y = -1;
|
||||
|
||||
do {
|
||||
counter--;
|
||||
|
||||
updateEvents();
|
||||
updateRoom();
|
||||
if (currentChapter == 3)
|
||||
updateScreen(0, 0, 0, y, 320, 200, screenSurface);
|
||||
else
|
||||
updateScreen(0, 1, 0, y, 320, 198, screenSurface);
|
||||
|
||||
if (trackCharacter == 0)
|
||||
y++;
|
||||
else
|
||||
y--;
|
||||
|
||||
if (currentChapter == 3) {
|
||||
if (y == 1)
|
||||
trackCharacter = 1;
|
||||
if (y == -1)
|
||||
trackCharacter = 0;
|
||||
} else {
|
||||
if (y == 2)
|
||||
trackCharacter = 1;
|
||||
if (y == 0)
|
||||
trackCharacter = 0;
|
||||
}
|
||||
pause(3);
|
||||
} while (counter > 0);
|
||||
|
||||
updateRoom();
|
||||
updateScreen();
|
||||
}
|
||||
|
||||
void DrasculaEngine::startWalking() {
|
||||
_characterMoved = true;
|
||||
|
||||
stepX = STEP_X;
|
||||
stepY = STEP_Y;
|
||||
|
||||
if (currentChapter == 2) {
|
||||
if ((roomX < curX) && (roomY <= (curY + curHeight)))
|
||||
quadrant_1();
|
||||
else if ((roomX < curX) && (roomY > (curY + curHeight)))
|
||||
quadrant_3();
|
||||
else if ((roomX > curX + curWidth) && (roomY <= (curY + curHeight)))
|
||||
quadrant_2();
|
||||
else if ((roomX > curX + curWidth) && (roomY > (curY + curHeight)))
|
||||
quadrant_4();
|
||||
else if (roomY < curY + curHeight)
|
||||
walkUp();
|
||||
else if (roomY > curY + curHeight)
|
||||
walkDown();
|
||||
else
|
||||
_characterMoved = false;
|
||||
} else {
|
||||
if ((roomX < curX + curWidth / 2 ) && (roomY <= (curY + curHeight)))
|
||||
quadrant_1();
|
||||
else if ((roomX < curX + curWidth / 2) && (roomY > (curY + curHeight)))
|
||||
quadrant_3();
|
||||
else if ((roomX > curX + curWidth / 2) && (roomY <= (curY + curHeight)))
|
||||
quadrant_2();
|
||||
else if ((roomX > curX + curWidth / 2) && (roomY > (curY + curHeight)))
|
||||
quadrant_4();
|
||||
else
|
||||
_characterMoved = false;
|
||||
}
|
||||
_startTime = getTime();
|
||||
}
|
||||
|
||||
void DrasculaEngine::moveCharacters() {
|
||||
int curPos[6];
|
||||
int r;
|
||||
|
||||
if (_characterMoved && stepX == STEP_X) {
|
||||
for (r = 0; r < stepX; r++) {
|
||||
if (currentChapter != 2) {
|
||||
if (trackProtagonist == 0 && roomX - r == curX + curWidth / 2) {
|
||||
_characterMoved = false;
|
||||
stepX = STEP_X;
|
||||
stepY = STEP_Y;
|
||||
}
|
||||
if (trackProtagonist == 1 && roomX + r == curX + curWidth / 2) {
|
||||
_characterMoved = false;
|
||||
stepX = STEP_X;
|
||||
stepY = STEP_Y;
|
||||
curX = roomX - curWidth / 2;
|
||||
curY = roomY - curHeight;
|
||||
}
|
||||
} else if (currentChapter == 2) {
|
||||
if (trackProtagonist == 0 && roomX - r == curX) {
|
||||
_characterMoved = false;
|
||||
stepX = STEP_X;
|
||||
stepY = STEP_Y;
|
||||
}
|
||||
if (trackProtagonist == 1 && roomX + r == curX + curWidth) {
|
||||
_characterMoved = false;
|
||||
stepX = STEP_X;
|
||||
stepY = STEP_Y;
|
||||
curX = roomX - curWidth + 4;
|
||||
curY = roomY - curHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_characterMoved && stepY == STEP_Y) {
|
||||
for (r = 0; r < stepY; r++) {
|
||||
if (trackProtagonist == 2 && roomY - r == curY + curHeight) {
|
||||
_characterMoved = false;
|
||||
stepX = STEP_X;
|
||||
stepY = STEP_Y;
|
||||
}
|
||||
if (trackProtagonist == 3 && roomY + r == curY + curHeight) {
|
||||
_characterMoved = false;
|
||||
stepX = STEP_X;
|
||||
stepY = STEP_Y;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (currentChapter != 2 && currentChapter != 3) {
|
||||
if (!_characterVisible) {
|
||||
increaseFrameNum();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!_characterMoved) {
|
||||
curPos[0] = 0;
|
||||
curPos[1] = DIF_MASK_HARE;
|
||||
curPos[2] = curX;
|
||||
curPos[3] = curY;
|
||||
if (currentChapter == 2) {
|
||||
curPos[4] = curWidth;
|
||||
curPos[5] = curHeight;
|
||||
} else {
|
||||
curPos[4] = CHARACTER_WIDTH;
|
||||
curPos[5] = CHARACTER_HEIGHT;
|
||||
}
|
||||
|
||||
if (trackProtagonist == 0) {
|
||||
curPos[1] = 0;
|
||||
if (currentChapter == 2)
|
||||
copyRect(curPos[0], curPos[1], curPos[2], curPos[3], curPos[4], curPos[5],
|
||||
extraSurface, screenSurface);
|
||||
else
|
||||
reduce_hare_chico(curPos[0], curPos[1], curPos[2], curPos[3], curPos[4], curPos[5],
|
||||
factor_red[curY + curHeight], extraSurface, screenSurface);
|
||||
} else if (trackProtagonist == 1) {
|
||||
if (currentChapter == 2)
|
||||
copyRect(curPos[0], curPos[1], curPos[2], curPos[3], curPos[4], curPos[5],
|
||||
extraSurface, screenSurface);
|
||||
else
|
||||
reduce_hare_chico(curPos[0], curPos[1], curPos[2], curPos[3], curPos[4], curPos[5],
|
||||
factor_red[curY + curHeight], extraSurface, screenSurface);
|
||||
} else if (trackProtagonist == 2) {
|
||||
if (currentChapter == 2)
|
||||
copyRect(curPos[0], curPos[1], curPos[2], curPos[3], curPos[4], curPos[5],
|
||||
backSurface, screenSurface);
|
||||
else
|
||||
reduce_hare_chico(curPos[0], curPos[1], curPos[2], curPos[3], curPos[4], curPos[5],
|
||||
factor_red[curY + curHeight], backSurface, screenSurface);
|
||||
} else {
|
||||
if (currentChapter == 2)
|
||||
copyRect(curPos[0], curPos[1], curPos[2], curPos[3], curPos[4], curPos[5],
|
||||
frontSurface, screenSurface);
|
||||
else
|
||||
reduce_hare_chico(curPos[0], curPos[1], curPos[2], curPos[3], curPos[4], curPos[5],
|
||||
factor_red[curY + curHeight], frontSurface, screenSurface);
|
||||
}
|
||||
} else if (_characterMoved) {
|
||||
curPos[0] = _frameX[_characterFrame];
|
||||
curPos[1] = frame_y + DIF_MASK_HARE;
|
||||
curPos[2] = curX;
|
||||
curPos[3] = curY;
|
||||
if (currentChapter == 2) {
|
||||
curPos[4] = curWidth;
|
||||
curPos[5] = curHeight;
|
||||
} else {
|
||||
curPos[4] = CHARACTER_WIDTH;
|
||||
curPos[5] = CHARACTER_HEIGHT;
|
||||
}
|
||||
if (trackProtagonist == 0) {
|
||||
curPos[1] = 0;
|
||||
if (currentChapter == 2)
|
||||
copyRect(curPos[0], curPos[1], curPos[2], curPos[3], curPos[4], curPos[5],
|
||||
extraSurface, screenSurface);
|
||||
else
|
||||
reduce_hare_chico(curPos[0], curPos[1], curPos[2], curPos[3], curPos[4], curPos[5],
|
||||
factor_red[curY + curHeight], extraSurface, screenSurface);
|
||||
} else if (trackProtagonist == 1) {
|
||||
if (currentChapter == 2)
|
||||
copyRect(curPos[0], curPos[1], curPos[2], curPos[3], curPos[4], curPos[5],
|
||||
extraSurface, screenSurface);
|
||||
else
|
||||
reduce_hare_chico(curPos[0], curPos[1], curPos[2], curPos[3], curPos[4], curPos[5],
|
||||
factor_red[curY + curHeight], extraSurface, screenSurface);
|
||||
} else if (trackProtagonist == 2) {
|
||||
if (currentChapter == 2)
|
||||
copyRect(curPos[0], curPos[1], curPos[2], curPos[3], curPos[4], curPos[5],
|
||||
backSurface, screenSurface);
|
||||
else
|
||||
reduce_hare_chico(curPos[0], curPos[1], curPos[2], curPos[3], curPos[4], curPos[5],
|
||||
factor_red[curY + curHeight], backSurface, screenSurface);
|
||||
} else {
|
||||
if (currentChapter == 2)
|
||||
copyRect(curPos[0], curPos[1], curPos[2], curPos[3], curPos[4], curPos[5],
|
||||
frontSurface, screenSurface);
|
||||
else
|
||||
reduce_hare_chico(curPos[0], curPos[1], curPos[2], curPos[3], curPos[4], curPos[5],
|
||||
factor_red[curY + curHeight], frontSurface, screenSurface);
|
||||
}
|
||||
increaseFrameNum();
|
||||
}
|
||||
}
|
||||
|
||||
void DrasculaEngine::quadrant_1() {
|
||||
float distanceX, distanceY;
|
||||
|
||||
if (currentChapter == 2)
|
||||
distanceX = curX - roomX;
|
||||
else
|
||||
distanceX = curX + curWidth / 2 - roomX;
|
||||
|
||||
distanceY = (curY + curHeight) - roomY;
|
||||
|
||||
if (distanceX < distanceY) {
|
||||
curDirection = kDirectionUp;
|
||||
trackProtagonist = 2;
|
||||
stepX = (int)(distanceX / (distanceY / STEP_Y));
|
||||
} else {
|
||||
curDirection = kDirectionUp;
|
||||
trackProtagonist = 0;
|
||||
stepY = (int)(distanceY / (distanceX / STEP_X));
|
||||
}
|
||||
}
|
||||
|
||||
void DrasculaEngine::quadrant_2() {
|
||||
float distanceX, distanceY;
|
||||
|
||||
if (currentChapter == 2)
|
||||
distanceX = ABS(curX + curWidth - roomX);
|
||||
else
|
||||
distanceX = ABS(curX + curWidth / 2 - roomX);
|
||||
|
||||
distanceY = (curY + curHeight) - roomY;
|
||||
|
||||
if (distanceX < distanceY) {
|
||||
curDirection = kDirectionRight;
|
||||
trackProtagonist = 2;
|
||||
stepX = (int)(distanceX / (distanceY / STEP_Y));
|
||||
} else {
|
||||
curDirection = kDirectionRight;
|
||||
trackProtagonist = 1;
|
||||
stepY = (int)(distanceY / (distanceX / STEP_X));
|
||||
}
|
||||
}
|
||||
|
||||
void DrasculaEngine::quadrant_3() {
|
||||
float distanceX, distanceY;
|
||||
|
||||
if (currentChapter == 2)
|
||||
distanceX = curX - roomX;
|
||||
else
|
||||
distanceX = curX + curWidth / 2 - roomX;
|
||||
|
||||
distanceY = roomY - (curY + curHeight);
|
||||
|
||||
if (distanceX < distanceY) {
|
||||
curDirection = kDirectionLeft;
|
||||
trackProtagonist = 3;
|
||||
stepX = (int)(distanceX / (distanceY / STEP_Y));
|
||||
} else {
|
||||
curDirection = kDirectionLeft;
|
||||
trackProtagonist = 0;
|
||||
stepY = (int)(distanceY / (distanceX / STEP_X));
|
||||
}
|
||||
}
|
||||
|
||||
void DrasculaEngine::quadrant_4() {
|
||||
float distanceX, distanceY;
|
||||
|
||||
if (currentChapter == 2)
|
||||
distanceX = ABS(curX + curWidth - roomX);
|
||||
else
|
||||
distanceX = ABS(curX + curWidth / 2 - roomX);
|
||||
|
||||
distanceY = roomY - (curY + curHeight);
|
||||
|
||||
if (distanceX < distanceY) {
|
||||
curDirection = kDirectionDown;
|
||||
trackProtagonist = 3;
|
||||
stepX = (int)(distanceX / (distanceY / STEP_Y));
|
||||
} else {
|
||||
curDirection = kDirectionDown;
|
||||
trackProtagonist = 1;
|
||||
stepY = (int)(distanceY / (distanceX / STEP_X));
|
||||
}
|
||||
}
|
||||
|
||||
void DrasculaEngine::increaseFrameNum() {
|
||||
if (getTime() - _startTime >= 6) {
|
||||
_startTime = getTime();
|
||||
_characterFrame++;
|
||||
if (_characterFrame == 6)
|
||||
_characterFrame = 0;
|
||||
|
||||
if (curDirection == kDirectionUp) {
|
||||
curX -= stepX;
|
||||
curY -= stepY;
|
||||
} else if (curDirection == kDirectionRight) {
|
||||
curX += stepX;
|
||||
curY -= stepY;
|
||||
} else if (curDirection == kDirectionDown) {
|
||||
curX += stepX;
|
||||
curY += stepY;
|
||||
} else if (curDirection == kDirectionLeft) {
|
||||
curX -= stepX;
|
||||
curY += stepY;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentChapter != 2) {
|
||||
curY += (int)(curHeight - newHeight);
|
||||
curX += (int)(curWidth - newWidth);
|
||||
curHeight = (int)newHeight;
|
||||
curWidth = (int)newWidth;
|
||||
}
|
||||
|
||||
// Fix bug #5903 DRASCULA-IT: Crash/graphic glitch at castle towers
|
||||
// Chapter 5 Room 45 is the castle tower part
|
||||
// Fixing the character's coordinate(0,0) in the tower section to prevent out of window coordinates and crash
|
||||
if ((currentChapter == 5) && (_roomNumber == 45)) {
|
||||
curY = 0;
|
||||
curX = 0;
|
||||
curHeight = 0;
|
||||
curWidth = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void DrasculaEngine::walkDown() {
|
||||
curDirection = kDirectionDown;
|
||||
trackProtagonist = 3;
|
||||
stepX = 0;
|
||||
}
|
||||
|
||||
void DrasculaEngine::walkUp() {
|
||||
curDirection = kDirectionUp;
|
||||
trackProtagonist = 2;
|
||||
stepX = 0;
|
||||
}
|
||||
|
||||
void DrasculaEngine::moveVonBraun() {
|
||||
int pos_vb[6];
|
||||
|
||||
if (vonBraunHasMoved == 0) {
|
||||
pos_vb[0] = 256;
|
||||
pos_vb[1] = 129;
|
||||
pos_vb[2] = vonBraunX;
|
||||
pos_vb[3] = 66;
|
||||
pos_vb[4] = 33;
|
||||
pos_vb[5] = 69;
|
||||
if (trackVonBraun == 0)
|
||||
pos_vb[0] = 222;
|
||||
else if (trackVonBraun == 1)
|
||||
pos_vb[0] = 188;
|
||||
} else {
|
||||
pos_vb[0] = actorFrames[kFrameVonBraun];
|
||||
pos_vb[1] = (trackVonBraun == 0) ? 62 : 131;
|
||||
pos_vb[2] = vonBraunX;
|
||||
pos_vb[3] = 66;
|
||||
pos_vb[4] = 28;
|
||||
pos_vb[5] = 68;
|
||||
|
||||
actorFrames[kFrameVonBraun] += 29;
|
||||
if (actorFrames[kFrameVonBraun] > 146)
|
||||
actorFrames[kFrameVonBraun] = 1;
|
||||
}
|
||||
|
||||
copyRect(pos_vb[0], pos_vb[1], pos_vb[2], pos_vb[3], pos_vb[4], pos_vb[5],
|
||||
frontSurface, screenSurface);
|
||||
}
|
||||
|
||||
void DrasculaEngine::placeVonBraun(int pointX) {
|
||||
trackVonBraun = (pointX < vonBraunX) ? 0 : 1;
|
||||
vonBraunHasMoved = 1;
|
||||
|
||||
while (!shouldQuit()) {
|
||||
updateEvents();
|
||||
updateRoom();
|
||||
updateScreen();
|
||||
if (trackVonBraun == 0) {
|
||||
vonBraunX = vonBraunX - 5;
|
||||
if (vonBraunX <= pointX)
|
||||
break;
|
||||
} else {
|
||||
vonBraunX = vonBraunX + 5;
|
||||
if (vonBraunX >= pointX)
|
||||
break;
|
||||
}
|
||||
pause(5);
|
||||
}
|
||||
|
||||
vonBraunHasMoved = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // End of namespace Drascula
|
||||
2252
engines/drascula/animation.cpp
Normal file
2252
engines/drascula/animation.cpp
Normal file
File diff suppressed because it is too large
Load Diff
3
engines/drascula/configure.engine
Normal file
3
engines/drascula/configure.engine
Normal file
@@ -0,0 +1,3 @@
|
||||
# This file is included from the main "configure" script
|
||||
# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] [components]
|
||||
add_engine drascula "Drascula: The Vampire Strikes Back" yes
|
||||
52
engines/drascula/console.cpp
Normal file
52
engines/drascula/console.cpp
Normal file
@@ -0,0 +1,52 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "drascula/console.h"
|
||||
#include "gui/debugger.h"
|
||||
#include "drascula/drascula.h"
|
||||
|
||||
namespace Drascula {
|
||||
|
||||
Console::Console(DrasculaEngine *vm) : GUI::Debugger(), _vm(vm) {
|
||||
registerCmd("room", WRAP_METHOD(Console, Cmd_Room));
|
||||
}
|
||||
|
||||
Console::~Console() {
|
||||
}
|
||||
|
||||
bool Console::Cmd_Room(int argc, const char **argv) {
|
||||
if (argc < 2) {
|
||||
debugPrintf("Usage: room <number>\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
int roomNum = atoi(argv[1]);
|
||||
|
||||
_vm->_loadedDifferentChapter = false;
|
||||
_vm->enterRoom(roomNum);
|
||||
_vm->selectVerb(kVerbNone);
|
||||
_vm->clearRoom();
|
||||
_vm->loadPic(roomNum, _vm->bgSurface, HALF_PAL);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // End of namespace Drascula
|
||||
43
engines/drascula/console.h
Normal file
43
engines/drascula/console.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef DRASCULA_CONSOLE_H
|
||||
#define DRASCULA_CONSOLE_H
|
||||
|
||||
#include "gui/debugger.h"
|
||||
|
||||
namespace Drascula {
|
||||
|
||||
class DrasculaEngine;
|
||||
|
||||
class Console : public GUI::Debugger {
|
||||
public:
|
||||
Console(DrasculaEngine *vm);
|
||||
~Console(void) override;
|
||||
|
||||
private:
|
||||
DrasculaEngine *_vm;
|
||||
|
||||
bool Cmd_Room(int argc, const char **argv);
|
||||
};
|
||||
|
||||
} // End of namespace Drascula
|
||||
#endif
|
||||
343
engines/drascula/converse.cpp
Normal file
343
engines/drascula/converse.cpp
Normal file
@@ -0,0 +1,343 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/textconsole.h"
|
||||
|
||||
#include "drascula/drascula.h"
|
||||
|
||||
#include "common/text-to-speech.h"
|
||||
|
||||
namespace Drascula {
|
||||
|
||||
void DrasculaEngine::playTalkSequence(int sequence) {
|
||||
bool seen = false;
|
||||
|
||||
for (int i = 0; i < _talkSequencesSize; i++) {
|
||||
if (_talkSequences[i].chapter == currentChapter &&
|
||||
_talkSequences[i].sequence == sequence) {
|
||||
seen = true;
|
||||
|
||||
doTalkSequenceCommand(_talkSequences[i]);
|
||||
} else if (seen) // Stop searching down the list
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DrasculaEngine::doTalkSequenceCommand(TalkSequenceCommand cmd) {
|
||||
switch (cmd.commandType) {
|
||||
case kPause:
|
||||
pause(cmd.action);
|
||||
break;
|
||||
case kSetFlag:
|
||||
flags[cmd.action] = 1;
|
||||
break;
|
||||
case kClearFlag:
|
||||
flags[cmd.action] = 0;
|
||||
break;
|
||||
case kPickObject:
|
||||
pickObject(cmd.action);
|
||||
break;
|
||||
case kAddObject:
|
||||
addObject(cmd.action);
|
||||
break;
|
||||
case kBreakOut:
|
||||
breakOut = 1;
|
||||
break;
|
||||
case kConverse:
|
||||
converse(cmd.action);
|
||||
break;
|
||||
case kPlaceVB:
|
||||
placeVonBraun(cmd.action);
|
||||
break;
|
||||
case kUpdateRoom:
|
||||
updateRoom();
|
||||
break;
|
||||
case kUpdateScreen:
|
||||
updateScreen();
|
||||
break;
|
||||
case kTrackProtagonist:
|
||||
trackProtagonist = cmd.action;
|
||||
break;
|
||||
case kPlaySound:
|
||||
playSound(cmd.action);
|
||||
break;
|
||||
case kFinishSound:
|
||||
finishSound();
|
||||
break;
|
||||
case kTalkerGeneral:
|
||||
talk(cmd.action);
|
||||
break;
|
||||
case kTalkerDrunk:
|
||||
talk_drunk(cmd.action);
|
||||
break;
|
||||
case kTalkerPianist:
|
||||
talk_pianist(cmd.action);
|
||||
break;
|
||||
case kTalkerBJ:
|
||||
talk_bj(cmd.action);
|
||||
break;
|
||||
case kTalkerVBNormal:
|
||||
talk_vonBraun(cmd.action, kVonBraunNormal);
|
||||
break;
|
||||
case kTalkerVBDoor:
|
||||
talk_vonBraun(cmd.action, kVonBraunDoor);
|
||||
break;
|
||||
case kTalkerIgorSeated:
|
||||
talk_igor(cmd.action, kIgorSeated);
|
||||
break;
|
||||
case kTalkerWerewolf:
|
||||
talk_werewolf(cmd.action);
|
||||
break;
|
||||
case kTalkerMus:
|
||||
talk_mus(cmd.action);
|
||||
break;
|
||||
case kTalkerDrascula:
|
||||
talk_drascula(cmd.action, 1);
|
||||
break;
|
||||
case kTalkerBartender0:
|
||||
talk_bartender(cmd.action, 0);
|
||||
break;
|
||||
case kTalkerBartender1:
|
||||
talk_bartender(cmd.action, 1);
|
||||
break;
|
||||
default:
|
||||
error("doTalkSequenceCommand: Unknown command: %d", cmd.commandType);
|
||||
}
|
||||
}
|
||||
|
||||
void DrasculaEngine::cleanupString(char *string) {
|
||||
uint len = strlen(string);
|
||||
for (uint h = 0; h < len; h++)
|
||||
if (string[h] == (char)0xa7)
|
||||
string[h] = ' ';
|
||||
}
|
||||
|
||||
void DrasculaEngine::converse(int index) {
|
||||
debug(4, "converse(%d)", index);
|
||||
|
||||
char fileName[20];
|
||||
Common::sprintf_s(fileName, "op_%d.cal", index);
|
||||
Common::SeekableReadStream *stream = _archives.open(fileName);
|
||||
if (!stream)
|
||||
error("missing data file %s", fileName);
|
||||
|
||||
int game1 = kDialogOptionUnselected,
|
||||
game2 = kDialogOptionUnselected,
|
||||
game3 = kDialogOptionUnselected;
|
||||
char phrase1[128], phrase2[128], phrase3[128], phrase4[128];
|
||||
char sound1[13], sound2[13], sound3[13], sound4[13];
|
||||
int phrase1_bottom, phrase2_bottom, phrase3_bottom, phrase4_bottom;
|
||||
int answer1, answer2, answer3;
|
||||
|
||||
breakOut = 0;
|
||||
|
||||
selectVerb(kVerbNone);
|
||||
|
||||
TextResourceParser p(stream, DisposeAfterUse::YES);
|
||||
|
||||
p.parseString(phrase1);
|
||||
p.parseString(phrase2);
|
||||
p.parseString(phrase3);
|
||||
p.parseString(phrase4);
|
||||
p.parseString(sound1);
|
||||
p.parseString(sound2);
|
||||
p.parseString(sound3);
|
||||
p.parseString(sound4);
|
||||
p.parseInt(answer1);
|
||||
p.parseInt(answer2);
|
||||
p.parseInt(answer3);
|
||||
|
||||
// no need to delete the stream, since TextResourceParser takes ownership
|
||||
// delete stream;
|
||||
|
||||
if (currentChapter == 2 && !strcmp(fileName, "op_5.cal") && flags[38] == 1 && flags[33] == 1) {
|
||||
Common::strlcpy(phrase3, _text[405], 128);
|
||||
Common::strcpy_s(sound3, "405.als");
|
||||
answer3 = 31;
|
||||
}
|
||||
|
||||
if (currentChapter == 6 && !strcmp(fileName, "op_12.cal") && flags[7] == 1) {
|
||||
Common::strlcpy(phrase3, _text[273], 128);
|
||||
Common::strcpy_s(sound3, "273.als");
|
||||
answer3 = 14;
|
||||
}
|
||||
|
||||
if (currentChapter == 6 && !strcmp(fileName, "op_12.cal") && flags[10] == 1) {
|
||||
Common::strlcpy(phrase3, _text[274], 128);
|
||||
Common::strcpy_s(sound3, "274.als");
|
||||
answer3 = 15;
|
||||
}
|
||||
|
||||
cleanupString(phrase1);
|
||||
cleanupString(phrase2);
|
||||
cleanupString(phrase3);
|
||||
cleanupString(phrase4);
|
||||
|
||||
loadPic("car.alg", backSurface);
|
||||
// TODO code here should limit y position for mouse in dialog menu,
|
||||
// but we can't implement this as there is lack in backend functionality
|
||||
// from 1(top) to 31
|
||||
color_abc(kColorLightGreen);
|
||||
|
||||
Common::String ttsPhrase1 = phrase1;
|
||||
Common::String ttsPhrase2 = phrase2;
|
||||
Common::String ttsPhrase3 = phrase3;
|
||||
Common::String ttsPhrase4 = phrase4;
|
||||
|
||||
if (_lang == kRussian) {
|
||||
ttsPhrase1.replace('%', ' ');
|
||||
ttsPhrase2.replace('%', ' ');
|
||||
ttsPhrase3.replace('%', ' ');
|
||||
ttsPhrase4.replace('%', ' ');
|
||||
}
|
||||
|
||||
sayText(ttsPhrase1, Common::TextToSpeechManager::QUEUE);
|
||||
sayText(ttsPhrase2, Common::TextToSpeechManager::QUEUE);
|
||||
sayText(ttsPhrase3, Common::TextToSpeechManager::QUEUE);
|
||||
sayText(ttsPhrase4, Common::TextToSpeechManager::QUEUE);
|
||||
|
||||
while (breakOut == 0 && !shouldQuit()) {
|
||||
updateRoom();
|
||||
|
||||
if (musicStatus() == 0 && roomMusic != 0) {
|
||||
if (currentChapter == 3 || currentChapter == 5) {
|
||||
playMusic(roomMusic);
|
||||
} else { // chapters 1, 2, 4, 6
|
||||
if (flags[11] == 0)
|
||||
playMusic(roomMusic);
|
||||
}
|
||||
}
|
||||
|
||||
updateEvents();
|
||||
flushKeyBuffer();
|
||||
flushActionBuffer();
|
||||
|
||||
phrase1_bottom = 8 * print_abc_opc(phrase1, 2, game1);
|
||||
phrase2_bottom = phrase1_bottom + 8 * print_abc_opc(phrase2, phrase1_bottom + 2, game2);
|
||||
phrase3_bottom = phrase2_bottom + 8 * print_abc_opc(phrase3, phrase2_bottom + 2, game3);
|
||||
phrase4_bottom = phrase3_bottom + 8 * print_abc_opc(phrase4, phrase3_bottom + 2, kDialogOptionUnselected);
|
||||
|
||||
if (_mouseY > 0 && _mouseY < phrase1_bottom) {
|
||||
if (game1 == kDialogOptionClicked && _color != kColorWhite)
|
||||
color_abc(kColorWhite);
|
||||
else if (game1 != kDialogOptionClicked && _color != kColorLightGreen)
|
||||
color_abc(kColorLightGreen);
|
||||
|
||||
print_abc_opc(phrase1, 2, kDialogOptionSelected);
|
||||
|
||||
sayText(ttsPhrase1, Common::TextToSpeechManager::INTERRUPT);
|
||||
|
||||
if (_leftMouseButton == 1) {
|
||||
delay(100);
|
||||
game1 = kDialogOptionClicked;
|
||||
talk(phrase1, sound1);
|
||||
response(answer1);
|
||||
}
|
||||
} else if (_mouseY > phrase1_bottom && _mouseY < phrase2_bottom) {
|
||||
if (game2 == kDialogOptionClicked && _color != kColorWhite)
|
||||
color_abc(kColorWhite);
|
||||
else if (game2 != kDialogOptionClicked && _color != kColorLightGreen)
|
||||
color_abc(kColorLightGreen);
|
||||
|
||||
print_abc_opc(phrase2, phrase1_bottom + 2, kDialogOptionSelected);
|
||||
|
||||
sayText(ttsPhrase2, Common::TextToSpeechManager::INTERRUPT);
|
||||
|
||||
if (_leftMouseButton == 1) {
|
||||
delay(100);
|
||||
game2 = kDialogOptionClicked;
|
||||
talk(phrase2, sound2);
|
||||
response(answer2);
|
||||
}
|
||||
} else if (_mouseY > phrase2_bottom && _mouseY < phrase3_bottom) {
|
||||
if (game3 == kDialogOptionClicked && _color != kColorWhite)
|
||||
color_abc(kColorWhite);
|
||||
else if (game3 != kDialogOptionClicked && _color != kColorLightGreen)
|
||||
color_abc(kColorLightGreen);
|
||||
|
||||
print_abc_opc(phrase3, phrase2_bottom + 2, kDialogOptionSelected);
|
||||
|
||||
sayText(ttsPhrase3, Common::TextToSpeechManager::INTERRUPT);
|
||||
|
||||
if (_leftMouseButton == 1) {
|
||||
delay(100);
|
||||
game3 = kDialogOptionClicked;
|
||||
talk(phrase3, sound3);
|
||||
response(answer3);
|
||||
}
|
||||
} else if (_mouseY > phrase3_bottom && _mouseY < phrase4_bottom) {
|
||||
print_abc_opc(phrase4, phrase3_bottom + 2, kDialogOptionSelected);
|
||||
|
||||
sayText(ttsPhrase4, Common::TextToSpeechManager::INTERRUPT);
|
||||
|
||||
if (_leftMouseButton == 1) {
|
||||
delay(100);
|
||||
talk(phrase4, sound4);
|
||||
breakOut = 1;
|
||||
}
|
||||
} else if (_color != kColorLightGreen)
|
||||
color_abc(kColorLightGreen);
|
||||
else
|
||||
_previousSaid.clear();
|
||||
|
||||
_system->delayMillis(10);
|
||||
updateScreen();
|
||||
} // while (breakOut == 0)
|
||||
|
||||
if (currentChapter == 2)
|
||||
loadPic(menuBackground, backSurface);
|
||||
else
|
||||
loadPic(99, backSurface);
|
||||
}
|
||||
|
||||
void DrasculaEngine::response(int function) {
|
||||
debug(4, "response(%d)", function);
|
||||
|
||||
if (function != 31)
|
||||
playTalkSequence(function);
|
||||
|
||||
if (currentChapter == 2) {
|
||||
bool reloadConversationCharset = false;
|
||||
|
||||
if (function == 16 || function == 20 || function == 23 || function == 29 || function == 31) {
|
||||
reloadConversationCharset = true;
|
||||
loadPic(menuBackground, backSurface);
|
||||
}
|
||||
|
||||
if (function == 16)
|
||||
animation_16_2();
|
||||
else if (function == 20)
|
||||
animation_20_2();
|
||||
else if (function == 23)
|
||||
animation_23_2();
|
||||
else if (function == 29)
|
||||
animation_29_2();
|
||||
else if (function == 31)
|
||||
animation_31_2();
|
||||
|
||||
if (reloadConversationCharset)
|
||||
loadPic("car.alg", backSurface);
|
||||
} else if (currentChapter == 3) {
|
||||
grr();
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Drascula
|
||||
5
engines/drascula/credits.pl
Normal file
5
engines/drascula/credits.pl
Normal file
@@ -0,0 +1,5 @@
|
||||
begin_section("Drascula");
|
||||
add_person("Filippos Karapetis", "bluegr", "");
|
||||
add_person("Paweł Kołodziejski", "aquadran", "");
|
||||
add_person("Thierry Crozat", "criezy", "");
|
||||
end_section();
|
||||
317
engines/drascula/detection.cpp
Normal file
317
engines/drascula/detection.cpp
Normal file
@@ -0,0 +1,317 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "base/plugins.h"
|
||||
#include "common/file.h"
|
||||
|
||||
#include "engines/advancedDetector.h"
|
||||
|
||||
#include "drascula/detection.h"
|
||||
|
||||
static const PlainGameDescriptor drasculaGames[] = {
|
||||
{"drascula", "Drascula: The Vampire Strikes Back"},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
namespace Drascula {
|
||||
|
||||
static const DrasculaGameDescription gameDescriptions[] = {
|
||||
|
||||
//// Packed versions //////////////////////////////////////////////////////
|
||||
|
||||
{
|
||||
// Drascula English version (original packed files)
|
||||
{
|
||||
"drascula",
|
||||
nullptr,
|
||||
{
|
||||
{"packet.001", 0, "c6a8697396e213a18472542d5f547cb4", 32847563},
|
||||
// HACK: List packet.001 twice to ensure this detector entry
|
||||
// is ranked just as high as the others (which each have two
|
||||
// detection files).
|
||||
{"packet.001", 0, "c6a8697396e213a18472542d5f547cb4", 32847563},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformDOS,
|
||||
GF_PACKED,
|
||||
GUIO2(GAMEOPTION_TTS, GUIO_LINKSPEECHTOSFX)
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
// Drascula French version (original packed files)
|
||||
{
|
||||
"drascula",
|
||||
nullptr,
|
||||
{
|
||||
{"packet.001", 0, "c6a8697396e213a18472542d5f547cb4", 32847563},
|
||||
{"packet.002", 1, "4401123400f22f212b89f15fb4b43013", 721122},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::FR_FRA,
|
||||
Common::kPlatformDOS,
|
||||
GF_PACKED,
|
||||
GUIO2(GAMEOPTION_TTS, GUIO_LINKSPEECHTOSFX)
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
// Drascula French version (ScummVM repacked files)
|
||||
{
|
||||
"drascula",
|
||||
nullptr,
|
||||
{
|
||||
{"packet.001", 0, "c6a8697396e213a18472542d5f547cb4", 32847563},
|
||||
{"packet.002", 1, "7b83cedb9bb326ed5143e5c459508d43", 722383},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::FR_FRA,
|
||||
Common::kPlatformDOS,
|
||||
GF_PACKED,
|
||||
GUIO2(GAMEOPTION_TTS, GUIO_LINKSPEECHTOSFX)
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
// Drascula German version (original packed files)
|
||||
{
|
||||
"drascula",
|
||||
nullptr,
|
||||
{
|
||||
{"packet.001", 0, "c6a8697396e213a18472542d5f547cb4", 32847563},
|
||||
{"packet.003", 1, "e8f4dc6091037329bab4ddb1cba35807", 719728},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::DE_DEU,
|
||||
Common::kPlatformDOS,
|
||||
GF_PACKED,
|
||||
GUIO2(GAMEOPTION_TTS, GUIO_LINKSPEECHTOSFX)
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
// Drascula Italian version (original packed version)
|
||||
{
|
||||
"drascula",
|
||||
nullptr,
|
||||
AD_ENTRY1s("packet.001", "0253e924af223f5fe52537023385159b", 32564209),
|
||||
Common::IT_ITA,
|
||||
Common::kPlatformDOS,
|
||||
GF_PACKED,
|
||||
GUIO2(GAMEOPTION_TTS, GUIO_LINKSPEECHTOSFX)
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
// Drascula Italian version (ScummVM repacked files)
|
||||
{
|
||||
"drascula",
|
||||
nullptr,
|
||||
{
|
||||
{"packet.001", 0, "c6a8697396e213a18472542d5f547cb4", 32847563},
|
||||
{"packet.005", 1, "58caac54b891f5d7f335e710e45e5d29", 16209623},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::IT_ITA,
|
||||
Common::kPlatformDOS,
|
||||
GF_PACKED,
|
||||
GUIO2(GAMEOPTION_TTS, GUIO_LINKSPEECHTOSFX)
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
// Drascula Spanish version (original packed version)
|
||||
{
|
||||
"drascula",
|
||||
nullptr,
|
||||
AD_ENTRY1s("packet.001", "3c971aba65a037d29d0b479cad6f5943", 31702652),
|
||||
Common::ES_ESP,
|
||||
Common::kPlatformDOS,
|
||||
GF_PACKED,
|
||||
GUIO2(GAMEOPTION_TTS, GUIO_LINKSPEECHTOSFX)
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
// Drascula Spanish version (ScummVM repacked files)
|
||||
{
|
||||
"drascula",
|
||||
nullptr,
|
||||
{
|
||||
{"packet.001", 0, "c6a8697396e213a18472542d5f547cb4", 32847563},
|
||||
{"packet.004", 1, "a289d3cf80d50f25ec569b653248437e", 17205838},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::ES_ESP,
|
||||
Common::kPlatformDOS,
|
||||
GF_PACKED,
|
||||
GUIO2(GAMEOPTION_TTS, GUIO_LINKSPEECHTOSFX)
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
// Drascula Russian version (ScummVM repacked files)
|
||||
{
|
||||
"drascula",
|
||||
nullptr,
|
||||
{
|
||||
{"packet.001", 0, "c6a8697396e213a18472542d5f547cb4", 32847563},
|
||||
{"packet.006", 1, "e464b99de7f226391337510d5c328258", 697173},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::RU_RUS,
|
||||
Common::kPlatformDOS,
|
||||
GF_PACKED,
|
||||
GUIO2(GAMEOPTION_TTS, GUIO_LINKSPEECHTOSFX)
|
||||
},
|
||||
},
|
||||
|
||||
//// Unpacked versions ////////////////////////////////////////////////////
|
||||
|
||||
{
|
||||
// Drascula English version
|
||||
{
|
||||
"drascula",
|
||||
nullptr,
|
||||
AD_ENTRY1s("14.ald", "09b2735953edcd43af115c65ae00b10e", 1595),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO2(GAMEOPTION_TTS, GUIO_LINKSPEECHTOSFX)
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
// Drascula French version
|
||||
{
|
||||
"drascula",
|
||||
nullptr,
|
||||
AD_ENTRY1s("14.ald", "eeeee96b82169003630e08992248296c", 608),
|
||||
Common::FR_FRA,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO2(GAMEOPTION_TTS, GUIO_LINKSPEECHTOSFX)
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
// Drascula French version (updated - bug #6303)
|
||||
{
|
||||
"drascula",
|
||||
nullptr,
|
||||
AD_ENTRY1s("14.ald", "1f9fbded768bee061cc22bc5bdeab540", 611),
|
||||
Common::FR_FRA,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO2(GAMEOPTION_TTS, GUIO_LINKSPEECHTOSFX)
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
// Drascula German version
|
||||
{
|
||||
"drascula",
|
||||
nullptr,
|
||||
AD_ENTRY1s("14.ald", "72e46089033d56bad1c179ac36e2a9d2", 610),
|
||||
Common::DE_DEU,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO2(GAMEOPTION_TTS, GUIO_LINKSPEECHTOSFX)
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
// Drascula Italian version
|
||||
{
|
||||
"drascula",
|
||||
nullptr,
|
||||
AD_ENTRY1s("14.ald", "02b49a18328d0bf2efe6ba658c9c7a1d", 2098),
|
||||
Common::IT_ITA,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO2(GAMEOPTION_TTS, GUIO_LINKSPEECHTOSFX)
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
// Drascula Italian version (updated - bug #6303)
|
||||
{
|
||||
"drascula",
|
||||
nullptr,
|
||||
AD_ENTRY1s("14.ald", "ccaee939bb3b344c048f28f9205710d1", 2925),
|
||||
Common::IT_ITA,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO2(GAMEOPTION_TTS, GUIO_LINKSPEECHTOSFX)
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
// Drascula Spanish version
|
||||
{
|
||||
"drascula",
|
||||
nullptr,
|
||||
AD_ENTRY1s("14.ald", "0746ed1a5cc8d9728f790c29813f4b43", 23059),
|
||||
Common::ES_ESP,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO2(GAMEOPTION_TTS, GUIO_LINKSPEECHTOSFX)
|
||||
},
|
||||
},
|
||||
{
|
||||
// Drascula Russian version
|
||||
{
|
||||
"drascula",
|
||||
nullptr,
|
||||
AD_ENTRY1s("14.ald", "4dfab170eae935a2e9889196df427a4a", 1426),
|
||||
Common::RU_RUS,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO2(GAMEOPTION_TTS, GUIO_LINKSPEECHTOSFX)
|
||||
},
|
||||
},
|
||||
|
||||
{ AD_TABLE_END_MARKER }
|
||||
};
|
||||
|
||||
class DrasculaMetaEngineDetection : public AdvancedMetaEngineDetection<Drascula::DrasculaGameDescription> {
|
||||
public:
|
||||
DrasculaMetaEngineDetection() : AdvancedMetaEngineDetection(Drascula::gameDescriptions, drasculaGames) {
|
||||
_guiOptions = GUIO2(GUIO_NOMIDI, GAMEOPTION_ORIGINAL_SAVELOAD);
|
||||
}
|
||||
|
||||
const char *getName() const override {
|
||||
return "drascula";
|
||||
}
|
||||
|
||||
const char *getEngineName() const override {
|
||||
return "Drascula: The Vampire Strikes Back";
|
||||
}
|
||||
|
||||
const char *getOriginalCopyright() const override {
|
||||
return "Drascula: The Vampire Strikes Back (C) 2000 Alcachofa Soft, (C) 1996 Digital Dreams Multimedia, (C) 1994 Emilio de Paz";
|
||||
}
|
||||
};
|
||||
|
||||
} // End of namespace Drascula
|
||||
|
||||
REGISTER_PLUGIN_STATIC(DRASCULA_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, Drascula::DrasculaMetaEngineDetection);
|
||||
44
engines/drascula/detection.h
Normal file
44
engines/drascula/detection.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef DRASCULA_DETECTION_H
|
||||
#define DRASCULA_DETECTION_H
|
||||
|
||||
#include "engines/advancedDetector.h"
|
||||
|
||||
namespace Drascula {
|
||||
|
||||
enum DrasculaGameFeatures {
|
||||
GF_PACKED = (1 << 0)
|
||||
};
|
||||
|
||||
struct DrasculaGameDescription {
|
||||
AD_GAME_DESCRIPTION_HELPERS(desc);
|
||||
|
||||
ADGameDescription desc;
|
||||
};
|
||||
|
||||
#define GAMEOPTION_ORIGINAL_SAVELOAD GUIO_GAMEOPTIONS1
|
||||
#define GAMEOPTION_TTS GUIO_GAMEOPTIONS2
|
||||
|
||||
} // End of namespace Drascula
|
||||
|
||||
#endif // DRASCULA_DETECTION_H
|
||||
1240
engines/drascula/drascula.cpp
Normal file
1240
engines/drascula/drascula.cpp
Normal file
File diff suppressed because it is too large
Load Diff
824
engines/drascula/drascula.h
Normal file
824
engines/drascula/drascula.h
Normal file
@@ -0,0 +1,824 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef DRASCULA_DRASCULA_H
|
||||
#define DRASCULA_DRASCULA_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/archive.h"
|
||||
#include "common/endian.h"
|
||||
#include "common/events.h"
|
||||
#include "common/file.h"
|
||||
#include "common/hash-str.h"
|
||||
#include "common/keyboard.h"
|
||||
#include "common/ptr.h"
|
||||
#include "common/random.h"
|
||||
#include "common/savefile.h"
|
||||
#include "common/system.h"
|
||||
#include "common/util.h"
|
||||
#include "common/text-to-speech.h"
|
||||
|
||||
#include "engines/savestate.h"
|
||||
|
||||
#include "drascula/console.h"
|
||||
#include "drascula/detection.h"
|
||||
|
||||
#include "audio/mixer.h"
|
||||
|
||||
#include "engines/engine.h"
|
||||
|
||||
/**
|
||||
* This is the namespace of the Drascula engine.
|
||||
*
|
||||
* Status of this engine: ???
|
||||
*
|
||||
* Games using this engine:
|
||||
* - Drascula: The Vampire Strikes Back
|
||||
*/
|
||||
namespace Drascula {
|
||||
|
||||
#define DRASCULA_DAT_VER 7
|
||||
#define DATAALIGNMENT 4
|
||||
|
||||
enum DRASCULAActions {
|
||||
kActionNone,
|
||||
kActionSkip,
|
||||
kActionLook,
|
||||
kActionPick,
|
||||
kActionOpen,
|
||||
kActionClose,
|
||||
kActionTalk,
|
||||
kActionMove,
|
||||
kActionLoadGame,
|
||||
kActionVerbReset,
|
||||
kActionVolumeControls,
|
||||
kActionSaveGame,
|
||||
kActionSubtitlesEnable,
|
||||
kActionSubtitlesDisable,
|
||||
kActionQuit,
|
||||
kActionEasterEgg,
|
||||
kActionPauseSpeech,
|
||||
kActionConfirmQuit,
|
||||
};
|
||||
|
||||
enum Languages {
|
||||
kEnglish = 0,
|
||||
kSpanish = 1,
|
||||
kGerman = 2,
|
||||
kFrench = 3,
|
||||
kItalian = 4,
|
||||
kRussian = 5
|
||||
};
|
||||
|
||||
enum Verbs {
|
||||
kVerbDefault = -1,
|
||||
kVerbNone = 0,
|
||||
kVerbLook = 1,
|
||||
kVerbPick = 2,
|
||||
kVerbOpen = 3,
|
||||
kVerbClose = 4,
|
||||
kVerbTalk = 5,
|
||||
kVerbMove = 6
|
||||
};
|
||||
|
||||
// Items up to chapter 3
|
||||
enum InventoryItems {
|
||||
kItemMoney = 7,
|
||||
kItemLeaves = 8,
|
||||
kItemCross = 9,
|
||||
kItemSpike = 10,
|
||||
kItemEarplugs = 11,
|
||||
kItemBook = 12,
|
||||
kItemBubbleGum = 13,
|
||||
kItemSickle = 14,
|
||||
kItemTissues = 15,
|
||||
kItemCigarettes = 16,
|
||||
kItemCandle = 17,
|
||||
kItemTwoCoins = 18,
|
||||
kItemOneCoin = 19,
|
||||
kItemReefer = 20,
|
||||
kItemKey = 21,
|
||||
kItemHandbag = 22,
|
||||
kItemEarWithEarPlug = 23,
|
||||
kItemPhone = 28
|
||||
};
|
||||
|
||||
// Items from chapter 4 onwards
|
||||
enum InventoryItems2 {
|
||||
kItemKey2 = 7,
|
||||
kItemCross2 = 9,
|
||||
kItemRope2 = 19,
|
||||
kItemReefer2 = 20,
|
||||
kItemOneCoin2 = 22,
|
||||
kItemPhone2 = 28
|
||||
};
|
||||
|
||||
enum Colors {
|
||||
kColorBrown = 1,
|
||||
kColorDarkBlue = 2,
|
||||
kColorLightGreen = 3,
|
||||
kColorDarkGreen = 4,
|
||||
kColorYellow = 5,
|
||||
kColorOrange = 6,
|
||||
kColorRed = 7,
|
||||
kColorMaroon = 8,
|
||||
kColorPurple = 9,
|
||||
kColorWhite = 10,
|
||||
kColorPink = 11
|
||||
};
|
||||
|
||||
enum SSNFrames {
|
||||
kFrameInit = 0,
|
||||
kFrameCmpRle = 1,
|
||||
kFrameCmpOff = 2,
|
||||
kFrameEndAnim = 3,
|
||||
kFrameSetPal = 4,
|
||||
kFrameMouseKey = 5, // unused
|
||||
kFrameEmptyFrame = 6
|
||||
};
|
||||
|
||||
enum IgorTalkerTypes {
|
||||
kIgorDch = 0,
|
||||
kIgorFront = 1,
|
||||
kIgorDoor = 2,
|
||||
kIgorSeated = 3,
|
||||
kIgorWig = 4
|
||||
};
|
||||
|
||||
enum VonBraunTalkerTypes {
|
||||
kVonBraunNormal = 0,
|
||||
kVonBraunDoor = 1
|
||||
};
|
||||
|
||||
enum AnimFrameTypes {
|
||||
kFrameBlind = 0,
|
||||
kFrameSnore = 1,
|
||||
kFrameBat = 2,
|
||||
kFrameVonBraun = 3,
|
||||
kFramePianist = 4,
|
||||
kFrameDrunk = 5,
|
||||
kFrameCandles = 6,
|
||||
kFramePendulum = 7
|
||||
};
|
||||
|
||||
enum DialogOptionStatus {
|
||||
kDialogOptionUnselected = 1,
|
||||
kDialogOptionSelected = 2,
|
||||
kDialogOptionClicked = 3
|
||||
};
|
||||
|
||||
enum TalkSequenceCommands {
|
||||
kPause = 0,
|
||||
kSetFlag = 1,
|
||||
kClearFlag = 2,
|
||||
kPickObject = 3,
|
||||
kAddObject = 4,
|
||||
kBreakOut = 5,
|
||||
kConverse = 6,
|
||||
kPlaceVB = 7,
|
||||
kUpdateRoom = 8,
|
||||
kUpdateScreen = 9,
|
||||
kTrackProtagonist = 10,
|
||||
kPlaySound = 11,
|
||||
kFinishSound = 12,
|
||||
kTalkerGeneral = 13,
|
||||
kTalkerDrunk = 14,
|
||||
kTalkerPianist = 15,
|
||||
kTalkerBJ = 16,
|
||||
kTalkerVBNormal = 17,
|
||||
kTalkerVBDoor = 18,
|
||||
kTalkerIgorSeated = 19,
|
||||
kTalkerWerewolf = 20,
|
||||
kTalkerMus = 21,
|
||||
kTalkerDrascula = 22,
|
||||
kTalkerBartender0 = 23,
|
||||
kTalkerBartender1 = 24
|
||||
};
|
||||
|
||||
enum CharacterDirections {
|
||||
kDirectionUp = 0,
|
||||
kDirectionDown = 1,
|
||||
kDirectionLeft = 2,
|
||||
kDirectionRight = 3
|
||||
};
|
||||
|
||||
enum MouseCursors {
|
||||
kCursorCrosshair = 0,
|
||||
kCursorCurrentItem = 1
|
||||
};
|
||||
|
||||
enum DoorActions {
|
||||
kCloseDoor = 0,
|
||||
kOpenDoor = 1
|
||||
};
|
||||
|
||||
struct TalkSequenceCommand {
|
||||
int chapter;
|
||||
int sequence;
|
||||
int commandType;
|
||||
int action;
|
||||
};
|
||||
|
||||
#define TEXTD_START 68
|
||||
|
||||
struct DrasculaGameDescription;
|
||||
|
||||
struct RoomTalkAction {
|
||||
int room;
|
||||
int chapter;
|
||||
int action;
|
||||
int objectID;
|
||||
int speechID;
|
||||
};
|
||||
|
||||
struct RoomUpdate {
|
||||
int roomNum;
|
||||
int flag;
|
||||
int flagValue;
|
||||
int sourceX;
|
||||
int sourceY;
|
||||
int destX;
|
||||
int destY;
|
||||
int width;
|
||||
int height;
|
||||
int type; // 0 - background, 1 - rect
|
||||
};
|
||||
|
||||
struct ItemLocation {
|
||||
int x;
|
||||
int y;
|
||||
};
|
||||
|
||||
struct CharInfo {
|
||||
byte inChar;
|
||||
uint16 mappedChar;
|
||||
byte charType; // 0 - letters, 1 - signs, 2 - accented
|
||||
};
|
||||
|
||||
class ArchiveMan : public Common::SearchSet {
|
||||
public:
|
||||
ArchiveMan();
|
||||
|
||||
void enableFallback(bool val) { _fallBack = val; }
|
||||
|
||||
void registerArchive(const Common::Path &filename, int priority);
|
||||
|
||||
Common::SeekableReadStream *open(const Common::Path &filename);
|
||||
|
||||
private:
|
||||
bool _fallBack;
|
||||
};
|
||||
|
||||
class TextResourceParser {
|
||||
Common::DisposablePtr<Common::SeekableReadStream> _stream;
|
||||
int _maxLen;
|
||||
|
||||
void getLine(char *buf);
|
||||
|
||||
public:
|
||||
TextResourceParser(Common::SeekableReadStream *stream, DisposeAfterUse::Flag dispose);
|
||||
|
||||
void parseInt(int &result);
|
||||
void parseString(char *result);
|
||||
};
|
||||
|
||||
|
||||
#define NUM_SAVES 10
|
||||
#define NUM_FLAGS 50
|
||||
#define DIF_MASK 55
|
||||
#define OBJWIDTH 40
|
||||
#define OBJHEIGHT 25
|
||||
|
||||
#define DIF_MASK_HARE 72
|
||||
#define DIF_MASK_ABC 22
|
||||
#define CHAR_WIDTH 8
|
||||
#define CHAR_HEIGHT 6
|
||||
|
||||
#define TALK_HEIGHT 25
|
||||
#define TALK_WIDTH 23
|
||||
#define STEP_X 8
|
||||
#define STEP_Y 3
|
||||
#define CHARACTER_HEIGHT 70
|
||||
#define CHARACTER_WIDTH 43
|
||||
#define FEET_HEIGHT 12
|
||||
|
||||
#define CHAR_WIDTH_OPC 6
|
||||
#define CHAR_HEIGHT_OPC 5
|
||||
#define NO_DOOR 99
|
||||
|
||||
#define COMPLETE_PAL 256
|
||||
#define HALF_PAL 128
|
||||
|
||||
#define KEYBUFSIZE 16
|
||||
#define ACTIONBUFSIZE 16
|
||||
|
||||
static const int interf_x[] = { 1, 65, 129, 193, 1, 65, 129 };
|
||||
static const int interf_y[] = { 51, 51, 51, 51, 83, 83, 83 };
|
||||
|
||||
struct RoomHandlers;
|
||||
|
||||
class DrasculaEngine : public Engine {
|
||||
protected:
|
||||
// Engine APIs
|
||||
Common::Error run() override;
|
||||
|
||||
public:
|
||||
DrasculaEngine(OSystem *syst, const DrasculaGameDescription *gameDesc);
|
||||
~DrasculaEngine() override;
|
||||
bool hasFeature(EngineFeature f) const override;
|
||||
|
||||
void syncSoundSettings() override;
|
||||
|
||||
Common::Error loadGameState(int slot) override;
|
||||
bool canLoadGameStateCurrently(Common::U32String *msg = nullptr) override;
|
||||
Common::Error saveGameState(int slot, const Common::String &desc, bool isAutosave = false) override;
|
||||
bool canSaveGameStateCurrently(Common::U32String *msg = nullptr) override;
|
||||
|
||||
Common::RandomSource *_rnd;
|
||||
const DrasculaGameDescription *_gameDescription;
|
||||
uint32 getFeatures() const;
|
||||
Common::Language getLanguage() const;
|
||||
void updateEvents();
|
||||
|
||||
void loadArchives();
|
||||
|
||||
Audio::SoundHandle _soundHandle;
|
||||
|
||||
void allocMemory();
|
||||
void freeMemory();
|
||||
void endChapter();
|
||||
|
||||
void loadPic(int roomNum, byte *targetSurface, int colorCount = 1) {
|
||||
char rm[20];
|
||||
Common::sprintf_s(rm, "%i.alg", roomNum);
|
||||
loadPic(rm, targetSurface, colorCount);
|
||||
}
|
||||
|
||||
void loadPic(const char *NamePcc, byte *targetSurface, int colorCount = 1);
|
||||
|
||||
typedef signed char DacPalette256[256][3];
|
||||
|
||||
void setRGB(byte *pal, int plt);
|
||||
void assignPalette(DacPalette256 pal);
|
||||
void setDefaultPalette(DacPalette256 pal);
|
||||
void setPalette(byte *PalBuf);
|
||||
void copyBackground(int xorg, int yorg, int xdes, int ydes, int width,
|
||||
int height, byte *src, byte *dest);
|
||||
|
||||
void copyBackground() {
|
||||
copyBackground(0, 0, 0, 0, 320, 200, bgSurface, screenSurface);
|
||||
}
|
||||
|
||||
void copyRect(int xorg, int yorg, int xdes, int ydes, int width,
|
||||
int height, byte *src, byte *dest);
|
||||
void updateScreen() {
|
||||
updateScreen(0, 0, 0, 0, 320, 200, screenSurface);
|
||||
}
|
||||
void updateScreen(int xorg, int yorg, int xdes, int ydes, int width, int height, byte *buffer);
|
||||
int checkWrapX(int x) {
|
||||
if (x < 0) x += 320;
|
||||
if (x > 319) x -= 320;
|
||||
return x;
|
||||
}
|
||||
int checkWrapY(int y) {
|
||||
if (y < 0) y += 200;
|
||||
if (y > 199) y -= 200;
|
||||
return y;
|
||||
}
|
||||
|
||||
DacPalette256 gamePalette;
|
||||
DacPalette256 defaultPalette;
|
||||
DacPalette256 brightPalette;
|
||||
DacPalette256 darkPalette;
|
||||
|
||||
byte *crosshairCursor;
|
||||
byte *mouseCursor;
|
||||
|
||||
// Graphics buffers/pointers
|
||||
byte *bgSurface;
|
||||
byte *backSurface;
|
||||
byte *cursorSurface;
|
||||
byte *drawSurface3;
|
||||
byte *drawSurface2;
|
||||
byte *tableSurface;
|
||||
byte *extraSurface; // not sure about this one, was "dir_hare_dch"
|
||||
byte *screenSurface;
|
||||
byte *frontSurface;
|
||||
|
||||
byte cPal[768];
|
||||
|
||||
ArchiveMan _archives;
|
||||
|
||||
int actorFrames[8];
|
||||
|
||||
int previousMusic, roomMusic;
|
||||
int _roomNumber;
|
||||
char roomDisk[20];
|
||||
char currentData[20];
|
||||
int numRoomObjs;
|
||||
char menuBackground[20];
|
||||
|
||||
char objName[30][20];
|
||||
char iconName[44][13];
|
||||
|
||||
int objectNum[40], visible[40], isDoor[40];
|
||||
int trackObj[40];
|
||||
Common::Point _roomObject[40];
|
||||
int inventoryObjects[43];
|
||||
int _doorDestRoom[40];
|
||||
Common::Point _doorDestPoint[40];
|
||||
int trackCharacter_alkeva[40], _roomExitId[40];
|
||||
Common::Rect _objectRect[40];
|
||||
int takeObject, pickedObject;
|
||||
bool _subtitlesDisabled;
|
||||
bool _menuBar, _menuScreen, _hasName;
|
||||
char textName[20];
|
||||
int curExcuseLook;
|
||||
int curExcuseAction;
|
||||
|
||||
int flags[NUM_FLAGS];
|
||||
|
||||
int frame_y;
|
||||
int curX, curY, curDirection, trackProtagonist, _characterFrame;
|
||||
bool _characterMoved, _characterVisible;
|
||||
int roomX, roomY, checkFlags;
|
||||
int doBreak;
|
||||
int stepX, stepY;
|
||||
int curHeight, curWidth, feetHeight;
|
||||
Common::Rect _walkRect;
|
||||
int lowerLimit, upperLimit;
|
||||
int trackFinal;
|
||||
bool _walkToObject;
|
||||
int objExit;
|
||||
int _startTime;
|
||||
int hasAnswer;
|
||||
int savedTime;
|
||||
int breakOut;
|
||||
int vonBraunX, trackVonBraun, vonBraunHasMoved;
|
||||
float newHeight, newWidth;
|
||||
int factor_red[202];
|
||||
int color_solo;
|
||||
int blinking;
|
||||
int igorX, igorY, trackIgor;
|
||||
int drasculaX, drasculaY, trackDrascula;
|
||||
int term_int;
|
||||
int currentChapter;
|
||||
bool _loadedDifferentChapter;
|
||||
int _currentSaveSlot;
|
||||
bool _canSaveLoad;
|
||||
int _color;
|
||||
int musicStopped;
|
||||
int _mouseX, _mouseY, _leftMouseButton, _rightMouseButton;
|
||||
bool _leftMouseButtonHeld;
|
||||
|
||||
Common::KeyState _keyBuffer[KEYBUFSIZE];
|
||||
int _keyBufferHead;
|
||||
int _keyBufferTail;
|
||||
|
||||
Common::CustomEventType _actionBuffer[ACTIONBUFSIZE];
|
||||
int _actionBufferHead;
|
||||
int _actionBufferTail;
|
||||
|
||||
bool loadDrasculaDat();
|
||||
|
||||
bool runCurrentChapter();
|
||||
void black();
|
||||
void pickObject(int);
|
||||
void walkUp();
|
||||
void walkDown();
|
||||
void moveVonBraun();
|
||||
void placeVonBraun(int pointX);
|
||||
void hipo_sin_nadie(int counter);
|
||||
void toggleDoor(int nflag, int doorNum, int action);
|
||||
void showMap();
|
||||
|
||||
void enterRoom(int);
|
||||
void clearRoom();
|
||||
void walkToPoint(Common::Point pos);
|
||||
void moveCursor();
|
||||
void checkObjects();
|
||||
void selectVerbFromBar();
|
||||
bool verify1();
|
||||
bool verify2();
|
||||
Common::KeyCode getScan();
|
||||
void addKeyToBuffer(Common::KeyState& key);
|
||||
void flushKeyBuffer();
|
||||
Common::CustomEventType getAction();
|
||||
void addActionToBuffer(Common::CustomEventType& action);
|
||||
void flushActionBuffer();
|
||||
void selectVerb(int);
|
||||
int updateVolume(int prevVolume, int prevVolumeY);
|
||||
void volumeControls();
|
||||
|
||||
bool saveLoadScreen();
|
||||
bool scummVMSaveLoadDialog(bool isSave);
|
||||
Common::String enterName(Common::String &selectedName);
|
||||
void loadSaveNames();
|
||||
void saveGame(int slot, const Common::String &desc);
|
||||
bool loadGame(int slot);
|
||||
void checkForOldSaveGames();
|
||||
void convertSaveGame(int slot, const Common::String &desc);
|
||||
|
||||
void print_abc(const char *, int, int);
|
||||
void delay(int ms);
|
||||
bool confirmExit();
|
||||
void screenSaver();
|
||||
void chooseObject(int object);
|
||||
void addObject(int);
|
||||
int removeObject(int osj);
|
||||
void playFLI(const char *filefli, int vel);
|
||||
void fadeFromBlack(int fadeSpeed);
|
||||
void fadeToBlack(int fadeSpeed);
|
||||
signed char adjustToVGA(signed char value);
|
||||
void color_abc(int cl);
|
||||
bool textFitsCentered(char *text, int x);
|
||||
void centerText(const char *,int,int);
|
||||
void playSound(int soundNum);
|
||||
bool animate(const char *animation, int FPS);
|
||||
void pause(int);
|
||||
void placeIgor();
|
||||
void placeDrascula();
|
||||
|
||||
void talkInit(const char *filename);
|
||||
bool isTalkFinished();
|
||||
void talk_igor(int, int);
|
||||
void talk_drascula(int index, int talkerType = 0);
|
||||
void talk_solo(const char *, const char *);
|
||||
void talk_bartender(int, int talkerType = 0);
|
||||
void talk_pen(const char *, const char *, int);
|
||||
void talk_bj_bed(int);
|
||||
void talk_htel(int);
|
||||
void talk_bj(int);
|
||||
void talk_trunk(int);
|
||||
void talk(int);
|
||||
void talk(const char *, const char *);
|
||||
void talk_sync(const char *, const char *, const char *);
|
||||
void talk_drunk(int);
|
||||
void talk_pianist(int);
|
||||
void talk_werewolf(int);
|
||||
void talk_mus(int);
|
||||
void talk_drascula_big(int);
|
||||
void talk_vonBraun(int, int);
|
||||
void talk_blind(int);
|
||||
void talk_hacker(int);
|
||||
void talk_generic(const char* said, const char* filename, int* faces, int faceCount, int* coords, byte* surface);
|
||||
|
||||
void hiccup(int);
|
||||
void finishSound();
|
||||
void stopSound();
|
||||
void playMusic(int p);
|
||||
void stopMusic();
|
||||
void updateMusic();
|
||||
int musicStatus();
|
||||
void updateRoom();
|
||||
void updateDoor(int);
|
||||
void setPaletteBase(int darkness);
|
||||
void updateVisible();
|
||||
void startWalking();
|
||||
void updateRefresh();
|
||||
void updateRefresh_pre();
|
||||
void moveCharacters();
|
||||
void showMenu();
|
||||
void clearMenu();
|
||||
void removeObject();
|
||||
bool exitRoom(int);
|
||||
bool pickupObject();
|
||||
bool checkAction(int);
|
||||
void setCursor(int cursor);
|
||||
void showCursor();
|
||||
void hideCursor();
|
||||
bool isCursorVisible();
|
||||
bool soundIsActive();
|
||||
void waitFrameSSN();
|
||||
void mixVideo(byte *OldScreen, byte *NewScreen, uint16 oldPitch);
|
||||
void decodeRLE(byte *BufferRLE, byte *MiVideoRLE, uint16 pitch = 320);
|
||||
void decodeOffset(byte *BufferOFF, byte *MiVideoOFF, int length);
|
||||
int playFrameSSN(Common::SeekableReadStream *stream);
|
||||
|
||||
int FrameSSN;
|
||||
int globalSpeed;
|
||||
uint32 LastFrame;
|
||||
|
||||
int flag_tv;
|
||||
|
||||
void showFrame(Common::SeekableReadStream *stream, bool firstFrame = false);
|
||||
int getTime();
|
||||
void reduce_hare_chico(int, int, int, int, int, int, int, byte *, byte *);
|
||||
void quadrant_1();
|
||||
void quadrant_2();
|
||||
void quadrant_3();
|
||||
void quadrant_4();
|
||||
void increaseFrameNum();
|
||||
int whichObject();
|
||||
bool checkMenuFlags();
|
||||
void setupRoomsTable();
|
||||
void freeRoomsTable();
|
||||
bool roomParse(int, int);
|
||||
void cleanupString(char *string);
|
||||
void playTalkSequence(int sequence);
|
||||
void doTalkSequenceCommand(TalkSequenceCommand cmd);
|
||||
void converse(int);
|
||||
int print_abc_opc(const char *, int, int);
|
||||
void response(int);
|
||||
void activatePendulum();
|
||||
|
||||
void MusicFadeout();
|
||||
void playFile(const char *fname);
|
||||
|
||||
void grr();
|
||||
void updateAnim(int y, int destX, int destY, int width, int height, int count, byte* src, int delayVal = 3, bool copyRectangle = false);
|
||||
|
||||
bool room(int rN, int fl);
|
||||
bool room_0(int);
|
||||
bool room_1(int);
|
||||
bool room_2(int);
|
||||
bool room_3(int);
|
||||
bool room_4(int);
|
||||
bool room_5(int);
|
||||
bool room_6(int);
|
||||
bool room_7(int);
|
||||
bool room_8(int);
|
||||
bool room_9(int);
|
||||
bool room_12(int);
|
||||
bool room_13(int);
|
||||
bool room_14(int);
|
||||
bool room_15(int);
|
||||
bool room_16(int);
|
||||
bool room_17(int);
|
||||
bool room_18(int);
|
||||
bool room_21(int);
|
||||
bool room_22(int);
|
||||
bool room_23(int);
|
||||
bool room_24(int);
|
||||
bool room_26(int);
|
||||
bool room_27(int);
|
||||
bool room_29(int);
|
||||
bool room_30(int);
|
||||
bool room_31(int);
|
||||
bool room_34(int);
|
||||
bool room_35(int);
|
||||
bool room_49(int);
|
||||
bool room_53(int);
|
||||
bool room_54(int);
|
||||
bool room_55(int);
|
||||
bool room_56(int);
|
||||
bool room_58(int);
|
||||
bool room_59(int);
|
||||
bool room_60(int);
|
||||
bool room_62(int);
|
||||
bool room_102(int);
|
||||
|
||||
void asco();
|
||||
|
||||
void animation_1_1(); // Game introduction
|
||||
void animation_2_1(); // John falls in love with BJ, who is then abducted by Drascula
|
||||
void animation_3_1(); // John talks with the bartender to book a room
|
||||
void animation_4_1(); // John talks with the pianist
|
||||
//
|
||||
void animation_2_2(); // John enters the chapel via the window
|
||||
void animation_4_2(); // John talks with the blind man (closeup)
|
||||
void animation_5_2(); // John breaks the chapel window with the pike
|
||||
void animation_6_2(); // The blind man (closeup) thanks John for giving him money and hands him the sickle
|
||||
void animation_7_2(); // John uses the sickle
|
||||
void animation_11_2(); // The drunk man says "they're all dead, thanks *hic*"
|
||||
void animation_12_2(); // Conversation screen - John talks to the pianist after BJ is abducted by Drascula
|
||||
void animation_13_2(); // ???
|
||||
void animation_14_2(); // The glass box falls from the ceiling
|
||||
void animation_16_2(); // The drunk tells us about Von Braun
|
||||
void animation_20_2(); // Von Braun tells John that he needs to have special skills to fight vampires
|
||||
void animation_23_2(); // Von Braun tests John's reactions to scratching noises
|
||||
void animation_24_2(); // Conversation screen - John talks with Von Braun
|
||||
void animation_25_2(); // The glass box is lifted back to the ceiling
|
||||
void animation_26_2(); // John gives the book to the pianist and gets his earplugs in return
|
||||
void animation_27_2(); // Von Braun admits that John is ready to fight vampires and gives him his money back
|
||||
void animation_29_2(); // Von Braun tells John what ingredients he needs for the brew
|
||||
void animation_31_2(); // Von Braun obtains the items needed for the brew from John and creates it
|
||||
void animation_34_2(); // John kicks an object
|
||||
void animation_35_2(); // John jumps into the well
|
||||
void animation_36_2(); // John asks the bartender about the pianist
|
||||
//
|
||||
void animation_2_3(); // John uses the cross with the Frankenstein-zombie ("yoda") and destroys him
|
||||
void animation_6_3(); // Frankenstein is blocking John's path
|
||||
//
|
||||
void animation_castle(); // Chapter 4 start - Drascula's castle exterior, lightning strikes
|
||||
void animation_1_4(); // Conversation screen - John talks with Igor
|
||||
void animation_5_4(); // John enters Igor's room dressed as Drascula
|
||||
void animation_6_4(); // Igor says that he's going for supper
|
||||
void animation_7_4(); // John removes Drascula's disguise
|
||||
void animation_8_4(); // Secret passage behind bookcase is revealed
|
||||
//
|
||||
void animation_1_5(); // John finds BJ
|
||||
void animation_5_5(); // ???
|
||||
void animation_12_5(); // Frankenstein comes to life
|
||||
void animation_12_5_frankenstein();
|
||||
void animation_14_5(); // John finds out that an object is empty
|
||||
//
|
||||
void animation_1_6(); // ???
|
||||
void animation_5_6(); // John is tied to the table. Drascula and Igor lower the pendulum
|
||||
void animation_6_6(); // John uses the pendulum to break free
|
||||
void animation_9_6(); // Game ending - John uses the cross on Drascula and reads BJ's letter
|
||||
void animation_19_6(); // Someone pops up from behind a door when trying to open it
|
||||
|
||||
void update_1_pre();
|
||||
void update_2();
|
||||
void update_3();
|
||||
void update_4();
|
||||
void update_6_pre();
|
||||
void update_9_pre();
|
||||
void update_14_pre();
|
||||
void update_13();
|
||||
void update_16_pre();
|
||||
void update_18_pre();
|
||||
void update_23_pre();
|
||||
void update_26_pre();
|
||||
void update_26();
|
||||
void update_35_pre();
|
||||
void update_58();
|
||||
void update_58_pre();
|
||||
void update_59_pre();
|
||||
void update_60_pre();
|
||||
void update_60();
|
||||
void update_62();
|
||||
void update_62_pre();
|
||||
void update_102();
|
||||
|
||||
void sayText(const Common::String &text, Common::TextToSpeechManager::Action action);
|
||||
|
||||
private:
|
||||
int _lang;
|
||||
|
||||
CharInfo *_charMap;
|
||||
int _charMapSize;
|
||||
|
||||
int _itemLocationsSize;
|
||||
int _polXSize;
|
||||
int _verbBarXSize;
|
||||
int _x1dMenuSize;
|
||||
int _frameXSize;
|
||||
int _candleXSize;
|
||||
int _pianistXSize;
|
||||
int _drunkXSize;
|
||||
int _roomPreUpdatesSize;
|
||||
int _roomUpdatesSize;
|
||||
int _roomActionsSize;
|
||||
int _talkSequencesSize;
|
||||
int _numLangs;
|
||||
|
||||
char **_text;
|
||||
char **_textd;
|
||||
char **_textb;
|
||||
char **_textbj;
|
||||
char **_texte;
|
||||
char **_texti;
|
||||
char **_textl;
|
||||
char **_textp;
|
||||
char **_textt;
|
||||
char **_textvb;
|
||||
char **_textsys;
|
||||
char **_texthis;
|
||||
char **_textverbs;
|
||||
char **_textmisc;
|
||||
char **_textd1;
|
||||
ItemLocation *_itemLocations;
|
||||
int *_polX, *_polY;
|
||||
int *_verbBarX;
|
||||
int *_x1d_menu, *_y1d_menu;
|
||||
int *_frameX;
|
||||
int *_candleX, *_candleY;
|
||||
int *_pianistX, *_drunkX;
|
||||
RoomUpdate *_roomPreUpdates, *_roomUpdates;
|
||||
RoomTalkAction *_roomActions;
|
||||
TalkSequenceCommand *_talkSequences;
|
||||
Common::String _saveNames[10];
|
||||
Common::String _previousSaid;
|
||||
Common::CodePage _ttsTextEncoding;
|
||||
|
||||
char **loadTexts(Common::File &in);
|
||||
void freeTexts(char **ptr);
|
||||
|
||||
protected:
|
||||
RoomHandlers *_roomHandlers;
|
||||
};
|
||||
|
||||
|
||||
} // End of namespace Drascula
|
||||
|
||||
#endif /* DRASCULA_DRASCULA_H */
|
||||
754
engines/drascula/graphics.cpp
Normal file
754
engines/drascula/graphics.cpp
Normal file
@@ -0,0 +1,754 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "drascula/drascula.h"
|
||||
#include "graphics/surface.h"
|
||||
|
||||
#include "common/stream.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/text-to-speech.h"
|
||||
|
||||
#include "backends/keymapper/keymapper.h"
|
||||
|
||||
namespace Drascula {
|
||||
|
||||
void DrasculaEngine::allocMemory() {
|
||||
// FIXME: decodeOffset writes beyond 64000, so this
|
||||
// buffer has been initialized to 64256 bytes (like
|
||||
// the original did with the MiVideoSSN buffer)
|
||||
screenSurface = (byte *)malloc(64256);
|
||||
assert(screenSurface);
|
||||
frontSurface = (byte *)malloc(64000);
|
||||
assert(frontSurface);
|
||||
backSurface = (byte *)malloc(64000);
|
||||
assert(backSurface);
|
||||
bgSurface = (byte *)malloc(64000);
|
||||
assert(bgSurface);
|
||||
drawSurface2 = (byte *)malloc(64000);
|
||||
assert(drawSurface2);
|
||||
drawSurface3 = (byte *)malloc(64000);
|
||||
assert(drawSurface3);
|
||||
tableSurface = (byte *)malloc(64000);
|
||||
assert(tableSurface);
|
||||
extraSurface = (byte *)malloc(64000);
|
||||
assert(extraSurface);
|
||||
crosshairCursor = (byte *)malloc(OBJWIDTH * OBJHEIGHT);
|
||||
assert(crosshairCursor);
|
||||
mouseCursor = (byte *)malloc(OBJWIDTH * OBJHEIGHT);
|
||||
assert(mouseCursor);
|
||||
cursorSurface = (byte *)malloc(64000);
|
||||
}
|
||||
|
||||
void DrasculaEngine::freeMemory() {
|
||||
free(screenSurface);
|
||||
free(bgSurface);
|
||||
free(backSurface);
|
||||
free(drawSurface2);
|
||||
free(tableSurface);
|
||||
free(drawSurface3);
|
||||
free(extraSurface);
|
||||
free(frontSurface);
|
||||
free(crosshairCursor);
|
||||
free(mouseCursor);
|
||||
free(cursorSurface);
|
||||
}
|
||||
|
||||
void DrasculaEngine::moveCursor() {
|
||||
copyBackground();
|
||||
|
||||
updateRefresh_pre();
|
||||
moveCharacters();
|
||||
updateRefresh();
|
||||
|
||||
if (!strcmp(textName, _textmisc[3]) && _hasName) {
|
||||
if (_color != kColorRed && !_menuScreen)
|
||||
color_abc(kColorRed);
|
||||
} else if (!_menuScreen && _color != kColorLightGreen)
|
||||
color_abc(kColorLightGreen);
|
||||
if (_hasName && !_menuScreen) {
|
||||
sayText(textName, Common::TextToSpeechManager::INTERRUPT);
|
||||
|
||||
centerText(textName, _mouseX, _mouseY);
|
||||
} else if (!_menuBar && !_menuScreen)
|
||||
_previousSaid.clear();
|
||||
|
||||
if (_menuScreen)
|
||||
showMenu();
|
||||
else if (_menuBar)
|
||||
clearMenu();
|
||||
}
|
||||
|
||||
void DrasculaEngine::loadPic(const char *NamePcc, byte *targetSurface, int colorCount) {
|
||||
debug(5, "loadPic(%s)", NamePcc);
|
||||
|
||||
uint dataSize = 0;
|
||||
byte *pcxData;
|
||||
|
||||
Common::SeekableReadStream *stream = _archives.open(NamePcc);
|
||||
if (!stream)
|
||||
error("missing game data %s %c", NamePcc, 7);
|
||||
|
||||
dataSize = stream->size() - 128 - (256 * 3);
|
||||
pcxData = (byte *)malloc(dataSize);
|
||||
|
||||
stream->seek(128, SEEK_SET);
|
||||
stream->read(pcxData, dataSize);
|
||||
|
||||
decodeRLE(pcxData, targetSurface);
|
||||
free(pcxData);
|
||||
|
||||
for (int i = 0; i < 256; i++) {
|
||||
cPal[i * 3 + 0] = stream->readByte();
|
||||
cPal[i * 3 + 1] = stream->readByte();
|
||||
cPal[i * 3 + 2] = stream->readByte();
|
||||
}
|
||||
|
||||
delete stream;
|
||||
|
||||
setRGB((byte *)cPal, colorCount);
|
||||
}
|
||||
|
||||
void DrasculaEngine::showFrame(Common::SeekableReadStream *stream, bool firstFrame) {
|
||||
int dataSize = stream->readSint32LE();
|
||||
byte *pcxData = (byte *)malloc(dataSize);
|
||||
stream->read(pcxData, dataSize);
|
||||
|
||||
for (int i = 0; i < 256; i++) {
|
||||
cPal[i * 3 + 0] = stream->readByte();
|
||||
cPal[i * 3 + 1] = stream->readByte();
|
||||
cPal[i * 3 + 2] = stream->readByte();
|
||||
}
|
||||
|
||||
byte *prevFrame = (byte *)malloc(64000);
|
||||
Graphics::Surface *screenSurf = _system->lockScreen();
|
||||
byte *screenBuffer = (byte *)screenSurf->getPixels();
|
||||
uint16 screenPitch = screenSurf->pitch;
|
||||
for (int y = 0; y < 200; y++) {
|
||||
memcpy(prevFrame+y*320, screenBuffer+y*screenPitch, 320);
|
||||
}
|
||||
|
||||
decodeRLE(pcxData, screenBuffer, screenPitch);
|
||||
free(pcxData);
|
||||
|
||||
if (!firstFrame)
|
||||
mixVideo(screenBuffer, prevFrame, screenPitch);
|
||||
|
||||
_system->unlockScreen();
|
||||
_system->updateScreen();
|
||||
|
||||
if (firstFrame)
|
||||
setPalette(cPal);
|
||||
|
||||
free(prevFrame);
|
||||
}
|
||||
|
||||
void DrasculaEngine::copyBackground(int xorg, int yorg, int xdes, int ydes, int width, int height, byte *src, byte *dest) {
|
||||
debug(5, "DrasculaEngine::copyBackground(xorg:%d, yorg:%d, xdes:%d, ydes:%d width:%d height:%d, src, dest)", xorg, yorg, xdes, ydes, width,height);
|
||||
dest += xdes + ydes * 320;
|
||||
src += xorg + yorg * 320;
|
||||
/* Unoptimized code
|
||||
for (int x = 0; x < height; x++) {
|
||||
memcpy(dest + 320 * x, src + 320 * x, width);
|
||||
} */
|
||||
|
||||
// A bit more optimized code, thanks to Fingolfin
|
||||
// Uses 2 less registers and performs 2 less multiplications
|
||||
int x = height;
|
||||
while (x--) {
|
||||
memcpy(dest, src, width);
|
||||
dest += 320;
|
||||
src += 320;
|
||||
}
|
||||
}
|
||||
|
||||
void DrasculaEngine::copyRect(int xorg, int yorg, int xdes, int ydes, int width,
|
||||
int height, byte *src, byte *dest) {
|
||||
int y, x;
|
||||
|
||||
//
|
||||
if (ydes < 0) {
|
||||
yorg += -ydes;
|
||||
height += ydes;
|
||||
ydes = 0;
|
||||
}
|
||||
if (xdes < 0) {
|
||||
xorg += -xdes;
|
||||
width += xdes;
|
||||
xdes = 0;
|
||||
}
|
||||
if ((xdes + width) > 319)
|
||||
width -= (xdes + width) - 320;
|
||||
if ((ydes + height) > 199)
|
||||
height -= (ydes + height) - 200;
|
||||
//
|
||||
|
||||
dest += xdes + ydes * 320;
|
||||
src += xorg + yorg * 320;
|
||||
|
||||
assert(xorg >= 0);
|
||||
assert(yorg >= 0);
|
||||
assert(xorg + width <= 320);
|
||||
assert(yorg + height <= 200);
|
||||
|
||||
int ptr = 0;
|
||||
for (y = 0; y < height; y++) {
|
||||
for (x = 0; x < width; x++) {
|
||||
if (src[ptr] != 255)
|
||||
dest[ptr] = src[ptr];
|
||||
ptr++;
|
||||
}
|
||||
ptr += 320 - width;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void DrasculaEngine::updateScreen(int xorg, int yorg, int xdes, int ydes, int width, int height, byte *buffer) {
|
||||
_system->copyRectToScreen(buffer + xorg + yorg * 320, 320, xdes, ydes, width, height);
|
||||
_system->updateScreen();
|
||||
}
|
||||
|
||||
void DrasculaEngine::print_abc(const char *said, int screenX, int screenY) {
|
||||
int letterY = 0, letterX = 0, i;
|
||||
uint len = strlen(said);
|
||||
byte c;
|
||||
|
||||
byte *srcSurface = tableSurface;
|
||||
if (_lang == kSpanish && currentChapter == 6)
|
||||
srcSurface = extraSurface;
|
||||
|
||||
for (uint h = 0; h < len; h++) {
|
||||
c = toupper(said[h]);
|
||||
|
||||
for (i = 0; i < _charMapSize; i++) {
|
||||
if (c == _charMap[i].inChar) {
|
||||
letterX = _charMap[i].mappedChar;
|
||||
|
||||
switch (_charMap[i].charType) {
|
||||
case 0: // letters
|
||||
default:
|
||||
letterY = (_lang == kSpanish) ? 149 : 158;
|
||||
break;
|
||||
case 1: // signs
|
||||
letterY = (_lang == kSpanish) ? 160 : 169;
|
||||
break;
|
||||
case 2: // accented
|
||||
letterY = 180;
|
||||
break;
|
||||
} // switch
|
||||
break;
|
||||
} // if
|
||||
} // for
|
||||
|
||||
copyRect(letterX, letterY, screenX, screenY,
|
||||
CHAR_WIDTH, CHAR_HEIGHT, srcSurface, screenSurface);
|
||||
|
||||
screenX = screenX + CHAR_WIDTH;
|
||||
if (screenX > 317) {
|
||||
screenX = 0;
|
||||
screenY = screenY + CHAR_HEIGHT + 2;
|
||||
}
|
||||
} // for
|
||||
}
|
||||
|
||||
int DrasculaEngine::print_abc_opc(const char *said, int screenY, int game) {
|
||||
int signY, letterY, letterX = 0;
|
||||
uint len = strlen(said);
|
||||
|
||||
int screenX = 1;
|
||||
int lines = 1;
|
||||
|
||||
for (uint h = 0; h < len; h++) {
|
||||
int wordLength;
|
||||
|
||||
// Look ahead to the end of the word.
|
||||
wordLength = 0;
|
||||
int pos = h;
|
||||
while (said[pos] && said[pos] != ' ') {
|
||||
wordLength++;
|
||||
pos++;
|
||||
}
|
||||
|
||||
if (screenX + wordLength * CHAR_WIDTH_OPC > 317) {
|
||||
screenX = 0;
|
||||
screenY += (CHAR_HEIGHT + 2);
|
||||
lines++;
|
||||
}
|
||||
|
||||
if (game == 1) {
|
||||
letterY = 6;
|
||||
signY = 15;
|
||||
} else if (game == 3) {
|
||||
letterY = 56;
|
||||
signY = 65;
|
||||
} else {
|
||||
letterY = 31;
|
||||
signY = 40;
|
||||
}
|
||||
|
||||
byte c = toupper(said[h]);
|
||||
|
||||
// WORKAROUND: Even original did not process it correctly
|
||||
// Fixes apostrophe rendering
|
||||
if (_lang != kSpanish)
|
||||
if (c == '\'')
|
||||
c = (byte)'\244';
|
||||
|
||||
for (int i = 0; i < _charMapSize; i++) {
|
||||
if (c == _charMap[i].inChar) {
|
||||
// Convert the mapped char of the normal font to the
|
||||
// mapped char of the dialogue font
|
||||
|
||||
int multiplier = (_charMap[i].mappedChar - 6) / 9;
|
||||
|
||||
letterX = multiplier * 7 + 10;
|
||||
|
||||
if (_charMap[i].charType > 0)
|
||||
letterY = signY;
|
||||
break;
|
||||
} // if
|
||||
} // for
|
||||
|
||||
copyRect(letterX, letterY, screenX, screenY,
|
||||
CHAR_WIDTH_OPC, CHAR_HEIGHT_OPC, backSurface, screenSurface);
|
||||
|
||||
screenX = screenX + CHAR_WIDTH_OPC;
|
||||
}
|
||||
|
||||
return lines;
|
||||
}
|
||||
|
||||
bool DrasculaEngine::textFitsCentered(char *text, int x) {
|
||||
int textLen = strlen(text);
|
||||
int halfLen = (textLen / 2) * CHAR_WIDTH;
|
||||
|
||||
//if (x > 160)
|
||||
// x = 315 - x;
|
||||
//return (halfLen <= x);
|
||||
|
||||
// The commented out code above is what the original engine is doing. Instead of testing the
|
||||
// upper bound if x is greater than 160 it takes the complement to 315 and test only the lower
|
||||
// bounds.
|
||||
// Also note that since it does an integer division to compute the half length of the string,
|
||||
// in the case where the string has an odd number of characters there is one more character to
|
||||
// the right than to the left. If the string center is beyond 160, this is taken care of by
|
||||
// taking the complement to 315 instead of 320. But if the string center is close to the screen
|
||||
// center, but not greater than 160, this can lead to the string being accepted despite having
|
||||
// one character beyond the right edge of the screen.
|
||||
// In ScummVM we therefore also test the right edge, which leads to differences
|
||||
// with the original engine, but for the better.
|
||||
if (x > 160)
|
||||
return (315 - x - halfLen >= 0);
|
||||
return (x - halfLen >= 0 && x + halfLen + (textLen % 2) * CHAR_WIDTH <= 320);
|
||||
}
|
||||
|
||||
void DrasculaEngine::centerText(const char *message, int textX, int textY) {
|
||||
char msg[200];
|
||||
Common::strlcpy(msg, message, 200);
|
||||
|
||||
// We make sure to have a width of at least 120 pixels by clipping the center.
|
||||
// In theory since the screen width is 320 I would expect something like this:
|
||||
// x = CLIP<int>(x, 60, 260);
|
||||
// return (x - halfLen >= 0 && x + halfLen <= 319);
|
||||
|
||||
// The engines does things differently though. It tries to clips text at 315 instead of 319.
|
||||
// See also the comment in textFitsCentered().
|
||||
|
||||
textX = CLIP<int>(textX, 60, 255);
|
||||
|
||||
// If the message fits on screen as-is, just print it here
|
||||
if (textFitsCentered(msg, textX)) {
|
||||
int x = textX - (strlen(msg) / 2) * CHAR_WIDTH - 1;
|
||||
// The original starts to draw (nbLines + 2) lines above textY, except if there is a single line
|
||||
// in which case it starts drawing at (nbLines + 3) above textY.
|
||||
// Also clip to the screen height although the original does not do it.
|
||||
int y = textY - 4 * CHAR_HEIGHT;
|
||||
y = CLIP<int>(y, 0, 200 - CHAR_HEIGHT);
|
||||
print_abc(msg, x, y);
|
||||
return;
|
||||
}
|
||||
|
||||
// If it's a one-word message it can't be broken up. It's probably a
|
||||
// mouse-over text, so try just sliding it to the side a bit to make it
|
||||
// fit. This happens with the word "TOTENKOPF" in the very first room
|
||||
// with the German translation.
|
||||
if (!strchr(msg, ' ')) {
|
||||
int len = strlen(msg);
|
||||
int x = CLIP<int>(textX - (len / 2) * CHAR_WIDTH - 1, 0, 319 - len * CHAR_WIDTH);
|
||||
int y = textY - 4 * CHAR_HEIGHT;
|
||||
y = CLIP<int>(y, 0, 200 - CHAR_HEIGHT);
|
||||
print_abc(msg, x, y);
|
||||
return;
|
||||
}
|
||||
|
||||
// Message doesn't fit on screen, split it
|
||||
char messageLines[15][41]; // screenWidth/charWidth = 320/8 = 40. Thus lines can have up to 41 characters with the null terminator (despite the original allocating only 40 characters here).
|
||||
int curLine = 0;
|
||||
char messageCurLine[50];
|
||||
char tmpMessageCurLine[50];
|
||||
*messageCurLine = 0;
|
||||
*tmpMessageCurLine = 0;
|
||||
// Get a word from the message
|
||||
char* curWord = strtok(msg, " ");
|
||||
while (curWord != nullptr) {
|
||||
// Check if the word and the current line fit on screen
|
||||
if (tmpMessageCurLine[0] != '\0')
|
||||
Common::strlcat(tmpMessageCurLine, " ", 50);
|
||||
Common::strlcat(tmpMessageCurLine, curWord, 50);
|
||||
if (textFitsCentered(tmpMessageCurLine, textX)) {
|
||||
// Line fits, so add the word to the current message line
|
||||
Common::strcpy_s(messageCurLine, tmpMessageCurLine);
|
||||
} else {
|
||||
// Line does't fit. Store the current line and start a new line.
|
||||
Common::strlcpy(messageLines[curLine++], messageCurLine, 41);
|
||||
Common::strlcpy(messageCurLine, curWord, 50);
|
||||
Common::strlcpy(tmpMessageCurLine, curWord, 50);
|
||||
}
|
||||
|
||||
// Get next word
|
||||
curWord = strtok(nullptr, " ");
|
||||
if (curWord == nullptr) {
|
||||
// The original has an interesting bug that if we split the text on several lines
|
||||
// a space is added at the end (which impacts the alignment, and may even cause the line
|
||||
// to become too long).
|
||||
Common::strlcat(messageCurLine, " ", 50);
|
||||
if (!textFitsCentered(messageCurLine, textX)) {
|
||||
messageCurLine[strlen(messageCurLine) - 1] = '\0';
|
||||
Common::strlcpy(messageLines[curLine++], messageCurLine, 41);
|
||||
Common::strcpy_s(messageLines[curLine++], " ");
|
||||
} else
|
||||
Common::strlcpy(messageLines[curLine++], messageCurLine, 41);
|
||||
}
|
||||
}
|
||||
|
||||
// The original starts to draw (nbLines + 2) lines above textY.
|
||||
// Also clip to the screen height although the original does not do it.
|
||||
int y = textY - (curLine + 2) * CHAR_HEIGHT;
|
||||
y = CLIP<int>(y, 0, 200 - curLine * (CHAR_HEIGHT + 2) + 2);
|
||||
for (int line = 0 ; line < curLine ; ++line, y += CHAR_HEIGHT + 2) {
|
||||
int textHalfLen = (strlen(messageLines[line]) / 2) * CHAR_WIDTH;
|
||||
print_abc(messageLines[line], textX - textHalfLen - 1, y);
|
||||
}
|
||||
}
|
||||
|
||||
void DrasculaEngine::screenSaver() {
|
||||
int xr, yr;
|
||||
byte *copia, *ghost;
|
||||
float coeff = 0, coeff2 = 0;
|
||||
int count = 0;
|
||||
int count2 = 0;
|
||||
int tempLine[320];
|
||||
int tempRow[200];
|
||||
|
||||
hideCursor();
|
||||
|
||||
clearRoom();
|
||||
|
||||
loadPic("sv.alg", bgSurface, HALF_PAL);
|
||||
|
||||
// inicio_ghost();
|
||||
copia = (byte *)malloc(64000);
|
||||
ghost = (byte *)malloc(65536);
|
||||
|
||||
// carga_ghost();
|
||||
Common::SeekableReadStream *stream = _archives.open("ghost.drv");
|
||||
if (!stream)
|
||||
error("Cannot open file ghost.drv");
|
||||
|
||||
stream->read(ghost, 65536);
|
||||
delete stream;
|
||||
|
||||
updateEvents();
|
||||
xr = _mouseX;
|
||||
yr = _mouseY;
|
||||
|
||||
while (!shouldQuit()) {
|
||||
// efecto(bgSurface);
|
||||
|
||||
memcpy(copia, bgSurface, 64000);
|
||||
coeff += 0.1f;
|
||||
coeff2 = coeff;
|
||||
|
||||
if (++count > 319)
|
||||
count = 0;
|
||||
|
||||
for (int i = 0; i < 320; i++) {
|
||||
tempLine[i] = (int)(sin(coeff2) * 16);
|
||||
coeff2 += 0.02f;
|
||||
tempLine[i] = checkWrapY(tempLine[i]);
|
||||
}
|
||||
|
||||
coeff2 = coeff;
|
||||
for (int i = 0; i < 200; i++) {
|
||||
tempRow[i] = (int)(sin(coeff2) * 16);
|
||||
coeff2 += 0.02f;
|
||||
tempRow[i] = checkWrapX(tempRow[i]);
|
||||
}
|
||||
|
||||
if (++count2 > 199)
|
||||
count2 = 0;
|
||||
|
||||
int x1_, y1_, off1, off2;
|
||||
|
||||
Graphics::Surface *screenSurf = _system->lockScreen();
|
||||
byte *screenBuffer = (byte *)screenSurf->getPixels();
|
||||
uint16 screenPitch = screenSurf->pitch;
|
||||
for (int i = 0; i < 200; i++) {
|
||||
for (int j = 0; j < 320; j++) {
|
||||
x1_ = j + tempRow[i];
|
||||
x1_ = checkWrapX(x1_);
|
||||
|
||||
y1_ = i + count2;
|
||||
y1_ = checkWrapY(y1_);
|
||||
|
||||
off1 = 320 * y1_ + x1_;
|
||||
|
||||
x1_ = j + count;
|
||||
x1_ = checkWrapX(x1_);
|
||||
|
||||
y1_ = i + tempLine[j];
|
||||
y1_ = checkWrapY(y1_);
|
||||
off2 = 320 * y1_ + x1_;
|
||||
|
||||
screenBuffer[screenPitch * i + j] = ghost[bgSurface[off2] + (copia[off1] << 8)];
|
||||
}
|
||||
}
|
||||
|
||||
_system->unlockScreen();
|
||||
_system->updateScreen();
|
||||
|
||||
_system->delayMillis(20);
|
||||
|
||||
// end of efecto()
|
||||
|
||||
updateEvents();
|
||||
if (_rightMouseButton == 1 || _leftMouseButton == 1)
|
||||
break;
|
||||
if (_mouseX != xr)
|
||||
break;
|
||||
if (_mouseY != yr)
|
||||
break;
|
||||
}
|
||||
// fin_ghost();
|
||||
free(copia);
|
||||
free(ghost);
|
||||
|
||||
loadPic(_roomNumber, bgSurface, HALF_PAL);
|
||||
showCursor();
|
||||
}
|
||||
|
||||
void DrasculaEngine::playFLI(const char *filefli, int vel) {
|
||||
// Open file
|
||||
globalSpeed = 1000 / vel;
|
||||
FrameSSN = 0;
|
||||
Common::SeekableReadStream *stream = _archives.open(filefli);
|
||||
Common::Keymapper *keymapper = g_system->getEventManager()->getKeymapper();
|
||||
|
||||
if (!stream) {
|
||||
warning("playFLI: Failed to load file '%s'", filefli);
|
||||
return;
|
||||
}
|
||||
LastFrame = _system->getMillis();
|
||||
|
||||
keymapper->getKeymap("game-shortcuts")->setEnabled(false);
|
||||
keymapper->getKeymap("animation")->setEnabled(true);
|
||||
|
||||
while (playFrameSSN(stream) && (!term_int) && !shouldQuit()) {
|
||||
if (getAction() == kActionSkip)
|
||||
term_int = 1;
|
||||
}
|
||||
|
||||
keymapper->getKeymap("animation")->setEnabled(false);
|
||||
keymapper->getKeymap("game-shortcuts")->setEnabled(true);
|
||||
|
||||
delete stream;
|
||||
}
|
||||
|
||||
int DrasculaEngine::playFrameSSN(Common::SeekableReadStream *stream) {
|
||||
int Exit = 0;
|
||||
uint32 length;
|
||||
byte *BufferSSN;
|
||||
|
||||
byte CHUNK = stream->readByte();
|
||||
|
||||
switch (CHUNK) {
|
||||
case kFrameSetPal: {
|
||||
byte dacSSN[768];
|
||||
stream->read(dacSSN, 768);
|
||||
setPalette(dacSSN);
|
||||
break;
|
||||
}
|
||||
case kFrameEmptyFrame:
|
||||
waitFrameSSN();
|
||||
break;
|
||||
case kFrameInit: {
|
||||
byte CMP = stream->readByte();
|
||||
length = stream->readUint32LE();
|
||||
if (CMP == kFrameCmpRle) {
|
||||
BufferSSN = (byte *)malloc(length);
|
||||
stream->read(BufferSSN, length);
|
||||
decodeRLE(BufferSSN, screenSurface);
|
||||
free(BufferSSN);
|
||||
waitFrameSSN();
|
||||
|
||||
if (FrameSSN) {
|
||||
Graphics::Surface *screenSurf = _system->lockScreen();
|
||||
byte *screenBuffer = (byte *)screenSurf->getPixels();
|
||||
uint16 screenPitch = screenSurf->pitch;
|
||||
mixVideo(screenBuffer, screenSurface, screenPitch);
|
||||
_system->unlockScreen();
|
||||
} else
|
||||
_system->copyRectToScreen(screenSurface, 320, 0, 0, 320, 200);
|
||||
|
||||
_system->updateScreen();
|
||||
FrameSSN++;
|
||||
} else {
|
||||
if (CMP == kFrameCmpOff) {
|
||||
BufferSSN = (byte *)malloc(length);
|
||||
stream->read(BufferSSN, length);
|
||||
decodeOffset(BufferSSN, screenSurface, length);
|
||||
free(BufferSSN);
|
||||
waitFrameSSN();
|
||||
if (FrameSSN) {
|
||||
Graphics::Surface *screenSurf = _system->lockScreen();
|
||||
byte *screenBuffer = (byte *)screenSurf->getPixels();
|
||||
uint16 screenPitch = screenSurf->pitch;
|
||||
mixVideo(screenBuffer, screenSurface, screenPitch);
|
||||
_system->unlockScreen();
|
||||
} else
|
||||
_system->copyRectToScreen(screenSurface, 320, 0, 0, 320, 200);
|
||||
|
||||
_system->updateScreen();
|
||||
FrameSSN++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kFrameEndAnim:
|
||||
Exit = 1;
|
||||
break;
|
||||
default:
|
||||
Exit = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
return (!Exit);
|
||||
}
|
||||
|
||||
void DrasculaEngine::decodeOffset(byte *BufferOFF, byte *MiVideoOFF, int length) {
|
||||
int x = 0;
|
||||
int size;
|
||||
int offset;
|
||||
|
||||
memset(screenSurface, 0, 64000);
|
||||
while (x < length) {
|
||||
offset = BufferOFF[x] + BufferOFF[x + 1] * 256;
|
||||
// FIXME: this writes beyond 64000, so the buffer has been initialized
|
||||
// to 64256 bytes (like the original did)
|
||||
size = BufferOFF[x + 2];
|
||||
memcpy(MiVideoOFF + offset, &BufferOFF[x + 3], size);
|
||||
x += 3 + size;
|
||||
}
|
||||
}
|
||||
|
||||
void DrasculaEngine::decodeRLE(byte* srcPtr, byte* dstPtr, uint16 pitch) {
|
||||
bool stopProcessing = false;
|
||||
byte pixel;
|
||||
uint repeat;
|
||||
int curByte = 0, curLine = 0;
|
||||
pitch -= 320;
|
||||
|
||||
while (!stopProcessing) {
|
||||
pixel = *srcPtr++;
|
||||
repeat = 1;
|
||||
if ((pixel & 192) == 192) {
|
||||
repeat = (pixel & 63);
|
||||
pixel = *srcPtr++;
|
||||
}
|
||||
for (uint j = 0; j < repeat; j++) {
|
||||
*dstPtr++ = pixel;
|
||||
if (++curByte >= 320) {
|
||||
curByte = 0;
|
||||
dstPtr += pitch;
|
||||
if (++curLine >= 200) {
|
||||
stopProcessing = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DrasculaEngine::mixVideo(byte *OldScreen, byte *NewScreen, uint16 oldPitch) {
|
||||
for (int y = 0; y < 200; y++) {
|
||||
for (int x = 0; x < 320; x++)
|
||||
OldScreen[x] ^= NewScreen[x];
|
||||
OldScreen += oldPitch;
|
||||
NewScreen += 320;
|
||||
}
|
||||
}
|
||||
|
||||
void DrasculaEngine::waitFrameSSN() {
|
||||
uint32 now;
|
||||
while ((now = _system->getMillis()) - LastFrame < ((uint32) globalSpeed))
|
||||
_system->delayMillis(globalSpeed - (now - LastFrame));
|
||||
LastFrame = LastFrame + globalSpeed;
|
||||
}
|
||||
|
||||
bool DrasculaEngine::animate(const char *animationFile, int FPS) {
|
||||
int NFrames;
|
||||
int cnt = 2;
|
||||
|
||||
Common::SeekableReadStream *stream = _archives.open(animationFile);
|
||||
|
||||
Common::Keymapper *keymapper = g_system->getEventManager()->getKeymapper();
|
||||
|
||||
if (!stream) {
|
||||
warning("Animation file %s not found", animationFile);
|
||||
return true;
|
||||
}
|
||||
|
||||
NFrames = stream->readSint32LE();
|
||||
showFrame(stream, true);
|
||||
_system->delayMillis(1000 / FPS);
|
||||
|
||||
keymapper->getKeymap("game-shortcuts")->setEnabled(false);
|
||||
keymapper->getKeymap("animation")->setEnabled(true);
|
||||
|
||||
while (cnt < NFrames) {
|
||||
showFrame(stream);
|
||||
_system->delayMillis(1000 / FPS);
|
||||
cnt++;
|
||||
byte key = getScan();
|
||||
Common::CustomEventType action = getAction();
|
||||
if (action == kActionSkip)
|
||||
term_int = 1;
|
||||
if (key != 0 || action != kActionNone)
|
||||
break;
|
||||
}
|
||||
delete stream;
|
||||
|
||||
keymapper->getKeymap("animation")->setEnabled(false);
|
||||
keymapper->getKeymap("game-shortcuts")->setEnabled(true);
|
||||
|
||||
return ((term_int == 1) || (getAction() == kActionSkip) || shouldQuit());
|
||||
}
|
||||
|
||||
} // End of namespace Drascula
|
||||
314
engines/drascula/interface.cpp
Normal file
314
engines/drascula/interface.cpp
Normal file
@@ -0,0 +1,314 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "drascula/drascula.h"
|
||||
#include "graphics/cursorman.h"
|
||||
|
||||
#include "common/text-to-speech.h"
|
||||
|
||||
#include "backends/keymapper/keymapper.h"
|
||||
|
||||
// The verbs are represented in-game as a picture, thus we are
|
||||
// adding transcriptions here
|
||||
// While only English, Spanish, Italian, and Russian are translated in-game,
|
||||
// they are translated for the TTS system here
|
||||
static const char *verbNamesEnglish[] = {
|
||||
"Walk",
|
||||
"Look",
|
||||
"Take",
|
||||
"Open",
|
||||
"Close",
|
||||
"Talk",
|
||||
"Push"
|
||||
};
|
||||
|
||||
static const char *verbNamesSpanish[] = {
|
||||
"Ir a",
|
||||
"Mirar",
|
||||
"Coger",
|
||||
"Abrir",
|
||||
"Cerrar",
|
||||
"Hablar",
|
||||
"Mover"
|
||||
};
|
||||
|
||||
static const char *verbNamesItalian[] = {
|
||||
"Vai",
|
||||
"Guarda",
|
||||
"Prendi",
|
||||
"Apri",
|
||||
"Chiudi",
|
||||
"Parla",
|
||||
"Premi"
|
||||
};
|
||||
|
||||
static const char *verbNamesFrench[] = {
|
||||
"Marcher",
|
||||
"Regarder",
|
||||
"Ramasser",
|
||||
"Ouvrir",
|
||||
"Fermer",
|
||||
"Parler",
|
||||
"Pousser"
|
||||
};
|
||||
|
||||
static const char *verbNamesGerman[] = {
|
||||
"Gehe",
|
||||
"Schau",
|
||||
"Nimm",
|
||||
"\231ffne",
|
||||
"Schlie\341e",
|
||||
"Rede",
|
||||
"Dr\201cke"
|
||||
};
|
||||
|
||||
static const char *verbNamesRussian[] = {
|
||||
"\xc8\xe4\xf2\xe8", // "Идти"
|
||||
"\xd1\xec\xee\xf2\xf0\xe5\xf2\xfc", // "Смотреть"
|
||||
"\xc2\xe7\xff\xf2\xfc", // "Взять"
|
||||
"\xce\xf2\xea\xf0\xfb\xf2\xfc", // "Открыть"
|
||||
"\xc7\xe0\xea\xf0\xfb\xf2\xfc", // "Закрыть"
|
||||
"\xc3\xee\xe2\xee\xf0\xe8\xf2\xfc", // "Говорить"
|
||||
"\xd2\xee\xeb\xea\xe0\xf2\xfc" // "Толкать"
|
||||
};
|
||||
|
||||
static const int kConfirmExit = 1;
|
||||
|
||||
namespace Drascula {
|
||||
|
||||
void DrasculaEngine::setCursor(int cursor) {
|
||||
switch (cursor) {
|
||||
case kCursorCrosshair:
|
||||
CursorMan.replaceCursor(crosshairCursor, 40, 25, 20, 17, 255);
|
||||
break;
|
||||
case kCursorCurrentItem:
|
||||
CursorMan.replaceCursor(mouseCursor, OBJWIDTH, OBJHEIGHT, 20, 17, 255);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DrasculaEngine::showCursor() {
|
||||
CursorMan.showMouse(true);
|
||||
}
|
||||
|
||||
void DrasculaEngine::hideCursor() {
|
||||
CursorMan.showMouse(false);
|
||||
}
|
||||
|
||||
bool DrasculaEngine::isCursorVisible() {
|
||||
return CursorMan.isVisible();
|
||||
}
|
||||
|
||||
void DrasculaEngine::selectVerbFromBar() {
|
||||
for (int n = 0; n < 7; n++) {
|
||||
if (_mouseX > _verbBarX[n] && _mouseX < _verbBarX[n + 1] && n > 0) {
|
||||
selectVerb(n);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// no verb selected
|
||||
selectVerb(kVerbNone);
|
||||
}
|
||||
|
||||
void DrasculaEngine::selectVerb(int verb) {
|
||||
debug(4, "selectVerb(%d)", verb);
|
||||
|
||||
int c = _menuScreen ? 0 : 171;
|
||||
|
||||
if (currentChapter == 5) {
|
||||
if (takeObject == 1 && pickedObject != 16)
|
||||
addObject(pickedObject);
|
||||
} else {
|
||||
if (takeObject == 1)
|
||||
addObject(pickedObject);
|
||||
}
|
||||
|
||||
for (int i = 0; i < OBJHEIGHT; i++)
|
||||
memcpy(mouseCursor + i * OBJWIDTH, cursorSurface + OBJWIDTH * verb + (c + i) * 320, OBJWIDTH);
|
||||
setCursor(kCursorCurrentItem);
|
||||
|
||||
if (verb > 0) {
|
||||
takeObject = 1;
|
||||
pickedObject = verb;
|
||||
|
||||
const char **verbNames;
|
||||
|
||||
switch (_lang) {
|
||||
case kEnglish:
|
||||
verbNames = verbNamesEnglish;
|
||||
break;
|
||||
case kSpanish:
|
||||
verbNames = verbNamesSpanish;
|
||||
break;
|
||||
case kGerman:
|
||||
verbNames = verbNamesGerman;
|
||||
break;
|
||||
case kFrench:
|
||||
verbNames = verbNamesFrench;
|
||||
break;
|
||||
case kItalian:
|
||||
verbNames = verbNamesItalian;
|
||||
break;
|
||||
case kRussian:
|
||||
verbNames = verbNamesRussian;
|
||||
break;
|
||||
default:
|
||||
verbNames = verbNamesEnglish;
|
||||
}
|
||||
|
||||
sayText(verbNames[verb], Common::TextToSpeechManager::INTERRUPT);
|
||||
} else {
|
||||
takeObject = 0;
|
||||
_hasName = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool DrasculaEngine::confirmExit() {
|
||||
byte key = 0;
|
||||
Common::CustomEventType action = kActionNone;
|
||||
Common::Keymapper *keymapper = g_system->getEventManager()->getKeymapper();
|
||||
|
||||
color_abc(kColorRed);
|
||||
updateRoom();
|
||||
centerText(_textsys[1], 160, 87);
|
||||
updateScreen();
|
||||
|
||||
sayText(_textsys[kConfirmExit], Common::TextToSpeechManager::INTERRUPT);
|
||||
|
||||
keymapper->getKeymap("game-shortcuts")->setEnabled(false);
|
||||
keymapper->getKeymap("quit-dialog")->setEnabled(true);
|
||||
|
||||
delay(100);
|
||||
while (!shouldQuit()) {
|
||||
key = getScan();
|
||||
action = getAction();
|
||||
if (key != 0 || action != kActionNone)
|
||||
break;
|
||||
|
||||
// This gives a better feedback to the user when he is asked to
|
||||
// confirm whether he wants to quit. It now still updates the room and
|
||||
// shows mouse cursor movement. Hopefully it will work in all
|
||||
// locations of the game.
|
||||
updateRoom();
|
||||
color_abc(kColorRed);
|
||||
centerText(_textsys[1], 160, 87);
|
||||
updateScreen();
|
||||
}
|
||||
|
||||
keymapper->getKeymap("quit-dialog")->setEnabled(false);
|
||||
keymapper->getKeymap("game-shortcuts")->setEnabled(true);
|
||||
|
||||
if (action == kActionConfirmQuit || shouldQuit()) {
|
||||
stopMusic();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DrasculaEngine::showMenu() {
|
||||
int h, n, x;
|
||||
byte *srcSurface = (currentChapter == 6) ? tableSurface : frontSurface;
|
||||
x = whichObject();
|
||||
|
||||
for (n = 1; n < ARRAYSIZE(inventoryObjects); n++) {
|
||||
h = inventoryObjects[n];
|
||||
|
||||
if (h != 0) {
|
||||
copyBackground(_polX[n], _polY[n], _itemLocations[n].x, _itemLocations[n].y,
|
||||
OBJWIDTH, OBJHEIGHT, srcSurface, screenSurface);
|
||||
}
|
||||
copyRect(_x1d_menu[h], _y1d_menu[h], _itemLocations[n].x, _itemLocations[n].y,
|
||||
OBJWIDTH, OBJHEIGHT, cursorSurface, screenSurface);
|
||||
}
|
||||
|
||||
if (x < 7) {
|
||||
sayText(iconName[x], Common::TextToSpeechManager::INTERRUPT);
|
||||
|
||||
print_abc(iconName[x], _itemLocations[x].x - 2, _itemLocations[x].y - 7);
|
||||
}
|
||||
}
|
||||
|
||||
void DrasculaEngine::clearMenu() {
|
||||
int n, verbActivated = 1;
|
||||
|
||||
for (n = 0; n < 7; n++) {
|
||||
if (_mouseX > _verbBarX[n] && _mouseX < _verbBarX[n + 1]) {
|
||||
verbActivated = 0;
|
||||
|
||||
const char **verbNames;
|
||||
|
||||
switch (_lang) {
|
||||
case kEnglish:
|
||||
verbNames = verbNamesEnglish;
|
||||
break;
|
||||
case kSpanish:
|
||||
verbNames = verbNamesSpanish;
|
||||
break;
|
||||
case kGerman:
|
||||
verbNames = verbNamesGerman;
|
||||
break;
|
||||
case kFrench:
|
||||
verbNames = verbNamesFrench;
|
||||
break;
|
||||
case kItalian:
|
||||
verbNames = verbNamesItalian;
|
||||
break;
|
||||
case kRussian:
|
||||
verbNames = verbNamesRussian;
|
||||
break;
|
||||
default:
|
||||
verbNames = verbNamesEnglish;
|
||||
}
|
||||
|
||||
sayText(verbNames[n], Common::TextToSpeechManager::INTERRUPT);
|
||||
}
|
||||
|
||||
copyRect(OBJWIDTH * n, OBJHEIGHT * verbActivated, _verbBarX[n], 2,
|
||||
OBJWIDTH, OBJHEIGHT, cursorSurface, screenSurface);
|
||||
verbActivated = 1;
|
||||
}
|
||||
}
|
||||
|
||||
bool DrasculaEngine::checkMenuFlags() {
|
||||
int n = whichObject();
|
||||
if (n != 0) {
|
||||
if (inventoryObjects[n] != 0 && checkAction(inventoryObjects[n]))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void DrasculaEngine::showMap() {
|
||||
_hasName = false;
|
||||
|
||||
for (int l = 0; l < numRoomObjs; l++) {
|
||||
if (_objectRect[l].contains(Common::Point(_mouseX, _mouseY)) && visible[l] == 1) {
|
||||
Common::strcpy_s(textName, objName[l]);
|
||||
_hasName = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Drascula
|
||||
347
engines/drascula/metaengine.cpp
Normal file
347
engines/drascula/metaengine.cpp
Normal file
@@ -0,0 +1,347 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "engines/savestate.h"
|
||||
#include "engines/advancedDetector.h"
|
||||
|
||||
#include "common/translation.h"
|
||||
|
||||
#include "graphics/thumbnail.h"
|
||||
|
||||
#include "drascula/drascula.h"
|
||||
#include "drascula/detection.h"
|
||||
|
||||
#include "backends/keymapper/action.h"
|
||||
#include "backends/keymapper/keymapper.h"
|
||||
#include "backends/keymapper/standard-actions.h"
|
||||
|
||||
namespace Drascula {
|
||||
|
||||
static const ADExtraGuiOptionsMap optionsList[] = {
|
||||
{
|
||||
GAMEOPTION_ORIGINAL_SAVELOAD,
|
||||
{
|
||||
_s("Use original save/load screens"),
|
||||
_s("Use the original save/load screens instead of the ScummVM ones"),
|
||||
"originalsaveload",
|
||||
false,
|
||||
0,
|
||||
0
|
||||
}
|
||||
},
|
||||
|
||||
#ifdef USE_TTS
|
||||
{
|
||||
GAMEOPTION_TTS,
|
||||
{
|
||||
_s("Enable Text to Speech"),
|
||||
_s("Use TTS to read text in the game (if TTS is available)"),
|
||||
"tts_enabled",
|
||||
false,
|
||||
0,
|
||||
0
|
||||
}
|
||||
},
|
||||
#endif
|
||||
|
||||
AD_EXTRA_GUI_OPTIONS_TERMINATOR
|
||||
};
|
||||
|
||||
uint32 DrasculaEngine::getFeatures() const {
|
||||
return _gameDescription->desc.flags;
|
||||
}
|
||||
|
||||
Common::Language DrasculaEngine::getLanguage() const {
|
||||
return _gameDescription->desc.language;
|
||||
}
|
||||
|
||||
void DrasculaEngine::loadArchives() {
|
||||
const ADGameFileDescription *ag;
|
||||
|
||||
if (getFeatures() & GF_PACKED) {
|
||||
for (ag = _gameDescription->desc.filesDescriptions; ag->fileName; ag++) {
|
||||
if (!_archives.hasArchive(ag->fileName))
|
||||
_archives.registerArchive(ag->fileName, ag->fileType);
|
||||
}
|
||||
}
|
||||
|
||||
_archives.enableFallback(true);
|
||||
}
|
||||
|
||||
} // End of namespace Drascula
|
||||
|
||||
|
||||
namespace Drascula {
|
||||
|
||||
SaveStateDescriptor loadMetaData(Common::ReadStream *s, int slot, bool setPlayTime);
|
||||
|
||||
class DrasculaMetaEngine : public AdvancedMetaEngine<Drascula::DrasculaGameDescription> {
|
||||
public:
|
||||
const char *getName() const override {
|
||||
return "drascula";
|
||||
}
|
||||
|
||||
const ADExtraGuiOptionsMap *getAdvancedExtraGuiOptions() const override {
|
||||
return Drascula::optionsList;
|
||||
}
|
||||
|
||||
Common::Error createInstance(OSystem *syst, Engine **engine, const Drascula::DrasculaGameDescription *gd) const override;
|
||||
bool hasFeature(MetaEngineFeature f) const override;
|
||||
|
||||
SaveStateList listSaves(const char *target) const override;
|
||||
int getMaximumSaveSlot() const override;
|
||||
bool removeSaveState(const char *target, int slot) const override;
|
||||
SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const override;
|
||||
Common::KeymapArray initKeymaps(const char *target) const override;
|
||||
};
|
||||
|
||||
bool DrasculaMetaEngine::hasFeature(MetaEngineFeature f) const {
|
||||
return
|
||||
(f == kSupportsListSaves) ||
|
||||
(f == kSupportsLoadingDuringStartup) ||
|
||||
(f == kSupportsDeleteSave) ||
|
||||
(f == kSavesSupportMetaInfo) ||
|
||||
(f == kSavesSupportThumbnail) ||
|
||||
(f == kSavesSupportCreationDate) ||
|
||||
(f == kSavesSupportPlayTime) ||
|
||||
(f == kSimpleSavesNames);
|
||||
}
|
||||
|
||||
SaveStateList DrasculaMetaEngine::listSaves(const char *target) const {
|
||||
Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
|
||||
Common::String pattern = target;
|
||||
pattern += ".###";
|
||||
|
||||
Common::StringArray filenames = saveFileMan->listSavefiles(pattern);
|
||||
SaveStateList saveList;
|
||||
int slotNum = 0;
|
||||
|
||||
for (const auto &filename : filenames) {
|
||||
// Obtain the last 3 digits of the filename, since they correspond to the save slot
|
||||
slotNum = atoi(filename.c_str() + filename.size() - 3);
|
||||
|
||||
if (slotNum >= 0 && slotNum <= getMaximumSaveSlot()) {
|
||||
Common::InSaveFile *in = saveFileMan->openForLoading(filename);
|
||||
if (in) {
|
||||
SaveStateDescriptor desc = loadMetaData(in, slotNum, false);
|
||||
if (desc.getSaveSlot() != slotNum) {
|
||||
// invalid
|
||||
delete in;
|
||||
continue;
|
||||
}
|
||||
saveList.push_back(desc);
|
||||
delete in;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort saves based on slot number.
|
||||
Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
|
||||
return saveList;
|
||||
}
|
||||
|
||||
SaveStateDescriptor DrasculaMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
|
||||
Common::String fileName = Common::String::format("%s.%03d", target, slot);
|
||||
|
||||
Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(fileName);
|
||||
|
||||
SaveStateDescriptor desc(this, slot, Common::U32String());
|
||||
if (in) {
|
||||
desc = Drascula::loadMetaData(in, slot, false);
|
||||
if (desc.getSaveSlot() != slot) {
|
||||
delete in;
|
||||
return SaveStateDescriptor();
|
||||
}
|
||||
|
||||
Graphics::Surface *thumbnail;
|
||||
if (!Graphics::loadThumbnail(*in, thumbnail)) {
|
||||
delete in;
|
||||
return SaveStateDescriptor();
|
||||
}
|
||||
desc.setThumbnail(thumbnail);
|
||||
|
||||
delete in;
|
||||
}
|
||||
|
||||
return desc;
|
||||
}
|
||||
|
||||
int DrasculaMetaEngine::getMaximumSaveSlot() const { return 999; }
|
||||
|
||||
bool DrasculaMetaEngine::removeSaveState(const char *target, int slot) const {
|
||||
Common::String fileName = Common::String::format("%s.%03d", target, slot);
|
||||
return g_system->getSavefileManager()->removeSavefile(fileName);
|
||||
}
|
||||
|
||||
Common::Error DrasculaMetaEngine::createInstance(OSystem *syst, Engine **engine, const Drascula::DrasculaGameDescription *desc) const {
|
||||
*engine = new Drascula::DrasculaEngine(syst,desc);
|
||||
return Common::kNoError;
|
||||
}
|
||||
|
||||
Common::KeymapArray DrasculaMetaEngine::initKeymaps(const char *target) const {
|
||||
using namespace Common;
|
||||
using namespace Drascula;
|
||||
|
||||
bool originalSaveLoad = ConfMan.getBool("originalsaveload", target);
|
||||
|
||||
Keymap *engineKeyMap = new Keymap(Keymap::kKeymapTypeGame, "drascula-default", _("Default keymappings"));
|
||||
Keymap *gameKeyMap = new Keymap(Keymap::kKeymapTypeGame, "game-shortcuts", _("Game keymappings"));
|
||||
Keymap *animationKeyMap = new Keymap(Keymap::kKeymapTypeGame, "animation", _("Animation keymappings"));
|
||||
Keymap *quitDialogKeyMap = new Keymap(Keymap::kKeymapTypeGame, "quit-dialog", _("Quit dialog keymappings"));
|
||||
|
||||
Action *act;
|
||||
|
||||
act = new Action(kStandardActionLeftClick, _("Move / Interact / Select"));
|
||||
act->setLeftClickEvent();
|
||||
act->addDefaultInputMapping("MOUSE_LEFT");
|
||||
act->addDefaultInputMapping("JOY_A");
|
||||
engineKeyMap->addAction(act);
|
||||
|
||||
act = new Action(kStandardActionRightClick, _("Inventory"));
|
||||
act->setRightClickEvent();
|
||||
act->addDefaultInputMapping("MOUSE_RIGHT");
|
||||
act->addDefaultInputMapping("JOY_B");
|
||||
engineKeyMap->addAction(act);
|
||||
|
||||
act = new Action("SKIP", _("Skip"));
|
||||
act->setCustomEngineActionEvent(kActionSkip);
|
||||
act->addDefaultInputMapping("ESCAPE");
|
||||
act->addDefaultInputMapping("JOY_X");
|
||||
animationKeyMap->addAction(act);
|
||||
|
||||
act = new Action("LOOK", _("Look"));
|
||||
act->setCustomEngineActionEvent(kActionLook);
|
||||
act->addDefaultInputMapping("F1");
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
act = new Action("PICK", _("Pick"));
|
||||
act->setCustomEngineActionEvent(kActionPick);
|
||||
act->addDefaultInputMapping("F2");
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
act = new Action("OPEN", _("Open"));
|
||||
act->setCustomEngineActionEvent(kActionOpen);
|
||||
act->addDefaultInputMapping("F3");
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
act = new Action("CLOSE", _("Close"));
|
||||
act->setCustomEngineActionEvent(kActionClose);
|
||||
act->addDefaultInputMapping("F4");
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
act = new Action("TALK", _("Talk"));
|
||||
act->setCustomEngineActionEvent(kActionTalk);
|
||||
act->addDefaultInputMapping("F5");
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
act = new Action("MOVE", _("Move"));
|
||||
act->setCustomEngineActionEvent(kActionMove);
|
||||
act->addDefaultInputMapping("F6");
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
act = new Action("LOAD", _("Load game"));
|
||||
act->setCustomEngineActionEvent(kActionLoadGame);
|
||||
act->addDefaultInputMapping("F7");
|
||||
act->addDefaultInputMapping("JOY_LEFT");
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
act = new Action("RESETVERBS", _("Reset selected verb"));
|
||||
act->setCustomEngineActionEvent(kActionVerbReset);
|
||||
act->addDefaultInputMapping("F8");
|
||||
act->addDefaultInputMapping("JOY_LEFT_TRIGGER");
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
act = new Action("VOLCONTROLS", _("Volume controls"));
|
||||
act->setCustomEngineActionEvent(kActionVolumeControls);
|
||||
act->addDefaultInputMapping("F9");
|
||||
act->addDefaultInputMapping("JOY_X");
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
if (originalSaveLoad) {
|
||||
act = new Action("SAVELOAD", _("Save / load game"));
|
||||
act->setCustomEngineActionEvent(kActionSaveGame);
|
||||
act->addDefaultInputMapping("F10");
|
||||
act->addDefaultInputMapping("JOY_RIGHT");
|
||||
gameKeyMap->addAction(act);
|
||||
} else {
|
||||
act = new Action("SAVE", _("Save game"));
|
||||
act->setCustomEngineActionEvent(kActionSaveGame);
|
||||
act->addDefaultInputMapping("F10");
|
||||
act->addDefaultInputMapping("JOY_RIGHT");
|
||||
gameKeyMap->addAction(act);
|
||||
}
|
||||
|
||||
act = new Action("SUBTITLESENABLE", _("Enable subtitles"));
|
||||
act->setCustomEngineActionEvent(kActionSubtitlesEnable);
|
||||
act->addDefaultInputMapping("v");
|
||||
act->addDefaultInputMapping("JOY_UP");
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
act = new Action("SUBTITLESDISABLE", _("Disable subtitles"));
|
||||
act->setCustomEngineActionEvent(kActionSubtitlesDisable);
|
||||
act->addDefaultInputMapping("t");
|
||||
act->addDefaultInputMapping("JOY_DOWN");
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
act = new Action("QUIT", _("Quit"));
|
||||
act->setCustomEngineActionEvent(kActionQuit);
|
||||
act->addDefaultInputMapping("ESCAPE");
|
||||
act->addDefaultInputMapping("JOY_RIGHT_TRIGGER");
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
act = new Action(kStandardActionEE, _("???"));
|
||||
act->setCustomEngineActionEvent(kActionEasterEgg);
|
||||
act->addDefaultInputMapping("0");
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
act = new Action("PAUSESPEECH", _("Pause speech"));
|
||||
act->setCustomEngineActionEvent(kActionPauseSpeech);
|
||||
act->addDefaultInputMapping("SPACE");
|
||||
act->addDefaultInputMapping("PAUSE");
|
||||
act->addDefaultInputMapping("JOY_Y");
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
act = new Action("QUITCONFIRM", _("Confirm quit"));
|
||||
act->setCustomEngineActionEvent(kActionConfirmQuit);
|
||||
act->addDefaultInputMapping("ESCAPE");
|
||||
act->addDefaultInputMapping("JOY_RIGHT_TRIGGER");
|
||||
quitDialogKeyMap->addAction(act);
|
||||
|
||||
KeymapArray keymaps(4);
|
||||
|
||||
keymaps[0] = engineKeyMap;
|
||||
keymaps[1] = gameKeyMap;
|
||||
keymaps[2] = animationKeyMap;
|
||||
keymaps[3] = quitDialogKeyMap;
|
||||
|
||||
animationKeyMap->setEnabled(false);
|
||||
quitDialogKeyMap->setEnabled(false);
|
||||
|
||||
return keymaps;
|
||||
}
|
||||
|
||||
} // End of namespace Drascula
|
||||
|
||||
#if PLUGIN_ENABLED_DYNAMIC(DRASCULA)
|
||||
REGISTER_PLUGIN_DYNAMIC(DRASCULA, PLUGIN_TYPE_ENGINE, Drascula::DrasculaMetaEngine);
|
||||
#else
|
||||
REGISTER_PLUGIN_STATIC(DRASCULA, PLUGIN_TYPE_ENGINE, Drascula::DrasculaMetaEngine);
|
||||
#endif
|
||||
30
engines/drascula/module.mk
Normal file
30
engines/drascula/module.mk
Normal file
@@ -0,0 +1,30 @@
|
||||
MODULE := engines/drascula
|
||||
|
||||
MODULE_OBJS := \
|
||||
actors.o \
|
||||
animation.o \
|
||||
console.o \
|
||||
converse.o \
|
||||
drascula.o \
|
||||
graphics.o \
|
||||
interface.o \
|
||||
metaengine.o \
|
||||
objects.o \
|
||||
palette.o \
|
||||
resource.o \
|
||||
rooms.o \
|
||||
saveload.o \
|
||||
sound.o \
|
||||
talk.o
|
||||
|
||||
|
||||
# This module can be built as a plugin
|
||||
ifeq ($(ENABLE_DRASCULA), DYNAMIC_PLUGIN)
|
||||
PLUGIN := 1
|
||||
endif
|
||||
|
||||
# Include common rules
|
||||
include $(srcdir)/rules.mk
|
||||
|
||||
# Detection objects
|
||||
DETECT_OBJS += $(MODULE)/detection.o
|
||||
306
engines/drascula/objects.cpp
Normal file
306
engines/drascula/objects.cpp
Normal file
@@ -0,0 +1,306 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/textconsole.h"
|
||||
|
||||
#include "drascula/drascula.h"
|
||||
|
||||
namespace Drascula {
|
||||
|
||||
void DrasculaEngine::pickObject(int object) {
|
||||
if (currentChapter == 6)
|
||||
loadPic("iconsp.alg", backSurface);
|
||||
else if (currentChapter == 4)
|
||||
loadPic("icons2.alg", backSurface);
|
||||
else if (currentChapter == 5)
|
||||
loadPic("icons3.alg", backSurface);
|
||||
else
|
||||
loadPic("icons.alg", backSurface);
|
||||
chooseObject(object);
|
||||
if (currentChapter == 2)
|
||||
loadPic(menuBackground, backSurface);
|
||||
else
|
||||
loadPic(99, backSurface);
|
||||
}
|
||||
|
||||
void DrasculaEngine::chooseObject(int object) {
|
||||
if (currentChapter == 5) {
|
||||
if (takeObject == 1 && !_menuScreen && pickedObject != 16)
|
||||
addObject(pickedObject);
|
||||
} else {
|
||||
if (takeObject == 1 && !_menuScreen)
|
||||
addObject(pickedObject);
|
||||
}
|
||||
for (int i = 0; i < OBJHEIGHT; i++)
|
||||
memcpy(mouseCursor + i * OBJWIDTH, backSurface + _x1d_menu[object] + (_y1d_menu[object] + i) * 320, OBJWIDTH);
|
||||
setCursor(kCursorCurrentItem);
|
||||
takeObject = 1;
|
||||
pickedObject = object;
|
||||
}
|
||||
|
||||
void DrasculaEngine::walkToPoint(Common::Point pos) {
|
||||
bool cursorVisible = isCursorVisible();
|
||||
hideCursor();
|
||||
|
||||
if (currentChapter == 5 || currentChapter == 6) {
|
||||
if (!_characterVisible) {
|
||||
curX = roomX;
|
||||
curY = roomY;
|
||||
updateRoom();
|
||||
updateScreen();
|
||||
return;
|
||||
}
|
||||
}
|
||||
roomX = pos.x;
|
||||
roomY = pos.y;
|
||||
startWalking();
|
||||
|
||||
while (!shouldQuit()) {
|
||||
updateRoom();
|
||||
updateScreen();
|
||||
updateEvents();
|
||||
if (!_characterMoved)
|
||||
break;
|
||||
|
||||
pause(3);
|
||||
}
|
||||
|
||||
if (_walkToObject) {
|
||||
_walkToObject = false;
|
||||
trackProtagonist = trackFinal;
|
||||
}
|
||||
updateRoom();
|
||||
updateScreen();
|
||||
|
||||
// _roomNumber -2 is end credits. Do not show cursor there
|
||||
if (cursorVisible && _roomNumber != -2)
|
||||
showCursor();
|
||||
}
|
||||
|
||||
void DrasculaEngine::checkObjects() {
|
||||
int l;
|
||||
|
||||
_hasName = false;
|
||||
|
||||
for (l = 0; l < numRoomObjs; l++) {
|
||||
if (_objectRect[l].contains(Common::Point(_mouseX, _mouseY)) && visible[l] == 1 && isDoor[l] == 0) {
|
||||
Common::strcpy_s(textName, objName[l]);
|
||||
_hasName = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (_mouseX > curX + 2 && _mouseY > curY + 2
|
||||
&& _mouseX < curX + curWidth - 2 && _mouseY < curY + curHeight - 2) {
|
||||
if (currentChapter == 2 || !_hasName) {
|
||||
Common::strcpy_s(textName, _textmisc[3]); // "hacker"
|
||||
_hasName = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an item from the inventory, namely the item in the slot
|
||||
* over which the mouse cursor currently hovers.
|
||||
*/
|
||||
void DrasculaEngine::removeObject() {
|
||||
int obj = 0, n;
|
||||
|
||||
updateRoom();
|
||||
|
||||
n = whichObject();
|
||||
if (n != 0) {
|
||||
obj = inventoryObjects[n];
|
||||
inventoryObjects[n] = 0;
|
||||
if (obj != 0)
|
||||
takeObject = 1;
|
||||
}
|
||||
|
||||
updateEvents();
|
||||
|
||||
if (takeObject == 1)
|
||||
chooseObject(obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the item with the given id from the inventory.
|
||||
* Returns 0 if was in the inventory, 1 otherwise.
|
||||
*/
|
||||
int DrasculaEngine::removeObject(int obj) {
|
||||
for (int n = 1; n < ARRAYSIZE(inventoryObjects); n++) {
|
||||
if (inventoryObjects[n] == obj) {
|
||||
inventoryObjects[n] = 0;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
bool DrasculaEngine::pickupObject() {
|
||||
int obj = pickedObject;
|
||||
checkFlags = 1;
|
||||
|
||||
updateRoom();
|
||||
showMenu();
|
||||
updateScreen();
|
||||
|
||||
// Objects with an ID smaller than 7 are the inventory verbs
|
||||
if (pickedObject >= 7) {
|
||||
int n = whichObject();
|
||||
if (n != 0 && inventoryObjects[n] == 0) {
|
||||
inventoryObjects[n] = obj;
|
||||
takeObject = 0;
|
||||
checkFlags = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (checkFlags == 1) {
|
||||
if (checkMenuFlags())
|
||||
return true;
|
||||
}
|
||||
updateEvents();
|
||||
if (takeObject == 0)
|
||||
selectVerb(kVerbNone);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the object with the given id to the inventory.
|
||||
*/
|
||||
void DrasculaEngine::addObject(int obj) {
|
||||
int n;
|
||||
|
||||
// Check whether obj is already in the inventory.
|
||||
// If it is, just do nothing and return.
|
||||
for (n = 1; n < ARRAYSIZE(inventoryObjects); n++) {
|
||||
if (inventoryObjects[n] == obj)
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, look for a free slot and add the object there.
|
||||
for (n = 1; n < ARRAYSIZE(inventoryObjects); n++) {
|
||||
if (inventoryObjects[n] == 0) {
|
||||
inventoryObjects[n] = obj;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If we get here, then we failed to add the object to the inventory.
|
||||
error("DrasculaEngine::addObject: Failed to add object %d to inventory", obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the id of the inventory slot under the mouse cursor right now.
|
||||
* If no inventory slot is under the mouse cursor, return 0.
|
||||
*/
|
||||
int DrasculaEngine::whichObject() {
|
||||
int n;
|
||||
|
||||
for (n = 1; n < ARRAYSIZE(inventoryObjects); n++) {
|
||||
if (_mouseX > _itemLocations[n].x && _mouseY > _itemLocations[n].y &&
|
||||
_mouseX < _itemLocations[n].x + OBJWIDTH &&
|
||||
_mouseY < _itemLocations[n].y + OBJHEIGHT) {
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DrasculaEngine::updateVisible() {
|
||||
if (currentChapter == 1) {
|
||||
// nothing
|
||||
} else if (currentChapter == 2) {
|
||||
if (_roomNumber == 2 && flags[40] == 0)
|
||||
visible[3] = 0;
|
||||
else if (_roomNumber == 3 && flags[3] == 1)
|
||||
visible[8] = 0;
|
||||
else if (_roomNumber == 6 && flags[1] == 1 && flags[10] == 0) {
|
||||
visible[2] = 0;
|
||||
visible[4] = 1;
|
||||
} else if (_roomNumber == 7 && flags[35] == 1)
|
||||
visible[3] = 0;
|
||||
else if (_roomNumber == 14 && flags[5] == 1)
|
||||
visible[4] = 0;
|
||||
else if (_roomNumber == 18 && flags[28] == 1)
|
||||
visible[2] = 0;
|
||||
} else if (currentChapter == 3) {
|
||||
// nothing
|
||||
} else if (currentChapter == 4) {
|
||||
if (_roomNumber == 23 && flags[0] == 0 && flags[11] == 0)
|
||||
visible[2] = 1;
|
||||
if (_roomNumber == 23 && flags[0] == 1 && flags[11] == 0)
|
||||
visible[2] = 0;
|
||||
if (_roomNumber == 21 && flags[10] == 1)
|
||||
visible[2] = 0;
|
||||
if (_roomNumber == 22 && flags[26] == 1) {
|
||||
visible[2] = 0;
|
||||
visible[1] = 1;
|
||||
}
|
||||
if (_roomNumber == 22 && flags[27] == 1)
|
||||
visible[3] = 0;
|
||||
if (_roomNumber == 26 && flags[21] == 0)
|
||||
Common::strlcpy(objName[2], _textmisc[0], 20);
|
||||
if (_roomNumber == 26 && flags[18] == 1)
|
||||
visible[2] = 0;
|
||||
if (_roomNumber == 26 && flags[12] == 1)
|
||||
visible[1] = 0;
|
||||
if (_roomNumber == 31 && flags[13] == 1)
|
||||
visible[1] = 0;
|
||||
if (_roomNumber == 35 && flags[14] == 1)
|
||||
visible[2] = 0;
|
||||
if (_roomNumber == 35 && flags[17] == 1)
|
||||
visible[3] = 1;
|
||||
if (_roomNumber == 35 && flags[15] == 1)
|
||||
visible[1] = 0;
|
||||
} else if (currentChapter == 5) {
|
||||
if (_roomNumber == 49 && flags[6] == 1)
|
||||
visible[2] = 0;
|
||||
if (_roomNumber == 49 && flags[6] == 0)
|
||||
visible[1] = 0;
|
||||
if (_roomNumber == 49 && flags[6] == 1)
|
||||
visible[1] = 1;
|
||||
if (_roomNumber == 45 && flags[6] == 1)
|
||||
visible[3] = 1;
|
||||
if (_roomNumber == 53 && flags[2] == 1)
|
||||
visible[3] = 0;
|
||||
if (_roomNumber == 54 && flags[13] == 1)
|
||||
visible[3] = 0;
|
||||
if (_roomNumber == 55 && flags[8] == 1)
|
||||
visible[1] = 0;
|
||||
} else if (currentChapter == 6) {
|
||||
if (_roomNumber == 58 && flags[8] == 0)
|
||||
isDoor[1] = 0;
|
||||
if (_roomNumber == 58 && flags[8] == 1)
|
||||
isDoor[1] = 1;
|
||||
if (_roomNumber == 59)
|
||||
isDoor[1] = 0;
|
||||
if (_roomNumber == 60) {
|
||||
trackDrascula = 0;
|
||||
drasculaX = 155;
|
||||
drasculaY = 69;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Drascula
|
||||
162
engines/drascula/palette.cpp
Normal file
162
engines/drascula/palette.cpp
Normal file
@@ -0,0 +1,162 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "graphics/paletteman.h"
|
||||
|
||||
#include "drascula/drascula.h"
|
||||
|
||||
namespace Drascula {
|
||||
|
||||
static const char colorTable[][3] = {
|
||||
{ 0, 0, 0 }, { 0x10, 0x3E, 0x28 },
|
||||
{ 0, 0, 0 }, // unused
|
||||
{ 0x16, 0x3F, 0x16 }, { 0x09, 0x3F, 0x12 },
|
||||
{ 0x3F, 0x3F, 0x15 },
|
||||
{ 0, 0, 0 }, // unused
|
||||
{ 0x38, 0, 0 }, { 0x3F, 0x27, 0x0B },
|
||||
{ 0x2A, 0, 0x2A }, { 0x30, 0x30, 0x30 },
|
||||
{ 98, 91, 100 }
|
||||
};
|
||||
|
||||
void DrasculaEngine::setRGB(byte *pal, int colorCount) {
|
||||
int x, cnt = 0;
|
||||
|
||||
for (x = 0; x < colorCount; x++) {
|
||||
gamePalette[x][0] = pal[cnt++] / 4;
|
||||
gamePalette[x][1] = pal[cnt++] / 4;
|
||||
gamePalette[x][2] = pal[cnt++] / 4;
|
||||
}
|
||||
setPalette((byte *)&gamePalette);
|
||||
}
|
||||
|
||||
void DrasculaEngine::black() {
|
||||
int color, component;
|
||||
DacPalette256 blackPalette;
|
||||
|
||||
for (color = 0; color < 256; color++)
|
||||
for (component = 0; component < 3; component++)
|
||||
blackPalette[color][component] = 0;
|
||||
|
||||
blackPalette[254][0] = 0x3F;
|
||||
blackPalette[254][1] = 0x3F;
|
||||
blackPalette[254][2] = 0x15;
|
||||
|
||||
setPalette((byte *)&blackPalette);
|
||||
}
|
||||
|
||||
void DrasculaEngine::setPalette(byte *PalBuf) {
|
||||
byte pal[256 * 3];
|
||||
|
||||
for (int i = 0; i < 3 * 256; ++i) {
|
||||
pal[i] = PalBuf[i] * 4;
|
||||
}
|
||||
|
||||
_system->getPaletteManager()->setPalette(pal, 0, 256);
|
||||
_system->updateScreen();
|
||||
}
|
||||
|
||||
void DrasculaEngine::color_abc(int cl) {
|
||||
_color = cl;
|
||||
|
||||
for (int i = 0; i <= 2; i++)
|
||||
gamePalette[254][i] = colorTable[cl][i];
|
||||
|
||||
setPalette((byte *)&gamePalette);
|
||||
}
|
||||
|
||||
signed char DrasculaEngine::adjustToVGA(signed char value) {
|
||||
return (value & 0x3F) * (value > 0);
|
||||
}
|
||||
|
||||
void DrasculaEngine::fadeToBlack(int fadeSpeed) {
|
||||
signed char fade;
|
||||
unsigned int color, component;
|
||||
|
||||
DacPalette256 palFade;
|
||||
|
||||
for (fade = 63; fade >= 0; fade--) {
|
||||
for (color = 0; color < 256; color++) {
|
||||
for (component = 0; component < 3; component++) {
|
||||
palFade[color][component] = adjustToVGA(gamePalette[color][component] - 63 + fade);
|
||||
}
|
||||
}
|
||||
pause(fadeSpeed);
|
||||
|
||||
setPalette((byte *)&palFade);
|
||||
|
||||
updateEvents();
|
||||
}
|
||||
}
|
||||
|
||||
void DrasculaEngine::fadeFromBlack(int fadeSpeed) {
|
||||
signed char fade;
|
||||
unsigned int color, component;
|
||||
|
||||
DacPalette256 palFade;
|
||||
|
||||
for (fade = 0; fade < 64; fade++) {
|
||||
for (color = 0; color < 256; color++) {
|
||||
for (component = 0; component < 3; component++) {
|
||||
palFade[color][component] = adjustToVGA(gamePalette[color][component] - 63 + fade);
|
||||
}
|
||||
}
|
||||
pause(fadeSpeed);
|
||||
|
||||
setPalette((byte *)&palFade);
|
||||
|
||||
updateEvents();
|
||||
}
|
||||
}
|
||||
|
||||
void DrasculaEngine::assignPalette(DacPalette256 pal) {
|
||||
int color, component;
|
||||
|
||||
for (color = 235; color < 253; color++)
|
||||
for (component = 0; component < 3; component++)
|
||||
pal[color][component] = gamePalette[color][component];
|
||||
}
|
||||
|
||||
void DrasculaEngine::setDefaultPalette(DacPalette256 pal) {
|
||||
int color, component;
|
||||
|
||||
for (color = 235; color < 253; color++) {
|
||||
for (component = 0; component < 3; component++) {
|
||||
gamePalette[color][component] = pal[color][component];
|
||||
}
|
||||
}
|
||||
setPalette((byte *)&gamePalette);
|
||||
}
|
||||
|
||||
void DrasculaEngine::setPaletteBase(int darkness) {
|
||||
signed char fade;
|
||||
unsigned int color, component;
|
||||
|
||||
for (fade = darkness; fade >= 0; fade--) {
|
||||
for (color = 235; color < 253; color++) {
|
||||
for (component = 0; component < 3; component++)
|
||||
gamePalette[color][component] = adjustToVGA(gamePalette[color][component] - 8 + fade);
|
||||
}
|
||||
}
|
||||
|
||||
setPalette((byte *)&gamePalette);
|
||||
}
|
||||
|
||||
} // End of namespace Drascula
|
||||
96
engines/drascula/resource.cpp
Normal file
96
engines/drascula/resource.cpp
Normal file
@@ -0,0 +1,96 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "drascula/drascula.h"
|
||||
#include "common/compression/unarj.h"
|
||||
|
||||
namespace Drascula {
|
||||
|
||||
ArchiveMan::ArchiveMan() {
|
||||
_fallBack = false;
|
||||
}
|
||||
|
||||
void ArchiveMan::registerArchive(const Common::Path &filename, int priority) {
|
||||
add(filename.toString(), Common::makeArjArchive(filename), priority);
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *ArchiveMan::open(const Common::Path &filename) {
|
||||
if (_fallBack && SearchMan.hasFile(filename)) {
|
||||
return SearchMan.createReadStreamForMember(filename);
|
||||
}
|
||||
|
||||
return createReadStreamForMember(filename);
|
||||
}
|
||||
|
||||
TextResourceParser::TextResourceParser(Common::SeekableReadStream *stream, DisposeAfterUse::Flag dispose) :
|
||||
_stream(stream, dispose) {
|
||||
|
||||
// NOTE: strangely enough, the code before this refactoring used the size of
|
||||
// the stream as a fixed maximum length for the parser. Using an updated
|
||||
// (size-pos) would make more sense to me, but let's see what the experts say.
|
||||
_maxLen = _stream->size();
|
||||
}
|
||||
|
||||
void TextResourceParser::getLine(char *buf) {
|
||||
byte c;
|
||||
char *b;
|
||||
|
||||
for (;;) {
|
||||
b = buf;
|
||||
while (true) {
|
||||
c = ~_stream->readByte();
|
||||
if (_stream->eos()) break;
|
||||
|
||||
if (c == '\r')
|
||||
continue;
|
||||
if (c == '\n' || b - buf >= (_maxLen - 1))
|
||||
break;
|
||||
*b++ = c;
|
||||
}
|
||||
*b = '\0';
|
||||
if (_stream->eos() && b == buf)
|
||||
return;
|
||||
if (b != buf)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void TextResourceParser::parseInt(int &result) {
|
||||
char buf[256];
|
||||
getLine(buf);
|
||||
|
||||
if (!sscanf(buf, "%d", &result)) {
|
||||
result = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void TextResourceParser::parseString(char* result) {
|
||||
char buf[256];
|
||||
getLine(buf);
|
||||
|
||||
if (!sscanf(buf, "%s", result)) {
|
||||
*result = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // End of namespace Drascula
|
||||
2039
engines/drascula/rooms.cpp
Normal file
2039
engines/drascula/rooms.cpp
Normal file
File diff suppressed because it is too large
Load Diff
512
engines/drascula/saveload.cpp
Normal file
512
engines/drascula/saveload.cpp
Normal file
@@ -0,0 +1,512 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/textconsole.h"
|
||||
#include "common/translation.h"
|
||||
#include "common/text-to-speech.h"
|
||||
|
||||
#include "engines/savestate.h"
|
||||
#include "graphics/thumbnail.h"
|
||||
#include "gui/message.h"
|
||||
#include "gui/saveload.h"
|
||||
|
||||
#include "drascula/drascula.h"
|
||||
|
||||
#include "backends/keymapper/keymapper.h"
|
||||
|
||||
namespace Drascula {
|
||||
|
||||
#define MAGIC_HEADER 0xD6A55A57 // (D)rascula (GA)me (S)cummVM (SA)ve (ST)ate
|
||||
#define SAVEGAME_VERSION 1
|
||||
|
||||
void DrasculaEngine::checkForOldSaveGames() {
|
||||
Common::String indexFileName = Common::String::format("%s.epa", _targetName.c_str());
|
||||
Common::InSaveFile *indexFile = _saveFileMan->openForLoading(indexFileName);
|
||||
|
||||
// Check for the existence of an old index file
|
||||
if (!indexFile)
|
||||
return;
|
||||
|
||||
GUI::MessageDialog dialog0(
|
||||
_("ScummVM found that you have old saved games for Drascula that should be converted.\n"
|
||||
"The old saved game format is no longer supported, so you will not be able to load your games if you don't convert them.\n\n"
|
||||
"Press OK to convert them now, otherwise you will be asked again the next time you start the game.\n"), _("OK"), _("Cancel"));
|
||||
|
||||
int choice = dialog0.runModal();
|
||||
if (choice != GUI::kMessageOK)
|
||||
return;
|
||||
|
||||
// Convert every save slot we find in the index file to the new format
|
||||
Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
|
||||
Common::String pattern = Common::String::format("%s??", _targetName.c_str());
|
||||
|
||||
// Get list of savefiles for target game
|
||||
Common::StringArray filenames = saveFileMan->listSavefiles(pattern);
|
||||
Common::Array<int> slots;
|
||||
|
||||
for (auto &filename : filenames) {
|
||||
// Obtain the last 2 digits of the filename, since they correspond to the save slot
|
||||
int slotNum = atoi(filename.c_str() + filename.size() - 2);
|
||||
|
||||
// Ensure save slot is within valid range
|
||||
if (slotNum >= 1 && slotNum <= 10) {
|
||||
slots.push_back(slotNum);
|
||||
}
|
||||
}
|
||||
|
||||
// Sort save slot ids
|
||||
Common::sort<int>(slots.begin(), slots.end());
|
||||
|
||||
// Get savegame names from index
|
||||
Common::String saveDesc;
|
||||
|
||||
int line = 1;
|
||||
for (uint i = 0; i < slots.size(); i++) {
|
||||
// Ignore lines corresponding to unused saveslots
|
||||
for (; line < slots[i]; line++)
|
||||
indexFile->readLine();
|
||||
|
||||
// Copy the name in the line corresponding to the save slot
|
||||
saveDesc = indexFile->readLine();
|
||||
|
||||
// Handle cases where the save directory and save index are detectably out of sync
|
||||
if (saveDesc == "*")
|
||||
saveDesc = "No name specified.";
|
||||
|
||||
// Increment line number to keep it in sync with slot number
|
||||
line++;
|
||||
|
||||
// Convert savegame
|
||||
convertSaveGame(slots[i], saveDesc);
|
||||
}
|
||||
|
||||
delete indexFile;
|
||||
|
||||
// Remove index file
|
||||
_saveFileMan->removeSavefile(indexFileName);
|
||||
}
|
||||
|
||||
SaveStateDescriptor loadMetaData(Common::ReadStream *s, int slot, bool setPlayTime) {
|
||||
uint32 sig = s->readUint32BE();
|
||||
byte version = s->readByte();
|
||||
|
||||
SaveStateDescriptor desc; // init to an invalid save slot
|
||||
|
||||
if (sig != MAGIC_HEADER || version > SAVEGAME_VERSION)
|
||||
return desc;
|
||||
|
||||
// Save is valid, set its slot number
|
||||
desc.setSaveSlot(slot);
|
||||
|
||||
Common::String name;
|
||||
byte size = s->readByte();
|
||||
for (int i = 0; i < size; ++i)
|
||||
name += s->readByte();
|
||||
desc.setDescription(name);
|
||||
|
||||
uint32 saveDate = s->readUint32LE();
|
||||
int day = (saveDate >> 24) & 0xFF;
|
||||
int month = (saveDate >> 16) & 0xFF;
|
||||
int year = saveDate & 0xFFFF;
|
||||
desc.setSaveDate(year, month, day);
|
||||
|
||||
uint16 saveTime = s->readUint16LE();
|
||||
int hour = (saveTime >> 8) & 0xFF;
|
||||
int minutes = saveTime & 0xFF;
|
||||
desc.setSaveTime(hour, minutes);
|
||||
|
||||
uint32 playTime = s->readUint32LE();
|
||||
desc.setPlayTime(playTime * 1000);
|
||||
if (setPlayTime)
|
||||
g_engine->setTotalPlayTime(playTime * 1000);
|
||||
|
||||
return desc;
|
||||
}
|
||||
|
||||
void saveMetaData(Common::WriteStream *s, const Common::String &desc) {
|
||||
TimeDate curTime;
|
||||
g_system->getTimeAndDate(curTime);
|
||||
|
||||
uint32 saveDate = ((curTime.tm_mday & 0xFF) << 24) | (((curTime.tm_mon + 1) & 0xFF) << 16) | ((curTime.tm_year + 1900) & 0xFFFF);
|
||||
uint16 saveTime = ((curTime.tm_hour & 0xFF) << 8) | ((curTime.tm_min) & 0xFF);
|
||||
uint32 playTime = g_engine->getTotalPlayTime() / 1000;
|
||||
|
||||
s->writeUint32BE(MAGIC_HEADER);
|
||||
s->writeByte(SAVEGAME_VERSION);
|
||||
s->writeByte(desc.size());
|
||||
s->writeString(desc);
|
||||
s->writeUint32LE(saveDate);
|
||||
s->writeUint16LE(saveTime);
|
||||
s->writeUint32LE(playTime);
|
||||
}
|
||||
|
||||
void DrasculaEngine::convertSaveGame(int slot, const Common::String &desc) {
|
||||
Common::String oldFileName = Common::String::format("%s%02d", _targetName.c_str(), slot);
|
||||
Common::String newFileName = Common::String::format("%s.%03d", _targetName.c_str(), slot);
|
||||
Common::InSaveFile *oldFile = _saveFileMan->openForLoading(oldFileName);
|
||||
if (!oldFile)
|
||||
error("Can't open %s", oldFileName.c_str());
|
||||
Common::OutSaveFile *newFile = _saveFileMan->openForSaving(newFileName);
|
||||
if (!newFile)
|
||||
error("Can't open %s", newFileName.c_str());
|
||||
|
||||
// Read data from old file
|
||||
int32 dataSize = oldFile->size();
|
||||
byte *buffer = new byte[dataSize];
|
||||
oldFile->read(buffer, dataSize);
|
||||
|
||||
// First, write the appropriate meta data in the new file
|
||||
saveMetaData(newFile, desc);
|
||||
Graphics::saveThumbnail(*newFile); // basically, at this point this will capture a black screen
|
||||
|
||||
// And then attach the actual save data
|
||||
newFile->write(buffer, dataSize);
|
||||
newFile->finalize();
|
||||
if (newFile->err())
|
||||
warning("Can't write file '%s'. (Disk full?)", newFileName.c_str());
|
||||
|
||||
delete[] buffer;
|
||||
delete newFile;
|
||||
delete oldFile;
|
||||
|
||||
// Remove old save file
|
||||
_saveFileMan->removeSavefile(oldFileName);
|
||||
}
|
||||
|
||||
Common::Error DrasculaEngine::loadGameState(int slot) {
|
||||
// The boolean returned by loadGame() indicates if loading is in the same
|
||||
// chapter or in a different one. Thus it does not indicate an error.
|
||||
loadGame(slot);
|
||||
return Common::kNoError;
|
||||
}
|
||||
|
||||
bool DrasculaEngine::canLoadGameStateCurrently(Common::U32String *msg) {
|
||||
return _canSaveLoad;
|
||||
}
|
||||
|
||||
Common::Error DrasculaEngine::saveGameState(int slot, const Common::String &desc, bool isAutosave) {
|
||||
saveGame(slot, desc);
|
||||
return Common::kNoError;
|
||||
}
|
||||
|
||||
bool DrasculaEngine::canSaveGameStateCurrently(Common::U32String *msg) {
|
||||
return _canSaveLoad;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the first 10 save names, to be used in Drascula's save/load screen
|
||||
*/
|
||||
void DrasculaEngine::loadSaveNames() {
|
||||
Common::String saveFileName;
|
||||
Common::InSaveFile *in;
|
||||
|
||||
for (int n = 0; n < NUM_SAVES; n++) {
|
||||
saveFileName = Common::String::format("%s.%03d", _targetName.c_str(), n + 1);
|
||||
if ((in = _saveFileMan->openForLoading(saveFileName))) {
|
||||
SaveStateDescriptor desc = loadMetaData(in, n + 1, false);
|
||||
_saveNames[n] = desc.getDescription();
|
||||
delete in;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DrasculaEngine::saveGame(int slot, const Common::String &desc) {
|
||||
Common::OutSaveFile *out;
|
||||
int l;
|
||||
|
||||
Common::String saveFileName = getSaveStateName(slot);
|
||||
if (!(out = _saveFileMan->openForSaving(saveFileName))) {
|
||||
error("Unable to open the file");
|
||||
}
|
||||
|
||||
saveMetaData(out, desc);
|
||||
Graphics::saveThumbnail(*out);
|
||||
|
||||
// Actual save data follows
|
||||
out->writeSint32LE(currentChapter);
|
||||
out->write(currentData, 20);
|
||||
out->writeSint32LE(curX);
|
||||
out->writeSint32LE(curY);
|
||||
out->writeSint32LE(trackProtagonist);
|
||||
|
||||
for (l = 1; l < ARRAYSIZE(inventoryObjects); l++) {
|
||||
out->writeSint32LE(inventoryObjects[l]);
|
||||
}
|
||||
|
||||
for (l = 0; l < NUM_FLAGS; l++) {
|
||||
out->writeSint32LE(flags[l]);
|
||||
}
|
||||
|
||||
out->writeSint32LE(takeObject);
|
||||
out->writeSint32LE(pickedObject);
|
||||
|
||||
out->finalize();
|
||||
if (out->err())
|
||||
warning("Can't write file '%s'. (Disk full?)", saveFileName.c_str());
|
||||
|
||||
delete out;
|
||||
}
|
||||
|
||||
bool DrasculaEngine::loadGame(int slot) {
|
||||
int l, savedChapter, roomNum = 0;
|
||||
Common::InSaveFile *in;
|
||||
|
||||
previousMusic = roomMusic;
|
||||
_menuScreen = false;
|
||||
if (currentChapter != 1)
|
||||
clearRoom();
|
||||
|
||||
Common::String saveFileName = getSaveStateName(slot);
|
||||
if (!(in = _saveFileMan->openForLoading(saveFileName))) {
|
||||
error("missing savegame file %s", saveFileName.c_str());
|
||||
}
|
||||
|
||||
// If we currently are in room 102 while being attached below the pendulum
|
||||
// the character is invisible and some surface are temporarily used for other
|
||||
// things. Reset those before loading the savegame otherwise we may have some
|
||||
// issues such as the protagonist being invisible after reloading a savegame.
|
||||
if (_roomNumber == 102 && flags[1] == 2) {
|
||||
_characterVisible = true;
|
||||
loadPic(96, frontSurface);
|
||||
loadPic(97, frontSurface);
|
||||
loadPic(97, extraSurface);
|
||||
loadPic(99, backSurface);
|
||||
}
|
||||
|
||||
|
||||
loadMetaData(in, slot, true);
|
||||
Graphics::skipThumbnail(*in);
|
||||
|
||||
savedChapter = in->readSint32LE();
|
||||
if (savedChapter != currentChapter) {
|
||||
_currentSaveSlot = slot;
|
||||
currentChapter = savedChapter - 1;
|
||||
_loadedDifferentChapter = true;
|
||||
delete in;
|
||||
return false;
|
||||
}
|
||||
|
||||
in->read(currentData, 20);
|
||||
curX = in->readSint32LE();
|
||||
curY = in->readSint32LE();
|
||||
trackProtagonist = in->readSint32LE();
|
||||
|
||||
for (l = 1; l < ARRAYSIZE(inventoryObjects); l++) {
|
||||
inventoryObjects[l] = in->readSint32LE();
|
||||
}
|
||||
|
||||
for (l = 0; l < NUM_FLAGS; l++) {
|
||||
flags[l] = in->readSint32LE();
|
||||
}
|
||||
|
||||
takeObject = in->readSint32LE();
|
||||
pickedObject = in->readSint32LE();
|
||||
_loadedDifferentChapter = false;
|
||||
if (!sscanf(currentData, "%d.ald", &roomNum))
|
||||
error("Bad save format");
|
||||
|
||||
// When loading room 102 while being attached below the pendulum Some variables
|
||||
// are not correctly set and can cause random crashes when calling enterRoom below.
|
||||
// The crash occurs in moveCharacters() when accessing factor_red[curY + curHeight].
|
||||
if (roomNum == 102 && flags[1] == 2) {
|
||||
curX = 103;
|
||||
curY = 108;
|
||||
curWidth = curHeight = 0;
|
||||
}
|
||||
|
||||
enterRoom(roomNum);
|
||||
selectVerb(kVerbNone);
|
||||
|
||||
// When loading room 102 while being attached below the pendulum we
|
||||
// need to call activatePendulum() to properly initialized the scene.
|
||||
if (_roomNumber == 102 && flags[1] == 2)
|
||||
activatePendulum();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Common::String DrasculaEngine::enterName(Common::String &selectedName) {
|
||||
Common::KeyCode key;
|
||||
Common::String inputLine = selectedName;
|
||||
Common::Keymapper *keymapper = g_system->getEventManager()->getKeymapper();
|
||||
|
||||
flushKeyBuffer();
|
||||
flushActionBuffer();
|
||||
keymapper->getKeymap("game-shortcuts")->setEnabled(false);
|
||||
_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, true);
|
||||
|
||||
while (!shouldQuit()) {
|
||||
copyBackground(115, 14, 115, 14, 176, 9, bgSurface, screenSurface);
|
||||
print_abc((inputLine + "-").c_str(), 117, 15);
|
||||
updateScreen();
|
||||
|
||||
key = getScan();
|
||||
|
||||
if (key != 0) {
|
||||
if (key >= 0 && key <= 0xFF && isAlpha(key)) {
|
||||
inputLine += tolower(key);
|
||||
} else if ((key >= Common::KEYCODE_0 && key <= Common::KEYCODE_9) || key == Common::KEYCODE_SPACE) {
|
||||
inputLine += key;
|
||||
} else if (key == Common::KEYCODE_ESCAPE) {
|
||||
inputLine.clear();
|
||||
break;
|
||||
} else if (key == Common::KEYCODE_RETURN) {
|
||||
break;
|
||||
} else if (key == Common::KEYCODE_BACKSPACE) {
|
||||
inputLine.deleteLastChar();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
keymapper->getKeymap("game-shortcuts")->setEnabled(true);
|
||||
_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false);
|
||||
return inputLine;
|
||||
}
|
||||
|
||||
bool DrasculaEngine::scummVMSaveLoadDialog(bool isSave) {
|
||||
GUI::SaveLoadChooser *dialog;
|
||||
Common::String desc;
|
||||
int slot;
|
||||
|
||||
if (isSave) {
|
||||
dialog = new GUI::SaveLoadChooser(_("Save game:"), _("Save"), true);
|
||||
|
||||
slot = dialog->runModalWithCurrentTarget();
|
||||
desc = dialog->getResultString();
|
||||
|
||||
if (desc.empty()) {
|
||||
// create our own description for the saved game, the user didn't enter it
|
||||
desc = dialog->createDefaultSaveDescription(slot);
|
||||
}
|
||||
|
||||
if (desc.size() > 28)
|
||||
desc = Common::String(desc.c_str(), 28);
|
||||
} else {
|
||||
dialog = new GUI::SaveLoadChooser(_("Restore game:"), _("Restore"), false);
|
||||
slot = dialog->runModalWithCurrentTarget();
|
||||
}
|
||||
|
||||
delete dialog;
|
||||
|
||||
if (slot < 0)
|
||||
return true;
|
||||
|
||||
if (isSave) {
|
||||
saveGame(slot, desc);
|
||||
return true;
|
||||
} else {
|
||||
return loadGame(slot);
|
||||
}
|
||||
}
|
||||
|
||||
bool DrasculaEngine::saveLoadScreen() {
|
||||
int n, selectedSlot = 0;
|
||||
Common::String selectedName;
|
||||
|
||||
clearRoom();
|
||||
loadPic("savescr.alg", bgSurface, HALF_PAL);
|
||||
color_abc(kColorLightGreen);
|
||||
setCursor(kCursorCrosshair);
|
||||
loadSaveNames();
|
||||
|
||||
while (!shouldQuit()) {
|
||||
copyBackground();
|
||||
for (n = 0; n < NUM_SAVES; n++) {
|
||||
print_abc(_saveNames[n].c_str(), 116, 27 + 9 * n);
|
||||
}
|
||||
print_abc(selectedName.c_str(), 117, 15);
|
||||
|
||||
if (selectedName.size() > 0) {
|
||||
sayText(selectedName.c_str(), Common::TextToSpeechManager::INTERRUPT);
|
||||
}
|
||||
|
||||
updateScreen();
|
||||
updateEvents();
|
||||
|
||||
if (_leftMouseButton == 1) {
|
||||
// Check if the user has clicked on a save slot
|
||||
for (n = 0; n < NUM_SAVES; n++) {
|
||||
if (_mouseX > 115 && _mouseY > 27 + (9 * n) && _mouseX < 115 + 175 && _mouseY < 27 + 10 + (9 * n)) {
|
||||
selectedSlot = n;
|
||||
selectedName = _saveNames[selectedSlot];
|
||||
if (selectedName.empty()) {
|
||||
selectedName = enterName(selectedName);
|
||||
if (!selectedName.empty())
|
||||
_saveNames[selectedSlot] = selectedName; // update save name
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the user has clicked in the text area above the save slots
|
||||
if (_mouseX > 117 && _mouseY > 15 && _mouseX < 295 && _mouseY < 24 && !selectedName.empty()) {
|
||||
selectedName = enterName(selectedName);
|
||||
if (!selectedName.empty())
|
||||
_saveNames[selectedSlot] = selectedName; // update save name
|
||||
}
|
||||
|
||||
// Check if the user has clicked a button
|
||||
if (_mouseX > 208 && _mouseY > 123 && _mouseX < 282 && _mouseY < 149) {
|
||||
// "Save" button
|
||||
if (selectedName.empty()) {
|
||||
sayText("Please select a slot", Common::TextToSpeechManager::INTERRUPT);
|
||||
|
||||
print_abc("Please select a slot", 117, 15);
|
||||
updateScreen();
|
||||
delay(200);
|
||||
} else {
|
||||
selectVerb(kVerbNone);
|
||||
clearRoom();
|
||||
loadPic(_roomNumber, bgSurface, HALF_PAL);
|
||||
updateRoom();
|
||||
updateScreen();
|
||||
|
||||
saveGame(selectedSlot + 1, _saveNames[selectedSlot]);
|
||||
return true;
|
||||
}
|
||||
} else if (_mouseX > 125 && _mouseY > 123 && _mouseX < 199 && _mouseY < 149) {
|
||||
// "Load" button
|
||||
if (selectedName.empty()) {
|
||||
sayText("Please select a slot", Common::TextToSpeechManager::INTERRUPT);
|
||||
|
||||
print_abc("Please select a slot", 117, 15);
|
||||
updateScreen();
|
||||
delay(200);
|
||||
} else {
|
||||
return loadGame(selectedSlot + 1);
|
||||
}
|
||||
} else if (_mouseX > 168 && _mouseY > 154 && _mouseX < 242 && _mouseY < 180) {
|
||||
// "Play" button
|
||||
break;
|
||||
}
|
||||
} // if (_leftMouseButton == 1)
|
||||
|
||||
_leftMouseButton = 0;
|
||||
delay(10);
|
||||
}
|
||||
|
||||
selectVerb(kVerbNone);
|
||||
clearRoom();
|
||||
loadPic(_roomNumber, bgSurface, HALF_PAL);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // End of namespace Drascula
|
||||
339
engines/drascula/sound.cpp
Normal file
339
engines/drascula/sound.cpp
Normal file
@@ -0,0 +1,339 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "audio/audiostream.h"
|
||||
#include "audio/mixer.h"
|
||||
#include "audio/decoders/raw.h"
|
||||
|
||||
#include "common/config-manager.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/substream.h"
|
||||
#include "common/text-to-speech.h"
|
||||
|
||||
#include "backends/audiocd/audiocd.h"
|
||||
|
||||
#include "drascula/drascula.h"
|
||||
|
||||
// For consistency with the verb names, the English words in the volume controls menu (which are pictures)
|
||||
// are translated into the game's language for text-to-speech
|
||||
static const char *volumeControlsEnglish[] = {
|
||||
"Master",
|
||||
"Voice/FX",
|
||||
"Music"
|
||||
};
|
||||
|
||||
static const char *volumeControlsSpanish[] = {
|
||||
"Maestro",
|
||||
"Voces/efectos de sonido",
|
||||
"M\243sica"
|
||||
};
|
||||
|
||||
static const char *volumeControlsItalian[] = {
|
||||
"Principale",
|
||||
"Voci/effetti sonori",
|
||||
"Musica"
|
||||
};
|
||||
|
||||
static const char *volumeControlsFrench[] = {
|
||||
"Principal",
|
||||
"Voix/effets sonores",
|
||||
"Musique"
|
||||
};
|
||||
|
||||
static const char *volumeControlsGerman[] = {
|
||||
"Gesamtlautst\204rke",
|
||||
"Stimmen/Soundeffekte",
|
||||
"Musik"
|
||||
};
|
||||
|
||||
// The Russian volume controls are translated in-game
|
||||
static const char *volumeControlsRussian[] = {
|
||||
"\xce\xe1\xf9\xe8\xe9", // "Общий"
|
||||
"\xc3\xee\xeb\xee\xf1", // "Голос"
|
||||
"\xcc\xf3\xe7\xfb\xea\xe0" // "Музыка"
|
||||
};
|
||||
|
||||
enum VolumeControlType {
|
||||
kMaster = 0,
|
||||
kSpeechAndSFX = 1,
|
||||
kMusic = 2
|
||||
};
|
||||
|
||||
namespace Drascula {
|
||||
|
||||
void DrasculaEngine::syncSoundSettings() {
|
||||
// Sync the engine with the config manager
|
||||
|
||||
bool mute = false;
|
||||
if (ConfMan.hasKey("mute"))
|
||||
mute = ConfMan.getBool("mute");
|
||||
|
||||
// We need to handle the speech mute separately here. This is because the
|
||||
// engine code should be able to rely on all speech sounds muted when the
|
||||
// user specified subtitles only mode, which results in "speech_mute" to
|
||||
// be set to "true". The global mute setting has precedence over the
|
||||
// speech mute setting though.
|
||||
bool speechMute = mute;
|
||||
if (!speechMute)
|
||||
speechMute = ConfMan.getBool("speech_mute");
|
||||
|
||||
_mixer->muteSoundType(Audio::Mixer::kPlainSoundType, mute);
|
||||
_mixer->muteSoundType(Audio::Mixer::kSFXSoundType, mute);
|
||||
_mixer->muteSoundType(Audio::Mixer::kSpeechSoundType, speechMute);
|
||||
_mixer->muteSoundType(Audio::Mixer::kMusicSoundType, mute);
|
||||
|
||||
int voiceVolume = ConfMan.getInt("speech_volume");
|
||||
int musicVolume = ConfMan.getInt("music_volume");
|
||||
// If the music and voice volume are correct, don't change anything.
|
||||
// Otherwise compute the master volume using an approximation of sqrt(max) * 16 which would result in the master
|
||||
// volume being the same value as the max of music and voice.
|
||||
if (_mixer->getVolumeForSoundType(Audio::Mixer::kSpeechSoundType) != voiceVolume || _mixer->getVolumeForSoundType(Audio::Mixer::kMusicSoundType) != musicVolume) {
|
||||
int masterVolume = MAX(musicVolume, voiceVolume) * 2 / 3 + 86;
|
||||
_mixer->setVolumeForSoundType(Audio::Mixer::kPlainSoundType, masterVolume);
|
||||
_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, voiceVolume);
|
||||
_mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, voiceVolume);
|
||||
_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, musicVolume);
|
||||
}
|
||||
}
|
||||
|
||||
int DrasculaEngine::updateVolume(int prevVolume, int prevVolumeY) {
|
||||
prevVolumeY += 10;
|
||||
if (_mouseY < prevVolumeY && prevVolume < 15)
|
||||
prevVolume++;
|
||||
if (_mouseY > prevVolumeY && prevVolume > 0)
|
||||
prevVolume--;
|
||||
return prevVolume;
|
||||
}
|
||||
|
||||
void DrasculaEngine::volumeControls() {
|
||||
if (_lang == kSpanish && currentChapter != 6)
|
||||
loadPic(95, tableSurface);
|
||||
|
||||
copyRect(1, 56, 73, 63, 177, 97, tableSurface, screenSurface);
|
||||
updateScreen(73, 63, 73, 63, 177, 97, screenSurface);
|
||||
|
||||
setCursor(kCursorCrosshair);
|
||||
showCursor();
|
||||
|
||||
// The engine has three volume controls: master, SFx/Speech and Music.
|
||||
// ScummVM doesn't have a master volume, so we abuse the kPlainSoundType to store it.
|
||||
// In drascula, we only use the kMusicSoundType and kSpeechSoundType to play sounds.
|
||||
// For consistency with the ScummVM options dialog we also set the kSFXSoundType volume
|
||||
// to the same value as the kMusicSoundType value, but we don't actually use it.
|
||||
|
||||
// The engines uses masterVolume, voiceVolume and musicVolume between 0 and 15.
|
||||
// We store in the mixer:
|
||||
// - masterVolume * 16 in kPlainSoundType
|
||||
// - (masterVolume + 1) * (voiceVolume + 1) - 1 in both kSpeechSoundType and kSFXSoundType
|
||||
// - (masterVolume + 1) * (musicVolume + 1) - 1 in kMusicSoundType
|
||||
|
||||
while (!shouldQuit()) {
|
||||
int masterVolume = CLIP((_mixer->getVolumeForSoundType(Audio::Mixer::kPlainSoundType) / 16), 0, 15);
|
||||
int voiceVolume = CLIP(((_mixer->getVolumeForSoundType(Audio::Mixer::kSpeechSoundType) + 1) / (masterVolume + 1) - 1), 0, 15);
|
||||
int musicVolume = CLIP(((_mixer->getVolumeForSoundType(Audio::Mixer::kMusicSoundType) + 1) / (masterVolume + 1) - 1), 0, 15);
|
||||
|
||||
int masterVolumeY = 72 + 61 - masterVolume * 4;
|
||||
int voiceVolumeY = 72 + 61 - voiceVolume * 4;
|
||||
int musicVolumeY = 72 + 61 - musicVolume * 4;
|
||||
|
||||
updateRoom();
|
||||
|
||||
copyRect(1, 56, 73, 63, 177, 97, tableSurface, screenSurface);
|
||||
|
||||
copyBackground(183, 56, 82, masterVolumeY, 39, 2 + masterVolume * 4, tableSurface, screenSurface);
|
||||
copyBackground(183, 56, 138, voiceVolumeY, 39, 2 + voiceVolume * 4, tableSurface, screenSurface);
|
||||
copyBackground(183, 56, 194, musicVolumeY, 39, 2 + musicVolume * 4, tableSurface, screenSurface);
|
||||
|
||||
updateScreen();
|
||||
|
||||
updateEvents();
|
||||
|
||||
// we're ignoring keypresses, so just empty the keyboard buffer
|
||||
while (getScan())
|
||||
;
|
||||
|
||||
if (_rightMouseButton == 1) {
|
||||
// Clear this to avoid going straight to the inventory
|
||||
_rightMouseButton = 0;
|
||||
delay(100);
|
||||
break;
|
||||
}
|
||||
if (_leftMouseButton == 1) {
|
||||
delay(100);
|
||||
if (_mouseX > 80 && _mouseX < 121) {
|
||||
masterVolume = updateVolume(masterVolume, masterVolumeY);
|
||||
_mixer->setVolumeForSoundType(Audio::Mixer::kPlainSoundType, masterVolume * 16);
|
||||
}
|
||||
|
||||
if (_mouseX > 136 && _mouseX < 178)
|
||||
voiceVolume = updateVolume(voiceVolume, voiceVolumeY);
|
||||
|
||||
if (_mouseX > 192 && _mouseX < 233)
|
||||
musicVolume = updateVolume(musicVolume, musicVolumeY);
|
||||
|
||||
voiceVolume = (masterVolume + 1) * (voiceVolume + 1) - 1;
|
||||
_mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, voiceVolume);
|
||||
_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, voiceVolume);
|
||||
ConfMan.setInt("speech_volume", voiceVolume);
|
||||
ConfMan.setInt("sfx_volume", voiceVolume);
|
||||
|
||||
musicVolume = (masterVolume + 1) * (musicVolume + 1) - 1;
|
||||
_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, musicVolume);
|
||||
ConfMan.setInt("music_volume", musicVolume);
|
||||
}
|
||||
|
||||
const char **controlNames;
|
||||
|
||||
switch (_lang) {
|
||||
case kEnglish:
|
||||
controlNames = volumeControlsEnglish;
|
||||
break;
|
||||
case kSpanish:
|
||||
controlNames = volumeControlsSpanish;
|
||||
break;
|
||||
case kGerman:
|
||||
controlNames = volumeControlsGerman;
|
||||
break;
|
||||
case kFrench:
|
||||
controlNames = volumeControlsFrench;
|
||||
break;
|
||||
case kItalian:
|
||||
controlNames = volumeControlsItalian;
|
||||
break;
|
||||
case kRussian:
|
||||
controlNames = volumeControlsRussian;
|
||||
break;
|
||||
default:
|
||||
controlNames = volumeControlsEnglish;
|
||||
}
|
||||
|
||||
Common::String ttsMessage;
|
||||
if (_mouseX > 80 && _mouseX < 121)
|
||||
ttsMessage = controlNames[kMaster];
|
||||
else if (_mouseX > 136 && _mouseX < 178)
|
||||
ttsMessage = controlNames[kSpeechAndSFX];
|
||||
else if (_mouseX > 192 && _mouseX < 233)
|
||||
ttsMessage = controlNames[kMusic];
|
||||
else
|
||||
_previousSaid.clear();
|
||||
|
||||
if (ttsMessage.size() > 0) {
|
||||
sayText(ttsMessage, Common::TextToSpeechManager::INTERRUPT);
|
||||
}
|
||||
}
|
||||
|
||||
if (_lang == kSpanish && currentChapter != 6)
|
||||
loadPic(974, tableSurface);
|
||||
|
||||
selectVerb(kVerbNone);
|
||||
|
||||
updateEvents();
|
||||
}
|
||||
|
||||
void DrasculaEngine::playSound(int soundNum) {
|
||||
char file[20];
|
||||
Common::sprintf_s(file, "s%i.als", soundNum);
|
||||
|
||||
playFile(file);
|
||||
}
|
||||
|
||||
void DrasculaEngine::finishSound() {
|
||||
delay(1);
|
||||
|
||||
while (soundIsActive())
|
||||
_system->delayMillis(10);
|
||||
}
|
||||
|
||||
void DrasculaEngine::playMusic(int p) {
|
||||
_system->getAudioCDManager()->stop();
|
||||
_system->getAudioCDManager()->play(p - 1, 1, 0, 0);
|
||||
}
|
||||
|
||||
void DrasculaEngine::stopMusic() {
|
||||
_system->getAudioCDManager()->stop();
|
||||
}
|
||||
|
||||
void DrasculaEngine::updateMusic() {
|
||||
_system->getAudioCDManager()->update();
|
||||
}
|
||||
|
||||
int DrasculaEngine::musicStatus() {
|
||||
return _system->getAudioCDManager()->isPlaying();
|
||||
}
|
||||
|
||||
void DrasculaEngine::stopSound() {
|
||||
_mixer->stopHandle(_soundHandle);
|
||||
}
|
||||
|
||||
void DrasculaEngine::MusicFadeout() {
|
||||
int org_vol = _mixer->getVolumeForSoundType(Audio::Mixer::kMusicSoundType);
|
||||
while (!shouldQuit()) {
|
||||
int vol = _mixer->getVolumeForSoundType(Audio::Mixer::kMusicSoundType);
|
||||
vol -= 10;
|
||||
if (vol < 0)
|
||||
vol = 0;
|
||||
_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, vol);
|
||||
if (vol == 0)
|
||||
break;
|
||||
updateEvents();
|
||||
_system->updateScreen();
|
||||
_system->delayMillis(50);
|
||||
}
|
||||
_system->getAudioCDManager()->stop();
|
||||
_system->delayMillis(100);
|
||||
_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, org_vol);
|
||||
}
|
||||
|
||||
void DrasculaEngine::playFile(const char *fname) {
|
||||
Common::SeekableReadStream *stream = _archives.open(fname);
|
||||
if (stream) {
|
||||
int startOffset = 32;
|
||||
int soundSize = stream->size() - 64;
|
||||
|
||||
if (!strcmp(fname, "3.als") && soundSize == 145166 && _lang != kSpanish) {
|
||||
// WORKAROUND: File 3.als with English speech files has a big silence at
|
||||
// its beginning and end. We seek past the silence at the beginning,
|
||||
// and ignore the silence at the end
|
||||
// Fixes bug #3969 - "DRASCULA: Voice delayed"
|
||||
startOffset = 73959;
|
||||
soundSize = soundSize - startOffset - 26306;
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *subStream = new Common::SeekableSubReadStream(
|
||||
stream, startOffset, startOffset + soundSize, DisposeAfterUse::YES);
|
||||
if (!subStream) {
|
||||
warning("playFile: Out of memory");
|
||||
delete stream;
|
||||
return;
|
||||
}
|
||||
|
||||
Audio::AudioStream *sound = Audio::makeRawStream(subStream, 11025,
|
||||
Audio::FLAG_UNSIGNED);
|
||||
_mixer->playStream(Audio::Mixer::kSpeechSoundType, &_soundHandle, sound);
|
||||
} else
|
||||
warning("playFile: Could not open %s", fname);
|
||||
}
|
||||
|
||||
bool DrasculaEngine::soundIsActive() {
|
||||
return _mixer->isSoundHandleActive(_soundHandle);
|
||||
}
|
||||
|
||||
} // End of namespace Drascula
|
||||
1006
engines/drascula/talk.cpp
Normal file
1006
engines/drascula/talk.cpp
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user