ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

textobject.cpp

Go to the documentation of this file.
00001 /* ResidualVM - A 3D game interpreter
00002  *
00003  * ResidualVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the AUTHORS
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 #include "engines/grim/debug.h"
00024 #include "engines/grim/grim.h"
00025 #include "engines/grim/textobject.h"
00026 #include "engines/grim/savegame.h"
00027 #include "engines/grim/lua.h"
00028 #include "engines/grim/font.h"
00029 #include "engines/grim/gfx_base.h"
00030 #include "engines/grim/color.h"
00031 
00032 namespace Grim {
00033 
00034 TextObjectCommon::TextObjectCommon() :
00035         _x(0), _y(0), _fgColor(0), _justify(0), _width(0), _height(0),
00036         _font(nullptr), _duration(0), _layer(0) {
00037     if (g_grim)
00038         g_grim->invalidateTextObjectsSortOrder();
00039 }
00040 
00041 void TextObjectCommon::setLayer(int layer) {
00042     _layer = layer;
00043     if (g_grim)
00044         g_grim->invalidateTextObjectsSortOrder();
00045 }
00046 
00047 TextObject::TextObject() :
00048         TextObjectCommon(), _numberLines(1), _textID(""), _elapsedTime(0),
00049         _maxLineWidth(0), _lines(nullptr), _userData(nullptr), _created(false),
00050         _blastDraw(false), _isSpeech(false), _stackLevel(0) {
00051 }
00052 
00053 TextObject::~TextObject() {
00054     delete[] _lines;
00055     if (_created) {
00056         g_driver->destroyTextObject(this);
00057     }
00058     if (g_grim)
00059         g_grim->invalidateTextObjectsSortOrder();
00060 }
00061 
00062 void TextObject::setText(const Common::String &text, bool delaySetup) {
00063     destroy();
00064     _textID = text;
00065     if (!delaySetup)
00066         setupText();
00067 }
00068 
00069 void TextObject::reset() {
00070     destroy();
00071     setupText();
00072 }
00073 
00074 void TextObject::saveState(SaveGame *state) const {
00075     state->writeColor(_fgColor);
00076 
00077     state->writeLESint32(_x);
00078     state->writeLESint32(_y);
00079     state->writeLESint32(_width);
00080     state->writeLESint32(_height);
00081     state->writeLESint32(_justify);
00082     state->writeLESint32(_numberLines);
00083     state->writeLESint32(_duration);
00084 
00085     state->writeBool(_blastDraw);
00086     state->writeBool(_isSpeech);
00087     state->writeLESint32(_elapsedTime);
00088 
00089     if (_font) {
00090         state->writeLESint32(_font->getId());
00091     } else {
00092         state->writeLESint32(-1);
00093     }
00094 
00095     state->writeString(_textID);
00096 
00097     if (g_grim->getGameType() == GType_MONKEY4) {
00098         state->writeLESint32(_layer);
00099         state->writeLESint32(_stackLevel);
00100     }
00101 }
00102 
00103 bool TextObject::restoreState(SaveGame *state) {
00104     _fgColor = state->readColor();
00105 
00106     _x            = state->readLESint32();
00107     _y            = state->readLESint32();
00108     _width        = state->readLESint32();
00109     _height       = state->readLESint32();
00110     _justify      = state->readLESint32();
00111     _numberLines  = state->readLESint32();
00112     _duration     = state->readLESint32();
00113 
00114     _blastDraw    = state->readBool();
00115     _isSpeech     = state->readBool();
00116     _elapsedTime  = state->readLESint32();
00117 
00118     int32 fontId = state->readLESint32();
00119     if (fontId == -1) {
00120         _font = nullptr;
00121     } else {
00122         _font = Font::getPool().getObject(fontId);
00123     }
00124 
00125     _textID = state->readString();
00126 
00127     if (g_grim->getGameType() == GType_MONKEY4) {
00128         _layer = state->readLESint32();
00129         _stackLevel = state->readLESint32();
00130         g_grim->invalidateTextObjectsSortOrder();
00131     }
00132 
00133     setupText();
00134     _created = false;
00135     _userData = nullptr;
00136 
00137     return true;
00138 }
00139 
00140 void TextObject::setDefaults(const TextObjectDefaults *defaults) {
00141     _x = defaults->getX();
00142     _y = defaults->getY();
00143     _font = defaults->getFont();
00144     _fgColor = defaults->getFGColor();
00145     _justify = defaults->getJustify();
00146 }
00147 
00148 int TextObject::getBitmapWidth() const {
00149     return _maxLineWidth;
00150 }
00151 
00152 int TextObject::getBitmapHeight() const {
00153     return _numberLines * _font->getKernedHeight();
00154 }
00155 
00156 int TextObject::getTextCharPosition(int pos) {
00157     int width = 0;
00158     Common::String msg = LuaBase::instance()->parseMsgText(_textID.c_str(), nullptr);
00159     for (int i = 0; (msg[i] != '\0') && (i < pos); ++i) {
00160         width += _font->getCharKernedWidth(msg[i]);
00161     }
00162     return width;
00163 }
00164 
00165 void TextObject::destroy() {
00166     if (_created) {
00167         g_driver->destroyTextObject(this);
00168         _created = false;
00169     }
00170 }
00171 
00172 void TextObject::setupText() {
00173     Common::String msg = LuaBase::instance()->parseMsgText(_textID.c_str(), nullptr);
00174     Common::String message;
00175 
00176     // remove spaces (NULL_TEXT) from the end of the string,
00177     // while this helps make the string unique it screws up
00178     // text justification
00179     // remove char of id 13 from the end of the string,
00180     int pos = msg.size() - 1;
00181     while (pos >= 0 && (msg[pos] == ' ' || msg[pos] == 13)) {
00182         msg.deleteLastChar();
00183         pos = msg.size() - 1;
00184     }
00185     delete[] _lines;
00186     if (msg.size() == 0) {
00187         _lines = nullptr;
00188         return;
00189     }
00190 
00191     // format the output message to incorporate line wrapping
00192     // (if necessary) for the text object
00193     const int SCREEN_WIDTH = _width ? _width : 640;
00194     const int SCREEN_MARGIN = SCREEN_WIDTH / 10;
00195 
00196     // If the speaker is too close to the edge of the screen we have to make
00197     // some room for the subtitles.
00198     if (_isSpeech) {
00199         if (_x < SCREEN_MARGIN) {
00200             _x = SCREEN_MARGIN;
00201         } else if (SCREEN_WIDTH - _x < SCREEN_MARGIN) {
00202             _x = SCREEN_WIDTH - SCREEN_MARGIN;
00203         }
00204     }
00205 
00206     // The maximum width for any line of text is determined by the justification
00207     // mode. Note that there are no left/right margins -- this is consistent
00208     // with GrimE.
00209     int maxWidth = 0;
00210     if (_justify == CENTER) {
00211         maxWidth = 2 * MIN(_x, SCREEN_WIDTH - _x);
00212     } else if (_justify == LJUSTIFY) {
00213         maxWidth = SCREEN_WIDTH - _x;
00214     } else if (_justify == RJUSTIFY) {
00215         maxWidth = _x;
00216     }
00217 
00218     // We break the message to lines not longer than maxWidth
00219     Common::String currLine;
00220     _numberLines = 1;
00221     int lineWidth = 0;
00222     for (uint i = 0; i < msg.size(); i++) {
00223         message += msg[i];
00224         currLine += msg[i];
00225         lineWidth += _font->getCharKernedWidth(msg[i]);
00226 
00227         if (currLine.size() > 1 && lineWidth > maxWidth) {
00228             if (currLine.contains(' ')) {
00229                 while (currLine.lastChar() != ' ' && currLine.size() > 1) {
00230                     lineWidth -= _font->getCharKernedWidth(currLine.lastChar());
00231                     message.deleteLastChar();
00232                     currLine.deleteLastChar();
00233                     --i;
00234                 }
00235             } else { // if it is a unique word
00236                 int dashWidth = _font->getCharKernedWidth('-');
00237                 while (lineWidth + dashWidth > maxWidth && currLine.size() > 1) {
00238                     lineWidth -= _font->getCharKernedWidth(currLine.lastChar());
00239                     message.deleteLastChar();
00240                     currLine.deleteLastChar();
00241                     --i;
00242                 }
00243                 message += '-';
00244             }
00245             message += '\n';
00246             currLine.clear();
00247             _numberLines++;
00248 
00249             lineWidth = 0;
00250         }
00251     }
00252 
00253     // If the text object is a speech subtitle, the y parameter is the
00254     // coordinate of the bottom of the text block (instead of the top). It means
00255     // that every extra line pushes the previous lines up, instead of being
00256     // printed further down the screen.
00257     const int SCREEN_TOP_MARGIN = _font->getKernedHeight();
00258     if (_isSpeech) {
00259         _y -= _numberLines * _font->getKernedHeight();
00260         if (_y < SCREEN_TOP_MARGIN) {
00261             _y = SCREEN_TOP_MARGIN;
00262         }
00263     }
00264 
00265     _lines = new Common::String[_numberLines];
00266 
00267     for (int j = 0; j < _numberLines; j++) {
00268         int nextLinePos, cutLen;
00269         const char *breakPos = strchr(message.c_str(), '\n');
00270         if (breakPos) {
00271             nextLinePos = breakPos - message.c_str();
00272             cutLen = nextLinePos + 1;
00273         } else {
00274             nextLinePos = message.size();
00275             cutLen = nextLinePos;
00276         }
00277         Common::String currentLine(message.c_str(), message.c_str() + nextLinePos);
00278         _lines[j] = currentLine;
00279         int width = _font->getKernedStringLength(currentLine);
00280         if (width > _maxLineWidth)
00281             _maxLineWidth = width;
00282         for (int count = 0; count < cutLen; count++)
00283             message.deleteChar(0);
00284     }
00285     _elapsedTime = 0;
00286 }
00287 
00288 int TextObject::getLineX(int line) const {
00289     int x = _x;
00290     if (_justify == CENTER)
00291         x = _x - (_font->getKernedStringLength(_lines[line]) / 2);
00292     else if (_justify == RJUSTIFY)
00293         x = _x - getBitmapWidth();
00294 
00295     if (x < 0)
00296         x = 0;
00297     return x;
00298 }
00299 
00300 int TextObject::getLineY(int line) const {
00301     int y = _y;
00302 
00303     if (g_grim->getGameType() == GType_GRIM) {
00304         if (_blastDraw) { // special case for Grim for menu text draw, issue #1083
00305             y = _y + 5;
00306         } else {
00307 /*          if (_font->getKernedHeight() == 21) // talk_font,verb_font
00308                 y = _y - 6;
00309             else if (_font->getKernedHeight() == 26) // special_font
00310                 y = _y - 12;
00311             else */if (_font->getKernedHeight() == 13) // computer_font
00312                 y = _y - 6;/*
00313             else if (_font->getKernedHeight() == 19) // pt_font
00314                 y = _y - 9;*/
00315             else
00316                 y = _y;
00317         }
00318     }
00319 
00320     if (y < 0)
00321         y = 0;
00322     y += _font->getKernedHeight() * line;
00323 
00324     return y;
00325 }
00326 
00327 void TextObject::draw() {
00328     if (!_lines)
00329         return;
00330 
00331     if (!_created) {
00332         g_driver->createTextObject(this);
00333         _created = true;
00334     }
00335 
00336     if (_justify > 3 || _justify < 0)
00337         warning("TextObject::draw: Unknown justification code (%d)", _justify);
00338 
00339     g_driver->drawTextObject(this);
00340 
00341 }
00342 
00343 void TextObject::update() {
00344     if (!_duration || !_created) {
00345         return;
00346     }
00347 
00348     _elapsedTime += g_grim->getFrameTime();
00349     if (_elapsedTime > _duration) {
00350         delete this;
00351     }
00352 }
00353 
00354 } // end of namespace Grim


Generated on Sat Oct 12 2019 05:00:57 for ResidualVM by doxygen 1.7.1
curved edge   curved edge