aboutsummaryrefslogtreecommitdiff
path: root/engines/sherlock
diff options
context:
space:
mode:
authorPaul Gilbert2015-05-30 10:46:19 -0400
committerPaul Gilbert2015-05-30 10:46:19 -0400
commit6b95fc6b1132cf5ce6edc4216368bd38eddc939b (patch)
tree45f0d9fa5e65ae9f42fefb301498c2dd704bf772 /engines/sherlock
parentc3712f9a73eb7511c2a5bea20aa61ea21d3258f4 (diff)
parent895189e3ffd1931ca01991731f98bbf74796e05d (diff)
downloadscummvm-rg350-6b95fc6b1132cf5ce6edc4216368bd38eddc939b.tar.gz
scummvm-rg350-6b95fc6b1132cf5ce6edc4216368bd38eddc939b.tar.bz2
scummvm-rg350-6b95fc6b1132cf5ce6edc4216368bd38eddc939b.zip
Merge branch 'sherlock2'
Diffstat (limited to 'engines/sherlock')
-rw-r--r--engines/sherlock/decompress.cpp128
-rw-r--r--engines/sherlock/events.cpp10
-rw-r--r--engines/sherlock/events.h7
-rw-r--r--engines/sherlock/inventory.cpp75
-rw-r--r--engines/sherlock/journal.cpp60
-rw-r--r--engines/sherlock/map.cpp23
-rw-r--r--engines/sherlock/map.h2
-rw-r--r--engines/sherlock/module.mk6
-rw-r--r--engines/sherlock/music.cpp5
-rw-r--r--engines/sherlock/music.h5
-rw-r--r--engines/sherlock/objects.cpp158
-rw-r--r--engines/sherlock/objects.h201
-rw-r--r--engines/sherlock/people.cpp227
-rw-r--r--engines/sherlock/people.h48
-rw-r--r--engines/sherlock/resources.cpp131
-rw-r--r--engines/sherlock/resources.h35
-rw-r--r--engines/sherlock/scalpel/scalpel.cpp17
-rw-r--r--engines/sherlock/scalpel/scalpel_scene.cpp381
-rw-r--r--engines/sherlock/scalpel/scalpel_scene.h61
-rw-r--r--engines/sherlock/scalpel/scalpel_user_interface.cpp2276
-rw-r--r--engines/sherlock/scalpel/scalpel_user_interface.h227
-rw-r--r--engines/sherlock/scalpel/settings.cpp (renamed from engines/sherlock/settings.cpp)10
-rw-r--r--engines/sherlock/scalpel/settings.h (renamed from engines/sherlock/settings.h)5
-rw-r--r--engines/sherlock/scene.cpp747
-rw-r--r--engines/sherlock/scene.h100
-rw-r--r--engines/sherlock/screen.cpp141
-rw-r--r--engines/sherlock/screen.h36
-rw-r--r--engines/sherlock/sherlock.cpp13
-rw-r--r--engines/sherlock/sherlock.h9
-rw-r--r--engines/sherlock/sound.cpp21
-rw-r--r--engines/sherlock/sound.h4
-rw-r--r--engines/sherlock/surface.cpp12
-rw-r--r--engines/sherlock/surface.h2
-rw-r--r--engines/sherlock/talk.cpp1398
-rw-r--r--engines/sherlock/talk.h240
-rw-r--r--engines/sherlock/tattoo/tattoo.cpp46
-rw-r--r--engines/sherlock/tattoo/tattoo.h30
-rw-r--r--engines/sherlock/tattoo/tattoo_scene.cpp402
-rw-r--r--engines/sherlock/tattoo/tattoo_scene.h77
-rw-r--r--engines/sherlock/tattoo/tattoo_user_interface.cpp150
-rw-r--r--engines/sherlock/tattoo/tattoo_user_interface.h83
-rw-r--r--engines/sherlock/user_interface.cpp2272
-rw-r--r--engines/sherlock/user_interface.h192
43 files changed, 6372 insertions, 3701 deletions
diff --git a/engines/sherlock/decompress.cpp b/engines/sherlock/decompress.cpp
new file mode 100644
index 0000000000..8e02da3212
--- /dev/null
+++ b/engines/sherlock/decompress.cpp
@@ -0,0 +1,128 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "sherlock/decompress.h"
+
+namespace Sherlock {
+
+/**
+ * Decompresses an LZW compressed resource. If no outSize is specified, it will
+ * decompress the entire resource. If, however, an explicit size is specified,
+ * then it means we're already within a resource, and only want to decompress
+ * part of it.
+ */
+Common::SeekableReadStream *decompressLZ(Common::SeekableReadStream &source, int32 outSize) {
+ if (outSize == -1) {
+ source.seek(5);
+ outSize = source.readSint32LE();
+ }
+
+ byte lzWindow[4096];
+ uint16 lzWindowPos;
+ uint16 cmd;
+
+ byte *outBuffer = new byte[outSize];
+ byte *outBufferEnd = outBuffer + outSize;
+ Common::MemoryReadStream *outS = new Common::MemoryReadStream(outBuffer, outSize, DisposeAfterUse::YES);
+
+ memset(lzWindow, 0xFF, 0xFEE);
+ lzWindowPos = 0xFEE;
+ cmd = 0;
+
+ do {
+ cmd >>= 1;
+ if (!(cmd & 0x100))
+ cmd = source.readByte() | 0xFF00;
+
+ if (cmd & 1) {
+ byte literal = source.readByte();
+ *outBuffer++ = literal;
+ lzWindow[lzWindowPos] = literal;
+ lzWindowPos = (lzWindowPos + 1) & 0x0FFF;
+ } else {
+ int copyPos, copyLen;
+ copyPos = source.readByte();
+ copyLen = source.readByte();
+ copyPos = copyPos | ((copyLen & 0xF0) << 4);
+ copyLen = (copyLen & 0x0F) + 3;
+ while (copyLen--) {
+ byte literal = lzWindow[copyPos];
+ copyPos = (copyPos + 1) & 0x0FFF;
+ *outBuffer++ = literal;
+ lzWindow[lzWindowPos] = literal;
+ lzWindowPos = (lzWindowPos + 1) & 0x0FFF;
+ }
+ }
+ } while (outBuffer < outBufferEnd);
+
+ return outS;
+}
+
+
+/**
+ * Decompresses a Rose Tattoo resource
+ *
+Common::SeekableReadStream *decompress32(Common::SeekableReadStream &source, int32 outSize) {
+ if (outSize == -1) {
+ outSize = source.readSint32LE();
+ }
+
+ byte lzWindow[8192];
+ byte *outBuffer = new byte[outSize];
+ byte *outBufferEnd = outBuffer + outSize;
+ Common::MemoryReadStream *outS = new Common::MemoryReadStream(outBuffer, outSize, DisposeAfterUse::YES);
+
+ memset(lzWindow, 0xFF, 8192);
+ int lzWindowPos = 0xFEE;
+ int cmd = 0;
+
+ do {
+ cmd >>= 1;
+ if (!(cmd & 0x100))
+ cmd = source.readByte() | 0xFF00;
+
+ if (cmd & 1) {
+ byte literal = source.readByte();
+ *outBuffer++ = literal;
+ lzWindow[lzWindowPos] = literal;
+ lzWindowPos = (lzWindowPos + 1) & 0x0FFF;
+ } else {
+ int copyPos, copyLen;
+ copyPos = source.readByte();
+ copyLen = source.readByte();
+ copyPos = copyPos | ((copyLen & 0xF0) << 4);
+ copyLen = (copyLen & 0x0F) + 3;
+ while (copyLen--) {
+ byte literal = lzWindow[copyPos];
+ copyPos = (copyPos + 1) & 0x0FFF;
+ *outBuffer++ = literal;
+ lzWindow[lzWindowPos] = literal;
+ lzWindowPos = (lzWindowPos + 1) & 0x0FFF;
+ }
+ }
+ } while (outBuffer < outBufferEnd);
+
+ return outS;
+}
+*/
+
+} // namespace Sherlock
diff --git a/engines/sherlock/events.cpp b/engines/sherlock/events.cpp
index 94ddc9a792..a02a06cafc 100644
--- a/engines/sherlock/events.cpp
+++ b/engines/sherlock/events.cpp
@@ -42,6 +42,8 @@ Events::Events(SherlockEngine *vm) {
_pressed = _released = false;
_rightPressed = _rightReleased = false;
_oldButtons = _oldRightButton = false;
+
+ loadCursors("rmouse.vgs");
}
Events::~Events() {
@@ -73,6 +75,14 @@ void Events::setCursor(const Graphics::Surface &src) {
showCursor();
}
+void Events::animateCursorIfNeeded() {
+ if (_cursorId >= WAIT && _cursorId < (WAIT + 3)) {
+ CursorId newId = (WAIT + 2) ? WAIT : (CursorId)((int)_cursorId + 1);
+ setCursor(newId);
+ }
+}
+
+
void Events::showCursor() {
CursorMan.showMouse(true);
}
diff --git a/engines/sherlock/events.h b/engines/sherlock/events.h
index c19a92de8c..b35109fefe 100644
--- a/engines/sherlock/events.h
+++ b/engines/sherlock/events.h
@@ -33,7 +33,7 @@ namespace Sherlock {
#define GAME_FRAME_RATE 60
#define GAME_FRAME_TIME (1000 / GAME_FRAME_RATE)
-enum CursorId { ARROW = 0, MAGNIFY = 1, WAIT = 2, INVALID_CURSOR = -1 };
+enum CursorId { ARROW = 0, MAGNIFY = 1, WAIT = 2, EXIT_ZONES_START = 5, INVALID_CURSOR = -1 };
class SherlockEngine;
@@ -78,6 +78,11 @@ public:
void setCursor(const Graphics::Surface &src);
/**
+ * Animates the mouse cursor if the Wait cursor is showing
+ */
+ void animateCursorIfNeeded();
+
+ /**
* Show the mouse cursor
*/
void showCursor();
diff --git a/engines/sherlock/inventory.cpp b/engines/sherlock/inventory.cpp
index 7f216a35c3..a8ecb64102 100644
--- a/engines/sherlock/inventory.cpp
+++ b/engines/sherlock/inventory.cpp
@@ -22,6 +22,7 @@
#include "sherlock/inventory.h"
#include "sherlock/sherlock.h"
+#include "sherlock/scalpel/scalpel_user_interface.h"
namespace Sherlock {
@@ -200,29 +201,29 @@ void Inventory::drawInventory(InvNewMode mode) {
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] - screen.stringWidth("Exit") / 2, "Exit");
- screen.makeButton(Common::Rect(INVENTORY_POINTS[1][0], CONTROLS_Y1, INVENTORY_POINTS[1][1],
- CONTROLS_Y1 + 10), INVENTORY_POINTS[1][2] - screen.stringWidth("Look") / 2, "Look");
- screen.makeButton(Common::Rect(INVENTORY_POINTS[2][0], CONTROLS_Y1, INVENTORY_POINTS[2][1],
- CONTROLS_Y1 + 10), INVENTORY_POINTS[2][2] - screen.stringWidth("Use") / 2, "Use");
- screen.makeButton(Common::Rect(INVENTORY_POINTS[3][0], CONTROLS_Y1, INVENTORY_POINTS[3][1],
- CONTROLS_Y1 + 10), INVENTORY_POINTS[3][2] - screen.stringWidth("Give") / 2, "Give");
- screen.makeButton(Common::Rect(INVENTORY_POINTS[4][0], CONTROLS_Y1, INVENTORY_POINTS[4][1],
- CONTROLS_Y1 + 10), INVENTORY_POINTS[4][2], "^^");
- screen.makeButton(Common::Rect(INVENTORY_POINTS[5][0], CONTROLS_Y1, INVENTORY_POINTS[5][1],
- CONTROLS_Y1 + 10), INVENTORY_POINTS[5][2], "^");
- screen.makeButton(Common::Rect(INVENTORY_POINTS[6][0], CONTROLS_Y1, INVENTORY_POINTS[6][1],
- CONTROLS_Y1 + 10), INVENTORY_POINTS[6][2], "_");
- screen.makeButton(Common::Rect(INVENTORY_POINTS[7][0], CONTROLS_Y1, INVENTORY_POINTS[7][1],
- CONTROLS_Y1 + 10), INVENTORY_POINTS[7][2], "__");
+ screen.makeButton(Common::Rect(Scalpel::INVENTORY_POINTS[0][0], CONTROLS_Y1, Scalpel::INVENTORY_POINTS[0][1],
+ CONTROLS_Y1 + 10), Scalpel::INVENTORY_POINTS[0][2] - screen.stringWidth("Exit") / 2, "Exit");
+ screen.makeButton(Common::Rect(Scalpel::INVENTORY_POINTS[1][0], CONTROLS_Y1, Scalpel::INVENTORY_POINTS[1][1],
+ CONTROLS_Y1 + 10), Scalpel::INVENTORY_POINTS[1][2] - screen.stringWidth("Look") / 2, "Look");
+ screen.makeButton(Common::Rect(Scalpel::INVENTORY_POINTS[2][0], CONTROLS_Y1, Scalpel::INVENTORY_POINTS[2][1],
+ CONTROLS_Y1 + 10), Scalpel::INVENTORY_POINTS[2][2] - screen.stringWidth("Use") / 2, "Use");
+ screen.makeButton(Common::Rect(Scalpel::INVENTORY_POINTS[3][0], CONTROLS_Y1, Scalpel::INVENTORY_POINTS[3][1],
+ CONTROLS_Y1 + 10), Scalpel::INVENTORY_POINTS[3][2] - screen.stringWidth("Give") / 2, "Give");
+ screen.makeButton(Common::Rect(Scalpel::INVENTORY_POINTS[4][0], CONTROLS_Y1, Scalpel::INVENTORY_POINTS[4][1],
+ CONTROLS_Y1 + 10), Scalpel::INVENTORY_POINTS[4][2], "^^");
+ screen.makeButton(Common::Rect(Scalpel::INVENTORY_POINTS[5][0], CONTROLS_Y1, Scalpel::INVENTORY_POINTS[5][1],
+ CONTROLS_Y1 + 10), Scalpel::INVENTORY_POINTS[5][2], "^");
+ screen.makeButton(Common::Rect(Scalpel::INVENTORY_POINTS[6][0], CONTROLS_Y1, Scalpel::INVENTORY_POINTS[6][1],
+ CONTROLS_Y1 + 10), Scalpel::INVENTORY_POINTS[6][2], "_");
+ screen.makeButton(Common::Rect(Scalpel::INVENTORY_POINTS[7][0], CONTROLS_Y1, Scalpel::INVENTORY_POINTS[7][1],
+ CONTROLS_Y1 + 10), Scalpel::INVENTORY_POINTS[7][2], "__");
if (tempMode == INVENTORY_DONT_DISPLAY)
mode = LOOK_INVENTORY_MODE;
_invMode = (InvMode)mode;
if (mode != PLAIN_INVENTORY) {
- ui._oldKey = INVENTORY_COMMANDS[(int)mode];
+ ui._oldKey = Scalpel::INVENTORY_COMMANDS[(int)mode];
} else {
ui._oldKey = -1;
}
@@ -243,7 +244,8 @@ void Inventory::drawInventory(InvNewMode mode) {
screen._backBuffer = &screen._backBuffer1;
}
- ui._oldUse = -1;
+ assert(IS_SERRATED_SCALPEL);
+ ((Scalpel::ScalpelUserInterface *)_vm->_ui)->_oldUse = -1;
}
void Inventory::invCommands(bool slamIt) {
@@ -251,55 +253,55 @@ void Inventory::invCommands(bool slamIt) {
UserInterface &ui = *_vm->_ui;
if (slamIt) {
- screen.buttonPrint(Common::Point(INVENTORY_POINTS[0][2], CONTROLS_Y1),
+ screen.buttonPrint(Common::Point(Scalpel::INVENTORY_POINTS[0][2], CONTROLS_Y1),
_invMode == INVMODE_EXIT ? COMMAND_HIGHLIGHTED :COMMAND_FOREGROUND,
true, "Exit");
- screen.buttonPrint(Common::Point(INVENTORY_POINTS[1][2], CONTROLS_Y1),
+ screen.buttonPrint(Common::Point(Scalpel::INVENTORY_POINTS[1][2], CONTROLS_Y1),
_invMode == INVMODE_LOOK ? COMMAND_HIGHLIGHTED :COMMAND_FOREGROUND,
true, "Look");
- screen.buttonPrint(Common::Point(INVENTORY_POINTS[2][2], CONTROLS_Y1),
+ screen.buttonPrint(Common::Point(Scalpel::INVENTORY_POINTS[2][2], CONTROLS_Y1),
_invMode == INVMODE_USE ? COMMAND_HIGHLIGHTED : COMMAND_FOREGROUND,
true, "Use");
- screen.buttonPrint(Common::Point(INVENTORY_POINTS[3][2], CONTROLS_Y1),
+ screen.buttonPrint(Common::Point(Scalpel::INVENTORY_POINTS[3][2], CONTROLS_Y1),
_invMode == INVMODE_GIVE ? COMMAND_HIGHLIGHTED : COMMAND_FOREGROUND,
true, "Give");
- screen.print(Common::Point(INVENTORY_POINTS[4][2], CONTROLS_Y1 + 1),
+ screen.print(Common::Point(Scalpel::INVENTORY_POINTS[4][2], CONTROLS_Y1 + 1),
_invIndex == 0 ? COMMAND_NULL : COMMAND_FOREGROUND,
"^^");
- screen.print(Common::Point(INVENTORY_POINTS[5][2], CONTROLS_Y1 + 1),
+ screen.print(Common::Point(Scalpel::INVENTORY_POINTS[5][2], CONTROLS_Y1 + 1),
_invIndex == 0 ? COMMAND_NULL : COMMAND_FOREGROUND,
"^");
- screen.print(Common::Point(INVENTORY_POINTS[6][2], CONTROLS_Y1 + 1),
+ screen.print(Common::Point(Scalpel::INVENTORY_POINTS[6][2], CONTROLS_Y1 + 1),
(_holdings - _invIndex <= 6) ? COMMAND_NULL : COMMAND_FOREGROUND,
"_");
- screen.print(Common::Point(INVENTORY_POINTS[7][2], CONTROLS_Y1 + 1),
+ screen.print(Common::Point(Scalpel::INVENTORY_POINTS[7][2], CONTROLS_Y1 + 1),
(_holdings - _invIndex <= 6) ? COMMAND_NULL : COMMAND_FOREGROUND,
"__");
if (_invMode != INVMODE_LOOK)
ui.clearInfo();
} else {
- screen.buttonPrint(Common::Point(INVENTORY_POINTS[0][2], CONTROLS_Y1),
+ screen.buttonPrint(Common::Point(Scalpel::INVENTORY_POINTS[0][2], CONTROLS_Y1),
_invMode == INVMODE_EXIT ? COMMAND_HIGHLIGHTED : COMMAND_FOREGROUND,
false, "Exit");
- screen.buttonPrint(Common::Point(INVENTORY_POINTS[1][2], CONTROLS_Y1),
+ screen.buttonPrint(Common::Point(Scalpel::INVENTORY_POINTS[1][2], CONTROLS_Y1),
_invMode == INVMODE_LOOK ? COMMAND_HIGHLIGHTED : COMMAND_FOREGROUND,
false, "Look");
- screen.buttonPrint(Common::Point(INVENTORY_POINTS[2][2], CONTROLS_Y1),
+ screen.buttonPrint(Common::Point(Scalpel::INVENTORY_POINTS[2][2], CONTROLS_Y1),
_invMode == INVMODE_USE ? COMMAND_HIGHLIGHTED : COMMAND_FOREGROUND,
false, "Use");
- screen.buttonPrint(Common::Point(INVENTORY_POINTS[3][2], CONTROLS_Y1),
+ screen.buttonPrint(Common::Point(Scalpel::INVENTORY_POINTS[3][2], CONTROLS_Y1),
_invMode == INVMODE_GIVE ? COMMAND_HIGHLIGHTED : COMMAND_FOREGROUND,
false, "Give");
- screen.gPrint(Common::Point(INVENTORY_POINTS[4][2], CONTROLS_Y1),
+ screen.gPrint(Common::Point(Scalpel::INVENTORY_POINTS[4][2], CONTROLS_Y1),
_invIndex == 0 ? COMMAND_NULL : COMMAND_FOREGROUND,
"^^");
- screen.gPrint(Common::Point(INVENTORY_POINTS[5][2], CONTROLS_Y1),
+ screen.gPrint(Common::Point(Scalpel::INVENTORY_POINTS[5][2], CONTROLS_Y1),
_invIndex == 0 ? COMMAND_NULL : COMMAND_FOREGROUND,
"^");
- screen.gPrint(Common::Point(INVENTORY_POINTS[6][2], CONTROLS_Y1),
+ screen.gPrint(Common::Point(Scalpel::INVENTORY_POINTS[6][2], CONTROLS_Y1),
(_holdings - _invIndex < 7) ? COMMAND_NULL : COMMAND_FOREGROUND,
"_");
- screen.gPrint(Common::Point(INVENTORY_POINTS[7][2], CONTROLS_Y1),
+ screen.gPrint(Common::Point(Scalpel::INVENTORY_POINTS[7][2], CONTROLS_Y1),
(_holdings - _invIndex < 7) ? COMMAND_NULL : COMMAND_FOREGROUND,
"__");
}
@@ -318,9 +320,12 @@ void Inventory::highlight(int index, byte color) {
}
void Inventory::refreshInv() {
+ if (IS_ROSE_TATTOO)
+ return;
+
Screen &screen = *_vm->_screen;
Talk &talk = *_vm->_talk;
- UserInterface &ui = *_vm->_ui;
+ Scalpel::ScalpelUserInterface &ui = *(Scalpel::ScalpelUserInterface *)_vm->_ui;
ui._invLookFlag = true;
freeInv();
diff --git a/engines/sherlock/journal.cpp b/engines/sherlock/journal.cpp
index 564db59042..b4b05da991 100644
--- a/engines/sherlock/journal.cpp
+++ b/engines/sherlock/journal.cpp
@@ -135,6 +135,7 @@ void Journal::loadJournalFile(bool alreadyLoaded) {
Screen &screen = *_vm->_screen;
Talk &talk = *_vm->_talk;
JournalEntry &journalEntry = _journal[_index];
+ const byte *opcodes = talk._opcodes;
Common::String dirFilename = _directory[journalEntry._converseNum];
bool replyOnly = journalEntry._replyOnly;
@@ -243,7 +244,7 @@ void Journal::loadJournalFile(bool alreadyLoaded) {
byte c = *replyP++;
// Is it a control character?
- if (c < SWITCH_SPEAKER) {
+ if (c < opcodes[0]) {
// Nope. Set flag for allowing control codes to insert spaces
ctrlSpace = true;
assert(c >= ' ');
@@ -290,7 +291,7 @@ void Journal::loadJournalFile(bool alreadyLoaded) {
byte v;
do {
v = *strP++;
- } while (v && (v < SWITCH_SPEAKER) && (v != '.') && (v != '!') && (v != '?'));
+ } while (v && (v < opcodes[0]) && (v != '.') && (v != '!') && (v != '?'));
if (v == '?')
journalString += " asked, \"";
@@ -306,11 +307,11 @@ void Journal::loadJournalFile(bool alreadyLoaded) {
journalString += c;
do {
journalString += *replyP++;
- } while (*replyP && *replyP < SWITCH_SPEAKER && *replyP != '{' && *replyP != '}');
+ } while (*replyP && *replyP < opcodes[0] && *replyP != '{' && *replyP != '}');
commentJustPrinted = false;
}
- } else if (c == SWITCH_SPEAKER) {
+ } else if (c == opcodes[OP_SWITCH_SPEAKER]) {
if (!startOfReply) {
if (!commentFlag && !commentJustPrinted)
journalString += "\"\n";
@@ -337,7 +338,7 @@ void Journal::loadJournalFile(bool alreadyLoaded) {
byte v;
do {
v = *strP++;
- } while (v && v < SWITCH_SPEAKER && v != '.' && v != '!' && v != '?');
+ } while (v && v < opcodes[0] && v != '.' && v != '!' && v != '?');
if (v == '?')
journalString += " asked, \"";
@@ -345,59 +346,42 @@ void Journal::loadJournalFile(bool alreadyLoaded) {
journalString += " said, \"";
} else {
// Control code, so move past it and any parameters
- switch (c) {
- case RUN_CANIMATION:
- case ASSIGN_PORTRAIT_LOCATION:
- case PAUSE:
- case PAUSE_WITHOUT_CONTROL:
- case WALK_TO_CANIMATION:
+ if (c == opcodes[OP_RUN_CANIMATION] || c == opcodes[OP_ASSIGN_PORTRAIT_LOCATION] ||
+ c == opcodes[OP_PAUSE] || c == opcodes[OP_PAUSE_WITHOUT_CONTROL] ||
+ c == opcodes[OP_WALK_TO_CANIMATION]) {
// These commands have a single parameter
++replyP;
- break;
- case ADJUST_OBJ_SEQUENCE:
+ } else if (c == opcodes[OP_ADJUST_OBJ_SEQUENCE]) {
replyP += (replyP[0] & 127) + replyP[1] + 2;
- break;
- case WALK_TO_COORDS:
- case MOVE_MOUSE:
+ } else if (c == opcodes[OP_WALK_TO_COORDS] || c == opcodes[OP_MOVE_MOUSE]) {
replyP += 4;
- break;
-
- case SET_FLAG:
- case IF_STATEMENT:
+
+ } else if (c == opcodes[OP_SET_FLAG] || c == opcodes[OP_IF_STATEMENT]) {
replyP += 2;
- break;
- case SFX_COMMAND:
- case PLAY_PROLOGUE:
- case CALL_TALK_FILE:
+ } else if (c == opcodes[OP_SFX_COMMAND] || c == opcodes[OP_PLAY_PROLOGUE] ||
+ c == opcodes[OP_CALL_TALK_FILE]) {
replyP += 8;
break;
- case TOGGLE_OBJECT:
- case ADD_ITEM_TO_INVENTORY:
- case SET_OBJECT:
- case DISPLAY_INFO_LINE:
- case REMOVE_ITEM_FROM_INVENTORY:
+ } else if (c == opcodes[OP_TOGGLE_OBJECT] || c == opcodes[OP_ADD_ITEM_TO_INVENTORY] ||
+ c == opcodes[OP_SET_OBJECT] || c == opcodes[OP_DISPLAY_INFO_LINE] ||
+ c == opcodes[OP_REMOVE_ITEM_FROM_INVENTORY]) {
replyP += (*replyP & 127) + 1;
- break;
- case GOTO_SCENE:
+ } else if (c == opcodes[OP_GOTO_SCENE]) {
replyP += 5;
- break;
- case CARRIAGE_RETURN:
+ } else if (c == opcodes[OP_CARRIAGE_RETURN]) {
journalString += "\n";
- break;
-
- default:
- break;
}
// Put a space in the output for a control character, unless it's
// immediately coming after another control character
- if (ctrlSpace && c != ASSIGN_PORTRAIT_LOCATION && c != CARRIAGE_RETURN && !commentJustPrinted) {
+ if (ctrlSpace && c != opcodes[OP_ASSIGN_PORTRAIT_LOCATION] && c != opcodes[OP_CARRIAGE_RETURN] &&
+ !commentJustPrinted) {
journalString += " ";
ctrlSpace = false;
}
diff --git a/engines/sherlock/map.cpp b/engines/sherlock/map.cpp
index 42775ae4b0..ffbca3fb42 100644
--- a/engines/sherlock/map.cpp
+++ b/engines/sherlock/map.cpp
@@ -22,6 +22,7 @@
#include "sherlock/map.h"
#include "sherlock/sherlock.h"
+#include "common/system.h"
namespace Sherlock {
@@ -50,7 +51,7 @@ const byte *MapPaths::getPath(int srcLocation, int destLocation) {
/*----------------------------------------------------------------*/
-Map::Map(SherlockEngine *vm) : _vm(vm), _topLine(SHERLOCK_SCREEN_WIDTH, 12) {
+Map::Map(SherlockEngine *vm): _vm(vm), _topLine(g_system->getWidth(), 12) {
_active = false;
_mapCursors = nullptr;
_shapes = nullptr;
@@ -64,8 +65,12 @@ Map::Map(SherlockEngine *vm) : _vm(vm), _topLine(SHERLOCK_SCREEN_WIDTH, 12) {
_oldCharPoint = 0;
_frameChangeFlag = false;
- for (int idx = 0; idx < MAX_HOLMES_SEQUENCE; ++idx)
- Common::fill(&_sequences[idx][0], &_sequences[idx][MAX_FRAME], 0);
+ // 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();
@@ -79,12 +84,17 @@ void Map::loadPoints(int count, const int *xList, const int *yList, const int *t
void Map::loadSequences(int count, const byte *seq) {
for (int idx = 0; idx < count; ++idx, seq += MAX_FRAME)
- Common::copy(seq, seq + MAX_FRAME, &_sequences[idx][0]);
+ Common::copy(seq, seq + MAX_FRAME, &_walkSequences[idx]._sequences[0]);
}
void Map::loadData() {
+ // TODO: Remove this
+ if (_vm->getGameID() == GType_RoseTattoo)
+ return;
+
// Load the list of location names
- Common::SeekableReadStream *txtStream = _vm->_res->load("chess.txt");
+ Common::SeekableReadStream *txtStream = _vm->_res->load(
+ _vm->getGameID() == GType_SerratedScalpel ? "chess.txt" : "map.txt");
int streamSize = txtStream->size();
while (txtStream->pos() < streamSize) {
@@ -283,7 +293,6 @@ void Map::setupSprites() {
p._type = CHARACTER;
p._position = Common::Point(12400, 5000);
p._sequenceNumber = 0;
- p._sequences = &_sequences;
p._images = _shapes;
p._imageFrame = nullptr;
p._frameNumber = 0;
@@ -296,8 +305,8 @@ void Map::setupSprites() {
p._noShapeSize = Common::Point(0, 0);
p._goto = Common::Point(28000, 15000);
p._status = 0;
+ p._walkSequences = _walkSequences;
p.setImageFrame();
-
scene._bgShapes.clear();
}
diff --git a/engines/sherlock/map.h b/engines/sherlock/map.h
index ab70b0885f..e0c7d038c4 100644
--- a/engines/sherlock/map.h
+++ b/engines/sherlock/map.h
@@ -76,7 +76,7 @@ private:
ImageFile *_mapCursors;
ImageFile *_shapes;
ImageFile *_iconShapes;
- byte _sequences[MAX_HOLMES_SEQUENCE][MAX_FRAME];
+ WalkSequences _walkSequences;
Point32 _lDrawnPos;
int _point;
bool _placesShown;
diff --git a/engines/sherlock/module.mk b/engines/sherlock/module.mk
index d8c1d781a9..9ebe416c4b 100644
--- a/engines/sherlock/module.mk
+++ b/engines/sherlock/module.mk
@@ -4,7 +4,12 @@ MODULE_OBJS = \
scalpel/darts.o \
scalpel/scalpel.o \
scalpel/drivers/adlib.o \
+ scalpel/scalpel_scene.o \
+ scalpel/scalpel_user_interface.o \
+ scalpel/settings.o \
tattoo/tattoo.o \
+ tattoo/tattoo_scene.o \
+ tattoo/tattoo_user_interface.o \
animation.o \
debugger.o \
detection.o \
@@ -19,7 +24,6 @@ MODULE_OBJS = \
saveload.o \
scene.o \
screen.o \
- settings.o \
sherlock.o \
sound.o \
surface.o \
diff --git a/engines/sherlock/music.cpp b/engines/sherlock/music.cpp
index ed047f61f8..3e31bcc83d 100644
--- a/engines/sherlock/music.cpp
+++ b/engines/sherlock/music.cpp
@@ -272,6 +272,11 @@ bool Music::loadSong(int songNumber) {
return true;
}
+bool Music::loadSong(const Common::String &songName) {
+ warning("TODO: Music::loadSong");
+ return false;
+}
+
void Music::syncMusicSettings() {
_musicOn = !ConfMan.getBool("mute") && !ConfMan.getBool("music_mute");
}
diff --git a/engines/sherlock/music.h b/engines/sherlock/music.h
index a195b26b68..c3710603cc 100644
--- a/engines/sherlock/music.h
+++ b/engines/sherlock/music.h
@@ -74,6 +74,11 @@ public:
bool loadSong(int songNumber);
/**
+ * Load a specified song
+ */
+ bool loadSong(const Common::String &songName);
+
+ /**
* Start playing a song
*/
void startSong();
diff --git a/engines/sherlock/objects.cpp b/engines/sherlock/objects.cpp
index 02f2526ae9..8818f805a5 100644
--- a/engines/sherlock/objects.cpp
+++ b/engines/sherlock/objects.cpp
@@ -46,7 +46,7 @@ void Sprite::clear() {
_description = "";
_examine.clear();
_pickUp = "";
- _sequences = nullptr;
+ _walkSequences.clear();
_images = nullptr;
_imageFrame = nullptr;
_walkCount = 0;
@@ -63,12 +63,26 @@ void Sprite::clear() {
_status = 0;
_misc = 0;
_numFrames = 0;
+ _altImages = nullptr;
+ _altSequences = false;
+ Common::fill(&_stopFrames[0], &_stopFrames[8], (ImageFrame *)nullptr);
}
void Sprite::setImageFrame() {
- int imageNumber = (*_sequences)[_sequenceNumber][_frameNumber] +
- (*_sequences)[_sequenceNumber][0] - 2;
- _imageFrame = &(*_images)[imageNumber];
+ int frameNum = MAX(_frameNumber, 0);
+ int imageNumber = _walkSequences[_sequenceNumber][frameNum];
+
+ if (IS_SERRATED_SCALPEL)
+ imageNumber = imageNumber + _walkSequences[_sequenceNumber][0] - 2;
+ else if (imageNumber > _numFrames)
+ imageNumber = 1;
+
+ // Get the images to use
+ ImageFile *images = _altSequences ? _altImages : _images;
+ assert(images);
+
+ // Set the frame pointer
+ _imageFrame = &(*images)[imageNumber];
}
void Sprite::adjustSprite() {
@@ -113,14 +127,14 @@ void Sprite::adjustSprite() {
people.gotoStand(*this);
}
} else if (!map._active) {
- _position.y = CLIP((int)_position.y, UPPER_LIMIT, LOWER_LIMIT);
- _position.x = CLIP((int)_position.x, LEFT_LIMIT, RIGHT_LIMIT);
+ _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 ((*_sequences)[_sequenceNumber][_frameNumber] == 0) {
+ if (_walkSequences[_sequenceNumber][_frameNumber] == 0) {
switch (_sequenceNumber) {
case STOP_UP:
case STOP_DOWN:
@@ -352,6 +366,34 @@ void Sprite::checkSprite() {
/*----------------------------------------------------------------*/
+void WalkSequence::load(Common::SeekableReadStream &s) {
+ char buffer[9];
+ s.read(buffer, 9);
+ _vgsName = Common::String(buffer);
+ _horizFlip = s.readByte() != 0;
+
+ _sequences.resize(s.readUint16LE());
+ s.read(&_sequences[0], _sequences.size());
+}
+
+/*----------------------------------------------------------------*/
+
+WalkSequences &WalkSequences::operator=(const WalkSequences &src) {
+ resize(src.size());
+ for (uint idx = 0; idx < size(); ++idx) {
+ const WalkSequence &wSrc = src[idx];
+ WalkSequence &wDest = (*this)[idx];
+ wDest._horizFlip = wSrc._horizFlip;
+
+ wDest._sequences.resize(wSrc._sequences.size());
+ Common::copy(&wSrc._sequences[0], &wSrc._sequences[0] + wSrc._sequences.size(), &wDest._sequences[0]);
+ }
+
+ return *this;
+}
+
+/*----------------------------------------------------------------*/
+
void ActionType::load(Common::SeekableReadStream &s) {
char buffer[12];
@@ -373,9 +415,14 @@ UseType::UseType() {
_useFlag = 0;
}
-void UseType::load(Common::SeekableReadStream &s) {
+void UseType::load(Common::SeekableReadStream &s, bool isRoseTattoo) {
char buffer[12];
+ if (isRoseTattoo) {
+ s.read(buffer, 12);
+ _verb = Common::String(buffer);
+ }
+
_cAnimNum = s.readByte();
_cAnimSpeed = s.readByte();
if (_cAnimSpeed & 0x80)
@@ -387,7 +434,9 @@ void UseType::load(Common::SeekableReadStream &s) {
}
_useFlag = s.readSint16LE();
- s.skip(6);
+
+ if (!isRoseTattoo)
+ s.skip(6);
s.read(buffer, 12);
_target = Common::String(buffer);
@@ -434,9 +483,16 @@ Object::Object() {
_descOffset = 0;
_seqCounter2 = 0;
_seqSize = 0;
+
+ _quickDraw = 0;
+ _scaleVal = 0;
+ _requiredFlag1 = 0;
+ _gotoSeq = 0;
+ _talkSeq = 0;
+ _restoreSlot = 0;
}
-void Object::load(Common::SeekableReadStream &s) {
+void Object::load(Common::SeekableReadStream &s, bool isRoseTattoo) {
char buffer[41];
s.read(buffer, 12);
_name = Common::String(buffer);
@@ -468,10 +524,10 @@ void Object::load(Common::SeekableReadStream &s) {
_goto.x = s.readSint16LE();
_goto.y = s.readSint16LE();
- _pickup = s.readByte();
- _defaultCommand = s.readByte();
- _lookFlag = s.readUint16LE();
- _pickupFlag = s.readUint16LE();
+ _pickup = isRoseTattoo ? 0 : s.readByte();
+ _defaultCommand = isRoseTattoo ? 0 : s.readByte();
+ _lookFlag = s.readSint16LE();
+ _pickupFlag = isRoseTattoo ? 0 : s.readSint16LE();
_requiredFlag = s.readSint16LE();
_noShapeSize.x = s.readUint16LE();
_noShapeSize.y = s.readUint16LE();
@@ -479,26 +535,45 @@ void Object::load(Common::SeekableReadStream &s) {
_misc = s.readByte();
_maxFrames = s.readUint16LE();
_flags = s.readByte();
- _aOpen.load(s);
+
+ if (!isRoseTattoo)
+ _aOpen.load(s);
+
_aType = (AType)s.readByte();
_lookFrames = s.readByte();
_seqCounter = s.readByte();
_lookPosition.x = s.readUint16LE();
- _lookPosition.y = s.readByte();
+ _lookPosition.y = isRoseTattoo ? s.readSint16LE() : s.readByte();
_lookFacing = s.readByte();
_lookcAnim = s.readByte();
- _aClose.load(s);
+
+ if (!isRoseTattoo)
+ _aClose.load(s);
+
_seqStack = s.readByte();
_seqTo = s.readByte();
_descOffset = s.readUint16LE();
_seqCounter2 = s.readByte();
_seqSize = s.readUint16LE();
- s.skip(1);
- _aMove.load(s);
- s.skip(8);
- for (int idx = 0; idx < USE_COUNT; ++idx)
- _use[idx].load(s);
+ if (isRoseTattoo) {
+ for (int idx = 0; idx < 6; ++idx)
+ _use[idx].load(s, true);
+
+ _quickDraw = s.readByte();
+ _scaleVal = s.readUint16LE();
+ _requiredFlag1 = s.readSint16LE();
+ _gotoSeq = s.readByte();
+ _talkSeq = s.readByte();
+ _restoreSlot = s.readByte();
+ } else {
+ s.skip(1);
+ _aMove.load(s);
+ s.skip(8);
+
+ for (int idx = 0; idx < 4; ++idx)
+ _use[idx].load(s, false);
+ }
}
void Object::toggleHidden() {
@@ -1032,17 +1107,30 @@ const Common::Rect Object::getOldBounds() const {
/*----------------------------------------------------------------*/
-void CAnim::load(Common::SeekableReadStream &s) {
+void CAnim::load(Common::SeekableReadStream &s, bool isRoseTattoo) {
char buffer[12];
s.read(buffer, 12);
_name = Common::String(buffer);
- s.read(_sequences, 30);
+ if (isRoseTattoo) {
+ Common::fill(&_sequences[0], &_sequences[30], 0);
+ _size = s.readUint32LE();
+ } else {
+ s.read(_sequences, 30);
+ }
+
_position.x = s.readSint16LE();
_position.y = s.readSint16LE();
- _size = s.readUint32LE();
- _type = (SpriteType)s.readUint16LE();
- _flags = s.readByte();
+
+ if (isRoseTattoo) {
+ _flags = s.readByte();
+ _scaleVal = s.readSint16LE();
+ } else {
+ _size = s.readUint32LE();
+ _type = (SpriteType)s.readUint16LE();
+ _flags = s.readByte();
+ }
+
_goto.x = s.readSint16LE();
_goto.y = s.readSint16LE();
_gotoDir = s.readSint16LE();
@@ -1053,6 +1141,22 @@ void CAnim::load(Common::SeekableReadStream &s) {
/*----------------------------------------------------------------*/
+CAnimStream::CAnimStream() {
+ _stream = nullptr;
+ _frameSize = 0;
+ _images = nullptr;
+ _imageFrame = nullptr;
+ _flags = 0;
+ _scaleVal = 0;
+ _zPlacement = 0;
+}
+
+void CAnimStream::getNextFrame() {
+ // TODO
+}
+
+/*----------------------------------------------------------------*/
+
SceneImage::SceneImage() {
_images = nullptr;
_maxFrames = 0;
diff --git a/engines/sherlock/objects.h b/engines/sherlock/objects.h
index bbd068eef7..d671066a23 100644
--- a/engines/sherlock/objects.h
+++ b/engines/sherlock/objects.h
@@ -46,7 +46,10 @@ enum SpriteType {
REMOVE = 5, // Object should be removed next frame
NO_SHAPE = 6, // Background object with no shape
HIDDEN = 7, // Hidden backgruond object
- HIDE_SHAPE = 8 // Object needs to be hidden
+ HIDE_SHAPE = 8, // Object needs to be hidden
+
+ // Rose Tattoo
+ HIDDEN_CHARACTER = 128
};
enum AType {
@@ -73,6 +76,7 @@ enum {
#define MAX_HOLMES_SEQUENCE 16
#define MAX_FRAME 30
+#define FIXED_INT_MULTIPLIER 100
// code put into sequences to defines 1-10 type seqs
#define SEQ_TO_CODE 67
@@ -98,16 +102,67 @@ public:
void operator-=(const Point32 &delta) { x -= delta.x; y -= delta.y; }
};
+
+struct WalkSequence {
+ Common::String _vgsName;
+ bool _horizFlip;
+ Common::Array<byte> _sequences;
+
+ WalkSequence() : _horizFlip(false) {}
+ const byte &operator[](int idx) { return _sequences[idx]; }
+
+ /**
+ * Load data for the sequence from a stream
+ */
+ void load(Common::SeekableReadStream &s);
+};
+
+class WalkSequences : public Common::Array < WalkSequence > {
+public:
+ WalkSequences &operator=(const WalkSequences &src);
+};
+
+enum { REVERSE_DIRECTION = 0x80 };
+#define NAMES_COUNT 4
+
+struct ActionType {
+ int _cAnimNum;
+ int _cAnimSpeed;
+ Common::String _names[NAMES_COUNT];
+
+ /**
+ * Load the data for the action
+ */
+ void load(Common::SeekableReadStream &s);
+};
+
+struct UseType {
+ int _cAnimNum;
+ int _cAnimSpeed;
+ Common::String _names[NAMES_COUNT];
+ int _useFlag; // Which flag USE will set (if any)
+ Common::String _target;
+ Common::String _verb;
+
+ UseType();
+
+ /**
+ * Load the data for the UseType
+ */
+ void load(Common::SeekableReadStream &s, bool isRoseTattoo);
+};
+
+
class Sprite {
private:
static SherlockEngine *_vm;
public:
- Common::String _name;
- Common::String _description;
- Common::StringArray _examine; // Examine in-depth description
+ Common::String _name;
+ Common::String _description;
+ Common::String _examine; // Examine in-depth description
Common::String _pickUp; // Message for if you can't pick up object
- const uint8 (*_sequences)[MAX_HOLMES_SEQUENCE][MAX_FRAME]; // Holds animation sequences
+ WalkSequences _walkSequences; // Holds animation sequences
ImageFile *_images; // Sprite images
ImageFrame *_imageFrame; // Pointer to shape in the images
int _walkCount; // Character walk counter
@@ -124,68 +179,69 @@ public:
int _status; // Status: open/closed, moved/not moved
int8 _misc; // Miscellaneous use
int _numFrames; // How many frames the object has
+
+ // Rose Tattoo fields
+ int _startSeq; // Frame sequence starts at
+ int _flags; // Flags for the sprite
+ int _aType; // Tells if this is an object, person, talk, etc.
+ int _lookFrames; // How many frames to play of a canim before pausing
+ int _seqCounter; // How many times the sequence has been run
+ Common::Point _lookPosition; // Where to look when examining object
+ int _lookFacing; // Direction to face when examining object
+ int _lookCAnim;
+ int _seqStack; // Allow gosubs to return to calling frame
+ int _seqTo; // Allows 1-5, 8-3 type sequences encoded in 2 bytes
+ uint _descOffset; // Tells where description starts in description text for scene
+ int _seqCounter2; // Counter of calling frame sequence
+ uint _seqSize; // Size of sequence
+ UseType _use[6];
+ int _quickDraw; // Flag telling whether to use quick draw routine or not
+ int _scaleVal; // Tells how to scale the sprite
+ int _requiredFlags1; // This flag must also be set, or the sprite is hidden
+ int _gotoSeq; // Used by Talk to tell which sequence to goto when able
+ int _talkSeq; // Tells which talk sequence currently in use (Talk or Listen)
+ int _restoreSlot; // Used when talk returns to the previous sequence
+
+ ImageFrame *_stopFrames[8]; // Stop/rest frame for each direction
+ ImageFile *_altImages; // Images used for alternate NPC sequences
+ bool _altSequences; // Which of the sequences the alt graphics apply to (0: main, 1=NPC seq)
+ int _centerWalk; // Flag telling the walk code to offset the walk destination
+ Common::Point _adjust; // Fine tuning adjustment to position when drawn
+ int _oldWalkSequence;
public:
Sprite() { clear(); }
+
static void setVm(SherlockEngine *vm) { _vm = vm; }
/**
- * Reset the data for the sprite
- */
+ * Reset the data for the sprite
+ */
void clear();
/**
- * Updates the image frame poiner for the sprite
- */
+ * Updates the image frame poiner for the sprite
+ */
void setImageFrame();
/**
- * This adjusts the sprites position, as well as it's animation sequence:
- */
+ * This adjusts the sprites position, as well as it's animation sequence:
+ */
void adjustSprite();
/**
- * Checks the sprite's position to see if it's collided with any special objects
- */
+ * Checks the sprite's position to see if it's collided with any special objects
+ */
void checkSprite();
/**
- * Return frame width
- */
+ * Return frame width
+ */
int frameWidth() const { return _imageFrame ? _imageFrame->_frame.w : 0; }
-
- /**
- * Return frame height
- */
- int frameHeight() const { return _imageFrame ? _imageFrame->_frame.h : 0; }
-};
-
-enum { REVERSE_DIRECTION = 0x80 };
-#define NAMES_COUNT 4
-
-struct ActionType {
- int _cAnimNum;
- int _cAnimSpeed;
- Common::String _names[NAMES_COUNT];
-
- /**
- * Load the data for the action
- */
- void load(Common::SeekableReadStream &s);
-};
-
-struct UseType {
- int _cAnimNum;
- int _cAnimSpeed;
- Common::String _names[NAMES_COUNT];
- int _useFlag; // Which flag USE will set (if any)
- Common::String _target;
-
- UseType();
/**
- * Load the data for the UseType
- */
- void load(Common::SeekableReadStream &s);
+ * Return frame height
+ */
+ int frameHeight() const { return _imageFrame ? _imageFrame->_frame.h : 0; }
};
enum { OBJ_BEHIND = 1, OBJ_FLIPPED = 2, OBJ_FORWARD = 4, TURNON_OBJ = 0x20, TURNOFF_OBJ = 0x40 };
@@ -233,35 +289,45 @@ public:
int _pickup;
int _defaultCommand; // Default right-click command
int _lookFlag; // Which flag LOOK will set (if any)
- int _pickupFlag; // Which flag PICKUP will set (if any)
int _requiredFlag; // Object will be hidden if not set
Common::Point _noShapeSize; // Size of a NO_SHAPE
int _status; // Status (open/closed, moved/not)
int8 _misc; // Misc field -- use varies with type
int _maxFrames; // Number of frames
int _flags; // Tells if object can be walked behind
- ActionType _aOpen; // Holds data for moving object
AType _aType; // Tells if this is an object, person, talk, etc.
int _lookFrames; // How many frames to play of the look anim before pausing
int _seqCounter; // How many times this sequence has been executed
Common::Point _lookPosition; // Where to walk when examining object
int _lookFacing; // Direction to face when examining object
int _lookcAnim;
- ActionType _aClose;
int _seqStack; // Allows gosubs to return to calling frame
int _seqTo; // Allows 1-5, 8-3 type sequences encoded in 2 bytes
uint _descOffset; // Tells where description starts in DescText
int _seqCounter2; // Counter of calling frame sequence
uint _seqSize; // Tells where description starts
+ UseType _use[6]; // Serrated Scalpel uses 4, Rose Tattoo 6
+
+ // Serrated Scalpel fields
+ int _pickupFlag; // Which flag PICKUP will set (if any)
+ ActionType _aOpen; // Holds data for moving object
+ ActionType _aClose;
ActionType _aMove;
- UseType _use[USE_COUNT];
+
+ // Rose Tattoo fields
+ int _quickDraw;
+ int _scaleVal;
+ int _requiredFlag1;
+ int _gotoSeq;
+ int _talkSeq;
+ int _restoreSlot;
Object();
/**
* Load the data for the object
*/
- void load(Common::SeekableReadStream &s);
+ void load(Common::SeekableReadStream &s, bool isRoseTattoo);
/**
* Toggle the type of an object between hidden and active
@@ -326,20 +392,45 @@ public:
struct CAnim {
Common::String _name; // Name
- byte _sequences[MAX_FRAME]; // Animation sequences
Common::Point _position; // Position
int _size; // Size of uncompressed animation
- SpriteType _type;
int _flags; // Tells if can be walked behind
Common::Point _goto; // coords holmes should walk to before starting canim
int _gotoDir;
Common::Point _teleportPos; // Location Holmes shoul teleport to after
int _teleportDir; // playing canim
+ // Scalpel specific
+ byte _sequences[MAX_FRAME]; // Animation sequences
+ SpriteType _type;
+
+ // Rose Tattoo specific
+ int _scaleVal; // How much the canim is scaled
+
/**
* Load the data for the animation
*/
- void load(Common::SeekableReadStream &s);
+ void load(Common::SeekableReadStream &s, bool isRoseTattoo);
+};
+
+struct CAnimStream {
+ Common::SeekableReadStream *_stream; // Stream to read frames from
+ int _frameSize; // Temporary used to store the frame size
+
+ void *_images; // TOOD: FIgure out hwo to hook up ImageFile with streaming support
+ ImageFrame *_imageFrame;
+
+ Common::Point _position; // Animation position
+ Common::Rect _oldBounds; // Bounds of previous frame
+ Common::Rect _removeBounds; // Remove area for just drawn frame
+
+ int _flags; // Flags
+ int _scaleVal; // Specifies the scale amount
+ int _zPlacement; // Used by doBgAnim for determining Z order
+
+ CAnimStream();
+
+ void getNextFrame();
};
struct SceneImage {
diff --git a/engines/sherlock/people.cpp b/engines/sherlock/people.cpp
index 3a630fdd99..0ef49ffefb 100644
--- a/engines/sherlock/people.cpp
+++ b/engines/sherlock/people.cpp
@@ -50,10 +50,38 @@ static const uint8 CHARACTER_SEQUENCES[MAX_HOLMES_SEQUENCE][MAX_FRAME] = {
{ 52, 1, 2, 3, 4, 0 } // Goto Stand Down Left
};
+// Rose Tattoo walk image libraries
+// Walk resources within WALK.LIB
+#define NUM_IN_WALK_LIB 10
+const char *const WALK_LIB_NAMES[10] = {
+ "SVGAWALK.VGS",
+ "COATWALK.VGS",
+ "WATSON.VGS",
+ "NOHAT.VGS",
+ "TUPRIGHT.VGS",
+ "TRIGHT.VGS",
+ "TDOWNRG.VGS",
+ "TWUPRIGH.VGS",
+ "TWRIGHT.VGS",
+ "TWDOWNRG.VGS"
+};
+
+/*----------------------------------------------------------------*/
+
+Person::Person() : Sprite(), _walkLoaded(false), _npcIndex(0), _npcStack(0), _npcPause(false) {
+ Common::fill(&_npcPath[0], &_npcPath[MAX_NPC_PATH], 0);
+ _tempX = _tempScaleVal = 0;
+}
+
+void Person::clearNPC() {
+ Common::fill(&_npcPath[0], &_npcPath[MAX_NPC_PATH], 0);
+ _npcIndex = _npcStack = 0;
+ _npcName = "";
+}
+
/*----------------------------------------------------------------*/
People::People(SherlockEngine *vm) : _vm(vm), _player(_data[0]) {
- _walkLoaded = false;
_holmesOn = true;
_oldWalkSequence = -1;
_allowWalkAbort = false;
@@ -68,66 +96,168 @@ People::People(SherlockEngine *vm) : _vm(vm), _player(_data[0]) {
_holmesQuotient = 0;
_hSavedPos = Common::Point(-1, -1);
_hSavedFacing = -1;
+ _forceWalkReload = false;
+ _useWalkLib = false;
+ _walkControl = 0;
_portrait._sequences = new byte[32];
}
People::~People() {
- if (_walkLoaded)
- delete _data[PLAYER]._images;
+ for (int idx = 0; idx < MAX_CHARACTERS; ++idx) {
+ if (_data[idx]._walkLoaded)
+ delete _data[PLAYER]._images;
+ }
+
delete _talkPics;
delete[] _portrait._sequences;
}
void People::reset() {
- // Note: The engine has theoretical support for two player characters but only the first one is used.
- // Watson is, instead, handled by a different sprite in each scene, with a very simple initial movement, if any
- Sprite &p = _data[PLAYER];
-
- p._description = "Sherlock Holmes!";
- p._type = CHARACTER;
- p._position = Common::Point(10000, 11000);
- p._sequenceNumber = STOP_DOWNRIGHT;
- p._sequences = &CHARACTER_SEQUENCES;
- p._imageFrame = nullptr;
- p._frameNumber = 1;
- p._delta = Common::Point(0, 0);
- p._oldPosition = Common::Point(0, 0);
- p._oldSize = Common::Point(0, 0);
- p._misc = 0;
- p._walkCount = 0;
- p._pickUp = "";
- p._allow = 0;
- p._noShapeSize = Common::Point(0, 0);
- p._goto = Common::Point(0, 0);
- p._status = 0;
+ _data[0]._description = "Sherlock Holmes!";
+
+ // Note: Serrated Scalpel only uses a single Person slot for Sherlock.. Watson is handled by scene sprites
+ int count = IS_SERRATED_SCALPEL ? 1 : MAX_CHARACTERS;
+ for (int idx = 0; idx < count; ++idx) {
+ Sprite &p = _data[idx];
+
+ p._type = (idx == 0) ? CHARACTER : INVALID;
+ if (IS_SERRATED_SCALPEL)
+ p._position = Point32(10000, 11000);
+ else
+ p._position = Point32(36000, 29000);
+
+ p._sequenceNumber = STOP_DOWNRIGHT;
+ p._imageFrame = nullptr;
+ p._frameNumber = 1;
+ p._delta = Common::Point(0, 0);
+ p._oldPosition = Common::Point(0, 0);
+ p._oldSize = Common::Point(0, 0);
+ p._misc = 0;
+ p._walkCount = 0;
+ p._pickUp = "";
+ p._allow = 0;
+ p._noShapeSize = Common::Point(0, 0);
+ p._goto = Common::Point(0, 0);
+ p._status = 0;
+ p._seqTo = 0;
+ p._seqCounter = p._seqCounter2 = 0;
+ p._seqStack = 0;
+ p._gotoSeq = p._talkSeq = 0;
+ p._restoreSlot = 0;
+ p._startSeq = 0;
+ p._altImages = nullptr;
+ p._altSequences = 0;
+ p._centerWalk = true;
+ p._adjust = Common::Point(0, 0);
+
+ // Load the default walk sequences
+ p._oldWalkSequence = -1;
+ p._walkSequences.clear();
+ if (IS_SERRATED_SCALPEL) {
+ p._walkSequences.resize(MAX_HOLMES_SEQUENCE);
+ for (int seqIdx = 0; seqIdx < MAX_HOLMES_SEQUENCE; ++seqIdx) {
+ p._walkSequences[seqIdx]._sequences.clear();
+
+ const byte *pSrc = &CHARACTER_SEQUENCES[seqIdx][0];
+ do {
+ p._walkSequences[seqIdx]._sequences.push_back(*pSrc);
+ } while (*pSrc++);
+ }
+ }
+ }
// Reset any walk path in progress when Sherlock leaves scenes
_walkTo.clear();
}
bool People::loadWalk() {
- if (_walkLoaded) {
- return false;
+ Resources &res = *_vm->_res;
+ bool result = false;
+
+ if (IS_SERRATED_SCALPEL) {
+ if (_data[PLAYER]._walkLoaded) {
+ return false;
+ } else {
+ _data[PLAYER]._images = new ImageFile("walk.vgs");
+ _data[PLAYER].setImageFrame();
+ _data[PLAYER]._walkLoaded = true;
+
+ result = true;
+ }
} else {
- _data[PLAYER]._images = new ImageFile("walk.vgs");
- _data[PLAYER].setImageFrame();
- _walkLoaded = true;
+ for (int idx = 0; idx < MAX_CHARACTERS; ++idx) {
+ if (!_data[idx]._walkLoaded && (_data[idx]._type == CHARACTER || _data[idx]._type == HIDDEN_CHARACTER)) {
+ if (_data[idx]._type == HIDDEN_CHARACTER)
+ _data[idx]._type = INVALID;
+
+ // See if this is one of the more used Walk Graphics stored in WALK.LIB
+ for (int libNum = 0; libNum < NUM_IN_WALK_LIB; ++libNum) {
+ if (!_data[0]._walkVGSName.compareToIgnoreCase(WALK_LIB_NAMES[libNum])) {
+ _useWalkLib = true;
+ break;
+ }
+ }
- return true;
+ // Load the images for the character
+ _data[idx]._images = new ImageFile(_data[idx]._walkVGSName, false);
+ _data[idx]._numFrames = _data[idx]._images->size();
+
+ // Load walk sequence data
+ Common::String fname = Common::String(_data[idx]._walkVGSName.c_str(), strchr(_data[idx]._walkVGSName.c_str(), '.'));
+ fname += ".SEQ";
+
+ // Load the walk sequence data
+ Common::SeekableReadStream *stream = res.load(fname, _useWalkLib ? "walk.lib" : "vgs.lib");
+
+ _data[idx]._walkSequences.resize(stream->readByte());
+
+ for (uint seqNum = 0; seqNum < _data[idx]._walkSequences.size(); ++seqNum)
+ _data[idx]._walkSequences[seqNum].load(*stream);
+
+ // Close the sequences resource
+ delete stream;
+ _useWalkLib = false;
+
+ _data[idx]._frameNumber = 0;
+ _data[idx].setImageFrame();
+
+ // Set the stop Frames pointers
+ for (int dirNum = 0; dirNum < 8; ++dirNum) {
+ int count = 0;
+ while (_data[idx]._walkSequences[dirNum + 8][count] != 0)
+ ++count;
+ count += 2;
+ count = _data[idx]._walkSequences[dirNum + 8][count] - 1;
+ _data[idx]._stopFrames[dirNum] = &(*_data[idx]._images)[count];
+ }
+
+ result = true;
+ _data[idx]._walkLoaded = true;
+ } else if (_data[idx]._type != CHARACTER) {
+ _data[idx]._walkLoaded = false;
+ }
+ }
}
+
+ _forceWalkReload = false;
+ return result;
}
bool People::freeWalk() {
- if (_walkLoaded) {
- delete _player._images;
- _player._images = nullptr;
-
- _walkLoaded = false;
- return true;
- } else {
- return false;
+ bool result = false;
+
+ for (int idx = 0; idx < MAX_CHARACTERS; ++idx) {
+ if (_data[idx]._walkLoaded) {
+ delete _data[idx]._images;
+ _data[idx]._images = nullptr;
+
+ _data[idx]._walkLoaded = false;
+ result = true;
+ }
}
+
+ return result;
}
void People::setWalking() {
@@ -553,9 +683,24 @@ void People::setTalking(int speaker) {
void People::synchronize(Common::Serializer &s) {
s.syncAsByte(_holmesOn);
- s.syncAsSint16LE(_player._position.x);
- s.syncAsSint16LE(_player._position.y);
- s.syncAsSint16LE(_player._sequenceNumber);
+
+ if (IS_SERRATED_SCALPEL) {
+ s.syncAsSint16LE(_player._position.x);
+ s.syncAsSint16LE(_player._position.y);
+ s.syncAsSint16LE(_player._sequenceNumber);
+ } else {
+ for (int idx = 0; idx < MAX_CHARACTERS; ++idx) {
+ Person &p = _data[idx];
+ s.syncAsSint16LE(p._position.x);
+ s.syncAsSint16LE(p._position.y);
+ s.syncAsSint16LE(p._sequenceNumber);
+ s.syncAsSint16LE(p._type);
+ s.syncString(p._walkVGSName);
+ s.syncString(p._description);
+ s.syncString(p._examine);
+ }
+ }
+
s.syncAsSint16LE(_holmesQuotient);
if (s.isLoading()) {
diff --git a/engines/sherlock/people.h b/engines/sherlock/people.h
index 8244fd9b59..013727d8ba 100644
--- a/engines/sherlock/people.h
+++ b/engines/sherlock/people.h
@@ -30,13 +30,13 @@
namespace Sherlock {
-// Player definitions. The game has theoretical support for two player characters but only the first one is used.
-// Watson is, instead, handled by a different sprite in each scene, with a very simple initial movement, if any
enum PeopleId {
PLAYER = 0,
AL = 0,
PEG = 1,
- MAX_PLAYERS = 2
+ MAX_CHARACTERS = 6,
+ MAX_NPC = 5,
+ MAX_NPC_PATH = 200
};
// Animation sequence identifiers for characters
@@ -63,20 +63,37 @@ struct PersonData {
_name(name), _portrait(portrait), _stillSequences(stillSequences), _talkSequences(talkSequences) {}
};
-class SherlockEngine;
-
class Person : public Sprite {
public:
- Person() : Sprite() {}
-
+ bool _walkLoaded;
Common::String _portrait;
+
+ // NPC related fields
+ int _npcIndex;
+ int _npcStack;
+ bool _npcPause;
+ byte _npcPath[MAX_NPC_PATH];
+ Common::String _npcName;
+ int _tempX;
+ int _tempScaleVal;
+
+ // Rose Tattoo fields
+ Common::String _walkVGSName; // Name of walk library person is using
+public:
+ Person();
+
+ /**
+ * Clear the NPC related data
+ */
+ void clearNPC();
};
+class SherlockEngine;
+
class People {
private:
SherlockEngine *_vm;
- Person _data[MAX_PLAYERS];
- bool _walkLoaded;
+ Person _data[MAX_CHARACTERS];
int _oldWalkSequence;
int _srcZone, _destZone;
public:
@@ -97,25 +114,24 @@ public:
bool _speakerFlip;
bool _holmesFlip;
int _holmesQuotient;
+ bool _forceWalkReload;
+ bool _useWalkLib;
+
+ int _walkControl;
public:
People(SherlockEngine *vm);
~People();
Person &operator[](PeopleId id) {
- assert(id < MAX_PLAYERS);
+ assert(id < MAX_CHARACTERS);
return _data[id];
}
Person &operator[](int idx) {
- assert(idx < MAX_PLAYERS);
+ assert(idx < MAX_CHARACTERS);
return _data[idx];
}
/**
- * Returns true if Sherlock is visible on the screen and enabled
- */
- bool isHolmesActive() const { return _walkLoaded && _holmesOn; }
-
- /**
* Reset the player data
*/
void reset();
diff --git a/engines/sherlock/resources.cpp b/engines/sherlock/resources.cpp
index 091ef3ea12..f1c705b522 100644
--- a/engines/sherlock/resources.cpp
+++ b/engines/sherlock/resources.cpp
@@ -65,7 +65,7 @@ void Cache::load(const Common::String &name, Common::SeekableReadStream &stream)
// Check whether the file is compressed
if (signature == MKTAG('L', 'Z', 'V', 26)) {
// It's compressed, so decompress the file and store it's data in the cache entry
- Common::SeekableReadStream *decompressed = _vm->_res->decompressLZ(stream);
+ Common::SeekableReadStream *decompressed = _vm->_res->decompress(stream);
cacheEntry.resize(decompressed->size());
decompressed->read(&cacheEntry[0], decompressed->size());
@@ -91,9 +91,12 @@ Resources::Resources(SherlockEngine *vm) : _vm(vm), _cache(vm) {
if (_vm->_interactiveFl) {
addToCache("vgs.lib");
addToCache("talk.lib");
- addToCache("sequence.txt");
addToCache("journal.txt");
- addToCache("portrait.lib");
+
+ if (IS_SERRATED_SCALPEL) {
+ addToCache("sequence.txt");
+ addToCache("portrait.lib");
+ }
}
}
@@ -103,8 +106,11 @@ void Resources::addToCache(const Common::String &filename) {
// Check to see if the file is a library
Common::SeekableReadStream *stream = load(filename);
uint32 header = stream->readUint32BE();
+
if (header == MKTAG('L', 'I', 'B', 26))
- loadLibraryIndex(filename, stream);
+ loadLibraryIndex(filename, stream, false);
+ else if (header == MKTAG('L', 'I', 'C', 26))
+ loadLibraryIndex(filename, stream, true);
delete stream;
}
@@ -159,12 +165,14 @@ Common::SeekableReadStream *Resources::load(const Common::String &filename) {
void Resources::decompressIfNecessary(Common::SeekableReadStream *&stream) {
bool isCompressed = stream->readUint32BE() == MKTAG('L', 'Z', 'V', 26);
- stream->seek(-4, SEEK_CUR);
if (isCompressed) {
- Common::SeekableReadStream *newStream = decompressLZ(*stream);
+ int outSize = stream->readUint32LE();
+ Common::SeekableReadStream *newStream = decompressLZ(*stream, outSize);
delete stream;
stream = newStream;
+ } else {
+ stream->seek(-4, SEEK_CUR);
}
}
@@ -174,7 +182,7 @@ Common::SeekableReadStream *Resources::load(const Common::String &filename, cons
// Check if the library has already had it's index read, and if not, load it
if (!_indexes.contains(libraryFile))
- loadLibraryIndex(libraryFile, libStream);
+ loadLibraryIndex(libraryFile, libStream, false);
// Extract the data for the specified resource and return it
LibraryEntry &entry = _indexes[libraryFile][filename];
@@ -192,7 +200,7 @@ bool Resources::exists(const Common::String &filename) const {
}
void Resources::loadLibraryIndex(const Common::String &libFilename,
- Common::SeekableReadStream *stream) {
+ Common::SeekableReadStream *stream, bool isNewStyle) {
uint32 offset, nextOffset;
// Create an index entry
@@ -203,6 +211,9 @@ void Resources::loadLibraryIndex(const Common::String &libFilename,
stream->seek(4);
int count = stream->readUint16LE();
+ if (isNewStyle)
+ stream->seek((count + 1) * 8, SEEK_CUR);
+
// Loop through reading in the entries
for (int idx = 0; idx < count; ++idx) {
// Read the name of the resource
@@ -231,24 +242,48 @@ int Resources::resourceIndex() const {
return _resourceIndex;
}
-Common::SeekableReadStream *Resources::decompressLZ(Common::SeekableReadStream &source) {
- if (_vm->getGameID() == GType_SerratedScalpel) {
- uint32 id = source.readUint32BE();
- assert(id == MKTAG('L', 'Z', 'V', 0x1A));
- }
+Common::SeekableReadStream *Resources::decompress(Common::SeekableReadStream &source) {
+ // This variation can't be used by Rose Tattoo, since compressed resources include the input size,
+ // not the output size. Which means their decompression has to be done via passed buffers
+ assert(_vm->getGameID() == GType_SerratedScalpel);
+
+ uint32 id = source.readUint32BE();
+ assert(id == MKTAG('L', 'Z', 'V', 0x1A));
- uint32 size = source.readUint32LE();
- return decompressLZ(source, size);
+ uint32 outputSize = source.readUint32LE();
+ return decompressLZ(source, outputSize);
+}
+
+Common::SeekableReadStream *Resources::decompress(Common::SeekableReadStream &source, uint32 outSize) {
+ int inSize = (_vm->getGameID() == GType_RoseTattoo) ? source.readUint32LE() : -1;
+ byte *outBuffer = (byte *)malloc(outSize);
+ Common::MemoryReadStream *outStream = new Common::MemoryReadStream(outBuffer, outSize, DisposeAfterUse::YES);
+
+ decompressLZ(source, outBuffer, outSize, inSize);
+
+ return outStream;
+}
+
+void Resources::decompress(Common::SeekableReadStream &source, byte *buffer, uint32 outSize) {
+ int inputSize = (_vm->getGameID() == GType_RoseTattoo) ? source.readUint32LE() : -1;
+
+ decompressLZ(source, buffer, outSize, inputSize);
}
Common::SeekableReadStream *Resources::decompressLZ(Common::SeekableReadStream &source, uint32 outSize) {
+ byte *dataOut = (byte *)malloc(outSize);
+ decompressLZ(source, dataOut, outSize, -1);
+
+ return new Common::MemoryReadStream(dataOut, outSize, DisposeAfterUse::YES);
+}
+
+void Resources::decompressLZ(Common::SeekableReadStream &source, byte *outBuffer, int32 outSize, int32 inSize) {
byte lzWindow[4096];
uint16 lzWindowPos;
uint16 cmd;
- byte *outBuffer = (byte *)malloc(outSize);
byte *outBufferEnd = outBuffer + outSize;
- Common::MemoryReadStream *outS = new Common::MemoryReadStream(outBuffer, outSize, DisposeAfterUse::YES);
+ int endPos = source.pos() + inSize;
memset(lzWindow, 0xFF, 0xFEE);
lzWindowPos = 0xFEE;
@@ -264,8 +299,7 @@ Common::SeekableReadStream *Resources::decompressLZ(Common::SeekableReadStream &
*outBuffer++ = literal;
lzWindow[lzWindowPos] = literal;
lzWindowPos = (lzWindowPos + 1) & 0x0FFF;
- }
- else {
+ } else {
int copyPos, copyLen;
copyPos = source.readByte();
copyLen = source.readByte();
@@ -279,9 +313,7 @@ Common::SeekableReadStream *Resources::decompressLZ(Common::SeekableReadStream &
lzWindowPos = (lzWindowPos + 1) & 0x0FFF;
}
}
- } while (outBuffer < outBufferEnd);
-
- return outS;
+ } while ((outSize == -1 || outBuffer < outBufferEnd) && (inSize == -1 || source.pos() < endPos));
}
/*----------------------------------------------------------------*/
@@ -325,13 +357,14 @@ void ImageFile::load(Common::SeekableReadStream &stream, bool skipPalette, bool
// Animation cutscene image files use a 16-bit x offset
frame._offset.x = stream.readUint16LE();
frame._rleEncoded = (frame._offset.x & 0xff) == 1;
+ frame._offset.y = stream.readByte();
} else {
// Standard image files have a separate byte for the RLE flag, and an 8-bit X offset
frame._rleEncoded = stream.readByte() == 1;
frame._offset.x = stream.readByte();
+ frame._offset.y = stream.readByte();
}
- frame._offset.y = stream.readByte();
frame._rleEncoded = !skipPalette && frame._rleEncoded;
if (frame._paletteBase) {
@@ -385,6 +418,28 @@ void ImageFile::decompressFrame(ImageFrame &frame, const byte *src) {
*pDest++ = *src & 0xF;
*pDest++ = (*src >> 4);
}
+ } else if (frame._rleEncoded && _vm->getGameID() == GType_RoseTattoo) {
+ // Rose Tattoo run length encoding doesn't use the RLE marker byte
+ byte *dst = (byte *)frame._frame.getPixels();
+
+ for (int yp = 0; yp < frame._height; ++yp) {
+ int xSize = frame._width;
+ while (xSize > 0) {
+ // Skip a given number of pixels
+ byte skip = *src++;
+ dst += skip;
+ xSize -= skip;
+ if (!xSize)
+ break;
+
+ // Get a run length, and copy the following number of pixels
+ int rleCount = *src++;
+ xSize -= rleCount;
+ while (rleCount-- > 0)
+ *dst++ = *src++;
+ }
+ assert(xSize == 0);
+ }
} else if (frame._rleEncoded) {
// RLE encoded
byte *dst = (byte *)frame._frame.getPixels();
@@ -411,4 +466,34 @@ void ImageFile::decompressFrame(ImageFrame &frame, const byte *src) {
}
}
+/*----------------------------------------------------------------*/
+
+int ImageFrame::sDrawXSize(int scaleVal) const {
+ int width = _width;
+ int scale = scaleVal == 0 ? 1 : scaleVal;
+
+ if (scaleVal >= 256)
+ --width;
+
+ int result = width * 256 / scale;
+ if (scaleVal >= 256)
+ ++result;
+
+ return result;
+}
+
+int ImageFrame::sDrawYSize(int scaleVal) const {
+ int height = _height;
+ int scale = scaleVal == 0 ? 1 : scaleVal;
+
+ if (scaleVal >= 256)
+ --height;
+
+ int result = height * 256 / scale;
+ if (scaleVal >= 256)
+ ++result;
+
+ return result;
+}
+
} // End of namespace Sherlock
diff --git a/engines/sherlock/resources.h b/engines/sherlock/resources.h
index fb91b30f94..5c071e3922 100644
--- a/engines/sherlock/resources.h
+++ b/engines/sherlock/resources.h
@@ -90,7 +90,7 @@ private:
/**
* Reads in the index from a library file, and caches it's index for later use
*/
- void loadLibraryIndex(const Common::String &libFilename, Common::SeekableReadStream *stream);
+ void loadLibraryIndex(const Common::String &libFilename, Common::SeekableReadStream *stream, bool isNewStyle);
public:
Resources(SherlockEngine *vm);
@@ -140,14 +140,29 @@ public:
int resourceIndex() const;
/**
- * Decompresses an LZW block of data with a specified output size
+ * Decompresses LZW compressed data
+ */
+ Common::SeekableReadStream *decompress(Common::SeekableReadStream &source);
+
+ /**
+ * Decompresses LZW compressed data
+ */
+ Common::SeekableReadStream *decompress(Common::SeekableReadStream &source, uint32 outSize);
+
+ /**
+ * Decompresses LZW compressed data
+ */
+ void decompress(Common::SeekableReadStream &source, byte *buffer, uint32 outSize);
+
+ /**
+ * Decompresses LZW compressed data
*/
static Common::SeekableReadStream *decompressLZ(Common::SeekableReadStream &source, uint32 outSize);
-
+
/**
- * Decompress an LZW compressed resource
+ * Decompresses LZW compressed data
*/
- Common::SeekableReadStream *decompressLZ(Common::SeekableReadStream &source);
+ static void decompressLZ(Common::SeekableReadStream &source, byte *outBuffer, int32 outSize, int32 inSize);
};
struct ImageFrame {
@@ -158,6 +173,16 @@ struct ImageFrame {
Common::Point _offset;
byte _rleMarker;
Graphics::Surface _frame;
+
+ /**
+ * Return the frame width adjusted by a specified scale amount
+ */
+ int sDrawXSize(int scaleVal) const;
+
+ /**
+ * Return the frame height adjusted by a specified scale amount
+ */
+ int sDrawYSize(int scaleVal) const;
};
class ImageFile : public Common::Array<ImageFrame> {
diff --git a/engines/sherlock/scalpel/scalpel.cpp b/engines/sherlock/scalpel/scalpel.cpp
index bee44bfb7d..304445df76 100644
--- a/engines/sherlock/scalpel/scalpel.cpp
+++ b/engines/sherlock/scalpel/scalpel.cpp
@@ -24,6 +24,7 @@
#include "sherlock/sherlock.h"
#include "sherlock/music.h"
#include "sherlock/animation.h"
+#include "engines/util.h"
namespace Sherlock {
@@ -379,6 +380,9 @@ ScalpelEngine::~ScalpelEngine() {
}
void ScalpelEngine::initialize() {
+ initGraphics(320, 200, false);
+
+ // Let the base engine intialize
SherlockEngine::initialize();
_darts = new Darts(this);
@@ -580,9 +584,9 @@ bool ScalpelEngine::scrollCredits() {
_screen->transBlitFrom(creditsImages[1], Common::Point(10, 400 - idx), false, 0);
// Don't show credit text on the top and bottom ten rows of the screen
- _screen->blitFrom(_screen->_backBuffer1, Common::Point(0, 0), Common::Rect(0, 0, SHERLOCK_SCREEN_WIDTH, 10));
- _screen->blitFrom(_screen->_backBuffer1, Common::Point(0, SHERLOCK_SCREEN_HEIGHT - 10),
- Common::Rect(0, SHERLOCK_SCREEN_HEIGHT - 10, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT));
+ _screen->blitFrom(_screen->_backBuffer1, Common::Point(0, 0), Common::Rect(0, 0, _screen->w(), 10));
+ _screen->blitFrom(_screen->_backBuffer1, Common::Point(0, _screen->h() - 10),
+ Common::Rect(0, _screen->h() - 10, _screen->w(), _screen->h()));
_events->delay(100);
}
@@ -804,7 +808,6 @@ void ScalpelEngine::startScene() {
break;
}
- _events->loadCursors("rmouse.vgs");
_events->setCursor(ARROW);
if (_scene->_goToScene == 99) {
@@ -828,9 +831,11 @@ void ScalpelEngine::eraseMirror12() {
void ScalpelEngine::doMirror12() {
People &people = *_people;
+ Person &player = people._player;
+
Common::Point pt((*_people)[AL]._position.x / 100, (*_people)[AL]._position.y / 100);
- int frameNum = (*people[AL]._sequences)[people[AL]._sequenceNumber][people[AL]._frameNumber] +
- (*people[AL]._sequences)[people[AL]._sequenceNumber][0] - 2;
+ int frameNum = player._walkSequences[player._sequenceNumber][player._frameNumber] +
+ player._walkSequences[player._sequenceNumber][0] - 2;
switch ((*_people)[AL]._sequenceNumber) {
case WALK_DOWN:
diff --git a/engines/sherlock/scalpel/scalpel_scene.cpp b/engines/sherlock/scalpel/scalpel_scene.cpp
new file mode 100644
index 0000000000..b6a42419d8
--- /dev/null
+++ b/engines/sherlock/scalpel/scalpel_scene.cpp
@@ -0,0 +1,381 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "sherlock/scalpel/scalpel_scene.h"
+#include "sherlock/scalpel/scalpel.h"
+#include "sherlock/events.h"
+#include "sherlock/people.h"
+#include "sherlock/screen.h"
+
+namespace Sherlock {
+
+namespace Scalpel {
+
+void ScalpelScene::checkBgShapes() {
+ People &people = *_vm->_people;
+ Person &holmes = people._player;
+ 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;
+ Sound &sound = *_vm->_sound;
+ UserInterface &ui = *_vm->_ui;
+ Common::Point mousePos = events.mousePos();
+
+ 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);
+ }
+ }
+
+ if (sound._diskSoundPlaying && !*sound._soundIsOn) {
+ // Loaded sound just finished playing
+ sound.freeDigiSound();
+ }
+}
+
+void ScalpelScene::doBgAnim() {
+ ScalpelEngine &vm = *((ScalpelEngine *)_vm);
+ Events &events = *_vm->_events;
+ People &people = *_vm->_people;
+ Screen &screen = *_vm->_screen;
+ Talk &talk = *_vm->_talk;
+
+ 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 == 12)
+ vm.eraseMirror12();
+
+ // Restore the back buffer from the back buffer 2 in the changed area
+ Common::Rect bounds(people[AL]._oldPosition.x, people[AL]._oldPosition.y,
+ people[AL]._oldPosition.x + people[AL]._oldSize.x,
+ people[AL]._oldPosition.y + people[AL]._oldSize.y);
+ Common::Point pt(bounds.left, bounds.top);
+
+ if (people[AL]._type == CHARACTER)
+ screen.restoreBackground(bounds);
+ else if (people[AL]._type == REMOVE)
+ screen._backBuffer->blitFrom(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._backBuffer->blitFrom(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[AL]._type == CHARACTER && people._holmesOn)
+ people[AL].adjustSprite();
+
+ // Flag the bg shapes which need to be redrawn
+ checkBgShapes();
+
+ if (_currentScene == 12)
+ vm.doMirror12();
+
+ // 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._backBuffer->transBlitFrom(*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._backBuffer->transBlitFrom(*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._backBuffer->transBlitFrom(*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._backBuffer->transBlitFrom(*o._imageFrame, o._position, o._flags & OBJ_FLIPPED);
+ }
+ }
+
+ // Draw the person if not animating
+ if (people[AL]._type == CHARACTER && people[AL]._walkLoaded) {
+ // If Holmes is too far to the right, move him back so he's on-screen
+ int xRight = SHERLOCK_SCREEN_WIDTH - 2 - people[AL]._imageFrame->_frame.w;
+ int tempX = MIN(people[AL]._position.x / FIXED_INT_MULTIPLIER, xRight);
+
+ bool flipped = people[AL]._sequenceNumber == WALK_LEFT || people[AL]._sequenceNumber == STOP_LEFT ||
+ people[AL]._sequenceNumber == WALK_UPLEFT || people[AL]._sequenceNumber == STOP_UPLEFT ||
+ people[AL]._sequenceNumber == WALK_DOWNRIGHT || people[AL]._sequenceNumber == STOP_DOWNRIGHT;
+ screen._backBuffer->transBlitFrom(*people[AL]._imageFrame,
+ Common::Point(tempX, people[AL]._position.y / FIXED_INT_MULTIPLIER - people[AL]._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._backBuffer->transBlitFrom(*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._backBuffer->transBlitFrom(*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._backBuffer->transBlitFrom(*o._imageFrame, o._position, o._flags & OBJ_FLIPPED);
+ }
+
+ // Draw any active portrait
+ if (people._portraitLoaded && people._portrait._type == ACTIVE_BG_SHAPE)
+ screen._backBuffer->transBlitFrom(*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._backBuffer->transBlitFrom(*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._backBuffer->transBlitFrom(*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[AL]._type != INVALID && ((_goToScene == -1 || _canimShapes.empty()))) {
+ if (people[AL]._type == REMOVE) {
+ screen.slamRect(Common::Rect(
+ people[AL]._oldPosition.x, people[AL]._oldPosition.y,
+ people[AL]._oldPosition.x + people[AL]._oldSize.x,
+ people[AL]._oldPosition.y + people[AL]._oldSize.y
+ ));
+ people[AL]._type = INVALID;
+ } else {
+ screen.flushImage(people[AL]._imageFrame,
+ Common::Point(people[AL]._position.x / FIXED_INT_MULTIPLIER,
+ people[AL]._position.y / FIXED_INT_MULTIPLIER - people[AL].frameHeight()),
+ &people[AL]._oldPosition.x, &people[AL]._oldPosition.y,
+ &people[AL]._oldSize.x, &people[AL]._oldSize.y);
+ }
+ }
+
+ if (_currentScene == 12)
+ vm.flushMirror12();
+
+ 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
+ _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
+ _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);
+ }
+}
+
+} // End of namespace Scalpel
+
+} // End of namespace Sherlock
diff --git a/engines/sherlock/scalpel/scalpel_scene.h b/engines/sherlock/scalpel/scalpel_scene.h
new file mode 100644
index 0000000000..e5a442f44f
--- /dev/null
+++ b/engines/sherlock/scalpel/scalpel_scene.h
@@ -0,0 +1,61 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#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 {
+
+class ScalpelScene : public Scene {
+private:
+ void doBgAnimCheckCursor();
+protected:
+ /**
+ * 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
+ */
+ virtual void checkBgShapes();
+public:
+ ScalpelScene(SherlockEngine *vm) : Scene(vm) {}
+
+ /**
+ * Draw all objects and characters.
+ */
+ virtual void doBgAnim();
+};
+
+} // End of namespace Scalpel
+
+} // End of namespace Sherlock
+
+#endif
diff --git a/engines/sherlock/scalpel/scalpel_user_interface.cpp b/engines/sherlock/scalpel/scalpel_user_interface.cpp
new file mode 100644
index 0000000000..f9ec8bb1bb
--- /dev/null
+++ b/engines/sherlock/scalpel/scalpel_user_interface.cpp
@@ -0,0 +1,2276 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "sherlock/scalpel/scalpel_user_interface.h"
+#include "sherlock/sherlock.h"
+#include "sherlock/scalpel/settings.h"
+
+namespace Sherlock {
+
+namespace Scalpel {
+
+// Main user interface menu control locations
+const int MENU_POINTS[12][4] = {
+ { 13, 153, 72, 165 },
+ { 13, 169, 72, 181 },
+ { 13, 185, 72, 197 },
+ { 88, 153, 152, 165 },
+ { 88, 169, 152, 181 },
+ { 88, 185, 152, 197 },
+ { 165, 153, 232, 165 },
+ { 165, 169, 232, 181 },
+ { 165, 185, 233, 197 },
+ { 249, 153, 305, 165 },
+ { 249, 169, 305, 181 },
+ { 249, 185, 305, 197 }
+};
+
+// Inventory control locations */
+const int INVENTORY_POINTS[8][3] = {
+ { 4, 50, 29 },
+ { 52, 99, 77 },
+ { 101, 140, 123 },
+ { 142, 187, 166 },
+ { 189, 219, 198 },
+ { 221, 251, 234 },
+ { 253, 283, 266 },
+ { 285, 315, 294 }
+};
+
+const char COMMANDS[13] = "LMTPOCIUGJFS";
+const char INVENTORY_COMMANDS[9] = { "ELUG-+,." };
+const char *const PRESS_KEY_FOR_MORE = "Press any Key for More.";
+const char *const PRESS_KEY_TO_CONTINUE = "Press any Key to Continue.";
+
+const char *const MOPEN[] = {
+ "This cannot be opened", "It is already open", "It is locked", "Wait for Watson", " ", "."
+};
+const char *const MCLOSE[] = {
+ "This cannot be closed", "It is already closed", "The safe door is in the way"
+};
+const char *const MMOVE[] = {
+ "This cannot be moved", "It is bolted to the floor", "It is too heavy", "The other crate is in the way"
+};
+const char *const MPICK[] = {
+ "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"
+};
+const char *const MUSE[] = {
+ "You can't do that", "It had no effect", "You can't reach it", "OK, the door looks bigger! Happy?",
+ "Doors don't smoke"
+};
+
+/*----------------------------------------------------------------*/
+
+
+ScalpelUserInterface::ScalpelUserInterface(SherlockEngine *vm): UserInterface(vm) {
+ _controls = new ImageFile("menu.all");
+ _controlPanel = new ImageFile("controls.vgs");
+ _keyPress = '\0';
+ _lookHelp = 0;
+ _bgFound = 0;
+ _oldBgFound = -1;
+ _help = _oldHelp = 0;
+ _key = _oldKey = '\0';
+ _temp = _oldTemp = 0;
+ _oldLook = 0;
+ _keyboardInput = false;
+ _pause = false;
+ _cNum = 0;
+ _find = 0;
+ _oldUse = 0;
+}
+
+ScalpelUserInterface::~ScalpelUserInterface() {
+ delete _controls;
+ delete _controlPanel;
+}
+
+void ScalpelUserInterface::reset() {
+ _oldKey = -1;
+ _help = _oldHelp = -1;
+ _oldTemp = _temp = -1;
+}
+
+void ScalpelUserInterface::drawInterface(int bufferNum) {
+ Screen &screen = *_vm->_screen;
+
+ if (bufferNum & 1)
+ screen._backBuffer1.transBlitFrom((*_controlPanel)[0], Common::Point(0, CONTROLS_Y));
+ if (bufferNum & 2)
+ screen._backBuffer2.transBlitFrom((*_controlPanel)[0], Common::Point(0, CONTROLS_Y));
+ if (bufferNum == 3)
+ screen._backBuffer2.fillRect(0, INFO_LINE, SHERLOCK_SCREEN_WIDTH, INFO_LINE + 10, INFO_BLACK);
+}
+
+void ScalpelUserInterface::handleInput() {
+ Events &events = *_vm->_events;
+ Inventory &inv = *_vm->_inventory;
+ People &people = *_vm->_people;
+ Scene &scene = *_vm->_scene;
+ Screen &screen = *_vm->_screen;
+ Talk &talk = *_vm->_talk;
+
+ if (_menuCounter)
+ whileMenuCounter();
+
+ Common::Point pt = events.mousePos();
+ _bgFound = scene.findBgShape(Common::Rect(pt.x, pt.y, pt.x + 1, pt.y + 1));
+ _keyPress = '\0';
+
+ // Check kbd and set the mouse released flag if Enter or space is pressed.
+ // Otherwise, the pressed _key is stored for later use
+ if (events.kbHit()) {
+ Common::KeyState keyState = events.getKey();
+ _keyPress = keyState.ascii;
+
+ if (keyState.keycode == Common::KEYCODE_x && keyState.flags & Common::KBD_ALT) {
+ _vm->quitGame();
+ events.pollEvents();
+ return;
+ }
+ }
+
+ // Do button highlighting check
+ if (!talk._scriptMoreFlag) { // Don't if scripts are running
+ if (((events._rightPressed || events._rightReleased) && _helpStyle) ||
+ (!_helpStyle && !_menuCounter)) {
+ // Handle any default commands if we're in STD_MODE
+ if (_menuMode == STD_MODE) {
+ if (pt.y < CONTROLS_Y &&
+ (events._rightPressed || (!_helpStyle && !events._released)) &&
+ (_bgFound != -1) && (_bgFound < 1000) &&
+ (scene._bgShapes[_bgFound]._defaultCommand ||
+ !scene._bgShapes[_bgFound]._description.empty())) {
+ // If there is no default command, so set it to Look
+ if (scene._bgShapes[_bgFound]._defaultCommand)
+ _help = scene._bgShapes[_bgFound]._defaultCommand - 1;
+ else
+ _help = 0;
+
+ // Reset 'help' if it is an invalid command
+ if (_help > 5)
+ _help = -1;
+ } else if (pt.y < CONTROLS_Y &&
+ ((events._rightReleased && _helpStyle) || (events._released && !_helpStyle)) &&
+ (_bgFound != -1 && _bgFound < 1000) &&
+ (scene._bgShapes[_bgFound]._defaultCommand ||
+ !scene._bgShapes[_bgFound]._description.empty())) {
+ // If there is no default command, set it to Look
+ if (scene._bgShapes[_bgFound]._defaultCommand)
+ _menuMode = (MenuMode)scene._bgShapes[_bgFound]._defaultCommand;
+ else
+ _menuMode = LOOK_MODE;
+ events._released = true;
+ events._pressed = events._oldButtons = false;
+ _help = _oldHelp = -1;
+
+ if (_menuMode == LOOK_MODE) {
+ // Set the flag to tell the game that this was a right-click
+ // call to look and should exit without the look button being pressed
+ _lookHelp = true;
+ }
+ } else {
+ _help = -1;
+ }
+
+ // Check if highlighting a different button than last time
+ if (_help != _oldHelp) {
+ // If another button was highlighted previously, restore it
+ if (_oldHelp != -1)
+ restoreButton(_oldHelp);
+
+ // If we're highlighting a new button, then draw it pressed
+ if (_help != -1)
+ depressButton(_help);
+
+ _oldHelp = _help;
+ }
+
+ if (_bgFound != _oldBgFound || _oldBgFound == -1) {
+ _infoFlag = true;
+ clearInfo();
+
+ if (_help != -1 && !scene._bgShapes[_bgFound]._description.empty()
+ && scene._bgShapes[_bgFound]._description[0] != ' ')
+ screen.print(Common::Point(0, INFO_LINE + 1),
+ INFO_FOREGROUND, "%s", scene._bgShapes[_bgFound]._description.c_str());
+
+ _oldBgFound = _bgFound;
+ }
+ } else {
+ // We're not in STD_MODE
+ // If there isn't a window open, then revert back to STD_MODE
+ if (!_windowOpen && events._rightReleased) {
+ // Restore all buttons
+ for (int idx = 0; idx < 12; ++idx)
+ restoreButton(idx);
+
+ _menuMode = STD_MODE;
+ _key = _oldKey = -1;
+ _temp = _oldTemp = _lookHelp = _invLookFlag = 0;
+ events.clearEvents();
+ }
+ }
+ }
+ }
+
+ // Reset the old bgshape number if the mouse button is released, so that
+ // it can e re-highlighted when we come back here
+ if ((events._rightReleased && _helpStyle) || (events._released && !_helpStyle))
+ _oldBgFound = -1;
+
+ // Do routines that should be done before input processing
+ switch (_menuMode) {
+ case LOOK_MODE:
+ if (!_windowOpen) {
+ if (events._released && _bgFound >= 0 && _bgFound < 1000) {
+ if (!scene._bgShapes[_bgFound]._examine.empty())
+ examine();
+ } else {
+ lookScreen(pt);
+ }
+ }
+ break;
+
+ case MOVE_MODE:
+ case OPEN_MODE:
+ case CLOSE_MODE:
+ case PICKUP_MODE:
+ lookScreen(pt);
+ break;
+
+ case TALK_MODE:
+ if (!_windowOpen) {
+ bool personFound;
+
+ if (_bgFound >= 1000) {
+ personFound = false;
+ if (!events._released)
+ lookScreen(pt);
+ } else {
+ personFound = _bgFound != -1 && scene._bgShapes[_bgFound]._aType == PERSON;
+ }
+
+ if (events._released && personFound)
+ talk.talk(_bgFound);
+ else if (personFound)
+ lookScreen(pt);
+ else if (_bgFound < 1000)
+ clearInfo();
+ }
+ break;
+
+ case USE_MODE:
+ case GIVE_MODE:
+ case INV_MODE:
+ if (inv._invMode == INVMODE_LOOK || inv._invMode == INVMODE_USE || inv._invMode == INVMODE_GIVE) {
+ if (pt.y > CONTROLS_Y)
+ lookInv();
+ else
+ lookScreen(pt);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ //
+ // Do input processing
+ //
+ if (events._pressed || events._released || events._rightPressed || _keyPress || _pause) {
+ if (((events._released && (_helpStyle || _help == -1)) || (events._rightReleased && !_helpStyle)) &&
+ (pt.y <= CONTROLS_Y) && (_menuMode == STD_MODE)) {
+ // The mouse was clicked in the playing area with no action buttons down.
+ // Check if the mouse was clicked in a script zone. If it was,
+ // then execute the script. Otherwise, walk to the given position
+ if (scene.checkForZones(pt, SCRIPT_ZONE) != 0 ||
+ scene.checkForZones(pt, NOWALK_ZONE) != 0) {
+ // Mouse clicked in script zone
+ events._pressed = events._released = false;
+ } else {
+ people._walkDest = pt;
+ people._allowWalkAbort = false;
+ people.goAllTheWay();
+ }
+
+ if (_oldKey != -1) {
+ restoreButton(_oldTemp);
+ _oldKey = -1;
+ }
+ }
+
+ // Handle action depending on selected mode
+ switch (_menuMode) {
+ case LOOK_MODE:
+ if (_windowOpen)
+ doLookControl();
+ break;
+
+ case MOVE_MODE:
+ doMiscControl(ALLOW_MOVE);
+ break;
+
+ case TALK_MODE:
+ if (_windowOpen)
+ doTalkControl();
+ break;
+
+ case OPEN_MODE:
+ doMiscControl(ALLOW_OPEN);
+ break;
+
+ case CLOSE_MODE:
+ doMiscControl(ALLOW_CLOSE);
+ break;
+
+ case PICKUP_MODE:
+ doPickControl();
+ break;
+
+ case USE_MODE:
+ case GIVE_MODE:
+ case INV_MODE:
+ doInvControl();
+ break;
+
+ case FILES_MODE:
+ doEnvControl();
+ break;
+
+ default:
+ break;
+ }
+
+ // As long as there isn't an open window, do main input processing.
+ // Windows are opened when in TALK, USE, INV, and GIVE modes
+ if ((!_windowOpen && !_menuCounter && pt.y > CONTROLS_Y) ||
+ _keyPress) {
+ if (events._pressed || events._released || _pause || _keyPress)
+ doMainControl();
+ }
+
+ if (pt.y < CONTROLS_Y && events._pressed && _oldTemp != (int)(_menuMode - 1) && _oldKey != -1)
+ restoreButton(_oldTemp);
+ }
+}
+
+void ScalpelUserInterface::depressButton(int num) {
+ Screen &screen = *_vm->_screen;
+ Common::Point pt(MENU_POINTS[num][0], MENU_POINTS[num][1]);
+
+ ImageFrame &frame = (*_controls)[num];
+ screen._backBuffer1.transBlitFrom(frame, pt);
+ screen.slamArea(pt.x, pt.y, pt.x + frame._width, pt.y + frame._height);
+}
+
+void ScalpelUserInterface::restoreButton(int num) {
+ Screen &screen = *_vm->_screen;
+ Common::Point pt(MENU_POINTS[num][0], MENU_POINTS[num][1]);
+ Graphics::Surface &frame = (*_controls)[num]._frame;
+
+ screen._backBuffer1.blitFrom(screen._backBuffer2, pt,
+ Common::Rect(pt.x, pt.y, pt.x + 90, pt.y + 19));
+ screen.slamArea(pt.x, pt.y, pt.x + frame.w, pt.y + frame.h);
+
+ if (!_menuCounter) {
+ _infoFlag = true;
+ clearInfo();
+ }
+}
+
+void ScalpelUserInterface::pushButton(int num) {
+ Events &events = *_vm->_events;
+ _oldKey = -1;
+
+ if (!events._released) {
+ if (_oldHelp != -1)
+ restoreButton(_oldHelp);
+ if (_help != -1)
+ restoreButton(_help);
+
+ depressButton(num);
+ events.wait(6);
+ }
+
+ restoreButton(num);
+}
+
+void ScalpelUserInterface::toggleButton(int num) {
+ Screen &screen = *_vm->_screen;
+
+ if (_menuMode != (MenuMode)(num + 1)) {
+ _menuMode = (MenuMode)(num + 1);
+ _oldKey = COMMANDS[num];
+ _oldTemp = num;
+
+ if (_keyboardInput) {
+ if (_oldHelp != -1 && _oldHelp != num)
+ restoreButton(_oldHelp);
+ if (_help != -1 && _help != num)
+ restoreButton(_help);
+
+ _keyboardInput = false;
+
+ ImageFrame &frame = (*_controls)[num];
+ Common::Point pt(MENU_POINTS[num][0], MENU_POINTS[num][1]);
+ screen._backBuffer1.transBlitFrom(frame, pt);
+ screen.slamArea(pt.x, pt.y, pt.x + frame._width, pt.y + frame._height);
+ }
+ } else {
+ _menuMode = STD_MODE;
+ _oldKey = -1;
+ restoreButton(num);
+ }
+}
+
+void ScalpelUserInterface::clearInfo() {
+ if (_infoFlag) {
+ _vm->_screen->vgaBar(Common::Rect(16, INFO_LINE, SHERLOCK_SCREEN_WIDTH - 19,
+ INFO_LINE + 10), INFO_BLACK);
+ _infoFlag = false;
+ _oldLook = -1;
+ }
+}
+
+void ScalpelUserInterface::clearWindow() {
+ if (_windowOpen) {
+ _vm->_screen->vgaBar(Common::Rect(3, CONTROLS_Y + 11, SHERLOCK_SCREEN_WIDTH - 2,
+ SHERLOCK_SCREEN_HEIGHT - 2), INV_BACKGROUND);
+ }
+}
+
+void ScalpelUserInterface::whileMenuCounter() {
+ if (!(--_menuCounter) || _vm->_events->checkInput()) {
+ _menuCounter = 0;
+ _infoFlag = true;
+ clearInfo();
+ }
+}
+
+void ScalpelUserInterface::examine() {
+ Events &events = *_vm->_events;
+ Inventory &inv = *_vm->_inventory;
+ People &people = *_vm->_people;
+ Scene &scene = *_vm->_scene;
+ Talk &talk = *_vm->_talk;
+ Common::Point pt = events.mousePos();
+
+ if (pt.y < (CONTROLS_Y + 9)) {
+ Object &obj = scene._bgShapes[_bgFound];
+
+ if (obj._lookcAnim != 0) {
+ int canimSpeed = ((obj._lookcAnim & 0xe0) >> 5) + 1;
+ scene._cAnimFramePause = obj._lookFrames;
+ _cAnimStr = obj._examine;
+ _cNum = (obj._lookcAnim & 0x1f) - 1;
+
+ scene.startCAnim(_cNum, canimSpeed);
+ } else if (obj._lookPosition.y != 0) {
+ // Need to walk to the object to be examined
+ people.walkToCoords(Common::Point(obj._lookPosition.x, obj._lookPosition.y * 100), obj._lookFacing);
+ }
+
+ if (!talk._talkToAbort) {
+ _cAnimStr = obj._examine;
+ if (obj._lookFlag)
+ _vm->setFlags(obj._lookFlag);
+ }
+ } else {
+ // Looking at an inventory item
+ _cAnimStr = inv[_selector]._examine;
+ if (inv[_selector]._lookFlag)
+ _vm->setFlags(inv[_selector]._lookFlag);
+ }
+
+ if (_invLookFlag) {
+ // Don't close the inventory window when starting an examine display, since its
+ // window will slide up to replace the inventory display
+ _windowOpen = false;
+ _menuMode = LOOK_MODE;
+ }
+
+ if (!talk._talkToAbort) {
+ if (!scene._cAnimFramePause)
+ printObjectDesc(_cAnimStr, true);
+ else
+ // description was already printed in startCAnimation
+ scene._cAnimFramePause = 0;
+ }
+}
+
+void ScalpelUserInterface::lookScreen(const Common::Point &pt) {
+ Events &events = *_vm->_events;
+ Inventory &inv = *_vm->_inventory;
+ Scene &scene = *_vm->_scene;
+ Screen &screen = *_vm->_screen;
+ Common::Point mousePos = events.mousePos();
+ int temp;
+ Common::String tempStr;
+
+ // Don't display anything for right button command
+ if ((events._rightPressed || events._rightPressed) && !events._pressed)
+ return;
+
+ if (mousePos.y < CONTROLS_Y && (temp = _bgFound) != -1) {
+ if (temp != _oldLook) {
+ _infoFlag = true;
+ clearInfo();
+
+ if (temp < 1000)
+ tempStr = scene._bgShapes[temp]._description;
+ else
+ tempStr = scene._bgShapes[temp - 1000]._description;
+
+ _infoFlag = true;
+ clearInfo();
+
+ // Only print description if there is one
+ if (!tempStr.empty() && tempStr[0] != ' ') {
+ // If inventory is active and an item is selected for a Use or Give action
+ if ((_menuMode == INV_MODE || _menuMode == USE_MODE || _menuMode == GIVE_MODE) &&
+ (inv._invMode == INVMODE_USE || inv._invMode == INVMODE_GIVE)) {
+ int width1 = 0, width2 = 0;
+ int x, width;
+ if (inv._invMode == INVMODE_USE) {
+ // Using an object
+ x = width = screen.stringWidth("Use ");
+
+ if (temp < 1000 && scene._bgShapes[temp]._aType != PERSON)
+ // It's not a person, so make it lowercase
+ tempStr.setChar(tolower(tempStr[0]), 0);
+
+ x += screen.stringWidth(tempStr);
+
+ // If we're using an inventory object, add in the width
+ // of the object name and the " on "
+ if (_selector != -1) {
+ width1 = screen.stringWidth(inv[_selector]._name);
+ x += width1;
+ width2 = screen.stringWidth(" on ");
+ x += width2;
+ }
+
+ // If the line will be too long, keep cutting off characters
+ // until the string will fit
+ while (x > 280) {
+ x -= screen.charWidth(tempStr.lastChar());
+ tempStr.deleteLastChar();
+ }
+
+ int xStart = (SHERLOCK_SCREEN_WIDTH - x) / 2;
+ screen.print(Common::Point(xStart, INFO_LINE + 1),
+ INFO_FOREGROUND, "Use ");
+
+ if (_selector != -1) {
+ screen.print(Common::Point(xStart + width, INFO_LINE + 1),
+ TALK_FOREGROUND, "%s", inv[_selector]._name.c_str());
+ screen.print(Common::Point(xStart + width + width1, INFO_LINE + 1),
+ INFO_FOREGROUND, " on ");
+ screen.print(Common::Point(xStart + width + width1 + width2, INFO_LINE + 1),
+ INFO_FOREGROUND, "%s", tempStr.c_str());
+ } else {
+ screen.print(Common::Point(xStart + width, INFO_LINE + 1),
+ INFO_FOREGROUND, "%s", tempStr.c_str());
+ }
+ } else if (temp >= 0 && temp < 1000 && _selector != -1 &&
+ scene._bgShapes[temp]._aType == PERSON) {
+ // Giving an object to a person
+ width1 = screen.stringWidth(inv[_selector]._name);
+ x = width = screen.stringWidth("Give ");
+ x += width1;
+ width2 = screen.stringWidth(" to ");
+ x += width2;
+ x += screen.stringWidth(tempStr);
+
+ // Ensure string will fit on-screen
+ while (x > 280) {
+ x -= screen.charWidth(tempStr.lastChar());
+ tempStr.deleteLastChar();
+ }
+
+ int xStart = (SHERLOCK_SCREEN_WIDTH - x) / 2;
+ screen.print(Common::Point(xStart, INFO_LINE + 1),
+ INFO_FOREGROUND, "Give ");
+ screen.print(Common::Point(xStart + width, INFO_LINE + 1),
+ TALK_FOREGROUND, "%s", inv[_selector]._name.c_str());
+ screen.print(Common::Point(xStart + width + width1, INFO_LINE + 1),
+ INFO_FOREGROUND, " to ");
+ screen.print(Common::Point(xStart + width + width1 + width2, INFO_LINE + 1),
+ INFO_FOREGROUND, "%s", tempStr.c_str());
+ }
+ } else {
+ screen.print(Common::Point(0, INFO_LINE + 1), INFO_FOREGROUND, "%s", tempStr.c_str());
+ }
+
+ _infoFlag = true;
+ _oldLook = temp;
+ }
+ }
+ } else {
+ clearInfo();
+ }
+}
+
+void ScalpelUserInterface::lookInv() {
+ Events &events = *_vm->_events;
+ Inventory &inv = *_vm->_inventory;
+ Screen &screen = *_vm->_screen;
+ Common::Point mousePos = events.mousePos();
+
+ if (mousePos.x > 15 && mousePos.x < 314 && mousePos.y > (CONTROLS_Y1 + 11)
+ && mousePos.y < (SHERLOCK_SCREEN_HEIGHT - 2)) {
+ int temp = (mousePos.x - 6) / 52 + inv._invIndex;
+ if (temp < inv._holdings) {
+ if (temp < inv._holdings) {
+ clearInfo();
+ screen.print(Common::Point(0, INFO_LINE + 1), INFO_FOREGROUND,
+ "%s", inv[temp]._description.c_str());
+ _infoFlag = true;
+ _oldLook = temp;
+ }
+ } else {
+ clearInfo();
+ }
+ } else {
+ clearInfo();
+ }
+}
+
+void ScalpelUserInterface::doEnvControl() {
+ Events &events = *_vm->_events;
+ SaveManager &saves = *_vm->_saves;
+ Scene &scene = *_vm->_scene;
+ Screen &screen = *_vm->_screen;
+ Talk &talk = *_vm->_talk;
+ Common::Point mousePos = events.mousePos();
+ static const char ENV_COMMANDS[7] = "ELSUDQ";
+
+ byte color;
+
+ _key = _oldKey = -1;
+ _keyboardInput = false;
+ int found = saves.getHighlightedButton();
+
+ if (events._pressed || events._released) {
+ events.clearKeyboard();
+
+ // Check for a filename entry being highlighted
+ if ((events._pressed || events._released) && mousePos.y > (CONTROLS_Y + 10)) {
+ int found1 = 0;
+ for (_selector = 0; (_selector < ONSCREEN_FILES_COUNT) && !found1; ++_selector)
+ if (mousePos.y > (CONTROLS_Y + 11 + _selector * 10) && mousePos.y < (CONTROLS_Y + 21 + _selector * 10))
+ found1 = 1;
+
+ if (_selector + saves._savegameIndex - 1 < MAX_SAVEGAME_SLOTS + (saves._envMode != SAVEMODE_LOAD))
+ _selector = _selector + saves._savegameIndex - 1;
+ else
+ _selector = -1;
+
+ if (!found1)
+ _selector = -1;
+ }
+
+ // Handle selecting buttons, if any
+ saves.highlightButtons(found);
+
+ if (found == 0 || found == 5)
+ saves._envMode = SAVEMODE_NONE;
+ }
+
+ if (_keyPress) {
+ _key = toupper(_keyPress);
+
+ // Escape _key will close the dialog
+ if (_key == Common::KEYCODE_ESCAPE)
+ _key = 'E';
+
+ if (_key == 'E' || _key == 'L' || _key == 'S' || _key == 'U' || _key == 'D' || _key == 'Q') {
+ const char *chP = strchr(ENV_COMMANDS, _key);
+ int btnIndex = !chP ? -1 : chP - ENV_COMMANDS;
+ saves.highlightButtons(btnIndex);
+ _keyboardInput = true;
+
+ if (_key == 'E' || _key == 'Q') {
+ saves._envMode = SAVEMODE_NONE;
+ } else if (_key >= '1' && _key <= '9') {
+ _keyboardInput = true;
+ _selector = _key - '1';
+ if (_selector >= MAX_SAVEGAME_SLOTS + (saves._envMode == SAVEMODE_LOAD ? 0 : 1))
+ _selector = -1;
+
+ if (saves.checkGameOnScreen(_selector))
+ _oldSelector = _selector;
+ } else {
+ _selector = -1;
+ }
+ }
+ }
+
+ if (_selector != _oldSelector) {
+ if (_oldSelector != -1 && _oldSelector >= saves._savegameIndex && _oldSelector < (saves._savegameIndex + ONSCREEN_FILES_COUNT)) {
+ screen.print(Common::Point(6, CONTROLS_Y + 12 + (_oldSelector - saves._savegameIndex) * 10),
+ INV_FOREGROUND, "%d.", _oldSelector + 1);
+ screen.print(Common::Point(24, CONTROLS_Y + 12 + (_oldSelector - saves._savegameIndex) * 10),
+ INV_FOREGROUND, "%s", saves._savegames[_oldSelector].c_str());
+ }
+
+ if (_selector != -1) {
+ screen.print(Common::Point(6, CONTROLS_Y + 12 + (_selector - saves._savegameIndex) * 10),
+ TALK_FOREGROUND, "%d.", _selector + 1);
+ screen.print(Common::Point(24, CONTROLS_Y + 12 + (_selector - saves._savegameIndex) * 10),
+ TALK_FOREGROUND, "%s", saves._savegames[_selector].c_str());
+ }
+
+ _oldSelector = _selector;
+ }
+
+ if (events._released || _keyboardInput) {
+ if ((found == 0 && events._released) || _key == 'E') {
+ banishWindow();
+ _windowBounds.top = CONTROLS_Y1;
+
+ events._pressed = events._released = _keyboardInput = false;
+ _keyPress = '\0';
+ } else if ((found == 1 && events._released) || _key == 'L') {
+ saves._envMode = SAVEMODE_LOAD;
+ if (_selector != -1) {
+ saves.loadGame(_selector + 1);
+ }
+ } else if ((found == 2 && events._released) || _key == 'S') {
+ saves._envMode = SAVEMODE_SAVE;
+ if (_selector != -1) {
+ if (saves.checkGameOnScreen(_selector))
+ _oldSelector = _selector;
+
+ if (saves.promptForDescription(_selector)) {
+ saves.saveGame(_selector + 1, saves._savegames[_selector]);
+
+ banishWindow(1);
+ _windowBounds.top = CONTROLS_Y1;
+ _key = _oldKey = -1;
+ _keyPress = '\0';
+ _keyboardInput = false;
+ } else {
+ if (!talk._talkToAbort) {
+ screen._backBuffer1.fillRect(Common::Rect(6, CONTROLS_Y + 11 + (_selector - saves._savegameIndex) * 10,
+ SHERLOCK_SCREEN_WIDTH - 2, CONTROLS_Y + 20 + (_selector - saves._savegameIndex) * 10), INV_BACKGROUND);
+ screen.gPrint(Common::Point(6, CONTROLS_Y + 11 + (_selector - saves._savegameIndex) * 10), INV_FOREGROUND,
+ "%d.", _selector + 1);
+ screen.gPrint(Common::Point(24, CONTROLS_Y + 11 + (_selector - saves._savegameIndex) * 10), INV_FOREGROUND,
+ "%s", saves._savegames[_selector].c_str());
+
+ screen.slamArea(6, CONTROLS_Y + 11 + (_selector - saves._savegameIndex) * 10, 311, 10);
+ _selector = _oldSelector = -1;
+ }
+ }
+ }
+ } else if (((found == 3 && events._released) || _key == 'U') && saves._savegameIndex) {
+ bool moreKeys;
+ do {
+ saves._savegameIndex--;
+ screen._backBuffer1.fillRect(Common::Rect(3, CONTROLS_Y + 11, SHERLOCK_SCREEN_WIDTH - 2,
+ SHERLOCK_SCREEN_HEIGHT - 1), INV_BACKGROUND);
+
+ for (int idx = saves._savegameIndex; idx < (saves._savegameIndex + ONSCREEN_FILES_COUNT); ++idx) {
+ color = INV_FOREGROUND;
+ if (idx == _selector && idx >= saves._savegameIndex && idx < (saves._savegameIndex + ONSCREEN_FILES_COUNT))
+ color = TALK_FOREGROUND;
+
+ screen.gPrint(Common::Point(6, CONTROLS_Y + 11 + (idx - saves._savegameIndex) * 10), color, "%d.", idx + 1);
+ screen.gPrint(Common::Point(24, CONTROLS_Y + 11 + (idx - saves._savegameIndex) * 10), color, "%s", saves._savegames[idx].c_str());
+ }
+
+ screen.slamRect(Common::Rect(3, CONTROLS_Y + 11, SHERLOCK_SCREEN_WIDTH - 2, SHERLOCK_SCREEN_HEIGHT));
+
+ color = !saves._savegameIndex ? COMMAND_NULL : COMMAND_FOREGROUND;
+ screen.buttonPrint(Common::Point(ENV_POINTS[3][2], CONTROLS_Y), color, true, "Up");
+ color = (saves._savegameIndex == MAX_SAVEGAME_SLOTS - ONSCREEN_FILES_COUNT) ? COMMAND_NULL : COMMAND_FOREGROUND;
+ screen.buttonPrint(Common::Point(ENV_POINTS[4][2], CONTROLS_Y), color, true, "Down");
+
+ // Check whether there are more pending U keys pressed
+ moreKeys = false;
+ if (events.kbHit()) {
+ Common::KeyState keyState = events.getKey();
+
+ _key = toupper(keyState.keycode);
+ moreKeys = _key == 'U';
+ }
+ } while ((saves._savegameIndex) && moreKeys);
+ } else if (((found == 4 && events._released) || _key == 'D') && saves._savegameIndex < (MAX_SAVEGAME_SLOTS - ONSCREEN_FILES_COUNT)) {
+ bool moreKeys;
+ do {
+ saves._savegameIndex++;
+ screen._backBuffer1.fillRect(Common::Rect(3, CONTROLS_Y + 11, SHERLOCK_SCREEN_WIDTH - 2,
+ SHERLOCK_SCREEN_HEIGHT - 1), INV_BACKGROUND);
+
+ for (int idx = saves._savegameIndex; idx < (saves._savegameIndex + ONSCREEN_FILES_COUNT); ++idx) {
+ if (idx == _selector && idx >= saves._savegameIndex && idx < (saves._savegameIndex + ONSCREEN_FILES_COUNT))
+ color = TALK_FOREGROUND;
+ else
+ color = INV_FOREGROUND;
+
+ screen.gPrint(Common::Point(6, CONTROLS_Y + 11 + (idx - saves._savegameIndex) * 10), color,
+ "%d.", idx + 1);
+ screen.gPrint(Common::Point(24, CONTROLS_Y + 11 + (idx - saves._savegameIndex) * 10), color,
+ "%s", saves._savegames[idx].c_str());
+ }
+
+ screen.slamRect(Common::Rect(3, CONTROLS_Y + 11, SHERLOCK_SCREEN_WIDTH - 2, SHERLOCK_SCREEN_HEIGHT));
+
+ color = (!saves._savegameIndex) ? COMMAND_NULL : COMMAND_FOREGROUND;
+ screen.buttonPrint(Common::Point(ENV_POINTS[3][2], CONTROLS_Y), color, true, "Up");
+
+ color = (saves._savegameIndex == MAX_SAVEGAME_SLOTS - ONSCREEN_FILES_COUNT) ? COMMAND_NULL : COMMAND_FOREGROUND;
+ screen.buttonPrint(Common::Point(ENV_POINTS[4][2], CONTROLS_Y), color, true, "Down");
+
+ // Check whether there are more pending D keys pressed
+ moreKeys = false;
+ if (events.kbHit()) {
+ Common::KeyState keyState;
+ _key = toupper(keyState.keycode);
+
+ moreKeys = _key == 'D';
+ }
+ } while (saves._savegameIndex < (MAX_SAVEGAME_SLOTS - ONSCREEN_FILES_COUNT) && moreKeys);
+ } else if ((found == 5 && events._released) || _key == 'Q') {
+ clearWindow();
+ screen.print(Common::Point(0, CONTROLS_Y + 20), INV_FOREGROUND, "Are you sure you wish to Quit ?");
+ screen.vgaBar(Common::Rect(0, CONTROLS_Y, SHERLOCK_SCREEN_WIDTH, CONTROLS_Y + 10), BORDER_COLOR);
+
+ screen.makeButton(Common::Rect(112, CONTROLS_Y, 160, CONTROLS_Y + 10), 136 - screen.stringWidth("Yes") / 2, "Yes");
+ screen.makeButton(Common::Rect(161, CONTROLS_Y, 209, CONTROLS_Y + 10), 184 - screen.stringWidth("No") / 2, "No");
+ screen.slamArea(112, CONTROLS_Y, 97, 10);
+
+ do {
+ scene.doBgAnim();
+
+ if (talk._talkToAbort)
+ return;
+
+ events.pollEventsAndWait();
+ events.setButtonState();
+ mousePos = events.mousePos();
+
+ if (events.kbHit()) {
+ Common::KeyState keyState = events.getKey();
+ _key = toupper(keyState.keycode);
+
+ if (_key == 'X' && (keyState.flags & Common::KBD_ALT) != 0) {
+ _vm->quitGame();
+ events.pollEvents();
+ return;
+ }
+
+ if (_key == Common::KEYCODE_ESCAPE)
+ _key = 'N';
+
+ if (_key == Common::KEYCODE_RETURN || _key == ' ') {
+ events._pressed = false;
+ events._released = true;
+ events._oldButtons = 0;
+ _keyPress = '\0';
+ }
+ }
+
+ if (events._pressed || events._released) {
+ if (mousePos.x > 112 && mousePos.x < 159 && mousePos.y > CONTROLS_Y && mousePos.y < (CONTROLS_Y + 9))
+ color = COMMAND_HIGHLIGHTED;
+ else
+ color = COMMAND_FOREGROUND;
+ screen.buttonPrint(Common::Point(136, CONTROLS_Y), color, true, "Yes");
+
+ if (mousePos.x > 161 && mousePos.x < 208 && mousePos.y > CONTROLS_Y && mousePos.y < (CONTROLS_Y + 9))
+ color = COMMAND_HIGHLIGHTED;
+ else
+ color = COMMAND_FOREGROUND;
+ screen.buttonPrint(Common::Point(184, CONTROLS_Y), color, true, "No");
+ }
+
+ if (mousePos.x > 112 && mousePos.x < 159 && mousePos.y > CONTROLS_Y && mousePos.y < (CONTROLS_Y + 9) && events._released)
+ _key = 'Y';
+
+ if (mousePos.x > 161 && mousePos.x < 208 && mousePos.y > CONTROLS_Y && mousePos.y < (CONTROLS_Y + 9) && events._released)
+ _key = 'N';
+ } while (!_vm->shouldQuit() && _key != 'Y' && _key != 'N');
+
+ if (_key == 'Y') {
+ _vm->quitGame();
+ events.pollEvents();
+ return;
+ } else {
+ screen.buttonPrint(Common::Point(184, CONTROLS_Y), COMMAND_HIGHLIGHTED, true, "No");
+ banishWindow(1);
+ _windowBounds.top = CONTROLS_Y1;
+ _key = -1;
+ }
+ } else {
+ if (_selector != -1) {
+ // Are we already in Load mode?
+ if (saves._envMode == SAVEMODE_LOAD) {
+ saves.loadGame(_selector + 1);
+ } else if (saves._envMode == SAVEMODE_SAVE || saves.isSlotEmpty(_selector)) {
+ // We're already in save mode, or pointing to an empty save slot
+ if (saves.checkGameOnScreen(_selector))
+ _oldSelector = _selector;
+
+ if (saves.promptForDescription(_selector)) {
+ saves.saveGame(_selector + 1, saves._savegames[_selector]);
+ banishWindow();
+ _windowBounds.top = CONTROLS_Y1;
+ _key = _oldKey = -1;
+ _keyPress = '\0';
+ _keyboardInput = false;
+ } else {
+ if (!talk._talkToAbort) {
+ screen._backBuffer1.fillRect(Common::Rect(6, CONTROLS_Y + 11 + (_selector - saves._savegameIndex) * 10,
+ 317, CONTROLS_Y + 20 + (_selector - saves._savegameIndex) * 10), INV_BACKGROUND);
+ screen.gPrint(Common::Point(6, CONTROLS_Y + 11 + (_selector - saves._savegameIndex) * 10),
+ INV_FOREGROUND, "%d.", _selector + 1);
+ screen.gPrint(Common::Point(24, CONTROLS_Y + 11 + (_selector - saves._savegameIndex) * 10),
+ INV_FOREGROUND, "%s", saves._savegames[_selector].c_str());
+ screen.slamArea(6, CONTROLS_Y + 11 + (_selector - saves._savegameIndex) * 10, 311, 10);
+ _selector = _oldSelector = -1;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+void ScalpelUserInterface::doInvControl() {
+ Events &events = *_vm->_events;
+ Inventory &inv = *_vm->_inventory;
+ Scene &scene = *_vm->_scene;
+ Screen &screen = *_vm->_screen;
+ Talk &talk = *_vm->_talk;
+ int colors[8];
+ Common::Point mousePos = events.mousePos();
+
+ _key = _oldKey = -1;
+ _keyboardInput = false;
+
+ // Check whether any inventory slot is highlighted
+ int found = -1;
+ Common::fill(&colors[0], &colors[8], (int)COMMAND_FOREGROUND);
+ for (int idx = 0; idx < 8; ++idx) {
+ Common::Rect r(INVENTORY_POINTS[idx][0], CONTROLS_Y1,
+ INVENTORY_POINTS[idx][1], CONTROLS_Y1 + 10);
+ if (r.contains(mousePos)) {
+ found = idx;
+ break;
+ }
+ }
+
+ if (events._pressed || events._released) {
+ events.clearKeyboard();
+
+ if (found != -1)
+ // If a slot highlighted, set its color
+ colors[found] = COMMAND_HIGHLIGHTED;
+ screen.buttonPrint(Common::Point(INVENTORY_POINTS[0][2], CONTROLS_Y1), colors[0], true, "Exit");
+
+ if (found >= 0 && found <= 3) {
+ screen.buttonPrint(Common::Point(INVENTORY_POINTS[1][2], CONTROLS_Y1), colors[1], true, "Look");
+ screen.buttonPrint(Common::Point(INVENTORY_POINTS[2][2], CONTROLS_Y1), colors[2], true, "Use");
+ screen.buttonPrint(Common::Point(INVENTORY_POINTS[3][2], CONTROLS_Y1), colors[3], true, "Give");
+ inv._invMode = (InvMode)found;
+ _selector = -1;
+ }
+
+ if (inv._invIndex) {
+ screen.print(Common::Point(INVENTORY_POINTS[4][2], CONTROLS_Y1 + 1), colors[4], "^^");
+ screen.print(Common::Point(INVENTORY_POINTS[5][2], CONTROLS_Y1 + 1), colors[5], "^");
+ }
+
+ if ((inv._holdings - inv._invIndex) > 6) {
+ screen.print(Common::Point(INVENTORY_POINTS[6][2], CONTROLS_Y1 + 1), colors[6], "_");
+ screen.print(Common::Point(INVENTORY_POINTS[7][2], CONTROLS_Y1 + 1), colors[7], "__");
+ }
+
+ bool flag = false;
+ if (inv._invMode == INVMODE_LOOK || inv._invMode == INVMODE_USE || inv._invMode == INVMODE_GIVE) {
+ Common::Rect r(15, CONTROLS_Y1 + 11, 314, SHERLOCK_SCREEN_HEIGHT - 2);
+ if (r.contains(mousePos)) {
+ _selector = (mousePos.x - 6) / 52 + inv._invIndex;
+ if (_selector < inv._holdings)
+ flag = true;
+ }
+ }
+
+ if (!flag && mousePos.y >(CONTROLS_Y1 + 11))
+ _selector = -1;
+ }
+
+ if (_keyPress) {
+ _key = toupper(_keyPress);
+
+ if (_key == Common::KEYCODE_ESCAPE)
+ // Escape will also 'E'xit out of inventory display
+ _key = 'E';
+
+ if (_key == 'E' || _key == 'L' || _key == 'U' || _key == 'G'
+ || _key == '-' || _key == '+') {
+ InvMode temp = inv._invMode;
+
+ const char *chP = strchr(INVENTORY_COMMANDS, _key);
+ inv._invMode = !chP ? INVMODE_INVALID : (InvMode)(chP - INVENTORY_COMMANDS);
+ inv.invCommands(true);
+
+ inv._invMode = temp;
+ _keyboardInput = true;
+ if (_key == 'E')
+ inv._invMode = INVMODE_EXIT;
+ _selector = -1;
+ } else {
+ _selector = -1;
+ }
+ }
+
+ if (_selector != _oldSelector) {
+ if (_oldSelector != -1) {
+ // Un-highlight
+ if (_oldSelector >= inv._invIndex && _oldSelector < (inv._invIndex + 6))
+ inv.highlight(_oldSelector, BUTTON_MIDDLE);
+ }
+
+ if (_selector != -1)
+ inv.highlight(_selector, 235);
+
+ _oldSelector = _selector;
+ }
+
+ if (events._released || _keyboardInput) {
+ if ((found == 0 && events._released) || _key == 'E') {
+ inv.freeInv();
+ _infoFlag = true;
+ clearInfo();
+ banishWindow(false);
+ _key = -1;
+ events.clearEvents();
+ events.setCursor(ARROW);
+ } else if ((found == 1 && events._released) || (_key == 'L')) {
+ inv._invMode = INVMODE_LOOK;
+ } else if ((found == 2 && events._released) || (_key == 'U')) {
+ inv._invMode = INVMODE_USE;
+ } else if ((found == 3 && events._released) || (_key == 'G')) {
+ inv._invMode = INVMODE_GIVE;
+ } else if (((found == 4 && events._released) || _key == ',') && inv._invIndex) {
+ if (inv._invIndex >= 6)
+ inv._invIndex -= 6;
+ else
+ inv._invIndex = 0;
+
+ screen.print(Common::Point(INVENTORY_POINTS[4][2], CONTROLS_Y1 + 1),
+ COMMAND_HIGHLIGHTED, "^^");
+ inv.freeGraphics();
+ inv.loadGraphics();
+ inv.putInv(SLAM_DISPLAY);
+ inv.invCommands(true);
+ } else if (((found == 5 && events._released) || _key == '-') && inv._invIndex > 0) {
+ --inv._invIndex;
+ screen.print(Common::Point(INVENTORY_POINTS[4][2], CONTROLS_Y1 + 1), COMMAND_HIGHLIGHTED, "^");
+ inv.freeGraphics();
+ inv.loadGraphics();
+ inv.putInv(SLAM_DISPLAY);
+ inv.invCommands(true);
+ } else if (((found == 6 && events._released) || _key == '+') && (inv._holdings - inv._invIndex) > 6) {
+ ++inv._invIndex;
+ screen.print(Common::Point(INVENTORY_POINTS[6][2], CONTROLS_Y1 + 1), COMMAND_HIGHLIGHTED, "_");
+ inv.freeGraphics();
+ inv.loadGraphics();
+ inv.putInv(SLAM_DISPLAY);
+ inv.invCommands(true);
+ } else if (((found == 7 && events._released) || _key == '.') && (inv._holdings - inv._invIndex) > 6) {
+ inv._invIndex += 6;
+ if ((inv._holdings - 6) < inv._invIndex)
+ inv._invIndex = inv._holdings - 6;
+
+ screen.print(Common::Point(INVENTORY_POINTS[7][2], CONTROLS_Y1 + 1), COMMAND_HIGHLIGHTED, "_");
+ inv.freeGraphics();
+ inv.loadGraphics();
+ inv.putInv(SLAM_DISPLAY);
+ inv.invCommands(true);
+ } else {
+ // If something is being given, make sure it's being given to a person
+ if (inv._invMode == INVMODE_GIVE) {
+ if (_bgFound != -1 && scene._bgShapes[_bgFound]._aType == PERSON)
+ _find = _bgFound;
+ else
+ _find = -1;
+ } else {
+ _find = _bgFound;
+ }
+
+ if ((mousePos.y < CONTROLS_Y1) && (inv._invMode == INVMODE_LOOK) && (_find >= 0) && (_find < 1000)) {
+ if (!scene._bgShapes[_find]._examine.empty() &&
+ scene._bgShapes[_find]._examine[0] >= ' ')
+ inv.refreshInv();
+ } else if (_selector != -1 || _find >= 0) {
+ // Selector is the inventory object that was clicked on, or selected.
+ // If it's -1, then no inventory item is highlighted yet. Otherwise,
+ // an object in the scene has been clicked.
+
+ if (_selector != -1 && inv._invMode == INVMODE_LOOK
+ && mousePos.y >(CONTROLS_Y1 + 11))
+ inv.refreshInv();
+
+ if (talk._talkToAbort)
+ return;
+
+ // Now check for the Use and Give actions. If inv_mode is INVMODE_GIVE,
+ // that means GIVE is in effect, _selector is the object being
+ // given, and _find is the target.
+ // The same applies to USE, except if _selector is -1, then USE
+ // is being tried on an object in the scene without an inventory
+ // object being highlighted first.
+
+ if ((inv._invMode == INVMODE_USE || (_selector != -1 && inv._invMode == INVMODE_GIVE)) && _find >= 0) {
+ events._pressed = events._released = false;
+ _infoFlag = true;
+ clearInfo();
+
+ int tempSel = _selector; // Save the selector
+ _selector = -1;
+
+ inv.putInv(SLAM_DISPLAY);
+ _selector = tempSel; // Restore it
+ InvMode tempMode = inv._invMode;
+ inv._invMode = INVMODE_USE55;
+ inv.invCommands(true);
+
+ _infoFlag = true;
+ clearInfo();
+ banishWindow(false);
+ _key = -1;
+
+ inv.freeInv();
+
+ bool giveFl = (tempMode >= INVMODE_GIVE);
+ if (_selector >= 0)
+ // Use/Give inv object with scene object
+ checkUseAction(&scene._bgShapes[_find]._use[0], inv[_selector]._name, MUSE, _find, giveFl);
+ else
+ // Now inv object has been highlighted
+ checkUseAction(&scene._bgShapes[_find]._use[0], "*SELF*", MUSE, _find, giveFl);
+
+ _selector = _oldSelector = -1;
+ }
+ }
+ }
+ }
+}
+
+void ScalpelUserInterface::doLookControl() {
+ Events &events = *_vm->_events;
+ Inventory &inv = *_vm->_inventory;
+ Screen &screen = *_vm->_screen;
+
+ _key = _oldKey = -1;
+ _keyboardInput = (_keyPress != '\0');
+
+ if (events._released || events._rightReleased || _keyboardInput) {
+ // Is an inventory object being looked at?
+ if (!_invLookFlag) {
+ // Is there any remaining text to display?
+ if (!_descStr.empty()) {
+ printObjectDesc(_descStr, false);
+ } else if (!_lookHelp) {
+ // Need to close the window and depress the Look button
+ Common::Point pt(MENU_POINTS[0][0], MENU_POINTS[0][1]);
+ screen._backBuffer2.blitFrom((*_controls)[0], pt);
+ banishWindow(true);
+
+ _windowBounds.top = CONTROLS_Y1;
+ _key = _oldKey = COMMANDS[LOOK_MODE - 1];
+ _temp = _oldTemp = 0;
+ _menuMode = LOOK_MODE;
+ events.clearEvents();
+
+ // Restore UI
+ drawInterface();
+ } else {
+ events.setCursor(ARROW);
+ banishWindow(true);
+ _windowBounds.top = CONTROLS_Y1;
+ _key = _oldKey = -1;
+ _temp = _oldTemp = 0;
+ _menuMode = STD_MODE;
+ events.clearEvents();
+ }
+ } else {
+ // Looking at an inventory object
+ // Backup the user interface
+ Surface tempSurface(SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT - CONTROLS_Y1);
+ tempSurface.blitFrom(screen._backBuffer2, Common::Point(0, 0),
+ Common::Rect(0, CONTROLS_Y1, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT));
+
+ inv.drawInventory(INVENTORY_DONT_DISPLAY);
+ banishWindow(true);
+
+ // Restore the ui
+ screen._backBuffer2.blitFrom(tempSurface, Common::Point(0, CONTROLS_Y1));
+
+ _windowBounds.top = CONTROLS_Y1;
+ _key = _oldKey = COMMANDS[LOOK_MODE - 1];
+ _temp = _oldTemp = 0;
+ events.clearEvents();
+ _invLookFlag = false;
+ _menuMode = INV_MODE;
+ _windowOpen = true;
+ }
+ }
+}
+
+void ScalpelUserInterface::doMainControl() {
+ Events &events = *_vm->_events;
+ Inventory &inv = *_vm->_inventory;
+ SaveManager &saves = *_vm->_saves;
+ Common::Point pt = events.mousePos();
+
+ if ((events._pressed || events._released) && pt.y > CONTROLS_Y) {
+ events.clearKeyboard();
+ _key = -1;
+
+ // Check whether the mouse is in any of the command areas
+ for (_temp = 0; (_temp < 12) && (_key == -1); ++_temp) {
+ Common::Rect r(MENU_POINTS[_temp][0], MENU_POINTS[_temp][1],
+ MENU_POINTS[_temp][2], MENU_POINTS[_temp][3]);
+ if (r.contains(pt))
+ _key = COMMANDS[_temp];
+ }
+ --_temp;
+ } else if (_keyPress) {
+ // Keyboard control
+ _keyboardInput = true;
+
+ if (_keyPress >= 'A' && _keyPress <= 'Z') {
+ const char *c = strchr(COMMANDS, _keyPress);
+ _temp = !c ? 12 : c - COMMANDS;
+ } else {
+ _temp = 12;
+ }
+
+ if (_temp == 12)
+ _key = -1;
+
+ if (events._rightPressed) {
+ _temp = 12;
+ _key = -1;
+ }
+ } else if (!events._released) {
+ _key = -1;
+ }
+
+ // Check if the button being pointed to has changed
+ if (_oldKey != _key && !_windowOpen) {
+ // Clear the info line
+ _infoFlag = true;
+ clearInfo();
+
+ // If there was an old button selected, restore it
+ if (_oldKey != -1) {
+ _menuMode = STD_MODE;
+ restoreButton(_oldTemp);
+ }
+
+ // If a new button is being pointed to, highlight it
+ if (_key != -1 && _temp < 12 && !_keyboardInput)
+ depressButton(_temp);
+
+ // Save the new button selection
+ _oldKey = _key;
+ _oldTemp = _temp;
+ }
+
+ if (!events._pressed && !_windowOpen) {
+ switch (_key) {
+ case 'L':
+ toggleButton(0);
+ break;
+ case 'M':
+ toggleButton(1);
+ break;
+ case 'T':
+ toggleButton(2);
+ break;
+ case 'P':
+ toggleButton(3);
+ break;
+ case 'O':
+ toggleButton(4);
+ break;
+ case 'C':
+ toggleButton(5);
+ break;
+ case 'I':
+ pushButton(6);
+ _selector = _oldSelector = -1;
+ _menuMode = INV_MODE;
+ inv.drawInventory(PLAIN_INVENTORY);
+ break;
+ case 'U':
+ pushButton(7);
+ _selector = _oldSelector = -1;
+ _menuMode = USE_MODE;
+ inv.drawInventory(USE_INVENTORY_MODE);
+ break;
+ case 'G':
+ pushButton(8);
+ _selector = _oldSelector = -1;
+ _menuMode = GIVE_MODE;
+ inv.drawInventory(GIVE_INVENTORY_MODE);
+ break;
+ case 'J':
+ pushButton(9);
+ _menuMode = JOURNAL_MODE;
+ journalControl();
+ break;
+ case 'F':
+ pushButton(10);
+
+ // Create a thumbnail of the current screen before the files dialog is shown, in case
+ // the user saves the game
+ saves.createThumbnail();
+
+ _selector = _oldSelector = -1;
+
+ if (_vm->_showOriginalSavesDialog) {
+ // Show the original dialog
+ _menuMode = FILES_MODE;
+ saves.drawInterface();
+ _windowOpen = true;
+ } else {
+ // Show the ScummVM GMM instead
+ _vm->_canLoadSave = true;
+ _vm->openMainMenuDialog();
+ _vm->_canLoadSave = false;
+ }
+ break;
+ case 'S':
+ pushButton(11);
+ _menuMode = SETUP_MODE;
+ Settings::show(_vm);
+ break;
+ default:
+ break;
+ }
+
+ _help = _oldHelp = _oldBgFound = -1;
+ }
+}
+
+void ScalpelUserInterface::doMiscControl(int allowed) {
+ Events &events = *_vm->_events;
+ Scene &scene = *_vm->_scene;
+ Talk &talk = *_vm->_talk;
+
+ if (events._released) {
+ _temp = _bgFound;
+ if (_bgFound != -1) {
+ // Only allow pointing to objects, not people
+ if (_bgFound < 1000) {
+ events.clearEvents();
+ Object &obj = scene._bgShapes[_bgFound];
+
+ switch (allowed) {
+ case ALLOW_OPEN:
+ checkAction(obj._aOpen, MOPEN, _temp);
+ if (_menuMode != TALK_MODE && !talk._talkToAbort) {
+ _menuMode = STD_MODE;
+ restoreButton(OPEN_MODE - 1);
+ _key = _oldKey = -1;
+ }
+ break;
+
+ case ALLOW_CLOSE:
+ checkAction(obj._aClose, MCLOSE, _temp);
+ if (_menuMode != TALK_MODE && !talk._talkToAbort) {
+ _menuMode = STD_MODE;
+ restoreButton(CLOSE_MODE - 1);
+ _key = _oldKey = -1;
+ }
+ break;
+
+ case ALLOW_MOVE:
+ checkAction(obj._aMove, MMOVE, _temp);
+ if (_menuMode != TALK_MODE && !talk._talkToAbort) {
+ _menuMode = STD_MODE;
+ restoreButton(MOVE_MODE - 1);
+ _key = _oldKey = -1;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+ }
+}
+
+void ScalpelUserInterface::doPickControl() {
+ Events &events = *_vm->_events;
+ Scene &scene = *_vm->_scene;
+ Talk &talk = *_vm->_talk;
+
+ if (events._released) {
+ if ((_temp = _bgFound) != -1) {
+ events.clearEvents();
+
+ // Don't allow characters to be picked up
+ if (_bgFound < 1000) {
+ scene._bgShapes[_bgFound].pickUpObject(MPICK);
+
+ if (!talk._talkToAbort && _menuMode != TALK_MODE) {
+ _key = _oldKey = -1;
+ _menuMode = STD_MODE;
+ restoreButton(PICKUP_MODE - 1);
+ }
+ }
+ }
+ }
+}
+
+void ScalpelUserInterface::doTalkControl() {
+ Events &events = *_vm->_events;
+ Journal &journal = *_vm->_journal;
+ People &people = *_vm->_people;
+ Screen &screen = *_vm->_screen;
+ Sound &sound = *_vm->_sound;
+ Talk &talk = *_vm->_talk;
+ Common::Point mousePos = events.mousePos();
+
+ _key = _oldKey = -1;
+ _keyboardInput = false;
+
+ if (events._pressed || events._released) {
+ events.clearKeyboard();
+
+ // Handle button printing
+ if (mousePos.x > 99 && mousePos.x < 138 && mousePos.y > CONTROLS_Y && mousePos.y < (CONTROLS_Y + 10) && !_endKeyActive)
+ screen.buttonPrint(Common::Point(119, CONTROLS_Y), COMMAND_HIGHLIGHTED, true, "Exit");
+ else if (_endKeyActive)
+ screen.buttonPrint(Common::Point(119, CONTROLS_Y), COMMAND_FOREGROUND, true, "Exit");
+
+ if (mousePos.x > 140 && mousePos.x < 170 && mousePos.y > CONTROLS_Y && mousePos.y < (CONTROLS_Y + 10) && talk._moreTalkUp)
+ screen.buttonPrint(Common::Point(159, CONTROLS_Y), COMMAND_HIGHLIGHTED, true, "Up");
+ else if (talk._moreTalkUp)
+ screen.buttonPrint(Common::Point(159, CONTROLS_Y), COMMAND_FOREGROUND, true, "Up");
+
+ if (mousePos.x > 181&& mousePos.x < 220 && mousePos.y > CONTROLS_Y && mousePos.y < (CONTROLS_Y + 10) && talk._moreTalkDown)
+ screen.buttonPrint(Common::Point(200, CONTROLS_Y), COMMAND_HIGHLIGHTED, true, "Down");
+ else if (talk._moreTalkDown)
+ screen.buttonPrint(Common::Point(200, CONTROLS_Y), COMMAND_FOREGROUND, true, "Down");
+
+ bool found = false;
+ for (_selector = talk._talkIndex; _selector < (int)talk._statements.size() && !found; ++_selector) {
+ if (mousePos.y > talk._statements[_selector]._talkPos.top &&
+ mousePos.y < talk._statements[_selector]._talkPos.bottom)
+ found = true;
+ }
+ --_selector;
+ if (!found)
+ _selector = -1;
+ }
+
+ if (_keyPress) {
+ _key = toupper(_keyPress);
+ if (_key == Common::KEYCODE_ESCAPE)
+ _key = 'E';
+
+ // Check for number press indicating reply line
+ if (_key >= '1' && _key <= ('1' + (int)talk._statements.size() - 1)) {
+ for (uint idx = 0; idx < talk._statements.size(); ++idx) {
+ if (talk._statements[idx]._talkMap == (_key - '1')) {
+ // Found the given statement
+ _selector = idx;
+ _key = -1;
+ _keyboardInput = true;
+ break;
+ }
+ }
+ } else if (_key == 'E' || _key == 'U' || _key == 'D') {
+ _keyboardInput = true;
+ } else {
+ _selector = -1;
+ }
+ }
+
+ if (_selector != _oldSelector) {
+ // Remove highlighting from previous line, if any
+ if (_oldSelector != -1) {
+ if (!((talk._talkHistory[talk._converseNum][_oldSelector] >> (_oldSelector & 7)) & 1))
+ talk.talkLine(_oldSelector, talk._statements[_oldSelector]._talkMap, INV_FOREGROUND,
+ talk._statements[_oldSelector]._talkPos.top, true);
+ else
+ talk.talkLine(_oldSelector, talk._statements[_oldSelector]._talkMap, TALK_NULL,
+ talk._statements[_oldSelector]._talkPos.top, true);
+ }
+
+ // Add highlighting to new line, if any
+ if (_selector != -1)
+ talk.talkLine(_selector, talk._statements[_selector]._talkMap, TALK_FOREGROUND,
+ talk._statements[_selector]._talkPos.top, true);
+
+ _oldSelector = _selector;
+ }
+
+ if (events._released || _keyboardInput) {
+ if (((Common::Rect(99, CONTROLS_Y, 138, CONTROLS_Y + 10).contains(mousePos) && events._released)
+ || _key == 'E') && _endKeyActive) {
+ talk.freeTalkVars();
+ talk.pullSequence();
+ banishWindow();
+ _windowBounds.top = CONTROLS_Y1;
+ } else if (((Common::Rect(140, CONTROLS_Y, 179, CONTROLS_Y + 10).contains(mousePos) && events._released)
+ || _key == 'U') && talk._moreTalkUp) {
+ while (talk._statements[--talk._talkIndex]._talkMap == -1)
+ ;
+ screen._backBuffer1.fillRect(Common::Rect(5, CONTROLS_Y + 11, SHERLOCK_SCREEN_WIDTH - 2,
+ SHERLOCK_SCREEN_HEIGHT - 1), INV_BACKGROUND);
+ talk.displayTalk(false);
+
+ screen.slamRect(Common::Rect(5, CONTROLS_Y, SHERLOCK_SCREEN_WIDTH - 5, SHERLOCK_SCREEN_HEIGHT - 2));
+ } else if (((Common::Rect(181, CONTROLS_Y, 220, CONTROLS_Y + 10).contains(mousePos) && events._released)
+ || _key == 'D') && talk._moreTalkDown) {
+ do {
+ ++talk._talkIndex;
+ } while (talk._talkIndex < (int)talk._statements.size() && talk._statements[talk._talkIndex]._talkMap == -1);
+
+ screen._backBuffer1.fillRect(Common::Rect(5, CONTROLS_Y + 11, SHERLOCK_SCREEN_WIDTH - 2,
+ SHERLOCK_SCREEN_HEIGHT - 1), INV_BACKGROUND);
+ talk.displayTalk(false);
+
+ screen.slamRect(Common::Rect(5, CONTROLS_Y, SHERLOCK_SCREEN_WIDTH - 5, SHERLOCK_SCREEN_HEIGHT - 2));
+ } else if (_selector != -1) {
+ screen.buttonPrint(Common::Point(119, CONTROLS_Y), COMMAND_NULL, true, "Exit");
+ screen.buttonPrint(Common::Point(159, CONTROLS_Y), COMMAND_NULL, true, "Up");
+ screen.buttonPrint(Common::Point(200, CONTROLS_Y), COMMAND_NULL, true, "Down");
+
+ // If the reply is new, add it to the journal
+ if (!talk._talkHistory[talk._converseNum][_selector]) {
+ journal.record(talk._converseNum, _selector);
+
+ // Add any Holmes point to Holmes' total, if any
+ if (talk._statements[_selector]._quotient)
+ people._holmesQuotient += talk._statements[_selector]._quotient;
+ }
+
+ // Flag the response as having been used
+ talk._talkHistory[talk._converseNum][_selector] = true;
+
+ clearWindow();
+ screen.print(Common::Point(16, CONTROLS_Y + 12), TALK_FOREGROUND, "Sherlock Holmes");
+ talk.talkLine(_selector + 128, talk._statements[_selector]._talkMap, COMMAND_FOREGROUND, CONTROLS_Y + 21, true);
+
+ switch (talk._statements[_selector]._portraitSide & 3) {
+ case 0:
+ case 1:
+ people._portraitSide = 20;
+ break;
+ case 2:
+ people._portraitSide = 220;
+ break;
+ case 3:
+ people._portraitSide = 120;
+ break;
+ }
+
+ // Check for flipping Holmes
+ if (talk._statements[_selector]._portraitSide & REVERSE_DIRECTION)
+ people._holmesFlip = true;
+
+ talk._speaker = 0;
+ people.setTalking(0);
+
+ if (!talk._statements[_selector]._voiceFile.empty() && sound._voices) {
+ sound.playSound(talk._statements[_selector]._voiceFile, WAIT_RETURN_IMMEDIATELY);
+
+ // Set voices as an indicator for waiting
+ sound._voices = 2;
+ sound._speechOn = *sound._soundIsOn;
+ } else {
+ sound._speechOn = false;
+ }
+
+ talk.waitForMore(talk._statements[_selector]._statement.size());
+ if (talk._talkToAbort)
+ return;
+
+ people.clearTalking();
+ if (talk._talkToAbort)
+ return;
+
+ while (!_vm->shouldQuit()) {
+ talk._scriptSelect = _selector;
+ talk._speaker = talk._talkTo;
+ talk.doScript(talk._statements[_selector]._reply);
+
+ if (!talk._talkToAbort) {
+ if (!talk._talkStealth)
+ clearWindow();
+
+ if (!talk._statements[_selector]._modified.empty()) {
+ for (uint idx = 0; idx < talk._statements[_selector]._modified.size(); ++idx) {
+ _vm->setFlags(talk._statements[_selector]._modified[idx]);
+ }
+
+ talk.setTalkMap();
+ }
+
+ // Check for another linked talk file
+ Common::String linkFilename = talk._statements[_selector]._linkFile;
+ if (!linkFilename.empty() && !talk._scriptMoreFlag) {
+ talk.freeTalkVars();
+ talk.loadTalkFile(linkFilename);
+
+ // Find the first new statement
+ int select = _selector = _oldSelector = -1;
+ for (uint idx = 0; idx < talk._statements.size() && select == -1; ++idx) {
+ if (!talk._statements[idx]._talkMap)
+ select = talk._talkIndex = idx;
+ }
+
+ // See if the new statement is a stealth reply
+ talk._talkStealth = talk._statements[select]._statement.hasPrefix("^") ? 2 : 0;
+
+ // Is the new talk file a standard file, reply first file, or a stealth file
+ if (!talk._statements[select]._statement.hasPrefix("*") &&
+ !talk._statements[select]._statement.hasPrefix("^")) {
+ // Not a reply first file, so display the new selections
+ if (_endKeyActive)
+ screen.buttonPrint(Common::Point(119, CONTROLS_Y), COMMAND_FOREGROUND, true, "Exit");
+ else
+ screen.buttonPrint(Common::Point(119, CONTROLS_Y), COMMAND_NULL, true, "Exit");
+
+ talk.displayTalk(true);
+ events.setCursor(ARROW);
+ break;
+ } else {
+ _selector = select;
+
+ if (!talk._talkHistory[talk._converseNum][_selector])
+ journal.record(talk._converseNum, _selector);
+
+ talk._talkHistory[talk._converseNum][_selector] = true;
+ }
+ } else {
+ talk.freeTalkVars();
+ talk.pullSequence();
+ banishWindow();
+ _windowBounds.top = CONTROLS_Y1;
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+
+ events._pressed = events._released = false;
+ events._oldButtons = 0;
+ talk._talkStealth = 0;
+
+ // If a script was pushed onto the script stack, restore it
+ if (!talk._scriptStack.empty()) {
+ ScriptStackEntry stackEntry = talk._scriptStack.pop();
+ talk._scriptName = stackEntry._name;
+ talk._scriptSaveIndex = stackEntry._currentIndex;
+ talk._scriptSelect = stackEntry._select;
+ }
+ }
+ }
+}
+
+void ScalpelUserInterface::journalControl() {
+ Events &events = *_vm->_events;
+ Journal &journal = *_vm->_journal;
+ Scene &scene = *_vm->_scene;
+ Screen &screen = *_vm->_screen;
+ bool doneFlag = false;
+
+ // Draw the journal screen
+ journal.drawInterface();
+
+ // Handle journal events
+ do {
+ _key = -1;
+ events.setButtonState();
+
+ // Handle keypresses
+ if (events.kbHit()) {
+ Common::KeyState keyState = events.getKey();
+ if (keyState.keycode == Common::KEYCODE_x && (keyState.flags & Common::KBD_ALT)) {
+ _vm->quitGame();
+ return;
+ } else if (keyState.keycode == Common::KEYCODE_e || keyState.keycode == Common::KEYCODE_ESCAPE) {
+ doneFlag = true;
+ } else {
+ _key = toupper(keyState.keycode);
+ }
+ }
+
+ if (!doneFlag)
+ doneFlag = journal.handleEvents(_key);
+ } while (!_vm->shouldQuit() && !doneFlag);
+
+ // Finish up
+ _infoFlag = _keyboardInput = false;
+ _keyPress = '\0';
+ _windowOpen = false;
+ _windowBounds.top = CONTROLS_Y1;
+ _key = -1;
+ _menuMode = STD_MODE;
+
+ // Reset the palette
+ screen.setPalette(screen._cMap);
+
+ screen._backBuffer1.blitFrom(screen._backBuffer2);
+ scene.updateBackground();
+ screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
+}
+
+void ScalpelUserInterface::printObjectDesc(const Common::String &str, bool firstTime) {
+ Events &events = *_vm->_events;
+ Inventory &inv = *_vm->_inventory;
+ Screen &screen = *_vm->_screen;
+ Talk &talk = *_vm->_talk;
+
+ if (str.hasPrefix("_")) {
+ _lookScriptFlag = true;
+ events.setCursor(MAGNIFY);
+ int savedSelector = _selector;
+ talk.talkTo(str.c_str() + 1);
+ _lookScriptFlag = false;
+
+ if (talk._talkToAbort) {
+ events.setCursor(ARROW);
+ return;
+ }
+
+ // Check if looking at an inventory object
+ if (!_invLookFlag) {
+ // See if this look was called by a right button click or not
+ if (!_lookHelp) {
+ // If it wasn't a right button click, then we need depress
+ // the look button before we close the window. So save a copy of the
+ // menu area, and draw the controls onto it
+ Surface tempSurface((*_controls)[0]._frame.w, (*_controls)[0]._frame.h);
+ Common::Point pt(MENU_POINTS[0][0], MENU_POINTS[0][1]);
+
+ tempSurface.blitFrom(screen._backBuffer2, Common::Point(0, 0),
+ Common::Rect(pt.x, pt.y, pt.x + tempSurface.w(), pt.y + tempSurface.h()));
+ screen._backBuffer2.transBlitFrom((*_controls)[0], pt);
+
+ banishWindow(1);
+ events.setCursor(MAGNIFY);
+ _windowBounds.top = CONTROLS_Y1;
+ _key = _oldKey = COMMANDS[LOOK_MODE - 1];
+ _temp = _oldTemp = 0;
+ _menuMode = LOOK_MODE;
+ events.clearEvents();
+
+ screen._backBuffer2.blitFrom(tempSurface, pt);
+ } else {
+ events.setCursor(ARROW);
+ banishWindow(true);
+ _windowBounds.top = CONTROLS_Y1;
+ _key = _oldKey = -1;
+ _temp = _oldTemp = 0;
+ _menuMode = STD_MODE;
+ _lookHelp = 0;
+ events.clearEvents();
+ }
+ } else {
+ // Looking at an inventory object
+ _selector = _oldSelector = savedSelector;
+
+ // Reload the inventory graphics and draw the inventory
+ inv.loadInv();
+ inv.putInv(SLAM_SECONDARY_BUFFER);
+ inv.freeInv();
+ banishWindow(1);
+
+ _windowBounds.top = CONTROLS_Y1;
+ _key = _oldKey = COMMANDS[INV_MODE - 1];
+ _temp = _oldTemp = 0;
+ events.clearEvents();
+
+ _invLookFlag = 0;
+ _menuMode = INV_MODE;
+ _windowOpen = true;
+ }
+
+ return;
+ }
+
+ Surface &bb = *screen._backBuffer;
+ if (firstTime) {
+ // Only draw the border on the first call
+ _infoFlag = true;
+ clearInfo();
+
+ bb.fillRect(Common::Rect(0, CONTROLS_Y, SHERLOCK_SCREEN_WIDTH,
+ CONTROLS_Y1 + 10), BORDER_COLOR);
+ bb.fillRect(Common::Rect(0, CONTROLS_Y + 10, 1, SHERLOCK_SCREEN_HEIGHT - 1),
+ BORDER_COLOR);
+ bb.fillRect(Common::Rect(SHERLOCK_SCREEN_WIDTH - 2, CONTROLS_Y + 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);
+ }
+
+ // Clear background
+ bb.fillRect(Common::Rect(2, CONTROLS_Y + 10, SHERLOCK_SCREEN_WIDTH - 2,
+ SHERLOCK_SCREEN_HEIGHT - 2), INV_BACKGROUND);
+
+ _windowBounds.top = CONTROLS_Y;
+ events.clearEvents();
+
+ // Loop through displaying up to five lines
+ bool endOfStr = false;
+ const char *msgP = str.c_str();
+ for (int lineNum = 0; lineNum < ONSCREEN_FILES_COUNT && !endOfStr; ++lineNum) {
+ int width = 0;
+ const char *lineStartP = msgP;
+
+ // Determine how much can be displayed on the line
+ do {
+ width += screen.charWidth(*msgP++);
+ } while (width < 300 && *msgP);
+
+ if (*msgP)
+ --msgP;
+ else
+ endOfStr = true;
+
+ // If the line needs to be wrapped, scan backwards to find
+ // the end of the previous word as a splitting point
+ if (width >= 300) {
+ while (*msgP != ' ')
+ --msgP;
+ endOfStr = false;
+ }
+
+ // Print out the line
+ Common::String line(lineStartP, msgP);
+ screen.gPrint(Common::Point(16, CONTROLS_Y + 12 + lineNum * 9),
+ INV_FOREGROUND, "%s", line.c_str());
+
+ if (!endOfStr)
+ // Start next line at start of the nxet word after space
+ ++msgP;
+ }
+
+ // Handle display depending on whether all the message was shown
+ if (!endOfStr) {
+ screen.makeButton(Common::Rect(46, CONTROLS_Y, 272, CONTROLS_Y + 10),
+ (SHERLOCK_SCREEN_WIDTH - screen.stringWidth(PRESS_KEY_FOR_MORE)) / 2,
+ PRESS_KEY_FOR_MORE);
+ screen.gPrint(Common::Point((SHERLOCK_SCREEN_WIDTH -
+ screen.stringWidth(PRESS_KEY_FOR_MORE)) / 2, CONTROLS_Y),
+ COMMAND_FOREGROUND, "P");
+ _descStr = msgP;
+ } else {
+ screen.makeButton(Common::Rect(46, CONTROLS_Y, 272, CONTROLS_Y + 10),
+ (SHERLOCK_SCREEN_WIDTH - screen.stringWidth(PRESS_KEY_TO_CONTINUE)) / 2,
+ PRESS_KEY_TO_CONTINUE);
+ screen.gPrint(Common::Point((SHERLOCK_SCREEN_WIDTH -
+ screen.stringWidth(PRESS_KEY_TO_CONTINUE)) / 2, CONTROLS_Y),
+ COMMAND_FOREGROUND, "P");
+ _descStr = "";
+ }
+
+ if (firstTime) {
+ if (!_slideWindows) {
+ screen.slamRect(Common::Rect(0, CONTROLS_Y,
+ SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT));
+ } else {
+ // Display the window
+ summonWindow();
+ }
+
+ _selector = _oldSelector = -1;
+ _windowOpen = true;
+ } else {
+ screen.slamRect(Common::Rect(0, CONTROLS_Y, SHERLOCK_SCREEN_WIDTH,
+ SHERLOCK_SCREEN_HEIGHT));
+ }
+}
+
+void ScalpelUserInterface::printObjectDesc() {
+ printObjectDesc(_cAnimStr, true);
+}
+
+void ScalpelUserInterface::summonWindow(const Surface &bgSurface, bool slideUp) {
+ Events &events = *_vm->_events;
+ Screen &screen = *_vm->_screen;
+
+ if (_windowOpen)
+ // A window is already open, so can't open another one
+ return;
+
+ if (slideUp) {
+ // Gradually slide up the display of the window
+ for (int idx = 1; idx <= bgSurface.h(); idx += 2) {
+ screen._backBuffer->blitFrom(bgSurface, Common::Point(0, SHERLOCK_SCREEN_HEIGHT - idx),
+ Common::Rect(0, 0, bgSurface.w(), idx));
+ screen.slamRect(Common::Rect(0, SHERLOCK_SCREEN_HEIGHT - idx,
+ SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT));
+
+ events.delay(10);
+ }
+ } else {
+ // Gradually slide down the display of the window
+ for (int idx = 1; idx <= bgSurface.h(); idx += 2) {
+ screen._backBuffer->blitFrom(bgSurface,
+ Common::Point(0, SHERLOCK_SCREEN_HEIGHT - bgSurface.h()),
+ Common::Rect(0, bgSurface.h() - idx, bgSurface.w(), bgSurface.h()));
+ screen.slamRect(Common::Rect(0, SHERLOCK_SCREEN_HEIGHT - bgSurface.h(),
+ SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT - bgSurface.h() + idx));
+
+ events.delay(10);
+ }
+ }
+
+ // Final display of the entire window
+ screen._backBuffer->blitFrom(bgSurface, Common::Point(0, SHERLOCK_SCREEN_HEIGHT - bgSurface.h()),
+ Common::Rect(0, 0, bgSurface.w(), bgSurface.h()));
+ screen.slamArea(0, SHERLOCK_SCREEN_HEIGHT - bgSurface.h(), bgSurface.w(), bgSurface.h());
+
+ _windowOpen = true;
+}
+
+void ScalpelUserInterface::summonWindow(bool slideUp, int height) {
+ Screen &screen = *_vm->_screen;
+
+ // Extract the window that's been drawn on the back buffer
+ Surface tempSurface(SHERLOCK_SCREEN_WIDTH,
+ (SHERLOCK_SCREEN_HEIGHT - height));
+ Common::Rect r(0, height, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
+ tempSurface.blitFrom(screen._backBuffer1, Common::Point(0, 0), r);
+
+ // Remove drawn window with original user interface
+ screen._backBuffer1.blitFrom(screen._backBuffer2,
+ Common::Point(0, height), r);
+
+ // Display the window gradually on-screen
+ summonWindow(tempSurface, slideUp);
+}
+
+void ScalpelUserInterface::banishWindow(bool slideUp) {
+ Events &events = *_vm->_events;
+ Screen &screen = *_vm->_screen;
+
+ if (_windowOpen) {
+ if (slideUp || !_slideWindows) {
+ // Slide window down
+ // Only slide the window if the window style allows it
+ if (_slideWindows) {
+ for (int idx = 2; idx < (SHERLOCK_SCREEN_HEIGHT - CONTROLS_Y); idx += 2) {
+ // Shift the window down by 2 lines
+ byte *pSrc = (byte *)screen._backBuffer1.getBasePtr(0, CONTROLS_Y + idx - 2);
+ byte *pSrcEnd = (byte *)screen._backBuffer1.getBasePtr(0, SHERLOCK_SCREEN_HEIGHT - 2);
+ byte *pDest = (byte *)screen._backBuffer1.getBasePtr(0, SHERLOCK_SCREEN_HEIGHT);
+ Common::copy_backward(pSrc, pSrcEnd, pDest);
+
+ // Restore lines from the ui in the secondary back buffer
+ screen._backBuffer1.blitFrom(screen._backBuffer2,
+ Common::Point(0, CONTROLS_Y),
+ Common::Rect(0, CONTROLS_Y, SHERLOCK_SCREEN_WIDTH, CONTROLS_Y + idx));
+
+ screen.slamArea(0, CONTROLS_Y + idx - 2, SHERLOCK_SCREEN_WIDTH,
+ SHERLOCK_SCREEN_HEIGHT - CONTROLS_Y - idx + 2);
+ events.delay(10);
+ }
+
+ // Restore final two old lines
+ screen._backBuffer1.blitFrom(screen._backBuffer2,
+ Common::Point(0, SHERLOCK_SCREEN_HEIGHT - 2),
+ Common::Rect(0, SHERLOCK_SCREEN_HEIGHT - 2,
+ SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT));
+ screen.slamArea(0, SHERLOCK_SCREEN_HEIGHT - 2, SHERLOCK_SCREEN_WIDTH, 2);
+ } else {
+ // Restore old area to completely erase window
+ screen._backBuffer1.blitFrom(screen._backBuffer2,
+ Common::Point(0, CONTROLS_Y),
+ Common::Rect(0, CONTROLS_Y, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT));
+ screen.slamRect(Common::Rect(0, CONTROLS_Y, SHERLOCK_SCREEN_WIDTH,
+ SHERLOCK_SCREEN_HEIGHT));
+ }
+ } else {
+ // Slide the original user interface up to cover the dialog
+ for (int idx = 1; idx < (SHERLOCK_SCREEN_HEIGHT - CONTROLS_Y1); idx += 2) {
+ byte *pSrc = (byte *)screen._backBuffer2.getBasePtr(0, CONTROLS_Y1);
+ byte *pSrcEnd = (byte *)screen._backBuffer2.getBasePtr(0, CONTROLS_Y1 + idx);
+ byte *pDest = (byte *)screen._backBuffer1.getBasePtr(0, SHERLOCK_SCREEN_HEIGHT - idx);
+ Common::copy(pSrc, pSrcEnd, pDest);
+
+ screen.slamArea(0, SHERLOCK_SCREEN_HEIGHT - idx, SHERLOCK_SCREEN_WIDTH,
+ SHERLOCK_SCREEN_HEIGHT);
+ events.delay(10);
+ }
+
+ // Show entire final area
+ screen._backBuffer1.blitFrom(screen._backBuffer2, Common::Point(0, CONTROLS_Y1),
+ Common::Rect(0, CONTROLS_Y1, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT));
+ screen.slamRect(Common::Rect(0, CONTROLS_Y1, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT));
+ }
+
+ _infoFlag = false;
+ _windowOpen = false;
+ }
+
+ _menuMode = STD_MODE;
+}
+
+void ScalpelUserInterface::checkUseAction(const UseType *use, const Common::String &invName,
+ const char *const messages[], int objNum, bool giveMode) {
+ Events &events = *_vm->_events;
+ Inventory &inv = *_vm->_inventory;
+ Scene &scene = *_vm->_scene;
+ Screen &screen = *_vm->_screen;
+ Talk &talk = *_vm->_talk;
+ bool printed = messages == nullptr;
+
+ if (objNum >= 1000) {
+ // Holmes was specified, so do nothing
+ _infoFlag = true;
+ clearInfo();
+ _infoFlag = true;
+
+ // Display error message
+ _menuCounter = 30;
+ screen.print(Common::Point(0, INFO_LINE + 1), INFO_FOREGROUND, "You can't do that to yourself.");
+ return;
+ }
+
+ // Scan for target item
+ int targetNum = -1;
+ if (giveMode) {
+ for (int idx = 0; idx < USE_COUNT && targetNum == -1; ++idx) {
+ if ((use[idx]._target.equalsIgnoreCase("*GIVE*") || use[idx]._target.equalsIgnoreCase("*GIVEP*"))
+ && use[idx]._names[0].equalsIgnoreCase(invName)) {
+ // Found a match
+ targetNum = idx;
+ if (use[idx]._target.equalsIgnoreCase("*GIVE*"))
+ inv.deleteItemFromInventory(invName);
+ }
+ }
+ } else {
+ for (int idx = 0; idx < USE_COUNT && targetNum == -1; ++idx) {
+ if (use[idx]._target.equalsIgnoreCase(invName))
+ targetNum = idx;
+ }
+ }
+
+ if (targetNum != -1) {
+ // Found a target, so do the action
+ const UseType &action = use[targetNum];
+
+ events.setCursor(WAIT);
+
+ if (action._useFlag)
+ _vm->setFlags(action._useFlag);
+
+ if (action._cAnimNum != 99) {
+ if (action._cAnimNum == 0)
+ scene.startCAnim(9, action._cAnimSpeed);
+ else
+ scene.startCAnim(action._cAnimNum - 1, action._cAnimSpeed);
+ }
+
+ if (!talk._talkToAbort) {
+ Object &obj = scene._bgShapes[objNum];
+ for (int idx = 0; idx < NAMES_COUNT && !talk._talkToAbort; ++idx) {
+ if (obj.checkNameForCodes(action._names[idx], messages)) {
+ if (!talk._talkToAbort)
+ printed = true;
+ }
+ }
+
+ // Print "Done..." as an ending, unless flagged for leaving scene or otherwise flagged
+ if (scene._goToScene != 1 && !printed && !talk._talkToAbort) {
+ _infoFlag = true;
+ clearInfo();
+ screen.print(Common::Point(0, INFO_LINE + 1), INFO_FOREGROUND, "Done...");
+ _menuCounter = 25;
+ }
+ }
+ } else {
+ // Couldn't find target, so print error
+ _infoFlag = true;
+ clearInfo();
+
+ if (giveMode) {
+ screen.print(Common::Point(0, INFO_LINE + 1), INFO_FOREGROUND, "No, thank you.");
+ } else if (messages == nullptr) {
+ screen.print(Common::Point(0, INFO_LINE + 1), INFO_FOREGROUND, "You can't do that.");
+ } else {
+ screen.print(Common::Point(0, INFO_LINE + 1), INFO_FOREGROUND, "%s", messages[0]);
+ }
+
+ _infoFlag = true;
+ _menuCounter = 30;
+ }
+
+ events.setCursor(ARROW);
+}
+
+void ScalpelUserInterface::checkAction(ActionType &action, const char *const messages[], int objNum) {
+ Events &events = *_vm->_events;
+ People &people = *_vm->_people;
+ Scene &scene = *_vm->_scene;
+ Screen &screen = *_vm->_screen;
+ Talk &talk = *_vm->_talk;
+ Common::Point pt(-1, -1);
+
+ if (objNum >= 1000)
+ // Ignore actions done on characters
+ return;
+
+ if (!action._cAnimSpeed) {
+ // Invalid action, to print error message
+ _infoFlag = true;
+ clearInfo();
+ screen.print(Common::Point(0, INFO_LINE + 1), INFO_FOREGROUND, "%s", messages[action._cAnimNum]);
+ _infoFlag = true;
+
+ // Set how long to show the message
+ _menuCounter = 30;
+ } else {
+ Object &obj = scene._bgShapes[objNum];
+
+ int cAnimNum;
+ if (action._cAnimNum == 0)
+ // Really a 10
+ cAnimNum = 9;
+ else
+ cAnimNum = action._cAnimNum - 1;
+
+ int dir = -1;
+ if (action._cAnimNum != 99) {
+ CAnim &anim = scene._cAnim[cAnimNum];
+
+ if (action._cAnimNum != 99) {
+ if (action._cAnimSpeed & REVERSE_DIRECTION) {
+ pt = anim._teleportPos;
+ dir = anim._teleportDir;
+ } else {
+ pt = anim._goto;
+ dir = anim._gotoDir;
+ }
+ }
+ } else {
+ pt = Common::Point(-1, -1);
+ dir = -1;
+ }
+
+ // Has a value, so do action
+ // Show wait cursor whilst walking to object and doing action
+ events.setCursor(WAIT);
+ bool printed = false;
+
+ for (int nameIdx = 0; nameIdx < NAMES_COUNT; ++nameIdx) {
+ if (action._names[nameIdx].hasPrefix("*") && action._names[nameIdx].size() >= 2
+ && toupper(action._names[nameIdx][1]) == 'W') {
+ if (obj.checkNameForCodes(Common::String(action._names[nameIdx].c_str() + 2), messages)) {
+ if (!talk._talkToAbort)
+ printed = true;
+ }
+ }
+ }
+
+ bool doCAnim = true;
+ for (int nameIdx = 0; nameIdx < NAMES_COUNT; ++nameIdx) {
+ if (action._names[nameIdx].hasPrefix("*") && action._names[nameIdx].size() >= 2) {
+ char ch = toupper(action._names[nameIdx][1]);
+
+ if (ch == 'T' || ch == 'B') {
+ printed = true;
+ if (pt.x != -1)
+ // Holmes needs to walk to object before the action is done
+ people.walkToCoords(pt, dir);
+
+ if (!talk._talkToAbort) {
+ // Ensure Holmes is on the exact intended location
+ people[AL]._position = pt;
+ people[AL]._sequenceNumber = dir;
+ people.gotoStand(people[AL]);
+
+ talk.talkTo(action._names[nameIdx].c_str() + 2);
+ if (ch == 'T')
+ doCAnim = false;
+ }
+ }
+ }
+ }
+
+ if (doCAnim && !talk._talkToAbort) {
+ if (pt.x != -1)
+ // Holmes needs to walk to object before the action is done
+ people.walkToCoords(pt, dir);
+ }
+
+ for (int nameIdx = 0; nameIdx < NAMES_COUNT; ++nameIdx) {
+ if (action._names[nameIdx].hasPrefix("*") && action._names[nameIdx].size() >= 2
+ && toupper(action._names[nameIdx][1]) == 'F') {
+ if (obj.checkNameForCodes(action._names[nameIdx].c_str() + 2, messages)) {
+ if (!talk._talkToAbort)
+ printed = true;
+ }
+ }
+ }
+
+ if (doCAnim && !talk._talkToAbort && action._cAnimNum != 99)
+ scene.startCAnim(cAnimNum, action._cAnimSpeed);
+
+ if (!talk._talkToAbort) {
+ for (int nameIdx = 0; nameIdx < NAMES_COUNT && !talk._talkToAbort; ++nameIdx) {
+ if (obj.checkNameForCodes(action._names[nameIdx], messages)) {
+ if (!talk._talkToAbort)
+ printed = true;
+ }
+ }
+
+ // Unless we're leaving the scene, print a "Done" message unless the printed flag has been set
+ if (scene._goToScene != 1 && !printed && !talk._talkToAbort) {
+ _infoFlag = true;
+ clearInfo();
+ screen.print(Common::Point(0, INFO_LINE + 1), INFO_FOREGROUND, "Done...");
+
+ // Set how long to show the message
+ _menuCounter = 30;
+ }
+ }
+ }
+
+ // Reset cursor back to arrow
+ events.setCursor(ARROW);
+}
+
+} // End of namespace Scalpel
+
+} // End of namespace Sherlock
diff --git a/engines/sherlock/scalpel/scalpel_user_interface.h b/engines/sherlock/scalpel/scalpel_user_interface.h
new file mode 100644
index 0000000000..8ed69ac8b5
--- /dev/null
+++ b/engines/sherlock/scalpel/scalpel_user_interface.h
@@ -0,0 +1,227 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef SHERLOCK_SCALPEL_UI_H
+#define SHERLOCK_SCALPEL_UI_H
+
+#include "common/scummsys.h"
+#include "sherlock/user_interface.h"
+
+namespace Sherlock {
+
+class Inventory;
+class Talk;
+
+namespace Scalpel {
+
+extern const char COMMANDS[13];
+extern const int MENU_POINTS[12][4];
+
+extern const int INVENTORY_POINTS[8][3];
+extern const char INVENTORY_COMMANDS[9];
+extern const char *const PRESS_KEY_FOR_MORE;
+extern const char *const PRESS_KEY_TO_CONTINUE;
+
+class Settings;
+
+class ScalpelUserInterface: public UserInterface {
+ friend class Inventory;
+ friend class Settings;
+ friend class Talk;
+private:
+ ImageFile *_controlPanel;
+ ImageFile *_controls;
+ char _keyPress;
+ int _lookHelp;
+ int _bgFound, _oldBgFound;
+ int _help, _oldHelp;
+ char _key, _oldKey;
+ int _temp, _oldTemp;
+ int _oldLook;
+ bool _keyboardInput;
+ bool _pause;
+ int _cNum;
+ Common::String _cAnimStr;
+ Common::String _descStr;
+ int _find;
+ int _oldUse;
+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(int num);
+
+ /**
+ * Creates a text window and uses it to display the in-depth description
+ * of the highlighted object
+ */
+ void examine();
+
+ /**
+ * 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 it's 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, const char *const messages[],
+ int objNum, bool giveMode);
+
+ /**
+ * Called for OPEN, CLOSE, and MOVE actions are being done
+ */
+ void checkAction(ActionType &action, const char *const messages[], int objNum);
+
+ /**
+ * Print the previously selected object's decription
+ */
+ void printObjectDesc(const Common::String &str, bool firstTime);
+public:
+ ScalpelUserInterface(SherlockEngine *vm);
+ virtual ~ScalpelUserInterface();
+
+ /**
+ * 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);
+public:
+ /**
+ * Resets the user interface
+ */
+ virtual void reset();
+
+ /**
+ * Main input handler for the user interface
+ */
+ virtual void handleInput();
+
+ /**
+ * Draw the user interface onto the screen's back buffers
+ */
+ virtual void drawInterface(int bufferNum = 3);
+
+ /**
+ * Displays a passed window by gradually scrolling it vertically on-screen
+ */
+ virtual void summonWindow(const Surface &bgSurface, bool slideUp = true);
+
+ /**
+ * Slide the window stored in the back buffer onto the screen
+ */
+ virtual void summonWindow(bool slideUp = true, int height = CONTROLS_Y);
+
+ /**
+ * Close a currently open window
+ * @param flag 0 = slide old window down, 1 = slide prior UI back up
+ */
+ virtual void banishWindow(bool slideUp = true);
+
+ /**
+ * Clears the info line of the screen
+ */
+ virtual void clearInfo();
+
+ /**
+ * Clear any active text window
+ */
+ virtual void clearWindow();
+
+ /**
+ * Print the previously selected object's decription
+ */
+ virtual void printObjectDesc();
+};
+
+} // End of namespace Scalpel
+
+} // End of namespace Sherlock
+
+#endif
diff --git a/engines/sherlock/settings.cpp b/engines/sherlock/scalpel/settings.cpp
index 397d882e0f..aa8033d25e 100644
--- a/engines/sherlock/settings.cpp
+++ b/engines/sherlock/scalpel/settings.cpp
@@ -21,10 +21,13 @@
*/
#include "sherlock/sherlock.h"
-#include "sherlock/settings.h"
+#include "sherlock/scalpel/settings.h"
+#include "sherlock/scalpel/scalpel_user_interface.h"
namespace Sherlock {
+namespace Scalpel {
+
static const int SETUP_POINTS[12][4] = {
{ 4, 154, 101, 53 }, // Exit
{ 4, 165, 101, 53 }, // Music Toggle
@@ -212,9 +215,10 @@ void Settings::show(SherlockEngine *vm) {
Sound &sound = *vm->_sound;
Music &music = *vm->_music;
Talk &talk = *vm->_talk;
- UserInterface &ui = *vm->_ui;
+ ScalpelUserInterface &ui = *(ScalpelUserInterface *)vm->_ui;
bool updateConfig = false;
+ assert(vm->getGameID() == GType_SerratedScalpel);
Settings settings(vm);
settings.drawInteface(false);
@@ -332,4 +336,6 @@ void Settings::show(SherlockEngine *vm) {
ui._key = -1;
}
+} // End of namespace Scalpel
+
} // End of namespace Sherlock
diff --git a/engines/sherlock/settings.h b/engines/sherlock/scalpel/settings.h
index fc5fb2959f..ff2e647a62 100644
--- a/engines/sherlock/settings.h
+++ b/engines/sherlock/scalpel/settings.h
@@ -28,7 +28,8 @@
namespace Sherlock {
class SherlockEngine;
-class UserInterface;
+
+namespace Scalpel {
class Settings {
private:
@@ -55,6 +56,8 @@ public:
static void show(SherlockEngine *vm);
};
+} // End of namespace Scalpel
+
} // End of namespace Sherlock
#endif
diff --git a/engines/sherlock/scene.cpp b/engines/sherlock/scene.cpp
index 781e596f40..76fb7ae3a7 100644
--- a/engines/sherlock/scene.cpp
+++ b/engines/sherlock/scene.cpp
@@ -23,6 +23,10 @@
#include "sherlock/scene.h"
#include "sherlock/sherlock.h"
#include "sherlock/scalpel/scalpel.h"
+#include "sherlock/scalpel/scalpel_scene.h"
+#include "sherlock/screen.h"
+#include "sherlock/tattoo/tattoo.h"
+#include "sherlock/tattoo/tattoo_scene.h"
namespace Sherlock {
@@ -33,13 +37,38 @@ static const int FS_TRANS[8] = {
/*----------------------------------------------------------------*/
-void BgFileHeader::load(Common::SeekableReadStream &s) {
+BgFileHeader::BgFileHeader() {
+ _numStructs = -1;
+ _numImages = -1;
+ _numcAnimations = -1;
+ _descSize = -1;
+ _seqSize = -1;
+
+ // Serrated Scalpel
+ _fill = -1;
+
+ // Rose Tattoo
+ _scrollSize = -1;
+ _bytesWritten = -1;
+ _fadeStyle = -1;
+ Common::fill(&_palette[0], &_palette[PALETTE_SIZE], 0);
+}
+
+void BgFileHeader::load(Common::SeekableReadStream &s, bool isRoseTattoo) {
_numStructs = s.readUint16LE();
_numImages = s.readUint16LE();
_numcAnimations = s.readUint16LE();
_descSize = s.readUint16LE();
_seqSize = s.readUint16LE();
- _fill = s.readUint16LE();
+
+ if (isRoseTattoo) {
+ _scrollSize = s.readUint16LE();
+ _bytesWritten = s.readUint32LE();
+ _fadeStyle = s.readByte();
+ } else {
+ _fill = s.readUint16LE();
+
+ }
}
/*----------------------------------------------------------------*/
@@ -55,18 +84,30 @@ void BgFileHeaderInfo::load(Common::SeekableReadStream &s) {
/*----------------------------------------------------------------*/
-void Exit::load(Common::SeekableReadStream &s) {
- int xp = s.readSint16LE();
- int yp = s.readSint16LE();
- int xSize = s.readSint16LE();
- int ySize = s.readSint16LE();
- _bounds = Common::Rect(xp, yp, xp + xSize, yp + ySize);
+void Exit::load(Common::SeekableReadStream &s, bool isRoseTattoo) {
+ if (isRoseTattoo) {
+ char buffer[41];
+ s.read(buffer, 41);
+ _dest = Common::String(buffer);
+ }
+
+ left = s.readSint16LE();
+ top = s.readSint16LE();
+ setWidth(s.readUint16LE());
+ setHeight(s.readUint16LE());
+ _image = isRoseTattoo ? s.readByte() : 0;
_scene = s.readSint16LE();
- _allow = s.readSint16LE();
+
+ if (!isRoseTattoo)
+ _allow = s.readSint16LE();
+
_people.x = s.readSint16LE();
_people.y = s.readSint16LE();
_peopleDir = s.readUint16LE();
+
+ if (isRoseTattoo)
+ _allow = s.readSint16LE();
}
/*----------------------------------------------------------------*/
@@ -100,7 +141,26 @@ int ObjectArray::indexOf(const Object &obj) const {
/*----------------------------------------------------------------*/
-Scene::Scene(SherlockEngine *vm) : _vm(vm) {
+void ScaleZone::load(Common::SeekableReadStream &s) {
+ left = s.readSint16LE();
+ top = s.readSint16LE();
+ setWidth(s.readUint16LE());
+ setHeight(s.readUint16LE());
+
+ _topNumber = s.readByte();
+ _bottomNumber = s.readByte();
+}
+
+/*----------------------------------------------------------------*/
+
+Scene *Scene::init(SherlockEngine *vm) {
+ if (vm->getGameID() == GType_SerratedScalpel)
+ return new Scalpel::ScalpelScene(vm);
+ else
+ return new Tattoo::TattooScene(vm);
+}
+
+Scene::Scene(SherlockEngine *vm): _vm(vm) {
for (int idx = 0; idx < SCENES_COUNT; ++idx)
Common::fill(&_sceneStats[idx][0], &_sceneStats[idx][65], false);
_currentScene = -1;
@@ -115,6 +175,7 @@ Scene::Scene(SherlockEngine *vm) : _vm(vm) {
_animating = 0;
_doBgAnimDone = true;
_tempFadeStyle = 0;
+ _exitZone = -1;
}
Scene::~Scene() {
@@ -149,8 +210,8 @@ void Scene::selectScene() {
_tempFadeStyle = 0;
}
- people._walkDest = Common::Point(people[AL]._position.x / 100,
- people[AL]._position.y / 100);
+ people._walkDest = Common::Point(people[AL]._position.x / FIXED_INT_MULTIPLIER,
+ people[AL]._position.y / FIXED_INT_MULTIPLIER);
_restoreFlag = true;
events.clearEvents();
@@ -193,11 +254,13 @@ void Scene::freeScene() {
bool Scene::loadScene(const Common::String &filename) {
Events &events = *_vm->_events;
Map &map = *_vm->_map;
+ Music &music = *_vm->_music;
People &people = *_vm->_people;
+ Resources &res = *_vm->_res;
SaveManager &saves = *_vm->_saves;
Screen &screen = *_vm->_screen;
Sound &sound = *_vm->_sound;
- Music &music = *_vm->_music;
+ Talk &talk = *_vm->_talk;
UserInterface &ui = *_vm->_ui;
bool flag;
@@ -213,8 +276,38 @@ bool Scene::loadScene(const Common::String &filename) {
_cAnim.clear();
_sequenceBuffer.clear();
+ // Check if it's a scene we need to keep trakc track of how many times we've visited
+ for (int idx = (int)_sceneTripCounters.size() - 1; idx >= 0; --idx) {
+ if (_sceneTripCounters[idx]._sceneNumber == _currentScene) {
+ if (--_sceneTripCounters[idx]._numTimes == 0) {
+ _vm->setFlags(_sceneTripCounters[idx]._flag);
+ _sceneTripCounters.remove_at(idx);
+ }
+ }
+ }
+
+ if (IS_ROSE_TATTOO) {
+ // Set the NPC paths for the scene
+ setNPCPath(0);
+
+ // Handle loading music for the scene
+ if (sound._midiDrvLoaded) {
+ if (talk._scriptMoreFlag != 1 && talk._scriptMoreFlag != 3)
+ sound._nextSongName = Common::String::format("res%02d", _currentScene);
+
+ // If it's a new song, then start it up
+ if (sound._currentSongName.compareToIgnoreCase(sound._nextSongName)) {
+ if (music.loadSong(sound._nextSongName)) {
+ sound.setMIDIVolume(sound._musicVolume);
+ if (music._musicOn)
+ music.startSong();
+ }
+ }
+ }
+ }
+
//
- // Load background shapes from <filename>.rrm
+ // Load the room resource file for the scene
//
Common::String rrmFile = filename + ".rrm";
@@ -223,15 +316,36 @@ bool Scene::loadScene(const Common::String &filename) {
Common::SeekableReadStream *rrmStream = _vm->_res->load(rrmFile);
rrmStream->seek(39);
- _version = rrmStream->readByte();
- _lzwMode = _version == 10;
+ if (IS_SERRATED_SCALPEL) {
+ _version = rrmStream->readByte();
+ _lzwMode = _version == 10;
+ } else {
+ _lzwMode = rrmStream->readByte() > 0;
+ }
// Go to header and read it in
rrmStream->seek(rrmStream->readUint32LE());
+
BgFileHeader bgHeader;
- bgHeader.load(*rrmStream);
+ bgHeader.load(*rrmStream, IS_ROSE_TATTOO);
_invGraphicItems = bgHeader._numImages + 1;
+ if (IS_ROSE_TATTOO) {
+ screen.initPaletteFade(bgHeader._bytesWritten);
+ rrmStream->read(screen._cMap, PALETTE_SIZE);
+ screen.translatePalette(screen._cMap);
+ screen.setupBGArea(screen._cMap);
+
+ screen.initScrollVars();
+
+ // Read in background
+ if (_lzwMode) {
+ res.decompress(*rrmStream, (byte *)screen._backBuffer1.getPixels(), SHERLOCK_SCREEN_WIDTH * SHERLOCK_SCREEN_HEIGHT);
+ } else {
+ rrmStream->read(screen._backBuffer1.getPixels(), SHERLOCK_SCREEN_WIDTH * SHERLOCK_SCREEN_HEIGHT);
+ }
+ }
+
// Read in the shapes header info
Common::Array<BgFileHeaderInfo> bgInfo;
bgInfo.resize(bgHeader._numStructs);
@@ -240,10 +354,35 @@ bool Scene::loadScene(const Common::String &filename) {
bgInfo[idx].load(*rrmStream);
// Read information
- if (!_lzwMode) {
+ if (IS_ROSE_TATTOO) {
+ // Load shapes
+ Common::SeekableReadStream *infoStream = !_lzwMode ? rrmStream : res.decompress(*rrmStream, bgHeader._numStructs * 625);
+
_bgShapes.resize(bgHeader._numStructs);
for (int idx = 0; idx < bgHeader._numStructs; ++idx)
- _bgShapes[idx].load(*rrmStream);
+ _bgShapes[idx].load(*infoStream, _vm->getGameID() == GType_RoseTattoo);
+
+ if (_lzwMode)
+ delete infoStream;
+
+ // Load description text
+ _descText.resize(bgHeader._descSize);
+ if (_lzwMode)
+ res.decompress(*rrmStream, (byte *)&_descText[0], bgHeader._descSize);
+ else
+ rrmStream->read(&_descText[0], bgHeader._descSize);
+
+ // Load sequences
+ _sequenceBuffer.resize(bgHeader._seqSize);
+ if (_lzwMode)
+ res.decompress(*rrmStream, &_sequenceBuffer[0], bgHeader._seqSize);
+ else
+ rrmStream->read(&_sequenceBuffer[0], bgHeader._seqSize);
+ } else if (!_lzwMode) {
+ // Serrated Scalpel uncompressed info
+ _bgShapes.resize(bgHeader._numStructs);
+ for (int idx = 0; idx < bgHeader._numStructs; ++idx)
+ _bgShapes[idx].load(*rrmStream, false);
if (bgHeader._descSize) {
_descText.resize(bgHeader._descSize);
@@ -255,6 +394,7 @@ bool Scene::loadScene(const Common::String &filename) {
rrmStream->read(&_sequenceBuffer[0], bgHeader._seqSize);
}
} else {
+ // Serrated Scalpel compressed info
Common::SeekableReadStream *infoStream;
// Read shapes
@@ -262,7 +402,7 @@ bool Scene::loadScene(const Common::String &filename) {
_bgShapes.resize(bgHeader._numStructs);
for (int idx = 0; idx < bgHeader._numStructs; ++idx)
- _bgShapes[idx].load(*infoStream);
+ _bgShapes[idx].load(*infoStream, false);
delete infoStream;
@@ -295,7 +435,7 @@ bool Scene::loadScene(const Common::String &filename) {
// Read in the image data
Common::SeekableReadStream *imageStream = _lzwMode ?
- Resources::decompressLZ(*rrmStream, bgInfo[idx]._filesize) :
+ res.decompress(*rrmStream, bgInfo[idx]._filesize) :
rrmStream->readStream(bgInfo[idx]._filesize);
_images[idx + 1]._images = new ImageFile(*imageStream);
@@ -323,13 +463,14 @@ bool Scene::loadScene(const Common::String &filename) {
// Load in cAnim list
_cAnim.clear();
if (bgHeader._numcAnimations) {
+ int animSize = IS_SERRATED_SCALPEL ? 65 : 47;
Common::SeekableReadStream *canimStream = _lzwMode ?
- Resources::decompressLZ(*rrmStream, 65 * bgHeader._numcAnimations) :
- rrmStream->readStream(65 * bgHeader._numcAnimations);
+ res.decompress(*rrmStream, animSize * bgHeader._numcAnimations) :
+ rrmStream->readStream(animSize * bgHeader._numcAnimations);
_cAnim.resize(bgHeader._numcAnimations);
for (uint idx = 0; idx < _cAnim.size(); ++idx)
- _cAnim[idx].load(*canimStream);
+ _cAnim[idx].load(*canimStream, IS_ROSE_TATTOO);
delete canimStream;
}
@@ -337,7 +478,7 @@ bool Scene::loadScene(const Common::String &filename) {
// Read in the room bounding areas
int size = rrmStream->readUint16LE();
Common::SeekableReadStream *boundsStream = !_lzwMode ? rrmStream :
- Resources::decompressLZ(*rrmStream, size);
+ res.decompress(*rrmStream, size);
_zones.resize(size / 10);
for (uint idx = 0; idx < _zones.size(); ++idx) {
@@ -352,10 +493,11 @@ bool Scene::loadScene(const Common::String &filename) {
delete boundsStream;
// Ensure we've reached the path version byte
- if (rrmStream->readByte() != 254)
+ if (rrmStream->readByte() != (IS_SERRATED_SCALPEL ? 254 : 251))
error("Invalid scene path data");
// Load the walk directory
+ assert(_zones.size() < MAX_ZONES);
for (uint idx1 = 0; idx1 < _zones.size(); ++idx1) {
for (uint idx2 = 0; idx2 < _zones.size(); ++idx2)
_walkDirectory[idx1][idx2] = rrmStream->readSint16LE();
@@ -364,7 +506,7 @@ bool Scene::loadScene(const Common::String &filename) {
// Read in the walk data
size = rrmStream->readUint16LE();
Common::SeekableReadStream *walkStream = !_lzwMode ? rrmStream :
- Resources::decompressLZ(*rrmStream, size);
+ res.decompress(*rrmStream, size);
_walkData.resize(size);
walkStream->read(&_walkData[0], size);
@@ -372,15 +514,27 @@ bool Scene::loadScene(const Common::String &filename) {
if (_lzwMode)
delete walkStream;
+ if (IS_ROSE_TATTOO) {
+ // Read in the entrance
+ _entrance.load(*rrmStream);
+
+ // Load scale zones
+ _scaleZones.resize(rrmStream->readByte());
+ for (uint idx = 0; idx < _scaleZones.size(); ++idx)
+ _scaleZones[idx].load(*rrmStream);
+ }
+
// Read in the exits
+ _exitZone = -1;
int numExits = rrmStream->readByte();
_exits.resize(numExits);
for (int idx = 0; idx < numExits; ++idx)
- _exits[idx].load(*rrmStream);
+ _exits[idx].load(*rrmStream, IS_ROSE_TATTOO);
- // Read in the entrance
- _entrance.load(*rrmStream);
+ if (IS_SERRATED_SCALPEL)
+ // Read in the entrance
+ _entrance.load(*rrmStream);
// Initialize sound list
int numSounds = rrmStream->readByte();
@@ -389,24 +543,32 @@ bool Scene::loadScene(const Common::String &filename) {
for (int idx = 0; idx < numSounds; ++idx)
_sounds[idx].load(*rrmStream);
- for (int idx = 0; idx < numSounds; ++idx)
- sound.loadSound(_sounds[idx]._name, _sounds[idx]._priority);
-
- // Read in palette
- rrmStream->read(screen._cMap, PALETTE_SIZE);
- for (int idx = 0; idx < PALETTE_SIZE; ++idx)
- screen._cMap[idx] = VGA_COLOR_TRANS(screen._cMap[idx]);
+ loadSceneSounds();
- Common::copy(screen._cMap, screen._cMap + PALETTE_SIZE, screen._sMap);
+ if (IS_ROSE_TATTOO) {
+ // Load the object sound list
+ char buffer[27];
+
+ _objSoundList.resize(rrmStream->readUint16LE());
+ for (uint idx = 0; idx < _objSoundList.size(); ++idx) {
+ rrmStream->read(buffer, 27);
+ _objSoundList[idx] = Common::String(buffer);
+ }
+ } else {
+ // Read in palette
+ rrmStream->read(screen._cMap, PALETTE_SIZE);
+ screen.translatePalette(screen._cMap);
+ Common::copy(screen._cMap, screen._cMap + PALETTE_SIZE, screen._sMap);
- // Read in the background
- Common::SeekableReadStream *bgStream = !_lzwMode ? rrmStream :
- Resources::decompressLZ(*rrmStream, SHERLOCK_SCREEN_WIDTH * SHERLOCK_SCENE_HEIGHT);
+ // Read in the background
+ Common::SeekableReadStream *bgStream = !_lzwMode ? rrmStream :
+ res.decompress(*rrmStream, SHERLOCK_SCREEN_WIDTH * SHERLOCK_SCENE_HEIGHT);
- bgStream->read(screen._backBuffer1.getPixels(), SHERLOCK_SCREEN_WIDTH * SHERLOCK_SCENE_HEIGHT);
+ bgStream->read(screen._backBuffer1.getPixels(), SHERLOCK_SCREEN_WIDTH * SHERLOCK_SCENE_HEIGHT);
- if (_lzwMode)
- delete bgStream;
+ if (_lzwMode)
+ delete bgStream;
+ }
// Backup the image and set the palette
screen._backBuffer2.blitFrom(screen._backBuffer1);
@@ -415,7 +577,7 @@ bool Scene::loadScene(const Common::String &filename) {
delete rrmStream;
}
- // Clear user interface area and draw controls
+ // Handle drawing any on-screen interface
ui.drawInterface();
checkSceneStatus();
@@ -447,7 +609,7 @@ bool Scene::loadScene(const Common::String &filename) {
checkInventory();
// Handle starting any music for the scene
- if (music._musicOn && music.loadSong(_currentScene))
+ if (IS_SERRATED_SCALPEL && music._musicOn && music.loadSong(_currentScene))
music.startSong();
// Load walking images if not already loaded
@@ -463,14 +625,25 @@ bool Scene::loadScene(const Common::String &filename) {
if (!_vm->isDemo()) {
// Reset the previous map location and position on overhead map
map._oldCharPoint = _currentScene;
- map._overPos.x = map[_currentScene].x * 100 - 600;
- map._overPos.y = map[_currentScene].y * 100 + 900;
+
+ if (IS_SERRATED_SCALPEL) {
+ map._overPos.x = (map[_currentScene].x - 6) * FIXED_INT_MULTIPLIER;
+ map._overPos.y = (map[_currentScene].y + 9) * FIXED_INT_MULTIPLIER;
+
+ }
}
events.clearEvents();
return flag;
}
+void Scene::loadSceneSounds() {
+ Sound &sound = *_vm->_sound;
+
+ for (uint idx = 0; idx < _sounds.size(); ++idx)
+ sound.loadSound(_sounds[idx]._name, _sounds[idx]._priority);
+}
+
void Scene::checkSceneStatus() {
if (_sceneStats[_currentScene][64]) {
for (uint idx = 0; idx < 64; ++idx) {
@@ -597,8 +770,8 @@ void Scene::transitionToScene() {
// Otherwise, this is a linked scene or entrance info, and must be translated
if (hSavedFacing < 8 && !saves._justLoaded) {
hSavedFacing = FS_TRANS[hSavedFacing];
- hSavedPos.x *= 100;
- hSavedPos.y *= 100;
+ hSavedPos.x *= FIXED_INT_MULTIPLIER;
+ hSavedPos.y *= FIXED_INT_MULTIPLIER;
}
}
@@ -638,7 +811,8 @@ void Scene::transitionToScene() {
}
if (Common::Rect(topLeft.x, topLeft.y, bottomRight.x, bottomRight.y).contains(
- Common::Point(people[PLAYER]._position.x / 100, people[PLAYER]._position.y / 100))) {
+ Common::Point(people[PLAYER]._position.x / FIXED_INT_MULTIPLIER,
+ people[PLAYER]._position.y / FIXED_INT_MULTIPLIER))) {
// Current point is already inside box - impact occurred on
// a previous call. So simply do nothing except talk until the
// player is clear of the box
@@ -669,11 +843,14 @@ void Scene::transitionToScene() {
updateBackground();
+ // Actually do the transition
if (screen._fadeStyle)
screen.randomTransition();
else
screen.blitFrom(screen._backBuffer1);
+ screen.update();
+ // Start any initial animation for the scene
if (cAnimNum != -1) {
CAnim &c = _cAnim[cAnimNum];
Common::Point pt = c._goto;
@@ -701,19 +878,26 @@ int Scene::toggleObject(const Common::String &name) {
void Scene::updateBackground() {
People &people = *_vm->_people;
- Screen &screen = *_vm->_screen;
- Sprite &player = people[AL];
-
- // Restrict drawing window
- screen.setDisplayBounds(Common::Rect(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCENE_HEIGHT));
// Update Holmes if he's turned on
- if (people._holmesOn)
- player.adjustSprite();
+ for (int idx = 0; idx < MAX_CHARACTERS; ++idx) {
+ if (people[idx]._type == CHARACTER)
+ people[idx].adjustSprite();
+ }
// Flag the bg shapes which need to be redrawn
- checkBgShapes(player._imageFrame, Common::Point(player._position.x / 100,
- player._position.y / 100));
+ checkBgShapes();
+
+ // Draw the shapes for the scene
+ drawAllShapes();
+}
+
+void Scene::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) {
@@ -725,7 +909,7 @@ void Scene::updateBackground() {
for (uint idx = 0; idx < _canimShapes.size(); ++idx) {
if (_canimShapes[idx]._type == ACTIVE_BG_SHAPE && _canimShapes[idx]._misc == BEHIND)
screen._backBuffer->transBlitFrom(*_canimShapes[idx]._imageFrame,
- _canimShapes[idx]._position, _canimShapes[idx]._flags & OBJ_FLIPPED);
+ _canimShapes[idx]._position, _canimShapes[idx]._flags & OBJ_FLIPPED);
}
// Draw all active shapes which are normal and behind the person
@@ -738,33 +922,37 @@ void Scene::updateBackground() {
for (uint idx = 0; idx < _canimShapes.size(); ++idx) {
if (_canimShapes[idx]._type == ACTIVE_BG_SHAPE && _canimShapes[idx]._misc == NORMAL_BEHIND)
screen._backBuffer->transBlitFrom(*_canimShapes[idx]._imageFrame, _canimShapes[idx]._position,
- _canimShapes[idx]._flags & OBJ_FLIPPED);
+ _canimShapes[idx]._flags & OBJ_FLIPPED);
}
- // Draw the player if he's active
- if (player._type == CHARACTER && people.isHolmesActive()) {
- bool flipped = player._sequenceNumber == WALK_LEFT || player._sequenceNumber == STOP_LEFT ||
- player._sequenceNumber == WALK_UPLEFT || player._sequenceNumber == STOP_UPLEFT ||
- player._sequenceNumber == WALK_DOWNRIGHT || player._sequenceNumber == STOP_DOWNRIGHT;
-
- screen._backBuffer->transBlitFrom(*player._imageFrame, Common::Point(player._position.x / 100,
- player._position.y / 100 - player.frameHeight()), 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._backBuffer->transBlitFrom(*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)
+ _bgShapes[idx]._misc == NORMAL_FORWARD)
screen._backBuffer->transBlitFrom(*_bgShapes[idx]._imageFrame, _bgShapes[idx]._position,
- _bgShapes[idx]._flags & OBJ_FLIPPED);
+ _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)
+ _canimShapes[idx]._misc == NORMAL_FORWARD)
screen._backBuffer->transBlitFrom(*_canimShapes[idx]._imageFrame, _canimShapes[idx]._position,
- _canimShapes[idx]._flags & OBJ_FLIPPED);
+ _canimShapes[idx]._flags & OBJ_FLIPPED);
}
// Draw all static and active shapes that are FORWARD
@@ -774,9 +962,9 @@ void Scene::updateBackground() {
_bgShapes[idx].frameHeight());
if ((_bgShapes[idx]._type == ACTIVE_BG_SHAPE || _bgShapes[idx]._type == STATIC_BG_SHAPE) &&
- _bgShapes[idx]._misc == FORWARD)
+ _bgShapes[idx]._misc == FORWARD)
screen._backBuffer->transBlitFrom(*_bgShapes[idx]._imageFrame, _bgShapes[idx]._position,
- _bgShapes[idx]._flags & OBJ_FLIPPED);
+ _bgShapes[idx]._flags & OBJ_FLIPPED);
}
// Draw all static and active canimations that are forward
@@ -784,7 +972,7 @@ void Scene::updateBackground() {
if ((_canimShapes[idx]._type == ACTIVE_BG_SHAPE || _canimShapes[idx]._type == STATIC_BG_SHAPE) &&
_canimShapes[idx]._misc == FORWARD)
screen._backBuffer->transBlitFrom(*_canimShapes[idx]._imageFrame, _canimShapes[idx]._position,
- _canimShapes[idx]._flags & OBJ_FLIPPED);
+ _canimShapes[idx]._flags & OBJ_FLIPPED);
}
screen.resetDisplayBounds();
@@ -792,47 +980,13 @@ void Scene::updateBackground() {
Exit *Scene::checkForExit(const Common::Rect &r) {
for (uint idx = 0; idx < _exits.size(); ++idx) {
- if (_exits[idx]._bounds.intersects(r))
+ if (_exits[idx].intersects(r))
return &_exits[idx];
}
return nullptr;
}
-void Scene::checkBgShapes(ImageFrame *frame, const Common::Point &pt) {
- // Iterate through the shapes
- for (uint idx = 0; idx < _bgShapes.size(); ++idx) {
- Object &obj = _bgShapes[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.frameHeight() - 1)) ?
- NORMAL_FORWARD : NORMAL_BEHIND;
- } else if (!(obj._flags & OBJ_BEHIND)) {
- obj._misc = BEHIND;
- } else if (obj._flags & OBJ_FORWARD) {
- obj._misc = FORWARD;
- }
- }
- }
-
- // Iterate through the canimshapes
- 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;
- }
- }
- }
-}
-
int Scene::startCAnim(int cAnimNum, int playRate) {
Events &events = *_vm->_events;
Map &map = *_vm->_map;
@@ -1045,333 +1199,6 @@ int Scene::startCAnim(int cAnimNum, int playRate) {
return 1;
}
-void Scene::doBgAnim() {
- Events &events = *_vm->_events;
- Inventory &inv = *_vm->_inventory;
- People &people = *_vm->_people;
- Screen &screen = *_vm->_screen;
- Sound &sound = *_vm->_sound;
- Talk &talk = *_vm->_talk;
- UserInterface &ui = *_vm->_ui;
-
- screen.setDisplayBounds(Common::Rect(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCENE_HEIGHT));
-
- int cursorId = events.getCursor();
- Common::Point mousePos = events.mousePos();
-
- talk._talkToAbort = false;
-
- // Animate the mouse cursor
- if (cursorId >= WAIT) {
- if (++cursorId > (WAIT + 2))
- cursorId = WAIT;
-
- events.setCursor((CursorId)cursorId);
- }
-
- 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);
- }
- }
-
- if (sound._diskSoundPlaying && !*sound._soundIsOn) {
- // Loaded sound just finished playing
- sound.freeDigiSound();
- }
-
- if (_restoreFlag) {
- if (people[AL]._type == CHARACTER)
- people[AL].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 == 12 && _vm->getGameID() == GType_SerratedScalpel)
- ((Scalpel::ScalpelEngine *)_vm)->eraseMirror12();
-
- // Restore the back buffer from the back buffer 2 in the changed area
- Common::Rect bounds(people[AL]._oldPosition.x, people[AL]._oldPosition.y,
- people[AL]._oldPosition.x + people[AL]._oldSize.x,
- people[AL]._oldPosition.y + people[AL]._oldSize.y);
- Common::Point pt(bounds.left, bounds.top);
-
- if (people[AL]._type == CHARACTER)
- screen.restoreBackground(bounds);
- else if (people[AL]._type == REMOVE)
- screen._backBuffer->blitFrom(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._backBuffer->blitFrom(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[AL]._type == CHARACTER && people._holmesOn)
- people[AL].adjustSprite();
-
- // Flag the bg shapes which need to be redrawn
- checkBgShapes(people[AL]._imageFrame,
- Common::Point(people[AL]._position.x / 100, people[AL]._position.y / 100));
-
- if (_currentScene == 12 && _vm->getGameID() == GType_SerratedScalpel)
- ((Scalpel::ScalpelEngine *)_vm)->doMirror12();
-
- // 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._backBuffer->transBlitFrom(*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._backBuffer->transBlitFrom(*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._backBuffer->transBlitFrom(*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._backBuffer->transBlitFrom(*o._imageFrame, o._position, o._flags & OBJ_FLIPPED);
- }
- }
-
- // Draw the person if not animating
- if (people[AL]._type == CHARACTER && people.isHolmesActive()) {
- // If Holmes is too far to the right, move him back so he's on-screen
- int xRight = SHERLOCK_SCREEN_WIDTH - 2 - people[AL]._imageFrame->_frame.w;
- int tempX = MIN(people[AL]._position.x / 100, xRight);
-
- bool flipped = people[AL]._sequenceNumber == WALK_LEFT || people[AL]._sequenceNumber == STOP_LEFT ||
- people[AL]._sequenceNumber == WALK_UPLEFT || people[AL]._sequenceNumber == STOP_UPLEFT ||
- people[AL]._sequenceNumber == WALK_DOWNRIGHT || people[AL]._sequenceNumber == STOP_DOWNRIGHT;
- screen._backBuffer->transBlitFrom(*people[AL]._imageFrame,
- Common::Point(tempX, people[AL]._position.y / 100 - people[AL]._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._backBuffer->transBlitFrom(*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._backBuffer->transBlitFrom(*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._backBuffer->transBlitFrom(*o._imageFrame, o._position, o._flags & OBJ_FLIPPED);
- }
-
- // Draw any active portrait
- if (people._portraitLoaded && people._portrait._type == ACTIVE_BG_SHAPE)
- screen._backBuffer->transBlitFrom(*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._backBuffer->transBlitFrom(*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._backBuffer->transBlitFrom(*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[AL]._type != INVALID && ((_goToScene == -1 || _canimShapes.empty()))) {
- if (people[AL]._type == REMOVE) {
- screen.slamRect(Common::Rect(
- people[AL]._oldPosition.x, people[AL]._oldPosition.y,
- people[AL]._oldPosition.x + people[AL]._oldSize.x,
- people[AL]._oldPosition.y + people[AL]._oldSize.y
- ));
- people[AL]._type = INVALID;
- } else {
- screen.flushImage(people[AL]._imageFrame,
- Common::Point(people[AL]._position.x / 100,
- people[AL]._position.y / 100 - people[AL].frameHeight()),
- &people[AL]._oldPosition.x, &people[AL]._oldPosition.y,
- &people[AL]._oldSize.x, &people[AL]._oldSize.y);
- }
- }
-
- if (_currentScene == 12 && _vm->getGameID() == GType_SerratedScalpel)
- ((Scalpel::ScalpelEngine *)_vm)->flushMirror12();
-
- 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
- _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
- _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 Scene::findBgShape(const Common::Rect &r) {
if (!_doBgAnimDone)
// New frame hasn't been drawn yet
@@ -1457,4 +1284,46 @@ void Scene::synchronize(Common::Serializer &s) {
}
}
+void Scene::setNPCPath(int npc) {
+ People &people = *_vm->_people;
+ Talk &talk = *_vm->_talk;
+
+ people[npc].clearNPC();
+ people[npc]._name = Common::String::format("WATS%.2dA", _currentScene);
+
+ // If we're in the middle of a script that will continue once the scene is loaded,
+ // return without calling the path script
+ if (talk._scriptMoreFlag == 1 || talk._scriptMoreFlag == 3)
+ return;
+
+ // Turn off all the NPCs, since the talk script will turn them back on as needed
+ for (uint idx = 0; idx < MAX_NPC; ++idx)
+ people[idx + 1]._type = INVALID;
+
+ // Call the path script for the scene
+ Common::String pathFile = Common::String::format("PATH%.2dA", _currentScene);
+ talk.talkTo(pathFile);
+}
+
+void Scene::checkBgShapes() {
+ People &people = *_vm->_people;
+ Person &holmes = people._player;
+ Common::Point pt(holmes._position.x / FIXED_INT_MULTIPLIER, holmes._position.y / FIXED_INT_MULTIPLIER);
+
+ // Iterate through the shapes
+ for (uint idx = 0; idx < _bgShapes.size(); ++idx) {
+ Object &obj = _bgShapes[idx];
+ if (obj._type == ACTIVE_BG_SHAPE || (IS_SERRATED_SCALPEL && obj._type == STATIC_BG_SHAPE)) {
+ if ((obj._flags & 5) == 1) {
+ obj._misc = (pt.y < (obj._position.y + obj.frameHeight() - 1)) ?
+ NORMAL_FORWARD : NORMAL_BEHIND;
+ } else if (!(obj._flags & OBJ_BEHIND)) {
+ obj._misc = BEHIND;
+ } else if (obj._flags & OBJ_FORWARD) {
+ obj._misc = FORWARD;
+ }
+ }
+ }
+}
+
} // End of namespace Sherlock
diff --git a/engines/sherlock/scene.h b/engines/sherlock/scene.h
index 88d12a3fb0..37a1b32740 100644
--- a/engines/sherlock/scene.h
+++ b/engines/sherlock/scene.h
@@ -29,6 +29,7 @@
#include "common/serializer.h"
#include "sherlock/objects.h"
#include "sherlock/resources.h"
+#include "sherlock/screen.h"
namespace Sherlock {
@@ -44,12 +45,23 @@ struct BgFileHeader {
int _numcAnimations;
int _descSize;
int _seqSize;
+
+ // Serrated Scalpel
int _fill;
+ // Rose Tattoo
+ int _scrollSize;
+ int _bytesWritten; // Size of the main body of the RRM
+ int _fadeStyle; // Fade style
+ byte _palette[PALETTE_SIZE]; // Palette
+
+
+ BgFileHeader();
+
/**
* Load the data for the object
*/
- void load(Common::SeekableReadStream &s);
+ void load(Common::SeekableReadStream &s, bool isRoseTattoo);
};
struct BgFileHeaderInfo {
@@ -63,18 +75,20 @@ struct BgFileHeaderInfo {
void load(Common::SeekableReadStream &s);
};
-struct Exit {
- Common::Rect _bounds;
-
+class Exit: public Common::Rect {
+public:
int _scene;
int _allow;
Common::Point _people;
int _peopleDir;
+ Common::String _dest;
+ int _image; // Arrow image to use
+
/**
* Load the data for the object
*/
- void load(Common::SeekableReadStream &s);
+ void load(Common::SeekableReadStream &s, bool isRoseTattoo);
};
struct SceneEntry {
@@ -106,9 +120,26 @@ public:
int indexOf(const Object &obj) const;
};
+class ScaleZone: public Common::Rect {
+public:
+ int _topNumber; // Numerator of scale size at the top of the zone
+ int _bottomNumber; // Numerator of scale size at the bottom of the zone
+
+ void load(Common::SeekableReadStream &s);
+};
+
+struct SceneTripEntry {
+ bool _flag;
+ int _sceneNumber;
+ int _numTimes;
+
+ SceneTripEntry() : _flag(false), _sceneNumber(0), _numTimes(0) {}
+ SceneTripEntry(bool flag, int sceneNumber, int numTimes) : _flag(flag),
+ _sceneNumber(sceneNumber), _numTimes(numTimes) {}
+};
+
class Scene {
private:
- SherlockEngine *_vm;
Common::String _rrmName;
bool _loadingSavedGame;
@@ -124,6 +155,11 @@ private:
bool loadScene(const Common::String &filename);
/**
+ * Loads sounds for the scene
+ */
+ void loadSceneSounds();
+
+ /**
* Set objects to their current persistent state. This includes things such as
* opening or moving them
*/
@@ -143,17 +179,26 @@ private:
void transitionToScene();
/**
+ * Restores objects to the correct status. This ensures that things like being opened or moved
+ * will remain the same on future visits to the scene
+ */
+ void saveSceneStatus();
+protected:
+ SherlockEngine *_vm;
+
+ /**
* 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(ImageFrame *frame, const Common::Point &pt);
+ virtual void checkBgShapes();
/**
- * Restores objects to the correct status. This ensures that things like being opened or moved
- * will remain the same on future visits to the scene
+ * Draw all the shapes, people and NPCs in the correct order
*/
- void saveSceneStatus();
+ void drawAllShapes();
+
+ Scene(SherlockEngine *vm);
public:
int _currentScene;
int _goToScene;
@@ -173,17 +218,21 @@ public:
int _walkDirectory[MAX_ZONES][MAX_ZONES];
Common::Array<byte> _walkData;
Common::Array<Exit> _exits;
+ int _exitZone;
SceneEntry _entrance;
Common::Array<SceneSound> _sounds;
ObjectArray _canimShapes;
+ Common::Array<ScaleZone> _scaleZones;
+ Common::StringArray _objSoundList;
bool _restoreFlag;
int _animating;
bool _doBgAnimDone;
int _tempFadeStyle;
int _cAnimFramePause;
+ Common::Array<SceneTripEntry> _sceneTripCounters;
public:
- Scene(SherlockEngine *vm);
- ~Scene();
+ static Scene *init(SherlockEngine *vm);
+ virtual ~Scene();
/**
* Handles loading the scene specified by _goToScene
@@ -224,11 +273,6 @@ public:
int toggleObject(const Common::String &name);
/**
- * Animate all objects and people.
- */
- void doBgAnim();
-
- /**
* Attempts to find a background shape within the passed bounds. If found,
* it will return the shape number, or -1 on failure.
*/
@@ -251,15 +295,27 @@ public:
int closestZone(const Common::Point &pt);
/**
- * Update the screen back buffer with all of the scene objects which need
- * to be drawn
+ * Synchronize the data for a savegame
*/
- void updateBackground();
+ void synchronize(Common::Serializer &s);
/**
- * Synchronize the data for a savegame
+ * Resets the NPC path information when entering a new scene.
+ * @remarks The default talk file for the given NPC is set to WATS##A, where ## is
+ * the scene number being entered
*/
- void synchronize(Common::Serializer &s);
+ void setNPCPath(int npc);
+public:
+ /**
+ * Draw all objects and characters.
+ */
+ virtual void doBgAnim() = 0;
+
+ /**
+ * Update the screen back buffer with all of the scene objects which need
+ * to be drawn
+ */
+ virtual void updateBackground();
};
} // End of namespace Sherlock
diff --git a/engines/sherlock/screen.cpp b/engines/sherlock/screen.cpp
index e70d0614d1..a3af5559c7 100644
--- a/engines/sherlock/screen.cpp
+++ b/engines/sherlock/screen.cpp
@@ -28,9 +28,9 @@
namespace Sherlock {
-Screen::Screen(SherlockEngine *vm) : Surface(SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT), _vm(vm),
- _backBuffer1(SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT),
- _backBuffer2(SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT),
+Screen::Screen(SherlockEngine *vm) : Surface(g_system->getWidth(), g_system->getHeight()), _vm(vm),
+ _backBuffer1(g_system->getWidth(), g_system->getHeight()),
+ _backBuffer2(g_system->getWidth(), g_system->getHeight()),
_backBuffer(&_backBuffer1) {
_transitionSeed = 1;
_fadeStyle = false;
@@ -38,7 +38,17 @@ Screen::Screen(SherlockEngine *vm) : Surface(SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCR
_fontHeight = 0;
Common::fill(&_cMap[0], &_cMap[PALETTE_SIZE], 0);
Common::fill(&_sMap[0], &_sMap[PALETTE_SIZE], 0);
- setFont(1);
+ Common::fill(&_tMap[0], &_tMap[PALETTE_SIZE], 0);
+ setFont(IS_SERRATED_SCALPEL ? 1 : 4);
+
+ // Rose Tattoo specific fields
+ _fadeBytesRead = _fadeBytesToRead = 0;
+ _oldFadePercent = 0;
+ _scrollSize = 0;
+ _scrollSpeed = 0;
+ _currentScroll = 0;
+ _targetScroll = 0;
+ _flushScreen = false;
}
Screen::~Screen() {
@@ -174,12 +184,13 @@ void Screen::randomTransition() {
Events &events = *_vm->_events;
const int TRANSITION_MULTIPLIER = 0x15a4e35;
_dirtyRects.clear();
+ assert(IS_SERRATED_SCALPEL);
for (int idx = 0; idx <= 65535 && !_vm->shouldQuit(); ++idx) {
_transitionSeed = _transitionSeed * TRANSITION_MULTIPLIER + 1;
int offset = _transitionSeed & 0xFFFF;
- if (offset < (SHERLOCK_SCREEN_WIDTH * SHERLOCK_SCREEN_HEIGHT))
+ if (offset < (this->w() * this->h()))
*((byte *)getPixels() + offset) = *((const byte *)_backBuffer->getPixels() + offset);
if (idx != 0 && (idx % 300) == 0) {
@@ -199,12 +210,12 @@ void Screen::randomTransition() {
void Screen::verticalTransition() {
Events &events = *_vm->_events;
- byte table[SHERLOCK_SCREEN_WIDTH];
- Common::fill(&table[0], &table[SHERLOCK_SCREEN_WIDTH], 0);
+ byte table[640];
+ Common::fill(&table[0], &table[640], 0);
- for (int yp = 0; yp < SHERLOCK_SCREEN_HEIGHT; ++yp) {
- for (int xp = 0; xp < SHERLOCK_SCREEN_WIDTH; ++xp) {
- int temp = (table[xp] >= 197) ? SHERLOCK_SCREEN_HEIGHT - table[xp] :
+ for (int yp = 0; yp < this->h(); ++yp) {
+ for (int xp = 0; xp < this->w(); ++xp) {
+ int temp = (table[xp] >= (this->h() - 3)) ? this->h() - table[xp] :
_vm->getRandomNumber(3) + 1;
if (temp) {
@@ -221,7 +232,7 @@ void Screen::verticalTransition() {
void Screen::restoreBackground(const Common::Rect &r) {
if (r.width() > 0 && r.height() > 0) {
Common::Rect tempRect = r;
- tempRect.clip(Common::Rect(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCENE_HEIGHT));
+ tempRect.clip(Common::Rect(0, 0, this->w(), SHERLOCK_SCENE_HEIGHT));
if (tempRect.isValidRect())
_backBuffer1.blitFrom(_backBuffer2, Common::Point(tempRect.left, tempRect.top), tempRect);
@@ -235,31 +246,62 @@ void Screen::slamArea(int16 xp, int16 yp, int16 width, int16 height) {
void Screen::slamRect(const Common::Rect &r) {
if (r.width() && r.height() > 0) {
Common::Rect tempRect = r;
- tempRect.clip(Common::Rect(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT));
+ tempRect.clip(Common::Rect(0, 0, this->w(), this->h()));
if (tempRect.isValidRect())
blitFrom(*_backBuffer, Common::Point(tempRect.left, tempRect.top), tempRect);
}
}
-void Screen::flushImage(ImageFrame *frame, const Common::Point &pt,
- int16 *xp, int16 *yp, int16 *width, int16 *height) {
+void Screen::flushImage(ImageFrame *frame, const Common::Point &pt, int16 *xp, int16 *yp,
+ int16 *width, int16 *height) {
Common::Point imgPos = pt + frame->_offset;
Common::Rect newBounds(imgPos.x, imgPos.y, imgPos.x + frame->_frame.w, imgPos.y + frame->_frame.h);
Common::Rect oldBounds(*xp, *yp, *xp + *width, *yp + *height);
- // See if the areas of the old and new overlap, and if so combine the areas
- if (newBounds.intersects(oldBounds)) {
- Common::Rect mergedBounds = newBounds;
- mergedBounds.extend(oldBounds);
- mergedBounds.right += 1;
- mergedBounds.bottom += 1;
+ if (!_flushScreen) {
+ // See if the areas of the old and new overlap, and if so combine the areas
+ if (newBounds.intersects(oldBounds)) {
+ Common::Rect mergedBounds = newBounds;
+ mergedBounds.extend(oldBounds);
+ mergedBounds.right += 1;
+ mergedBounds.bottom += 1;
- slamRect(mergedBounds);
- } else {
- // The two areas are independent, so copy them both
- slamRect(newBounds);
- slamRect(oldBounds);
+ slamRect(mergedBounds);
+ } else {
+ // The two areas are independent, so copy them both
+ slamRect(newBounds);
+ slamRect(oldBounds);
+ }
+ }
+
+ *xp = newBounds.left;
+ *yp = newBounds.top;
+ *width = newBounds.width();
+ *height = newBounds.height();
+}
+
+void Screen::flushScaleImage(ImageFrame *frame, const Common::Point &pt, int16 *xp, int16 *yp,
+ int16 *width, int16 *height, int scaleVal) {
+ Common::Point imgPos = pt + frame->_offset;
+ Common::Rect newBounds(imgPos.x, imgPos.y, imgPos.x + frame->sDrawXSize(scaleVal),
+ imgPos.y + frame->sDrawYSize(scaleVal));
+ Common::Rect oldBounds(*xp, *yp, *xp + *width, *yp + *height);
+
+ if (!_flushScreen) {
+ // See if the areas of the old and new overlap, and if so combine the areas
+ if (newBounds.intersects(oldBounds)) {
+ Common::Rect mergedBounds = newBounds;
+ mergedBounds.extend(oldBounds);
+ mergedBounds.right += 1;
+ mergedBounds.bottom += 1;
+
+ slamRect(mergedBounds);
+ } else {
+ // The two areas are independent, so copy them both
+ slamRect(newBounds);
+ slamRect(oldBounds);
+ }
}
*xp = newBounds.left;
@@ -281,13 +323,13 @@ void Screen::print(const Common::Point &pt, byte color, const char *formatStr, .
pos.y--; // Font is always drawing one line higher
if (!pos.x)
// Center text horizontally
- pos.x = (SHERLOCK_SCREEN_WIDTH - width) / 2;
+ pos.x = (this->w() - width) / 2;
Common::Rect textBounds(pos.x, pos.y, pos.x + width, pos.y + _fontHeight);
- if (textBounds.right > SHERLOCK_SCREEN_WIDTH)
- textBounds.moveTo(SHERLOCK_SCREEN_WIDTH - width, textBounds.top);
- if (textBounds.bottom > SHERLOCK_SCREEN_HEIGHT)
- textBounds.moveTo(textBounds.left, SHERLOCK_SCREEN_HEIGHT - _fontHeight);
+ if (textBounds.right > this->w())
+ textBounds.moveTo(this->w() - width, textBounds.top);
+ if (textBounds.bottom > this->h())
+ textBounds.moveTo(textBounds.left, this->h() - _fontHeight);
// Write out the string at the given position
writeString(str, Common::Point(textBounds.left, textBounds.top), color);
@@ -416,7 +458,7 @@ void Screen::resetDisplayBounds() {
Common::Rect Screen::getDisplayBounds() {
return (_backBuffer == &_sceneSurface) ? Common::Rect(0, 0, _sceneSurface.w(), _sceneSurface.h()) :
- Common::Rect(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
+ Common::Rect(0, 0, this->w(), this->h());
}
void Screen::synchronize(Common::Serializer &s) {
@@ -426,4 +468,41 @@ void Screen::synchronize(Common::Serializer &s) {
setFont(fontNumb);
}
+void Screen::initPaletteFade(int bytesToRead) {
+ Common::copy(&_cMap[0], &_cMap[PALETTE_SIZE], &_sMap[0]);
+ Common::copy(&_cMap[0], &_cMap[PALETTE_SIZE], &_tMap[0]);
+
+ // Set how many bytes need to be read / have been read
+ _fadeBytesRead = 0;
+ _fadeBytesToRead = bytesToRead;
+ _oldFadePercent = 0;
+}
+
+int Screen::fadeRead(Common::SeekableReadStream &stream, byte *buf, int totalSize) {
+ warning("TODO: fadeRead");
+ stream.read(buf, totalSize);
+ return totalSize;
+}
+
+/**
+ * Creates a grey-scale version of the passed palette
+ */
+void Screen::setupBGArea(const byte cMap[PALETTE_SIZE]) {
+ warning("TODO");
+}
+
+/**
+ * Initializes scroll variables
+ */
+void Screen::initScrollVars() {
+ _scrollSize = 0;
+ _currentScroll = 0;
+ _targetScroll = 0;
+}
+
+void Screen::translatePalette(byte palette[PALETTE_SIZE]) {
+ for (int idx = 0; idx < PALETTE_SIZE; ++idx)
+ palette[idx] = VGA_COLOR_TRANS(palette[idx]);
+}
+
} // End of namespace Sherlock
diff --git a/engines/sherlock/screen.h b/engines/sherlock/screen.h
index a2c0aa3c84..8fda9cbb9c 100644
--- a/engines/sherlock/screen.h
+++ b/engines/sherlock/screen.h
@@ -66,6 +66,12 @@ private:
int _fontHeight;
Surface _sceneSurface;
+ // Rose Tattoo fields
+ int _fadeBytesRead, _fadeBytesToRead;
+ int _oldFadePercent;
+ byte _lookupTable[PALETTE_COUNT];
+ byte _lookupTable1[PALETTE_COUNT];
+private:
/**
* Merges together overlapping dirty areas of the screen
*/
@@ -92,6 +98,10 @@ public:
bool _fadeStyle;
byte _cMap[PALETTE_SIZE];
byte _sMap[PALETTE_SIZE];
+ byte _tMap[PALETTE_SIZE];
+ int _currentScroll, _targetScroll;
+ int _scrollSize, _scrollSpeed;
+ bool _flushScreen;
public:
Screen(SherlockEngine *vm);
virtual ~Screen();
@@ -171,8 +181,15 @@ public:
* Copy an image from the back buffer to the screen, taking care of both the
* new area covered by the shape as well as the old area, which must be restored
*/
- void flushImage(ImageFrame *frame, const Common::Point &pt,
- int16 *xp, int16 *yp, int16 *width, int16 *height);
+ void flushImage(ImageFrame *frame, const Common::Point &pt, int16 *xp, int16 *yp,
+ int16 *width, int16 *height);
+
+ /**
+ * Similar to flushImage, this method takes in an extra parameter for the scale proporation,
+ * which affects the calculated bounds accordingly
+ */
+ void flushScaleImage(ImageFrame *frame, const Common::Point &pt, int16 *xp, int16 *yp,
+ int16 *width, int16 *height, int scaleVal);
/**
* Returns the width of a string in pixels
@@ -232,6 +249,21 @@ public:
* Synchronize the data for a savegame
*/
void synchronize(Common::Serializer &s);
+
+ // Rose Tattoo specific methods
+ void initPaletteFade(int bytesToRead);
+
+ int fadeRead(Common::SeekableReadStream &stream, byte *buf, int totalSize);
+
+ void setupBGArea(const byte cMap[PALETTE_SIZE]);
+
+ void initScrollVars();
+
+ /**
+ * Translate a palette from 6-bit RGB values to full 8-bit values suitable for passing
+ * to the underlying palette manager
+ */
+ static void translatePalette(byte palette[PALETTE_SIZE]);
};
} // End of namespace Sherlock
diff --git a/engines/sherlock/sherlock.cpp b/engines/sherlock/sherlock.cpp
index 9807c23fee..d3a409a67b 100644
--- a/engines/sherlock/sherlock.cpp
+++ b/engines/sherlock/sherlock.cpp
@@ -25,7 +25,6 @@
#include "common/scummsys.h"
#include "common/config-manager.h"
#include "common/debug-channels.h"
-#include "engines/util.h"
namespace Sherlock {
@@ -72,8 +71,6 @@ SherlockEngine::~SherlockEngine() {
}
void SherlockEngine::initialize() {
- initGraphics(SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT, false);
-
DebugMan.addDebugChannel(kDebugScript, "scripts", "Script debug level");
ImageFile::setVm(this);
@@ -98,11 +95,11 @@ void SherlockEngine::initialize() {
_journal = new Journal(this);
_people = new People(this);
_saves = new SaveManager(this, _targetName);
- _scene = new Scene(this);
+ _scene = Scene::init(this);
_screen = new Screen(this);
_sound = new Sound(this, _mixer);
- _talk = new Talk(this);
- _ui = new UserInterface(this);
+ _talk = Talk::init(this);
+ _ui = UserInterface::init(this);
// Load game settings
loadConfig();
@@ -213,7 +210,9 @@ void SherlockEngine::loadConfig() {
ConfMan.registerDefault("font", 1);
_screen->setFont(ConfMan.getInt("font"));
- _screen->_fadeStyle = ConfMan.getBool("fade_style");
+ if (getGameID() == GType_SerratedScalpel)
+ _screen->_fadeStyle = ConfMan.getBool("fade_style");
+
_ui->_helpStyle = ConfMan.getBool("help_style");
_ui->_slideWindows = ConfMan.getBool("window_style");
_people->_portraitsOn = ConfMan.getBool("portraits_on");
diff --git a/engines/sherlock/sherlock.h b/engines/sherlock/sherlock.h
index 2e85651700..e71c729893 100644
--- a/engines/sherlock/sherlock.h
+++ b/engines/sherlock/sherlock.h
@@ -59,9 +59,9 @@ enum GameType {
GType_RoseTattoo = 1
};
-#define SHERLOCK_SCREEN_WIDTH 320
-#define SHERLOCK_SCREEN_HEIGHT 200
-#define SHERLOCK_SCENE_HEIGHT 138
+#define SHERLOCK_SCREEN_WIDTH _vm->_screen->w()
+#define SHERLOCK_SCREEN_HEIGHT _vm->_screen->h()
+#define SHERLOCK_SCENE_HEIGHT (IS_SERRATED_SCALPEL ? 138 : 480)
struct SherlockGameDescription;
@@ -199,6 +199,9 @@ public:
void synchronize(Common::Serializer &s);
};
+#define IS_ROSE_TATTOO (_vm->getGameID() == GType_RoseTattoo)
+#define IS_SERRATED_SCALPEL (_vm->getGameID() == GType_SerratedScalpel)
+
} // End of namespace Sherlock
#endif
diff --git a/engines/sherlock/sound.cpp b/engines/sherlock/sound.cpp
index a923cf9e57..e7f4fe8a9a 100644
--- a/engines/sherlock/sound.cpp
+++ b/engines/sherlock/sound.cpp
@@ -58,18 +58,27 @@ Sound::Sound(SherlockEngine *vm, Audio::Mixer *mixer) : _vm(vm), _mixer(mixer) {
_soundIsOn = &_soundPlaying;
_curPriority = 0;
_digiBuf = nullptr;
+ _midiDrvLoaded = false;
+ _musicVolume = 0;
_soundOn = true;
_speechOn = true;
+ _vm->_res->addToCache("MUSIC.LIB");
if (!_vm->_interactiveFl)
_vm->_res->addToCache("TITLE.SND");
else {
- _vm->_res->addToCache("SND.SND");
+ _vm->_res->addToCache("MUSIC.LIB");
+
+ if (IS_ROSE_TATTOO) {
+ _vm->_res->addToCache("SOUND.LIB");
+ } else {
+ _vm->_res->addToCache("SND.SND");
- if (!_vm->isDemo()) {
- _vm->_res->addToCache("TITLE.SND");
- _vm->_res->addToCache("EPILOGUE.SND");
+ if (!_vm->isDemo()) {
+ _vm->_res->addToCache("TITLE.SND");
+ _vm->_res->addToCache("EPILOGUE.SND");
+ }
}
}
}
@@ -210,5 +219,9 @@ void Sound::freeDigiSound() {
_soundPlaying = false;
}
+void Sound::setMIDIVolume(int volume) {
+ // TODO
+}
+
} // End of namespace Sherlock
diff --git a/engines/sherlock/sound.h b/engines/sherlock/sound.h
index 2351bebd6e..e1c0777763 100644
--- a/engines/sherlock/sound.h
+++ b/engines/sherlock/sound.h
@@ -56,6 +56,9 @@ public:
bool _soundPlaying;
bool *_soundIsOn;
byte *_digiBuf;
+ bool _midiDrvLoaded;
+ Common::String _currentSongName, _nextSongName;
+ int _musicVolume;
public:
Sound(SherlockEngine *vm, Audio::Mixer *mixer);
@@ -91,6 +94,7 @@ public:
void stopSndFuncPtr(int v1, int v2);
void freeDigiSound();
+ void setMIDIVolume(int volume);
};
} // End of namespace Sherlock
diff --git a/engines/sherlock/surface.cpp b/engines/sherlock/surface.cpp
index 80495a398c..5a9e59e01b 100644
--- a/engines/sherlock/surface.cpp
+++ b/engines/sherlock/surface.cpp
@@ -96,6 +96,12 @@ void Surface::transBlitFrom(const ImageFrame &src, const Common::Point &pt,
transBlitFrom(src._frame, pt + src._offset, flipped, overrideColor);
}
+void Surface::transBlitFrom(const Surface &src, const Common::Point &pt,
+ bool flipped, int overrideColor) {
+ const Graphics::Surface &s = src._surface;
+ transBlitFrom(s, pt, flipped, overrideColor);
+}
+
void Surface::transBlitFrom(const Graphics::Surface &src, const Common::Point &pt,
bool flipped, int overrideColor) {
Common::Rect drawRect(0, 0, src.w, src.h);
@@ -186,4 +192,10 @@ void Surface::setPixels(byte *pixels, int width, int height) {
_surface.setPixels(pixels);
}
+void Surface::maskArea(const ImageFrame &src, const Common::Point &pt, int scrollX) {
+ // TODO
+ error("TODO: maskArea");
+}
+
+
} // End of namespace Sherlock
diff --git a/engines/sherlock/surface.h b/engines/sherlock/surface.h
index ccabf02a23..663f87f37f 100644
--- a/engines/sherlock/surface.h
+++ b/engines/sherlock/surface.h
@@ -126,6 +126,8 @@ public:
*/
void fillRect(const Common::Rect &r, byte color);
+ void maskArea(const ImageFrame &src, const Common::Point &pt, int scrollX);
+
/**
* Clear the screen
*/
diff --git a/engines/sherlock/talk.cpp b/engines/sherlock/talk.cpp
index 319d1a383f..dd6e776082 100644
--- a/engines/sherlock/talk.cpp
+++ b/engines/sherlock/talk.cpp
@@ -23,11 +23,153 @@
#include "sherlock/talk.h"
#include "sherlock/sherlock.h"
#include "sherlock/screen.h"
+#include "sherlock/scalpel/scalpel_user_interface.h"
namespace Sherlock {
#define SPEAKER_REMOVE 0x80
+const byte SCALPEL_OPCODES[] = {
+ 128, // OP_SWITCH_SPEAKER
+ 129, // OP_RUN_CANIMATION
+ 130, // OP_ASSIGN_PORTRAIT_LOCATION
+ 131, // OP_PAUSE
+ 132, // OP_REMOVE_PORTRAIT
+ 133, // OP_CLEAR_WINDOW
+ 134, // OP_ADJUST_OBJ_SEQUENCE
+ 135, // OP_WALK_TO_COORDS
+ 136, // OP_PAUSE_WITHOUT_CONTROL
+ 137, // OP_BANISH_WINDOW
+ 138, // OP_SUMMON_WINDOW
+ 139, // OP_SET_FLAG
+ 140, // OP_SFX_COMMAND
+ 141, // OP_TOGGLE_OBJECT
+ 142, // OP_STEALTH_MODE_ACTIVE
+ 143, // OP_IF_STATEMENT
+ 144, // OP_ELSE_STATEMENT
+ 145, // OP_END_IF_STATEMENT
+ 146, // OP_STEALTH_MODE_DEACTIVATE
+ 147, // OP_TURN_HOLMES_OFF
+ 148, // OP_TURN_HOLMES_ON
+ 149, // OP_GOTO_SCENE
+ 150, // OP_PLAY_PROLOGUE
+ 151, // OP_ADD_ITEM_TO_INVENTORY
+ 152, // OP_SET_OBJECT
+ 153, // OP_CALL_TALK_FILE
+ 143, // OP_MOVE_MOUSE
+ 155, // OP_DISPLAY_INFO_LINE
+ 156, // OP_CLEAR_INFO_LINE
+ 157, // OP_WALK_TO_CANIMATION
+ 158, // OP_REMOVE_ITEM_FROM_INVENTORY
+ 159, // OP_ENABLE_END_KEY
+ 160, // OP_DISABLE_END_KEY
+ 161, // OP_CARRIAGE_RETURN
+ 0, // OP_MOUSE_ON_OFF
+ 0, // OP_SET_WALK_CONTROL
+ 0, // OP_SET_TALK_SEQUENCE
+ 0, // OP_PLAY_SONG
+ 0, // OP_WALK_HOLMES_AND_NPC_TO_CANIM
+ 0, // OP_SET_NPC_PATH_DEST
+ 0, // OP_NEXT_SONG
+ 0, // OP_SET_NPC_PATH_PAUSE
+ 0, // OP_PASSWORD
+ 0, // OP_SET_SCENE_ENTRY_FLAG
+ 0, // OP_WALK_NPC_TO_CANIM
+ 0, // OP_WALK_HOLMES_AND_NPC_TO_COORDS
+ 0, // OP_WALK_HOLMES_AND_NPC_TO_COORDS
+ 0, // OP_SET_NPC_TALK_FILE
+ 0, // OP_TURN_NPC_OFF
+ 0, // OP_TURN_NPC_ON
+ 0, // OP_NPC_DESC_ON_OFF
+ 0, // OP_NPC_PATH_PAUSE_TAKING_NOTES
+ 0, // OP_NPC_PATH_PAUSE_LOOKING_HOLMES
+ 0, // OP_ENABLE_TALK_INTERRUPTS
+ 0, // OP_DISABLE_TALK_INTERRUPTS
+ 0, // OP_SET_NPC_INFO_LINE
+ 0, // OP_SET_NPC_POSITION
+ 0, // OP_NPC_PATH_LABEL
+ 0, // OP_PATH_GOTO_LABEL
+ 0, // OP_PATH_IF_FLAG_GOTO_LABEL
+ 0, // OP_NPC_WALK_GRAPHICS
+ 0, // OP_NPC_VERB
+ 0, // OP_NPC_VERB_CANIM
+ 0, // OP_NPC_VERB_SCRIPT
+ 0, // OP_RESTORE_PEOPLE_SEQUENCE
+ 0, // OP_NPC_VERB_TARGET
+ 0 // OP_TURN_SOUNDS_OFF
+};
+
+const byte TATTOO_OPCODES[] = {
+ 170, // OP_SWITCH_SPEAKER
+ 171, // OP_RUN_CANIMATION
+ 0, // OP_ASSIGN_PORTRAIT_LOCATION
+ 173, // OP_PAUSE
+ 0, // OP_REMOVE_PORTRAIT
+ 0, // OP_CLEAR_WINDOW
+ 176, // OP_ADJUST_OBJ_SEQUENCE
+ 177, // OP_WALK_TO_COORDS
+ 178, // OP_PAUSE_WITHOUT_CONTROL
+ 179, // OP_BANISH_WINDOW
+ 0, // OP_SUMMON_WINDOW
+ 181, // OP_SET_FLAG
+ 0, // OP_SFX_COMMAND
+ 183, // OP_TOGGLE_OBJECT
+ 184, // OP_STEALTH_MODE_ACTIVE
+ 0, // OP_IF_STATEMENT
+ 0, // OP_ELSE_STATEMENT
+ 0, // OP_END_IF_STATEMENT
+ 188, // OP_STEALTH_MODE_DEACTIVATE
+ 189, // OP_TURN_HOLMES_OFF
+ 190, // OP_TURN_HOLMES_ON
+ 191, // OP_GOTO_SCENE
+ 0, // OP_PLAY_PROLOGUE
+ 193, // OP_ADD_ITEM_TO_INVENTORY
+ 194, // OP_SET_OBJECT
+ 172, // OP_CALL_TALK_FILE
+ 0, // OP_MOVE_MOUSE
+ 0, // OP_DISPLAY_INFO_LINE
+ 0, // OP_CLEAR_INFO_LINE
+ 199, // OP_WALK_TO_CANIMATION
+ 200, // OP_REMOVE_ITEM_FROM_INVENTORY
+ 201, // OP_ENABLE_END_KEY
+ 202, // OP_DISABLE_END_KEY
+ 0, // OP_CARRIAGE_RETURN
+ 174, // OP_MOUSE_ON_OFF
+ 175, // OP_SET_WALK_CONTROL
+ 180, // OP_SET_TALK_SEQUENCE
+ 182, // OP_PLAY_SONG
+ 187, // OP_WALK_HOLMES_AND_NPC_TO_CANIM
+ 192, // OP_SET_NPC_PATH_DEST
+ 195, // OP_NEXT_SONG
+ 196, // OP_SET_NPC_PATH_PAUSE
+ 197, // OP_PASSWORD
+ 198, // OP_SET_SCENE_ENTRY_FLAG
+ 185, // OP_WALK_NPC_TO_CANIM
+ 204, // OP_WALK_HOLMES_AND_NPC_TO_COORDS
+ 205, // OP_SET_NPC_TALK_FILE
+ 206, // OP_TURN_NPC_OFF
+ 207, // OP_TURN_NPC_ON
+ 208, // OP_NPC_DESC_ON_OFF
+ 209, // OP_NPC_PATH_PAUSE_TAKING_NOTES
+ 210, // OP_NPC_PATH_PAUSE_LOOKING_HOLMES
+ 211, // OP_ENABLE_TALK_INTERRUPTS
+ 212, // OP_DISABLE_TALK_INTERRUPTS
+ 213, // OP_SET_NPC_INFO_LINE
+ 214, // OP_SET_NPC_POSITION
+ 215, // OP_NPC_PATH_LABEL
+ 216, // OP_PATH_GOTO_LABEL
+ 217, // OP_PATH_IF_FLAG_GOTO_LABEL
+ 218, // OP_NPC_WALK_GRAPHICS
+ 220, // OP_NPC_VERB
+ 221, // OP_NPC_VERB_CANIM
+ 222, // OP_NPC_VERB_SCRIPT
+ 224, // OP_RESTORE_PEOPLE_SEQUENCE
+ 226, // OP_NPC_VERB_TARGET
+ 227 // OP_TURN_SOUNDS_OFF
+};
+
+/*----------------------------------------------------------------*/
+
SequenceEntry::SequenceEntry() {
_objNum = 0;
_frameNumber = 0;
@@ -90,6 +232,13 @@ void TalkSequences::clear() {
/*----------------------------------------------------------------*/
+Talk *Talk::init(SherlockEngine *vm) {
+ if (vm->getGameID() == GType_SerratedScalpel)
+ return new ScalpelTalk(vm);
+ else
+ return new TattooTalk(vm);
+}
+
Talk::Talk(SherlockEngine *vm) : _vm(vm) {
_talkCounter = 0;
_talkToAbort = false;
@@ -103,6 +252,15 @@ Talk::Talk(SherlockEngine *vm) : _vm(vm) {
_moreTalkDown = _moreTalkUp = false;
_scriptMoreFlag = 0;
_scriptSaveIndex = -1;
+ _opcodes = IS_SERRATED_SCALPEL ? SCALPEL_OPCODES : TATTOO_OPCODES;
+
+ _charCount = 0;
+ _line = 0;
+ _yp = 0;
+ _wait = 0;
+ _pauseFlag = false;
+ _seqCount = 0;
+ _scriptStart = _scriptEnd = nullptr;
}
void Talk::talkTo(const Common::String &filename) {
@@ -172,9 +330,11 @@ void Talk::talkTo(const Common::String &filename) {
while (!_sequenceStack.empty())
pullSequence();
- // Restore any pressed button
- if (!ui._windowOpen && savedMode != STD_MODE)
- ui.restoreButton((int)(savedMode - 1));
+ if (IS_SERRATED_SCALPEL) {
+ // Restore any pressed button
+ if (!ui._windowOpen && savedMode != STD_MODE)
+ ((Scalpel::ScalpelUserInterface *)_vm->_ui)->restoreButton((int)(savedMode - 1));
+ }
// Clear the ui counter so that anything displayed on the info line
// before the window was opened isn't cleared
@@ -388,7 +548,7 @@ void Talk::talkTo(const Common::String &filename) {
}
- ui._key = ui._oldKey = COMMANDS[TALK_MODE - 1];
+ ui._key = ui._oldKey = Scalpel::COMMANDS[TALK_MODE - 1];
ui._temp = ui._oldTemp = 0;
ui._menuMode = TALK_MODE;
_talkToFlag = 2;
@@ -570,7 +730,7 @@ void Talk::stripVoiceCommands() {
// Scan for an sound effect byte, which indicates to play a sound
for (uint idx = 0; idx < statement._reply.size(); ++idx) {
- if (statement._reply[idx] == (char)SFX_COMMAND) {
+ if (statement._reply[idx] == (char)_opcodes[OP_SFX_COMMAND]) {
// Replace instruction character with a space, and delete the
// rest of the name following it
statement._reply = Common::String(statement._reply.c_str(),
@@ -625,9 +785,9 @@ void Talk::drawInterface() {
screen.makeButton(Common::Rect(181, CONTROLS_Y, 221, CONTROLS_Y + 10),
200 - screen.stringWidth("Down") / 2, "Down");
} else {
- int strWidth = screen.stringWidth(PRESS_KEY_TO_CONTINUE);
+ int strWidth = screen.stringWidth(Scalpel::PRESS_KEY_TO_CONTINUE);
screen.makeButton(Common::Rect(46, CONTROLS_Y, 273, CONTROLS_Y + 10),
- 160 - strWidth / 2, PRESS_KEY_TO_CONTINUE);
+ 160 - strWidth / 2, Scalpel::PRESS_KEY_TO_CONTINUE);
screen.gPrint(Common::Point(160 - strWidth / 2, CONTROLS_Y), COMMAND_FOREGROUND, "P");
}
}
@@ -919,38 +1079,33 @@ void Talk::setStillSeq(int speaker) {
}
void Talk::doScript(const Common::String &script) {
- Animation &anim = *_vm->_animation;
- Events &events = *_vm->_events;
- Inventory &inv = *_vm->_inventory;
- Map &map = *_vm->_map;
People &people = *_vm->_people;
Scene &scene = *_vm->_scene;
Screen &screen = *_vm->_screen;
- Sound &sound = *_vm->_sound;
UserInterface &ui = *_vm->_ui;
- int wait = 0;
- bool pauseFlag = false;
- bool endStr = false;
- int yp = CONTROLS_Y + 12;
- int charCount = 0;
- int line = 0;
- bool noTextYet = true;
bool openTalkWindow = false;
- int seqCount;
_savedSequences.clear();
- const byte *scriptStart = (const byte *)script.c_str();
- const byte *scriptEnd = scriptStart + script.size();
- const byte *str = scriptStart;
+ _scriptStart = (const byte *)script.c_str();
+ _scriptEnd = _scriptStart + script.size();
+ const byte *str = _scriptStart;
+ _yp = CONTROLS_Y + 12;
+ _charCount = 0;
+ _line = 0;
+ _wait = 0;
+ _pauseFlag = false;
+ _seqCount = 0;
+ _noTextYet = true;
+ _endStr = false;
if (_scriptMoreFlag) {
_scriptMoreFlag = 0;
- str = scriptStart + _scriptSaveIndex;
+ str = _scriptStart + _scriptSaveIndex;
}
// Check if the script begins with a Stealh Mode Active command
- if (str[0] == STEALTH_MODE_ACTIVE || _talkStealth) {
+ if (str[0] == _opcodes[OP_STEALTH_MODE_ACTIVE] || _talkStealth) {
_talkStealth = 2;
_speaker |= SPEAKER_REMOVE;
} else {
@@ -958,7 +1113,7 @@ void Talk::doScript(const Common::String &script) {
ui.clearWindow();
// Need to switch speakers?
- if (str[0] == SWITCH_SPEAKER) {
+ if (str[0] == _opcodes[OP_SWITCH_SPEAKER]) {
_speaker = str[1] - 1;
str += 2;
pullSequence();
@@ -969,7 +1124,7 @@ void Talk::doScript(const Common::String &script) {
}
// Assign portrait location?
- if (str[0] == ASSIGN_PORTRAIT_LOCATION) {
+ if (str[0] == _opcodes[OP_ASSIGN_PORTRAIT_LOCATION]) {
switch (str[1] & 15) {
case 1:
people._portraitSide = 20;
@@ -991,7 +1146,7 @@ void Talk::doScript(const Common::String &script) {
}
// Remove portrait?
- if (str[0] == REMOVE_PORTRAIT) {
+ if (str[0] == _opcodes[OP_REMOVE_PORTRAIT]) {
_speaker = 255;
} else {
// Nope, so set the first speaker
@@ -1001,401 +1156,22 @@ void Talk::doScript(const Common::String &script) {
do {
Common::String tempString;
- wait = 0;
+ _wait = 0;
byte c = str[0];
if (!c) {
- endStr = true;
+ _endStr = true;
} else if (c == '{') {
// Start of comment, so skip over it
while (*str++ != '}')
;
- } else if (c >= SWITCH_SPEAKER) {
+ } else if (c >= 128 && c <= 227 && _opcodeTable[c - 128]) {
// Handle control code
- switch (c) {
- case SWITCH_SPEAKER:
- if (!(_speaker & SPEAKER_REMOVE))
- people.clearTalking();
- if (_talkToAbort)
- return;
-
- ui.clearWindow();
- yp = CONTROLS_Y + 12;
- charCount = line = 0;
-
- _speaker = *++str - 1;
- people.setTalking(_speaker);
- pullSequence();
- pushSequence(_speaker);
- setSequence(_speaker);
- break;
-
- case RUN_CANIMATION:
- ++str;
- scene.startCAnim((str[0] - 1) & 127, (str[0] & 0x80) ? -1 : 1);
- if (_talkToAbort)
- return;
-
- // Check if next character is changing side or changing portrait
- if (charCount && (str[1] == SWITCH_SPEAKER || str[1] == ASSIGN_PORTRAIT_LOCATION))
- wait = 1;
- break;
-
- case ASSIGN_PORTRAIT_LOCATION:
- ++str;
- switch (str[0] & 15) {
- case 1:
- people._portraitSide = 20;
- break;
- case 2:
- people._portraitSide = 220;
- break;
- case 3:
- people._portraitSide = 120;
- break;
- default:
- break;
- }
-
- if (str[0] > 15)
- people._speakerFlip = true;
- break;
-
- case PAUSE:
- // Pause
- charCount = *++str;
- wait = pauseFlag = true;
- break;
-
- case REMOVE_PORTRAIT:
- if (_speaker >= 0 && _speaker < SPEAKER_REMOVE)
- people.clearTalking();
- pullSequence();
- if (_talkToAbort)
- return;
-
- _speaker |= SPEAKER_REMOVE;
- break;
-
- case CLEAR_WINDOW:
- ui.clearWindow();
- yp = CONTROLS_Y + 12;
- charCount = line = 0;
- break;
-
- case ADJUST_OBJ_SEQUENCE:
- {
- // Get the name of the object to adjust
- ++str;
- for (int idx = 0; idx < (str[0] & 127); ++idx)
- tempString += str[idx + 2];
-
- // Scan for object
- int objId = -1;
- for (uint idx = 0; idx < scene._bgShapes.size(); ++idx) {
- if (tempString.equalsIgnoreCase(scene._bgShapes[idx]._name))
- objId = idx;
- }
- if (objId == -1)
- error("Could not find object %s to change", tempString.c_str());
-
- // Should the script be overwritten?
- if (str[0] > 0x80) {
- // Save the current sequence
- _savedSequences.push(SequenceEntry());
- SequenceEntry &seqEntry = _savedSequences.top();
- seqEntry._objNum = objId;
- seqEntry._seqTo = scene._bgShapes[objId]._seqTo;
- for (uint idx = 0; idx < scene._bgShapes[objId]._seqSize; ++idx)
- seqEntry._sequences.push_back(scene._bgShapes[objId]._sequences[idx]);
- }
-
- // Get number of bytes to change
- seqCount = str[1];
- str += (str[0] & 127) + 2;
-
- // Copy in the new sequence
- for (int idx = 0; idx < seqCount; ++idx, ++str)
- scene._bgShapes[objId]._sequences[idx] = str[0] - 1;
-
- // Reset object back to beginning of new sequence
- scene._bgShapes[objId]._frameNumber = 0;
+ switch ((this->*_opcodeTable[c - 128])(str)) {
+ case RET_EXIT:
+ return;
+ case RET_CONTINUE:
continue;
- }
-
- case WALK_TO_COORDS:
- ++str;
-
- people.walkToCoords(Common::Point(((str[0] - 1) * 256 + str[1] - 1) * 100,
- str[2] * 100), str[3] - 1);
- if (_talkToAbort)
- return;
-
- str += 3;
- break;
-
- case PAUSE_WITHOUT_CONTROL:
- ++str;
-
- for (int idx = 0; idx < (str[0] - 1); ++idx) {
- scene.doBgAnim();
- if (_talkToAbort)
- return;
-
- // Check for button press
- events.pollEvents();
- events.setButtonState();
- }
- break;
-
- case BANISH_WINDOW:
- if (!(_speaker & SPEAKER_REMOVE))
- people.clearTalking();
- pullSequence();
-
- if (_talkToAbort)
- return;
-
- _speaker |= SPEAKER_REMOVE;
- ui.banishWindow();
- ui._menuMode = TALK_MODE;
- noTextYet = true;
- break;
-
- case SUMMON_WINDOW:
- drawInterface();
- events._pressed = events._released = false;
- events.clearKeyboard();
- noTextYet = false;
-
- if (_speaker != -1) {
- screen.buttonPrint(Common::Point(119, CONTROLS_Y), COMMAND_NULL, false, "Exit");
- screen.buttonPrint(Common::Point(159, CONTROLS_Y), COMMAND_NULL, false, "Up");
- screen.buttonPrint(Common::Point(200, CONTROLS_Y), COMMAND_NULL, false, "Down");
- }
- break;
-
- case SET_FLAG: {
- ++str;
- int flag1 = (str[0] - 1) * 256 + str[1] - 1 - (str[1] == 1 ? 1 : 0);
- int flag = (flag1 & 0x3fff) * (flag1 >= 0x4000 ? -1 : 1);
- _vm->setFlags(flag);
- ++str;
- break;
- }
-
- case SFX_COMMAND:
- ++str;
- if (sound._voices) {
- for (int idx = 0; idx < 8 && str[idx] != '~'; ++idx)
- tempString += str[idx];
- sound.playSound(tempString, WAIT_RETURN_IMMEDIATELY);
-
- // Set voices to wait for more
- sound._voices = 2;
- sound._speechOn = (*sound._soundIsOn);
- }
-
- wait = 1;
- str += 7;
- break;
-
- case TOGGLE_OBJECT:
- ++str;
- for (int idx = 0; idx < str[0]; ++idx)
- tempString += str[idx + 1];
-
- scene.toggleObject(tempString);
- str += str[0];
- break;
-
- case STEALTH_MODE_ACTIVE:
- _talkStealth = 2;
- break;
-
- case IF_STATEMENT: {
- ++str;
- int flag = (str[0] - 1) * 256 + str[1] - 1 - (str[1] == 1 ? 1 : 0);
- ++str;
- wait = 0;
-
- bool result = flag < 0x8000;
- if (_vm->readFlags(flag & 0x7fff) != result) {
- do {
- ++str;
- } while (str[0] && str[0] != ELSE_STATEMENT && str[0] != END_IF_STATEMENT);
-
- if (!str[0])
- endStr = true;
- }
- break;
- }
-
- case ELSE_STATEMENT:
- // If this is encountered here, it means that a preceeding IF statement was found,
- // and evaluated to true. Now all the statements for the true block are finished,
- // so skip over the block of code that would have executed if the result was false
- wait = 0;
- do {
- ++str;
- } while (str[0] && str[0] != END_IF_STATEMENT);
- break;
-
- case STEALTH_MODE_DEACTIVATE:
- _talkStealth = 0;
- events.clearKeyboard();
- break;
-
- case TURN_HOLMES_OFF:
- people._holmesOn = false;
- break;
-
- case TURN_HOLMES_ON:
- people._holmesOn = true;
- break;
-
- case GOTO_SCENE:
- scene._goToScene = str[1] - 1;
-
- if (scene._goToScene != 100) {
- // Not going to the map overview
- map._oldCharPoint = scene._goToScene;
- map._overPos.x = map[scene._goToScene].x * 100 - 600;
- map._overPos.y = map[scene._goToScene].y * 100 + 900;
-
- // Run a canimation?
- if (str[2] > 100) {
- people._hSavedFacing = str[2];
- people._hSavedPos = Common::Point(160, 100);
- }
- }
- str += 6;
-
- _scriptMoreFlag = (scene._goToScene == 100) ? 2 : 1;
- _scriptSaveIndex = str - scriptStart;
- endStr = true;
- wait = 0;
- break;
-
- case PLAY_PROLOGUE:
- ++str;
- for (int idx = 0; idx < 8 && str[idx] != '~'; ++idx)
- tempString += str[idx];
-
- anim.play(tempString, 1, 3, true, 4);
- break;
-
- case ADD_ITEM_TO_INVENTORY:
- ++str;
- for (int idx = 0; idx < str[0]; ++idx)
- tempString += str[idx + 1];
- str += str[0];
-
- inv.putNameInInventory(tempString);
- break;
-
- case SET_OBJECT: {
- ++str;
- for (int idx = 0; idx < (str[0] & 127); ++idx)
- tempString += str[idx + 1];
-
- // Set comparison state according to if we want to hide or unhide
- bool state = (str[0] >= SPEAKER_REMOVE);
- str += str[0] & 127;
-
- for (uint idx = 0; idx < scene._bgShapes.size(); ++idx) {
- Object &object = scene._bgShapes[idx];
- if (tempString.equalsIgnoreCase(object._name)) {
- // Only toggle the object if it's not in the desired state already
- if ((object._type == HIDDEN && state) || (object._type != HIDDEN && !state))
- object.toggleHidden();
- }
- }
- break;
- }
-
- case CALL_TALK_FILE: {
- ++str;
- for (int idx = 0; idx < 8 && str[idx] != '~'; ++idx)
- tempString += str[idx];
- str += 8;
-
- int scriptCurrentIndex = str - scriptStart;
-
- // Save the current script position and new talk file
- if (_scriptStack.size() < 9) {
- ScriptStackEntry rec1;
- rec1._name = _scriptName;
- rec1._currentIndex = scriptCurrentIndex;
- rec1._select = _scriptSelect;
- _scriptStack.push(rec1);
-
- // Push the new talk file onto the stack
- ScriptStackEntry rec2;
- rec2._name = tempString;
- rec2._currentIndex = 0;
- rec2._select = 100;
- _scriptStack.push(rec2);
- } else {
- error("Script stack overflow");
- }
-
- _scriptMoreFlag = 1;
- endStr = true;
- wait = 0;
- break;
- }
-
- case MOVE_MOUSE:
- ++str;
- events.moveMouse(Common::Point((str[0] - 1) * 256 + str[1] - 1, str[2]));
- if (_talkToAbort)
- return;
- str += 3;
- break;
-
- case DISPLAY_INFO_LINE:
- ++str;
- for (int idx = 0; idx < str[0]; ++idx)
- tempString += str[idx + 1];
- str += str[0];
-
- screen.print(Common::Point(0, INFO_LINE + 1), INFO_FOREGROUND, "%s", tempString.c_str());
- ui._menuCounter = 30;
- break;
-
- case CLEAR_INFO_LINE:
- ui._infoFlag = true;
- ui.clearInfo();
- break;
-
- case WALK_TO_CANIMATION: {
- ++str;
- CAnim &animation = scene._cAnim[str[0] - 1];
-
- people.walkToCoords(animation._goto, animation._gotoDir);
- if (_talkToAbort)
- return;
- break;
- }
-
- case REMOVE_ITEM_FROM_INVENTORY:
- ++str;
- for (int idx = 0; idx < str[0]; ++idx)
- tempString += str[idx + 1];
- str += str[0];
-
- inv.deleteItemFromInventory(tempString);
- break;
-
- case ENABLE_END_KEY:
- ui._endKeyActive = true;
- break;
-
- case DISABLE_END_KEY:
- ui._endKeyActive = false;
- break;
-
default:
break;
}
@@ -1403,8 +1179,8 @@ void Talk::doScript(const Common::String &script) {
++str;
} else {
// If the window isn't yet open, draw the window before printing starts
- if (!ui._windowOpen && noTextYet) {
- noTextYet = false;
+ if (!ui._windowOpen && _noTextYet) {
+ _noTextYet = false;
drawInterface();
if (_talkTo != -1) {
@@ -1415,19 +1191,19 @@ void Talk::doScript(const Common::String &script) {
}
// If it's the first line, display the speaker
- if (!line && _speaker >= 0 && _speaker < (int)people._characters.size()) {
+ if (!_line && _speaker >= 0 && _speaker < (int)people._characters.size()) {
// If the window is open, display the name directly on-screen.
// Otherwise, simply draw it on the back buffer
if (ui._windowOpen) {
- screen.print(Common::Point(16, yp), TALK_FOREGROUND, "%s",
+ screen.print(Common::Point(16, _yp), TALK_FOREGROUND, "%s",
people._characters[_speaker & 127]._name);
} else {
- screen.gPrint(Common::Point(16, yp - 1), TALK_FOREGROUND, "%s",
+ screen.gPrint(Common::Point(16, _yp - 1), TALK_FOREGROUND, "%s",
people._characters[_speaker & 127]._name);
openTalkWindow = true;
}
- yp += 9;
+ _yp += 9;
}
// Find amount of text that will fit on the line
@@ -1435,23 +1211,23 @@ void Talk::doScript(const Common::String &script) {
do {
width += screen.charWidth(str[idx]);
++idx;
- ++charCount;
- } while (width < 298 && str[idx] && str[idx] != '{' && str[idx] < SWITCH_SPEAKER);
+ ++_charCount;
+ } while (width < 298 && str[idx] && str[idx] != '{' && str[idx] < _opcodes[0]);
if (str[idx] || width >= 298) {
- if (str[idx] < SWITCH_SPEAKER && str[idx] != '{') {
+ if (str[idx] < _opcodes[0] && str[idx] != '{') {
--idx;
- --charCount;
+ --_charCount;
}
} else {
- endStr = true;
+ _endStr = true;
}
// If word wrap is needed, find the start of the current word
if (width >= 298) {
while (str[idx] != ' ') {
--idx;
- --charCount;
+ --_charCount;
}
}
@@ -1461,16 +1237,16 @@ void Talk::doScript(const Common::String &script) {
// If the speaker indicates a description file, print it in yellow
if (_speaker != -1) {
if (ui._windowOpen) {
- screen.print(Common::Point(16, yp), COMMAND_FOREGROUND, "%s", lineStr.c_str());
+ screen.print(Common::Point(16, _yp), COMMAND_FOREGROUND, "%s", lineStr.c_str());
} else {
- screen.gPrint(Common::Point(16, yp - 1), COMMAND_FOREGROUND, "%s", lineStr.c_str());
+ screen.gPrint(Common::Point(16, _yp - 1), COMMAND_FOREGROUND, "%s", lineStr.c_str());
openTalkWindow = true;
}
} else {
if (ui._windowOpen) {
- screen.print(Common::Point(16, yp), COMMAND_FOREGROUND, "%s", lineStr.c_str());
+ screen.print(Common::Point(16, _yp), COMMAND_FOREGROUND, "%s", lineStr.c_str());
} else {
- screen.gPrint(Common::Point(16, yp - 1), COMMAND_FOREGROUND, "%s", lineStr.c_str());
+ screen.gPrint(Common::Point(16, _yp - 1), COMMAND_FOREGROUND, "%s", lineStr.c_str());
openTalkWindow = true;
}
}
@@ -1479,37 +1255,27 @@ void Talk::doScript(const Common::String &script) {
str += idx;
// If line wrap occurred, then move to after the separating space between the words
- if (str[0] < SWITCH_SPEAKER && str[0] != '{')
+ if (str[0] < _opcodes[0] && str[0] != '{')
++str;
- yp += 9;
- ++line;
+ _yp += 9;
+ ++_line;
// Certain different conditions require a wait
- if ((line == 4 && str < scriptEnd && str[0] != SFX_COMMAND && str[0] != PAUSE && _speaker != -1) ||
- (line == 5 && str < scriptEnd && str[0] != PAUSE && _speaker == -1) ||
- endStr) {
- wait = 1;
+ if ((_line == 4 && str < _scriptEnd && str[0] != _opcodes[OP_SFX_COMMAND] && str[0] != _opcodes[OP_PAUSE] && _speaker != -1) ||
+ (_line == 5 && str < _scriptEnd && str[0] != _opcodes[OP_PAUSE] && _speaker == -1) ||
+ _endStr) {
+ _wait = 1;
}
- switch (str >= scriptEnd ? 0 : str[0]) {
- case SWITCH_SPEAKER:
- case ASSIGN_PORTRAIT_LOCATION:
- case BANISH_WINDOW:
- case IF_STATEMENT:
- case ELSE_STATEMENT:
- case END_IF_STATEMENT:
- case GOTO_SCENE:
- case CALL_TALK_FILE:
- wait = 1;
- break;
- default:
- break;
- }
+ byte v = (str >= _scriptEnd ? 0 : str[0]);
+ _wait = v == _opcodes[OP_SWITCH_SPEAKER] || v == _opcodes[OP_ASSIGN_PORTRAIT_LOCATION] ||
+ v == _opcodes[OP_BANISH_WINDOW] || _opcodes[OP_IF_STATEMENT] || v == _opcodes[OP_ELSE_STATEMENT] ||
+ v == _opcodes[OP_END_IF_STATEMENT] || v == _opcodes[OP_GOTO_SCENE] || v == _opcodes[OP_CALL_TALK_FILE];
}
// Open window if it wasn't already open, and text has already been printed
- if ((openTalkWindow && wait) || (openTalkWindow && str[0] >= SWITCH_SPEAKER && str[0] != CARRIAGE_RETURN)) {
+ if ((openTalkWindow && _wait) || (openTalkWindow && str[0] >= _opcodes[0] && str[0] != _opcodes[OP_CARRIAGE_RETURN])) {
if (!ui._slideWindows) {
screen.slamRect(Common::Rect(0, CONTROLS_Y, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT));
} else {
@@ -1520,34 +1286,34 @@ void Talk::doScript(const Common::String &script) {
openTalkWindow = false;
}
- if (wait) {
+ if (_wait) {
// Handling pausing
- if (!pauseFlag && charCount < 160)
- charCount = 160;
+ if (!_pauseFlag && _charCount < 160)
+ _charCount = 160;
- wait = waitForMore(charCount);
- if (wait == -1)
- endStr = true;
+ _wait = waitForMore(_charCount);
+ if (_wait == -1)
+ _endStr = true;
// If a key was pressed to finish the window, see if further voice files should be skipped
- if (wait >= 0 && wait < 254) {
- if (str[0] == SFX_COMMAND)
+ if (_wait >= 0 && _wait < 254) {
+ if (str[0] == _opcodes[OP_SFX_COMMAND])
str += 9;
}
// Clear the window unless the wait was due to a PAUSE command
- if (!pauseFlag && wait != -1 && str < scriptEnd && str[0] != SFX_COMMAND) {
+ if (!_pauseFlag && _wait != -1 && str < _scriptEnd && str[0] != _opcodes[OP_SFX_COMMAND]) {
if (!_talkStealth)
ui.clearWindow();
- yp = CONTROLS_Y + 12;
- charCount = line = 0;
+ _yp = CONTROLS_Y + 12;
+ _charCount = _line = 0;
}
- pauseFlag = false;
+ _pauseFlag = false;
}
- } while (!_vm->shouldQuit() && !endStr);
+ } while (!_vm->shouldQuit() && !_endStr);
- if (wait != -1) {
+ if (_wait != -1) {
for (int ssIndex = 0; ssIndex < (int)_savedSequences.size(); ++ssIndex) {
SequenceEntry &seq = _savedSequences[ssIndex];
Object &object = scene._bgShapes[seq._objNum];
@@ -1662,4 +1428,704 @@ void Talk::synchronize(Common::Serializer &s) {
}
}
+OpcodeReturn Talk::cmdAddItemToInventory(const byte *&str) {
+ Inventory &inv = *_vm->_inventory;
+ Common::String tempString;
+
+ ++str;
+ for (int idx = 0; idx < str[0]; ++idx)
+ tempString += str[idx + 1];
+ str += str[0];
+
+ inv.putNameInInventory(tempString);
+ return RET_SUCCESS;
+}
+
+OpcodeReturn Talk::cmdAdjustObjectSequence(const byte *&str) {
+ Scene &scene = *_vm->_scene;
+ Common::String tempString;
+
+ // Get the name of the object to adjust
+ ++str;
+ for (int idx = 0; idx < (str[0] & 127); ++idx)
+ tempString += str[idx + 2];
+
+ // Scan for object
+ int objId = -1;
+ for (uint idx = 0; idx < scene._bgShapes.size(); ++idx) {
+ if (tempString.equalsIgnoreCase(scene._bgShapes[idx]._name))
+ objId = idx;
+ }
+ if (objId == -1)
+ error("Could not find object %s to change", tempString.c_str());
+
+ // Should the script be overwritten?
+ if (str[0] > 0x80) {
+ // Save the current sequence
+ _savedSequences.push(SequenceEntry());
+ SequenceEntry &seqEntry = _savedSequences.top();
+ seqEntry._objNum = objId;
+ seqEntry._seqTo = scene._bgShapes[objId]._seqTo;
+ for (uint idx = 0; idx < scene._bgShapes[objId]._seqSize; ++idx)
+ seqEntry._sequences.push_back(scene._bgShapes[objId]._sequences[idx]);
+ }
+
+ // Get number of bytes to change
+ _seqCount = str[1];
+ str += (str[0] & 127) + 2;
+
+ // Copy in the new sequence
+ for (int idx = 0; idx < _seqCount; ++idx, ++str)
+ scene._bgShapes[objId]._sequences[idx] = str[0] - 1;
+
+ // Reset object back to beginning of new sequence
+ scene._bgShapes[objId]._frameNumber = 0;
+
+ return RET_CONTINUE;
+}
+
+OpcodeReturn Talk::cmdBanishWindow(const byte *&str) {
+ People &people = *_vm->_people;
+ UserInterface &ui = *_vm->_ui;
+
+ if (!(_speaker & SPEAKER_REMOVE))
+ people.clearTalking();
+ pullSequence();
+
+ if (_talkToAbort)
+ return RET_EXIT;
+
+ _speaker |= SPEAKER_REMOVE;
+ ui.banishWindow();
+ ui._menuMode = TALK_MODE;
+ _noTextYet = true;
+
+ return RET_SUCCESS;
+}
+
+OpcodeReturn Talk::cmdCallTalkFile(const byte *&str) {
+ Common::String tempString;
+
+ ++str;
+ for (int idx = 0; idx < 8 && str[idx] != '~'; ++idx)
+ tempString += str[idx];
+ str += 8;
+
+ int scriptCurrentIndex = str - _scriptStart;
+
+ // Save the current script position and new talk file
+ if (_scriptStack.size() < 9) {
+ ScriptStackEntry rec1;
+ rec1._name = _scriptName;
+ rec1._currentIndex = scriptCurrentIndex;
+ rec1._select = _scriptSelect;
+ _scriptStack.push(rec1);
+
+ // Push the new talk file onto the stack
+ ScriptStackEntry rec2;
+ rec2._name = tempString;
+ rec2._currentIndex = 0;
+ rec2._select = 100;
+ _scriptStack.push(rec2);
+ }
+ else {
+ error("Script stack overflow");
+ }
+
+ _scriptMoreFlag = 1;
+ _endStr = true;
+ _wait = 0;
+
+ return RET_SUCCESS;
+}
+
+OpcodeReturn Talk::cmdDisableEndKey(const byte *&str) {
+ _vm->_ui->_endKeyActive = false;
+ return RET_SUCCESS;
+}
+
+OpcodeReturn Talk::cmdEnableEndKey(const byte *&str) {
+ _vm->_ui->_endKeyActive = true;
+ return RET_SUCCESS;
+}
+
+OpcodeReturn Talk::cmdGotoScene(const byte *&str) {
+ Map &map = *_vm->_map;
+ People &people = *_vm->_people;
+ Scene &scene = *_vm->_scene;
+ scene._goToScene = str[1] - 1;
+
+ if (scene._goToScene != 100) {
+ // Not going to the map overview
+ map._oldCharPoint = scene._goToScene;
+ map._overPos.x = map[scene._goToScene].x * 100 - 600;
+ map._overPos.y = map[scene._goToScene].y * 100 + 900;
+
+ // Run a canimation?
+ if (str[2] > 100) {
+ people._hSavedFacing = str[2];
+ people._hSavedPos = Common::Point(160, 100);
+ }
+ }
+ str += 6;
+
+ _scriptMoreFlag = (scene._goToScene == 100) ? 2 : 1;
+ _scriptSaveIndex = str - _scriptStart;
+ _endStr = true;
+ _wait = 0;
+
+ return RET_SUCCESS;
+}
+
+OpcodeReturn Talk::cmdHolmesOff(const byte *&str) {
+ People &people = *_vm->_people;
+ people[PLAYER]._type = REMOVE;
+
+ return RET_SUCCESS;
+}
+
+OpcodeReturn Talk::cmdHolmesOn(const byte *&str) {
+ People &people = *_vm->_people;
+ people[PLAYER]._type = CHARACTER;
+
+ return RET_SUCCESS;
+}
+
+OpcodeReturn Talk::cmdPause(const byte *&str) {
+ _charCount = *++str;
+ _wait = _pauseFlag = true;
+
+ return RET_SUCCESS;
+}
+
+OpcodeReturn Talk::cmdPauseWithoutControl(const byte *&str) {
+ Events &events = *_vm->_events;
+ Scene &scene = *_vm->_scene;
+ ++str;
+
+ for (int idx = 0; idx < (str[0] - 1); ++idx) {
+ scene.doBgAnim();
+ if (_talkToAbort)
+ return RET_EXIT;
+
+ // Check for button press
+ events.pollEvents();
+ events.setButtonState();
+ }
+
+ return RET_SUCCESS;
+}
+
+OpcodeReturn Talk::cmdRemoveItemFromInventory(const byte *&str) {
+ Inventory &inv = *_vm->_inventory;
+ Common::String tempString;
+
+ ++str;
+ for (int idx = 0; idx < str[0]; ++idx)
+ tempString += str[idx + 1];
+ str += str[0];
+
+ inv.deleteItemFromInventory(tempString);
+
+ return RET_SUCCESS;
+}
+
+OpcodeReturn Talk::cmdRunCAnimation(const byte *&str) {
+ Scene &scene = *_vm->_scene;
+
+ ++str;
+ scene.startCAnim((str[0] - 1) & 127, (str[0] & 0x80) ? -1 : 1);
+ if (_talkToAbort)
+ return RET_EXIT;
+
+ // Check if next character is changing side or changing portrait
+ if (_charCount && (str[1] == _opcodes[OP_SWITCH_SPEAKER] || str[1] == _opcodes[OP_ASSIGN_PORTRAIT_LOCATION]))
+ _wait = 1;
+
+ return RET_SUCCESS;
+}
+
+OpcodeReturn Talk::cmdSetFlag(const byte *&str) {
+ ++str;
+ int flag1 = (str[0] - 1) * 256 + str[1] - 1 - (str[1] == 1 ? 1 : 0);
+ int flag = (flag1 & 0x3fff) * (flag1 >= 0x4000 ? -1 : 1);
+ _vm->setFlags(flag);
+ ++str;
+
+ return RET_SUCCESS;
+}
+
+OpcodeReturn Talk::cmdSetObject(const byte *&str) {
+ Scene &scene = *_vm->_scene;
+ Common::String tempString;
+
+ ++str;
+ for (int idx = 0; idx < (str[0] & 127); ++idx)
+ tempString += str[idx + 1];
+
+ // Set comparison state according to if we want to hide or unhide
+ bool state = (str[0] >= SPEAKER_REMOVE);
+ str += str[0] & 127;
+
+ for (uint idx = 0; idx < scene._bgShapes.size(); ++idx) {
+ Object &object = scene._bgShapes[idx];
+ if (tempString.equalsIgnoreCase(object._name)) {
+ // Only toggle the object if it's not in the desired state already
+ if ((object._type == HIDDEN && state) || (object._type != HIDDEN && !state))
+ object.toggleHidden();
+ }
+ }
+
+ return RET_SUCCESS;
+}
+
+OpcodeReturn Talk::cmdStealthModeActivate(const byte *&str) {
+ _talkStealth = 2;
+ return RET_SUCCESS;
+}
+
+OpcodeReturn Talk::cmdStealthModeDeactivate(const byte *&str) {
+ Events &events = *_vm->_events;
+
+ _talkStealth = 0;
+ events.clearKeyboard();
+
+ return RET_SUCCESS;
+}
+
+OpcodeReturn Talk::cmdSwitchSpeaker(const byte *&str) {
+ People &people = *_vm->_people;
+ UserInterface &ui = *_vm->_ui;
+
+ if (!(_speaker & SPEAKER_REMOVE))
+ people.clearTalking();
+ if (_talkToAbort)
+ return RET_EXIT;
+
+ ui.clearWindow();
+ _yp = CONTROLS_Y + 12;
+ _charCount = _line = 0;
+
+ _speaker = *++str - 1;
+ people.setTalking(_speaker);
+ pullSequence();
+ pushSequence(_speaker);
+ setSequence(_speaker);
+
+ return RET_SUCCESS;
+}
+
+OpcodeReturn Talk::cmdToggleObject(const byte *&str) {
+ Scene &scene = *_vm->_scene;
+ Common::String tempString;
+
+ ++str;
+ for (int idx = 0; idx < str[0]; ++idx)
+ tempString += str[idx + 1];
+
+ scene.toggleObject(tempString);
+ str += str[0];
+
+ return RET_SUCCESS;
+}
+
+OpcodeReturn Talk::cmdWalkToCAnimation(const byte *&str) {
+ People &people = *_vm->_people;
+ Scene &scene = *_vm->_scene;
+
+ ++str;
+ CAnim &animation = scene._cAnim[str[0] - 1];
+ people.walkToCoords(animation._goto, animation._gotoDir);
+
+ return _talkToAbort ? RET_EXIT : RET_SUCCESS;
+}
+
+OpcodeReturn Talk::cmdWalkToCoords(const byte *&str) {
+ People &people = *_vm->_people;
+ ++str;
+
+ people.walkToCoords(Common::Point(((str[0] - 1) * 256 + str[1] - 1) * 100,
+ str[2] * 100), str[3] - 1);
+ if (_talkToAbort)
+ return RET_EXIT;
+
+ str += 3;
+ return RET_SUCCESS;
+}
+
+/*----------------------------------------------------------------*/
+
+ScalpelTalk::ScalpelTalk(SherlockEngine *vm) : Talk(vm) {
+ static OpcodeMethod OPCODE_METHODS[] = {
+ (OpcodeMethod)&ScalpelTalk::cmdSwitchSpeaker,
+ (OpcodeMethod)&ScalpelTalk::cmdRunCAnimation,
+ (OpcodeMethod)&ScalpelTalk::cmdAssignPortraitLocation,
+
+ (OpcodeMethod)&ScalpelTalk::cmdPause,
+ (OpcodeMethod)&ScalpelTalk::cmdRemovePortrait,
+ (OpcodeMethod)&ScalpelTalk::cmdClearWindow,
+ (OpcodeMethod)&ScalpelTalk::cmdAdjustObjectSequence,
+ (OpcodeMethod)&ScalpelTalk::cmdWalkToCoords,
+ (OpcodeMethod)&ScalpelTalk::cmdPauseWithoutControl,
+ (OpcodeMethod)&ScalpelTalk::cmdBanishWindow,
+ (OpcodeMethod)&ScalpelTalk::cmdSummonWindow,
+ (OpcodeMethod)&ScalpelTalk::cmdSetFlag,
+ (OpcodeMethod)&ScalpelTalk::cmdSfxCommand,
+
+ (OpcodeMethod)&ScalpelTalk::cmdToggleObject,
+ (OpcodeMethod)&ScalpelTalk::cmdStealthModeActivate,
+ (OpcodeMethod)&ScalpelTalk::cmdIf,
+ (OpcodeMethod)&ScalpelTalk::cmdElse,
+ nullptr,
+ (OpcodeMethod)&ScalpelTalk::cmdStealthModeDeactivate,
+ (OpcodeMethod)&ScalpelTalk::cmdHolmesOff,
+ (OpcodeMethod)&ScalpelTalk::cmdHolmesOn,
+ (OpcodeMethod)&ScalpelTalk::cmdGotoScene,
+ (OpcodeMethod)&ScalpelTalk::cmdPlayPrologue,
+
+ (OpcodeMethod)&ScalpelTalk::cmdAddItemToInventory,
+ (OpcodeMethod)&ScalpelTalk::cmdSetObject,
+ (OpcodeMethod)&ScalpelTalk::cmdCallTalkFile,
+ (OpcodeMethod)&ScalpelTalk::cmdMoveMouse,
+ (OpcodeMethod)&ScalpelTalk::cmdDisplayInfoLine,
+ (OpcodeMethod)&ScalpelTalk::cmdClearInfoLine,
+ (OpcodeMethod)&ScalpelTalk::cmdWalkToCAnimation,
+ (OpcodeMethod)&ScalpelTalk::cmdRemoveItemFromInventory,
+ (OpcodeMethod)&ScalpelTalk::cmdEnableEndKey,
+ (OpcodeMethod)&ScalpelTalk::cmdDisableEndKey,
+
+ (OpcodeMethod)&ScalpelTalk::cmdCarriageReturn,
+ nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr
+ };
+
+ _opcodeTable = OPCODE_METHODS;
+}
+
+OpcodeReturn ScalpelTalk::cmdAssignPortraitLocation(const byte *&str) {
+ People &people = *_vm->_people;
+
+ ++str;
+ switch (str[0] & 15) {
+ case 1:
+ people._portraitSide = 20;
+ break;
+ case 2:
+ people._portraitSide = 220;
+ break;
+ case 3:
+ people._portraitSide = 120;
+ break;
+ default:
+ break;
+ }
+
+ if (str[0] > 15)
+ people._speakerFlip = true;
+
+ return RET_SUCCESS;
+}
+
+OpcodeReturn ScalpelTalk::cmdClearInfoLine(const byte *&str) {
+ UserInterface &ui = *_vm->_ui;
+
+ ui._infoFlag = true;
+ ui.clearInfo();
+
+ return RET_SUCCESS;
+}
+
+OpcodeReturn ScalpelTalk::cmdClearWindow(const byte *&str) {
+ UserInterface &ui = *_vm->_ui;
+
+ ui.clearWindow();
+ _yp = CONTROLS_Y + 12;
+ _charCount = _line = 0;
+
+ return RET_SUCCESS;
+}
+
+OpcodeReturn ScalpelTalk::cmdDisplayInfoLine(const byte *&str) {
+ Screen &screen = *_vm->_screen;
+ UserInterface &ui = *_vm->_ui;
+ Common::String tempString;
+
+ ++str;
+ for (int idx = 0; idx < str[0]; ++idx)
+ tempString += str[idx + 1];
+ str += str[0];
+
+ screen.print(Common::Point(0, INFO_LINE + 1), INFO_FOREGROUND, "%s", tempString.c_str());
+ ui._menuCounter = 30;
+
+ return RET_SUCCESS;
+}
+
+OpcodeReturn ScalpelTalk::cmdElse(const byte *&str) {
+ // If this is encountered here, it means that a preceeding IF statement was found,
+ // and evaluated to true. Now all the statements for the true block are finished,
+ // so skip over the block of code that would have executed if the result was false
+ _wait = 0;
+ do {
+ ++str;
+ } while (str[0] && str[0] != _opcodes[OP_END_IF_STATEMENT]);
+
+ return RET_SUCCESS;
+}
+
+OpcodeReturn ScalpelTalk::cmdIf(const byte *&str) {
+ ++str;
+ int flag = (str[0] - 1) * 256 + str[1] - 1 - (str[1] == 1 ? 1 : 0);
+ ++str;
+ _wait = 0;
+
+ bool result = flag < 0x8000;
+ if (_vm->readFlags(flag & 0x7fff) != result) {
+ do {
+ ++str;
+ } while (str[0] && str[0] != _opcodes[OP_ELSE_STATEMENT] && str[0] != _opcodes[OP_END_IF_STATEMENT]);
+
+ if (!str[0])
+ _endStr = true;
+ }
+
+ return RET_SUCCESS;
+}
+
+OpcodeReturn ScalpelTalk::cmdMoveMouse(const byte *&str) {
+ Events &events = *_vm->_events;
+
+ ++str;
+ events.moveMouse(Common::Point((str[0] - 1) * 256 + str[1] - 1, str[2]));
+ if (_talkToAbort)
+ return RET_EXIT;
+ str += 3;
+
+ return RET_SUCCESS;
+}
+
+OpcodeReturn ScalpelTalk::cmdPlayPrologue(const byte *&str) {
+ Animation &anim = *_vm->_animation;
+ Common::String tempString;
+
+ ++str;
+ for (int idx = 0; idx < 8 && str[idx] != '~'; ++idx)
+ tempString += str[idx];
+
+ anim.play(tempString, 1, 3, true, 4);
+
+ return RET_SUCCESS;
+}
+
+OpcodeReturn ScalpelTalk::cmdRemovePortrait(const byte *&str) {
+ People &people = *_vm->_people;
+
+ if (_speaker >= 0 && _speaker < SPEAKER_REMOVE)
+ people.clearTalking();
+ pullSequence();
+ if (_talkToAbort)
+ return RET_EXIT;
+
+ _speaker |= SPEAKER_REMOVE;
+ return RET_SUCCESS;
+}
+
+OpcodeReturn ScalpelTalk::cmdSfxCommand(const byte *&str) {
+ Sound &sound = *_vm->_sound;
+ Common::String tempString;
+
+ ++str;
+ if (sound._voices) {
+ for (int idx = 0; idx < 8 && str[idx] != '~'; ++idx)
+ tempString += str[idx];
+ sound.playSound(tempString, WAIT_RETURN_IMMEDIATELY);
+
+ // Set voices to wait for more
+ sound._voices = 2;
+ sound._speechOn = (*sound._soundIsOn);
+ }
+
+ _wait = 1;
+ str += 7;
+
+ return RET_SUCCESS;
+}
+
+OpcodeReturn ScalpelTalk::cmdSummonWindow(const byte *&str) {
+ Events &events = *_vm->_events;
+ Screen &screen = *_vm->_screen;
+
+ drawInterface();
+ events._pressed = events._released = false;
+ events.clearKeyboard();
+ _noTextYet = false;
+
+ if (_speaker != -1) {
+ screen.buttonPrint(Common::Point(119, CONTROLS_Y), COMMAND_NULL, false, "Exit");
+ screen.buttonPrint(Common::Point(159, CONTROLS_Y), COMMAND_NULL, false, "Up");
+ screen.buttonPrint(Common::Point(200, CONTROLS_Y), COMMAND_NULL, false, "Down");
+ }
+
+ return RET_SUCCESS;
+}
+
+OpcodeReturn ScalpelTalk::cmdCarriageReturn(const byte *&str) {
+ return RET_SUCCESS;
+}
+
+/*----------------------------------------------------------------*/
+
+TattooTalk::TattooTalk(SherlockEngine *vm) : Talk(vm) {
+ static OpcodeMethod OPCODE_METHODS[] = {
+ nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
+ (OpcodeMethod)&TattooTalk::cmdSwitchSpeaker,
+
+ (OpcodeMethod)&TattooTalk::cmdRunCAnimation,
+ (OpcodeMethod)&TattooTalk::cmdCallTalkFile,
+ (OpcodeMethod)&TattooTalk::cmdPause,
+ (OpcodeMethod)&TattooTalk::cmdMouseOnOff,
+ (OpcodeMethod)&TattooTalk::cmdSetWalkControl,
+ (OpcodeMethod)&TattooTalk::cmdAdjustObjectSequence,
+ (OpcodeMethod)&TattooTalk::cmdWalkToCoords,
+ (OpcodeMethod)&TattooTalk::cmdPauseWithoutControl,
+ (OpcodeMethod)&TattooTalk::cmdBanishWindow,
+ (OpcodeMethod)&TattooTalk::cmdSetTalkSequence,
+
+ (OpcodeMethod)&TattooTalk::cmdSetFlag,
+ (OpcodeMethod)&TattooTalk::cmdPlaySong,
+ (OpcodeMethod)&TattooTalk::cmdToggleObject,
+ (OpcodeMethod)&TattooTalk::cmdStealthModeActivate,
+ (OpcodeMethod)&TattooTalk::cmdWalkNPCToCAnimation,
+ (OpcodeMethod)&TattooTalk::cmdWalkNPCToCoords,
+ (OpcodeMethod)&TattooTalk::cmdWalkHomesAndNPCToCoords,
+ (OpcodeMethod)&TattooTalk::cmdStealthModeDeactivate,
+ (OpcodeMethod)&TattooTalk::cmdHolmesOff,
+ (OpcodeMethod)&TattooTalk::cmdHolmesOn,
+
+ (OpcodeMethod)&TattooTalk::cmdGotoScene,
+ (OpcodeMethod)&TattooTalk::cmdSetNPCPathDest,
+ (OpcodeMethod)&TattooTalk::cmdAddItemToInventory,
+ (OpcodeMethod)&TattooTalk::cmdSetObject,
+ (OpcodeMethod)&TattooTalk::cmdNextSong,
+ (OpcodeMethod)&TattooTalk::cmdSetNPCPathPause,
+ (OpcodeMethod)&TattooTalk::cmdPassword,
+ (OpcodeMethod)&TattooTalk::cmdSetSceneEntryFlag,
+ (OpcodeMethod)&TattooTalk::cmdWalkToCAnimation,
+ (OpcodeMethod)&TattooTalk::cmdRemoveItemFromInventory,
+
+ (OpcodeMethod)&TattooTalk::cmdEnableEndKey,
+ (OpcodeMethod)&TattooTalk::cmdDisableEndKey,
+ nullptr,
+ (OpcodeMethod)&TattooTalk::cmdWalkHomesAndNPCToCoords,
+ (OpcodeMethod)&TattooTalk::cmdSetNPCTalkFile,
+ (OpcodeMethod)&TattooTalk::cmdSetNPCOff,
+ (OpcodeMethod)&TattooTalk::cmdSetNPCOn,
+ (OpcodeMethod)&TattooTalk::cmdSetNPCDescOnOff,
+ (OpcodeMethod)&TattooTalk::cmdSetNPCPathPauseTakingNotes,
+ (OpcodeMethod)&TattooTalk::cmdSetNPCPathPauseLookingHolmes,
+
+ (OpcodeMethod)&TattooTalk::cmdTalkInterruptsEnable,
+ (OpcodeMethod)&TattooTalk::cmdTalkInterruptsDisable,
+ (OpcodeMethod)&TattooTalk::cmdSetNPCInfoLine,
+ (OpcodeMethod)&TattooTalk::cmdSetNPCPosition,
+ (OpcodeMethod)&TattooTalk::cmdNPCLabelSet,
+ (OpcodeMethod)&TattooTalk::cmdNPCLabelGoto,
+ (OpcodeMethod)&TattooTalk::cmdNPCLabelIfFlagGoto,
+ (OpcodeMethod)&TattooTalk::cmdSetNPCWalkGraphics,
+ nullptr,
+ (OpcodeMethod)&TattooTalk::cmdSetNPCVerb,
+
+ (OpcodeMethod)&TattooTalk::cmdSetNPCVerbCAnimation,
+ (OpcodeMethod)&TattooTalk::cmdSetNPCVerbScript,
+ nullptr,
+ (OpcodeMethod)&TattooTalk::cmdRestorePeopleSequence,
+ (OpcodeMethod)&TattooTalk::cmdSetNPCVerbTarget,
+ (OpcodeMethod)&TattooTalk::cmdTurnSoundsOff
+ };
+
+ _opcodeTable = OPCODE_METHODS;
+}
+
+OpcodeReturn TattooTalk::cmdMouseOnOff(const byte *&str) { error("TODO: script opcode"); }
+
+OpcodeReturn TattooTalk::cmdNextSong(const byte *&str) {
+ Sound &sound = *_vm->_sound;
+
+ // Get the name of the next song to play
+ ++str;
+ sound._nextSongName = "";
+ for (int idx = 0; idx < 8; ++idx) {
+ if (str[idx] != '~')
+ sound._nextSongName += str[idx];
+ else
+ break;
+ }
+ str += 7;
+
+ return RET_SUCCESS;
+}
+
+OpcodeReturn TattooTalk::cmdNPCLabelGoto(const byte *&str) { error("TODO: script opcode"); }
+OpcodeReturn TattooTalk::cmdNPCLabelIfFlagGoto(const byte *&str) { error("TODO: script opcode"); }
+OpcodeReturn TattooTalk::cmdNPCLabelSet(const byte *&str) { error("TODO: script opcode"); }
+OpcodeReturn TattooTalk::cmdPassword(const byte *&str) { error("TODO: script opcode"); }
+OpcodeReturn TattooTalk::cmdPlaySong(const byte *&str) { error("TODO: script opcode"); }
+OpcodeReturn TattooTalk::cmdRestorePeopleSequence(const byte *&str) { error("TODO: script opcode"); }
+OpcodeReturn TattooTalk::cmdSetNPCDescOnOff(const byte *&str) { error("TODO: script opcode"); }
+OpcodeReturn TattooTalk::cmdSetNPCInfoLine(const byte *&str) { error("TODO: script opcode"); }
+OpcodeReturn TattooTalk::cmdSetNPCOff(const byte *&str) { error("TODO: script opcode"); }
+OpcodeReturn TattooTalk::cmdSetNPCOn(const byte *&str) { error("TODO: script opcode"); }
+OpcodeReturn TattooTalk::cmdSetNPCPathDest(const byte *&str) { error("TODO: script opcode"); }
+OpcodeReturn TattooTalk::cmdSetNPCPathPause(const byte *&str) { error("TODO: script opcode"); }
+OpcodeReturn TattooTalk::cmdSetNPCPathPauseTakingNotes(const byte *&str) { error("TODO: script opcode"); }
+OpcodeReturn TattooTalk::cmdSetNPCPathPauseLookingHolmes(const byte *&str) { error("TODO: script opcode"); }
+OpcodeReturn TattooTalk::cmdSetNPCPosition(const byte *&str) { error("TODO: script opcode"); }
+OpcodeReturn TattooTalk::cmdSetNPCTalkFile(const byte *&str) { error("TODO: script opcode"); }
+OpcodeReturn TattooTalk::cmdSetNPCVerb(const byte *&str) { error("TODO: script opcode"); }
+OpcodeReturn TattooTalk::cmdSetNPCVerbCAnimation(const byte *&str) { error("TODO: script opcode"); }
+OpcodeReturn TattooTalk::cmdSetNPCVerbScript(const byte *&str) { error("TODO: script opcode"); }
+OpcodeReturn TattooTalk::cmdSetNPCVerbTarget(const byte *&str) { error("TODO: script opcode"); }
+
+OpcodeReturn TattooTalk::cmdSetNPCWalkGraphics(const byte *&str) {
+ ++str;
+ int npc = *str - 1;
+ People &people = *_vm->_people;
+ Person &person = people[npc];
+
+ // Build up walk library name for the given NPC
+ person._walkVGSName = "";
+ for (int idx = 0; idx < 8; ++idx) {
+ if (str[idx + 1] != '~')
+ person._walkVGSName += str[idx + 1];
+ else
+ break;
+ }
+ person._walkVGSName += ".VGS";
+
+ people._forceWalkReload = true;
+ str += 8;
+
+ return RET_SUCCESS;
+}
+
+OpcodeReturn TattooTalk::cmdSetSceneEntryFlag(const byte *&str) { error("TODO: script opcode"); }
+OpcodeReturn TattooTalk::cmdSetTalkSequence(const byte *&str) { error("TODO: script opcode"); }
+OpcodeReturn TattooTalk::cmdSetWalkControl(const byte *&str) { error("TODO: script opcode"); }
+OpcodeReturn TattooTalk::cmdTalkInterruptsDisable(const byte *&str) { error("TODO: script opcode"); }
+OpcodeReturn TattooTalk::cmdTalkInterruptsEnable(const byte *&str) { error("TODO: script opcode"); }
+OpcodeReturn TattooTalk::cmdTurnSoundsOff(const byte *&str) { error("TODO: script opcode"); }
+OpcodeReturn TattooTalk::cmdWalkHolmesAndNPCToCAnimation(const byte *&str) { error("TODO: script opcode"); }
+OpcodeReturn TattooTalk::cmdWalkNPCToCAnimation(const byte *&str) { error("TODO: script opcode"); }
+OpcodeReturn TattooTalk::cmdWalkNPCToCoords(const byte *&str) { error("TODO: script opcode"); }
+OpcodeReturn TattooTalk::cmdWalkHomesAndNPCToCoords(const byte *&str) { error("TODO: script opcode"); }
+
} // End of namespace Sherlock
diff --git a/engines/sherlock/talk.h b/engines/sherlock/talk.h
index d26259dbfd..a5f81f38ba 100644
--- a/engines/sherlock/talk.h
+++ b/engines/sherlock/talk.h
@@ -36,42 +36,83 @@ namespace Sherlock {
#define MAX_TALK_FILES 500
enum {
- SWITCH_SPEAKER = 128,
- RUN_CANIMATION = 129,
- ASSIGN_PORTRAIT_LOCATION = 130,
- PAUSE = 131,
- REMOVE_PORTRAIT = 132,
- CLEAR_WINDOW = 133,
- ADJUST_OBJ_SEQUENCE = 134,
- WALK_TO_COORDS = 135,
- PAUSE_WITHOUT_CONTROL = 136,
- BANISH_WINDOW = 137,
- SUMMON_WINDOW = 138,
- SET_FLAG = 139,
- SFX_COMMAND = 140,
- TOGGLE_OBJECT = 141,
- STEALTH_MODE_ACTIVE = 142,
- IF_STATEMENT = 143,
- ELSE_STATEMENT = 144,
- END_IF_STATEMENT = 145,
- STEALTH_MODE_DEACTIVATE = 146,
- TURN_HOLMES_OFF = 147,
- TURN_HOLMES_ON = 148,
- GOTO_SCENE = 149,
- PLAY_PROLOGUE = 150,
- ADD_ITEM_TO_INVENTORY = 151,
- SET_OBJECT = 152,
- CALL_TALK_FILE = 153,
- MOVE_MOUSE = 154,
- DISPLAY_INFO_LINE = 155,
- CLEAR_INFO_LINE = 156,
- WALK_TO_CANIMATION = 157,
- REMOVE_ITEM_FROM_INVENTORY = 158,
- ENABLE_END_KEY = 159,
- DISABLE_END_KEY = 160,
- CARRIAGE_RETURN = 161
+ OP_SWITCH_SPEAKER = 0,
+ OP_RUN_CANIMATION = 1,
+ OP_ASSIGN_PORTRAIT_LOCATION = 2,
+ OP_PAUSE = 3,
+ OP_REMOVE_PORTRAIT = 4,
+ OP_CLEAR_WINDOW = 5,
+ OP_ADJUST_OBJ_SEQUENCE = 6,
+ OP_WALK_TO_COORDS = 7,
+ OP_PAUSE_WITHOUT_CONTROL = 8,
+ OP_BANISH_WINDOW = 9,
+ OP_SUMMON_WINDOW = 10,
+ OP_SET_FLAG = 11,
+ OP_SFX_COMMAND = 12,
+ OP_TOGGLE_OBJECT = 13,
+ OP_STEALTH_MODE_ACTIVE = 14,
+ OP_IF_STATEMENT = 15,
+ OP_ELSE_STATEMENT = 16,
+ OP_END_IF_STATEMENT = 17,
+ OP_STEALTH_MODE_DEACTIVATE = 18,
+ OP_TURN_HOLMES_OFF = 19,
+ OP_TURN_HOLMES_ON = 20,
+ OP_GOTO_SCENE = 21,
+ OP_PLAY_PROLOGUE = 22,
+ OP_ADD_ITEM_TO_INVENTORY = 23,
+ OP_SET_OBJECT = 24,
+ OP_CALL_TALK_FILE = 25,
+ OP_MOVE_MOUSE = 26,
+ OP_DISPLAY_INFO_LINE = 27,
+ OP_CLEAR_INFO_LINE = 28,
+ OP_WALK_TO_CANIMATION = 29,
+ OP_REMOVE_ITEM_FROM_INVENTORY = 30,
+ OP_ENABLE_END_KEY = 31,
+ OP_DISABLE_END_KEY = 32,
+ OP_CARRIAGE_RETURN = 33,
+
+ OP_MOUSE_OFF_ON = 34,
+ OP_SET_WALK_CONTROL = 35,
+ OP_SET_TALK_SEQUENCE = 36,
+ OP_PLAY_SONG = 37,
+ OP_WALK_HOLMES_AND_NPC_TO_CANIM = 38,
+ OP_SET_NPC_PATH_DEST = 39,
+ OP_NEXT_SONG = 40,
+ OP_SET_NPC_PATH_PAUSE = 41,
+ OP_NEED_PASSWORD = 42,
+ OP_SET_SCENE_ENTRY_FLAG = 43,
+ OP_WALK_NPC_TO_CANIM = 44,
+ OP_WALK_HOLMES_AND_NPC_TO_COORDS = 45,
+ OP_SET_NPC_TALK_FILE = 46,
+ OP_TURN_NPC_OFF = 47,
+ OP_TURN_NPC_ON = 48,
+ OP_NPC_DESC_ON_OFF = 49,
+ OP_NPC_PATH_PAUSE_TAKING_NOTES = 50,
+ OP_NPC_PATH_PAUSE_LOOKING_HOLMES = 51,
+ OP_ENABLE_TALK_INTERRUPTS = 52,
+ OP_DISABLE_TALK_INTERRUPTS = 53,
+ OP_SET_NPC_INFO_LINE = 54,
+ OP_SET_NPC_POSITION = 54,
+ OP_NPC_PATH_LABEL = 55,
+ OP_PATH_GOTO_LABEL = 56,
+ OP_PATH_IF_FLAG_GOTO_LABEL = 57,
+ OP_NPC_WALK_GRAPHICS = 58,
+ OP_NPC_VERB = 59,
+ OP_NPC_VERB_CANIM = 60,
+ OP_NPC_VERB_SCRIPT = 61,
+ OP_RESTORE_PEOPLE_SEQUENCE = 62,
+ OP_NPC_VERB_TARGET = 63,
+ OP_TURN_SOUNDS_OFF = 64
};
+enum OpcodeReturn { RET_EXIT = -1, RET_SUCCESS = 0, RET_CONTINUE = 1 };
+
+class SherlockEngine;
+class Talk;
+namespace Scalpel { class ScalpelUserInterface; };
+
+typedef OpcodeReturn(Talk::*OpcodeMethod)(const byte *&str);
+
struct SequenceEntry {
int _objNum;
Common::Array<byte> _sequences;
@@ -122,24 +163,8 @@ struct TalkSequences {
void clear();
};
-class SherlockEngine;
-class UserInterface;
-
class Talk {
- friend class UserInterface;
-private:
- SherlockEngine *_vm;
- Common::Stack<SequenceEntry> _savedSequences;
- Common::Stack<SequenceEntry> _sequenceStack;
- Common::Stack<ScriptStackEntry> _scriptStack;
- Common::Array<Statement> _statements;
- TalkHistoryEntry _talkHistory[MAX_TALK_FILES];
- int _speaker;
- int _talkIndex;
- int _scriptSelect;
- int _talkStealth;
- int _talkToFlag;
- int _scriptSaveIndex;
+ friend class Scalpel::ScalpelUserInterface;
private:
/**
* Remove any voice commands from a loaded statement list
@@ -173,6 +198,55 @@ private:
* the amount of text that's been displayed
*/
int waitForMore(int delay);
+protected:
+ SherlockEngine *_vm;
+ OpcodeMethod *_opcodeTable;
+ Common::Stack<SequenceEntry> _savedSequences;
+ Common::Stack<SequenceEntry> _sequenceStack;
+ Common::Stack<ScriptStackEntry> _scriptStack;
+ Common::Array<Statement> _statements;
+ TalkHistoryEntry _talkHistory[MAX_TALK_FILES];
+ int _speaker;
+ int _talkIndex;
+ int _scriptSelect;
+ int _talkStealth;
+ int _talkToFlag;
+ int _scriptSaveIndex;
+
+ // These fields are used solely by doScript, but are fields because all the script opcodes are
+ // separate methods now, and need access to these fields
+ int _yp;
+ int _charCount;
+ int _line;
+ int _wait;
+ bool _pauseFlag;
+ bool _endStr, _noTextYet;
+ int _seqCount;
+ const byte *_scriptStart, *_scriptEnd;
+protected:
+ Talk(SherlockEngine *vm);
+
+ OpcodeReturn cmdAddItemToInventory(const byte *&str);
+ OpcodeReturn cmdAdjustObjectSequence(const byte *&str);
+ OpcodeReturn cmdBanishWindow(const byte *&str);
+ OpcodeReturn cmdCallTalkFile(const byte *&str);
+ OpcodeReturn cmdDisableEndKey(const byte *&str);
+ OpcodeReturn cmdEnableEndKey(const byte *&str);
+ OpcodeReturn cmdGotoScene(const byte *&str);
+ OpcodeReturn cmdHolmesOff(const byte *&str);
+ OpcodeReturn cmdHolmesOn(const byte *&str);
+ OpcodeReturn cmdPause(const byte *&str);
+ OpcodeReturn cmdPauseWithoutControl(const byte *&str);
+ OpcodeReturn cmdRemoveItemFromInventory(const byte *&str);
+ OpcodeReturn cmdRunCAnimation(const byte *&str);
+ OpcodeReturn cmdSetFlag(const byte *&str);
+ OpcodeReturn cmdSetObject(const byte *&str);
+ OpcodeReturn cmdStealthModeActivate(const byte *&str);
+ OpcodeReturn cmdStealthModeDeactivate(const byte *&str);
+ OpcodeReturn cmdSwitchSpeaker(const byte *&str);
+ OpcodeReturn cmdToggleObject(const byte *&str);
+ OpcodeReturn cmdWalkToCAnimation(const byte *&str);
+ OpcodeReturn cmdWalkToCoords(const byte *&str);
public:
bool _talkToAbort;
int _talkCounter;
@@ -181,8 +255,11 @@ public:
Common::String _scriptName;
bool _moreTalkUp, _moreTalkDown;
int _converseNum;
+ const byte *_opcodes;
+
public:
- Talk(SherlockEngine *vm);
+ static Talk *init(SherlockEngine *vm);
+ virtual ~Talk() {}
/**
* Return a given talk statement
@@ -267,6 +344,65 @@ public:
void synchronize(Common::Serializer &s);
};
+class ScalpelTalk : public Talk {
+protected:
+ OpcodeReturn cmdAssignPortraitLocation(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 cmdCarriageReturn(const byte *&str);
+public:
+ ScalpelTalk(SherlockEngine *vm);
+ virtual ~ScalpelTalk() {}
+};
+
+class TattooTalk : public Talk {
+protected:
+ OpcodeReturn cmdMouseOnOff(const byte *&str);
+ OpcodeReturn cmdNextSong(const byte *&str);
+ OpcodeReturn cmdPassword(const byte *&str);
+ OpcodeReturn cmdPlaySong(const byte *&str);
+ OpcodeReturn cmdRestorePeopleSequence(const byte *&str);
+ OpcodeReturn cmdSetNPCDescOnOff(const byte *&str);
+ OpcodeReturn cmdSetNPCInfoLine(const byte *&str);
+ OpcodeReturn cmdNPCLabelGoto(const byte *&str);
+ OpcodeReturn cmdNPCLabelIfFlagGoto(const byte *&str);
+ OpcodeReturn cmdNPCLabelSet(const byte *&str);
+ OpcodeReturn cmdSetNPCOff(const byte *&str);
+ OpcodeReturn cmdSetNPCOn(const byte *&str);
+ OpcodeReturn cmdSetNPCPathDest(const byte *&str);
+ OpcodeReturn cmdSetNPCPathPause(const byte *&str);
+ OpcodeReturn cmdSetNPCPathPauseTakingNotes(const byte *&str);
+ OpcodeReturn cmdSetNPCPathPauseLookingHolmes(const byte *&str);
+ OpcodeReturn cmdSetNPCPosition(const byte *&str);
+ OpcodeReturn cmdSetNPCTalkFile(const byte *&str);
+ OpcodeReturn cmdSetNPCVerb(const byte *&str);
+ OpcodeReturn cmdSetNPCVerbCAnimation(const byte *&str);
+ OpcodeReturn cmdSetNPCVerbScript(const byte *&str);
+ OpcodeReturn cmdSetNPCVerbTarget(const byte *&str);
+ OpcodeReturn cmdSetNPCWalkGraphics(const byte *&str);
+ OpcodeReturn cmdSetSceneEntryFlag(const byte *&str);
+ OpcodeReturn cmdSetTalkSequence(const byte *&str);
+ OpcodeReturn cmdSetWalkControl(const byte *&str);
+ OpcodeReturn cmdTalkInterruptsDisable(const byte *&str);
+ OpcodeReturn cmdTalkInterruptsEnable(const byte *&str);
+ OpcodeReturn cmdTurnSoundsOff(const byte *&str);
+ OpcodeReturn cmdWalkHolmesAndNPCToCAnimation(const byte *&str);
+ OpcodeReturn cmdWalkNPCToCAnimation(const byte *&str);
+ OpcodeReturn cmdWalkNPCToCoords(const byte *&str);
+ OpcodeReturn cmdWalkHomesAndNPCToCoords(const byte *&str);
+public:
+ TattooTalk(SherlockEngine *vm);
+ virtual ~TattooTalk() {}
+};
+
} // End of namespace Sherlock
#endif
diff --git a/engines/sherlock/tattoo/tattoo.cpp b/engines/sherlock/tattoo/tattoo.cpp
index d4059ac413..368b24bfcd 100644
--- a/engines/sherlock/tattoo/tattoo.cpp
+++ b/engines/sherlock/tattoo/tattoo.cpp
@@ -21,15 +21,61 @@
*/
#include "sherlock/tattoo/tattoo.h"
+#include "engines/util.h"
namespace Sherlock {
namespace Tattoo {
+TattooEngine::TattooEngine(OSystem *syst, const SherlockGameDescription *gameDesc) :
+ SherlockEngine(syst, gameDesc) {
+ _creditsActive = false;
+}
+
void TattooEngine::showOpening() {
// TODO
}
+void TattooEngine::initialize() {
+ initGraphics(640, 480, true);
+
+ // Initialize the base engine
+ SherlockEngine::initialize();
+
+ _flags.resize(100 * 8);
+
+ // Add some more files to the cache
+ _res->addToCache("walk.lib");
+
+ // Starting scene
+ _scene->_goToScene = 91;
+
+ // Load an initial palette
+ loadInitialPalette();
+}
+
+void TattooEngine::startScene() {
+ // TODO
+}
+
+void TattooEngine::loadInitialPalette() {
+ byte palette[768];
+ Common::SeekableReadStream *stream = _res->load("room.pal");
+ stream->read(palette, PALETTE_SIZE);
+ _screen->translatePalette(palette);
+ _screen->setPalette(palette);
+
+ delete stream;
+}
+
+void TattooEngine::drawCredits() {
+ // TODO
+}
+
+void TattooEngine::eraseCredits() {
+ // TODO
+}
+
} // End of namespace Tattoo
} // End of namespace Scalpel
diff --git a/engines/sherlock/tattoo/tattoo.h b/engines/sherlock/tattoo/tattoo.h
index b98395597c..bb6310dbe3 100644
--- a/engines/sherlock/tattoo/tattoo.h
+++ b/engines/sherlock/tattoo/tattoo.h
@@ -30,12 +30,38 @@ namespace Sherlock {
namespace Tattoo {
class TattooEngine : public SherlockEngine {
+private:
+ /**
+ * Loads the initial palette for the game
+ */
+ void loadInitialPalette();
protected:
+ /**
+ * Initialize the engine
+ */
+ virtual void initialize();
+
virtual void showOpening();
+
+ /**
+ * Starting a scene within the game
+ */
+ virtual void startScene();
+public:
+ bool _creditsActive;
public:
- TattooEngine(OSystem *syst, const SherlockGameDescription *gameDesc) :
- SherlockEngine(syst, gameDesc) {}
+ TattooEngine(OSystem *syst, const SherlockGameDescription *gameDesc);
virtual ~TattooEngine() {}
+
+ /**
+ * Draw credits on the screen
+ */
+ void drawCredits();
+
+ /**
+ * Erase any area of the screen covered by credits
+ */
+ void eraseCredits();
};
} // End of namespace Tattoo
diff --git a/engines/sherlock/tattoo/tattoo_scene.cpp b/engines/sherlock/tattoo/tattoo_scene.cpp
new file mode 100644
index 0000000000..dd96f3358e
--- /dev/null
+++ b/engines/sherlock/tattoo/tattoo_scene.cpp
@@ -0,0 +1,402 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "sherlock/tattoo/tattoo_scene.h"
+#include "sherlock/tattoo/tattoo.h"
+#include "sherlock/tattoo/tattoo_user_interface.h"
+#include "sherlock/events.h"
+#include "sherlock/people.h"
+
+namespace Sherlock {
+
+namespace Tattoo {
+
+TattooScene::TattooScene(SherlockEngine *vm) : Scene(vm) {
+ _arrowZone = -1;
+ _mask = _mask1 = nullptr;
+ _maskCounter = 0;
+}
+
+void TattooScene::checkBgShapes() {
+ People &people = *_vm->_people;
+ Person &holmes = people._player;
+ 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();
+
+ // Check for any active playing animation
+ if (_activeCAnim._images && _activeCAnim._zPlacement != REMOVE) {
+ switch (_activeCAnim._flags & 3) {
+ case 0:
+ _activeCAnim._zPlacement = BEHIND;
+ break;
+ case 1:
+ _activeCAnim._zPlacement = ((_activeCAnim._position.y + _activeCAnim._imageFrame->_frame.h - 1)) ?
+ NORMAL_FORWARD : NORMAL_BEHIND;
+ break;
+ case 2:
+ _activeCAnim._zPlacement = FORWARD;
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void TattooScene::doBgAnimCheckCursor() {
+ Events &events = *_vm->_events;
+ UserInterface &ui = *_vm->_ui;
+ Common::Point mousePos = events.mousePos();
+
+ // If we're in Look Mode, make sure the cursor is the magnifying glass
+ if (ui._menuMode == LOOK_MODE && events.getCursor() != MAGNIFY)
+ events.setCursor(MAGNIFY);
+
+ // See if the mouse is over any of the arrow zones, and if so, change the cursor to the correct
+ // arrow cursor indicating the direcetion of the exit
+ if (events.getCursor() == ARROW || events.getCursor() >= EXIT_ZONES_START) {
+ CursorId cursorId = ARROW;
+
+ if (ui._menuMode == STD_MODE && _arrowZone != -1 && _currentScene != 90) {
+ for (uint idx = 0; idx < _exits.size(); ++idx) {
+ Exit &exit = _exits[idx];
+ if (exit.contains(mousePos))
+ cursorId = (CursorId)(exit._image + EXIT_ZONES_START);
+ }
+ }
+
+ events.setCursor(cursorId);
+ }
+}
+
+void TattooScene::doBgAnimEraseBackground() {
+ TattooEngine &vm = *((TattooEngine *)_vm);
+ People &people = *_vm->_people;
+ Screen &screen = *_vm->_screen;
+ TattooUserInterface &ui = *((TattooUserInterface *)_vm->_ui);
+
+ static const int16 OFFSETS[16] = { -1, -2, -3, -3, -2, -1, -1, 0, 1, 2, 3, 3, 2, 1, 0, 0 };
+
+ if (_mask != nullptr) {
+ if (screen._backBuffer1.w() > screen.w())
+ screen.blitFrom(screen._backBuffer1, Common::Point(0, 0), Common::Rect(screen._currentScroll, 0,
+ screen._currentScroll + screen.w(), screen.h()));
+ else
+ screen.blitFrom(screen._backBuffer1);
+
+ switch (_currentScene) {
+ case 7:
+ if (++_maskCounter == 2) {
+ _maskCounter = 0;
+ if (--_maskOffset.x < 0)
+ _maskOffset.x = SHERLOCK_SCREEN_WIDTH - 1;
+ }
+ break;
+
+ case 8:
+ _maskOffset.x += 2;
+ if (_maskOffset.x >= SHERLOCK_SCREEN_WIDTH)
+ _maskOffset.x = 0;
+ break;
+
+ case 18:
+ case 68:
+ ++_maskCounter;
+ if (_maskCounter / 4 >= 16)
+ _maskCounter = 0;
+
+ _maskOffset.x = OFFSETS[_maskCounter / 4];
+ break;
+
+ case 53:
+ if (++_maskCounter == 2) {
+ _maskCounter = 0;
+ if (++_maskOffset.x == screen._backBuffer1.w())
+ _maskOffset.x = 0;
+ }
+ break;
+
+ default:
+ break;
+ }
+ } else {
+ // Standard scene without mask, so call user interface to erase any UI elements as necessary
+ ui.doBgAnimRestoreUI();
+
+ // Restore background for any areas covered by characters and shapes
+ for (uint idx = 0; idx < MAX_CHARACTERS; ++idx)
+ screen.restoreBackground(Common::Rect(people[idx]._oldPosition.x, people[idx]._oldPosition.y,
+ people[idx]._oldPosition.x + people[idx]._oldSize.x, people[idx]._oldPosition.y + people[idx]._oldSize.y));
+
+ for (uint idx = 0; idx < _bgShapes.size(); ++idx) {
+ Object &obj = _bgShapes[idx];
+
+ if ((obj._type == ACTIVE_BG_SHAPE && (obj._maxFrames > 1 || obj._delta.x != 0 || obj._delta.y != 0)) ||
+ obj._type == HIDE_SHAPE || obj._type == REMOVE)
+ screen._backBuffer1.blitFrom(*obj._imageFrame, obj._oldPosition,
+ Common::Rect(obj._oldPosition.x, obj._oldPosition.y, obj._oldPosition.x + obj._oldSize.x,
+ obj._oldPosition.y + obj._oldSize.y));
+ }
+
+ // If credits are active, erase the area they cover
+ if (vm._creditsActive)
+ vm.eraseCredits();
+ }
+
+ for (uint idx = 0; idx < _bgShapes.size(); ++idx) {
+ Object &obj = _bgShapes[idx];
+
+ if (obj._type == NO_SHAPE && (obj._flags & 1) == 0) {
+ screen._backBuffer1.blitFrom(screen._backBuffer2, obj._position, obj.getNoShapeBounds());
+
+ obj._oldPosition = obj._position;
+ obj._oldSize = obj._noShapeSize;
+ }
+ }
+
+ // Adjust the Target Scroll if needed
+ if ((people[people._walkControl]._position.x / FIXED_INT_MULTIPLIER - screen._currentScroll) <
+ (SHERLOCK_SCREEN_WIDTH / 8) && people[people._walkControl]._delta.x < 0) {
+
+ screen._targetScroll = (short)(people[people._walkControl]._position.x / FIXED_INT_MULTIPLIER -
+ SHERLOCK_SCREEN_WIDTH / 8 - 250);
+ if (screen._targetScroll < 0)
+ screen._targetScroll = 0;
+ }
+
+ if ((people[people._walkControl]._position.x / FIXED_INT_MULTIPLIER - screen._currentScroll) > (SHERLOCK_SCREEN_WIDTH / 4 * 3)
+ && people[people._walkControl]._delta.x > 0)
+ screen._targetScroll = (short)(people[people._walkControl]._position.x / FIXED_INT_MULTIPLIER -
+ SHERLOCK_SCREEN_WIDTH / 4 * 3 + 250);
+
+ if (screen._targetScroll > screen._scrollSize)
+ screen._targetScroll = screen._scrollSize;
+
+ ui.doScroll();
+}
+
+void TattooScene::doBgAnim() {
+ TattooUserInterface &ui = *((TattooUserInterface *)_vm->_ui);
+
+ doBgAnimCheckCursor();
+
+// Events &events = *_vm->_events;
+ People &people = *_vm->_people;
+// Scene &scene = *_vm->_scene;
+ Screen &screen = *_vm->_screen;
+ Talk &talk = *_vm->_talk;
+
+ screen.setDisplayBounds(Common::Rect(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCENE_HEIGHT));
+ talk._talkToAbort = false;
+
+ // Check the characters and sprites for updates
+ for (uint 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();
+ }
+
+ // Erase any affected background areas
+ doBgAnimEraseBackground();
+
+ doBgAnimUpdateBgObjectsAndAnim();
+
+ ui.drawInterface();
+}
+
+void TattooScene::doBgAnimUpdateBgObjectsAndAnim() {
+ TattooEngine &vm = *((TattooEngine *)_vm);
+ People &people = *_vm->_people;
+ Screen &screen = *_vm->_screen;
+
+ for (uint idx = 0; idx < _bgShapes.size(); ++idx) {
+ Object &obj = _bgShapes[idx];
+ if (obj._type == ACTIVE_BG_SHAPE || obj._type == NO_SHAPE)
+ obj.adjustObject();
+ }
+
+ for (uint idx = 0; idx < MAX_CHARACTERS; ++idx) {
+ if (people[idx]._type == CHARACTER)
+ people[idx].adjustSprite();
+ }
+
+ if (_activeCAnim._images != nullptr != _activeCAnim._zPlacement != REMOVE) {
+ _activeCAnim.getNextFrame();
+ }
+
+ // Flag the bg shapes which need to be redrawn
+ checkBgShapes();
+ drawAllShapes();
+
+
+ if (_mask != nullptr) {
+ switch (_currentScene) {
+ case 7:
+ screen._backBuffer1.maskArea((*_mask)[0], Common::Point(_maskOffset.x - SHERLOCK_SCREEN_WIDTH, 110), screen._currentScroll);
+ screen._backBuffer1.maskArea((*_mask)[0], Common::Point(_maskOffset.x, 110), screen._currentScroll);
+ screen._backBuffer1.maskArea((*_mask)[0], Common::Point(_maskOffset.x + SHERLOCK_SCREEN_WIDTH, 110), screen._currentScroll);
+ break;
+
+ case 8:
+ screen._backBuffer1.maskArea((*_mask)[0], Common::Point(_maskOffset.x - SHERLOCK_SCREEN_WIDTH, 180), screen._currentScroll);
+ screen._backBuffer1.maskArea((*_mask)[0], Common::Point(_maskOffset.x, 180), screen._currentScroll);
+ screen._backBuffer1.maskArea((*_mask)[0], Common::Point(_maskOffset.x + SHERLOCK_SCREEN_WIDTH, 180), screen._currentScroll);
+ if (!_vm->readFlags(880))
+ screen._backBuffer1.maskArea((*_mask1)[0], Common::Point(940, 300), screen._currentScroll);
+ break;
+
+ case 18:
+ screen._backBuffer1.maskArea((*_mask)[0], Common::Point(_maskOffset.x, 203), screen._currentScroll);
+ if (!_vm->readFlags(189))
+ screen._backBuffer1.maskArea((*_mask1)[0], Common::Point(124 + _maskOffset.x, 239), screen._currentScroll);
+ break;
+
+ case 53:
+ screen._backBuffer1.maskArea((*_mask)[0], Common::Point(_maskOffset.x, 110), screen._currentScroll);
+ screen._backBuffer1.maskArea((*_mask)[0], Common::Point(_maskOffset.x - SHERLOCK_SCREEN_WIDTH, 110), screen._currentScroll);
+ break;
+
+ case 68:
+ screen._backBuffer1.maskArea((*_mask)[0], Common::Point(_maskOffset.x, 203), screen._currentScroll);
+ screen._backBuffer1.maskArea((*_mask1)[0], Common::Point(124 + _maskOffset.x, 239), screen._currentScroll);
+ break;
+ }
+ }
+}
+
+
+void TattooScene::updateBackground() {
+ People &people = *_vm->_people;
+ Screen &screen = *_vm->_screen;
+
+ Scene::updateBackground();
+
+ if (_mask != nullptr) {
+ switch (_currentScene) {
+ case 7:
+ screen._backBuffer1.maskArea((*_mask)[0], Common::Point(_maskOffset.x - SHERLOCK_SCREEN_WIDTH, 110), screen._currentScroll);
+ screen._backBuffer1.maskArea((*_mask)[0], Common::Point(_maskOffset.x, 110), screen._currentScroll);
+ screen._backBuffer1.maskArea((*_mask)[0], Common::Point(_maskOffset.x + SHERLOCK_SCREEN_WIDTH, 110), screen._currentScroll);
+ break;
+
+ case 8:
+ screen._backBuffer1.maskArea((*_mask)[0], Common::Point(_maskOffset.x - SHERLOCK_SCREEN_WIDTH, 180), screen._currentScroll);
+ screen._backBuffer1.maskArea((*_mask)[0], Common::Point(_maskOffset.x, 180), screen._currentScroll);
+ screen._backBuffer1.maskArea((*_mask)[0], Common::Point(_maskOffset.x + SHERLOCK_SCREEN_WIDTH, 180), screen._currentScroll);
+ if (!_vm->readFlags(880))
+ screen._backBuffer1.maskArea((*_mask1)[0], Common::Point(940, 300), screen._currentScroll);
+ break;
+
+ case 18:
+ screen._backBuffer1.maskArea((*_mask)[0], Common::Point(0, 203), screen._currentScroll);
+ if (!_vm->readFlags(189))
+ screen._backBuffer1.maskArea((*_mask1)[0], Common::Point(124, 239), screen._currentScroll);
+ break;
+
+ case 53:
+ screen._backBuffer1.maskArea((*_mask)[0], Common::Point(_maskOffset.x, 110), screen._currentScroll);
+ break;
+
+ case 68:
+ screen._backBuffer1.maskArea((*_mask)[0], Common::Point(0, 203), screen._currentScroll);
+ screen._backBuffer1.maskArea((*_mask1)[0], Common::Point(124, 239), screen._currentScroll);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ screen._flushScreen = true;
+
+ for (int idx = 0; idx < MAX_CHARACTERS; ++idx) {
+ Person &p = people[idx];
+
+ if (p._type != INVALID) {
+ if (_goToScene == -1 || _cAnim.size() == 0) {
+ if (p._type == REMOVE) {
+ screen.slamArea(p._oldPosition.x, p._oldPosition.y, p._oldSize.x, p._oldSize.y);
+ p._type = INVALID;
+ } else {
+ if (p._tempScaleVal == 256) {
+ screen.flushImage(p._imageFrame, Common::Point(p._tempX, p._position.y / FIXED_INT_MULTIPLIER
+ - p._imageFrame->_width), &p._oldPosition.x, &p._oldPosition.y, &p._oldSize.x, &p._oldSize.y);
+ } else {
+ int ts = p._imageFrame->sDrawYSize(p._tempScaleVal);
+ int ty = p._position.y / FIXED_INT_MULTIPLIER - ts;
+ screen.flushScaleImage(p._imageFrame, Common::Point(p._tempX, ty),
+ &p._oldPosition.x, &p._oldPosition.y, &p._oldSize.x, &p._oldSize.y, p._tempScaleVal);
+ }
+ }
+ }
+ }
+ }
+
+ for (uint idx = 0; idx < _bgShapes.size(); ++idx) {
+ Object &obj = _bgShapes[idx];
+
+ if (obj._type == ACTIVE_BG_SHAPE || obj._type == REMOVE) {
+ if (_goToScene == -1) {
+ if (obj._scaleVal == 256)
+ screen.flushImage(obj._imageFrame, obj._position, &obj._oldPosition.x, &obj._oldPosition.y,
+ &obj._oldSize.x, &obj._oldSize.y);
+ else
+ screen.flushScaleImage(obj._imageFrame, obj._position, &obj._oldPosition.x, &obj._oldPosition.y,
+ &obj._oldSize.x, &obj._oldSize.y, obj._scaleVal);
+
+ if (obj._type == REMOVE)
+ obj._type = INVALID;
+ }
+ }
+ }
+
+ for (uint idx = 0; idx < _bgShapes.size(); ++idx) {
+ Object &obj = _bgShapes[idx];
+
+ if (_goToScene == -1) {
+ if (obj._type == NO_SHAPE && (obj._flags & 1) == 0) {
+ screen.slamRect(obj.getNoShapeBounds());
+ screen.slamRect(obj.getOldBounds());
+ } else if (obj._type == HIDE_SHAPE) {
+ if (obj._scaleVal == 256)
+ screen.flushImage(obj._imageFrame, obj._position, &obj._oldPosition.x, &obj._oldPosition.y,
+ &obj._oldSize.x, &obj._oldSize.y);
+ else
+ screen.flushScaleImage(obj._imageFrame, obj._position, &obj._oldPosition.x, &obj._oldPosition.y,
+ &obj._oldSize.x, &obj._oldSize.y, obj._scaleVal);
+ obj._type = HIDDEN;
+ }
+ }
+ }
+
+ screen._flushScreen = false;
+}
+
+
+} // End of namespace Tattoo
+
+} // End of namespace Sherlock
diff --git a/engines/sherlock/tattoo/tattoo_scene.h b/engines/sherlock/tattoo/tattoo_scene.h
new file mode 100644
index 0000000000..de28306c1b
--- /dev/null
+++ b/engines/sherlock/tattoo/tattoo_scene.h
@@ -0,0 +1,77 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef SHERLOCK_TATTOO_SCENE_H
+#define SHERLOCK_TATTOO_SCENE_H
+
+#include "common/scummsys.h"
+#include "sherlock/scene.h"
+
+namespace Sherlock {
+
+namespace Tattoo {
+
+class TattooScene : public Scene {
+private:
+ int _arrowZone;
+ int _maskCounter;
+ Common::Point _maskOffset;
+private:
+ void doBgAnimCheckCursor();
+
+ void doBgAnimEraseBackground();
+
+ /**
+ * Update the background objects and canimations as part of doBgAnim
+ */
+ void doBgAnimUpdateBgObjectsAndAnim();
+protected:
+ /**
+ * 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
+ */
+ virtual void checkBgShapes();
+public:
+ ImageFile *_mask, *_mask1;
+ CAnimStream _activeCAnim;
+public:
+ TattooScene(SherlockEngine *vm);
+
+ /**
+ * Draw all objects and characters.
+ */
+ virtual void doBgAnim();
+
+ /**
+ * Update the screen back buffer with all of the scene objects which need
+ * to be drawn
+ */
+ virtual void updateBackground();
+
+};
+
+} // End of namespace Tattoo
+
+} // End of namespace Sherlock
+
+#endif
diff --git a/engines/sherlock/tattoo/tattoo_user_interface.cpp b/engines/sherlock/tattoo/tattoo_user_interface.cpp
new file mode 100644
index 0000000000..e76322833f
--- /dev/null
+++ b/engines/sherlock/tattoo/tattoo_user_interface.cpp
@@ -0,0 +1,150 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "sherlock/tattoo/tattoo_user_interface.h"
+#include "sherlock/tattoo/tattoo_scene.h"
+#include "sherlock/tattoo/tattoo.h"
+
+namespace Sherlock {
+
+namespace Tattoo {
+
+TattooUserInterface::TattooUserInterface(SherlockEngine *vm): UserInterface(vm) {
+ _menuBuffer = nullptr;
+ _invMenuBuffer = nullptr;
+ _tagBuffer = nullptr;
+ _invGraphic = nullptr;
+}
+
+void TattooUserInterface::handleInput() {
+ // TODO
+ _vm->_events->pollEventsAndWait();
+}
+
+void TattooUserInterface::drawInterface(int bufferNum) {
+ Screen &screen = *_vm->_screen;
+ TattooEngine &vm = *((TattooEngine *)_vm);
+
+ if (_invMenuBuffer != nullptr) {
+ Common::Rect r = _invMenuBounds;
+ r.grow(-3);
+ r.translate(-screen._currentScroll, 0);
+ _grayAreas.clear();
+ _grayAreas.push_back(r);
+
+ drawGrayAreas();
+ screen._backBuffer1.transBlitFrom(*_invMenuBuffer, Common::Point(_invMenuBounds.left, _invMenuBounds.top));
+ }
+
+ if (_menuBuffer != nullptr) {
+ Common::Rect r = _menuBounds;
+ r.grow(-3);
+ r.translate(-screen._currentScroll, 0);
+ _grayAreas.clear();
+ _grayAreas.push_back(r);
+
+ drawGrayAreas();
+ screen._backBuffer1.transBlitFrom(*_menuBuffer, Common::Point(_invMenuBounds.left, _invMenuBounds.top));
+ }
+
+ // See if we need to draw a Text Tag floating with the cursor
+ if (_tagBuffer != nullptr)
+ screen._backBuffer1.transBlitFrom(*_tagBuffer, Common::Point(_tagBounds.left, _tagBounds.top));
+
+ // See if we need to draw an Inventory Item Graphic floating with the cursor
+ if (_invGraphic != nullptr)
+ screen._backBuffer1.transBlitFrom(*_invGraphic, Common::Point(_invGraphicBounds.left, _invGraphicBounds.top));
+
+ if (vm._creditsActive)
+ vm.drawCredits();
+}
+
+void TattooUserInterface::doBgAnimRestoreUI() {
+ TattooScene &scene = *((TattooScene *)_vm->_scene);
+ Screen &screen = *_vm->_screen;
+
+ // If _oldMenuBounds was set, then either a new menu has been opened or the current menu has been closed.
+ // Either way, we need to restore the area where the menu was displayed
+ if (_oldMenuBounds.width() > 0)
+ screen._backBuffer1.blitFrom(screen._backBuffer2, Common::Point(_oldMenuBounds.left, _oldMenuBounds.top),
+ _oldMenuBounds);
+
+ if (_oldInvMenuBounds.width() > 0)
+ screen._backBuffer1.blitFrom(screen._backBuffer2, Common::Point(_oldInvMenuBounds.left, _oldInvMenuBounds.top),
+ _oldInvMenuBounds);
+
+ if (_menuBuffer != nullptr)
+ screen._backBuffer1.blitFrom(screen._backBuffer2, Common::Point(_menuBounds.left, _menuBounds.top), _menuBounds);
+ if (_invMenuBuffer != nullptr)
+ screen._backBuffer1.blitFrom(screen._backBuffer2, Common::Point(_invMenuBounds.left, _invMenuBounds.top), _invMenuBounds);
+
+ // If there is a Text Tag being display, restore the area underneath it
+ if (_oldTagBounds.width() > 0)
+ screen._backBuffer1.blitFrom(screen._backBuffer2, Common::Point(_oldTagBounds.left, _oldTagBounds.top),
+ _oldTagBounds);
+
+ // If there is an Inventory being shown, restore the graphics underneath it
+ if (_oldInvGraphicBounds.width() > 0)
+ screen._backBuffer1.blitFrom(screen._backBuffer2, Common::Point(_oldInvGraphicBounds.left, _oldInvGraphicBounds.top),
+ _oldInvGraphicBounds);
+
+ // If a canimation is active, restore the graphics underneath it
+ if (scene._activeCAnim._images != nullptr)
+ screen.restoreBackground(scene._activeCAnim._oldBounds);
+
+ // If a canimation just ended, remove it's graphics from the backbuffer
+ if (scene._activeCAnim._removeBounds.width() > 0)
+ screen.restoreBackground(scene._activeCAnim._removeBounds);
+}
+
+void TattooUserInterface::doScroll() {
+ Screen &screen = *_vm->_screen;
+ int oldScroll = screen._currentScroll;
+
+ // If we're already at the target scroll position, nothing needs to be done
+ if (screen._targetScroll == screen._currentScroll)
+ return;
+
+ screen._flushScreen = true;
+ if (screen._targetScroll > screen._currentScroll) {
+ screen._currentScroll += screen._scrollSpeed;
+ if (screen._currentScroll > screen._targetScroll)
+ screen._currentScroll = screen._targetScroll;
+ } else if (screen._targetScroll < screen._currentScroll) {
+ screen._currentScroll -= screen._scrollSpeed;
+ if (screen._currentScroll < screen._targetScroll)
+ screen._currentScroll = screen._targetScroll;
+ }
+
+ if (_menuBuffer != nullptr)
+ _menuBounds.translate(screen._currentScroll - oldScroll, 0);
+ if (_invMenuBuffer != nullptr)
+ _invMenuBounds.translate(screen._currentScroll - oldScroll, 0);
+}
+
+void TattooUserInterface::drawGrayAreas() {
+ // TODO
+}
+
+} // End of namespace Tattoo
+
+} // End of namespace Sherlock
diff --git a/engines/sherlock/tattoo/tattoo_user_interface.h b/engines/sherlock/tattoo/tattoo_user_interface.h
new file mode 100644
index 0000000000..2125f1ba07
--- /dev/null
+++ b/engines/sherlock/tattoo/tattoo_user_interface.h
@@ -0,0 +1,83 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef SHERLOCK_TATTOO_UI_H
+#define SHERLOCK_TATTOO_UI_H
+
+#include "common/scummsys.h"
+#include "sherlock/user_interface.h"
+
+namespace Sherlock {
+
+namespace Tattoo {
+
+class TattooUserInterface : public UserInterface {
+private:
+ Common::Rect _menuBounds;
+ Common::Rect _oldMenuBounds;
+ Common::Rect _invMenuBounds;
+ Common::Rect _oldInvMenuBounds;
+ Common::Rect _tagBounds;
+ Common::Rect _oldTagBounds;
+ Common::Rect _invGraphicBounds;
+ Common::Rect _oldInvGraphicBounds;
+ Surface *_menuBuffer;
+ Surface *_invMenuBuffer;
+ Surface *_tagBuffer;
+ Surface *_invGraphic;
+ Common::Array<Common::Rect> _grayAreas;
+private:
+ /**
+ * Draws designated areas of the screen that are meant to be grayed out using grayscale colors
+ */
+ void drawGrayAreas();
+public:
+ TattooUserInterface(SherlockEngine *vm);
+
+ /**
+ * Handles restoring any areas of the back buffer that were/are covered by UI elements
+ */
+ void doBgAnimRestoreUI();
+
+ /**
+ * Checks to see if the screen needs to be scrolled. If so, scrolls it towards the target position
+ */
+ void doScroll();
+public:
+ virtual ~TattooUserInterface() {}
+
+ /**
+ * Main input handler for the user interface
+ */
+ virtual void handleInput();
+
+ /**
+ * Draw the user interface onto the screen's back buffers
+ */
+ virtual void drawInterface(int bufferNum = 3);
+};
+
+} // End of namespace Tattoo
+
+} // End of namespace Sherlock
+
+#endif
diff --git a/engines/sherlock/user_interface.cpp b/engines/sherlock/user_interface.cpp
index 524ecf3d2f..9fff7cc999 100644
--- a/engines/sherlock/user_interface.cpp
+++ b/engines/sherlock/user_interface.cpp
@@ -22,2269 +22,35 @@
#include "sherlock/user_interface.h"
#include "sherlock/sherlock.h"
-#include "sherlock/settings.h"
+#include "sherlock/scalpel/scalpel_user_interface.h"
+#include "sherlock/tattoo/tattoo_user_interface.h"
namespace Sherlock {
-// Main user interface menu control locations
-const int MENU_POINTS[12][4] = {
- { 13, 153, 72, 165 },
- { 13, 169, 72, 181 },
- { 13, 185, 72, 197 },
- { 88, 153, 152, 165 },
- { 88, 169, 152, 181 },
- { 88, 185, 152, 197 },
- { 165, 153, 232, 165 },
- { 165, 169, 232, 181 },
- { 165, 185, 233, 197 },
- { 249, 153, 305, 165 },
- { 249, 169, 305, 181 },
- { 249, 185, 305, 197 }
-};
-
-// Inventory control locations */
-const int INVENTORY_POINTS[8][3] = {
- { 4, 50, 29 },
- { 52, 99, 77 },
- { 101, 140, 123 },
- { 142, 187, 166 },
- { 189, 219, 198 },
- { 221, 251, 234 },
- { 253, 283, 266 },
- { 285, 315, 294 }
-};
-
-const char COMMANDS[13] = "LMTPOCIUGJFS";
-const char INVENTORY_COMMANDS[9] = { "ELUG-+,." };
-const char *const PRESS_KEY_FOR_MORE = "Press any Key for More.";
-const char *const PRESS_KEY_TO_CONTINUE = "Press any Key to Continue.";
-
-const char *const MOPEN[] = {
- "This cannot be opened", "It is already open", "It is locked", "Wait for Watson", " ", "."
-};
-const char *const MCLOSE[] = {
- "This cannot be closed", "It is already closed", "The safe door is in the way"
-};
-const char *const MMOVE[] = {
- "This cannot be moved", "It is bolted to the floor", "It is too heavy", "The other crate is in the way"
-};
-const char *const MPICK[] = {
- "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"
-};
-const char *const MUSE[] = {
- "You can't do that", "It had no effect", "You can't reach it", "OK, the door looks bigger! Happy?",
- "Doors don't smoke"
-};
-
-/*----------------------------------------------------------------*/
+UserInterface *UserInterface::init(SherlockEngine *vm) {
+ if (vm->getGameID() == GType_SerratedScalpel)
+ return new Scalpel::ScalpelUserInterface(vm);
+ else
+ return new Tattoo::TattooUserInterface(vm);
+}
UserInterface::UserInterface(SherlockEngine *vm) : _vm(vm) {
- if (_vm->_interactiveFl) {
- _controls = new ImageFile("menu.all");
- _controlPanel = new ImageFile("controls.vgs");
- } else {
- _controls = nullptr;
- _controlPanel = nullptr;
- }
-
- _bgFound = 0;
- _oldBgFound = -1;
- _keyPress = '\0';
- _helpStyle = false;
- _menuCounter = 0;
_menuMode = STD_MODE;
- _help = _oldHelp = 0;
- _lookHelp = 0;
- _key = _oldKey = '\0';
- _temp = _oldTemp = 0;
- _temp1 = 0;
- _invLookFlag = 0;
+ _menuCounter = 0;
+ _infoFlag = false;
_windowOpen = false;
- _oldLook = false;
- _keyboardInput = false;
- _pause = false;
- _cNum = 0;
- _selector = _oldSelector = -1;
- _windowBounds = Common::Rect(0, CONTROLS_Y1, SHERLOCK_SCREEN_WIDTH - 1,
- SHERLOCK_SCREEN_HEIGHT - 1);
- _slideWindows = true;
- _find = 0;
- _oldUse = 0;
_endKeyActive = true;
+ _invLookFlag = 0;
+ _slideWindows = true;
+ _helpStyle = false;
+ _windowBounds = Common::Rect(0, CONTROLS_Y1, SHERLOCK_SCREEN_WIDTH - 1, SHERLOCK_SCREEN_HEIGHT - 1);
_lookScriptFlag = false;
- _infoFlag = false;
-}
-
-UserInterface::~UserInterface() {
- delete _controls;
- delete _controlPanel;
-}
-
-void UserInterface::reset() {
- _oldKey = -1;
- _help = _oldHelp = -1;
- _oldTemp = _temp = -1;
-}
-
-void UserInterface::drawInterface(int bufferNum) {
- Screen &screen = *_vm->_screen;
-
- if (bufferNum & 1)
- screen._backBuffer1.transBlitFrom((*_controlPanel)[0], Common::Point(0, CONTROLS_Y));
- if (bufferNum & 2)
- screen._backBuffer2.transBlitFrom((*_controlPanel)[0], Common::Point(0, CONTROLS_Y));
- if (bufferNum == 3)
- screen._backBuffer2.fillRect(0, INFO_LINE, SHERLOCK_SCREEN_WIDTH, INFO_LINE + 10, INFO_BLACK);
-}
-
-void UserInterface::handleInput() {
- Events &events = *_vm->_events;
- Inventory &inv = *_vm->_inventory;
- People &people = *_vm->_people;
- Scene &scene = *_vm->_scene;
- Screen &screen = *_vm->_screen;
- Talk &talk = *_vm->_talk;
-
- if (_menuCounter)
- whileMenuCounter();
-
- Common::Point pt = events.mousePos();
- _bgFound = scene.findBgShape(Common::Rect(pt.x, pt.y, pt.x + 1, pt.y + 1));
- _keyPress = '\0';
-
- // Check kbd and set the mouse released flag if Enter or space is pressed.
- // Otherwise, the pressed _key is stored for later use
- if (events.kbHit()) {
- Common::KeyState keyState = events.getKey();
- _keyPress = keyState.ascii;
-
- if (keyState.keycode == Common::KEYCODE_x && keyState.flags & Common::KBD_ALT) {
- _vm->quitGame();
- events.pollEvents();
- return;
- }
- }
-
- // Do button highlighting check
- if (!talk._scriptMoreFlag) { // Don't if scripts are running
- if (((events._rightPressed || events._rightReleased) && _helpStyle) ||
- (!_helpStyle && !_menuCounter)) {
- // Handle any default commands if we're in STD_MODE
- if (_menuMode == STD_MODE) {
- if (pt.y < CONTROLS_Y &&
- (events._rightPressed || (!_helpStyle && !events._released)) &&
- (_bgFound != -1) && (_bgFound < 1000) &&
- (scene._bgShapes[_bgFound]._defaultCommand ||
- !scene._bgShapes[_bgFound]._description.empty())) {
- // If there is no default command, so set it to Look
- if (scene._bgShapes[_bgFound]._defaultCommand)
- _help = scene._bgShapes[_bgFound]._defaultCommand - 1;
- else
- _help = 0;
-
- // Reset 'help' if it is an invalid command
- if (_help > 5)
- _help = -1;
- } else if (pt.y < CONTROLS_Y &&
- ((events._rightReleased && _helpStyle) || (events._released && !_helpStyle)) &&
- (_bgFound != -1 && _bgFound < 1000) &&
- (scene._bgShapes[_bgFound]._defaultCommand ||
- !scene._bgShapes[_bgFound]._description.empty())) {
- // If there is no default command, set it to Look
- if (scene._bgShapes[_bgFound]._defaultCommand)
- _menuMode = (MenuMode)scene._bgShapes[_bgFound]._defaultCommand;
- else
- _menuMode = LOOK_MODE;
- events._released = true;
- events._pressed = events._oldButtons = false;
- _help = _oldHelp = -1;
-
- if (_menuMode == LOOK_MODE) {
- // Set the flag to tell the game that this was a right-click
- // call to look and should exit without the look button being pressed
- _lookHelp = true;
- }
- } else {
- _help = -1;
- }
-
- // Check if highlighting a different button than last time
- if (_help != _oldHelp) {
- // If another button was highlighted previously, restore it
- if (_oldHelp != -1)
- restoreButton(_oldHelp);
-
- // If we're highlighting a new button, then draw it pressed
- if (_help != -1)
- depressButton(_help);
-
- _oldHelp = _help;
- }
-
- if (_bgFound != _oldBgFound || _oldBgFound == -1) {
- _infoFlag = true;
- clearInfo();
-
- if (_help != -1 && !scene._bgShapes[_bgFound]._description.empty()
- && scene._bgShapes[_bgFound]._description[0] != ' ')
- screen.print(Common::Point(0, INFO_LINE + 1),
- INFO_FOREGROUND, "%s", scene._bgShapes[_bgFound]._description.c_str());
-
- _oldBgFound = _bgFound;
- }
- } else {
- // We're not in STD_MODE
- // If there isn't a window open, then revert back to STD_MODE
- if (!_windowOpen && events._rightReleased) {
- // Restore all buttons
- for (int idx = 0; idx < 12; ++idx)
- restoreButton(idx);
-
- _menuMode = STD_MODE;
- _key = _oldKey = -1;
- _temp = _oldTemp = _lookHelp = _invLookFlag = 0;
- events.clearEvents();
- }
- }
- }
- }
-
- // Reset the old bgshape number if the mouse button is released, so that
- // it can e re-highlighted when we come back here
- if ((events._rightReleased && _helpStyle) || (events._released && !_helpStyle))
- _oldBgFound = -1;
-
- // Do routines that should be done before input processing
- switch (_menuMode) {
- case LOOK_MODE:
- if (!_windowOpen) {
- if (events._released && _bgFound >= 0 && _bgFound < 1000) {
- if (!scene._bgShapes[_bgFound]._examine.empty())
- examine();
- } else {
- lookScreen(pt);
- }
- }
- break;
-
- case MOVE_MODE:
- case OPEN_MODE:
- case CLOSE_MODE:
- case PICKUP_MODE:
- lookScreen(pt);
- break;
-
- case TALK_MODE:
- if (!_windowOpen) {
- bool personFound;
-
- if (_bgFound >= 1000) {
- personFound = false;
- if (!events._released)
- lookScreen(pt);
- } else {
- personFound = _bgFound != -1 && scene._bgShapes[_bgFound]._aType == PERSON;
- }
-
- if (events._released && personFound)
- talk.talk(_bgFound);
- else if (personFound)
- lookScreen(pt);
- else if (_bgFound < 1000)
- clearInfo();
- }
- break;
-
- case USE_MODE:
- case GIVE_MODE:
- case INV_MODE:
- if (inv._invMode == INVMODE_LOOK || inv._invMode == INVMODE_USE || inv._invMode == INVMODE_GIVE) {
- if (pt.y > CONTROLS_Y)
- lookInv();
- else
- lookScreen(pt);
- }
- break;
-
- default:
- break;
- }
-
- //
- // Do input processing
- //
- if (events._pressed || events._released || events._rightPressed || _keyPress || _pause) {
- if (((events._released && (_helpStyle || _help == -1)) || (events._rightReleased && !_helpStyle)) &&
- (pt.y <= CONTROLS_Y) && (_menuMode == STD_MODE)) {
- // The mouse was clicked in the playing area with no action buttons down.
- // Check if the mouse was clicked in a script zone. If it was,
- // then execute the script. Otherwise, walk to the given position
- if (scene.checkForZones(pt, SCRIPT_ZONE) != 0 ||
- scene.checkForZones(pt, NOWALK_ZONE) != 0) {
- // Mouse clicked in script zone
- events._pressed = events._released = false;
- } else {
- people._walkDest = pt;
- people._allowWalkAbort = false;
- people.goAllTheWay();
- }
-
- if (_oldKey != -1) {
- restoreButton(_oldTemp);
- _oldKey = -1;
- }
- }
-
- // Handle action depending on selected mode
- switch (_menuMode) {
- case LOOK_MODE:
- if (_windowOpen)
- doLookControl();
- break;
-
- case MOVE_MODE:
- doMiscControl(ALLOW_MOVE);
- break;
-
- case TALK_MODE:
- if (_windowOpen)
- doTalkControl();
- break;
-
- case OPEN_MODE:
- doMiscControl(ALLOW_OPEN);
- break;
-
- case CLOSE_MODE:
- doMiscControl(ALLOW_CLOSE);
- break;
-
- case PICKUP_MODE:
- doPickControl();
- break;
-
- case USE_MODE:
- case GIVE_MODE:
- case INV_MODE:
- doInvControl();
- break;
-
- case FILES_MODE:
- doEnvControl();
- break;
-
- default:
- break;
- }
-
- // As long as there isn't an open window, do main input processing.
- // Windows are opened when in TALK, USE, INV, and GIVE modes
- if ((!_windowOpen && !_menuCounter && pt.y > CONTROLS_Y) ||
- _keyPress) {
- if (events._pressed || events._released || _pause || _keyPress)
- doMainControl();
- }
-
- if (pt.y < CONTROLS_Y && events._pressed && _oldTemp != (int)(_menuMode - 1) && _oldKey != -1)
- restoreButton(_oldTemp);
- }
-}
-
-void UserInterface::depressButton(int num) {
- Screen &screen = *_vm->_screen;
- Common::Point pt(MENU_POINTS[num][0], MENU_POINTS[num][1]);
-
- ImageFrame &frame = (*_controls)[num];
- screen._backBuffer1.transBlitFrom(frame, pt);
- screen.slamArea(pt.x, pt.y, pt.x + frame._width, pt.y + frame._height);
-}
-
-void UserInterface::restoreButton(int num) {
- Screen &screen = *_vm->_screen;
- Common::Point pt(MENU_POINTS[num][0], MENU_POINTS[num][1]);
- Graphics::Surface &frame = (*_controls)[num]._frame;
-
- screen._backBuffer1.blitFrom(screen._backBuffer2, pt,
- Common::Rect(pt.x, pt.y, pt.x + 90, pt.y + 19));
- screen.slamArea(pt.x, pt.y, pt.x + frame.w, pt.y + frame.h);
-
- if (!_menuCounter) {
- _infoFlag = true;
- clearInfo();
- }
-}
-
-void UserInterface::pushButton(int num) {
- Events &events = *_vm->_events;
- _oldKey = -1;
-
- if (!events._released) {
- if (_oldHelp != -1)
- restoreButton(_oldHelp);
- if (_help != -1)
- restoreButton(_help);
-
- depressButton(num);
- events.wait(6);
- }
-
- restoreButton(num);
-}
-
-void UserInterface::toggleButton(int num) {
- Screen &screen = *_vm->_screen;
-
- if (_menuMode != (MenuMode)(num + 1)) {
- _menuMode = (MenuMode)(num + 1);
- _oldKey = COMMANDS[num];
- _oldTemp = num;
-
- if (_keyboardInput) {
- if (_oldHelp != -1 && _oldHelp != num)
- restoreButton(_oldHelp);
- if (_help != -1 && _help != num)
- restoreButton(_help);
-
- _keyboardInput = false;
-
- ImageFrame &frame = (*_controls)[num];
- Common::Point pt(MENU_POINTS[num][0], MENU_POINTS[num][1]);
- screen._backBuffer1.transBlitFrom(frame, pt);
- screen.slamArea(pt.x, pt.y, pt.x + frame._width, pt.y + frame._height);
- }
- } else {
- _menuMode = STD_MODE;
- _oldKey = -1;
- restoreButton(num);
- }
-}
-
-void UserInterface::clearInfo() {
- if (_infoFlag) {
- _vm->_screen->vgaBar(Common::Rect(16, INFO_LINE, SHERLOCK_SCREEN_WIDTH - 19,
- INFO_LINE + 10), INFO_BLACK);
- _infoFlag = false;
- _oldLook = -1;
- }
-}
-
-void UserInterface::clearWindow() {
- if (_windowOpen) {
- _vm->_screen->vgaBar(Common::Rect(3, CONTROLS_Y + 11, SHERLOCK_SCREEN_WIDTH - 2,
- SHERLOCK_SCREEN_HEIGHT - 2), INV_BACKGROUND);
- }
-}
-
-void UserInterface::whileMenuCounter() {
- if (!(--_menuCounter) || _vm->_events->checkInput()) {
- _menuCounter = 0;
- _infoFlag = true;
- clearInfo();
- }
-}
-
-void UserInterface::examine() {
- Events &events = *_vm->_events;
- Inventory &inv = *_vm->_inventory;
- People &people = *_vm->_people;
- Scene &scene = *_vm->_scene;
- Talk &talk = *_vm->_talk;
- Common::Point pt = events.mousePos();
-
- if (pt.y < (CONTROLS_Y + 9)) {
- Object &obj = scene._bgShapes[_bgFound];
-
- if (obj._lookcAnim != 0) {
- int canimSpeed = ((obj._lookcAnim & 0xe0) >> 5) + 1;
- scene._cAnimFramePause = obj._lookFrames;
- _cAnimStr = obj._examine;
- _cNum = (obj._lookcAnim & 0x1f) - 1;
-
- scene.startCAnim(_cNum, canimSpeed);
- } else if (obj._lookPosition.y != 0) {
- // Need to walk to the object to be examined
- people.walkToCoords(Common::Point(obj._lookPosition.x, obj._lookPosition.y * 100), obj._lookFacing);
- }
-
- if (!talk._talkToAbort) {
- _cAnimStr = obj._examine;
- if (obj._lookFlag)
- _vm->setFlags(obj._lookFlag);
- }
- } else {
- // Looking at an inventory item
- _cAnimStr = inv[_selector]._examine;
- if (inv[_selector]._lookFlag)
- _vm->setFlags(inv[_selector]._lookFlag);
- }
-
- if (_invLookFlag) {
- // Don't close the inventory window when starting an examine display, since its
- // window will slide up to replace the inventory display
- _windowOpen = false;
- _menuMode = LOOK_MODE;
- }
-
- if (!talk._talkToAbort) {
- if (!scene._cAnimFramePause)
- printObjectDesc(_cAnimStr, true);
- else
- // description was already printed in startCAnimation
- scene._cAnimFramePause = 0;
- }
-}
-
-void UserInterface::lookScreen(const Common::Point &pt) {
- Events &events = *_vm->_events;
- Inventory &inv = *_vm->_inventory;
- Scene &scene = *_vm->_scene;
- Screen &screen = *_vm->_screen;
- Common::Point mousePos = events.mousePos();
- int temp;
- Common::String tempStr;
-
- // Don't display anything for right button command
- if ((events._rightPressed || events._rightPressed) && !events._pressed)
- return;
-
- if (mousePos.y < CONTROLS_Y && (temp = _bgFound) != -1) {
- if (temp != _oldLook) {
- _infoFlag = true;
- clearInfo();
-
- if (temp < 1000)
- tempStr = scene._bgShapes[temp]._description;
- else
- tempStr = scene._bgShapes[temp - 1000]._description;
-
- _infoFlag = true;
- clearInfo();
-
- // Only print description if there is one
- if (!tempStr.empty() && tempStr[0] != ' ') {
- // If inventory is active and an item is selected for a Use or Give action
- if ((_menuMode == INV_MODE || _menuMode == USE_MODE || _menuMode == GIVE_MODE) &&
- (inv._invMode == INVMODE_USE || inv._invMode == INVMODE_GIVE)) {
- int width1 = 0, width2 = 0;
- int x, width;
- if (inv._invMode == INVMODE_USE) {
- // Using an object
- x = width = screen.stringWidth("Use ");
-
- if (temp < 1000 && scene._bgShapes[temp]._aType != PERSON)
- // It's not a person, so make it lowercase
- tempStr.setChar(tolower(tempStr[0]), 0);
-
- x += screen.stringWidth(tempStr);
-
- // If we're using an inventory object, add in the width
- // of the object name and the " on "
- if (_selector != -1) {
- width1 = screen.stringWidth(inv[_selector]._name);
- x += width1;
- width2 = screen.stringWidth(" on ");
- x += width2;
- }
-
- // If the line will be too long, keep cutting off characters
- // until the string will fit
- while (x > 280) {
- x -= screen.charWidth(tempStr.lastChar());
- tempStr.deleteLastChar();
- }
-
- int xStart = (SHERLOCK_SCREEN_WIDTH - x) / 2;
- screen.print(Common::Point(xStart, INFO_LINE + 1),
- INFO_FOREGROUND, "Use ");
-
- if (_selector != -1) {
- screen.print(Common::Point(xStart + width, INFO_LINE + 1),
- TALK_FOREGROUND, "%s", inv[_selector]._name.c_str());
- screen.print(Common::Point(xStart + width + width1, INFO_LINE + 1),
- INFO_FOREGROUND, " on ");
- screen.print(Common::Point(xStart + width + width1 + width2, INFO_LINE + 1),
- INFO_FOREGROUND, "%s", tempStr.c_str());
- } else {
- screen.print(Common::Point(xStart + width, INFO_LINE + 1),
- INFO_FOREGROUND, "%s", tempStr.c_str());
- }
- } else if (temp >= 0 && temp < 1000 && _selector != -1 &&
- scene._bgShapes[temp]._aType == PERSON) {
- // Giving an object to a person
- width1 = screen.stringWidth(inv[_selector]._name);
- x = width = screen.stringWidth("Give ");
- x += width1;
- width2 = screen.stringWidth(" to ");
- x += width2;
- x += screen.stringWidth(tempStr);
-
- // Ensure string will fit on-screen
- while (x > 280) {
- x -= screen.charWidth(tempStr.lastChar());
- tempStr.deleteLastChar();
- }
-
- int xStart = (SHERLOCK_SCREEN_WIDTH - x) / 2;
- screen.print(Common::Point(xStart, INFO_LINE + 1),
- INFO_FOREGROUND, "Give ");
- screen.print(Common::Point(xStart + width, INFO_LINE + 1),
- TALK_FOREGROUND, "%s", inv[_selector]._name.c_str());
- screen.print(Common::Point(xStart + width + width1, INFO_LINE + 1),
- INFO_FOREGROUND, " to ");
- screen.print(Common::Point(xStart + width + width1 + width2, INFO_LINE + 1),
- INFO_FOREGROUND, "%s", tempStr.c_str());
- }
- } else {
- screen.print(Common::Point(0, INFO_LINE + 1), INFO_FOREGROUND, "%s", tempStr.c_str());
- }
-
- _infoFlag = true;
- _oldLook = temp;
- }
- }
- } else {
- clearInfo();
- }
-}
-
-void UserInterface::lookInv() {
- Events &events = *_vm->_events;
- Inventory &inv = *_vm->_inventory;
- Screen &screen = *_vm->_screen;
- Common::Point mousePos = events.mousePos();
-
- if (mousePos.x > 15 && mousePos.x < 314 && mousePos.y > (CONTROLS_Y1 + 11)
- && mousePos.y < (SHERLOCK_SCREEN_HEIGHT - 2)) {
- int temp = (mousePos.x - 6) / 52 + inv._invIndex;
- if (temp < inv._holdings) {
- if (temp < inv._holdings) {
- clearInfo();
- screen.print(Common::Point(0, INFO_LINE + 1), INFO_FOREGROUND,
- "%s", inv[temp]._description.c_str());
- _infoFlag = true;
- _oldLook = temp;
- }
- } else {
- clearInfo();
- }
- } else {
- clearInfo();
- }
-}
-
-void UserInterface::doEnvControl() {
- Events &events = *_vm->_events;
- SaveManager &saves = *_vm->_saves;
- Scene &scene = *_vm->_scene;
- Screen &screen = *_vm->_screen;
- Talk &talk = *_vm->_talk;
- Common::Point mousePos = events.mousePos();
- static const char ENV_COMMANDS[7] = "ELSUDQ";
-
- byte color;
-
- _key = _oldKey = -1;
- _keyboardInput = false;
- int found = saves.getHighlightedButton();
-
- if (events._pressed || events._released) {
- events.clearKeyboard();
-
- // Check for a filename entry being highlighted
- if ((events._pressed || events._released) && mousePos.y > (CONTROLS_Y + 10)) {
- int found1 = 0;
- for (_selector = 0; (_selector < ONSCREEN_FILES_COUNT) && !found1; ++_selector)
- if (mousePos.y > (CONTROLS_Y + 11 + _selector * 10) && mousePos.y < (CONTROLS_Y + 21 + _selector * 10))
- found1 = 1;
-
- if (_selector + saves._savegameIndex - 1 < MAX_SAVEGAME_SLOTS + (saves._envMode != SAVEMODE_LOAD))
- _selector = _selector + saves._savegameIndex - 1;
- else
- _selector = -1;
-
- if (!found1)
- _selector = -1;
- }
-
- // Handle selecting buttons, if any
- saves.highlightButtons(found);
-
- if (found == 0 || found == 5)
- saves._envMode = SAVEMODE_NONE;
- }
-
- if (_keyPress) {
- _key = toupper(_keyPress);
-
- // Escape _key will close the dialog
- if (_key == Common::KEYCODE_ESCAPE)
- _key = 'E';
-
- if (_key == 'E' || _key == 'L' || _key == 'S' || _key == 'U' || _key == 'D' || _key == 'Q') {
- const char *chP = strchr(ENV_COMMANDS, _key);
- int btnIndex = !chP ? -1 : chP - ENV_COMMANDS;
- saves.highlightButtons(btnIndex);
- _keyboardInput = true;
-
- if (_key == 'E' || _key == 'Q') {
- saves._envMode = SAVEMODE_NONE;
- } else if (_key >= '1' && _key <= '9') {
- _keyboardInput = true;
- _selector = _key - '1';
- if (_selector >= MAX_SAVEGAME_SLOTS + (saves._envMode == SAVEMODE_LOAD ? 0 : 1))
- _selector = -1;
-
- if (saves.checkGameOnScreen(_selector))
- _oldSelector = _selector;
- } else {
- _selector = -1;
- }
- }
- }
-
- if (_selector != _oldSelector) {
- if (_oldSelector != -1 && _oldSelector >= saves._savegameIndex && _oldSelector < (saves._savegameIndex + ONSCREEN_FILES_COUNT)) {
- screen.print(Common::Point(6, CONTROLS_Y + 12 + (_oldSelector - saves._savegameIndex) * 10),
- INV_FOREGROUND, "%d.", _oldSelector + 1);
- screen.print(Common::Point(24, CONTROLS_Y + 12 + (_oldSelector - saves._savegameIndex) * 10),
- INV_FOREGROUND, "%s", saves._savegames[_oldSelector].c_str());
- }
-
- if (_selector != -1) {
- screen.print(Common::Point(6, CONTROLS_Y + 12 + (_selector - saves._savegameIndex) * 10),
- TALK_FOREGROUND, "%d.", _selector + 1);
- screen.print(Common::Point(24, CONTROLS_Y + 12 + (_selector - saves._savegameIndex) * 10),
- TALK_FOREGROUND, "%s", saves._savegames[_selector].c_str());
- }
-
- _oldSelector = _selector;
- }
-
- if (events._released || _keyboardInput) {
- if ((found == 0 && events._released) || _key == 'E') {
- banishWindow();
- _windowBounds.top = CONTROLS_Y1;
-
- events._pressed = events._released = _keyboardInput = false;
- _keyPress = '\0';
- } else if ((found == 1 && events._released) || _key == 'L') {
- saves._envMode = SAVEMODE_LOAD;
- if (_selector != -1) {
- saves.loadGame(_selector + 1);
- }
- } else if ((found == 2 && events._released) || _key == 'S') {
- saves._envMode = SAVEMODE_SAVE;
- if (_selector != -1) {
- if (saves.checkGameOnScreen(_selector))
- _oldSelector = _selector;
-
- if (saves.promptForDescription(_selector)) {
- saves.saveGame(_selector + 1, saves._savegames[_selector]);
-
- banishWindow(1);
- _windowBounds.top = CONTROLS_Y1;
- _key = _oldKey = -1;
- _keyPress = '\0';
- _keyboardInput = false;
- } else {
- if (!talk._talkToAbort) {
- screen._backBuffer1.fillRect(Common::Rect(6, CONTROLS_Y + 11 + (_selector - saves._savegameIndex) * 10,
- SHERLOCK_SCREEN_WIDTH - 2, CONTROLS_Y + 20 + (_selector - saves._savegameIndex) * 10), INV_BACKGROUND);
- screen.gPrint(Common::Point(6, CONTROLS_Y + 11 + (_selector - saves._savegameIndex) * 10), INV_FOREGROUND,
- "%d.", _selector + 1);
- screen.gPrint(Common::Point(24, CONTROLS_Y + 11 + (_selector - saves._savegameIndex) * 10), INV_FOREGROUND,
- "%s", saves._savegames[_selector].c_str());
-
- screen.slamArea(6, CONTROLS_Y + 11 + (_selector - saves._savegameIndex) * 10, 311, 10);
- _selector = _oldSelector = -1;
- }
- }
- }
- } else if (((found == 3 && events._released) || _key == 'U') && saves._savegameIndex) {
- bool moreKeys;
- do {
- saves._savegameIndex--;
- screen._backBuffer1.fillRect(Common::Rect(3, CONTROLS_Y + 11, SHERLOCK_SCREEN_WIDTH - 2,
- SHERLOCK_SCREEN_HEIGHT - 1), INV_BACKGROUND);
-
- for (int idx = saves._savegameIndex; idx < (saves._savegameIndex + ONSCREEN_FILES_COUNT); ++idx) {
- color = INV_FOREGROUND;
- if (idx == _selector && idx >= saves._savegameIndex && idx < (saves._savegameIndex + ONSCREEN_FILES_COUNT))
- color = TALK_FOREGROUND;
-
- screen.gPrint(Common::Point(6, CONTROLS_Y + 11 + (idx - saves._savegameIndex) * 10), color, "%d.", idx + 1);
- screen.gPrint(Common::Point(24, CONTROLS_Y + 11 + (idx - saves._savegameIndex) * 10), color, "%s", saves._savegames[idx].c_str());
- }
-
- screen.slamRect(Common::Rect(3, CONTROLS_Y + 11, SHERLOCK_SCREEN_WIDTH - 2, SHERLOCK_SCREEN_HEIGHT));
-
- color = !saves._savegameIndex ? COMMAND_NULL : COMMAND_FOREGROUND;
- screen.buttonPrint(Common::Point(ENV_POINTS[3][2], CONTROLS_Y), color, true, "Up");
- color = (saves._savegameIndex == MAX_SAVEGAME_SLOTS - ONSCREEN_FILES_COUNT) ? COMMAND_NULL : COMMAND_FOREGROUND;
- screen.buttonPrint(Common::Point(ENV_POINTS[4][2], CONTROLS_Y), color, true, "Down");
-
- // Check whether there are more pending U keys pressed
- moreKeys = false;
- if (events.kbHit()) {
- Common::KeyState keyState = events.getKey();
-
- _key = toupper(keyState.keycode);
- moreKeys = _key == 'U';
- }
- } while ((saves._savegameIndex) && moreKeys);
- } else if (((found == 4 && events._released) || _key == 'D') && saves._savegameIndex < (MAX_SAVEGAME_SLOTS - ONSCREEN_FILES_COUNT)) {
- bool moreKeys;
- do {
- saves._savegameIndex++;
- screen._backBuffer1.fillRect(Common::Rect(3, CONTROLS_Y + 11, SHERLOCK_SCREEN_WIDTH - 2,
- SHERLOCK_SCREEN_HEIGHT - 1), INV_BACKGROUND);
-
- for (int idx = saves._savegameIndex; idx < (saves._savegameIndex + ONSCREEN_FILES_COUNT); ++idx) {
- if (idx == _selector && idx >= saves._savegameIndex && idx < (saves._savegameIndex + ONSCREEN_FILES_COUNT))
- color = TALK_FOREGROUND;
- else
- color = INV_FOREGROUND;
-
- screen.gPrint(Common::Point(6, CONTROLS_Y + 11 + (idx - saves._savegameIndex) * 10), color,
- "%d.", idx + 1);
- screen.gPrint(Common::Point(24, CONTROLS_Y + 11 + (idx - saves._savegameIndex) * 10), color,
- "%s", saves._savegames[idx].c_str());
- }
-
- screen.slamRect(Common::Rect(3, CONTROLS_Y + 11, SHERLOCK_SCREEN_WIDTH - 2, SHERLOCK_SCREEN_HEIGHT));
-
- color = (!saves._savegameIndex) ? COMMAND_NULL : COMMAND_FOREGROUND;
- screen.buttonPrint(Common::Point(ENV_POINTS[3][2], CONTROLS_Y), color, true, "Up");
-
- color = (saves._savegameIndex == MAX_SAVEGAME_SLOTS - ONSCREEN_FILES_COUNT) ? COMMAND_NULL : COMMAND_FOREGROUND;
- screen.buttonPrint(Common::Point(ENV_POINTS[4][2], CONTROLS_Y), color, true, "Down");
-
- // Check whether there are more pending D keys pressed
- moreKeys = false;
- if (events.kbHit()) {
- Common::KeyState keyState;
- _key = toupper(keyState.keycode);
-
- moreKeys = _key == 'D';
- }
- } while (saves._savegameIndex < (MAX_SAVEGAME_SLOTS - ONSCREEN_FILES_COUNT) && moreKeys);
- } else if ((found == 5 && events._released) || _key == 'Q') {
- clearWindow();
- screen.print(Common::Point(0, CONTROLS_Y + 20), INV_FOREGROUND, "Are you sure you wish to Quit ?");
- screen.vgaBar(Common::Rect(0, CONTROLS_Y, SHERLOCK_SCREEN_WIDTH, CONTROLS_Y + 10), BORDER_COLOR);
-
- screen.makeButton(Common::Rect(112, CONTROLS_Y, 160, CONTROLS_Y + 10), 136 - screen.stringWidth("Yes") / 2, "Yes");
- screen.makeButton(Common::Rect(161, CONTROLS_Y, 209, CONTROLS_Y + 10), 184 - screen.stringWidth("No") / 2, "No");
- screen.slamArea(112, CONTROLS_Y, 97, 10);
-
- do {
- scene.doBgAnim();
-
- if (talk._talkToAbort)
- return;
-
- events.pollEventsAndWait();
- events.setButtonState();
- mousePos = events.mousePos();
-
- if (events.kbHit()) {
- Common::KeyState keyState = events.getKey();
- _key = toupper(keyState.keycode);
-
- if (_key == 'X' && (keyState.flags & Common::KBD_ALT) != 0) {
- _vm->quitGame();
- events.pollEvents();
- return;
- }
-
- if (_key == Common::KEYCODE_ESCAPE)
- _key = 'N';
-
- if (_key == Common::KEYCODE_RETURN || _key == ' ') {
- events._pressed = false;
- events._released = true;
- events._oldButtons = 0;
- _keyPress = '\0';
- }
- }
-
- if (events._pressed || events._released) {
- if (mousePos.x > 112 && mousePos.x < 159 && mousePos.y > CONTROLS_Y && mousePos.y < (CONTROLS_Y + 9))
- color = COMMAND_HIGHLIGHTED;
- else
- color = COMMAND_FOREGROUND;
- screen.buttonPrint(Common::Point(136, CONTROLS_Y), color, true, "Yes");
-
- if (mousePos.x > 161 && mousePos.x < 208 && mousePos.y > CONTROLS_Y && mousePos.y < (CONTROLS_Y + 9))
- color = COMMAND_HIGHLIGHTED;
- else
- color = COMMAND_FOREGROUND;
- screen.buttonPrint(Common::Point(184, CONTROLS_Y), color, true, "No");
- }
-
- if (mousePos.x > 112 && mousePos.x < 159 && mousePos.y > CONTROLS_Y && mousePos.y < (CONTROLS_Y + 9) && events._released)
- _key = 'Y';
-
- if (mousePos.x > 161 && mousePos.x < 208 && mousePos.y > CONTROLS_Y && mousePos.y < (CONTROLS_Y + 9) && events._released)
- _key = 'N';
- } while (!_vm->shouldQuit() && _key != 'Y' && _key != 'N');
-
- if (_key == 'Y') {
- _vm->quitGame();
- events.pollEvents();
- return;
- } else {
- screen.buttonPrint(Common::Point(184, CONTROLS_Y), COMMAND_HIGHLIGHTED, true, "No");
- banishWindow(1);
- _windowBounds.top = CONTROLS_Y1;
- _key = -1;
- }
- } else {
- if (_selector != -1) {
- // Are we already in Load mode?
- if (saves._envMode == SAVEMODE_LOAD) {
- saves.loadGame(_selector + 1);
- } else if (saves._envMode == SAVEMODE_SAVE || saves.isSlotEmpty(_selector)) {
- // We're already in save mode, or pointing to an empty save slot
- if (saves.checkGameOnScreen(_selector))
- _oldSelector = _selector;
-
- if (saves.promptForDescription(_selector)) {
- saves.saveGame(_selector + 1, saves._savegames[_selector]);
- banishWindow();
- _windowBounds.top = CONTROLS_Y1;
- _key = _oldKey = -1;
- _keyPress = '\0';
- _keyboardInput = false;
- } else {
- if (!talk._talkToAbort) {
- screen._backBuffer1.fillRect(Common::Rect(6, CONTROLS_Y + 11 + (_selector - saves._savegameIndex) * 10,
- 317, CONTROLS_Y + 20 + (_selector - saves._savegameIndex) * 10), INV_BACKGROUND);
- screen.gPrint(Common::Point(6, CONTROLS_Y + 11 + (_selector - saves._savegameIndex) * 10),
- INV_FOREGROUND, "%d.", _selector + 1);
- screen.gPrint(Common::Point(24, CONTROLS_Y + 11 + (_selector - saves._savegameIndex) * 10),
- INV_FOREGROUND, "%s", saves._savegames[_selector].c_str());
- screen.slamArea(6, CONTROLS_Y + 11 + (_selector - saves._savegameIndex) * 10, 311, 10);
- _selector = _oldSelector = -1;
- }
- }
- }
- }
- }
- }
-}
-
-void UserInterface::doInvControl() {
- Events &events = *_vm->_events;
- Inventory &inv = *_vm->_inventory;
- Scene &scene = *_vm->_scene;
- Screen &screen = *_vm->_screen;
- Talk &talk = *_vm->_talk;
- int colors[8];
- Common::Point mousePos = events.mousePos();
-
- _key = _oldKey = -1;
- _keyboardInput = false;
-
- // Check whether any inventory slot is highlighted
- int found = -1;
- Common::fill(&colors[0], &colors[8], (int)COMMAND_FOREGROUND);
- for (int idx = 0; idx < 8; ++idx) {
- Common::Rect r(INVENTORY_POINTS[idx][0], CONTROLS_Y1,
- INVENTORY_POINTS[idx][1], CONTROLS_Y1 + 10);
- if (r.contains(mousePos)) {
- found = idx;
- break;
- }
- }
-
- if (events._pressed || events._released) {
- events.clearKeyboard();
-
- if (found != -1)
- // If a slot highlighted, set its color
- colors[found] = COMMAND_HIGHLIGHTED;
- screen.buttonPrint(Common::Point(INVENTORY_POINTS[0][2], CONTROLS_Y1), colors[0], true, "Exit");
-
- if (found >= 0 && found <= 3) {
- screen.buttonPrint(Common::Point(INVENTORY_POINTS[1][2], CONTROLS_Y1), colors[1], true, "Look");
- screen.buttonPrint(Common::Point(INVENTORY_POINTS[2][2], CONTROLS_Y1), colors[2], true, "Use");
- screen.buttonPrint(Common::Point(INVENTORY_POINTS[3][2], CONTROLS_Y1), colors[3], true, "Give");
- inv._invMode = (InvMode)found;
- _selector = -1;
- }
-
- if (inv._invIndex) {
- screen.print(Common::Point(INVENTORY_POINTS[4][2], CONTROLS_Y1 + 1), colors[4], "^^");
- screen.print(Common::Point(INVENTORY_POINTS[5][2], CONTROLS_Y1 + 1), colors[5], "^");
- }
-
- if ((inv._holdings - inv._invIndex) > 6) {
- screen.print(Common::Point(INVENTORY_POINTS[6][2], CONTROLS_Y1 + 1), colors[6], "_");
- screen.print(Common::Point(INVENTORY_POINTS[7][2], CONTROLS_Y1 + 1), colors[7], "__");
- }
-
- bool flag = false;
- if (inv._invMode == INVMODE_LOOK || inv._invMode == INVMODE_USE || inv._invMode == INVMODE_GIVE) {
- Common::Rect r(15, CONTROLS_Y1 + 11, 314, SHERLOCK_SCREEN_HEIGHT - 2);
- if (r.contains(mousePos)) {
- _selector = (mousePos.x - 6) / 52 + inv._invIndex;
- if (_selector < inv._holdings)
- flag = true;
- }
- }
-
- if (!flag && mousePos.y >(CONTROLS_Y1 + 11))
- _selector = -1;
- }
-
- if (_keyPress) {
- _key = toupper(_keyPress);
-
- if (_key == Common::KEYCODE_ESCAPE)
- // Escape will also 'E'xit out of inventory display
- _key = 'E';
-
- if (_key == 'E' || _key == 'L' || _key == 'U' || _key == 'G'
- || _key == '-' || _key == '+') {
- InvMode temp = inv._invMode;
-
- const char *chP = strchr(INVENTORY_COMMANDS, _key);
- inv._invMode = !chP ? INVMODE_INVALID : (InvMode)(chP - INVENTORY_COMMANDS);
- inv.invCommands(true);
-
- inv._invMode = temp;
- _keyboardInput = true;
- if (_key == 'E')
- inv._invMode = INVMODE_EXIT;
- _selector = -1;
- } else {
- _selector = -1;
- }
- }
-
- if (_selector != _oldSelector) {
- if (_oldSelector != -1) {
- // Un-highlight
- if (_oldSelector >= inv._invIndex && _oldSelector < (inv._invIndex + 6))
- inv.highlight(_oldSelector, BUTTON_MIDDLE);
- }
-
- if (_selector != -1)
- inv.highlight(_selector, 235);
-
- _oldSelector = _selector;
- }
-
- if (events._released || _keyboardInput) {
- if ((found == 0 && events._released) || _key == 'E') {
- inv.freeInv();
- _infoFlag = true;
- clearInfo();
- banishWindow(false);
- _key = -1;
- events.clearEvents();
- events.setCursor(ARROW);
- } else if ((found == 1 && events._released) || (_key == 'L')) {
- inv._invMode = INVMODE_LOOK;
- } else if ((found == 2 && events._released) || (_key == 'U')) {
- inv._invMode = INVMODE_USE;
- } else if ((found == 3 && events._released) || (_key == 'G')) {
- inv._invMode = INVMODE_GIVE;
- } else if (((found == 4 && events._released) || _key == ',') && inv._invIndex) {
- if (inv._invIndex >= 6)
- inv._invIndex -= 6;
- else
- inv._invIndex = 0;
-
- screen.print(Common::Point(INVENTORY_POINTS[4][2], CONTROLS_Y1 + 1),
- COMMAND_HIGHLIGHTED, "^^");
- inv.freeGraphics();
- inv.loadGraphics();
- inv.putInv(SLAM_DISPLAY);
- inv.invCommands(true);
- } else if (((found == 5 && events._released) || _key == '-') && inv._invIndex > 0) {
- --inv._invIndex;
- screen.print(Common::Point(INVENTORY_POINTS[4][2], CONTROLS_Y1 + 1), COMMAND_HIGHLIGHTED, "^");
- inv.freeGraphics();
- inv.loadGraphics();
- inv.putInv(SLAM_DISPLAY);
- inv.invCommands(true);
- } else if (((found == 6 && events._released) || _key == '+') && (inv._holdings - inv._invIndex) > 6) {
- ++inv._invIndex;
- screen.print(Common::Point(INVENTORY_POINTS[6][2], CONTROLS_Y1 + 1), COMMAND_HIGHLIGHTED, "_");
- inv.freeGraphics();
- inv.loadGraphics();
- inv.putInv(SLAM_DISPLAY);
- inv.invCommands(true);
- } else if (((found == 7 && events._released) || _key == '.') && (inv._holdings - inv._invIndex) > 6) {
- inv._invIndex += 6;
- if ((inv._holdings - 6) < inv._invIndex)
- inv._invIndex = inv._holdings - 6;
-
- screen.print(Common::Point(INVENTORY_POINTS[7][2], CONTROLS_Y1 + 1), COMMAND_HIGHLIGHTED, "_");
- inv.freeGraphics();
- inv.loadGraphics();
- inv.putInv(SLAM_DISPLAY);
- inv.invCommands(true);
- } else {
- // If something is being given, make sure it's being given to a person
- if (inv._invMode == INVMODE_GIVE) {
- if (_bgFound != -1 && scene._bgShapes[_bgFound]._aType == PERSON)
- _find = _bgFound;
- else
- _find = -1;
- } else {
- _find = _bgFound;
- }
-
- if ((mousePos.y < CONTROLS_Y1) && (inv._invMode == INVMODE_LOOK) && (_find >= 0) && (_find < 1000)) {
- if (!scene._bgShapes[_find]._examine.empty() &&
- scene._bgShapes[_find]._examine[0] >= ' ')
- inv.refreshInv();
- } else if (_selector != -1 || _find >= 0) {
- // Selector is the inventory object that was clicked on, or selected.
- // If it's -1, then no inventory item is highlighted yet. Otherwise,
- // an object in the scene has been clicked.
- if (_selector != -1 && inv._invMode == INVMODE_LOOK
- && mousePos.y >(CONTROLS_Y1 + 11))
- inv.refreshInv();
-
- if (talk._talkToAbort)
- return;
-
- // Now check for the Use and Give actions. If inv_mode is INVMODE_GIVE,
- // that means GIVE is in effect, _selector is the object being
- // given, and _find is the target.
- // The same applies to USE, except if _selector is -1, then USE
- // is being tried on an object in the scene without an inventory
- // object being highlighted first.
-
- if ((inv._invMode == INVMODE_USE || (_selector != -1 && inv._invMode == INVMODE_GIVE)) && _find >= 0) {
- events._pressed = events._released = false;
- _infoFlag = true;
- clearInfo();
-
- int tempSel = _selector; // Save the selector
- _selector = -1;
-
- inv.putInv(SLAM_DISPLAY);
- _selector = tempSel; // Restore it
- InvMode tempMode = inv._invMode;
- inv._invMode = INVMODE_USE55;
- inv.invCommands(true);
-
- _infoFlag = true;
- clearInfo();
- banishWindow(false);
- _key = -1;
-
- inv.freeInv();
-
- bool giveFl = (tempMode >= INVMODE_GIVE);
- if (_selector >= 0)
- // Use/Give inv object with scene object
- checkUseAction(&scene._bgShapes[_find]._use[0], inv[_selector]._name, MUSE, _find, giveFl);
- else
- // Now inv object has been highlighted
- checkUseAction(&scene._bgShapes[_find]._use[0], "*SELF*", MUSE, _find, giveFl);
-
- _selector = _oldSelector = -1;
- }
- }
- }
- }
-}
-
-void UserInterface::doLookControl() {
- Events &events = *_vm->_events;
- Inventory &inv = *_vm->_inventory;
- Screen &screen = *_vm->_screen;
-
- _key = _oldKey = -1;
- _keyboardInput = (_keyPress != '\0');
-
- if (events._released || events._rightReleased || _keyboardInput) {
- // Is an inventory object being looked at?
- if (!_invLookFlag) {
- // Is there any remaining text to display?
- if (!_descStr.empty()) {
- printObjectDesc(_descStr, false);
- } else if (!_lookHelp) {
- // Need to close the window and depress the Look button
- Common::Point pt(MENU_POINTS[0][0], MENU_POINTS[0][1]);
- screen._backBuffer2.blitFrom((*_controls)[0], pt);
- banishWindow(true);
-
- _windowBounds.top = CONTROLS_Y1;
- _key = _oldKey = COMMANDS[LOOK_MODE - 1];
- _temp = _oldTemp = 0;
- _menuMode = LOOK_MODE;
- events.clearEvents();
-
- // Restore UI
- drawInterface();
- } else {
- events.setCursor(ARROW);
- banishWindow(true);
- _windowBounds.top = CONTROLS_Y1;
- _key = _oldKey = -1;
- _temp = _oldTemp = 0;
- _menuMode = STD_MODE;
- events.clearEvents();
- }
- } else {
- // Looking at an inventory object
- // Backup the user interface
- Surface tempSurface(SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT - CONTROLS_Y1);
- tempSurface.blitFrom(screen._backBuffer2, Common::Point(0, 0),
- Common::Rect(0, CONTROLS_Y1, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT));
-
- inv.drawInventory(INVENTORY_DONT_DISPLAY);
- banishWindow(true);
-
- // Restore the ui
- screen._backBuffer2.blitFrom(tempSurface, Common::Point(0, CONTROLS_Y1));
-
- _windowBounds.top = CONTROLS_Y1;
- _key = _oldKey = COMMANDS[LOOK_MODE - 1];
- _temp = _oldTemp = 0;
- events.clearEvents();
- _invLookFlag = false;
- _menuMode = INV_MODE;
- _windowOpen = true;
- }
- }
-}
-
-void UserInterface::doMainControl() {
- Events &events = *_vm->_events;
- Inventory &inv = *_vm->_inventory;
- SaveManager &saves = *_vm->_saves;
- Common::Point pt = events.mousePos();
-
- if ((events._pressed || events._released) && pt.y > CONTROLS_Y) {
- events.clearKeyboard();
- _key = -1;
-
- // Check whether the mouse is in any of the command areas
- for (_temp = 0; (_temp < 12) && (_key == -1); ++_temp) {
- Common::Rect r(MENU_POINTS[_temp][0], MENU_POINTS[_temp][1],
- MENU_POINTS[_temp][2], MENU_POINTS[_temp][3]);
- if (r.contains(pt))
- _key = COMMANDS[_temp];
- }
- --_temp;
- } else if (_keyPress) {
- // Keyboard control
- _keyboardInput = true;
-
- if (_keyPress >= 'A' && _keyPress <= 'Z') {
- const char *c = strchr(COMMANDS, _keyPress);
- _temp = !c ? 12 : c - COMMANDS;
- } else {
- _temp = 12;
- }
-
- if (_temp == 12)
- _key = -1;
-
- if (events._rightPressed) {
- _temp = 12;
- _key = -1;
- }
- } else if (!events._released) {
- _key = -1;
- }
-
- // Check if the button being pointed to has changed
- if (_oldKey != _key && !_windowOpen) {
- // Clear the info line
- _infoFlag = true;
- clearInfo();
-
- // If there was an old button selected, restore it
- if (_oldKey != -1) {
- _menuMode = STD_MODE;
- restoreButton(_oldTemp);
- }
-
- // If a new button is being pointed to, highlight it
- if (_key != -1 && _temp < 12 && !_keyboardInput)
- depressButton(_temp);
-
- // Save the new button selection
- _oldKey = _key;
- _oldTemp = _temp;
- }
-
- if (!events._pressed && !_windowOpen) {
- switch (_key) {
- case 'L':
- toggleButton(0);
- break;
- case 'M':
- toggleButton(1);
- break;
- case 'T':
- toggleButton(2);
- break;
- case 'P':
- toggleButton(3);
- break;
- case 'O':
- toggleButton(4);
- break;
- case 'C':
- toggleButton(5);
- break;
- case 'I':
- pushButton(6);
- _selector = _oldSelector = -1;
- _menuMode = INV_MODE;
- inv.drawInventory(PLAIN_INVENTORY);
- break;
- case 'U':
- pushButton(7);
- _selector = _oldSelector = -1;
- _menuMode = USE_MODE;
- inv.drawInventory(USE_INVENTORY_MODE);
- break;
- case 'G':
- pushButton(8);
- _selector = _oldSelector = -1;
- _menuMode = GIVE_MODE;
- inv.drawInventory(GIVE_INVENTORY_MODE);
- break;
- case 'J':
- pushButton(9);
- _menuMode = JOURNAL_MODE;
- journalControl();
- break;
- case 'F':
- pushButton(10);
-
- // Create a thumbnail of the current screen before the files dialog is shown, in case
- // the user saves the game
- saves.createThumbnail();
-
- _selector = _oldSelector = -1;
-
- if (_vm->_showOriginalSavesDialog) {
- // Show the original dialog
- _menuMode = FILES_MODE;
- saves.drawInterface();
- _windowOpen = true;
- } else {
- // Show the ScummVM GMM instead
- _vm->_canLoadSave = true;
- _vm->openMainMenuDialog();
- _vm->_canLoadSave = false;
- }
- break;
- case 'S':
- pushButton(11);
- _menuMode = SETUP_MODE;
- Settings::show(_vm);
- break;
- default:
- break;
- }
-
- _help = _oldHelp = _oldBgFound = -1;
- }
-}
-
-void UserInterface::doMiscControl(int allowed) {
- Events &events = *_vm->_events;
- Scene &scene = *_vm->_scene;
- Talk &talk = *_vm->_talk;
-
- if (events._released) {
- _temp = _bgFound;
- if (_bgFound != -1) {
- // Only allow pointing to objects, not people
- if (_bgFound < 1000) {
- events.clearEvents();
- Object &obj = scene._bgShapes[_bgFound];
-
- switch (allowed) {
- case ALLOW_OPEN:
- checkAction(obj._aOpen, MOPEN, _temp);
- if (_menuMode != TALK_MODE && !talk._talkToAbort) {
- _menuMode = STD_MODE;
- restoreButton(OPEN_MODE - 1);
- _key = _oldKey = -1;
- }
- break;
-
- case ALLOW_CLOSE:
- checkAction(obj._aClose, MCLOSE, _temp);
- if (_menuMode != TALK_MODE && !talk._talkToAbort) {
- _menuMode = STD_MODE;
- restoreButton(CLOSE_MODE - 1);
- _key = _oldKey = -1;
- }
- break;
-
- case ALLOW_MOVE:
- checkAction(obj._aMove, MMOVE, _temp);
- if (_menuMode != TALK_MODE && !talk._talkToAbort) {
- _menuMode = STD_MODE;
- restoreButton(MOVE_MODE - 1);
- _key = _oldKey = -1;
- }
- break;
-
- default:
- break;
- }
- }
- }
- }
-}
-
-void UserInterface::doPickControl() {
- Events &events = *_vm->_events;
- Scene &scene = *_vm->_scene;
- Talk &talk = *_vm->_talk;
-
- if (events._released) {
- if ((_temp = _bgFound) != -1) {
- events.clearEvents();
-
- // Don't allow characters to be picked up
- if (_bgFound < 1000) {
- scene._bgShapes[_bgFound].pickUpObject(MPICK);
-
- if (!talk._talkToAbort && _menuMode != TALK_MODE) {
- _key = _oldKey = -1;
- _menuMode = STD_MODE;
- restoreButton(PICKUP_MODE - 1);
- }
- }
- }
- }
-}
-
-void UserInterface::doTalkControl() {
- Events &events = *_vm->_events;
- Journal &journal = *_vm->_journal;
- People &people = *_vm->_people;
- Screen &screen = *_vm->_screen;
- Sound &sound = *_vm->_sound;
- Talk &talk = *_vm->_talk;
- Common::Point mousePos = events.mousePos();
-
- _key = _oldKey = -1;
- _keyboardInput = false;
-
- if (events._pressed || events._released) {
- events.clearKeyboard();
-
- // Handle button printing
- if (mousePos.x > 99 && mousePos.x < 138 && mousePos.y > CONTROLS_Y && mousePos.y < (CONTROLS_Y + 10) && !_endKeyActive)
- screen.buttonPrint(Common::Point(119, CONTROLS_Y), COMMAND_HIGHLIGHTED, true, "Exit");
- else if (_endKeyActive)
- screen.buttonPrint(Common::Point(119, CONTROLS_Y), COMMAND_FOREGROUND, true, "Exit");
-
- if (mousePos.x > 140 && mousePos.x < 170 && mousePos.y > CONTROLS_Y && mousePos.y < (CONTROLS_Y + 10) && talk._moreTalkUp)
- screen.buttonPrint(Common::Point(159, CONTROLS_Y), COMMAND_HIGHLIGHTED, true, "Up");
- else if (talk._moreTalkUp)
- screen.buttonPrint(Common::Point(159, CONTROLS_Y), COMMAND_FOREGROUND, true, "Up");
-
- if (mousePos.x > 181&& mousePos.x < 220 && mousePos.y > CONTROLS_Y && mousePos.y < (CONTROLS_Y + 10) && talk._moreTalkDown)
- screen.buttonPrint(Common::Point(200, CONTROLS_Y), COMMAND_HIGHLIGHTED, true, "Down");
- else if (talk._moreTalkDown)
- screen.buttonPrint(Common::Point(200, CONTROLS_Y), COMMAND_FOREGROUND, true, "Down");
-
- bool found = false;
- for (_selector = talk._talkIndex; _selector < (int)talk._statements.size() && !found; ++_selector) {
- if (mousePos.y > talk._statements[_selector]._talkPos.top &&
- mousePos.y < talk._statements[_selector]._talkPos.bottom)
- found = true;
- }
- --_selector;
- if (!found)
- _selector = -1;
- }
-
- if (_keyPress) {
- _key = toupper(_keyPress);
- if (_key == Common::KEYCODE_ESCAPE)
- _key = 'E';
-
- // Check for number press indicating reply line
- if (_key >= '1' && _key <= ('1' + (int)talk._statements.size() - 1)) {
- for (uint idx = 0; idx < talk._statements.size(); ++idx) {
- if (talk._statements[idx]._talkMap == (_key - '1')) {
- // Found the given statement
- _selector = idx;
- _key = -1;
- _keyboardInput = true;
- break;
- }
- }
- } else if (_key == 'E' || _key == 'U' || _key == 'D') {
- _keyboardInput = true;
- } else {
- _selector = -1;
- }
- }
-
- if (_selector != _oldSelector) {
- // Remove highlighting from previous line, if any
- if (_oldSelector != -1) {
- if (!((talk._talkHistory[talk._converseNum][_oldSelector] >> (_oldSelector & 7)) & 1))
- talk.talkLine(_oldSelector, talk._statements[_oldSelector]._talkMap, INV_FOREGROUND,
- talk._statements[_oldSelector]._talkPos.top, true);
- else
- talk.talkLine(_oldSelector, talk._statements[_oldSelector]._talkMap, TALK_NULL,
- talk._statements[_oldSelector]._talkPos.top, true);
- }
-
- // Add highlighting to new line, if any
- if (_selector != -1)
- talk.talkLine(_selector, talk._statements[_selector]._talkMap, TALK_FOREGROUND,
- talk._statements[_selector]._talkPos.top, true);
-
- _oldSelector = _selector;
- }
-
- if (events._released || _keyboardInput) {
- if (((Common::Rect(99, CONTROLS_Y, 138, CONTROLS_Y + 10).contains(mousePos) && events._released)
- || _key == 'E') && _endKeyActive) {
- talk.freeTalkVars();
- talk.pullSequence();
- banishWindow();
- _windowBounds.top = CONTROLS_Y1;
- } else if (((Common::Rect(140, CONTROLS_Y, 179, CONTROLS_Y + 10).contains(mousePos) && events._released)
- || _key == 'U') && talk._moreTalkUp) {
- while (talk._statements[--talk._talkIndex]._talkMap == -1)
- ;
- screen._backBuffer1.fillRect(Common::Rect(5, CONTROLS_Y + 11, SHERLOCK_SCREEN_WIDTH - 2,
- SHERLOCK_SCREEN_HEIGHT - 1), INV_BACKGROUND);
- talk.displayTalk(false);
-
- screen.slamRect(Common::Rect(5, CONTROLS_Y, SHERLOCK_SCREEN_WIDTH - 5, SHERLOCK_SCREEN_HEIGHT - 2));
- } else if (((Common::Rect(181, CONTROLS_Y, 220, CONTROLS_Y + 10).contains(mousePos) && events._released)
- || _key == 'D') && talk._moreTalkDown) {
- do {
- ++talk._talkIndex;
- } while (talk._talkIndex < (int)talk._statements.size() && talk._statements[talk._talkIndex]._talkMap == -1);
-
- screen._backBuffer1.fillRect(Common::Rect(5, CONTROLS_Y + 11, SHERLOCK_SCREEN_WIDTH - 2,
- SHERLOCK_SCREEN_HEIGHT - 1), INV_BACKGROUND);
- talk.displayTalk(false);
-
- screen.slamRect(Common::Rect(5, CONTROLS_Y, SHERLOCK_SCREEN_WIDTH - 5, SHERLOCK_SCREEN_HEIGHT - 2));
- } else if (_selector != -1) {
- screen.buttonPrint(Common::Point(119, CONTROLS_Y), COMMAND_NULL, true, "Exit");
- screen.buttonPrint(Common::Point(159, CONTROLS_Y), COMMAND_NULL, true, "Up");
- screen.buttonPrint(Common::Point(200, CONTROLS_Y), COMMAND_NULL, true, "Down");
-
- // If the reply is new, add it to the journal
- if (!talk._talkHistory[talk._converseNum][_selector]) {
- journal.record(talk._converseNum, _selector);
-
- // Add any Holmes point to Holmes' total, if any
- if (talk._statements[_selector]._quotient)
- people._holmesQuotient += talk._statements[_selector]._quotient;
- }
-
- // Flag the response as having been used
- talk._talkHistory[talk._converseNum][_selector] = true;
-
- clearWindow();
- screen.print(Common::Point(16, CONTROLS_Y + 12), TALK_FOREGROUND, "Sherlock Holmes");
- talk.talkLine(_selector + 128, talk._statements[_selector]._talkMap, COMMAND_FOREGROUND, CONTROLS_Y + 21, true);
-
- switch (talk._statements[_selector]._portraitSide & 3) {
- case 0:
- case 1:
- people._portraitSide = 20;
- break;
- case 2:
- people._portraitSide = 220;
- break;
- case 3:
- people._portraitSide = 120;
- break;
- }
-
- // Check for flipping Holmes
- if (talk._statements[_selector]._portraitSide & REVERSE_DIRECTION)
- people._holmesFlip = true;
-
- talk._speaker = 0;
- people.setTalking(0);
-
- if (!talk._statements[_selector]._voiceFile.empty() && sound._voices) {
- sound.playSound(talk._statements[_selector]._voiceFile, WAIT_RETURN_IMMEDIATELY);
-
- // Set voices as an indicator for waiting
- sound._voices = 2;
- sound._speechOn = *sound._soundIsOn;
- } else {
- sound._speechOn = false;
- }
-
- talk.waitForMore(talk._statements[_selector]._statement.size());
- if (talk._talkToAbort)
- return;
-
- people.clearTalking();
- if (talk._talkToAbort)
- return;
-
- while (!_vm->shouldQuit()) {
- talk._scriptSelect = _selector;
- talk._speaker = talk._talkTo;
- talk.doScript(talk._statements[_selector]._reply);
-
- if (!talk._talkToAbort) {
- if (!talk._talkStealth)
- clearWindow();
-
- if (!talk._statements[_selector]._modified.empty()) {
- for (uint idx = 0; idx < talk._statements[_selector]._modified.size(); ++idx) {
- _vm->setFlags(talk._statements[_selector]._modified[idx]);
- }
-
- talk.setTalkMap();
- }
-
- // Check for another linked talk file
- Common::String linkFilename = talk._statements[_selector]._linkFile;
- if (!linkFilename.empty() && !talk._scriptMoreFlag) {
- talk.freeTalkVars();
- talk.loadTalkFile(linkFilename);
-
- // Find the first new statement
- int select = _selector = _oldSelector = -1;
- for (uint idx = 0; idx < talk._statements.size() && select == -1; ++idx) {
- if (!talk._statements[idx]._talkMap)
- select = talk._talkIndex = idx;
- }
-
- // See if the new statement is a stealth reply
- talk._talkStealth = talk._statements[select]._statement.hasPrefix("^") ? 2 : 0;
-
- // Is the new talk file a standard file, reply first file, or a stealth file
- if (!talk._statements[select]._statement.hasPrefix("*") &&
- !talk._statements[select]._statement.hasPrefix("^")) {
- // Not a reply first file, so display the new selections
- if (_endKeyActive)
- screen.buttonPrint(Common::Point(119, CONTROLS_Y), COMMAND_FOREGROUND, true, "Exit");
- else
- screen.buttonPrint(Common::Point(119, CONTROLS_Y), COMMAND_NULL, true, "Exit");
-
- talk.displayTalk(true);
- events.setCursor(ARROW);
- break;
- } else {
- _selector = select;
-
- if (!talk._talkHistory[talk._converseNum][_selector])
- journal.record(talk._converseNum, _selector);
-
- talk._talkHistory[talk._converseNum][_selector] = true;
- }
- } else {
- talk.freeTalkVars();
- talk.pullSequence();
- banishWindow();
- _windowBounds.top = CONTROLS_Y1;
- break;
- }
- } else {
- break;
- }
- }
-
- events._pressed = events._released = false;
- events._oldButtons = 0;
- talk._talkStealth = 0;
-
- // If a script was pushed onto the script stack, restore it
- if (!talk._scriptStack.empty()) {
- ScriptStackEntry stackEntry = talk._scriptStack.pop();
- talk._scriptName = stackEntry._name;
- talk._scriptSaveIndex = stackEntry._currentIndex;
- talk._scriptSelect = stackEntry._select;
- }
- }
- }
-}
-
-void UserInterface::journalControl() {
- Events &events = *_vm->_events;
- Journal &journal = *_vm->_journal;
- Scene &scene = *_vm->_scene;
- Screen &screen = *_vm->_screen;
- bool doneFlag = false;
-
- // Draw the journal screen
- journal.drawInterface();
-
- // Handle journal events
- do {
- _key = -1;
- events.setButtonState();
-
- // Handle keypresses
- if (events.kbHit()) {
- Common::KeyState keyState = events.getKey();
- if (keyState.keycode == Common::KEYCODE_x && (keyState.flags & Common::KBD_ALT)) {
- _vm->quitGame();
- return;
- } else if (keyState.keycode == Common::KEYCODE_e || keyState.keycode == Common::KEYCODE_ESCAPE) {
- doneFlag = true;
- } else {
- _key = toupper(keyState.keycode);
- }
- }
-
- if (!doneFlag)
- doneFlag = journal.handleEvents(_key);
- } while (!_vm->shouldQuit() && !doneFlag);
-
- // Finish up
- _infoFlag = _keyboardInput = false;
- _keyPress = '\0';
- _windowOpen = false;
- _windowBounds.top = CONTROLS_Y1;
- _key = -1;
- _menuMode = STD_MODE;
-
- // Reset the palette
- screen.setPalette(screen._cMap);
-
- screen._backBuffer1.blitFrom(screen._backBuffer2);
- scene.updateBackground();
- screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
-}
-
-void UserInterface::printObjectDesc(const Common::String &str, bool firstTime) {
- Events &events = *_vm->_events;
- Inventory &inv = *_vm->_inventory;
- Screen &screen = *_vm->_screen;
- Talk &talk = *_vm->_talk;
-
- if (str.hasPrefix("_")) {
- _lookScriptFlag = true;
- events.setCursor(MAGNIFY);
- int savedSelector = _selector;
- talk.talkTo(str.c_str() + 1);
- _lookScriptFlag = false;
-
- if (talk._talkToAbort) {
- events.setCursor(ARROW);
- return;
- }
-
- // Check if looking at an inventory object
- if (!_invLookFlag) {
- // See if this look was called by a right button click or not
- if (!_lookHelp) {
- // If it wasn't a right button click, then we need depress
- // the look button before we close the window. So save a copy of the
- // menu area, and draw the controls onto it
- Surface tempSurface((*_controls)[0]._frame.w, (*_controls)[0]._frame.h);
- Common::Point pt(MENU_POINTS[0][0], MENU_POINTS[0][1]);
-
- tempSurface.blitFrom(screen._backBuffer2, Common::Point(0, 0),
- Common::Rect(pt.x, pt.y, pt.x + tempSurface.w(), pt.y + tempSurface.h()));
- screen._backBuffer2.transBlitFrom((*_controls)[0], pt);
-
- banishWindow(1);
- events.setCursor(MAGNIFY);
- _windowBounds.top = CONTROLS_Y1;
- _key = _oldKey = COMMANDS[LOOK_MODE - 1];
- _temp = _oldTemp = 0;
- _menuMode = LOOK_MODE;
- events.clearEvents();
-
- screen._backBuffer2.blitFrom(tempSurface, pt);
- } else {
- events.setCursor(ARROW);
- banishWindow(true);
- _windowBounds.top = CONTROLS_Y1;
- _key = _oldKey = -1;
- _temp = _oldTemp = 0;
- _menuMode = STD_MODE;
- _lookHelp = 0;
- events.clearEvents();
- }
- } else {
- // Looking at an inventory object
- _selector = _oldSelector = savedSelector;
-
- // Reload the inventory graphics and draw the inventory
- inv.loadInv();
- inv.putInv(SLAM_SECONDARY_BUFFER);
- inv.freeInv();
- banishWindow(1);
-
- _windowBounds.top = CONTROLS_Y1;
- _key = _oldKey = COMMANDS[INV_MODE - 1];
- _temp = _oldTemp = 0;
- events.clearEvents();
-
- _invLookFlag = 0;
- _menuMode = INV_MODE;
- _windowOpen = true;
- }
-
- return;
- }
-
- Surface &bb = *screen._backBuffer;
- if (firstTime) {
- // Only draw the border on the first call
- _infoFlag = true;
- clearInfo();
-
- bb.fillRect(Common::Rect(0, CONTROLS_Y, SHERLOCK_SCREEN_WIDTH,
- CONTROLS_Y1 + 10), BORDER_COLOR);
- bb.fillRect(Common::Rect(0, CONTROLS_Y + 10, 1, SHERLOCK_SCREEN_HEIGHT - 1),
- BORDER_COLOR);
- bb.fillRect(Common::Rect(SHERLOCK_SCREEN_WIDTH - 2, CONTROLS_Y + 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);
- }
-
- // Clear background
- bb.fillRect(Common::Rect(2, CONTROLS_Y + 10, SHERLOCK_SCREEN_WIDTH - 2,
- SHERLOCK_SCREEN_HEIGHT - 2), INV_BACKGROUND);
-
- _windowBounds.top = CONTROLS_Y;
- events.clearEvents();
-
- // Loop through displaying up to five lines
- bool endOfStr = false;
- const char *msgP = str.c_str();
- for (int lineNum = 0; lineNum < ONSCREEN_FILES_COUNT && !endOfStr; ++lineNum) {
- int width = 0;
- const char *lineStartP = msgP;
-
- // Determine how much can be displayed on the line
- do {
- width += screen.charWidth(*msgP++);
- } while (width < 300 && *msgP);
-
- if (*msgP)
- --msgP;
- else
- endOfStr = true;
-
- // If the line needs to be wrapped, scan backwards to find
- // the end of the previous word as a splitting point
- if (width >= 300) {
- while (*msgP != ' ')
- --msgP;
- endOfStr = false;
- }
-
- // Print out the line
- Common::String line(lineStartP, msgP);
- screen.gPrint(Common::Point(16, CONTROLS_Y + 12 + lineNum * 9),
- INV_FOREGROUND, "%s", line.c_str());
-
- if (!endOfStr)
- // Start next line at start of the nxet word after space
- ++msgP;
- }
-
- // Handle display depending on whether all the message was shown
- if (!endOfStr) {
- screen.makeButton(Common::Rect(46, CONTROLS_Y, 272, CONTROLS_Y + 10),
- (SHERLOCK_SCREEN_WIDTH - screen.stringWidth(PRESS_KEY_FOR_MORE)) / 2,
- PRESS_KEY_FOR_MORE);
- screen.gPrint(Common::Point((SHERLOCK_SCREEN_WIDTH -
- screen.stringWidth(PRESS_KEY_FOR_MORE)) / 2, CONTROLS_Y),
- COMMAND_FOREGROUND, "P");
- _descStr = msgP;
- } else {
- screen.makeButton(Common::Rect(46, CONTROLS_Y, 272, CONTROLS_Y + 10),
- (SHERLOCK_SCREEN_WIDTH - screen.stringWidth(PRESS_KEY_TO_CONTINUE)) / 2,
- PRESS_KEY_TO_CONTINUE);
- screen.gPrint(Common::Point((SHERLOCK_SCREEN_WIDTH -
- screen.stringWidth(PRESS_KEY_TO_CONTINUE)) / 2, CONTROLS_Y),
- COMMAND_FOREGROUND, "P");
- _descStr = "";
- }
-
- if (firstTime) {
- if (!_slideWindows) {
- screen.slamRect(Common::Rect(0, CONTROLS_Y,
- SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT));
- } else {
- // Display the window
- summonWindow();
- }
-
- _selector = _oldSelector = -1;
- _windowOpen = true;
- } else {
- screen.slamRect(Common::Rect(0, CONTROLS_Y, SHERLOCK_SCREEN_WIDTH,
- SHERLOCK_SCREEN_HEIGHT));
- }
-}
-
-void UserInterface::printObjectDesc() {
- printObjectDesc(_cAnimStr, true);
-}
-
-void UserInterface::summonWindow(const Surface &bgSurface, bool slideUp) {
- Events &events = *_vm->_events;
- Screen &screen = *_vm->_screen;
-
- if (_windowOpen)
- // A window is already open, so can't open another one
- return;
-
- if (slideUp) {
- // Gradually slide up the display of the window
- for (int idx = 1; idx <= bgSurface.h(); idx += 2) {
- screen._backBuffer->blitFrom(bgSurface, Common::Point(0, SHERLOCK_SCREEN_HEIGHT - idx),
- Common::Rect(0, 0, bgSurface.w(), idx));
- screen.slamRect(Common::Rect(0, SHERLOCK_SCREEN_HEIGHT - idx,
- SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT));
-
- events.delay(10);
- }
- } else {
- // Gradually slide down the display of the window
- for (int idx = 1; idx <= bgSurface.h(); idx += 2) {
- screen._backBuffer->blitFrom(bgSurface,
- Common::Point(0, SHERLOCK_SCREEN_HEIGHT - bgSurface.h()),
- Common::Rect(0, bgSurface.h() - idx, bgSurface.w(), bgSurface.h()));
- screen.slamRect(Common::Rect(0, SHERLOCK_SCREEN_HEIGHT - bgSurface.h(),
- SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT - bgSurface.h() + idx));
-
- events.delay(10);
- }
- }
-
- // Final display of the entire window
- screen._backBuffer->blitFrom(bgSurface, Common::Point(0, SHERLOCK_SCREEN_HEIGHT - bgSurface.h()),
- Common::Rect(0, 0, bgSurface.w(), bgSurface.h()));
- screen.slamArea(0, SHERLOCK_SCREEN_HEIGHT - bgSurface.h(), bgSurface.w(), bgSurface.h());
-
- _windowOpen = true;
-}
-
-void UserInterface::summonWindow(bool slideUp, int height) {
- Screen &screen = *_vm->_screen;
-
- // Extract the window that's been drawn on the back buffer
- Surface tempSurface(SHERLOCK_SCREEN_WIDTH,
- (SHERLOCK_SCREEN_HEIGHT - height));
- Common::Rect r(0, height, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
- tempSurface.blitFrom(screen._backBuffer1, Common::Point(0, 0), r);
-
- // Remove drawn window with original user interface
- screen._backBuffer1.blitFrom(screen._backBuffer2,
- Common::Point(0, height), r);
-
- // Display the window gradually on-screen
- summonWindow(tempSurface, slideUp);
-}
-
-void UserInterface::banishWindow(bool slideUp) {
- Events &events = *_vm->_events;
- Screen &screen = *_vm->_screen;
-
- if (_windowOpen) {
- if (slideUp || !_slideWindows) {
- // Slide window down
- // Only slide the window if the window style allows it
- if (_slideWindows) {
- for (int idx = 2; idx < (SHERLOCK_SCREEN_HEIGHT - CONTROLS_Y); idx += 2) {
- // Shift the window down by 2 lines
- byte *pSrc = (byte *)screen._backBuffer1.getBasePtr(0, CONTROLS_Y + idx - 2);
- byte *pSrcEnd = (byte *)screen._backBuffer1.getBasePtr(0, SHERLOCK_SCREEN_HEIGHT - 2);
- byte *pDest = (byte *)screen._backBuffer1.getBasePtr(0, SHERLOCK_SCREEN_HEIGHT);
- Common::copy_backward(pSrc, pSrcEnd, pDest);
-
- // Restore lines from the ui in the secondary back buffer
- screen._backBuffer1.blitFrom(screen._backBuffer2,
- Common::Point(0, CONTROLS_Y),
- Common::Rect(0, CONTROLS_Y, SHERLOCK_SCREEN_WIDTH, CONTROLS_Y + idx));
-
- screen.slamArea(0, CONTROLS_Y + idx - 2, SHERLOCK_SCREEN_WIDTH,
- SHERLOCK_SCREEN_HEIGHT - CONTROLS_Y - idx + 2);
- events.delay(10);
- }
-
- // Restore final two old lines
- screen._backBuffer1.blitFrom(screen._backBuffer2,
- Common::Point(0, SHERLOCK_SCREEN_HEIGHT - 2),
- Common::Rect(0, SHERLOCK_SCREEN_HEIGHT - 2,
- SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT));
- screen.slamArea(0, SHERLOCK_SCREEN_HEIGHT - 2, SHERLOCK_SCREEN_WIDTH, 2);
- } else {
- // Restore old area to completely erase window
- screen._backBuffer1.blitFrom(screen._backBuffer2,
- Common::Point(0, CONTROLS_Y),
- Common::Rect(0, CONTROLS_Y, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT));
- screen.slamRect(Common::Rect(0, CONTROLS_Y, SHERLOCK_SCREEN_WIDTH,
- SHERLOCK_SCREEN_HEIGHT));
- }
- } else {
- // Slide the original user interface up to cover the dialog
- for (int idx = 1; idx < (SHERLOCK_SCREEN_HEIGHT - CONTROLS_Y1); idx += 2) {
- byte *pSrc = (byte *)screen._backBuffer2.getBasePtr(0, CONTROLS_Y1);
- byte *pSrcEnd = (byte *)screen._backBuffer2.getBasePtr(0, CONTROLS_Y1 + idx);
- byte *pDest = (byte *)screen._backBuffer1.getBasePtr(0, SHERLOCK_SCREEN_HEIGHT - idx);
- Common::copy(pSrc, pSrcEnd, pDest);
-
- screen.slamArea(0, SHERLOCK_SCREEN_HEIGHT - idx, SHERLOCK_SCREEN_WIDTH,
- SHERLOCK_SCREEN_HEIGHT);
- events.delay(10);
- }
-
- // Show entire final area
- screen._backBuffer1.blitFrom(screen._backBuffer2, Common::Point(0, CONTROLS_Y1),
- Common::Rect(0, CONTROLS_Y1, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT));
- screen.slamRect(Common::Rect(0, CONTROLS_Y1, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT));
- }
-
- _infoFlag = false;
- _windowOpen = false;
- }
-
- _menuMode = STD_MODE;
-}
-
-void UserInterface::checkUseAction(const UseType *use, const Common::String &invName,
- const char *const messages[], int objNum, bool giveMode) {
- Events &events = *_vm->_events;
- Inventory &inv = *_vm->_inventory;
- Scene &scene = *_vm->_scene;
- Screen &screen = *_vm->_screen;
- Talk &talk = *_vm->_talk;
- bool printed = messages == nullptr;
-
- if (objNum >= 1000) {
- // Holmes was specified, so do nothing
- _infoFlag = true;
- clearInfo();
- _infoFlag = true;
-
- // Display error message
- _menuCounter = 30;
- screen.print(Common::Point(0, INFO_LINE + 1), INFO_FOREGROUND, "You can't do that to yourself.");
- return;
- }
-
- // Scan for target item
- int targetNum = -1;
- if (giveMode) {
- for (int idx = 0; idx < USE_COUNT && targetNum == -1; ++idx) {
- if ((use[idx]._target.equalsIgnoreCase("*GIVE*") || use[idx]._target.equalsIgnoreCase("*GIVEP*"))
- && use[idx]._names[0].equalsIgnoreCase(invName)) {
- // Found a match
- targetNum = idx;
- if (use[idx]._target.equalsIgnoreCase("*GIVE*"))
- inv.deleteItemFromInventory(invName);
- }
- }
- } else {
- for (int idx = 0; idx < USE_COUNT && targetNum == -1; ++idx) {
- if (use[idx]._target.equalsIgnoreCase(invName))
- targetNum = idx;
- }
- }
-
- if (targetNum != -1) {
- // Found a target, so do the action
- const UseType &action = use[targetNum];
-
- events.setCursor(WAIT);
-
- if (action._useFlag)
- _vm->setFlags(action._useFlag);
-
- if (action._cAnimNum != 99) {
- if (action._cAnimNum == 0)
- scene.startCAnim(9, action._cAnimSpeed);
- else
- scene.startCAnim(action._cAnimNum - 1, action._cAnimSpeed);
- }
-
- if (!talk._talkToAbort) {
- Object &obj = scene._bgShapes[objNum];
- for (int idx = 0; idx < NAMES_COUNT && !talk._talkToAbort; ++idx) {
- if (obj.checkNameForCodes(action._names[idx], messages)) {
- if (!talk._talkToAbort)
- printed = true;
- }
- }
-
- // Print "Done..." as an ending, unless flagged for leaving scene or otherwise flagged
- if (scene._goToScene != 1 && !printed && !talk._talkToAbort) {
- _infoFlag = true;
- clearInfo();
- screen.print(Common::Point(0, INFO_LINE + 1), INFO_FOREGROUND, "Done...");
- _menuCounter = 25;
- }
- }
- } else {
- // Couldn't find target, so print error
- _infoFlag = true;
- clearInfo();
-
- if (giveMode) {
- screen.print(Common::Point(0, INFO_LINE + 1), INFO_FOREGROUND, "No, thank you.");
- } else if (messages == nullptr) {
- screen.print(Common::Point(0, INFO_LINE + 1), INFO_FOREGROUND, "You can't do that.");
- } else {
- screen.print(Common::Point(0, INFO_LINE + 1), INFO_FOREGROUND, "%s", messages[0]);
- }
-
- _infoFlag = true;
- _menuCounter = 30;
- }
-
- events.setCursor(ARROW);
-}
-
-void UserInterface::checkAction(ActionType &action, const char *const messages[], int objNum) {
- Events &events = *_vm->_events;
- People &people = *_vm->_people;
- Scene &scene = *_vm->_scene;
- Screen &screen = *_vm->_screen;
- Talk &talk = *_vm->_talk;
- Common::Point pt(-1, -1);
-
- if (objNum >= 1000)
- // Ignore actions done on characters
- return;
-
- if (!action._cAnimSpeed) {
- // Invalid action, to print error message
- _infoFlag = true;
- clearInfo();
- screen.print(Common::Point(0, INFO_LINE + 1), INFO_FOREGROUND, "%s", messages[action._cAnimNum]);
- _infoFlag = true;
-
- // Set how long to show the message
- _menuCounter = 30;
- } else {
- Object &obj = scene._bgShapes[objNum];
-
- int cAnimNum;
- if (action._cAnimNum == 0)
- // Really a 10
- cAnimNum = 9;
- else
- cAnimNum = action._cAnimNum - 1;
-
- int dir = -1;
- if (action._cAnimNum != 99) {
- CAnim &anim = scene._cAnim[cAnimNum];
-
- if (action._cAnimNum != 99) {
- if (action._cAnimSpeed & REVERSE_DIRECTION) {
- pt = anim._teleportPos;
- dir = anim._teleportDir;
- } else {
- pt = anim._goto;
- dir = anim._gotoDir;
- }
- }
- } else {
- pt = Common::Point(-1, -1);
- dir = -1;
- }
-
- // Has a value, so do action
- // Show wait cursor whilst walking to object and doing action
- events.setCursor(WAIT);
- bool printed = false;
-
- for (int nameIdx = 0; nameIdx < NAMES_COUNT; ++nameIdx) {
- if (action._names[nameIdx].hasPrefix("*") && action._names[nameIdx].size() >= 2
- && toupper(action._names[nameIdx][1]) == 'W') {
- if (obj.checkNameForCodes(Common::String(action._names[nameIdx].c_str() + 2), messages)) {
- if (!talk._talkToAbort)
- printed = true;
- }
- }
- }
-
- bool doCAnim = true;
- for (int nameIdx = 0; nameIdx < NAMES_COUNT; ++nameIdx) {
- if (action._names[nameIdx].hasPrefix("*") && action._names[nameIdx].size() >= 2) {
- char ch = toupper(action._names[nameIdx][1]);
-
- if (ch == 'T' || ch == 'B') {
- printed = true;
- if (pt.x != -1)
- // Holmes needs to walk to object before the action is done
- people.walkToCoords(pt, dir);
-
- if (!talk._talkToAbort) {
- // Ensure Holmes is on the exact intended location
- people[AL]._position = pt;
- people[AL]._sequenceNumber = dir;
- people.gotoStand(people[AL]);
-
- talk.talkTo(action._names[nameIdx].c_str() + 2);
- if (ch == 'T')
- doCAnim = false;
- }
- }
- }
- }
-
- if (doCAnim && !talk._talkToAbort) {
- if (pt.x != -1)
- // Holmes needs to walk to object before the action is done
- people.walkToCoords(pt, dir);
- }
-
- for (int nameIdx = 0; nameIdx < NAMES_COUNT; ++nameIdx) {
- if (action._names[nameIdx].hasPrefix("*") && action._names[nameIdx].size() >= 2
- && toupper(action._names[nameIdx][1]) == 'F') {
- if (obj.checkNameForCodes(action._names[nameIdx].c_str() + 2, messages)) {
- if (!talk._talkToAbort)
- printed = true;
- }
- }
- }
-
- if (doCAnim && !talk._talkToAbort && action._cAnimNum != 99)
- scene.startCAnim(cAnimNum, action._cAnimSpeed);
-
- if (!talk._talkToAbort) {
- for (int nameIdx = 0; nameIdx < NAMES_COUNT && !talk._talkToAbort; ++nameIdx) {
- if (obj.checkNameForCodes(action._names[nameIdx], messages)) {
- if (!talk._talkToAbort)
- printed = true;
- }
- }
-
- // Unless we're leaving the scene, print a "Done" message unless the printed flag has been set
- if (scene._goToScene != 1 && !printed && !talk._talkToAbort) {
- _infoFlag = true;
- clearInfo();
- screen.print(Common::Point(0, INFO_LINE + 1), INFO_FOREGROUND, "Done...");
-
- // Set how long to show the message
- _menuCounter = 30;
- }
- }
- }
-
- // Reset cursor back to arrow
- events.setCursor(ARROW);
+ _key = _oldKey = '\0';
+ _selector = _oldSelector = -1;
+ _temp = _oldTemp = 0;
+ _temp1 = 0;
+ _lookHelp = 0;
}
} // End of namespace Sherlock
diff --git a/engines/sherlock/user_interface.h b/engines/sherlock/user_interface.h
index 1f7b5feaab..042997a3e2 100644
--- a/engines/sherlock/user_interface.h
+++ b/engines/sherlock/user_interface.h
@@ -50,134 +50,11 @@ enum MenuMode {
SETUP_MODE = 12
};
-extern const char COMMANDS[13];
-extern const int MENU_POINTS[12][4];
-
-extern const int INVENTORY_POINTS[8][3];
-extern const char INVENTORY_COMMANDS[9];
-extern const char *const PRESS_KEY_FOR_MORE;
-extern const char *const PRESS_KEY_TO_CONTINUE;
-
-class SherlockEngine;
-class Inventory;
-class Talk;
-class UserInterface;
-
class UserInterface {
- friend class Inventory;
- friend class Settings;
- friend class Talk;
-private:
+protected:
SherlockEngine *_vm;
- ImageFile *_controlPanel;
- ImageFile *_controls;
- int _bgFound;
- int _oldBgFound;
- char _keyPress;
- int _lookHelp;
- int _help, _oldHelp;
- char _key, _oldKey;
- int _temp, _oldTemp;
- int _oldLook;
- bool _keyboardInput;
- bool _pause;
- int _cNum;
- int _selector, _oldSelector;
- Common::String _cAnimStr;
- bool _lookScriptFlag;
- Common::Rect _windowBounds;
- Common::String _descStr;
- int _find;
- int _oldUse;
-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(int num);
-
- /**
- * Creates a text window and uses it to display the in-depth description
- * of the highlighted object
- */
- void examine();
- /**
- * 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 it's 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, const char *const messages[],
- int objNum, bool giveMode);
-
- /**
- * Called for OPEN, CLOSE, and MOVE actions are being done
- */
- void checkAction(ActionType &action, const char *const messages[], int objNum);
+ UserInterface(SherlockEngine *vm);
public:
MenuMode _menuMode;
int _menuCounter;
@@ -185,74 +62,67 @@ public:
bool _windowOpen;
bool _endKeyActive;
int _invLookFlag;
- int _temp1;
bool _slideWindows;
bool _helpStyle;
+ Common::Rect _windowBounds;
+ bool _lookScriptFlag;
+
+ // TODO: Not so sure these should be in the base class. May want to refactor them to SherlockEngine, or refactor
+ // various Scalpel dialogs to keep their own private state of key/selections
+ char _key, _oldKey;
+ int _selector, _oldSelector;
+ int _temp, _oldTemp;
+ int _temp1;
+ int _lookHelp;
public:
- UserInterface(SherlockEngine *vm);
- ~UserInterface();
+ static UserInterface *init(SherlockEngine *vm);
+ virtual ~UserInterface() {}
/**
* Resets the user interface
*/
- void reset();
+ virtual void reset() {}
/**
* Draw the user interface onto the screen's back buffers
- */
- void drawInterface(int bufferNum = 3);
+ */
+ virtual void drawInterface(int bufferNum = 3) {}
/**
* Main input handler for the user interface
*/
- void handleInput();
+ virtual void handleInput() {}
/**
- * Clears the info line of the screen
+ * Displays a passed window by gradually scrolling it vertically on-screen
*/
- void clearInfo();
+ virtual void summonWindow(const Surface &bgSurface, bool slideUp = true) {}
/**
- * Clear any active text window
+ * Slide the window stored in the back buffer onto the screen
*/
- void clearWindow();
+ virtual void summonWindow(bool slideUp = true, int height = CONTROLS_Y) {}
/**
- * Handles counting down whilst checking for input, then clears the info line.
+ * Close a currently open window
+ * @param flag 0 = slide old window down, 1 = slide prior UI back up
*/
- void whileMenuCounter();
+ virtual void banishWindow(bool slideUp = true) {}
/**
- * Print the description of an object
- */
- void printObjectDesc(const Common::String &str, bool firstTime);
-
- /**
- * Print the previously selected object's decription
+ * Clears the info line of the screen
*/
- void printObjectDesc();
+ virtual void clearInfo() {}
/**
- * Displays a passed window by gradually scrolling it vertically on-screen
- */
- void summonWindow(const Surface &bgSurface, bool slideUp = true);
-
- /**
- * Slide the window stored in the back buffer onto the screen
- */
- void summonWindow(bool slideUp = true, int height = CONTROLS_Y);
-
- /**
- * Close a currently open window
- * @param flag 0 = slide old window down, 1 = slide prior UI back up
+ * Clear any active text window
*/
- void banishWindow(bool slideUp = true);
+ virtual void clearWindow() {}
/**
- * Draws the image for the given user interface button in the up
- * (not selected) position
+ * Print the previously selected object's decription
*/
- void restoreButton(int num);
+ virtual void printObjectDesc() {}
};
} // End of namespace Sherlock