488 lines
13 KiB
C++
488 lines
13 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 3 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, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
/**********************************************
|
|
*
|
|
* USED IN:
|
|
* Star Trek TNG Interactive Technial Manual
|
|
*
|
|
**********************************************/
|
|
|
|
/*
|
|
* -- QTVRW External Factory. 14Oct94 GRB
|
|
* QTVRW
|
|
* X mDispose
|
|
* S mGetHPanAngle
|
|
* S mGetMovieRect
|
|
* I mGetNodeID
|
|
* I mGetQuality
|
|
* S mGetVPanAngle
|
|
* S mGetZoomAngle
|
|
* S mMouseOver
|
|
* S mName
|
|
* IS mNew, type
|
|
* ISII mOpenMovie filename, x, y
|
|
* II mSetActive mode
|
|
* XS mSetHPanAngle angle
|
|
* II mSetNodeID node
|
|
* II mSetQuality qual
|
|
* XOS mSetRolloverCallback factory, method
|
|
* IS mSetTransitionMode mode
|
|
* II mSetTransitionSpeed speed
|
|
* XS mSetVPanAngle angle
|
|
* XS mSetZoomAngle angle
|
|
* I mUpdate
|
|
*/
|
|
|
|
#include "director/director.h"
|
|
#include "director/lingo/lingo.h"
|
|
#include "director/lingo/lingo-builtins.h"
|
|
#include "director/lingo/lingo-code.h"
|
|
#include "director/lingo/lingo-object.h"
|
|
#include "director/lingo/lingo-utils.h"
|
|
#include "director/lingo/xlibs/q/qtvr.h"
|
|
#include "director/window.h"
|
|
|
|
namespace Director {
|
|
|
|
const char *const QTVR::xlibName = "QTVRW";
|
|
const XlibFileDesc QTVR::fileNames[] = {
|
|
{ "QTVR", nullptr },
|
|
{ "QTVR.QTC", nullptr },
|
|
{ nullptr, nullptr },
|
|
};
|
|
|
|
static const MethodProto xlibMethods[] = {
|
|
{ "new", QTVR::m_new, 1, 1, 400 }, // D4
|
|
{ "dispose", QTVR::m_dispose, 1, 1, 400 }, // D4
|
|
{ "getHPanAngle", QTVR::m_getHPanAngle, 0, 0, 400 }, // D4
|
|
{ "getMovieRect", QTVR::m_getMovieRect, 0, 0, 400 }, // D4
|
|
{ "getNodeID", QTVR::m_getNodeID, 0, 0, 400 }, // D4
|
|
{ "getQuality", QTVR::m_getQuality, 0, 0, 400 }, // D4
|
|
{ "getVPanAngle", QTVR::m_getVPanAngle, 0, 0, 400 }, // D4
|
|
{ "getZoomAngle", QTVR::m_getZoomAngle, 0, 0, 400 }, // D4
|
|
{ "mouseOver", QTVR::m_mouseOver, 0, 0, 400 }, // D4
|
|
{ "name", QTVR::m_name, 0, 0, 400 }, // D4
|
|
{ "openMovie", QTVR::m_openMovie, 3, 3, 400 }, // D4
|
|
{ "setActive", QTVR::m_setActive, 1, 1, 400 }, // D4
|
|
{ "setHPanAngle", QTVR::m_setHPanAngle, 1, 1, 400 }, // D4
|
|
{ "setNodeID", QTVR::m_setNodeID, 1, 1, 400 }, // D4
|
|
{ "setQuality", QTVR::m_setQuality, 1, 1, 400 }, // D4
|
|
{ "setRolloverCallback", QTVR::m_setRolloverCallback, 2, 2, 400 }, // D4
|
|
{ "setTransitionMode", QTVR::m_setTransitionMode, 1, 1, 400 }, // D4
|
|
{ "setTransitionSpeed", QTVR::m_setTransitionSpeed, 1, 1, 400 }, // D4
|
|
{ "setVPanAngle", QTVR::m_setVPanAngle, 1, 1, 400 }, // D4
|
|
{ "setZoomAngle", QTVR::m_setZoomAngle, 1, 1, 400 }, // D4
|
|
{ "update", QTVR::m_update, 0, 0, 400 }, // D4
|
|
{ nullptr, nullptr, 0, 0, 0 }
|
|
};
|
|
|
|
QTVRXObject::QTVRXObject(ObjectType ObjectType) :Object<QTVRXObject>("QTVR") {
|
|
_objType = ObjectType;
|
|
}
|
|
|
|
QTVRXObject::~QTVRXObject() {
|
|
close();
|
|
}
|
|
|
|
void QTVRXObject::close() {
|
|
if (_video) {
|
|
_video->close();
|
|
delete _video;
|
|
_video = nullptr;
|
|
}
|
|
if (_widget) {
|
|
delete _widget;
|
|
_widget = nullptr;
|
|
}
|
|
}
|
|
|
|
void QTVR::open(ObjectType type, const Common::Path &path) {
|
|
if (type == kXObj) {
|
|
QTVRXObject::initMethods(xlibMethods);
|
|
QTVRXObject *xobj = new QTVRXObject(kXObj);
|
|
g_lingo->exposeXObject(xlibName, xobj);
|
|
}
|
|
}
|
|
|
|
void QTVR::close(ObjectType type) {
|
|
if (type == kXObj) {
|
|
QTVRXObject::cleanupMethods();
|
|
g_lingo->_globalvars[xlibName] = Datum();
|
|
}
|
|
}
|
|
|
|
|
|
void QTVR::m_new(int nargs) {
|
|
g_lingo->printSTUBWithArglist("QTVR::m_new", nargs);
|
|
g_lingo->dropStack(nargs);
|
|
g_lingo->push(g_lingo->_state->me);
|
|
}
|
|
|
|
void QTVR::m_dispose(int nargs) {
|
|
ARGNUMCHECK(0);
|
|
|
|
QTVRXObject *me = (QTVRXObject *)g_lingo->_state->me.u.obj;
|
|
me->close();
|
|
}
|
|
|
|
void QTVR::m_getHPanAngle(int nargs) {
|
|
ARGNUMCHECK(0);
|
|
|
|
QTVRXObject *me = (QTVRXObject *)g_lingo->_state->me.u.obj;
|
|
|
|
g_lingo->push(me->_video->getPanAngle());
|
|
}
|
|
|
|
void QTVR::m_getMovieRect(int nargs) {
|
|
ARGNUMCHECK(0);
|
|
|
|
QTVRXObject *me = (QTVRXObject *)g_lingo->_state->me.u.obj;
|
|
|
|
g_lingo->push(Common::String::format("%d,%d,%d,%d", me->_rect.left, me->_rect.top, me->_rect.right, me->_rect.bottom));
|
|
}
|
|
|
|
void QTVR::m_getNodeID(int nargs) {
|
|
ARGNUMCHECK(0);
|
|
|
|
QTVRXObject *me = (QTVRXObject *)g_lingo->_state->me.u.obj;
|
|
|
|
g_lingo->push((int)me->_video->getCurrentNodeID());
|
|
}
|
|
|
|
void QTVR::m_getQuality(int nargs) {
|
|
ARGNUMCHECK(0);
|
|
|
|
QTVRXObject *me = (QTVRXObject *)g_lingo->_state->me.u.obj;
|
|
|
|
g_lingo->push(me->_video->getQuality());
|
|
}
|
|
|
|
void QTVR::m_getVPanAngle(int nargs) {
|
|
ARGNUMCHECK(0);
|
|
|
|
QTVRXObject *me = (QTVRXObject *)g_lingo->_state->me.u.obj;
|
|
|
|
g_lingo->push(me->_video->getTiltAngle());
|
|
}
|
|
|
|
void QTVR::m_getZoomAngle(int nargs) {
|
|
ARGNUMCHECK(0);
|
|
|
|
QTVRXObject *me = (QTVRXObject *)g_lingo->_state->me.u.obj;
|
|
|
|
g_lingo->push(me->_video->getFOV());
|
|
}
|
|
|
|
void QTVR::m_mouseOver(int nargs) {
|
|
ARGNUMCHECK(0);
|
|
|
|
QTVRXObject *me = (QTVRXObject *)g_lingo->_state->me.u.obj;
|
|
Common::Point pos = g_director->getCurrentWindow()->getMousePos();
|
|
|
|
if (!me->_active || !me->_rect.contains(pos)) {
|
|
g_lingo->pushVoid();
|
|
return;
|
|
}
|
|
|
|
// Execute handler on first call to MouseOver
|
|
int hotspot;
|
|
|
|
int node;
|
|
bool nodeChanged = false;
|
|
|
|
while (true) {
|
|
Graphics::Surface const *frame = me->_video->decodeNextFrame();
|
|
|
|
if (!frame) {
|
|
g_lingo->pushVoid();
|
|
return;
|
|
}
|
|
|
|
Graphics::Surface *dither = frame->convertTo(g_director->_wm->_pixelformat, me->_video->getPalette(), 256, g_director->getPalette(), 256, Graphics::kDitherNaive);
|
|
|
|
g_director->getCurrentWindow()->getSurface()->copyRectToSurface(
|
|
dither->getPixels(), dither->pitch, me->_rect.left, me->_rect.top, dither->w, dither->h
|
|
);
|
|
|
|
dither->free();
|
|
delete dither;
|
|
|
|
g_director->getCurrentWindow()->setDirty(true);
|
|
|
|
Common::Event event;
|
|
|
|
while (g_system->getEventManager()->pollEvent(event)) {
|
|
if (Common::isMouseEvent(event)) {
|
|
pos = g_director->getCurrentWindow()->getMousePos();
|
|
|
|
if (!me->_rect.contains(pos))
|
|
break;
|
|
}
|
|
|
|
node = me->_video->getCurrentNodeID();
|
|
|
|
if (event.type == Common::EVENT_LBUTTONUP) {
|
|
me->_widget->processEvent(event);
|
|
|
|
if (me->_video->getCurrentNodeID() != node)
|
|
nodeChanged = true;
|
|
|
|
hotspot = me->_video->getClickedHotspotID();
|
|
|
|
if (!hotspot) {
|
|
if (nodeChanged)
|
|
g_lingo->push(Common::String::format("jump,%d", node));
|
|
else
|
|
g_lingo->push(Common::String("pan ,0"));
|
|
|
|
return;
|
|
}
|
|
|
|
const Common::QuickTimeParser::PanoHotSpot *hotspotData = me->_video->getHotSpotByID(hotspot);
|
|
|
|
if (nodeChanged || (hotspotData && hotspotData->type == MKTAG('l','i','n','k')))
|
|
g_lingo->push(Common::String::format("jump,%d", node));
|
|
else
|
|
g_lingo->push(Common::String::format("%s,%d", hotspotData ? tag2str((uint32)hotspotData->type) : "undf", hotspot));
|
|
|
|
return;
|
|
}
|
|
|
|
hotspot = me->_video->getRolloverHotspotID();
|
|
|
|
me->_widget->processEvent(event);
|
|
|
|
if (hotspot != me->_video->getRolloverHotspotID() && !me->_rolloverCallbackMethod.empty()) {
|
|
g_lingo->push(me->_rolloverCallbackObject);
|
|
g_lingo->push(me->_video->getRolloverHotspotID());
|
|
|
|
int cframe = g_lingo->_state->callstack.size();
|
|
LC::call(me->_rolloverCallbackObject.u.obj->getMethod(me->_rolloverCallbackMethod), 2, false);
|
|
g_lingo->execute(cframe);
|
|
}
|
|
}
|
|
|
|
LB::b_updateStage(0);
|
|
|
|
if (!me->_rect.contains(pos))
|
|
break;
|
|
|
|
if (event.type == Common::EVENT_QUIT) {
|
|
g_director->processEventQUIT();
|
|
break;
|
|
}
|
|
|
|
g_director->delayMillis(10);
|
|
}
|
|
|
|
g_lingo->_theResult = 0;
|
|
}
|
|
|
|
void QTVR::m_name(int nargs) {
|
|
// TODO Clarify that it is indeed hotspot name
|
|
g_lingo->printArgs("QTVR::m_name", nargs);
|
|
|
|
ARGNUMCHECK(0);
|
|
|
|
QTVRXObject *me = (QTVRXObject *)g_lingo->_state->me.u.obj;
|
|
|
|
const Common::QuickTimeParser::PanoHotSpot *hotspot = me->_video->getClickedHotspot();
|
|
|
|
if (!hotspot) {
|
|
g_lingo->push(Common::String(""));
|
|
return;
|
|
}
|
|
|
|
g_lingo->push(me->_video->getHotSpotName(hotspot->id));
|
|
}
|
|
|
|
void QTVR::m_openMovie(int nargs) {
|
|
g_lingo->printArgs("QTVR::m_QTVROpen", nargs);
|
|
ARGNUMCHECK(3);
|
|
|
|
int top = g_lingo->pop().asInt();
|
|
int left = g_lingo->pop().asInt();
|
|
Common::String pathStr = g_lingo->pop().asString();
|
|
|
|
QTVRXObject *me = (QTVRXObject *)g_lingo->_state->me.u.obj;
|
|
|
|
Common::Path path = findMoviePath(pathStr);
|
|
if (path.empty()) {
|
|
Common::String error = Common::String::format("Error: Movie file ('%s') not found!", pathStr.c_str());
|
|
g_lingo->push(error);
|
|
return;
|
|
}
|
|
|
|
me->_video = new Video::QuickTimeDecoder();
|
|
debugC(5, kDebugXObj, "QtvrxtraXtra::m_QTVROpen(): Loading QT file ('%s')", path.toString().c_str());
|
|
if (!me->_video->loadFile(path)) {
|
|
Common::String error = Common::String::format("Error: Failed to load movie file ('%s')!", path.toString().c_str());
|
|
g_lingo->push(error);
|
|
return;
|
|
}
|
|
|
|
me->_video->setTargetSize(320, 200);
|
|
|
|
me->_rect = Common::Rect(left, top, left + me->_video->getWidth(), top + me->_video->getHeight());
|
|
|
|
|
|
me->_widget = new QtvrWidget(me, g_director->getCurrentWindow()->getMacWindow(),
|
|
me->_rect.left, me->_rect.top, me->_rect.width(), me->_rect.height(),
|
|
g_director->getMacWindowManager());
|
|
|
|
g_lingo->push(path.toString());
|
|
}
|
|
|
|
void QTVR::m_setActive(int nargs) {
|
|
ARGNUMCHECK(1);
|
|
|
|
QTVRXObject *me = (QTVRXObject *)g_lingo->_state->me.u.obj;
|
|
|
|
me->_active = (g_lingo->pop().asInt() != 0);
|
|
}
|
|
|
|
void QTVR::m_setHPanAngle(int nargs) {
|
|
ARGNUMCHECK(1);
|
|
|
|
QTVRXObject *me = (QTVRXObject *)g_lingo->_state->me.u.obj;
|
|
|
|
me->_video->setPanAngle(atof(g_lingo->pop().asString().c_str()));
|
|
}
|
|
|
|
void QTVR::m_setNodeID(int nargs) {
|
|
ARGNUMCHECK(1);
|
|
|
|
QTVRXObject *me = (QTVRXObject *)g_lingo->_state->me.u.obj;
|
|
|
|
me->_video->goToNode(g_lingo->pop().asInt());
|
|
}
|
|
|
|
void QTVR::m_setQuality(int nargs) {
|
|
ARGNUMCHECK(1);
|
|
|
|
QTVRXObject *me = (QTVRXObject *)g_lingo->_state->me.u.obj;
|
|
|
|
me->_video->setQuality(g_lingo->pop().asInt());
|
|
}
|
|
|
|
void QTVR::m_setRolloverCallback(int nargs) {
|
|
ARGNUMCHECK(2);
|
|
|
|
QTVRXObject *me = (QTVRXObject *)g_lingo->_state->me.u.obj;
|
|
|
|
me->_rolloverCallbackMethod = g_lingo->pop().asString();
|
|
me->_rolloverCallbackObject = g_lingo->pop();
|
|
|
|
if (me->_rolloverCallbackObject.type != OBJECT || !(me->_rolloverCallbackObject.u.obj->getObjType() & kFactoryObj))
|
|
error("QTVR::m_setRolloverCallback(): first argument is not a Factory but %s", me->_rolloverCallbackObject.type2str());
|
|
}
|
|
|
|
void QTVR::m_setTransitionMode(int nargs) {
|
|
ARGNUMCHECK(1);
|
|
|
|
QTVRXObject *me = (QTVRXObject *)g_lingo->_state->me.u.obj;
|
|
|
|
me->_video->setTransitionMode(g_lingo->pop().asString());
|
|
}
|
|
|
|
void QTVR::m_setTransitionSpeed(int nargs) {
|
|
ARGNUMCHECK(1);
|
|
|
|
QTVRXObject *me = (QTVRXObject *)g_lingo->_state->me.u.obj;
|
|
|
|
me->_video->setTransitionSpeed(g_lingo->pop().asFloat());
|
|
}
|
|
|
|
void QTVR::m_setVPanAngle(int nargs) {
|
|
ARGNUMCHECK(1);
|
|
|
|
QTVRXObject *me = (QTVRXObject *)g_lingo->_state->me.u.obj;
|
|
|
|
me->_video->setTiltAngle(atof(g_lingo->pop().asString().c_str()));
|
|
}
|
|
|
|
void QTVR::m_setZoomAngle(int nargs) {
|
|
ARGNUMCHECK(1);
|
|
|
|
QTVRXObject *me = (QTVRXObject *)g_lingo->_state->me.u.obj;
|
|
|
|
me->_video->setFOV(atof(g_lingo->pop().asString().c_str()));
|
|
}
|
|
|
|
void QTVR::m_update(int nargs) {
|
|
ARGNUMCHECK(0);
|
|
|
|
QTVRXObject *me = (QTVRXObject *)g_lingo->_state->me.u.obj;
|
|
|
|
me->_active = true;
|
|
|
|
Graphics::Surface const *frame = me->_video->decodeNextFrame();
|
|
|
|
if (!frame)
|
|
return;
|
|
|
|
Graphics::Surface *dither = frame->convertTo(g_director->_wm->_pixelformat, me->_video->getPalette(), 256, g_director->getPalette(), 256, Graphics::kDitherNaive);
|
|
|
|
g_director->getCurrentWindow()->getSurface()->copyRectToSurface(
|
|
dither->getPixels(), dither->pitch, me->_rect.left, me->_rect.top, dither->w, dither->h
|
|
);
|
|
|
|
dither->free();
|
|
delete dither;
|
|
}
|
|
|
|
|
|
///////////////
|
|
// Widget
|
|
///////////////
|
|
|
|
QtvrWidget::QtvrWidget(QTVRXObject *xtra, Graphics::MacWidget *parent, int x, int y, int w, int h, Graphics::MacWindowManager *wm) :
|
|
Graphics::MacWidget(parent, x, y, w, h, wm, true), _xtra(xtra) {
|
|
|
|
_priority = 10000; // We stay on top of everything
|
|
}
|
|
|
|
bool QtvrWidget::processEvent(Common::Event &event) {
|
|
if (!_xtra->_active)
|
|
return false;
|
|
|
|
switch (event.type) {
|
|
case Common::EVENT_LBUTTONDOWN:
|
|
_xtra->_video->handleMouseButton(true, event.mouse.x - _xtra->_rect.left, event.mouse.y - _xtra->_rect.top);
|
|
return true;
|
|
case Common::EVENT_LBUTTONUP:
|
|
_xtra->_video->handleMouseButton(false, event.mouse.x - _xtra->_rect.left, event.mouse.y - _xtra->_rect.top);
|
|
return true;
|
|
case Common::EVENT_MOUSEMOVE:
|
|
_xtra->_video->handleMouseMove(event.mouse.x - _xtra->_rect.left, event.mouse.y - _xtra->_rect.top);
|
|
return true;
|
|
case Common::EVENT_KEYDOWN:
|
|
case Common::EVENT_KEYUP:
|
|
_xtra->_video->handleKey(event.kbd, event.type == Common::EVENT_KEYDOWN);
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
} // End of namespace Director
|