Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
engines/drascula/drascula.cpp
engines/drascula/metaengine.cpp
engines/drascula/saveload.cpp

469
engines/drascula/actors.cpp Normal file
View 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

File diff suppressed because it is too large Load Diff

View 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

View 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

View 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

View 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

View File

@@ -0,0 +1,5 @@
begin_section("Drascula");
add_person("Filippos Karapetis", "bluegr", "");
add_person("Pawe&#322; Ko&#322;odziejski", "aquadran", "");
add_person("Thierry Crozat", "criezy", "");
end_section();

View 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);

View 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

File diff suppressed because it is too large Load Diff

824
engines/drascula/drascula.h Normal file
View 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 */

View 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

View 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

View 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

View 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

View 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

View 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

View 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

File diff suppressed because it is too large Load Diff

View 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
View 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

File diff suppressed because it is too large Load Diff