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

2
engines/hopkins/POTFILES Normal file
View File

@@ -0,0 +1,2 @@
engines/hopkins/metaengine.cpp

796
engines/hopkins/anim.cpp Normal file
View File

@@ -0,0 +1,796 @@
/* 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 "hopkins/anim.h"
#include "hopkins/files.h"
#include "hopkins/globals.h"
#include "hopkins/graphics.h"
#include "hopkins/hopkins.h"
#include "common/system.h"
#include "common/file.h"
#include "common/rect.h"
#include "engines/util.h"
namespace Hopkins {
AnimationManager::AnimationManager(HopkinsEngine *vm) {
_vm = vm;
_clearAnimationFl = false;
for (int i = 0; i < 8; ++i)
Common::fill((byte *)&Bank[i], (byte *)&Bank[i] + sizeof(BankItem), 0);
for (int i = 0; i < 35; ++i)
Common::fill((byte *)&_animBqe[i], (byte *)&_animBqe[i] + sizeof(BqeAnimItem), 0);
}
void AnimationManager::clearAll() {
initAnimBqe();
}
/**
* Play Animation
* @param filename Filename of animation to play
* @param rate1 Delay amount before starting animation
* @param rate2 Delay amount between animation frames
* @param rate3 Delay amount after animation finishes
*/
void AnimationManager::playAnim(const Common::Path &hiresName, const Common::Path &lowresName, uint32 rate1, uint32 rate2, uint32 rate3, bool skipSeqFl) {
Common::File f;
if (_vm->shouldQuit())
return;
_vm->_events->mouseOff();
byte *screenP = _vm->_graphicsMan->_backBuffer;
if (!f.open(hiresName)) {
if (!f.open(lowresName))
error("Files not found: %s - %s", hiresName.toString().c_str(), lowresName.toString().c_str());
}
f.skip(6);
f.read(_vm->_graphicsMan->_palette, 800);
f.skip(4);
size_t nbytes = f.readUint32LE();
f.skip(14);
f.read(screenP, nbytes);
if (_clearAnimationFl)
_vm->_graphicsMan->clearScreen();
if (skipSeqFl) {
_vm->_graphicsMan->setPaletteVGA256(_vm->_graphicsMan->_palette);
} else {
_vm->_graphicsMan->setPaletteVGA256(_vm->_graphicsMan->_palette);
_vm->_graphicsMan->display8BitRect(screenP, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0);
_vm->_graphicsMan->addRefreshRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
_vm->_graphicsMan->updateScreen();
}
_vm->_events->_rateCounter = 0;
_vm->_events->_escKeyFl = false;
_vm->_soundMan->loadAnimSound();
if (_vm->_globals->_eventMode == EVENTMODE_IGNORE) {
// Do pre-animation delay
do {
if (_vm->_events->_escKeyFl)
break;
_vm->_events->refreshEvents();
} while (!_vm->shouldQuit() && _vm->_events->_rateCounter < rate1);
}
if (!_vm->_events->_escKeyFl) {
_vm->_events->_rateCounter = 0;
int frameNumber = 0;
while (!_vm->shouldQuit()) {
++frameNumber;
_vm->_soundMan->playAnimSound(frameNumber);
byte imageStr[17];
// Read frame header
if (f.read(imageStr, 16) != 16)
break;
imageStr[16] = 0;
if (strncmp((const char *)imageStr, "IMAGE=", 6))
break;
f.read(screenP, READ_LE_UINT32(imageStr + 8));
if (_vm->_globals->_eventMode == EVENTMODE_IGNORE) {
do {
if (_vm->_events->_escKeyFl)
break;
_vm->_events->refreshEvents();
_vm->_soundMan->checkSoundEnd();
} while (!_vm->shouldQuit() && _vm->_events->_rateCounter < rate2);
}
if (!_vm->_events->_escKeyFl) {
_vm->_events->_rateCounter = 0;
if (*screenP != kByteStop)
_vm->_graphicsMan->copyVideoVbe16(screenP);
_vm->_graphicsMan->addRefreshRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
_vm->_graphicsMan->updateScreen();
_vm->_soundMan->checkSoundEnd();
}
}
}
if (_vm->_globals->_eventMode == EVENTMODE_IGNORE && !_vm->_events->_escKeyFl) {
// Do post-animation delay
do {
if (_vm->_events->_escKeyFl)
break;
_vm->_events->refreshEvents();
_vm->_soundMan->checkSoundEnd();
} while (_vm->_events->_rateCounter < rate3);
}
if (!_vm->_events->_escKeyFl) {
_vm->_events->_rateCounter = 0;
_vm->_soundMan->checkSoundEnd();
}
if (_vm->_graphicsMan->_fadingFl) {
byte *screenCopy = _vm->_globals->allocMemory(307200);
f.seek(6);
f.read(_vm->_graphicsMan->_palette, 800);
f.skip(4);
nbytes = f.readUint32LE();
f.skip(14);
f.read(screenP, nbytes);
memcpy(screenCopy, screenP, 307200);
for (;;) {
byte imageStr[17];
if (f.read(imageStr, 16) != 16)
break;
imageStr[16] = 0;
if (strncmp((const char *)imageStr, "IMAGE=", 6))
break;
f.read(screenP, READ_LE_UINT32(imageStr + 8));
if (*screenP != kByteStop)
_vm->_graphicsMan->copyWinscanVbe3(screenP, screenCopy);
}
_vm->_graphicsMan->fadeOutDefaultLength(screenCopy);
_vm->_globals->freeMemory(screenCopy);
}
_vm->_graphicsMan->_fadingFl = false;
f.close();
_vm->_graphicsMan->_skipVideoLockFl = false;
_vm->_events->mouseOn();
}
/**
* Play Animation, type 2
*/
void AnimationManager::playAnim2(const Common::Path &hiresName, const Common::Path &lowresName, uint32 rate1, uint32 rate2, uint32 rate3) {
int oldScrollPosX = 0;
byte *screenP = nullptr;
Common::File f;
if (_vm->shouldQuit())
return;
_vm->_events->mouseOff();
while (!_vm->shouldQuit()) {
memcpy(_vm->_graphicsMan->_oldPalette, _vm->_graphicsMan->_palette, 769);
_vm->_graphicsMan->backupScreen();
if (!_vm->_graphicsMan->_lineNbr)
_vm->_graphicsMan->_scrollOffset = 0;
screenP = _vm->_graphicsMan->_backBuffer;
if (!f.open(hiresName)) {
if (!f.open(lowresName))
error("Error opening files: %s - %s", hiresName.toString().c_str(), lowresName.toString().c_str());
}
f.skip(6);
f.read(_vm->_graphicsMan->_palette, 800);
f.skip(4);
size_t nbytes = f.readUint32LE();
f.skip(14);
f.read(screenP, nbytes);
_vm->_graphicsMan->clearPalette();
oldScrollPosX = _vm->_graphicsMan->_scrollPosX;
_vm->_graphicsMan->setScreenWidth(SCREEN_WIDTH);
_vm->_graphicsMan->scrollScreen(0);
_vm->_graphicsMan->clearScreen();
_vm->_graphicsMan->_maxX = SCREEN_WIDTH;
_vm->_graphicsMan->setPaletteVGA256(_vm->_graphicsMan->_palette);
_vm->_graphicsMan->display8BitRect(screenP, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0);
_vm->_graphicsMan->addRefreshRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
_vm->_graphicsMan->updateScreen();
_vm->_events->_rateCounter = 0;
_vm->_events->_escKeyFl = false;
_vm->_soundMan->loadAnimSound();
if (_vm->_globals->_eventMode == EVENTMODE_IGNORE) {
while (!_vm->_events->_escKeyFl && _vm->_events->_rateCounter < rate1) {
_vm->_events->refreshEvents();
}
}
break;
}
if (!_vm->_events->_escKeyFl) {
_vm->_events->_rateCounter = 0;
int frameNumber = 0;
for (;;) {
if (_vm->_events->_escKeyFl)
break;
++frameNumber;
_vm->_soundMan->playAnimSound(frameNumber);
byte imageStr[17];
if (f.read(imageStr, 16) != 16)
break;
imageStr[16] = 0;
if (strncmp((const char *)imageStr, "IMAGE=", 6))
break;
f.read(screenP, READ_LE_UINT32(imageStr + 8));
if (_vm->_globals->_eventMode == EVENTMODE_IGNORE) {
while (!_vm->_events->_escKeyFl && _vm->_events->_rateCounter < rate2) {
_vm->_events->refreshEvents();
_vm->_soundMan->checkSoundEnd();
}
}
_vm->_events->_rateCounter = 0;
if (*screenP != kByteStop)
_vm->_graphicsMan->copyVideoVbe16(screenP);
_vm->_graphicsMan->addRefreshRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
_vm->_graphicsMan->updateScreen();
_vm->_soundMan->checkSoundEnd();
}
if (_vm->_globals->_eventMode == EVENTMODE_IGNORE) {
while (!_vm->_events->_escKeyFl && _vm->_events->_rateCounter < rate3) {
_vm->_events->refreshEvents();
_vm->_soundMan->checkSoundEnd();
}
}
}
_vm->_graphicsMan->_skipVideoLockFl = false;
f.close();
if (_vm->_graphicsMan->_fadingFl) {
f.seek(6);
f.read(_vm->_graphicsMan->_palette, 800);
f.skip(4);
size_t nbytes = f.readUint32LE();
f.skip(14);
f.read(screenP, nbytes);
byte *ptra = _vm->_globals->allocMemory(307200);
memcpy(ptra, screenP, 307200);
for (;;) {
byte imageStr[17];
if (f.read(imageStr, 16) != 16)
break;
imageStr[16] = 0;
if (strncmp((const char *)imageStr, "IMAGE=", 6))
break;
f.read(screenP, READ_LE_UINT32(imageStr + 8));
if (*screenP != kByteStop)
_vm->_graphicsMan->copyWinscanVbe3(screenP, ptra);
}
_vm->_graphicsMan->fadeOutDefaultLength(ptra);
ptra = _vm->_globals->freeMemory(ptra);
}
_vm->_graphicsMan->_fadingFl = false;
_vm->_graphicsMan->restoreScreen();
memcpy(_vm->_graphicsMan->_palette, _vm->_graphicsMan->_oldPalette, 769);
_vm->_graphicsMan->clearPalette();
_vm->_graphicsMan->clearScreen();
_vm->_graphicsMan->_scrollPosX = oldScrollPosX;
_vm->_graphicsMan->scrollScreen(oldScrollPosX);
if (_vm->_graphicsMan->_largeScreenFl) {
_vm->_graphicsMan->setScreenWidth(2 * SCREEN_WIDTH);
_vm->_graphicsMan->_maxX = 2 * SCREEN_WIDTH;
_vm->_graphicsMan->display8BitRect(_vm->_graphicsMan->_frontBuffer, _vm->_events->_startPos.x, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0);
} else {
_vm->_graphicsMan->setScreenWidth(SCREEN_WIDTH);
_vm->_graphicsMan->_maxX = SCREEN_WIDTH;
_vm->_graphicsMan->clearScreen();
_vm->_graphicsMan->display8BitRect(_vm->_graphicsMan->_frontBuffer, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0);
}
_vm->_graphicsMan->addRefreshRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
_vm->_graphicsMan->fadeInShort();
_vm->_graphicsMan->updateScreen();
_vm->_events->mouseOn();
}
/**
* Load Animation
*/
void AnimationManager::loadAnim(const Common::Path &animName) {
clearAnim();
Common::Path filename(animName);
filename.appendInPlace(".ANI");
Common::File f;
if (!f.open(filename))
error("Failed to open %s", filename.toString().c_str());
int filesize = f.size();
int nbytes = filesize - 115;
char header[10];
char dummyBuf[15];
char filename1[15];
char filename2[15];
char filename3[15];
char filename4[15];
char filename5[15];
char filename6[15];
f.read(header, 10);
f.read(dummyBuf, 15);
f.read(filename1, 15);
f.read(filename2, 15);
f.read(filename3, 15);
f.read(filename4, 15);
f.read(filename5, 15);
f.read(filename6, 15);
if (READ_BE_UINT32(header) != MKTAG('A', 'N', 'I', 'S'))
error("Invalid animation File: %s", filename.toString().c_str());
const char *files[6] = { &filename1[0], &filename2[0], &filename3[0], &filename4[0],
&filename5[0], &filename6[0] };
for (int idx = 0; idx <= 5; ++idx) {
if (files[idx][0]) {
if (!f.exists(files[idx]))
error("Missing file %s in animation File: %s", files[idx], filename.toString().c_str());
if (loadSpriteBank(idx + 1, files[idx]))
error("Invalid sprite bank in animation File: %s", filename.toString().c_str());
}
}
byte *data = _vm->_globals->allocMemory(nbytes + 1);
f.read(data, nbytes);
f.close();
for (int idx = 1; idx <= 20; ++idx)
searchAnim(data, idx, nbytes);
_vm->_globals->freeMemory(data);
}
/**
* Clear animation
*/
void AnimationManager::clearAnim() {
for (int idx = 0; idx < 35; ++idx) {
_animBqe[idx]._data = _vm->_globals->freeMemory(_animBqe[idx]._data);
_animBqe[idx]._enabledFl = false;
}
for (int idx = 0; idx < 8; ++idx) {
Bank[idx]._data = _vm->_globals->freeMemory(Bank[idx]._data);
Bank[idx]._loadedFl = false;
Bank[idx]._filename = "";
Bank[idx]._fileHeader = 0;
}
}
/**
* Load Sprite Bank
*/
int AnimationManager::loadSpriteBank(int idx, const Common::Path &filename) {
int result = 0;
Bank[idx]._loadedFl = true;
Bank[idx]._filename = filename;
byte *fileDataPtr = _vm->_fileIO->loadFile(filename);
Bank[idx]._fileHeader = 0;
if (fileDataPtr[1] == 'L' && fileDataPtr[2] == 'E')
Bank[idx]._fileHeader = 1;
else if (fileDataPtr[1] == 'O' && fileDataPtr[2] == 'R')
Bank[idx]._fileHeader = 2;
if (!Bank[idx]._fileHeader) {
_vm->_globals->freeMemory(fileDataPtr);
Bank[idx]._loadedFl = false;
result = -1;
}
Bank[idx]._data = fileDataPtr;
int objectDataIdx = 0;
for(objectDataIdx = 0; objectDataIdx <= 249; objectDataIdx++) {
int width = _vm->_objectsMan->getWidth(fileDataPtr, objectDataIdx);
int height = _vm->_objectsMan->getHeight(fileDataPtr, objectDataIdx);
if (!width && !height)
break;
}
if (objectDataIdx > 249) {
_vm->_globals->freeMemory(fileDataPtr);
Bank[idx]._loadedFl = false;
result = -2;
}
Bank[idx]._objDataIdx = objectDataIdx;
Common::String ofsFilename = Bank[idx]._filename.baseName();
char ch;
do {
ch = ofsFilename.lastChar();
ofsFilename.deleteLastChar();
} while (ch != '.');
ofsFilename += ".OFS";
Common::Path ofsPathname(Bank[idx]._filename.getParent().appendComponent(ofsFilename));
Common::File f;
if (f.exists(ofsPathname)) {
byte *ofsData = _vm->_fileIO->loadFile(ofsPathname);
byte *curOfsData = ofsData;
for (int objIdx = 0; objIdx < Bank[idx]._objDataIdx; ++objIdx, curOfsData += 8) {
int x1 = READ_LE_INT16(curOfsData);
int y1 = READ_LE_INT16(curOfsData + 2);
int x2 = READ_LE_INT16(curOfsData + 4);
int y2 = READ_LE_INT16(curOfsData + 6);
_vm->_objectsMan->setOffsetXY(Bank[idx]._data, objIdx, x1, y1, 0);
if (Bank[idx]._fileHeader == 2)
_vm->_objectsMan->setOffsetXY(Bank[idx]._data, objIdx, x2, y2, 1);
}
_vm->_globals->freeMemory(ofsData);
result = 0;
}
return result;
}
/**
* Search Animation
*/
void AnimationManager::searchAnim(const byte *data, int animIndex, int bufSize) {
for (int dataIdx = 0; dataIdx <= bufSize; dataIdx++) {
if (READ_BE_UINT32(&data[dataIdx]) == MKTAG('A', 'N', 'I', 'M')) {
int entryIndex = data[dataIdx + 4];
if (animIndex == entryIndex) {
int curBufferPos = dataIdx + 5;
int count = 0;
bool innerLoopCond = false;
do {
if (READ_BE_UINT32(&data[curBufferPos]) == MKTAG('A', 'N', 'I', 'M') || READ_BE_UINT24(&data[curBufferPos]) == MKTAG24('F', 'I', 'N'))
innerLoopCond = true;
if (bufSize < curBufferPos) {
_animBqe[animIndex]._enabledFl = false;
_animBqe[animIndex]._data = nullptr;
return;
}
++curBufferPos;
++count;
} while (!innerLoopCond);
_animBqe[animIndex]._data = _vm->_globals->allocMemory(count + 50);
_animBqe[animIndex]._enabledFl = true;
memcpy(_animBqe[animIndex]._data, data + dataIdx + 5, 20);
byte *dataP = _animBqe[animIndex]._data;
int curDestDataIndx = 20;
int curSrcDataIndx = dataIdx + 25;
for (int i = 0; i <= 4999; i++) {
memcpy(dataP + curDestDataIndx, data + curSrcDataIndx, 10);
if (!READ_LE_UINT16(data + curSrcDataIndx + 4))
break;
curDestDataIndx += 10;
curSrcDataIndx += 10;
}
break;
}
}
if (READ_BE_UINT24(&data[dataIdx]) == MKTAG24('F', 'I', 'N'))
break;
}
}
/**
* Play sequence
*/
void AnimationManager::playSequence(const Common::Path &file, uint32 rate1, uint32 rate2, uint32 rate3, bool skipEscFl, bool skipSeqFl, bool noColFl) {
if (_vm->shouldQuit())
return;
_vm->_events->_mouseFl = false;
if (!noColFl) {
_vm->_events->refreshScreenAndEvents();
_vm->_graphicsMan->backupScreen();
if (!_vm->_graphicsMan->_lineNbr)
_vm->_graphicsMan->_scrollOffset = 0;
}
byte *screenP = _vm->_graphicsMan->_backBuffer;
Common::File f;
if (!f.open(file))
error("Error opening file - %s", file.toString().c_str());
f.skip(6);
f.read(_vm->_graphicsMan->_palette, 800);
f.skip(4);
size_t nbytes = f.readUint32LE();
f.skip(14);
f.read(screenP, nbytes);
if (skipSeqFl) {
if (!_vm->getIsDemo()) {
_vm->_graphicsMan->setColorPercentage(252, 100, 100, 100);
_vm->_graphicsMan->setColorPercentage(253, 100, 100, 100);
_vm->_graphicsMan->setColorPercentage(251, 100, 100, 100);
_vm->_graphicsMan->setColorPercentage(254, 0, 0, 0);
}
_vm->_graphicsMan->setPaletteVGA256(_vm->_graphicsMan->_palette);
} else {
_vm->_graphicsMan->display8BitRect(screenP, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0);
_vm->_graphicsMan->addRefreshRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
_vm->_graphicsMan->updateScreen();
}
bool skipFl = false;
if (noColFl)
_vm->_graphicsMan->fadeInDefaultLength(screenP);
_vm->_events->_rateCounter = 0;
_vm->_events->_escKeyFl = false;
_vm->_soundMan->loadAnimSound();
if (_vm->_globals->_eventMode == EVENTMODE_IGNORE) {
do {
if (_vm->shouldQuit() || (_vm->_events->_escKeyFl && !skipEscFl)) {
skipFl = true;
break;
}
_vm->_events->_escKeyFl = false;
_vm->_events->refreshEvents();
_vm->_soundMan->checkSoundEnd();
} while (_vm->_events->_rateCounter < rate1);
}
_vm->_events->_rateCounter = 0;
if (!skipFl) {
int soundNumber = 0;
for (;;) {
++soundNumber;
_vm->_soundMan->playAnimSound(soundNumber);
byte imageStr[17];
if (f.read(imageStr, 16) != 16)
break;
imageStr[16] = 0;
if (strncmp((const char *)imageStr, "IMAGE=", 6))
break;
f.read(screenP, READ_LE_UINT32(imageStr + 8));
if (_vm->_globals->_eventMode == EVENTMODE_IGNORE) {
do {
if (_vm->shouldQuit() || (_vm->_events->_escKeyFl && !skipEscFl)) {
skipFl = true;
break;
}
_vm->_events->_escKeyFl = false;
_vm->_events->refreshEvents();
_vm->_soundMan->checkSoundEnd();
} while (_vm->_events->_rateCounter < rate2);
}
if (skipFl)
break;
_vm->_events->_rateCounter = 0;
if (*screenP != kByteStop)
_vm->_graphicsMan->copyVideoVbe16a(screenP);
_vm->_graphicsMan->addRefreshRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
_vm->_graphicsMan->updateScreen();
_vm->_soundMan->checkSoundEnd();
}
}
if (_vm->_globals->_eventMode == EVENTMODE_IGNORE && !skipFl) {
do {
if (_vm->shouldQuit() || (_vm->_events->_escKeyFl && !skipEscFl)) {
skipFl = true;
break;
}
_vm->_events->_escKeyFl = false;
_vm->_events->refreshEvents();
_vm->_soundMan->checkSoundEnd();
} while (_vm->_events->_rateCounter < rate3);
}
if (!skipFl)
_vm->_events->_rateCounter = 0;
_vm->_graphicsMan->_skipVideoLockFl = false;
f.close();
if (!noColFl) {
_vm->_graphicsMan->restoreScreen();
_vm->_events->_mouseFl = true;
}
}
/**
* Play Sequence type 2
*/
void AnimationManager::playSequence2(const Common::Path &file, uint32 rate1, uint32 rate2, uint32 rate3, bool skipSeqFl) {
byte *screenP;
Common::File f;
if (_vm->shouldQuit())
return;
_vm->_events->_mouseFl = false;
screenP = _vm->_graphicsMan->_backBuffer;
if (!f.open(file))
error("File not found - %s", file.toString().c_str());
f.skip(6);
f.read(_vm->_graphicsMan->_palette, 800);
f.skip(4);
size_t nbytes = f.readUint32LE();
f.skip(14);
f.read(screenP, nbytes);
if (skipSeqFl) {
_vm->_graphicsMan->setPaletteVGA256(_vm->_graphicsMan->_palette);
} else {
_vm->_graphicsMan->setPaletteVGA256(_vm->_graphicsMan->_palette);
_vm->_graphicsMan->display8BitRect(screenP, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0);
_vm->_graphicsMan->addRefreshRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
_vm->_graphicsMan->updateScreen();
}
_vm->_events->_rateCounter = 0;
_vm->_events->_escKeyFl = false;
_vm->_soundMan->loadAnimSound();
if (_vm->_globals->_eventMode == EVENTMODE_IGNORE) {
do {
_vm->_events->refreshEvents();
_vm->_soundMan->checkSoundEnd();
} while (!_vm->shouldQuit() && !_vm->_events->_escKeyFl && _vm->_events->_rateCounter < rate1);
}
if (!_vm->_events->_escKeyFl) {
_vm->_events->_rateCounter = 0;
int frameNumber = 0;
while (!_vm->shouldQuit()) {
_vm->_soundMan->playAnimSound(frameNumber++);
byte imageStr[17];
if (f.read(imageStr, 16) != 16)
break;
imageStr[16] = 0;
if (strncmp((const char *)imageStr, "IMAGE=", 6))
break;
f.read(screenP, READ_LE_UINT32(imageStr + 8));
if (_vm->_globals->_eventMode == EVENTMODE_IGNORE) {
do {
_vm->_events->refreshEvents();
} while (!_vm->shouldQuit() && !_vm->_events->_escKeyFl && _vm->_events->_rateCounter < rate2);
}
_vm->_events->_rateCounter = 0;
if (*screenP != kByteStop)
_vm->_graphicsMan->copyVideoVbe16a(screenP);
_vm->_graphicsMan->addRefreshRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
_vm->_graphicsMan->updateScreen();
_vm->_soundMan->checkSoundEnd();
}
}
if (_vm->_globals->_eventMode == EVENTMODE_IGNORE) {
// Wait for third rate delay
do {
_vm->_events->refreshEvents();
_vm->_soundMan->checkSoundEnd();
} while (!_vm->shouldQuit() && !_vm->_events->_escKeyFl && _vm->_events->_rateCounter < rate3);
}
_vm->_events->_rateCounter = 0;
if (_vm->_graphicsMan->_fadingFl) {
byte *ptra = _vm->_globals->allocMemory(307200);
f.seek(6);
f.read(_vm->_graphicsMan->_palette, 800);
f.skip(4);
nbytes = f.readUint32LE();
f.skip(14);
f.read(screenP, nbytes);
memcpy(ptra, screenP, 307200);
for (;;) {
byte imageStr[17];
if (f.read(imageStr, 16) != 16)
break;
imageStr[16] = 0;
if (strncmp((const char *)imageStr, "IMAGE=", 6))
break;
f.read(screenP, READ_LE_UINT32(imageStr + 8));
if (*screenP != kByteStop)
_vm->_graphicsMan->copyWinscanVbe(screenP, ptra);
}
_vm->_graphicsMan->fadeOutDefaultLength(ptra);
ptra = _vm->_globals->freeMemory(ptra);
}
_vm->_graphicsMan->_fadingFl = false;
f.close();
_vm->_events->_mouseFl = true;
}
void AnimationManager::initAnimBqe() {
for (int idx = 0; idx < 35; ++idx) {
_animBqe[idx]._data = nullptr;
_animBqe[idx]._enabledFl = false;
}
for (int idx = 0; idx < 8; ++idx) {
Bank[idx]._data = nullptr;
Bank[idx]._loadedFl = false;
Bank[idx]._filename = "";
Bank[idx]._fileHeader = 0;
}
}
} // End of namespace Hopkins

77
engines/hopkins/anim.h Normal file
View File

@@ -0,0 +1,77 @@
/* 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 HOPKINS_ANIM_H
#define HOPKINS_ANIM_H
#include "common/scummsys.h"
#include "common/endian.h"
#include "common/path.h"
#include "graphics/surface.h"
namespace Hopkins {
struct BankItem {
byte *_data;
bool _loadedFl;
Common::Path _filename;
int _fileHeader;
int _objDataIdx;
};
struct BqeAnimItem {
byte *_data;
bool _enabledFl;
};
class HopkinsEngine;
class AnimationManager {
private:
bool _clearAnimationFl;
HopkinsEngine *_vm;
void initAnimBqe();
int loadSpriteBank(int idx, const Common::Path &filename);
void searchAnim(const byte *data, int animIndex, int count);
public:
BqeAnimItem _animBqe[35];
BankItem Bank[8];
AnimationManager(HopkinsEngine *vm);
void clearAll();
void loadAnim(const Common::Path &animName);
void clearAnim();
void playAnim(const Common::Path &hiresName, const Common::Path &lowresName, uint32 rate1, uint32 rate2, uint32 rate3, bool skipSeqFl = false);
void playAnim2(const Common::Path &hiresName, const Common::Path &lowresName, uint32 rate1, uint32 rate2, uint32 rate3);
void playSequence(const Common::Path &file, uint32 rate1, uint32 rate2, uint32 rate3, bool skipEscFl, bool skipSeqFl, bool noColFl = false);
void playSequence2(const Common::Path &file, uint32 rate1, uint32 rate2, uint32 rate3, bool skipSeqFl = false);
void setClearAnimFlag() { _clearAnimationFl = true; }
void unsetClearAnimFlag() { _clearAnimationFl = false; }
};
} // End of namespace Hopkins
#endif /* HOPKINS_ANIM_H */

