aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Gilbert2015-07-22 20:26:28 -0400
committerPaul Gilbert2015-07-22 20:26:28 -0400
commit71296d6461bd8346793ff939b5d2e84d22c58dfa (patch)
treec2615022f05f6b0ffbe4bba30a340eb517ec7fa6
parent1a680ddb134a6fe2bb70a07c5bd08d3f833b66e2 (diff)
downloadscummvm-rg350-71296d6461bd8346793ff939b5d2e84d22c58dfa.tar.gz
scummvm-rg350-71296d6461bd8346793ff939b5d2e84d22c58dfa.tar.bz2
scummvm-rg350-71296d6461bd8346793ff939b5d2e84d22c58dfa.zip
SHERLOCK: RT: Split TattooEngine code into separate widget classes
-rw-r--r--engines/sherlock/module.mk2
-rw-r--r--engines/sherlock/tattoo/tattoo.cpp398
-rw-r--r--engines/sherlock/tattoo/tattoo.h35
-rw-r--r--engines/sherlock/tattoo/tattoo_scene.cpp4
-rw-r--r--engines/sherlock/tattoo/tattoo_user_interface.cpp16
-rw-r--r--engines/sherlock/tattoo/tattoo_user_interface.h6
-rw-r--r--engines/sherlock/tattoo/widget_credits.cpp210
-rw-r--r--engines/sherlock/tattoo/widget_credits.h84
-rw-r--r--engines/sherlock/tattoo/widget_hangman.cpp264
-rw-r--r--engines/sherlock/tattoo/widget_hangman.h48
10 files changed, 625 insertions, 442 deletions
diff --git a/engines/sherlock/module.mk b/engines/sherlock/module.mk
index 32c5d3acc3..4c12fc7a00 100644
--- a/engines/sherlock/module.mk
+++ b/engines/sherlock/module.mk
@@ -33,6 +33,8 @@ MODULE_OBJS = \
tattoo/tattoo_talk.o \
tattoo/tattoo_user_interface.o \
tattoo/widget_base.o \
+ tattoo/widget_credits.o \
+ tattoo/widget_hangman.o \
tattoo/widget_inventory.o \
tattoo/widget_lab.o \
tattoo/widget_talk.o \
diff --git a/engines/sherlock/tattoo/tattoo.cpp b/engines/sherlock/tattoo/tattoo.cpp
index 90d2e5d958..920bef1a4c 100644
--- a/engines/sherlock/tattoo/tattoo.cpp
+++ b/engines/sherlock/tattoo/tattoo.cpp
@@ -34,13 +34,11 @@ namespace Sherlock {
namespace Tattoo {
TattooEngine::TattooEngine(OSystem *syst, const SherlockGameDescription *gameDesc) :
- SherlockEngine(syst, gameDesc), _darts(this) {
- _creditsActive = false;
+ SherlockEngine(syst, gameDesc), _darts(this), _hangmanWidget(this) {
_runningProlog = false;
_fastMode = false;
_allowFastMode = true;
_transparentMenus = true;
- _creditSpeed = 4;
}
TattooEngine::~TattooEngine() {
@@ -175,400 +173,8 @@ void TattooEngine::loadInventory() {
inv.push_back(InventoryItem(0, inv8, invDesc8, "_LANT02I"));
}
-void TattooEngine::initCredits() {
- Common::SeekableReadStream *stream = _res->load("credits.txt");
- int spacing = _screen->fontHeight() * 2;
- int yp = _screen->h();
-
- _creditsActive = true;
- _creditLines.clear();
-
- while (stream->pos() < stream->size()) {
- Common::String line = stream->readLine();
-
- if (line.hasPrefix("Scroll Speed")) {
- const char *p = line.c_str() + 12;
- while ((*p < '0') || (*p > '9'))
- p++;
-
- _creditSpeed = atoi(p);
- } else if (line.hasPrefix("Y Spacing")) {
- const char *p = line.c_str() + 12;
- while ((*p < '0') || (*p > '9'))
- p++;
-
- spacing = atoi(p) + _screen->fontHeight() + 1;
- } else {
- int width = _screen->stringWidth(line) + 2;
-
- _creditLines.push_back(CreditLine(line, Common::Point((_screen->w() - width) / 2 + 1, yp), width));
- yp += spacing;
- }
- }
-
- // Post-processing for finding split lines
- for (int l = 0; l < (int)_creditLines.size(); ++l) {
- CreditLine &cl = _creditLines[l];
- const char *p = strchr(cl._line.c_str(), '-');
-
- if (p != nullptr && p[1] == '>') {
- cl._line2 = Common::String(p + 3);
- cl._line = Common::String(cl._line.c_str(), p);
-
- int width = cl._width;
- int width1 = _screen->stringWidth(cl._line);
- int width2 = _screen->stringWidth(cl._line2);
-
- int c = 1;
- for (int l1 = l + 1; l1 < (int)_creditLines.size(); ++l1) {
- if ((p = strchr(_creditLines[l1]._line.c_str(), '-')) != nullptr) {
- if (p[1] == '>') {
- Common::String line1 = Common::String(_creditLines[l1]._line.c_str(), p);
- Common::String line2 = Common::String(p + 3);
-
- width1 = MAX(width1, _screen->stringWidth(line1));
-
- if (_screen->stringWidth(line2) > width2)
- width2 = _screen->stringWidth(line2);
- ++c;
- } else {
- break;
- }
- } else {
- break;
- }
- }
-
- width = width1 + width2 + _screen->widestChar();
- width1 += _screen->widestChar();
-
- for (int l1 = l; l1 < l + c; ++l1) {
- _creditLines[l1]._width = width;
- _creditLines[l1]._xOffset = width1;
- }
-
- l += c - 1;
- }
- }
-
- delete stream;
-}
-
-void TattooEngine::drawCredits() {
- Common::Rect screenRect(0, 0, _screen->w(), _screen->h());
- Surface &bb1 = _screen->_backBuffer1;
-
- for (uint idx = 0; idx < _creditLines.size() && _creditLines[idx]._position.y < _screen->h(); ++idx) {
- if (screenRect.contains(_creditLines[idx]._position)) {
- if (!_creditLines[idx]._line2.empty()) {
- int x1 = _creditLines[idx]._position.x;
- int x2 = x1 + _creditLines[idx]._xOffset;
- const Common::String &line1 = _creditLines[idx]._line;
- const Common::String &line2 = _creditLines[idx]._line2;
-
- bb1.writeString(line1, Common::Point(x1 - 1, _creditLines[idx]._position.y - 1), 0);
- bb1.writeString(line1, Common::Point(x1, _creditLines[idx]._position.y - 1), 0);
- bb1.writeString(line1, Common::Point(x1 + 1, _creditLines[idx]._position.y - 1), 0);
-
- bb1.writeString(line1, Common::Point(x1 - 1, _creditLines[idx]._position.y), 0);
- bb1.writeString(line1, Common::Point(x1 + 1, _creditLines[idx]._position.y), 0);
-
- bb1.writeString(line1, Common::Point(x1 - 1, _creditLines[idx]._position.y + 1), 0);
- bb1.writeString(line1, Common::Point(x1, _creditLines[idx]._position.y + 1), 0);
- bb1.writeString(line1, Common::Point(x1 + 1, _creditLines[idx]._position.y + 1), 0);
-
- bb1.writeString(line1, Common::Point(x1, _creditLines[idx]._position.y), INFO_TOP);
-
- bb1.writeString(line2, Common::Point(x2 - 1, _creditLines[idx]._position.y - 1), 0);
- bb1.writeString(line2, Common::Point(x2, _creditLines[idx]._position.y - 1), 0);
- bb1.writeString(line2, Common::Point(x2 + 1, _creditLines[idx]._position.y - 1), 0);
-
- bb1.writeString(line2, Common::Point(x2 - 1, _creditLines[idx]._position.y), 0);
- bb1.writeString(line2, Common::Point(x2 + 1, _creditLines[idx]._position.y), 0);
-
- bb1.writeString(line2, Common::Point(x2 - 1, _creditLines[idx]._position.y + 1), 0);
- bb1.writeString(line2, Common::Point(x2, _creditLines[idx]._position.y + 1), 0);
- bb1.writeString(line2, Common::Point(x2 + 1, _creditLines[idx]._position.y + 1), 0);
-
- bb1.writeString(line2, Common::Point(x2, _creditLines[idx]._position.y), INFO_TOP);
- } else {
- bb1.writeString(_creditLines[idx]._line, Common::Point(_creditLines[idx]._position.x - 1, _creditLines[idx]._position.y - 1), 0);
- bb1.writeString(_creditLines[idx]._line, Common::Point(_creditLines[idx]._position.x, _creditLines[idx]._position.y - 1), 0);
- bb1.writeString(_creditLines[idx]._line, Common::Point(_creditLines[idx]._position.x + 1, _creditLines[idx]._position.y - 1), 0);
-
- bb1.writeString(_creditLines[idx]._line, Common::Point(_creditLines[idx]._position.x - 1, _creditLines[idx]._position.y), 0);
- bb1.writeString(_creditLines[idx]._line, Common::Point(_creditLines[idx]._position.x + 1, _creditLines[idx]._position.y), 0);
-
- bb1.writeString(_creditLines[idx]._line, Common::Point(_creditLines[idx]._position.x - 1, _creditLines[idx]._position.y + 1), 0);
- bb1.writeString(_creditLines[idx]._line, Common::Point(_creditLines[idx]._position.x, _creditLines[idx]._position.y + 1), 0);
- bb1.writeString(_creditLines[idx]._line, Common::Point(_creditLines[idx]._position.x + 1, _creditLines[idx]._position.y + 1), 0);
-
- bb1.writeString(_creditLines[idx]._line, Common::Point(_creditLines[idx]._position.x, _creditLines[idx]._position.y), INFO_TOP);
- }
- }
- }
-}
-
-void TattooEngine::blitCredits() {
- Common::Rect screenRect(0, -_creditSpeed, _screen->w(), _screen->h() + _creditSpeed);
-
- for (uint idx = 0; idx < _creditLines.size(); ++idx) {
- if (screenRect.contains(_creditLines[idx]._position)) {
- Common::Rect r(_creditLines[idx]._width, _screen->fontHeight() + 2);
- r.moveTo(_creditLines[idx]._position.x, _creditLines[idx]._position.y - 1);
-
- _screen->slamRect(r);
- }
-
- _creditLines[idx]._position.y -= _creditSpeed;
- }
-}
-
-void TattooEngine::eraseCredits() {
- Common::Rect screenRect(0, -_creditSpeed, _screen->w(), _screen->h() + _creditSpeed);
-
- for (uint idx = 0; idx < _creditLines.size(); ++idx) {
- if (screenRect.contains(_creditLines[idx]._position)) {
- Common::Rect r(_creditLines[idx]._width, _screen->fontHeight() + 3);
- r.moveTo(_creditLines[idx]._position.x, _creditLines[idx]._position.y - 1 + _creditSpeed);
-
- _screen->restoreBackground(r);
- }
- }
-
- if (_creditLines[_creditLines.size() - 1]._position.y < -_creditSpeed) {
- _creditLines.clear();
- _creditsActive = false;
- setFlags(!3000);
- }
-}
-
void TattooEngine::doHangManPuzzle() {
- char answers[3][10];
- Common::Point lines[3];
- const char *solutions[3];
- int numWide, spacing;
- ImageFile *paper;
- Common::Point cursorPos;
- byte cursorColor = 254;
- bool solved = false;
- bool done = false;
- bool flag = false;
- size_t i = 0;
-
- switch (getLanguage()) {
- case Common::FR_FRA:
- lines[0] = Common::Point(34, 210);
- lines[1] = Common::Point(72, 242);
- lines[2] = Common::Point(34, 276);
- numWide = 8;
- spacing = 19;
- paper = new ImageFile("paperf.vgs");
- break;
-
- case Common::DE_DEU:
- lines[0] = Common::Point(44, 73);
- lines[1] = Common::Point(56, 169);
- lines[2] = Common::Point(47, 256);
- numWide = 7;
- spacing = 19;
- paper = new ImageFile("paperg.vgs");
- break;
-
- default:
- // English
- lines[0] = Common::Point(65, 84);
- lines[1] = Common::Point(65, 159);
- lines[2] = Common::Point(75, 234);
- numWide = 5;
- spacing = 20;
- paper = new ImageFile("paper.vgs");
- break;
- }
-
- ImageFrame &paperFrame = (*paper)[0];
- Common::Rect paperBounds(paperFrame._width, paperFrame._height);
- paperBounds.moveTo((_screen->w() - paperFrame._width) / 2, (_screen->h() - paperFrame._height) / 2);
-
- for (int line = 0; line<3; ++line) {
- lines[line].x += paperBounds.left;
- lines[line].y += paperBounds.top;
-
- for (i = 0; i <= (size_t)numWide; ++i)
- answers[line][i] = 0;
- }
-
- _screen->_backBuffer1.blitFrom(paperFrame, Common::Point(paperBounds.left + _screen->_currentScroll.x, 0));
-
- // If they have already solved the puzzle, put the answer on the graphic
- if (readFlags(299)) {
- for (int line = 0; line < 3; ++line) {
- cursorPos.y = lines[line].y - _screen->fontHeight() - 2;
-
- for (i = 0; i < strlen(solutions[line]); ++i) {
- cursorPos.x = lines[line].x + 8 - _screen->widestChar() / 2 + i * spacing;
- _screen->gPrint(Common::Point(cursorPos.x + _screen->widestChar() / 2 -
- _screen->charWidth(solutions[line][i]) / 2, cursorPos.y), 0, "%c", solutions[line][i]);
- }
- }
- }
-
- _screen->slamRect(paperBounds);
- cursorPos = Common::Point(lines[0].x + 8 - _screen->widestChar() / 2, lines[0].y - _screen->fontHeight() - 2);
- int line = 0;
-
- // If they have not solved the puzzle, let them solve it here
- if (!readFlags(299)) {
- do {
- while (!_events->kbHit()) {
- // See if a key or a mouse button is pressed
- _events->pollEventsAndWait();
- _events->setButtonState();
-
- flag = !flag;
- if (flag) {
- _screen->_backBuffer1.fillRect(Common::Rect(cursorPos.x + _screen->_currentScroll.x, cursorPos.y,
- cursorPos.x + _screen->widestChar() + _screen->_currentScroll.x - 1, cursorPos.y + _screen->fontHeight() - 1), cursorColor);
- if (answers[line][i])
- _screen->gPrint(Common::Point(cursorPos.x + _screen->widestChar() / 2 - _screen->charWidth(answers[line][i]) / 2,
- cursorPos.y), 0, "%c", answers[line][i]);
- _screen->slamArea(cursorPos.x, cursorPos.y, _screen->widestChar(), _screen->fontHeight());
- } else {
- _screen->setDisplayBounds(Common::Rect(cursorPos.x + _screen->_currentScroll.x, cursorPos.y,
- cursorPos.x + _screen->widestChar() + _screen->_currentScroll.x, cursorPos.y + _screen->fontHeight()));
- _screen->_backBuffer->blitFrom(paperFrame, Common::Point(paperBounds.left + _screen->_currentScroll.x, paperBounds.top));
- _screen->resetDisplayBounds();
-
- if (answers[line][i])
- _screen->gPrint(Common::Point(cursorPos.x + _screen->widestChar() / 2 - _screen->charWidth(answers[line][i]) / 2,
- cursorPos.y), 0, "%c", answers[line][i]);
- _screen->slamArea(cursorPos.x, cursorPos.y, _screen->widestChar(), _screen->fontHeight());
- }
-
- if (!_events->kbHit())
- _events->wait(2);
- }
-
- if (_events->kbHit()) {
- Common::KeyState keyState = _events->getKey();
-
- if (((toupper(keyState.ascii) >= 'A') && (toupper(keyState.ascii) <= 'Z')) ||
- ((keyState.ascii >= 128) && ((keyState.ascii <= 168) || (keyState.ascii == 225)))) {
- answers[line][i] = keyState.ascii;
- keyState.keycode = Common::KEYCODE_RIGHT;
- }
-
- _screen->setDisplayBounds(Common::Rect(cursorPos.x + _screen->_currentScroll.x, cursorPos.y,
- cursorPos.x + _screen->widestChar() + _screen->_currentScroll.x, cursorPos.y + _screen->fontHeight()));
- _screen->_backBuffer->blitFrom(paperFrame, Common::Point(paperBounds.left + _screen->_currentScroll.x, paperBounds.top));
- _screen->resetDisplayBounds();
-
- if (answers[line][i])
- _screen->gPrint(Common::Point(cursorPos.x + _screen->widestChar() / 2 - _screen->charWidth(answers[line][i]) / 2,
- cursorPos.y), 0, "%c", answers[line][i]);
- _screen->slamArea(cursorPos.x, cursorPos.y, _screen->widestChar(), _screen->fontHeight());
-
- switch (keyState.keycode) {
- case Common::KEYCODE_ESCAPE:
- done = true;
- break;
-
- case Common::KEYCODE_UP:
- case Common::KEYCODE_KP8:
- if (line) {
- line--;
- if (i >= strlen(solutions[line]))
- i = strlen(solutions[line]) - 1;
- }
- break;
-
- case Common::KEYCODE_DOWN:
- case Common::KEYCODE_KP2:
- if (line < 2) {
- ++line;
- if (i >= strlen(solutions[line]))
- i = strlen(solutions[line]) - 1;
- }
- break;
-
- case Common::KEYCODE_BACKSPACE:
- case Common::KEYCODE_LEFT:
- case Common::KEYCODE_KP4:
- if (i)
- --i;
- else if (line) {
- --line;
-
- i = strlen(solutions[line]) - 1;
- }
-
- if (keyState.keycode == Common::KEYCODE_BACKSPACE)
- answers[line][i] = ' ';
- break;
-
- case Common::KEYCODE_RIGHT:
- case Common::KEYCODE_KP6:
- if (i < strlen(solutions[line]) - 1)
- i++;
- else if (line < 2) {
- ++line;
- i = 0;
- }
- break;
-
- case Common::KEYCODE_DELETE:
- answers[line][i] = ' ';
- break;
-
- default:
- break;
- }
- }
-
- cursorPos.x = lines[line].x + 8 - _screen->widestChar() / 2 + i * spacing;
- cursorPos.y = lines[line].y - _screen->fontHeight() - 2;
-
- // See if all of their anwers are correct
- if (!scumm_stricmp(answers[0], solutions[0]) && !scumm_stricmp(answers[1], solutions[1]) &&
- !scumm_stricmp(answers[2], solutions[2])) {
- done = true;
- solved = true;
- }
- } while (!done && !shouldQuit());
- } else {
- // They have already solved the puzzle, so just display the solution and wait for a mouse or key click
- do {
- _events->pollEventsAndWait();
- _events->setButtonState();
-
- if ((_events->kbHit()) || (_events->_released) || (_events->_rightReleased)) {
- done = true;
- _events->clearEvents();
- }
- } while (!done && !shouldQuit());
- }
-
- delete paper;
- _screen->_backBuffer1.blitFrom(_screen->_backBuffer2, Common::Point(paperBounds.left + _screen->_currentScroll.x, paperBounds.top),
- Common::Rect(paperBounds.left + _screen->_currentScroll.x, paperBounds.top,
- paperBounds.right + _screen->_currentScroll.x, paperBounds.bottom));
- _scene->doBgAnim();
-
- _screen->slamArea(paperBounds.left + _screen->_currentScroll.x, paperBounds.top,
- paperBounds.width(), paperBounds.height());
-
- // Don't call the talk files if the puzzle has already been solved
- if (readFlags(299))
- return;
-
- // If they solved the puzzle correctly, set the solved flag and run the appropriate talk scripts
- if (solved) {
- _talk->talkTo("SLVE12S.TLK");
- _talk->talkTo("WATS12X.TLK");
- setFlags(299);
- } else {
- _talk->talkTo("HOLM12X.TLK");
- }
+ _hangmanWidget.show();
}
} // End of namespace Tattoo
diff --git a/engines/sherlock/tattoo/tattoo.h b/engines/sherlock/tattoo/tattoo.h
index a9798dce41..774e243733 100644
--- a/engines/sherlock/tattoo/tattoo.h
+++ b/engines/sherlock/tattoo/tattoo.h
@@ -25,6 +25,7 @@
#include "sherlock/sherlock.h"
#include "sherlock/tattoo/tattoo_darts.h"
+#include "sherlock/tattoo/widget_hangman.h"
namespace Sherlock {
@@ -51,21 +52,10 @@ enum {
FLAG_ALT_MAP_MUSIC = 525
};
-struct CreditLine {
- Common::Point _position;
- int _xOffset;
- int _width;
- Common::String _line, _line2;
-
- CreditLine(const Common::String &line, const Common::Point &pt, int width) :
- _line(line), _position(pt), _width(width), _xOffset(0) {}
-};
-
class TattooEngine : public SherlockEngine {
private:
Darts _darts;
- Common::Array<CreditLine> _creditLines;
- int _creditSpeed;
+ WidgetHangman _hangmanWidget;
/**
* Loads the initial palette for the game
@@ -89,7 +79,6 @@ protected:
*/
virtual void startScene();
public:
- bool _creditsActive;
bool _runningProlog;
bool _fastMode, _allowFastMode;
bool _transparentMenus;
@@ -97,26 +86,6 @@ public:
TattooEngine(OSystem *syst, const SherlockGameDescription *gameDesc);
virtual ~TattooEngine();
- /**
- * Initialize and load credit data for display
- */
- void initCredits();
-
- /**
- * Draw credits on the screen
- */
- void drawCredits();
-
- /**
- * Blit the drawn credits to the screen
- */
- void blitCredits();
-
- /**
- * Erase any area of the screen covered by credits
- */
- void eraseCredits();
-
void doHangManPuzzle();
};
diff --git a/engines/sherlock/tattoo/tattoo_scene.cpp b/engines/sherlock/tattoo/tattoo_scene.cpp
index ba462ca255..9b5c5b2156 100644
--- a/engines/sherlock/tattoo/tattoo_scene.cpp
+++ b/engines/sherlock/tattoo/tattoo_scene.cpp
@@ -350,8 +350,8 @@ void TattooScene::doBgAnim() {
ui.drawInterface();
- if (vm._creditsActive)
- vm.blitCredits();
+ if (ui._creditsWidget.active())
+ ui._creditsWidget.blitCredits();
if (!vm._fastMode)
events.wait(3);
diff --git a/engines/sherlock/tattoo/tattoo_user_interface.cpp b/engines/sherlock/tattoo/tattoo_user_interface.cpp
index ae09ba5fc7..731c215a27 100644
--- a/engines/sherlock/tattoo/tattoo_user_interface.cpp
+++ b/engines/sherlock/tattoo/tattoo_user_interface.cpp
@@ -31,7 +31,7 @@ namespace Tattoo {
TattooUserInterface::TattooUserInterface(SherlockEngine *vm): UserInterface(vm),
_inventoryWidget(vm), _messageWidget(vm), _textWidget(vm), _tooltipWidget(vm), _verbsWidget(vm),
- _labWidget(vm) {
+ _labWidget(vm), _creditsWidget(vm) {
Common::fill(&_lookupTable[0], &_lookupTable[PALETTE_COUNT], 0);
Common::fill(&_lookupTable1[0], &_lookupTable1[PALETTE_COUNT], 0);
_scrollSize = 0;
@@ -239,8 +239,8 @@ void TattooUserInterface::handleInput() {
_keyState.keycode = Common::KEYCODE_INVALID;
// Check for credits starting
- if (_vm->readFlags(3000) && !vm._creditsActive)
- vm.initCredits();
+ if (_vm->readFlags(3000) && !_creditsWidget.active())
+ _creditsWidget.initCredits();
// Check the mouse positioning
if (events.isCursorVisible())
@@ -294,15 +294,14 @@ void TattooUserInterface::handleInput() {
void TattooUserInterface::drawInterface(int bufferNum) {
Screen &screen = *_vm->_screen;
- TattooEngine &vm = *(TattooEngine *)_vm;
// Draw any active on-screen widgets
for (Common::List<WidgetBase *>::iterator i = _widgets.begin(); i != _widgets.end(); ++i)
(*i)->draw();
// Handle drawing credits
- if (vm._creditsActive)
- vm.drawCredits();
+ if (_creditsWidget.active())
+ _creditsWidget.drawCredits();
// Bring the widgets to the screen
if (_mask != nullptr)
@@ -649,7 +648,6 @@ void TattooUserInterface::setupBGArea(const byte cMap[PALETTE_SIZE]) {
}
void TattooUserInterface::doBgAnimEraseBackground() {
- TattooEngine &vm = *((TattooEngine *)_vm);
People &people = *_vm->_people;
Scene &scene = *_vm->_scene;
Screen &screen = *_vm->_screen;
@@ -714,8 +712,8 @@ void TattooUserInterface::doBgAnimEraseBackground() {
}
// If credits are active, erase the area they cover
- if (vm._creditsActive)
- vm.eraseCredits();
+ if (_creditsWidget.active())
+ _creditsWidget.eraseCredits();
}
for (uint idx = 0; idx < scene._bgShapes.size(); ++idx) {
diff --git a/engines/sherlock/tattoo/tattoo_user_interface.h b/engines/sherlock/tattoo/tattoo_user_interface.h
index 1cefec688d..b16f91134b 100644
--- a/engines/sherlock/tattoo/tattoo_user_interface.h
+++ b/engines/sherlock/tattoo/tattoo_user_interface.h
@@ -28,6 +28,7 @@
#include "sherlock/saveload.h"
#include "sherlock/screen.h"
#include "sherlock/user_interface.h"
+#include "sherlock/tattoo/widget_credits.h"
#include "sherlock/tattoo/widget_inventory.h"
#include "sherlock/tattoo/widget_lab.h"
#include "sherlock/tattoo/widget_text.h"
@@ -106,10 +107,11 @@ public:
Common::Point _maskOffset;
int _maskCounter;
ImageFile *_interfaceImages;
- WidgetText _textWidget;
+ WidgetCredits _creditsWidget;
WidgetLab _labWidget;
- WidgetVerbs _verbsWidget;
+ WidgetText _textWidget;
WidgetSceneTooltip _tooltipWidget;
+ WidgetVerbs _verbsWidget;
public:
TattooUserInterface(SherlockEngine *vm);
virtual ~TattooUserInterface();
diff --git a/engines/sherlock/tattoo/widget_credits.cpp b/engines/sherlock/tattoo/widget_credits.cpp
new file mode 100644
index 0000000000..dcb73b5adb
--- /dev/null
+++ b/engines/sherlock/tattoo/widget_credits.cpp
@@ -0,0 +1,210 @@
+/* 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/widget_credits.h"
+#include "sherlock/tattoo/tattoo.h"
+
+namespace Sherlock {
+
+namespace Tattoo {
+
+WidgetCredits::WidgetCredits(SherlockEngine *vm) : _vm(vm) {
+ _creditSpeed = 4;
+ _creditsActive = false;
+}
+
+void WidgetCredits::initCredits() {
+ Resources &res = *_vm->_res;
+ Screen &screen = *_vm->_screen;
+ Common::SeekableReadStream *stream = res.load("credits.txt");
+ int spacing = screen.fontHeight() * 2;
+ int yp = screen.h();
+
+ _creditsActive = true;
+ _creditLines.clear();
+
+ while (stream->pos() < stream->size()) {
+ Common::String line = stream->readLine();
+
+ if (line.hasPrefix("Scroll Speed")) {
+ const char *p = line.c_str() + 12;
+ while ((*p < '0') || (*p > '9'))
+ p++;
+
+ _creditSpeed = atoi(p);
+ } else if (line.hasPrefix("Y Spacing")) {
+ const char *p = line.c_str() + 12;
+ while ((*p < '0') || (*p > '9'))
+ p++;
+
+ spacing = atoi(p) + screen.fontHeight() + 1;
+ } else {
+ int width = screen.stringWidth(line) + 2;
+
+ _creditLines.push_back(CreditLine(line, Common::Point((screen.w() - width) / 2 + 1, yp), width));
+ yp += spacing;
+ }
+ }
+
+ // Post-processing for finding split lines
+ for (int l = 0; l < (int)_creditLines.size(); ++l) {
+ CreditLine &cl = _creditLines[l];
+ const char *p = strchr(cl._line.c_str(), '-');
+
+ if (p != nullptr && p[1] == '>') {
+ cl._line2 = Common::String(p + 3);
+ cl._line = Common::String(cl._line.c_str(), p);
+
+ int width = cl._width;
+ int width1 = screen.stringWidth(cl._line);
+ int width2 = screen.stringWidth(cl._line2);
+
+ int c = 1;
+ for (int l1 = l + 1; l1 < (int)_creditLines.size(); ++l1) {
+ if ((p = strchr(_creditLines[l1]._line.c_str(), '-')) != nullptr) {
+ if (p[1] == '>') {
+ Common::String line1 = Common::String(_creditLines[l1]._line.c_str(), p);
+ Common::String line2 = Common::String(p + 3);
+
+ width1 = MAX(width1, screen.stringWidth(line1));
+
+ if (screen.stringWidth(line2) > width2)
+ width2 = screen.stringWidth(line2);
+ ++c;
+ } else {
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+
+ width = width1 + width2 + screen.widestChar();
+ width1 += screen.widestChar();
+
+ for (int l1 = l; l1 < l + c; ++l1) {
+ _creditLines[l1]._width = width;
+ _creditLines[l1]._xOffset = width1;
+ }
+
+ l += c - 1;
+ }
+ }
+
+ delete stream;
+}
+
+void WidgetCredits::drawCredits() {
+ Screen &screen = *_vm->_screen;
+ Common::Rect screenRect(0, 0, screen.w(), screen.h());
+ Surface &bb1 = screen._backBuffer1;
+
+ for (uint idx = 0; idx < _creditLines.size() && _creditLines[idx]._position.y < screen.h(); ++idx) {
+ if (screenRect.contains(_creditLines[idx]._position)) {
+ if (!_creditLines[idx]._line2.empty()) {
+ int x1 = _creditLines[idx]._position.x;
+ int x2 = x1 + _creditLines[idx]._xOffset;
+ const Common::String &line1 = _creditLines[idx]._line;
+ const Common::String &line2 = _creditLines[idx]._line2;
+
+ bb1.writeString(line1, Common::Point(x1 - 1, _creditLines[idx]._position.y - 1), 0);
+ bb1.writeString(line1, Common::Point(x1, _creditLines[idx]._position.y - 1), 0);
+ bb1.writeString(line1, Common::Point(x1 + 1, _creditLines[idx]._position.y - 1), 0);
+
+ bb1.writeString(line1, Common::Point(x1 - 1, _creditLines[idx]._position.y), 0);
+ bb1.writeString(line1, Common::Point(x1 + 1, _creditLines[idx]._position.y), 0);
+
+ bb1.writeString(line1, Common::Point(x1 - 1, _creditLines[idx]._position.y + 1), 0);
+ bb1.writeString(line1, Common::Point(x1, _creditLines[idx]._position.y + 1), 0);
+ bb1.writeString(line1, Common::Point(x1 + 1, _creditLines[idx]._position.y + 1), 0);
+
+ bb1.writeString(line1, Common::Point(x1, _creditLines[idx]._position.y), INFO_TOP);
+
+ bb1.writeString(line2, Common::Point(x2 - 1, _creditLines[idx]._position.y - 1), 0);
+ bb1.writeString(line2, Common::Point(x2, _creditLines[idx]._position.y - 1), 0);
+ bb1.writeString(line2, Common::Point(x2 + 1, _creditLines[idx]._position.y - 1), 0);
+
+ bb1.writeString(line2, Common::Point(x2 - 1, _creditLines[idx]._position.y), 0);
+ bb1.writeString(line2, Common::Point(x2 + 1, _creditLines[idx]._position.y), 0);
+
+ bb1.writeString(line2, Common::Point(x2 - 1, _creditLines[idx]._position.y + 1), 0);
+ bb1.writeString(line2, Common::Point(x2, _creditLines[idx]._position.y + 1), 0);
+ bb1.writeString(line2, Common::Point(x2 + 1, _creditLines[idx]._position.y + 1), 0);
+
+ bb1.writeString(line2, Common::Point(x2, _creditLines[idx]._position.y), INFO_TOP);
+ } else {
+ bb1.writeString(_creditLines[idx]._line, Common::Point(_creditLines[idx]._position.x - 1, _creditLines[idx]._position.y - 1), 0);
+ bb1.writeString(_creditLines[idx]._line, Common::Point(_creditLines[idx]._position.x, _creditLines[idx]._position.y - 1), 0);
+ bb1.writeString(_creditLines[idx]._line, Common::Point(_creditLines[idx]._position.x + 1, _creditLines[idx]._position.y - 1), 0);
+
+ bb1.writeString(_creditLines[idx]._line, Common::Point(_creditLines[idx]._position.x - 1, _creditLines[idx]._position.y), 0);
+ bb1.writeString(_creditLines[idx]._line, Common::Point(_creditLines[idx]._position.x + 1, _creditLines[idx]._position.y), 0);
+
+ bb1.writeString(_creditLines[idx]._line, Common::Point(_creditLines[idx]._position.x - 1, _creditLines[idx]._position.y + 1), 0);
+ bb1.writeString(_creditLines[idx]._line, Common::Point(_creditLines[idx]._position.x, _creditLines[idx]._position.y + 1), 0);
+ bb1.writeString(_creditLines[idx]._line, Common::Point(_creditLines[idx]._position.x + 1, _creditLines[idx]._position.y + 1), 0);
+
+ bb1.writeString(_creditLines[idx]._line, Common::Point(_creditLines[idx]._position.x, _creditLines[idx]._position.y), INFO_TOP);
+ }
+ }
+ }
+}
+
+void WidgetCredits::blitCredits() {
+ Screen &screen = *_vm->_screen;
+ Common::Rect screenRect(0, -_creditSpeed, screen.w(), screen.h() + _creditSpeed);
+
+ for (uint idx = 0; idx < _creditLines.size(); ++idx) {
+ if (screenRect.contains(_creditLines[idx]._position)) {
+ Common::Rect r(_creditLines[idx]._width, screen.fontHeight() + 2);
+ r.moveTo(_creditLines[idx]._position.x, _creditLines[idx]._position.y - 1);
+
+ screen.slamRect(r);
+ }
+
+ _creditLines[idx]._position.y -= _creditSpeed;
+ }
+}
+
+void WidgetCredits::eraseCredits() {
+ Screen &screen = *_vm->_screen;
+ Common::Rect screenRect(0, -_creditSpeed, screen.w(), screen.h() + _creditSpeed);
+
+ for (uint idx = 0; idx < _creditLines.size(); ++idx) {
+ if (screenRect.contains(_creditLines[idx]._position)) {
+ Common::Rect r(_creditLines[idx]._width, screen.fontHeight() + 3);
+ r.moveTo(_creditLines[idx]._position.x, _creditLines[idx]._position.y - 1 + _creditSpeed);
+
+ screen.restoreBackground(r);
+ }
+ }
+
+ if (_creditLines[_creditLines.size() - 1]._position.y < -_creditSpeed) {
+ _creditLines.clear();
+ _creditsActive = false;
+ _vm->setFlags(!3000);
+ }
+}
+
+} // End of namespace Tattoo
+
+} // End of namespace Sherlock
diff --git a/engines/sherlock/tattoo/widget_credits.h b/engines/sherlock/tattoo/widget_credits.h
new file mode 100644
index 0000000000..5c0504944e
--- /dev/null
+++ b/engines/sherlock/tattoo/widget_credits.h
@@ -0,0 +1,84 @@
+/* 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_CREDITS_H
+#define SHERLOCK_TATTOO_CREDITS_H
+
+#include "common/array.h"
+#include "common/rect.h"
+
+namespace Sherlock {
+
+ class SherlockEngine;
+
+namespace Tattoo {
+
+struct CreditLine {
+ Common::Point _position;
+ int _xOffset;
+ int _width;
+ Common::String _line, _line2;
+
+ CreditLine(const Common::String &line, const Common::Point &pt, int width) :
+ _line(line), _position(pt), _width(width), _xOffset(0) {}
+};
+
+class WidgetCredits {
+private:
+ SherlockEngine *_vm;
+ Common::Array<CreditLine> _creditLines;
+ int _creditSpeed;
+ bool _creditsActive;
+public:
+ WidgetCredits(SherlockEngine *vm);
+
+ /**
+ * Returns true if the credits are active
+ */
+ bool active() const { return _creditsActive; }
+
+ /**
+ * Initialize and load credit data for display
+ */
+ void initCredits();
+
+ /**
+ * Draw credits on the screen
+ */
+ void drawCredits();
+
+ /**
+ * Blit the drawn credits to the screen
+ */
+ void blitCredits();
+
+ /**
+ * Erase any area of the screen covered by credits
+ */
+ void eraseCredits();
+};
+
+} // End of namespace Tattoo
+
+} // End of namespace Sherlock
+
+#endif
diff --git a/engines/sherlock/tattoo/widget_hangman.cpp b/engines/sherlock/tattoo/widget_hangman.cpp
new file mode 100644
index 0000000000..84cfb02101
--- /dev/null
+++ b/engines/sherlock/tattoo/widget_hangman.cpp
@@ -0,0 +1,264 @@
+/* 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/widget_hangman.h"
+#include "sherlock/tattoo/tattoo.h"
+
+namespace Sherlock {
+
+namespace Tattoo {
+
+void WidgetHangman::show() {
+ Events &events = *_vm->_events;
+ Scene &scene = *_vm->_scene;
+ Screen &screen = *_vm->_screen;
+ Talk &talk = *_vm->_talk;
+ char answers[3][10];
+ Common::Point lines[3];
+ const char *solutions[3];
+ int numWide, spacing;
+ ImageFile *paper;
+ Common::Point cursorPos;
+ byte cursorColor = 254;
+ bool solved = false;
+ bool done = false;
+ bool flag = false;
+ size_t i = 0;
+
+ switch (_vm->getLanguage()) {
+ case Common::FR_FRA:
+ lines[0] = Common::Point(34, 210);
+ lines[1] = Common::Point(72, 242);
+ lines[2] = Common::Point(34, 276);
+ numWide = 8;
+ spacing = 19;
+ paper = new ImageFile("paperf.vgs");
+ break;
+
+ case Common::DE_DEU:
+ lines[0] = Common::Point(44, 73);
+ lines[1] = Common::Point(56, 169);
+ lines[2] = Common::Point(47, 256);
+ numWide = 7;
+ spacing = 19;
+ paper = new ImageFile("paperg.vgs");
+ break;
+
+ default:
+ // English
+ lines[0] = Common::Point(65, 84);
+ lines[1] = Common::Point(65, 159);
+ lines[2] = Common::Point(75, 234);
+ numWide = 5;
+ spacing = 20;
+ paper = new ImageFile("paper.vgs");
+ break;
+ }
+
+ ImageFrame &paperFrame = (*paper)[0];
+ Common::Rect paperBounds(paperFrame._width, paperFrame._height);
+ paperBounds.moveTo((screen.w() - paperFrame._width) / 2, (screen.h() - paperFrame._height) / 2);
+
+ for (int line = 0; line<3; ++line) {
+ lines[line].x += paperBounds.left;
+ lines[line].y += paperBounds.top;
+
+ for (i = 0; i <= (size_t)numWide; ++i)
+ answers[line][i] = 0;
+ }
+
+ screen._backBuffer1.blitFrom(paperFrame, Common::Point(paperBounds.left + screen._currentScroll.x, 0));
+
+ // If they have already solved the puzzle, put the answer on the graphic
+ if (_vm->readFlags(299)) {
+ for (int line = 0; line < 3; ++line) {
+ cursorPos.y = lines[line].y - screen.fontHeight() - 2;
+
+ for (i = 0; i < strlen(solutions[line]); ++i) {
+ cursorPos.x = lines[line].x + 8 - screen.widestChar() / 2 + i * spacing;
+ screen.gPrint(Common::Point(cursorPos.x + screen.widestChar() / 2 -
+ screen.charWidth(solutions[line][i]) / 2, cursorPos.y), 0, "%c", solutions[line][i]);
+ }
+ }
+ }
+
+ screen.slamRect(paperBounds);
+ cursorPos = Common::Point(lines[0].x + 8 - screen.widestChar() / 2, lines[0].y - screen.fontHeight() - 2);
+ int line = 0;
+
+ // If they have not solved the puzzle, let them solve it here
+ if (!_vm->readFlags(299)) {
+ do {
+ while (!events.kbHit()) {
+ // See if a key or a mouse button is pressed
+ events.pollEventsAndWait();
+ events.setButtonState();
+
+ flag = !flag;
+ if (flag) {
+ screen._backBuffer1.fillRect(Common::Rect(cursorPos.x + screen._currentScroll.x, cursorPos.y,
+ cursorPos.x + screen.widestChar() + screen._currentScroll.x - 1, cursorPos.y + screen.fontHeight() - 1), cursorColor);
+ if (answers[line][i])
+ screen.gPrint(Common::Point(cursorPos.x + screen.widestChar() / 2 - screen.charWidth(answers[line][i]) / 2,
+ cursorPos.y), 0, "%c", answers[line][i]);
+ screen.slamArea(cursorPos.x, cursorPos.y, screen.widestChar(), screen.fontHeight());
+ } else {
+ screen.setDisplayBounds(Common::Rect(cursorPos.x + screen._currentScroll.x, cursorPos.y,
+ cursorPos.x + screen.widestChar() + screen._currentScroll.x, cursorPos.y + screen.fontHeight()));
+ screen._backBuffer->blitFrom(paperFrame, Common::Point(paperBounds.left + screen._currentScroll.x, paperBounds.top));
+ screen.resetDisplayBounds();
+
+ if (answers[line][i])
+ screen.gPrint(Common::Point(cursorPos.x + screen.widestChar() / 2 - screen.charWidth(answers[line][i]) / 2,
+ cursorPos.y), 0, "%c", answers[line][i]);
+ screen.slamArea(cursorPos.x, cursorPos.y, screen.widestChar(), screen.fontHeight());
+ }
+
+ if (!events.kbHit())
+ events.wait(2);
+ }
+
+ if (events.kbHit()) {
+ Common::KeyState keyState = events.getKey();
+
+ if (((toupper(keyState.ascii) >= 'A') && (toupper(keyState.ascii) <= 'Z')) ||
+ ((keyState.ascii >= 128) && ((keyState.ascii <= 168) || (keyState.ascii == 225)))) {
+ answers[line][i] = keyState.ascii;
+ keyState.keycode = Common::KEYCODE_RIGHT;
+ }
+
+ screen.setDisplayBounds(Common::Rect(cursorPos.x + screen._currentScroll.x, cursorPos.y,
+ cursorPos.x + screen.widestChar() + screen._currentScroll.x, cursorPos.y + screen.fontHeight()));
+ screen._backBuffer->blitFrom(paperFrame, Common::Point(paperBounds.left + screen._currentScroll.x, paperBounds.top));
+ screen.resetDisplayBounds();
+
+ if (answers[line][i])
+ screen.gPrint(Common::Point(cursorPos.x + screen.widestChar() / 2 - screen.charWidth(answers[line][i]) / 2,
+ cursorPos.y), 0, "%c", answers[line][i]);
+ screen.slamArea(cursorPos.x, cursorPos.y, screen.widestChar(), screen.fontHeight());
+
+ switch (keyState.keycode) {
+ case Common::KEYCODE_ESCAPE:
+ done = true;
+ break;
+
+ case Common::KEYCODE_UP:
+ case Common::KEYCODE_KP8:
+ if (line) {
+ line--;
+ if (i >= strlen(solutions[line]))
+ i = strlen(solutions[line]) - 1;
+ }
+ break;
+
+ case Common::KEYCODE_DOWN:
+ case Common::KEYCODE_KP2:
+ if (line < 2) {
+ ++line;
+ if (i >= strlen(solutions[line]))
+ i = strlen(solutions[line]) - 1;
+ }
+ break;
+
+ case Common::KEYCODE_BACKSPACE:
+ case Common::KEYCODE_LEFT:
+ case Common::KEYCODE_KP4:
+ if (i)
+ --i;
+ else if (line) {
+ --line;
+
+ i = strlen(solutions[line]) - 1;
+ }
+
+ if (keyState.keycode == Common::KEYCODE_BACKSPACE)
+ answers[line][i] = ' ';
+ break;
+
+ case Common::KEYCODE_RIGHT:
+ case Common::KEYCODE_KP6:
+ if (i < strlen(solutions[line]) - 1)
+ i++;
+ else if (line < 2) {
+ ++line;
+ i = 0;
+ }
+ break;
+
+ case Common::KEYCODE_DELETE:
+ answers[line][i] = ' ';
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ cursorPos.x = lines[line].x + 8 - screen.widestChar() / 2 + i * spacing;
+ cursorPos.y = lines[line].y - screen.fontHeight() - 2;
+
+ // See if all of their anwers are correct
+ if (!scumm_stricmp(answers[0], solutions[0]) && !scumm_stricmp(answers[1], solutions[1]) &&
+ !scumm_stricmp(answers[2], solutions[2])) {
+ done = true;
+ solved = true;
+ }
+ } while (!done && !_vm->shouldQuit());
+ } else {
+ // They have already solved the puzzle, so just display the solution and wait for a mouse or key click
+ do {
+ events.pollEventsAndWait();
+ events.setButtonState();
+
+ if ((events.kbHit()) || (events._released) || (events._rightReleased)) {
+ done = true;
+ events.clearEvents();
+ }
+ } while (!done && !_vm->shouldQuit());
+ }
+
+ delete paper;
+ screen._backBuffer1.blitFrom(screen._backBuffer2, Common::Point(paperBounds.left + screen._currentScroll.x, paperBounds.top),
+ Common::Rect(paperBounds.left + screen._currentScroll.x, paperBounds.top,
+ paperBounds.right + screen._currentScroll.x, paperBounds.bottom));
+ scene.doBgAnim();
+
+ screen.slamArea(paperBounds.left + screen._currentScroll.x, paperBounds.top,
+ paperBounds.width(), paperBounds.height());
+
+ // Don't call the talk files if the puzzle has already been solved
+ if (_vm->readFlags(299))
+ return;
+
+ // If they solved the puzzle correctly, set the solved flag and run the appropriate talk scripts
+ if (solved) {
+ talk.talkTo("SLVE12S.TLK");
+ talk.talkTo("WATS12X.TLK");
+ _vm->setFlags(299);
+ } else {
+ talk.talkTo("HOLM12X.TLK");
+ }
+}
+
+} // End of namespace Tattoo
+
+} // End of namespace Sherlock
diff --git a/engines/sherlock/tattoo/widget_hangman.h b/engines/sherlock/tattoo/widget_hangman.h
new file mode 100644
index 0000000000..daa598720a
--- /dev/null
+++ b/engines/sherlock/tattoo/widget_hangman.h
@@ -0,0 +1,48 @@
+/* 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_HANGMAN_H
+#define SHERLOCK_TATTOO_HANGMAN_H
+
+namespace Sherlock {
+
+namespace Tattoo {
+
+class TattooEngine;
+
+class WidgetHangman {
+private:
+ TattooEngine *_vm;
+public:
+ WidgetHangman(TattooEngine *vm) : _vm(vm) {}
+
+ /**
+ * Show the hangman puzzle
+ */
+ void show();
+};
+
+} // End of namespace Tattoo
+
+} // End of namespace Sherlock
+
+#endif