Initial commit
This commit is contained in:
307
engines/sherlock/scalpel/3do/scalpel_3do_screen.cpp
Normal file
307
engines/sherlock/scalpel/3do/scalpel_3do_screen.cpp
Normal file
@@ -0,0 +1,307 @@
|
||||
/* 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 "sherlock/scalpel/scalpel_screen.h"
|
||||
#include "sherlock/scalpel/scalpel.h"
|
||||
#include "sherlock/scalpel/3do/scalpel_3do_screen.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
namespace Scalpel {
|
||||
|
||||
Scalpel3DOScreen::Scalpel3DOScreen(SherlockEngine *vm): ScalpelScreen(vm) {
|
||||
}
|
||||
|
||||
void Scalpel3DOScreen::SHblitFrom(const Graphics::Surface &src) {
|
||||
SHblitFrom(src, Common::Point(0, 0));
|
||||
}
|
||||
|
||||
void Scalpel3DOScreen::SHblitFrom(const Graphics::Surface &src, const Common::Point &destPos) {
|
||||
SHblitFrom(src, destPos, Common::Rect(0, 0, src.w, src.h));
|
||||
}
|
||||
|
||||
void Scalpel3DOScreen::SHblitFrom(const Graphics::Surface &src, const Common::Point &pt, const Common::Rect &srcBounds) {
|
||||
if (!_vm->_isScreenDoubled) {
|
||||
ScalpelScreen::SHblitFrom(src, pt, srcBounds);
|
||||
return;
|
||||
}
|
||||
|
||||
Common::Rect srcRect = srcBounds;
|
||||
srcRect.clip(320, 200);
|
||||
Common::Rect destRect(pt.x, pt.y, pt.x + srcRect.width(), pt.y + srcRect.height());
|
||||
|
||||
if (!srcRect.isValidRect() || !clip(srcRect, destRect))
|
||||
return;
|
||||
|
||||
// Add dirty area remapped to the 640x200 surface
|
||||
addDirtyRect(Common::Rect(destRect.left * 2, destRect.top * 2, destRect.right * 2, destRect.bottom * 2));
|
||||
|
||||
// Transfer the area, doubling each pixel
|
||||
for (int yp = 0; yp < srcRect.height(); ++yp) {
|
||||
const uint16 *srcP = (const uint16 *)src.getBasePtr(srcRect.left, srcRect.top + yp);
|
||||
uint16 *destP = (uint16 *)getBasePtr(destRect.left * 2, (destRect.top + yp) * 2);
|
||||
|
||||
for (int xp = srcRect.left; xp < srcRect.right; ++xp, ++srcP, destP += 2) {
|
||||
*destP = *srcP;
|
||||
*(destP + 1) = *srcP;
|
||||
*(destP + 640) = *srcP;
|
||||
*(destP + 640 + 1) = *srcP;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Scalpel3DOScreen::transBlitFromUnscaled(const Graphics::Surface &src, const Common::Point &pt,
|
||||
bool flipped, int overrideColor) {
|
||||
error("TODO: Refactor");
|
||||
#if 0
|
||||
if (!_vm->_isScreenDoubled) {
|
||||
ScalpelScreen::transBlitFromUnscaled(src, pt, flipped, overrideColor);
|
||||
return;
|
||||
}
|
||||
|
||||
Common::Rect drawRect(0, 0, src.w, src.h);
|
||||
Common::Rect destRect(pt.x, pt.y, pt.x + src.w, pt.y + src.h);
|
||||
|
||||
// Clip the display area to on-screen
|
||||
if (!clip(drawRect, destRect))
|
||||
// It's completely off-screen
|
||||
return;
|
||||
|
||||
if (flipped)
|
||||
drawRect = Common::Rect(src.w - drawRect.right, src.h - drawRect.bottom,
|
||||
src.w - drawRect.left, src.h - drawRect.top);
|
||||
|
||||
Common::Point destPt(destRect.left, destRect.top);
|
||||
addDirtyRect(Common::Rect(destPt.x * 2, destPt.y * 2, (destPt.x + drawRect.width()) * 2,
|
||||
(destPt.y + drawRect.height()) * 2));
|
||||
|
||||
assert(src.format.bytesPerPixel == 2 && _surface.format.bytesPerPixel == 2);
|
||||
|
||||
for (int yp = 0; yp < drawRect.height(); ++yp) {
|
||||
const uint16 *srcP = (const uint16 *)src.getBasePtr(
|
||||
flipped ? drawRect.right - 1 : drawRect.left, drawRect.top + yp);
|
||||
uint16 *destP = (uint16 *)getBasePtr(destPt.x * 2, (destPt.y + yp) * 2);
|
||||
|
||||
for (int xp = 0; xp < drawRect.width(); ++xp, destP += 2) {
|
||||
// RGB 0, 0, 0 -> transparent on 3DO
|
||||
if (*srcP) {
|
||||
*destP = *srcP;
|
||||
*(destP + 1) = *srcP;
|
||||
*(destP + 640) = *srcP;
|
||||
*(destP + 640 + 1) = *srcP;
|
||||
}
|
||||
|
||||
srcP = flipped ? srcP - 1 : srcP + 1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Scalpel3DOScreen::SHfillRect(const Common::Rect &r, uint color) {
|
||||
if (_vm->_isScreenDoubled)
|
||||
ScalpelScreen::fillRect(Common::Rect(r.left * 2, r.top * 2, r.right * 2, r.bottom * 2), color);
|
||||
else
|
||||
ScalpelScreen::fillRect(r, color);
|
||||
}
|
||||
|
||||
void Scalpel3DOScreen::SHtransBlitFrom(const ImageFrame &src, const Common::Point &pt,
|
||||
bool flipped, int scaleVal) {
|
||||
ScalpelScreen::SHtransBlitFrom(src, pt, flipped,
|
||||
_vm->_isScreenDoubled ? scaleVal / 2 : scaleVal);
|
||||
}
|
||||
|
||||
void Scalpel3DOScreen::SHtransBlitFrom(const Graphics::Surface &src, const Common::Point &pt,
|
||||
bool flipped, int scaleVal) {
|
||||
ScalpelScreen::SHtransBlitFrom(src, pt, flipped,
|
||||
_vm->_isScreenDoubled ? scaleVal / 2 : scaleVal);
|
||||
}
|
||||
|
||||
|
||||
void Scalpel3DOScreen::fadeIntoScreen3DO(int speed) {
|
||||
Events &events = *_vm->_events;
|
||||
uint16 *currentScreenBasePtr = (uint16 *)getPixels();
|
||||
uint16 *targetScreenBasePtr = (uint16 *)_backBuffer.getPixels();
|
||||
uint16 currentScreenPixel = 0;
|
||||
uint16 targetScreenPixel = 0;
|
||||
|
||||
uint16 currentScreenPixelRed = 0;
|
||||
uint16 currentScreenPixelGreen = 0;
|
||||
uint16 currentScreenPixelBlue = 0;
|
||||
|
||||
uint16 targetScreenPixelRed = 0;
|
||||
uint16 targetScreenPixelGreen = 0;
|
||||
uint16 targetScreenPixelBlue = 0;
|
||||
|
||||
uint16 screenWidth = SHERLOCK_SCREEN_WIDTH;
|
||||
uint16 screenHeight = SHERLOCK_SCREEN_HEIGHT;
|
||||
uint16 screenX = 0;
|
||||
uint16 screenY = 0;
|
||||
uint16 pixelsChanged = 0;
|
||||
|
||||
clearDirtyRects();
|
||||
|
||||
do {
|
||||
pixelsChanged = 0;
|
||||
uint16 *currentScreenPtr = currentScreenBasePtr;
|
||||
uint16 *targetScreenPtr = targetScreenBasePtr;
|
||||
|
||||
for (screenY = 0; screenY < screenHeight; screenY++) {
|
||||
for (screenX = 0; screenX < screenWidth; screenX++) {
|
||||
currentScreenPixel = *currentScreenPtr;
|
||||
targetScreenPixel = *targetScreenPtr;
|
||||
|
||||
if (currentScreenPixel != targetScreenPixel) {
|
||||
// pixel doesn't match, adjust accordingly
|
||||
currentScreenPixelRed = currentScreenPixel & 0xF800;
|
||||
currentScreenPixelGreen = currentScreenPixel & 0x07E0;
|
||||
currentScreenPixelBlue = currentScreenPixel & 0x001F;
|
||||
targetScreenPixelRed = targetScreenPixel & 0xF800;
|
||||
targetScreenPixelGreen = targetScreenPixel & 0x07E0;
|
||||
targetScreenPixelBlue = targetScreenPixel & 0x001F;
|
||||
|
||||
if (currentScreenPixelRed != targetScreenPixelRed) {
|
||||
if (currentScreenPixelRed < targetScreenPixelRed) {
|
||||
currentScreenPixelRed += 0x0800;
|
||||
} else {
|
||||
currentScreenPixelRed -= 0x0800;
|
||||
}
|
||||
}
|
||||
if (currentScreenPixelGreen != targetScreenPixelGreen) {
|
||||
// Adjust +2/-2 because we are running RGB555 at RGB565
|
||||
if (currentScreenPixelGreen < targetScreenPixelGreen) {
|
||||
currentScreenPixelGreen += 0x0040;
|
||||
} else {
|
||||
currentScreenPixelGreen -= 0x0040;
|
||||
}
|
||||
}
|
||||
if (currentScreenPixelBlue != targetScreenPixelBlue) {
|
||||
if (currentScreenPixelBlue < targetScreenPixelBlue) {
|
||||
currentScreenPixelBlue += 0x0001;
|
||||
} else {
|
||||
currentScreenPixelBlue -= 0x0001;
|
||||
}
|
||||
}
|
||||
|
||||
uint16 v = currentScreenPixelRed | currentScreenPixelGreen | currentScreenPixelBlue;
|
||||
*currentScreenPtr = v;
|
||||
if (_vm->_isScreenDoubled) {
|
||||
*(currentScreenPtr + 1) = v;
|
||||
*(currentScreenPtr + 640) = v;
|
||||
*(currentScreenPtr + 640 + 1) = v;
|
||||
}
|
||||
|
||||
pixelsChanged++;
|
||||
}
|
||||
|
||||
currentScreenPtr += _vm->_isScreenDoubled ? 2 : 1;
|
||||
targetScreenPtr++;
|
||||
}
|
||||
|
||||
if (_vm->_isScreenDoubled)
|
||||
currentScreenPtr += 640;
|
||||
}
|
||||
|
||||
// Too much considered dirty at the moment
|
||||
if (_vm->_isScreenDoubled)
|
||||
addDirtyRect(Common::Rect(0, 0, screenWidth * 2, screenHeight * 2));
|
||||
else
|
||||
addDirtyRect(Common::Rect(0, 0, screenWidth, screenHeight));
|
||||
|
||||
events.pollEvents();
|
||||
events.delay(10 * speed);
|
||||
} while ((pixelsChanged) && (!_vm->shouldQuit()));
|
||||
}
|
||||
|
||||
void Scalpel3DOScreen::blitFrom3DOcolorLimit(uint16 limitColor) {
|
||||
uint16 *currentScreenPtr = (uint16 *)getPixels();
|
||||
uint16 *targetScreenPtr = (uint16 *)_backBuffer.getPixels();
|
||||
uint16 currentScreenPixel = 0;
|
||||
|
||||
uint16 screenWidth = SHERLOCK_SCREEN_WIDTH;
|
||||
uint16 screenHeight = SHERLOCK_SCREEN_HEIGHT;
|
||||
uint16 screenX = 0;
|
||||
uint16 screenY = 0;
|
||||
|
||||
uint16 currentScreenPixelRed = 0;
|
||||
uint16 currentScreenPixelGreen = 0;
|
||||
uint16 currentScreenPixelBlue = 0;
|
||||
|
||||
uint16 limitPixelRed = limitColor & 0xF800;
|
||||
uint16 limitPixelGreen = limitColor & 0x07E0;
|
||||
uint16 limitPixelBlue = limitColor & 0x001F;
|
||||
|
||||
for (screenY = 0; screenY < screenHeight; screenY++) {
|
||||
for (screenX = 0; screenX < screenWidth; screenX++) {
|
||||
currentScreenPixel = *targetScreenPtr;
|
||||
|
||||
currentScreenPixelRed = currentScreenPixel & 0xF800;
|
||||
currentScreenPixelGreen = currentScreenPixel & 0x07E0;
|
||||
currentScreenPixelBlue = currentScreenPixel & 0x001F;
|
||||
|
||||
if (currentScreenPixelRed < limitPixelRed)
|
||||
currentScreenPixelRed = limitPixelRed;
|
||||
if (currentScreenPixelGreen < limitPixelGreen)
|
||||
currentScreenPixelGreen = limitPixelGreen;
|
||||
if (currentScreenPixelBlue < limitPixelBlue)
|
||||
currentScreenPixelBlue = limitPixelBlue;
|
||||
|
||||
uint16 v = currentScreenPixelRed | currentScreenPixelGreen | currentScreenPixelBlue;
|
||||
*currentScreenPtr = v;
|
||||
if (_vm->_isScreenDoubled) {
|
||||
*(currentScreenPtr + 1) = v;
|
||||
*(currentScreenPtr + 640) = v;
|
||||
*(currentScreenPtr + 640 + 1) = v;
|
||||
}
|
||||
|
||||
currentScreenPtr += _vm->_isScreenDoubled ? 2 : 1;
|
||||
targetScreenPtr++;
|
||||
}
|
||||
|
||||
if (_vm->_isScreenDoubled)
|
||||
currentScreenPtr += 640;
|
||||
}
|
||||
|
||||
// Too much considered dirty at the moment
|
||||
if (_vm->_isScreenDoubled)
|
||||
addDirtyRect(Common::Rect(0, 0, screenWidth * 2, screenHeight * 2));
|
||||
else
|
||||
addDirtyRect(Common::Rect(0, 0, screenWidth, screenHeight));
|
||||
}
|
||||
|
||||
uint16 Scalpel3DOScreen::width() const {
|
||||
return _vm->_isScreenDoubled ? this->w / 2 : this->w;
|
||||
}
|
||||
|
||||
uint16 Scalpel3DOScreen::height() const {
|
||||
return _vm->_isScreenDoubled ? this->h / 2 : this->h;
|
||||
}
|
||||
|
||||
void Scalpel3DOScreen::rawBlitFrom(const Graphics::Surface &src, const Common::Point &pt) {
|
||||
Common::Rect srcRect(0, 0, src.w, src.h);
|
||||
Common::Rect destRect(pt.x, pt.y, pt.x + src.w, pt.y + src.h);
|
||||
|
||||
addDirtyRect(destRect);
|
||||
copyRectToSurface(src, destRect.left, destRect.top, srcRect);
|
||||
}
|
||||
|
||||
} // End of namespace Scalpel
|
||||
|
||||
} // End of namespace Sherlock
|
||||
96
engines/sherlock/scalpel/3do/scalpel_3do_screen.h
Normal file
96
engines/sherlock/scalpel/3do/scalpel_3do_screen.h
Normal file
@@ -0,0 +1,96 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SHERLOCK_SCALPEL_3DO_SCREEN_H
|
||||
#define SHERLOCK_SCALPEL_3DO_SCREEN_H
|
||||
|
||||
#include "sherlock/scalpel/scalpel_screen.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
class SherlockEngine;
|
||||
|
||||
namespace Scalpel {
|
||||
|
||||
class Scalpel3DOScreen : public ScalpelScreen {
|
||||
protected:
|
||||
/**
|
||||
* Draws a surface at a given position within this surface with transparency
|
||||
*/
|
||||
virtual void transBlitFromUnscaled(const Graphics::Surface &src, const Common::Point &pt, bool flipped,
|
||||
int overrideColor);
|
||||
public:
|
||||
Scalpel3DOScreen(SherlockEngine *vm);
|
||||
~Scalpel3DOScreen() override {}
|
||||
|
||||
/**
|
||||
* Draws a sub-section of a surface at a given position within this surface
|
||||
*/
|
||||
void rawBlitFrom(const Graphics::Surface &src, const Common::Point &pt);
|
||||
|
||||
/**
|
||||
* Fade backbuffer 1 into screen (3DO RGB!)
|
||||
*/
|
||||
void fadeIntoScreen3DO(int speed);
|
||||
|
||||
void blitFrom3DOcolorLimit(uint16 color);
|
||||
|
||||
/**
|
||||
* Draws a surface on this surface
|
||||
*/
|
||||
void SHblitFrom(const Graphics::Surface &src) override;
|
||||
|
||||
/**
|
||||
* Draws a surface at a given position within this surface
|
||||
*/
|
||||
void SHblitFrom(const Graphics::Surface &src, const Common::Point &destPos) override;
|
||||
|
||||
/**
|
||||
* Draws a sub-section of a surface at a given position within this surface
|
||||
*/
|
||||
void SHblitFrom(const Graphics::Surface &src, const Common::Point &destPos, const Common::Rect &srcBounds) override;
|
||||
|
||||
/**
|
||||
* Draws an image frame at a given position within this surface with transparency
|
||||
*/
|
||||
void SHtransBlitFrom(const ImageFrame &src, const Common::Point &pt,
|
||||
bool flipped = false, int scaleVal = SCALE_THRESHOLD) override;
|
||||
|
||||
/**
|
||||
* Draws an image frame at a given position within this surface with transparency
|
||||
*/
|
||||
void SHtransBlitFrom(const Graphics::Surface &src, const Common::Point &pt,
|
||||
bool flipped = false, int scaleVal = SCALE_THRESHOLD) override;
|
||||
|
||||
/**
|
||||
* Fill a given area of the surface with a given color
|
||||
*/
|
||||
void SHfillRect(const Common::Rect &r, uint color) override;
|
||||
|
||||
uint16 width() const override;
|
||||
uint16 height() const override;
|
||||
};
|
||||
|
||||
} // End of namespace Scalpel
|
||||
|
||||
} // End of namespace Sherlock
|
||||
|
||||
#endif
|
||||
643
engines/sherlock/scalpel/drivers/adlib.cpp
Normal file
643
engines/sherlock/scalpel/drivers/adlib.cpp
Normal file
@@ -0,0 +1,643 @@
|
||||
/* 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 "sherlock/sherlock.h"
|
||||
|
||||
#include "common/system.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
#include "audio/fmopl.h"
|
||||
#include "audio/mididrv.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
#define SHERLOCK_ADLIB_VOICES_COUNT 9
|
||||
#define SHERLOCK_ADLIB_NOTES_COUNT 96
|
||||
|
||||
byte operator1Register[SHERLOCK_ADLIB_VOICES_COUNT] = {
|
||||
0x00, 0x01, 0x02, 0x08, 0x09, 0x0A, 0x10, 0x11, 0x12
|
||||
};
|
||||
|
||||
byte operator2Register[SHERLOCK_ADLIB_VOICES_COUNT] = {
|
||||
0x03, 0x04, 0x05, 0x0B, 0x0C, 0x0D, 0x13, 0x14, 0x15
|
||||
};
|
||||
|
||||
struct percussionChannelEntry {
|
||||
byte requiredNote;
|
||||
byte replacementNote;
|
||||
};
|
||||
|
||||
// hardcoded, dumped from ADHOM.DRV
|
||||
const percussionChannelEntry percussionChannelTable[SHERLOCK_ADLIB_VOICES_COUNT] = {
|
||||
{ 0x00, 0x00 },
|
||||
{ 0x00, 0x00 },
|
||||
{ 0x00, 0x00 },
|
||||
{ 0x00, 0x00 },
|
||||
{ 0x00, 0x00 },
|
||||
{ 0x00, 0x00 },
|
||||
{ 0x24, 0x0C },
|
||||
{ 0x38, 0x01 },
|
||||
{ 0x26, 0x1E }
|
||||
};
|
||||
|
||||
struct InstrumentEntry {
|
||||
byte reg20op1;
|
||||
byte reg40op1;
|
||||
byte reg60op1;
|
||||
byte reg80op1;
|
||||
byte regE0op1;
|
||||
byte reg20op2;
|
||||
byte reg40op2;
|
||||
byte reg60op2;
|
||||
byte reg80op2;
|
||||
byte regE0op2;
|
||||
byte regC0;
|
||||
byte frequencyAdjust;
|
||||
};
|
||||
|
||||
// hardcoded, dumped from ADHOM.DRV
|
||||
const InstrumentEntry instrumentTable[] = {
|
||||
{ 0x71, 0x89, 0x51, 0x11, 0x00, 0x61, 0x23, 0x42, 0x15, 0x01, 0x02, 0xF4 },
|
||||
{ 0x22, 0x20, 0x97, 0x89, 0x00, 0xA2, 0x1F, 0x70, 0x07, 0x00, 0x0A, 0xF4 },
|
||||
{ 0x70, 0x1A, 0x64, 0x13, 0x00, 0x20, 0x1F, 0x53, 0x46, 0x00, 0x0E, 0xF4 },
|
||||
{ 0xB6, 0x4A, 0xB6, 0x32, 0x00, 0x11, 0x2B, 0xD1, 0x31, 0x00, 0x0E, 0xE8 },
|
||||
{ 0x71, 0x8B, 0x51, 0x11, 0x00, 0x61, 0x20, 0x32, 0x35, 0x01, 0x02, 0xF4 },
|
||||
{ 0x71, 0x8A, 0x51, 0x11, 0x00, 0x61, 0x20, 0x32, 0x25, 0x01, 0x02, 0xF4 },
|
||||
{ 0x23, 0x0F, 0xF4, 0x04, 0x02, 0x2F, 0x25, 0xF0, 0x43, 0x00, 0x06, 0xE8 },
|
||||
{ 0x71, 0x1C, 0x71, 0x03, 0x00, 0x21, 0x1F, 0x54, 0x17, 0x00, 0x0E, 0xF4 },
|
||||
{ 0x71, 0x8A, 0x6E, 0x17, 0x00, 0x25, 0x27, 0x6B, 0x0E, 0x00, 0x02, 0xF4 },
|
||||
{ 0x71, 0x1D, 0x81, 0x03, 0x00, 0x21, 0x1F, 0x64, 0x17, 0x00, 0x0E, 0xF4 },
|
||||
{ 0x01, 0x4B, 0xF1, 0x50, 0x00, 0x01, 0x23, 0xD2, 0x76, 0x00, 0x06, 0xF4 },
|
||||
{ 0x2F, 0xCA, 0xF8, 0xE5, 0x00, 0x21, 0x1F, 0xC0, 0xFF, 0x00, 0x00, 0xF4 },
|
||||
{ 0x29, 0xCD, 0xF0, 0x91, 0x00, 0x21, 0x1F, 0xE0, 0x86, 0x00, 0x02, 0xF4 },
|
||||
{ 0x24, 0xD0, 0xF0, 0x01, 0x00, 0x21, 0x1F, 0xE0, 0x86, 0x00, 0x02, 0xF4 },
|
||||
{ 0x23, 0xC8, 0xF0, 0x01, 0x00, 0x21, 0x1F, 0xE0, 0x86, 0x00, 0x02, 0xF4 },
|
||||
{ 0x64, 0xC9, 0xB0, 0x01, 0x00, 0x61, 0x1F, 0xF0, 0x86, 0x00, 0x02, 0xF4 },
|
||||
{ 0x33, 0x85, 0xA1, 0x10, 0x00, 0x15, 0x9F, 0x72, 0x23, 0x00, 0x08, 0xF4 },
|
||||
{ 0x31, 0x85, 0xA1, 0x10, 0x00, 0x15, 0x9F, 0x73, 0x33, 0x00, 0x08, 0xF4 },
|
||||
{ 0x31, 0x81, 0xA1, 0x30, 0x00, 0x16, 0x9F, 0xC2, 0x74, 0x00, 0x08, 0xF4 },
|
||||
{ 0x03, 0x8A, 0xF0, 0x7B, 0x00, 0x02, 0x9F, 0xF4, 0x7B, 0x00, 0x08, 0xF4 },
|
||||
{ 0x03, 0x8A, 0xF0, 0x7B, 0x00, 0x01, 0x9F, 0xF4, 0x7B, 0x00, 0x08, 0xF4 },
|
||||
{ 0x23, 0x8A, 0xF2, 0x7B, 0x00, 0x01, 0x9F, 0xF4, 0x7B, 0x00, 0x08, 0xF4 },
|
||||
{ 0x32, 0x80, 0x01, 0x10, 0x00, 0x12, 0x9F, 0x72, 0x33, 0x00, 0x08, 0xF4 },
|
||||
{ 0x32, 0x80, 0x01, 0x10, 0x00, 0x14, 0x9F, 0x73, 0x33, 0x00, 0x08, 0xF4 },
|
||||
{ 0x31, 0x16, 0x73, 0x8E, 0x00, 0x21, 0x1F, 0x80, 0x9E, 0x00, 0x0E, 0xF4 },
|
||||
{ 0x30, 0x16, 0x73, 0x7E, 0x00, 0x21, 0x1F, 0x80, 0x9E, 0x00, 0x0E, 0x00 },
|
||||
{ 0x31, 0x94, 0x33, 0x73, 0x00, 0x21, 0x1F, 0xA0, 0x97, 0x00, 0x0E, 0xF4 },
|
||||
{ 0x31, 0x94, 0xD3, 0x73, 0x00, 0x21, 0x20, 0xA0, 0x97, 0x00, 0x0E, 0xF4 },
|
||||
{ 0x31, 0x45, 0xF1, 0x53, 0x00, 0x32, 0x1F, 0xF2, 0x27, 0x00, 0x06, 0xF4 },
|
||||
{ 0x13, 0x0C, 0xF2, 0x01, 0x00, 0x15, 0x2F, 0xF2, 0xB6, 0x00, 0x08, 0xF4 },
|
||||
{ 0x11, 0x0C, 0xF2, 0x01, 0x00, 0x11, 0x1F, 0xF2, 0xB6, 0x00, 0x08, 0xF4 },
|
||||
{ 0x11, 0x0A, 0xFE, 0x04, 0x00, 0x11, 0x1F, 0xF2, 0xBD, 0x00, 0x08, 0xF4 },
|
||||
{ 0x16, 0x4D, 0xFA, 0x11, 0x00, 0xE1, 0x20, 0xF1, 0xF1, 0x00, 0x08, 0xF4 },
|
||||
{ 0x16, 0x40, 0xBA, 0x11, 0x00, 0xF1, 0x20, 0x24, 0x31, 0x00, 0x08, 0xF4 },
|
||||
{ 0x61, 0xA7, 0x72, 0x8E, 0x00, 0xE1, 0x9F, 0x50, 0x1A, 0x00, 0x02, 0xF4 },
|
||||
{ 0x18, 0x4D, 0x32, 0x13, 0x00, 0xE1, 0x20, 0x51, 0xE3, 0x00, 0x08, 0xF4 },
|
||||
{ 0x17, 0xC0, 0x12, 0x41, 0x00, 0x31, 0x9F, 0x13, 0x31, 0x00, 0x06, 0xF4 },
|
||||
{ 0x03, 0x8F, 0xF5, 0x55, 0x00, 0x21, 0x9F, 0xF3, 0x33, 0x00, 0x00, 0xF4 },
|
||||
{ 0x13, 0x4D, 0xFA, 0x11, 0x00, 0xE1, 0x20, 0xF1, 0xF1, 0x00, 0x08, 0xF4 },
|
||||
{ 0x11, 0x43, 0x20, 0x15, 0x00, 0xF1, 0x20, 0x31, 0xF8, 0x00, 0x08, 0xF4 },
|
||||
{ 0x11, 0x03, 0x82, 0x97, 0x00, 0xE4, 0x60, 0xF0, 0xF2, 0x00, 0x08, 0xF4 },
|
||||
{ 0x05, 0x40, 0xD1, 0x53, 0x00, 0x14, 0x1F, 0x51, 0x71, 0x00, 0x06, 0xF4 },
|
||||
{ 0xF1, 0x01, 0x77, 0x17, 0x00, 0x21, 0x1F, 0x81, 0x18, 0x00, 0x02, 0xF4 },
|
||||
{ 0xF1, 0x18, 0x32, 0x11, 0x00, 0xE1, 0x1F, 0xF1, 0x13, 0x00, 0x00, 0xF4 },
|
||||
{ 0x73, 0x48, 0xF1, 0x53, 0x00, 0x71, 0x1F, 0xF1, 0x06, 0x00, 0x08, 0xF4 },
|
||||
{ 0x71, 0x8D, 0x71, 0x11, 0x00, 0x61, 0x5F, 0x72, 0x15, 0x00, 0x06, 0xF4 },
|
||||
{ 0xD7, 0x4F, 0xF2, 0x61, 0x00, 0xD2, 0x1F, 0xF1, 0xB2, 0x00, 0x08, 0xF4 },
|
||||
{ 0x01, 0x11, 0xF0, 0xFF, 0x00, 0x01, 0x1F, 0xF0, 0xF8, 0x00, 0x0A, 0xF4 },
|
||||
{ 0x31, 0x8B, 0x41, 0x11, 0x00, 0x61, 0x1F, 0x22, 0x13, 0x00, 0x06, 0xF4 },
|
||||
{ 0x71, 0x1C, 0x71, 0x03, 0x00, 0x21, 0x1F, 0x64, 0x07, 0x00, 0x0E, 0xF4 },
|
||||
{ 0x31, 0x8B, 0x41, 0x11, 0x00, 0x61, 0x1F, 0x32, 0x15, 0x00, 0x02, 0xF4 },
|
||||
{ 0x71, 0x1C, 0xFD, 0x13, 0x00, 0x21, 0x1F, 0xE7, 0xD6, 0x00, 0x0E, 0xF4 },
|
||||
{ 0x71, 0x1C, 0x51, 0x03, 0x00, 0x21, 0x1F, 0x54, 0x67, 0x00, 0x0E, 0xF4 },
|
||||
{ 0x71, 0x1C, 0x51, 0x03, 0x00, 0x21, 0x1F, 0x54, 0x17, 0x00, 0x0E, 0xF4 },
|
||||
{ 0x71, 0x1C, 0x54, 0x15, 0x00, 0x21, 0x1F, 0x53, 0x49, 0x00, 0x0E, 0xF4 },
|
||||
{ 0x71, 0x56, 0x51, 0x03, 0x00, 0x61, 0x1F, 0x54, 0x17, 0x00, 0x0E, 0xF4 },
|
||||
{ 0x71, 0x1C, 0x51, 0x03, 0x00, 0x21, 0x1F, 0x54, 0x17, 0x00, 0x0E, 0xF4 },
|
||||
{ 0x02, 0x29, 0xF5, 0x75, 0x00, 0x01, 0x9F, 0xF2, 0xF3, 0x00, 0x00, 0xF4 },
|
||||
{ 0x02, 0x29, 0xF0, 0x75, 0x00, 0x01, 0x9F, 0xF4, 0x33, 0x00, 0x00, 0xF4 },
|
||||
{ 0x01, 0x49, 0xF1, 0x53, 0x00, 0x11, 0x1F, 0xF1, 0x74, 0x00, 0x06, 0xF4 },
|
||||
{ 0x01, 0x89, 0xF1, 0x53, 0x00, 0x11, 0x1F, 0xF1, 0x74, 0x00, 0x06, 0xF4 },
|
||||
{ 0x02, 0x89, 0xF1, 0x53, 0x00, 0x11, 0x1F, 0xF1, 0x74, 0x00, 0x06, 0xF4 },
|
||||
{ 0x02, 0x80, 0xF1, 0x53, 0x00, 0x11, 0x1F, 0xF1, 0x74, 0x00, 0x06, 0xF4 },
|
||||
{ 0x01, 0x40, 0xF1, 0x53, 0x00, 0x08, 0x5F, 0xF1, 0x53, 0x00, 0x00, 0xF4 },
|
||||
{ 0x21, 0x15, 0xD3, 0x2C, 0x00, 0x21, 0x9F, 0xC3, 0x2C, 0x00, 0x0A, 0xF4 },
|
||||
{ 0x01, 0x18, 0xD4, 0xF2, 0x00, 0x21, 0x9F, 0xC4, 0x8A, 0x00, 0x0A, 0xF4 },
|
||||
{ 0x01, 0x4E, 0xF0, 0x7B, 0x00, 0x11, 0x1F, 0xF4, 0xC8, 0x00, 0x04, 0xF4 },
|
||||
{ 0x01, 0x44, 0xF0, 0xAB, 0x00, 0x11, 0x1F, 0xF3, 0xAB, 0x00, 0x04, 0xF4 },
|
||||
{ 0x53, 0x0E, 0xF4, 0xC8, 0x00, 0x11, 0x1F, 0xF1, 0xBB, 0x00, 0x04, 0xF4 },
|
||||
{ 0x53, 0x0B, 0xF2, 0xC8, 0x00, 0x11, 0x1F, 0xF2, 0xC5, 0x00, 0x04, 0xF4 },
|
||||
{ 0x21, 0x15, 0xB4, 0x4C, 0x00, 0x21, 0x1F, 0x94, 0xAC, 0x00, 0x0A, 0xF4 },
|
||||
{ 0x21, 0x15, 0x94, 0x1C, 0x00, 0x21, 0x1F, 0x64, 0xAC, 0x00, 0x0A, 0xF4 },
|
||||
{ 0x22, 0x1B, 0x97, 0x89, 0x00, 0xA2, 0x1F, 0x70, 0x07, 0x00, 0x0A, 0xF4 },
|
||||
{ 0x21, 0x19, 0x77, 0xBF, 0x00, 0xA1, 0x9F, 0x60, 0x2A, 0x00, 0x06, 0xF4 },
|
||||
{ 0xA1, 0x13, 0xD6, 0xAF, 0x00, 0xE2, 0x9F, 0x60, 0x2A, 0x00, 0x02, 0xF4 },
|
||||
{ 0xA2, 0x1D, 0x95, 0x24, 0x00, 0xE2, 0x9F, 0x60, 0x2A, 0x00, 0x02, 0xF4 },
|
||||
{ 0x32, 0x9A, 0x51, 0x19, 0x00, 0x61, 0x9F, 0x60, 0x39, 0x00, 0x0C, 0xF4 },
|
||||
{ 0xA4, 0x12, 0xF4, 0x30, 0x00, 0xE2, 0x9F, 0x60, 0x2A, 0x00, 0x02, 0xF4 },
|
||||
{ 0x21, 0x16, 0x63, 0x0E, 0x00, 0x21, 0x1F, 0x63, 0x0E, 0x00, 0x0C, 0xF4 },
|
||||
{ 0x31, 0x16, 0x63, 0x0A, 0x00, 0x21, 0x1F, 0x63, 0x0B, 0x00, 0x0C, 0xF4 },
|
||||
{ 0x21, 0x1B, 0x63, 0x0A, 0x00, 0x21, 0x1F, 0x63, 0x0B, 0x00, 0x0C, 0xF4 },
|
||||
{ 0x20, 0x1B, 0x63, 0x0A, 0x00, 0x21, 0x1F, 0x63, 0x0B, 0x00, 0x0C, 0xF4 },
|
||||
{ 0x32, 0x1C, 0x82, 0x18, 0x00, 0x61, 0x9F, 0x60, 0x07, 0x00, 0x0C, 0xF4 },
|
||||
{ 0x32, 0x18, 0x61, 0x14, 0x00, 0xE1, 0x9F, 0x72, 0x16, 0x00, 0x0C, 0xF4 },
|
||||
{ 0x31, 0xC0, 0x77, 0x17, 0x00, 0x22, 0x1F, 0x6B, 0x09, 0x00, 0x02, 0xF4 },
|
||||
{ 0x71, 0xC3, 0x8E, 0x17, 0x00, 0x22, 0x24, 0x8B, 0x0E, 0x00, 0x02, 0xF4 },
|
||||
{ 0x70, 0x8D, 0x6E, 0x17, 0x00, 0x22, 0x1F, 0x6B, 0x0E, 0x00, 0x02, 0xF4 },
|
||||
{ 0x24, 0x4F, 0xF2, 0x06, 0x00, 0x31, 0x1F, 0x52, 0x06, 0x00, 0x0E, 0xF4 },
|
||||
{ 0x31, 0x1B, 0x64, 0x07, 0x00, 0x61, 0x1F, 0xD0, 0x67, 0x00, 0x0E, 0xF4 },
|
||||
{ 0x31, 0x1B, 0x61, 0x06, 0x00, 0x61, 0x1F, 0xD2, 0x36, 0x00, 0x0C, 0xF4 },
|
||||
{ 0x31, 0x1F, 0x31, 0x06, 0x00, 0x61, 0x1F, 0x50, 0x36, 0x00, 0x0C, 0xF4 },
|
||||
{ 0x31, 0x1F, 0x41, 0x06, 0x00, 0x61, 0x1F, 0xA0, 0x36, 0x00, 0x0C, 0xF4 },
|
||||
{ 0x21, 0x9A, 0x53, 0x56, 0x00, 0x21, 0x9F, 0xA0, 0x16, 0x00, 0x0E, 0xF4 },
|
||||
{ 0x21, 0x9A, 0x53, 0x56, 0x00, 0x21, 0x9F, 0xA0, 0x16, 0x00, 0x0E, 0xF4 },
|
||||
{ 0x61, 0x19, 0x53, 0x58, 0x00, 0x21, 0x1F, 0xA0, 0x18, 0x00, 0x0C, 0xF4 },
|
||||
{ 0x61, 0x19, 0x73, 0x57, 0x00, 0x21, 0x1F, 0xA0, 0x17, 0x00, 0x0C, 0xF4 },
|
||||
{ 0x21, 0x1B, 0x71, 0xA6, 0x00, 0x21, 0x1F, 0xA1, 0x96, 0x00, 0x0E, 0xF4 },
|
||||
{ 0x85, 0x91, 0xF5, 0x44, 0x00, 0xA1, 0x1F, 0xF0, 0x45, 0x00, 0x06, 0xF4 },
|
||||
{ 0x07, 0x51, 0xF5, 0x33, 0x00, 0x61, 0x1F, 0xF0, 0x25, 0x00, 0x06, 0xF4 },
|
||||
{ 0x13, 0x8C, 0xFF, 0x21, 0x00, 0x11, 0x9F, 0xFF, 0x03, 0x00, 0x0E, 0xF4 },
|
||||
{ 0x38, 0x8C, 0xF3, 0x0D, 0x00, 0xB1, 0x5F, 0xF5, 0x33, 0x00, 0x0E, 0xF4 },
|
||||
{ 0x87, 0x91, 0xF5, 0x55, 0x00, 0x22, 0x1F, 0xF0, 0x54, 0x00, 0x06, 0xF4 },
|
||||
{ 0xB6, 0x4A, 0xB6, 0x32, 0x00, 0x11, 0x2B, 0xD1, 0x31, 0x00, 0x0E, 0xF4 },
|
||||
{ 0x04, 0x00, 0xFE, 0xF0, 0x00, 0xC2, 0x1F, 0xF6, 0xB5, 0x00, 0x0E, 0xF4 },
|
||||
{ 0x05, 0x4E, 0xDA, 0x15, 0x00, 0x01, 0x9F, 0xF0, 0x13, 0x00, 0x0A, 0xF4 },
|
||||
{ 0x31, 0x44, 0xF2, 0x9A, 0x00, 0x32, 0x1F, 0xF0, 0x27, 0x00, 0x06, 0xF4 },
|
||||
{ 0xB0, 0xC4, 0xA4, 0x02, 0x00, 0xD7, 0x9F, 0x40, 0x42, 0x00, 0x00, 0xF4 },
|
||||
{ 0xCA, 0x84, 0xF0, 0xF0, 0x00, 0xCF, 0x1F, 0x59, 0x62, 0x00, 0x0C, 0xF4 },
|
||||
{ 0x30, 0x35, 0xF5, 0xF0, 0x00, 0x35, 0x1F, 0xF0, 0x9B, 0x00, 0x02, 0xF4 },
|
||||
{ 0x63, 0x0F, 0xF4, 0x04, 0x02, 0x6F, 0x1F, 0xF0, 0x43, 0x00, 0x06, 0xF4 },
|
||||
{ 0x07, 0x40, 0x09, 0x53, 0x00, 0x05, 0x1F, 0xF6, 0x94, 0x00, 0x0E, 0xF4 },
|
||||
{ 0x09, 0x4E, 0xDA, 0x25, 0x00, 0x01, 0x1F, 0xF1, 0x15, 0x00, 0x0A, 0xF4 },
|
||||
{ 0x04, 0x00, 0xF3, 0xA0, 0x02, 0x04, 0x1F, 0xF8, 0x46, 0x00, 0x0E, 0xF4 },
|
||||
{ 0x07, 0x00, 0xF0, 0xF0, 0x00, 0x00, 0x1F, 0x5C, 0xDC, 0x00, 0x0E, 0xF4 },
|
||||
{ 0x1F, 0x1E, 0xE5, 0x5B, 0x00, 0x0F, 0x1F, 0x5D, 0xFA, 0x00, 0x0E, 0xF4 },
|
||||
{ 0x11, 0x8A, 0xF1, 0x11, 0x00, 0x01, 0x5F, 0xF1, 0xB3, 0x00, 0x06, 0xF4 },
|
||||
{ 0x00, 0x40, 0xD1, 0x53, 0x00, 0x00, 0x1F, 0xF2, 0x56, 0x00, 0x0E, 0xF4 },
|
||||
{ 0x32, 0x44, 0xF8, 0xFF, 0x00, 0x11, 0x1F, 0xF5, 0x7F, 0x00, 0x0E, 0xF4 },
|
||||
{ 0x00, 0x40, 0x09, 0x53, 0x00, 0x02, 0x1F, 0xF7, 0x94, 0x00, 0x0E, 0xF4 },
|
||||
{ 0x11, 0x86, 0xF2, 0xA8, 0x00, 0x01, 0x9F, 0xA0, 0xA8, 0x00, 0x08, 0xF4 },
|
||||
{ 0x00, 0x50, 0xF2, 0x70, 0x00, 0x13, 0x1F, 0xF2, 0x72, 0x00, 0x0E, 0xF4 },
|
||||
{ 0xF0, 0x00, 0x11, 0x11, 0x00, 0xE0, 0xDF, 0x11, 0x11, 0x00, 0x0E, 0xF4 }
|
||||
};
|
||||
|
||||
// hardcoded, dumped from ADHOM.DRV
|
||||
uint16 frequencyLookUpTable[SHERLOCK_ADLIB_NOTES_COUNT] = {
|
||||
0x0158, 0x016C, 0x0182, 0x0199, 0x01B1, 0x01CB, 0x01E6, 0x0203, 0x0222, 0x0242,
|
||||
0x0265, 0x0289, 0x0558, 0x056C, 0x0582, 0x0599, 0x05B1, 0x05CB, 0x05E6, 0x0603,
|
||||
0x0622, 0x0642, 0x0665, 0x0689, 0x0958, 0x096C, 0x0982, 0x0999, 0x09B1, 0x09CB,
|
||||
0x09E6, 0x0A03, 0x0A22, 0x0A42, 0x0A65, 0x0A89, 0x0D58, 0x0D6C, 0x0D82, 0x0D99,
|
||||
0x0DB1, 0x0DCB, 0x0DE6, 0x0E03, 0x0E22, 0x0E42, 0x0E65, 0x0E89, 0x1158, 0x116C,
|
||||
0x1182, 0x1199, 0x11B1, 0x11CB, 0x11E6, 0x1203, 0x1222, 0x1242, 0x1265, 0x1289,
|
||||
0x1558, 0x156C, 0x1582, 0x1599, 0x15B1, 0x15CB, 0x15E6, 0x1603, 0x1622, 0x1642,
|
||||
0x1665, 0x1689, 0x1958, 0x196C, 0x1982, 0x1999, 0x19B1, 0x19CB, 0x19E6, 0x1A03,
|
||||
0x1A22, 0x1A42, 0x1A65, 0x1A89, 0x1D58, 0x1D6C, 0x1D82, 0x1D99, 0x1DB1, 0x1DCB,
|
||||
0x1DE6, 0x1E03, 0x1E22, 0x1E42, 0x1E65, 0x1E89
|
||||
};
|
||||
|
||||
class MidiDriver_SH_AdLib : public MidiDriver {
|
||||
public:
|
||||
MidiDriver_SH_AdLib(Audio::Mixer *mixer)
|
||||
: _masterVolume(15), _opl(nullptr),
|
||||
_adlibTimerProc(nullptr), _adlibTimerParam(nullptr), _isOpen(false) {
|
||||
memset(_voiceChannelMapping, 0, sizeof(_voiceChannelMapping));
|
||||
}
|
||||
~MidiDriver_SH_AdLib() override { }
|
||||
|
||||
// MidiDriver
|
||||
int open() override;
|
||||
void close() override;
|
||||
void send(uint32 b) override;
|
||||
MidiChannel *allocateChannel() override { return nullptr; }
|
||||
MidiChannel *getPercussionChannel() override { return nullptr; }
|
||||
bool isOpen() const override { return _isOpen; }
|
||||
uint32 getBaseTempo() override { return 1000000 / OPL::OPL::kDefaultCallbackFrequency; }
|
||||
|
||||
int getPolyphony() const { return SHERLOCK_ADLIB_VOICES_COUNT; }
|
||||
bool hasRhythmChannel() const { return false; }
|
||||
|
||||
void setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) override;
|
||||
|
||||
void setVolume(byte volume);
|
||||
uint32 property(int prop, uint32 param) override;
|
||||
|
||||
void newMusicData(byte *musicData, int32 musicDataSize);
|
||||
|
||||
private:
|
||||
struct adlib_ChannelEntry {
|
||||
bool inUse;
|
||||
uint16 inUseTimer;
|
||||
const InstrumentEntry *currentInstrumentPtr;
|
||||
byte currentNote;
|
||||
byte currentA0hReg;
|
||||
byte currentB0hReg;
|
||||
|
||||
adlib_ChannelEntry() : inUse(false), inUseTimer(0), currentInstrumentPtr(nullptr), currentNote(0),
|
||||
currentA0hReg(0), currentB0hReg(0) { }
|
||||
};
|
||||
|
||||
OPL::OPL *_opl;
|
||||
int _masterVolume;
|
||||
|
||||
Common::TimerManager::TimerProc _adlibTimerProc;
|
||||
void *_adlibTimerParam;
|
||||
|
||||
bool _isOpen;
|
||||
|
||||
// points to a MIDI channel for each of the new voice channels
|
||||
byte _voiceChannelMapping[SHERLOCK_ADLIB_VOICES_COUNT];
|
||||
|
||||
// stores information about all FM voice channels
|
||||
adlib_ChannelEntry _channels[SHERLOCK_ADLIB_VOICES_COUNT];
|
||||
|
||||
void onTimer();
|
||||
|
||||
void resetAdLib();
|
||||
void resetAdLibOperatorRegisters(byte baseRegister, byte value);
|
||||
void resetAdLibFMVoiceChannelRegisters(byte baseRegister, byte value);
|
||||
|
||||
void programChange(byte MIDIchannel, byte parameter);
|
||||
void setRegister(int reg, int value);
|
||||
void noteOn(byte MIDIchannel, byte note, byte velocity);
|
||||
void noteOff(byte MIDIchannel, byte note);
|
||||
void voiceOnOff(byte FMVoiceChannel, bool KeyOn, byte note, byte velocity);
|
||||
|
||||
void pitchBendChange(byte MIDIchannel, byte parameter1, byte parameter2);
|
||||
};
|
||||
|
||||
int MidiDriver_SH_AdLib::open() {
|
||||
debugC(kDebugLevelAdLibDriver, "AdLib: starting driver");
|
||||
|
||||
_opl = OPL::Config::create(OPL::Config::kOpl2);
|
||||
|
||||
if (!_opl)
|
||||
return -1;
|
||||
|
||||
_opl->init();
|
||||
|
||||
_isOpen = true;
|
||||
|
||||
_opl->start(new Common::Functor0Mem<void, MidiDriver_SH_AdLib>(this, &MidiDriver_SH_AdLib::onTimer));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void MidiDriver_SH_AdLib::close() {
|
||||
// Stop the OPL timer
|
||||
_opl->stop();
|
||||
|
||||
delete _opl;
|
||||
}
|
||||
|
||||
void MidiDriver_SH_AdLib::setVolume(byte volume) {
|
||||
_masterVolume = volume;
|
||||
//renewNotes(-1, true);
|
||||
}
|
||||
|
||||
// this should/must get called per tick
|
||||
// original driver did this before MIDI data processing on each tick
|
||||
// we do it atm after MIDI data processing
|
||||
void MidiDriver_SH_AdLib::onTimer() {
|
||||
if (_adlibTimerProc)
|
||||
(*_adlibTimerProc)(_adlibTimerParam);
|
||||
|
||||
// this should/must get called per tick
|
||||
// original driver did this before MIDI data processing on each tick
|
||||
// we do it atm after MIDI data processing
|
||||
for (byte FMvoiceChannel = 0; FMvoiceChannel < SHERLOCK_ADLIB_VOICES_COUNT; FMvoiceChannel++) {
|
||||
if (_channels[FMvoiceChannel].inUse) {
|
||||
_channels[FMvoiceChannel].inUseTimer++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Called when a music track got loaded into memory
|
||||
void MidiDriver_SH_AdLib::newMusicData(byte *musicData, int32 musicDataSize) {
|
||||
assert(musicDataSize >= 0x7F);
|
||||
// MIDI Channel <-> FM Voice Channel mapping at offset 0x22 of music data
|
||||
memcpy(&_voiceChannelMapping, musicData + 0x22, 9);
|
||||
|
||||
// reset OPL
|
||||
resetAdLib();
|
||||
|
||||
// reset current channel data
|
||||
memset(&_channels, 0, sizeof(_channels));
|
||||
}
|
||||
|
||||
void MidiDriver_SH_AdLib::resetAdLib() {
|
||||
|
||||
setRegister(0x01, 0x20); // enable waveform control on both operators
|
||||
setRegister(0x04, 0xE0); // Timer control
|
||||
|
||||
setRegister(0x08, 0); // select FM music mode
|
||||
setRegister(0xBD, 0); // disable Rhythm
|
||||
|
||||
// reset FM voice instrument data
|
||||
resetAdLibOperatorRegisters(0x20, 0);
|
||||
resetAdLibOperatorRegisters(0x60, 0);
|
||||
resetAdLibOperatorRegisters(0x80, 0);
|
||||
resetAdLibFMVoiceChannelRegisters(0xA0, 0);
|
||||
resetAdLibFMVoiceChannelRegisters(0xB0, 0);
|
||||
resetAdLibFMVoiceChannelRegisters(0xC0, 0);
|
||||
resetAdLibOperatorRegisters(0xE0, 0);
|
||||
resetAdLibOperatorRegisters(0x40, 0x3F);
|
||||
}
|
||||
|
||||
void MidiDriver_SH_AdLib::resetAdLibOperatorRegisters(byte baseRegister, byte value) {
|
||||
byte operatorIndex;
|
||||
|
||||
for (operatorIndex = 0; operatorIndex < 0x16; operatorIndex++) {
|
||||
switch (operatorIndex) {
|
||||
case 0x06:
|
||||
case 0x07:
|
||||
case 0x0E:
|
||||
case 0x0F:
|
||||
break;
|
||||
default:
|
||||
setRegister(baseRegister + operatorIndex, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MidiDriver_SH_AdLib::resetAdLibFMVoiceChannelRegisters(byte baseRegister, byte value) {
|
||||
byte FMvoiceChannel;
|
||||
|
||||
for (FMvoiceChannel = 0; FMvoiceChannel < SHERLOCK_ADLIB_VOICES_COUNT; FMvoiceChannel++) {
|
||||
setRegister(baseRegister + FMvoiceChannel, value);
|
||||
}
|
||||
}
|
||||
|
||||
// MIDI messages can be found at https://web.archive.org/web/20120128110425/http://www.midi.org/techspecs/midimessages.php
|
||||
void MidiDriver_SH_AdLib::send(uint32 b) {
|
||||
byte command = b & 0xf0;
|
||||
byte channel = b & 0xf;
|
||||
byte op1 = (b >> 8) & 0xff;
|
||||
byte op2 = (b >> 16) & 0xff;
|
||||
|
||||
switch (command) {
|
||||
case 0x80:
|
||||
noteOff(channel, op1);
|
||||
break;
|
||||
case 0x90:
|
||||
noteOn(channel, op1, op2);
|
||||
break;
|
||||
case 0xb0: // Control change
|
||||
// Doesn't seem to be implemented in the Sherlock Holmes adlib driver
|
||||
break;
|
||||
case 0xc0: // Program Change
|
||||
programChange(channel, op1);
|
||||
break;
|
||||
case 0xa0: // Polyphonic key pressure (aftertouch)
|
||||
case 0xd0: // Channel pressure (aftertouch)
|
||||
// Aftertouch doesn't seem to be implemented in the Sherlock Holmes adlib driver
|
||||
break;
|
||||
case 0xe0:
|
||||
debugC(kDebugLevelAdLibDriver, "AdLib: pitch bend change");
|
||||
pitchBendChange(channel, op1, op2);
|
||||
break;
|
||||
case 0xf0: // SysEx
|
||||
warning("ADLIB: SysEx: %x", b);
|
||||
break;
|
||||
default:
|
||||
warning("ADLIB: Unknown event %02x", command);
|
||||
}
|
||||
}
|
||||
|
||||
void MidiDriver_SH_AdLib::noteOn(byte MIDIchannel, byte note, byte velocity) {
|
||||
int16 oldestInUseChannel = -1;
|
||||
uint16 oldestInUseTimer = 0;
|
||||
|
||||
if (velocity == 0)
|
||||
return noteOff(MIDIchannel, note);
|
||||
|
||||
if (MIDIchannel != 9) {
|
||||
// Not Percussion
|
||||
for (byte FMvoiceChannel = 0; FMvoiceChannel < SHERLOCK_ADLIB_VOICES_COUNT; FMvoiceChannel++) {
|
||||
if (_voiceChannelMapping[FMvoiceChannel] == MIDIchannel) {
|
||||
if (!_channels[FMvoiceChannel].inUse) {
|
||||
_channels[FMvoiceChannel].inUse = true;
|
||||
_channels[FMvoiceChannel].currentNote = note;
|
||||
|
||||
voiceOnOff(FMvoiceChannel, true, note, velocity);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Look for oldest in-use channel
|
||||
for (byte FMvoiceChannel = 0; FMvoiceChannel < SHERLOCK_ADLIB_VOICES_COUNT; FMvoiceChannel++) {
|
||||
if (_voiceChannelMapping[FMvoiceChannel] == MIDIchannel) {
|
||||
if (_channels[FMvoiceChannel].inUseTimer > oldestInUseTimer) {
|
||||
oldestInUseTimer = _channels[FMvoiceChannel].inUseTimer;
|
||||
oldestInUseChannel = FMvoiceChannel;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (oldestInUseChannel >= 0) {
|
||||
// channel found
|
||||
debugC(kDebugLevelAdLibDriver, "AdLib: used In-Use channel");
|
||||
// original driver used note 0, we use the current note
|
||||
// because using note 0 could create a bad note (out of index) and we check that. Original driver didn't.
|
||||
voiceOnOff(oldestInUseChannel, false, _channels[oldestInUseChannel].currentNote, 0);
|
||||
|
||||
_channels[oldestInUseChannel].inUse = true;
|
||||
_channels[oldestInUseChannel].inUseTimer = 0; // safety, original driver also did this
|
||||
_channels[oldestInUseChannel].currentNote = note;
|
||||
voiceOnOff(oldestInUseChannel, true, note, velocity);
|
||||
return;
|
||||
}
|
||||
debugC(kDebugLevelAdLibDriver, "AdLib: MIDI channel not mapped/all FM voice channels busy %d", MIDIchannel);
|
||||
|
||||
} else {
|
||||
// Percussion channel
|
||||
for (byte FMvoiceChannel = 0; FMvoiceChannel < SHERLOCK_ADLIB_VOICES_COUNT; FMvoiceChannel++) {
|
||||
if (_voiceChannelMapping[FMvoiceChannel] == MIDIchannel) {
|
||||
if (note == percussionChannelTable[FMvoiceChannel].requiredNote) {
|
||||
_channels[FMvoiceChannel].inUse = true;
|
||||
_channels[FMvoiceChannel].currentNote = note;
|
||||
|
||||
voiceOnOff(FMvoiceChannel, true, percussionChannelTable[FMvoiceChannel].replacementNote, velocity);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
debugC(kDebugLevelAdLibDriver, "AdLib: percussion MIDI channel not mapped/all FM voice channels busy");
|
||||
}
|
||||
}
|
||||
|
||||
void MidiDriver_SH_AdLib::noteOff(byte MIDIchannel, byte note) {
|
||||
for (byte FMvoiceChannel = 0; FMvoiceChannel < SHERLOCK_ADLIB_VOICES_COUNT; FMvoiceChannel++) {
|
||||
if (_voiceChannelMapping[FMvoiceChannel] == MIDIchannel) {
|
||||
if (_channels[FMvoiceChannel].currentNote == note) {
|
||||
_channels[FMvoiceChannel].inUse = false;
|
||||
_channels[FMvoiceChannel].inUseTimer = 0;
|
||||
_channels[FMvoiceChannel].currentNote = 0;
|
||||
|
||||
if (MIDIchannel != 9) {
|
||||
// not-percussion
|
||||
voiceOnOff(FMvoiceChannel, false, note, 0);
|
||||
} else {
|
||||
voiceOnOff(FMvoiceChannel, false, percussionChannelTable[FMvoiceChannel].replacementNote, 0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MidiDriver_SH_AdLib::voiceOnOff(byte FMvoiceChannel, bool keyOn, byte note, byte velocity) {
|
||||
byte frequencyOffset = 0;
|
||||
uint16 frequency = 0;
|
||||
byte op2RegAdjust = 0;
|
||||
byte regValue40h = 0;
|
||||
byte regValueA0h = 0;
|
||||
byte regValueB0h = 0;
|
||||
|
||||
// Look up frequency
|
||||
if (_channels[FMvoiceChannel].currentInstrumentPtr) {
|
||||
frequencyOffset = note + _channels[FMvoiceChannel].currentInstrumentPtr->frequencyAdjust;
|
||||
} else {
|
||||
frequencyOffset = note;
|
||||
}
|
||||
if (frequencyOffset >= SHERLOCK_ADLIB_NOTES_COUNT) {
|
||||
warning("CRITICAL - AdLib driver: bad note!!!");
|
||||
return;
|
||||
}
|
||||
frequency = frequencyLookUpTable[frequencyOffset];
|
||||
|
||||
if (keyOn) {
|
||||
// adjust register 40h
|
||||
if (_channels[FMvoiceChannel].currentInstrumentPtr) {
|
||||
regValue40h = _channels[FMvoiceChannel].currentInstrumentPtr->reg40op2;
|
||||
}
|
||||
regValue40h = regValue40h - (velocity >> 3);
|
||||
op2RegAdjust = operator2Register[FMvoiceChannel];
|
||||
setRegister(0x40 + op2RegAdjust, regValue40h);
|
||||
}
|
||||
|
||||
regValueA0h = frequency & 0xFF;
|
||||
regValueB0h = frequency >> 8;
|
||||
if (keyOn) {
|
||||
regValueB0h |= 0x20; // set Key-On flag
|
||||
}
|
||||
|
||||
setRegister(0xA0 + FMvoiceChannel, regValueA0h);
|
||||
setRegister(0xB0 + FMvoiceChannel, regValueB0h);
|
||||
_channels[FMvoiceChannel].currentA0hReg = regValueA0h;
|
||||
_channels[FMvoiceChannel].currentB0hReg = regValueB0h;
|
||||
}
|
||||
|
||||
void MidiDriver_SH_AdLib::pitchBendChange(byte MIDIchannel, byte parameter1, byte parameter2) {
|
||||
uint16 channelFrequency = 0;
|
||||
byte channelRegB0hWithoutFrequency = 0;
|
||||
uint16 parameter = 0;
|
||||
byte regValueA0h = 0;
|
||||
byte regValueB0h = 0;
|
||||
|
||||
for (byte FMvoiceChannel = 0; FMvoiceChannel < SHERLOCK_ADLIB_VOICES_COUNT; FMvoiceChannel++) {
|
||||
if (_voiceChannelMapping[FMvoiceChannel] == MIDIchannel) {
|
||||
if (_channels[FMvoiceChannel].inUse) {
|
||||
// FM voice channel found and it's currently in use -> apply pitch bend change
|
||||
|
||||
// Remove frequency bits from current channel B0h-register
|
||||
channelFrequency = ((_channels[FMvoiceChannel].currentB0hReg << 8) | (_channels[FMvoiceChannel].currentA0hReg)) & 0x3FF;
|
||||
channelRegB0hWithoutFrequency = _channels[FMvoiceChannel].currentB0hReg & 0xFC;
|
||||
|
||||
if (parameter2 < 0x40) {
|
||||
channelFrequency = channelFrequency / 2;
|
||||
} else {
|
||||
parameter2 = parameter2 - 0x40;
|
||||
}
|
||||
parameter1 = parameter1 * 2;
|
||||
parameter = parameter1 | (parameter2 << 8);
|
||||
parameter = parameter * 4;
|
||||
|
||||
parameter = (parameter >> 8) + 0xFF;
|
||||
channelFrequency = channelFrequency * parameter;
|
||||
channelFrequency = (channelFrequency >> 8) | (parameter << 8);
|
||||
|
||||
regValueA0h = channelFrequency & 0xFF;
|
||||
regValueB0h = (channelFrequency >> 8) | channelRegB0hWithoutFrequency;
|
||||
|
||||
setRegister(0xA0 + FMvoiceChannel, regValueA0h);
|
||||
setRegister(0xB0 + FMvoiceChannel, regValueB0h);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MidiDriver_SH_AdLib::programChange(byte MIDIchannel, byte op1) {
|
||||
const InstrumentEntry *instrumentPtr;
|
||||
byte op1Reg = 0;
|
||||
byte op2Reg = 0;
|
||||
|
||||
// setup instrument
|
||||
instrumentPtr = &instrumentTable[op1];
|
||||
//warning("program change for MIDI channel %d, instrument id %d", MIDIchannel, op1);
|
||||
|
||||
for (byte FMvoiceChannel = 0; FMvoiceChannel < SHERLOCK_ADLIB_VOICES_COUNT; FMvoiceChannel++) {
|
||||
if (_voiceChannelMapping[FMvoiceChannel] == MIDIchannel) {
|
||||
|
||||
op1Reg = operator1Register[FMvoiceChannel];
|
||||
op2Reg = operator2Register[FMvoiceChannel];
|
||||
|
||||
setRegister(0x20 + op1Reg, instrumentPtr->reg20op1);
|
||||
setRegister(0x40 + op1Reg, instrumentPtr->reg40op1);
|
||||
setRegister(0x60 + op1Reg, instrumentPtr->reg60op1);
|
||||
setRegister(0x80 + op1Reg, instrumentPtr->reg80op1);
|
||||
setRegister(0xE0 + op1Reg, instrumentPtr->regE0op1);
|
||||
|
||||
setRegister(0x20 + op2Reg, instrumentPtr->reg20op2);
|
||||
setRegister(0x40 + op2Reg, instrumentPtr->reg40op2);
|
||||
setRegister(0x60 + op2Reg, instrumentPtr->reg60op2);
|
||||
setRegister(0x80 + op2Reg, instrumentPtr->reg80op2);
|
||||
setRegister(0xE0 + op2Reg, instrumentPtr->regE0op2);
|
||||
|
||||
setRegister(0xC0 + FMvoiceChannel, instrumentPtr->regC0);
|
||||
|
||||
// Remember instrument
|
||||
_channels[FMvoiceChannel].currentInstrumentPtr = instrumentPtr;
|
||||
}
|
||||
}
|
||||
}
|
||||
void MidiDriver_SH_AdLib::setRegister(int reg, int value) {
|
||||
_opl->write(0x220, reg);
|
||||
_opl->write(0x221, value);
|
||||
}
|
||||
|
||||
uint32 MidiDriver_SH_AdLib::property(int prop, uint32 param) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void MidiDriver_SH_AdLib::setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) {
|
||||
_adlibTimerProc = timerProc;
|
||||
_adlibTimerParam = timerParam;
|
||||
}
|
||||
|
||||
MidiDriver *MidiDriver_SH_AdLib_create() {
|
||||
return new MidiDriver_SH_AdLib(g_system->getMixer());
|
||||
}
|
||||
|
||||
void MidiDriver_SH_AdLib_newMusicData(MidiDriver *driver, byte *musicData, int32 musicDataSize) {
|
||||
static_cast<MidiDriver_SH_AdLib *>(driver)->newMusicData(musicData, musicDataSize);
|
||||
}
|
||||
|
||||
} // End of namespace Sherlock
|
||||
40
engines/sherlock/scalpel/drivers/mididriver.h
Normal file
40
engines/sherlock/scalpel/drivers/mididriver.h
Normal file
@@ -0,0 +1,40 @@
|
||||
/* 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 SHERLOCK_SCALPEL_DRIVERS_MIDIDRIVER_H
|
||||
#define SHERLOCK_SCALPEL_DRIVERS_MIDIDRIVER_H
|
||||
|
||||
#include "sherlock/sherlock.h"
|
||||
#include "audio/mididrv.h"
|
||||
#include "common/error.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
extern MidiDriver *MidiDriver_SH_AdLib_create();
|
||||
extern void MidiDriver_SH_AdLib_newMusicData(MidiDriver *driver, byte *musicData, int32 musicDataSize);
|
||||
|
||||
extern MidiDriver *MidiDriver_MT32_create();
|
||||
extern void MidiDriver_MT32_uploadPatches(MidiDriver *driver, byte *driverData, int32 driverSize);
|
||||
extern void MidiDriver_MT32_newMusicData(MidiDriver *driver, byte *musicData, int32 musicDataSize);
|
||||
|
||||
} // End of namespace Sherlock
|
||||
|
||||
#endif // SHERLOCK_SCALPEL_DRIVERS_MIDIDRIVER_H
|
||||
281
engines/sherlock/scalpel/drivers/mt32.cpp
Normal file
281
engines/sherlock/scalpel/drivers/mt32.cpp
Normal file
@@ -0,0 +1,281 @@
|
||||
/* 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 "sherlock/sherlock.h"
|
||||
#include "sherlock/scalpel/drivers/mididriver.h"
|
||||
|
||||
#include "common/config-manager.h"
|
||||
#include "common/file.h"
|
||||
#include "common/system.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
//#include "audio/mididrv.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
#define SHERLOCK_MT32_CHANNEL_COUNT 16
|
||||
|
||||
const byte mt32ReverbDataSysEx[] = {
|
||||
0x10, 0x00, 0x01, 0x01, 0x05, 0x05, 0xFF
|
||||
};
|
||||
|
||||
class MidiDriver_MT32 : public MidiDriver {
|
||||
public:
|
||||
MidiDriver_MT32() {
|
||||
_driver = nullptr;
|
||||
_isOpen = false;
|
||||
_nativeMT32 = false;
|
||||
_baseFreq = 250;
|
||||
|
||||
memset(_MIDIchannelActive, 1, sizeof(_MIDIchannelActive));
|
||||
}
|
||||
~MidiDriver_MT32() override;
|
||||
|
||||
// MidiDriver
|
||||
int open() override;
|
||||
void close() override;
|
||||
bool isOpen() const override { return _isOpen; }
|
||||
|
||||
void send(uint32 b) override;
|
||||
|
||||
void newMusicData(byte *musicData, int32 musicDataSize);
|
||||
|
||||
MidiChannel *allocateChannel() override {
|
||||
if (_driver)
|
||||
return _driver->allocateChannel();
|
||||
return nullptr;
|
||||
}
|
||||
MidiChannel *getPercussionChannel() override {
|
||||
if (_driver)
|
||||
return _driver->getPercussionChannel();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void setTimerCallback(void *timer_param, Common::TimerManager::TimerProc timer_proc) override {
|
||||
if (_driver)
|
||||
_driver->setTimerCallback(timer_param, timer_proc);
|
||||
}
|
||||
|
||||
uint32 getBaseTempo() override {
|
||||
if (_driver) {
|
||||
return _driver->getBaseTempo();
|
||||
}
|
||||
return 1000000 / _baseFreq;
|
||||
}
|
||||
|
||||
protected:
|
||||
Common::Mutex _mutex;
|
||||
MidiDriver *_driver;
|
||||
bool _nativeMT32;
|
||||
|
||||
bool _isOpen;
|
||||
int _baseFreq;
|
||||
|
||||
private:
|
||||
// points to a MIDI channel for each of the new voice channels
|
||||
byte _MIDIchannelActive[SHERLOCK_MT32_CHANNEL_COUNT];
|
||||
|
||||
public:
|
||||
void uploadMT32Patches(byte *driverData, int32 driverSize);
|
||||
|
||||
void mt32SysEx(const byte *&dataPtr, int32 &bytesLeft);
|
||||
};
|
||||
|
||||
MidiDriver_MT32::~MidiDriver_MT32() {
|
||||
Common::StackLock lock(_mutex);
|
||||
if (_driver) {
|
||||
_driver->setTimerCallback(nullptr, nullptr);
|
||||
_driver->close();
|
||||
delete _driver;
|
||||
}
|
||||
_driver = nullptr;
|
||||
}
|
||||
|
||||
int MidiDriver_MT32::open() {
|
||||
assert(!_driver);
|
||||
|
||||
debugC(kDebugLevelMT32Driver, "MT32: starting driver");
|
||||
|
||||
// Setup midi driver
|
||||
MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_PREFER_MT32);
|
||||
MusicType musicType = MidiDriver::getMusicType(dev);
|
||||
|
||||
switch (musicType) {
|
||||
case MT_MT32:
|
||||
_nativeMT32 = true;
|
||||
break;
|
||||
case MT_GM:
|
||||
if (ConfMan.getBool("native_mt32")) {
|
||||
_nativeMT32 = true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
_driver = MidiDriver::createMidi(dev);
|
||||
if (!_driver)
|
||||
return 255;
|
||||
|
||||
if (_nativeMT32)
|
||||
_driver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
|
||||
|
||||
int ret = _driver->open();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (_nativeMT32)
|
||||
_driver->sendMT32Reset();
|
||||
else
|
||||
_driver->sendGMReset();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void MidiDriver_MT32::close() {
|
||||
if (_driver) {
|
||||
_driver->close();
|
||||
}
|
||||
}
|
||||
|
||||
// Called when a music track got loaded into memory
|
||||
void MidiDriver_MT32::newMusicData(byte *musicData, int32 musicDataSize) {
|
||||
assert(musicDataSize >= 0x7F); // Security check
|
||||
|
||||
// MIDI Channel Enable/Disable bytes at offset 0x2 of music data
|
||||
memcpy(&_MIDIchannelActive, musicData + 0x2, SHERLOCK_MT32_CHANNEL_COUNT);
|
||||
|
||||
// Send 16 bytes from offset 0x12 to MT32
|
||||
// All the music tracks of Sherlock seem to contain dummy data
|
||||
// probably a feature, that was used in the game "Ski or Die"
|
||||
// that's why we don't implement this
|
||||
|
||||
// Also send these bytes to MT32 (SysEx) - seems to be reverb configuration
|
||||
if (_nativeMT32) {
|
||||
const byte *reverbData = mt32ReverbDataSysEx;
|
||||
int32 reverbDataSize = sizeof(mt32ReverbDataSysEx);
|
||||
mt32SysEx(reverbData, reverbDataSize);
|
||||
}
|
||||
}
|
||||
|
||||
void MidiDriver_MT32::uploadMT32Patches(byte *driverData, int32 driverSize) {
|
||||
if (!_driver)
|
||||
return;
|
||||
|
||||
if (!_nativeMT32)
|
||||
return;
|
||||
|
||||
// patch data starts at offset 0x863
|
||||
assert(driverSize == 0x13B9); // Security check
|
||||
assert(driverData[0x863] == 0x7F); // another security check
|
||||
|
||||
const byte *patchPtr = driverData + 0x863;
|
||||
int32 bytesLeft = driverSize - 0x863;
|
||||
|
||||
while(1) {
|
||||
mt32SysEx(patchPtr, bytesLeft);
|
||||
|
||||
assert(bytesLeft);
|
||||
if (*patchPtr == 0x80) // List terminator
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MidiDriver_MT32::mt32SysEx(const byte *&dataPtr, int32 &bytesLeft) {
|
||||
byte sysExMessage[270];
|
||||
uint16 sysExPos = 0;
|
||||
byte sysExByte = 0;
|
||||
uint16 sysExChecksum = 0;
|
||||
|
||||
memset(&sysExMessage, 0, sizeof(sysExMessage));
|
||||
|
||||
sysExMessage[0] = 0x41; // Roland
|
||||
sysExMessage[1] = 0x10;
|
||||
sysExMessage[2] = 0x16; // Model MT32
|
||||
sysExMessage[3] = 0x12; // Command DT1
|
||||
|
||||
sysExPos = 4;
|
||||
sysExChecksum = 0;
|
||||
while (1) {
|
||||
assert(bytesLeft);
|
||||
|
||||
sysExByte = *dataPtr++;
|
||||
bytesLeft--;
|
||||
if (sysExByte == 0xff)
|
||||
break; // Message done
|
||||
|
||||
assert(sysExPos < sizeof(sysExMessage));
|
||||
sysExMessage[sysExPos++] = sysExByte;
|
||||
sysExChecksum -= sysExByte;
|
||||
}
|
||||
|
||||
// Calculate checksum
|
||||
assert(sysExPos < sizeof(sysExMessage));
|
||||
sysExMessage[sysExPos++] = sysExChecksum & 0x7f;
|
||||
|
||||
debugC(kDebugLevelMT32Driver, "MT32: uploading patch data, size %d", sysExPos);
|
||||
|
||||
// Send SysEx
|
||||
_driver->sysEx(sysExMessage, sysExPos);
|
||||
|
||||
// Wait the time it takes to send the SysEx data
|
||||
uint32 delay = (sysExPos + 2) * 1000 / 3125;
|
||||
|
||||
// Plus an additional delay for the MT-32 rev00
|
||||
if (_nativeMT32)
|
||||
delay += 40;
|
||||
|
||||
g_system->delayMillis(delay);
|
||||
}
|
||||
|
||||
// MIDI messages can be found at https://web.archive.org/web/20120128110425/http://www.midi.org/techspecs/midimessages.php
|
||||
void MidiDriver_MT32::send(uint32 b) {
|
||||
byte command = b & 0xf0;
|
||||
byte channel = b & 0xf;
|
||||
|
||||
if (command == 0xF0) {
|
||||
if (_driver) {
|
||||
_driver->send(b);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (_MIDIchannelActive[channel]) {
|
||||
// Only forward MIDI-data in case the channel is currently enabled via music-data
|
||||
if (_driver) {
|
||||
_driver->send(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MidiDriver *MidiDriver_MT32_create() {
|
||||
return new MidiDriver_MT32();
|
||||
}
|
||||
|
||||
void MidiDriver_MT32_newMusicData(MidiDriver *driver, byte *musicData, int32 musicDataSize) {
|
||||
static_cast<MidiDriver_MT32 *>(driver)->newMusicData(musicData, musicDataSize);
|
||||
}
|
||||
|
||||
void MidiDriver_MT32_uploadPatches(MidiDriver *driver, byte *driverData, int32 driverSize) {
|
||||
static_cast<MidiDriver_MT32 *>(driver)->uploadMT32Patches(driverData, driverSize);
|
||||
}
|
||||
|
||||
} // End of namespace Sherlock
|
||||
1448
engines/sherlock/scalpel/scalpel.cpp
Normal file
1448
engines/sherlock/scalpel/scalpel.cpp
Normal file
File diff suppressed because it is too large
Load Diff
159
engines/sherlock/scalpel/scalpel.h
Normal file
159
engines/sherlock/scalpel/scalpel.h
Normal file
@@ -0,0 +1,159 @@
|
||||
/* 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 SHERLOCK_SCALPEL_H
|
||||
#define SHERLOCK_SCALPEL_H
|
||||
|
||||
#include "sherlock/sherlock.h"
|
||||
#include "sherlock/scalpel/scalpel_darts.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
namespace Scalpel {
|
||||
|
||||
extern uint BUTTON_TOP;
|
||||
extern uint BUTTON_MIDDLE;
|
||||
extern uint BUTTON_BOTTOM;
|
||||
extern uint COMMAND_FOREGROUND;
|
||||
extern uint COMMAND_HIGHLIGHTED;
|
||||
extern uint COMMAND_NULL;
|
||||
extern uint INFO_FOREGROUND;
|
||||
extern uint INFO_BACKGROUND;
|
||||
extern uint INV_FOREGROUND;
|
||||
extern uint INV_BACKGROUND;
|
||||
extern uint PEN_COLOR;
|
||||
extern uint INFO_BLACK;
|
||||
extern uint BORDER_COLOR;
|
||||
extern uint COMMAND_BACKGROUND;
|
||||
extern uint BUTTON_BACKGROUND;
|
||||
extern uint TALK_FOREGROUND;
|
||||
extern uint TALK_NULL;
|
||||
|
||||
class ScalpelEngine : public SherlockEngine {
|
||||
private:
|
||||
Darts *_darts;
|
||||
int _mapResult;
|
||||
|
||||
/**
|
||||
* Initialize graphics mode
|
||||
*/
|
||||
void setupGraphics();
|
||||
|
||||
/**
|
||||
* Show the 3DO splash screen
|
||||
*/
|
||||
bool show3DOSplash();
|
||||
|
||||
/**
|
||||
* Show the starting city cutscene which shows the game title
|
||||
*/
|
||||
bool showCityCutscene();
|
||||
bool showCityCutscene3DO();
|
||||
|
||||
/**
|
||||
* Show the back alley where the initial murder takes place
|
||||
*/
|
||||
bool showAlleyCutscene();
|
||||
bool showAlleyCutscene3DO();
|
||||
|
||||
/**
|
||||
* Show the Baker Street outside cutscene
|
||||
*/
|
||||
bool showStreetCutscene();
|
||||
bool showStreetCutscene3DO();
|
||||
|
||||
/**
|
||||
* Show Holmes and Watson at the breakfast table, lestrade's note, and then the scrolling credits
|
||||
*/
|
||||
bool showOfficeCutscene();
|
||||
bool showOfficeCutscene3DO();
|
||||
|
||||
/**
|
||||
* Show the game credits
|
||||
*/
|
||||
bool scrollCredits();
|
||||
|
||||
/**
|
||||
* Load the default inventory for the game, which includes both the initial active inventory,
|
||||
* as well as special pending inventory items which can appear automatically in the player's
|
||||
* inventory once given required flags are set
|
||||
*/
|
||||
void loadInventory();
|
||||
|
||||
/**
|
||||
* Transition to show an image
|
||||
*/
|
||||
void showLBV(const Common::Path &filename);
|
||||
protected:
|
||||
/**
|
||||
* Game initialization
|
||||
*/
|
||||
void initialize() override;
|
||||
|
||||
/**
|
||||
* Show the opening sequence
|
||||
*/
|
||||
void showOpening() override;
|
||||
|
||||
/**
|
||||
* Starting a scene within the game
|
||||
*/
|
||||
void startScene() override;
|
||||
public:
|
||||
ScalpelEngine(OSystem *syst, const SherlockGameDescription *gameDesc);
|
||||
~ScalpelEngine() override;
|
||||
|
||||
/**
|
||||
* Takes care of clearing the mirror in scene 12 (mansion drawing room), in case anything drew over it
|
||||
*/
|
||||
void eraseBrumwellMirror();
|
||||
|
||||
/**
|
||||
* Takes care of drawing Holme's reflection onto the mirror in scene 12 (mansion drawing room)
|
||||
*/
|
||||
void doBrumwellMirror();
|
||||
|
||||
/**
|
||||
* This clears the mirror in scene 12 (mansion drawing room) in case anything messed draw over it
|
||||
*/
|
||||
void flushBrumwellMirror();
|
||||
|
||||
/**
|
||||
* Show the ScummVM restore savegame dialog
|
||||
*/
|
||||
void showScummVMSaveDialog();
|
||||
|
||||
/**
|
||||
* Show the ScummVM restore savegame dialog
|
||||
*/
|
||||
void showScummVMRestoreDialog();
|
||||
|
||||
/**
|
||||
* Play back a 3do movie
|
||||
*/
|
||||
bool play3doMovie(const Common::Path &filename, const Common::Point &pos, bool isPortrait = false);
|
||||
};
|
||||
|
||||
} // End of namespace Scalpel
|
||||
|
||||
} // End of namespace Sherlock
|
||||
|
||||
#endif
|
||||
562
engines/sherlock/scalpel/scalpel_darts.cpp
Normal file
562
engines/sherlock/scalpel/scalpel_darts.cpp
Normal file
@@ -0,0 +1,562 @@
|
||||
/* 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 "sherlock/scalpel/scalpel_darts.h"
|
||||
#include "sherlock/scalpel/scalpel.h"
|
||||
|
||||
#include "backends/keymapper/keymapper.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
namespace Scalpel {
|
||||
|
||||
enum {
|
||||
STATUS_INFO_X = 218,
|
||||
STATUS_INFO_Y = 53,
|
||||
DART_INFO_X = 218,
|
||||
DART_INFO_Y = 103,
|
||||
DARTBARHX = 35,
|
||||
DARTHORIZY = 190,
|
||||
DARTBARVX = 1,
|
||||
DARTHEIGHTY = 25,
|
||||
DARTBARSIZE = 150,
|
||||
DART_BAR_FORE = 8
|
||||
};
|
||||
|
||||
enum {
|
||||
DART_COL_FORE = 5,
|
||||
PLAYER_COLOR = 11
|
||||
};
|
||||
#define OPPONENTS_COUNT 4
|
||||
|
||||
const char *const OPPONENT_NAMES[OPPONENTS_COUNT] = {
|
||||
"Skipper", "Willy", "Micky", "Tom"
|
||||
};
|
||||
|
||||
/*----------------------------------------------------------------*/
|
||||
|
||||
Darts::Darts(ScalpelEngine *vm) : _vm(vm) {
|
||||
_dartImages = nullptr;
|
||||
_level = 0;
|
||||
_computerPlayer = 1;
|
||||
_playerDartMode = false;
|
||||
_dartScore1 = _dartScore2 = 0;
|
||||
_roundNumber = 0;
|
||||
_playerDartMode = false;
|
||||
_roundScore = 0;
|
||||
_oldDartButtons = false;
|
||||
}
|
||||
|
||||
void Darts::playDarts() {
|
||||
Events &events = *_vm->_events;
|
||||
Screen &screen = *_vm->_screen;
|
||||
int playerNumber = 0;
|
||||
int lastDart;
|
||||
|
||||
// Change the font
|
||||
int oldFont = screen.fontNumber();
|
||||
screen.setFont(2);
|
||||
|
||||
loadDarts();
|
||||
initDarts();
|
||||
|
||||
Common::Keymapper *keymapper = g_system->getEventManager()->getKeymapper();
|
||||
keymapper->getKeymap("scalpel")->setEnabled(false);
|
||||
keymapper->getKeymap("scalpel-quit")->setEnabled(false);
|
||||
keymapper->getKeymap("scalpel-darts")->setEnabled(true);
|
||||
|
||||
bool done = false;
|
||||
do {
|
||||
int score, roundStartScore;
|
||||
roundStartScore = score = playerNumber == 0 ? _dartScore1 : _dartScore2;
|
||||
|
||||
// Show player details
|
||||
showNames(playerNumber);
|
||||
showStatus(playerNumber);
|
||||
_roundScore = 0;
|
||||
|
||||
if (_vm->shouldQuit())
|
||||
return;
|
||||
|
||||
for (int idx = 0; idx < 3; ++idx) {
|
||||
// Throw a single dart
|
||||
if (_computerPlayer == 1)
|
||||
lastDart = throwDart(idx + 1, playerNumber * 2);
|
||||
else if (_computerPlayer == 2)
|
||||
lastDart = throwDart(idx + 1, playerNumber + 1);
|
||||
else
|
||||
lastDart = throwDart(idx + 1, 0);
|
||||
|
||||
score -= lastDart;
|
||||
_roundScore += lastDart;
|
||||
|
||||
screen._backBuffer1.SHblitFrom(screen._backBuffer2, Common::Point(DART_INFO_X, DART_INFO_Y - 1),
|
||||
Common::Rect(DART_INFO_X, DART_INFO_Y - 1, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT));
|
||||
screen.print(Common::Point(DART_INFO_X, DART_INFO_Y), DART_COL_FORE, "Dart # %d", idx + 1);
|
||||
screen.print(Common::Point(DART_INFO_X, DART_INFO_Y + 10), DART_COL_FORE, "Scored %d points", lastDart);
|
||||
|
||||
if (score != 0 && playerNumber == 0)
|
||||
screen.print(Common::Point(DART_INFO_X, DART_INFO_Y + 30), DART_COL_FORE, "Press a key");
|
||||
|
||||
if (score == 0) {
|
||||
// Some-one has won
|
||||
screen.print(Common::Point(DART_INFO_X, DART_INFO_Y + 20), PLAYER_COLOR, "GAME OVER!");
|
||||
|
||||
if (playerNumber == 0) {
|
||||
screen.print(Common::Point(DART_INFO_X, DART_INFO_Y + 30), PLAYER_COLOR, "Holmes Wins!");
|
||||
if (_level < OPPONENTS_COUNT)
|
||||
_vm->setFlagsDirect(318 + _level);
|
||||
} else {
|
||||
screen.print(Common::Point(DART_INFO_X, DART_INFO_Y + 30), PLAYER_COLOR, "%s Wins!", _opponent.c_str());
|
||||
}
|
||||
|
||||
screen.print(Common::Point(DART_INFO_X, DART_INFO_Y + 40), DART_COL_FORE, "Press a key");
|
||||
|
||||
idx = 10;
|
||||
done = true;
|
||||
} else if (score < 0) {
|
||||
screen.print(Common::Point(DART_INFO_X, DART_INFO_Y + 20), PLAYER_COLOR, "BUSTED!");
|
||||
|
||||
idx = 10;
|
||||
score = roundStartScore;
|
||||
}
|
||||
|
||||
if (playerNumber == 0)
|
||||
_dartScore1 = score;
|
||||
else
|
||||
_dartScore2 = score;
|
||||
|
||||
showStatus(playerNumber);
|
||||
events.clearKeyboard();
|
||||
|
||||
if ((playerNumber == 0 && _computerPlayer == 1) || _computerPlayer == 0 || done) {
|
||||
int dartKey;
|
||||
while (!(dartKey = dartHit()) && !_vm->shouldQuit())
|
||||
events.delay(10);
|
||||
|
||||
if (dartKey == kActionScalpelDartsExit) {
|
||||
idx = 10;
|
||||
done = true;
|
||||
}
|
||||
} else {
|
||||
events.wait(20);
|
||||
}
|
||||
|
||||
screen._backBuffer1.SHblitFrom(screen._backBuffer2, Common::Point(DART_INFO_X, DART_INFO_Y - 1),
|
||||
Common::Rect(DART_INFO_X, DART_INFO_Y - 1, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT));
|
||||
screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
|
||||
}
|
||||
|
||||
playerNumber ^= 1;
|
||||
if (!playerNumber)
|
||||
++_roundNumber;
|
||||
|
||||
done |= _vm->shouldQuit();
|
||||
|
||||
if (!done) {
|
||||
screen._backBuffer2.SHblitFrom((*_dartImages)[0], Common::Point(0, 0));
|
||||
screen._backBuffer1.SHblitFrom(screen._backBuffer2);
|
||||
screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
|
||||
}
|
||||
} while (!done);
|
||||
|
||||
closeDarts();
|
||||
screen.fadeToBlack();
|
||||
|
||||
keymapper->getKeymap("scalpel-darts")->setEnabled(false);
|
||||
keymapper->getKeymap("scalpel")->setEnabled(true);
|
||||
keymapper->getKeymap("scalpel-quit")->setEnabled(true);
|
||||
|
||||
// Restore font
|
||||
screen.setFont(oldFont);
|
||||
}
|
||||
|
||||
void Darts::loadDarts() {
|
||||
Screen &screen = *_vm->_screen;
|
||||
|
||||
_dartImages = new ImageFile("darts.vgs");
|
||||
screen.setPalette(_dartImages->_palette);
|
||||
|
||||
screen._backBuffer1.SHblitFrom((*_dartImages)[0], Common::Point(0, 0));
|
||||
screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
|
||||
}
|
||||
|
||||
void Darts::initDarts() {
|
||||
_dartScore1 = _dartScore2 = 301;
|
||||
_roundNumber = 1;
|
||||
|
||||
if (_level == 9) {
|
||||
// No computer players
|
||||
_computerPlayer = 0;
|
||||
_level = 0;
|
||||
} else if (_level == 8) {
|
||||
_level = _vm->getRandomNumber(3);
|
||||
_computerPlayer = 2;
|
||||
} else {
|
||||
// Check flags for opponents
|
||||
for (int idx = 0; idx < OPPONENTS_COUNT; ++idx) {
|
||||
if (_vm->readFlags(314 + idx))
|
||||
_level = idx;
|
||||
}
|
||||
}
|
||||
|
||||
_opponent = OPPONENT_NAMES[_level];
|
||||
}
|
||||
|
||||
void Darts::closeDarts() {
|
||||
delete _dartImages;
|
||||
_dartImages = nullptr;
|
||||
}
|
||||
|
||||
void Darts::showNames(int playerNum) {
|
||||
Screen &screen = *_vm->_screen;
|
||||
byte color = playerNum == 0 ? PLAYER_COLOR : DART_COL_FORE;
|
||||
|
||||
// Print Holmes first
|
||||
if (playerNum == 0)
|
||||
screen.print(Common::Point(STATUS_INFO_X, STATUS_INFO_Y), PLAYER_COLOR + 3, "Holmes");
|
||||
else
|
||||
screen.print(Common::Point(STATUS_INFO_X, STATUS_INFO_Y), color, "Holmes");
|
||||
|
||||
screen._backBuffer1.fillRect(Common::Rect(STATUS_INFO_X, STATUS_INFO_Y + 10,
|
||||
STATUS_INFO_X + 31, STATUS_INFO_Y + 12), color);
|
||||
screen.slamArea(STATUS_INFO_X, STATUS_INFO_Y + 10, 31, 12);
|
||||
|
||||
// Second player
|
||||
color = playerNum == 1 ? PLAYER_COLOR : DART_COL_FORE;
|
||||
|
||||
if (playerNum != 0)
|
||||
screen.print(Common::Point(STATUS_INFO_X + 50, STATUS_INFO_Y), PLAYER_COLOR + 3,
|
||||
"%s", _opponent.c_str());
|
||||
else
|
||||
screen.print(Common::Point(STATUS_INFO_X + 50, STATUS_INFO_Y), color,
|
||||
"%s", _opponent.c_str());
|
||||
|
||||
screen._backBuffer1.fillRect(Common::Rect(STATUS_INFO_X + 50, STATUS_INFO_Y + 10,
|
||||
STATUS_INFO_X + 81, STATUS_INFO_Y + 12), color);
|
||||
screen.slamArea(STATUS_INFO_X + 50, STATUS_INFO_Y + 10, 81, 12);
|
||||
|
||||
// Make a copy of the back buffer to the secondary one
|
||||
screen._backBuffer2.SHblitFrom(screen._backBuffer1);
|
||||
}
|
||||
|
||||
void Darts::showStatus(int playerNum) {
|
||||
Screen &screen = *_vm->_screen;
|
||||
byte color;
|
||||
|
||||
// Copy scoring screen from secondary back buffer. This will erase any previously displayed status/score info
|
||||
screen._backBuffer1.SHblitFrom(screen._backBuffer2, Common::Point(STATUS_INFO_X, STATUS_INFO_Y + 10),
|
||||
Common::Rect(STATUS_INFO_X, STATUS_INFO_Y + 10, SHERLOCK_SCREEN_WIDTH, STATUS_INFO_Y + 48));
|
||||
|
||||
color = (playerNum == 0) ? PLAYER_COLOR : DART_COL_FORE;
|
||||
screen.print(Common::Point(STATUS_INFO_X + 6, STATUS_INFO_Y + 13), color, "%d", _dartScore1);
|
||||
|
||||
color = (playerNum == 1) ? PLAYER_COLOR : DART_COL_FORE;
|
||||
screen.print(Common::Point(STATUS_INFO_X + 56, STATUS_INFO_Y + 13), color, "%d", _dartScore2);
|
||||
screen.print(Common::Point(STATUS_INFO_X, STATUS_INFO_Y + 25), PLAYER_COLOR, "Round: %d", _roundNumber);
|
||||
screen.print(Common::Point(STATUS_INFO_X, STATUS_INFO_Y + 35), PLAYER_COLOR, "Turn Total: %d", _roundScore);
|
||||
screen.slamRect(Common::Rect(STATUS_INFO_X, STATUS_INFO_Y + 10, SHERLOCK_SCREEN_WIDTH, STATUS_INFO_Y + 48));
|
||||
}
|
||||
|
||||
int Darts::throwDart(int dartNum, int computer) {
|
||||
Events &events = *_vm->_events;
|
||||
Screen &screen = *_vm->_screen;
|
||||
Common::Point targetNum;
|
||||
int width, height;
|
||||
|
||||
events.clearKeyboard();
|
||||
|
||||
erasePowerBars();
|
||||
screen.print(Common::Point(DART_INFO_X, DART_INFO_Y), DART_COL_FORE, "Dart # %d", dartNum);
|
||||
|
||||
if (!computer) {
|
||||
screen.print(Common::Point(DART_INFO_X, DART_INFO_Y + 10), DART_COL_FORE, "Hit a key");
|
||||
screen.print(Common::Point(DART_INFO_X, DART_INFO_Y + 18), DART_COL_FORE, "to start");
|
||||
}
|
||||
|
||||
if (!computer) {
|
||||
while (!_vm->shouldQuit() && !dartHit())
|
||||
;
|
||||
} else {
|
||||
events.delay(10);
|
||||
}
|
||||
|
||||
if (_vm->shouldQuit())
|
||||
return 0;
|
||||
|
||||
screen._backBuffer1.SHblitFrom(screen._backBuffer2, Common::Point(DART_INFO_X, DART_INFO_Y - 1),
|
||||
Common::Rect(DART_INFO_X, DART_INFO_Y - 1, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT));
|
||||
screen.slamRect(Common::Rect(DART_INFO_X, DART_INFO_Y - 1, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT));
|
||||
|
||||
// If it's a computer player, choose a dart destination
|
||||
if (computer)
|
||||
targetNum = getComputerDartDest(computer - 1);
|
||||
|
||||
width = doPowerBar(Common::Point(DARTBARHX, DARTHORIZY), DART_BAR_FORE, targetNum.x, false);
|
||||
height = 101 - doPowerBar(Common::Point(DARTBARVX, DARTHEIGHTY), DART_BAR_FORE, targetNum.y, true);
|
||||
|
||||
// For human players, slight y adjustment
|
||||
if (computer == 0)
|
||||
height += 2;
|
||||
|
||||
// Copy the bars to the secondary back buffer so that they remain fixed at their selected values
|
||||
// whilst the dart is being animated at being thrown at the board
|
||||
screen._backBuffer2.SHblitFrom(screen._backBuffer1, Common::Point(DARTBARHX - 1, DARTHORIZY - 1),
|
||||
Common::Rect(DARTBARHX - 1, DARTHORIZY - 1, DARTBARHX + DARTBARSIZE + 3, DARTHORIZY + 10));
|
||||
screen._backBuffer2.SHblitFrom(screen._backBuffer1, Common::Point(DARTBARVX - 1, DARTHEIGHTY - 1),
|
||||
Common::Rect(DARTBARVX - 1, DARTHEIGHTY - 1, DARTBARVX + 11, DARTHEIGHTY + DARTBARSIZE + 3));
|
||||
|
||||
// Convert height and width to relative range of -50 to 50, where 0,0 is the exact centre of the board
|
||||
height -= 50;
|
||||
width -= 50;
|
||||
|
||||
Common::Point dartPos(111 + width * 2, 99 + height * 2);
|
||||
drawDartThrow(dartPos);
|
||||
|
||||
return dartScore(dartPos);
|
||||
}
|
||||
|
||||
void Darts::drawDartThrow(const Common::Point &pt) {
|
||||
Events &events = *_vm->_events;
|
||||
Screen &screen = *_vm->_screen;
|
||||
Common::Point pos(pt.x, pt.y + 2);
|
||||
Common::Rect oldDrawBounds;
|
||||
int delta = 9;
|
||||
|
||||
for (int idx = 4; idx < 23; ++idx) {
|
||||
ImageFrame &frame = (*_dartImages)[idx];
|
||||
|
||||
// Adjust draw position for animating dart
|
||||
if (idx < 13)
|
||||
pos.y -= delta--;
|
||||
else if (idx == 13)
|
||||
delta = 1;
|
||||
else
|
||||
pos.y += delta++;
|
||||
|
||||
// Draw the dart
|
||||
Common::Point drawPos(pos.x - frame._width / 2, pos.y - frame._height);
|
||||
screen._backBuffer1.SHtransBlitFrom(frame, drawPos);
|
||||
screen.slamArea(drawPos.x, drawPos.y, frame._width, frame._height);
|
||||
|
||||
// Handle erasing old dart frame area
|
||||
if (!oldDrawBounds.isEmpty())
|
||||
screen.slamRect(oldDrawBounds);
|
||||
|
||||
oldDrawBounds = Common::Rect(drawPos.x, drawPos.y, drawPos.x + frame._width, drawPos.y + frame._height);
|
||||
screen._backBuffer1.SHblitFrom(screen._backBuffer2, drawPos, oldDrawBounds);
|
||||
|
||||
events.wait(2);
|
||||
}
|
||||
|
||||
// Draw dart in final "stuck to board" form
|
||||
screen._backBuffer1.SHtransBlitFrom((*_dartImages)[22], Common::Point(oldDrawBounds.left, oldDrawBounds.top));
|
||||
screen._backBuffer2.SHtransBlitFrom((*_dartImages)[22], Common::Point(oldDrawBounds.left, oldDrawBounds.top));
|
||||
screen.slamRect(oldDrawBounds);
|
||||
}
|
||||
|
||||
void Darts::erasePowerBars() {
|
||||
Screen &screen = *_vm->_screen;
|
||||
|
||||
screen._backBuffer1.fillRect(Common::Rect(DARTBARHX, DARTHORIZY, DARTBARHX + DARTBARSIZE, DARTHORIZY + 10), BLACK);
|
||||
screen._backBuffer1.fillRect(Common::Rect(DARTBARVX, DARTHEIGHTY, DARTBARVX + 10, DARTHEIGHTY + DARTBARSIZE), BLACK);
|
||||
screen._backBuffer1.SHtransBlitFrom((*_dartImages)[2], Common::Point(DARTBARHX - 1, DARTHORIZY - 1));
|
||||
screen._backBuffer1.SHtransBlitFrom((*_dartImages)[3], Common::Point(DARTBARVX - 1, DARTHEIGHTY - 1));
|
||||
screen.slamArea(DARTBARHX - 1, DARTHORIZY - 1, DARTBARSIZE + 3, 11);
|
||||
screen.slamArea(DARTBARVX - 1, DARTHEIGHTY - 1, 11, DARTBARSIZE + 3);
|
||||
}
|
||||
|
||||
int Darts::doPowerBar(const Common::Point &pt, byte color, int goToPower, bool isVertical) {
|
||||
Events &events = *_vm->_events;
|
||||
Screen &screen = *_vm->_screen;
|
||||
bool done;
|
||||
int idx = 0;
|
||||
|
||||
events.clearEvents();
|
||||
events.delay(100);
|
||||
|
||||
// Display loop
|
||||
do {
|
||||
done = _vm->shouldQuit() || idx >= DARTBARSIZE;
|
||||
|
||||
if (idx == (goToPower - 1))
|
||||
// Reached target power for a computer player
|
||||
done = true;
|
||||
else if (goToPower == 0) {
|
||||
// Check for press
|
||||
if (dartHit())
|
||||
done = true;
|
||||
}
|
||||
|
||||
if (isVertical) {
|
||||
screen._backBuffer1.hLine(pt.x, pt.y + DARTBARSIZE - 1 - idx, pt.x + 8, color);
|
||||
screen._backBuffer1.SHtransBlitFrom((*_dartImages)[3], Common::Point(pt.x - 1, pt.y - 1));
|
||||
screen.slamArea(pt.x, pt.y + DARTBARSIZE - 1 - idx, 8, 2);
|
||||
} else {
|
||||
screen._backBuffer1.vLine(pt.x + idx, pt.y, pt.y + 8, color);
|
||||
screen._backBuffer1.SHtransBlitFrom((*_dartImages)[2], Common::Point(pt.x - 1, pt.y - 1));
|
||||
screen.slamArea(pt.x + idx, pt.y, 1, 8);
|
||||
}
|
||||
|
||||
if (!(idx % 8))
|
||||
events.wait(1);
|
||||
|
||||
++idx;
|
||||
} while (!done);
|
||||
|
||||
return MIN(idx * 100 / DARTBARSIZE, 100);
|
||||
}
|
||||
|
||||
int Darts::dartHit() {
|
||||
Events &events = *_vm->_events;
|
||||
|
||||
// Process pending events
|
||||
events.pollEvents();
|
||||
|
||||
if (events.actionHit()) {
|
||||
// Action was pressed, so return it
|
||||
Common::CustomEventType action = events.getAction();
|
||||
return action;
|
||||
}
|
||||
|
||||
if (events.kbHit()) {
|
||||
// Key was pressed, so return it
|
||||
Common::KeyState keyState = events.getKey();
|
||||
return keyState.keycode;
|
||||
}
|
||||
|
||||
_oldDartButtons = events._pressed;
|
||||
events.setButtonState();
|
||||
|
||||
// Only return true if the mouse button is newly pressed
|
||||
return (events._pressed && !_oldDartButtons) ? 1 : 0;
|
||||
}
|
||||
|
||||
int Darts::dartScore(const Common::Point &pt) {
|
||||
Common::Point pos(pt.x - 37, pt.y - 33);
|
||||
Graphics::Surface &scoreImg = (*_dartImages)[1]._frame;
|
||||
|
||||
if (pos.x < 0 || pos.y < 0 || pos.x >= scoreImg.w || pos.y >= scoreImg.h)
|
||||
// Not on the board
|
||||
return 0;
|
||||
|
||||
// On board, so get the score from the pixel at that position
|
||||
int score = *(const byte *)scoreImg.getBasePtr(pos.x, pos.y);
|
||||
return score;
|
||||
}
|
||||
|
||||
Common::Point Darts::getComputerDartDest(int playerNum) {
|
||||
Common::Point target;
|
||||
int score = playerNum == 0 ? _dartScore1 : _dartScore2;
|
||||
|
||||
if (score > 50) {
|
||||
// Aim for the bullseye
|
||||
target.x = target.y = 76;
|
||||
|
||||
if (_level <= 1 && _vm->getRandomNumber(1) == 1) {
|
||||
// Introduce margin of error
|
||||
target.x += _vm->getRandomNumber(21) - 10;
|
||||
target.y += _vm->getRandomNumber(21) - 10;
|
||||
}
|
||||
} else {
|
||||
int aim = score;
|
||||
|
||||
bool done;
|
||||
Common::Point pt;
|
||||
do {
|
||||
done = findNumberOnBoard(aim, pt);
|
||||
--aim;
|
||||
} while (!done);
|
||||
|
||||
target.x = 75 + ((pt.x - 75) * 20 / 27);
|
||||
target.y = 75 + ((pt.y - 75) * 2 / 3);
|
||||
}
|
||||
|
||||
// Pick a level of accuracy. The higher the level, the more accurate their throw will be
|
||||
int accuracy = _vm->getRandomNumber(10) + _level * 2;
|
||||
|
||||
if (accuracy <= 2) {
|
||||
target.x += _vm->getRandomNumber(71) - 35;
|
||||
target.y += _vm->getRandomNumber(71) - 35;
|
||||
} else if (accuracy <= 4) {
|
||||
target.x += _vm->getRandomNumber(51) - 25;
|
||||
target.y += _vm->getRandomNumber(51) - 25;
|
||||
} else if (accuracy <= 6) {
|
||||
target.x += _vm->getRandomNumber(31) - 15;
|
||||
target.y += _vm->getRandomNumber(31) - 15;
|
||||
} else if (accuracy <= 8) {
|
||||
target.x += _vm->getRandomNumber(21) - 10;
|
||||
target.y += _vm->getRandomNumber(21) - 10;
|
||||
} else if (accuracy <= 10) {
|
||||
target.x += _vm->getRandomNumber(11) - 5;
|
||||
target.y += _vm->getRandomNumber(11) - 5;
|
||||
}
|
||||
|
||||
if (target.x < 1)
|
||||
target.x = 1;
|
||||
if (target.y < 1)
|
||||
target.y = 1;
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
bool Darts::findNumberOnBoard(int aim, Common::Point &pt) {
|
||||
ImageFrame &board = (*_dartImages)[1];
|
||||
|
||||
// Scan board image for the special "center" pixels
|
||||
bool done = false;
|
||||
for (int yp = 0; yp < 132 && !done; ++yp) {
|
||||
const byte *srcP = (const byte *)board._frame.getBasePtr(0, yp);
|
||||
for (int xp = 0; xp < 147 && !done; ++xp, ++srcP) {
|
||||
int score = *srcP;
|
||||
|
||||
// Check for match
|
||||
if (score == aim) {
|
||||
done = true;
|
||||
|
||||
// Aim at non-double/triple numbers where possible
|
||||
if (aim < 21) {
|
||||
pt.x = xp + 5;
|
||||
pt.y = yp + 5;
|
||||
|
||||
score = *(const byte *)board._frame.getBasePtr(xp + 10, yp + 10);
|
||||
if (score != aim)
|
||||
// Not aiming at non-double/triple number yet
|
||||
done = false;
|
||||
} else {
|
||||
// Aiming at a double or triple
|
||||
pt.x = xp + 3;
|
||||
pt.y = yp + 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (aim == 3)
|
||||
pt.x += 15;
|
||||
pt.y = 132 - pt.y;
|
||||
|
||||
return done;
|
||||
}
|
||||
|
||||
} // End of namespace Scalpel
|
||||
|
||||
} // End of namespace Sherlock
|
||||
129
engines/sherlock/scalpel/scalpel_darts.h
Normal file
129
engines/sherlock/scalpel/scalpel_darts.h
Normal file
@@ -0,0 +1,129 @@
|
||||
/* 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 SHERLOCK_SCALPEL_DARTS_H
|
||||
#define SHERLOCK_SCALPEL_DARTS_H
|
||||
|
||||
#include "sherlock/image_file.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
namespace Scalpel {
|
||||
|
||||
class ScalpelEngine;
|
||||
|
||||
class Darts {
|
||||
private:
|
||||
ScalpelEngine *_vm;
|
||||
ImageFile *_dartImages;
|
||||
int _dartScore1, _dartScore2;
|
||||
int _roundNumber;
|
||||
int _level;
|
||||
int _computerPlayer;
|
||||
Common::String _opponent;
|
||||
bool _playerDartMode;
|
||||
int _roundScore;
|
||||
bool _oldDartButtons;
|
||||
|
||||
/**
|
||||
* Load the graphics needed for the dart game
|
||||
*/
|
||||
void loadDarts();
|
||||
|
||||
/**
|
||||
* Initializes the variables needed for the dart game
|
||||
*/
|
||||
void initDarts();
|
||||
|
||||
/**
|
||||
* Frees the images used by the dart game
|
||||
*/
|
||||
void closeDarts();
|
||||
|
||||
/**
|
||||
* Show the names of the people playing, Holmes and his opponent
|
||||
*/
|
||||
void showNames(int playerNum);
|
||||
|
||||
/**
|
||||
* Show the player score and game status
|
||||
*/
|
||||
void showStatus(int playerNum);
|
||||
|
||||
/**
|
||||
* Throws a single dart.
|
||||
* @param dartNum Dart number
|
||||
* @param computer 0 = Player, 1 = 1st player computer, 2 = 2nd player computer
|
||||
* @returns Score for what dart hit
|
||||
*/
|
||||
int throwDart(int dartNum, int computer);
|
||||
|
||||
/**
|
||||
* Draw a dart moving towards the board
|
||||
*/
|
||||
void drawDartThrow(const Common::Point &pt);
|
||||
|
||||
/**
|
||||
* Erases the power bars
|
||||
*/
|
||||
void erasePowerBars();
|
||||
|
||||
/**
|
||||
* Show a gradually incrementing incrementing power that bar. If goToPower is provided, it will
|
||||
* increment to that power level ignoring all keyboard input (ie. for computer throws).
|
||||
* Otherwise, it will increment until either a key/mouse button is pressed, or it reaches the end
|
||||
*/
|
||||
int doPowerBar(const Common::Point &pt, byte color, int goToPower, bool isVertical);
|
||||
|
||||
/**
|
||||
* Returns true if a mouse button or key is pressed.
|
||||
*/
|
||||
int dartHit();
|
||||
|
||||
/**
|
||||
* Return the score of the given location on the dart-board
|
||||
*/
|
||||
int dartScore(const Common::Point &pt);
|
||||
|
||||
/**
|
||||
* Calculates where a computer player is trying to throw their dart, and choose the actual
|
||||
* point that was hit with some margin of error
|
||||
*/
|
||||
Common::Point getComputerDartDest(int playerNum);
|
||||
|
||||
/**
|
||||
* Returns the center position for the area of the dartboard with a given number
|
||||
*/
|
||||
bool findNumberOnBoard(int aim, Common::Point &pt);
|
||||
public:
|
||||
Darts(ScalpelEngine *vm);
|
||||
|
||||
/**
|
||||
* Main method for playing darts game
|
||||
*/
|
||||
void playDarts();
|
||||
};
|
||||
|
||||
} // End of namespace Scalpel
|
||||
|
||||
} // End of namespace Sherlock
|
||||
|
||||
#endif
|
||||
90
engines/sherlock/scalpel/scalpel_debugger.cpp
Normal file
90
engines/sherlock/scalpel/scalpel_debugger.cpp
Normal file
@@ -0,0 +1,90 @@
|
||||
/* 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 "sherlock/scalpel/scalpel_debugger.h"
|
||||
#include "sherlock/sherlock.h"
|
||||
#include "audio/audiostream.h"
|
||||
#include "audio/mixer.h"
|
||||
#include "audio/decoders/aiff.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
namespace Scalpel {
|
||||
|
||||
ScalpelDebugger::ScalpelDebugger(SherlockEngine *vm) : Debugger(vm) {
|
||||
registerCmd("3do_playmovie", WRAP_METHOD(ScalpelDebugger, cmd3DO_PlayMovie));
|
||||
registerCmd("3do_playaudio", WRAP_METHOD(ScalpelDebugger, cmd3DO_PlayAudio));
|
||||
}
|
||||
|
||||
bool ScalpelDebugger::cmd3DO_PlayMovie(int argc, const char **argv) {
|
||||
if (argc != 2) {
|
||||
debugPrintf("Format: 3do_playmovie <3do-movie-file>\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
// play gets postponed until debugger is closed
|
||||
Common::String filename = argv[1];
|
||||
_3doPlayMovieFile = filename;
|
||||
|
||||
return cmdExit(0, nullptr);
|
||||
}
|
||||
|
||||
bool ScalpelDebugger::cmd3DO_PlayAudio(int argc, const char **argv) {
|
||||
if (argc != 2) {
|
||||
debugPrintf("Format: 3do_playaudio <3do-audio-file>\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
Common::File *file = new Common::File();
|
||||
if (!file->open(argv[1])) {
|
||||
debugPrintf("can not open specified audio file\n");
|
||||
delete file;
|
||||
return true;
|
||||
}
|
||||
|
||||
Audio::AudioStream *testStream;
|
||||
Audio::SoundHandle testHandle;
|
||||
|
||||
// Try to load the given file as AIFF/AIFC
|
||||
testStream = Audio::makeAIFFStream(file, DisposeAfterUse::YES);
|
||||
|
||||
if (testStream) {
|
||||
g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &testHandle, testStream);
|
||||
_vm->_events->clearEvents();
|
||||
|
||||
while ((!_vm->shouldQuit()) && g_system->getMixer()->isSoundHandleActive(testHandle)) {
|
||||
_vm->_events->pollEvents();
|
||||
g_system->delayMillis(10);
|
||||
if (_vm->_events->kbHit()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
debugPrintf("playing completed\n");
|
||||
g_system->getMixer()->stopHandle(testHandle);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // End of namespace Scalpel
|
||||
|
||||
} // End of namespace Sherlock
|
||||
53
engines/sherlock/scalpel/scalpel_debugger.h
Normal file
53
engines/sherlock/scalpel/scalpel_debugger.h
Normal file
@@ -0,0 +1,53 @@
|
||||
/* 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 SHERLOCK_SCALPEL_DEBUGGER_H
|
||||
#define SHERLOCK_SCALPEL_DEBUGGER_H
|
||||
|
||||
#include "sherlock/debugger.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
class SherlockEngine;
|
||||
|
||||
namespace Scalpel {
|
||||
|
||||
class ScalpelDebugger : public Debugger {
|
||||
private:
|
||||
/**
|
||||
* Plays a 3DO movie
|
||||
*/
|
||||
bool cmd3DO_PlayMovie(int argc, const char **argv);
|
||||
|
||||
/**
|
||||
* Plays a 3DO audio
|
||||
*/
|
||||
bool cmd3DO_PlayAudio(int argc, const char **argv);
|
||||
public:
|
||||
ScalpelDebugger(SherlockEngine *vm);
|
||||
~ScalpelDebugger() override {}
|
||||
};
|
||||
|
||||
} // End of namespace Scalpel
|
||||
|
||||
} // End of namespace Sherlock
|
||||
|
||||
#endif /* SHERLOCK_DEBUGGER_H */
|
||||
889
engines/sherlock/scalpel/scalpel_fixed_text.cpp
Normal file
889
engines/sherlock/scalpel/scalpel_fixed_text.cpp
Normal file
@@ -0,0 +1,889 @@
|
||||
/* 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 "sherlock/scalpel/scalpel_fixed_text.h"
|
||||
#include "sherlock/sherlock.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
namespace Scalpel {
|
||||
|
||||
static const char *const fixedTextEN[] = {
|
||||
// Game hotkeys
|
||||
"LMTPOCIUGJFS",
|
||||
// SH1: Window buttons
|
||||
"EExit",
|
||||
"UUp",
|
||||
"DDown",
|
||||
// SH1: Inventory buttons
|
||||
"EExit",
|
||||
"LLook",
|
||||
"UUse",
|
||||
"GGive",
|
||||
// SH1: Journal text
|
||||
"Watson's Journal",
|
||||
"Page %d",
|
||||
// SH1: Journal buttons
|
||||
"EExit",
|
||||
"BBack 10",
|
||||
"UUp",
|
||||
"DDown",
|
||||
"AAhead 10",
|
||||
"SSearch",
|
||||
"FFirst Page",
|
||||
"LLast Page",
|
||||
"PPrint Text",
|
||||
// SH1: Journal search
|
||||
"EExit",
|
||||
"BBackward",
|
||||
"FForward",
|
||||
"Text Not Found !",
|
||||
// SH1: Settings
|
||||
"EExit",
|
||||
"MMusic on",
|
||||
"MMusic off",
|
||||
"PPortraits on",
|
||||
"PPortraits off",
|
||||
"JJoystick off",
|
||||
"NNew Font Style",
|
||||
"SSound Effects on",
|
||||
"SSound Effects off",
|
||||
"WWindows Slide",
|
||||
"WWindows Appear",
|
||||
"CCalibrate Joystick",
|
||||
"AAuto Help left",
|
||||
"AAuto Help right",
|
||||
"VVoices on",
|
||||
"VVoices off",
|
||||
"FFade by Pixel",
|
||||
"FFade Directly",
|
||||
"KKey Pad Slow",
|
||||
"KKey Pad Fast",
|
||||
// Load/Save
|
||||
"EExit",
|
||||
"LLoad",
|
||||
"SSave",
|
||||
"UUp",
|
||||
"DDown",
|
||||
"QQuit",
|
||||
// Quit Game
|
||||
"Are you sure you wish to Quit ?",
|
||||
"YYes",
|
||||
"NNo",
|
||||
// SH1: Press key text
|
||||
"PPress any Key for More.",
|
||||
"PPress any Key to Continue.",
|
||||
// SH1: Initial Inventory
|
||||
"A message requesting help",
|
||||
"A number of business cards",
|
||||
"Opera Tickets",
|
||||
"Cuff Link",
|
||||
"Wire Hook",
|
||||
"Note",
|
||||
"An open pocket watch",
|
||||
"A piece of paper with numbers on it",
|
||||
"A letter folded many times",
|
||||
"Tarot Cards",
|
||||
"An ornate key",
|
||||
"A pawn ticket",
|
||||
// SH1: User Interface
|
||||
"No, thank you.",
|
||||
"You can't do that.",
|
||||
"Done...",
|
||||
"Use ",
|
||||
" on %s",
|
||||
"Give ",
|
||||
" to %s",
|
||||
// SH1: People names
|
||||
"Sherlock Holmes",
|
||||
"Dr. Watson",
|
||||
"Inspector Lestrade",
|
||||
"Constable O'Brien",
|
||||
"Constable Lewis",
|
||||
"Sheila Parker",
|
||||
"Henry Carruthers",
|
||||
"Lesley",
|
||||
"An Usher",
|
||||
"Fredrick Epstein",
|
||||
"Mrs. Worthington",
|
||||
"The Coach",
|
||||
"A Player",
|
||||
"Tim",
|
||||
"James Sanders",
|
||||
"Belle",
|
||||
"Cleaning Girl",
|
||||
"Wiggins",
|
||||
"Paul",
|
||||
"The Bartender",
|
||||
"A Dirty Drunk",
|
||||
"A Shouting Drunk",
|
||||
"A Staggering Drunk",
|
||||
"The Bouncer",
|
||||
"The Coroner",
|
||||
"Reginald Snipes",
|
||||
"George Blackwood",
|
||||
"Lars",
|
||||
"The Chemist",
|
||||
"Inspector Gregson",
|
||||
"Jacob Farthington",
|
||||
"Mycroft",
|
||||
"Old Sherman",
|
||||
"Richard",
|
||||
"The Barman",
|
||||
"A Dandy Player",
|
||||
"A Rough-looking Player",
|
||||
"A Spectator",
|
||||
"Robert Hunt",
|
||||
"Violet",
|
||||
"Pettigrew",
|
||||
"Augie",
|
||||
"Anna Carroway",
|
||||
"A Guard",
|
||||
"Antonio Caruso",
|
||||
"Toby the Dog",
|
||||
"Simon Kingsley",
|
||||
"Alfred",
|
||||
"Lady Brumwell",
|
||||
"Madame Rosa",
|
||||
"Joseph Moorehead",
|
||||
"Mrs. Beale",
|
||||
"Felix",
|
||||
"Hollingston",
|
||||
"Constable Callaghan",
|
||||
"Sergeant Duncan",
|
||||
"Lord Brumwell",
|
||||
"Nigel Jaimeson",
|
||||
"Jonas",
|
||||
"Constable Dugan"
|
||||
};
|
||||
|
||||
// sharp-s : 0xE1 / octal 341
|
||||
// small a-umlaut: 0x84 / octal 204
|
||||
// small o-umlaut: 0x94 / octal 224
|
||||
// small u-umlaut: 0x81 / octal 201
|
||||
static const char *const fixedTextDE[] = {
|
||||
// Game hotkeys
|
||||
"SBRNOCTEGADU", // original: did not support hotkeys for actions
|
||||
// SH1: Window buttons
|
||||
"ZZur\201ck",
|
||||
"HHoch",
|
||||
"RRunter",
|
||||
// SH1: Inventory buttons
|
||||
"ZZur\201ck",
|
||||
"SSchau",
|
||||
"BBenutze",
|
||||
"GGib",
|
||||
// SH1: Journal text
|
||||
"Watsons Tagebuch",
|
||||
"Seite %d",
|
||||
// SH1: Journal buttons
|
||||
"ZZur\201ck", // original: "Zur\201ck"
|
||||
"o10 hoch",
|
||||
"HHoch",
|
||||
"RRunter",
|
||||
"u10 runter", // original: "10 runter"
|
||||
"SSuche",
|
||||
"EErste Seite",
|
||||
"LLetzte Seite",
|
||||
"DDrucke Text",
|
||||
// SH1: Journal search
|
||||
"ZZur\201ck",
|
||||
"RR\201ckw\204rts", // original: "Backward"
|
||||
"VVorw\204rts", // original: "Forward"
|
||||
"Text nicht gefunden!",
|
||||
// SH1: Settings
|
||||
"ZZur\201ck", // original interpreter: "Exit"
|
||||
"MMusik an",
|
||||
"MMusik aus",
|
||||
"PPortr\204ts an", // original interpreter: "Portraits"
|
||||
"PPortr\204ts aus",
|
||||
"JJoystick aus",
|
||||
"NNeue Schrift",
|
||||
"GGer\204uscheffekte on", // original interpreter: "Effekte"
|
||||
"GGer\204uscheffekte off",
|
||||
"FFenster gleitend",
|
||||
"FFenster direkt",
|
||||
"JJustiere Joystick",
|
||||
"HHilfe links",
|
||||
"HHilfe rechts",
|
||||
"SSprache an",
|
||||
"SSprache aus",
|
||||
"cSchnitt",
|
||||
"BBlende",
|
||||
"CCursor langsam",
|
||||
"CCursor schnell",
|
||||
// Load/Save
|
||||
"ZZur\201ck",
|
||||
"LLaden",
|
||||
"SSichern",
|
||||
"HHoch",
|
||||
"RRunter",
|
||||
"EEnde",
|
||||
// Quit Game
|
||||
"Das Spiel verlassen ?",
|
||||
"JJa",
|
||||
"NNein",
|
||||
// SH1: Press key text
|
||||
"MMehr auf Tastendruck...",
|
||||
"BBeliebige Taste dr\201cken.",
|
||||
// SH1: Initial Inventory
|
||||
"Ein Hilferuf von Lestrade",
|
||||
"Holmes' Visitenkarten",
|
||||
"Karten f\201rs Opernhaus",
|
||||
"Manschettenkn\224pfe",
|
||||
"Zum Haken verbogener Drahtkorb",
|
||||
"Mitteilung am Epstein",
|
||||
"Eine offene Taschenuhr",
|
||||
"Ein Zettel mit Zahlen drauf",
|
||||
"Ein mehrfach gefalteter Briefbogen",
|
||||
"Ein Tarot-Kartenspiel", // original interpreter: "Ein Tarock-Kartenspiel" [sic]
|
||||
"Ein verzierter Schl\201ssel",
|
||||
"Ein Pfandschein",
|
||||
// SH1: User Interface
|
||||
"Nein, vielen Dank.",
|
||||
"Nein, das geht wirklich nicht.", // original: "Nein, das geht wirklich nicht"
|
||||
"Fertig...",
|
||||
"Benutze ",
|
||||
" mit %s",
|
||||
"Gib ", // original: "Gebe "
|
||||
" an %s", // original: " zu %s"
|
||||
// SH1: People names
|
||||
"Sherlock Holmes",
|
||||
"Dr. Watson",
|
||||
"Inspektor Lestrade",
|
||||
"Konstabler O'Brien",
|
||||
"Konstabler Lewis",
|
||||
"Sheila Parker",
|
||||
"Henry Carruthers",
|
||||
"Lesley",
|
||||
"Platzanweiser",
|
||||
"Fredrick Epstein",
|
||||
"Mrs. Worthington",
|
||||
"Der Trainer",
|
||||
"Ein Spieler",
|
||||
"Tim",
|
||||
"James Sanders",
|
||||
"Belle",
|
||||
"Putzm\204dchen",
|
||||
"Wiggins",
|
||||
"Paul",
|
||||
"Gastwirt",
|
||||
"Schmutziger Betrunkener",
|
||||
"Lallender Betrunkener",
|
||||
"Torkelnder Betrunkener",
|
||||
"The Bouncer",
|
||||
"Der Leichenbeschauer",
|
||||
"Reginald Snipes",
|
||||
"George Blackwood",
|
||||
"Lars",
|
||||
"Apotheker",
|
||||
"Inspektor Gregson",
|
||||
"Jacob Farthington",
|
||||
"Mycroft",
|
||||
"Old Sherman",
|
||||
"Richard",
|
||||
"Barkeeper",
|
||||
"Jock Mahoney",
|
||||
"Nobby Charleton",
|
||||
"Zuschauer",
|
||||
"Robert Hunt",
|
||||
"Violet",
|
||||
"Pettigrew",
|
||||
"Augie",
|
||||
"Anna Carroway",
|
||||
"Wache",
|
||||
"Antonio Caruso",
|
||||
"Toby the Dog",
|
||||
"Simon Kingsley",
|
||||
"Alfred",
|
||||
"Lady Brumwell",
|
||||
"Madame Rosa",
|
||||
"Joseph Moorehead",
|
||||
"Mrs. Beale",
|
||||
"Felix",
|
||||
"Hollingston",
|
||||
"Konstabler Callaghan",
|
||||
"Sergeant Duncan",
|
||||
"Lord Brumwell",
|
||||
"Nigel Jaimeson",
|
||||
"Jonas",
|
||||
"Konstabler Dugan"
|
||||
};
|
||||
|
||||
// up-side down exclamation mark - 0xAD / octal 255
|
||||
// up-side down question mark - 0xA8 / octal 250
|
||||
// n with a wave on top - 0xA4 / octal 244
|
||||
// more characters see engines/sherlock/fixed_text.cpp
|
||||
static const char *const fixedTextES[] = {
|
||||
// Game hotkeys
|
||||
"VMHTACIUDNFO",
|
||||
// SH1: Window buttons
|
||||
"aSalir", // original interpreter: "Exit"
|
||||
"SSubir",
|
||||
"BBajar",
|
||||
// SH1: Inventory buttons
|
||||
"SSalir", // original interpreter: "Exit"
|
||||
"MMirar",
|
||||
"UUsar",
|
||||
"DDar",
|
||||
// SH1: Journal text
|
||||
"Diario de Watson",
|
||||
"Pagina %d",
|
||||
// SH1: Journal buttons
|
||||
"aSalir", // original interpreter: "Exit"
|
||||
"RRetroceder",
|
||||
"SSubir",
|
||||
"JbaJar",
|
||||
"AAdelante",
|
||||
"BBuscar",
|
||||
"11a pagina",
|
||||
"UUlt pagina",
|
||||
"IImprimir",
|
||||
// SH1: Journal search
|
||||
"SSalir", // original interpreter: "Exit"
|
||||
"RRetroceder",
|
||||
"AAvanzar",
|
||||
"Texto no encontrado!",
|
||||
// SH1: Settings
|
||||
"aSalir", // original interpreter: "Exit"
|
||||
"MMusica si",
|
||||
"MMusica no",
|
||||
"RRetratos si",
|
||||
"RRetratos no",
|
||||
"JJoystick no",
|
||||
"NNuevo fuente",
|
||||
"Sefectos Sonido si",
|
||||
"Sefectos Sonido no",
|
||||
"Tven Tanas desliz.",
|
||||
"Tven Tanas aparecen",
|
||||
"CCalibrar Joystick",
|
||||
"yAyuda lzq", // TODO: check this
|
||||
"yAyuda Dcha",
|
||||
"VVoces si",
|
||||
"VVoces no",
|
||||
"FFundido a pixel",
|
||||
"FFundido directo",
|
||||
"eTeclado lento",
|
||||
"eTeclado rapido",
|
||||
// Load/Save
|
||||
"aSalir", // original interpreter: "Exit"
|
||||
"CCargar",
|
||||
"GGrabar",
|
||||
"SSubir",
|
||||
"BBajar",
|
||||
"AAcabar",
|
||||
// Quit Game
|
||||
"\250Seguro que quieres Acabar?",
|
||||
"SSi",
|
||||
"NNo",
|
||||
// SH1: Press key text
|
||||
"TTecla para ver mas",
|
||||
"TTecla para continuar",
|
||||
// SH1: Initial Inventory
|
||||
"Un mensaje solicitando ayuda",
|
||||
"Unas cuantas tarjetas de visita",
|
||||
"Entradas para la opera",
|
||||
"Unos gemelos",
|
||||
"Un gancho de alambre",
|
||||
"Una nota",
|
||||
"Un reloj de bolsillo abierto",
|
||||
"Un trozo de papel con unos numeros",
|
||||
"Un carta muy plegada",
|
||||
"Unas cartas de Tarot",
|
||||
"Una llave muy vistosa",
|
||||
"Una papeleta de empe\244o",
|
||||
// SH1: User Interface
|
||||
"No, gracias.",
|
||||
"No puedes hacerlo.", // original: "No puedes hacerlo"
|
||||
"Hecho...",
|
||||
"Usar ",
|
||||
" sobre %s",
|
||||
"Dar ",
|
||||
" a %s",
|
||||
// SH1: People names
|
||||
"Sherlock Holmes",
|
||||
"Dr. Watson",
|
||||
"El inspector Lestrade",
|
||||
"El agente O'Brien",
|
||||
"El agente Lewis",
|
||||
"Sheila Parker",
|
||||
"Henry Carruthers",
|
||||
"Lesley",
|
||||
"Un ujier",
|
||||
"Fredrick Epstein",
|
||||
"Mrs. Worthington",
|
||||
"El entrenador",
|
||||
"El jugador",
|
||||
"Tim",
|
||||
"James Sanders",
|
||||
"Belle",
|
||||
"La chica de la limpieza",
|
||||
"Wiggins",
|
||||
"Paul",
|
||||
"El barman",
|
||||
"Un sucio borracho",
|
||||
"Un borracho griton",
|
||||
"Un tambaleante borracho",
|
||||
"El gorila",
|
||||
"El forense",
|
||||
"Reginald Snipes",
|
||||
"George Blackwood",
|
||||
"Lars",
|
||||
"El quimico",
|
||||
"El inspector Gregson",
|
||||
"Jacob Farthington",
|
||||
"Mycroft",
|
||||
"Old Sherman",
|
||||
"Richard",
|
||||
"El barman",
|
||||
"Un jugador dandy",
|
||||
"Un duro jugador",
|
||||
"Un espectador",
|
||||
"Robert Hunt",
|
||||
"Violeta",
|
||||
"Pettigrew",
|
||||
"Augie",
|
||||
"Anna Carroway",
|
||||
"Un guarda",
|
||||
"Antonio Caruso",
|
||||
"El perro Toby",
|
||||
"Simon Kingsley",
|
||||
"Alfred",
|
||||
"Lady Brumwell",
|
||||
"Madame Rosa",
|
||||
"Joseph Moorehead",
|
||||
"Mrs. Beale",
|
||||
"Felix",
|
||||
"Hollingston",
|
||||
"El agente Callaghan",
|
||||
"El sargento Duncan",
|
||||
"Lord Brumwell",
|
||||
"Nigel Jaimeson",
|
||||
"Jonas",
|
||||
"El agente Dugan"
|
||||
};
|
||||
|
||||
static const char *const fixedTextZH[] = {
|
||||
// Game hotkeys
|
||||
"LMTPOCIUGJFS",
|
||||
// SH1: Window buttons
|
||||
"E\xc2\xf7\xb6\x7d(E)", /* "E離開"; "EExit" */
|
||||
"U\xa4\x57(U)", /* "U上"; "UUp" */
|
||||
"D\xa4\x55(D)", /* "D下"; "DDown" */
|
||||
// SH1: Inventory buttons
|
||||
"E\xc2\xf7\xb6\x7d(E)", /* "E離開"; "EExit" */
|
||||
"L\xac\x64\xac\xdd(L)", /* "L查看"; "LLook" */
|
||||
"U\xa8\xcf\xa5\xce(U)", /* "U使用"; "UUse" */
|
||||
"G\xb5\xb9\xbb\x50(G)", /* "G給與"; "GGive" */
|
||||
// TODO: Inventorty next/prev buttons:
|
||||
//"\xa5\xaa\xad\xb6", /* "左頁"; */
|
||||
//"\xa5\xaa\xa4\x40", /* "左一"; */
|
||||
//"\xa5\x6b\xa4\x40", /* "右一"; */
|
||||
//"\xa5\x6b\xad\xb6", /* "右頁"; */
|
||||
// SH1: Journal text
|
||||
"\xb5\xd8\xa5\xcd\xaa\xba\xb5\xa7\xb0\x4f", /* "華生的筆記"; "Watson's Journal" */
|
||||
"\xb2\xc4\x25\x64\xad\xb6", /* "第%d頁"; "Page %d" */
|
||||
// SH1: Journal buttons
|
||||
"E\xc2\xf7\xb6\x7d(E)", /* "E離開"; "EExit" */
|
||||
"B\xab\x65\xa4\x51\xad\xb6(B)", /* "B前十頁"; "BBack 10" */
|
||||
"U\xa4\x57(U)", /* "U上"; "UUp" */
|
||||
"D\xa4\x55(D)", /* "D下"; "DDown" */
|
||||
"A\xab\xe1\xa4\x51\xad\xb6(A)", /* "A後十頁"; "AAhead 10" */
|
||||
"S\xb4\x4d\xa7\xe4(S)", /* "S尋找"; "SSearch" */
|
||||
"F\xad\xba\xad\xb6(F)", /* "F首頁"; "FFirst Page" */
|
||||
"L\xa9\xb3\xad\xb6(L)", /* "L底頁"; "LLast Page" */
|
||||
"P\xa6\x43\xa6\x4c(P)", /* "列印"; "PPrint Text" */
|
||||
// SH1: Journal search
|
||||
"\xc2\xf7\xb6\x7d", /* "E離開"; "Exit" */
|
||||
"\xab\x65\xb4\x4d", /* "前尋"; "Backward" */
|
||||
"\xab\xe1\xb4\x4d", /* "後尋"; "Forward" */
|
||||
"\xa8\x53\xa6\xb3\xa7\xe4\xa8\xec\x21", /* "沒有找到!"; "Text Not Found !" */
|
||||
// SH1: Settings
|
||||
"E\xc2\xf7\xb6\x7d(E)", /* "離開"; "EExit" */
|
||||
"M\xad\xb5\xbc\xd6\xb6\x7d(M)", /* "M音樂開"; "MMusic on" */
|
||||
"M\xad\xb5\xbc\xd6\xc3\xf6(M)", /* "M音樂關"; "MMusic off" */
|
||||
"P\xa8\x76\xb9\xb3\xb6\x7d(P)", /* "P肖像開"; "PPortrait on" */
|
||||
"P\xa8\x76\xb9\xb3\xc3\xf6(P)", /* "P肖像關"; "PPortrait off" */
|
||||
"JJoystick off", // Not used in Chinese as this button is skipped
|
||||
"NNew Font Style", // Not used in Chinese as only one font is available
|
||||
"S\xad\xb5\xae\xc4\xb6\x7d(S)", /* "S音效開"; "SSound Effects on" */
|
||||
"S\xad\xb5\xae\xc4\xc3\xf6(S)", /* "S音效關"; "SSound Effects off" */
|
||||
"W\xb5\xf8\xb5\xa1\xb7\xc6\xb1\xb2(W)", /* "W視窗滑捲"; "WWindow Slide Scroll" */
|
||||
"W\xb5\xf8\xb5\xa1\xa8\x71\xa5\x58(W)", /* "W視窗秀出"; "WWindow Show" */
|
||||
"C\xbd\xd5\xbe\xe3\xb7\x6e\xb1\xec(C)", /* "調整搖桿"; "CCalibrate Joystick" */
|
||||
"A\xbb\xb2\xa7\x55\xa5\xaa(A)", /* "A輔助左"; "AAuto Help left" */
|
||||
"A\xbb\xb2\xa7\x55\xa5\x6b(A)", /* "A輔助右"; "AAuto Help right" */
|
||||
"VVoices on", // Not used in Chinese as no voices are available
|
||||
"VVoices off", // Not used in Chinese as no voices are available
|
||||
"F\xb2\x48\xa5\x58\xc2\x49\xaa\xac(F)", /* "F淡出點狀"; "FFade by Pixel" */
|
||||
"F\xb2\x48\xa5\x58\xaa\xbd\xb1\xb5(F)", /* "F淡出直接"; "FFade Directly" */
|
||||
"K\xc1\xe4\xaa\xa9\xba\x43(K)", /* "K鍵版慢"; "KKey Pad Slow" */
|
||||
"K\xc1\xe4\xaa\xa9\xa7\xd6(K)", /* "K鍵版快"; "KKey Pad Fast" */
|
||||
// Load/Save
|
||||
"EExit", // TODO
|
||||
"L\xb8\xfc\xa4\x4a(L)", /* "L載入"; "LLoad" */
|
||||
"S\xc0\x78\xa6\x73(S)", /* "S儲存"; "SSave" */
|
||||
"U\xa4\x57(U)", /* "U上"; "UUp" */
|
||||
"D\xa4\x55(D)", /* "D下"; "DDown" */
|
||||
"Q\xb5\xb2\xa7\xf4(Q)", /* "Q結束"; "QQuit" */
|
||||
// Quit Game
|
||||
"\xb1\x7a\xbd\x54\xa9\x77\xad\x6e\xb5\xb2\xa7\xf4\xb9\x43\xc0\xb8\xb6\xdc\x3f", /* "您確定要結束遊戲嗎?"; "Are you sure you wish to Quit ?" */
|
||||
"Y\xac\x4f(Y)", /* "Y是"; "YYes" */
|
||||
"N\xa4\xa3(N)", /* "N不"; "NNo" */
|
||||
// SH1: Press key text
|
||||
"P\xbd\xd0\xab\xf6\xa5\xf4\xb7\x4e\xc1\xe4\xc4\x7e\xc4\xf2\xa4\x55\xad\xb6\xa4\xba\xae\x65.(P)", /* "P請按任意鍵繼續下頁內容."; "PPress any Key for More." */
|
||||
"P\xbd\xd0\xab\xf6\xa5\xf4\xb7\x4e\xc1\xe4\xc4\x7e\xc4\xf2.(P)", /* "P請按任意鍵繼續."; "PPress any Key to Continue." */
|
||||
// SH1: Initial Inventory
|
||||
"\xab\x4b\xb1\xf8\xaf\xc8", /* "便條紙"; "A message requesting help" */
|
||||
"\xba\xd6\xba\xb8\xbc\xaf\xb4\xb5\xaa\xba\xa6\x57\xa4\xf9", /* "福爾摩斯的名片"; "A number of business cards" */
|
||||
"\xba\x71\xbc\x40\xb0\x7c\xc1\x70\xb2\xbc", /* "歌劇院聯票"; "Opera Tickets" */
|
||||
"\xb3\x53\xb3\xa7", /* "袖釦"; "Cuff Link" */
|
||||
"\xc5\x4b\xb5\xb7\xa4\xc4", /* "鐵絲勾"; "Wire Hook" */
|
||||
"\xa9\xf1\xa6\xe6\xb1\xf8", /* "放行條"; "Note" */
|
||||
"\xa5\xb4\xb6\x7d\xaa\xba\xc3\x68\xbf\xf6", /* "打開的懷錶"; "An open pocket watch" */
|
||||
"\xaf\xc8", /* "紙"; "A piece of paper with numbers on it" */
|
||||
"\xab\x48", /* "信"; "A letter folded many times" */
|
||||
"\xaf\xc8\xb5\x50", /* "紙牌"; "Tarot Cards" */
|
||||
"\xb5\xd8\xc4\x52\xaa\xba\xc6\x5f\xb0\xcd", /* "華麗的鑰匙"; "An ornate key" */
|
||||
"\xb7\xed\xb2\xbc", /* "當票"; "A pawn ticket" */
|
||||
// SH1: User Interface
|
||||
"\xa4\xa3\x2c\xc1\xc2\xc1\xc2\xb1\x7a\x2e", /* "不,謝謝您."; "No, thank you." */
|
||||
"You can't do that.", // TODO
|
||||
"\xa7\xb9\xb2\xa6\x2e\x2e\x2e", /* "完畢..."; "Done..." */
|
||||
"Use ", // TODO
|
||||
" on %s", // TODO
|
||||
"Give ", // TODO
|
||||
" to %s", // TODO
|
||||
// SH1: People names
|
||||
"\xba\xd6\xba\xb8\xbc\xaf\xb4\xb5", /* "福爾摩斯"; "Sherlock Holmes" */
|
||||
"\xb5\xd8\xa5\xcd\xc2\xe5\xa5\xcd", /* "華生醫生"; "Dr. Watson" */
|
||||
"\xb5\xdc\xb4\xb5\xb1\x5a\xbc\x77\xb1\xb4\xaa\xf8", /* "萊斯崔德探長"; "Inspector Lestrade" */
|
||||
"\xb6\xf8\xa5\xac\xb5\xdc\xa6\x77\xa8\xb5\xa6\xf5", /* "奧布萊安巡佐"; "Constable O'Brien" */
|
||||
"\xb9\x70\xba\xfb\xb4\xb5\xa8\xb5\xa6\xf5", /* "雷維斯巡佐"; "Constable Lewis" */
|
||||
"\xdf\xc4\xdb\x69\x2e\xa9\xac\xa7\x4a", /* "葸菈.帕克"; "Sheila Parker" */
|
||||
"\xa6\xeb\xa7\x51\x2e\xa5\x64\xcd\xba\xb7\xe6", /* "亨利.卡芮瑟"; "Henry Carruthers" */
|
||||
"\xb5\xdc\xb5\xb7\xb2\xfa", /* "萊絲莉"; "Lesley" */
|
||||
"\xa4\xde\xae\x79\xad\xfb", /* "引座員"; "An Usher" */
|
||||
"\xa5\xb1\xb7\xe7\xbc\x77\x2e\xa6\xe3\xa7\x42\xb4\xb5\xa5\xc5", /* "弗瑞德.艾伯斯汀"; "Fredrick Epstein" */
|
||||
"\xb4\xec\xa8\xaf\xb9\x79\xa4\xd3\xa4\xd3", /* "渥辛頓太太"; "Mrs. Worthington" */
|
||||
"\xb1\xd0\xbd\x6d", /* "教練"; "The Coach" */
|
||||
"\xa4\x40\xa6\x57\xb6\xa4\xad\xfb", /* "一名隊員"; "A Player" */
|
||||
"\xb4\xa3\xa9\x69", /* "提姆"; "Tim", */
|
||||
"\xa9\x69\xa4\x68\x2e\xae\xe1\xbc\x77\xb4\xb5", /* "姆士.桑德斯"; "James Sanders" */
|
||||
"\xa8\xa9\xb2\xfa", /* "貝莉"; "Belle" */
|
||||
"\xb2\x4d\xbc\xe4\xa4\x6b\xa4\x75", /* "清潔女工"; "Cleaning Girl" */
|
||||
"\xc3\x51\xaa\xf7\xb4\xb5", /* "魏金斯"; "Wiggins" */
|
||||
"\xab\x4f\xc3\xb9", /* "保羅"; "Paul" */
|
||||
"\xb0\x73\xab\x4f", /* "酒保"; "The Bartender" */
|
||||
"\xa4\x40\xad\xd3\xbb\xea\xc5\xbc\xaa\xba\xb0\x73\xb0\xad", /* "一個骯髒的酒鬼"; "A Dirty Drunk" */
|
||||
"\xa4\x40\xad\xd3\xa4\x6a\xc1\x6e\xbb\xa1\xb8\xdc\xaa\xba\xb0\x73\xb0\xad", /* "一個大聲說話的酒鬼"; "A Shouting Drunk" */
|
||||
"\xa4\x40\xad\xd3\xa8\xab\xb8\xf4\xb7\x6e\xb7\x45\xaa\xba\xb0\x73\xb0\xad", /* "一個走路搖幌的酒鬼"; "A Staggering Drunk" */
|
||||
"\xab\x4f\xc3\xf0", /* "保鏢"; "The Bouncer" */
|
||||
"\xc5\xe7\xab\xcd\xa9\x78", /* "驗屍官"; "The Coroner" */
|
||||
"\xc3\x4d\xa4\x68\xaa\x41\xa9\xb1\xaa\xba\xb9\xd9\xad\x70", /* "騎士服店的夥計"; "Reginald Snipes" lit. "The clerk of the knight clothing store" */
|
||||
"\xb3\xec\xaa\x76\x2e\xa5\xac\xb5\xdc\xa7\x4a\xa5\xee", /* "喬治.布萊克伍"; "George Blackwood" */
|
||||
"\xbf\xe0\xa6\xd5\xb4\xb5", /* "賴耳斯"; "Lars" */
|
||||
"\xc3\xc4\xa9\xd0\xa6\xd1\xaa\x4f", /* "藥房老板"; "The Chemist" lit "Pharmacy owner" */
|
||||
"\xb8\xaf\xb7\xe7\xb4\xcb\xb1\xb4\xaa\xf8", /* "葛瑞森探長"; "Inspector Gregson" */
|
||||
"\xb8\xeb\xa5\x69\xa7\x42\x2e\xaa\x6b\xa8\xaf\xb9\x79", /* "賈可伯.法辛頓"; "Jacob Farthington" */
|
||||
"\xb3\xc1\xa6\xd2\xa4\xd2", /* "麥考夫"; "Mycroft" */
|
||||
"\xa6\xd1\xb3\xb7\xb0\xd2", /* "老雪曼"; "Old Sherman" */
|
||||
"\xb2\x7a\xac\x64", /* "理查"; "Richard" */
|
||||
"\xbd\xd5\xb0\x73\xae\x76", /* "調酒師"; "The Barman" */
|
||||
"\xa4\x40\xad\xd3\xa4\x40\xac\x79\xaa\xba\xaa\xb1\xaa\xcc", /* "一個一流的玩者"; "A Dandy Player" */
|
||||
"\xa4\x40\xad\xd3\xa4\x54\xac\x79\xaa\xba\xaa\xb1\xaa\xcc", /* "一個三流的玩者"; "A Rough-looking Player" lit "A third-rate player" */
|
||||
"\xae\xc7\xc6\x5b\xaa\xcc", /* "旁觀者"; "A Spectator" */
|
||||
"\xc3\xb9\xa7\x42\x2e\xba\x7e\xaf\x53", /* "羅伯.漢特"; "Robert Hunt" */
|
||||
"Violet", // TODO, Maybe "\xcb\xa2\xb5\xdc\xaf\x53", /* "芃萊特" */
|
||||
"\xa8\xa9\xab\xd2\xae\xe6\xbe\x7c", /* "貝帝格魯"; "Pettigrew" */
|
||||
"\xb6\xf8\xa6\x4e", /* "奧吉"; "Augie" */
|
||||
"\xa6\x77\xae\x52\x2e\xa5\x64\xac\xa5\xc1\xa8", /* "安娜.卡洛薇"; "Anna Carroway" */
|
||||
"\xc4\xb5\xbd\xc3", /* "警衛"; "A Guard" */
|
||||
"\xa6\x77\xaa\x46\xa5\xa7\xb6\xf8\x2e\xa5\x64", /* "安東尼奧.卡"; "Antonio Caruso" */
|
||||
"\xa6\xab\xa4\xf1", /* "托比"; "Toby the Dog" lit "Toby" */
|
||||
"\xa6\xe8\xbb\x58\x2e\xaa\xf7\xb4\xb5\xb5\xdc", /* "西蒙.金斯萊"; "Simon Kingsley" */
|
||||
"\xa8\xc8\xa6\xf2\xa6\x43\xbc\x77", /* "亞佛列德"; "Alfred" */
|
||||
"\xa5\xac\xaa\xf9\xab\xc2\xba\xb8\xa4\xd2\xa4\x48", /* "布門威爾夫人"; "Lady Brumwell" */
|
||||
"\xc3\xb9\xb2\xef\xa4\xd2\xa4\x48", /* "羅莎夫人"; "Madame Rosa" */
|
||||
"\xac\xf9\xb7\xe6\x2e\xbc\xaf\xba\xb8\xae\xfc\xbc\x77", /* "約瑟.摩爾海德"; "Joseph Moorehead" */
|
||||
"\xb2\xa6\xba\xb8\xa4\xd3\xa4\xd3", /* "畢爾太太"; "Mrs. Beale" */
|
||||
"\xb5\xe1\xa7\x51\xa7\x4a\xb4\xb5", /* "菲利克斯"; "Felix" */
|
||||
"\xb2\xfc\xc6\x46\xb9\x79", /* "荷靈頓"; "Hollingston" */
|
||||
"\xa5\x64\xb5\xdc\xba\x7e\xa8\xb5\xa6\xf5", /* "卡萊漢巡佐"; "Constable Callaghan" */
|
||||
"\xbe\x48\xaa\xd6\xa8\xb5\xa6\xf5", /* "鄧肯巡佐"; "Sergeant Duncan" */
|
||||
"\xa5\xac\xaa\xf9\xab\xc2\xba\xb8\xc0\xef\xa4\x68", /* "布門威爾爵士"; "Lord Brumwell" */
|
||||
"\xa5\xa7\xae\xe6\x2e\xb3\xc7\xa9\x69\xb4\xcb", /* "尼格.傑姆森"; "Nigel Jaimeson" */
|
||||
"\xc1\xe9\xaf\xc7\xb4\xb5\x2e\xb7\xe7\xa7\x4a", /* "鍾納斯.瑞克"; "Jonas" */
|
||||
"\xbe\x48\xae\xda\xa8\xb5\xa6\xf5" /* "鄧根巡佐"; "Constable Dugan" */
|
||||
};
|
||||
|
||||
// =========================================
|
||||
|
||||
// === Sherlock Holmes 1: Serrated Scalpel ===
|
||||
static const char *const fixedTextEN_ActionOpen[] = {
|
||||
"This cannot be opened",
|
||||
"It is already open",
|
||||
"It is locked",
|
||||
"Wait for Watson",
|
||||
" ",
|
||||
"."
|
||||
};
|
||||
|
||||
static const char *const fixedTextDE_ActionOpen[] = {
|
||||
"Das kann man nicht \224ffnen",
|
||||
"Ist doch schon offen!",
|
||||
"Leider verschlossen",
|
||||
"Warte auf Watson",
|
||||
" ",
|
||||
"."
|
||||
};
|
||||
|
||||
static const char *const fixedTextES_ActionOpen[] = {
|
||||
"No puede ser abierto",
|
||||
"Ya esta abierto",
|
||||
"Esta cerrado",
|
||||
"Espera a Watson",
|
||||
" ",
|
||||
"."
|
||||
};
|
||||
|
||||
static const char *const fixedTextZH_ActionOpen[] = {
|
||||
"\xb3\x6f\xb5\x4c\xaa\x6b\xa5\xb4\xb6\x7d\xaa\xba", /* "這無法打開的"; "This cannot be opened" */
|
||||
"\xa5\xa6\xa4\x77\xb8\x67\xa5\xb4\xb6\x7d\xa4\x46", /* "它已經打開了"; "It is already open" */
|
||||
"\xa5\xa6\xb3\x51\xc2\xea\xa6\xed\xa4\x46", /* "它被鎖住了"; "It is locked" */
|
||||
"\xb5\xa5\xab\xdd\xb5\xd8\xa5\xcd", /* "等待華生"; "Wait for Watson" */
|
||||
" ",
|
||||
"."
|
||||
};
|
||||
|
||||
static const char *const fixedTextEN_ActionClose[] = {
|
||||
"This cannot be closed",
|
||||
"It is already closed",
|
||||
"The safe door is in the way"
|
||||
};
|
||||
|
||||
static const char *const fixedTextDE_ActionClose[] = {
|
||||
"Das kann man nicht schlie\341en",
|
||||
"Ist doch schon zu!",
|
||||
"Die safet\201r ist Weg"
|
||||
};
|
||||
|
||||
static const char *const fixedTextES_ActionClose[] = {
|
||||
"No puede ser cerrado",
|
||||
"Ya esta cerrado",
|
||||
"La puerta de seguridad esta entre medias"
|
||||
};
|
||||
|
||||
static const char *const fixedTextZH_ActionClose[] = {
|
||||
"\xb3\x6f\xb5\x4c\xaa\x6b\xc3\xf6\xa6\xed\xaa\xba", /* "這無法關住的"; "This cannot be closed" */
|
||||
"\xa5\xa6\xa4\x77\xb8\x67\xc3\xf6\xb0\x5f\xa8\xd3\xa4\x46", /* "它已經關起來了"; "It is already closed" */
|
||||
"The safe door is in the way", // TODO
|
||||
};
|
||||
|
||||
static const char *const fixedTextEN_ActionMove[] = {
|
||||
"This cannot be moved",
|
||||
"It is bolted to the floor",
|
||||
"It is too heavy",
|
||||
"The other crate is in the way"
|
||||
};
|
||||
|
||||
|
||||
static const char *const fixedTextDE_ActionMove[] = {
|
||||
"L\204\341t sich nicht bewegen",
|
||||
"Festged\201belt in der Erde...",
|
||||
"Oha, VIEL zu schwer",
|
||||
"Die andere Kiste ist im Weg" // original: "Der andere Kiste ist im Weg"
|
||||
};
|
||||
|
||||
static const char *const fixedTextES_ActionMove[] = {
|
||||
"No puede moverse",
|
||||
"Esta sujeto a la pared",
|
||||
"Es demasiado pesado",
|
||||
"El otro cajon esta en mitad"
|
||||
};
|
||||
|
||||
static const char *const fixedTextZH_ActionMove[] = {
|
||||
"\xb3\x6f\xb5\x4c\xaa\x6b\xb2\xbe\xb0\xca\xaa\xba", /* "這無法移動的"; "This cannot be moved" */
|
||||
"It is bolted to the floor", // TODO
|
||||
"\xb3\x6f\xaa\x46\xa6\xe8\xa4\xd3\xad\xab\xa4\x46", /* "這東西太重了"; "It is too heavy" */
|
||||
"\xb3\x51\xa8\xe4\xa5\xa6\xaa\xba\xa4\xec\xbd\x63\xbe\xd7\xa6\xed\xb8\xf4\xa4\x46", /* "被其它的木箱擋住路了"; "The other crate is in the way" */
|
||||
};
|
||||
|
||||
static const char *const fixedTextEN_ActionPick[] = {
|
||||
"Nothing of interest here",
|
||||
"It is bolted down",
|
||||
"It is too big to carry",
|
||||
"It is too heavy",
|
||||
"I think a girl would be more your type",
|
||||
"Those flowers belong to Penny",
|
||||
"She's far too young for you!",
|
||||
"I think a girl would be more your type!",
|
||||
"Government property for official use only"
|
||||
};
|
||||
|
||||
static const char *const fixedTextDE_ActionPick[] = {
|
||||
"Nichts Interessantes da",
|
||||
"Zu gut befestigt",
|
||||
"Ist ja wohl ein bi\341chen zu gro\341, oder ?",
|
||||
"Oha, VIEL zu schwer",
|
||||
"Ich denke, Du stehst mehr auf M\204dchen ?",
|
||||
"Diese Blumen geh\224ren Penny",
|
||||
"Sie ist doch viel zu jung f\201r Dich!",
|
||||
"Ich denke, Du stehst mehr auf M\204dchen ?",
|
||||
"Staatseigentum - Nur f\201r den Dienstgebrauch !"
|
||||
};
|
||||
|
||||
static const char *const fixedTextES_ActionPick[] = {
|
||||
"No hay nada interesante",
|
||||
"Esta anclado al suelo",
|
||||
"Es muy grande para llevarlo",
|
||||
"Pesa demasiado",
|
||||
"Creo que una chica sera mas tu tipo",
|
||||
"Esas flores pertenecen a Penny",
|
||||
"\255Es demasiado joven para ti!",
|
||||
"\255Creo que una chica sera mas tu tipo!",
|
||||
"Propiedad del gobierno para uso oficial"
|
||||
};
|
||||
|
||||
static const char *const fixedTextZH_ActionPick[] = {
|
||||
"\xa8\x53\xa6\xb3\xa4\xb0\xbb\xf2\xa5\x69\xad\xc8\xb1\x6f\xae\xb3", /* "沒有什麼可值得拿"; "Nothing of interest here" */
|
||||
"It is bolted down", // TODO
|
||||
"It is too big to carry", // TODO
|
||||
"\xa8\xba\xa4\xd3\xad\xab\xa4\x46", /* "那太重了"; "It is too heavy" */
|
||||
"I think a girl would be more your type", // TODO
|
||||
"Those flowers belong to Penny", // TODO
|
||||
"She's far too young for you!", // TODO
|
||||
"I think a girl would be more your type!", // TODO
|
||||
"\xac\x46\xa9\xb2\xa9\xd2\xa6\xb3\x2c\xb6\xc8\xaf\xe0\xa8\xd1\xa9\x78\xa4\xe8\xa8\xcf\xa5\xce" /* "政府所有,僅能供官方使用"; "Government property for official use only" */
|
||||
};
|
||||
|
||||
static const char *const fixedTextEN_ActionUse[] = {
|
||||
"You can't do that",
|
||||
"It had no effect",
|
||||
"You can't reach it",
|
||||
"OK, the door looks bigger! Happy?",
|
||||
"Doors don't smoke"
|
||||
};
|
||||
|
||||
static const char *const fixedTextDE_ActionUse[] = {
|
||||
"Nein, das geht wirklich nicht",
|
||||
"Tja keinerlei Wirkung",
|
||||
"Da kommst du nicht dran",
|
||||
"Na gut, die T\201r sieht jetzt gr\224\341er aus. Zufrieden?",
|
||||
"T\201ren sind Nichtraucher!"
|
||||
};
|
||||
|
||||
static const char *const fixedTextES_ActionUse[] = {
|
||||
"No puedes hacerlo",
|
||||
"No tuvo ningun efecto",
|
||||
"No puedes alcanzarlo",
|
||||
"Bien, \255es enorme! \250Feliz?",
|
||||
"Las puertas no fuman"
|
||||
};
|
||||
|
||||
|
||||
static const char *const fixedTextZH_ActionUse[] = {
|
||||
"\xb1\x7a\xb5\x4c\xaa\x6b\xa8\xba\xbc\xcb\xa8\xcf\xa5\xce", /* "您無法那樣使用"; "You can't do that" */
|
||||
"\xa5\xa6\xac\x4f\xa8\x53\xa6\xb3\xae\xc4\xaa\x47\xaa\xba", /* "它是沒有效果的"; "It had no effect" */
|
||||
"\xb1\x7a\xb5\x4c\xaa\x6b\xa8\xec\xb9\x46\xa8\xba\xc3\xe4", /* "您無法到達那邊"; "You can't reach it" */
|
||||
"\xa6\x6e\xa4\x46\x21\xaa\xf9\xa4\x77\xb8\x67\xb6\x7d\xa4\x46\x2c\xb0\xaa\xbf\xb3\xb6\xdc\x3f", /* "好了!門已經開了,高興嗎?"; "OK, the door looks bigger! Happy?" */
|
||||
"\xaa\xf9\xb5\x4c\xaa\x6b\xa9\xe2\xb7\xcf" /* "門無法抽煙"; "Doors don't smoke" */
|
||||
};
|
||||
|
||||
#define FIXEDTEXT_GETCOUNT(_name_) sizeof(_name_) / sizeof(byte *)
|
||||
#define FIXEDTEXT_ENTRY(_name_) _name_, FIXEDTEXT_GETCOUNT(_name_)
|
||||
|
||||
static const FixedTextActionEntry fixedTextEN_Actions[] = {
|
||||
{ FIXEDTEXT_ENTRY(fixedTextEN_ActionOpen) },
|
||||
{ FIXEDTEXT_ENTRY(fixedTextEN_ActionClose) },
|
||||
{ FIXEDTEXT_ENTRY(fixedTextEN_ActionMove) },
|
||||
{ FIXEDTEXT_ENTRY(fixedTextEN_ActionPick) },
|
||||
{ FIXEDTEXT_ENTRY(fixedTextEN_ActionUse) }
|
||||
};
|
||||
|
||||
static const FixedTextActionEntry fixedTextDE_Actions[] = {
|
||||
{ FIXEDTEXT_ENTRY(fixedTextDE_ActionOpen) },
|
||||
{ FIXEDTEXT_ENTRY(fixedTextDE_ActionClose) },
|
||||
{ FIXEDTEXT_ENTRY(fixedTextDE_ActionMove) },
|
||||
{ FIXEDTEXT_ENTRY(fixedTextDE_ActionPick) },
|
||||
{ FIXEDTEXT_ENTRY(fixedTextDE_ActionUse) }
|
||||
};
|
||||
|
||||
static const FixedTextActionEntry fixedTextES_Actions[] = {
|
||||
{ FIXEDTEXT_ENTRY(fixedTextES_ActionOpen) },
|
||||
{ FIXEDTEXT_ENTRY(fixedTextES_ActionClose) },
|
||||
{ FIXEDTEXT_ENTRY(fixedTextES_ActionMove) },
|
||||
{ FIXEDTEXT_ENTRY(fixedTextES_ActionPick) },
|
||||
{ FIXEDTEXT_ENTRY(fixedTextES_ActionUse) }
|
||||
};
|
||||
|
||||
static const FixedTextActionEntry fixedTextZH_Actions[] = {
|
||||
{ FIXEDTEXT_ENTRY(fixedTextZH_ActionOpen) },
|
||||
{ FIXEDTEXT_ENTRY(fixedTextZH_ActionClose) },
|
||||
{ FIXEDTEXT_ENTRY(fixedTextZH_ActionMove) },
|
||||
{ FIXEDTEXT_ENTRY(fixedTextZH_ActionPick) },
|
||||
{ FIXEDTEXT_ENTRY(fixedTextZH_ActionUse) }
|
||||
};
|
||||
|
||||
// =========================================
|
||||
|
||||
const FixedTextLanguageEntry fixedTextLanguages[] = {
|
||||
{ Common::DE_DEU, fixedTextDE, fixedTextDE_Actions },
|
||||
{ Common::ES_ESP, fixedTextES, fixedTextES_Actions },
|
||||
{ Common::EN_ANY, fixedTextEN, fixedTextEN_Actions },
|
||||
{ Common::ZH_TWN, fixedTextZH, fixedTextZH_Actions },
|
||||
{ Common::UNK_LANG, fixedTextEN, fixedTextEN_Actions }
|
||||
};
|
||||
|
||||
// =========================================
|
||||
|
||||
// =========================================
|
||||
|
||||
ScalpelFixedText::ScalpelFixedText(SherlockEngine *vm) : FixedText(vm) {
|
||||
// Figure out which fixed texts to use
|
||||
Common::Language curLanguage = _vm->getLanguage();
|
||||
|
||||
const FixedTextLanguageEntry *curLanguageEntry = fixedTextLanguages;
|
||||
|
||||
while (curLanguageEntry->language != Common::UNK_LANG) {
|
||||
if (curLanguageEntry->language == curLanguage)
|
||||
break; // found current language
|
||||
curLanguageEntry++;
|
||||
}
|
||||
_curLanguageEntry = curLanguageEntry;
|
||||
}
|
||||
|
||||
const char *ScalpelFixedText::getText(int fixedTextId) {
|
||||
return _curLanguageEntry->fixedTextArray[fixedTextId];
|
||||
}
|
||||
|
||||
const Common::String ScalpelFixedText::getActionMessage(FixedTextActionId actionId, int messageIndex) {
|
||||
assert(actionId >= 0);
|
||||
assert(messageIndex >= 0);
|
||||
const FixedTextActionEntry *curActionEntry = &_curLanguageEntry->actionArray[actionId];
|
||||
|
||||
assert(messageIndex < curActionEntry->fixedTextArrayCount);
|
||||
return Common::String(curActionEntry->fixedTextArray[messageIndex]);
|
||||
}
|
||||
|
||||
} // End of namespace Scalpel
|
||||
|
||||
} // End of namespace Sherlock
|
||||
215
engines/sherlock/scalpel/scalpel_fixed_text.h
Normal file
215
engines/sherlock/scalpel/scalpel_fixed_text.h
Normal 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SHERLOCK_SCALPEL_FIXED_TEXT_H
|
||||
#define SHERLOCK_SCALPEL_FIXED_TEXT_H
|
||||
|
||||
#include "sherlock/fixed_text.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
namespace Scalpel {
|
||||
|
||||
enum FixedTextId {
|
||||
// Game hotkeys
|
||||
kFixedText_Game_Hotkeys = 0,
|
||||
// Window buttons
|
||||
kFixedText_Window_Exit,
|
||||
kFixedText_Window_Up,
|
||||
kFixedText_Window_Down,
|
||||
// Inventory buttons
|
||||
kFixedText_Inventory_Exit,
|
||||
kFixedText_Inventory_Look,
|
||||
kFixedText_Inventory_Use,
|
||||
kFixedText_Inventory_Give,
|
||||
// Journal text
|
||||
kFixedText_Journal_WatsonsJournal,
|
||||
kFixedText_Journal_Page,
|
||||
// Journal buttons
|
||||
kFixedText_Journal_Exit,
|
||||
kFixedText_Journal_Back10,
|
||||
kFixedText_Journal_Up,
|
||||
kFixedText_Journal_Down,
|
||||
kFixedText_Journal_Ahead10,
|
||||
kFixedText_Journal_Search,
|
||||
kFixedText_Journal_FirstPage,
|
||||
kFixedText_Journal_LastPage,
|
||||
kFixedText_Journal_PrintText,
|
||||
// Journal search
|
||||
kFixedText_JournalSearch_Exit,
|
||||
kFixedText_JournalSearch_Backward,
|
||||
kFixedText_JournalSearch_Forward,
|
||||
kFixedText_JournalSearch_NotFound,
|
||||
// Settings
|
||||
kFixedText_Settings_Exit,
|
||||
kFixedText_Settings_MusicOn,
|
||||
kFixedText_Settings_MusicOff,
|
||||
kFixedText_Settings_PortraitsOn,
|
||||
kFixedText_Settings_PortraitsOff,
|
||||
kFixedText_Settings_JoystickOff,
|
||||
kFixedText_Settings_NewFontStyle,
|
||||
kFixedText_Settings_SoundEffectsOn,
|
||||
kFixedText_Settings_SoundEffectsOff,
|
||||
kFixedText_Settings_WindowsSlide,
|
||||
kFixedText_Settings_WindowsAppear,
|
||||
kFixedText_Settings_CalibrateJoystick,
|
||||
kFixedText_Settings_AutoHelpLeft,
|
||||
kFixedText_Settings_AutoHelpRight,
|
||||
kFixedText_Settings_VoicesOn,
|
||||
kFixedText_Settings_VoicesOff,
|
||||
kFixedText_Settings_FadeByPixel,
|
||||
kFixedText_Settings_FadeDirectly,
|
||||
kFixedText_Settings_KeyPadSlow,
|
||||
kFixedText_Settings_KeyPadFast,
|
||||
// Load/Save
|
||||
kFixedText_LoadSave_Exit,
|
||||
kFixedText_LoadSave_Load,
|
||||
kFixedText_LoadSave_Save,
|
||||
kFixedText_LoadSave_Up,
|
||||
kFixedText_LoadSave_Down,
|
||||
kFixedText_LoadSave_Quit,
|
||||
// Quit Game
|
||||
kFixedText_QuitGame_Question,
|
||||
kFixedText_QuitGame_Yes,
|
||||
kFixedText_QuitGame_No,
|
||||
// Press key text
|
||||
kFixedText_PressKey_ForMore,
|
||||
kFixedText_PressKey_ToContinue,
|
||||
// Initial inventory
|
||||
kFixedText_InitInventory_Message,
|
||||
kFixedText_InitInventory_HolmesCard,
|
||||
kFixedText_InitInventory_Tickets,
|
||||
kFixedText_InitInventory_CuffLink,
|
||||
kFixedText_InitInventory_WireHook,
|
||||
kFixedText_InitInventory_Note,
|
||||
kFixedText_InitInventory_OpenWatch,
|
||||
kFixedText_InitInventory_Paper,
|
||||
kFixedText_InitInventory_Letter,
|
||||
kFixedText_InitInventory_Tarot,
|
||||
kFixedText_InitInventory_OrnateKey,
|
||||
kFixedText_InitInventory_PawnTicket,
|
||||
// SH1: User Interface
|
||||
kFixedText_UserInterface_NoThankYou,
|
||||
kFixedText_UserInterface_YouCantDoThat,
|
||||
kFixedText_UserInterface_Done,
|
||||
kFixedText_UserInterface_Use,
|
||||
kFixedText_UserInterface_UseOn,
|
||||
kFixedText_UserInterface_Give,
|
||||
kFixedText_UserInterface_GiveTo,
|
||||
// People names
|
||||
kFixedText_People_SherlockHolmes,
|
||||
kFixedText_People_DrWatson,
|
||||
kFixedText_People_InspectorLestrade,
|
||||
kFixedText_People_ConstableOBrien,
|
||||
kFixedText_People_ConstableLewis,
|
||||
kFixedText_People_SheilaParker,
|
||||
kFixedText_People_HenryCarruthers,
|
||||
kFixedText_People_Lesley,
|
||||
kFixedText_People_AnUsher,
|
||||
kFixedText_People_FredrickEpstein,
|
||||
kFixedText_People_MrsWorthington,
|
||||
kFixedText_People_TheCoach,
|
||||
kFixedText_People_APlayer,
|
||||
kFixedText_People_Tim,
|
||||
kFixedText_People_JamesSanders,
|
||||
kFixedText_People_Belle,
|
||||
kFixedText_People_CleaningGirl,
|
||||
kFixedText_People_Wiggins,
|
||||
kFixedText_People_Paul,
|
||||
kFixedText_People_TheBartender,
|
||||
kFixedText_People_ADirtyDrunk,
|
||||
kFixedText_People_AShoutingDrunk,
|
||||
kFixedText_People_AStaggeringDrunk,
|
||||
kFixedText_People_TheBouncer,
|
||||
kFixedText_People_TheCoroner,
|
||||
kFixedText_People_ReginaldSnipes,
|
||||
kFixedText_People_GeorgeBlackwood,
|
||||
kFixedText_People_Lars,
|
||||
kFixedText_People_TheChemist,
|
||||
kFixedText_People_InspectorGregson,
|
||||
kFixedText_People_JacobFarthington,
|
||||
kFixedText_People_Mycroft,
|
||||
kFixedText_People_OldSherman,
|
||||
kFixedText_People_Richard,
|
||||
kFixedText_People_TheBarman,
|
||||
kFixedText_People_ADandyPlayer,
|
||||
kFixedText_People_ARoughlookingPlayer,
|
||||
kFixedText_People_ASpectator,
|
||||
kFixedText_People_RobertHunt,
|
||||
kFixedText_People_Violet,
|
||||
kFixedText_People_Pettigrew,
|
||||
kFixedText_People_Augie,
|
||||
kFixedText_People_AnnaCarroway,
|
||||
kFixedText_People_AGuard,
|
||||
kFixedText_People_AntonioCaruso,
|
||||
kFixedText_People_TobyTheDog,
|
||||
kFixedText_People_SimonKingsley,
|
||||
kFixedText_People_Alfred,
|
||||
kFixedText_People_LadyBrumwell,
|
||||
kFixedText_People_MadameRosa,
|
||||
kFixedText_People_JosephMoorehead,
|
||||
kFixedText_People_MrsBeale,
|
||||
kFixedText_People_Felix,
|
||||
kFixedText_People_Hollingston,
|
||||
kFixedText_People_ConstableCallaghan,
|
||||
kFixedText_People_SergeantDuncan,
|
||||
kFixedText_People_LordBrumwell,
|
||||
kFixedText_People_NigelJaimeson,
|
||||
kFixedText_People_Jonas,
|
||||
kFixedText_People_ConstableDugan
|
||||
};
|
||||
|
||||
struct FixedTextActionEntry {
|
||||
const char *const *fixedTextArray;
|
||||
int fixedTextArrayCount;
|
||||
};
|
||||
|
||||
struct FixedTextLanguageEntry {
|
||||
Common::Language language;
|
||||
const char *const *fixedTextArray;
|
||||
const FixedTextActionEntry *actionArray;
|
||||
};
|
||||
|
||||
class ScalpelFixedText: public FixedText {
|
||||
private:
|
||||
const FixedTextLanguageEntry *_curLanguageEntry;
|
||||
public:
|
||||
ScalpelFixedText(SherlockEngine *vm);
|
||||
~ScalpelFixedText() override {}
|
||||
|
||||
/**
|
||||
* Gets text
|
||||
*/
|
||||
const char *getText(int fixedTextId) override;
|
||||
|
||||
/**
|
||||
* Get action message
|
||||
*/
|
||||
const Common::String getActionMessage(FixedTextActionId actionId, int messageIndex) override;
|
||||
};
|
||||
|
||||
extern const FixedTextLanguageEntry fixedTextLanguages[];
|
||||
|
||||
} // End of namespace Scalpel
|
||||
|
||||
} // End of namespace Sherlock
|
||||
|
||||
#endif
|
||||
306
engines/sherlock/scalpel/scalpel_inventory.cpp
Normal file
306
engines/sherlock/scalpel/scalpel_inventory.cpp
Normal file
@@ -0,0 +1,306 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sherlock/scalpel/scalpel_inventory.h"
|
||||
#include "sherlock/scalpel/scalpel_fixed_text.h"
|
||||
#include "sherlock/scalpel/scalpel_screen.h"
|
||||
#include "sherlock/scalpel/scalpel_user_interface.h"
|
||||
#include "sherlock/scalpel/scalpel.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
namespace Scalpel {
|
||||
|
||||
ScalpelInventory::ScalpelInventory(SherlockEngine *vm) : Inventory(vm) {
|
||||
_invShapes.resize(6);
|
||||
|
||||
_fixedTextExit = FIXED(Inventory_Exit);
|
||||
_fixedTextLook = FIXED(Inventory_Look);
|
||||
_fixedTextUse = FIXED(Inventory_Use);
|
||||
_fixedTextGive = FIXED(Inventory_Give);
|
||||
|
||||
_actionsIndexed[0] = kActionScalpelInvExit;
|
||||
_actionsIndexed[1] = kActionScalpelInvLook;
|
||||
_actionsIndexed[2] = kActionScalpelInvUse;
|
||||
_actionsIndexed[3] = kActionScalpelInvGive;
|
||||
_actionsIndexed[4] = kActionScalpelInvLeft;
|
||||
_actionsIndexed[5] = kActionScalpelInvRight;
|
||||
_actionsIndexed[6] = kActionScalpelInvPageLeft;
|
||||
_actionsIndexed[7] = kActionScalpelInvPageRight;
|
||||
}
|
||||
|
||||
ScalpelInventory::~ScalpelInventory() {
|
||||
}
|
||||
|
||||
int ScalpelInventory::identifyUserButton(Common::CustomEventType action) {
|
||||
for (uint16 actionNr = 0; actionNr < ARRAYSIZE(_actionsIndexed); actionNr++) {
|
||||
if (action == _actionsIndexed[actionNr])
|
||||
return actionNr;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void ScalpelInventory::drawInventory(InvNewMode mode) {
|
||||
ScalpelScreen &screen = *(ScalpelScreen *)_vm->_screen;
|
||||
UserInterface &ui = *_vm->_ui;
|
||||
InvNewMode tempMode = mode;
|
||||
|
||||
loadInv();
|
||||
|
||||
if (mode == INVENTORY_DONT_DISPLAY) {
|
||||
screen.activateBackBuffer2();
|
||||
}
|
||||
|
||||
// Draw the window background
|
||||
Surface &bb = *screen.getBackBuffer();
|
||||
bb.fillRect(Common::Rect(0, CONTROLS_Y1, SHERLOCK_SCREEN_WIDTH, CONTROLS_Y1 + 10), BORDER_COLOR);
|
||||
bb.fillRect(Common::Rect(0, CONTROLS_Y1 + 10, 2, SHERLOCK_SCREEN_HEIGHT), BORDER_COLOR);
|
||||
bb.fillRect(Common::Rect(SHERLOCK_SCREEN_WIDTH - 2, CONTROLS_Y1 + 10,
|
||||
SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT), BORDER_COLOR);
|
||||
bb.fillRect(Common::Rect(0, SHERLOCK_SCREEN_HEIGHT - 1, SHERLOCK_SCREEN_WIDTH,
|
||||
SHERLOCK_SCREEN_HEIGHT), BORDER_COLOR);
|
||||
bb.fillRect(Common::Rect(2, CONTROLS_Y1 + 10, SHERLOCK_SCREEN_WIDTH - 2, SHERLOCK_SCREEN_HEIGHT - 1),
|
||||
INV_BACKGROUND);
|
||||
|
||||
// Draw the buttons
|
||||
screen.makeButton(Common::Rect(INVENTORY_POINTS[0][0], CONTROLS_Y1, INVENTORY_POINTS[0][1],
|
||||
CONTROLS_Y1 + 10), INVENTORY_POINTS[0][2], _fixedTextExit);
|
||||
screen.makeButton(Common::Rect(INVENTORY_POINTS[1][0], CONTROLS_Y1, INVENTORY_POINTS[1][1],
|
||||
CONTROLS_Y1 + 10), INVENTORY_POINTS[1][2], _fixedTextLook);
|
||||
screen.makeButton(Common::Rect(INVENTORY_POINTS[2][0], CONTROLS_Y1, INVENTORY_POINTS[2][1],
|
||||
CONTROLS_Y1 + 10), INVENTORY_POINTS[2][2], _fixedTextUse);
|
||||
screen.makeButton(Common::Rect(INVENTORY_POINTS[3][0], CONTROLS_Y1, INVENTORY_POINTS[3][1],
|
||||
CONTROLS_Y1 + 10), INVENTORY_POINTS[3][2], _fixedTextGive);
|
||||
screen.makeButton(Common::Rect(INVENTORY_POINTS[4][0], CONTROLS_Y1, INVENTORY_POINTS[4][1],
|
||||
CONTROLS_Y1 + 10), INVENTORY_POINTS[4][2] + 8, "^^", false); // 2 arrows pointing to the left
|
||||
screen.makeButton(Common::Rect(INVENTORY_POINTS[5][0], CONTROLS_Y1, INVENTORY_POINTS[5][1],
|
||||
CONTROLS_Y1 + 10), INVENTORY_POINTS[5][2] + 4, "^", false); // 1 arrow pointing to the left
|
||||
screen.makeButton(Common::Rect(INVENTORY_POINTS[6][0], CONTROLS_Y1, INVENTORY_POINTS[6][1],
|
||||
CONTROLS_Y1 + 10), INVENTORY_POINTS[6][2] + 4, "_", false); // 1 arrow pointing to the right
|
||||
screen.makeButton(Common::Rect(INVENTORY_POINTS[7][0], CONTROLS_Y1, INVENTORY_POINTS[7][1],
|
||||
CONTROLS_Y1 + 10), INVENTORY_POINTS[7][2] + 8, "__", false); // 2 arrows pointing to the right
|
||||
|
||||
if (tempMode == INVENTORY_DONT_DISPLAY)
|
||||
mode = LOOK_INVENTORY_MODE;
|
||||
_invMode = (InvMode)((int)mode);
|
||||
|
||||
if (mode != PLAIN_INVENTORY) {
|
||||
assert((uint)mode < ARRAYSIZE(_actionsIndexed));
|
||||
ui._oldAction = _actionsIndexed[mode];
|
||||
} else {
|
||||
ui._oldAction = (Common::CustomEventType) -1;
|
||||
}
|
||||
|
||||
invCommands(0);
|
||||
putInv(SLAM_DONT_DISPLAY);
|
||||
|
||||
if (tempMode != INVENTORY_DONT_DISPLAY) {
|
||||
if (!ui._slideWindows) {
|
||||
screen.slamRect(Common::Rect(0, CONTROLS_Y1, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT));
|
||||
} else {
|
||||
ui.summonWindow(false, CONTROLS_Y1);
|
||||
}
|
||||
|
||||
ui._windowOpen = true;
|
||||
} else {
|
||||
// Reset the screen back buffer to the first buffer now that drawing is done
|
||||
screen.activateBackBuffer1();
|
||||
}
|
||||
|
||||
assert(IS_SERRATED_SCALPEL);
|
||||
((ScalpelUserInterface *)_vm->_ui)->_oldUse = -1;
|
||||
}
|
||||
|
||||
void ScalpelInventory::invCommands(bool slamIt) {
|
||||
ScalpelScreen &screen = *(ScalpelScreen *)_vm->_screen;
|
||||
UserInterface &ui = *_vm->_ui;
|
||||
|
||||
if (slamIt) {
|
||||
screen.buttonPrint(Common::Point(INVENTORY_POINTS[0][2], CONTROLS_Y1),
|
||||
_invMode == INVMODE_EXIT ? COMMAND_HIGHLIGHTED :COMMAND_FOREGROUND,
|
||||
true, _fixedTextExit);
|
||||
screen.buttonPrint(Common::Point(INVENTORY_POINTS[1][2], CONTROLS_Y1),
|
||||
_invMode == INVMODE_LOOK ? COMMAND_HIGHLIGHTED :COMMAND_FOREGROUND,
|
||||
true, _fixedTextLook);
|
||||
screen.buttonPrint(Common::Point(INVENTORY_POINTS[2][2], CONTROLS_Y1),
|
||||
_invMode == INVMODE_USE ? COMMAND_HIGHLIGHTED : COMMAND_FOREGROUND,
|
||||
true, _fixedTextUse);
|
||||
screen.buttonPrint(Common::Point(INVENTORY_POINTS[3][2], CONTROLS_Y1),
|
||||
_invMode == INVMODE_GIVE ? COMMAND_HIGHLIGHTED : COMMAND_FOREGROUND,
|
||||
true, _fixedTextGive);
|
||||
screen.print(Common::Point(INVENTORY_POINTS[4][2], CONTROLS_Y1 + 1),
|
||||
_invIndex == 0 ? COMMAND_NULL : COMMAND_FOREGROUND,
|
||||
"^^"); // 2 arrows pointing to the left
|
||||
screen.print(Common::Point(INVENTORY_POINTS[5][2], CONTROLS_Y1 + 1),
|
||||
_invIndex == 0 ? COMMAND_NULL : COMMAND_FOREGROUND,
|
||||
"^"); // 2 arrows pointing to the left
|
||||
screen.print(Common::Point(INVENTORY_POINTS[6][2], CONTROLS_Y1 + 1),
|
||||
(_holdings - _invIndex <= 6) ? COMMAND_NULL : COMMAND_FOREGROUND,
|
||||
"_"); // 1 arrow pointing to the right
|
||||
screen.print(Common::Point(INVENTORY_POINTS[7][2], CONTROLS_Y1 + 1),
|
||||
(_holdings - _invIndex <= 6) ? COMMAND_NULL : COMMAND_FOREGROUND,
|
||||
"__"); // 2 arrows pointing to the right
|
||||
if (_invMode != INVMODE_LOOK)
|
||||
ui.clearInfo();
|
||||
} else {
|
||||
screen.buttonPrint(Common::Point(INVENTORY_POINTS[0][2], CONTROLS_Y1),
|
||||
_invMode == INVMODE_EXIT ? COMMAND_HIGHLIGHTED : COMMAND_FOREGROUND,
|
||||
false, _fixedTextExit);
|
||||
screen.buttonPrint(Common::Point(INVENTORY_POINTS[1][2], CONTROLS_Y1),
|
||||
_invMode == INVMODE_LOOK ? COMMAND_HIGHLIGHTED : COMMAND_FOREGROUND,
|
||||
false, _fixedTextLook);
|
||||
screen.buttonPrint(Common::Point(INVENTORY_POINTS[2][2], CONTROLS_Y1),
|
||||
_invMode == INVMODE_USE ? COMMAND_HIGHLIGHTED : COMMAND_FOREGROUND,
|
||||
false, _fixedTextUse);
|
||||
screen.buttonPrint(Common::Point(INVENTORY_POINTS[3][2], CONTROLS_Y1),
|
||||
_invMode == INVMODE_GIVE ? COMMAND_HIGHLIGHTED : COMMAND_FOREGROUND,
|
||||
false, _fixedTextGive);
|
||||
screen.gPrint(Common::Point(INVENTORY_POINTS[4][2], CONTROLS_Y1),
|
||||
_invIndex == 0 ? COMMAND_NULL : COMMAND_FOREGROUND,
|
||||
"^^"); // 2 arrows pointing to the left
|
||||
screen.gPrint(Common::Point(INVENTORY_POINTS[5][2], CONTROLS_Y1),
|
||||
_invIndex == 0 ? COMMAND_NULL : COMMAND_FOREGROUND,
|
||||
"^"); // 1 arrow pointing to the left
|
||||
screen.gPrint(Common::Point(INVENTORY_POINTS[6][2], CONTROLS_Y1),
|
||||
(_holdings - _invIndex < 7) ? COMMAND_NULL : COMMAND_FOREGROUND,
|
||||
"_"); // 1 arrow pointing to the right
|
||||
screen.gPrint(Common::Point(INVENTORY_POINTS[7][2], CONTROLS_Y1),
|
||||
(_holdings - _invIndex < 7) ? COMMAND_NULL : COMMAND_FOREGROUND,
|
||||
"__"); // 2 arrows pointing to the right
|
||||
}
|
||||
}
|
||||
|
||||
void ScalpelInventory::highlight(int index, byte color) {
|
||||
Screen &screen = *_vm->_screen;
|
||||
Surface &bb = *screen.getBackBuffer();
|
||||
int slot = index - _invIndex;
|
||||
ImageFrame &frame = (*_invShapes[slot])[0];
|
||||
|
||||
bb.fillRect(Common::Rect(8 + slot * 52, 165, (slot + 1) * 52, 194), color);
|
||||
bb.SHtransBlitFrom(frame, Common::Point(6 + slot * 52 + ((47 - frame._width) / 2),
|
||||
163 + ((33 - frame._height) / 2)));
|
||||
screen.slamArea(8 + slot * 52, 165, 44, 30);
|
||||
}
|
||||
|
||||
void ScalpelInventory::refreshInv() {
|
||||
Screen &screen = *_vm->_screen;
|
||||
Talk &talk = *_vm->_talk;
|
||||
ScalpelUserInterface &ui = *(ScalpelUserInterface *)_vm->_ui;
|
||||
|
||||
ui._invLookFlag = true;
|
||||
freeInv();
|
||||
|
||||
ui._infoFlag = true;
|
||||
ui.clearInfo();
|
||||
|
||||
screen._backBuffer2.SHblitFrom(screen._backBuffer1, Common::Point(0, CONTROLS_Y),
|
||||
Common::Rect(0, CONTROLS_Y, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT));
|
||||
ui.examine();
|
||||
|
||||
if (!talk._talkToAbort) {
|
||||
screen._backBuffer2.SHblitFrom((*ui._controlPanel)[0], Common::Point(0, CONTROLS_Y));
|
||||
loadInv();
|
||||
}
|
||||
}
|
||||
|
||||
void ScalpelInventory::putInv(InvSlamMode slamIt) {
|
||||
ScalpelScreen &screen = *(ScalpelScreen *)_vm->_screen;
|
||||
UserInterface &ui = *_vm->_ui;
|
||||
|
||||
// If an inventory item has disappeared (due to using it or giving it),
|
||||
// a blank space slot may have appeared. If so, adjust the inventory
|
||||
if (_invIndex > 0 && _invIndex > (_holdings - (int)_invShapes.size())) {
|
||||
--_invIndex;
|
||||
freeGraphics();
|
||||
loadGraphics();
|
||||
}
|
||||
|
||||
if (slamIt != SLAM_SECONDARY_BUFFER) {
|
||||
screen.makePanel(Common::Rect(6, 163, 54, 197));
|
||||
screen.makePanel(Common::Rect(58, 163, 106, 197));
|
||||
screen.makePanel(Common::Rect(110, 163, 158, 197));
|
||||
screen.makePanel(Common::Rect(162, 163, 210, 197));
|
||||
screen.makePanel(Common::Rect(214, 163, 262, 197));
|
||||
screen.makePanel(Common::Rect(266, 163, 314, 197));
|
||||
}
|
||||
|
||||
// Iterate through displaying up to 6 objects at a time
|
||||
for (int idx = _invIndex; idx < _holdings && (idx - _invIndex) < (int)_invShapes.size(); ++idx) {
|
||||
int itemNum = idx - _invIndex;
|
||||
Surface &bb = (slamIt == SLAM_SECONDARY_BUFFER) ? screen._backBuffer2 : *screen.getBackBuffer();
|
||||
Common::Rect r(8 + itemNum * 52, 165, 51 + itemNum * 52, 194);
|
||||
|
||||
// Draw the background
|
||||
if (idx == ui._selector) {
|
||||
bb.fillRect(r, BUTTON_BACKGROUND);
|
||||
}
|
||||
else if (slamIt == SLAM_SECONDARY_BUFFER) {
|
||||
bb.fillRect(r, BUTTON_MIDDLE);
|
||||
}
|
||||
|
||||
// Draw the item image
|
||||
ImageFrame &frame = (*_invShapes[itemNum])[0];
|
||||
bb.SHtransBlitFrom(frame, Common::Point(6 + itemNum * 52 + ((47 - frame._width) / 2),
|
||||
163 + ((33 - frame._height) / 2)));
|
||||
}
|
||||
|
||||
if (slamIt == SLAM_DISPLAY)
|
||||
screen.slamArea(6, 163, 308, 34);
|
||||
|
||||
if (slamIt != SLAM_SECONDARY_BUFFER)
|
||||
ui.clearInfo();
|
||||
|
||||
if (slamIt == 0) {
|
||||
invCommands(0);
|
||||
}
|
||||
else if (slamIt == SLAM_SECONDARY_BUFFER) {
|
||||
screen.activateBackBuffer2();
|
||||
invCommands(0);
|
||||
screen.activateBackBuffer1();
|
||||
}
|
||||
}
|
||||
|
||||
void ScalpelInventory::loadInv() {
|
||||
// Exit if the inventory names are already loaded
|
||||
if (_names.size() > 0)
|
||||
return;
|
||||
|
||||
// Load the inventory names
|
||||
Common::SeekableReadStream *stream = _vm->_res->load("invent.txt");
|
||||
|
||||
int streamSize = stream->size();
|
||||
while (stream->pos() < streamSize) {
|
||||
Common::String name;
|
||||
char c;
|
||||
while ((c = stream->readByte()) != 0)
|
||||
name += c;
|
||||
|
||||
_names.push_back(name);
|
||||
}
|
||||
|
||||
delete stream;
|
||||
|
||||
loadGraphics();
|
||||
}
|
||||
|
||||
} // End of namespace Scalpel
|
||||
|
||||
} // End of namespace Sherlock
|
||||
87
engines/sherlock/scalpel/scalpel_inventory.h
Normal file
87
engines/sherlock/scalpel/scalpel_inventory.h
Normal file
@@ -0,0 +1,87 @@
|
||||
/* 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 SHERLOCK_SCALPEL_INVENTORY_H
|
||||
#define SHERLOCK_SCALPEL_INVENTORY_H
|
||||
|
||||
#include "sherlock/inventory.h"
|
||||
|
||||
#include "common/events.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
namespace Scalpel {
|
||||
|
||||
class ScalpelInventory : public Inventory {
|
||||
public:
|
||||
ScalpelInventory(SherlockEngine *vm);
|
||||
~ScalpelInventory() override;
|
||||
|
||||
Common::String _fixedTextExit;
|
||||
Common::String _fixedTextLook;
|
||||
Common::String _fixedTextUse;
|
||||
Common::String _fixedTextGive;
|
||||
|
||||
Common::CustomEventType _actionsIndexed[8];
|
||||
|
||||
/**
|
||||
* Put the game into inventory mode and open the interface window.
|
||||
*/
|
||||
void drawInventory(InvNewMode flag);
|
||||
|
||||
/**
|
||||
* Prints the line of inventory commands at the top of an inventory window with
|
||||
* the correct highlighting
|
||||
*/
|
||||
void invCommands(bool slamIt);
|
||||
|
||||
/**
|
||||
* Set the highlighting color of a given inventory item
|
||||
*/
|
||||
void highlight(int index, byte color);
|
||||
|
||||
/**
|
||||
* Support method for refreshing the display of the inventory
|
||||
*/
|
||||
void refreshInv();
|
||||
|
||||
/**
|
||||
* Display the character's inventory. The slamIt parameter specifies:
|
||||
*/
|
||||
void putInv(InvSlamMode slamIt);
|
||||
|
||||
/**
|
||||
* Load the list of names the inventory items correspond to, if not already loaded,
|
||||
* and then calls loadGraphics to load the associated graphics
|
||||
*/
|
||||
void loadInv() override;
|
||||
|
||||
/**
|
||||
* Identifies a button number according to the action, that the user pressed
|
||||
*/
|
||||
int identifyUserButton(Common::CustomEventType action);
|
||||
};
|
||||
|
||||
} // End of namespace Scalpel
|
||||
|
||||
} // End of namespace Sherlock
|
||||
|
||||
#endif
|
||||
694
engines/sherlock/scalpel/scalpel_journal.cpp
Normal file
694
engines/sherlock/scalpel/scalpel_journal.cpp
Normal file
@@ -0,0 +1,694 @@
|
||||
/* 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 "sherlock/journal.h"
|
||||
#include "sherlock/sherlock.h"
|
||||
#include "sherlock/scalpel/scalpel_fixed_text.h"
|
||||
#include "sherlock/scalpel/scalpel_journal.h"
|
||||
#include "sherlock/scalpel/scalpel_screen.h"
|
||||
#include "sherlock/scalpel/scalpel.h"
|
||||
#include "sherlock/tattoo/tattoo_journal.h"
|
||||
#include "graphics/palette.h"
|
||||
|
||||
#include "backends/keymapper/keymapper.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
namespace Scalpel {
|
||||
|
||||
#define JOURNAL_BUTTONS_Y_INTL 178
|
||||
#define JOURNAL_BUTTONS_Y_ZH 181
|
||||
#define JOURNAL_SEARCH_LEFT 15
|
||||
#define JOURNAL_SEARCH_TOP_INTL 186
|
||||
#define JOURNAL_SEARCH_TOP_ZH 184
|
||||
#define JOURNAL_SEARCH_RIGHT 296
|
||||
#define JOURNAL_SEACRH_MAX_CHARS 50
|
||||
|
||||
// Positioning of buttons in the journal view
|
||||
static const int JOURNAL_POINTS_INTL[9][3] = {
|
||||
{ 6, 68, 37 },
|
||||
{ 69, 131, 100 },
|
||||
{ 132, 192, 162 },
|
||||
{ 193, 250, 221 },
|
||||
{ 251, 313, 281 },
|
||||
{ 6, 82, 44 },
|
||||
{ 83, 159, 121 },
|
||||
{ 160, 236, 198 },
|
||||
{ 237, 313, 275 }
|
||||
};
|
||||
|
||||
static const int JOURNAL_POINTS_ZH[9][3] = {
|
||||
{ 0, 52, 26 },
|
||||
{ 52, 121, 87 },
|
||||
{ 122, 157, 140 },
|
||||
{ 157, 194, 176 },
|
||||
{ 194, 265, 230 },
|
||||
{ 265, 320, 293 },
|
||||
{ 270, 320, 295 },
|
||||
{ 270, 320, 295 },
|
||||
{ 0, 0, 0 }
|
||||
};
|
||||
|
||||
static const int SEARCH_POINTS_INTL[3][3] = {
|
||||
{ 51, 123, 86 },
|
||||
{ 124, 196, 159 },
|
||||
{ 197, 269, 232 }
|
||||
};
|
||||
|
||||
static const int SEARCH_POINTS_ZH[3][3] = {
|
||||
{ 206, 243, 225 },
|
||||
{ 243, 279, 261 },
|
||||
{ 279, 315, 297 }
|
||||
};
|
||||
|
||||
/*----------------------------------------------------------------*/
|
||||
|
||||
ScalpelJournal::ScalpelJournal(SherlockEngine *vm) : Journal(vm) {
|
||||
if (_vm->_interactiveFl) {
|
||||
// Load the journal directory and location names
|
||||
loadLocations();
|
||||
}
|
||||
|
||||
_fixedTextWatsonsJournal = FIXED(Journal_WatsonsJournal);
|
||||
_fixedTextExit = FIXED(Journal_Exit);
|
||||
_fixedTextBack10 = FIXED(Journal_Back10);
|
||||
_fixedTextUp = FIXED(Journal_Up);
|
||||
_fixedTextDown = FIXED(Journal_Down);
|
||||
_fixedTextAhead10 = FIXED(Journal_Ahead10);
|
||||
_fixedTextSearch = FIXED(Journal_Search);
|
||||
_fixedTextFirstPage = FIXED(Journal_FirstPage);
|
||||
_fixedTextLastPage = FIXED(Journal_LastPage);
|
||||
_fixedTextPrintText = FIXED(Journal_PrintText);
|
||||
|
||||
_fixedTextSearchExit = FIXED(JournalSearch_Exit);
|
||||
_fixedTextSearchBackward = FIXED(JournalSearch_Backward);
|
||||
_fixedTextSearchForward = FIXED(JournalSearch_Forward);
|
||||
_fixedTextSearchNotFound = FIXED(JournalSearch_NotFound);
|
||||
|
||||
_hotkeySearchExit = toupper(_fixedTextSearchExit[0]);
|
||||
_hotkeySearchBackward = toupper(_fixedTextSearchBackward[0]);
|
||||
_hotkeySearchForward = toupper(_fixedTextSearchForward[0]);
|
||||
}
|
||||
|
||||
Common::Rect ScalpelJournal::getButtonRect(JournalButton btn) {
|
||||
int idx = btn - 1;
|
||||
if (_vm->getLanguage() == Common::ZH_TWN) {
|
||||
if (btn >= BTN_FIRST_PAGE) {
|
||||
return Common::Rect(JOURNAL_POINTS_ZH[idx][0], JOURNAL_BUTTONS_Y_ZH - (btn - BTN_FIRST_PAGE + 1) * 19,
|
||||
JOURNAL_POINTS_ZH[idx][1], JOURNAL_BUTTONS_Y_ZH + 19 - (btn - BTN_FIRST_PAGE + 1) * 19);
|
||||
} else
|
||||
return Common::Rect(JOURNAL_POINTS_ZH[idx][0], JOURNAL_BUTTONS_Y_ZH,
|
||||
JOURNAL_POINTS_ZH[idx][1], JOURNAL_BUTTONS_Y_ZH + 19);
|
||||
} else {
|
||||
if (btn >= BTN_SEARCH)
|
||||
return Common::Rect(JOURNAL_POINTS_INTL[idx][0], JOURNAL_BUTTONS_Y_INTL + 11,
|
||||
JOURNAL_POINTS_INTL[idx][1], JOURNAL_BUTTONS_Y_INTL + 21);
|
||||
else
|
||||
return Common::Rect(JOURNAL_POINTS_INTL[idx][0], JOURNAL_BUTTONS_Y_INTL,
|
||||
JOURNAL_POINTS_INTL[idx][1], JOURNAL_BUTTONS_Y_INTL + 10);
|
||||
}
|
||||
}
|
||||
|
||||
Common::Point ScalpelJournal::getButtonTextPoint(JournalButton btn) {
|
||||
int idx = btn - 1;
|
||||
if (_vm->getLanguage() == Common::ZH_TWN) {
|
||||
if (btn >= BTN_FIRST_PAGE)
|
||||
return Common::Point(JOURNAL_POINTS_ZH[idx][2], JOURNAL_BUTTONS_Y_ZH + 2 - (btn - BTN_FIRST_PAGE + 1) * 19);
|
||||
else
|
||||
return Common::Point(JOURNAL_POINTS_ZH[idx][2], JOURNAL_BUTTONS_Y_ZH + 2);
|
||||
} else {
|
||||
if (btn >= BTN_SEARCH)
|
||||
return Common::Point(JOURNAL_POINTS_INTL[idx][2], JOURNAL_BUTTONS_Y_INTL + 11);
|
||||
else
|
||||
return Common::Point(JOURNAL_POINTS_INTL[idx][2], JOURNAL_BUTTONS_Y_INTL);
|
||||
}
|
||||
}
|
||||
|
||||
Common::Rect ScalpelJournal::getSearchButtonRect(int idx) {
|
||||
if (_vm->getLanguage() == Common::ZH_TWN) {
|
||||
return Common::Rect(SEARCH_POINTS_ZH[idx][0], 175, SEARCH_POINTS_ZH[idx][1], 194);
|
||||
} else {
|
||||
return Common::Rect(SEARCH_POINTS_INTL[idx][0], 174, SEARCH_POINTS_INTL[idx][1], 184);
|
||||
}
|
||||
}
|
||||
|
||||
Common::Point ScalpelJournal::getSearchButtonTextPoint(int idx) {
|
||||
if (_vm->getLanguage() == Common::ZH_TWN) {
|
||||
return Common::Point(SEARCH_POINTS_ZH[idx][2], 177);
|
||||
} else {
|
||||
return Common::Point(SEARCH_POINTS_INTL[idx][2], 174);
|
||||
}
|
||||
}
|
||||
|
||||
void ScalpelJournal::loadLocations() {
|
||||
Resources &res = *_vm->_res;
|
||||
|
||||
_directory.clear();
|
||||
_locations.clear();
|
||||
|
||||
|
||||
Common::SeekableReadStream *dir = res.load("talk.lib");
|
||||
dir->skip(4); // Skip header
|
||||
|
||||
// Get the numer of entries
|
||||
_directory.resize(dir->readUint16LE());
|
||||
|
||||
// Read in each entry
|
||||
char buffer[17];
|
||||
for (uint idx = 0; idx < _directory.size(); ++idx) {
|
||||
dir->read(buffer, 17);
|
||||
buffer[16] = '\0';
|
||||
|
||||
_directory[idx] = Common::String(buffer);
|
||||
}
|
||||
|
||||
delete dir;
|
||||
|
||||
if (IS_3DO) {
|
||||
// 3DO: storage of locations is currently unknown TODO
|
||||
return;
|
||||
}
|
||||
|
||||
// Load in the locations stored in journal.txt
|
||||
Common::SeekableReadStream *loc = res.load("journal.txt");
|
||||
|
||||
while (loc->pos() < loc->size()) {
|
||||
Common::String line;
|
||||
char c;
|
||||
while ((c = loc->readByte()) != 0)
|
||||
line += c;
|
||||
|
||||
// WORKAROUND: Special fixes for faulty translations
|
||||
// Was obviously not done in the original interpreter
|
||||
if (_vm->getLanguage() == Common::ES_ESP) {
|
||||
// Spanish version
|
||||
// We fix all sorts of typos
|
||||
// see bug #6931
|
||||
if (line == "En el cajellon destras del teatro Regency") {
|
||||
line = "En el callejon detras del teatro Regency";
|
||||
} else if (line == "En el apartamente de Simon Kingsley") {
|
||||
line = "En el apartamento de Simon Kingsley";
|
||||
} else if (line == "Bajo la muelle de Savoy Pier") {
|
||||
line = "Bajo el muelle de Savoy Pier";
|
||||
} else if (line == "En le viejo Sherman") {
|
||||
line = "En el viejo Sherman";
|
||||
} else if (line == "En la entrada de la cada de Anna Carroway") {
|
||||
line = "En la entrada de la casa de Anna Carroway";
|
||||
}
|
||||
}
|
||||
|
||||
_locations.push_back(line);
|
||||
}
|
||||
|
||||
delete loc;
|
||||
}
|
||||
|
||||
void ScalpelJournal::drawFrame() {
|
||||
Resources &res = *_vm->_res;
|
||||
ScalpelScreen &screen = *(ScalpelScreen *)_vm->_screen;
|
||||
byte palette[Graphics::PALETTE_SIZE];
|
||||
|
||||
// Load in the journal background
|
||||
Common::SeekableReadStream *bg = res.load("journal.lbv");
|
||||
bg->read(screen._backBuffer1.getPixels(), SHERLOCK_SCREEN_WIDTH * SHERLOCK_SCREEN_HEIGHT);
|
||||
bg->read(palette, Graphics::PALETTE_SIZE);
|
||||
delete bg;
|
||||
|
||||
// Translate the palette for display
|
||||
for (int idx = 0; idx < Graphics::PALETTE_SIZE; ++idx)
|
||||
palette[idx] = PALETTE_6BIT_TO_8BIT(palette[idx]);
|
||||
|
||||
// Set the palette and print the title
|
||||
screen.setPalette(palette);
|
||||
if (_vm->getLanguage() == Common::ZH_TWN) {
|
||||
screen.gPrint(Common::Point(111, 13), BUTTON_BOTTOM, "%s", _fixedTextWatsonsJournal.c_str());
|
||||
screen.gPrint(Common::Point(110, 12), INV_FOREGROUND, "%s", _fixedTextWatsonsJournal.c_str());
|
||||
} else {
|
||||
screen.gPrint(Common::Point(111, 18), BUTTON_BOTTOM, "%s", _fixedTextWatsonsJournal.c_str());
|
||||
screen.gPrint(Common::Point(110, 17), INV_FOREGROUND, "%s", _fixedTextWatsonsJournal.c_str());
|
||||
}
|
||||
|
||||
// Draw the buttons
|
||||
screen.makeButton(getButtonRect(BTN_EXIT), getButtonTextPoint(BTN_EXIT), _fixedTextExit);
|
||||
screen.makeButton(getButtonRect(BTN_BACK10), getButtonTextPoint(BTN_BACK10), _fixedTextBack10);
|
||||
screen.makeButton(getButtonRect(BTN_UP), getButtonTextPoint(BTN_UP), _fixedTextUp);
|
||||
screen.makeButton(getButtonRect(BTN_DOWN), getButtonTextPoint(BTN_DOWN), _fixedTextDown);
|
||||
screen.makeButton(getButtonRect(BTN_AHEAD110), getButtonTextPoint(BTN_AHEAD110), _fixedTextAhead10);
|
||||
screen.makeButton(getButtonRect(BTN_SEARCH), getButtonTextPoint(BTN_SEARCH), _fixedTextSearch);
|
||||
screen.makeButton(getButtonRect(BTN_FIRST_PAGE), getButtonTextPoint(BTN_FIRST_PAGE), _fixedTextFirstPage);
|
||||
screen.makeButton(getButtonRect(BTN_LAST_PAGE), getButtonTextPoint(BTN_LAST_PAGE), _fixedTextLastPage);
|
||||
|
||||
// WORKAROUND: Draw Print Text button as disabled, since we don't support it in ScummVM
|
||||
// In Chinese version skip it altogether to make space for hotkeys
|
||||
if (_vm->getLanguage() != Common::ZH_TWN) {
|
||||
screen.makeButton(getButtonRect(BTN_PRINT_TEXT), getButtonTextPoint(BTN_PRINT_TEXT), _fixedTextPrintText);
|
||||
screen.buttonPrint(getButtonTextPoint(BTN_PRINT_TEXT), COMMAND_NULL, false, _fixedTextPrintText);
|
||||
}
|
||||
}
|
||||
|
||||
void ScalpelJournal::drawInterface() {
|
||||
ScalpelScreen &screen = *(ScalpelScreen *)_vm->_screen;
|
||||
|
||||
drawFrame();
|
||||
|
||||
if (_journal.empty()) {
|
||||
_up = _down = 0;
|
||||
} else {
|
||||
drawJournal(0, 0);
|
||||
}
|
||||
|
||||
doArrows();
|
||||
|
||||
// Show the entire screen
|
||||
screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
|
||||
}
|
||||
|
||||
void ScalpelJournal::doArrows() {
|
||||
ScalpelScreen &screen = *(ScalpelScreen *)_vm->_screen;
|
||||
byte color;
|
||||
|
||||
color = (_page > 1) ? COMMAND_FOREGROUND : COMMAND_NULL;
|
||||
screen.buttonPrint(getButtonTextPoint(BTN_BACK10), color, false, _fixedTextBack10);
|
||||
screen.buttonPrint(getButtonTextPoint(BTN_UP), color, false, _fixedTextUp);
|
||||
|
||||
color = _down ? COMMAND_FOREGROUND : COMMAND_NULL;
|
||||
screen.buttonPrint(getButtonTextPoint(BTN_DOWN), color, false, _fixedTextDown);
|
||||
screen.buttonPrint(getButtonTextPoint(BTN_AHEAD110), color, false, _fixedTextAhead10);
|
||||
screen.buttonPrint(getButtonTextPoint(BTN_LAST_PAGE), color, false, _fixedTextLastPage);
|
||||
|
||||
color = _journal.size() > 0 ? COMMAND_FOREGROUND : COMMAND_NULL;
|
||||
screen.buttonPrint(getButtonTextPoint(BTN_SEARCH), color, false, _fixedTextSearch);
|
||||
if (_vm->getLanguage() != Common::ZH_TWN) {
|
||||
screen.buttonPrint(getButtonTextPoint(BTN_PRINT_TEXT), COMMAND_NULL, false, _fixedTextPrintText);
|
||||
}
|
||||
|
||||
color = _page > 1 ? COMMAND_FOREGROUND : COMMAND_NULL;
|
||||
screen.buttonPrint(getButtonTextPoint(BTN_FIRST_PAGE), color, false, _fixedTextFirstPage);
|
||||
}
|
||||
|
||||
JournalButton ScalpelJournal::getHighlightedButton(const Common::Point &pt) {
|
||||
if (getButtonRect(BTN_EXIT).contains(pt))
|
||||
return BTN_EXIT;
|
||||
|
||||
if (getButtonRect(BTN_BACK10).contains(pt) && _page > 1)
|
||||
return BTN_BACK10;
|
||||
|
||||
if (getButtonRect(BTN_UP).contains(pt) && _up)
|
||||
return BTN_UP;
|
||||
|
||||
if (getButtonRect(BTN_DOWN).contains(pt) && _down)
|
||||
return BTN_DOWN;
|
||||
|
||||
if (getButtonRect(BTN_AHEAD110).contains(pt) && _down)
|
||||
return BTN_AHEAD110;
|
||||
|
||||
if (getButtonRect(BTN_SEARCH).contains(pt) && !_journal.empty())
|
||||
return BTN_SEARCH;
|
||||
|
||||
if (getButtonRect(BTN_FIRST_PAGE).contains(pt) && _up)
|
||||
return BTN_FIRST_PAGE;
|
||||
|
||||
if (getButtonRect(BTN_LAST_PAGE).contains(pt) && _down)
|
||||
return BTN_LAST_PAGE;
|
||||
|
||||
if (_vm->getLanguage() != Common::ZH_TWN && getButtonRect(BTN_PRINT_TEXT).contains(pt) && !_journal.empty())
|
||||
return BTN_PRINT_TEXT;
|
||||
|
||||
return BTN_NONE;
|
||||
}
|
||||
|
||||
bool ScalpelJournal::handleEvents(Common::CustomEventType action) {
|
||||
Events &events = *_vm->_events;
|
||||
ScalpelScreen &screen = *(ScalpelScreen *)_vm->_screen;
|
||||
bool doneFlag = false;
|
||||
|
||||
Common::Point pt = events.mousePos();
|
||||
JournalButton btn = getHighlightedButton(pt);
|
||||
byte color;
|
||||
|
||||
if (events._pressed || events._released) {
|
||||
// Exit button
|
||||
color = (btn == BTN_EXIT) ? COMMAND_HIGHLIGHTED : COMMAND_FOREGROUND;
|
||||
screen.buttonPrint(getButtonTextPoint(BTN_EXIT), color, true, _fixedTextExit);
|
||||
|
||||
// Back 10 button
|
||||
if (btn == BTN_BACK10) {
|
||||
screen.buttonPrint(getButtonTextPoint(BTN_BACK10), COMMAND_HIGHLIGHTED, true, _fixedTextBack10);
|
||||
} else if (_page > 1) {
|
||||
screen.buttonPrint(getButtonTextPoint(BTN_BACK10), COMMAND_FOREGROUND, true, _fixedTextBack10);
|
||||
}
|
||||
|
||||
// Up button
|
||||
if (btn == BTN_UP) {
|
||||
screen.buttonPrint(getButtonTextPoint(BTN_UP), COMMAND_HIGHLIGHTED, true, _fixedTextUp);
|
||||
} else if (_up) {
|
||||
screen.buttonPrint(getButtonTextPoint(BTN_UP), COMMAND_FOREGROUND, true, _fixedTextUp);
|
||||
}
|
||||
|
||||
// Down button
|
||||
if (btn == BTN_DOWN) {
|
||||
screen.buttonPrint(getButtonTextPoint(BTN_DOWN), COMMAND_HIGHLIGHTED, true, _fixedTextDown);
|
||||
} else if (_down) {
|
||||
screen.buttonPrint(getButtonTextPoint(BTN_DOWN), COMMAND_FOREGROUND, true, _fixedTextDown);
|
||||
}
|
||||
|
||||
// Ahead 10 button
|
||||
if (btn == BTN_AHEAD110) {
|
||||
screen.buttonPrint(getButtonTextPoint(BTN_AHEAD110), COMMAND_HIGHLIGHTED, true, _fixedTextAhead10);
|
||||
} else if (_down) {
|
||||
screen.buttonPrint(getButtonTextPoint(BTN_AHEAD110), COMMAND_FOREGROUND, true, _fixedTextAhead10);
|
||||
}
|
||||
|
||||
// Search button
|
||||
if (btn == BTN_SEARCH) {
|
||||
color = COMMAND_HIGHLIGHTED;
|
||||
} else if (_journal.empty()) {
|
||||
color = COMMAND_NULL;
|
||||
} else {
|
||||
color = COMMAND_FOREGROUND;
|
||||
}
|
||||
screen.buttonPrint(getButtonTextPoint(BTN_SEARCH), color, true, _fixedTextSearch);
|
||||
|
||||
// First Page button
|
||||
if (btn == BTN_FIRST_PAGE) {
|
||||
color = COMMAND_HIGHLIGHTED;
|
||||
} else if (_up) {
|
||||
color = COMMAND_FOREGROUND;
|
||||
} else {
|
||||
color = COMMAND_NULL;
|
||||
}
|
||||
screen.buttonPrint(getButtonTextPoint(BTN_FIRST_PAGE), color, true, _fixedTextFirstPage);
|
||||
|
||||
// Last Page button
|
||||
if (btn == BTN_LAST_PAGE) {
|
||||
color = COMMAND_HIGHLIGHTED;
|
||||
} else if (_down) {
|
||||
color = COMMAND_FOREGROUND;
|
||||
} else {
|
||||
color = COMMAND_NULL;
|
||||
}
|
||||
screen.buttonPrint(getButtonTextPoint(BTN_LAST_PAGE), color, true, _fixedTextLastPage);
|
||||
|
||||
if (_vm->getLanguage() != Common::ZH_TWN) {
|
||||
// Print Text button
|
||||
screen.buttonPrint(getButtonTextPoint(BTN_PRINT_TEXT), COMMAND_NULL, true, _fixedTextPrintText);
|
||||
}
|
||||
}
|
||||
|
||||
if (btn == BTN_EXIT && events._released) {
|
||||
// Exit button pressed
|
||||
doneFlag = true;
|
||||
|
||||
} else if (((btn == BTN_BACK10 && events._released) || action == kActionScalpelJournalBack10) && (_page > 1)) {
|
||||
// Scrolll up 10 pages
|
||||
if (_page < 11)
|
||||
drawJournal(1, (_page - 1) * LINES_PER_PAGE);
|
||||
else
|
||||
drawJournal(1, 10 * LINES_PER_PAGE);
|
||||
|
||||
doArrows();
|
||||
screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
|
||||
|
||||
} else if (((btn == BTN_UP && events._released) || action == kActionScalpelScrollUp) && _up) {
|
||||
// Scroll up
|
||||
drawJournal(1, LINES_PER_PAGE);
|
||||
doArrows();
|
||||
screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
|
||||
|
||||
} else if (((btn == BTN_DOWN && events._released) || action == kActionScalpelScrollDown) && _down) {
|
||||
// Scroll down
|
||||
drawJournal(2, LINES_PER_PAGE);
|
||||
doArrows();
|
||||
screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
|
||||
|
||||
} else if (((btn == BTN_AHEAD110 && events._released) || action == kActionScalpelJournalAhead10) && _down) {
|
||||
// Scroll down 10 pages
|
||||
if ((_page + 10) > _maxPage)
|
||||
drawJournal(2, (_maxPage - _page) * LINES_PER_PAGE);
|
||||
else
|
||||
drawJournal(2, 10 * LINES_PER_PAGE);
|
||||
|
||||
doArrows();
|
||||
screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
|
||||
|
||||
} else if (((btn == BTN_SEARCH && events._released) || action == kActionScalpelJournalSearch) && !_journal.empty()) {
|
||||
screen.buttonPrint(getButtonTextPoint(BTN_SEARCH), COMMAND_FOREGROUND, true, _fixedTextSearch);
|
||||
bool notFound = false;
|
||||
|
||||
Common::Keymapper *keymapper = g_system->getEventManager()->getKeymapper();
|
||||
keymapper->disableAllGameKeymaps();
|
||||
|
||||
do {
|
||||
int dir;
|
||||
if ((dir = getSearchString(notFound)) != 0) {
|
||||
int savedIndex = _index;
|
||||
int savedSub = _sub;
|
||||
int savedPage = _page;
|
||||
|
||||
if (drawJournal(dir + 2, 1000 * LINES_PER_PAGE) == 0) {
|
||||
_index = savedIndex;
|
||||
_sub = savedSub;
|
||||
_page = savedPage;
|
||||
|
||||
drawFrame();
|
||||
drawJournal(0, 0);
|
||||
notFound = true;
|
||||
} else {
|
||||
doneFlag = true;
|
||||
}
|
||||
|
||||
doArrows();
|
||||
screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
|
||||
} else {
|
||||
doneFlag = true;
|
||||
}
|
||||
} while (!doneFlag);
|
||||
doneFlag = false;
|
||||
|
||||
keymapper->getKeymap("scalpel-journal")->setEnabled(true);
|
||||
keymapper->getKeymap("scalpel-scroll")->setEnabled(true);
|
||||
keymapper->getKeymap("scalpel-quit")->setEnabled(true);
|
||||
|
||||
} else if (((btn == BTN_FIRST_PAGE && events._released) || action == kActionScalpelJournalFirstPage) && _up) {
|
||||
// First page
|
||||
_index = _sub = 0;
|
||||
_up = _down = false;
|
||||
_page = 1;
|
||||
|
||||
drawFrame();
|
||||
drawJournal(0, 0);
|
||||
doArrows();
|
||||
screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
|
||||
|
||||
} else if (((btn == BTN_LAST_PAGE && events._released) || action == kActionScalpelJournalLastPage) && _down) {
|
||||
// Last page
|
||||
if ((_page + 10) > _maxPage)
|
||||
drawJournal(2, (_maxPage - _page) * LINES_PER_PAGE);
|
||||
else
|
||||
drawJournal(2, 1000 * LINES_PER_PAGE);
|
||||
|
||||
doArrows();
|
||||
screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
|
||||
}
|
||||
|
||||
events.wait(2);
|
||||
|
||||
return doneFlag;
|
||||
}
|
||||
|
||||
int ScalpelJournal::getSearchString(bool printError) {
|
||||
Events &events = *_vm->_events;
|
||||
ScalpelScreen &screen = *(ScalpelScreen *)_vm->_screen;
|
||||
Talk &talk = *_vm->_talk;
|
||||
int xp;
|
||||
int yp;
|
||||
bool flag = false;
|
||||
Common::String name;
|
||||
int done = 0;
|
||||
byte color;
|
||||
bool isChinese = _vm->getLanguage() == Common::ZH_TWN;
|
||||
|
||||
// Draw search panel
|
||||
if (isChinese)
|
||||
screen.makePanel(Common::Rect(6, 171, 318, 199));
|
||||
else
|
||||
screen.makePanel(Common::Rect(6, 171, 313, 199));
|
||||
|
||||
screen.makeButton(getSearchButtonRect(0), getSearchButtonTextPoint(0), _fixedTextSearchExit, !isChinese);
|
||||
screen.makeButton(getSearchButtonRect(1), getSearchButtonTextPoint(1), _fixedTextSearchBackward, !isChinese);
|
||||
screen.makeButton(getSearchButtonRect(2), getSearchButtonTextPoint(2), _fixedTextSearchForward, !isChinese);
|
||||
|
||||
if (isChinese)
|
||||
screen.makeField(Common::Rect(12, 175, 205, 194));
|
||||
else
|
||||
screen.makeField(Common::Rect(12, 185, 307, 196));
|
||||
|
||||
if (printError) {
|
||||
screen.gPrint(Common::Point((SHERLOCK_SCREEN_WIDTH - screen.stringWidth(_fixedTextSearchNotFound)) / 2, 185),
|
||||
INV_FOREGROUND, "%s", _fixedTextSearchNotFound.c_str());
|
||||
} else if (!_find.empty()) {
|
||||
// There's already a search term, display it already
|
||||
screen.gPrint(Common::Point(15, 185), TALK_FOREGROUND, "%s", _find.c_str());
|
||||
name = _find;
|
||||
}
|
||||
|
||||
if (_vm->getLanguage() == Common::ZH_TWN)
|
||||
screen.slamArea(6, 171, 312, 28);
|
||||
else
|
||||
screen.slamArea(6, 171, 307, 28);
|
||||
|
||||
if (printError) {
|
||||
// Give time for user to see the message
|
||||
events.setButtonState();
|
||||
for (int idx = 0; idx < 40 && !_vm->shouldQuit() && !events.kbHit() && !events._released; ++idx) {
|
||||
events.pollEvents();
|
||||
events.setButtonState();
|
||||
events.wait(2);
|
||||
}
|
||||
|
||||
events.clearKeyboard();
|
||||
events.clearActions();
|
||||
screen._backBuffer1.fillRect(Common::Rect(13, 186, 306, 195), BUTTON_MIDDLE);
|
||||
|
||||
if (!_find.empty()) {
|
||||
screen.gPrint(Common::Point(15, 185), TALK_FOREGROUND, "%s", _find.c_str());
|
||||
name = _find;
|
||||
}
|
||||
|
||||
screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
|
||||
}
|
||||
|
||||
xp = JOURNAL_SEARCH_LEFT + screen.stringWidth(name);
|
||||
yp = isChinese ? JOURNAL_SEARCH_TOP_ZH : JOURNAL_SEARCH_TOP_INTL;
|
||||
|
||||
do {
|
||||
events._released = false;
|
||||
JournalButton found = BTN_NONE;
|
||||
|
||||
while (!_vm->shouldQuit() && !events.kbHit() && !events._released) {
|
||||
found = BTN_NONE;
|
||||
if (talk._talkToAbort)
|
||||
return 0;
|
||||
|
||||
// Check if key or mouse button press has occurred
|
||||
events.setButtonState();
|
||||
Common::Point pt = events.mousePos();
|
||||
|
||||
flag = !flag;
|
||||
screen.vgaBar(Common::Rect(xp, yp, xp + 8, yp + 9), flag ? INV_FOREGROUND : BUTTON_MIDDLE);
|
||||
|
||||
if (events._pressed || events._released) {
|
||||
if (getSearchButtonRect(0).contains(pt)) {
|
||||
found = BTN_EXIT;
|
||||
color = COMMAND_HIGHLIGHTED;
|
||||
} else {
|
||||
color = COMMAND_FOREGROUND;
|
||||
}
|
||||
screen.buttonPrint(getSearchButtonTextPoint(0), color, false, _fixedTextSearchExit, !isChinese);
|
||||
|
||||
if (getSearchButtonRect(1).contains(pt)) {
|
||||
found = BTN_BACKWARD;
|
||||
color = COMMAND_HIGHLIGHTED;
|
||||
} else {
|
||||
color = COMMAND_FOREGROUND;
|
||||
}
|
||||
screen.buttonPrint(getSearchButtonTextPoint(1), color, false, _fixedTextSearchBackward, !isChinese);
|
||||
|
||||
if (getSearchButtonRect(2).contains(pt)) {
|
||||
found = BTN_FORWARD;
|
||||
color = COMMAND_HIGHLIGHTED;
|
||||
} else {
|
||||
color = COMMAND_FOREGROUND;
|
||||
}
|
||||
screen.buttonPrint(getSearchButtonTextPoint(2), color, false, _fixedTextSearchForward, !isChinese);
|
||||
}
|
||||
|
||||
events.wait(2);
|
||||
}
|
||||
|
||||
if (events.kbHit()) {
|
||||
Common::KeyState keyState = events.getKey();
|
||||
|
||||
if ((keyState.keycode == Common::KEYCODE_BACKSPACE) && (name.size() > 0)) {
|
||||
screen.vgaBar(Common::Rect(xp - screen.charWidth(name.lastChar()), yp, xp + 8, yp + 9), BUTTON_MIDDLE);
|
||||
xp -= screen.charWidth(name.lastChar());
|
||||
screen.vgaBar(Common::Rect(xp, yp, xp + 8, yp + 9), INV_FOREGROUND);
|
||||
name.deleteLastChar();
|
||||
|
||||
} else if (keyState.keycode == Common::KEYCODE_RETURN) {
|
||||
done = 1;
|
||||
|
||||
} else if (keyState.keycode == Common::KEYCODE_ESCAPE) {
|
||||
screen.vgaBar(Common::Rect(xp, yp, xp + 8, yp + 9), BUTTON_MIDDLE);
|
||||
done = -1;
|
||||
|
||||
} else if (keyState.ascii >= ' ' && keyState.ascii <= 'z' && keyState.keycode != Common::KEYCODE_AT &&
|
||||
name.size() < JOURNAL_SEACRH_MAX_CHARS && (xp + screen.charWidth(keyState.ascii)) < JOURNAL_SEARCH_RIGHT) {
|
||||
char ch = toupper(keyState.ascii);
|
||||
screen.vgaBar(Common::Rect(xp, yp, xp + 8, yp + 9), BUTTON_MIDDLE);
|
||||
screen.print(Common::Point(xp, yp), TALK_FOREGROUND, "%c", ch);
|
||||
xp += screen.charWidth(ch);
|
||||
name += ch;
|
||||
}
|
||||
}
|
||||
|
||||
if (events._released) {
|
||||
switch (found) {
|
||||
case BTN_EXIT:
|
||||
done = -1; break;
|
||||
case BTN_BACKWARD:
|
||||
done = 2; break;
|
||||
case BTN_FORWARD:
|
||||
done = 1; break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (!done && !_vm->shouldQuit());
|
||||
|
||||
if (done != -1) {
|
||||
_find = name;
|
||||
} else {
|
||||
done = 0;
|
||||
}
|
||||
|
||||
// Redisplay the journal screen
|
||||
drawFrame();
|
||||
drawJournal(0, 0);
|
||||
screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
|
||||
|
||||
return done;
|
||||
}
|
||||
|
||||
void ScalpelJournal::resetPosition() {
|
||||
_index = _sub = _up = _down = 0;
|
||||
_page = 1;
|
||||
}
|
||||
|
||||
void ScalpelJournal::record(int converseNum, int statementNum, bool replyOnly) {
|
||||
// there seems to be no journal in the 3DO version
|
||||
if (!IS_3DO)
|
||||
Journal::record(converseNum, statementNum, replyOnly);
|
||||
}
|
||||
|
||||
} // End of namespace Scalpel
|
||||
|
||||
} // End of namespace Sherlock
|
||||
126
engines/sherlock/scalpel/scalpel_journal.h
Normal file
126
engines/sherlock/scalpel/scalpel_journal.h
Normal file
@@ -0,0 +1,126 @@
|
||||
/* 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 SHERLOCK_SCALPEL_JOURNAL_H
|
||||
#define SHERLOCK_SCALPEL_JOURNAL_H
|
||||
|
||||
#include "sherlock/journal.h"
|
||||
#include "sherlock/saveload.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "common/array.h"
|
||||
#include "common/events.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/str-array.h"
|
||||
#include "common/stream.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
namespace Scalpel {
|
||||
|
||||
enum JournalButton {
|
||||
BTN_NONE, BTN_EXIT, BTN_BACK10, BTN_UP, BTN_DOWN, BTN_AHEAD110, BTN_SEARCH,
|
||||
BTN_FIRST_PAGE, BTN_LAST_PAGE, BTN_PRINT_TEXT, BTN_BACKWARD, BTN_FORWARD
|
||||
};
|
||||
|
||||
class ScalpelJournal: public Journal {
|
||||
public:
|
||||
Common::String _fixedTextWatsonsJournal;
|
||||
Common::String _fixedTextExit;
|
||||
Common::String _fixedTextBack10;
|
||||
Common::String _fixedTextUp;
|
||||
Common::String _fixedTextDown;
|
||||
Common::String _fixedTextAhead10;
|
||||
Common::String _fixedTextSearch;
|
||||
Common::String _fixedTextFirstPage;
|
||||
Common::String _fixedTextLastPage;
|
||||
Common::String _fixedTextPrintText;
|
||||
|
||||
Common::String _fixedTextSearchExit;
|
||||
Common::String _fixedTextSearchBackward;
|
||||
Common::String _fixedTextSearchForward;
|
||||
Common::String _fixedTextSearchNotFound;
|
||||
|
||||
byte _hotkeySearchExit;
|
||||
byte _hotkeySearchBackward;
|
||||
byte _hotkeySearchForward;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Load the list of journal locations
|
||||
*/
|
||||
void loadLocations();
|
||||
|
||||
/**
|
||||
* Display the arrows that can be used to scroll up and down pages
|
||||
*/
|
||||
void doArrows();
|
||||
|
||||
/**
|
||||
* Show the search submenu and allow the player to enter a search string
|
||||
*/
|
||||
int getSearchString(bool printError);
|
||||
|
||||
/**
|
||||
* Returns the button, if any, that is under the specified position
|
||||
*/
|
||||
JournalButton getHighlightedButton(const Common::Point &pt);
|
||||
|
||||
Common::Rect getSearchButtonRect(int idx);
|
||||
Common::Point getSearchButtonTextPoint(int idx);
|
||||
public:
|
||||
ScalpelJournal(SherlockEngine *vm);
|
||||
~ScalpelJournal() override {}
|
||||
|
||||
/**
|
||||
* Display the journal
|
||||
*/
|
||||
void drawInterface();
|
||||
|
||||
/**
|
||||
* Handle events whilst the journal is being displayed
|
||||
*/
|
||||
bool handleEvents(Common::CustomEventType action);
|
||||
public:
|
||||
/**
|
||||
* Draw the journal background, frame, and interface buttons
|
||||
*/
|
||||
void drawFrame() override;
|
||||
|
||||
/**
|
||||
* Reset viewing position to the start of the journal
|
||||
*/
|
||||
void resetPosition() override;
|
||||
|
||||
/**
|
||||
* Records statements that are said, in the order which they are said. The player
|
||||
* can then read the journal to review them
|
||||
*/
|
||||
void record(int converseNum, int statementNum, bool replyOnly = false) override;
|
||||
|
||||
Common::Rect getButtonRect(JournalButton btn);
|
||||
Common::Point getButtonTextPoint(JournalButton btn);
|
||||
};
|
||||
|
||||
} // End of namespace Scalpel
|
||||
|
||||
} // End of namespace Sherlock
|
||||
|
||||
#endif
|
||||
621
engines/sherlock/scalpel/scalpel_map.cpp
Normal file
621
engines/sherlock/scalpel/scalpel_map.cpp
Normal file
@@ -0,0 +1,621 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/system.h"
|
||||
#include "sherlock/scalpel/scalpel_map.h"
|
||||
#include "sherlock/events.h"
|
||||
#include "sherlock/people.h"
|
||||
#include "sherlock/screen.h"
|
||||
#include "sherlock/sherlock.h"
|
||||
|
||||
#include "backends/keymapper/keymapper.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
namespace Scalpel {
|
||||
|
||||
MapPaths::MapPaths() {
|
||||
_numLocations = 0;
|
||||
}
|
||||
|
||||
void MapPaths::load(int numLocations, Common::SeekableReadStream &s) {
|
||||
_numLocations = numLocations;
|
||||
_paths.resize(_numLocations * _numLocations);
|
||||
|
||||
for (int idx = 0; idx < (numLocations * numLocations); ++idx) {
|
||||
Common::Array<byte> &path = _paths[idx];
|
||||
int v;
|
||||
|
||||
do {
|
||||
v = s.readByte();
|
||||
path.push_back(v);
|
||||
} while (v && v < 254);
|
||||
}
|
||||
}
|
||||
|
||||
const byte *MapPaths::getPath(int srcLocation, int destLocation) {
|
||||
return &_paths[srcLocation * _numLocations + destLocation][0];
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------*/
|
||||
|
||||
ScalpelMap::ScalpelMap(SherlockEngine *vm): Map(vm), _topLine(g_system->getWidth(), 12, g_system->getScreenFormat()) {
|
||||
_mapCursors = nullptr;
|
||||
_shapes = nullptr;
|
||||
_iconShapes = nullptr;
|
||||
_point = 0;
|
||||
_placesShown = false;
|
||||
_cursorIndex = -1;
|
||||
_drawMap = false;
|
||||
_overPos = Point32(130 * FIXED_INT_MULTIPLIER, 126 * FIXED_INT_MULTIPLIER);
|
||||
_frameChangeFlag = false;
|
||||
|
||||
// Initialise the initial walk sequence set
|
||||
_walkSequences.resize(MAX_HOLMES_SEQUENCE);
|
||||
for (int idx = 0; idx < MAX_HOLMES_SEQUENCE; ++idx) {
|
||||
_walkSequences[idx]._sequences.resize(MAX_FRAME);
|
||||
Common::fill(&_walkSequences[idx]._sequences[0], &_walkSequences[idx]._sequences[0] + MAX_FRAME, 0);
|
||||
}
|
||||
|
||||
if (!_vm->isDemo())
|
||||
loadData();
|
||||
}
|
||||
|
||||
void ScalpelMap::loadPoints(int count, const int *xList, const int *yList, const int *transList) {
|
||||
for (int idx = 0; idx < count; ++idx, ++xList, ++yList, ++transList) {
|
||||
_points.push_back(MapEntry(*xList, *yList, *transList));
|
||||
}
|
||||
}
|
||||
|
||||
void ScalpelMap::loadSequences(int count, const byte *seq) {
|
||||
for (int idx = 0; idx < count; ++idx, seq += MAX_FRAME)
|
||||
Common::copy(seq, seq + MAX_FRAME, &_walkSequences[idx]._sequences[0]);
|
||||
}
|
||||
|
||||
void ScalpelMap::loadData() {
|
||||
// Load the list of location names
|
||||
Common::SeekableReadStream *txtStream = _vm->_res->load("chess.txt");
|
||||
|
||||
int streamSize = txtStream->size();
|
||||
while (txtStream->pos() < streamSize) {
|
||||
Common::String line;
|
||||
char c;
|
||||
while ((c = txtStream->readByte()) != '\0')
|
||||
line += c;
|
||||
|
||||
// WORKAROUND: Special fixes for faulty translations
|
||||
// Was obviously not done in the original interpreter
|
||||
if (_vm->getLanguage() == Common::ES_ESP) {
|
||||
// Spanish version
|
||||
if (line == " Alley") {
|
||||
// The "Alley" location was not translated, we do this now
|
||||
// see bug #6931
|
||||
line = " Callejon";
|
||||
} else if (line == " Alamacen") {
|
||||
// "Warehouse" location has a typo, we fix it
|
||||
// see bug #6931
|
||||
line = " Almacen";
|
||||
}
|
||||
}
|
||||
|
||||
_locationNames.push_back(line);
|
||||
}
|
||||
|
||||
delete txtStream;
|
||||
|
||||
// Load the path data
|
||||
Common::SeekableReadStream *pathStream = _vm->_res->load("chess.pth");
|
||||
|
||||
// Get routes between different locations on the map
|
||||
_paths.load(31, *pathStream);
|
||||
|
||||
// Load in the co-ordinates that the paths refer to
|
||||
_pathPoints.resize(208);
|
||||
for (uint idx = 0; idx < _pathPoints.size(); ++idx) {
|
||||
_pathPoints[idx].x = pathStream->readSint16LE();
|
||||
_pathPoints[idx].y = pathStream->readSint16LE();
|
||||
}
|
||||
|
||||
delete pathStream;
|
||||
}
|
||||
|
||||
int ScalpelMap::show() {
|
||||
Debugger &debugger = *_vm->_debugger;
|
||||
Events &events = *_vm->_events;
|
||||
People &people = *_vm->_people;
|
||||
Screen &screen = *_vm->_screen;
|
||||
bool changed = false, exitFlag = false;
|
||||
_active = true;
|
||||
|
||||
// Set font and custom cursor for the map
|
||||
int oldFont = screen.fontNumber();
|
||||
screen.setFont(0);
|
||||
|
||||
// Initial screen clear
|
||||
screen._backBuffer1.clear();
|
||||
screen.clear();
|
||||
|
||||
// Load the entire map
|
||||
ImageFile *bigMap = nullptr;
|
||||
if (!IS_3DO) {
|
||||
// PC
|
||||
bigMap = new ImageFile("bigmap.vgs");
|
||||
screen.setPalette(bigMap->_palette);
|
||||
} else {
|
||||
// 3DO
|
||||
bigMap = new ImageFile3DO("overland.cel", kImageFile3DOType_Cel);
|
||||
}
|
||||
|
||||
// Load need sprites
|
||||
setupSprites();
|
||||
|
||||
if (!IS_3DO) {
|
||||
screen._backBuffer1.SHblitFrom((*bigMap)[0], Common::Point(-_bigPos.x, -_bigPos.y));
|
||||
screen._backBuffer1.SHblitFrom((*bigMap)[1], Common::Point(-_bigPos.x, SHERLOCK_SCREEN_HEIGHT - _bigPos.y));
|
||||
screen._backBuffer1.SHblitFrom((*bigMap)[2], Common::Point(SHERLOCK_SCREEN_WIDTH - _bigPos.x, -_bigPos.y));
|
||||
screen._backBuffer1.SHblitFrom((*bigMap)[3], Common::Point(SHERLOCK_SCREEN_WIDTH - _bigPos.x, SHERLOCK_SCREEN_HEIGHT - _bigPos.y));
|
||||
} else {
|
||||
screen._backBuffer1.SHblitFrom((*bigMap)[0], Common::Point(-_bigPos.x, -_bigPos.y));
|
||||
screen.SHblitFrom((*bigMap)[0], Common::Point(-_bigPos.x, -_bigPos.y));
|
||||
}
|
||||
|
||||
_drawMap = true;
|
||||
_charPoint = -1;
|
||||
_point = -1;
|
||||
people[HOLMES]._position = _lDrawnPos = _overPos;
|
||||
|
||||
// Show place icons
|
||||
showPlaces();
|
||||
saveTopLine();
|
||||
_placesShown = true;
|
||||
|
||||
Common::Keymapper *keymapper = g_system->getEventManager()->getKeymapper();
|
||||
keymapper->disableAllGameKeymaps();
|
||||
keymapper->getKeymap("scalpel-map")->setEnabled(true);
|
||||
|
||||
// Keep looping until either a location is picked, or the game is ended
|
||||
while (!_vm->shouldQuit() && !exitFlag) {
|
||||
events.pollEventsAndWait();
|
||||
events.setButtonState();
|
||||
|
||||
if (debugger._showAllLocations == LOC_REFRESH) {
|
||||
showPlaces();
|
||||
screen.slamArea(screen._currentScroll.x, screen._currentScroll.y, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_WIDTH);
|
||||
}
|
||||
|
||||
// Action handling
|
||||
if (events.actionHit()) {
|
||||
Common::CustomEventType action = events.getAction();
|
||||
|
||||
if (action == kActionScalpelMapSelect) {
|
||||
// this action simulates a mouse release
|
||||
events._pressed = false;
|
||||
events._released = true;
|
||||
events._oldButtons = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore scrolling attempts until the screen is drawn
|
||||
if (!_drawMap) {
|
||||
Common::Point pt = events.mousePos();
|
||||
|
||||
// Check for vertical map scrolling
|
||||
if ((pt.y > (SHERLOCK_SCREEN_HEIGHT - 10) && _bigPos.y < 200) || (pt.y < 10 && _bigPos.y > 0)) {
|
||||
if (pt.y > (SHERLOCK_SCREEN_HEIGHT - 10))
|
||||
_bigPos.y += 10;
|
||||
else
|
||||
_bigPos.y -= 10;
|
||||
|
||||
changed = true;
|
||||
}
|
||||
|
||||
// Check for horizontal map scrolling
|
||||
if ((pt.x > (SHERLOCK_SCREEN_WIDTH - 10) && _bigPos.x < 315) || (pt.x < 10 && _bigPos.x > 0)) {
|
||||
if (pt.x > (SHERLOCK_SCREEN_WIDTH - 10))
|
||||
_bigPos.x += 15;
|
||||
else
|
||||
_bigPos.x -= 15;
|
||||
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
// Map has scrolled, so redraw new map view
|
||||
changed = false;
|
||||
|
||||
if (!IS_3DO) {
|
||||
screen._backBuffer1.SHblitFrom((*bigMap)[0], Common::Point(-_bigPos.x, -_bigPos.y));
|
||||
screen._backBuffer1.SHblitFrom((*bigMap)[1], Common::Point(-_bigPos.x, SHERLOCK_SCREEN_HEIGHT - _bigPos.y));
|
||||
screen._backBuffer1.SHblitFrom((*bigMap)[2], Common::Point(SHERLOCK_SCREEN_WIDTH - _bigPos.x, -_bigPos.y));
|
||||
screen._backBuffer1.SHblitFrom((*bigMap)[3], Common::Point(SHERLOCK_SCREEN_WIDTH - _bigPos.x, SHERLOCK_SCREEN_HEIGHT - _bigPos.y));
|
||||
} else {
|
||||
screen._backBuffer1.SHblitFrom((*bigMap)[0], Common::Point(-_bigPos.x, -_bigPos.y));
|
||||
}
|
||||
|
||||
showPlaces();
|
||||
_placesShown = false;
|
||||
|
||||
saveTopLine();
|
||||
_savedPos.x = -1;
|
||||
updateMap(true);
|
||||
} else if (!_drawMap) {
|
||||
if (!_placesShown) {
|
||||
showPlaces();
|
||||
_placesShown = true;
|
||||
}
|
||||
|
||||
if (_cursorIndex == 0) {
|
||||
Common::Point pt = events.mousePos();
|
||||
highlightIcon(Common::Point(pt.x - 4 + _bigPos.x, pt.y + _bigPos.y));
|
||||
}
|
||||
updateMap(false);
|
||||
}
|
||||
|
||||
if ((events._released || events._rightReleased) && _point != -1) {
|
||||
if (people[HOLMES]._walkCount == 0) {
|
||||
people[HOLMES]._walkDest = _points[_point] + Common::Point(4, 9);
|
||||
_charPoint = _point;
|
||||
|
||||
// Start walking to selected location
|
||||
walkTheStreets();
|
||||
|
||||
// Show wait cursor
|
||||
_cursorIndex = 1;
|
||||
events.setCursor((*_mapCursors)[_cursorIndex]._frame);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if a scene has beeen selected and we've finished "moving" to it
|
||||
if (people[HOLMES]._walkCount == 0) {
|
||||
if (_charPoint >= 1 && _charPoint < (int)_points.size())
|
||||
exitFlag = true;
|
||||
}
|
||||
|
||||
if (_drawMap) {
|
||||
_drawMap = false;
|
||||
|
||||
if (screen._fadeStyle)
|
||||
screen.randomTransition();
|
||||
else
|
||||
screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
|
||||
}
|
||||
|
||||
// Wait for a frame
|
||||
events.wait(1);
|
||||
}
|
||||
|
||||
keymapper->getKeymap("scalpel-map")->setEnabled(false);
|
||||
keymapper->getKeymap("sherlock-default")->setEnabled(true);
|
||||
keymapper->getKeymap("scalpel")->setEnabled(true);
|
||||
keymapper->getKeymap("scalpel-quit")->setEnabled(true);
|
||||
|
||||
freeSprites();
|
||||
_overPos = people[HOLMES]._position;
|
||||
|
||||
// Reset font
|
||||
screen.setFont(oldFont);
|
||||
|
||||
// Free map graphic
|
||||
delete bigMap;
|
||||
|
||||
_active = false;
|
||||
return _charPoint;
|
||||
}
|
||||
|
||||
void ScalpelMap::setupSprites() {
|
||||
Events &events = *_vm->_events;
|
||||
People &people = *_vm->_people;
|
||||
Scene &scene = *_vm->_scene;
|
||||
_savedPos.x = -1;
|
||||
|
||||
if (!IS_3DO) {
|
||||
// PC
|
||||
_mapCursors = new ImageFile("omouse.vgs");
|
||||
_shapes = new ImageFile("mapicon.vgs");
|
||||
_iconShapes = new ImageFile("overicon.vgs");
|
||||
} else {
|
||||
// 3DO
|
||||
_mapCursors = new ImageFile3DO("omouse.vgs", kImageFile3DOType_RoomFormat);
|
||||
_shapes = new ImageFile3DO("mapicon.vgs", kImageFile3DOType_RoomFormat);
|
||||
_iconShapes = new ImageFile3DO("overicon.vgs", kImageFile3DOType_RoomFormat);
|
||||
}
|
||||
|
||||
_cursorIndex = 0;
|
||||
events.setCursor((*_mapCursors)[_cursorIndex]._frame);
|
||||
|
||||
_iconSave.create((*_shapes)[4]._width, (*_shapes)[4]._height, g_system->getScreenFormat());
|
||||
Person &p = people[HOLMES];
|
||||
p._description = " ";
|
||||
p._type = CHARACTER;
|
||||
p._position = Common::Point(12400, 5000);
|
||||
p._sequenceNumber = 0;
|
||||
p._images = _shapes;
|
||||
p._imageFrame = nullptr;
|
||||
p._frameNumber = 0;
|
||||
p._delta = Common::Point(0, 0);
|
||||
p._oldSize = Common::Point(0, 0);
|
||||
p._oldSize = Common::Point(0, 0);
|
||||
p._misc = 0;
|
||||
p._walkCount = 0;
|
||||
p._allow = 0;
|
||||
p._noShapeSize = Common::Point(0, 0);
|
||||
p._goto = Common::Point(28000, 15000);
|
||||
p._status = 0;
|
||||
p._walkSequences = _walkSequences;
|
||||
p.setImageFrame();
|
||||
scene._bgShapes.clear();
|
||||
}
|
||||
|
||||
void ScalpelMap::freeSprites() {
|
||||
delete _mapCursors;
|
||||
delete _shapes;
|
||||
delete _iconShapes;
|
||||
}
|
||||
|
||||
void ScalpelMap::showPlaces() {
|
||||
Debugger &debugger = *_vm->_debugger;
|
||||
Screen &screen = *_vm->_screen;
|
||||
|
||||
for (uint idx = 0; idx < _points.size(); ++idx) {
|
||||
const MapEntry &pt = _points[idx];
|
||||
|
||||
if (pt.x != 0 && pt.y != 0) {
|
||||
if (debugger._showAllLocations != LOC_DISABLED)
|
||||
_vm->setFlagsDirect(idx);
|
||||
|
||||
if (pt.x >= _bigPos.x && (pt.x - _bigPos.x) < SHERLOCK_SCREEN_WIDTH
|
||||
&& pt.y >= _bigPos.y && (pt.y - _bigPos.y) < SHERLOCK_SCREEN_HEIGHT) {
|
||||
if (_vm->readFlags(idx)) {
|
||||
screen._backBuffer1.SHtransBlitFrom((*_iconShapes)[pt._translate],
|
||||
Common::Point(pt.x - _bigPos.x - 6, pt.y - _bigPos.y - 12));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (debugger._showAllLocations == LOC_REFRESH)
|
||||
debugger._showAllLocations = LOC_ALL;
|
||||
}
|
||||
|
||||
void ScalpelMap::saveTopLine() {
|
||||
_topLine.SHblitFrom(_vm->_screen->_backBuffer1, Common::Point(0, 0), Common::Rect(0, 0, SHERLOCK_SCREEN_WIDTH, 12));
|
||||
}
|
||||
|
||||
void ScalpelMap::eraseTopLine() {
|
||||
Screen &screen = *_vm->_screen;
|
||||
screen._backBuffer1.SHblitFrom(_topLine, Common::Point(0, 0));
|
||||
screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, _topLine.height());
|
||||
}
|
||||
|
||||
void ScalpelMap::showPlaceName(int idx, bool highlighted) {
|
||||
People &people = *_vm->_people;
|
||||
Screen &screen = *_vm->_screen;
|
||||
|
||||
Common::String name = _locationNames[idx];
|
||||
int width = screen.stringWidth(name);
|
||||
|
||||
if (!_cursorIndex) {
|
||||
restoreIcon();
|
||||
saveIcon(people[HOLMES]._imageFrame, _lDrawnPos);
|
||||
|
||||
bool flipped = people[HOLMES]._sequenceNumber == MAP_DOWNLEFT || people[HOLMES]._sequenceNumber == MAP_LEFT
|
||||
|| people[HOLMES]._sequenceNumber == MAP_UPLEFT;
|
||||
screen._backBuffer1.SHtransBlitFrom(*people[HOLMES]._imageFrame, _lDrawnPos, flipped);
|
||||
}
|
||||
|
||||
if (highlighted) {
|
||||
int xp = (SHERLOCK_SCREEN_WIDTH - screen.stringWidth(name)) / 2;
|
||||
screen.gPrint(Common::Point(xp + 2, 2), BLACK, "%s", name.c_str());
|
||||
screen.gPrint(Common::Point(xp + 1, 1), BLACK, "%s", name.c_str());
|
||||
screen.gPrint(Common::Point(xp, 0), 12, "%s", name.c_str());
|
||||
|
||||
screen.slamArea(xp, 0, width + 2, 15);
|
||||
}
|
||||
}
|
||||
|
||||
void ScalpelMap::updateMap(bool flushScreen) {
|
||||
Events &events = *_vm->_events;
|
||||
People &people = *_vm->_people;
|
||||
Screen &screen = *_vm->_screen;
|
||||
Common::Point osPos = _savedPos;
|
||||
Common::Point osSize = _savedSize;
|
||||
Common::Point hPos;
|
||||
|
||||
if (_cursorIndex >= 1) {
|
||||
if (++_cursorIndex > (1 + 8))
|
||||
_cursorIndex = 1;
|
||||
|
||||
events.setCursor((*_mapCursors)[(_cursorIndex + 1) / 2]._frame);
|
||||
}
|
||||
|
||||
if (!_drawMap && !flushScreen)
|
||||
restoreIcon();
|
||||
else
|
||||
_savedPos.x = -1;
|
||||
|
||||
people[HOLMES].adjustSprite();
|
||||
|
||||
_lDrawnPos.x = hPos.x = people[HOLMES]._position.x / FIXED_INT_MULTIPLIER - _bigPos.x;
|
||||
_lDrawnPos.y = hPos.y = people[HOLMES]._position.y / FIXED_INT_MULTIPLIER - people[HOLMES].frameHeight() - _bigPos.y;
|
||||
|
||||
// Draw the person icon
|
||||
saveIcon(people[HOLMES]._imageFrame, hPos);
|
||||
if (people[HOLMES]._sequenceNumber == MAP_DOWNLEFT || people[HOLMES]._sequenceNumber == MAP_LEFT
|
||||
|| people[HOLMES]._sequenceNumber == MAP_UPLEFT)
|
||||
screen._backBuffer1.SHtransBlitFrom(*people[HOLMES]._imageFrame, hPos, true);
|
||||
else
|
||||
screen._backBuffer1.SHtransBlitFrom(*people[HOLMES]._imageFrame, hPos, false);
|
||||
|
||||
if (flushScreen) {
|
||||
screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
|
||||
} else if (!_drawMap) {
|
||||
if (hPos.x > 0 && hPos.y >= 0 && hPos.x < SHERLOCK_SCREEN_WIDTH && hPos.y < SHERLOCK_SCREEN_HEIGHT)
|
||||
screen.flushImage(people[HOLMES]._imageFrame, Common::Point(people[HOLMES]._position.x / FIXED_INT_MULTIPLIER - _bigPos.x,
|
||||
people[HOLMES]._position.y / FIXED_INT_MULTIPLIER - people[HOLMES].frameHeight() - _bigPos.y),
|
||||
&people[HOLMES]._oldPosition.x, &people[HOLMES]._oldPosition.y, &people[HOLMES]._oldSize.x, &people[HOLMES]._oldSize.y);
|
||||
|
||||
if (osPos.x != -1)
|
||||
screen.slamArea(osPos.x, osPos.y, osSize.x, osSize.y);
|
||||
}
|
||||
}
|
||||
|
||||
void ScalpelMap::walkTheStreets() {
|
||||
People &people = *_vm->_people;
|
||||
Common::Array<Common::Point> tempPath;
|
||||
|
||||
// Get indexes into the path lists for the start and destination scenes
|
||||
int start = _points[_oldCharPoint]._translate;
|
||||
int dest = _points[_charPoint]._translate;
|
||||
|
||||
// Get pointer to start of path
|
||||
const byte *path = _paths.getPath(start, dest);
|
||||
|
||||
// Add in destination position
|
||||
people[HOLMES]._walkTo.clear();
|
||||
Common::Point destPos = people[HOLMES]._walkDest;
|
||||
|
||||
// Check for any intermediate points between the two locations
|
||||
if (path[0] || _charPoint > 50 || _oldCharPoint > 50) {
|
||||
people[HOLMES]._sequenceNumber = -1;
|
||||
|
||||
if (_charPoint == 51 || _oldCharPoint == 51) {
|
||||
people[HOLMES].setWalking();
|
||||
} else {
|
||||
bool reversePath = false;
|
||||
|
||||
// Check for moving the path backwards or forwards
|
||||
if (path[0] == 255) {
|
||||
reversePath = true;
|
||||
SWAP(start, dest);
|
||||
path = _paths.getPath(start, dest);
|
||||
}
|
||||
|
||||
do {
|
||||
int idx = *path++;
|
||||
tempPath.push_back(_pathPoints[idx - 1] + Common::Point(4, 4));
|
||||
} while (*path != 254);
|
||||
|
||||
// Load up the path to use
|
||||
people[HOLMES]._walkTo.clear();
|
||||
|
||||
if (reversePath) {
|
||||
for (int idx = (int)tempPath.size() - 1; idx >= 0; --idx)
|
||||
people[HOLMES]._walkTo.push(tempPath[idx]);
|
||||
} else {
|
||||
for (int idx = 0; idx < (int)tempPath.size(); ++idx)
|
||||
people[HOLMES]._walkTo.push(tempPath[idx]);
|
||||
}
|
||||
|
||||
people[HOLMES]._walkDest = people[HOLMES]._walkTo.pop() + Common::Point(12, 6);
|
||||
people[HOLMES].setWalking();
|
||||
}
|
||||
} else {
|
||||
people[HOLMES]._walkCount = 0;
|
||||
}
|
||||
|
||||
// Store the final destination icon position
|
||||
people[HOLMES]._walkTo.push(destPos);
|
||||
}
|
||||
|
||||
void ScalpelMap::saveIcon(ImageFrame *src, const Common::Point &pt) {
|
||||
Screen &screen = *_vm->_screen;
|
||||
Common::Point size(src->_width, src->_height);
|
||||
Common::Point pos = pt;
|
||||
|
||||
if (pos.x < 0) {
|
||||
size.x += pos.x;
|
||||
pos.x = 0;
|
||||
}
|
||||
|
||||
if (pos.y < 0) {
|
||||
size.y += pos.y;
|
||||
pos.y = 0;
|
||||
}
|
||||
|
||||
if ((pos.x + size.x) > SHERLOCK_SCREEN_WIDTH)
|
||||
size.x -= (pos.x + size.x) - SHERLOCK_SCREEN_WIDTH;
|
||||
|
||||
if ((pos.y + size.y) > SHERLOCK_SCREEN_HEIGHT)
|
||||
size.y -= (pos.y + size.y) - SHERLOCK_SCREEN_HEIGHT;
|
||||
|
||||
if (size.x < 1 || size.y < 1 || pos.x >= SHERLOCK_SCREEN_WIDTH || pos.y >= SHERLOCK_SCREEN_HEIGHT || _drawMap) {
|
||||
// Flag as the area not needing to be saved
|
||||
_savedPos.x = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
assert(size.x <= _iconSave.width() && size.y <= _iconSave.height());
|
||||
_iconSave.SHblitFrom(screen._backBuffer1, Common::Point(0, 0),
|
||||
Common::Rect(pos.x, pos.y, pos.x + size.x, pos.y + size.y));
|
||||
_savedPos = pos;
|
||||
_savedSize = size;
|
||||
}
|
||||
|
||||
void ScalpelMap::restoreIcon() {
|
||||
Screen &screen = *_vm->_screen;
|
||||
|
||||
if (_savedPos.x >= 0 && _savedPos.y >= 0 && _savedPos.x <= SHERLOCK_SCREEN_WIDTH
|
||||
&& _savedPos.y < SHERLOCK_SCREEN_HEIGHT)
|
||||
screen._backBuffer1.SHblitFrom(_iconSave, _savedPos, Common::Rect(0, 0, _savedSize.x, _savedSize.y));
|
||||
}
|
||||
|
||||
void ScalpelMap::highlightIcon(const Common::Point &pt) {
|
||||
int oldPoint = _point;
|
||||
|
||||
// Iterate through the icon list
|
||||
bool done = false;
|
||||
for (int idx = 0; idx < (int)_points.size(); ++idx) {
|
||||
const MapEntry &entry = _points[idx];
|
||||
|
||||
// Check whether the mouse is over a given icon
|
||||
if (entry.x != 0 && entry.y != 0) {
|
||||
if (Common::Rect(entry.x - 8, entry.y - 8, entry.x + 9, entry.y + 9).contains(pt)) {
|
||||
done = true;
|
||||
|
||||
if (_point != idx && _vm->readFlags(idx)) {
|
||||
// Changed to a new valid (visible) location
|
||||
eraseTopLine();
|
||||
showPlaceName(idx, true);
|
||||
_point = idx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!done) {
|
||||
// No icon was highlighted
|
||||
if (_point != -1) {
|
||||
// No longer highlighting previously highlighted icon, so erase it
|
||||
showPlaceName(_point, false);
|
||||
eraseTopLine();
|
||||
}
|
||||
|
||||
_point = -1;
|
||||
} else if (oldPoint != -1 && oldPoint != _point) {
|
||||
showPlaceName(oldPoint, false);
|
||||
eraseTopLine();
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Scalpel
|
||||
|
||||
} // End of namespace Sherlock
|
||||
172
engines/sherlock/scalpel/scalpel_map.h
Normal file
172
engines/sherlock/scalpel/scalpel_map.h
Normal file
@@ -0,0 +1,172 @@
|
||||
/* 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 SHERLOCK_SCALPEL_MAP_H
|
||||
#define SHERLOCK_SCALPEL_MAP_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/array.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/str-array.h"
|
||||
#include "sherlock/surface.h"
|
||||
#include "sherlock/map.h"
|
||||
#include "sherlock/resources.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
class SherlockEngine;
|
||||
|
||||
namespace Scalpel {
|
||||
|
||||
|
||||
struct MapEntry : Common::Point {
|
||||
int _translate;
|
||||
|
||||
MapEntry() : Common::Point(), _translate(-1) {}
|
||||
|
||||
MapEntry(int posX, int posY, int translate) : Common::Point(posX, posY), _translate(translate) {}
|
||||
};
|
||||
|
||||
class MapPaths {
|
||||
private:
|
||||
int _numLocations;
|
||||
Common::Array< Common::Array<byte> > _paths;
|
||||
|
||||
public:
|
||||
MapPaths();
|
||||
|
||||
/**
|
||||
* Load the data for the paths between locations on the map
|
||||
*/
|
||||
void load(int numLocations, Common::SeekableReadStream &s);
|
||||
|
||||
/**
|
||||
* Get the path between two locations on the map
|
||||
*/
|
||||
const byte *getPath(int srcLocation, int destLocation);
|
||||
};
|
||||
|
||||
class ScalpelMap: public Map {
|
||||
private:
|
||||
Common::Array<MapEntry> _points; // Map locations for each scene
|
||||
Common::StringArray _locationNames;
|
||||
MapPaths _paths;
|
||||
Common::Array<Common::Point> _pathPoints;
|
||||
Common::Point _savedPos;
|
||||
Common::Point _savedSize;
|
||||
Surface _topLine;
|
||||
ImageFile *_mapCursors;
|
||||
ImageFile *_shapes;
|
||||
ImageFile *_iconShapes;
|
||||
WalkSequences _walkSequences;
|
||||
Point32 _lDrawnPos;
|
||||
int _point;
|
||||
bool _placesShown;
|
||||
int _cursorIndex;
|
||||
bool _drawMap;
|
||||
Surface _iconSave;
|
||||
protected:
|
||||
/**
|
||||
* Load data needed for the map
|
||||
*/
|
||||
void loadData();
|
||||
|
||||
/**
|
||||
* Load and initialize all the sprites that are needed for the map display
|
||||
*/
|
||||
void setupSprites();
|
||||
|
||||
/**
|
||||
* Free the sprites and data used by the map
|
||||
*/
|
||||
void freeSprites();
|
||||
|
||||
/**
|
||||
* Draws an icon for every place that's currently known
|
||||
*/
|
||||
void showPlaces();
|
||||
|
||||
/**
|
||||
* Makes a copy of the top rows of the screen that are used to display location names
|
||||
*/
|
||||
void saveTopLine();
|
||||
|
||||
/**
|
||||
* Erases anything shown in the top line by restoring the previously saved original map background
|
||||
*/
|
||||
void eraseTopLine();
|
||||
|
||||
/**
|
||||
* Prints the name of the specified icon
|
||||
*/
|
||||
void showPlaceName(int idx, bool highlighted);
|
||||
|
||||
/**
|
||||
* Update all on-screen sprites to account for any scrolling of the map
|
||||
*/
|
||||
void updateMap(bool flushScreen);
|
||||
|
||||
/**
|
||||
* Handle moving icon for player from their previous location on the map to a destination location
|
||||
*/
|
||||
void walkTheStreets();
|
||||
|
||||
/**
|
||||
* Save the area under the player's icon
|
||||
*/
|
||||
void saveIcon(ImageFrame *src, const Common::Point &pt);
|
||||
|
||||
/**
|
||||
* Restore the area under the player's icon
|
||||
*/
|
||||
void restoreIcon();
|
||||
|
||||
/**
|
||||
* Handles highlighting map icons, showing their names
|
||||
*/
|
||||
void highlightIcon(const Common::Point &pt);
|
||||
public:
|
||||
ScalpelMap(SherlockEngine *vm);
|
||||
~ScalpelMap() override {}
|
||||
|
||||
const MapEntry &operator[](int idx) { return _points[idx]; }
|
||||
|
||||
/**
|
||||
* Loads the list of points for locations on the map for each scene
|
||||
*/
|
||||
void loadPoints(int count, const int *xList, const int *yList, const int *transList);
|
||||
|
||||
/**
|
||||
* Load the sequence data for player icon animations
|
||||
*/
|
||||
void loadSequences(int count, const byte *seq);
|
||||
|
||||
/**
|
||||
* Show the map
|
||||
*/
|
||||
int show() override;
|
||||
};
|
||||
|
||||
} // End of namespace Scalpel
|
||||
|
||||
} // End of namespace Sherlock
|
||||
|
||||
#endif
|
||||
570
engines/sherlock/scalpel/scalpel_people.cpp
Normal file
570
engines/sherlock/scalpel/scalpel_people.cpp
Normal file
@@ -0,0 +1,570 @@
|
||||
/* 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 "sherlock/scalpel/scalpel_people.h"
|
||||
#include "sherlock/scalpel/scalpel_map.h"
|
||||
#include "sherlock/sherlock.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
namespace Scalpel {
|
||||
|
||||
// Walk speeds
|
||||
#define MWALK_SPEED 2
|
||||
#define XWALK_SPEED 4
|
||||
#define YWALK_SPEED 1
|
||||
|
||||
/*----------------------------------------------------------------*/
|
||||
|
||||
void ScalpelPerson::adjustSprite() {
|
||||
Map &map = *_vm->_map;
|
||||
People &people = *_vm->_people;
|
||||
Scene &scene = *_vm->_scene;
|
||||
Talk &talk = *_vm->_talk;
|
||||
|
||||
if (_type == INVALID || (_type == CHARACTER && scene._animating))
|
||||
return;
|
||||
|
||||
if (!talk._talkCounter && _type == CHARACTER && _walkCount) {
|
||||
// Handle active movement for the sprite
|
||||
_position += _delta;
|
||||
--_walkCount;
|
||||
|
||||
if (!_walkCount) {
|
||||
// If there any points left for the character to walk to along the
|
||||
// route to a destination, then move to the next point
|
||||
if (!people[HOLMES]._walkTo.empty()) {
|
||||
_walkDest = people[HOLMES]._walkTo.pop();
|
||||
setWalking();
|
||||
} else {
|
||||
gotoStand();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_type == CHARACTER && !map._active) {
|
||||
if ((_position.y / FIXED_INT_MULTIPLIER) > LOWER_LIMIT) {
|
||||
_position.y = LOWER_LIMIT * FIXED_INT_MULTIPLIER;
|
||||
gotoStand();
|
||||
}
|
||||
|
||||
if ((_position.y / FIXED_INT_MULTIPLIER) < UPPER_LIMIT) {
|
||||
_position.y = UPPER_LIMIT * FIXED_INT_MULTIPLIER;
|
||||
gotoStand();
|
||||
}
|
||||
|
||||
if ((_position.x / FIXED_INT_MULTIPLIER) < LEFT_LIMIT) {
|
||||
_position.x = LEFT_LIMIT * FIXED_INT_MULTIPLIER;
|
||||
gotoStand();
|
||||
}
|
||||
|
||||
if ((_position.x / FIXED_INT_MULTIPLIER) > RIGHT_LIMIT) {
|
||||
_position.x = RIGHT_LIMIT * FIXED_INT_MULTIPLIER;
|
||||
gotoStand();
|
||||
}
|
||||
} else if (!map._active) {
|
||||
_position.y = CLIP((int)_position.y, (int)UPPER_LIMIT, (int)LOWER_LIMIT);
|
||||
_position.x = CLIP((int)_position.x, (int)LEFT_LIMIT, (int)RIGHT_LIMIT);
|
||||
}
|
||||
|
||||
if (!map._active || (map._frameChangeFlag = !map._frameChangeFlag))
|
||||
++_frameNumber;
|
||||
|
||||
if (_frameNumber >= (int)_walkSequences[_sequenceNumber]._sequences.size() ||
|
||||
_walkSequences[_sequenceNumber][_frameNumber] == 0) {
|
||||
switch (_sequenceNumber) {
|
||||
case STOP_UP:
|
||||
case STOP_DOWN:
|
||||
case STOP_LEFT:
|
||||
case STOP_RIGHT:
|
||||
case STOP_UPRIGHT:
|
||||
case STOP_UPLEFT:
|
||||
case STOP_DOWNRIGHT:
|
||||
case STOP_DOWNLEFT:
|
||||
// We're in a stop sequence, so reset back to the last frame, so
|
||||
// the character is shown as standing still
|
||||
--_frameNumber;
|
||||
break;
|
||||
|
||||
default:
|
||||
// Move 1 past the first frame - we need to compensate, since we
|
||||
// already passed the frame increment
|
||||
_frameNumber = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the _imageFrame to point to the new frame's image
|
||||
setImageFrame();
|
||||
|
||||
// Check to see if character has entered an exit zone
|
||||
if (!_walkCount && scene._walkedInScene && scene._goToScene == -1) {
|
||||
Common::Rect charRect(_position.x / FIXED_INT_MULTIPLIER - 5, _position.y / FIXED_INT_MULTIPLIER - 2,
|
||||
_position.x / FIXED_INT_MULTIPLIER + 5, _position.y / FIXED_INT_MULTIPLIER + 2);
|
||||
Exit *exit = scene.checkForExit(charRect);
|
||||
|
||||
if (exit) {
|
||||
scene._goToScene = exit->_scene;
|
||||
|
||||
if (exit->_newPosition.x != 0) {
|
||||
people._savedPos = exit->_newPosition;
|
||||
|
||||
if (people._savedPos._facing > 100 && people._savedPos.x < 1)
|
||||
people._savedPos.x = 100;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScalpelPerson::gotoStand() {
|
||||
ScalpelMap &map = *(ScalpelMap *)_vm->_map;
|
||||
People &people = *_vm->_people;
|
||||
_walkTo.clear();
|
||||
_walkCount = 0;
|
||||
|
||||
switch (_sequenceNumber) {
|
||||
case Scalpel::WALK_UP:
|
||||
_sequenceNumber = STOP_UP;
|
||||
break;
|
||||
case WALK_DOWN:
|
||||
_sequenceNumber = STOP_DOWN;
|
||||
break;
|
||||
case TALK_LEFT:
|
||||
case WALK_LEFT:
|
||||
_sequenceNumber = STOP_LEFT;
|
||||
break;
|
||||
case TALK_RIGHT:
|
||||
case WALK_RIGHT:
|
||||
_sequenceNumber = STOP_RIGHT;
|
||||
break;
|
||||
case WALK_UPRIGHT:
|
||||
_sequenceNumber = STOP_UPRIGHT;
|
||||
break;
|
||||
case WALK_UPLEFT:
|
||||
_sequenceNumber = STOP_UPLEFT;
|
||||
break;
|
||||
case WALK_DOWNRIGHT:
|
||||
_sequenceNumber = STOP_DOWNRIGHT;
|
||||
break;
|
||||
case WALK_DOWNLEFT:
|
||||
_sequenceNumber = STOP_DOWNLEFT;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Only restart frame at 0 if the sequence number has changed
|
||||
if (_oldWalkSequence != -1 || _sequenceNumber == Scalpel::STOP_UP)
|
||||
_frameNumber = 0;
|
||||
|
||||
if (map._active) {
|
||||
_sequenceNumber = 0;
|
||||
people[HOLMES]._position.x = (map[map._charPoint].x - 6) * FIXED_INT_MULTIPLIER;
|
||||
people[HOLMES]._position.y = (map[map._charPoint].y + 10) * FIXED_INT_MULTIPLIER;
|
||||
}
|
||||
|
||||
_oldWalkSequence = -1;
|
||||
people._allowWalkAbort = true;
|
||||
}
|
||||
|
||||
void ScalpelPerson::setWalking() {
|
||||
Map &map = *_vm->_map;
|
||||
Scene &scene = *_vm->_scene;
|
||||
int oldDirection, oldFrame;
|
||||
Common::Point speed, delta;
|
||||
|
||||
// Flag that player has now walked in the scene
|
||||
scene._walkedInScene = true;
|
||||
|
||||
// Stop any previous walking, since a new dest is being set
|
||||
_walkCount = 0;
|
||||
oldDirection = _sequenceNumber;
|
||||
oldFrame = _frameNumber;
|
||||
|
||||
// Set speed to use horizontal and vertical movement
|
||||
if (map._active) {
|
||||
speed = Common::Point(MWALK_SPEED, MWALK_SPEED);
|
||||
} else {
|
||||
speed = Common::Point(XWALK_SPEED, YWALK_SPEED);
|
||||
}
|
||||
|
||||
// If the player is already close to the given destination that no
|
||||
// walking is needed, move to the next straight line segment in the
|
||||
// overall walking route, if there is one
|
||||
for (;;) {
|
||||
// Since we want the player to be centered on the destination they
|
||||
// clicked, but characters draw positions start at their left, move
|
||||
// the destination half the character width to draw him centered
|
||||
int temp;
|
||||
if (_walkDest.x >= (temp = _imageFrame->_frame.w / 2))
|
||||
_walkDest.x -= temp;
|
||||
|
||||
delta = Common::Point(
|
||||
ABS(_position.x / FIXED_INT_MULTIPLIER - _walkDest.x),
|
||||
ABS(_position.y / FIXED_INT_MULTIPLIER - _walkDest.y)
|
||||
);
|
||||
|
||||
// If we're ready to move a sufficient distance, that's it. Otherwise,
|
||||
// move onto the next portion of the walk path, if there is one
|
||||
if ((delta.x > 3 || delta.y > 0) || _walkTo.empty())
|
||||
break;
|
||||
|
||||
// Pop next walk segment off the walk route stack
|
||||
_walkDest = _walkTo.pop();
|
||||
}
|
||||
|
||||
// If a sufficient move is being done, then start the move
|
||||
if (delta.x > 3 || delta.y) {
|
||||
// See whether the major movement is horizontal or vertical
|
||||
if (delta.x >= delta.y) {
|
||||
// Set the initial frame sequence for the left and right, as well
|
||||
// as setting the delta x depending on direction
|
||||
if (_walkDest.x < (_position.x / FIXED_INT_MULTIPLIER)) {
|
||||
_sequenceNumber = (map._active ? (int)MAP_LEFT : (int)WALK_LEFT);
|
||||
_delta.x = speed.x * -FIXED_INT_MULTIPLIER;
|
||||
} else {
|
||||
_sequenceNumber = (map._active ? (int)MAP_RIGHT : (int)WALK_RIGHT);
|
||||
_delta.x = speed.x * FIXED_INT_MULTIPLIER;
|
||||
}
|
||||
|
||||
// See if the x delta is too small to be divided by the speed, since
|
||||
// this would cause a divide by zero error
|
||||
if (delta.x >= speed.x) {
|
||||
// Det the delta y
|
||||
_delta.y = (delta.y * FIXED_INT_MULTIPLIER) / (delta.x / speed.x);
|
||||
if (_walkDest.y < (_position.y / FIXED_INT_MULTIPLIER))
|
||||
_delta.y = -_delta.y;
|
||||
|
||||
// Set how many times we should add the delta to the player's position
|
||||
_walkCount = delta.x / speed.x;
|
||||
} else {
|
||||
// The delta x was less than the speed (ie. we're really close to
|
||||
// the destination). So set delta to 0 so the player won't move
|
||||
_delta = Point32(0, 0);
|
||||
_position = Point32(_walkDest.x * FIXED_INT_MULTIPLIER, _walkDest.y * FIXED_INT_MULTIPLIER);
|
||||
|
||||
_walkCount = 1;
|
||||
}
|
||||
|
||||
// See if the sequence needs to be changed for diagonal walking
|
||||
if (_delta.y > 150) {
|
||||
if (!map._active) {
|
||||
switch (_sequenceNumber) {
|
||||
case WALK_LEFT:
|
||||
_sequenceNumber = WALK_DOWNLEFT;
|
||||
break;
|
||||
case WALK_RIGHT:
|
||||
_sequenceNumber = WALK_DOWNRIGHT;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (_delta.y < -150) {
|
||||
if (!map._active) {
|
||||
switch (_sequenceNumber) {
|
||||
case WALK_LEFT:
|
||||
_sequenceNumber = WALK_UPLEFT;
|
||||
break;
|
||||
case WALK_RIGHT:
|
||||
_sequenceNumber = WALK_UPRIGHT;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Major movement is vertical, so set the sequence for up and down,
|
||||
// and set the delta Y depending on the direction
|
||||
if (_walkDest.y < (_position.y / FIXED_INT_MULTIPLIER)) {
|
||||
_sequenceNumber = WALK_UP;
|
||||
_delta.y = speed.y * -FIXED_INT_MULTIPLIER;
|
||||
} else {
|
||||
_sequenceNumber = WALK_DOWN;
|
||||
_delta.y = speed.y * FIXED_INT_MULTIPLIER;
|
||||
}
|
||||
|
||||
// If we're on the overhead map, set the sequence so we keep moving
|
||||
// in the same direction
|
||||
if (map._active)
|
||||
_sequenceNumber = (oldDirection == -1) ? MAP_RIGHT : oldDirection;
|
||||
|
||||
// Set the delta x
|
||||
_delta.x = (delta.x * FIXED_INT_MULTIPLIER) / (delta.y / speed.y);
|
||||
if (_walkDest.x < (_position.x / FIXED_INT_MULTIPLIER))
|
||||
_delta.x = -_delta.x;
|
||||
|
||||
_walkCount = delta.y / speed.y;
|
||||
}
|
||||
}
|
||||
|
||||
// See if the new walk sequence is the same as the old. If it's a new one,
|
||||
// we need to reset the frame number to zero so its animation starts at
|
||||
// its beginning. Otherwise, if it's the same sequence, we can leave it
|
||||
// as is, so it keeps the animation going at wherever it was up to
|
||||
if (_sequenceNumber != _oldWalkSequence)
|
||||
_frameNumber = 0;
|
||||
_oldWalkSequence = _sequenceNumber;
|
||||
|
||||
if (!_walkCount)
|
||||
gotoStand();
|
||||
|
||||
// If the sequence is the same as when we started, then Holmes was
|
||||
// standing still and we're trying to re-stand him, so reset Holmes'
|
||||
// rame to the old frame number from before it was reset to 0
|
||||
if (_sequenceNumber == oldDirection)
|
||||
_frameNumber = oldFrame;
|
||||
}
|
||||
|
||||
void ScalpelPerson::walkToCoords(const Point32 &destPos, int destDir) {
|
||||
Events &events = *_vm->_events;
|
||||
People &people = *_vm->_people;
|
||||
Scene &scene = *_vm->_scene;
|
||||
Talk &talk = *_vm->_talk;
|
||||
|
||||
CursorId oldCursor = events.getCursor();
|
||||
events.setCursor(WAIT);
|
||||
|
||||
_walkDest = Common::Point(destPos.x / FIXED_INT_MULTIPLIER + 10, destPos.y / FIXED_INT_MULTIPLIER);
|
||||
people._allowWalkAbort = true;
|
||||
goAllTheWay();
|
||||
|
||||
// Keep calling doBgAnim until the walk is done
|
||||
do {
|
||||
events.pollEventsAndWait();
|
||||
scene.doBgAnim();
|
||||
} while (!_vm->shouldQuit() && _walkCount);
|
||||
|
||||
if (!talk._talkToAbort) {
|
||||
// Put character exactly on destination position, and set direction
|
||||
_position = destPos;
|
||||
_sequenceNumber = destDir;
|
||||
gotoStand();
|
||||
|
||||
// Draw Holmes facing the new direction
|
||||
scene.doBgAnim();
|
||||
|
||||
if (!talk._talkToAbort)
|
||||
events.setCursor(oldCursor);
|
||||
}
|
||||
}
|
||||
|
||||
Common::Point ScalpelPerson::getSourcePoint() const {
|
||||
return Common::Point(_position.x / FIXED_INT_MULTIPLIER + frameWidth() / 2,
|
||||
_position.y / FIXED_INT_MULTIPLIER);
|
||||
}
|
||||
|
||||
void ScalpelPerson::synchronize(Serializer &s) {
|
||||
if (_walkCount)
|
||||
gotoStand();
|
||||
|
||||
s.syncAsSint32LE(_position.x);
|
||||
s.syncAsSint32LE(_position.y);
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------*/
|
||||
|
||||
ScalpelPeople::ScalpelPeople(SherlockEngine *vm) : People(vm) {
|
||||
_data.push_back(new ScalpelPerson());
|
||||
}
|
||||
|
||||
void ScalpelPeople::setTalking(int speaker) {
|
||||
Resources &res = *_vm->_res;
|
||||
|
||||
// If no speaker is specified, then we can exit immediately
|
||||
if (speaker == -1)
|
||||
return;
|
||||
|
||||
if (_portraitsOn) {
|
||||
delete _talkPics;
|
||||
Common::Path filename(Common::String::format("%s.vgs", _characters[speaker]._portrait));
|
||||
_talkPics = new ImageFile(filename);
|
||||
|
||||
// Load portrait sequences
|
||||
Common::SeekableReadStream *stream = res.load("sequence.txt");
|
||||
stream->seek(speaker * MAX_FRAME);
|
||||
|
||||
int idx = 0;
|
||||
do {
|
||||
_portrait._sequences[idx] = stream->readByte();
|
||||
++idx;
|
||||
} while (idx < 2 || _portrait._sequences[idx - 2] || _portrait._sequences[idx - 1]);
|
||||
|
||||
delete stream;
|
||||
|
||||
_portrait._maxFrames = idx;
|
||||
_portrait._frameNumber = 0;
|
||||
_portrait._sequenceNumber = 0;
|
||||
_portrait._images = _talkPics;
|
||||
_portrait._imageFrame = &(*_talkPics)[0];
|
||||
_portrait._position = Common::Point(_portraitSide, 10);
|
||||
_portrait._delta = Common::Point(0, 0);
|
||||
_portrait._oldPosition = Common::Point(0, 0);
|
||||
_portrait._goto = Common::Point(0, 0);
|
||||
_portrait._flags = 5;
|
||||
_portrait._status = 0;
|
||||
_portrait._misc = 0;
|
||||
_portrait._allow = 0;
|
||||
_portrait._type = ACTIVE_BG_SHAPE;
|
||||
_portrait._name = " ";
|
||||
_portrait._description = " ";
|
||||
_portrait._examine = " ";
|
||||
_portrait._walkCount = 0;
|
||||
|
||||
if (_holmesFlip || _speakerFlip) {
|
||||
_portrait._flags |= 2;
|
||||
|
||||
_holmesFlip = false;
|
||||
_speakerFlip = false;
|
||||
}
|
||||
|
||||
if (_portraitSide == 20)
|
||||
_portraitSide = 220;
|
||||
else
|
||||
_portraitSide = 20;
|
||||
|
||||
_portraitLoaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
void ScalpelPeople::synchronize(Serializer &s) {
|
||||
(*this)[HOLMES].synchronize(s);
|
||||
s.syncAsSint16LE(_holmesQuotient);
|
||||
s.syncAsByte(_holmesOn);
|
||||
|
||||
if (s.isLoading()) {
|
||||
_savedPos = _data[HOLMES]->_position;
|
||||
_savedPos._facing = _data[HOLMES]->_sequenceNumber;
|
||||
}
|
||||
}
|
||||
|
||||
void ScalpelPeople::setTalkSequence(int speaker, int sequenceNum) {
|
||||
People &people = *_vm->_people;
|
||||
Scene &scene = *_vm->_scene;
|
||||
|
||||
// If no speaker is specified, then nothing needs to be done
|
||||
if (speaker == -1)
|
||||
return;
|
||||
|
||||
if (speaker) {
|
||||
int objNum = people.findSpeaker(speaker);
|
||||
if (objNum != -1) {
|
||||
Object &obj = scene._bgShapes[objNum];
|
||||
|
||||
if (obj._seqSize < MAX_TALK_SEQUENCES) {
|
||||
warning("Tried to copy too many talk frames");
|
||||
} else {
|
||||
for (int idx = 0; idx < MAX_TALK_SEQUENCES; ++idx) {
|
||||
obj._sequences[idx] = people._characters[speaker]._talkSequences[idx];
|
||||
if (idx > 0 && !obj._sequences[idx] && !obj._sequences[idx - 1])
|
||||
return;
|
||||
|
||||
obj._frameNumber = 0;
|
||||
obj._sequenceNumber = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ScalpelPeople::loadWalk() {
|
||||
bool result = false;
|
||||
|
||||
if (_data[HOLMES]->_walkLoaded) {
|
||||
return false;
|
||||
} else {
|
||||
if (!IS_3DO) {
|
||||
_data[HOLMES]->_images = new ImageFile("walk.vgs");
|
||||
} else {
|
||||
// Load walk.anim on 3DO, which is a cel animation file
|
||||
_data[HOLMES]->_images = new ImageFile3DO("walk.anim", kImageFile3DOType_CelAnimation);
|
||||
}
|
||||
_data[HOLMES]->setImageFrame();
|
||||
_data[HOLMES]->_walkLoaded = true;
|
||||
|
||||
result = true;
|
||||
}
|
||||
|
||||
_forceWalkReload = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
const Common::Point ScalpelPeople::restrictToZone(int zoneId, const Common::Point &destPos) {
|
||||
Scene &scene = *_vm->_scene;
|
||||
Common::Point walkDest = destPos;
|
||||
|
||||
// The destination isn't in a zone
|
||||
if (walkDest.x >= (SHERLOCK_SCREEN_WIDTH - 1))
|
||||
walkDest.x = SHERLOCK_SCREEN_WIDTH - 2;
|
||||
|
||||
// Trace a line between the centroid of the found closest zone to
|
||||
// the destination, to find the point at which the zone will be left
|
||||
const Common::Rect &destRect = scene._zones[zoneId];
|
||||
const Common::Point destCenter((destRect.left + destRect.right) / 2,
|
||||
(destRect.top + destRect.bottom) / 2);
|
||||
const Common::Point delta = walkDest - destCenter;
|
||||
Point32 pt(destCenter.x * FIXED_INT_MULTIPLIER, destCenter.y * FIXED_INT_MULTIPLIER);
|
||||
|
||||
// Move along the line until the zone is left
|
||||
do {
|
||||
pt += delta;
|
||||
} while (destRect.contains(pt.x / FIXED_INT_MULTIPLIER, pt.y / FIXED_INT_MULTIPLIER));
|
||||
|
||||
// Set the new walk destination to the last point that was in the
|
||||
// zone just before it was left
|
||||
return Common::Point((pt.x - delta.x * 2) / FIXED_INT_MULTIPLIER,
|
||||
(pt.y - delta.y * 2) / FIXED_INT_MULTIPLIER);
|
||||
}
|
||||
|
||||
void ScalpelPeople::setListenSequence(int speaker, int sequenceNum) {
|
||||
People &people = *_vm->_people;
|
||||
Scene &scene = *_vm->_scene;
|
||||
|
||||
// Don't bother doing anything if no specific speaker is specified
|
||||
if (speaker == -1)
|
||||
return;
|
||||
|
||||
if (speaker) {
|
||||
int objNum = people.findSpeaker(speaker);
|
||||
if (objNum != -1) {
|
||||
Object &obj = scene._bgShapes[objNum];
|
||||
|
||||
if (obj._seqSize < MAX_TALK_SEQUENCES) {
|
||||
warning("Tried to copy too few still frames");
|
||||
} else {
|
||||
for (uint idx = 0; idx < MAX_TALK_SEQUENCES; ++idx) {
|
||||
obj._sequences[idx] = people._characters[speaker]._stillSequences[idx];
|
||||
if (idx > 0 && !people._characters[speaker]._talkSequences[idx] &&
|
||||
!people._characters[speaker]._talkSequences[idx - 1])
|
||||
break;
|
||||
}
|
||||
|
||||
obj._frameNumber = 0;
|
||||
obj._seqTo = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Scalpel
|
||||
|
||||
} // End of namespace Sherlock
|
||||
129
engines/sherlock/scalpel/scalpel_people.h
Normal file
129
engines/sherlock/scalpel/scalpel_people.h
Normal file
@@ -0,0 +1,129 @@
|
||||
/* 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 SHERLOCK_SCALPEL_PEOPLE_H
|
||||
#define SHERLOCK_SCALPEL_PEOPLE_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "sherlock/people.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
class SherlockEngine;
|
||||
|
||||
namespace Scalpel {
|
||||
|
||||
// Animation sequence identifiers for characters
|
||||
enum ScalpelSequences {
|
||||
WALK_RIGHT = 0, WALK_DOWN = 1, WALK_LEFT = 2, WALK_UP = 3, STOP_LEFT = 4,
|
||||
STOP_DOWN = 5, STOP_RIGHT = 6, STOP_UP = 7, WALK_UPRIGHT = 8,
|
||||
WALK_DOWNRIGHT = 9, WALK_UPLEFT = 10, WALK_DOWNLEFT = 11,
|
||||
STOP_UPRIGHT = 12, STOP_UPLEFT = 13, STOP_DOWNRIGHT = 14,
|
||||
STOP_DOWNLEFT = 15, TALK_RIGHT = 6, TALK_LEFT = 4
|
||||
};
|
||||
|
||||
class ScalpelPerson : public Person {
|
||||
public:
|
||||
ScalpelPerson() : Person() {}
|
||||
~ScalpelPerson() override {}
|
||||
|
||||
/**
|
||||
* Synchronize the data for a savegame
|
||||
*/
|
||||
virtual void synchronize(Serializer &s);
|
||||
|
||||
/**
|
||||
* This adjusts the sprites position, as well as its animation sequence:
|
||||
*/
|
||||
void adjustSprite() override;
|
||||
|
||||
/**
|
||||
* Bring a moving character to a standing position
|
||||
*/
|
||||
void gotoStand() override;
|
||||
|
||||
/**
|
||||
* Set the variables for moving a character from one poisition to another
|
||||
* in a straight line
|
||||
*/
|
||||
void setWalking() override;
|
||||
|
||||
/**
|
||||
* Walk to the co-ordinates passed, and then face the given direction
|
||||
*/
|
||||
void walkToCoords(const Point32 &destPos, int destDir) override;
|
||||
|
||||
/**
|
||||
* Get the source position for a character potentially affected by scaling
|
||||
*/
|
||||
Common::Point getSourcePoint() const override;
|
||||
};
|
||||
|
||||
class ScalpelPeople : public People {
|
||||
public:
|
||||
ScalpelPeople(SherlockEngine *vm);
|
||||
~ScalpelPeople() override {}
|
||||
|
||||
ScalpelPerson &operator[](PeopleId id) { return *(ScalpelPerson *)_data[id]; }
|
||||
ScalpelPerson &operator[](int idx) { return *(ScalpelPerson *)_data[idx]; }
|
||||
|
||||
/**
|
||||
* Setup the data for an animating speaker portrait at the top of the screen
|
||||
*/
|
||||
void setTalking(int speaker);
|
||||
|
||||
/**
|
||||
* Synchronize the data for a savegame
|
||||
*/
|
||||
void synchronize(Serializer &s) override;
|
||||
|
||||
/**
|
||||
* Change the sequence of the scene background object associated with the specified speaker.
|
||||
*/
|
||||
void setTalkSequence(int speaker, int sequenceNum = 1) override;
|
||||
|
||||
/**
|
||||
* Restrict passed point to zone using Sherlock's positioning rules
|
||||
*/
|
||||
const Common::Point restrictToZone(int zoneId, const Common::Point &destPos) override;
|
||||
|
||||
/**
|
||||
* Load the walking images for Sherlock
|
||||
*/
|
||||
bool loadWalk() override;
|
||||
|
||||
/**
|
||||
* If the specified speaker is a background object, it will set it so that it uses
|
||||
* the Listen Sequence (specified by the sequence number). If the current sequence
|
||||
* has an Allow Talk Code in it, the _gotoSeq field will be set so that the object
|
||||
* begins listening as soon as it hits the Allow Talk Code. If there is no Abort Code,
|
||||
* the Listen Sequence will begin immediately.
|
||||
* @param speaker Who is speaking
|
||||
* @param sequenceNum Which listen sequence to use
|
||||
*/
|
||||
void setListenSequence(int speaker, int sequenceNum = 1) override;
|
||||
};
|
||||
|
||||
} // End of namespace Scalpel
|
||||
|
||||
} // End of namespace Sherlock
|
||||
|
||||
#endif
|
||||
308
engines/sherlock/scalpel/scalpel_saveload.cpp
Normal file
308
engines/sherlock/scalpel/scalpel_saveload.cpp
Normal file
@@ -0,0 +1,308 @@
|
||||
/* 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 "sherlock/scalpel/scalpel_fixed_text.h"
|
||||
#include "sherlock/scalpel/scalpel_saveload.h"
|
||||
#include "sherlock/scalpel/scalpel_screen.h"
|
||||
#include "sherlock/scalpel/scalpel.h"
|
||||
|
||||
#include "backends/keymapper/keymapper.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
namespace Scalpel {
|
||||
|
||||
const int ENV_POINTS[6][3] = {
|
||||
{ 41, 80, 61 }, // Exit
|
||||
{ 81, 120, 101 }, // Load
|
||||
{ 121, 160, 141 }, // Save
|
||||
{ 161, 200, 181 }, // Up
|
||||
{ 201, 240, 221 }, // Down
|
||||
{ 241, 280, 261 } // Quit
|
||||
};
|
||||
|
||||
/*----------------------------------------------------------------*/
|
||||
|
||||
ScalpelSaveManager::ScalpelSaveManager(SherlockEngine *vm, const Common::String &target) :
|
||||
SaveManager(vm, target), _envMode(SAVEMODE_NONE) {
|
||||
|
||||
_fixedTextExit = FIXED(LoadSave_Exit);
|
||||
_fixedTextLoad = FIXED(LoadSave_Load);
|
||||
_fixedTextSave = FIXED(LoadSave_Save);
|
||||
_fixedTextUp = FIXED(LoadSave_Up);
|
||||
_fixedTextDown = FIXED(LoadSave_Down);
|
||||
_fixedTextQuit = FIXED(LoadSave_Quit);
|
||||
|
||||
_actionsIndexed[0] = kActionScalpelFilesExit;
|
||||
_actionsIndexed[1] = kActionScalpelFilesLoad;
|
||||
_actionsIndexed[2] = kActionScalpelFilesSave;
|
||||
_actionsIndexed[3] = kActionScalpelScrollUp;
|
||||
_actionsIndexed[4] = kActionScalpelScrollDown;
|
||||
_actionsIndexed[5] = kActionScalpelFilesQuit;
|
||||
_actionsIndexed[6] = kActionScalpelFiles1;
|
||||
_actionsIndexed[7] = kActionScalpelFiles2;
|
||||
_actionsIndexed[8] = kActionScalpelFiles3;
|
||||
_actionsIndexed[9] = kActionScalpelFiles4;
|
||||
_actionsIndexed[10] = kActionScalpelFiles5;
|
||||
_actionsIndexed[11] = kActionScalpelFiles6;
|
||||
_actionsIndexed[12] = kActionScalpelFiles7;
|
||||
_actionsIndexed[13] = kActionScalpelFiles8;
|
||||
_actionsIndexed[14] = kActionScalpelFiles9;
|
||||
|
||||
_fixedTextQuitGameQuestion = FIXED(QuitGame_Question);
|
||||
_fixedTextQuitGameYes = FIXED(QuitGame_Yes);
|
||||
_fixedTextQuitGameNo = FIXED(QuitGame_No);
|
||||
}
|
||||
|
||||
int ScalpelSaveManager::identifyUserButton(Common::CustomEventType action) {
|
||||
for (uint16 actionNr = 0; actionNr < ARRAYSIZE(_actionsIndexed); actionNr++) {
|
||||
if (action == _actionsIndexed[actionNr])
|
||||
return actionNr;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void ScalpelSaveManager::drawInterface() {
|
||||
ScalpelScreen &screen = *(ScalpelScreen *)_vm->_screen;
|
||||
UserInterface &ui = *_vm->_ui;
|
||||
|
||||
// Create a list of savegame slots
|
||||
createSavegameList();
|
||||
|
||||
screen._backBuffer1.fillRect(Common::Rect(0, CONTROLS_Y, SHERLOCK_SCREEN_WIDTH, CONTROLS_Y + 10), BORDER_COLOR);
|
||||
screen._backBuffer1.fillRect(Common::Rect(0, CONTROLS_Y + 10, 2, SHERLOCK_SCREEN_HEIGHT), BORDER_COLOR);
|
||||
screen._backBuffer1.fillRect(Common::Rect(318, CONTROLS_Y + 10, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT), BORDER_COLOR);
|
||||
screen._backBuffer1.fillRect(Common::Rect(0, 199, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT), BORDER_COLOR);
|
||||
screen._backBuffer1.fillRect(Common::Rect(2, CONTROLS_Y + 10, SHERLOCK_SCREEN_WIDTH - 2, SHERLOCK_SCREEN_HEIGHT - 1), INV_BACKGROUND);
|
||||
|
||||
screen.makeButton(Common::Rect(ENV_POINTS[0][0], CONTROLS_Y, ENV_POINTS[0][1], CONTROLS_Y + 10),
|
||||
ENV_POINTS[0][2], _fixedTextExit);
|
||||
screen.makeButton(Common::Rect(ENV_POINTS[1][0], CONTROLS_Y, ENV_POINTS[1][1], CONTROLS_Y + 10),
|
||||
ENV_POINTS[1][2], _fixedTextLoad);
|
||||
screen.makeButton(Common::Rect(ENV_POINTS[2][0], CONTROLS_Y, ENV_POINTS[2][1], CONTROLS_Y + 10),
|
||||
ENV_POINTS[2][2], _fixedTextSave);
|
||||
screen.makeButton(Common::Rect(ENV_POINTS[3][0], CONTROLS_Y, ENV_POINTS[3][1], CONTROLS_Y + 10),
|
||||
ENV_POINTS[3][2], _fixedTextUp);
|
||||
screen.makeButton(Common::Rect(ENV_POINTS[4][0], CONTROLS_Y, ENV_POINTS[4][1], CONTROLS_Y + 10),
|
||||
ENV_POINTS[4][2], _fixedTextDown);
|
||||
screen.makeButton(Common::Rect(ENV_POINTS[5][0], CONTROLS_Y, ENV_POINTS[5][1], CONTROLS_Y + 10),
|
||||
ENV_POINTS[5][2], _fixedTextQuit);
|
||||
|
||||
if (!_savegameIndex)
|
||||
screen.buttonPrint(Common::Point(ENV_POINTS[3][2], CONTROLS_Y), COMMAND_NULL, 0, _fixedTextUp);
|
||||
|
||||
if (_savegameIndex == MAX_SAVEGAME_SLOTS - ONSCREEN_FILES_COUNT)
|
||||
screen.buttonPrint(Common::Point(ENV_POINTS[4][2], CONTROLS_Y), COMMAND_NULL, 0, _fixedTextDown);
|
||||
|
||||
for (int idx = _savegameIndex; idx < _savegameIndex + ONSCREEN_FILES_COUNT; ++idx) {
|
||||
screen.gPrint(Common::Point(6, CONTROLS_Y + 11 + (idx - _savegameIndex) * 10),
|
||||
INV_FOREGROUND, "%d.", idx + 1);
|
||||
screen.gPrint(Common::Point(24, CONTROLS_Y + 11 + (idx - _savegameIndex) * 10),
|
||||
INV_FOREGROUND, "%s", _savegames[idx].c_str());
|
||||
}
|
||||
|
||||
if (!ui._slideWindows) {
|
||||
screen.slamRect(Common::Rect(0, CONTROLS_Y, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT));
|
||||
} else {
|
||||
ui.summonWindow();
|
||||
}
|
||||
|
||||
_envMode = SAVEMODE_NONE;
|
||||
}
|
||||
|
||||
int ScalpelSaveManager::getHighlightedButton() const {
|
||||
Common::Point pt = _vm->_events->mousePos();
|
||||
|
||||
for (int idx = 0; idx < 6; ++idx) {
|
||||
if (pt.x > ENV_POINTS[idx][0] && pt.x < ENV_POINTS[idx][1] && pt.y > CONTROLS_Y
|
||||
&& pt.y < (CONTROLS_Y + 10))
|
||||
return idx;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void ScalpelSaveManager::highlightButtons(int btnIndex) {
|
||||
ScalpelScreen &screen = *(ScalpelScreen *)_vm->_screen;
|
||||
byte color = (btnIndex == 0) ? COMMAND_HIGHLIGHTED : COMMAND_FOREGROUND;
|
||||
|
||||
screen.buttonPrint(Common::Point(ENV_POINTS[0][2], CONTROLS_Y), color, 1, _fixedTextExit);
|
||||
|
||||
if ((btnIndex == 1) || ((_envMode == SAVEMODE_LOAD) && (btnIndex != 2)))
|
||||
screen.buttonPrint(Common::Point(ENV_POINTS[1][2], CONTROLS_Y), COMMAND_HIGHLIGHTED, true, _fixedTextLoad);
|
||||
else
|
||||
screen.buttonPrint(Common::Point(ENV_POINTS[1][2], CONTROLS_Y), COMMAND_FOREGROUND, true, _fixedTextLoad);
|
||||
|
||||
if ((btnIndex == 2) || ((_envMode == SAVEMODE_SAVE) && (btnIndex != 1)))
|
||||
screen.buttonPrint(Common::Point(ENV_POINTS[2][2], CONTROLS_Y), COMMAND_HIGHLIGHTED, true, _fixedTextSave);
|
||||
else
|
||||
screen.buttonPrint(Common::Point(ENV_POINTS[2][2], CONTROLS_Y), COMMAND_FOREGROUND, true, _fixedTextSave);
|
||||
|
||||
if (btnIndex == 3 && _savegameIndex)
|
||||
screen.buttonPrint(Common::Point(ENV_POINTS[3][2], CONTROLS_Y), COMMAND_HIGHLIGHTED, true, _fixedTextUp);
|
||||
else
|
||||
if (_savegameIndex)
|
||||
screen.buttonPrint(Common::Point(ENV_POINTS[3][2], CONTROLS_Y), COMMAND_FOREGROUND, true, _fixedTextUp);
|
||||
|
||||
if ((btnIndex == 4) && (_savegameIndex < MAX_SAVEGAME_SLOTS - 5))
|
||||
screen.buttonPrint(Common::Point(ENV_POINTS[4][2], CONTROLS_Y), COMMAND_HIGHLIGHTED, true, _fixedTextDown);
|
||||
else if (_savegameIndex < (MAX_SAVEGAME_SLOTS - 5))
|
||||
screen.buttonPrint(Common::Point(ENV_POINTS[4][2], CONTROLS_Y), COMMAND_FOREGROUND, true, _fixedTextDown);
|
||||
|
||||
color = (btnIndex == 5) ? COMMAND_HIGHLIGHTED : COMMAND_FOREGROUND;
|
||||
screen.buttonPrint(Common::Point(ENV_POINTS[5][2], CONTROLS_Y), color, 1, _fixedTextQuit);
|
||||
}
|
||||
|
||||
bool ScalpelSaveManager::checkGameOnScreen(int slot) {
|
||||
ScalpelScreen &screen = *(ScalpelScreen *)_vm->_screen;
|
||||
|
||||
// Check if it's already on-screen
|
||||
if (slot != -1 && (slot < _savegameIndex || slot >= (_savegameIndex + ONSCREEN_FILES_COUNT))) {
|
||||
_savegameIndex = slot;
|
||||
|
||||
screen._backBuffer1.fillRect(Common::Rect(3, CONTROLS_Y + 11, SHERLOCK_SCREEN_WIDTH - 2,
|
||||
SHERLOCK_SCREEN_HEIGHT - 1), INV_BACKGROUND);
|
||||
|
||||
for (int idx = _savegameIndex; idx < (_savegameIndex + 5); ++idx) {
|
||||
screen.gPrint(Common::Point(6, CONTROLS_Y + 11 + (idx - _savegameIndex) * 10),
|
||||
INV_FOREGROUND, "%d.", idx + 1);
|
||||
screen.gPrint(Common::Point(24, CONTROLS_Y + 11 + (idx - _savegameIndex) * 10),
|
||||
INV_FOREGROUND, "%s", _savegames[idx].c_str());
|
||||
}
|
||||
|
||||
screen.slamRect(Common::Rect(3, CONTROLS_Y + 11, 318, SHERLOCK_SCREEN_HEIGHT));
|
||||
|
||||
byte color = !_savegameIndex ? COMMAND_NULL : COMMAND_FOREGROUND;
|
||||
screen.buttonPrint(Common::Point(ENV_POINTS[3][2], CONTROLS_Y), color, 1, _fixedTextUp);
|
||||
|
||||
color = (_savegameIndex == (MAX_SAVEGAME_SLOTS - 5)) ? COMMAND_NULL : COMMAND_FOREGROUND;
|
||||
screen.buttonPrint(Common::Point(ENV_POINTS[4][2], CONTROLS_Y), color, 1, _fixedTextDown);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ScalpelSaveManager::promptForDescription(int slot) {
|
||||
Events &events = *_vm->_events;
|
||||
Scene &scene = *_vm->_scene;
|
||||
ScalpelScreen &screen = *(ScalpelScreen *)_vm->_screen;
|
||||
Talk &talk = *_vm->_talk;
|
||||
int xp, yp;
|
||||
bool flag = false;
|
||||
|
||||
screen.buttonPrint(Common::Point(ENV_POINTS[0][2], CONTROLS_Y), COMMAND_NULL, true, _fixedTextExit);
|
||||
screen.buttonPrint(Common::Point(ENV_POINTS[1][2], CONTROLS_Y), COMMAND_NULL, true, _fixedTextLoad);
|
||||
screen.buttonPrint(Common::Point(ENV_POINTS[2][2], CONTROLS_Y), COMMAND_NULL, true, _fixedTextSave);
|
||||
screen.buttonPrint(Common::Point(ENV_POINTS[3][2], CONTROLS_Y), COMMAND_NULL, true, _fixedTextUp);
|
||||
screen.buttonPrint(Common::Point(ENV_POINTS[4][2], CONTROLS_Y), COMMAND_NULL, true, _fixedTextDown);
|
||||
screen.buttonPrint(Common::Point(ENV_POINTS[5][2], CONTROLS_Y), COMMAND_NULL, true, _fixedTextQuit);
|
||||
|
||||
Common::String saveName = _savegames[slot];
|
||||
if (isSlotEmpty(slot)) {
|
||||
// It's an empty slot, so start off with an empty save name
|
||||
saveName = "";
|
||||
|
||||
yp = CONTROLS_Y + 12 + (slot - _savegameIndex) * 10;
|
||||
screen.vgaBar(Common::Rect(24, yp, 85, yp + 9), INV_BACKGROUND);
|
||||
}
|
||||
|
||||
screen.print(Common::Point(6, CONTROLS_Y + 12 + (slot - _savegameIndex) * 10), TALK_FOREGROUND, "%d.", slot + 1);
|
||||
screen.print(Common::Point(24, CONTROLS_Y + 12 + (slot - _savegameIndex) * 10), TALK_FOREGROUND, "%s", saveName.c_str());
|
||||
xp = 24 + screen.stringWidth(saveName);
|
||||
yp = CONTROLS_Y + 12 + (slot - _savegameIndex) * 10;
|
||||
|
||||
int done = 0;
|
||||
|
||||
Common::Keymapper *keymapper = _vm->getEventManager()->getKeymapper();
|
||||
keymapper->disableAllGameKeymaps();
|
||||
do {
|
||||
while (!_vm->shouldQuit() && !events.kbHit()) {
|
||||
scene.doBgAnim();
|
||||
|
||||
if (talk._talkToAbort)
|
||||
return false;
|
||||
|
||||
// Allow event processing
|
||||
events.pollEventsAndWait();
|
||||
events.setButtonState();
|
||||
|
||||
flag = !flag;
|
||||
if (flag)
|
||||
screen.vgaBar(Common::Rect(xp, yp - 1, xp + 8, yp + 9), INV_FOREGROUND);
|
||||
else
|
||||
screen.vgaBar(Common::Rect(xp, yp - 1, xp + 8, yp + 9), INV_BACKGROUND);
|
||||
}
|
||||
if (_vm->shouldQuit())
|
||||
return false;
|
||||
|
||||
// Get the next keypress
|
||||
Common::KeyState keyState = events.getKey();
|
||||
|
||||
if (keyState.keycode == Common::KEYCODE_BACKSPACE && saveName.size() > 0) {
|
||||
// Delete character of save name
|
||||
screen.vgaBar(Common::Rect(xp - screen.charWidth(saveName.lastChar()), yp - 1,
|
||||
xp + 8, yp + 9), INV_BACKGROUND);
|
||||
xp -= screen.charWidth(saveName.lastChar());
|
||||
screen.vgaBar(Common::Rect(xp, yp - 1, xp + 8, yp + 9), INV_FOREGROUND);
|
||||
saveName.deleteLastChar();
|
||||
|
||||
} else if (keyState.keycode == Common::KEYCODE_RETURN && saveName.compareToIgnoreCase(EMPTY_SAVEGAME_SLOT)) {
|
||||
done = 1;
|
||||
|
||||
} else if (keyState.keycode == Common::KEYCODE_ESCAPE) {
|
||||
screen.vgaBar(Common::Rect(xp, yp - 1, xp + 8, yp + 9), INV_BACKGROUND);
|
||||
done = -1;
|
||||
|
||||
} else if (keyState.ascii >= ' ' && keyState.ascii <= 'z' && saveName.size() < 50
|
||||
&& (xp + screen.charWidth(keyState.ascii)) < 308) {
|
||||
char c = (char)keyState.ascii;
|
||||
|
||||
screen.vgaBar(Common::Rect(xp, yp - 1, xp + 8, yp + 9), INV_BACKGROUND);
|
||||
screen.print(Common::Point(xp, yp), TALK_FOREGROUND, "%c", c);
|
||||
xp += screen.charWidth(c);
|
||||
screen.vgaBar(Common::Rect(xp, yp - 1, xp + 8, yp + 9), INV_FOREGROUND);
|
||||
saveName += c;
|
||||
}
|
||||
} while (!done);
|
||||
|
||||
keymapper->getKeymap("sherlock-default")->setEnabled(true);
|
||||
keymapper->getKeymap("scalpel-quit")->setEnabled(true);
|
||||
|
||||
if (done == 1) {
|
||||
// Enter key perssed
|
||||
_savegames[slot] = saveName;
|
||||
keymapper->getKeymap("scalpel")->setEnabled(true);
|
||||
} else {
|
||||
done = 0;
|
||||
_envMode = SAVEMODE_NONE;
|
||||
highlightButtons(-1);
|
||||
keymapper->getKeymap("scalpel-files")->setEnabled(true);
|
||||
keymapper->getKeymap("scalpel-scroll")->setEnabled(true);
|
||||
}
|
||||
|
||||
return done == 1;
|
||||
}
|
||||
|
||||
} // End of namespace Scalpel
|
||||
|
||||
} // End of namespace Sherlock
|
||||
91
engines/sherlock/scalpel/scalpel_saveload.h
Normal file
91
engines/sherlock/scalpel/scalpel_saveload.h
Normal file
@@ -0,0 +1,91 @@
|
||||
/* 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 SHERLOCK_SCALPEL_SAVELOAD_H
|
||||
#define SHERLOCK_SCALPEL_SAVELOAD_H
|
||||
|
||||
#include "sherlock/saveload.h"
|
||||
|
||||
#include "common/events.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
namespace Scalpel {
|
||||
|
||||
extern const int ENV_POINTS[6][3];
|
||||
|
||||
class ScalpelSaveManager: public SaveManager {
|
||||
public:
|
||||
SaveMode _envMode;
|
||||
|
||||
Common::String _fixedTextExit;
|
||||
Common::String _fixedTextLoad;
|
||||
Common::String _fixedTextSave;
|
||||
Common::String _fixedTextUp;
|
||||
Common::String _fixedTextDown;
|
||||
Common::String _fixedTextQuit;
|
||||
|
||||
Common::CustomEventType _actionsIndexed[15];
|
||||
|
||||
Common::String _fixedTextQuitGameQuestion;
|
||||
Common::String _fixedTextQuitGameYes;
|
||||
Common::String _fixedTextQuitGameNo;
|
||||
|
||||
public:
|
||||
ScalpelSaveManager(SherlockEngine *vm, const Common::String &target);
|
||||
~ScalpelSaveManager() override {}
|
||||
|
||||
/**
|
||||
* Shows the in-game dialog interface for loading and saving games
|
||||
*/
|
||||
void drawInterface();
|
||||
|
||||
/**
|
||||
* Return the index of the button the mouse is over, if any
|
||||
*/
|
||||
int getHighlightedButton() const;
|
||||
|
||||
/**
|
||||
* Handle highlighting buttons
|
||||
*/
|
||||
void highlightButtons(int btnIndex);
|
||||
|
||||
/**
|
||||
* Make sure that the selected savegame is on-screen
|
||||
*/
|
||||
bool checkGameOnScreen(int slot);
|
||||
|
||||
/**
|
||||
* Prompts the user to enter a description in a given slot
|
||||
*/
|
||||
bool promptForDescription(int slot);
|
||||
|
||||
/**
|
||||
* Identifies a button number according to the action, that the user pressed
|
||||
*/
|
||||
int identifyUserButton(Common::CustomEventType action);
|
||||
};
|
||||
|
||||
} // End of namespace Scalpel
|
||||
|
||||
} // End of namespace Sherlock
|
||||
|
||||
#endif
|
||||
757
engines/sherlock/scalpel/scalpel_scene.cpp
Normal file
757
engines/sherlock/scalpel/scalpel_scene.cpp
Normal file
@@ -0,0 +1,757 @@
|
||||
/* 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 "sherlock/scalpel/scalpel_scene.h"
|
||||
#include "sherlock/scalpel/scalpel_map.h"
|
||||
#include "sherlock/scalpel/scalpel_people.h"
|
||||
#include "sherlock/scalpel/scalpel_user_interface.h"
|
||||
#include "sherlock/scalpel/scalpel.h"
|
||||
#include "sherlock/events.h"
|
||||
#include "sherlock/people.h"
|
||||
#include "sherlock/screen.h"
|
||||
#include "sherlock/sherlock.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
namespace Scalpel {
|
||||
|
||||
const int FS_TRANS[8] = {
|
||||
STOP_UP, STOP_UPRIGHT, STOP_RIGHT, STOP_DOWNRIGHT, STOP_DOWN, STOP_DOWNLEFT, STOP_LEFT, STOP_UPLEFT
|
||||
};
|
||||
|
||||
/*----------------------------------------------------------------*/
|
||||
|
||||
ScalpelScene::~ScalpelScene() {
|
||||
for (uint idx = 0; idx < _canimShapes.size(); ++idx)
|
||||
delete _canimShapes[idx];
|
||||
}
|
||||
|
||||
bool ScalpelScene::loadScene(const Common::Path &filename) {
|
||||
ScalpelMap &map = *(ScalpelMap *)_vm->_map;
|
||||
bool result = Scene::loadScene(filename);
|
||||
|
||||
if (!_vm->isDemo()) {
|
||||
// Reset the previous map location and position on overhead map
|
||||
map._oldCharPoint = _currentScene;
|
||||
|
||||
map._overPos.x = (map[_currentScene].x - 6) * FIXED_INT_MULTIPLIER;
|
||||
map._overPos.y = (map[_currentScene].y + 9) * FIXED_INT_MULTIPLIER;
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void ScalpelScene::drawAllShapes() {
|
||||
People &people = *_vm->_people;
|
||||
Screen &screen = *_vm->_screen;
|
||||
|
||||
// Restrict drawing window
|
||||
screen.setDisplayBounds(Common::Rect(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCENE_HEIGHT));
|
||||
|
||||
// Draw all active shapes which are behind the person
|
||||
for (uint idx = 0; idx < _bgShapes.size(); ++idx) {
|
||||
if (_bgShapes[idx]._type == ACTIVE_BG_SHAPE && _bgShapes[idx]._misc == BEHIND)
|
||||
screen.getBackBuffer()->SHtransBlitFrom(*_bgShapes[idx]._imageFrame, _bgShapes[idx]._position, _bgShapes[idx]._flags & OBJ_FLIPPED);
|
||||
}
|
||||
|
||||
// Draw all canimations which are behind the person
|
||||
for (uint idx = 0; idx < _canimShapes.size(); ++idx) {
|
||||
if (_canimShapes[idx]->_type == ACTIVE_BG_SHAPE && _canimShapes[idx]->_misc == BEHIND)
|
||||
screen.getBackBuffer()->SHtransBlitFrom(*_canimShapes[idx]->_imageFrame,
|
||||
_canimShapes[idx]->_position, _canimShapes[idx]->_flags & OBJ_FLIPPED);
|
||||
}
|
||||
|
||||
// Draw all active shapes which are normal and behind the person
|
||||
for (uint idx = 0; idx < _bgShapes.size(); ++idx) {
|
||||
if (_bgShapes[idx]._type == ACTIVE_BG_SHAPE && _bgShapes[idx]._misc == NORMAL_BEHIND)
|
||||
screen.getBackBuffer()->SHtransBlitFrom(*_bgShapes[idx]._imageFrame, _bgShapes[idx]._position, _bgShapes[idx]._flags & OBJ_FLIPPED);
|
||||
}
|
||||
|
||||
// Draw all canimations which are normal and behind the person
|
||||
for (uint idx = 0; idx < _canimShapes.size(); ++idx) {
|
||||
if (_canimShapes[idx]->_type == ACTIVE_BG_SHAPE && _canimShapes[idx]->_misc == NORMAL_BEHIND)
|
||||
screen.getBackBuffer()->SHtransBlitFrom(*_canimShapes[idx]->_imageFrame, _canimShapes[idx]->_position,
|
||||
_canimShapes[idx]->_flags & OBJ_FLIPPED);
|
||||
}
|
||||
|
||||
// Draw any active characters
|
||||
for (int idx = 0; idx < MAX_CHARACTERS; ++idx) {
|
||||
Person &p = people[idx];
|
||||
if (p._type == CHARACTER && p._walkLoaded) {
|
||||
bool flipped = IS_SERRATED_SCALPEL && (
|
||||
p._sequenceNumber == WALK_LEFT || p._sequenceNumber == STOP_LEFT ||
|
||||
p._sequenceNumber == WALK_UPLEFT || p._sequenceNumber == STOP_UPLEFT ||
|
||||
p._sequenceNumber == WALK_DOWNRIGHT || p._sequenceNumber == STOP_DOWNRIGHT);
|
||||
|
||||
screen.getBackBuffer()->SHtransBlitFrom(*p._imageFrame, Common::Point(p._position.x / FIXED_INT_MULTIPLIER,
|
||||
p._position.y / FIXED_INT_MULTIPLIER - p.frameHeight()), flipped);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw all static and active shapes that are NORMAL and are in front of the player
|
||||
for (uint idx = 0; idx < _bgShapes.size(); ++idx) {
|
||||
if ((_bgShapes[idx]._type == ACTIVE_BG_SHAPE || _bgShapes[idx]._type == STATIC_BG_SHAPE) &&
|
||||
_bgShapes[idx]._misc == NORMAL_FORWARD)
|
||||
screen.getBackBuffer()->SHtransBlitFrom(*_bgShapes[idx]._imageFrame, _bgShapes[idx]._position,
|
||||
_bgShapes[idx]._flags & OBJ_FLIPPED);
|
||||
}
|
||||
|
||||
// Draw all static and active canimations that are NORMAL and are in front of the player
|
||||
for (uint idx = 0; idx < _canimShapes.size(); ++idx) {
|
||||
if ((_canimShapes[idx]->_type == ACTIVE_BG_SHAPE || _canimShapes[idx]->_type == STATIC_BG_SHAPE) &&
|
||||
_canimShapes[idx]->_misc == NORMAL_FORWARD)
|
||||
screen.getBackBuffer()->SHtransBlitFrom(*_canimShapes[idx]->_imageFrame, _canimShapes[idx]->_position,
|
||||
_canimShapes[idx]->_flags & OBJ_FLIPPED);
|
||||
}
|
||||
|
||||
// Draw all static and active shapes that are FORWARD
|
||||
for (uint idx = 0; idx < _bgShapes.size(); ++idx) {
|
||||
_bgShapes[idx]._oldPosition = _bgShapes[idx]._position;
|
||||
_bgShapes[idx]._oldSize = Common::Point(_bgShapes[idx].frameWidth(),
|
||||
_bgShapes[idx].frameHeight());
|
||||
|
||||
if ((_bgShapes[idx]._type == ACTIVE_BG_SHAPE || _bgShapes[idx]._type == STATIC_BG_SHAPE) &&
|
||||
_bgShapes[idx]._misc == FORWARD)
|
||||
screen.getBackBuffer()->SHtransBlitFrom(*_bgShapes[idx]._imageFrame, _bgShapes[idx]._position,
|
||||
_bgShapes[idx]._flags & OBJ_FLIPPED);
|
||||
}
|
||||
|
||||
// Draw all static and active canimations that are forward
|
||||
for (uint idx = 0; idx < _canimShapes.size(); ++idx) {
|
||||
if ((_canimShapes[idx]->_type == ACTIVE_BG_SHAPE || _canimShapes[idx]->_type == STATIC_BG_SHAPE) &&
|
||||
_canimShapes[idx]->_misc == FORWARD)
|
||||
screen.getBackBuffer()->SHtransBlitFrom(*_canimShapes[idx]->_imageFrame, _canimShapes[idx]->_position,
|
||||
_canimShapes[idx]->_flags & OBJ_FLIPPED);
|
||||
}
|
||||
|
||||
screen.resetDisplayBounds();
|
||||
}
|
||||
|
||||
void ScalpelScene::checkBgShapes() {
|
||||
People &people = *_vm->_people;
|
||||
Person &holmes = people[HOLMES];
|
||||
Common::Point pt(holmes._position.x / FIXED_INT_MULTIPLIER, holmes._position.y / FIXED_INT_MULTIPLIER);
|
||||
|
||||
// Call the base scene method to handle bg shapes
|
||||
Scene::checkBgShapes();
|
||||
|
||||
// Iterate through the canim list
|
||||
for (uint idx = 0; idx < _canimShapes.size(); ++idx) {
|
||||
Object &obj = *_canimShapes[idx];
|
||||
if (obj._type == STATIC_BG_SHAPE || obj._type == ACTIVE_BG_SHAPE) {
|
||||
if ((obj._flags & 5) == 1) {
|
||||
obj._misc = (pt.y < (obj._position.y + obj._imageFrame->_frame.h - 1)) ?
|
||||
NORMAL_FORWARD : NORMAL_BEHIND;
|
||||
} else if (!(obj._flags & 1)) {
|
||||
obj._misc = BEHIND;
|
||||
} else if (obj._flags & 4) {
|
||||
obj._misc = FORWARD;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScalpelScene::doBgAnimCheckCursor() {
|
||||
Inventory &inv = *_vm->_inventory;
|
||||
Events &events = *_vm->_events;
|
||||
UserInterface &ui = *_vm->_ui;
|
||||
Common::Point mousePos = events.mousePos();
|
||||
events.animateCursorIfNeeded();
|
||||
|
||||
if (ui._menuMode == LOOK_MODE) {
|
||||
if (mousePos.y > CONTROLS_Y1)
|
||||
events.setCursor(ARROW);
|
||||
else if (mousePos.y < CONTROLS_Y)
|
||||
events.setCursor(MAGNIFY);
|
||||
}
|
||||
|
||||
// Check for setting magnifying glass cursor
|
||||
if (ui._menuMode == INV_MODE || ui._menuMode == USE_MODE || ui._menuMode == GIVE_MODE) {
|
||||
if (inv._invMode == INVMODE_LOOK) {
|
||||
// Only show Magnifying glass cursor if it's not on the inventory command line
|
||||
if (mousePos.y < CONTROLS_Y || mousePos.y >(CONTROLS_Y1 + 13))
|
||||
events.setCursor(MAGNIFY);
|
||||
else
|
||||
events.setCursor(ARROW);
|
||||
} else {
|
||||
events.setCursor(ARROW);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScalpelScene::doBgAnim() {
|
||||
ScalpelEngine &vm = *((ScalpelEngine *)_vm);
|
||||
Events &events = *_vm->_events;
|
||||
People &people = *_vm->_people;
|
||||
Screen &screen = *_vm->_screen;
|
||||
Talk &talk = *_vm->_talk;
|
||||
|
||||
doBgAnimCheckCursor();
|
||||
|
||||
screen.setDisplayBounds(Common::Rect(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCENE_HEIGHT));
|
||||
talk._talkToAbort = false;
|
||||
|
||||
if (_restoreFlag) {
|
||||
for (int idx = 0; idx < MAX_CHARACTERS; ++idx) {
|
||||
if (people[idx]._type == CHARACTER)
|
||||
people[idx].checkSprite();
|
||||
}
|
||||
|
||||
for (uint idx = 0; idx < _bgShapes.size(); ++idx) {
|
||||
if (_bgShapes[idx]._type == ACTIVE_BG_SHAPE)
|
||||
_bgShapes[idx].checkObject();
|
||||
}
|
||||
|
||||
if (people._portraitLoaded && people._portrait._type == ACTIVE_BG_SHAPE)
|
||||
people._portrait.checkObject();
|
||||
|
||||
for (uint idx = 0; idx < _canimShapes.size(); ++idx) {
|
||||
if (_canimShapes[idx]->_type != INVALID && _canimShapes[idx]->_type != REMOVE)
|
||||
_canimShapes[idx]->checkObject();
|
||||
}
|
||||
|
||||
if (_currentScene == DRAWING_ROOM)
|
||||
vm.eraseBrumwellMirror();
|
||||
|
||||
// Restore the back buffer from the back buffer 2 in the changed area
|
||||
Common::Rect bounds(people[HOLMES]._oldPosition.x, people[HOLMES]._oldPosition.y,
|
||||
people[HOLMES]._oldPosition.x + people[HOLMES]._oldSize.x,
|
||||
people[HOLMES]._oldPosition.y + people[HOLMES]._oldSize.y);
|
||||
Common::Point pt(bounds.left, bounds.top);
|
||||
|
||||
if (people[HOLMES]._type == CHARACTER)
|
||||
screen.restoreBackground(bounds);
|
||||
else if (people[HOLMES]._type == REMOVE)
|
||||
screen.getBackBuffer()->SHblitFrom(screen._backBuffer2, pt, bounds);
|
||||
|
||||
for (uint idx = 0; idx < _bgShapes.size(); ++idx) {
|
||||
Object &o = _bgShapes[idx];
|
||||
if (o._type == ACTIVE_BG_SHAPE || o._type == HIDE_SHAPE || o._type == REMOVE)
|
||||
screen.restoreBackground(o.getOldBounds());
|
||||
}
|
||||
|
||||
if (people._portraitLoaded)
|
||||
screen.restoreBackground(Common::Rect(
|
||||
people._portrait._oldPosition.x, people._portrait._oldPosition.y,
|
||||
people._portrait._oldPosition.x + people._portrait._oldSize.x,
|
||||
people._portrait._oldPosition.y + people._portrait._oldSize.y
|
||||
));
|
||||
|
||||
for (uint idx = 0; idx < _bgShapes.size(); ++idx) {
|
||||
Object &o = _bgShapes[idx];
|
||||
if (o._type == NO_SHAPE && ((o._flags & OBJ_BEHIND) == 0)) {
|
||||
// Restore screen area
|
||||
screen.getBackBuffer()->SHblitFrom(screen._backBuffer2, o._position,
|
||||
Common::Rect(o._position.x, o._position.y,
|
||||
o._position.x + o._noShapeSize.x, o._position.y + o._noShapeSize.y));
|
||||
|
||||
o._oldPosition = o._position;
|
||||
o._oldSize = o._noShapeSize;
|
||||
}
|
||||
}
|
||||
|
||||
for (uint idx = 0; idx < _canimShapes.size(); ++idx) {
|
||||
Object &o = *_canimShapes[idx];
|
||||
if (o._type == ACTIVE_BG_SHAPE || o._type == HIDE_SHAPE || o._type == REMOVE)
|
||||
screen.restoreBackground(Common::Rect(o._oldPosition.x, o._oldPosition.y,
|
||||
o._oldPosition.x + o._oldSize.x, o._oldPosition.y + o._oldSize.y));
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Update the background objects and canimations
|
||||
//
|
||||
|
||||
for (uint idx = 0; idx < _bgShapes.size(); ++idx) {
|
||||
Object &o = _bgShapes[idx];
|
||||
if (o._type == ACTIVE_BG_SHAPE || o._type == NO_SHAPE)
|
||||
o.adjustObject();
|
||||
}
|
||||
|
||||
if (people._portraitLoaded && people._portrait._type == ACTIVE_BG_SHAPE)
|
||||
people._portrait.adjustObject();
|
||||
|
||||
for (uint idx = 0; idx < _canimShapes.size(); ++idx) {
|
||||
if (_canimShapes[idx]->_type != INVALID)
|
||||
_canimShapes[idx]->adjustObject();
|
||||
}
|
||||
|
||||
if (people[HOLMES]._type == CHARACTER && people._holmesOn)
|
||||
people[HOLMES].adjustSprite();
|
||||
|
||||
// Flag the bg shapes which need to be redrawn
|
||||
checkBgShapes();
|
||||
|
||||
if (_currentScene == DRAWING_ROOM)
|
||||
vm.doBrumwellMirror();
|
||||
|
||||
// Draw all active shapes which are behind the person
|
||||
for (uint idx = 0; idx < _bgShapes.size(); ++idx) {
|
||||
Object &o = _bgShapes[idx];
|
||||
if (o._type == ACTIVE_BG_SHAPE && o._misc == BEHIND)
|
||||
screen.getBackBuffer()->SHtransBlitFrom(*o._imageFrame, o._position, o._flags & OBJ_FLIPPED);
|
||||
}
|
||||
|
||||
// Draw all canimations which are behind the person
|
||||
for (uint idx = 0; idx < _canimShapes.size(); ++idx) {
|
||||
Object &o = *_canimShapes[idx];
|
||||
if (o._type == ACTIVE_BG_SHAPE && o._misc == BEHIND) {
|
||||
screen.getBackBuffer()->SHtransBlitFrom(*o._imageFrame, o._position, o._flags & OBJ_FLIPPED);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw all active shapes which are HAPPEN and behind the person
|
||||
for (uint idx = 0; idx < _bgShapes.size(); ++idx) {
|
||||
Object &o = _bgShapes[idx];
|
||||
if (o._type == ACTIVE_BG_SHAPE && o._misc == NORMAL_BEHIND)
|
||||
screen.getBackBuffer()->SHtransBlitFrom(*o._imageFrame, o._position, o._flags & OBJ_FLIPPED);
|
||||
}
|
||||
|
||||
// Draw all canimations which are NORMAL and behind the person
|
||||
for (uint idx = 0; idx < _canimShapes.size(); ++idx) {
|
||||
Object &o = *_canimShapes[idx];
|
||||
if (o._type == ACTIVE_BG_SHAPE && o._misc == NORMAL_BEHIND) {
|
||||
screen.getBackBuffer()->SHtransBlitFrom(*o._imageFrame, o._position, o._flags & OBJ_FLIPPED);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw the player if he's active and his walk has been loaded into memory
|
||||
if (people[HOLMES]._type == CHARACTER && people[HOLMES]._walkLoaded && people._holmesOn) {
|
||||
// If Holmes is too far to the right, move him back so he's on-screen
|
||||
int xRight = SHERLOCK_SCREEN_WIDTH - 2 - people[HOLMES]._imageFrame->_frame.w;
|
||||
int tempX = MIN(people[HOLMES]._position.x / FIXED_INT_MULTIPLIER, xRight);
|
||||
|
||||
bool flipped = people[HOLMES]._sequenceNumber == WALK_LEFT || people[HOLMES]._sequenceNumber == STOP_LEFT ||
|
||||
people[HOLMES]._sequenceNumber == WALK_UPLEFT || people[HOLMES]._sequenceNumber == STOP_UPLEFT ||
|
||||
people[HOLMES]._sequenceNumber == WALK_DOWNRIGHT || people[HOLMES]._sequenceNumber == STOP_DOWNRIGHT;
|
||||
screen.getBackBuffer()->SHtransBlitFrom(*people[HOLMES]._imageFrame,
|
||||
Common::Point(tempX, people[HOLMES]._position.y / FIXED_INT_MULTIPLIER - people[HOLMES]._imageFrame->_frame.h), flipped);
|
||||
}
|
||||
|
||||
// Draw all static and active shapes are NORMAL and are in front of the person
|
||||
for (uint idx = 0; idx < _bgShapes.size(); ++idx) {
|
||||
Object &o = _bgShapes[idx];
|
||||
if ((o._type == ACTIVE_BG_SHAPE || o._type == STATIC_BG_SHAPE) && o._misc == NORMAL_FORWARD)
|
||||
screen.getBackBuffer()->SHtransBlitFrom(*o._imageFrame, o._position, o._flags & OBJ_FLIPPED);
|
||||
}
|
||||
|
||||
// Draw all static and active canimations that are NORMAL and are in front of the person
|
||||
for (uint idx = 0; idx < _canimShapes.size(); ++idx) {
|
||||
Object &o = *_canimShapes[idx];
|
||||
if ((o._type == ACTIVE_BG_SHAPE || o._type == STATIC_BG_SHAPE) && o._misc == NORMAL_FORWARD) {
|
||||
screen.getBackBuffer()->SHtransBlitFrom(*o._imageFrame, o._position, o._flags & OBJ_FLIPPED);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw all static and active shapes that are in front of the person
|
||||
for (uint idx = 0; idx < _bgShapes.size(); ++idx) {
|
||||
Object &o = _bgShapes[idx];
|
||||
if ((o._type == ACTIVE_BG_SHAPE || o._type == STATIC_BG_SHAPE) && o._misc == FORWARD)
|
||||
screen.getBackBuffer()->SHtransBlitFrom(*o._imageFrame, o._position, o._flags & OBJ_FLIPPED);
|
||||
}
|
||||
|
||||
// Draw any active portrait
|
||||
if (people._portraitLoaded && people._portrait._type == ACTIVE_BG_SHAPE)
|
||||
screen.getBackBuffer()->SHtransBlitFrom(*people._portrait._imageFrame,
|
||||
people._portrait._position, people._portrait._flags & OBJ_FLIPPED);
|
||||
|
||||
// Draw all static and active canimations that are in front of the person
|
||||
for (uint idx = 0; idx < _canimShapes.size(); ++idx) {
|
||||
Object &o = *_canimShapes[idx];
|
||||
if ((o._type == ACTIVE_BG_SHAPE || o._type == STATIC_BG_SHAPE) && o._misc == FORWARD) {
|
||||
screen.getBackBuffer()->SHtransBlitFrom(*o._imageFrame, o._position, o._flags & OBJ_FLIPPED);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw all NO_SHAPE shapes which have flag bit 0 clear
|
||||
for (uint idx = 0; idx < _bgShapes.size(); ++idx) {
|
||||
Object &o = _bgShapes[idx];
|
||||
if (o._type == NO_SHAPE && (o._flags & OBJ_BEHIND) == 0)
|
||||
screen.getBackBuffer()->SHtransBlitFrom(*o._imageFrame, o._position, o._flags & OBJ_FLIPPED);
|
||||
}
|
||||
|
||||
// Bring the newly built picture to the screen
|
||||
if (_animating == 2) {
|
||||
_animating = 0;
|
||||
screen.slamRect(Common::Rect(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCENE_HEIGHT));
|
||||
} else {
|
||||
if (people[HOLMES]._type != INVALID && ((_goToScene == -1 || _canimShapes.empty()))) {
|
||||
if (people[HOLMES]._type == REMOVE) {
|
||||
screen.slamRect(Common::Rect(
|
||||
people[HOLMES]._oldPosition.x, people[HOLMES]._oldPosition.y,
|
||||
people[HOLMES]._oldPosition.x + people[HOLMES]._oldSize.x,
|
||||
people[HOLMES]._oldPosition.y + people[HOLMES]._oldSize.y
|
||||
));
|
||||
people[HOLMES]._type = INVALID;
|
||||
} else {
|
||||
screen.flushImage(people[HOLMES]._imageFrame,
|
||||
Common::Point(people[HOLMES]._position.x / FIXED_INT_MULTIPLIER,
|
||||
people[HOLMES]._position.y / FIXED_INT_MULTIPLIER - people[HOLMES].frameHeight()),
|
||||
&people[HOLMES]._oldPosition.x, &people[HOLMES]._oldPosition.y,
|
||||
&people[HOLMES]._oldSize.x, &people[HOLMES]._oldSize.y);
|
||||
}
|
||||
}
|
||||
|
||||
if (_currentScene == DRAWING_ROOM)
|
||||
vm.flushBrumwellMirror();
|
||||
|
||||
for (uint idx = 0; idx < _bgShapes.size(); ++idx) {
|
||||
Object &o = _bgShapes[idx];
|
||||
if ((o._type == ACTIVE_BG_SHAPE || o._type == REMOVE) && _goToScene == -1) {
|
||||
screen.flushImage(o._imageFrame, o._position,
|
||||
&o._oldPosition.x, &o._oldPosition.y, &o._oldSize.x, &o._oldSize.y);
|
||||
}
|
||||
}
|
||||
|
||||
if (people._portraitLoaded) {
|
||||
if (people._portrait._type == REMOVE)
|
||||
screen.slamRect(Common::Rect(
|
||||
people._portrait._position.x, people._portrait._position.y,
|
||||
people._portrait._position.x + people._portrait._delta.x,
|
||||
people._portrait._position.y + people._portrait._delta.y
|
||||
));
|
||||
else
|
||||
screen.flushImage(people._portrait._imageFrame, people._portrait._position,
|
||||
&people._portrait._oldPosition.x, &people._portrait._oldPosition.y,
|
||||
&people._portrait._oldSize.x, &people._portrait._oldSize.y);
|
||||
|
||||
if (people._portrait._type == REMOVE)
|
||||
people._portrait._type = INVALID;
|
||||
}
|
||||
|
||||
if (_goToScene == -1) {
|
||||
for (uint idx = 0; idx < _bgShapes.size(); ++idx) {
|
||||
Object &o = _bgShapes[idx];
|
||||
if (o._type == NO_SHAPE && (o._flags & OBJ_BEHIND) == 0) {
|
||||
screen.slamArea(o._position.x, o._position.y, o._oldSize.x, o._oldSize.y);
|
||||
screen.slamArea(o._oldPosition.x, o._oldPosition.y, o._oldSize.x, o._oldSize.y);
|
||||
} else if (o._type == HIDE_SHAPE) {
|
||||
// Hiding shape, so flush it out and mark it as hidden
|
||||
screen.flushImage(o._imageFrame, o._position,
|
||||
&o._oldPosition.x, &o._oldPosition.y, &o._oldSize.x, &o._oldSize.y);
|
||||
o._type = HIDDEN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int idx = _canimShapes.size() - 1; idx >= 0; --idx) {
|
||||
Object &o = *_canimShapes[idx];
|
||||
|
||||
if (o._type == INVALID) {
|
||||
// Anim shape was invalidated by checkEndOfSequence, so at this point we can remove it
|
||||
delete _canimShapes[idx];
|
||||
_canimShapes.remove_at(idx);
|
||||
} else if (o._type == REMOVE) {
|
||||
if (_goToScene == -1)
|
||||
screen.slamArea(o._position.x, o._position.y, o._delta.x, o._delta.y);
|
||||
|
||||
// Shape for an animation is no longer needed, so remove it completely
|
||||
delete _canimShapes[idx];
|
||||
_canimShapes.remove_at(idx);
|
||||
} else if (o._type == ACTIVE_BG_SHAPE) {
|
||||
screen.flushImage(o._imageFrame, o._position,
|
||||
&o._oldPosition.x, &o._oldPosition.y, &o._oldSize.x, &o._oldSize.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_restoreFlag = true;
|
||||
_doBgAnimDone = true;
|
||||
|
||||
events.wait(3);
|
||||
screen.resetDisplayBounds();
|
||||
|
||||
// Check if the method was called for calling a portrait, and a talk was
|
||||
// interrupting it. This talk file would not have been executed at the time,
|
||||
// since we needed to finish the 'doBgAnim' to finish clearing the portrait
|
||||
if (people._clearingThePortrait && talk._scriptMoreFlag == 3) {
|
||||
// Reset the flags and call to talk
|
||||
people._clearingThePortrait = false;
|
||||
talk._scriptMoreFlag = 0;
|
||||
talk.talkTo(talk._scriptName);
|
||||
}
|
||||
}
|
||||
|
||||
int ScalpelScene::startCAnim(int cAnimNum, int playRate) {
|
||||
Events &events = *_vm->_events;
|
||||
ScalpelMap &map = *(ScalpelMap *)_vm->_map;
|
||||
People &people = *_vm->_people;
|
||||
Resources &res = *_vm->_res;
|
||||
Talk &talk = *_vm->_talk;
|
||||
ScalpelUserInterface &ui = *(ScalpelUserInterface *)_vm->_ui;
|
||||
Point32 tpPos, walkPos;
|
||||
int tpDir, walkDir;
|
||||
int tFrames = 0;
|
||||
int gotoCode = -1;
|
||||
Object *cObj;
|
||||
|
||||
// Validation
|
||||
if (cAnimNum >= (int)_cAnim.size())
|
||||
// number out of bounds
|
||||
return -1;
|
||||
if (_canimShapes.size() >= 3 || playRate == 0)
|
||||
// Too many active animations, or invalid play rate
|
||||
return 0;
|
||||
|
||||
CAnim &cAnim = _cAnim[cAnimNum];
|
||||
if (playRate < 0) {
|
||||
// Reverse direction
|
||||
walkPos = cAnim._teleport[0];
|
||||
walkDir = cAnim._teleport[0]._facing;
|
||||
tpPos = cAnim._goto[0];
|
||||
tpDir = cAnim._goto[0]._facing;
|
||||
} else {
|
||||
// Forward direction
|
||||
walkPos = cAnim._goto[0];
|
||||
walkDir = cAnim._goto[0]._facing;
|
||||
tpPos = cAnim._teleport[0];
|
||||
tpDir = cAnim._teleport[0]._facing;
|
||||
}
|
||||
|
||||
CursorId oldCursor = events.getCursor();
|
||||
events.setCursor(WAIT);
|
||||
|
||||
if (walkPos.x != -1) {
|
||||
// Holmes must walk to the walk point before the cAnimation is started
|
||||
if (people[HOLMES]._position != walkPos)
|
||||
people[HOLMES].walkToCoords(walkPos, walkDir);
|
||||
}
|
||||
|
||||
if (talk._talkToAbort)
|
||||
return 1;
|
||||
|
||||
// Add new anim shape entry for displaying the animation
|
||||
cObj = new Object();
|
||||
_canimShapes.push_back(cObj);
|
||||
|
||||
// Copy the canimation into the bgShapes type canimation structure so it can be played
|
||||
cObj->_allow = cAnimNum + 1; // Keep track of the parent structure
|
||||
cObj->_name = _cAnim[cAnimNum]._name; // Copy name
|
||||
|
||||
// Remove any attempt to draw object frame
|
||||
if (cAnim._type == NO_SHAPE && cAnim._sequences[0] < 100)
|
||||
cAnim._sequences[0] = 0;
|
||||
|
||||
cObj->_sequences = cAnim._sequences;
|
||||
cObj->_images = nullptr;
|
||||
cObj->_position = cAnim._position;
|
||||
cObj->_delta = Common::Point(0, 0);
|
||||
cObj->_type = cAnim._type;
|
||||
cObj->_flags = cAnim._flags;
|
||||
|
||||
cObj->_maxFrames = 0;
|
||||
cObj->_frameNumber = -1;
|
||||
cObj->_sequenceNumber = cAnimNum;
|
||||
cObj->_oldPosition = Common::Point(0, 0);
|
||||
cObj->_oldSize = Common::Point(0, 0);
|
||||
cObj->_goto = Common::Point(0, 0);
|
||||
cObj->_status = 0;
|
||||
cObj->_misc = 0;
|
||||
cObj->_imageFrame = nullptr;
|
||||
|
||||
if (cAnim._name.size() > 0 && cAnim._type != NO_SHAPE) {
|
||||
if (tpPos.x != -1)
|
||||
people[HOLMES]._type = REMOVE;
|
||||
|
||||
Common::Path fname(cAnim._name + ".vgs");
|
||||
if (!res.isInCache(fname)) {
|
||||
// Set up RRM scene data
|
||||
Common::SeekableReadStream *roomStream = res.load(_roomFilename);
|
||||
roomStream->seek(cAnim._dataOffset);
|
||||
//rrmStream->seek(44 + cAnimNum * 4);
|
||||
//rrmStream->seek(rrmStream->readUint32LE());
|
||||
|
||||
// Load the canimation into the cache
|
||||
Common::SeekableReadStream *imgStream = !_compressed ? roomStream->readStream(cAnim._dataSize) :
|
||||
Resources::decompressLZ(*roomStream, cAnim._dataSize);
|
||||
res.addToCache(fname, *imgStream);
|
||||
|
||||
delete imgStream;
|
||||
delete roomStream;
|
||||
}
|
||||
|
||||
// Now load the resource as an image
|
||||
if (!IS_3DO) {
|
||||
cObj->_images = new ImageFile(fname);
|
||||
} else {
|
||||
cObj->_images = new ImageFile3DO(fname, kImageFile3DOType_RoomFormat);
|
||||
}
|
||||
cObj->_imageFrame = &(*cObj->_images)[0];
|
||||
cObj->_maxFrames = cObj->_images->size();
|
||||
|
||||
int frames = 0;
|
||||
if (playRate < 0) {
|
||||
// Reverse direction
|
||||
// Count number of frames
|
||||
while (frames < MAX_FRAME && cObj->_sequences[frames])
|
||||
++frames;
|
||||
} else {
|
||||
// Forward direction
|
||||
BaseObject::_countCAnimFrames = true;
|
||||
|
||||
while (cObj->_type == ACTIVE_BG_SHAPE) {
|
||||
cObj->checkObject();
|
||||
++frames;
|
||||
|
||||
if (frames >= 1000)
|
||||
error("CAnim has infinite loop sequence");
|
||||
}
|
||||
|
||||
if (frames > 1)
|
||||
--frames;
|
||||
|
||||
BaseObject::_countCAnimFrames = false;
|
||||
|
||||
cObj->_type = cAnim._type;
|
||||
cObj->_frameNumber = -1;
|
||||
cObj->_position = cAnim._position;
|
||||
cObj->_delta = Common::Point(0, 0);
|
||||
}
|
||||
|
||||
// Return if animation has no frames in it
|
||||
if (frames == 0)
|
||||
return -2;
|
||||
|
||||
++frames;
|
||||
int repeat = ABS(playRate);
|
||||
int dir;
|
||||
|
||||
if (playRate < 0) {
|
||||
// Play in reverse
|
||||
dir = -2;
|
||||
cObj->_frameNumber = frames - 3;
|
||||
} else {
|
||||
dir = 0;
|
||||
}
|
||||
|
||||
tFrames = frames - 1;
|
||||
int pauseFrame = (_cAnimFramePause) ? frames - _cAnimFramePause : -1;
|
||||
|
||||
while (--frames) {
|
||||
if (frames == pauseFrame)
|
||||
ui.printObjectDesc();
|
||||
|
||||
doBgAnim();
|
||||
|
||||
// Repeat same frame
|
||||
int temp = repeat;
|
||||
while (--temp > 0) {
|
||||
cObj->_frameNumber--;
|
||||
doBgAnim();
|
||||
|
||||
if (_vm->shouldQuit())
|
||||
return 0;
|
||||
}
|
||||
|
||||
cObj->_frameNumber += dir;
|
||||
}
|
||||
|
||||
people[HOLMES]._type = CHARACTER;
|
||||
}
|
||||
|
||||
// Teleport to ending coordinates if necessary
|
||||
if (tpPos.x != -1) {
|
||||
people[HOLMES]._position = tpPos; // Place the player
|
||||
people[HOLMES]._sequenceNumber = tpDir;
|
||||
people[HOLMES].gotoStand();
|
||||
}
|
||||
|
||||
if (playRate < 0)
|
||||
// Reverse direction - set to end sequence
|
||||
cObj->_frameNumber = tFrames - 1;
|
||||
|
||||
if (cObj->_frameNumber <= 26)
|
||||
gotoCode = cObj->_sequences[cObj->_frameNumber + 3];
|
||||
|
||||
// Unless anim shape has already been removed, do a final check to allow it to become REMOVEd
|
||||
for (uint idx = 0; idx < _canimShapes.size(); ++idx) {
|
||||
if (_canimShapes[idx] == cObj) {
|
||||
cObj->checkObject();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (gotoCode > 0 && !talk._talkToAbort) {
|
||||
_goToScene = gotoCode;
|
||||
|
||||
if (_goToScene < 97 && map[_goToScene].x) {
|
||||
map._overPos = map[_goToScene];
|
||||
}
|
||||
}
|
||||
|
||||
people.loadWalk();
|
||||
|
||||
if (tpPos.x != -1 && !talk._talkToAbort) {
|
||||
// Teleport to ending coordinates
|
||||
people[HOLMES]._position = tpPos;
|
||||
people[HOLMES]._sequenceNumber = tpDir;
|
||||
|
||||
people[HOLMES].gotoStand();
|
||||
}
|
||||
|
||||
events.setCursor(oldCursor);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ScalpelScene::closestZone(const Common::Point &pt) {
|
||||
int dist = 1000;
|
||||
int zone = -1;
|
||||
|
||||
for (uint idx = 0; idx < _zones.size(); ++idx) {
|
||||
Common::Point zc((_zones[idx].left + _zones[idx].right) / 2,
|
||||
(_zones[idx].top + _zones[idx].bottom) / 2);
|
||||
int d = ABS(zc.x - pt.x) + ABS(zc.y - pt.y);
|
||||
|
||||
if (d < dist) {
|
||||
// Found a closer zone
|
||||
dist = d;
|
||||
zone = idx;
|
||||
}
|
||||
}
|
||||
|
||||
return zone;
|
||||
}
|
||||
|
||||
int ScalpelScene::findBgShape(const Common::Point &pt) {
|
||||
if (!_doBgAnimDone)
|
||||
// New frame hasn't been drawn yet
|
||||
return -1;
|
||||
|
||||
for (int idx = (int)_bgShapes.size() - 1; idx >= 0; --idx) {
|
||||
Object &o = _bgShapes[idx];
|
||||
if (o._type != INVALID && o._type != NO_SHAPE && o._type != HIDDEN
|
||||
&& o._type != REMOVE && o._aType <= PERSON) {
|
||||
if (o.getNewBounds().contains(pt))
|
||||
return idx;
|
||||
} else if (o._type == NO_SHAPE) {
|
||||
if (o.getNoShapeBounds().contains(pt))
|
||||
return idx;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
} // End of namespace Scalpel
|
||||
|
||||
} // End of namespace Sherlock
|
||||
105
engines/sherlock/scalpel/scalpel_scene.h
Normal file
105
engines/sherlock/scalpel/scalpel_scene.h
Normal 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 SHERLOCK_SCALPEL_SCENE_H
|
||||
#define SHERLOCK_SCALPEL_SCENE_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/array.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/serializer.h"
|
||||
#include "sherlock/objects.h"
|
||||
#include "sherlock/scene.h"
|
||||
#include "sherlock/screen.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
namespace Scalpel {
|
||||
|
||||
extern const int FS_TRANS[8];
|
||||
|
||||
enum { BLACKWOOD_CAPTURE = 2, BAKER_STREET = 4, DRAWING_ROOM = 12, STATION = 17, PUB_INTERIOR = 19,
|
||||
LAWYER_OFFICE = 27, BAKER_ST_EXTERIOR = 39, RESCUE_ANNA = 52, MOOREHEAD_DEATH = 53, EXIT_GAME = 55,
|
||||
BRUMWELL_SUICIDE = 70, OVERHEAD_MAP2 = 98, DARTS_GAME = 99, OVERHEAD_MAP = 100 };
|
||||
|
||||
class ScalpelScene : public Scene {
|
||||
private:
|
||||
void doBgAnimCheckCursor();
|
||||
protected:
|
||||
/**
|
||||
* Loads the data associated for a given scene. The room resource file's format is:
|
||||
* BGHEADER: Holds an index for the rest of the file
|
||||
* STRUCTS: The objects for the scene
|
||||
* IMAGES: The graphic information for the structures
|
||||
*
|
||||
* The _misc field of the structures contains the number of the graphic image
|
||||
* that it should point to after loading; _misc is then set to 0.
|
||||
*/
|
||||
bool loadScene(const Common::Path &filename) override;
|
||||
|
||||
/**
|
||||
* Checks all the background shapes. If a background shape is animating,
|
||||
* it will flag it as needing to be drawn. If a non-animating shape is
|
||||
* colliding with another shape, it will also flag it as needing drawing
|
||||
*/
|
||||
void checkBgShapes() override;
|
||||
|
||||
/**
|
||||
* Draw all the shapes, people and NPCs in the correct order
|
||||
*/
|
||||
void drawAllShapes() override;
|
||||
|
||||
/**
|
||||
* Returns the index of the closest zone to a given point.
|
||||
*/
|
||||
int closestZone(const Common::Point &pt) override;
|
||||
public:
|
||||
ScalpelScene(SherlockEngine *vm) : Scene(vm) {}
|
||||
|
||||
~ScalpelScene() override;
|
||||
|
||||
/**
|
||||
* Draw all objects and characters.
|
||||
*/
|
||||
void doBgAnim() override;
|
||||
|
||||
/**
|
||||
* Attempt to start a canimation sequence. It will load the requisite graphics, and
|
||||
* then copy the canim object into the _canimShapes array to start the animation.
|
||||
*
|
||||
* @param cAnimNum The canim object within the current scene
|
||||
* @param playRate Play rate. 0 is invalid; 1=normal speed, 2=1/2 speed, etc.
|
||||
* A negative playRate can also be specified to play the animation in reverse
|
||||
*/
|
||||
int startCAnim(int cAnimNum, int playRate = 1) override;
|
||||
|
||||
/**
|
||||
* Attempts to find a background shape within the passed bounds. If found,
|
||||
* it will return the shape number, or -1 on failure.
|
||||
*/
|
||||
int findBgShape(const Common::Point &pt) override;
|
||||
};
|
||||
|
||||
} // End of namespace Scalpel
|
||||
|
||||
} // End of namespace Sherlock
|
||||
|
||||
#endif
|
||||
137
engines/sherlock/scalpel/scalpel_screen.cpp
Normal file
137
engines/sherlock/scalpel/scalpel_screen.cpp
Normal file
@@ -0,0 +1,137 @@
|
||||
/* 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 "sherlock/scalpel/scalpel_screen.h"
|
||||
#include "sherlock/scalpel/scalpel.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
namespace Scalpel {
|
||||
|
||||
ScalpelScreen::ScalpelScreen(SherlockEngine *vm) : Screen(vm) {
|
||||
_backBuffer1.create(320, 200, g_system->getScreenFormat());
|
||||
_backBuffer2.create(320, 200, g_system->getScreenFormat());
|
||||
activateBackBuffer1();
|
||||
}
|
||||
|
||||
void ScalpelScreen::makeButton(const Common::Rect &bounds, const Common::Point &textPoint,
|
||||
const Common::String &buttonText, bool textContainsHotkey) {
|
||||
|
||||
Surface &bb = _backBuffer;
|
||||
bb.fillRect(Common::Rect(bounds.left, bounds.top, bounds.right, bounds.top + 1), BUTTON_TOP);
|
||||
bb.fillRect(Common::Rect(bounds.left, bounds.top, bounds.left + 1, bounds.bottom), BUTTON_TOP);
|
||||
bb.fillRect(Common::Rect(bounds.right - 1, bounds.top, bounds.right, bounds.bottom), BUTTON_BOTTOM);
|
||||
bb.fillRect(Common::Rect(bounds.left + 1, bounds.bottom - 1, bounds.right, bounds.bottom), BUTTON_BOTTOM);
|
||||
bb.fillRect(Common::Rect(bounds.left + 1, bounds.top + 1, bounds.right - 1, bounds.bottom - 1), BUTTON_MIDDLE);
|
||||
|
||||
buttonPrint(textPoint, COMMAND_FOREGROUND, false, buttonText, textContainsHotkey);
|
||||
}
|
||||
|
||||
void ScalpelScreen::makeButton(const Common::Rect &bounds, int textX,
|
||||
const Common::String &buttonText, bool textContainsHotkey) {
|
||||
makeButton(bounds, Common::Point(textX, bounds.top), buttonText, textContainsHotkey);
|
||||
}
|
||||
|
||||
// ButtonText is supposed to have its hotkey as a prefix. The hotkey will get highlighted.
|
||||
void ScalpelScreen::buttonPrint(const Common::Point &pt, uint color, bool slamIt,
|
||||
const Common::String &buttonText, bool textContainsHotkey) {
|
||||
int xStart = pt.x;
|
||||
int skipTextOffset = textContainsHotkey ? +1 : 0; // skip first char in case text contains hotkey
|
||||
|
||||
// Center text around given x-coordinate
|
||||
if (textContainsHotkey) {
|
||||
xStart -= (stringWidth(Common::String(buttonText.c_str() + 1)) / 2);
|
||||
} else {
|
||||
xStart -= (stringWidth(buttonText) / 2);
|
||||
}
|
||||
|
||||
if (color == COMMAND_FOREGROUND) {
|
||||
uint16 prefixOffsetX = 0;
|
||||
byte hotkey = buttonText[0];
|
||||
|
||||
// Hotkey needs to be highlighted
|
||||
if (textContainsHotkey) {
|
||||
Common::String prefixText = Common::String(buttonText.c_str() + 1);
|
||||
uint16 prefixTextLen = prefixText.size();
|
||||
uint16 prefixTextPos = 0;
|
||||
|
||||
// Hotkey was passed additionally, we search for the hotkey inside the button text and
|
||||
// remove it from there. We then draw the whole text as highlighted and afterward
|
||||
// the processed text again as regular text (without the hotkey)
|
||||
while (prefixTextPos < prefixTextLen) {
|
||||
if (prefixText[prefixTextPos] == hotkey) {
|
||||
// Hotkey found, remove remaining text
|
||||
while (prefixTextPos < prefixText.size()) {
|
||||
prefixText.deleteLastChar();
|
||||
}
|
||||
break;
|
||||
}
|
||||
prefixTextPos++;
|
||||
}
|
||||
|
||||
if (prefixTextPos < prefixTextLen) {
|
||||
// only adjust in case hotkey character was actually found
|
||||
prefixOffsetX = stringWidth(prefixText);
|
||||
}
|
||||
}
|
||||
|
||||
if (slamIt) {
|
||||
print(Common::Point(xStart, pt.y + 1),
|
||||
COMMAND_FOREGROUND, "%s", buttonText.c_str() + skipTextOffset);
|
||||
if (textContainsHotkey)
|
||||
print(Common::Point(xStart + prefixOffsetX, pt.y + 1), COMMAND_HIGHLIGHTED, "%c", hotkey);
|
||||
} else {
|
||||
gPrint(Common::Point(xStart, pt.y),
|
||||
COMMAND_FOREGROUND, "%s", buttonText.c_str() + skipTextOffset);
|
||||
if (textContainsHotkey)
|
||||
gPrint(Common::Point(xStart + prefixOffsetX, pt.y), COMMAND_HIGHLIGHTED, "%c", hotkey);
|
||||
}
|
||||
} else if (slamIt) {
|
||||
print(Common::Point(xStart, pt.y + 1), color, "%s", buttonText.c_str() + skipTextOffset);
|
||||
} else {
|
||||
gPrint(Common::Point(xStart, pt.y), color, "%s", buttonText.c_str() + skipTextOffset);
|
||||
}
|
||||
}
|
||||
|
||||
void ScalpelScreen::makePanel(const Common::Rect &r) {
|
||||
_backBuffer.fillRect(r, BUTTON_MIDDLE);
|
||||
_backBuffer.hLine(r.left, r.top, r.right - 2, BUTTON_TOP);
|
||||
_backBuffer.hLine(r.left + 1, r.top + 1, r.right - 3, BUTTON_TOP);
|
||||
_backBuffer.vLine(r.left, r.top, r.bottom - 1, BUTTON_TOP);
|
||||
_backBuffer.vLine(r.left + 1, r.top + 1, r.bottom - 2, BUTTON_TOP);
|
||||
|
||||
_backBuffer.vLine(r.right - 1, r.top, r.bottom - 1, BUTTON_BOTTOM);
|
||||
_backBuffer.vLine(r.right - 2, r.top + 1, r.bottom - 2, BUTTON_BOTTOM);
|
||||
_backBuffer.hLine(r.left, r.bottom - 1, r.right - 1, BUTTON_BOTTOM);
|
||||
_backBuffer.hLine(r.left + 1, r.bottom - 2, r.right - 1, BUTTON_BOTTOM);
|
||||
}
|
||||
|
||||
void ScalpelScreen::makeField(const Common::Rect &r) {
|
||||
_backBuffer.fillRect(r, BUTTON_MIDDLE);
|
||||
_backBuffer.hLine(r.left, r.top, r.right - 1, BUTTON_BOTTOM);
|
||||
_backBuffer.hLine(r.left + 1, r.bottom - 1, r.right - 1, BUTTON_TOP);
|
||||
_backBuffer.vLine(r.left, r.top + 1, r.bottom - 1, BUTTON_BOTTOM);
|
||||
_backBuffer.vLine(r.right - 1, r.top + 1, r.bottom - 2, BUTTON_TOP);
|
||||
}
|
||||
|
||||
} // End of namespace Scalpel
|
||||
|
||||
} // End of namespace Sherlock
|
||||
68
engines/sherlock/scalpel/scalpel_screen.h
Normal file
68
engines/sherlock/scalpel/scalpel_screen.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/* 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 SHERLOCK_SCALPEL_SCREEN_H
|
||||
#define SHERLOCK_SCALPEL_SCREEN_H
|
||||
|
||||
#include "sherlock/screen.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
class SherlockEngine;
|
||||
|
||||
namespace Scalpel {
|
||||
|
||||
class ScalpelScreen : public Screen {
|
||||
public:
|
||||
ScalpelScreen(SherlockEngine *vm);
|
||||
~ScalpelScreen() override {}
|
||||
|
||||
/**
|
||||
* Draws a button for use in the inventory, talk, and examine dialogs.
|
||||
* ButtonText is supposed to have its hotkey as a prefix. The hotkey will get highlighted.
|
||||
*/
|
||||
void makeButton(const Common::Rect &bounds, const Common::Point &textPoint, const Common::String &buttonText, bool textContainsHotkey = true);
|
||||
void makeButton(const Common::Rect &bounds, int textX, const Common::String &buttonText, bool textContainsHotkey = true);
|
||||
|
||||
/**
|
||||
* Prints an interface command with the first letter highlighted to indicate
|
||||
* what keyboard shortcut is associated with it
|
||||
* ButtonText is supposed to have its hotkey as a prefix. The hotkey will get highlighted.
|
||||
*/
|
||||
void buttonPrint(const Common::Point &pt, uint color, bool slamIt, const Common::String &buttonText, bool textContainsHotkey = true);
|
||||
|
||||
/**
|
||||
* Draw a panel in the back buffer with a raised area effect around the edges
|
||||
*/
|
||||
void makePanel(const Common::Rect &r);
|
||||
|
||||
/**
|
||||
* Draw a field in the back buffer with a raised area effect around the edges,
|
||||
* suitable for text input.
|
||||
*/
|
||||
void makeField(const Common::Rect &r);
|
||||
};
|
||||
|
||||
} // End of namespace Scalpel
|
||||
|
||||
} // End of namespace Sherlock
|
||||
|
||||
#endif
|
||||
1025
engines/sherlock/scalpel/scalpel_talk.cpp
Normal file
1025
engines/sherlock/scalpel/scalpel_talk.cpp
Normal file
File diff suppressed because it is too large
Load Diff
168
engines/sherlock/scalpel/scalpel_talk.h
Normal file
168
engines/sherlock/scalpel/scalpel_talk.h
Normal file
@@ -0,0 +1,168 @@
|
||||
/* 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 SHERLOCK_SCALPEL_TALK_H
|
||||
#define SHERLOCK_SCALPEL_TALK_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/array.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/serializer.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/stack.h"
|
||||
#include "sherlock/talk.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
namespace Scalpel {
|
||||
|
||||
class ScalpelTalk : public Talk {
|
||||
private:
|
||||
Common::Stack<SequenceEntry> _sequenceStack;
|
||||
|
||||
/**
|
||||
* Get the center position for the current speaker, if any
|
||||
*/
|
||||
Common::Point get3doPortraitPosition() const;
|
||||
|
||||
OpcodeReturn cmdSwitchSpeaker(const byte *&str);
|
||||
OpcodeReturn cmdAssignPortraitLocation(const byte *&str);
|
||||
OpcodeReturn cmdGotoScene(const byte *&str);
|
||||
OpcodeReturn cmdCallTalkFile(const byte *&str);
|
||||
OpcodeReturn cmdClearInfoLine(const byte *&str);
|
||||
OpcodeReturn cmdClearWindow(const byte *&str);
|
||||
OpcodeReturn cmdDisplayInfoLine(const byte *&str);
|
||||
OpcodeReturn cmdElse(const byte *&str);
|
||||
OpcodeReturn cmdIf(const byte *&str);
|
||||
OpcodeReturn cmdMoveMouse(const byte *&str);
|
||||
OpcodeReturn cmdPlayPrologue(const byte *&str);
|
||||
OpcodeReturn cmdRemovePortrait(const byte *&str);
|
||||
OpcodeReturn cmdSfxCommand(const byte *&str);
|
||||
OpcodeReturn cmdSummonWindow(const byte *&str);
|
||||
OpcodeReturn cmdWalkToCoords(const byte *&str);
|
||||
protected:
|
||||
/**
|
||||
* Display the talk interface window
|
||||
*/
|
||||
void talkInterface(const byte *&str) override;
|
||||
|
||||
/**
|
||||
* Pause when displaying a talk dialog on-screen
|
||||
*/
|
||||
void talkWait(const byte *&str) override;
|
||||
|
||||
/**
|
||||
* Called when the active speaker is switched
|
||||
*/
|
||||
void switchSpeaker() override;
|
||||
|
||||
/**
|
||||
* Called when a character being spoken to has no talk options to display
|
||||
*/
|
||||
void nothingToSay() override;
|
||||
|
||||
/**
|
||||
* Show the talk display
|
||||
*/
|
||||
void showTalk() override;
|
||||
public:
|
||||
ScalpelTalk(SherlockEngine *vm);
|
||||
~ScalpelTalk() override {}
|
||||
|
||||
Common::String _fixedTextWindowExit;
|
||||
Common::String _fixedTextWindowUp;
|
||||
Common::String _fixedTextWindowDown;
|
||||
|
||||
/**
|
||||
* Opens the talk file 'talk.tlk' and searches the index for the specified
|
||||
* conversation. If found, the data for that conversation is loaded
|
||||
*/
|
||||
void loadTalkFile(const Common::String &filename) override;
|
||||
|
||||
/**
|
||||
* Called whenever a conversation or item script needs to be run. For standard conversations,
|
||||
* it opens up a description window similar to how 'talk' does, but shows a 'reply' directly
|
||||
* instead of waiting for a statement option.
|
||||
* @remarks It seems that at some point, all item scripts were set up to use this as well.
|
||||
* In their case, the conversation display is simply suppressed, and control is passed on to
|
||||
* doScript to implement whatever action is required.
|
||||
*/
|
||||
void talkTo(const Common::String &filename) override;
|
||||
|
||||
/**
|
||||
* When the talk window has been displayed, waits a period of time proportional to
|
||||
* the amount of text that's been displayed
|
||||
*/
|
||||
int waitForMore(int delay) override;
|
||||
|
||||
/**
|
||||
* Draws the interface for conversation display
|
||||
*/
|
||||
void drawInterface() override;
|
||||
|
||||
/**
|
||||
* Display a list of statements in a window at the bottom of the screen that the
|
||||
* player can select from.
|
||||
*/
|
||||
bool displayTalk(bool slamIt) override;
|
||||
|
||||
/**
|
||||
* Prints a single conversation option in the interface window
|
||||
*/
|
||||
int talkLine(int lineNum, int stateNum, byte color, int lineY, bool slamIt) override;
|
||||
|
||||
/**
|
||||
* Trigger to play a 3DO talk dialog movie
|
||||
*/
|
||||
bool talk3DOMovieTrigger(int subIndex);
|
||||
|
||||
/**
|
||||
* Handles skipping over bad text in conversations
|
||||
*/
|
||||
static void skipBadText(const byte *&msgP);
|
||||
|
||||
/**
|
||||
* Push the details of a passed object onto the saved sequences stack
|
||||
*/
|
||||
void pushSequenceEntry(Object *obj) override;
|
||||
|
||||
/**
|
||||
* Pulls a background object sequence from the sequence stack and restore's the
|
||||
* object's sequence
|
||||
*/
|
||||
void pullSequence(int slot = -1) override;
|
||||
|
||||
/**
|
||||
* Returns true if the script stack is empty
|
||||
*/
|
||||
bool isSequencesEmpty() const override { return _sequenceStack.empty(); }
|
||||
|
||||
/**
|
||||
* Clears the stack of pending object sequences associated with speakers in the scene
|
||||
*/
|
||||
void clearSequences() override;
|
||||
};
|
||||
|
||||
} // End of namespace Scalpel
|
||||
|
||||
} // End of namespace Sherlock
|
||||
|
||||
#endif
|
||||
2369
engines/sherlock/scalpel/scalpel_user_interface.cpp
Normal file
2369
engines/sherlock/scalpel/scalpel_user_interface.cpp
Normal file
File diff suppressed because it is too large
Load Diff
247
engines/sherlock/scalpel/scalpel_user_interface.h
Normal file
247
engines/sherlock/scalpel/scalpel_user_interface.h
Normal file
@@ -0,0 +1,247 @@
|
||||
/* 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 SHERLOCK_SCALPEL_UI_H
|
||||
#define SHERLOCK_SCALPEL_UI_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "sherlock/user_interface.h"
|
||||
|
||||
#include "common/events.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
class Inventory;
|
||||
class Talk;
|
||||
|
||||
namespace Scalpel {
|
||||
|
||||
extern const int MENU_POINTS[12][4];
|
||||
|
||||
extern const int INVENTORY_POINTS[8][3];
|
||||
|
||||
enum {
|
||||
MAINBUTTON_LOOK = 0,
|
||||
MAINBUTTON_MOVE,
|
||||
MAINBUTTON_TALK,
|
||||
MAINBUTTON_PICKUP,
|
||||
MAINBUTTON_OPEN,
|
||||
MAINBUTTON_CLOSE,
|
||||
MAINBUTTON_INVENTORY,
|
||||
MAINBUTTON_USE,
|
||||
MAINBUTTON_GIVE,
|
||||
MAINBUTTON_JOURNAL,
|
||||
MAINBUTTON_FILES,
|
||||
MAINBUTTON_SETUP,
|
||||
MAINBUTTON_LOADGAME,
|
||||
MAINBUTTON_SAVEGAME
|
||||
};
|
||||
|
||||
class Settings;
|
||||
|
||||
class ScalpelUserInterface: public UserInterface {
|
||||
friend class Settings;
|
||||
friend class Sherlock::Talk;
|
||||
private:
|
||||
char _keyPress;
|
||||
Common::CustomEventType _actionPress;
|
||||
int _lookHelp;
|
||||
int _help, _oldHelp;
|
||||
int _key;
|
||||
Common::CustomEventType _action, _oldAction;
|
||||
int _temp, _oldTemp; // button number (0-11)
|
||||
int _oldLook;
|
||||
bool _keyboardInput;
|
||||
bool _actionInput;
|
||||
bool _pause;
|
||||
int _cNum;
|
||||
Common::String _cAnimStr;
|
||||
Common::String _descStr;
|
||||
int _find;
|
||||
private:
|
||||
/**
|
||||
* Draws the image for a user interface button in the down/pressed state.
|
||||
*/
|
||||
void depressButton(int num);
|
||||
|
||||
/**
|
||||
* If he mouse button is pressed, then calls depressButton to draw the button
|
||||
* as pressed; if not, it will show it as released with a call to "restoreButton".
|
||||
*/
|
||||
void pushButton(int num);
|
||||
|
||||
/**
|
||||
* By the time this method has been called, the graphics for the button change
|
||||
* have already been drawn. This simply takes care of switching the mode around
|
||||
* accordingly
|
||||
*/
|
||||
void toggleButton(uint16 num);
|
||||
|
||||
/**
|
||||
* Print the name of an object in the scene
|
||||
*/
|
||||
void lookScreen(const Common::Point &pt);
|
||||
|
||||
/**
|
||||
* Gets the item in the inventory the mouse is on and display's its description
|
||||
*/
|
||||
void lookInv();
|
||||
|
||||
/**
|
||||
* Handles input when the file list window is being displayed
|
||||
*/
|
||||
void doEnvControl();
|
||||
|
||||
/**
|
||||
* Handle input whilst the inventory is active
|
||||
*/
|
||||
void doInvControl();
|
||||
|
||||
/**
|
||||
* Handles waiting whilst an object's description window is open.
|
||||
*/
|
||||
void doLookControl();
|
||||
|
||||
/**
|
||||
* Handles input until one of the user interface buttons/commands is selected
|
||||
*/
|
||||
void doMainControl();
|
||||
|
||||
/**
|
||||
* Handles the input for the MOVE, OPEN, and CLOSE commands
|
||||
*/
|
||||
void doMiscControl(int allowed);
|
||||
|
||||
/**
|
||||
* Handles input for picking up items
|
||||
*/
|
||||
void doPickControl();
|
||||
|
||||
/**
|
||||
* Handles input when in talk mode. It highlights the buttons and available statements,
|
||||
* and handles allowing the user to click on them
|
||||
*/
|
||||
void doTalkControl();
|
||||
|
||||
/**
|
||||
* Handles events when the Journal is active.
|
||||
* @remarks Whilst this would in theory be better in the Journal class, since it displays in
|
||||
* the user interface, it uses so many internal UI fields, that it sort of made some sense
|
||||
* to put it in the UserInterface class.
|
||||
*/
|
||||
void journalControl();
|
||||
|
||||
/**
|
||||
* Checks to see whether a USE action is valid on the given object
|
||||
*/
|
||||
void checkUseAction(const UseType *use, const Common::String &invName, FixedTextActionId fixedTextActionId,
|
||||
int objNum, bool giveMode);
|
||||
|
||||
/**
|
||||
* Print the previously selected object's decription
|
||||
*/
|
||||
void printObjectDesc(const Common::String &str, bool firstTime);
|
||||
public:
|
||||
ImageFile *_controlPanel;
|
||||
ImageFile *_controls;
|
||||
int _oldUse;
|
||||
|
||||
Common::CustomEventType _actionsIndexed[14];
|
||||
|
||||
public:
|
||||
ScalpelUserInterface(SherlockEngine *vm);
|
||||
~ScalpelUserInterface() override;
|
||||
|
||||
/**
|
||||
* Handles counting down whilst checking for input, then clears the info line.
|
||||
*/
|
||||
void whileMenuCounter();
|
||||
|
||||
/**
|
||||
* Draws the image for the given user interface button in the up
|
||||
* (not selected) position
|
||||
*/
|
||||
void restoreButton(int num);
|
||||
|
||||
/**
|
||||
* Creates a text window and uses it to display the in-depth description
|
||||
* of the highlighted object
|
||||
*/
|
||||
void examine();
|
||||
|
||||
Common::Point getTopLeftButtonPoint(int num) const;
|
||||
Common::Rect getButtonRect(int buttonNr) const;
|
||||
int infoLineHeight() const;
|
||||
int infoLineYOffset() const;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Resets the user interface
|
||||
*/
|
||||
void reset() override;
|
||||
|
||||
/**
|
||||
* Main input handler for the user interface
|
||||
*/
|
||||
void handleInput() override;
|
||||
|
||||
/**
|
||||
* Draw the user interface onto the screen's back buffers
|
||||
*/
|
||||
void drawInterface(int bufferNum = 3) override;
|
||||
|
||||
/**
|
||||
* Displays a passed window by gradually scrolling it vertically on-screen
|
||||
*/
|
||||
void summonWindow(const Surface &bgSurface, bool slideUp = true) override;
|
||||
|
||||
/**
|
||||
* Slide the window stored in the back buffer onto the screen
|
||||
*/
|
||||
void summonWindow(bool slideUp = true, int height = CONTROLS_Y) override;
|
||||
|
||||
/**
|
||||
* Close a currently open window
|
||||
* @param flag 0 = slide old window down, 1 = slide prior UI back up
|
||||
*/
|
||||
void banishWindow(bool slideUp = true) override;
|
||||
|
||||
/**
|
||||
* Clears the info line of the screen
|
||||
*/
|
||||
void clearInfo() override;
|
||||
|
||||
/**
|
||||
* Clear any active text window
|
||||
*/
|
||||
void clearWindow() override;
|
||||
|
||||
/**
|
||||
* Print the previously selected object's decription
|
||||
*/
|
||||
virtual void printObjectDesc();
|
||||
};
|
||||
|
||||
} // End of namespace Scalpel
|
||||
|
||||
} // End of namespace Sherlock
|
||||
|
||||
#endif
|
||||
468
engines/sherlock/scalpel/settings.cpp
Normal file
468
engines/sherlock/scalpel/settings.cpp
Normal file
@@ -0,0 +1,468 @@
|
||||
/* 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 "sherlock/sherlock.h"
|
||||
#include "sherlock/scalpel/settings.h"
|
||||
#include "sherlock/scalpel/scalpel_screen.h"
|
||||
#include "sherlock/scalpel/scalpel_user_interface.h"
|
||||
#include "sherlock/scalpel/scalpel_fixed_text.h"
|
||||
#include "sherlock/scalpel/scalpel.h"
|
||||
|
||||
#include "backends/keymapper/keymapper.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
namespace Scalpel {
|
||||
|
||||
static const int SETUP_POINTS_INTL[12][4] = {
|
||||
{ 4, 154, 101, 53 }, // Exit
|
||||
{ 4, 165, 101, 53 }, // Music Toggle
|
||||
{ 219, 165, 316, 268 }, // Voice Toggle
|
||||
{ 103, 165, 217, 160 }, // Sound Effects Toggle
|
||||
{ 219, 154, 316, 268 }, // Help Button Left/Right
|
||||
{ 103, 154, 217, 160 }, // New Font Style
|
||||
{ 4, 187, 101, 53 }, // Joystick Toggle
|
||||
{ 103, 187, 217, 160 }, // Calibrate Joystick
|
||||
{ 219, 176, 316, 268 }, // Fade Style
|
||||
{ 103, 176, 217, 160 }, // Window Open Style
|
||||
{ 4, 176, 101, 53 }, // Portraits Toggle
|
||||
{ 219, 187, 316, 268 } // _key Pad Accel. Toggle
|
||||
};
|
||||
|
||||
// Different from original to accommodate hotkeys
|
||||
static const int SETUP_POINTS_ZH[12][4] = {
|
||||
{ 3, 159, 73, 38 }, // Exit // OK
|
||||
{ 3, 178, 73, 38 }, // Music Toggle // OK
|
||||
{ 0, 0, 0, 0 }, // Voice Toggle
|
||||
{ 74, 178, 145, 109 }, // Sound Effects Toggle // OK
|
||||
{ 146, 178, 233, 191 }, // Help Button Left/Right // OK
|
||||
{ 0, 0, 0, 0 }, // New Font Style // OK
|
||||
{ 0, 0, 0, 0 }, // Joystick Toggle // OK
|
||||
{ 0, 0, 0, 0 }, // Calibrate Joystick // OK
|
||||
{ 234, 159, 317, 276 }, // Fade Style // OK
|
||||
{ 146, 159, 233, 191 }, // Window Open Style // OK
|
||||
{ 74, 159, 145, 109 }, // Portraits Toggle // OK
|
||||
{ 234, 178, 317, 276 } // _key Pad Accel. Toggle
|
||||
};
|
||||
|
||||
/*----------------------------------------------------------------*/
|
||||
|
||||
bool Settings::doesButtonExist(int num) const {
|
||||
if (_vm->getLanguage() == Common::Language::ZH_TWN)
|
||||
return num != 2 && num != 5 && num != 6 && num != 7;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Settings::makeButtonNumDisabled(int num, const Common::String &s) {
|
||||
if (!doesButtonExist(num))
|
||||
return;
|
||||
|
||||
ScalpelScreen &screen = *(ScalpelScreen *)_vm->_screen;
|
||||
makeButtonNum(num, s);
|
||||
screen.buttonPrint(getButtonTextPoint(num), COMMAND_NULL, false, s);
|
||||
}
|
||||
|
||||
Common::Rect Settings::getButtonRect(int num) const {
|
||||
if (_vm->getLanguage() == Common::Language::ZH_TWN) {
|
||||
return Common::Rect(SETUP_POINTS_ZH[num][0], SETUP_POINTS_ZH[num][1],
|
||||
SETUP_POINTS_ZH[num][2], SETUP_POINTS_ZH[num][1] + 19);
|
||||
} else {
|
||||
return Common::Rect(SETUP_POINTS_INTL[num][0], SETUP_POINTS_INTL[num][1],
|
||||
SETUP_POINTS_INTL[num][2], SETUP_POINTS_INTL[num][1] + 10);
|
||||
}
|
||||
}
|
||||
|
||||
void Settings::makeButtonNum(int num, const Common::String &s) {
|
||||
if (!doesButtonExist(num))
|
||||
return;
|
||||
|
||||
ScalpelScreen &screen = *(ScalpelScreen *)_vm->_screen;
|
||||
screen.makeButton(getButtonRect(num), getButtonTextPoint(num), s);
|
||||
}
|
||||
|
||||
Common::Point Settings::getButtonTextPoint(int num) const {
|
||||
if (_vm->getLanguage() == Common::Language::ZH_TWN) {
|
||||
return Common::Point(SETUP_POINTS_ZH[num][3], SETUP_POINTS_ZH[num][1] + 2);
|
||||
} else {
|
||||
return Common::Point(SETUP_POINTS_INTL[num][3], SETUP_POINTS_INTL[num][1]);
|
||||
}
|
||||
}
|
||||
|
||||
void Settings::drawInterface(bool flag) {
|
||||
People &people = *_vm->_people;
|
||||
ScalpelScreen &screen = *(ScalpelScreen *)_vm->_screen;
|
||||
Sound &sound = *_vm->_sound;
|
||||
Music &music = *_vm->_music;
|
||||
UserInterface &ui = *_vm->_ui;
|
||||
Common::String tempStr;
|
||||
|
||||
if (!flag) {
|
||||
screen._backBuffer1.fillRect(Common::Rect(0, CONTROLS_Y1, SHERLOCK_SCREEN_WIDTH, CONTROLS_Y1 + 1), BORDER_COLOR);
|
||||
screen._backBuffer1.fillRect(Common::Rect(0, CONTROLS_Y1 + 1, 2, SHERLOCK_SCREEN_HEIGHT), BORDER_COLOR);
|
||||
screen._backBuffer1.fillRect(Common::Rect(SHERLOCK_SCREEN_WIDTH - 2, CONTROLS_Y1 + 1, SHERLOCK_SCREEN_WIDTH,
|
||||
SHERLOCK_SCREEN_HEIGHT), BORDER_COLOR);
|
||||
screen._backBuffer1.hLine(0, SHERLOCK_SCREEN_HEIGHT - 1, SHERLOCK_SCREEN_WIDTH - 1, BORDER_COLOR);
|
||||
screen._backBuffer1.fillRect(Common::Rect(2, CONTROLS_Y1 + 1, SHERLOCK_SCREEN_WIDTH - 2,
|
||||
SHERLOCK_SCREEN_HEIGHT - 1), INV_BACKGROUND);
|
||||
}
|
||||
|
||||
tempStr = FIXED(Settings_Exit);
|
||||
_actionExit = kActionScalpelSettingsExit;
|
||||
makeButtonNum(0, tempStr);
|
||||
|
||||
if (music._musicOn) {
|
||||
tempStr = FIXED(Settings_MusicOn);
|
||||
} else {
|
||||
tempStr = FIXED(Settings_MusicOff);
|
||||
}
|
||||
_actionMusic = kActionScalpelSettingsToggleMusic;
|
||||
makeButtonNum(1, tempStr);
|
||||
|
||||
if (people._portraitsOn) {
|
||||
tempStr = FIXED(Settings_PortraitsOn);
|
||||
} else {
|
||||
tempStr = FIXED(Settings_PortraitsOff);
|
||||
}
|
||||
_actionPortraits = kActionScalpelSettingsTogglePortraits;
|
||||
makeButtonNum(10, tempStr);
|
||||
|
||||
// WORKAROUND: We don't support the joystick in ScummVM, so draw the next two buttons as disabled
|
||||
tempStr = FIXED(Settings_JoystickOff);
|
||||
makeButtonNumDisabled(6, tempStr);
|
||||
|
||||
tempStr = FIXED(Settings_NewFontStyle);
|
||||
_actionNewFontStyle = kActionScalpelSettingsChangeFontStyle;
|
||||
makeButtonNum(5, tempStr);
|
||||
|
||||
if (sound._digitized) {
|
||||
tempStr = FIXED(Settings_SoundEffectsOn);
|
||||
} else {
|
||||
tempStr = FIXED(Settings_SoundEffectsOff);
|
||||
}
|
||||
_actionSoundEffects = kActionScalpelSettingsToggleSoundEffects;
|
||||
makeButtonNum(3, tempStr);
|
||||
|
||||
if (ui._slideWindows) {
|
||||
tempStr = FIXED(Settings_WindowsSlide);
|
||||
} else {
|
||||
tempStr = FIXED(Settings_WindowsAppear);
|
||||
}
|
||||
_actionWindows = kActionScalpelSettingsToggleWindowsMode;
|
||||
makeButtonNum(9, tempStr);
|
||||
|
||||
tempStr = FIXED(Settings_CalibrateJoystick);
|
||||
makeButtonNumDisabled(7, tempStr);
|
||||
|
||||
if (ui._helpStyle) {
|
||||
tempStr = FIXED(Settings_AutoHelpRight);
|
||||
} else {
|
||||
tempStr = FIXED(Settings_AutoHelpLeft);
|
||||
}
|
||||
_actionAutoHelp = kActionScalpelSettingsChangeAutohelpLoc;
|
||||
makeButtonNum(4, tempStr);
|
||||
|
||||
if (sound._voices) {
|
||||
tempStr = FIXED(Settings_VoicesOn);
|
||||
} else {
|
||||
tempStr = FIXED(Settings_VoicesOff);
|
||||
}
|
||||
_actionVoices = kActionScalpelSettingsToggleVoices;
|
||||
makeButtonNum(2, tempStr);
|
||||
|
||||
if (screen._fadeStyle) {
|
||||
tempStr = FIXED(Settings_FadeByPixel);
|
||||
} else {
|
||||
tempStr = FIXED(Settings_FadeDirectly);
|
||||
}
|
||||
_actionFade = kActionScalpelSettingsChangeFadeMode;
|
||||
makeButtonNum(8, tempStr);
|
||||
|
||||
if (screen._fadeStyle) {
|
||||
// German version uses a different action for fade modes
|
||||
if (_vm->getLanguage() == Common::Language::DE_DEU)
|
||||
_actionFade = kActionScalpelSettingsFadeByPixels;
|
||||
} else {
|
||||
if (_vm->getLanguage() == Common::Language::DE_DEU)
|
||||
_actionFade = kActionScalpelSettingsFadeDirectly;
|
||||
}
|
||||
|
||||
tempStr = FIXED(Settings_KeyPadSlow);
|
||||
makeButtonNumDisabled(11, tempStr);
|
||||
|
||||
_actionsIndexed[0] = _actionExit;
|
||||
_actionsIndexed[1] = _actionMusic;
|
||||
_actionsIndexed[2] = _actionVoices;
|
||||
_actionsIndexed[3] = _actionSoundEffects;
|
||||
_actionsIndexed[4] = _actionAutoHelp;
|
||||
_actionsIndexed[5] = _actionNewFontStyle;
|
||||
_actionsIndexed[8] = _actionFade;
|
||||
_actionsIndexed[9] = _actionWindows;
|
||||
_actionsIndexed[10] = _actionPortraits;
|
||||
|
||||
// Show the window immediately, or slide it on-screen
|
||||
if (!flag) {
|
||||
if (!ui._slideWindows) {
|
||||
screen.slamRect(Common::Rect(0, CONTROLS_Y1, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT));
|
||||
} else {
|
||||
ui.summonWindow(true, CONTROLS_Y1);
|
||||
}
|
||||
|
||||
ui._windowOpen = true;
|
||||
} else {
|
||||
screen.slamRect(Common::Rect(0, CONTROLS_Y1, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT));
|
||||
}
|
||||
}
|
||||
|
||||
int Settings::drawButtons(const Common::Point &pt, Common::CustomEventType action) {
|
||||
Events &events = *_vm->_events;
|
||||
People &people = *_vm->_people;
|
||||
ScalpelScreen &screen = *(ScalpelScreen *)_vm->_screen;
|
||||
Music &music = *_vm->_music;
|
||||
Sound &sound = *_vm->_sound;
|
||||
UserInterface &ui = *_vm->_ui;
|
||||
int found = -1;
|
||||
byte color;
|
||||
Common::String tempStr;
|
||||
|
||||
for (int idx = 0; idx < 12; ++idx) {
|
||||
if (!doesButtonExist(idx))
|
||||
continue;
|
||||
if ((getButtonRect(idx).contains(pt) && (events._pressed || events._released))
|
||||
|| (action == _actionsIndexed[idx])) {
|
||||
found = idx;
|
||||
color = COMMAND_HIGHLIGHTED;
|
||||
} else {
|
||||
color = COMMAND_FOREGROUND;
|
||||
}
|
||||
|
||||
// Print the button text
|
||||
switch (idx) {
|
||||
case 0:
|
||||
tempStr = FIXED(Settings_Exit);
|
||||
break;
|
||||
case 1:
|
||||
if (music._musicOn) {
|
||||
tempStr = FIXED(Settings_MusicOn);
|
||||
} else {
|
||||
tempStr = FIXED(Settings_MusicOff);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (sound._voices) {
|
||||
tempStr = FIXED(Settings_VoicesOn);
|
||||
} else {
|
||||
tempStr = FIXED(Settings_VoicesOff);
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if (sound._digitized) {
|
||||
tempStr = FIXED(Settings_SoundEffectsOn);
|
||||
} else {
|
||||
tempStr = FIXED(Settings_SoundEffectsOff);
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
if (ui._helpStyle) {
|
||||
tempStr = FIXED(Settings_AutoHelpRight);
|
||||
} else {
|
||||
tempStr = FIXED(Settings_AutoHelpLeft);
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
tempStr = FIXED(Settings_NewFontStyle);
|
||||
break;
|
||||
case 6:
|
||||
// Joystick Off - disabled in ScummVM
|
||||
continue;
|
||||
case 7:
|
||||
// Calibrate Joystick - disabled in ScummVM
|
||||
continue;
|
||||
case 8:
|
||||
if (screen._fadeStyle) {
|
||||
tempStr = FIXED(Settings_FadeByPixel);
|
||||
} else {
|
||||
tempStr = FIXED(Settings_FadeDirectly);
|
||||
}
|
||||
break;
|
||||
case 9:
|
||||
if (ui._slideWindows) {
|
||||
tempStr = FIXED(Settings_WindowsSlide);
|
||||
} else {
|
||||
tempStr = FIXED(Settings_WindowsAppear);
|
||||
}
|
||||
break;
|
||||
case 10:
|
||||
if (people._portraitsOn) {
|
||||
tempStr = FIXED(Settings_PortraitsOn);
|
||||
} else {
|
||||
tempStr = FIXED(Settings_PortraitsOff);
|
||||
}
|
||||
break;
|
||||
case 11:
|
||||
// Key Pad Slow - disabled in ScummVM
|
||||
continue;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
screen.buttonPrint(getButtonTextPoint(idx), color, true, tempStr);
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
void Settings::show(SherlockEngine *vm) {
|
||||
Events &events = *vm->_events;
|
||||
People &people = *vm->_people;
|
||||
Scene &scene = *vm->_scene;
|
||||
Screen &screen = *vm->_screen;
|
||||
Sound &sound = *vm->_sound;
|
||||
Music &music = *vm->_music;
|
||||
Talk &talk = *vm->_talk;
|
||||
ScalpelUserInterface &ui = *(ScalpelUserInterface *)vm->_ui;
|
||||
bool updateConfig = false;
|
||||
|
||||
assert(vm->getGameID() == GType_SerratedScalpel);
|
||||
Settings settings(vm);
|
||||
settings.drawInterface(false);
|
||||
|
||||
Common::Keymapper *keymapper = g_system->getEventManager()->getKeymapper();
|
||||
keymapper->getKeymap("scalpel")->setEnabled(false);
|
||||
keymapper->getKeymap("scalpel-settings")->setEnabled(true);
|
||||
|
||||
do {
|
||||
if (ui._menuCounter)
|
||||
ui.whileMenuCounter();
|
||||
|
||||
int found = -1;
|
||||
ui._action = kActionNone;
|
||||
|
||||
scene.doBgAnim();
|
||||
if (talk._talkToAbort)
|
||||
return;
|
||||
|
||||
events.setButtonState();
|
||||
Common::Point pt = events.mousePos();
|
||||
|
||||
if (events._pressed || events._released || events.actionHit()) {
|
||||
ui.clearInfo();
|
||||
ui._action = kActionNone;
|
||||
|
||||
if (events.actionHit()) {
|
||||
ui._action = events.getAction();
|
||||
|
||||
if (ui._action == kActionScalpelSettingsSelect) {
|
||||
events._pressed = false;
|
||||
events._oldButtons = 0;
|
||||
ui._actionPress = kActionNone;
|
||||
events._released = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle highlighting button under mouse
|
||||
found = settings.drawButtons(pt, ui._action);
|
||||
}
|
||||
|
||||
if ((found == 0 && events._released) || (ui._action == settings._actionExit))
|
||||
// Exit
|
||||
break;
|
||||
|
||||
if ((found == 1 && events._released) || ui._action == settings._actionMusic) {
|
||||
// Toggle music
|
||||
music._musicOn = !music._musicOn;
|
||||
if (!music._musicOn)
|
||||
music.stopMusic();
|
||||
else
|
||||
music.startSong();
|
||||
|
||||
updateConfig = true;
|
||||
settings.drawInterface(true);
|
||||
}
|
||||
|
||||
if ((found == 2 && events._released) || ui._action == settings._actionVoices) {
|
||||
sound._voices = !sound._voices;
|
||||
updateConfig = true;
|
||||
settings.drawInterface(true);
|
||||
}
|
||||
|
||||
if ((found == 3 && events._released) || ui._action == settings._actionSoundEffects) {
|
||||
// Toggle sound effects
|
||||
sound._digitized = !sound._digitized;
|
||||
updateConfig = true;
|
||||
settings.drawInterface(true);
|
||||
}
|
||||
|
||||
if ((found == 4 && events._released) || ui._action == settings._actionAutoHelp) {
|
||||
// Help button style
|
||||
ui._helpStyle = !ui._helpStyle;
|
||||
updateConfig = true;
|
||||
settings.drawInterface(true);
|
||||
}
|
||||
|
||||
if ((found == 5 && events._released) || ui._action == settings._actionNewFontStyle) {
|
||||
// New font style
|
||||
int fontNum = screen.fontNumber() + 1;
|
||||
if (fontNum == 3)
|
||||
fontNum = 0;
|
||||
|
||||
screen.setFont(fontNum);
|
||||
updateConfig = true;
|
||||
settings.drawInterface(true);
|
||||
}
|
||||
|
||||
if ((found == 8 && events._released) || ui._action == settings._actionFade) {
|
||||
// Toggle fade style
|
||||
screen._fadeStyle = !screen._fadeStyle;
|
||||
updateConfig = true;
|
||||
settings.drawInterface(true);
|
||||
}
|
||||
|
||||
if ((found == 9 && events._released) || ui._action == settings._actionWindows) {
|
||||
// Window style
|
||||
ui._slideWindows = !ui._slideWindows;
|
||||
updateConfig = true;
|
||||
settings.drawInterface(true);
|
||||
}
|
||||
|
||||
if ((found == 10 && events._released) || ui._action == settings._actionPortraits) {
|
||||
// Toggle portraits being shown
|
||||
people._portraitsOn = !people._portraitsOn;
|
||||
updateConfig = true;
|
||||
settings.drawInterface(true);
|
||||
}
|
||||
} while (!vm->shouldQuit());
|
||||
|
||||
ui.banishWindow();
|
||||
|
||||
keymapper->getKeymap("scalpel-settings")->setEnabled(false);
|
||||
keymapper->getKeymap("scalpel")->setEnabled(true);
|
||||
|
||||
if (updateConfig)
|
||||
vm->saveConfig();
|
||||
|
||||
ui._actionPress = kActionNone;
|
||||
ui._actionInput = false;
|
||||
ui._windowBounds.top = CONTROLS_Y1;
|
||||
ui._action = (Common::CustomEventType) -1;
|
||||
}
|
||||
|
||||
} // End of namespace Scalpel
|
||||
|
||||
} // End of namespace Sherlock
|
||||
96
engines/sherlock/scalpel/settings.h
Normal file
96
engines/sherlock/scalpel/settings.h
Normal file
@@ -0,0 +1,96 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SHERLOCK_SETTINGS_H
|
||||
#define SHERLOCK_SETTINGS_H
|
||||
|
||||
#include "common/events.h"
|
||||
#include "common/scummsys.h"
|
||||
|
||||
#include "sherlock/sherlock.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
class SherlockEngine;
|
||||
|
||||
namespace Scalpel {
|
||||
|
||||
class Settings {
|
||||
private:
|
||||
SherlockEngine *_vm;
|
||||
|
||||
Settings(SherlockEngine *vm) : _vm(vm) {
|
||||
_actionExit = kActionNone;
|
||||
_actionMusic = kActionNone;
|
||||
_actionPortraits = kActionNone;
|
||||
_actionNewFontStyle = kActionNone;
|
||||
_actionSoundEffects = kActionNone;
|
||||
_actionWindows = kActionNone;
|
||||
_actionAutoHelp = kActionNone;
|
||||
_actionVoices = kActionNone;
|
||||
_actionFade = kActionNone;
|
||||
|
||||
memset(_actionsIndexed, kActionNone, sizeof(_actionsIndexed));
|
||||
}
|
||||
|
||||
Common::CustomEventType _actionExit;
|
||||
Common::CustomEventType _actionMusic;
|
||||
Common::CustomEventType _actionPortraits;
|
||||
Common::CustomEventType _actionNewFontStyle;
|
||||
Common::CustomEventType _actionSoundEffects;
|
||||
Common::CustomEventType _actionWindows;
|
||||
Common::CustomEventType _actionAutoHelp;
|
||||
Common::CustomEventType _actionVoices;
|
||||
Common::CustomEventType _actionFade;
|
||||
|
||||
Common::CustomEventType _actionsIndexed[12];
|
||||
|
||||
/**
|
||||
* Draws the interface for the settings window
|
||||
*/
|
||||
void drawInterface(bool flag);
|
||||
|
||||
/**
|
||||
* Draws the buttons for the settings dialog
|
||||
*/
|
||||
int drawButtons(const Common::Point &pt, Common::CustomEventType action);
|
||||
|
||||
Common::Rect getButtonRect(int num) const;
|
||||
Common::Point getButtonTextPoint(int num) const;
|
||||
void makeButtonNum(int num, const Common::String &s);
|
||||
void makeButtonNumDisabled(int num, const Common::String &s);
|
||||
bool doesButtonExist(int num) const;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Handles input when the settings window is being shown
|
||||
* @remarks Whilst this would in theory be better in the Journal class, since it displays in
|
||||
* the user interface, it uses so many internal UI fields, that it sort of made some sense
|
||||
* to put it in the UserInterface class.
|
||||
*/
|
||||
static void show(SherlockEngine *vm);
|
||||
};
|
||||
|
||||
} // End of namespace Scalpel
|
||||
|
||||
} // End of namespace Sherlock
|
||||
|
||||
#endif
|
||||
686
engines/sherlock/scalpel/tsage/logo.cpp
Normal file
686
engines/sherlock/scalpel/tsage/logo.cpp
Normal file
@@ -0,0 +1,686 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "sherlock/scalpel/tsage/logo.h"
|
||||
#include "sherlock/scalpel/scalpel.h"
|
||||
|
||||
namespace Sherlock {
|
||||
namespace Scalpel {
|
||||
namespace TsAGE {
|
||||
|
||||
TLib *Visage::_tLib;
|
||||
|
||||
Visage::Visage() {
|
||||
_resNum = -1;
|
||||
_rlbNum = -1;
|
||||
_stream = nullptr;
|
||||
}
|
||||
|
||||
void Visage::setVisage(int resNum, int rlbNum) {
|
||||
if ((_resNum != resNum) || (_rlbNum != rlbNum)) {
|
||||
_resNum = resNum;
|
||||
_rlbNum = rlbNum;
|
||||
delete _stream;
|
||||
|
||||
// Games after Ringworld have an extra indirection via the visage index file
|
||||
Common::SeekableReadStream *stream = _tLib->getResource(RES_VISAGE, resNum, 9999);
|
||||
if (rlbNum == 0)
|
||||
rlbNum = 1;
|
||||
|
||||
// Check how many slots there are
|
||||
uint16 count = stream->readUint16LE();
|
||||
if (rlbNum > count)
|
||||
rlbNum = count;
|
||||
|
||||
// Get the flags/rlbNum to use
|
||||
stream->seek((rlbNum - 1) * 4 + 2);
|
||||
uint32 v = stream->readUint32LE();
|
||||
int flags = v >> 30;
|
||||
|
||||
if (flags & 3) {
|
||||
rlbNum = (int)(v & 0xff);
|
||||
}
|
||||
assert((flags & 3) == 0);
|
||||
delete stream;
|
||||
|
||||
_stream = _tLib->getResource(RES_VISAGE, resNum, rlbNum);
|
||||
}
|
||||
}
|
||||
|
||||
void Visage::clear() {
|
||||
delete _stream;
|
||||
_stream = nullptr;
|
||||
}
|
||||
|
||||
Visage::~Visage() {
|
||||
delete _stream;
|
||||
}
|
||||
|
||||
void Visage::getFrame(ObjectSurface &s, int frameNum) {
|
||||
_stream->seek(0);
|
||||
int numFrames = _stream->readUint16LE();
|
||||
if (frameNum > numFrames)
|
||||
frameNum = numFrames;
|
||||
if (frameNum > 0)
|
||||
--frameNum;
|
||||
|
||||
_stream->seek(frameNum * 4 + 2);
|
||||
int offset = _stream->readUint32LE();
|
||||
_stream->seek(offset);
|
||||
|
||||
surfaceFromRes(s);
|
||||
}
|
||||
|
||||
int Visage::getFrameCount() const {
|
||||
_stream->seek(0);
|
||||
return _stream->readUint16LE();
|
||||
}
|
||||
|
||||
bool Visage::isLoaded() const {
|
||||
return _stream != nullptr;
|
||||
}
|
||||
|
||||
void Visage::surfaceFromRes(ObjectSurface &s) {
|
||||
int frameWidth = _stream->readUint16LE();
|
||||
int frameHeight = _stream->readUint16LE();
|
||||
Common::Rect r(0, 0, frameWidth, frameHeight);
|
||||
s.create(r.width(), r.height());
|
||||
|
||||
s._centroid.x = _stream->readSint16LE();
|
||||
s._centroid.y = _stream->readSint16LE();
|
||||
|
||||
_stream->skip(1);
|
||||
byte flags = _stream->readByte();
|
||||
bool rleEncoded = (flags & 2) != 0;
|
||||
|
||||
byte *destP = (byte *)s.getPixels();
|
||||
|
||||
if (!rleEncoded) {
|
||||
_stream->read(destP, r.width() * r.height());
|
||||
} else {
|
||||
Common::fill(destP, destP + (r.width() * r.height()), 0xff);
|
||||
|
||||
for (int yp = 0; yp < r.height(); ++yp) {
|
||||
int width = r.width();
|
||||
destP = (byte *)s.getBasePtr(0, yp);
|
||||
|
||||
while (width > 0) {
|
||||
uint8 controlVal = _stream->readByte();
|
||||
if ((controlVal & 0x80) == 0) {
|
||||
// Copy specified number of bytes
|
||||
_stream->read(destP, controlVal);
|
||||
width -= controlVal;
|
||||
destP += controlVal;
|
||||
} else if ((controlVal & 0x40) == 0) {
|
||||
// Skip a specified number of output pixels
|
||||
destP += controlVal & 0x3f;
|
||||
width -= controlVal & 0x3f;
|
||||
} else {
|
||||
// Copy a specified pixel a given number of times
|
||||
controlVal &= 0x3f;
|
||||
int pixel = _stream->readByte();
|
||||
|
||||
Common::fill(destP, destP + controlVal, pixel);
|
||||
destP += controlVal;
|
||||
width -= controlVal;
|
||||
}
|
||||
}
|
||||
assert(width == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
ScalpelEngine *Object::_vm;
|
||||
|
||||
Object::Object() {
|
||||
_vm = nullptr;
|
||||
_isAnimating = _finished = false;
|
||||
_frame = 0;
|
||||
_numFrames = 0;
|
||||
_frameChange = 0;
|
||||
_angle = _changeCtr = 0;
|
||||
_walkStartFrame = 0;
|
||||
_majorDiff = _minorDiff = 0;
|
||||
_updateStartFrame = 0;
|
||||
}
|
||||
|
||||
void Object::setVisage(int visage, int strip) {
|
||||
_visage.setVisage(visage, strip);
|
||||
}
|
||||
|
||||
void Object::setAnimMode(bool isAnimating) {
|
||||
_isAnimating = isAnimating;
|
||||
_finished = false;
|
||||
|
||||
_updateStartFrame = _vm->_events->getFrameCounter();
|
||||
if (_numFrames)
|
||||
_updateStartFrame += 60 / _numFrames;
|
||||
_frameChange = 1;
|
||||
}
|
||||
|
||||
void Object::setDestination(const Common::Point &pt) {
|
||||
_destination = pt;
|
||||
|
||||
int moveRate = 10;
|
||||
_walkStartFrame = _vm->_events->getFrameCounter();
|
||||
_walkStartFrame += 60 / moveRate;
|
||||
|
||||
calculateMoveAngle();
|
||||
|
||||
// Get the difference
|
||||
int diffX = _destination.x - _position.x;
|
||||
int diffY = _destination.y - _position.y;
|
||||
int xSign = (diffX < 0) ? -1 : (diffX > 0 ? 1 : 0);
|
||||
int ySign = (diffY < 0) ? -1 : (diffY > 0 ? 1 : 0);
|
||||
diffX = ABS(diffX);
|
||||
diffY = ABS(diffY);
|
||||
|
||||
if (diffX < diffY) {
|
||||
_minorDiff = diffX / 2;
|
||||
_majorDiff = diffY;
|
||||
} else {
|
||||
_minorDiff = diffY / 2;
|
||||
_majorDiff = diffX;
|
||||
}
|
||||
|
||||
// Set the destination position
|
||||
_moveDelta = Common::Point(diffX, diffY);
|
||||
_moveSign = Common::Point(xSign, ySign);
|
||||
_changeCtr = 0;
|
||||
|
||||
assert(diffX || diffY);
|
||||
}
|
||||
|
||||
void Object::erase() {
|
||||
Screen &screen = *_vm->_screen;
|
||||
|
||||
if (_visage.isLoaded() && !_oldBounds.isEmpty())
|
||||
screen.SHblitFrom(screen._backBuffer1, Common::Point(_oldBounds.left, _oldBounds.top), _oldBounds);
|
||||
}
|
||||
|
||||
void Object::update() {
|
||||
Screen &screen = *_vm->_screen;
|
||||
|
||||
if (_visage.isLoaded()) {
|
||||
if (isMoving()) {
|
||||
uint32 currTime = _vm->_events->getFrameCounter();
|
||||
if (_walkStartFrame <= currTime) {
|
||||
int moveRate = 10;
|
||||
int frameInc = 60 / moveRate;
|
||||
_walkStartFrame = currTime + frameInc;
|
||||
move();
|
||||
}
|
||||
}
|
||||
|
||||
if (_isAnimating) {
|
||||
if (_frame < _visage.getFrameCount())
|
||||
_frame = changeFrame();
|
||||
else
|
||||
_finished = true;
|
||||
}
|
||||
|
||||
// Get the new frame
|
||||
ObjectSurface s;
|
||||
_visage.getFrame(s, _frame);
|
||||
|
||||
// Display the frame
|
||||
_oldBounds = Common::Rect(_position.x, _position.y, _position.x + s.width(), _position.y + s.height());
|
||||
_oldBounds.translate(-s._centroid.x, -s._centroid.y);
|
||||
screen.SHtransBlitFrom(s, Common::Point(_oldBounds.left, _oldBounds.top));
|
||||
}
|
||||
}
|
||||
|
||||
int Object::changeFrame() {
|
||||
int frameNum = _frame;
|
||||
uint32 currentFrame = _vm->_events->getFrameCounter();
|
||||
|
||||
if (_updateStartFrame <= currentFrame) {
|
||||
if (_numFrames > 0) {
|
||||
int v = 60 / _numFrames;
|
||||
_updateStartFrame = currentFrame + v;
|
||||
|
||||
frameNum = getNewFrame();
|
||||
}
|
||||
}
|
||||
|
||||
return frameNum;
|
||||
}
|
||||
|
||||
int Object::getNewFrame() {
|
||||
int frameNum = _frame + _frameChange;
|
||||
|
||||
if (_frameChange > 0) {
|
||||
if (frameNum > _visage.getFrameCount()) {
|
||||
frameNum = 1;
|
||||
}
|
||||
} else if (frameNum < 1) {
|
||||
frameNum = _visage.getFrameCount();
|
||||
}
|
||||
|
||||
return frameNum;
|
||||
}
|
||||
|
||||
void Object::calculateMoveAngle() {
|
||||
int xDiff = _destination.x - _position.x, yDiff = _position.y - _destination.y;
|
||||
|
||||
if (!xDiff && !yDiff)
|
||||
_angle = 0;
|
||||
else if (!xDiff)
|
||||
_angle = (_destination.y >= _position.y) ? 180 : 0;
|
||||
else if (!yDiff)
|
||||
_angle = (_destination.x >= _position.x) ? 90 : 270;
|
||||
else {
|
||||
int result = (((xDiff * 100) / ((abs(xDiff) + abs(yDiff))) * 90) / 100);
|
||||
|
||||
if (yDiff < 0)
|
||||
result = 180 - result;
|
||||
else if (xDiff < 0)
|
||||
result += 360;
|
||||
|
||||
_angle = result;
|
||||
}
|
||||
}
|
||||
|
||||
bool Object::isAnimEnded() const {
|
||||
return _finished;
|
||||
}
|
||||
|
||||
bool Object::isMoving() const {
|
||||
return (_destination.x != 0) && (_destination != _position);
|
||||
}
|
||||
|
||||
void Object::move() {
|
||||
Common::Point currPos = _position;
|
||||
Common::Point moveDiff(5, 3);
|
||||
int percent = 100;
|
||||
|
||||
if (dontMove())
|
||||
return;
|
||||
|
||||
if (_moveDelta.x >= _moveDelta.y) {
|
||||
int xAmount = _moveSign.x * moveDiff.x * percent / 100;
|
||||
if (!xAmount)
|
||||
xAmount = _moveSign.x;
|
||||
currPos.x += xAmount;
|
||||
|
||||
int yAmount = ABS(_destination.y - currPos.y);
|
||||
int yChange = _majorDiff / ABS(xAmount);
|
||||
int ySign;
|
||||
|
||||
if (!yChange)
|
||||
ySign = _moveSign.y;
|
||||
else {
|
||||
int v = yAmount / yChange;
|
||||
_changeCtr += yAmount % yChange;
|
||||
if (_changeCtr >= yChange) {
|
||||
++v;
|
||||
_changeCtr -= yChange;
|
||||
}
|
||||
|
||||
ySign = _moveSign.y * v;
|
||||
}
|
||||
|
||||
currPos.y += ySign;
|
||||
_majorDiff -= ABS(xAmount);
|
||||
} else {
|
||||
int yAmount = _moveSign.y * moveDiff.y * percent / 100;
|
||||
if (!yAmount)
|
||||
yAmount = _moveSign.y;
|
||||
currPos.y += yAmount;
|
||||
|
||||
int xAmount = ABS(_destination.x - currPos.x);
|
||||
int xChange = _majorDiff / ABS(yAmount);
|
||||
int xSign;
|
||||
|
||||
if (!xChange)
|
||||
xSign = _moveSign.x;
|
||||
else {
|
||||
int v = xAmount / xChange;
|
||||
_changeCtr += xAmount % xChange;
|
||||
if (_changeCtr >= xChange) {
|
||||
++v;
|
||||
_changeCtr -= xChange;
|
||||
}
|
||||
|
||||
xSign = _moveSign.x * v;
|
||||
}
|
||||
|
||||
currPos.x += xSign;
|
||||
_majorDiff -= ABS(yAmount);
|
||||
}
|
||||
|
||||
_position = currPos;
|
||||
if (dontMove())
|
||||
_position = _destination;
|
||||
}
|
||||
|
||||
bool Object::dontMove() const {
|
||||
return (_majorDiff <= 0);
|
||||
}
|
||||
|
||||
void Object::endMove() {
|
||||
_position = _destination;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------*/
|
||||
|
||||
bool Logo::show(ScalpelEngine *vm) {
|
||||
Events &events = *vm->_events;
|
||||
Logo *logo = new Logo(vm);
|
||||
bool interrupted = false;
|
||||
|
||||
while (!logo->finished()) {
|
||||
logo->nextFrame();
|
||||
|
||||
// Erase areas from previous frame, and update and re-draw objects
|
||||
for (int idx = 0; idx < 4; ++idx)
|
||||
logo->_objects[idx].erase();
|
||||
for (int idx = 0; idx < 4; ++idx)
|
||||
logo->_objects[idx].update();
|
||||
|
||||
events.delay(10);
|
||||
events.setButtonState();
|
||||
++logo->_frameCounter;
|
||||
|
||||
interrupted = vm->shouldQuit() || events.kbHit() || events._pressed || events.actionHit();
|
||||
if (interrupted) {
|
||||
// Keyboard, mouse, or action button pressed, so break out of logo display
|
||||
events.clearEvents();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
delete logo;
|
||||
return !interrupted;
|
||||
}
|
||||
|
||||
Logo::Logo(ScalpelEngine *vm) : _vm(vm), _lib("sf3.rlb") {
|
||||
Object::_vm = vm;
|
||||
Visage::_tLib = &_lib;
|
||||
|
||||
_finished = false;
|
||||
|
||||
// Initialize counter
|
||||
_counter = 0;
|
||||
_frameCounter = 0;
|
||||
|
||||
// Initialize wait frame counters
|
||||
_waitFrames = 0;
|
||||
_waitStartFrame = 0;
|
||||
|
||||
// Initialize animation counters
|
||||
_animateObject = 0;
|
||||
_animateStartFrame = 0;
|
||||
_animateFrameDelay = 0;
|
||||
_animateFrames = nullptr;
|
||||
_animateFrame = 0;
|
||||
|
||||
// Save a copy of the original palette
|
||||
_vm->_screen->getPalette(_originalPalette);
|
||||
|
||||
// Set up the palettes
|
||||
Common::fill(&_palette1[0], &_palette1[Graphics::PALETTE_SIZE], 0);
|
||||
Common::fill(&_palette1[0], &_palette2[Graphics::PALETTE_SIZE], 0);
|
||||
Common::fill(&_palette1[0], &_palette3[Graphics::PALETTE_SIZE], 0);
|
||||
|
||||
_lib.getPalette(_palette1, 1111);
|
||||
_lib.getPalette(_palette1, 10);
|
||||
_lib.getPalette(_palette2, 1111);
|
||||
_lib.getPalette(_palette2, 1);
|
||||
_lib.getPalette(_palette3, 1111);
|
||||
_lib.getPalette(_palette3, 14);
|
||||
}
|
||||
|
||||
Logo::~Logo() {
|
||||
// Restore the original palette
|
||||
_vm->_screen->setPalette(_originalPalette);
|
||||
}
|
||||
|
||||
bool Logo::finished() const {
|
||||
return _finished;
|
||||
}
|
||||
|
||||
const AnimationFrame handFrames[] = {
|
||||
{ 1, 33, 91 }, { 2, 44, 124 }, { 3, 64, 153 }, { 4, 87, 174 },
|
||||
{ 5, 114, 191 }, { 6, 125, 184 }, { 7, 154, 187 }, { 8, 181, 182 },
|
||||
{ 9, 191, 167 }, { 10, 190, 150 }, { 11, 182, 139 }, { 11, 170, 130 },
|
||||
{ 11, 158, 121 }, { 0, 0, 0 }
|
||||
};
|
||||
|
||||
const AnimationFrame companyFrames[] = {
|
||||
{ 1, 155, 94 }, { 2, 155, 94 }, { 3, 155, 94 }, { 4, 155, 94 },
|
||||
{ 5, 155, 94 }, { 6, 155, 94 }, { 7, 155, 94 }, { 8, 155, 94 },
|
||||
{ 0, 0, 0 }
|
||||
};
|
||||
|
||||
void Logo::nextFrame() {
|
||||
Screen &screen = *_vm->_screen;
|
||||
|
||||
if (_waitFrames) {
|
||||
uint32 currFrame = _frameCounter;
|
||||
if (currFrame - _waitStartFrame < _waitFrames) {
|
||||
return;
|
||||
}
|
||||
_waitStartFrame = 0;
|
||||
_waitFrames = 0;
|
||||
}
|
||||
|
||||
if (_animateFrames) {
|
||||
uint32 currFrame = _frameCounter;
|
||||
if (currFrame > _animateStartFrame + _animateFrameDelay) {
|
||||
AnimationFrame animationFrame = _animateFrames[_animateFrame];
|
||||
if (animationFrame.frame) {
|
||||
_objects[_animateObject]._frame = animationFrame.frame;
|
||||
_objects[_animateObject]._position = Common::Point(animationFrame.x, animationFrame.y);
|
||||
_animateStartFrame += _animateFrameDelay;
|
||||
_animateFrame++;
|
||||
} else {
|
||||
_animateObject = 0;
|
||||
_animateFrameDelay = 0;
|
||||
_animateFrames = nullptr;
|
||||
_animateStartFrame = 0;
|
||||
_animateFrame = 0;
|
||||
}
|
||||
}
|
||||
if (_animateFrames)
|
||||
return;
|
||||
}
|
||||
|
||||
switch (_counter++) {
|
||||
case 0:
|
||||
// Load the background and fade it in
|
||||
loadBackground();
|
||||
fade(_palette1);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
// First half of square, circle, and triangle arranging themselves
|
||||
_objects[0].setVisage(16, 1);
|
||||
_objects[0]._frame = 1;
|
||||
_objects[0]._position = Common::Point(169, 107);
|
||||
_objects[0]._numFrames = 7;
|
||||
_objects[0].setAnimMode(true);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
// Keep waiting until first animation ends
|
||||
if (!_objects[0].isAnimEnded()) {
|
||||
--_counter;
|
||||
} else {
|
||||
// Start second half of the shapes animation
|
||||
_objects[0].setVisage(16, 2);
|
||||
_objects[0]._frame = 1;
|
||||
_objects[0]._numFrames = 11;
|
||||
_objects[0].setAnimMode(true);
|
||||
}
|
||||
break;
|
||||
|
||||
case 3:
|
||||
// Keep waiting until second animation of shapes ordering themselves ends
|
||||
if (!_objects[0].isAnimEnded()) {
|
||||
--_counter;
|
||||
} else {
|
||||
// Fade out the background but keep the shapes visible
|
||||
fade(_palette2);
|
||||
screen._backBuffer1.clear();
|
||||
}
|
||||
waitFrames(10);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
// Load the new palette
|
||||
byte palette[Graphics::PALETTE_SIZE];
|
||||
Common::copy(&_palette2[0], &_palette2[Graphics::PALETTE_SIZE], &palette[0]);
|
||||
_lib.getPalette(palette, 12);
|
||||
screen.clear();
|
||||
screen.setPalette(palette);
|
||||
|
||||
// Morph into the EA logo
|
||||
_objects[0].setVisage(12, 1);
|
||||
_objects[0]._frame = 1;
|
||||
_objects[0]._numFrames = 7;
|
||||
_objects[0].setAnimMode(true);
|
||||
_objects[0]._position = Common::Point(170, 142);
|
||||
_objects[0].setDestination(Common::Point(158, 71));
|
||||
break;
|
||||
|
||||
case 5:
|
||||
// Wait until the logo has expanded upwards to form EA logo
|
||||
if (_objects[0].isMoving())
|
||||
--_counter;
|
||||
break;
|
||||
|
||||
case 6:
|
||||
fade(_palette3, 40);
|
||||
break;
|
||||
|
||||
case 7:
|
||||
// Show the 'Electronic Arts' company name
|
||||
_objects[1].setVisage(14, 1);
|
||||
_objects[1]._frame = 1;
|
||||
_objects[1]._position = Common::Point(152, 98);
|
||||
waitFrames(120);
|
||||
break;
|
||||
|
||||
case 8:
|
||||
// Start sequence of positioning and size hand cursor in an arc
|
||||
_objects[2].setVisage(18, 1);
|
||||
startAnimation(2, 5, &handFrames[0]);
|
||||
break;
|
||||
|
||||
case 9:
|
||||
// Show a highlighting of the company name
|
||||
_objects[1].remove();
|
||||
_objects[2].erase();
|
||||
_objects[2].remove();
|
||||
_objects[3].setVisage(19, 1);
|
||||
startAnimation(3, 8, &companyFrames[0]);
|
||||
break;
|
||||
|
||||
case 10:
|
||||
waitFrames(180);
|
||||
break;
|
||||
|
||||
case 11:
|
||||
_finished = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Logo::waitFrames(uint frames) {
|
||||
_waitFrames = frames;
|
||||
_waitStartFrame = _frameCounter;
|
||||
}
|
||||
|
||||
void Logo::startAnimation(uint object, uint frameDelay, const AnimationFrame *frames) {
|
||||
_animateObject = object;
|
||||
_animateFrameDelay = frameDelay;
|
||||
_animateFrames = frames;
|
||||
_animateStartFrame = _frameCounter;
|
||||
_animateFrame = 1;
|
||||
|
||||
_objects[object]._frame = frames[0].frame;
|
||||
_objects[object]._position = Common::Point(frames[0].x, frames[0].y);
|
||||
}
|
||||
|
||||
void Logo::loadBackground() {
|
||||
Screen &screen = *_vm->_screen;
|
||||
|
||||
for (int idx = 0; idx < 4; ++idx) {
|
||||
// Get the portion of the screen
|
||||
Common::SeekableReadStream *stream = _lib.getResource(RES_BITMAP, 10, idx);
|
||||
|
||||
// Load it onto the surface
|
||||
Common::Point pt((idx / 2) * (SHERLOCK_SCREEN_WIDTH / 2), (idx % 2) * (SHERLOCK_SCREEN_HEIGHT / 2));
|
||||
for (int y = 0; y < (SHERLOCK_SCREEN_HEIGHT / 2); ++y, ++pt.y) {
|
||||
byte *pDest = (byte *)screen._backBuffer1.getBasePtr(pt.x, pt.y);
|
||||
stream->read(pDest, SHERLOCK_SCREEN_WIDTH / 2);
|
||||
}
|
||||
|
||||
// _backgroundBounds = Rect(0, 0, READ_LE_UINT16(data), READ_LE_UINT16(data + 2));
|
||||
delete stream;
|
||||
}
|
||||
|
||||
// Default to a blank palette
|
||||
byte palette[Graphics::PALETTE_SIZE];
|
||||
Common::fill(&palette[0], &palette[Graphics::PALETTE_SIZE], 0);
|
||||
screen.setPalette(palette);
|
||||
|
||||
// Copy the surface to the screen
|
||||
screen.SHblitFrom(screen._backBuffer1);
|
||||
}
|
||||
|
||||
void Logo::fade(const byte palette[Graphics::PALETTE_SIZE], int step) {
|
||||
Events &events = *_vm->_events;
|
||||
Screen &screen = *_vm->_screen;
|
||||
byte startPalette[Graphics::PALETTE_SIZE];
|
||||
byte tempPalette[Graphics::PALETTE_SIZE];
|
||||
|
||||
screen.getPalette(startPalette);
|
||||
|
||||
for (int percent = 0; percent < 100; percent += step) {
|
||||
for (int palIndex = 0; palIndex < 256; ++palIndex) {
|
||||
const byte *pal1P = (const byte *)&startPalette[palIndex * 3];
|
||||
const byte *pal2P = (const byte *)&palette[palIndex * 3];
|
||||
byte *destP = &tempPalette[palIndex * 3];
|
||||
|
||||
for (int rgbIndex = 0; rgbIndex < 3; ++rgbIndex, ++pal1P, ++pal2P, ++destP) {
|
||||
*destP = (int)*pal1P + ((int)*pal2P - (int)*pal1P) * percent / 100;
|
||||
}
|
||||
}
|
||||
|
||||
screen.setPalette(tempPalette);
|
||||
events.wait(1);
|
||||
}
|
||||
|
||||
// Set final palette
|
||||
screen.setPalette(palette);
|
||||
}
|
||||
|
||||
} // end of namespace TsAGE
|
||||
} // end of namespace Scalpel
|
||||
} // end of namespace Sherlock
|
||||
249
engines/sherlock/scalpel/tsage/logo.h
Normal file
249
engines/sherlock/scalpel/tsage/logo.h
Normal file
@@ -0,0 +1,249 @@
|
||||
/* 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 SHERLOCK_SCALPEL_TSAGE_LOGO_H
|
||||
#define SHERLOCK_SCALPEL_TSAGE_LOGO_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/array.h"
|
||||
#include "common/file.h"
|
||||
#include "common/list.h"
|
||||
#include "common/str.h"
|
||||
#include "common/str-array.h"
|
||||
#include "common/util.h"
|
||||
#include "graphics/surface.h"
|
||||
#include "sherlock/scalpel/tsage/resources.h"
|
||||
#include "sherlock/screen.h"
|
||||
|
||||
namespace Sherlock {
|
||||
namespace Scalpel {
|
||||
|
||||
class ScalpelEngine;
|
||||
|
||||
namespace TsAGE {
|
||||
|
||||
class ObjectSurface : public Surface {
|
||||
public:
|
||||
Common::Point _centroid;
|
||||
public:
|
||||
ObjectSurface() : Surface() {}
|
||||
~ObjectSurface() override {}
|
||||
};
|
||||
|
||||
class Visage {
|
||||
private:
|
||||
Common::SeekableReadStream *_stream;
|
||||
|
||||
/**
|
||||
* Translates a raw image resource into a graphics surface
|
||||
*/
|
||||
void surfaceFromRes(ObjectSurface &s);
|
||||
public:
|
||||
static TLib *_tLib;
|
||||
int _resNum;
|
||||
int _rlbNum;
|
||||
public:
|
||||
Visage();
|
||||
~Visage();
|
||||
|
||||
/**
|
||||
* Set the visage number
|
||||
*/
|
||||
void setVisage(int resNum, int rlbNum = 9999);
|
||||
|
||||
/**
|
||||
* Clear the visage
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* Get a frame from the visage
|
||||
*/
|
||||
void getFrame(ObjectSurface &s, int frameNum);
|
||||
|
||||
/**
|
||||
* Return the number of frames
|
||||
*/
|
||||
int getFrameCount() const;
|
||||
|
||||
/**
|
||||
* Returns whether the visage is loaded
|
||||
*/
|
||||
bool isLoaded() const;
|
||||
};
|
||||
|
||||
class Object {
|
||||
private:
|
||||
Visage _visage;
|
||||
uint32 _updateStartFrame;
|
||||
bool _isAnimating;
|
||||
bool _finished;
|
||||
uint32 _walkStartFrame;
|
||||
int _angle;
|
||||
int _changeCtr;
|
||||
int _majorDiff, _minorDiff;
|
||||
Common::Point _moveDelta;
|
||||
Common::Point _moveSign;
|
||||
|
||||
/**
|
||||
* Return the next frame when the object is animating
|
||||
*/
|
||||
int changeFrame();
|
||||
|
||||
/**
|
||||
* Gets the next frame in the sequence
|
||||
*/
|
||||
int getNewFrame();
|
||||
|
||||
/**
|
||||
* Calculate the angle between the current position and a designated destination
|
||||
*/
|
||||
void calculateMoveAngle();
|
||||
|
||||
/**
|
||||
* Handle any object movement
|
||||
*/
|
||||
void move();
|
||||
|
||||
/**
|
||||
* Returns whether not to make any movement
|
||||
*/
|
||||
bool dontMove() const;
|
||||
|
||||
/**
|
||||
* Ends any current movement
|
||||
*/
|
||||
void endMove();
|
||||
public:
|
||||
static ScalpelEngine *_vm;
|
||||
Common::Point _position;
|
||||
Common::Point _destination;
|
||||
Common::Rect _oldBounds;
|
||||
int _frame;
|
||||
int _numFrames;
|
||||
int _frameChange;
|
||||
public:
|
||||
Object();
|
||||
|
||||
/**
|
||||
* Load the data for the object
|
||||
*/
|
||||
void setVisage(int visage, int strip);
|
||||
|
||||
/**
|
||||
* Sets whether the object is animating
|
||||
*/
|
||||
void setAnimMode(bool isAnimating);
|
||||
|
||||
/**
|
||||
* Starts an object moving to a given destination
|
||||
*/
|
||||
void setDestination(const Common::Point &pt);
|
||||
|
||||
/**
|
||||
* Returns true if an animation is ended
|
||||
*/
|
||||
bool isAnimEnded() const;
|
||||
|
||||
/**
|
||||
* Return true if object is moving
|
||||
*/
|
||||
bool isMoving() const;
|
||||
|
||||
/**
|
||||
* Erase the area the object was previously drawn at, by restoring the background
|
||||
*/
|
||||
void erase();
|
||||
|
||||
/**
|
||||
* Update the frame
|
||||
*/
|
||||
void update();
|
||||
|
||||
/**
|
||||
* Remove an object from being displayed
|
||||
*/
|
||||
void remove() { _visage.clear(); }
|
||||
};
|
||||
|
||||
struct AnimationFrame {
|
||||
int frame;
|
||||
int x;
|
||||
int y;
|
||||
};
|
||||
|
||||
class Logo {
|
||||
private:
|
||||
ScalpelEngine *_vm;
|
||||
TLib _lib;
|
||||
int _counter, _frameCounter;
|
||||
bool _finished;
|
||||
byte _originalPalette[Graphics::PALETTE_SIZE];
|
||||
byte _palette1[Graphics::PALETTE_SIZE];
|
||||
byte _palette2[Graphics::PALETTE_SIZE];
|
||||
byte _palette3[Graphics::PALETTE_SIZE];
|
||||
Object _objects[4];
|
||||
uint _waitFrames;
|
||||
uint32 _waitStartFrame;
|
||||
int _animateObject;
|
||||
uint32 _animateStartFrame;
|
||||
uint _animateFrameDelay;
|
||||
const AnimationFrame *_animateFrames;
|
||||
uint _animateFrame;
|
||||
|
||||
Logo(ScalpelEngine *vm);
|
||||
~Logo();
|
||||
|
||||
void nextFrame();
|
||||
|
||||
bool finished() const;
|
||||
|
||||
/**
|
||||
* Wait for a number of frames. Note that the frame count in _events is
|
||||
* not the same as the number of calls to nextFrame().
|
||||
*/
|
||||
void waitFrames(uint frames);
|
||||
|
||||
/**
|
||||
* Start an animation sequence. Used for sequences that are described
|
||||
* one frame at a time because they do unusual things, or run at
|
||||
* unusual rates.
|
||||
*/
|
||||
void startAnimation(uint object, uint frameDelay, const AnimationFrame *frames);
|
||||
|
||||
/**
|
||||
* Load the background for the scene
|
||||
*/
|
||||
void loadBackground();
|
||||
|
||||
/**
|
||||
* Fade from the current palette to a new one
|
||||
*/
|
||||
void fade(const byte palette[Graphics::PALETTE_SIZE], int step = 6);
|
||||
public:
|
||||
static bool show(ScalpelEngine *vm);
|
||||
};
|
||||
|
||||
} // end of namespace TsAGE
|
||||
} // end of namespace Scalpel
|
||||
} // end of namespace Sherlock
|
||||
|
||||
#endif
|
||||
372
engines/sherlock/scalpel/tsage/resources.cpp
Normal file
372
engines/sherlock/scalpel/tsage/resources.cpp
Normal file
@@ -0,0 +1,372 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/memstream.h"
|
||||
#include "common/stack.h"
|
||||
#include "sherlock/scalpel/tsage/resources.h"
|
||||
|
||||
namespace Sherlock {
|
||||
namespace Scalpel {
|
||||
namespace TsAGE {
|
||||
|
||||
static uint16 bitMasks[4] = {0x1ff, 0x3ff, 0x7ff, 0xfff};
|
||||
|
||||
uint16 BitReader::readToken() {
|
||||
assert((numBits >= 9) && (numBits <= 12));
|
||||
uint16 result = _remainder;
|
||||
int bitsLeft = numBits - _bitsLeft;
|
||||
int bitOffset = _bitsLeft;
|
||||
_bitsLeft = 0;
|
||||
|
||||
while (bitsLeft >= 0) {
|
||||
_remainder = readByte();
|
||||
result |= _remainder << bitOffset;
|
||||
bitsLeft -= 8;
|
||||
bitOffset += 8;
|
||||
}
|
||||
|
||||
_bitsLeft = -bitsLeft;
|
||||
_remainder >>= 8 - _bitsLeft;
|
||||
return result & bitMasks[numBits - 9];
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
TLib::TLib(const Common::Path &filename) : _filename(filename) {
|
||||
|
||||
// If the resource strings list isn't yet loaded, load them
|
||||
if (_resStrings.size() == 0) {
|
||||
Common::File f;
|
||||
if (f.open("tsage.cfg")) {
|
||||
while (!f.eos()) {
|
||||
_resStrings.push_back(f.readLine());
|
||||
}
|
||||
f.close();
|
||||
}
|
||||
}
|
||||
|
||||
if (!_file.open(filename))
|
||||
error("Missing file %s", filename.toString().c_str());
|
||||
|
||||
loadIndex();
|
||||
}
|
||||
|
||||
TLib::~TLib() {
|
||||
_resStrings.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a section index from the given position in the file
|
||||
*/
|
||||
void TLib::loadSection(uint32 fileOffset) {
|
||||
_resources.clear();
|
||||
_file.seek(fileOffset);
|
||||
_sections.fileOffset = fileOffset;
|
||||
|
||||
loadSection(_file, _resources);
|
||||
}
|
||||
|
||||
struct DecodeReference {
|
||||
uint16 vWord;
|
||||
uint8 vByte;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets a resource from the currently loaded section
|
||||
*/
|
||||
Common::SeekableReadStream *TLib::getResource(uint16 id, bool suppressErrors) {
|
||||
// Scan for an entry for the given Id
|
||||
ResourceEntry *re = nullptr;
|
||||
ResourceList::iterator iter;
|
||||
for (iter = _resources.begin(); iter != _resources.end(); ++iter) {
|
||||
if ((*iter).id == id) {
|
||||
re = &(*iter);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!re) {
|
||||
if (suppressErrors)
|
||||
return nullptr;
|
||||
error("Could not find resource Id #%d", id);
|
||||
}
|
||||
|
||||
if (!re->isCompressed) {
|
||||
// Read in the resource data and return it
|
||||
byte *dataP = (byte *)malloc(re->size);
|
||||
_file.seek(_sections.fileOffset + re->fileOffset);
|
||||
_file.read(dataP, re->size);
|
||||
|
||||
return new Common::MemoryReadStream(dataP, re->size, DisposeAfterUse::YES);
|
||||
}
|
||||
|
||||
/*
|
||||
* Decompress the data block
|
||||
*/
|
||||
|
||||
_file.seek(_sections.fileOffset + re->fileOffset);
|
||||
Common::ReadStream *compStream = _file.readStream(re->size);
|
||||
BitReader bitReader(*compStream);
|
||||
|
||||
byte *dataOut = (byte *)malloc(re->uncompressedSize);
|
||||
byte *destP = dataOut;
|
||||
uint bytesWritten = 0;
|
||||
|
||||
uint16 ctrCurrent = 0x102, ctrMax = 0x200;
|
||||
uint16 word_48050 = 0, currentToken = 0, word_48054 =0;
|
||||
byte byte_49068 = 0, byte_49069 = 0;
|
||||
|
||||
const uint tableSize = 0x1000;
|
||||
DecodeReference *table = (DecodeReference *)malloc(tableSize * sizeof(DecodeReference));
|
||||
if (!table)
|
||||
error("[TLib::getResource] Cannot allocate table buffer");
|
||||
|
||||
for (uint i = 0; i < tableSize; ++i) {
|
||||
table[i].vByte = table[i].vWord = 0;
|
||||
}
|
||||
Common::Stack<uint16> tokenList;
|
||||
|
||||
for (;;) {
|
||||
// Get the next decode token
|
||||
uint16 token = bitReader.readToken();
|
||||
|
||||
// Handle the token
|
||||
if (token == 0x101) {
|
||||
// End of compressed stream
|
||||
break;
|
||||
} else if (token == 0x100) {
|
||||
// Reset bit-rate
|
||||
bitReader.numBits = 9;
|
||||
ctrMax = 0x200;
|
||||
ctrCurrent = 0x102;
|
||||
|
||||
// Set variables with next token
|
||||
currentToken = word_48050 = bitReader.readToken();
|
||||
byte_49069 = byte_49068 = (byte)currentToken;
|
||||
|
||||
++bytesWritten;
|
||||
assert(bytesWritten <= re->uncompressedSize);
|
||||
*destP++ = byte_49069;
|
||||
} else {
|
||||
word_48054 = word_48050 = token;
|
||||
|
||||
if (token >= ctrCurrent) {
|
||||
word_48050 = currentToken;
|
||||
tokenList.push(byte_49068);
|
||||
}
|
||||
|
||||
while (word_48050 >= 0x100) {
|
||||
assert(word_48050 < 0x1000);
|
||||
tokenList.push(table[word_48050].vByte);
|
||||
word_48050 = table[word_48050].vWord;
|
||||
}
|
||||
|
||||
byte_49069 = byte_49068 = (byte)word_48050;
|
||||
tokenList.push(word_48050);
|
||||
|
||||
// Write out any cached tokens
|
||||
while (!tokenList.empty()) {
|
||||
++bytesWritten;
|
||||
assert(bytesWritten <= re->uncompressedSize);
|
||||
*destP++ = tokenList.pop();
|
||||
}
|
||||
|
||||
assert(ctrCurrent < 0x1000);
|
||||
table[ctrCurrent].vByte = byte_49069;
|
||||
table[ctrCurrent].vWord = currentToken;
|
||||
++ctrCurrent;
|
||||
|
||||
currentToken = word_48054;
|
||||
if ((ctrCurrent >= ctrMax) && (bitReader.numBits != 12)) {
|
||||
// Move to the next higher bit-rate
|
||||
++bitReader.numBits;
|
||||
ctrMax <<= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(table);
|
||||
|
||||
assert(bytesWritten == re->uncompressedSize);
|
||||
delete compStream;
|
||||
return new Common::MemoryReadStream(dataOut, re->uncompressedSize, DisposeAfterUse::YES);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the correct section and loads the specified resource within it
|
||||
*/
|
||||
Common::SeekableReadStream *TLib::getResource(ResourceType resType, uint16 resNum, uint16 rlbNum, bool suppressErrors) {
|
||||
SectionList::iterator i = _sections.begin();
|
||||
while ((i != _sections.end()) && ((*i).resType != resType || (*i).resNum != resNum))
|
||||
++i;
|
||||
if (i == _sections.end()) {
|
||||
if (suppressErrors)
|
||||
return nullptr;
|
||||
error("Unknown resource type %d num %d", resType, resNum);
|
||||
}
|
||||
|
||||
loadSection((*i).fileOffset);
|
||||
|
||||
return getResource(rlbNum, suppressErrors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the offset of the start of a resource in the resource file
|
||||
*/
|
||||
uint32 TLib::getResourceStart(ResourceType resType, uint16 resNum, uint16 rlbNum, ResourceEntry &entry) {
|
||||
// Find the correct section
|
||||
SectionList::iterator i = _sections.begin();
|
||||
while ((i != _sections.end()) && ((*i).resType != resType || (*i).resNum != resNum))
|
||||
++i;
|
||||
if (i == _sections.end()) {
|
||||
error("Unknown resource type %d num %d", resType, resNum);
|
||||
}
|
||||
|
||||
// Load in the section index
|
||||
loadSection((*i).fileOffset);
|
||||
|
||||
// Scan for an entry for the given Id
|
||||
ResourceEntry *re = nullptr;
|
||||
ResourceList::iterator iter;
|
||||
for (iter = _resources.begin(); iter != _resources.end(); ++iter) {
|
||||
if ((*iter).id == rlbNum) {
|
||||
re = &(*iter);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Throw an error if no resource was found, or the resource is compressed
|
||||
if (!re || re->isCompressed)
|
||||
error("Invalid resource Id #%d", rlbNum);
|
||||
|
||||
// Return the resource entry as well as the file offset
|
||||
entry = *re;
|
||||
return _sections.fileOffset + entry.fileOffset;
|
||||
}
|
||||
|
||||
void TLib::loadIndex() {
|
||||
uint16 resNum, configId, fileOffset;
|
||||
|
||||
// Load the root resources section
|
||||
loadSection(0);
|
||||
|
||||
// Get the single resource from it
|
||||
Common::SeekableReadStream *stream = getResource(0);
|
||||
|
||||
_sections.clear();
|
||||
|
||||
// Loop through reading the entries
|
||||
while ((resNum = stream->readUint16LE()) != 0xffff) {
|
||||
configId = stream->readUint16LE();
|
||||
fileOffset = stream->readUint16LE();
|
||||
|
||||
SectionEntry se;
|
||||
se.resNum = resNum;
|
||||
se.resType = (ResourceType)(configId & 0x1f);
|
||||
se.fileOffset = (((configId >> 5) & 0x7ff) << 16) | fileOffset;
|
||||
|
||||
_sections.push_back(se);
|
||||
}
|
||||
|
||||
delete stream;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the specified palette resource and returns it's data
|
||||
*
|
||||
* @paletteNum Specefies the palette number
|
||||
*/
|
||||
void TLib::getPalette(byte palette[Graphics::PALETTE_SIZE], int paletteNum) {
|
||||
// Get the specified palette
|
||||
Common::SeekableReadStream *stream = getResource(RES_PALETTE, paletteNum, 0, true);
|
||||
if (!stream)
|
||||
return;
|
||||
|
||||
int startNum = stream->readUint16LE();
|
||||
int numEntries = stream->readUint16LE();
|
||||
assert((startNum < 256) && ((startNum + numEntries) <= 256));
|
||||
stream->skip(2);
|
||||
|
||||
// Copy over the data
|
||||
stream->read(&palette[startNum * 3], numEntries * 3);
|
||||
|
||||
delete stream;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open up the given resource file using a passed file object. If the desired entry is found
|
||||
* in the index, return the index entry for it, and move the file to the start of the resource
|
||||
*/
|
||||
bool TLib::scanIndex(Common::File &f, ResourceType resType, int rlbNum, int resNum,
|
||||
ResourceEntry &resEntry) {
|
||||
// Load the root section index
|
||||
ResourceList resList;
|
||||
loadSection(f, resList);
|
||||
|
||||
// Loop through the index for the desired entry
|
||||
ResourceList::iterator iter;
|
||||
for (iter = resList.begin(); iter != resList.end(); ++iter) {
|
||||
ResourceEntry &re = *iter;
|
||||
if (re.id == resNum) {
|
||||
// Found it, so exit
|
||||
resEntry = re;
|
||||
f.seek(re.fileOffset);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// No matching entry found
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inner logic for decoding a section index into a passed resource list object
|
||||
*/
|
||||
void TLib::loadSection(Common::File &f, ResourceList &resources) {
|
||||
if (f.readUint32BE() != 0x544D492D)
|
||||
error("Data block is not valid Rlb data");
|
||||
|
||||
/*uint8 unknown1 = */f.readByte();
|
||||
uint16 numEntries = f.readByte();
|
||||
|
||||
for (uint i = 0; i < numEntries; ++i) {
|
||||
uint16 id = f.readUint16LE();
|
||||
uint16 size = f.readUint16LE();
|
||||
uint16 uncSize = f.readUint16LE();
|
||||
uint8 sizeHi = f.readByte();
|
||||
uint8 type = f.readByte() >> 5;
|
||||
assert(type <= 1);
|
||||
uint32 offset = f.readUint32LE();
|
||||
|
||||
ResourceEntry re;
|
||||
re.id = id;
|
||||
re.fileOffset = offset;
|
||||
re.isCompressed = type != 0;
|
||||
re.size = ((sizeHi & 0xF) << 16) | size;
|
||||
re.uncompressedSize = ((sizeHi & 0xF0) << 12) | uncSize;
|
||||
|
||||
resources.push_back(re);
|
||||
}
|
||||
}
|
||||
|
||||
} // end of namespace TsAGE
|
||||
} // end of namespace Scalpel
|
||||
} // end of namespace Sherlock
|
||||
138
engines/sherlock/scalpel/tsage/resources.h
Normal file
138
engines/sherlock/scalpel/tsage/resources.h
Normal file
@@ -0,0 +1,138 @@
|
||||
/* 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 SHERLOCK_SCALPEL_TSAGE_RESOURCES_H
|
||||
#define SHERLOCK_SCALPEL_TSAGE_RESOURCES_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/array.h"
|
||||
#include "common/file.h"
|
||||
#include "common/list.h"
|
||||
#include "common/str.h"
|
||||
#include "common/str-array.h"
|
||||
#include "common/util.h"
|
||||
#include "graphics/surface.h"
|
||||
#include "sherlock/screen.h"
|
||||
|
||||
namespace Sherlock {
|
||||
namespace Scalpel {
|
||||
namespace TsAGE {
|
||||
|
||||
// Magic number used by original game to identify valid memory blocks
|
||||
const uint32 MEMORY_ENTRY_ID = 0xE11DA722;
|
||||
|
||||
const int MEMORY_POOL_SIZE = 1000;
|
||||
|
||||
enum ResourceType { RES_LIBRARY, RES_STRIP, RES_IMAGE, RES_PALETTE, RES_VISAGE, RES_SOUND, RES_MESSAGE,
|
||||
RES_FONT, RES_POINTER, RES_BANK, RES_SND_DRIVER, RES_PRIORITY, RES_CONTROL, RES_WALKRGNS,
|
||||
RES_BITMAP, RES_SAVE, RES_SEQUENCE,
|
||||
// Return to Ringworld specific resource types
|
||||
RT17, RT18, RT19, RT20, RT21, RT22, RT23, RT24, RT25, RT26, RT27, RT28, RT29, RT30, RT31
|
||||
};
|
||||
|
||||
class SectionEntry {
|
||||
public:
|
||||
ResourceType resType;
|
||||
uint16 resNum;
|
||||
uint32 fileOffset;
|
||||
|
||||
SectionEntry() {
|
||||
resType = RES_LIBRARY;
|
||||
resNum = 0;
|
||||
fileOffset = 0;
|
||||
}
|
||||
};
|
||||
|
||||
class ResourceEntry {
|
||||
public:
|
||||
uint16 id;
|
||||
bool isCompressed;
|
||||
uint32 fileOffset;
|
||||
uint32 size;
|
||||
uint32 uncompressedSize;
|
||||
|
||||
ResourceEntry() {
|
||||
id = 0;
|
||||
isCompressed = false;
|
||||
fileOffset = 0;
|
||||
size = 0;
|
||||
uncompressedSize = 0;
|
||||
}
|
||||
};
|
||||
|
||||
typedef Common::List<ResourceEntry> ResourceList;
|
||||
|
||||
class SectionList : public Common::List<SectionEntry> {
|
||||
public:
|
||||
uint32 fileOffset;
|
||||
|
||||
SectionList() {
|
||||
fileOffset = 0;
|
||||
}
|
||||
};
|
||||
|
||||
class BitReader {
|
||||
private:
|
||||
Common::ReadStream &_stream;
|
||||
uint8 _remainder, _bitsLeft;
|
||||
byte readByte() { return _stream.eos() ? 0 : _stream.readByte(); }
|
||||
public:
|
||||
BitReader(Common::ReadStream &s) : _stream(s) {
|
||||
numBits = 9;
|
||||
_remainder = 0;
|
||||
_bitsLeft = 0;
|
||||
}
|
||||
uint16 readToken();
|
||||
|
||||
int numBits;
|
||||
};
|
||||
|
||||
class TLib {
|
||||
private:
|
||||
Common::StringArray _resStrings;
|
||||
private:
|
||||
Common::File _file;
|
||||
Common::Path _filename;
|
||||
ResourceList _resources;
|
||||
SectionList _sections;
|
||||
|
||||
void loadSection(uint32 fileOffset);
|
||||
void loadIndex();
|
||||
|
||||
static bool scanIndex(Common::File &f, ResourceType resType, int rlbNum, int resNum, ResourceEntry &resEntry);
|
||||
static void loadSection(Common::File &f, ResourceList &resources);
|
||||
public:
|
||||
TLib(const Common::Path &filename);
|
||||
~TLib();
|
||||
|
||||
const Common::Path &getFilename() { return _filename; }
|
||||
const SectionList &getSections() { return _sections; }
|
||||
Common::SeekableReadStream *getResource(uint16 id, bool suppressErrors = false);
|
||||
Common::SeekableReadStream *getResource(ResourceType resType, uint16 resNum, uint16 rlbNum, bool suppressErrors = false);
|
||||
uint32 getResourceStart(ResourceType resType, uint16 resNum, uint16 rlbNum, ResourceEntry &entry);
|
||||
void getPalette(byte palette[Graphics::PALETTE_SIZE], int paletteNum);
|
||||
};
|
||||
|
||||
} // end of namespace TsAGE
|
||||
} // end of namespace Scalpel
|
||||
} // end of namespace Sherlock
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user