1271
engines/hopkins/computer.cpp Normal file

File diff suppressed because it is too large Load Diff

105
engines/hopkins/computer.h Normal file
View File

@@ -0,0 +1,105 @@
/* 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 HOPKINS_COMPUTER_H
#define HOPKINS_COMPUTER_H
#include "common/scummsys.h"
#include "common/str.h"
#include "common/rect.h"
namespace Hopkins {
class HopkinsEngine;
enum ComputerEnum { COMPUTER_HOPKINS = 1, COMPUTER_SAMANTHA = 2, COMPUTER_PUBLIC = 3 };
class ComputerManager {
private:
HopkinsEngine *_vm;
struct MenuItem {
int _lineSize;
char _line[90];
};
struct ScoreItem {
Common::String _name;
Common::String _score;
};
MenuItem _menuText[50];
char _inputBuf[200];
ScoreItem _score[6];
int _textColor;
Common::Point _textPosition;
Common::Point _ballPosition;
byte *_breakoutSpr;
int16 *_breakoutLevel;
int _breakoutBrickNbr;
int _breakoutScore;
int _breakoutLives;
int _breakoutSpeed;
bool _ballRightFl;
bool _ballUpFl;
int _breakoutLevelNbr;
int _padPositionX;
int _lowestHiScore;
int _minBreakoutMoveSpeed;
int _maxBreakoutMoveSpeed;
int _lastBreakoutMoveSpeed;
void loadMenu();
void restoreFBIRoom();
void setVideoMode();
void setTextMode();
void clearScreen();
void setTextColor(int col);
void setTextPosition(int yp, int xp);
void outText(const Common::String &msg);
void outText2(const Common::String &msg);
void readText(int idx);
void loadHiscore();
void newLevel();
void setModeVGA256();
void displayLives();
void displayBricks();
void displayGamesSubMenu();
int displayHiscores();
void displayHiscoreLine(const byte *objectData, int x, int y, int curChar);
void displayMessage(int xp, int yp, int textIdx);
void displayScore();
void displayScoreChar(int charPos, int charDisp);
void getScoreName();
void playBreakout();
int moveBall();
void saveScore();
void checkBallCollisions();
public:
ComputerManager(HopkinsEngine *vm);
void showComputer(ComputerEnum mode);
};
} // End of namespace Hopkins
#endif /* HOPKINS_COMPUTER_H */

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 hopkins "Hopkins FBI" yes "" "" "16bit highres"

View File

@@ -0,0 +1,4 @@
begin_section("Hopkins");
add_person("Arnaud Boutonn&eacute;", "Strangerke", "");
add_person("Paul Gilbert", "dreammaster", "");
end_section();

View File

@@ -0,0 +1,89 @@
/* 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 "hopkins/debugger.h"
#include "hopkins/globals.h"
#include "hopkins/graphics.h"
#include "hopkins/hopkins.h"
namespace Hopkins {
Debugger::Debugger(HopkinsEngine *vm) : GUI::Debugger() {
_vm = vm;
registerCmd("continue", WRAP_METHOD(Debugger, cmdExit));
registerCmd("rects", WRAP_METHOD(Debugger, cmd_DirtyRects));
registerCmd("teleport", WRAP_METHOD(Debugger, cmd_Teleport));
registerCmd("show_room", WRAP_METHOD(Debugger, cmd_ShowCurrentRoom));
registerCmd("zones", WRAP_METHOD(Debugger, cmd_Zones));
registerCmd("lines", WRAP_METHOD(Debugger, cmd_Lines));
}
// Turns dirty rects on or off
bool Debugger::cmd_DirtyRects(int argc, const char **argv) {
if (argc != 2) {
debugPrintf("%s: [on | off]\n", argv[0]);
return true;
} else {
_vm->_graphicsMan->_showDirtyRects = !strcmp(argv[1], "on");
return false;
}
}
// Change room number
bool Debugger::cmd_Teleport(int argc, const char **argv) {
if (argc != 2) {
debugPrintf("%s: [Room number]\n", argv[0]);
return true;
} else {
_vm->_globals->_exitId = atoi(argv[1]);
return false;
}
}
// Display room number
bool Debugger::cmd_ShowCurrentRoom(int argc, const char **argv) {
debugPrintf("Current room: %d\n", _vm->_globals->_curRoomNum);
return true;
}
bool Debugger::cmd_Zones(int argc, const char **argv) {
if (argc != 2) {
debugPrintf("%s: [on | off]\n", argv[0]);
return true;
} else {
_vm->_graphicsMan->_showZones = !strcmp(argv[1], "on");
return false;
}
}
bool Debugger::cmd_Lines(int argc, const char **argv) {
if (argc != 2) {
debugPrintf("%s: [on | off]\n", argv[0]);
return true;
} else {
_vm->_graphicsMan->_showLines = !strcmp(argv[1], "on");
return false;
}
}
} // End of namespace Hopkins

View File

@@ -0,0 +1,49 @@
/* 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 HOPKINS_DEBUGGER_H
#define HOPKINS_DEBUGGER_H
#include "common/scummsys.h"
#include "gui/debugger.h"
namespace Hopkins {
class HopkinsEngine;
class Debugger : public GUI::Debugger {
private:
HopkinsEngine *_vm;
public:
Debugger(HopkinsEngine *vm);
~Debugger() override {}
bool cmd_DirtyRects(int argc, const char **argv);
bool cmd_Teleport(int argc, const char **argv);
bool cmd_ShowCurrentRoom(int argc, const char **argv);
bool cmd_Zones(int argc, const char **argv);
bool cmd_Lines(int argc, const char **argv);
};
} // End of namespace Hopkins
#endif

View File

@@ -0,0 +1,72 @@
/* 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 "engines/advancedDetector.h"
#include "hopkins/detection.h"
#include "hopkins/hopkins.h"
static const DebugChannelDef debugFlagList[] = {
{Hopkins::kDebugPath, "Path", "Pathfinding debug level"},
{Hopkins::kDebugGraphics, "Graphics", "Graphics debug level"},
DEBUG_CHANNEL_END
};
static const PlainGameDescriptor hopkinsGames[] = {
{"hopkins", "Hopkins FBI"},
{nullptr, nullptr}
};
#include "hopkins/detection_tables.h"
const static char *const directoryGlobs[] = {
"voice",
"link",
nullptr
};
class HopkinsMetaEngineDetection : public AdvancedMetaEngineDetection<Hopkins::HopkinsGameDescription> {
public:
HopkinsMetaEngineDetection() : AdvancedMetaEngineDetection(Hopkins::gameDescriptions, hopkinsGames) {
_maxScanDepth = 3;
_directoryGlobs = directoryGlobs;
}
const char *getName() const override {
return "hopkins";
}
const char *getEngineName() const override {
return "Hopkins FBI";
}
const char *getOriginalCopyright() const override {
return "Hopkins FBI (C) 1997-2003 MP Entertainment";
}
const DebugChannelDef *getDebugChannels() const override {
return debugFlagList;
}
};
REGISTER_PLUGIN_STATIC(HOPKINS_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, HopkinsMetaEngineDetection);

View File

@@ -0,0 +1,38 @@
/* 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 HOPKINS_DETECTION_H
#define HOPKINS_DETECTION_H
namespace Hopkins {
struct HopkinsGameDescription {
AD_GAME_DESCRIPTION_HELPERS(desc);
ADGameDescription desc;
};
#define GAMEOPTION_GORE_DEFAULT_ON GUIO_GAMEOPTIONS1
#define GAMEOPTION_GORE_DEFAULT_OFF GUIO_GAMEOPTIONS2
} // End of namespace Hopkins
#endif // HOPKINS_DETECTION_H

View File

@@ -0,0 +1,176 @@
/* 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/>.
*
*/
namespace Hopkins {
static const HopkinsGameDescription gameDescriptions[] = {
{
// Hopkins FBI Linux Demo UK 1.00 and 1.02
{
"hopkins",
"Demo",
AD_ENTRY1s("RES_VAN.RES", "29414c05be8f9fe794c61572a65def12", 16060544),
Common::EN_ANY,
Common::kPlatformLinux,
ADGF_DEMO,
GUIO2(GAMEOPTION_GORE_DEFAULT_ON, GUIO_NOMIDI)
},
},
{
// Hopkins FBI OS/2, provided by Strangerke
{
"hopkins",
0,
AD_ENTRY2s("ENG_VOI.RES", "fa5789d1d8c19d160bce44a33e742fdf", 66860711,
"CREAN.TXT", "e13aa69d9e043f066776e1d0ef98fdf5", 1871),
Common::EN_ANY,
Common::kPlatformOS2,
ADGF_NO_FLAGS,
GUIO2(GAMEOPTION_GORE_DEFAULT_ON, GUIO_NOMIDI)
},
},
{
// Hopkins FBI BeOS, provided by Strangerke & Eriktorbjorn
{
"hopkins",
0,
AD_ENTRY1s("ENG_VOI.RES", "fa5789d1d8c19d160bce44a33e742fdf", 66860711),
Common::EN_ANY,
Common::kPlatformBeOS,
ADGF_NO_FLAGS,
GUIO2(GAMEOPTION_GORE_DEFAULT_ON, GUIO_NOMIDI)
},
},
{
// Hopkins FBI Win95 Spanish
{
"hopkins",
0,
AD_ENTRY1s("RES_VES.RES", "77ee08896466ae88cc1af3bf1a0bf78c", 32882302),
Common::ES_ESP,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO2(GAMEOPTION_GORE_DEFAULT_ON, GUIO_NOMIDI)
},
},
{
// Hopkins FBI Win95 UK, provided by Strangerke, alexbevi, greencis
{
"hopkins",
0,
AD_ENTRY1s("RES_VAN.RES", "f1693ac0b0859c8ecd8cb30ff43cf55f", 38296346),
Common::EN_ANY,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO2(GAMEOPTION_GORE_DEFAULT_OFF, GUIO_NOMIDI)
},
},
{
// Hopkins FBI Win95 RU, provided by greencis in bug #6324
{
"hopkins",
0,
AD_ENTRY1s("res_van.res", "bf17c710e184a25a6c8e9d1d9503c38e", 32197685),
Common::RU_RUS,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO2(GAMEOPTION_GORE_DEFAULT_ON, GUIO_NOMIDI)
},
},
{
// Hopkins FBI Linux, provided by Strangerke
{
"hopkins",
0,
AD_ENTRY1s("RES_VFR.RES", "0490d4d1aa71075ebf71cc79e5dc7894", 39817945),
Common::FR_FRA,
Common::kPlatformLinux,
ADGF_NO_FLAGS,
GUIO2(GAMEOPTION_GORE_DEFAULT_ON, GUIO_NOMIDI)
},
},
{
// Hopkins FBI Linux, provided by Strangerke
{
"hopkins",
0,
AD_ENTRY1s("RES_VAN.RES", "29414c05be8f9fe794c61572a65def12", 38832455),
Common::EN_ANY,
Common::kPlatformLinux,
ADGF_NO_FLAGS,
GUIO2(GAMEOPTION_GORE_DEFAULT_ON, GUIO_NOMIDI)
},
},
{
// Hopkins FBI Win95, French, provided by SylvainTV
{
"hopkins",
0,
AD_ENTRY1s("RES_VFR.RES", "b8a3849063c9eeefe80e82cfce1ad3cd", 39269361),
Common::FR_FRA,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO2(GAMEOPTION_GORE_DEFAULT_ON, GUIO_NOMIDI)
},
},
{
// Hopkins FBI Win95 Polish, provided by Paput in bug #6511
{
"hopkins",
0,
AD_ENTRY1s("RES_VAN.RES", "f2fec5172e4a7a9d35cb2a5f948ef6a9", 39400865),
Common::PL_POL,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO2(GAMEOPTION_GORE_DEFAULT_OFF, GUIO_NOMIDI)
},
},
{
// Hopkins FBI Win95 Demo, provided by Strangerke
// CHECKME: No voice! a second file is required though... Also, it has multi-language support
{
"hopkins",
"Demo",
AD_ENTRY1s("Hopkins.exe", "0c9ebfe371f4dcf84a49f333f04839a0", 376897),
Common::EN_ANY,
Common::kPlatformWindows,
ADGF_DEMO,
GUIO2(GAMEOPTION_GORE_DEFAULT_ON, GUIO_NOMIDI)
},
},
{
// Hopkins FBI Win95 Polish Demo, provided by Strangerke
{
"hopkins",
"Demo",
AD_ENTRY1s("RES_VAN.RES", "8262cfba261c200af4451902689dffe0", 12233202),
Common::PL_POL,
Common::kPlatformWindows,
ADGF_DEMO,
GUIO2(GAMEOPTION_GORE_DEFAULT_OFF, GUIO_NOMIDI)
},
},
{ AD_TABLE_END_MARKER }
};
} // End of namespace Hopkins

799
engines/hopkins/dialogs.cpp Normal file
View File

