mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-22 01:39:57 +00:00
848 lines
23 KiB
C++
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 "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"
|
|
|
|
#include "common/textconsole.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_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 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_CENTER);
|
|
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_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 (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_CENTER);
|
|
|
|
// 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_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 (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
|