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 * CursorPositionProcess()
22 * TagProcess()
23 * PointProcess()
24 */
25
26 #include "common/coroutines.h"
27 #include "tinsel/actors.h"
28 #include "tinsel/background.h"
29 #include "tinsel/cursor.h"
30 #include "tinsel/dw.h"
31 #include "tinsel/events.h"
32 #include "tinsel/font.h"
33 #include "tinsel/graphics.h"
34 #include "tinsel/multiobj.h"
35 #include "tinsel/object.h"
36 #include "tinsel/pcode.h"
37 #include "tinsel/polygons.h"
38 #include "tinsel/rince.h"
39 #include "tinsel/sched.h"
40 #include "tinsel/strres.h"
41 #include "tinsel/text.h"
42 #include "tinsel/tinsel.h"
43
44 #include "common/textconsole.h"
45
46 namespace Tinsel {
47
48 //----------------- EXTERNAL GLOBAL DATA --------------------
49
50 #ifdef DEBUG
51 //extern int Overrun; // The overrun counter, in DOS_DW.C
52
53 extern int g_newestString; // The overrun counter, in STRRES.C
54 #endif
55
56
57 //----------------- LOCAL DEFINES --------------------
58
59 #define LPOSX 295 // X-co-ord of lead actor's position display
60 #define CPOSX 24 // X-co-ord of cursor's position display
61 #define OPOSX SCRN_CENTER_X // X-co-ord of overrun counter's display
62 #define SPOSX SCRN_CENTER_X // X-co-ord of string numbner's display
63
64 #define POSY 0 // Y-co-ord of these position displays
65
66 enum HotSpotTag {
67 NO_HOTSPOT_TAG,
68 POLY_HOTSPOT_TAG,
69 ACTOR_HOTSPOT_TAG
70 };
71
72 //----------------- LOCAL GLOBAL DATA --------------------
73
74 // FIXME: Avoid non-const global vars
75
76 static bool g_DispPath = false;
77 static bool g_bShowString = false;
78
79 static int g_TaggedActor = 0;
80 static HPOLYGON g_hTaggedPolygon = NOPOLY;
81
82 static bool g_bTagsActive = true;
83
84 static bool g_bPointingActive = true;
85
86 static char g_tagBuffer[64];
87
88 #ifdef DEBUG
89 /**
90 * Displays the cursor and lead actor's co-ordinates and the overrun
91 * counter. Also which path polygon the cursor is in, if required.
92 *
93 * This process is only started up if a Glitter showpos() call is made.
94 * Obviously, this is for testing purposes only...
95 */
CursorPositionProcess(CORO_PARAM,const void *)96 void CursorPositionProcess(CORO_PARAM, const void *) {
97 // COROUTINE
98 CORO_BEGIN_CONTEXT;
99 int prevsX, prevsY; // Last screen top left
100 int prevcX, prevcY; // Last displayed cursor position
101 int prevlX, prevlY; // Last displayed lead actor position
102 // int prevOver; // Last displayed overrun
103 int prevString; // Last displayed string number
104
105 OBJECT *cpText; // cursor position text object pointer
106 OBJECT *cpathText; // cursor path text object pointer
107 OBJECT *rpText; // text object pointer
108 // OBJECT *opText; // text object pointer
109 OBJECT *spText; // string number text object pointer
110 CORO_END_CONTEXT(_ctx);
111
112 CORO_BEGIN_CODE(_ctx);
113
114 _ctx->prevsX = -1;
115 _ctx->prevsY = -1;
116 _ctx->prevcX = -1;
117 _ctx->prevcY = -1;
118 _ctx->prevlX = -1;
119 _ctx->prevlY = -1;
120 // _ctx->prevOver = -1;
121 _ctx->prevString = -1;
122
123 _ctx->cpText = NULL;
124 _ctx->cpathText = NULL;
125 _ctx->rpText = NULL;
126 // _ctx->opText = NULL;
127 _ctx->spText = NULL;
128
129
130 int aniX, aniY; // cursor/lead actor position
131 int Loffset, Toffset; // Screen top left
132
133 char PositionString[64]; // sprintf() things into here
134
135 PMOVER pActor; // Lead actor
136
137 while (1) {
138 PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
139
140 /*-----------------------------------*\
141 | Cursor's position and path display. |
142 \*-----------------------------------*/
143 GetCursorXY(&aniX, &aniY, false);
144
145 // Change in cursor position?
146 if (aniX != _ctx->prevcX || aniY != _ctx->prevcY ||
147 Loffset != _ctx->prevsX || Toffset != _ctx->prevsY) {
148 // kill current text objects
149 if (_ctx->cpText) {
150 MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), _ctx->cpText);
151 }
152 if (_ctx->cpathText) {
153 MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), _ctx->cpathText);
154 _ctx->cpathText = NULL;
155 }
156
157 // New text objects
158 sprintf(PositionString, "%d %d", aniX + Loffset, aniY + Toffset);
159 _ctx->cpText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), PositionString,
160 0, CPOSX, POSY, GetTagFontHandle(), TXT_CENTER);
161 if (g_DispPath) {
162 HPOLYGON hp = InPolygon(aniX + Loffset, aniY + Toffset, PATH);
163 if (hp == NOPOLY)
164 sprintf(PositionString, "No path");
165 else
166 sprintf(PositionString, "%d,%d %d,%d %d,%d %d,%d",
167 PolyCornerX(hp, 0), PolyCornerY(hp, 0),
168 PolyCornerX(hp, 1), PolyCornerY(hp, 1),
169 PolyCornerX(hp, 2), PolyCornerY(hp, 2),
170 PolyCornerX(hp, 3), PolyCornerY(hp, 3));
171 _ctx->cpathText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), PositionString,
172 0, 4, POSY+ 10, GetTagFontHandle(), 0);
173 }
174
175 // update previous position
176 _ctx->prevcX = aniX;
177 _ctx->prevcY = aniY;
178 }
179
180 #if 0
181 /*------------------------*\
182 | Overrun counter display. |
183 \*------------------------*/
184 if (Overrun != _ctx->prevOver) {
185 // kill current text objects
186 if (_ctx->opText) {
187 MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), _ctx->opText);
188 }
189
190 sprintf(PositionString, "%d", Overrun);
191 _ctx->opText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), PositionString,
192 0, OPOSX, POSY, GetTagFontHandle(), TXT_CENTER);
193
194 // update previous value
195 _ctx->prevOver = Overrun;
196 }
197 #endif
198
199 /*----------------------*\
200 | Lead actor's position. |
201 \*----------------------*/
202 pActor = GetMover(LEAD_ACTOR);
203 if (pActor && getMActorState(pActor)) {
204 // get lead's animation position
205 GetActorPos(LEAD_ACTOR, &aniX, &aniY);
206
207 // Change in position?
208 if (aniX != _ctx->prevlX || aniY != _ctx->prevlY ||
209 Loffset != _ctx->prevsX || Toffset != _ctx->prevsY) {
210 // Kill current text objects
211 if (_ctx->rpText) {
212 MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), _ctx->rpText);
213 }
214
215 // create new text object list
216 sprintf(PositionString, "%d %d", aniX, aniY);
217 _ctx->rpText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), PositionString,
218 0, LPOSX, POSY, GetTagFontHandle(), TXT_CENTER);
219
220 // update previous position
221 _ctx->prevlX = aniX;
222 _ctx->prevlY = aniY;
223 }
224 }
225
226 /*-------------*\
227 | String number |
228 \*-------------*/
229 if (g_bShowString && g_newestString != _ctx->prevString) {
230 // kill current text objects
231 if (_ctx->spText) {
232 MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), _ctx->spText);
233 }
234
235 sprintf(PositionString, "String: %d", g_newestString);
236 _ctx->spText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), PositionString,
237 0, SPOSX, POSY+10, GetTalkFontHandle(), TXT_CENTER);
238
239 // update previous value
240 _ctx->prevString = g_newestString;
241 }
242
243 // update previous playfield position
244 _ctx->prevsX = Loffset;
245 _ctx->prevsY = Toffset;
246
247 CORO_SLEEP(1); // allow re-scheduling
248 }
249 CORO_END_CODE;
250 }
251 #endif
252
253 /**
254 * While inventory/menu is open.
255 */
DisablePointing()256 void DisablePointing() {
257 int i;
258 HPOLYGON hPoly; // Polygon handle
259
260 g_bPointingActive = false;
261
262 for (i = 0; i < MAX_POLY; i++) {
263 hPoly = GetPolyHandle(i);
264
265 if (hPoly != NOPOLY && PolyType(hPoly) == TAG && PolyIsPointedTo(hPoly)) {
266 SetPolyPointedTo(hPoly, false);
267 SetPolyTagWanted(hPoly, false, false, 0);
268 PolygonEvent(Common::nullContext, hPoly, UNPOINT, 0, false, 0);
269 }
270 }
271
272 // For each tagged actor
273 for (i = 0; (i = NextTaggedActor(i)) != 0; ) {
274 if (ActorIsPointedTo(i)) {
275 SetActorPointedTo(i, false);
276 SetActorTagWanted(i, false, false, 0);
277
278 ActorEvent(Common::nullContext, i, UNPOINT, false, 0);
279 }
280 }
281 }
282
283 /**
284 * EnablePointing()
285 */
EnablePointing()286 void EnablePointing() {
287 g_bPointingActive = true;
288 }
289
290 /**
291 * Tag process keeps us updated as to which tagged actor is currently tagged
292 * (if one is). Tag process asks us for this information, as does ProcessUserEvent().
293 */
SaveTaggedActor(int ano)294 static void SaveTaggedActor(int ano) {
295 g_TaggedActor = ano;
296 }
297
298 /**
299 * Tag process keeps us updated as to which tagged actor is currently tagged
300 * (if one is). Tag process asks us for this information, as does ProcessUserEvent().
301 */
GetTaggedActor()302 int GetTaggedActor() {
303 return g_TaggedActor;
304 }
305
306 /**
307 * Tag process keeps us updated as to which polygon is currently tagged
308 * (if one is). Tag process asks us for this information, as does ProcessUserEvent().
309 */
SaveTaggedPoly(HPOLYGON hp)310 static void SaveTaggedPoly(HPOLYGON hp) {
311 g_hTaggedPolygon = hp;
312 }
313
GetTaggedPoly()314 HPOLYGON GetTaggedPoly() {
315 return g_hTaggedPolygon;
316 }
317
318 /**
319 * Given cursor position and an actor number, ascertains whether the
320 * cursor is within the actor's tag area.
321 * Returns TRUE for a positive result, FALSE for negative.
322 * If TRUE, the mid-top co-ordinates of the actor's tag area are also
323 * returned.
324 */
InHotSpot(int ano,int aniX,int aniY,int * pxtext,int * pytext)325 static bool InHotSpot(int ano, int aniX, int aniY, int *pxtext, int *pytext) {
326 int Top, Bot; // Top and bottom limits of active area
327 int left, right; // left and right of active area
328 int qrt = 0; // 1/4 of height (sometimes 1/2)
329
330 // First check if within x-range
331 if (aniX > (left = GetActorLeft(ano)) && aniX < (right = GetActorRight(ano))) {
332 Top = GetActorTop(ano);
333 Bot = GetActorBottom(ano);
334
335 // y-range varies according to tag-type
336 switch (TagType(ano)) {
337 case TAG_DEF:
338 // Next to bottom 1/4 of the actor's area
339 qrt = (Bot - Top) >> 1; // Half actor's height
340 Top += qrt; // Top = mid-height
341
342 qrt = qrt >> 1; // Quarter height
343 Bot -= qrt; // Bot = 1/4 way up
344 break;
345
346 case TAG_Q1TO3:
347 // Top 3/4 of the actor's area
348 qrt = (Bot - Top) >> 2; // 1/4 actor's height
349 Bot -= qrt; // Bot = 1/4 way up
350 break;
351
352 case TAG_Q1TO4:
353 // All the actor's area
354 break;
355
356 default:
357 error("illegal tag area type");
358 }
359
360 // Now check if within y-range
361 if (aniY >= Top && aniY <= Bot) {
362 if (TagType(ano) == TAG_Q1TO3)
363 *pytext = Top + qrt;
364 else
365 *pytext = Top;
366 *pxtext = (left + right) / 2;
367 return true;
368 }
369 }
370 return false;
371 }
372
373 /**
374 * See if the cursor is over a tagged actor's hot-spot. If so, display
375 * the tag or, if tag already displayed, maintain the tag's position on
376 * the screen.
377 */
ActorTag(int curX,int curY,HotSpotTag * pTag,OBJECT ** ppText)378 static bool ActorTag(int curX, int curY, HotSpotTag *pTag, OBJECT **ppText) {
379 // FIXME: Avoid non-const global vars
380 static int tagX = 0, tagY = 0; // Values when tag was displayed
381 int newX, newY; // new values, to keep tag in place
382 int ano;
383 int xtext, ytext;
384 bool newActor;
385
386 if (TinselV2) {
387 // Tinsel 2 version
388 // Get the foremost pointed to actor
389 int actor = FrontTaggedActor();
390
391 if (actor == 0) {
392 SaveTaggedActor(0);
393 return false;
394 }
395
396 // If new actor
397 // or actor has suddenly decided it wants tagging...
398 if (actor != GetTaggedActor() || (ActorTagIsWanted(actor) && !*ppText)) {
399 // Put up actor tag
400 SaveTaggedActor(actor); // This actor tagged
401 SaveTaggedPoly(NOPOLY); // No tagged polygon
402
403 if (*ppText)
404 MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), *ppText);
405
406 if (ActorTagIsWanted(actor)) {
407 GetActorTagPos(actor, &tagX, &tagY, false);
408 LoadStringRes(GetActorTagHandle(actor), g_tagBuffer, sizeof(g_tagBuffer));
409
410 // May have buggered cursor
411 EndCursorFollowed();
412 *ppText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), g_tagBuffer,
413 0, tagX, tagY, GetTagFontHandle(), TXT_CENTER, 0);
414 assert(*ppText);
415 MultiSetZPosition(*ppText, Z_TAG_TEXT);
416 } else
417 *ppText = NULL;
418 } else if (*ppText) {
419 // Same actor, maintain tag position
420 GetActorTagPos(actor, &newX, &newY, false);
421
422 if (newX != tagX || newY != tagY) {
423 MultiMoveRelXY(*ppText, newX - tagX, newY - tagY);
424 tagX = newX;
425 tagY = newY;
426 }
427 }
428
429 return true;
430 }
431
432 // Tinsel 1 version
433 // For each actor with a tag....
434 FirstTaggedActor();
435 while ((ano = NextTaggedActor()) != 0) {
436 if (InHotSpot(ano, curX, curY, &xtext, &ytext)) {
437 // Put up or maintain actor tag
438 if (*pTag != ACTOR_HOTSPOT_TAG)
439 newActor = true;
440 else if (ano != GetTaggedActor())
441 newActor = true; // Different actor
442 else
443 newActor = false; // Same actor
444
445 if (newActor) {
446 // Display actor's tag
447
448 if (*ppText)
449 MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), *ppText);
450
451 *pTag = ACTOR_HOTSPOT_TAG;
452 SaveTaggedActor(ano); // This actor tagged
453 SaveTaggedPoly(NOPOLY); // No tagged polygon
454
455 PlayfieldGetPos(FIELD_WORLD, &tagX, &tagY);
456 LoadStringRes(GetActorTag(ano), TextBufferAddr(), TBUFSZ);
457 *ppText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), TextBufferAddr(),
458 0, xtext - tagX, ytext - tagY, GetTagFontHandle(), TXT_CENTER);
459 assert(*ppText); // Actor tag string produced NULL text
460 MultiSetZPosition(*ppText, Z_TAG_TEXT);
461 } else {
462 // Maintain actor tag's position
463
464 PlayfieldGetPos(FIELD_WORLD, &newX, &newY);
465 if (newX != tagX || newY != tagY) {
466 MultiMoveRelXY(*ppText, tagX - newX, tagY - newY);
467 tagX = newX;
468 tagY = newY;
469 }
470 }
471 return true;
472 }
473 }
474
475 // No tagged actor
476 if (*pTag == ACTOR_HOTSPOT_TAG) {
477 *pTag = NO_HOTSPOT_TAG;
478 SaveTaggedActor(0);
479 }
480 return false;
481 }
482
483 /**
484 * Perhaps some comment in due course.
485 *
486 * Under control of PointProcess(), when the cursor is over a TAG or
487 * EXIT polygon, its pointState flag is set to POINTING. If its Glitter
488 * code contains a printtag() call, its tagState flag gets set to TAG_ON.
489 */
PolyTag(HotSpotTag * pTag,OBJECT ** ppText)490 static bool PolyTag(HotSpotTag *pTag, OBJECT **ppText) {
491 // FIXME: Avoid non-const global vars
492 static int Loffset = 0, Toffset = 0; // Values when tag was displayed
493 static int curX = 0, curY = 0;
494 int nLoff, nToff; // new values, to keep tag in place
495 HPOLYGON hp;
496 bool newPoly;
497 int shift;
498
499 int tagx, tagy; // Tag display co-ordinates
500 SCNHANDLE hTagtext; // Tag text
501
502 // For each polgon with a tag....
503 for (int i = 0; i < MAX_POLY; i++) {
504 hp = GetPolyHandle(i);
505
506 if (TinselV2 && (hp == NOPOLY))
507 continue;
508
509 // Added code for un-tagged tags
510 if ((hp != NOPOLY) && (PolyPointState(hp) == PS_POINTING) && (PolyTagState(hp) != TAG_ON)) {
511 // This poly is entitled to be tagged
512 if (hp != GetTaggedPoly()) {
513 if (*ppText) {
514 MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), *ppText);
515 *ppText = NULL;
516 }
517 *pTag = POLY_HOTSPOT_TAG;
518 SaveTaggedActor(0); // No tagged actor
519 SaveTaggedPoly(hp); // This polygon tagged
520 }
521 return true;
522 } else if ((TinselV2 && PolyTagIsWanted(hp)) ||
523 (!TinselV2 && hp != NOPOLY && PolyTagState(hp) == TAG_ON)) {
524 // Put up or maintain polygon tag
525 newPoly = false;
526 if (TinselV2) {
527 if (hp != GetTaggedPoly())
528 newPoly = true; // Different polygon
529 } else {
530 if (*pTag != POLY_HOTSPOT_TAG)
531 newPoly = true; // A new polygon (no current)
532 else if (hp != GetTaggedPoly())
533 newPoly = true; // Different polygon
534 }
535
536 if (newPoly) {
537 if (*ppText)
538 MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), *ppText);
539
540 if (!TinselV2)
541 *pTag = POLY_HOTSPOT_TAG;
542 SaveTaggedActor(0); // No tagged actor
543 SaveTaggedPoly(hp); // This polygon tagged
544
545 PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
546 GetTagTag(hp, &hTagtext, &tagx, &tagy);
547
548 int strLen;
549 if (GetPolyTagHandle(hp) != 0)
550 strLen = LoadStringRes(GetPolyTagHandle(hp), TextBufferAddr(), TBUFSZ);
551 else
552 strLen = LoadStringRes(hTagtext, TextBufferAddr(), TBUFSZ);
553
554 if (strLen == 0)
555 // No valid string returned, so leave ppText as NULL
556 ppText = NULL;
557 else if (TinselV2 && !PolyTagFollowsCursor(hp)) {
558 // May have buggered cursor
559 EndCursorFollowed();
560
561 *ppText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS),
562 TextBufferAddr(), 0, tagx - Loffset, tagy - Toffset,
563 GetTagFontHandle(), TXT_CENTER, 0);
564 } else if (TinselV2) {
565 // Bugger cursor
566 const char *tagPtr = TextBufferAddr();
567 if (tagPtr[0] < ' ' && tagPtr[1] == EOS_CHAR)
568 StartCursorFollowed();
569
570 GetCursorXYNoWait(&curX, &curY, false);
571 *ppText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), TextBufferAddr(),
572 0, curX, curY, GetTagFontHandle(), TXT_CENTER, 0);
573 } else {
574 // Handle displaying the tag text on-screen
575 *ppText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), TextBufferAddr(),
576 0, tagx - Loffset, tagy - Toffset,
577 GetTagFontHandle(), TXT_CENTER);
578 assert(*ppText); // Polygon tag string produced NULL text
579 }
580
581 // DW1 has some tags without text, e.g. the "equals" button when talking to the guard in act 3
582 if (ppText) {
583 MultiSetZPosition(*ppText, Z_TAG_TEXT);
584
585 /*
586 * New feature: Don't go off the side of the background
587 */
588 shift = MultiRightmost(*ppText) + Loffset + 2;
589 if (shift >= BgWidth()) // Not off right
590 MultiMoveRelXY(*ppText, BgWidth() - shift, 0);
591 shift = MultiLeftmost(*ppText) + Loffset - 1;
592 if (shift <= 0) // Not off left
593 MultiMoveRelXY(*ppText, -shift, 0);
594 shift = MultiLowest(*ppText) + Toffset;
595 if (shift > BgHeight()) // Not off bottom
596 MultiMoveRelXY(*ppText, 0, BgHeight() - shift);
597 }
598 } else if (TinselV2 && (*ppText)) {
599 if (!PolyTagFollowsCursor(hp)) {
600 PlayfieldGetPos(FIELD_WORLD, &nLoff, &nToff);
601 if (nLoff != Loffset || nToff != Toffset) {
602 MultiMoveRelXY(*ppText, Loffset - nLoff, Toffset - nToff);
603 Loffset = nLoff;
604 Toffset = nToff;
605 }
606 } else {
607 GetCursorXY(&tagx, &tagy, false);
608 if (tagx != curX || tagy != curY) {
609 MultiMoveRelXY(*ppText, tagx - curX, tagy - curY);
610 curX = tagx;
611 curY = tagy;
612 }
613 }
614 } else if (!TinselV2) {
615 PlayfieldGetPos(FIELD_WORLD, &nLoff, &nToff);
616 if (nLoff != Loffset || nToff != Toffset) {
617 MultiMoveRelXY(*ppText, Loffset - nLoff, Toffset - nToff);
618 Loffset = nLoff;
619 Toffset = nToff;
620 }
621 }
622 return true;
623 }
624 }
625
626 // No tagged polygon
627 if (TinselV2)
628 SaveTaggedPoly(NOPOLY);
629 else if (*pTag == POLY_HOTSPOT_TAG) {
630 *pTag = NO_HOTSPOT_TAG;
631 SaveTaggedPoly(NOPOLY);
632 }
633 return false;
634 }
635
636 /**
637 * Handle display of tagged actor and polygon tags.
638 * Tagged actor's get priority over polygons.
639 */
TagProcess(CORO_PARAM,const void *)640 void TagProcess(CORO_PARAM, const void *) {
641 // COROUTINE
642 CORO_BEGIN_CONTEXT;
643 OBJECT *pText; // text object pointer
644 HotSpotTag Tag;
645 CORO_END_CONTEXT(_ctx);
646
647 CORO_BEGIN_CODE(_ctx);
648
649 _ctx->pText = NULL;
650 _ctx->Tag = NO_HOTSPOT_TAG;
651
652 SaveTaggedActor(0); // No tagged actor yet
653 SaveTaggedPoly(NOPOLY); // No tagged polygon yet
654
655 while (1) {
656 if (g_bTagsActive) {
657 int curX, curY; // cursor position
658 while (!GetCursorXYNoWait(&curX, &curY, true))
659 CORO_SLEEP(1);
660
661 if (!ActorTag(curX, curY, &_ctx->Tag, &_ctx->pText)
662 && !PolyTag(&_ctx->Tag, &_ctx->pText)) {
663 // Nothing tagged. Remove tag, if there is one
664 if (_ctx->pText) {
665 MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), _ctx->pText);
666 _ctx->pText = NULL;
667
668 if (TinselV2)
669 // May have buggered cursor
670 EndCursorFollowed();
671 }
672 }
673 } else {
674 SaveTaggedActor(0);
675 SaveTaggedPoly(NOPOLY);
676
677 // Remove tag, if there is one
678 if (_ctx->pText) {
679 // kill current text objects
680 MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), _ctx->pText);
681 _ctx->pText = NULL;
682 _ctx->Tag = NO_HOTSPOT_TAG;
683 }
684 }
685
686 CORO_SLEEP(1); // allow re-scheduling
687 }
688
689 CORO_END_CODE;
690 }
691
692 /**
693 * Called from PointProcess() as appropriate.
694 */
enteringpoly(CORO_PARAM,HPOLYGON hp)695 static void enteringpoly(CORO_PARAM, HPOLYGON hp) {
696 CORO_BEGIN_CONTEXT;
697 CORO_END_CONTEXT(_ctx);
698
699 CORO_BEGIN_CODE(_ctx);
700
701 SetPolyPointState(hp, PS_POINTING);
702
703 if (TinselV2)
704 CORO_INVOKE_ARGS(PolygonEvent, (CORO_SUBCTX, hp, POINTED, 0, false, 0));
705 else
706 RunPolyTinselCode(hp, POINTED, PLR_NOEVENT, false);
707
708 CORO_END_CODE;
709 }
710
711 /**
712 * Called from PointProcess() as appropriate.
713 */
leavingpoly(CORO_PARAM,HPOLYGON hp)714 static void leavingpoly(CORO_PARAM, HPOLYGON hp) {
715 CORO_BEGIN_CONTEXT;
716 CORO_END_CONTEXT(_ctx);
717
718 CORO_BEGIN_CODE(_ctx);
719
720 SetPolyPointState(hp, PS_NOT_POINTING);
721
722 if (TinselV2) {
723 CORO_INVOKE_ARGS(PolygonEvent, (CORO_SUBCTX, hp, UNPOINT, 0, false, 0));
724 SetPolyTagWanted(hp, false, false, 0);
725
726 } else if (PolyTagState(hp) == TAG_ON) {
727 // Delete this tag entry
728 SetPolyTagState(hp, TAG_OFF);
729 }
730
731 CORO_END_CODE;
732 }
733
734 /**
735 * For TAG and EXIT polygons, monitor cursor entering and leaving.
736 * Maintain the polygons' pointState and tagState flags accordingly.
737 * Also run the polygon's Glitter code when the cursor enters.
738 */
PointProcess(CORO_PARAM,const void *)739 void PointProcess(CORO_PARAM, const void *) {
740 // COROUTINE
741 CORO_BEGIN_CONTEXT;
742 HPOLYGON hPoly;
743 int i;
744 int curX, curY; // cursor/tagged actor position
745 CORO_END_CONTEXT(_ctx);
746
747 CORO_BEGIN_CODE(_ctx);
748
749 if (TinselV2)
750 EnablePointing();
751
752 while (1) {
753 while (!GetCursorXYNoWait(&_ctx->curX, &_ctx->curY, true))
754 CORO_SLEEP(1);
755
756 /*----------------------------------*\
757 | For polygons of type TAG and EXIT. |
758 \*----------------------------------*/
759 for (_ctx->i = 0; _ctx->i < MAX_POLY; _ctx->i++) {
760 _ctx->hPoly = GetPolyHandle(_ctx->i);
761 if ((_ctx->hPoly == NOPOLY) || ((PolyType(_ctx->hPoly) != TAG) &&
762 (PolyType(_ctx->hPoly) != EXIT)))
763 continue;
764
765 if (!PolyIsPointedTo(_ctx->hPoly)) {
766 if (IsInPolygon(_ctx->curX, _ctx->curY, _ctx->hPoly)) {
767 if (TinselV2) {
768 SetPolyPointedTo(_ctx->hPoly, true);
769 CORO_INVOKE_ARGS(PolygonEvent, (CORO_SUBCTX, _ctx->hPoly, POINTED, 0, false, 0));
770 } else {
771 CORO_INVOKE_1(enteringpoly, _ctx->hPoly);
772 }
773 }
774 } else {
775 if (!IsInPolygon(_ctx->curX, _ctx->curY, _ctx->hPoly)) {
776 if (TinselV2) {
777 SetPolyPointedTo(_ctx->hPoly, false);
778 SetPolyTagWanted(_ctx->hPoly, false, false, 0);
779 CORO_INVOKE_ARGS(PolygonEvent, (CORO_SUBCTX, _ctx->hPoly, UNPOINT, 0, false, 0));
780 } else {
781 CORO_INVOKE_1(leavingpoly, _ctx->hPoly);
782 }
783 }
784 }
785 }
786
787 if (TinselV2) {
788 // For each tagged actor
789 for (_ctx->i = 0; (_ctx->i = NextTaggedActor(_ctx->i)) != 0; ) {
790 if (!ActorIsPointedTo(_ctx->i)) {
791 if (InHotSpot(_ctx->i, _ctx->curX, _ctx->curY)) {
792 SetActorPointedTo(_ctx->i, true);
793 CORO_INVOKE_ARGS(ActorEvent, (CORO_SUBCTX, _ctx->i, POINTED, false, 0));
794 }
795 } else {
796 if (!InHotSpot(_ctx->i, _ctx->curX, _ctx->curY)) {
797 SetActorPointedTo(_ctx->i, false);
798 SetActorTagWanted(_ctx->i, false, false, 0);
799 CORO_INVOKE_ARGS(ActorEvent, (CORO_SUBCTX, _ctx->i, UNPOINT, false, 0));
800 }
801 }
802 }
803
804 // allow re-scheduling
805 do {
806 CORO_SLEEP(1);
807 } while (!g_bPointingActive);
808 } else {
809 // allow re-scheduling
810 CORO_SLEEP(1);
811 }
812 }
813
814 CORO_END_CODE;
815 }
816
DisableTags()817 void DisableTags() {
818 g_bTagsActive = false;
819 }
820
EnableTags()821 void EnableTags() {
822 g_bTagsActive = true;
823 }
824
DisableTagsIfEnabled()825 bool DisableTagsIfEnabled() {
826 if (g_bTagsActive) {
827 DisableTags();
828 return true;
829 } else
830 return false;
831 }
832
833 /**
834 * For testing purposes only.
835 * Causes CursorPositionProcess() to display, or not, the path that the
836 * cursor is in.
837 */
TogglePathDisplay()838 void TogglePathDisplay() {
839 g_DispPath ^= 1; // Toggle path display (XOR with true)
840 }
841
842
setshowstring()843 void setshowstring() {
844 g_bShowString = true;
845 }
846
847 } // End of namespace Tinsel
848