@@ -0,0 +1,799 @@
/* 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 "hopkins/dialogs.h"
#include "hopkins/events.h"
#include "hopkins/files.h"
#include "hopkins/globals.h"
#include "hopkins/graphics.h"
#include "hopkins/hopkins.h"
#include "hopkins/sound.h"
#include "common/scummsys.h"
#include "common/config-manager.h"
#include "common/events.h"
#include "common/file.h"
#include "common/util.h"
namespace Hopkins {
DialogsManager::DialogsManager(HopkinsEngine *vm) {
_vm = vm;
_inventFl = false;
_inventDisplayedFl = false;
_removeInventFl = false;
_inventX = _inventY = 0;
_oldInventX = 0;
_inventWidth = _inventHeight = 0;
_inventWin1 = nullptr;
_inventBuf2 = nullptr;
_inventoryIcons = nullptr;
}
DialogsManager::~DialogsManager() {
_vm->_globals->freeMemory(_inventWin1);
_vm->_globals->freeMemory(_inventBuf2);
_vm->_globals->freeMemory(_inventoryIcons);
}
void DialogsManager::clearAll() {
_inventWin1 = nullptr;
_inventBuf2 = nullptr;
}
void DialogsManager::loadIcons() {
_inventoryIcons = _vm->_fileIO->loadFile("ICONE.SPR");
}
void DialogsManager::drawInvent(Common::Point oldBorder, int oldBorderSpriteIndex, Common::Point newBorder, int newBorderSpriteIndex) {
if (!_inventDisplayedFl)
return;
_vm->_graphicsMan->restoreSurfaceRect(_vm->_graphicsMan->_frontBuffer, _inventWin1, _inventX, _inventY, _inventWidth, _inventHeight);
if (oldBorder.x && oldBorder.y)
_vm->_graphicsMan->drawVesaSprite(_vm->_graphicsMan->_frontBuffer, _inventBuf2, oldBorder.x + 300, oldBorder.y + 300, oldBorderSpriteIndex + 1);
if (newBorder.x && newBorder.y)
_vm->_graphicsMan->drawVesaSprite(_vm->_graphicsMan->_frontBuffer, _inventBuf2, newBorder.x + 300, newBorder.y + 300, newBorderSpriteIndex);
_vm->_graphicsMan->addDirtyRect(_inventX, _inventY, _inventX + _inventWidth, _inventY + _inventHeight);
}
void DialogsManager::showOptionsDialog() {
_vm->_events->changeMouseCursor(0);
_vm->_events->refreshScreenAndEvents();
Common::Path filename;
if (_vm->getPlatform() == Common::kPlatformOS2 || _vm->getPlatform() == Common::kPlatformBeOS)
filename = "OPTION.SPR";
else {
switch (_vm->_globals->_language) {
case LANG_FR:
filename = "OPTIFR.SPR";
break;
case LANG_EN:
filename = "OPTIAN.SPR";
break;
case LANG_SP:
filename = "OPTIES.SPR";
break;
default:
break;
}
}
_vm->_globals->_optionDialogSpr = _vm->_fileIO->loadFile(filename);
_vm->_globals->_optionDialogFl = true;
int scrollOffset = _vm->_graphicsMan->_scrollOffset;
bool doneFlag = false;
do {
if (_vm->_events->getMouseButton()) {
Common::Point mousePos(_vm->_events->getMouseX(), _vm->_events->getMouseY());
if (!_vm->_soundMan->_musicOffFl) {
if (mousePos.x >= scrollOffset + 300 && mousePos.y > 113 && mousePos.x <= scrollOffset + 327 && mousePos.y <= 138) {
// Change the music volume
++_vm->_soundMan->_musicVolume;
if (_vm->_soundMan->_musicVolume <= 12)
_vm->_soundMan->playSoundFile("bruit2.wav");
else
_vm->_soundMan->_musicVolume = 12;
_vm->_soundMan->setMODMusicVolume(_vm->_soundMan->_musicVolume);
_vm->_soundMan->updateScummVMSoundSettings();
}
if (!_vm->_soundMan->_musicOffFl && mousePos.x >= scrollOffset + 331 && mousePos.y > 113 && mousePos.x <= scrollOffset + 358 && mousePos.y <= 138) {
--_vm->_soundMan->_musicVolume;
if (_vm->_soundMan->_musicVolume >= 0)
_vm->_soundMan->playSoundFile("bruit2.wav");
else
_vm->_soundMan->_musicVolume = 0;
_vm->_soundMan->setMODMusicVolume(_vm->_soundMan->_musicVolume);
_vm->_soundMan->updateScummVMSoundSettings();
}
}
if (!_vm->_soundMan->_soundOffFl) {
// increase volume
if (mousePos.x >= scrollOffset + 300 && mousePos.y > 140 && mousePos.x <= scrollOffset + 327 && mousePos.y <= 165) {
++_vm->_soundMan->_soundVolume;
if (_vm->_soundMan->_soundVolume <= 16)
_vm->_soundMan->playSoundFile("bruit2.wav");
else
_vm->_soundMan->_soundVolume = 16;
_vm->_soundMan->setMODSampleVolume();
_vm->_soundMan->updateScummVMSoundSettings();
}
// Decrease volume
if (!_vm->_soundMan->_soundOffFl && mousePos.x >= scrollOffset + 331 && mousePos.y > 140 && mousePos.x <= scrollOffset + 358 && mousePos.y <= 165) {
--_vm->_soundMan->_soundVolume;
if (_vm->_soundMan->_soundVolume >= 0)
_vm->_soundMan->playSoundFile("bruit2.wav");
else
_vm->_soundMan->_soundVolume = 0;
_vm->_soundMan->setMODSampleVolume();
_vm->_soundMan->updateScummVMSoundSettings();
}
}
if (!_vm->_soundMan->_voiceOffFl) {
if (mousePos.x >= scrollOffset + 300 && mousePos.y > 167 && mousePos.x <= scrollOffset + 327 && mousePos.y <= 192) {
++_vm->_soundMan->_voiceVolume;
if (_vm->_soundMan->_voiceVolume <= 16)
_vm->_soundMan->playSoundFile("bruit2.wav");
else
_vm->_soundMan->_voiceVolume = 16;
_vm->_soundMan->setMODVoiceVolume();
_vm->_soundMan->updateScummVMSoundSettings();
}
if (!_vm->_soundMan->_voiceOffFl && mousePos.x >= scrollOffset + 331 && mousePos.y > 167 && mousePos.x <= scrollOffset + 358 && mousePos.y <= 192) {
--_vm->_soundMan->_voiceVolume;
if (_vm->_soundMan->_voiceVolume >= 0)
_vm->_soundMan->playSoundFile("bruit2.wav");
else
_vm->_soundMan->_voiceVolume = 0;
_vm->_soundMan->setMODVoiceVolume();
_vm->_soundMan->updateScummVMSoundSettings();
}
}
if (mousePos.x >= scrollOffset + 431) {
if (mousePos.y > 194 && mousePos.x <= scrollOffset + 489 && mousePos.y <= 219)
_vm->_soundMan->_textOffFl = !_vm->_soundMan->_textOffFl;
if (mousePos.x >= scrollOffset + 431) {
if (mousePos.y > 167 && mousePos.x <= scrollOffset + 489 && mousePos.y <= 192) {
_vm->_soundMan->_voiceOffFl = !_vm->_soundMan->_voiceOffFl;
_vm->_soundMan->updateScummVMSoundSettings();
}
if (mousePos.x >= scrollOffset + 431) {
if (mousePos.y > 113 && mousePos.x <= scrollOffset + 489 && mousePos.y <= 138) {
if (_vm->_soundMan->_musicOffFl) {
_vm->_soundMan->_musicOffFl = false;
_vm->_soundMan->setMODMusicVolume(_vm->_soundMan->_musicVolume);
} else {
_vm->_soundMan->_musicOffFl = true;
_vm->_soundMan->setMODMusicVolume(0);
}
_vm->_soundMan->updateScummVMSoundSettings();
}
if (mousePos.x >= scrollOffset + 431 && mousePos.y > 140 && mousePos.x <= scrollOffset + 489 && mousePos.y <= 165) {
_vm->_soundMan->_soundOffFl = !_vm->_soundMan->_soundOffFl;
_vm->_soundMan->updateScummVMSoundSettings();
}
}
}
}
if (mousePos.x >= scrollOffset + 175 && mousePos.y > 285 && mousePos.x <= scrollOffset + 281 && mousePos.y <= 310) {
_vm->_globals->_exitId = 300;
doneFlag = true;
}
if (mousePos.x >= scrollOffset + 355 && mousePos.y > 285 && mousePos.x <= scrollOffset + 490 && mousePos.y <= 310)
doneFlag = true;
if (mousePos.x >= scrollOffset + 300 && mousePos.y > 194 && mousePos.x <= scrollOffset + 358 && mousePos.y <= 219) {
switch (_vm->_graphicsMan->_scrollSpeed) {
case 1:
_vm->_graphicsMan->_scrollSpeed = 2;
break;
case 2:
_vm->_graphicsMan->_scrollSpeed = 4;
break;
case 4:
_vm->_graphicsMan->_scrollSpeed = 8;
break;
case 8:
_vm->_graphicsMan->_scrollSpeed = 16;
break;
case 16:
_vm->_graphicsMan->_scrollSpeed = 32;
break;
case 32:
_vm->_graphicsMan->_scrollSpeed = 48;
break;
case 48:
_vm->_graphicsMan->_scrollSpeed = 64;
break;
case 64:
_vm->_graphicsMan->_scrollSpeed = 128;
break;
case 128:
_vm->_graphicsMan->_scrollSpeed = 160;
break;
case 160:
_vm->_graphicsMan->_scrollSpeed = 320;
break;
case 320:
_vm->_graphicsMan->_scrollSpeed = 1;
break;
default:
break;
}
}
// Values are blocked, thus handling the zone is useless
//if (mousePos.x >= _vm->_graphicsManager->ofscroll + 348 && mousePos.y > 248 && mousePos.x <= _vm->_graphicsManager->ofscroll + 394 && mousePos.y <= 273)
// _vm->_globals->_speed = 2;
if ( mousePos.x < scrollOffset + 165 || mousePos.x > scrollOffset + 496
|| mousePos.y < 107 || mousePos.y > 318)
doneFlag = true;
}
if (_vm->_globals->_speed == 1)
_vm->_globals->_menuSpeed = 6;
else if (_vm->_globals->_speed == 2)
_vm->_globals->_menuSpeed = 5;
else if (_vm->_globals->_speed == 3)
_vm->_globals->_menuSpeed = 4;
_vm->_globals->_menuTextOff = !_vm->_soundMan->_textOffFl ? 7 : 8;
_vm->_globals->_menuVoiceOff = !_vm->_soundMan->_voiceOffFl ? 7 : 8;
_vm->_globals->_menuSoundOff = !_vm->_soundMan->_soundOffFl ? 7 : 8;
_vm->_globals->_menuMusicOff = !_vm->_soundMan->_musicOffFl ? 7 : 8;
_vm->_globals->_menuDisplayType = 9;
switch (_vm->_graphicsMan->_scrollSpeed) {
case 1:
_vm->_globals->_menuScrollSpeed = 12;
break;
case 2:
_vm->_globals->_menuScrollSpeed = 13;
break;
case 4:
_vm->_globals->_menuScrollSpeed = 14;
break;
case 8:
_vm->_globals->_menuScrollSpeed = 15;
break;
case 16:
_vm->_globals->_menuScrollSpeed = 16;
break;
case 32:
_vm->_globals->_menuScrollSpeed = 17;
break;
case 48:
_vm->_globals->_menuScrollSpeed = 18;
break;
case 64:
_vm->_globals->_menuScrollSpeed = 19;
break;
case 128:
_vm->_globals->_menuScrollSpeed = 20;
break;
case 160:
_vm->_globals->_menuScrollSpeed = 21;
break;
case 320:
_vm->_globals->_menuScrollSpeed = 22;
break;
case 640:
_vm->_globals->_menuScrollSpeed = 23;
break;
default:
break;
}
_vm->_events->refreshScreenAndEvents();
} while (!doneFlag);
_vm->_graphicsMan->copySurface(_vm->_graphicsMan->_backBuffer, scrollOffset + 164,
107, 335, 215, _vm->_graphicsMan->_frontBuffer, scrollOffset + 164, 107);
_vm->_graphicsMan->addDirtyRect(scrollOffset + 164, 107, scrollOffset + 498, 320);
_vm->_globals->_optionDialogSpr = _vm->_globals->freeMemory(_vm->_globals->_optionDialogSpr);
_vm->_globals->_optionDialogFl = false;
}
void DialogsManager::showInventory() {
if (_removeInventFl || _inventDisplayedFl || _vm->_globals->_disableInventFl)
return;
_vm->_graphicsMan->_scrollStatus = 1;
_vm->_objectsMan->_eraseVisibleCounter = 4;
_vm->_objectsMan->_visibleFl = false;
for (int i = 0; i <= 1; i++) {
inventAnim();
_vm->_events->getMouseX();
_vm->_events->getMouseY();
_vm->_events->refreshScreenAndEvents();
}
_inventWin1 = nullptr;
bool loopFl;
do {
loopFl = false;
_vm->_events->_curMouseButton = 0;
_vm->_events->_mouseButton = 0;
_vm->_globals->_disableInventFl = true;
_vm->_graphicsMan->setColorPercentage2(251, 100, 100, 100);
Common::Path filename;
if (_vm->getPlatform() == Common::kPlatformOS2 || _vm->getPlatform() == Common::kPlatformBeOS)
filename = "INVENT.SPR";
else {
switch (_vm->_globals->_language) {
case LANG_EN:
filename = "INVENTAN.SPR";
break;
case LANG_FR:
filename = "INVENTFR.SPR";
break;
case LANG_SP:
filename = "INVENTES.SPR";
break;
default:
break;
}
}
Common::File f;
if (!f.open(filename))
error("Error opening file - %s", filename.toString().c_str());
size_t filesize = f.size();
_inventWin1 = _vm->_globals->allocMemory(filesize);
_vm->_fileIO->readStream(f, _inventWin1, filesize);
f.close();
_inventBuf2 = _vm->_fileIO->loadFile("INVENT2.SPR");
_inventX = _vm->_graphicsMan->_scrollOffset + 152;
_inventY = 114;
_inventWidth = _vm->_objectsMan->getWidth(_inventWin1, 0);
_inventHeight = _vm->_objectsMan->getHeight(_inventWin1, 0);
_vm->_graphicsMan->drawCompressedSprite(_vm->_graphicsMan->_frontBuffer, _inventWin1, _inventX + 300, 414, 0, 0, 0, false);
int curPosY = 0;
int inventCount = 0;
for (int inventLine = 1; inventLine <= 5; inventLine++) {
int curPosX = 0;
for (int inventCol = 1; inventCol <= 6; inventCol++) {
++inventCount;
int inventIdx = _vm->_globals->_inventory[inventCount];
// The last two zones are not reserved for the inventory: Options and Save/Load
if (inventIdx && inventCount <= 29) {
byte *obj = _vm->_objectsMan->loadObjectFromFile(inventIdx, false);
_vm->_graphicsMan->restoreSurfaceRect(_vm->_graphicsMan->_frontBuffer, obj, _inventX + curPosX + 6,
curPosY + 120, _vm->_objectsMan->getObjectWidth(), _vm->_objectsMan->getObjectHeight());
_vm->_globals->freeMemory(obj);
}
curPosX += 54;
};
curPosY += 38;
}
_vm->_graphicsMan->copySurfaceRect(_vm->_graphicsMan->_frontBuffer, _inventWin1, _inventX, _inventY, _inventWidth, _inventHeight);
_vm->_events->_curMouseButton = 0;
int newInventoryItem = 0;
// Main loop to select an inventory item
while (!_vm->shouldQuit()) {
// Turn on drawing the inventory dialog in the event manager
_inventDisplayedFl = true;
int mousePosX = _vm->_events->getMouseX();
int mousePosY = _vm->_events->getMouseY();
int mouseButton = _vm->_events->getMouseButton();
int oldInventoryItem = newInventoryItem;
newInventoryItem = _vm->_linesMan->checkInventoryHotspots(mousePosX, mousePosY);
if (newInventoryItem != oldInventoryItem)
_vm->_objectsMan->initBorder(newInventoryItem);
int cursorId = _vm->_events->_mouseCursorId;
if (cursorId != 1 && cursorId != 2 && cursorId != 3 && cursorId != 16) {
if (mouseButton == 2) {
_vm->_objectsMan->nextObjectIcon(newInventoryItem);
cursorId = _vm->_events->_mouseCursorId;
if (cursorId != 23)
_vm->_events->changeMouseCursor(cursorId);
}
}
cursorId = _vm->_events->_mouseCursorId;
if (mouseButton == 1) {
if (cursorId == 1 || cursorId == 2 || cursorId == 3 || cursorId == 16 || !cursorId)
break;
_vm->_objectsMan->takeInventoryObject(_vm->_globals->_inventory[newInventoryItem]);
if (_vm->_events->_mouseCursorId == 8)
break;
_vm->_script->_tempObjectFl = true;
_vm->_globals->_saveData->_data[svLastObjectIndex] = _vm->_objectsMan->_curObjectIndex;
_vm->_globals->_saveData->_data[svLastInventoryItem] = _vm->_globals->_inventory[newInventoryItem];
_vm->_globals->_saveData->_data[svLastInvMouseCursor] = _vm->_events->_mouseCursorId;
_vm->_objectsMan->loadObjectIniFile();
_vm->_script->_tempObjectFl = false;
if (_vm->_soundMan->_voiceOffFl) {
do {
_vm->_events->refreshScreenAndEvents();
} while (!_vm->_globals->_exitId && _vm->_events->getMouseButton() != 1);
_vm->_fontMan->hideText(9);
}
if (_vm->_globals->_exitId) {
if (_vm->_globals->_exitId == 2) {
_vm->_globals->_exitId = 0;
break;
}
_vm->_globals->_exitId = 0;
_inventBuf2 = _vm->_globals->freeMemory(_inventBuf2);
_inventWin1 = _vm->_globals->freeMemory(_inventWin1);
loopFl = true;
break;
} else
_inventDisplayedFl = true;
}
if (_removeInventFl)
break;
_vm->_events->refreshScreenAndEvents();
if (_vm->_globals->_screenId >= 35 && _vm->_globals->_screenId <= 40)
_vm->_objectsMan->handleSpecialGames();
}
} while (loopFl);
_vm->_fontMan->hideText(9);
if (_inventDisplayedFl) {
_inventDisplayedFl = false;
_vm->_graphicsMan->copySurface(_vm->_graphicsMan->_backBuffer, _inventX, 114, _inventWidth, _inventHeight, _vm->_graphicsMan->_frontBuffer, _inventX, 114);
_vm->_graphicsMan->addDirtyRect(_inventX, 114, _inventX + _inventWidth, _inventWidth + 114);
_vm->_objectsMan->_refreshBobMode10Fl = true;
}
_inventWin1 = _vm->_globals->freeMemory(_inventWin1);
_inventBuf2 = _vm->_globals->freeMemory(_inventBuf2);
int cursorId = _vm->_events->_mouseCursorId;
if (cursorId == 1)
showOptionsDialog();
else if (cursorId == 3)
showLoadGame();
else if (cursorId == 2)
showSaveGame();
_vm->_events->_mouseCursorId = 4;
_vm->_events->changeMouseCursor(4);
_vm->_objectsMan->_oldBorderPos = Common::Point(0, 0);
_vm->_objectsMan->_borderPos = Common::Point(0, 0);
_vm->_globals->_disableInventFl = false;
_vm->_graphicsMan->_scrollStatus = 0;
}
/**
* Inventory Animations
*/
void DialogsManager::inventAnim() {
if (_vm->_globals->_disableInventFl)
return;
if (_vm->_objectsMan->_eraseVisibleCounter && !_vm->_objectsMan->_visibleFl) {
_vm->_graphicsMan->copySurface(_vm->_graphicsMan->_backBuffer, _oldInventX, 27, 48, 38,
_vm->_graphicsMan->_frontBuffer, _oldInventX, 27);
_vm->_graphicsMan->addDirtyRect(_oldInventX, 27, _oldInventX + 48, 65);
--_vm->_objectsMan->_eraseVisibleCounter;
}
if (_vm->_objectsMan->_visibleFl) {
if (_oldInventX <= 1)
_oldInventX = 2;
_vm->_graphicsMan->copySurface(_vm->_graphicsMan->_backBuffer, _oldInventX, 27, 48, 38,
_vm->_graphicsMan->_frontBuffer, _oldInventX, 27);
_vm->_graphicsMan->addDirtyRect(_oldInventX, 27, _oldInventX + 48, 65);
int newOffset = _vm->_graphicsMan->_scrollOffset + 2;
_vm->_graphicsMan->drawVesaSprite(_vm->_graphicsMan->_frontBuffer, _inventoryIcons, newOffset + 300, 327, 0);
_vm->_graphicsMan->addDirtyRect(newOffset, 27, newOffset + 45, 62);
_oldInventX = newOffset;
}
if (_vm->_globals->_saveData->_data[svField357] == 1) {
if (_vm->_globals->_saveData->_data[svField353] == 1)
_vm->_graphicsMan->drawCompressedSprite(_vm->_graphicsMan->_frontBuffer, _vm->_objectsMan->_headSprites, 832, 325, 0, 0, 0, false);
if (_vm->_globals->_saveData->_data[svField355] == 1)
_vm->_graphicsMan->drawCompressedSprite(_vm->_graphicsMan->_frontBuffer, _vm->_objectsMan->_headSprites, 866, 325, 1, 0, 0, false);
_vm->_graphicsMan->addDirtyRect(532, 25, 560, 60);
_vm->_graphicsMan->addDirtyRect(566, 25, 594, 60);
}
if (_vm->_globals->_saveData->_data[svField356] == 1) {
_vm->_graphicsMan->drawCompressedSprite(_vm->_graphicsMan->_frontBuffer, _vm->_objectsMan->_headSprites, 832, 325, 0, 0, 0, false);
_vm->_graphicsMan->addDirtyRect(532, 25, 560, 60);
}
if (_vm->_globals->_saveData->_data[svField354] == 1) {
_vm->_graphicsMan->drawCompressedSprite(_vm->_graphicsMan->_frontBuffer, _vm->_objectsMan->_headSprites, 832, 325, 0, 0, 0, false);
_vm->_graphicsMan->addDirtyRect(532, 25, 560, 60);
}
}
/**
* Test dialog opening
*/
void DialogsManager::testDialogOpening() {
if (_vm->_globals->_cityMapEnabledFl)
_vm->_events->_gameKey = KEY_NONE;
if ((_vm->_events->_gameKey == KEY_NONE) || _inventFl)
return;
DIALOG_KEY key = _vm->_events->_gameKey;
_vm->_events->_gameKey = KEY_NONE;
_inventFl = true;
switch (key) {
case KEY_INVENTORY:
showInventory();
break;
case KEY_OPTIONS:
_vm->_graphicsMan->_scrollStatus = 1;
showOptionsDialog();
_vm->_graphicsMan->_scrollStatus = 0;
break;
case KEY_LOAD:
_vm->_graphicsMan->_scrollStatus = 1;
showLoadGame();
_vm->_graphicsMan->_scrollStatus = 0;
break;
case KEY_SAVE:
_vm->_graphicsMan->_scrollStatus = 1;
showSaveGame();
_vm->_graphicsMan->_scrollStatus = 0;
break;
default:
break;
}
_inventFl = false;
_vm->_events->_gameKey = KEY_NONE;
}
/**
* Load Game dialog
*/
void DialogsManager::showLoadGame() {
_vm->_events->refreshScreenAndEvents();
showSaveLoad(MODE_LOAD);
int slotNumber;
do {
slotNumber = searchSavegames();
_vm->_events->refreshScreenAndEvents();
} while (!_vm->shouldQuit() && (!slotNumber || _vm->_events->getMouseButton() != 1));
_vm->_objectsMan->_saveLoadFl = false;
int16 startPosX = _vm->_events->_startPos.x + 183;
_vm->_graphicsMan->copySurface(_vm->_graphicsMan->_backBuffer, startPosX, 60, 274, 353, _vm->_graphicsMan->_frontBuffer, startPosX, 60);
_vm->_graphicsMan->addDirtyRect(startPosX, 60, startPosX + 274, 413);
_vm->_objectsMan->_refreshBobMode10Fl = true;
_vm->_objectsMan->_saveLoadSprite = _vm->_globals->freeMemory(_vm->_objectsMan->_saveLoadSprite);
_vm->_objectsMan->_saveLoadSprite2 = _vm->_globals->freeMemory(_vm->_objectsMan->_saveLoadSprite2);
_vm->_objectsMan->_saveLoadX = 0;
_vm->_objectsMan->_saveLoadY = 0;
if (slotNumber != 7) {
_vm->_saveLoad->loadGame(slotNumber);
}
_vm->_objectsMan->changeObject(14);
}
/**
* Save Game dialog
*/
void DialogsManager::showSaveGame() {
_vm->_events->refreshScreenAndEvents();
showSaveLoad(MODE_SAVE);
int slotNumber;
do {
slotNumber = searchSavegames();
_vm->_events->refreshScreenAndEvents();
} while (!_vm->shouldQuit() && (!slotNumber || _vm->_events->getMouseButton() != 1));
_vm->_objectsMan->_saveLoadFl = false;
int16 startPosX = _vm->_events->_startPos.x + 183;
_vm->_graphicsMan->copySurface(_vm->_graphicsMan->_backBuffer, startPosX, 60, 274, 353, _vm->_graphicsMan->_frontBuffer, startPosX, 60);
_vm->_graphicsMan->addDirtyRect(startPosX, 60, startPosX + 274, 413);
_vm->_objectsMan->_refreshBobMode10Fl = true;
_vm->_objectsMan->_saveLoadSprite = _vm->_globals->freeMemory(_vm->_objectsMan->_saveLoadSprite);
_vm->_objectsMan->_saveLoadSprite2 = _vm->_globals->freeMemory(_vm->_objectsMan->_saveLoadSprite2);
_vm->_objectsMan->_saveLoadX = 0;
_vm->_objectsMan->_saveLoadY = 0;
if (slotNumber != 7) {
// Since the original GUI doesn't support save names, use a default name
Common::String saveName = Common::String::format("Save #%d", slotNumber);
_vm->_events->refreshScreenAndEvents();
// Save the game
_vm->_saveLoad->saveGame(slotNumber, saveName);
}
}
/**
* Load/Save dialog
*/
void DialogsManager::showSaveLoad(SaveLoadMode mode) {
Common::Path filename;
if (_vm->getPlatform() == Common::kPlatformOS2 || _vm->getPlatform() == Common::kPlatformBeOS)
filename = "SAVE.SPR";
else {
switch (_vm->_globals->_language) {
case LANG_EN:
filename = "SAVEAN.SPR";
break;
case LANG_FR:
filename = "SAVEFR.SPR";
break;
case LANG_SP:
filename = "SAVEES.SPR";
break;
default:
break;
}
}
_vm->_objectsMan->_saveLoadSprite = _vm->_objectsMan->loadSprite(filename);
_vm->_objectsMan->_saveLoadSprite2 = _vm->_objectsMan->loadSprite("SAVE2.SPR");
int16 startPosX = _vm->_events->_startPos.x;
_vm->_graphicsMan->drawVesaSprite(_vm->_graphicsMan->_frontBuffer, _vm->_objectsMan->_saveLoadSprite, startPosX + 483, 360, 0);
if (_vm->_globals->_language == LANG_FR) {
if (mode == MODE_SAVE)
_vm->_graphicsMan->drawVesaSprite(_vm->_graphicsMan->_frontBuffer, _vm->_objectsMan->_saveLoadSprite, startPosX + 525, 375, 1);
else if (mode == MODE_LOAD)
_vm->_graphicsMan->drawVesaSprite(_vm->_graphicsMan->_frontBuffer, _vm->_objectsMan->_saveLoadSprite, startPosX + 515, 375, 2);
} else {
if (mode == MODE_SAVE)
_vm->_graphicsMan->drawVesaSprite(_vm->_graphicsMan->_frontBuffer, _vm->_objectsMan->_saveLoadSprite, startPosX + 535, 372, 1);
else if (mode == MODE_LOAD)
_vm->_graphicsMan->drawVesaSprite(_vm->_graphicsMan->_frontBuffer, _vm->_objectsMan->_saveLoadSprite, startPosX + 539, 372, 2);
}
for (int slotNumber = 1; slotNumber <= 6; ++slotNumber) {
hopkinsSavegameHeader header;
if (_vm->_saveLoad->readSavegameHeader(slotNumber, header, false)) {
Graphics::Surface thumb8;
_vm->_saveLoad->convertThumb16To8(header._thumbnail, &thumb8);
byte *thumb = (byte *)thumb8.getPixels();
int16 startPosX_ = _vm->_events->_startPos.x;
switch (slotNumber) {
case 1:
_vm->_graphicsMan->restoreSurfaceRect(_vm->_graphicsMan->_frontBuffer, thumb, startPosX_ + 190, 112, 128, 87);
break;
case 2:
_vm->_graphicsMan->restoreSurfaceRect(_vm->_graphicsMan->_frontBuffer, thumb, startPosX_ + 323, 112, 128, 87);
break;
case 3:
_vm->_graphicsMan->restoreSurfaceRect(_vm->_graphicsMan->_frontBuffer, thumb, startPosX_ + 190, 203, 128, 87);
break;
case 4:
_vm->_graphicsMan->restoreSurfaceRect(_vm->_graphicsMan->_frontBuffer, thumb, startPosX_ + 323, 203, 128, 87);
break;
case 5:
_vm->_graphicsMan->restoreSurfaceRect(_vm->_graphicsMan->_frontBuffer, thumb, startPosX_ + 190, 294, 128, 87);
break;
case 6:
_vm->_graphicsMan->restoreSurfaceRect(_vm->_graphicsMan->_frontBuffer, thumb, startPosX_ + 323, 294, 128, 87);
break;
default:
break;
}
thumb8.free();
header._thumbnail->free();
delete header._thumbnail;
}
}
_vm->_graphicsMan->copySurfaceRect(_vm->_graphicsMan->_frontBuffer, _vm->_objectsMan->_saveLoadSprite, _vm->_events->_startPos.x + 183, 60, 274, 353);
_vm->_objectsMan->_saveLoadFl = true;
_vm->_objectsMan->_saveLoadX = 0;
_vm->_objectsMan->_saveLoadY = 0;
}
/**
* Search savegames
*/
int DialogsManager::searchSavegames() {
int xp = _vm->_events->getMouseX();
int yp = _vm->_events->getMouseY();
int16 startPosX = _vm->_graphicsMan->_scrollOffset = _vm->_events->_startPos.x;
int slotNumber = 0;
if (yp >= 112 && yp <= 198) {
if (xp > startPosX + 189 && xp < startPosX + 318) {
slotNumber = 1;
_vm->_objectsMan->_saveLoadX = 189;
_vm->_objectsMan->_saveLoadY = 111;
} else if (xp > startPosX + 322 && xp < startPosX + 452) {
slotNumber = 2;
_vm->_objectsMan->_saveLoadX = 322;
_vm->_objectsMan->_saveLoadY = 111;
}
} else if (yp >= 203 && yp <= 289) {
if (xp > startPosX + 189 && xp < startPosX + 318) {
slotNumber = 3;
_vm->_objectsMan->_saveLoadX = 189;
_vm->_objectsMan->_saveLoadY = 202;
} else if (xp > startPosX + 322 && xp < startPosX + 452) {
slotNumber = 4;
_vm->_objectsMan->_saveLoadX = 322;
_vm->_objectsMan->_saveLoadY = 202;
}
} else if (yp >= 294 && yp <= 380) {
if (xp > startPosX + 189 && xp < startPosX + 318) {
slotNumber = 5;
_vm->_objectsMan->_saveLoadX = 189;
_vm->_objectsMan->_saveLoadY = 293;
} else if (xp > startPosX + 322 && xp < startPosX + 452) {
slotNumber = 6;
_vm->_objectsMan->_saveLoadX = 322;
_vm->_objectsMan->_saveLoadY = 293;
}
} else if (yp >= 388 && yp <= 404 && xp > startPosX + 273 && xp < startPosX + 355) {
slotNumber = 7;
_vm->_objectsMan->_saveLoadX = 0;
_vm->_objectsMan->_saveLoadY = 0;
} else {
slotNumber = 0;
_vm->_objectsMan->_saveLoadX = 0;
_vm->_objectsMan->_saveLoadY = 0;
}
return slotNumber;
}
} // End of namespace Hopkins

76
engines/hopkins/dialogs.h Normal file
View File

@@ -0,0 +1,76 @@
/* 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 HOPKINS_DIALOGS_H
#define HOPKINS_DIALOGS_H
#include "common/scummsys.h"
#include "common/system.h"
#include "common/error.h"
#include "common/rect.h"
namespace Hopkins {
class HopkinsEngine;
enum SaveLoadMode { MODE_SAVE = 1, MODE_LOAD = 2 };
/**
* Class for manging game dialogs
*/
class DialogsManager {
private:
byte *_inventWin1;
byte *_inventBuf2;
byte *_inventoryIcons;
bool _inventDisplayedFl;
bool _removeInventFl;
int _inventX, _inventY;
int _inventWidth, _inventHeight;
int _oldInventX;
HopkinsEngine *_vm;
void showSaveLoad(SaveLoadMode mode);
int searchSavegames();
public:
bool _inventFl;
DialogsManager(HopkinsEngine *vm);
~DialogsManager();
void inventAnim();
void showInventory();
void showLoadGame();
void showSaveGame();
void showOptionsDialog();
void testDialogOpening();
void clearAll();
void drawInvent(Common::Point oldBorder, int oldBorderSpriteIndex, Common::Point newBorder, int newBorderSpriteIndex);
void loadIcons();
void disableInvent() { _removeInventFl = true; }
void enableInvent() { _removeInventFl = false; }
};
} // End of namespace Hopkins
#endif /* HOPKINS_MENU_H */

542
engines/hopkins/events.cpp Normal file
View File

