1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "sherlock/tattoo/tattoo_map.h"
24 #include "sherlock/tattoo/tattoo_scene.h"
25 #include "sherlock/tattoo/tattoo.h"
26
27 namespace Sherlock {
28
29 namespace Tattoo {
30
31 #define MAP_NAME_COLOR 131
32 #define CLOSEUP_STEPS 30
33 #define SCROLL_SPEED 16
34
35 /*-------------------------------------------------------------------------*/
36
clear()37 void MapEntry::clear() {
38 _iconNum = -1;
39 _description = "";
40 }
41
42 /*-------------------------------------------------------------------------*/
43
TattooMap(SherlockEngine * vm)44 TattooMap::TattooMap(SherlockEngine *vm) : Map(vm), _mapTooltip(vm) {
45 _iconImages = nullptr;
46 _bgFound = _oldBgFound = 0;
47
48 loadData();
49 }
50
show()51 int TattooMap::show() {
52 Debugger &debugger = *_vm->_debugger;
53 Events &events = *_vm->_events;
54 Music &music = *_vm->_music;
55 Resources &res = *_vm->_res;
56 TattooScene &scene = *(TattooScene *)_vm->_scene;
57 Screen &screen = *_vm->_screen;
58 int result = 0;
59
60 // Check if we need to keep track of how many times player has been to the map
61 for (uint idx = 0; idx < scene._sceneTripCounters.size(); ++idx) {
62 SceneTripEntry &entry = scene._sceneTripCounters[idx];
63
64 if (entry._sceneNumber == OVERHEAD_MAP || entry._sceneNumber == OVERHEAD_MAP2) {
65 if (--entry._numTimes == 0) {
66 _vm->setFlagsDirect(entry._flag);
67 scene._sceneTripCounters.remove_at(idx);
68 }
69 }
70 }
71
72 if (music._musicOn) {
73 // See if Holmes or Watson is the active character
74 Common::String song;
75 if (_vm->readFlags(FLAG_PLAYER_IS_HOLMES))
76 // Player is Holmes
77 song = "Cue9";
78 else if (_vm->readFlags(FLAG_ALT_MAP_MUSIC))
79 song = "Cue8";
80 else
81 song = "Cue7";
82
83 if (music.loadSong(song)) {
84 music.startSong();
85 }
86 }
87
88 screen.initPaletteFade(1364485);
89
90 // Load the custom mouse cursors for the map
91 ImageFile cursors("omouse.vgs");
92 events.setCursor(cursors[0]._frame);
93 events.warpMouse();
94
95 // Load the data for the map
96 _iconImages = new ImageFile("mapicons.vgs");
97 loadData();
98
99 // Load the palette
100 Common::SeekableReadStream *stream = res.load("map.pal");
101 stream->read(screen._cMap, PALETTE_SIZE);
102 screen.translatePalette(screen._cMap);
103 delete stream;
104
105 // Load the map image and draw it to the back buffer
106 ImageFile *map = new ImageFile("map.vgs");
107 screen._backBuffer1.create(SHERLOCK_SCREEN_WIDTH * 2, SHERLOCK_SCREEN_HEIGHT * 2);
108 screen._backBuffer1.SHblitFrom((*map)[0], Common::Point(0, 0));
109 screen.activateBackBuffer1();
110 delete map;
111
112 screen.clear();
113 screen.setPalette(screen._cMap);
114 drawMapIcons();
115
116 // Copy the map drawn in the back buffer to the secondary back buffer
117 screen._backBuffer2.create(SHERLOCK_SCREEN_WIDTH * 2, SHERLOCK_SCREEN_HEIGHT * 2);
118 screen._backBuffer2.SHblitFrom(screen._backBuffer1);
119
120 // Display the built map to the screen
121 screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
122
123 // Set initial scroll position
124 _targetScroll = _bigPos;
125 screen._currentScroll = Common::Point(-1, -1);
126
127 do {
128 // Allow for event processing and get the current mouse position
129 events.pollEventsAndWait();
130 events.setButtonState();
131 Common::Point mousePos = events.screenMousePos();
132
133 if (debugger._showAllLocations == LOC_REFRESH) {
134 drawMapIcons();
135 screen.slamArea(screen._currentScroll.x, screen._currentScroll.y, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_WIDTH);
136 }
137
138 music.checkSongProgress();
139 checkMapNames(true);
140
141 if (mousePos.x < (SHERLOCK_SCREEN_WIDTH / 6))
142 _targetScroll.x -= 2 * SCROLL_SPEED * (SHERLOCK_SCREEN_WIDTH / 6 - mousePos.x) / (SHERLOCK_SCREEN_WIDTH / 6);
143 if (mousePos.x > (SHERLOCK_SCREEN_WIDTH * 5 / 6))
144 _targetScroll.x += 2 * SCROLL_SPEED * (mousePos.x - (SHERLOCK_SCREEN_WIDTH * 5 / 6)) / (SHERLOCK_SCREEN_WIDTH / 6);
145 if (mousePos.y < (SHERLOCK_SCREEN_HEIGHT / 6))
146 _targetScroll.y -= 2 * SCROLL_SPEED * (SHERLOCK_SCREEN_HEIGHT / 6 - mousePos.y) / (SHERLOCK_SCREEN_HEIGHT / 6);
147 if (mousePos.y > (SHERLOCK_SCREEN_HEIGHT * 5 / 6))
148 _targetScroll.y += 2 * SCROLL_SPEED * (mousePos.y - SHERLOCK_SCREEN_HEIGHT * 5 / 6) / (SHERLOCK_SCREEN_HEIGHT / 6);
149
150 if (_targetScroll.x < 0)
151 _targetScroll.x = 0;
152 if ((_targetScroll.x + SHERLOCK_SCREEN_WIDTH) > screen._backBuffer1.width())
153 _targetScroll.x = screen._backBuffer1.width() - SHERLOCK_SCREEN_WIDTH;
154 if (_targetScroll.y < 0)
155 _targetScroll.y = 0;
156 if ((_targetScroll.y + SHERLOCK_SCREEN_HEIGHT) > screen._backBuffer1.height())
157 _targetScroll.y = screen._backBuffer1.height() - SHERLOCK_SCREEN_HEIGHT;
158
159 // Check the keyboard
160 if (events.kbHit()) {
161 Common::KeyState keyState = events.getKey();
162
163 switch (keyState.keycode) {
164 case Common::KEYCODE_HOME:
165 _targetScroll.x = 0;
166 _targetScroll.y = 0;
167 break;
168
169 case Common::KEYCODE_END:
170 _targetScroll.x = screen._backBuffer1.width() - SHERLOCK_SCREEN_WIDTH;
171 _targetScroll.y = screen._backBuffer1.height() - SHERLOCK_SCREEN_HEIGHT;
172 break;
173
174 case Common::KEYCODE_PAGEUP:
175 _targetScroll.y -= SHERLOCK_SCREEN_HEIGHT;
176 if (_targetScroll.y < 0)
177 _targetScroll.y = 0;
178 break;
179
180 case Common::KEYCODE_PAGEDOWN:
181 _targetScroll.y += SHERLOCK_SCREEN_HEIGHT;
182 if (_targetScroll.y > (screen._backBuffer1.height() - SHERLOCK_SCREEN_HEIGHT))
183 _targetScroll.y = screen._backBuffer1.height() - SHERLOCK_SCREEN_HEIGHT;
184 break;
185
186 case Common::KEYCODE_SPACE:
187 events._pressed = false;
188 events._oldButtons = 0;
189 events._released = true;
190 break;
191
192 default:
193 break;
194 }
195 }
196
197 // Handle any scrolling of the map
198 if (screen._currentScroll != _targetScroll) {
199 // If there is a Text description being displayed, restore the area under it
200 _mapTooltip.erase();
201
202 screen._currentScroll = _targetScroll;
203
204 checkMapNames(false);
205 screen.slamArea(_targetScroll.x, _targetScroll.y, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
206 }
207
208 // Handling if a location has been clicked on
209 if (events._released && _bgFound != -1) {
210 // If there is a Text description being displayed, restore the area under it
211 _mapTooltip.erase();
212
213 // Save the current scroll position on the map
214 _bigPos = screen._currentScroll;
215
216 showCloseUp(_bgFound);
217 result = _bgFound + 1;
218 }
219 } while (!result && !_vm->shouldQuit());
220
221 music.stopMusic();
222 events.clearEvents();
223 _mapTooltip.banishWindow();
224
225 // Reset the back buffers back to standard size
226 screen._backBuffer1.create(SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
227 screen._backBuffer2.create(SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
228 screen.activateBackBuffer1();
229
230 return result;
231 }
232
loadData()233 void TattooMap::loadData() {
234 Resources &res = *_vm->_res;
235 char c;
236
237 Common::SeekableReadStream *stream = res.load("map.txt");
238
239 _data.resize(100);
240 for (uint idx = 0; idx < _data.size(); ++idx)
241 _data[idx].clear();
242
243 do
244 {
245 // Find the start of the number
246 do {
247 c = stream->readByte();
248 if (stream->pos() >= stream->size())
249 break;
250 } while (c < '0' || c > '9');
251 if (stream->pos() >= stream->size())
252 break;
253
254 // Get the scene number
255 Common::String locStr;
256 locStr += c;
257 while ((c = stream->readByte()) != '.')
258 locStr += c;
259 MapEntry &mapEntry = _data[atoi(locStr.c_str()) - 1];
260
261 // Get the location name
262 while (stream->readByte() != '"')
263 ;
264
265 while ((c = stream->readByte()) != '"')
266 mapEntry._description += c;
267
268 // Find the ( specifying the (X,Y) position of the Icon
269 while (stream->readByte() != '(')
270 ;
271
272 // Get the X Position of the icon
273 Common::String numStr;
274 while ((c = stream->readByte()) != ',')
275 numStr += c;
276 mapEntry.x = atoi(numStr.c_str());
277
278 // Get the Y position of the icon
279 numStr = "";
280 while ((c = stream->readByte()) != ')')
281 numStr += c;
282 mapEntry.y = atoi(numStr.c_str());
283
284 // Find and get the location's icon number
285 while (stream->readByte() != '#')
286 ;
287
288 Common::String iconStr;
289 while (stream->pos() < stream->size() && (c = stream->readByte()) != '\r')
290 iconStr += c;
291
292 mapEntry._iconNum = atoi(iconStr.c_str()) - 1;
293 } while (stream->pos() < stream->size());
294
295 delete stream;
296 }
297
drawMapIcons()298 void TattooMap::drawMapIcons() {
299 Debugger &debugger = *_vm->_debugger;
300 Screen &screen = *_vm->_screen;
301
302 for (uint idx = 0; idx < _data.size(); ++idx) {
303 if (debugger._showAllLocations != LOC_DISABLED)
304 _vm->setFlagsDirect(idx + 1);
305
306 if (_data[idx]._iconNum != -1 && _vm->readFlags(idx + 1)) {
307 MapEntry &mapEntry = _data[idx];
308 ImageFrame &img = (*_iconImages)[mapEntry._iconNum];
309 screen._backBuffer1.SHtransBlitFrom(img._frame, Common::Point(mapEntry.x - img._width / 2,
310 mapEntry.y - img._height / 2));
311 }
312 }
313
314 if (debugger._showAllLocations == LOC_REFRESH)
315 debugger._showAllLocations = LOC_ALL;
316 }
317
checkMapNames(bool slamIt)318 void TattooMap::checkMapNames(bool slamIt) {
319 Events &events = *_vm->_events;
320 Common::Point mapPos = events.mousePos();
321
322 // See if the mouse is pointing at any of the map locations
323 _bgFound = -1;
324
325 for (uint idx = 0; idx < _data.size(); ++idx) {
326 if (_data[idx]._iconNum != -1 && _vm->readFlags(idx + 1)) {
327 MapEntry &mapEntry = _data[idx];
328 ImageFrame &img = (*_iconImages)[mapEntry._iconNum];
329 Common::Rect r(mapEntry.x - img._width / 2, mapEntry.y - img._height / 2,
330 mapEntry.x + img._width / 2, mapEntry.y + img._height / 2);
331
332 if (r.contains(mapPos)) {
333 _bgFound = idx;
334 break;
335 }
336 }
337 }
338
339 // Handle updating the tooltip
340 if (_bgFound != _oldBgFound) {
341 if (_bgFound == -1) {
342 _mapTooltip.setText("");
343 } else {
344 const Common::String &desc = _data[_bgFound]._description;
345 _mapTooltip.setText(desc);
346 }
347
348 _oldBgFound = _bgFound;
349 }
350
351 _mapTooltip.handleEvents();
352 if (slamIt)
353 _mapTooltip.draw();
354 }
355
restoreArea(const Common::Rect & bounds)356 void TattooMap::restoreArea(const Common::Rect &bounds) {
357 Screen &screen = *_vm->_screen;
358
359 Common::Rect r = bounds;
360 r.clip(Common::Rect(0, 0, screen._backBuffer1.width(), screen._backBuffer1.height()));
361
362 if (!r.isEmpty())
363 screen._backBuffer1.SHblitFrom(screen._backBuffer2, Common::Point(r.left, r.top), r);
364 }
365
showCloseUp(int closeUpNum)366 void TattooMap::showCloseUp(int closeUpNum) {
367 Events &events = *_vm->_events;
368 Screen &screen = *_vm->_screen;
369
370 // Hide the cursor
371 events.hideCursor();
372
373 // Get the closeup images
374 Common::String fname = Common::String::format("res%02d.vgs", closeUpNum + 1);
375 ImageFile pic(fname);
376
377 Point32 closeUp(_data[closeUpNum].x * 100, _data[closeUpNum].y * 100);
378 Point32 delta((SHERLOCK_SCREEN_WIDTH / 2 - closeUp.x / 100) * 100 / CLOSEUP_STEPS,
379 (SHERLOCK_SCREEN_HEIGHT / 2 - closeUp.y / 100) * 100 / CLOSEUP_STEPS);
380 Common::Rect oldBounds(closeUp.x / 100, closeUp.y / 100, closeUp.x / 100 + 1, closeUp.y / 100 + 1);
381 int size = 64;
382 int n = 256;
383 int deltaVal = 512;
384 bool minimize = false;
385 int scaleVal, newSize;
386
387 do {
388 scaleVal = n;
389 newSize = pic[0].sDrawXSize(n);
390
391 if (newSize > size) {
392 if (minimize)
393 deltaVal /= 2;
394 n += deltaVal;
395 } else {
396 minimize = true;
397 deltaVal /= 2;
398 n -= deltaVal;
399 if (n < 1)
400 n = 1;
401 }
402 } while (deltaVal && size != newSize);
403
404 int deltaScale = (SCALE_THRESHOLD - scaleVal) / CLOSEUP_STEPS;
405
406 for (int step = 0; step < CLOSEUP_STEPS; ++step) {
407 Common::Point picSize(pic[0].sDrawXSize(scaleVal), pic[0].sDrawYSize(scaleVal));
408 Common::Point pt(screen._currentScroll.x + closeUp.x / 100 - picSize.x / 2,
409 screen._currentScroll.y + closeUp.y / 100 - picSize.y / 2);
410
411 restoreArea(oldBounds);
412 screen._backBuffer1.SHtransBlitFrom(pic[0], pt, false, 0, scaleVal);
413
414 screen.slamRect(oldBounds);
415 screen.slamArea(pt.x, pt.y, picSize.x, picSize.y);
416
417 oldBounds = Common::Rect(pt.x, pt.y, pt.x + picSize.x + 1, pt.y + picSize.y + 1);
418 closeUp += delta;
419 scaleVal += deltaScale;
420
421 events.wait(1);
422 }
423
424 // Handle final drawing of closeup
425 Common::Rect r(screen._currentScroll.x + SHERLOCK_SCREEN_WIDTH / 2 - pic[0]._width / 2,
426 screen._currentScroll.y + SHERLOCK_SCREEN_HEIGHT / 2 - pic[0]._height / 2,
427 screen._currentScroll.x + SHERLOCK_SCREEN_WIDTH / 2 - pic[0]._width / 2 + pic[0]._width,
428 screen._currentScroll.y + SHERLOCK_SCREEN_HEIGHT / 2 - pic[0]._height / 2 + pic[0]._height);
429
430 restoreArea(oldBounds);
431 screen._backBuffer1.SHtransBlitFrom(pic[0], Common::Point(r.left, r.top));
432 screen.slamRect(oldBounds);
433 screen.slamRect(r);
434
435 events.wait(60);
436 events.showCursor();
437 }
438
439 } // End of namespace Tattoo
440
441 } // End of namespace Sherlock
442