2016-08-23 09:41:00 +02: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.
|
2016-09-03 12:46:38 +02:00
|
|
|
*
|
2016-08-23 09:41:00 +02:00
|
|
|
* 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.
|
2016-09-03 12:46:38 +02:00
|
|
|
*
|
2016-08-23 09:41:00 +02:00
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "common/system.h"
|
2016-08-23 13:30:00 +02:00
|
|
|
#include "graphics/font.h"
|
2016-10-06 23:48:50 +02:00
|
|
|
#include "graphics/macgui/macfontmanager.h"
|
2016-08-23 09:41:00 +02:00
|
|
|
#include "graphics/macgui/macwindowmanager.h"
|
2017-01-11 11:25:31 +11:00
|
|
|
#include "graphics/primitives.h"
|
2016-08-23 09:41:00 +02:00
|
|
|
#include "image/bmp.h"
|
|
|
|
|
|
|
|
#include "director/director.h"
|
2016-11-08 21:51:51 +01:00
|
|
|
#include "director/cast.h"
|
2016-08-23 09:41:00 +02:00
|
|
|
#include "director/frame.h"
|
|
|
|
#include "director/images.h"
|
2016-10-26 10:08:17 +02:00
|
|
|
#include "director/archive.h"
|
2016-08-23 09:41:00 +02:00
|
|
|
#include "director/score.h"
|
|
|
|
#include "director/sprite.h"
|
|
|
|
|
|
|
|
namespace Director {
|
|
|
|
|
|
|
|
Frame::Frame(DirectorEngine *vm) {
|
|
|
|
_vm = vm;
|
|
|
|
_transDuration = 0;
|
|
|
|
_transType = kTransNone;
|
|
|
|
_transArea = 0;
|
|
|
|
_transChunkSize = 0;
|
|
|
|
_tempo = 0;
|
|
|
|
|
|
|
|
_sound1 = 0;
|
|
|
|
_sound2 = 0;
|
|
|
|
_soundType1 = 0;
|
|
|
|
_soundType2 = 0;
|
|
|
|
|
|
|
|
_actionId = 0;
|
|
|
|
_skipFrameFlag = 0;
|
|
|
|
_blend = 0;
|
|
|
|
|
2016-08-26 12:33:48 +02:00
|
|
|
_palette = NULL;
|
|
|
|
|
2016-10-28 22:48:28 +02:00
|
|
|
_sprites.resize(CHANNEL_COUNT + 1);
|
2016-08-23 09:41:00 +02:00
|
|
|
|
|
|
|
for (uint16 i = 0; i < _sprites.size(); i++) {
|
|
|
|
Sprite *sp = new Sprite();
|
|
|
|
_sprites[i] = sp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Frame::Frame(const Frame &frame) {
|
|
|
|
_vm = frame._vm;
|
|
|
|
_actionId = frame._actionId;
|
|
|
|
_transArea = frame._transArea;
|
|
|
|
_transDuration = frame._transDuration;
|
|
|
|
_transType = frame._transType;
|
|
|
|
_transChunkSize = frame._transChunkSize;
|
|
|
|
_tempo = frame._tempo;
|
|
|
|
_sound1 = frame._sound1;
|
|
|
|
_sound2 = frame._sound2;
|
|
|
|
_soundType1 = frame._soundType1;
|
|
|
|
_soundType2 = frame._soundType2;
|
|
|
|
_skipFrameFlag = frame._skipFrameFlag;
|
|
|
|
_blend = frame._blend;
|
|
|
|
_palette = new PaletteInfo();
|
|
|
|
|
2016-08-26 18:17:04 +02:00
|
|
|
debugC(1, kDebugLoading, "Frame. action: %d transType: %d transDuration: %d", _actionId, _transType, _transDuration);
|
|
|
|
|
2016-10-28 22:48:28 +02:00
|
|
|
_sprites.resize(CHANNEL_COUNT + 1);
|
2016-08-23 09:41:00 +02:00
|
|
|
|
2016-10-28 22:48:28 +02:00
|
|
|
for (uint16 i = 0; i < CHANNEL_COUNT + 1; i++) {
|
2016-08-23 09:41:00 +02:00
|
|
|
_sprites[i] = new Sprite(*frame._sprites[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Frame::~Frame() {
|
|
|
|
delete _palette;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Frame::readChannel(Common::SeekableSubReadStreamEndian &stream, uint16 offset, uint16 size) {
|
|
|
|
if (offset >= 32) {
|
|
|
|
if (size <= 16)
|
|
|
|
readSprite(stream, offset, size);
|
|
|
|
else {
|
2016-09-01 10:52:11 +02:00
|
|
|
// read > 1 sprites channel
|
2016-08-23 09:41:00 +02:00
|
|
|
while (size > 16) {
|
|
|
|
byte spritePosition = (offset - 32) / 16;
|
|
|
|
uint16 nextStart = (spritePosition + 1) * 16 + 32;
|
|
|
|
uint16 needSize = nextStart - offset;
|
|
|
|
readSprite(stream, offset, needSize);
|
|
|
|
offset += needSize;
|
|
|
|
size -= needSize;
|
|
|
|
}
|
|
|
|
readSprite(stream, offset, size);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
readMainChannels(stream, offset, size);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-28 22:48:28 +02:00
|
|
|
void Frame::readChannels(Common::ReadStreamEndian *stream) {
|
2017-01-09 09:46:51 +11:00
|
|
|
byte unk[16];
|
2016-10-28 22:48:28 +02:00
|
|
|
|
2017-01-09 09:46:51 +11:00
|
|
|
if (_vm->getVersion() < 4) {
|
|
|
|
_actionId = stream->readByte();
|
|
|
|
_soundType1 = stream->readByte(); // type: 0x17 for sounds (sound is cast id), 0x16 for MIDI (sound is cmd id)
|
|
|
|
uint8 transFlags = stream->readByte(); // 0x80 is whole stage (vs changed area), rest is duration in 1/4ths of a second
|
2016-10-28 22:48:28 +02:00
|
|
|
|
2017-01-09 09:46:51 +11:00
|
|
|
if (transFlags & 0x80)
|
|
|
|
_transArea = 1;
|
|
|
|
else
|
|
|
|
_transArea = 0;
|
|
|
|
_transDuration = transFlags & 0x7f;
|
|
|
|
|
|
|
|
_transChunkSize = stream->readByte();
|
|
|
|
_tempo = stream->readByte();
|
|
|
|
_transType = static_cast<TransitionType>(stream->readByte());
|
|
|
|
_sound1 = stream->readUint16();
|
|
|
|
if (_vm->getPlatform() == Common::kPlatformMacintosh) {
|
|
|
|
_sound2 = stream->readUint16();
|
|
|
|
_soundType2 = stream->readByte();
|
|
|
|
} else {
|
|
|
|
stream->read(unk, 3);
|
|
|
|
warning("unk1: %x unk2: %x unk3: %x", unk[0], unk[1], unk[2]);
|
|
|
|
}
|
|
|
|
_skipFrameFlag = stream->readByte();
|
|
|
|
_blend = stream->readByte();
|
2016-10-28 22:48:28 +02:00
|
|
|
|
2017-01-09 09:46:51 +11:00
|
|
|
if (_vm->getPlatform() != Common::kPlatformMacintosh) {
|
|
|
|
_sound2 = stream->readUint16();
|
|
|
|
_soundType2 = stream->readByte();
|
|
|
|
}
|
2016-10-28 22:48:28 +02:00
|
|
|
|
2017-01-09 09:46:51 +11:00
|
|
|
uint16 palette = stream->readUint16();
|
2016-10-28 22:48:28 +02:00
|
|
|
|
2017-01-09 09:46:51 +11:00
|
|
|
if (palette) {
|
|
|
|
warning("STUB: Palette info");
|
|
|
|
}
|
|
|
|
|
|
|
|
debugC(kDebugLoading, 8, "%d %d %d %d %d %d %d %d %d %d %d", _actionId, _soundType1, _transDuration, _transChunkSize, _tempo, _transType, _sound1, _skipFrameFlag, _blend, _sound2, _soundType2);
|
|
|
|
|
|
|
|
_palette = new PaletteInfo();
|
|
|
|
_palette->firstColor = stream->readByte(); // for cycles. note: these start at 0x80 (for pal entry 0)!
|
|
|
|
_palette->lastColor = stream->readByte();
|
|
|
|
_palette->flags = stream->readByte();
|
|
|
|
_palette->speed = stream->readByte();
|
|
|
|
_palette->frameCount = stream->readUint16();
|
|
|
|
|
|
|
|
_palette->cycleCount = stream->readUint16();
|
|
|
|
} else if (_vm->getVersion() < 5) {
|
|
|
|
stream->read(unk, 16);
|
|
|
|
_actionId = stream->readUint16();
|
|
|
|
stream->read(unk, 5);
|
|
|
|
}
|
2016-10-28 22:48:28 +02:00
|
|
|
|
|
|
|
|
2016-10-28 22:56:15 +02:00
|
|
|
stream->read(unk, 6);
|
2016-10-28 22:48:28 +02:00
|
|
|
|
|
|
|
if (_vm->getPlatform() == Common::kPlatformMacintosh) {
|
2016-10-28 22:56:15 +02:00
|
|
|
if (_vm->getVersion() < 4) {
|
|
|
|
stream->read(unk, 3);
|
|
|
|
} else {
|
|
|
|
stream->read(unk, 11);
|
|
|
|
//Common::hexdump(unk, 11);
|
|
|
|
|
|
|
|
if (_vm->getVersion() >= 5) {
|
|
|
|
stream->read(unk, 7);
|
|
|
|
//Common::hexdump(unk, 7);
|
|
|
|
}
|
2016-10-28 22:48:28 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0; i < CHANNEL_COUNT; i++) {
|
|
|
|
Sprite &sprite = *_sprites[i + 1];
|
|
|
|
|
2017-01-09 09:46:51 +11:00
|
|
|
sprite._scriptId = stream->readByte();
|
|
|
|
sprite._spriteType = stream->readByte();
|
|
|
|
sprite._enabled = sprite._spriteType != 0;
|
2016-10-28 22:48:28 +02:00
|
|
|
sprite._x2 = stream->readUint16();
|
|
|
|
|
|
|
|
sprite._flags = stream->readUint16();
|
|
|
|
sprite._ink = static_cast<InkType>(sprite._flags & 0x3f);
|
|
|
|
|
|
|
|
if (sprite._flags & 0x40)
|
|
|
|
sprite._trails = 1;
|
|
|
|
else
|
|
|
|
sprite._trails = 0;
|
|
|
|
|
|
|
|
sprite._castId = stream->readUint16();
|
|
|
|
sprite._startPoint.y = stream->readUint16();
|
|
|
|
sprite._startPoint.x = stream->readUint16();
|
|
|
|
sprite._height = stream->readUint16();
|
|
|
|
sprite._width = stream->readUint16();
|
|
|
|
|
|
|
|
if (_vm->getPlatform() == Common::kPlatformMacintosh && _vm->getVersion() >= 4) {
|
|
|
|
sprite._scriptId = stream->readUint16();
|
|
|
|
sprite._flags2 = stream->readByte(); // 0x40 editable, 0x80 moveable
|
|
|
|
sprite._unk2 = stream->readByte();
|
|
|
|
|
|
|
|
if (_vm->getVersion() >= 5)
|
|
|
|
sprite._unk3 = stream->readUint32();
|
|
|
|
}
|
2017-01-09 09:46:51 +11:00
|
|
|
|
|
|
|
debugC(kDebugLoading, 8, "%03d(%d)[%x,%x,%04x,%d/%d/%d/%d] script:%d",
|
|
|
|
sprite._castId, sprite._enabled, sprite._x1, sprite._x2, sprite._flags,
|
|
|
|
sprite._startPoint.x, sprite._startPoint.y, sprite._width, sprite._height, sprite._scriptId);
|
2016-10-28 22:48:28 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-23 09:41:00 +02:00
|
|
|
void Frame::readMainChannels(Common::SeekableSubReadStreamEndian &stream, uint16 offset, uint16 size) {
|
|
|
|
uint16 finishPosition = offset + size;
|
|
|
|
|
|
|
|
while (offset < finishPosition) {
|
|
|
|
switch(offset) {
|
|
|
|
case kScriptIdPosition:
|
|
|
|
_actionId = stream.readByte();
|
|
|
|
offset++;
|
|
|
|
break;
|
|
|
|
case kSoundType1Position:
|
|
|
|
_soundType1 = stream.readByte();
|
|
|
|
offset++;
|
|
|
|
break;
|
|
|
|
case kTransFlagsPosition: {
|
2016-09-01 10:52:11 +02:00
|
|
|
uint8 transFlags = stream.readByte();
|
|
|
|
if (transFlags & 0x80)
|
|
|
|
_transArea = 1;
|
|
|
|
else
|
|
|
|
_transArea = 0;
|
|
|
|
_transDuration = transFlags & 0x7f;
|
|
|
|
offset++;
|
2016-08-23 09:41:00 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case kTransChunkSizePosition:
|
|
|
|
_transChunkSize = stream.readByte();
|
|
|
|
offset++;
|
|
|
|
break;
|
|
|
|
case kTempoPosition:
|
|
|
|
_tempo = stream.readByte();
|
|
|
|
offset++;
|
|
|
|
break;
|
|
|
|
case kTransTypePosition:
|
|
|
|
_transType = static_cast<TransitionType>(stream.readByte());
|
|
|
|
offset++;
|
|
|
|
break;
|
|
|
|
case kSound1Position:
|
|
|
|
_sound1 = stream.readUint16();
|
|
|
|
offset+=2;
|
|
|
|
break;
|
|
|
|
case kSkipFrameFlagsPosition:
|
|
|
|
_skipFrameFlag = stream.readByte();
|
|
|
|
offset++;
|
|
|
|
break;
|
|
|
|
case kBlendPosition:
|
|
|
|
_blend = stream.readByte();
|
|
|
|
offset++;
|
|
|
|
break;
|
|
|
|
case kSound2Position:
|
|
|
|
_sound2 = stream.readUint16();
|
|
|
|
offset += 2;
|
|
|
|
break;
|
|
|
|
case kSound2TypePosition:
|
|
|
|
_soundType2 = stream.readByte();
|
|
|
|
offset += 1;
|
|
|
|
break;
|
|
|
|
case kPaletePosition:
|
|
|
|
if (stream.readUint16())
|
|
|
|
readPaletteInfo(stream);
|
|
|
|
offset += 16;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
offset++;
|
|
|
|
stream.readByte();
|
2016-08-30 18:21:34 +02:00
|
|
|
debugC(kDebugLoading, "Frame::readMainChannels: Field Position %d, Finish Position %d", offset, finishPosition);
|
2016-08-23 09:41:00 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2016-10-28 22:01:37 +02:00
|
|
|
|
|
|
|
warning("%d %d %d %d %d %d %d %d %d %d %d", _actionId, _soundType1, _transDuration, _transChunkSize, _tempo, _transType, _sound1, _skipFrameFlag, _blend, _sound2, _soundType2);
|
2016-08-23 09:41:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void Frame::readPaletteInfo(Common::SeekableSubReadStreamEndian &stream) {
|
|
|
|
_palette->firstColor = stream.readByte();
|
|
|
|
_palette->lastColor = stream.readByte();
|
|
|
|
_palette->flags = stream.readByte();
|
|
|
|
_palette->speed = stream.readByte();
|
|
|
|
_palette->frameCount = stream.readUint16();
|
|
|
|
stream.skip(8); //unknown
|
|
|
|
}
|
|
|
|
|
|
|
|
void Frame::readSprite(Common::SeekableSubReadStreamEndian &stream, uint16 offset, uint16 size) {
|
|
|
|
uint16 spritePosition = (offset - 32) / 16;
|
|
|
|
uint16 spriteStart = spritePosition * 16 + 32;
|
|
|
|
|
|
|
|
uint16 fieldPosition = offset - spriteStart;
|
|
|
|
uint16 finishPosition = fieldPosition + size;
|
|
|
|
|
|
|
|
Sprite &sprite = *_sprites[spritePosition];
|
2016-10-28 22:01:37 +02:00
|
|
|
int x1 = 0;
|
|
|
|
int x2 = 0;
|
2016-08-23 09:41:00 +02:00
|
|
|
|
|
|
|
while (fieldPosition < finishPosition) {
|
|
|
|
switch (fieldPosition) {
|
|
|
|
case kSpritePositionUnk1:
|
2016-10-28 22:01:37 +02:00
|
|
|
x1 = stream.readByte();
|
2016-08-23 09:41:00 +02:00
|
|
|
fieldPosition++;
|
|
|
|
break;
|
|
|
|
case kSpritePositionEnabled:
|
|
|
|
sprite._enabled = (stream.readByte() != 0);
|
|
|
|
fieldPosition++;
|
|
|
|
break;
|
|
|
|
case kSpritePositionUnk2:
|
2016-10-28 22:01:37 +02:00
|
|
|
x2 = stream.readUint16();
|
2016-08-23 09:41:00 +02:00
|
|
|
fieldPosition += 2;
|
|
|
|
break;
|
|
|
|
case kSpritePositionFlags:
|
|
|
|
sprite._flags = stream.readUint16();
|
|
|
|
sprite._ink = static_cast<InkType>(sprite._flags & 0x3f);
|
|
|
|
|
|
|
|
if (sprite._flags & 0x40)
|
|
|
|
sprite._trails = 1;
|
|
|
|
else
|
|
|
|
sprite._trails = 0;
|
|
|
|
|
|
|
|
fieldPosition += 2;
|
|
|
|
break;
|
|
|
|
case kSpritePositionCastId:
|
|
|
|
sprite._castId = stream.readUint16();
|
|
|
|
fieldPosition += 2;
|
|
|
|
break;
|
|
|
|
case kSpritePositionY:
|
|
|
|
sprite._startPoint.y = stream.readUint16();
|
|
|
|
fieldPosition += 2;
|
|
|
|
break;
|
|
|
|
case kSpritePositionX:
|
|
|
|
sprite._startPoint.x = stream.readUint16();
|
|
|
|
fieldPosition += 2;
|
|
|
|
break;
|
|
|
|
case kSpritePositionWidth:
|
|
|
|
sprite._width = stream.readUint16();
|
|
|
|
fieldPosition += 2;
|
|
|
|
break;
|
|
|
|
case kSpritePositionHeight:
|
|
|
|
sprite._height = stream.readUint16();
|
|
|
|
fieldPosition += 2;
|
|
|
|
break;
|
|
|
|
default:
|
2016-09-01 10:52:11 +02:00
|
|
|
// end of channel, go to next sprite channel
|
2016-08-23 09:41:00 +02:00
|
|
|
readSprite(stream, spriteStart + 16, finishPosition - fieldPosition);
|
|
|
|
fieldPosition = finishPosition;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2016-10-28 22:01:37 +02:00
|
|
|
warning("%03d(%d)[%x,%x,%04x,%d/%d/%d/%d]", sprite._castId, sprite._enabled, x1, x2, sprite._flags, sprite._startPoint.x, sprite._startPoint.y, sprite._width, sprite._height);
|
|
|
|
|
2016-08-23 09:41:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void Frame::prepareFrame(Score *score) {
|
2017-01-09 09:46:51 +11:00
|
|
|
_drawRects.clear();
|
2016-08-23 09:41:00 +02:00
|
|
|
renderSprites(*score->_surface, false);
|
|
|
|
renderSprites(*score->_trailSurface, true);
|
|
|
|
|
|
|
|
if (_transType != 0)
|
2016-09-01 10:52:11 +02:00
|
|
|
//T ODO Handle changing area case
|
2016-08-23 09:41:00 +02:00
|
|
|
playTransition(score);
|
|
|
|
|
|
|
|
if (_sound1 != 0 || _sound2 != 0) {
|
|
|
|
playSoundChannel();
|
|
|
|
}
|
|
|
|
|
|
|
|
g_system->copyRectToScreen(score->_surface->getPixels(), score->_surface->pitch, 0, 0, score->_surface->getBounds().width(), score->_surface->getBounds().height());
|
|
|
|
}
|
|
|
|
|
|
|
|
void Frame::playSoundChannel() {
|
|
|
|
debug(0, "Sound1 %d", _sound1);
|
2016-09-01 10:52:11 +02:00
|
|
|
debug(0, "Sound2 %d", _sound2);
|
2016-08-23 09:41:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void Frame::playTransition(Score *score) {
|
|
|
|
uint16 duration = _transDuration * 250; // _transDuration in 1/4 of sec
|
2016-09-01 10:52:11 +02:00
|
|
|
duration = (duration == 0 ? 250 : duration); // director supports transition duration = 0, but animation play like value = 1, idk.
|
2016-08-23 09:41:00 +02:00
|
|
|
|
|
|
|
if (_transChunkSize == 0)
|
2016-09-01 10:52:11 +02:00
|
|
|
_transChunkSize = 1; // equal to 1 step
|
2016-08-23 09:41:00 +02:00
|
|
|
|
|
|
|
uint16 stepDuration = duration / _transChunkSize;
|
|
|
|
uint16 steps = duration / stepDuration;
|
|
|
|
|
|
|
|
switch (_transType) {
|
|
|
|
case kTransCoverDown:
|
|
|
|
{
|
|
|
|
uint16 stepSize = score->_movieRect.height() / steps;
|
|
|
|
Common::Rect r = score->_movieRect;
|
|
|
|
|
|
|
|
for (uint16 i = 1; i < steps; i++) {
|
|
|
|
r.setHeight(stepSize * i);
|
|
|
|
|
|
|
|
g_system->delayMillis(stepDuration);
|
|
|
|
score->processEvents();
|
|
|
|
|
|
|
|
g_system->copyRectToScreen(score->_surface->getPixels(), score->_surface->pitch, 0, 0, r.width(), r.height());
|
|
|
|
g_system->updateScreen();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case kTransCoverUp:
|
|
|
|
{
|
|
|
|
uint16 stepSize = score->_movieRect.height() / steps;
|
|
|
|
Common::Rect r = score->_movieRect;
|
|
|
|
|
|
|
|
for (uint16 i = 1; i < steps; i++) {
|
|
|
|
r.setHeight(stepSize * i);
|
|
|
|
|
|
|
|
g_system->delayMillis(stepDuration);
|
|
|
|
score->processEvents();
|
|
|
|
|
|
|
|
g_system->copyRectToScreen(score->_surface->getPixels(), score->_surface->pitch, 0, score->_movieRect.height() - stepSize * i, r.width(), r.height());
|
|
|
|
g_system->updateScreen();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case kTransCoverRight: {
|
|
|
|
uint16 stepSize = score->_movieRect.width() / steps;
|
|
|
|
Common::Rect r = score->_movieRect;
|
|
|
|
|
|
|
|
for (uint16 i = 1; i < steps; i++) {
|
|
|
|
r.setWidth(stepSize * i);
|
|
|
|
|
|
|
|
g_system->delayMillis(stepDuration);
|
|
|
|
score->processEvents();
|
|
|
|
|
|
|
|
g_system->copyRectToScreen(score->_surface->getPixels(), score->_surface->pitch, 0, 0, r.width(), r.height());
|
|
|
|
g_system->updateScreen();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case kTransCoverLeft: {
|
|
|
|
uint16 stepSize = score->_movieRect.width() / steps;
|
|
|
|
Common::Rect r = score->_movieRect;
|
|
|
|
|
|
|
|
for (uint16 i = 1; i < steps; i++) {
|
|
|
|
r.setWidth(stepSize * i);
|
|
|
|
|
|
|
|
g_system->delayMillis(stepDuration);
|
|
|
|
score->processEvents();
|
|
|
|
|
|
|
|
g_system->copyRectToScreen(score->_surface->getPixels(), score->_surface->pitch, score->_movieRect.width() - stepSize * i, 0, r.width(), r.height());
|
|
|
|
g_system->updateScreen();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case kTransCoverUpLeft: {
|
|
|
|
uint16 stepSize = score->_movieRect.width() / steps;
|
|
|
|
Common::Rect r = score->_movieRect;
|
|
|
|
|
|
|
|
for (uint16 i = 1; i < steps; i++) {
|
|
|
|
r.setWidth(stepSize * i);
|
|
|
|
r.setHeight(stepSize * i);
|
|
|
|
|
|
|
|
g_system->delayMillis(stepDuration);
|
|
|
|
score->processEvents();
|
|
|
|
|
|
|
|
g_system->copyRectToScreen(score->_surface->getPixels(), score->_surface->pitch, score->_movieRect.width() - stepSize * i, score->_movieRect.height() - stepSize * i, r.width(), r.height());
|
|
|
|
g_system->updateScreen();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case kTransCoverUpRight: {
|
|
|
|
uint16 stepSize = score->_movieRect.width() / steps;
|
|
|
|
Common::Rect r = score->_movieRect;
|
|
|
|
|
|
|
|
for (uint16 i = 1; i < steps; i++) {
|
|
|
|
r.setWidth(stepSize * i);
|
|
|
|
r.setHeight(stepSize * i);
|
|
|
|
|
|
|
|
g_system->delayMillis(stepDuration);
|
|
|
|
score->processEvents();
|
|
|
|
|
|
|
|
g_system->copyRectToScreen(score->_surface->getPixels(), score->_surface->pitch, 0, score->_movieRect.height() - stepSize * i, r.width(), r.height());
|
|
|
|
g_system->updateScreen();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case kTransCoverDownLeft: {
|
|
|
|
uint16 stepSize = score->_movieRect.width() / steps;
|
|
|
|
Common::Rect r = score->_movieRect;
|
|
|
|
|
|
|
|
for (uint16 i = 1; i < steps; i++) {
|
|
|
|
r.setWidth(stepSize * i);
|
|
|
|
r.setHeight(stepSize * i);
|
|
|
|
|
|
|
|
g_system->delayMillis(stepDuration);
|
|
|
|
score->processEvents();
|
|
|
|
|
|
|
|
g_system->copyRectToScreen(score->_surface->getPixels(), score->_surface->pitch, score->_movieRect.width() - stepSize * i, 0, r.width(), r.height());
|
|
|
|
g_system->updateScreen();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case kTransCoverDownRight: {
|
|
|
|
uint16 stepSize = score->_movieRect.width() / steps;
|
|
|
|
Common::Rect r = score->_movieRect;
|
|
|
|
|
|
|
|
for (uint16 i = 1; i < steps; i++) {
|
|
|
|
r.setWidth(stepSize * i);
|
|
|
|
r.setHeight(stepSize * i);
|
|
|
|
|
|
|
|
g_system->delayMillis(stepDuration);
|
|
|
|
score->processEvents();
|
|
|
|
|
|
|
|
g_system->copyRectToScreen(score->_surface->getPixels(), score->_surface->pitch, 0, 0, r.width(), r.height());
|
|
|
|
g_system->updateScreen();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
warning("Unhandled transition type %d %d %d", _transType, duration, _transChunkSize);
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Frame::renderSprites(Graphics::ManagedSurface &surface, bool renderTrail) {
|
|
|
|
for (uint16 i = 0; i < CHANNEL_COUNT; i++) {
|
|
|
|
if (_sprites[i]->_enabled) {
|
|
|
|
if ((_sprites[i]->_trails == 0 && renderTrail) || (_sprites[i]->_trails == 1 && !renderTrail))
|
|
|
|
continue;
|
|
|
|
|
2017-01-09 09:46:51 +11:00
|
|
|
CastType castType;
|
2016-08-23 09:41:00 +02:00
|
|
|
Cast *cast;
|
2017-01-09 09:46:51 +11:00
|
|
|
if (_vm->getVersion() < 4) {
|
|
|
|
switch (_sprites[i]->_spriteType) {
|
|
|
|
case 0x01:
|
|
|
|
castType = kCastBitmap;
|
|
|
|
break;
|
|
|
|
case 0x02:
|
2017-01-10 23:13:45 +11:00
|
|
|
case 0x0c: //this is actually a mouse-over shape? I don't think it's a real button.
|
2017-01-09 09:46:51 +11:00
|
|
|
castType = kCastShape;
|
|
|
|
break;
|
|
|
|
case 0x07:
|
|
|
|
castType = kCastText;
|
|
|
|
break;
|
2016-08-23 09:41:00 +02:00
|
|
|
}
|
|
|
|
} else {
|
2017-01-09 09:46:51 +11:00
|
|
|
if (!_vm->_currentScore->_casts.contains(_sprites[i]->_castId)) {
|
|
|
|
if (!_vm->getSharedCasts()->contains(_sprites[i]->_castId)) {
|
|
|
|
warning("Cast id %d not found", _sprites[i]->_castId);
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
warning("Getting cast id %d from shared cast", _sprites[i]->_castId);
|
|
|
|
cast = _vm->getSharedCasts()->getVal(_sprites[i]->_castId);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
cast = _vm->_currentScore->_casts[_sprites[i]->_castId];
|
|
|
|
}
|
|
|
|
castType = cast->type;
|
2016-08-23 09:41:00 +02:00
|
|
|
}
|
|
|
|
|
2017-01-09 09:46:51 +11:00
|
|
|
//this needs precedence to be hit first... D3 does something really tricky with cast IDs for shapes.
|
|
|
|
//I don't like this implementation 100% as the 'cast' above might not actually hit a member and be null?
|
|
|
|
if (castType == kCastShape) {
|
|
|
|
renderShape(surface, i);
|
|
|
|
} else if (castType == kCastText) {
|
|
|
|
renderText(surface, i, _vm->getVersion() < 4 ? _sprites[i]->_castId + 1024 : cast->children[0].index);
|
|
|
|
} else if (castType == kCastButton) {
|
|
|
|
renderButton(surface, i, _vm->getVersion() < 4 ? _sprites[i]->_castId + 1024 : cast->children[0].index);
|
|
|
|
} else {
|
|
|
|
Image::ImageDecoder *img = getImageFrom(_sprites[i]->_castId);
|
2016-08-23 09:41:00 +02:00
|
|
|
|
2017-01-09 09:46:51 +11:00
|
|
|
if (!img) {
|
|
|
|
warning("Image with id %d not found", _sprites[i]->_castId);
|
|
|
|
continue;
|
|
|
|
}
|
2016-08-23 09:41:00 +02:00
|
|
|
|
2017-01-09 09:46:51 +11:00
|
|
|
if (!img->getSurface()) {
|
|
|
|
warning("Frame::renderSprites: Could not load image %d", _sprites[i]->_castId);
|
|
|
|
continue;
|
|
|
|
}
|
2016-08-23 09:41:00 +02:00
|
|
|
|
2017-01-09 09:46:51 +11:00
|
|
|
assert(_sprites[i]->_cast);
|
|
|
|
|
|
|
|
uint32 regX = static_cast<BitmapCast *>(_sprites[i]->_cast)->regX;
|
|
|
|
uint32 regY = static_cast<BitmapCast *>(_sprites[i]->_cast)->regY;
|
|
|
|
uint32 rectLeft = static_cast<BitmapCast *>(_sprites[i]->_cast)->initialRect.left;
|
|
|
|
uint32 rectTop = static_cast<BitmapCast *>(_sprites[i]->_cast)->initialRect.top;
|
|
|
|
|
|
|
|
int x = _sprites[i]->_startPoint.x - regX + rectLeft;
|
|
|
|
int y = _sprites[i]->_startPoint.y - regY + rectTop;
|
|
|
|
int height = _sprites[i]->_height;
|
|
|
|
int width = _sprites[i]->_width;
|
|
|
|
|
|
|
|
Common::Rect drawRect = Common::Rect(x, y, x + width, y + height);
|
|
|
|
_drawRects[i] = drawRect;
|
|
|
|
|
|
|
|
switch (_sprites[i]->_ink) {
|
|
|
|
case kInkTypeCopy:
|
|
|
|
surface.blitFrom(*img->getSurface(), Common::Point(x, y));
|
|
|
|
break;
|
|
|
|
case kInkTypeTransparent:
|
|
|
|
// FIXME: is it always white (last entry in pallette)?
|
|
|
|
surface.transBlitFrom(*img->getSurface(), Common::Point(x, y), _vm->getPaletteColorCount() - 1);
|
|
|
|
break;
|
|
|
|
case kInkTypeBackgndTrans:
|
|
|
|
drawBackgndTransSprite(surface, *img->getSurface(), drawRect);
|
|
|
|
break;
|
|
|
|
case kInkTypeMatte:
|
|
|
|
drawMatteSprite(surface, *img->getSurface(), drawRect);
|
|
|
|
break;
|
|
|
|
case kInkTypeGhost:
|
|
|
|
drawGhostSprite(surface, *img->getSurface(), drawRect);
|
|
|
|
break;
|
|
|
|
case kInkTypeReverse:
|
|
|
|
drawReverseSprite(surface, *img->getSurface(), drawRect);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
warning("Unhandled ink type %d", _sprites[i]->_ink);
|
|
|
|
surface.blitFrom(*img->getSurface(), Common::Point(x, y));
|
|
|
|
break;
|
|
|
|
}
|
2016-08-23 09:41:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-09 09:46:51 +11:00
|
|
|
void Frame::renderShape(Graphics::ManagedSurface &surface, uint16 spriteID) {
|
|
|
|
Common::Rect r = Common::Rect(_sprites[spriteID]->_startPoint.x,
|
|
|
|
_sprites[spriteID]->_startPoint.y,
|
|
|
|
_sprites[spriteID]->_startPoint.x + _sprites[spriteID]->_width,
|
|
|
|
_sprites[spriteID]->_startPoint.y + _sprites[spriteID]->_height);
|
|
|
|
|
|
|
|
Graphics::Surface tmpSurface;
|
|
|
|
tmpSurface.create(r.width(), r.height(), Graphics::PixelFormat::createFormatCLUT8());
|
|
|
|
|
|
|
|
tmpSurface.fillRect(Common::Rect(r.width(), r.height()), 0);
|
|
|
|
|
|
|
|
switch (_sprites[spriteID]->_ink) {
|
|
|
|
case kInkTypeCopy:
|
|
|
|
surface.blitFrom(tmpSurface, Common::Point(_sprites[spriteID]->_startPoint.x, _sprites[spriteID]->_startPoint.y));
|
|
|
|
break;
|
|
|
|
case kInkTypeTransparent:
|
|
|
|
// FIXME: is it always white (last entry in pallette)?
|
|
|
|
surface.transBlitFrom(tmpSurface, Common::Point(_sprites[spriteID]->_startPoint.x, _sprites[spriteID]->_startPoint.y), _vm->getPaletteColorCount() - 1);
|
|
|
|
break;
|
|
|
|
case kInkTypeBackgndTrans:
|
|
|
|
drawBackgndTransSprite(surface, tmpSurface, r);
|
|
|
|
break;
|
|
|
|
case kInkTypeMatte:
|
|
|
|
drawMatteSprite(surface, tmpSurface, r);
|
|
|
|
break;
|
|
|
|
case kInkTypeGhost:
|
|
|
|
drawGhostSprite(surface, tmpSurface, r);
|
|
|
|
break;
|
|
|
|
case kInkTypeReverse:
|
|
|
|
drawReverseSprite(surface, tmpSurface, r);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
warning("Unhandled ink type %d", _sprites[spriteID]->_ink);
|
|
|
|
surface.blitFrom(tmpSurface, Common::Point(_sprites[spriteID]->_startPoint.x, _sprites[spriteID]->_startPoint.y));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
_drawRects[spriteID] = r;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Frame::renderButton(Graphics::ManagedSurface &surface, uint16 spriteId, uint16 textId) {
|
|
|
|
renderText(surface, spriteId, _vm->getMainArchive()->getResource(MKTAG('S', 'T', 'X', 'T'), textId), true);
|
2016-08-23 09:41:00 +02:00
|
|
|
|
|
|
|
uint16 castID = _sprites[spriteId]->_castId;
|
|
|
|
ButtonCast *button = static_cast<ButtonCast *>(_vm->_currentScore->_casts[castID]);
|
|
|
|
|
|
|
|
uint32 rectLeft = button->initialRect.left;
|
|
|
|
uint32 rectTop = button->initialRect.top;
|
|
|
|
|
|
|
|
int x = _sprites[spriteId]->_startPoint.x + rectLeft;
|
|
|
|
int y = _sprites[spriteId]->_startPoint.y + rectTop;
|
|
|
|
int height = _sprites[spriteId]->_height;
|
|
|
|
int width = _sprites[spriteId]->_width;
|
|
|
|
|
2017-01-09 09:46:51 +11:00
|
|
|
Common::Rect _rect;
|
|
|
|
|
2016-08-23 09:41:00 +02:00
|
|
|
switch (button->buttonType) {
|
|
|
|
case kTypeCheckBox:
|
2016-09-01 10:52:11 +02:00
|
|
|
// Magic numbers: checkbox square need to move left about 5px from text and 12px side size (D4)
|
2017-01-09 09:46:51 +11:00
|
|
|
_rect = Common::Rect(x - 17, y, x + 12, y + 12);
|
|
|
|
surface.frameRect(_rect, 0);
|
|
|
|
_drawRects[spriteId] = _rect;
|
2016-08-23 09:41:00 +02:00
|
|
|
break;
|
2017-01-11 11:25:31 +11:00
|
|
|
case kTypeButton: {
|
2017-01-11 16:50:11 +11:00
|
|
|
_rect = Common::Rect(x, y, x + width - 1, y + height + 5);
|
2017-01-11 11:25:31 +11:00
|
|
|
Graphics::MacPlotData pd(&surface, &_vm->getMacWindowManager()->getPatterns(), Graphics::MacGUIConstants::kPatternSolid, 1);
|
|
|
|
Graphics::drawRoundRect(_rect, 4, 0, false, Graphics::macDrawPixel, &pd);
|
|
|
|
_drawRects[spriteId] = _rect;
|
|
|
|
}
|
2016-08-23 09:41:00 +02:00
|
|
|
break;
|
|
|
|
case kTypeRadio:
|
|
|
|
warning("STUB: renderButton: kTypeRadio");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-24 22:39:44 +02:00
|
|
|
Image::ImageDecoder *Frame::getImageFrom(uint16 spriteId) {
|
2016-08-23 09:41:00 +02:00
|
|
|
uint16 imgId = spriteId + 1024;
|
2017-01-09 09:46:51 +11:00
|
|
|
|
|
|
|
if (_vm->getVersion() >= 4 && _vm->_currentScore->_casts[spriteId]->children.size() > 0)
|
|
|
|
imgId = _vm->_currentScore->_casts[spriteId]->children[0].index;
|
|
|
|
|
2016-08-23 09:41:00 +02:00
|
|
|
Image::ImageDecoder *img = NULL;
|
|
|
|
|
|
|
|
if (_vm->_currentScore->getArchive()->hasResource(MKTAG('D', 'I', 'B', ' '), imgId)) {
|
|
|
|
img = new DIBDecoder();
|
|
|
|
img->loadStream(*_vm->_currentScore->getArchive()->getResource(MKTAG('D', 'I', 'B', ' '), imgId));
|
|
|
|
return img;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_vm->getSharedDIB() != NULL && _vm->getSharedDIB()->contains(imgId)) {
|
|
|
|
img = new DIBDecoder();
|
|
|
|
img->loadStream(*_vm->getSharedDIB()->getVal(imgId));
|
|
|
|
return img;
|
|
|
|
}
|
|
|
|
|
2017-01-10 23:13:45 +11:00
|
|
|
Common::SeekableReadStream *pic = NULL;
|
|
|
|
BitmapCast *bc = NULL;
|
2016-08-24 22:39:44 +02:00
|
|
|
|
2017-01-10 23:13:45 +11:00
|
|
|
if (_vm->getSharedBMP() != NULL && _vm->getSharedBMP()->contains(imgId)) {
|
|
|
|
pic = _vm->getSharedBMP()->getVal(imgId);
|
|
|
|
bc = static_cast<BitmapCast *>(_vm->getSharedCasts()->getVal(spriteId));
|
|
|
|
} else if (_vm->_currentScore->getArchive()->hasResource(MKTAG('B', 'I', 'T', 'D'), imgId)) {
|
|
|
|
pic = _vm->_currentScore->getArchive()->getResource(MKTAG('B', 'I', 'T', 'D'), imgId);
|
|
|
|
bc = static_cast<BitmapCast *>(_vm->_currentScore->_casts[spriteId]);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pic != NULL && bc != NULL) {
|
2017-01-09 09:46:51 +11:00
|
|
|
if (_vm->getVersion() < 4) {
|
2016-08-24 22:39:44 +02:00
|
|
|
int w = bc->initialRect.width(), h = bc->initialRect.height();
|
|
|
|
|
|
|
|
debugC(2, kDebugImages, "id: %d, w: %d, h: %d, flags: %x, some: %x, unk1: %d, unk2: %d",
|
|
|
|
imgId, w, h, bc->flags, bc->someFlaggyThing, bc->unk1, bc->unk2);
|
2016-08-26 21:50:26 +02:00
|
|
|
img = new BITDDecoder(w, h);
|
2017-01-09 09:46:51 +11:00
|
|
|
} else if (_vm->getVersion() < 6) {
|
|
|
|
BitmapCast *bc = static_cast<BitmapCast *>(_vm->_currentScore->_casts[spriteId]);
|
|
|
|
int w = bc->initialRect.width(), h = bc->initialRect.height();
|
|
|
|
|
|
|
|
debugC(2, kDebugImages, "id: %d, w: %d, h: %d, flags: %x, some: %x, unk1: %d, unk2: %d",
|
|
|
|
imgId, w, h, bc->flags, bc->someFlaggyThing, bc->unk1, bc->unk2);
|
|
|
|
img = new BITDDecoderV4(w, h);
|
2016-08-23 09:41:00 +02:00
|
|
|
} else {
|
|
|
|
img = new Image::BitmapDecoder();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (debugChannelSet(8, kDebugLoading)) {
|
2016-08-24 22:39:44 +02:00
|
|
|
Common::SeekableReadStream *s = pic;
|
2016-08-23 09:41:00 +02:00
|
|
|
byte buf[1024];
|
|
|
|
int n = s->read(buf, 1024);
|
|
|
|
Common::hexdump(buf, n);
|
2017-01-09 09:46:51 +11:00
|
|
|
s->seek(0);
|
2016-08-23 09:41:00 +02:00
|
|
|
}
|
|
|
|
|
2016-08-24 22:39:44 +02:00
|
|
|
img->loadStream(*pic);
|
2016-08-23 09:41:00 +02:00
|
|
|
return img;
|
|
|
|
}
|
|
|
|
|
|
|
|
warning("Image %d not found", spriteId);
|
|
|
|
return img;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-01-09 09:46:51 +11:00
|
|
|
void Frame::renderText(Graphics::ManagedSurface &surface, uint16 spriteID, uint16 castID) {
|
|
|
|
Common::SeekableSubReadStreamEndian *textStream = NULL;
|
2016-08-23 09:41:00 +02:00
|
|
|
|
2017-01-09 09:46:51 +11:00
|
|
|
if (_vm->_currentScore->_movieArchive->hasResource(MKTAG('S', 'T', 'X', 'T'), castID)) {
|
|
|
|
textStream = _vm->_currentScore->_movieArchive->getResource(MKTAG('S', 'T', 'X', 'T'), castID);
|
|
|
|
} else if (_vm->getSharedSTXT() != nullptr) {
|
2016-08-23 09:41:00 +02:00
|
|
|
textStream = _vm->getSharedSTXT()->getVal(spriteID + 1024);
|
|
|
|
}
|
2017-01-09 09:46:51 +11:00
|
|
|
|
2017-01-10 23:13:45 +11:00
|
|
|
byte buf[1024];
|
|
|
|
textStream->read(buf, textStream->size());
|
|
|
|
textStream->seek(0);
|
|
|
|
Common::hexdump(buf, textStream->size());
|
|
|
|
|
2017-01-09 09:46:51 +11:00
|
|
|
renderText(surface, spriteID, textStream, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Frame::renderText(Graphics::ManagedSurface &surface, uint16 spriteID, Common::SeekableSubReadStreamEndian *textStream, bool isButtonLabel) {
|
|
|
|
if (textStream == NULL) return;
|
|
|
|
|
|
|
|
uint16 castID = _sprites[spriteID]->_castId;
|
|
|
|
TextCast *textCast = static_cast<TextCast *>(_vm->_currentScore->_casts[castID]);
|
|
|
|
|
2016-10-18 11:15:48 +02:00
|
|
|
uint32 unk1 = textStream->readUint32();
|
2016-08-23 09:41:00 +02:00
|
|
|
uint32 strLen = textStream->readUint32();
|
2016-10-18 11:15:48 +02:00
|
|
|
uint32 dataLen = textStream->readUint32();
|
2016-08-23 09:41:00 +02:00
|
|
|
Common::String text;
|
|
|
|
|
|
|
|
for (uint32 i = 0; i < strLen; i++) {
|
|
|
|
byte ch = textStream->readByte();
|
|
|
|
if (ch == 0x0d) {
|
|
|
|
ch = '\n';
|
|
|
|
}
|
|
|
|
text += ch;
|
|
|
|
}
|
|
|
|
|
2016-10-18 11:31:28 +02:00
|
|
|
debugC(3, kDebugText, "renderText: unk1: %d strLen: %d dataLen: %d textlen: %u", unk1, strLen, dataLen, text.size());
|
|
|
|
if (strLen < 200)
|
|
|
|
debugC(3, kDebugText, "text: '%s'", text.c_str());
|
2016-10-18 11:15:48 +02:00
|
|
|
|
2017-01-10 23:13:45 +11:00
|
|
|
if (_vm->getVersion() >= 4) {
|
|
|
|
uint16 a = textStream->readUint16();
|
|
|
|
uint32 b = textStream->readUint32();
|
|
|
|
uint16 c = textStream->readUint16();
|
|
|
|
uint16 d = textStream->readUint16();
|
|
|
|
textCast->fontId = textStream->readUint16();
|
|
|
|
textCast->textSlant = textStream->readByte();
|
|
|
|
textStream->readByte();
|
|
|
|
textCast->fontSize = textStream->readUint16();
|
|
|
|
|
|
|
|
textCast->palinfo1 = textStream->readUint16();
|
|
|
|
textCast->palinfo2 = textStream->readUint16();
|
|
|
|
textCast->palinfo3 = textStream->readUint16();
|
|
|
|
}
|
|
|
|
|
2017-01-11 11:25:31 +11:00
|
|
|
uint16 boxShadow = (uint16)textCast->boxShadow;
|
|
|
|
uint16 borderSize = (uint16)textCast->borderSize;
|
|
|
|
uint16 padding = (uint16)textCast->gutterSize;
|
|
|
|
uint16 textShadow = (uint16)textCast->textShadow;
|
|
|
|
|
2017-01-09 09:46:51 +11:00
|
|
|
uint32 rectLeft = textCast->initialRect.left;
|
|
|
|
uint32 rectTop = textCast->initialRect.top;
|
2016-08-23 09:41:00 +02:00
|
|
|
|
2017-01-11 16:50:11 +11:00
|
|
|
int x = _sprites[spriteID]->_startPoint.x; // +rectLeft;
|
|
|
|
int y = _sprites[spriteID]->_startPoint.y; // +rectTop;
|
|
|
|
|
|
|
|
int height = _sprites[spriteID]->_height;
|
|
|
|
if (_vm->getVersion() >= 4 && !isButtonLabel) height = textCast->initialRect.bottom;
|
|
|
|
height += textShadow;
|
|
|
|
|
2016-08-23 09:41:00 +02:00
|
|
|
int width = _sprites[spriteID]->_width;
|
2017-01-11 16:50:11 +11:00
|
|
|
if (_vm->getVersion() >= 4 && !isButtonLabel) width = textCast->initialRect.right;
|
2016-08-23 09:41:00 +02:00
|
|
|
|
2017-01-10 23:13:45 +11:00
|
|
|
Graphics::MacFont macFont(textCast->fontId, textCast->fontSize, textCast->textSlant);
|
2016-08-23 09:41:00 +02:00
|
|
|
|
|
|
|
if (_vm->_currentScore->_fontMap.contains(textCast->fontId)) {
|
2016-10-06 23:48:50 +02:00
|
|
|
// Override
|
|
|
|
macFont.setName(_vm->_currentScore->_fontMap[textCast->fontId]);
|
2016-08-23 09:41:00 +02:00
|
|
|
}
|
|
|
|
|
2016-10-06 23:48:50 +02:00
|
|
|
const Graphics::Font *font = _vm->_wm->_fontMan->getFont(macFont);
|
2016-08-23 09:41:00 +02:00
|
|
|
|
2016-10-18 11:31:28 +02:00
|
|
|
debugC(3, kDebugText, "renderText: x: %d y: %d w: %d h: %d font: '%s'", x, y, width, height, _vm->_wm->_fontMan->getFontName(macFont));
|
|
|
|
|
2017-01-10 23:13:45 +11:00
|
|
|
int alignment = (int)textCast->textAlign;
|
2017-01-11 11:25:31 +11:00
|
|
|
if (alignment == -1) alignment = 3;
|
2017-01-10 23:13:45 +11:00
|
|
|
else alignment++;
|
|
|
|
|
2017-01-11 16:50:11 +11:00
|
|
|
uint16 textX = x, textY = y;
|
|
|
|
if (!isButtonLabel) {
|
|
|
|
if (borderSize > 0) {
|
|
|
|
textX += (borderSize + 1);
|
|
|
|
textY += borderSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (padding > 0) {
|
|
|
|
width += padding * 2;
|
|
|
|
height += padding;
|
|
|
|
if (textCast->textAlign == kTextAlignLeft) textX += padding;
|
|
|
|
else if (textCast->textAlign == kTextAlignRight) textX -= (padding + 1);
|
|
|
|
//TODO: alignment issue with odd-size-width center-aligned text
|
|
|
|
//else if (textCast->textAlign == kTextAlignCenter && ((borderSize + padding) % 2 == 1)) textX--;
|
|
|
|
textY += padding / 2;
|
|
|
|
}
|
2017-01-10 23:13:45 +11:00
|
|
|
|
2017-01-11 16:50:11 +11:00
|
|
|
if (textShadow > 0) {
|
|
|
|
if (borderSize == 0) textX += 1;
|
|
|
|
font->drawString(&surface, text, textX + textShadow, textY + textShadow,
|
|
|
|
width, (_sprites[spriteID]->_ink == kInkTypeReverse ? 255 : 0), (Graphics::TextAlign)alignment);
|
|
|
|
height -= textShadow;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
textY += 2;
|
2017-01-10 23:13:45 +11:00
|
|
|
}
|
|
|
|
|
2017-01-09 09:46:51 +11:00
|
|
|
//TODO: the colour is wrong here... need to determine the correct colour for all versions!
|
2017-01-11 16:50:11 +11:00
|
|
|
font->drawString(&surface, text, textX, textY,
|
2017-01-10 23:13:45 +11:00
|
|
|
width, (_sprites[spriteID]->_ink == kInkTypeReverse ? 255 : 0), (Graphics::TextAlign)alignment);
|
2017-01-09 09:46:51 +11:00
|
|
|
|
|
|
|
if (isButtonLabel)
|
|
|
|
return;
|
2016-08-23 09:41:00 +02:00
|
|
|
|
2017-01-11 16:50:11 +11:00
|
|
|
uint16 borderX = x + borderSize - 1, borderY = y + borderSize - 1, borderHeight = height, borderWidth = width;
|
2017-01-10 23:13:45 +11:00
|
|
|
if (borderSize != kSizeNone) {
|
|
|
|
while (borderSize) {
|
2017-01-11 16:50:11 +11:00
|
|
|
borderWidth += 2;
|
|
|
|
borderHeight += 2;
|
|
|
|
surface.frameRect(Common::Rect(borderX, borderY, borderX + borderWidth, borderY + borderHeight), 0);
|
2017-01-10 23:13:45 +11:00
|
|
|
borderSize--;
|
2017-01-11 16:50:11 +11:00
|
|
|
borderX--;
|
|
|
|
borderY--;
|
2016-08-23 09:41:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-10 23:13:45 +11:00
|
|
|
if (boxShadow > 0) {
|
2017-01-11 16:50:11 +11:00
|
|
|
borderSize = (uint16)textCast->borderSize;
|
|
|
|
uint baseOffsetX = x + boxShadow;
|
|
|
|
uint baseOffsetY = y + height + (borderSize * 2);
|
|
|
|
uint sideOffsetX = x + borderWidth;
|
|
|
|
uint sideOffsetY = y + boxShadow;
|
|
|
|
while (boxShadow) {
|
|
|
|
surface.drawLine(baseOffsetX, baseOffsetY + (boxShadow - 1), baseOffsetX + borderWidth - 1, baseOffsetY + (boxShadow - 1), 0);
|
|
|
|
surface.drawLine(sideOffsetX + (boxShadow - 1), sideOffsetY, sideOffsetX + (boxShadow - 1), sideOffsetY + borderHeight - 1, 0);
|
|
|
|
boxShadow--;
|
2016-08-23 09:41:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Frame::drawBackgndTransSprite(Graphics::ManagedSurface &target, const Graphics::Surface &sprite, Common::Rect &drawRect) {
|
2016-09-01 10:52:11 +02:00
|
|
|
uint8 skipColor = _vm->getPaletteColorCount() - 1; // FIXME is it always white (last entry in pallette) ?
|
2016-08-23 09:41:00 +02:00
|
|
|
|
|
|
|
for (int ii = 0; ii < sprite.h; ii++) {
|
|
|
|
const byte *src = (const byte *)sprite.getBasePtr(0, ii);
|
|
|
|
byte *dst = (byte *)target.getBasePtr(drawRect.left, drawRect.top + ii);
|
|
|
|
|
|
|
|
for (int j = 0; j < drawRect.width(); j++) {
|
|
|
|
if (*src != skipColor)
|
|
|
|
*dst = *src;
|
|
|
|
|
|
|
|
src++;
|
|
|
|
dst++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Frame::drawGhostSprite(Graphics::ManagedSurface &target, const Graphics::Surface &sprite, Common::Rect &drawRect) {
|
|
|
|
uint8 skipColor = _vm->getPaletteColorCount() - 1;
|
|
|
|
for (int ii = 0; ii < sprite.h; ii++) {
|
|
|
|
const byte *src = (const byte *)sprite.getBasePtr(0, ii);
|
|
|
|
byte *dst = (byte *)target.getBasePtr(drawRect.left, drawRect.top + ii);
|
|
|
|
|
|
|
|
for (int j = 0; j < drawRect.width(); j++) {
|
|
|
|
if ((getSpriteIDFromPos(Common::Point(drawRect.left + j, drawRect.top + ii)) != 0) && (*src != skipColor))
|
2016-09-01 10:52:11 +02:00
|
|
|
*dst = (_vm->getPaletteColorCount() - 1) - *src; // Oposite color
|
2016-08-23 09:41:00 +02:00
|
|
|
|
|
|
|
src++;
|
|
|
|
dst++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Frame::drawReverseSprite(Graphics::ManagedSurface &target, const Graphics::Surface &sprite, Common::Rect &drawRect) {
|
|
|
|
uint8 skipColor = _vm->getPaletteColorCount() - 1;
|
|
|
|
for (int ii = 0; ii < sprite.h; ii++) {
|
|
|
|
const byte *src = (const byte *)sprite.getBasePtr(0, ii);
|
|
|
|
byte *dst = (byte *)target.getBasePtr(drawRect.left, drawRect.top + ii);
|
|
|
|
|
|
|
|
for (int j = 0; j < drawRect.width(); j++) {
|
|
|
|
if ((getSpriteIDFromPos(Common::Point(drawRect.left + j, drawRect.top + ii)) != 0))
|
|
|
|
*dst = (_vm->getPaletteColorCount() - 1) - *src;
|
|
|
|
else if (*src != skipColor)
|
|
|
|
*dst = *src;
|
|
|
|
src++;
|
|
|
|
dst++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Frame::drawMatteSprite(Graphics::ManagedSurface &target, const Graphics::Surface &sprite, Common::Rect &drawRect) {
|
2016-08-27 12:10:56 +02:00
|
|
|
// Like background trans, but all white pixels NOT ENCLOSED by coloured pixels are transparent
|
2016-08-23 09:41:00 +02:00
|
|
|
Graphics::Surface tmp;
|
|
|
|
tmp.copyFrom(sprite);
|
|
|
|
|
|
|
|
// Searching white color in the corners
|
|
|
|
int whiteColor = -1;
|
|
|
|
|
|
|
|
for (int corner = 0; corner < 4; corner++) {
|
|
|
|
int x = (corner & 0x1) ? tmp.w - 1 : 0;
|
|
|
|
int y = (corner & 0x2) ? tmp.h - 1 : 0;
|
|
|
|
|
|
|
|
byte color = *(byte *)tmp.getBasePtr(x, y);
|
|
|
|
|
|
|
|
if (_vm->getPalette()[color * 3 + 0] == 0xff &&
|
|
|
|
_vm->getPalette()[color * 3 + 1] == 0xff &&
|
|
|
|
_vm->getPalette()[color * 3 + 2] == 0xff) {
|
|
|
|
whiteColor = color;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (whiteColor == -1) {
|
2016-08-27 12:15:00 +02:00
|
|
|
debugC(1, kDebugImages, "No white color for Matte image");
|
2016-08-23 09:41:00 +02:00
|
|
|
|
2016-08-27 12:15:00 +02:00
|
|
|
for (int yy = 0; yy < tmp.h; yy++) {
|
|
|
|
const byte *src = (const byte *)tmp.getBasePtr(0, yy);
|
|
|
|
byte *dst = (byte *)target.getBasePtr(drawRect.left, drawRect.top + yy);
|
2016-08-23 09:41:00 +02:00
|
|
|
|
2016-08-27 12:15:00 +02:00
|
|
|
for (int xx = 0; xx < drawRect.width(); xx++, src++, dst++)
|
|
|
|
*dst = *src;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Graphics::FloodFill ff(&tmp, whiteColor, 0, true);
|
2016-08-23 09:41:00 +02:00
|
|
|
|
2016-08-27 12:15:00 +02:00
|
|
|
for (int yy = 0; yy < tmp.h; yy++) {
|
|
|
|
ff.addSeed(0, yy);
|
|
|
|
ff.addSeed(tmp.w - 1, yy);
|
|
|
|
}
|
2016-08-23 09:41:00 +02:00
|
|
|
|
2016-08-27 12:15:00 +02:00
|
|
|
for (int xx = 0; xx < tmp.w; xx++) {
|
|
|
|
ff.addSeed(xx, 0);
|
|
|
|
ff.addSeed(xx, tmp.h - 1);
|
|
|
|
}
|
|
|
|
ff.fillMask();
|
2016-08-23 09:41:00 +02:00
|
|
|
|
2016-08-27 12:15:00 +02:00
|
|
|
for (int yy = 0; yy < tmp.h; yy++) {
|
|
|
|
const byte *src = (const byte *)tmp.getBasePtr(0, yy);
|
|
|
|
const byte *mask = (const byte *)ff.getMask()->getBasePtr(0, yy);
|
|
|
|
byte *dst = (byte *)target.getBasePtr(drawRect.left, drawRect.top + yy);
|
|
|
|
|
|
|
|
for (int xx = 0; xx < drawRect.width(); xx++, src++, dst++, mask++)
|
|
|
|
if (*mask == 0)
|
|
|
|
*dst = *src;
|
|
|
|
}
|
2016-08-23 09:41:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
tmp.free();
|
|
|
|
}
|
|
|
|
|
|
|
|
uint16 Frame::getSpriteIDFromPos(Common::Point pos) {
|
2016-09-01 10:52:11 +02:00
|
|
|
// Find first from top to bottom
|
2017-01-09 09:46:51 +11:00
|
|
|
//TODO: THIS NEEDS TO BE REVERSED!
|
|
|
|
for (Common::HashMap<uint16, Common::Rect>::const_iterator dr = _drawRects.begin(); dr != _drawRects.end(); dr++)
|
|
|
|
if (dr->_value.contains(pos))
|
|
|
|
return dr->_key;
|
2016-08-23 09:41:00 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-09-01 10:52:11 +02:00
|
|
|
} // End of namespace Director
|