1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "titanic/support/credit_text.h"
24 #include "titanic/core/game_object.h"
25 #include "titanic/events.h"
26 #include "titanic/support/files_manager.h"
27 #include "titanic/support/screen_manager.h"
28 #include "titanic/titanic.h"
29
30 namespace Titanic {
31
32 #define FRAMES_PER_CYCLE 16
33
CCreditText()34 CCreditText::CCreditText() : _screenManagerP(nullptr), _ticks(0),
35 _fontHeight(1), _objectP(nullptr), _yOffset(0),
36 _priorInc(0), _textR(0), _textG(0), _textB(0), _deltaR(0),
37 _deltaG(0), _deltaB(0), _counter(0) {
38 }
39
clear()40 void CCreditText::clear() {
41 _groups.destroyContents();
42 _objectP = nullptr;
43 }
44
load(CGameObject * obj,CScreenManager * screenManager,const Rect & rect)45 void CCreditText::load(CGameObject *obj, CScreenManager *screenManager,
46 const Rect &rect) {
47 _objectP = obj;
48 _screenManagerP = screenManager;
49 _rect = rect;
50
51 setup();
52
53 _ticks = g_vm->_events->getTicksCount();
54 _priorInc = 0;
55 _textR = 0xFF;
56 _textG = 0xFF;
57 _textB = 0xFF;
58 _deltaR = 0;
59 _deltaG = 0;
60 _deltaB = 0;
61 _counter = 0;
62 }
63
setup()64 void CCreditText::setup() {
65 Common::SeekableReadStream *stream = g_vm->_filesManager->getResource(
66 CString::format("TEXT/155"));
67 int oldFontNumber = _screenManagerP->setFontNumber(3);
68 _fontHeight = _screenManagerP->getFontHeight();
69
70 while (stream->pos() < stream->size()) {
71 // Read in the line
72 CString srcLine = readLine(stream);
73
74 // Create a new group and line within it
75 CCreditLineGroup *group = new CCreditLineGroup();
76 CCreditLine *line = new CCreditLine(srcLine,
77 _screenManagerP->stringWidth(srcLine));
78 group->_lines.push_back(line);
79
80 // Loop to add more lines to the group
81 bool hasDots = false;
82 while (stream->pos() < stream->size()) {
83 srcLine = readLine(stream);
84 if (srcLine.empty())
85 break;
86
87 line = new CCreditLine(srcLine,
88 _screenManagerP->stringWidth(srcLine));
89 group->_lines.push_back(line);
90
91 if (srcLine.contains("...."))
92 hasDots = true;
93 }
94
95 _groups.push_back(group);
96 if (hasDots)
97 handleDots(group);
98 }
99
100 _screenManagerP->setFontNumber(oldFontNumber);
101 _groupIt = _groups.begin();
102 _lineIt = (*_groupIt)->_lines.begin();
103 _yOffset = _objectP->_bounds.height() + _fontHeight * 2;
104 }
105
readLine(Common::SeekableReadStream * stream)106 CString CCreditText::readLine(Common::SeekableReadStream *stream) {
107 CString line;
108 char c = stream->readByte();
109
110 while (c != '\r' && c != '\n' && c != '\0') {
111 line += c;
112
113 if (stream->pos() == stream->size())
114 break;
115 c = stream->readByte();
116 }
117
118 if (c == '\r') {
119 // Read following '\n'
120 stream->readByte();
121 }
122
123 return line;
124 }
125
handleDots(CCreditLineGroup * group)126 void CCreditText::handleDots(CCreditLineGroup *group) {
127 uint maxWidth = 0;
128 CCreditLines::iterator second = group->_lines.begin();
129 ++second;
130
131 // Figure out the maximum width of secondary lines
132 for (CCreditLines::iterator i = second; i != group->_lines.end(); ++i)
133 maxWidth = MAX(maxWidth, (*i)->_lineWidth);
134
135 int charWidth = _screenManagerP->stringWidth(".");
136
137 // Process the secondary lines
138 for (CCreditLines::iterator i = second; i != group->_lines.end(); ++i) {
139 CCreditLine *line = *i;
140 if (line->_lineWidth >= maxWidth)
141 continue;
142
143 int dotsCount = (maxWidth + charWidth / 2 - line->_lineWidth) / charWidth;
144 int dotIndex = line->_line.indexOf("....");
145
146 if (dotIndex > 0) {
147 CString leftStr = line->_line.left(dotIndex);
148 CString dotsStr('.', dotsCount);
149 CString rightStr = line->_line.right(dotIndex);
150
151 line->_line = CString::format("%s%s%s", leftStr.c_str(),
152 dotsStr.c_str(), rightStr.c_str());
153 line->_lineWidth = maxWidth;
154 }
155 }
156 }
157
draw()158 bool CCreditText::draw() {
159 if (_groupIt == _groups.end())
160 return false;
161
162 if (++_counter >= FRAMES_PER_CYCLE) {
163 _textR += _deltaR;
164 _textG += _deltaG;
165 _textB += _deltaB;
166 _deltaR = g_vm->getRandomNumber(63) + 192 - _textR;
167 _deltaG = g_vm->getRandomNumber(63) + 192 - _textG;
168 _deltaB = g_vm->getRandomNumber(63) + 192 - _textB;
169 _counter = 0;
170 }
171
172 // Positioning adjustment, changing lines and/or group if necessary
173 int yDiff = (int)(g_vm->_events->getTicksCount() - _ticks) / 22 - _priorInc;
174
175 while (yDiff > 0) {
176 if (_yOffset > 0) {
177 if (yDiff < _yOffset) {
178 _yOffset -= yDiff;
179 _priorInc += yDiff;
180 yDiff = 0;
181 } else {
182 yDiff -= _yOffset;
183 _priorInc += _yOffset;
184 _yOffset = 0;
185 }
186 } else {
187 if (yDiff < _fontHeight)
188 break;
189
190 ++_lineIt;
191 yDiff -= _fontHeight;
192 _priorInc += _fontHeight;
193
194 if (_lineIt == (*_groupIt)->_lines.end()) {
195 // Move to next line group
196 ++_groupIt;
197 if (_groupIt == _groups.end())
198 // Reached end of groups
199 return false;
200
201 _lineIt = (*_groupIt)->_lines.begin();
202 _yOffset = _fontHeight * 3 / 2;
203 }
204 }
205 }
206
207 int oldFontNumber = _screenManagerP->setFontNumber(3);
208 CCreditLineGroups::iterator groupIt = _groupIt;
209 CCreditLines::iterator lineIt = _lineIt;
210
211 Point textPos;
212 for (textPos.y = _rect.top + _yOffset - yDiff; textPos.y <= _rect.bottom;
213 textPos.y += _fontHeight) {
214 int textR = _textR + _deltaR * _counter / FRAMES_PER_CYCLE;
215 int textG = _textG + _deltaG * _counter / FRAMES_PER_CYCLE;
216 int textB = _textB + _deltaB * _counter / FRAMES_PER_CYCLE;
217
218 // Single iteration loop to figure out RGB values for the line
219 do {
220 int percent = 0;
221 if (textPos.y < (_rect.top + 2 * _fontHeight)) {
222 percent = (textPos.y - _rect.top) * 100 / (_fontHeight * 2);
223 if (percent < 0)
224 percent = 0;
225 } else {
226 int bottom = _rect.bottom - 2 * _fontHeight;
227 if (textPos.y < bottom)
228 break;
229
230 percent = (_rect.bottom - textPos.y) * 100
231 / (_fontHeight * 2);
232 }
233
234 // Adjust the RGB to the specified percentage intensity
235 textR = textR * percent / 100;
236 textG = textG * percent / 100;
237 textB = textB * percent / 100;
238 } while (0);
239
240 // Write out the line
241 _screenManagerP->setFontColor(textR, textG, textB);
242 textPos.x = _rect.left + (_rect.width() - (*lineIt)->_lineWidth) / 2;
243 _screenManagerP->writeString(SURFACE_BACKBUFFER, textPos,
244 _rect, (*lineIt)->_line, (*lineIt)->_lineWidth);
245
246 // Move to next line
247 ++lineIt;
248 if (lineIt == (*groupIt)->_lines.end()) {
249 ++groupIt;
250 if (groupIt == _groups.end())
251 // Finished all lines
252 break;
253
254 lineIt = (*groupIt)->_lines.begin();
255 textPos.y += _fontHeight * 3 / 2;
256 }
257 }
258
259 _objectP->makeDirty();
260 _screenManagerP->setFontNumber(oldFontNumber);
261 return true;
262 }
263
264 } // End of namespace Titanic
265