@@ -0,0 +1,542 @@
/* 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 "hopkins/events.h"
#include "hopkins/files.h"
#include "hopkins/globals.h"
#include "hopkins/hopkins.h"
#include "hopkins/sound.h"
#include "common/system.h"
#include "common/textconsole.h"
#include "graphics/cursorman.h"
#include "backends/keymapper/keymapper.h"
namespace Hopkins {
EventsManager::EventsManager(HopkinsEngine *vm) {
_vm = vm;
_mouseFl = false;
_mouseLinuxFl = false;
_mouseSizeX = _mouseSizeY = 0;
_mouseOffset.x = _mouseOffset.y = 0;
_startPos.x = _startPos.y = 0;
_breakoutFl = false;
_mouseSpriteId = 0;
_curMouseButton = 0;
_mouseButton = 0;
_mouseCursor = nullptr;
_gameCounter = 0;
_rateCounter = 0;
_escKeyFl = false;
_gameKey = KEY_NONE;
_mouseCursorId = 0;
_oldIconId = 0;
_objectBuf = nullptr;
Common::fill(&_keyState[0], &_keyState[256], false);
_priorCounterTime = _priorFrameTime = g_system->getMillis();
}
EventsManager::~EventsManager() {
_vm->_globals->freeMemory(_objectBuf);
_vm->_globals->freeMemory(_mouseCursor);
}
void EventsManager::clearAll() {
_vm->_globals->freeMemory(_objectBuf);
_objectBuf = _vm->_globals->allocMemory(2500);
}
void EventsManager::initMouseData() {
if (_vm->getPlatform() == Common::kPlatformLinux)
_mouseLinuxFl = true;
else
_mouseLinuxFl = false;
if (_mouseLinuxFl) {
_mouseSizeX = 52;
_mouseSizeY = 32;
} else {
_mouseSizeX = 34;
_mouseSizeY = 20;
}
switch (_vm->_globals->_language) {
case LANG_EN:
if (!_mouseLinuxFl)
_mouseCursor = _vm->_fileIO->loadFile("SOUAN.SPR");
else
_mouseCursor = _vm->_fileIO->loadFile("LSOUAN.SPR");
break;
case LANG_FR:
if (!_mouseLinuxFl)
_mouseCursor = _vm->_fileIO->loadFile("SOUFR.SPR");
else
_mouseCursor = _vm->_fileIO->loadFile("LSOUFR.SPR");
break;
case LANG_SP:
_mouseCursor = _vm->_fileIO->loadFile("SOUES.SPR");
break;
default:
break;
}
}
// Mouse On
void EventsManager::setMouseOn() {
_mouseFl = true;
if (_mouseLinuxFl) {
_mouseSizeX = 52;
_mouseSizeY = 32;
} else {
_mouseSizeX = 34;
_mouseSizeY = 20;
}
_mouseOffset.x = 0;
_mouseOffset.y = 0;
if (!_breakoutFl)
setMouseXY(300, 200);
else
setMouseXY(150, 100);
}
/**
* Set Mouse position
*/
void EventsManager::setMouseXY(Common::Point pos) {
g_system->warpMouse(pos.x, pos.y);
}
/**
* Set Mouse position
*/
void EventsManager::setMouseXY(int xp, int yp) {
g_system->warpMouse(xp, yp);
}
/**
* Get Mouse X
*/
int EventsManager::getMouseX() {
_mousePos.x = _startPos.x + g_system->getEventManager()->getMousePos().x;
_mousePos.y = g_system->getEventManager()->getMousePos().y;
return _mousePos.x + _mouseOffset.x;
}
/**
* Get Mouse Y
*/
int EventsManager::getMouseY() {
_mousePos.x = _startPos.x + g_system->getEventManager()->getMousePos().x;
_mousePos.y = g_system->getEventManager()->getMousePos().y;
return _mousePos.y + _mouseOffset.y;
}
/**
* Get Mouse Button
*/
int EventsManager::getMouseButton() {
refreshEvents();
return _curMouseButton;
}
/**
* Mouse Off
*/
void EventsManager::mouseOff() {
_mouseFl = false;
CursorMan.showMouse(false);
}
/**
* Mouse On
*/
void EventsManager::mouseOn() {
setMouseOn();
_mouseFl = true;
CursorMan.showMouse(true);
}
/**
* Change Mouse Cursor
*/
void EventsManager::changeMouseCursor(int id) {
int cursorId = id;
if (_mouseCursorId == 23)
return;
if (id == 4 && _mouseCursorId == 4 && _vm->_globals->_freezeCharacterFl)
cursorId = 0;
if (cursorId == 25)
cursorId = 5;
if (_oldIconId != cursorId || !cursorId) {
_oldIconId = cursorId;
_mouseSpriteId = cursorId;
updateCursor();
}
}
/**
* Check Events
*/
void EventsManager::refreshEvents() {
_vm->_soundMan->checkSounds();
pollEvents();
}
void EventsManager::checkForNextFrameCounter() {
int32 delayAmount = 10 - (g_system->getMillis() - _priorCounterTime);
if (delayAmount > 0)
_vm->_system->delayMillis(delayAmount);
// Check for whether to increment the game counter
uint32 milli = g_system->getMillis();
while ((milli - _priorCounterTime) >= 10) {
_priorCounterTime += 10;
_rateCounter += 3;
}
// Check for next game frame
if ((milli - _priorFrameTime) >= GAME_FRAME_TIME) {
++_gameCounter;
_priorFrameTime = milli;
_vm->_graphicsMan->updateScreen();
}
}
void EventsManager::delay(int totalMilli) {
uint32 delayEnd = g_system->getMillis() + totalMilli;
while (!_vm->shouldQuit() && g_system->getMillis() < delayEnd) {
g_system->delayMillis(10);
}
}
void EventsManager::pollEvents() {
checkForNextFrameCounter();
Common::Event event;
while (g_system->getEventManager()->pollEvent(event)) {
// Handle keypress
switch (event.type) {
case Common::EVENT_QUIT:
case Common::EVENT_RETURN_TO_LAUNCHER:
return;
case Common::EVENT_CUSTOM_ENGINE_ACTION_START:
handleKey(event);
return;
case Common::EVENT_KEYDOWN:
_keyState[(byte)toupper(event.kbd.ascii)] = true;
return;
case Common::EVENT_KEYUP:
_keyState[(byte)toupper(event.kbd.ascii)] = false;
return;
case Common::EVENT_LBUTTONDOWN:
_mouseButton = 1;
return;
case Common::EVENT_RBUTTONDOWN:
_mouseButton = 2;
return;
case Common::EVENT_LBUTTONUP:
case Common::EVENT_RBUTTONUP:
_mouseButton = 0;
return;
default:
break;
}
}
for (char chr = 'A'; chr <= 'Z'; chr++)
_keyState[(byte)chr] = false;
for (char chr = '0'; chr <= '9'; chr++)
_keyState[(byte)chr] = false;
}
void EventsManager::handleKey(const Common::Event &event) {
_escKeyFl = (event.customType == kActionEscape);
if (event.customType == kActionInventory)
_gameKey = KEY_INVENTORY;
else if (event.customType == kActionSave)
_gameKey = KEY_SAVE;
else if (event.customType == kActionLoad)
_gameKey = KEY_LOAD;
else if (event.customType == kActionOptions)
_gameKey = KEY_OPTIONS;
}
/**
* Waits for a keypress, ignoring mouse events
* @return Keypress, or -1 if game quit was requested
*/
int EventsManager::waitKeyPress() {
Common::Keymapper *keymapper = _vm->getEventManager()->getKeymapper();
keymapper->getKeymap("game-shortcuts")->setEnabled(false);
char foundChar = '\0';
while (!foundChar) {
if (_vm->shouldQuit())
return -1;
for (char ch = 'A'; ch <= 'Z'; ++ch) {
if (_keyState[(byte)ch]) {
foundChar = ch;
break;
}
}
for (char ch = '0'; ch <= '9'; ++ch) {
if (_keyState[(byte)ch]) {
foundChar = ch;
break;
}
}
if (_keyState[(byte)'.'])
foundChar = '.';
else if (_keyState[8])
// BACKSPACE
foundChar = 8;
else if (_keyState[13])
// ENTER
foundChar = 13;
else if (_keyState[(byte)' '])
foundChar = ' ';
refreshScreenAndEvents();
}
// Wait for keypress release
while (_keyState[(byte)foundChar] && !_vm->shouldQuit()) {
refreshScreenAndEvents();
g_system->delayMillis(10);
}
keymapper->getKeymap("game-shortcuts")->setEnabled(true);
// Return character
return foundChar;
}
void EventsManager::refreshScreenAndEvents() {
int bottom = 0;
int right = 0;
int height = 0;
int width = 0;
int xp = 0;
int yp = 0;
if (_mouseFl) {
int mouseWidth = 20;
if (!_mouseLinuxFl)
mouseWidth = 10;
int mouseHeight = 20;
if (!_mouseLinuxFl)
mouseHeight = 15;
xp = _mousePos.x - mouseWidth;
yp = _mousePos.y;
width = _mouseSizeX;
height = _mouseSizeY;
if (_mouseCursorId == 23) {
width = _vm->_objectsMan->getObjectWidth();
height = _vm->_objectsMan->getObjectHeight();
} else {
if (_breakoutFl) {
if (xp < _vm->_graphicsMan->_minX)
xp = _vm->_graphicsMan->_minX;
if (_mousePos.y < _vm->_graphicsMan->_minY)
yp = _vm->_graphicsMan->_minY;
if (_mouseSizeX + xp >= _vm->_graphicsMan->_maxX)
width = _mouseSizeX - (_mouseSizeX + xp - _vm->_graphicsMan->_maxX);
if (yp + _mouseSizeY >= _vm->_graphicsMan->_maxY)
height = _vm->_graphicsMan->_maxY - yp;
} else {
if (xp < _vm->_graphicsMan->_minX)
xp = _vm->_graphicsMan->_minX - mouseWidth;
mouseHeight = (int16)mouseHeight;
if (_mousePos.y < _vm->_graphicsMan->_minY - mouseHeight)
yp = _vm->_graphicsMan->_minY - mouseHeight;
if (_mouseSizeX + xp >= _vm->_graphicsMan->_maxX)
width = _mouseSizeX - (_mouseSizeX + xp - _vm->_graphicsMan->_maxX - mouseWidth);
if (yp + _mouseSizeY >= mouseHeight + _vm->_graphicsMan->_maxY)
height = _vm->_graphicsMan->_maxY - mouseHeight - yp;
}
right = xp + width;
bottom = yp + height;
}
}
if (!_vm->_globals->_linuxEndDemoFl)
_vm->_objectsMan->displaySprite();
if (!_mouseFl) {
updateCursor();
} else if (_mouseCursorId == 23) {
if (yp < _vm->_graphicsMan->_maxY && xp < _vm->_graphicsMan->_maxX) {
if (width + xp > _vm->_graphicsMan->_maxX)
width = _vm->_graphicsMan->_maxX - xp;
if (yp + height > _vm->_graphicsMan->_maxY)
height = _vm->_graphicsMan->_maxY - yp;
if (width > 1 && height > 1) {
updateCursor();
}
}
} else if (yp < _vm->_graphicsMan->_maxY && xp < _vm->_graphicsMan->_maxX && width > 1 && height > 1) {
updateCursor();
_vm->_graphicsMan->addDirtyRect(xp, yp, right, bottom);
}
_vm->_globals->_speed = 2;
bool externalLoopFl = false;
do {
while (!_vm->shouldQuit()) {
checkForNextFrameCounter();
bool innerLoopFl = false;
while (!_vm->shouldQuit() && (_breakoutFl || _vm->_globals->_eventMode != EVENTMODE_IGNORE)) {
checkForNextFrameCounter();
if (!_breakoutFl) {
innerLoopFl = true;
break;
}
if (_rateCounter > 1) {
externalLoopFl = true;
break;
}
}
if (innerLoopFl || _vm->_globals->_speed != 2)
break;
if (externalLoopFl ||_rateCounter > 9) {
externalLoopFl = true;
break;
}
}
if (externalLoopFl)
break;
} while (!_vm->shouldQuit() && _vm->_globals->_eventMode == EVENTMODE_CREDITS && _rateCounter <= 15);
_vm->_globals->_speed = 2;
_rateCounter = 0;
if (!_vm->_graphicsMan->_largeScreenFl || _vm->_graphicsMan->_scrollStatus == 1) {
_vm->_graphicsMan->displayDirtyRects();
} else {
if (_vm->_graphicsMan->_scrollStatus != 2) {
if (getMouseX() > _vm->_graphicsMan->_scrollPosX + 620)
_vm->_graphicsMan->_scrollPosX += _vm->_graphicsMan->_scrollSpeed;
if (getMouseX() < _vm->_graphicsMan->_scrollPosX + 10)
_vm->_graphicsMan->_scrollPosX -= _vm->_graphicsMan->_scrollSpeed;
}
_vm->_graphicsMan->_scrollPosX = CLIP(_vm->_graphicsMan->_scrollPosX, 0, SCREEN_WIDTH);
if (_vm->_graphicsMan->_oldScrollPosX == _vm->_graphicsMan->_scrollPosX) {
_vm->_graphicsMan->displayDirtyRects();
} else {
_vm->_fontMan->hideText(9);
_vm->_graphicsMan->display8BitRect(_vm->_graphicsMan->_frontBuffer, _vm->_graphicsMan->_scrollPosX, 20, SCREEN_WIDTH, 440, 0, 20);
_vm->_graphicsMan->resetRefreshRects();
_vm->_graphicsMan->addRefreshRect(0, 20, SCREEN_WIDTH, SCREEN_HEIGHT - 20);
_vm->_graphicsMan->resetDirtyRects();
_startPos.x = _vm->_graphicsMan->_scrollPosX;
_vm->_graphicsMan->_scrollOffset = _vm->_graphicsMan->_scrollPosX;
}
_vm->_graphicsMan->_oldScrollPosX = _vm->_graphicsMan->_scrollPosX;
_startPos.x = _vm->_graphicsMan->_scrollPosX;
_vm->_graphicsMan->_scrollOffset = _vm->_graphicsMan->_scrollPosX;
}
_curMouseButton = _mouseButton;
_mouseButton = 0;
_vm->_soundMan->checkSoundEnd();
refreshEvents();
}
void EventsManager::updateCursor() {
// Backup the current sprite clipping bounds and reset them
Common::Rect clipBounds(_vm->_graphicsMan->_minX, _vm->_graphicsMan->_minY,
_vm->_graphicsMan->_maxX, _vm->_graphicsMan->_maxY);
_vm->_graphicsMan->_minX = _vm->_graphicsMan->_minY = 0;
_vm->_graphicsMan->_maxX = _vm->_objectsMan->getObjectWidth();
_vm->_graphicsMan->_maxY = _vm->_objectsMan->getObjectHeight();
int pitch = _vm->_graphicsMan->_lineNbr2;
_vm->_graphicsMan->_lineNbr2 = _vm->_objectsMan->getObjectWidth();
// Create the temporary cursor surface
byte *cursorSurface = new byte[_vm->_objectsMan->getObjectHeight() * _vm->_objectsMan->getObjectWidth()];
Common::fill(cursorSurface, cursorSurface + _vm->_objectsMan->getObjectHeight() * _vm->_objectsMan->getObjectWidth(), 0);
if (_mouseCursorId != 23) {
// Draw standard cursor
_vm->_graphicsMan->drawVesaSprite(cursorSurface, _mouseCursor, 300, 300, _mouseSpriteId);
} else {
// Draw the active inventory object
_vm->_graphicsMan->drawCompressedSprite(cursorSurface, _objectBuf, 300, 300, 0, 0, 0, false);
}
// Reset the clipping bounds
_vm->_graphicsMan->_minX = clipBounds.left;
_vm->_graphicsMan->_minY = clipBounds.top;
_vm->_graphicsMan->_maxX = clipBounds.right;
_vm->_graphicsMan->_maxY = clipBounds.bottom;
_vm->_graphicsMan->_lineNbr2 = pitch;
// Create a cursor palette
Graphics::PixelFormat pixelFormat = g_system->getScreenFormat();
byte *cursorPalette = new byte[3 * PALETTE_SIZE];
uint16 *paletteColors = (uint16 *)_vm->_graphicsMan->_palettePixels;
for (int i = 0; i < PALETTE_SIZE; i++) {
uint8 r, g, b;
pixelFormat.colorToRGB(READ_LE_UINT16(&paletteColors[i]), r, g, b);
cursorPalette[3 * i] = r;
cursorPalette[3 * i + 1] = g;
cursorPalette[3 * i + 2] = b;
}
// Calculate the X offset within the pointer image to the actual cursor data
int xOffset = !_mouseLinuxFl ? 10 : 20;
// Set the ScummVM cursor from the surface
CursorMan.replaceCursorPalette(cursorPalette, 0, PALETTE_SIZE - 1);
CursorMan.replaceCursor(cursorSurface, _vm->_objectsMan->getObjectWidth(), _vm->_objectsMan->getObjectHeight(),
xOffset, 0, 0, false);
// Delete the cursor surface and palette
delete[] cursorPalette;
delete[] cursorSurface;
}
} // End of namespace Hopkins

93
engines/hopkins/events.h Normal file
View File

@@ -0,0 +1,93 @@
/* 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 HOPKINS_EVENTS_H
#define HOPKINS_EVENTS_H
#include "common/scummsys.h"
#include "common/events.h"
#include "common/str.h"
namespace Hopkins {
#define GAME_FRAME_RATE 50
#define GAME_FRAME_TIME (1000 / GAME_FRAME_RATE)
class HopkinsEngine;
enum DIALOG_KEY { KEY_NONE = 0, KEY_INVENTORY = 1, KEY_OPTIONS = 2, KEY_SAVE = 3, KEY_LOAD = 4 };
class EventsManager {
private:
int _oldIconId;
uint32 _priorCounterTime;
uint32 _priorFrameTime;
bool _keyState[256];
bool _mouseLinuxFl;
int _mouseSizeX, _mouseSizeY;
HopkinsEngine *_vm;
void pollEvents();
void handleKey(const Common::Event &event);
void checkForNextFrameCounter();
void updateCursor();
public:
DIALOG_KEY _gameKey;
uint32 _rateCounter;
uint32 _gameCounter;
bool _escKeyFl;
bool _mouseFl;
bool _breakoutFl;
Common::Point _startPos;
Common::Point _mousePos;
Common::Point _mouseOffset;
int _mouseSpriteId;
int _curMouseButton;
int _mouseButton;
int _mouseCursorId;
byte *_objectBuf;
byte *_mouseCursor;
EventsManager(HopkinsEngine *vm);
~EventsManager();
void clearAll();
void initMouseData();
void delay(int totalMilli);
void changeMouseCursor(int id);
void refreshEvents();
int waitKeyPress();
int getMouseX();
int getMouseY();
int getMouseButton();
void setMouseXY(Common::Point pos);
void setMouseXY(int xp, int yp);
void mouseOn();
void mouseOff();
void setMouseOn();
void refreshScreenAndEvents();
};
} // End of namespace Hopkins
#endif /* HOPKINS_EVENTS_H */

265
engines/hopkins/files.cpp Normal file
View File

@@ -0,0 +1,265 @@
/* 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 "hopkins/files.h"
#include "hopkins/hopkins.h"
#include "hopkins/globals.h"
#include "common/config-manager.h"
#include "common/system.h"
#include "common/debug.h"
#include "common/file.h"
#include "common/str.h"
#include "common/savefile.h"
namespace Hopkins {
FileManager::FileManager(HopkinsEngine *vm) {
_vm = vm;
_catalogPos = 0;
_catalogSize = 0;
}
/**
* Load a file
*/
byte *FileManager::loadFile(const Common::Path &file) {
Common::File f;
if (!f.open(file))
error("Error opening %s", file.toString().c_str());
// Allocate space for the file contents
size_t filesize = f.size();
byte *data = _vm->_globals->allocMemory(filesize+1);
if (!data)
error("Error allocating space for file being loaded - %s", file.toString().c_str());
readStream(f, data, filesize);
f.close();
data[filesize] = '\0';
return data;
}
/**
* Read a given number of bytes from a Stream into a pre-allocated buffer
*/
int FileManager::readStream(Common::ReadStream &stream, void *buf, size_t nbytes) {
return stream.read(buf, nbytes);
}
/**
* The original censorship was based on blood.dat file.
* It's now using the config manager and a per-engine GUI option.
*/
void FileManager::initCensorship() {
_vm->_globals->_censorshipFl = !ConfMan.getBool("enable_gore");
}
/**
* Check if a file is present
*/
bool FileManager::fileExists(const Common::Path &file) {
Common::File f;
return f.exists(file);
}
/**
* Search file in Cat file
*/
byte *FileManager::searchCat(const Common::Path &file, CatMode mode, bool &fileFoundFl) {
byte *ptr = nullptr;
fileFoundFl = true;
Common::File f;
Common::String filename = file.toString('/');
Common::Path secondaryFilename;
filename.toUppercase();
switch (mode) {
case RES_INI:
if (!f.exists("RES_INI.CAT")) {
fileFoundFl = false;
return nullptr;
}
ptr = loadFile("RES_INI.CAT");
secondaryFilename = "RES_INI.RES";
break;
case RES_REP:
if (!f.exists("RES_REP.CAT")) {
fileFoundFl = false;
return nullptr;
}
ptr = loadFile("RES_REP.CAT");
secondaryFilename = "RES_REP.RES";
break;
case RES_LIN:
if (!f.exists("RES_LIN.CAT")) {
fileFoundFl = false;
return nullptr;
}
ptr = loadFile("RES_LIN.CAT");
secondaryFilename = "RES_LIN.RES";
break;
case RES_PER:
if (!f.exists("RES_PER.CAT")) {
fileFoundFl = false;
return nullptr;
}
ptr = loadFile("RES_PER.CAT");
secondaryFilename = "RES_PER.RES";
break;
case RES_PIC:
if (!f.exists("PIC.CAT")) {
fileFoundFl = false;
return nullptr;
}
ptr = loadFile("PIC.CAT");
break;
case RES_SAN:
if (!f.exists("RES_SAN.CAT")) {
fileFoundFl = false;
return nullptr;
}
ptr = loadFile("RES_SAN.CAT");
break;
case RES_SLI:
if (!f.exists("RES_SLI.CAT")) {
fileFoundFl = false;
return nullptr;
}
ptr = loadFile("RES_SLI.CAT");
break;
case RES_VOI: {
Common::Path tmpFilename;
if (_vm->getPlatform() == Common::kPlatformOS2 || _vm->getPlatform() == Common::kPlatformBeOS)
tmpFilename = "ENG_VOI.CAT";
// Win95 and Linux versions uses another set of names
else {
switch (_vm->_globals->_language) {
case LANG_EN:
tmpFilename = "RES_VAN.CAT";
break;
case LANG_FR:
tmpFilename = "RES_VFR.CAT";
break;
case LANG_SP:
tmpFilename = "RES_VES.CAT";
break;
default:
break;
}
}
if (!f.exists(tmpFilename)) {
fileFoundFl = false;
return nullptr;
}
ptr = loadFile(tmpFilename);
break;
}
default:
break;
}
// Scan for an entry in the catalogue
byte *result;
bool matchFlag = false;
int offsetVal = 0;
while (!matchFlag) {
Common::String name = (const char *)ptr + offsetVal;
if (name == filename) {
// Found entry for file, so get it's details from the catalogue entry
const byte *pData = ptr + offsetVal;
_catalogPos = READ_LE_UINT32(pData + 15);
_catalogSize = READ_LE_UINT32(pData + 19);
matchFlag = true;
}
if (name == "FINIS") {
_vm->_globals->freeMemory(ptr);
fileFoundFl = false;
return nullptr;
}
offsetVal += 23;
}
_vm->_globals->freeMemory(ptr);
if (!secondaryFilename.empty()) {
if (!f.open(secondaryFilename))
error("CHARGE_FICHIER");
f.seek(_catalogPos);
byte *catData = _vm->_globals->allocMemory(_catalogSize);
if (catData == nullptr)
error("CHARGE_FICHIER");
readStream(f, catData, _catalogSize);
f.close();
result = catData;
} else {
result = nullptr;
}
return result;
}
/**
* Returns the size of a file. Throws an error if the file can't be found
*/
uint32 FileManager::fileSize(const Common::Path &filename) {
Common::File f;
uint32 size;
if (!f.open(filename))
error("Could not find file %s", filename.toString().c_str());
size = f.size();
f.close();
return size;
}
} // End of namespace Hopkins

59
engines/hopkins/files.h Normal file
View File

@@ -0,0 +1,59 @@
/* 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 HOPKINS_FILES_H
#define HOPKINS_FILES_H
#include "common/scummsys.h"
namespace Common {
class ReadStream;
class Path;
}
namespace Hopkins {
class HopkinsEngine;
// RES_ANI = 4 has been removed because it's not used
enum CatMode { RES_INI = 1, RES_REP = 2, RES_LIN = 3, RES_PER = 5,
RES_PIC = 6, RES_SAN = 7, RES_SLI = 8, RES_VOI = 9 };
class FileManager {
public:
uint32 _catalogPos;
uint32 _catalogSize;
HopkinsEngine *_vm;
FileManager(HopkinsEngine *vm);
bool fileExists(const Common::Path &file);
byte *loadFile(const Common::Path &file);
int readStream(Common::ReadStream &stream, void *buf, size_t nbytes);
void initCensorship();
byte *searchCat(const Common::Path &file, CatMode mode, bool &fileFoundFl);
uint32 fileSize(const Common::Path &filename);
};
} // End of namespace Hopkins
#endif /* HOPKINS_GLOBALS_H */

498
engines/hopkins/font.cpp Normal file
View File

@@ -0,0 +1,498 @@
/* 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 "hopkins/font.h"
#include "hopkins/files.h"
#include "hopkins/globals.h"
#include "hopkins/graphics.h"
#include "hopkins/hopkins.h"
#include "hopkins/objects.h"
#include "common/system.h"
#include "common/file.h"
#include "common/textconsole.h"
namespace Hopkins {
FontManager::FontManager(HopkinsEngine *vm) {
_vm = vm;
clearAll();
}
FontManager::~FontManager() {
_vm->_globals->freeMemory(_font);
_vm->_globals->freeMemory(_zoneText);
}
void FontManager::loadZoneText() {
switch (_vm->_globals->_language) {
case LANG_EN:
_zoneText = _vm->_fileIO->loadFile("ZONEAN.TXT");
break;
case LANG_FR:
_zoneText = _vm->_fileIO->loadFile("ZONE01.TXT");
break;
case LANG_SP:
_zoneText = _vm->_fileIO->loadFile("ZONEES.TXT");
break;
default:
break;
}
}
void FontManager::clearAll() {
_font = nullptr;
_fontFixedHeight = 0;
_fontFixedWidth = 0;
for (int idx = 0; idx < 12; ++idx) {
Common::fill((byte *)&_text[idx], (byte *)&_text[idx] + sizeof(TxtItem), 0);
_textList[idx]._enabledFl = false;
_textList[idx]._height = 0;
_textList[idx]._width = 0;
_textList[idx]._pos.x = 0;
_textList[idx]._pos.y = 0;
}
for (int idx = 0; idx < 21; idx++)
_textSortArray[idx] = 0;
_oldName = Common::String("");
_indexName = Common::String("");
for (int idx = 0; idx < 4048; idx++)
_index[idx] = 0;
_tempText = nullptr;
_zoneText = nullptr;
_boxWidth = 240;
}
void FontManager::initData() {
_font = _vm->_fileIO->loadFile("FONTE3.SPR");
_fontFixedWidth = 12;
_fontFixedHeight = 21;
loadZoneText();
}
/**
* Display Text
*/
void FontManager::showText(int idx) {
if ((idx - 5) > MAX_TEXT)
error("Attempted to display text > MAX_TEXT.");
TxtItem &txt = _text[idx - 5];
txt._textOnFl = true;
txt._textLoadedFl = false;
txt._textBlock = _vm->_globals->freeMemory(txt._textBlock);
}
/**
* Hide text
*/
void FontManager::hideText(int idx) {
if ((idx - 5) > MAX_TEXT)
error("Attempted to display text > MAX_TEXT.");
TxtItem &txt = _text[idx - 5];
txt._textOnFl = false;
txt._textLoadedFl = false;
txt._textBlock = _vm->_globals->freeMemory(txt._textBlock);
}
/**
* Set Text Color
*/
void FontManager::setTextColor(int idx, byte colByte) {
_text[idx - 5]._color = colByte;
}
/**
* Set Text Optimal Color
*/
void FontManager::setOptimalColor(int idx1, int idx2, int idx3, int idx4) {
setTextColor(idx1, 255);
setTextColor(idx2, 255);
setTextColor(idx3, 255);
setTextColor(idx4, 253);
}
/**
* Init text structure
*/
void FontManager::initTextBuffers(int idx, int messageId, const Common::Path &filename, int xp, int yp, int textType, int length, int color) {
assert(idx - 5 >= 0 && (idx - 5) <= MAX_TEXT);
TxtItem &txt = _text[idx - 5];
txt._textOnFl = false;
txt._filename = filename;
txt._pos.x = xp;
txt._pos.y = yp;
txt._messageId = messageId;
txt._textType = textType;
txt._length = length;
txt._color = color;
}
// Box
void FontManager::box(int idx, int messageId, const Common::Path &filename, int xp, int yp) {
int textPosX = xp;
if (idx < 0)
error("Bad number for text");
_fontFixedWidth = 11;
_boxWidth = 11 * _text[idx]._length;
if (_text[idx]._textLoadedFl) {
int textType = _text[idx]._textType;
if (textType != 6 && textType != 1 && textType != 3 && textType != 5) {
int yCurrent = yp + 5;
for (int lineNum = 0; lineNum < _text[idx]._lineCount; ++lineNum) {
displayText(xp + 5, yCurrent, _text[idx]._lines[lineNum], _text[idx]._color);
yCurrent += _fontFixedHeight + 1;
}
} else {
int height = _text[idx]._height;
int width = _text[idx]._width;
_vm->_graphicsMan->restoreSurfaceRect(
_vm->_graphicsMan->_frontBuffer,
_text[idx]._textBlock,
xp,
yp,
_text[idx]._width,
_text[idx]._height);
_vm->_graphicsMan->addDirtyRect(xp, yp, xp + width, yp + height);
}
} else {
int lineCount = 0;
for (int i = 0; i <= 19; i++)
_textSortArray[i] = 0;
_text[idx]._textLoadedFl = true;
Common::String basename(filename.baseName());
if (filename != _oldName) {
// Starting to access a new file, so read in the index file for the file
_oldName = filename;
_indexName = filename.getParent();
_indexName.joinInPlace(Common::String(basename.c_str(), basename.size() - 3));
_indexName.appendInPlace("IND");
Common::File f;
if (!f.open(_indexName))
error("Error opening file - %s", _indexName.toString().c_str());
int filesize = f.size();
for (int i = 0; i < (filesize / 4); ++i)
_index[i] = f.readUint32LE();
f.close();
}
int bufSize;
if (basename[0] != 'Z' || basename[1] != 'O') {
Common::File f;
if (!f.open(filename))
error("Error opening file - %s", _indexName.toString().c_str());
bufSize = 2048;
f.seek(_index[messageId]);
_tempText = _vm->_globals->allocMemory(2058);
if (_tempText == nullptr)
error("Error allocating text");
Common::fill(&_tempText[0], &_tempText[2058], 0);
f.read(_tempText, 2048);
f.close();
} else {
bufSize = 100;
_tempText = _vm->_globals->allocMemory(110);
Common::fill(&_tempText[0], &_tempText[110], 0);
memcpy(_tempText, _zoneText + _index[messageId], 96);
WRITE_LE_UINT16((uint16 *)_tempText + 48, READ_LE_INT16(_zoneText + _index[messageId] + 96));
}
byte *curTempTextPtr = _tempText;
for (int i = 0; i < bufSize; i++) {
byte curChar = *curTempTextPtr;
if ((byte)(*curTempTextPtr + 46) > 27) {
if ((byte)(curChar + 80) > 27) {
if ((byte)(curChar - 65) <= 25 || (byte)(curChar - 97) <= 25)
curChar = 32;
} else {
curChar -= 79;
}
} else {
curChar += 111;
}
*curTempTextPtr = curChar;
curTempTextPtr++;
};
int textLength;
for (textLength = 0; textLength < bufSize; textLength++) {
byte curChar = _tempText[textLength];
if (curChar == '\r' || curChar == '\n') {
_tempText[textLength] = 0;
if (!_text[idx]._length)
break;
}
}
if (bufSize && bufSize > textLength) {
_text[idx]._length = textLength;
_boxWidth = 0;
for (int curStrIdx = 0; curStrIdx < textLength + 1; curStrIdx++) {
byte curChar = _tempText[curStrIdx];
if (curChar <= 31)
curChar = ' ';
_boxWidth += _vm->_objectsMan->getWidth(_font, curChar - 32);
}
_boxWidth += 2;
_text[idx]._pos.x = 320 - abs(_boxWidth / 2);
textPosX = _vm->_events->_startPos.x + _text[idx]._pos.x;
lineCount = 1;
_text[idx]._lines[0] = Common::String((const char *)_tempText, textLength);
} else {
if (!_boxWidth)
_boxWidth = 240;
int tempTextIdx = 0;
int lineSize;
byte curChar;
do {
int curLineSize = 0;
int ptrb = _boxWidth - 4;
for (;;) {
lineSize = curLineSize;
do {
curChar = _tempText[tempTextIdx + curLineSize++];
} while (curChar != ' ' && curChar != '%');
if (curLineSize >= ptrb / _fontFixedWidth) {
if (curChar == '%')
curChar = ' ';
break;
}
if (curChar == '%') {
lineSize = curLineSize;
break;
}
}
// WORKAROUND: Perhaps due to the usage of ScummVM strings here, recalculate what the
// actual length of the line to be copied will be. Otherwise, you can see artifacts,
// such as a single character beyond the end of string NULL.
int actualSize = 0;
while (actualSize < lineSize && _tempText[tempTextIdx + actualSize])
++actualSize;
_text[idx]._lines[lineCount] = Common::String((const char *)_tempText + tempTextIdx, actualSize);
_textSortArray[lineCount++] = lineSize;
tempTextIdx += lineSize;
} while (curChar != '%');
for (int i = 0; i <= 19; i++) {
if (_textSortArray[i] <= 0) {
_textSortArray[i] = 0;
} else {
int ptrc = 0;
for (int curIdx = 0; curIdx < _textSortArray[i] - 1; curIdx++) {
Common::String &line = _text[idx]._lines[i];
byte curChar2 = (curIdx >= (int)line.size()) ? '\0' : line.c_str()[curIdx];
if (curChar2 <= 31)
curChar2 = ' ';
ptrc += _vm->_objectsMan->getWidth(_font, (byte)curChar2 - 32);
}
_textSortArray[i] = ptrc;
}
}
for (int i = 0; i <= 19; i++) {
for (int j = i + 1; j != i; j = (j + 1) % 20) {
if (_textSortArray[i] < _textSortArray[j])
_textSortArray[i] = 0;
}
};
for (int i = 0; i <= 19; i++) {
if (_textSortArray[i])
_boxWidth = _textSortArray[i];
}
if ((_text[idx]._textType < 2) || (_text[idx]._textType > 3)) {
int i;
for (i = xp - _vm->_events->_startPos.x; _boxWidth + i > 638 && i > -2 && _text[idx]._textType; i -= 2)
;
_text[idx]._pos.x = i;
textPosX = _vm->_events->_startPos.x + i;
} else {
_text[idx]._pos.x = textPosX;
}
}
int posX = textPosX;
int posY = yp;
int saveWidth = _boxWidth + 10;
int saveHeight = (_fontFixedHeight + 1) * lineCount + 12;
if (_text[idx]._textType == 6) {
_text[idx]._pos.x = 315 - abs(saveWidth / 2);
textPosX = posX = _vm->_events->_startPos.x + _text[idx]._pos.x;
_text[idx]._pos.y = posY = 50;
}
int textType = _text[idx]._textType;
if (textType == 1 || textType == 3 || textType == 5 || textType == 6) {
int size = saveHeight * saveWidth;
byte *ptrd = _vm->_globals->allocMemory(size);
if (ptrd == nullptr)
error("Cutting a block for text box (%d)", size);
_vm->_graphicsMan->copySurfaceRect(_vm->_graphicsMan->_frontBuffer, ptrd, posX, posY, saveWidth, saveHeight);
_vm->_graphicsMan->fillSurface(ptrd, _vm->_graphicsMan->_colorTable, size);
_vm->_graphicsMan->restoreSurfaceRect(_vm->_graphicsMan->_frontBuffer, ptrd, posX, posY, saveWidth, saveHeight);
_vm->_globals->freeMemory(ptrd);
_vm->_graphicsMan->drawHorizontalLine(_vm->_graphicsMan->_frontBuffer, posX, posY, saveWidth, (byte)-2);
_vm->_graphicsMan->drawHorizontalLine(_vm->_graphicsMan->_frontBuffer, posX, saveHeight + posY, saveWidth, (byte)-2);
_vm->_graphicsMan->drawVerticalLine(_vm->_graphicsMan->_frontBuffer, posX, posY, saveHeight, (byte)-2);
_vm->_graphicsMan->drawVerticalLine(_vm->_graphicsMan->_frontBuffer, saveWidth + posX, posY, saveHeight, (byte)-2);
}
_text[idx]._lineCount = lineCount;
int textPosY = posY + 5;
for (int lineNum = 0; lineNum < lineCount; ++lineNum) {
displayText(textPosX + 5, textPosY, _text[idx]._lines[lineNum], _text[idx]._color);
textPosY += _fontFixedHeight + 1;
}
int blockWidth = saveWidth + 1;
int blockHeight = saveHeight + 1;
_text[idx]._width = blockWidth;
_text[idx]._height = blockHeight;
textType = _text[idx]._textType;
if (textType == 6 || textType == 1 || textType == 3 || textType == 5) {
_text[idx]._textBlock = _vm->_globals->freeMemory(_text[idx]._textBlock);
int blockSize = blockHeight * blockWidth;
byte *ptre = _vm->_globals->allocMemory(blockSize + 20);
if (ptre == nullptr)
error("Cutting a block for text box (%d)", blockSize);
_text[idx]._textBlock = ptre;
_text[idx]._width = blockWidth;
_text[idx]._height = blockHeight;
_vm->_graphicsMan->copySurfaceRect(_vm->_graphicsMan->_frontBuffer, _text[idx]._textBlock, posX, posY, _text[idx]._width, blockHeight);
}
_tempText = _vm->_globals->freeMemory(_tempText);
}
}
/**
* Directly display text (using a VESA segment)
*/
void FontManager::displayTextVesa(int xp, int yp, const Common::String &message, int col) {
int charIndex;
int currentX = xp;
const char *srcP = message.c_str();
for (;;) {
byte currChar = *srcP++;
if (!currChar)
break;
if (currChar >= 32) {
charIndex = currChar - 32;
_vm->_graphicsMan->displayFont(_vm->_graphicsMan->_frontBuffer, _font, currentX, yp, currChar - 32, col);
currentX += _vm->_objectsMan->getWidth(_font, charIndex);
}
}
_vm->_graphicsMan->addDirtyRect(xp, yp, currentX, yp + 12);
}
/**
* Directly display text
*/
void FontManager::displayText(int xp, int yp, const Common::String &message, int col) {
for (uint idx = 0; idx < message.size(); ++idx) {
byte currentChar = (byte)message[idx];
if (currentChar > 31) {
int characterIndex = currentChar - 32;
_vm->_graphicsMan->displayFont(_vm->_graphicsMan->_frontBuffer, _font, xp, yp, characterIndex, col);
_vm->_graphicsMan->addDirtyRect(xp, yp, xp + _vm->_objectsMan->getWidth(_font, characterIndex) + 1, yp + _vm->_objectsMan->getHeight(_font, characterIndex) + 1);
xp += _vm->_objectsMan->getWidth(_font, characterIndex);
}
}
}
/**
* Compute character width and render text using variable width fonts
*/
void FontManager::renderTextDisplay(int xp, int yp, const Common::String &msg, int col) {
const char *srcP = msg.c_str();
int charEndPosX = xp;
int fontCol = col;
byte curChar = *srcP++;
while (curChar) {
if (curChar == '&') {
fontCol = 2;
curChar = *srcP++;
}
if (curChar == '$') {
fontCol = 4;
curChar = *srcP++;
}
if (!curChar)
break;
if (curChar >= 32) {
byte printChar = curChar - 32;
_vm->_graphicsMan->displayFont(_vm->_graphicsMan->_frontBuffer, _font, charEndPosX, yp, printChar, fontCol);
// UGLY HACK: For some obscure reason, the BeOS and OS/2 versions use another font file, which doesn't have variable width.
// All the fonts have a length of 9, which results in completely broken text in the computer.
// This horrible workaround fixes the English versions of the game. So far, no other languages are known for those platforms.
// Just in case, all the accentuated characters are handled properly, which *should* be OK for the other languages too.
int charWidth;
if (_vm->getPlatform() == Common::kPlatformOS2 || _vm->getPlatform() == Common::kPlatformBeOS) {
if ((curChar >= 'A' && curChar <= 'Z') || (curChar >= 'a' && curChar <= 'z' && curChar != 'm' && curChar != 'w') || (curChar >= '0' && curChar <= '9') || curChar == '*' || (curChar >= 128 && curChar <= 168))
charWidth = _vm->_objectsMan->getWidth(_font, printChar) - 1;
else if (curChar == 'm' || curChar == 'w')
charWidth = _vm->_objectsMan->getWidth(_font, printChar);
else
charWidth = 6;
} else
charWidth = _vm->_objectsMan->getWidth(_font, printChar);
int charStartPosX = charEndPosX;
charEndPosX += charWidth;
_vm->_graphicsMan->addDirtyRect(charStartPosX, yp, charEndPosX, yp + 12);
if (_vm->_events->_escKeyFl) {
_vm->_globals->_eventMode = EVENTMODE_IGNORE;
_vm->_events->refreshScreenAndEvents();
} else {
_vm->_globals->_eventMode = EVENTMODE_ALT;
_vm->_events->refreshScreenAndEvents();
_vm->_globals->_eventMode = EVENTMODE_IGNORE;
}
}
curChar = *srcP++;
}
}
} // End of namespace Hopkins

