/* 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