mirror of
https://github.com/libretro/scummvm.git
synced 2025-01-01 06:58:34 +00:00
1dbf8d73d5
Use of global vars is what prevents RTL from working in Tinsel (and probably in other engines). More specifically, the fact that many global vars are not explicitly inited when the engine is (re)launched. svn-id: r54262
849 lines
23 KiB
C++
849 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.
|
|
*
|
|
* $URL$
|
|
* $Id$
|
|
*
|
|
* CursorPositionProcess()
|
|
* TagProcess()
|
|
* PointProcess()
|
|
*/
|
|
|
|
#include "tinsel/actors.h"
|
|
#include "tinsel/background.h"
|
|
#include "tinsel/coroutine.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"
|
|
|
|
namespace Tinsel {
|
|
|
|
//----------------- EXTERNAL GLOBAL DATA --------------------
|
|
|
|
#ifdef DEBUG
|
|
//extern int Overrun; // The overrun counter, in DOS_DW.C
|
|
|
|
extern int 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_CENTRE_X // X-co-ord of overrun counter's display
|
|
#define SPOSX SCRN_CENTRE_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 DispPath = false;
|
|
static bool bShowString = false;
|
|
|
|
static int TaggedActor = 0;
|
|
static HPOLYGON hTaggedPolygon = NOPOLY;
|
|
|
|
static bool bTagsActive = true;
|
|
|
|
static bool bPointingActive = true;
|
|
|
|
static char 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_CENTRE);
|
|
if (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_CENTRE);
|
|
|
|
// 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_CENTRE);
|
|
|
|
// update previous position
|
|
_ctx->prevlX = aniX;
|
|
_ctx->prevlY = aniY;
|
|
}
|
|
}
|
|
|
|
/*-------------*\
|
|
| String number |
|
|
\*-------------*/
|
|
if (bShowString && newestString != _ctx->prevString) {
|
|
// kill current text objects
|
|
if (_ctx->spText) {
|
|
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), _ctx->spText);
|
|
}
|
|
|
|
sprintf(PositionString, "String: %d", newestString);
|
|
_ctx->spText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), PositionString,
|
|
0, SPOSX, POSY+10, GetTalkFontHandle(), TXT_CENTRE);
|
|
|
|
// update previous value
|
|
_ctx->prevString = 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
|
|
|
|
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(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(nullContext, i, UNPOINT, false, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* EnablePointing()
|
|
*/
|
|
void EnablePointing() {
|
|
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) {
|
|
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 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) {
|
|
hTaggedPolygon = hp;
|
|
}
|
|
|
|
HPOLYGON GetTaggedPoly() {
|
|
return 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), tagBuffer, sizeof(tagBuffer));
|
|
|
|
// May have buggered cursor
|
|
EndCursorFollowed();
|
|
*ppText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), tagBuffer,
|
|
0, tagX, tagY, GetTagFontHandle(), TXT_CENTRE, 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_CENTRE);
|
|
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_CENTRE, 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_CENTRE, 0);
|
|
} else {
|
|
// Handle displaying the tag text on-screen
|
|
*ppText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), TextBufferAddr(),
|
|
0, tagx - Loffset, tagy - Toffset,
|
|
GetTagFontHandle(), TXT_CENTRE);
|
|
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 (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 (!bPointingActive);
|
|
} else {
|
|
// allow re-scheduling
|
|
CORO_SLEEP(1);
|
|
}
|
|
}
|
|
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
void DisableTags() {
|
|
bTagsActive = false;
|
|
}
|
|
|
|
void EnableTags() {
|
|
bTagsActive = true;
|
|
}
|
|
|
|
bool DisableTagsIfEnabled() {
|
|
if (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() {
|
|
DispPath ^= 1; // Toggle path display (XOR with true)
|
|
}
|
|
|
|
|
|
void setshowstring() {
|
|
bShowString = true;
|
|
}
|
|
|
|
} // End of namespace Tinsel
|