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

696 lines
17 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.
*
* Cursor and cursor trails.
*/
#include "tinsel/cursor.h"
#include "tinsel/anim.h"
#include "tinsel/background.h"
#include "tinsel/cursor.h"
#include "tinsel/dw.h"
#include "tinsel/events.h" // For EventsManager class
#include "tinsel/film.h"
#include "tinsel/graphics.h"
#include "tinsel/handle.h"
#include "tinsel/dialogs.h"
#include "tinsel/multiobj.h" // multi-part object defintions etc.
#include "tinsel/object.h"
#include "tinsel/pid.h"
#include "tinsel/play.h"
#include "tinsel/sched.h"
#include "tinsel/sysvar.h"
#include "tinsel/text.h"
#include "tinsel/timers.h" // For ONE_SECOND constant
#include "tinsel/tinlib.h" // resetidletime()
#include "tinsel/tinsel.h" // For engine access
namespace Tinsel {
//----------------- LOCAL DEFINES --------------------
#define ITERATION_BASE FRAC_ONE
#define ITER_ACCELERATION (10L << (FRAC_BITS - 4))
//----------------- LOCAL GLOBAL DATA --------------------
// FIXME: Avoid non-const global vars
static OBJECT *g_McurObj = NULL; // Main cursor object
static OBJECT *g_AcurObj = NULL; // Auxiliary cursor object
static ANIM g_McurAnim = {0,0,0,0,0}; // Main cursor animation structure
static ANIM g_AcurAnim = {0,0,0,0,0}; // Auxiliary cursor animation structure
static bool g_bHiddenCursor = false; // Set when cursor is hidden
static bool g_bTempNoTrailers = false; // Set when cursor trails are hidden
static bool g_bTempHide = false; // Set when cursor is hidden
static bool g_bFrozenCursor = false; // Set when cursor position is frozen
static frac_t g_IterationSize = 0;
static SCNHANDLE g_hCursorFilm = 0; // Handle to cursor reel data
static int g_numTrails = 0;
static int g_nextTrail = 0;
static bool g_bWhoa = false; // Set by DropCursor() at the end of a scene
// - causes cursor processes to do nothing
// Reset when main cursor has re-initialized
static uint16 g_restart = 0; // When main cursor has been bWhoa-ed, it waits
// for this to be set to 0x8000.
// Main cursor sets all the bits after a re-start
// - each cursor trail examines it's own bit
// to trigger a trail restart.
static short g_ACoX = 0, g_ACoY = 0; // Auxillary cursor image's animation offsets
#define MAX_TRAILERS 10
static struct {
ANIM trailAnim; // Animation structure
OBJECT *trailObj; // This trailer's object
} g_ntrailData [MAX_TRAILERS];
static int g_lastCursorX = 0, g_lastCursorY = 0;
//----------------- FORWARD REFERENCES --------------------
static void DoCursorMove();
/**
* Initialize and insert a cursor trail object, set its Z-pos, and hide
* it. Also initialize its animation script.
*/
static void InitCurTrailObj(int i, int x, int y) {
const FREEL *pfr; // pointer to reel
IMAGE *pim; // pointer to image
const MULTI_INIT *pmi; // MULTI_INIT structure
const FILM *pfilm;
if (!g_numTrails)
return;
// Get rid of old object
if (g_ntrailData[i].trailObj != NULL)
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), g_ntrailData[i].trailObj);
pim = GetImageFromFilm(g_hCursorFilm, i+1, &pfr, &pmi, &pfilm);// Get pointer to image
assert(BgPal()); // No background palette
pim->hImgPal = TO_32(BgPal());
// Initialize and insert the object, set its Z-pos, and hide it
g_ntrailData[i].trailObj = MultiInitObject(pmi);
MultiInsertObject(GetPlayfieldList(FIELD_STATUS), g_ntrailData[i].trailObj);
MultiSetZPosition(g_ntrailData[i].trailObj, Z_CURSORTRAIL);
MultiSetAniXY(g_ntrailData[i].trailObj, x, y);
// Initialize the animation script
InitStepAnimScript(&g_ntrailData[i].trailAnim, g_ntrailData[i].trailObj, FROM_32(pfr->script), ONE_SECOND / FROM_32(pfilm->frate));
StepAnimScript(&g_ntrailData[i].trailAnim);
}
/**
* Get the cursor position from the mouse driver.
*/
static bool GetDriverPosition(int *x, int *y) {
Common::Point ptMouse = _vm->getMousePosition();
*x = ptMouse.x;
*y = ptMouse.y;
return(*x >= 0 && *x <= SCREEN_WIDTH - 1 &&
*y >= 0 && *y <= SCREEN_HEIGHT - 1);
}
/**
* Move the cursor relative to current position.
*/
void AdjustCursorXY(int deltaX, int deltaY) {
int x, y;
if (deltaX || deltaY) {
if (GetDriverPosition(&x, &y))
_vm->setMousePosition(Common::Point(x + deltaX, y + deltaY));
}
DoCursorMove();
}
/**
* Move the cursor to an absolute position.
*/
void SetCursorXY(int newx, int newy) {
int x, y;
int Loffset, Toffset; // Screen offset
PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
newx -= Loffset;
newy -= Toffset;
if (GetDriverPosition(&x, &y))
_vm->setMousePosition(Common::Point(newx, newy));
DoCursorMove();
}
/**
* Move the cursor to a screen position.
*/
void SetCursorScreenXY(int newx, int newy) {
int x, y;
if (GetDriverPosition(&x, &y))
_vm->setMousePosition(Common::Point(newx, newy));
DoCursorMove();
}
/**
* Called by the world and his brother.
* Returns the cursor's animation position in (x,y).
* Returns false if there is no cursor object.
*/
bool GetCursorXYNoWait(int *x, int *y, bool absolute) {
if (g_McurObj == NULL) {
*x = *y = 0;
return false;
}
GetAniPosition(g_McurObj, x, y);
if (absolute) {
int Loffset, Toffset; // Screen offset
PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
*x += Loffset;
*y += Toffset;
}
return true;
}
/**
* Called by the world and his brother.
* Returns the cursor's animation position.
* If called while there is no cursor object, the calling process ends
* up waiting until there is.
*/
void GetCursorXY(int *x, int *y, bool absolute) {
//while (McurObj == NULL)
// ProcessSleepSelf();
assert(g_McurObj);
GetCursorXYNoWait(x, y, absolute);
}
/**
* Re-initialize the main cursor to use the main cursor reel.
* Called from TINLIB.C to restore cursor after hiding it.
* Called from INVENTRY.C to restore cursor after customising it.
*/
void RestoreMainCursor() {
const FILM *pfilm;
if (g_McurObj != NULL) {
pfilm = (const FILM *)LockMem(g_hCursorFilm);
InitStepAnimScript(&g_McurAnim, g_McurObj, FROM_32(pfilm->reels->script), ONE_SECOND / FROM_32(pfilm->frate));
StepAnimScript(&g_McurAnim);
}
g_bHiddenCursor = false;
g_bFrozenCursor = false;
}
/**
* Called from INVENTRY.C to customise the main cursor.
*/
void SetTempCursor(SCNHANDLE pScript) {
if (g_McurObj != NULL)
InitStepAnimScript(&g_McurAnim, g_McurObj, pScript, 2);
}
/**
* Hide the cursor.
*/
void DwHideCursor() {
int i;
g_bHiddenCursor = true;
if (g_McurObj)
MultiHideObject(g_McurObj);
if (g_AcurObj)
MultiHideObject(g_AcurObj);
for (i = 0; i < g_numTrails; i++) {
if (g_ntrailData[i].trailObj != NULL) {
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), g_ntrailData[i].trailObj);
g_ntrailData[i].trailObj = NULL;
}
}
}
/**
* Unhide the cursor.
*/
void UnHideCursor() {
g_bHiddenCursor = false;
}
/**
* Freeze the cursor.
*/
void FreezeCursor() {
g_bFrozenCursor = true;
}
/**
* Freeze the cursor, or not.
*/
void DoFreezeCursor(bool bFreeze) {
g_bFrozenCursor = bFreeze;
}
/**
* HideCursorTrails
*/
void HideCursorTrails() {
int i;
g_bTempNoTrailers = true;
for (i = 0; i < g_numTrails; i++) {
if (g_ntrailData[i].trailObj != NULL) {
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), g_ntrailData[i].trailObj);
g_ntrailData[i].trailObj = NULL;
}
}
}
/**
* UnHideCursorTrails
*/
void UnHideCursorTrails() {
g_bTempNoTrailers = false;
}
/**
* Get pointer to image from a film reel. And the rest.
*/
IMAGE *GetImageFromReel(const FREEL *pfr, const MULTI_INIT **ppmi) {
const MULTI_INIT *pmi;
const FRAME *pFrame;
pmi = (const MULTI_INIT *)LockMem(FROM_32(pfr->mobj));
if (ppmi)
*ppmi = pmi;
pFrame = (const FRAME *)LockMem(FROM_32(pmi->hMulFrame));
// get pointer to image
return (IMAGE *)LockMem(READ_32(pFrame));
}
/**
* Get pointer to image from a film. And the rest.
*/
IMAGE *GetImageFromFilm(SCNHANDLE hFilm, int reel, const FREEL **ppfr, const MULTI_INIT **ppmi, const FILM **ppfilm) {
const FILM *pfilm;
const FREEL *pfr;
pfilm = (const FILM *)LockMem(hFilm);
if (ppfilm)
*ppfilm = pfilm;
pfr = &pfilm->reels[reel];
if (ppfr)
*ppfr = pfr;
return GetImageFromReel(pfr, ppmi);
}
/**
* Delete auxillary cursor. Restore animation offsets in the image.
*/
void DelAuxCursor() {
if (g_AcurObj != NULL) {
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), g_AcurObj);
g_AcurObj = NULL;
}
}
/**
* Set auxillary cursor.
* Save animation offsets from the image if required.
*/
void SetAuxCursor(SCNHANDLE hFilm) {
IMAGE *pim; // Pointer to auxillary cursor's image
const FREEL *pfr;
const MULTI_INIT *pmi;
const FILM *pfilm;
int x, y; // Cursor position
DelAuxCursor(); // Get rid of previous
GetCursorXY(&x, &y, false); // Note: also waits for cursor to appear
pim = GetImageFromFilm(hFilm, 0, &pfr, &pmi, &pfilm);// Get pointer to image
assert(BgPal()); // no background palette
pim->hImgPal = TO_32(BgPal()); // Poke in the background palette
g_ACoX = (short)(FROM_16(pim->imgWidth)/2 - ((int16) FROM_16(pim->anioffX)));
g_ACoY = (short)((FROM_16(pim->imgHeight) & ~C16_FLAG_MASK)/2 -
((int16) FROM_16(pim->anioffY)));
// Initialize and insert the auxillary cursor object
g_AcurObj = MultiInitObject(pmi);
MultiInsertObject(GetPlayfieldList(FIELD_STATUS), g_AcurObj);
// Initialize the animation and set its position
InitStepAnimScript(&g_AcurAnim, g_AcurObj, FROM_32(pfr->script), ONE_SECOND / FROM_32(pfilm->frate));
MultiSetAniXY(g_AcurObj, x - g_ACoX, y - g_ACoY);
MultiSetZPosition(g_AcurObj, Z_ACURSOR);
if (g_bHiddenCursor)
MultiHideObject(g_AcurObj);
}
/**
* MoveCursor
*/
static void DoCursorMove() {
int startX, startY;
Common::Point ptMouse;
frac_t newX, newY;
unsigned dir;
// get cursors start animation position
GetCursorXYNoWait(&startX, &startY, false);
// get mouse drivers current position
ptMouse = _vm->getMousePosition();
// convert to fixed point
newX = intToFrac(ptMouse.x);
newY = intToFrac(ptMouse.y);
// modify mouse driver position depending on cursor keys
dir = _vm->getKeyDirection();
if (dir != 0) {
if (dir & MSK_LEFT)
newX -= g_IterationSize;
if (dir & MSK_RIGHT)
newX += g_IterationSize;
if (dir & MSK_UP)
newY -= g_IterationSize;
if (dir & MSK_DOWN)
newY += g_IterationSize;
g_IterationSize += ITER_ACCELERATION;
// set new mouse driver position
_vm->setMousePosition(Common::Point(fracToInt(newX), fracToInt(newY)));
} else
g_IterationSize = ITERATION_BASE;
// get new mouse driver position - could have been modified
ptMouse = _vm->getMousePosition();
if (g_lastCursorX != ptMouse.x || g_lastCursorY != ptMouse.y) {
resetUserEventTime();
if (!g_bTempNoTrailers && !g_bHiddenCursor) {
InitCurTrailObj(g_nextTrail++, g_lastCursorX, g_lastCursorY);
if (g_nextTrail == g_numTrails)
g_nextTrail = 0;
}
}
// adjust cursor to new mouse position
if (g_McurObj)
MultiSetAniXY(g_McurObj, ptMouse.x, ptMouse.y);
if (g_AcurObj != NULL)
MultiSetAniXY(g_AcurObj, ptMouse.x - g_ACoX, ptMouse.y - g_ACoY);
if (InventoryActive() && g_McurObj) {
// Notify the inventory
Xmovement(ptMouse.x - startX);
Ymovement(ptMouse.y - startY);
}
g_lastCursorX = ptMouse.x;
g_lastCursorY = ptMouse.y;
}
/**
* Initialize cursor object.
*/
static void InitCurObj() {
const FILM *pFilm;
const FREEL *pfr;
const MULTI_INIT *pmi;
IMAGE *pim;
if (TinselV2) {
pFilm = (const FILM *)LockMem(g_hCursorFilm);
pfr = (const FREEL *)&pFilm->reels[0];
pmi = (MULTI_INIT *)LockMem(FROM_32(pfr->mobj));
PokeInPalette(pmi);
} else {
assert(BgPal()); // no background palette
pim = GetImageFromFilm(g_hCursorFilm, 0, &pfr, &pmi, &pFilm);// Get pointer to image
pim->hImgPal = TO_32(BgPal());
g_AcurObj = NULL; // No auxillary cursor
}
g_McurObj = MultiInitObject(pmi);
MultiInsertObject(GetPlayfieldList(FIELD_STATUS), g_McurObj);
InitStepAnimScript(&g_McurAnim, g_McurObj, FROM_32(pfr->script), ONE_SECOND / FROM_32(pFilm->frate));
}
/**
* Initialize the cursor position.
*/
static void InitCurPos() {
Common::Point ptMouse = _vm->getMousePosition();
g_lastCursorX = ptMouse.x;
g_lastCursorY = ptMouse.y;
MultiSetZPosition(g_McurObj, Z_CURSOR);
DoCursorMove();
MultiHideObject(g_McurObj);
g_IterationSize = ITERATION_BASE;
}
/**
* CursorStoppedCheck
*/
static void CursorStoppedCheck(CORO_PARAM) {
// COROUTINE
CORO_BEGIN_CONTEXT;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
// If scene is closing down
if (g_bWhoa) {
// ...wait for next scene start-up
while (g_restart != 0x8000)
CORO_SLEEP(1);
// Re-initialize
InitCurObj();
InitCurPos();
InventoryIconCursor(false); // May be holding something
// Re-start the cursor trails
g_restart = (uint16)-1; // set all bits
g_bWhoa = false;
}
CORO_END_CODE;
}
/**
* The main cursor process.
*/
void CursorProcess(CORO_PARAM, const void *) {
// COROUTINE
CORO_BEGIN_CONTEXT;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
while (!g_hCursorFilm || !BgPal())
CORO_SLEEP(1);
InitCurObj();
InitCurPos();
InventoryIconCursor(false); // May be holding something
g_bWhoa = false;
g_restart = 0;
while (1) {
// allow rescheduling
CORO_SLEEP(1);
// Stop/start between scenes
CORO_INVOKE_0(CursorStoppedCheck);
// Step the animation script(s)
StepAnimScript(&g_McurAnim);
if (g_AcurObj != NULL)
StepAnimScript(&g_AcurAnim);
for (int i = 0; i < g_numTrails; i++) {
if (g_ntrailData[i].trailObj != NULL) {
if (StepAnimScript(&g_ntrailData[i].trailAnim) == ScriptFinished) {
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), g_ntrailData[i].trailObj);
g_ntrailData[i].trailObj = NULL;
}
}
}
// Move the cursor as appropriate
if (!g_bFrozenCursor)
DoCursorMove();
// If the cursor should be hidden...
if (g_bHiddenCursor || g_bTempHide) {
// ...hide the cursor object(s)
MultiHideObject(g_McurObj);
if (g_AcurObj)
MultiHideObject(g_AcurObj);
for (int i = 0; i < g_numTrails; i++) {
if (g_ntrailData[i].trailObj != NULL)
MultiHideObject(g_ntrailData[i].trailObj);
}
// Wait 'til cursor is again required.
while (g_bHiddenCursor) {
CORO_SLEEP(1);
// Stop/start between scenes
CORO_INVOKE_0(CursorStoppedCheck);
}
}
}
CORO_END_CODE;
}
/**
* Called from dec_cursor() Glitter function.
* Register the handle to cursor reel data.
*/
void DwInitCursor(SCNHANDLE bfilm) {
const FILM *pfilm;
g_hCursorFilm = bfilm;
pfilm = (const FILM *)LockMem(g_hCursorFilm);
g_numTrails = FROM_32(pfilm->numreels) - 1;
assert(g_numTrails <= MAX_TRAILERS);
}
/**
* DropCursor is called when a scene is closing down.
*/
void DropCursor() {
if (TinselV2) {
if (g_AcurObj)
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), g_AcurObj);
if (g_McurObj)
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), g_McurObj);
g_restart = 0;
}
g_AcurObj = NULL; // No auxillary cursor
g_McurObj = NULL; // No cursor object (imminently deleted elsewhere)
g_bHiddenCursor = false; // Not hidden in next scene
g_bTempNoTrailers = false; // Trailers not hidden in next scene
g_bWhoa = true; // Suspend cursor processes
for (int i = 0; i < g_numTrails; i++) {
if (g_ntrailData[i].trailObj != NULL) {
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), g_ntrailData[i].trailObj);
g_ntrailData[i].trailObj = NULL;
}
}
}
/**
* RestartCursor is called when a new scene is starting up.
*/
void RestartCursor() {
g_restart = 0x8000; // Get the main cursor to re-initialize
}
/**
* Called when restarting the game, ensures correct re-start with NULL
* pointers etc.
*/
void RebootCursor() {
g_McurObj = g_AcurObj = NULL;
for (int i = 0; i < MAX_TRAILERS; i++)
g_ntrailData[i].trailObj = NULL;
g_bHiddenCursor = g_bTempNoTrailers = g_bFrozenCursor = false;
g_hCursorFilm = 0;
g_bWhoa = false;
g_restart = 0;
}
void StartCursorFollowed() {
DelAuxCursor();
if (!SysVar(SV_ENABLEPRINTCURSOR))
g_bTempHide = true;
}
void EndCursorFollowed() {
InventoryIconCursor(false); // May be holding something
g_bTempHide = false;
}
bool isCursorShown() {
return !(g_bTempHide || g_bHiddenCursor);
}
} // End of namespace Tinsel