2020-06-29 18:16:48 -04:00
|
|
|
/* 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.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2020-07-09 00:38:37 -04:00
|
|
|
#include "common/file.h"
|
2020-07-03 11:02:10 -04:00
|
|
|
#include "common/system.h"
|
2020-07-03 14:57:31 -04:00
|
|
|
#include "common/macresman.h"
|
2020-07-03 11:02:10 -04:00
|
|
|
|
2020-06-29 18:16:48 -04:00
|
|
|
#include "graphics/primitives.h"
|
2020-07-01 17:40:52 +02:00
|
|
|
#include "graphics/macgui/macwindowmanager.h"
|
2020-06-29 18:16:48 -04:00
|
|
|
|
|
|
|
#include "director/director.h"
|
2020-07-03 14:57:31 -04:00
|
|
|
#include "director/cast.h"
|
|
|
|
#include "director/lingo/lingo.h"
|
2020-06-30 19:32:14 -04:00
|
|
|
#include "director/movie.h"
|
2020-08-04 23:26:50 -04:00
|
|
|
#include "director/window.h"
|
2020-06-29 18:16:48 -04:00
|
|
|
#include "director/score.h"
|
2020-06-30 13:11:29 -04:00
|
|
|
#include "director/castmember.h"
|
2020-07-17 23:27:52 -04:00
|
|
|
#include "director/cursor.h"
|
2020-07-08 13:27:41 -04:00
|
|
|
#include "director/channel.h"
|
2021-07-27 13:00:28 -04:00
|
|
|
#include "director/sound.h"
|
2020-06-29 18:16:48 -04:00
|
|
|
#include "director/sprite.h"
|
2020-07-03 14:57:31 -04:00
|
|
|
#include "director/util.h"
|
2021-07-20 19:31:52 +08:00
|
|
|
#include "director/sound.h"
|
2020-06-29 18:16:48 -04:00
|
|
|
|
|
|
|
namespace Director {
|
|
|
|
|
2020-08-06 13:50:09 -04:00
|
|
|
Window::Window(int id, bool scrollable, bool resizable, bool editable, Graphics::MacWindowManager *wm, DirectorEngine *vm, bool isStage)
|
2020-08-04 23:26:50 -04:00
|
|
|
: MacWindow(id, scrollable, resizable, editable, wm), Object<Window>("Window") {
|
2020-07-03 14:57:31 -04:00
|
|
|
_vm = vm;
|
2020-08-06 13:50:09 -04:00
|
|
|
_isStage = isStage;
|
2020-08-16 22:55:06 +02:00
|
|
|
_stageColor = _wm->_colorBlack;
|
2020-07-01 13:05:02 -04:00
|
|
|
_puppetTransition = nullptr;
|
2021-07-27 13:00:28 -04:00
|
|
|
_soundManager = new DirectorSound(this);
|
2020-07-03 14:57:31 -04:00
|
|
|
|
|
|
|
_currentMovie = nullptr;
|
|
|
|
_mainArchive = nullptr;
|
|
|
|
_macBinary = nullptr;
|
|
|
|
_nextMovie.frameI = -1;
|
|
|
|
_newMovieStarted = true;
|
2020-07-07 16:37:42 -04:00
|
|
|
|
|
|
|
_objType = kWindowObj;
|
2020-07-24 17:51:25 +02:00
|
|
|
_startFrame = _vm->getStartMovie().startFrame;
|
2020-08-06 14:50:59 -04:00
|
|
|
|
2020-08-07 19:01:11 -04:00
|
|
|
_windowType = -1;
|
2020-08-07 13:40:52 -04:00
|
|
|
_titleVisible = true;
|
|
|
|
updateBorderType();
|
2021-07-21 20:41:36 -04:00
|
|
|
|
2021-07-26 12:52:21 -04:00
|
|
|
_retPC = 0;
|
|
|
|
_retScript = nullptr;
|
|
|
|
_retContext = nullptr;
|
|
|
|
_retFreezeContext = false;
|
|
|
|
_retLocalVars = nullptr;
|
2020-07-03 14:57:31 -04:00
|
|
|
}
|
|
|
|
|
2020-08-04 23:26:50 -04:00
|
|
|
Window::~Window() {
|
2021-07-27 23:50:44 -04:00
|
|
|
delete _soundManager;
|
2020-07-03 14:57:31 -04:00
|
|
|
delete _currentMovie;
|
|
|
|
if (_macBinary) {
|
|
|
|
delete _macBinary;
|
|
|
|
_macBinary = nullptr;
|
|
|
|
}
|
2020-06-29 18:16:48 -04:00
|
|
|
}
|
|
|
|
|
2021-06-30 20:48:42 +08:00
|
|
|
void Window::invertChannel(Channel *channel, const Common::Rect &destRect) {
|
2021-08-03 16:14:50 +08:00
|
|
|
const Graphics::Surface *mask;
|
|
|
|
|
|
|
|
// in D3, we have inverted QDshape
|
|
|
|
if (channel->_sprite->isQDShape() && channel->_sprite->_ink == kInkTypeMatte)
|
|
|
|
mask = channel->_sprite->getQDMatte();
|
|
|
|
else
|
|
|
|
mask = channel->getMask(true);
|
|
|
|
|
2021-06-30 20:48:42 +08:00
|
|
|
Common::Rect srcRect = channel->getBbox();
|
|
|
|
srcRect.clip(destRect);
|
2020-07-09 16:04:58 -04:00
|
|
|
|
2021-07-25 16:43:20 +08:00
|
|
|
// let compiler to optimize it
|
|
|
|
int xoff = srcRect.left - channel->getBbox().left;
|
|
|
|
int yoff = srcRect.top - channel->getBbox().top;
|
|
|
|
|
2020-08-16 23:03:13 +02:00
|
|
|
if (_wm->_pixelformat.bytesPerPixel == 1) {
|
2021-06-30 20:48:42 +08:00
|
|
|
for (int i = 0; i < srcRect.height(); i++) {
|
|
|
|
byte *src = (byte *)_composeSurface->getBasePtr(srcRect.left, srcRect.top + i);
|
2021-07-25 16:43:20 +08:00
|
|
|
const byte *msk = mask ? (const byte *)mask->getBasePtr(xoff, yoff + i) : nullptr;
|
2020-07-09 16:04:58 -04:00
|
|
|
|
2021-06-30 20:48:42 +08:00
|
|
|
for (int j = 0; j < srcRect.width(); j++, src++)
|
2020-08-16 00:58:34 +02:00
|
|
|
if (!mask || (msk && !(*msk++)))
|
|
|
|
*src = ~(*src);
|
2020-08-16 23:03:13 +02:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
uint32 alpha = _wm->_pixelformat.ARGBToColor(255, 0, 0, 0);
|
|
|
|
|
2021-06-30 20:48:42 +08:00
|
|
|
for (int i = 0; i < srcRect.height(); i++) {
|
|
|
|
uint32 *src = (uint32 *)_composeSurface->getBasePtr(srcRect.left, srcRect.top + i);
|
2021-07-25 16:43:20 +08:00
|
|
|
const uint32 *msk = mask ? (const uint32 *)mask->getBasePtr(xoff, yoff + i) : nullptr;
|
2020-08-16 00:58:34 +02:00
|
|
|
|
2021-06-30 20:48:42 +08:00
|
|
|
for (int j = 0; j < srcRect.width(); j++, src++)
|
2020-08-16 00:58:34 +02:00
|
|
|
if (!mask || (msk && !(*msk++)))
|
2020-08-16 23:03:13 +02:00
|
|
|
*src = ~(*src & ~alpha) | alpha;
|
2020-08-16 00:58:34 +02:00
|
|
|
}
|
2020-07-09 11:08:15 -04:00
|
|
|
}
|
2020-07-06 11:27:45 -04:00
|
|
|
}
|
|
|
|
|
2020-08-04 23:26:50 -04:00
|
|
|
bool Window::render(bool forceRedraw, Graphics::ManagedSurface *blitTo) {
|
2020-07-09 00:38:37 -04:00
|
|
|
if (!_currentMovie)
|
|
|
|
return false;
|
|
|
|
|
2020-06-29 18:16:48 -04:00
|
|
|
if (forceRedraw) {
|
|
|
|
blitTo->clear(_stageColor);
|
2020-07-31 08:25:56 -04:00
|
|
|
markAllDirty();
|
2020-06-29 18:16:48 -04:00
|
|
|
} else {
|
2021-06-04 13:16:26 +02:00
|
|
|
if (_dirtyRects.size() == 0 && _currentMovie->_videoPlayback == false)
|
2020-07-14 13:22:54 -04:00
|
|
|
return false;
|
2020-06-29 18:16:48 -04:00
|
|
|
|
|
|
|
mergeDirtyRects();
|
|
|
|
}
|
|
|
|
|
2020-07-14 13:22:54 -04:00
|
|
|
if (!blitTo)
|
2020-07-21 12:49:15 -04:00
|
|
|
blitTo = _composeSurface;
|
2021-06-30 20:16:46 +08:00
|
|
|
Channel *hiliteChannel = _currentMovie->getScore()->getChannelById(_currentMovie->_currentHiliteChannelId);
|
2020-07-14 13:22:54 -04:00
|
|
|
|
2020-06-29 18:16:48 -04:00
|
|
|
for (Common::List<Common::Rect>::iterator i = _dirtyRects.begin(); i != _dirtyRects.end(); i++) {
|
|
|
|
const Common::Rect &r = *i;
|
|
|
|
blitTo->fillRect(r, _stageColor);
|
|
|
|
|
2020-07-09 00:38:37 -04:00
|
|
|
_dirtyChannels = _currentMovie->getScore()->getSpriteIntersections(r);
|
2020-08-23 20:31:48 +02:00
|
|
|
for (int pass = 0; pass < 2; pass++) {
|
|
|
|
for (Common::List<Channel *>::iterator j = _dirtyChannels.begin(); j != _dirtyChannels.end(); j++) {
|
|
|
|
if ((*j)->isActiveVideo() && (*j)->isVideoDirectToStage()) {
|
|
|
|
if (pass == 0)
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
if (pass == 1)
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2021-06-30 20:16:46 +08:00
|
|
|
if ((*j)->_visible) {
|
2020-08-23 20:31:48 +02:00
|
|
|
inkBlitFrom(*j, r, blitTo);
|
2021-06-30 20:48:42 +08:00
|
|
|
if ((*j) == hiliteChannel)
|
|
|
|
invertChannel(hiliteChannel, r);
|
2021-06-30 20:16:46 +08:00
|
|
|
}
|
2020-08-23 20:31:48 +02:00
|
|
|
}
|
2020-07-08 21:37:45 +08:00
|
|
|
}
|
2020-06-29 18:16:48 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
_dirtyRects.clear();
|
|
|
|
_contentIsDirty = true;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-08-16 00:58:34 +02:00
|
|
|
void Window::setStageColor(uint32 stageColor, bool forceReset) {
|
2020-08-06 11:18:19 -04:00
|
|
|
if (stageColor != _stageColor || forceReset) {
|
2020-06-29 18:16:48 -04:00
|
|
|
_stageColor = stageColor;
|
|
|
|
reset();
|
2020-07-31 08:25:56 -04:00
|
|
|
markAllDirty();
|
2020-06-29 18:16:48 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-04 23:26:50 -04:00
|
|
|
void Window::reset() {
|
2020-08-06 12:36:08 -04:00
|
|
|
resize(_composeSurface->w, _composeSurface->h, true);
|
2020-07-21 12:49:15 -04:00
|
|
|
_composeSurface->clear(_stageColor);
|
2020-06-29 18:16:48 -04:00
|
|
|
_contentIsDirty = true;
|
|
|
|
}
|
|
|
|
|
2020-08-04 23:26:50 -04:00
|
|
|
void Window::inkBlitFrom(Channel *channel, Common::Rect destRect, Graphics::ManagedSurface *blitTo) {
|
2020-06-29 18:16:48 -04:00
|
|
|
Common::Rect srcRect = channel->getBbox();
|
|
|
|
destRect.clip(srcRect);
|
|
|
|
|
2020-07-09 11:08:15 -04:00
|
|
|
DirectorPlotData pd = channel->getPlotData();
|
|
|
|
pd.destRect = destRect;
|
|
|
|
pd.dst = blitTo;
|
2020-06-29 18:16:48 -04:00
|
|
|
|
2020-07-09 15:33:03 -04:00
|
|
|
if (pd.ms) {
|
2020-07-09 11:08:15 -04:00
|
|
|
inkBlitShape(&pd, srcRect);
|
2020-07-09 15:33:03 -04:00
|
|
|
} else if (pd.srf) {
|
2020-07-09 22:45:58 -04:00
|
|
|
if (channel->isStretched()) {
|
|
|
|
srcRect = channel->getBbox(true);
|
|
|
|
inkBlitStretchSurface(&pd, srcRect, channel->getMask());
|
|
|
|
} else {
|
|
|
|
inkBlitSurface(&pd, srcRect, channel->getMask());
|
|
|
|
}
|
2020-07-06 10:39:32 -04:00
|
|
|
} else {
|
2021-06-30 18:41:00 -04:00
|
|
|
warning("Window::inkBlitFrom: No source surface: spriteType: %d, castType: %d, castId: %s", channel->_sprite->_spriteType, channel->_sprite->_cast ? channel->_sprite->_cast->_type : 0, channel->_sprite->_castId.asString().c_str());
|
2020-07-03 18:45:58 -04:00
|
|
|
}
|
|
|
|
}
|
2020-06-29 18:16:48 -04:00
|
|
|
|
2020-08-04 23:26:50 -04:00
|
|
|
void Window::inkBlitShape(DirectorPlotData *pd, Common::Rect &srcRect) {
|
2020-07-09 15:33:03 -04:00
|
|
|
if (!pd->ms)
|
2020-07-09 13:49:41 -04:00
|
|
|
return;
|
|
|
|
|
2020-07-09 15:33:03 -04:00
|
|
|
// Preprocess shape colours
|
2020-07-09 09:36:35 -04:00
|
|
|
switch (pd->ink) {
|
|
|
|
case kInkTypeNotTrans:
|
|
|
|
case kInkTypeNotReverse:
|
|
|
|
case kInkTypeNotGhost:
|
2020-06-29 18:16:48 -04:00
|
|
|
return;
|
2020-07-09 09:36:35 -04:00
|
|
|
case kInkTypeReverse:
|
2020-07-09 15:33:03 -04:00
|
|
|
pd->ms->foreColor = 0;
|
|
|
|
pd->ms->backColor = 0;
|
2020-07-09 09:36:35 -04:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2020-07-03 18:45:58 -04:00
|
|
|
|
2021-03-28 02:33:53 +01:00
|
|
|
Common::Rect fillAreaRect((int)srcRect.width(), (int)srcRect.height());
|
|
|
|
fillAreaRect.moveTo(srcRect.left, srcRect.top);
|
2020-07-09 15:33:03 -04:00
|
|
|
Graphics::MacPlotData plotFill(pd->dst, nullptr, &g_director->getPatterns(), pd->ms->pattern, srcRect.left, srcRect.top, 1, pd->ms->backColor);
|
2020-07-03 18:45:58 -04:00
|
|
|
|
2020-07-09 15:33:03 -04:00
|
|
|
Common::Rect strokeRect(MAX((int)srcRect.width() - pd->ms->lineSize, 0), MAX((int)srcRect.height() - pd->ms->lineSize, 0));
|
2020-07-03 18:45:58 -04:00
|
|
|
strokeRect.moveTo(srcRect.left, srcRect.top);
|
2020-07-09 15:33:03 -04:00
|
|
|
Graphics::MacPlotData plotStroke(pd->dst, nullptr, &g_director->getPatterns(), 1, strokeRect.left, strokeRect.top, pd->ms->lineSize, pd->ms->backColor);
|
2020-07-03 18:45:58 -04:00
|
|
|
|
2020-07-09 15:33:03 -04:00
|
|
|
switch (pd->ms->spriteType) {
|
2020-07-03 18:45:58 -04:00
|
|
|
case kRectangleSprite:
|
2020-07-09 15:33:03 -04:00
|
|
|
pd->ms->pd = &plotFill;
|
2021-03-28 02:33:53 +01:00
|
|
|
Graphics::drawFilledRect(fillAreaRect, pd->ms->foreColor, g_director->getInkDrawPixel(), pd);
|
2020-07-03 18:45:58 -04:00
|
|
|
// fall through
|
|
|
|
case kOutlinedRectangleSprite:
|
2021-07-04 18:58:08 +08:00
|
|
|
// if we have lineSize <= 0, means we are not drawing anything. so we may return directly.
|
|
|
|
if (pd->ms->lineSize <= 0)
|
|
|
|
break;
|
2020-07-09 15:33:03 -04:00
|
|
|
pd->ms->pd = &plotStroke;
|
2020-08-16 00:11:51 +02:00
|
|
|
Graphics::drawRect(strokeRect, pd->ms->foreColor, g_director->getInkDrawPixel(), pd);
|
2020-07-03 18:45:58 -04:00
|
|
|
break;
|
|
|
|
case kRoundedRectangleSprite:
|
2020-07-09 15:33:03 -04:00
|
|
|
pd->ms->pd = &plotFill;
|
2021-03-28 02:33:53 +01:00
|
|
|
Graphics::drawRoundRect(fillAreaRect, 12, pd->ms->foreColor, true, g_director->getInkDrawPixel(), pd);
|
2020-07-03 18:45:58 -04:00
|
|
|
// fall through
|
|
|
|
case kOutlinedRoundedRectangleSprite:
|
2021-07-04 18:58:08 +08:00
|
|
|
if (pd->ms->lineSize <= 0)
|
|
|
|
break;
|
2020-07-09 15:33:03 -04:00
|
|
|
pd->ms->pd = &plotStroke;
|
2020-08-16 00:11:51 +02:00
|
|
|
Graphics::drawRoundRect(strokeRect, 12, pd->ms->foreColor, false, g_director->getInkDrawPixel(), pd);
|
2020-07-03 18:45:58 -04:00
|
|
|
break;
|
|
|
|
case kOvalSprite:
|
2020-07-09 15:33:03 -04:00
|
|
|
pd->ms->pd = &plotFill;
|
2021-03-28 02:33:53 +01:00
|
|
|
Graphics::drawEllipse(fillAreaRect.left, fillAreaRect.top, fillAreaRect.right, fillAreaRect.bottom, pd->ms->foreColor, true, g_director->getInkDrawPixel(), pd);
|
2020-07-03 18:45:58 -04:00
|
|
|
// fall through
|
|
|
|
case kOutlinedOvalSprite:
|
2021-07-04 18:58:08 +08:00
|
|
|
if (pd->ms->lineSize <= 0)
|
|
|
|
break;
|
2020-07-09 15:33:03 -04:00
|
|
|
pd->ms->pd = &plotStroke;
|
2020-08-16 00:11:51 +02:00
|
|
|
Graphics::drawEllipse(strokeRect.left, strokeRect.top, strokeRect.right, strokeRect.bottom, pd->ms->foreColor, false, g_director->getInkDrawPixel(), pd);
|
2020-07-03 18:45:58 -04:00
|
|
|
break;
|
|
|
|
case kLineTopBottomSprite:
|
2020-07-09 15:33:03 -04:00
|
|
|
pd->ms->pd = &plotStroke;
|
2020-08-16 00:11:51 +02:00
|
|
|
Graphics::drawLine(strokeRect.left, strokeRect.top, strokeRect.right, strokeRect.bottom, pd->ms->foreColor, g_director->getInkDrawPixel(), pd);
|
2020-07-03 18:45:58 -04:00
|
|
|
break;
|
|
|
|
case kLineBottomTopSprite:
|
2020-07-09 15:33:03 -04:00
|
|
|
pd->ms->pd = &plotStroke;
|
2021-06-24 10:00:43 +08:00
|
|
|
Graphics::drawLine(strokeRect.left, strokeRect.bottom, strokeRect.right, strokeRect.top, pd->ms->foreColor, g_director->getInkDrawPixel(), pd);
|
2020-07-03 18:45:58 -04:00
|
|
|
break;
|
|
|
|
default:
|
2020-08-04 23:26:50 -04:00
|
|
|
warning("Window::inkBlitFrom: Expected shape type but got type %d", pd->ms->spriteType);
|
2020-06-29 18:16:48 -04:00
|
|
|
}
|
2020-07-03 18:45:58 -04:00
|
|
|
}
|
2020-06-29 18:16:48 -04:00
|
|
|
|
2020-08-04 23:26:50 -04:00
|
|
|
void Window::inkBlitSurface(DirectorPlotData *pd, Common::Rect &srcRect, const Graphics::Surface *mask) {
|
2020-07-09 15:33:03 -04:00
|
|
|
if (!pd->srf)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// TODO: Determine why colourization causes problems in Warlock
|
|
|
|
if (pd->sprite == kTextSprite)
|
|
|
|
pd->applyColor = false;
|
|
|
|
|
2020-07-23 10:51:11 -04:00
|
|
|
pd->srcPoint.y = abs(srcRect.top - pd->destRect.top);
|
2020-07-03 18:45:58 -04:00
|
|
|
for (int i = 0; i < pd->destRect.height(); i++, pd->srcPoint.y++) {
|
2020-08-16 00:58:34 +02:00
|
|
|
if (_wm->_pixelformat.bytesPerPixel == 1) {
|
|
|
|
pd->srcPoint.x = abs(srcRect.left - pd->destRect.left);
|
|
|
|
const byte *msk = mask ? (const byte *)mask->getBasePtr(pd->srcPoint.x, pd->srcPoint.y) : nullptr;
|
|
|
|
|
|
|
|
for (int j = 0; j < pd->destRect.width(); j++, pd->srcPoint.x++) {
|
2021-06-19 19:28:21 +08:00
|
|
|
if (!mask || (msk && !(*msk++))) {
|
2020-08-16 00:58:34 +02:00
|
|
|
(g_director->getInkDrawPixel())(pd->destRect.left + j, pd->destRect.top + i,
|
|
|
|
preprocessColor(pd, *((byte *)pd->srf->getBasePtr(pd->srcPoint.x, pd->srcPoint.y))), pd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
pd->srcPoint.x = abs(srcRect.left - pd->destRect.left);
|
|
|
|
const uint32 *msk = mask ? (const uint32 *)mask->getBasePtr(pd->srcPoint.x, pd->srcPoint.y) : nullptr;
|
|
|
|
|
|
|
|
for (int j = 0; j < pd->destRect.width(); j++, pd->srcPoint.x++) {
|
2021-06-19 19:28:21 +08:00
|
|
|
if (!mask || (msk && !(*msk++))) {
|
2020-08-16 00:58:34 +02:00
|
|
|
(g_director->getInkDrawPixel())(pd->destRect.left + j, pd->destRect.top + i,
|
2020-08-16 21:46:36 +02:00
|
|
|
preprocessColor(pd, *((int *)pd->srf->getBasePtr(pd->srcPoint.x, pd->srcPoint.y))), pd);
|
2020-08-16 00:58:34 +02:00
|
|
|
}
|
2020-07-09 15:33:03 -04:00
|
|
|
}
|
2020-07-23 10:51:11 -04:00
|
|
|
}
|
2020-07-09 15:33:03 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-04 23:26:50 -04:00
|
|
|
void Window::inkBlitStretchSurface(DirectorPlotData *pd, Common::Rect &srcRect, const Graphics::Surface *mask) {
|
2020-07-09 22:45:58 -04:00
|
|
|
if (!pd->srf)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// TODO: Determine why colourization causes problems in Warlock
|
|
|
|
if (pd->sprite == kTextSprite)
|
|
|
|
pd->applyColor = false;
|
|
|
|
|
|
|
|
int scaleX = SCALE_THRESHOLD * srcRect.width() / pd->destRect.width();
|
|
|
|
int scaleY = SCALE_THRESHOLD * srcRect.height() / pd->destRect.height();
|
|
|
|
|
2020-07-23 10:51:11 -04:00
|
|
|
pd->srcPoint.y = abs(srcRect.top - pd->destRect.top);
|
2020-07-09 22:45:58 -04:00
|
|
|
|
|
|
|
for (int i = 0, scaleYCtr = 0; i < pd->destRect.height(); i++, scaleYCtr += scaleY, pd->srcPoint.y++) {
|
2020-08-16 00:58:34 +02:00
|
|
|
if (_wm->_pixelformat.bytesPerPixel == 1) {
|
|
|
|
pd->srcPoint.x = abs(srcRect.left - pd->destRect.left);
|
|
|
|
const byte *msk = mask ? (const byte *)mask->getBasePtr(pd->srcPoint.x, pd->srcPoint.y) : nullptr;
|
|
|
|
|
|
|
|
for (int xCtr = 0, scaleXCtr = 0; xCtr < pd->destRect.width(); xCtr++, scaleXCtr += scaleX, pd->srcPoint.x++) {
|
2021-06-19 19:28:21 +08:00
|
|
|
if (!mask || !(*msk++)) {
|
2020-08-16 00:58:34 +02:00
|
|
|
(g_director->getInkDrawPixel())(pd->destRect.left + xCtr, pd->destRect.top + i,
|
|
|
|
preprocessColor(pd, *((byte *)pd->srf->getBasePtr(scaleXCtr / SCALE_THRESHOLD, scaleYCtr / SCALE_THRESHOLD))), pd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
pd->srcPoint.x = abs(srcRect.left - pd->destRect.left);
|
|
|
|
const uint32 *msk = mask ? (const uint32 *)mask->getBasePtr(pd->srcPoint.x, pd->srcPoint.y) : nullptr;
|
|
|
|
|
|
|
|
for (int xCtr = 0, scaleXCtr = 0; xCtr < pd->destRect.width(); xCtr++, scaleXCtr += scaleX, pd->srcPoint.x++) {
|
2021-06-19 19:28:21 +08:00
|
|
|
if (!mask || !(*msk++)) {
|
2020-08-16 00:58:34 +02:00
|
|
|
(g_director->getInkDrawPixel())(pd->destRect.left + xCtr, pd->destRect.top + i,
|
2020-08-16 21:46:36 +02:00
|
|
|
preprocessColor(pd, *((int *)pd->srf->getBasePtr(scaleXCtr / SCALE_THRESHOLD, scaleYCtr / SCALE_THRESHOLD))), pd);
|
2020-08-16 00:58:34 +02:00
|
|
|
}
|
2020-07-23 10:50:03 -04:00
|
|
|
}
|
2020-07-09 22:45:58 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-23 01:23:28 +02:00
|
|
|
int Window::preprocessColor(DirectorPlotData *p, uint32 src) {
|
2020-07-09 15:33:03 -04:00
|
|
|
// HACK: Right now this method is just used for adjusting the colourization on text
|
|
|
|
// sprites, as it would be costly to colourize the chunks on the fly each
|
|
|
|
// time a section needs drawing. It's ugly but mostly works.
|
|
|
|
if (p->sprite == kTextSprite) {
|
|
|
|
switch(p->ink) {
|
|
|
|
case kInkTypeMask:
|
2021-06-19 19:28:21 +08:00
|
|
|
src = (src == p->backColor ? p->foreColor : 0xff);
|
2020-07-09 15:33:03 -04:00
|
|
|
break;
|
|
|
|
case kInkTypeReverse:
|
|
|
|
src = (src == p->foreColor ? 0 : p->colorWhite);
|
|
|
|
break;
|
|
|
|
case kInkTypeNotReverse:
|
|
|
|
src = (src == p->backColor ? p->colorWhite : 0);
|
|
|
|
break;
|
2021-07-23 19:04:11 +08:00
|
|
|
// looks like this part is wrong, maybe it's very same as reverse?
|
|
|
|
// check warlock/DATA/WARLOCKSHIP/ENG/ABOUT to see more detail.
|
|
|
|
// case kInkTypeGhost:
|
|
|
|
// src = (src == p->foreColor ? p->backColor : p->colorWhite);
|
|
|
|
// break;
|
2020-07-09 15:33:03 -04:00
|
|
|
case kInkTypeNotGhost:
|
|
|
|
src = (src == p->backColor ? p->colorWhite : p->backColor);
|
|
|
|
break;
|
|
|
|
case kInkTypeNotCopy:
|
|
|
|
src = (src == p->foreColor ? p->backColor : p->foreColor);
|
|
|
|
break;
|
|
|
|
case kInkTypeNotTrans:
|
|
|
|
src = (src == p->foreColor ? p->backColor : p->colorWhite);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2020-06-29 18:16:48 -04:00
|
|
|
}
|
2020-07-09 15:33:03 -04:00
|
|
|
|
|
|
|
return src;
|
2020-06-29 18:16:48 -04:00
|
|
|
}
|
|
|
|
|
2020-08-04 23:26:50 -04:00
|
|
|
Common::Point Window::getMousePos() {
|
2020-07-06 13:12:55 -04:00
|
|
|
return g_system->getEventManager()->getMousePos() - Common::Point(_innerDims.left, _innerDims.top);
|
2020-07-03 11:02:10 -04:00
|
|
|
}
|
|
|
|
|
2020-08-04 23:26:50 -04:00
|
|
|
void Window::setVisible(bool visible, bool silent) {
|
2020-07-09 00:38:37 -04:00
|
|
|
// setting visible triggers movie load
|
2020-07-09 20:07:12 -04:00
|
|
|
if (!_currentMovie && !silent) {
|
2020-07-09 00:38:37 -04:00
|
|
|
Common::String movieName = getName();
|
|
|
|
setNextMovie(movieName);
|
|
|
|
}
|
|
|
|
|
|
|
|
BaseMacWindow::setVisible(visible);
|
2020-08-10 22:22:05 -04:00
|
|
|
|
|
|
|
if (visible)
|
|
|
|
_wm->setActiveWindow(_id);
|
2020-07-09 00:38:37 -04:00
|
|
|
}
|
|
|
|
|
2020-08-04 23:26:50 -04:00
|
|
|
bool Window::setNextMovie(Common::String &movieFilenameRaw) {
|
2020-07-09 00:38:37 -04:00
|
|
|
Common::String movieFilename = pathMakeRelative(movieFilenameRaw);
|
|
|
|
|
|
|
|
bool fileExists = false;
|
2021-08-04 14:51:15 -04:00
|
|
|
Common::File file;
|
|
|
|
if (file.open(Common::Path(movieFilename, _vm->_dirSeparator))) {
|
|
|
|
fileExists = true;
|
|
|
|
file.close();
|
2020-07-09 00:38:37 -04:00
|
|
|
}
|
|
|
|
|
2021-08-04 14:51:15 -04:00
|
|
|
debug(1, "Window::setNextMovie: '%s' -> '%s' -> '%s'", movieFilenameRaw.c_str(), convertPath(movieFilenameRaw).c_str(), movieFilename.c_str());
|
2020-07-09 00:38:37 -04:00
|
|
|
|
|
|
|
if (!fileExists) {
|
|
|
|
warning("Movie %s does not exist", movieFilename.c_str());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-08-04 14:51:15 -04:00
|
|
|
_nextMovie.movie = movieFilename;
|
2020-07-09 00:38:37 -04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-08-06 14:50:59 -04:00
|
|
|
void Window::updateBorderType() {
|
|
|
|
if (_isStage) {
|
|
|
|
setBorderType(3);
|
2020-08-07 13:40:52 -04:00
|
|
|
} else if (!_titleVisible) {
|
|
|
|
setBorderType(2);
|
2020-08-06 14:50:59 -04:00
|
|
|
} else {
|
|
|
|
setBorderType(MAX(0, MIN(_windowType, 16)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-04 23:26:50 -04:00
|
|
|
bool Window::step() {
|
2020-07-10 11:06:02 -04:00
|
|
|
// finish last movie
|
|
|
|
if (_currentMovie && _currentMovie->getScore()->_playState == kPlayStopped) {
|
2020-07-13 15:58:02 -04:00
|
|
|
debugC(3, kDebugEvents, "\n@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
|
2021-07-27 19:10:11 +02:00
|
|
|
debugC(3, kDebugEvents, "@@@@ Finishing movie '%s' in '%s'", toPrintable(_currentMovie->getMacName()).c_str(), _currentPath.c_str());
|
2020-07-13 15:58:02 -04:00
|
|
|
debugC(3, kDebugEvents, "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n");
|
2020-07-03 14:57:31 -04:00
|
|
|
|
2020-07-10 11:06:02 -04:00
|
|
|
_currentMovie->getScore()->stopPlay();
|
2021-07-27 19:10:11 +02:00
|
|
|
debugC(1, kDebugEvents, "Finished playback of movie '%s'", toPrintable(_currentMovie->getMacName()).c_str());
|
2020-07-09 12:20:23 -04:00
|
|
|
|
2020-07-10 11:06:02 -04:00
|
|
|
if (_vm->getGameGID() == GID_TESTALL) {
|
|
|
|
_nextMovie = getNextMovieFromQueue();
|
2020-07-03 14:57:31 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-10 11:06:02 -04:00
|
|
|
// prepare next movie
|
2020-07-03 14:57:31 -04:00
|
|
|
if (!_nextMovie.movie.empty()) {
|
|
|
|
_newMovieStarted = true;
|
|
|
|
|
|
|
|
_currentPath = getPath(_nextMovie.movie, _currentPath);
|
|
|
|
|
|
|
|
Cast *sharedCast = nullptr;
|
|
|
|
if (_currentMovie) {
|
|
|
|
sharedCast = _currentMovie->getSharedCast();
|
|
|
|
_currentMovie->_sharedCast = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
delete _currentMovie;
|
|
|
|
_currentMovie = nullptr;
|
|
|
|
|
2021-08-02 16:31:10 -04:00
|
|
|
Archive *mov = openMainArchive(_currentPath + Common::lastPathComponent(_nextMovie.movie, g_director->_dirSeparator));
|
2020-07-03 14:57:31 -04:00
|
|
|
|
|
|
|
if (!mov) {
|
|
|
|
warning("nextMovie: No movie is loaded");
|
|
|
|
|
|
|
|
if (_vm->getGameGID() == GID_TESTALL) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-07-03 15:51:18 -04:00
|
|
|
_currentMovie = new Movie(this);
|
2020-07-03 14:57:31 -04:00
|
|
|
_currentMovie->setArchive(mov);
|
2020-07-16 18:14:04 -04:00
|
|
|
|
|
|
|
debug(0, "\n@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
|
2021-07-27 19:10:11 +02:00
|
|
|
debug(0, "@@@@ Switching to movie '%s' in '%s'", toPrintable(_currentMovie->getMacName()).c_str(), _currentPath.c_str());
|
2020-07-16 18:14:04 -04:00
|
|
|
debug(0, "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n");
|
2020-07-03 14:57:31 -04:00
|
|
|
|
|
|
|
g_lingo->resetLingo();
|
2021-07-16 16:05:13 -04:00
|
|
|
Common::String sharedCastPath = getSharedCastPath();
|
|
|
|
if (!sharedCastPath.empty()) {
|
|
|
|
if (sharedCast && sharedCast->_castArchive
|
|
|
|
&& sharedCast->_castArchive->getPathName().equalsIgnoreCase(sharedCastPath)) {
|
|
|
|
// if we are not deleting shared cast, then we need to clear those previous widget pointer
|
|
|
|
sharedCast->releaseCastMemberWidget();
|
|
|
|
_currentMovie->_sharedCast = sharedCast;
|
|
|
|
} else {
|
|
|
|
delete sharedCast;
|
|
|
|
_currentMovie->loadSharedCastsFrom(sharedCastPath);
|
|
|
|
}
|
2020-07-03 14:57:31 -04:00
|
|
|
} else {
|
|
|
|
delete sharedCast;
|
|
|
|
}
|
2021-07-27 13:00:28 -04:00
|
|
|
_soundManager->changingMovie();
|
2020-07-03 14:57:31 -04:00
|
|
|
|
|
|
|
_nextMovie.movie.clear();
|
2020-07-10 11:06:02 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// play current movie
|
|
|
|
if (_currentMovie) {
|
|
|
|
switch (_currentMovie->getScore()->_playState) {
|
|
|
|
case kPlayNotStarted:
|
|
|
|
{
|
2020-07-16 18:14:04 -04:00
|
|
|
debug(0, "\n@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
|
2021-07-27 18:14:20 +02:00
|
|
|
debug(0, "@@@@ Loading movie '%s' in '%s'", toPrintable(_currentMovie->getMacName()).c_str(), _currentPath.c_str());
|
2020-07-16 18:14:04 -04:00
|
|
|
debug(0, "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n");
|
|
|
|
|
2020-07-10 11:06:02 -04:00
|
|
|
bool goodMovie = _currentMovie->loadArchive();
|
|
|
|
|
|
|
|
// If we came in a loop, then skip as requested
|
|
|
|
if (!_nextMovie.frameS.empty()) {
|
|
|
|
_currentMovie->getScore()->setStartToLabel(_nextMovie.frameS);
|
|
|
|
_nextMovie.frameS.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_nextMovie.frameI != -1) {
|
|
|
|
_currentMovie->getScore()->setCurrentFrame(_nextMovie.frameI);
|
|
|
|
_nextMovie.frameI = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!debugChannelSet(-1, kDebugCompileOnly) && goodMovie) {
|
|
|
|
debugC(1, kDebugEvents, "Starting playback of movie '%s'", _currentMovie->getMacName().c_str());
|
|
|
|
_currentMovie->getScore()->startPlay();
|
2020-07-24 17:51:25 +02:00
|
|
|
if (_startFrame != -1) {
|
|
|
|
_currentMovie->getScore()->setCurrentFrame(_startFrame);
|
|
|
|
_startFrame = -1;
|
|
|
|
}
|
2020-07-10 11:06:02 -04:00
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// fall through
|
|
|
|
case kPlayStarted:
|
2020-07-16 18:14:04 -04:00
|
|
|
debugC(3, kDebugEvents, "\n@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
|
2021-07-27 19:10:11 +02:00
|
|
|
debugC(3, kDebugEvents, "@@@@ Stepping movie '%s' in '%s'", toPrintable(_currentMovie->getMacName()).c_str(), _currentPath.c_str());
|
2020-07-16 18:14:04 -04:00
|
|
|
debugC(3, kDebugEvents, "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n");
|
2020-07-10 11:06:02 -04:00
|
|
|
_currentMovie->getScore()->step();
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
2020-07-03 14:57:31 -04:00
|
|
|
}
|
|
|
|
|
2020-07-09 12:20:23 -04:00
|
|
|
return false;
|
2020-07-03 14:57:31 -04:00
|
|
|
}
|
|
|
|
|
2021-07-16 16:05:13 -04:00
|
|
|
Common::String Window::getSharedCastPath() {
|
|
|
|
Common::Array<Common::String> namesToTry;
|
|
|
|
if (_vm->getVersion() < 400) {
|
|
|
|
if (g_director->getPlatform() == Common::kPlatformWindows) {
|
|
|
|
namesToTry.push_back("SHARDCST.MMM");
|
|
|
|
} else {
|
|
|
|
namesToTry.push_back("Shared Cast");
|
|
|
|
}
|
|
|
|
} else if (_vm->getVersion() < 500) {
|
|
|
|
namesToTry.push_back("Shared.dir");
|
|
|
|
namesToTry.push_back("Shared.dxr");
|
|
|
|
} else {
|
|
|
|
// TODO: Does D5 actually support D4-style shared cast?
|
|
|
|
namesToTry.push_back("Shared.cst");
|
|
|
|
namesToTry.push_back("Shared.cxt");
|
|
|
|
}
|
|
|
|
|
|
|
|
for (uint i = 0; i < namesToTry.size(); i++) {
|
|
|
|
Common::File f;
|
2021-08-02 16:31:10 -04:00
|
|
|
if (f.open(Common::Path(_currentPath + namesToTry[i], _vm->_dirSeparator))) {
|
2021-07-16 16:05:13 -04:00
|
|
|
f.close();
|
|
|
|
return _currentPath + namesToTry[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return Common::String();
|
|
|
|
}
|
|
|
|
|
2020-08-21 00:35:36 +02:00
|
|
|
} // End of namespace Director
|