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  * Cursor and cursor trails.
22  */
23 
24 #include "tinsel/cursor.h"
25 
26 #include "tinsel/anim.h"
27 #include "tinsel/background.h"
28 #include "tinsel/cursor.h"
29 #include "tinsel/dw.h"
30 #include "tinsel/events.h"		// For EventsManager class
31 #include "tinsel/film.h"
32 #include "tinsel/graphics.h"
33 #include "tinsel/handle.h"
34 #include "tinsel/dialogs.h"
35 #include "tinsel/multiobj.h"	// multi-part object defintions etc.
36 #include "tinsel/object.h"
37 #include "tinsel/pid.h"
38 #include "tinsel/play.h"
39 #include "tinsel/sched.h"
40 #include "tinsel/sysvar.h"
41 #include "tinsel/text.h"
42 #include "tinsel/timers.h"		// For ONE_SECOND constant
43 #include "tinsel/tinlib.h"		// resetidletime()
44 #include "tinsel/tinsel.h"		// For engine access
45 
46 
47 namespace Tinsel {
48 
49 //----------------- LOCAL DEFINES --------------------
50 
51 #define ITERATION_BASE		FRAC_ONE
52 #define ITER_ACCELERATION	(10L << (FRAC_BITS - 4))
53 
Cursor()54 Cursor::Cursor() {
55 	_mainCursor = nullptr;
56 	_auxCursor = nullptr;
57 
58 	_mainCursorAnim = {0, 0, 0, 0, 0};
59 	_auxCursorAnim = {0, 0, 0, 0, 0};
60 
61 	_hiddenCursor = false;
62 	_hiddenTrails = false;
63 	_tempHiddenCursor = false;
64 	_frozenCursor = false;
65 
66 	_iterationSize = 0;
67 
68 	_cursorFilm = 0;
69 
70 	_numTrails = 0;
71 	_nextTrail = 0;
72 
73 	_cursorProcessesStopped = false;
74 	_cursorProcessesRestarted = false;
75 
76 	_auxCursorOffsetX = 0;
77 	_auxCursorOffsetY = 0;
78 
79 	_lastCursorX = 0;
80 	_lastCursorY = 0;
81 }
82 
83 /**
84  * Initialize and insert a cursor trail object, set its Z-pos, and hide
85  * it. Also initialize its animation script.
86  */
InitCurTrailObj(int i,int x,int y)87 void Cursor::InitCurTrailObj(int i, int x, int y) {
88 	const FREEL *pfr;		// pointer to reel
89 	IMAGE *pim;		// pointer to image
90 	const MULTI_INIT *pmi;		// MULTI_INIT structure
91 
92 	const FILM *pfilm;
93 
94 	if (!_numTrails)
95 		return;
96 
97 	// Get rid of old object
98 	if (_trailData[i].trailObj != NULL)
99 		MultiDeleteObject(_vm->_bg->GetPlayfieldList(FIELD_STATUS), _trailData[i].trailObj);
100 
101 	pim = GetImageFromFilm(_cursorFilm, i+1, &pfr, &pmi, &pfilm);// Get pointer to image
102 	assert(_vm->_bg->BgPal()); // No background palette
103 	pim->hImgPal = TO_32(_vm->_bg->BgPal());
104 
105 	// Initialize and insert the object, set its Z-pos, and hide it
106 	_trailData[i].trailObj = MultiInitObject(pmi);
107 	MultiInsertObject(_vm->_bg->GetPlayfieldList(FIELD_STATUS), _trailData[i].trailObj);
108 	MultiSetZPosition(_trailData[i].trailObj, Z_CURSORTRAIL);
109 	MultiSetAniXY(_trailData[i].trailObj, x, y);
110 
111 	// Initialize the animation script
112 	InitStepAnimScript(&_trailData[i].trailAnim, _trailData[i].trailObj, FROM_32(pfr->script), ONE_SECOND / FROM_32(pfilm->frate));
113 	StepAnimScript(&_trailData[i].trailAnim);
114 }
115 
116 /**
117  * Get the cursor position from the mouse driver.
118  */
GetDriverPosition(int * x,int * y)119 bool Cursor::GetDriverPosition(int *x, int *y) {
120 	Common::Point ptMouse = _vm->getMousePosition();
121 	*x = ptMouse.x;
122 	*y = ptMouse.y;
123 
124 	return(*x >= 0 && *x <= SCREEN_WIDTH - 1 &&
125 		*y >= 0 && *y <= SCREEN_HEIGHT - 1);
126 }
127 
128 /**
129  * Move the cursor relative to current position.
130  */
AdjustCursorXY(int deltaX,int deltaY)131 void Cursor::AdjustCursorXY(int deltaX, int deltaY) {
132 	int x, y;
133 
134 	if (deltaX || deltaY) {
135 		if (GetDriverPosition(&x, &y))
136 			_vm->setMousePosition(Common::Point(x + deltaX, y + deltaY));
137 	}
138 	DoCursorMove();
139 }
140 
141 /**
142  * Move the cursor to an absolute position.
143  */
SetCursorXY(int newx,int newy)144 void Cursor::SetCursorXY(int newx, int newy) {
145 	int	x, y;
146 	int	Loffset, Toffset;	// Screen offset
147 
148 	_vm->_bg->PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
149 	newx -= Loffset;
150 	newy -= Toffset;
151 
152 	if (GetDriverPosition(&x, &y))
153 		_vm->setMousePosition(Common::Point(newx, newy));
154 	DoCursorMove();
155 }
156 
157 /**
158  * Move the cursor to a screen position.
159  */
SetCursorScreenXY(int newx,int newy)160 void Cursor::SetCursorScreenXY(int newx, int newy) {
161 	int	x, y;
162 
163 	if (GetDriverPosition(&x, &y))
164 		_vm->setMousePosition(Common::Point(newx, newy));
165 	DoCursorMove();
166 }
167 
168 /**
169  * Called by the world and his brother.
170  * Returns the cursor's animation position in (x,y).
171  * Returns false if there is no cursor object.
172  */
GetCursorXYNoWait(int * x,int * y,bool absolute)173 bool Cursor::GetCursorXYNoWait(int *x, int *y, bool absolute) {
174 	if (_mainCursor == NULL) {
175 		*x = *y = 0;
176 		return false;
177 	}
178 
179 	GetAniPosition(_mainCursor, x, y);
180 
181 	if (absolute) {
182 		int	Loffset, Toffset;	// Screen offset
183 		_vm->_bg->PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
184 		*x += Loffset;
185 		*y += Toffset;
186 	}
187 
188 	return true;
189 }
190 
191 /**
192  * Called by the world and his brother.
193  * Returns the cursor's animation position.
194  * If called while there is no cursor object, the calling process ends
195  * up waiting until there is.
196  */
GetCursorXY(int * x,int * y,bool absolute)197 void Cursor::GetCursorXY(int *x, int *y, bool absolute) {
198 	//while (McurObj == NULL)
199 	//	ProcessSleepSelf();
200 	assert(_mainCursor);
201 	GetCursorXYNoWait(x, y, absolute);
202 }
203 
204 /**
205  * Re-initialize the main cursor to use the main cursor reel.
206  * Called from TINLIB.C to restore cursor after hiding it.
207  * Called from INVENTRY.C to restore cursor after customising it.
208  */
RestoreMainCursor()209 void Cursor::RestoreMainCursor() {
210 	const FILM *pfilm;
211 
212 	if (_mainCursor != NULL) {
213 		pfilm = (const FILM *)_vm->_handle->LockMem(_cursorFilm);
214 
215 		InitStepAnimScript(&_mainCursorAnim, _mainCursor, FROM_32(pfilm->reels->script), ONE_SECOND / FROM_32(pfilm->frate));
216 		StepAnimScript(&_mainCursorAnim);
217 	}
218 	_hiddenCursor = false;
219 	_frozenCursor = false;
220 }
221 
222 /**
223  * Called from INVENTRY.C to customise the main cursor.
224  */
SetTempCursor(SCNHANDLE pScript)225 void Cursor::SetTempCursor(SCNHANDLE pScript) {
226 	if (_mainCursor != NULL)
227 		InitStepAnimScript(&_mainCursorAnim, _mainCursor, pScript, 2);
228 }
229 
230 /**
231  * Hide the cursor.
232  */
DwHideCursor()233 void Cursor::DwHideCursor() {
234 	_hiddenCursor = true;
235 
236 	if (_mainCursor)
237 		MultiHideObject(_mainCursor);
238 	if (_auxCursor)
239 		MultiHideObject(_auxCursor);
240 
241 	for (int i = 0; i < _numTrails; i++) {
242 		if (_trailData[i].trailObj != NULL) {
243 			MultiDeleteObject(_vm->_bg->GetPlayfieldList(FIELD_STATUS), _trailData[i].trailObj);
244 			_trailData[i].trailObj = nullptr;
245 		}
246 	}
247 }
248 
HideCursorProcess()249 void Cursor::HideCursorProcess() {
250 	if (_mainCursor)
251 		MultiHideObject(_mainCursor);
252 	if (_auxCursor)
253 		MultiHideObject(_auxCursor);
254 
255 	for (int i = 0; i < _numTrails; i++) {
256 		if (_trailData[i].trailObj != NULL)
257 			MultiHideObject(_trailData[i].trailObj);
258 	}
259 }
260 
261 /**
262  * Unhide the cursor.
263  */
UnHideCursor()264 void Cursor::UnHideCursor() {
265 	_hiddenCursor = false;
266 }
267 
268 /**
269  * Freeze the cursor.
270  */
FreezeCursor()271 void Cursor::FreezeCursor() {
272 	_frozenCursor = true;
273 }
274 
275 /**
276  * Freeze the cursor, or not.
277  */
DoFreezeCursor(bool bFreeze)278 void Cursor::DoFreezeCursor(bool bFreeze) {
279 	_frozenCursor = bFreeze;
280 }
281 
282 /**
283  * HideCursorTrails
284  */
HideCursorTrails()285 void Cursor::HideCursorTrails() {
286 	int i;
287 
288 	_hiddenTrails = true;
289 
290 	for (i = 0; i < _numTrails; i++)	{
291 		if (_trailData[i].trailObj != NULL) {
292 			MultiDeleteObject(_vm->_bg->GetPlayfieldList(FIELD_STATUS), _trailData[i].trailObj);
293 			_trailData[i].trailObj = nullptr;
294 		}
295 	}
296 }
297 
298 /**
299  * UnHideCursorTrails
300  */
UnHideCursorTrails()301 void Cursor::UnHideCursorTrails() {
302 	_hiddenTrails = false;
303 }
304 
305 /**
306  * Get pointer to image from a film reel. And the rest.
307  */
GetImageFromReel(const FREEL * pfr,const MULTI_INIT ** ppmi)308 IMAGE *Cursor::GetImageFromReel(const FREEL *pfr, const MULTI_INIT **ppmi) {
309 	const MULTI_INIT *pmi;
310 	const FRAME *pFrame;
311 
312 	pmi = (const MULTI_INIT *)_vm->_handle->LockMem(FROM_32(pfr->mobj));
313 	if (ppmi)
314 		*ppmi = pmi;
315 
316 	pFrame = (const FRAME *)_vm->_handle->LockMem(FROM_32(pmi->hMulFrame));
317 
318 	// get pointer to image
319 	return (IMAGE *)_vm->_handle->LockMem(READ_32(pFrame));
320 }
321 
322 /**
323  * Get pointer to image from a film. And the rest.
324  */
GetImageFromFilm(SCNHANDLE hFilm,int reel,const FREEL ** ppfr,const MULTI_INIT ** ppmi,const FILM ** ppfilm)325 IMAGE *Cursor::GetImageFromFilm(SCNHANDLE hFilm, int reel, const FREEL **ppfr, const MULTI_INIT **ppmi, const FILM **ppfilm) {
326 	const FILM *pfilm;
327 	const FREEL *pfr;
328 
329 	pfilm = (const FILM *)_vm->_handle->LockMem(hFilm);
330 	if (ppfilm)
331 		*ppfilm = pfilm;
332 
333 	pfr = &pfilm->reels[reel];
334 	if (ppfr)
335 		*ppfr = pfr;
336 
337 	return GetImageFromReel(pfr, ppmi);
338 }
339 
340 /**
341  * Delete auxillary cursor. Restore animation offsets in the image.
342  */
DelAuxCursor()343 void Cursor::DelAuxCursor() {
344 	if (_auxCursor != NULL) {
345 		MultiDeleteObject(_vm->_bg->GetPlayfieldList(FIELD_STATUS), _auxCursor);
346 		_auxCursor = nullptr;
347 	}
348 }
349 
350 /**
351  * Set auxillary cursor.
352  * Save animation offsets from the image if required.
353  */
SetAuxCursor(SCNHANDLE hFilm)354 void Cursor::SetAuxCursor(SCNHANDLE hFilm) {
355 	IMAGE *pim;		// Pointer to auxillary cursor's image
356 	const FREEL *pfr;
357 	const MULTI_INIT *pmi;
358 	const FILM *pfilm;
359 	int	x, y;		// Cursor position
360 
361 	DelAuxCursor();		// Get rid of previous
362 
363 	// WORKAROUND: There's no palette when loading a DW1 savegame with a held item, so exit if so
364 	if (!_vm->_bg->BgPal())
365 		return;
366 
367 	GetCursorXY(&x, &y, false);	// Note: also waits for cursor to appear
368 
369 	pim = GetImageFromFilm(hFilm, 0, &pfr, &pmi, &pfilm);// Get pointer to image
370 	assert(_vm->_bg->BgPal()); // no background palette
371 	pim->hImgPal = TO_32(_vm->_bg->BgPal());			// Poke in the background palette
372 
373 	_auxCursorOffsetX = (short)(FROM_16(pim->imgWidth)/2 - ((int16) FROM_16(pim->anioffX)));
374 	_auxCursorOffsetY = (short)((FROM_16(pim->imgHeight) & ~C16_FLAG_MASK)/2 -
375 		((int16) FROM_16(pim->anioffY)));
376 
377 	// Initialize and insert the auxillary cursor object
378 	_auxCursor = MultiInitObject(pmi);
379 	MultiInsertObject(_vm->_bg->GetPlayfieldList(FIELD_STATUS), _auxCursor);
380 
381 	// Initialize the animation and set its position
382 	InitStepAnimScript(&_auxCursorAnim, _auxCursor, FROM_32(pfr->script), ONE_SECOND / FROM_32(pfilm->frate));
383 	MultiSetAniXY(_auxCursor, x - _auxCursorOffsetX, y - _auxCursorOffsetY);
384 	MultiSetZPosition(_auxCursor, Z_ACURSOR);
385 
386 	if (_hiddenCursor)
387 		MultiHideObject(_auxCursor);
388 }
389 
390 /**
391  * MoveCursor
392  */
DoCursorMove()393 void Cursor::DoCursorMove() {
394 	int	startX, startY;
395 	Common::Point ptMouse;
396 	frac_t newX, newY;
397 	unsigned dir;
398 
399 	// get cursors start animation position
400 	GetCursorXYNoWait(&startX, &startY, false);
401 
402 	// get mouse drivers current position
403 	ptMouse = _vm->getMousePosition();
404 
405 	// convert to fixed point
406 	newX = intToFrac(ptMouse.x);
407 	newY = intToFrac(ptMouse.y);
408 
409 	// modify mouse driver position depending on cursor keys
410 	dir = _vm->getKeyDirection();
411 	if (dir != 0) {
412 		if (dir & MSK_LEFT)
413 			newX -= _iterationSize;
414 
415 		if (dir & MSK_RIGHT)
416 			newX += _iterationSize;
417 
418 		if (dir & MSK_UP)
419 			newY -= _iterationSize;
420 
421 		if (dir & MSK_DOWN)
422 			newY += _iterationSize;
423 
424 		_iterationSize += ITER_ACCELERATION;
425 
426 		// set new mouse driver position
427 		_vm->setMousePosition(Common::Point(fracToInt(newX), fracToInt(newY)));
428 	} else
429 
430 		_iterationSize = ITERATION_BASE;
431 
432 	// get new mouse driver position - could have been modified
433 	ptMouse = _vm->getMousePosition();
434 
435 	if (_lastCursorX != ptMouse.x || _lastCursorY != ptMouse.y) {
436 		resetUserEventTime();
437 
438 		if (!_hiddenTrails && !_hiddenCursor) {
439 			InitCurTrailObj(_nextTrail++, _lastCursorX, _lastCursorY);
440 			if (_nextTrail == _numTrails)
441 				_nextTrail = 0;
442 		}
443 	}
444 
445 	// adjust cursor to new mouse position
446 	if (_mainCursor)
447 		MultiSetAniXY(_mainCursor, ptMouse.x, ptMouse.y);
448 	if (_auxCursor != NULL)
449 		MultiSetAniXY(_auxCursor, ptMouse.x - _auxCursorOffsetX, ptMouse.y - _auxCursorOffsetY);
450 
451 	if (_vm->_dialogs->InventoryActive() && _mainCursor) {
452 		// Notify the inventory
453 		_vm->_dialogs->Xmovement(ptMouse.x - startX);
454 		_vm->_dialogs->Ymovement(ptMouse.y - startY);
455 	}
456 
457 	_lastCursorX = ptMouse.x;
458 	_lastCursorY = ptMouse.y;
459 }
460 
461 /**
462  * Initialize cursor object.
463  */
InitCurObj()464 void Cursor::InitCurObj() {
465 	const FILM *pFilm;
466 	const FREEL *pfr;
467 	const MULTI_INIT *pmi;
468 	IMAGE *pim;
469 
470 	if (TinselV2 || TinselV3) {
471 		pFilm = (const FILM *)_vm->_handle->LockMem(_cursorFilm);
472 		pfr = (const FREEL *)&pFilm->reels[0];
473 		pmi = (MULTI_INIT *)_vm->_handle->LockMem(FROM_32(pfr->mobj));
474 
475 		if (!TinselV3) {
476 			PokeInPalette(pmi);
477 		}
478 	} else {
479 		assert(_vm->_bg->BgPal()); // no background palette
480 
481 		pim = GetImageFromFilm(_cursorFilm, 0, &pfr, &pmi, &pFilm);// Get pointer to image
482 		pim->hImgPal = TO_32(_vm->_bg->BgPal());
483 
484 		_auxCursor = nullptr;		// No auxillary cursor
485 	}
486 
487 	_mainCursor = MultiInitObject(pmi);
488 	MultiInsertObject(_vm->_bg->GetPlayfieldList(FIELD_STATUS), _mainCursor);
489 
490 	InitStepAnimScript(&_mainCursorAnim, _mainCursor, FROM_32(pfr->script), ONE_SECOND / FROM_32(pFilm->frate));
491 }
492 
493 /**
494  * Initialize the cursor position.
495  */
InitCurPos()496 void Cursor::InitCurPos() {
497 	Common::Point ptMouse = _vm->getMousePosition();
498 	_lastCursorX = ptMouse.x;
499 	_lastCursorY = ptMouse.y;
500 
501 	MultiSetZPosition(_mainCursor, Z_CURSOR);
502 	DoCursorMove();
503 	MultiHideObject(_mainCursor);
504 
505 	_iterationSize = ITERATION_BASE;
506 }
507 
508 /**
509  * Called from dec_cursor() Glitter function.
510  * Register the handle to cursor reel data.
511  */
DwInitCursor(SCNHANDLE bfilm)512 void Cursor::DwInitCursor(SCNHANDLE bfilm) {
513 	const FILM *pfilm;
514 
515 	_cursorFilm = bfilm;
516 
517 	pfilm = (const FILM *)_vm->_handle->LockMem(_cursorFilm);
518 	_numTrails = FROM_32(pfilm->numreels) - 1;
519 
520 	assert(_numTrails <= MAX_TRAILERS);
521 }
522 
523 /**
524  * DropCursor is called when a scene is closing down.
525  */
DropCursor()526 void Cursor::DropCursor() {
527 	if (TinselV2) {
528 		if (_auxCursor)
529 			MultiDeleteObject(_vm->_bg->GetPlayfieldList(FIELD_STATUS), _auxCursor);
530 		if (_mainCursor)
531 			MultiDeleteObject(_vm->_bg->GetPlayfieldList(FIELD_STATUS), _mainCursor);
532 
533 		_cursorProcessesRestarted = false;
534 	}
535 
536 	_auxCursor = nullptr;		// No auxillary cursor
537 	_mainCursor = nullptr;		// No cursor object (imminently deleted elsewhere)
538 	_hiddenCursor = false;	// Not hidden in next scene
539 	_hiddenTrails = false;	// Trailers not hidden in next scene
540 	_cursorProcessesStopped = true;		// Suspend cursor processes
541 
542 	for (int i = 0; i < _numTrails; i++) {
543 		if (_trailData[i].trailObj != NULL)		{
544 			MultiDeleteObject(_vm->_bg->GetPlayfieldList(FIELD_STATUS), _trailData[i].trailObj);
545 			_trailData[i].trailObj = nullptr;
546 		}
547 	}
548 }
549 
550 /**
551  * RestartCursor is called when a new scene is starting up.
552  */
RestartCursor()553 void Cursor::RestartCursor() {
554 	_cursorProcessesRestarted = true;	// Get the main cursor to re-initialize
555 }
556 
557 /**
558  * Called when restarting the game, ensures correct re-start with NULL
559  * pointers etc.
560  */
RebootCursor()561 void Cursor::RebootCursor() {
562 	_mainCursor = _auxCursor = nullptr;
563 	for (int i = 0; i < MAX_TRAILERS; i++)
564 		_trailData[i].trailObj = nullptr;
565 
566 	_hiddenCursor = _hiddenTrails = _frozenCursor = false;
567 
568 	_cursorFilm = 0;
569 
570 	_cursorProcessesStopped = false;
571 	_cursorProcessesRestarted = false;
572 }
573 
StartCursorFollowed()574 void Cursor::StartCursorFollowed() {
575 	DelAuxCursor();
576 
577 	if (!SysVar(SV_ENABLEPRINTCURSOR))
578 		_tempHiddenCursor = true;
579 }
580 
EndCursorFollowed()581 void Cursor::EndCursorFollowed() {
582 	_vm->_dialogs->InventoryIconCursor(false); // May be holding something
583 	_tempHiddenCursor = false;
584 }
585 
isCursorShown()586 bool Cursor::isCursorShown() {
587 	return !(_tempHiddenCursor || _hiddenCursor);
588 }
589 
AnimateProcess()590 void Cursor::AnimateProcess() {
591 	// Step the animation script(s)
592 	StepAnimScript(&_mainCursorAnim);
593 	if (_auxCursor != NULL)
594 		StepAnimScript(&_auxCursorAnim);
595 	for (int i = 0; i < _vm->_cursor->NumTrails(); i++) {
596 		if (_trailData[i].trailObj != NULL) {
597 			if (StepAnimScript(&_trailData[i].trailAnim) == ScriptFinished) {
598 				MultiDeleteObject(_vm->_bg->GetPlayfieldList(FIELD_STATUS), _trailData[i].trailObj);
599 				_trailData[i].trailObj = nullptr;
600 			}
601 		}
602 	}
603 
604 	// Move the cursor as appropriate
605 	if (!_vm->_cursor->CursorIsFrozen())
606 		_vm->_cursor->DoCursorMove();
607 }
608 
609 /**
610  * CursorStoppedCheck
611  */
CursorStoppedCheck(CORO_PARAM)612 void CursorStoppedCheck(CORO_PARAM) {
613 	// COROUTINE
614 	CORO_BEGIN_CONTEXT;
615 	CORO_END_CONTEXT(_ctx);
616 
617 	CORO_BEGIN_CODE(_ctx);
618 
619 	// If scene is closing down
620 	if (_vm->_cursor->_cursorProcessesStopped) {
621 		// ...wait for next scene start-up
622 		while (!_vm->_cursor->_cursorProcessesRestarted)
623 			CORO_SLEEP(1);
624 
625 		// Re-initialize
626 		_vm->_cursor->InitCurObj();
627 		_vm->_cursor->InitCurPos();
628 		_vm->_dialogs->InventoryIconCursor(false); // May be holding something
629 
630 		// Re-start the cursor trails
631 		_vm->_cursor->_cursorProcessesRestarted = true;
632 		_vm->_cursor->_cursorProcessesStopped = false;
633 	}
634 	CORO_END_CODE;
635 }
636 
CanInitializeCursor()637 bool CanInitializeCursor() {
638 	if (!_vm->_cursor->HasReelData()) {
639 		return false;
640 	} else if (TinselVersion != TINSEL_V3) {
641 		return (_vm->_bg->BgPal() != 0);
642 	}
643 	return true;
644 }
645 
646 /**
647  * The main cursor process.
648  */
CursorProcess(CORO_PARAM,const void *)649 void CursorProcess(CORO_PARAM, const void *) {
650 	// COROUTINE
651 	CORO_BEGIN_CONTEXT;
652 	CORO_END_CONTEXT(_ctx);
653 
654 	CORO_BEGIN_CODE(_ctx);
655 
656 	while (!CanInitializeCursor())
657 		CORO_SLEEP(1);
658 
659 	_vm->_cursor->InitCurObj();
660 	_vm->_cursor->InitCurPos();
661 	_vm->_dialogs->InventoryIconCursor(false); // May be holding something
662 
663 	_vm->_cursor->_cursorProcessesStopped = false;
664 	_vm->_cursor->_cursorProcessesRestarted = false;
665 
666 	while (1) {
667 		// allow rescheduling
668 		CORO_SLEEP(1);
669 
670 		// Stop/start between scenes
671 		CORO_INVOKE_0(CursorStoppedCheck);
672 
673 		_vm->_cursor->AnimateProcess();
674 
675 		// If the cursor should be hidden...
676 		if (_vm->_cursor->ShouldBeHidden()) {
677 			_vm->_cursor->HideCursorProcess();
678 
679 			// Wait 'til cursor is again required.
680 			while (_vm->_cursor->IsHidden()) {
681 				CORO_SLEEP(1);
682 
683 				// Stop/start between scenes
684 				CORO_INVOKE_0(CursorStoppedCheck);
685 			}
686 		}
687 	}
688 	CORO_END_CODE;
689 }
690 
691 } // End of namespace Tinsel
692