97
engines/hopkins/font.h Normal file
View File

@@ -0,0 +1,97 @@
/* 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 HOPKINS_FONT_H
#define HOPKINS_FONT_H
#include "common/scummsys.h"
#include "common/events.h"
#include "common/str.h"
namespace Hopkins {
#define MAX_TEXT 11
class HopkinsEngine;
struct TxtItem {
bool _textOnFl;
Common::Path _filename;
Common::Point _pos;
int _messageId;
int _lineCount;
Common::String _lines[10];
int _textType;
int _length;
byte *_textBlock;
int16 _width;
int16 _height;
bool _textLoadedFl;
int _color;
};
struct TxtItemList {
bool _enabledFl;
Common::Point _pos;
int16 _width;
int16 _height;
};
class FontManager {
private:
HopkinsEngine *_vm;
void setTextColor(int idx, byte colByte);
int _textSortArray[21];
Common::Path _oldName;
Common::Path _indexName;
int _index[4048];
byte *_tempText;
byte *_zoneText;
int _boxWidth;
void loadZoneText();
public:
byte *_font;
int _fontFixedWidth;
int _fontFixedHeight;
TxtItem _text[12];
TxtItemList _textList[12];
FontManager(HopkinsEngine *vm);
~FontManager();
void clearAll();
void initData();
void showText(int idx);
void hideText(int idx);
void initTextBuffers(int idx, int messageId, const Common::Path &filename, int xp, int yp, int textType, int length, int color);
void displayText(int xp, int yp, const Common::String &message, int col);
void displayTextVesa(int xp, int yp, const Common::String &message, int col);
void renderTextDisplay(int xp, int yp, const Common::String &msg, int col);
void setOptimalColor(int idx1, int idx2, int idx3, int idx4);
void box(int idx, int messageId, const Common::Path &filename, int xp, int yp);
};
} // End of namespace Hopkins
#endif /* HOPKINS_FONT_H */

215
engines/hopkins/globals.cpp Normal file
View File

@@ -0,0 +1,215 @@
/* 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 "hopkins/globals.h"
#include "hopkins/files.h"
#include "hopkins/font.h"
#include "hopkins/graphics.h"
#include "hopkins/hopkins.h"
#include "common/textconsole.h"
#include "common/file.h"
namespace Hopkins {
// Default data for the Hopkins array
const int HOPKINS_PERSO_0[] = {
0, -2, 0, -3, 0, -6, 0, -1, 0, -3, 0, -3, 0, -5, 0, -3, 0, -6, 0, -3, 0, -3, 0, -3,
9, -4, 8, -4, 6, -2, 9, -2, 9, -3, 9, -3, 9, -4, 9, -2, 9, -2, 8, -2, 9, -3, 9, -2,
13, 0, 13, 0, 13, 0, 13, 0, 14, 0, 13, 0, 13, 0, 12, 0, 12, 0, 14, 0, 13, 0, 14, 0,
10, 3, 9, 3, 10, 4, 8, 2, 7, 1, 10, 2, 9, 2, 7, 4, 7, 3, 8, 0, 9, 1, 9, 1, 0, 4, 0,
4, 0, 6, 0, 3, 0, 4, 0, 3, 0, 4, 0, 4, 0, 6, 0, 3, 0, 3, 0, 3
};
const int HOPKINS_PERSO_1[] = {
0, -2, 0, -2, 0, -5, 0, -1, 0, -2, 0, -2, 0, -4, 0, -2, 0, -5, 0, -2, 0, -2, 0, -2,
11, 0, 10, 0, 11, 0, 11, 0, 11, 0, 11, 0, 12, 0, 11, 0, 9, 0, 10, 0, 11, 0, 11, 0,
11, 0, 10, 0, 11, 0, 11, 0, 11, 0, 11, 0, 12, 0, 11, 0, 9, 0, 10, 0, 11, 0, 11, 0,
11, 0, 10, 0, 11, 0, 11, 0, 11, 0, 11, 0, 12, 0, 11, 0, 9, 0, 10, 0, 11, 0, 11, 0,
0, 3, 0, 3, 0, 5, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 5, 0, 3, 0, 3, 0, 3
};
const int HOPKINS_PERSO_2[] = {
0, -2, 0, 0, 0, -3, 0, -2, 0, -2, 0, -1, 0, -2, 0, -1, 0, -3, 0, -2, 0, -2, 0, -2,
8, 0, 9, 0, 5, 0, 9, 0, 7, 0, 7, 0, 7, 0, 7, 0, 6, 0, 7, 0, 6, 0, 9, 0, 8, 0, 9, 0,
5, 0, 9, 0, 7, 0, 7, 0, 7, 0, 7, 0, 6, 0, 7, 0, 6, 0, 9, 0, 8, 0, 9, 0, 5, 0, 9, 0,
7, 0, 7, 0, 7, 0, 7, 0, 6, 0, 7, 0, 6, 0, 9, 0, 0, 2, 0, 0, 0, 2, 0, 1, 0, 2, 0, 2,
0, 2, 0, 2, 0, 2, 0, 1, 0, 2, 0, 2
};
Globals::Globals(HopkinsEngine *vm) {
_vm = vm;
// Initialize array properties
for (int i = 0; i < 500; ++i)
_spriteSize[i] = 0;
for (int i = 0; i < 70; ++i)
Common::fill((byte *)&_hopkinsItem[i], (byte *)&_hopkinsItem[i] + sizeof(HopkinsItem), 0);
for (int i = 0; i < 36; ++i)
_inventory[i] = 0;
// Initialize fields
_language = LANG_EN;
_linuxEndDemoFl = false;
_speed = 1;
_eventMode = EVENTMODE_DEFAULT;
_exitId = 0;
_characterSpriteBuf = nullptr;
_screenId = 0;
_prevScreenId = 0;
_characterMaxPosY = 0;
_menuScrollSpeed = 0;
_menuSpeed = 0;
_menuSoundOff = 0;
_menuVoiceOff = 0;
_menuMusicOff = 0;
_menuTextOff = 0;
_menuDisplayType = 0;
_checkDistanceFl = false;
_characterType = CHARACTER_HOPKINS;
_actionMoveTo = false;
_actionDirection = DIR_NONE;
_creditsStartX = -1;
_creditsEndX = -1;
_creditsStartY = -1;
_creditsEndY = -1;
_creditsPosY = 0;
_creditsLineNumb = 0;
memset(_creditsItem, 0, 12000);
_creditsStep = 0;
_oceanDirection = DIR_NONE;
// Initialize pointers
_levelSpriteBuf = nullptr;
_saveData = nullptr;
_answerBuffer = nullptr;
_characterSpriteBuf = nullptr;
_optionDialogSpr = nullptr;
// Reset flags
_censorshipFl = false;
_disableInventFl = false;
_freezeCharacterFl = false;
_optionDialogFl = false;
_introSpeechOffFl = false;
_cityMapEnabledFl = false;
_baseMapColor = 50;
_curRoomNum = 0;
}
Globals::~Globals() {
freeMemory(_levelSpriteBuf);
freeMemory((byte *)_saveData);
freeMemory(_answerBuffer);
freeMemory(_characterSpriteBuf);
}
void Globals::setConfig() {
// CHECKME: Should be in Globals() but it doesn't work
// The Polish version is a translation of the English version. The filenames are the same.
// The Russian version looks like a translation of the English version, based on the filenames.
switch (_vm->getLanguage()) {
case Common::EN_ANY:
case Common::PL_POL:
case Common::RU_RUS:
_language = LANG_EN;
break;
case Common::FR_FRA:
_language = LANG_FR;
break;
case Common::ES_ESP:
_language = LANG_SP;
break;
default:
error("Hopkins - SetConfig(): Unknown language in internal language mapping");
break;
}
// End of CHECKME
switch (_language) {
case LANG_EN:
_zoneFilename = "ZONEAN.TXT";
_textFilename = "TEXTEAN.TXT";
break;
case LANG_FR:
_zoneFilename = "ZONE01.TXT";
_textFilename = "TEXTE01.TXT";
break;
case LANG_SP:
_zoneFilename = "ZONEES.TXT";
_textFilename = "TEXTEES.TXT";
break;
default:
break;
}
}
void Globals::clearAll() {
_vm->_fontMan->clearAll();
_vm->_dialog->clearAll();
_answerBuffer = nullptr;
_levelSpriteBuf = nullptr;
_saveData = nullptr;
_vm->_objectsMan->_curObjectIndex = 0;
_vm->_linesMan->clearAll();
_vm->_objectsMan->clearAll();
_saveData = (Savegame *)malloc(sizeof(Savegame));
_saveData->reset();
_vm->_events->clearAll();
}
void Globals::loadCharacterData() {
const int *srcList[] = { HOPKINS_PERSO_0, HOPKINS_PERSO_1, HOPKINS_PERSO_2 };
const int *srcP = srcList[_characterType];
for (int idx = 0; idx < 240 / 4; ++idx) {
_hopkinsItem[idx]._speedX = *srcP++;
_hopkinsItem[idx]._speedY = *srcP++;
}
_vm->_objectsMan->resetOldFrameIndex();
_vm->_objectsMan->resetOldDirection();
}
byte *Globals::allocMemory(int count) {
byte *result = (byte *)malloc(count);
if (!result)
result = nullptr;
return result;
}
byte *Globals::freeMemory(byte *p) {
if (p)
free(p);
return nullptr;
}
} // End of namespace Hopkins

251
engines/hopkins/globals.h Normal file
View File

@@ -0,0 +1,251 @@
/* 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 HOPKINS_GLOBALS_H
#define HOPKINS_GLOBALS_H
#include "common/scummsys.h"
#include "common/str.h"
#include "common/util.h"
#include "common/events.h"
namespace Hopkins {
struct HopkinsItem {
int _speedX;
int _speedY;
};
struct CharacterLocation {
Common::Point _pos;
int _startSpriteIndex;
int _location;
int _zoomFactor;
void reset() {
_pos.x = 0;
_pos.y = 0;
_startSpriteIndex = 0;
_location = 0;
_zoomFactor = 0;
}
CharacterLocation() { reset(); }
};
enum SauvegardeOffset {
svLastMouseCursor = 1
, svLastZoneNum = 2
, svLastObjectIndex = 3
, svDialogField4 = 4
, svLastScreenId = 5
, svLastPrevScreenId = 6
, svLastInventoryItem = 8
, svLastInvMouseCursor = 9
, svLastSavegameSlot = 10
, svFreedHostageFl = 80
, svField94 = 94
, svField95 = 95
, svForestAvailableFl = 113
, svHutBurningFl = 117
, svHopkinsCloneFl = 121
, svAlternateSpriteFl = 122
, svHeavenGuardGoneFl = 123
, svField132 = 132
, svField133 = 133
, svGameWonFl = 135
, svCinemaCurtainCond1 = 166
, svCinemaCurtainCond2 = 167
, svBankAttackAnimPlayedFl = 170
, svCopCall1PlayedFl = 171
, svCopCall2PlayedFl = 172
, svField173 = 173
, svField176 = 176
, svPoolDogGoneFl = 177
, svCinemaDogGoneFl = 181
, svField183 = 183
, svField184 = 184
, svField186 = 186
, svField188 = 188
, svField200 = 200
, svField214 = 214
, svBombBoxOpenedFl = 220
, svBombDisarmedFl = 225
, svField228 = 228
, svField231 = 231
, svField253 = 253
, svField261 = 261
, svField270 = 270
, svField300 = 300
, svBaseElevatorCond1 = 311
, svBaseFireFl = 312
, svSecondElevatorAvailableFl = 318
, svField320 = 320
, svEscapeLeftJailFl = 330
, svField333 = 333
, svField338 = 338
, svField339 = 339
, svField340 = 340
, svField341 = 341
, svField352 = 352
, svField353 = 353
, svField354 = 354
, svField355 = 355
, svField356 = 356
, svField357 = 357
, svField399 = 399
, svField401 = 401
};
// As Script engine directly access savegame fields,
// refactoring it in separated fields properly named is impossible
struct Savegame {
byte _data[2050];
CharacterLocation _cloneHopkins;
CharacterLocation _realHopkins;
CharacterLocation _samantha;
int16 _inventory[35]; // Originally at offset 1300 of data array
int16 _mapCarPosX;
int16 _mapCarPosY;
void reset() {
for (uint16 i = 0; i < ARRAYSIZE(_data); i++) {
_data[i] = 0;
}
_cloneHopkins.reset();
_realHopkins.reset();
_samantha.reset();
for (uint16 i = 0; i < ARRAYSIZE(_inventory); i++) {
_inventory[i] = 0;
}
_mapCarPosX = 0;
_mapCarPosY = 0;
}
Savegame() { reset(); }
};
struct CreditItem {
bool _actvFl;
int _color;
int _linePosY;
int _lineSize;
byte _line[50];
};
enum Language { LANG_EN = 0, LANG_FR = 1, LANG_SP = 2};
enum PlayerCharacter { CHARACTER_HOPKINS = 0, CHARACTER_HOPKINS_CLONE = 1, CHARACTER_SAMANTHA = 2 };
enum Directions {
DIR_NONE = -1,
DIR_UP = 1,
DIR_UP_RIGHT = 2,
DIR_RIGHT = 3,
DIR_DOWN_RIGHT = 4,
DIR_DOWN = 5,
DIR_DOWN_LEFT = 6,
DIR_LEFT = 7,
DIR_UP_LEFT = 8
};
enum EventMode {
EVENTMODE_DEFAULT = 0,
EVENTMODE_IGNORE = 1,
EVENTMODE_CREDITS = 3,
EVENTMODE_ALT = 4
};
class HopkinsEngine;
/**
* Engine Globals
*/
class Globals {
private:
HopkinsEngine *_vm;
public:
bool _disableInventFl;
bool _cityMapEnabledFl;
bool _linuxEndDemoFl;
bool _censorshipFl;
bool _introSpeechOffFl;
int _exitId;
Directions _oceanDirection;
int _actionDirection;
int _inventory[36];
int _screenId;
int _prevScreenId;
int _characterMaxPosY;
int _baseMapColor;
int _spriteSize[500];
PlayerCharacter _characterType;
uint _speed;
byte *_answerBuffer;
Savegame *_saveData;
Language _language;
HopkinsItem _hopkinsItem[70];
CreditItem _creditsItem[200];
int _creditsLineNumb;
int _creditsStep;
int _creditsPosY;
int _creditsStartX;
int _creditsEndX;
int _creditsStartY;
int _creditsEndY;
int _menuSpeed;
int _menuSoundOff;
int _menuTextOff;
int _menuVoiceOff;
int _menuMusicOff;
int _menuDisplayType;
int _menuScrollSpeed;
byte *_optionDialogSpr;
bool _optionDialogFl;
bool _actionMoveTo;
bool _freezeCharacterFl;
bool _checkDistanceFl;
byte *_characterSpriteBuf;
Common::Path _zoneFilename;
Common::Path _textFilename;
byte *_levelSpriteBuf;
EventMode _eventMode;
Globals(HopkinsEngine *vm);
~Globals();
byte *allocMemory(int count);
byte *freeMemory(byte *p);
void setConfig();
void clearAll();
void loadCharacterData();
int _curRoomNum;
};
} // End of namespace Hopkins
#endif /* HOPKINS_GLOBALS_H */

1936
engines/hopkins/graphics.cpp Normal file

File diff suppressed because it is too large Load Diff

192
engines/hopkins/graphics.h Normal file
View File

@@ -0,0 +1,192 @@
/* 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 HOPKINS_GRAPHICS_H
#define HOPKINS_GRAPHICS_H
#include "common/scummsys.h"
#include "common/array.h"
#include "common/endian.h"
#include "common/path.h"
#include "common/rect.h"
#include "graphics/surface.h"
namespace Hopkins {
constexpr int DIRTY_RECTS_SIZE = 250;
constexpr int PALETTE_SIZE = 256;
constexpr int PALETTE_BLOCK_SIZE = (PALETTE_SIZE * 3);
constexpr int PALETTE_EXT_BLOCK_SIZE = 800;
static const byte kSetOffset = 251;
static const byte kByteStop = 252;
static const byte k8bVal = 253;
static const byte k16bVal = 254;
struct RGB8 {
byte r;
byte g;
byte b;
};
class HopkinsEngine;
class GraphicsManager {
private:
HopkinsEngine *_vm;
int _lockCounter;
bool _initGraphicsFl;
int _screenWidth;
int _screenHeight;
byte *_videoPtr;
int _width;
int _posXClipped, _posYClipped;
bool _clipFl;
int _specialWidth;
int _enlargedX, _enlargedY;
bool _enlargedXFl, _enlargedYFl;
int _clipX1, _clipY1;
int _reduceX, _reducedY;
int _zoomOutFactor;
bool _manualScroll;
void loadScreen(const Common::Path &file);
void loadPCX640(byte *surface, const Common::Path &file, byte *palette, bool typeFlag);
void loadPCX320(byte *surface, const Common::Path &file, byte *palette);
void fadeIn(const byte *palette, int step, const byte *surface);
void fadeOut(const byte *palette, int step, const byte *surface);
void changePalette(const byte *palette);
uint16 mapRGB(byte r, byte g, byte b);
void copy16bFromSurfaceScaleX2(const byte *surface);
void translateSurface(byte *destP, const byte *srcP, int count, int minThreshold, int maxThreshold);
void displayScaled8BitRect(const byte *surface, int xp, int yp, int width, int height, int destX, int destY);
void lockScreen();
void unlockScreen();
public:
byte _paletteBuffer[PALETTE_SIZE * 2];
byte _colorTable[PALETTE_EXT_BLOCK_SIZE];
byte _palette[PALETTE_EXT_BLOCK_SIZE];
byte _oldPalette[PALETTE_EXT_BLOCK_SIZE];
byte *_backBuffer;
byte *_frontBuffer;
byte *_screenBuffer;
byte *_backupScreen;
bool _largeScreenFl;
bool _noFadingFl;
bool _fadingFl;
bool _skipVideoLockFl;
int _scrollOffset;
int _scrollPosX;
int _oldScrollPosX;
int _scrollSpeed;
int _lineNbr;
int _lineNbr2;
int _minX, _minY;
int _maxX, _maxY;
int _scrollStatus;
int _fadeDefaultSpeed;
int _screenLineSize;
/**
* The _dirtyRects list contains paletted game areas that need to be redrawn.
* The _dstrect array is the list of areas of the screen that ScummVM needs to be redrawn.
* Some areas, such as the animation managers, skip the _dirtyRects and use _dstrec directly.
*/
Common::Array<Common::Rect> _dirtyRects;
Common::Array<Common::Rect> _refreshRects;
bool _showDirtyRects;
bool _showZones;
bool _showLines;
byte *_palettePixels;
public:
GraphicsManager(HopkinsEngine *vm);
~GraphicsManager();
void clearPalette();
void clearScreen();
void clearVesaScreen();
void resetDirtyRects();
void resetRefreshRects();
void addDirtyRect(int x1, int y1, int x2, int y2);
void addDirtyRect(const Common::Rect &r) { addDirtyRect(r.left, r.top, r.right, r.bottom); }
void addRefreshRect(int x1, int y1, int x2, int y2);
void addRectToArray(Common::Array<Common::Rect> &rects, const Common::Rect &newRect);
void displayDirtyRects();
void displayRefreshRects();
void displayZones();
void displayLines();
void displayDebugRect(Graphics::Surface *surface, const Common::Rect &srcRect, uint32 color = 0xffffff);
void copySurface(const byte *surface, int x1, int y1, int width, int height, byte *destSurface, int destX, int destY);
void loadImage(const Common::Path &file);
void loadVgaImage(const Common::Path &file);
void fadeInLong();
void fadeInBreakout();
void fadeInDefaultLength(const byte *surface);
void fadeInShort();
void fadeOutDefaultLength(const byte *surface);
void fadeOutBreakout();
void fadeOutLong();
void fadeOutShort();
void copyWinscanVbe3(const byte *srcData, byte *destSurface);
void copyWinscanVbe(const byte *srcP, byte *destP);
void copyVideoVbe16(const byte *srcData);
void copyVideoVbe16a(const byte *srcData);
void copySurfaceRect(const byte *srcSurface, byte *destSurface, int xs, int ys, int width, int height);
void restoreSurfaceRect(byte *destSurface, const byte *src, int xp, int yp, int width, int height);
void displayFont(byte *surface, const byte *spriteData, int xp, int yp, int characterIndex, int color);
void drawHorizontalLine(byte *surface, int xp, int yp, uint16 width, byte col);
void drawVerticalLine(byte *surface, int xp, int yp, int height, byte col);
void initColorTable(int minIndex, int maxIndex, byte *palette);
void setGraphicalMode(int width, int height);
void setPaletteVGA256(const byte *palette);
void setPaletteVGA256WithRefresh(const byte *palette, const byte *surface);
void scrollScreen(int amount);
int zoomIn(int v, int percentage);
int zoomOut(int v, int percentage);
void initScreen(const Common::Path &file, int mode, bool initializeScreen);
void displayAllBob();
void endDisplayBob();
void updateScreen();
void reduceScreenPart(const byte *srcSruface, byte *destSurface, int xp, int yp, int width, int height, int zoom);
void setScreenWidth(int pitch);
void setColorPercentage(int palIndex, int r, int g, int b);
void setColorPercentage2(int palIndex, int r, int g, int b);
void fastDisplay(const byte *spriteData, int xp, int yp, int spriteIndex, bool addSegment = true);
void fastDisplay2(const byte *objectData, int xp, int yp, int idx, bool addSegment = true);
void drawCompressedSprite(byte *surface, const byte *srcData, int xp300, int yp300, int frameIndex, int zoom1, int zoom2, bool flipFl);
void copyRect(const byte *srcSurface, int x1, int y1, uint16 width, int height, byte *destSurface, int destX, int destY);
void drawVesaSprite(byte *surface, const byte *spriteData, int xp, int yp, int spriteIndex);
void display8BitRect(const byte *surface, int xs, int ys, int width, int height, int destX, int destY);
void fillSurface(byte *surface, byte *col, int size);
void displayScreen(bool initPalette);
void backupScreen();
void restoreScreen();
};
} // End of namespace Hopkins
#endif /* HOPKINS_GRAPHICS_H */

