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