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 "titanic/core/game_object.h"
24 #include "titanic/core/mail_man.h"
25 #include "titanic/core/resource_key.h"
26 #include "titanic/core/room_item.h"
27 #include "titanic/core/project_item.h"
28 #include "titanic/debugger.h"
29 #include "titanic/events.h"
30 #include "titanic/game_manager.h"
31 #include "titanic/npcs/true_talk_npc.h"
32 #include "titanic/pet_control/pet_control.h"
33 #include "titanic/star_control/star_control.h"
34 #include "titanic/support/files_manager.h"
35 #include "titanic/support/screen_manager.h"
36 #include "titanic/support/video_surface.h"
37 #include "titanic/titanic.h"
38 
39 namespace Titanic {
40 
41 EMPTY_MESSAGE_MAP(CGameObject, CNamedItem);
42 
43 CCreditText *CGameObject::_credits;
44 int CGameObject::_soundHandles[4];
45 
init()46 void CGameObject::init() {
47 	_credits = nullptr;
48 	_soundHandles[0] = _soundHandles[1] = -1;
49 	_soundHandles[2] = _soundHandles[3] = -1;
50 }
51 
deinit()52 void CGameObject::deinit() {
53 	if (_credits) {
54 		_credits->clear();
55 		delete _credits;
56 		_credits = nullptr;
57 	}
58 }
59 
CGameObject()60 CGameObject::CGameObject(): CNamedItem() {
61 	_bounds = Rect(0, 0, 15, 15);
62 	_unused1 = 0;
63 	_unused2 = 0;
64 	_unused3 = 0;
65 	_nonvisual = false;
66 	_toggleR = 0xF0;
67 	_toggleG = 0xF0;
68 	_toggleB = 0xFF;
69 	_isPendingMail = false;
70 	_destRoomFlags = 0;
71 	_roomFlags = 0;
72 	_visible = true;
73 	_handleMouseFlag = false;
74 	_cursorId = CURSOR_ARROW;
75 	_initialFrame = 0;
76 	_frameNumber = -1;
77 	_text = nullptr;
78 	_textBorder = _textBorderRight = 0;
79 	_surface = nullptr;
80 	_unused4 = 0;
81 }
82 
~CGameObject()83 CGameObject::~CGameObject() {
84 	delete _surface;
85 	delete _text;
86 }
87 
save(SimpleFile * file,int indent)88 void CGameObject::save(SimpleFile *file, int indent) {
89 	file->writeNumberLine(7, indent);
90 	_movieRangeInfoList.destroyContents();
91 
92 	if (_surface) {
93 		const CMovieRangeInfoList *rangeList = _surface->getMovieRangeInfo();
94 
95 		if (rangeList) {
96 			for (CMovieRangeInfoList::const_iterator i = rangeList->begin();
97 				i != rangeList->end(); ++i) {
98 				CMovieRangeInfo *rangeInfo = new CMovieRangeInfo(*i);
99 				rangeInfo->_initialFrame = (i == rangeList->begin()) ? getMovieFrame() : -1;
100 				_movieRangeInfoList.push_back(rangeInfo);
101 			}
102 		}
103 	}
104 
105 	_movieRangeInfoList.save(file, indent);
106 	_movieRangeInfoList.destroyContents();
107 
108 	file->writeNumberLine(getMovieFrame(), indent + 1);
109 	file->writeNumberLine(_cursorId, indent + 1);
110 	_movieClips.save(file, indent + 1);
111 	file->writeNumberLine(_handleMouseFlag, indent + 1);
112 	file->writeNumberLine(_nonvisual, indent + 1);
113 	file->writeQuotedLine(_resource, indent + 1);
114 	file->writeBounds(_bounds, indent + 1);
115 
116 	file->writeFloatLine(_unused1, indent + 1);
117 	file->writeFloatLine(_unused2, indent + 1);
118 	file->writeFloatLine(_unused3, indent + 1);
119 
120 	file->writeNumberLine(_toggleR, indent + 1);
121 	file->writeNumberLine(_toggleG, indent + 1);
122 	file->writeNumberLine(_toggleB, indent + 1);
123 	file->writeNumberLine(_unused4, indent + 1);
124 	file->writeNumberLine(_visible, indent + 1);
125 	file->writeNumberLine(_isPendingMail, indent + 1);
126 	file->writeNumberLine(_destRoomFlags, indent + 1);
127 	file->writeNumberLine(_roomFlags, indent + 1);
128 
129 	if (_surface) {
130 		_surface->_resourceKey.save(file, indent);
131 	} else {
132 		CResourceKey resourceKey;
133 		resourceKey.save(file, indent);
134 	}
135 	file->writeNumberLine(_surface != nullptr, indent);
136 
137 	CNamedItem::save(file, indent);
138 }
139 
load(SimpleFile * file)140 void CGameObject::load(SimpleFile *file) {
141 	int val = file->readNumber();
142 	CResourceKey resourceKey;
143 
144 	switch (val) {
145 	case 7:
146 		_movieRangeInfoList.load(file);
147 		_frameNumber = file->readNumber();
148 		// Intentional fall-through
149 
150 	case 6:
151 		_cursorId = (CursorId)file->readNumber();
152 		// Intentional fall-through
153 
154 	case 5:
155 		_movieClips.load(file);
156 		// Intentional fall-through
157 
158 	case 4:
159 		_handleMouseFlag = file->readNumber();
160 		// Intentional fall-through
161 
162 	case 3:
163 		_nonvisual = file->readNumber();
164 		// Intentional fall-through
165 
166 	case 2:
167 		_resource = file->readString();
168 		// Intentional fall-through
169 
170 	case 1:
171 		_bounds = file->readBounds();
172 		_unused1 = file->readFloat();
173 		_unused2 = file->readFloat();
174 		_unused3 = file->readFloat();
175 		_toggleR = file->readNumber();
176 		_toggleG = file->readNumber();
177 		_toggleB = file->readNumber();
178 		_unused4 = file->readNumber();
179 		_visible = file->readNumber() != 0;
180 		_isPendingMail = file->readNumber();
181 		_destRoomFlags = file->readNumber();
182 		_roomFlags = file->readNumber();
183 
184 		resourceKey.load(file);
185 		_surface = nullptr;
186 		val = file->readNumber();
187 		if (val) {
188 			_resource = resourceKey.getString();
189 		}
190 		break;
191 
192 	default:
193 		break;
194 	}
195 
196 	CNamedItem::load(file);
197 }
198 
draw(CScreenManager * screenManager)199 void CGameObject::draw(CScreenManager *screenManager) {
200 	if (!_visible)
201 		return;
202 	if (_credits && _credits->_objectP == this) {
203 		if (!_credits->draw())
204 			CGameObject::deinit();
205 
206 		return;
207 	}
208 
209 	if (_nonvisual) {
210 		// If a text is defined, handle drawing it
211 		if (_text && _bounds.intersects(getGameManager()->_bounds))
212 			_text->draw(screenManager);
213 
214 		return;
215 	} else {
216 		if (!_surface) {
217 			if (!_resource.empty()) {
218 				loadResource(_resource);
219 				_resource = "";
220 			}
221 		}
222 
223 		if (_surface) {
224 			_bounds.setWidth(_surface->getWidth());
225 			_bounds.setHeight(_surface->getHeight());
226 
227 			if (!_bounds.width() || !_bounds.height())
228 				return;
229 
230 			if (_frameNumber >= 0) {
231 				loadFrame(_frameNumber);
232 				_frameNumber = -1;
233 			}
234 
235 			if (!_movieRangeInfoList.empty())
236 				processMoveRangeInfo();
237 
238 			if (_bounds.intersects(getGameManager()->_bounds)) {
239 				if (_surface) {
240 					Point destPos(_bounds.left, _bounds.top);
241 					screenManager->blitFrom(SURFACE_BACKBUFFER, _surface, &destPos);
242 				}
243 
244 				if (_text)
245 					_text->draw(screenManager);
246 			}
247 		}
248 	}
249 }
250 
getBounds() const251 Rect CGameObject::getBounds() const {
252 	return (_surface && _surface->hasFrame()) ? _bounds : Rect();
253 }
254 
freeSurface()255 void CGameObject::freeSurface() {
256 	// Handle freeing the surfaces of objects when their view is left
257 	if (_surface) {
258 		_resource = _surface->_resourceKey.getString();
259 		_initialFrame = getMovieFrame();
260 
261 		delete _surface;
262 		_surface = nullptr;
263 	}
264 }
265 
stopMovie()266 void CGameObject::stopMovie() {
267 	if (_surface)
268 		_surface->stopMovie();
269 }
270 
checkPoint(const Point & pt,bool ignoreSurface,bool visibleOnly)271 bool CGameObject::checkPoint(const Point &pt, bool ignoreSurface, bool visibleOnly) {
272 	if ((!_visible && visibleOnly) || !_bounds.contains(pt))
273 		return false;
274 
275 	if (ignoreSurface || _nonvisual)
276 		return true;
277 
278 	if (!_surface) {
279 		if (_frameNumber == -1)
280 			return true;
281 		loadFrame(_frameNumber);
282 		if (!_surface)
283 			return true;
284 	}
285 
286 	Common::Point pixelPos = pt - _bounds;
287 	if (_surface->_flipVertically) {
288 		pixelPos.y = ((_bounds.height() - _bounds.top) / 2) * 2 - pixelPos.y;
289 	}
290 
291 	uint transColor = _surface->getTransparencyColor();
292 	uint pixel = _surface->getPixel(pixelPos);
293 	return pixel != transColor;
294 }
295 
findPoint(Quadrant quadrant,Point & pt)296 bool CGameObject::findPoint(Quadrant quadrant, Point &pt) {
297 	// Start by checking a centroid point in the bounds
298 	if (!_bounds.isEmpty()) {
299 		pt = _bounds.getPoint(quadrant);
300 		if (checkPoint(pt, false, true))
301 			return true;
302 	}
303 
304 	// Scan through all the area of the bounds to find a valid point
305 	for (; pt.y < _bounds.bottom; ++pt.y, pt.x = _bounds.left) {
306 		for (; pt.x < _bounds.right; ++pt.x) {
307 			if (checkPoint(pt, false, true))
308 				return true;
309 		}
310 	}
311 
312 	pt = Point(0, 0);
313 	return false;
314 }
315 
clipRect(const Rect & rect1,Rect & rect2) const316 bool CGameObject::clipRect(const Rect &rect1, Rect &rect2) const {
317 	if (!rect2.intersects(rect1))
318 		return false;
319 
320 	rect2.clip(rect1);
321 	return true;
322 }
323 
draw(CScreenManager * screenManager,const Rect & destRect,const Rect & srcRect)324 void CGameObject::draw(CScreenManager *screenManager, const Rect &destRect, const Rect &srcRect) {
325 	Rect tempRect = destRect;
326 	if (clipRect(srcRect, tempRect)) {
327 		if (!_surface && !_resource.empty()) {
328 			loadResource(_resource);
329 			_resource.clear();
330 		}
331 
332 		if (_surface)
333 			screenManager->blitFrom(SURFACE_PRIMARY, &tempRect, _surface);
334 	}
335 }
336 
draw(CScreenManager * screenManager,const Point & destPos)337 void CGameObject::draw(CScreenManager *screenManager, const Point &destPos) {
338 	if (!_surface && !_resource.empty()) {
339 		loadResource(_resource);
340 		_resource.clear();
341 	}
342 
343 	if (_surface) {
344 		int xSize = _surface->getWidth();
345 		int ySize = _surface->getHeight();
346 
347 		if (xSize > 0 && ySize > 0) {
348 			screenManager->blitFrom(SURFACE_BACKBUFFER, _surface, &destPos);
349 		}
350 	}
351 }
352 
draw(CScreenManager * screenManager,const Point & destPos,const Rect & srcRect)353 void CGameObject::draw(CScreenManager *screenManager, const Point &destPos, const Rect &srcRect) {
354 	draw(screenManager, Rect(destPos.x, destPos.y, destPos.x + 52, destPos.y + 52), srcRect);
355 }
356 
isPet() const357 bool CGameObject::isPet() const {
358 	return isInstanceOf(CPetControl::_type);
359 }
360 
loadResource(const CString & name)361 void CGameObject::loadResource(const CString &name) {
362 	switch (name.fileTypeSuffix()) {
363 	case FILETYPE_IMAGE:
364 		loadImage(name);
365 		break;
366 	case FILETYPE_MOVIE:
367 		loadMovie(name);
368 		break;
369 	default:
370 		break;
371 	}
372 }
373 
loadMovie(const CString & name,bool pendingFlag)374 void CGameObject::loadMovie(const CString &name, bool pendingFlag) {
375 	g_vm->_filesManager->preload(name);
376 
377 	// Create the surface if it doesn't already exist
378 	if (!_surface) {
379 		CGameManager *gameManager = getGameManager();
380 		_surface = new OSVideoSurface(gameManager->setScreenManager(), nullptr);
381 	}
382 
383 	// Load the new movie resource
384 	CResourceKey key(name);
385 	_surface->loadResource(key);
386 
387 	if (_surface->hasSurface() && !pendingFlag) {
388 		_bounds.setWidth(_surface->getWidth());
389 		_bounds.setHeight(_surface->getHeight());
390 	}
391 
392 	if (_initialFrame)
393 		loadFrame(_initialFrame);
394 }
395 
loadImage(const CString & name,bool pendingFlag)396 void CGameObject::loadImage(const CString &name, bool pendingFlag) {
397 	// Get a refernce to the game and screen managers
398 	CGameManager *gameManager = getGameManager();
399 	CScreenManager *screenManager;
400 
401 	if (gameManager && (screenManager = CScreenManager::setCurrent()) != nullptr) {
402 		// Destroy the object's surface if it already had one
403 		if (_surface) {
404 			delete _surface;
405 			_surface = nullptr;
406 		}
407 
408 		g_vm->_filesManager->preload(name);
409 
410 		if (!name.empty()) {
411 			_surface = new OSVideoSurface(screenManager, CResourceKey(name), pendingFlag);
412 		}
413 
414 		if (_surface && !pendingFlag) {
415 			_bounds.right = _surface->getWidth();
416 			_bounds.bottom = _surface->getHeight();
417 		}
418 
419 		// Mark the object's area as dirty, so that on the next frame rendering
420 		// this object will be redrawn
421 		makeDirty();
422 	}
423 
424 	_initialFrame = 0;
425 }
426 
loadFrame(int frameNumber)427 void CGameObject::loadFrame(int frameNumber) {
428 	_frameNumber = -1;
429 
430 	if (!_surface && !_resource.empty()) {
431 		loadResource(_resource);
432 		_resource.clear();
433 	}
434 
435 	if (_surface)
436 		_surface->setMovieFrame(frameNumber);
437 
438 	makeDirty();
439 }
440 
processMoveRangeInfo()441 void CGameObject::processMoveRangeInfo() {
442 	for (CMovieRangeInfoList::iterator i = _movieRangeInfoList.begin(); i != _movieRangeInfoList.end(); ++i)
443 		(*i)->process(this);
444 
445 	_movieRangeInfoList.destroyContents();
446 }
447 
makeDirty(const Rect & r)448 void CGameObject::makeDirty(const Rect &r) {
449 	CGameManager *gameManager = getGameManager();
450 	if (gameManager)
451 		gameManager->addDirtyRect(r);
452 }
453 
makeDirty()454 void CGameObject::makeDirty() {
455 	makeDirty(_bounds);
456 }
457 
isSoundActive(int handle) const458 bool CGameObject::isSoundActive(int handle) const {
459 	if (handle != 0 && handle != -1) {
460 		CGameManager *gameManager = getGameManager();
461 		if (gameManager)
462 			return gameManager->_sound.isActive(handle);
463 	}
464 
465 	return false;
466 }
467 
playAmbientSound(const CString & resName,VolumeMode mode,bool initialMute,bool repeated,int handleIndex,Audio::Mixer::SoundType soundType)468 void CGameObject::playAmbientSound(const CString &resName, VolumeMode mode, bool initialMute, bool repeated,
469 		int handleIndex, Audio::Mixer::SoundType soundType) {
470 	if (handleIndex < 0 || handleIndex > 3)
471 		return;
472 	CGameManager *gameManager = getGameManager();
473 	if (!gameManager)
474 		return;
475 
476 	// Preload the file, and stop any existing sound using the given slot
477 	CSound &sound = gameManager->_sound;
478 	g_vm->_filesManager->preload(resName);
479 	if (_soundHandles[handleIndex] != -1) {
480 		sound.stopSound(_soundHandles[handleIndex]);
481 		_soundHandles[handleIndex] = -1;
482 	}
483 
484 	// If no new name specified, then exit
485 	if (resName.empty())
486 		return;
487 
488 	uint newVolume = sound._soundManager.getModeVolume(mode);
489 	uint volume = initialMute ? 0 : newVolume;
490 
491 	CProximity prox;
492 	prox._channelVolume = volume;
493 	prox._repeated = repeated;
494 	prox._soundType = soundType;
495 
496 	switch (handleIndex) {
497 	case 0:
498 		prox._channelMode = 6;
499 		break;
500 	case 1:
501 		prox._channelMode = 7;
502 		break;
503 	case 2:
504 		prox._channelMode = 8;
505 		break;
506 	case 3:
507 		prox._channelMode = 9;
508 		break;
509 	default:
510 		break;
511 	}
512 
513 	_soundHandles[handleIndex] = sound.playSound(resName, prox);
514 
515 	if (_soundHandles[handleIndex])
516 		sound.setVolume(_soundHandles[handleIndex], newVolume, 2);
517 }
518 
setSoundVolume(int handle,uint percent,uint seconds)519 void CGameObject::setSoundVolume(int handle, uint percent, uint seconds) {
520 	if (handle != 0 && handle != -1) {
521 		CGameManager *gameManager = getGameManager();
522 		if (gameManager)
523 			return gameManager->_sound.setVolume(handle, percent, seconds);
524 	}
525 }
526 
stopAmbientSound(bool transition,int handleIndex)527 void CGameObject::stopAmbientSound(bool transition, int handleIndex) {
528 	CGameManager *gameManager = getGameManager();
529 	if (!gameManager)
530 		return;
531 	CSound &sound = gameManager->_sound;
532 
533 	if (handleIndex == -1) {
534 		for (int idx = 0; idx < 4; ++idx) {
535 			if (_soundHandles[idx] != -1) {
536 				sound.setVolume(_soundHandles[idx], 0, transition ? 1 : 0);
537 				sound.setCanFree(_soundHandles[idx]);
538 				_soundHandles[idx] = -1;
539 			}
540 		}
541 	} else if (handleIndex >= 0 && handleIndex <= 2 && _soundHandles[handleIndex] != -1) {
542 		if (transition) {
543 			// Transitioning to silent over 1 second
544 			sound.setVolume(_soundHandles[handleIndex], 0, 1);
545 			sleep(1000);
546 		}
547 
548 		sound.stopSound(_soundHandles[handleIndex]);
549 		_soundHandles[handleIndex] = -1;
550 	}
551 }
552 
setAmbientSoundVolume(VolumeMode mode,uint seconds,int handleIndex)553 void CGameObject::setAmbientSoundVolume(VolumeMode mode, uint seconds, int handleIndex) {
554 	CGameManager *gameManager = getGameManager();
555 	if (!gameManager)
556 		return;
557 	CSound &sound = gameManager->_sound;
558 
559 	if (handleIndex == -1) {
560 		// Iterate through calling the method for each handle
561 		for (int idx = 0; idx < 3; ++idx)
562 			setAmbientSoundVolume(mode, seconds, idx);
563 	} else if (handleIndex >= 0 && handleIndex <= 3 && _soundHandles[handleIndex] != -1) {
564 		uint newVolume = sound._soundManager.getModeVolume(mode);
565 		sound.setVolume(_soundHandles[handleIndex], newVolume, seconds);
566 	}
567 }
568 
stopSoundChannel(bool channel3)569 void CGameObject::stopSoundChannel(bool channel3) {
570 	getGameManager()->_sound.stopChannel(channel3 ? 3 : 0);
571 }
572 
setVisible(bool val)573 void CGameObject::setVisible(bool val) {
574 	if (val != _visible) {
575 		_visible = val;
576 		makeDirty();
577 	}
578 }
579 
petHighlightGlyph(int val)580 void CGameObject::petHighlightGlyph(int val) {
581 	CPetControl *pet = getPetControl();
582 	if (pet)
583 		pet->highlightGlyph(val);
584 }
585 
petHideCursor()586 void CGameObject::petHideCursor() {
587 	CPetControl *pet = getPetControl();
588 	if (pet)
589 		pet->hideCursor();
590 }
591 
petShowCursor()592 void CGameObject::petShowCursor() {
593 	CPetControl *pet = getPetControl();
594 	if (pet)
595 		pet->showCursor();
596 }
597 
petShow()598 void CGameObject::petShow() {
599 	CGameManager *gameManager = getGameManager();
600 	if (gameManager) {
601 		gameManager->_gameState._petActive = true;
602 		gameManager->_gameState.setMode(GSMODE_INTERACTIVE);
603 		gameManager->markAllDirty();
604 	}
605 }
606 
petHide()607 void CGameObject::petHide() {
608 	CGameManager *gameManager = getGameManager();
609 	if (gameManager) {
610 		gameManager->_gameState._petActive = false;
611 		gameManager->_gameState.setMode(GSMODE_INTERACTIVE);
612 		gameManager->markAllDirty();
613 	}
614 }
615 
petIncAreaLocks()616 void CGameObject::petIncAreaLocks() {
617 	CPetControl *pet = getPetControl();
618 	if (pet)
619 		pet->incAreaLocks();
620 }
621 
petDecAreaLocks()622 void CGameObject::petDecAreaLocks() {
623 	CPetControl *pet = getPetControl();
624 	if (pet)
625 		pet->decAreaLocks();
626 }
627 
petSetRemoteTarget()628 void CGameObject::petSetRemoteTarget() {
629 	CPetControl *pet = getPetControl();
630 	if (pet)
631 		pet->setRemoteTarget(this);
632 }
633 
playMovie(uint flags)634 void CGameObject::playMovie(uint flags) {
635 	_frameNumber = -1;
636 
637 	if (!_surface && !_resource.empty()) {
638 		loadResource(_resource);
639 		_resource.clear();
640 	}
641 
642 	CGameObject *obj = (flags & MOVIE_NOTIFY_OBJECT) ? this : nullptr;
643 	if (_surface) {
644 		_surface->playMovie(flags, obj);
645 		if (flags & MOVIE_WAIT_FOR_FINISH)
646 			getGameManager()->_gameState.addMovie(_surface->_movie);
647 	}
648 }
649 
playMovie(int startFrame,int endFrame,uint flags)650 void CGameObject::playMovie(int startFrame, int endFrame, uint flags) {
651 	_frameNumber = -1;
652 
653 	if (!_surface && !_resource.empty()) {
654 		loadResource(_resource);
655 		_resource.clear();
656 	}
657 
658 	CGameObject *obj = (flags & MOVIE_NOTIFY_OBJECT) ? this : nullptr;
659 	if (_surface) {
660 		_surface->playMovie(startFrame, endFrame, flags, obj);
661 		if (flags & MOVIE_WAIT_FOR_FINISH)
662 			getGameManager()->_gameState.addMovie(_surface->_movie);
663 	}
664 }
665 
666 
playMovie(int startFrame,int endFrame,int initialFrame,uint flags)667 void CGameObject::playMovie(int startFrame, int endFrame, int initialFrame, uint flags) {
668 	_frameNumber = -1;
669 
670 	if (!_surface && !_resource.empty()) {
671 		loadResource(_resource);
672 		_resource.clear();
673 	}
674 
675 	CGameObject *obj = (flags & MOVIE_NOTIFY_OBJECT) ? this : nullptr;
676 	if (_surface) {
677 		_surface->playMovie(startFrame, endFrame, initialFrame, flags, obj);
678 		if (flags & MOVIE_WAIT_FOR_FINISH)
679 			getGameManager()->_gameState.addMovie(_surface->_movie);
680 	}
681 }
682 
playClip(const CString & name,uint flags)683 void CGameObject::playClip(const CString &name, uint flags) {
684 	debugC(DEBUG_DETAILED, kDebugScripts, "playClip - %s", name.c_str());
685 
686 	_frameNumber = -1;
687 	CMovieClip *clip = _movieClips.findByName(name);
688 	if (clip)
689 		playMovie(clip->_startFrame, clip->_endFrame, flags);
690 }
691 
playClip(uint startFrame,uint endFrame)692 void CGameObject::playClip(uint startFrame, uint endFrame) {
693 	debugC(DEBUG_DETAILED, kDebugScripts, "playClip - %d to %d", startFrame, endFrame);
694 
695 	CMovieClip *clip = new CMovieClip("", startFrame, endFrame);
696 	CGameManager *gameManager = getGameManager();
697 	CRoomItem *room = gameManager->getRoom();
698 
699 	gameManager->playClip(clip, room, room);
700 	delete clip;
701 }
702 
playRandomClip(const char * const * names,uint flags)703 void CGameObject::playRandomClip(const char *const *names, uint flags) {
704 	// Count size of array
705 	int count = 0;
706 	for (const char *const *p = names; *p; ++p)
707 		++count;
708 
709 	// Play clip
710 	const char *name = names[g_vm->getRandomNumber(count - 1)];
711 	playClip(name, flags);
712 }
713 
playCutscene(uint startFrame,uint endFrame)714 bool CGameObject::playCutscene(uint startFrame, uint endFrame) {
715 	if (!_surface) {
716 		if (!_resource.empty())
717 			loadResource(_resource);
718 		_resource.clear();
719 	}
720 
721 	bool result = true;
722 	if (_surface && _surface->loadIfReady() && _surface->_movie) {
723 		disableMouse();
724 		result = _surface->_movie->playCutscene(_bounds, startFrame, endFrame);
725 		enableMouse();
726 	}
727 
728 	return result;
729 }
730 
savePosition()731 void CGameObject::savePosition() {
732 	_savedPos = _bounds;
733 }
734 
resetPosition()735 void CGameObject::resetPosition() {
736 	setPosition(_savedPos);
737 }
738 
setPosition(const Point & newPos)739 void CGameObject::setPosition(const Point &newPos) {
740 	makeDirty();
741 	_bounds.moveTo(newPos);
742 	makeDirty();
743 }
744 
checkStartDragging(CMouseDragStartMsg * msg)745 bool CGameObject::checkStartDragging(CMouseDragStartMsg *msg) {
746 	if (_visible && checkPoint(msg->_mousePos, msg->_handled, 1)) {
747 		savePosition();
748 		msg->_dragItem = this;
749 		return true;
750 	} else {
751 		return false;
752 	}
753 }
754 
hasActiveMovie() const755 bool CGameObject::hasActiveMovie() const {
756 	if (_surface && _surface->_movie)
757 		return _surface->_movie->isActive();
758 	return false;
759 }
760 
getMovieFrame() const761 int CGameObject::getMovieFrame() const {
762 	if (_surface && _surface->_movie)
763 		return _surface->_movie->getFrame();
764 	else if (_frameNumber > 0)
765 		// WORKAROUND: If an object has a pending frame to be set to,
766 		// but the movie hasn't yet been loaded, return that frame
767 		return _frameNumber;
768 
769 	return _initialFrame;
770 }
771 
surfaceHasFrame() const772 bool CGameObject::surfaceHasFrame() const {
773 	return _surface ? _surface->hasFrame() : false;
774 }
775 
loadSound(const CString & name)776 void CGameObject::loadSound(const CString &name) {
777 	CGameManager *gameManager = getGameManager();
778 	if (gameManager) {
779 		g_vm->_filesManager->preload(name);
780 		if (!name.empty())
781 			gameManager->_sound.loadSound(name);
782 	}
783 }
784 
playSound(const CString & name,uint volume,int balance,bool repeated)785 int CGameObject::playSound(const CString &name, uint volume, int balance, bool repeated) {
786 	CProximity prox;
787 	prox._channelVolume = volume;
788 	prox._balance = balance;
789 	prox._repeated = repeated;
790 	return playSound(name, prox);
791 }
792 
playSound(const CString & name,CProximity & prox)793 int CGameObject::playSound(const CString &name, CProximity &prox) {
794 	if (prox._positioningMode == POSMODE_VECTOR) {
795 		// If the proximity doesn't have a position defined, default it to
796 		// the position of the view to which the game object belongs
797 		if (prox._posX == 0.0 && prox._posY == 0.0 && prox._posZ == 0.0)
798 			findView()->getPosition(prox._posX, prox._posY, prox._posZ);
799 	}
800 
801 	CGameManager *gameManager = getGameManager();
802 	if (gameManager && !name.empty()) {
803 		g_vm->_filesManager->preload(name);
804 
805 		return gameManager->_sound.playSound(name, prox);
806 	}
807 
808 	return -1;
809 }
810 
queueSound(const CString & name,uint priorHandle,uint volume,int balance,bool repeated,Audio::Mixer::SoundType soundType)811 int CGameObject::queueSound(const CString &name, uint priorHandle, uint volume, int balance, bool repeated,
812 		Audio::Mixer::SoundType soundType) {
813 	CProximity prox;
814 	prox._balance = balance;
815 	prox._repeated = repeated;
816 	prox._channelVolume = volume;
817 	prox._priorSoundHandle = priorHandle;
818 	prox._soundType = soundType;
819 
820 	return playSound(name, prox);
821 }
822 
stopSound(int handle,uint seconds)823 void CGameObject::stopSound(int handle, uint seconds) {
824 	if (handle != 0 && handle != -1) {
825 		CGameManager *gameManager = getGameManager();
826 		if (gameManager) {
827 			if (seconds) {
828 				gameManager->_sound.setVolume(handle, 0, seconds);
829 				gameManager->_sound.setCanFree(handle);
830 			} else {
831 				gameManager->_sound.stopSound(handle);
832 			}
833 		}
834 	}
835 }
836 
addTimer(int endVal,uint firstDuration,uint repeatDuration)837 int CGameObject::addTimer(int endVal, uint firstDuration, uint repeatDuration) {
838 	CTimeEventInfo *timer = new CTimeEventInfo(getTicksCount(), repeatDuration != 0,
839 		firstDuration, repeatDuration, this, endVal, CString());
840 
841 	getGameManager()->addTimer(timer);
842 	return timer->_id;
843 }
844 
addTimer(uint firstDuration,uint repeatDuration)845 int CGameObject::addTimer(uint firstDuration, uint repeatDuration) {
846 	CTimeEventInfo *timer = new CTimeEventInfo(getTicksCount(), repeatDuration != 0,
847 		firstDuration, repeatDuration, this, 0, CString());
848 
849 	CGameManager *gameMan = getGameManager();
850 	if (gameMan)
851 		gameMan->addTimer(timer);
852 	return timer->_id;
853 }
854 
stopTimer(int id)855 void CGameObject::stopTimer(int id) {
856 	CGameManager *gameMan = getGameManager();
857 	if (gameMan)
858 		gameMan->stopTimer(id);
859 }
860 
startAnimTimer(const CString & action,uint firstDuration,uint repeatDuration)861 int CGameObject::startAnimTimer(const CString &action, uint firstDuration, uint repeatDuration) {
862 	CGameManager *gameMan = getGameManager();
863 	if (gameMan) {
864 		CTimeEventInfo *timer = new CTimeEventInfo(getTicksCount(), repeatDuration > 0,
865 			firstDuration, repeatDuration, this, 0, action);
866 		gameMan->addTimer(timer);
867 		return timer->_id;
868 	}
869 
870 	return -1;
871 }
872 
stopAnimTimer(int id)873 void CGameObject::stopAnimTimer(int id) {
874 	CGameManager *gameMan = getGameManager();
875 	if (gameMan)
876 		gameMan->stopTimer(id);
877 }
878 
gotoView(const CString & viewName,const CString & clipName)879 void CGameObject::gotoView(const CString &viewName, const CString &clipName) {
880 	CViewItem *newView = parseView(viewName);
881 	CGameManager *gameManager = getGameManager();
882 	CViewItem *oldView = gameManager->getView();
883 	if (!oldView || !newView)
884 		return;
885 
886 	CMovieClip *clip = nullptr;
887 	if (clipName.empty()) {
888 		CLinkItem *link = oldView->findLink(newView);
889 		if (link)
890 			clip = link->getClip();
891 	} else {
892 		clip = oldView->findNode()->findRoom()->findClip(clipName);
893 	}
894 
895 	// Change the view
896 	gameManager->_gameState.changeView(newView, clip);
897 }
898 
parseView(const CString & viewString)899 CViewItem *CGameObject::parseView(const CString &viewString) {
900 	return getRoot()->parseView(viewString);
901 }
902 
getViewFullName() const903 CString CGameObject::getViewFullName() const {
904 	CGameManager *gameManager = getGameManager();
905 	CViewItem *view = gameManager->getView();
906 	CNodeItem *node = view->findNode();
907 	CRoomItem *room = node->findRoom();
908 
909 	return CString::format("%s.%s.%s", room->getName().c_str(),
910 		node->getName().c_str(), view->getName().c_str());
911 }
912 
sleep(uint milli)913 void CGameObject::sleep(uint milli) {
914 	// Use an empty event target so that standard scene drawing won't happen
915 	Events &events = *g_vm->_events;
916 	CEventTarget nullTarget;
917 	events.addTarget(&nullTarget);
918 	events.sleep(milli);
919 	events.removeTarget();
920 }
921 
getMousePos() const922 Point CGameObject::getMousePos() const {
923 	return getGameManager()->_gameState.getMousePos();
924 }
925 
compareViewNameTo(const CString & name) const926 bool CGameObject::compareViewNameTo(const CString &name) const {
927 	return !getViewFullName().compareToIgnoreCase(name);
928 }
929 
compareRoomNameTo(const CString & name)930 bool CGameObject::compareRoomNameTo(const CString &name) {
931 	CRoomItem *room = getGameManager()->getRoom();
932 	return !room->getName().compareToIgnoreCase(name);
933 }
934 
getRoomName() const935 CString CGameObject::getRoomName() const {
936 	CRoomItem *room = getRoom();
937 	return room ? room->getName() : CString();
938 }
939 
getRoomNodeName() const940 CString CGameObject::getRoomNodeName() const {
941 	CNodeItem *node = getNode();
942 	if (!node)
943 		return CString();
944 
945 	CRoomItem *room = node->findRoom();
946 
947 	return CString::format("%s.%s", room->getName().c_str(), node->getName().c_str());
948 }
949 
getFullViewName()950 CString CGameObject::getFullViewName() {
951 	CGameManager *gameManager = getGameManager();
952 	return gameManager ? gameManager->getFullViewName() : CString();
953 }
954 
getMailManFirstObject() const955 CGameObject *CGameObject::getMailManFirstObject() const {
956 	CMailMan *mailMan = getMailMan();
957 	return mailMan ? mailMan->getFirstObject() : nullptr;
958 }
959 
getMailManNextObject(CGameObject * prior) const960 CGameObject *CGameObject::getMailManNextObject(CGameObject *prior) const {
961 	CMailMan *mailMan = getMailMan();
962 	return mailMan ? mailMan->getNextObject(prior) : nullptr;
963 }
964 
findMailByFlags(RoomFlagsComparison compareType,uint roomFlags)965 CGameObject *CGameObject::findMailByFlags(RoomFlagsComparison compareType, uint roomFlags) {
966 	CMailMan *mailMan = getMailMan();
967 	if (!mailMan)
968 		return nullptr;
969 
970 	for (CGameObject *obj = mailMan->getFirstObject(); obj;
971 			obj = mailMan->getNextObject(obj)) {
972 		if (compareRoomFlags(compareType, obj->_roomFlags, roomFlags))
973 			return obj;
974 	}
975 
976 	return nullptr;
977 }
978 
getNextMail(CGameObject * prior)979 CGameObject *CGameObject::getNextMail(CGameObject *prior) {
980 	CMailMan *mailMan = getMailMan();
981 	return mailMan ? mailMan->getNextObject(prior) : nullptr;
982 }
983 
findRoomObject(const CString & name) const984 CGameObject *CGameObject::findRoomObject(const CString &name) const {
985 	return dynamic_cast<CGameObject *>(findRoom()->findByName(name));
986 }
987 
findInRoom(const CString & name)988 CGameObject *CGameObject::findInRoom(const CString &name) {
989 	CRoomItem *room = getRoom();
990 	return room ? dynamic_cast<CGameObject *>(room->findByName(name)) : nullptr;
991 }
992 
find(const CString & name,CGameObject ** item,int findAreas)993 Found CGameObject::find(const CString &name, CGameObject **item, int findAreas) {
994 	CGameObject *go;
995 	*item = nullptr;
996 
997 	// Scan under PET if flagged
998 	if (findAreas & FIND_PET) {
999 		for (go = getPetControl()->getFirstObject(); go; go = getPetControl()->getNextObject(go)) {
1000 			if (go->getName() == name) {
1001 				*item = go;
1002 				return FOUND_PET;
1003 			}
1004 		}
1005 	}
1006 
1007 	if (findAreas & FIND_MAILMAN) {
1008 		for (go = getMailManFirstObject(); go; go = getMailManNextObject(go)) {
1009 			if (go->getName() == name) {
1010 				*item = go;
1011 				return FOUND_MAILMAN;
1012 			}
1013 		}
1014 	}
1015 
1016 	if (findAreas & FIND_GLOBAL) {
1017 		go = dynamic_cast<CGameObject *>(getRoot()->findByName(name));
1018 		if (go) {
1019 			*item = go;
1020 			return FOUND_GLOBAL;
1021 		}
1022 	}
1023 
1024 	if (findAreas & FIND_ROOM) {
1025 		go = findRoomObject(name);
1026 		if (go) {
1027 			*item = go;
1028 			return FOUND_ROOM;
1029 		}
1030 	}
1031 
1032 	return FOUND_NONE;
1033 }
1034 
moveToView()1035 void CGameObject::moveToView() {
1036 	CViewItem *view = getGameManager()->getView();
1037 	detach();
1038 	addUnder(view);
1039 }
1040 
moveToView(const CString & name)1041 void CGameObject::moveToView(const CString &name) {
1042 	CViewItem *view = parseView(name);
1043 	detach();
1044 	addUnder(view);
1045 }
1046 
stateChangeSeason()1047 void CGameObject::stateChangeSeason() {
1048 	getGameManager()->_gameState.changeSeason();
1049 }
1050 
stateGetSeason() const1051 Season CGameObject::stateGetSeason() const {
1052 	return getGameManager()->_gameState._seasonNum;
1053 }
1054 
stateSetParrotMet()1055 void CGameObject::stateSetParrotMet() {
1056 	getGameManager()->_gameState.setParrotMet(true);
1057 }
1058 
stateGetParrotMet() const1059 bool CGameObject::stateGetParrotMet() const {
1060 	return getGameManager()->_gameState.getParrotMet();
1061 }
1062 
incParrotResponse()1063 void CGameObject::incParrotResponse() {
1064 	getGameManager()->_gameState.incParrotResponse();
1065 }
1066 
getParrotResponse() const1067 int CGameObject::getParrotResponse() const {
1068 	return getGameManager()->_gameState._parrotResponseIndex;
1069 }
1070 
quitGame()1071 void CGameObject::quitGame() {
1072 	getGameManager()->_gameState._quitGame = true;
1073 }
1074 
incTransitions()1075 void CGameObject::incTransitions() {
1076 	getGameManager()->incTransitions();
1077 }
1078 
decTransitions()1079 void CGameObject::decTransitions() {
1080 	getGameManager()->decTransitions();
1081 }
1082 
setMovieFrameRate(double rate)1083 void CGameObject::setMovieFrameRate(double rate) {
1084 	if (_surface)
1085 		_surface->setMovieFrameRate(rate);
1086 }
1087 
setText(const CString & str,int border,int borderRight)1088 void CGameObject::setText(const CString &str, int border, int borderRight) {
1089 	if (!_text)
1090 		_text = new CTextControl();
1091 	_textBorder = border;
1092 	_textBorderRight = borderRight;
1093 
1094 	setTextBounds();
1095 	_text->setText(str);
1096 	CScreenManager *screenManager = getGameManager()->setScreenManager();
1097 	_text->scrollToTop(screenManager);
1098 }
1099 
setTextHasBorders(bool hasBorders)1100 void CGameObject::setTextHasBorders(bool hasBorders) {
1101 	if (!_text)
1102 		_text = new CTextControl();
1103 
1104 	_text->setHasBorder(hasBorders);
1105 }
1106 
setTextBounds()1107 void CGameObject::setTextBounds() {
1108 	Rect rect = _bounds;
1109 	rect.grow(_textBorder);
1110 	rect.right -= _textBorderRight;
1111 
1112 	_text->setBounds(rect);
1113 	makeDirty();
1114 }
1115 
setTextColor(byte r,byte g,byte b)1116 void CGameObject::setTextColor(byte r, byte g, byte b) {
1117 	if (!_text)
1118 		_text = new CTextControl();
1119 
1120 	_text->setColor(r, g, b);
1121 }
1122 
setTextFontNumber(int fontNumber)1123 void CGameObject::setTextFontNumber(int fontNumber) {
1124 	if (!_text)
1125 		_text = new CTextControl();
1126 
1127 	_text->setFontNumber(fontNumber);
1128 }
1129 
getTextWidth() const1130 int CGameObject::getTextWidth() const {
1131 	assert(_text);
1132 	return _text->getTextWidth(CScreenManager::_screenManagerPtr);
1133 }
1134 
getTextCursor() const1135 CTextCursor *CGameObject::getTextCursor() const {
1136 	return CScreenManager::_screenManagerPtr->_textCursor;
1137 }
1138 
getMovement() const1139 Movement CGameObject::getMovement() const {
1140 	return CLinkItem::getMovementFromCursor(_cursorId);
1141 }
1142 
scrollTextUp()1143 void CGameObject::scrollTextUp() {
1144 	if (_text)
1145 		_text->scrollUp(CScreenManager::_screenManagerPtr);
1146 	makeDirty();
1147 }
1148 
scrollTextDown()1149 void CGameObject::scrollTextDown() {
1150 	if (_text)
1151 		_text->scrollDown(CScreenManager::_screenManagerPtr);
1152 	makeDirty();
1153 }
1154 
lockMouse()1155 void CGameObject::lockMouse() {
1156 	CGameManager *gameMan = getGameManager();
1157 	gameMan->lockInputHandler();
1158 
1159 	if (CScreenManager::_screenManagerPtr->_mouseCursor)
1160 		CScreenManager::_screenManagerPtr->_mouseCursor->incBusyCount();
1161 }
1162 
unlockMouse()1163 void CGameObject::unlockMouse() {
1164 	if (CScreenManager::_screenManagerPtr->_mouseCursor)
1165 		CScreenManager::_screenManagerPtr->_mouseCursor->decBusyCount();
1166 
1167 	CGameManager *gameMan = getGameManager();
1168 	gameMan->unlockInputHandler();
1169 }
1170 
hideMouse()1171 void CGameObject::hideMouse() {
1172 	CScreenManager::_screenManagerPtr->_mouseCursor->incHideCounter();
1173 }
1174 
showMouse()1175 void CGameObject::showMouse() {
1176 	CScreenManager::_screenManagerPtr->_mouseCursor->decHideCounter();
1177 }
1178 
disableMouse()1179 void CGameObject::disableMouse() {
1180 	lockInputHandler();
1181 	hideMouse();
1182 }
1183 
enableMouse()1184 void CGameObject::enableMouse() {
1185 	unlockInputHandler();
1186 	showMouse();
1187 }
1188 
mouseDisableControl()1189 void CGameObject::mouseDisableControl() {
1190 	CScreenManager::_screenManagerPtr->_mouseCursor->disableControl();
1191 }
1192 
mouseEnableControl()1193 void CGameObject::mouseEnableControl() {
1194 	CScreenManager::_screenManagerPtr->_mouseCursor->enableControl();
1195 }
1196 
mouseSetPosition(const Point & pt,double rate)1197 void CGameObject::mouseSetPosition(const Point &pt, double rate) {
1198 	CScreenManager::_screenManagerPtr->_mouseCursor->setPosition(pt, rate);
1199 }
1200 
lockInputHandler()1201 void CGameObject::lockInputHandler() {
1202 	getGameManager()->lockInputHandler();
1203 }
1204 
unlockInputHandler()1205 void CGameObject::unlockInputHandler() {
1206 	getGameManager()->unlockInputHandler();
1207 }
1208 
loadSurface()1209 void CGameObject::loadSurface() {
1210 	if (!_surface && !_resource.empty()) {
1211 		loadResource(_resource);
1212 		_resource.clear();
1213 	}
1214 
1215 	if (_surface)
1216 		_surface->loadIfReady();
1217 }
1218 
changeView(const CString & viewName)1219 bool CGameObject::changeView(const CString &viewName) {
1220 	return getRoot()->changeView(viewName, "");
1221 }
1222 
changeView(const CString & viewName,const CString & clipName)1223 bool CGameObject::changeView(const CString &viewName, const CString &clipName) {
1224 	return getRoot()->changeView(viewName, clipName);
1225 }
1226 
dragMove(const Point & pt)1227 void CGameObject::dragMove(const Point &pt) {
1228 	if (_surface) {
1229 		_bounds.setWidth(_surface->getWidth());
1230 		_bounds.setHeight(_surface->getHeight());
1231 	}
1232 
1233 	setPosition(Point(pt.x - _bounds.width() / 2, pt.y - _bounds.height() / 2));
1234 }
1235 
getDraggingObject() const1236 CGameObject *CGameObject::getDraggingObject() const {
1237 	CTreeItem *item = getGameManager()->_dragItem;
1238 	return dynamic_cast<CGameObject *>(item);
1239 }
1240 
getControid() const1241 Point CGameObject::getControid() const {
1242 	return Point(_bounds.left + _bounds.width() / 2,
1243 		_bounds.top + _bounds.height() / 2);
1244 }
1245 
clipExistsByStart(const CString & name,int startFrame) const1246 bool CGameObject::clipExistsByStart(const CString &name, int startFrame) const {
1247 	return _movieClips.existsByStart(name, startFrame);
1248 }
1249 
clipExistsByEnd(const CString & name,int endFrame) const1250 bool CGameObject::clipExistsByEnd(const CString &name, int endFrame) const {
1251 	return _movieClips.existsByEnd(name, endFrame);
1252 }
1253 
petClear() const1254 void CGameObject::petClear() const {
1255 	CPetControl *petControl = getPetControl();
1256 	if (petControl)
1257 		petControl->resetRemoteTarget();
1258 }
1259 
getDontSave() const1260 CDontSaveFileItem *CGameObject::getDontSave() const {
1261 	CProjectItem *project = getRoot();
1262 	return project ? project->getDontSaveFileItem() : nullptr;
1263 }
1264 
getPetControl() const1265 CPetControl *CGameObject::getPetControl() const {
1266 	return dynamic_cast<CPetControl *>(getDontSaveChild(CPetControl::_type));
1267 }
1268 
getMailMan() const1269 CMailMan *CGameObject::getMailMan() const {
1270 	return dynamic_cast<CMailMan *>(getDontSaveChild(CMailMan::_type));
1271 }
1272 
getDontSaveChild(ClassDef * classDef) const1273 CTreeItem *CGameObject::getDontSaveChild(ClassDef *classDef) const {
1274 	CProjectItem *root = getRoot();
1275 	if (!root)
1276 		return nullptr;
1277 
1278 	CDontSaveFileItem *dontSave = root->getDontSaveFileItem();
1279 	if (!dontSave)
1280 		return nullptr;
1281 
1282 	return dontSave->findChildInstanceOf(classDef);
1283 }
1284 
getHiddenRoom() const1285 CRoomItem *CGameObject::getHiddenRoom() const {
1286 	CProjectItem *root = getRoot();
1287 	return root ? root->findHiddenRoom() : nullptr;
1288 }
1289 
locateRoom(const CString & name) const1290 CRoomItem *CGameObject::locateRoom(const CString &name) const {
1291 	if (name.empty())
1292 		return nullptr;
1293 
1294 	CProjectItem *project = getRoot();
1295 	for (CRoomItem *room = project->findFirstRoom(); room; room = project->findNextRoom(room)) {
1296 		if (!room->getName().compareToIgnoreCase(name))
1297 			return room;
1298 	}
1299 
1300 	return nullptr;
1301 }
1302 
getHiddenObject(const CString & name) const1303 CGameObject *CGameObject::getHiddenObject(const CString &name) const {
1304 	CRoomItem *room = getHiddenRoom();
1305 	return room ? dynamic_cast<CGameObject *>(findUnder(room, name)) : nullptr;
1306 }
1307 
findUnder(CTreeItem * parent,const CString & name) const1308 CTreeItem *CGameObject::findUnder(CTreeItem *parent, const CString &name) const {
1309 	if (!parent)
1310 		return nullptr;
1311 
1312 	for (CTreeItem *item = parent->getFirstChild(); item; item = item->scan(parent)) {
1313 		if (item->getName() == name)
1314 			return item;
1315 	}
1316 
1317 	return nullptr;
1318 }
1319 
findRoomByName(const CString & name)1320 CRoomItem *CGameObject::findRoomByName(const CString &name) {
1321 	CProjectItem *project = getRoot();
1322 	for (CRoomItem *room = project->findFirstRoom(); room; room = project->findNextRoom(room)) {
1323 		if (!room->getName().compareToIgnoreCase(name))
1324 			return room;
1325 	}
1326 
1327 	return nullptr;
1328 }
1329 
getMusicRoom() const1330 CMusicRoom *CGameObject::getMusicRoom() const {
1331 	CGameManager *gameManager = getGameManager();
1332 	return gameManager ? &gameManager->_musicRoom : nullptr;
1333 }
1334 
getPassengerClass() const1335 PassengerClass CGameObject::getPassengerClass() const {
1336 	CGameManager *gameManager = getGameManager();
1337 	return gameManager ? gameManager->_gameState._passengerClass : THIRD_CLASS;
1338 }
1339 
getPriorClass() const1340 PassengerClass CGameObject::getPriorClass() const {
1341 	CGameManager *gameManager = getGameManager();
1342 	return gameManager ? gameManager->_gameState._priorClass : THIRD_CLASS;
1343 }
1344 
setPassengerClass(PassengerClass newClass)1345 void CGameObject::setPassengerClass(PassengerClass newClass) {
1346 	if (newClass >= 1 && newClass <= 4) {
1347 		// Change the passenger class
1348 		CGameManager *gameMan = getGameManager();
1349 		gameMan->_gameState._priorClass = gameMan->_gameState._passengerClass;
1350 		gameMan->_gameState._passengerClass = newClass;
1351 
1352 		// Setup the PET again, so the new class's PET background can take effect
1353 		CPetControl *petControl = getPetControl();
1354 		if (petControl)
1355 			petControl->reset();
1356 	}
1357 }
1358 
createCredits()1359 void CGameObject::createCredits() {
1360 	_credits = new CCreditText();
1361 	CScreenManager *screenManager = getGameManager()->setScreenManager();
1362 	_credits->load(this, screenManager, _bounds);
1363 }
1364 
setToggleColor(byte r,byte g,byte b)1365 void CGameObject::setToggleColor(byte r, byte g, byte b) {
1366 	makeDirty();
1367 	_toggleR = r;
1368 	_toggleG = g;
1369 	_toggleB = b;
1370 }
1371 
movieSetPlaying(bool flag)1372 void CGameObject::movieSetPlaying(bool flag) {
1373 	if (!_surface && !_resource.empty()) {
1374 		loadResource(_resource);
1375 		_resource.clear();
1376 	}
1377 
1378 	if (_surface && _surface->_movie)
1379 		_surface->_movie->setPlaying(flag);
1380 }
1381 
movieEvent(int frameNumber)1382 void CGameObject::movieEvent(int frameNumber) {
1383 	if (_surface)
1384 		_surface->addMovieEvent(frameNumber, this);
1385 }
1386 
movieEvent()1387 void CGameObject::movieEvent() {
1388 	if (_surface)
1389 		_surface->addMovieEvent(-1, this);
1390 }
1391 
getClipDuration(const CString & name,int frameRate) const1392 int CGameObject::getClipDuration(const CString &name, int frameRate) const {
1393 	CMovieClip *clip = _movieClips.findByName(name);
1394 	return clip ? (clip->_endFrame - clip->_startFrame) * 1000 / frameRate : 0;
1395 }
1396 
getTicksCount()1397 uint32 CGameObject::getTicksCount() {
1398 	return g_vm->_events->getTicksCount();
1399 }
1400 
getResource(const CString & name)1401 Common::SeekableReadStream *CGameObject::getResource(const CString &name) {
1402 	return g_vm->_filesManager->getResource(name);
1403 }
1404 
compareRoomFlags(RoomFlagsComparison compareType,uint flags1,uint flags2)1405 bool CGameObject::compareRoomFlags(RoomFlagsComparison compareType, uint flags1, uint flags2) {
1406 	switch (compareType) {
1407 	case RFC_LOCATION:
1408 		return CRoomFlags::compareLocation(flags1, flags2);
1409 
1410 	case RFC_CLASS_ELEVATOR:
1411 		return CRoomFlags::compareClassElevator(flags1, flags2);
1412 
1413 	case RFC_TITANIA:
1414 		return CRoomFlags::isTitania(flags1, flags2);
1415 
1416 	default:
1417 		return false;
1418 	}
1419 }
1420 
stateSetSoundMakerAllowed(bool flag)1421 void CGameObject::stateSetSoundMakerAllowed(bool flag) {
1422 	getGameManager()->_gameState._soundMakerAllowed = flag;
1423 }
1424 
addMail(uint destRoomFlags)1425 void CGameObject::addMail(uint destRoomFlags) {
1426 	CMailMan *mailMan = getMailMan();
1427 	if (mailMan) {
1428 		makeDirty();
1429 		mailMan->addMail(this, destRoomFlags);
1430 	}
1431 }
1432 
setMailDest(uint roomFlags)1433 void CGameObject::setMailDest(uint roomFlags) {
1434 	CMailMan *mailMan = getMailMan();
1435 	if (mailMan) {
1436 		makeDirty();
1437 		mailMan->setMailDest(this, roomFlags);
1438 	}
1439 }
1440 
mailExists(uint roomFlags) const1441 bool CGameObject::mailExists(uint roomFlags) const {
1442 	return findMail(roomFlags) != nullptr;
1443 }
1444 
findMail(uint roomFlags) const1445 CGameObject *CGameObject::findMail(uint roomFlags) const {
1446 	CMailMan *mailMan = getMailMan();
1447 	return mailMan ? mailMan->findMail(roomFlags) : nullptr;
1448 }
1449 
sendMail(uint currRoomFlags,uint newRoomFlags)1450 void CGameObject::sendMail(uint currRoomFlags, uint newRoomFlags) {
1451 	CMailMan *mailMan = getMailMan();
1452 	if (mailMan)
1453 		mailMan->sendMail(currRoomFlags, newRoomFlags);
1454 }
1455 
resetMail()1456 void CGameObject::resetMail() {
1457 	CMailMan *mailMan = getMailMan();
1458 	if (mailMan)
1459 		mailMan->resetValue();
1460 }
1461 
getRandomNumber(int max,int * oldVal)1462 int CGameObject::getRandomNumber(int max, int *oldVal) {
1463 	if (oldVal) {
1464 		int startingVal = *oldVal;
1465 		while (*oldVal == startingVal && max > 0)
1466 			*oldVal = g_vm->getRandomNumber(max);
1467 
1468 		return *oldVal;
1469 	} else {
1470 		return g_vm->getRandomNumber(max);
1471 	}
1472 }
1473 
1474 /*------------------------------------------------------------------------*/
1475 
getRoom() const1476 CRoomItem *CGameObject::getRoom() const {
1477 	CGameManager *gameManager = getGameManager();
1478 	return gameManager ? gameManager->getRoom() : nullptr;
1479 }
1480 
getNode() const1481 CNodeItem *CGameObject::getNode() const {
1482 	CGameManager *gameManager = getGameManager();
1483 	return gameManager ? gameManager->getNode() : nullptr;
1484 }
1485 
getView() const1486 CViewItem *CGameObject::getView() const {
1487 	CGameManager *gameManager = getGameManager();
1488 	return gameManager ? gameManager->getView() : nullptr;
1489 }
1490 
1491 /*------------------------------------------------------------------------*/
1492 
petAddToCarryParcel(CGameObject * obj)1493 void CGameObject::petAddToCarryParcel(CGameObject *obj) {
1494 	CPetControl *pet = getPetControl();
1495 	if (pet) {
1496 		CGameObject *parcel = pet->getHiddenObject("CarryParcel");
1497 		if (parcel)
1498 			parcel->moveUnder(obj);
1499 	}
1500 }
1501 
petAddToInventory()1502 void CGameObject::petAddToInventory() {
1503 	assert(dynamic_cast<CCarry *>(this));
1504 
1505 	CPetControl *pet = getPetControl();
1506 	if (pet) {
1507 		makeDirty();
1508 		pet->addToInventory(this);
1509 	}
1510 }
1511 
petContainerRemove(CGameObject * obj)1512 CTreeItem *CGameObject::petContainerRemove(CGameObject *obj) {
1513 	CPetControl *pet = getPetControl();
1514 	if (!obj || !pet)
1515 		return nullptr;
1516 	if (!obj->compareRoomNameTo("CarryParcel"))
1517 		return obj;
1518 
1519 	CGameObject *item = dynamic_cast<CGameObject *>(pet->getLastChild());
1520 	if (item)
1521 		item->detach();
1522 
1523 	pet->moveToHiddenRoom(obj);
1524 	pet->removeFromInventory(item, false, false);
1525 
1526 	return item;
1527 }
1528 
petCheckNode(const CString & name)1529 bool CGameObject::petCheckNode(const CString &name) {
1530 	CPetControl *pet = getPetControl();
1531 	return pet ? pet->checkNode(name) : false;
1532 }
1533 
petDismissBot(const CString & name)1534 bool CGameObject::petDismissBot(const CString &name) {
1535 	CPetControl *pet = getPetControl();
1536 	return pet ? pet->dismissBot(name) : false;
1537 }
1538 
petDoorOrBellbotPresent() const1539 bool CGameObject::petDoorOrBellbotPresent() const {
1540 	CPetControl *pet = getPetControl();
1541 	return pet ? pet->isDoorOrBellbotPresent() : false;
1542 }
1543 
petDisplayMessage(int unused,StringId stringId)1544 void CGameObject::petDisplayMessage(int unused, StringId stringId) {
1545 	petDisplayMessage(stringId);
1546 }
1547 
petDisplayMessage(StringId stringId,int param)1548 void CGameObject::petDisplayMessage(StringId stringId, int param) {
1549 	CPetControl *pet = getPetControl();
1550 	if (pet)
1551 		pet->displayMessage(stringId, param);
1552 }
1553 
petDisplayMessage(int unused,const CString & msg)1554 void CGameObject::petDisplayMessage(int unused, const CString &msg) {
1555 	petDisplayMessage(msg);
1556 }
1557 
petDisplayMessage(const CString & msg,int param)1558 void CGameObject::petDisplayMessage(const CString &msg, int param) {
1559 	CPetControl *pet = getPetControl();
1560 	if (pet)
1561 		pet->displayMessage(msg, param);
1562 }
1563 
petInvChange()1564 void CGameObject::petInvChange() {
1565 	CPetControl *pet = getPetControl();
1566 	if (pet)
1567 		pet->invChange(this);
1568 }
1569 
petLockInput()1570 void CGameObject::petLockInput() {
1571 	getPetControl()->incInputLocks();
1572 }
1573 
petMoveToHiddenRoom()1574 void CGameObject::petMoveToHiddenRoom() {
1575 	CPetControl *pet = getPetControl();
1576 	if (pet) {
1577 		makeDirty();
1578 		pet->moveToHiddenRoom(this);
1579 	}
1580 }
1581 
petReassignRoom(PassengerClass passClassNum)1582 void CGameObject::petReassignRoom(PassengerClass passClassNum) {
1583 	CPetControl *petControl = getPetControl();
1584 	if (petControl)
1585 		petControl->reassignRoom(passClassNum);
1586 }
1587 
petSetArea(PetArea newArea) const1588 void CGameObject::petSetArea(PetArea newArea) const {
1589 	CPetControl *pet = getPetControl();
1590 	if (pet)
1591 		pet->setArea(newArea);
1592 }
1593 
petSetRoomsWellEntry(int entryNum)1594 void CGameObject::petSetRoomsWellEntry(int entryNum) {
1595 	CPetControl *petControl = getPetControl();
1596 	if (petControl)
1597 		petControl->setRoomsWellEntry(entryNum);
1598 }
1599 
petGetRoomsWellEntry() const1600 int CGameObject::petGetRoomsWellEntry() const {
1601 	CPetControl *petControl = getPetControl();
1602 	return petControl ? petControl->getRoomsWellEntry() : 0;
1603 }
1604 
petSetRoomsElevatorBroken(bool flag)1605 void CGameObject::petSetRoomsElevatorBroken(bool flag) {
1606 	CPetControl *pet = getPetControl();
1607 	if (pet)
1608 		pet->setRoomsElevatorBroken(flag);
1609 }
1610 
petOnSummonBot(const CString & name,int val)1611 void CGameObject::petOnSummonBot(const CString &name, int val) {
1612 	CPetControl *pet = getPetControl();
1613 	if (pet)
1614 		pet->onSummonBot(name, val);
1615 }
1616 
petUnlockInput()1617 void CGameObject::petUnlockInput() {
1618 	getPetControl()->decInputLocks();
1619 }
1620 
1621 /*------------------------------------------------------------------------*/
1622 
getStarControl() const1623 CStarControl *CGameObject::getStarControl() const {
1624 	CStarControl *starControl = dynamic_cast<CStarControl *>(getDontSaveChild(CStarControl::_type));
1625 	if (!starControl) {
1626 		CViewItem *view = getGameManager()->getView();
1627 		if (view)
1628 			starControl = dynamic_cast<CStarControl *>(view->findChildInstanceOf(CStarControl::_type));
1629 	}
1630 
1631 	return starControl;
1632 }
1633 
starFn(StarControlAction action)1634 void CGameObject::starFn(StarControlAction action) {
1635 	CStarControl *starControl = getStarControl();
1636 	if (starControl)
1637 		starControl->doAction(action);
1638 }
1639 
starIsSolved() const1640 bool CGameObject::starIsSolved() const {
1641 	CStarControl *starControl = getStarControl();
1642 	return starControl ? starControl->isSolved() : false;
1643 }
1644 
1645 /*------------------------------------------------------------------------*/
1646 
startTalking(const CString & npcName,uint id,CViewItem * view)1647 void CGameObject::startTalking(const CString &npcName, uint id, CViewItem *view) {
1648 	CTrueTalkNPC *npc = static_cast<CTrueTalkNPC *>(getRoot()->findByName(npcName));
1649 	startTalking(npc, id, view);
1650 }
1651 
startTalking(CTrueTalkNPC * npc,uint id,CViewItem * view)1652 void CGameObject::startTalking(CTrueTalkNPC *npc, uint id, CViewItem *view) {
1653 	CGameManager *gameManager = getGameManager();
1654 	if (gameManager) {
1655 		CTrueTalkManager *talkManager = gameManager->getTalkManager();
1656 		if (talkManager)
1657 			talkManager->start(npc, id, view);
1658 	}
1659 }
1660 
setTalking(CTrueTalkNPC * npc,bool viewFlag,CViewItem * view)1661 void CGameObject::setTalking(CTrueTalkNPC *npc, bool viewFlag, CViewItem *view) {
1662 	CPetControl *pet = getPetControl();
1663 	if (pet)
1664 		pet->setActiveNPC(npc);
1665 
1666 	if (viewFlag)
1667 		npc->setView(view);
1668 
1669 	if (pet)
1670 		pet->refreshNPC();
1671 }
1672 
talkSetDialRegion(const CString & name,int dialNum,int regionNum)1673 void CGameObject::talkSetDialRegion(const CString &name, int dialNum, int regionNum) {
1674 	CGameManager *gameManager = getGameManager();
1675 	if (gameManager) {
1676 		CTrueTalkManager *talkManager = gameManager->getTalkManager();
1677 		if (talkManager) {
1678 			TTnpcScript *npcScript = talkManager->getTalker(name);
1679 			if (npcScript)
1680 				npcScript->setDialRegion(dialNum, regionNum);
1681 		}
1682 	}
1683 }
1684 
talkGetDialRegion(const CString & name,int dialNum)1685 int CGameObject::talkGetDialRegion(const CString &name, int dialNum) {
1686 	CGameManager *gameManager = getGameManager();
1687 	if (gameManager) {
1688 		CTrueTalkManager *talkManager = gameManager->getTalkManager();
1689 		if (talkManager) {
1690 			TTnpcScript *npcScript = talkManager->getTalker(name);
1691 			if (npcScript)
1692 				return npcScript->getDialRegion(dialNum);
1693 		}
1694 	}
1695 
1696 	return 0;
1697 }
1698 
1699 /*------------------------------------------------------------------------*/
1700 
getNodeChangedCtr() const1701 uint CGameObject::getNodeChangedCtr() const {
1702 	return getGameManager()->_gameState.getNodeChangedCtr();
1703 }
1704 
getNodeEnterTicks() const1705 uint CGameObject::getNodeEnterTicks() const {
1706 	return getGameManager()->_gameState.getNodeEnterTicks();
1707 }
1708 
1709 
1710 } // End of namespace Titanic
1711