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