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