Initial commit
This commit is contained in:
484
engines/nancy/misc/hypertext.cpp
Normal file
484
engines/nancy/misc/hypertext.cpp
Normal file
@@ -0,0 +1,484 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#include "common/tokenizer.h"
|
||||
|
||||
#include "engines/nancy/nancy.h"
|
||||
#include "engines/nancy/graphics.h"
|
||||
#include "engines/nancy/resource.h"
|
||||
|
||||
#include "engines/nancy/misc/hypertext.h"
|
||||
|
||||
namespace Nancy {
|
||||
namespace Misc {
|
||||
|
||||
struct MetaInfo {
|
||||
enum Type { kColor, kFont, kMark, kHotspot };
|
||||
|
||||
Type type;
|
||||
uint numChars;
|
||||
byte index;
|
||||
};
|
||||
|
||||
void HypertextParser::initSurfaces(uint width, uint height, const Graphics::PixelFormat &format, uint32 backgroundColor, uint32 highlightBackgroundColor) {
|
||||
_backgroundColor = backgroundColor;
|
||||
_highlightBackgroundColor = highlightBackgroundColor;
|
||||
_fullSurface.create(width, height, format);
|
||||
_fullSurface.clear(backgroundColor);
|
||||
_textHighlightSurface.create(width, height, format);
|
||||
_textHighlightSurface.clear(highlightBackgroundColor);
|
||||
}
|
||||
|
||||
void HypertextParser::addTextLine(const Common::String &text) {
|
||||
_textLines.push_back(text);
|
||||
_needsTextRedraw = true;
|
||||
}
|
||||
|
||||
void HypertextParser::addImage(uint16 lineID, const Common::Rect &src) {
|
||||
_imageLineIDs.push_back(lineID);
|
||||
_imageSrcs.push_back(src);
|
||||
}
|
||||
|
||||
void HypertextParser::setImageName(const Common::Path &name) {
|
||||
_imageName = name;
|
||||
}
|
||||
|
||||
void HypertextParser::drawAllText(const Common::Rect &textBounds, uint leftOffsetNonNewline, uint fontID, uint highlightFontID) {
|
||||
using namespace Common;
|
||||
|
||||
const Font *font = nullptr;
|
||||
const Font *highlightFont = nullptr;
|
||||
Graphics::ManagedSurface image;
|
||||
|
||||
_numDrawnLines = 0;
|
||||
|
||||
if (!_imageName.empty()) {
|
||||
g_nancy->_resource->loadImage(_imageName, image);
|
||||
}
|
||||
|
||||
for (uint lineID = 0; lineID < _textLines.size(); ++lineID) {
|
||||
Common::String currentLine;
|
||||
bool hasHotspot = false;
|
||||
Rect hotspot;
|
||||
Common::Queue<MetaInfo> metaInfo;
|
||||
Common::Queue<uint16> newlineTokens;
|
||||
newlineTokens.push(0);
|
||||
int curFontID = fontID;
|
||||
uint numNonSpaceChars = 0;
|
||||
|
||||
// Token braces plus invalid characters that are known to appear in strings
|
||||
Common::StringTokenizer tokenizer(_textLines[lineID], "<>\"");
|
||||
|
||||
Common::String curToken;
|
||||
bool reachedEndTag = false;
|
||||
while(!tokenizer.empty() && !reachedEndTag) {
|
||||
curToken = tokenizer.nextToken();
|
||||
|
||||
if (tokenizer.delimitersAtTokenBegin().lastChar() == '<' && tokenizer.delimitersAtTokenEnd().firstChar() == '>') {
|
||||
switch (curToken.firstChar()) {
|
||||
case 'i' :
|
||||
// CC begin
|
||||
// fall through
|
||||
case 'o' :
|
||||
// CC end
|
||||
if (curToken.size() != 1) {
|
||||
break;
|
||||
}
|
||||
|
||||
continue;
|
||||
case 'e' :
|
||||
// End conversation. Originally used for quickly ending dialogue when debugging, but
|
||||
// also marks the ending of the current text line.
|
||||
if (curToken.size() != 1) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Ignore the rest of the text. This fixes nancy7 scene 5770
|
||||
reachedEndTag = true;
|
||||
continue;
|
||||
case 'h' :
|
||||
// Hotspot
|
||||
if (curToken.size() != 1) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (hasHotspot) {
|
||||
// Replace duplicate hotspot token with a newline to copy the original behavior
|
||||
currentLine += '\n';
|
||||
}
|
||||
|
||||
hasHotspot = true;
|
||||
continue;
|
||||
case 'H' :
|
||||
// Hotspot inside list, begin
|
||||
if (curToken.size() != 1) {
|
||||
break;
|
||||
}
|
||||
|
||||
metaInfo.push({MetaInfo::kHotspot, numNonSpaceChars, 1});
|
||||
continue;
|
||||
case 'L' :
|
||||
// Hotspot inside list, end
|
||||
if (curToken.size() != 1) {
|
||||
break;
|
||||
}
|
||||
|
||||
metaInfo.push({MetaInfo::kHotspot, numNonSpaceChars, 0});
|
||||
continue;
|
||||
case 'n' :
|
||||
// Newline
|
||||
if (curToken.size() != 1) {
|
||||
break;
|
||||
}
|
||||
|
||||
currentLine += '\n';
|
||||
newlineTokens.push(numNonSpaceChars);
|
||||
continue;
|
||||
case 't' :
|
||||
// Tab
|
||||
if (curToken.size() != 1) {
|
||||
break;
|
||||
}
|
||||
|
||||
currentLine += '\t';
|
||||
continue;
|
||||
case 'c' :
|
||||
// Color tokens
|
||||
// We keep the positions (excluding spaces) and colors of the color tokens in a queue
|
||||
if (curToken.size() != 2) {
|
||||
break;
|
||||
}
|
||||
|
||||
metaInfo.push({MetaInfo::kColor, numNonSpaceChars, (byte)(curToken[1] - '0')});
|
||||
continue;
|
||||
case 'f' :
|
||||
// Font token
|
||||
// This selects a specific font ID for the following text
|
||||
if (curToken.size() != 2) {
|
||||
break;
|
||||
}
|
||||
|
||||
metaInfo.push({MetaInfo::kFont, numNonSpaceChars, (byte)(curToken[1] - '0')});
|
||||
continue;
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
// Mark token for Nancy 8 and later games. no-op for earlier games
|
||||
if (g_nancy->getGameType() <= kGameTypeNancy7) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (curToken.size() != 1) {
|
||||
break;
|
||||
}
|
||||
|
||||
metaInfo.push({MetaInfo::kMark, numNonSpaceChars, (byte)(curToken[0] - '1')});
|
||||
continue;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Ignore non-tokens when they're between braces. This fixes nancy6 scenes 1953 & 1954,
|
||||
// where some sound names slipped through into the text data.
|
||||
debugC(Nancy::kDebugHypertext, "Unrecognized hypertext tag <%s>", curToken.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Count the number of non-space characters. We use this to keep track
|
||||
// of where color changes should happen, since including whitespaces
|
||||
// presents a lot of edge cases when combined with word wrapping
|
||||
for (uint i = 0; i < curToken.size(); ++i) {
|
||||
if (curToken[i] != ' ') {
|
||||
++numNonSpaceChars;
|
||||
}
|
||||
}
|
||||
|
||||
currentLine += curToken;
|
||||
}
|
||||
|
||||
font = g_nancy->_graphics->getFont(curFontID);
|
||||
highlightFont = g_nancy->_graphics->getFont(highlightFontID);
|
||||
assert(font && highlightFont);
|
||||
|
||||
// Do word wrapping on the text, sans tokens. This assumes
|
||||
// all text uses fonts of the same width
|
||||
Array<Common::String> wrappedLines;
|
||||
font->wordWrap(currentLine, textBounds.width(), wrappedLines, 0);
|
||||
|
||||
// Setup most of the hotspot; textbox
|
||||
if (hasHotspot) {
|
||||
hotspot.left = textBounds.left;
|
||||
hotspot.top = textBounds.top + (_numDrawnLines * font->getFontHeight()) - 1;
|
||||
hotspot.setHeight(0);
|
||||
hotspot.setWidth(0);
|
||||
}
|
||||
|
||||
// Go through the wrapped lines and draw them, making sure to
|
||||
// respect color tokens
|
||||
uint totalCharsDrawn = 0;
|
||||
byte colorID = _defaultTextColor;
|
||||
uint numNewlineTokens = 0;
|
||||
uint horizontalOffset = 0;
|
||||
bool newLineStart = false;
|
||||
for (uint lineNumber = 0; lineNumber < wrappedLines.size(); ++lineNumber) {
|
||||
Common::String &line = wrappedLines[lineNumber];
|
||||
horizontalOffset = 0;
|
||||
newLineStart = false;
|
||||
|
||||
// Draw images
|
||||
if (newlineTokens.empty()) {
|
||||
warning("HypertextParser::drawAllText():: newlineTokens list was empty at line %u out of %u wrapped lines", lineNumber+1, wrappedLines.size());
|
||||
}
|
||||
|
||||
if (!newlineTokens.empty() && newlineTokens.front() <= totalCharsDrawn) {
|
||||
newlineTokens.pop();
|
||||
newLineStart = true;
|
||||
|
||||
for (uint i = 0; i < _imageLineIDs.size(); ++i) {
|
||||
if (numNewlineTokens == _imageLineIDs[i]) {
|
||||
// A lot of magic numbers that make sure we draw pixel-perfect. This is a mess for three reasons:
|
||||
// - The original engine draws strings with a bottom-left anchor, while ScummVM uses top-left
|
||||
// - The original engine uses inclusive rects, while ScummVM uses non-includive
|
||||
// - The original engine does some stupid stuff with spacing
|
||||
// This works correctly in nancy7, but might fail with different games/fonts
|
||||
if (lineNumber != 0) {
|
||||
_imageVerticalOffset += (font->getFontHeight() + 1) / 2 + 1;
|
||||
}
|
||||
|
||||
_fullSurface.blitFrom(image, _imageSrcs[i],
|
||||
Common::Point( textBounds.left + horizontalOffset + 1,
|
||||
textBounds.top + _numDrawnLines * highlightFont->getFontHeight() + _imageVerticalOffset));
|
||||
_imageVerticalOffset += _imageSrcs[i].height() - 1;
|
||||
|
||||
if (lineNumber == 0) {
|
||||
_imageVerticalOffset += font->getFontHeight() / 2 - 1;
|
||||
} else {
|
||||
_imageVerticalOffset += (font->getFontHeight() + 1) / 2 + 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
++numNewlineTokens;
|
||||
}
|
||||
|
||||
// Trim whitespaces (only) at beginning and end of wrapped lines
|
||||
while (line.lastChar() == ' ') {
|
||||
line.deleteLastChar();
|
||||
}
|
||||
|
||||
while (line.firstChar() == ' ') {
|
||||
line.deleteChar(0);
|
||||
}
|
||||
|
||||
bool newWrappedLine = true; // Used to ensure color/font changes don't mess up hotspots
|
||||
while (!line.empty()) {
|
||||
Common::String subLine;
|
||||
|
||||
while (metaInfo.size() && totalCharsDrawn >= metaInfo.front().numChars) {
|
||||
// We have a color/font change token, a hyperlink, or a mark at begginning of (what's left of) the current line
|
||||
MetaInfo change = metaInfo.pop();
|
||||
switch (change.type) {
|
||||
case MetaInfo::kFont:
|
||||
curFontID = change.index;
|
||||
font = g_nancy->_graphics->getFont(curFontID);
|
||||
break;
|
||||
case MetaInfo::kColor:
|
||||
colorID = change.index;
|
||||
break;
|
||||
case MetaInfo::kMark: {
|
||||
auto *mark = GetEngineData(MARK);
|
||||
assert(mark);
|
||||
|
||||
if (lineNumber == 0) {
|
||||
// A mark on the first line pushes up all text
|
||||
if (textBounds.top - _imageVerticalOffset > 3) {
|
||||
_imageVerticalOffset -= 3;
|
||||
} else {
|
||||
_imageVerticalOffset = -textBounds.top;
|
||||
}
|
||||
}
|
||||
|
||||
Common::Rect markSrc = mark->_markSrcs[change.index];
|
||||
Common::Rect markDest = markSrc;
|
||||
markDest.moveTo(textBounds.left + horizontalOffset + (newLineStart ? 0 : leftOffsetNonNewline) + 1,
|
||||
lineNumber == 0 ?
|
||||
textBounds.top - ((font->getFontHeight() + 1) / 2) + _imageVerticalOffset + 4 :
|
||||
textBounds.top + _numDrawnLines * font->getFontHeight() + _imageVerticalOffset - 4);
|
||||
|
||||
// For now we do not check if we need to go to new line; neither does the original
|
||||
_fullSurface.blitFrom(g_nancy->_graphics->_object0, markSrc, markDest);
|
||||
|
||||
horizontalOffset += markDest.width() + 2;
|
||||
break;
|
||||
}
|
||||
case MetaInfo::kHotspot:
|
||||
// List only
|
||||
hasHotspot = change.index;
|
||||
|
||||
if (hasHotspot) {
|
||||
hotspot.left = textBounds.left + (newLineStart ? 0 : horizontalOffset + leftOffsetNonNewline);
|
||||
hotspot.top = textBounds.top + _numDrawnLines * font->getFontHeight() + _imageVerticalOffset - 1;
|
||||
hotspot.setHeight(0);
|
||||
hotspot.setWidth(0);
|
||||
} else {
|
||||
_hotspots.push_back(hotspot);
|
||||
hotspot = { 0, 0, 0, 0 };
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint lineSizeNoSpace = 0;
|
||||
for (uint i = 0; i < line.size(); ++i) {
|
||||
if (!isSpace(line[i])) {
|
||||
++lineSizeNoSpace;
|
||||
}
|
||||
}
|
||||
|
||||
if (metaInfo.size() && totalCharsDrawn < metaInfo.front().numChars && metaInfo.front().numChars <= (totalCharsDrawn + lineSizeNoSpace)) {
|
||||
// There's a token inside the current line, so split off the part before it
|
||||
uint subSize = metaInfo.front().numChars - totalCharsDrawn;
|
||||
for (uint i = 0; i < subSize; ++i) {
|
||||
if (isSpace(line[i])) {
|
||||
++subSize;
|
||||
}
|
||||
}
|
||||
subLine = line.substr(0, subSize);
|
||||
line = line.substr(subLine.size());
|
||||
}
|
||||
|
||||
// Choose whether to draw the subLine, or the full line
|
||||
Common::String &stringToDraw = subLine.size() ? subLine : line;
|
||||
|
||||
// Draw the normal text
|
||||
font->drawString( &_fullSurface,
|
||||
stringToDraw,
|
||||
textBounds.left + horizontalOffset + (newLineStart ? 0 : leftOffsetNonNewline),
|
||||
textBounds.top + _numDrawnLines * font->getFontHeight() + _imageVerticalOffset,
|
||||
textBounds.width(),
|
||||
colorID);
|
||||
|
||||
// Then, draw the highlight
|
||||
if (hasHotspot && !_textHighlightSurface.empty()) {
|
||||
highlightFont->drawString( &_textHighlightSurface,
|
||||
stringToDraw,
|
||||
textBounds.left + horizontalOffset + (newLineStart ? leftOffsetNonNewline : 0),
|
||||
textBounds.top + _numDrawnLines * highlightFont->getFontHeight() + _imageVerticalOffset,
|
||||
textBounds.width(),
|
||||
colorID);
|
||||
}
|
||||
|
||||
// Count number of non-space characters drawn. Used for color.
|
||||
// Note that we use isSpace() specifically to exclude the tab character
|
||||
for (uint i = 0; i < stringToDraw.size(); ++i) {
|
||||
if (!isSpace(stringToDraw[i])) {
|
||||
++totalCharsDrawn;
|
||||
}
|
||||
}
|
||||
|
||||
// Add to the width/height of the hotspot
|
||||
if (hasHotspot) {
|
||||
hotspot.setWidth(MAX<int16>(hotspot.width(), font->getStringWidth(stringToDraw)));
|
||||
|
||||
if (!stringToDraw.empty() && newWrappedLine) {
|
||||
hotspot.setHeight(hotspot.height() + font->getFontHeight());
|
||||
}
|
||||
}
|
||||
|
||||
newWrappedLine = false;
|
||||
|
||||
if (subLine.size()) {
|
||||
horizontalOffset += font->getStringWidth(subLine);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
++_numDrawnLines;
|
||||
|
||||
// Record the height of the text currently drawn. Used for textbox scrolling
|
||||
_drawnTextHeight = (_numDrawnLines - 1) * font->getFontHeight() + _imageVerticalOffset;
|
||||
}
|
||||
|
||||
// Draw the footer image(s)
|
||||
for (uint i = 0; i < _imageLineIDs.size(); ++i) {
|
||||
if (numNewlineTokens <= _imageLineIDs[i]) {
|
||||
_imageVerticalOffset += (font->getFontHeight() + 1) / 2 + 1;
|
||||
|
||||
_fullSurface.blitFrom(image, _imageSrcs[i],
|
||||
Common::Point( textBounds.left + horizontalOffset + 1,
|
||||
textBounds.top + _numDrawnLines * highlightFont->getFontHeight() + _imageVerticalOffset));
|
||||
_imageVerticalOffset += _imageSrcs[i].height() - 1;
|
||||
|
||||
if (i < _imageLineIDs.size() - 1) {
|
||||
_imageVerticalOffset += (font->getFontHeight() + 1) / 2 + 3;
|
||||
}
|
||||
|
||||
_drawnTextHeight = (_numDrawnLines - 1) * font->getFontHeight() + _imageVerticalOffset;
|
||||
}
|
||||
}
|
||||
|
||||
// Add the hotspot to the list
|
||||
if (hasHotspot) {
|
||||
_hotspots.push_back(hotspot);
|
||||
}
|
||||
|
||||
// Note: disabled since it was most likely a bug, and is behavior exclusive to the textbox
|
||||
/*
|
||||
// Simulate a bug in the original engine where player text longer than
|
||||
// a single line gets a double newline afterwards
|
||||
if (wrappedLines.size() > 1 && hasHotspot) {
|
||||
++_numLines;
|
||||
|
||||
if (lineID == _textLines.size() - 1) {
|
||||
_lastResponseisMultiline = true;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// Add a newline after every full piece of text
|
||||
++_numDrawnLines;
|
||||
_drawnTextHeight += font->getFontHeight();
|
||||
}
|
||||
|
||||
// Add a line's height at end of text to replicate original behavior
|
||||
if (font) {
|
||||
_drawnTextHeight += font->getFontHeight();
|
||||
}
|
||||
|
||||
_needsTextRedraw = false;
|
||||
}
|
||||
|
||||
void HypertextParser::clear() {
|
||||
if (_textLines.size()) {
|
||||
_fullSurface.clear(_backgroundColor);
|
||||
_textHighlightSurface.clear(_highlightBackgroundColor);
|
||||
_textLines.clear();
|
||||
_hotspots.clear();
|
||||
_numDrawnLines = 0;
|
||||
_drawnTextHeight = 0;
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Misc
|
||||
} // End of namespace Nancy
|
||||
80
engines/nancy/misc/hypertext.h
Normal file
80
engines/nancy/misc/hypertext.h
Normal file
@@ -0,0 +1,80 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NANCY_MISC_HYPERTEXT_H
|
||||
#define NANCY_MISC_HYPERTEXT_H
|
||||
|
||||
#include "engines/nancy/renderobject.h"
|
||||
|
||||
namespace Nancy {
|
||||
namespace Misc {
|
||||
|
||||
// Base class for handling the engine's custom hypertext format
|
||||
// Used by the Textbox and by Autotext action records
|
||||
class HypertextParser {
|
||||
public:
|
||||
HypertextParser() :
|
||||
_backgroundColor(0),
|
||||
_highlightBackgroundColor(0),
|
||||
_numDrawnLines(0),
|
||||
_drawnTextHeight(0),
|
||||
_needsTextRedraw(false),
|
||||
_defaultTextColor(0),
|
||||
_imageVerticalOffset(0) {}
|
||||
virtual ~HypertextParser() {};
|
||||
|
||||
bool hasBeenDrawn() const { return !_needsTextRedraw; }
|
||||
|
||||
protected:
|
||||
void initSurfaces(uint width, uint height, const struct Graphics::PixelFormat &format, uint32 backgroundColor, uint32 highlightBackgroundColor);
|
||||
|
||||
void addTextLine(const Common::String &text);
|
||||
void addImage(uint16 lineID, const Common::Rect &src);
|
||||
void setImageName(const Common::Path &name);
|
||||
|
||||
void drawAllText(const Common::Rect &textBounds, uint leftOffsetNonNewline, uint fontID, uint highlightFontID);
|
||||
virtual void clear();
|
||||
|
||||
Graphics::ManagedSurface _fullSurface; // Contains all rendered text (may be cropped)
|
||||
Graphics::ManagedSurface _textHighlightSurface; // Same as above, but drawn with the highlight font
|
||||
|
||||
uint32 _backgroundColor;
|
||||
uint32 _highlightBackgroundColor;
|
||||
uint _defaultTextColor;
|
||||
int _imageVerticalOffset;
|
||||
|
||||
Common::Array<Common::String> _textLines;
|
||||
Common::Array<Common::Rect> _hotspots;
|
||||
|
||||
// Data for displaying images inside text; used in Hypertext
|
||||
Common::Path _imageName;
|
||||
Common::Array<uint16> _imageLineIDs;
|
||||
Common::Array<Common::Rect> _imageSrcs;
|
||||
|
||||
uint16 _numDrawnLines;
|
||||
uint16 _drawnTextHeight;
|
||||
bool _needsTextRedraw;
|
||||
};
|
||||
|
||||
} // End of namespace Misc
|
||||
} // End of namespace Nancy
|
||||
|
||||
#endif // NANCY_MISC_HYPERTEXT_H
|
||||
193
engines/nancy/misc/lightning.cpp
Normal file
193
engines/nancy/misc/lightning.cpp
Normal file
@@ -0,0 +1,193 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "engines/nancy/graphics.h"
|
||||
#include "engines/nancy/sound.h"
|
||||
#include "engines/nancy/nancy.h"
|
||||
|
||||
#include "engines/nancy/misc/lightning.h"
|
||||
|
||||
#include "engines/nancy/state/scene.h"
|
||||
|
||||
#include "common/stream.h"
|
||||
#include "common/random.h"
|
||||
|
||||
namespace Nancy {
|
||||
namespace Misc {
|
||||
|
||||
void editPalette(byte *colors, uint percent) {
|
||||
float alpha = (float) percent / 100;
|
||||
|
||||
for (int i = 0; i < 256 * 3; ++i) {
|
||||
uint16 origColor = colors[i];
|
||||
colors[i] = MIN<uint16>(alpha * origColor + origColor, 255);
|
||||
}
|
||||
}
|
||||
|
||||
void Lightning::beginLightning(int16 distance, uint16 pulseTime, int16 rgbPercent) {
|
||||
int16 midpoint;
|
||||
float delta;
|
||||
|
||||
// Calculate the min & max power of the lightning
|
||||
midpoint = (rgbPercent - (distance * 5));
|
||||
delta = 0.4 * midpoint;
|
||||
|
||||
_minRGBPercent = MAX<uint16>(0, midpoint - delta);
|
||||
_maxRGBPercent = MIN<uint16>(rgbPercent, midpoint + delta);
|
||||
|
||||
// Calculate the min & max delay between lightning strikes
|
||||
midpoint = 13000 - (pulseTime * 500);
|
||||
delta = 1.5 * midpoint;
|
||||
|
||||
_minInterPulseDelay = MAX<int16>(500, midpoint - delta);
|
||||
_maxInterPulseDelay = MIN<int16>(13000, midpoint + delta);
|
||||
|
||||
// Calculate the min & max length of the lightning strikes
|
||||
// _minPulseLength is always 5 due to an oversight in the original code
|
||||
_maxPulseLength = pulseTime * 10;
|
||||
|
||||
// Calculate the min & max delay between end of lightning and start of thunder sound
|
||||
midpoint = distance * 400;
|
||||
delta = midpoint * 0.4;
|
||||
|
||||
_minSoundStartDelay = MAX<int16>(250, midpoint - delta);
|
||||
_maxSoundStartDelay = midpoint + delta; // No minimum value, probably a bug
|
||||
|
||||
_state = kBegin;
|
||||
}
|
||||
|
||||
void Lightning::endLightning() {
|
||||
_state = kNotRunning;
|
||||
|
||||
_viewportObjs.clear();
|
||||
_viewportObjOriginalPalettes.clear();
|
||||
}
|
||||
|
||||
void Lightning::run() {
|
||||
switch (_state) {
|
||||
case kNotRunning: {
|
||||
// Check if the endgame has started
|
||||
if (NancySceneState.getEventFlag(82, g_nancy->_true)) {
|
||||
uint16 sceneID = NancySceneState.getSceneInfo().sceneID;
|
||||
|
||||
// Check if we're inside an appropriate scene
|
||||
if ((sceneID < 152) ||
|
||||
(sceneID > 177 && sceneID < 230) ||
|
||||
(sceneID > 230 && sceneID < 233) ||
|
||||
(sceneID > 235 && sceneID < 318) ||
|
||||
(sceneID > 326 && sceneID < 334) ||
|
||||
(sceneID > 341 && sceneID < 1726) ||
|
||||
(sceneID > 1731)) {
|
||||
|
||||
beginLightning(2, 22, 65);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case kBegin:
|
||||
g_nancy->_graphics->grabViewportObjects(_viewportObjs);
|
||||
|
||||
for (RenderObject *obj : _viewportObjs) {
|
||||
if (!obj) {
|
||||
continue;
|
||||
}
|
||||
|
||||
_viewportObjOriginalPalettes.push_back(new byte[256 * 3]);
|
||||
obj->grabPalette(_viewportObjOriginalPalettes.back());
|
||||
}
|
||||
|
||||
_state = kStartPulse;
|
||||
// fall through
|
||||
case kStartPulse:
|
||||
_nextStateTime = g_nancy->getTotalPlayTime() + g_nancy->_randomSource->getRandomNumberRngSigned(_minPulseLength, _maxPulseLength);
|
||||
handleThunder();
|
||||
handlePulse(true);
|
||||
_state = kPulse;
|
||||
break;
|
||||
case kPulse:
|
||||
if (g_nancy->getTotalPlayTime() > _nextStateTime) {
|
||||
|
||||
_nextStateTime = g_nancy->getTotalPlayTime() + g_nancy->_randomSource->getRandomNumberRngSigned(_minInterPulseDelay, _maxInterPulseDelay);
|
||||
|
||||
_state = kThunder;
|
||||
|
||||
if (!g_nancy->_sound->isSoundPlaying("TH1")) {
|
||||
_nextSoundToPlay = 0;
|
||||
_nextSoundTime0 = g_nancy->getTotalPlayTime() + g_nancy->_randomSource->getRandomNumberRngSigned(_minSoundStartDelay, _maxSoundStartDelay);
|
||||
} else if (!g_nancy->_sound->isSoundPlaying("TH2")) {
|
||||
_nextSoundToPlay = 1;
|
||||
_nextSoundTime1 = g_nancy->getTotalPlayTime() + g_nancy->_randomSource->getRandomNumberRngSigned(_minSoundStartDelay, _maxSoundStartDelay);
|
||||
} else {
|
||||
_nextSoundToPlay = -1;
|
||||
}
|
||||
|
||||
handlePulse(false);
|
||||
}
|
||||
|
||||
handleThunder();
|
||||
break;
|
||||
case kThunder:
|
||||
if (g_nancy->getTotalPlayTime() > _nextStateTime) {
|
||||
_state = kStartPulse;
|
||||
}
|
||||
|
||||
handleThunder();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Lightning::handlePulse(bool on) {
|
||||
for (uint i = 0; i < _viewportObjs.size(); ++i) {
|
||||
RenderObject *obj = _viewportObjs[i];
|
||||
|
||||
if (!obj) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (on) {
|
||||
byte newPalette[256 * 3];
|
||||
obj->grabPalette(newPalette);
|
||||
editPalette(newPalette, g_nancy->_randomSource->getRandomNumberRngSigned(_minRGBPercent, _maxRGBPercent));
|
||||
obj->setPalette(newPalette);
|
||||
} else {
|
||||
obj->setPalette(_viewportObjOriginalPalettes[i]);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void Lightning::handleThunder() {
|
||||
if (_nextSoundToPlay == 0) {
|
||||
if (g_nancy->getTotalPlayTime() > _nextSoundTime0) {
|
||||
g_nancy->_sound->playSound("TH1");
|
||||
_nextSoundToPlay = -1;
|
||||
}
|
||||
} else if (_nextSoundToPlay == 1) {
|
||||
if (g_nancy->getTotalPlayTime() > _nextSoundTime1) {
|
||||
g_nancy->_sound->playSound("TH2");
|
||||
_nextSoundToPlay = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Misc
|
||||
} // End of namespace Nancy
|
||||
73
engines/nancy/misc/lightning.h
Normal file
73
engines/nancy/misc/lightning.h
Normal file
@@ -0,0 +1,73 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NANCY_MISC_LIGHTNING_H
|
||||
#define NANCY_MISC_LIGHTNING_H
|
||||
|
||||
#include "engines/nancy/commontypes.h"
|
||||
|
||||
namespace Nancy {
|
||||
namespace Misc {
|
||||
|
||||
// Special class that handles The Vampire Diaries' lightning screen effect.
|
||||
// Activated by the LightningOn action record as well as at the endgame section
|
||||
class Lightning {
|
||||
public:
|
||||
enum LightningState { kBegin, kStartPulse, kPulse, kThunder, kNotRunning };
|
||||
|
||||
void beginLightning(int16 distance, uint16 pulseTime, int16 rgbPercent);
|
||||
void endLightning();
|
||||
|
||||
void run();
|
||||
|
||||
private:
|
||||
void handlePulse(bool on);
|
||||
void handleThunder();
|
||||
|
||||
//bool _isRunning = false;
|
||||
LightningState _state = kNotRunning;
|
||||
|
||||
int16 _minRGBPercent = 0;
|
||||
int16 _maxRGBPercent = 0;
|
||||
|
||||
int16 _minInterPulseDelay = 0;
|
||||
int16 _maxInterPulseDelay = 0;
|
||||
|
||||
int16 _minPulseLength = 5;
|
||||
int16 _maxPulseLength = 0;
|
||||
|
||||
int16 _minSoundStartDelay = 0;
|
||||
int16 _maxSoundStartDelay = 0;
|
||||
|
||||
uint32 _nextStateTime = 0;
|
||||
uint32 _nextSoundTime0 = 0;
|
||||
uint32 _nextSoundTime1 = 0;
|
||||
|
||||
int _nextSoundToPlay = 0;
|
||||
|
||||
Common::Array<RenderObject *> _viewportObjs;
|
||||
Common::Array<byte *> _viewportObjOriginalPalettes;
|
||||
};
|
||||
|
||||
} // End of namespace Misc
|
||||
} // End of namespace Nancy
|
||||
|
||||
#endif // NANCY_MISC_LIGHTNING_H
|
||||
70
engines/nancy/misc/mousefollow.cpp
Normal file
70
engines/nancy/misc/mousefollow.cpp
Normal file
@@ -0,0 +1,70 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "engines/nancy/misc/mousefollow.h"
|
||||
#include "engines/nancy/enginedata.h"
|
||||
#include "engines/nancy/nancy.h"
|
||||
#include "engines/nancy/graphics.h"
|
||||
|
||||
namespace Nancy {
|
||||
namespace Misc {
|
||||
|
||||
MouseFollowObject::MouseFollowObject()
|
||||
: RenderObject(8) {
|
||||
_viewportData = GetEngineData(VIEW);
|
||||
}
|
||||
|
||||
void MouseFollowObject::handleInput(NancyInput &input) {
|
||||
Common::Point mousePos = input.mousePos;
|
||||
Common::Rect viewport = _viewportData->screenPosition;
|
||||
|
||||
if (!_isPickedUp || !viewport.contains(mousePos)) {
|
||||
return;
|
||||
}
|
||||
|
||||
mousePos.x -= viewport.left;
|
||||
mousePos.y -= viewport.top;
|
||||
|
||||
// Move the tile under the cursor
|
||||
Common::Rect newScreenPos = _drawSurface.getBounds();
|
||||
newScreenPos.moveTo(mousePos);
|
||||
newScreenPos.translate(-newScreenPos.width() / 2, -newScreenPos.height() / 2);
|
||||
|
||||
// Clip movement so the ring stays entirely inside the viewport
|
||||
if (newScreenPos.left < 0) {
|
||||
newScreenPos.translate(-newScreenPos.left, 0);
|
||||
} else if (newScreenPos.right > viewport.width()) {
|
||||
newScreenPos.translate(viewport.width() - newScreenPos.right, 0);
|
||||
}
|
||||
|
||||
if (newScreenPos.top < 0) {
|
||||
newScreenPos.translate(0, -newScreenPos.top);
|
||||
} else if (newScreenPos.bottom > viewport.height()) {
|
||||
newScreenPos.translate(0, viewport.height() - newScreenPos.bottom);
|
||||
}
|
||||
|
||||
if (newScreenPos != _screenPosition) {
|
||||
moveTo(newScreenPos);
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Misc
|
||||
} // End of namespace Nancy
|
||||
65
engines/nancy/misc/mousefollow.h
Normal file
65
engines/nancy/misc/mousefollow.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NANCY_MISC_MOUSEFOLLOW_H
|
||||
#define NANCY_MISC_MOUSEFOLLOW_H
|
||||
|
||||
#include "engines/nancy/renderobject.h"
|
||||
#include "engines/nancy/input.h"
|
||||
|
||||
namespace Nancy {
|
||||
|
||||
struct VIEW;
|
||||
|
||||
namespace Misc {
|
||||
|
||||
// Describes an object that follows the mouse's movement, making
|
||||
// sure that its center stays above the mouse hotspot. The position
|
||||
// of such an object is clipped to the Viewport. Used in puzzles:
|
||||
// - AssemblyPuzzle
|
||||
// - CubePuzzle
|
||||
// - RippedLetterPuzzle
|
||||
// - TowerPuzzle
|
||||
// - TangramPuzzle
|
||||
class MouseFollowObject : public RenderObject {
|
||||
public:
|
||||
MouseFollowObject();
|
||||
virtual ~MouseFollowObject() {}
|
||||
|
||||
virtual void pickUp() { _isPickedUp = true; }
|
||||
virtual void putDown() { _isPickedUp = false; }
|
||||
|
||||
void setZ(uint16 z) { _z = z; _needsRedraw = true; }
|
||||
void handleInput(NancyInput &input);
|
||||
|
||||
protected:
|
||||
bool isViewportRelative() const override { return true; }
|
||||
|
||||
bool _isPickedUp = false;
|
||||
byte _rotation = 0;
|
||||
|
||||
const VIEW *_viewportData = nullptr;
|
||||
};
|
||||
|
||||
} // End of namespace Misc
|
||||
} // End of namespace Nancy
|
||||
|
||||
#endif // NANCY_MISC_MOUSEFOLLOW_H
|
||||
164
engines/nancy/misc/specialeffect.cpp
Normal file
164
engines/nancy/misc/specialeffect.cpp
Normal file
@@ -0,0 +1,164 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "engines/nancy/nancy.h"
|
||||
#include "engines/nancy/graphics.h"
|
||||
|
||||
#include "engines/nancy/misc/specialeffect.h"
|
||||
|
||||
namespace Nancy {
|
||||
namespace Misc {
|
||||
|
||||
void SpecialEffect::init() {
|
||||
if (g_nancy->getGameType() <= kGameTypeNancy6) {
|
||||
// nancy2-6 have a fixed number of frames for the effect, which is defined in the SPEC chunk
|
||||
auto *specialEffectData = GetEngineData(SPEC);
|
||||
assert(specialEffectData);
|
||||
|
||||
_numFrames = _type == kBlackout ? specialEffectData->fadeToBlackNumFrames : specialEffectData->crossDissolveNumFrames;
|
||||
_frameTime = _type == kBlackout ? specialEffectData->fadeToBlackFrameTime : _frameTime;
|
||||
|
||||
// We use the type definitions in nancy7, which are 1-indexed
|
||||
++_type;
|
||||
}
|
||||
|
||||
// nancy7 got rid of the SPEC chunk, and the data now contains the total amount of time
|
||||
// that the effect should run for instead.
|
||||
if (_rect.isEmpty()) {
|
||||
if (g_nancy->getGameType() <= kGameTypeNancy6 && _type == kCrossDissolve) {
|
||||
// Earlier games did the whole screen (most easily testable in the nancy3 intro if one moves the scrollbar)
|
||||
_rect = Common::Rect(640, 480);
|
||||
} else {
|
||||
const VIEW *viewportData = (const VIEW *)g_nancy->getEngineData("VIEW");
|
||||
assert(viewportData);
|
||||
|
||||
_rect = viewportData->screenPosition;
|
||||
}
|
||||
}
|
||||
|
||||
_drawSurface.create(_rect.width(), _rect.height(), g_nancy->_graphics->getScreenPixelFormat());
|
||||
moveTo(_rect);
|
||||
setTransparent(false);
|
||||
|
||||
RenderObject::init();
|
||||
}
|
||||
|
||||
void SpecialEffect::updateGraphics() {
|
||||
if (_numFrames) {
|
||||
// Early version with constant number of frames, linear interpolation
|
||||
if (g_nancy->getTotalPlayTime() > _nextFrameTime && _currentFrame < (int)_numFrames && isInitialized()) {
|
||||
++_currentFrame;
|
||||
_nextFrameTime += _frameTime;
|
||||
|
||||
GraphicsManager::crossDissolve(_fadeFrom, _fadeTo, 255 * _currentFrame / _numFrames, _rect, _drawSurface);
|
||||
setVisible(true);
|
||||
}
|
||||
} else {
|
||||
// nancy7+ version, draws as many frames as possible, ease in/out interpolation
|
||||
if (_startTime == 0) {
|
||||
_startTime = g_nancy->getTotalPlayTime();
|
||||
|
||||
// The original code times how long the first frame takes to draw,
|
||||
// divides the total time by that time, and uses the result to
|
||||
// decide how many frames need to be drawn. This results in highly
|
||||
// variable timing depending on the machine the game is played on.
|
||||
// On modern PCs, it even results in a divide by 0 that effectively
|
||||
// stops the special effect from playing. Using the original _totalTime
|
||||
// value results in the effect taking much longer than the developers
|
||||
// intended (as made obvious in the dog scare sequence in the beginning
|
||||
// of nancy7), so we manually shorten the timings to better match what
|
||||
// the developers would've seen when on their machines.
|
||||
_totalTime /= 2;
|
||||
_fadeToBlackTime /= 2;
|
||||
}
|
||||
|
||||
if (g_nancy->getTotalPlayTime() > _startTime + _totalTime && (_type != kThroughBlack || _throughBlackStarted2nd)) {
|
||||
if (_currentFrame == 0) {
|
||||
// Ensure at least one dissolve frame is shown
|
||||
++_currentFrame;
|
||||
GraphicsManager::crossDissolve(_fadeFrom, _fadeTo, 128, _rect, _drawSurface);
|
||||
setVisible(true);
|
||||
}
|
||||
} else {
|
||||
// Use a curve for all fades. Not entirely accurate to the original engine,
|
||||
// since that pre-calculated the number of frames and did some exponent magic on them
|
||||
float alpha = (float)(g_nancy->getTotalPlayTime() - _startTime) / (float)_totalTime;
|
||||
bool start2nd = alpha > 1;
|
||||
alpha = alpha * alpha * (3.0 - 2.0 * alpha);
|
||||
alpha *= 255;
|
||||
GraphicsManager::crossDissolve(_fadeFrom, _fadeTo, alpha, _rect, _drawSurface);
|
||||
setVisible(true);
|
||||
++_currentFrame;
|
||||
|
||||
if (start2nd && _type == kThroughBlack) {
|
||||
_throughBlackStarted2nd = true;
|
||||
_fadeFrom.clear();
|
||||
setVisible(false);
|
||||
g_nancy->_graphics->screenshotScreen(_fadeTo);
|
||||
setVisible(true);
|
||||
_startTime = g_nancy->getTotalPlayTime();
|
||||
_currentFrame = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SpecialEffect::onSceneChange() {
|
||||
g_nancy->_graphics->screenshotScreen(_fadeFrom);
|
||||
_drawSurface.rawBlitFrom(_fadeFrom, _rect, Common::Point());
|
||||
}
|
||||
|
||||
void SpecialEffect::afterSceneChange() {
|
||||
if (_fadeFrom.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_type == kCrossDissolve) {
|
||||
g_nancy->_graphics->screenshotScreen(_fadeTo);
|
||||
} else {
|
||||
_fadeTo.create(640, 480, _drawSurface.format);
|
||||
_fadeTo.clear();
|
||||
}
|
||||
|
||||
// Workaround for the way ManagedSurface handles transparency. Both pure black
|
||||
// and pure white appear in scenes with SpecialEffects, and those happen to be
|
||||
// the two default values transBlitFrom uses for transColor. By doing this,
|
||||
// transColor gets set to the one color guaranteed to not appear in any scene,
|
||||
// and transparency works correctly
|
||||
_fadeTo.setTransparentColor(g_nancy->_graphics->getTransColor());
|
||||
|
||||
registerGraphics();
|
||||
_nextFrameTime = g_nancy->getTotalPlayTime() + _frameTime;
|
||||
_fadeToBlackEndTime = g_nancy->getTotalPlayTime() + _totalTime + _fadeToBlackTime;
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
bool SpecialEffect::isDone() const {
|
||||
if (_type == kBlackout) {
|
||||
return g_nancy->getTotalPlayTime() > _fadeToBlackEndTime;
|
||||
} else {
|
||||
bool canFinish = (_type == kThroughBlack) ? _throughBlackStarted2nd : true;
|
||||
return _totalTime ? ((g_nancy->getTotalPlayTime() > _startTime + _totalTime) && (_currentFrame != 0) && canFinish) : (_currentFrame >= (int)_numFrames);
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Misc
|
||||
} // End of namespace Nancy
|
||||
87
engines/nancy/misc/specialeffect.h
Normal file
87
engines/nancy/misc/specialeffect.h
Normal file
@@ -0,0 +1,87 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NANCY_MISC_SPECIALEFFECT_H
|
||||
#define NANCY_MISC_SPECIALEFFECT_H
|
||||
|
||||
#include "engines/nancy/time.h"
|
||||
#include "engines/nancy/renderobject.h"
|
||||
|
||||
namespace Nancy {
|
||||
|
||||
struct SPEC;
|
||||
|
||||
namespace Misc {
|
||||
|
||||
class SpecialEffect : public RenderObject {
|
||||
public:
|
||||
static const byte kBlackout = 1;
|
||||
static const byte kCrossDissolve = 2;
|
||||
static const byte kThroughBlack = 3;
|
||||
|
||||
SpecialEffect(byte type, uint16 fadeToBlackTime, uint16 frameTime) :
|
||||
RenderObject(16),
|
||||
_type(type),
|
||||
_fadeToBlackTime(fadeToBlackTime),
|
||||
_frameTime(frameTime) {}
|
||||
|
||||
SpecialEffect(byte type, uint32 totalTime, uint16 fadeToBlackTime, Common::Rect rect) :
|
||||
RenderObject(16),
|
||||
_type(type),
|
||||
_totalTime(totalTime),
|
||||
_fadeToBlackTime(fadeToBlackTime),
|
||||
_rect(rect) {}
|
||||
virtual ~SpecialEffect() {}
|
||||
|
||||
void init() override;
|
||||
void updateGraphics() override;
|
||||
|
||||
void onSceneChange();
|
||||
void afterSceneChange();
|
||||
|
||||
bool isDone() const;
|
||||
bool isInitialized() const { return _initialized; }
|
||||
|
||||
protected:
|
||||
bool _initialized = false;
|
||||
|
||||
uint32 _nextFrameTime = 0;
|
||||
uint32 _fadeToBlackEndTime = 0;
|
||||
|
||||
Graphics::ManagedSurface _fadeFrom;
|
||||
Graphics::ManagedSurface _fadeTo;
|
||||
|
||||
byte _type = 1;
|
||||
uint16 _fadeToBlackTime = 0;
|
||||
uint32 _frameTime = 0;
|
||||
uint32 _totalTime = 0;
|
||||
Common::Rect _rect;
|
||||
|
||||
int _currentFrame = 0;
|
||||
uint _numFrames = 0;
|
||||
uint32 _startTime = 0;
|
||||
bool _throughBlackStarted2nd = false;
|
||||
};
|
||||
|
||||
} // End of namespace Misc
|
||||
} // End of namespace Nancy
|
||||
|
||||
#endif // NANCY_MISC_SPECIALEFFECT_H
|
||||
Reference in New Issue
Block a user