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_scene.h"
24 #include "sherlock/tattoo/tattoo_people.h"
25 #include "sherlock/tattoo/tattoo_talk.h"
26 #include "sherlock/tattoo/tattoo_user_interface.h"
27 #include "sherlock/tattoo/tattoo.h"
28 #include "sherlock/events.h"
29 #include "sherlock/people.h"
30
31 namespace Sherlock {
32
33 namespace Tattoo {
34
35 const int FS_TRANS[8] = {
36 STOP_UP, STOP_UPRIGHT, STOP_RIGHT, STOP_DOWNRIGHT, STOP_DOWN, STOP_DOWNLEFT, STOP_LEFT, STOP_UPLEFT
37 };
38
39 /*----------------------------------------------------------------*/
40
41 struct ShapeEntry {
42 Object *_shape;
43 TattooPerson *_person;
44 bool _isAnimation;
45 int _yp;
46 int _ordering;
47
ShapeEntrySherlock::Tattoo::ShapeEntry48 ShapeEntry(TattooPerson *person, int yp, int ordering) :
49 _shape(nullptr), _person(person), _yp(yp), _isAnimation(false), _ordering(ordering) {}
ShapeEntrySherlock::Tattoo::ShapeEntry50 ShapeEntry(Object *shape, int yp, int ordering) :
51 _shape(shape), _person(nullptr), _yp(yp), _isAnimation(false), _ordering(ordering) {}
ShapeEntrySherlock::Tattoo::ShapeEntry52 ShapeEntry(int yp, int ordering) :
53 _shape(nullptr), _person(nullptr), _yp(yp), _isAnimation(true), _ordering(ordering) {}
54 };
55 typedef Common::List<ShapeEntry> ShapeList;
56
sortImagesY(const ShapeEntry & s1,const ShapeEntry & s2)57 static bool sortImagesY(const ShapeEntry &s1, const ShapeEntry &s2) {
58 // Objects are order by the calculated Y position first and then, when both are equal,
59 // by the order in which the entries were added
60 return s1._yp < s2._yp || (s1._yp == s2._yp && s1._ordering < s2._ordering);
61 }
62
63 /*----------------------------------------------------------------*/
64
TattooScene(SherlockEngine * vm)65 TattooScene::TattooScene(SherlockEngine *vm) : Scene(vm), _labWidget(vm) {
66 _labTableScene = false;
67 }
68
loadScene(const Common::String & filename)69 bool TattooScene::loadScene(const Common::String &filename) {
70 TattooEngine &vm = *(TattooEngine *)_vm;
71 Events &events = *_vm->_events;
72 Music &music = *_vm->_music;
73 Talk &talk = *_vm->_talk;
74 TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
75
76 // If we're going to the first game scene after the intro sequence, flag it as finished
77 if (vm._runningProlog && _currentScene == STARTING_GAME_SCENE) {
78 vm._runningProlog = false;
79 events.showCursor();
80 talk._talkToAbort = false;
81 }
82
83 // Check if it's a scene we need to keep trakc track of how many times we've visited
84 for (int idx = (int)_sceneTripCounters.size() - 1; idx >= 0; --idx) {
85 if (_sceneTripCounters[idx]._sceneNumber == _currentScene) {
86 if (--_sceneTripCounters[idx]._numTimes == 0) {
87 _vm->setFlags(_sceneTripCounters[idx]._flag);
88 _sceneTripCounters.remove_at(idx);
89 }
90 }
91 }
92
93 // Handle loading music for the scene
94 if (talk._scriptMoreFlag != 1 && talk._scriptMoreFlag != 3)
95 music._nextSongName = Common::String::format("res%02d", _currentScene);
96
97 // Set the NPC paths for the scene
98 setNPCPath(WATSON);
99
100 // If it's a new song, then start it up
101 if (music._currentSongName.compareToIgnoreCase(music._nextSongName)) {
102 // WORKAROUND: Stop playing music after Diogenes fire scene in the intro,
103 // since it overlaps slightly into the next scene
104 if (talk._scriptName == "prol80p" && _currentScene == 80) {
105 music.stopMusic();
106 events.wait(5);
107 }
108
109 if (music.loadSong(music._nextSongName)) {
110 if (music._musicOn)
111 music.startSong();
112 }
113 }
114
115 bool result = Scene::loadScene(filename);
116
117 if (_currentScene != STARTING_INTRO_SCENE) {
118 // Set the menu/ui mode and whether we're in a lab table close-up scene
119 _labTableScene = _currentScene > 91 && _currentScene < 100;
120 ui._menuMode = _labTableScene ? LAB_MODE : STD_MODE;
121
122 if (_labTableScene)
123 ui.addFixedWidget(&_labWidget);
124 }
125
126 return result;
127 }
128
drawAllShapes()129 void TattooScene::drawAllShapes() {
130 TattooPeople &people = *(TattooPeople *)_vm->_people;
131 Screen &screen = *_vm->_screen;
132 ShapeList shapeList;
133 int ordering = 0;
134
135 // Draw all objects and animations that are set to behind
136 screen.setDisplayBounds(Common::Rect(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT));
137
138 // Draw all active shapes which are behind the person
139 for (uint idx = 0; idx < _bgShapes.size(); ++idx) {
140 Object &obj = _bgShapes[idx];
141
142 if (obj._type == ACTIVE_BG_SHAPE && obj._misc == BEHIND) {
143 if (obj._quickDraw && obj._scaleVal == SCALE_THRESHOLD)
144 screen._backBuffer1.SHblitFrom(*obj._imageFrame, obj._position);
145 else
146 screen._backBuffer1.SHtransBlitFrom(*obj._imageFrame, obj._position, obj._flags & OBJ_FLIPPED, 0, obj._scaleVal);
147 }
148 }
149
150 // Draw the animation if it is behind the person
151 if (_activeCAnim.active() && _activeCAnim._zPlacement == BEHIND)
152 screen._backBuffer1.SHtransBlitFrom(_activeCAnim._imageFrame, _activeCAnim._position,
153 (_activeCAnim._flags & 4) >> 1, 0, _activeCAnim._scaleVal);
154
155 screen.resetDisplayBounds();
156
157 // Queue drawing of all objects that are set to NORMAL.
158 for (uint idx = 0; idx < _bgShapes.size(); ++idx) {
159 Object &obj = _bgShapes[idx];
160
161 if (obj._type == ACTIVE_BG_SHAPE && (obj._misc == NORMAL_BEHIND || obj._misc == NORMAL_FORWARD)) {
162 if (obj._scaleVal == SCALE_THRESHOLD)
163 shapeList.push_back(ShapeEntry(&obj, obj._position.y + obj._imageFrame->_offset.y +
164 obj._imageFrame->_height, ordering++));
165 else
166 shapeList.push_back(ShapeEntry(&obj, obj._position.y + obj._imageFrame->sDrawYOffset(obj._scaleVal) +
167 obj._imageFrame->sDrawYSize(obj._scaleVal), ordering++));
168 }
169 }
170
171 // Queue drawing the animation if it is NORMAL and can fall in front of, or behind the people
172 if (_activeCAnim.active() && (_activeCAnim._zPlacement == NORMAL_BEHIND || _activeCAnim._zPlacement == NORMAL_FORWARD)) {
173 if (_activeCAnim._scaleVal == SCALE_THRESHOLD)
174 shapeList.push_back(ShapeEntry(_activeCAnim._position.y + _activeCAnim._imageFrame._offset.y +
175 _activeCAnim._imageFrame._height, ordering++));
176 else
177 shapeList.push_back(ShapeEntry(_activeCAnim._position.y + _activeCAnim._imageFrame.sDrawYOffset(_activeCAnim._scaleVal) +
178 _activeCAnim._imageFrame.sDrawYSize(_activeCAnim._scaleVal), ordering++));
179 }
180
181 // Queue all active characters for drawing
182 for (int idx = 0; idx < MAX_CHARACTERS; ++idx) {
183 if (people[idx]._type == CHARACTER && people[idx]._walkLoaded)
184 shapeList.push_back(ShapeEntry(&people[idx], people[idx]._position.y / FIXED_INT_MULTIPLIER, ordering++));
185 }
186
187 // Sort the list
188 Common::sort(shapeList.begin(), shapeList.end(), sortImagesY);
189
190 // Draw the list of shapes in order
191 for (ShapeList::iterator i = shapeList.begin(); i != shapeList.end(); ++i) {
192 ShapeEntry &se = *i;
193
194 if (se._shape) {
195 // it's a bg shape
196 if (se._shape->_quickDraw && se._shape->_scaleVal == SCALE_THRESHOLD)
197 screen._backBuffer1.SHblitFrom(*se._shape->_imageFrame, se._shape->_position);
198 else
199 screen._backBuffer1.SHtransBlitFrom(*se._shape->_imageFrame, se._shape->_position,
200 se._shape->_flags & OBJ_FLIPPED, 0, se._shape->_scaleVal);
201 } else if (se._isAnimation) {
202 // It's an active animation
203 screen._backBuffer1.SHtransBlitFrom(_activeCAnim._imageFrame, _activeCAnim._position,
204 (_activeCAnim._flags & 4) >> 1, 0, _activeCAnim._scaleVal);
205 } else {
206 // Drawing person
207 TattooPerson &p = *se._person;
208
209 p._tempX = p._position.x / FIXED_INT_MULTIPLIER;
210 p._tempScaleVal = getScaleVal(p._position);
211 Common::Point adjust = p._adjust;
212
213 if (p._tempScaleVal == SCALE_THRESHOLD) {
214 p._tempX += adjust.x;
215 screen._backBuffer1.SHtransBlitFrom(*p._imageFrame, Common::Point(p._tempX, p._position.y / FIXED_INT_MULTIPLIER
216 - p.frameHeight() - adjust.y), p._walkSequences[p._sequenceNumber]._horizFlip, 0, p._tempScaleVal);
217 } else {
218 if (adjust.x) {
219 if (!p._tempScaleVal)
220 ++p._tempScaleVal;
221
222 if (p._tempScaleVal >= SCALE_THRESHOLD && adjust.x)
223 --adjust.x;
224
225 adjust.x = adjust.x * SCALE_THRESHOLD / p._tempScaleVal;
226
227 if (p._tempScaleVal >= SCALE_THRESHOLD)
228 ++adjust.x;
229 p._tempX += adjust.x;
230 }
231
232 if (adjust.y) {
233 if (!p._tempScaleVal)
234 p._tempScaleVal++;
235
236 if (p._tempScaleVal >= SCALE_THRESHOLD && adjust.y)
237 --adjust.y;
238
239 adjust.y = adjust.y * SCALE_THRESHOLD / p._tempScaleVal;
240
241 if (p._tempScaleVal >= SCALE_THRESHOLD)
242 ++adjust.y;
243 }
244
245 screen._backBuffer1.SHtransBlitFrom(*p._imageFrame, Common::Point(p._tempX, p._position.y / FIXED_INT_MULTIPLIER
246 - p._imageFrame->sDrawYSize(p._tempScaleVal) - adjust.y), p._walkSequences[p._sequenceNumber]._horizFlip, 0, p._tempScaleVal);
247 }
248 }
249 }
250
251 // Draw all objects & canimations that are set to FORWARD.
252 // Draw all static and active shapes that are FORWARD
253 for (uint idx = 0; idx < _bgShapes.size(); ++idx) {
254 Object &obj = _bgShapes[idx];
255
256 if (obj._type == ACTIVE_BG_SHAPE && obj._misc == FORWARD) {
257 if (obj._quickDraw && obj._scaleVal == SCALE_THRESHOLD)
258 screen._backBuffer1.SHblitFrom(*obj._imageFrame, obj._position);
259 else
260 screen._backBuffer1.SHtransBlitFrom(*obj._imageFrame, obj._position, obj._flags & OBJ_FLIPPED, 0, obj._scaleVal);
261 }
262 }
263
264 // Draw the canimation if it is set as FORWARD
265 if (_activeCAnim.active() && _activeCAnim._zPlacement == FORWARD)
266 screen._backBuffer1.SHtransBlitFrom(_activeCAnim._imageFrame, _activeCAnim._position, (_activeCAnim._flags & 4) >> 1, 0, _activeCAnim._scaleVal);
267
268 // Draw all NO_SHAPE shapes which have their flag bits clear
269 for (uint idx = 0; idx < _bgShapes.size(); ++idx) {
270 Object &obj = _bgShapes[idx];
271 if (obj._type == NO_SHAPE && (obj._flags & 1) == 0)
272 screen._backBuffer1.fillRect(obj.getNoShapeBounds(), 15);
273 }
274 }
275
paletteLoaded()276 void TattooScene::paletteLoaded() {
277 Screen &screen = *_vm->_screen;
278 TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
279
280 ui.setupBGArea(screen._cMap);
281 ui.initScrollVars();
282 }
283
checkBgShapes()284 void TattooScene::checkBgShapes() {
285 // Call the base scene method to handle bg shapes
286 Scene::checkBgShapes();
287
288 // Check for any active playing animation
289 if (_activeCAnim.active() && _activeCAnim._zPlacement != REMOVE) {
290 switch (_activeCAnim._flags & 3) {
291 case 0:
292 _activeCAnim._zPlacement = BEHIND;
293 break;
294 case 1:
295 _activeCAnim._zPlacement = ((_activeCAnim._position.y + _activeCAnim._imageFrame._frame.h - 1)) ?
296 NORMAL_FORWARD : NORMAL_BEHIND;
297 break;
298 case 2:
299 _activeCAnim._zPlacement = FORWARD;
300 break;
301 default:
302 break;
303 }
304 }
305 }
306
freeScene()307 void TattooScene::freeScene() {
308 TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
309 Scene::freeScene();
310
311 // Delete any scene overlays that were used by the scene
312 delete ui._mask;
313 delete ui._mask1;
314 ui._mask = ui._mask1 = nullptr;
315 }
316
doBgAnimCheckCursor()317 void TattooScene::doBgAnimCheckCursor() {
318 Events &events = *_vm->_events;
319 TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
320 Common::Point mousePos = events.mousePos();
321
322 // If we're in Look Mode, make sure the cursor is the magnifying glass
323 if (ui._menuMode == LOOK_MODE && events.getCursor() != MAGNIFY)
324 events.setCursor(MAGNIFY);
325
326 // See if the mouse is over any of the arrow zones, and if so, change the cursor to the correct
327 // arrow cursor indicating the direcetion of the exit
328 if (events.getCursor() == ARROW || events.getCursor() >= EXIT_ZONES_START) {
329 CursorId cursorId = ARROW;
330
331 if (ui._menuMode == STD_MODE && ui._arrowZone != -1 && _currentScene != 90) {
332 for (uint idx = 0; idx < _exits.size(); ++idx) {
333 Exit &exit = _exits[idx];
334 if (exit.contains(mousePos))
335 cursorId = (CursorId)(exit._image + EXIT_ZONES_START);
336 }
337 }
338
339 events.setCursor(cursorId);
340 } else {
341 events.animateCursorIfNeeded();
342 }
343 }
344
doBgAnim()345 void TattooScene::doBgAnim() {
346 TattooEngine &vm = *(TattooEngine *)_vm;
347 Events &events = *_vm->_events;
348 Music &music = *_vm->_music;
349 TattooPeople &people = *(TattooPeople *)_vm->_people;
350 Screen &screen = *_vm->_screen;
351 Talk &talk = *_vm->_talk;
352 TattooUserInterface &ui = *((TattooUserInterface *)_vm->_ui);
353
354 doBgAnimCheckCursor();
355 music.checkSongProgress();
356
357 talk._talkToAbort = false;
358
359 // Check the characters and sprites for updates
360 for (int idx = 0; idx < MAX_CHARACTERS; ++idx) {
361 if (people[idx]._type == CHARACTER)
362 people[idx].checkSprite();
363 }
364
365 for (uint idx = 0; idx < _bgShapes.size(); ++idx) {
366 if (_bgShapes[idx]._type == ACTIVE_BG_SHAPE)
367 _bgShapes[idx].checkObject();
368 }
369
370 // If one of the objects has signalled a call to a talk file, to go to another scene, exit immediately
371 if (_goToScene != -1)
372 return;
373
374 // Erase any affected background areas
375 ui.doBgAnimEraseBackground();
376
377 doBgAnimUpdateBgObjectsAndAnim();
378
379 doBgAnimDrawSprites();
380
381 ui.drawInterface();
382
383 if (ui._creditsWidget.active())
384 ui._creditsWidget.blitCredits();
385
386 if (screen._flushScreen) {
387 screen.slamArea(screen._currentScroll.x, screen._currentScroll.y, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
388 screen._flushScreen = false;
389 }
390
391 screen._flushScreen = false;
392 _doBgAnimDone = true;
393 ui._drawMenu = false;
394
395 // Handle drawing tooltips
396 if (ui._menuMode == STD_MODE || ui._menuMode == LAB_MODE)
397 ui._tooltipWidget.draw();
398 if (!ui._postRenderWidgets.empty()) {
399 for (WidgetList::iterator i = ui._postRenderWidgets.begin(); i != ui._postRenderWidgets.end(); ++i)
400 (*i)->draw();
401 ui._postRenderWidgets.clear();
402 }
403
404 if (!vm._fastMode)
405 events.wait(3);
406
407 for (int idx = 1; idx < MAX_CHARACTERS; ++idx) {
408 if (people[idx]._updateNPCPath)
409 people[idx].updateNPC();
410 }
411 }
412
doBgAnimUpdateBgObjectsAndAnim()413 void TattooScene::doBgAnimUpdateBgObjectsAndAnim() {
414 People &people = *_vm->_people;
415 TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
416
417 for (uint idx = 0; idx < _bgShapes.size(); ++idx) {
418 Object &obj = _bgShapes[idx];
419 if (obj._type == ACTIVE_BG_SHAPE || obj._type == NO_SHAPE)
420 obj.adjustObject();
421 }
422
423 for (int idx = 0; idx < MAX_CHARACTERS; ++idx) {
424 if (people[idx]._type == CHARACTER)
425 people[idx].adjustSprite();
426 }
427
428 // Flag the bg shapes which need to be redrawn
429 checkBgShapes();
430 drawAllShapes();
431
432 ui.drawMaskArea(true);
433 }
434
updateBackground()435 void TattooScene::updateBackground() {
436 TattooPeople &people = *(TattooPeople *)_vm->_people;
437 Screen &screen = *_vm->_screen;
438 TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
439
440 Scene::updateBackground();
441
442 ui.drawMaskArea(false);
443
444 screen._flushScreen = true;
445
446 for (int idx = 0; idx < MAX_CHARACTERS; ++idx) {
447 TattooPerson &p = people[idx];
448
449 if (p._type != INVALID) {
450 if (_goToScene == -1 || _cAnim.size() == 0) {
451 if (p._type == REMOVE) {
452 screen.slamArea(p._oldPosition.x, p._oldPosition.y, p._oldSize.x, p._oldSize.y);
453 p._type = INVALID;
454 } else {
455 if (p._tempScaleVal == SCALE_THRESHOLD) {
456 screen.flushImage(p._imageFrame, Common::Point(p._tempX, p._position.y / FIXED_INT_MULTIPLIER
457 - p._imageFrame->_width), &p._oldPosition.x, &p._oldPosition.y, &p._oldSize.x, &p._oldSize.y);
458 } else {
459 int ts = p._imageFrame->sDrawYSize(p._tempScaleVal);
460 int ty = p._position.y / FIXED_INT_MULTIPLIER - ts;
461 screen.flushScaleImage(p._imageFrame, Common::Point(p._tempX, ty),
462 &p._oldPosition.x, &p._oldPosition.y, &p._oldSize.x, &p._oldSize.y, p._tempScaleVal);
463 }
464 }
465 }
466 }
467 }
468
469 for (uint idx = 0; idx < _bgShapes.size(); ++idx) {
470 Object &obj = _bgShapes[idx];
471
472 if (obj._type == ACTIVE_BG_SHAPE || obj._type == REMOVE) {
473 if (_goToScene == -1) {
474 if (obj._scaleVal == SCALE_THRESHOLD)
475 screen.flushImage(obj._imageFrame, obj._position, &obj._oldPosition.x, &obj._oldPosition.y,
476 &obj._oldSize.x, &obj._oldSize.y);
477 else
478 screen.flushScaleImage(obj._imageFrame, obj._position, &obj._oldPosition.x, &obj._oldPosition.y,
479 &obj._oldSize.x, &obj._oldSize.y, obj._scaleVal);
480
481 if (obj._type == REMOVE)
482 obj._type = INVALID;
483 }
484 }
485 }
486
487 for (uint idx = 0; idx < _bgShapes.size(); ++idx) {
488 Object &obj = _bgShapes[idx];
489
490 if (_goToScene == -1) {
491 if (obj._type == NO_SHAPE && (obj._flags & 1) == 0) {
492 screen.slamRect(obj.getNoShapeBounds());
493 screen.slamRect(obj.getOldBounds());
494 } else if (obj._type == HIDE_SHAPE) {
495 if (obj._scaleVal == SCALE_THRESHOLD)
496 screen.flushImage(obj._imageFrame, obj._position, &obj._oldPosition.x, &obj._oldPosition.y,
497 &obj._oldSize.x, &obj._oldSize.y);
498 else
499 screen.flushScaleImage(obj._imageFrame, obj._position, &obj._oldPosition.x, &obj._oldPosition.y,
500 &obj._oldSize.x, &obj._oldSize.y, obj._scaleVal);
501 obj._type = HIDDEN;
502 }
503 }
504 }
505
506 screen._flushScreen = false;
507 }
508
doBgAnimDrawSprites()509 void TattooScene::doBgAnimDrawSprites() {
510 TattooPeople &people = *(TattooPeople *)_vm->_people;
511 Screen &screen = *_vm->_screen;
512
513 for (int idx = 0; idx < MAX_CHARACTERS; ++idx) {
514 TattooPerson &person = people[idx];
515
516 if (person._type != INVALID) {
517 if (_goToScene == -1 || _cAnim.size() == 0) {
518 if (person._type == REMOVE) {
519 screen.slamRect(person.getOldBounds());
520 person._type = INVALID;
521 } else {
522 if (person._tempScaleVal == SCALE_THRESHOLD) {
523 screen.flushImage(person._imageFrame, Common::Point(person._tempX, person._position.y / FIXED_INT_MULTIPLIER
524 - person.frameHeight()), &person._oldPosition.x, &person._oldPosition.y, &person._oldSize.x, &person._oldSize.y);
525 } else {
526 int ts = person._imageFrame->sDrawYSize(person._tempScaleVal);
527 int ty = person._position.y / FIXED_INT_MULTIPLIER - ts;
528 screen.flushScaleImage(person._imageFrame, Common::Point(person._tempX, ty),
529 &person._oldPosition.x, &person._oldPosition.y, &person._oldSize.x, &person._oldSize.y, person._tempScaleVal);
530 }
531 }
532 }
533 }
534 }
535
536 for (uint idx = 0; idx < _bgShapes.size(); ++idx) {
537 Object &obj = _bgShapes[idx];
538
539 if (obj._type == ACTIVE_BG_SHAPE || obj._type == REMOVE) {
540 if (_goToScene == -1) {
541 if (obj._scaleVal == SCALE_THRESHOLD)
542 screen.flushImage(obj._imageFrame, obj._position, &obj._oldPosition.x, &obj._oldPosition.y,
543 &obj._oldSize.x, &obj._oldSize.y);
544 else
545 screen.flushScaleImage(obj._imageFrame, obj._position, &obj._oldPosition.x, &obj._oldPosition.y,
546 &obj._oldSize.x, &obj._oldSize.y, obj._scaleVal);
547
548 if (obj._type == REMOVE)
549 obj._type = INVALID;
550 }
551 }
552 }
553
554 for (uint idx = 0; idx < _bgShapes.size(); ++idx) {
555 Object &obj = _bgShapes[idx];
556
557 if (_goToScene == -1) {
558 if (obj._type == NO_SHAPE && (obj._flags & 1) == 0) {
559 screen.slamRect(obj.getNoShapeBounds());
560 screen.slamRect(obj.getOldBounds());
561 } else if (obj._type == HIDE_SHAPE) {
562 if (obj._scaleVal == SCALE_THRESHOLD)
563 screen.flushImage(obj._imageFrame, obj._position, &obj._oldPosition.x, &obj._oldPosition.y,
564 &obj._oldSize.x, &obj._oldSize.y);
565 else
566 screen.flushScaleImage(obj._imageFrame, obj._position, &obj._oldPosition.x, &obj._oldPosition.y,
567 &obj._oldSize.x, &obj._oldSize.y, obj._scaleVal);
568 obj._type = HIDDEN;
569 }
570 }
571 }
572
573 if (_activeCAnim.active() || _activeCAnim._zPlacement == REMOVE) {
574 if (_activeCAnim._zPlacement != REMOVE) {
575 screen.flushImage(&_activeCAnim._imageFrame, _activeCAnim._position, _activeCAnim._oldBounds, _activeCAnim._scaleVal);
576 } else {
577 screen.slamRect(_activeCAnim._removeBounds);
578 _activeCAnim._removeBounds = Common::Rect(0, 0, 0, 0);
579 _activeCAnim._zPlacement = -1; // Reset _zPlacement so we don't REMOVE again
580 }
581 }
582 }
583
getScaleVal(const Point32 & pt)584 int TattooScene::getScaleVal(const Point32 &pt) {
585 bool found = false;
586 int result = SCALE_THRESHOLD;
587 Common::Point pos(pt.x / FIXED_INT_MULTIPLIER, pt.y / FIXED_INT_MULTIPLIER);
588
589 for (uint idx = 0; idx < _scaleZones.size() && !found; ++idx) {
590 ScaleZone &sz = _scaleZones[idx];
591 if (sz.contains(pos)) {
592 int n = (sz._bottomNumber - sz._topNumber) * 100 / sz.height() * (pos.y - sz.top) / 100 + sz._topNumber;
593 result = 25600L / n;
594 // CHECKME: Shouldn't we set 'found' at this place?
595 }
596 }
597
598 // If it wasn't found, we may be off screen to the left or right, so find the scale zone
599 // that would apply to the y val passed in disregarding the x
600 if (!found) {
601 for (uint idx = 0; idx < _scaleZones.size() && !found; ++idx) {
602 ScaleZone &sz = _scaleZones[idx];
603 if (pos.y >= sz.top && pos.y < sz.bottom) {
604 int n = (sz._bottomNumber - sz._topNumber) * 100 / sz.height() * (pos.y - sz.top) / 100 + sz._topNumber;
605 result = 25600L / n;
606 }
607 }
608 }
609
610 return result;
611 }
612
startCAnim(int cAnimNum,int playRate)613 int TattooScene::startCAnim(int cAnimNum, int playRate) {
614 TattooEngine &vm = *(TattooEngine *)_vm;
615 Events &events = *_vm->_events;
616 TattooPeople &people = *(TattooPeople *)_vm->_people;
617 Resources &res = *_vm->_res;
618 Talk &talk = *_vm->_talk;
619 UserInterface &ui = *_vm->_ui;
620
621 // Exit immediately if the anim number is out of range, or the anim doesn't have a position specified
622 if (cAnimNum < 0 || cAnimNum >= (int)_cAnim.size() || _cAnim[cAnimNum]._position.x == -1)
623 // Return out of range error
624 return -1;
625
626 // Get the co-ordinates that the Player & NPC #1 must walk to and end on
627 CAnim &cAnim = _cAnim[cAnimNum];
628 PositionFacing goto1 = cAnim._goto[0];
629 PositionFacing goto2 = cAnim._goto[1];
630 PositionFacing teleport1 = cAnim._teleport[0];
631 PositionFacing teleport2 = cAnim._teleport[1];
632
633 // See if the Player must walk to a position before the animation starts
634 SpriteType savedPlayerType = people[HOLMES]._type;
635 if (goto1.x != -1 && people[HOLMES]._type == CHARACTER) {
636 if (people[HOLMES]._position != goto1)
637 people[HOLMES].walkToCoords(goto1, goto1._facing);
638 }
639
640 if (talk._talkToAbort)
641 return 1;
642
643 // See if NPC #1 must walk to a position before the animation starts
644 SpriteType savedNPCType = people[WATSON]._type;
645 if (goto2.x != -1 && people[WATSON]._type == CHARACTER) {
646 if (people[WATSON]._position != goto2)
647 people[WATSON].walkToCoords(goto2, goto2._facing);
648 }
649
650 if (talk._talkToAbort)
651 return 1;
652
653 // Turn the player (and NPC #1 if neccessary) off before running the canimation
654 if (teleport1.x != -1 && savedPlayerType == CHARACTER)
655 people[HOLMES]._type = REMOVE;
656
657 if (teleport2.x != -1 && savedNPCType == CHARACTER)
658 people[WATSON]._type = REMOVE;
659
660 if (ui._windowOpen)
661 ui.banishWindow();
662
663 // Open up the room resource file and get the data for the animation
664 Common::SeekableReadStream *stream = res.load(_roomFilename);
665 stream->seek(44 + cAnimNum * 4);
666 stream->seek(stream->readUint32LE());
667 Common::SeekableReadStream *animStream = stream->readStream(cAnim._dataSize);
668 delete stream;
669
670 // Set up the active animation
671 _activeCAnim._position = cAnim._position;
672 _activeCAnim._oldBounds = Common::Rect(0, 0, 0, 0);
673 _activeCAnim._flags = cAnim._flags;
674 _activeCAnim._scaleVal = cAnim._scaleVal;
675 _activeCAnim._zPlacement = 0;
676
677 _activeCAnim.load(animStream, _compressed);
678
679 while (!_vm->shouldQuit()) {
680 // Get the next frame
681 if (!_activeCAnim.getNextFrame())
682 break;
683
684 // Draw the frame
685 doBgAnim();
686
687 // Check for Escape key being pressed to abort animation
688 events.pollEvents();
689 if (events.kbHit()) {
690 Common::KeyState keyState = events.getKey();
691
692 if (keyState.keycode == Common::KEYCODE_ESCAPE && vm._runningProlog) {
693 _vm->setFlags(-76);
694 _vm->setFlags(396);
695 _goToScene = STARTING_GAME_SCENE;
696 talk._talkToAbort = true;
697 _activeCAnim.close();
698 break;
699 }
700 }
701 }
702
703 // Turn the people back on
704 people[HOLMES]._type = savedPlayerType;
705 if (teleport2.x != -1)
706 people[WATSON]._type = savedNPCType;
707
708 // Teleport the Player to the ending coordinates if necessary
709 if (teleport1.x != -1 && savedPlayerType == CHARACTER) {
710 people[HOLMES]._position = teleport1;
711 people[HOLMES]._sequenceNumber = teleport1._facing;
712 people[HOLMES].gotoStand();
713 }
714
715 // Teleport Watson to the ending coordinates if necessary
716 if (teleport2.x != -1 && savedNPCType == CHARACTER) {
717 people[WATSON]._position = teleport2;
718 people[WATSON]._sequenceNumber = teleport2._facing;
719 people[WATSON].gotoStand();
720 }
721
722 // Flag the Canimation to be cleared
723 _activeCAnim._zPlacement = REMOVE;
724 _activeCAnim._removeBounds = _activeCAnim._oldBounds;
725 _vm->_ui->_bgFound = -1;
726
727 // Free up the animation
728 _activeCAnim.close();
729
730 return 1;
731 }
732
setNPCPath(int npc)733 void TattooScene::setNPCPath(int npc) {
734 TattooPeople &people = *(TattooPeople *)_vm->_people;
735 SaveManager &saves = *_vm->_saves;
736 Talk &talk = *_vm->_talk;
737
738 // Don't do initial scene setup if a savegame has just been loaded
739 if (saves._justLoaded)
740 return;
741
742 people[npc].clearNPC();
743 people[npc]._npcName = Common::String::format("WATS%.2dA", _currentScene);
744
745 // If we're in the middle of a script that will continue once the scene is loaded,
746 // return without calling the path script
747 if (talk._scriptMoreFlag == 1 || talk._scriptMoreFlag == 3)
748 return;
749
750 // Turn off all the NPCs, since the talk script will turn them back on as needed
751 for (int idx = 1; idx < MAX_CHARACTERS; ++idx)
752 people[idx]._type = INVALID;
753
754 // Call the path script for the scene
755 Common::String pathFile = Common::String::format("PATH%.2dA", _currentScene);
756 talk.talkTo(pathFile);
757 }
758
findBgShape(const Common::Point & pt)759 int TattooScene::findBgShape(const Common::Point &pt) {
760 People &people = *_vm->_people;
761 UserInterface &ui = *_vm->_ui;
762
763 if (!_doBgAnimDone)
764 // New frame hasn't been drawn yet
765 return -1;
766
767 int result = -1;
768 for (int idx = (int)_bgShapes.size() - 1; idx >= 0 && result == -1; --idx) {
769 Object &o = _bgShapes[idx];
770
771 if (o._type != INVALID && o._type != NO_SHAPE && o._type != HIDDEN &&
772 (o._aType <= PERSON || (ui._menuMode == LAB_MODE && o._aType == SOLID))) {
773 if (o.getNewBounds().contains(pt))
774 result = idx;
775 } else if (o._type == NO_SHAPE) {
776 if (o.getNoShapeBounds().contains(pt))
777 result = idx;
778 }
779 }
780
781 // Now check for the mouse being over an NPC. If so, it overrides any found bg object
782 for (int idx = 1; idx < MAX_CHARACTERS; ++idx) {
783 Person &person = people[idx];
784
785 if (person._type == CHARACTER) {
786 int scaleVal = getScaleVal(person._position);
787 Common::Rect charRect;
788
789 if (scaleVal == SCALE_THRESHOLD)
790 charRect = Common::Rect(person.frameWidth(), person.frameHeight());
791 else
792 charRect = Common::Rect(person._imageFrame->sDrawXSize(scaleVal), person._imageFrame->sDrawYSize(scaleVal));
793 charRect.moveTo(person._position.x / FIXED_INT_MULTIPLIER, person._position.y / FIXED_INT_MULTIPLIER
794 - charRect.height());
795
796 if (charRect.contains(pt))
797 result = 1000 + idx;
798 }
799 }
800
801 return result;
802 }
803
synchronize(Serializer & s)804 void TattooScene::synchronize(Serializer &s) {
805 TattooEngine &vm = *(TattooEngine *)_vm;
806 TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
807 Scene::synchronize(s);
808
809 if (s.isLoading()) {
810 // In case we were showing the intro prologue or the ending credits, stop them
811 vm._runningProlog = false;
812 ui._creditsWidget.close();
813 }
814 }
815
closestZone(const Common::Point & pt)816 int TattooScene::closestZone(const Common::Point &pt) {
817 int zone = -1;
818 int dist = 9999;
819
820 for (uint idx = 0; idx < _zones.size(); ++idx) {
821 Common::Rect &r = _zones[idx];
822
823 // Check the distance from the point to the center of the zone
824 int d = ABS(r.left + (r.width() / 2) - pt.x) + ABS(r.top + (r.height() / 2) - pt.y);
825 if (d < dist) {
826 dist = d;
827 zone = idx;
828 }
829
830 // Check the distance from the point to the upper left of the zone
831 d = ABS((int)(r.left - pt.x)) + ABS((int)(r.top - pt.y));
832 if (d < dist)
833 {
834 dist = d;
835 zone = idx;
836 }
837
838 // Check the distance from the point to the upper right of the zone
839 d = ABS(r.left + r.width() - pt.x) + ABS(r.top - pt.y);
840 if (d < dist) {
841 dist = d;
842 zone = idx;
843 }
844
845 // Check the distance from the point to the lower left of the zone
846 d = ABS(r.left - pt.x) + ABS(r.top + r.height() - pt.y);
847 if (d < dist) {
848 dist = d;
849 zone = idx;
850 }
851
852 // Check the distance from the point to the lower right of the zone
853 d = ABS(r.left + r.width() - pt.x) + ABS(r.top + r.height() - pt.y);
854 if (d < dist) {
855 dist = d;
856 zone = idx;
857 }
858 }
859
860 return zone;
861 }
862
863 } // End of namespace Tattoo
864
865 } // End of namespace Sherlock
866