/* 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 .
*
*/
/**********************************************
*
* 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("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