2925
engines/hopkins/hopkins.cpp Normal file

File diff suppressed because it is too large Load Diff

192
engines/hopkins/hopkins.h Normal file
View File

@@ -0,0 +1,192 @@
/* 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 HOPKINS_HOPKINS_H
#define HOPKINS_HOPKINS_H
#include "hopkins/anim.h"
#include "hopkins/computer.h"
#include "hopkins/debugger.h"
#include "hopkins/dialogs.h"
#include "hopkins/events.h"
#include "hopkins/files.h"
#include "hopkins/font.h"
#include "hopkins/globals.h"
#include "hopkins/graphics.h"
#include "hopkins/lines.h"
#include "hopkins/menu.h"
#include "hopkins/objects.h"
#include "hopkins/saveload.h"
#include "hopkins/script.h"
#include "hopkins/sound.h"
#include "hopkins/talk.h"
#include "common/scummsys.h"
#include "common/system.h"
#include "common/error.h"
#include "common/random.h"
#include "common/hash-str.h"
#include "common/util.h"
#include "engines/engine.h"
#include "graphics/surface.h"
/**
* This is the namespace of the Hopkins engine.
*
* Status of this engine: In Development
*
* Games using this engine:
* - Hopkins FBI
*/
namespace Hopkins {
#define SCREEN_WIDTH 640
#define SCREEN_HEIGHT 480
enum HOPKINSAction {
kActionNone,
kActionEscape,
kActionInventory,
kActionSave,
kActionLoad,
kActionOptions
};
enum HopkinsDebugChannels {
kDebugPath = 1,
kDebugGraphics,
};
/**
* A wrapper macro used around three character constants, like 'END', to
* ensure portability. Typical usage: MKTAG24('E','N','D').
*/
#define MKTAG24(a0,a1,a2) ((uint32)((a2) | (a1) << 8 | ((a0) << 16)))
struct HopkinsGameDescription;
class HopkinsEngine : public Engine {
private:
const HopkinsGameDescription *_gameDescription;
Common::RandomSource _randomSource;
void initializeSystem();
void displayNotAvailable();
void restoreSystem();
void endLinuxDemo();
void displayEndDemo();
void bombExplosion();
void handleConflagration();
void playSubmarineCutscene();
void playUnderwaterBaseCutscene();
void playPlaneCutscene();
void playEnding();
bool isUnderwaterSubScene();
/**
* Displays the map screen in the underground base.
*/
int handleBaseMap();
/**
* Loads the base map from the PBASE file
*/
void loadBaseMap();
/**
* Draws a simple base map for the Windows version, which implemented a 'Wolfenstein 3D'
* style shooter for the base, rather than having a map
*/
void drawBaseMap();
void handleOceanMouseEvents();
void setSubmarineSprites();
void handleOceanMaze(int16 curExitId, const Common::Path &backgroundFilename, Directions defaultDirection, int16 exit1, int16 exit2, int16 exit3, int16 exit4, int16 soundId);
void loadCredits();
void displayCredits(int startPosY, byte *buffer, char color);
void displayCredits();
void handleNotAvailable(int nextScreen);
bool runWin95Demo();
bool runLinuxDemo();
bool runFull();
/**
* Show warning screen about the game being adults only.
*/
bool displayAdultDisclaimer();
protected:
// Engine APIs
Common::Error run() override;
bool hasFeature(EngineFeature f) const override;
public:
AnimationManager *_animMan;
ComputerManager *_computer;
DialogsManager *_dialog;
EventsManager *_events;
FileManager *_fileIO;
FontManager *_fontMan;
Globals *_globals;
GraphicsManager *_graphicsMan;
LinesManager *_linesMan;
MenuManager *_menuMan;
ObjectsManager *_objectsMan;
SaveLoadManager *_saveLoad;
ScriptManager *_script;
SoundManager *_soundMan;
TalkManager *_talkMan;
public:
HopkinsEngine(OSystem *syst, const HopkinsGameDescription *gameDesc);
~HopkinsEngine() override;
void GUIError(const Common::String &msg);
uint32 getFeatures() const;
Common::Language getLanguage() const;
Common::Platform getPlatform() const;
uint16 getVersion() const;
bool getIsDemo() const;
const Common::String &getTargetName() const;
int getRandomNumber(int maxNumber);
bool canLoadGameStateCurrently(Common::U32String *msg = nullptr) override;
bool canSaveGameStateCurrently(Common::U32String *msg = nullptr) override;
Common::Error loadGameState(int slot) override;
Common::Error saveGameState(int slot, const Common::String &desc, bool isAutosave = false) override;
int _startGameSlot;
/**
* Run the introduction sequence
*/
void playIntro();
/**
* Synchronizes the sound settings from ScummVM into the engine
*/
void syncSoundSettings() override;
};
} // End of namespace Hopkins
#endif /* HOPKINS_HOPKINS_H */

2952
engines/hopkins/lines.cpp Normal file

File diff suppressed because it is too large Load Diff

197
engines/hopkins/lines.h Normal file
View File

@@ -0,0 +1,197 @@
/* 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 HOPKINS_LINES_H
#define HOPKINS_LINES_H
#include "hopkins/globals.h"
#include "common/scummsys.h"
#include "common/str.h"
namespace Hopkins {
class HopkinsEngine;
struct LigneZoneItem {
int _count;
int _bobZoneIdx;
int16 *_zoneData;
};
#define INVALID_LINE_VALUE 1300
#define MAX_LINES 400
struct RouteItem;
struct LigneItem {
int _lineDataEndIdx;
Directions _direction;
Directions _directionRouteInc;
Directions _directionRouteDec;
int16 *_lineData;
int appendToRouteInc(int from, int to, RouteItem *route, int index);
int appendToRouteDec(int from, int to, RouteItem *route, int index);
};
struct SmoothItem {
int _posX;
int _posY;
};
struct SegmentItem {
int _minZoneLineIdx;
int _maxZoneLineIdx;
};
struct SquareZoneItem {
bool _enabledFl;
int _left;
int _right;
int _top;
int _bottom;
int _minZoneLineIdx;
int _maxZoneLineIdx;
bool _squareZoneFl;
};
struct ZoneItem {
int _destX;
int _destY;
int _spriteIndex;
int _verbFl1;
int _verbFl2;
int _verbFl3;
int _verbFl4;
int _verbFl5;
int _verbFl6;
int _verbFl7;
int _verbFl8;
int _verbFl9;
int _verbFl10;
bool _enabledFl;
int _messageId;
};
struct RouteItem {
int16 _x;
int16 _y;
Directions _dir;
bool isValid() const { return _x != -1 || _y != -1; }
void invalidate() { _x = _y = -1; _dir = DIR_NONE; }
void set(int16 X, int16 Y, Directions dir) { _x = X; _y = Y; _dir = dir; }
};
class LinesManager {
private:
HopkinsEngine *_vm;
bool _forceHideText;
int _hotspotTextColor;
int _pathFindingMaxDepth;
SmoothItem _smoothRoute[4000];
Directions _smoothMoveDirection;
LigneZoneItem _zoneLine[MAX_LINES+1];
SegmentItem _segment[101];
int _currentSegmentId;
int _maxLineIdx;
int _lastLine;
int _newLineIdx;
int _newLineDataIdx;
int _newRouteIdx;
int _newPosX;
int _newPosY;
int _oldMouseX, _oldMouseY;
int _oldRouteFromX;
int _oldRouteFromY;
int _oldRouteDestX;
int _oldRouteDestY;
int _oldZoneNum;
byte *_largeBuf;
RouteItem *_testRoute0;
RouteItem *_testRoute1;
int16 *_lineBuf;
RouteItem _bestRoute[8001];
int _zoneSkipCount;
int _oldMouseZoneId;
int avoidObstacle(int lineIdx, int lineDataIdx, int routeIdx, int destLineIdx, int destLineDataIdx, RouteItem *route);
int avoidObstacleOnSegment(int lineIdx, int lineDataIdx, int routeIdx, int destLineIdx, int destLineDataIdx, RouteItem *route, int startLineIdx, int endLineIdx);
int checkInventoryHotspotsRow(int posX, int minZoneNum, bool lastRow);
void removeZoneLine(int idx);
void removeLine(int idx);
int checkCollision(int xp, int yp);
bool checkCollisionLine(int xp, int yp, int *foundDataIdx, int *foundLineIdx, int startLineIdx, int endLineIdx);
bool checkSmoothMove(int fromX, int fromY, int destX, int destY);
bool makeSmoothMove(int fromX, int fromY, int destX, int destY);
int characterRoute(int fromX, int fromY, int destX, int destY, int startLineIdx, int endLineIdx, int routeIdx);
int testLine(int paramX, int paramY, int *testValue, int *foundLineIdx, int *foundDataIdx);
void useRoute0(int idx, int curRouteIdx);
void useRoute1(int idx, int curRouteIdx);
void useRoute2(int idx, int curRouteIdx);
int computeYSteps(int idx);
int computeRouteIdx(int lineIdx, int dataIdx, int fromX, int fromY, int destX, int destY, int routerIdx, RouteItem *route);
bool MIRACLE(int fromX, int fromY, int lineIdx, int destLineIdx, int routeIdx);
bool PLAN_TEST(int paramX, int paramY, int superRouteIdx, int paramStartLineIdx, int paramEndLineIdx);
public:
RouteItem *_route;
RouteItem *_testRoute2;
int _bobZone[105];
bool _bobZoneFl[105];
ZoneItem _zone[106];
SquareZoneItem _squareZone[101];
LigneItem _lineItem[MAX_LINES];
int _linesNumb;
LinesManager(HopkinsEngine *vm);
~LinesManager();
void clearAll();
void setMaxLineIdx(int idx);
int checkInventoryHotspots(int posX, int posY);
void addZoneLine(int idx, int fromX, int fromY, int destX, int destY, int bobZoneIdx);
void loadLines(const Common::Path &file);
void addLine(int lineIdx, Directions direction, int fromX, int fromY, int destX, int destY);
void initRoute();
RouteItem *findRoute(int fromX, int fromY, int destX, int destY);
RouteItem *cityMapCarRoute(int x1, int y1, int x2, int y2);
void clearAllZones();
void initSquareZones();
void resetLines();
void resetLinesNumb();
void resetLastLine();
void enableZone(int idx);
void disableZone(int idx);
void checkZone();
int getMouseZone();
void optimizeRoute(RouteItem *route);
};
} // End of namespace Hopkins
#endif /* HOPKINS_FONT_H */

185
engines/hopkins/menu.cpp Normal file
View File

@@ -0,0 +1,185 @@
/* 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 "hopkins/menu.h"
#include "hopkins/dialogs.h"
#include "hopkins/files.h"
#include "hopkins/hopkins.h"
#include "hopkins/globals.h"
#include "hopkins/events.h"
#include "hopkins/graphics.h"
#include "hopkins/sound.h"
#include "common/scummsys.h"
#include "common/events.h"
#include "common/file.h"
#include "common/util.h"
namespace Hopkins {
enum MenuSelection { MENU_NONE = 0, PLAY_GAME = 1, LOAD_GAME = 2, OPTIONS = 3, INTRODUCTION = 4, QUIT = 5 };
MenuManager::MenuManager(HopkinsEngine *vm) {
_vm = vm;
}
int MenuManager::menu() {
byte *spriteData = nullptr;
MenuSelection menuIndex;
Common::Point mousePos;
signed int result;
int frameIndex[] = { 0, 0, 0, 0, 0 };
if (_vm->shouldQuit())
return -1;
result = 0;
while (!_vm->shouldQuit()) {
_vm->_objectsMan->_forestFl = false;
_vm->_events->_breakoutFl = false;
_vm->_globals->_disableInventFl = true;
_vm->_globals->_exitId = 0;
for (int idx = 0; idx < 31; ++idx)
_vm->_globals->_inventory[idx] = 0;
memset(_vm->_globals->_saveData->_data, 0, 2050-50);
_vm->_objectsMan->addObject(14);
memset(frameIndex, 0, sizeof(int) * ARRAYSIZE(frameIndex));
if (_vm->getPlatform() == Common::kPlatformOS2 || _vm->getPlatform() == Common::kPlatformBeOS)
_vm->_graphicsMan->loadImage("MENU");
else {
switch (_vm->_globals->_language) {
case LANG_EN:
_vm->_graphicsMan->loadImage("MENUAN");
break;
case LANG_FR:
_vm->_graphicsMan->loadImage("MENUFR");
break;
case LANG_SP:
_vm->_graphicsMan->loadImage("MENUES");
break;
default:
break;
}
}
_vm->_graphicsMan->fadeInLong();
if (_vm->getPlatform() == Common::kPlatformOS2 || _vm->getPlatform() == Common::kPlatformBeOS)
spriteData = _vm->_objectsMan->loadSprite("MENU.SPR");
else {
switch (_vm->_globals->_language) {
case LANG_EN:
spriteData = _vm->_objectsMan->loadSprite("MENUAN.SPR");
break;
case LANG_FR:
spriteData = _vm->_objectsMan->loadSprite("MENUFR.SPR");
break;
case LANG_SP:
spriteData = _vm->_objectsMan->loadSprite("MENUES.SPR");
break;
default:
break;
}
}
_vm->_events->mouseOn();
_vm->_events->changeMouseCursor(0);
_vm->_events->_mouseCursorId = 0;
_vm->_events->_mouseSpriteId = 0;
_vm->_soundMan->playSound(28);
// Loop to make menu selection
bool selectionMade = false;
do {
if (_vm->shouldQuit())
return -1;
menuIndex = MENU_NONE;
mousePos = Common::Point(_vm->_events->getMouseX(), _vm->_events->getMouseY());
if (mousePos.x >= 232 && mousePos.x <= 408) {
if (mousePos.y >= 261 && mousePos.y <= 284)
menuIndex = PLAY_GAME;
else if (mousePos.y >= 293 && mousePos.y <= 316)
menuIndex = LOAD_GAME;
else if (mousePos.y >= 325 && mousePos.y <= 347)
menuIndex = OPTIONS;
else if (mousePos.y >= 356 && mousePos.y <= 379)
menuIndex = INTRODUCTION;
else if (mousePos.y >= 388 && mousePos.y <= 411)
menuIndex = QUIT;
}
memset(frameIndex, 0, sizeof(int) * ARRAYSIZE(frameIndex));
if (menuIndex > MENU_NONE)
frameIndex[menuIndex - 1] = 1;
_vm->_graphicsMan->fastDisplay(spriteData, 230, 259, frameIndex[0]);
_vm->_graphicsMan->fastDisplay(spriteData, 230, 291, frameIndex[1] + 2);
_vm->_graphicsMan->fastDisplay(spriteData, 230, 322, frameIndex[2] + 4);
_vm->_graphicsMan->fastDisplay(spriteData, 230, 354, frameIndex[3] + 6);
_vm->_graphicsMan->fastDisplay(spriteData, 230, 386, frameIndex[4] + 8);
_vm->_events->refreshScreenAndEvents();
if (_vm->_events->getMouseButton() == 1 && menuIndex != MENU_NONE)
selectionMade = true;
} while (!selectionMade);
if (menuIndex > MENU_NONE) {
_vm->_graphicsMan->fastDisplay(spriteData, 230, 259 + 32 * (menuIndex - 1), 10 + (menuIndex - 1));
_vm->_events->refreshScreenAndEvents();
_vm->_events->delay(200);
}
if (menuIndex == PLAY_GAME) {
result = 1;
break;
} else if (menuIndex == LOAD_GAME) {
_vm->_globals->_exitId = -1;
_vm->_dialog->showLoadGame();
if (_vm->_globals->_exitId != -1) {
result = _vm->_globals->_exitId;
break;
}
_vm->_globals->_exitId = 0;
} else if (menuIndex == OPTIONS) {
_vm->_dialog->showOptionsDialog();
} else if (menuIndex == INTRODUCTION) {
_vm->playIntro();
} else if (menuIndex == QUIT) {
result = -1;
break;
}
}
_vm->_globals->freeMemory(spriteData);
_vm->_globals->_disableInventFl = false;
_vm->_graphicsMan->fadeOutLong();
return result;
}
} // End of namespace Hopkins

45
engines/hopkins/menu.h Normal file
View File

@@ -0,0 +1,45 @@
/* 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 HOPKINS_MENU_H
#define HOPKINS_MENU_H
#include "common/scummsys.h"
#include "common/system.h"
#include "common/error.h"
namespace Hopkins {
class HopkinsEngine;
class MenuManager {
private:
HopkinsEngine *_vm;
public:
MenuManager(HopkinsEngine *vm);
int menu();
};
} // End of namespace Hopkins
#endif /* HOPKINS_MENU_H */

View File

