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