Initial commit
This commit is contained in:
222
engines/sherlock/tattoo/tattoo.cpp
Normal file
222
engines/sherlock/tattoo/tattoo.cpp
Normal file
@@ -0,0 +1,222 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/config-manager.h"
|
||||
#include "engines/util.h"
|
||||
#include "sherlock/tattoo/tattoo.h"
|
||||
#include "sherlock/tattoo/tattoo_fixed_text.h"
|
||||
#include "sherlock/tattoo/tattoo_resources.h"
|
||||
#include "sherlock/tattoo/tattoo_scene.h"
|
||||
#include "sherlock/tattoo/tattoo_user_interface.h"
|
||||
#include "sherlock/tattoo/widget_base.h"
|
||||
#include "sherlock/people.h"
|
||||
|
||||
#include "backends/keymapper/keymapper.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
namespace Tattoo {
|
||||
|
||||
TattooEngine::TattooEngine(OSystem *syst, const SherlockGameDescription *gameDesc) :
|
||||
SherlockEngine(syst, gameDesc), _darts(this), _foolscapWidget(this) {
|
||||
_runningProlog = false;
|
||||
_fastMode = false;
|
||||
_allowFastMode = true;
|
||||
_transparentMenus = true;
|
||||
_textWindowsOn = true;
|
||||
}
|
||||
|
||||
TattooEngine::~TattooEngine() {
|
||||
}
|
||||
|
||||
void TattooEngine::showOpening() {
|
||||
// No implementation - opening is done using in-game scenes
|
||||
}
|
||||
|
||||
void TattooEngine::initialize() {
|
||||
initGraphics(640, 480);
|
||||
|
||||
// Initialize the base engine
|
||||
SherlockEngine::initialize();
|
||||
|
||||
// Initialise the global flags
|
||||
_flags.resize(3200);
|
||||
_flags[1] = _flags[4] = _flags[76] = true;
|
||||
_runningProlog = true;
|
||||
|
||||
Common::Keymapper *keymapper = g_system->getEventManager()->getKeymapper();
|
||||
keymapper->getKeymap("tattoo-prolog")->setEnabled(true);
|
||||
|
||||
// Add some more files to the cache
|
||||
_res->addToCache("walk.lib");
|
||||
|
||||
// Set up list of people
|
||||
TattooFixedText &fixedText = *(TattooFixedText *)_fixedText;
|
||||
const char *peopleNamePtr = nullptr;
|
||||
|
||||
for (int idx = 0; idx < TATTOO_MAX_PEOPLE; ++idx) {
|
||||
peopleNamePtr = fixedText.getText(PEOPLE_DATA[idx].fixedTextId);
|
||||
|
||||
_people->_characters.push_back(PersonData(
|
||||
peopleNamePtr,
|
||||
PEOPLE_DATA[idx].portrait, nullptr, nullptr));
|
||||
}
|
||||
|
||||
// Load the inventory
|
||||
loadInventory();
|
||||
|
||||
// Starting scene
|
||||
_scene->_goToScene = STARTING_INTRO_SCENE;
|
||||
|
||||
// Load an initial palette
|
||||
loadInitialPalette();
|
||||
}
|
||||
|
||||
void TattooEngine::startScene() {
|
||||
TattooUserInterface &ui = *(TattooUserInterface *)_ui;
|
||||
|
||||
if (_scene->_goToScene == OVERHEAD_MAP || _scene->_goToScene == OVERHEAD_MAP2) {
|
||||
// Show the map
|
||||
_scene->_currentScene = OVERHEAD_MAP;
|
||||
_scene->_goToScene = _map->show();
|
||||
|
||||
_people->_savedPos = Common::Point(-1, -1);
|
||||
_people->_savedPos._facing = -1;
|
||||
}
|
||||
|
||||
switch (_scene->_goToScene) {
|
||||
case 7:
|
||||
case 8:
|
||||
case 18:
|
||||
case 53:
|
||||
case 68:
|
||||
// Load overlay mask(s) for the scene
|
||||
ui._mask = _res->load(Common::Path(Common::String::format("res%02d.msk", _scene->_goToScene)));
|
||||
if (_scene->_goToScene == 8)
|
||||
ui._mask1 = _res->load("res08a.msk");
|
||||
else if (_scene->_goToScene == 18 || _scene->_goToScene == 68)
|
||||
ui._mask1 = _res->load("res08a.msk");
|
||||
break;
|
||||
|
||||
case STARTING_INTRO_SCENE:
|
||||
// Disable input so that the intro can't be skipped until the game's logo has been shown
|
||||
ui._lockoutTimer = STARTUP_KEYS_DISABLED_DELAY;
|
||||
break;
|
||||
|
||||
case 101:
|
||||
// Darts Board minigame
|
||||
_darts.playDarts(GAME_CRICKET);
|
||||
break;
|
||||
|
||||
case 102:
|
||||
// Darts Board minigame
|
||||
_darts.playDarts(GAME_301);
|
||||
break;
|
||||
|
||||
case 103:
|
||||
// Darts Board minigame
|
||||
_darts.playDarts(GAME_501);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
_events->setCursor(ARROW);
|
||||
}
|
||||
|
||||
void TattooEngine::loadInitialPalette() {
|
||||
byte palette[768];
|
||||
Common::SeekableReadStream *stream = _res->load("room.pal");
|
||||
stream->read(palette, Graphics::PALETTE_SIZE);
|
||||
_screen->translatePalette(palette);
|
||||
_screen->setPalette(palette);
|
||||
|
||||
delete stream;
|
||||
}
|
||||
|
||||
void TattooEngine::loadInventory() {
|
||||
Inventory &inv = *_inventory;
|
||||
|
||||
Common::String inv1 = _fixedText->getText(kFixedText_Inv1);
|
||||
Common::String inv2 = _fixedText->getText(kFixedText_Inv2);
|
||||
Common::String inv3 = _fixedText->getText(kFixedText_Inv3);
|
||||
Common::String inv4 = _fixedText->getText(kFixedText_Inv4);
|
||||
Common::String inv5 = _fixedText->getText(kFixedText_Inv5);
|
||||
Common::String inv6 = _fixedText->getText(kFixedText_Inv6);
|
||||
Common::String inv7 = _fixedText->getText(kFixedText_Inv7);
|
||||
Common::String inv8 = _fixedText->getText(kFixedText_Inv8);
|
||||
Common::String invDesc1 = _fixedText->getText(kFixedText_InvDesc1);
|
||||
Common::String invDesc2 = _fixedText->getText(kFixedText_InvDesc2);
|
||||
Common::String invDesc3 = _fixedText->getText(kFixedText_InvDesc3);
|
||||
Common::String invDesc4 = _fixedText->getText(kFixedText_InvDesc4);
|
||||
Common::String invDesc5 = _fixedText->getText(kFixedText_InvDesc5);
|
||||
Common::String invDesc6 = _fixedText->getText(kFixedText_InvDesc6);
|
||||
Common::String invDesc7 = _fixedText->getText(kFixedText_InvDesc7);
|
||||
Common::String invDesc8 = _fixedText->getText(kFixedText_InvDesc8);
|
||||
Common::String solve = _fixedText->getText(kFixedText_Solve);
|
||||
|
||||
// Initial inventory
|
||||
inv._holdings = 5;
|
||||
inv.push_back(InventoryItem(0, inv1, invDesc1, "_ITEM01A"));
|
||||
inv.push_back(InventoryItem(0, inv2, invDesc2, "_ITEM02A"));
|
||||
inv.push_back(InventoryItem(0, inv3, invDesc3, "_ITEM03A"));
|
||||
inv.push_back(InventoryItem(0, inv4, invDesc4, "_ITEM04A"));
|
||||
inv.push_back(InventoryItem(0, inv5, invDesc5, "_ITEM05A"));
|
||||
|
||||
// Hidden items
|
||||
inv.push_back(InventoryItem(295, inv6, invDesc6, "_PAP212D", solve));
|
||||
inv.push_back(InventoryItem(294, inv7, invDesc7, "_PAP212I"));
|
||||
inv.push_back(InventoryItem(818, inv8, invDesc8, "_LANT02I"));
|
||||
}
|
||||
|
||||
void TattooEngine::doFoolscapPuzzle() {
|
||||
_foolscapWidget.show();
|
||||
}
|
||||
|
||||
void TattooEngine::loadConfig() {
|
||||
SherlockEngine::loadConfig();
|
||||
|
||||
_transparentMenus = ConfMan.getBool("transparent_windows");
|
||||
_textWindowsOn = ConfMan.getBool("subtitles") || !_sound->_speechOn;
|
||||
}
|
||||
|
||||
void TattooEngine::saveConfig() {
|
||||
SherlockEngine::saveConfig();
|
||||
|
||||
ConfMan.setBool("transparent_windows", _transparentMenus);
|
||||
ConfMan.setBool("subtitles", _textWindowsOn);
|
||||
ConfMan.flushToDisk();
|
||||
}
|
||||
|
||||
bool TattooEngine::canLoadGameStateCurrently(Common::U32String *msg) {
|
||||
TattooUserInterface &ui = *(TattooUserInterface *)_ui;
|
||||
return _canLoadSave && !ui._creditsWidget.active() && !_runningProlog;
|
||||
}
|
||||
|
||||
bool TattooEngine::canSaveGameStateCurrently(Common::U32String *msg) {
|
||||
TattooUserInterface &ui = *(TattooUserInterface *)_ui;
|
||||
return _canLoadSave && !ui._creditsWidget.active() && !_runningProlog;
|
||||
}
|
||||
|
||||
} // End of namespace Tattoo
|
||||
|
||||
} // End of namespace Sherlock
|
||||
119
engines/sherlock/tattoo/tattoo.h
Normal file
119
engines/sherlock/tattoo/tattoo.h
Normal file
@@ -0,0 +1,119 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SHERLOCK_TATTOO_H
|
||||
#define SHERLOCK_TATTOO_H
|
||||
|
||||
#include "sherlock/sherlock.h"
|
||||
#include "sherlock/tattoo/tattoo_darts.h"
|
||||
#include "sherlock/tattoo/widget_foolscap.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
namespace Tattoo {
|
||||
|
||||
enum {
|
||||
INV_FOREGROUND = 167,
|
||||
INV_BACKGROUND = 1,
|
||||
INFO_FOREGROUND = 233,
|
||||
INFO_BACKGROUND = 239,
|
||||
INFO_TOP = 185,
|
||||
INFO_MIDDLE = 186,
|
||||
INFO_BOTTOM = 188,
|
||||
MENU_BACKGROUND = 225,
|
||||
COMMAND_FOREGROUND = 15,
|
||||
COMMAND_HIGHLIGHTED = 254,
|
||||
COMMAND_NULL = 193,
|
||||
PEN_COLOR = 248,
|
||||
PEN_HIGHLIGHT_COLOR = 129
|
||||
};
|
||||
|
||||
enum {
|
||||
FLAG_PLAYER_IS_HOLMES = 76,
|
||||
FLAG_ALT_MAP_MUSIC = 525
|
||||
};
|
||||
|
||||
class TattooEngine : public SherlockEngine {
|
||||
private:
|
||||
Darts _darts;
|
||||
WidgetFoolscap _foolscapWidget;
|
||||
|
||||
/**
|
||||
* Loads the initial palette for the game
|
||||
*/
|
||||
void loadInitialPalette();
|
||||
|
||||
/**
|
||||
* Load the initial inventory
|
||||
*/
|
||||
void loadInventory();
|
||||
protected:
|
||||
/**
|
||||
* Initialize the engine
|
||||
*/
|
||||
void initialize() override;
|
||||
|
||||
void showOpening() override;
|
||||
|
||||
/**
|
||||
* Starting a scene within the game
|
||||
*/
|
||||
void startScene() override;
|
||||
|
||||
/**
|
||||
* Load configuration options
|
||||
*/
|
||||
void loadConfig() override;
|
||||
public:
|
||||
bool _runningProlog;
|
||||
bool _fastMode, _allowFastMode;
|
||||
bool _transparentMenus;
|
||||
bool _textWindowsOn;
|
||||
public:
|
||||
TattooEngine(OSystem *syst, const SherlockGameDescription *gameDesc);
|
||||
~TattooEngine() override;
|
||||
|
||||
/**
|
||||
* Shows the foolscap puzzle
|
||||
*/
|
||||
void doFoolscapPuzzle();
|
||||
|
||||
/**
|
||||
* Save the game configuration
|
||||
*/
|
||||
void saveConfig() override;
|
||||
|
||||
/**
|
||||
* Returns true if a savegame can be loaded
|
||||
*/
|
||||
bool canLoadGameStateCurrently(Common::U32String *msg = nullptr) override;
|
||||
|
||||
/**
|
||||
* Returns true if the game can be saved
|
||||
*/
|
||||
bool canSaveGameStateCurrently(Common::U32String *msg = nullptr) override;
|
||||
};
|
||||
|
||||
} // End of namespace Tattoo
|
||||
|
||||
} // End of namespace Sherlock
|
||||
|
||||
#endif
|
||||
1051
engines/sherlock/tattoo/tattoo_darts.cpp
Normal file
1051
engines/sherlock/tattoo/tattoo_darts.cpp
Normal file
File diff suppressed because it is too large
Load Diff
172
engines/sherlock/tattoo/tattoo_darts.h
Normal file
172
engines/sherlock/tattoo/tattoo_darts.h
Normal file
@@ -0,0 +1,172 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SHERLOCK_TATTOO_DARTS_H
|
||||
#define SHERLOCK_TATTOO_DARTS_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "sherlock/image_file.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
class SherlockEngine;
|
||||
|
||||
namespace Tattoo {
|
||||
|
||||
enum GameType { GAME_301, GAME_CRICKET, GAME_501 };
|
||||
|
||||
class Darts {
|
||||
private:
|
||||
SherlockEngine *_vm;
|
||||
GameType _gameType;
|
||||
ImageFile *_hand1, *_hand2;
|
||||
ImageFile *_dartGraphics;
|
||||
ImageFile *_dartsLeft;
|
||||
ImageFile *_dartMap;
|
||||
ImageFile *_dartBoard;
|
||||
Common::Rect _dartInfo;
|
||||
int _cricketScore[2][7];
|
||||
int _score1, _score2;
|
||||
int _roundNum;
|
||||
int _roundScore;
|
||||
int _level;
|
||||
int _compPlay;
|
||||
Common::String _opponent;
|
||||
int _spacing;
|
||||
bool _oldDartButtons;
|
||||
int _handX;
|
||||
Common::Point _handSize;
|
||||
bool _escapePressed;
|
||||
|
||||
/**
|
||||
* Initialize game variables
|
||||
*/
|
||||
void initDarts();
|
||||
|
||||
/**
|
||||
* Load dartboard graphics
|
||||
*/
|
||||
void loadDarts();
|
||||
|
||||
/**
|
||||
* Free loaded dart images
|
||||
*/
|
||||
void closeDarts();
|
||||
|
||||
/**
|
||||
* Show the player names
|
||||
*/
|
||||
void showNames(int playerNum);
|
||||
|
||||
/**
|
||||
* Show the current scores
|
||||
*/
|
||||
void showStatus(int playerNum);
|
||||
|
||||
/**
|
||||
* Erases the power bars
|
||||
*/
|
||||
void erasePowerBars();
|
||||
|
||||
/**
|
||||
* Returns true if a mouse button or key is pressed
|
||||
*/
|
||||
bool dartHit();
|
||||
|
||||
/**
|
||||
* Shows a power bar and increments it until a key or mouse button is pressed. If the bar
|
||||
* reaches the end, it will also end. The reached power bar number is returned.
|
||||
* @param pt Bar position
|
||||
* @param color draw color
|
||||
* @param goToPower If provided, input is ignored, and the bar is increased up to the specified level
|
||||
* @param orientation 0=Horizontal, 1=Vertical
|
||||
*/
|
||||
int doPowerBar(const Common::Point &pt, byte color, int goToPower, int orientation);
|
||||
|
||||
/**
|
||||
* This is similar to doPowerBar, except it draws the player's hand moving across the
|
||||
* bottom of the screen to indicate the positioning of the darts
|
||||
*/
|
||||
int drawHand(int goToPower, int computer);
|
||||
|
||||
/**
|
||||
* Converts a passed co-ordinates from screen co-ordinates to an offset within the dartboard
|
||||
*/
|
||||
Common::Point convertFromScreenToScoreCoords(const Common::Point &pt) const;
|
||||
|
||||
/**
|
||||
* Return the score a dart at the given position will get
|
||||
*/
|
||||
int dartScore(const Common::Point &pt);
|
||||
|
||||
/**
|
||||
* Draw a dart travelling to the board
|
||||
*/
|
||||
void drawDartThrow(const Common::Point &dartPos, int computer);
|
||||
|
||||
/**
|
||||
* Looks for the passed number on the dartboard. If it finds it, it will return
|
||||
* the co-ordinates of the center of the number
|
||||
*/
|
||||
int findNumberOnBoard(int aim, Common::Point &pt);
|
||||
|
||||
/**
|
||||
* Calculates a position for the comptuer wants to throw, and then calculates where they
|
||||
* actually did throw. The computer will not always hit what it's aiming it.
|
||||
*/
|
||||
void getComputerNumber(int playerNum, Common::Point &targetPos);
|
||||
|
||||
/**
|
||||
* Throw one dart. If computer is 1 or 2, the computer will throw the dart, and user input
|
||||
* will be ignored.
|
||||
* @param computer 1=1st computer player, 2=2nd computer player
|
||||
*/
|
||||
int throwDart(int dartNum, int computer);
|
||||
|
||||
/**
|
||||
* This will update the number of hits for the target score, as well as updating the
|
||||
* score if it's closed
|
||||
*/
|
||||
void doCricketScoreHits(int player, int scoreIndex, int numHits);
|
||||
|
||||
/**
|
||||
* Updates the score based upon what the dart hit
|
||||
*/
|
||||
void updateCricketScore(int player, int dartVal, int multiplier);
|
||||
|
||||
/**
|
||||
* Draw the darts left
|
||||
*/
|
||||
void drawDartsLeft(int dartNum, int computer);
|
||||
public:
|
||||
Darts(SherlockEngine *vm);
|
||||
|
||||
/**
|
||||
* Play the darts game
|
||||
*/
|
||||
void playDarts(GameType gameType);
|
||||
};
|
||||
|
||||
} // End of namespace Tattoo
|
||||
|
||||
} // End of namespace Sherlock
|
||||
|
||||
#endif
|
||||
34
engines/sherlock/tattoo/tattoo_debugger.cpp
Normal file
34
engines/sherlock/tattoo/tattoo_debugger.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sherlock/tattoo/tattoo_debugger.h"
|
||||
#include "sherlock/sherlock.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
namespace Tattoo {
|
||||
|
||||
TattooDebugger::TattooDebugger(SherlockEngine *vm) : Debugger(vm) {
|
||||
}
|
||||
|
||||
} // End of namespace Tattoo
|
||||
|
||||
} // End of namespace Sherlock
|
||||
43
engines/sherlock/tattoo/tattoo_debugger.h
Normal file
43
engines/sherlock/tattoo/tattoo_debugger.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SHERLOCK_TATTOO_DEBUGGER_H
|
||||
#define SHERLOCK_TATTOO_DEBUGGER_H
|
||||
|
||||
#include "sherlock/debugger.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
class SherlockEngine;
|
||||
|
||||
namespace Tattoo {
|
||||
|
||||
class TattooDebugger : public Debugger {
|
||||
public:
|
||||
TattooDebugger(SherlockEngine *vm);
|
||||
~TattooDebugger() override {}
|
||||
};
|
||||
|
||||
} // End of namespace Tattoo
|
||||
|
||||
} // End of namespace Sherlock
|
||||
|
||||
#endif /* SHERLOCK_TATTOO_DEBUGGER_H */
|
||||
1174
engines/sherlock/tattoo/tattoo_fixed_text.cpp
Normal file
1174
engines/sherlock/tattoo/tattoo_fixed_text.cpp
Normal file
File diff suppressed because it is too large
Load Diff
239
engines/sherlock/tattoo/tattoo_fixed_text.h
Normal file
239
engines/sherlock/tattoo/tattoo_fixed_text.h
Normal file
@@ -0,0 +1,239 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SHERLOCK_TATTOO_FIXED_TEXT_H
|
||||
#define SHERLOCK_TATTOO_FIXED_TEXT_H
|
||||
|
||||
#include "sherlock/fixed_text.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
namespace Tattoo {
|
||||
|
||||
enum FixedTextId {
|
||||
kFixedText_Inv1,
|
||||
kFixedText_InvDesc1,
|
||||
kFixedText_Inv2,
|
||||
kFixedText_InvDesc2,
|
||||
kFixedText_Inv3,
|
||||
kFixedText_InvDesc3,
|
||||
kFixedText_Inv4,
|
||||
kFixedText_InvDesc4,
|
||||
kFixedText_Inv5,
|
||||
kFixedText_InvDesc5,
|
||||
kFixedText_Inv6,
|
||||
kFixedText_InvDesc6,
|
||||
kFixedText_Inv7,
|
||||
kFixedText_InvDesc7,
|
||||
kFixedText_Inv8,
|
||||
kFixedText_InvDesc8,
|
||||
kFixedText_Open,
|
||||
kFixedText_Look,
|
||||
kFixedText_Talk,
|
||||
kFixedText_Use,
|
||||
kFixedText_Journal,
|
||||
kFixedText_Inventory,
|
||||
kFixedText_Options,
|
||||
kFixedText_Solve,
|
||||
kFixedText_With,
|
||||
kFixedText_NoEffect,
|
||||
kFixedText_NothingToSay,
|
||||
kFixedText_PickedUp,
|
||||
|
||||
kFixedText_Page,
|
||||
kFixedText_CloseJournal,
|
||||
kFixedText_SearchJournal,
|
||||
kFixedText_SaveJournal,
|
||||
kFixedText_AbortSearch,
|
||||
kFixedText_SearchBackwards,
|
||||
kFixedText_SearchForwards,
|
||||
kFixedText_TextNotFound,
|
||||
|
||||
kFixedText_DartsPlayerHolmes,
|
||||
kFixedText_DartsPlayerJock,
|
||||
kFixedText_DartsBull,
|
||||
kFixedText_DartsCurrentRound,
|
||||
kFixedText_DartsCurrentTotalPoints,
|
||||
kFixedText_DartsCurrentDart,
|
||||
kFixedText_DartsStartPressKey1,
|
||||
kFixedText_DartsStartPressKey2,
|
||||
kFixedText_DartsPressKey,
|
||||
kFixedText_DartsGameOver,
|
||||
kFixedText_DartsBusted,
|
||||
kFixedText_DartsWins,
|
||||
kFixedText_DartsScoredPoint,
|
||||
kFixedText_DartsScoredPoints,
|
||||
kFixedText_DartsHitSingle,
|
||||
kFixedText_DartsHitDouble,
|
||||
kFixedText_DartsHitTriple,
|
||||
kFixedText_DartsHitSingleBullseye,
|
||||
kFixedText_DartsHitDoubleBullseye,
|
||||
kFixedText_DartsHitTripleBullseye,
|
||||
|
||||
kFixedText_Apply,
|
||||
kFixedText_Water,
|
||||
kFixedText_Heat,
|
||||
kFixedText_LoadGame,
|
||||
kFixedText_SaveGame,
|
||||
kFixedText_Music,
|
||||
kFixedText_SoundEffects,
|
||||
kFixedText_Voices,
|
||||
kFixedText_TextWindows,
|
||||
kFixedText_TransparentMenus,
|
||||
kFixedText_ChangeFont,
|
||||
kFixedText_Off,
|
||||
kFixedText_On,
|
||||
kFixedText_Quit,
|
||||
kFixedText_AreYouSureYou,
|
||||
kFixedText_WishToQuit,
|
||||
kFixedText_Yes,
|
||||
kFixedText_No,
|
||||
kFixedText_EnterPassword,
|
||||
kFixedText_CorrectPassword,
|
||||
kFixedText_WatsonsJournal,
|
||||
kFixedText_JournalSaved,
|
||||
// SH2: People names
|
||||
kFixedText_People_SherlockHolmes,
|
||||
kFixedText_People_DrWatson,
|
||||
kFixedText_People_MrsHudson,
|
||||
kFixedText_People_StanleyForbes,
|
||||
kFixedText_People_MycroftHolmes,
|
||||
kFixedText_People_Wiggins,
|
||||
kFixedText_People_PoliceConstableBurns,
|
||||
kFixedText_People_AugustusTrimble,
|
||||
kFixedText_People_PoliceConstableDaley,
|
||||
kFixedText_People_Matron,
|
||||
kFixedText_People_SisterGrace,
|
||||
kFixedText_People_PrestonMcCabe,
|
||||
kFixedText_People_BobColleran,
|
||||
kFixedText_People_JonasRigby,
|
||||
kFixedText_People_PoliceConstableRoach,
|
||||
kFixedText_People_JamesDewar,
|
||||
kFixedText_People_SergeantJeremyDuncan,
|
||||
kFixedText_People_InspectorGregson,
|
||||
kFixedText_People_InspectorLestrade,
|
||||
kFixedText_People_JesseNeedhem,
|
||||
kFixedText_People_ArthurFleming,
|
||||
kFixedText_People_MrThomasPratt,
|
||||
kFixedText_People_MathildaTillieMason,
|
||||
kFixedText_People_AdrianRussell,
|
||||
kFixedText_People_EldridgeWhitney,
|
||||
kFixedText_People_Hepplethwaite,
|
||||
kFixedText_People_HoraceSilverbridge,
|
||||
kFixedText_People_OldSherman,
|
||||
kFixedText_People_MaxwellVerner,
|
||||
kFixedText_People_MillicentRedding,
|
||||
kFixedText_People_VirgilSilverbridge,
|
||||
kFixedText_People_GeorgeOKeeffe,
|
||||
kFixedText_People_LordDenysLawton,
|
||||
kFixedText_People_Jenkins,
|
||||
kFixedText_People_JockMahoney,
|
||||
kFixedText_People_Bartender,
|
||||
kFixedText_People_LadyCordeliaLockridge,
|
||||
kFixedText_People_Pettigrew,
|
||||
kFixedText_People_SirAveryFanshawe,
|
||||
kFixedText_People_Hodgkins,
|
||||
kFixedText_People_WilburBirdyHeywood,
|
||||
kFixedText_People_JacobFarthington,
|
||||
kFixedText_People_PhilipBledsoe,
|
||||
kFixedText_People_SidneyFowler,
|
||||
kFixedText_People_ProfessorTheodoreTotman,
|
||||
kFixedText_People_RoseHinchem,
|
||||
kFixedText_People_Tallboy,
|
||||
kFixedText_People_EthlebertStitchRumsey,
|
||||
kFixedText_People_CharlesFreedman,
|
||||
kFixedText_People_NigelHemmings,
|
||||
kFixedText_People_FairfaxCarter,
|
||||
kFixedText_People_WilhelmII,
|
||||
kFixedText_People_Wachthund,
|
||||
kFixedText_People_JonathanWilson,
|
||||
kFixedText_People_DavidLloydJones,
|
||||
kFixedText_People_EdwardHargrove,
|
||||
kFixedText_People_Misteray,
|
||||
kFixedText_People_TheLascar,
|
||||
kFixedText_People_Parrot,
|
||||
kFixedText_People_VincentScarrett,
|
||||
kFixedText_People_Alexandra,
|
||||
kFixedText_People_QueenVictoria,
|
||||
kFixedText_People_JohnBrown,
|
||||
kFixedText_People_APatient1,
|
||||
kFixedText_People_APatient2,
|
||||
kFixedText_People_Patron,
|
||||
kFixedText_People_QueenVictoria2,
|
||||
kFixedText_People_PatientInWhite,
|
||||
kFixedText_People_Lush,
|
||||
kFixedText_People_Drunk,
|
||||
kFixedText_People_Prostitute,
|
||||
kFixedText_People_Mudlark,
|
||||
kFixedText_People_Grinder,
|
||||
kFixedText_People_Bouncer,
|
||||
kFixedText_People_AgnesRatchet,
|
||||
kFixedText_People_AloysiusRatchet,
|
||||
kFixedText_People_RealEstateAgent,
|
||||
kFixedText_People_CandyClerk,
|
||||
kFixedText_People_Beadle,
|
||||
kFixedText_People_Prussian,
|
||||
kFixedText_People_MrsRowbottom,
|
||||
kFixedText_People_MissLloydJones,
|
||||
kFixedText_People_TavernPatron,
|
||||
kFixedText_People_User,
|
||||
kFixedText_People_Toby,
|
||||
kFixedText_People_Stationer,
|
||||
kFixedText_People_LawClerk,
|
||||
kFixedText_People_MinistryClerk,
|
||||
kFixedText_People_Bather,
|
||||
kFixedText_People_Maid,
|
||||
kFixedText_People_LadyFanshawe,
|
||||
kFixedText_People_SidneyRatchet,
|
||||
kFixedText_People_Boy,
|
||||
kFixedText_People_Patron2,
|
||||
kFixedText_People_ConstableBrit,
|
||||
kFixedText_People_WagonDriver
|
||||
};
|
||||
|
||||
struct FixedTextLanguageEntry {
|
||||
Common::Language language;
|
||||
const char *const *fixedTextArray;
|
||||
};
|
||||
|
||||
class TattooFixedText: public FixedText {
|
||||
private:
|
||||
const FixedTextLanguageEntry *_curLanguageEntry;
|
||||
public:
|
||||
TattooFixedText(SherlockEngine *vm);
|
||||
~TattooFixedText() override {}
|
||||
|
||||
/**
|
||||
* Gets text
|
||||
*/
|
||||
const char *getText(int fixedTextId) override;
|
||||
|
||||
/**
|
||||
* Get action message
|
||||
*/
|
||||
const Common::String getActionMessage(FixedTextActionId actionId, int messageIndex) override;
|
||||
};
|
||||
|
||||
} // End of namespace Tattoo
|
||||
|
||||
} // End of namespace Sherlock
|
||||
|
||||
#endif
|
||||
62
engines/sherlock/tattoo/tattoo_inventory.cpp
Normal file
62
engines/sherlock/tattoo/tattoo_inventory.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sherlock/tattoo/tattoo_inventory.h"
|
||||
#include "sherlock/tattoo/tattoo.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
namespace Tattoo {
|
||||
|
||||
TattooInventory::TattooInventory(SherlockEngine *vm) : Inventory(vm) {
|
||||
_invShapes.resize(8);
|
||||
}
|
||||
|
||||
TattooInventory::~TattooInventory() {
|
||||
}
|
||||
|
||||
void TattooInventory::loadInv() {
|
||||
// Exit if the inventory names are already loaded
|
||||
if (_names.size() > 0)
|
||||
return;
|
||||
|
||||
// Load the inventory names
|
||||
Common::SeekableReadStream *stream = _vm->_res->load("invent.txt");
|
||||
|
||||
int count = stream->readByte();
|
||||
|
||||
for (int idx = 0; idx < count; ++idx) {
|
||||
Common::String name;
|
||||
char c;
|
||||
while ((c = stream->readByte()) != 0)
|
||||
name += c;
|
||||
|
||||
_names.push_back(name);
|
||||
}
|
||||
|
||||
delete stream;
|
||||
|
||||
loadGraphics();
|
||||
}
|
||||
|
||||
} // End of namespace Tattoo
|
||||
|
||||
} // End of namespace Sherlock
|
||||
47
engines/sherlock/tattoo/tattoo_inventory.h
Normal file
47
engines/sherlock/tattoo/tattoo_inventory.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SHERLOCK_TATTOO_INVENTORY_H
|
||||
#define SHERLOCK_TATTOO_INVENTORY_H
|
||||
|
||||
#include "sherlock/inventory.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
namespace Tattoo {
|
||||
|
||||
class TattooInventory : public Inventory {
|
||||
public:
|
||||
TattooInventory(SherlockEngine *vm);
|
||||
~TattooInventory() override;
|
||||
|
||||
/**
|
||||
* Load the list of names the inventory items correspond to, if not already loaded,
|
||||
* and then calls loadGraphics to load the associated graphics
|
||||
*/
|
||||
void loadInv() override;
|
||||
};
|
||||
|
||||
} // End of namespace Tattoo
|
||||
|
||||
} // End of namespace Sherlock
|
||||
|
||||
#endif
|
||||
1170
engines/sherlock/tattoo/tattoo_journal.cpp
Normal file
1170
engines/sherlock/tattoo/tattoo_journal.cpp
Normal file
File diff suppressed because it is too large
Load Diff
123
engines/sherlock/tattoo/tattoo_journal.h
Normal file
123
engines/sherlock/tattoo/tattoo_journal.h
Normal file
@@ -0,0 +1,123 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SHERLOCK_TATTOO_JOURNAL_H
|
||||
#define SHERLOCK_TATTOO_JOURNAL_H
|
||||
|
||||
#include "sherlock/journal.h"
|
||||
#include "sherlock/image_file.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
namespace Tattoo {
|
||||
|
||||
enum JournalHighlight {
|
||||
JH_NONE = -1, JH_CLOSE = 0, JH_SEARCH = 1, JH_SAVE = 2,
|
||||
JH_SCROLL_LEFT = 3, JH_PAGE_LEFT = 4, JH_PAGE_RIGHT = 5, JH_SCROLL_RIGHT = 6, JH_THUMBNAIL = 7
|
||||
};
|
||||
|
||||
class TattooJournal : public Journal {
|
||||
private:
|
||||
ImageFile *_journalImages;
|
||||
int _selector, _oldSelector;
|
||||
bool _wait;
|
||||
bool _exitJournal;
|
||||
uint32 _scrollingTimer;
|
||||
int _savedIndex, _savedSub, _savedPage;
|
||||
|
||||
/**
|
||||
* Load the list of journal locations
|
||||
*/
|
||||
void loadLocations();
|
||||
|
||||
/**
|
||||
* Displays the controls used by the journal
|
||||
* @param mode 0: Normal journal buttons, 1: Search interface
|
||||
*/
|
||||
void drawControls(int mode);
|
||||
|
||||
/**
|
||||
* Draw the journal controls used by the journal
|
||||
*/
|
||||
void highlightJournalControls(bool slamIt);
|
||||
|
||||
/**
|
||||
* Draw the journal controls used in search mode
|
||||
*/
|
||||
void highlightSearchControls(bool slamIt);
|
||||
|
||||
void drawScrollBar();
|
||||
|
||||
/**
|
||||
* Check for and handle any pending keyboard events
|
||||
*/
|
||||
void handleKeyboardEvents();
|
||||
|
||||
/**
|
||||
* Handle mouse presses on interface buttons
|
||||
*/
|
||||
void handleButtons();
|
||||
|
||||
/**
|
||||
* Disable the journal controls
|
||||
*/
|
||||
void disableControls();
|
||||
|
||||
/**
|
||||
* Get in a name to search through the journal for
|
||||
*/
|
||||
int getFindName(bool printError);
|
||||
|
||||
/**
|
||||
* Save the journal to file
|
||||
*/
|
||||
void saveJournal();
|
||||
|
||||
/**
|
||||
* Show a message that the journal has been saved to file
|
||||
*/
|
||||
void showSavedDialog();
|
||||
public:
|
||||
TattooJournal(SherlockEngine *vm);
|
||||
~TattooJournal() override {}
|
||||
|
||||
/**
|
||||
* Show the journal
|
||||
*/
|
||||
void show();
|
||||
public:
|
||||
/**
|
||||
* Draw the journal background, frame, and interface buttons
|
||||
*/
|
||||
void drawFrame() override;
|
||||
|
||||
/**
|
||||
* Records statements that are said, in the order which they are said. The player
|
||||
* can then read the journal to review them
|
||||
*/
|
||||
void record(int converseNum, int statementNum, bool replyOnly = false) override;
|
||||
};
|
||||
|
||||
} // End of namespace Tattoo
|
||||
|
||||
} // End of namespace Sherlock
|
||||
|
||||
#endif
|
||||
446
engines/sherlock/tattoo/tattoo_map.cpp
Normal file
446
engines/sherlock/tattoo/tattoo_map.cpp
Normal file
@@ -0,0 +1,446 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sherlock/tattoo/tattoo_map.h"
|
||||
#include "sherlock/tattoo/tattoo_scene.h"
|
||||
#include "sherlock/tattoo/tattoo.h"
|
||||
|
||||
#include "backends/keymapper/keymapper.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
namespace Tattoo {
|
||||
|
||||
#define MAP_NAME_COLOR 131
|
||||
#define CLOSEUP_STEPS 30
|
||||
#define SCROLL_SPEED 16
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
void MapEntry::clear() {
|
||||
_iconNum = -1;
|
||||
_description = "";
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
TattooMap::TattooMap(SherlockEngine *vm) : Map(vm), _mapTooltip(vm) {
|
||||
_iconImages = nullptr;
|
||||
_bgFound = _oldBgFound = 0;
|
||||
|
||||
loadData();
|
||||
}
|
||||
|
||||
int TattooMap::show() {
|
||||
Debugger &debugger = *_vm->_debugger;
|
||||
Events &events = *_vm->_events;
|
||||
Music &music = *_vm->_music;
|
||||
Resources &res = *_vm->_res;
|
||||
TattooScene &scene = *(TattooScene *)_vm->_scene;
|
||||
Screen &screen = *_vm->_screen;
|
||||
int result = 0;
|
||||
|
||||
// Check if we need to keep track of how many times player has been to the map
|
||||
for (uint idx = 0; idx < scene._sceneTripCounters.size(); ++idx) {
|
||||
SceneTripEntry &entry = scene._sceneTripCounters[idx];
|
||||
|
||||
if (entry._sceneNumber == OVERHEAD_MAP || entry._sceneNumber == OVERHEAD_MAP2) {
|
||||
if (--entry._numTimes == 0) {
|
||||
_vm->setFlagsDirect(entry._flag);
|
||||
scene._sceneTripCounters.remove_at(idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (music._musicOn) {
|
||||
// See if Holmes or Watson is the active character
|
||||
Common::String song;
|
||||
if (_vm->readFlags(FLAG_PLAYER_IS_HOLMES))
|
||||
// Player is Holmes
|
||||
song = "Cue9";
|
||||
else if (_vm->readFlags(FLAG_ALT_MAP_MUSIC))
|
||||
song = "Cue8";
|
||||
else
|
||||
song = "Cue7";
|
||||
|
||||
if (music.loadSong(song)) {
|
||||
music.startSong();
|
||||
}
|
||||
}
|
||||
|
||||
screen.initPaletteFade(1364485);
|
||||
|
||||
// Load the custom mouse cursors for the map
|
||||
ImageFile cursors("omouse.vgs");
|
||||
events.setCursor(cursors[0]._frame);
|
||||
events.warpMouse();
|
||||
|
||||
// Load the data for the map
|
||||
_iconImages = new ImageFile("mapicons.vgs");
|
||||
loadData();
|
||||
|
||||
// Load the palette
|
||||
Common::SeekableReadStream *stream = res.load("map.pal");
|
||||
stream->read(screen._cMap, Graphics::PALETTE_SIZE);
|
||||
screen.translatePalette(screen._cMap);
|
||||
delete stream;
|
||||
|
||||
// Load the map image and draw it to the back buffer
|
||||
ImageFile *map = new ImageFile("map.vgs");
|
||||
screen._backBuffer1.create(SHERLOCK_SCREEN_WIDTH * 2, SHERLOCK_SCREEN_HEIGHT * 2);
|
||||
screen._backBuffer1.SHblitFrom((*map)[0], Common::Point(0, 0));
|
||||
screen.activateBackBuffer1();
|
||||
delete map;
|
||||
|
||||
screen.clear();
|
||||
screen.setPalette(screen._cMap);
|
||||
drawMapIcons();
|
||||
|
||||
// Copy the map drawn in the back buffer to the secondary back buffer
|
||||
screen._backBuffer2.create(SHERLOCK_SCREEN_WIDTH * 2, SHERLOCK_SCREEN_HEIGHT * 2);
|
||||
screen._backBuffer2.SHblitFrom(screen._backBuffer1);
|
||||
|
||||
// Set initial scroll position, forcing the map to be displayed
|
||||
_targetScroll = _bigPos;
|
||||
screen._currentScroll = Common::Point(-1, -1);
|
||||
|
||||
Common::Keymapper *keymapper = g_system->getEventManager()->getKeymapper();
|
||||
keymapper->getKeymap("tattoo")->setEnabled(false);
|
||||
keymapper->getKeymap("tattoo-map")->setEnabled(true);
|
||||
|
||||
do {
|
||||
// Allow for event processing and get the current mouse position
|
||||
events.pollEventsAndWait();
|
||||
events.setButtonState();
|
||||
Common::Point mousePos = events.screenMousePos();
|
||||
|
||||
if (debugger._showAllLocations == LOC_REFRESH) {
|
||||
drawMapIcons();
|
||||
screen.slamArea(screen._currentScroll.x, screen._currentScroll.y, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_WIDTH);
|
||||
}
|
||||
|
||||
music.checkSongProgress();
|
||||
checkMapNames(true);
|
||||
|
||||
if (mousePos.x < (SHERLOCK_SCREEN_WIDTH / 6))
|
||||
_targetScroll.x -= 2 * SCROLL_SPEED * (SHERLOCK_SCREEN_WIDTH / 6 - mousePos.x) / (SHERLOCK_SCREEN_WIDTH / 6);
|
||||
if (mousePos.x > (SHERLOCK_SCREEN_WIDTH * 5 / 6))
|
||||
_targetScroll.x += 2 * SCROLL_SPEED * (mousePos.x - (SHERLOCK_SCREEN_WIDTH * 5 / 6)) / (SHERLOCK_SCREEN_WIDTH / 6);
|
||||
if (mousePos.y < (SHERLOCK_SCREEN_HEIGHT / 6))
|
||||
_targetScroll.y -= 2 * SCROLL_SPEED * (SHERLOCK_SCREEN_HEIGHT / 6 - mousePos.y) / (SHERLOCK_SCREEN_HEIGHT / 6);
|
||||
if (mousePos.y > (SHERLOCK_SCREEN_HEIGHT * 5 / 6))
|
||||
_targetScroll.y += 2 * SCROLL_SPEED * (mousePos.y - SHERLOCK_SCREEN_HEIGHT * 5 / 6) / (SHERLOCK_SCREEN_HEIGHT / 6);
|
||||
|
||||
if (_targetScroll.x < 0)
|
||||
_targetScroll.x = 0;
|
||||
if ((_targetScroll.x + SHERLOCK_SCREEN_WIDTH) > screen._backBuffer1.width())
|
||||
_targetScroll.x = screen._backBuffer1.width() - SHERLOCK_SCREEN_WIDTH;
|
||||
if (_targetScroll.y < 0)
|
||||
_targetScroll.y = 0;
|
||||
if ((_targetScroll.y + SHERLOCK_SCREEN_HEIGHT) > screen._backBuffer1.height())
|
||||
_targetScroll.y = screen._backBuffer1.height() - SHERLOCK_SCREEN_HEIGHT;
|
||||
|
||||
// Check the action
|
||||
if (events.actionHit()) {
|
||||
Common::CustomEventType action = events.getAction();
|
||||
|
||||
switch (action) {
|
||||
case kActionTattooMapTopLeft:
|
||||
_targetScroll.x = 0;
|
||||
_targetScroll.y = 0;
|
||||
break;
|
||||
|
||||
case kActionTattooMapBottomRight:
|
||||
_targetScroll.x = screen._backBuffer1.width() - SHERLOCK_SCREEN_WIDTH;
|
||||
_targetScroll.y = screen._backBuffer1.height() - SHERLOCK_SCREEN_HEIGHT;
|
||||
break;
|
||||
|
||||
case kActionTattooMapUp:
|
||||
_targetScroll.y -= SHERLOCK_SCREEN_HEIGHT;
|
||||
if (_targetScroll.y < 0)
|
||||
_targetScroll.y = 0;
|
||||
break;
|
||||
|
||||
case kActionTattooMapDown:
|
||||
_targetScroll.y += SHERLOCK_SCREEN_HEIGHT;
|
||||
if (_targetScroll.y > (screen._backBuffer1.height() - SHERLOCK_SCREEN_HEIGHT))
|
||||
_targetScroll.y = screen._backBuffer1.height() - SHERLOCK_SCREEN_HEIGHT;
|
||||
break;
|
||||
|
||||
case kActionTattooMapSelect:
|
||||
events._pressed = false;
|
||||
events._oldButtons = 0;
|
||||
events._released = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle any scrolling of the map
|
||||
if (screen._currentScroll != _targetScroll) {
|
||||
// If there is a Text description being displayed, restore the area under it
|
||||
_mapTooltip.erase();
|
||||
|
||||
screen._currentScroll = _targetScroll;
|
||||
|
||||
checkMapNames(false);
|
||||
screen.slamArea(_targetScroll.x, _targetScroll.y, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
|
||||
}
|
||||
|
||||
// Handling if a location has been clicked on
|
||||
if (events._released && _bgFound != -1) {
|
||||
// If there is a Text description being displayed, restore the area under it
|
||||
_mapTooltip.erase();
|
||||
|
||||
// Save the current scroll position on the map
|
||||
_bigPos = screen._currentScroll;
|
||||
|
||||
showCloseUp(_bgFound);
|
||||
result = _bgFound + 1;
|
||||
}
|
||||
} while (!result && !_vm->shouldQuit());
|
||||
|
||||
keymapper->getKeymap("tattoo-map")->setEnabled(false);
|
||||
keymapper->getKeymap("tattoo")->setEnabled(true);
|
||||
|
||||
music.stopMusic();
|
||||
events.clearEvents();
|
||||
_mapTooltip.banishWindow();
|
||||
|
||||
// Reset the back buffers back to standard size
|
||||
screen._backBuffer1.create(SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
|
||||
screen._backBuffer2.create(SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
|
||||
screen.activateBackBuffer1();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void TattooMap::loadData() {
|
||||
Resources &res = *_vm->_res;
|
||||
char c;
|
||||
|
||||
Common::SeekableReadStream *stream = res.load("map.txt");
|
||||
|
||||
_data.resize(100);
|
||||
for (uint idx = 0; idx < _data.size(); ++idx)
|
||||
_data[idx].clear();
|
||||
|
||||
do
|
||||
{
|
||||
// Find the start of the number
|
||||
do {
|
||||
c = stream->readByte();
|
||||
if (stream->pos() >= stream->size())
|
||||
break;
|
||||
} while (c < '0' || c > '9');
|
||||
if (stream->pos() >= stream->size())
|
||||
break;
|
||||
|
||||
// Get the scene number
|
||||
Common::String locStr;
|
||||
locStr += c;
|
||||
while ((c = stream->readByte()) != '.')
|
||||
locStr += c;
|
||||
MapEntry &mapEntry = _data[atoi(locStr.c_str()) - 1];
|
||||
|
||||
// Get the location name
|
||||
while (stream->readByte() != '"')
|
||||
;
|
||||
|
||||
while ((c = stream->readByte()) != '"')
|
||||
mapEntry._description += c;
|
||||
|
||||
// Find the ( specifying the (X,Y) position of the Icon
|
||||
while (stream->readByte() != '(')
|
||||
;
|
||||
|
||||
// Get the X Position of the icon
|
||||
Common::String numStr;
|
||||
while ((c = stream->readByte()) != ',')
|
||||
numStr += c;
|
||||
mapEntry.x = atoi(numStr.c_str());
|
||||
|
||||
// Get the Y position of the icon
|
||||
numStr = "";
|
||||
while ((c = stream->readByte()) != ')')
|
||||
numStr += c;
|
||||
mapEntry.y = atoi(numStr.c_str());
|
||||
|
||||
// Find and get the location's icon number
|
||||
while (stream->readByte() != '#')
|
||||
;
|
||||
|
||||
Common::String iconStr;
|
||||
while (stream->pos() < stream->size() && (c = stream->readByte()) != '\r')
|
||||
iconStr += c;
|
||||
|
||||
mapEntry._iconNum = atoi(iconStr.c_str()) - 1;
|
||||
} while (stream->pos() < stream->size());
|
||||
|
||||
delete stream;
|
||||
}
|
||||
|
||||
void TattooMap::drawMapIcons() {
|
||||
Debugger &debugger = *_vm->_debugger;
|
||||
Screen &screen = *_vm->_screen;
|
||||
|
||||
for (uint idx = 0; idx < _data.size(); ++idx) {
|
||||
if (debugger._showAllLocations != LOC_DISABLED)
|
||||
_vm->setFlagsDirect(idx + 1);
|
||||
|
||||
if (_data[idx]._iconNum != -1 && _vm->readFlags(idx + 1)) {
|
||||
MapEntry &mapEntry = _data[idx];
|
||||
ImageFrame &img = (*_iconImages)[mapEntry._iconNum];
|
||||
screen._backBuffer1.SHtransBlitFrom(img._frame, Common::Point(mapEntry.x - img._width / 2,
|
||||
mapEntry.y - img._height / 2));
|
||||
}
|
||||
}
|
||||
|
||||
if (debugger._showAllLocations == LOC_REFRESH)
|
||||
debugger._showAllLocations = LOC_ALL;
|
||||
}
|
||||
|
||||
void TattooMap::checkMapNames(bool slamIt) {
|
||||
Events &events = *_vm->_events;
|
||||
Common::Point mapPos = events.mousePos();
|
||||
|
||||
// See if the mouse is pointing at any of the map locations
|
||||
_bgFound = -1;
|
||||
|
||||
for (uint idx = 0; idx < _data.size(); ++idx) {
|
||||
if (_data[idx]._iconNum != -1 && _vm->readFlags(idx + 1)) {
|
||||
MapEntry &mapEntry = _data[idx];
|
||||
ImageFrame &img = (*_iconImages)[mapEntry._iconNum];
|
||||
Common::Rect r(mapEntry.x - img._width / 2, mapEntry.y - img._height / 2,
|
||||
mapEntry.x + img._width / 2, mapEntry.y + img._height / 2);
|
||||
|
||||
if (r.contains(mapPos)) {
|
||||
_bgFound = idx;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle updating the tooltip
|
||||
if (_bgFound != _oldBgFound) {
|
||||
if (_bgFound == -1) {
|
||||
_mapTooltip.setText("");
|
||||
} else {
|
||||
const Common::String &desc = _data[_bgFound]._description;
|
||||
_mapTooltip.setText(desc);
|
||||
}
|
||||
|
||||
_oldBgFound = _bgFound;
|
||||
}
|
||||
|
||||
_mapTooltip.handleEvents();
|
||||
if (slamIt)
|
||||
_mapTooltip.draw();
|
||||
}
|
||||
|
||||
void TattooMap::restoreArea(const Common::Rect &bounds) {
|
||||
Screen &screen = *_vm->_screen;
|
||||
|
||||
Common::Rect r = bounds;
|
||||
r.clip(Common::Rect(0, 0, screen._backBuffer1.width(), screen._backBuffer1.height()));
|
||||
|
||||
if (!r.isEmpty())
|
||||
screen._backBuffer1.SHblitFrom(screen._backBuffer2, Common::Point(r.left, r.top), r);
|
||||
}
|
||||
|
||||
void TattooMap::showCloseUp(int closeUpNum) {
|
||||
Events &events = *_vm->_events;
|
||||
Screen &screen = *_vm->_screen;
|
||||
|
||||
// Hide the cursor
|
||||
events.hideCursor();
|
||||
|
||||
// Get the closeup images
|
||||
Common::Path fname(Common::String::format("res%02d.vgs", closeUpNum + 1));
|
||||
ImageFile pic(fname);
|
||||
|
||||
Point32 closeUp(_data[closeUpNum].x * 100, _data[closeUpNum].y * 100);
|
||||
Point32 delta((SHERLOCK_SCREEN_WIDTH / 2 - closeUp.x / 100) * 100 / CLOSEUP_STEPS,
|
||||
(SHERLOCK_SCREEN_HEIGHT / 2 - closeUp.y / 100) * 100 / CLOSEUP_STEPS);
|
||||
Common::Rect oldBounds(closeUp.x / 100, closeUp.y / 100, closeUp.x / 100 + 1, closeUp.y / 100 + 1);
|
||||
int size = 64;
|
||||
int n = 256;
|
||||
int deltaVal = 512;
|
||||
bool minimize = false;
|
||||
int scaleVal, newSize;
|
||||
|
||||
do {
|
||||
scaleVal = n;
|
||||
newSize = pic[0].sDrawXSize(n);
|
||||
|
||||
if (newSize > size) {
|
||||
if (minimize)
|
||||
deltaVal /= 2;
|
||||
n += deltaVal;
|
||||
} else {
|
||||
minimize = true;
|
||||
deltaVal /= 2;
|
||||
n -= deltaVal;
|
||||
if (n < 1)
|
||||
n = 1;
|
||||
}
|
||||
} while (deltaVal && size != newSize);
|
||||
|
||||
int deltaScale = (SCALE_THRESHOLD - scaleVal) / CLOSEUP_STEPS;
|
||||
|
||||
for (int step = 0; step < CLOSEUP_STEPS; ++step) {
|
||||
Common::Point picSize(pic[0].sDrawXSize(scaleVal), pic[0].sDrawYSize(scaleVal));
|
||||
Common::Point pt(screen._currentScroll.x + closeUp.x / 100 - picSize.x / 2,
|
||||
screen._currentScroll.y + closeUp.y / 100 - picSize.y / 2);
|
||||
|
||||
restoreArea(oldBounds);
|
||||
screen._backBuffer1.SHtransBlitFrom(pic[0], pt, false, scaleVal);
|
||||
|
||||
screen.slamRect(oldBounds);
|
||||
screen.slamArea(pt.x, pt.y, picSize.x, picSize.y);
|
||||
|
||||
oldBounds = Common::Rect(pt.x, pt.y, pt.x + picSize.x + 1, pt.y + picSize.y + 1);
|
||||
closeUp += delta;
|
||||
scaleVal += deltaScale;
|
||||
|
||||
events.wait(1);
|
||||
}
|
||||
|
||||
// Handle final drawing of closeup
|
||||
Common::Rect r(screen._currentScroll.x + SHERLOCK_SCREEN_WIDTH / 2 - pic[0]._width / 2,
|
||||
screen._currentScroll.y + SHERLOCK_SCREEN_HEIGHT / 2 - pic[0]._height / 2,
|
||||
screen._currentScroll.x + SHERLOCK_SCREEN_WIDTH / 2 - pic[0]._width / 2 + pic[0]._width,
|
||||
screen._currentScroll.y + SHERLOCK_SCREEN_HEIGHT / 2 - pic[0]._height / 2 + pic[0]._height);
|
||||
|
||||
restoreArea(oldBounds);
|
||||
screen._backBuffer1.SHtransBlitFrom(pic[0], Common::Point(r.left, r.top));
|
||||
screen.slamRect(oldBounds);
|
||||
screen.slamRect(r);
|
||||
|
||||
events.wait(60);
|
||||
events.showCursor();
|
||||
}
|
||||
|
||||
} // End of namespace Tattoo
|
||||
|
||||
} // End of namespace Sherlock
|
||||
92
engines/sherlock/tattoo/tattoo_map.h
Normal file
92
engines/sherlock/tattoo/tattoo_map.h
Normal file
@@ -0,0 +1,92 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SHERLOCK_TATTOO_MAP_H
|
||||
#define SHERLOCK_TATTOO_MAP_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "sherlock/map.h"
|
||||
#include "sherlock/resources.h"
|
||||
#include "sherlock/surface.h"
|
||||
#include "sherlock/tattoo/widget_tooltip.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
class SherlockEngine;
|
||||
|
||||
namespace Tattoo {
|
||||
|
||||
struct MapEntry : Common::Point {
|
||||
int _iconNum;
|
||||
Common::String _description;
|
||||
|
||||
MapEntry() : Common::Point(), _iconNum(-1) {}
|
||||
MapEntry(int posX, int posY, int iconNum) : Common::Point(posX, posY), _iconNum(iconNum) {}
|
||||
void clear();
|
||||
};
|
||||
|
||||
class TattooMap : public Map {
|
||||
private:
|
||||
Common::Array<MapEntry> _data;
|
||||
ImageFile *_iconImages;
|
||||
int _bgFound, _oldBgFound;
|
||||
WidgetMapTooltip _mapTooltip;
|
||||
Common::Point _targetScroll;
|
||||
|
||||
/**
|
||||
* Load data needed for the map
|
||||
*/
|
||||
void loadData();
|
||||
|
||||
/**
|
||||
* Draws all available location icons onto the back buffer
|
||||
*/
|
||||
void drawMapIcons();
|
||||
|
||||
/**
|
||||
* Draws the location names of whatever the mouse moves over on the map
|
||||
*/
|
||||
void checkMapNames(bool slamIt);
|
||||
|
||||
/**
|
||||
* Restores an area of the map background
|
||||
*/
|
||||
void restoreArea(const Common::Rect &bounds);
|
||||
|
||||
/**
|
||||
* This will load a specified close up and zoom it up to the middle of the screen
|
||||
*/
|
||||
void showCloseUp(int closeUpNum);
|
||||
public:
|
||||
TattooMap(SherlockEngine *vm);
|
||||
~TattooMap() override {}
|
||||
|
||||
/**
|
||||
* Show the map
|
||||
*/
|
||||
int show() override;
|
||||
};
|
||||
|
||||
} // End of namespace Tattoo
|
||||
|
||||
} // End of namespace Sherlock
|
||||
|
||||
#endif
|
||||
1519
engines/sherlock/tattoo/tattoo_people.cpp
Normal file
1519
engines/sherlock/tattoo/tattoo_people.cpp
Normal file
File diff suppressed because it is too large
Load Diff
279
engines/sherlock/tattoo/tattoo_people.h
Normal file
279
engines/sherlock/tattoo/tattoo_people.h
Normal file
@@ -0,0 +1,279 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SHERLOCK_TATTOO_PEOPLE_H
|
||||
#define SHERLOCK_TATTOO_PEOPLE_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/stack.h"
|
||||
#include "sherlock/people.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
class SherlockEngine;
|
||||
|
||||
namespace Tattoo {
|
||||
|
||||
// Animation sequence identifiers for characters
|
||||
enum TattooSequences {
|
||||
// Walk Sequences Numbers for NPCs
|
||||
WALK_UP = 0,
|
||||
WALK_UPRIGHT = 1,
|
||||
WALK_RIGHT = 2,
|
||||
WALK_DOWNRIGHT = 3,
|
||||
WALK_DOWN = 4,
|
||||
WALK_DOWNLEFT = 5,
|
||||
WALK_LEFT = 6,
|
||||
WALK_UPLEFT = 7,
|
||||
|
||||
// Stop Sequences Numbers for NPCs
|
||||
STOP_UP = 8,
|
||||
STOP_UPRIGHT = 9,
|
||||
STOP_RIGHT = 10,
|
||||
STOP_DOWNRIGHT = 11,
|
||||
STOP_DOWN = 12,
|
||||
STOP_DOWNLEFT = 13,
|
||||
STOP_LEFT = 14,
|
||||
STOP_UPLEFT = 15,
|
||||
|
||||
// NPC Talk Sequence Numbers
|
||||
TALK_UPRIGHT = 16,
|
||||
TALK_RIGHT = 17,
|
||||
TALK_DOWNRIGHT = 18,
|
||||
TALK_DOWNLEFT = 19,
|
||||
TALK_LEFT = 20,
|
||||
TALK_UPLEFT = 21,
|
||||
|
||||
// NPC Listen Sequence Numbers
|
||||
LISTEN_UPRIGHT = 22,
|
||||
LISTEN_RIGHT = 23,
|
||||
LISTEN_DOWNRIGHT = 24,
|
||||
LISTEN_DOWNLEFT = 25,
|
||||
LISTEN_LEFT = 26,
|
||||
LISTEN_UPLEFT = 27
|
||||
};
|
||||
|
||||
enum NpcPath {
|
||||
NPCPATH_SET_DEST = 1,
|
||||
NPCPATH_PAUSE = 2,
|
||||
NPCPATH_SET_TALK_FILE = 3,
|
||||
NPCPATH_CALL_TALK_FILE = 4,
|
||||
NPCPATH_TAKE_NOTES = 5,
|
||||
NPCPATH_FACE_HOLMES = 6,
|
||||
NPCPATH_PATH_LABEL = 7,
|
||||
NPCPATH_GOTO_LABEL = 8,
|
||||
NPCPATH_IFFLAG_GOTO_LABEL = 9
|
||||
};
|
||||
|
||||
struct SavedNPCPath {
|
||||
byte _path[MAX_NPC_PATH];
|
||||
int _npcIndex;
|
||||
int _npcPause;
|
||||
Point32 _position;
|
||||
int _npcFacing;
|
||||
bool _lookHolmes;
|
||||
|
||||
SavedNPCPath();
|
||||
SavedNPCPath(byte path[MAX_NPC_PATH], int npcIndex, int npcPause, const Point32 &position,
|
||||
int npcFacing, bool lookHolmes);
|
||||
};
|
||||
|
||||
class TattooPerson: public Person {
|
||||
private:
|
||||
Point32 _nextDest;
|
||||
private:
|
||||
bool checkCollision() const;
|
||||
|
||||
/**
|
||||
* Free the alternate graphics used by NPCs
|
||||
*/
|
||||
void freeAltGraphics();
|
||||
protected:
|
||||
/**
|
||||
* Get the source position for a character potentially affected by scaling
|
||||
*/
|
||||
Common::Point getSourcePoint() const override;
|
||||
public:
|
||||
Common::Stack<SavedNPCPath> _pathStack;
|
||||
int _npcIndex;
|
||||
int _npcPause;
|
||||
byte _npcPath[MAX_NPC_PATH];
|
||||
bool _npcMoved;
|
||||
int _npcFacing;
|
||||
bool _resetNPCPath;
|
||||
int _savedNpcSequence;
|
||||
int _savedNpcFrame;
|
||||
int _tempX;
|
||||
int _tempScaleVal;
|
||||
bool _updateNPCPath;
|
||||
bool _lookHolmes;
|
||||
public:
|
||||
TattooPerson();
|
||||
~TattooPerson() override;
|
||||
|
||||
/**
|
||||
* Clear the NPC related data
|
||||
*/
|
||||
void clearNPC();
|
||||
|
||||
/**
|
||||
* Called from doBgAnim to move NPCs along any set paths. If an NPC is paused in his path,
|
||||
* he will remain paused until his pause timer runs out. If he is walking somewhere,
|
||||
* he will continue walking there until he reaches the dest position. When an NPC stops moving,
|
||||
* the next element of his path is processed.
|
||||
*
|
||||
* The path is an array of bytes with control codes followed by their parameters as needed.
|
||||
*/
|
||||
void updateNPC();
|
||||
|
||||
/**
|
||||
* Push the NPC's path data onto the path stack for when a talk file moves the NPC that
|
||||
* has some control codes.
|
||||
*/
|
||||
void pushNPCPath();
|
||||
|
||||
/**
|
||||
* Pull an NPC's path data that has been previously saved on the path stack for that character.
|
||||
* There are two possibilities for when the NPC was interrupted, and both are handled differently:
|
||||
* 1) The NPC was paused at a position
|
||||
* If the NPC didn't move, we can just restore his pause counter and exit. But if he did move,
|
||||
* he must return to that position, and the path index must be reset to the pause he was executing.
|
||||
* This means that the index must be decremented by 3
|
||||
* 2) The NPC was in route to a position
|
||||
* He must be set to walk to that position again. This is done by moving the path index
|
||||
* so that it points to the code that set the NPC walking there in the first place.
|
||||
* The regular calls to updateNPC will handle the rest
|
||||
*/
|
||||
void pullNPCPath();
|
||||
|
||||
/**
|
||||
* Checks a sprite associated with an NPC to see if the frame sequence specified
|
||||
* in the sequence number uses alternate graphics, and if so if they need to be loaded
|
||||
*/
|
||||
void checkWalkGraphics();
|
||||
|
||||
/**
|
||||
* Synchronize the data for a savegame
|
||||
*/
|
||||
void synchronize(Serializer &s);
|
||||
|
||||
|
||||
/**
|
||||
* Walk Holmes to the NPC
|
||||
*/
|
||||
void walkHolmesToNPC();
|
||||
|
||||
/**
|
||||
* Walk both the specified character and Holmes to specified destination positions
|
||||
*/
|
||||
void walkBothToCoords(const PositionFacing &holmesDest, const PositionFacing &npcDest);
|
||||
|
||||
/**
|
||||
* This adjusts the sprites position, as well as its animation sequence:
|
||||
*/
|
||||
void adjustSprite() override;
|
||||
|
||||
/**
|
||||
* Bring a moving character to a standing position
|
||||
*/
|
||||
void gotoStand() override;
|
||||
|
||||
/**
|
||||
* Set the variables for moving a character from one poisition to another
|
||||
* in a straight line
|
||||
*/
|
||||
void setWalking() override;
|
||||
|
||||
/**
|
||||
* Walk to the co-ordinates passed, and then face the given direction
|
||||
*/
|
||||
void walkToCoords(const Point32 &destPos, int destDir) override;
|
||||
|
||||
/**
|
||||
* Adjusts the frame and sequence variables of a sprite that corresponds to the current speaker
|
||||
* so that it points to the beginning of the sequence number's talk sequence in the object's
|
||||
* sequence buffer
|
||||
* @param seq Which sequence to use (if there's more than 1)
|
||||
* @remarks 1: First talk seq, 2: second talk seq, etc.
|
||||
*/
|
||||
void setObjTalkSequence(int seq) override;
|
||||
|
||||
/**
|
||||
* Center the visible screen so that the person is in the center of the screen
|
||||
*/
|
||||
void centerScreenOnPerson() override;
|
||||
};
|
||||
|
||||
class TattooPeople : public People {
|
||||
public:
|
||||
TattooPeople(SherlockEngine *vm);
|
||||
~TattooPeople() override {}
|
||||
|
||||
TattooPerson &operator[](PeopleId id) { return *(TattooPerson *)_data[id]; }
|
||||
TattooPerson &operator[](int idx) { return *(TattooPerson *)_data[idx]; }
|
||||
|
||||
/**
|
||||
* Restore any saved NPC walk path data from any of the NPCs
|
||||
*/
|
||||
void pullNPCPaths();
|
||||
|
||||
/**
|
||||
* Finds the scene background object corresponding to a specified speaker
|
||||
*/
|
||||
int findSpeaker(int speaker) override;
|
||||
|
||||
/**
|
||||
* Synchronize the data for a savegame
|
||||
*/
|
||||
void synchronize(Serializer &s) override;
|
||||
|
||||
/**
|
||||
* Change the sequence of the scene background object associated with the specified speaker.
|
||||
*/
|
||||
void setTalkSequence(int speaker, int sequenceNum = 1) override;
|
||||
|
||||
/**
|
||||
* Load the walking images for Sherlock
|
||||
*/
|
||||
bool loadWalk() override;
|
||||
|
||||
/**
|
||||
* Restrict passed point to zone using Sherlock's positioning rules
|
||||
*/
|
||||
const Common::Point restrictToZone(int zoneId, const Common::Point &destPos) override;
|
||||
|
||||
/**
|
||||
* If the specified speaker is a background object, it will set it so that it uses
|
||||
* the Listen Sequence (specified by the sequence number). If the current sequence
|
||||
* has an Allow Talk Code in it, the _gotoSeq field will be set so that the object
|
||||
* begins listening as soon as it hits the Allow Talk Code. If there is no Abort Code,
|
||||
* the Listen Sequence will begin immediately.
|
||||
* @param speaker Who is speaking
|
||||
* @param sequenceNum Which listen sequence to use
|
||||
*/
|
||||
void setListenSequence(int speaker, int sequenceNum = 1) override;
|
||||
};
|
||||
|
||||
} // End of namespace Tattoo
|
||||
|
||||
} // End of namespace Sherlock
|
||||
|
||||
#endif
|
||||
130
engines/sherlock/tattoo/tattoo_resources.cpp
Normal file
130
engines/sherlock/tattoo/tattoo_resources.cpp
Normal file
@@ -0,0 +1,130 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sherlock/tattoo/tattoo_resources.h"
|
||||
#include "sherlock/tattoo/tattoo_fixed_text.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
namespace Tattoo {
|
||||
|
||||
const PeopleData PEOPLE_DATA[TATTOO_MAX_PEOPLE] = {
|
||||
{ "HOLM", kFixedText_People_SherlockHolmes },
|
||||
{ "WATS", kFixedText_People_DrWatson },
|
||||
{ "HUDS", kFixedText_People_MrsHudson },
|
||||
{ "FORB", kFixedText_People_StanleyForbes },
|
||||
{ "MYCR", kFixedText_People_MycroftHolmes },
|
||||
{ "WIGG", kFixedText_People_Wiggins },
|
||||
{ "BURN", kFixedText_People_PoliceConstableBurns },
|
||||
{ "TRIM", kFixedText_People_AugustusTrimble },
|
||||
{ "DALE", kFixedText_People_PoliceConstableDaley },
|
||||
{ "MATR", kFixedText_People_Matron },
|
||||
{ "GRAC", kFixedText_People_SisterGrace },
|
||||
{ "MCCA", kFixedText_People_PrestonMcCabe },
|
||||
{ "COLL", kFixedText_People_BobColleran },
|
||||
{ "JONA", kFixedText_People_JonasRigby },
|
||||
{ "ROAC", kFixedText_People_PoliceConstableRoach },
|
||||
{ "DEWA", kFixedText_People_JamesDewar },
|
||||
{ "JERE", kFixedText_People_SergeantJeremyDuncan },
|
||||
{ "GREG", kFixedText_People_InspectorGregson },
|
||||
{ "LEST", kFixedText_People_InspectorLestrade },
|
||||
{ "NEED", kFixedText_People_JesseNeedhem },
|
||||
{ "FLEM", kFixedText_People_ArthurFleming },
|
||||
{ "PRAT", kFixedText_People_MrThomasPratt },
|
||||
{ "TILL", kFixedText_People_MathildaTillieMason },
|
||||
{ "RUSS", kFixedText_People_AdrianRussell },
|
||||
{ "WHIT", kFixedText_People_EldridgeWhitney },
|
||||
{ "HEPP", kFixedText_People_Hepplethwaite },
|
||||
{ "HORA", kFixedText_People_HoraceSilverbridge },
|
||||
{ "SHER", kFixedText_People_OldSherman },
|
||||
{ "VERN", kFixedText_People_MaxwellVerner },
|
||||
{ "REDD", kFixedText_People_MillicentRedding },
|
||||
{ "VIRG", kFixedText_People_VirgilSilverbridge },
|
||||
{ "GEOR", kFixedText_People_GeorgeOKeeffe },
|
||||
{ "LAWT", kFixedText_People_LordDenysLawton },
|
||||
{ "JENK", kFixedText_People_Jenkins },
|
||||
{ "JOCK", kFixedText_People_JockMahoney },
|
||||
{ "BART", kFixedText_People_Bartender },
|
||||
{ "LADY", kFixedText_People_LadyCordeliaLockridge },
|
||||
{ "PETT", kFixedText_People_Pettigrew },
|
||||
{ "FANS", kFixedText_People_SirAveryFanshawe },
|
||||
{ "HODG", kFixedText_People_Hodgkins },
|
||||
{ "WILB", kFixedText_People_WilburBirdyHeywood },
|
||||
{ "JACO", kFixedText_People_JacobFarthington },
|
||||
{ "BLED", kFixedText_People_PhilipBledsoe },
|
||||
{ "FOWL", kFixedText_People_SidneyFowler },
|
||||
{ "PROF", kFixedText_People_ProfessorTheodoreTotman },
|
||||
{ "ROSE", kFixedText_People_RoseHinchem },
|
||||
{ "TALL", kFixedText_People_Tallboy },
|
||||
{ "STIT", kFixedText_People_EthlebertStitchRumsey },
|
||||
{ "FREE", kFixedText_People_CharlesFreedman },
|
||||
{ "HEMM", kFixedText_People_NigelHemmings },
|
||||
{ "CART", kFixedText_People_FairfaxCarter },
|
||||
{ "WILH", kFixedText_People_WilhelmII },
|
||||
{ "WACH", kFixedText_People_Wachthund },
|
||||
{ "WILS", kFixedText_People_JonathanWilson },
|
||||
{ "DAVE", kFixedText_People_DavidLloydJones },
|
||||
{ "HARG", kFixedText_People_EdwardHargrove },
|
||||
{ "MORI", kFixedText_People_Misteray },
|
||||
{ "LASC", kFixedText_People_TheLascar },
|
||||
{ "PARR", kFixedText_People_Parrot },
|
||||
{ "SCAR", kFixedText_People_VincentScarrett },
|
||||
{ "ALEX", kFixedText_People_Alexandra },
|
||||
{ "QUEE", kFixedText_People_QueenVictoria },
|
||||
{ "JOHN", kFixedText_People_JohnBrown },
|
||||
{ "PAT1", kFixedText_People_APatient1 },
|
||||
{ "PAT2", kFixedText_People_APatient2 },
|
||||
{ "PATR", kFixedText_People_Patron },
|
||||
{ "QUEN", kFixedText_People_QueenVictoria },
|
||||
{ "WITE", kFixedText_People_PatientInWhite },
|
||||
{ "LUSH", kFixedText_People_Lush },
|
||||
{ "DRNK", kFixedText_People_Drunk },
|
||||
{ "PROS", kFixedText_People_Prostitute },
|
||||
{ "MUDL", kFixedText_People_Mudlark },
|
||||
{ "GRIN", kFixedText_People_Grinder },
|
||||
{ "BOUN", kFixedText_People_Bouncer },
|
||||
{ "RATC", kFixedText_People_AgnesRatchet },
|
||||
{ "ALOY", kFixedText_People_AloysiusRatchet },
|
||||
{ "REAL", kFixedText_People_RealEstateAgent },
|
||||
{ "CAND", kFixedText_People_CandyClerk },
|
||||
{ "BEAD", kFixedText_People_Beadle },
|
||||
{ "PRUS", kFixedText_People_Prussian },
|
||||
{ "ROWB", kFixedText_People_MrsRowbottom },
|
||||
{ "MSLJ", kFixedText_People_MissLloydJones },
|
||||
{ "TPAT", kFixedText_People_TavernPatron },
|
||||
{ "USER", kFixedText_People_User },
|
||||
{ "TOBY", kFixedText_People_Toby },
|
||||
{ "STAT", kFixedText_People_Stationer },
|
||||
{ "CLRK", kFixedText_People_LawClerk },
|
||||
{ "CLER", kFixedText_People_MinistryClerk },
|
||||
{ "BATH", kFixedText_People_Bather },
|
||||
{ "MAID", kFixedText_People_Maid },
|
||||
{ "LADF", kFixedText_People_LadyFanshawe },
|
||||
{ "SIDN", kFixedText_People_SidneyRatchet },
|
||||
{ "BOYO", kFixedText_People_Boy },
|
||||
{ "PTR2", kFixedText_People_Patron2 },
|
||||
{ "BRIT", kFixedText_People_ConstableBrit },
|
||||
{ "DROV", kFixedText_People_WagonDriver }
|
||||
};
|
||||
|
||||
} // End of namespace Tattoo
|
||||
|
||||
} // End of namespace Sherlock
|
||||
44
engines/sherlock/tattoo/tattoo_resources.h
Normal file
44
engines/sherlock/tattoo/tattoo_resources.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SHERLOCK_TATTOO_RESOURCES_H
|
||||
#define SHERLOCK_TATTOO_RESOURCES_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
namespace Tattoo {
|
||||
|
||||
#define TATTOO_MAX_PEOPLE 96
|
||||
|
||||
struct PeopleData {
|
||||
const char *portrait;
|
||||
int fixedTextId;
|
||||
};
|
||||
|
||||
extern const PeopleData PEOPLE_DATA[TATTOO_MAX_PEOPLE];
|
||||
|
||||
} // End of namespace Tattoo
|
||||
|
||||
} // End of namespace Sherlock
|
||||
|
||||
#endif
|
||||
886
engines/sherlock/tattoo/tattoo_scene.cpp
Normal file
886
engines/sherlock/tattoo/tattoo_scene.cpp
Normal file
@@ -0,0 +1,886 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sherlock/tattoo/tattoo_scene.h"
|
||||
#include "sherlock/tattoo/tattoo_people.h"
|
||||
#include "sherlock/tattoo/tattoo_talk.h"
|
||||
#include "sherlock/tattoo/tattoo_user_interface.h"
|
||||
#include "sherlock/tattoo/tattoo.h"
|
||||
#include "sherlock/events.h"
|
||||
#include "sherlock/people.h"
|
||||
|
||||
#include "backends/keymapper/keymapper.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
namespace Tattoo {
|
||||
|
||||
const int FS_TRANS[8] = {
|
||||
STOP_UP, STOP_UPRIGHT, STOP_RIGHT, STOP_DOWNRIGHT, STOP_DOWN, STOP_DOWNLEFT, STOP_LEFT, STOP_UPLEFT
|
||||
};
|
||||
|
||||
/*----------------------------------------------------------------*/
|
||||
|
||||
struct ShapeEntry {
|
||||
Object *_shape;
|
||||
TattooPerson *_person;
|
||||
bool _isAnimation;
|
||||
int _yp;
|
||||
int _ordering;
|
||||
|
||||
ShapeEntry(TattooPerson *person, int yp, int ordering) :
|
||||
_shape(nullptr), _person(person), _yp(yp), _isAnimation(false), _ordering(ordering) {}
|
||||
ShapeEntry(Object *shape, int yp, int ordering) :
|
||||
_shape(shape), _person(nullptr), _yp(yp), _isAnimation(false), _ordering(ordering) {}
|
||||
ShapeEntry(int yp, int ordering) :
|
||||
_shape(nullptr), _person(nullptr), _yp(yp), _isAnimation(true), _ordering(ordering) {}
|
||||
};
|
||||
typedef Common::List<ShapeEntry> ShapeList;
|
||||
|
||||
static bool sortImagesY(const ShapeEntry &s1, const ShapeEntry &s2) {
|
||||
// Objects are order by the calculated Y position first and then, when both are equal,
|
||||
// by the order in which the entries were added
|
||||
return s1._yp < s2._yp || (s1._yp == s2._yp && s1._ordering < s2._ordering);
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------*/
|
||||
|
||||
TattooScene::TattooScene(SherlockEngine *vm) : Scene(vm), _labWidget(vm) {
|
||||
_labTableScene = false;
|
||||
}
|
||||
|
||||
bool TattooScene::loadScene(const Common::Path &filename) {
|
||||
TattooEngine &vm = *(TattooEngine *)_vm;
|
||||
Events &events = *_vm->_events;
|
||||
Music &music = *_vm->_music;
|
||||
Talk &talk = *_vm->_talk;
|
||||
TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
|
||||
|
||||
// If we're going to the first game scene after the intro sequence, flag it as finished
|
||||
if (vm._runningProlog && _currentScene == STARTING_GAME_SCENE) {
|
||||
vm._runningProlog = false;
|
||||
events.showCursor();
|
||||
talk._talkToAbort = false;
|
||||
|
||||
Common::Keymapper *keymapper = g_system->getEventManager()->getKeymapper();
|
||||
keymapper->getKeymap("tattoo-prolog")->setEnabled(false);
|
||||
}
|
||||
|
||||
// Check if it's a scene we need to keep 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle loading music for the scene
|
||||
if (talk._scriptMoreFlag != 1 && talk._scriptMoreFlag != 3)
|
||||
music._nextSongName = Common::String::format("res%02d", _currentScene);
|
||||
|
||||
// Set the NPC paths for the scene
|
||||
setNPCPath(WATSON);
|
||||
|
||||
// If it's a new song, then start it up
|
||||
if (music._currentSongName.compareToIgnoreCase(music._nextSongName)) {
|
||||
// WORKAROUND: Stop playing music after Diogenes fire scene in the intro,
|
||||
// since it overlaps slightly into the next scene
|
||||
if (talk._scriptName == "prol80p" && _currentScene == 80) {
|
||||
music.stopMusic();
|
||||
events.wait(5);
|
||||
}
|
||||
|
||||
if (music.loadSong(music._nextSongName)) {
|
||||
if (music._musicOn)
|
||||
music.startSong();
|
||||
}
|
||||
}
|
||||
|
||||
bool result = Scene::loadScene(filename);
|
||||
|
||||
if (_currentScene != STARTING_INTRO_SCENE) {
|
||||
// Set the menu/ui mode and whether we're in a lab table close-up scene
|
||||
_labTableScene = _currentScene > 91 && _currentScene < 100;
|
||||
ui._menuMode = _labTableScene ? LAB_MODE : STD_MODE;
|
||||
|
||||
if (_labTableScene)
|
||||
ui.addFixedWidget(&_labWidget);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void TattooScene::drawAllShapes() {
|
||||
TattooPeople &people = *(TattooPeople *)_vm->_people;
|
||||
Screen &screen = *_vm->_screen;
|
||||
ShapeList shapeList;
|
||||
int ordering = 0;
|
||||
|
||||
// Draw all objects and animations that are set to behind
|
||||
screen.setDisplayBounds(Common::Rect(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT));
|
||||
|
||||
// Draw all active shapes which are behind the person
|
||||
for (uint idx = 0; idx < _bgShapes.size(); ++idx) {
|
||||
Object &obj = _bgShapes[idx];
|
||||
|
||||
if (obj._type == ACTIVE_BG_SHAPE && obj._misc == BEHIND) {
|
||||
if (obj._quickDraw && obj._scaleVal == SCALE_THRESHOLD)
|
||||
screen._backBuffer1.SHblitFrom(*obj._imageFrame, obj._position);
|
||||
else
|
||||
screen._backBuffer1.SHtransBlitFrom(*obj._imageFrame, obj._position, obj._flags & OBJ_FLIPPED, obj._scaleVal);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw the animation if it is behind the person
|
||||
if (_activeCAnim.active() && _activeCAnim._zPlacement == BEHIND)
|
||||
screen._backBuffer1.SHtransBlitFrom(_activeCAnim._imageFrame, _activeCAnim._position,
|
||||
(_activeCAnim._flags & 4) >> 1, _activeCAnim._scaleVal);
|
||||
|
||||
screen.resetDisplayBounds();
|
||||
|
||||
// Queue drawing of all objects that are set to NORMAL.
|
||||
for (uint idx = 0; idx < _bgShapes.size(); ++idx) {
|
||||
Object &obj = _bgShapes[idx];
|
||||
|
||||
if (obj._type == ACTIVE_BG_SHAPE && (obj._misc == NORMAL_BEHIND || obj._misc == NORMAL_FORWARD)) {
|
||||
if (obj._scaleVal == SCALE_THRESHOLD)
|
||||
shapeList.push_back(ShapeEntry(&obj, obj._position.y + obj._imageFrame->_offset.y +
|
||||
obj._imageFrame->_height, ordering++));
|
||||
else
|
||||
shapeList.push_back(ShapeEntry(&obj, obj._position.y + obj._imageFrame->sDrawYOffset(obj._scaleVal) +
|
||||
obj._imageFrame->sDrawYSize(obj._scaleVal), ordering++));
|
||||
}
|
||||
}
|
||||
|
||||
// Queue drawing the animation if it is NORMAL and can fall in front of, or behind the people
|
||||
if (_activeCAnim.active() && (_activeCAnim._zPlacement == NORMAL_BEHIND || _activeCAnim._zPlacement == NORMAL_FORWARD)) {
|
||||
if (_activeCAnim._scaleVal == SCALE_THRESHOLD)
|
||||
shapeList.push_back(ShapeEntry(_activeCAnim._position.y + _activeCAnim._imageFrame._offset.y +
|
||||
_activeCAnim._imageFrame._height, ordering++));
|
||||
else
|
||||
shapeList.push_back(ShapeEntry(_activeCAnim._position.y + _activeCAnim._imageFrame.sDrawYOffset(_activeCAnim._scaleVal) +
|
||||
_activeCAnim._imageFrame.sDrawYSize(_activeCAnim._scaleVal), ordering++));
|
||||
}
|
||||
|
||||
// Queue all active characters for drawing
|
||||
for (int idx = 0; idx < MAX_CHARACTERS; ++idx) {
|
||||
if (people[idx]._type == CHARACTER && people[idx]._walkLoaded)
|
||||
shapeList.push_back(ShapeEntry(&people[idx], people[idx]._position.y / FIXED_INT_MULTIPLIER, ordering++));
|
||||
}
|
||||
|
||||
// Sort the list
|
||||
Common::sort(shapeList.begin(), shapeList.end(), sortImagesY);
|
||||
|
||||
// Draw the list of shapes in order
|
||||
for (ShapeList::iterator i = shapeList.begin(); i != shapeList.end(); ++i) {
|
||||
ShapeEntry &se = *i;
|
||||
|
||||
if (se._shape) {
|
||||
// it's a bg shape
|
||||
if (se._shape->_quickDraw && se._shape->_scaleVal == SCALE_THRESHOLD)
|
||||
screen._backBuffer1.SHblitFrom(*se._shape->_imageFrame, se._shape->_position);
|
||||
else
|
||||
screen._backBuffer1.SHtransBlitFrom(*se._shape->_imageFrame, se._shape->_position,
|
||||
se._shape->_flags & OBJ_FLIPPED, se._shape->_scaleVal);
|
||||
} else if (se._isAnimation) {
|
||||
// It's an active animation
|
||||
screen._backBuffer1.SHtransBlitFrom(_activeCAnim._imageFrame, _activeCAnim._position,
|
||||
(_activeCAnim._flags & 4) >> 1, _activeCAnim._scaleVal);
|
||||
} else {
|
||||
// Drawing person
|
||||
TattooPerson &p = *se._person;
|
||||
|
||||
p._tempX = p._position.x / FIXED_INT_MULTIPLIER;
|
||||
p._tempScaleVal = getScaleVal(p._position);
|
||||
Common::Point adjust = p._adjust;
|
||||
|
||||
if (p._tempScaleVal == SCALE_THRESHOLD) {
|
||||
p._tempX += adjust.x;
|
||||
screen._backBuffer1.SHtransBlitFrom(*p._imageFrame, Common::Point(p._tempX, p._position.y / FIXED_INT_MULTIPLIER
|
||||
- p.frameHeight() - adjust.y), p._walkSequences[p._sequenceNumber]._horizFlip, p._tempScaleVal);
|
||||
} else {
|
||||
if (adjust.x) {
|
||||
if (!p._tempScaleVal)
|
||||
++p._tempScaleVal;
|
||||
|
||||
if (p._tempScaleVal >= SCALE_THRESHOLD && adjust.x)
|
||||
--adjust.x;
|
||||
|
||||
adjust.x = adjust.x * SCALE_THRESHOLD / p._tempScaleVal;
|
||||
|
||||
if (p._tempScaleVal >= SCALE_THRESHOLD)
|
||||
++adjust.x;
|
||||
p._tempX += adjust.x;
|
||||
}
|
||||
|
||||
if (adjust.y) {
|
||||
if (!p._tempScaleVal)
|
||||
p._tempScaleVal++;
|
||||
|
||||
if (p._tempScaleVal >= SCALE_THRESHOLD && adjust.y)
|
||||
--adjust.y;
|
||||
|
||||
adjust.y = adjust.y * SCALE_THRESHOLD / p._tempScaleVal;
|
||||
|
||||
if (p._tempScaleVal >= SCALE_THRESHOLD)
|
||||
++adjust.y;
|
||||
}
|
||||
|
||||
screen._backBuffer1.SHtransBlitFrom(*p._imageFrame, Common::Point(p._tempX, p._position.y / FIXED_INT_MULTIPLIER
|
||||
- p._imageFrame->sDrawYSize(p._tempScaleVal) - adjust.y), p._walkSequences[p._sequenceNumber]._horizFlip, p._tempScaleVal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw all objects & canimations that are set to FORWARD.
|
||||
// Draw all static and active shapes that are FORWARD
|
||||
for (uint idx = 0; idx < _bgShapes.size(); ++idx) {
|
||||
Object &obj = _bgShapes[idx];
|
||||
|
||||
if (obj._type == ACTIVE_BG_SHAPE && obj._misc == FORWARD) {
|
||||
if (obj._quickDraw && obj._scaleVal == SCALE_THRESHOLD)
|
||||
screen._backBuffer1.SHblitFrom(*obj._imageFrame, obj._position);
|
||||
else
|
||||
screen._backBuffer1.SHtransBlitFrom(*obj._imageFrame, obj._position, obj._flags & OBJ_FLIPPED, obj._scaleVal);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw the canimation if it is set as FORWARD
|
||||
if (_activeCAnim.active() && _activeCAnim._zPlacement == FORWARD)
|
||||
screen._backBuffer1.SHtransBlitFrom(_activeCAnim._imageFrame, _activeCAnim._position, (_activeCAnim._flags & 4) >> 1, _activeCAnim._scaleVal);
|
||||
|
||||
// Draw all NO_SHAPE shapes which have their flag bits clear
|
||||
for (uint idx = 0; idx < _bgShapes.size(); ++idx) {
|
||||
Object &obj = _bgShapes[idx];
|
||||
if (obj._type == NO_SHAPE && (obj._flags & 1) == 0)
|
||||
screen._backBuffer1.fillRect(obj.getNoShapeBounds(), 15);
|
||||
}
|
||||
}
|
||||
|
||||
void TattooScene::paletteLoaded() {
|
||||
Screen &screen = *_vm->_screen;
|
||||
TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
|
||||
|
||||
ui.setupBGArea(screen._cMap);
|
||||
ui.initScrollVars();
|
||||
}
|
||||
|
||||
void TattooScene::checkBgShapes() {
|
||||
// Call the base scene method to handle bg shapes
|
||||
Scene::checkBgShapes();
|
||||
|
||||
// Check for any active playing animation
|
||||
if (_activeCAnim.active() && _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::freeScene() {
|
||||
TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
|
||||
Scene::freeScene();
|
||||
|
||||
// Delete any scene overlays that were used by the scene
|
||||
delete ui._mask;
|
||||
delete ui._mask1;
|
||||
ui._mask = ui._mask1 = nullptr;
|
||||
}
|
||||
|
||||
void TattooScene::doBgAnimCheckCursor() {
|
||||
Events &events = *_vm->_events;
|
||||
TattooUserInterface &ui = *(TattooUserInterface *)_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 && ui._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);
|
||||
} else {
|
||||
events.animateCursorIfNeeded();
|
||||
}
|
||||
}
|
||||
|
||||
void TattooScene::doBgAnim() {
|
||||
TattooEngine &vm = *(TattooEngine *)_vm;
|
||||
Events &events = *_vm->_events;
|
||||
Music &music = *_vm->_music;
|
||||
TattooPeople &people = *(TattooPeople *)_vm->_people;
|
||||
Screen &screen = *_vm->_screen;
|
||||
Talk &talk = *_vm->_talk;
|
||||
TattooUserInterface &ui = *((TattooUserInterface *)_vm->_ui);
|
||||
|
||||
doBgAnimCheckCursor();
|
||||
music.checkSongProgress();
|
||||
|
||||
talk._talkToAbort = false;
|
||||
|
||||
// Check the characters and sprites for updates
|
||||
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 one of the objects has signalled a call to a talk file, to go to another scene, exit immediately
|
||||
if (_goToScene != -1)
|
||||
return;
|
||||
|
||||
// Erase any affected background areas
|
||||
ui.doBgAnimEraseBackground();
|
||||
|
||||
doBgAnimUpdateBgObjectsAndAnim();
|
||||
|
||||
doBgAnimDrawSprites();
|
||||
|
||||
ui.drawInterface();
|
||||
|
||||
if (ui._creditsWidget.active())
|
||||
ui._creditsWidget.blitCredits();
|
||||
|
||||
if (screen._flushScreen) {
|
||||
screen.slamArea(screen._currentScroll.x, screen._currentScroll.y, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
|
||||
screen._flushScreen = false;
|
||||
}
|
||||
|
||||
screen._flushScreen = false;
|
||||
_doBgAnimDone = true;
|
||||
ui._drawMenu = false;
|
||||
|
||||
// Handle drawing tooltips
|
||||
if (ui._menuMode == STD_MODE || ui._menuMode == LAB_MODE)
|
||||
ui._tooltipWidget.draw();
|
||||
if (!ui._postRenderWidgets.empty()) {
|
||||
for (WidgetList::iterator i = ui._postRenderWidgets.begin(); i != ui._postRenderWidgets.end(); ++i)
|
||||
(*i)->draw();
|
||||
ui._postRenderWidgets.clear();
|
||||
}
|
||||
|
||||
if (!vm._fastMode)
|
||||
events.wait(3);
|
||||
|
||||
for (int idx = 1; idx < MAX_CHARACTERS; ++idx) {
|
||||
if (people[idx]._updateNPCPath)
|
||||
people[idx].updateNPC();
|
||||
}
|
||||
}
|
||||
|
||||
void TattooScene::doBgAnimUpdateBgObjectsAndAnim() {
|
||||
People &people = *_vm->_people;
|
||||
TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
|
||||
|
||||
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 (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();
|
||||
drawAllShapes();
|
||||
|
||||
ui.drawMaskArea(true);
|
||||
}
|
||||
|
||||
void TattooScene::updateBackground() {
|
||||
TattooPeople &people = *(TattooPeople *)_vm->_people;
|
||||
Screen &screen = *_vm->_screen;
|
||||
TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
|
||||
|
||||
Scene::updateBackground();
|
||||
|
||||
ui.drawMaskArea(false);
|
||||
|
||||
screen._flushScreen = true;
|
||||
|
||||
for (int idx = 0; idx < MAX_CHARACTERS; ++idx) {
|
||||
TattooPerson &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 == SCALE_THRESHOLD) {
|
||||
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 == SCALE_THRESHOLD)
|
||||
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 == SCALE_THRESHOLD)
|
||||
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;
|
||||
}
|
||||
|
||||
void TattooScene::doBgAnimDrawSprites() {
|
||||
TattooPeople &people = *(TattooPeople *)_vm->_people;
|
||||
Screen &screen = *_vm->_screen;
|
||||
|
||||
for (int idx = 0; idx < MAX_CHARACTERS; ++idx) {
|
||||
TattooPerson &person = people[idx];
|
||||
|
||||
if (person._type != INVALID) {
|
||||
if (_goToScene == -1 || _cAnim.size() == 0) {
|
||||
if (person._type == REMOVE) {
|
||||
screen.slamRect(person.getOldBounds());
|
||||
person._type = INVALID;
|
||||
} else {
|
||||
if (person._tempScaleVal == SCALE_THRESHOLD) {
|
||||
screen.flushImage(person._imageFrame, Common::Point(person._tempX, person._position.y / FIXED_INT_MULTIPLIER
|
||||
- person.frameHeight()), &person._oldPosition.x, &person._oldPosition.y, &person._oldSize.x, &person._oldSize.y);
|
||||
} else {
|
||||
int ts = person._imageFrame->sDrawYSize(person._tempScaleVal);
|
||||
int ty = person._position.y / FIXED_INT_MULTIPLIER - ts;
|
||||
screen.flushScaleImage(person._imageFrame, Common::Point(person._tempX, ty),
|
||||
&person._oldPosition.x, &person._oldPosition.y, &person._oldSize.x, &person._oldSize.y, person._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 == SCALE_THRESHOLD)
|
||||
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 == SCALE_THRESHOLD)
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_activeCAnim.active() || _activeCAnim._zPlacement == REMOVE) {
|
||||
if (_activeCAnim._zPlacement != REMOVE) {
|
||||
screen.flushImage(&_activeCAnim._imageFrame, _activeCAnim._position, _activeCAnim._oldBounds, _activeCAnim._scaleVal);
|
||||
} else {
|
||||
screen.slamRect(_activeCAnim._removeBounds);
|
||||
_activeCAnim._removeBounds = Common::Rect(0, 0, 0, 0);
|
||||
_activeCAnim._zPlacement = -1; // Reset _zPlacement so we don't REMOVE again
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int TattooScene::getScaleVal(const Point32 &pt) {
|
||||
bool found = false;
|
||||
int result = SCALE_THRESHOLD;
|
||||
Common::Point pos(pt.x / FIXED_INT_MULTIPLIER, pt.y / FIXED_INT_MULTIPLIER);
|
||||
|
||||
for (uint idx = 0; idx < _scaleZones.size() && !found; ++idx) {
|
||||
ScaleZone &sz = _scaleZones[idx];
|
||||
if (sz.contains(pos)) {
|
||||
int n = (sz._bottomNumber - sz._topNumber) * 100 / sz.height() * (pos.y - sz.top) / 100 + sz._topNumber;
|
||||
result = 25600L / n;
|
||||
// CHECKME: Shouldn't we set 'found' at this place?
|
||||
}
|
||||
}
|
||||
|
||||
// If it wasn't found, we may be off screen to the left or right, so find the scale zone
|
||||
// that would apply to the y val passed in disregarding the x
|
||||
if (!found) {
|
||||
for (uint idx = 0; idx < _scaleZones.size() && !found; ++idx) {
|
||||
ScaleZone &sz = _scaleZones[idx];
|
||||
if (pos.y >= sz.top && pos.y < sz.bottom) {
|
||||
int n = (sz._bottomNumber - sz._topNumber) * 100 / sz.height() * (pos.y - sz.top) / 100 + sz._topNumber;
|
||||
result = 25600L / n;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int TattooScene::startCAnim(int cAnimNum, int playRate) {
|
||||
TattooEngine &vm = *(TattooEngine *)_vm;
|
||||
Events &events = *_vm->_events;
|
||||
TattooPeople &people = *(TattooPeople *)_vm->_people;
|
||||
Resources &res = *_vm->_res;
|
||||
Talk &talk = *_vm->_talk;
|
||||
UserInterface &ui = *_vm->_ui;
|
||||
|
||||
// Exit immediately if the anim number is out of range, or the anim doesn't have a position specified
|
||||
if (cAnimNum < 0 || cAnimNum >= (int)_cAnim.size() || _cAnim[cAnimNum]._position.x == -1)
|
||||
// Return out of range error
|
||||
return -1;
|
||||
|
||||
// Get the co-ordinates that the Player & NPC #1 must walk to and end on
|
||||
CAnim &cAnim = _cAnim[cAnimNum];
|
||||
PositionFacing goto1 = cAnim._goto[0];
|
||||
PositionFacing goto2 = cAnim._goto[1];
|
||||
PositionFacing teleport1 = cAnim._teleport[0];
|
||||
PositionFacing teleport2 = cAnim._teleport[1];
|
||||
|
||||
// See if the Player must walk to a position before the animation starts
|
||||
SpriteType savedPlayerType = people[HOLMES]._type;
|
||||
if (goto1.x != -1 && people[HOLMES]._type == CHARACTER) {
|
||||
if (people[HOLMES]._position != goto1)
|
||||
people[HOLMES].walkToCoords(goto1, goto1._facing);
|
||||
}
|
||||
|
||||
if (talk._talkToAbort)
|
||||
return 1;
|
||||
|
||||
// See if NPC #1 must walk to a position before the animation starts
|
||||
SpriteType savedNPCType = people[WATSON]._type;
|
||||
if (goto2.x != -1 && people[WATSON]._type == CHARACTER) {
|
||||
if (people[WATSON]._position != goto2)
|
||||
people[WATSON].walkToCoords(goto2, goto2._facing);
|
||||
}
|
||||
|
||||
if (talk._talkToAbort)
|
||||
return 1;
|
||||
|
||||
// Turn the player (and NPC #1 if necessary) off before running the canimation
|
||||
if (teleport1.x != -1 && savedPlayerType == CHARACTER)
|
||||
people[HOLMES]._type = REMOVE;
|
||||
|
||||
if (teleport2.x != -1 && savedNPCType == CHARACTER)
|
||||
people[WATSON]._type = REMOVE;
|
||||
|
||||
if (ui._windowOpen)
|
||||
ui.banishWindow();
|
||||
|
||||
// Open up the room resource file and get the data for the animation
|
||||
Common::SeekableReadStream *stream = res.load(_roomFilename);
|
||||
stream->seek(44 + cAnimNum * 4);
|
||||
stream->seek(stream->readUint32LE());
|
||||
Common::SeekableReadStream *animStream = stream->readStream(cAnim._dataSize);
|
||||
delete stream;
|
||||
|
||||
// Set up the active animation
|
||||
_activeCAnim._position = cAnim._position;
|
||||
_activeCAnim._oldBounds = Common::Rect(0, 0, 0, 0);
|
||||
_activeCAnim._flags = cAnim._flags;
|
||||
_activeCAnim._scaleVal = cAnim._scaleVal;
|
||||
_activeCAnim._zPlacement = 0;
|
||||
|
||||
_activeCAnim.load(animStream, _compressed);
|
||||
|
||||
while (!_vm->shouldQuit()) {
|
||||
// Get the next frame
|
||||
if (!_activeCAnim.getNextFrame())
|
||||
break;
|
||||
|
||||
// Draw the frame
|
||||
doBgAnim();
|
||||
|
||||
// Check for skip prolog action being pressed to abort animation
|
||||
events.pollEvents();
|
||||
if (events.actionHit()) {
|
||||
Common::CustomEventType action = events.getAction();
|
||||
|
||||
if (action == kActionTattooSkipProlog && vm._runningProlog) {
|
||||
_vm->setFlags(-76);
|
||||
_vm->setFlags(396);
|
||||
_goToScene = STARTING_GAME_SCENE;
|
||||
talk._talkToAbort = true;
|
||||
_activeCAnim.close();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Turn the people back on
|
||||
people[HOLMES]._type = savedPlayerType;
|
||||
if (teleport2.x != -1)
|
||||
people[WATSON]._type = savedNPCType;
|
||||
|
||||
// Teleport the Player to the ending coordinates if necessary
|
||||
if (teleport1.x != -1 && savedPlayerType == CHARACTER) {
|
||||
people[HOLMES]._position = teleport1;
|
||||
people[HOLMES]._sequenceNumber = teleport1._facing;
|
||||
people[HOLMES].gotoStand();
|
||||
}
|
||||
|
||||
// Teleport Watson to the ending coordinates if necessary
|
||||
if (teleport2.x != -1 && savedNPCType == CHARACTER) {
|
||||
people[WATSON]._position = teleport2;
|
||||
people[WATSON]._sequenceNumber = teleport2._facing;
|
||||
people[WATSON].gotoStand();
|
||||
}
|
||||
|
||||
// Flag the Canimation to be cleared
|
||||
_activeCAnim._zPlacement = REMOVE;
|
||||
_activeCAnim._removeBounds = _activeCAnim._oldBounds;
|
||||
_vm->_ui->_bgFound = -1;
|
||||
|
||||
// Free up the animation
|
||||
_activeCAnim.close();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void TattooScene::setNPCPath(int npc) {
|
||||
TattooPeople &people = *(TattooPeople *)_vm->_people;
|
||||
SaveManager &saves = *_vm->_saves;
|
||||
Talk &talk = *_vm->_talk;
|
||||
|
||||
// Don't do initial scene setup if a savegame has just been loaded
|
||||
if (saves._justLoaded)
|
||||
return;
|
||||
|
||||
people[npc].clearNPC();
|
||||
people[npc]._npcName = 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 (int idx = 1; idx < MAX_CHARACTERS; ++idx)
|
||||
people[idx]._type = INVALID;
|
||||
|
||||
// Call the path script for the scene
|
||||
Common::String pathFile = Common::String::format("PATH%.2dA", _currentScene);
|
||||
talk.talkTo(pathFile);
|
||||
}
|
||||
|
||||
int TattooScene::findBgShape(const Common::Point &pt) {
|
||||
People &people = *_vm->_people;
|
||||
UserInterface &ui = *_vm->_ui;
|
||||
|
||||
if (!_doBgAnimDone)
|
||||
// New frame hasn't been drawn yet
|
||||
return -1;
|
||||
|
||||
int result = -1;
|
||||
for (int idx = (int)_bgShapes.size() - 1; idx >= 0 && result == -1; --idx) {
|
||||
Object &o = _bgShapes[idx];
|
||||
|
||||
if (o._type != INVALID && o._type != NO_SHAPE && o._type != HIDDEN &&
|
||||
(o._aType <= PERSON || (ui._menuMode == LAB_MODE && o._aType == SOLID))) {
|
||||
if (o.getNewBounds().contains(pt))
|
||||
result = idx;
|
||||
} else if (o._type == NO_SHAPE) {
|
||||
if (o.getNoShapeBounds().contains(pt))
|
||||
result = idx;
|
||||
}
|
||||
}
|
||||
|
||||
// Now check for the mouse being over an NPC. If so, it overrides any found bg object
|
||||
for (int idx = 1; idx < MAX_CHARACTERS; ++idx) {
|
||||
Person &person = people[idx];
|
||||
|
||||
if (person._type == CHARACTER) {
|
||||
int scaleVal = getScaleVal(person._position);
|
||||
Common::Rect charRect;
|
||||
|
||||
if (scaleVal == SCALE_THRESHOLD)
|
||||
charRect = Common::Rect(person.frameWidth(), person.frameHeight());
|
||||
else
|
||||
charRect = Common::Rect(person._imageFrame->sDrawXSize(scaleVal), person._imageFrame->sDrawYSize(scaleVal));
|
||||
charRect.moveTo(person._position.x / FIXED_INT_MULTIPLIER, person._position.y / FIXED_INT_MULTIPLIER
|
||||
- charRect.height());
|
||||
|
||||
if (charRect.contains(pt))
|
||||
result = 1000 + idx;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void TattooScene::synchronize(Serializer &s) {
|
||||
TattooEngine &vm = *(TattooEngine *)_vm;
|
||||
TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
|
||||
uint numSceneTripCounters = 0;
|
||||
Scene::synchronize(s);
|
||||
|
||||
// Since save version 5: sync _sceneTripCounters
|
||||
if (s.isSaving())
|
||||
numSceneTripCounters = _sceneTripCounters.size();
|
||||
s.syncAsUint32LE(numSceneTripCounters, 5);
|
||||
if (s.isLoading())
|
||||
_sceneTripCounters.resize(numSceneTripCounters);
|
||||
|
||||
for (auto &tripCounter : _sceneTripCounters) {
|
||||
s.syncAsSint32LE(tripCounter._flag, 5);
|
||||
s.syncAsSint32LE(tripCounter._sceneNumber, 5);
|
||||
s.syncAsSint32LE(tripCounter._numTimes, 5);
|
||||
}
|
||||
|
||||
if (s.isLoading()) {
|
||||
// In case we were showing the intro prologue or the ending credits, stop them
|
||||
vm._runningProlog = false;
|
||||
ui._creditsWidget.close();
|
||||
|
||||
Common::Keymapper *keymapper = g_system->getEventManager()->getKeymapper();
|
||||
keymapper->getKeymap("tattoo-prolog")->setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
int TattooScene::closestZone(const Common::Point &pt) {
|
||||
int zone = -1;
|
||||
int dist = 9999;
|
||||
|
||||
for (uint idx = 0; idx < _zones.size(); ++idx) {
|
||||
Common::Rect &r = _zones[idx];
|
||||
|
||||
// Check the distance from the point to the center of the zone
|
||||
int d = ABS(r.left + (r.width() / 2) - pt.x) + ABS(r.top + (r.height() / 2) - pt.y);
|
||||
if (d < dist) {
|
||||
dist = d;
|
||||
zone = idx;
|
||||
}
|
||||
|
||||
// Check the distance from the point to the upper left of the zone
|
||||
d = ABS((int)(r.left - pt.x)) + ABS((int)(r.top - pt.y));
|
||||
if (d < dist)
|
||||
{
|
||||
dist = d;
|
||||
zone = idx;
|
||||
}
|
||||
|
||||
// Check the distance from the point to the upper right of the zone
|
||||
d = ABS(r.left + r.width() - pt.x) + ABS(r.top - pt.y);
|
||||
if (d < dist) {
|
||||
dist = d;
|
||||
zone = idx;
|
||||
}
|
||||
|
||||
// Check the distance from the point to the lower left of the zone
|
||||
d = ABS(r.left - pt.x) + ABS(r.top + r.height() - pt.y);
|
||||
if (d < dist) {
|
||||
dist = d;
|
||||
zone = idx;
|
||||
}
|
||||
|
||||
// Check the distance from the point to the lower right of the zone
|
||||
d = ABS(r.left + r.width() - pt.x) + ABS(r.top + r.height() - pt.y);
|
||||
if (d < dist) {
|
||||
dist = d;
|
||||
zone = idx;
|
||||
}
|
||||
}
|
||||
|
||||
return zone;
|
||||
}
|
||||
|
||||
} // End of namespace Tattoo
|
||||
|
||||
} // End of namespace Sherlock
|
||||
156
engines/sherlock/tattoo/tattoo_scene.h
Normal file
156
engines/sherlock/tattoo/tattoo_scene.h
Normal file
@@ -0,0 +1,156 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SHERLOCK_TATTOO_SCENE_H
|
||||
#define SHERLOCK_TATTOO_SCENE_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "sherlock/scene.h"
|
||||
#include "sherlock/tattoo/widget_lab.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
namespace Tattoo {
|
||||
|
||||
extern const int FS_TRANS[8];
|
||||
|
||||
enum {
|
||||
STARTING_GAME_SCENE = 1, WEARY_PUNT = 52, TRAIN_RIDE = 69, STARTING_INTRO_SCENE = 91, OVERHEAD_MAP2 = 90, OVERHEAD_MAP = 100
|
||||
};
|
||||
|
||||
struct SceneTripEntry {
|
||||
int _flag;
|
||||
int _sceneNumber;
|
||||
int _numTimes;
|
||||
|
||||
SceneTripEntry() : _flag(0), _sceneNumber(0), _numTimes(0) {}
|
||||
SceneTripEntry(int flag, int sceneNumber, int numTimes) : _flag(flag),
|
||||
_sceneNumber(sceneNumber), _numTimes(numTimes) {}
|
||||
};
|
||||
|
||||
class TattooScene : public Scene {
|
||||
private:
|
||||
WidgetLab _labWidget;
|
||||
|
||||
void doBgAnimCheckCursor();
|
||||
|
||||
/**
|
||||
* Update the background objects and canimations as part of doBgAnim
|
||||
*/
|
||||
void doBgAnimUpdateBgObjectsAndAnim();
|
||||
|
||||
void doBgAnimDrawSprites();
|
||||
|
||||
/**
|
||||
* 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 setNPCPath(int npc);
|
||||
protected:
|
||||
/**
|
||||
* Loads the data associated for a given scene. The room resource file's format is:
|
||||
* BGHEADER: Holds an index for the rest of the file
|
||||
* STRUCTS: The objects for the scene
|
||||
* IMAGES: The graphic information for the structures
|
||||
*
|
||||
* The _misc field of the structures contains the number of the graphic image
|
||||
* that it should point to after loading; _misc is then set to 0.
|
||||
*/
|
||||
bool loadScene(const Common::Path &filename) override;
|
||||
|
||||
/**
|
||||
* Checks all the background shapes. If a background shape is animating,
|
||||
* it will flag it as needing to be drawn. If a non-animating shape is
|
||||
* colliding with another shape, it will also flag it as needing drawing
|
||||
*/
|
||||
void checkBgShapes() override;
|
||||
|
||||
/**
|
||||
* Draw all the shapes, people and NPCs in the correct order
|
||||
*/
|
||||
void drawAllShapes() override;
|
||||
|
||||
/**
|
||||
* Called by loadScene when the palette is loaded for Rose Tattoo
|
||||
*/
|
||||
void paletteLoaded() override;
|
||||
|
||||
/**
|
||||
* Synchronize the data for a savegame
|
||||
*/
|
||||
void synchronize(Serializer &s) override;
|
||||
|
||||
/**
|
||||
* Returns the index of the closest zone to a given point.
|
||||
*/
|
||||
int closestZone(const Common::Point &pt) override;
|
||||
public:
|
||||
StreamingImageFile _activeCAnim;
|
||||
Common::Array<SceneTripEntry> _sceneTripCounters;
|
||||
bool _labTableScene;
|
||||
public:
|
||||
TattooScene(SherlockEngine *vm);
|
||||
|
||||
/**
|
||||
* Returns the scale value for the passed co-ordinates. This is taken from the scene's
|
||||
* scale zones, interpolating inbetween the top and bottom values of the zones as needed
|
||||
*/
|
||||
int getScaleVal(const Point32 &pt);
|
||||
|
||||
/**
|
||||
* Fres all the graphics and other dynamically allocated data for the scene
|
||||
*/
|
||||
void freeScene() override;
|
||||
|
||||
/**
|
||||
* Draw all objects and characters.
|
||||
*/
|
||||
void doBgAnim() override;
|
||||
|
||||
/**
|
||||
* Update the screen back buffer with all of the scene objects which need
|
||||
* to be drawn
|
||||
*/
|
||||
void updateBackground() override;
|
||||
|
||||
/**
|
||||
* Attempt to start a canimation sequence. It will load the requisite graphics, and
|
||||
* then copy the canim object into the _canimShapes array to start the animation.
|
||||
*
|
||||
* @param cAnimNum The canim object within the current scene
|
||||
* @param playRate Play rate. 0 is invalid; 1=normal speed, 2=1/2 speed, etc.
|
||||
* A negative playRate can also be specified to play the animation in reverse
|
||||
*/
|
||||
int startCAnim(int cAnimNum, int playRate = 1) override;
|
||||
|
||||
/**
|
||||
* Attempts to find a background shape within the passed bounds. If found,
|
||||
* it will return the shape number, or -1 on failure.
|
||||
*/
|
||||
int findBgShape(const Common::Point &pt) override;
|
||||
};
|
||||
|
||||
} // End of namespace Tattoo
|
||||
|
||||
} // End of namespace Sherlock
|
||||
|
||||
#endif
|
||||
37
engines/sherlock/tattoo/tattoo_screen.cpp
Normal file
37
engines/sherlock/tattoo/tattoo_screen.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sherlock/tattoo/tattoo_screen.h"
|
||||
#include "sherlock/tattoo/tattoo.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
namespace Tattoo {
|
||||
|
||||
TattooScreen::TattooScreen(SherlockEngine *vm) : Screen(vm) {
|
||||
_backBuffer1.create(640, 480);
|
||||
_backBuffer2.create(640, 480);
|
||||
activateBackBuffer1();
|
||||
}
|
||||
|
||||
} // End of namespace Tattoo
|
||||
|
||||
} // End of namespace Sherlock
|
||||
43
engines/sherlock/tattoo/tattoo_screen.h
Normal file
43
engines/sherlock/tattoo/tattoo_screen.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SHERLOCK_TATTOO_SCREEN_H
|
||||
#define SHERLOCK_TATTOO_SCREEN_H
|
||||
|
||||
#include "sherlock/screen.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
class SherlockEngine;
|
||||
|
||||
namespace Tattoo {
|
||||
|
||||
class TattooScreen : public Screen {
|
||||
public:
|
||||
TattooScreen(SherlockEngine *vm);
|
||||
~TattooScreen() override {}
|
||||
};
|
||||
|
||||
} // End of namespace Tattoo
|
||||
|
||||
} // End of namespace Sherlock
|
||||
|
||||
#endif
|
||||
1021
engines/sherlock/tattoo/tattoo_talk.cpp
Normal file
1021
engines/sherlock/tattoo/tattoo_talk.cpp
Normal file
File diff suppressed because it is too large
Load Diff
142
engines/sherlock/tattoo/tattoo_talk.h
Normal file
142
engines/sherlock/tattoo/tattoo_talk.h
Normal file
@@ -0,0 +1,142 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SHERLOCK_TATTOO_TALK_H
|
||||
#define SHERLOCK_TATTOO_TALK_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/array.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/serializer.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/stack.h"
|
||||
#include "sherlock/talk.h"
|
||||
#include "sherlock/tattoo/widget_password.h"
|
||||
#include "sherlock/tattoo/widget_talk.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
namespace Tattoo {
|
||||
|
||||
#define TALK_SEQUENCE_STACK_SIZE 20
|
||||
|
||||
class WidgetTalk;
|
||||
|
||||
class TattooTalk : public Talk {
|
||||
friend class WidgetTalk;
|
||||
private:
|
||||
WidgetTalk _talkWidget;
|
||||
WidgetPassword _passwordWidget;
|
||||
SequenceEntry _sequenceStack[TALK_SEQUENCE_STACK_SIZE];
|
||||
|
||||
OpcodeReturn cmdCallTalkFile(const byte *&str);
|
||||
OpcodeReturn cmdSwitchSpeaker(const byte *&str);
|
||||
OpcodeReturn cmdMouseOnOff(const byte *&str);
|
||||
OpcodeReturn cmdGotoScene(const byte *&str);
|
||||
OpcodeReturn cmdWalkHolmesToCoords(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);
|
||||
protected:
|
||||
/**
|
||||
* Display the talk interface window
|
||||
*/
|
||||
void talkInterface(const byte *&str) override;
|
||||
|
||||
/**
|
||||
* Called when a character being spoken to has no talk options to display
|
||||
*/
|
||||
void nothingToSay() override;
|
||||
|
||||
/**
|
||||
* Show the talk display
|
||||
*/
|
||||
void showTalk() override;
|
||||
public:
|
||||
TattooTalk(SherlockEngine *vm);
|
||||
~TattooTalk() override {}
|
||||
|
||||
/**
|
||||
* Called whenever a conversation or item script needs to be run. For standard conversations,
|
||||
* it opens up a description window similar to how 'talk' does, but shows a 'reply' directly
|
||||
* instead of waiting for a statement option.
|
||||
* @remarks It seems that at some point, all item scripts were set up to use this as well.
|
||||
* In their case, the conversation display is simply suppressed, and control is passed on to
|
||||
* doScript to implement whatever action is required.
|
||||
*/
|
||||
void talkTo(const Common::String &filename) override;
|
||||
|
||||
/**
|
||||
* Push the details of a passed object onto the saved sequences stack
|
||||
*/
|
||||
void pushSequenceEntry(Object *obj) override;
|
||||
|
||||
/**
|
||||
* Pulls a background object sequence from the sequence stack and restore's the
|
||||
* object's sequence
|
||||
*/
|
||||
void pullSequence(int slot = -1) override;
|
||||
|
||||
/**
|
||||
* Returns true if the script stack is empty
|
||||
*/
|
||||
bool isSequencesEmpty() const override;
|
||||
|
||||
/**
|
||||
* Clears the stack of pending object sequences associated with speakers in the scene
|
||||
*/
|
||||
void clearSequences() override;
|
||||
};
|
||||
|
||||
} // End of namespace Tattoo
|
||||
|
||||
} // End of namespace Sherlock
|
||||
|
||||
#endif
|
||||
984
engines/sherlock/tattoo/tattoo_user_interface.cpp
Normal file
984
engines/sherlock/tattoo/tattoo_user_interface.cpp
Normal file
@@ -0,0 +1,984 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sherlock/tattoo/tattoo_user_interface.h"
|
||||
#include "sherlock/tattoo/tattoo_fixed_text.h"
|
||||
#include "sherlock/tattoo/tattoo_journal.h"
|
||||
#include "sherlock/tattoo/tattoo_scene.h"
|
||||
#include "sherlock/tattoo/tattoo.h"
|
||||
|
||||
#include "backends/keymapper/keymapper.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
namespace Tattoo {
|
||||
|
||||
bool WidgetList::contains(const WidgetBase *item) const {
|
||||
for (const_iterator i = begin(); i != end(); ++i) {
|
||||
if ((*i) == item)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
TattooUserInterface::TattooUserInterface(SherlockEngine *vm): UserInterface(vm),
|
||||
_inventoryWidget(vm), _messageWidget(vm), _textWidget(vm), _tooltipWidget(vm),
|
||||
_verbsWidget(vm), _creditsWidget(vm), _optionsWidget(vm), _quitWidget(vm) {
|
||||
Common::fill(&_lookupTable[0], &_lookupTable[Graphics::PALETTE_COUNT], 0);
|
||||
Common::fill(&_lookupTable1[0], &_lookupTable1[Graphics::PALETTE_COUNT], 0);
|
||||
_scrollSize = 0;
|
||||
_scrollSpeed = 16;
|
||||
_drawMenu = false;
|
||||
_bgShape = nullptr;
|
||||
_personFound = false;
|
||||
_lockoutTimer = 0;
|
||||
_exitZone = -1;
|
||||
_scriptZone = -1;
|
||||
_arrowZone = _oldArrowZone = -1;
|
||||
_activeObj = -1;
|
||||
_cAnimFramePause = 0;
|
||||
_scrollHighlight = SH_NONE;
|
||||
_mask = _mask1 = nullptr;
|
||||
_maskCounter = 0;
|
||||
|
||||
_interfaceImages = new ImageFile("intrface.vgs");
|
||||
}
|
||||
|
||||
TattooUserInterface::~TattooUserInterface() {
|
||||
delete _interfaceImages;
|
||||
delete _mask;
|
||||
delete _mask1;
|
||||
}
|
||||
|
||||
void TattooUserInterface::initScrollVars() {
|
||||
Screen &screen = *_vm->_screen;
|
||||
_scrollSize = screen._backBuffer1.width() - SHERLOCK_SCREEN_WIDTH;
|
||||
_targetScroll = Common::Point(0, 0);
|
||||
screen._currentScroll = Common::Point(0, 0);
|
||||
}
|
||||
|
||||
void TattooUserInterface::lookAtObject() {
|
||||
Events &events = *_vm->_events;
|
||||
People &people = *_vm->_people;
|
||||
Scene &scene = *_vm->_scene;
|
||||
Sound &sound = *_vm->_sound;
|
||||
Talk &talk = *_vm->_talk;
|
||||
Common::Point mousePos = events.mousePos();
|
||||
Common::String desc;
|
||||
|
||||
_lookPos = mousePos;
|
||||
_menuMode = LOOK_MODE;
|
||||
|
||||
if (_personFound) {
|
||||
desc = people[_bgFound - 1000]._examine;
|
||||
} else {
|
||||
// Check if there is a Look animation
|
||||
if (_bgShape->_lookcAnim != 0) {
|
||||
//int cAnimSpeed = _bgShape->_lookcAnim & 0xe0;
|
||||
//cAnimSpeed >>= 5;
|
||||
//++cAnimSpeed;
|
||||
|
||||
_cAnimFramePause = _bgShape->_lookFrames;
|
||||
desc = _bgShape->_examine;
|
||||
|
||||
int cNum = (_bgShape->_lookcAnim & 0x1f) - 1;
|
||||
scene.startCAnim(cNum);
|
||||
} else if (_bgShape->_lookPosition.y != 0) {
|
||||
// Need to walk to object before looking at it
|
||||
people[HOLMES].walkToCoords(_bgShape->_lookPosition, _bgShape->_lookPosition._facing);
|
||||
}
|
||||
|
||||
if (!talk._talkToAbort) {
|
||||
desc = _bgShape->_examine;
|
||||
|
||||
if (_bgShape->_lookFlag)
|
||||
_vm->setFlags(_bgShape->_lookFlag);
|
||||
|
||||
// Find the Sound File to Play if there is one
|
||||
if (!desc.hasPrefix("_")) {
|
||||
for (uint idx = 0; idx < scene._objSoundList.size(); ++idx) {
|
||||
// Get the object name up to the equals
|
||||
const char *p = strchr(scene._objSoundList[idx].c_str(), '=');
|
||||
|
||||
// Form the name and remove any trailing spaces
|
||||
Common::String name(scene._objSoundList[idx].c_str(), p);
|
||||
while (name.hasSuffix(" "))
|
||||
name.deleteLastChar();
|
||||
|
||||
// See if this Object Sound List entry matches the object's name
|
||||
if (!_bgShape->_name.compareToIgnoreCase(name)) {
|
||||
// Move forward to get the sound filename
|
||||
while ((*p == ' ') || (*p == '='))
|
||||
++p;
|
||||
|
||||
// If it's not "NONE", play the speech File
|
||||
Common::String soundName(p);
|
||||
if (soundName.compareToIgnoreCase("NONE")) {
|
||||
soundName.toLowercase();
|
||||
if (!soundName.contains('.'))
|
||||
soundName += ".wav";
|
||||
|
||||
sound.playSound(Common::Path(soundName), WAIT_RETURN_IMMEDIATELY);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Only show the desciption if the object has one, and if no talk file interrupted while walking to it
|
||||
if (!talk._talkToAbort && !desc.empty()) {
|
||||
if (_cAnimFramePause == 0)
|
||||
printObjectDesc(desc, true);
|
||||
else
|
||||
// The description was already printed by an animation
|
||||
_cAnimFramePause = 0;
|
||||
} else if (desc.empty()) {
|
||||
// There was no description to display, so reset back to STD_MODE
|
||||
_menuMode = STD_MODE;
|
||||
}
|
||||
}
|
||||
|
||||
void TattooUserInterface::printObjectDesc(const Common::String &str, bool firstTime) {
|
||||
Events &events = *_vm->_events;
|
||||
TattooScene &scene = *(TattooScene *)_vm->_scene;
|
||||
Talk &talk = *_vm->_talk;
|
||||
|
||||
if (str.hasPrefix("_")) {
|
||||
// The passed string specifies a talk file
|
||||
_lookScriptFlag = true;
|
||||
events.setCursor(MAGNIFY);
|
||||
int savedSelector = _selector;
|
||||
|
||||
if (!_invLookFlag)
|
||||
_windowOpen = false;
|
||||
|
||||
talk.talkTo(str.c_str() + 1);
|
||||
_lookScriptFlag = false;
|
||||
|
||||
if (talk._talkToAbort) {
|
||||
events.setCursor(ARROW);
|
||||
return;
|
||||
}
|
||||
|
||||
// See if we're looking at an inventory item
|
||||
if (_invLookFlag) {
|
||||
_selector = _oldSelector = savedSelector;
|
||||
doInventory(0);
|
||||
_invLookFlag = false;
|
||||
|
||||
} else {
|
||||
// Nope
|
||||
events.setCursor(ARROW);
|
||||
_action = kActionNone;
|
||||
_keyState.keycode = Common::KEYCODE_INVALID;
|
||||
_menuMode = scene._labTableScene ? LAB_MODE : STD_MODE;
|
||||
events._pressed = events._released = events._rightReleased = false;
|
||||
events._oldButtons = 0;
|
||||
}
|
||||
} else {
|
||||
events._pressed = events._released = events._rightReleased = false;
|
||||
|
||||
// Show text dialog
|
||||
_textWidget.load(str);
|
||||
_textWidget.summonWindow();
|
||||
|
||||
if (firstTime)
|
||||
_selector = _oldSelector = -1;
|
||||
|
||||
_drawMenu = _windowOpen = true;
|
||||
}
|
||||
}
|
||||
|
||||
void TattooUserInterface::doJournal() {
|
||||
TattooJournal &journal = *(TattooJournal *)_vm->_journal;
|
||||
TattooScene &scene = *(TattooScene *)_vm->_scene;
|
||||
Screen &screen = *_vm->_screen;
|
||||
byte lookupTable[Graphics::PALETTE_COUNT], lookupTable1[Graphics::PALETTE_COUNT];
|
||||
|
||||
Common::copy(&_lookupTable[0], &_lookupTable[Graphics::PALETTE_COUNT], &lookupTable[0]);
|
||||
Common::copy(&_lookupTable1[0], &_lookupTable1[Graphics::PALETTE_COUNT], &lookupTable1[0]);
|
||||
_menuMode = JOURNAL_MODE;
|
||||
journal.show();
|
||||
|
||||
_menuMode = STD_MODE;
|
||||
_windowOpen = false;
|
||||
_action = kActionNone;
|
||||
_keyState.keycode = Common::KEYCODE_INVALID;
|
||||
|
||||
// Restore the old screen palette and greyscale lookup table
|
||||
screen.clear();
|
||||
screen.setPalette(screen._cMap);
|
||||
Common::copy(&lookupTable[0], &lookupTable[Graphics::PALETTE_COUNT], &_lookupTable[0]);
|
||||
Common::copy(&lookupTable1[0], &lookupTable1[Graphics::PALETTE_COUNT], &_lookupTable1[0]);
|
||||
|
||||
// Restore the scene
|
||||
screen._backBuffer1.SHblitFrom(screen._backBuffer2);
|
||||
scene.updateBackground();
|
||||
screen.slamArea(screen._currentScroll.x, screen._currentScroll.y, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
|
||||
}
|
||||
|
||||
void TattooUserInterface::reset() {
|
||||
UserInterface::reset();
|
||||
_lookPos = Common::Point(SHERLOCK_SCREEN_WIDTH / 2, SHERLOCK_SCREEN_HEIGHT / 2);
|
||||
_tooltipWidget.setText("");
|
||||
_widgets.clear();
|
||||
_fixedWidgets.clear();
|
||||
}
|
||||
|
||||
void TattooUserInterface::handleInput() {
|
||||
TattooEngine &vm = *(TattooEngine *)_vm;
|
||||
Events &events = *_vm->_events;
|
||||
TattooScene &scene = *(TattooScene *)_vm->_scene;
|
||||
Common::Point mousePos = events.mousePos();
|
||||
|
||||
_keyState.keycode = Common::KEYCODE_INVALID;
|
||||
_action = kActionNone;
|
||||
|
||||
// Check for credits starting
|
||||
if (_vm->readFlags(3000) && !_creditsWidget.active())
|
||||
_creditsWidget.initCredits();
|
||||
|
||||
// Check the mouse positioning
|
||||
if (events.isCursorVisible())
|
||||
_bgFound = scene.findBgShape(mousePos);
|
||||
_personFound = _bgFound >= 1000;
|
||||
_bgShape = (_bgFound != -1 && _bgFound < 1000) ? &scene._bgShapes[_bgFound] : nullptr;
|
||||
|
||||
if (_lockoutTimer)
|
||||
--_lockoutTimer;
|
||||
|
||||
// Action handling
|
||||
if (events.actionHit()) {
|
||||
_action = events.getAction();
|
||||
|
||||
if (_action == kActionTattooSkipProlog && vm._runningProlog && !_lockoutTimer) {
|
||||
vm.setFlags(-76);
|
||||
vm.setFlags(396);
|
||||
scene._goToScene = STARTING_GAME_SCENE;
|
||||
} else if (_menuMode == STD_MODE) {
|
||||
if (_action == kActionTattooChangeSpeed && vm._allowFastMode) {
|
||||
events.toggleSpeed();
|
||||
|
||||
} else if (_action == kActionTattooLook && _bgFound != -1) {
|
||||
// Beging used for testing that Look dialogs work
|
||||
lookAtObject();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (events.kbHit()) {
|
||||
_keyState = events.getKey();
|
||||
}
|
||||
|
||||
if (!events.isCursorVisible()) {
|
||||
_keyState.keycode = Common::KEYCODE_INVALID;
|
||||
_action = kActionNone;
|
||||
}
|
||||
|
||||
// If there's any active widgets/windows, let the most recently open one do event processing
|
||||
if (!_widgets.empty())
|
||||
_widgets.back()->handleEvents();
|
||||
else if (!_fixedWidgets.empty())
|
||||
_fixedWidgets.back()->handleEvents();
|
||||
|
||||
// Handle input depending on what mode we're in
|
||||
switch (_menuMode) {
|
||||
case STD_MODE:
|
||||
doStandardControl();
|
||||
break;
|
||||
case LOOK_MODE:
|
||||
doLookControl();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void TattooUserInterface::drawInterface(int bufferNum) {
|
||||
Screen &screen = *_vm->_screen;
|
||||
|
||||
// Draw any active on-screen widgets
|
||||
for (Common::List<WidgetBase *>::iterator i = _fixedWidgets.begin(); i != _fixedWidgets.end(); ++i)
|
||||
(*i)->draw();
|
||||
for (Common::List<WidgetBase *>::iterator i = _widgets.begin(); i != _widgets.end(); ++i)
|
||||
(*i)->draw();
|
||||
|
||||
// Handle drawing credits
|
||||
// TODO: See if credits are only shown on a single screen. If so, _fixedWidgets could be used
|
||||
if (_creditsWidget.active())
|
||||
_creditsWidget.drawCredits();
|
||||
|
||||
// Bring the widgets to the screen
|
||||
if (_mask != nullptr)
|
||||
screen._flushScreen = true;
|
||||
}
|
||||
|
||||
void TattooUserInterface::doBgAnimRestoreUI() {
|
||||
TattooScene &scene = *((TattooScene *)_vm->_scene);
|
||||
Screen &screen = *_vm->_screen;
|
||||
|
||||
// If there are any on-screen widgets, then erase them
|
||||
for (Common::List<WidgetBase *>::iterator i = _widgets.begin(); i != _widgets.end(); ++i)
|
||||
(*i)->erase();
|
||||
for (Common::List<WidgetBase *>::iterator i = _fixedWidgets.begin(); i != _fixedWidgets.end(); ++i)
|
||||
(*i)->erase();
|
||||
|
||||
// If there is a Text Tag being display, restore the area underneath it
|
||||
_tooltipWidget.erase();
|
||||
|
||||
// If a canimation is active, restore the graphics underneath it
|
||||
if (scene._activeCAnim.active())
|
||||
screen.restoreBackground(scene._activeCAnim._oldBounds);
|
||||
|
||||
// If a canimation just ended, remove its graphics from the backbuffer
|
||||
if (scene._activeCAnim._removeBounds.width() > 0)
|
||||
screen.restoreBackground(scene._activeCAnim._removeBounds);
|
||||
}
|
||||
|
||||
void TattooUserInterface::doScroll() {
|
||||
Screen &screen = *_vm->_screen;
|
||||
|
||||
// If we're already at the target scroll position, nothing needs to be done
|
||||
if (_targetScroll.x == screen._currentScroll.x)
|
||||
return;
|
||||
|
||||
screen._flushScreen = true;
|
||||
if (_targetScroll.x > screen._currentScroll.x) {
|
||||
screen._currentScroll.x += _scrollSpeed;
|
||||
if (screen._currentScroll.x > _targetScroll.x)
|
||||
screen._currentScroll.x = _targetScroll.x;
|
||||
} else if (_targetScroll.x < screen._currentScroll.x) {
|
||||
screen._currentScroll.x -= _scrollSpeed;
|
||||
if (screen._currentScroll.x < _targetScroll.x)
|
||||
screen._currentScroll.x = _targetScroll.x;
|
||||
}
|
||||
|
||||
// Reset the default look position to the center of the new screen area
|
||||
_lookPos = screen._currentScroll + Common::Point(SHERLOCK_SCREEN_WIDTH / 2, SHERLOCK_SCREEN_HEIGHT / 2);
|
||||
}
|
||||
|
||||
void TattooUserInterface::doStandardControl() {
|
||||
TattooEngine &vm = *(TattooEngine *)_vm;
|
||||
Events &events = *_vm->_events;
|
||||
People &people = *_vm->_people;
|
||||
SaveManager &saves = *_vm->_saves;
|
||||
TattooScene &scene = *(TattooScene *)_vm->_scene;
|
||||
Talk &talk = *_vm->_talk;
|
||||
Common::Point mousePos = events.mousePos();
|
||||
|
||||
// Don't do any input processing whilst the prolog is running
|
||||
// or the cursor is hidden (e.g. by a call to cmdMouseOnOff())
|
||||
if (vm._runningProlog || !events.isCursorVisible())
|
||||
return;
|
||||
|
||||
// When the end credits are active, any press will open the ScummVM global main menu
|
||||
if (_creditsWidget.active()) {
|
||||
if (_keyState.keycode || _action || events._released || events._rightReleased) {
|
||||
vm._canLoadSave = true;
|
||||
vm.openMainMenuDialog();
|
||||
vm._canLoadSave = false;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Display the names of any Objects the cursor is pointing at
|
||||
displayObjectNames();
|
||||
|
||||
switch (_action) {
|
||||
case kActionTattooSave:
|
||||
// Save game
|
||||
events.warpMouse();
|
||||
saveGame();
|
||||
return;
|
||||
|
||||
case kActionTattooLoad:
|
||||
// Load game
|
||||
events.warpMouse();
|
||||
loadGame();
|
||||
return;
|
||||
|
||||
case kActionTattooJournal:
|
||||
// Display journal
|
||||
if (vm.readFlags(FLAG_PLAYER_IS_HOLMES)) {
|
||||
freeMenu();
|
||||
doJournal();
|
||||
|
||||
// See if we're in a Lab Table Room
|
||||
_menuMode = (scene._labTableScene) ? LAB_MODE : STD_MODE;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case kActionTattooInv:
|
||||
// Display inventory
|
||||
freeMenu();
|
||||
doInventory(3);
|
||||
return;
|
||||
|
||||
case kActionTattooOptions:
|
||||
// Display options
|
||||
events.warpMouse();
|
||||
_optionsWidget.load();
|
||||
return;
|
||||
|
||||
case kActionTattooQuit:
|
||||
// Quit menu
|
||||
freeMenu();
|
||||
events.warpMouse();
|
||||
doQuitMenu();
|
||||
return;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// See if a mouse button was released
|
||||
if (events._released || events._rightReleased) {
|
||||
// See if the mouse was released in an exit (Arrow) zone. Unless it's also pointing at an object
|
||||
// within the zone, in which case the object gets precedence
|
||||
_exitZone = -1;
|
||||
if (_arrowZone != -1 && events._released)
|
||||
_exitZone = _arrowZone;
|
||||
|
||||
// Turn any Text display off
|
||||
if (_arrowZone == -1 || events._rightReleased)
|
||||
freeMenu();
|
||||
|
||||
bool noDesc = false;
|
||||
if (_personFound) {
|
||||
if (people[_bgFound - 1000]._description.empty() || people[_bgFound - 1000]._description.hasPrefix(" "))
|
||||
noDesc = true;
|
||||
} else if (_bgFound != -1) {
|
||||
if (_bgShape->_description.empty() || _bgShape->_description.hasPrefix(" "))
|
||||
noDesc = true;
|
||||
} else {
|
||||
noDesc = true;
|
||||
}
|
||||
|
||||
if (events._rightReleased) {
|
||||
// Show the verbs menu for the highlighted object
|
||||
_tooltipWidget.banishWindow();
|
||||
saves.createThumbnail();
|
||||
_verbsWidget.load(!noDesc);
|
||||
_verbsWidget.summonWindow();
|
||||
|
||||
_selector = _oldSelector = -1;
|
||||
_activeObj = _bgFound;
|
||||
_menuMode = VERB_MODE;
|
||||
} else if (_personFound || (_bgFound != -1 && _bgFound < 1000 && _bgShape->_aType == PERSON)) {
|
||||
// The object found is a person (the default for people is TALK)
|
||||
talk.initTalk(_bgFound);
|
||||
_activeObj = -1;
|
||||
} else if (!noDesc) {
|
||||
// Either call the code to Look at its Examine Field or call the Exit animation
|
||||
// if the object is an exit, specified by the first four characters of the name being "EXIT"
|
||||
Common::String name = _personFound ? people[_bgFound - 1000]._name : _bgShape->_name;
|
||||
if (!name.hasPrefix("EXIT")) {
|
||||
lookAtObject();
|
||||
} else {
|
||||
// Run the Exit animation and set which scene to go to next
|
||||
for (int idx = 0; idx < 6; ++idx) {
|
||||
if (!_bgShape->_use[idx]._verb.compareToIgnoreCase("Open")) {
|
||||
checkAction(_bgShape->_use[idx], _bgFound);
|
||||
_activeObj = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// See if there are any Script Zones where they clicked
|
||||
if (scene.checkForZones(mousePos, _scriptZone) != 0) {
|
||||
// Mouse click in a script zone
|
||||
events._pressed = events._released = false;
|
||||
} else if (scene.checkForZones(mousePos, NOWALK_ZONE) != 0) {
|
||||
events._pressed = events._released = false;
|
||||
} else {
|
||||
// Walk to where the mouse was clicked
|
||||
people[HOLMES]._walkDest = mousePos;
|
||||
people[HOLMES].goAllTheWay();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TattooUserInterface::doLookControl() {
|
||||
Events &events = *_vm->_events;
|
||||
TattooScene &scene = *(TattooScene *)_vm->_scene;
|
||||
|
||||
// See if a mouse button was released, a key pressed or a action pressed to close the active look dialog
|
||||
if (events._released || events._rightReleased || _keyState.keycode || _action) {
|
||||
// See if we were looking at an inventory object
|
||||
if (!_invLookFlag) {
|
||||
// See if there is any more text to display
|
||||
if (!_textWidget._remainingText.empty()) {
|
||||
printObjectDesc(_textWidget._remainingText, false);
|
||||
} else {
|
||||
// Otherwise restore the background and go back into STD_MODE
|
||||
freeMenu();
|
||||
_action = kActionNone;
|
||||
_keyState.keycode = Common::KEYCODE_INVALID;
|
||||
_menuMode = scene._labTableScene ? LAB_MODE : STD_MODE;
|
||||
|
||||
events.setCursor(ARROW);
|
||||
events._pressed = events._released = events._rightReleased = false;
|
||||
events._oldButtons = 0;
|
||||
}
|
||||
} else {
|
||||
// We were looking at a Inventory object
|
||||
// Erase the text window, and then redraw the inventory window
|
||||
_textWidget.banishWindow();
|
||||
doInventory(0);
|
||||
|
||||
_invLookFlag = false;
|
||||
_action = kActionNone;
|
||||
_keyState.keycode = Common::KEYCODE_INVALID;
|
||||
|
||||
events.setCursor(ARROW);
|
||||
events._pressed = events._released = events._rightReleased = false;
|
||||
events._oldButtons = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TattooUserInterface::displayObjectNames() {
|
||||
Events &events = *_vm->_events;
|
||||
Scene &scene = *_vm->_scene;
|
||||
Common::Point mousePos = events.mousePos();
|
||||
_arrowZone = -1;
|
||||
|
||||
if (_bgFound == -1 || scene._currentScene == OVERHEAD_MAP2) {
|
||||
for (uint idx = 0; idx < scene._exits.size() && _arrowZone == -1; ++idx) {
|
||||
Exit &exit = scene._exits[idx];
|
||||
if (exit.contains(mousePos))
|
||||
_arrowZone = idx;
|
||||
}
|
||||
}
|
||||
|
||||
_tooltipWidget.handleEvents();
|
||||
_oldArrowZone = _arrowZone;
|
||||
}
|
||||
|
||||
void TattooUserInterface::doInventory(int mode) {
|
||||
People &people = *_vm->_people;
|
||||
people[HOLMES].gotoStand();
|
||||
|
||||
_inventoryWidget.load(mode);
|
||||
_inventoryWidget.summonWindow();
|
||||
|
||||
_menuMode = INV_MODE;
|
||||
}
|
||||
|
||||
void TattooUserInterface::doControls() {
|
||||
_optionsWidget.load();
|
||||
}
|
||||
|
||||
void TattooUserInterface::pickUpObject(int objNum) {
|
||||
Inventory &inv = *_vm->_inventory;
|
||||
Scene &scene = *_vm->_scene;
|
||||
Talk &talk = *_vm->_talk;
|
||||
Object &obj = scene._bgShapes[objNum];
|
||||
bool printed = false;
|
||||
int verbField = -1;
|
||||
|
||||
// Find which Verb field to use for pick up data
|
||||
for (int idx = 0; idx < 6; ++idx) {
|
||||
if (!scumm_stricmp(obj._use[idx]._target.c_str(), "*PICKUP"))
|
||||
verbField = idx;
|
||||
}
|
||||
|
||||
if (verbField != -1) {
|
||||
if (obj._use[verbField]._cAnimNum)
|
||||
scene.startCAnim(obj._use[verbField]._cAnimNum - 1);
|
||||
}
|
||||
|
||||
if (!talk._talkToAbort) {
|
||||
if (obj._type == NO_SHAPE)
|
||||
obj._type = INVALID;
|
||||
else
|
||||
// Erase shape
|
||||
obj._type = REMOVE;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if (verbField != -1) {
|
||||
for (int idx = 0; idx < 4 && !talk._talkToAbort; ++idx) {
|
||||
if (obj.checkNameForCodes(obj._use[verbField]._names[idx])) {
|
||||
if (!talk._talkToAbort)
|
||||
printed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (talk._talkToAbort)
|
||||
return;
|
||||
|
||||
// Add the item to the player's inventory
|
||||
inv.putItemInInventory(obj);
|
||||
|
||||
if (!printed) {
|
||||
Common::String desc = obj._description;
|
||||
desc.setChar(tolower(desc[0]), 0);
|
||||
|
||||
putMessage("%s %s", FIXED(PickedUp), desc.c_str());
|
||||
}
|
||||
|
||||
if (_menuMode != TALK_MODE && _menuMode != MESSAGE_MODE) {
|
||||
_menuMode = STD_MODE;
|
||||
_keyState.keycode = Common::KEYCODE_INVALID;
|
||||
_action = kActionNone;
|
||||
}
|
||||
}
|
||||
|
||||
void TattooUserInterface::doQuitMenu() {
|
||||
_quitWidget.show();
|
||||
}
|
||||
|
||||
void TattooUserInterface::putMessage(const char *formatStr, ...) {
|
||||
// Create the string to display
|
||||
va_list args;
|
||||
va_start(args, formatStr);
|
||||
Common::String str = Common::String::vformat(formatStr, args);
|
||||
va_end(args);
|
||||
|
||||
// Open the message widget
|
||||
_menuMode = MESSAGE_MODE;
|
||||
_messageWidget.load(str, 25);
|
||||
_messageWidget.summonWindow();
|
||||
}
|
||||
|
||||
void TattooUserInterface::setupBGArea(const byte cMap[Graphics::PALETTE_SIZE]) {
|
||||
Scene &scene = *_vm->_scene;
|
||||
|
||||
// This requires that there is a 16 grayscale palette sequence in the palette that goes from lighter
|
||||
// to darker as the palette numbers go up. The last palette entry in that run is specified by _bgColor
|
||||
byte *p = &_lookupTable[0];
|
||||
for (int idx = 0; idx < Graphics::PALETTE_COUNT; ++idx)
|
||||
*p++ = BG_GREYSCALE_RANGE_END - (cMap[idx * 3] * 30 + cMap[idx * 3 + 1] * 59 + cMap[idx * 3 + 2] * 11) / 480;
|
||||
|
||||
// If we're going to a scene with a haze special effect, initialize the translate table to lighten the colors
|
||||
if (_mask != nullptr) {
|
||||
p = &_lookupTable1[0];
|
||||
|
||||
for (int idx = 0; idx < Graphics::PALETTE_COUNT; ++idx) {
|
||||
int r, g, b;
|
||||
switch (scene._currentScene) {
|
||||
case 8:
|
||||
r = cMap[idx * 3] * 4 / 5;
|
||||
g = cMap[idx * 3 + 1] * 3 / 4;
|
||||
b = cMap[idx * 3 + 2] * 3 / 4;
|
||||
break;
|
||||
|
||||
case 18:
|
||||
case 68:
|
||||
r = cMap[idx * 3] * 4 / 3;
|
||||
g = cMap[idx * 3 + 1] * 4 / 3;
|
||||
b = cMap[idx * 3 + 2] * 4 / 3;
|
||||
break;
|
||||
|
||||
case 7:
|
||||
case 53:
|
||||
r = cMap[idx * 3] * 4 / 3;
|
||||
g = cMap[idx * 3 + 1] * 4 / 3;
|
||||
b = cMap[idx * 3 + 2] * 4 / 3;
|
||||
break;
|
||||
|
||||
default:
|
||||
r = g = b = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
byte c = 0xff;
|
||||
int cd = 99999;
|
||||
|
||||
for (int pal = 0; pal < Graphics::PALETTE_COUNT; ++pal) {
|
||||
int d = (r - cMap[pal * 3]) * (r - cMap[pal * 3]) + (g - cMap[pal * 3 + 1]) * (g - cMap[pal * 3 + 1]) +
|
||||
(b - cMap[pal * 3 + 2]) * (b - cMap[pal * 3 + 2]);
|
||||
|
||||
if (d < cd) {
|
||||
c = pal;
|
||||
cd = d;
|
||||
if (!d)
|
||||
break;
|
||||
}
|
||||
}
|
||||
*p++ = c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TattooUserInterface::doBgAnimEraseBackground() {
|
||||
People &people = *_vm->_people;
|
||||
Scene &scene = *_vm->_scene;
|
||||
Screen &screen = *_vm->_screen;
|
||||
|
||||
static const int16 OFFSETS[16] = { -1, -2, -3, -3, -2, -1, -1, 0, 1, 2, 3, 3, 2, 1, 0, 0 };
|
||||
|
||||
if (_mask != nullptr) {
|
||||
// Since a mask is active, restore the screen from the secondary back buffer prior to applying the mask
|
||||
screen._backBuffer1.SHblitFrom(screen._backBuffer2, screen._currentScroll, Common::Rect(screen._currentScroll.x, 0,
|
||||
screen._currentScroll.x + SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT));
|
||||
|
||||
switch (scene._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.width())
|
||||
_maskOffset.x = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// Standard scene without mask, so call user interface to erase any UI elements as necessary
|
||||
doBgAnimRestoreUI();
|
||||
|
||||
// Restore background for any areas covered by characters and shapes
|
||||
for (int 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 < scene._bgShapes.size(); ++idx) {
|
||||
Object &obj = scene._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.SHblitFrom(screen._backBuffer2, 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 (_creditsWidget.active())
|
||||
_creditsWidget.eraseCredits();
|
||||
}
|
||||
|
||||
for (uint idx = 0; idx < scene._bgShapes.size(); ++idx) {
|
||||
Object &obj = scene._bgShapes[idx];
|
||||
|
||||
if (obj._type == NO_SHAPE && (obj._flags & 1) == 0) {
|
||||
screen._backBuffer1.SHblitFrom(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.x) <
|
||||
(SHERLOCK_SCREEN_WIDTH / 8) && people[people._walkControl]._delta.x < 0) {
|
||||
|
||||
_targetScroll.x = (short)(people[people._walkControl]._position.x / FIXED_INT_MULTIPLIER -
|
||||
SHERLOCK_SCREEN_WIDTH / 8 - 250);
|
||||
if (_targetScroll.x < 0)
|
||||
_targetScroll.x = 0;
|
||||
}
|
||||
|
||||
if ((people[people._walkControl]._position.x / FIXED_INT_MULTIPLIER - screen._currentScroll.x) >
|
||||
(SHERLOCK_SCREEN_WIDTH / 4 * 3) && people[people._walkControl]._delta.x > 0)
|
||||
_targetScroll.x = (short)(people[people._walkControl]._position.x / FIXED_INT_MULTIPLIER -
|
||||
SHERLOCK_SCREEN_WIDTH / 4 * 3 + 250);
|
||||
|
||||
if (_targetScroll.x > _scrollSize)
|
||||
_targetScroll.x = _scrollSize;
|
||||
|
||||
doScroll();
|
||||
}
|
||||
|
||||
void TattooUserInterface::drawMaskArea(bool mode) {
|
||||
Scene &scene = *_vm->_scene;
|
||||
int xp = mode ? _maskOffset.x : 0;
|
||||
|
||||
if (_mask != nullptr) {
|
||||
switch (scene._currentScene) {
|
||||
case 7:
|
||||
maskArea(*_mask, Common::Point(_maskOffset.x - SHERLOCK_SCREEN_WIDTH, 110));
|
||||
maskArea(*_mask, Common::Point(_maskOffset.x, 110));
|
||||
maskArea(*_mask, Common::Point(_maskOffset.x + SHERLOCK_SCREEN_WIDTH, 110));
|
||||
break;
|
||||
|
||||
case 8:
|
||||
maskArea(*_mask, Common::Point(_maskOffset.x - SHERLOCK_SCREEN_WIDTH, 180));
|
||||
maskArea(*_mask, Common::Point(_maskOffset.x, 180));
|
||||
maskArea(*_mask, Common::Point(_maskOffset.x + SHERLOCK_SCREEN_WIDTH, 180));
|
||||
if (!_vm->readFlags(880))
|
||||
maskArea(*_mask1, Common::Point(940, 300));
|
||||
break;
|
||||
|
||||
case 18:
|
||||
maskArea(*_mask, Common::Point(xp, 203));
|
||||
if (!_vm->readFlags(189))
|
||||
maskArea(*_mask1, Common::Point(124 + xp, 239));
|
||||
break;
|
||||
|
||||
case 53:
|
||||
maskArea(*_mask, Common::Point(_maskOffset.x, 110));
|
||||
if (mode)
|
||||
maskArea(*_mask, Common::Point(_maskOffset.x - SHERLOCK_SCREEN_WIDTH, 110));
|
||||
break;
|
||||
|
||||
case 68:
|
||||
maskArea(*_mask, Common::Point(xp, 203));
|
||||
maskArea(*_mask1, Common::Point(124 + xp, 239));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TattooUserInterface::maskArea(Common::SeekableReadStream &mask, const Common::Point &pt) {
|
||||
Screen &screen = *_vm->_screen;
|
||||
Surface &bb1 = screen._backBuffer1;
|
||||
mask.seek(0);
|
||||
int xSize = mask.readUint16LE();
|
||||
int ySize = mask.readUint16LE();
|
||||
int pixel, len, xp, yp;
|
||||
|
||||
for (yp = 0; yp < ySize; ++yp) {
|
||||
byte *ptr = (byte *)bb1.getBasePtr(pt.x, pt.y + yp);
|
||||
|
||||
for (xp = 0; xp < xSize;) {
|
||||
// The mask data consists of pairs of pixel/lengths, where all non-zero pixels means that the
|
||||
// given pixel on the back buffer is darkened (the mask pixel value isn't otherwise used)
|
||||
pixel = mask.readByte();
|
||||
len = mask.readByte();
|
||||
|
||||
for (; len > 0; --len, ++xp, ++ptr) {
|
||||
if (pixel && (pt.x + xp) >= screen._currentScroll.x && (pt.x + xp) < (screen._currentScroll.x + SHERLOCK_SCREEN_WIDTH)) {
|
||||
*ptr = _lookupTable1[*ptr];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert(xp == xSize);
|
||||
}
|
||||
}
|
||||
|
||||
void TattooUserInterface::makeBGArea(const Common::Rect &r) {
|
||||
Screen &screen = *_vm->_screen;
|
||||
|
||||
for (int yp = r.top; yp < r.bottom; ++yp) {
|
||||
byte *ptr = (byte *)screen._backBuffer1.getBasePtr(r.left, yp);
|
||||
|
||||
for (int xp = r.left; xp < r.right; ++xp, ++ptr)
|
||||
*ptr = _lookupTable[*ptr];
|
||||
}
|
||||
|
||||
screen.slamRect(r);
|
||||
}
|
||||
|
||||
void TattooUserInterface::drawDialogRect(Surface &s, const Common::Rect &r, bool raised) {
|
||||
if (raised) {
|
||||
// Draw Left
|
||||
s.vLine(r.left, r.top, r.bottom - 1, INFO_TOP);
|
||||
s.vLine(r.left + 1, r.top, r.bottom - 2, INFO_TOP);
|
||||
// Draw Top
|
||||
s.hLine(r.left + 2, r.top, r.right - 1, INFO_TOP);
|
||||
s.hLine(r.left + 2, r.top + 1, r.right - 2, INFO_TOP);
|
||||
// Draw Right
|
||||
s.vLine(r.right - 1, r.top + 1, r.bottom - 1, INFO_BOTTOM);
|
||||
s.vLine(r.right - 2, r.top + 2, r.bottom - 1, INFO_BOTTOM);
|
||||
// Draw Bottom
|
||||
s.hLine(r.left + 1, r.bottom - 1, r.right - 3, INFO_BOTTOM);
|
||||
s.hLine(r.left + 2, r.bottom - 2, r.right - 3, INFO_BOTTOM);
|
||||
|
||||
} else {
|
||||
// Draw Left
|
||||
s.vLine(r.left, r.top, r.bottom - 1, INFO_BOTTOM);
|
||||
s.vLine(r.left + 1, r.top, r.bottom - 2, INFO_BOTTOM);
|
||||
// Draw Top
|
||||
s.hLine(r.left + 2, r.top, r.right - 1, INFO_BOTTOM);
|
||||
s.hLine(r.left + 2, r.top + 1, r.right - 2, INFO_BOTTOM);
|
||||
// Draw Right
|
||||
s.vLine(r.right - 1, r.top + 1, r.bottom - 1, INFO_TOP);
|
||||
s.vLine(r.right - 2, r.top + 2, r.bottom - 1, INFO_TOP);
|
||||
// Draw Bottom
|
||||
s.hLine(r.left + 1, r.bottom - 1, r.right - 3, INFO_TOP);
|
||||
s.hLine(r.left + 2, r.bottom - 2, r.right - 3, INFO_TOP);
|
||||
}
|
||||
}
|
||||
|
||||
void TattooUserInterface::banishWindow(bool slideUp) {
|
||||
if (!_widgets.empty())
|
||||
_widgets.back()->banishWindow();
|
||||
}
|
||||
|
||||
void TattooUserInterface::freeMenu() {
|
||||
for (Common::List<WidgetBase *>::iterator i = _widgets.begin(); i != _widgets.end(); ++i)
|
||||
(*i)->erase();
|
||||
_widgets.clear();
|
||||
}
|
||||
|
||||
void TattooUserInterface::clearWindow() {
|
||||
banishWindow();
|
||||
}
|
||||
|
||||
void TattooUserInterface::loadGame() {
|
||||
WidgetFiles &files = *(WidgetFiles *)_vm->_saves;
|
||||
files.show(SAVEMODE_LOAD);
|
||||
}
|
||||
|
||||
void TattooUserInterface::saveGame() {
|
||||
WidgetFiles &files = *(WidgetFiles *)_vm->_saves;
|
||||
files.show(SAVEMODE_SAVE);
|
||||
}
|
||||
|
||||
void TattooUserInterface::addFixedWidget(WidgetBase *widget) {
|
||||
_fixedWidgets.push_back(widget);
|
||||
widget->summonWindow();
|
||||
}
|
||||
|
||||
} // End of namespace Tattoo
|
||||
|
||||
} // End of namespace Sherlock
|
||||
264
engines/sherlock/tattoo/tattoo_user_interface.h
Normal file
264
engines/sherlock/tattoo/tattoo_user_interface.h
Normal file
@@ -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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SHERLOCK_TATTOO_UI_H
|
||||
#define SHERLOCK_TATTOO_UI_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/list.h"
|
||||
#include "sherlock/saveload.h"
|
||||
#include "sherlock/screen.h"
|
||||
#include "sherlock/user_interface.h"
|
||||
#include "sherlock/tattoo/widget_credits.h"
|
||||
#include "sherlock/tattoo/widget_files.h"
|
||||
#include "sherlock/tattoo/widget_inventory.h"
|
||||
#include "sherlock/tattoo/widget_options.h"
|
||||
#include "sherlock/tattoo/widget_quit.h"
|
||||
#include "sherlock/tattoo/widget_text.h"
|
||||
#include "sherlock/tattoo/widget_tooltip.h"
|
||||
#include "sherlock/tattoo/widget_verbs.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
namespace Tattoo {
|
||||
|
||||
// Button width/height
|
||||
#define BUTTON_SIZE 15
|
||||
// How long to play the intro before it can be skipped
|
||||
#define STARTUP_KEYS_DISABLED_DELAY 200
|
||||
|
||||
class WidgetBase;
|
||||
|
||||
enum ScrollHighlight { SH_NONE = 0, SH_SCROLL_UP = 1, SH_PAGE_UP = 2, SH_THUMBNAIL = 3, SH_PAGE_DOWN = 4, SH_SCROLL_DOWN = 5 };
|
||||
|
||||
class WidgetList : public Common::List <WidgetBase *> {
|
||||
public:
|
||||
bool contains(const WidgetBase *item) const;
|
||||
};
|
||||
|
||||
class TattooUserInterface : public UserInterface {
|
||||
friend class WidgetBase;
|
||||
private:
|
||||
int _scriptZone;
|
||||
int _cAnimFramePause;
|
||||
WidgetInventory _inventoryWidget;
|
||||
WidgetMessage _messageWidget;
|
||||
WidgetQuit _quitWidget;
|
||||
WidgetList _fixedWidgets;
|
||||
WidgetList _widgets;
|
||||
byte _lookupTable[Graphics::PALETTE_COUNT];
|
||||
byte _lookupTable1[Graphics::PALETTE_COUNT];
|
||||
private:
|
||||
/**
|
||||
* Handle any input when we're in standard mode (with no windows open)
|
||||
*/
|
||||
void doStandardControl();
|
||||
|
||||
/**
|
||||
* Handle input when in look mode
|
||||
*/
|
||||
void doLookControl();
|
||||
|
||||
/**
|
||||
* Handle input when the File window is open
|
||||
*/
|
||||
void doFileControl();
|
||||
|
||||
/**
|
||||
* Handle input while the verb menu is open
|
||||
*/
|
||||
void doVerbControl();
|
||||
|
||||
/**
|
||||
* Free any active menu
|
||||
*/
|
||||
void freeMenu();
|
||||
public:
|
||||
Common::Point _targetScroll;
|
||||
int _scrollSize, _scrollSpeed;
|
||||
bool _drawMenu;
|
||||
int _arrowZone, _oldArrowZone;
|
||||
Object *_bgShape;
|
||||
bool _personFound;
|
||||
int _activeObj;
|
||||
Common::KeyState _keyState;
|
||||
Common::CustomEventType _action;
|
||||
Common::Point _lookPos;
|
||||
ScrollHighlight _scrollHighlight;
|
||||
Common::SeekableReadStream *_mask, *_mask1;
|
||||
Common::Point _maskOffset;
|
||||
int _maskCounter;
|
||||
int _lockoutTimer;
|
||||
ImageFile *_interfaceImages;
|
||||
WidgetCredits _creditsWidget;
|
||||
WidgetOptions _optionsWidget;
|
||||
WidgetText _textWidget;
|
||||
WidgetSceneTooltip _tooltipWidget;
|
||||
WidgetVerbs _verbsWidget;
|
||||
WidgetList _postRenderWidgets;
|
||||
public:
|
||||
TattooUserInterface(SherlockEngine *vm);
|
||||
~TattooUserInterface() override;
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
/**
|
||||
* Initializes scroll variables
|
||||
*/
|
||||
void initScrollVars();
|
||||
|
||||
/**
|
||||
* Display the long description for an object in a window
|
||||
*/
|
||||
void lookAtObject();
|
||||
|
||||
/**
|
||||
* Display the passed long description for an object. If the flag firstTime is set,
|
||||
* the window will be opened to accommodate the text. Otherwise, the remaining text
|
||||
* will be printed in an already open window
|
||||
*/
|
||||
void printObjectDesc(const Common::String &str, bool firstTime);
|
||||
|
||||
/**
|
||||
* Handles displaying the journal
|
||||
*/
|
||||
void doJournal();
|
||||
|
||||
/**
|
||||
* Put the game in inventory mode by opening the inventory dialog
|
||||
*/
|
||||
void doInventory(int mode);
|
||||
|
||||
/**
|
||||
* Handle the display of the options/setup menu
|
||||
*/
|
||||
void doControls();
|
||||
|
||||
/**
|
||||
* Handle the display of the quit menu
|
||||
*/
|
||||
void doQuitMenu();
|
||||
|
||||
/**
|
||||
* Pick up the selected object
|
||||
*/
|
||||
void pickUpObject(int objNum);
|
||||
|
||||
/**
|
||||
* This will display a text message in a dialog at the bottom of the screen
|
||||
*/
|
||||
void putMessage(MSVC_PRINTF const char *formatStr, ...) GCC_PRINTF(2, 3);
|
||||
|
||||
/**
|
||||
* Makes a greyscale translation table for each palette entry in the table
|
||||
*/
|
||||
void setupBGArea(const byte cMap[Graphics::PALETTE_SIZE]);
|
||||
|
||||
/**
|
||||
* Erase any background as needed before drawing frame
|
||||
*/
|
||||
void doBgAnimEraseBackground();
|
||||
|
||||
/**
|
||||
* Draws overlays onto the scene. Basically, the smoke effects some scenes have
|
||||
*/
|
||||
void drawMaskArea(bool mode);
|
||||
|
||||
/**
|
||||
* Takes the data passed in the image and apply it to the surface at the given position.
|
||||
* The src mask data is encoded with a different color for each item. To highlight one,
|
||||
the runs that do not match the highlight number will be darkened
|
||||
*/
|
||||
void maskArea(Common::SeekableReadStream &mask, const Common::Point &pt);
|
||||
|
||||
/**
|
||||
* Translate a given area of the back buffer to greyscale shading
|
||||
*/
|
||||
void makeBGArea(const Common::Rect &r);
|
||||
|
||||
/**
|
||||
* Draws all the dialog rectangles for any items that need them
|
||||
*/
|
||||
void drawDialogRect(Surface &s, const Common::Rect &r, bool raised);
|
||||
|
||||
/**
|
||||
* If the mouse cursor is point at the cursor, then display the name of the object on the screen.
|
||||
* If there is no object being pointed it, clear any previously displayed name
|
||||
*/
|
||||
void displayObjectNames();
|
||||
|
||||
/**
|
||||
* Show the load game dialog, and allow the user to load a game
|
||||
*/
|
||||
void loadGame();
|
||||
|
||||
/**
|
||||
* Show the save game dialog, and allow the user to save the game
|
||||
*/
|
||||
void saveGame();
|
||||
|
||||
/**
|
||||
* Add a widget to the current scene to be executed if there are no active widgets in the
|
||||
* main _widgets list
|
||||
*/
|
||||
void addFixedWidget(WidgetBase *widget);
|
||||
public:
|
||||
/**
|
||||
* Resets the user interface
|
||||
*/
|
||||
void reset() override;
|
||||
|
||||
/**
|
||||
* Main input handler for the user interface
|
||||
*/
|
||||
void handleInput() override;
|
||||
|
||||
/**
|
||||
* Draw the user interface onto the screen's back buffers
|
||||
*/
|
||||
void drawInterface(int bufferNum = 3) override;
|
||||
|
||||
/**
|
||||
* Clear any active text window
|
||||
*/
|
||||
void clearWindow() override;
|
||||
|
||||
/**
|
||||
* Banish any active window
|
||||
* @remarks Tattoo doesn't use sliding windows, but the parameter is in the base
|
||||
* UserInterface class as a convenience for Scalpel UI code
|
||||
*/
|
||||
void banishWindow(bool slideUp = true) override;
|
||||
};
|
||||
|
||||
} // End of namespace Tattoo
|
||||
|
||||
} // End of namespace Sherlock
|
||||
|
||||
#endif
|
||||
351
engines/sherlock/tattoo/widget_base.cpp
Normal file
351
engines/sherlock/tattoo/widget_base.cpp
Normal file
@@ -0,0 +1,351 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sherlock/tattoo/widget_base.h"
|
||||
#include "sherlock/tattoo/tattoo.h"
|
||||
#include "sherlock/tattoo/tattoo_scene.h"
|
||||
#include "sherlock/tattoo/tattoo_talk.h"
|
||||
#include "sherlock/tattoo/tattoo_user_interface.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
namespace Tattoo {
|
||||
|
||||
WidgetBase::WidgetBase(SherlockEngine *vm) : _vm(vm) {
|
||||
_scroll = false;
|
||||
_dialogTimer = 0;
|
||||
_outsideMenu = false;
|
||||
}
|
||||
|
||||
void WidgetBase::summonWindow() {
|
||||
TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
|
||||
|
||||
// Double-check that the same widget isn't added twice
|
||||
if (ui._widgets.contains(this))
|
||||
error("Tried to add a widget multiple times");
|
||||
|
||||
// Add widget to the screen
|
||||
if (!ui._fixedWidgets.contains(this))
|
||||
ui._widgets.push_back(this);
|
||||
ui._windowOpen = true;
|
||||
|
||||
_outsideMenu = false;
|
||||
|
||||
draw();
|
||||
}
|
||||
|
||||
void WidgetBase::banishWindow() {
|
||||
TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
|
||||
|
||||
erase();
|
||||
_surface.free();
|
||||
ui._widgets.remove(this);
|
||||
ui._windowOpen = false;
|
||||
}
|
||||
|
||||
void WidgetBase::close() {
|
||||
Events &events = *_vm->_events;
|
||||
TattooScene &scene = *(TattooScene *)_vm->_scene;
|
||||
TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
|
||||
|
||||
banishWindow();
|
||||
ui._menuMode = scene._labTableScene ? LAB_MODE : STD_MODE;
|
||||
events.clearEvents();
|
||||
}
|
||||
|
||||
bool WidgetBase::active() const {
|
||||
TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
|
||||
for (Common::List<WidgetBase *>::iterator i = ui._widgets.begin(); i != ui._widgets.end(); ++i) {
|
||||
if ((*i) == this)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void WidgetBase::erase() {
|
||||
Screen &screen = *_vm->_screen;
|
||||
|
||||
if (_oldBounds.width() > 0) {
|
||||
// Restore the affected area from the secondary back buffer into the first one, and then copy to screen
|
||||
screen._backBuffer1.SHblitFrom(screen._backBuffer2, Common::Point(_oldBounds.left, _oldBounds.top), _oldBounds);
|
||||
screen.slamRect(_oldBounds);
|
||||
|
||||
// Reset the old bounds so it won't be erased again
|
||||
_oldBounds = Common::Rect(0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void WidgetBase::draw() {
|
||||
Screen &screen = *_vm->_screen;
|
||||
|
||||
// If there was a previously drawn frame in a different position that hasn't yet been erased, then erase it
|
||||
if (_oldBounds.width() > 0 && _oldBounds != _bounds)
|
||||
erase();
|
||||
|
||||
if (_bounds.width() > 0 && !_surface.empty()) {
|
||||
// Get the area to draw, adjusted for scroll position
|
||||
restrictToScreen();
|
||||
|
||||
// Draw the background for the widget
|
||||
drawBackground();
|
||||
|
||||
// Draw the widget onto the back buffer and then slam it to the screen
|
||||
screen._backBuffer1.SHtransBlitFrom(_surface, Common::Point(_bounds.left, _bounds.top));
|
||||
screen.slamRect(_bounds);
|
||||
|
||||
// Store a copy of the drawn area for later erasing
|
||||
_oldBounds = _bounds;
|
||||
}
|
||||
}
|
||||
|
||||
void WidgetBase::drawBackground() {
|
||||
TattooEngine &vm = *(TattooEngine *)_vm;
|
||||
Screen &screen = *_vm->_screen;
|
||||
TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
|
||||
|
||||
Common::Rect bounds = _bounds;
|
||||
bounds.grow(-3);
|
||||
|
||||
if (vm._transparentMenus) {
|
||||
ui.makeBGArea(bounds);
|
||||
} else {
|
||||
screen._backBuffer1.fillRect(bounds, MENU_BACKGROUND);
|
||||
}
|
||||
}
|
||||
|
||||
Common::String WidgetBase::splitLines(const Common::String &str, Common::StringArray &lines, int maxWidth, uint maxLines) {
|
||||
Talk &talk = *_vm->_talk;
|
||||
|
||||
lines.clear();
|
||||
uint idx;
|
||||
for (idx = 0; idx < str.size(); idx++)
|
||||
if (str[idx] >= talk._opcodes[OP_SWITCH_SPEAKER] && str[idx] != talk._opcodes[OP_NULL])
|
||||
break;
|
||||
Common::String rest;
|
||||
Common::Array<Common::String> arr = _surface.wordWrap(str.substr(0, idx), maxWidth, rest, Common::String::npos, maxLines);
|
||||
lines.swap(arr);
|
||||
|
||||
// Return any remaining text left over
|
||||
return rest + str.substr(idx);
|
||||
}
|
||||
|
||||
void WidgetBase::restrictToScreen() {
|
||||
Screen &screen = *_vm->_screen;
|
||||
|
||||
if (_bounds.left < screen._currentScroll.x)
|
||||
_bounds.moveTo(screen._currentScroll.x, _bounds.top);
|
||||
if (_bounds.top < 0)
|
||||
_bounds.moveTo(_bounds.left, 0);
|
||||
if (_bounds.right > (screen._currentScroll.x + SHERLOCK_SCREEN_WIDTH))
|
||||
_bounds.moveTo(screen._currentScroll.x + SHERLOCK_SCREEN_WIDTH - _bounds.width(), _bounds.top);
|
||||
if (_bounds.bottom > screen._backBuffer1.height())
|
||||
_bounds.moveTo(_bounds.left, screen._backBuffer1.height() - _bounds.height());
|
||||
}
|
||||
|
||||
void WidgetBase::makeInfoArea(Surface &s) {
|
||||
TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
|
||||
ImageFile &images = *ui._interfaceImages;
|
||||
|
||||
// Draw the four corners of the Info Box
|
||||
s.SHtransBlitFrom(images[0], Common::Point(0, 0));
|
||||
s.SHtransBlitFrom(images[1], Common::Point(s.width() - images[1]._width, 0));
|
||||
s.SHtransBlitFrom(images[2], Common::Point(0, s.height() - images[2]._height));
|
||||
s.SHtransBlitFrom(images[3], Common::Point(s.width() - images[3]._width, s.height() - images[3]._height));
|
||||
|
||||
// Draw the top of the Info Box
|
||||
s.hLine(images[0]._width, 0, s.width() - images[1]._width, INFO_TOP);
|
||||
s.hLine(images[0]._width, 1, s.width() - images[1]._width, INFO_MIDDLE);
|
||||
s.hLine(images[0]._width, 2, s.width() - images[1]._width, INFO_BOTTOM);
|
||||
|
||||
// Draw the bottom of the Info Box
|
||||
s.hLine(images[0]._width, s.height()- 3, s.width() - images[1]._width, INFO_TOP);
|
||||
s.hLine(images[0]._width, s.height()- 2, s.width() - images[1]._width, INFO_MIDDLE);
|
||||
s.hLine(images[0]._width, s.height()- 1, s.width() - images[1]._width, INFO_BOTTOM);
|
||||
|
||||
// Draw the left Side of the Info Box
|
||||
s.vLine(0, images[0]._height, s.height()- images[2]._height, INFO_TOP);
|
||||
s.vLine(1, images[0]._height, s.height()- images[2]._height, INFO_MIDDLE);
|
||||
s.vLine(2, images[0]._height, s.height()- images[2]._height, INFO_BOTTOM);
|
||||
|
||||
// Draw the right Side of the Info Box
|
||||
s.vLine(s.width() - 3, images[0]._height, s.height()- images[2]._height, INFO_TOP);
|
||||
s.vLine(s.width() - 2, images[0]._height, s.height()- images[2]._height, INFO_MIDDLE);
|
||||
s.vLine(s.width() - 1, images[0]._height, s.height()- images[2]._height, INFO_BOTTOM);
|
||||
}
|
||||
|
||||
void WidgetBase::makeInfoArea() {
|
||||
makeInfoArea(_surface);
|
||||
}
|
||||
|
||||
void WidgetBase::drawDialogRect(const Common::Rect &r, bool raised) {
|
||||
static_cast<TattooUserInterface *>(_vm->_ui)->drawDialogRect(_surface, r, raised);
|
||||
}
|
||||
|
||||
void WidgetBase::checkTabbingKeys(int numOptions) {
|
||||
}
|
||||
|
||||
Common::Rect WidgetBase::getScrollBarBounds() const {
|
||||
Common::Rect r(BUTTON_SIZE, _bounds.height() - 6);
|
||||
r.moveTo(_bounds.width() - BUTTON_SIZE - 3, 3);
|
||||
return r;
|
||||
}
|
||||
|
||||
void WidgetBase::drawScrollBar(int index, int pageSize, int count) {
|
||||
TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
|
||||
|
||||
// Fill the area with transparency
|
||||
Common::Rect r = getScrollBarBounds();
|
||||
_surface.fillRect(r, TRANSPARENCY);
|
||||
|
||||
bool raised = ui._scrollHighlight != 1;
|
||||
_surface.fillRect(Common::Rect(r.left + 2, r.top + 2, r.right - 2, r.top + BUTTON_SIZE - 2), INFO_MIDDLE);
|
||||
ui.drawDialogRect(_surface, Common::Rect(r.left, r.top, r.left + BUTTON_SIZE, r.top + BUTTON_SIZE), raised);
|
||||
|
||||
raised = ui._scrollHighlight != 5;
|
||||
_surface.fillRect(Common::Rect(r.left + 2, r.bottom - BUTTON_SIZE + 2, r.right - 2, r.bottom - 2), INFO_MIDDLE);
|
||||
ui.drawDialogRect(_surface, Common::Rect(r.left, r.bottom - BUTTON_SIZE, r.right, r.bottom), raised);
|
||||
|
||||
// Draw the arrows on the scroll buttons
|
||||
byte color = index ? INFO_BOTTOM + 2 : INFO_BOTTOM;
|
||||
_surface.hLine(r.left + r.width() / 2, r.top - 2 + BUTTON_SIZE / 2, r.left + r.width() / 2, color);
|
||||
_surface.hLine(r.left + r.width() / 2 - 1, r.top - 1 + BUTTON_SIZE / 2, r.left + r.width() / 2 + 1, color);
|
||||
_surface.hLine(r.left + r.width() / 2 - 2, r.top + BUTTON_SIZE / 2, r.left + r.width() / 2 + 2, color);
|
||||
_surface.hLine(r.left + r.width() / 2 - 3, r.top + 1 + BUTTON_SIZE / 2, r.left + r.width() / 2 + 3, color);
|
||||
|
||||
color = (index + pageSize) < count ? INFO_BOTTOM + 2 : INFO_BOTTOM;
|
||||
_surface.hLine(r.left + r.width() / 2 - 3, r.bottom - 1 - BUTTON_SIZE + BUTTON_SIZE / 2, r.left + r.width() / 2 + 3, color);
|
||||
_surface.hLine(r.left + r.width() / 2 - 2, r.bottom - 1 - BUTTON_SIZE + 1 + BUTTON_SIZE / 2, r.left + r.width() / 2 + 2, color);
|
||||
_surface.hLine(r.left + r.width() / 2 - 1, r.bottom - 1 - BUTTON_SIZE + 2 + BUTTON_SIZE / 2, r.left + r.width() / 2 + 1, color);
|
||||
_surface.hLine(r.left + r.width() / 2, r.bottom - 1 - BUTTON_SIZE + 3 + BUTTON_SIZE / 2, r.left + r.width() / 2, color);
|
||||
|
||||
// Draw the scroll position bar
|
||||
int barHeight = (r.height() - BUTTON_SIZE * 2) * pageSize / count;
|
||||
barHeight = CLIP(barHeight, BUTTON_SIZE, r.height() - BUTTON_SIZE * 2);
|
||||
int barY = (count <= pageSize) ? r.top + BUTTON_SIZE : r.top + BUTTON_SIZE +
|
||||
(r.height() - BUTTON_SIZE * 2 - barHeight) * index / (count - pageSize);
|
||||
|
||||
_surface.fillRect(Common::Rect(r.left + 2, barY + 2, r.right - 2, barY + barHeight - 2), INFO_MIDDLE);
|
||||
ui.drawDialogRect(_surface, Common::Rect(r.left, barY, r.right, barY + barHeight), true);
|
||||
}
|
||||
|
||||
void WidgetBase::handleScrollbarEvents(int index, int pageSize, int count) {
|
||||
Events &events = *_vm->_events;
|
||||
TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
|
||||
Common::Point mousePos = events.mousePos();
|
||||
|
||||
// If they're dragging the scrollbar thumb, keep it selected whilst the button is being held
|
||||
if ((events._pressed || events._released) && ui._scrollHighlight == SH_THUMBNAIL)
|
||||
return;
|
||||
|
||||
ui._scrollHighlight = SH_NONE;
|
||||
|
||||
if ((!events._pressed && !events._rightReleased) || !_scroll)
|
||||
return;
|
||||
|
||||
Common::Rect r = getScrollBarBounds();
|
||||
r.translate(_bounds.left, _bounds.top);
|
||||
|
||||
// Calculate the Scroll Position bar
|
||||
int barHeight = (r.height() - BUTTON_SIZE * 2) * pageSize / count;
|
||||
barHeight = CLIP(barHeight, BUTTON_SIZE, r.height() - BUTTON_SIZE * 2);
|
||||
int barY = (count <= pageSize) ? r.top + BUTTON_SIZE : r.top + BUTTON_SIZE +
|
||||
(r.height() - BUTTON_SIZE * 2 - barHeight) * index / (count - pageSize);
|
||||
|
||||
if (Common::Rect(r.left, r.top, r.right, r.top + BUTTON_SIZE).contains(mousePos))
|
||||
// Mouse on scroll up button
|
||||
ui._scrollHighlight = SH_SCROLL_UP;
|
||||
else if (Common::Rect(r.left, r.top + BUTTON_SIZE, r.right, barY).contains(mousePos))
|
||||
// Mouse on paging up area (the area of the vertical bar above the thumbnail)
|
||||
ui._scrollHighlight = SH_PAGE_UP;
|
||||
else if (Common::Rect(r.left, barY, r.right, barY + barHeight).contains(mousePos))
|
||||
// Mouse on scrollbar thumb
|
||||
ui._scrollHighlight = SH_THUMBNAIL;
|
||||
else if (Common::Rect(r.left, barY + barHeight, r.right, r.bottom - BUTTON_SIZE).contains(mousePos))
|
||||
// Mouse on paging down area (the area of the vertical bar below the thumbnail)
|
||||
ui._scrollHighlight = SH_PAGE_DOWN;
|
||||
else if (Common::Rect(r.left, r.bottom - BUTTON_SIZE, r.right, r.bottom).contains(mousePos))
|
||||
// Mouse on scroll down button
|
||||
ui._scrollHighlight = SH_SCROLL_DOWN;
|
||||
}
|
||||
|
||||
void WidgetBase::handleScrolling(int &scrollIndex, int pageSize, int max) {
|
||||
Events &events = *_vm->_events;
|
||||
TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
|
||||
Common::CustomEventType action = ui._action;
|
||||
Common::Point mousePos = events.mousePos();
|
||||
|
||||
Common::Rect r = getScrollBarBounds();
|
||||
r.translate(_bounds.left, _bounds.top);
|
||||
|
||||
if (ui._scrollHighlight != SH_NONE || action == kActionTattooWidgetScrollStart || action == kActionTattooWidgetScrollEnd
|
||||
|| action == kActionTattooWidgetScrollPageUp || action == kActionTattooWidgetScrollPageDown
|
||||
|| action == kActionTattooWidgetScrollUp || action == kActionTattooWidgetScrollDown) {
|
||||
// Check for the scrollbar
|
||||
if (ui._scrollHighlight == SH_THUMBNAIL) {
|
||||
int yp = mousePos.y;
|
||||
yp = CLIP(yp, r.top + BUTTON_SIZE + 3, r.bottom - BUTTON_SIZE - 3);
|
||||
|
||||
// Calculate the line number that corresponds to the position that the mouse is on the scrollbar
|
||||
int lineNum = (yp - r.top - BUTTON_SIZE - 3) * (max - pageSize) / (r.height() - BUTTON_SIZE * 2 - 6);
|
||||
scrollIndex = CLIP(lineNum, 0, max - pageSize);
|
||||
}
|
||||
|
||||
// Get the current frame so we can check the scroll timer against it
|
||||
uint32 frameNum = events.getFrameCounter();
|
||||
|
||||
if (frameNum > _dialogTimer) {
|
||||
// Set the timeout for the next scroll if the mouse button remains held down
|
||||
_dialogTimer = (_dialogTimer == 0) ? frameNum + pageSize : frameNum + 1;
|
||||
|
||||
// Check for Scroll Up
|
||||
if ((ui._scrollHighlight == SH_SCROLL_UP || action == kActionTattooWidgetScrollUp) && scrollIndex)
|
||||
--scrollIndex;
|
||||
|
||||
// Check for Page Up
|
||||
else if ((ui._scrollHighlight == SH_PAGE_UP || action == kActionTattooWidgetScrollPageUp) && scrollIndex)
|
||||
scrollIndex -= pageSize;
|
||||
|
||||
// Check for Page Down
|
||||
else if ((ui._scrollHighlight == SH_PAGE_DOWN || action == kActionTattooWidgetScrollPageDown)
|
||||
&& (scrollIndex + pageSize < max)) {
|
||||
scrollIndex += pageSize;
|
||||
if (scrollIndex + pageSize >max)
|
||||
scrollIndex = max - pageSize;
|
||||
}
|
||||
|
||||
// Check for Scroll Down
|
||||
else if ((ui._scrollHighlight == SH_SCROLL_DOWN || action == kActionTattooWidgetScrollDown) && (scrollIndex + pageSize < max))
|
||||
++scrollIndex;
|
||||
}
|
||||
|
||||
if (action == kActionTattooWidgetScrollEnd)
|
||||
scrollIndex = max - pageSize;
|
||||
|
||||
if (scrollIndex < 0 || action == kActionTattooWidgetScrollStart)
|
||||
scrollIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Tattoo
|
||||
|
||||
} // End of namespace Sherlock
|
||||
146
engines/sherlock/tattoo/widget_base.h
Normal file
146
engines/sherlock/tattoo/widget_base.h
Normal file
@@ -0,0 +1,146 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SHERLOCK_TATTOO_WIDGET_BASE_H
|
||||
#define SHERLOCK_TATTOO_WIDGET_BASE_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/str-array.h"
|
||||
#include "sherlock/surface.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
class SherlockEngine;
|
||||
class ImageFile;
|
||||
|
||||
namespace Tattoo {
|
||||
|
||||
class WidgetBase {
|
||||
private:
|
||||
uint32 _dialogTimer;
|
||||
protected:
|
||||
SherlockEngine *_vm;
|
||||
Common::Rect _bounds;
|
||||
Common::Rect _oldBounds;
|
||||
Surface _surface;
|
||||
bool _outsideMenu;
|
||||
bool _scroll;
|
||||
|
||||
/**
|
||||
* Used by descendent classes to split up long text for display across multiple lines
|
||||
*/
|
||||
Common::String splitLines(const Common::String &str, Common::StringArray &lines, int maxWidth, uint maxLines);
|
||||
|
||||
/**
|
||||
* Ensure that menu is drawn entirely on-screen
|
||||
*/
|
||||
void restrictToScreen();
|
||||
|
||||
/**
|
||||
* Draw a window frame around the dges of the passed surface
|
||||
*/
|
||||
void makeInfoArea(Surface &s);
|
||||
|
||||
/**
|
||||
* Draw a window frame around the widget's surface
|
||||
*/
|
||||
void makeInfoArea();
|
||||
|
||||
/**
|
||||
* Draw a dialog rectangle
|
||||
*/
|
||||
void drawDialogRect(const Common::Rect &r, bool raised = true);
|
||||
|
||||
/**
|
||||
* Return the area of a widget that the scrollbar will be drawn in
|
||||
*/
|
||||
virtual Common::Rect getScrollBarBounds() const;
|
||||
|
||||
/**
|
||||
* Draw the scrollbar for the dialog
|
||||
*/
|
||||
void drawScrollBar(int index, int pageSize, int count);
|
||||
|
||||
/**
|
||||
* Handles any events when the mouse is on the scrollbar
|
||||
*/
|
||||
void handleScrollbarEvents(int index, int pageSize, int count);
|
||||
|
||||
/**
|
||||
* Handle adjusting a passed scrolling index as necessary
|
||||
*/
|
||||
void handleScrolling(int &scrollIndex, int pageSize, int max);
|
||||
|
||||
/**
|
||||
* Close the dialog
|
||||
*/
|
||||
void close();
|
||||
|
||||
/**
|
||||
* Handle drawing the background on the area the widget is going to cover
|
||||
*/
|
||||
virtual void drawBackground();
|
||||
public:
|
||||
WidgetBase(SherlockEngine *vm);
|
||||
virtual ~WidgetBase() {}
|
||||
|
||||
/**
|
||||
* Returns true if the given widget is active in the user interface's widget list
|
||||
*/
|
||||
bool active() const;
|
||||
|
||||
/**
|
||||
* Erase any previous display of the widget on the screen
|
||||
*/
|
||||
virtual void erase();
|
||||
|
||||
/**
|
||||
* Update the display of the widget on the screen
|
||||
*/
|
||||
virtual void draw();
|
||||
|
||||
/**
|
||||
* Used by some descendents to check for keys to mouse the mouse within the dialog
|
||||
*/
|
||||
void checkTabbingKeys(int numOptions);
|
||||
|
||||
/**
|
||||
* Summon the window
|
||||
*/
|
||||
virtual void summonWindow();
|
||||
|
||||
/**
|
||||
* Close a currently active menu
|
||||
*/
|
||||
virtual void banishWindow();
|
||||
|
||||
/**
|
||||
* Handle event processing
|
||||
*/
|
||||
virtual void handleEvents() {}
|
||||
};
|
||||
|
||||
} // End of namespace Tattoo
|
||||
|
||||
} // End of namespace Sherlock
|
||||
|
||||
#endif
|
||||
215
engines/sherlock/tattoo/widget_credits.cpp
Normal file
215
engines/sherlock/tattoo/widget_credits.cpp
Normal file
@@ -0,0 +1,215 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#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.height();
|
||||
|
||||
_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.width() - 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::close() {
|
||||
_creditsActive = false;
|
||||
_creditLines.clear();
|
||||
}
|
||||
|
||||
void WidgetCredits::drawCredits() {
|
||||
Screen &screen = *_vm->_screen;
|
||||
Common::Rect screenRect(0, 0, screen.width(), screen.height());
|
||||
Surface &bb1 = screen._backBuffer1;
|
||||
|
||||
for (uint idx = 0; idx < _creditLines.size() && _creditLines[idx]._position.y < screen.height(); ++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.width(), screen.height() + _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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WidgetCredits::eraseCredits() {
|
||||
Screen &screen = *_vm->_screen;
|
||||
Common::Rect screenRect(0, -_creditSpeed, screen.width(), screen.height() + _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);
|
||||
screen.slamRect(r);
|
||||
}
|
||||
|
||||
_creditLines[idx]._position.y -= _creditSpeed;
|
||||
}
|
||||
|
||||
if (_creditLines[_creditLines.size() - 1]._position.y < -_creditSpeed) {
|
||||
// Completely finished credits display. Note that the credits will still remain flagged as active,
|
||||
// so that the user interface knows not to allow and standard scene interaction
|
||||
_creditLines.clear();
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Tattoo
|
||||
|
||||
} // End of namespace Sherlock
|
||||
89
engines/sherlock/tattoo/widget_credits.h
Normal file
89
engines/sherlock/tattoo/widget_credits.h
Normal file
@@ -0,0 +1,89 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SHERLOCK_TATTOO_CREDITS_H
|
||||
#define SHERLOCK_TATTOO_CREDITS_H
|
||||
|
||||
#include "common/array.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/str.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();
|
||||
|
||||
/**
|
||||
* Closes down credits display
|
||||
*/
|
||||
void close();
|
||||
|
||||
/**
|
||||
* 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
|
||||
457
engines/sherlock/tattoo/widget_files.cpp
Normal file
457
engines/sherlock/tattoo/widget_files.cpp
Normal file
@@ -0,0 +1,457 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/translation.h"
|
||||
#include "gui/saveload.h"
|
||||
#include "sherlock/tattoo/widget_files.h"
|
||||
#include "sherlock/tattoo/tattoo.h"
|
||||
#include "sherlock/tattoo/tattoo_fixed_text.h"
|
||||
#include "sherlock/tattoo/tattoo_scene.h"
|
||||
#include "sherlock/tattoo/tattoo_user_interface.h"
|
||||
|
||||
#include "backends/keymapper/keymapper.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
namespace Tattoo {
|
||||
|
||||
#define FILES_LINES_COUNT 5
|
||||
|
||||
WidgetFiles::WidgetFiles(SherlockEngine *vm, const Common::String &target) :
|
||||
SaveManager(vm, target), WidgetBase(vm), _vm(vm) {
|
||||
_fileMode = SAVEMODE_NONE;
|
||||
_selector = _oldSelector = -1;
|
||||
}
|
||||
|
||||
void WidgetFiles::show(SaveMode mode) {
|
||||
Events &events = *_vm->_events;
|
||||
TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
|
||||
Common::Point mousePos = events.mousePos();
|
||||
|
||||
if (_vm->_showOriginalSavesDialog) {
|
||||
// Render and display the file dialog
|
||||
_fileMode = mode;
|
||||
ui._menuMode = FILES_MODE;
|
||||
_selector = _oldSelector = -1;
|
||||
_scroll = true;
|
||||
createSavegameList();
|
||||
|
||||
// Set up the display area
|
||||
_bounds = Common::Rect(SHERLOCK_SCREEN_WIDTH * 2 / 3, (_surface.fontHeight() + 1) *
|
||||
(FILES_LINES_COUNT + 1) + 17);
|
||||
_bounds.moveTo(mousePos.x - _bounds.width() / 2, mousePos.y - _bounds.height() / 2);
|
||||
|
||||
// Create the surface and render its contents
|
||||
_surface.create(_bounds.width(), _bounds.height());
|
||||
render(RENDER_ALL);
|
||||
|
||||
summonWindow();
|
||||
ui._menuMode = FILES_MODE;
|
||||
|
||||
Common::Keymapper *keymapper = g_system->getEventManager()->getKeymapper();
|
||||
keymapper->getKeymap("tattoo")->setEnabled(false);
|
||||
keymapper->getKeymap("tattoo-scrolling")->setEnabled(true);
|
||||
keymapper->getKeymap("tattoo-files")->setEnabled(true);
|
||||
} else if (mode == SAVEMODE_LOAD) {
|
||||
showScummVMRestoreDialog();
|
||||
} else {
|
||||
showScummVMSaveDialog();
|
||||
}
|
||||
}
|
||||
|
||||
void WidgetFiles::showScummVMSaveDialog() {
|
||||
GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Save game:"), _("Save"), true);
|
||||
|
||||
int slot = dialog->runModalWithCurrentTarget();
|
||||
if (slot >= 0) {
|
||||
Common::String desc = dialog->getResultString();
|
||||
|
||||
if (desc.empty()) {
|
||||
// create our own description for the saved game, the user didn't enter it
|
||||
desc = dialog->createDefaultSaveDescription(slot);
|
||||
}
|
||||
|
||||
_vm->saveGameState(slot, desc);
|
||||
}
|
||||
|
||||
close();
|
||||
delete dialog;
|
||||
}
|
||||
|
||||
void WidgetFiles::showScummVMRestoreDialog() {
|
||||
GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Restore game:"), _("Restore"), false);
|
||||
int slot = dialog->runModalWithCurrentTarget();
|
||||
close();
|
||||
delete dialog;
|
||||
|
||||
if (slot >= 0) {
|
||||
_vm->loadGameState(slot);
|
||||
}
|
||||
}
|
||||
|
||||
void WidgetFiles::render(FilesRenderMode mode) {
|
||||
TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
|
||||
ImageFile &images = *ui._interfaceImages;
|
||||
byte color;
|
||||
|
||||
if (mode == RENDER_ALL) {
|
||||
_surface.clear(TRANSPARENCY);
|
||||
makeInfoArea();
|
||||
|
||||
switch (_fileMode) {
|
||||
case SAVEMODE_LOAD:
|
||||
_surface.writeString(FIXED(LoadGame),
|
||||
Common::Point((_surface.width() - _surface.stringWidth(FIXED(LoadGame))) / 2, 5), INFO_TOP);
|
||||
break;
|
||||
|
||||
case SAVEMODE_SAVE:
|
||||
_surface.writeString(FIXED(SaveGame),
|
||||
Common::Point((_surface.width() - _surface.stringWidth(FIXED(SaveGame))) / 2, 5), INFO_TOP);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
_surface.hLine(3, _surface.fontHeight() + 7, _surface.width() - 4, INFO_TOP);
|
||||
_surface.hLine(3, _surface.fontHeight() + 8, _surface.width() - 4, INFO_MIDDLE);
|
||||
_surface.hLine(3, _surface.fontHeight() + 9, _surface.width() - 4, INFO_BOTTOM);
|
||||
_surface.SHtransBlitFrom(images[4], Common::Point(0, _surface.fontHeight() + 6));
|
||||
_surface.SHtransBlitFrom(images[5], Common::Point(_surface.width() - images[5]._width, _surface.fontHeight() + 6));
|
||||
|
||||
int xp = _surface.width() - BUTTON_SIZE - 6;
|
||||
_surface.vLine(xp, _surface.fontHeight() + 10, _bounds.height() - 4, INFO_TOP);
|
||||
_surface.vLine(xp + 1, _surface.fontHeight() + 10, _bounds.height() - 4, INFO_MIDDLE);
|
||||
_surface.vLine(xp + 2, _surface.fontHeight() + 10, _bounds.height() - 4, INFO_BOTTOM);
|
||||
_surface.SHtransBlitFrom(images[6], Common::Point(xp - 1, _surface.fontHeight() + 8));
|
||||
_surface.SHtransBlitFrom(images[7], Common::Point(xp - 1, _bounds.height() - 4));
|
||||
}
|
||||
|
||||
int xp = _surface.stringWidth("00.") + _surface.widestChar() + 5;
|
||||
int yp = _surface.fontHeight() + 14;
|
||||
|
||||
for (int idx = _savegameIndex; idx < (_savegameIndex + FILES_LINES_COUNT); ++idx) {
|
||||
if (idx == _selector && mode != RENDER_ALL)
|
||||
color = COMMAND_HIGHLIGHTED;
|
||||
else
|
||||
color = INFO_TOP;
|
||||
|
||||
if (mode == RENDER_NAMES_AND_SCROLLBAR)
|
||||
_surface.fillRect(Common::Rect(4, yp, _surface.width() - BUTTON_SIZE - 9, yp + _surface.fontHeight()), TRANSPARENCY);
|
||||
|
||||
Common::String numStr = Common::String::format("%d.", idx + 1);
|
||||
_surface.writeString(numStr, Common::Point(_surface.widestChar(), yp), color);
|
||||
_surface.writeString(_savegames[idx], Common::Point(xp, yp), color);
|
||||
|
||||
yp += _surface.fontHeight() + 1;
|
||||
}
|
||||
|
||||
// Draw the Scrollbar if necessary
|
||||
if (mode != RENDER_NAMES)
|
||||
drawScrollBar(_savegameIndex, FILES_LINES_COUNT, _savegames.size());
|
||||
}
|
||||
|
||||
void WidgetFiles::handleEvents() {
|
||||
Events &events = *_vm->_events;
|
||||
TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
|
||||
Common::Point mousePos = events.mousePos();
|
||||
Common::CustomEventType action = ui._action;
|
||||
|
||||
// Handle scrollbar events
|
||||
ScrollHighlight oldHighlight = ui._scrollHighlight;
|
||||
handleScrollbarEvents(_savegameIndex, FILES_LINES_COUNT, _savegames.size());
|
||||
|
||||
int oldScrollIndex = _savegameIndex;
|
||||
handleScrolling(_savegameIndex, FILES_LINES_COUNT, _savegames.size());
|
||||
|
||||
// See if the mouse is pointing at any filenames in the window
|
||||
if (Common::Rect(_bounds.left, _bounds.top + _surface.fontHeight() + 14,
|
||||
_bounds.right - BUTTON_SIZE - 5, _bounds.bottom - 5).contains(mousePos)) {
|
||||
_selector = (mousePos.y - _bounds.top - _surface.fontHeight() - 14) / (_surface.fontHeight() + 1) +
|
||||
_savegameIndex;
|
||||
} else {
|
||||
_selector = -1;
|
||||
}
|
||||
|
||||
// Check for the slot change actions
|
||||
if (action == kActionTattooFilesNextSlot || action == kActionTattooFilesNextPageSlot) {
|
||||
// If the mouse is not over any of the filenames, move the mouse so that it points to the first one
|
||||
if (_selector == -1) {
|
||||
events.warpMouse(Common::Point(_bounds.right - BUTTON_SIZE - 20,
|
||||
_bounds.top + _surface.fontHeight() * 2 + 8));
|
||||
} else {
|
||||
// See if we're doing Next Page Slot action
|
||||
if (action == kActionTattooFilesNextPageSlot) {
|
||||
// We're doing Shift Tab
|
||||
if (_selector == _savegameIndex)
|
||||
_selector = _savegameIndex + 4;
|
||||
else
|
||||
--_selector;
|
||||
} else {
|
||||
// We're doing Next Slot action
|
||||
++_selector;
|
||||
if (_selector >= _savegameIndex + 5)
|
||||
_selector = _savegameIndex;
|
||||
}
|
||||
|
||||
events.warpMouse(Common::Point(mousePos.x, _bounds.top + _surface.fontHeight() * 2
|
||||
+ 8 + (_selector - _savegameIndex) * (_surface.fontHeight() + 1)));
|
||||
}
|
||||
}
|
||||
|
||||
// Only redraw the window if the scrollbar position has changed
|
||||
if (ui._scrollHighlight != oldHighlight || oldScrollIndex != _savegameIndex || _selector != _oldSelector)
|
||||
render(RENDER_NAMES_AND_SCROLLBAR);
|
||||
_oldSelector = _selector;
|
||||
|
||||
if (events._firstPress && !_bounds.contains(mousePos))
|
||||
_outsideMenu = true;
|
||||
|
||||
if (events._released || events._rightReleased || action == kActionTattooFilesSelect) {
|
||||
ui._scrollHighlight = SH_NONE;
|
||||
|
||||
Common::Keymapper *keymapper = g_system->getEventManager()->getKeymapper();
|
||||
|
||||
if (_outsideMenu && !_bounds.contains(mousePos)) {
|
||||
keymapper->getKeymap("tattoo-scrolling")->setEnabled(false);
|
||||
keymapper->getKeymap("tattoo-files")->setEnabled(false);
|
||||
keymapper->getKeymap("tattoo")->setEnabled(true);
|
||||
close();
|
||||
} else {
|
||||
_outsideMenu = false;
|
||||
|
||||
if (_selector != -1) {
|
||||
if (_fileMode == SAVEMODE_LOAD) {
|
||||
// We're in Load Mode
|
||||
_vm->loadGameState(_selector);
|
||||
} else if (_fileMode == SAVEMODE_SAVE) {
|
||||
keymapper->getKeymap("tattoo-scrolling")->setEnabled(false);
|
||||
keymapper->getKeymap("tattoo-files")->setEnabled(false);
|
||||
keymapper->getKeymap("tattoo-files-name")->setEnabled(true);
|
||||
keymapper->getKeymap("tattoo-exit")->setEnabled(true);
|
||||
// We're in Save Mode
|
||||
if (getFilename())
|
||||
_vm->saveGameState(_selector, _savegames[_selector]);
|
||||
|
||||
keymapper->getKeymap("tattoo-files-name")->setEnabled(false);
|
||||
keymapper->getKeymap("tattoo-exit")->setEnabled(false);
|
||||
keymapper->getKeymap("tattoo")->setEnabled(true);
|
||||
close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool WidgetFiles::getFilename() {
|
||||
Events &events = *_vm->_events;
|
||||
TattooScene &scene = *(TattooScene *)_vm->_scene;
|
||||
Screen &screen = *_vm->_screen;
|
||||
Talk &talk = *_vm->_talk;
|
||||
int index = 0;
|
||||
int done = 0;
|
||||
bool blinkFlag = false;
|
||||
int blinkCountdown = 0;
|
||||
int cursorColor = 192;
|
||||
byte color, textColor;
|
||||
bool insert = true;
|
||||
|
||||
assert(_selector != -1);
|
||||
Common::Point pt(_surface.stringWidth("00.") + _surface.widestChar() + 5,
|
||||
_surface.fontHeight() + 14 + (_selector - _savegameIndex) * (_surface.fontHeight() + 1));
|
||||
|
||||
Common::String numStr = Common::String::format("%d.", _selector + 1);
|
||||
_surface.writeString(numStr, Common::Point(_surface.widestChar(), pt.y), COMMAND_HIGHLIGHTED);
|
||||
|
||||
Common::String filename = _savegames[_selector];
|
||||
|
||||
if (isSlotEmpty(_selector)) {
|
||||
index = 0;
|
||||
_surface.fillRect(Common::Rect(pt.x, pt.y, _bounds.width() - BUTTON_SIZE - 9, pt.y + _surface.fontHeight()), TRANSPARENCY);
|
||||
filename = "";
|
||||
} else {
|
||||
index = filename.size();
|
||||
_surface.writeString(filename, pt, COMMAND_HIGHLIGHTED);
|
||||
pt.x = _surface.stringWidth("00.") + _surface.stringWidth(filename) + _surface.widestChar() + 5;
|
||||
}
|
||||
|
||||
do {
|
||||
scene.doBgAnim();
|
||||
|
||||
if (talk._talkToAbort)
|
||||
return false;
|
||||
|
||||
char currentChar = (index == (int)filename.size()) ? ' ' : filename[index];
|
||||
Common::String charString = Common::String::format("%c", currentChar);
|
||||
int width = screen.charWidth(currentChar);
|
||||
|
||||
// Wait for keypress or action
|
||||
while (!events.kbHit() && !events.actionHit()) {
|
||||
events.pollEventsAndWait();
|
||||
events.setButtonState();
|
||||
|
||||
scene.doBgAnim();
|
||||
|
||||
if (talk._talkToAbort)
|
||||
return false;
|
||||
|
||||
if (--blinkCountdown <= 0) {
|
||||
blinkCountdown = 3;
|
||||
blinkFlag = !blinkFlag;
|
||||
if (blinkFlag) {
|
||||
textColor = 236;
|
||||
color = cursorColor;
|
||||
} else {
|
||||
textColor = COMMAND_HIGHLIGHTED;
|
||||
color = TRANSPARENCY;
|
||||
}
|
||||
|
||||
_surface.fillRect(Common::Rect(pt.x, pt.y, pt.x + width, pt.y + _surface.fontHeight()), color);
|
||||
if (currentChar != ' ')
|
||||
_surface.writeString(charString, pt, textColor);
|
||||
}
|
||||
if (_vm->shouldQuit())
|
||||
return false;
|
||||
}
|
||||
|
||||
while (events.kbHit()) {
|
||||
Common::KeyState keyState = events.getKey();
|
||||
if (keyState.keycode == Common::KEYCODE_BACKSPACE && index > 0) {
|
||||
pt.x -= _surface.charWidth(filename[index - 1]);
|
||||
--index;
|
||||
|
||||
if (insert) {
|
||||
filename.deleteChar(index);
|
||||
} else {
|
||||
filename.setChar(' ', index);
|
||||
}
|
||||
|
||||
_surface.fillRect(Common::Rect(pt.x, pt.y, _surface.width() - BUTTON_SIZE - 9, pt.y + _surface.fontHeight()), TRANSPARENCY);
|
||||
_surface.writeString(filename.c_str() + index, pt, COMMAND_HIGHLIGHTED);
|
||||
} else if (keyState.keycode == Common::KEYCODE_DELETE && index < (int)filename.size()) {
|
||||
filename.deleteChar(index);
|
||||
|
||||
_surface.fillRect(Common::Rect(pt.x, pt.y, _bounds.width() - BUTTON_SIZE - 9, pt.y + _surface.fontHeight()), TRANSPARENCY);
|
||||
_surface.writeString(Common::String(filename.c_str() + index), pt, COMMAND_HIGHLIGHTED);
|
||||
} else if (keyState.keycode == Common::KEYCODE_RETURN) {
|
||||
done = 1;
|
||||
}
|
||||
|
||||
if ((keyState.ascii >= ' ') && (keyState.ascii <= 'z') && (index < 50)) {
|
||||
if (pt.x + _surface.charWidth(keyState.ascii) < _surface.w - BUTTON_SIZE - 20) {
|
||||
if (insert)
|
||||
filename.insertChar(keyState.ascii, index);
|
||||
else
|
||||
filename.setChar(keyState.ascii, index);
|
||||
|
||||
_surface.fillRect(Common::Rect(pt.x, pt.y, _bounds.width() - BUTTON_SIZE - 9,
|
||||
pt.y + _surface.fontHeight()), TRANSPARENCY);
|
||||
_surface.writeString(filename.c_str() + index, pt, COMMAND_HIGHLIGHTED);
|
||||
pt.x += _surface.charWidth(keyState.ascii);
|
||||
++index;
|
||||
}
|
||||
}
|
||||
|
||||
currentChar = (index == (int)filename.size()) ? ' ' : filename[index];
|
||||
charString = Common::String::format("%c", currentChar);
|
||||
width = screen.charWidth(currentChar);
|
||||
}
|
||||
|
||||
while (events.actionHit()) {
|
||||
Common::CustomEventType action = events.getAction();
|
||||
|
||||
if ((action == kActionTattooFilesNameLeft && index > 0)
|
||||
|| (action == kActionTattooFilesNameRight && index < (int)filename.size() && pt.x < (_bounds.right - BUTTON_SIZE - 20))
|
||||
|| (action == kActionTattooFilesNameStart && index > 0)
|
||||
|| (action == kActionTattooFilesNameEnd)) {
|
||||
_surface.fillRect(Common::Rect(pt.x, pt.y, pt.x + width, pt.y + _surface.fontHeight()), TRANSPARENCY);
|
||||
if (currentChar)
|
||||
_surface.writeString(charString, pt, COMMAND_HIGHLIGHTED);
|
||||
|
||||
switch (action) {
|
||||
case kActionTattooFilesNameLeft:
|
||||
pt.x -= _surface.charWidth(filename[index - 1]);
|
||||
--index;
|
||||
break;
|
||||
|
||||
case kActionTattooFilesNameRight:
|
||||
pt.x += _surface.charWidth(filename[index]);
|
||||
++index;
|
||||
break;
|
||||
|
||||
case kActionTattooFilesNameStart:
|
||||
pt.x = _surface.stringWidth("00.") + _surface.widestChar() + 5;
|
||||
index = 0;
|
||||
break;
|
||||
|
||||
case kActionTattooFilesNameEnd:
|
||||
pt.x = _surface.stringWidth("00.") + _surface.stringWidth(filename) + _surface.widestChar() + 5;
|
||||
index = filename.size();
|
||||
|
||||
while (filename[index - 1] == ' ' && index > 0) {
|
||||
pt.x -= _surface.charWidth(filename[index - 1]);
|
||||
--index;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if (action == kActionTattooFilesNameToggleInsertMode) {
|
||||
insert = !insert;
|
||||
if (insert)
|
||||
cursorColor = 192;
|
||||
else
|
||||
cursorColor = 200;
|
||||
} else if (action == kActionTattooExit) {
|
||||
_selector = -1;
|
||||
render(RENDER_NAMES_AND_SCROLLBAR);
|
||||
done = -1;
|
||||
}
|
||||
|
||||
currentChar = (index == (int)filename.size()) ? ' ' : filename[index];
|
||||
charString = Common::String::format("%c", currentChar);
|
||||
width = screen.charWidth(currentChar);
|
||||
}
|
||||
|
||||
} while (!done && !_vm->shouldQuit());
|
||||
|
||||
scene.doBgAnim();
|
||||
|
||||
if (talk._talkToAbort)
|
||||
return false;
|
||||
|
||||
if (done == 1)
|
||||
_savegames[_selector] = filename;
|
||||
|
||||
return done == 1;
|
||||
}
|
||||
|
||||
Common::Rect WidgetFiles::getScrollBarBounds() const {
|
||||
Common::Rect scrollRect(BUTTON_SIZE, _bounds.height() - _surface.fontHeight() - 13);
|
||||
scrollRect.moveTo(_bounds.width() - BUTTON_SIZE - 3, _surface.fontHeight() + 10);
|
||||
|
||||
return scrollRect;
|
||||
}
|
||||
|
||||
} // End of namespace Tattoo
|
||||
|
||||
} // End of namespace Sherlock
|
||||
85
engines/sherlock/tattoo/widget_files.h
Normal file
85
engines/sherlock/tattoo/widget_files.h
Normal file
@@ -0,0 +1,85 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SHERLOCK_TATTOO_WIDGET_FILES_H
|
||||
#define SHERLOCK_TATTOO_WIDGET_FILES_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "sherlock/tattoo/widget_base.h"
|
||||
#include "sherlock/saveload.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
class SherlockEngine;
|
||||
|
||||
namespace Tattoo {
|
||||
|
||||
enum FilesRenderMode { RENDER_ALL, RENDER_NAMES, RENDER_NAMES_AND_SCROLLBAR };
|
||||
|
||||
class WidgetFiles: public WidgetBase, public SaveManager {
|
||||
private:
|
||||
SherlockEngine *_vm;
|
||||
SaveMode _fileMode;
|
||||
int _selector, _oldSelector;
|
||||
|
||||
/**
|
||||
* Render the dialog
|
||||
*/
|
||||
void render(FilesRenderMode mode);
|
||||
|
||||
/**
|
||||
* Show the ScummVM Save Game dialog
|
||||
*/
|
||||
void showScummVMSaveDialog();
|
||||
|
||||
/**
|
||||
* Show the ScummVM Load Game dialog
|
||||
*/
|
||||
void showScummVMRestoreDialog();
|
||||
|
||||
/**
|
||||
* Prompt the user for a savegame name in the currently selected slot
|
||||
*/
|
||||
bool getFilename();
|
||||
|
||||
/**
|
||||
* Return the area of a widget that the scrollbar will be drawn in
|
||||
*/
|
||||
Common::Rect getScrollBarBounds() const override;
|
||||
public:
|
||||
WidgetFiles(SherlockEngine *vm, const Common::String &target);
|
||||
|
||||
/**
|
||||
* Prompt the user whether to quit
|
||||
*/
|
||||
void show(SaveMode mode);
|
||||
|
||||
/**
|
||||
* Handle event processing
|
||||
*/
|
||||
void handleEvents() override;
|
||||
};
|
||||
|
||||
} // End of namespace Tattoo
|
||||
|
||||
} // End of namespace Sherlock
|
||||
|
||||
#endif
|
||||
321
engines/sherlock/tattoo/widget_foolscap.cpp
Normal file
321
engines/sherlock/tattoo/widget_foolscap.cpp
Normal file
@@ -0,0 +1,321 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sherlock/tattoo/widget_foolscap.h"
|
||||
#include "sherlock/tattoo/tattoo_fixed_text.h"
|
||||
#include "sherlock/tattoo/tattoo_scene.h"
|
||||
#include "sherlock/tattoo/tattoo_user_interface.h"
|
||||
#include "sherlock/tattoo/tattoo.h"
|
||||
|
||||
#include "backends/keymapper/keymapper.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
namespace Tattoo {
|
||||
|
||||
WidgetFoolscap::WidgetFoolscap(TattooEngine *vm) : WidgetBase(vm) {
|
||||
for (int idx = 0; idx < 3; ++idx) {
|
||||
Common::fill(&_answers[idx][0], &_answers[idx][10], 0);
|
||||
_solutions[idx] = nullptr;
|
||||
}
|
||||
_images = nullptr;
|
||||
_numWide = 0;
|
||||
_spacing = 0;
|
||||
_blinkFlag = false;
|
||||
_blinkCounter = 0;
|
||||
_lineNum = _charNum = 0;
|
||||
_solved = false;
|
||||
}
|
||||
|
||||
WidgetFoolscap::~WidgetFoolscap() {
|
||||
delete _images;
|
||||
}
|
||||
|
||||
void WidgetFoolscap::show() {
|
||||
Screen &screen = *_vm->_screen;
|
||||
TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
|
||||
|
||||
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;
|
||||
_images = 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;
|
||||
_images = 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;
|
||||
_images = new ImageFile("paper.vgs");
|
||||
break;
|
||||
}
|
||||
|
||||
_solved = false;
|
||||
_blinkFlag = false;
|
||||
_blinkCounter = 0;
|
||||
_lineNum = _charNum = 0;
|
||||
_cursorPos = Common::Point(_lines[0].x + 8 - screen.widestChar() / 2, _lines[0].y - screen.fontHeight() - 2);
|
||||
|
||||
// Set up window bounds
|
||||
ImageFrame &paperFrame = (*_images)[0];
|
||||
_bounds = Common::Rect(paperFrame._width, paperFrame._height);
|
||||
_bounds.moveTo(screen._currentScroll.x + (SHERLOCK_SCREEN_WIDTH - paperFrame._width) / 2,
|
||||
(SHERLOCK_SCREEN_HEIGHT - paperFrame._height) / 2);
|
||||
|
||||
// Clear answer data and set correct solution strings
|
||||
for (int idx = 0; idx < 3; ++idx)
|
||||
Common::fill(&_answers[idx][0], &_answers[idx][10], 0);
|
||||
_solutions[0] = FIXED(Apply);
|
||||
_solutions[1] = FIXED(Water);
|
||||
_solutions[2] = FIXED(Heat);
|
||||
|
||||
// Set up the window background
|
||||
_surface.create(_bounds.width(), _bounds.height());
|
||||
_surface.SHblitFrom(paperFrame, Common::Point(0, 0));
|
||||
|
||||
// If they have already solved the puzzle, put the answer on the graphic
|
||||
if (_vm->readFlags(299)) {
|
||||
Common::Point cursorPos;
|
||||
for (int line = 0; line < 3; ++line) {
|
||||
cursorPos.y = _lines[line].y - screen.fontHeight() - 2;
|
||||
|
||||
for (uint idx = 0; idx < strlen(_solutions[line]); ++idx) {
|
||||
cursorPos.x = _lines[line].x + 8 - screen.widestChar() / 2 + idx * _spacing;
|
||||
char c = _solutions[line][idx];
|
||||
|
||||
Common::String str = Common::String::format("%c", c);
|
||||
_surface.writeString(str, Common::Point(cursorPos.x + screen.widestChar() / 2
|
||||
- screen.charWidth(c) / 2, cursorPos.y), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Show the window
|
||||
summonWindow();
|
||||
ui._menuMode = FOOLSCAP_MODE;
|
||||
|
||||
Common::Keymapper *keymapper = g_system->getEventManager()->getKeymapper();
|
||||
keymapper->getKeymap("tattoo")->setEnabled(false);
|
||||
keymapper->getKeymap("tattoo-foolscap")->setEnabled(true);
|
||||
}
|
||||
|
||||
void WidgetFoolscap::handleEvents() {
|
||||
Events &events = *_vm->_events;
|
||||
Screen &screen = *_vm->_screen;
|
||||
TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
|
||||
Common::Point mousePos = events.mousePos();
|
||||
byte cursorColor = 254;
|
||||
|
||||
if (events._firstPress && !_bounds.contains(mousePos))
|
||||
_outsideMenu = true;
|
||||
|
||||
// If they have not solved the puzzle, let them solve it here
|
||||
if (!_vm->readFlags(299)) {
|
||||
if (!ui._keyState.keycode && !ui._action) {
|
||||
if (--_blinkCounter < 0) {
|
||||
_blinkCounter = 3;
|
||||
_blinkFlag = !_blinkFlag;
|
||||
|
||||
if (_blinkFlag) {
|
||||
// Draw the caret
|
||||
_surface.fillRect(Common::Rect(_cursorPos.x, _cursorPos.y, _cursorPos.x + screen.widestChar() - 1,
|
||||
_cursorPos.y + screen.fontHeight() - 1), cursorColor);
|
||||
|
||||
if (_answers[_lineNum][_charNum]) {
|
||||
Common::String str = Common::String::format("%c", _answers[_lineNum][_charNum]);
|
||||
_surface.writeString(str, Common::Point(_cursorPos.x + screen.widestChar() / 2
|
||||
- screen.charWidth(_answers[_lineNum][_charNum]) / 2, _cursorPos.y), 0);
|
||||
}
|
||||
} else {
|
||||
// Restore background
|
||||
restoreChar();
|
||||
|
||||
// Draw the character at that position if there is one
|
||||
if (_answers[_lineNum][_charNum]) {
|
||||
Common::String str = Common::String::format("%c", _answers[_lineNum][_charNum]);
|
||||
_surface.writeString(str, Common::Point(_cursorPos.x + screen.widestChar() / 2
|
||||
- screen.charWidth(_answers[_lineNum][_charNum]) / 2, _cursorPos.y), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Handle keyboard and action events
|
||||
handleKeyboardEvents();
|
||||
}
|
||||
}
|
||||
|
||||
if ((events._released || events._rightReleased) && _outsideMenu && !_bounds.contains(mousePos)) {
|
||||
// Clicked outside window to close it
|
||||
events.clearEvents();
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
void WidgetFoolscap::handleKeyboardEvents() {
|
||||
Screen &screen = *_vm->_screen;
|
||||
TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
|
||||
Common::KeyState keyState = ui._keyState;
|
||||
Common::CustomEventType action = ui._action;
|
||||
|
||||
if ((toupper(keyState.ascii) >= 'A') && (toupper(keyState.ascii) <= 'Z')) {
|
||||
// Visible key pressed, set it and set the keycode to move the caret to the right
|
||||
_answers[_lineNum][_charNum] = keyState.ascii;
|
||||
action = kActionTattooFoolscapRight;
|
||||
}
|
||||
|
||||
// Restore background
|
||||
restoreChar();
|
||||
|
||||
if (_answers[_lineNum][_charNum]) {
|
||||
Common::String str = Common::String::format("%c", _answers[_lineNum][_charNum]);
|
||||
_surface.writeString(str, Common::Point(_cursorPos.x + screen.widestChar() / 2
|
||||
- screen.charWidth(_answers[_lineNum][_charNum]) / 2, _cursorPos.y), 0);
|
||||
}
|
||||
|
||||
switch (keyState.keycode) {
|
||||
case Common::KEYCODE_BACKSPACE:
|
||||
if (_charNum)
|
||||
--_charNum;
|
||||
else if (_lineNum) {
|
||||
--_lineNum;
|
||||
|
||||
_charNum = strlen(_solutions[_lineNum]) - 1;
|
||||
}
|
||||
|
||||
_answers[_lineNum][_charNum] = ' ';
|
||||
break;
|
||||
|
||||
case Common::KEYCODE_DELETE:
|
||||
_answers[_lineNum][_charNum] = ' ';
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (action) {
|
||||
case kActionTattooFoolscapExit:
|
||||
close();
|
||||
break;
|
||||
|
||||
case kActionTattooFoolscapUp:
|
||||
if (_lineNum) {
|
||||
--_lineNum;
|
||||
if (_charNum >= (int)strlen(_solutions[_lineNum]))
|
||||
_charNum = (int)strlen(_solutions[_lineNum]) - 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case kActionTattooFoolscapDown:
|
||||
if (_lineNum < 2) {
|
||||
++_lineNum;
|
||||
if (_charNum >= (int)strlen(_solutions[_lineNum]))
|
||||
_charNum = (int)strlen(_solutions[_lineNum]) - 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case kActionTattooFoolscapLeft:
|
||||
if (_charNum)
|
||||
--_charNum;
|
||||
else if (_lineNum) {
|
||||
--_lineNum;
|
||||
|
||||
_charNum = strlen(_solutions[_lineNum]) - 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case kActionTattooFoolscapRight:
|
||||
if (_charNum < (int)strlen(_solutions[_lineNum]) - 1)
|
||||
++_charNum;
|
||||
else if (_lineNum < 2) {
|
||||
++_lineNum;
|
||||
_charNum = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
_cursorPos.x = _lines[_lineNum].x + 8 - screen.widestChar() / 2 + _charNum * _spacing;
|
||||
_cursorPos.y = _lines[_lineNum].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])) {
|
||||
_solved = true;
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
void WidgetFoolscap::restoreChar() {
|
||||
Screen &screen = *_vm->_screen;
|
||||
ImageFrame &bgFrame = (*_images)[0];
|
||||
_surface.SHblitFrom(bgFrame, _cursorPos, Common::Rect(_cursorPos.x, _cursorPos.y,
|
||||
_cursorPos.x + screen.widestChar(), _cursorPos.y + screen.fontHeight()));
|
||||
}
|
||||
|
||||
void WidgetFoolscap::close() {
|
||||
TattooScene &scene = *(TattooScene *)_vm->_scene;
|
||||
Talk &talk = *_vm->_talk;
|
||||
TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
|
||||
delete _images;
|
||||
_images = nullptr;
|
||||
|
||||
Common::Keymapper *keymapper = g_system->getEventManager()->getKeymapper();
|
||||
keymapper->getKeymap("tattoo-foolscap")->setEnabled(false);
|
||||
keymapper->getKeymap("tattoo")->setEnabled(true);
|
||||
|
||||
// Close the window
|
||||
banishWindow();
|
||||
ui._menuMode = scene._labTableScene ? LAB_MODE : STD_MODE;
|
||||
|
||||
// Don't call the talk files if the puzzle has already been solved
|
||||
if (!_vm->readFlags(299)) {
|
||||
// Run the appropriate script depending on whether or not they solved the puzzle correctly
|
||||
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
|
||||
81
engines/sherlock/tattoo/widget_foolscap.h
Normal file
81
engines/sherlock/tattoo/widget_foolscap.h
Normal file
@@ -0,0 +1,81 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SHERLOCK_TATTOO_FOOLSCAP_H
|
||||
#define SHERLOCK_TATTOO_FOOLSCAP_H
|
||||
|
||||
#include "sherlock/tattoo/widget_base.h"
|
||||
#include "sherlock/image_file.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
namespace Tattoo {
|
||||
|
||||
class TattooEngine;
|
||||
|
||||
class WidgetFoolscap: public WidgetBase {
|
||||
private:
|
||||
ImageFile *_images;
|
||||
Common::Point _lines[3];
|
||||
char _answers[3][10];
|
||||
const char *_solutions[3];
|
||||
int _numWide;
|
||||
int _spacing;
|
||||
Common::Point _cursorPos;
|
||||
int _blinkCounter;
|
||||
bool _blinkFlag;
|
||||
int _lineNum, _charNum;
|
||||
bool _solved;
|
||||
|
||||
/**
|
||||
* Handle keyboard events
|
||||
*/
|
||||
void handleKeyboardEvents();
|
||||
|
||||
/**
|
||||
* Restore the background for the current line/horiz position
|
||||
*/
|
||||
void restoreChar();
|
||||
public:
|
||||
WidgetFoolscap(TattooEngine *vm);
|
||||
~WidgetFoolscap() override;
|
||||
|
||||
/**
|
||||
* Show the foolscap puzzle
|
||||
*/
|
||||
void show();
|
||||
|
||||
/**
|
||||
* Close the window
|
||||
*/
|
||||
void close();
|
||||
|
||||
/**
|
||||
* Handle events whilst the widget is on-screen
|
||||
*/
|
||||
void handleEvents() override;
|
||||
};
|
||||
|
||||
} // End of namespace Tattoo
|
||||
|
||||
} // End of namespace Sherlock
|
||||
|
||||
#endif
|
||||
802
engines/sherlock/tattoo/widget_inventory.cpp
Normal file
802
engines/sherlock/tattoo/widget_inventory.cpp
Normal file
@@ -0,0 +1,802 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sherlock/tattoo/widget_inventory.h"
|
||||
#include "sherlock/tattoo/tattoo_fixed_text.h"
|
||||
#include "sherlock/tattoo/tattoo_people.h"
|
||||
#include "sherlock/tattoo/tattoo_scene.h"
|
||||
#include "sherlock/tattoo/tattoo_user_interface.h"
|
||||
#include "sherlock/tattoo/tattoo.h"
|
||||
|
||||
#include "backends/keymapper/keymapper.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
namespace Tattoo {
|
||||
|
||||
#define INVENTORY_XSIZE 70 // Width of the box that surrounds inventory items
|
||||
#define INVENTORY_YSIZE 70 // Height of the box that surrounds inventory items
|
||||
#define MAX_INV_COMMANDS 10 // Maximum elements in dialog
|
||||
#define NUM_INV_PER_LINE 4 // Number of inentory items per line in the dialog
|
||||
|
||||
WidgetInventoryTooltip::WidgetInventoryTooltip(SherlockEngine *vm, WidgetInventory *owner) :
|
||||
WidgetTooltipBase(vm), _owner(owner) {
|
||||
}
|
||||
|
||||
void WidgetInventoryTooltip::setText(const Common::String &str) {
|
||||
// If no text specified, erase any previously displayed tooltip and free its surface
|
||||
if (str.empty()) {
|
||||
erase();
|
||||
_surface.free();
|
||||
return;
|
||||
}
|
||||
|
||||
int width = _surface.stringWidth(str) + 2;
|
||||
int height = 0;
|
||||
Common::String line1 = str, line2;
|
||||
|
||||
// See if we need to split it into two lines
|
||||
if (width > 150) {
|
||||
// Yes, we do
|
||||
const char *s = str.c_str();
|
||||
const char *space = nullptr;
|
||||
int dif = 10000;
|
||||
|
||||
while (*s) {
|
||||
s = strchr(s, ' ');
|
||||
|
||||
if (!s) {
|
||||
if (!space) {
|
||||
height = _surface.stringHeight(str) + 2;
|
||||
} else {
|
||||
line1 = Common::String(str.c_str(), space);
|
||||
line2 = Common::String(space + 1);
|
||||
height = _surface.stringHeight(line1) + _surface.stringHeight(line2) + 4;
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
line1 = Common::String(str.c_str(), s);
|
||||
line2 = Common::String(s + 1);
|
||||
int width1 = _surface.stringWidth(line1);
|
||||
int width2 = _surface.stringWidth(line2);
|
||||
|
||||
if (ABS(width1 - width2) < dif) {
|
||||
// Found a split point that results in less overall width
|
||||
space = s;
|
||||
dif = ABS(width1 - width2);
|
||||
width = MAX(width1, width2);
|
||||
}
|
||||
|
||||
s++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
height = _surface.stringHeight(str) + 2;
|
||||
}
|
||||
|
||||
// Allocate a fresh surface for the new string
|
||||
_bounds = Common::Rect(width, height);
|
||||
_surface.create(width, height);
|
||||
_surface.clear(TRANSPARENCY);
|
||||
|
||||
if (line2.empty()) {
|
||||
_surface.writeFancyString(str, Common::Point(0, 0), BLACK, INFO_TOP);
|
||||
} else {
|
||||
int xp, yp;
|
||||
|
||||
xp = (_bounds.width() - _surface.stringWidth(line1) - 2) / 2;
|
||||
_surface.writeFancyString(line1, Common::Point(xp, 0), BLACK, INFO_TOP);
|
||||
|
||||
xp = (_bounds.width() - _surface.stringWidth(line2) - 2) / 2;
|
||||
yp = _surface.stringHeight(line2) + 2;
|
||||
_surface.writeFancyString(line2, Common::Point(xp, yp), BLACK, INFO_TOP);
|
||||
}
|
||||
}
|
||||
|
||||
void WidgetInventoryTooltip::handleEvents() {
|
||||
Events &events = *_vm->_events;
|
||||
FixedText &fixedText = *_vm->_fixedText;
|
||||
Inventory &inv = *_vm->_inventory;
|
||||
TattooPeople &people = *(TattooPeople *)_vm->_people;
|
||||
Scene &scene = *_vm->_scene;
|
||||
TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
|
||||
Common::Point mousePos = events.mousePos();
|
||||
Common::String str;
|
||||
int select = -1, oldSelect = 999;
|
||||
Common::String strWith = fixedText.getText(kFixedText_With);
|
||||
Common::String strUse = fixedText.getText(kFixedText_Use);
|
||||
|
||||
// Register the tooltip for requiring post-rendering drawing, since we draw directly to the screen if a scene
|
||||
// mask is active, since the initial draw to the screen will be covered by the mask rendering
|
||||
if (ui._mask) {
|
||||
ui._postRenderWidgets.push_back(this);
|
||||
}
|
||||
|
||||
// If we are using an inventory item on an object in the room, display the appropriate text above the mouse cursor
|
||||
if (_owner->_invVerbMode == 3) {
|
||||
select = ui._bgFound;
|
||||
oldSelect = ui._oldBgFound;
|
||||
|
||||
if (select != -1 && (select != oldSelect || (select != -1 && _surface.empty()))) {
|
||||
// See if we're pointing at a shape or a sprite
|
||||
if (select < 1000) {
|
||||
Object &obj = scene._bgShapes[select];
|
||||
|
||||
if (!obj._description.empty() && !obj._description.hasPrefix(" ")) {
|
||||
if (_vm->getLanguage() == Common::EL_GRC) {
|
||||
|
||||
if (!_owner->_swapItems)
|
||||
str = Common::String::format("%s %s %s %s", _owner->_action.c_str(), obj._description.c_str(),
|
||||
inv[_owner->_invSelect]._name.c_str(), _owner->_verb.c_str());
|
||||
else
|
||||
str = Common::String::format("%s %s %s %s", _owner->_action.c_str(), inv[_owner->_invSelect]._name.c_str(),
|
||||
obj._description.c_str(), _owner->_verb.c_str());
|
||||
} else {
|
||||
if (_owner->_swapItems)
|
||||
str = Common::String::format("%s %s %s %s", _owner->_verb.c_str(), obj._description.c_str(), _owner->_action.c_str(),
|
||||
inv[_owner->_invSelect]._name.c_str());
|
||||
else
|
||||
str = Common::String::format("%s %s %s %s", _owner->_verb.c_str(), inv[_owner->_invSelect]._name.c_str(),
|
||||
_owner->_action.c_str(), obj._description.c_str());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Person &person = people[ui._bgFound - 1000];
|
||||
|
||||
if (!person._description.empty() && !person._description.hasPrefix(" ")) {
|
||||
if (_vm->getLanguage() == Common::EL_GRC) {
|
||||
if (!_owner->_swapItems)
|
||||
str = Common::String::format("%s %s %s %s", _owner->_action.c_str(), person._description.c_str(),
|
||||
inv[_owner->_invSelect]._name.c_str(), _owner->_verb.c_str());
|
||||
else
|
||||
str = Common::String::format("%s %s %s %s", _owner->_action.c_str(), inv[_owner->_invSelect]._name.c_str(),
|
||||
person._description.c_str(), _owner->_verb.c_str());
|
||||
} else {
|
||||
|
||||
if (_owner->_swapItems)
|
||||
str = Common::String::format("%s %s %s %s", _owner->_verb.c_str(), person._description.c_str(),
|
||||
_owner->_action.c_str(), inv[_owner->_invSelect]._name.c_str());
|
||||
else
|
||||
str = Common::String::format("%s %s %s %s", _owner->_verb.c_str(),
|
||||
inv[_owner->_invSelect]._name.c_str(), _owner->_action.c_str(), person._description.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const Common::Rect &b = _owner->_bounds;
|
||||
Common::Rect r(b.left + 3, b.top + 3, b.right - 3 - BUTTON_SIZE, b.bottom - 3);
|
||||
|
||||
if (r.contains(mousePos)) {
|
||||
select = (mousePos.x - r.left) / (INVENTORY_XSIZE + 3) + NUM_INVENTORY_SHOWN / 2 *
|
||||
((mousePos.y - r.top) / (INVENTORY_YSIZE + 3)) + inv._invIndex;
|
||||
|
||||
if (select >= inv._holdings) {
|
||||
select = -1;
|
||||
} else {
|
||||
oldSelect = _owner->_invSelect;
|
||||
|
||||
if (select != _owner->_invSelect || _surface.empty()) {
|
||||
|
||||
if (_owner->_invMode == 1) {
|
||||
// See if we were pointing at a shapre or sprite
|
||||
if (ui._activeObj < 1000) {
|
||||
Object &obj = scene._bgShapes[ui._activeObj];
|
||||
|
||||
if (!obj._description.empty() && !obj._description.hasPrefix(" "))
|
||||
str = Common::String::format("%s %s %s %s", strUse.c_str(), inv[select]._name.c_str(),
|
||||
strWith.c_str(), obj._description.c_str());
|
||||
} else {
|
||||
Person &person = people[ui._activeObj - 1000];
|
||||
|
||||
if (!person._description.empty() && !person._description.hasPrefix(" "))
|
||||
str = Common::String::format("%s %s %s %s", strUse.c_str(), inv[select]._name.c_str(),
|
||||
strWith.c_str(), person._description.c_str());
|
||||
}
|
||||
} else {
|
||||
if (_owner->_invVerbMode == 2)
|
||||
str = Common::String::format("%s %s %s %s", strUse.c_str(), inv[_owner->_invSelect]._name.c_str(),
|
||||
strWith.c_str(), inv[select]._name.c_str());
|
||||
else
|
||||
str = inv[select]._description.c_str();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// See if they are pointing at a different inventory object and we need to
|
||||
// change the graphics of the Text Tag
|
||||
if (select != oldSelect || (select != -1 && _surface.empty())) {
|
||||
// Set the text
|
||||
setText(str);
|
||||
|
||||
if (_owner->_invVerbMode != 3)
|
||||
_owner->_invSelect = select;
|
||||
else
|
||||
ui._oldBgFound = select;
|
||||
} else if (select == -1 && oldSelect != -1) {
|
||||
setText(Common::String());
|
||||
return;
|
||||
}
|
||||
|
||||
if (_owner->_invVerbMode == 3)
|
||||
// Adjust tooltip to be above the inventory item being shown above the standard cursor
|
||||
mousePos.y -= events._hotspotPos.y;
|
||||
|
||||
// Update the position of the tooltip
|
||||
int xs = CLIP(mousePos.x - _bounds.width() / 2, 0, SHERLOCK_SCENE_WIDTH - _bounds.width());
|
||||
int ys = CLIP(mousePos.y - _bounds.height(), 0, SHERLOCK_SCREEN_HEIGHT - _bounds.height());
|
||||
_bounds.moveTo(xs, ys);
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------*/
|
||||
|
||||
WidgetInventoryVerbs::WidgetInventoryVerbs(SherlockEngine *vm, WidgetInventory *owner) :
|
||||
WidgetBase(vm), _owner(owner) {
|
||||
_invVerbSelect = _oldInvVerbSelect = -1;
|
||||
}
|
||||
|
||||
void WidgetInventoryVerbs::load() {
|
||||
Events &events = *_vm->_events;
|
||||
Inventory &inv = *_vm->_inventory;
|
||||
People &people = *_vm->_people;
|
||||
Scene &scene = *_vm->_scene;
|
||||
TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
|
||||
Common::Point mousePos = events.mousePos();
|
||||
|
||||
// Make the Verb List for this Inventory Item
|
||||
_inventCommands.clear();
|
||||
_inventCommands.push_back(FIXED(Look));
|
||||
|
||||
// Default the Action word to "with"
|
||||
_owner->_action = _vm->getLanguage() == Common::EL_GRC ? "" : FIXED(With);
|
||||
|
||||
// Search all the bgshapes for any matching Target Fields
|
||||
for (uint idx = 0; idx < scene._bgShapes.size(); ++idx) {
|
||||
Object &obj = scene._bgShapes[idx];
|
||||
|
||||
if (obj._type != INVALID && obj._type != HIDDEN) {
|
||||
for (int useNum = 0; useNum < 6; ++useNum) {
|
||||
if (!obj._use[useNum]._verb.hasPrefix("*") &&
|
||||
!obj._use[useNum]._target.compareToIgnoreCase(inv[_owner->_invSelect]._name)) {
|
||||
// Make sure the Verb is not already in the list
|
||||
bool found1 = false;
|
||||
for (uint cmdNum = 0; cmdNum < _inventCommands.size() && !found1; ++cmdNum) {
|
||||
if (!_inventCommands[cmdNum].compareToIgnoreCase(obj._use[useNum]._verb))
|
||||
found1 = true;
|
||||
}
|
||||
|
||||
if (!found1) {
|
||||
_inventCommands.push_back(obj._use[useNum]._verb);
|
||||
|
||||
// Check for any Special Action commands
|
||||
for (int nameNum = 0; nameNum < 4; ++nameNum) {
|
||||
if (!scumm_strnicmp(obj._use[useNum]._names[nameNum].c_str(), "*V", 2)) {
|
||||
if (!scumm_strnicmp(obj._use[useNum]._names[nameNum].c_str(), "*VSWAP", 6))
|
||||
_owner->_swapItems = true;
|
||||
else
|
||||
_owner->_action = Common::String(obj._use[useNum]._names[nameNum].c_str() + 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Search the NPCs for matches as well
|
||||
for (int idx = 1; idx < MAX_CHARACTERS; ++idx) {
|
||||
for (int useNum = 0; useNum < 2; ++useNum) {
|
||||
if (!people[idx]._use[useNum]._target.compareToIgnoreCase(inv[_owner->_invSelect]._name) &&
|
||||
!people[idx]._use[useNum]._verb.empty() && !people[idx]._use[useNum]._verb.hasPrefix(" ")) {
|
||||
bool found1 = false;
|
||||
for (uint cmdNum = 0; cmdNum < _inventCommands.size() && !found1; ++cmdNum) {
|
||||
if (!_inventCommands[cmdNum].compareToIgnoreCase(people[idx]._use[cmdNum]._verb))
|
||||
found1 = true;
|
||||
}
|
||||
|
||||
if (!found1)
|
||||
_inventCommands.push_back(people[idx]._use[useNum]._verb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Finally see if the item itself has a verb
|
||||
if (!inv[_owner->_invSelect]._verb._verb.empty()) {
|
||||
// Don't add "Solve" to the Foolscap if it's already been "Solved"
|
||||
if (inv[_owner->_invSelect]._verb._verb.compareToIgnoreCase(FIXED(Solve)) || !_vm->readFlags(299))
|
||||
_inventCommands.push_back(inv[_owner->_invSelect]._verb._verb);
|
||||
}
|
||||
|
||||
// Now find the widest command in the _inventCommands array
|
||||
int width = 0;
|
||||
for (uint idx = 0; idx < _inventCommands.size(); ++idx)
|
||||
width = MAX(width, _surface.stringWidth(_inventCommands[idx]));
|
||||
|
||||
// Set up bounds for the menu
|
||||
_bounds = Common::Rect(width + _surface.widestChar() * 2 + 6,
|
||||
(_surface.fontHeight() + 7) * _inventCommands.size() + 3);
|
||||
_bounds.moveTo(mousePos.x - _bounds.width() / 2, mousePos.y - _bounds.height() / 2);
|
||||
|
||||
// Create the surface
|
||||
_surface.create(_bounds.width(), _bounds.height());
|
||||
_surface.clear(TRANSPARENCY);
|
||||
makeInfoArea();
|
||||
|
||||
// Draw the Verb commands and the lines separating them
|
||||
ImageFile &images = *ui._interfaceImages;
|
||||
for (int idx = 0; idx < (int)_inventCommands.size(); ++idx) {
|
||||
_surface.writeString(_inventCommands[idx], Common::Point((_bounds.width() -
|
||||
_surface.stringWidth(_inventCommands[idx])) / 2, (_surface.fontHeight() + 7) * idx + 5), INFO_TOP);
|
||||
|
||||
if (idx < (int)_inventCommands.size() - 1) {
|
||||
_surface.vLine(3, (_surface.fontHeight() + 7) * (idx + 1), _bounds.right - 4, INFO_TOP);
|
||||
_surface.vLine(3, (_surface.fontHeight() + 7) * (idx + 1) + 1, _bounds.right - 4, INFO_MIDDLE);
|
||||
_surface.vLine(3, (_surface.fontHeight() + 7) * (idx + 1) + 2, _bounds.right - 4, INFO_BOTTOM);
|
||||
|
||||
_surface.SHtransBlitFrom(images[4], Common::Point(0, (_surface.fontHeight() + 7) * (idx + 1)));
|
||||
_surface.SHtransBlitFrom(images[5], Common::Point(_bounds.width() - images[5]._width,
|
||||
(_surface.fontHeight() + 7) * (idx + 1) - 1));
|
||||
}
|
||||
}
|
||||
|
||||
summonWindow();
|
||||
}
|
||||
|
||||
void WidgetInventoryVerbs::handleEvents() {
|
||||
Events &events = *_vm->_events;
|
||||
Inventory &inv = *_vm->_inventory;
|
||||
TattooScene &scene = *(TattooScene *)_vm->_scene;
|
||||
Common::Point mousePos = events.mousePos();
|
||||
TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
|
||||
TattooEngine &vm = *(TattooEngine *)_vm;
|
||||
|
||||
// Handle changing highlighted verb entry
|
||||
highlightControls();
|
||||
|
||||
// See if they want to close the menu (by clicking outside the menu)
|
||||
Common::Rect innerBounds = _bounds;
|
||||
innerBounds.grow(-3);
|
||||
|
||||
// Flag is they are pressing outside of the menu
|
||||
if (!innerBounds.contains(mousePos))
|
||||
_outsideMenu = true;
|
||||
|
||||
if (events._released || events._rightReleased || ui._action == kActionTattooInvExit) {
|
||||
ui._scrollHighlight = SH_NONE;
|
||||
banishWindow();
|
||||
|
||||
if (_outsideMenu || ui._action == kActionTattooInvExit) {
|
||||
_owner->_invVerbMode = 0;
|
||||
} else if (innerBounds.contains(mousePos)) {
|
||||
_outsideMenu = false;
|
||||
|
||||
// Check if they are trying to solve the Foolscap puzzle, or looking at the completed puzzle
|
||||
bool doFoolscap = !inv[_owner->_invSelect]._name.compareToIgnoreCase(FIXED(Inv6)) &&
|
||||
!_inventCommands[_invVerbSelect].compareToIgnoreCase(FIXED(Solve));
|
||||
doFoolscap |= (!inv[_owner->_invSelect]._name.compareToIgnoreCase(FIXED(Inv6)) || !inv[_owner->_invSelect]._name.compareToIgnoreCase(FIXED(Inv7)))
|
||||
&& !_inventCommands[_invVerbSelect].compareToIgnoreCase(FIXED(Look)) && vm.readFlags(299);
|
||||
|
||||
if (doFoolscap) {
|
||||
// Close the entire Inventory and return to Standard Mode
|
||||
_owner->_invVerbMode = 0;
|
||||
|
||||
_owner->_tooltipWidget.banishWindow();
|
||||
_owner->banishWindow();
|
||||
inv.freeInv();
|
||||
|
||||
events.clearEvents();
|
||||
vm.doFoolscapPuzzle();
|
||||
} else if (_invVerbSelect == 0) {
|
||||
// They have released the mouse on the Look Verb command, so Look at the inventory item
|
||||
ui._invLookFlag = true;
|
||||
inv.freeInv();
|
||||
ui._windowOpen = false;
|
||||
ui._lookPos = mousePos;
|
||||
ui.printObjectDesc(inv[_owner->_invSelect]._examine, true);
|
||||
} else {
|
||||
_owner->_invVerbMode = 3;
|
||||
ui._oldBgFound = -1;
|
||||
|
||||
// See if the selected Verb with the selected Iventory Item, is to be used by itself
|
||||
if (!_inventCommands[_invVerbSelect].compareToIgnoreCase(inv[_owner->_invSelect]._verb._verb) ||
|
||||
!inv[_owner->_invSelect]._verb._target.compareToIgnoreCase("*SELF")) {
|
||||
inv.freeInv();
|
||||
|
||||
ui._menuMode = scene._labTableScene ? LAB_MODE : STD_MODE;
|
||||
events.clearEvents();
|
||||
ui.checkAction(inv[_owner->_invSelect]._verb, 2000);
|
||||
} else {
|
||||
_owner->_verb = _inventCommands[_invVerbSelect];
|
||||
}
|
||||
|
||||
// If we are still in Inventory Mode, setup the graphic to float in front of the mouse cursor
|
||||
if (ui._menuMode == INV_MODE) {
|
||||
// Add the inventory item to the cursor
|
||||
ImageFrame &imgFrame = (*inv._invShapes[_owner->_invSelect - inv._invIndex])[0];
|
||||
events.setCursor(ARROW, Common::Point(-100, imgFrame._height), imgFrame._frame);
|
||||
|
||||
// Close the inventory dialog without banishing it, so it can keep getting events
|
||||
// to handle tooltips and actually making the selection of what object to use them item on
|
||||
inv.freeInv();
|
||||
_owner->_surface.free();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WidgetInventoryVerbs::highlightControls() {
|
||||
Events &events = *_vm->_events;
|
||||
Common::Point mousePos = events.mousePos();
|
||||
|
||||
Common::Rect innerBounds = _bounds;
|
||||
innerBounds.grow(-3);
|
||||
|
||||
// Set the highlighted verb
|
||||
_invVerbSelect = -1;
|
||||
if (innerBounds.contains(mousePos))
|
||||
_invVerbSelect = (mousePos.y - _bounds.top - 3) / (_surface.fontHeight() + 7);
|
||||
|
||||
// See if the highlighted verb has changed
|
||||
if (_invVerbSelect != _oldInvVerbSelect) {
|
||||
// Draw the list again, with the new highlighting
|
||||
for (int idx = 0; idx < (int)_inventCommands.size(); ++idx) {
|
||||
byte color = (idx == _invVerbSelect) ? COMMAND_HIGHLIGHTED : INFO_TOP;
|
||||
_surface.writeString(_inventCommands[idx], Common::Point(
|
||||
(_bounds.width() - _surface.stringWidth(_inventCommands[idx])) / 2,
|
||||
(_surface.fontHeight() + 7) * idx + 5), color);
|
||||
}
|
||||
|
||||
_oldInvVerbSelect = _invVerbSelect;
|
||||
}
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------*/
|
||||
|
||||
WidgetInventory::WidgetInventory(SherlockEngine *vm) : WidgetBase(vm),
|
||||
_tooltipWidget(vm, this), _verbList(vm, this) {
|
||||
_invMode = 0;
|
||||
_invVerbMode = 0;
|
||||
_invSelect = _oldInvSelect = -1;
|
||||
_selector = _oldSelector = -1;
|
||||
_swapItems = false;
|
||||
}
|
||||
|
||||
void WidgetInventory::load(int mode) {
|
||||
Events &events = *_vm->_events;
|
||||
Inventory &inv = *_vm->_inventory;
|
||||
Screen &screen = *_vm->_screen;
|
||||
Common::Point mousePos = events.mousePos();
|
||||
|
||||
if (mode == 3) {
|
||||
mode = 2;
|
||||
mousePos = Common::Point(screen._currentScroll.x + SHERLOCK_SCREEN_WIDTH / 2, SHERLOCK_SCREEN_HEIGHT / 2);
|
||||
}
|
||||
|
||||
if (mode != 0)
|
||||
_invMode = mode;
|
||||
_invVerbMode = 0;
|
||||
_invSelect = _oldInvSelect = -1;
|
||||
_selector = _oldSelector = -1;
|
||||
_scroll = true;
|
||||
|
||||
if (mode == 0) {
|
||||
banishWindow();
|
||||
} else {
|
||||
_bounds = Common::Rect((INVENTORY_XSIZE + 3) * NUM_INVENTORY_SHOWN / 2 + BUTTON_SIZE + 6,
|
||||
(INVENTORY_YSIZE + 3) * 2 + 3);
|
||||
_bounds.moveTo(mousePos.x - _bounds.width() / 2, mousePos.y - _bounds.height() / 2);
|
||||
}
|
||||
|
||||
Common::Keymapper *keymapper = g_system->getEventManager()->getKeymapper();
|
||||
keymapper->getKeymap("tattoo")->setEnabled(false);
|
||||
keymapper->getKeymap("tattoo-scrolling")->setEnabled(true);
|
||||
keymapper->getKeymap("tattoo-inv")->setEnabled(true);
|
||||
|
||||
// Ensure menu will be on-screen
|
||||
restrictToScreen();
|
||||
|
||||
// Load the inventory data
|
||||
inv.loadInv();
|
||||
|
||||
// Redraw the inventory menu on the widget surface
|
||||
_surface.create(_bounds.width(), _bounds.height());
|
||||
_surface.clear(TRANSPARENCY);
|
||||
|
||||
// Draw the window background and then the inventory on top of it
|
||||
makeInfoArea(_surface);
|
||||
drawBars();
|
||||
drawInventory();
|
||||
}
|
||||
|
||||
void WidgetInventory::drawBars() {
|
||||
TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
|
||||
ImageFile &images = *ui._interfaceImages;
|
||||
int x;
|
||||
|
||||
_surface.hLine(3, INVENTORY_YSIZE + 3, _bounds.width() - 4, INFO_TOP);
|
||||
_surface.hLine(3, INVENTORY_YSIZE + 4, _bounds.width() - 4, INFO_MIDDLE);
|
||||
_surface.hLine(3, INVENTORY_YSIZE + 5, _bounds.width() - 4, INFO_BOTTOM);
|
||||
_surface.SHtransBlitFrom(images[4], Common::Point(0, INVENTORY_YSIZE + 2));
|
||||
|
||||
for (int idx = 1; idx <= NUM_INVENTORY_SHOWN / 2; ++idx) {
|
||||
x = idx * (INVENTORY_XSIZE + 3);
|
||||
|
||||
_surface.vLine(x, 3, _bounds.height() - 4, INFO_TOP);
|
||||
_surface.vLine(x + 1, 3, _bounds.height() - 4, INFO_MIDDLE);
|
||||
_surface.vLine(x + 2, 3, _bounds.height() - 4, INFO_BOTTOM);
|
||||
|
||||
_surface.SHtransBlitFrom(images[6], Common::Point(x - 1, 1));
|
||||
_surface.SHtransBlitFrom(images[7], Common::Point(x - 1, _bounds.height() - 4));
|
||||
_surface.SHtransBlitFrom(images[6], Common::Point(x - 1, INVENTORY_YSIZE + 5));
|
||||
_surface.SHtransBlitFrom(images[7], Common::Point(x - 1, INVENTORY_YSIZE + 2));
|
||||
}
|
||||
|
||||
_surface.vLine(x + 2, INVENTORY_YSIZE + 2, INVENTORY_YSIZE + 8, INFO_BOTTOM);
|
||||
}
|
||||
|
||||
void WidgetInventory::drawInventory() {
|
||||
Inventory &inv = *_vm->_inventory;
|
||||
|
||||
// TODO: Refactor _invIndex into this widget class
|
||||
for (int idx = 0, itemId = inv._invIndex; idx < NUM_INVENTORY_SHOWN; ++idx, ++itemId) {
|
||||
// Figure out the drawing position
|
||||
Common::Point pt(3 + (INVENTORY_XSIZE + 3) * (idx % (NUM_INVENTORY_SHOWN / 2)),
|
||||
3 + (INVENTORY_YSIZE + 3) * (idx / (NUM_INVENTORY_SHOWN / 2)));
|
||||
|
||||
// Draw the box to serve as the background for the item
|
||||
_surface.hLine(pt.x + 1, pt.y, pt.x + INVENTORY_XSIZE - 2, TRANSPARENCY);
|
||||
_surface.fillRect(Common::Rect(pt.x, pt.y + 1, pt.x + INVENTORY_XSIZE, pt.y + INVENTORY_YSIZE - 1), TRANSPARENCY);
|
||||
_surface.hLine(pt.x + 1, pt.y + INVENTORY_YSIZE - 1, pt.x + INVENTORY_XSIZE - 2, TRANSPARENCY);
|
||||
|
||||
// Draw the item
|
||||
if (itemId < inv._holdings) {
|
||||
ImageFrame &img = (*inv._invShapes[idx])[0];
|
||||
_surface.SHtransBlitFrom(img, Common::Point(pt.x + (INVENTORY_XSIZE - img._width) / 2,
|
||||
pt.y + (INVENTORY_YSIZE - img._height) / 2));
|
||||
}
|
||||
}
|
||||
|
||||
drawScrollBar(inv._invIndex / NUM_INV_PER_LINE, NUM_INVENTORY_SHOWN / NUM_INV_PER_LINE,
|
||||
(inv._holdings + NUM_INV_PER_LINE - 1) / NUM_INV_PER_LINE);
|
||||
}
|
||||
|
||||
void WidgetInventory::handleEvents() {
|
||||
TattooEngine &vm = *(TattooEngine *)_vm;
|
||||
Events &events = *_vm->_events;
|
||||
Inventory &inv = *_vm->_inventory;
|
||||
People &people = *_vm->_people;
|
||||
TattooScene &scene = *(TattooScene *)_vm->_scene;
|
||||
TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
|
||||
Common::Point mousePos = events.mousePos();
|
||||
|
||||
if (_invVerbMode == 1) {
|
||||
checkTabbingKeys(MAX_INV_COMMANDS);
|
||||
} else if (_invVerbMode == 0) {
|
||||
checkInvTabbingKeys();
|
||||
|
||||
// Handle scrollbar events
|
||||
int oldScrollIndex = inv._invIndex / NUM_INV_PER_LINE;
|
||||
int invIndex = inv._invIndex / NUM_INV_PER_LINE;
|
||||
|
||||
ScrollHighlight oldHighlight = ui._scrollHighlight;
|
||||
handleScrollbarEvents(invIndex, NUM_INVENTORY_SHOWN / NUM_INV_PER_LINE,
|
||||
(inv._holdings + NUM_INV_PER_LINE - 1) / NUM_INV_PER_LINE);
|
||||
|
||||
handleScrolling(invIndex, NUM_INVENTORY_SHOWN / NUM_INV_PER_LINE,
|
||||
(inv._holdings + NUM_INV_PER_LINE - 1) / NUM_INV_PER_LINE);
|
||||
|
||||
if (oldScrollIndex != invIndex) {
|
||||
// Starting visible item index has changed, so set the index and reload inventory graphics
|
||||
inv._invIndex = invIndex * NUM_INV_PER_LINE;
|
||||
inv.freeGraphics();
|
||||
inv.loadGraphics();
|
||||
}
|
||||
|
||||
if (ui._scrollHighlight != oldHighlight || oldScrollIndex != invIndex) {
|
||||
drawInventory();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (_invVerbMode != 1)
|
||||
_tooltipWidget.handleEvents();
|
||||
|
||||
// Flag is they started pressing outside of the menu
|
||||
if (events._firstPress && !_bounds.contains(mousePos))
|
||||
_outsideMenu = true;
|
||||
|
||||
if (_invVerbMode != 3)
|
||||
highlightControls();
|
||||
|
||||
// See if they released a mouse button button
|
||||
if (events._released || events._rightReleased || ui._action == kActionTattooInvExit) {
|
||||
ui._scrollHighlight = SH_NONE;
|
||||
|
||||
// See if they have a Verb List open for an Inventry Item
|
||||
if (_invVerbMode == 1)
|
||||
return;
|
||||
|
||||
if (_invVerbMode == 3) {
|
||||
// Selecting object after inventory verb has been selected
|
||||
_tooltipWidget.banishWindow();
|
||||
close();
|
||||
|
||||
if (ui._action != kActionTattooInvExit) {
|
||||
// If user pointed at an item, use the selected inventory item with this item
|
||||
bool found = false;
|
||||
if (ui._bgFound != -1) {
|
||||
if (ui._personFound) {
|
||||
Person &person = people[ui._bgFound - 1000];
|
||||
|
||||
for (int idx = 0; idx < 2; ++idx) {
|
||||
if (!person._use[idx]._verb.compareToIgnoreCase(_verb) &&
|
||||
!person._use[idx]._target.compareToIgnoreCase(_invTarget)) {
|
||||
ui.checkAction(person._use[idx], ui._bgFound);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int idx = 0; idx < 6; ++idx) {
|
||||
if (!ui._bgShape->_use[idx]._verb.compareToIgnoreCase(_verb) &&
|
||||
!ui._bgShape->_use[idx]._target.compareToIgnoreCase(_invTarget)) {
|
||||
ui.checkAction(ui._bgShape->_use[idx], ui._bgFound);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
ui.putMessage("%s", FIXED(NoEffect));
|
||||
}
|
||||
} else if ((_outsideMenu && !_bounds.contains(mousePos)) || ui._action == kActionTattooInvExit) {
|
||||
// Want to close the window (clicked outside of it). So close the window and return to Standard
|
||||
close();
|
||||
|
||||
} else if (_bounds.contains(mousePos)) {
|
||||
// Mouse button was released inside the inventory window
|
||||
_outsideMenu = false;
|
||||
|
||||
// See if they are pointing at one of the inventory items
|
||||
if (_invSelect != -1) {
|
||||
// See if they are in Use Obj with Inv. Mode (they right clicked on an item
|
||||
// in the room and selected "Use with Inv.")
|
||||
if (_invMode == 1) {
|
||||
_tooltipWidget.banishWindow();
|
||||
banishWindow();
|
||||
|
||||
// See if the item in the room that they started with was a person
|
||||
bool found = false;
|
||||
if (ui._activeObj >= 1000) {
|
||||
// Object was a person, activate anything in his two verb fields
|
||||
for (int idx = 0; idx < 2; ++idx) {
|
||||
if (!people[ui._activeObj - 1000]._use[idx]._target.compareToIgnoreCase(inv[_invSelect]._name)) {
|
||||
ui.checkAction(people[ui._activeObj - 1000]._use[idx], ui._activeObj);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Object was a regular object, activate anything in its verb fields
|
||||
for (int idx = 0; idx < 6; ++idx) {
|
||||
if (!scene._bgShapes[ui._activeObj]._use[idx]._target.compareToIgnoreCase(inv[_invSelect]._name)) {
|
||||
ui.checkAction(scene._bgShapes[ui._activeObj]._use[idx], ui._activeObj);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
ui.putMessage("%s", FIXED(NoEffect));
|
||||
|
||||
} else {
|
||||
// See if they right clicked on an item
|
||||
if (events._rightReleased) {
|
||||
_invVerbMode = 1;
|
||||
_verbList._oldInvVerbSelect = -1;
|
||||
_tooltipWidget.banishWindow();
|
||||
|
||||
// Keep track of the name of the inventory object so we can check it against the target fields
|
||||
// of verbs when we activate it
|
||||
_invTarget = inv[_invSelect]._name;
|
||||
_swapItems = false;
|
||||
|
||||
_verbList.load();
|
||||
} else {
|
||||
// They left clicked on an inventory item, so Look at it
|
||||
|
||||
// Check if they are looking at the solved Foolscap
|
||||
if ((!inv[_invSelect]._name.compareToIgnoreCase(FIXED(Inv6)) || !inv[_invSelect]._name.compareToIgnoreCase(FIXED(Inv7)))
|
||||
&& vm.readFlags(299)) {
|
||||
banishWindow();
|
||||
_tooltipWidget.erase();
|
||||
|
||||
_invVerbMode = 0;
|
||||
inv.freeInv();
|
||||
|
||||
events.clearEvents();
|
||||
events.setCursor(ARROW);
|
||||
ui._menuMode = scene._labTableScene ? LAB_MODE : STD_MODE;
|
||||
|
||||
scene.doBgAnim();
|
||||
vm.doFoolscapPuzzle();
|
||||
} else {
|
||||
ui._invLookFlag = true;
|
||||
inv.freeInv();
|
||||
|
||||
_tooltipWidget.banishWindow();
|
||||
ui._windowOpen = false;
|
||||
ui._lookPos = mousePos;
|
||||
ui.printObjectDesc(inv[_invSelect]._examine, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WidgetInventory::checkInvTabbingKeys() {
|
||||
}
|
||||
|
||||
void WidgetInventory::highlightControls() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
void WidgetInventory::banishWindow() {
|
||||
WidgetBase::banishWindow();
|
||||
|
||||
Common::Keymapper *keymapper = g_system->getEventManager()->getKeymapper();
|
||||
keymapper->getKeymap("tattoo-scrolling")->setEnabled(false);
|
||||
keymapper->getKeymap("tattoo-inv")->setEnabled(false);
|
||||
keymapper->getKeymap("tattoo")->setEnabled(true);
|
||||
|
||||
_verbList.banishWindow();
|
||||
}
|
||||
|
||||
void WidgetInventory::draw() {
|
||||
WidgetBase::draw();
|
||||
_tooltipWidget.draw();
|
||||
}
|
||||
|
||||
void WidgetInventory::erase() {
|
||||
WidgetBase::erase();
|
||||
_tooltipWidget.erase();
|
||||
}
|
||||
|
||||
void WidgetInventory::close() {
|
||||
Events &events = *_vm->_events;
|
||||
Inventory &inv = *_vm->_inventory;
|
||||
TattooScene &scene = *(TattooScene *)_vm->_scene;
|
||||
TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
|
||||
|
||||
banishWindow();
|
||||
inv.freeInv();
|
||||
events.clearEvents();
|
||||
|
||||
events.setCursor(ARROW);
|
||||
ui._menuMode = scene._labTableScene ? LAB_MODE : STD_MODE;
|
||||
}
|
||||
|
||||
} // End of namespace Tattoo
|
||||
|
||||
} // End of namespace Sherlock
|
||||
157
engines/sherlock/tattoo/widget_inventory.h
Normal file
157
engines/sherlock/tattoo/widget_inventory.h
Normal file
@@ -0,0 +1,157 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SHERLOCK_TATTOO_WIDGET_INVENTORY_H
|
||||
#define SHERLOCK_TATTOO_WIDGET_INVENTORY_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "sherlock/tattoo/widget_base.h"
|
||||
#include "sherlock/tattoo/widget_tooltip.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
class SherlockEngine;
|
||||
|
||||
namespace Tattoo {
|
||||
|
||||
#define NUM_INVENTORY_SHOWN 8 // Number of Inventory Items Shown
|
||||
|
||||
class WidgetInventory;
|
||||
|
||||
class WidgetInventoryTooltip: public WidgetTooltipBase {
|
||||
private:
|
||||
WidgetInventory *_owner;
|
||||
protected:
|
||||
/**
|
||||
* Overriden from base class, since tooltips have a completely transparent background
|
||||
*/
|
||||
void drawBackground() override {}
|
||||
public:
|
||||
WidgetInventoryTooltip(SherlockEngine *vm, WidgetInventory *owner);
|
||||
~WidgetInventoryTooltip() override {}
|
||||
|
||||
/**
|
||||
* Set the text for the tooltip
|
||||
*/
|
||||
void setText(const Common::String &str);
|
||||
|
||||
/**
|
||||
* Handle updating the tooltip state
|
||||
*/
|
||||
void handleEvents() override;
|
||||
};
|
||||
|
||||
class WidgetInventoryVerbs : public WidgetBase {
|
||||
private:
|
||||
WidgetInventory *_owner;
|
||||
Common::StringArray _inventCommands;
|
||||
|
||||
void highlightControls();
|
||||
public:
|
||||
int _invVerbSelect, _oldInvVerbSelect;
|
||||
public:
|
||||
WidgetInventoryVerbs(SherlockEngine *vm, WidgetInventory *owner);
|
||||
~WidgetInventoryVerbs() override {}
|
||||
|
||||
void load();
|
||||
|
||||
/**
|
||||
* Handle updating the tooltip state
|
||||
*/
|
||||
void handleEvents() override;
|
||||
};
|
||||
|
||||
class WidgetInventory: public WidgetBase {
|
||||
friend class WidgetInventoryTooltip;
|
||||
friend class WidgetInventoryVerbs;
|
||||
private:
|
||||
int _invVerbMode;
|
||||
int _selector, _oldSelector;
|
||||
int _invSelect, _oldInvSelect;
|
||||
WidgetInventoryTooltip _tooltipWidget;
|
||||
WidgetInventoryVerbs _verbList;
|
||||
bool _swapItems;
|
||||
Surface _menuSurface;
|
||||
Common::String _invTarget;
|
||||
|
||||
/**
|
||||
* Draw the bars within the dialog
|
||||
*/
|
||||
void drawBars();
|
||||
|
||||
/**
|
||||
* Check for keys to mouse the mouse within the inventory dialog
|
||||
*/
|
||||
void checkInvTabbingKeys();
|
||||
|
||||
/**
|
||||
* Highlights the controls
|
||||
*/
|
||||
void highlightControls();
|
||||
public:
|
||||
int _invMode;
|
||||
Common::String _action;
|
||||
Common::String _verb;
|
||||
public:
|
||||
WidgetInventory(SherlockEngine *vm);
|
||||
~WidgetInventory() override {}
|
||||
|
||||
/**
|
||||
* Load the inventory window
|
||||
*/
|
||||
void load(int mode);
|
||||
|
||||
/**
|
||||
* Draw the inventory on the surface
|
||||
*/
|
||||
void drawInventory();
|
||||
|
||||
/**
|
||||
* Close the window
|
||||
*/
|
||||
void close();
|
||||
|
||||
/**
|
||||
* Handle events whilst the widget is on-screen
|
||||
*/
|
||||
void handleEvents() override;
|
||||
|
||||
/**
|
||||
* Close a currently active menu
|
||||
*/
|
||||
void banishWindow() override;
|
||||
|
||||
/**
|
||||
* Erase any previous display of the widget on the screen
|
||||
*/
|
||||
void erase() override;
|
||||
|
||||
/**
|
||||
* Update the display of the widget on the screen
|
||||
*/
|
||||
void draw() override;
|
||||
};
|
||||
|
||||
} // End of namespace Tattoo
|
||||
|
||||
} // End of namespace Sherlock
|
||||
|
||||
#endif
|
||||
197
engines/sherlock/tattoo/widget_lab.cpp
Normal file
197
engines/sherlock/tattoo/widget_lab.cpp
Normal file
@@ -0,0 +1,197 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sherlock/tattoo/widget_lab.h"
|
||||
#include "sherlock/tattoo/tattoo_fixed_text.h"
|
||||
#include "sherlock/tattoo/tattoo_user_interface.h"
|
||||
#include "sherlock/tattoo/tattoo.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
namespace Tattoo {
|
||||
|
||||
WidgetLab::WidgetLab(SherlockEngine *vm) : WidgetBase(vm) {
|
||||
_labObject = nullptr;
|
||||
}
|
||||
|
||||
void WidgetLab::summonWindow() {
|
||||
WidgetBase::summonWindow();
|
||||
_labObject = nullptr;
|
||||
}
|
||||
|
||||
void WidgetLab::handleEvents() {
|
||||
Events &events = *_vm->_events;
|
||||
Scene &scene = *_vm->_scene;
|
||||
TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
|
||||
Common::Point mousePos = events.mousePos();
|
||||
|
||||
WidgetBase::handleEvents();
|
||||
|
||||
// Handle drawing tooltips. If the user is dragging a lab item, display a tooltip for using the item
|
||||
// on another. Otherwise, fall back on showing standard tooltips
|
||||
if (events.getCursor() == INVALID_CURSOR)
|
||||
displayLabNames();
|
||||
else
|
||||
ui.displayObjectNames();
|
||||
|
||||
// See if they've released a mouse button to do an action
|
||||
if (events._released || events._rightReleased) {
|
||||
// See if the mouse was released in an exit/arrow zone (ie. the "Exit" button)
|
||||
ui._exitZone = -1;
|
||||
if (ui._arrowZone != -1 && events._released)
|
||||
ui._exitZone = ui._arrowZone;
|
||||
|
||||
// Turn any current tooltip off
|
||||
if (ui._arrowZone == -1 || events._rightReleased)
|
||||
ui._tooltipWidget.setText("");
|
||||
|
||||
bool noDesc = false;
|
||||
if (ui._bgFound != -1) {
|
||||
if (ui._bgShape->_description.hasPrefix(" ") || ui._bgShape->_description.empty())
|
||||
noDesc = true;
|
||||
} else {
|
||||
noDesc = true;
|
||||
}
|
||||
|
||||
events.setCursor(ARROW);
|
||||
|
||||
if (events._rightReleased) {
|
||||
// If the player is dragging an object around, restore it to its previous location and reset the cursor
|
||||
if (_labObject) {
|
||||
_labObject->toggleHidden();
|
||||
|
||||
// Toggle any other objects (like shadows) tied to this object
|
||||
for (int idx = 0; idx < 6; ++idx) {
|
||||
if (!_labObject->_use[idx]._target.compareToIgnoreCase("Toggle")) {
|
||||
for (int nameNum = 0; nameNum < 4; ++nameNum)
|
||||
scene.toggleObject(_labObject->_use[idx]._names[nameNum]);
|
||||
}
|
||||
}
|
||||
|
||||
events.setCursor(ARROW);
|
||||
}
|
||||
|
||||
// Show the command list for this object
|
||||
ui._verbsWidget.load(!noDesc);
|
||||
} else if (!noDesc) {
|
||||
// The player has released on an object, see if they had an object selected
|
||||
// that will be used with this new object
|
||||
if (_labObject) {
|
||||
// See if the dragged object can be used with the new object
|
||||
for (int idx = 0; idx < 6; ++idx) {
|
||||
// See if the name of the dragged object is in any of the Target
|
||||
// fields of the verbs for the new object
|
||||
if (!_labObject->_name.compareToIgnoreCase(ui._bgShape->_use[idx]._target.c_str())) {
|
||||
// This object can be used, so use it
|
||||
ui.checkAction(ui._bgShape->_use[idx], ui._bgFound);
|
||||
ui._activeObj = -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Restore the dragged object to its previous location
|
||||
_labObject->toggleHidden();
|
||||
|
||||
// Toggle any other objects (like shadows) tied to this object
|
||||
for (int idx = 0; idx < 6; ++idx) {
|
||||
if (!_labObject->_use[idx]._target.compareToIgnoreCase("Toggle")) {
|
||||
for (int nameNum = 0; nameNum < 4; ++nameNum)
|
||||
scene.toggleObject(_labObject->_use[idx]._names[nameNum]);
|
||||
}
|
||||
}
|
||||
} else if (!ui._bgShape->_name.compareToIgnoreCase("Exit")) {
|
||||
// Execute the Exit button's script, which will leave the scene
|
||||
ui.lookAtObject();
|
||||
}
|
||||
} else {
|
||||
// The player has released the mouse while NOT over an object. If theu were dragging an object
|
||||
// around with the mouse, restore it to its previous location and reset the cursor
|
||||
if (_labObject) {
|
||||
_labObject->toggleHidden();
|
||||
|
||||
// Toggle any other objects (like shadows) tied to this object
|
||||
for (int idx = 0; idx < 6; ++idx) {
|
||||
if (!_labObject->_use[idx]._target.compareToIgnoreCase("Toggle")) {
|
||||
for (int nameNum = 0; nameNum < 4; ++nameNum)
|
||||
scene.toggleObject(_labObject->_use[idx]._names[nameNum]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_labObject = nullptr;
|
||||
ui._tooltipWidget._offsetY = 0;
|
||||
} else if (events._pressed && !_labObject) {
|
||||
// If the mouse is over an object and the object is not SOLID, then we need to pick this object
|
||||
// up so the player can move it around
|
||||
if (ui._bgFound != -1) {
|
||||
// Check if the object is set as SOLID, you can't pick up Solid items
|
||||
if (ui._bgShape->_aType != SOLID && ui._bgShape->_type != NO_SHAPE) {
|
||||
// Save a reference to the object about to be dragged
|
||||
_labObject = ui._bgShape;
|
||||
|
||||
// Set the mouse cursor to the object
|
||||
Graphics::Surface &img = _labObject->_imageFrame->_frame;
|
||||
Common::Point cursorOffset = mousePos - _labObject->_position;
|
||||
events.setCursor(ARROW, cursorOffset, img);
|
||||
ui._tooltipWidget._offsetY = cursorOffset.y;
|
||||
|
||||
// Hide this object until they are done with it (releasing it)
|
||||
_labObject->toggleHidden();
|
||||
|
||||
// Toggle any other objects (like shadows) tied to this object
|
||||
for (int idx = 0; idx < 6; ++idx) {
|
||||
if (!_labObject->_use[idx]._target.compareToIgnoreCase("Toggle")) {
|
||||
for (int nameNum = 0; nameNum < 4; ++nameNum)
|
||||
scene.toggleObject(_labObject->_use[idx]._names[nameNum]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WidgetLab::displayLabNames() {
|
||||
TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
|
||||
|
||||
// See if thay are pointing at a different object and we need to change the tooltip
|
||||
if (ui._bgFound != ui._oldBgFound) {
|
||||
// See if there is a new object to be displayed
|
||||
if (ui._bgFound == -1) {
|
||||
ui._tooltipWidget.setText("");
|
||||
} else {
|
||||
Common::String str = Common::String::format("%s %s %s %s", FIXED(Use), _labObject->_description.c_str(),
|
||||
FIXED(With), ui._bgShape->_description.c_str());
|
||||
|
||||
// Make sure that the Object has a name
|
||||
if (!ui._bgShape->_description.empty() && !ui._bgShape->_description.hasPrefix(" ")) {
|
||||
ui._tooltipWidget.setText(str);
|
||||
} else {
|
||||
ui._tooltipWidget.setText("");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ui._oldArrowZone = ui._arrowZone;
|
||||
}
|
||||
|
||||
} // End of namespace Tattoo
|
||||
|
||||
} // End of namespace Sherlock
|
||||
65
engines/sherlock/tattoo/widget_lab.h
Normal file
65
engines/sherlock/tattoo/widget_lab.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SHERLOCK_TATTOO_WIDGET_LAB_H
|
||||
#define SHERLOCK_TATTOO_WIDGET_LAB_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "sherlock/tattoo/widget_base.h"
|
||||
#include "sherlock/objects.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
class SherlockEngine;
|
||||
|
||||
namespace Tattoo {
|
||||
|
||||
class WidgetLab: public WidgetBase {
|
||||
private:
|
||||
Object *_labObject;
|
||||
|
||||
/**
|
||||
* Display tooltips of an object being dragged along with any object the dragged
|
||||
* object is currently over
|
||||
*/
|
||||
void displayLabNames();
|
||||
public:
|
||||
Common::String _remainingText;
|
||||
public:
|
||||
WidgetLab(SherlockEngine *vm);
|
||||
~WidgetLab() override {}
|
||||
|
||||
/**
|
||||
* Summon the window
|
||||
*/
|
||||
void summonWindow() override;
|
||||
|
||||
/**
|
||||
* Handle event processing
|
||||
*/
|
||||
void handleEvents() override;
|
||||
};
|
||||
|
||||
} // End of namespace Tattoo
|
||||
|
||||
} // End of namespace Sherlock
|
||||
|
||||
#endif
|
||||
395
engines/sherlock/tattoo/widget_options.cpp
Normal file
395
engines/sherlock/tattoo/widget_options.cpp
Normal file
@@ -0,0 +1,395 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sherlock/tattoo/widget_options.h"
|
||||
#include "sherlock/tattoo/tattoo.h"
|
||||
#include "sherlock/tattoo/tattoo_fixed_text.h"
|
||||
#include "sherlock/tattoo/tattoo_scene.h"
|
||||
#include "sherlock/tattoo/tattoo_user_interface.h"
|
||||
|
||||
#include "backends/keymapper/keymapper.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
namespace Tattoo {
|
||||
|
||||
WidgetOptions::WidgetOptions(SherlockEngine *vm) : WidgetBase(vm) {
|
||||
_midiSliderX = _digiSliderX = 0;
|
||||
_selector = _oldSelector = -1;
|
||||
}
|
||||
|
||||
void WidgetOptions::load() {
|
||||
Events &events = *_vm->_events;
|
||||
TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
|
||||
_centerPos = events.mousePos();
|
||||
|
||||
render();
|
||||
|
||||
summonWindow();
|
||||
ui._menuMode = OPTION_MODE;
|
||||
|
||||
Common::Keymapper *keymapper = g_system->getEventManager()->getKeymapper();
|
||||
keymapper->getKeymap("tattoo")->setEnabled(false);
|
||||
keymapper->getKeymap("tattoo-options")->setEnabled(true);
|
||||
keymapper->getKeymap("tattoo-exit")->setEnabled(true);
|
||||
}
|
||||
|
||||
void WidgetOptions::handleEvents() {
|
||||
TattooEngine &vm = *(TattooEngine *)_vm;
|
||||
Events &events = *_vm->_events;
|
||||
Music &music = *_vm->_music;
|
||||
Screen &screen = *_vm->_screen;
|
||||
Sound &sound = *_vm->_sound;
|
||||
Talk &talk = *_vm->_talk;
|
||||
TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
|
||||
Common::Point mousePos = events.mousePos();
|
||||
Common::CustomEventType action = ui._action;
|
||||
Common::Keymapper *keymapper = g_system->getEventManager()->getKeymapper();
|
||||
|
||||
if (talk._talkToAbort) {
|
||||
sound.stopSound();
|
||||
return;
|
||||
}
|
||||
|
||||
// Flag if they started pressing outside the window
|
||||
if (events._firstPress && !_bounds.contains(mousePos))
|
||||
_outsideMenu = true;
|
||||
|
||||
if (action != kActionNone) {
|
||||
// Emulate a mouse release if Enter or Space Bar is pressed
|
||||
if (ui._action == kActionTattooOptionsSelect) {
|
||||
events._pressed = events._oldButtons = false;
|
||||
events._released = true;
|
||||
} else if (ui._action == kActionTattooExit) {
|
||||
keymapper->getKeymap("tattoo-options")->setEnabled(false);
|
||||
keymapper->getKeymap("tattoo-exit")->setEnabled(false);
|
||||
keymapper->getKeymap("tattoo")->setEnabled(true);
|
||||
close();
|
||||
return;
|
||||
} else {
|
||||
checkTabbingKeys(11);
|
||||
}
|
||||
}
|
||||
|
||||
// Check highlighting the various controls
|
||||
if (_bounds.contains(mousePos)) {
|
||||
_selector = (mousePos.y - _bounds.top) / (_surface.fontHeight() + 7);
|
||||
|
||||
// If one of the sliders has been selected, & the mouse is not pressed, reset the selector to -1
|
||||
if ((_selector == 3 || _selector == 6) && !events._pressed)
|
||||
_selector = -1;
|
||||
} else {
|
||||
_selector = -1;
|
||||
if (_outsideMenu && (events._released || events._rightReleased)) {
|
||||
events.clearEvents();
|
||||
keymapper->getKeymap("tattoo-options")->setEnabled(false);
|
||||
keymapper->getKeymap("tattoo-exit")->setEnabled(false);
|
||||
keymapper->getKeymap("tattoo")->setEnabled(true);
|
||||
close();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If the selected control has changed, redraw the dialog contents
|
||||
if (_selector != _oldSelector)
|
||||
render(OP_CONTENTS);
|
||||
_oldSelector = _selector;
|
||||
|
||||
// Adjust the Volume Sliders (if necessary) here
|
||||
switch (_selector) {
|
||||
case 3: {
|
||||
// Set Music Volume
|
||||
_midiSliderX = mousePos.x - _bounds.left;
|
||||
if (_midiSliderX < _surface.widestChar())
|
||||
_midiSliderX = _surface.widestChar();
|
||||
else
|
||||
if (_midiSliderX > _bounds.width() - _surface.widestChar())
|
||||
_midiSliderX = _bounds.width() - _surface.widestChar();
|
||||
|
||||
int newVolume = (_midiSliderX - _surface.widestChar()) * 255 / (_bounds.width() - _surface.widestChar() * 2);
|
||||
if (newVolume != music._musicVolume) {
|
||||
music.setMusicVolume(newVolume);
|
||||
vm.saveConfig();
|
||||
}
|
||||
|
||||
render(OP_NAMES);
|
||||
break;
|
||||
}
|
||||
|
||||
case 6: {
|
||||
// Set Digitized Volume
|
||||
_digiSliderX = mousePos.x - _bounds.left;
|
||||
if (_digiSliderX < _surface.widestChar())
|
||||
_digiSliderX = _surface.widestChar();
|
||||
else if (_digiSliderX > _bounds.width() - _surface.widestChar())
|
||||
_digiSliderX = _bounds.width() - _surface.widestChar();
|
||||
|
||||
int newVolume = (_digiSliderX - _surface.widestChar()) * 255 / (_bounds.width() - _surface.widestChar() * 2);
|
||||
if (newVolume != sound._soundVolume) {
|
||||
sound.setVolume(newVolume);
|
||||
vm.saveConfig();
|
||||
}
|
||||
|
||||
render(OP_NAMES);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Option selected
|
||||
if (events._released || events._rightReleased) {
|
||||
events.clearEvents();
|
||||
_outsideMenu = false;
|
||||
int temp = _selector;
|
||||
_selector = -1;
|
||||
|
||||
switch (temp) {
|
||||
case 0:
|
||||
// Load Game
|
||||
keymapper->getKeymap("tattoo-options")->setEnabled(false);
|
||||
keymapper->getKeymap("tattoo-exit")->setEnabled(false);
|
||||
keymapper->getKeymap("tattoo")->setEnabled(true);
|
||||
close();
|
||||
ui.loadGame();
|
||||
break;
|
||||
|
||||
case 1:
|
||||
// Save Game
|
||||
keymapper->getKeymap("tattoo-options")->setEnabled(false);
|
||||
keymapper->getKeymap("tattoo-exit")->setEnabled(false);
|
||||
keymapper->getKeymap("tattoo")->setEnabled(true);
|
||||
close();
|
||||
ui.saveGame();
|
||||
break;
|
||||
|
||||
case 2:
|
||||
// Toggle Music
|
||||
music._musicOn = !music._musicOn;
|
||||
if (!music._musicOn)
|
||||
music.stopMusic();
|
||||
else
|
||||
music.startSong();
|
||||
|
||||
render(OP_NAMES);
|
||||
vm.saveConfig();
|
||||
break;
|
||||
|
||||
case 4:
|
||||
// Toggle Sound Effects
|
||||
sound.stopSound();
|
||||
sound._digitized = !sound._digitized;
|
||||
|
||||
render(OP_NAMES);
|
||||
vm.saveConfig();
|
||||
break;
|
||||
|
||||
case 5:
|
||||
// Toggle Voices
|
||||
sound._speechOn = !sound._speechOn;
|
||||
|
||||
render(OP_NAMES);
|
||||
vm.saveConfig();
|
||||
break;
|
||||
|
||||
case 7:
|
||||
// Toggle Text Windows
|
||||
vm._textWindowsOn = !vm._textWindowsOn;
|
||||
|
||||
render(OP_NAMES);
|
||||
vm.saveConfig();
|
||||
break;
|
||||
|
||||
case 8: {
|
||||
// New Font Style
|
||||
int fontNumber = screen.fontNumber() + 1;
|
||||
if (fontNumber == 7)
|
||||
fontNumber = 0;
|
||||
screen.setFont(fontNumber);
|
||||
|
||||
render(OP_ALL);
|
||||
vm.saveConfig();
|
||||
break;
|
||||
}
|
||||
|
||||
case 9:
|
||||
// Toggle Transparent Menus
|
||||
vm._transparentMenus = !vm._transparentMenus;
|
||||
|
||||
render(OP_NAMES);
|
||||
vm.saveConfig();
|
||||
break;
|
||||
|
||||
case 10:
|
||||
// Quit
|
||||
keymapper->getKeymap("tattoo-options")->setEnabled(false);
|
||||
keymapper->getKeymap("tattoo-exit")->setEnabled(false);
|
||||
keymapper->getKeymap("tattoo")->setEnabled(true);
|
||||
banishWindow();
|
||||
ui.doQuitMenu();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
_oldSelector = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void WidgetOptions::render(OptionRenderMode mode) {
|
||||
TattooEngine &vm = *(TattooEngine *)_vm;
|
||||
Music &music = *_vm->_music;
|
||||
Sound &sound = *_vm->_sound;
|
||||
TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
|
||||
ImageFile &images = *ui._interfaceImages;
|
||||
const char *const OFF_ON[2] = { FIXED(Off), FIXED(On) };
|
||||
|
||||
// Draw the border if necessary
|
||||
if (mode == OP_ALL) {
|
||||
// Set bounds for the dialog
|
||||
Common::String widestString = Common::String::format("%s %s", FIXED(TransparentMenus), FIXED(Off));
|
||||
_bounds = Common::Rect(_surface.stringWidth(widestString) + _surface.widestChar() * 2 + 6,
|
||||
(_surface.fontHeight() + 7) * 11 + 3);
|
||||
_bounds.moveTo(_centerPos.x - _bounds.width() / 2, _centerPos.y - _bounds.height() / 2);
|
||||
|
||||
// Get slider positions
|
||||
_midiSliderX = music._musicVolume * (_bounds.width() - _surface.widestChar() * 2) / 255 + _surface.widestChar();
|
||||
_digiSliderX = sound._soundVolume * (_bounds.width() - _surface.widestChar() * 2) / 255 + _surface.widestChar();
|
||||
|
||||
// Setup the dialog
|
||||
_surface.create(_bounds.width(), _bounds.height());
|
||||
_surface.clear(TRANSPARENCY);
|
||||
makeInfoArea();
|
||||
|
||||
// Draw the lines separating options in the dialog
|
||||
int yp = _surface.fontHeight() + 7;
|
||||
for (int idx = 0; idx < 7; ++idx) {
|
||||
_surface.SHtransBlitFrom(images[4], Common::Point(0, yp - 1));
|
||||
_surface.SHtransBlitFrom(images[5], Common::Point(_surface.width() - images[5]._width, yp - 1));
|
||||
_surface.hLine(3, yp, _surface.width() - 4, INFO_TOP);
|
||||
_surface.hLine(3, yp + 1, _surface.width() - 4, INFO_MIDDLE);
|
||||
_surface.hLine(3, yp + 2, _surface.width() - 4, INFO_BOTTOM);
|
||||
|
||||
yp += _surface.fontHeight() + 7;
|
||||
if (idx == 1)
|
||||
yp += _surface.fontHeight() + 7;
|
||||
else if (idx == 2)
|
||||
yp += (_surface.fontHeight() + 7) * 2;
|
||||
}
|
||||
}
|
||||
|
||||
// Now go through and display all the items that can be highlighted
|
||||
for (int idx = 0, yp = 5; idx < 11; ++idx, yp += _surface.fontHeight() + 7) {
|
||||
if (mode == OP_ALL || idx == _selector || idx == _oldSelector) {
|
||||
if (mode == OP_NAMES)
|
||||
_surface.fillRect(Common::Rect(4, yp, _surface.width() - 5, yp + _surface.fontHeight() - 1), TRANSPARENCY);
|
||||
byte color = (idx == _selector) ? COMMAND_HIGHLIGHTED : INFO_TOP;
|
||||
Common::String str;
|
||||
|
||||
switch (idx) {
|
||||
case 0:
|
||||
str = FIXED(LoadGame);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
str = FIXED(SaveGame);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
str = Common::String::format("%s %s", FIXED(Music), OFF_ON[music._musicOn]);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
drawSlider(yp, _midiSliderX);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
str = Common::String::format("%s %s", FIXED(SoundEffects), OFF_ON[sound._digitized]);
|
||||
break;
|
||||
|
||||
case 5:
|
||||
str = Common::String::format("%s %s", FIXED(Voices), OFF_ON[sound._speechOn]);
|
||||
break;
|
||||
|
||||
case 6:
|
||||
drawSlider(yp, _digiSliderX);
|
||||
break;
|
||||
|
||||
case 7:
|
||||
if (!sound._voices) {
|
||||
color = INFO_BOTTOM;
|
||||
str = Common::String::format("%s %s", FIXED(TextWindows), FIXED(On));
|
||||
} else {
|
||||
str = Common::String::format("%s %s", FIXED(TextWindows), OFF_ON[vm._textWindowsOn]);
|
||||
}
|
||||
break;
|
||||
|
||||
case 8:
|
||||
str = FIXED(ChangeFont);
|
||||
break;
|
||||
|
||||
case 9:
|
||||
str = Common::String::format("%s %s", FIXED(TransparentMenus), OFF_ON[vm._transparentMenus]);
|
||||
break;
|
||||
|
||||
case 10:
|
||||
str = FIXED(Quit);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Unless we're doing one of the Slider Controls, print the text for the line
|
||||
if (idx != 3 && idx != 6) {
|
||||
int xp = (_surface.width() - _surface.stringWidth(str)) / 2;
|
||||
_surface.writeString(str, Common::Point(xp, yp), color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WidgetOptions::drawSlider(int yp, int sliderX) {
|
||||
int num = (_surface.fontHeight() + 4) & 0xfe;
|
||||
int sliderY = yp + num / 2 - 8;
|
||||
|
||||
_surface.fillRect(Common::Rect(4, sliderY - (num - 6) / 2, _surface.width() - 5,
|
||||
sliderY - (num - 6) / 2 + num - 1), TRANSPARENCY);
|
||||
_surface.fillRect(Common::Rect(_surface.widestChar(), sliderY + 2,
|
||||
_surface.width() - _surface.widestChar() - 1, sliderY + 4), INFO_MIDDLE);
|
||||
drawDialogRect(Common::Rect(_surface.widestChar(), sliderY, _surface.width() - _surface.widestChar(), sliderY + 6));
|
||||
|
||||
_surface.fillRect(Common::Rect(sliderX - 1, sliderY - (num - 6) / 2 + 2,
|
||||
sliderX + 2, sliderY - (num - 6) / 2 + num - 2), INFO_MIDDLE);
|
||||
drawDialogRect(Common::Rect(sliderX - 3, sliderY - (num - 6) / 2,
|
||||
sliderX + 4, sliderY - (num - 6) / 2 + num));
|
||||
|
||||
if (sliderX - 4 > _surface.widestChar())
|
||||
_surface.fillRect(Common::Rect(sliderX - 4, sliderY, sliderX - 3, sliderY + 4), INFO_BOTTOM);
|
||||
if (sliderX + 4 < _surface.width() - _surface.widestChar())
|
||||
_surface.fillRect(Common::Rect(sliderX + 4, sliderY, sliderX + 5, sliderY + 4), INFO_BOTTOM);
|
||||
}
|
||||
|
||||
} // End of namespace Tattoo
|
||||
|
||||
} // End of namespace Sherlock
|
||||
73
engines/sherlock/tattoo/widget_options.h
Normal file
73
engines/sherlock/tattoo/widget_options.h
Normal file
@@ -0,0 +1,73 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SHERLOCK_TATTOO_WIDGET_OPTIONS_H
|
||||
#define SHERLOCK_TATTOO_WIDGET_OPTIONS_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "sherlock/tattoo/widget_base.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
class SherlockEngine;
|
||||
|
||||
namespace Tattoo {
|
||||
|
||||
enum OptionRenderMode { OP_ALL = 0, OP_CONTENTS = 1, OP_NAMES = 2};
|
||||
|
||||
/**
|
||||
* Handles displaying the options dialog
|
||||
*/
|
||||
class WidgetOptions : public WidgetBase {
|
||||
private:
|
||||
int _midiSliderX, _digiSliderX;
|
||||
int _selector, _oldSelector;
|
||||
Common::Point _centerPos;
|
||||
|
||||
/**
|
||||
* Render the contents of the dialog onto the widget's surface
|
||||
*/
|
||||
void render(OptionRenderMode mode = OP_ALL);
|
||||
|
||||
/**
|
||||
* Draw a slider on the widget's surface
|
||||
*/
|
||||
void drawSlider(int yp, int sliderX);
|
||||
public:
|
||||
WidgetOptions(SherlockEngine *vm);
|
||||
~WidgetOptions() override {}
|
||||
|
||||
/**
|
||||
* Load and then display the options dialog
|
||||
*/
|
||||
void load();
|
||||
|
||||
/**
|
||||
* Handle event processing
|
||||
*/
|
||||
void handleEvents() override;
|
||||
};
|
||||
|
||||
} // End of namespace Tattoo
|
||||
|
||||
} // End of namespace Sherlock
|
||||
|
||||
#endif
|
||||
220
engines/sherlock/tattoo/widget_password.cpp
Normal file
220
engines/sherlock/tattoo/widget_password.cpp
Normal file
@@ -0,0 +1,220 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sherlock/tattoo/widget_password.h"
|
||||
#include "sherlock/tattoo/tattoo.h"
|
||||
#include "sherlock/tattoo/tattoo_fixed_text.h"
|
||||
#include "sherlock/tattoo/tattoo_user_interface.h"
|
||||
|
||||
#include "backends/keymapper/keymapper.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
namespace Tattoo {
|
||||
|
||||
WidgetPassword::WidgetPassword(SherlockEngine *vm) : WidgetBase(vm) {
|
||||
_blinkFlag = false;
|
||||
_blinkCounter = 0;
|
||||
_index = 0;
|
||||
_cursorColor = 192;
|
||||
_insert = true;
|
||||
}
|
||||
|
||||
void WidgetPassword::show() {
|
||||
TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
|
||||
ImageFile &images = *ui._interfaceImages;
|
||||
|
||||
// Set the up window to be centered on the screen
|
||||
_bounds = Common::Rect(_surface.widestChar() * 20 + 6, (_surface.fontHeight() + 7) * 2 + 3);
|
||||
_bounds.moveTo(SHERLOCK_SCREEN_WIDTH / 2 - _bounds.width() / 2, SHERLOCK_SCREEN_HEIGHT / 2 - _bounds.height() / 2);
|
||||
|
||||
// Create the surface
|
||||
_surface.create(_bounds.width(), _bounds.height());
|
||||
_surface.clear(TRANSPARENCY);
|
||||
makeInfoArea();
|
||||
|
||||
// Draw the header area
|
||||
_surface.writeString(FIXED(EnterPassword), Common::Point((_bounds.width() - _surface.stringWidth(FIXED(EnterPassword))) / 2, 5), INFO_TOP);
|
||||
_surface.hLine(3, _surface.fontHeight() + 7, _bounds.width() - 4, INFO_TOP);
|
||||
_surface.hLine(3, _surface.fontHeight() + 8, _bounds.width() - 4, INFO_MIDDLE);
|
||||
_surface.hLine(3, _surface.fontHeight() + 9, _bounds.width() - 4, INFO_BOTTOM);
|
||||
_surface.SHtransBlitFrom(images[4], Common::Point(0, _surface.fontHeight() + 7 - 1));
|
||||
_surface.SHtransBlitFrom(images[5], Common::Point(_bounds.width() - images[5]._width, _surface.fontHeight() + 7 - 1));
|
||||
|
||||
// Set the password entry data
|
||||
_cursorPos = Common::Point(_surface.widestChar(), _surface.fontHeight() + 12);
|
||||
_password = "";
|
||||
_index = 0;
|
||||
_cursorColor = 192;
|
||||
_insert = true;
|
||||
|
||||
// Show the dialog
|
||||
ui._menuMode = PASSWORD_MODE;
|
||||
summonWindow();
|
||||
|
||||
Common::Keymapper *keymapper = g_system->getEventManager()->getKeymapper();
|
||||
keymapper->getKeymap("tattoo")->setEnabled(false);
|
||||
keymapper->getKeymap("tattoo-password")->setEnabled(true);
|
||||
}
|
||||
|
||||
void WidgetPassword::handleEvents() {
|
||||
Events &events = *_vm->_events;
|
||||
TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
|
||||
Common::Point mousePos = events.mousePos();
|
||||
const Common::KeyCode &keycode = ui._keyState.keycode;
|
||||
Common::CustomEventType action = ui._action;
|
||||
char currentChar = (_index == (int)_password.size()) ? ' ' : _password[_index];
|
||||
int width = _surface.charWidth(currentChar);
|
||||
|
||||
if (!keycode && !action) {
|
||||
// Nothing entered, so keep blinking the cursor
|
||||
if (--_blinkCounter < 0) {
|
||||
_blinkCounter = 3;
|
||||
_blinkFlag = !_blinkFlag;
|
||||
|
||||
byte color, textColor;
|
||||
if (_blinkFlag) {
|
||||
textColor = 236;
|
||||
color = _cursorColor;
|
||||
} else {
|
||||
textColor = COMMAND_HIGHLIGHTED;
|
||||
color = TRANSPARENCY;
|
||||
}
|
||||
|
||||
// Draw the cursor and the character it's over
|
||||
_surface.fillRect(Common::Rect(_cursorPos.x, _cursorPos.y, _cursorPos.x + width, _cursorPos.y + _surface.fontHeight()), color);
|
||||
if (currentChar != ' ')
|
||||
_surface.writeString(Common::String::format("%c", _password[_index]), _cursorPos, textColor);
|
||||
}
|
||||
} else if (keycode == Common::KEYCODE_BACKSPACE && _index) {
|
||||
_cursorPos.x -= _surface.charWidth(_password[_index - 1]);
|
||||
|
||||
if (_insert)
|
||||
_password.deleteChar(_index - 1);
|
||||
else
|
||||
_password.setChar(' ', _index - 1);
|
||||
|
||||
// Redraw the text
|
||||
--_index;
|
||||
_surface.fillRect(Common::Rect(_cursorPos.x, _cursorPos.y, _bounds.width() - 9, _cursorPos.y +
|
||||
_surface.fontHeight() - 1), TRANSPARENCY);
|
||||
_surface.writeString(_password.c_str() + _index, _cursorPos, COMMAND_HIGHLIGHTED);
|
||||
} else if ((action == kActionTattooPasswordLeft && _index > 0)
|
||||
|| (action == kActionTattooPasswordRight && _index < (int)_password.size() && _cursorPos.x < (_bounds.width() - _surface.widestChar() - 3))
|
||||
|| (action == kActionTattooPasswordStart && _index > 0)
|
||||
|| (action == kActionTattooPasswordEnd)) {
|
||||
// Restore character the cursor was previously over
|
||||
_surface.fillRect(Common::Rect(_cursorPos.x, _cursorPos.y, _cursorPos.x + width, _cursorPos.y + _surface.fontHeight()), TRANSPARENCY);
|
||||
if (currentChar != ' ')
|
||||
_surface.writeString(Common::String::format("%c", _password[_index]), _cursorPos, COMMAND_HIGHLIGHTED);
|
||||
|
||||
switch (action) {
|
||||
case kActionTattooPasswordLeft:
|
||||
_cursorPos.x -= _surface.charWidth(_password[_index - 1]);
|
||||
--_index;
|
||||
break;
|
||||
case kActionTattooPasswordRight:
|
||||
_cursorPos.x += _surface.charWidth(_password[_index]);
|
||||
++_index;
|
||||
break;
|
||||
case kActionTattooPasswordStart:
|
||||
_cursorPos.x = _surface.widestChar();
|
||||
_index = 0;
|
||||
break;
|
||||
case kActionTattooPasswordEnd:
|
||||
_cursorPos.x = _surface.stringWidth(_password) + _surface.widestChar();
|
||||
_index = _password.size();
|
||||
|
||||
while (_index > 0 && _password[_index - 1] == ' ') {
|
||||
_cursorPos.x -= _surface.charWidth(_password[_index - 1]);
|
||||
--_index;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if (action == kActionTattooPasswordToggleInsertMode) {
|
||||
_insert = !_insert;
|
||||
_cursorColor = _insert ? 192 : 200;
|
||||
} else if (keycode == Common::KEYCODE_DELETE) {
|
||||
if (_index < (int)_password.size())
|
||||
_password.deleteChar(_index);
|
||||
|
||||
// Redraw the text
|
||||
_surface.fillRect(Common::Rect(_cursorPos.x, _cursorPos.y, _bounds.width() - 9, _cursorPos.y +
|
||||
_surface.fontHeight() - 1), TRANSPARENCY);
|
||||
_surface.writeString(_password.c_str() + _index, _cursorPos, COMMAND_HIGHLIGHTED);
|
||||
} else if (keycode == Common::KEYCODE_RETURN || keycode == Common::KEYCODE_ESCAPE) {
|
||||
close();
|
||||
return;
|
||||
} else if ((ui._keyState.ascii >= ' ') && (ui._keyState.ascii <= 'z')) {
|
||||
if (_cursorPos.x + _surface.charWidth(ui._keyState.ascii) < _bounds.width() - _surface.widestChar() - 3) {
|
||||
if (_insert)
|
||||
_password.insertChar(ui._keyState.ascii, _index);
|
||||
else
|
||||
_password.setChar(ui._keyState.ascii, _index);
|
||||
|
||||
// Redraw the text
|
||||
_surface.fillRect(Common::Rect(_cursorPos.x, _cursorPos.y, _bounds.width() - 9, _cursorPos.y +
|
||||
_surface.fontHeight() - 1), TRANSPARENCY);
|
||||
_surface.writeString(_password.c_str() + _index, _cursorPos, COMMAND_HIGHLIGHTED);
|
||||
|
||||
_cursorPos.x += _surface.charWidth(ui._keyState.ascii);
|
||||
++_index;
|
||||
}
|
||||
}
|
||||
|
||||
// Also handle clicking outside the window to abort
|
||||
if (events._firstPress && !_bounds.contains(mousePos))
|
||||
_outsideMenu = true;
|
||||
|
||||
if ((events._released || events._rightReleased) && _outsideMenu && !_bounds.contains(mousePos)) {
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
void WidgetPassword::close() {
|
||||
Talk &talk = *_vm->_talk;
|
||||
|
||||
Common::Keymapper *keymapper = g_system->getEventManager()->getKeymapper();
|
||||
keymapper->getKeymap("tattoo-password")->setEnabled(false);
|
||||
keymapper->getKeymap("tattoo")->setEnabled(true);
|
||||
|
||||
banishWindow();
|
||||
if (talk._talkToAbort)
|
||||
return;
|
||||
|
||||
// See if they entered the correct password
|
||||
Common::String correct1 = FIXED(CorrectPassword);
|
||||
Common::String correct2 = Common::String::format("%s?", FIXED(CorrectPassword));
|
||||
Common::String correct3 = Common::String::format("%s ?", FIXED(CorrectPassword));
|
||||
|
||||
if (!_password.compareToIgnoreCase(correct1) || !_password.compareToIgnoreCase(correct2)
|
||||
|| !_password.compareToIgnoreCase(correct3))
|
||||
// They got it correct
|
||||
_vm->setFlags(149);
|
||||
|
||||
talk.talkTo("LASC52P");
|
||||
}
|
||||
|
||||
} // End of namespace Tattoo
|
||||
|
||||
} // End of namespace Sherlock
|
||||
67
engines/sherlock/tattoo/widget_password.h
Normal file
67
engines/sherlock/tattoo/widget_password.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SHERLOCK_TATTOO_WIDGET_PASSWORD_H
|
||||
#define SHERLOCK_TATTOO_WIDGET_PASSWORD_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "sherlock/tattoo/widget_base.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
class SherlockEngine;
|
||||
|
||||
namespace Tattoo {
|
||||
|
||||
class WidgetPassword: public WidgetBase {
|
||||
private:
|
||||
Common::Point _cursorPos;
|
||||
Common::String _password;
|
||||
int _index;
|
||||
bool _blinkFlag;
|
||||
int _blinkCounter;
|
||||
byte _cursorColor;
|
||||
bool _insert;
|
||||
|
||||
/**
|
||||
* Close the window and check if the entered password is correct
|
||||
*/
|
||||
void close();
|
||||
public:
|
||||
WidgetPassword(SherlockEngine *vm);
|
||||
~WidgetPassword() override {}
|
||||
|
||||
/**
|
||||
* Show the password entry window
|
||||
*/
|
||||
void show();
|
||||
|
||||
/**
|
||||
* Handle event processing
|
||||
*/
|
||||
void handleEvents() override;
|
||||
};
|
||||
|
||||
} // End of namespace Tattoo
|
||||
|
||||
} // End of namespace Sherlock
|
||||
|
||||
#endif
|
||||
168
engines/sherlock/tattoo/widget_quit.cpp
Normal file
168
engines/sherlock/tattoo/widget_quit.cpp
Normal file
@@ -0,0 +1,168 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sherlock/tattoo/widget_quit.h"
|
||||
#include "sherlock/tattoo/tattoo.h"
|
||||
#include "sherlock/tattoo/tattoo_fixed_text.h"
|
||||
#include "sherlock/tattoo/tattoo_user_interface.h"
|
||||
|
||||
#include "backends/keymapper/keymapper.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
namespace Tattoo {
|
||||
|
||||
WidgetQuit::WidgetQuit(SherlockEngine *vm) : WidgetBase(vm) {
|
||||
_select = _oldSelect = -1;
|
||||
}
|
||||
|
||||
void WidgetQuit::show() {
|
||||
Events &events = *_vm->_events;
|
||||
TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
|
||||
ImageFile &images = *ui._interfaceImages;
|
||||
Common::Point mousePos = events.mousePos();
|
||||
const char *YES = FIXED(Yes);
|
||||
const char *NO = FIXED(No);
|
||||
|
||||
// Set up the display area
|
||||
_bounds = Common::Rect(_surface.stringWidth(FIXED(AreYouSureYou)) + _surface.widestChar() * 2,
|
||||
(_surface.fontHeight() + 7) * 4);
|
||||
_bounds.moveTo(mousePos.x - _bounds.width() / 2, mousePos.y - _bounds.height() / 2);
|
||||
|
||||
// Create the surface
|
||||
_surface.create(_bounds.width(), _bounds.height());
|
||||
_surface.clear(TRANSPARENCY);
|
||||
makeInfoArea();
|
||||
|
||||
// Draw the message text
|
||||
_surface.writeString(FIXED(AreYouSureYou), Common::Point((_surface.width() - _surface.stringWidth(FIXED(AreYouSureYou))) / 2, 5), INFO_TOP);
|
||||
_surface.writeString(FIXED(WishToQuit), Common::Point((_surface.width() - _surface.stringWidth(FIXED(WishToQuit))) / 2,
|
||||
_surface.fontHeight() + 9), INFO_TOP);
|
||||
|
||||
// Draw the horizontal bars separating the commands and the message
|
||||
int yp = (_surface.fontHeight() + 4) * 2 + 3;
|
||||
for (int idx = 0; idx < 2; ++idx) {
|
||||
_surface.SHtransBlitFrom(images[4], Common::Point(0, yp - 1));
|
||||
_surface.SHtransBlitFrom(images[5], Common::Point(_surface.width() - images[5]._width, yp - 1));
|
||||
_surface.hLine(3, yp, _surface.width() - 4, INFO_TOP);
|
||||
_surface.hLine(3, yp + 1, _surface.width() - 4, INFO_MIDDLE);
|
||||
_surface.hLine(3, yp + 2, _surface.width() - 4, INFO_BOTTOM);
|
||||
|
||||
const char *btn = (idx == 0) ? YES : NO;
|
||||
_surface.writeString(btn, Common::Point((_bounds.width() - _surface.stringWidth(btn)) / 2, yp + 5), INFO_TOP);
|
||||
yp += _surface.fontHeight() + 7;
|
||||
}
|
||||
|
||||
ui._menuMode = QUIT_MODE;
|
||||
summonWindow();
|
||||
|
||||
Common::Keymapper *keymapper = g_system->getEventManager()->getKeymapper();
|
||||
keymapper->getKeymap("tattoo")->setEnabled(false);
|
||||
keymapper->getKeymap("tattoo-quit-dialog")->setEnabled(true);
|
||||
}
|
||||
|
||||
void WidgetQuit::handleEvents() {
|
||||
Events &events = *_vm->_events;
|
||||
Talk &talk = *_vm->_talk;
|
||||
TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
|
||||
Common::Point mousePos = events.mousePos();
|
||||
Common::Rect yesRect(_bounds.left, _bounds.top + (_surface.fontHeight() + 4) * 2 + 3, _bounds.right,
|
||||
_bounds.top + (_surface.fontHeight() + 4) * 2 + 3 + _surface.fontHeight() + 7);
|
||||
Common::Rect noRect(_bounds.left, _bounds.top + (_surface.fontHeight() + 4) * 2 + _surface.fontHeight() + 10,
|
||||
_bounds.right, _bounds.top + (_surface.fontHeight() + 4) * 2 + 10 + _surface.fontHeight() * 2 + 7);
|
||||
Common::CustomEventType action = ui._action;
|
||||
Common::Keymapper *keymapper = g_system->getEventManager()->getKeymapper();
|
||||
|
||||
if (talk._talkToAbort)
|
||||
return;
|
||||
|
||||
// Determine the highlighted item
|
||||
_select = -1;
|
||||
if (yesRect.contains(mousePos))
|
||||
_select = 1;
|
||||
else if (noRect.contains(mousePos))
|
||||
_select = 0;
|
||||
|
||||
if (action != kActionNone) {
|
||||
|
||||
switch (action) {
|
||||
case kActionTattooQuitDialogNextOption:
|
||||
// If the mouse is not over any of the options, move the mouse so that it points to the first option
|
||||
if (_select == -1)
|
||||
events.warpMouse(Common::Point(_bounds.right - 10, _bounds.top + (_surface.fontHeight() + 4) * 2
|
||||
+ 3 + _surface.fontHeight() + 1));
|
||||
else if (_select == 1)
|
||||
events.warpMouse(Common::Point(mousePos.x, _bounds.top + (_surface.fontHeight() + 4) * 2
|
||||
+ 3 + _surface.fontHeight() * 2 + 11));
|
||||
else
|
||||
events.warpMouse(Common::Point(mousePos.x, _bounds.top + (_surface.fontHeight() + 4) * 2
|
||||
+ 3 + _surface.fontHeight() + 1));
|
||||
break;
|
||||
|
||||
case kActionTattooQuitDialogNo:
|
||||
keymapper->getKeymap("tattoo-quit-dialog")->setEnabled(false);
|
||||
keymapper->getKeymap("tattoo")->setEnabled(true);
|
||||
close();
|
||||
return;
|
||||
|
||||
case kActionTattooQuitDialogYes:
|
||||
keymapper->getKeymap("tattoo-quit-dialog")->setEnabled(false);
|
||||
keymapper->getKeymap("tattoo")->setEnabled(true);
|
||||
close();
|
||||
_vm->quitGame();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for change of the highlighted item
|
||||
if (_select != _oldSelect) {
|
||||
byte color = (_select == 1) ? COMMAND_HIGHLIGHTED : INFO_TOP;
|
||||
int yp = (_surface.fontHeight() + 4) * 2 + 8;
|
||||
_surface.writeString(FIXED(Yes), Common::Point((_surface.width() - _surface.stringWidth(FIXED(Yes))) / 2, yp), color);
|
||||
|
||||
color = (_select == 0) ? COMMAND_HIGHLIGHTED : INFO_TOP;
|
||||
yp += (_surface.fontHeight() + 7);
|
||||
_surface.writeString(FIXED(No), Common::Point((_surface.width() - _surface.stringWidth(FIXED(No))) / 2, yp), color);
|
||||
}
|
||||
_oldSelect = _select;
|
||||
|
||||
// Flag is they started pressing outside of the menu
|
||||
if (events._firstPress && !_bounds.contains(mousePos))
|
||||
_outsideMenu = true;
|
||||
|
||||
if (events._released || events._rightReleased) {
|
||||
events.clearEvents();
|
||||
keymapper->getKeymap("tattoo-options")->setEnabled(false);
|
||||
keymapper->getKeymap("tattoo-exit")->setEnabled(false);
|
||||
keymapper->getKeymap("tattoo")->setEnabled(true);
|
||||
close();
|
||||
if (_select == 1)
|
||||
// Yes selected
|
||||
_vm->quitGame();
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Tattoo
|
||||
|
||||
} // End of namespace Sherlock
|
||||
56
engines/sherlock/tattoo/widget_quit.h
Normal file
56
engines/sherlock/tattoo/widget_quit.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SHERLOCK_TATTOO_WIDGET_QUIT_H
|
||||
#define SHERLOCK_TATTOO_WIDGET_QUIT_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "sherlock/tattoo/widget_base.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
class SherlockEngine;
|
||||
|
||||
namespace Tattoo {
|
||||
|
||||
class WidgetQuit: public WidgetBase {
|
||||
private:
|
||||
int _select, _oldSelect;
|
||||
public:
|
||||
WidgetQuit(SherlockEngine *vm);
|
||||
~WidgetQuit() override {}
|
||||
|
||||
/**
|
||||
* Prompt the user whether to quit
|
||||
*/
|
||||
void show();
|
||||
|
||||
/**
|
||||
* Handle event processing
|
||||
*/
|
||||
void handleEvents() override;
|
||||
};
|
||||
|
||||
} // End of namespace Tattoo
|
||||
|
||||
} // End of namespace Sherlock
|
||||
|
||||
#endif
|
||||
491
engines/sherlock/tattoo/widget_talk.cpp
Normal file
491
engines/sherlock/tattoo/widget_talk.cpp
Normal file
@@ -0,0 +1,491 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sherlock/tattoo/widget_talk.h"
|
||||
#include "sherlock/tattoo/tattoo_fixed_text.h"
|
||||
#include "sherlock/tattoo/tattoo_journal.h"
|
||||
#include "sherlock/tattoo/tattoo_people.h"
|
||||
#include "sherlock/tattoo/tattoo_talk.h"
|
||||
#include "sherlock/tattoo/tattoo_scene.h"
|
||||
#include "sherlock/tattoo/tattoo_user_interface.h"
|
||||
#include "sherlock/tattoo/tattoo.h"
|
||||
|
||||
#include "backends/keymapper/keymapper.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
namespace Tattoo {
|
||||
|
||||
#define STATEMENT_NUM_X 6
|
||||
#define NUM_VISIBLE_TALK_LINES 6
|
||||
|
||||
WidgetTalk::WidgetTalk(SherlockEngine *vm) : WidgetBase(vm) {
|
||||
_talkScrollIndex = 0;
|
||||
_selector = _oldSelector = -1;
|
||||
_talkTextX = 0;
|
||||
_dialogTimer = 0;
|
||||
}
|
||||
|
||||
void WidgetTalk::getTalkWindowSize() {
|
||||
//TattooTalk &talk = *(TattooTalk *)_vm->_talk;
|
||||
int width, height;
|
||||
|
||||
// See how many statements are going to be available
|
||||
#if 0
|
||||
int numStatements = 0;
|
||||
for (uint idx = 0; idx < talk._statements.size(); ++idx) {
|
||||
if (talk._statements[idx]._talkMap != -1)
|
||||
++numStatements;
|
||||
}
|
||||
#endif
|
||||
|
||||
width = SHERLOCK_SCREEN_WIDTH * 2 / 3;
|
||||
|
||||
// Split up the questions into separate strings for each line
|
||||
_bounds = Common::Rect(width, 1);
|
||||
setStatementLines();
|
||||
|
||||
// Make sure that the window does not get too big
|
||||
if (_statementLines.size() < 7) {
|
||||
height = (_surface.fontHeight() + 1) * _statementLines.size() + 9;
|
||||
_scroll = false;
|
||||
} else {
|
||||
// Set up the height to a constrained amount, and add extra width for the scrollbar
|
||||
width += BUTTON_SIZE + 3;
|
||||
height = (_surface.fontHeight() + 1) * 6 + 9;
|
||||
_scroll = true;
|
||||
}
|
||||
|
||||
_bounds = Common::Rect(width, height);
|
||||
}
|
||||
|
||||
void WidgetTalk::load() {
|
||||
TattooPeople &people = *(TattooPeople *)_vm->_people;
|
||||
TattooScene &scene = *(TattooScene *)_vm->_scene;
|
||||
|
||||
// Figure out the window size
|
||||
getTalkWindowSize();
|
||||
|
||||
// Place the window centered above the player
|
||||
Common::Point pt;
|
||||
int scaleVal = scene.getScaleVal(people[HOLMES]._position);
|
||||
pt.x = people[HOLMES]._position.x / FIXED_INT_MULTIPLIER - _bounds.width() / 2;
|
||||
|
||||
if (scaleVal == SCALE_THRESHOLD) {
|
||||
pt.x += people[0].frameWidth() / 2;
|
||||
pt.y = people[HOLMES]._position.y / FIXED_INT_MULTIPLIER - people[HOLMES].frameHeight()
|
||||
- _bounds.height() - _surface.fontHeight();
|
||||
} else {
|
||||
pt.x += people[HOLMES]._imageFrame->sDrawXSize(scaleVal) / 2;
|
||||
pt.y = people[HOLMES]._position.y / FIXED_INT_MULTIPLIER - people[HOLMES]._imageFrame->sDrawYSize(scaleVal)
|
||||
- _bounds.height() - _surface.fontHeight();
|
||||
}
|
||||
|
||||
_bounds.moveTo(pt);
|
||||
|
||||
// Set up the surface
|
||||
_surface.create(_bounds.width(), _bounds.height());
|
||||
_surface.clear(TRANSPARENCY);
|
||||
|
||||
// Form the background for the new window
|
||||
makeInfoArea();
|
||||
|
||||
Common::Keymapper *keymapper = g_system->getEventManager()->getKeymapper();
|
||||
keymapper->getKeymap("tattoo")->setEnabled(false);
|
||||
keymapper->getKeymap("tattoo-scrolling")->setEnabled(true);
|
||||
keymapper->getKeymap("tattoo-talk")->setEnabled(true);
|
||||
keymapper->getKeymap("tattoo-exit")->setEnabled(true);
|
||||
}
|
||||
|
||||
void WidgetTalk::handleEvents() {
|
||||
Events &events = *_vm->_events;
|
||||
TattooJournal &journal = *(TattooJournal *)_vm->_journal;
|
||||
TattooPeople &people = *(TattooPeople *)_vm->_people;
|
||||
TattooScene &scene = *(TattooScene *)_vm->_scene;
|
||||
Sound &sound = *_vm->_sound;
|
||||
TattooTalk &talk = *(TattooTalk *)_vm->_talk;
|
||||
TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
|
||||
Common::Point mousePos = events.mousePos();
|
||||
Common::KeyCode keycode = ui._keyState.keycode;
|
||||
Common::CustomEventType action = ui._action;
|
||||
bool hotkey = false;
|
||||
bool callParrotFile = false;
|
||||
|
||||
// Handle scrollbar events
|
||||
ScrollHighlight oldHighlight = ui._scrollHighlight;
|
||||
handleScrollbarEvents(_talkScrollIndex, NUM_VISIBLE_TALK_LINES, _statementLines.size());
|
||||
|
||||
int oldScrollIndex = _talkScrollIndex;
|
||||
handleScrolling(_talkScrollIndex, NUM_VISIBLE_TALK_LINES, _statementLines.size());
|
||||
|
||||
// Only redraw the window if the scrollbar position has changed
|
||||
if (ui._scrollHighlight != oldHighlight || oldScrollIndex != _talkScrollIndex)
|
||||
render(HL_NO_HIGHLIGHTING);
|
||||
|
||||
// Flag if they started pressing outside of the window
|
||||
if (events._firstPress && !_bounds.contains(mousePos))
|
||||
_outsideMenu = true;
|
||||
|
||||
// Check for which statement they are pointing at
|
||||
_selector = -1;
|
||||
if (ui._scrollHighlight == SH_NONE) {
|
||||
if (Common::Rect(_bounds.left, _bounds.top + 5, _bounds.right - 3, _bounds.bottom - 5).contains(mousePos)) {
|
||||
if (_scroll) {
|
||||
// Disregard the scrollbar when setting the statement number
|
||||
if (!Common::Rect(_bounds.right - BUTTON_SIZE, _bounds.top, _bounds.right, _bounds.bottom).contains(mousePos))
|
||||
_selector = (mousePos.y - _bounds.top - 5) / (_surface.fontHeight() + 1) + _talkScrollIndex;
|
||||
} else {
|
||||
_selector = (mousePos.y - _bounds.top - 5) / (_surface.fontHeight() + 1);
|
||||
}
|
||||
|
||||
// Now translate the line number of the displayed line into the appropriate
|
||||
// Statement number or set it to 255 to indicate no Statement selected
|
||||
if (_selector >= 0 && _selector < (int)_statementLines.size())
|
||||
_selector = _statementLines[_selector]._num;
|
||||
else
|
||||
_selector = -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for the tab keys
|
||||
if ((action == kActionTattooTalkNext || action == kActionTattooTalkPrevious) && ui._scrollHighlight == SH_NONE) {
|
||||
if (_selector == -1) {
|
||||
_selector = _statementLines[_scroll ? _talkScrollIndex : 0]._num;
|
||||
|
||||
events.warpMouse(Common::Point(_bounds.right - BUTTON_SIZE - 10, _bounds.top + _surface.fontHeight() + 2));
|
||||
} else {
|
||||
if (action == kActionTattooTalkPrevious) {
|
||||
_selector = (mousePos.y - _bounds.top - 5) / (_surface.fontHeight() + 1) + _talkScrollIndex;
|
||||
if (_statementLines[_selector]._num == _statementLines[_talkScrollIndex]._num) {
|
||||
_selector = (_bounds.height() - 10) / (_surface.fontHeight() + 1) + _talkScrollIndex;
|
||||
} else {
|
||||
int idx = _selector;
|
||||
do {
|
||||
--_selector;
|
||||
} while (_selector > 0 && _statementLines[idx]._num == _statementLines[_selector]._num);
|
||||
}
|
||||
|
||||
int idx = _selector;
|
||||
while ((_statementLines[idx]._num == _statementLines[_selector - 1]._num) && (_selector > _talkScrollIndex))
|
||||
--_selector;
|
||||
} else {
|
||||
_selector = (mousePos.y - _bounds.top - 5) / (_surface.fontHeight() + 1) + _talkScrollIndex;
|
||||
if (_statementLines[_selector]._num == _statementLines[(_bounds.height() - 10) / (_surface.fontHeight() + 1) + _talkScrollIndex]._num) {
|
||||
_selector = _talkScrollIndex;
|
||||
} else {
|
||||
int idx = _selector;
|
||||
do {
|
||||
++_selector;
|
||||
} while (_selector < (int)_statementLines.size() && _statementLines[idx]._num == _statementLines[_selector]._num);
|
||||
}
|
||||
}
|
||||
|
||||
events.warpMouse(Common::Point(mousePos.x, _bounds.top + _surface.fontHeight() + 2 + (_surface.fontHeight() + 1)
|
||||
* (_selector - _talkScrollIndex)));
|
||||
_selector = _statementLines[_selector]._num;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle selecting a talk entry if a numeric key has been pressed
|
||||
if (keycode >= Common::KEYCODE_1 && keycode <= Common::KEYCODE_9) {
|
||||
int x = 0, y = 0, t;
|
||||
|
||||
for (t = 0; t < (int)_statementLines.size(); ++t) {
|
||||
if (t > 0 && _statementLines[x]._num != _statementLines[t]._num) {
|
||||
x = t;
|
||||
++y;
|
||||
}
|
||||
|
||||
if (y == (keycode - Common::KEYCODE_1)) {
|
||||
_selector = _statementLines[t]._num;
|
||||
_outsideMenu = false;
|
||||
hotkey = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Display the selected statement highlighted and reset the last statement.
|
||||
if (_selector != _oldSelector) {
|
||||
render(HL_CHANGED_HIGHLIGHTS);
|
||||
_oldSelector = _selector;
|
||||
}
|
||||
|
||||
if (events._released || events._rightReleased || action == kActionTattooExit || hotkey) {
|
||||
events.clearEvents();
|
||||
_dialogTimer = 0;
|
||||
ui._scrollHighlight = SH_NONE;
|
||||
|
||||
Common::Keymapper *keymapper = g_system->getEventManager()->getKeymapper();
|
||||
|
||||
// See if they want to close the menu (click outside the window or Escape pressed)
|
||||
if ((_outsideMenu && !_bounds.contains(mousePos)) || action == kActionTattooExit) {
|
||||
if (action == kActionTattooExit)
|
||||
_selector = -1;
|
||||
|
||||
talk.freeTalkVars();
|
||||
talk.pullSequence();
|
||||
|
||||
for (int idx = 1; idx < MAX_CHARACTERS; ++idx) {
|
||||
if (people[idx]._type == CHARACTER) {
|
||||
while (!people[idx]._pathStack.empty())
|
||||
people[idx].pullNPCPath();
|
||||
}
|
||||
}
|
||||
|
||||
banishWindow();
|
||||
ui._menuMode = scene._labTableScene ? LAB_MODE : STD_MODE;
|
||||
|
||||
keymapper->getKeymap("tattoo-scrolling")->setEnabled(false);
|
||||
keymapper->getKeymap("tattoo-talk")->setEnabled(false);
|
||||
keymapper->getKeymap("tattoo-exit")->setEnabled(false);
|
||||
keymapper->getKeymap("tattoo")->setEnabled(true);
|
||||
|
||||
if (scene._currentScene == WEARY_PUNT)
|
||||
callParrotFile = true;
|
||||
}
|
||||
|
||||
_outsideMenu = false;
|
||||
|
||||
// See if they have selected a statement to say
|
||||
if (_selector != -1) {
|
||||
if (!talk._talkHistory[talk._converseNum][_selector] && talk._statements[_selector]._journal)
|
||||
journal.record(talk._converseNum, _selector);
|
||||
talk._talkHistory[talk._converseNum][_selector] = true;
|
||||
|
||||
keymapper->getKeymap("tattoo-scrolling")->setEnabled(false);
|
||||
keymapper->getKeymap("tattoo-talk")->setEnabled(false);
|
||||
keymapper->getKeymap("tattoo-exit")->setEnabled(false);
|
||||
keymapper->getKeymap("tattoo")->setEnabled(true);
|
||||
|
||||
banishWindow();
|
||||
talk._speaker = _vm->readFlags(FLAG_PLAYER_IS_HOLMES) ? HOLMES : WATSON;
|
||||
_scroll = false;
|
||||
const byte *msg = (const byte *)talk._statements[_selector]._statement.c_str();
|
||||
talk.talkInterface(msg);
|
||||
|
||||
if (sound._speechOn)
|
||||
sound._talkSoundFile += Common::String::format("%02dA", _selector + 1);
|
||||
|
||||
int msgLen = MAX((int)talk._statements[_selector]._statement.size(), 160);
|
||||
people.setTalkSequence(talk._speaker);
|
||||
|
||||
talk.waitForMore(msgLen);
|
||||
if (talk._talkToAbort)
|
||||
return;
|
||||
|
||||
people.setListenSequence(talk._speaker);
|
||||
|
||||
do {
|
||||
talk._scriptSelect = _selector;
|
||||
talk._speaker = talk._talkTo;
|
||||
|
||||
// Make a copy of the reply (since talkTo can reload the statements list), and call talkTo
|
||||
Common::String reply = talk._statements[_selector]._reply;
|
||||
talk.doScript(reply);
|
||||
|
||||
// Reset the misc field in case any people changed their sequences
|
||||
for (int idx = 0; idx < MAX_CHARACTERS; ++idx)
|
||||
people[idx]._misc = 0;
|
||||
|
||||
if (!talk._talkToAbort) {
|
||||
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();
|
||||
}
|
||||
|
||||
// See if there is another talk file linked to this.
|
||||
if (!talk._statements[_selector]._linkFile.empty() && !talk._scriptMoreFlag) {
|
||||
Common::String linkFile = talk._statements[_selector]._linkFile;
|
||||
talk.freeTalkVars();
|
||||
talk.loadTalkFile(linkFile);
|
||||
|
||||
_talkScrollIndex = 0;
|
||||
int select = -1;
|
||||
_selector = _oldSelector = -1;
|
||||
|
||||
// Find the first statement that has all its flags set correctly
|
||||
for (uint idx = 0; idx < talk._statements.size() && select == -1; ++select) {
|
||||
if (!talk._statements[idx]._talkMap)
|
||||
select = idx;
|
||||
}
|
||||
|
||||
if (select == -1) {
|
||||
talk.freeTalkVars();
|
||||
talk.nothingToSay();
|
||||
return;
|
||||
}
|
||||
|
||||
// See is the new statement is in stealth mode
|
||||
talk._talkStealth = (talk._statements[select]._statement.hasPrefix("^")) ? 2 : 0;
|
||||
|
||||
// See if the new file is a standard file, a reply first file, or a Stealth Mode file
|
||||
if (!talk._statements[select]._statement.hasPrefix("*") && !talk._statements[select]._statement.hasPrefix("^")) {
|
||||
load();
|
||||
summonWindow();
|
||||
|
||||
setStatementLines();
|
||||
render(HL_NO_HIGHLIGHTING);
|
||||
break;
|
||||
} else {
|
||||
_selector = select;
|
||||
|
||||
if (!talk._talkHistory[talk._converseNum][_selector] && talk._statements[_selector]._journal)
|
||||
journal.record(talk._converseNum, _selector);
|
||||
|
||||
talk._talkHistory[talk._converseNum][_selector] = true;
|
||||
}
|
||||
} else {
|
||||
talk.freeTalkVars();
|
||||
talk.pullSequence();
|
||||
|
||||
for (int idx = 1; idx < MAX_CHARACTERS; ++idx) {
|
||||
if (people[idx]._type == CHARACTER)
|
||||
while (!people[idx]._pathStack.empty())
|
||||
people[idx].pullNPCPath();
|
||||
}
|
||||
|
||||
if (ui._menuMode != PASSWORD_MODE) {
|
||||
ui.banishWindow();
|
||||
ui._menuMode = scene._labTableScene ? LAB_MODE : STD_MODE;
|
||||
events.setCursor(ARROW);
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} while (!_vm->shouldQuit());
|
||||
|
||||
events.clearEvents();
|
||||
|
||||
// Now, if a script was pushed onto the script stack, restore them to allow the previous script to continue.
|
||||
talk.popStack();
|
||||
}
|
||||
}
|
||||
|
||||
if (callParrotFile)
|
||||
talk.talkTo("POUT52A");
|
||||
}
|
||||
|
||||
void WidgetTalk::render(Highlight highlightMode) {
|
||||
TattooTalk &talk = *(TattooTalk *)_vm->_talk;
|
||||
|
||||
if (highlightMode != HL_SCROLLBAR_ONLY) {
|
||||
int yp = 5;
|
||||
int statementNum = 1;
|
||||
|
||||
// Draw all the statements
|
||||
// Check whether scrolling has occurred, and if so, figure out what the starting
|
||||
// number for the first visible statement will be
|
||||
if (_talkScrollIndex) {
|
||||
for (int idx = 1; idx <= _talkScrollIndex; ++idx) {
|
||||
if (_statementLines[idx - 1]._num != _statementLines[idx]._num)
|
||||
++statementNum;
|
||||
}
|
||||
}
|
||||
|
||||
// Main drawing loop
|
||||
for (uint idx = _talkScrollIndex; idx < _statementLines.size() && yp < (_bounds.height() - _surface.fontHeight()); ++idx) {
|
||||
if (highlightMode == HL_NO_HIGHLIGHTING || _statementLines[idx]._num == _selector ||
|
||||
_statementLines[idx]._num == _oldSelector) {
|
||||
// Erase the line contents
|
||||
_surface.fillRect(Common::Rect(3, yp, _surface.width() - BUTTON_SIZE - 3, yp + _surface.fontHeight()), TRANSPARENCY);
|
||||
|
||||
// Different coloring based on whether the option has been previously chosen or not
|
||||
byte color = (!talk._talkHistory[talk._converseNum][_statementLines[idx]._num]) ?
|
||||
INFO_TOP : INFO_BOTTOM;
|
||||
|
||||
if (_statementLines[idx]._num == _selector && highlightMode == HL_CHANGED_HIGHLIGHTS)
|
||||
color = COMMAND_HIGHLIGHTED;
|
||||
|
||||
// See if it's the start of a new statement, so needs the statement number to be displayed
|
||||
if (!idx || _statementLines[idx]._num != _statementLines[idx - 1]._num) {
|
||||
Common::String numStr = Common::String::format("%d.", statementNum);
|
||||
_surface.writeString(numStr, Common::Point(STATEMENT_NUM_X, yp), color);
|
||||
}
|
||||
|
||||
// Display the statement line
|
||||
_surface.writeString(_statementLines[idx]._line, Common::Point(_talkTextX, yp), color);
|
||||
}
|
||||
yp += _surface.fontHeight() + 1;
|
||||
|
||||
// If the next line starts a new statement, then increment the statement number
|
||||
if (idx == (_statementLines.size() - 1) || _statementLines[idx]._num != _statementLines[idx + 1]._num)
|
||||
++statementNum;
|
||||
}
|
||||
}
|
||||
|
||||
// See if the scroll bar needs to be drawn
|
||||
if (_scroll && highlightMode != HL_CHANGED_HIGHLIGHTS)
|
||||
drawScrollBar(_talkScrollIndex, NUM_VISIBLE_TALK_LINES, _statementLines.size());
|
||||
}
|
||||
|
||||
void WidgetTalk::setStatementLines() {
|
||||
TattooTalk &talk = *(TattooTalk *)_vm->_talk;
|
||||
const char *numStr = "19.";
|
||||
|
||||
// See how many statements are going to be available
|
||||
int numStatements = 0;
|
||||
for (uint idx = 0; idx < talk._statements.size(); ++idx) {
|
||||
if (talk._statements[idx]._talkMap != -1)
|
||||
++numStatements;
|
||||
}
|
||||
|
||||
// If there are more lines than can be displayed in the interface window at one time, adjust the allowed
|
||||
// width to take into account needing a scrollbar
|
||||
int xSize = _scroll ? _bounds.width() - BUTTON_SIZE - 3 : _bounds.width();
|
||||
|
||||
// Also adjust the width to allow room for the statement numbers at the left edge of the display
|
||||
int n = (numStatements < 10) ? 1 : 0;
|
||||
xSize -= _surface.stringWidth(numStr + n) + _surface.widestChar() / 2 + 9;
|
||||
_talkTextX = _surface.stringWidth(numStr + n) + _surface.widestChar() / 4 + 6;
|
||||
_statementLines.clear();
|
||||
|
||||
for (uint statementNum = 0; statementNum < talk._statements.size(); ++statementNum) {
|
||||
// See if this statement meets all of its flag requirements
|
||||
if (talk._statements[statementNum]._talkMap != -1) {
|
||||
// Get the next statement text to process
|
||||
Common::String str = talk._statements[statementNum]._statement;
|
||||
|
||||
Common::StringArray statementLines;
|
||||
splitLines(str, statementLines, xSize, 999);
|
||||
|
||||
// Add the lines in
|
||||
for (uint idx = 0; idx < statementLines.size(); ++idx)
|
||||
_statementLines.push_back(StatementLine(statementLines[idx], statementNum));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WidgetTalk::refresh() {
|
||||
_talkScrollIndex = 0;
|
||||
_selector = _oldSelector = -1;
|
||||
|
||||
setStatementLines();
|
||||
render(HL_NO_HIGHLIGHTING);
|
||||
}
|
||||
|
||||
} // End of namespace Tattoo
|
||||
|
||||
} // End of namespace Sherlock
|
||||
94
engines/sherlock/tattoo/widget_talk.h
Normal file
94
engines/sherlock/tattoo/widget_talk.h
Normal file
@@ -0,0 +1,94 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SHERLOCK_TATTOO_WIDGET_TALK_H
|
||||
#define SHERLOCK_TATTOO_WIDGET_TALK_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "sherlock/tattoo/widget_base.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
class SherlockEngine;
|
||||
|
||||
namespace Tattoo {
|
||||
|
||||
enum Highlight { HL_NO_HIGHLIGHTING, HL_CHANGED_HIGHLIGHTS, HL_SCROLLBAR_ONLY };
|
||||
|
||||
/**
|
||||
* Handles displaying a dialog with conversation options the player can select from
|
||||
*/
|
||||
class WidgetTalk: public WidgetBase {
|
||||
struct StatementLine {
|
||||
Common::String _line;
|
||||
int _num;
|
||||
|
||||
StatementLine() : _num(0) {}
|
||||
StatementLine(const Common::String &line, int num) : _line(line), _num(num) {}
|
||||
};
|
||||
private:
|
||||
int _talkScrollIndex;
|
||||
Common::Array<StatementLine> _statementLines;
|
||||
int _selector, _oldSelector;
|
||||
int _talkTextX;
|
||||
uint32 _dialogTimer;
|
||||
|
||||
/**
|
||||
* Get the needed size for a talk window
|
||||
*/
|
||||
void getTalkWindowSize();
|
||||
|
||||
/**
|
||||
* Re-renders the contenst of the window to the widget's surface
|
||||
*/
|
||||
void render(Highlight highlightMode);
|
||||
|
||||
/**
|
||||
* This initializes the _statementLines array, which contains the talk options split up line
|
||||
* by line, as well as which statement a particular line is part of.
|
||||
*/
|
||||
void setStatementLines();
|
||||
public:
|
||||
WidgetTalk(SherlockEngine *vm);
|
||||
~WidgetTalk() override {}
|
||||
|
||||
/**
|
||||
* Figures out how many lines the available talk lines will take up, and opens a text window
|
||||
* of appropriate size
|
||||
*/
|
||||
void load();
|
||||
|
||||
/**
|
||||
* Refresh the talk display
|
||||
*/
|
||||
void refresh();
|
||||
|
||||
/**
|
||||
* Handle event processing
|
||||
*/
|
||||
void handleEvents() override;
|
||||
};
|
||||
|
||||
} // End of namespace Tattoo
|
||||
|
||||
} // End of namespace Sherlock
|
||||
|
||||
#endif
|
||||
228
engines/sherlock/tattoo/widget_text.cpp
Normal file
228
engines/sherlock/tattoo/widget_text.cpp
Normal file
@@ -0,0 +1,228 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sherlock/tattoo/widget_text.h"
|
||||
#include "sherlock/tattoo/tattoo_people.h"
|
||||
#include "sherlock/tattoo/tattoo_scene.h"
|
||||
#include "sherlock/tattoo/tattoo_user_interface.h"
|
||||
#include "sherlock/tattoo/tattoo.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
namespace Tattoo {
|
||||
|
||||
WidgetText::WidgetText(SherlockEngine *vm) : WidgetBase(vm) {
|
||||
}
|
||||
|
||||
void WidgetText::load(const Common::String &str, int speaker) {
|
||||
Screen &screen = *_vm->_screen;
|
||||
Talk &talk = *_vm->_talk;
|
||||
TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
|
||||
Common::StringArray lines;
|
||||
|
||||
int width = SHERLOCK_SCREEN_WIDTH / 3;
|
||||
int height;
|
||||
|
||||
for (;;) {
|
||||
splitLines(str, lines, width - _surface.widestChar() * 2, 100);
|
||||
height = (screen.fontHeight() + 1) * lines.size() + 9;
|
||||
|
||||
if ((width - _surface.widestChar() * 2 > height * 3 / 2) || (width - _surface.widestChar() * 2
|
||||
> SHERLOCK_SCREEN_WIDTH * 3 / 4))
|
||||
break;
|
||||
|
||||
width += (width / 4);
|
||||
}
|
||||
|
||||
// See if it's only a single line long
|
||||
if (height == _surface.fontHeight() + 10) {
|
||||
width = _surface.widestChar() * 2 + 6;
|
||||
|
||||
const char *strP = str.c_str();
|
||||
while (*strP && (*strP < talk._opcodes[OP_SWITCH_SPEAKER] || *strP == talk._opcodes[OP_NULL]))
|
||||
width += _surface.charWidth(*strP++);
|
||||
}
|
||||
|
||||
_bounds = Common::Rect(width, height);
|
||||
|
||||
if (speaker == -1) {
|
||||
// No speaker specified, so center window on look position
|
||||
_bounds.translate(ui._lookPos.x - width / 2, ui._lookPos.y - height / 2);
|
||||
} else {
|
||||
// Speaker specified, so center the window above them
|
||||
centerWindowOnSpeaker(speaker);
|
||||
}
|
||||
|
||||
render(str);
|
||||
}
|
||||
|
||||
void WidgetText::centerWindowOnSpeaker(int speaker) {
|
||||
TattooPeople &people = *(TattooPeople *)_vm->_people;
|
||||
TattooScene &scene = *(TattooScene *)_vm->_scene;
|
||||
Common::Point pt;
|
||||
|
||||
speaker &= 0x7f;
|
||||
bool flag = _vm->readFlags(FLAG_PLAYER_IS_HOLMES);
|
||||
if (people[HOLMES]._type == CHARACTER && ((speaker == HOLMES && flag) || (speaker == WATSON && !flag))) {
|
||||
// Place the window centered above the player
|
||||
pt.x = people[HOLMES]._position.x / FIXED_INT_MULTIPLIER - _bounds.width() / 2;
|
||||
|
||||
int scaleVal = scene.getScaleVal(people[HOLMES]._position);
|
||||
if (scaleVal == SCALE_THRESHOLD) {
|
||||
pt.x += people[HOLMES].frameWidth() / 2;
|
||||
pt.y = people[HOLMES]._position.y / FIXED_INT_MULTIPLIER - people[HOLMES].frameHeight()
|
||||
- _bounds.height() - _surface.fontHeight();
|
||||
} else {
|
||||
pt.x += people[HOLMES]._imageFrame->sDrawXSize(scaleVal) / 2;
|
||||
pt.y = people[HOLMES]._position.y / FIXED_INT_MULTIPLIER - people[HOLMES]._imageFrame->sDrawYSize(scaleVal)
|
||||
- _bounds.height() - _surface.fontHeight();
|
||||
}
|
||||
} else {
|
||||
pt.y = -1;
|
||||
|
||||
// Check each NPC to see if they are the one that is talking
|
||||
for (int idx = 1; idx < MAX_CHARACTERS; ++idx) {
|
||||
// WORKAROUND: Fixes an original game bug where the positioning for Watson's dialogs
|
||||
// during conversations at the Park Lake lake scene is in the incorrect position
|
||||
if (speaker == 1 && scene._currentScene == 30)
|
||||
continue;
|
||||
|
||||
if (people[idx]._type == CHARACTER) {
|
||||
if (!scumm_strnicmp(people[idx]._npcName.c_str(), people._characters[speaker]._portrait, 4)) {
|
||||
// Place the window above the player
|
||||
pt.x = people[idx]._position.x / FIXED_INT_MULTIPLIER - _bounds.width() / 2;
|
||||
|
||||
int scaleVal = scene.getScaleVal(people[idx]._position);
|
||||
if (scaleVal == SCALE_THRESHOLD) {
|
||||
pt.x += people[idx].frameWidth() / 2;
|
||||
pt.y = people[idx]._position.y / FIXED_INT_MULTIPLIER - people[idx].frameHeight()
|
||||
- _bounds.height() - _surface.fontHeight();
|
||||
}
|
||||
else {
|
||||
pt.x += people[idx]._imageFrame->sDrawXSize(scaleVal) / 2;
|
||||
pt.y = people[idx]._position.y / FIXED_INT_MULTIPLIER - people[idx]._imageFrame->sDrawYSize(scaleVal)
|
||||
- _bounds.height() - _surface.fontHeight();
|
||||
}
|
||||
|
||||
if (pt.y < 0)
|
||||
pt.y = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pt.y == -1) {
|
||||
for (uint idx = 0; idx < scene._bgShapes.size(); ++idx) {
|
||||
Object &obj = scene._bgShapes[idx];
|
||||
|
||||
if (obj._type == ACTIVE_BG_SHAPE && !scumm_strnicmp(obj._name.c_str(), people._characters[speaker]._portrait, 4)) {
|
||||
// Place the window centered above the character
|
||||
pt.x = obj._position.x - _bounds.width() / 2;
|
||||
pt.y = obj._position.y - _bounds.height() - _surface.fontHeight();
|
||||
if (pt.y < 0)
|
||||
pt.y = 0;
|
||||
if (obj._scaleVal == SCALE_THRESHOLD)
|
||||
pt.x += obj.frameWidth() / 2;
|
||||
else
|
||||
pt.x += obj._imageFrame->sDrawXSize(obj._scaleVal) / 2;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pt.y == -1) {
|
||||
pt.x = SHERLOCK_SCREEN_WIDTH / 2 - _bounds.width() / 2;
|
||||
pt.y = SHERLOCK_SCREEN_HEIGHT / 2 - _bounds.height() / 2;
|
||||
}
|
||||
}
|
||||
|
||||
_bounds.moveTo(pt);
|
||||
}
|
||||
|
||||
void WidgetText::render(const Common::String &str) {
|
||||
Common::StringArray lines;
|
||||
_remainingText = splitLines(str, lines, _bounds.width() - _surface.widestChar() * 2,
|
||||
_bounds.height() / (_surface.fontHeight() + 1));
|
||||
|
||||
// Allocate a surface for the window
|
||||
_surface.create(_bounds.width(), _bounds.height());
|
||||
_surface.clear(TRANSPARENCY);
|
||||
|
||||
// Form the background for the new window
|
||||
makeInfoArea();
|
||||
|
||||
int yp = 5;
|
||||
for (int lineNum = 0; yp < (_bounds.height() - _surface.fontHeight() / 2); ++lineNum) {
|
||||
_surface.writeString(lines[lineNum], Common::Point(_surface.widestChar(), yp), INFO_TOP);
|
||||
yp += _surface.fontHeight() + 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------*/
|
||||
|
||||
WidgetMessage::WidgetMessage(SherlockEngine *vm) : WidgetBase(vm) {
|
||||
_menuCounter = 0;
|
||||
}
|
||||
|
||||
void WidgetMessage::load(const Common::String &str, int time) {
|
||||
Events &events = *_vm->_events;
|
||||
Common::Point mousePos = events.mousePos();
|
||||
_menuCounter = time;
|
||||
|
||||
// Set up the bounds for the dialog to be a single line
|
||||
_bounds = Common::Rect(_surface.stringWidth(str) + _surface.widestChar() * 2 + 6, _surface.fontHeight() + 10);
|
||||
_bounds.moveTo(mousePos.x - _bounds.width() / 2, mousePos.y - _bounds.height() / 2);
|
||||
|
||||
// Allocate a surface for the window
|
||||
_surface.create(_bounds.width(), _bounds.height());
|
||||
_surface.clear(TRANSPARENCY);
|
||||
|
||||
// Form the background for the new window and write the line of text
|
||||
makeInfoArea();
|
||||
_surface.writeString(str, Common::Point(_surface.widestChar() + 3, 5), INFO_TOP);
|
||||
}
|
||||
|
||||
void WidgetMessage::handleEvents() {
|
||||
Events &events = *_vm->_events;
|
||||
TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
|
||||
WidgetBase::handleEvents();
|
||||
|
||||
--_menuCounter;
|
||||
|
||||
// Check if a mouse or keypress has occurred, or the display counter has expired
|
||||
if (events._pressed || events._released || events._rightPressed || events._rightReleased ||
|
||||
ui._keyState.keycode || !_menuCounter || ui._action) {
|
||||
// Close the window
|
||||
banishWindow();
|
||||
|
||||
// Reset cursor and switch back to standard mode
|
||||
events.setCursor(ARROW);
|
||||
events.clearEvents();
|
||||
ui._action = kActionNone;
|
||||
ui._oldBgFound = -1;
|
||||
ui._menuMode = STD_MODE;
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Tattoo
|
||||
|
||||
} // End of namespace Sherlock
|
||||
79
engines/sherlock/tattoo/widget_text.h
Normal file
79
engines/sherlock/tattoo/widget_text.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SHERLOCK_TATTOO_WIDGET_TEXT_H
|
||||
#define SHERLOCK_TATTOO_WIDGET_TEXT_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "sherlock/tattoo/widget_base.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
class SherlockEngine;
|
||||
|
||||
namespace Tattoo {
|
||||
|
||||
class WidgetText: public WidgetBase {
|
||||
private:
|
||||
/**
|
||||
* Center the area the dialog will be drawn on above a given speaker
|
||||
*/
|
||||
void centerWindowOnSpeaker(int speaker);
|
||||
|
||||
/**
|
||||
* Build up the text dialog based on the previously set bounds
|
||||
*/
|
||||
void render(const Common::String &str);
|
||||
public:
|
||||
Common::String _remainingText;
|
||||
public:
|
||||
WidgetText(SherlockEngine *vm);
|
||||
~WidgetText() override {}
|
||||
|
||||
/**
|
||||
* Load the data for the text window
|
||||
*/
|
||||
void load(const Common::String &str, int speaker = -1);
|
||||
};
|
||||
|
||||
class WidgetMessage : public WidgetBase {
|
||||
private:
|
||||
int _menuCounter;
|
||||
public:
|
||||
WidgetMessage(SherlockEngine *vm);
|
||||
~WidgetMessage() override {}
|
||||
|
||||
/**
|
||||
* Load the data for the text window
|
||||
*/
|
||||
void load(const Common::String &str, int time);
|
||||
|
||||
/**
|
||||
* Handle event processing
|
||||
*/
|
||||
void handleEvents() override;
|
||||
};
|
||||
|
||||
} // End of namespace Tattoo
|
||||
|
||||
} // End of namespace Sherlock
|
||||
|
||||
#endif
|
||||
223
engines/sherlock/tattoo/widget_tooltip.cpp
Normal file
223
engines/sherlock/tattoo/widget_tooltip.cpp
Normal file
@@ -0,0 +1,223 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sherlock/tattoo/widget_tooltip.h"
|
||||
#include "sherlock/tattoo/tattoo_map.h"
|
||||
#include "sherlock/tattoo/tattoo_scene.h"
|
||||
#include "sherlock/tattoo/tattoo_user_interface.h"
|
||||
#include "sherlock/tattoo/tattoo.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
namespace Tattoo {
|
||||
|
||||
#define MAX_TOOLTIP_WIDTH 150
|
||||
|
||||
void WidgetTooltipBase::draw() {
|
||||
Screen &screen = *_vm->_screen;
|
||||
|
||||
// If there was a previously drawn frame in a different position that hasn't yet been erased, then erase it
|
||||
if (_oldBounds.width() > 0 && _oldBounds != _bounds)
|
||||
erase();
|
||||
|
||||
if (_bounds.width() > 0 && !_surface.empty()) {
|
||||
restrictToScreen();
|
||||
|
||||
// Blit the affected area to the screen
|
||||
screen.slamRect(_bounds);
|
||||
|
||||
// Draw the widget directly onto the screen. Unlike other widgets, we don't draw to the back buffer,
|
||||
// since nothing should be drawing on top of tooltips, so there's no need to store in the back buffer
|
||||
screen.SHtransBlitFrom(_surface, Common::Point(_bounds.left - screen._currentScroll.x,
|
||||
_bounds.top - screen._currentScroll.y));
|
||||
|
||||
// Store a copy of the drawn area for later erasing
|
||||
_oldBounds = _bounds;
|
||||
}
|
||||
}
|
||||
|
||||
void WidgetTooltipBase::erase() {
|
||||
Screen &screen = *_vm->_screen;
|
||||
|
||||
if (_oldBounds.width() > 0) {
|
||||
// Restore the affected area from the back buffer to the screen
|
||||
screen.slamRect(_oldBounds);
|
||||
|
||||
// Reset the old bounds so it won't be erased again
|
||||
_oldBounds = Common::Rect(0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------*/
|
||||
|
||||
WidgetTooltip::WidgetTooltip(SherlockEngine *vm) : WidgetTooltipBase (vm), _offsetY(0) {
|
||||
}
|
||||
|
||||
void WidgetTooltip::setText(const Common::String &strIn) {
|
||||
Events &events = *_vm->_events;
|
||||
Common::Point mousePos = events.mousePos();
|
||||
bool reset = false;
|
||||
Common::String str = Fonts::unescape(strIn);
|
||||
|
||||
// Make sure that the description is present
|
||||
if (!str.empty()) {
|
||||
int width = _surface.stringWidth(str) + 2;
|
||||
int height = _surface.stringHeight(str) + 2;
|
||||
Common::String line1 = str, line2 = "";
|
||||
|
||||
// See if we need to split it into two lines
|
||||
if (width > MAX_TOOLTIP_WIDTH) {
|
||||
// Go forward word by word to find out where to split the line
|
||||
const char *s = str.c_str();
|
||||
const char *space = nullptr;
|
||||
int dif = 10000;
|
||||
|
||||
for (;;) {
|
||||
// Find end of next word
|
||||
s = strchr(s + 1, ' ');
|
||||
|
||||
if (s == nullptr) {
|
||||
// Reached end of string
|
||||
if (space != nullptr) {
|
||||
line1 = Common::String(str.c_str(), space);
|
||||
line2 = Common::String(space + 1);
|
||||
height = _surface.stringHeight(line1) + _surface.stringHeight(line2) + 4;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Found space separating words, so see what width the string up to now is
|
||||
Common::String tempLine1 = Common::String(str.c_str(), s);
|
||||
Common::String tempLine2 = Common::String(s + 1);
|
||||
int width1 = _surface.stringWidth(tempLine1);
|
||||
int width2 = _surface.stringWidth(tempLine2);
|
||||
|
||||
// See if we've found a split point that results in a less overall width
|
||||
if (ABS(width1 - width2) < dif) {
|
||||
// Found a better split point
|
||||
dif = ABS(width1 - width2);
|
||||
space = s;
|
||||
line1 = tempLine1;
|
||||
line2 = tempLine2;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// No line split needed
|
||||
height = _surface.stringHeight(str) + 2;
|
||||
}
|
||||
|
||||
// Reallocate the text surface with the new size
|
||||
_surface.create(width, height);
|
||||
_surface.clear(TRANSPARENCY);
|
||||
|
||||
if (line2.empty()) {
|
||||
// Only a single line
|
||||
_surface.writeFancyString(str, Common::Point(0, 0), BLACK, INFO_TOP);
|
||||
} else {
|
||||
// Two lines to display
|
||||
int xp, yp;
|
||||
xp = (width - _surface.stringWidth(line1) - 2) / 2;
|
||||
_surface.writeFancyString(line1, Common::Point(xp, 0), BLACK, INFO_TOP);
|
||||
|
||||
xp = (width - _surface.stringWidth(line2) - 2) / 2;
|
||||
yp = _surface.stringHeight(line1) + 2;
|
||||
_surface.writeFancyString(line2, Common::Point(xp, yp), BLACK, INFO_TOP);
|
||||
}
|
||||
|
||||
// Set the initial display position for the tooltip text
|
||||
int tagX = mousePos.x - width / 2;
|
||||
int tagY = mousePos.y - height - _offsetY;
|
||||
|
||||
_bounds = Common::Rect(tagX, tagY, tagX + width, tagY + height);
|
||||
} else {
|
||||
reset = true;
|
||||
}
|
||||
|
||||
if (reset && !_surface.empty()) {
|
||||
_surface.free();
|
||||
}
|
||||
}
|
||||
|
||||
void WidgetTooltip::handleEvents() {
|
||||
Events &events = *_vm->_events;
|
||||
Common::Point mousePos = events.mousePos();
|
||||
|
||||
// Set the new position for the tooltip
|
||||
int xp = mousePos.x - _bounds.width() / 2;
|
||||
int yp = mousePos.y - _bounds.height() - _offsetY;
|
||||
|
||||
_bounds.moveTo(xp, yp);
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------*/
|
||||
|
||||
void WidgetSceneTooltip::handleEvents() {
|
||||
Events &events = *_vm->_events;
|
||||
People &people = *_vm->_people;
|
||||
Scene &scene = *_vm->_scene;
|
||||
TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
|
||||
Common::Point mousePos = events.mousePos();
|
||||
|
||||
// See if thay are pointing at a different object and we need to regenerate the tooltip text
|
||||
if (ui._bgFound != ui._oldBgFound || (ui._bgFound != -1 && _surface.empty()) ||
|
||||
ui._arrowZone != ui._oldArrowZone || (ui._arrowZone != -1 && _surface.empty())) {
|
||||
// See if there is a new object to display text for
|
||||
if ((ui._bgFound != -1 && (ui._bgFound != ui._oldBgFound || (ui._bgFound != -1 && _surface.empty()))) ||
|
||||
(ui._arrowZone != -1 && (ui._arrowZone != ui._oldArrowZone || (ui._arrowZone != -1 && _surface.empty())))) {
|
||||
Common::String str;
|
||||
if (ui._bgFound != -1) {
|
||||
// Clear the Arrow Zone fields so it won't think we're displaying an Arrow Zone cursor
|
||||
if (scene._currentScene != OVERHEAD_MAP2)
|
||||
ui._arrowZone = ui._oldArrowZone = -1;
|
||||
|
||||
// Get the description string
|
||||
str = (ui._bgFound < 1000) ? scene._bgShapes[ui._bgFound]._description :
|
||||
people[ui._bgFound - 1000]._description;
|
||||
|
||||
// WORKAORUND: On the train ride to Cambridge, don't show any tooltips
|
||||
if (scene._currentScene == TRAIN_RIDE)
|
||||
str = "";
|
||||
} else {
|
||||
// Get the exit zone description
|
||||
str = scene._exits[ui._arrowZone]._dest;
|
||||
}
|
||||
|
||||
setText(str.hasPrefix(" ") ? Common::String() : str);
|
||||
} else if ((ui._bgFound == -1 && ui._oldBgFound != -1) || (ui._arrowZone == -1 && ui._oldArrowZone != -1)) {
|
||||
setText("");
|
||||
}
|
||||
|
||||
ui._oldBgFound = ui._bgFound;
|
||||
} else {
|
||||
// Set the new position for the tooltip
|
||||
int tagX = CLIP(mousePos.x - _bounds.width() / 2, 0, SHERLOCK_SCREEN_WIDTH - _bounds.width());
|
||||
int tagY = MAX(mousePos.y - _bounds.height() - _offsetY, 0);
|
||||
_bounds.moveTo(tagX, tagY);
|
||||
}
|
||||
|
||||
ui._oldArrowZone = ui._arrowZone;
|
||||
|
||||
WidgetTooltip::handleEvents();
|
||||
}
|
||||
|
||||
} // End of namespace Tattoo
|
||||
|
||||
} // End of namespace Sherlock
|
||||
88
engines/sherlock/tattoo/widget_tooltip.h
Normal file
88
engines/sherlock/tattoo/widget_tooltip.h
Normal file
@@ -0,0 +1,88 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SHERLOCK_TATTOO_WIDGET_TOOLTIP_H
|
||||
#define SHERLOCK_TATTOO_WIDGET_TOOLTIP_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/rect.h"
|
||||
#include "sherlock/tattoo/widget_base.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
class SherlockEngine;
|
||||
|
||||
namespace Tattoo {
|
||||
|
||||
class WidgetTooltipBase : public WidgetBase {
|
||||
public:
|
||||
WidgetTooltipBase(SherlockEngine *vm) : WidgetBase(vm) {}
|
||||
~WidgetTooltipBase() override {}
|
||||
|
||||
/**
|
||||
* Erase any previous display of the widget on the screen
|
||||
*/
|
||||
void erase() override;
|
||||
|
||||
/**
|
||||
* Update the display of the widget on the screen
|
||||
*/
|
||||
void draw() override;
|
||||
};
|
||||
|
||||
class WidgetTooltip: public WidgetTooltipBase {
|
||||
public:
|
||||
int _offsetY;
|
||||
public:
|
||||
WidgetTooltip(SherlockEngine *vm);
|
||||
~WidgetTooltip() override {}
|
||||
|
||||
/**
|
||||
* Set the text for the tooltip
|
||||
*/
|
||||
void setText(const Common::String &str);
|
||||
|
||||
/**
|
||||
* Handle updating the tooltip state
|
||||
*/
|
||||
void handleEvents() override;
|
||||
};
|
||||
|
||||
class WidgetSceneTooltip : public WidgetTooltip {
|
||||
public:
|
||||
WidgetSceneTooltip(SherlockEngine *vm) : WidgetTooltip(vm) {}
|
||||
|
||||
/**
|
||||
* Handle updating the tooltip state
|
||||
*/
|
||||
void handleEvents() override;
|
||||
};
|
||||
|
||||
class WidgetMapTooltip : public WidgetTooltip {
|
||||
public:
|
||||
WidgetMapTooltip(SherlockEngine *vm) : WidgetTooltip(vm) {}
|
||||
};
|
||||
|
||||
} // End of namespace Tattoo
|
||||
|
||||
} // End of namespace Sherlock
|
||||
|
||||
#endif
|
||||
328
engines/sherlock/tattoo/widget_verbs.cpp
Normal file
328
engines/sherlock/tattoo/widget_verbs.cpp
Normal file
@@ -0,0 +1,328 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sherlock/tattoo/widget_verbs.h"
|
||||
#include "sherlock/tattoo/tattoo_fixed_text.h"
|
||||
#include "sherlock/tattoo/tattoo_scene.h"
|
||||
#include "sherlock/tattoo/tattoo_user_interface.h"
|
||||
#include "sherlock/tattoo/tattoo_people.h"
|
||||
#include "sherlock/tattoo/tattoo.h"
|
||||
|
||||
#include "backends/keymapper/keymapper.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
namespace Tattoo {
|
||||
|
||||
WidgetVerbs::WidgetVerbs(SherlockEngine *vm) : WidgetBase(vm) {
|
||||
_selector = _oldSelector = -1;
|
||||
_outsideMenu = false;
|
||||
}
|
||||
|
||||
void WidgetVerbs::load(bool objectsOn) {
|
||||
Events &events = *_vm->_events;
|
||||
TattooPeople &people = *(TattooPeople *)_vm->_people;
|
||||
Talk &talk = *_vm->_talk;
|
||||
TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
|
||||
Common::Point mousePos = events.mousePos();
|
||||
bool isWatson = false;
|
||||
|
||||
if (talk._talkToAbort)
|
||||
return;
|
||||
|
||||
ui._activeObj = ui._bgFound;
|
||||
_outsideMenu = false;
|
||||
_verbCommands.clear();
|
||||
_selector = _oldSelector = -1;
|
||||
|
||||
Common::Keymapper *keymapper = g_system->getEventManager()->getKeymapper();
|
||||
keymapper->getKeymap("tattoo")->setEnabled(false);
|
||||
keymapper->getKeymap("tattoo-exit")->setEnabled(true);
|
||||
|
||||
// Check if we need to show options for the highlighted object
|
||||
if (objectsOn) {
|
||||
// Set the verb list accordingly, depending on the target being a
|
||||
// person or an object
|
||||
if (ui._personFound) {
|
||||
TattooPerson &person = people[ui._activeObj - 1000];
|
||||
|
||||
if (!scumm_strnicmp(person._npcName.c_str(), "WATS", 4))
|
||||
isWatson = true;
|
||||
|
||||
|
||||
if (scumm_strnicmp(person._examine.c_str(), "_EXIT", 5))
|
||||
_verbCommands.push_back(FIXED(Look));
|
||||
|
||||
_verbCommands.push_back(FIXED(Talk));
|
||||
|
||||
// Add any extra active verbs from the NPC's verb list
|
||||
for (int idx = 0; idx < 2; ++idx) {
|
||||
if (!person._use[idx]._verb.empty() && !person._use[idx]._verb.hasPrefix(" ") &&
|
||||
(person._use[idx]._target.empty() || person._use[idx]._target.hasPrefix(" "))) {
|
||||
_verbCommands.push_back(person._use[idx]._verb);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!scumm_strnicmp(ui._bgShape->_name.c_str(), "WATS", 4))
|
||||
// Looking at Watson
|
||||
isWatson = true;
|
||||
|
||||
if (scumm_strnicmp(ui._bgShape->_examine.c_str(), "_EXIT", 5))
|
||||
// It's not an exit, so include Look as an option
|
||||
_verbCommands.push_back(FIXED(Look));
|
||||
|
||||
if (ui._bgShape->_aType == PERSON)
|
||||
_verbCommands.push_back(FIXED(Talk));
|
||||
|
||||
// Add any extra active verbs from the object's verb list
|
||||
for (int idx = 0; idx < 6; ++idx) {
|
||||
UseType &use = ui._bgShape->_use[idx];
|
||||
if (!use._verb.empty() && !use._verb.hasPrefix(" ") && !use._verb.hasPrefix("*") &&
|
||||
(use._target.empty() || use._target.hasPrefix("*") || use._target.hasPrefix(" "))) {
|
||||
_verbCommands.push_back(use._verb);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If clicked on Watson, have Journal as an option
|
||||
if (isWatson)
|
||||
_verbCommands.push_back(FIXED(Journal));
|
||||
|
||||
// Add the system commands
|
||||
_verbCommands.push_back(FIXED(Inventory));
|
||||
_verbCommands.push_back(FIXED(Options));
|
||||
|
||||
// Figure out the needed width to show the commands
|
||||
int width = 0;
|
||||
for (uint idx = 0; idx < _verbCommands.size(); ++idx)
|
||||
width = MAX(width, _surface.stringWidth(_verbCommands[idx]));
|
||||
width += _surface.widestChar() * 2 + 6;
|
||||
int height = (_surface.fontHeight() + 7) * _verbCommands.size() + 3;
|
||||
|
||||
// Set the bounds
|
||||
_bounds = Common::Rect(width, height);
|
||||
_bounds.moveTo(mousePos.x - _bounds.width() / 2, mousePos.y - _bounds.height() / 2);
|
||||
|
||||
// Render the window on the internal surface
|
||||
render();
|
||||
}
|
||||
|
||||
void WidgetVerbs::render() {
|
||||
TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
|
||||
ImageFile &images = *ui._interfaceImages;
|
||||
|
||||
// Create the drawing surface
|
||||
_surface.create(_bounds.width(), _bounds.height());
|
||||
_surface.clear(TRANSPARENCY);
|
||||
|
||||
// Draw basic background
|
||||
makeInfoArea();
|
||||
|
||||
// Draw the verb commands and the lines separating them
|
||||
for (uint idx = 0; idx < _verbCommands.size(); ++idx) {
|
||||
_surface.writeString(_verbCommands[idx], Common::Point((_bounds.width() - _surface.stringWidth(_verbCommands[idx])) / 2,
|
||||
(_surface.fontHeight() + 7) * idx + 5), INFO_TOP);
|
||||
|
||||
if (idx < (_verbCommands.size() - 1)) {
|
||||
_surface.hLine(3, (_surface.fontHeight() + 7) * (idx + 1), _bounds.width() - 4, INFO_TOP);
|
||||
_surface.hLine(3, (_surface.fontHeight() + 7) * (idx + 1) + 1, _bounds.width() - 4, INFO_MIDDLE);
|
||||
_surface.hLine(3, (_surface.fontHeight() + 7) * (idx + 1) + 2, _bounds.width() - 4, INFO_BOTTOM);
|
||||
|
||||
_surface.SHtransBlitFrom(images[4], Common::Point(0, (_surface.fontHeight() + 7) * (idx + 1) - 1));
|
||||
_surface.SHtransBlitFrom(images[5], Common::Point(_bounds.width() - images[5]._width,
|
||||
(_surface.fontHeight() + 7) * (idx + 1) - 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WidgetVerbs::handleEvents() {
|
||||
Events &events = *_vm->_events;
|
||||
FixedText &fixedText = *_vm->_fixedText;
|
||||
People &people = *_vm->_people;
|
||||
TattooScene &scene = *(TattooScene *)_vm->_scene;
|
||||
Talk &talk = *_vm->_talk;
|
||||
TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
|
||||
Common::Point mousePos = events.mousePos();
|
||||
Common::Keymapper *keymapper = g_system->getEventManager()->getKeymapper();
|
||||
|
||||
Common::String strLook = fixedText.getText(kFixedText_Look);
|
||||
Common::String strTalk = fixedText.getText(kFixedText_Talk);
|
||||
Common::String strJournal = fixedText.getText(kFixedText_Journal);
|
||||
|
||||
checkTabbingKeys(_verbCommands.size());
|
||||
|
||||
// Highlight verb display as necessary
|
||||
highlightVerbControls();
|
||||
|
||||
// Flag if the user has started pressing the button with the cursor outsie the menu
|
||||
if (events._firstPress && !_bounds.contains(mousePos))
|
||||
_outsideMenu = true;
|
||||
|
||||
// See if they released the mouse button
|
||||
if (events._released || events._rightReleased) {
|
||||
// See if they want to close the menu (they clicked outside of the menu)
|
||||
if (!_bounds.contains(mousePos)) {
|
||||
if (_outsideMenu) {
|
||||
if (events._rightReleased) {
|
||||
// Change to the item (if any) that was right-clicked on, and re-draw the verb menu
|
||||
ui._bgFound = scene.findBgShape(mousePos);
|
||||
ui._personFound = ui._bgFound >= 1000;
|
||||
ui._bgShape = ui._personFound || ui._bgFound == -1 ? nullptr : &scene._bgShapes[ui._bgFound];
|
||||
|
||||
bool noDesc = false;
|
||||
if (ui._personFound) {
|
||||
if (people[ui._bgFound - 1000]._description.empty() || people[ui._bgFound - 1000]._description.hasPrefix(" "))
|
||||
noDesc = true;
|
||||
} else if (ui._bgFound != -1) {
|
||||
if (ui._bgShape->_description.empty() || ui._bgShape->_description.hasPrefix(" "))
|
||||
noDesc = true;
|
||||
} else {
|
||||
noDesc = true;
|
||||
}
|
||||
|
||||
// Call the Routine to turn on the Commands for this Object
|
||||
load(!noDesc);
|
||||
} else {
|
||||
// Close the window and clear the events
|
||||
banishWindow();
|
||||
events.clearEvents();
|
||||
|
||||
keymapper->getKeymap("tattoo-exit")->setEnabled(false);
|
||||
keymapper->getKeymap("tattoo")->setEnabled(true);
|
||||
|
||||
// Reset the active UI mode
|
||||
ui._menuMode = scene._labTableScene ? LAB_MODE : STD_MODE;
|
||||
}
|
||||
}
|
||||
} else if (_bounds.contains(mousePos) && _selector != -1) {
|
||||
// Mouse is within the menu
|
||||
// Erase the menu
|
||||
banishWindow();
|
||||
events.clearEvents();
|
||||
|
||||
keymapper->getKeymap("tattoo-exit")->setEnabled(false);
|
||||
keymapper->getKeymap("tattoo")->setEnabled(true);
|
||||
|
||||
// See if they are activating the Look Command
|
||||
if (!_verbCommands[_selector].compareToIgnoreCase(strLook)) {
|
||||
ui._bgFound = ui._activeObj;
|
||||
if (ui._activeObj >= 1000) {
|
||||
ui._personFound = true;
|
||||
} else {
|
||||
ui._personFound = false;
|
||||
ui._bgShape = &scene._bgShapes[ui._activeObj];
|
||||
}
|
||||
|
||||
ui.lookAtObject();
|
||||
|
||||
} else if (!_verbCommands[_selector].compareToIgnoreCase(strTalk)) {
|
||||
// Talk command is being activated
|
||||
talk.initTalk(ui._activeObj);
|
||||
ui._activeObj = -1;
|
||||
|
||||
} else if (!_verbCommands[_selector].compareToIgnoreCase(strJournal)) {
|
||||
ui.doJournal();
|
||||
|
||||
// See if we're in a Lab Table scene
|
||||
ui._menuMode = scene._labTableScene ? LAB_MODE : STD_MODE;
|
||||
} else if (_selector >= ((int)_verbCommands.size() - 2)) {
|
||||
switch (_selector - (int)_verbCommands.size() + 2) {
|
||||
case 0:
|
||||
// Inventory
|
||||
ui.doInventory(2);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
// Options
|
||||
ui.doControls();
|
||||
break;
|
||||
|
||||
default:
|
||||
ui._menuMode = scene._labTableScene ? LAB_MODE : STD_MODE;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// If they have selected anything else, process it
|
||||
people[HOLMES].gotoStand();
|
||||
|
||||
if (ui._activeObj < 1000) {
|
||||
for (int idx = 0; idx < 6; ++idx) {
|
||||
if (!_verbCommands[_selector].compareToIgnoreCase(scene._bgShapes[ui._activeObj]._use[idx]._verb)) {
|
||||
// See if they are Picking this object up
|
||||
if (!scene._bgShapes[ui._activeObj]._use[idx]._target.compareToIgnoreCase("*PICKUP"))
|
||||
ui.pickUpObject(ui._activeObj);
|
||||
else
|
||||
ui.checkAction(scene._bgShapes[ui._activeObj]._use[idx], ui._activeObj);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int idx = 0; idx < 2; ++idx) {
|
||||
if (!_verbCommands[_selector].compareToIgnoreCase(people[ui._activeObj - 1000]._use[idx]._verb))
|
||||
ui.checkAction(people[ui._activeObj - 1000]._use[idx], ui._activeObj);
|
||||
}
|
||||
}
|
||||
|
||||
ui._activeObj = -1;
|
||||
if (ui._menuMode != MESSAGE_MODE) {
|
||||
// See if we're in a Lab Table Room
|
||||
ui._menuMode = scene._labTableScene ? LAB_MODE : STD_MODE;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (ui._action == kActionTattooExit) {
|
||||
// User closing the menu with the exit action
|
||||
banishWindow();
|
||||
ui._menuMode = scene._labTableScene ? LAB_MODE : STD_MODE;
|
||||
|
||||
keymapper->getKeymap("tattoo-exit")->setEnabled(false);
|
||||
keymapper->getKeymap("tattoo")->setEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
void WidgetVerbs::highlightVerbControls() {
|
||||
Events &events = *_vm->_events;
|
||||
Screen &screen = *_vm->_screen;
|
||||
Common::Point mousePos = events.mousePos();
|
||||
|
||||
// Get highlighted verb
|
||||
_selector = -1;
|
||||
Common::Rect bounds = _bounds;
|
||||
bounds.grow(-3);
|
||||
if (bounds.contains(mousePos))
|
||||
_selector = (mousePos.y - bounds.top) / (screen.fontHeight() + 7);
|
||||
|
||||
// See if a new verb is being pointed at
|
||||
if (_selector != _oldSelector) {
|
||||
// Redraw the verb list
|
||||
for (int idx = 0; idx < (int)_verbCommands.size(); ++idx) {
|
||||
byte color = (idx == _selector) ? (byte)COMMAND_HIGHLIGHTED : (byte)INFO_TOP;
|
||||
_surface.writeString(_verbCommands[idx], Common::Point((_bounds.width() - screen.stringWidth(_verbCommands[idx])) / 2,
|
||||
(screen.fontHeight() + 7) * idx + 5), color);
|
||||
}
|
||||
|
||||
_oldSelector = _selector;
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Tattoo
|
||||
|
||||
} // End of namespace Sherlock
|
||||
71
engines/sherlock/tattoo/widget_verbs.h
Normal file
71
engines/sherlock/tattoo/widget_verbs.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SHERLOCK_TATTOO_WIDGET_VERBS_H
|
||||
#define SHERLOCK_TATTOO_WIDGET_VERBS_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/str-array.h"
|
||||
#include "sherlock/tattoo/widget_base.h"
|
||||
|
||||
namespace Sherlock {
|
||||
|
||||
class SherlockEngine;
|
||||
|
||||
namespace Tattoo {
|
||||
|
||||
class WidgetVerbs: public WidgetBase {
|
||||
private:
|
||||
int _selector, _oldSelector;
|
||||
bool _outsideMenu;
|
||||
|
||||
/**
|
||||
* Highlights the controls for the verb list
|
||||
*/
|
||||
void highlightVerbControls();
|
||||
|
||||
/**
|
||||
* Renders the window on an internal surface for later drawing on-screen
|
||||
*/
|
||||
void render();
|
||||
public:
|
||||
Common::StringArray _verbCommands;
|
||||
public:
|
||||
WidgetVerbs(SherlockEngine *vm);
|
||||
~WidgetVerbs() override {}
|
||||
|
||||
/**
|
||||
* Turns on the menu with all the verbs that are available for the given object
|
||||
*/
|
||||
void load(bool objectsOn);
|
||||
|
||||
/**
|
||||
* Process input for the dialog
|
||||
*/
|
||||
void handleEvents() override;
|
||||
};
|
||||
|
||||
} // End of namespace Tattoo
|
||||
|
||||
} // End of namespace Sherlock
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user