@@ -0,0 +1,268 @@
/* 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 "hopkins/hopkins.h"
#include "base/plugins.h"
#include "common/savefile.h"
#include "common/str-array.h"
#include "common/translation.h"
#include "common/memstream.h"
#include "engines/advancedDetector.h"
#include "common/system.h"
#include "graphics/surface.h"
#include "backends/keymapper/action.h"
#include "backends/keymapper/keymapper.h"
#include "backends/keymapper/standard-actions.h"
#include "hopkins/detection.h"
#define MAX_SAVES 99
namespace Hopkins {
static const ADExtraGuiOptionsMap optionsList[] = {
{
GAMEOPTION_GORE_DEFAULT_OFF,
{
_s("Gore Mode"),
_s("Enable Gore Mode when available"),
"enable_gore",
false,
0,
0
}
},
{
GAMEOPTION_GORE_DEFAULT_ON,
{
_s("Gore Mode"),
_s("Enable Gore Mode when available"),
"enable_gore",
true,
0,
0
}
},
AD_EXTRA_GUI_OPTIONS_TERMINATOR
};
uint32 HopkinsEngine::getFeatures() const {
return _gameDescription->desc.flags;
}
Common::Language HopkinsEngine::getLanguage() const {
return _gameDescription->desc.language;
}
Common::Platform HopkinsEngine::getPlatform() const {
return _gameDescription->desc.platform;
}
bool HopkinsEngine::getIsDemo() const {
return _gameDescription->desc.flags & ADGF_DEMO;
}
const Common::String &HopkinsEngine::getTargetName() const {
return _targetName;
}
} // End of namespace Hopkins
class HopkinsMetaEngine : public AdvancedMetaEngine<Hopkins::HopkinsGameDescription> {
public:
const char *getName() const override {
return "hopkins";
}
const ADExtraGuiOptionsMap *getAdvancedExtraGuiOptions() const override {
return Hopkins::optionsList;
}
bool hasFeature(MetaEngineFeature f) const override;
Common::Error createInstance(OSystem *syst, Engine **engine, const Hopkins::HopkinsGameDescription *desc) 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 HopkinsMetaEngine::hasFeature(MetaEngineFeature f) const {
return
(f == kSupportsListSaves) ||
(f == kSupportsLoadingDuringStartup) ||
(f == kSupportsDeleteSave) ||
(f == kSavesSupportMetaInfo) ||
(f == kSavesSupportThumbnail) ||
(f == kSimpleSavesNames);
}
bool Hopkins::HopkinsEngine::hasFeature(EngineFeature f) const {
return
(f == kSupportsReturnToLauncher) ||
(f == kSupportsLoadingDuringRuntime) ||
(f == kSupportsSavingDuringRuntime);
}
Common::Error HopkinsMetaEngine::createInstance(OSystem *syst, Engine **engine, const Hopkins::HopkinsGameDescription *desc) const {
*engine = new Hopkins::HopkinsEngine(syst,desc);
return Common::kNoError;
}
SaveStateList HopkinsMetaEngine::listSaves(const char *target) const {
Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
Common::String saveDesc;
Common::String pattern = Common::String::format("%s.0##", target);
Common::StringArray filenames = saveFileMan->listSavefiles(pattern);
Hopkins::hopkinsSavegameHeader header;
SaveStateList saveList;
for (const auto &filename : filenames) {
const char *ext = strrchr(filename.c_str(), '.');
int slot = ext ? atoi(ext + 1) : -1;
if (slot >= 0 && slot < MAX_SAVES) {
Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(filename);
if (in) {
if (Hopkins::SaveLoadManager::readSavegameHeader(in, header)) {
saveList.push_back(SaveStateDescriptor(this, slot, header._saveName));
}
delete in;
}
}
}
// Sort saves based on slot number.
Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
return saveList;
}
int HopkinsMetaEngine::getMaximumSaveSlot() const {
return MAX_SAVES;
}
bool HopkinsMetaEngine::removeSaveState(const char *target, int slot) const {
Common::String filename = Common::String::format("%s.%03d", target, slot);
return g_system->getSavefileManager()->removeSavefile(filename);
}
SaveStateDescriptor HopkinsMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
Common::String filename = Common::String::format("%s.%03d", target, slot);
Common::InSaveFile *f = g_system->getSavefileManager()->openForLoading(filename);
if (f) {
Hopkins::hopkinsSavegameHeader header;
if (!Hopkins::SaveLoadManager::readSavegameHeader(f, header, false)) {
delete f;
return SaveStateDescriptor();
}
delete f;
// Create the return descriptor
SaveStateDescriptor desc(this, slot, header._saveName);
desc.setThumbnail(header._thumbnail);
desc.setSaveDate(header._year, header._month, header._day);
desc.setSaveTime(header._hour, header._minute);
desc.setPlayTime(header._totalFrames * GAME_FRAME_TIME);
return desc;
}
return SaveStateDescriptor();
}
Common::KeymapArray HopkinsMetaEngine::initKeymaps(const char *target) const {
using namespace Common;
using namespace Hopkins;
Keymap *engineKeyMap = new Keymap(Keymap::kKeymapTypeGame, "hopkins-default", _("Default keymappings"));
Keymap *gameKeyMap = new Keymap(Keymap::kKeymapTypeGame, "game-shortcuts", _("Game keymappings"));
Action *act;
act = new Action(kStandardActionLeftClick, _("Left click"));
act->setLeftClickEvent();
act->addDefaultInputMapping("MOUSE_LEFT");
act->addDefaultInputMapping("JOY_A");
engineKeyMap->addAction(act);
act = new Action(kStandardActionRightClick, _("Right click"));
act->setRightClickEvent();
act->addDefaultInputMapping("MOUSE_RIGHT");
act->addDefaultInputMapping("JOY_B");
engineKeyMap->addAction(act);
act = new Action("ESCAPE", _("Exit / Skip"));
act->setCustomEngineActionEvent(kActionEscape);
act->addDefaultInputMapping("ESCAPE");
act->addDefaultInputMapping("JOY_BACK");
gameKeyMap->addAction(act);
act = new Action("INVENTORY", _("Open inventory"));
act->setCustomEngineActionEvent(kActionInventory);
act->addDefaultInputMapping("i");
act->addDefaultInputMapping("TAB");
act->addDefaultInputMapping("JOY_X");
gameKeyMap->addAction(act);
act = new Action("SAVE", _("Save game"));
act->setCustomEngineActionEvent(kActionSave);
act->addDefaultInputMapping("F5");
act->addDefaultInputMapping("JOY_LEFT_SHOULDER");
gameKeyMap->addAction(act);
act = new Action("LOAD", _("Load game"));
act->setCustomEngineActionEvent(kActionLoad);
act->addDefaultInputMapping("F7");
act->addDefaultInputMapping("JOY_RIGHT_SHOULDER");
gameKeyMap->addAction(act);
act = new Action("OPTIONS", _("Options menu"));
act->setCustomEngineActionEvent(kActionOptions);
act->addDefaultInputMapping("o");
act->addDefaultInputMapping("F1");
act->addDefaultInputMapping("JOY_Y");
gameKeyMap->addAction(act);
KeymapArray keymaps(2);
keymaps[0] = engineKeyMap;
keymaps[1] = gameKeyMap;
return keymaps;
}
#if PLUGIN_ENABLED_DYNAMIC(HOPKINS)
REGISTER_PLUGIN_DYNAMIC(HOPKINS, PLUGIN_TYPE_ENGINE, HopkinsMetaEngine);
#else
REGISTER_PLUGIN_STATIC(HOPKINS, PLUGIN_TYPE_ENGINE, HopkinsMetaEngine);
#endif

32
engines/hopkins/module.mk Normal file
View File

@@ -0,0 +1,32 @@
MODULE := engines/hopkins
MODULE_OBJS := \
anim.o \
computer.o \
debugger.o \
dialogs.o \
events.o \
files.o \
font.o \
graphics.o \
globals.o \
hopkins.o \
lines.o \
menu.o \
metaengine.o \
objects.o \
saveload.o \
script.o \
sound.o \
talk.o
# This module can be built as a plugin
ifeq ($(ENABLE_HOPKINS), DYNAMIC_PLUGIN)
PLUGIN := 1
endif
# Include common rules
include $(srcdir)/rules.mk
# Detection objects
DETECT_OBJS += $(MODULE)/detection.o

4084
engines/hopkins/objects.cpp Normal file

File diff suppressed because it is too large Load Diff

337
engines/hopkins/objects.h Normal file
View File

@@ -0,0 +1,337 @@
/* 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 HOPKINS_OBJECTS_H
#define HOPKINS_OBJECTS_H
#include "hopkins/globals.h"
#include "common/scummsys.h"
#include "common/endian.h"
#include "common/str.h"
#define MAX_SPRITE 5
namespace Hopkins {
struct ObjectAuthIcon {
byte _objectFileNum;
byte _idx;
byte _flag1;
byte _flag2;
byte _flag3;
byte _flag4;
byte _flag5;
byte _flag6;
};
struct SpriteItem {
int _animationType;
const byte *_spriteData;
Common::Point _spritePos;
int _zoomFactor;
bool _flipFl;
int _spriteIndex;
int _deltaX;
int _deltaY;
bool _rleFl;
bool _activeFl;
int _destX;
int _destY;
int _width;
int _height;
int _zoomPct;
int _reducePct;
};
struct BobItem {
int _bobMode;
byte *_spriteData;
int _xp;
int _yp;
int _frameIndex;
int _animDataIdx;
int _moveChange1;
int _moveChange2;
bool _disabledAnimationFl;
byte *_animData;
bool _bobMode10;
int _bobModeChange;
int _modeChangeCtr;
int _modeChangeUnused;
bool _disableFl; // Set to true in B_CACHE_OFF()
int _zoomFactor;
bool _flipFl;
bool _isSpriteFl;
bool _activeFl;
int _oldX;
int _oldY;
int _oldWidth;
int _oldHeight;
int _oldX2;
int _zooInmFactor;
int _zoomOutFactor;
};
struct HidingItem {
int _x;
int _y;
int _spriteIndex;
int _width;
int _height;
int _useCount;
byte *_spriteData;
bool _resetUseCount;
int _yOffset;
};
struct LockAnimItem {
bool _enableFl;
int _posX;
};
struct VBobItem {
const byte *_spriteData;
int _displayMode;
int _xp;
int _yp;
int _frameIndex;
byte *_surface;
int _oldX;
int _oldY;
int _oldFrameIndex;
const byte *_oldSpriteData;
};
struct ListeItem {
bool _visibleFl;
int _posX;
int _posY;
int _width;
int _height;
};
/**
* Mode for SortItem records
*/
enum SortMode { SORT_NONE = 0, SORT_BOB = 1, SORT_SPRITE = 2, SORT_HIDING = 3 };
/**
* Structure to represent a pending display of either a Bob, Sprite, or Cache Item.
*/
struct SortItem {
SortMode _sortMode;
int _index;
int _priority;
};
class HopkinsEngine;
class ObjectsManager {
private:
HopkinsEngine *_vm;
int _objectWidth, _objectHeight;
int _oldBorderSpriteIndex;
int _borderSpriteIndex;
byte *_spritePtr;
const byte *_oldSpriteData;
int _verb;
int _oldSpriteIndex;
int _oldFrameIndex;
int _oldDirectionSpriteIdx;
Directions _oldDirection;
Directions _lastDirection;
bool _oldFlipFl;
int _curGestureFile;
byte *_gestureBuf;
int _homeRateCounter;
int _sortedDisplayCount;
SortItem _sortedDisplay[51];
byte *_hidingItemData[6];
HidingItem _hidingItem[25];
bool _hidingActiveFl;
ObjectAuthIcon _objectAuthIcons[300];
int _curObjectFileNum;
byte *_objectDataBuf;
VBobItem _vBob[30];
ListeItem _liste[6];
ListeItem _liste2[35];
void initVBob();
void clearVBob();
void sprite_alone(const byte *objectData, byte *sprite, int objIndex);
void removeObjectDataBuf();
int getOffsetX(const byte *spriteData, int spriteIndex, bool isSize);
int getOffsetY(const byte *spriteData, int spriteIndex, bool isSize);
void capture_mem_sprite(const byte *objectData, byte *sprite, int objIndex);
void setBobInfo(int idx);
void computeHideCounter(int idx);
void initBobVariables(int idx);
void checkHidingItem();
void displayHiding(int idx);
void computeSprite(int idx);
void beforeSort(SortMode sortMode, int index, int priority);
void displayBobAnim();
void displayVBob();
void showSprite(int idx);
void clearSprite();
void setSpriteZoom(int idx, int zoomFactor);
void loadZone(const Common::Path &file);
void changeCharacterHead(PlayerCharacter oldCharacter, PlayerCharacter newCharacter);
void goHome2();
void nextVerbIcon();
void handleForest(int screenId, int minX, int maxX, int minY, int maxY, int idx);
void sceneSpecialIni();
void showActionAnimation(const byte *spriteData, const Common::String &actionStr, int speed, bool flipFl);
public:
bool _disableFl;
bool _forestFl;
bool _visibleFl;
bool _saveLoadFl;
bool _forceZoneFl;
bool _changeVerbFl;
bool _helicopterFl;
bool _twoCharactersFl;
bool _changeHeadFl;
bool _priorityFl;
int _jumpVerb;
int _jumpZone;
int _zoneNum;
int _eraseVisibleCounter;
int _curObjectIndex;
int _startSpriteIndex;
int _saveLoadX, _saveLoadY;
int _mapCarPosX, _mapCarPosY;
int _oldCharacterPosX, _oldCharacterPosY;
Common::Point _borderPos;
Common::Point _oldBorderPos;
Common::Point _characterPos;
byte *_forestSprite;
byte *_saveLoadSprite;
byte *_saveLoadSprite2;
byte *_headSprites;
SpriteItem _sprite[6];
BobItem _bob[36];
LockAnimItem _lockedAnims[36];
bool _charactersEnabledFl;
bool _refreshBobMode10Fl;
ObjectsManager(HopkinsEngine *vm);
~ObjectsManager();
void clearAll();
int getWidth(const byte *objectData, int idx);
int getHeight(const byte *objectData, int idx);
byte *loadSprite(const Common::Path &file);
void loadLinkFile(const Common::Path &file, bool OBSSEUL = false);
void addStaticSprite(const byte *spriteData, Common::Point pos, int idx, int spriteIndex, int zoomFactor, bool flipFl, int deltaX, int deltaY);
void animateSprite(int idx);
void removeSprite(int idx);
void setSpriteX(int idx, int xp);
void setSpriteY(int idx, int yp);
int getSpriteX(int idx);
int getSpriteY(int idx);
void setSpriteIndex(int idx, int spriteIndex);
void displaySprite();
void computeAndSetSpriteSize();
void setFlipSprite(int idx, bool flip);
int getBobAnimDataIdx(int idx);
void initBorder(int zoneIdx);
void nextObjectIcon(int idx);
void takeInventoryObject(int idx);
void handleSpecialGames();
void addObject(int objIndex);
void changeObject(int objIndex);
void removeObject(int objIndex);
void resetBob(int idx);
void hideBob(int idx);
void displayBob(int idx);
void setBobOffset(int idx, int offset);
void setBobAnimDataIdx(int idx, int animIdx);
void setBobAnimation(int idx);
void stopBobAnimation(int idx);
int getBobPosX(int idx);
void handleCityMap();
void clearScreen();
void disableVerb(int idx, int a2);
void enableVerb(int idx, int a2);
void lockAnimX(int idx, int x);
void handleLeftButton();
void handleRightButton();
void setOffsetXY(byte *data, int idx, int xp, int yp, bool isSize);
void setVerb(int id);
void doActionBack(int idx);
void doActionRight(int idx);
void doActionFront(int idx);
void doActionLeft(int idx);
void doActionDiagRight(int idx);
void doActionDiagLeft(int idx);
void loadObjects();
byte *loadObjectFromFile(int objIndex, bool mode);
void resetHidingItems();
void resetHidingUseCount(int idx);
void setHidingUseCount(int idx);
void loadHidingItems(const Common::Path &file);
void enableHidingBehavior();
void disableHidingBehavior();
void disableHidingItem(int idx);
void resetHomeRateCounter() { _homeRateCounter = 0; }
void resetOldFrameIndex() { _oldFrameIndex = -1; }
void resetOldDirection() { _oldDirection = DIR_NONE; }
int getObjectWidth() { return _objectWidth; }
int getObjectHeight() { return _objectHeight; }
void showSpecialActionAnimationWithFlip(const byte *spriteData, const Common::String &animationSeq, int speed, bool flipFl);
void showSpecialActionAnimation(const byte *spriteData, const Common::String &animString, int speed);
void checkEventBobAnim(int idx, int animIdx, int animDataIdx, int a4);
void setMultiBobAnim(int idx1, int idx2, int anim1Idx, int anim2Idx);
void loadObjectIniFile();
void quickDisplayBobSprite(int idx);
void initVbob(const byte *src, int idx, int xp, int yp, int frameIndex);
void disableVbob(int idx);
void setAndPlayAnim(int idx, int animIdx, int destPosi, bool animAction);
void sceneControl(const Common::Path &backgroundFile, const Common::Path &linkFile,
const Common::Path &animFile, const Common::Path &s4, int soundNum, bool initializeScreen);
void sceneControl2(const Common::Path &backgroundFile, const Common::Path &linkFile,
const Common::Path &animFile, const Common::Path &s4, int soundNum, bool initializeScreen);
void goHome();
void paradise();
};
} // End of namespace Hopkins
#endif /* HOPKINS_OBJECTS_H */

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 "hopkins/saveload.h"
#include "hopkins/files.h"
#include "hopkins/globals.h"
#include "hopkins/hopkins.h"
#include "common/system.h"
#include "common/savefile.h"
#include "graphics/surface.h"
#include "graphics/scaler.h"
#include "graphics/thumbnail.h"
namespace Hopkins {
const char *SAVEGAME_STR = "HOPKINS";
#define SAVEGAME_STR_SIZE 13
SaveLoadManager::SaveLoadManager(HopkinsEngine *vm) {
_vm = vm;
}
bool SaveLoadManager::save(const Common::String &file, const void *buf, size_t n) {
Common::OutSaveFile *savefile = g_system->getSavefileManager()->openForSaving(file);
if (savefile) {
size_t bytesWritten = savefile->write(buf, n);
savefile->finalize();
delete savefile;
return bytesWritten == n;
} else
return false;
}
bool SaveLoadManager::saveExists(const Common::String &file) {
Common::InSaveFile *savefile = g_system->getSavefileManager()->openForLoading(file);
bool result = savefile != nullptr;
delete savefile;
return result;
}
// Save File
bool SaveLoadManager::saveFile(const Common::String &file, const void *buf, size_t n) {
return save(file, buf, n);
}
void SaveLoadManager::load(const Common::String &file, byte *buf) {
Common::InSaveFile *savefile = g_system->getSavefileManager()->openForLoading(file);
if (savefile == nullptr)
error("Error opening file - %s", file.c_str());
int32 filesize = savefile->size();
savefile->read(buf, filesize);
delete savefile;
}
WARN_UNUSED_RESULT bool SaveLoadManager::readSavegameHeader(Common::InSaveFile *in, hopkinsSavegameHeader &header, bool skipThumbnail) {
char saveIdentBuffer[SAVEGAME_STR_SIZE + 1];
// Validate the header Id
in->read(saveIdentBuffer, SAVEGAME_STR_SIZE + 1);
if (strncmp(saveIdentBuffer, SAVEGAME_STR, SAVEGAME_STR_SIZE))
return false;
header._version = in->readByte();
if (header._version > HOPKINS_SAVEGAME_VERSION)
return false;
// Read in the string
header._saveName.clear();
char ch;
while ((ch = (char)in->readByte()) != '\0') header._saveName += ch;
// Get the thumbnail
if (!Graphics::loadThumbnail(*in, header._thumbnail, skipThumbnail)) {
return false;
}
// Read in save date/time
header._year = in->readSint16LE();
header._month = in->readSint16LE();
header._day = in->readSint16LE();
header._hour = in->readSint16LE();
header._minute = in->readSint16LE();
header._totalFrames = in->readUint32LE();
return true;
}
void SaveLoadManager::writeSavegameHeader(Common::OutSaveFile *out, hopkinsSavegameHeader &header) {
// Write out a savegame header
out->write(SAVEGAME_STR, SAVEGAME_STR_SIZE + 1);
out->writeByte(HOPKINS_SAVEGAME_VERSION);
// Write savegame name
out->write(header._saveName.c_str(), header._saveName.size() + 1);
// Create a thumbnail and save it
Graphics::Surface *thumb = new Graphics::Surface();
createThumbnail(thumb);
Graphics::saveThumbnail(*out, *thumb);
thumb->free();
delete thumb;
// Write out the save date/time
TimeDate td;
g_system->getTimeAndDate(td);
out->writeSint16LE(td.tm_year + 1900);
out->writeSint16LE(td.tm_mon + 1);
out->writeSint16LE(td.tm_mday);
out->writeSint16LE(td.tm_hour);
out->writeSint16LE(td.tm_min);
out->writeUint32LE(_vm->_events->_gameCounter);
}
Common::Error SaveLoadManager::saveGame(int slot, const Common::String &saveName) {
/* Pack any necessary data into the savegame data structure */
// Set the selected slot number
_vm->_globals->_saveData->_data[svLastSavegameSlot] = slot;
// Set up the inventory
for (int i = 0; i < 35; ++i)
_vm->_globals->_saveData->_inventory[i] = _vm->_globals->_inventory[i];
_vm->_globals->_saveData->_mapCarPosX = _vm->_objectsMan->_mapCarPosX;
_vm->_globals->_saveData->_mapCarPosY = _vm->_objectsMan->_mapCarPosY;
/* Create the savegame */
Common::OutSaveFile *savefile = g_system->getSavefileManager()->openForSaving(
_vm->getSaveStateName(slot));
if (!savefile)
return Common::kCreatingFileFailed;
// Set up the serializer
Common::Serializer serializer(nullptr, savefile);
// Write out the savegame header
hopkinsSavegameHeader header;
header._saveName = saveName;
header._version = HOPKINS_SAVEGAME_VERSION;
writeSavegameHeader(savefile, header);
// Write out the savegame data
syncSavegameData(serializer, header._version);
// Save file complete
savefile->finalize();
delete savefile;
return Common::kNoError;
}
Common::Error SaveLoadManager::loadGame(int slot) {
// Try and open the save file for reading
Common::InSaveFile *savefile = g_system->getSavefileManager()->openForLoading(
_vm->getSaveStateName(slot));
if (!savefile)
return Common::kReadingFailed;
// Set up the serializer
Common::Serializer serializer(savefile, nullptr);
// Read in the savegame header
hopkinsSavegameHeader header;
if (!readSavegameHeader(savefile, header)) {
delete savefile;
return Common::kReadingFailed;
}
// Read in the savegame data
syncSavegameData(serializer, header._version);
// Loading save file complete
delete savefile;
// Unpack the inventory
for (int i = 0; i < 35; ++i)
_vm->_globals->_inventory[i] = _vm->_globals->_saveData->_inventory[i];
// Set variables from loaded data as necessary
_vm->_globals->_saveData->_data[svLastSavegameSlot] = slot;
_vm->_globals->_exitId = _vm->_globals->_saveData->_data[svLastScreenId];
_vm->_globals->_saveData->_data[svLastPrevScreenId] = 0;
_vm->_globals->_screenId = 0;
_vm->_objectsMan->_mapCarPosX = _vm->_globals->_saveData->_mapCarPosX;
_vm->_objectsMan->_mapCarPosY = _vm->_globals->_saveData->_mapCarPosY;
return Common::kNoError;
}
WARN_UNUSED_RESULT bool SaveLoadManager::readSavegameHeader(int slot, hopkinsSavegameHeader &header, bool skipThumbnail) {
// Try and open the save file for reading
Common::InSaveFile *savefile = g_system->getSavefileManager()->openForLoading(
_vm->getSaveStateName(slot));
if (!savefile)
return false;
bool result = readSavegameHeader(savefile, header, skipThumbnail);
delete savefile;
return result;
}
#define REDUCE_AMOUNT 80
void SaveLoadManager::createThumbnail(Graphics::Surface *s) {
int w = _vm->_graphicsMan->zoomOut(SCREEN_WIDTH, REDUCE_AMOUNT);
int h = _vm->_graphicsMan->zoomOut(SCREEN_HEIGHT - 40, REDUCE_AMOUNT);
Graphics::Surface thumb8;
thumb8.create(w, h, Graphics::PixelFormat::createFormatCLUT8());
_vm->_graphicsMan->reduceScreenPart(_vm->_graphicsMan->_frontBuffer, (byte *)thumb8.getPixels(),
_vm->_events->_startPos.x, 20, SCREEN_WIDTH, SCREEN_HEIGHT - 40, 80);
// Convert the 8-bit pixel to 16 bit surface
s->create(w, h, Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0));
const byte *srcP = (const byte *)thumb8.getPixels();
uint16 *destP = (uint16 *)s->getPixels();
for (int yp = 0; yp < h; ++yp) {
// Copy over the line, using the source pixels as lookups into the pixels palette
const byte *lineSrcP = srcP;
uint16 *lineDestP = destP;
for (int xp = 0; xp < w; ++xp)
*lineDestP++ = *(uint16 *)&_vm->_graphicsMan->_palettePixels[*lineSrcP++ * 2];
// Move to the start of the next line
srcP += w;
destP += w;
}
thumb8.free();
}
void SaveLoadManager::syncSavegameData(Common::Serializer &s, int version) {
// The brief version 3 had the highscores embedded. They're in a separate file now, so skip
if (version == 3 && s.isLoading())
s.skip(100);
s.syncBytes(&_vm->_globals->_saveData->_data[0], 2050);
syncCharacterLocation(s, _vm->_globals->_saveData->_cloneHopkins);
syncCharacterLocation(s, _vm->_globals->_saveData->_realHopkins);
syncCharacterLocation(s, _vm->_globals->_saveData->_samantha);
for (int i = 0; i < 35; ++i)
s.syncAsSint16LE(_vm->_globals->_saveData->_inventory[i]);
if (version > 1) {
s.syncAsSint16LE(_vm->_globals->_saveData->_mapCarPosX);
s.syncAsSint16LE(_vm->_globals->_saveData->_mapCarPosY);
} else {
_vm->_globals->_saveData->_mapCarPosX = _vm->_globals->_saveData->_mapCarPosY = 0;
}
}
void SaveLoadManager::syncCharacterLocation(Common::Serializer &s, CharacterLocation &item) {
s.syncAsSint16LE(item._pos.x);
s.syncAsSint16LE(item._pos.y);
s.syncAsSint16LE(item._startSpriteIndex);
s.syncAsSint16LE(item._location);
s.syncAsSint16LE(item._zoomFactor);
}
void SaveLoadManager::convertThumb16To8(Graphics::Surface *thumb16, Graphics::Surface *thumb8) {
thumb8->create(thumb16->w, thumb16->h, Graphics::PixelFormat::createFormatCLUT8());
Graphics::PixelFormat pixelFormat16(2, 5, 6, 5, 0, 11, 5, 0, 0);
byte paletteR[PALETTE_SIZE];
byte paletteG[PALETTE_SIZE];
byte paletteB[PALETTE_SIZE];
for (int palIndex = 0; palIndex < PALETTE_SIZE; ++palIndex) {
uint16 p = READ_UINT16(&_vm->_graphicsMan->_palettePixels[palIndex * 2]);
pixelFormat16.colorToRGB(p, paletteR[palIndex], paletteG[palIndex], paletteB[palIndex]);
}
const uint16 *srcP = (const uint16 *)thumb16->getPixels();
byte *destP = (byte *)thumb8->getPixels();
for (int yp = 0; yp < thumb16->h; ++yp) {
const uint16 *lineSrcP = srcP;
byte *lineDestP = destP;
for (int xp = 0; xp < thumb16->w; ++xp) {
byte r, g, b;
pixelFormat16.colorToRGB(*lineSrcP++, r, g, b);
// Do like in the original and show thumbnail as a grayscale picture
int lum = (r * 21 + g * 72 + b * 7) / 100;
r = g = b = lum;
// Scan the palette for the closest match
int difference = 99999, foundIndex = 0;
for (int palIndex = 0; palIndex < PALETTE_SIZE; ++palIndex) {
byte rCurrent = paletteR[palIndex];
byte gCurrent = paletteG[palIndex];
byte bCurrent = paletteB[palIndex];
int diff = ABS((int)r - (int)rCurrent) + ABS((int)g - (int)gCurrent) + ABS((int)b - (int)bCurrent);
if (diff < difference) {
difference = diff;
foundIndex = palIndex;
}
}
*lineDestP++ = foundIndex;
}
// Move to the start of the next line
srcP += thumb16->w;
destP += thumb16->w;
}
}
} // End of namespace Hopkins

View File

@@ -0,0 +1,77 @@
/* 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 HOPKINS_SAVELOAD_H
#define HOPKINS_SAVELOAD_H
#include "hopkins/globals.h"
#include "hopkins/graphics.h"
#include "common/scummsys.h"
#include "common/savefile.h"
#include "common/serializer.h"
#include "common/str.h"
namespace Hopkins {
class HopkinsEngine;
#define HOPKINS_SAVEGAME_VERSION 4
struct hopkinsSavegameHeader {
uint8 _version;
Common::String _saveName;
Graphics::Surface *_thumbnail;
int _year, _month, _day;
int _hour, _minute;
int _totalFrames;
};
class SaveLoadManager {
private:
HopkinsEngine *_vm;
void createThumbnail(Graphics::Surface *s);
void syncSavegameData(Common::Serializer &s, int version);
void syncCharacterLocation(Common::Serializer &s, CharacterLocation &item);
public:
SaveLoadManager(HopkinsEngine *vm);
bool saveExists(const Common::String &file);
bool save(const Common::String &file, const void *buf, size_t n);
bool saveFile(const Common::String &file, const void *buf, size_t n);
void load(const Common::String &file, byte *buf);
WARN_UNUSED_RESULT static bool readSavegameHeader(Common::InSaveFile *in, hopkinsSavegameHeader &header, bool skipThumbnail = true);
void writeSavegameHeader(Common::OutSaveFile *out, hopkinsSavegameHeader &header);
WARN_UNUSED_RESULT bool readSavegameHeader(int slot, hopkinsSavegameHeader &header, bool skipThumbnail = true);
Common::Error saveGame(int slot, const Common::String &saveName);
Common::Error loadGame(int slot);
/**
* Converts a 16-bit thumbnail to 8 bit paletted using the currently active palette.
*/
void convertThumb16To8(Graphics::Surface *thumb16, Graphics::Surface *thumb8);
};
} // End of namespace Hopkins
#endif /* HOPKINS_SAVELOAD_H */

2686
engines/hopkins/script.cpp Normal file

File diff suppressed because it is too large Load Diff

49
engines/hopkins/script.h Normal file
View File

@@ -0,0 +1,49 @@
/* 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 HOPKINS_SCRIPT_H
#define HOPKINS_SCRIPT_H
#include "hopkins/globals.h"
#include "common/scummsys.h"
#include "common/endian.h"
#include "common/str.h"
namespace Hopkins {
class ScriptManager {
private:
HopkinsEngine *_vm;
int checkOpcode(const byte *dataP);
public:
bool _tempObjectFl;
ScriptManager(HopkinsEngine *vm);
int handleOpcode(const byte *dataP);
int handleIf(const byte *dataP, int offset);
int handleGoto(const byte *dataP);
};
} // End of namespace Hopkins
#endif /* HOPKINS_SCRIPT_H */

949
engines/hopkins/sound.cpp Normal file
View File

