/* 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. * */ /* * This file is based on WME Lite. * http://dead-code.org/redir.php?target=wmelite * Copyright (c) 2011 Jan Nedoma */ #include "engines/wintermute/base/base_parser.h" #include "engines/wintermute/base/base_sub_frame.h" #include "engines/wintermute/base/base_active_rect.h" #include "engines/wintermute/base/base_dynamic_buffer.h" #include "engines/wintermute/base/gfx/base_surface.h" #include "engines/wintermute/base/base_surface_storage.h" #include "engines/wintermute/base/base_game.h" #include "engines/wintermute/base/base_engine.h" #include "engines/wintermute/base/gfx/base_renderer.h" #include "engines/wintermute/base/scriptables/script_value.h" #include "engines/wintermute/base/scriptables/script_stack.h" #include "graphics/transform_tools.h" #include "graphics/transform_struct.h" namespace Wintermute { IMPLEMENT_PERSISTENT(BaseSubFrame, false) ////////////////////////////////////////////////////////////////////////// BaseSubFrame::BaseSubFrame(BaseGame *inGame) : BaseScriptable(inGame, true) { _surface = nullptr; _hotspotX = Graphics::kDefaultHotspotX; _hotspotY = Graphics::kDefaultHotspotY; _alpha = Graphics::kDefaultRgbaMod; _transparent = 0xFFFF00FF; _wantsDefaultRect = false; _rect.setEmpty(); _editorSelected = false; _surfaceFilename = nullptr; _cKDefault = true; _cKRed = _cKBlue = _cKGreen = 0; _lifeTime = -1; _keepLoaded = false; _2DOnly = _3DOnly = false; _decoration = false; _mirrorX = _mirrorY = false; } ////////////////////////////////////////////////////////////////////////// BaseSubFrame::~BaseSubFrame() { if (_surface) { _gameRef->_surfaceStorage->removeSurface(_surface); } delete[] _surfaceFilename; _surfaceFilename = nullptr; } TOKEN_DEF_START TOKEN_DEF(IMAGE) TOKEN_DEF(TRANSPARENT) TOKEN_DEF(RECT) TOKEN_DEF(HOTSPOT) TOKEN_DEF(2D_ONLY) TOKEN_DEF(3D_ONLY) TOKEN_DEF(DECORATION) TOKEN_DEF(ALPHA_COLOR) TOKEN_DEF(ALPHA) TOKEN_DEF(MIRROR_X) TOKEN_DEF(MIRROR_Y) TOKEN_DEF(EDITOR_SELECTED) TOKEN_DEF(EDITOR_PROPERTY) TOKEN_DEF_END ////////////////////////////////////////////////////////////////////// bool BaseSubFrame::loadBuffer(char *buffer, int lifeTime, bool keepLoaded) { TOKEN_TABLE_START(commands) TOKEN_TABLE(IMAGE) TOKEN_TABLE(TRANSPARENT) TOKEN_TABLE(RECT) TOKEN_TABLE(HOTSPOT) TOKEN_TABLE(2D_ONLY) TOKEN_TABLE(3D_ONLY) TOKEN_TABLE(DECORATION) TOKEN_TABLE(ALPHA_COLOR) TOKEN_TABLE(ALPHA) TOKEN_TABLE(MIRROR_X) TOKEN_TABLE(MIRROR_Y) TOKEN_TABLE(EDITOR_SELECTED) TOKEN_TABLE(EDITOR_PROPERTY) TOKEN_TABLE_END char *params; int cmd; BaseParser parser; Rect32 rect; int r = 255, g = 255, b = 255; int ar = 255, ag = 255, ab = 255, alpha = 255; bool custoTrans = false; rect.setEmpty(); char *surfaceFile = nullptr; delete _surface; _surface = nullptr; while ((cmd = parser.getCommand(&buffer, commands, ¶ms)) > 0) { switch (cmd) { case TOKEN_IMAGE: surfaceFile = params; break; case TOKEN_TRANSPARENT: parser.scanStr(params, "%d,%d,%d", &r, &g, &b); custoTrans = true; break; case TOKEN_RECT: parser.scanStr(params, "%d,%d,%d,%d", &rect.left, &rect.top, &rect.right, &rect.bottom); break; case TOKEN_HOTSPOT: parser.scanStr(params, "%d,%d", &_hotspotX, &_hotspotY); break; case TOKEN_2D_ONLY: parser.scanStr(params, "%b", &_2DOnly); break; case TOKEN_3D_ONLY: parser.scanStr(params, "%b", &_3DOnly); break; case TOKEN_MIRROR_X: parser.scanStr(params, "%b", &_mirrorX); break; case TOKEN_MIRROR_Y: parser.scanStr(params, "%b", &_mirrorY); break; case TOKEN_DECORATION: parser.scanStr(params, "%b", &_decoration); break; case TOKEN_ALPHA_COLOR: parser.scanStr(params, "%d,%d,%d", &ar, &ag, &ab); break; case TOKEN_ALPHA: parser.scanStr(params, "%d", &alpha); break; case TOKEN_EDITOR_SELECTED: parser.scanStr(params, "%b", &_editorSelected); break; case TOKEN_EDITOR_PROPERTY: parseEditorProperty(params, false); break; } } if (cmd == PARSERR_TOKENNOTFOUND) { BaseEngine::LOG(0, "Syntax error in SUBFRAME definition"); return STATUS_FAILED; } if (surfaceFile != nullptr) { if (custoTrans) { setSurface(surfaceFile, false, r, g, b, lifeTime, keepLoaded); } else { setSurface(surfaceFile, true, 0, 0, 0, lifeTime, keepLoaded); } } _alpha = BYTETORGBA(ar, ag, ab, alpha); if (custoTrans) { _transparent = BYTETORGBA(r, g, b, 0xFF); } /* if (_surface == nullptr) { BaseEngine::LOG(0, "Error parsing sub-frame. Image not set."); return STATUS_FAILED; } */ if (rect.isRectEmpty()) { setDefaultRect(); } else { setRect(rect); } return STATUS_OK; } Rect32 BaseSubFrame::getRect() { if (_wantsDefaultRect && _surface) { _rect.setRect(0, 0, _surface->getWidth(), _surface->getHeight()); _wantsDefaultRect = false; } return _rect; } void BaseSubFrame::setRect(Rect32 rect) { _wantsDefaultRect = false; _rect = rect; } const char* BaseSubFrame::getSurfaceFilename() { return _surfaceFilename; } ////////////////////////////////////////////////////////////////////// bool BaseSubFrame::draw(int x, int y, BaseObject *registerOwner, float zoomX, float zoomY, bool precise, uint32 alpha, float rotate, Graphics::TSpriteBlendMode blendMode) { rotate = fmod(rotate, 360.0f); if (rotate < 0) { rotate += 360.0f; } if (!_surface) { return STATUS_OK; } if (registerOwner != nullptr && !_decoration) { if (zoomX == Graphics::kDefaultZoomX && zoomY == Graphics::kDefaultZoomY) { BaseEngine::getRenderer()->addRectToList(new BaseActiveRect(_gameRef, registerOwner, this, x - _hotspotX + getRect().left, y - _hotspotY + getRect().top, getRect().right - getRect().left, getRect().bottom - getRect().top, zoomX, zoomY, precise)); } else { BaseEngine::getRenderer()->addRectToList(new BaseActiveRect(_gameRef, registerOwner, this, (int)(x - (_hotspotX + getRect().left) * (zoomX / 100)), (int)(y - (_hotspotY + getRect().top) * (zoomY / 100)), (int)((getRect().right - getRect().left) * (zoomX / 100)), (int)((getRect().bottom - getRect().top) * (zoomY / 100)), zoomX, zoomY, precise)); } } if (_gameRef->getSuspendedRendering()) { return STATUS_OK; } bool res; //if (Alpha==0xFFFFFFFF) Alpha = _alpha; // TODO: better (combine owner's and self alpha) if (_alpha != Graphics::kDefaultRgbaMod) { alpha = _alpha; } if (rotate != Graphics::kDefaultAngle) { Point32 boxOffset, rotatedHotspot, hotspotOffset; Common::Point origin(x, y); Common::Point newOrigin; Rect32 oldRect1 = getRect(); Common::Rect oldRect(oldRect1.left, oldRect1.top, oldRect1.right, oldRect1.bottom); Common::Point newHotspot; Graphics::TransformStruct transform = Graphics::TransformStruct(zoomX, zoomY, (uint32)rotate, _hotspotX, _hotspotY, blendMode, alpha, _mirrorX, _mirrorY, 0, 0); Rect32 newRect = Graphics::TransformTools::newRect(oldRect, transform, &newHotspot); newOrigin = origin - newHotspot; res = _surface->displayTransform(newOrigin.x, newOrigin.y, oldRect, newRect, transform); } else { if (zoomX == Graphics::kDefaultZoomX && zoomY == Graphics::kDefaultZoomY) { res = _surface->displayTrans(x - _hotspotX, y - _hotspotY, getRect(), alpha, blendMode, _mirrorX, _mirrorY); } else { res = _surface->displayTransZoom((int)(x - _hotspotX * (zoomX / Graphics::kDefaultZoomX)), (int)(y - _hotspotY * (zoomY / Graphics::kDefaultZoomY)), getRect(), zoomX, zoomY, alpha, blendMode, _mirrorX, _mirrorY); } } return res; } ////////////////////////////////////////////////////////////////////////// bool BaseSubFrame::getBoundingRect(Rect32 *rect, int x, int y, float scaleX, float scaleY) { if (!rect) { return false; } float ratioX = scaleX / 100.0f; float ratioY = scaleY / 100.0f; rect->setRect((int)(x - _hotspotX * ratioX), (int)(y - _hotspotY * ratioY), (int)(x - _hotspotX * ratioX + (getRect().right - getRect().left) * ratioX), (int)(y - _hotspotY * ratioY + (getRect().bottom - getRect().top) * ratioY)); return true; } ////////////////////////////////////////////////////////////////////////// bool BaseSubFrame::saveAsText(BaseDynamicBuffer *buffer, int indent, bool complete) { if (complete) { buffer->putTextIndent(indent, "SUBFRAME {\n"); } if (_surface && _surface->getFileNameStr() != "") { buffer->putTextIndent(indent + 2, "IMAGE = \"%s\"\n", _surface->getFileName()); } if (_transparent != 0xFFFF00FF) { buffer->putTextIndent(indent + 2, "TRANSPARENT { %d,%d,%d }\n", RGBCOLGetR(_transparent), RGBCOLGetG(_transparent), RGBCOLGetB(_transparent)); } Rect32 rect; rect.setEmpty(); if (_surface) { rect.setRect(0, 0, _surface->getWidth(), _surface->getHeight()); } if (!(rect == getRect())) { buffer->putTextIndent(indent + 2, "RECT { %d,%d,%d,%d }\n", getRect().left, getRect().top, getRect().right, getRect().bottom); } if (_hotspotX != 0 || _hotspotY != 0) { buffer->putTextIndent(indent + 2, "HOTSPOT {%d, %d}\n", _hotspotX, _hotspotY); } if (_alpha != 0xFFFFFFFF) { buffer->putTextIndent(indent + 2, "ALPHA_COLOR { %d,%d,%d }\n", RGBCOLGetR(_alpha), RGBCOLGetG(_alpha), RGBCOLGetB(_alpha)); buffer->putTextIndent(indent + 2, "ALPHA = %d\n", RGBCOLGetA(_alpha)); } if (_mirrorX) { buffer->putTextIndent(indent + 2, "MIRROR_X=%s\n", _mirrorX ? "TRUE" : "FALSE"); } if (_mirrorY) { buffer->putTextIndent(indent + 2, "MIRROR_Y=%s\n", _mirrorY ? "TRUE" : "FALSE"); } if (_2DOnly) { buffer->putTextIndent(indent + 2, "2D_ONLY=%s\n", _2DOnly ? "TRUE" : "FALSE"); } if (_3DOnly) { buffer->putTextIndent(indent + 2, "3D_ONLY=%s\n", _3DOnly ? "TRUE" : "FALSE"); } if (_decoration) { buffer->putTextIndent(indent + 2, "DECORATION=%s\n", _decoration ? "TRUE" : "FALSE"); } if (_editorSelected) { buffer->putTextIndent(indent + 2, "EDITOR_SELECTED=%s\n", _editorSelected ? "TRUE" : "FALSE"); } BaseClass::saveAsText(buffer, indent + 2); if (complete) { buffer->putTextIndent(indent, "}\n\n"); } return STATUS_OK; } ////////////////////////////////////////////////////////////////////////// void BaseSubFrame::setDefaultRect() { if (_surface) { _wantsDefaultRect = true; } else { _wantsDefaultRect = false; _rect.setEmpty(); } } ////////////////////////////////////////////////////////////////////////// bool BaseSubFrame::persist(BasePersistenceManager *persistMgr) { BaseScriptable::persist(persistMgr); persistMgr->transferBool(TMEMBER(_2DOnly)); persistMgr->transferBool(TMEMBER(_3DOnly)); persistMgr->transferUint32(TMEMBER(_alpha)); persistMgr->transferBool(TMEMBER(_decoration)); persistMgr->transferBool(TMEMBER(_editorSelected)); persistMgr->transferSint32(TMEMBER(_hotspotX)); persistMgr->transferSint32(TMEMBER(_hotspotY)); persistMgr->transferRect32(TMEMBER(_rect)); persistMgr->transferBool(TMEMBER(_wantsDefaultRect)); persistMgr->transferCharPtr(TMEMBER(_surfaceFilename)); persistMgr->transferBool(TMEMBER(_cKDefault)); persistMgr->transferByte(TMEMBER(_cKRed)); persistMgr->transferByte(TMEMBER(_cKGreen)); persistMgr->transferByte(TMEMBER(_cKBlue)); persistMgr->transferSint32(TMEMBER(_lifeTime)); persistMgr->transferBool(TMEMBER(_keepLoaded)); persistMgr->transferBool(TMEMBER(_mirrorX)); persistMgr->transferBool(TMEMBER(_mirrorY)); persistMgr->transferUint32(TMEMBER(_transparent)); return STATUS_OK; } ////////////////////////////////////////////////////////////////////////// // high level scripting interface ////////////////////////////////////////////////////////////////////////// bool BaseSubFrame::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { ////////////////////////////////////////////////////////////////////////// // GetImage ////////////////////////////////////////////////////////////////////////// if (strcmp(name, "GetImage") == 0) { stack->correctParams(0); if (!_surfaceFilename) { stack->pushNULL(); } else { stack->pushString(_surfaceFilename); } return STATUS_OK; } ////////////////////////////////////////////////////////////////////////// // SetImage ////////////////////////////////////////////////////////////////////////// else if (strcmp(name, "SetImage") == 0) { stack->correctParams(1); ScValue *val = stack->pop(); if (val->isNULL()) { if (_surface) { _gameRef->_surfaceStorage->removeSurface(_surface); } delete[] _surfaceFilename; _surfaceFilename = nullptr; stack->pushBool(true); } else { const char *filename = val->getString(); if (DID_SUCCEED(setSurface(filename))) { setDefaultRect(); stack->pushBool(true); } else { stack->pushBool(false); } } return STATUS_OK; } else { return BaseScriptable::scCallMethod(script, stack, thisStack, name); } } ////////////////////////////////////////////////////////////////////////// ScValue *BaseSubFrame::scGetProperty(const Common::String &name) { if (!_scValue) { _scValue = new ScValue(_gameRef); } _scValue->setNULL(); ////////////////////////////////////////////////////////////////////////// // Type (RO) ////////////////////////////////////////////////////////////////////////// if (name == "Type") { _scValue->setString("subframe"); return _scValue; } ////////////////////////////////////////////////////////////////////////// // AlphaColor ////////////////////////////////////////////////////////////////////////// else if (name == "AlphaColor") { _scValue->setInt((int)_alpha); return _scValue; } ////////////////////////////////////////////////////////////////////////// // TransparentColor (RO) ////////////////////////////////////////////////////////////////////////// else if (name == "TransparentColor") { _scValue->setInt((int)_transparent); return _scValue; } ////////////////////////////////////////////////////////////////////////// // Is2DOnly ////////////////////////////////////////////////////////////////////////// else if (name == "Is2DOnly") { _scValue->setBool(_2DOnly); return _scValue; } ////////////////////////////////////////////////////////////////////////// // Is3DOnly ////////////////////////////////////////////////////////////////////////// else if (name == "Is3DOnly") { _scValue->setBool(_3DOnly); return _scValue; } ////////////////////////////////////////////////////////////////////////// // MirrorX ////////////////////////////////////////////////////////////////////////// else if (name == "MirrorX") { _scValue->setBool(_mirrorX); return _scValue; } ////////////////////////////////////////////////////////////////////////// // MirrorY ////////////////////////////////////////////////////////////////////////// else if (name == "MirrorY") { _scValue->setBool(_mirrorY); return _scValue; } ////////////////////////////////////////////////////////////////////////// // Decoration ////////////////////////////////////////////////////////////////////////// else if (name == "Decoration") { _scValue->setBool(_decoration); return _scValue; } ////////////////////////////////////////////////////////////////////////// // HotspotX ////////////////////////////////////////////////////////////////////////// else if (name == "HotspotX") { _scValue->setInt(_hotspotX); return _scValue; } ////////////////////////////////////////////////////////////////////////// // HotspotY ////////////////////////////////////////////////////////////////////////// else if (name == "HotspotY") { _scValue->setInt(_hotspotY); return _scValue; } else { return BaseScriptable::scGetProperty(name); } } ////////////////////////////////////////////////////////////////////////// bool BaseSubFrame::scSetProperty(const char *name, ScValue *value) { ////////////////////////////////////////////////////////////////////////// // AlphaColor ////////////////////////////////////////////////////////////////////////// if (strcmp(name, "AlphaColor") == 0) { _alpha = (uint32)value->getInt(); return STATUS_OK; } ////////////////////////////////////////////////////////////////////////// // Is2DOnly ////////////////////////////////////////////////////////////////////////// else if (strcmp(name, "Is2DOnly") == 0) { _2DOnly = value->getBool(); return STATUS_OK; } ////////////////////////////////////////////////////////////////////////// // Is3DOnly ////////////////////////////////////////////////////////////////////////// else if (strcmp(name, "Is3DOnly") == 0) { _3DOnly = value->getBool(); return STATUS_OK; } ////////////////////////////////////////////////////////////////////////// // MirrorX ////////////////////////////////////////////////////////////////////////// else if (strcmp(name, "MirrorX") == 0) { _mirrorX = value->getBool(); return STATUS_OK; } ////////////////////////////////////////////////////////////////////////// // MirrorY ////////////////////////////////////////////////////////////////////////// else if (strcmp(name, "MirrorY") == 0) { _mirrorY = value->getBool(); return STATUS_OK; } ////////////////////////////////////////////////////////////////////////// // Decoration ////////////////////////////////////////////////////////////////////////// else if (strcmp(name, "Decoration") == 0) { _decoration = value->getBool(); return STATUS_OK; } ////////////////////////////////////////////////////////////////////////// // HotspotX ////////////////////////////////////////////////////////////////////////// else if (strcmp(name, "HotspotX") == 0) { _hotspotX = value->getInt(); return STATUS_OK; } ////////////////////////////////////////////////////////////////////////// // HotspotY ////////////////////////////////////////////////////////////////////////// else if (strcmp(name, "HotspotY") == 0) { _hotspotY = value->getInt(); return STATUS_OK; } else { return BaseScriptable::scSetProperty(name, value); } } ////////////////////////////////////////////////////////////////////////// const char *BaseSubFrame::scToString() { return "[subframe]"; } ////////////////////////////////////////////////////////////////////////// bool BaseSubFrame::setSurface(const Common::String &filename, bool defaultCK, byte ckRed, byte ckGreen, byte ckBlue, int lifeTime, bool keepLoaded) { if (_surface) { _gameRef->_surfaceStorage->removeSurface(_surface); _surface = nullptr; } delete[] _surfaceFilename; _surfaceFilename = nullptr; _surface = _gameRef->_surfaceStorage->addSurface(filename, defaultCK, ckRed, ckGreen, ckBlue, lifeTime, keepLoaded); if (_surface) { _surfaceFilename = new char[filename.size() + 1]; strcpy(_surfaceFilename, filename.c_str()); _cKDefault = defaultCK; _cKRed = ckRed; _cKGreen = ckGreen; _cKBlue = ckBlue; _lifeTime = lifeTime; _keepLoaded = keepLoaded; return STATUS_OK; } else { return STATUS_FAILED; } } ////////////////////////////////////////////////////////////////////////// bool BaseSubFrame::setSurfaceSimple() { if (!_surfaceFilename) { _surface = nullptr; return STATUS_OK; } _surface = _gameRef->_surfaceStorage->addSurface(_surfaceFilename, _cKDefault, _cKRed, _cKGreen, _cKBlue, _lifeTime, _keepLoaded); if (_surface) { return STATUS_OK; } else { return STATUS_FAILED; } } Common::String BaseSubFrame::debuggerToString() const { return Common::String::format("%p: BaseSubFrame \"%s\" - Mirror:(%d, %d), Hotspot:(%d, %d), ", (const void *)this, getName(), _mirrorX, _mirrorY, _hotspotX, _hotspotY); } } // End of namespace Wintermute