scummvm/engines/tinsel/pdisplay.cpp
2014-02-18 02:39:39 +01:00

848 lines
23 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* CursorPositionProcess()
* TagProcess()
* PointProcess()
*/
#include "common/coroutines.h"
#include "tinsel/actors.h"
#include "tinsel/background.h"
#include "tinsel/cursor.h"
#include "tinsel/dw.h"
#include "tinsel/events.h"
#include "tinsel/font.h"
#include "tinsel/graphics.h"
#include "tinsel/multiobj.h"
#include "tinsel/object.h"
#include "tinsel/pcode.h"
#include "tinsel/polygons.h"
#include "tinsel/rince.h"
#include "tinsel/sched.h"
#include "tinsel/strres.h"
#include "tinsel/text.h"
#include "tinsel/tinsel.h"
#include "common/textconsole.h"
namespace Tinsel {
//----------------- EXTERNAL GLOBAL DATA --------------------
#ifdef DEBUG
//extern int Overrun; // The overrun counter, in DOS_DW.C
extern int g_newestString; // The overrun counter, in STRRES.C
#endif
//----------------- LOCAL DEFINES --------------------
#define LPOSX 295 // X-co-ord of lead actor's position display
#define CPOSX 24 // X-co-ord of cursor's position display
#define OPOSX SCRN_CENTER_X // X-co-ord of overrun counter's display
#define SPOSX SCRN_CENTER_X // X-co-ord of string numbner's display
#define POSY 0 // Y-co-ord of these position displays
enum HotSpotTag {
NO_HOTSPOT_TAG,
POLY_HOTSPOT_TAG,
ACTOR_HOTSPOT_TAG
};
//----------------- LOCAL GLOBAL DATA --------------------
// FIXME: Avoid non-const global vars
static bool g_DispPath = false;
static bool g_bShowString = false;
static int g_TaggedActor = 0;
static HPOLYGON g_hTaggedPolygon = NOPOLY;
static bool g_bTagsActive = true;
static bool g_bPointingActive = true;
static char g_tagBuffer[64];
#ifdef DEBUG
/**
* Displays the cursor and lead actor's co-ordinates and the overrun
* counter. Also which path polygon the cursor is in, if required.
*
* This process is only started up if a Glitter showpos() call is made.
* Obviously, this is for testing purposes only...
*/
void CursorPositionProcess(CORO_PARAM, const void *) {
// COROUTINE
CORO_BEGIN_CONTEXT;
int prevsX, prevsY; // Last screen top left
int prevcX, prevcY; // Last displayed cursor position
int prevlX, prevlY; // Last displayed lead actor position
// int prevOver; // Last displayed overrun
int prevString; // Last displayed string number
OBJECT *cpText; // cursor position text object pointer
OBJECT *cpathText; // cursor path text object pointer
OBJECT *rpText; // text object pointer
// OBJECT *opText; // text object pointer
OBJECT *spText; // string number text object pointer
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
_ctx->prevsX = -1;
_ctx->prevsY = -1;
_ctx->prevcX = -1;
_ctx->prevcY = -1;
_ctx->prevlX = -1;
_ctx->prevlY = -1;
// _ctx->prevOver = -1;
_ctx->prevString = -1;
_ctx->cpText = NULL;
_ctx->cpathText = NULL;
_ctx->rpText = NULL;
// _ctx->opText = NULL;
_ctx->spText = NULL;
int aniX, aniY; // cursor/lead actor position
int Loffset, Toffset; // Screen top left
char PositionString[64]; // sprintf() things into here
PMOVER pActor; // Lead actor
while (1) {
PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
/*-----------------------------------*\
| Cursor's position and path display. |
\*-----------------------------------*/
GetCursorXY(&aniX, &aniY, false);
// Change in cursor position?
if (aniX != _ctx->prevcX || aniY != _ctx->prevcY ||
Loffset != _ctx->prevsX || Toffset != _ctx->prevsY) {
// kill current text objects
if (_ctx->cpText) {
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), _ctx->cpText);
}
if (_ctx->cpathText) {
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), _ctx->cpathText);
_ctx->cpathText = NULL;
}
// New text objects
sprintf(PositionString, "%d %d", aniX + Loffset, aniY + Toffset);
_ctx->cpText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), PositionString,
0, CPOSX, POSY, GetTagFontHandle(), TXT_CENTER);
if (g_DispPath) {
HPOLYGON hp = InPolygon(aniX + Loffset, aniY + Toffset, PATH);
if (hp == NOPOLY)
sprintf(PositionString, "No path");
else
sprintf(PositionString, "%d,%d %d,%d %d,%d %d,%d",
PolyCornerX(hp, 0), PolyCornerY(hp, 0),
PolyCornerX(hp, 1), PolyCornerY(hp, 1),
PolyCornerX(hp, 2), PolyCornerY(hp, 2),
PolyCornerX(hp, 3), PolyCornerY(hp, 3));
_ctx->cpathText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), PositionString,
0, 4, POSY+ 10, GetTagFontHandle(), 0);
}
// update previous position
_ctx->prevcX = aniX;
_ctx->prevcY = aniY;
}
#if 0
/*------------------------*\
| Overrun counter display. |
\*------------------------*/
if (Overrun != _ctx->prevOver) {
// kill current text objects
if (_ctx->opText) {
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), _ctx->opText);
}
sprintf(PositionString, "%d", Overrun);
_ctx->opText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), PositionString,
0, OPOSX, POSY, GetTagFontHandle(), TXT_CENTER);
// update previous value
_ctx->prevOver = Overrun;
}
#endif
/*----------------------*\
| Lead actor's position. |
\*----------------------*/
pActor = GetMover(LEAD_ACTOR);
if (pActor && getMActorState(pActor)) {
// get lead's animation position
GetActorPos(LEAD_ACTOR, &aniX, &aniY);
// Change in position?
if (aniX != _ctx->prevlX || aniY != _ctx->prevlY ||
Loffset != _ctx->prevsX || Toffset != _ctx->prevsY) {
// Kill current text objects
if (_ctx->rpText) {
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), _ctx->rpText);
}
// create new text object list
sprintf(PositionString, "%d %d", aniX, aniY);
_ctx->rpText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), PositionString,
0, LPOSX, POSY, GetTagFontHandle(), TXT_CENTER);
// update previous position
_ctx->prevlX = aniX;
_ctx->prevlY = aniY;
}
}
/*-------------*\
| String number |
\*-------------*/
if (g_bShowString && g_newestString != _ctx->prevString) {
// kill current text objects
if (_ctx->spText) {
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), _ctx->spText);
}
sprintf(PositionString, "String: %d", g_newestString);
_ctx->spText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), PositionString,
0, SPOSX, POSY+10, GetTalkFontHandle(), TXT_CENTER);
// update previous value
_ctx->prevString = g_newestString;
}
// update previous playfield position
_ctx->prevsX = Loffset;
_ctx->prevsY = Toffset;
CORO_SLEEP(1); // allow re-scheduling
}
CORO_END_CODE;
}
#endif
/**
* While inventory/menu is open.
*/
void DisablePointing() {
int i;
HPOLYGON hPoly; // Polygon handle
g_bPointingActive = false;
for (i = 0; i < MAX_POLY; i++) {
hPoly = GetPolyHandle(i);
if (hPoly != NOPOLY && PolyType(hPoly) == TAG && PolyIsPointedTo(hPoly)) {
SetPolyPointedTo(hPoly, false);
SetPolyTagWanted(hPoly, false, false, 0);
PolygonEvent(Common::nullContext, hPoly, UNPOINT, 0, false, 0);
}
}
// For each tagged actor
for (i = 0; (i = NextTaggedActor(i)) != 0; ) {
if (ActorIsPointedTo(i)) {
SetActorPointedTo(i, false);
SetActorTagWanted(i, false, false, 0);
ActorEvent(Common::nullContext, i, UNPOINT, false, 0);
}
}
}
/**
* EnablePointing()
*/
void EnablePointing() {
g_bPointingActive = true;
}
/**
* Tag process keeps us updated as to which tagged actor is currently tagged
* (if one is). Tag process asks us for this information, as does ProcessUserEvent().
*/
static void SaveTaggedActor(int ano) {
g_TaggedActor = ano;
}
/**
* Tag process keeps us updated as to which tagged actor is currently tagged
* (if one is). Tag process asks us for this information, as does ProcessUserEvent().
*/
int GetTaggedActor() {
return g_TaggedActor;
}
/**
* Tag process keeps us updated as to which polygon is currently tagged
* (if one is). Tag process asks us for this information, as does ProcessUserEvent().
*/
static void SaveTaggedPoly(HPOLYGON hp) {
g_hTaggedPolygon = hp;
}
HPOLYGON GetTaggedPoly() {
return g_hTaggedPolygon;
}
/**
* Given cursor position and an actor number, ascertains whether the
* cursor is within the actor's tag area.
* Returns TRUE for a positive result, FALSE for negative.
* If TRUE, the mid-top co-ordinates of the actor's tag area are also
* returned.
*/
static bool InHotSpot(int ano, int aniX, int aniY, int *pxtext, int *pytext) {
int Top, Bot; // Top and bottom limits of active area
int left, right; // left and right of active area
int qrt = 0; // 1/4 of height (sometimes 1/2)
// First check if within x-range
if (aniX > (left = GetActorLeft(ano)) && aniX < (right = GetActorRight(ano))) {
Top = GetActorTop(ano);
Bot = GetActorBottom(ano);
// y-range varies according to tag-type
switch (TagType(ano)) {
case TAG_DEF:
// Next to bottom 1/4 of the actor's area
qrt = (Bot - Top) >> 1; // Half actor's height
Top += qrt; // Top = mid-height
qrt = qrt >> 1; // Quarter height
Bot -= qrt; // Bot = 1/4 way up
break;
case TAG_Q1TO3:
// Top 3/4 of the actor's area
qrt = (Bot - Top) >> 2; // 1/4 actor's height
Bot -= qrt; // Bot = 1/4 way up
break;
case TAG_Q1TO4:
// All the actor's area
break;
default:
error("illegal tag area type");
}
// Now check if within y-range
if (aniY >= Top && aniY <= Bot) {
if (TagType(ano) == TAG_Q1TO3)
*pytext = Top + qrt;
else
*pytext = Top;
*pxtext = (left + right) / 2;
return true;
}
}
return false;
}
/**
* See if the cursor is over a tagged actor's hot-spot. If so, display
* the tag or, if tag already displayed, maintain the tag's position on
* the screen.
*/
static bool ActorTag(int curX, int curY, HotSpotTag *pTag, OBJECT **ppText) {
// FIXME: Avoid non-const global vars
static int tagX = 0, tagY = 0; // Values when tag was displayed
int newX, newY; // new values, to keep tag in place
int ano;
int xtext, ytext;
bool newActor;
if (TinselV2) {
// Tinsel 2 version
// Get the foremost pointed to actor
int actor = FrontTaggedActor();
if (actor == 0) {
SaveTaggedActor(0);
return false;
}
// If new actor
// or actor has suddenly decided it wants tagging...
if (actor != GetTaggedActor() || (ActorTagIsWanted(actor) && !*ppText)) {
// Put up actor tag
SaveTaggedActor(actor); // This actor tagged
SaveTaggedPoly(NOPOLY); // No tagged polygon
if (*ppText)
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), *ppText);
if (ActorTagIsWanted(actor)) {
GetActorTagPos(actor, &tagX, &tagY, false);
LoadStringRes(GetActorTagHandle(actor), g_tagBuffer, sizeof(g_tagBuffer));
// May have buggered cursor
EndCursorFollowed();
*ppText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), g_tagBuffer,
0, tagX, tagY, GetTagFontHandle(), TXT_CENTER, 0);
assert(*ppText);
MultiSetZPosition(*ppText, Z_TAG_TEXT);
} else
*ppText = NULL;
} else if (*ppText) {
// Same actor, maintain tag position
GetActorTagPos(actor, &newX, &newY, false);
if (newX != tagX || newY != tagY) {
MultiMoveRelXY(*ppText, newX - tagX, newY - tagY);
tagX = newX;
tagY = newY;
}
}
return true;
}
// Tinsel 1 version
// For each actor with a tag....
FirstTaggedActor();
while ((ano = NextTaggedActor()) != 0) {
if (InHotSpot(ano, curX, curY, &xtext, &ytext)) {
// Put up or maintain actor tag
if (*pTag != ACTOR_HOTSPOT_TAG)
newActor = true;
else if (ano != GetTaggedActor())
newActor = true; // Different actor
else
newActor = false; // Same actor
if (newActor) {
// Display actor's tag
if (*ppText)
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), *ppText);
*pTag = ACTOR_HOTSPOT_TAG;
SaveTaggedActor(ano); // This actor tagged
SaveTaggedPoly(NOPOLY); // No tagged polygon
PlayfieldGetPos(FIELD_WORLD, &tagX, &tagY);
LoadStringRes(GetActorTag(ano), TextBufferAddr(), TBUFSZ);
*ppText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), TextBufferAddr(),
0, xtext - tagX, ytext - tagY, GetTagFontHandle(), TXT_CENTER);
assert(*ppText); // Actor tag string produced NULL text
MultiSetZPosition(*ppText, Z_TAG_TEXT);
} else {
// Maintain actor tag's position
PlayfieldGetPos(FIELD_WORLD, &newX, &newY);
if (newX != tagX || newY != tagY) {
MultiMoveRelXY(*ppText, tagX - newX, tagY - newY);
tagX = newX;
tagY = newY;
}
}
return true;
}
}
// No tagged actor
if (*pTag == ACTOR_HOTSPOT_TAG) {
*pTag = NO_HOTSPOT_TAG;
SaveTaggedActor(0);
}
return false;
}
/**
* Perhaps some comment in due course.
*
* Under control of PointProcess(), when the cursor is over a TAG or
* EXIT polygon, its pointState flag is set to POINTING. If its Glitter
* code contains a printtag() call, its tagState flag gets set to TAG_ON.
*/
static bool PolyTag(HotSpotTag *pTag, OBJECT **ppText) {
// FIXME: Avoid non-const global vars
static int Loffset = 0, Toffset = 0; // Values when tag was displayed
static int curX = 0, curY = 0;
int nLoff, nToff; // new values, to keep tag in place
HPOLYGON hp;
bool newPoly;
int shift;
int tagx, tagy; // Tag display co-ordinates
SCNHANDLE hTagtext; // Tag text
// For each polgon with a tag....
for (int i = 0; i < MAX_POLY; i++) {
hp = GetPolyHandle(i);
if (TinselV2 && (hp == NOPOLY))
continue;
// Added code for un-tagged tags
if ((hp != NOPOLY) && (PolyPointState(hp) == PS_POINTING) && (PolyTagState(hp) != TAG_ON)) {
// This poly is entitled to be tagged
if (hp != GetTaggedPoly()) {
if (*ppText) {
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), *ppText);
*ppText = NULL;
}
*pTag = POLY_HOTSPOT_TAG;
SaveTaggedActor(0); // No tagged actor
SaveTaggedPoly(hp); // This polygon tagged
}
return true;
} else if ((TinselV2 && PolyTagIsWanted(hp)) ||
(!TinselV2 && hp != NOPOLY && PolyTagState(hp) == TAG_ON)) {
// Put up or maintain polygon tag
newPoly = false;
if (TinselV2) {
if (hp != GetTaggedPoly())
newPoly = true; // Different polygon
} else {
if (*pTag != POLY_HOTSPOT_TAG)
newPoly = true; // A new polygon (no current)
else if (hp != GetTaggedPoly())
newPoly = true; // Different polygon
}
if (newPoly) {
if (*ppText)
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), *ppText);
if (!TinselV2)
*pTag = POLY_HOTSPOT_TAG;
SaveTaggedActor(0); // No tagged actor
SaveTaggedPoly(hp); // This polygon tagged
PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
GetTagTag(hp, &hTagtext, &tagx, &tagy);
int strLen;
if (GetPolyTagHandle(hp) != 0)
strLen = LoadStringRes(GetPolyTagHandle(hp), TextBufferAddr(), TBUFSZ);
else
strLen = LoadStringRes(hTagtext, TextBufferAddr(), TBUFSZ);
if (strLen == 0)
// No valid string returned, so leave ppText as NULL
ppText = NULL;
else if (TinselV2 && !PolyTagFollowsCursor(hp)) {
// May have buggered cursor
EndCursorFollowed();
*ppText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS),
TextBufferAddr(), 0, tagx - Loffset, tagy - Toffset,
GetTagFontHandle(), TXT_CENTER, 0);
} else if (TinselV2) {
// Bugger cursor
const char *tagPtr = TextBufferAddr();
if (tagPtr[0] < ' ' && tagPtr[1] == EOS_CHAR)
StartCursorFollowed();
GetCursorXYNoWait(&curX, &curY, false);
*ppText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), TextBufferAddr(),
0, curX, curY, GetTagFontHandle(), TXT_CENTER, 0);
} else {
// Handle displaying the tag text on-screen
*ppText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), TextBufferAddr(),
0, tagx - Loffset, tagy - Toffset,
GetTagFontHandle(), TXT_CENTER);
assert(*ppText); // Polygon tag string produced NULL text
}
// DW1 has some tags without text, e.g. the "equals" button when talking to the guard in act 3
if (ppText) {
MultiSetZPosition(*ppText, Z_TAG_TEXT);
/*
* New feature: Don't go off the side of the background
*/
shift = MultiRightmost(*ppText) + Loffset + 2;
if (shift >= BgWidth()) // Not off right
MultiMoveRelXY(*ppText, BgWidth() - shift, 0);
shift = MultiLeftmost(*ppText) + Loffset - 1;
if (shift <= 0) // Not off left
MultiMoveRelXY(*ppText, -shift, 0);
shift = MultiLowest(*ppText) + Toffset;
if (shift > BgHeight()) // Not off bottom
MultiMoveRelXY(*ppText, 0, BgHeight() - shift);
}
} else if (TinselV2 && (*ppText)) {
if (!PolyTagFollowsCursor(hp)) {
PlayfieldGetPos(FIELD_WORLD, &nLoff, &nToff);
if (nLoff != Loffset || nToff != Toffset) {
MultiMoveRelXY(*ppText, Loffset - nLoff, Toffset - nToff);
Loffset = nLoff;
Toffset = nToff;
}
} else {
GetCursorXY(&tagx, &tagy, false);
if (tagx != curX || tagy != curY) {
MultiMoveRelXY(*ppText, tagx - curX, tagy - curY);
curX = tagx;
curY = tagy;
}
}
} else if (!TinselV2) {
PlayfieldGetPos(FIELD_WORLD, &nLoff, &nToff);
if (nLoff != Loffset || nToff != Toffset) {
MultiMoveRelXY(*ppText, Loffset - nLoff, Toffset - nToff);
Loffset = nLoff;
Toffset = nToff;
}
}
return true;
}
}
// No tagged polygon
if (TinselV2)
SaveTaggedPoly(NOPOLY);
else if (*pTag == POLY_HOTSPOT_TAG) {
*pTag = NO_HOTSPOT_TAG;
SaveTaggedPoly(NOPOLY);
}
return false;
}
/**
* Handle display of tagged actor and polygon tags.
* Tagged actor's get priority over polygons.
*/
void TagProcess(CORO_PARAM, const void *) {
// COROUTINE
CORO_BEGIN_CONTEXT;
OBJECT *pText; // text object pointer
HotSpotTag Tag;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
_ctx->pText = NULL;
_ctx->Tag = NO_HOTSPOT_TAG;
SaveTaggedActor(0); // No tagged actor yet
SaveTaggedPoly(NOPOLY); // No tagged polygon yet
while (1) {
if (g_bTagsActive) {
int curX, curY; // cursor position
while (!GetCursorXYNoWait(&curX, &curY, true))
CORO_SLEEP(1);
if (!ActorTag(curX, curY, &_ctx->Tag, &_ctx->pText)
&& !PolyTag(&_ctx->Tag, &_ctx->pText)) {
// Nothing tagged. Remove tag, if there is one
if (_ctx->pText) {
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), _ctx->pText);
_ctx->pText = NULL;
if (TinselV2)
// May have buggered cursor
EndCursorFollowed();
}
}
} else {
SaveTaggedActor(0);
SaveTaggedPoly(NOPOLY);
// Remove tag, if there is one
if (_ctx->pText) {
// kill current text objects
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), _ctx->pText);
_ctx->pText = NULL;
_ctx->Tag = NO_HOTSPOT_TAG;
}
}
CORO_SLEEP(1); // allow re-scheduling
}
CORO_END_CODE;
}
/**
* Called from PointProcess() as appropriate.
*/
static void enteringpoly(CORO_PARAM, HPOLYGON hp) {
CORO_BEGIN_CONTEXT;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
SetPolyPointState(hp, PS_POINTING);
if (TinselV2)
CORO_INVOKE_ARGS(PolygonEvent, (CORO_SUBCTX, hp, POINTED, 0, false, 0));
else
RunPolyTinselCode(hp, POINTED, PLR_NOEVENT, false);
CORO_END_CODE;
}
/**
* Called from PointProcess() as appropriate.
*/
static void leavingpoly(CORO_PARAM, HPOLYGON hp) {
CORO_BEGIN_CONTEXT;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
SetPolyPointState(hp, PS_NOT_POINTING);
if (TinselV2) {
CORO_INVOKE_ARGS(PolygonEvent, (CORO_SUBCTX, hp, UNPOINT, 0, false, 0));
SetPolyTagWanted(hp, false, false, 0);
} else if (PolyTagState(hp) == TAG_ON) {
// Delete this tag entry
SetPolyTagState(hp, TAG_OFF);
}
CORO_END_CODE;
}
/**
* For TAG and EXIT polygons, monitor cursor entering and leaving.
* Maintain the polygons' pointState and tagState flags accordingly.
* Also run the polygon's Glitter code when the cursor enters.
*/
void PointProcess(CORO_PARAM, const void *) {
// COROUTINE
CORO_BEGIN_CONTEXT;
HPOLYGON hPoly;
int i;
int curX, curY; // cursor/tagged actor position
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
if (TinselV2)
EnablePointing();
while (1) {
while (!GetCursorXYNoWait(&_ctx->curX, &_ctx->curY, true))
CORO_SLEEP(1);
/*----------------------------------*\
| For polygons of type TAG and EXIT. |
\*----------------------------------*/
for (_ctx->i = 0; _ctx->i < MAX_POLY; _ctx->i++) {
_ctx->hPoly = GetPolyHandle(_ctx->i);
if ((_ctx->hPoly == NOPOLY) || ((PolyType(_ctx->hPoly) != TAG) &&
(PolyType(_ctx->hPoly) != EXIT)))
continue;
if (!PolyIsPointedTo(_ctx->hPoly)) {
if (IsInPolygon(_ctx->curX, _ctx->curY, _ctx->hPoly)) {
if (TinselV2) {
SetPolyPointedTo(_ctx->hPoly, true);
CORO_INVOKE_ARGS(PolygonEvent, (CORO_SUBCTX, _ctx->hPoly, POINTED, 0, false, 0));
} else {
CORO_INVOKE_1(enteringpoly, _ctx->hPoly);
}
}
} else {
if (!IsInPolygon(_ctx->curX, _ctx->curY, _ctx->hPoly)) {
if (TinselV2) {
SetPolyPointedTo(_ctx->hPoly, false);
SetPolyTagWanted(_ctx->hPoly, false, false, 0);
CORO_INVOKE_ARGS(PolygonEvent, (CORO_SUBCTX, _ctx->hPoly, UNPOINT, 0, false, 0));
} else {
CORO_INVOKE_1(leavingpoly, _ctx->hPoly);
}
}
}
}
if (TinselV2) {
// For each tagged actor
for (_ctx->i = 0; (_ctx->i = NextTaggedActor(_ctx->i)) != 0; ) {
if (!ActorIsPointedTo(_ctx->i)) {
if (InHotSpot(_ctx->i, _ctx->curX, _ctx->curY)) {
SetActorPointedTo(_ctx->i, true);
CORO_INVOKE_ARGS(ActorEvent, (CORO_SUBCTX, _ctx->i, POINTED, false, 0));
}
} else {
if (!InHotSpot(_ctx->i, _ctx->curX, _ctx->curY)) {
SetActorPointedTo(_ctx->i, false);
SetActorTagWanted(_ctx->i, false, false, 0);
CORO_INVOKE_ARGS(ActorEvent, (CORO_SUBCTX, _ctx->i, UNPOINT, false, 0));
}
}
}
// allow re-scheduling
do {
CORO_SLEEP(1);
} while (!g_bPointingActive);
} else {
// allow re-scheduling
CORO_SLEEP(1);
}
}
CORO_END_CODE;
}
void DisableTags() {
g_bTagsActive = false;
}
void EnableTags() {
g_bTagsActive = true;
}
bool DisableTagsIfEnabled() {
if (g_bTagsActive) {
DisableTags();
return true;
} else
return false;
}
/**
* For testing purposes only.
* Causes CursorPositionProcess() to display, or not, the path that the
* cursor is in.
*/
void TogglePathDisplay() {
g_DispPath ^= 1; // Toggle path display (XOR with true)
}
void setshowstring() {
g_bShowString = true;
}
} // End of namespace Tinsel