@@ -0,0 +1,949 @@
/* 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 "hopkins/sound.h"
#include "hopkins/globals.h"
#include "hopkins/hopkins.h"
#include "audio/decoders/adpcm_intern.h"
#include "audio/decoders/wave.h"
#include "audio/softsynth/pcspk.h"
#include "common/system.h"
#include "common/config-manager.h"
#include "common/file.h"
#include "common/textconsole.h"
#include "audio/audiostream.h"
#include "audio/mods/module.h"
#include "audio/mods/protracker.h"
#include "audio/decoders/raw.h"
namespace Hopkins {
class APC_ADPCMStream : public Audio::DVI_ADPCMStream {
public:
APC_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, int rate, int channels) : DVI_ADPCMStream(stream, disposeAfterUse, stream->size(), rate, channels, 0) {
stream->seek(-12, SEEK_CUR);
_status.ima_ch[0].last = _startValue[0] = stream->readUint32LE();
_status.ima_ch[1].last = _startValue[1] = stream->readUint32LE();
stream->seek(4, SEEK_CUR);
}
void reset() override {
DVI_ADPCMStream::reset();
_status.ima_ch[0].last = _startValue[0];
_status.ima_ch[1].last = _startValue[1];
}
private:
int16 _startValue[2];
};
Audio::RewindableAudioStream *makeAPCStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) {
if (stream->readUint32BE() != MKTAG('C', 'R', 'Y', 'O'))
return nullptr;
if (stream->readUint32BE() != MKTAG('_', 'A', 'P', 'C'))
return nullptr;
stream->readUint32BE(); // version
stream->readUint32LE(); // out size
uint32 rate = stream->readUint32LE();
stream->skip(8); // initial values, will be handled by the class
bool stereo = stream->readUint32LE() != 0;
return new APC_ADPCMStream(stream, disposeAfterUse, rate, stereo ? 2 : 1);
}
class TwaAudioStream : public Audio::AudioStream {
public:
TwaAudioStream(const Common::Path &name, Common::SeekableReadStream *stream) {
_name = name;
_cueSheet.clear();
_cueStream = nullptr;
_cue = 0;
_loadedCue = -1;
for (;;) {
char buf[3];
stream->read(buf, 3);
if (buf[0] == 'x' || stream->eos())
break;
_cueSheet.push_back(atol(buf));
}
for (_cue = 0; _cue < _cueSheet.size(); _cue++) {
if (loadCue(_cue))
break;
}
}
~TwaAudioStream() override {
delete _cueStream;
_cueStream = nullptr;
}
bool isStereo() const override {
return _cueStream ? _cueStream->isStereo() : true;
}
int getRate() const override {
return _cueStream ? _cueStream->getRate() : 22050;
}
bool endOfData() const override {
return _cueStream == nullptr;
}
int readBuffer(int16 *buffer, const int numSamples) override {
if (!_cueStream)
return 0;
int16 *buf = buffer;
int samplesLeft = numSamples;
while (samplesLeft) {
if (_cueStream) {
int readSamples = _cueStream->readBuffer(buf, samplesLeft);
buf += readSamples;
samplesLeft -= readSamples;
}
if (samplesLeft > 0) {
if (++_cue >= _cueSheet.size()) {
_cue = 0;
}
loadCue(_cue);
}
}
return numSamples;
}
protected:
bool loadCue(int nr) {
if (_loadedCue == _cueSheet[nr]) {
_cueStream->rewind();
return true;
}
delete _cueStream;
_cueStream = nullptr;
_loadedCue = _cueSheet[nr];
Common::Path filename(_name);
filename.appendInPlace(Common::String::format("_%02d", _cueSheet[nr]));
Common::File *file = new Common::File();
if (file->open(filename.append(".APC"))) {
_cueStream = makeAPCStream(file, DisposeAfterUse::YES);
return true;
}
if (file->open(filename.append(".WAV"))) {
_cueStream = Audio::makeWAVStream(file, DisposeAfterUse::YES);
return true;
}
if (file->open(filename.append(".RAW"))) {
_cueStream = Audio::makeRawStream(file, 22050, Audio::FLAG_UNSIGNED, DisposeAfterUse::YES);
return true;
}
warning("TwaAudioStream::loadCue: Missing cue %d (%s)", nr, filename.toString().c_str());
_loadedCue = -1;
delete file;
return false;
}
private:
Common::Path _name;
Common::Array<int> _cueSheet;
Audio::RewindableAudioStream *_cueStream;
uint _cue;
int _loadedCue;
};
Audio::AudioStream *makeTwaStream(const Common::Path &name, Common::SeekableReadStream *stream) {
return new TwaAudioStream(name, stream);
}
SoundManager::SoundManager(HopkinsEngine *vm) {
_vm = vm;
_specialSoundNum = 0;
_soundVolume = 0;
_voiceVolume = 0;
_musicVolume = 0;
_soundOffFl = true;
_musicOffFl = true;
_voiceOffFl = true;
_textOffFl = false;
_soundFl = false;
_skipRefreshFl = false;
_currentSoundIndex = 0;
_oldSoundNumber = 0;
_modPlayingFl = false;
}
SoundManager::~SoundManager() {
stopMusic();
delMusic();
_vm->_mixer->stopHandle(_musicHandle);
_modPlayingFl = false;
}
void SoundManager::checkSoundEnd() {
if (!_soundOffFl && _soundFl) {
if (!checkVoiceStatus(1)) {
stopVoice(1);
delWav(_currentSoundIndex);
}
}
}
void SoundManager::loadAnimSound() {
switch (_specialSoundNum) {
case 2:
loadSample(5, "mitra1.wav");
loadSample(1, "tir2.wav");
loadSample(2, "sound6.wav");
loadSample(3, "sound5.WAV");
loadSample(4, "sound4.WAV");
break;
case 5:
loadWav("CRIE.WAV", 1);
break;
case 14:
loadWav("SOUND14.WAV", 1);
break;
case 16:
loadWav("SOUND16.WAV", 1);
break;
case 198:
loadWav("SOUND3.WAV", 1);
break;
case 199:
loadWav("SOUND22.WAV", 1);
break;
case 200:
mixVoice(682, 1);
break;
case 208:
loadWav("SOUND77.WAV", 1);
break;
case 210:
loadWav("SOUND78.WAV", 1);
break;
case 211:
loadWav("SOUND78.WAV", 1);
break;
case 229:
loadWav("SOUND80.WAV", 1);
loadWav("SOUND82.WAV", 2);
break;
default:
break;
}
}
void SoundManager::playAnimSound(int animFrame) {
if (!_vm->_globals->_censorshipFl && _specialSoundNum == 2) {
switch (animFrame) {
case 20:
playSample(5);
break;
case 57:
case 63:
case 69:
playSample(1);
break;
case 75:
// This removes the sound of the gun played while the guard is being shot, as this part of the scene has been
// removed in the Polish version of the game
if (_vm->getLanguage() != Common::PL_POL)
playSample(2);
break;
case 95:
// This fixes an original bug in the Polish version of the game, which was literally butchered for some reason
if (_vm->getLanguage() == Common::PL_POL)
playSample(3);
break;
case 109:
if (_vm->getLanguage() != Common::PL_POL)
playSample(3);
break;
case 108:
// This fixes an original bug in the Polish version of the game, which was literally butchered for some reason
if (_vm->getLanguage() == Common::PL_POL)
playSample(4);
break;
case 122:
if (_vm->getLanguage() != Common::PL_POL)
playSample(4);
break;
default:
break;
}
} else if (_specialSoundNum == 1 && animFrame == 17)
playSoundFile("SOUND42.WAV");
else if (_specialSoundNum == 5 && animFrame == 19)
playWav(1);
else if (_specialSoundNum == 14 && animFrame == 625)
playWav(1);
else if (_specialSoundNum == 16 && animFrame == 25)
playWav(1);
else if (_specialSoundNum == 17) {
if (animFrame == 6)
playSample(1);
else if (animFrame == 14)
playSample(2);
else if (animFrame == 67)
playSample(3);
} else if (_specialSoundNum == 198 && animFrame == 15)
playWav(1);
else if (_specialSoundNum == 199 && animFrame == 72)
playWav(1);
else if (_specialSoundNum == 208 && animFrame == 40)
playWav(1);
else if (_specialSoundNum == 210 && animFrame == 2)
playWav(1);
else if (_specialSoundNum == 211 && animFrame == 22)
playWav(1);
else if (_specialSoundNum == 229) {
if (animFrame == 15)
playWav(1);
else if (animFrame == 91)
playWav(2);
}
}
static const char *const modSounds[] = {
"appart", "ville", "Rock", "police", "deep",
"purgat", "riviere", "SUSPENS", "labo", "cadavre",
"cabane", "purgat2", "foret", "ile", "ile2",
"hopkins", "peur", "URAVOLGA", "BASE", "cadavre2",
"usine", "chien", "coeur", "stand", "ocean",
"base3", "gloop", "cant", "feel", "lost",
"tobac"
};
void SoundManager::playSound(int soundNumber) {
if (_vm->getPlatform() == Common::kPlatformOS2 || _vm->getPlatform() == Common::kPlatformBeOS) {
if (soundNumber > 27)
return;
}
if (_oldSoundNumber != soundNumber || !_modPlayingFl) {
if (_modPlayingFl)
stopSound();
playMod(modSounds[soundNumber - 1]);
_oldSoundNumber = soundNumber;
}
}
void SoundManager::stopSound() {
stopVoice(0);
stopVoice(1);
stopVoice(2);
if (_soundFl)
delWav(_currentSoundIndex);
for (int i = 1; i <= 48; ++i)
removeWavSample(i);
if (_modPlayingFl) {
stopMusic();
delMusic();
_modPlayingFl = false;
}
}
void SoundManager::playMod(const Common::Path &file) {
if (_musicOffFl)
return;
Common::String modFile = file.baseName();
// HACK
if (modFile == "URAVOLGA" && (_vm->getPlatform() == Common::kPlatformWindows || _vm->getPlatform() == Common::kPlatformLinux))
modFile = "peur";
// The Windows/Linux version chops off the music file names to 5 characters
if (modFile.size() > 5 && (_vm->getPlatform() == Common::kPlatformWindows || _vm->getPlatform() == Common::kPlatformLinux)) {
if (!modFile.hasSuffix("2")) {
while (modFile.size() > 5)
modFile.deleteLastChar();
} else {
while (modFile.size() > 4)
modFile.deleteLastChar();
modFile += "2";
}
}
if (_modPlayingFl) {
stopMusic();
delMusic();
_modPlayingFl = false;
}
loadMusic(file.getParent().appendComponent(modFile));
playMusic();
_modPlayingFl = true;
}
void SoundManager::loadMusic(const Common::Path &file) {
if (_music._active)
delMusic();
Common::File f;
if (_vm->getPlatform() == Common::kPlatformOS2 || _vm->getPlatform() == Common::kPlatformBeOS) {
Common::Path filename(file);
filename.appendInPlace(".MOD");
if (!f.open(filename))
error("Error opening file %s", filename.toString().c_str());
Modules::Module *module;
Audio::AudioStream *modStream = Audio::makeProtrackerStream(&f, 0, 44100, true, &module);
// WORKAROUND: This song is played at the empty lot where the
// bank robbers have left the helicopter. The MOD file appears
// to be slightly broken. Almost half of it is just the same
// noise repeating. We fix this by only playing the working
// part of it. The result is pretty close to the Windows music.
if (file.equalsIgnoreCase("cadavre")) {
module->songlen = 3;
}
_vm->_mixer->playStream(Audio::Mixer::kMusicSoundType, &_musicHandle, modStream);
} else {
Common::Path filename(file);
filename.appendInPlace(".TWA");
if (!f.open(filename))
error("Error opening file %s", filename.toString().c_str());
Audio::AudioStream *twaStream = makeTwaStream(file, &f);
_vm->_mixer->playStream(Audio::Mixer::kMusicSoundType, &_musicHandle, twaStream);
f.close();
}
_music._active = true;
}
void SoundManager::playMusic() {
}
void SoundManager::stopMusic() {
_vm->_mixer->stopHandle(_musicHandle);
}
void SoundManager::delMusic() {
_music._active = false;
}
void SoundManager::checkSounds() {
checkVoiceActivity();
}
/**
* Checks voices to see if they're finished
*/
void SoundManager::checkVoiceActivity() {
// Check the status of each voice.
bool hasActiveVoice = false;
for (int i = 0; i < VOICE_COUNT; ++i)
hasActiveVoice |= checkVoiceStatus(i);
if (!hasActiveVoice && _soundFl) {
_soundFl = false;
_currentSoundIndex = 0;
}
}
bool SoundManager::mixVoice(int voiceId, int voiceMode, bool dispTxtFl) {
int fileNumber;
bool breakFlag;
Common::String prefix;
Common::Path filename;
Common::File f;
size_t catPos, catLen;
fileNumber = voiceId;
if (_voiceOffFl)
return false;
if ((voiceMode == 1 || voiceMode == 2)
&& ( voiceId == 4 || voiceId == 16 || voiceId == 121
|| voiceId == 142 || voiceId == 182 || voiceId == 191
|| voiceId == 212 || voiceId == 225 || voiceId == 239
|| voiceId == 245 || voiceId == 297 || voiceId == 308
|| voiceId == 333 || voiceId == 348 || voiceId == 352
|| voiceId == 358 || voiceId == 364 || voiceId == 371
|| voiceId == 394 || voiceId == 414 || voiceId == 429
|| voiceId == 442 || voiceId == 446 || voiceId == 461
|| voiceId == 468 || voiceId == 476 || voiceId == 484
|| voiceId == 491 || voiceId == 497 || voiceId == 501
|| voiceId == 511 || voiceId == 520 || voiceId == 536
|| voiceId == 554 || voiceId == 566 || voiceId == 573
|| voiceId == 632 || voiceId == 645))
fileNumber = 684;
if (voiceMode == 1 || voiceMode == 2)
prefix = "DF";
else if (voiceMode == 3)
prefix = "IF";
else if (voiceMode == 4)
prefix = "TF";
else if (voiceMode == 5)
prefix = "OF";
// BeOS and OS/2 versions are using a slightly different speech order during intro
// This map those values to the ones used by the Win95 and Linux versions
int mappedFileNumber = fileNumber;
if (voiceMode == 3 && (_vm->getPlatform() == Common::kPlatformOS2 || _vm->getPlatform() == Common::kPlatformBeOS)) {
if (fileNumber == 4)
mappedFileNumber = 0;
else if (fileNumber > 4)
mappedFileNumber = fileNumber - 1;
}
filename = Common::Path(Common::String::format("%s%d", prefix.c_str(), mappedFileNumber));
bool fileFoundFl = false;
_vm->_fileIO->searchCat(filename.append(".WAV"), RES_VOI, fileFoundFl);
if (fileFoundFl) {
if (_vm->getPlatform() == Common::kPlatformOS2 || _vm->getPlatform() == Common::kPlatformBeOS) {
filename = "ENG_VOI.RES";
} else {
// Win95 and Linux versions uses another set of names
switch (_vm->_globals->_language) {
case LANG_FR:
filename = "RES_VFR.RES";
break;
case LANG_EN:
filename = "RES_VAN.RES";
break;
case LANG_SP:
filename = "RES_VES.RES";
break;
default:
break;
}
}
catPos = _vm->_fileIO->_catalogPos;
catLen = _vm->_fileIO->_catalogSize;
} else {
_vm->_fileIO->searchCat(filename.append(".APC"), RES_VOI, fileFoundFl);
if (fileFoundFl) {
if (_vm->getPlatform() == Common::kPlatformOS2 || _vm->getPlatform() == Common::kPlatformBeOS) {
filename = "ENG_VOI.RES";
} else {
// Win95 and Linux versions uses another set of names
switch (_vm->_globals->_language) {
case LANG_FR:
filename = "RES_VFR.RES";
break;
case LANG_EN:
filename = "RES_VAN.RES";
break;
case LANG_SP:
filename = "RES_VES.RES";
break;
default:
break;
}
}
catPos = _vm->_fileIO->_catalogPos;
catLen = _vm->_fileIO->_catalogSize;
} else {
_vm->_fileIO->searchCat(filename.append(".RAW"), RES_VOI, fileFoundFl);
if (fileFoundFl) {
if (_vm->getPlatform() == Common::kPlatformOS2 || _vm->getPlatform() == Common::kPlatformBeOS) {
filename = "ENG_VOI.RES";
} else {
// Win95 and Linux versions uses another set of names
switch (_vm->_globals->_language) {
case LANG_FR:
filename = "RES_VFR.RES";
break;
case LANG_EN:
filename = "RES_VAN.RES";
break;
case LANG_SP:
filename = "RES_VES.RES";
break;
default:
break;
}
}
catPos = _vm->_fileIO->_catalogPos;
catLen = _vm->_fileIO->_catalogSize;
} else {
if (!f.exists(filename.append(".WAV"))) {
if (!f.exists(filename.append(".APC")))
return false;
filename.appendInPlace(".APC");
} else
filename.appendInPlace(".WAV");
catPos = 0;
catLen = 0;
}
}
}
int oldMusicVol = _musicVolume;
if (!loadVoice(filename, catPos, catLen, _sWav[20])) {
// This case only concerns the English Win95 demo
// If it's not possible to load the voice, we force the active flag
// to false in order to make sure the missing buffer won't be played
// accidentally later
_sWav[20]._active = false;
} else {
_sWav[20]._active = true;
// Reduce music volume during speech
if (!_musicOffFl && _musicVolume > 2) {
_musicVolume -= _musicVolume * 9 / 20;
setMODMusicVolume(_musicVolume);
}
}
playVoice();
_vm->_events->_escKeyFl = false;
// Loop for playing voice
breakFlag = false;
do {
if (_specialSoundNum != 4 && !_skipRefreshFl)
_vm->_events->refreshScreenAndEvents();
if (_vm->_events->getMouseButton())
break;
_vm->_events->refreshEvents();
if (_vm->_events->_escKeyFl)
break;
// We only check the voice status if the file has been loaded properly
// This avoids skipping completely the talk animations in the Win95 UK Demo
if (!checkVoiceStatus(2) && _sWav[20]._active)
breakFlag = true;
// This is specific to the Win95 UK Demo again: if nothing is displayed,
// don't wait for a click event.
if (!_sWav[20]._active && !dispTxtFl)
break;
} while (!_vm->shouldQuit() && !breakFlag);
stopVoice(2);
removeWavSample(20);
// Speech is over, set the music volume back to normal
_musicVolume = oldMusicVol;
if (!_musicOffFl && _musicVolume > 2) {
setMODMusicVolume(_musicVolume);
}
_vm->_events->_escKeyFl = false;
_skipRefreshFl = false;
return true;
}
void SoundManager::removeSample(int soundIndex) {
if (checkVoiceStatus(1))
stopVoice(1);
if (checkVoiceStatus(2))
stopVoice(2);
removeWavSample(soundIndex);
_sound[soundIndex]._active = false;
}
void SoundManager::playSoundFile(const Common::Path &file) {
if (_soundOffFl)
return;
// Fallback for the menu option.
// The BeOS and OS/2 versions don't play sound at this point.
// sound20 sounds very close to bruit2 from the linux and Win95 versions.
Common::File f;
Common::Path filename;
if (file == "bruit2.wav" && !f.exists(file))
filename = "sound20.wav";
else
filename = file;
if (_soundFl)
delWav(_currentSoundIndex);
loadWav(filename, 1);
playWav(1);
}
void SoundManager::directPlayWav(const Common::Path &file) {
if (_soundOffFl)
return;
loadWav(file, 1);
playWav(1);
}
void SoundManager::setMODSampleVolume() {
for (int idx = 0; idx < SWAV_COUNT; ++idx) {
if (idx != 20 && _sWav[idx]._active) {
int volume = _soundVolume * 255 / 16;
_vm->_mixer->setChannelVolume(_sWav[idx]._soundHandle, volume);
}
}
}
void SoundManager::setMODVoiceVolume() {
if (_sWav[20]._active) {
int volume = _voiceVolume * 255 / 16;
_vm->_mixer->setChannelVolume(_sWav[20]._soundHandle, volume);
}
}
void SoundManager::setMODMusicVolume(int volume) {
if (_vm->_mixer->isSoundHandleActive(_musicHandle))
_vm->_mixer->setChannelVolume(_musicHandle, volume * 255 / 16);
}
void SoundManager::loadSample(int wavIndex, const Common::Path &file) {
loadWavSample(wavIndex, file, false);
_sound[wavIndex]._active = true;
}
void SoundManager::playSample(int wavIndex, int voiceMode) {
if (_soundOffFl || !_sound[wavIndex]._active)
return;
if (_soundFl)
delWav(_currentSoundIndex);
switch (voiceMode) {
case 5:
// Case added to identify the former PLAY_SAMPLE2 calls
case 9:
if (checkVoiceStatus(1))
stopVoice(1);
playWavSample(1, wavIndex);
break;
case 6:
if (checkVoiceStatus(2))
stopVoice(1);
playWavSample(2, wavIndex);
break;
default:
break;
}
}
bool SoundManager::checkVoiceStatus(int voiceIndex) {
if (_voice[voiceIndex]._status) {
int wavIndex = _voice[voiceIndex]._wavIndex;
if (_sWav[wavIndex]._audioStream && _sWav[wavIndex]._audioStream->endOfStream())
stopVoice(voiceIndex);
}
return _voice[voiceIndex]._status;
}
void SoundManager::stopVoice(int voiceIndex) {
if (_voice[voiceIndex]._status) {
_voice[voiceIndex]._status = false;
int wavIndex = _voice[voiceIndex]._wavIndex;
if (_sWav[wavIndex]._active && _sWav[wavIndex]._freeSampleFl)
removeWavSample(wavIndex);
}
_voice[voiceIndex]._status = false;
}
void SoundManager::playVoice() {
if (!_sWav[20]._active)
return;
if (!_voice[2]._status) {
int wavIndex = _voice[2]._wavIndex;
if (_sWav[wavIndex]._active && _sWav[wavIndex]._freeSampleFl)
removeWavSample(wavIndex);
}
playWavSample(2, 20);
}
bool SoundManager::removeWavSample(int wavIndex) {
if (!_sWav[wavIndex]._active)
return false;
_vm->_mixer->stopHandle(_sWav[wavIndex]._soundHandle);
delete _sWav[wavIndex]._audioStream;
_sWav[wavIndex]._audioStream = nullptr;
_sWav[wavIndex]._active = false;
return true;
}
bool SoundManager::loadVoice(const Common::Path &filename, size_t fileOffset, size_t entryLength, SwavItem &item) {
Common::File f;
if (!f.open(filename)) {
// Fallback to APC...
if (!f.open(setExtension(filename, ".APC"))) {
// The English demo doesn't include the speech file.
// This avoids it to crash when discussing with other characters
if (!_vm->getIsDemo())
error("Could not open %s for reading", filename.toString().c_str());
return false;
}
}
f.seek(fileOffset);
item._audioStream = makeSoundStream(f.readStream((entryLength == 0) ? f.size() : entryLength));
f.close();
return true;
}
void SoundManager::loadWavSample(int wavIndex, const Common::Path &filename, bool freeSample) {
if (_sWav[wavIndex]._active)
removeWavSample(wavIndex);
if (loadVoice(filename, 0, 0, _sWav[wavIndex])) {
_sWav[wavIndex]._active = true;
_sWav[wavIndex]._freeSampleFl = freeSample;
} else{
_sWav[wavIndex]._active = false;
}
}
void SoundManager::loadWav(const Common::Path &file, int wavIndex) {
loadWavSample(wavIndex, file, true);
}
void SoundManager::playWav(int wavIndex) {
if (_soundFl || _soundOffFl)
return;
_soundFl = true;
_currentSoundIndex = wavIndex;
playWavSample(1, wavIndex);
}
void SoundManager::delWav(int wavIndex) {
if (!removeWavSample(wavIndex))
return;
if (checkVoiceStatus(1))
stopVoice(1);
_currentSoundIndex = 0;
_soundFl = false;
}
void SoundManager::playWavSample(int voiceIndex, int wavIndex) {
if (!_sWav[wavIndex]._active)
warning("Bad handle");
if (_voice[voiceIndex]._status && _sWav[wavIndex]._active && _sWav[wavIndex]._freeSampleFl)
removeWavSample(wavIndex);
_voice[voiceIndex]._status = true;
_voice[voiceIndex]._wavIndex = wavIndex;
int volume = (voiceIndex == 2) ? _voiceVolume * 255 / 16 : _soundVolume * 255 / 16;
// If the handle is still in use, stop it. Otherwise we'll lose the
// handle to that sound. This can currently happen (but probably
// shouldn't) when skipping a movie.
if (_vm->_mixer->isSoundHandleActive(_sWav[wavIndex]._soundHandle))
_vm->_mixer->stopHandle(_sWav[wavIndex]._soundHandle);
// Start the voice playing
_sWav[wavIndex]._audioStream->rewind();
_vm->_mixer->playStream(Audio::Mixer::kSFXSoundType, &_sWav[wavIndex]._soundHandle,
_sWav[wavIndex]._audioStream, -1, volume, 0, DisposeAfterUse::NO);
}
void SoundManager::syncSoundSettings() {
bool muteAll = false;
if (ConfMan.hasKey("mute"))
muteAll = ConfMan.getBool("mute");
// Update the mute settings
_musicOffFl = muteAll || (ConfMan.hasKey("music_mute") && ConfMan.getBool("music_mute"));
_soundOffFl = muteAll || (ConfMan.hasKey("sfx_mute") && ConfMan.getBool("sfx_mute"));
_voiceOffFl = muteAll || (ConfMan.hasKey("speech_mute") && ConfMan.getBool("speech_mute"));
// Update the volume levels
_musicVolume = MIN(255, ConfMan.getInt("music_volume")) * 16 / 255;
_soundVolume = MIN(255, ConfMan.getInt("sfx_volume")) * 16 / 255;
_voiceVolume = MIN(255, ConfMan.getInt("speech_volume")) * 16 / 255;
// Update any active sounds
for (int idx = 0; idx < SWAV_COUNT; ++idx) {
if (_sWav[idx]._active) {
int volume = (idx == 20) ? (_voiceVolume * 255 / 16) : (_soundVolume * 255 / 16);
_vm->_mixer->setChannelVolume(_sWav[idx]._soundHandle, volume);
}
}
if (_vm->_mixer->isSoundHandleActive(_musicHandle)) {
_vm->_mixer->setChannelVolume(_musicHandle, _musicVolume * 255 / 16);
}
}
void SoundManager::updateScummVMSoundSettings() {
ConfMan.setBool("mute", _musicOffFl && _soundOffFl && _voiceOffFl);
ConfMan.setBool("music_mute", _musicOffFl);
ConfMan.setBool("sfx_mute", _soundOffFl);
ConfMan.setBool("speech_mute", _voiceOffFl);
ConfMan.setInt("music_volume", _musicVolume * 255 / 16);
ConfMan.setInt("sfx_volume", _soundVolume * 255 / 16);
ConfMan.setInt("speech_volume", _voiceVolume * 255 / 16);
ConfMan.flushToDisk();
}
/**
* Creates an audio stream based on a passed raw stream
*/
Audio::RewindableAudioStream *SoundManager::makeSoundStream(Common::SeekableReadStream *stream) {
if (_vm->getPlatform() == Common::kPlatformWindows)
return makeAPCStream(stream, DisposeAfterUse::YES);
else if (_vm->getPlatform() == Common::kPlatformLinux)
return Audio::makeWAVStream(stream, DisposeAfterUse::YES);
else
return Audio::makeRawStream(stream, 22050, Audio::FLAG_UNSIGNED, DisposeAfterUse::YES);
}
// Blatant rip from gob engine. Hi DrMcCoy!
Common::Path SoundManager::setExtension(const Common::Path &str, const Common::String &ext) {
if (str.empty())
return str;
Common::String basename(str.baseName());
const char *dot = strrchr(basename.c_str(), '.');
if (dot)
return str.getParent().appendComponent(Common::String(basename.c_str(), dot - basename.c_str()) + ext);
return str.append(ext);
}
} // End of namespace Hopkins

151
engines/hopkins/sound.h Normal file
View File

@@ -0,0 +1,151 @@
/* 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 HOPKINS_SOUND_H
#define HOPKINS_SOUND_H
#include "common/scummsys.h"
#include "common/str.h"
#include "audio/mixer.h"
namespace Audio {
class RewindableAudioStream;
}
namespace Common {
class SeekableReadStream;
}
namespace Hopkins {
class VoiceItem {
public:
VoiceItem() : _status(false), _wavIndex(0) {}
bool _status;
int _wavIndex;
};
class SwavItem {
public:
SwavItem() : _active(false), _audioStream(NULL), _freeSampleFl(false) {}
bool _active;
Audio::RewindableAudioStream *_audioStream;
Audio::SoundHandle _soundHandle;
bool _freeSampleFl;
};
class MusicItem {
public:
MusicItem() : _active(false) {}
bool _active;
};
class SoundItem {
public:
SoundItem() : _active(false) {}
bool _active;
};
#define VOICE_COUNT 3
#define SWAV_COUNT 50
#define SOUND_COUNT 10
class HopkinsEngine;
class SoundManager {
private:
HopkinsEngine *_vm;
Audio::SoundHandle _musicHandle;
int _currentSoundIndex;
bool _modPlayingFl;
int _oldSoundNumber;
VoiceItem _voice[VOICE_COUNT];
SwavItem _sWav[SWAV_COUNT];
SoundItem _sound[SOUND_COUNT];
MusicItem _music;
void playMod(const Common::Path &file);
void loadMusic(const Common::Path &file);
void playMusic();
void stopMusic();
void delMusic();
bool checkVoiceStatus(int voiceIndex);
bool loadVoice(const Common::Path &filename, size_t fileOffset, size_t entryLength, SwavItem &item);
void stopVoice(int voiceIndex);
void playVoice();
void delWav(int wavIndex);
void checkVoiceActivity();
Common::Path setExtension(const Common::Path &str, const Common::String &ext);
Audio::RewindableAudioStream *makeSoundStream(Common::SeekableReadStream *stream);
bool removeWavSample(int wavIndex);
void loadWavSample(int wavIndex, const Common::Path &filename, bool freeSample);
void playWavSample(int voiceIndex, int wavIndex);
public:
bool _musicOffFl;
bool _soundOffFl;
bool _voiceOffFl;
bool _textOffFl;
bool _soundFl;
bool _skipRefreshFl;
int _musicVolume;
int _soundVolume;
int _voiceVolume;
int _specialSoundNum;
public:
SoundManager(HopkinsEngine *vm);
~SoundManager();
void loadAnimSound();
void playAnimSound(int animFrame);
void loadSample(int wavIndex, const Common::Path &file);
void playSample(int wavIndex, int voiceMode = 9);
void removeSample(int soundIndex);
void checkSoundEnd();
void checkSounds();
void playSoundFile(const Common::Path &file);
void playSound(int soundNumber);
void stopSound();
void updateScummVMSoundSettings();
void syncSoundSettings();
bool mixVoice(int voiceId, int voiceMode, bool displTxtFl = false);
void setMODMusicVolume(int volume);
void setMODSampleVolume();
void setMODVoiceVolume();
void loadWav(const Common::Path &file, int wavIndex);
void playWav(int wavIndex);
void directPlayWav(const Common::Path &file2);
};
} // End of namespace Hopkins
#endif /* HOPKINS_SOUND_H */

1094
engines/hopkins/talk.cpp Normal file

File diff suppressed because it is too large Load Diff

77
engines/hopkins/talk.h Normal file
View File

@@ -0,0 +1,77 @@
/* 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 HOPKINS_TALK_H
#define HOPKINS_TALK_H
#include "common/scummsys.h"
#include "common/path.h"
namespace Hopkins {
class HopkinsEngine;
#define MIN_LETTERS_PER_LINE 65
class TalkManager {
private:
HopkinsEngine *_vm;
Common::Path _questionsFilename;
Common::Path _answersFilename;
byte *_characterBuffer;
byte *_characterPalette;
size_t _characterSize;
int _dialogueMesgId1, _dialogueMesgId2;
int _dialogueMesgId3, _dialogueMesgId4;
int _paletteBufferIdx;
void getStringFromBuffer(int srcStart, Common::Path &dest, const char *srcData);
int dialogQuestion(bool animatedFl);
int dialogAnswer(int idx, bool animatedFl);
void searchCharacterPalette(int startIdx, bool dark);
void dialogWait();
void dialogTalk();
void dialogEndTalk();
void startCharacterAnim0(int startIndedx, bool readOnlyFl);
void initCharacterAnim();
void clearCharacterAnim();
bool searchCharacterAnim(int idx, const byte *bufPerso, int animId, int bufferSize);
int countBoxLines(int idx, const Common::Path &file);
void dialogAnim();
void displayBobDialogAnim(int idx);
public:
byte *_characterAnim;
byte *_characterSprite;
TalkManager(HopkinsEngine *vm);
void startStaticCharacterDialogue(const Common::Path &filename);
void startAnimatedCharacterDialogue(const Common::Path &filename);
void animateObject(const Common::Path &filename);
void handleAnswer(int zone, int verb);
void handleForestAnswser(int zone, int verb);
};
} // End of namespace Hopkins
#endif /* HOPKINS_TALK_H */