Initial commit
This commit is contained in:
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
|
||||
Reference in New Issue
Block a user