mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-15 06:08:35 +00:00
454779e30a
When the map was displayed, there would often be a noticeable glitch where the map jumped from the upper left corner to the saved position. This removes that initial map display and relies on scrolling to always move it to the correct position. This fixes #10850
439 lines
13 KiB
C++
439 lines
13 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
*/
|
|
|
|
#include "sherlock/tattoo/tattoo_map.h"
|
|
#include "sherlock/tattoo/tattoo_scene.h"
|
|
#include "sherlock/tattoo/tattoo.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, 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);
|
|
|
|
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 keyboard
|
|
if (events.kbHit()) {
|
|
Common::KeyState keyState = events.getKey();
|
|
|
|
switch (keyState.keycode) {
|
|
case Common::KEYCODE_HOME:
|
|
_targetScroll.x = 0;
|
|
_targetScroll.y = 0;
|
|
break;
|
|
|
|
case Common::KEYCODE_END:
|
|
_targetScroll.x = screen._backBuffer1.width() - SHERLOCK_SCREEN_WIDTH;
|
|
_targetScroll.y = screen._backBuffer1.height() - SHERLOCK_SCREEN_HEIGHT;
|
|
break;
|
|
|
|
case Common::KEYCODE_PAGEUP:
|
|
_targetScroll.y -= SHERLOCK_SCREEN_HEIGHT;
|
|
if (_targetScroll.y < 0)
|
|
_targetScroll.y = 0;
|
|
break;
|
|
|
|
case Common::KEYCODE_PAGEDOWN:
|
|
_targetScroll.y += SHERLOCK_SCREEN_HEIGHT;
|
|
if (_targetScroll.y > (screen._backBuffer1.height() - SHERLOCK_SCREEN_HEIGHT))
|
|
_targetScroll.y = screen._backBuffer1.height() - SHERLOCK_SCREEN_HEIGHT;
|
|
break;
|
|
|
|
case Common::KEYCODE_SPACE:
|
|
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());
|
|
|
|
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::String 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, 0, 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
|